diff --git a/AndroidKernelModule.mk b/AndroidKernelModule.mk new file mode 100644 index 000000000000..246e1a463057 --- /dev/null +++ b/AndroidKernelModule.mk @@ -0,0 +1,203 @@ +USE_NEW_DLKM_MK := false + +ifeq ($(call is-board-platform-in-list,taro kalama parrot crow pineapple), true) +#Taro and beyond are using a new mechanisim to build kernel modules +USE_NEW_DLKM_MK := true +endif + +ifeq ($(TARGET_BOARD_PLATFORM), bengal) +ifeq ($(TARGET_KERNEL_VERSION), 5.15) +#new bengal uses new mechanisim to build kernel modules +USE_NEW_DLKM_MK := true +endif +endif + +ifeq ($(USE_NEW_DLKM_MK), true) +ifneq (,$(findstring $(LOCAL_MODULE),$(BOARD_VENDOR_KERNEL_MODULES))) +#Taro and beyond are using a new mechanisim to build kernel modules +include device/qcom/common/dlkm/Build_external_kernelmodule.mk +endif +else +ifneq (,$(findstring gki,$(KERNEL_DEFCONFIG))) +#GKI product should use Qcom latest dlkm compile file +include device/qcom/common/dlkm/AndroidKernelModule.mk +else +DISABLE_THIS_DLKM := $(strip $(TARGET_KERNEL_DLKM_DISABLE)) + +ifeq ($(DISABLE_THIS_DLKM),true) +ifneq (,$(filter $(LOCAL_MODULE),$(TARGET_KERNEL_DLKM_OVERRIDE))) + DISABLE_THIS_DLKM = false +else +endif +endif + +ifeq ($(DISABLE_THIS_DLKM),true) +$(warning DLKM '$(LOCAL_MODULE)' disabled for target) +else + +# Assign external kernel modules to the DLKM class +LOCAL_MODULE_CLASS := DLKM + +# Set the default install path to vendor/lib/modules +LOCAL_MODULE_PATH := $(strip $(LOCAL_MODULE_PATH)) +ifeq ($(LOCAL_MODULE_PATH),) + LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules +endif + +# Set the default Kbuild file path to LOCAL_PATH +KBUILD_FILE := $(strip $(KBUILD_FILE)) +ifeq ($(KBUILD_FILE),) + KBUILD_FILE := $(LOCAL_PATH)/Kbuild +endif + +# Get rid of any whitespace +LOCAL_MODULE_KBUILD_NAME := $(strip $(LOCAL_MODULE_KBUILD_NAME)) + +include $(BUILD_SYSTEM)/base_rules.mk + + +# The kernel build system doesn't support parallel kernel module builds +# that share the same output directory. Thus, in order to build multiple +# kernel modules that reside in a single directory (and therefore have +# the same output directory), there must be just one invocation of the +# kernel build system that builds all the modules of a given directory. +# +# Therefore, all kernel modules must depend on the same, unique target +# that invokes the kernel build system and builds all of the modules +# for the directory. The $(KBUILD_TARGET) target serves this purpose. +# To ensure the value of KBUILD_TARGET is unique, it is essentially set +# to the path of the source directory, i.e. LOCAL_PATH. +# +# Since KBUILD_TARGET is used as a target and a variable name, it should +# not contain characters other than letters, numbers, and underscores. +KBUILD_TARGET := $(strip \ + $(subst .,_, \ + $(subst -,_, \ + $(subst :,_, \ + $(subst /,_,$(LOCAL_PATH)))))) + +# Intermediate directory where the kernel modules are created +# by the kernel build system. Ideally this would be the same +# directory as LOCAL_BUILT_MODULE, but because we're using +# relative paths for both O= and M=, we don't have much choice +KBUILD_OUT_DIR := $(TARGET_OUT_INTERMEDIATES)/$(LOCAL_PATH) + +# Path to the intermediate location where the kernel build +# system creates the kernel module. +KBUILD_MODULE := $(KBUILD_OUT_DIR)/$(LOCAL_MODULE) + +# Since we only invoke the kernel build system once per directory, +# each kernel module must depend on the same target. +$(KBUILD_MODULE): kbuild_out := $(KBUILD_OUT_DIR)/$(LOCAL_MODULE_KBUILD_NAME) +$(KBUILD_MODULE): $(KBUILD_TARGET) +ifneq "$(LOCAL_MODULE_KBUILD_NAME)" "" + mv -f $(kbuild_out) $@ +endif + + +# To ensure KERNEL_OUT and TARGET_PREBUILT_INT_KERNEL are defined, +# kernel/AndroidKernel.mk (kernel_definitions.mk) must be included. While m and regular +# make builds will include kernel/AndroidKernel.mk, mm and mmm builds +# do not. Therefore, we need to explicitly include kernel/AndroidKernel.mk (kernel_definitions.mk). +# It is safe to include it more than once because the entire file is +# guarded by "ifeq ($(TARGET_PREBUILT_KERNEL),) ... endif". +# If AndroidKernel.mk is not found, fall back to use the kernel_definitions.mk +ifneq ($(wildcard $(TARGET_KERNEL_SOURCE)/AndroidKernel.mk),) +TARGET_KERNEL_PATH := $(TARGET_KERNEL_SOURCE)/AndroidKernel.mk +else +TARGET_KERNEL_PATH := device/qcom/kernelscripts/kernel_definitions.mk +endif +include $(TARGET_KERNEL_PATH) + +# Simply copy the kernel module from where the kernel build system +# created it to the location where the Android build system expects it. +# If LOCAL_MODULE_DEBUG_ENABLE is set, strip debug symbols. So that, +# the final images generated by ABS will have the stripped version of +# the modules +ifeq ($(TARGET_KERNEL_VERSION),3.18) + MODULE_SIGN_FILE := perl ./$(TARGET_KERNEL_SOURCE)/scripts/sign-file + MODSECKEY := $(KERNEL_OUT)/signing_key.priv + MODPUBKEY := $(KERNEL_OUT)/signing_key.x509 +else + MODULE_SIGN_FILE := $(KERNEL_OUT)/scripts/sign-file + MODSECKEY := $(KERNEL_OUT)/certs/signing_key.pem + MODPUBKEY := $(KERNEL_OUT)/certs/signing_key.x509 +endif + +$(LOCAL_BUILT_MODULE): $(KBUILD_MODULE) | $(ACP) +ifneq "$(LOCAL_MODULE_DEBUG_ENABLE)" "" + mkdir -p $(dir $@) + cp $< $<.unstripped + $(TARGET_STRIP) --strip-debug $< + cp $< $<.stripped +endif + @sh -c "\ + KMOD_SIG_ALL=`cat $(KERNEL_OUT)/.config | grep CONFIG_MODULE_SIG_ALL | cut -d'=' -f2`; \ + KMOD_SIG_HASH=`cat $(KERNEL_OUT)/.config | grep CONFIG_MODULE_SIG_HASH | cut -d'=' -f2 | sed 's/\"//g'`; \ + if [ \"\$$KMOD_SIG_ALL\" = \"y\" ] && [ -n \"\$$KMOD_SIG_HASH\" ]; then \ + echo \"Signing kernel module: \" `basename $<`; \ + cp $< $<.unsigned; \ + $(MODULE_SIGN_FILE) \$$KMOD_SIG_HASH $(MODSECKEY) $(MODPUBKEY) $<; \ + fi; \ + " + $(transform-prebuilt-to-target) + +# This should really be cleared in build/core/clear-vars.mk, but for +# the time being, we need to clear it ourselves +LOCAL_MODULE_KBUILD_NAME := +LOCAL_MODULE_DEBUG_ENABLE := + +# Ensure the kernel module created by the kernel build system, as +# well as all the other intermediate files, are removed during a clean. +$(cleantarget): PRIVATE_CLEAN_FILES := $(PRIVATE_CLEAN_FILES) $(KBUILD_OUT_DIR) + + +# Since this file will be included more than once for directories +# with more than one kernel module, the shared KBUILD_TARGET rule should +# only be defined once to avoid "overriding commands ..." warnings. +ifndef $(KBUILD_TARGET)_RULE +$(KBUILD_TARGET)_RULE := 1 + +# Kernel modules have to be built after: +# * the kernel config has been created +# * host executables, like scripts/basic/fixdep, have been built +# (otherwise parallel invocations of the kernel build system will +# fail as they all try to compile these executables at the same time) +# * a full kernel build (to make module versioning work) +# +# For these reasons, kernel modules are dependent on +# TARGET_PREBUILT_INT_KERNEL which will ensure all of the above. +# +# NOTE: Due to a bug in the kernel build system when using a Kbuild file +# and relative paths for both O= and M=, the Kbuild file must +# be copied to the output directory. +# +# NOTE: The following paths are equivalent: +# $(KBUILD_OUT_DIR) +# $(KERNEL_OUT)/../$(LOCAL_PATH) +.PHONY: $(KBUILD_TARGET) +$(KBUILD_TARGET): local_path := $(LOCAL_PATH) +$(KBUILD_TARGET): kbuild_out_dir := $(KBUILD_OUT_DIR) +$(KBUILD_TARGET): kbuild_options := $(KBUILD_OPTIONS) +ifneq ($(USE_CLANG_FOR_MODULES),) +$(KBUILD_TARGET): $(TARGET_PREBUILT_INT_KERNEL) + @mkdir -p $(kbuild_out_dir) + $(hide) cp -f $(local_path)/Kbuild $(kbuild_out_dir)/Kbuild + $(MAKE) -C $(TARGET_KERNEL_SOURCE) M=$(KERNEL_TO_BUILD_ROOT_OFFSET)$(local_path) O=$(KERNEL_TO_BUILD_ROOT_OFFSET)$(KERNEL_OUT) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(real_cc) $(KERNEL_CFLAGS) modules $(kbuild_options) ANDROID_BUILD_TOP=$$(pwd) TOP=$$(pwd) +else +$(KBUILD_TARGET): $(TARGET_PREBUILT_INT_KERNEL) + @mkdir -p $(kbuild_out_dir) + $(hide) cp -f $(local_path)/Kbuild $(kbuild_out_dir)/Kbuild + $(MAKE) -C $(TARGET_KERNEL_SOURCE) M=$(KERNEL_TO_BUILD_ROOT_OFFSET)$(local_path) O=$(KERNEL_TO_BUILD_ROOT_OFFSET)$(KERNEL_OUT) ARCH=$(KERNEL_ARCH) CROSS_COMPILE=$(KERNEL_CROSS_COMPILE) $(KERNEL_CFLAGS) modules $(kbuild_options) TOP=$$(pwd) +endif + +# Once the KBUILD_OPTIONS variable has been used for the target +# that's specific to the LOCAL_PATH, clear it. If this isn't done, +# then every kernel module would need to explicitly set KBUILD_OPTIONS, +# or the variable would have to be cleared in 'include $(CLEAR_VARS)' +# which would require a change to build/core. +KBUILD_OPTIONS := +endif +endif +endif #(findstring gki,$(KERNEL_DEFCONFIG)) +endif #(is-board-platform-in-list,taro....) diff --git a/Documentation/leds/ledtrig-sol.rst b/Documentation/leds/ledtrig-sol.rst new file mode 100644 index 000000000000..4aa95ce10d21 --- /dev/null +++ b/Documentation/leds/ledtrig-sol.rst @@ -0,0 +1,90 @@ +================================ +Sign-of-life LED trigger +================================ + +Description +=========== + + This is a Sign-of-life LED trigger for QCOM Tri-led Driver. + Refer -> kernel_platform/qcom/proprietary/devicetree/bindings/leds/leds-qti-tri-led.txt + This trigger will register with LED core framework to individually + get activated or deactivated. + The trigger will be registered with the power_supply_class to get charging/discharging + state and battery capacity. + +Handle power-supply states for Charging/Discharging +=================================================== + +Set blink/solid Led settings as below + +LED Brighness depend on Device Charging State +---------------------------------------------- + Full while Device Charging + Half Brighness while not Charging + +Led Functional Mode Depend on the Battery capacity +-------------------------------------------------- + Battery Level < LOW_CAP_THRESHOLD_DEFAULT + Fast Blink "charging_low" led node(s) + +LOW_CAP_THRESHOLD_DEFAULT < Battery Level < BATT_CAP_THRESHOLD_HIGH + Slow Blink "charging" led node(s) (If device charging) + Enable Solid 50% brightness for "charging" led node(s) (If device not charging) + +BATT_CAP_THRESHOLD_HIGH < Battery Level < BATT_CAP_THRESHOLD_FULL + Slow Blink "charging_full" led node(s) (If device charging) + Enable Solid 50% brightness for "charging_full" led node(s) (If device not charging) + +Battery Level == BATT_CAP_THRESHOLD_FULL + Enable Solid 100% brightness for "charging_full" led node(s) + +Label each node with corresponding colour in device tree +====================================================== + + Qcom Tri-Led will have settings for each Led (R,G,B). + The trigger driver will control with blink or solid pattern via labels nodes like + charging_low node: Charging/Discharging and Low battery state + charging node: Charging/Discharging state with medium battery + charging_full node: Charging/Discharging state while battery near to Full. + +Example of device tree setting +============================= + +:: + +&pm8550_rgb { + red { + status = "ok"; + label = "charging_low"; + linux,default-trigger = "sign-of-life"; + }; + + green { + status = "ok"; + label = "charging_full"; + linux,default-trigger = "sign-of-life"; + }; + + blue { + status = "ok"; + label = "charging"; + linux,default-trigger = "sign-of-life"; + }; +}; + +List of Methods in Sol trigger driver +==================================== + static int sol_trig_activate(struct led_classdev *led_cdev) + { + /* Set default sol data register with power_supply notifier */ + } + + static void sol_trig_deactivate(struct led_classdev *led_cdev) + { + /* Unregister with power_supply notifier and Free sol data */ + } + + static int sol_led_update_status(led_name, blink, brightness, sol_data) + { + /* API to set sol led colour, brightness etc based on battery status */ + } diff --git a/arch/arm64/boot/dts/vendor/bindings/cs40l2x.txt b/arch/arm64/boot/dts/vendor/bindings/cs40l2x.txt new file mode 100644 index 000000000000..87de608072b4 --- /dev/null +++ b/arch/arm64/boot/dts/vendor/bindings/cs40l2x.txt @@ -0,0 +1,119 @@ +CS40L20 Boosted Haptics Driver + +Required properties: + + - compatible : One of "cirrus,cs40l20", "cirrus,cs40l25", "cirrus,cs40l25a" + or "cirrus,cs40l25b". + + - reg : The I2C slave address of the device. + + - VA-supply, VP-supply : Regulators for the device's VA and VP supplies, + respectively. See the following: + Documentation/devicetree/bindings/regulator/regulator.txt + + - cirrus,boost-ind-nanohenry : Boost inductor value, expressed in nH. Valid + values include 1000, 1200, 1500 and 2200. + + - cirrus,boost-cap-microfarad : Total equivalent boost capacitance on the VBST + and VAMP pins, derated at 11 volts DC. The value must be rounded to the + nearest integer and expressed in uF. + + - cirrus,boost-ipk-milliamp : Boost inductor peak current, expressed in mA. + Valid values range from 1600 to 4500 (inclusive) in steps of 50. + +Optional properties: + + - reset-gpios : GPIO used for resetting the device. + + - cirrus,refclk-gpio2 : Boolean for configuring the device to expect its + 32.768-kHz reference clock on the REFCLK/GPIO2 pin. If this property is + omitted, the device expects its 32.768-kHz reference clock on the + ASP_BCLK/REFCLK pin. This property is ignored for CS40L25A and CS40L25B + devices as reference clock selection is configured automatically. + + - cirrus,f0-default : Default LRA resonant frequency (f0), expressed as + follows: cirrus,f0-default = f0 (Hz) * 2^14. This value represents the + frequency used during playback of PWLE segments specified with frequency + equal to f0; it also serves as the unit-specific f0 input to the click + compensation algorithm. It can be overwritten at a later time by writing + to the f0_stored sysfs control. + + If this value is omitted or specified as zero, the measurement recorded in + the f0_measured sysfs control is used. If LRA diagnostics has not been + administered and f0_measured is uninitialized, 2621440 (160 Hz) is used. + + - cirrus,f0-min : Minimum LRA resonant frequency (f0) that may be written to + the f0_stored sysfs control, expressed using the same numerical format as + cirrus,f0-default. If this value is omitted or specified as zero, no lower- + bound validation is performed. + + - cirrus,f0-max : Maximum LRA resonant frequency (f0) that may be written to + the f0_stored sysfs control, expressed using the same numerical format as + cirrus,f0-default. If this value is omitted or specified as zero, no upper- + bound validation is performed. + + - cirrus,redc-default : Default LRA series resistance (ReDC), expressed as + follows: cirrus,redc-default = ReDC (ohms) / 5.857 * 2^17. This value + represents the unit-specific ReDC input to the click compensation algorithm. + It can be overwritten at a later time by writing to the redc_stored sysfs + control. + + If this value is omitted or specified as zero, the measurement recorded in + the redc_measured sysfs control is used. If LRA diagnostics has not been + administered and redc_measured is uninitialized, 340787 (15.2 ohms) is used. + + - cirrus,redc-min : Minimum LRA series resistance (ReDC) that may be written + to the redc_stored sysfs control, expressed using the same numerical format + as cirrus,redc-default. If this value is omitted or specified as zero, no + lower-bound validation is performed. + + - cirrus,redc-max : Maximum LRA series resistance (ReDC) that may be written + to the redc_stored sysfs control, expressed using the same numerical format + as cirrus,redc-default. If this value is omitted or specified as zero, no + upper-bound validation is performed. + + - cirrus,gpio1-rise-index : Specifies the wavetable index mapped to GPIO1 + rising edges. If this value is omitted, specified as zero or exceeds the + maximum available index in the wavetable, GPIO1 rising edges are mapped to + index 1. + + - cirrus,gpio1-fall-index : Specifies the wavetable index mapped to GPIO1 + falling edges. If this value is omitted, specified as zero or exceeds the + maximum available index in the wavetable, GPIO1 falling edges are mapped to + index 2. + + - cirrus,gpio1-fall-timeout : Specifies the number of 48-kHz periods for + which the device remains in the active state in search of a GPIO1 falling + edge, following a GPIO1 rising edge (the latter of which renders the device + active). If a GPIO1 falling edge does not arrive within this timeout, the + device automatically returns to the standby state and the subsequent GPIO1 + falling edge is ignored. If this value is omitted or exceeds the maximum + timeout (8388607) then a default of 240000 is assumed (corresponding to + 240000 / 48000 = 5 seconds). If this value is specified as zero, the timeout + is effectively disabled. + + - cirrus,gpio1-mode : Specifies the operating mode of the GPIO1 pin, equal to + one of the following. + + 0 = enabled by default + 1 = disabled by default + 2 = disabled by default, but automatically enabled and disabled upon suspend + and resume, respectively + + If this value is omitted or given an invalid value, mode 0 (enabled by + default) is assumed. Regardless of mode, the GPIO1 pin can be enabled or + disabled at will from user space using the gpio1_enable sysfs control. + +Example: + + cs40l20: cs40l20@43 { + compatible = "cirrus,cs40l20"; + reg = <0x43>; + reset-gpios = <&gpio0 54 0>; + VA-supply = <&dummy_vreg>; + VP-supply = <&dummy_vreg>; + cirrus,boost-ind-nanohenry = <1000>; + cirrus,boost-cap-microfarad = <4>; + cirrus,boost-ipk-milliamp = <4500>; + cirrus,refclk-gpio2; + }; diff --git a/arch/arm64/boot/dts/vendor/bindings/drv8424.txt b/arch/arm64/boot/dts/vendor/bindings/drv8424.txt new file mode 100644 index 000000000000..37616237b8bf --- /dev/null +++ b/arch/arm64/boot/dts/vendor/bindings/drv8424.txt @@ -0,0 +1,63 @@ +DRV8424 stepper motor driver from Texas Instruments + +Motor driver is a platform device. Motorola specific HW layout +allows for the following controllable GPIOs: + + - MOTOR_POWER_EN + - MOTOR_FAULT_INT + - MOTOR_STEP + - MOTOR_DIR + - MOTOR_EN + - MOTOR_SLEEP + - MOTOR_ACTIVE + +There is no kernel configuration option associated with DRV8424 DLKM + +Required properties: + - compatible: only one supported "moto,drv8424" + - drv8424-gpio-cells: number of elements allocated for gpio array in the driver + - MOTOR_POWER_EN-gpio: gpio enabling power to the motor + - MOTOR_FAULT_INT-gpio: gpio used as IRQ to indicate motor fault + - MOTOR_STEP-gpio: gpio used as input for pulse generator + - MOTOR_DIR-gpio: gpio to control motor direction + - MOTOR_EN-gpio: gpio to control drv8424 EN state + - MOTOR_SLEEP-gpio: gpio to control drv8424 SLEEP state + - MOTOR_ACTIVE-gpio: gpio to control hall effect sensor measurements + - pinctrl-names: pinctrl state "drv8424_int_default" required for motor fault IRQ + +Optional properties: + - pinctrl-names: list of pre-defined name (currently only "init_state" supported) + - power-default-off: keep power off when not in use + - support-mode: driver supports MODE0 and MODE1 setup + - support-torque: driver supports T0 and T1 setup + - no-sensors: ignore sensors data (mostly debug option) + +Note: +Only following pinctrl state names are valid!!! + "m0_low", "m0_high", "m0_disable", + "m1_low", "m1_high", "m1_disable", + "t0_low", "t0_high", "t0_disable", + "t1_low", "t1_high", "t1_disable", + "drv8424_int_default", + "drv8424_clk_active", + "drv8424_clk_sleep", + "init_state" + +Example: + + moto_drv8424_support { + compatible = "moto,drv8424"; + drv8424-gpios-cells = <11>; + pinctrl-names = "drv8424_int_default", "init_state"; + pinctrl-0 = <&drv8424_int_default>; + pinctrl-1 = <&drv8424_pwr_en_init &drv8424_step_init &drv8424_dir_init + &drv8424_sleep_init &drv8424_en_init &drv8424_step_init &drv8424_active_init>; + MOTOR_POWER_EN-gpio = <&tlmm 102>; + MOTOR_FAULT_INT-gpio = <&tlmm 112>; + MOTOR_STEP-gpio = <&tlmm 182>; + MOTOR_DIR-gpio = <&tlmm 183>; + MOTOR_SLEEP-gpio = <&tlmm 184>; + MOTOR_ACTIVE-gpio = <&tlmm 185>; + vdd-supply = <&S10B>; + power-default-off; + }; \ No newline at end of file diff --git a/arch/arm64/boot/dts/vendor/bindings/input/misc/ets_fps_mmi/ets_fps.txt b/arch/arm64/boot/dts/vendor/bindings/input/misc/ets_fps_mmi/ets_fps.txt new file mode 100644 index 000000000000..ddb25e19386d --- /dev/null +++ b/arch/arm64/boot/dts/vendor/bindings/input/misc/ets_fps_mmi/ets_fps.txt @@ -0,0 +1,28 @@ + +Egis Technology Inc fingerprint driver +et320 is a fps sensor in front of the phone, it connects to mainboard via SPI. +et516 is a fps sensor on the back of the phone, it connects to mainboard via SPI. +Required properties: +- compatible: Should be "egistec,et516" or "egistec,et320" +- gpio_irq : GPIO used for interrupt +- gpio_rst: GPIO used for ets sensor reset +- gpio_ldo1p8_en : GPIO used to enable vdd 1.8v +- gpio_ldo3p3_en : GPIO used to enable vcc 3.3v +- pinctrl-names,pinctrl-0: the pincontrol setting to configure muxing properly + for pins that connect to ets fps sensor. + +Example: + ets_fps: ets_fps{ + compatible = "egistec,et320","egistec,et516"; + status = "ok"; + reg = <0>; + interrupt-parent = <&tlmm>; + interrupts = <128 0x0>; + egistec,gpio_irq = <&tlmm 128 0x1>; + egistec,gpio_rst = <&tlmm 127 0x2>; + egistec,gpio_ldo1p8_en = <&tlmm 132 0x0>; + egistec,gpio_ldo3p3_en = <&tlmm 133 0x0>; + pinctrl-names = "default", "suspend"; + pinctrl-0 = <&rst_active &int_active &ldo_en_active &ldo3_en_active>; + pinctrl-1 = <&rst_suspend &int_suspend &ldo_en_suspend &ldo3_en_suspend>; + }; diff --git a/arch/arm64/boot/dts/vendor/bindings/input/misc/goodix_fod_mmi/goodix_fod_mmi.txt b/arch/arm64/boot/dts/vendor/bindings/input/misc/goodix_fod_mmi/goodix_fod_mmi.txt new file mode 100644 index 000000000000..e5afdc6b7188 --- /dev/null +++ b/arch/arm64/boot/dts/vendor/bindings/input/misc/goodix_fod_mmi/goodix_fod_mmi.txt @@ -0,0 +1,18 @@ +Goodix Fingerprint TEE driver + +Required properties: + +-compatible : should be "goodix,fingerprint". +-interrupt-parent : gpio interrupt parent declare +-fp-gpio-irq : irq gpio declare +-fp-gpio-reset : reset gpio declare + +Example: + +goodix_fp { + compatible = "goodix,fingerprint"; + interrupt-parent = <&tlmm>; + fp-gpio-irq = <&tlmm 58 0x00>; + fp-gpio-reset = <&tlmm 59 0x00>; + status = "okay"; +}; diff --git a/arch/arm64/boot/dts/vendor/bindings/input/misc/stmvl53l1.txt b/arch/arm64/boot/dts/vendor/bindings/input/misc/stmvl53l1.txt new file mode 100644 index 000000000000..6c6b7a34a923 --- /dev/null +++ b/arch/arm64/boot/dts/vendor/bindings/input/misc/stmvl53l1.txt @@ -0,0 +1,28 @@ +st,stmvl53l1 laser driver + +Required properties: + + - compatible : Should be "st,stmvl53l1". + +Optional properites: + + + Example: + stmvl53l1@29 { + status = "ok"; + compatible = "st,stmvl53l1"; + reg = <0x29>; + /* L22 is always on for now */ + /* vdd-supply = <&pm8998_l22>; */ + gpios = <&pm8998_gpios 15 0>, <&tlmm 79 0>; + qcom,gpio-reset = <0>; + qcom,gpio-req-tbl-num = <0 1>; + qcom,gpio-req-tbl-flags = <0 1>; + qcom,gpio-req-tbl-label ="TOF_XSHUTDOWN","TOF_INT"; + pinctrl-names = "laser_default", "laser_suspend"; + pinctrl-0 = <&laser_active >; + pinctrl-1 = <&laser_suspend >; + st,sensorthreshold = <60 65>; + st,xtalkval = <19>; + }; + diff --git a/arch/arm64/boot/dts/vendor/bindings/input/misc/sx933x/sx933x.txt b/arch/arm64/boot/dts/vendor/bindings/input/misc/sx933x/sx933x.txt new file mode 100644 index 000000000000..c975cb3cf2d4 --- /dev/null +++ b/arch/arm64/boot/dts/vendor/bindings/input/misc/sx933x/sx933x.txt @@ -0,0 +1,37 @@ +sx933x cap touch sensor from semtech + +It is a i2c slave device +two specific properties for driver init registers's value + +a).CONFIG_INPUT_SX933x corresponding driver sx933x_sar.c, +sx933x is 5 channels driver. + +Required properties: + - Semtech,button-flag :flag of used buttons,0~4 bits refers to button 0~4 + - Semtech,reg-num :num of registers need to be set + - Semtech,reg-init :registers's val, format is + +Example: + + sx933x@28 { + compatible = "Semtech,sx933x"; + reg = <0x28>; + interrupt-parent = <&tlmm>; + interrupts = <75 0>; + Semtech,nirq-gpio = <&tlmm 75 0x00>; + cap_vdd-supply = <&pm660l_l8>; + Semtech,reg-num = <12>; + Semtech,button-flag = <0x07>; //used button 0~2 + Semtech,reg-init = <0x8020 0x3F00C0 + 0x8024 0x44F + 0x8028 0x24928000 + 0x802C 0x44F + 0x8030 0x24960000 + 0x8034 0x44F + 0x8038 0x24B20000 + 0x803C 0x44F + 0x8040 0x25920000 + 0x8044 0x44F + 0x8048 0x2C920000 + 0x4004 0x67 >; + }; diff --git a/arch/arm64/boot/dts/vendor/bindings/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_i2c.txt b/arch/arm64/boot/dts/vendor/bindings/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_i2c.txt new file mode 100644 index 000000000000..11ec15a5603b --- /dev/null +++ b/arch/arm64/boot/dts/vendor/bindings/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_i2c.txt @@ -0,0 +1,71 @@ +Synaptics TCM I2C touchscreen controller + +Required properties: + - compatible: + should be "synaptics,tcm-i2c" + - reg: + i2c slave address of device + - interrupt-parent: + hardware controller of interrupt signal + - interrupts: + gpio number and flags of interrupt signal + - vdd-supply: + digital power source + - avdd-supply: + analog power source + - pinctrl-names: + - pinctrl-0: + - pinctrl-1: + should be defined if using pinctrl framework + "pmx_ts_active": active configuration of pins + "pmx_ts_suspend": disabled configuration of pins + - synaptics,bus-reg-name: + name of digital power source regulator + - synaptics,pwr-reg-name: + name of analog power source regulator + - synaptics,irq-gpio: + interrupt hardware controller, gpio number, and flags + - synaptics,irq-on-state: + active state of interrupt signal + +Optional properties: + - synaptics,power-gpio: + hardware controller and gpio number of power control signal + - synaptics,power-delay-ms: + delay time in ms after powering on device + - synaptics,reset-gpio: + hardware controller and gpio number of reset signal + - synaptics,reset-delay-ms: + delay time in ms after issuing reset to device + - synaptics,reset-on-state: + active state of reset signal + - synaptics,reset-active-ms: + active duration in ms of reset signal + - synaptics,x-flip: + flip x axis + - synaptics,y-flip: + flip y axis + - synaptics,swap-axes: + swap x and y axes + - synaptics,ubl-i2c-addr: + i2c slave address of device in microbootloader mode + +Example: + synaptics_tcm@2c { + compatible = "synaptics,tcm-i2c"; + reg = <0x2c>; + interrupt-parent = <&msm_gpio>; + interrupts = <65 0x2008>; + vdd-supply = <&pm8994_lvs2>; + avdd-supply = <&pm8994_l22>; + pinctrl-names = "pmx_ts_active", "pmx_ts_suspend"; + pinctrl-0 = <&ts_active>; + pinctrl-1 = <&ts_suspend>; + synaptics,pwr-reg-name = "avdd"; + synaptics,bus-reg-name = "vdd"; + synaptics,irq-gpio = <&msm_gpio 65 0x2008>; + synaptics,irq-on-state = <0>; + synaptics,power-delay-ms = <200>; + synaptics,reset-delay-ms = <200>; + synaptics,ubl-i2c-addr = <0x2c>; + }; diff --git a/arch/arm64/boot/dts/vendor/bindings/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_spi.txt b/arch/arm64/boot/dts/vendor/bindings/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_spi.txt new file mode 100644 index 000000000000..d73abb5b6023 --- /dev/null +++ b/arch/arm64/boot/dts/vendor/bindings/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_spi.txt @@ -0,0 +1,86 @@ +Synaptics TCM SPI touchscreen controller + +Required properties: + - compatible: + should be "synaptics,tcm-spi" + - reg: + should be 0 + - spi-max-frequency: + maximum spi clock frequency + - interrupt-parent: + hardware controller of interrupt signal + - interrupts: + gpio number and flags of interrupt signal + - vdd-supply: + digital power source + - avdd-supply: + analog power source + - pinctrl-names: + - pinctrl-0: + - pinctrl-1: + should be defined if using pinctrl framework + "pmx_ts_active": active configuration of pins + "pmx_ts_suspend": disabled configuration of pins + - synaptics,bus-reg-name: + name of digital power source regulator + - synaptics,pwr-reg-name: + name of analog power source regulator + - synaptics,irq-gpio: + interrupt hardware controller, gpio number, and flags + - synaptics,irq-on-state: + active state of interrupt signal + +Optional properties: + - synaptics,spi-mode: + spi mode + - synaptics,byte-delay-us: + inter-byte delay time in us + - synaptics,block-delay-us: + inter-block delay time in us + - synaptics,power-gpio: + hardware controller and gpio number of power control signal + - synaptics,power-delay-ms: + delay time in ms after powering on device + - synaptics,reset-gpio: + hardware controller and gpio number of reset signal + - synaptics,reset-delay-ms: + delay time in ms after issuing reset to device + - synaptics,reset-on-state: + active state of reset signal + - synaptics,reset-active-ms: + active duration in ms of reset signal + - synaptics,x-flip: + flip x axis + - synaptics,y-flip: + flip y axis + - synaptics,swap-axes: + swap x and y axes + - synaptics,ubl-max-freq: + maximum spi clock frequency for microbootloader mode + - synaptics,ubl-byte-delay-us: + inter-byte delay time in us for microbootloader mode + +Example: + synaptics_tcm@0 { + compatible = "synaptics,tcm-spi"; + reg = <0>; + spi-max-frequency = <10000000>; + interrupt-parent = <&msm_gpio>; + interrupts = <65 0x2008>; + vdd-supply = <&pm8994_lvs2>; + avdd-supply = <&pm8994_l22>; + pinctrl-names = "pmx_ts_active", "pmx_ts_suspend"; + pinctrl-0 = <&ts_active>; + pinctrl-1 = <&ts_suspend>; + synaptics,bus-reg-name = "vdd"; + synaptics,pwr-reg-name = "avdd"; + synaptics,irq-gpio = <&msm_gpio 65 0x2008>; + synaptics,irq-on-state = <0>; + synaptics,spi-mode = <3>; + synaptics,byte-delay-us = <0>; + synaptics,block-delay-us = <0>; + synaptics,power-delay-ms = <200>; + synaptics,reset-delay-ms = <200>; + synaptics,ubl-max-freq = <5000000>; + synaptics,ubl-byte-delay-us = <20>; + }; diff --git a/arch/arm64/boot/dts/vendor/bindings/sound/tas2560-algo.txt b/arch/arm64/boot/dts/vendor/bindings/sound/tas2560-algo.txt new file mode 100644 index 000000000000..0de051b46676 --- /dev/null +++ b/arch/arm64/boot/dts/vendor/bindings/sound/tas2560-algo.txt @@ -0,0 +1,14 @@ +Tas2560 algo driver + +Required properties: + + - compatible : "ti,tas2560-algo" + + - tas2560-port-id : afe port id for the mi2s used for tas2560. + +Example: + +ti,tas2560-algo { + compatible = "ti,tas2560-algo"; + ti,tas2560-port-id = <0x1016>, <0x1017>; +}; diff --git a/drivers/backlight/aw99703/Android.mk b/drivers/backlight/aw99703/Android.mk new file mode 100644 index 000000000000..dbc467d05587 --- /dev/null +++ b/drivers/backlight/aw99703/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := leds_aw99703.ko +LOCAL_MODULE_TAGS := optional + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/backlight/aw99703/Kbuild b/drivers/backlight/aw99703/Kbuild new file mode 100644 index 000000000000..0772225e16dc --- /dev/null +++ b/drivers/backlight/aw99703/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +obj-m += leds_aw99703.o diff --git a/drivers/backlight/aw99703/Makefile b/drivers/backlight/aw99703/Makefile new file mode 100644 index 000000000000..224435d57383 --- /dev/null +++ b/drivers/backlight/aw99703/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/backlight/aw99703/align_convert.h b/drivers/backlight/aw99703/align_convert.h new file mode 100644 index 000000000000..faa5dc8771aa --- /dev/null +++ b/drivers/backlight/aw99703/align_convert.h @@ -0,0 +1,2055 @@ +#ifndef _ALIGN_CONVERT_H_ +#define _ALIGN_CONVERT_H_ + +static unsigned int align_convert[2048] = { +0, +1, +1, +1, +1, +1, +25, +82, +131, +174, +212, +246, +277, +306, +332, +356, +379, +400, +421, +440, +458, +475, +491, +506, +521, +536, +549, +562, +575, +587, +599, +610, +621, +632, +642, +652, +662, +672, +681, +690, +699, +707, +716, +724, +732, +739, +747, +754, +762, +769, +776, +783, +789, +796, +802, +809, +815, +821, +827, +833, +839, +844, +850, +855, +861, +866, +872, +877, +882, +887, +892, +897, +901, +906, +911, +915, +920, +925, +929, +933, +938, +942, +946, +950, +954, +959, +963, +967, +970, +974, +978, +982, +986, +989, +993, +997, +1000, +1004, +1007, +1011, +1014, +1018, +1021, +1025, +1028, +1031, +1034, +1038, +1041, +1044, +1047, +1050, +1053, +1056, +1059, +1062, +1065, +1068, +1071, +1074, +1077, +1080, +1083, +1085, +1088, +1091, +1094, +1096, +1099, +1102, +1104, +1107, +1110, +1112, +1115, +1117, +1120, +1122, +1125, +1127, +1130, +1132, +1135, +1137, +1139, +1142, +1144, +1147, +1149, +1151, +1153, +1156, +1158, +1160, +1162, +1165, +1167, +1169, +1171, +1173, +1176, +1178, +1180, +1182, +1184, +1186, +1188, +1190, +1192, +1194, +1196, +1198, +1200, +1202, +1204, +1206, +1208, +1210, +1212, +1214, +1216, +1218, +1220, +1222, +1223, +1225, +1227, +1229, +1231, +1233, +1234, +1236, +1238, +1240, +1242, +1243, +1245, +1247, +1249, +1250, +1252, +1254, +1255, +1257, +1259, +1260, +1262, +1264, +1265, +1267, +1269, +1270, +1272, +1274, +1275, +1277, +1278, +1280, +1282, +1283, +1285, +1286, +1288, +1289, +1291, +1292, +1294, +1295, +1297, +1298, +1300, +1301, +1303, +1304, +1306, +1307, +1309, +1310, +1312, +1313, +1314, +1316, +1317, +1319, +1320, +1322, +1323, +1324, +1326, +1327, +1328, +1330, +1331, +1333, +1334, +1335, +1337, +1338, +1339, +1341, +1342, +1343, +1344, +1346, +1347, +1348, +1350, +1351, +1352, +1354, +1355, +1356, +1357, +1359, +1360, +1361, +1362, +1364, +1365, +1366, +1367, +1368, +1370, +1371, +1372, +1373, +1374, +1376, +1377, +1378, +1379, +1380, +1382, +1383, +1384, +1385, +1386, +1387, +1389, +1390, +1391, +1392, +1393, +1394, +1395, +1396, +1398, +1399, +1400, +1401, +1402, +1403, +1404, +1405, +1406, +1408, +1409, +1410, +1411, +1412, +1413, +1414, +1415, +1416, +1417, +1418, +1419, +1420, +1421, +1422, +1423, +1424, +1426, +1427, +1428, +1429, +1430, +1431, +1432, +1433, +1434, +1435, +1436, +1437, +1438, +1439, +1440, +1441, +1442, +1443, +1444, +1445, +1446, +1446, +1447, +1448, +1449, +1450, +1451, +1452, +1453, +1454, +1455, +1456, +1457, +1458, +1459, +1460, +1461, +1462, +1463, +1463, +1464, +1465, +1466, +1467, +1468, +1469, +1470, +1471, +1472, +1473, +1473, +1474, +1475, +1476, +1477, +1478, +1479, +1480, +1480, +1481, +1482, +1483, +1484, +1485, +1486, +1487, +1487, +1488, +1489, +1490, +1491, +1492, +1493, +1493, +1494, +1495, +1496, +1497, +1498, +1498, +1499, +1500, +1501, +1502, +1503, +1503, +1504, +1505, +1506, +1507, +1507, +1508, +1509, +1510, +1511, +1511, +1512, +1513, +1514, +1515, +1515, +1516, +1517, +1518, +1519, +1519, +1520, +1521, +1522, +1522, +1523, +1524, +1525, +1526, +1526, +1527, +1528, +1529, +1529, +1530, +1531, +1532, +1532, +1533, +1534, +1535, +1535, +1536, +1537, +1538, +1538, +1539, +1540, +1541, +1541, +1542, +1543, +1543, +1544, +1545, +1546, +1546, +1547, +1548, +1548, +1549, +1550, +1551, +1551, +1552, +1553, +1553, +1554, +1555, +1556, +1556, +1557, +1558, +1558, +1559, +1560, +1560, +1561, +1562, +1563, +1563, +1564, +1565, +1565, +1566, +1567, +1567, +1568, +1569, +1569, +1570, +1571, +1571, +1572, +1573, +1573, +1574, +1575, +1575, +1576, +1577, +1577, +1578, +1579, +1579, +1580, +1581, +1581, +1582, +1583, +1583, +1584, +1585, +1585, +1586, +1586, +1587, +1588, +1588, +1589, +1590, +1590, +1591, +1592, +1592, +1593, +1593, +1594, +1595, +1595, +1596, +1597, +1597, +1598, +1598, +1599, +1600, +1600, +1601, +1602, +1602, +1603, +1603, +1604, +1605, +1605, +1606, +1606, +1607, +1608, +1608, +1609, +1609, +1610, +1611, +1611, +1612, +1612, +1613, +1614, +1614, +1615, +1615, +1616, +1617, +1617, +1618, +1618, +1619, +1619, +1620, +1621, +1621, +1622, +1622, +1623, +1624, +1624, +1625, +1625, +1626, +1626, +1627, +1628, +1628, +1629, +1629, +1630, +1630, +1631, +1631, +1632, +1633, +1633, +1634, +1634, +1635, +1635, +1636, +1636, +1637, +1638, +1638, +1639, +1639, +1640, +1640, +1641, +1641, +1642, +1643, +1643, +1644, +1644, +1645, +1645, +1646, +1646, +1647, +1647, +1648, +1648, +1649, +1650, +1650, +1651, +1651, +1652, +1652, +1653, +1653, +1654, +1654, +1655, +1655, +1656, +1656, +1657, +1657, +1658, +1658, +1659, +1660, +1660, +1661, +1661, +1662, +1662, +1663, +1663, +1664, +1664, +1665, +1665, +1666, +1666, +1667, +1667, +1668, +1668, +1669, +1669, +1670, +1670, +1671, +1671, +1672, +1672, +1673, +1673, +1674, +1674, +1675, +1675, +1676, +1676, +1677, +1677, +1678, +1678, +1679, +1679, +1680, +1680, +1681, +1681, +1682, +1682, +1683, +1683, +1684, +1684, +1684, +1685, +1685, +1686, +1686, +1687, +1687, +1688, +1688, +1689, +1689, +1690, +1690, +1691, +1691, +1692, +1692, +1693, +1693, +1694, +1694, +1694, +1695, +1695, +1696, +1696, +1697, +1697, +1698, +1698, +1699, +1699, +1700, +1700, +1700, +1701, +1701, +1702, +1702, +1703, +1703, +1704, +1704, +1705, +1705, +1706, +1706, +1706, +1707, +1707, +1708, +1708, +1709, +1709, +1710, +1710, +1710, +1711, +1711, +1712, +1712, +1713, +1713, +1714, +1714, +1714, +1715, +1715, +1716, +1716, +1717, +1717, +1718, +1718, +1718, +1719, +1719, +1720, +1720, +1721, +1721, +1721, +1722, +1722, +1723, +1723, +1724, +1724, +1724, +1725, +1725, +1726, +1726, +1727, +1727, +1727, +1728, +1728, +1729, +1729, +1730, +1730, +1730, +1731, +1731, +1732, +1732, +1732, +1733, +1733, +1734, +1734, +1735, +1735, +1735, +1736, +1736, +1737, +1737, +1737, +1738, +1738, +1739, +1739, +1740, +1740, +1740, +1741, +1741, +1742, +1742, +1742, +1743, +1743, +1744, +1744, +1744, +1745, +1745, +1746, +1746, +1746, +1747, +1747, +1748, +1748, +1748, +1749, +1749, +1750, +1750, +1750, +1751, +1751, +1752, +1752, +1752, +1753, +1753, +1754, +1754, +1754, +1755, +1755, +1756, +1756, +1756, +1757, +1757, +1757, +1758, +1758, +1759, +1759, +1759, +1760, +1760, +1761, +1761, +1761, +1762, +1762, +1763, +1763, +1763, +1764, +1764, +1764, +1765, +1765, +1766, +1766, +1766, +1767, +1767, +1767, +1768, +1768, +1769, +1769, +1769, +1770, +1770, +1770, +1771, +1771, +1772, +1772, +1772, +1773, +1773, +1773, +1774, +1774, +1775, +1775, +1775, +1776, +1776, +1776, +1777, +1777, +1778, +1778, +1778, +1779, +1779, +1779, +1780, +1780, +1780, +1781, +1781, +1782, +1782, +1782, +1783, +1783, +1783, +1784, +1784, +1784, +1785, +1785, +1785, +1786, +1786, +1787, +1787, +1787, +1788, +1788, +1788, +1789, +1789, +1789, +1790, +1790, +1790, +1791, +1791, +1792, +1792, +1792, +1793, +1793, +1793, +1794, +1794, +1794, +1795, +1795, +1795, +1796, +1796, +1796, +1797, +1797, +1797, +1798, +1798, +1798, +1799, +1799, +1800, +1800, +1800, +1801, +1801, +1801, +1802, +1802, +1802, +1803, +1803, +1803, +1804, +1804, +1804, +1805, +1805, +1805, +1806, +1806, +1806, +1807, +1807, +1807, +1808, +1808, +1808, +1809, +1809, +1809, +1810, +1810, +1810, +1811, +1811, +1811, +1812, +1812, +1812, +1813, +1813, +1813, +1814, +1814, +1814, +1815, +1815, +1815, +1816, +1816, +1816, +1817, +1817, +1817, +1818, +1818, +1818, +1819, +1819, +1819, +1820, +1820, +1820, +1821, +1821, +1821, +1821, +1822, +1822, +1822, +1823, +1823, +1823, +1824, +1824, +1824, +1825, +1825, +1825, +1826, +1826, +1826, +1827, +1827, +1827, +1828, +1828, +1828, +1829, +1829, +1829, +1829, +1830, +1830, +1830, +1831, +1831, +1831, +1832, +1832, +1832, +1833, +1833, +1833, +1834, +1834, +1834, +1834, +1835, +1835, +1835, +1836, +1836, +1836, +1837, +1837, +1837, +1838, +1838, +1838, +1838, +1839, +1839, +1839, +1840, +1840, +1840, +1841, +1841, +1841, +1842, +1842, +1842, +1842, +1843, +1843, +1843, +1844, +1844, +1844, +1845, +1845, +1845, +1845, +1846, +1846, +1846, +1847, +1847, +1847, +1848, +1848, +1848, +1848, +1849, +1849, +1849, +1850, +1850, +1850, +1851, +1851, +1851, +1851, +1852, +1852, +1852, +1853, +1853, +1853, +1853, +1854, +1854, +1854, +1855, +1855, +1855, +1856, +1856, +1856, +1856, +1857, +1857, +1857, +1858, +1858, +1858, +1858, +1859, +1859, +1859, +1860, +1860, +1860, +1860, +1861, +1861, +1861, +1862, +1862, +1862, +1862, +1863, +1863, +1863, +1864, +1864, +1864, +1864, +1865, +1865, +1865, +1866, +1866, +1866, +1866, +1867, +1867, +1867, +1868, +1868, +1868, +1868, +1869, +1869, +1869, +1870, +1870, +1870, +1870, +1871, +1871, +1871, +1872, +1872, +1872, +1872, +1873, +1873, +1873, +1873, +1874, +1874, +1874, +1875, +1875, +1875, +1875, +1876, +1876, +1876, +1876, +1877, +1877, +1877, +1878, +1878, +1878, +1878, +1879, +1879, +1879, +1879, +1880, +1880, +1880, +1881, +1881, +1881, +1881, +1882, +1882, +1882, +1882, +1883, +1883, +1883, +1884, +1884, +1884, +1884, +1885, +1885, +1885, +1885, +1886, +1886, +1886, +1886, +1887, +1887, +1887, +1888, +1888, +1888, +1888, +1889, +1889, +1889, +1889, +1890, +1890, +1890, +1890, +1891, +1891, +1891, +1892, +1892, +1892, +1892, +1893, +1893, +1893, +1893, +1894, +1894, +1894, +1894, +1895, +1895, +1895, +1895, +1896, +1896, +1896, +1896, +1897, +1897, +1897, +1898, +1898, +1898, +1898, +1899, +1899, +1899, +1899, +1900, +1900, +1900, +1900, +1901, +1901, +1901, +1901, +1902, +1902, +1902, +1902, +1903, +1903, +1903, +1903, +1904, +1904, +1904, +1904, +1905, +1905, +1905, +1905, +1906, +1906, +1906, +1906, +1907, +1907, +1907, +1907, +1908, +1908, +1908, +1908, +1909, +1909, +1909, +1909, +1910, +1910, +1910, +1910, +1911, +1911, +1911, +1911, +1912, +1912, +1912, +1912, +1913, +1913, +1913, +1913, +1914, +1914, +1914, +1914, +1915, +1915, +1915, +1915, +1916, +1916, +1916, +1916, +1917, +1917, +1917, +1917, +1918, +1918, +1918, +1918, +1919, +1919, +1919, +1919, +1919, +1920, +1920, +1920, +1920, +1921, +1921, +1921, +1921, +1922, +1922, +1922, +1922, +1923, +1923, +1923, +1923, +1924, +1924, +1924, +1924, +1925, +1925, +1925, +1925, +1925, +1926, +1926, +1926, +1926, +1927, +1927, +1927, +1927, +1928, +1928, +1928, +1928, +1929, +1929, +1929, +1929, +1930, +1930, +1930, +1930, +1930, +1931, +1931, +1931, +1931, +1932, +1932, +1932, +1932, +1933, +1933, +1933, +1933, +1933, +1934, +1934, +1934, +1934, +1935, +1935, +1935, +1935, +1936, +1936, +1936, +1936, +1936, +1937, +1937, +1937, +1937, +1938, +1938, +1938, +1938, +1939, +1939, +1939, +1939, +1939, +1940, +1940, +1940, +1940, +1941, +1941, +1941, +1941, +1942, +1942, +1942, +1942, +1942, +1943, +1943, +1943, +1943, +1944, +1944, +1944, +1944, +1944, +1945, +1945, +1945, +1945, +1946, +1946, +1946, +1946, +1946, +1947, +1947, +1947, +1947, +1948, +1948, +1948, +1948, +1948, +1949, +1949, +1949, +1949, +1950, +1950, +1950, +1950, +1950, +1951, +1951, +1951, +1951, +1952, +1952, +1952, +1952, +1952, +1953, +1953, +1953, +1953, +1954, +1954, +1954, +1954, +1954, +1955, +1955, +1955, +1955, +1956, +1956, +1956, +1956, +1956, +1957, +1957, +1957, +1957, +1957, +1958, +1958, +1958, +1958, +1959, +1959, +1959, +1959, +1959, +1960, +1960, +1960, +1960, +1961, +1961, +1961, +1961, +1961, +1962, +1962, +1962, +1962, +1962, +1963, +1963, +1963, +1963, +1963, +1964, +1964, +1964, +1964, +1965, +1965, +1965, +1965, +1965, +1966, +1966, +1966, +1966, +1966, +1967, +1967, +1967, +1967, +1968, +1968, +1968, +1968, +1968, +1969, +1969, +1969, +1969, +1969, +1970, +1970, +1970, +1970, +1970, +1971, +1971, +1971, +1971, +1971, +1972, +1972, +1972, +1972, +1973, +1973, +1973, +1973, +1973, +1974, +1974, +1974, +1974, +1974, +1975, +1975, +1975, +1975, +1975, +1976, +1976, +1976, +1976, +1976, +1977, +1977, +1977, +1977, +1977, +1978, +1978, +1978, +1978, +1978, +1979, +1979, +1979, +1979, +1980, +1980, +1980, +1980, +1980, +1981, +1981, +1981, +1981, +1981, +1982, +1982, +1982, +1982, +1982, +1983, +1983, +1983, +1983, +1983, +1984, +1984, +1984, +1984, +1984, +1985, +1985, +1985, +1985, +1985, +1986, +1986, +1986, +1986, +1986, +1987, +1987, +1987, +1987, +1987, +1988, +1988, +1988, +1988, +1988, +1989, +1989, +1989, +1989, +1989, +1990, +1990, +1990, +1990, +1990, +1990, +1991, +1991, +1991, +1991, +1991, +1992, +1992, +1992, +1992, +1992, +1993, +1993, +1993, +1993, +1993, +1994, +1994, +1994, +1994, +1994, +1995, +1995, +1995, +1995, +1995, +1996, +1996, +1996, +1996, +1996, +1997, +1997, +1997, +1997, +1997, +1997, +1998, +1998, +1998, +1998, +1998, +1999, +1999, +1999, +1999, +1999, +2000, +2000, +2000, +2000, +2000, +2001, +2001, +2001, +2001, +2001, +2002, +2002, +2002, +2002, +2002, +2002, +2003, +2003, +2003, +2003, +2003, +2004, +2004, +2004, +2004, +2004, +2005, +2005, +2005, +2005, +2005, +2005, +2006, +2006, +2006, +2006, +2006, +2007, +2007, +2007, +2007, +2007, +2008, +2008, +2008, +2008, +2008, +2008, +2009, +2009, +2009, +2009, +2009, +2010, +2010, +2010, +2010, +2010, +2011, +2011, +2011, +2011, +2011, +2011, +2012, +2012, +2012, +2012, +2012, +2013, +2013, +2013, +2013, +2013, +2013, +2014, +2014, +2014, +2014, +2014, +2015, +2015, +2015, +2015, +2015, +2016, +2016, +2016, +2016, +2016, +2016, +2017, +2017, +2017, +2017, +2017, +2018, +2018, +2018, +2018, +2018, +2018, +2019, +2019, +2019, +2019, +2019, +2020, +2020, +2020, +2020, +2020, +2020, +2021, +2021, +2021, +2021, +2021, +2021, +2022, +2022, +2022, +2022, +2022, +2023, +2023, +2023, +2023, +2023, +2023, +2024, +2024, +2024, +2024, +2024, +2025, +2025, +2025, +2025, +2025, +2025, +2026, +2026, +2026, +2026, +2026, +2026, +2027, +2027, +2027, +2027, +2027, +2028, +2028, +2028, +2028, +2028, +2028, +2029, +2029, +2029, +2029, +2029, +2029, +2030, +2030, +2030, +2030, +2030, +2031, +2031, +2031, +2031, +2031, +2031, +2032, +2032, +2032, +2032, +2032, +2032, +2033, +2033, +2033, +2033, +2033, +2033, +2034, +2034, +2034, +2034, +2034, +2035, +2035, +2035, +2035, +2035, +2035, +2036, +2036, +2036, +2036, +2036, +2036, +2037, +2037, +2037, +2037, +2037, +2037, +2038, +2038, +2038, +2038, +2038, +2038, +2039, +2039, +2039, +2039, +2039, +2040, +2040, +2040, +2040, +2040, +2040, +2041, +2041, +2041, +2041, +2041, +2041, +2042, +2042, +2042, +2042, +2042, +2042, +2043, +2043, +2043, +2043, +2043, +2043, +2044, +2044, +2044, +2044, +2044, +2044, +2045, +2045, +2045, +2045, +2045, +2045, +2046, +2046, +2046, +2046, +2046, +2046, +2047 +}; + +#endif \ No newline at end of file diff --git a/drivers/backlight/aw99703/leds_aw99703.c b/drivers/backlight/aw99703/leds_aw99703.c new file mode 100644 index 000000000000..50fd54336b13 --- /dev/null +++ b/drivers/backlight/aw99703/leds_aw99703.c @@ -0,0 +1,824 @@ +/* +* aw99703.c aw99703 backlight module +* +* Version: v1.0.3 +* +* Copyright (c) 2019 AWINIC Technology CO., LTD +* +* Author: Joseph +* +* 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "leds_aw99703.h" +#include "align_convert.h" + +#define AW99703_LED_DEV "aw99703-bl" +#define AW99703_NAME "aw99703-bl" + +#define AW99703_VERSION "v1.0.3" + +struct aw99703_data *g_aw99703_data; + +static int platform_read_i2c_block(struct i2c_client *client, char *writebuf, + int writelen, char *readbuf, int readlen) +{ + int ret; + + if (writelen > 0) { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) + dev_err(&client->dev, "%s: i2c read error.\n", + __func__); + } else { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s:i2c read error.\n", __func__); + } + return ret; +} + +static int aw99703_i2c_read(struct i2c_client *client, u8 addr, u8 *val) +{ + return platform_read_i2c_block(client, &addr, 1, val, 1); +} + +static int platform_write_i2c_block(struct i2c_client *client, + char *writebuf, int writelen) +{ + int ret; + + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s: i2c write error.\n", __func__); + + return ret; +} + +static int aw99703_i2c_write(struct i2c_client *client, u8 addr, const u8 val) +{ + u8 buf[2] = {0}; + + buf[0] = addr; + buf[1] = val; + + return platform_write_i2c_block(client, buf, sizeof(buf)); +} +static void aw99703_hwen_pin_ctrl(struct aw99703_data *drvdata, int en) +{ + if (gpio_is_valid(drvdata->hwen_gpio)) { + if (en) { + pr_info("hwen pin is going to be high!---<%d>\n", en); + gpio_set_value(drvdata->hwen_gpio, true); + usleep_range(3500, 4000); + } else { + pr_info("hwen pin is going to be low!---<%d>\n", en); + gpio_set_value(drvdata->hwen_gpio, false); + usleep_range(1000, 2000); + } + } +} + +static int aw99703_gpio_init(struct aw99703_data *drvdata) +{ + + int ret; + + if (gpio_is_valid(drvdata->hwen_gpio)) { + ret = gpio_request(drvdata->hwen_gpio, "hwen_gpio"); + if (ret < 0) { + pr_err("failed to request gpio\n"); + return -1; + } + pr_info("gpio is valid %d!\n",drvdata->hwen_gpio); + aw99703_hwen_pin_ctrl(drvdata, 1); + } + + return 0; +} + +static int aw99703_i2c_write_bit(struct i2c_client *client, + unsigned int reg_addr, unsigned int mask, unsigned char reg_data) +{ + unsigned char reg_val = 0; + + aw99703_i2c_read(client, reg_addr, ®_val); + reg_val &= mask; + reg_val |= reg_data; + aw99703_i2c_write(client, reg_addr, reg_val); + + return 0; +} + +static int aw99703_brightness_map(unsigned int level) +{ + /*MAX_LEVEL_256*/ + if (g_aw99703_data->bl_map == 1) { + if (level == 255) + return 2047; + return level * 8; + } + /*MAX_LEVEL_1024*/ + if (g_aw99703_data->bl_map == 2) + return level * 2; + /*MAX_LEVEL_2048*/ + if (g_aw99703_data->bl_map == 3) + return level; + + return level; +} + +static int aw99703_bl_enable_channel(struct aw99703_data *drvdata) +{ + int ret = 0; + + if (drvdata->channel == 3) { + pr_info("%s turn all channel on!\n", __func__); + ret = aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_LEDCUR, + AW99703_LEDCUR_CHANNEL_MASK, + AW99703_LEDCUR_CH3_ENABLE | + AW99703_LEDCUR_CH2_ENABLE | + AW99703_LEDCUR_CH1_ENABLE); + } else if (drvdata->channel == 2) { + pr_info("%s turn two channel on!\n", __func__); + ret = aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_LEDCUR, + AW99703_LEDCUR_CHANNEL_MASK, + AW99703_LEDCUR_CH2_ENABLE | + AW99703_LEDCUR_CH1_ENABLE); + } else if (drvdata->channel == 1) { + pr_info("%s turn one channel on!\n", __func__); + ret = aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_LEDCUR, + AW99703_LEDCUR_CHANNEL_MASK, + AW99703_LEDCUR_CH1_ENABLE); + } else { + pr_info("%s all channels are going to be disabled\n", __func__); + ret = aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_LEDCUR, + AW99703_LEDCUR_CHANNEL_MASK, + 0x98); + } + + return ret; +} + +static void aw99703_pwm_mode_enable(struct aw99703_data *drvdata) +{ + if (drvdata->pwm_mode) { + aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_MODE, + AW99703_MODE_PDIS_MASK, + AW99703_MODE_PDIS_ENABLE); + pr_info("%s pwm_mode is enable\n", __func__); + } else { + aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_MODE, + AW99703_MODE_PDIS_MASK, + AW99703_MODE_PDIS_DISABLE); + pr_info("%s pwm_mode is disable\n", __func__); + } +} + +static void aw99703_ramp_setting(struct aw99703_data *drvdata) +{ + aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_TURNCFG, + AW99703_TURNCFG_ON_TIM_MASK, + drvdata->ramp_on_time << 4); + pr_info("%s drvdata->ramp_on_time is 0x%x\n", + __func__, drvdata->ramp_on_time); + + aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_TURNCFG, + AW99703_TURNCFG_OFF_TIM_MASK, + drvdata->ramp_off_time); + pr_info("%s drvdata->ramp_off_time is 0x%x\n", + __func__, drvdata->ramp_off_time); + +} +static void aw99703_transition_ramp(struct aw99703_data *drvdata) +{ + + pr_info("%s enter\n", __func__); + aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_TRANCFG, + AW99703_TRANCFG_PWM_TIM_MASK, + drvdata->pwm_trans_dim); + pr_info("%s drvdata->pwm_trans_dim is 0x%x\n", __func__, + drvdata->pwm_trans_dim); + + aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_TRANCFG, + AW99703_TRANCFG_I2C_TIM_MASK, + drvdata->i2c_trans_dim); + pr_info("%s drvdata->i2c_trans_dim is 0x%x\n", + __func__, drvdata->i2c_trans_dim); + +} + +static int aw99703_backlight_init(struct aw99703_data *drvdata) +{ + pr_info("%s enter.\n", __func__); + + aw99703_pwm_mode_enable(drvdata); + + /*mode:map type*/ + aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_MODE, + AW99703_MODE_MAP_MASK, + drvdata->map_type); + + /*default OVPSEL 38V*/ + aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_BSTCTR1, + AW99703_BSTCTR1_OVPSEL_MASK, + drvdata->ovp_level); + + /*switch frequency 1000kHz*/ + aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_BSTCTR1, + AW99703_BSTCTR1_SF_MASK, + AW99703_BSTCTR1_SF_1000KHZ); + + /*OCP SELECT*/ + aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_BSTCTR1, + AW99703_BSTCTR1_OCPSEL_MASK, + drvdata->ocp_level); + + /*BSTCRT2 IDCTSEL*/ + if (drvdata->bl_reconfig_enable && (AW99703_REG_BSTCTR2 == drvdata->bl_slow_reg)) { + pr_info("%s: REG_BSTCTR2 slow set\n", __func__); + aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_BSTCTR2, + AW99703_BSTCTR2_IDCTSEL_MASK | AW99703_BSTCTR2_EMISEL_MASK, + AW99703_BSTCTR2_IDCTSEL_10UH | AW99703_BSTCTR2_EMISEL_SLOW1); + } else { + aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_BSTCTR2, + AW99703_BSTCTR2_IDCTSEL_MASK, + AW99703_BSTCTR2_IDCTSEL_10UH); + } + + /*Backlight current full scale*/ + aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_LEDCUR, + AW99703_LEDCUR_BLFS_MASK, + drvdata->full_scale_led << 3); + + aw99703_bl_enable_channel(drvdata); + + aw99703_ramp_setting(drvdata); + aw99703_transition_ramp(drvdata); + + return 0; +} + +static int aw99703_backlight_enable(struct aw99703_data *drvdata) +{ + pr_info("%s enter.\n", __func__); + + aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_MODE, + AW99703_MODE_WORKMODE_MASK, + AW99703_MODE_WORKMODE_BACKLIGHT); + + drvdata->enable = true; + + return 0; +} + +int aw99703_set_brightness(struct aw99703_data *drvdata, int brt_val) +{ + pr_info("%s brt_val is %d\n", __func__, brt_val); + + if (drvdata->enable == false) { + if(brt_val == 0) + return 0; + aw99703_backlight_init(drvdata); + } + + brt_val = aw99703_brightness_map(brt_val); + + if((0 == drvdata->map_type) && (ALIGN_OLED == drvdata->led_current_align)) { + brt_val = align_convert[brt_val]; + pr_info("%s align convert brt_val is %d\n", __func__, brt_val); + } + + if (brt_val > 0) { + /*enalbe bl mode*/ + /* set backlight brt_val */ + aw99703_i2c_write(drvdata->client, + AW99703_REG_LEDLSB, + brt_val&0x0007); + aw99703_i2c_write(drvdata->client, + AW99703_REG_LEDMSB, + (brt_val >> 3)&0xff); + + /* backlight enable */ + aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_MODE, + AW99703_MODE_WORKMODE_MASK, + AW99703_MODE_WORKMODE_BACKLIGHT); + drvdata->enable = true; + } else { + /* standby mode*/ + aw99703_i2c_write_bit(drvdata->client, + AW99703_REG_MODE, + AW99703_MODE_WORKMODE_MASK, + AW99703_MODE_WORKMODE_STANDBY); + } + + drvdata->brightness = brt_val; + + if (drvdata->brightness == 0) + drvdata->enable = false; + return 0; +} + +#ifdef KERNEL_ABOVE_4_14 +static int aw99703_bl_get_brightness(struct backlight_device *bl_dev) +{ + return bl_dev->props.brightness; +} + +static int aw99703_bl_update_status(struct backlight_device *bl_dev) +{ + struct aw99703_data *drvdata = bl_get_data(bl_dev); + int brt; + + if (bl_dev->props.state & BL_CORE_SUSPENDED) + bl_dev->props.brightness = 0; + + brt = bl_dev->props.brightness; + /* + * Brightness register should always be written + * not only register based mode but also in PWM mode. + */ + return aw99703_set_brightness(drvdata, brt); +} + +static const struct backlight_ops aw99703_bl_ops = { + .update_status = aw99703_bl_update_status, + .get_brightness = aw99703_bl_get_brightness, +}; +#endif + +static int aw99703_read_chipid(struct aw99703_data *drvdata) +{ + int ret = -1; + u8 value = 0; + unsigned char cnt = 0; + + while (cnt < AW_READ_CHIPID_RETRIES) { + ret = aw99703_i2c_read(drvdata->client, 0x00, &value); + if (ret < 0) { + pr_err("%s: failed to read reg AW99703_REG_ID: %d\n", + __func__, ret); + } + switch (value) { + case 0x03: + pr_info("%s aw99703 detected\n", __func__); + return 0; + default: + pr_info("%s unsupported device revision (0x%x)\n", + __func__, value); + break; + } + cnt++; + + msleep(AW_READ_CHIPID_RETRY_DELAY); + } + + return -EINVAL; +} + + +static void __aw99703_work(struct aw99703_data *led, + enum led_brightness value) +{ + mutex_lock(&led->lock); + aw99703_set_brightness(led, value); + mutex_unlock(&led->lock); +} + +static void aw99703_work(struct work_struct *work) +{ + struct aw99703_data *drvdata = container_of(work, + struct aw99703_data, work); + + __aw99703_work(drvdata, drvdata->led_dev.brightness); +} + + +static void aw99703_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brt_val) +{ + struct aw99703_data *drvdata; + + drvdata = container_of(led_cdev, struct aw99703_data, led_dev); + schedule_work(&drvdata->work); +} + +static int aw99703_parse_dt(struct aw99703_data *drvdata) +{ + struct device_node *chosen; + int rc = -EINVAL; + + pr_info("%s enter\n", __func__); + chosen = of_find_node_by_name(NULL, "chosen"); + if (chosen) { + const char *supplier; + char *s, *d; + + rc = of_property_read_string(chosen, "mmi,panel_name", (const char **)&supplier); + if (rc) { + pr_info("%s: cannot read mmi,panel_name %d\n", __func__, rc); + } else { + int split_num = 0; + /* keep panel & ic info */ + s = (char *)supplier; + d = drvdata->panel_info; + while (*s && split_num < 2) { + *d++ = *s++; + if (*s == '_') + split_num++; + } + + pr_info("%s: panel_info %s\n", __func__, drvdata->panel_info); + } + of_node_put(chosen); + } + + return rc; +} + +static void +aw99703_get_dt_data(struct device *dev, struct aw99703_data *drvdata) +{ + int rc; + struct device_node *np = dev->of_node; + u32 bl_channel, temp; + + drvdata->hwen_gpio = of_get_named_gpio(np, "aw99703,hwen-gpio", 0); + pr_info("%s drvdata->hwen_gpio --<%d>\n", __func__, drvdata->hwen_gpio); + + rc = of_property_read_u32(np, "aw99703,pwm-mode", &drvdata->pwm_mode); + if (rc != 0) + pr_err("%s pwm-mode not found\n", __func__); + else + pr_info("%s pwm_mode=%d\n", __func__, drvdata->pwm_mode); + + rc = of_property_read_u32(np, "aw99703,map-type", &drvdata->map_type); + if (rc != 0) { + drvdata->map_type = AW99703_MODE_MAP_LINEAR; + pr_err("%s map-type not found\n", __func__); + } + else + pr_info("%s map-type=%d\n", __func__, drvdata->map_type); + + rc = of_property_read_u32(np, "current-align-type", &drvdata->led_current_align); + if (rc != 0) { + drvdata->led_current_align = ALIGN_NONE; + pr_err("%s current-align-type not found\n", __func__); + } + else + pr_info("%s current-align-type=%d\n", __func__, drvdata->led_current_align); + + drvdata->using_lsb = of_property_read_bool(np, "aw99703,using-lsb"); + pr_info("%s using_lsb --<%d>\n", __func__, drvdata->using_lsb); + + if (drvdata->using_lsb) { + drvdata->max_brightness = 2047; + } else { + drvdata->max_brightness = 255; + } + + rc = of_property_read_u32(np, "aw99703,default-brightness", &drvdata->default_brightness); + if (rc != 0) { + drvdata->default_brightness = drvdata->max_brightness; + pr_err("%s default-brightness not found, set to max %d\n", __func__, drvdata->default_brightness); + } + else + pr_info("%s default-brightness=%d\n", __func__, drvdata->default_brightness); + + rc = of_property_read_u32(np, "aw99703,bl-fscal-led", &temp); + if (rc) { + pr_err("Invalid backlight full-scale led current!\n"); + } else { + drvdata->full_scale_led = temp; + pr_info("%s full-scale led current --<%d mA>\n", + __func__, drvdata->full_scale_led); + } + + rc = of_property_read_u32(np, "aw99703,turn-on-ramp", &temp); + if (rc) { + pr_err("Invalid ramp timing ,turnon!\n"); + } else { + drvdata->ramp_on_time = temp; + pr_info("%s ramp on time --<%d ms>\n", + __func__, drvdata->ramp_on_time); + } + + rc = of_property_read_u32(np, "aw99703,turn-off-ramp", &temp); + if (rc) { + pr_err("Invalid ramp timing ,,turnoff!\n"); + } else { + drvdata->ramp_off_time = temp; + pr_info("%s ramp off time --<%d ms>\n", + __func__, drvdata->ramp_off_time); + } + + rc = of_property_read_u32(np, "aw99703,pwm-trans-dim", &temp); + if (rc) { + pr_err("Invalid pwm-tarns-dim value!\n"); + } else { + drvdata->pwm_trans_dim = temp; + pr_info("%s pwm trnasition dimming --<%d ms>\n", + __func__, drvdata->pwm_trans_dim); + } + + rc = of_property_read_u32(np, "aw99703,i2c-trans-dim", &temp); + if (rc) { + pr_err("Invalid i2c-trans-dim value !\n"); + } else { + drvdata->i2c_trans_dim = temp; + pr_info("%s i2c transition dimming --<%d ms>\n", + __func__, drvdata->i2c_trans_dim); + } + + rc = of_property_read_u32(np, "aw99703,bl-channel", &bl_channel); + if (rc) { + pr_err("Invalid channel setup\n"); + } else { + drvdata->channel = bl_channel; + pr_info("%s bl-channel --<%x>\n", __func__, drvdata->channel); + } + + rc = of_property_read_u32(np, "aw99703,bl-map", &drvdata->bl_map); + if (rc != 0) + pr_err("%s bl_map not found\n", __func__); + else + pr_info("%s bl_map=%d\n", __func__, drvdata->bl_map); + + rc = of_property_read_u32(np, "aw99703,ovp-level", &temp); + if (rc) { + drvdata->ovp_level = AW99703_BSTCTR1_OVPSEL_38V; + pr_err("ovp-level not found, set default %x!\n", drvdata->ovp_level); + } else { + drvdata->ovp_level = temp<<2; + pr_info("%s ovp_level %x\n", __func__, drvdata->ovp_level); + } + + rc = of_property_read_u32(np, "aw99703,ocp-level", &temp); + if (rc) { + drvdata->ocp_level = AW99703_BSTCTR1_OCPSEL_2P7A; + pr_err("ocp-level not found, set default %x!\n", drvdata->ocp_level); + } else { + drvdata->ocp_level = temp; + pr_info("%s ocp_level %x\n", __func__, drvdata->ocp_level); + } + + rc = of_property_read_u32(np, "aw99703,bl-slow-reg", &drvdata->bl_slow_reg); + if (!rc) { + const char *reconfg_panel; + + pr_info("%s bl_slow_reg=%d\n", __func__, drvdata->bl_slow_reg); + rc = of_property_read_string(np, "aw99703,bl-reconfig-panel", (const char **)&reconfg_panel); + if (rc) { + /* enable for all panels */ + drvdata->bl_reconfig_enable = true; + } + else { + rc = aw99703_parse_dt(drvdata); + if (!rc && strstr(drvdata->panel_info, reconfg_panel)) { + /* enable for matched panel */ + drvdata->bl_reconfig_enable = true; + } + } + pr_info("%s bl_reconfig_enable=%d\n", __func__, drvdata->bl_reconfig_enable); + } + +} + +/****************************************************** + * + * sys group attribute: reg + * + ******************************************************/ +static ssize_t aw99703_i2c_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw99703_data *aw99703 = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0, 0}; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw99703_i2c_write(aw99703->client, + (unsigned char)databuf[0], + (unsigned char)databuf[1]); + } + + return count; +} + +static ssize_t aw99703_i2c_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw99703_data *aw99703 = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + for (i = 0; i < AW99703_REG_MAX; i++) { + if (!(aw99703_reg_access[i]®_RD_ACCESS)) + continue; + aw99703_i2c_read(aw99703->client, i, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, "reg:0x%02x=0x%02x\n", + i, reg_val); + } + + return len; +} + +static DEVICE_ATTR(reg, 0664, aw99703_i2c_reg_show, aw99703_i2c_reg_store); +static struct attribute *aw99703_attributes[] = { + &dev_attr_reg.attr, + NULL +}; + +static struct attribute_group aw99703_attribute_group = { + .attrs = aw99703_attributes +}; + +static int aw99703_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct aw99703_data *drvdata; +#ifdef KERNEL_ABOVE_4_14 + struct backlight_device *bl_dev; + struct backlight_properties props; +#endif + int err = 0; + pr_err("%s enter! driver version %s\n", __func__, AW99703_VERSION); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("%s : I2C_FUNC_I2C not supported\n", __func__); + err = -EIO; + goto err_out; + } + + if (!client->dev.of_node) { + pr_err("%s : no device node\n", __func__); + err = -ENOMEM; + goto err_out; + } + + drvdata = kzalloc(sizeof(struct aw99703_data), GFP_KERNEL); + if (drvdata == NULL) { + pr_err("%s : kzalloc failed\n", __func__); + err = -ENOMEM; + goto err_out; + } + + drvdata->client = client; + drvdata->adapter = client->adapter; + drvdata->addr = client->addr; + drvdata->brightness = LED_OFF; + drvdata->enable = true; + drvdata->led_dev.default_trigger = "bkl-trigger"; + drvdata->led_dev.name = AW99703_LED_DEV; + drvdata->led_dev.brightness_set = aw99703_brightness_set; + drvdata->led_dev.max_brightness = MAX_BRIGHTNESS; + mutex_init(&drvdata->lock); + INIT_WORK(&drvdata->work, aw99703_work); + aw99703_get_dt_data(&client->dev, drvdata); + i2c_set_clientdata(client, drvdata); + aw99703_gpio_init(drvdata); + + err = aw99703_read_chipid(drvdata); + if (err < 0) { + pr_err("%s : ID idenfy failed\n", __func__); + goto err_init; + } + + err = led_classdev_register(&client->dev, &drvdata->led_dev); + if (err < 0) { + pr_err("%s : Register led class failed\n", __func__); + err = -ENODEV; + goto err_init; + } else { + pr_debug("%s: Register led class successful\n", __func__); + } + +#ifdef KERNEL_ABOVE_4_14 + memset(&props, 0, sizeof(struct backlight_properties)); + props.type = BACKLIGHT_PLATFORM; + props.brightness = MAX_BRIGHTNESS; + props.max_brightness = MAX_BRIGHTNESS; + bl_dev = backlight_device_register(AW99703_NAME, &client->dev, + drvdata, &aw99703_bl_ops, &props); +#endif + + g_aw99703_data = drvdata; + aw99703_backlight_init(drvdata); + aw99703_backlight_enable(drvdata); + + aw99703_set_brightness(drvdata, drvdata->default_brightness); + err = sysfs_create_group(&client->dev.kobj, &aw99703_attribute_group); + if (err < 0) { + dev_info(&client->dev, "%s error creating sysfs attr files\n", + __func__); + goto err_sysfs; + } + pr_info("%s exit\n", __func__); + return 0; + +err_sysfs: +err_init: + gpio_free(drvdata->hwen_gpio); + kfree(drvdata); +err_out: + return err; +} + +static int aw99703_remove(struct i2c_client *client) +{ + struct aw99703_data *drvdata = i2c_get_clientdata(client); + + led_classdev_unregister(&drvdata->led_dev); + + kfree(drvdata); + return 0; +} + +static const struct i2c_device_id aw99703_id[] = { + {AW99703_NAME, 0}, + {} +}; +static struct of_device_id match_table[] = { + {.compatible = "awinic,aw99703-bl",}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, aw99703_id); + +static struct i2c_driver aw99703_i2c_driver = { + .probe = aw99703_probe, + .remove = aw99703_remove, + .id_table = aw99703_id, + .driver = { + .name = AW99703_NAME, + .owner = THIS_MODULE, + .of_match_table = match_table, + }, +}; + +module_i2c_driver(aw99703_i2c_driver); +MODULE_DESCRIPTION("Back Light driver for aw99703"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/backlight/aw99703/leds_aw99703.h b/drivers/backlight/aw99703/leds_aw99703.h new file mode 100644 index 000000000000..5d90a11d79a2 --- /dev/null +++ b/drivers/backlight/aw99703/leds_aw99703.h @@ -0,0 +1,263 @@ +#ifndef _AW99703_REG_H_ +#define _AW99703_REG_H_ + +/********************************************************* + * + * kernel version + * + ********************************************************/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) +#define KERNEL_ABOVE_4_14 +#endif + +/******************************************** + * Register List + *******************************************/ +#define AW99703_REG_MAX 0x70 +#define AW99703_REG_ID 0x00 +#define AW99703_REG_SFTRST 0x01 +#define AW99703_REG_MODE 0x02 +#define AW99703_REG_LEDCUR 0x03 +#define AW99703_REG_BSTCTR1 0x04 +#define AW99703_REG_BSTCTR2 0x05 +#define AW99703_REG_LEDLSB 0x06 +#define AW99703_REG_LEDMSB 0x07 +#define AW99703_REG_PWM 0x08 +#define AW99703_REG_TURNCFG 0x09 +#define AW99703_REG_TRANCFG 0x0a +#define AW99703_REG_FLASH 0x0b +#define AW99703_REG_AFHIGH 0x0c +#define AW99703_REG_AFLOW 0x0d +#define AW99703_REG_FLAGS1 0x0e +#define AW99703_REG_FLAGS2 0x0f +#define AW99703_REG_FLAGS3 0x11 +#define AW99703_REG_AUTOZERO 0x21 +#define AW99703_REG_EMI 0x22 +#define AW99703_REG_BSTCTR3 0x23 +#define AW99703_REG_BSTCTR4 0x24 +#define AW99703_REG_BSTCTR5 0x25 +#define AW99703_REG_LEDCFG 0x26 +#define AW99703_REG_DITHER 0x27 +#define AW99703_REG_PWMMSB 0x28 +#define AW99703_REG_PWMLSB 0x29 +#define AW99703_REG_TEST 0x31 +#define AW99703_REG_FLTDIS 0x33 +#define AW99703_REG_EFUSE1 0x40 +#define AW99703_REG_EFUSE2 0x41 +#define AW99703_REG_EFUSE3 0x42 +#define AW99703_REG_EFUSE4 0x43 +#define AW99703_REG_EFUSE5 0x44 +#define AW99703_REG_EFUSE6 0x45 +#define AW99703_REG_EFUSE7 0x46 +#define AW99703_REG_EFUSE8 0x47 +#define AW99703_REG_EFRUN 0x48 +#define AW99703_REG_EFMODE 0x49 +#define AW99703_REG_WPRT1 0x67 +#define AW99703_REG_WPRT2 0x68 +#define AW99703_REG_SCANEN 0x69 + + +/******************************************** + * Register Access + *******************************************/ +#define REG_NONE_ACCESS 0 +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) + +const unsigned char aw99703_reg_access[AW99703_REG_MAX] = { + [AW99703_REG_ID] = REG_RD_ACCESS, + [AW99703_REG_SFTRST] = REG_WR_ACCESS, + [AW99703_REG_MODE] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW99703_REG_LEDCUR] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW99703_REG_BSTCTR1] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW99703_REG_BSTCTR2] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW99703_REG_LEDLSB] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW99703_REG_LEDMSB] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW99703_REG_PWM] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW99703_REG_TURNCFG] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW99703_REG_TRANCFG] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW99703_REG_FLASH] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW99703_REG_AFHIGH] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW99703_REG_AFLOW] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW99703_REG_FLAGS1] = REG_RD_ACCESS, + [AW99703_REG_FLAGS2] = REG_RD_ACCESS, + [AW99703_REG_FLAGS3] = REG_RD_ACCESS, + [AW99703_REG_AUTOZERO] = REG_RD_ACCESS, + [AW99703_REG_EMI] = REG_RD_ACCESS, + [AW99703_REG_BSTCTR3] = REG_RD_ACCESS, + [AW99703_REG_BSTCTR4] = REG_RD_ACCESS, + [AW99703_REG_BSTCTR5] = REG_RD_ACCESS, + [AW99703_REG_LEDCFG] = REG_RD_ACCESS, + [AW99703_REG_DITHER] = REG_RD_ACCESS, + [AW99703_REG_PWMMSB] = REG_RD_ACCESS, + [AW99703_REG_PWMLSB] = REG_RD_ACCESS, + [AW99703_REG_TEST] = REG_RD_ACCESS, + [AW99703_REG_FLTDIS] = REG_RD_ACCESS, + [AW99703_REG_EFUSE1] = REG_RD_ACCESS, + [AW99703_REG_EFUSE2] = REG_RD_ACCESS, + [AW99703_REG_EFUSE3] = REG_RD_ACCESS, + [AW99703_REG_EFUSE4] = REG_RD_ACCESS, + [AW99703_REG_EFUSE5] = REG_RD_ACCESS, + [AW99703_REG_EFUSE6] = REG_RD_ACCESS, + [AW99703_REG_EFUSE7] = REG_RD_ACCESS, + [AW99703_REG_EFUSE8] = REG_RD_ACCESS, + [AW99703_REG_EFRUN] = REG_RD_ACCESS, + [AW99703_REG_EFMODE] = REG_RD_ACCESS, + [AW99703_REG_WPRT1] = REG_RD_ACCESS, + [AW99703_REG_WPRT2] = REG_RD_ACCESS, + [AW99703_REG_SCANEN] = REG_RD_ACCESS, +}; + +#define MAX_BRIGHTNESS (2047) +/****************************************************** +* Register Detail +*****************************************************/ +/*SFTRST:0x01*/ +#define AW99703_SFTRST_MASK (~(1<<0)) +#define AW99703_SFTRST_NOT_RST (0<<0) +#define AW99703_SFTRST_RST (1<<0) + +/*MODE:0x02*/ +#define AW99703_MODE_PDIS_MASK (~(1<<4)) +#define AW99703_MODE_PDIS_ENABLE (0<<4) +#define AW99703_MODE_PDIS_DISABLE (1<<4) +#define AW99703_MODE_MAP_MASK (~(1<<2)) +#define AW99703_MODE_MAP_EXPONENTIAL (0<<2) +#define AW99703_MODE_MAP_LINEAR (1<<2) +#define AW99703_MODE_WORKMODE_MASK (~(3<<0)) +#define AW99703_MODE_WORKMODE_STANDBY (0<<0) +#define AW99703_MODE_WORKMODE_BACKLIGHT (1<<0) +#define AW99703_MODE_WORKMODE_FLASH (2<<0) + +/*MODE:0x03*/ +#define AW99703_LEDCUR_BLFS_MASK (~(31<<3)) +#define AW99703_LEDCUR_CHANNEL_MASK (~(7<<0)) +#define AW99703_LEDCUR_CH3_ENABLE (1<<2) +#define AW99703_LEDCUR_CH3_DISABLE (0<<2) +#define AW99703_LEDCUR_CH2_ENABLE (1<<1) +#define AW99703_LEDCUR_CH2_DISABLE (0<<1) +#define AW99703_LEDCUR_CH1_ENABLE (1<<0) +#define AW99703_LEDCUR_CH1_DISABLE (0<<0) + +/*BSTCTR1:0x04*/ +#define AW99703_BSTCTR1_SF_SFT_MASK (~(3<<6)) +#define AW99703_BSTCTR1_SF_SFT_UP20 (1<<6) +#define AW99703_BSTCTR1_SF_SFT_DOWN12 (2<<6) +#define AW99703_BSTCTR1_SF_SFT_DOWN24 (3<<6) +#define AW99703_BSTCTR1_SF_MASK (~(1<<5)) +#define AW99703_BSTCTR1_SF_500KHZ (0<<5) +#define AW99703_BSTCTR1_SF_1000KHZ (1<<5) +#define AW99703_BSTCTR1_OVPSEL_MASK (~(7<<2)) +#define AW99703_BSTCTR1_OVPSEL_17V (0<<2) +#define AW99703_BSTCTR1_OVPSEL_24V (1<<2) +#define AW99703_BSTCTR1_OVPSEL_31V (2<<2) +#define AW99703_BSTCTR1_OVPSEL_38V (3<<2) +#define AW99703_BSTCTR1_OVPSEL_41P5V (4<<2) +#define AW99703_BSTCTR1_OCPSEL_MASK (~(3<<0)) +#define AW99703_BSTCTR1_OCPSEL_0P9A (0<<0) +#define AW99703_BSTCTR1_OCPSEL_1P8A (1<<0) +#define AW99703_BSTCTR1_OCPSEL_2P7A (2<<0) +#define AW99703_BSTCTR1_OCPSEL_3P4A (3<<0) + +/*BSTCTR2:0x05*/ +#define AW99703_BSTCTR2_AFEN_MASK (~(1<<7)) +#define AW99703_BSTCTR2_AFEN_ENABLE (1<<7) +#define AW99703_BSTCTR2_AFEN_DISABLE (0<<7) +#define AW99703_BSTCTR2_IDCTSEL_MASK (~(1<<6)) +#define AW99703_BSTCTR2_IDCTSEL_4P7UH (0<<6) +#define AW99703_BSTCTR2_IDCTSEL_10UH (1<<6) +#define AW99703_BSTCTR2_EMISEL_MASK (~(7<<3)) +#define AW99703_BSTCTR2_EMISEL_TYPICAL (0<<3) +#define AW99703_BSTCTR2_EMISEL_SLOW1 (1<<3) +#define AW99703_BSTCTR2_EMISEL_SLOW2 (2<<3) +#define AW99703_BSTCTR2_EMISEL_SLOW3 (3<<3) +#define AW99703_BSTCTR2_EMISEL_FAST1 (4<<3) +#define AW99703_BSTCTR2_EMISEL_FAST2 (5<<3) +#define AW99703_BSTCTR2_EMISEL_FAST3 (6<<3) +#define AW99703_BSTCTR2_EMISEL_FAST4 (7<<3) +#define AW99703_BSTCTR2_ADEN_MASK (~(1<<2)) +#define AW99703_BSTCTR2_ADEN_ENABLE (1<<2) +#define AW99703_BSTCTR2_ADEN_DISABLE (0<<2) + +/*PWM:0x08*/ +#define AW99703_PWM_P_SF_MASK (~(3<<6)) +#define AW99703_PWM_P_SF_800KHZ (0<<6) +#define AW99703_PWM_P_SF_4MKHZ (1<<6) +#define AW99703_PWM_P_SF_24MKHZ (2<<6) +#define AW99703_PWM_P_ACT_MASK (~(1<<5)) +#define AW99703_PWM_P_ACT_HIGH (0<<5) +#define AW99703_PWM_P_ACT_LOW (1<<5) +#define AW99703_PWM_P_HYS_MASK (~(7<<2)) +#define AW99703_PWM_P_HYS_NONE (0<<2) +#define AW99703_PWM_P_HYS_1LSB (1<<2) +#define AW99703_PWM_P_HYS_2LSB (2<<2) +#define AW99703_PWM_P_HYS_3LSB (3<<2) +#define AW99703_PWM_P_HYS_4LSB (4<<2) +#define AW99703_PWM_P_HYS_5LSB (5<<2) +#define AW99703_PWM_P_HYS_6LSB (6<<2) +#define AW99703_PWM_P_HYS_7LSB (7<<2) +#define AW99703_PWM_P_FLT_MASK (~(3<<0)) +#define AW99703_PWM_P_FLT_NONE (0<<0) +#define AW99703_PWM_P_FLT_100MS (1<<0) +#define AW99703_PWM_P_FLT_150MS (2<<0) +#define AW99703_PWM_P_FLT_200MS (3<<0) + +/*TURNCFG:0x09*/ +#define AW99703_TURNCFG_ON_TIM_MASK (~(15<<4)) +#define AW99703_TURNCFG_OFF_TIM_MASK (~(15<<0)) + +/*TRANCFG:0x0A*/ +#define AW99703_TRANCFG_PWM_TIM_MASK (~(7<<4)) +#define AW99703_TRANCFG_I2C_TIM_MASK (~(15<<0)) + +#define MAX_BRIGHTNESS (2047) +#define AW_READ_CHIPID_RETRIES 5 +#define AW_READ_CHIPID_RETRY_DELAY 2 + +enum backlight_exp_current_align { + ALIGN_NONE, + ALIGN_OLED +}; + +struct aw99703_data { + struct led_classdev led_dev; + struct i2c_client *client; + struct device dev; + struct i2c_adapter *adapter; + unsigned short addr; + struct mutex lock; + struct work_struct work; + enum led_brightness brightness; + bool enable; + unsigned char pwm_cfg; + unsigned char full_scale_current; + bool brt_code_enable; + unsigned short *brt_code_table; + int hwen_gpio; + unsigned int pwm_mode; + unsigned int map_type; + bool using_lsb; + bool bl_reconfig_enable; + char panel_info[16]; + unsigned int bl_slow_reg; + unsigned int pwm_period; + unsigned int full_scale_led; + unsigned int ramp_on_time; + unsigned int ramp_off_time; + unsigned int pwm_trans_dim; + unsigned int i2c_trans_dim; + unsigned int channel; + unsigned int ovp_level; + unsigned int ocp_level; + unsigned int frequency; + unsigned int led_current_align; + unsigned int default_brightness; + unsigned int max_brightness; + unsigned int induct_current; + unsigned int flash_current; + unsigned int flash_timeout; + unsigned int bl_map; + struct backlight_device *bl_dev; +}; + +#endif diff --git a/drivers/backlight/ktd3136/Android.mk b/drivers/backlight/ktd3136/Android.mk new file mode 100644 index 000000000000..d16a842d4ee2 --- /dev/null +++ b/drivers/backlight/ktd3136/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := ktd3136_bl.ko +LOCAL_MODULE_TAGS := optional + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/backlight/ktd3136/Kbuild b/drivers/backlight/ktd3136/Kbuild new file mode 100644 index 000000000000..b4cf2eb7e7fe --- /dev/null +++ b/drivers/backlight/ktd3136/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +obj-m += ktd3136_bl.o diff --git a/drivers/backlight/ktd3136/Makefile b/drivers/backlight/ktd3136/Makefile new file mode 100644 index 000000000000..123bea3f132d --- /dev/null +++ b/drivers/backlight/ktd3136/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/backlight/ktd3136/ktd3136_bl.c b/drivers/backlight/ktd3136/ktd3136_bl.c new file mode 100644 index 000000000000..f6872b261864 --- /dev/null +++ b/drivers/backlight/ktd3136/ktd3136_bl.c @@ -0,0 +1,704 @@ +/* + * KTD3136_BL Driver + * + * SiliconMitus KTD3136 Backlight driver chip + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_FB) +#include +#include +#endif + +#include "ktd3136_bl.h" + +#define KTD3136_LED_DEV "ktd3136-BL" +#define KTD3136_NAME "ktd3136-bl" +#define KTD3136_CHIP_ID 0x18 + +static int platform_read_i2c_block(struct i2c_client *client, char *writebuf, + int writelen, char *readbuf, int readlen) +{ + int ret; + + if (writelen > 0) { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) + dev_err(&client->dev, "%s: i2c read error.\n", + __func__); + } else { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s:i2c read error.\n", __func__); + } + return ret; +} + +static int ktd3136_read_reg(struct i2c_client *client, u8 addr, u8 *val) +{ + return platform_read_i2c_block(client, &addr, 1, val, 1); +} + +static int platform_write_i2c_block(struct i2c_client *client, char *writebuf, int writelen) +{ + int ret; + + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s: i2c write error.\n", __func__); + + return ret; +} + +static int ktd3136_write_reg(struct i2c_client *client, u8 addr, const u8 val) +{ + u8 buf[2] = {0}; + + buf[0] = addr; + buf[1] = val; + return platform_write_i2c_block(client, buf, sizeof(buf)); +} +static void ktd3136_hwen_pin_ctrl(struct ktd3136_data *drvdata, int en) +{ + if (gpio_is_valid(drvdata->hwen_gpio)) { + if (en) { + pr_debug("hwen pin is going to be high!---<%d>\n", en); + gpio_set_value(drvdata->hwen_gpio, true); + } else { + pr_debug("hwen pin is going to be low!---<%d>\n", en); + gpio_set_value(drvdata->hwen_gpio, false); + } + msleep(1); + } +} + +static int ktd3136_gpio_init(struct ktd3136_data *drvdata) +{ + int ret; + + pr_info("%s enter\n", __func__); + if (gpio_is_valid(drvdata->hwen_gpio)) { + ret = gpio_request(drvdata->hwen_gpio, "ktd_hwen_gpio"); + if (ret<0) { + pr_err("failed to request gpio\n"); + return -1; + } + ret = gpio_direction_output(drvdata->hwen_gpio, 0); + pr_debug(" request gpio init\n"); + if (ret<0) { + pr_err("failed to set output"); + gpio_free(drvdata->hwen_gpio); + return ret; + } + pr_debug("gpio is valid!\n"); + ktd3136_hwen_pin_ctrl(drvdata, 1); + } + + pr_info("%s exit\n", __func__); + return 0; +} +static int ktd3136_masked_write(struct i2c_client *client, int reg, u8 mask, u8 val) +{ + int rc; + u8 temp; + + rc = ktd3136_read_reg(client, reg, &temp); + if (rc < 0) { + pr_err("failed to read reg=0x%x, rc=%d\n", reg, rc); + } else { + temp &= ~mask; + temp |= val & mask; + rc = ktd3136_write_reg(client, reg, temp); + if (rc<0) { + pr_err( "failed to write masked data. reg=%03x, rc=%d\n", reg, rc); + } + } + + ktd3136_read_reg(client, reg, &temp); + return rc; +} + +static int ktd3136_bl_enable_channel(struct ktd3136_data *drvdata) +{ + int ret = -1; + + if (drvdata->channel == 0) { + //default value for mode Register, all channel disabled. + pr_debug("all channels are going to be disabled\n"); + ret = ktd3136_write_reg(drvdata->client, REG_PWM, 0x18);//b0001 1000 + } else if (drvdata->channel == 3) { + pr_debug("turn all channel on!\n"); + ret = ktd3136_masked_write(drvdata->client, REG_PWM, 0x07, 0x07); + } else if (drvdata->channel == 2) { + ret = ktd3136_masked_write(drvdata->client, REG_PWM, 0x07, 0x03); + } + + return ret; +} +static void ktd3136_pwm_mode_enable(struct ktd3136_data *drvdata, bool en) +{ + u8 value; + + if (en) { + if (drvdata->pwm_mode) { + pr_debug("already activated!\n"); + } else { + drvdata->pwm_mode = en; + } + ktd3136_masked_write(drvdata->client, REG_PWM, 0x80, 0x80); + } else { + if (drvdata->pwm_mode) { + drvdata->pwm_mode = en; + } + ktd3136_masked_write(drvdata->client, REG_PWM, 0x80, 0x00); + } + + ktd3136_read_reg(drvdata->client, REG_PWM, &value); + pr_debug("current pwm_mode is --<%x>\n", value); +} +static int ktd_find_bit(int x) +{ + int i = 0; + + while ((x = x >> 1)) + i++; + + return i+1; +} + +static void ktd3136_ramp_setting(struct ktd3136_data *drvdata) +{ + unsigned int max_time = 16384; + int temp = 0; + + if (drvdata->ramp_on_time == 0) {//512us + ktd3136_masked_write(drvdata->client, REG_RAMP_ON, 0xf0, 0x00); + pr_debug("rampon time is 0 \n"); + } else if (drvdata->ramp_on_time > max_time) { + ktd3136_masked_write(drvdata->client, REG_RAMP_ON, 0xf0, 0xf0); + pr_debug("rampon time is max \n"); + } else { + temp = ktd_find_bit(drvdata->ramp_on_time); + ktd3136_masked_write(drvdata->client, REG_RAMP_ON, 0xf0, temp<<4); + pr_debug("temp is %d\n", temp); + } + + if (drvdata->ramp_off_time == 0) {//512us + ktd3136_masked_write(drvdata->client, REG_RAMP_ON, 0x0f, 0x00); + pr_debug("rampoff time is 0 \n"); + } else if (drvdata->ramp_off_time > max_time) { + ktd3136_masked_write(drvdata->client, REG_RAMP_ON, 0x0f, 0x0f); + pr_debug("rampoff time is max \n"); + } else { + temp = ktd_find_bit(drvdata->ramp_off_time); + ktd3136_masked_write(drvdata->client, REG_RAMP_ON, 0x0f, temp); + pr_debug("temp is %d\n", temp); + } + +} +static void ktd3136_transition_ramp(struct ktd3136_data *drvdata) +{ + int reg_i2c, reg_pwm, temp; + + if (drvdata->i2c_trans_dim >= 1024) { + reg_i2c = 0xf; + } else if (drvdata->i2c_trans_dim < 128) { + reg_i2c = 0x0; + } else { + temp =drvdata->i2c_trans_dim/64; + reg_i2c = temp-1; + pr_debug("reg_i2c is --<0x%x>\n", reg_i2c); + } + + if(drvdata->pwm_trans_dim >= 256){ + reg_pwm = 0x7; + }else if(drvdata->pwm_trans_dim < 4){ + reg_pwm = 0x0; + }else{ + temp = ktd_find_bit(drvdata->pwm_trans_dim); + reg_pwm = temp -2; + pr_debug("temp is %d\n", temp); + } + + ktd3136_masked_write(drvdata->client, REG_TRANS_RAMP, 0x70, reg_pwm); + ktd3136_masked_write(drvdata->client, REG_TRANS_RAMP, 0x0f, reg_i2c); + +} + +static int ktd3136_backlight_init(struct ktd3136_data *drvdata) +{ + int err = 0; + u8 value; + u8 update_value; + pr_info("%s enter.\n", __func__); + update_value = (drvdata->ovp_level == 32) ? 0x20 : 0x00; + (drvdata->induct_current == 2600) ? update_value |=0x08 : update_value; + (drvdata->frequency == 1000) ? update_value |=0x40: update_value; + + ktd3136_write_reg(drvdata->client, REG_CONTROL, update_value | 0x06); /* Linear default*/ + ktd3136_bl_enable_channel(drvdata); + if (drvdata->pwm_mode) { + ktd3136_pwm_mode_enable(drvdata, true); + } else { + ktd3136_pwm_mode_enable(drvdata, false); + } + ktd3136_ramp_setting(drvdata); + ktd3136_transition_ramp(drvdata); + ktd3136_read_reg(drvdata->client, REG_CONTROL, &value); + pr_debug("read control register -before--<0x%x> -after--<0x%x> \n", + update_value, value); + + pr_info("%s exit\n", __func__); + return err; +} + +static int ktd3136_backlight_enable(struct ktd3136_data *drvdata) +{ + int err = 0; + pr_info("%s enter.\n", __func__); + ktd3136_masked_write(drvdata->client, REG_MODE, 0xf8, drvdata->full_scale_led); + drvdata->enable = true; + + return err; +} + +int ktd3136_set_brightness(struct ktd3136_data *drvdata, int brt_val) +{ + pr_info("%s brt_val is %d\n", __func__, brt_val); + + if (drvdata->enable == false) + ktd3136_backlight_init(drvdata); + + if (brt_val>0) { + ktd3136_masked_write(drvdata->client, REG_MODE, 0x01, 0x01); //enalbe bl mode + } else { + ktd3136_masked_write(drvdata->client, REG_MODE, 0x01, 0x00); //disable bl mode + } + if (drvdata->using_lsb) { + ktd3136_masked_write(drvdata->client, REG_RATIO_LSB, 0x07, brt_val); + ktd3136_masked_write(drvdata->client, REG_RATIO_MSB, 0xff, brt_val>>3); + } else { + ktd3136_masked_write(drvdata->client, REG_RATIO_LSB, 0x07, ktd3136_brightness_table_reg4[brt_val]); + ktd3136_masked_write(drvdata->client, REG_RATIO_MSB, 0xff, ktd3136_brightness_table_reg5[brt_val]); + } + + if (drvdata->enable == false) + ktd3136_backlight_enable(drvdata); + + drvdata->brightness = brt_val; + + if (drvdata->brightness == 0) + drvdata->enable = false; + + return 0; +} + +#ifdef KERNEL_ABOVE_4_14 +static int ktd3136_bl_get_brightness(struct backlight_device *bl_dev) +{ + return bl_dev->props.brightness; +} + +static int ktd3136_bl_update_status(struct backlight_device *bl_dev) +{ + struct ktd3136_data *drvdata = bl_get_data(bl_dev); + int brt; + + if (bl_dev->props.state & BL_CORE_SUSPENDED) + bl_dev->props.brightness = 0; + + brt = bl_dev->props.brightness; + /* + * Brightness register should always be written + * not only register based mode but also in PWM mode. + */ + return ktd3136_set_brightness(drvdata, brt); +} + +static const struct backlight_ops ktd3136_bl_ops = { + .update_status = ktd3136_bl_update_status, + .get_brightness = ktd3136_bl_get_brightness, +}; +#endif + +static int ktd3136_backlight_reset(struct ktd3136_data *drvdata) +{ + int err = 0; + ktd3136_masked_write(drvdata->client, REG_SW_RESET, 0x01, 0x01); + return err; +} + +static int ktd3136_check_id(struct ktd3136_data *drvdata) +{ + u8 value=0; + int err = 0; + ktd3136_read_reg(drvdata->client, 0x00, &value); + pr_info("%s: ID check: %x0x\n", __func__, value); + if (value != KTD3136_CHIP_ID) { + pr_err("%s : ID check err\n", __func__); + err = -EINVAL; + return err; + } + + return err; +} + +static void ktd3136_check_status(struct ktd3136_data *drvdata) +{ + u8 value=0; + + ktd3136_read_reg(drvdata->client, REG_STATUS, &value); + if (value) { + pr_err("status bit has been change! <%x>", value); + + if (value & RESET_CONDITION_BITS) { + ktd3136_backlight_reset(drvdata); + ktd3136_backlight_init(drvdata); + ktd3136_backlight_enable(drvdata); + } + + } + return ; +} + +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + int *blank; + struct fb_event *evdata = data; + struct ktd3136_data *drvdata = + container_of(self, struct ktd3136_data, fb_notif); + + /* + * FB_EVENT_BLANK(0x09): A hardware display blank change occurred. + * FB_EARLY_EVENT_BLANK(0x10): A hardware display blank early change + * occurred. + */ + if (evdata && evdata->data && (event == FB_EARLY_EVENT_BLANK)) { + blank = evdata->data; + if (*blank == FB_BLANK_POWERDOWN) + drvdata->enable = false; + } + + return NOTIFY_OK; +} +#endif + +static void __ktd3136_work(struct ktd3136_data *led, + enum led_brightness value) +{ + mutex_lock(&led->lock); + ktd3136_set_brightness(led, value); + mutex_unlock(&led->lock); +} + +static void ktd3136_work(struct work_struct *work) +{ + struct ktd3136_data *drvdata = container_of(work, + struct ktd3136_data, work); + + __ktd3136_work(drvdata, drvdata->led_dev.brightness); + + return; +} + + +static void ktd3136_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brt_val) +{ + struct ktd3136_data *drvdata; + + drvdata = container_of(led_cdev, struct ktd3136_data, led_dev); + + schedule_work(&drvdata->work); +} + +static void ktd3136_get_dt_data(struct device *dev, struct ktd3136_data *drvdata) +{ + int rc; + struct device_node *np = dev->of_node; + u32 bl_channel, temp; + drvdata->hwen_gpio = of_get_named_gpio(np, "ktd,hwen-gpio", 0); + pr_info("%s hwen --<%d>\n", __func__, drvdata->hwen_gpio); + + drvdata->pwm_mode = of_property_read_bool(np,"ktd,pwm-mode"); + pr_debug("pwmmode --<%d> \n", drvdata->pwm_mode); + + drvdata->using_lsb = of_property_read_bool(np, "ktd,using-lsb"); + pr_info("%s using_lsb --<%d>\n", __func__, drvdata->using_lsb); + + if (drvdata->using_lsb) { + drvdata->default_brightness = 0x7ff; + drvdata->max_brightness = 2047; + } else { + drvdata->default_brightness = 0xff; + drvdata->max_brightness = 255; + } + rc = of_property_read_u32(np, "ktd,pwm-frequency", &temp); + if (rc) { + pr_err("Invalid pwm-frequency!\n"); + } else { + drvdata->pwm_period = temp; + pr_debug("pwm-frequency --<%d> \n", drvdata->pwm_period); + } + + rc = of_property_read_u32(np, "ktd,bl-fscal-led", &temp); + if (rc) { + pr_err("Invalid backlight full-scale led current!\n"); + } else { + drvdata->full_scale_led = temp; + pr_debug("full-scale led current --<%d mA> \n", drvdata->full_scale_led); + } + + rc = of_property_read_u32(np, "ktd,turn-on-ramp", &temp); + if (rc) { + pr_err("Invalid ramp timing ,,turnon!\n"); + } else { + drvdata->ramp_on_time = temp; + pr_debug("ramp on time --<%d ms> \n", drvdata->ramp_on_time); + } + + rc = of_property_read_u32(np, "ktd,turn-off-ramp", &temp); + if (rc) { + pr_err("Invalid ramp timing ,,turnoff!\n"); + } else { + drvdata->ramp_off_time = temp; + pr_debug("ramp off time --<%d ms> \n", drvdata->ramp_off_time); + } + + rc = of_property_read_u32(np, "ktd,pwm-trans-dim", &temp); + if (rc) { + pr_err("Invalid pwm-tarns-dim value!\n"); + } + else { + drvdata->pwm_trans_dim = temp; + pr_debug("pwm trnasition dimming --<%d ms> \n", drvdata->pwm_trans_dim); + } + + rc = of_property_read_u32(np, "ktd,i2c-trans-dim", &temp); + if (rc) { + pr_err("Invalid i2c-trans-dim value !\n"); + } else { + drvdata->i2c_trans_dim = temp; + pr_debug("i2c transition dimming --<%d ms>\n", drvdata->i2c_trans_dim); + } + + rc = of_property_read_u32(np, "ktd,bl-channel", &bl_channel); + if (rc) { + pr_err("Invalid channel setup\n"); + } else { + drvdata->channel = bl_channel; + pr_debug("bl-channel --<%x> \n", drvdata->channel); + } + + rc = of_property_read_u32(np, "ktd,ovp-level", &temp); + if (!rc) { + drvdata->ovp_level = temp; + pr_debug("ovp-level --<%d> --temp <%d>\n", drvdata->ovp_level, temp); + }else + pr_err("Invalid OVP level!\n"); + + rc = of_property_read_u32(np, "ktd,switching-frequency", &temp); + if (!rc) { + drvdata->frequency = temp; + pr_debug("switching frequency --<%d> \n", drvdata->frequency); + } else { + pr_err("Invalid Frequency value!\n"); + } + + rc = of_property_read_u32(np, "ktd,inductor-current", &temp); + if (!rc) { + drvdata->induct_current = temp; + pr_debug("inductor current limit --<%d> \n", drvdata->induct_current); + } else + pr_err("invalid induct_current limit\n"); + + rc = of_property_read_u32(np, "ktd,flash-timeout", &temp); + if (!rc) { + drvdata->flash_timeout = temp; + pr_debug("flash timeout --<%d> \n", drvdata->flash_timeout); + } else { + pr_err("invalid flash-time value!\n"); + } + + rc = of_property_read_u32(np, "ktd,flash-current", &temp); + if (!rc) { + drvdata->flash_current = temp; + pr_debug("flash current --<0x%x> \n", drvdata->flash_current); + } else { + pr_err("invalid flash current value!\n"); + } +} + +static int ktd3136_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ktd3136_data *drvdata; +#ifdef KERNEL_ABOVE_4_14 + struct backlight_device *bl_dev; + struct backlight_properties props; +#endif + int err = 0; + + pr_info("%s enter!\n", __func__); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("%s : I2C_FUNC_I2C not supported\n", __func__); + err = -EIO; + goto err_out; + } + + if (!client->dev.of_node) { + pr_err("%s : no device node\n", __func__); + err = -ENOMEM; + goto err_out; + } + + drvdata = kzalloc(sizeof(struct ktd3136_data), GFP_KERNEL); + if (drvdata == NULL) { + pr_err("%s : kzalloc failed\n", __func__); + err = -ENOMEM; + goto err_out; + } + + drvdata->client = client; + drvdata->adapter = client->adapter; +#ifdef KTD3136_REG_CONFILCT + pr_info("%s: client->addr=0x%x\n", __func__, client->addr); + client->addr = KTD3136_REG_REAL; + pr_info("%s: confilct, reset client->addr to 0x%x\n", __func__, client->addr); +#endif + drvdata->addr = client->addr; + drvdata->brightness = LED_OFF; + drvdata->enable = true; + drvdata->led_dev.default_trigger = "bkl-trigger"; + drvdata->led_dev.name = KTD3136_LED_DEV; + drvdata->led_dev.brightness_set = ktd3136_brightness_set; + drvdata->led_dev.max_brightness = MAX_BRIGHTNESS; + ktd3136_get_dt_data(&client->dev, drvdata); + i2c_set_clientdata(client, drvdata); + err =ktd3136_check_id(drvdata); + if (err <0) { + pr_err("%s : ID idenfy failed\n", __func__); + goto err_init; + } + + mutex_init(&drvdata->lock); + INIT_WORK(&drvdata->work, ktd3136_work); + err = led_classdev_register(&client->dev, &drvdata->led_dev); + if (err < 0) { + pr_err("%s : Register led class failed\n", __func__); + err = -ENODEV; + goto err_init; + } else { + pr_info("%s: Register led class successful\n", __func__); + } + ktd3136_gpio_init(drvdata); + +#ifdef KERNEL_ABOVE_4_14 + memset(&props, 0, sizeof(struct backlight_properties)); + props.type = BACKLIGHT_PLATFORM; + props.brightness = MAX_BRIGHTNESS; + props.max_brightness = MAX_BRIGHTNESS; + bl_dev = backlight_device_register(KTD3136_NAME, &client->dev, + drvdata, &ktd3136_bl_ops, &props); +#endif + ktd3136_backlight_init(drvdata); + ktd3136_backlight_enable(drvdata); + ktd3136_check_status(drvdata); + +#if defined(CONFIG_FB) + drvdata->fb_notif.notifier_call = fb_notifier_callback; + err = fb_register_client(&drvdata->fb_notif); + if (err) + pr_err("%s : Unable to register fb_notifier: %d\n", __func__, err); +#endif + + pr_info("%s exit\n", __func__); + return 0; + +err_init: + pr_info("%s err_init\n", __func__); + kfree(drvdata); +err_out: + pr_info("%s err_out\n", __func__); + return err; +} + +static int ktd3136_remove(struct i2c_client *client) +{ + struct ktd3136_data *drvdata = i2c_get_clientdata(client); + + led_classdev_unregister(&drvdata->led_dev); + + kfree(drvdata); + return 0; +} + +static const struct i2c_device_id ktd3136_id[] = { + {KTD3136_NAME, 0}, + {} +}; +static struct of_device_id match_table[] = { + {.compatible = "ktd,ktd3136",} +}; + +MODULE_DEVICE_TABLE(i2c, ktd3136_id); + +static struct i2c_driver ktd3136_i2c_driver = { + .probe = ktd3136_probe, + .remove = ktd3136_remove, + .id_table = ktd3136_id, + .driver = { + .name = KTD3136_NAME, + .owner = THIS_MODULE, + .of_match_table = match_table, + }, +}; + +module_i2c_driver(ktd3136_i2c_driver); +MODULE_DESCRIPTION("Backlight driver for ktd3136"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/backlight/ktd3136/ktd3136_bl.h b/drivers/backlight/ktd3136/ktd3136_bl.h new file mode 100644 index 000000000000..cbf516333081 --- /dev/null +++ b/drivers/backlight/ktd3136/ktd3136_bl.h @@ -0,0 +1,120 @@ +#ifndef _KTD3136_REG_H_ +#define _KTD3136_REG_H_ + +#include + +/********************************************************* + * + * kernel version + * + ********************************************************/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) +#define KERNEL_ABOVE_4_14 +#endif + +#undef pr_debug +#define pr_debug pr_info + +/********************************************************* + * + * kernel version + * + ********************************************************/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) +#define KERNEL_ABOVE_4_14 +#endif + +#define KTD3136_REG_CONFILCT +#ifdef KTD3136_REG_CONFILCT +#define KTD3136_REG_REAL 0x36 +#endif + +#define MAX_BRIGHTNESS (2047) +#define BANK_NONE 0x00 +#define REG_DEV_ID 0x00 +#define REG_SW_RESET 0x01 +#define REG_MODE 0x02 +#define REG_CONTROL 0x03 +#define REG_RATIO_LSB 0x04 +#define REG_RATIO_MSB 0x05 +#define REG_PWM 0x06 +#define REG_RAMP_ON 0x07 +#define REG_TRANS_RAMP 0x08 +#define REG_FLASH_SETTING 0x09 +#define REG_STATUS 0x0A +#define BIT_CH3_FAULT BIT(7) +#define BIT_CH2_FAULT BIT(6) +#define BIT_CH1_FAULT BIT(5) +#define BIT_FLASH_TIMEOUT BIT(4) +#define BIT_OVP BIT(3) +#define BIT_UVLO BIT(2) +#define BIT_OCP BIT(1) +#define BIT_THERMAL_SHUTDOWN BIT(0) + +#define RESET_CONDITION_BITS (BIT_CH3_FAULT | BIT_CH2_FAULT | BIT_CH1_FAULT | BIT_OVP | BIT_OCP) + + +int ktd3136_brightness_table_reg4[256] = {0x01,0x02,0x04,0x04,0x07,0x02,0x00,0x06,0x04,0x02,0x03,0x04,0x05,0x06,0x02,0x06,0x02, + 0x06,0x02,0x06,0x02,0x06,0x02,0x04,0x05,0x06,0x05,0x03,0x00,0x05,0x02,0x06,0x02,0x06,0x02,0x06,0x02,0x06,0x02,0x06, + 0x02,0x06,0x02,0x06,0x02,0x06,0x01,0x04,0x07,0x02,0x05,0x00,0x03,0x06,0x01,0x04,0x07,0x02,0x05,0x00,0x03,0x05,0x07, + 0x01,0x03,0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x00,0x01,0x02,0x03, + 0x04,0x05,0x06,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x06,0x05, + 0x04,0x03,0x02,0x01,0x00,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x07,0x05,0x03,0x01,0x07,0x05,0x03,0x01,0x07,0x05,0x03, + 0x01,0x07,0x05,0x03,0x00,0x05,0x02,0x07,0x04,0x01,0x06,0x03,0x00,0x05,0x02,0x07,0x04,0x01,0x06,0x03,0x00,0x05,0x02, + 0x07,0x03,0x07,0x03,0x07,0x03,0x07,0x03,0x07,0x03,0x07,0x03,0x07,0x03,0x07,0x03,0x07,0x03,0x07,0x03,0x07,0x03,0x07, + 0x03,0x07,0x03,0x07,0x03,0x06,0x01,0x04,0x07,0x02,0x05,0x00,0x03,0x06,0x01,0x04,0x07,0x02,0x05,0x00,0x03,0x06,0x01, + 0x04,0x07,0x02,0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x03, + 0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x03,0x05,0x07,0x01,0x03,0x05,0x07,0x01, + 0x03,0x05,0x07,0x01,0x03,0x04,0x05,0x06,0x07}; + +int ktd3136_brightness_table_reg5[256] = {0x00,0x06,0x0C,0x11,0x15,0x1A,0x1E,0x21,0x25,0x29,0x2C,0x2F,0x32,0x35,0x38,0x3A,0x3D, + 0x3F,0x42,0x44,0x47,0x49,0x4C,0x4E,0x50,0x52,0x54,0x56,0x58,0x59,0x5B,0x5C,0x5E,0x5F,0x61,0x62,0x64,0x65,0x67,0x68, + 0x6A,0x6B,0x6D,0x6E,0x70,0x71,0x73,0x74,0x75,0x77,0x78,0x7A,0x7B,0x7C,0x7E,0x7F,0x80,0x82,0x83,0x85,0x86,0x87,0x88, + 0x8A,0x8B,0x8C,0x8D,0x8F,0x90,0x91,0x92,0x94,0x95,0x96,0x97,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F,0xA1,0xA2,0xA3,0xA4, + 0xA5,0xA6,0xA7,0xA8,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF,0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xB9,0xBA,0xBB, + 0xBC,0xBD,0xBE,0xBF,0xC0,0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC7,0xC8,0xC9,0xC9,0xCA,0xCB,0xCC,0xCC,0xCD,0xCE, + 0xCF,0xCF,0xD0,0xD1,0xD2,0xD2,0xD3,0xD3,0xD4,0xD5,0xD5,0xD6,0xD7,0xD7,0xD8,0xD8,0xD9,0xDA,0xDA,0xDB,0xDC,0xDC,0xDD, + 0xDD,0xDE,0xDE,0xDF,0xDF,0xE0,0xE0,0xE1,0xE1,0xE2,0xE2,0xE3,0xE3,0xE4,0xE4,0xE5,0xE5,0xE6,0xE6,0xE7,0xE7,0xE8,0xE8, + 0xE9,0xE9,0xEA,0xEA,0xEB,0xEB,0xEC,0xEC,0xEC,0xED,0xED,0xEE,0xEE,0xEE,0xEF,0xEF,0xEF,0xF0,0xF0,0xF1,0xF1,0xF1,0xF2, + 0xF2,0xF2,0xF3,0xF3,0xF3,0xF4,0xF4,0xF4,0xF4,0xF5,0xF5,0xF5,0xF5,0xF6,0xF6,0xF6,0xF6,0xF7,0xF7,0xF7,0xF7,0xF8,0xF8, + 0xF8,0xF8,0xF9,0xF9,0xF9,0xF9,0xFA,0xFA,0xFA,0xFA,0xFB,0xFB,0xFB,0xFB,0xFC,0xFC,0xFC,0xFC,0xFD,0xFD,0xFD,0xFD,0xFE, + 0xFE,0xFE,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; + +struct ktd3136_data { + struct led_classdev led_dev; + struct i2c_client *client; + struct i2c_adapter *adapter; + unsigned short addr; + struct mutex lock; + struct work_struct work; + enum led_brightness brightness; +#ifdef CONFIG_FB + struct notifier_block fb_notif; +#endif + bool enable; + u8 pwm_cfg; + u8 boost_ctl; + u8 full_scale_current; + bool brt_code_enable; + u16 *brt_code_table; + int hwen_gpio; + bool pwm_mode; + bool using_lsb; + unsigned int pwm_period; + unsigned int full_scale_led; + unsigned int ramp_on_time; + unsigned int ramp_off_time; + unsigned int pwm_trans_dim; + unsigned int i2c_trans_dim; + unsigned int channel; + unsigned int ovp_level; + unsigned int frequency; + unsigned int default_brightness; + unsigned int max_brightness; + unsigned int induct_current; + unsigned int flash_current; + unsigned int flash_timeout; + +}; + +#endif diff --git a/drivers/backlight/lm3697/Android.mk b/drivers/backlight/lm3697/Android.mk new file mode 100644 index 000000000000..8c9c3e27ad0f --- /dev/null +++ b/drivers/backlight/lm3697/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := leds_lm3697.ko +LOCAL_MODULE_TAGS := optional + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/backlight/lm3697/Kbuild b/drivers/backlight/lm3697/Kbuild new file mode 100644 index 000000000000..3204c539185a --- /dev/null +++ b/drivers/backlight/lm3697/Kbuild @@ -0,0 +1,8 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +obj-m := leds_lm3697.o +leds_lm3697-objs += ti_lm3697.o +leds_lm3697-objs += ti_lm3697_backlight.o +leds_lm3697-objs += ti_lm3697_backlight_data.o diff --git a/drivers/backlight/lm3697/ti-lmu.h b/drivers/backlight/lm3697/ti-lmu.h new file mode 100644 index 000000000000..09ae353b4672 --- /dev/null +++ b/drivers/backlight/lm3697/ti-lmu.h @@ -0,0 +1,71 @@ +/* + * TI LMU (Lighting Management Unit) Devices + * + * Copyright 2017 Texas Instruments + * + * Author: Milo Kim + * + * 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. + */ + +#ifndef __MFD_TI_LMU_H__ +#define __MFD_TI_LMU_H__ + +#include +#include +#include + +/* Notifier event */ +#define LMU_EVENT_MONITOR_DONE 0x01 + +enum ti_lmu_id { + LM3697, + LMU_MAX_ID, +}; + +enum ti_lmu_max_current { + LMU_IMAX_5mA, + LMU_IMAX_6mA, + LMU_IMAX_7mA = 0x03, + LMU_IMAX_8mA, + LMU_IMAX_9mA, + LMU_IMAX_10mA = 0x07, + LMU_IMAX_11mA, + LMU_IMAX_12mA, + LMU_IMAX_13mA, + LMU_IMAX_14mA, + LMU_IMAX_15mA = 0x0D, + LMU_IMAX_16mA, + LMU_IMAX_17mA, + LMU_IMAX_18mA, + LMU_IMAX_19mA, + LMU_IMAX_20mA = 0x13, + LMU_IMAX_21mA, + LMU_IMAX_22mA, + LMU_IMAX_23mA = 0x17, + LMU_IMAX_24mA, + LMU_IMAX_25mA, + LMU_IMAX_26mA, + LMU_IMAX_27mA = 0x1C, + LMU_IMAX_28mA, + LMU_IMAX_29mA, + LMU_IMAX_30mA, +}; + +/** + * struct ti_lmu + * + * @dev: Parent device pointer + * @regmap: Used for i2c communcation on accessing registers + * @en_gpio: GPIO for HWEN pin [Optional] + * @notifier: Notifier for reporting hwmon event + */ +struct ti_lmu { + struct device *dev; + struct regmap *regmap; + int en_gpio; + struct blocking_notifier_head notifier; +}; +#endif diff --git a/drivers/backlight/lm3697/ti_lm3697.c b/drivers/backlight/lm3697/ti_lm3697.c new file mode 100644 index 000000000000..f72e48d416f7 --- /dev/null +++ b/drivers/backlight/lm3697/ti_lm3697.c @@ -0,0 +1,183 @@ +/* + * TI LMU (Lighting Management Unit) Core Driver + * + * Copyright 2017 Texas Instruments + * + * Author: Milo Kim + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ti_lm3697.h" +#include "ti_lm3697_regulator.h" +#include "ti-lmu.h" + +struct ti_lmu_data { + struct mfd_cell *cells; + int num_cells; + unsigned int max_register; +}; + +#define LM3697_I2C_ADDR 0x36 + +static int ti_lmu_enable_hw(struct ti_lmu *lmu, enum ti_lmu_id id) +{ + int ret; + + if (gpio_is_valid(lmu->en_gpio)) { + ret = devm_gpio_request_one(lmu->dev, lmu->en_gpio, + GPIOF_OUT_INIT_HIGH, "lmu_hwen"); + if (ret) { + dev_err(lmu->dev, "Can not request enable GPIO: %d\n", + ret); + return ret; + } + } + + /* Delay about 1ms after HW enable pin control */ + usleep_range(1000, 1500); + + return 0; +} + +static void ti_lmu_disable_hw(struct ti_lmu *lmu) +{ + if (gpio_is_valid(lmu->en_gpio)) + gpio_set_value(lmu->en_gpio, 0); +} + + +static struct mfd_cell lm3697_devices[] = { + { + .name = "ti-lmu-backlight", + .id = LM3697, + .of_compatible = "ti,lm3697-backlight", + }, + /* Monitoring driver for open/short circuit detection */ + { + .name = "ti-lmu-fault-monitor", + .id = LM3697, + .of_compatible = "ti,lm3697-fault-monitor", + }, +}; + +#define TI_LMU_DATA(chip, max_reg) \ +static const struct ti_lmu_data chip##_data = \ +{ \ + .cells = chip##_devices, \ + .num_cells = ARRAY_SIZE(chip##_devices),\ + .max_register = max_reg, \ +} \ + +TI_LMU_DATA(lm3697, LM3697_MAX_REG); + +static const struct of_device_id ti_lmu_of_match[] = { + { .compatible = "ti,lm3697", .data = &lm3697_data }, + { } +}; +MODULE_DEVICE_TABLE(of, ti_lmu_of_match); + +static int ti_lmu_probe(struct i2c_client *cl, const struct i2c_device_id *id) +{ + struct device *dev = &cl->dev; + const struct of_device_id *match; + const struct ti_lmu_data *data; + struct regmap_config regmap_cfg; + struct ti_lmu *lmu; + int ret; + + if (cl->addr != LM3697_I2C_ADDR) + cl->addr = LM3697_I2C_ADDR; + + match = of_match_device(ti_lmu_of_match, dev); + if (!match) + return -ENODEV; + /* + * Get device specific data from of_match table. + * This data is defined by using TI_LMU_DATA() macro. + */ + data = (struct ti_lmu_data *)match->data; + + lmu = devm_kzalloc(dev, sizeof(*lmu), GFP_KERNEL); + if (!lmu) + return -ENOMEM; + + lmu->dev = &cl->dev; + + /* Setup regmap */ + memset(®map_cfg, 0, sizeof(struct regmap_config)); + regmap_cfg.reg_bits = 8; + regmap_cfg.val_bits = 8; + regmap_cfg.name = id->name; + regmap_cfg.max_register = data->max_register; + + lmu->regmap = devm_regmap_init_i2c(cl, ®map_cfg); + if (IS_ERR(lmu->regmap)) + return PTR_ERR(lmu->regmap); + + /* HW enable pin control and additional power up sequence if required */ + lmu->en_gpio = of_get_named_gpio(dev->of_node, "enable-gpios", 0); + ret = ti_lmu_enable_hw(lmu, id->driver_data); + if (ret) + return ret; + + /* + * Fault circuit(open/short) can be detected by ti-lmu-fault-monitor. + * After fault detection is done, some devices should re-initialize + * configuration. The notifier enables such kind of handling. + */ + BLOCKING_INIT_NOTIFIER_HEAD(&lmu->notifier); + + i2c_set_clientdata(cl, lmu); + + mfd_add_devices(lmu->dev, 0, data->cells, + data->num_cells, NULL, 0, NULL); + + ti_lmu_backlight_device_init(); + + return ret; +} + +static int ti_lmu_remove(struct i2c_client *cl) +{ + struct ti_lmu *lmu = i2c_get_clientdata(cl); + + ti_lmu_disable_hw(lmu); + mfd_remove_devices(lmu->dev); + return 0; +} + +static const struct i2c_device_id ti_lmu_ids[] = { + { "lm3697", LM3697 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ti_lmu_ids); + +static struct i2c_driver ti_lmu_driver = { + .probe = ti_lmu_probe, + .remove = ti_lmu_remove, + .driver = { + .name = "ti-lmu", + .of_match_table = ti_lmu_of_match, + }, + .id_table = ti_lmu_ids, +}; + +module_i2c_driver(ti_lmu_driver); + +MODULE_DESCRIPTION("TI LMU MFD Core Driver"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/backlight/lm3697/ti_lm3697.h b/drivers/backlight/lm3697/ti_lm3697.h new file mode 100644 index 000000000000..10804bd31425 --- /dev/null +++ b/drivers/backlight/lm3697/ti_lm3697.h @@ -0,0 +1,252 @@ +/* + * TI LMU (Lighting Management Unit) Backlight Common Driver + * + * Copyright 2016 Texas Instruments + * + * Author: Milo Kim + * + * 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. + */ + +#ifndef __TI_LMU_BACKLIGHT_H__ +#define __TI_LMU_BACKLIGHT_H__ + +#include +#include +#include +#include "ti_lm3697_regulator.h" +#include "ti-lmu.h" +/** + * LMU backlight register data + * value[23:16] | mask[15:8] | address[7:0] + */ +#define LMU_BL_REG(addr, mask, value) \ + ((value << 16) | (mask << 8) | addr) + +#define LMU_BL_GET_ADDR(x) (x & 0xFF) +#define LMU_BL_GET_MASK(x) ((x >> 8) & 0xFF) +#define LMU_BL_GET_VAL(x) ((x >> 16) & 0xFF) + +#define LM3697_INIT_RAMP_SELECT \ + LMU_BL_REG(LM3697_REG_RAMP_CONF, LM3697_RAMP_MASK, LM3697_RAMP_EACH) +#define LM3697_CHANNEL_1 \ + LMU_BL_REG(LM3697_REG_HVLED_OUTPUT_CFG, LM3697_HVLED1_CFG_MASK, \ + LM3697_HVLED1_CFG_SHIFT) +#define LM3697_CHANNEL_2 \ + LMU_BL_REG(LM3697_REG_HVLED_OUTPUT_CFG, LM3697_HVLED2_CFG_MASK, \ + LM3697_HVLED2_CFG_SHIFT) +#define LM3697_CHANNEL_3 \ + LMU_BL_REG(LM3697_REG_HVLED_OUTPUT_CFG, LM3697_HVLED3_CFG_MASK, \ + LM3697_HVLED3_CFG_SHIFT) +#define LM3697_MODE_PWM_A \ + LMU_BL_REG(LM3697_REG_PWM_CFG, LM3697_PWM_A_MASK, LM3697_PWM_A_MASK) +#define LM3697_MODE_PWM_B \ + LMU_BL_REG(LM3697_REG_PWM_CFG, LM3697_PWM_B_MASK, LM3697_PWM_B_MASK) +#define LM3697_RAMPUP \ + LMU_BL_REG(LM3697_REG_BL0_RAMP, LM3697_RAMPUP_MASK, LM3697_RAMPUP_SHIFT) +#define LM3697_RAMPDN \ + LMU_BL_REG(LM3697_REG_BL0_RAMP, LM3697_RAMPDN_MASK, LM3697_RAMPDN_SHIFT) + +#define LM3697_MAX_CHANNELS 3 + +#define MAX_BRIGHTNESS_8BIT 255 +#define MAX_BRIGHTNESS_11BIT 2047 + +enum ti_lmu_bl_ctrl_mode { + BL_REGISTER_BASED, + BL_PWM_BASED, +}; + +enum ti_lmu_bl_pwm_action { + /* Update PWM duty, no brightness register update is required */ + UPDATE_PWM_ONLY, + /* Update not only duty but also brightness register */ + UPDATE_PWM_AND_BRT_REGISTER, + /* Update max value in brightness registers */ + UPDATE_MAX_BRT, +}; + +enum ti_lmu_bl_ramp_mode { + BL_RAMP_UP, + BL_RAMP_DOWN, +}; + +struct ti_lmu_bl; +struct ti_lmu_bl_chip; + +/** + * struct ti_lmu_bl_reg + * + * @init: Device initialization registers + * @num_init: Numbers of initialization registers + * @channel: Backlight channel configuration registers + * @mode: Brightness control mode registers + * @ramp: Ramp registers for lighting effect + * @ramp_reg_offset: Ramp register offset. + * Only used for multiple ramp registers. + * @enable: Enable control register address + * @enable_offset: Enable control register bit offset + * @enable_usec: Delay time for updating enable register. + * Unit is microsecond. + * @brightness_msb: Brightness MSB(Upper 8 bits) registers. + * Concatenated with LSB in 11 bit dimming mode. + * In 8 bit dimming, only MSB is used. + * @brightness_lsb: Brightness LSB(Lower 3 bits) registers. + * Only valid in 11 bit dimming mode. + */ +struct ti_lmu_bl_reg { + u32 *init; + int num_init; + u32 *channel; + u32 *mode; + u32 *ramp; + int ramp_reg_offset; + u8 *enable; + u8 enable_offset; + unsigned long enable_usec; + u8 *brightness_msb; + u8 *brightness_lsb; +}; + +/** + * struct ti_lmu_bl_cfg + * + * @reginfo: Device register configuration + * @num_channels: Number of backlight channels + * @single_bank_used: [Optional] Set true if one bank controls multiple channels. + * Only used for LM36274. + * @max_brightness: Max brightness value of backlight device + * @pwm_action: How to control brightness registers in PWM mode + * @ramp_table: [Optional] Ramp time table for lighting effect. + * It's used for searching approximate register index. + * @size_ramp: [Optional] Size of ramp table + * @fault_monitor_used: [Optional] Set true if the device needs to handle + * LMU fault monitor event. + * + * This structure is used for device specific data configuration. + */ +struct ti_lmu_bl_cfg { + const struct ti_lmu_bl_reg *reginfo; + int num_channels; + bool single_bank_used; + int max_brightness; + enum ti_lmu_bl_pwm_action pwm_action; + int *ramp_table; + int size_ramp; + bool fault_monitor_used; +}; + +/** + * struct ti_lmu_bl_chip + * + * @dev: Parent device pointer + * @lmu: LMU structure. + * Used for register R/W access and notification. + * @cfg: Device configuration data + * @lmu_bl: Multiple backlight channels + * @num_backlights: Number of backlight channels + * @nb: Notifier block for handling LMU fault monitor event + * + * One backlight chip can have multiple backlight channels, 'ti_lmu_bl'. + */ +struct ti_lmu_bl_chip { + struct device *dev; + struct ti_lmu *lmu; + const struct ti_lmu_bl_cfg *cfg; + struct ti_lmu_bl *lmu_bl; + int num_backlights; + struct notifier_block nb; +}; + +/** + * struct ti_lmu_bl + * + * @chip: Pointer to parent backlight device + * @bl_dev: Backlight subsystem device structure + * @bank_id: Backlight bank ID + * @name: Backlight channel name + * @mode: Backlight control mode + * @led_sources: Backlight output channel configuration. + * Bit mask is set on parsing DT. + * @default_brightness: [Optional] Initial brightness value + * @ramp_up_msec: [Optional] Ramp up time + * @ramp_down_msec: [Optional] Ramp down time + * @pwm_period: [Optional] PWM period + * @pwm: [Optional] PWM subsystem structure + * + * Each backlight device has its own channel configuration. + * For chip control, parent chip data structure is used. + */ +struct ti_lmu_bl { + struct ti_lmu_bl_chip *chip; + struct backlight_device *bl_dev; + + int bank_id; + const char *name; + enum ti_lmu_bl_ctrl_mode mode; + unsigned long led_sources; + + unsigned int default_brightness; + + /* Used for lighting effect */ + unsigned int ramp_up_msec; + unsigned int ramp_down_msec; + + /* Only valid in PWM mode */ + unsigned int pwm_period; + + /* current mode */ + unsigned int led_current_mode; + + /* Boost OVP */ + unsigned int led_boost_ovp; + /* Boost frequency */ + unsigned int led_boost_freq; + + /* Map type */ + unsigned int map_type; + + /* Align boost current to AW chip */ + unsigned int led_current_align; + + struct pwm_device *pwm; +}; + +enum backlight_hbm_mode { + HBM_MODE_UN_SET, + HBM_MODE_DEFAULT = 1, + HBM_MODE_LEVEL1, //CURRENT = HBM_MODE_DEFAULT*112.5% + HBM_MODE_LEVEL2, //CURRENT = HBM_MODE_DEFAULT*125% + HBM_MODE_LEVEL3, //CURRENT = HBM_MODE_DEFAULT*107% + HBM_MODE_LEVEL4, //CURRENT = HBM_MODE_DEFAULT*111% + HBM_MODE_LEVEL_MAX +}; + +enum backlight_boost_ovp { + BOOST_OVP_16V, + BOOST_OVP_24V, + BOOST_OVP_32V, + BOOST_OVP_40V +}; + +enum backlight_boost_freq { + BOOST_FREQ_500K, + BOOST_FREQ_1M +}; + +enum backlight_map_type { + EXPONENTIAL_TYPE, + LINEAR_TYPE +}; + +enum backlight_exp_current_align { + ALIGN_NONE, + ALIGN_AW99703 +}; + +extern struct ti_lmu_bl_cfg lmu_bl_cfg[LMU_MAX_ID]; +extern int ti_lmu_backlight_device_init(void); +extern void ti_lmu_backlight_exit(void); +#endif diff --git a/drivers/backlight/lm3697/ti_lm3697_backlight.c b/drivers/backlight/lm3697/ti_lm3697_backlight.c new file mode 100644 index 000000000000..fe0247fe6b3b --- /dev/null +++ b/drivers/backlight/lm3697/ti_lm3697_backlight.c @@ -0,0 +1,963 @@ +/* + * TI LMU (Lighting Management Unit) Backlight Driver + * + * Copyright 2016 Texas Instruments + * + * Author: Milo Kim + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ti_lm3697.h" +#include "ti_lm3697_regulator.h" +#include "ti-lmu.h" + +#define NUM_DUAL_CHANNEL 2 +#define LMU_BACKLIGHT_DUAL_CHANNEL_USED (BIT(0) | BIT(1)) +#define LMU_BACKLIGHT_11BIT_LSB_MASK (BIT(0) | BIT(1) | BIT(2)) +#define LMU_BACKLIGHT_11BIT_MSB_SHIFT 3 +#define DEFAULT_PWM_NAME "lmu-backlight" +#define LM3697_NAME "lm3697-bl" + +static struct ti_lmu_bl_chip *bl_chip; + +int translate_value[2048] = {0, 50, 55, 171, 253, 317, 360, 414, 452, 486, 516, 543, 568, 591, 612, 632, 651, 668, 684, 700, 714, 728, 742, 754, 767, 778, 790,\ + 800, 811, 821, 831, 840, 849, 858, 866, 875, 883, 891, 898, 906, 913, 920, 927, 934, 940, 947, 953, 959, 965, 971, 977, 983, 988,\ + 994, 999, 1004, 1009, 1014, 1019, 1024, 1029, 1034, 1038, 1043, 1048, 1052, 1056, 1061, 1065, 1069, 1073, 1077, 1081, 1085,\ + 1089, 1093, 1097, 1101, 1104, 1108, 1111, 1115, 1119, 1122, 1125, 1129, 1132, 1135, 1139, 1142, 1145, 1148, 1151, 1155, 1158,\ + 1161, 1164, 1167, 1170, 1172, 1175, 1178, 1181, 1184, 1187, 1189, 1192, 1195, 1197, 1200, 1203, 1205, 1208, 1210, 1213, 1215,\ + 1218, 1220, 1223, 1225, 1228, 1230, 1232, 1235, 1237, 1239, 1242, 1244, 1246, 1248, 1251, 1253, 1255, 1257, 1259, 1261, 1263,\ + 1266, 1268, 1270, 1272, 1274, 1276, 1278, 1280, 1282, 1284, 1286, 1288, 1290, 1291, 1293, 1295, 1297, 1299, 1301, 1303, 1305, 1306,\ + 1308, 1310, 1312, 1314, 1315, 1317, 1319, 1321, 1322, 1324, 1326, 1327, 1329, 1331, 1332, 1334, 1336, 1337, 1339, 1341, 1342,\ + 1344, 1345, 1347, 1348, 1350, 1352, 1353, 1355, 1356, 1358, 1359, 1361, 1362, 1364, 1365, 1367, 1368, 1370, 1371, 1372, 1374,\ + 1375, 1377, 1378, 1380, 1381, 1382, 1384, 1385, 1386, 1388, 1389, 1391, 1392, 1393, 1395, 1396, 1397, 1399, 1400, 1401, 1402,\ + 1404, 1405, 1406, 1408, 1409, 1410, 1411, 1413, 1414, 1415, 1416, 1418, 1419, 1420, 1421, 1423, 1424, 1425, 1426, 1427, 1428,\ + 1430, 1431, 1432, 1433, 1434, 1435, 1437, 1438, 1439, 1440, 1441, 1442, 1443, 1445, 1446, 1447, 1448, 1449, 1450, 1451, 1452,\ + 1453, 1454, 1456, 1457, 1458, 1459, 1460, 1461, 1462, 1463, 1464, 1465, 1466, 1467, 1468, 1469, 1470, 1471, 1472, 1473, 1474,\ + 1475, 1476, 1477, 1478, 1479, 1480, 1481, 1482, 1483, 1484, 1485, 1486, 1487, 1488, 1489, 1490, 1491, 1492, 1493, 1494, 1495,\ + 1496, 1497, 1498, 1498, 1499, 1500, 1501, 1502, 1503, 1504, 1505, 1506, 1507, 1508, 1508, 1509, 1510, 1511, 1512, 1513, 1514,\ + 1515, 1516, 1516, 1517, 1518, 1519, 1520, 1521, 1522, 1522, 1523, 1524, 1525, 1526, 1527, 1528, 1528, 1529, 1530, 1531, 1532,\ + 1533, 1533, 1534, 1535, 1536, 1537, 1537, 1538, 1539, 1540, 1541, 1541, 1542, 1543, 1544, 1545, 1545, 1546, 1547, 1548, 1549,\ + 1549, 1550, 1551, 1552, 1552, 1553, 1554, 1555, 1555, 1556, 1557, 1558, 1558, 1559, 1560, 1561, 1561, 1562, 1563, 1564, 1564,\ + 1565, 1566, 1567, 1567, 1568, 1569, 1570, 1570, 1571, 1572, 1572, 1573, 1574, 1575, 1575, 1576, 1577, 1577, 1578, 1579, 1579,\ + 1580, 1581, 1582, 1582, 1583, 1584, 1584, 1585, 1586, 1586, 1587, 1588, 1588, 1589, 1590, 1590, 1591, 1592, 1592, 1593, 1594,\ + 1594, 1595, 1596, 1596, 1597, 1598, 1598, 1599, 1600, 1600, 1601, 1602, 1602, 1603, 1604, 1604, 1605, 1606, 1606, 1607, 1607,\ + 1608, 1609, 1609, 1610, 1611, 1611, 1612, 1612, 1613, 1614, 1614, 1615, 1616, 1616, 1617, 1617, 1618, 1619, 1619, 1620, 1620,\ + 1621, 1622, 1622, 1623, 1623, 1624, 1625, 1625, 1626, 1626, 1627, 1628, 1628, 1629, 1629, 1630, 1631, 1631, 1632, 1632, 1633,\ + 1633, 1634, 1635, 1635, 1636, 1636, 1637, 1637, 1638, 1639, 1639, 1640, 1640, 1641, 1641, 1642, 1643, 1643, 1644, 1644, 1645,\ + 1645, 1646, 1646, 1647, 1648, 1648, 1649, 1649, 1650, 1650, 1651, 1651, 1652, 1652, 1653, 1654, 1654, 1655, 1655, 1656, 1656,\ + 1657, 1657, 1658, 1658, 1659, 1659, 1660, 1660, 1661, 1662, 1662, 1663, 1663, 1664, 1664, 1665, 1665, 1666, 1666, 1667, 1667,\ + 1668, 1668, 1669, 1669, 1670, 1670, 1671, 1671, 1672, 1672, 1673, 1673, 1674, 1674, 1675, 1675, 1676, 1676, 1677, 1677, 1678,\ + 1678, 1679, 1679, 1680, 1680, 1681, 1681, 1682, 1682, 1683, 1683, 1684, 1684, 1685, 1685, 1686, 1686, 1687, 1687, 1688, 1688,\ + 1689, 1689, 1689, 1690, 1690, 1691, 1691, 1692, 1692, 1693, 1693, 1694, 1694, 1695, 1695, 1696, 1696, 1697, 1697, 1697, 1698,\ + 1698, 1699, 1699, 1700, 1700, 1701, 1701, 1702, 1702, 1703, 1703, 1703, 1704, 1704, 1705, 1705, 1706, 1706, 1707, 1707, 1707,\ + 1708, 1708, 1709, 1709, 1710, 1710, 1711, 1711, 1711, 1712, 1712, 1713, 1713, 1714, 1714, 1715, 1715, 1715, 1716, 1716, 1717,\ + 1717, 1718, 1718, 1718, 1719, 1719, 1720, 1720, 1721, 1721, 1721, 1722, 1722, 1723, 1723, 1724, 1724, 1724, 1725, 1725, 1726,\ + 1726, 1726, 1727, 1727, 1728, 1728, 1729, 1729, 1729, 1730, 1730, 1731, 1731, 1731, 1732, 1732, 1733, 1733, 1733, 1734, 1734,\ + 1735, 1735, 1736, 1736, 1736, 1737, 1737, 1738, 1738, 1738, 1739, 1739, 1740, 1740, 1740, 1741, 1741, 1742, 1742, 1742, 1743,\ + 1743, 1744, 1744, 1744, 1745, 1745, 1745, 1746, 1746, 1747, 1747, 1747, 1748, 1748, 1749, 1749, 1749, 1750, 1750, 1751, 1751,\ + 1751, 1752, 1752, 1752, 1753, 1753, 1754, 1754, 1754, 1755, 1755, 1755, 1756, 1756, 1757, 1757, 1757, 1758, 1758, 1758, 1759,\ + 1759, 1760, 1760, 1760, 1761, 1761, 1761, 1762, 1762, 1763, 1763, 1763, 1764, 1764, 1764, 1765, 1765, 1766, 1766, 1766, 1767,\ + 1767, 1767, 1768, 1768, 1768, 1769, 1769, 1769, 1770, 1770, 1771, 1771, 1771, 1772, 1772, 1772, 1773, 1773, 1773, 1774, 1774,\ + 1774, 1775, 1775, 1776, 1776, 1776, 1777, 1777, 1777, 1778, 1778, 1778, 1779, 1779, 1779, 1780, 1780, 1780, 1781, 1781, 1781,\ + 1782, 1782, 1782, 1783, 1783, 1784, 1784, 1784, 1785, 1785, 1785, 1786, 1786, 1786, 1787, 1787, 1787, 1788, 1788, 1788, 1789,\ + 1789, 1789, 1790, 1790, 1790, 1791, 1791, 1791, 1792, 1792, 1792, 1793, 1793, 1793, 1794, 1794, 1794, 1795, 1795, 1795, 1796,\ + 1796, 1796, 1797, 1797, 1797, 1798, 1798, 1798, 1799, 1799, 1799, 1800, 1800, 1800, 1800, 1801, 1801, 1801, 1802, 1802, 1802,\ + 1803, 1803, 1803, 1804, 1804, 1804, 1805, 1805, 1805, 1806, 1806, 1806, 1807, 1807, 1807, 1808, 1808, 1808, 1808, 1809, 1809,\ + 1809, 1810, 1810, 1810, 1811, 1811, 1811, 1812, 1812, 1812, 1813, 1813, 1813, 1813, 1814, 1814, 1814, 1815, 1815, 1815, 1816,\ + 1816, 1816, 1816, 1817, 1817, 1817, 1818, 1818, 1818, 1819, 1819, 1819, 1820, 1820, 1820, 1820, 1821, 1821, 1821, 1822, 1822,\ + 1822, 1823, 1823, 1823, 1823, 1824, 1824, 1824, 1825, 1825, 1825, 1826, 1826, 1826, 1826, 1827, 1827, 1827, 1828, 1828, 1828,\ + 1828, 1829, 1829, 1829, 1830, 1830, 1830, 1831, 1831, 1831, 1831, 1832, 1832, 1832, 1833, 1833, 1833, 1833, 1834, 1834, 1834,\ + 1835, 1835, 1835, 1835, 1836, 1836, 1836, 1837, 1837, 1837, 1837, 1838, 1838, 1838, 1839, 1839, 1839, 1839, 1840, 1840, 1840,\ + 1841, 1841, 1841, 1841, 1842, 1842, 1842, 1842, 1843, 1843, 1843, 1844, 1844, 1844, 1844, 1845, 1845, 1845, 1846, 1846, 1846,\ + 1846, 1847, 1847, 1847, 1847, 1848, 1848, 1848, 1849, 1849, 1849, 1849, 1850, 1850, 1850, 1850, 1851, 1851, 1851, 1852, 1852,\ + 1852, 1852, 1853, 1853, 1853, 1853, 1854, 1854, 1854, 1854, 1855, 1855, 1855, 1856, 1856, 1856, 1856, 1857, 1857, 1857, 1857,\ + 1858, 1858, 1858, 1858, 1859, 1859, 1859, 1860, 1860, 1860, 1860, 1861, 1861, 1861, 1861, 1862, 1862, 1862, 1862, 1863, 1863,\ + 1863, 1863, 1864, 1864, 1864, 1864, 1865, 1865, 1865, 1865, 1866, 1866, 1866, 1867, 1867, 1867, 1867, 1868, 1868, 1868, 1868,\ + 1869, 1869, 1869, 1869, 1870, 1870, 1870, 1870, 1871, 1871, 1871, 1871, 1872, 1872, 1872, 1872, 1873, 1873, 1873, 1873, 1874,\ + 1874, 1874, 1874, 1875, 1875, 1875, 1875, 1876, 1876, 1876, 1876, 1877, 1877, 1877, 1877, 1878, 1878, 1878, 1878, 1879, 1879,\ + 1879, 1879, 1880, 1880, 1880, 1880, 1881, 1881, 1881, 1881, 1882, 1882, 1882, 1882, 1882, 1883, 1883, 1883, 1883, 1884, 1884,\ + 1884, 1884, 1885, 1885, 1885, 1885, 1886, 1886, 1886, 1886, 1887, 1887, 1887, 1887, 1888, 1888, 1888, 1888, 1888, 1889, 1889,\ + 1889, 1889, 1890, 1890, 1890, 1890, 1891, 1891, 1891, 1891, 1892, 1892, 1892, 1892, 1892, 1893, 1893, 1893, 1893, 1894, 1894,\ + 1894, 1894, 1895, 1895, 1895, 1895, 1896, 1896, 1896, 1896, 1896, 1897, 1897, 1897, 1897, 1898, 1898, 1898, 1898, 1899, 1899,\ + 1899, 1899, 1899, 1900, 1900, 1900, 1900, 1901, 1901, 1901, 1901, 1901, 1902, 1902, 1902, 1902, 1903, 1903, 1903, 1903, 1904,\ + 1904, 1904, 1904, 1904, 1905, 1905, 1905, 1905, 1906, 1906, 1906, 1906, 1906, 1907, 1907, 1907, 1907, 1908, 1908, 1908, 1908,\ + 1908, 1909, 1909, 1909, 1909, 1910, 1910, 1910, 1910, 1910, 1911, 1911, 1911, 1911, 1912, 1912, 1912, 1912, 1912, 1913, 1913,\ + 1913, 1913, 1913, 1914, 1914, 1914, 1914, 1915, 1915, 1915, 1915, 1915, 1916, 1916, 1916, 1916, 1917, 1917, 1917, 1917, 1917,\ + 1918, 1918, 1918, 1918, 1918, 1919, 1919, 1919, 1919, 1920, 1920, 1920, 1920, 1920, 1921, 1921, 1921, 1921, 1921, 1922, 1922,\ + 1922, 1922, 1922, 1923, 1923, 1923, 1923, 1924, 1924, 1924, 1924, 1924, 1925, 1925, 1925, 1925, 1925, 1926, 1926, 1926, 1926,\ + 1926, 1927, 1927, 1927, 1927, 1927, 1928, 1928, 1928, 1928, 1929, 1929, 1929, 1929, 1929, 1930, 1930, 1930, 1930, 1930, 1931,\ + 1931, 1931, 1931, 1931, 1932, 1932, 1932, 1932, 1932, 1933, 1933, 1933, 1933, 1933, 1934, 1934, 1934, 1934, 1934, 1935, 1935,\ + 1935, 1935, 1935, 1936, 1936, 1936, 1936, 1936, 1937, 1937, 1937, 1937, 1937, 1938, 1938, 1938, 1938, 1938, 1939, 1939, 1939,\ + 1939, 1939, 1940, 1940, 1940, 1940, 1940, 1941, 1941, 1941, 1941, 1941, 1942, 1942, 1942, 1942, 1942, 1943, 1943, 1943, 1943,\ + 1943, 1944, 1944, 1944, 1944, 1944, 1945, 1945, 1945, 1945, 1945, 1946, 1946, 1946, 1946, 1946, 1947, 1947, 1947, 1947, 1947,\ + 1947, 1948, 1948, 1948, 1948, 1948, 1949, 1949, 1949, 1949, 1949, 1950, 1950, 1950, 1950, 1950, 1951, 1951, 1951, 1951, 1951,\ + 1952, 1952, 1952, 1952, 1952, 1952, 1953, 1953, 1953, 1953, 1953, 1954, 1954, 1954, 1954, 1954, 1955, 1955, 1955, 1955, 1955,\ + 1956, 1956, 1956, 1956, 1956, 1956, 1957, 1957, 1957, 1957, 1957, 1958, 1958, 1958, 1958, 1958, 1958, 1959, 1959, 1959, 1959,\ + 1959, 1960, 1960, 1960, 1960, 1960, 1961, 1961, 1961, 1961, 1961, 1961, 1962, 1962, 1962, 1962, 1962, 1963, 1963, 1963, 1963,\ + 1963, 1963, 1964, 1964, 1964, 1964, 1964, 1965, 1965, 1965, 1965, 1965, 1965, 1966, 1966, 1966, 1966, 1966, 1967, 1967, 1967,\ + 1967, 1967, 1967, 1968, 1968, 1968, 1968, 1968, 1969, 1969, 1969, 1969, 1969, 1969, 1970, 1970, 1970, 1970, 1970, 1971, 1971,\ + 1971, 1971, 1971, 1971, 1972, 1972, 1972, 1972, 1972, 1972, 1973, 1973, 1973, 1973, 1973, 1974, 1974, 1974, 1974, 1974, 1974,\ + 1975, 1975, 1975, 1975, 1975, 1975, 1976, 1976, 1976, 1976, 1976, 1977, 1977, 1977, 1977, 1977, 1977, 1978, 1978, 1978, 1978,\ + 1978, 1978, 1979, 1979, 1979, 1979, 1979, 1979, 1980, 1980, 1980, 1980, 1980, 1980, 1981, 1981, 1981, 1981, 1981, 1982, 1982,\ + 1982, 1982, 1982, 1982, 1983, 1983, 1983, 1983, 1983, 1983, 1984, 1984, 1984, 1984, 1984, 1984, 1985, 1985, 1985, 1985, 1985,\ + 1985, 1986, 1986, 1986, 1986, 1986, 1986, 1987, 1987, 1987, 1987, 1987, 1987, 1988, 1988, 1988, 1988, 1988, 1988, 1989, 1989,\ + 1989, 1989, 1989, 1989, 1990, 1990, 1990, 1990, 1990, 1990, 1991, 1991, 1991, 1991, 1991, 1991, 1992, 1992, 1992, 1992, 1992,\ + 1992, 1993, 1993, 1993, 1993, 1993, 1993, 1994, 1994, 1994, 1994, 1994, 1994, 1995, 1995, 1995, 1995, 1995, 1995, 1996, 1996,\ + 1996, 1996, 1996, 1996, 1997, 1997, 1997, 1997, 1997, 1997, 1998, 1998, 1998, 1998, 1998, 1998, 1999, 1999, 1999, 1999, 1999,\ + 1999, 1999, 2000, 2000, 2000, 2000, 2000, 2000, 2001, 2001, 2001, 2001, 2001, 2001, 2002, 2002, 2002, 2002, 2002, 2002, 2003,\ + 2003, 2003, 2003, 2003, 2003, 2003, 2004, 2004, 2004, 2004, 2004, 2004, 2005, 2005, 2005, 2005, 2005, 2005, 2006, 2006, 2006,\ + 2006, 2006, 2006, 2006, 2007, 2007, 2007, 2007, 2007, 2007, 2008, 2008, 2008, 2008, 2008, 2008, 2009, 2009, 2009, 2009, 2009,\ + 2009, 2009, 2010, 2010, 2010, 2010, 2010, 2010, 2011, 2011, 2011, 2011, 2011, 2011, 2011, 2012, 2012, 2012, 2012, 2012, 2012,\ + 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2014, 2014, 2014, 2014, 2014, 2014, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2016,\ + 2016, 2016, 2016, 2016, 2016, 2017, 2017, 2017, 2017, 2017, 2017, 2017, 2018, 2018, 2018, 2018, 2018, 2018, 2019, 2019, 2019,\ + 2019, 2019, 2019, 2019, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2021, 2021, 2021, 2021, 2021, 2021, 2022, 2022, 2022, 2022,\ + 2022, 2022, 2022, 2023, 2023, 2023, 2023, 2023, 2023, 2023, 2024, 2024, 2024, 2024, 2024, 2024, 2024, 2025, 2025, 2025, 2025,\ + 2025, 2025, 2026, 2026, 2026, 2026, 2026, 2026, 2026, 2027, 2027, 2027, 2027, 2027, 2027, 2027, 2028, 2028, 2028, 2028, 2028,\ + 2028, 2028, 2029, 2029, 2029, 2029, 2029, 2029, 2029, 2030, 2030, 2030, 2030, 2030, 2030, 2030, 2031, 2031, 2031, 2031, 2031,\ + 2031, 2031, 2032, 2032, 2032, 2032, 2032, 2032, 2032, 2033, 2033, 2033, 2033, 2033, 2033, 2033, 2034, 2034, 2034, 2034, 2034,\ + 2034, 2034, 2035, 2035, 2035, 2035, 2035, 2035, 2035, 2036, 2036, 2036, 2036, 2036, 2036, 2036, 2037, 2037, 2037, 2037, 2037,\ + 2037, 2037, 2038, 2038, 2038, 2038, 2038, 2038, 2038, 2039, 2039, 2039, 2039, 2039, 2039, 2039, 2040, 2040, 2040, 2040}; + +static int dump_i2c_reg(struct ti_lmu_bl_chip *chip) +{ + struct regmap *regmap = chip->lmu->regmap; + int ret_val = 0; + + regmap_read(regmap, 0x10, &ret_val); + pr_err("[bkl] %s read 0x10 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x1A, &ret_val); + pr_err("[bkl] %s read 0x1A = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x1C, &ret_val); + pr_err("[bkl] %s read 0x1C = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x22, &ret_val); + pr_err("[bkl] %s read 0x22 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x23, &ret_val); + pr_err("[bkl] %s read 0x23 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x24, &ret_val); + pr_err("[bkl] %s read 0x24 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x18, &ret_val); + pr_err("[bkl] %s read 0x18 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x16, &ret_val); + pr_err("[bkl] %s read 0x16 = 0x%x\n", __func__, ret_val); + + return 0; +} + +int ti_hbm_set(enum backlight_hbm_mode hbm_mode) +{ + struct regmap *regmap = bl_chip->lmu->regmap; + int value = 0; + + pr_err("[bkl] %s enter\n", __func__); + + switch (hbm_mode) { + case HBM_MODE_DEFAULT: + regmap_write(regmap, 0x18, 0x15);//21.8mA + pr_err("This is hbm mode 1\n"); + break; + case HBM_MODE_LEVEL1: + regmap_write(regmap, 0x18, 0x19);//25mA + pr_err("This is hbm mode 2\n"); + break; + case HBM_MODE_LEVEL2: + regmap_write(regmap, 0x18, 0x1C);//27.4mA + pr_err("This is hbm mode 3\n"); + break; + case HBM_MODE_LEVEL3: + regmap_write(regmap, 0x18, 0x17);//23.4mA + pr_err("This is hbm mode 4\n"); + break; + case HBM_MODE_LEVEL4: + regmap_write(regmap, 0x18, 0x18);//24.2mA + pr_err("This is hbm mode 5\n"); + break; + default: + pr_err("This isn't hbm mode\n"); + break; + } + + regmap_read(regmap, 0x18, &value); + pr_err("[bkl]%s hbm_mode = %d,regmap value=0x%x\n", __func__, hbm_mode, value); + return 0; +} + +static int ti_lmu_backlight_enable(struct ti_lmu_bl *lmu_bl, int enable) +{ + struct ti_lmu_bl_chip *chip = lmu_bl->chip; + struct regmap *regmap = chip->lmu->regmap; + unsigned long enable_time = chip->cfg->reginfo->enable_usec; + u8 *reg = chip->cfg->reginfo->enable; + + if (!reg) + return -EINVAL; + + if (enable) + return regmap_write(regmap, *reg, 0x02); + else + return regmap_write(regmap, *reg, 0x00); + + if (enable_time > 0) + usleep_range(enable_time, enable_time + 100); +} + +static void ti_lmu_backlight_pwm_ctrl(struct ti_lmu_bl *lmu_bl, int brightness, + int max_brightness) +{ + struct pwm_device *pwm; + unsigned int duty, period; + + if (!lmu_bl->pwm) { + pwm = devm_pwm_get(lmu_bl->chip->dev, DEFAULT_PWM_NAME); + if (IS_ERR(pwm)) { + dev_err(lmu_bl->chip->dev, + "Can not get PWM device, err: %ld\n", + PTR_ERR(pwm)); + return; + } + + lmu_bl->pwm = pwm; + } + + period = lmu_bl->pwm_period; + duty = brightness * period / max_brightness; + + pwm_config(lmu_bl->pwm, duty, period); + if (duty) + pwm_enable(lmu_bl->pwm); + else + pwm_disable(lmu_bl->pwm); +} + +static int ti_lmu_backlight_update_brightness_register(struct ti_lmu_bl *lmu_bl, + int brightness) +{ + const struct ti_lmu_bl_cfg *cfg = lmu_bl->chip->cfg; + const struct ti_lmu_bl_reg *reginfo = cfg->reginfo; + struct regmap *regmap = lmu_bl->chip->lmu->regmap; + u8 reg, val; + int ret; + // int i = 0; + + regmap_write(regmap, 0x13, 0x11); + + if (lmu_bl->mode == BL_PWM_BASED) { + switch (cfg->pwm_action) { + case UPDATE_PWM_ONLY: + /* No register update is required */ + return 0; + case UPDATE_MAX_BRT: + /* + * PWM can start from any non-zero code and dim down + * to zero. So, brightness register should be updated + * even in PWM mode. + */ + if (brightness > 0) + brightness = MAX_BRIGHTNESS_11BIT; + else + brightness = 0; + break; + default: + break; + } + } + + /* + * Brightness register update + * + * 11 bit dimming: update LSB bits and write MSB byte. + * MSB brightness should be shifted. + * 8 bit dimming: write MSB byte. + */ + if (brightness < 0) { + return -EINVAL; + } + + if (!reginfo->brightness_msb) + return -EINVAL; + + if (cfg->max_brightness == MAX_BRIGHTNESS_11BIT) { + if (!reginfo->brightness_lsb) + return -EINVAL; + + if(lmu_bl->map_type == EXPONENTIAL_TYPE && lmu_bl->led_current_align) { + if(brightness) { + pr_info("[bkl] %s brightness = %d, align = %d\n", __func__, + brightness, lmu_bl->led_current_align); + brightness = brightness*8383/10000+324; + } + } + + reg = reginfo->brightness_lsb[lmu_bl->bank_id]; + ret = regmap_update_bits(regmap, reg, + LMU_BACKLIGHT_11BIT_LSB_MASK, + brightness); + if (ret) + return ret; + pr_info("[bkl][after]11bit %s brightness = %d\n", __func__, brightness); + val = (brightness >> LMU_BACKLIGHT_11BIT_MSB_SHIFT) & 0xFF; + } else { + val = brightness & 0xFF; + pr_err("[bkl]8bit %s val = %d\n", __func__, val); + } + + reg = reginfo->brightness_msb[lmu_bl->bank_id]; + ret = regmap_write(regmap, reg, val); + return ret; +} +static int ti_lmu_backlight_set_brightness(int brightness) +{ + struct ti_lmu_bl *lmu_bl = bl_chip->lmu_bl; + int ret; + + if (brightness > 0) + ret = ti_lmu_backlight_enable(lmu_bl, 1); + else + ret = ti_lmu_backlight_enable(lmu_bl, 0); + + if (ret) { + pr_err("[bkl] %s enable failed ret %d \n", __func__, ret); + return ret; + } + + return ti_lmu_backlight_update_brightness_register(lmu_bl, brightness); +} + +int lm3697_set_brightness(int brightness) +{ + printk(KERN_INFO "[bkl][before]%s brightness = %d\n", __func__, brightness); + + return ti_lmu_backlight_set_brightness(brightness); +} + +static int ti_lmu_backlight_get_brightness(struct backlight_device *bl_dev) +{ + return bl_dev->props.brightness; +} + +static int ti_lmu_backlight_update_status(struct backlight_device *bl_dev) +{ + struct ti_lmu_bl *lmu_bl = bl_get_data(bl_dev); + int brightness = bl_dev->props.brightness; + int ret; + + if (bl_dev->props.state & (BL_CORE_SUSPENDED | BL_CORE_FBBLANK)) + brightness = 0; + + if (brightness > 0) + ret = ti_lmu_backlight_enable(lmu_bl, 1); + else + ret = ti_lmu_backlight_enable(lmu_bl, 0); + + if (ret) + return ret; + + if (lmu_bl->mode == BL_PWM_BASED) + ti_lmu_backlight_pwm_ctrl(lmu_bl, brightness, + bl_dev->props.max_brightness); + + return ti_lmu_backlight_update_brightness_register(lmu_bl, brightness); +} + +static const struct backlight_ops lmu_backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = ti_lmu_backlight_update_status, + .get_brightness = ti_lmu_backlight_get_brightness, +}; + +static int ti_lmu_backlight_of_get_ctrl_bank(struct device_node *np, + struct ti_lmu_bl *lmu_bl) +{ + const char *name; + u32 *sources; + int num_channels = lmu_bl->chip->cfg->num_channels; + int ret, num_sources; + + sources = devm_kzalloc(lmu_bl->chip->dev, num_channels, GFP_KERNEL); + if (!sources) + return -ENOMEM; + + if (!of_property_read_string(np, "label", &name)) + lmu_bl->name = name; + else + lmu_bl->name = np->name; + + ret = of_property_count_u32_elems(np, "led-sources"); + if (ret < 0 || ret > num_channels) + return -EINVAL; + num_sources = ret; + ret = of_property_read_u32_array(np, "led-sources", sources, + num_sources); + if (ret){ + return ret; + } + lmu_bl->led_sources = 0; + while (num_sources--) + set_bit(sources[num_sources], &lmu_bl->led_sources); + + return 0; +} + +static void ti_lmu_backlight_of_get_light_properties(struct device_node *np, + struct ti_lmu_bl *lmu_bl) +{ + of_property_read_u32(np, "default-brightness-level", + &lmu_bl->default_brightness); + + of_property_read_u32(np, "ramp-up-msec", &lmu_bl->ramp_up_msec); + of_property_read_u32(np, "ramp-down-msec", &lmu_bl->ramp_down_msec); + + if(of_property_read_u32(np, "current-mode", &lmu_bl->led_current_mode)) { + lmu_bl->led_current_mode = HBM_MODE_DEFAULT; + pr_info("[bkl] %s led_current_mode default %d\n", __func__, + lmu_bl->led_current_mode); + } + + if(of_property_read_u32(np, "boost-ovp", &lmu_bl->led_boost_ovp)) { + lmu_bl->led_boost_ovp = BOOST_OVP_32V; + pr_info("[bkl] %s led_boost_ovp default %d\n", __func__, + lmu_bl->led_boost_ovp); + } + + if(of_property_read_u32(np, "boost-freq", &lmu_bl->led_boost_freq)) { + lmu_bl->led_boost_freq = BOOST_FREQ_500K; + pr_info("[bkl] %s led_boost_freq default %d\n", __func__, + lmu_bl->led_boost_freq); + } + + + if(of_property_read_u32(np, "map-type", &lmu_bl->map_type)) { + lmu_bl->map_type = LINEAR_TYPE; + pr_info("[bkl] %s map_type default %d\n", __func__, + lmu_bl->map_type); + } + + if(of_property_read_u32(np, "current-align-type", &lmu_bl->led_current_align)) { + lmu_bl->led_current_align = ALIGN_NONE; + pr_info("[bkl] %s led_current_align default %d, no align, keep as lm3697 exp mode\n", __func__, + lmu_bl->led_current_align); + } +} + +static void ti_lmu_backlight_of_get_brightness_mode(struct device_node *np, + struct ti_lmu_bl *lmu_bl) +{ + of_property_read_u32(np, "pwm-period", &lmu_bl->pwm_period); + + if (lmu_bl->pwm_period > 0) + lmu_bl->mode = BL_PWM_BASED; + else + lmu_bl->mode = BL_REGISTER_BASED; +} + +static int ti_lmu_backlight_of_create(struct ti_lmu_bl_chip *chip, + struct device_node *np) +{ + struct device_node *child; + struct ti_lmu_bl *lmu_bl, *each; + int ret, num_backlights; + int i = 0; + + num_backlights = of_get_child_count(np); + if (num_backlights == 0) { + dev_err(chip->dev, "No backlight strings\n"); + return -ENODEV; + } + + /* One chip can have mulitple backlight strings */ + lmu_bl = devm_kzalloc(chip->dev, sizeof(*lmu_bl) * num_backlights, + GFP_KERNEL); + if (!lmu_bl) + return -ENOMEM; + + /* Child is mapped to LMU backlight control bank */ + for_each_child_of_node(np, child) { + each = lmu_bl + i; + each->bank_id = i; + each->chip = chip; + + ret = ti_lmu_backlight_of_get_ctrl_bank(child, each); + if (ret) { + of_node_put(np); + return ret; + } + + ti_lmu_backlight_of_get_light_properties(child, each); + ti_lmu_backlight_of_get_brightness_mode(child, each); + + i++; + } + + chip->lmu_bl = lmu_bl; + chip->num_backlights = num_backlights; + + return 0; +} + +static int ti_lmu_backlight_update_ctrl_mode(struct ti_lmu_bl *lmu_bl) +{ + struct regmap *regmap = lmu_bl->chip->lmu->regmap; + u32 *reg = lmu_bl->chip->cfg->reginfo->mode + lmu_bl->bank_id; + u8 val; + + if (!reg) + return 0; + + /* + * Update PWM configuration register. + * If the mode is register based, then clear the bit. + */ + if (lmu_bl->mode == BL_PWM_BASED) + val = LMU_BL_GET_VAL(*reg); + else + val = 0; + + return regmap_update_bits(regmap, LMU_BL_GET_ADDR(*reg), + LMU_BL_GET_MASK(*reg), val); +} + +static int ti_lmu_backlight_convert_ramp_to_index(struct ti_lmu_bl *lmu_bl, + enum ti_lmu_bl_ramp_mode mode) +{ + const int *ramp_table = lmu_bl->chip->cfg->ramp_table; + const int size = lmu_bl->chip->cfg->size_ramp; + unsigned int msec; + int i; + + if (!ramp_table) + return -EINVAL; + + switch (mode) { + case BL_RAMP_UP: + msec = lmu_bl->ramp_up_msec; + break; + case BL_RAMP_DOWN: + msec = lmu_bl->ramp_down_msec; + break; + default: + return -EINVAL; + } + + if (msec <= ramp_table[0]) + return 0; + + if (msec > ramp_table[size - 1]) + return size - 1; + + for (i = 1; i < size; i++) { + if (msec == ramp_table[i]) + return i; + + /* Find an approximate index by looking up the table */ + if (msec > ramp_table[i - 1] && msec < ramp_table[i]) { + if (msec - ramp_table[i - 1] < ramp_table[i] - msec) + return i - 1; + else + return i; + } + } + return -EINVAL; +} + +static int ti_lmu_backlight_set_ramp(struct ti_lmu_bl *lmu_bl) +{ + struct regmap *regmap = lmu_bl->chip->lmu->regmap; + const struct ti_lmu_bl_reg *reginfo = lmu_bl->chip->cfg->reginfo; + int offset = reginfo->ramp_reg_offset; + int i, ret, index; + u32 reg; + + for (i = BL_RAMP_UP; i <= BL_RAMP_DOWN; i++) { + index = ti_lmu_backlight_convert_ramp_to_index(lmu_bl, i); + if (index > 0) { + if (!reginfo->ramp) + break; + + if (lmu_bl->bank_id == 0) + reg = reginfo->ramp[i]; + else + reg = reginfo->ramp[i] + offset; + + /* + * Note that the result of LMU_BL_GET_VAL() is + * shift bit. So updated bit is shifted index value. + */ + ret = regmap_update_bits(regmap, LMU_BL_GET_ADDR(reg), + LMU_BL_GET_MASK(reg), + index << LMU_BL_GET_VAL(reg)); + if (ret) + return ret; + } + } + + return 0; +} + +static int ti_lmu_backlight_configure(struct ti_lmu_bl *lmu_bl) +{ + int ret; + + ret = ti_lmu_backlight_update_ctrl_mode(lmu_bl); + if (ret) + return ret; + + return ti_lmu_backlight_set_ramp(lmu_bl); +} + +static int ti_lmu_backlight_init(struct ti_lmu_bl_chip *chip) +{ + struct regmap *regmap = chip->lmu->regmap; + unsigned char boost_ctl; + unsigned char brightness_cfg; + pr_err("[bkl] %s enter\n", __func__); + /* + * 'init' register data consists of address, mask, value. + * Driver can get each data by using LMU_BL_GET_ADDR(), + * LMU_BL_GET_MASK(), LMU_BL_GET_VAL(). + */ + + boost_ctl = (chip->lmu_bl->led_boost_ovp<<1) | chip->lmu_bl->led_boost_freq; + brightness_cfg = chip->lmu_bl->map_type; + + regmap_write(regmap, 0x10, 0x07); + regmap_write(regmap, 0x12, 0x11); + regmap_write(regmap, 0x16, brightness_cfg); + regmap_write(regmap, 0x19, 0x07); + regmap_write(regmap, 0x18, 0x15);//21.8mA default + regmap_write(regmap, 0x1A, boost_ctl); + regmap_write(regmap, 0x1C, 0x0E); + //regmap_write(regmap, 0x22, 0x07); + //regmap_write(regmap, 0x23, 0xFF); + regmap_write(regmap, 0x24, 0x02); + regmap_write(regmap, 0xB4, 0x03); + + + pr_err("[bkl] %s finish\n", __func__); + return 0; +} + +static int ti_lmu_backlight_reload(struct ti_lmu_bl_chip *chip) +{ + struct ti_lmu_bl *each; + int i, ret; + + ret = ti_lmu_backlight_init(chip); + if (ret) + return ret; + + for (i = 0; i < chip->num_backlights; i++) { + each = chip->lmu_bl + i; + ret = ti_lmu_backlight_configure(each); + if (ret) + return ret; + + backlight_update_status(each->bl_dev); + } + + return 0; +} + +static int ti_lmu_backlight_add_device(struct device *dev, + struct ti_lmu_bl *lmu_bl) +{ + struct backlight_device *bl_dev; + struct backlight_properties props; + memset(&props, 0, sizeof(struct backlight_properties)); + props.type = BACKLIGHT_PLATFORM; + props.brightness = lmu_bl->default_brightness; + props.max_brightness = lmu_bl->chip->cfg->max_brightness; + + bl_dev = backlight_device_register(LM3697_NAME, dev, lmu_bl, + &lmu_backlight_ops, &props); + if (IS_ERR(bl_dev)) + return PTR_ERR(bl_dev); + + lmu_bl->bl_dev = bl_dev; + + return 0; +} + +static ssize_t i2c_reg_dump_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + + struct regmap *regmap = bl_chip->lmu->regmap; + int ret_val = 0; + + regmap_read(regmap, 0x00, &ret_val); + pr_err("[bkl] %s read 0x00 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x01, &ret_val); + pr_err("[bkl] %s read 0x01 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x10, &ret_val); + pr_err("[bkl] %s read 0x10 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x11, &ret_val); + pr_err("[bkl] %s read 0x11 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x12, &ret_val); + pr_err("[bkl] %s read 0x12 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x13, &ret_val); + pr_err("[bkl] %s read 0x13 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x14, &ret_val); + pr_err("[bkl] %s read 0x14 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x15, &ret_val); + pr_err("[bkl] %s read 0x15 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x16, &ret_val); + pr_err("[bkl] %s read 0x16 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x17, &ret_val); + pr_err("[bkl] %s read 0x17 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x18, &ret_val); + pr_err("[bkl] %s read 0x18 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x19, &ret_val); + pr_err("[bkl] %s read 0x19 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x1A, &ret_val); + pr_err("[bkl] %s read 0x1A = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x1B, &ret_val); + pr_err("[bkl] %s read 0x1B = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x1C, &ret_val); + pr_err("[bkl] %s read 0x1C = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x20, &ret_val); + pr_err("[bkl] %s read 0x20 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x21, &ret_val); + pr_err("[bkl] %s read 0x21 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x22, &ret_val); + pr_err("[bkl] %s read 0x22 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x23, &ret_val); + pr_err("[bkl] %s read 0x23 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0x24, &ret_val); + pr_err("[bkl] %s read 0x24 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0xB0, &ret_val); + pr_err("[bkl] %s read 0xB0 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0xB2, &ret_val); + pr_err("[bkl] %s read 0xB2 = 0x%x\n", __func__, ret_val); + regmap_read(regmap, 0xB4, &ret_val); + pr_err("[bkl] %s read 0xB4 = 0x%x\n", __func__, ret_val); + + ret = snprintf(buf, PAGE_SIZE, "reg dump done \n"); + return ret; +} + +DEVICE_ATTR(i2c_reg_dump, S_IRUGO, i2c_reg_dump_show, NULL); + +static int ti_lm3697_read_chipid(struct ti_lmu_bl_chip *chip) +{ + int ret_val = 0; + unsigned char cnt = 0; + struct regmap *regmap = chip->lmu->regmap; + + while (cnt < 5) + { + regmap_read(regmap, 0x00, &ret_val); + switch (ret_val) + { + case 0x01: + pr_info("%s ti_lm3697 detected\n",__func__); + return 0; + default: + pr_info("%s unsupported device revision (0x%x)\n", + __func__, ret_val); + break; + } + cnt++; + msleep(2); + } + + return -EINVAL; +} +static struct ti_lmu_bl_chip * +ti_lmu_backlight_register(struct device *dev, struct ti_lmu *lmu, + const struct ti_lmu_bl_cfg *cfg) +{ + struct ti_lmu_bl_chip *chip; + struct ti_lmu_bl *each; + int i, ret; + + pr_debug("[bkl] %s enter\n", __func__); + + if (!cfg) { + dev_err(dev, "Operation is not configured\n"); + goto err_ein; + } + + chip = devm_kzalloc(dev, sizeof(*chip), 0); + if (!chip) + goto err_eno; + + chip->dev = dev; + chip->lmu = lmu; + chip->cfg = cfg; + + ret = ti_lmu_backlight_of_create(chip, dev->of_node); + if (ret){ + goto err_init; + } + + ret = ti_lm3697_read_chipid(chip); + if (ret < 0) + { + pr_err("%s : ID idenfy failed\n", __func__); + goto err_init; + } + + for (i = 0; i < chip->num_backlights; i++) { + each = chip->lmu_bl + i; + ret = ti_lmu_backlight_configure(each); + if (ret) { + dev_err(dev, "[bkl] Backlight config err: %d\n", ret); + goto err_init; + } + ret = ti_lmu_backlight_add_device(dev, each); + if (ret) { + dev_err(dev, "[bkl] Backlight device err: %d\n", ret); + goto err_init; + } + } + + ret = ti_lmu_backlight_init(chip); + if (ret) { + dev_err(dev, "Backlight init err: %d\n", ret); + goto err_init; + } + + bl_chip = chip; + + ti_hbm_set(chip->lmu_bl->led_current_mode); + + dump_i2c_reg(chip); + + ret = device_create_file(dev, &dev_attr_i2c_reg_dump); + if (ret < 0) { + dev_err(dev, "failed to create dev_attr_i2c_reg_dump\n"); + } + + pr_err("[bkl] %s finish\n", __func__); + + return chip; + +err_init: + if(chip->lmu_bl) + devm_kfree(dev, chip->lmu_bl); + if(chip) + devm_kfree(dev, chip); +err_ein: + gpio_free(lmu->en_gpio); + return ERR_PTR(-EINVAL); +err_eno: + gpio_free(lmu->en_gpio); + return ERR_PTR(-ENOMEM); +} + +static void ti_lmu_backlight_unregister(struct ti_lmu_bl_chip *chip) +{ + struct ti_lmu_bl *each; + int i; + + /* Turn off the brightness */ + for (i = 0; i < chip->num_backlights; i++) { + each = chip->lmu_bl + i; + each->bl_dev->props.brightness = 0; + backlight_update_status(each->bl_dev); + backlight_device_unregister(each->bl_dev); + } + device_remove_file(chip->dev, &dev_attr_i2c_reg_dump); +} + +static int ti_lmu_backlight_monitor_notifier(struct notifier_block *nb, + unsigned long action, void *unused) +{ + struct ti_lmu_bl_chip *chip = container_of(nb, struct ti_lmu_bl_chip, + nb); + + if (action == LMU_EVENT_MONITOR_DONE) { + if (ti_lmu_backlight_reload(chip)) + return NOTIFY_STOP; + } + + return NOTIFY_OK; +} + +static int ti_lmu_backlight_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ti_lmu *lmu = dev_get_drvdata(dev->parent); + struct ti_lmu_bl_chip *chip; + int ret; + + pr_err("[bkl] %s enter\n", __func__); + + + chip = ti_lmu_backlight_register(dev, lmu, &lmu_bl_cfg[pdev->id]); + if (IS_ERR(chip)) { + pr_err("[bkl] %s error bkl register\n", __func__); + return -ENODEV; + } + /* + * Notifier callback is required because backlight device needs + * reconfiguration after fault detection procedure is done by + * ti-lmu-fault-monitor driver. + */ + if (chip->cfg->fault_monitor_used) { + chip->nb.notifier_call = ti_lmu_backlight_monitor_notifier; + ret = blocking_notifier_chain_register(&chip->lmu->notifier, + &chip->nb); + if (ret) + return ret; + } + + platform_set_drvdata(pdev, chip); + pr_err("[bkl] %s finish\n", __func__); + return 0; +} + +static int ti_lmu_backlight_remove(struct platform_device *pdev) +{ + struct ti_lmu_bl_chip *chip = platform_get_drvdata(pdev); + + if (chip->cfg->fault_monitor_used) + blocking_notifier_chain_unregister(&chip->lmu->notifier, + &chip->nb); + + ti_lmu_backlight_unregister(chip); + + return 0; +} + +static struct platform_driver ti_lmu_backlight_driver = { + .probe = ti_lmu_backlight_probe, + .remove = ti_lmu_backlight_remove, + .driver = { + .name = "ti-lmu-backlight", + }, +}; + +int ti_lmu_backlight_device_init(void) +{ + return platform_driver_register(&ti_lmu_backlight_driver); +} +EXPORT_SYMBOL(ti_lmu_backlight_device_init); +void ti_lmu_backlight_exit(void) +{ + return platform_driver_unregister(&ti_lmu_backlight_driver); +} +EXPORT_SYMBOL(ti_lmu_backlight_exit); +// module_platform_driver(ti_lmu_backlight_driver) + +MODULE_DESCRIPTION("TI LMU Backlight Driver"); +MODULE_AUTHOR("Milo Kim"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:ti-lmu-backlight"); diff --git a/drivers/backlight/lm3697/ti_lm3697_backlight_data.c b/drivers/backlight/lm3697/ti_lm3697_backlight_data.c new file mode 100644 index 000000000000..f2ed06f15c79 --- /dev/null +++ b/drivers/backlight/lm3697/ti_lm3697_backlight_data.c @@ -0,0 +1,82 @@ +/* + * TI LMU (Lighting Management Unit) Backlight Device Data + * + * Copyright 2016 Texas Instruments + * + * Author: Milo Kim + * + * 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. + */ + +#include +#include +#include "ti_lm3697.h" +#include "ti_lm3697_regulator.h" +#include "ti-lmu.h" + +/* LM3697 */ +static u32 lm3697_init_regs[] = { + //LM3697_INIT_RAMP_SELECT, + LM3697_REG_HVLED_OUTPUT_CFG, + LM3697_REG_BOOST_CFG, + LM3697_REG_PWM_CFG, + LM3697_REG_BRT_B_LSB, + LM3697_REG_BRT_B_MSB, + LM3697_REG_ENABLE, +}; + +static u32 lm3697_channel_regs[] = { + LM3697_CHANNEL_1, + LM3697_CHANNEL_2, + LM3697_CHANNEL_3, +}; + +static u32 lm3697_mode_regs[] = { + LM3697_MODE_PWM_B, +}; + +static u32 lm3697_ramp_regs[] = { + LM3697_RAMPUP, + LM3697_RAMPDN, +}; + +static u8 lm3697_enable_reg = LM3697_REG_ENABLE; + +static u8 lm3697_brightness_msb_regs[] = { + LM3697_REG_BRT_B_MSB, +}; + +static u8 lm3697_brightness_lsb_regs[] = { + LM3697_REG_BRT_B_LSB, +}; + +static const struct ti_lmu_bl_reg lm3697_reg_info = { + .init = lm3697_init_regs, + .num_init = ARRAY_SIZE(lm3697_init_regs), + .channel = lm3697_channel_regs, + .mode = lm3697_mode_regs, + .ramp = lm3697_ramp_regs, + .ramp_reg_offset = 1, /* For LM3697_REG_BL1_RAMPUP/DN */ + .enable = &lm3697_enable_reg, + .brightness_msb = lm3697_brightness_msb_regs, + .brightness_lsb = lm3697_brightness_lsb_regs, +}; + +static int common_ramp_table[] = { + 2, 250, 500, 1000, 2000, 4000, 8000, 16000, +}; + +struct ti_lmu_bl_cfg lmu_bl_cfg[LMU_MAX_ID] = { + { + .reginfo = &lm3697_reg_info, + .num_channels = LM3697_MAX_CHANNELS, + .max_brightness = MAX_BRIGHTNESS_11BIT, + .pwm_action = UPDATE_PWM_AND_BRT_REGISTER, + .ramp_table = common_ramp_table, + .size_ramp = ARRAY_SIZE(common_ramp_table), + .fault_monitor_used = true, + }, +}; +EXPORT_SYMBOL_GPL(lmu_bl_cfg); diff --git a/drivers/backlight/lm3697/ti_lm3697_regulator.h b/drivers/backlight/lm3697/ti_lm3697_regulator.h new file mode 100644 index 000000000000..343188bbacc3 --- /dev/null +++ b/drivers/backlight/lm3697/ti_lm3697_regulator.h @@ -0,0 +1,63 @@ +/* + * TI LMU (Lighting Management Unit) Device Register Map + * + * Copyright 2017 Texas Instruments + * + * Author: Milo Kim + * + * 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. + */ + +#ifndef __MFD_TI_LMU_REGISTER_H__ +#define __MFD_TI_LMU_REGISTER_H__ + +#include + +/* LM3697 */ +#define LM3697_REG_HVLED_OUTPUT_CFG 0x10 +#define LM3697_HVLED1_CFG_MASK BIT(0) +#define LM3697_HVLED2_CFG_MASK BIT(1) +#define LM3697_HVLED3_CFG_MASK BIT(2) +#define LM3697_HVLED1_CFG_SHIFT 0 +#define LM3697_HVLED2_CFG_SHIFT 1 +#define LM3697_HVLED3_CFG_SHIFT 2 + +#define LM3697_REG_BL0_RAMP 0x11 +#define LM3697_REG_BL1_RAMP 0x12 +#define LM3697_RAMPUP_MASK 0xF0 +#define LM3697_RAMPUP_SHIFT 4 +#define LM3697_RAMPDN_MASK 0x0F +#define LM3697_RAMPDN_SHIFT 0 + +#define LM3697_REG_RAMP_CONF 0x14 +#define LM3697_RAMP_MASK 0x0F +#define LM3697_RAMP_EACH 0x05 + +#define LM3697_REG_BOOST_CFG 0x1A + +#define LM3697_REG_PWM_CFG 0x1C +#define LM3697_PWM_A_MASK BIT(0) +#define LM3697_PWM_B_MASK BIT(1) + +#define LM3697_REG_IMAX_A 0x17 +#define LM3697_REG_IMAX_B 0x18 + +#define LM3697_REG_FEEDBACK_ENABLE 0x19 + +#define LM3697_REG_BRT_A_LSB 0x20 +#define LM3697_REG_BRT_A_MSB 0x21 +#define LM3697_REG_BRT_B_LSB 0x22 +#define LM3697_REG_BRT_B_MSB 0x23 + +#define LM3697_REG_ENABLE 0x24 + +#define LM3697_REG_OPEN_FAULT_STATUS 0xB0 + +#define LM3697_REG_SHORT_FAULT_STATUS 0xB2 + +#define LM3697_REG_MONITOR_ENABLE 0xB4 + +#define LM3697_MAX_REG 0xB4 +#endif diff --git a/drivers/backlight/sm5350/Android.mk b/drivers/backlight/sm5350/Android.mk new file mode 100644 index 000000000000..41a59b25e04d --- /dev/null +++ b/drivers/backlight/sm5350/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := sm5350_bl.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/backlight/sm5350/Kbuild b/drivers/backlight/sm5350/Kbuild new file mode 100644 index 000000000000..41852f505da4 --- /dev/null +++ b/drivers/backlight/sm5350/Kbuild @@ -0,0 +1,5 @@ +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +obj-m += sm5350_bl.o + diff --git a/drivers/backlight/sm5350/sm5350_bl.c b/drivers/backlight/sm5350/sm5350_bl.c new file mode 100644 index 000000000000..8cdf68bbc3e2 --- /dev/null +++ b/drivers/backlight/sm5350/sm5350_bl.c @@ -0,0 +1,523 @@ +/* + * SM5350 BL Driver + * + * SiliconMitus SM5350 Backlight driver chip + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SM5350_NAME "sm5350-bl" + +#define MAX_BRIGHTNESS (2047) +#define BANK_NONE 0x00 +#define BANK_A 0x01 +#define BANK_B 0x02 + +#define SM5350_HVLED_CURR_SINK_OUT_CFG 0x07 +#define SM5350_BRIGHTNESS_CFG 0x01 + +#define SM5350_REVISION_REG 0x00 +#define SM5350_SW_RESET_REG 0x01 +#define SM5350_HVLED_CURR_SINK_OUT_CFG_REG 0x10 +#define SM5350_CTL_A_RAMP_TIME_REG 0x11 +#define SM5350_CTL_B_RAMP_TIME_REG 0x12 +#define SM5350_CTL_RUNTIME_RAMP_TIME_REG 0x13 +#define SM5350_CTL_RUNTIME_RAMP_CFG_REG 0x14 +#define SM5350_BRIGHTNESS_CFG_REG 0x16 +#define SM5350_CTL_A_FULL_SCALE_CURR_REG 0x17 +#define SM5350_CTL_B_FULL_SCALE_CURR_REG 0x18 +#define SM5350_HVLED_CURR_SINK_FEEDBACK_REG 0x19 +#define SM5350_BOOST_CTL_REG 0x1A +#define SM5350_AUTO_FREQ_THRESHOLD_REG 0x1B +#define SM5350_PWM_CFG_REG 0x1C +#define SM5350_CTL_A_BRIGHTNESS_LSB_REG 0x20 +#define SM5350_CTL_A_BRIGHTNESS_MSB_REG 0x21 +#define SM5350_CTL_B_BRIGHTNESS_LSB_REG 0x22 +#define SM5350_CTL_B_BRIGHTNESS_MSB_REG 0x23 +#define SM5350_CTL_B_BANK_EN_REG 0x24 +#define SM5350_HVLED_OPEN_FAULTS_REG 0xB0 +#define SM5350_HVLED_SHORT_FAULTS_REG 0xB2 +#define SM5350_LED_FAULT_ENABLES_REG 0xB4 + +enum backlight_exp_current_align { + ALIGN_NONE, + ALIGN_AW99703 +}; + +struct sm5350_data { + struct i2c_client *client; + struct i2c_adapter *adapter; + unsigned short addr; + struct work_struct work; + bool enable; + bool bank_A; + bool bank_B; + u8 ctl_bank_en; + u8 pwm_cfg; + u8 boost_ctl; + u8 full_scale_current; + u8 map_mode; + unsigned int led_current_align; /* Align boost current to AW chip */ + unsigned int default_brightness; + bool brt_code_enable; + u16 *brt_code_table; + int en_gpio; + struct backlight_device *bl_dev; +}; + +struct sm5350_data *g_sm5350_data; + +static int platform_read_i2c_block(struct i2c_client *client, char *writebuf, + int writelen, char *readbuf, int readlen) +{ + int ret; + + if (writelen > 0) { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) + dev_err(&client->dev, "%s: i2c read error.\n", + __func__); + } else { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s:i2c read error.\n", __func__); + } + return ret; +} + +static int sm5350_read_reg(struct i2c_client *client, u8 addr, u8 *val) +{ + return platform_read_i2c_block(client, &addr, 1, val, 1); +} + +static int platform_write_i2c_block(struct i2c_client *client, char *writebuf, int writelen) +{ + int ret; + + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s: i2c write error.\n", __func__); + + return ret; +} + +static int sm5350_write_reg(struct i2c_client *client, u8 addr, const u8 val) +{ + u8 buf[2] = {0}; + + buf[0] = addr; + buf[1] = val; + return platform_write_i2c_block(client, buf, sizeof(buf)); +} + +static int sm5350_init_registers(struct sm5350_data *drvdata) +{ + int err = 0; + + sm5350_write_reg(drvdata->client, SM5350_BOOST_CTL_REG, drvdata->boost_ctl); + sm5350_write_reg(drvdata->client, SM5350_PWM_CFG_REG, drvdata->pwm_cfg); + sm5350_write_reg(drvdata->client, SM5350_CTL_B_BANK_EN_REG, drvdata->ctl_bank_en); + sm5350_write_reg(drvdata->client, SM5350_BRIGHTNESS_CFG_REG, drvdata->map_mode); + sm5350_write_reg(drvdata->client, SM5350_HVLED_CURR_SINK_OUT_CFG_REG, SM5350_HVLED_CURR_SINK_OUT_CFG); + sm5350_write_reg(drvdata->client, SM5350_CTL_B_FULL_SCALE_CURR_REG, drvdata->full_scale_current); + + drvdata->enable = true; + + return err; +} + +static int sm5350_bl_get_brightness(struct backlight_device *bl_dev) +{ + return bl_dev->props.brightness; +} + +int sm5350_set_brightness(struct sm5350_data *drvdata, int brt_val) +{ + u8 brt_LSB = 0; + u8 brt_MSB = 0; + int index = 0, remainder; + int code, code1, code2; + printk("%s backlight_val = %d\n",__func__, brt_val); + + if ((drvdata->map_mode == 0) && (drvdata->led_current_align == ALIGN_AW99703)) + brt_val = brt_val*8383/10000+324; + + if (drvdata->brt_code_enable) { + index = brt_val / 10; + remainder = brt_val % 10; + + code1 = drvdata->brt_code_table[index]; + code2 = drvdata->brt_code_table[index+1]; + + code = (code2 - code1) * remainder / 10 + code1; + + brt_LSB = code % 0x7; + brt_MSB = (code >> 3) & 0xFF; + printk("brt_LSB_1 %x, brt_MSB_1 %x\n", brt_LSB, brt_MSB); + } else { + brt_LSB = brt_val & 0x7; + brt_MSB = (brt_val >> 3) & 0xFF; + } + printk("brt_LSB %x, brt_MSB %x\n", brt_LSB, brt_MSB); + + if (drvdata->enable == false) + sm5350_init_registers(drvdata); + + if (drvdata->bank_B) { + sm5350_write_reg(drvdata->client, SM5350_CTL_B_BRIGHTNESS_LSB_REG, brt_LSB); + sm5350_write_reg(drvdata->client, SM5350_CTL_B_BRIGHTNESS_MSB_REG, brt_MSB); + } + + if (brt_val == 0) + drvdata->enable = false; + + return 0; +} + +static int sm5350_bl_update_status(struct backlight_device *bl_dev) +{ + struct sm5350_data *drvdata = bl_get_data(bl_dev); + int brt; + + if (bl_dev->props.state & BL_CORE_SUSPENDED) + bl_dev->props.brightness = 0; + + brt = bl_dev->props.brightness; + /* + * Brightness register should always be written + * not only register based mode but also in PWM mode. + */ + return sm5350_set_brightness(drvdata, brt); +} + +static const struct backlight_ops sm5350_bl_ops = { + .update_status = sm5350_bl_update_status, + .get_brightness = sm5350_bl_get_brightness, +}; + +static void dump_sm5350_regs(struct sm5350_data *drvdata) +{ + u8 brt_LSB = 0; + u8 brt_MSB = 0; + u8 boost_ctl, pwm_cfg, ctl_bank_en, full_scale_current, revision; + + sm5350_read_reg(drvdata->client, SM5350_BOOST_CTL_REG, &boost_ctl); + sm5350_read_reg(drvdata->client, SM5350_PWM_CFG_REG, &pwm_cfg); + sm5350_read_reg(drvdata->client, SM5350_CTL_B_BANK_EN_REG, &ctl_bank_en); + sm5350_read_reg(drvdata->client, SM5350_CTL_A_FULL_SCALE_CURR_REG, &full_scale_current); + sm5350_read_reg(drvdata->client, SM5350_CTL_B_BRIGHTNESS_LSB_REG, &brt_LSB); + sm5350_read_reg(drvdata->client, SM5350_CTL_B_BRIGHTNESS_MSB_REG, &brt_MSB); + sm5350_read_reg(drvdata->client, SM5350_REVISION_REG, &revision); + + pr_err(">>-- boost_ctl(0x%x), pwm_cfg(0x%x), ctl_bank_en(0x%x), full_scale_current(0x%x), brt_LSB(0x%x), brt_MSB(0x%x), revision(0x%x).\n", + boost_ctl, pwm_cfg, ctl_bank_en, full_scale_current, brt_LSB, brt_MSB, revision); +} + +static int sm5350_get_dt_data(struct device *dev, struct sm5350_data *drvdata) +{ + int rc; + u32 tmp; + struct device_node *of_node = NULL; + int len; + const char *data; + u32 *buf; + int i = 0; + + of_node = dev->of_node; + + drvdata->en_gpio = of_get_named_gpio(of_node, "hwen-gpio", 0); + if (drvdata->en_gpio < 0) { + pr_err("%s,dt not get en_gpio, en_gpio = %d\n", __func__, drvdata->en_gpio); + return -EINVAL; + } + rc = of_property_read_u32(of_node, "boost-ctl", &tmp); + if (rc) { + pr_err("%s:%d, dt not specified\n", + __func__, __LINE__); + return -EINVAL; + } + drvdata->boost_ctl = (!rc ? tmp : 0); + pr_debug("%s : boost_ctl=0x%x\n",__func__, drvdata->boost_ctl); + + rc = of_property_read_u32(of_node, "map-mode", &tmp); + drvdata->map_mode= (!rc ? tmp : 1); /* 1: linear, 0: expo, linear as default*/ + pr_debug("%s : map_mode=0x%x\n",__func__, drvdata->map_mode); + + if (of_property_read_u32(of_node, "current-align-type", &drvdata->led_current_align)) + drvdata->led_current_align = ALIGN_NONE; + pr_debug("%s : led_current_align=0x%x\n",__func__, drvdata->led_current_align); + + rc = of_property_read_u32(of_node, "sm5350,default-brightness", &tmp); + drvdata->default_brightness= (!rc ? tmp : MAX_BRIGHTNESS); + pr_debug("%s : default_brightness=0x%x\n",__func__, drvdata->default_brightness); + + rc = of_property_read_u32(of_node, "pwm-cfg", &tmp); + if (rc) { + pr_err("%s:%d, dt not specified\n", + __func__, __LINE__); + return -EINVAL; + } + drvdata->pwm_cfg = (!rc ? tmp : 0); + pr_debug("%s : pwm_cfg=0x%x\n",__func__, drvdata->pwm_cfg); + + rc = of_property_read_u32(of_node, "ctl-bank-en", &tmp); + if (rc) { + pr_err("%s:%d, dt not specified\n", + __func__, __LINE__); + return -EINVAL; + } + drvdata->ctl_bank_en = (!rc ? tmp : 0); + pr_debug("%s : ctl_bank_en=0x%x\n",__func__, drvdata->ctl_bank_en); + + if (drvdata->ctl_bank_en & 0x01) + drvdata->bank_A = true; + if (drvdata->ctl_bank_en & 0x02) + drvdata->bank_B = true; + + pr_debug("%s : bank_A=%d bank_B=%d\n",__func__, drvdata->bank_A, drvdata->bank_B); + + rc = of_property_read_u32(of_node, "full-scale-current", &tmp); + if (rc) { + pr_err("%s:%d, dt not specified\n", + __func__, __LINE__); + return -EINVAL; + } + drvdata->full_scale_current = (!rc ? tmp : 0); + + pr_info("bank_A = %d, bank_B = %d, pwm_cfg = 0x%x, full_scale_current = 0x%x, map_mode = 0x%x, boost_ctl = 0x%x.\n", + drvdata->bank_A, drvdata->bank_B, drvdata->pwm_cfg, drvdata->full_scale_current, drvdata->map_mode, drvdata->boost_ctl); + + drvdata->brt_code_enable = of_property_read_bool(of_node, "brt-code-enable"); + + if (drvdata->brt_code_enable == false) { + pr_info("%s : brt_code_enable = %d, rc = %d.\n",__func__, drvdata->brt_code_enable, rc); + return rc; + } + + data = of_get_property(of_node, "brt-code-table", &len); + if (!data) { + pr_err("%s: read brt-code-table failed\n", __func__); + //return -ENOMEM; + } + + len /= sizeof(u32); + + buf = kzalloc(len * sizeof(u32), GFP_KERNEL); + if (!buf) + return -ENOMEM; + + rc = of_property_read_u32_array(of_node, "brt-code-table", buf, len); + if (rc) { + pr_err("%s:%d, dt not specified\n",__func__, __LINE__); + rc = -EINVAL; + goto end; + } + + drvdata->brt_code_table = kzalloc(len * sizeof(u16), GFP_KERNEL); + if (!drvdata->brt_code_table) { + pr_err("%s:%d, allocate memory failed\n",__func__, __LINE__); + rc = -ENOMEM; + goto end; + } + + for (i=0; i < len; i++) { + drvdata->brt_code_table[i] = (u16) buf[i]; + pr_debug("%s : buf=%d i=%d\n",__func__, buf[i], i); + } +end: + kfree(buf); + return rc; +} + +static int sm5350_bl_enable_hw(struct sm5350_data *drvdata){ + + return gpio_request_one(drvdata->en_gpio, GPIOF_OUT_INIT_HIGH, "sm5350_hwen"); + +} + +static int sm5350_read_revision(struct sm5350_data *drvdata) +{ + int ret = -1; + u8 value = 0; + unsigned char cnt = 0; + + while (cnt < 5) { + ret = sm5350_read_reg(drvdata->client, SM5350_REVISION_REG, &value); + if (ret < 0) { + pr_err("%s: failed to read reg SM5350_REVISION_REG: %d\n", + __func__, ret); + return ret; + } + switch (value) { + case 0x00: + pr_info("%s sm5350 detected\n", __func__); + return 0; + default: + pr_info("%s unsupported device revision (0x%x)\n", + __func__, value); + break; + } + cnt++; + + msleep(2); + } + + return -EINVAL; +} + +static int sm5350_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct sm5350_data *drvdata; + struct backlight_device *bl_dev; + struct backlight_properties props; + int err = 0; + + printk("%s entry\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("%s : I2C_FUNC_I2C not supported\n", __func__); + err = -EIO; + goto err_out; + } + + if (!client->dev.of_node) { + pr_err("%s : no device node\n", __func__); + err = -ENOMEM; + goto err_out; + } + + drvdata = kzalloc(sizeof(struct sm5350_data), GFP_KERNEL); + if (drvdata == NULL) { + pr_err("%s : kzalloc failed\n", __func__); + err = -ENOMEM; + goto err_out; + } + + drvdata->client = client; + drvdata->adapter = client->adapter; + drvdata->addr = client->addr; + drvdata->enable = true; + + g_sm5350_data = drvdata; + + err = sm5350_get_dt_data(&client->dev, drvdata); + if(err < 0) { + pr_err("%s : get dt failed\n", __func__); + err = -ENOMEM; + goto err_init; + } + err = sm5350_bl_enable_hw(drvdata); + if (err) + goto err_init; + + i2c_set_clientdata(client, drvdata); + + /*sm5350 read revision*/ + err = sm5350_read_revision(drvdata); + if (err < 0) { + pr_err("%s : ID idenfy failed\n", __func__); + goto err_init; + } + /*sm5350 read revision*/ + printk("sm-sm5350 detected success\n"); + + memset(&props, 0, sizeof(struct backlight_properties)); + props.type = BACKLIGHT_PLATFORM; + props.brightness = MAX_BRIGHTNESS; + props.max_brightness = MAX_BRIGHTNESS; + bl_dev = backlight_device_register(SM5350_NAME, &client->dev, + drvdata, &sm5350_bl_ops, &props); + + if (IS_ERR(bl_dev)) + return PTR_ERR(bl_dev); + drvdata->bl_dev = bl_dev; + sm5350_init_registers(drvdata); + dump_sm5350_regs(drvdata); + sm5350_set_brightness(drvdata, drvdata->default_brightness); + + printk("sm-sm5350 probe okay\n"); + return 0; + +err_init: + kfree(drvdata); +err_out: + return err; +} + +static int sm5350_remove(struct i2c_client *client) +{ + struct sm5350_data *drvdata = i2c_get_clientdata(client); + + backlight_device_unregister(drvdata->bl_dev); + kfree(drvdata); + return 0; +} + +static const struct i2c_device_id sm5350_id[] = { + {SM5350_NAME, 0}, + {} +}; +static struct of_device_id match_table[] = { + {.compatible = "sm-sm5350",} +}; + +MODULE_DEVICE_TABLE(i2c, sm5350_id); + +static struct i2c_driver sm5350_i2c_driver = { + .probe = sm5350_probe, + .remove = sm5350_remove, + .id_table = sm5350_id, + .driver = { + .name = SM5350_NAME, + .owner = THIS_MODULE, + .of_match_table = match_table, + }, +}; + +module_i2c_driver(sm5350_i2c_driver); +MODULE_DESCRIPTION("Back Light driver for SM5350"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/ese/st54spi_gpio/Android.mk b/drivers/ese/st54spi_gpio/Android.mk new file mode 100644 index 000000000000..2b5e6d6ec317 --- /dev/null +++ b/drivers/ese/st54spi_gpio/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := st54spi_gpio.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/ese/st54spi_gpio/Kbuild b/drivers/ese/st54spi_gpio/Kbuild new file mode 100644 index 000000000000..0e0b41203d4c --- /dev/null +++ b/drivers/ese/st54spi_gpio/Kbuild @@ -0,0 +1,6 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += st54spi_gpio.o + diff --git a/drivers/ese/st54spi_gpio/Makefile b/drivers/ese/st54spi_gpio/Makefile new file mode 100644 index 000000000000..918721816043 --- /dev/null +++ b/drivers/ese/st54spi_gpio/Makefile @@ -0,0 +1,16 @@ +all: modules + +modules: + $(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 + + diff --git a/drivers/ese/st54spi_gpio/st54spi_gpio.c b/drivers/ese/st54spi_gpio/st54spi_gpio.c new file mode 100644 index 000000000000..cce4dfe003f9 --- /dev/null +++ b/drivers/ese/st54spi_gpio/st54spi_gpio.c @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ST54SPI GPIO driver + * Copyright (C) 2021 ST Microelectronics S.A. + * Copyright (c) 2022 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 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + **********************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Flag ESE_CONF_GPIO_OPEN_RELEASE is to configure nRESET GPIO in + open function and freeing GPIO in release function */ +#define ESE_CONF_GPIO_OPEN_RELEASE + +/* Flag ESE_CONF_GPIO_PROBE_REMOVE is to configure + nRESET GPIO in probe function */ +//#define ESE_CONF_GPIO_PROBE_REMOVE + +#define ST54SPI_GPIO__MAGIC 0xEB +#define ST54SPI_GET_GPIO _IOW(ST54SPI_GPIO__MAGIC, 0x01, unsigned int) +#define ST54SPI_SET_GPIO _IOW(ST54SPI_GPIO__MAGIC, 0x02, unsigned int) + +struct st54spi_gpio_device { + dev_t st54spi_gpio_dev_t; + struct cdev c_dev; + struct class *class; + struct device *device; + struct spi_device *spi_dev; + /* GPIO for st54spi Reset pin (output) */ + int gpiod_reset; +}; + +/** @brief IOCTL function to be used to set or get data from upper layer. + * + * @param pfile fil node for opened device. + * @cmd IOCTL type from upper layer. + * @arg IOCTL arg from upper layer. + * + * @return 0 on success, error code for failures. + */ +long st54spi_gpio_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct st54spi_gpio_device *st54spi_gpio_dev = pfile->private_data; + + if (!st54spi_gpio_dev) { + pr_err("%s ENODEV! st54spi_gpio_dev is NULL\n", __func__); + return -ENODEV; + } + + switch (cmd) { + case ST54SPI_GET_GPIO: + ret = gpio_get_value(st54spi_gpio_dev->gpiod_reset); + break; + case ST54SPI_SET_GPIO: + if ((arg == 0) || (arg == 1)) { + gpio_set_value(st54spi_gpio_dev->gpiod_reset, arg); + } else { + pr_err("%s bad arg %lu\n", __func__, arg); + ret = -ENOIOCTLCMD; + } + break; + default: + pr_err("%s Unsupported ioctl cmd 0x%x, arg %lu\n", + __func__, cmd, arg); + ret = -ENOIOCTLCMD; + } + return ret; +} + +/* This function will be called when we open the Device file*/ + +static int st54spi_gpio_dev_open(struct inode *inode, struct file *pfile) +{ +#ifdef ESE_CONF_GPIO_OPEN_RELEASE + int rc; +#endif + struct st54spi_gpio_device *st54spi_gpio_dev = container_of(inode->i_cdev, + struct st54spi_gpio_device, c_dev); + + pr_info("%s : Device File Opened\n", __func__); + if (!st54spi_gpio_dev) { + pr_err("%s ENODEV NULL\n", __func__); + return -ENODEV; + } +#ifdef ESE_CONF_GPIO_OPEN_RELEASE + rc = gpio_request(st54spi_gpio_dev->gpiod_reset, "gpio-power_nreset"); + if (rc < 0) { + pr_err("%s: request gpio failed: %d\n", __func__, rc); + return -EFAULT; + } + + rc = gpio_direction_output(st54spi_gpio_dev->gpiod_reset, 0); + if (rc < 0) { + pr_err("%s: gpio cannot set the output %d\n", __func__, rc); + gpio_free(st54spi_gpio_dev->gpiod_reset); + return -EFAULT; + } +#endif + + pfile->private_data = st54spi_gpio_dev; + return 0; +} + +/* This function will be called when we close the Device file*/ + +static int st54spi_gpio_dev_release(struct inode *inode, struct file *pfile) +{ +#ifdef ESE_CONF_GPIO_OPEN_RELEASE + struct st54spi_gpio_device *st54spi_gpio_dev = pfile->private_data; + + if (gpio_is_valid(st54spi_gpio_dev->gpiod_reset)) + gpio_free(st54spi_gpio_dev->gpiod_reset); +#endif + pr_info("%s: Device File Closed\n", __func__); + pfile->private_data = NULL; + return 0; +} + + +static const struct file_operations st54spi_gpio_dev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = st54spi_gpio_dev_open, + .release = st54spi_gpio_dev_release, + .unlocked_ioctl = st54spi_gpio_dev_ioctl, +}; + +/* This function will be called to probe the character device*/ +static int st54spi_gpio_probe(struct platform_device *pdev) +{ + int rc; + struct st54spi_gpio_device *st54spi_gpio_dev; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + + if (np == NULL) { + pr_err("%s : struct np is null\n", __func__); + return -ENOMEM; + } + + pr_info("%s : enter", __func__); + st54spi_gpio_dev = devm_kzalloc(dev, sizeof(*st54spi_gpio_dev), GFP_KERNEL); + if (!st54spi_gpio_dev) + return -ENOMEM; + + /* Create device node */ + rc = alloc_chrdev_region(&st54spi_gpio_dev->st54spi_gpio_dev_t, 0, 1, "st54spi_gpio"); + if (rc < 0) { + pr_err("%s: alloc_chrdev_region() failed\n", __func__); + return rc; + } + + st54spi_gpio_dev->class = class_create(THIS_MODULE, "st54spi_gpio"); + if (IS_ERR(st54spi_gpio_dev->class)) { + rc = PTR_ERR(st54spi_gpio_dev->class); + pr_err("%s: Error creating st54spi_gpio_dev->class: %d\n", __func__, rc); + goto fail_class_create; + } + + cdev_init(&st54spi_gpio_dev->c_dev, &st54spi_gpio_dev_fops); + rc = cdev_add(&st54spi_gpio_dev->c_dev, st54spi_gpio_dev->st54spi_gpio_dev_t, 1); + if (rc) { + pr_err("%s: Error calling cdev_add: %d\n", __func__, rc); + goto fail_cdev_add; + } + + st54spi_gpio_dev->device = device_create(st54spi_gpio_dev->class, + NULL, st54spi_gpio_dev->st54spi_gpio_dev_t, + st54spi_gpio_dev, "st54spi_gpio"); + if (IS_ERR(st54spi_gpio_dev->device)) { + rc = PTR_ERR(st54spi_gpio_dev->device); + pr_err("%s: device_create failed: %d\n", __func__, rc); + goto fail_device_create; + } + + /* Setup gpio-power_nreset */ + st54spi_gpio_dev->gpiod_reset = of_get_named_gpio(np, "gpio-power_nreset", 0); + if (!gpio_is_valid(st54spi_gpio_dev->gpiod_reset)) { + pr_err("%s : Unable to request gpio-power_nreset\n", __func__); + rc = -EFAULT; + goto fail_gpiod_get; + } + +#ifdef ESE_CONF_GPIO_PROBE_REMOVE + rc = gpio_request(st54spi_gpio_dev->gpiod_reset, "gpio-power_nreset"); + if (rc < 0) { + pr_err("%s: request gpio failed: %d\n", __func__, rc); + rc = -EFAULT; + goto fail_gpiod_get; + } + + rc = gpio_direction_output(st54spi_gpio_dev->gpiod_reset, 0); + if (rc < 0) { + pr_err("%s: gpio cannot set the output %d\n", __func__, rc); + rc = -EFAULT; + goto fail_gpiod_request; + } +#endif + + platform_set_drvdata(pdev, st54spi_gpio_dev); + + return 0; + +#ifdef ESE_CONF_GPIO_PROBE_REMOVE +fail_gpiod_request: + gpio_free(st54spi_gpio_dev->gpiod_reset); +#endif +fail_gpiod_get: + device_destroy(st54spi_gpio_dev->class, st54spi_gpio_dev->st54spi_gpio_dev_t); +fail_device_create: + cdev_del(&st54spi_gpio_dev->c_dev); +fail_cdev_add: + class_destroy(st54spi_gpio_dev->class); +fail_class_create: + unregister_chrdev_region(st54spi_gpio_dev->st54spi_gpio_dev_t, 1); + devm_kfree(dev, st54spi_gpio_dev); + + return rc; +} + +/* This function will be called to remove the character device*/ +static int st54spi_gpio_remove(struct platform_device *pdev) +{ + struct st54spi_gpio_device *st54spi_gpio_dev; + struct device *dev = &pdev->dev; + + pr_info("%s : enter", __func__); + st54spi_gpio_dev = platform_get_drvdata(pdev); + device_destroy(st54spi_gpio_dev->class, st54spi_gpio_dev->st54spi_gpio_dev_t); + unregister_chrdev_region(st54spi_gpio_dev->st54spi_gpio_dev_t, 1); + class_destroy(st54spi_gpio_dev->class); + cdev_del(&st54spi_gpio_dev->c_dev); + devm_kfree(dev, st54spi_gpio_dev); + dev_set_drvdata(dev, NULL); +#ifdef ESE_CONF_GPIO_PROBE_REMOVE + if (gpio_is_valid(st54spi_gpio_dev->gpiod_reset)) + gpio_free(st54spi_gpio_dev->gpiod_reset); + +#endif + return 0; +} + +static const struct of_device_id st54spi_gpio_of_match[] = { + { .compatible = "st,st54spi_gpio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, st54spi_gpio_of_match); + +static struct platform_driver st54spi_gpio_driver = { + .driver = { + .name = "st54spi_gpio", + .owner = THIS_MODULE, + .of_match_table = st54spi_gpio_of_match, + }, + .probe = st54spi_gpio_probe, + .remove = st54spi_gpio_remove, +}; + +/* module load/unload record keeping */ +static int __init st54spi_gpio_dev_init(void) +{ + pr_info("%s : Loading st54spi gpio_driver 1.0\n", __func__); + return platform_driver_register(&st54spi_gpio_driver); +} + +module_init(st54spi_gpio_dev_init); + +static void __exit st54spi_gpio_dev_exit(void) +{ + pr_info("%s : Unloading st54spi gpio_driver 1.0\n", __func__); + platform_driver_unregister(&st54spi_gpio_driver); +} + +module_exit(st54spi_gpio_dev_exit); + +MODULE_AUTHOR("STMicroelectronics"); +MODULE_DESCRIPTION("ST54SPI GPIO driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("st54spi_gpio"); diff --git a/drivers/ese/st54x/Android.mk b/drivers/ese/st54x/Android.mk new file mode 100644 index 000000000000..dd11c6fbda9c --- /dev/null +++ b/drivers/ese/st54x/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := st54spi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/ese/st54x/Kbuild b/drivers/ese/st54x/Kbuild new file mode 100644 index 000000000000..54f720126045 --- /dev/null +++ b/drivers/ese/st54x/Kbuild @@ -0,0 +1,6 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += st54spi.o + diff --git a/drivers/ese/st54x/Makefile b/drivers/ese/st54x/Makefile new file mode 100644 index 000000000000..918721816043 --- /dev/null +++ b/drivers/ese/st54x/Makefile @@ -0,0 +1,16 @@ +all: modules + +modules: + $(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 + + diff --git a/drivers/ese/st54x/st54spi.c b/drivers/ese/st54x/st54spi.c new file mode 100644 index 000000000000..41c9b40fef0c --- /dev/null +++ b/drivers/ese/st54x/st54spi.c @@ -0,0 +1,1240 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Simple synchronous userspace interface to SPI devices + * Copyright (C) 2020 ST Microelectronics S.A. + * Copyright (C) 2006 SWAPP Andrea Paterniani + * Copyright (C) 2007 David Brownell (simplification, cleanup) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +// #define WITH_SPI_CLK_MNGT 1 + +#include "linux/st21nfc.h" +#include "linux/spi/spi-msm-geni.h" + + +/* + * This supports access to SPI devices using normal userspace I/O calls. + * Note that while traditional UNIX/POSIX I/O semantics are half duplex, + * and often mask message boundaries, full SPI support requires full duplex + * transfers. There are several kinds of internal message boundaries to + * handle chipselect management and other protocol options. + * + * SPI has a character major number assigned. We allocate minor numbers + * dynamically using a bitmask. You must use hotplug tools, such as udev + * (or mdev with busybox) to create and destroy the /dev/st54spi device + * nodes, since there is no fixed association of minor numbers with any + * particular SPI bus or device. + */ +//#define SPIDEV_MAJOR 0 /* dynamic */ +static int spidev_major; +#define N_SPI_MINORS 1 /* ... up to 256 */ + +static DECLARE_BITMAP(minors, N_SPI_MINORS); + +#define ST54SPI_IOC_RD_POWER _IOR(SPI_IOC_MAGIC, 99, __u32) +#define ST54SPI_IOC_WR_POWER _IOW(SPI_IOC_MAGIC, 99, __u32) + +/* Bit masks for spi_device.mode management. Note that incorrect + * settings for some settings can cause *lots* of trouble for other + * devices on a shared bus: + * + * - CS_HIGH ... this device will be active when it shouldn't be + * - 3WIRE ... when active, it won't behave as it should + * - NO_CS ... there will be no explicit message boundaries; this + * is completely incompatible with the shared bus model + * - READY ... transfers may proceed when they shouldn't. + * + * REVISIT should changing those flags be privileged? + */ +#define SPI_MODE_MASK \ + (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | \ + SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP | \ + SPI_NO_CS | SPI_READY | SPI_TX_DUAL | \ + SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD) + +struct st54spi_data { + dev_t devt; + spinlock_t spi_lock; + struct spi_device *spi; + struct spi_device *spi_reset; + struct list_head device_entry; + + /* TX/RX buffers are NULL unless this device is open (users > 0) */ + struct mutex buf_lock; + unsigned int users; + u8 *tx_buffer; + u8 *rx_buffer; + u32 speed_hz; + + /* GPIO for SE_POWER_REQ / SE_nRESET */ + int power_or_nreset_gpio_mode; + int power_or_nreset_gpio; + int nfcc_needs_poweron; + int sehal_needs_poweron; + int se_is_poweron; + struct pinctrl *pctrl; + struct pinctrl_state *pctrl_mode_spi, *pctrl_mode_idle; + +}; + +#define POWER_MODE_NONE -1 +#define POWER_MODE_ST54H 0 +#define POWER_MODE_ST54J 1 + +static LIST_HEAD(device_list); +static DEFINE_MUTEX(device_list_lock); + +static unsigned int bufsiz = 4096; +module_param(bufsiz, uint, 0444); +MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message"); + +static bool debug_enabled = true; +#define VERBOSE 1 + +/*-------------------------------------------------------------------------*/ + +static ssize_t st54spi_sync( + struct st54spi_data *st54spi, struct spi_message *message) +{ + DECLARE_COMPLETION_ONSTACK(done); + int status; + struct spi_device *spi; + + spin_lock_irq(&st54spi->spi_lock); + spi = st54spi->spi; + spin_unlock_irq(&st54spi->spi_lock); + + if (spi == NULL) + status = -ESHUTDOWN; + else + status = spi_sync(spi, message); + + if (status == 0) + status = message->actual_length; + + return status; +} + +static inline ssize_t st54spi_sync_write( + struct st54spi_data *st54spi, size_t len) +{ + struct spi_transfer t = { + .tx_buf = st54spi->tx_buffer, + .len = len, + .speed_hz = st54spi->speed_hz, + }; + struct spi_message m; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + return st54spi_sync(st54spi, &m); +} + +static inline ssize_t st54spi_sync_read( + struct st54spi_data *st54spi, size_t len) +{ + struct spi_transfer t = { + .rx_buf = st54spi->rx_buffer, + .len = len, + .speed_hz = st54spi->speed_hz, + }; + struct spi_message m; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + return st54spi_sync(st54spi, &m); +} + +/*-------------------------------------------------------------------------*/ + +/* Read-only message with current device setup */ +static ssize_t st54spi_read( + struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + struct st54spi_data *st54spi; + ssize_t status = 0; + + /* chipselect only toggles at start or end of operation */ + if (count > bufsiz) + return -EMSGSIZE; + + st54spi = filp->private_data; + + if (debug_enabled) + dev_info(&st54spi->spi->dev, "st54spi Read: %zu bytes\n", count); + + mutex_lock(&st54spi->buf_lock); + status = st54spi_sync_read(st54spi, count); + if (status > 0) { + unsigned long missing; + + missing = copy_to_user(buf, st54spi->rx_buffer, status); + if (missing == status) + status = -EFAULT; + else + status = status - missing; + } + mutex_unlock(&st54spi->buf_lock); + + if (debug_enabled) + dev_info(&st54spi->spi->dev, "st54spi Read: status: %zd\n", status); + + return status; +} + +/* Write-only message with current device setup */ +static ssize_t st54spi_write( + struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) +{ + struct st54spi_data *st54spi; + ssize_t status = 0; + unsigned long missing; + + /* chipselect only toggles at start or end of operation */ + if (count > bufsiz) + return -EMSGSIZE; + + st54spi = filp->private_data; + + if (debug_enabled) + dev_info(&st54spi->spi->dev, "st54spi Write: %zu bytes\n", count); + + mutex_lock(&st54spi->buf_lock); + missing = copy_from_user(st54spi->tx_buffer, buf, count); + if (missing == 0) + status = st54spi_sync_write(st54spi, count); + else + status = -EFAULT; + mutex_unlock(&st54spi->buf_lock); + + if (debug_enabled) + dev_info(&st54spi->spi->dev, "st54spi Write: status: %zd\n", status); + + return status; +} + +static int st54spi_message( + struct st54spi_data *st54spi, + struct spi_ioc_transfer *u_xfers, + unsigned int n_xfers) +{ + struct spi_message msg; + struct spi_transfer *k_xfers; + struct spi_transfer *k_tmp; + struct spi_ioc_transfer *u_tmp; + unsigned int n, total, tx_total, rx_total; + u8 *tx_buf, *rx_buf; + int status = -EFAULT; + + spi_message_init(&msg); + k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL); + if (k_xfers == NULL) + return -ENOMEM; + + /* Construct spi_message, copying any tx data to bounce buffer. + * We walk the array of user-provided transfers, using each one + * to initialize a kernel version of the same transfer. + */ + tx_buf = st54spi->tx_buffer; + rx_buf = st54spi->rx_buffer; + total = 0; + tx_total = 0; + rx_total = 0; + for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers; + n; n--, k_tmp++, u_tmp++) { + k_tmp->len = u_tmp->len; + + total += k_tmp->len; + /* Since the function returns the total length of transfers + * on success, restrict the total to positive int values to + * avoid the return value looking like an error. Also check + * each transfer length to avoid arithmetic overflow. + */ + if (total > INT_MAX || k_tmp->len > INT_MAX) { + status = -EMSGSIZE; + goto done; + } + + if (u_tmp->rx_buf) { + /* this transfer needs space in RX bounce buffer */ + rx_total += k_tmp->len; + if (rx_total > bufsiz) { + status = -EMSGSIZE; + goto done; + } + k_tmp->rx_buf = rx_buf; + if (!access_ok((u8 __user *)(uintptr_t)u_tmp->rx_buf, + u_tmp->len)) + goto done; + rx_buf += k_tmp->len; + } + if (u_tmp->tx_buf) { + /* this transfer needs space in TX bounce buffer */ + tx_total += k_tmp->len; + if (tx_total > bufsiz) { + status = -EMSGSIZE; + goto done; + } + k_tmp->tx_buf = tx_buf; + if (copy_from_user(tx_buf, + (const u8 __user *)(uintptr_t)u_tmp->tx_buf, + u_tmp->len)) + goto done; + tx_buf += k_tmp->len; + } + + k_tmp->cs_change = !!u_tmp->cs_change; + k_tmp->tx_nbits = u_tmp->tx_nbits; + k_tmp->rx_nbits = u_tmp->rx_nbits; + k_tmp->bits_per_word = u_tmp->bits_per_word; + k_tmp->delay_usecs = u_tmp->delay_usecs; + k_tmp->speed_hz = u_tmp->speed_hz; + if (!k_tmp->speed_hz) + k_tmp->speed_hz = st54spi->speed_hz; +#ifdef VERBOSE + dev_dbg(&st54spi->spi->dev, " xfer len %u %s%s%s%dbits %u usec %uHz\n", + u_tmp->len, u_tmp->rx_buf ? "rx " : "", u_tmp->tx_buf ? "tx " : "", + u_tmp->cs_change ? "cs " : "", + u_tmp->bits_per_word ?: st54spi->spi->bits_per_word, + u_tmp->delay_usecs, u_tmp->speed_hz ?: st54spi->spi->max_speed_hz); +#endif + spi_message_add_tail(k_tmp, &msg); + } + + status = st54spi_sync(st54spi, &msg); + if (status < 0) + goto done; + + /* copy any rx data out of bounce buffer */ + rx_buf = st54spi->rx_buffer; + for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) { + if (u_tmp->rx_buf) { + if (__copy_to_user( + (u8 __user *)(uintptr_t)u_tmp->rx_buf, + rx_buf, u_tmp->len)) { + status = -EFAULT; + goto done; + } + rx_buf += u_tmp->len; + } + } + status = total; + +done: + kfree(k_xfers); + return status; +} + +static struct spi_ioc_transfer *st54spi_get_ioc_message( + unsigned int cmd, + struct spi_ioc_transfer __user *u_ioc, + unsigned int *n_ioc) +{ + struct spi_ioc_transfer *ioc; + u32 tmp; + + /* Check type, command number and direction */ + if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC || + _IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0)) || + _IOC_DIR(cmd) != _IOC_WRITE) + return ERR_PTR(-ENOTTY); + + tmp = _IOC_SIZE(cmd); + if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) + return ERR_PTR(-EINVAL); + + *n_ioc = tmp / sizeof(struct spi_ioc_transfer); + if (*n_ioc == 0) + return NULL; + + /* copy into scratch area */ + ioc = kmalloc(tmp, GFP_KERNEL); + if (!ioc) + return ERR_PTR(-ENOMEM); + + if (__copy_from_user(ioc, u_ioc, tmp)) { + kfree(ioc); + return ERR_PTR(-EFAULT); + } + return ioc; +} + +static void st54spi_power_off(struct st54spi_data *st54spi) +{ + int ret; + + if (debug_enabled) + dev_info(&st54spi->spi->dev, "%s\n", __func__); + + // Set NSS pin as highZ (ST54H and ST54J). + + // Change NSS polarity to have NSS low. + ret = pinctrl_select_state(st54spi->pctrl, st54spi->pctrl_mode_idle); + + if (ret < 0) { + dev_err(&st54spi->spi->dev, + "%s : change CSB management to High Z failed!\n", __func__); + } + + // Set SE_PWR_REQ / SE_nRESET to low + if (st54spi->power_or_nreset_gpio) + gpio_set_value(st54spi->power_or_nreset_gpio, 1); + + // if ST54H block access to SPI in case this is done during a CLF reset + if (st54spi->power_or_nreset_gpio_mode == POWER_MODE_ST54H) { + // disallow access to SPI r/w + if (st54spi->spi) { + spin_lock_irq(&st54spi->spi_lock); + st54spi->spi_reset = st54spi->spi; + st54spi->spi = NULL; + spin_unlock_irq(&st54spi->spi_lock); + } + + // Give time to the CLF to detect falling SE_PWR_REQ + // and pull down the line before continue. + usleep_range(2000, 4500); + } + + + st54spi->se_is_poweron = 0; +} + +static void st54spi_power_on(struct st54spi_data *st54spi) +{ + int ret; + + if (debug_enabled) + dev_info(&st54spi->spi->dev, "%s\n", __func__); + + // set SE_PWR_REQ / SE_nRESET to high and wait for CLF + eSE reaction + if (st54spi->power_or_nreset_gpio) { + gpio_set_value(st54spi->power_or_nreset_gpio, 1); + usleep_range(1000, 1500); + } + + // Set NSS pin for the SPI function. + ret = pinctrl_select_state(st54spi->pctrl, st54spi->pctrl_mode_spi); + + if (ret < 0) { + dev_err(&st54spi->spi->dev, + "%s : change NSS management to SPI failed!\n", __func__); + } + + usleep_range(4000, 5000); + + if (st54spi->power_or_nreset_gpio_mode == POWER_MODE_ST54H) { + // re-allow SPI xfers + if (st54spi->spi_reset) { + spin_lock_irq(&st54spi->spi_lock); + st54spi->spi = st54spi->spi_reset; + st54spi->spi_reset = NULL; + spin_unlock_irq(&st54spi->spi_lock); + } + } + + st54spi->se_is_poweron = 1; +} + +static void st54spi_power_set(struct st54spi_data *st54spi, int val) +{ + if (!st54spi) + return; + + if (debug_enabled) + dev_info(&st54spi->spi->dev, "st54spi sehal pwr_req: %d\n", val); + + if (val) { + st54spi->sehal_needs_poweron = 1; + if (st54spi->se_is_poweron == 0) + st54spi_power_on(st54spi); + } else { + st54spi->sehal_needs_poweron = 0; + if ((st54spi->se_is_poweron == 1) && + (st54spi->nfcc_needs_poweron == 0)) + // we don t need power anymore + st54spi_power_off(st54spi); + } +} + +static int st54spi_power_get(struct st54spi_data *st54spi) +{ + if (st54spi->power_or_nreset_gpio) + return gpio_get_value(st54spi->power_or_nreset_gpio); + return 0; +} + +static long st54spi_ioctl( + struct file *filp, unsigned int cmd, unsigned long arg) +{ + int err = 0; + int retval = 0; + struct st54spi_data *st54spi; + struct spi_device *spi; + u32 tmp; + unsigned int n_ioc; + struct spi_ioc_transfer *ioc; + + /* Check type and command number */ + if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC) + return -ENOTTY; + + /* Check access direction once here; don't repeat below. + * IOC_DIR is from the user perspective, while access_ok is + * from the kernel perspective; so they look reversed. + */ + if (_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok((void __user *)arg, _IOC_SIZE(cmd)); + if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok((void __user *)arg, _IOC_SIZE(cmd)); + if (err) + return -EFAULT; + + /* guard against device removal before, or while, + * we issue this ioctl. + */ + st54spi = filp->private_data; + spin_lock_irq(&st54spi->spi_lock); + spi = spi_dev_get(st54spi->spi); + spin_unlock_irq(&st54spi->spi_lock); + + if (debug_enabled) + dev_info(&st54spi->spi->dev, "st54spi ioctl cmd %d\n", cmd); + + if (spi == NULL) + return -ESHUTDOWN; + + /* use the buffer lock here for triple duty: + * - prevent I/O (from us) so calling spi_setup() is safe; + * - prevent concurrent SPI_IOC_WR_* from morphing + * data fields while SPI_IOC_RD_* reads them; + * - SPI_IOC_MESSAGE needs the buffer locked "normally". + */ + mutex_lock(&st54spi->buf_lock); + + switch (cmd) { + /* read requests */ + case SPI_IOC_RD_MODE: + retval = __put_user( + spi->mode & SPI_MODE_MASK, (__u8 __user *)arg); + break; + case SPI_IOC_RD_MODE32: + retval = __put_user( + spi->mode & SPI_MODE_MASK, (__u32 __user *)arg); + break; + case SPI_IOC_RD_LSB_FIRST: + retval = __put_user( + (spi->mode & SPI_LSB_FIRST) ? + 1 : 0, (__u8 __user *)arg); + break; + case SPI_IOC_RD_BITS_PER_WORD: + retval = __put_user( + spi->bits_per_word, (__u8 __user *)arg); + break; + case SPI_IOC_RD_MAX_SPEED_HZ: + retval = __put_user( + st54spi->speed_hz, (__u32 __user *)arg); + break; + case ST54SPI_IOC_RD_POWER: + retval = __put_user( + st54spi_power_get(st54spi), (__u32 __user *)arg); + break; + + /* write requests */ + case SPI_IOC_WR_MODE: + case SPI_IOC_WR_MODE32: + if (cmd == SPI_IOC_WR_MODE) + retval = __get_user(tmp, (u8 __user *)arg); + else + retval = __get_user(tmp, (u32 __user *)arg); + if (retval == 0) { + u32 save = spi->mode; + + if (tmp & ~SPI_MODE_MASK) { + retval = -EINVAL; + break; + } + + tmp |= spi->mode & ~SPI_MODE_MASK; + spi->mode = (u16)tmp; + retval = spi_setup(spi); + if (retval < 0) + spi->mode = save; + else + dev_dbg(&spi->dev, "spi mode %x\n", tmp); + } + break; + case SPI_IOC_WR_LSB_FIRST: + retval = __get_user(tmp, (__u8 __user *)arg); + if (retval == 0) { + u32 save = spi->mode; + + if (tmp) + spi->mode |= SPI_LSB_FIRST; + else + spi->mode &= ~SPI_LSB_FIRST; + retval = spi_setup(spi); + if (retval < 0) + spi->mode = save; + else + dev_dbg(&spi->dev, + "%csb first\n", tmp ? + 'l' : 'm'); + } + break; + case SPI_IOC_WR_BITS_PER_WORD: + retval = __get_user(tmp, (__u8 __user *)arg); + if (retval == 0) { + u8 save = spi->bits_per_word; + + spi->bits_per_word = tmp; + retval = spi_setup(spi); + if (retval < 0) + spi->bits_per_word = save; + else + dev_dbg(&spi->dev, "%d bits per word\n", tmp); + } + break; + case SPI_IOC_WR_MAX_SPEED_HZ: + retval = __get_user(tmp, (__u32 __user *)arg); + if (retval == 0) { + u32 save = spi->max_speed_hz; + + spi->max_speed_hz = tmp; + retval = spi_setup(spi); + if (retval >= 0) + st54spi->speed_hz = tmp; + else + dev_dbg(&spi->dev, "%d Hz (max)\n", tmp); + spi->max_speed_hz = save; + } + break; + case ST54SPI_IOC_WR_POWER: + retval = __get_user(tmp, (__u32 __user *)arg); + if (retval == 0) { + st54spi_power_set(st54spi, tmp ? 1 : 0); + dev_dbg(&spi->dev, "SE_POWER_REQ/SE_NRESET set: %d\n", tmp); + } + break; + + default: + /* segmented and/or full-duplex I/O request */ + /* Check message and copy into scratch area */ + ioc = st54spi_get_ioc_message( + cmd, (struct spi_ioc_transfer __user *)arg, &n_ioc); + if (IS_ERR(ioc)) { + retval = PTR_ERR(ioc); + break; + } + if (!ioc) + break; /* n_ioc is also 0 */ + + /* translate to spi_message, execute */ + retval = st54spi_message(st54spi, ioc, n_ioc); + kfree(ioc); + break; + } + + mutex_unlock(&st54spi->buf_lock); + spi_dev_put(spi); + + if (debug_enabled) + dev_info(&spi->dev, "st54spi ioctl retval %d\n", retval); + + return retval; +} + +#ifdef CONFIG_COMPAT +static long st54spi_compat_ioc_message( + struct file *filp, unsigned int cmd, unsigned long arg) { + struct spi_ioc_transfer __user *u_ioc; + int retval = 0; + struct st54spi_data *st54spi; + struct spi_device *spi; + unsigned int n_ioc, n; + struct spi_ioc_transfer *ioc; + + u_ioc = (struct spi_ioc_transfer __user *)compat_ptr(arg); + if (!access_ok(u_ioc, _IOC_SIZE(cmd))) + return -EFAULT; + + /* guard against device removal before, or while, + * we issue this ioctl. + */ + st54spi = filp->private_data; + spin_lock_irq(&st54spi->spi_lock); + spi = spi_dev_get(st54spi->spi); + spin_unlock_irq(&st54spi->spi_lock); + + if (debug_enabled) + dev_info(&st54spi->spi->dev, "st54spi compat_ioctl cmd %d\n", cmd); + if (spi == NULL) + return -ESHUTDOWN; + + /* SPI_IOC_MESSAGE needs the buffer locked "normally" */ + mutex_lock(&st54spi->buf_lock); + + /* Check message and copy into scratch area */ + ioc = st54spi_get_ioc_message(cmd, u_ioc, &n_ioc); + if (IS_ERR(ioc)) { + retval = PTR_ERR(ioc); + goto done; + } + if (!ioc) + goto done; /* n_ioc is also 0 */ + + /* Convert buffer pointers */ + for (n = 0; n < n_ioc; n++) { + ioc[n].rx_buf = (uintptr_t)compat_ptr(ioc[n].rx_buf); + ioc[n].tx_buf = (uintptr_t)compat_ptr(ioc[n].tx_buf); + } + + /* translate to spi_message, execute */ + retval = st54spi_message(st54spi, ioc, n_ioc); + kfree(ioc); + +done: + mutex_unlock(&st54spi->buf_lock); + spi_dev_put(spi); + if (debug_enabled) + dev_info(&st54spi->spi->dev, "st54spi compat_ioctl retval %d\n", retval); + return retval; +} + +static long st54spi_compat_ioctl( + struct file *filp, unsigned int cmd, unsigned long arg) +{ + if (_IOC_TYPE(cmd) == SPI_IOC_MAGIC && + _IOC_NR(cmd) == _IOC_NR(SPI_IOC_MESSAGE(0)) && + _IOC_DIR(cmd) == _IOC_WRITE) + return st54spi_compat_ioc_message(filp, cmd, arg); + + return st54spi_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#else +#define st54spi_compat_ioctl NULL +#endif /* CONFIG_COMPAT */ + +static int st54spi_open(struct inode *inode, struct file *filp) +{ + struct st54spi_data *st54spi; + int status = -ENXIO; + + mutex_lock(&device_list_lock); + + list_for_each_entry(st54spi, &device_list, device_entry) { + if (st54spi->devt == inode->i_rdev) { + status = 0; + break; + } + } + + if (status) { + dev_dbg(&st54spi->spi->dev, "st54spi: nothing for minor %d\n", iminor(inode)); + goto err_find_dev; + } + + // Authorize only 1 process to open the device. + if (st54spi->users > 0) { + dev_err(&st54spi->spi->dev, "already open\n"); + mutex_unlock(&device_list_lock); + return -EBUSY; + } + + if (debug_enabled) + dev_info(&st54spi->spi->dev, "st54spi: open\n"); + + if (!st54spi->tx_buffer) { + st54spi->tx_buffer = kmalloc(bufsiz, GFP_KERNEL); + if (!st54spi->tx_buffer) { + // dev_dbg(&st54spi->spi->dev, "open/ENOMEM\n"); + status = -ENOMEM; + goto err_find_dev; + } + } + + if (!st54spi->rx_buffer) { + st54spi->rx_buffer = kmalloc(bufsiz, GFP_KERNEL); + if (!st54spi->rx_buffer) { + // dev_dbg(&st54spi->spi->dev, "open/ENOMEM\n"); + status = -ENOMEM; + goto err_alloc_rx_buf; + } + } + + st54spi->users++; + filp->private_data = st54spi; + nonseekable_open(inode, filp); + + mutex_unlock(&device_list_lock); + + if (debug_enabled) + dev_info(&st54spi->spi->dev, "st54spi: open - force power on\n"); + st54spi_power_set(st54spi, 1); + return 0; + +err_alloc_rx_buf: + kfree(st54spi->tx_buffer); + st54spi->tx_buffer = NULL; +err_find_dev: + mutex_unlock(&device_list_lock); + return status; +} + +static int st54spi_release(struct inode *inode, struct file *filp) +{ + struct st54spi_data *st54spi; + + mutex_lock(&device_list_lock); + st54spi = filp->private_data; + filp->private_data = NULL; + + if (debug_enabled) + dev_info(&st54spi->spi->dev, "st54spi: release\n"); + + /* last close? */ + st54spi->users--; + if (!st54spi->users) { + int dofree; + + if (debug_enabled) + dev_info(&st54spi->spi->dev, "st54spi: release - may allow power off\n"); + + st54spi_power_set(st54spi, 0); + + kfree(st54spi->tx_buffer); + st54spi->tx_buffer = NULL; + + kfree(st54spi->rx_buffer); + st54spi->rx_buffer = NULL; + + spin_lock_irq(&st54spi->spi_lock); + if (st54spi->spi) + st54spi->speed_hz = st54spi->spi->max_speed_hz; + + /* ... after we unbound from the underlying device? */ + dofree = ((st54spi->spi == NULL) && + (st54spi->spi_reset == NULL)); + spin_unlock_irq(&st54spi->spi_lock); + + if (dofree) + kfree(st54spi); + } + mutex_unlock(&device_list_lock); + + return 0; +} + +static const struct file_operations st54spi_fops = { + .owner = THIS_MODULE, + /* REVISIT switch to aio primitives, so that userspace + * gets more complete API coverage. It'll simplify things + * too, except for the locking. + */ + .write = st54spi_write, + .read = st54spi_read, + .unlocked_ioctl = st54spi_ioctl, + .compat_ioctl = st54spi_compat_ioctl, + .open = st54spi_open, + .release = st54spi_release, + .llseek = no_llseek, +}; + +/*-------------------------------------------------------------------------*/ + +/* The main reason to have this class is to make mdev/udev create the + * /dev/st54spi character device nodes exposing our userspace API. + * It also simplifies memory management. + */ + +static struct class *st54spi_class; + +static const struct of_device_id st54spi_dt_ids[] = { + {.compatible = "st,st54spi"}, + {}, +}; +MODULE_DEVICE_TABLE(of, st54spi_dt_ids); + +#ifdef CONFIG_ACPI + +/* Dummy SPI devices not to be used in production systems */ +#define SPIDEV_ACPI_DUMMY 1 + +static const struct acpi_device_id st54spi_acpi_ids[] = { + /* + * The ACPI SPT000* devices are only meant for development and + * testing. Systems used in production should have a proper ACPI + * description of the connected peripheral and they should also use + * a proper driver instead of poking directly to the SPI bus. + */ + {"SPT0001", SPIDEV_ACPI_DUMMY}, + {"SPT0002", SPIDEV_ACPI_DUMMY}, + {"SPT0003", SPIDEV_ACPI_DUMMY}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, st54spi_acpi_ids); + +static void st54spi_probe_acpi(struct spi_device *spi) +{ + const struct acpi_device_id *id; + + if (!has_acpi_companion(&spi->dev)) + return; + + id = acpi_match_device(st54spi_acpi_ids, &spi->dev); + if (WARN_ON(!id)) + return; +} +#else +static inline void st54spi_probe_acpi(struct spi_device *spi) {} +#endif + +/*-------------------------------------------------------------------------*/ + +static int st54spi_parse_dt(struct device *dev, struct st54spi_data *pdata) +{ + int r = 0; + struct device_node *np = dev->of_node; + + np = of_find_compatible_node(NULL, NULL, "st,st54spi"); + + if (np) { + const char *power_mode; + r = of_get_named_gpio(np, "gpio-power_nreset-std", 0); + if (r < 0) + dev_info(dev, "%s: get ST54 failed (%d)", __FILE__, r); + else + pdata->power_or_nreset_gpio = r; + r = 0; + + // Read power mode. + power_mode = of_get_property(np, "power_mode", NULL); + if (!power_mode) { + dev_info(dev, "%s: Default power mode: ST54H\n", __FILE__); + pdata->power_or_nreset_gpio_mode = POWER_MODE_ST54H; + } else if (!strcmp(power_mode, "ST54J")) { + dev_info(dev, "%s: Power mode: ST54J\n", __FILE__); + pdata->power_or_nreset_gpio_mode = POWER_MODE_ST54J; + } else if (!strcmp(power_mode, "ST54H")) { + dev_info(dev, "%s: Power mode: ST54H\n", __FILE__); + pdata->power_or_nreset_gpio_mode = POWER_MODE_ST54H; + } else if (!strcmp(power_mode, "none")) { + dev_info(dev, "%s: Power mode: none\n", __FILE__); + pdata->power_or_nreset_gpio_mode = POWER_MODE_NONE; + } else { + dev_err(dev, "%s: Power mode unknown: %s\n", __FILE__, power_mode); + return -1; + } + } else { + dev_info(dev, "%s : get num err.\n", __func__); + return -1; + } + + // We need to use pinmux to control NSS + pdata->pctrl = devm_pinctrl_get(dev); + if (IS_ERR(pdata->pctrl)) { + dev_err(dev, "%s: Unable to allocate pinctrl: %ld\n", + __FILE__, PTR_ERR(pdata->pctrl)); + return -1; + } + + pdata->pctrl_mode_spi = pinctrl_lookup_state(pdata->pctrl, "spi_active"); + if (IS_ERR(pdata->pctrl_mode_spi)) { + dev_err(dev, "%s: Unable to find qupv3_se12_spi_active: %ld\n", + __FILE__, PTR_ERR(pdata->pctrl_mode_spi)); + return -1; + } + + pdata->pctrl_mode_idle = pinctrl_lookup_state(pdata->pctrl, "spi_sleep"); + if (IS_ERR(pdata->pctrl_mode_idle)) { + dev_err(dev, "%s: Unable to find qupv3_se12_spi_sleep: %ld\n", + __FILE__, PTR_ERR(pdata->pctrl_mode_idle)); + return -1; + } + dev_info(dev, "[dsc]%s : pinctrl initialized\n", __func__); + + dev_info(dev, "[dsc]%s : get power_or_nreset_gpio[%d]\n", + __func__, pdata->power_or_nreset_gpio); + return r; +} + +#ifndef MODULE +static void st54spi_st21nfc_cb(int dir, void *data) +{ + struct st54spi_data *st54spi = (struct st54spi_data *)data; + + if (!st54spi) + return; + dev_info(&st54spi->spi->dev, "%s : dir %d data %p\n", __func__, dir, st54spi); + + switch (dir) { + case ST54SPI_CB_RESET_START: + if (st54spi->se_is_poweron) + st54spi_power_off(st54spi); + break; + + case ST54SPI_CB_RESET_END: + // wait for the CLF to boot once nRESET is released + usleep_range(4000, 8000); + + st54spi_power_on(st54spi); + break; + + case ST54SPI_CB_ESE_USED: + st54spi->nfcc_needs_poweron = 1; + if (st54spi->se_is_poweron == 0) + st54spi_power_on(st54spi); + break; + + case ST54SPI_CB_ESE_NOT_USED: + st54spi->nfcc_needs_poweron = 0; + if ((st54spi->se_is_poweron == 1) && + (st54spi->sehal_needs_poweron == 0)) + // we don t need power anymore + st54spi_power_off(st54spi); + break; + } +} +#endif // !MODULE + +/* Change CS_TIME for ST54 */ + +/* +static struct spi_delay st54spi_delay = { + .unit = SPI_DELAY_UNIT_USECS, + .value = 10, +}; +*/ + +static int st54spi_probe(struct spi_device *spi) +{ + struct st54spi_data *st54spi; + int status, ret; + unsigned long minor; + struct spi_geni_qcom_ctrl_data *spi_param = NULL; + + /* + * st54spi should never be referenced in DT without a specific + * compatible string, it is a Linux implementation thing + * rather than a description of the hardware. + */ + + st54spi_probe_acpi(spi); + + /* Allocate driver data */ + st54spi = kzalloc(sizeof(*st54spi), GFP_KERNEL); + if (!st54spi) + return -ENOMEM; + + /* Initialize the driver data */ + st54spi->spi = spi; + spin_lock_init(&st54spi->spi_lock); + mutex_init(&st54spi->buf_lock); + + INIT_LIST_HEAD(&st54spi->device_entry); + + /* If we can allocate a minor number, hook up this device. + * Reusing minors is fine so long as udev or mdev is working. + */ + mutex_lock(&device_list_lock); + minor = find_first_zero_bit(minors, N_SPI_MINORS); + if (minor < N_SPI_MINORS) { + struct device *dev; + + st54spi->devt = MKDEV(spidev_major, minor); + dev = device_create(st54spi_class, &spi->dev, st54spi->devt, + // spidev, "spidev%d.%d", + // spi->master->bus_num, spi->chip_select); + st54spi, "st54spi"); + status = PTR_ERR_OR_ZERO(dev); + } else { + dev_dbg(&spi->dev, "no minor number available!\n"); + status = -ENODEV; + } + if (status == 0) { + set_bit(minor, minors); + list_add(&st54spi->device_entry, &device_list); + } + mutex_unlock(&device_list_lock); + + st54spi->speed_hz = spi->max_speed_hz; + dev_dbg(&spi->dev, "st54spi->speed_hz=%d\n", st54spi->speed_hz); + // { + // /* fixed SPI clock speed: 109200000 */ + // int period = DIV_ROUND_UP(109200000, st54spi->speed_hz); + + // st54spi_chip_info.cs_idletime = period; + // st54spi_chip_info.cs_holdtime = period; + // } + + // this method exists since kernel 5.3 + // it uses u8 parameter in kernel 5.4 (clk count) and spi_delay parameter in kernel 5.5 (clk_count fobidden) + // target 10us delay ==> use CLK=4MHz and value 31 (even if u8 data, some platforms limit to 0-31) + + // spi_set_cs_timing(spi, &st54spi_delay, NULL, NULL); + + spi_param = devm_kzalloc(&spi->dev, sizeof(spi_param), GFP_KERNEL); + if (spi_param == NULL) { + status = -ENOMEM; + } else { + /* Initialize the driver data */ + spi_param->spi_cs_clk_delay = 90; + spi->controller_data = spi_param; + } + + if (status == 0) + spi_set_drvdata(spi, st54spi); + else + kfree(st54spi); + + (void)st54spi_parse_dt(&spi->dev, st54spi); + + if (st54spi->power_or_nreset_gpio != 0) { + int default_value = 0; + + ret = gpio_request(st54spi->power_or_nreset_gpio, + "gpio-power_nreset-std" + ); + if (ret) + dev_info(&spi->dev, "%s : power request failed (%d)\n", + __FILE__, ret); + + dev_info(&spi->dev, "%s : power/nreset GPIO = %d\n", __func__, + st54spi->power_or_nreset_gpio); + ret = gpio_direction_output(st54spi->power_or_nreset_gpio, default_value); + if (ret) + dev_info(&spi->dev, "%s : reset direction_output failed\n", __FILE__); + + /* active high */ + gpio_set_value(st54spi->power_or_nreset_gpio, default_value); + } + + if (st54spi->power_or_nreset_gpio_mode == POWER_MODE_ST54H) { +#ifndef MODULE + dev_info(&spi->dev, "%s : Register with st21nfc driver, %p\n", + __func__, st54spi); + st21nfc_register_st54spi_cb(st54spi_st21nfc_cb, st54spi); +#else + dev_err(&spi->dev, "%s : st54spi as module cannot use ST54H fully\n", + __func__); +#endif + } + + return status; +} + +static int st54spi_remove(struct spi_device *spi) +{ + struct st54spi_data *st54spi = spi_get_drvdata(spi); + + if (st54spi->power_or_nreset_gpio_mode == POWER_MODE_ST54H) { +#ifndef MODULE + dev_info(&st54spi->spi->dev, "%s : Unregister from st21nfc driver\n", + __func__); + st21nfc_unregister_st54spi_cb(); +#endif + } + /* make sure ops on existing fds can abort cleanly */ + spin_lock_irq(&st54spi->spi_lock); + st54spi->spi = NULL; + st54spi->spi_reset = NULL; + spin_unlock_irq(&st54spi->spi_lock); + + /* prevent new opens */ + mutex_lock(&device_list_lock); + list_del(&st54spi->device_entry); + device_destroy(st54spi_class, st54spi->devt); + clear_bit(MINOR(st54spi->devt), minors); + if (st54spi->users == 0) + kfree(st54spi); + + mutex_unlock(&device_list_lock); + + return 0; +} + +static struct spi_driver st54spi_spi_driver = { + .driver = { + .name = "st54spi", + .of_match_table = of_match_ptr(st54spi_dt_ids), + .acpi_match_table = ACPI_PTR(st54spi_acpi_ids), + }, + .probe = st54spi_probe, + .remove = st54spi_remove, + + /* NOTE: suspend/resume methods are not necessary here. + * We don't do anything except pass the requests to/from + * the underlying controller. The refrigerator handles + * most issues; the controller driver handles the rest. + */ +}; + +/*-------------------------------------------------------------------------*/ + +static int __init st54spi_init(void) +{ + int status; + + pr_info("Loading st54spi driver\n"); + + /* Claim our 256 reserved device numbers. Then register a class + * that will key udev/mdev to add/remove /dev nodes. Last, register + * the driver which manages those device numbers. + */ + BUILD_BUG_ON(N_SPI_MINORS > 256); + spidev_major = __register_chrdev(0, 0, N_SPI_MINORS, + "spi", &st54spi_fops); + pr_info("Loading st54spi driver, major: %d\n", spidev_major); + + st54spi_class = class_create(THIS_MODULE, "eSE"); + if (IS_ERR(st54spi_class)) { + unregister_chrdev(spidev_major, st54spi_spi_driver.driver.name); + return PTR_ERR(st54spi_class); + } + + status = spi_register_driver(&st54spi_spi_driver); + if (status < 0) { + class_destroy(st54spi_class); + unregister_chrdev(spidev_major, st54spi_spi_driver.driver.name); + } + pr_info("Loading st54spi driver: %d\n", status); + return status; +} +module_init(st54spi_init); + +static void __exit st54spi_exit(void) +{ + spi_unregister_driver(&st54spi_spi_driver); + class_destroy(st54spi_class); + unregister_chrdev(spidev_major, st54spi_spi_driver.driver.name); +} +module_exit(st54spi_exit); + +MODULE_AUTHOR("Andrea Paterniani, "); +MODULE_DESCRIPTION("User mode SPI device interface"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:st54spi"); diff --git a/drivers/fm/Android.mk b/drivers/fm/Android.mk new file mode 100644 index 000000000000..a2c74915881a --- /dev/null +++ b/drivers/fm/Android.mk @@ -0,0 +1,10 @@ +ifeq ($(BOARD_HAS_FM_ELNA), true) +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := fm_ctrl.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk +endif diff --git a/drivers/fm/Kbuild b/drivers/fm/Kbuild new file mode 100644 index 000000000000..3140ec437078 --- /dev/null +++ b/drivers/fm/Kbuild @@ -0,0 +1,4 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall + +obj-m += fm_ctrl.o diff --git a/drivers/fm/Makefile b/drivers/fm/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/fm/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/fm/fm_ctrl.c b/drivers/fm/fm_ctrl.c new file mode 100644 index 000000000000..37a387b9949c --- /dev/null +++ b/drivers/fm/fm_ctrl.c @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DRIVER_VERSION "0.0.1" + +struct fm_ctrl_drvdata { + struct device *dev; + struct pinctrl *pinctrl; + struct pinctrl_state *pstate_default; + struct pinctrl_state *pstate_active; + struct pinctrl_state *pstate_suspend; + bool factory_mode; +}; + + +static bool mmi_factory_check(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool factory = false; + + if (np) + factory = of_property_read_bool(np, "mmi,factory-cable"); + + of_node_put(np); + + return factory; +} + +static ssize_t device_name_read(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct fm_ctrl_drvdata *data = dev_get_drvdata(dev); + + if (!data) { + pr_err("fm_ctrl drvdata is NULL\n"); + return -EINVAL; + } + + return snprintf(buf, PAGE_SIZE, "moto fm control intf\n"); +} + + +static ssize_t elna_en_read(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct fm_ctrl_drvdata *data = dev_get_drvdata(dev); + + if (!data) { + pr_err("fm_ctrl drvdata is NULL\n"); + return -EINVAL; + } + + return snprintf(buf, PAGE_SIZE, "fm_ctrl: not support!\n"); +} + +static ssize_t elna_en_write(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct fm_ctrl_drvdata *data = dev_get_drvdata(dev); + int ret = 0; + unsigned int res = 0; + + if (!data) { + pr_err("fm_ctrl drvdata is NULL\n"); + return -EINVAL; + } + + ret = kstrtouint(buf, 0, &res); + if(ret) { + pr_err("fm_ctrl failed to get data, set as default!\n"); + } + if(1 == res) { + ret = pinctrl_select_state(data->pinctrl, data->pstate_active); + } + else { + ret = pinctrl_select_state(data->pinctrl, data->pstate_suspend); + } + if(ret) { + pr_err("fm_ctrl failed to set pinctrl!\n"); + } + else { + pr_info("fm_ctrl set elan=%u\n", res); + } + + return count; +} + + +static DEVICE_ATTR(device_name, 0444, device_name_read, NULL); +static DEVICE_ATTR(elna_en, 0644, elna_en_read, elna_en_write); + +static struct attribute *fm_ctrl_sysfs_attrs[] = { + &dev_attr_device_name.attr, + &dev_attr_elna_en.attr, + NULL, +}; + +static struct attribute_group fm_ctrl_sysfs_attr_grp = { + .attrs = fm_ctrl_sysfs_attrs, +}; + + +static int fm_ctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fm_ctrl_drvdata *drvdata; + int ret = 0; + + dev_dbg(&pdev->dev, "%s begin\n", __func__); + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->dev = &pdev->dev; + platform_set_drvdata(pdev, drvdata); + + /* Get pinctrl if target uses pinctrl */ + drvdata->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(drvdata->pinctrl)) { + ret = PTR_ERR(drvdata->pinctrl); + pr_err("%s: Pincontrol DT property returned %X\n", __func__, ret); + return ret; + } + + drvdata->pstate_default = pinctrl_lookup_state(drvdata->pinctrl, + "default"); + if (IS_ERR_OR_NULL(drvdata->pstate_default)) { + ret = PTR_ERR(drvdata->pstate_default); + pr_err("Can not lookup default pinstate %d\n", ret); + return -ENOENT; + } + drvdata->pstate_active = pinctrl_lookup_state(drvdata->pinctrl, + "elna_active"); + if (IS_ERR_OR_NULL(drvdata->pstate_active)) { + ret = PTR_ERR(drvdata->pstate_active); + pr_err("Can not lookup active pinstate %d\n", ret); + return ret; + } + drvdata->pstate_suspend = pinctrl_lookup_state(drvdata->pinctrl, + "elna_suspend"); + if (IS_ERR_OR_NULL(drvdata->pstate_suspend)) { + ret = PTR_ERR(drvdata->pstate_suspend); + pr_err("Can not lookup suspend pinstate %d\n", ret); + return ret; + } + drvdata->factory_mode = mmi_factory_check(); + if(drvdata->factory_mode) { + ret = pinctrl_select_state(drvdata->pinctrl, drvdata->pstate_active); + if(ret) { + pr_err("fm_ctrl failed to set pinctrl @factory mode!\n"); + } + else { + pr_info("fm_ctrl enable elan @ factory mode\n"); + } + } + else { + ret = pinctrl_select_state(drvdata->pinctrl, drvdata->pstate_default); + if(ret) { + pr_err("fm_ctrl failed to set pinctrl as default!\n"); + } + else { + pr_info("fm_ctrl disable elan as default.\n"); + } + } + + ret = sysfs_create_group(&dev->kobj, &fm_ctrl_sysfs_attr_grp); + if (ret) { + pr_err("%s: sysfs group creation failed %d\n", __func__, ret); + return ret; + } + + device_init_wakeup(&pdev->dev, 1); + dev_info(&pdev->dev, "probe: All success !\n"); + + return ret; +} + +static int fm_ctrl_remove(struct platform_device *pdev) +{ + device_init_wakeup(&pdev->dev, 0); + return 0; +} + + +static const struct of_device_id fm_ctrl_match[] = { + { .compatible = "moto,fmctrl" }, + {} +}; + +static struct platform_driver fm_ctrl_plat_driver = { + .probe = fm_ctrl_probe, + .remove = fm_ctrl_remove, + .driver = { + .name = "fm_ctrl", + .owner = THIS_MODULE, + .of_match_table = fm_ctrl_match, + }, +}; + +module_platform_driver(fm_ctrl_plat_driver); + + +MODULE_AUTHOR("Motorola Mobiity"); +MODULE_DESCRIPTION("FMRadio control interface driver"); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/pcal6408/Android.mk b/drivers/gpio/pcal6408/Android.mk new file mode 100755 index 000000000000..3e121a3a2aff --- /dev/null +++ b/drivers/gpio/pcal6408/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := gpio-pcal6408.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/gpio/pcal6408/Kbuild b/drivers/gpio/pcal6408/Kbuild new file mode 100755 index 000000000000..ec9c539d221a --- /dev/null +++ b/drivers/gpio/pcal6408/Kbuild @@ -0,0 +1,9 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +ifeq ($(TARGET_BUILD_VARIANT),userdebug) + EXTRA_CFLAGS += -DGPIOI2C_USER_DEBUG +endif + +obj-m += gpio-pcal6408.o diff --git a/drivers/gpio/pcal6408/gpio-pcal6408.c b/drivers/gpio/pcal6408/gpio-pcal6408.c new file mode 100755 index 000000000000..486f39bca255 --- /dev/null +++ b/drivers/gpio/pcal6408/gpio-pcal6408.c @@ -0,0 +1,616 @@ +/* + * Copyright (c) 2020 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCAL6408_DBG(msg, ...) \ + pr_debug("pcal6048: [%s] "msg, __func__, ##__VA_ARGS__) +#define PCAL6408_ERR(msg, ...) \ + pr_err("pcal6048: [%s] "msg, __func__, ##__VA_ARGS__) +#define PCAL6408_LOG(msg, ...) \ + pr_info("pcal6048: [%s] "msg, __func__, ##__VA_ARGS__) + +#define PCAL6408_ADDR_INPUT 0x00 +#define PCAL6408_ADDR_OUTPUT 0x01 +#define PCAL6408_ADDR_CONFIG 0x03 +#define PCAL6408_ADDR_PULL_EN 0x43 +#define PCAL6408_ADDR_PULL_CFG 0x44 + +#define TCA6418_ADDR_INPUT 0x14 +#define TCA6418_ADDR_OUTPUT 0x17 +#define TCA6418_ADDR_CONFIG 0x23 +#define TCA6418_ADDR_PULL_CFG 0x2C + +#define BIAS_PULL_UP 0 +#define BIAS_PULL_DOWN 1 +#define BIAS_NO_PULL 2 + +struct pcal6048_dev; + +struct pcal6048_config { + char *name; + int nr_gpio; + int dir_out_val; + int direction; + int output; + int input; + int (*get_bit)(int); + int (*set_pulldown)(int, struct pcal6048_dev *); + int (*set_pullup)(int, struct pcal6048_dev *); + int (*set_nopull)(int, struct pcal6048_dev *); + int (*get_pull)(int, struct pcal6048_dev *); +}; + +struct pcal6048_dev { + const char *name; + struct i2c_client *client; + struct device *dev; + struct regmap *regmap; + struct gpio_chip gpio_chip; + const struct pcal6048_config *conf; + struct pinctrl_desc ctrldesc; + struct pinctrl_dev *pctl_dev; + struct pinctrl_gpio_range grange; + int reset_gpio; +}; + +static int pcal6048_set_reg(struct pcal6048_dev *chip, int gpio, int reg); +static int pcal6048_clear_reg(struct pcal6048_dev *chip, int gpio, int reg); +static int pcal6048_read_reg(struct pcal6048_dev *chip, int gpio, int reg); + +static int pcal6408_get_bit(int gpio) +{ + return gpio; +} + +static int pcal6408_set_pulldown(int gpio, struct pcal6048_dev *chip) +{ + if (pcal6048_clear_reg(chip, gpio, PCAL6408_ADDR_PULL_CFG)) { + PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio); + return 1; + } + + if (pcal6048_set_reg(chip, gpio, PCAL6408_ADDR_PULL_EN)) { + PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio); + return 1; + } + + return 0; +} + +static int pcal6408_set_pullup(int gpio, struct pcal6048_dev *chip) +{ + if (pcal6048_set_reg(chip, gpio, PCAL6408_ADDR_PULL_CFG)) { + PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio); + return 1; + } + + if (pcal6048_set_reg(chip, gpio, PCAL6408_ADDR_PULL_EN)) { + PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio); + return 1; + } + + return 0; +} + +static int pcal6408_set_nopull(int gpio, struct pcal6048_dev *chip) +{ + if (pcal6048_clear_reg(chip, gpio, PCAL6408_ADDR_PULL_EN)) { + PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio); + return 1; + } + + return 0; +} + +static int pcal6408_get_pull(int gpio, struct pcal6048_dev *chip) +{ + if (!pcal6048_read_reg(chip, gpio, PCAL6408_ADDR_PULL_EN)) + return BIAS_NO_PULL; + else if (pcal6048_read_reg(chip, gpio, PCAL6408_ADDR_PULL_CFG)) + return BIAS_PULL_UP; + else + return BIAS_PULL_DOWN; +} + +static const struct pcal6048_config pcal6408a_conf = { + .name = "pcal6408a_conf", + .nr_gpio = 8, + .set_pulldown = pcal6408_set_pulldown, + .set_pullup = pcal6408_set_pullup, + .set_nopull = pcal6408_set_nopull, + .get_pull = pcal6408_get_pull, + .get_bit = pcal6408_get_bit, + .dir_out_val = 0, /* Set 0 to direction for output */ + .direction = PCAL6408_ADDR_CONFIG, + .output = PCAL6408_ADDR_OUTPUT, + .input = PCAL6408_ADDR_INPUT, +}; + +static int tca6418_get_bit(int gpio) +{ + /* Bitmapping is strange, gpio7 is bit 0 of first addr, + * but gpio8 is bit 0 of second addr, gpio 16 is bit 0 + * of third addr + */ + if (gpio <= 7) + return (7 - gpio); + else if (gpio > 7 && gpio <= 15) + return gpio - 8; + else + return gpio - 16; +} + +static int tca6418_set_pulldown(int gpio, struct pcal6048_dev *chip) +{ + if (pcal6048_clear_reg(chip, gpio, TCA6418_ADDR_PULL_CFG)) { + PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio); + return 1; + } + + return 0; +} + +static int tca6418_set_nopull(int gpio, struct pcal6048_dev *chip) +{ + if (pcal6048_set_reg(chip, gpio, TCA6418_ADDR_PULL_CFG)) { + PCAL6408_ERR("Failed to set pulldown for gpio %d\n", gpio); + return 1; + } + + return 0; +} + +static int tca6418_get_pull(int gpio, struct pcal6048_dev *chip) +{ + if (!pcal6048_read_reg(chip, gpio, TCA6418_ADDR_PULL_CFG)) + return BIAS_PULL_DOWN; + else + return BIAS_NO_PULL; +} + +static const struct pcal6048_config tca6418e_conf = { + .name = "tca6418e_conf", + .nr_gpio = 18, + .set_pulldown = tca6418_set_pulldown, + .set_pullup = NULL, + .set_nopull = tca6418_set_nopull, + .get_pull = tca6418_get_pull, + .get_bit = tca6418_get_bit, + .dir_out_val = 1, /* Set 1 to direction for output */ + .direction = TCA6418_ADDR_CONFIG, + .output = TCA6418_ADDR_OUTPUT, + .input = TCA6418_ADDR_INPUT, +}; + +static int pcal6048_read_reg(struct pcal6048_dev *chip, int gpio, int reg) +{ + int bit_num = chip->conf->get_bit(gpio); + uint8_t bit = BIT(bit_num); + int reg_off = (gpio / 8); + int val; + int ret = -1; + + PCAL6408_DBG("%s (gpio %d), read bit %d from 0x%x\n", + chip->conf->name, gpio, bit_num, reg + reg_off); + + ret = regmap_read(chip->regmap, reg + reg_off, &val); + if (ret < 0) + return 0; + + return !!(val & bit); +} + +static int pcal6048_set_reg(struct pcal6048_dev *chip, int gpio, int reg) +{ + int bit_num = chip->conf->get_bit(gpio); + uint8_t bit = BIT(bit_num); + int reg_off = (gpio / 8); + + PCAL6408_DBG("%s (gpio %d), set bit %d in 0x%x\n", + chip->conf->name, gpio, bit_num, reg + reg_off); + + return regmap_write_bits(chip->regmap, reg + reg_off, bit, bit); +} + +static int pcal6048_clear_reg(struct pcal6048_dev *chip, int gpio, int reg) +{ + int bit_num = chip->conf->get_bit(gpio); + uint8_t bit = BIT(bit_num); + int reg_off = (gpio / 8); + + PCAL6408_DBG("%s (gpio %d), clear bit %d in 0x%x\n", + chip->conf->name, gpio, bit_num, reg + reg_off); + + return regmap_write_bits(chip->regmap, reg + reg_off, bit, 0); +} + +static int pcal6048_direction_in(struct gpio_chip *gc, unsigned offset) +{ + struct pcal6048_dev *chip = gpiochip_get_data(gc); + int ret; + + if (!chip->conf->dir_out_val) + ret = pcal6048_set_reg(chip, offset, chip->conf->direction); + else + ret = pcal6048_clear_reg(chip, offset, chip->conf->direction); + + return ret; +} + +static void pcal6048_set_gpio(struct gpio_chip *gc, unsigned offset, int value) +{ + struct pcal6048_dev *chip = gpiochip_get_data(gc); + + if (value) + pcal6048_set_reg(chip, offset, chip->conf->output); + else + pcal6048_clear_reg(chip, offset, chip->conf->output); +} + +static int pcal6048_direction_out(struct gpio_chip *gc, unsigned offset, int value) +{ + struct pcal6048_dev *chip = gpiochip_get_data(gc); + int ret; + + ret = pcal6048_set_reg(chip, offset, chip->conf->output); + if (ret) + return ret; + + if (chip->conf->dir_out_val) + ret = pcal6048_set_reg(chip, offset, chip->conf->direction); + else + ret = pcal6048_clear_reg(chip, offset, chip->conf->direction); + + pcal6048_set_gpio(gc, offset, value); + + return ret; +} + +/* 0 is out, 1 is in */ +static int pcal6048_get_direction(struct gpio_chip *gc, unsigned offset) +{ + struct pcal6048_dev *chip = gpiochip_get_data(gc); + int val = pcal6048_read_reg(chip, offset, chip->conf->direction); + + if (val == chip->conf->dir_out_val) + return 0; + else; + return 1; +} + +static int pcal6048_get_gpio(struct gpio_chip *gc, unsigned offset) +{ + struct pcal6048_dev *chip = gpiochip_get_data(gc); + + /* TODO do I need to read twice to make sure it is cleared? (TCA6418e pg 12) */ + return pcal6048_read_reg(chip, offset, chip->conf->input); +} + +static int pcal6048_set_config(struct gpio_chip *gc, unsigned int offset, unsigned long config) +{ + struct pcal6048_dev *chip = gpiochip_get_data(gc); + unsigned arg = pinconf_to_config_argument(config); + unsigned param = pinconf_to_config_param(config); + + PCAL6408_DBG("Set config for gpio %d!\n", offset); + + switch (param) { + case PIN_CONFIG_BIAS_PULL_UP: + if (chip->conf->set_pullup) + return chip->conf->set_pullup(offset, chip); + else + return -ENOTSUPP; + case PIN_CONFIG_BIAS_PULL_DOWN: + if (chip->conf->set_pulldown) + return chip->conf->set_pulldown(offset, chip); + else + return -ENOTSUPP; + case PIN_CONFIG_BIAS_DISABLE: + if (chip->conf->set_nopull) + return chip->conf->set_nopull(offset, chip); + else + return -ENOTSUPP; + case PIN_CONFIG_INPUT_ENABLE: + return pcal6048_direction_in(gc, offset); + case PIN_CONFIG_OUTPUT_ENABLE: + return pcal6048_direction_out(gc, offset, 1); + case PIN_CONFIG_OUTPUT: + return pcal6048_direction_out(gc, offset, !!arg); + default: + return -ENOTSUPP; + } +} + +static void pcal6048_dbg_show(struct seq_file *s, struct gpio_chip *gc) +{ + struct pcal6048_dev *chip = gpiochip_get_data(gc); + unsigned gpio = gc->base; + unsigned i; + int dir = 0; + int pull = 0; + + for (i = 0; i < gc->ngpio; i++, gpio++) { + seq_printf(s, " gpio%d:", i); + + dir = pcal6048_get_direction(gc, i); + /* Input is 1 */ + if (dir) + seq_printf(s, " in %d", pcal6048_get_gpio(gc, i)); + else + seq_printf(s, " out (set to %d)", + pcal6048_read_reg(chip, i, chip->conf->output)); + + if (chip->conf->get_pull) { + pull = chip->conf->get_pull(i, chip); + switch(pull) { + case BIAS_NO_PULL: + seq_printf(s, " bias-no-pull"); + break; + case BIAS_PULL_DOWN: + seq_printf(s, " bias-pull-down"); + break; + case BIAS_PULL_UP: + seq_printf(s, " bias-pull-up"); + break; + default: + break; + } + } + + seq_puts(s, "\n"); + } +} + +static void pcal6048_setup_gpio_chip(struct pcal6048_dev *chip) +{ + struct gpio_chip *gc; + + gc = &chip->gpio_chip; + + gc->label = chip->conf->name; + gc->base = -1; + gc->ngpio = chip->conf->nr_gpio; + gc->parent = chip->dev; + gc->owner = THIS_MODULE; + gc->direction_input = pcal6048_direction_in; + gc->direction_output = pcal6048_direction_out; + gc->get_direction = pcal6048_get_direction; + gc->get = pcal6048_get_gpio; + gc->set = pcal6048_set_gpio; + gc->set_config = pcal6048_set_config; + gc->dbg_show = pcal6048_dbg_show; + gc->can_sleep = true; +} + +static int pcal6048_pinconf_get(struct pinctrl_dev *pctldev, + unsigned pin, unsigned long *config) +{ + return -ENOTSUPP; +} + +static int pcal6048_pinconf_set(struct pinctrl_dev *pctldev, + unsigned int pin, unsigned long *configs, + unsigned int num_configs) +{ + struct pcal6048_dev *chip = pinctrl_dev_get_drvdata(pctldev); + int i; + + for (i=0; i < num_configs; i++) { + chip->gpio_chip.set_config(&chip->gpio_chip, pin, configs[i]); + } + + return 0; +} + +static int pcal6048_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct pcal6048_dev *chip = pinctrl_dev_get_drvdata(pctldev); + + return chip->conf->nr_gpio; +} + +static const char *pcal6048_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct pcal6048_dev *chip = pinctrl_dev_get_drvdata(pctldev); + + return chip->conf->name; +} + +static const struct pinctrl_ops pcal6048_pctl_ops = { + .get_groups_count = pcal6048_get_groups_count, + .get_group_name = pcal6048_get_group_name, + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, +}; + +static const struct pinconf_ops pcal6048_pinconf_ops = { + .is_generic = true, + .pin_config_get = pcal6048_pinconf_get, + .pin_config_set = pcal6048_pinconf_set, +}; + +static int pcal6048_register_pinctrl(struct pcal6048_dev *chip) +{ + struct pinctrl_desc *ctrldesc = &chip->ctrldesc; + struct pinctrl_pin_desc *pindesc, *pdesc; + int pin; + + ctrldesc->name = chip->conf->name; + ctrldesc->owner = THIS_MODULE; + ctrldesc->confops = &pcal6048_pinconf_ops; + ctrldesc->pctlops = &pcal6048_pctl_ops; + + pindesc = devm_kcalloc(chip->dev, + chip->conf->nr_gpio, sizeof(*pindesc), + GFP_KERNEL); + + ctrldesc->pins = pindesc; + ctrldesc->npins = chip->conf->nr_gpio; + + pdesc = pindesc; + for (pin = 0; pin < chip->conf->nr_gpio; pin++) { + pdesc->number = pin; + pdesc->name = kasprintf(GFP_KERNEL, "gpio%d", pin); + pdesc++; + } + + chip->pctl_dev = devm_pinctrl_register(chip->dev, ctrldesc, chip); + + if (IS_ERR(chip->pctl_dev)) { + PCAL6408_ERR("Failed to register pinctrl\n"); + return PTR_ERR(chip->pctl_dev); + } + + chip->grange.base = chip->gpio_chip.base; + chip->grange.npins = chip->conf->nr_gpio; + chip->grange.gc = &chip->gpio_chip; + + pinctrl_add_gpio_range(chip->pctl_dev, &chip->grange); + + return 0; +} + +static const struct regmap_config pcal6048_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xFF, +}; + +static const struct of_device_id pcal6048_match_table[] = { + { .compatible = "ti,tca6418e", .data = &tca6418e_conf }, + { .compatible = "nxp,pcal6408a", .data = &pcal6408a_conf }, + { }, +}; + +static int pcal6048_config_reset(struct pcal6048_dev *chip) +{ + int err = 0; + + chip->reset_gpio = of_get_gpio(chip->dev->of_node, 0); + if (gpio_is_valid(chip->reset_gpio)) { + err = devm_gpio_request(chip->dev, chip->reset_gpio, "pcal6048_reset"); + if (err) { + PCAL6408_ERR("Reset gpio request failed\n"); + return err; + } else { + /* Active low, so set it high */ + err = gpio_direction_output(chip->reset_gpio, 1); + if (err) { + PCAL6408_ERR("Reset gpio set failed\n"); + return err; + } + } + } else + PCAL6408_LOG("No reset gpio set in dtb\n"); + + return 0; +} + +static int pcal6048_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + struct pcal6048_dev *chip; + const struct of_device_id *match; + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->client = client; + chip->dev = &client->dev; + chip->name = "pcal6048"; + + chip->regmap = regmap_init_i2c(client, &pcal6048_regmap_config); + if (IS_ERR(chip->regmap)) { + PCAL6408_ERR("Couldn't initialize register regmap rc = %ld\n", + PTR_ERR(chip->regmap)); + rc = PTR_ERR(chip->regmap); + goto free_mem; + } + + match = of_match_device(pcal6048_match_table, chip->dev); + if (!match || !match->data) { + PCAL6408_ERR("Missing config data!\n"); + goto free_mem; + } + + chip->conf = match->data; + PCAL6408_DBG("Using config %s\n", chip->conf->name); + + i2c_set_clientdata(client, chip); + dev_set_drvdata(chip->dev, chip); + + pcal6048_setup_gpio_chip(chip); + pcal6048_config_reset(chip); + + rc = devm_gpiochip_add_data(chip->dev, &chip->gpio_chip, chip); + if (rc) { + PCAL6408_ERR("Couldn't add gpio chip rc=%d\n", rc); + goto free_mem; + } + + pcal6048_register_pinctrl(chip); + + return 0; + +free_mem: + devm_kfree(chip->dev, chip); + return rc; +} + +static int pcal6048_remove(struct i2c_client *client) +{ + return 0; +} + +static struct i2c_driver pcal6048_driver = { + .driver = { + .name = "pcal6048", + .owner = THIS_MODULE, + .of_match_table = pcal6048_match_table, + }, + .probe = pcal6048_probe, + .remove = pcal6048_remove, +}; + +module_i2c_driver(pcal6048_driver); + +MODULE_DESCRIPTION("pcal6048"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gud/MobiCoreDriver/Android.mk b/drivers/gud/MobiCoreDriver/Android.mk new file mode 100644 index 000000000000..43f38c709b37 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/Android.mk @@ -0,0 +1,11 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mcDrvModule.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +ifeq ($(RSU_SELECT_INTERNAL_CLOCK), true) +KERNEL_CFLAGS += RSU_INTERNAL_CLOCK=y +endif +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/gud/MobiCoreDriver/Kbuild b/drivers/gud/MobiCoreDriver/Kbuild new file mode 100644 index 000000000000..973e3a3eff68 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/Kbuild @@ -0,0 +1,49 @@ +#/* +# * Copyright (c) 2013-2018 TRUSTONIC LIMITED +# * 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 +# * 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. +# */ +# +# Makefile for the Kinibi core driver +# + + +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -DNDEBUG +EXTRA_CFLAGS += -Wno-declaration-after-statement +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/drivers/gud/MobiCoreDriver + +ifneq ($(filter m y,$(RSU_INTERNAL_CLOCK)),) + EXTRA_CFLAGS += -DRSU_INTERNAL_CLOCK +endif + +obj-m += mcDrvModule.o + + +mcDrvModule-y := \ + admin.o \ + client.o \ + clientlib.o \ + clock.o \ + fastcall.o \ + iwp.o \ + logging.o \ + main.o \ + mcp.o \ + mmu.o \ + nq.o \ + session.o \ + teeclientapi.o \ + user.o \ + xen_be.o \ + xen_common.o \ + xen_fe.o diff --git a/drivers/gud/MobiCoreDriver/Makefile b/drivers/gud/MobiCoreDriver/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/gud/MobiCoreDriver/admin.c b/drivers/gud/MobiCoreDriver/admin.c new file mode 100644 index 000000000000..9a4813d7f363 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/admin.c @@ -0,0 +1,1186 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2018 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE +#include /* signal_pending */ +#endif +#include + +#include "mc_user.h" +#include "mc_admin.h" + +#include "main.h" +#include "mmu.h" /* For load_check and load_token */ +#include "mcp.h" +#include "nq.h" +#include "client.h" +#include "admin.h" + +static struct { + struct mutex admin_tgid_mutex; /* Lock for admin_tgid below */ + pid_t admin_tgid; + int (*tee_start_cb)(void); + void (*tee_stop_cb)(void); + int last_tee_ret; + struct notifier_block tee_stop_notifier; +} l_ctx; + +static struct mc_admin_driver_request { + /* Global */ + struct mutex mutex; /* Protects access to this struct */ + struct mutex states_mutex; /* Protect access to the states */ + enum client_state { + IDLE, + REQUEST_SENT, + BUFFERS_READY, + } client_state; + enum server_state { + NOT_CONNECTED, /* Device not open */ + READY, /* Waiting for requests */ + REQUEST_RECEIVED, /* Got a request, is working */ + RESPONSE_SENT, /* Has sent a response header */ + DATA_SENT, /* Blocked until data is consumed */ + } server_state; + /* Request */ + u32 request_id; + struct mc_admin_request request; + struct completion client_complete; + /* Response */ + struct mc_admin_response response; + struct completion server_complete; + void *buffer; /* Reception buffer (pre-allocated) */ + size_t size; /* Size of the reception buffer */ + bool lock_channel_during_freeze;/* Is freezing ongoing ? */ +} g_request; + +/* The mutex around the channel communication has to be wrapped in order + * to handle this use case : + * client 1 calls request_send() + * wait on wait_for_completion_interruptible (with channel mutex taken) + * client 2 calls request_send() + * waits on mutex_lock(channel mutex) + * kernel starts freezing tasks (suspend or reboot ongoing) + * if we do nothing, then the freezing will be aborted because client 1 + * and 2 have to enter the refrigerator by themselves. + * Note : mutex cannot be held during freezing, so client 1 has release it + * => step 1 : client 1 sets a bool that says that the channel is still in use + * => step 2 : client 1 release the lock and enter the refrigerator + * => now any client trying to use the channel will face the bool preventing + * to use the channel. They also have to enter the refrigerator. + * + * These 3 functions handle this + */ +static void check_freezing_ongoing(void) +{ + /* We don't want to let the channel be used. Let everyone know + * that we're using it + */ + g_request.lock_channel_during_freeze = 1; + /* Now we can safely release the lock */ + mutex_unlock(&g_request.mutex); + /* Let's try to freeze */ + try_to_freeze(); + /* Either freezing happened or was canceled. + * In both cases, reclaim the lock + */ + mutex_lock(&g_request.mutex); + g_request.lock_channel_during_freeze = 0; +} + +static void channel_lock(void) +{ + while (1) { + mutex_lock(&g_request.mutex); + /* We took the lock, but is there any freezing ongoing? */ + if (g_request.lock_channel_during_freeze == 0) + break; + + /* yes, so let's freeze */ + mutex_unlock(&g_request.mutex); + try_to_freeze(); + /* Either freezing succeeded or was canceled. + * In both case, try again to get the lock. + * Give some CPU time to let the contender + * finish his channel operation + */ + msleep(500); + }; +} + +static void channel_unlock(void) +{ + mutex_unlock(&g_request.mutex); +} + +#if KERNEL_VERSION(3, 13, 0) <= LINUX_VERSION_CODE +static inline void reinit_completion_local(struct completion *x) +{ + reinit_completion(x); +} +#else +static inline void reinit_completion_local(struct completion *x) +{ + INIT_COMPLETION(*x); +} +#endif + +static struct tee_object *tee_object_alloc(bool is_sp_trustlet, size_t length) +{ + struct tee_object *obj; + size_t size = sizeof(*obj) + length; + size_t header_length = 0; + + /* Determine required size */ + if (is_sp_trustlet) { + /* Need space for lengths info and containers */ + header_length = sizeof(struct mc_blob_len_info); + size += header_length + 3 * MAX_SO_CONT_SIZE; + } + + /* Check size for overflow */ + if (size < length || size > OBJECT_LENGTH_MAX) { + mc_dev_err(-ENOMEM, "cannot allocate object of size %zu", + length); + return NULL; + } + + /* Allocate memory */ + obj = vzalloc(size); + if (!obj) + return NULL; + + /* A non-zero header_length indicates that we have a SP trustlet */ + obj->header_length = (u32)header_length; + obj->length = (u32)length; + return obj; +} + +void tee_object_free(struct tee_object *obj) +{ + vfree(obj); +} + +static inline void client_state_change(enum client_state state) +{ + mutex_lock(&g_request.states_mutex); + mc_dev_devel("client state changes from %d to %d", + g_request.client_state, state); + g_request.client_state = state; + mutex_unlock(&g_request.states_mutex); +} + +static inline bool client_state_is(enum client_state state) +{ + bool is; + + mutex_lock(&g_request.states_mutex); + is = g_request.client_state == state; + mutex_unlock(&g_request.states_mutex); + return is; +} + +static inline void server_state_change(enum server_state state) +{ + mutex_lock(&g_request.states_mutex); + mc_dev_devel("server state changes from %d to %d", + g_request.server_state, state); + g_request.server_state = state; + mutex_unlock(&g_request.states_mutex); +} + +static inline bool server_state_is(enum server_state state) +{ + bool is; + + mutex_lock(&g_request.states_mutex); + is = g_request.server_state == state; + mutex_unlock(&g_request.states_mutex); + return is; +} + +static void request_cancel(void); + +static int request_send(u32 command, const struct mc_uuid_t *uuid, bool is_gp, + u32 spid) +{ + int counter_ms = 0; + int wait_tens = 0; + int ret = 0; + + /* Prepare request */ + mutex_lock(&g_request.states_mutex); + /* Wait a little for daemon to connect */ + while (g_request.server_state == NOT_CONNECTED) { + mutex_unlock(&g_request.states_mutex); + if (signal_pending(current)) + return -ERESTARTSYS; + + if (counter_ms == 10000) { /* print every 10s */ + wait_tens++; + mc_dev_info("daemon not connected after %d0s, waiting", + wait_tens); + counter_ms = 0; + } + + msleep(20); + counter_ms += 20; + mutex_lock(&g_request.states_mutex); + } + + WARN_ON(g_request.client_state != IDLE); + if (g_request.server_state != READY) { + mutex_unlock(&g_request.states_mutex); + if (g_request.server_state != NOT_CONNECTED) { + ret = -EPROTO; + mc_dev_err(ret, "invalid daemon state %d", + g_request.server_state); + goto end; + } else { + ret = -EHOSTUNREACH; + mc_dev_err(ret, "daemon not connected"); + goto end; + } + } + + memset(&g_request.request, 0, sizeof(g_request.request)); + memset(&g_request.response, 0, sizeof(g_request.response)); + /* + * Do not update the request ID until it is dealt with, in case the + * daemon arrives later. + */ + g_request.request.request_id = g_request.request_id; + g_request.request.command = command; + if (uuid) + memcpy(&g_request.request.uuid, uuid, sizeof(*uuid)); + else + memset(&g_request.request.uuid, 0, sizeof(*uuid)); + + g_request.request.is_gp = is_gp; + g_request.request.spid = spid; + g_request.client_state = REQUEST_SENT; + mutex_unlock(&g_request.states_mutex); + + /* Send request */ + complete(&g_request.client_complete); + mc_dev_devel("request sent"); + + /* Wait for header */ + do { + ret = wait_for_completion_interruptible( + &g_request.server_complete); + if (!ret) + break; + /* we may have to freeze now */ + check_freezing_ongoing(); + /* freezing happened or was canceled, + * let's sleep and try again + */ + msleep(500); + } while (1); + mc_dev_devel("response received"); + + /* Server should be waiting with some data for us */ + mutex_lock(&g_request.states_mutex); + switch (g_request.server_state) { + case NOT_CONNECTED: + /* Daemon gone */ + ret = -EPIPE; + mc_dev_devel("daemon disconnected"); + break; + case READY: + /* No data to come, likely an error */ + ret = -g_request.response.error_no; + mc_dev_devel("daemon ret=%d", ret); + break; + case RESPONSE_SENT: + case DATA_SENT: + /* Normal case, data to come */ + ret = 0; + break; + case REQUEST_RECEIVED: + /* Should not happen as complete means the state changed */ + ret = -EPIPE; + mc_dev_err(ret, "daemon is in a bad state: %d", + g_request.server_state); + break; + } + + mutex_unlock(&g_request.states_mutex); + +end: + if (ret) + request_cancel(); + + mc_dev_devel("ret=%d", ret); + return ret; +} + +static int request_receive(void *address, u32 size) +{ + /* + * At this point we have received the header and prepared some buffers + * to receive data that we know are coming from the server. + */ + + /* Check server state */ + bool server_ok; + + mutex_lock(&g_request.states_mutex); + server_ok = (g_request.server_state == RESPONSE_SENT) || + (g_request.server_state == DATA_SENT); + mutex_unlock(&g_request.states_mutex); + if (!server_ok) { + int ret = -EPIPE; + + mc_dev_err(ret, "expected server state %d or %d, not %d", + RESPONSE_SENT, DATA_SENT, g_request.server_state); + request_cancel(); + return ret; + } + + /* Setup reception buffer */ + g_request.buffer = address; + g_request.size = size; + client_state_change(BUFFERS_READY); + + /* Unlock write of data */ + complete(&g_request.client_complete); + + /* Wait for data */ + do { + int ret = 0; + + ret = wait_for_completion_interruptible( + &g_request.server_complete); + if (!ret) + break; + /* We may have to freeze now */ + check_freezing_ongoing(); + /* freezing happened or was canceled, + * let's sleep and try again + */ + msleep(500); + } while (1); + + /* Reset reception buffer */ + g_request.buffer = NULL; + g_request.size = 0; + + /* Return to idle state */ + client_state_change(IDLE); + return 0; +} + +/* Must be called instead of request_receive() to cancel a pending request */ +static void request_cancel(void) +{ + /* Unlock write of data */ + mutex_lock(&g_request.states_mutex); + if (g_request.server_state == DATA_SENT) + complete(&g_request.client_complete); + + /* Return to idle state */ + g_request.client_state = IDLE; + mutex_unlock(&g_request.states_mutex); +} + +static int admin_get_root_container(void *address) +{ + int ret = 0; + + /* Lock communication channel */ + channel_lock(); + + /* Send request and wait for header */ + ret = request_send(MC_DRV_GET_ROOT_CONTAINER, NULL, 0, 0); + if (ret) + goto end; + + /* Check length against max */ + if (g_request.response.length >= MAX_SO_CONT_SIZE) { + request_cancel(); + ret = EREMOTEIO; + mc_dev_err(ret, "response length exceeds maximum"); + goto end; + } + + /* Get data */ + ret = request_receive(address, g_request.response.length); + if (!ret) + ret = g_request.response.length; + +end: + channel_unlock(); + return ret; +} + +static int admin_get_sp_container(void *address, u32 spid) +{ + int ret = 0; + + /* Lock communication channel */ + channel_lock(); + + /* Send request and wait for header */ + ret = request_send(MC_DRV_GET_SP_CONTAINER, NULL, 0, spid); + if (ret) + goto end; + + /* Check length against max */ + if (g_request.response.length >= MAX_SO_CONT_SIZE) { + request_cancel(); + ret = EREMOTEIO; + mc_dev_err(ret, "response length exceeds maximum"); + goto end; + } + + /* Get data */ + ret = request_receive(address, g_request.response.length); + if (!ret) + ret = g_request.response.length; + +end: + channel_unlock(); + return ret; +} + +static int admin_get_trustlet_container(void *address, + const struct mc_uuid_t *uuid, u32 spid) +{ + int ret = 0; + + /* Lock communication channel */ + channel_lock(); + + /* Send request and wait for header */ + ret = request_send(MC_DRV_GET_TRUSTLET_CONTAINER, uuid, 0, spid); + if (ret) + goto end; + + /* Check length against max */ + if (g_request.response.length >= MAX_SO_CONT_SIZE) { + request_cancel(); + ret = EREMOTEIO; + mc_dev_err(ret, "response length exceeds maximum"); + goto end; + } + + /* Get data */ + ret = request_receive(address, g_request.response.length); + if (!ret) + ret = g_request.response.length; + +end: + channel_unlock(); + return ret; +} + +static struct tee_object *admin_get_trustlet(const struct mc_uuid_t *uuid, + bool is_gp, u32 *spid) +{ + struct tee_object *obj = NULL; + bool is_sp_tl; + int ret = 0; + + /* Lock communication channel */ + channel_lock(); + + /* Send request and wait for header */ + ret = request_send(MC_DRV_GET_TRUSTLET, uuid, is_gp, 0); + if (ret) + goto end; + + /* Allocate memory */ + is_sp_tl = g_request.response.service_type == SERVICE_TYPE_SP_TRUSTLET; + obj = tee_object_alloc(is_sp_tl, g_request.response.length); + if (!obj) { + request_cancel(); + ret = -ENOMEM; + goto end; + } + + /* Get data */ + ret = request_receive(&obj->data[obj->header_length], obj->length); + *spid = g_request.response.spid; + +end: + channel_unlock(); + if (ret) + return ERR_PTR(ret); + + return obj; +} + +static void mc_admin_sendcrashdump(void) +{ + int ret = 0; + + /* Lock communication channel */ + channel_lock(); + + /* Send request and wait for header */ + ret = request_send(MC_DRV_SIGNAL_CRASH, NULL, false, 0); + if (ret) + goto end; + + /* Done */ + request_cancel(); + +end: + channel_unlock(); +} + +static int tee_stop_notifier_fn(struct notifier_block *nb, unsigned long event, + void *data) +{ + mc_admin_sendcrashdump(); + l_ctx.last_tee_ret = -EHOSTUNREACH; + return 0; +} + +static int tee_object_make(u32 spid, struct tee_object *obj) +{ + struct mc_blob_len_info *l_info = (struct mc_blob_len_info *)obj->data; + u8 *address = &obj->data[obj->header_length + obj->length]; + struct mclf_header_v2 *thdr; + int ret; + + /* Get root container */ + ret = admin_get_root_container(address); + if (ret < 0) + goto err; + + l_info->root_size = ret; + address += ret; + + /* Get SP container */ + ret = admin_get_sp_container(address, spid); + if (ret < 0) + goto err; + + l_info->sp_size = ret; + address += ret; + + /* Get trustlet container */ + thdr = (struct mclf_header_v2 *)&obj->data[obj->header_length]; + ret = admin_get_trustlet_container(address, &thdr->uuid, spid); + if (ret < 0) + goto err; + + l_info->ta_size = ret; + address += ret; + + /* Setup lengths information */ + l_info->magic = MC_TLBLOBLEN_MAGIC; + obj->length += sizeof(*l_info); + obj->length += l_info->root_size + l_info->sp_size + l_info->ta_size; + ret = 0; + +err: + return ret; +} + +struct tee_object *tee_object_copy(uintptr_t address, size_t length) +{ + struct tee_object *obj; + + /* Allocate memory */ + obj = tee_object_alloc(false, length); + if (!obj) + return ERR_PTR(-ENOMEM); + + /* Copy trustlet */ + memcpy(obj->data, (void *)address, length); + return obj; +} + +struct tee_object *tee_object_read(u32 spid, uintptr_t address, size_t length) +{ + char __user *addr = (char __user *)address; + struct tee_object *obj; + u8 *data; + struct mclf_header_v2 thdr; + int ret; + + /* Check length */ + if (length < sizeof(thdr)) { + ret = -EFAULT; + mc_dev_err(ret, "buffer shorter than header size"); + return ERR_PTR(ret); + } + + /* Read header */ + if (copy_from_user(&thdr, addr, sizeof(thdr))) { + ret = -EFAULT; + mc_dev_err(ret, "header: copy_from_user failed"); + return ERR_PTR(ret); + } + + /* Check header */ + if (thdr.intro.magic != MC_SERVICE_HEADER_MAGIC_BE && + thdr.intro.magic != MC_SERVICE_HEADER_MAGIC_LE) { + ret = -EINVAL; + mc_dev_err(ret, "header: invalid magic"); + return ERR_PTR(ret); + } + + /* Allocate memory */ + obj = tee_object_alloc(thdr.service_type == SERVICE_TYPE_SP_TRUSTLET, + length); + if (!obj) + return ERR_PTR(-ENOMEM); + + /* Copy header */ + data = &obj->data[obj->header_length]; + memcpy(data, &thdr, sizeof(thdr)); + /* Copy the rest of the data */ + data += sizeof(thdr); + if (copy_from_user(data, &addr[sizeof(thdr)], length - sizeof(thdr))) { + ret = -EFAULT; + mc_dev_err(ret, "data: copy_from_user failed"); + vfree(obj); + return ERR_PTR(ret); + } + + if (obj->header_length) { + ret = tee_object_make(spid, obj); + if (ret) { + vfree(obj); + return ERR_PTR(ret); + } + } + + return obj; +} + +struct tee_object *tee_object_select(const struct mc_uuid_t *uuid) +{ + struct tee_object *obj; + struct mclf_header_v2 *thdr; + + obj = tee_object_alloc(false, sizeof(*thdr)); + if (!obj) + return ERR_PTR(-ENOMEM); + + thdr = (struct mclf_header_v2 *)&obj->data[obj->header_length]; + memcpy(&thdr->uuid, uuid, sizeof(thdr->uuid)); + return obj; +} + +struct tee_object *tee_object_get(const struct mc_uuid_t *uuid, bool is_gp) +{ + struct tee_object *obj; + u32 spid = 0; + + /* admin_get_trustlet creates the right object based on service type */ + obj = admin_get_trustlet(uuid, is_gp, &spid); + if (IS_ERR(obj)) + return obj; + + /* SP trustlet: create full secure object with all containers */ + if (obj->header_length) { + int ret; + + /* Do not return EINVAL in this case as SPID was not found */ + if (!spid) { + vfree(obj); + return ERR_PTR(-ENOENT); + } + + ret = tee_object_make(spid, obj); + if (ret) { + vfree(obj); + return ERR_PTR(ret); + } + } + + return obj; +} + +static inline int load_driver(struct tee_client *client, + struct mc_admin_load_info *load_info) +{ + struct mcp_open_info info = { + .spid = load_info->spid, + .va = load_info->address, + .len = load_info->length, + .uuid = &load_info->uuid, + .tci_len = PAGE_SIZE, + .user = true, + }; + + u32 session_id = 0; + int ret; + + if (info.va) + info.type = TEE_MC_DRIVER; + else + info.type = TEE_MC_DRIVER_UUID; + + /* Create DCI in case it's needed */ + ret = client_cbuf_create(client, info.tci_len, &info.tci_va, NULL); + if (ret) + return ret; + + /* Open session */ + ret = client_mc_open_common(client, &info, &session_id); + if (!ret) + mc_dev_devel("driver loaded with session id %x", session_id); + + /* + * Always 'free' the buffer (will remain as long as used), never freed + * otherwise + */ + client_cbuf_free(client, info.tci_va); + + return ret; +} + +static inline int load_token(struct mc_admin_load_info *token) +{ + struct tee_mmu *mmu; + struct mcp_buffer_map map; + struct mc_ioctl_buffer buf; + int ret; + + buf.va = (uintptr_t)token->address; + buf.len = token->length; + buf.flags = MC_IO_MAP_INPUT; + mmu = tee_mmu_create(current->mm, &buf); + if (IS_ERR(mmu)) + return PTR_ERR(mmu); + + tee_mmu_buffer(mmu, &map); + ret = mcp_load_token(token->address, &map); + tee_mmu_put(mmu); + return ret; +} + +static inline int load_check(struct mc_admin_load_info *info) +{ + struct tee_object *obj; + struct tee_mmu *mmu; + struct mcp_buffer_map map; + struct mc_ioctl_buffer buf; + int ret; + + obj = tee_object_read(info->spid, info->address, info->length); + if (IS_ERR(obj)) + return PTR_ERR(obj); + + buf.va = (uintptr_t)obj->data; + buf.len = obj->length; + buf.flags = MC_IO_MAP_INPUT; + mmu = tee_mmu_create(NULL, &buf); + if (IS_ERR(mmu)) + return PTR_ERR(mmu); + + tee_mmu_buffer(mmu, &map); + ret = mcp_load_check(obj, &map); + tee_mmu_put(mmu); + return ret; +} + +static inline int load_key_so(struct mc_admin_load_info *key_so) +{ + struct tee_mmu *mmu; + struct mcp_buffer_map map; + struct mc_ioctl_buffer buf; + int ret; + + buf.va = (uintptr_t)key_so->address; + buf.len = key_so->length; + buf.flags = MC_IO_MAP_INPUT; + mmu = tee_mmu_create(current->mm, &buf); + if (IS_ERR(mmu)) + return PTR_ERR(mmu); + + tee_mmu_buffer(mmu, &map); + ret = mcp_load_key_so(key_so->address, &map); + tee_mmu_put(mmu); + return ret; +} + +static ssize_t admin_write(struct file *file, const char __user *user, + size_t len, loff_t *off) +{ + int ret; + + /* No offset allowed */ + if (*off) { + ret = -ECOMM; + mc_dev_err(ret, "offset not supported"); + g_request.response.error_no = EPIPE; + goto err; + } + + if (server_state_is(REQUEST_RECEIVED)) { + /* Check client state */ + if (!client_state_is(REQUEST_SENT)) { + ret = -EPIPE; + mc_dev_err(ret, "expected client state %d, not %d", + REQUEST_SENT, g_request.client_state); + g_request.response.error_no = EPIPE; + goto err; + } + + /* Receive response header */ + if (copy_from_user(&g_request.response, user, + sizeof(g_request.response))) { + ret = -ECOMM; + mc_dev_err(ret, "failed to get response from daemon"); + g_request.response.error_no = EPIPE; + goto err; + } + + /* Check request ID */ + if (g_request.request.request_id != + g_request.response.request_id) { + ret = -EBADE; + mc_dev_err(ret, "expected id %d, not %d", + g_request.request.request_id, + g_request.response.request_id); + g_request.response.error_no = EPIPE; + goto err; + } + + /* Response header is acceptable */ + ret = sizeof(g_request.response); + if (g_request.response.length) + server_state_change(RESPONSE_SENT); + else + server_state_change(READY); + + goto end; + } else if (server_state_is(RESPONSE_SENT)) { + /* Server is waiting */ + server_state_change(DATA_SENT); + + /* Get data */ + ret = wait_for_completion_interruptible( + &g_request.client_complete); + + /* Server received a signal, let see if it tries again */ + if (ret) { + server_state_change(RESPONSE_SENT); + return ret; + } + + /* Check client state */ + if (!client_state_is(BUFFERS_READY)) { + ret = -EPIPE; + mc_dev_err(ret, "expected client state %d, not %d", + BUFFERS_READY, g_request.client_state); + g_request.response.error_no = EPIPE; + goto err; + } + + /* We do not deal with several writes */ + if (len != g_request.size) + len = g_request.size; + + ret = copy_from_user(g_request.buffer, user, len); + if (ret) { + ret = -ECOMM; + mc_dev_err(ret, "failed to get data from daemon"); + g_request.response.error_no = EPIPE; + goto err; + } + + ret = len; + server_state_change(READY); + goto end; + } else { + ret = -ECOMM; + goto err; + } + +err: + server_state_change(READY); +end: + complete(&g_request.server_complete); + return ret; +} + +static ssize_t admin_read(struct file *file, char __user *user, size_t len, + loff_t *off) +{ + /* No offset allowed */ + if (*off) { + int ret = -ECOMM; + + mc_dev_err(ret, "offset not supported"); + return ret; + } + + return nq_get_stop_message(user, len); +} + +static long admin_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *uarg = (void __user *)arg; + int ret = -EINVAL; + + mc_dev_devel("%u from %s", _IOC_NR(cmd), current->comm); + + switch (cmd) { + case MC_ADMIN_IO_GET_DRIVER_REQUEST: { + /* Update TGID as it may change (when becoming a daemon) */ + if (l_ctx.admin_tgid != current->tgid) { + l_ctx.admin_tgid = current->tgid; + mc_dev_info("daemon PID changed to %d", + l_ctx.admin_tgid); + } + + /* Block until a request is available */ + server_state_change(READY); + ret = wait_for_completion_interruptible( + &g_request.client_complete); + if (ret) + /* Interrupted by signal */ + break; + + /* Check client state */ + if (!client_state_is(REQUEST_SENT)) { + ret = -EPIPE; + mc_dev_err(ret, "expected client state %d, not %d", + REQUEST_SENT, g_request.client_state); + g_request.response.error_no = EPIPE; + complete(&g_request.server_complete); + break; + } + + /* Send request (the driver request mutex is held) */ + ret = copy_to_user(uarg, &g_request.request, + sizeof(g_request.request)); + if (ret) { + server_state_change(READY); + complete(&g_request.server_complete); + ret = -EPROTO; + break; + } + + /* Now that the daemon got it, update the request ID */ + g_request.request_id++; + + server_state_change(REQUEST_RECEIVED); + break; + } + case MC_ADMIN_IO_GET_INFO: { + struct mc_admin_driver_info info; + + info.drv_version = MC_VERSION(MCDRVMODULEAPI_VERSION_MAJOR, + MCDRVMODULEAPI_VERSION_MINOR); + info.initial_cmd_id = g_request.request_id; + ret = copy_to_user(uarg, &info, sizeof(info)); + break; + } + case MC_ADMIN_IO_LOAD_DRIVER: { + struct tee_client *client = file->private_data; + struct mc_admin_load_info info; + + if (copy_from_user(&info, uarg, sizeof(info))) { + ret = -EFAULT; + break; + } + + /* Make sure we have a local client */ + if (!client) { + client = client_create(true); + /* Store client for future use/close */ + file->private_data = client; + } + + if (!client) { + ret = -ENOMEM; + break; + } + + ret = load_driver(client, &info); + break; + } + case MC_ADMIN_IO_LOAD_TOKEN: { + struct mc_admin_load_info info; + + if (copy_from_user(&info, uarg, sizeof(info))) { + ret = -EFAULT; + break; + } + + ret = load_token(&info); + break; + } + case MC_ADMIN_IO_LOAD_CHECK: { + struct mc_admin_load_info info; + + if (copy_from_user(&info, uarg, sizeof(info))) { + ret = -EFAULT; + break; + } + + ret = load_check(&info); + break; + } + case MC_ADMIN_IO_LOAD_KEY_SO: { + struct mc_admin_load_info info; + + if (copy_from_user(&info, uarg, sizeof(info))) { + ret = -EFAULT; + break; + } + + ret = load_key_so(&info); + break; + } + default: + ret = -ENOIOCTLCMD; + } + + return ret; +} + +/* + * mc_fd_release() - This function will be called from user space as close(...) + * The client data are freed and the associated memory pages are unreserved. + * + * @inode + * @file + * + * Returns 0 + */ +static int admin_release(struct inode *inode, struct file *file) +{ + /* Close client if any */ + if (file->private_data) + client_close((struct tee_client *)file->private_data); + + /* Requests from driver to daemon */ + mutex_lock(&g_request.states_mutex); + mc_dev_devel("server state changes from %d to %d", + g_request.server_state, NOT_CONNECTED); + g_request.server_state = NOT_CONNECTED; + /* A non-zero command indicates that a thread is waiting */ + if (g_request.client_state != IDLE) { + g_request.response.error_no = ESHUTDOWN; + complete(&g_request.server_complete); + } + mutex_unlock(&g_request.states_mutex); + mc_dev_info("daemon connection closed, TGID %d", l_ctx.admin_tgid); + l_ctx.admin_tgid = 0; + + /* + * ret is quite irrelevant here as most apps don't care about the + * return value from close() and it's quite difficult to recover + */ + return 0; +} + +static int admin_open(struct inode *inode, struct file *file) +{ + int ret = 0; + + /* Only one connection allowed to admin interface */ + mutex_lock(&l_ctx.admin_tgid_mutex); + if (l_ctx.admin_tgid) { + ret = -EBUSY; + mc_dev_err(ret, "daemon connection already open, PID %d", + l_ctx.admin_tgid); + } else { + l_ctx.admin_tgid = current->tgid; + } + mutex_unlock(&l_ctx.admin_tgid_mutex); + if (ret) + return ret; + + /* Setup the usual variables */ + mc_dev_devel("accept %s as daemon", current->comm); + + /* + * daemon is connected so now we can safely suppose + * the secure world is loaded too + */ + if (l_ctx.last_tee_ret == TEE_START_NOT_TRIGGERED) + l_ctx.last_tee_ret = l_ctx.tee_start_cb(); + + /* Failed to start the TEE, either now or before */ + if (l_ctx.last_tee_ret) { + mutex_lock(&l_ctx.admin_tgid_mutex); + l_ctx.admin_tgid = 0; + mutex_unlock(&l_ctx.admin_tgid_mutex); + return l_ctx.last_tee_ret; + } + + reinit_completion_local(&g_request.client_complete); + reinit_completion_local(&g_request.server_complete); + /* Requests from driver to daemon */ + mc_dev_info("daemon connection open, TGID %d", l_ctx.admin_tgid); + return 0; +} + +/* function table structure of this device driver. */ +static const struct file_operations mc_admin_fops = { + .owner = THIS_MODULE, + .open = admin_open, + .release = admin_release, + .unlocked_ioctl = admin_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = admin_ioctl, +#endif + .write = admin_write, + .read = admin_read, +}; + +int mc_admin_init(struct cdev *cdev, int (*tee_start_cb)(void), + void (*tee_stop_cb)(void)) +{ + mutex_init(&l_ctx.admin_tgid_mutex); + /* Requests from driver to daemon */ + mutex_init(&g_request.mutex); + mutex_init(&g_request.states_mutex); + g_request.request_id = 42; + init_completion(&g_request.client_complete); + init_completion(&g_request.server_complete); + l_ctx.tee_stop_notifier.notifier_call = tee_stop_notifier_fn; + nq_register_tee_stop_notifier(&l_ctx.tee_stop_notifier); + /* Create char device */ + cdev_init(cdev, &mc_admin_fops); + /* Register the call back for starting the secure world */ + l_ctx.tee_start_cb = tee_start_cb; + l_ctx.tee_stop_cb = tee_stop_cb; + l_ctx.last_tee_ret = TEE_START_NOT_TRIGGERED; + return 0; +} + +void mc_admin_exit(void) +{ + nq_unregister_tee_stop_notifier(&l_ctx.tee_stop_notifier); + if (!l_ctx.last_tee_ret) + l_ctx.tee_stop_cb(); +} diff --git a/drivers/gud/MobiCoreDriver/admin.h b/drivers/gud/MobiCoreDriver/admin.h new file mode 100644 index 000000000000..f08d59e9c3ea --- /dev/null +++ b/drivers/gud/MobiCoreDriver/admin.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef _MC_ADMIN_H_ +#define _MC_ADMIN_H_ + +struct cdev; +struct mc_uuid_t; +struct tee_object; + +int mc_admin_init(struct cdev *cdev, int (*tee_start_cb)(void), + void (*tee_stop_cb)(void)); +void mc_admin_exit(void); + +struct tee_object *tee_object_select(const struct mc_uuid_t *uuid); +struct tee_object *tee_object_get(const struct mc_uuid_t *uuid, bool is_gp); +struct tee_object *tee_object_copy(uintptr_t address, size_t length); +struct tee_object *tee_object_read(u32 spid, uintptr_t address, size_t length); +void tee_object_free(struct tee_object *object); + +#endif /* _MC_ADMIN_H_ */ diff --git a/drivers/gud/MobiCoreDriver/arm.h b/drivers/gud/MobiCoreDriver/arm.h new file mode 100644 index 000000000000..9e2f541c1934 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/arm.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ +#ifndef _MC_ARM_H_ +#define _MC_ARM_H_ + +#include "main.h" + +#ifdef CONFIG_ARM64 +static inline bool has_security_extensions(void) +{ + return true; +} + +static inline bool is_secure_mode(void) +{ + return false; +} +#else +/* + * ARM Trustzone specific masks and modes + * Vanilla Linux is unaware of TrustZone extension. + * I.e. arch/arm/include/asm/ptrace.h does not define monitor mode. + * Also TZ bits in cpuid are not defined, ARM port uses magic numbers, + * see arch/arm/kernel/setup.c + */ +#define ARM_MONITOR_MODE (0x16) /*(0b10110)*/ +#define ARM_SECURITY_EXTENSION_MASK (0x30) + +/* check if CPU supports the ARM TrustZone Security Extensions */ +static inline bool has_security_extensions(void) +{ + u32 fea = 0; + + asm volatile( + "mrc p15, 0, %[fea], cr0, cr1, 0" : + [fea]"=r" (fea)); + + mc_dev_devel("CPU Features: 0x%X", fea); + + /* + * If the CPU features ID has 0 for security features then the CPU + * doesn't support TrustZone at all! + */ + if ((fea & ARM_SECURITY_EXTENSION_MASK) == 0) + return false; + + return true; +} + +/* check if running in secure mode */ +static inline bool is_secure_mode(void) +{ + u32 cpsr = 0; + u32 nsacr = 0; + + asm volatile( + "mrc p15, 0, %[nsacr], cr1, cr1, 2\n" + "mrs %[cpsr], cpsr\n" : + [nsacr]"=r" (nsacr), + [cpsr]"=r"(cpsr)); + + mc_dev_devel("CPRS.M = set to 0x%X", cpsr & MODE_MASK); + mc_dev_devel("SCR.NS = set to 0x%X", nsacr); + + /* + * If the NSACR contains the reset value(=0) then most likely we are + * running in Secure MODE. + * If the cpsr mode is set to monitor mode then we cannot load! + */ + if (nsacr == 0 || ((cpsr & MODE_MASK) == ARM_MONITOR_MODE)) + return true; + + return false; +} +#endif + +#endif /* _MC_ARM_H_ */ diff --git a/drivers/gud/MobiCoreDriver/build_tag.h b/drivers/gud/MobiCoreDriver/build_tag.h new file mode 100644 index 000000000000..4440ec68b652 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/build_tag.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2018 TRUSTONIC LIMITED + * 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 + * 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. + */ +#ifndef MOBICORE_COMPONENT_BUILD_TAG +#define MOBICORE_COMPONENT_BUILD_TAG \ + "t-base-QC8996-Android-410a-V108-20211010_233907_31049_105441" +#endif diff --git a/drivers/gud/MobiCoreDriver/client.c b/drivers/gud/MobiCoreDriver/client.c new file mode 100644 index 000000000000..73d168c2df82 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/client.c @@ -0,0 +1,1486 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2018 TRUSTONIC LIMITED + * 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 + * 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. + */ +#include +#include +#include +#include +#include +#include /* struct task_struct */ +#include +#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE +#include /* get_task_mm */ +#include /* put_task_struct */ +#endif +#include /* sockfd_lookup */ +#include /* fput */ + +#include "mc_user.h" +#include "mc_admin.h" + +#include "main.h" +#include "mmu.h" +#include "session.h" +#include "client.h" + +/* Client/context */ +struct tee_client { + /* PID of task that opened the device, 0 if kernel */ + pid_t pid; + /* Command for task*/ + char comm[TASK_COMM_LEN]; + /* Number of references kept to this object */ + struct kref kref; + /* List of contiguous buffers allocated by mcMallocWsm for the client */ + struct list_head cbufs; + struct mutex cbufs_lock; /* lock for the cbufs list */ + /* List of TA sessions opened by this client */ + struct list_head sessions; + struct list_head closing_sessions; + struct mutex sessions_lock; /* sessions list + closing */ + /* Client lock for quick WSMs and operations changes */ + struct mutex quick_lock; + /* Client lock for CWSMs release functions */ + struct mutex cwsm_release_lock; + /* List of WSMs for a client */ + struct list_head cwsms; + /* List of GP operation for a client */ + struct list_head operations; + /* The list entry to attach to "ctx.clients" list */ + struct list_head list; + /* task_struct for the client application, if going through a proxy */ + struct task_struct *task; +}; + +/* Context */ +static struct client_ctx { + /* Clients list */ + struct mutex clients_lock; + struct list_head clients; + /* Clients waiting for their last cbuf to be released */ + struct mutex closing_clients_lock; + struct list_head closing_clients; +} client_ctx; + +/* Buffer shared with SWd at client level */ +struct cwsm { + /* Client this cbuf belongs to */ + struct tee_client *client; + /* Buffer info */ + struct gp_shared_memory memref; + /* MMU L2 table */ + struct tee_mmu *mmu; + /* Buffer SWd addr */ + u32 sva; + /* Number of references kept to this object */ + struct kref kref; + /* The list entry for the client to list its WSMs */ + struct list_head list; +}; + +/* + * Contiguous buffer allocated to TLCs. + * These buffers are used as world shared memory (wsm) to share with + * secure world. + */ +struct cbuf { + /* Client this cbuf belongs to */ + struct tee_client *client; + /* List element for client's list of cbuf's */ + struct list_head list; + /* Number of references kept to this buffer */ + struct kref kref; + /* virtual Kernel start address */ + uintptr_t addr; + /* virtual Userspace start address */ + uintptr_t uaddr; + /* physical start address */ + phys_addr_t phys; + /* 2^order = number of pages allocated */ + unsigned int order; + /* Length of memory mapped to user */ + u32 len; + /* Has been freed via the API */ + bool api_freed; +}; + +static inline void cbuf_get(struct cbuf *cbuf) +{ + kref_get(&cbuf->kref); +} + +/* Must only be called by tee_cbuf_put */ +static void cbuf_release(struct kref *kref) +{ + struct cbuf *cbuf = container_of(kref, struct cbuf, kref); + struct tee_client *client = cbuf->client; + + /* Unlist from client */ + list_del_init(&cbuf->list); + /* Release client token */ + client_put(client); + /* Free */ + free_pages(cbuf->addr, cbuf->order); + mc_dev_devel("freed cbuf %p: client %p addr %lx uaddr %lx len %u", + cbuf, client, cbuf->addr, cbuf->uaddr, cbuf->len); + kfree(cbuf); + /* Decrement debug counter */ + atomic_dec(&g_ctx.c_cbufs); +} + +void tee_cbuf_put(struct cbuf *cbuf) +{ + struct tee_client *client = cbuf->client; + + mutex_lock(&client->cbufs_lock); + kref_put(&cbuf->kref, cbuf_release); + mutex_unlock(&client->cbufs_lock); +} + +/* + * Map a kernel contiguous buffer to user space + */ +static int cbuf_map(struct vm_area_struct *vmarea, uintptr_t addr, u32 len, + uintptr_t *uaddr) +{ + int ret; + + if (!uaddr) + return -EINVAL; + + if (!vmarea) + return -EINVAL; + + if (!addr) + return -EINVAL; + + if (len != (u32)(vmarea->vm_end - vmarea->vm_start)) { + ret = -EINVAL; + mc_dev_err(ret, "cbuf incompatible with vma"); + return ret; + } + + vmarea->vm_flags |= VM_IO; + ret = remap_pfn_range(vmarea, vmarea->vm_start, + page_to_pfn(virt_to_page(addr)), + vmarea->vm_end - vmarea->vm_start, + vmarea->vm_page_prot); + if (ret) { + *uaddr = 0; + mc_dev_err(ret, "User mapping failed"); + return ret; + } + + *uaddr = vmarea->vm_start; + return 0; +} + +/* + * Returns true if client is a kernel object. + */ +static inline bool client_is_kernel(struct tee_client *client) +{ + return !client->pid; +} + +static struct cwsm *cwsm_create(struct tee_client *client, + struct tee_mmu *mmu, + const struct gp_shared_memory *memref, + struct gp_return *gp_ret) +{ + struct cwsm *cwsm; + u32 sva; + int ret; + + cwsm = kzalloc(sizeof(*cwsm), GFP_KERNEL); + if (!cwsm) + return ERR_PTR(iwp_set_ret(-ENOMEM, gp_ret)); + + if (mmu) { + cwsm->mmu = mmu; + tee_mmu_get(cwsm->mmu); + } else { + struct mc_ioctl_buffer buf = { + .va = (uintptr_t)memref->buffer, + .len = memref->size, + .flags = memref->flags, + }; + + if (client_is_kernel(client)) { + cwsm->mmu = tee_mmu_create(NULL, &buf); + } else { + struct mm_struct *mm = get_task_mm(current); + + if (!mm) { + ret = -EPERM; + mc_dev_err(ret, "can't get mm"); + goto err_cwsm; + } + + /* Build MMU table for buffer */ + cwsm->mmu = tee_mmu_create(mm, &buf); + mmput(mm); + } + + if (IS_ERR(cwsm->mmu)) { + ret = iwp_set_ret(PTR_ERR(cwsm->mmu), gp_ret); + goto err_cwsm; + } + } + + ret = iwp_register_shared_mem(cwsm->mmu, &sva, gp_ret); + if (ret) + goto err_mmu; + + /* Get a token on the client */ + client_get(client); + cwsm->client = client; + memcpy(&cwsm->memref, memref, sizeof(cwsm->memref)); + cwsm->sva = sva; + kref_init(&cwsm->kref); + INIT_LIST_HEAD(&cwsm->list); + /* Add buffer to list */ + mutex_lock(&client->quick_lock); + list_add_tail(&cwsm->list, &client->cwsms); + mutex_unlock(&client->quick_lock); + mc_dev_devel("created cwsm %p: client %p sva %x", cwsm, client, sva); + /* Increment debug counter */ + atomic_inc(&g_ctx.c_cwsms); + return cwsm; + +err_mmu: + tee_mmu_put(cwsm->mmu); +err_cwsm: + kfree(cwsm); + return ERR_PTR(ret); +} + +static inline void cwsm_get(struct cwsm *cwsm) +{ + kref_get(&cwsm->kref); +} + +/* Must only be called by cwsm_put */ +static void cwsm_release(struct kref *kref) +{ + struct cwsm *cwsm = container_of(kref, struct cwsm, kref); + struct tee_client *client = cwsm->client; + struct mcp_buffer_map map; + + /* Unlist from client */ + list_del_init(&cwsm->list); + /* Unmap buffer from SWd (errors ignored) */ + tee_mmu_buffer(cwsm->mmu, &map); + map.secure_va = cwsm->sva; + iwp_release_shared_mem(&map); + /* Release MMU */ + tee_mmu_put(cwsm->mmu); + /* Release client token */ + client_put(client); + /* Free */ + mc_dev_devel("freed cwsm %p: client %p", cwsm, client); + kfree(cwsm); + /* Decrement debug counter */ + atomic_dec(&g_ctx.c_cwsms); +} + +static inline void cwsm_put(struct cwsm *cwsm) +{ + struct tee_client *client = cwsm->client; + + mutex_lock(&client->quick_lock); + kref_put(&cwsm->kref, cwsm_release); + mutex_unlock(&client->quick_lock); +} + +static inline struct cwsm *cwsm_find(struct tee_client *client, + const struct gp_shared_memory *memref) +{ + struct cwsm *cwsm = NULL, *candidate; + + mc_dev_devel("find shared mem for buf %llx size %llu flags %x", + memref->buffer, memref->size, memref->flags); + mutex_lock(&client->quick_lock); + list_for_each_entry(candidate, &client->cwsms, list) { + mc_dev_devel("candidate buf %llx size %llu flags %x", + candidate->memref.buffer, candidate->memref.size, + candidate->memref.flags); + if (candidate->memref.buffer == memref->buffer && + candidate->memref.size == memref->size && + candidate->memref.flags == memref->flags) { + cwsm = candidate; + cwsm_get(cwsm); + mc_dev_devel("match"); + break; + } + } + mutex_unlock(&client->quick_lock); + return cwsm; +} + +static inline struct cwsm *cwsm_find_by_sva(struct tee_client *client, u32 sva) +{ + struct cwsm *cwsm = NULL, *candidate; + + mutex_lock(&client->quick_lock); + list_for_each_entry(candidate, &client->cwsms, list) + if (candidate->sva == sva) { + cwsm = candidate; + cwsm_get(cwsm); + break; + } + mutex_unlock(&client->quick_lock); + return cwsm; +} + +/* + * Returns the secure virtual address from a registered mem + */ +u32 client_get_cwsm_sva(struct tee_client *client, + const struct gp_shared_memory *memref) +{ + struct cwsm *cwsm = cwsm_find(client, memref); + + if (!cwsm) + return 0; + + mc_dev_devel("found sva %x", cwsm->sva); + return cwsm->sva; +} + +void client_get(struct tee_client *client) +{ + kref_get(&client->kref); +} + +void client_put_cwsm_sva(struct tee_client *client, u32 sva) +{ + struct cwsm *cwsm; + + mutex_lock(&client->cwsm_release_lock); + cwsm = cwsm_find_by_sva(client, sva); + if (!cwsm) + goto end; + + /* Release reference taken by cwsm_find_by_sva */ + cwsm_put(cwsm); + cwsm_put(cwsm); +end: + mutex_unlock(&client->cwsm_release_lock); +} + +/* + * Allocate and initialize a client object + */ +struct tee_client *client_create(bool is_from_kernel) +{ + struct tee_client *client; + + /* Allocate client structure */ + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return NULL; + + /* Increment debug counter */ + atomic_inc(&g_ctx.c_clients); + /* initialize members */ + client->pid = is_from_kernel ? 0 : current->pid; + memcpy(client->comm, current->comm, sizeof(client->comm)); + kref_init(&client->kref); + INIT_LIST_HEAD(&client->cbufs); + mutex_init(&client->cbufs_lock); + INIT_LIST_HEAD(&client->sessions); + INIT_LIST_HEAD(&client->closing_sessions); + mutex_init(&client->sessions_lock); + INIT_LIST_HEAD(&client->list); + mutex_init(&client->quick_lock); + mutex_init(&client->cwsm_release_lock); + INIT_LIST_HEAD(&client->cwsms); + INIT_LIST_HEAD(&client->operations); + /* Add client to list of clients */ + mutex_lock(&client_ctx.clients_lock); + list_add_tail(&client->list, &client_ctx.clients); + mutex_unlock(&client_ctx.clients_lock); + mc_dev_devel("created client %p", client); + return client; +} + +/* Must only be called by client_put */ +static void client_release(struct kref *kref) +{ + struct tee_client *client; + + client = container_of(kref, struct tee_client, kref); + /* Client is closed, remove from closing list */ + list_del(&client->list); + mc_dev_devel("freed client %p", client); + if (client->task) + put_task_struct(client->task); + + kfree(client); + /* Decrement debug counter */ + atomic_dec(&g_ctx.c_clients); +} + +int client_put(struct tee_client *client) +{ + int ret; + + mutex_lock(&client_ctx.closing_clients_lock); + ret = kref_put(&client->kref, client_release); + mutex_unlock(&client_ctx.closing_clients_lock); + return ret; +} + +/* + * Set client "closing" state, only if it contains no session. + * Once in "closing" state, system "close" can be called. + * Return: 0 if this state could be set. + */ +bool client_has_sessions(struct tee_client *client) +{ + bool ret; + + /* Check for sessions */ + mutex_lock(&client->sessions_lock); + ret = !list_empty(&client->sessions); + mutex_unlock(&client->sessions_lock); + mc_dev_devel("client %p, exit with %d", client, ret); + return ret; +} + +static inline void client_put_session(struct tee_client *client, + struct tee_session *session) +{ + /* Remove session from client's closing list */ + mutex_lock(&client->sessions_lock); + list_del(&session->list); + mutex_unlock(&client->sessions_lock); + /* Release the ref we took on creation */ + session_put(session); +} + +/* + * At this point, nobody has access to the client anymore, so no new sessions + * are being created. + */ +static void client_close_sessions(struct tee_client *client) +{ + struct tee_session *session; + + mutex_lock(&client->sessions_lock); + while (!list_empty(&client->sessions)) { + session = list_first_entry(&client->sessions, + struct tee_session, list); + + /* Move session to closing sessions list */ + list_move(&session->list, &client->closing_sessions); + /* Call session_close without lock */ + mutex_unlock(&client->sessions_lock); + if (!session_close(session)) + client_put_session(client, session); + mutex_lock(&client->sessions_lock); + } + + mutex_unlock(&client->sessions_lock); +} + +/* + * At this point, nobody has access to the client anymore, so no new contiguous + * buffers are being created. + */ +static void client_close_kernel_cbufs(struct tee_client *client) +{ + /* Put buffers allocated and not freed via the kernel API */ + if (!client_is_kernel(client)) + return; + + /* Look for cbufs that the client has not freed and put them */ + while (true) { + struct cbuf *cbuf = NULL, *candidate; + + mutex_lock(&client->cbufs_lock); + list_for_each_entry(candidate, &client->cbufs, list) { + if (!candidate->api_freed) { + candidate->api_freed = true; + cbuf = candidate; + break; + } + } + mutex_unlock(&client->cbufs_lock); + + if (!cbuf) + break; + + tee_cbuf_put(cbuf); + } +} + +/* Client is closing: make sure all CSMs are gone */ +static void client_release_cwsms(struct tee_client *client) +{ + /* Look for cbufs that the client has not freed and put them */ + while (!list_empty(&client->cwsms)) { + struct cwsm *cwsm; + + cwsm = list_first_entry(&client->cwsms, struct cwsm, list); + cwsm_put(cwsm); + } +} + +/* Client is closing: make sure all cancelled operations are gone */ +static void client_release_gp_operations(struct tee_client *client) +{ + struct client_gp_operation *op, *nop; + + mutex_lock(&client->quick_lock); + list_for_each_entry_safe(op, nop, &client->operations, list) { + /* Only cancelled operations are kzalloc'd */ + mc_dev_devel("flush cancelled operation %p for started %llu", + op, op->started); + if (op->cancelled) + kfree(op); + } + mutex_unlock(&client->quick_lock); +} + +/* + * Release a client and the session+cbuf objects it contains. + * @param client_t client + * @return driver error code + */ +void client_close(struct tee_client *client) +{ + /* Move client from active clients to closing clients for debug */ + mutex_lock(&client_ctx.clients_lock); + mutex_lock(&client_ctx.closing_clients_lock); + list_move(&client->list, &client_ctx.closing_clients); + mutex_unlock(&client_ctx.closing_clients_lock); + mutex_unlock(&client_ctx.clients_lock); + client_close_kernel_cbufs(client); + /* Close all remaining sessions */ + client_close_sessions(client); + /* Release all cwsms, no need to lock as sessions are closed */ + client_release_cwsms(client); + client_release_gp_operations(client); + client_put(client); + mc_dev_devel("client %p closed", client); +} + +/* + * Clean all structures shared with the SWd (note: incomplete but unused) + */ +void client_cleanup(void) +{ + struct tee_client *client; + + mutex_lock(&client_ctx.clients_lock); + list_for_each_entry(client, &client_ctx.clients, list) { + mutex_lock(&client->sessions_lock); + while (!list_empty(&client->sessions)) { + struct tee_session *session; + + session = list_first_entry(&client->sessions, + struct tee_session, list); + list_del(&session->list); + session_mc_cleanup_session(session); + } + mutex_unlock(&client->sessions_lock); + } + mutex_unlock(&client_ctx.clients_lock); +} + +/* + * Open TA for given client. TA binary is provided by the daemon. + * @param + * @return driver error code + */ +int client_mc_open_session(struct tee_client *client, + const struct mc_uuid_t *uuid, + uintptr_t tci_va, size_t tci_len, u32 *session_id) +{ + struct mcp_open_info info = { + .type = TEE_MC_UUID, + .uuid = uuid, + .tci_va = tci_va, + .tci_len = tci_len, + .user = !client_is_kernel(client), + }; + int ret; + + ret = client_mc_open_common(client, &info, session_id); + mc_dev_devel("session %x, exit with %d", *session_id, ret); + return ret; +} + +/* + * Open TA for given client. TA binary is provided by the client. + * @param + * @return driver error code + */ +int client_mc_open_trustlet(struct tee_client *client, + u32 spid, uintptr_t ta_va, size_t ta_len, + uintptr_t tci_va, size_t tci_len, u32 *session_id) +{ + struct mcp_open_info info = { + .type = TEE_MC_TA, + .spid = spid, + .va = ta_va, + .len = ta_len, + .tci_va = tci_va, + .tci_len = tci_len, + .user = !client_is_kernel(client), + }; + int ret; + + ret = client_mc_open_common(client, &info, session_id); + mc_dev_devel("session %x, exit with %d", *session_id, ret); + return ret; +} + +/* + * Opens a TA and add corresponding session object to given client + * return: driver error code + */ +int client_mc_open_common(struct tee_client *client, struct mcp_open_info *info, + u32 *session_id) +{ + struct tee_session *session = NULL; + int ret = 0; + + /* + * Create session object with temp sid=0 BEFORE session is started, + * otherwise if a GP TA is started and NWd session object allocation + * fails, we cannot handle the potentially delayed GP closing. + * Adding session to list must be done AFTER it is started (once we have + * sid), therefore it cannot be done within session_create(). + */ + session = session_create(client, NULL); + if (IS_ERR(session)) + return PTR_ERR(session); + + ret = session_mc_open_session(session, info); + if (ret) + goto err; + + mutex_lock(&client->sessions_lock); + /* Add session to client */ + list_add_tail(&session->list, &client->sessions); + /* Set session ID returned by SWd */ + *session_id = session->mcp_session.sid; + mutex_unlock(&client->sessions_lock); + +err: + /* Close or free session on error */ + if (ret == -ENODEV) { + /* The session must enter the closing process... */ + list_add_tail(&session->list, &client->closing_sessions); + if (!session_close(session)) + client_put_session(client, session); + } else if (ret) { + session_put(session); + } + + return ret; +} + +/* + * Remove a session object from client and close corresponding TA + * Return: true if session was found and closed + */ +int client_remove_session(struct tee_client *client, u32 session_id) +{ + struct tee_session *session = NULL, *candidate; + int ret; + + /* Move session from main list to closing list */ + mutex_lock(&client->sessions_lock); + list_for_each_entry(candidate, &client->sessions, list) { + if (candidate->mcp_session.sid == session_id) { + session = candidate; + list_move(&session->list, &client->closing_sessions); + break; + } + } + + mutex_unlock(&client->sessions_lock); + if (!session) + return -ENXIO; + + /* Close session */ + ret = session_close(session); + if (!ret) + client_put_session(client, session); + + return ret; +} + +/* + * Find a session object and increment its reference counter. + * Object cannot be freed until its counter reaches 0. + * return: pointer to the object, NULL if not found. + */ +static struct tee_session *client_get_session(struct tee_client *client, + u32 session_id) +{ + struct tee_session *session = NULL, *candidate; + + mutex_lock(&client->sessions_lock); + list_for_each_entry(candidate, &client->sessions, list) { + if (candidate->mcp_session.sid == session_id) { + session = candidate; + session_get(session); + break; + } + } + + mutex_unlock(&client->sessions_lock); + if (!session) + mc_dev_err(-ENXIO, "session %x not found", session_id); + + return session; +} + +/* + * Send a notification to TA + * @return driver error code + */ +int client_notify_session(struct tee_client *client, u32 session_id) +{ + struct tee_session *session; + int ret; + + /* Find/get session */ + session = client_get_session(client, session_id); + if (!session) + return -ENXIO; + + /* Send command to SWd */ + ret = session_mc_notify(session); + /* Put session */ + session_put(session); + mc_dev_devel("session %x, exit with %d", session_id, ret); + return ret; +} + +/* + * Wait for a notification from TA + * @return driver error code + */ +int client_waitnotif_session(struct tee_client *client, u32 session_id, + s32 timeout, bool silent_expiry) +{ + struct tee_session *session; + int ret; + + /* Find/get session */ + session = client_get_session(client, session_id); + if (!session) + return -ENXIO; + + ret = session_mc_wait(session, timeout, silent_expiry); + /* Put session */ + session_put(session); + mc_dev_devel("session %x, exit with %d", session_id, ret); + return ret; +} + +/* + * Read session exit/termination code + */ +int client_get_session_exitcode(struct tee_client *client, u32 session_id, + s32 *err) +{ + struct tee_session *session; + int ret; + + /* Find/get session */ + session = client_get_session(client, session_id); + if (!session) + return -ENXIO; + + /* Retrieve error */ + ret = session_mc_get_err(session, err); + /* Put session */ + session_put(session); + mc_dev_devel("session %x, exit code %d", session_id, *err); + return ret; +} + +/* Share a buffer with given TA in SWd */ +int client_mc_map(struct tee_client *client, u32 session_id, + struct tee_mmu *mmu, struct mc_ioctl_buffer *buf) +{ + struct tee_session *session; + int ret; + + /* Find/get session */ + session = client_get_session(client, session_id); + if (!session) + return -ENXIO; + + /* Add buffer to the session */ + ret = session_mc_map(session, mmu, buf); + /* Put session */ + session_put(session); + mc_dev_devel("session %x, exit with %d", session_id, ret); + return ret; +} + +/* Stop sharing a buffer with SWd */ +int client_mc_unmap(struct tee_client *client, u32 session_id, + const struct mc_ioctl_buffer *buf) +{ + struct tee_session *session; + int ret; + + /* Find/get session */ + session = client_get_session(client, session_id); + if (!session) + return -ENXIO; + + /* Remove buffer from session */ + ret = session_mc_unmap(session, buf); + /* Put session */ + session_put(session); + mc_dev_devel("session %x, exit with %d", session_id, ret); + return ret; +} + +int client_gp_initialize_context(struct tee_client *client, + struct gp_return *gp_ret) +{ + return iwp_set_ret(0, gp_ret); +} + +int client_gp_register_shared_mem(struct tee_client *client, + struct tee_mmu *mmu, u32 *sva, + const struct gp_shared_memory *memref, + struct gp_return *gp_ret) +{ + struct cwsm *cwsm = NULL; + + if (memref->size > BUFFER_LENGTH_MAX) { + mc_dev_err(-EINVAL, "buffer size %llu too big", memref->size); + return -EINVAL; + } + + if (!mmu) + /* cwsm_find automatically takes a reference */ + cwsm = cwsm_find(client, memref); + + if (!cwsm) + cwsm = cwsm_create(client, mmu, memref, gp_ret); + + /* gp_ret set by callee */ + if (IS_ERR(cwsm)) + return PTR_ERR(cwsm); + + if (sva) + *sva = cwsm->sva; + + return iwp_set_ret(0, gp_ret); +} + +int client_gp_release_shared_mem(struct tee_client *client, + const struct gp_shared_memory *memref) +{ + struct cwsm *cwsm; + int ret = 0; + + mutex_lock(&client->cwsm_release_lock); + cwsm = cwsm_find(client, memref); + if (!cwsm) { + ret = -ENOENT; + goto end; + } + + /* Release reference taken by cwsm_find */ + cwsm_put(cwsm); + cwsm_put(cwsm); +end: + mutex_unlock(&client->cwsm_release_lock); + return ret; +} + +/* + * Opens a TA and add corresponding session object to given client + * return: driver error code + */ +int client_gp_open_session(struct tee_client *client, + const struct mc_uuid_t *uuid, + struct gp_operation *operation, + const struct mc_identity *identity, + struct gp_return *gp_ret, + u32 *session_id) +{ + struct tee_session *session = NULL; + int ret = 0; + + /* + * Create session object with temp sid=0 BEFORE session is started, + * otherwise if a GP TA is started and NWd session object allocation + * fails, we cannot handle the potentially delayed GP closing. + * Adding session to list must be done AFTER it is started (once we have + * sid), therefore it cannot be done within session_create(). + */ + session = session_create(client, identity); + if (IS_ERR(session)) + return iwp_set_ret(PTR_ERR(session), gp_ret); + + /* Open session */ + ret = session_gp_open_session(session, uuid, operation, gp_ret); + if (ret) + goto end; + + mutex_lock(&client->sessions_lock); + /* Add session to client */ + list_add_tail(&session->list, &client->sessions); + mutex_unlock(&client->sessions_lock); + /* Set sid returned by SWd */ + *session_id = session->iwp_session.sid; + +end: + if (ret) + session_put(session); + + mc_dev_devel("gp session %x, exit with %d", *session_id, ret); + return ret; +} + +int client_gp_open_session_domu(struct tee_client *client, + const struct mc_uuid_t *uuid, u64 started, + struct interworld_session *iws, + struct tee_mmu **mmus, + struct gp_return *gp_ret) +{ + struct tee_session *session = NULL; + int ret = 0; + + /* Don't pass NULL for identity as it would make a MC session */ + session = session_create(client, ERR_PTR(-ENOENT)); + if (IS_ERR(session)) + return iwp_set_ret(PTR_ERR(session), gp_ret); + + /* Open session */ + ret = session_gp_open_session_domu(session, uuid, started, iws, + mmus, gp_ret); + if (ret) + goto end; + + mutex_lock(&client->sessions_lock); + /* Add session to client */ + list_add_tail(&session->list, &client->sessions); + mutex_unlock(&client->sessions_lock); + +end: + if (ret) + session_put(session); + + mc_dev_devel("gp session %x, exit with %d", + session->iwp_session.sid, ret); + return ret; +} + +int client_gp_close_session(struct tee_client *client, u32 session_id) +{ + struct tee_session *session = NULL, *candidate; + int ret = 0; + + /* Move session from main list to closing list */ + mutex_lock(&client->sessions_lock); + list_for_each_entry(candidate, &client->sessions, list) { + if (candidate->iwp_session.sid == session_id) { + session = candidate; + list_move(&session->list, &client->closing_sessions); + break; + } + } + + mutex_unlock(&client->sessions_lock); + if (!session) + return -ENXIO; + + ret = session_close(session); + if (!ret) + client_put_session(client, session); + + return ret; +} + +/* + * Send a command to the TA + * @param + * @return driver error code + */ +int client_gp_invoke_command(struct tee_client *client, u32 session_id, + u32 command_id, + struct gp_operation *operation, + struct gp_return *gp_ret) +{ + struct tee_session *session; + int ret = 0; + + session = client_get_session(client, session_id); + if (!session) + return iwp_set_ret(-ENXIO, gp_ret); + + ret = session_gp_invoke_command(session, command_id, operation, gp_ret); + + /* Put session */ + session_put(session); + return ret; +} + +int client_gp_invoke_command_domu(struct tee_client *client, u32 session_id, + u64 started, struct interworld_session *iws, + struct tee_mmu **mmus, + struct gp_return *gp_ret) +{ + struct tee_session *session; + int ret = 0; + + session = client_get_session(client, session_id); + if (!session) + return iwp_set_ret(-ENXIO, gp_ret); + + ret = session_gp_invoke_command_domu(session, started, iws, mmus, + gp_ret); + + /* Put session */ + session_put(session); + return ret; +} + +void client_gp_request_cancellation(struct tee_client *client, u64 started) +{ + struct client_gp_operation *op; + u64 slot; + bool found = false; + + /* Look for operation */ + mutex_lock(&client->quick_lock); + list_for_each_entry(op, &client->operations, list) + if (op->started == started) { + slot = op->slot; + found = true; + mc_dev_devel( + "found no operation cancel for started %llu", + started); + break; + } + + /* Operation not found: assume it is coming */ + if (!found) { + op = kzalloc(sizeof(*op), GFP_KERNEL); + if (op) { + op->started = started; + op->cancelled = true; + list_add_tail(&op->list, &client->operations); + mc_dev_devel( + "add cancelled operation %p for started %llu", + op, op->started); + } + } + mutex_unlock(&client->quick_lock); + + if (found) + session_gp_request_cancellation(slot); +} + +/* + * This callback is called on remap + */ +static void cbuf_vm_open(struct vm_area_struct *vmarea) +{ + struct cbuf *cbuf = vmarea->vm_private_data; + + cbuf_get(cbuf); +} + +/* + * This callback is called on unmap + */ +static void cbuf_vm_close(struct vm_area_struct *vmarea) +{ + struct cbuf *cbuf = vmarea->vm_private_data; + + tee_cbuf_put(cbuf); +} + +static const struct vm_operations_struct cbuf_vm_ops = { + .open = cbuf_vm_open, + .close = cbuf_vm_close, +}; + +/* + * Create a cbuf object and add it to client + */ +int client_cbuf_create(struct tee_client *client, u32 len, uintptr_t *addr, + struct vm_area_struct *vmarea) +{ + struct cbuf *cbuf = NULL; + unsigned int order; + int ret = 0; + + if (!client) + return -EINVAL; + + if (!len) { + mc_dev_err(-EINVAL, "buffer size 0 not supported"); + return -EINVAL; + } + + if (len > BUFFER_LENGTH_MAX) { + mc_dev_err(-EINVAL, "buffer size %u too big", len); + return -EINVAL; + } + + order = get_order(len); + if (order > MAX_ORDER) { + ret = -ENOMEM; + mc_dev_err(ret, "Buffer size too large"); + return ret; + } + + /* Allocate buffer descriptor structure */ + cbuf = kzalloc(sizeof(*cbuf), GFP_KERNEL); + if (!cbuf) + return -ENOMEM; + + /* Increment debug counter */ + atomic_inc(&g_ctx.c_cbufs); + /* Allocate buffer */ + cbuf->addr = __get_free_pages(GFP_USER | __GFP_ZERO, order); + if (!cbuf->addr) { + kfree(cbuf); + /* Decrement debug counter */ + atomic_dec(&g_ctx.c_cbufs); + return -ENOMEM; + } + + /* Map to user space if applicable */ + if (!client_is_kernel(client)) { + ret = cbuf_map(vmarea, cbuf->addr, len, &cbuf->uaddr); + if (ret) { + free_pages(cbuf->addr, order); + kfree(cbuf); + /* Decrement debug counter */ + atomic_dec(&g_ctx.c_cbufs); + return ret; + } + } + + /* Init descriptor members */ + cbuf->client = client; + cbuf->phys = virt_to_phys((void *)cbuf->addr); + cbuf->len = len; + cbuf->order = order; + kref_init(&cbuf->kref); + INIT_LIST_HEAD(&cbuf->list); + + /* Keep cbuf in VMA private data for refcounting (user-space clients) */ + if (vmarea) { + vmarea->vm_private_data = cbuf; + vmarea->vm_ops = &cbuf_vm_ops; + } + + /* Fill return parameter for k-api */ + if (addr) + *addr = cbuf->addr; + + /* Get a token on the client */ + client_get(client); + + /* Add buffer to list */ + mutex_lock(&client->cbufs_lock); + list_add_tail(&cbuf->list, &client->cbufs); + mutex_unlock(&client->cbufs_lock); + mc_dev_devel("created cbuf %p: client %p addr %lx uaddr %lx len %u", + cbuf, client, cbuf->addr, cbuf->uaddr, cbuf->len); + return ret; +} + +/* + * Find a contiguous buffer (cbuf) in the cbuf list of given client that + * contains given address and take a reference on it. + * Return pointer to the object, or NULL if not found. + */ +static struct cbuf *cbuf_get_by_addr(struct tee_client *client, uintptr_t addr) +{ + struct cbuf *cbuf = NULL, *candidate; + bool is_kernel = client_is_kernel(client); + + mutex_lock(&client->cbufs_lock); + list_for_each_entry(candidate, &client->cbufs, list) { + /* Compare to kernel VA or user VA depending on client type */ + uintptr_t start = is_kernel ? + candidate->addr : candidate->uaddr; + uintptr_t end = start + candidate->len; + + /* Check that (user) cbuf has not been unmapped */ + if (!start) + break; + + if (addr >= start && addr < end) { + cbuf = candidate; + break; + } + } + + if (cbuf) + cbuf_get(cbuf); + + mutex_unlock(&client->cbufs_lock); + return cbuf; +} + +/* + * Remove a cbuf object from client, and mark it for freeing. + * Freeing will happen once all current references are released. + * + * Note: this function could be subject to the same race condition as + * client_gp_release_shared_mem() and client_put_cwsm_sva(), but it is trusted + * as it can only be called by kernel drivers. So no lock around + * cbuf_get_by_addr() and the two tee_cbuf_put(). + */ +int client_cbuf_free(struct tee_client *client, uintptr_t addr) +{ + struct cbuf *cbuf = cbuf_get_by_addr(client, addr); + + if (!cbuf) { + mc_dev_err(-EINVAL, "cbuf %lu not found", addr); + return -EINVAL; + } + + /* Release reference taken by cbuf_get_by_addr */ + tee_cbuf_put(cbuf); + mutex_lock(&client->cbufs_lock); + cbuf->api_freed = true; + mutex_unlock(&client->cbufs_lock); + tee_cbuf_put(cbuf); + return 0; +} + +bool client_gp_operation_add(struct tee_client *client, + struct client_gp_operation *operation) +{ + struct client_gp_operation *op; + bool found = false; + + mutex_lock(&client->quick_lock); + list_for_each_entry(op, &client->operations, list) + if (op->started == operation->started && op->cancelled) { + found = true; + break; + } + + if (found) { + list_del(&op->list); + mc_dev_devel("found cancelled operation %p for started %llu", + op, op->started); + kfree(op); + } else { + list_add_tail(&operation->list, &client->operations); + mc_dev_devel("add operation for started %llu", + operation->started); + } + mutex_unlock(&client->quick_lock); + return !found; +} + +void client_gp_operation_remove(struct tee_client *client, + struct client_gp_operation *operation) +{ + mutex_lock(&client->quick_lock); + list_del(&operation->list); + mutex_unlock(&client->quick_lock); +} + +struct tee_mmu *client_mmu_create(struct tee_client *client, + const struct mc_ioctl_buffer *buf_in, + struct cbuf **cbuf_p) +{ + /* Check if buffer is contained in a cbuf */ + struct mc_ioctl_buffer buf = *buf_in; + struct cbuf *cbuf = cbuf_get_by_addr(client, buf.va); + struct mm_struct *mm = NULL; + struct tee_mmu *mmu; + + *cbuf_p = cbuf; + if (cbuf) { + uintptr_t offset; + + if (client_is_kernel(client)) { + offset = buf.va - cbuf->addr; + } else { + offset = buf.va - cbuf->uaddr; + /* Update va to point to kernel address */ + buf.va = cbuf->addr + offset; + } + + if ((offset + buf.len) > cbuf->len) { + mc_dev_err(-EINVAL, "crosses cbuf boundary"); + tee_cbuf_put(cbuf); + return ERR_PTR(-EINVAL); + } + } else if (!client_is_kernel(client)) { + mm = get_task_mm(current); + if (!mm) { + mc_dev_err(-EPERM, "can't get mm"); + return ERR_PTR(-EPERM); + } + } + + /* Build MMU table for buffer */ + mmu = tee_mmu_create(mm, &buf); + if (mm) + mmput(mm); + + if (IS_ERR_OR_NULL(mmu) && cbuf) + tee_cbuf_put(cbuf); + + return mmu; +} + +void client_init(void) +{ + INIT_LIST_HEAD(&client_ctx.clients); + mutex_init(&client_ctx.clients_lock); + + INIT_LIST_HEAD(&client_ctx.closing_clients); + mutex_init(&client_ctx.closing_clients_lock); +} + +static inline int cbuf_debug_structs(struct kasnprintf_buf *buf, + struct cbuf *cbuf) +{ + return kasnprintf(buf, + "\tcbuf %pK [%d]: addr %pK uaddr %pK len %u\n", + cbuf, kref_read(&cbuf->kref), (void *)cbuf->addr, + (void *)cbuf->uaddr, cbuf->len); +} + +static inline int cwsm_debug_structs(struct kasnprintf_buf *buf, + struct cwsm *cwsm) +{ + return kasnprintf(buf, + "\tcwsm %pK [%d]: buf %pK len %llu flags 0x%x\n", + cwsm, kref_read(&cwsm->kref), + (void *)(uintptr_t)cwsm->memref.buffer, + cwsm->memref.size, cwsm->memref.flags); +} + +static int client_debug_structs(struct kasnprintf_buf *buf, + struct tee_client *client, bool is_closing) +{ + struct cbuf *cbuf; + struct cwsm *cwsm; + struct tee_session *session; + int ret; + + if (client->pid) + ret = kasnprintf(buf, "client %pK [%d]: %s (%d)%s\n", + client, kref_read(&client->kref), + client->comm, client->pid, + is_closing ? " " : ""); + else + ret = kasnprintf(buf, "client %pK [%d]: [kernel]%s\n", + client, kref_read(&client->kref), + is_closing ? " " : ""); + + if (ret < 0) + return ret; + + /* Buffers */ + mutex_lock(&client->cbufs_lock); + if (list_empty(&client->cbufs)) + goto done_cbufs; + + list_for_each_entry(cbuf, &client->cbufs, list) { + ret = cbuf_debug_structs(buf, cbuf); + if (ret < 0) + goto done_cbufs; + } + +done_cbufs: + mutex_unlock(&client->cbufs_lock); + if (ret < 0) + return ret; + + /* WMSs */ + mutex_lock(&client->quick_lock); + if (list_empty(&client->cwsms)) + goto done_cwsms; + + list_for_each_entry(cwsm, &client->cwsms, list) { + ret = cwsm_debug_structs(buf, cwsm); + if (ret < 0) + goto done_cwsms; + } + +done_cwsms: + mutex_unlock(&client->quick_lock); + if (ret < 0) + return ret; + + /* Sessions */ + mutex_lock(&client->sessions_lock); + list_for_each_entry(session, &client->sessions, list) { + ret = session_debug_structs(buf, session, false); + if (ret < 0) + goto done_sessions; + } + + list_for_each_entry(session, &client->closing_sessions, list) { + ret = session_debug_structs(buf, session, true); + if (ret < 0) + goto done_sessions; + } + +done_sessions: + mutex_unlock(&client->sessions_lock); + + if (ret < 0) + return ret; + + return 0; +} + +int clients_debug_structs(struct kasnprintf_buf *buf) +{ + struct tee_client *client; + ssize_t ret = 0; + + mutex_lock(&client_ctx.clients_lock); + list_for_each_entry(client, &client_ctx.clients, list) { + ret = client_debug_structs(buf, client, false); + if (ret < 0) + break; + } + mutex_unlock(&client_ctx.clients_lock); + + if (ret < 0) + return ret; + + mutex_lock(&client_ctx.closing_clients_lock); + list_for_each_entry(client, &client_ctx.closing_clients, list) { + ret = client_debug_structs(buf, client, true); + if (ret < 0) + break; + } + mutex_unlock(&client_ctx.closing_clients_lock); + + return ret; +} diff --git a/drivers/gud/MobiCoreDriver/client.h b/drivers/gud/MobiCoreDriver/client.h new file mode 100644 index 000000000000..8928f4a9e850 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/client.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2018 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef _CLIENT_H_ +#define _CLIENT_H_ + +#include +#include /* TASK_COMM_LEN */ + +#include "mc_user.h" /* many types */ + +struct tee_client; +struct mcp_open_info; +struct tee_mmu; +struct interworld_session; + +/* Client */ +struct tee_client *client_create(bool is_from_kernel); +void client_get(struct tee_client *client); +int client_put(struct tee_client *client); +bool client_has_sessions(struct tee_client *client); +void client_close(struct tee_client *client); +void client_cleanup(void); + +/* MC */ +int client_mc_open_session(struct tee_client *client, + const struct mc_uuid_t *uuid, + uintptr_t tci_va, size_t tci_len, u32 *session_id); +int client_mc_open_trustlet(struct tee_client *client, + u32 spid, uintptr_t ta_va, size_t ta_len, + uintptr_t tci_va, size_t tci_len, u32 *session_id); +int client_mc_open_common(struct tee_client *client, struct mcp_open_info *info, + u32 *session_id); +int client_remove_session(struct tee_client *client, u32 session_id); +int client_notify_session(struct tee_client *client, u32 session_id); +int client_waitnotif_session(struct tee_client *client, u32 session_id, + s32 timeout, bool silent_expiry); +int client_get_session_exitcode(struct tee_client *client, u32 session_id, + s32 *exit_code); +int client_mc_map(struct tee_client *client, u32 session_id, + struct tee_mmu *mmu, struct mc_ioctl_buffer *buf); +int client_mc_unmap(struct tee_client *client, u32 session_id, + const struct mc_ioctl_buffer *buf); + +/* GP */ +int client_gp_initialize_context(struct tee_client *client, + struct gp_return *gp_ret); +int client_gp_register_shared_mem(struct tee_client *client, + struct tee_mmu *mmu, u32 *sva, + const struct gp_shared_memory *memref, + struct gp_return *gp_ret); +int client_gp_release_shared_mem(struct tee_client *client, + const struct gp_shared_memory *memref); +int client_gp_open_session(struct tee_client *client, + const struct mc_uuid_t *uuid, + struct gp_operation *operation, + const struct mc_identity *identity, + struct gp_return *gp_ret, + u32 *session_id); +int client_gp_open_session_domu(struct tee_client *client, + const struct mc_uuid_t *uuid, u64 started, + struct interworld_session *iws, + struct tee_mmu **mmus, + struct gp_return *gp_ret); +int client_gp_close_session(struct tee_client *client, u32 session_id); +int client_gp_invoke_command(struct tee_client *client, u32 session_id, + u32 command_id, + struct gp_operation *operation, + struct gp_return *gp_ret); +int client_gp_invoke_command_domu(struct tee_client *client, u32 session_id, + u64 started, struct interworld_session *iws, + struct tee_mmu **mmus, + struct gp_return *gp_ret); +void client_gp_request_cancellation(struct tee_client *client, u64 started); + +/* Contiguous buffer */ +int client_cbuf_create(struct tee_client *client, u32 len, uintptr_t *addr, + struct vm_area_struct *vmarea); +int client_cbuf_free(struct tee_client *client, uintptr_t addr); + +/* GP internal */ +struct client_gp_operation { + struct list_head list; + u64 started; + u64 slot; + int cancelled; +}; + +/* Called from session when a new operation starts/ends */ +bool client_gp_operation_add(struct tee_client *client, + struct client_gp_operation *operation); +void client_gp_operation_remove(struct tee_client *client, + struct client_gp_operation *operation); + +/* MMU */ +struct cbuf; + +struct tee_mmu *client_mmu_create(struct tee_client *client, + const struct mc_ioctl_buffer *buf_in, + struct cbuf **cbuf_p); +void tee_cbuf_put(struct cbuf *cbuf); + +/* Buffer shared with SWd at client level */ +u32 client_get_cwsm_sva(struct tee_client *client, + const struct gp_shared_memory *memref); +void client_put_cwsm_sva(struct tee_client *client, u32 sva); + +/* Global */ +void client_init(void); + +/* Debug */ +int clients_debug_structs(struct kasnprintf_buf *buf); + +#endif /* _CLIENT_H_ */ diff --git a/drivers/gud/MobiCoreDriver/clientlib.c b/drivers/gud/MobiCoreDriver/clientlib.c new file mode 100644 index 000000000000..a1254bdf2a42 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/clientlib.c @@ -0,0 +1,439 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2018 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include "mc_user.h" +#include "mc_admin.h" +#include "mobicore_driver_api.h" + +#include "main.h" +#include "client.h" + +static enum mc_result convert(int err) +{ + switch (-err) { + case 0: + return MC_DRV_OK; + case ENOMSG: + return MC_DRV_NO_NOTIFICATION; + case EBADMSG: + return MC_DRV_ERR_NOTIFICATION; + case EAGAIN: + return MC_DRV_ERR_OUT_OF_RESOURCES; + case EHOSTDOWN: + return MC_DRV_ERR_INIT; + case ENODEV: + return MC_DRV_ERR_UNKNOWN_DEVICE; + case ENXIO: + return MC_DRV_ERR_UNKNOWN_SESSION; + case EPERM: + return MC_DRV_ERR_INVALID_OPERATION; + case EBADE: + return MC_DRV_ERR_INVALID_RESPONSE; + case ETIME: + return MC_DRV_ERR_TIMEOUT; + case ENOMEM: + return MC_DRV_ERR_NO_FREE_MEMORY; + case EUCLEAN: + return MC_DRV_ERR_FREE_MEMORY_FAILED; + case ENOTEMPTY: + return MC_DRV_ERR_SESSION_PENDING; + case EHOSTUNREACH: + return MC_DRV_ERR_DAEMON_UNREACHABLE; + case ENOENT: + return MC_DRV_ERR_INVALID_DEVICE_FILE; + case EINVAL: + return MC_DRV_ERR_INVALID_PARAMETER; + case EPROTO: + return MC_DRV_ERR_KERNEL_MODULE; + case ECOMM: + return MC_DRV_INFO_NOTIFICATION; + case EUNATCH: + return MC_DRV_ERR_NQ_FAILED; + case ERESTARTSYS: + return MC_DRV_ERR_INTERRUPTED_BY_SIGNAL; + default: + mc_dev_devel("error is %d", err); + return MC_DRV_ERR_UNKNOWN; + } +} + +static inline bool is_valid_device(u32 device_id) +{ + return device_id == MC_DEVICE_ID_DEFAULT; +} + +static struct tee_client *client; +static int open_count; +static DEFINE_MUTEX(dev_mutex); /* Lock for the device */ + +static bool clientlib_client_get(void) +{ + int ret = true; + + mutex_lock(&dev_mutex); + if (!client) + ret = false; + else + client_get(client); + + mutex_unlock(&dev_mutex); + return ret; +} + +static void clientlib_client_put(void) +{ + mutex_lock(&dev_mutex); + if (client_put(client)) + client = NULL; + + mutex_unlock(&dev_mutex); +} + +enum mc_result mc_open_device(u32 device_id) +{ + enum mc_result mc_result = MC_DRV_OK; + int ret; + + /* Check parameters */ + if (!is_valid_device(device_id)) + return MC_DRV_ERR_UNKNOWN_DEVICE; + + mutex_lock(&dev_mutex); + /* Make sure TEE was started */ + ret = mc_wait_tee_start(); + if (ret) { + mc_dev_err(ret, "TEE failed to start, now or in the past"); + mc_result = MC_DRV_ERR_INVALID_DEVICE_FILE; + goto end; + } + + if (!open_count) + client = client_create(true); + + if (client) { + open_count++; + mc_dev_devel("successfully opened the device"); + } else { + mc_result = MC_DRV_ERR_INVALID_DEVICE_FILE; + mc_dev_err(-ENOMEM, "could not open device"); + } + +end: + mutex_unlock(&dev_mutex); + return mc_result; +} +EXPORT_SYMBOL(mc_open_device); + +enum mc_result mc_close_device(u32 device_id) +{ + enum mc_result mc_result = MC_DRV_OK; + + /* Check parameters */ + if (!is_valid_device(device_id)) + return MC_DRV_ERR_UNKNOWN_DEVICE; + + if (!clientlib_client_get()) + return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN; + + mutex_lock(&dev_mutex); + if (open_count > 1) { + open_count--; + goto end; + } + + /* Check sessions and freeze client */ + if (client_has_sessions(client)) { + mc_result = MC_DRV_ERR_SESSION_PENDING; + goto end; + } + + /* Close the device */ + client_close(client); + open_count = 0; + +end: + mutex_unlock(&dev_mutex); + clientlib_client_put(); + return mc_result; +} +EXPORT_SYMBOL(mc_close_device); + +enum mc_result mc_open_session(struct mc_session_handle *session, + const struct mc_uuid_t *uuid, + u8 *tci_va, u32 tci_len) +{ + enum mc_result ret; + + /* Check parameters */ + if (!session || !uuid) + return MC_DRV_ERR_INVALID_PARAMETER; + + if (!is_valid_device(session->device_id)) + return MC_DRV_ERR_UNKNOWN_DEVICE; + + if (!clientlib_client_get()) + return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN; + + /* Call core api */ + ret = convert( + client_mc_open_session(client, uuid, (uintptr_t)tci_va, tci_len, + &session->session_id)); + clientlib_client_put(); + return ret; +} +EXPORT_SYMBOL(mc_open_session); + +enum mc_result mc_open_trustlet(struct mc_session_handle *session, u32 spid, + u8 *ta_va, u32 ta_len, u8 *tci_va, u32 tci_len) +{ + enum mc_result ret; + + /* Check parameters */ + if (!session || !ta_va || !ta_len) + return MC_DRV_ERR_INVALID_PARAMETER; + + if (!is_valid_device(session->device_id)) + return MC_DRV_ERR_UNKNOWN_DEVICE; + + if (!clientlib_client_get()) + return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN; + + /* Call core api */ + ret = convert( + client_mc_open_trustlet(client, spid, (uintptr_t)ta_va, ta_len, + (uintptr_t)tci_va, tci_len, + &session->session_id)); + clientlib_client_put(); + return ret; +} +EXPORT_SYMBOL(mc_open_trustlet); + +enum mc_result mc_close_session(struct mc_session_handle *session) +{ + enum mc_result ret; + + /* Check parameters */ + if (!session) + return MC_DRV_ERR_INVALID_PARAMETER; + + if (!is_valid_device(session->device_id)) + return MC_DRV_ERR_UNKNOWN_DEVICE; + + if (!clientlib_client_get()) + return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN; + + /* Call core api */ + ret = convert(client_remove_session(client, session->session_id)); + clientlib_client_put(); + return ret; +} +EXPORT_SYMBOL(mc_close_session); + +enum mc_result mc_notify(struct mc_session_handle *session) +{ + enum mc_result ret; + + /* Check parameters */ + if (!session) + return MC_DRV_ERR_INVALID_PARAMETER; + + if (!is_valid_device(session->device_id)) + return MC_DRV_ERR_UNKNOWN_DEVICE; + + if (!clientlib_client_get()) + return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN; + + /* Call core api */ + ret = convert(client_notify_session(client, session->session_id)); + clientlib_client_put(); + return ret; +} +EXPORT_SYMBOL(mc_notify); + +enum mc_result mc_wait_notification(struct mc_session_handle *session, + s32 timeout) +{ + enum mc_result ret; + + /* Check parameters */ + if (!session) + return MC_DRV_ERR_INVALID_PARAMETER; + + if (!is_valid_device(session->device_id)) + return MC_DRV_ERR_UNKNOWN_DEVICE; + + if (!clientlib_client_get()) + return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN; + + /* Call core api */ + do { + ret = convert(client_waitnotif_session(client, + session->session_id, + timeout, false)); + } while ((timeout == MC_INFINITE_TIMEOUT) && + (ret == MC_DRV_ERR_INTERRUPTED_BY_SIGNAL)); + + clientlib_client_put(); + return ret; +} +EXPORT_SYMBOL(mc_wait_notification); + +enum mc_result mc_malloc_wsm(u32 device_id, u32 align, u32 len, u8 **wsm, + u32 wsm_flags) +{ + enum mc_result ret; + uintptr_t va; + + /* Check parameters */ + if (!is_valid_device(device_id)) + return MC_DRV_ERR_UNKNOWN_DEVICE; + + if (!len) + return MC_DRV_ERR_INVALID_PARAMETER; + + if (!wsm) + return MC_DRV_ERR_INVALID_PARAMETER; + + if (!clientlib_client_get()) + return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN; + + /* Call core api */ + ret = convert(client_cbuf_create(client, len, &va, NULL)); + if (ret == MC_DRV_OK) + *wsm = (u8 *)va; + + clientlib_client_put(); + return ret; +} +EXPORT_SYMBOL(mc_malloc_wsm); + +enum mc_result mc_free_wsm(u32 device_id, u8 *wsm) +{ + enum mc_result ret; + uintptr_t va = (uintptr_t)wsm; + + /* Check parameters */ + if (!is_valid_device(device_id)) + return MC_DRV_ERR_UNKNOWN_DEVICE; + + if (!clientlib_client_get()) + return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN; + + /* Call core api */ + ret = convert(client_cbuf_free(client, va)); + clientlib_client_put(); + return ret; +} +EXPORT_SYMBOL(mc_free_wsm); + +enum mc_result mc_map(struct mc_session_handle *session, void *address, + u32 length, struct mc_bulk_map *map_info) +{ + enum mc_result ret; + struct mc_ioctl_buffer buf = { + .va = (uintptr_t)address, + .len = length, + .flags = MC_IO_MAP_INPUT_OUTPUT, + }; + + /* Check parameters */ + if (!session) + return MC_DRV_ERR_INVALID_PARAMETER; + + if (!is_valid_device(session->device_id)) + return MC_DRV_ERR_UNKNOWN_DEVICE; + + if (!map_info) + return MC_DRV_ERR_INVALID_PARAMETER; + + if (!clientlib_client_get()) + return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN; + + /* Call core api */ + ret = convert(client_mc_map(client, session->session_id, NULL, &buf)); + if (ret == MC_DRV_OK) { + map_info->secure_virt_addr = buf.sva; + map_info->secure_virt_len = buf.len; + } + + clientlib_client_put(); + return ret; +} +EXPORT_SYMBOL(mc_map); + +enum mc_result mc_unmap(struct mc_session_handle *session, void *address, + struct mc_bulk_map *map_info) +{ + enum mc_result ret; + struct mc_ioctl_buffer buf = { + .va = (uintptr_t)address, + .flags = MC_IO_MAP_INPUT_OUTPUT, + }; + + /* Check parameters */ + if (!session) + return MC_DRV_ERR_INVALID_PARAMETER; + + if (!is_valid_device(session->device_id)) + return MC_DRV_ERR_UNKNOWN_DEVICE; + + if (!map_info) + return MC_DRV_ERR_INVALID_PARAMETER; + + if (!clientlib_client_get()) + return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN; + + /* Call core api */ + buf.len = map_info->secure_virt_len; + buf.sva = map_info->secure_virt_addr; + + ret = convert(client_mc_unmap(client, session->session_id, &buf)); + clientlib_client_put(); + return ret; +} +EXPORT_SYMBOL(mc_unmap); + +enum mc_result mc_get_session_error_code(struct mc_session_handle *session, + s32 *exit_code) +{ + enum mc_result ret; + + /* Check parameters */ + if (!session) + return MC_DRV_ERR_INVALID_PARAMETER; + + if (!is_valid_device(session->device_id)) + return MC_DRV_ERR_UNKNOWN_DEVICE; + + if (!exit_code) + return MC_DRV_ERR_INVALID_PARAMETER; + + if (!clientlib_client_get()) + return MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN; + + /* Call core api */ + ret = convert(client_get_session_exitcode(client, session->session_id, + exit_code)); + clientlib_client_put(); + return ret; +} +EXPORT_SYMBOL(mc_get_session_error_code); diff --git a/drivers/gud/MobiCoreDriver/clock.c b/drivers/gud/MobiCoreDriver/clock.c new file mode 100644 index 000000000000..1b2e5772802a --- /dev/null +++ b/drivers/gud/MobiCoreDriver/clock.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2018 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#include "platform.h" + +#ifdef MC_CRYPTO_CLOCK_MANAGEMENT + +#include +#include +#include +#include + +#include "main.h" +#include "clock.h" + +static struct clk_context { + struct clk *mc_ce_iface_clk; + struct clk *mc_ce_core_clk; + struct clk *mc_ce_bus_clk; + struct clk *mc_ce_core_src_clk; + /* Clocks are managed by Linux Kernel. No need to do anything */ + bool no_clock_support; +} clk_ctx; + +int mc_clock_init(void) +{ + int ret; +#ifdef MC_CLOCK_CORESRC_DEFAULTRATE + int core_src_rate = MC_CLOCK_CORESRC_DEFAULTRATE; +#ifdef MC_CRYPTO_CLOCK_CORESRC_PROPNAME + u32 of_core_src_rate = MC_CLOCK_CORESRC_DEFAULTRATE; +#endif +#endif +#ifdef TT_CRYPTO_NO_CLOCK_SUPPORT_FEATURE + struct device_node *np; + + np = of_find_node_by_name(NULL, TT_CLOCK_DEVICE_NAME); + if (!np) { + ret = -ENOENT; + mc_dev_err(ret, "cannot get clock device from DT"); + goto error; + } + + clk_ctx.no_clock_support = + of_property_read_bool(np, TT_CRYPTO_NO_CLOCK_SUPPORT_FEATURE); + if (clk_ctx.no_clock_support) + return 0; +#endif /* TT_CRYPTO_NO_CLOCK_SUPPORT_FEATURE */ + +#ifdef MC_CLOCK_CORESRC_DEFAULTRATE +#ifdef MC_CRYPTO_CLOCK_CORESRC_PROPNAME + /* Get core clk src */ + clk_ctx.mc_ce_core_src_clk = clk_get(g_ctx.mcd, "core_clk_src"); + if (IS_ERR_OR_NULL(clk_ctx.mc_ce_core_src_clk)) { + ret = PTR_ERR(clk_ctx.mc_ce_core_src_clk); + mc_dev_err(ret, "cannot get core src clock"); + goto error; + } +#endif + +#ifdef MC_CRYPTO_CLOCK_CORESRC_PROPNAME + ret = of_property_read_u32(g_ctx.mcd->of_node, + MC_CRYPTO_CLOCK_CORESRC_PROPNAME, + &of_core_src_rate); + if (ret) { + core_src_rate = MC_CLOCK_CORESRC_DEFAULTRATE; + mc_dev_info("cannot get clock frequency from DT, use %d", + core_src_rate); + } else { + core_src_rate = of_core_src_rate; + } + +#endif /* MC_CRYPTO_CLOCK_CORESRC_PROPNAME */ + + ret = clk_set_rate(clk_ctx.mc_ce_core_src_clk, core_src_rate); + if (ret) { + clk_put(clk_ctx.mc_ce_core_src_clk); + clk_ctx.mc_ce_core_src_clk = NULL; + mc_dev_err(ret, "cannot set core clock src rate"); + ret = -EIO; + goto error; + } +#endif /* MC_CLOCK_CORESRC_DEFAULTRATE */ + + /* Get core clk */ + clk_ctx.mc_ce_core_clk = clk_get(g_ctx.mcd, "core_clk"); + if (IS_ERR(clk_ctx.mc_ce_core_clk)) { + ret = PTR_ERR(clk_ctx.mc_ce_core_clk); + mc_dev_err(ret, "cannot get core clock"); + goto error; + } + + /* Get Interface clk */ + clk_ctx.mc_ce_iface_clk = clk_get(g_ctx.mcd, "iface_clk"); + if (IS_ERR(clk_ctx.mc_ce_iface_clk)) { + clk_put(clk_ctx.mc_ce_core_clk); + ret = PTR_ERR(clk_ctx.mc_ce_iface_clk); + mc_dev_err(ret, "cannot get iface clock"); + goto error; + } + + /* Get AXI clk */ + clk_ctx.mc_ce_bus_clk = clk_get(g_ctx.mcd, "bus_clk"); + if (IS_ERR(clk_ctx.mc_ce_bus_clk)) { + clk_put(clk_ctx.mc_ce_iface_clk); + clk_put(clk_ctx.mc_ce_core_clk); + ret = PTR_ERR(clk_ctx.mc_ce_bus_clk); + mc_dev_err(ret, "cannot get AXI bus clock"); + goto error; + } + + return 0; + +error: + clk_ctx.mc_ce_core_clk = NULL; + clk_ctx.mc_ce_iface_clk = NULL; + clk_ctx.mc_ce_bus_clk = NULL; + clk_ctx.mc_ce_core_src_clk = NULL; + return ret; +} + +void mc_clock_exit(void) +{ + if (clk_ctx.no_clock_support) + return; + + if (clk_ctx.mc_ce_iface_clk) + clk_put(clk_ctx.mc_ce_iface_clk); + + if (clk_ctx.mc_ce_core_clk) + clk_put(clk_ctx.mc_ce_core_clk); + + if (clk_ctx.mc_ce_bus_clk) + clk_put(clk_ctx.mc_ce_bus_clk); + + if (clk_ctx.mc_ce_core_src_clk) + clk_put(clk_ctx.mc_ce_core_src_clk); +} + +int mc_clock_enable(void) +{ + int ret; + + if (clk_ctx.no_clock_support) + return 0; + + ret = clk_prepare_enable(clk_ctx.mc_ce_core_clk); + if (ret) { + mc_dev_err(ret, "cannot enable core clock"); + goto err_core; + } + + ret = clk_prepare_enable(clk_ctx.mc_ce_iface_clk); + if (ret) { + mc_dev_err(ret, "cannot enable interface clock"); + goto err_iface; + } + + ret = clk_prepare_enable(clk_ctx.mc_ce_bus_clk); + if (ret) { + mc_dev_err(ret, "cannot enable bus clock"); + goto err_bus; + } + + return 0; + +err_bus: + clk_disable_unprepare(clk_ctx.mc_ce_iface_clk); +err_iface: + clk_disable_unprepare(clk_ctx.mc_ce_core_clk); +err_core: + return ret; +} + +void mc_clock_disable(void) +{ + if (clk_ctx.no_clock_support) + return; + + if (clk_ctx.mc_ce_iface_clk) + clk_disable_unprepare(clk_ctx.mc_ce_iface_clk); + + if (clk_ctx.mc_ce_core_clk) + clk_disable_unprepare(clk_ctx.mc_ce_core_clk); + + if (clk_ctx.mc_ce_bus_clk) + clk_disable_unprepare(clk_ctx.mc_ce_bus_clk); +} + +#endif /* MC_CRYPTO_CLOCK_MANAGEMENT */ diff --git a/drivers/gud/MobiCoreDriver/clock.h b/drivers/gud/MobiCoreDriver/clock.h new file mode 100644 index 000000000000..b0b2cabb7b84 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/clock.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef _MC_CLOCK_H_ +#define _MC_CLOCK_H_ + +#include "platform.h" /* MC_CRYPTO_CLOCK_MANAGEMENT */ + +#ifdef MC_CRYPTO_CLOCK_MANAGEMENT + +/* Initialize secure crypto clocks */ +int mc_clock_init(void); +/* Free secure crypto clocks */ +void mc_clock_exit(void); +/* Enable secure crypto clocks */ +int mc_clock_enable(void); +/* Disable secure crypto clocks */ +void mc_clock_disable(void); + +#else /* MC_CRYPTO_CLOCK_MANAGEMENT */ + +static inline int mc_clock_init(void) +{ + return 0; +} + +static inline void mc_clock_exit(void) +{ +} + +static inline int mc_clock_enable(void) +{ + return 0; +} + +static inline void mc_clock_disable(void) +{ +} + +#endif /* !MC_CRYPTO_CLOCK_MANAGEMENT */ + +#endif /* _MC_CLOCK_H_ */ diff --git a/drivers/gud/MobiCoreDriver/fastcall.c b/drivers/gud/MobiCoreDriver/fastcall.c new file mode 100644 index 000000000000..ef0cd98acb81 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/fastcall.c @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2018 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#include +#include +#include /* local_clock */ +#include +#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE +#include /* local_clock */ +#endif + +#include "mcifc.h" + +#include "platform.h" /* MC_SMC_FASTCALL */ +#include "main.h" +#include "fastcall.h" + +/* Use the arch_extension sec pseudo op before switching to secure world */ +#if defined(__GNUC__) && \ + defined(__GNUC_MINOR__) && \ + defined(__GNUC_PATCHLEVEL__) && \ + ((__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)) \ + >= 40502 +#ifndef CONFIG_ARM64 +#define MC_ARCH_EXTENSION_SEC +#endif +#endif + +/* Base for all fastcalls, do not use outside of other structs */ +union fc_common { + struct { + u32 cmd; + u32 param[3]; + } in; + + struct { + u32 resp; + u32 ret; + u32 param[2]; + } out; +}; + +union fc_init { + union fc_common common; + + struct { + u32 cmd; + u32 base; + u32 nq_info; + u32 mcp_info; + } in; + + struct { + u32 resp; + u32 ret; + u32 flags; + u32 rfu; + } out; +}; + +union fc_info { + union fc_common common; + + struct { + u32 cmd; + u32 ext_info_id; + } in; + + struct { + u32 resp; + u32 ret; + u32 state; + u32 ext_info; + } out; +}; + +union fc_trace { + union fc_common common; + + struct { + u32 cmd; + u32 buffer_low; + u32 buffer_high; + u32 size; + } in; + + struct { + u32 resp; + u32 ret; + } out; +}; + +union fc_nsiq { + union fc_common common; + + struct { + u32 cmd; + u32 debug_ret; + u32 debug_session_id; + u32 debug_payload; + } in; + + struct { + u32 resp; + u32 ret; + } out; +}; + +union fc_yield { + union fc_common common; + + struct { + u32 cmd; + u32 debug_ret; + u32 debug_timeslice; + } in; + + struct { + u32 resp; + u32 ret; + } out; +}; + +/* Structure to log SMC calls */ +struct smc_log_entry { + u64 cpu_clk; + int cpu_id; + union fc_common fc; +}; + +#define SMC_LOG_SIZE 1024 +static struct smc_log_entry smc_log[SMC_LOG_SIZE]; +static int smc_log_index; + +/* + * convert fast call return code to linux driver module error code + */ +static int convert_fc_ret(u32 ret) +{ + switch (ret) { + case MC_FC_RET_OK: + return 0; + case MC_FC_RET_ERR_INVALID: + return -EINVAL; + case MC_FC_RET_ERR_ALREADY_INITIALIZED: + return -EBUSY; + default: + return -EFAULT; + } +} + +/* + * __smc() - fast call to MobiCore + * + * @data: pointer to fast call data + */ +static inline int __smc(union fc_common *fc, const char *func) +{ + int ret = 0; + + /* Log SMC call */ + smc_log[smc_log_index].cpu_clk = local_clock(); + smc_log[smc_log_index].cpu_id = raw_smp_processor_id(); + smc_log[smc_log_index].fc = *fc; + if (++smc_log_index >= SMC_LOG_SIZE) + smc_log_index = 0; + +#ifdef MC_SMC_FASTCALL + ret = smc_fastcall(fc, sizeof(*fc)); +#else /* MC_SMC_FASTCALL */ + { +#ifdef CONFIG_ARM64 + /* SMC expect values in x0-x3 */ + register u64 reg0 __asm__("x0") = fc->in.cmd; + register u64 reg1 __asm__("x1") = fc->in.param[0]; + register u64 reg2 __asm__("x2") = fc->in.param[1]; + register u64 reg3 __asm__("x3") = fc->in.param[2]; + + /* + * According to AARCH64 SMC Calling Convention (ARM DEN 0028A), + * section 3.1: registers x4-x17 are unpredictable/scratch + * registers. So we have to make sure that the compiler does + * not allocate any of those registers by letting him know that + * the asm code might clobber them. + */ + __asm__ volatile ( + "smc #0\n" + : "+r"(reg0), "+r"(reg1), "+r"(reg2), "+r"(reg3) + : + : "x4", "x5", "x6", "x7", "x8", "x9", "x10", "x11", + "x12", "x13", "x14", "x15", "x16", "x17" + ); +#else /* CONFIG_ARM64 */ + /* SMC expect values in r0-r3 */ + register u32 reg0 __asm__("r0") = fc->in.cmd; + register u32 reg1 __asm__("r1") = fc->in.param[0]; + register u32 reg2 __asm__("r2") = fc->in.param[1]; + register u32 reg3 __asm__("r3") = fc->in.param[2]; + + __asm__ volatile ( +#ifdef MC_ARCH_EXTENSION_SEC + /* + * This pseudo op is supported and required from + * binutils 2.21 on + */ + ".arch_extension sec\n" +#endif /* MC_ARCH_EXTENSION_SEC */ + "smc #0\n" + : "+r"(reg0), "+r"(reg1), "+r"(reg2), "+r"(reg3) + ); + +#endif /* !CONFIG_ARM64 */ + + /* set response */ + fc->out.resp = reg0; + fc->out.ret = reg1; + fc->out.param[0] = reg2; + fc->out.param[1] = reg3; + } +#endif /* !MC_SMC_FASTCALL */ + + if (ret) { + mc_dev_err(ret, "failed for %s", func); + } else { + ret = convert_fc_ret(fc->out.ret); + if (ret) + mc_dev_err(ret, "%s failed (%x)", func, fc->out.ret); + } + + return ret; +} + +#define smc(__fc__) __smc(__fc__.common, __func__) + +int fc_init(uintptr_t addr, ptrdiff_t off, size_t q_len, size_t buf_len) +{ + union fc_init fc; +#ifdef CONFIG_ARM64 + u32 addr_high = (u32)(addr >> 32); +#else + u32 addr_high = 0; +#endif + + /* Call the INIT fastcall to setup MobiCore initialization */ + memset(&fc, 0, sizeof(fc)); + fc.in.cmd = MC_FC_INIT; + /* base address of mci buffer PAGE_SIZE (default is 4KB) aligned */ + fc.in.base = (u32)addr; + /* notification buffer start/length [16:16] [start, length] */ + fc.in.nq_info = (u32)(((addr_high & 0xFFFF) << 16) | (q_len & 0xFFFF)); + /* mcp buffer start/length [16:16] [start, length] */ + fc.in.mcp_info = (u32)((off << 16) | (buf_len & 0xFFFF)); + mc_dev_devel("cmd=0x%08x, base=0x%08x, nq_info=0x%08x, mcp_info=0x%08x", + fc.in.cmd, fc.in.base, fc.in.nq_info, + fc.in.mcp_info); + return smc(&fc); +} + +int fc_info(u32 ext_info_id, u32 *state, u32 *ext_info) +{ + union fc_info fc; + int ret = 0; + + memset(&fc, 0, sizeof(fc)); + fc.in.cmd = MC_FC_INFO; + fc.in.ext_info_id = ext_info_id; + ret = smc(&fc); + if (ret) { + if (state) + *state = MC_STATUS_NOT_INITIALIZED; + + if (ext_info) + *ext_info = 0; + + mc_dev_err(ret, "failed for index %d", ext_info_id); + } else { + if (state) + *state = fc.out.state; + + if (ext_info) + *ext_info = fc.out.ext_info; + } + + return ret; +} + +int fc_trace_init(phys_addr_t buffer, u32 size) +{ + union fc_trace fc; + + memset(&fc, 0, sizeof(fc)); + fc.in.cmd = MC_FC_MEM_TRACE; + fc.in.buffer_low = (u32)buffer; +#ifdef CONFIG_ARM64 + fc.in.buffer_high = (u32)(buffer >> 32); +#endif + fc.in.size = size; + return smc(&fc); +} + +int fc_trace_deinit(void) +{ + return fc_trace_init(0, 0); +} + +/* sid, payload only used for debug purpose */ +int fc_nsiq(u32 session_id, u32 payload) +{ + union fc_nsiq fc; + + memset(&fc, 0, sizeof(fc)); + fc.in.cmd = MC_SMC_N_SIQ; + fc.in.debug_session_id = session_id; + fc.in.debug_payload = payload; + return smc(&fc); +} + +/* timeslice only used for debug purpose */ +int fc_yield(u32 timeslice) +{ + union fc_yield fc; + + memset(&fc, 0, sizeof(fc)); + fc.in.cmd = MC_SMC_N_YIELD; + fc.in.debug_timeslice = timeslice; + return smc(&fc); +} + +static int show_smc_log_entry(struct kasnprintf_buf *buf, + struct smc_log_entry *entry) +{ + return kasnprintf(buf, "%20llu %10d 0x%08x 0x%08x 0x%08x 0x%08x\n", + entry->cpu_clk, entry->cpu_id, entry->fc.in.cmd, + entry->fc.in.param[0], entry->fc.in.param[1], + entry->fc.in.param[2]); +} + +/* + * Dump SMC log circular buffer, starting from oldest command. It is assumed + * nothing goes in any more at this point. + */ +int mc_fastcall_debug_smclog(struct kasnprintf_buf *buf) +{ + int i, ret = 0; + + ret = kasnprintf(buf, "%10s %20s %10s %-10s %-10s %-10s\n", "CPU id", + "CPU clock", "command", "param1", "param2", "param3"); + if (ret < 0) + return ret; + + if (smc_log[smc_log_index].cpu_clk) + /* Buffer has wrapped around, dump end (oldest records) */ + for (i = smc_log_index; i < SMC_LOG_SIZE; i++) { + ret = show_smc_log_entry(buf, &smc_log[i]); + if (ret < 0) + return ret; + } + + /* Dump first records */ + for (i = 0; i < smc_log_index; i++) { + ret = show_smc_log_entry(buf, &smc_log[i]); + if (ret < 0) + return ret; + } + + return ret; +} diff --git a/drivers/gud/MobiCoreDriver/fastcall.h b/drivers/gud/MobiCoreDriver/fastcall.h new file mode 100644 index 000000000000..97b3103d26e7 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/fastcall.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2018 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef _TBASE_FASTCALL_H_ +#define _TBASE_FASTCALL_H_ + +int fc_init(uintptr_t base_pa, ptrdiff_t off, size_t q_len, size_t buf_len); +int fc_info(u32 ext_info_id, u32 *state, u32 *ext_info); +int fc_trace_init(phys_addr_t buffer, u32 size); +int fc_trace_deinit(void); +int fc_nsiq(u32 session_id, u32 payload); +int fc_yield(u32 timeslice); + +int mc_fastcall_debug_smclog(struct kasnprintf_buf *buf); + +#endif /* _TBASE_FASTCALL_H_ */ diff --git a/drivers/gud/MobiCoreDriver/gptci.h b/drivers/gud/MobiCoreDriver/gptci.h new file mode 100644 index 000000000000..a51edd4f98bb --- /dev/null +++ b/drivers/gud/MobiCoreDriver/gptci.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef _GP_TCI_H_ +#define _GP_TCI_H_ + +struct tee_value { + u32 a; + u32 b; +}; + +struct _teec_memory_reference_internal { + u32 sva; + u32 len; + u32 output_size; +}; + +union _teec_parameter_internal { + struct tee_value value; + struct _teec_memory_reference_internal memref; +}; + +enum _teec_tci_type { + _TA_OPERATION_OPEN_SESSION = 1, + _TA_OPERATION_INVOKE_COMMAND = 2, + _TA_OPERATION_CLOSE_SESSION = 3, +}; + +struct _teec_operation_internal { + enum _teec_tci_type type; + u32 command_id; + u32 param_types; + union _teec_parameter_internal params[4]; + bool is_cancelled; + u8 rfu_padding[3]; +}; + +struct _teec_tci { + char header[8]; + struct teec_uuid destination; + struct _teec_operation_internal operation; + u32 ready; + u32 return_origin; + u32 return_status; +}; + +/** + * Termination codes + */ +#define TA_EXIT_CODE_PANIC 300 +#define TA_EXIT_CODE_TCI 301 +#define TA_EXIT_CODE_PARAMS 302 +#define TA_EXIT_CODE_FINISHED 303 +#define TA_EXIT_CODE_SESSIONSTATE 304 +#define TA_EXIT_CODE_CREATEFAILED 305 + +#endif /* _GP_TCI_H_ */ diff --git a/drivers/gud/MobiCoreDriver/iwp.c b/drivers/gud/MobiCoreDriver/iwp.c new file mode 100644 index 000000000000..1284bc804a89 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/iwp.c @@ -0,0 +1,1218 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE +#include /* local_clock */ +#endif + +#include "tee_client_api.h" /* GP error codes/origins FIXME move */ +#include "mc_user.h" +#include "mc_admin.h" + +#include "mcimcp.h" +#include "mcifc.h" +#include "mcinq.h" /* SID_MCP */ +#include "mcitime.h" /* struct mcp_time */ +#include "mciiwp.h" + +#include "main.h" +#include "admin.h" /* tee_object* for 'blob' */ +#include "mmu.h" /* MMU for 'blob' */ +#include "nq.h" +#include "xen_fe.h" +#include "iwp.h" + +#define IWP_RETRIES 5 +#define IWP_TIMEOUT 10 +#define INVALID_IWS_SLOT 0xFFFFFFFF + +/* Macros */ +#define _TEEC_GET_PARAM_TYPE(t, i) (((t) >> (4 * (i))) & 0xF) + +/* Parameter number */ +#define _TEEC_PARAMETER_NUMBER 4 + +struct iws { + struct list_head list; + u64 slot; +}; + +static struct { + bool iwp_dead; + struct interworld_session *iws; + /* Interworld lists lock */ + struct mutex iws_list_lock; + /* InterWorld lists */ + struct iws *iws_list_pool; + struct list_head free_iws; + struct list_head allocd_iws; + /* Sessions */ + struct mutex sessions_lock; + struct list_head sessions; + /* TEE bad state detection */ + struct notifier_block tee_stop_notifier; + /* Log of last commands */ +#define LAST_CMDS_SIZE 256 + struct mutex last_cmds_mutex; /* Log protection */ + struct command_info { + u64 cpu_clk; /* Kernel time */ + pid_t pid; /* Caller PID */ + u32 id; /* IWP command ID */ + u32 session_id; + char uuid_str[34]; + enum state { + UNUSED, /* Unused slot */ + PENDING, /* Previous command in progress */ + SENT, /* Waiting for response */ + COMPLETE, /* Got result */ + FAILED, /* Something went wrong */ + } state; /* Command processing state */ + struct gp_return result; /* Command result */ + int errno; /* Return code */ + } last_cmds[LAST_CMDS_SIZE]; + int last_cmds_index; +} l_ctx; + +static void iwp_notif_handler(u32 id, u32 payload) +{ + struct iwp_session *iwp_session = NULL, *candidate; + + mutex_lock(&l_ctx.sessions_lock); + list_for_each_entry(candidate, &l_ctx.sessions, list) { + mc_dev_devel("candidate->slot [%08llx]", candidate->slot); + /* If id is SID_CANCEL_OPERATION, there is pseudo session */ + if (candidate->slot == payload && + (id != SID_CANCEL_OPERATION || candidate->sid == id)) { + iwp_session = candidate; + break; + } + } + mutex_unlock(&l_ctx.sessions_lock); + + if (!iwp_session) { + mc_dev_err(-ENXIO, "IWP no session found for id=0x%x slot=0x%x", + id, payload); + return; + } + + mc_dev_devel("IWP: iwp_session [%p] id [%08x] slot [%08x]", + iwp_session, id, payload); + nq_session_state_update(&iwp_session->nq_session, NQ_NOTIF_RECEIVED); + complete(&iwp_session->completion); +} + +void iwp_session_init(struct iwp_session *iwp_session, + const struct identity *identity) +{ + nq_session_init(&iwp_session->nq_session, true); + iwp_session->sid = SID_INVALID; + iwp_session->slot = INVALID_IWS_SLOT; + INIT_LIST_HEAD(&iwp_session->list); + mutex_init(&iwp_session->notif_wait_lock); + init_completion(&iwp_session->completion); + mutex_init(&iwp_session->iws_lock); + iwp_session->state = IWP_SESSION_RUNNING; + if (identity) + iwp_session->client_identity = *identity; +} + +static u64 iws_slot_get(void) +{ + struct iws *iws; + u64 slot = INVALID_IWS_SLOT; + + if (is_xen_domu()) + return (uintptr_t)kzalloc(sizeof(*iws), GFP_KERNEL); + + mutex_lock(&l_ctx.iws_list_lock); + if (!list_empty(&l_ctx.free_iws)) { + iws = list_first_entry(&l_ctx.free_iws, struct iws, list); + slot = iws->slot; + list_move(&iws->list, &l_ctx.allocd_iws); + atomic_inc(&g_ctx.c_slots); + mc_dev_devel("got slot %llu", slot); + } + mutex_unlock(&l_ctx.iws_list_lock); + return slot; +} + +/* Passing INVALID_IWS_SLOT is supported */ +static void iws_slot_put(u64 slot) +{ + struct iws *iws; + bool found = false; + + if (is_xen_domu()) { + kfree((void *)(uintptr_t)slot); + return; + } + + mutex_lock(&l_ctx.iws_list_lock); + list_for_each_entry(iws, &l_ctx.allocd_iws, list) { + if (slot == iws->slot) { + list_move(&iws->list, &l_ctx.free_iws); + atomic_dec(&g_ctx.c_slots); + found = true; + mc_dev_devel("put slot %llu", slot); + break; + } + } + mutex_unlock(&l_ctx.iws_list_lock); + + if (!found) + mc_dev_err(-EINVAL, "slot %llu not found", slot); +} + +static inline struct interworld_session *slot_to_iws(u64 slot) +{ + if (is_xen_domu()) + return (struct interworld_session *)(uintptr_t)slot; + + return (struct interworld_session *)((uintptr_t)l_ctx.iws + (u32)slot); +} + +/* + * IWP command functions + */ +static int iwp_cmd(struct iwp_session *iwp_session, u32 id, + struct teec_uuid *uuid, bool killable) +{ + struct command_info *cmd_info; + int ret; + + /* Initialize MCP log */ + mutex_lock(&l_ctx.last_cmds_mutex); + cmd_info = &l_ctx.last_cmds[l_ctx.last_cmds_index]; + memset(cmd_info, 0, sizeof(*cmd_info)); + cmd_info->cpu_clk = local_clock(); + cmd_info->pid = current->pid; + cmd_info->id = id; + if (id == SID_OPEN_SESSION || id == SID_OPEN_TA) { + /* Keep UUID because it's an 'open session' cmd */ + const char *cuuid = (const char *)uuid; + size_t i; + + cmd_info->uuid_str[0] = ' '; + for (i = 0; i < sizeof(*uuid); i++) { + snprintf(&cmd_info->uuid_str[1 + i * 2], 3, "%02x", + cuuid[i]); + } + } else if (id == SID_CANCEL_OPERATION) { + struct interworld_session *iws = slot_to_iws(iwp_session->slot); + + if (iws) + cmd_info->session_id = iws->session_handle; + else + cmd_info->session_id = 0; + } else { + cmd_info->session_id = iwp_session->sid; + } + + cmd_info->state = PENDING; + iwp_set_ret(0, &cmd_info->result); + if (++l_ctx.last_cmds_index >= LAST_CMDS_SIZE) + l_ctx.last_cmds_index = 0; + mutex_unlock(&l_ctx.last_cmds_mutex); + + if (l_ctx.iwp_dead) + return -EHOSTUNREACH; + + mc_dev_devel("psid [%08x], sid [%08x]", id, iwp_session->sid); + ret = nq_session_notify(&iwp_session->nq_session, id, + iwp_session->slot); + if (ret) { + mc_dev_err(ret, "sid [%08x]: sending failed", iwp_session->sid); + mutex_lock(&l_ctx.last_cmds_mutex); + cmd_info->errno = ret; + cmd_info->state = FAILED; + mutex_unlock(&l_ctx.last_cmds_mutex); + return ret; + } + + /* Update MCP log */ + mutex_lock(&l_ctx.last_cmds_mutex); + cmd_info->state = SENT; + mutex_unlock(&l_ctx.last_cmds_mutex); + + /* + * NB: Wait cannot be interruptible as we need an answer from SWd. It's + * up to the user-space to request a cancellation (for open session and + * command invocation operations.) + * + * We do provide a way out to make applications killable in some cases + * though. + */ + if (killable) { + ret = wait_for_completion_killable(&iwp_session->completion); + if (ret) { + iwp_request_cancellation(iwp_session->slot); + /* Make sure the SWd did not die in the meantime */ + if (l_ctx.iwp_dead) + return -EHOSTUNREACH; + + wait_for_completion(&iwp_session->completion); + } + } else { + wait_for_completion(&iwp_session->completion); + } + + if (l_ctx.iwp_dead) + return -EHOSTUNREACH; + + /* Update MCP log */ + mutex_lock(&l_ctx.last_cmds_mutex); + { + struct interworld_session *iws = slot_to_iws(iwp_session->slot); + + cmd_info->result.origin = iws->return_origin; + cmd_info->result.value = iws->status; + if (id == SID_OPEN_SESSION || id == SID_OPEN_TA) + cmd_info->session_id = iws->session_handle; + } + cmd_info->state = COMPLETE; + mutex_unlock(&l_ctx.last_cmds_mutex); + nq_session_state_update(&iwp_session->nq_session, NQ_NOTIF_CONSUMED); + return 0; +} + +/* + * Convert errno into GP error and set origin to COMMS. + * Note: -ECHILD is used to tell the caller that we have a GP error in value, so + * we return 0 on success and -ECHILD on error. If -ECHILD is given, we assume + * that value is already correctly set. + */ +int iwp_set_ret(int ret, struct gp_return *gp_ret) +{ + if (ret == -ECHILD) { + /* Already set */ + return ret; + } + + gp_ret->origin = TEEC_ORIGIN_COMMS; + switch (ret) { + case 0: + gp_ret->origin = TEEC_ORIGIN_TRUSTED_APP; + gp_ret->value = TEEC_SUCCESS; + return 0; + case -EACCES: + gp_ret->value = TEEC_ERROR_ACCESS_DENIED; + break; + case -EBUSY: + gp_ret->value = TEEC_ERROR_BUSY; + break; + case -ECANCELED: + gp_ret->value = TEEC_ERROR_CANCEL; + break; + case -EINVAL: + case -EFAULT: + gp_ret->value = TEEC_ERROR_BAD_PARAMETERS; + break; + case -EKEYREJECTED: + gp_ret->value = TEEC_ERROR_SECURITY; + break; + case -ENOENT: + gp_ret->value = TEEC_ERROR_ITEM_NOT_FOUND; + break; + case -ENOMEM: + gp_ret->value = TEEC_ERROR_OUT_OF_MEMORY; + break; + case -EHOSTUNREACH: + /* Tee crashed */ + gp_ret->value = TEEC_ERROR_TARGET_DEAD; + break; + case -ENXIO: + /* Session not found or not running */ + gp_ret->value = TEEC_ERROR_BAD_STATE; + break; + default: + gp_ret->value = TEEC_ERROR_GENERIC; + } + return -ECHILD; +} + +int iwp_register_shared_mem(struct tee_mmu *mmu, u32 *sva, + struct gp_return *gp_ret) +{ + int ret; + +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + return xen_gp_register_shared_mem(mmu, sva, gp_ret); +#endif + + ret = mcp_map(SID_MEMORY_REFERENCE, mmu, sva); + /* iwp_set_ret would override the origin if called after */ + ret = iwp_set_ret(ret, gp_ret); + if (ret) + gp_ret->origin = TEEC_ORIGIN_TEE; + + return ret; +} + +int iwp_release_shared_mem(struct mcp_buffer_map *map) +{ +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + return xen_gp_release_shared_mem(map); +#endif + + return mcp_unmap(SID_MEMORY_REFERENCE, map); +} + +static int iwp_operation_to_iws(struct gp_operation *operation, + struct interworld_session *iws, + struct mc_ioctl_buffer *bufs, + struct gp_shared_memory **parents) +{ + int param_type, i; + + iws->param_types = 0; + for (i = 0; i < _TEEC_PARAMETER_NUMBER; i++) { + /* Reset reference for temporary memory */ + bufs[i].va = 0; + /* Reset reference for registered memory */ + parents[i] = NULL; + param_type = _TEEC_GET_PARAM_TYPE(operation->param_types, i); + + switch (param_type) { + case TEEC_NONE: + case TEEC_VALUE_OUTPUT: + break; + case TEEC_VALUE_INPUT: + case TEEC_VALUE_INOUT: + iws->params[i].value.a = operation->params[i].value.a; + iws->params[i].value.b = operation->params[i].value.b; + break; + case TEEC_MEMREF_TEMP_INPUT: + case TEEC_MEMREF_TEMP_OUTPUT: + case TEEC_MEMREF_TEMP_INOUT: + if (operation->params[i].tmpref.buffer) { + struct gp_temp_memref *tmpref; + + tmpref = &operation->params[i].tmpref; + /* Prepare buffer to map */ + bufs[i].va = tmpref->buffer; + if (tmpref->size > BUFFER_LENGTH_MAX) { + mc_dev_err(-EINVAL, + "buffer size %llu too big", + tmpref->size); + return -EINVAL; + } + + bufs[i].len = tmpref->size; + if (param_type == TEEC_MEMREF_TEMP_INPUT) + bufs[i].flags = MC_IO_MAP_INPUT; + else if (param_type == TEEC_MEMREF_TEMP_OUTPUT) + bufs[i].flags = MC_IO_MAP_OUTPUT; + else + bufs[i].flags = MC_IO_MAP_INPUT_OUTPUT; + } else { + if (operation->params[i].tmpref.size) + return -EINVAL; + + /* Null buffer, won't get mapped */ + iws->params[i].tmpref.physical_address = 0; + iws->params[i].tmpref.size = 0; + iws->params[i].tmpref.offset = 0; + iws->params[i].tmpref.wsm_type = WSM_INVALID; + } + break; + case TEEC_MEMREF_WHOLE: + parents[i] = &operation->params[i].memref.parent; + iws->params[i].memref.offset = 0; + iws->params[i].memref.size = + operation->params[i].memref.parent.size; + break; + case TEEC_MEMREF_PARTIAL_INPUT: + case TEEC_MEMREF_PARTIAL_OUTPUT: + case TEEC_MEMREF_PARTIAL_INOUT: + parents[i] = &operation->params[i].memref.parent; + iws->params[i].memref.offset = + operation->params[i].memref.offset; + iws->params[i].memref.size = + operation->params[i].memref.size; + break; + default: + return -EINVAL; + } + + iws->param_types |= (u32)(param_type << (i * 4)); + } + + return 0; +} + +static inline void iwp_iws_set_tmpref(struct interworld_session *iws, int i, + const struct mcp_buffer_map *map) +{ + iws->params[i].tmpref.physical_address = map->addr; + iws->params[i].tmpref.size = map->length; + iws->params[i].tmpref.offset = map->offset; + iws->params[i].tmpref.wsm_type = map->type; +} + +static inline void iwp_iws_set_memref(struct interworld_session *iws, int i, + u32 sva) +{ + iws->params[i].memref.memref_handle = sva; +} + +static inline void iwp_iws_set_refs(struct interworld_session *iws, + const struct iwp_buffer_map *maps) +{ + int i; + + for (i = 0; i < _TEEC_PARAMETER_NUMBER; i++) + if (maps[i].sva) + iwp_iws_set_memref(iws, i, maps[i].sva); + else if (maps[i].map.addr) + iwp_iws_set_tmpref(iws, i, &maps[i].map); +} + +static void iwp_iws_to_operation(const struct interworld_session *iws, + struct gp_operation *operation) +{ + int i; + + for (i = 0; i < _TEEC_PARAMETER_NUMBER; i++) { + switch (_TEEC_GET_PARAM_TYPE(operation->param_types, i)) { + case TEEC_VALUE_OUTPUT: + case TEEC_VALUE_INOUT: + operation->params[i].value.a = iws->params[i].value.a; + operation->params[i].value.b = iws->params[i].value.b; + break; + case TEEC_MEMREF_TEMP_OUTPUT: + case TEEC_MEMREF_TEMP_INOUT: + operation->params[i].tmpref.size = + iws->params[i].tmpref.size; + break; + case TEEC_MEMREF_WHOLE: + if (operation->params[i].memref.parent.flags != + TEEC_MEM_INPUT) + operation->params[i].memref.size = + iws->params[i].tmpref.size; + break; + case TEEC_MEMREF_PARTIAL_OUTPUT: + case TEEC_MEMREF_PARTIAL_INOUT: + operation->params[i].memref.size = + iws->params[i].tmpref.size; + break; + case TEEC_NONE: + case TEEC_VALUE_INPUT: + case TEEC_MEMREF_TEMP_INPUT: + case TEEC_MEMREF_PARTIAL_INPUT: + break; + default: + /* Error caught by iwp_operation_to_iws() */ + break; + } + } +} + +static inline void mcuuid_to_tee_uuid(const struct mc_uuid_t *in, + struct teec_uuid *out) +{ + /* + * Warning: this code works only on little-endian platforms. + */ + out->time_low = in->value[3] + + (in->value[2] << 8) + + (in->value[1] << 16) + + (in->value[0] << 24); + out->time_mid = in->value[5] + + (in->value[4] << 8); + out->time_hi_and_version = in->value[7] + + (in->value[6] << 8); + memcpy(out->clock_seq_and_node, in->value + 8, 8); +} + +static const char *origin_to_string(u32 origin) +{ + switch (origin) { + case TEEC_ORIGIN_API: + return "API"; + case TEEC_ORIGIN_COMMS: + return "COMMS"; + case TEEC_ORIGIN_TEE: + return "TEE"; + case TEEC_ORIGIN_TRUSTED_APP: + return "TRUSTED_APP"; + } + return "UNKNOWN"; +} + +static const char *value_to_string(u32 value) +{ + switch (value) { + case TEEC_SUCCESS: + return "SUCCESS"; + case TEEC_ERROR_GENERIC: + return "GENERIC"; + case TEEC_ERROR_ACCESS_DENIED: + return "ACCESS_DENIED"; + case TEEC_ERROR_CANCEL: + return "CANCEL"; + case TEEC_ERROR_ACCESS_CONFLICT: + return "ACCESS_CONFLICT"; + case TEEC_ERROR_EXCESS_DATA: + return "EXCESS_DATA"; + case TEEC_ERROR_BAD_FORMAT: + return "BAD_FORMAT"; + case TEEC_ERROR_BAD_PARAMETERS: + return "BAD_PARAMETERS"; + case TEEC_ERROR_BAD_STATE: + return "BAD_STATE"; + case TEEC_ERROR_ITEM_NOT_FOUND: + return "ITEM_NOT_FOUND"; + case TEEC_ERROR_NOT_IMPLEMENTED: + return "NOT_IMPLEMENTED"; + case TEEC_ERROR_NOT_SUPPORTED: + return "NOT_SUPPORTED"; + case TEEC_ERROR_NO_DATA: + return "NO_DATA"; + case TEEC_ERROR_OUT_OF_MEMORY: + return "OUT_OF_MEMORY"; + case TEEC_ERROR_BUSY: + return "BUSY"; + case TEEC_ERROR_COMMUNICATION: + return "COMMUNICATION"; + case TEEC_ERROR_SECURITY: + return "SECURITY"; + case TEEC_ERROR_SHORT_BUFFER: + return "SHORT_BUFFER"; + case TEEC_ERROR_TARGET_DEAD: + return "TARGET_DEAD"; + case TEEC_ERROR_STORAGE_NO_SPACE: + return "STORAGE_NO_SPACE"; + } + return NULL; +} + +static const char *cmd_to_string(u32 id) +{ + switch (id) { + case SID_OPEN_SESSION: + return "open session"; + case SID_INVOKE_COMMAND: + return "invoke command"; + case SID_CLOSE_SESSION: + return "close session"; + case SID_CANCEL_OPERATION: + return "cancel operation"; + case SID_MEMORY_REFERENCE: + return "memory reference"; + case SID_OPEN_TA: + return "open TA"; + case SID_REQ_TA: + return "request TA"; + } + return "unknown"; +} + +static const char *state_to_string(enum iwp_session_state state) +{ + switch (state) { + case IWP_SESSION_RUNNING: + return "running"; + case IWP_SESSION_CLOSE_REQUESTED: + return "close requested"; + case IWP_SESSION_CLOSED: + return "closed"; + } + return "error"; +} + +int iwp_open_session_prepare( + struct iwp_session *iwp_session, + struct gp_operation *operation, + struct mc_ioctl_buffer *bufs, + struct gp_shared_memory **parents, + struct gp_return *gp_ret) +{ + struct interworld_session *iws; + u64 slot, op_slot; + int ret = 0; + + /* Get session final slot */ + slot = iws_slot_get(); + mc_dev_devel("slot [%08llx]", slot); + if (slot == INVALID_IWS_SLOT) { + ret = -ENOMEM; + mc_dev_err(ret, "can't get slot"); + return iwp_set_ret(ret, gp_ret); + } + + /* Get session temporary slot */ + op_slot = iws_slot_get(); + mc_dev_devel("op_slot [%08llx]", op_slot); + if (op_slot == INVALID_IWS_SLOT) { + ret = -ENOMEM; + mc_dev_err(ret, "can't get op_slot"); + iws_slot_put(slot); + return iwp_set_ret(ret, gp_ret); + } + + mutex_lock(&iwp_session->iws_lock); + + /* Prepare final session: refer to temporary slot in final one */ + iwp_session->slot = slot; + iws = slot_to_iws(slot); + memset(iws, 0, sizeof(*iws)); + + /* Prepare temporary session */ + iwp_session->op_slot = op_slot; + iws = slot_to_iws(op_slot); + memset(iws, 0, sizeof(*iws)); + + if (operation) { + ret = iwp_operation_to_iws(operation, iws, bufs, parents); + if (ret) + iwp_open_session_abort(iwp_session); + } + + return iwp_set_ret(ret, gp_ret); +} + +void iwp_open_session_abort(struct iwp_session *iwp_session) +{ + iws_slot_put(iwp_session->slot); + iws_slot_put(iwp_session->op_slot); + mutex_unlock(&iwp_session->iws_lock); +} + +/* + * Like open session except we pass the TA blob from NWd to SWd + */ +int iwp_open_session( + struct iwp_session *iwp_session, + const struct mc_uuid_t *uuid, + struct gp_operation *operation, + const struct iwp_buffer_map *maps, + struct interworld_session *iws_in, + struct tee_mmu **mmus, + struct gp_return *gp_ret) +{ + struct interworld_session *iws = slot_to_iws(iwp_session->slot); + struct interworld_session *op_iws = slot_to_iws(iwp_session->op_slot); + struct tee_object *obj = NULL; + struct tee_mmu *obj_mmu = NULL; + struct mcp_buffer_map obj_map; + int ret; + + /* Operation is NULL when called from Xen BE */ + if (operation) { + /* Login info */ + op_iws->login = iwp_session->client_identity.login_type; + mc_dev_devel("iws->login [%08x]", op_iws->login); + memcpy(&op_iws->client_uuid, + iwp_session->client_identity.login_data, + sizeof(op_iws->client_uuid)); + + /* Put ingoing operation in temporary IWS */ + iwp_iws_set_refs(op_iws, maps); + } else { + struct mcp_buffer_map map; + int i; + + *op_iws = *iws_in; + + /* Insert correct mapping in operation */ + for (i = 0; i < 4; i++) { + if (!mmus[i]) + continue; + + tee_mmu_buffer(mmus[i], &map); + iwp_iws_set_tmpref(op_iws, i, &map); + } + } + + /* For the SWd to find the TA slot from the main one */ + iws->command_id = (u32)iwp_session->op_slot; + + /* TA blob handling */ + if (!is_xen_domu()) { + union mclf_header *header; + + obj = tee_object_get(uuid, true); + if (IS_ERR(obj)) { + /* Tell SWd to load TA from SFS as not in registry */ + if (PTR_ERR(obj) == -ENOENT) + obj = tee_object_select(uuid); + + if (IS_ERR(obj)) + return PTR_ERR(obj); + } + + /* Convert UUID */ + header = (union mclf_header *)(&obj->data[obj->header_length]); + mcuuid_to_tee_uuid(&header->mclf_header_v2.uuid, + &op_iws->target_uuid); + + /* Create mapping for blob (alloc'd by driver => task = NULL) */ + { + struct mc_ioctl_buffer buf = { + .va = (uintptr_t)obj->data, + .len = obj->length, + .flags = MC_IO_MAP_INPUT, + }; + + obj_mmu = tee_mmu_create(NULL, &buf); + if (IS_ERR(obj_mmu)) { + ret = PTR_ERR(obj_mmu); + goto err_mmu; + } + + iws->param_types = TEEC_MEMREF_TEMP_INPUT; + tee_mmu_buffer(obj_mmu, &obj_map); + iwp_iws_set_tmpref(iws, 0, &obj_map); + mc_dev_devel("wsm_type [%04x], offset [%04x]", + obj_map.type, obj_map.offset); + mc_dev_devel("size [%08x], physical_address [%08llx]", + obj_map.length, obj_map.addr); + } + } + + /* Add to local list of sessions so we can receive the notification */ + mutex_lock(&l_ctx.sessions_lock); + list_add_tail(&iwp_session->list, &l_ctx.sessions); + mutex_unlock(&l_ctx.sessions_lock); + + /* Send IWP open command */ +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + ret = xen_gp_open_session(iwp_session, uuid, maps, iws, op_iws, + gp_ret); + else +#endif + ret = iwp_cmd(iwp_session, SID_OPEN_TA, &op_iws->target_uuid, + true); + + /* Temporary slot is not needed any more */ + iws_slot_put(iwp_session->op_slot); + /* Treat remote errors as errors, just use a specific errno */ + if (!ret && iws->status != TEEC_SUCCESS) { + gp_ret->origin = iws->return_origin; + gp_ret->value = iws->status; + ret = -ECHILD; + } + + if (!ret) { + /* set unique identifier for list search */ + iwp_session->sid = iws->session_handle; + /* Get outgoing operation from main IWS */ + if (operation) + iwp_iws_to_operation(iws, operation); + else + *iws_in = *iws; + + } else { + /* Remove from list of sessions */ + mutex_lock(&l_ctx.sessions_lock); + list_del(&iwp_session->list); + mutex_unlock(&l_ctx.sessions_lock); + iws_slot_put(iwp_session->slot); + mc_dev_devel("failed: %s from %s, ret %d", + value_to_string(gp_ret->value), + origin_to_string(gp_ret->origin), ret); + } + + mutex_unlock(&iwp_session->iws_lock); + + /* Blob not needed as re-mapped by the SWd */ + if (obj_mmu) + tee_mmu_put(obj_mmu); + +err_mmu: + /* Delete secure object */ + if (obj) + tee_object_free(obj); + + return iwp_set_ret(ret, gp_ret); +} + +static void iwp_session_release( + struct iwp_session *iwp_session) +{ + iwp_session->state = IWP_SESSION_CLOSED; + + /* Remove from list of sessions */ + mutex_lock(&l_ctx.sessions_lock); + list_del(&iwp_session->list); + mutex_unlock(&l_ctx.sessions_lock); + + nq_session_exit(&iwp_session->nq_session); + iws_slot_put(iwp_session->slot); +} + +/* + * Legacy and GP TAs close differently: + * - GP TAs always send a notification with payload, whether on close or crash + * - GP TAs may take time to close + */ +int iwp_close_session( + struct iwp_session *iwp_session) +{ + int ret = 0; + + if (is_xen_domu()) { +#ifdef TRUSTONIC_XEN_DOMU + ret = xen_gp_close_session(iwp_session); +#endif + } else { + mutex_lock(&iwp_session->iws_lock); + iwp_session->state = IWP_SESSION_CLOSE_REQUESTED; + + /* Send IWP open command */ + ret = iwp_cmd(iwp_session, SID_CLOSE_SESSION, NULL, false); + mutex_unlock(&iwp_session->iws_lock); + } + + iwp_session_release(iwp_session); + mc_dev_devel("close session %x ret %d state %s", iwp_session->sid, + ret, state_to_string(iwp_session->state)); + return ret; +} + +int iwp_invoke_command_prepare( + struct iwp_session *iwp_session, + u32 command_id, + struct gp_operation *operation, + struct mc_ioctl_buffer *bufs, + struct gp_shared_memory **parents, + struct gp_return *gp_ret) +{ + struct interworld_session *iws; + int ret = 0; + + if (iwp_session->state != IWP_SESSION_RUNNING) + return iwp_set_ret(-EBADFD, gp_ret); + + mutex_lock(&iwp_session->iws_lock); + iws = slot_to_iws(iwp_session->slot); + memset(iws, 0, sizeof(*iws)); + iws->session_handle = iwp_session->sid; + if (operation) { + iws->command_id = command_id; + ret = iwp_operation_to_iws(operation, iws, bufs, parents); + if (ret) + iwp_invoke_command_abort(iwp_session); + } + + return iwp_set_ret(ret, gp_ret); +} + +void iwp_invoke_command_abort( + struct iwp_session *iwp_session) +{ + mutex_unlock(&iwp_session->iws_lock); +} + +int iwp_invoke_command( + struct iwp_session *iwp_session, + struct gp_operation *operation, + const struct iwp_buffer_map *maps, + struct interworld_session *iws_in, + struct tee_mmu **mmus, + struct gp_return *gp_ret) +{ + struct interworld_session *iws = slot_to_iws(iwp_session->slot); + int ret = 0; + + /* Operation is NULL when called from Xen BE */ + if (operation) { + /* Update IWS with operation maps */ + iwp_iws_set_refs(iws, maps); + } else { + struct mcp_buffer_map map; + int i; + + *iws = *iws_in; + + /* Insert correct mapping in operation */ + for (i = 0; i < 4; i++) { + if (!mmus[i]) + continue; + + tee_mmu_buffer(mmus[i], &map); + iwp_iws_set_tmpref(iws, i, &map); + } + } + +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + ret = xen_gp_invoke_command(iwp_session, maps, iws, gp_ret); + else +#endif + ret = iwp_cmd(iwp_session, SID_INVOKE_COMMAND, NULL, true); + + /* Treat remote errors as errors, just use a specific errno */ + if (!ret && iws->status != TEEC_SUCCESS) + ret = -ECHILD; + + if (operation) + iwp_iws_to_operation(iws, operation); + else + *iws_in = *iws; + + if (ret && (ret != -ECHILD)) { + ret = iwp_set_ret(ret, gp_ret); + mc_dev_devel("failed with ret [%08x]", ret); + } else { + gp_ret->origin = iws->return_origin; + gp_ret->value = iws->status; + } + + mutex_unlock(&iwp_session->iws_lock); + return ret; +} + +int iwp_request_cancellation( + u64 slot) +{ + /* Pseudo IWP session for cancellation */ + struct iwp_session iwp_session; + int ret; + +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + return xen_gp_request_cancellation( + (uintptr_t)slot_to_iws(slot)); +#endif + + iwp_session_init(&iwp_session, NULL); + /* sid is local. Set is to SID_CANCEL_OPERATION to make things clear */ + iwp_session.sid = SID_CANCEL_OPERATION; + iwp_session.slot = slot; + mutex_lock(&l_ctx.sessions_lock); + list_add_tail(&iwp_session.list, &l_ctx.sessions); + mutex_unlock(&l_ctx.sessions_lock); + ret = iwp_cmd(&iwp_session, SID_CANCEL_OPERATION, NULL, false); + mutex_lock(&l_ctx.sessions_lock); + list_del(&iwp_session.list); + mutex_unlock(&l_ctx.sessions_lock); + return ret; +} + +static int debug_sessions(struct kasnprintf_buf *buf) +{ + struct iwp_session *session; + int ret; + + /* Header */ + ret = kasnprintf(buf, "%20s %4s %-15s %-11s %7s\n", + "CPU clock", "ID", "state", "notif state", "slot"); + if (ret < 0) + return ret; + + mutex_lock(&l_ctx.sessions_lock); + list_for_each_entry(session, &l_ctx.sessions, list) { + const char *state_str; + u64 cpu_clk; + + state_str = nq_session_state(&session->nq_session, &cpu_clk); + ret = kasnprintf(buf, "%20llu %4x %-15s %-11s %7llu\n", cpu_clk, + session->sid == SID_INVALID ? 0 : session->sid, + state_to_string(session->state), state_str, + session->slot); + if (ret < 0) + break; + } + mutex_unlock(&l_ctx.sessions_lock); + return ret; +} + +static ssize_t debug_sessions_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + return debug_generic_read(file, user_buf, count, ppos, + debug_sessions); +} + +static const struct file_operations debug_sessions_ops = { + .read = debug_sessions_read, + .llseek = default_llseek, + .open = debug_generic_open, + .release = debug_generic_release, +}; + +static inline int show_log_entry(struct kasnprintf_buf *buf, + struct command_info *cmd_info) +{ + const char *state_str = "unknown"; + const char *value_str = value_to_string(cmd_info->result.value); + char value[16]; + + switch (cmd_info->state) { + case UNUSED: + state_str = "unused"; + break; + case PENDING: + state_str = "pending"; + break; + case SENT: + state_str = "sent"; + break; + case COMPLETE: + state_str = "complete"; + break; + case FAILED: + state_str = "failed"; + break; + } + + if (!value_str) { + snprintf(value, sizeof(value), "%08x", cmd_info->result.value); + value_str = value; + } + + return kasnprintf(buf, "%20llu %5d %-16s %5x %-8s %5d %-11s %-17s%s\n", + cmd_info->cpu_clk, cmd_info->pid, + cmd_to_string(cmd_info->id), cmd_info->session_id, + state_str, cmd_info->errno, + origin_to_string(cmd_info->result.origin), value_str, + cmd_info->uuid_str); +} + +static int debug_last_cmds(struct kasnprintf_buf *buf) +{ + struct command_info *cmd_info; + int i, ret = 0; + + /* Initialize MCP log */ + mutex_lock(&l_ctx.last_cmds_mutex); + ret = kasnprintf(buf, "%20s %5s %-16s %5s %-8s %5s %-11s %-17s%s\n", + "CPU clock", "PID", "command", "S-ID", + "state", "errno", "origin", "value", "UUID"); + if (ret < 0) + goto out; + + cmd_info = &l_ctx.last_cmds[l_ctx.last_cmds_index]; + if (cmd_info->state != UNUSED) + /* Buffer has wrapped around, dump end (oldest records) */ + for (i = l_ctx.last_cmds_index; i < LAST_CMDS_SIZE; i++) { + ret = show_log_entry(buf, cmd_info++); + if (ret < 0) + goto out; + } + + /* Dump first records */ + cmd_info = &l_ctx.last_cmds[0]; + for (i = 0; i < l_ctx.last_cmds_index; i++) { + ret = show_log_entry(buf, cmd_info++); + if (ret < 0) + goto out; + } + +out: + mutex_unlock(&l_ctx.last_cmds_mutex); + return ret; +} + +static ssize_t debug_last_cmds_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + return debug_generic_read(file, user_buf, count, ppos, debug_last_cmds); +} + +static const struct file_operations debug_last_cmds_ops = { + .read = debug_last_cmds_read, + .llseek = default_llseek, + .open = debug_generic_open, + .release = debug_generic_release, +}; + +static inline void mark_iwp_dead(void) +{ + struct iwp_session *session; + + l_ctx.iwp_dead = true; + /* Signal all potential waiters that SWd is going away */ + mutex_lock(&l_ctx.sessions_lock); + list_for_each_entry(session, &l_ctx.sessions, list) + complete(&session->completion); + mutex_unlock(&l_ctx.sessions_lock); +} + +static int tee_stop_notifier_fn(struct notifier_block *nb, unsigned long event, + void *data) +{ + mark_iwp_dead(); + return 0; +} + +int iwp_init(void) +{ + int i; + + l_ctx.iws = nq_get_iwp_buffer(); + INIT_LIST_HEAD(&l_ctx.free_iws); + INIT_LIST_HEAD(&l_ctx.allocd_iws); + l_ctx.iws_list_pool = kcalloc(MAX_IW_SESSION, sizeof(struct iws), + GFP_KERNEL); + if (!l_ctx.iws_list_pool) + return -ENOMEM; + + for (i = 0; i < MAX_IW_SESSION; i++) { + l_ctx.iws_list_pool[i].slot = + i * sizeof(struct interworld_session); + list_add(&l_ctx.iws_list_pool[i].list, &l_ctx.free_iws); + } + + mutex_init(&l_ctx.iws_list_lock); + INIT_LIST_HEAD(&l_ctx.sessions); + mutex_init(&l_ctx.sessions_lock); + nq_register_notif_handler(iwp_notif_handler, true); + l_ctx.tee_stop_notifier.notifier_call = tee_stop_notifier_fn; + nq_register_tee_stop_notifier(&l_ctx.tee_stop_notifier); + /* Debugfs */ + mutex_init(&l_ctx.last_cmds_mutex); + return 0; +} + +void iwp_exit(void) +{ + mark_iwp_dead(); + nq_unregister_tee_stop_notifier(&l_ctx.tee_stop_notifier); +} + +int iwp_start(void) +{ + /* Create debugfs sessions and last commands entries */ + debugfs_create_file("iwp_sessions", 0400, g_ctx.debug_dir, NULL, + &debug_sessions_ops); + debugfs_create_file("last_iwp_commands", 0400, g_ctx.debug_dir, NULL, + &debug_last_cmds_ops); + return 0; +} + +void iwp_stop(void) +{ +} diff --git a/drivers/gud/MobiCoreDriver/iwp.h b/drivers/gud/MobiCoreDriver/iwp.h new file mode 100644 index 000000000000..8dff2c591571 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/iwp.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef _MC_IWP_H_ +#define _MC_IWP_H_ + +#include "mcloadformat.h" /* struct identity */ + +#include "nq.h" +#include "mcp.h" /* mcp_buffer_map FIXME move to nq? */ + +struct iwp_session { + /* Notification queue session */ + struct nq_session nq_session; + /* Session ID */ + u32 sid; + /* IWS slot */ + u64 slot; + /* IWS other slot needed at open */ + u64 op_slot; + /* Sessions list (protected by iwp sessions_lock) */ + struct list_head list; + /* Notification waiter lock */ + struct mutex notif_wait_lock; /* Only one at a time */ + /* Notification received */ + struct completion completion; + /* Interworld struct lock */ + struct mutex iws_lock; + /* Session state (protected by iwp sessions_lock) */ + enum iwp_session_state { + IWP_SESSION_RUNNING, + IWP_SESSION_CLOSE_REQUESTED, + IWP_SESSION_CLOSED, + } state; + /* GP TAs have login information */ + struct identity client_identity; +}; + +struct iwp_buffer_map { + struct mcp_buffer_map map; + u32 sva; +}; + +/* Private to iwp_session structure */ +void iwp_session_init(struct iwp_session *session, + const struct identity *identity); + +/* Getters */ +static inline u32 iwp_session_id(struct iwp_session *session) +{ + return session->sid; +} + +static inline u64 iwp_session_slot(struct iwp_session *session) +{ + return session->slot; +} + +/* Convert local errno to GP return values */ +int iwp_set_ret(int ret, struct gp_return *gp_ret); + +/* Commands */ +int iwp_register_shared_mem( + struct tee_mmu *mmu, + u32 *sva, + struct gp_return *gp_ret); +int iwp_release_shared_mem( + struct mcp_buffer_map *map); +int iwp_open_session_prepare( + struct iwp_session *session, + struct gp_operation *operation, + struct mc_ioctl_buffer *bufs, + struct gp_shared_memory **parents, + struct gp_return *gp_ret); +void iwp_open_session_abort( + struct iwp_session *iwp_session); +int iwp_open_session( + struct iwp_session *iwp_session, + const struct mc_uuid_t *uuid, + struct gp_operation *operation, + const struct iwp_buffer_map *maps, + struct interworld_session *iws, + struct tee_mmu **mmus, + struct gp_return *gp_ret); +int iwp_close_session( + struct iwp_session *iwp_session); +int iwp_invoke_command_prepare( + struct iwp_session *iwp_session, + u32 command_id, + struct gp_operation *operation, + struct mc_ioctl_buffer *bufs, + struct gp_shared_memory **parents, + struct gp_return *gp_ret); +void iwp_invoke_command_abort( + struct iwp_session *iwp_session); +int iwp_invoke_command( + struct iwp_session *iwp_session, + struct gp_operation *operation, + const struct iwp_buffer_map *maps, + struct interworld_session *iws, + struct tee_mmu **mmus, + struct gp_return *gp_ret); +int iwp_request_cancellation( + u64 slot); + +/* Initialisation/cleanup */ +int iwp_init(void); +void iwp_exit(void); +int iwp_start(void); +void iwp_stop(void); + +#endif /* _MC_IWP_H_ */ diff --git a/drivers/gud/MobiCoreDriver/logging.c b/drivers/gud/MobiCoreDriver/logging.c new file mode 100644 index 000000000000..2ef1d2e61db0 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/logging.c @@ -0,0 +1,261 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include "main.h" +#include "logging.h" + +/* Supported log buffer version */ +#define MC_LOG_VERSION 2 + +/* Default length of the log ring buffer 256KiB */ +#define LOG_BUF_ORDER 6 + +/* Max Len of a log line for printing */ +#define LOG_LINE_SIZE 256 + +/* Definitions for log version 2 */ +#define LOG_TYPE_MASK (0x0007) +#define LOG_TYPE_CHAR 0 +#define LOG_TYPE_INTEGER 1 + +/* Field length */ +#define LOG_LENGTH_MASK (0x00F8) +#define LOG_LENGTH_SHIFT 3 + +/* Extra attributes */ +#define LOG_EOL (0x0100) +#define LOG_INTEGER_DECIMAL (0x0200) +#define LOG_INTEGER_SIGNED (0x0400) + +/* active cpu id */ +#define LOG_CPUID_MASK (0xF000) +#define LOG_CPUID_SHIFT 12 + +struct mc_logmsg { + u16 ctrl; /* Type and format of data */ + u16 source; /* Unique value for each event source */ + u32 log_data; /* Value, if any */ +}; + +/* MobiCore internal trace buffer structure. */ +struct mc_trace_buf { + u32 version; /* version of trace buffer */ + u32 length; /* length of buff */ + u32 head; /* last write position */ + u8 buff[]; /* start of the log buffer */ +}; + +static struct logging_ctx { + struct kthread_work work; + struct kthread_worker worker; + struct task_struct *thread; + union { + struct mc_trace_buf *trace_buf; /* Circular log buffer */ + unsigned long trace_page; + }; + u32 tail; /* MobiCore log read position */ + int thread_err; + u16 prev_source; /* Previous Log source */ + char line[LOG_LINE_SIZE + 1];/* Log Line buffer */ + u32 line_len; /* Log Line buffer current length */ +#if KERNEL_VERSION(4, 4, 0) > LINUX_VERSION_CODE + u32 enabled; /* Log can be disabled via debugfs */ +#else + bool enabled; /* Log can be disabled via debugfs */ +#endif + bool dead; +} log_ctx; + +static inline void log_eol(u16 source, u32 cpuid) +{ + if (!log_ctx.line_len) + return; + + if (log_ctx.prev_source) + /* TEE user-space */ + dev_info(g_ctx.mcd, "%03x(%u)|%s\n", log_ctx.prev_source, + cpuid, log_ctx.line); + else + /* TEE kernel */ + dev_info(g_ctx.mcd, "mtk(%u)|%s\n", cpuid, log_ctx.line); + log_ctx.line[0] = '\0'; + log_ctx.line_len = 0; +} + +/* + * Collect chars in log_ctx.line buffer and output the buffer when it is full. + * No locking needed because only "mobicore_log" thread updates this buffer. + */ +static inline void log_char(char ch, u16 source, u32 cpuid) +{ + if (ch == '\0') + return; + + if (ch == '\n' || ch == '\r') { + log_eol(source, cpuid); + return; + } + + if (log_ctx.line_len >= LOG_LINE_SIZE || source != log_ctx.prev_source) + log_eol(source, cpuid); + + log_ctx.line[log_ctx.line_len++] = ch; + log_ctx.line[log_ctx.line_len] = 0; + log_ctx.prev_source = source; +} + +static inline void log_string(u32 ch, u16 source, u32 cpuid) +{ + while (ch) { + log_char(ch & 0xFF, source, cpuid); + ch >>= 8; + } +} + +static inline void log_number(u32 format, u32 value, u16 source, u32 cpuid) +{ + int width = (format & LOG_LENGTH_MASK) >> LOG_LENGTH_SHIFT; + char fmt[16]; + char buffer[32]; + const char *reader = buffer; + + if (format & LOG_INTEGER_DECIMAL) + if (format & LOG_INTEGER_SIGNED) + snprintf(fmt, sizeof(fmt), "%%%ud", width); + else + snprintf(fmt, sizeof(fmt), "%%%uu", width); + else + snprintf(fmt, sizeof(fmt), "%%0%ux", width); + + snprintf(buffer, sizeof(buffer), fmt, value); + while (*reader) + log_char(*reader++, source, cpuid); +} + +static inline int log_msg(void *data) +{ + struct mc_logmsg *msg = (struct mc_logmsg *)data; + int log_type = msg->ctrl & LOG_TYPE_MASK; + int cpuid = ((msg->ctrl & LOG_CPUID_MASK) >> LOG_CPUID_SHIFT); + + switch (log_type) { + case LOG_TYPE_CHAR: + log_string(msg->log_data, msg->source, cpuid); + break; + case LOG_TYPE_INTEGER: + log_number(msg->ctrl, msg->log_data, msg->source, cpuid); + break; + } + if (msg->ctrl & LOG_EOL) + log_eol(msg->source, cpuid); + + return sizeof(*msg); +} + +static void logging_worker(struct kthread_work *work) +{ + static DEFINE_MUTEX(local_mutex); + + mutex_lock(&local_mutex); + while (log_ctx.trace_buf->head != log_ctx.tail) { + if (log_ctx.trace_buf->version != MC_LOG_VERSION) { + mc_dev_err(-EINVAL, "Bad log data v%d (exp. v%d), stop", + log_ctx.trace_buf->version, MC_LOG_VERSION); + log_ctx.dead = true; + break; + } + + log_ctx.tail += log_msg(&log_ctx.trace_buf->buff[log_ctx.tail]); + /* Wrap over if no space left for a complete message */ + if ((log_ctx.tail + sizeof(struct mc_logmsg)) > + log_ctx.trace_buf->length) + log_ctx.tail = 0; + } + mutex_unlock(&local_mutex); +} + +/* + * Wake up the log reader thread + * This should be called from the places where calls into MobiCore have + * generated some logs(eg, yield, SIQ...) + */ +void logging_run(void) +{ + if (log_ctx.enabled && !log_ctx.dead && + log_ctx.trace_buf->head != log_ctx.tail) +#if KERNEL_VERSION(4, 9, 0) > LINUX_VERSION_CODE + queue_kthread_work(&log_ctx.worker, &log_ctx.work); +#else + kthread_queue_work(&log_ctx.worker, &log_ctx.work); +#endif +} + +/* + * Setup MobiCore kernel log. It assumes it's running on CORE 0! + * The fastcall will complain if that is not the case! + */ +int logging_init(phys_addr_t *buffer, u32 *size) +{ + /* + * We are going to map this buffer into virtual address space in SWd. + * To reduce complexity there, we use a contiguous buffer. + */ + log_ctx.trace_page = __get_free_pages(GFP_KERNEL | __GFP_ZERO, + LOG_BUF_ORDER); + if (!log_ctx.trace_page) + return -ENOMEM; + + *buffer = virt_to_phys((void *)(log_ctx.trace_page)); + *size = BIT(LOG_BUF_ORDER) * PAGE_SIZE; + + /* Logging thread */ +#if KERNEL_VERSION(4, 9, 0) > LINUX_VERSION_CODE + init_kthread_work(&log_ctx.work, logging_worker); + init_kthread_worker(&log_ctx.worker); +#else + kthread_init_work(&log_ctx.work, logging_worker); + kthread_init_worker(&log_ctx.worker); +#endif + log_ctx.thread = kthread_create(kthread_worker_fn, &log_ctx.worker, + "tee_log"); + if (IS_ERR(log_ctx.thread)) + return PTR_ERR(log_ctx.thread); + + wake_up_process(log_ctx.thread); + + /* Debugfs switch */ + log_ctx.enabled = true; + debugfs_create_bool("swd_debug", 0600, g_ctx.debug_dir, + &log_ctx.enabled); + return 0; +} + +void logging_exit(bool buffer_busy) +{ + /* + * This is not racey as the only caller for logging_run is the + * scheduler which gets stopped before us, and long before we exit. + */ + kthread_stop(log_ctx.thread); + if (!buffer_busy) + free_pages(log_ctx.trace_page, LOG_BUF_ORDER); +} diff --git a/drivers/gud/MobiCoreDriver/logging.h b/drivers/gud/MobiCoreDriver/logging.h new file mode 100644 index 000000000000..3791e2e8abb0 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/logging.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef _MC_LOGGING_H_ +#define _MC_LOGGING_H_ + +void logging_run(void); +int logging_init(phys_addr_t *buffer, u32 *size); +void logging_exit(bool buffer_busy); + +#endif /* _MC_LOGGING_H_ */ diff --git a/drivers/gud/MobiCoreDriver/main.c b/drivers/gud/MobiCoreDriver/main.c new file mode 100644 index 000000000000..1846ece25477 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/main.c @@ -0,0 +1,743 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2020 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mc_user.h" +#include "mc_admin.h" /* MC_ADMIN_DEVNODE */ + +#include "platform.h" /* MC_PM_RUNTIME */ +#include "main.h" +#include "arm.h" +#include "admin.h" +#include "user.h" +#include "iwp.h" +#include "mcp.h" +#include "nq.h" +#include "client.h" +#include "xen_be.h" +#include "xen_fe.h" +#include "build_tag.h" + +/* Default entry for our driver in device tree */ +#ifndef MC_DEVICE_PROPNAME +#define MC_DEVICE_PROPNAME "arm,mcd" +#endif + +/* Define a MobiCore device structure for use with dev_debug() etc */ +static struct device_driver driver = { + .name = "Trustonic" +}; + +static struct device device = { + .driver = &driver +}; + +struct mc_device_ctx g_ctx = { + .mcd = &device +}; + +static struct { + /* Device tree compatibility */ + bool use_platform_driver; + /* TEE start return code mutex */ + struct mutex start_mutex; + /* TEE start return code */ + int start_ret; +#ifdef MC_PM_RUNTIME + /* Whether hibernation succeeded */ + bool did_hibernate; + /* Reboot notifications */ + struct notifier_block reboot_notifier; + /* PM notifications */ + struct notifier_block pm_notifier; +#endif + /* Devices */ + dev_t device; + struct class *class; + /* Admin device */ + struct cdev admin_cdev; + /* User device */ + dev_t user_dev; + struct cdev user_cdev; + /* Debug counters */ + struct mutex struct_counters_buf_mutex; + char struct_counters_buf[256]; + int struct_counters_buf_len; +} main_ctx; + +static int mobicore_start(void); +static void mobicore_stop(void); + +int kasnprintf(struct kasnprintf_buf *buf, const char *fmt, ...) +{ + va_list args; + int max_size = buf->size - buf->off; + int i; + + va_start(args, fmt); + i = vsnprintf(buf->buf + buf->off, max_size, fmt, args); + if (i >= max_size) { + int new_size = PAGE_ALIGN(buf->size + i + 1); + char *new_buf = krealloc(buf->buf, new_size, buf->gfp); + + if (!new_buf) { + i = -ENOMEM; + } else { + buf->buf = new_buf; + buf->size = new_size; + max_size = buf->size - buf->off; + i = vsnprintf(buf->buf + buf->off, max_size, fmt, args); + } + } + + if (i > 0) + buf->off += i; + + va_end(args); + return i; +} + +static inline void kasnprintf_buf_reset(struct kasnprintf_buf *buf) +{ + kfree(buf->buf); + buf->buf = NULL; + buf->size = 0; + buf->off = 0; +} + +ssize_t debug_generic_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos, + int (*function)(struct kasnprintf_buf *buf)) +{ + struct kasnprintf_buf *buf = file->private_data; + int ret = 0; + + mutex_lock(&buf->mutex); + /* Add/update buffer */ + if (!*ppos) { + kasnprintf_buf_reset(buf); + ret = function(buf); + if (ret < 0) { + kasnprintf_buf_reset(buf); + goto end; + } + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf->buf, + buf->off); + +end: + mutex_unlock(&buf->mutex); + return ret; +} + +int debug_generic_open(struct inode *inode, struct file *file) +{ + struct kasnprintf_buf *buf; + + file->private_data = kzalloc(sizeof(*buf), GFP_KERNEL); + if (!file->private_data) + return -ENOMEM; + + buf = file->private_data; + mutex_init(&buf->mutex); + buf->gfp = GFP_KERNEL; + return 0; +} + +int debug_generic_release(struct inode *inode, struct file *file) +{ + struct kasnprintf_buf *buf = file->private_data; + + if (!buf) + return 0; + + kasnprintf_buf_reset(buf); + kfree(buf); + return 0; +} + +static ssize_t debug_structs_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + return debug_generic_read(file, user_buf, count, ppos, + clients_debug_structs); +} + +static const struct file_operations debug_structs_ops = { + .read = debug_structs_read, + .llseek = default_llseek, + .open = debug_generic_open, + .release = debug_generic_release, +}; + +static ssize_t debug_struct_counters_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + if (!*ppos) { + int ret; + + mutex_lock(&main_ctx.struct_counters_buf_mutex); + ret = snprintf(main_ctx.struct_counters_buf, + sizeof(main_ctx.struct_counters_buf), + "clients: %d\n" + "cbufs: %d\n" + "cwsms: %d\n" + "sessions: %d\n" + "swsms: %d\n" + "mmus: %d\n" + "maps: %d\n" + "slots: %d\n" + "xen maps: %d\n" + "xen fes: %d\n", + atomic_read(&g_ctx.c_clients), + atomic_read(&g_ctx.c_cbufs), + atomic_read(&g_ctx.c_cwsms), + atomic_read(&g_ctx.c_sessions), + atomic_read(&g_ctx.c_wsms), + atomic_read(&g_ctx.c_mmus), + atomic_read(&g_ctx.c_maps), + atomic_read(&g_ctx.c_slots), + atomic_read(&g_ctx.c_xen_maps), + atomic_read(&g_ctx.c_xen_fes)); + mutex_unlock(&main_ctx.struct_counters_buf_mutex); + if (ret > 0) + main_ctx.struct_counters_buf_len = ret; + } + + return simple_read_from_buffer(user_buf, count, ppos, + main_ctx.struct_counters_buf, + main_ctx.struct_counters_buf_len); +} + +static const struct file_operations debug_struct_counters_ops = { + .read = debug_struct_counters_read, + .llseek = default_llseek, +}; + +static inline int device_user_init(void) +{ + struct device *dev; + int ret = 0; + + main_ctx.user_dev = MKDEV(MAJOR(main_ctx.device), 1); + /* Create the user node */ + mc_user_init(&main_ctx.user_cdev); + ret = cdev_add(&main_ctx.user_cdev, main_ctx.user_dev, 1); + if (ret) { + mc_dev_err(ret, "user cdev_add failed"); + return ret; + } + + main_ctx.user_cdev.owner = THIS_MODULE; + dev = device_create(main_ctx.class, NULL, main_ctx.user_dev, NULL, + MC_USER_DEVNODE); + if (IS_ERR(dev)) { + ret = PTR_ERR(dev); + cdev_del(&main_ctx.user_cdev); + mc_dev_err(ret, "user device_create failed"); + return ret; + } + + /* Create debugfs structs entry */ + debugfs_create_file("structs", 0400, g_ctx.debug_dir, NULL, + &debug_structs_ops); + + return 0; +} + +static inline void device_user_exit(void) +{ + device_destroy(main_ctx.class, main_ctx.user_dev); + cdev_del(&main_ctx.user_cdev); +} + +#ifdef MC_PM_RUNTIME +static int reboot_notifier(struct notifier_block *nb, unsigned long event, + void *dummy) +{ + switch (event) { + case SYS_HALT: + case SYS_POWER_OFF: + main_ctx.did_hibernate = true; + break; + } + + return 0; +} + +static int suspend_notifier(struct notifier_block *nb, unsigned long event, + void *dummy) +{ + int ret = 0; + + main_ctx.did_hibernate = false; + switch (event) { + case PM_SUSPEND_PREPARE: + return nq_suspend(); + case PM_POST_SUSPEND: + return nq_resume(); +#ifdef TRUSTONIC_HIBERNATION_SUPPORT + case PM_HIBERNATION_PREPARE: + /* Try to stop the TEE nicely (ignore failure) */ + nq_stop(); + break; + case PM_POST_HIBERNATION: + if (main_ctx.did_hibernate) { + /* Really did hibernate */ + client_cleanup(); + main_ctx.start_ret = TEE_START_NOT_TRIGGERED; + return mobicore_start(); + } + + /* Did not hibernate, just restart the TEE */ + ret = nq_start(); +#endif + } + + return ret; +} +#endif /* MC_PM_RUNTIME */ + +static inline int check_version(void) +{ + struct mc_version_info version_info; + int ret; + + /* Must be called before creating the user device node to avoid race */ + ret = mcp_get_version(&version_info); + if (ret) + return ret; + + /* CMP version is meaningless in this case and is thus not printed */ + mc_dev_info("\n" + " product_id = %s\n" + " version_mci = 0x%08x\n" + " version_so = 0x%08x\n" + " version_mclf = 0x%08x\n" + " version_container = 0x%08x\n" + " version_mc_config = 0x%08x\n" + " version_tl_api = 0x%08x\n" + " version_dr_api = 0x%08x\n" + " version_nwd = 0x%08x\n", + version_info.product_id, + version_info.version_mci, + version_info.version_so, + version_info.version_mclf, + version_info.version_container, + version_info.version_mc_config, + version_info.version_tl_api, + version_info.version_dr_api, + version_info.version_nwd); + + /* Determine which features are supported */ + if (version_info.version_mci != MC_VERSION(1, 7)) { + ret = -EHOSTDOWN; + mc_dev_err(ret, "TEE incompatible with this driver"); + return ret; + } + + return 0; +} + +static int mobicore_start_domu(void) +{ + mutex_lock(&main_ctx.start_mutex); + if (main_ctx.start_ret != TEE_START_NOT_TRIGGERED) + goto end; + + /* Must be called before creating the user device node to avoid race */ + main_ctx.start_ret = check_version(); + if (main_ctx.start_ret) + goto end; + + main_ctx.start_ret = device_user_init(); +end: + mutex_unlock(&main_ctx.start_mutex); + return main_ctx.start_ret; +} + +static int mobicore_start(void) +{ + int ret; + + mutex_lock(&main_ctx.start_mutex); + if (main_ctx.start_ret != TEE_START_NOT_TRIGGERED) + goto got_ret; + + ret = nq_start(); + if (ret) { + mc_dev_err(ret, "NQ start failed"); + goto err_nq; + } + + ret = mcp_start(); + if (ret) { + mc_dev_err(ret, "MCP start failed"); + goto err_mcp; + } + + ret = iwp_start(); + if (ret) { + mc_dev_err(ret, "IWP start failed"); + goto err_iwp; + } + + /* Must be called before creating the user device node to avoid race */ + ret = check_version(); + if (ret) + goto err_version; + +#ifdef MC_PM_RUNTIME + main_ctx.reboot_notifier.notifier_call = reboot_notifier; + ret = register_reboot_notifier(&main_ctx.reboot_notifier); + if (ret) { + mc_dev_err(ret, "reboot notifier registration failed"); + goto err_pm_notif; + } + + main_ctx.pm_notifier.notifier_call = suspend_notifier; + ret = register_pm_notifier(&main_ctx.pm_notifier); + if (ret) { + unregister_reboot_notifier(&main_ctx.reboot_notifier); + mc_dev_err(ret, "PM notifier register failed"); + goto err_pm_notif; + } +#if KERNEL_VERSION(4, 6, 0) <= LINUX_VERSION_CODE + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "Trustonic", + nq_cpu_on, nq_cpu_off); +#endif +#endif + + if (is_xen_dom0()) { + ret = xen_be_init(); + if (ret) + goto err_xen_be; + } + + ret = device_user_init(); + if (ret) + goto err_device_user; + + main_ctx.start_ret = 0; + goto got_ret; + +err_device_user: + if (is_xen_dom0()) + xen_be_exit(); +err_xen_be: +#ifdef MC_PM_RUNTIME + unregister_reboot_notifier(&main_ctx.reboot_notifier); + unregister_pm_notifier(&main_ctx.pm_notifier); +err_pm_notif: +#endif +err_version: + iwp_stop(); +err_iwp: + mcp_stop(); +err_mcp: + nq_stop(); +err_nq: + main_ctx.start_ret = ret; +got_ret: + mutex_unlock(&main_ctx.start_mutex); + return main_ctx.start_ret; +} + +static void mobicore_stop(void) +{ + device_user_exit(); + if (is_xen_dom0()) + xen_be_exit(); + + if (!is_xen_domu()) { +#ifdef MC_PM_RUNTIME + unregister_reboot_notifier(&main_ctx.reboot_notifier); + unregister_pm_notifier(&main_ctx.pm_notifier); +#endif + iwp_stop(); + mcp_stop(); + nq_stop(); + } +} + +int mc_wait_tee_start(void) +{ + int ret; + + mutex_lock(&main_ctx.start_mutex); + while (main_ctx.start_ret == TEE_START_NOT_TRIGGERED) { + mutex_unlock(&main_ctx.start_mutex); + ssleep(1); + mutex_lock(&main_ctx.start_mutex); + } + + ret = main_ctx.start_ret; + mutex_unlock(&main_ctx.start_mutex); + return ret; +} + +static inline int device_common_init(void) +{ + int ret; + + ret = alloc_chrdev_region(&main_ctx.device, 0, 2, "trustonic_tee"); + if (ret) { + mc_dev_err(ret, "alloc_chrdev_region failed"); + return ret; + } + + main_ctx.class = class_create(THIS_MODULE, "trustonic_tee"); + if (IS_ERR(main_ctx.class)) { + ret = PTR_ERR(main_ctx.class); + mc_dev_err(ret, "class_create failed"); + unregister_chrdev_region(main_ctx.device, 2); + return ret; + } + + return 0; +} + +static inline void device_common_exit(void) +{ + class_destroy(main_ctx.class); + unregister_chrdev_region(main_ctx.device, 2); +} + +static inline int device_admin_init(void) +{ + struct device *dev; + int ret = 0; + + /* Create the ADMIN node */ + ret = mc_admin_init(&main_ctx.admin_cdev, mobicore_start, + mobicore_stop); + if (ret) + goto err_init; + + ret = cdev_add(&main_ctx.admin_cdev, main_ctx.device, 1); + if (ret) { + mc_dev_err(ret, "admin cdev_add failed"); + goto err_cdev; + } + + main_ctx.admin_cdev.owner = THIS_MODULE; + dev = device_create(main_ctx.class, NULL, main_ctx.device, NULL, + MC_ADMIN_DEVNODE); + if (IS_ERR(dev)) { + ret = PTR_ERR(dev); + mc_dev_err(ret, "admin device_create failed"); + goto err_device; + } + + return 0; + +err_device: + cdev_del(&main_ctx.admin_cdev); +err_cdev: + mc_admin_exit(); +err_init: + return ret; +} + +static inline void device_admin_exit(void) +{ + device_destroy(main_ctx.class, main_ctx.device); + cdev_del(&main_ctx.admin_cdev); + mc_admin_exit(); +} + +/* + * This function is called by the kernel during startup or by a insmod command. + * This device is installed and registered as cdev, then interrupt and + * queue handling is set up + */ +static int mobicore_probe(struct platform_device *pdev) +{ + int ret = 0; + + if (pdev) + g_ctx.mcd->of_node = pdev->dev.of_node; + +#ifdef MOBICORE_COMPONENT_BUILD_TAG + mc_dev_info("MobiCore %s", MOBICORE_COMPONENT_BUILD_TAG); +#endif + /* Hardware does not support ARM TrustZone -> Cannot continue! */ + if (!is_xen_domu() && !has_security_extensions()) { + ret = -ENODEV; + mc_dev_err(ret, "Hardware doesn't support ARM TrustZone!"); + return ret; + } + + /* Running in secure mode -> Cannot load the driver! */ + if (is_secure_mode()) { + ret = -ENODEV; + mc_dev_err(ret, "Running in secure MODE!"); + return ret; + } + + /* Make sure we can create debugfs entries */ + g_ctx.debug_dir = debugfs_create_dir("trustonic_tee", NULL); + + /* Initialize debug counters */ + atomic_set(&g_ctx.c_clients, 0); + atomic_set(&g_ctx.c_cbufs, 0); + atomic_set(&g_ctx.c_cwsms, 0); + atomic_set(&g_ctx.c_sessions, 0); + atomic_set(&g_ctx.c_wsms, 0); + atomic_set(&g_ctx.c_mmus, 0); + atomic_set(&g_ctx.c_maps, 0); + atomic_set(&g_ctx.c_slots, 0); + atomic_set(&g_ctx.c_xen_maps, 0); + atomic_set(&g_ctx.c_xen_fes, 0); + main_ctx.start_ret = TEE_START_NOT_TRIGGERED; + mutex_init(&main_ctx.start_mutex); + mutex_init(&main_ctx.struct_counters_buf_mutex); + /* Create debugfs info entries */ + debugfs_create_file("structs_counters", 0400, g_ctx.debug_dir, NULL, + &debug_struct_counters_ops); + + /* Initialize common API layer */ + client_init(); + + /* Initialize plenty of nice features */ + ret = nq_init(); + if (ret) { + mc_dev_err(ret, "NQ init failed"); + goto fail_nq_init; + } + + ret = mcp_init(); + if (ret) { + mc_dev_err(ret, "MCP init failed"); + goto err_mcp; + } + + ret = iwp_init(); + if (ret) { + mc_dev_err(ret, "IWP init failed"); + goto err_iwp; + } + + ret = device_common_init(); + if (ret) + goto err_common; + + if (!is_xen_domu()) { + /* Admin dev is for the daemon to communicate with the driver */ + ret = device_admin_init(); + if (ret) + goto err_admin; + +#ifndef MC_DELAYED_TEE_START + ret = mobicore_start(); +#endif + if (ret) + goto err_start; + } + + return 0; + +err_start: + device_admin_exit(); +err_admin: + device_common_exit(); +err_common: + iwp_exit(); +err_iwp: + mcp_exit(); +err_mcp: + nq_exit(); +fail_nq_init: + debugfs_remove_recursive(g_ctx.debug_dir); + return ret; +} + +static int mobicore_probe_not_of(void) +{ + return mobicore_probe(NULL); +} + +static const struct of_device_id of_match_table[] = { + { .compatible = MC_DEVICE_PROPNAME }, + { } +}; + +static struct platform_driver mc_plat_driver = { + .probe = mobicore_probe, + .driver = { + .name = "mcd", + .owner = THIS_MODULE, + .of_match_table = of_match_table, + } +}; + +static int __init mobicore_init(void) +{ + dev_set_name(g_ctx.mcd, "TEE"); + /* + * Do not remove or change the following trace. + * The string "MobiCore" is used to detect if the TEE is in of the image + */ + mc_dev_info("MobiCore mcDrvModuleApi version is %d.%d", + MCDRVMODULEAPI_VERSION_MAJOR, + MCDRVMODULEAPI_VERSION_MINOR); + + /* In a Xen DomU, just register the front-end */ + if (is_xen_domu()) + return xen_fe_init(mobicore_probe_not_of, mobicore_start_domu); + + main_ctx.use_platform_driver = + of_find_compatible_node(NULL, NULL, MC_DEVICE_PROPNAME); + if (main_ctx.use_platform_driver) + return platform_driver_register(&mc_plat_driver); + + return mobicore_probe_not_of(); +} + +static void __exit mobicore_exit(void) +{ + if (is_xen_domu()) + xen_fe_exit(); + + if (main_ctx.use_platform_driver) + platform_driver_unregister(&mc_plat_driver); + + if (!is_xen_domu()) + device_admin_exit(); + + device_common_exit(); + iwp_exit(); + mcp_exit(); + nq_exit(); + debugfs_remove_recursive(g_ctx.debug_dir); +} + +module_init(mobicore_init); +module_exit(mobicore_exit); + +MODULE_AUTHOR("Trustonic Limited"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MobiCore driver"); diff --git a/drivers/gud/MobiCoreDriver/main.h b/drivers/gud/MobiCoreDriver/main.h new file mode 100644 index 000000000000..8c8de978ffb7 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/main.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2018 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef _MC_MAIN_H_ +#define _MC_MAIN_H_ + +#include /* dev_* macros */ +#include /* gfp_t */ +#include /* struct inode and struct file */ +#include +#include +#include + +#define MC_VERSION(major, minor) \ + ((((major) & 0x0000ffff) << 16) | ((minor) & 0x0000ffff)) +#define MC_VERSION_MAJOR(x) ((x) >> 16) +#define MC_VERSION_MINOR(x) ((x) & 0xffff) + +#define mc_dev_err(__ret__, fmt, ...) \ + dev_err(g_ctx.mcd, "ERROR %d %s: " fmt "\n", \ + __ret__, __func__, ##__VA_ARGS__) + +#define mc_dev_info(fmt, ...) \ + dev_info(g_ctx.mcd, "%s: " fmt "\n", __func__, ##__VA_ARGS__) + +#ifdef DEBUG +#define mc_dev_devel(fmt, ...) \ + dev_info(g_ctx.mcd, "%s: " fmt "\n", __func__, ##__VA_ARGS__) +#else /* DEBUG */ +#define mc_dev_devel(...) do {} while (0) +#endif /* !DEBUG */ + +#define TEEC_TT_LOGIN_KERNEL 0x80000000 + +#define TEE_START_NOT_TRIGGERED 1 + +/* MobiCore Driver Kernel Module context data. */ +struct mc_device_ctx { + struct device *mcd; + /* debugfs root */ + struct dentry *debug_dir; + + /* Debug counters */ + atomic_t c_clients; + atomic_t c_cbufs; + atomic_t c_cwsms; + atomic_t c_sessions; + atomic_t c_wsms; + atomic_t c_mmus; + atomic_t c_maps; + atomic_t c_slots; + atomic_t c_xen_maps; + atomic_t c_xen_fes; +}; + +extern struct mc_device_ctx g_ctx; + +/* Debug stuff */ +struct kasnprintf_buf { + struct mutex mutex; /* Protect buf/size/off access */ + gfp_t gfp; + void *buf; + int size; + int off; +}; + +/* Wait for TEE to start and get status */ +int mc_wait_tee_start(void); + +extern __printf(2, 3) +int kasnprintf(struct kasnprintf_buf *buf, const char *fmt, ...); +ssize_t debug_generic_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos, + int (*function)(struct kasnprintf_buf *buf)); +int debug_generic_open(struct inode *inode, struct file *file); +int debug_generic_release(struct inode *inode, struct file *file); + +#if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE +static inline unsigned int kref_read(struct kref *kref) +{ + return atomic_read(&kref->refcount); +} +#endif + +/* Xen support */ + +#ifdef CONFIG_XEN +#if KERNEL_VERSION(4, 4, 0) <= LINUX_VERSION_CODE +#define TRUSTONIC_XEN_DOMU +#endif +#endif + +static inline bool is_xen_dom0(void) +{ +#if KERNEL_VERSION(3, 18, 0) <= LINUX_VERSION_CODE + return xen_domain() && xen_initial_domain(); +#else + return false; +#endif +} + +static inline bool is_xen_domu(void) +{ +#ifdef TRUSTONIC_XEN_DOMU + return xen_domain() && !xen_initial_domain(); +#else + return false; +#endif +} + +#endif /* _MC_MAIN_H_ */ diff --git a/drivers/gud/MobiCoreDriver/mc_admin.h b/drivers/gud/MobiCoreDriver/mc_admin.h new file mode 100644 index 000000000000..b66fff693290 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mc_admin.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef __MC_ADMIN_IOCTL_H__ +#define __MC_ADMIN_IOCTL_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MC_ADMIN_DEVNODE "mobicore" + +/* Driver/daemon commands */ +enum { + /* Command 0 is reserved */ + MC_DRV_GET_ROOT_CONTAINER = 1, + MC_DRV_GET_SP_CONTAINER = 2, + MC_DRV_GET_TRUSTLET_CONTAINER = 3, + MC_DRV_GET_TRUSTLET = 4, + MC_DRV_SIGNAL_CRASH = 5, +}; + +/* MobiCore IOCTL magic number */ +#define MC_IOC_MAGIC 'M' + +struct mc_admin_request { + __u32 request_id; /* Unique request identifier */ + __u32 command; /* Command to daemon */ + struct mc_uuid_t uuid; /* UUID of trustlet, if relevant */ + __u32 is_gp; /* Whether trustlet is GP */ + __u32 spid; /* SPID of trustlet, if relevant */ +}; + +struct mc_admin_response { + __u32 request_id; /* Unique request identifier */ + __u32 error_no; /* Errno from daemon */ + __u32 spid; /* SPID of trustlet, if relevant */ + __u32 service_type; /* Type of trustlet being returned */ + __u32 length; /* Length of data to get */ + /* Any data follows */ +}; + +struct mc_admin_driver_info { + /* Version, and something else..*/ + __u32 drv_version; + __u32 initial_cmd_id; +}; + +struct mc_admin_load_info { + __u32 spid; /* SPID of trustlet, if relevant */ + __u64 address; /* Address of the data */ + __u32 length; /* Length of data to get */ + struct mc_uuid_t uuid; /* UUID of trustlet, if relevant */ +}; + +#define MC_ADMIN_IO_GET_DRIVER_REQUEST \ + _IOR(MC_IOC_MAGIC, 0, struct mc_admin_request) +#define MC_ADMIN_IO_GET_INFO \ + _IOR(MC_IOC_MAGIC, 1, struct mc_admin_driver_info) +#define MC_ADMIN_IO_LOAD_DRIVER \ + _IOW(MC_IOC_MAGIC, 2, struct mc_admin_load_info) +#define MC_ADMIN_IO_LOAD_TOKEN \ + _IOW(MC_IOC_MAGIC, 3, struct mc_admin_load_info) +#define MC_ADMIN_IO_LOAD_CHECK \ + _IOW(MC_IOC_MAGIC, 4, struct mc_admin_load_info) +#define MC_ADMIN_IO_LOAD_KEY_SO \ + _IOW(MC_IOC_MAGIC, 5, struct mc_admin_load_info) + +#ifdef __cplusplus +} +#endif +#endif /* __MC_ADMIN_IOCTL_H__ */ diff --git a/drivers/gud/MobiCoreDriver/mc_linux_api.h b/drivers/gud/MobiCoreDriver/mc_linux_api.h new file mode 100644 index 000000000000..591c27c9b37b --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mc_linux_api.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (c) 2013-2019 TRUSTONIC LIMITED + * 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 + * 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. + */ +#ifndef _MC_LINUX_API_H_ +#define _MC_LINUX_API_H_ + +#include + +/* + * Manage dynamically switch TEE worker threads/TEE affinity to big core only. + * Or default core affinity. + * Warning: Both PLAT_DEFAULT_TEE_AFFINITY_MASK and + * BIG_CORE_SWITCH_AFFINITY_MASK have to be defined + */ +#if defined(BIG_CORE_SWITCH_AFFINITY_MASK) +void set_tee_worker_threads_on_big_core(bool big_core); +#endif + +#endif /* _MC_LINUX_API_H_ */ diff --git a/drivers/gud/MobiCoreDriver/mc_user.h b/drivers/gud/MobiCoreDriver/mc_user.h new file mode 100644 index 000000000000..b8b32c26ec3d --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mc_user.h @@ -0,0 +1,307 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (c) 2013-2018 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef _MC_USER_H_ +#define _MC_USER_H_ + +#define MCDRVMODULEAPI_VERSION_MAJOR 7 +#define MCDRVMODULEAPI_VERSION_MINOR 0 + +#include + +#ifndef __KERNEL__ +#define BIT(n) (1 << (n)) +#endif /* __KERNEL__ */ + +#define MC_USER_DEVNODE "mobicore-user" + +/** Maximum length of MobiCore product ID string. */ +#define MC_PRODUCT_ID_LEN 64 + +/** Number of buffers that can be mapped at once */ +#define MC_MAP_MAX 4 + +/* Max length for buffers */ +#define MC_MAX_TCI_LEN 0x100000 +#define BUFFER_LENGTH_MAX 0x40000000 + +/* Max length for objects */ +#define OBJECT_LENGTH_MAX 0x8000000 + +/* Flags for buffers to map (aligned on GP) */ +#define MC_IO_MAP_INPUT BIT(0) +#define MC_IO_MAP_OUTPUT BIT(1) +#define MC_IO_MAP_INPUT_OUTPUT (MC_IO_MAP_INPUT | MC_IO_MAP_OUTPUT) + +/* + * Universally Unique Identifier (UUID) according to ISO/IEC 11578. + */ +struct mc_uuid_t { + __u8 value[16]; /* Value of the UUID */ +}; + +/* + * GP TA login types. + */ +enum mc_login_type { + LOGIN_PUBLIC = 0, + LOGIN_USER, + LOGIN_GROUP, + LOGIN_APPLICATION = 4, + LOGIN_USER_APPLICATION, + LOGIN_GROUP_APPLICATION, +}; + +/* + * GP TA identity structure. + */ +struct mc_identity { + enum mc_login_type login_type; + union { + __u8 login_data[16]; + gid_t gid; /* Requested group id */ + struct { + uid_t euid; + uid_t ruid; + } uid; + }; +}; + +/* + * Data exchange structure of the MC_IO_OPEN_SESSION ioctl command. + */ +struct mc_ioctl_open_session { + struct mc_uuid_t uuid; /* trustlet uuid */ + __u32 is_gp_uuid; /* uuid is for GP TA */ + __u32 sid; /* session id (out) */ + __u64 tci; /* tci buffer pointer */ + __u32 tcilen; /* tci length */ + struct mc_identity identity; /* GP TA identity */ +}; + +/* + * Data exchange structure of the MC_IO_OPEN_TRUSTLET ioctl command. + */ +struct mc_ioctl_open_trustlet { + __u32 sid; /* session id (out) */ + __u32 spid; /* trustlet spid */ + __u64 buffer; /* trustlet binary pointer */ + __u32 tlen; /* binary length */ + __u64 tci; /* tci buffer pointer */ + __u32 tcilen; /* tci length */ +}; + +/* + * Data exchange structure of the MC_IO_WAIT ioctl command. + */ +struct mc_ioctl_wait { + __u32 sid; /* session id (in) */ + __s32 timeout; /* notification timeout */ + __u32 partial; /* for proxy server to retry silently */ +}; + +/* + * Data exchange structure of the MC_IO_ALLOC ioctl command. + */ +struct mc_ioctl_alloc { + __u32 len; /* buffer length */ + __u32 handle; /* user handle for the buffer (out) */ +}; + +/* + * Buffer mapping incoming and outgoing information. + */ +struct mc_ioctl_buffer { + __u64 va; /* user space address of buffer */ + __u32 len; /* buffer length */ + __u64 sva; /* SWd virt address of buffer (out) */ + __u32 flags; /* buffer flags */ +}; + +/* + * Data exchange structure of the MC_IO_MAP and MC_IO_UNMAP ioctl commands. + */ +struct mc_ioctl_map { + __u32 sid; /* session id */ + struct mc_ioctl_buffer buf; /* buffers info */ +}; + +/* + * Data exchange structure of the MC_IO_ERR ioctl command. + */ +struct mc_ioctl_geterr { + __u32 sid; /* session id */ + __s32 value; /* error value (out) */ +}; + +/* + * Global MobiCore Version Information. + */ +struct mc_version_info { + char product_id[MC_PRODUCT_ID_LEN]; /* Product ID string */ + __u32 version_mci; /* Mobicore Control Interface */ + __u32 version_so; /* Secure Objects */ + __u32 version_mclf; /* MobiCore Load Format */ + __u32 version_container; /* MobiCore Container Format */ + __u32 version_mc_config; /* MobiCore Config. Block Format */ + __u32 version_tl_api; /* MobiCore Trustlet API */ + __u32 version_dr_api; /* MobiCore Driver API */ + __u32 version_nwd; /* This Driver */ +}; + +/* + * GP TA operation structure. + */ +struct gp_value { + __u32 a; + __u32 b; +}; + +struct gp_temp_memref { + __u64 buffer; + __u64 size; +}; + +struct gp_shared_memory { + __u64 buffer; + __u64 size; + __u32 flags; +}; + +struct gp_regd_memref { + struct gp_shared_memory parent; + __u64 size; + __u64 offset; +}; + +union gp_param { + struct gp_temp_memref tmpref; + struct gp_regd_memref memref; + struct gp_value value; +}; + +struct gp_operation { + __u32 started; + __u32 param_types; + union gp_param params[4]; +}; + +struct gp_return { + __u32 origin; + __u32 value; +}; + +/* + * Data exchange structure of the MC_IO_GP_INITIALIZE_CONTEXT ioctl command. + */ +struct mc_ioctl_gp_initialize_context { + struct gp_return ret; /* return origin/value (out) */ +}; + +/* + * Data exchange structure of the MC_IO_GP_REGISTER_SHARED_MEM ioctl command. + */ +struct mc_ioctl_gp_register_shared_mem { + struct gp_shared_memory memref; + struct gp_return ret; /* return origin/value (out) */ +}; + +/* + * Data exchange structure of the MC_IO_GP_RELEASE_SHARED_MEM ioctl command. + */ +struct mc_ioctl_gp_release_shared_mem { + struct gp_shared_memory memref; +}; + +/* + * Data exchange structure of the MC_IO_GP_OPEN_SESSION ioctl command. + */ +struct mc_ioctl_gp_open_session { + struct mc_uuid_t uuid; /* trustlet uuid */ + struct mc_identity identity; /* GP TA identity */ + struct gp_operation operation; /* set of parameters */ + struct gp_return ret; /* return origin/value (out) */ + __u32 session_id; /* session id (out) */ +}; + +/* + * Data exchange structure of the MC_IO_GP_CLOSE_SESSION ioctl command. + */ +struct mc_ioctl_gp_close_session { + __u32 session_id; /* session id */ +}; + +/* + * Data exchange structure of the MC_IO_GP_INVOKE_COMMAND ioctl command. + */ +struct mc_ioctl_gp_invoke_command { + struct gp_operation operation; /* set of parameters */ + __u32 session_id; /* session id */ + __u32 command_id; /* ID of the command */ + struct gp_return ret; /* return origin/value (out) */ +}; + +/* + * Data exchange structure of the MC_IO_GP_CANCEL ioctl command. + */ +struct mc_ioctl_gp_request_cancellation { + struct gp_operation operation; /* set of parameters */ +}; + +/* + * defines for the ioctl mobicore driver module function call from user space. + */ +/* MobiCore IOCTL magic number */ +#define MC_IOC_MAGIC 'M' + +/* + * Implement corresponding functions from user api + */ +#define MC_IO_OPEN_SESSION \ + _IOWR(MC_IOC_MAGIC, 0, struct mc_ioctl_open_session) +#define MC_IO_OPEN_TRUSTLET \ + _IOWR(MC_IOC_MAGIC, 1, struct mc_ioctl_open_trustlet) +#define MC_IO_CLOSE_SESSION \ + _IO(MC_IOC_MAGIC, 2) +#define MC_IO_NOTIFY \ + _IO(MC_IOC_MAGIC, 3) +#define MC_IO_WAIT \ + _IOW(MC_IOC_MAGIC, 4, struct mc_ioctl_wait) +#define MC_IO_MAP \ + _IOWR(MC_IOC_MAGIC, 5, struct mc_ioctl_map) +#define MC_IO_UNMAP \ + _IOW(MC_IOC_MAGIC, 6, struct mc_ioctl_map) +#define MC_IO_ERR \ + _IOWR(MC_IOC_MAGIC, 7, struct mc_ioctl_geterr) +#define MC_IO_HAS_SESSIONS \ + _IO(MC_IOC_MAGIC, 8) +#define MC_IO_VERSION \ + _IOR(MC_IOC_MAGIC, 9, struct mc_version_info) +#define MC_IO_GP_INITIALIZE_CONTEXT \ + _IOW(MC_IOC_MAGIC, 20, struct mc_ioctl_gp_initialize_context) +#define MC_IO_GP_REGISTER_SHARED_MEM \ + _IOWR(MC_IOC_MAGIC, 21, struct mc_ioctl_gp_register_shared_mem) +#define MC_IO_GP_RELEASE_SHARED_MEM \ + _IOW(MC_IOC_MAGIC, 23, struct mc_ioctl_gp_release_shared_mem) +#define MC_IO_GP_OPEN_SESSION \ + _IOWR(MC_IOC_MAGIC, 24, struct mc_ioctl_gp_open_session) +#define MC_IO_GP_CLOSE_SESSION \ + _IOW(MC_IOC_MAGIC, 25, struct mc_ioctl_gp_close_session) +#define MC_IO_GP_INVOKE_COMMAND \ + _IOWR(MC_IOC_MAGIC, 26, struct mc_ioctl_gp_invoke_command) +#define MC_IO_GP_REQUEST_CANCELLATION \ + _IOW(MC_IOC_MAGIC, 27, struct mc_ioctl_gp_request_cancellation) + +#endif /* _MC_USER_H_ */ diff --git a/drivers/gud/MobiCoreDriver/mcifc.h b/drivers/gud/MobiCoreDriver/mcifc.h new file mode 100644 index 000000000000..cb9d151ced64 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mcifc.h @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ +#ifndef MCIFC_H_ +#define MCIFC_H_ + +#include "platform.h" + +/** @name MobiCore FastCall Definition + * Defines for the two different FastCall's. + */ +/**/ + +/* --- global ---- */ +#define MC_FC_INVALID ((u32)0) /**< Invalid FastCall ID */ + +#if (defined(CONFIG_ARM64) && !defined(MC_ARMV7_FC)) || (defined(MC_AARCH32_FC)) + +#define FASTCALL_OWNER_TZOS (0x3F000000) +#define FASTCALL_ATOMIC_MASK BIT(31) +/**Trusted OS Fastcalls SMC32 */ +#define MC_FC_STD32_BASE \ + ((u32)(FASTCALL_OWNER_TZOS | FASTCALL_ATOMIC_MASK)) +/* SMC32 Trusted OS owned Fastcalls */ +#define MC_FC_STD32(x) ((u32)(MC_FC_STD32_BASE + (x))) + +#define MC_FC_INIT MC_FC_STD32(1) /**< Initializing FastCall. */ +#define MC_FC_INFO MC_FC_STD32(2) /**< Info FastCall. */ +#define MC_FC_MEM_TRACE MC_FC_STD32(10) /**< Enable SWd tracing via memory */ + +#else + +#define MC_FC_INIT ((u32)(-1)) /**< Initializing FastCall. */ +#define MC_FC_INFO ((u32)(-2)) /**< Info FastCall. */ +#define MC_FC_MEM_TRACE ((u32)(-31)) /**< Enable SWd tracing via memory */ + +#endif + +/** @} */ + +/** @name MobiCore SMC Definition + * Defines the different secure monitor calls (SMC) for world switching. + */ +/**< Yield to switch from NWd to SWd. */ +#define MC_SMC_N_YIELD 3 +/**< SIQ to switch from NWd to SWd. */ +#define MC_SMC_N_SIQ 4 +/** @} */ + +/** @name MobiCore status + * MobiCore status information. + */ +/**< MobiCore is not yet initialized. FastCall FcInit() to set up MobiCore.*/ +#define MC_STATUS_NOT_INITIALIZED 0 +/**< Bad parameters have been passed in FcInit(). */ +#define MC_STATUS_BAD_INIT 1 +/**< MobiCore did initialize properly. */ +#define MC_STATUS_INITIALIZED 2 +/**< MobiCore kernel halted due to an unrecoverable exception. Further + * information is available extended info + */ +#define MC_STATUS_HALT 3 +/** @} */ + +/** @name Extended Info Identifiers + * Extended info parameters for MC_FC_INFO to obtain further information + * depending on MobiCore state. + */ +/**< Version of the MobiCore Control Interface (MCI) */ +#define MC_EXT_INFO_ID_MCI_VERSION 0 +/**< MobiCore control flags */ +#define MC_EXT_INFO_ID_FLAGS 1 +/**< MobiCore halt condition code */ +#define MC_EXT_INFO_ID_HALT_CODE 2 +/**< MobiCore halt condition instruction pointer */ +#define MC_EXT_INFO_ID_HALT_IP 3 +/**< MobiCore fault counter */ +#define MC_EXT_INFO_ID_FAULT_CNT 4 +/**< MobiCore last fault cause */ +#define MC_EXT_INFO_ID_FAULT_CAUSE 5 +/**< MobiCore last fault meta */ +#define MC_EXT_INFO_ID_FAULT_META 6 +/**< MobiCore last fault threadid */ +#define MC_EXT_INFO_ID_FAULT_THREAD 7 +/**< MobiCore last fault instruction pointer */ +#define MC_EXT_INFO_ID_FAULT_IP 8 +/**< MobiCore last fault stack pointer */ +#define MC_EXT_INFO_ID_FAULT_SP 9 +/**< MobiCore last fault ARM arch information */ +#define MC_EXT_INFO_ID_FAULT_ARCH_DFSR 10 +/**< MobiCore last fault ARM arch information */ +#define MC_EXT_INFO_ID_FAULT_ARCH_ADFSR 11 +/**< MobiCore last fault ARM arch information */ +#define MC_EXT_INFO_ID_FAULT_ARCH_DFAR 12 +/**< MobiCore last fault ARM arch information */ +#define MC_EXT_INFO_ID_FAULT_ARCH_IFSR 13 +/**< MobiCore last fault ARM arch information */ +#define MC_EXT_INFO_ID_FAULT_ARCH_AIFSR 14 +/**< MobiCore last fault ARM arch information */ +#define MC_EXT_INFO_ID_FAULT_ARCH_IFAR 15 +/**< MobiCore configured by Daemon via fc_init flag */ +#define MC_EXT_INFO_ID_MC_CONFIGURED 16 +/**< MobiCore scheduling status: idle/non-idle */ +#define MC_EXT_INFO_ID_MC_SCHED_STATUS 17 +/**< MobiCore runtime status: initialized, halted */ +#define MC_EXT_INFO_ID_MC_STATUS 18 +/**< MobiCore exception handler last partner */ +#define MC_EXT_INFO_ID_MC_EXC_PARTNER 19 +/**< MobiCore exception handler last peer */ +#define MC_EXT_INFO_ID_MC_EXC_IPCPEER 20 +/**< MobiCore exception handler last IPC message */ +#define MC_EXT_INFO_ID_MC_EXC_IPCMSG 21 +/**< MobiCore exception handler last IPC data */ +#define MC_EXT_INFO_ID_MC_EXC_IPCDATA 22 +/**< MobiCore exception handler last UUID (uses 4 slots: 23 to 26) */ +#define MC_EXT_INFO_ID_MC_EXC_UUID 23 +#define MC_EXT_INFO_ID_MC_EXC_UUID1 24 +#define MC_EXT_INFO_ID_MC_EXC_UUID2 25 +#define MC_EXT_INFO_ID_MC_EXC_UUID3 26 +/**< MobiCore exception handler last crashing task offset */ +#define MC_EXT_INFO_ID_TASK_OFFSET 27 +/**< MobiCore exception handler last crashing task's mclib offset */ +#define MC_EXT_INFO_ID_MCLIB_OFFSET 28 + +/** @} */ + +/** @name FastCall return values + * Return values of the MobiCore FastCalls. + */ +/**< No error. Everything worked fine. */ +#define MC_FC_RET_OK 0 +/**< FastCall was not successful. */ +#define MC_FC_RET_ERR_INVALID 1 +/**< MobiCore has already been initialized. */ +#define MC_FC_RET_ERR_ALREADY_INITIALIZED 5 +/**< Call is not allowed. */ +#define TEE_FC_RET_ERR_NOABILITY 6 +/** @} */ + +/** @name Init FastCall flags + * Return flags of the Init FastCall. + */ +/**< SWd uses LPAE MMU table format. */ +#define MC_FC_INIT_FLAG_LPAE BIT(0) +/** @} */ + +#endif /** MCIFC_H_ */ + +/** @} */ diff --git a/drivers/gud/MobiCoreDriver/mciiwp.h b/drivers/gud/MobiCoreDriver/mciiwp.h new file mode 100644 index 000000000000..5a3ff3bf0e10 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mciiwp.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef MCIIWP_H_ +#define MCIIWP_H_ + +#include "tee_client_types.h" /* teec_uuid FIXME it's all mixed up! */ + +/** Session ID for notifications for the Dragon CA-to-TA communication protocol + * + * Session ID are distinct from any valid MCP session identifier + * and from the existing pseudo-session identifiers : + * - SID_MCP = 0 + * - SID_INVALID = 0xffffffff + * + * A session ID is a thread ID, and since thread IDs have a nonzero task ID as + * their lowest 16 bits, we can use values of the form 0x????0000 + */ +#define SID_OPEN_SESSION (0x00010000) +#define SID_INVOKE_COMMAND (0x00020000) +#define SID_CLOSE_SESSION (0x00030000) +#define SID_CANCEL_OPERATION (0x00040000) +#define SID_MEMORY_REFERENCE (0x00050000) +#define SID_OPEN_TA (0x00060000) +#define SID_REQ_TA (0x00070000) + +/* To quickly detect IWP notifications */ +#define SID_IWP_NOTIFICATION \ + (SID_OPEN_SESSION | SID_INVOKE_COMMAND | SID_CLOSE_SESSION | \ + SID_CANCEL_OPERATION | SID_MEMORY_REFERENCE | SID_OPEN_TA | SID_REQ_TA) + +struct interworld_parameter_value { + u32 a; + u32 b; + u8 unused[8]; +}; + +/** The API parameter type TEEC_MEMREF_WHOLE is translated into these types + * and does not appear in the inter-world protocol. + * + * - memref_handle references a previously registered memory reference + * 'offset' bytes <= memref_handle < 'offset + size' bytes + * + * These sizes must be contained within the memory reference. + */ +struct interworld_parameter_memref { + u32 offset; + u32 size; + u32 memref_handle; + u32 unused; +}; + +/** This structure is used for the parameter types TEEC_MEMREF_TEMP_xxx. + * + * The parameter is located in World Shared Memory which is established + * for the command and torn down afterwards. + * + * The number of pages to share is 'size + offset' divided by the page + * size, rounded up. + * Inside the shared pages, the buffer starts at address 'offset' + * and ends after 'size' bytes. + * + * - wsm_type parameter may be WSM_CONTIGUOUS or WSM_L1. + * - offset must be less than the page size (4096). + * - size must be less than 0xfffff000. + */ +struct interworld_parameter_tmpref { + u16 wsm_type; + u16 offset; + u32 size; + u64 physical_address; +}; + +/** + * + */ +union interworld_parameter { + struct interworld_parameter_value value; + struct interworld_parameter_memref memref; + struct interworld_parameter_tmpref tmpref; +}; + +/** + * An inter-world session structure represents an active session between + * a normal world client and RTM. + * It is located in the MCI buffer, must be 8-byte aligned + * + * NB : since the session structure is in shared memory, it must have the + * same layout on both sides (normal world kernel and RTM). + * All types use platform endianness (specifically, the endianness used by + * the secure world). + */ +struct interworld_session { + u32 status; + u32 return_origin; + u16 session_handle; + u16 param_types; + + union { + u32 command_id; /** invoke-command only */ + u32 login; /** open-session only */ + }; + + union interworld_parameter params[4]; + + /* The following fields are only used during open-session */ + struct teec_uuid target_uuid; + struct teec_uuid client_uuid; +}; + +#endif /** MCIIWP_H_ */ diff --git a/drivers/gud/MobiCoreDriver/mcimcp.h b/drivers/gud/MobiCoreDriver/mcimcp.h new file mode 100644 index 000000000000..992dcc4a7c4e --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mcimcp.h @@ -0,0 +1,487 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2018 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef MCP_H_ +#define MCP_H_ + +#include "mcloadformat.h" + +/** Indicates a response */ +#define FLAG_RESPONSE BIT(31) + +/** MobiCore Return Code Defines. + * List of the possible MobiCore return codes. + */ +enum mcp_result { + /** Memory has successfully been mapped */ + MC_MCP_RET_OK = 0, + /** The session ID is invalid */ + MC_MCP_RET_ERR_INVALID_SESSION = 1, + /** The UUID of the Trustlet is unknown */ + MC_MCP_RET_ERR_UNKNOWN_UUID = 2, + /** The ID of the driver is unknown */ + MC_MCP_RET_ERR_UNKNOWN_DRIVER_ID = 3, + /** No more session are allowed */ + MC_MCP_RET_ERR_NO_MORE_SESSIONS = 4, + /** The container is invalid */ + MC_MCP_RET_ERR_CONTAINER_INVALID = 5, + /** The Trustlet is invalid */ + MC_MCP_RET_ERR_TRUSTLET_INVALID = 6, + /** The memory block has already been mapped before */ + MC_MCP_RET_ERR_ALREADY_MAPPED = 7, + /** Alignment or length error in the command parameters */ + MC_MCP_RET_ERR_INVALID_PARAM = 8, + /** No space left in the virtual address space of the session */ + MC_MCP_RET_ERR_OUT_OF_RESOURCES = 9, + /** WSM type unknown or broken WSM */ + MC_MCP_RET_ERR_INVALID_WSM = 10, + /** unknown error */ + MC_MCP_RET_ERR_UNKNOWN = 11, + /** Length of map invalid */ + MC_MCP_RET_ERR_INVALID_MAPPING_LENGTH = 12, + /** Map can only be applied to Trustlet session */ + MC_MCP_RET_ERR_MAPPING_TARGET = 13, + /** Couldn't open crypto session */ + MC_MCP_RET_ERR_OUT_OF_CRYPTO_RESOURCES = 14, + /** System Trustlet signature verification failed */ + MC_MCP_RET_ERR_SIGNATURE_VERIFICATION_FAILED = 15, + /** System Trustlet public key is wrong */ + MC_MCP_RET_ERR_WRONG_PUBLIC_KEY = 16, + /** Wrong containter type(s) */ + MC_MCP_RET_ERR_CONTAINER_TYPE_MISMATCH = 17, + /** Container is locked (or not activated) */ + MC_MCP_RET_ERR_CONTAINER_LOCKED = 18, + /** SPID is not registered with root container */ + MC_MCP_RET_ERR_SP_NO_CHILD = 19, + /** UUID is not registered with sp container */ + MC_MCP_RET_ERR_TL_NO_CHILD = 20, + /** Unwrapping of root container failed */ + MC_MCP_RET_ERR_UNWRAP_ROOT_FAILED = 21, + /** Unwrapping of service provider container failed */ + MC_MCP_RET_ERR_UNWRAP_SP_FAILED = 22, + /** Unwrapping of Trustlet container failed */ + MC_MCP_RET_ERR_UNWRAP_TRUSTLET_FAILED = 23, + /** Container version mismatch */ + MC_MCP_RET_ERR_CONTAINER_VERSION_MISMATCH = 24, + /** Decryption of service provider trustlet failed */ + MC_MCP_RET_ERR_SP_TL_DECRYPTION_FAILED = 25, + /** Hash check of service provider trustlet failed */ + MC_MCP_RET_ERR_SP_TL_HASH_CHECK_FAILED = 26, + /** Activation/starting of task failed */ + MC_MCP_RET_ERR_LAUNCH_TASK_FAILED = 27, + /** Closing of task not yet possible, try again later */ + MC_MCP_RET_ERR_CLOSE_TASK_FAILED = 28, + /**< Service is blocked and a session cannot be opened to it */ + MC_MCP_RET_ERR_SERVICE_BLOCKED = 29, + /**< Service is locked and a session cannot be opened to it */ + MC_MCP_RET_ERR_SERVICE_LOCKED = 30, + /**< Service was forcefully killed (due to an administrative command) */ + MC_MCP_RET_ERR_SERVICE_KILLED = 31, + /**< Service version is lower than the one installed. */ + MC_MCP_RET_ERR_DOWNGRADE_NOT_AUTHORIZED = 32, + /**< Filesystem not yet ready. */ + MC_MCP_RET_ERR_SYSTEM_NOT_READY = 33, + /** The command is unknown */ + MC_MCP_RET_ERR_UNKNOWN_COMMAND = 50, + /** The command data is invalid */ + MC_MCP_RET_ERR_INVALID_DATA = 51 +}; + +/** Possible MCP Command IDs + * Command ID must be between 0 and 0x7FFFFFFF. + */ +enum cmd_id { + /** Invalid command ID */ + MC_MCP_CMD_ID_INVALID = 0x00, + /** Open a session */ + MC_MCP_CMD_OPEN_SESSION = 0x01, + /** Close an existing session */ + MC_MCP_CMD_CLOSE_SESSION = 0x03, + /** Map WSM to session */ + MC_MCP_CMD_MAP = 0x04, + /** Unmap WSM from session */ + MC_MCP_CMD_UNMAP = 0x05, + /** Prepare for suspend */ + MC_MCP_CMD_SUSPEND = 0x06, + /** Resume from suspension */ + MC_MCP_CMD_RESUME = 0x07, + /** Get MobiCore version information */ + MC_MCP_CMD_GET_MOBICORE_VERSION = 0x09, + /** Close MCP and unmap MCI */ + MC_MCP_CMD_CLOSE_MCP = 0x0A, + /** Load token for device attestation */ + MC_MCP_CMD_LOAD_TOKEN = 0x0B, + /** Check that TA can be loaded */ + MC_MCP_CMD_CHECK_LOAD_TA = 0x0C, + /** Load a decryption key */ + MC_MCP_CMD_LOAD_SYSENC_KEY_SO = 0x0D, +}; + +/* + * Types of WSM known to the MobiCore. + */ +#define WSM_TYPE_MASK 0xFF +#define WSM_INVALID 0 /** Invalid memory type */ +#define WSM_L1 3 /** Buffer mapping uses fake L1 table */ +/**< Bitflag indicating that the buffer should be uncached */ +#define WSM_UNCACHED 0x100 + +/* + * Magic number used to identify if Open Command supports GP client + * authentication. + */ +#define MC_GP_CLIENT_AUTH_MAGIC 0x47504131 /* "GPA1" */ + +/* + * Initialisation values flags + */ +/* Set if IRQ is present */ +#define MC_IV_FLAG_IRQ BIT(0) +/* Set if GP TIME is supported */ +#define MC_IV_FLAG_TIME BIT(1) +/* Set if GP client uses interworld session */ +#define MC_IV_FLAG_IWP BIT(2) + +struct init_values { + u32 flags; + u32 irq; + u32 time_ofs; + u32 time_len; + /* interworld session buffer offset in MCI */ + u32 iws_buf_ofs; + /* interworld session buffer size */ + u32 iws_buf_size; + u8 padding[8]; +}; + +/** Command header. + * It just contains the command ID. Only values specified in cmd_id are + * allowed as command IDs. If the command ID is unspecified the MobiCore + * returns an empty response with the result set to + * MC_MCP_RET_ERR_UNKNOWN_COMMAND. + */ +struct cmd_header { + enum cmd_id cmd_id; /** Command ID of the command */ +}; + +/** Response header. + * MobiCore will reply to every MCP command with an MCP response. Like the MCP + * command the response consists of a header followed by response data. The + * response is written to the same memory location as the MCP command. + */ +struct rsp_header { + u32 rsp_id; /** Command ID | FLAG_RESPONSE */ + enum mcp_result result; /** Result of the command execution */ +}; + +/** @defgroup CMD MCP Commands + */ + +/** @defgroup ASMCMD Administrative Commands + */ + +/** @defgroup MCPGETMOBICOREVERSION GET_MOBICORE_VERSION + * Get MobiCore version info. + * + */ + +/** Get MobiCore Version Command */ +struct cmd_get_version { + struct cmd_header cmd_header; /** Command header */ +}; + +/** Get MobiCore Version Command Response */ +struct rsp_get_version { + struct rsp_header rsp_header; /** Response header */ + struct mc_version_info version_info; /** MobiCore version info */ +}; + +/** @defgroup POWERCMD Power Management Commands + */ + +/** @defgroup MCPSUSPEND SUSPEND + * Prepare MobiCore suspension. + * This command allows MobiCore and MobiCore drivers to release or clean + * resources and save device state. + * + */ + +/** Suspend Command */ +struct cmd_suspend { + struct cmd_header cmd_header; /** Command header */ +}; + +/** Suspend Command Response */ +struct rsp_suspend { + struct rsp_header rsp_header; /** Response header */ +}; + +/** @defgroup MCPRESUME RESUME + * Resume MobiCore from suspension. + * This command allows MobiCore and MobiCore drivers to reinitialize hardware + * affected by suspension. + * + */ + +/** Resume Command */ +struct cmd_resume { + struct cmd_header cmd_header; /** Command header */ +}; + +/** Resume Command Response */ +struct rsp_resume { + struct rsp_header rsp_header; /** Response header */ +}; + +/** @defgroup SESSCMD Session Management Commands + */ + +/** @defgroup MCPOPEN OPEN + * Load and open a session to a Trustlet. + * The OPEN command loads Trustlet data to the MobiCore context and opens a + * session to the Trustlet. If wsm_data_type is WSM_INVALID MobiCore tries to + * start a pre-installed Trustlet associated with the uuid passed. The uuid + * passed must match the uuid contained in the load data (if available). + * On success, MobiCore returns the session ID which can be used for further + * communication. + */ + +/** GP client authentication data */ +struct cmd_open_data { + u32 mclf_magic; /** ASCII "MCLF" on older versions */ + struct identity identity; /** Login method and data */ +}; + +/** Open Command */ +struct cmd_open { + struct cmd_header cmd_header; /** Command header */ + struct mc_uuid_t uuid; /** Service UUID */ + u8 unused[4]; /** Padding to be 64-bit aligned */ + u64 adr_tci_buffer; /** Physical address of the TCI MMU */ + u64 adr_load_data; /** Physical address of the data MMU */ + u32 ofs_tci_buffer; /** Offset to the data */ + u32 len_tci_buffer; /** Length of the TCI */ + u32 wsmtype_tci; /** Type of WSM used for the TCI */ + u32 wsm_data_type; /** Type of MMU */ + u32 ofs_load_data; /** Offset to the data */ + u32 len_load_data; /** Length of the data to load */ + union { + struct cmd_open_data cmd_open_data; /** Client login data */ + union mclf_header tl_header; /** Service header */ + }; + u32 is_gpta; /** true if looking for an SD/GP-TA */ +}; + +/** Open Command Response */ +struct rsp_open { + struct rsp_header rsp_header; /** Response header */ + u32 session_id; /** Session ID */ +}; + +/** TA Load Check Command */ +struct cmd_check_load { + struct cmd_header cmd_header; /** Command header */ + struct mc_uuid_t uuid; /** Service UUID */ + u8 unused[4]; /** Padding to be 64-bit aligned */ + u64 adr_load_data; /** Physical address of the data */ + u32 wsm_data_type; /** Type of MMU */ + u32 ofs_load_data; /** Offset to the data */ + u32 len_load_data; /** Length of the data to load */ + union mclf_header tl_header; /** Service header */ +}; + +/** TA Load Check Response */ +struct rsp_check_load { + struct rsp_header rsp_header; /** Response header */ +}; + +/** @defgroup MCPCLOSE CLOSE + * Close an existing session to a Trustlet. + * The CLOSE command terminates a session and frees all resources in the + * MobiCore system which are currently occupied by the session. Before closing + * the session, the MobiCore runtime management waits until all pending + * operations, like calls to drivers, invoked by the Trustlet have been + * terminated. Mapped memory will automatically be unmapped from the MobiCore + * context. The NWd is responsible for processing the freed memory according to + * the Rich-OS needs. + * + */ + +/** Close Command */ +struct cmd_close { + struct cmd_header cmd_header; /** Command header */ + u32 session_id; /** Session ID */ +}; + +/** Close Command Response */ +struct rsp_close { + struct rsp_header rsp_header; /** Response header */ +}; + +/** @defgroup MCPMAP MAP + * Map a portion of memory to a session. + * The MAP command provides a block of memory to the context of a service. + * The memory then becomes world-shared memory (WSM). + * The only allowed memory type here is WSM_L1. + */ + +/** Map Command */ +struct cmd_map { + struct cmd_header cmd_header; /** Command header */ + u32 session_id; /** Session ID */ + u32 wsm_type; /** Type of MMU */ + u32 ofs_buffer; /** Offset to the payload */ + u64 adr_buffer; /** Physical address of the MMU */ + u32 len_buffer; /** Length of the buffer */ + u32 flags; /** Attributes (read/write) */ +}; + +#define MCP_MAP_MAX 0x100000 /** Maximum length for MCP map */ + +/** Map Command Response */ +struct rsp_map { + struct rsp_header rsp_header; /** Response header */ + /** Virtual address the WSM is mapped to, may include an offset! */ + u32 secure_va; +}; + +/** @defgroup MCPUNMAP UNMAP + * Unmap a portion of world-shared memory from a session. + * The UNMAP command is used to unmap a previously mapped block of + * world shared memory from the context of a session. + * + * Attention: The memory block will be immediately unmapped from the specified + * session. If the service is still accessing the memory, the service will + * trigger a segmentation fault. + */ + +/** Unmap Command */ +struct cmd_unmap { + struct cmd_header cmd_header; /** Command header */ + u32 session_id; /** Session ID */ + u32 wsm_type; /** Type of WSM used of the memory */ + /** Virtual address the WSM is mapped to, may include an offset! */ + u32 secure_va; + u32 virtual_buffer_len; /** Length of virtual buffer */ +}; + +/** Unmap Command Response */ +struct rsp_unmap { + struct rsp_header rsp_header; /** Response header */ +}; + +/** @defgroup MCPLOADTOKEN + * Load a token from the normal world and share it with the TEE + * If something fails, the device attestation functionality will be disabled + */ + +/** Load Token */ +struct cmd_load_token { + struct cmd_header cmd_header; /** Command header */ + u32 wsm_data_type; /** Type of MMU */ + u64 adr_load_data; /** Physical address of the MMU */ + u64 ofs_load_data; /** Offset to the data */ + u64 len_load_data; /** Length of the data */ +}; + +/** Load Token Command Response */ +struct rsp_load_token { + struct rsp_header rsp_header; /** Response header */ +}; + +/** @defgroup MCPLOADKEYSO + * Load a key SO from the normal world and share it with the TEE + * If something fails, the device attestation functionality will be disabled + */ + +/** Load key SO */ +struct cmd_load_key_so { + struct cmd_header cmd_header; /** Command header */ + u32 wsm_data_type; /** Type of MMU */ + u64 adr_load_data; /** Physical address of the MMU */ + u64 ofs_load_data; /** Offset to the data */ + u64 len_load_data; /** Length of the data */ +}; + +/** Load key SO Command Response */ +struct rsp_load_key_so { + struct rsp_header rsp_header; /** Response header */ +}; + +/** Structure of the MCP buffer */ +union mcp_message { + struct init_values init_values; /** Initialisation values */ + struct cmd_header cmd_header; /** Command header */ + struct rsp_header rsp_header; + struct cmd_open cmd_open; /** Load and open service */ + struct rsp_open rsp_open; + struct cmd_close cmd_close; /** Close command */ + struct rsp_close rsp_close; + struct cmd_map cmd_map; /** Map WSM to service */ + struct rsp_map rsp_map; + struct cmd_unmap cmd_unmap; /** Unmap WSM from service */ + struct rsp_unmap rsp_unmap; + struct cmd_suspend cmd_suspend; /** Suspend MobiCore */ + struct rsp_suspend rsp_suspend; + struct cmd_resume cmd_resume; /** Resume MobiCore */ + struct rsp_resume rsp_resume; + struct cmd_get_version cmd_get_version; /** Get MobiCore Version */ + struct rsp_get_version rsp_get_version; + struct cmd_load_token cmd_load_token; /** Load token */ + struct rsp_load_token rsp_load_token; + struct cmd_check_load cmd_check_load; /** TA load check */ + struct rsp_check_load rsp_check_load; + struct cmd_load_key_so cmd_load_key_so;/** Load key SO */ + struct rsp_load_key_so rsp_load_key_so; +}; + +#define MC_FLAG_NO_SLEEP_REQ 0 +#define MC_FLAG_REQ_TO_SLEEP 1 + +#define MC_STATE_NORMAL_EXECUTION 0 +#define MC_STATE_READY_TO_SLEEP 1 + +#define MC_STATE_FLAG_TEE_HALT_MASK BIT(0) + +struct sleep_mode { + u16 sleep_req; /** Ask SWd to get ready to sleep */ + u16 ready_to_sleep; /** SWd is now ready to sleep */ +}; + +/** MobiCore status flags */ +struct mcp_flags { + /** If not MC_FLAG_SCHEDULE_IDLE, MobiCore needs scheduling */ + u32 schedule; + struct sleep_mode sleep_mode; + /** Secure-world sleep timeout in milliseconds */ + s32 timeout_ms; + /** TEE flags */ + u8 tee_flags; + /** Reserved for future use */ + u8 RFU_padding[3]; +}; + +/** MobiCore is idle. No scheduling required */ +#define MC_FLAG_SCHEDULE_IDLE 0 +/** MobiCore is non idle, scheduling is required */ +#define MC_FLAG_SCHEDULE_NON_IDLE 1 + +/** MCP buffer structure */ +struct mcp_buffer { + struct mcp_flags flags; /** MobiCore Flags */ + union mcp_message message; /** MCP message buffer */ +}; + +#endif /* MCP_H_ */ diff --git a/drivers/gud/MobiCoreDriver/mcinq.h b/drivers/gud/MobiCoreDriver/mcinq.h new file mode 100644 index 000000000000..ba44aca22597 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mcinq.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef NQ_H_ +#define NQ_H_ + +/** \name NQ Size Defines + * Minimum and maximum count of elements in the notification queue. + */ +#define MIN_NQ_ELEM 1 /** Minimum notification queue elements */ +#define MAX_NQ_ELEM 64 /** Maximum notification queue elements */ + +/* Compute notification queue size in bytes from its number of elements */ +#define NQ_SIZE(n) (2 * (sizeof(struct notification_queue_header)\ + + (n) * sizeof(struct notification))) + +/** \name NQ Length Defines + * Note that there is one queue for NWd->SWd and one queue for SWd->NWd + */ +/** Minimum size for the notification queue data structure */ +#define MIN_NQ_LEN NQ_SIZE(MIN_NQ_ELEM) +/** Maximum size for the notification queue data structure */ +#define MAX_NQ_LEN NQ_SIZE(MAX_NQ_ELEM) + +/** \name Session ID Defines + * Standard Session IDs. + */ +/** MCP session ID, used to communicate with MobiCore (e.g. to start/stop TA) */ +#define SID_MCP 0 +/** Invalid session id, returned in case of error */ +#define SID_INVALID 0xffffffff + +/** Notification data structure */ +struct notification { + u32 session_id; /** Session ID */ + s32 payload; /** Additional notification info */ +}; + +/** Notification payload codes. + * 0 indicated a plain simple notification, + * a positive value is a termination reason from the task, + * a negative value is a termination reason from MobiCore. + * Possible negative values are given below. + */ +enum notification_payload { + /** task terminated, but exit code is invalid */ + ERR_INVALID_EXIT_CODE = -1, + /** task terminated due to session end, no exit code available */ + ERR_SESSION_CLOSE = -2, + /** task terminated due to invalid operation */ + ERR_INVALID_OPERATION = -3, + /** session ID is unknown */ + ERR_INVALID_SID = -4, + /** session is not active */ + ERR_SID_NOT_ACTIVE = -5, + /** session was force-killed (due to an administrative command). */ + ERR_SESSION_KILLED = -6, +}; + +/** Declaration of the notification queue header. + * layout as specified in the data structure specification. + */ +struct notification_queue_header { + u32 write_cnt; /** Write counter */ + u32 read_cnt; /** Read counter */ + u32 queue_size; /** Queue size */ +}; + +/** Queue struct which defines a queue object. + * The queue struct is accessed by the queue type of + * function. elementCnt must be a power of two and the power needs + * to be smaller than power of u32 (obviously 32). + */ +struct notification_queue { + struct notification_queue_header hdr; /** Queue header */ + struct notification notification[MIN_NQ_ELEM]; /** Elements */ +}; + +#endif /** NQ_H_ */ diff --git a/drivers/gud/MobiCoreDriver/mcitime.h b/drivers/gud/MobiCoreDriver/mcitime.h new file mode 100644 index 000000000000..e1982e3f03d8 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mcitime.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2015-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef MCITIME_H_ +#define MCITIME_H_ + +/* + * Trustonic TEE RICH OS Time: + * -seconds and nanoseconds since Jan 1, 1970, UTC + * -monotonic counter + */ +struct mcp_time { + u64 wall_clock_seconds; + u64 wall_clock_nsec; + u64 monotonic_seconds; + u64 monotonic_nsec; +}; + +#endif /* MCITIME_H_ */ diff --git a/drivers/gud/MobiCoreDriver/mcloadformat.h b/drivers/gud/MobiCoreDriver/mcloadformat.h new file mode 100644 index 000000000000..cf957761b8f7 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mcloadformat.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ +#ifndef MCLOADFORMAT_H_ +#define MCLOADFORMAT_H_ + +/** Trustlet Blob length info */ +#define MC_TLBLOBLEN_MAGIC 0x7672746C /* Magic for SWd: vrtl */ +#define MAX_SO_CONT_SIZE 512 /* Max size for a container */ + +/** MCLF magic */ +/**< "MCLF" in big endian integer representation */ +#define MC_SERVICE_HEADER_MAGIC_BE \ + ((uint32_t)('M' | ('C' << 8) | ('L' << 16) | ('F' << 24))) +/**< "MCLF" in little endian integer representation */ +#define MC_SERVICE_HEADER_MAGIC_LE \ + ((uint32_t)(('M' << 24) | ('C' << 16) | ('L' << 8) | 'F')) + +/** MCLF flags */ +/**< Loaded service cannot be unloaded from MobiCore. */ +#define MC_SERVICE_HEADER_FLAGS_PERMANENT BIT(0) +/**< Service has no WSM control interface. */ +#define MC_SERVICE_HEADER_FLAGS_NO_CONTROL_INTERFACE BIT(1) +/**< Service can be debugged. */ +#define MC_SERVICE_HEADER_FLAGS_DEBUGGABLE BIT(2) +/**< New-layout trusted application or trusted driver. */ +#define MC_SERVICE_HEADER_FLAGS_EXTENDED_LAYOUT BIT(3) + +/** Service type. + * The service type defines the type of executable. + */ +enum service_type { + SERVICE_TYPE_ILLEGAL = 0, + SERVICE_TYPE_DRIVER = 1, + SERVICE_TYPE_SP_TRUSTLET = 2, + SERVICE_TYPE_SYSTEM_TRUSTLET = 3, + SERVICE_TYPE_MIDDLEWARE = 4, + SERVICE_TYPE_LAST_ENTRY = 5, +}; + +/** + * Descriptor for a memory segment. + */ +struct segment_descriptor { + u32 start; /**< Virtual start address */ + u32 len; /**< Segment length in bytes */ +}; + +/** + * MCLF intro for data structure identification. + * Must be the first element of a valid MCLF file. + */ +struct mclf_intro { + u32 magic; /**< Header magic value ASCII "MCLF" */ + u32 version; /**< Version the MCLF header struct */ +}; + +/** + * @defgroup MCLF_VER_V2 MCLF Version 32 + * @ingroup MCLF_VER + * + * @addtogroup MCLF_VER_V2 + */ + +/* + * GP TA identity. + */ +struct identity { + /**< GP TA login type */ + u32 login_type; + /**< GP TA login data */ + u8 login_data[16]; +}; + +/** + * Version 2.1/2.2 MCLF header. + */ +struct mclf_header_v2 { + /**< MCLF header start with the mandatory intro */ + struct mclf_intro intro; + /**< Service flags */ + u32 flags; + /**< Type of memory the service must be executed from */ + u32 mem_type; + /**< Type of service */ + enum service_type service_type; + /**< Number of instances which can be run simultaneously */ + u32 num_instances; + /**< Loadable service unique identifier (UUID) */ + struct mc_uuid_t uuid; + /**< If the service_type is SERVICE_TYPE_DRIVER the Driver ID is used */ + u32 driver_id; + /**< + * Number of threads (N) in a service: + * SERVICE_TYPE_SP_TRUSTLET: N = 1 + * SERVICE_TYPE_SYSTEM_TRUSTLET: N = 1 + * SERVICE_TYPE_DRIVER: N >= 1 + */ + u32 num_threads; + /**< Virtual text segment */ + struct segment_descriptor text; + /**< Virtual data segment */ + struct segment_descriptor data; + /**< Length of the BSS segment in bytes. MUST be at least 8 byte */ + u32 bss_len; + /**< Virtual start address of service code */ + u32 entry; + /**< Version of the interface the driver exports */ + u32 service_version; +}; + +/** + * @addtogroup MCLF + */ + +/** MCLF header */ +union mclf_header { + /**< Intro for data identification */ + struct mclf_intro intro; + /**< Version 2 header */ + struct mclf_header_v2 mclf_header_v2; +}; + +struct mc_blob_len_info { + u32 magic; /**< New blob format magic number */ + u32 root_size; /**< Root container size */ + u32 sp_size; /**< SP container size */ + u32 ta_size; /**< TA container size */ + u32 reserved[4]; /**< Reserved for further Use */ +}; + +#endif /* MCLOADFORMAT_H_ */ diff --git a/drivers/gud/MobiCoreDriver/mcp.c b/drivers/gud/MobiCoreDriver/mcp.c new file mode 100644 index 000000000000..f01e94d8047c --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mcp.c @@ -0,0 +1,1038 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2020 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE +#include /* local_clock */ +#endif + +#include "mc_user.h" +#include "mc_admin.h" + +#include "mcimcp.h" +#include "mcifc.h" +#include "mcinq.h" /* SID_MCP */ +#include "mcitime.h" /* struct mcp_time */ +#include "mciiwp.h" + +#include "main.h" +#include "admin.h" /* tee_object* for 'blob' */ +#include "mmu.h" /* MMU for 'blob' */ +#include "nq.h" +#include "xen_fe.h" +#include "mcp.h" + +/* respond timeout for MCP notification, in secs */ +#define MCP_TIMEOUT 10 +#define MCP_RETRIES 5 +#define MCP_NF_QUEUE_SZ 8 + +static struct { + union mcp_message *buffer; /* MCP communication buffer */ + struct mutex buffer_mutex; /* Lock for the buffer above */ + struct completion complete; + bool mcp_dead; + struct mcp_session mcp_session; /* Pseudo session for MCP */ + /* Unexpected notification (during MCP open) */ + struct mutex unexp_notif_mutex; + struct notification unexp_notif; + /* Sessions */ + struct mutex sessions_lock; + struct list_head sessions; + /* TEE bad state detection */ + struct notifier_block tee_stop_notifier; + u32 timeout_period; + /* Log of last commands */ +#define LAST_CMDS_SIZE 1024 + struct mutex last_cmds_mutex; /* Log protection */ + struct command_info { + u64 cpu_clk; /* Kernel time */ + pid_t pid; /* Caller PID */ + enum cmd_id id; /* MCP command ID */ + u32 session_id; + char uuid_str[34]; + enum state { + UNUSED, /* Unused slot */ + PENDING, /* Previous command in progress */ + SENT, /* Waiting for response */ + COMPLETE, /* Got result */ + FAILED, /* Something went wrong */ + } state; /* Command processing state */ + int errno; /* Return code */ + enum mcp_result result; /* Command result */ + } last_cmds[LAST_CMDS_SIZE]; + int last_cmds_index; +} l_ctx; + +static const char *cmd_to_string(enum cmd_id id) +{ + switch (id) { + case MC_MCP_CMD_ID_INVALID: + return "invalid"; + case MC_MCP_CMD_OPEN_SESSION: + return "open session"; + case MC_MCP_CMD_CLOSE_SESSION: + return "close session"; + case MC_MCP_CMD_MAP: + return "map"; + case MC_MCP_CMD_UNMAP: + return "unmap"; + case MC_MCP_CMD_SUSPEND: + return "suspend"; + case MC_MCP_CMD_RESUME: + return "resume"; + case MC_MCP_CMD_GET_MOBICORE_VERSION: + return "get version"; + case MC_MCP_CMD_CLOSE_MCP: + return "close mcp"; + case MC_MCP_CMD_LOAD_TOKEN: + return "load token"; + case MC_MCP_CMD_CHECK_LOAD_TA: + return "check load TA"; + case MC_MCP_CMD_LOAD_SYSENC_KEY_SO: + return "load Key SO"; + } + return "unknown"; +} + +static const char *state_to_string(enum mcp_session_state state) +{ + switch (state) { + case MCP_SESSION_RUNNING: + return "running"; + case MCP_SESSION_CLOSE_FAILED: + return "close failed"; + case MCP_SESSION_CLOSED: + return "closed"; + } + return "error"; +} + +static inline void mark_mcp_dead(void) +{ + struct mcp_session *session; + + l_ctx.mcp_dead = true; + complete(&l_ctx.complete); + /* Signal all potential waiters that SWd is going away */ + list_for_each_entry(session, &l_ctx.sessions, list) + complete(&session->completion); +} + +static int tee_stop_notifier_fn(struct notifier_block *nb, unsigned long event, + void *data) +{ + mark_mcp_dead(); + return 0; +} + +void mcp_session_init(struct mcp_session *session) +{ + nq_session_init(&session->nq_session, false); + session->sid = SID_INVALID; + INIT_LIST_HEAD(&session->list); + mutex_init(&session->notif_wait_lock); + init_completion(&session->completion); + mutex_init(&session->exit_code_lock); + session->exit_code = 0; + session->state = MCP_SESSION_RUNNING; + session->notif_count = 0; +} + +static inline bool mcp_session_isrunning(struct mcp_session *session) +{ + bool ret; + + mutex_lock(&l_ctx.sessions_lock); + ret = session->state == MCP_SESSION_RUNNING; + mutex_unlock(&l_ctx.sessions_lock); + return ret; +} + +/* + * session remains valid thanks to the upper layers reference counters, but the + * SWd session may have died, in which case we are informed. + */ +int mcp_wait(struct mcp_session *session, s32 timeout, int silent_expiry) +{ + s32 err = 0; + int ret = 0; + + mutex_lock(&session->notif_wait_lock); +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) { + ret = xen_mc_wait(session, timeout, silent_expiry); + mutex_unlock(&session->notif_wait_lock); + return ret; + } +#endif + + if (l_ctx.mcp_dead) { + ret = -EHOSTUNREACH; + goto end; + } + + if (!mcp_session_isrunning(session)) { + ret = -ENXIO; + goto end; + } + + mcp_get_err(session, &err); + if (err) { + ret = -ECOMM; + goto end; + } + + if (timeout < 0) { + ret = wait_for_completion_interruptible(&session->completion); + if (ret) + goto end; + } else { + ret = wait_for_completion_interruptible_timeout( + &session->completion, timeout * HZ / 1000); + if (ret < 0) + /* Interrupted */ + goto end; + + if (!ret) { + /* Timed out */ + ret = -ETIME; + goto end; + } + + ret = 0; + } + + if (l_ctx.mcp_dead) { + ret = -EHOSTUNREACH; + goto end; + } + + mcp_get_err(session, &err); + if (err) { + ret = -ECOMM; + goto end; + } + + if (!mcp_session_isrunning(session)) { + ret = -ENXIO; + goto end; + } + +end: + if (!ret) + nq_session_state_update(&session->nq_session, + NQ_NOTIF_CONSUMED); + else if (ret != -ERESTARTSYS) + nq_session_state_update(&session->nq_session, NQ_NOTIF_DEAD); + + mutex_unlock(&session->notif_wait_lock); + if (ret && ((ret != -ETIME) || !silent_expiry)) { +#ifdef CONFIG_FREEZER + if (ret == -ERESTARTSYS && system_freezing_cnt.counter == 1) + mc_dev_devel("freezing session %x", session->sid); + else +#endif + mc_dev_devel("session %x ec %d ret %d", + session->sid, session->exit_code, ret); + } + + return ret; +} + +int mcp_get_err(struct mcp_session *session, s32 *err) +{ +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + return xen_mc_get_err(session, err); +#endif + + mutex_lock(&session->exit_code_lock); + *err = session->exit_code; + mutex_unlock(&session->exit_code_lock); + if (*err) + mc_dev_info("session %x ec %d", session->sid, *err); + + return 0; +} + +static inline int wait_mcp_notification(void) +{ + unsigned long timeout = msecs_to_jiffies(l_ctx.timeout_period * 1000); + int try, ret = -ETIME; + + /* + * Total timeout is l_ctx.timeout_period * MCP_RETRIES, but we check for + * a crash to try and terminate before then if things go wrong. + */ + for (try = 1; try <= MCP_RETRIES; try++) { + /* + * Wait non-interruptible to keep MCP synchronised even if + * caller is interrupted by signal. + */ + if (wait_for_completion_timeout(&l_ctx.complete, timeout) > 0) + return 0; + + mc_dev_err(ret, "no answer after %ds", + l_ctx.timeout_period * try); + } + + mc_dev_err(ret, "timed out waiting for MCP notification"); + nq_signal_tee_hung(); + return ret; +} + +static int mcp_cmd(union mcp_message *cmd, + /* The fields below are for debug purpose only */ + u32 in_session_id, + u32 *out_session_id, + struct mc_uuid_t *uuid) +{ + int err = 0, ret = -EHOSTUNREACH; + union mcp_message *msg; + enum cmd_id cmd_id = cmd->cmd_header.cmd_id; + struct command_info *cmd_info; + + /* Initialize MCP log */ + mutex_lock(&l_ctx.last_cmds_mutex); + cmd_info = &l_ctx.last_cmds[l_ctx.last_cmds_index]; + cmd_info->cpu_clk = local_clock(); + cmd_info->pid = current->pid; + cmd_info->id = cmd_id; + cmd_info->session_id = in_session_id; + if (uuid) { + /* Keep UUID because it's an 'open session' cmd */ + size_t i; + + cmd_info->uuid_str[0] = ' '; + for (i = 0; i < sizeof(uuid->value); i++) { + snprintf(&cmd_info->uuid_str[1 + i * 2], 3, "%02x", + uuid->value[i]); + } + } else { + cmd_info->uuid_str[0] = '\0'; + } + + cmd_info->state = PENDING; + cmd_info->errno = 0; + cmd_info->result = MC_MCP_RET_OK; + if (++l_ctx.last_cmds_index >= LAST_CMDS_SIZE) + l_ctx.last_cmds_index = 0; + mutex_unlock(&l_ctx.last_cmds_mutex); + + mutex_lock(&l_ctx.buffer_mutex); + msg = l_ctx.buffer; + if (l_ctx.mcp_dead) + goto out; + + /* Copy message to MCP buffer */ + memcpy(msg, cmd, sizeof(*msg)); + + /* Send MCP notification, with cmd_id as payload for debug purpose */ + nq_session_notify(&l_ctx.mcp_session.nq_session, l_ctx.mcp_session.sid, + cmd_id); + + /* Update MCP log */ + mutex_lock(&l_ctx.last_cmds_mutex); + cmd_info->state = SENT; + mutex_unlock(&l_ctx.last_cmds_mutex); + ret = wait_mcp_notification(); + if (ret) + goto out; + + /* Check response ID */ + if (msg->rsp_header.rsp_id != (cmd_id | FLAG_RESPONSE)) { + ret = -EBADE; + mc_dev_err(ret, "MCP command got invalid response (0x%X)", + msg->rsp_header.rsp_id); + goto out; + } + + /* Convert result */ + switch (msg->rsp_header.result) { + case MC_MCP_RET_OK: + err = 0; + break; + case MC_MCP_RET_ERR_CLOSE_TASK_FAILED: + err = -EAGAIN; + break; + case MC_MCP_RET_ERR_NO_MORE_SESSIONS: + err = -EBUSY; + break; + case MC_MCP_RET_ERR_OUT_OF_RESOURCES: + err = -ENOSPC; + break; + case MC_MCP_RET_ERR_UNKNOWN_UUID: + err = -ENOENT; + break; + case MC_MCP_RET_ERR_WRONG_PUBLIC_KEY: + err = -EKEYREJECTED; + break; + case MC_MCP_RET_ERR_SERVICE_BLOCKED: + err = -ECONNREFUSED; + break; + case MC_MCP_RET_ERR_SERVICE_LOCKED: + err = -ECONNABORTED; + break; + case MC_MCP_RET_ERR_SERVICE_KILLED: + err = -ECONNRESET; + break; + case MC_MCP_RET_ERR_SYSTEM_NOT_READY: + err = -EAGAIN; + break; + case MC_MCP_RET_ERR_DOWNGRADE_NOT_AUTHORIZED: + err = -EPERM; + break; + default: + err = -EPERM; + } + + /* Copy response back to caller struct */ + memcpy(cmd, msg, sizeof(*cmd)); + +out: + /* Update MCP log */ + mutex_lock(&l_ctx.last_cmds_mutex); + if (ret) { + cmd_info->state = FAILED; + cmd_info->errno = -ret; + } else { + cmd_info->state = COMPLETE; + cmd_info->errno = -err; + cmd_info->result = msg->rsp_header.result; + /* For open session: get SID */ + if (!err && out_session_id) + cmd_info->session_id = *out_session_id; + } + mutex_unlock(&l_ctx.last_cmds_mutex); + mutex_unlock(&l_ctx.buffer_mutex); + if (ret) { + mc_dev_err(ret, "%s: sending failed", cmd_to_string(cmd_id)); + return ret; + } + + if (err) { + if (cmd_id == MC_MCP_CMD_CLOSE_SESSION && err == -EAGAIN) + mc_dev_devel("%s: try again", + cmd_to_string(cmd_id)); + else + mc_dev_err(err, "%s: res %d", cmd_to_string(cmd_id), + msg->rsp_header.result); + return err; + } + + return 0; +} + +static inline int __mcp_get_version(struct mc_version_info *version_info) +{ + union mcp_message cmd; + u32 version; + int ret; + +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + return xen_mc_get_version(version_info); +#endif + + version = MC_VERSION(MCDRVMODULEAPI_VERSION_MAJOR, + MCDRVMODULEAPI_VERSION_MINOR); + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_header.cmd_id = MC_MCP_CMD_GET_MOBICORE_VERSION; + ret = mcp_cmd(&cmd, 0, NULL, NULL); + if (ret) + return ret; + + memcpy(version_info, &cmd.rsp_get_version.version_info, + sizeof(*version_info)); + /* + * The CMP version is meaningless in this case, and is replaced + * by the driver's own version. + */ + version_info->version_nwd = version; + return 0; +} + +int mcp_get_version(struct mc_version_info *version_info) +{ + static struct mc_version_info static_version_info; + + /* If cache empty, get version from the SWd and cache it */ + if (!static_version_info.version_nwd) { + int ret = __mcp_get_version(&static_version_info); + + if (ret) + return ret; + } + + /* Copy cached version */ + memcpy(version_info, &static_version_info, sizeof(*version_info)); + nq_set_version_ptr(static_version_info.product_id); + return 0; +} + +int mcp_load_token(uintptr_t data, const struct mcp_buffer_map *map) +{ + union mcp_message cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_header.cmd_id = MC_MCP_CMD_LOAD_TOKEN; + cmd.cmd_load_token.wsm_data_type = map->type; + cmd.cmd_load_token.adr_load_data = map->addr; + cmd.cmd_load_token.ofs_load_data = map->offset; + cmd.cmd_load_token.len_load_data = map->length; + return mcp_cmd(&cmd, 0, NULL, NULL); +} + +int mcp_load_check(const struct tee_object *obj, + const struct mcp_buffer_map *map) +{ + const union mclf_header *header; + union mcp_message cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_header.cmd_id = MC_MCP_CMD_CHECK_LOAD_TA; + /* Data */ + cmd.cmd_check_load.wsm_data_type = map->type; + cmd.cmd_check_load.adr_load_data = map->addr; + cmd.cmd_check_load.ofs_load_data = map->offset; + cmd.cmd_check_load.len_load_data = map->length; + /* Header */ + header = (union mclf_header *)(obj->data + obj->header_length); + cmd.cmd_check_load.uuid = header->mclf_header_v2.uuid; + return mcp_cmd(&cmd, 0, NULL, &cmd.cmd_check_load.uuid); +} + +int mcp_load_key_so(uintptr_t data, const struct mcp_buffer_map *map) +{ + union mcp_message cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_header.cmd_id = MC_MCP_CMD_LOAD_SYSENC_KEY_SO; + cmd.cmd_load_key_so.wsm_data_type = map->type; + cmd.cmd_load_key_so.adr_load_data = map->addr; + cmd.cmd_load_key_so.ofs_load_data = map->offset; + cmd.cmd_load_key_so.len_load_data = map->length; + return mcp_cmd(&cmd, 0, NULL, NULL); +} + +int mcp_open_session(struct mcp_session *session, struct mcp_open_info *info, + bool *tci_in_use) +{ + static DEFINE_MUTEX(local_mutex); + struct tee_object *obj; + const union mclf_header *header; + struct tee_mmu *obj_mmu; + struct mcp_buffer_map obj_map; + union mcp_message cmd; + int ret; + +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) { + ret = xen_mc_open_session(session, info); + if (ret) + return ret; + + /* Add to list of sessions */ + mutex_lock(&l_ctx.sessions_lock); + list_add_tail(&session->list, &l_ctx.sessions); + mutex_unlock(&l_ctx.sessions_lock); + return 0; + } +#endif + + /* Create 'blob' */ + if (info->type == TEE_MC_UUID) { + /* Get TA from registry */ + obj = tee_object_get(info->uuid, false); + /* Tell SWd to load TA from SFS as not in registry */ + if (IS_ERR(obj) && (PTR_ERR(obj) == -ENOENT)) + obj = tee_object_select(info->uuid); + } else if (info->type == TEE_MC_DRIVER_UUID) { + /* Load driver using only uuid */ + obj = tee_object_select(info->uuid); + *tci_in_use = false; + } else if (info->user) { + /* Create secure object from user-space trustlet binary */ + obj = tee_object_read(info->spid, info->va, info->len); + } else { + /* Create secure object from kernel-space trustlet binary */ + obj = tee_object_copy(info->va, info->len); + } + + if (IS_ERR(obj)) + return PTR_ERR(obj); + + /* Header */ + header = (const union mclf_header *)(&obj->data[obj->header_length]); + if (info->type == TEE_MC_DRIVER && + (header->mclf_header_v2.flags & + MC_SERVICE_HEADER_FLAGS_NO_CONTROL_INTERFACE)) + *tci_in_use = false; + + /* Create mapping for blob (allocated by driver, so task = NULL) */ + { + struct mc_ioctl_buffer buf = { + .va = (uintptr_t)obj->data, + .len = obj->length, + .flags = MC_IO_MAP_INPUT, + }; + + obj_mmu = tee_mmu_create(NULL, &buf); + if (IS_ERR(obj_mmu)) { + ret = PTR_ERR(obj_mmu); + goto err_mmu; + } + + tee_mmu_buffer(obj_mmu, &obj_map); + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_header.cmd_id = MC_MCP_CMD_OPEN_SESSION; + /* Data */ + cmd.cmd_open.uuid = header->mclf_header_v2.uuid; + cmd.cmd_open.wsm_data_type = obj_map.type; + cmd.cmd_open.adr_load_data = obj_map.addr; + cmd.cmd_open.ofs_load_data = obj_map.offset; + cmd.cmd_open.len_load_data = obj_map.length; + /* Buffer */ + if (*tci_in_use) { + struct mcp_buffer_map map; + + tee_mmu_buffer(info->tci_mmu, &map); + cmd.cmd_open.wsmtype_tci = map.type; + cmd.cmd_open.adr_tci_buffer = map.addr; + cmd.cmd_open.ofs_tci_buffer = map.offset; + cmd.cmd_open.len_tci_buffer = map.length; + } else { + cmd.cmd_open.wsmtype_tci = WSM_INVALID; + } + + /* Reset unexpected notification */ + mutex_lock(&local_mutex); + l_ctx.unexp_notif.session_id = SID_MCP; /* Cannot be */ + cmd.cmd_open.cmd_open_data.mclf_magic = MC_GP_CLIENT_AUTH_MAGIC; + + /* Send MCP open command */ + ret = mcp_cmd(&cmd, 0, &cmd.rsp_open.session_id, &cmd.cmd_open.uuid); + /* Make sure we have a valid session ID */ + if (!ret && !cmd.rsp_open.session_id) + ret = -EBADE; + + if (!ret) { + session->sid = cmd.rsp_open.session_id; + /* Add to list of sessions */ + mutex_lock(&l_ctx.sessions_lock); + list_add_tail(&session->list, &l_ctx.sessions); + mutex_unlock(&l_ctx.sessions_lock); + /* Check for spurious notification */ + mutex_lock(&l_ctx.unexp_notif_mutex); + if (l_ctx.unexp_notif.session_id == session->sid) { + mutex_lock(&session->exit_code_lock); + session->exit_code = l_ctx.unexp_notif.payload; + mutex_unlock(&session->exit_code_lock); + nq_session_state_update(&session->nq_session, + NQ_NOTIF_RECEIVED); + complete(&session->completion); + } + + mutex_unlock(&l_ctx.unexp_notif_mutex); + } + + mutex_unlock(&local_mutex); + + /* Blob for UUID/TA not needed as re-mapped by the SWd */ + tee_mmu_put(obj_mmu); + +err_mmu: + /* Delete secure object */ + tee_object_free(obj); + + return ret; +} + +/* + * Legacy and GP TAs close differently: + * - GP TAs always send a notification with payload, whether on close or crash + * - Legacy TAs only send a notification with payload on crash + * - GP TAs may take time to close, and we get -EAGAIN back from mcp_cmd + * - Legacy TAs always close when asked, unless they are driver in which case + * they just don't close at all + */ +int mcp_close_session(struct mcp_session *session) +{ + union mcp_message cmd; + /* ret's value is always set, but some compilers complain */ + int ret = -ENXIO; + + if (is_xen_domu()) { +#ifdef TRUSTONIC_XEN_DOMU + ret = xen_mc_close_session(session); +#endif + } else { + /* Signal a potential waiter that SWd session is going away */ + complete(&session->completion); + /* Send MCP command */ + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_header.cmd_id = MC_MCP_CMD_CLOSE_SESSION; + cmd.cmd_close.session_id = session->sid; + ret = mcp_cmd(&cmd, cmd.cmd_close.session_id, NULL, NULL); + } + + mutex_lock(&l_ctx.sessions_lock); + if (!ret) { + session->state = MCP_SESSION_CLOSED; + list_del(&session->list); + nq_session_exit(&session->nq_session); + } else { + /* Something is not right, assume session is still running */ + session->state = MCP_SESSION_CLOSE_FAILED; + } + mutex_unlock(&l_ctx.sessions_lock); + mc_dev_devel("close session %x ret %d state %s", + session->sid, ret, state_to_string(session->state)); + return ret; +} + +/* + * Session is to be removed from NWd records as SWd has been wiped clean + */ +void mcp_cleanup_session(struct mcp_session *session) +{ + mutex_lock(&l_ctx.sessions_lock); + session->state = MCP_SESSION_CLOSED; + list_del(&session->list); + nq_session_exit(&session->nq_session); + mutex_unlock(&l_ctx.sessions_lock); +} + +int mcp_map(u32 session_id, struct tee_mmu *mmu, u32 *sva) +{ + struct mcp_buffer_map map; + union mcp_message cmd; + int ret; + +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + return xen_mc_map(session_id, mmu, sva); +#endif + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_header.cmd_id = MC_MCP_CMD_MAP; + cmd.cmd_map.session_id = session_id; + tee_mmu_buffer(mmu, &map); + cmd.cmd_map.wsm_type = map.type; + cmd.cmd_map.adr_buffer = map.addr; + cmd.cmd_map.ofs_buffer = map.offset; + cmd.cmd_map.len_buffer = map.length; + cmd.cmd_map.flags = map.flags; + ret = mcp_cmd(&cmd, session_id, NULL, NULL); + if (!ret) { + *sva = cmd.rsp_map.secure_va; + atomic_inc(&g_ctx.c_maps); + } + + return ret; +} + +int mcp_unmap(u32 session_id, const struct mcp_buffer_map *map) +{ + union mcp_message cmd; + int ret; + +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + return xen_mc_unmap(session_id, map); +#endif + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_header.cmd_id = MC_MCP_CMD_UNMAP; + cmd.cmd_unmap.session_id = session_id; + cmd.cmd_unmap.wsm_type = map->type; + cmd.cmd_unmap.virtual_buffer_len = map->length; + cmd.cmd_unmap.secure_va = map->secure_va; + ret = mcp_cmd(&cmd, session_id, NULL, NULL); + if (!ret) + atomic_dec(&g_ctx.c_maps); + + return ret; +} + +static int mcp_close(void) +{ + union mcp_message cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.cmd_header.cmd_id = MC_MCP_CMD_CLOSE_MCP; + return mcp_cmd(&cmd, 0, NULL, NULL); +} + +int mcp_notify(struct mcp_session *session) +{ + if (l_ctx.mcp_dead) + return -EHOSTUNREACH; + + if (session->sid == SID_MCP) + mc_dev_devel("notify MCP"); + else + mc_dev_devel("notify session %x", session->sid); + +#ifdef TRUSTONIC_XEN_DOMU + if (is_xen_domu()) + return xen_mc_notify(session); +#endif + + /* Put notif_count as payload for debug purpose */ + return nq_session_notify(&session->nq_session, session->sid, + ++session->notif_count); +} + +static inline void session_notif_handler(struct mcp_session *session, u32 id, + u32 payload) +{ + mutex_lock(&l_ctx.sessions_lock); + mc_dev_devel("MCP notif from session %x exit code %d state %d", + id, payload, session ? session->state : -1); + if (session) { + /* TA has terminated */ + if (payload) { + /* Update exit code, or not */ + mutex_lock(&session->exit_code_lock); + session->exit_code = payload; + mutex_unlock(&session->exit_code_lock); + } + + nq_session_state_update(&session->nq_session, + NQ_NOTIF_RECEIVED); + + /* Unblock waiter */ + complete(&session->completion); + } + mutex_unlock(&l_ctx.sessions_lock); + + /* Unknown session, probably being started */ + if (!session) { + mutex_lock(&l_ctx.unexp_notif_mutex); + l_ctx.unexp_notif.session_id = id; + l_ctx.unexp_notif.payload = payload; + mutex_unlock(&l_ctx.unexp_notif_mutex); + } +} + +static void mcp_notif_handler(u32 id, u32 payload) +{ + if (id == SID_MCP) { + /* MCP notification */ + mc_dev_devel("notification from MCP"); + complete(&l_ctx.complete); + } else { + /* Session notification */ + struct mcp_session *session = NULL, *candidate; + + mutex_lock(&l_ctx.sessions_lock); + list_for_each_entry(candidate, &l_ctx.sessions, list) { + if (candidate->sid == id) { + session = candidate; + break; + } + } + mutex_unlock(&l_ctx.sessions_lock); + + /* session is NULL if id not found */ + session_notif_handler(session, id, payload); + } +} + +static int debug_sessions(struct kasnprintf_buf *buf) +{ + struct mcp_session *session; + int ret; + + /* Header */ + ret = kasnprintf(buf, "%20s %4s %-15s %-11s %4s\n", + "CPU clock", "ID", "state", "notif state", "ec"); + if (ret < 0) + return ret; + + mutex_lock(&l_ctx.sessions_lock); + list_for_each_entry(session, &l_ctx.sessions, list) { + const char *state_str; + u64 cpu_clk; + s32 err = 0; + + state_str = nq_session_state(&session->nq_session, &cpu_clk); + mcp_get_err(session, &err); + ret = kasnprintf(buf, "%20llu %4x %-15s %-11s %4d\n", cpu_clk, + session->sid, state_to_string(session->state), + state_str, err); + if (ret < 0) + break; + } + mutex_unlock(&l_ctx.sessions_lock); + return ret; +} + +static ssize_t debug_sessions_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + return debug_generic_read(file, user_buf, count, ppos, + debug_sessions); +} + +static const struct file_operations debug_sessions_ops = { + .read = debug_sessions_read, + .llseek = default_llseek, + .open = debug_generic_open, + .release = debug_generic_release, +}; + +static inline int show_log_entry(struct kasnprintf_buf *buf, + struct command_info *cmd_info) +{ + const char *state_str = "unknown"; + + switch (cmd_info->state) { + case UNUSED: + state_str = "unused"; + break; + case PENDING: + state_str = "pending"; + break; + case SENT: + state_str = "sent"; + break; + case COMPLETE: + state_str = "complete"; + break; + case FAILED: + state_str = "failed"; + break; + } + + return kasnprintf(buf, "%20llu %5d %-16s %5x %-8s %5d %6d%s\n", + cmd_info->cpu_clk, cmd_info->pid, + cmd_to_string(cmd_info->id), cmd_info->session_id, + state_str, cmd_info->errno, cmd_info->result, + cmd_info->uuid_str); +} + +static int debug_last_cmds(struct kasnprintf_buf *buf) +{ + struct command_info *cmd_info; + int i, ret = 0; + + /* Initialize MCP log */ + mutex_lock(&l_ctx.last_cmds_mutex); + ret = kasnprintf(buf, "%20s %5s %-16s %5s %-8s %5s %6s %s\n", + "CPU clock", "PID", "command", "S-ID", + "state", "errno", "result", "UUID"); + if (ret < 0) + goto out; + + cmd_info = &l_ctx.last_cmds[l_ctx.last_cmds_index]; + if (cmd_info->state != UNUSED) + /* Buffer has wrapped around, dump end (oldest records) */ + for (i = l_ctx.last_cmds_index; i < LAST_CMDS_SIZE; i++) { + ret = show_log_entry(buf, cmd_info++); + if (ret < 0) + goto out; + } + + /* Dump first records */ + cmd_info = &l_ctx.last_cmds[0]; + for (i = 0; i < l_ctx.last_cmds_index; i++) { + ret = show_log_entry(buf, cmd_info++); + if (ret < 0) + goto out; + } + +out: + mutex_unlock(&l_ctx.last_cmds_mutex); + return ret; +} + +static ssize_t debug_last_cmds_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + return debug_generic_read(file, user_buf, count, ppos, debug_last_cmds); +} + +static const struct file_operations debug_last_cmds_ops = { + .read = debug_last_cmds_read, + .llseek = default_llseek, + .open = debug_generic_open, + .release = debug_generic_release, +}; + +int mcp_init(void) +{ + l_ctx.buffer = nq_get_mcp_buffer(); + mutex_init(&l_ctx.buffer_mutex); + init_completion(&l_ctx.complete); + /* Setup notification queue mutex */ + mcp_session_init(&l_ctx.mcp_session); + l_ctx.mcp_session.sid = SID_MCP; + mutex_init(&l_ctx.unexp_notif_mutex); + INIT_LIST_HEAD(&l_ctx.sessions); + mutex_init(&l_ctx.sessions_lock); + mutex_init(&l_ctx.last_cmds_mutex); + + l_ctx.timeout_period = MCP_TIMEOUT; + + nq_register_notif_handler(mcp_notif_handler, false); + l_ctx.tee_stop_notifier.notifier_call = tee_stop_notifier_fn; + nq_register_tee_stop_notifier(&l_ctx.tee_stop_notifier); + + return 0; +} + +void mcp_exit(void) +{ + mark_mcp_dead(); + nq_unregister_tee_stop_notifier(&l_ctx.tee_stop_notifier); +} + +int mcp_start(void) +{ + /* Create debugfs sessions and last commands entries */ + debugfs_create_file("sessions", 0400, g_ctx.debug_dir, NULL, + &debug_sessions_ops); + debugfs_create_file("last_mcp_commands", 0400, g_ctx.debug_dir, NULL, + &debug_last_cmds_ops); + debugfs_create_u32("mcp_timeout", 0600, g_ctx.debug_dir, + &l_ctx.timeout_period); + return 0; +} + +void mcp_stop(void) +{ + mcp_close(); +} diff --git a/drivers/gud/MobiCoreDriver/mcp.h b/drivers/gud/MobiCoreDriver/mcp.h new file mode 100644 index 000000000000..895b329097c8 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mcp.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2020 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef _MC_MCP_H_ +#define _MC_MCP_H_ + +#include "mcloadformat.h" /* struct identity */ +#include "nq.h" + +struct tee_mmu; + +/* Structure to hold the TA/driver information at open */ +struct mcp_open_info { + enum { + TEE_MC_UUID, + TEE_MC_TA, + TEE_MC_DRIVER, + TEE_MC_DRIVER_UUID, + } type; + /* TA/driver */ + const struct mc_uuid_t *uuid; + u32 spid; + uintptr_t va; + size_t len; + /* TCI */ + uintptr_t tci_va; + size_t tci_len; + struct tee_mmu *tci_mmu; + /* Origin */ + int user; +}; + +/* Structure to hold the TA/driver descriptor to pass to MCP */ +struct tee_object { + u32 length; /* Total length */ + u32 header_length; /* Length of header before payload */ + u8 data[]; /* Header followed by payload */ +}; + +/* Structure to hold all mapped buffer data to pass to MCP */ +struct mcp_buffer_map { + u64 addr; /** Page-aligned PA, or VA */ + unsigned long nr_pages; /** Total number of pages mapped */ + u32 secure_va; /** SWd virtual address */ + u32 offset; /** Data offset inside the first page */ + u32 length; /** Length of the data */ + u32 type; /** Type of MMU */ + u32 flags; /** Flags (typically read/write) */ + struct tee_mmu *mmu; /** MMU from which the map was made */ +}; + +struct mcp_session { + /* Notification queue session */ + struct nq_session nq_session; + /* Session ID */ + u32 sid; + /* Sessions list (protected by mcp sessions_lock) */ + struct list_head list; + /* Notification waiter lock */ + struct mutex notif_wait_lock; /* Only one at a time */ + /* Notification received */ + struct completion completion; + /* Notification lock */ + struct mutex exit_code_lock; + /* Last notification */ + s32 exit_code; + /* Session state (protected by mcp sessions_lock) */ + enum mcp_session_state { + MCP_SESSION_RUNNING, + MCP_SESSION_CLOSE_FAILED, + MCP_SESSION_CLOSED, + } state; + /* Notification counter */ + u32 notif_count; +}; + +/* Init for the mcp_session structure */ +void mcp_session_init(struct mcp_session *session); + +/* Commands */ +int mcp_get_version(struct mc_version_info *version_info); +int mcp_load_token(uintptr_t data, const struct mcp_buffer_map *buffer_map); +int mcp_load_check(const struct tee_object *obj, + const struct mcp_buffer_map *buffer_map); +int mcp_load_key_so(uintptr_t data, const struct mcp_buffer_map *buffer_map); +int mcp_open_session(struct mcp_session *session, struct mcp_open_info *info, + bool *tci_in_use); +int mcp_close_session(struct mcp_session *session); +void mcp_cleanup_session(struct mcp_session *session); +int mcp_map(u32 session_id, struct tee_mmu *mmu, u32 *sva); +int mcp_unmap(u32 session_id, const struct mcp_buffer_map *map); +int mcp_notify(struct mcp_session *mcp_session); +int mcp_wait(struct mcp_session *session, s32 timeout, int silent_expiry); +int mcp_get_err(struct mcp_session *session, s32 *err); + +/* Initialisation/cleanup */ +int mcp_init(void); +void mcp_exit(void); +int mcp_start(void); +void mcp_stop(void); + +#endif /* _MC_MCP_H_ */ diff --git a/drivers/gud/MobiCoreDriver/mmu.c b/drivers/gud/MobiCoreDriver/mmu.c new file mode 100644 index 000000000000..ff526d96d471 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mmu.c @@ -0,0 +1,754 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2020 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_DMA_SHARED_BUFFER +#include +#endif +#ifdef CONFIG_ION +#ifdef CONFIG_ION_SYSTEM_HEAP /* Android kernel only */ +#if KERNEL_VERSION(5, 4, 0) < LINUX_VERSION_CODE +#include +#elif KERNEL_VERSION(4, 11, 12) < LINUX_VERSION_CODE +#include "../../drivers/staging/android/ion/ion.h" +#elif KERNEL_VERSION(3, 14, 0) < LINUX_VERSION_CODE +#include "../../drivers/staging/android/ion/ion_priv.h" +#endif /* KERNEL_VERSION */ +#else /* CONFIG_ION_SYSTEM_HEAP */ +#if KERNEL_VERSION(5, 4, 0) < LINUX_VERSION_CODE +#include "../../drivers/staging/android/ion/ion.h" +#elif KERNEL_VERSION(4, 11, 12) < LINUX_VERSION_CODE +#include "../../drivers/staging/android/ion/ion.h" +#elif KERNEL_VERSION(3, 14, 0) < LINUX_VERSION_CODE +/* very old Android kernel without ION_SYSTEM_HEAP falls here */ +#include "../../drivers/staging/android/ion/ion_priv.h" +#endif +#endif /* CONFIG_ION_SYSTEM_HEAP */ +#endif /* CONFIG_ION */ + +#ifdef CONFIG_XEN +/* To get the MFN */ +#include +#include +#endif + +#include "mc_user.h" + +#include "mcimcp.h" + +#include "main.h" +#include "mcp.h" /* mcp_buffer_map */ +#include "mmu.h" + +#define PHYS_48BIT_MASK (BIT(48) - 1) + +/* Common */ +#define MMU_BUFFERABLE BIT(2) /* AttrIndx[0] */ +#define MMU_CACHEABLE BIT(3) /* AttrIndx[1] */ +#define MMU_EXT_NG BIT(11) /* ARMv6 and higher */ + +/* LPAE */ +#define MMU_TYPE_PAGE (3 << 0) +#define MMU_NS BIT(5) +#define MMU_AP_RW_ALL BIT(6) /* AP[2:1], RW, at any privilege level */ +#define MMU_AP2_RO BIT(7) +#define MMU_EXT_SHARED_64 (3 << 8) /* SH[1:0], inner shareable */ +#define MMU_EXT_AF BIT(10) /* Access Flag */ +#define MMU_EXT_XN (((u64)1) << 54) /* XN */ + +/* Non-LPAE */ +#define MMU_TYPE_EXT (3 << 0) /* v5 */ +#define MMU_TYPE_SMALL (2 << 0) +#define MMU_EXT_AP0 BIT(4) +#define MMU_EXT_AP1 (2 << 4) +#define MMU_EXT_AP2 BIT(9) +#define MMU_EXT_TEX(x) ((x) << 6) /* v5 */ +#define MMU_EXT_SHARED_32 BIT(10) /* ARMv6 and higher */ + +/* ION */ +/* Trustonic Specific flag to detect ION mem */ +#define MMU_ION_BUF BIT(24) + +/* + * Specific case for kernel 4.4.168 that does not have the same + * get_user_pages() implementation + */ +#if KERNEL_VERSION(4, 4, 167) < LINUX_VERSION_CODE && \ + KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE +static inline long gup_local(struct mm_struct *mm, uintptr_t start, + unsigned long nr_pages, int write, + struct page **pages) +{ + unsigned int gup_flags = 0; + + if (write) + gup_flags |= FOLL_WRITE; + + return get_user_pages(NULL, mm, start, nr_pages, gup_flags, pages, + NULL); +} +#elif KERNEL_VERSION(4, 6, 0) > LINUX_VERSION_CODE +static inline long gup_local(struct mm_struct *mm, uintptr_t start, + unsigned long nr_pages, int write, + struct page **pages) +{ + return get_user_pages(NULL, mm, start, nr_pages, write, 0, pages, NULL); +} +#elif KERNEL_VERSION(4, 9, 0) > LINUX_VERSION_CODE +static inline long gup_local(struct mm_struct *mm, uintptr_t start, + unsigned long nr_pages, int write, + struct page **pages) +{ + unsigned int gup_flags = 0; + + if (write) + gup_flags |= FOLL_WRITE; + /* gup_flags |= FOLL_CMA; */ + + return get_user_pages_remote(NULL, mm, start, nr_pages, gup_flags, + 0, pages, NULL); +} +#elif KERNEL_VERSION(4, 10, 0) > LINUX_VERSION_CODE +static inline long gup_local(struct mm_struct *mm, uintptr_t start, + unsigned long nr_pages, int write, + struct page **pages) +{ + unsigned int gup_flags = 0; + + if (write) + gup_flags |= FOLL_WRITE; + + return get_user_pages_remote(NULL, mm, start, nr_pages, gup_flags, + pages, NULL); +} +#elif KERNEL_VERSION(5, 4, 0) > LINUX_VERSION_CODE +static inline long gup_local(struct mm_struct *mm, uintptr_t start, + unsigned long nr_pages, int write, + struct page **pages) +{ + unsigned int gup_flags = 0; + + if (write) + gup_flags |= FOLL_WRITE; + + return get_user_pages_remote(NULL, mm, start, nr_pages, gup_flags, + pages, NULL, NULL); +} +#elif KERNEL_VERSION(5, 10, 0) > LINUX_VERSION_CODE +static inline long gup_local(struct mm_struct *mm, uintptr_t start, + unsigned long nr_pages, int write, + struct page **pages) +{ + unsigned int gup_flags = 0; + + gup_flags |= FOLL_LONGTERM; + if (write) + gup_flags |= FOLL_WRITE; + + return get_user_pages(start, nr_pages, gup_flags, pages, NULL); +} +#else +static inline long gup_local(struct mm_struct *mm, uintptr_t start, + unsigned long nr_pages, int write, + struct page **pages) +{ + unsigned int gup_flags = 0; + + gup_flags |= FOLL_LONGTERM; + if (write) + gup_flags |= FOLL_WRITE; + + return pin_user_pages(start, nr_pages, gup_flags, pages, NULL); +} +#endif + +static inline long gup_local_repeat(struct mm_struct *mm, uintptr_t start, + unsigned long nr_pages, int write, + struct page **pages) +{ + int retries = 10; + long ret = 0; + + while (retries--) { + ret = gup_local(mm, start, nr_pages, write, pages); + + if (-EBUSY != ret) + break; + } + + return ret; +} + +/* + * A table that could be either a pmd or pte + */ +union mmu_table { + u64 *entries; /* Array of PTEs */ + /* Array of pages */ + struct page **pages; + /* Array of VAs */ + uintptr_t *vas; + /* Address of table */ + void *addr; + /* Page for table */ + unsigned long page; +}; + +/* + * MMU table allocated to the Daemon or a TLC describing a world shared + * buffer. + * When users map a malloc()ed area into SWd, a MMU table is allocated. + * In addition, the area of maximum 1MB virtual address space is mapped into + * the MMU table and a handle for this table is returned to the user. + */ +struct tee_mmu { + struct kref kref; + /* Array of pages that hold buffer ptes*/ + union mmu_table pte_tables[PMD_ENTRIES_MAX]; + /* Actual number of ptes tables */ + size_t nr_pmd_entries; + /* Contains phys @ of ptes tables */ + union mmu_table pmd_table; + struct tee_deleter *deleter; /* Xen map to free */ + unsigned long nr_pages; + int pages_created; /* Leak check */ + int pages_locked; /* Leak check */ + u32 offset; + u32 length; + u32 flags; + /* Pages are from user space */ + bool user; + bool use_pages_and_vas; + /* ION case only */ + struct dma_buf *dma_buf; + struct dma_buf_attachment *attach; + struct sg_table *sgt; +}; + +static void tee_mmu_delete(struct tee_mmu *mmu) +{ + unsigned long chunk, nr_pages_left = mmu->nr_pages; + +#ifdef CONFIG_DMA_SHARED_BUFFER + if (mmu->dma_buf) { + dma_buf_unmap_attachment(mmu->attach, mmu->sgt, + DMA_BIDIRECTIONAL); + dma_buf_detach(mmu->dma_buf, mmu->attach); + dma_buf_put(mmu->dma_buf); + } +#endif + + /* Release all locked user space pages */ + for (chunk = 0; chunk < mmu->nr_pmd_entries; chunk++) { + union mmu_table *pte_table = &mmu->pte_tables[chunk]; + unsigned long nr_pages = nr_pages_left; + + if (nr_pages > PTE_ENTRIES_MAX) + nr_pages = PTE_ENTRIES_MAX; + + nr_pages_left -= nr_pages; + + if (!pte_table->page) + break; + + if (mmu->user && mmu->use_pages_and_vas) { + struct page **page = pte_table->pages; + int i; + + for (i = 0; i < nr_pages; i++, page++) + put_page(*page); + + mmu->pages_locked -= nr_pages; + } else if (mmu->user) { + u64 *pte64 = pte_table->entries; + pte_t pte; + int i; + + for (i = 0; i < nr_pages; i++) { +#if (KERNEL_VERSION(4, 7, 0) > LINUX_VERSION_CODE) || defined(CONFIG_ARM) + { + pte = *pte64++; + /* Unused entries are 0 */ + if (!pte) + break; + } +#else + { + pte.pte = *pte64++; + /* Unused entries are 0 */ + if (!pte.pte) + break; + } +#endif + + /* pte_page() cannot return NULL */ +#if KERNEL_VERSION(5, 10, 0) > LINUX_VERSION_CODE + put_page(pte_page(pte)); +#else + unpin_user_page(pte_page(pte)); +#endif + } + + mmu->pages_locked -= nr_pages; + } + + free_page(pte_table->page); + mmu->pages_created--; + } + + if (mmu->pmd_table.page) { + free_page(mmu->pmd_table.page); + mmu->pages_created--; + } + + if (mmu->pages_created || mmu->pages_locked) + mc_dev_err(-EUCLEAN, + "leak detected: still in use %d, still locked %d", + mmu->pages_created, mmu->pages_locked); + + if (mmu->deleter) + mmu->deleter->delete(mmu->deleter->object); + + kfree(mmu); + + /* Decrement debug counter */ + atomic_dec(&g_ctx.c_mmus); +} + +static struct tee_mmu *tee_mmu_create_common(const struct mcp_buffer_map *b_map) +{ + struct tee_mmu *mmu; + int ret = -ENOMEM; + + if (b_map->nr_pages > (PMD_ENTRIES_MAX * PTE_ENTRIES_MAX)) { + ret = -EINVAL; + mc_dev_err(ret, "data mapping exceeds %d pages: %lu", + PMD_ENTRIES_MAX * PTE_ENTRIES_MAX, b_map->nr_pages); + return ERR_PTR(ret); + } + + /* Allocate the struct */ + mmu = kzalloc(sizeof(*mmu), GFP_KERNEL); + if (!mmu) + return ERR_PTR(-ENOMEM); + + /* Increment debug counter */ + atomic_inc(&g_ctx.c_mmus); + kref_init(&mmu->kref); + + /* The Xen front-end does not use PTEs */ + if (is_xen_domu()) + mmu->use_pages_and_vas = true; + + /* Buffer info */ + mmu->offset = b_map->offset; + mmu->length = b_map->length; + mmu->flags = b_map->flags; + + /* Pages info */ + mmu->nr_pages = b_map->nr_pages; + mmu->nr_pmd_entries = (mmu->nr_pages + PTE_ENTRIES_MAX - 1) / + PTE_ENTRIES_MAX; + mc_dev_devel("mmu->nr_pages %lu num_ptes_pages %zu", + mmu->nr_pages, mmu->nr_pmd_entries); + + /* Allocate a page for the L1 table, always used for DomU */ + mmu->pmd_table.page = get_zeroed_page(GFP_KERNEL); + if (!mmu->pmd_table.page) + goto end; + + mmu->pages_created++; + + return mmu; + +end: + tee_mmu_delete(mmu); + return ERR_PTR(ret); +} + +static bool mmu_get_dma_buffer(struct tee_mmu *mmu, int va) +{ +#ifdef CONFIG_DMA_SHARED_BUFFER + struct dma_buf *buf; + + buf = dma_buf_get(va); + if (IS_ERR(buf)) + return false; + + mmu->dma_buf = buf; + mmu->attach = dma_buf_attach(mmu->dma_buf, g_ctx.mcd); + if (IS_ERR(mmu->attach)) + goto err_attach; + + mmu->sgt = dma_buf_map_attachment(mmu->attach, DMA_BIDIRECTIONAL); + if (IS_ERR(mmu->sgt)) + goto err_map; + + return true; + +err_map: + dma_buf_detach(mmu->dma_buf, mmu->attach); + +err_attach: + dma_buf_put(mmu->dma_buf); +#endif + return false; +} + +/* + * Allocate MMU table and map buffer into it. + * That is, create respective table entries. + */ +struct tee_mmu *tee_mmu_create(struct mm_struct *mm, + const struct mc_ioctl_buffer *buf) +{ + struct tee_mmu *mmu; + const void *data = (const void *)(uintptr_t)buf->va; + const void *reader = (const void *)((uintptr_t)data & PAGE_MASK); + struct page **pages; /* Same as below, conveniently typed */ + unsigned long pages_page = 0; /* Page to contain the page pointers */ + unsigned long chunk; + struct mcp_buffer_map b_map = { + .offset = (u32)(buf->va & ~PAGE_MASK), + .length = buf->len, + .flags = buf->flags, + }; + bool writeable = buf->flags & MC_IO_MAP_OUTPUT; + int ret = 0; + +#ifndef CONFIG_DMA_SHARED_BUFFER + if (buf->flags & MMU_ION_BUF) { + mc_dev_err(-EINVAL, "ION buffers not supported by kernel"); + return ERR_PTR(-EINVAL); + } +#endif + + /* Check input arguments */ + if (!(buf->flags & MMU_ION_BUF) && !buf->va) + return ERR_PTR(-EINVAL); + + if (buf->flags & MMU_ION_BUF) + /* buf->va is not a valid address. ION buffers are aligned */ + b_map.offset = 0; + + /* Allocate the struct */ + b_map.nr_pages = PAGE_ALIGN(b_map.offset + b_map.length) / PAGE_SIZE; + /* Allow Registered Shared mem with valid pointer and zero size. */ + if (!b_map.nr_pages) + b_map.nr_pages = 1; + + mmu = tee_mmu_create_common(&b_map); + if (IS_ERR(mmu)) + return mmu; + + if (buf->flags & MMU_ION_BUF) { + mc_dev_devel("Buffer is ION"); + /* Buffer is ION - + * va is the client's dma_buf fd, which should be converted + * to a struct sg_table * directly. + */ + if (!mmu_get_dma_buffer(mmu, buf->va)) { + mc_dev_err(ret, "mmu_get_dma_buffer failed"); + ret = -EINVAL; + goto end; + } + } + /* Get a page to store page pointers */ + pages_page = get_zeroed_page(GFP_KERNEL); + if (!pages_page) { + ret = -ENOMEM; + goto end; + } + mmu->pages_created++; + + pages = (struct page **)pages_page; + for (chunk = 0; chunk < mmu->nr_pmd_entries; chunk++) { + unsigned long nr_pages; + int i; + + /* Size to map for this chunk */ + if (chunk == (mmu->nr_pmd_entries - 1)) + nr_pages = ((mmu->nr_pages - 1) % PTE_ENTRIES_MAX) + 1; + else + nr_pages = PTE_ENTRIES_MAX; + + /* Allocate a page to hold ptes that describe buffer pages */ + mmu->pte_tables[chunk].page = get_zeroed_page(GFP_KERNEL); + if (!mmu->pte_tables[chunk].page) { + ret = -ENOMEM; + goto end; + } + mmu->pages_created++; + + /* Add page address to pmd table if needed */ + if (mmu->use_pages_and_vas) + mmu->pmd_table.vas[chunk] = + mmu->pte_tables[chunk].page; + else + mmu->pmd_table.entries[chunk] = + virt_to_phys(mmu->pte_tables[chunk].addr); + + /* Get pages */ + if (mmu->dma_buf) { + /* Buffer is ION */ + struct sg_mapping_iter miter; + struct page **page_ptr; + unsigned int cnt = 0; + unsigned int global_cnt = 0; + + page_ptr = pages; + sg_miter_start(&miter, mmu->sgt->sgl, + mmu->sgt->nents, + SG_MITER_FROM_SG); + + while (sg_miter_next(&miter)) { + if (((global_cnt) >= + (PTE_ENTRIES_MAX * chunk)) && + cnt < nr_pages) { + page_ptr[cnt] = miter.page; + cnt++; + } + global_cnt++; + } + sg_miter_stop(&miter); + } else if (mm) { + long gup_ret; + + /* Buffer was allocated in user space */ +#if KERNEL_VERSION(5, 7, 19) < LINUX_VERSION_CODE + down_read(&mm->mmap_lock); +#else + down_read(&mm->mmap_sem); +#endif + /* + * Always try to map read/write from a Linux PoV, so + * Linux creates (page faults) the underlying pages if + * missing. + */ + gup_ret = gup_local_repeat(mm, (uintptr_t)reader, + nr_pages, 1, pages); + if ((gup_ret == -EFAULT) && !writeable) { + /* + * If mapping read/write fails, and the buffer + * is to be shared as input only, try to map + * again read-only. + */ + gup_ret = gup_local_repeat(mm, + (uintptr_t)reader, + nr_pages, 0, pages); + } +#if KERNEL_VERSION(5, 7, 19) < LINUX_VERSION_CODE + up_read(&mm->mmap_lock); +#else + up_read(&mm->mmap_sem); +#endif + if (gup_ret < 0) { + ret = gup_ret; + mc_dev_err(ret, "failed to get user pages @%p", + reader); + goto end; + } + + /* check if we could lock all pages. */ + if (gup_ret != nr_pages) { + mc_dev_err((int)gup_ret, + "failed to get user pages"); +#if KERNEL_VERSION(4, 15, 0) > LINUX_VERSION_CODE + release_pages(pages, gup_ret, 0); +#else + release_pages(pages, gup_ret); +#endif + ret = -EINVAL; + goto end; + } + + reader += nr_pages * PAGE_SIZE; + mmu->user = true; + mmu->pages_locked += nr_pages; + } else if (is_vmalloc_addr(data)) { + /* Buffer vmalloc'ed in kernel space */ + for (i = 0; i < nr_pages; i++) { + struct page *page = vmalloc_to_page(reader); + + if (!page) { + ret = -EINVAL; + mc_dev_err(ret, + "failed to map address"); + goto end; + } + + pages[i] = page; + reader += PAGE_SIZE; + } + } else { + /* Buffer kmalloc'ed in kernel space */ + struct page *page = virt_to_page(reader); + + reader += nr_pages * PAGE_SIZE; + for (i = 0; i < nr_pages; i++) + pages[i] = page++; + } + + /* Create Table of physical addresses*/ + if (mmu->use_pages_and_vas) { + memcpy(mmu->pte_tables[chunk].pages, pages, + nr_pages * sizeof(*pages)); + } else { + for (i = 0; i < nr_pages; i++) { + mmu->pte_tables[chunk].entries[i] = + page_to_phys(pages[i]); + } + } + } + +end: + if (pages_page) { + free_page(pages_page); + mmu->pages_created--; + } + + if (ret) { + tee_mmu_delete(mmu); + return ERR_PTR(ret); + } + + mc_dev_devel( + "created mmu %p: %s va %llx len %u off %u flg %x pmd table %lx", + mmu, mmu->user ? "user" : "kernel", buf->va, mmu->length, + mmu->offset, mmu->flags, mmu->pmd_table.page); + return mmu; +} + +struct tee_mmu *tee_mmu_wrap(struct tee_deleter *deleter, struct page **pages, + const struct mcp_buffer_map *b_map) +{ + int ret = -EINVAL; +#ifdef CONFIG_XEN + struct tee_mmu *mmu; + unsigned long chunk, nr_pages_left; + + /* Allocate the struct */ + mmu = tee_mmu_create_common(b_map); + if (IS_ERR(mmu)) + return mmu; + + nr_pages_left = mmu->nr_pages; + for (chunk = 0; chunk < mmu->nr_pmd_entries; chunk++) { + unsigned long nr_pages = nr_pages_left; + u64 *pte; + int i; + + if (nr_pages > PTE_ENTRIES_MAX) + nr_pages = PTE_ENTRIES_MAX; + + nr_pages_left -= nr_pages; + + /* Allocate a page to hold ptes that describe buffer pages */ + mmu->pte_tables[chunk].page = get_zeroed_page(GFP_KERNEL); + if (!mmu->pte_tables[chunk].page) { + ret = -ENOMEM; + goto err; + } + mmu->pages_created++; + + /* Add page address to pmd table if needed */ + mmu->pmd_table.entries[chunk] = + virt_to_phys(mmu->pte_tables[chunk].addr); + + /* Convert to PTEs */ + pte = &mmu->pte_tables[chunk].entries[0]; + + for (i = 0; i < nr_pages; i++, pages++, pte++) { + unsigned long phys; + unsigned long pfn; + + phys = page_to_phys(*pages); +#if defined CONFIG_ARM64 + phys &= PHYS_48BIT_MASK; +#endif + pfn = PFN_DOWN(phys); + *pte = __pfn_to_mfn(pfn) << PAGE_SHIFT; + } + } + + mmu->deleter = deleter; + mc_dev_devel("wrapped mmu %p: len %u off %u flg %x pmd table %lx", + mmu, mmu->length, mmu->offset, mmu->flags, + mmu->pmd_table.page); + return mmu; + +err: + tee_mmu_delete(mmu); +#endif + return ERR_PTR(ret); +} + +void tee_mmu_set_deleter(struct tee_mmu *mmu, struct tee_deleter *deleter) +{ + mmu->deleter = deleter; +} + +static void tee_mmu_release(struct kref *kref) +{ + struct tee_mmu *mmu = container_of(kref, struct tee_mmu, kref); + + mc_dev_devel("free mmu %p: %s len %u off %u pmd table %lx", + mmu, mmu->user ? "user" : "kernel", mmu->length, + mmu->offset, mmu->pmd_table.page); + tee_mmu_delete(mmu); +} + +void tee_mmu_get(struct tee_mmu *mmu) +{ + kref_get(&mmu->kref); +} + +void tee_mmu_put(struct tee_mmu *mmu) +{ + kref_put(&mmu->kref, tee_mmu_release); +} + +void tee_mmu_buffer(struct tee_mmu *mmu, struct mcp_buffer_map *map) +{ + if (mmu->use_pages_and_vas) + map->addr = mmu->pmd_table.page; + else + map->addr = virt_to_phys(mmu->pmd_table.addr); + + map->secure_va = 0; + map->offset = mmu->offset; + map->length = mmu->length; + map->nr_pages = mmu->nr_pages; + map->flags = mmu->flags; + map->type = WSM_L1; + map->mmu = mmu; +} + +int tee_mmu_debug_structs(struct kasnprintf_buf *buf, const struct tee_mmu *mmu) +{ + return kasnprintf(buf, + "\t\t\tmmu %pK: %s len %u off %u table %pK\n", + mmu, mmu->user ? "user" : "kernel", mmu->length, + mmu->offset, (void *)mmu->pmd_table.page); +} diff --git a/drivers/gud/MobiCoreDriver/mmu.h b/drivers/gud/MobiCoreDriver/mmu.h new file mode 100644 index 000000000000..3a6185411f18 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mmu.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef _TBASE_MEM_H_ +#define _TBASE_MEM_H_ + +/* + * This represents the maximum number of entries in a Page Table Entries + * array which maps one 4KiB page. Each entry is 64 bits long physical + * address with some possible flags. With 512 entries it is possible + * to map 2MiB memory block. + */ +#define PTE_ENTRIES_MAX 512 + +/* + * This represents the maximum number of entries in a Page Middle Directory + * which maps one 4KiB page. Each entry is a 64 bits physical address that + * points to a PTE. With 512 entries t is possible to map 1GB memory block. + */ +#define PMD_ENTRIES_MAX 512 + +struct tee_mmu; +struct mcp_buffer_map; + +struct tee_deleter { + void *object; + void (*delete)(void *object); +}; + +/* + * Allocate MMU table and map buffer into it. + * That is, create respective table entries. + */ +struct tee_mmu *tee_mmu_create(struct mm_struct *mm, + const struct mc_ioctl_buffer *buf); + +/* + * Allocate MMU table and map pages into it. + * This is for Xen Dom0 to re-create a buffer with existing pages. + */ +struct tee_mmu *tee_mmu_wrap(struct tee_deleter *deleter, struct page **pages, + const struct mcp_buffer_map *buf); + +/* + * Give the MMU an object to release when released + */ +void tee_mmu_set_deleter(struct tee_mmu *mmu, struct tee_deleter *deleter); + +/* + * Gets a reference on a MMU table. + */ +void tee_mmu_get(struct tee_mmu *mmu); + +/* + * Puts a reference on a MMU table. + */ +void tee_mmu_put(struct tee_mmu *mmu); + +/* + * Fill in buffer info for MMU table. + */ +void tee_mmu_buffer(struct tee_mmu *mmu, struct mcp_buffer_map *map); + +/* + * Add info to debug buffer. + */ +int tee_mmu_debug_structs(struct kasnprintf_buf *buf, + const struct tee_mmu *mmu); + +#endif /* _TBASE_MEM_H_ */ diff --git a/drivers/gud/MobiCoreDriver/mobicore_driver_api.h b/drivers/gud/MobiCoreDriver/mobicore_driver_api.h new file mode 100644 index 000000000000..13414f69429b --- /dev/null +++ b/drivers/gud/MobiCoreDriver/mobicore_driver_api.h @@ -0,0 +1,467 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ +#ifndef _MOBICORE_DRIVER_API_H_ +#define _MOBICORE_DRIVER_API_H_ + +#include "mc_user.h" + +#define __MC_CLIENT_LIB_API + +/* + * Return values of MobiCore driver functions. + */ +enum mc_result { + /* Function call succeeded. */ + MC_DRV_OK = 0, + /* No notification available. */ + MC_DRV_NO_NOTIFICATION = 1, + /* Error during notification on communication level. */ + MC_DRV_ERR_NOTIFICATION = 2, + /* Function not implemented. */ + MC_DRV_ERR_NOT_IMPLEMENTED = 3, + /* No more resources available. */ + MC_DRV_ERR_OUT_OF_RESOURCES = 4, + /* Driver initialization failed. */ + MC_DRV_ERR_INIT = 5, + /* Unknown error. */ + MC_DRV_ERR_UNKNOWN = 6, + /* The specified device is unknown. */ + MC_DRV_ERR_UNKNOWN_DEVICE = 7, + /* The specified session is unknown.*/ + MC_DRV_ERR_UNKNOWN_SESSION = 8, + /* The specified operation is not allowed. */ + MC_DRV_ERR_INVALID_OPERATION = 9, + /* The response header from the MC is invalid. */ + MC_DRV_ERR_INVALID_RESPONSE = 10, + /* Function call timed out. */ + MC_DRV_ERR_TIMEOUT = 11, + /* Can not allocate additional memory. */ + MC_DRV_ERR_NO_FREE_MEMORY = 12, + /* Free memory failed. */ + MC_DRV_ERR_FREE_MEMORY_FAILED = 13, + /* Still some open sessions pending. */ + MC_DRV_ERR_SESSION_PENDING = 14, + /* MC daemon not reachable */ + MC_DRV_ERR_DAEMON_UNREACHABLE = 15, + /* The device file of the kernel module could not be opened. */ + MC_DRV_ERR_INVALID_DEVICE_FILE = 16, + /* Invalid parameter. */ + MC_DRV_ERR_INVALID_PARAMETER = 17, + /* Unspecified error from Kernel Module*/ + MC_DRV_ERR_KERNEL_MODULE = 18, + /* Error during mapping of additional bulk memory to session. */ + MC_DRV_ERR_BULK_MAPPING = 19, + /* Error during unmapping of additional bulk memory to session. */ + MC_DRV_ERR_BULK_UNMAPPING = 20, + /* Notification received, exit code available. */ + MC_DRV_INFO_NOTIFICATION = 21, + /* Set up of NWd connection failed. */ + MC_DRV_ERR_NQ_FAILED = 22, + /* Wrong daemon version. */ + MC_DRV_ERR_DAEMON_VERSION = 23, + /* Wrong container version. */ + MC_DRV_ERR_CONTAINER_VERSION = 24, + /* System Trustlet public key is wrong. */ + MC_DRV_ERR_WRONG_PUBLIC_KEY = 25, + /* Wrong container type(s). */ + MC_DRV_ERR_CONTAINER_TYPE_MISMATCH = 26, + /* Container is locked (or not activated). */ + MC_DRV_ERR_CONTAINER_LOCKED = 27, + /* SPID is not registered with root container. */ + MC_DRV_ERR_SP_NO_CHILD = 28, + /* UUID is not registered with sp container. */ + MC_DRV_ERR_TL_NO_CHILD = 29, + /* Unwrapping of root container failed. */ + MC_DRV_ERR_UNWRAP_ROOT_FAILED = 30, + /* Unwrapping of service provider container failed. */ + MC_DRV_ERR_UNWRAP_SP_FAILED = 31, + /* Unwrapping of Trustlet container failed. */ + MC_DRV_ERR_UNWRAP_TRUSTLET_FAILED = 32, + /* No device associated with connection. */ + MC_DRV_ERR_DAEMON_DEVICE_NOT_OPEN = 33, + /* TA blob attestation is incorrect. */ + MC_DRV_ERR_TA_ATTESTATION_ERROR = 34, + /* Interrupted system call. */ + MC_DRV_ERR_INTERRUPTED_BY_SIGNAL = 35, + /* Service is blocked and opensession is thus not allowed. */ + MC_DRV_ERR_SERVICE_BLOCKED = 36, + /* Service is locked and opensession is thus not allowed. */ + MC_DRV_ERR_SERVICE_LOCKED = 37, + /* Service was killed by the TEE (due to an administrative command). */ + MC_DRV_ERR_SERVICE_KILLED = 38, + /* All permitted instances to the service are used */ + MC_DRV_ERR_NO_FREE_INSTANCES = 39, + /* TA blob header is incorrect. */ + MC_DRV_ERR_TA_HEADER_ERROR = 40, +}; + +/* + * Structure of Session Handle, includes the Session ID and the Device ID the + * Session belongs to. + * The session handle will be used for session-based MobiCore communication. + * It will be passed to calls which address a communication end point in the + * MobiCore environment. + */ +struct mc_session_handle { + u32 session_id; /* MobiCore session ID */ + u32 device_id; /* Device ID the session belongs to */ +}; + +/* + * Information structure about additional mapped Bulk buffer between the + * Trustlet Connector (NWd) and the Trustlet (SWd). This structure is + * initialized from a Trustlet Connector by calling mc_map(). + * In order to use the memory within a Trustlet the Trustlet Connector has to + * inform the Trustlet with the content of this structure via the TCI. + */ +struct mc_bulk_map { + /* + * The virtual address of the Bulk buffer regarding the address space + * of the Trustlet, already includes a possible offset! + */ + u32 secure_virt_addr; + u32 secure_virt_len; /* Length of the mapped Bulk buffer */ +}; + +/* The default device ID */ +#define MC_DEVICE_ID_DEFAULT 0 +/* Wait infinite for a response of the MC. */ +#define MC_INFINITE_TIMEOUT ((s32)(-1)) +/* Do not wait for a response of the MC. */ +#define MC_NO_TIMEOUT 0 +/* TCI/DCI must not exceed 1MiB */ +#define MC_MAX_TCI_LEN 0x100000 + +/** + * mc_open_device() - Open a new connection to a MobiCore device. + * @device_id: Identifier for the MobiCore device to be used. + * MC_DEVICE_ID_DEFAULT refers to the default device. + * + * Initializes all device specific resources required to communicate with a + * MobiCore instance located on the specified device in the system. If the + * device does not exist the function will return MC_DRV_ERR_UNKNOWN_DEVICE. + * + * Return codes: + * MC_DRV_OK: operation completed successfully + * MC_DRV_ERR_INVALID_OPERATION: device already opened + * MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon + * MC_DRV_ERR_UNKNOWN_DEVICE: device_id unknown + * MC_DRV_ERR_INVALID_DEVICE_FILE: kernel module under /dev/mobicore + * cannot be opened + */ +__MC_CLIENT_LIB_API enum mc_result mc_open_device( + u32 device_id); + +/** + * mc_close_device() - Close the connection to a MobiCore device. + * @device_id: Identifier for the MobiCore device. + * + * When closing a device, active sessions have to be closed beforehand. + * Resources associated with the device will be released. + * The device may be opened again after it has been closed. + * + * MC_DEVICE_ID_DEFAULT refers to the default device. + * + * Return codes: + * MC_DRV_OK: operation completed successfully + * MC_DRV_ERR_UNKNOWN_DEVICE: device id is invalid + * MC_DRV_ERR_SESSION_PENDING: a session is still open + * MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon occur + */ +__MC_CLIENT_LIB_API enum mc_result mc_close_device( + u32 device_id); + +/** + * mc_open_session() - Open a new session to a Trustlet. + * @session: On success, the session data will be returned + * @uuid: UUID of the Trustlet to be opened + * @tci: TCI buffer for communicating with the Trustlet + * @tci_len: Length of the TCI buffer. Maximum allowed value + * is MC_MAX_TCI_LEN + * + * The Trustlet with the given UUID has to be available in the flash filesystem. + * + * Write MCP open message to buffer and notify MobiCore about the availability + * of a new command. + * + * Waits till the MobiCore responses with the new session ID (stored in the MCP + * buffer). + * + * Note that session.device_id has to be the device id of an opened device. + * + * Return codes: + * MC_DRV_OK: operation completed successfully + * MC_DRV_INVALID_PARAMETER: session parameter is invalid + * MC_DRV_ERR_UNKNOWN_DEVICE: device id is invalid + * MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon socket occur + * MC_DRV_ERR_NQ_FAILED: daemon returns an error + */ +__MC_CLIENT_LIB_API enum mc_result mc_open_session( + struct mc_session_handle *session, + const struct mc_uuid_t *uuid, + u8 *tci, + u32 tci_len); + +/** + * mc_open_trustlet() - Open a new session to the provided Trustlet. + * @session: On success, the session data will be returned + * @spid: Service Provider ID (for SP trustlets otherwise ignored) + * @trustlet Memory buffer containing the Trusted Application binary + * @trustlet_len Trusted Application length + * @tci: TCI buffer for communicating with the Trustlet + * @tci_len: Length of the TCI buffer. Maximum allowed value + * is MC_MAX_TCI_LEN + * + * Write MCP open message to buffer and notify MobiCore about the availability + * of a new command. + * + * Waits till the MobiCore responses with the new session ID (stored in the MCP + * buffer). + * + * Note that session.device_id has to be the device id of an opened device. + * + * Return codes: + * MC_DRV_OK: operation completed successfully + * MC_DRV_INVALID_PARAMETER: session parameter is invalid + * MC_DRV_ERR_UNKNOWN_DEVICE: device id is invalid + * MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon socket occur + * MC_DRV_ERR_NQ_FAILED: daemon returns an error + */ +__MC_CLIENT_LIB_API enum mc_result mc_open_trustlet( + struct mc_session_handle *session, + u32 spid, + u8 *trustlet, + u32 trustlet_len, + u8 *tci, + u32 len); + +/** + * mc_close_session() - Close a Trustlet session. + * @session: Session to be closed. + * + * Closes the specified MobiCore session. The call will block until the + * session has been closed. + * + * Device device_id has to be opened in advance. + * + * Return codes: + * MC_DRV_OK: operation completed successfully + * MC_DRV_INVALID_PARAMETER: session parameter is invalid + * MC_DRV_ERR_UNKNOWN_SESSION: session id is invalid + * MC_DRV_ERR_UNKNOWN_DEVICE: device id of session is invalid + * MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon occur + * MC_DRV_ERR_INVALID_DEVICE_FILE: daemon cannot open Trustlet file + */ +__MC_CLIENT_LIB_API enum mc_result mc_close_session( + struct mc_session_handle *session); + +/** + * mc_notify() - Notify a session. + * @session: The session to be notified. + * + * Notifies the session end point about available message data. + * If the session parameter is correct, notify will always succeed. + * Corresponding errors can only be received by mc_wait_notification(). + * + * A session has to be opened in advance. + * + * Return codes: + * MC_DRV_OK: operation completed successfully + * MC_DRV_INVALID_PARAMETER: session parameter is invalid + * MC_DRV_ERR_UNKNOWN_SESSION: session id is invalid + * MC_DRV_ERR_UNKNOWN_DEVICE: device id of session is invalid + */ +__MC_CLIENT_LIB_API enum mc_result mc_notify( + struct mc_session_handle *session); + +/** + * mc_wait_notification() - Wait for a notification. + * @session: The session the notification should correspond to. + * @timeout: Time in milliseconds to wait + * (MC_NO_TIMEOUT : direct return, > 0 : milliseconds, + * MC_INFINITE_TIMEOUT : wait infinitely) + * + * Wait for a notification issued by the MobiCore for a specific session. + * The timeout parameter specifies the number of milliseconds the call will wait + * for a notification. + * + * If the caller passes 0 as timeout value the call will immediately return. + * If timeout value is below 0 the call will block until a notification for the + * session has been received. + * + * If timeout is below 0, call will block. + * + * Caller has to trust the other side to send a notification to wake him up + * again. + * + * Return codes: + * MC_DRV_OK: operation completed successfully + * MC_DRV_ERR_TIMEOUT: no notification arrived in time + * MC_DRV_INFO_NOTIFICATION: a problem with the session was + * encountered. Get more details with + * mc_get_session_error_code() + * MC_DRV_ERR_NOTIFICATION: a problem with the socket occurred + * MC_DRV_INVALID_PARAMETER: a parameter is invalid + * MC_DRV_ERR_UNKNOWN_SESSION: session id is invalid + * MC_DRV_ERR_UNKNOWN_DEVICE: device id of session is invalid + */ +__MC_CLIENT_LIB_API enum mc_result mc_wait_notification( + struct mc_session_handle *session, + s32 timeout); + +/** + * mc_malloc_wsm() - Allocate a block of world shared memory (WSM). + * @device_id: The ID of an opened device to retrieve the WSM from. + * @align: The alignment (number of pages) of the memory block + * (e.g. 0x00000001 for 4kb). + * @len: Length of the block in bytes. + * @wsm: Virtual address of the world shared memory block. + * @wsm_flags: Platform specific flags describing the memory to + * be allocated. + * + * The MC driver allocates a contiguous block of memory which can be used as + * WSM. + * This implicates that the allocated memory is aligned according to the + * alignment parameter. + * + * Always returns a buffer of size WSM_SIZE aligned to 4K. + * + * Align and wsm_flags are currently ignored + * + * Return codes: + * MC_DRV_OK: operation completed successfully + * MC_DRV_INVALID_PARAMETER: a parameter is invalid + * MC_DRV_ERR_UNKNOWN_DEVICE: device id is invalid + * MC_DRV_ERR_NO_FREE_MEMORY: no more contiguous memory is + * available in this size or for this + * process + */ +__MC_CLIENT_LIB_API enum mc_result mc_malloc_wsm( + u32 device_id, + u32 align, + u32 len, + u8 **wsm, + u32 wsm_flags); + +/** + * mc_free_wsm() - Free a block of world shared memory (WSM). + * @device_id: The ID to which the given address belongs + * @wsm: Address of WSM block to be freed + * + * The MC driver will free a block of world shared memory (WSM) previously + * allocated with mc_malloc_wsm(). The caller has to assure that the address + * handed over to the driver is a valid WSM address. + * + * Return codes: + * MC_DRV_OK: operation completed successfully + * MC_DRV_INVALID_PARAMETER: a parameter is invalid + * MC_DRV_ERR_UNKNOWN_DEVICE: when device id is invalid + * MC_DRV_ERR_FREE_MEMORY_FAILED: on failure + */ +__MC_CLIENT_LIB_API enum mc_result mc_free_wsm( + u32 device_id, + u8 *wsm); + +/** + *mc_map() - Map additional bulk buffer between a Trustlet Connector (TLC) + * and the Trustlet (TL) for a session + * @session: Session handle with information of the device_id and + * the session_id. The given buffer is mapped to the + * session specified in the sessionHandle + * @buf: Virtual address of a memory portion (relative to TLC) + * to be shared with the Trustlet, already includes a + * possible offset! + * @len: length of buffer block in bytes. + * @map_info: Information structure about the mapped Bulk buffer + * between the TLC (NWd) and the TL (SWd). + * + * Memory allocated in user space of the TLC can be mapped as additional + * communication channel (besides TCI) to the Trustlet. Limitation of the + * Trustlet memory structure apply: only 6 chunks can be mapped with a maximum + * chunk size of 1 MiB each. + * + * It is up to the application layer (TLC) to inform the Trustlet + * about the additional mapped bulk memory. + * + * Return codes: + * MC_DRV_OK: operation completed successfully + * MC_DRV_INVALID_PARAMETER: a parameter is invalid + * MC_DRV_ERR_UNKNOWN_SESSION: session id is invalid + * MC_DRV_ERR_UNKNOWN_DEVICE: device id of session is invalid + * MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon occur + * MC_DRV_ERR_BULK_MAPPING: buf is already uses as bulk buffer or + * when registering the buffer failed + */ +__MC_CLIENT_LIB_API enum mc_result mc_map( + struct mc_session_handle *session, + void *buf, + u32 len, + struct mc_bulk_map *map_info); + +/** + * mc_unmap() - Remove additional mapped bulk buffer between Trustlet Connector + * (TLC) and the Trustlet (TL) for a session + * @session: Session handle with information of the device_id and + * the session_id. The given buffer is unmapped from the + * session specified in the sessionHandle. + * @buf: Virtual address of a memory portion (relative to TLC) + * shared with the TL, already includes a possible offset! + * @map_info: Information structure about the mapped Bulk buffer + * between the TLC (NWd) and the TL (SWd) + * + * The bulk buffer will immediately be unmapped from the session context. + * + * The application layer (TLC) must inform the TL about unmapping of the + * additional bulk memory before calling mc_unmap! + * + * The clientlib currently ignores the len field in map_info. + * + * Return codes: + * MC_DRV_OK: operation completed successfully + * MC_DRV_INVALID_PARAMETER: a parameter is invalid + * MC_DRV_ERR_UNKNOWN_SESSION: session id is invalid + * MC_DRV_ERR_UNKNOWN_DEVICE: device id of session is invalid + * MC_DRV_ERR_DAEMON_UNREACHABLE: problems with daemon occur + * MC_DRV_ERR_BULK_UNMAPPING: buf was not registered earlier + * or when unregistering failed + */ +__MC_CLIENT_LIB_API enum mc_result mc_unmap( + struct mc_session_handle *session, + void *buf, + struct mc_bulk_map *map_info); + +/* + * mc_get_session_error_code() - Get additional error information of the last + * error that occurred on a session. + * @session: Session handle with information of the device_id and + * the session_id + * @exit_code: >0 Trustlet has terminated itself with this value, + * <0 Trustlet is dead because of an error within the + * MobiCore (e.g. Kernel exception). See also MCI + * definition. + * + * After the request the stored error code will be deleted. + * + * Return codes: + * MC_DRV_OK: operation completed successfully + * MC_DRV_INVALID_PARAMETER: a parameter is invalid + * MC_DRV_ERR_UNKNOWN_SESSION: session id is invalid + * MC_DRV_ERR_UNKNOWN_DEVICE: device id of session is invalid + */ +__MC_CLIENT_LIB_API enum mc_result mc_get_session_error_code( + struct mc_session_handle *session, + s32 *exit_code); + +#endif /* _MOBICORE_DRIVER_API_H_ */ diff --git a/drivers/gud/MobiCoreDriver/nq.c b/drivers/gud/MobiCoreDriver/nq.c new file mode 100644 index 000000000000..11bb7e58cc1e --- /dev/null +++ b/drivers/gud/MobiCoreDriver/nq.c @@ -0,0 +1,1179 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2020 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE +#include /* local_clock */ +#endif +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +#include +#endif + +#include "platform.h" /* CPU-related information */ + +#include "mc_user.h" +#include "mc_linux_api.h" + +#include "mcifc.h" +#include "mciiwp.h" +#include "mcimcp.h" +#include "mcinq.h" +#include "mcitime.h" /* struct mcp_time */ + +#include "main.h" +#include "clock.h" +#include "fastcall.h" +#include "logging.h" +#include "nq.h" + +#define NQ_NUM_ELEMS 64 +#define SCHEDULING_FREQ 5 /**< N-SIQ every n-th time */ +#define DEFAULT_TIMEOUT_MS 20000 /* We do nothing on timeout anyway */ + +/* PLAT_DEFAULT_TEE_AFFINITY_MASK + * should be defined in platform.h + * if TEE affinity is not set to all cores + */ +#ifndef PLAT_DEFAULT_TEE_AFFINITY_MASK +#define PLAT_DEFAULT_TEE_AFFINITY_MASK (0xFFFF) +#endif + +static struct { + struct mutex buffer_mutex; /* Lock on SWd communication buffer */ + struct mcp_buffer *mcp_buffer; + struct interworld_session *iwp_buffer; + struct task_struct *irq_bh_thread; + struct completion irq_bh_complete; + bool irq_bh_thread_run; + int irq; + struct blocking_notifier_head tee_stop_notifiers; + void (*mcp_notif_handler)(u32 id, u32 payload); + void (*iwp_notif_handler)(u32 id, u32 payload); + /* MobiCore MCI information */ + unsigned int order; + union { + void *mci; + struct { + struct notification_queue *tx; + struct notification_queue *rx; + } nq; + }; + /* + * This notifications list is to be used to queue notifications when the + * notification queue overflows, so no session gets its notification + * lost, especially MCP. + */ + struct mutex notifications_mutex; + struct list_head notifications; + /* Dump buffer */ + char *tee_version; + struct kasnprintf_buf dump; + /* Time */ + struct mcp_time *time; + /* Protects above shared MCP time */ + struct mutex mcp_time_mutex; + + /* Scheduler */ + struct task_struct *tee_scheduler_thread; + bool tee_scheduler_run; + bool tee_hung; + int boot_ret; + struct completion boot_complete; /* Signal end of boot */ + struct completion idle_complete; /* Unblock scheduler thread */ + struct completion sleep_complete; /* Wait for sleep status */ + struct mutex sleep_mutex; /* Protect sleep request */ + struct mutex request_mutex; /* Protect all below */ + /* The order of this enum matters */ + enum sched_command { + NONE, /* No specific request */ + YIELD, /* Run the SWd */ + NSIQ, /* Schedule the SWd */ + } request; + bool suspended; + + /* Logging */ + phys_addr_t log_buffer; + u32 log_buffer_size; + bool log_buffer_busy; + struct cpumask current_cpumask; + struct mutex cpumask_mutex; /* protect cpumask access */ +} l_ctx; + +static inline bool is_iwp_id(u32 id) +{ + return (id & SID_IWP_NOTIFICATION) != 0; +} + +static inline void session_state_update_internal(struct nq_session *session, + enum nq_notif_state state) +{ + mutex_lock(&session->mutex); + session->state = state; + session->cpu_clk = local_clock(); + mutex_unlock(&session->mutex); +} + +/* + * Notification Queue overflow management: + * - once the SWd NQ is full, sessions get added to the overflow queue: + * 'l_ctx.notifications' + * - as long as this queue is not empty, new notifications get added there + * first, if not already present, then the queue is flushed + * - the queue is also flushed by the scheduler once the SWd has run + */ +static inline bool notif_queue_full(void) +{ + struct notification_queue *tx = l_ctx.nq.tx; + + return (tx->hdr.write_cnt - tx->hdr.read_cnt) == tx->hdr.queue_size; +} + +static inline void notif_queue_push(u32 session_id, u32 payload) +{ + struct notification_queue_header *hdr = &l_ctx.nq.tx->hdr; + u32 i = hdr->write_cnt % hdr->queue_size; + + l_ctx.nq.tx->notification[i].session_id = session_id; + l_ctx.nq.tx->notification[i].payload = payload; + /* + * Ensure notification[] is written before we update the counter + * We want a ARM dmb() / ARM64 dmb(sy) here + */ + smp_mb(); + + hdr->write_cnt++; + /* + * Ensure write_cnt is written before new notification + * We want a ARM dsb() / ARM64 dsb(sy) here + */ + rmb(); +} + +static void retrieve_last_session_payload(u32 *session_id, u32 *payload) +{ + struct notification_queue_header *hdr = &l_ctx.nq.tx->hdr; + u32 i = (hdr->write_cnt - 1) % hdr->queue_size; + + *session_id = l_ctx.nq.tx->notification[i].session_id; + *payload = l_ctx.nq.tx->notification[i].payload; +} + +/* Must be called with l_ctx.notifications_mutex taken */ +static inline bool nq_notifications_flush(void) +{ + bool flushed = false; + + while (!list_empty(&l_ctx.notifications) && !notif_queue_full()) { + struct nq_session *session; + + session = list_first_entry(&l_ctx.notifications, + struct nq_session, list); + mc_dev_devel("pop %x", session->id); + notif_queue_push(session->id, session->payload); + session_state_update_internal(session, NQ_NOTIF_SENT); + list_del_init(&session->list); + flushed = true; + } + + return flushed; +} + +static int nq_scheduler_command(enum sched_command command) +{ + if (IS_ERR_OR_NULL(l_ctx.tee_scheduler_thread)) + return -EFAULT; + + mutex_lock(&l_ctx.request_mutex); + if (l_ctx.request < command) { + l_ctx.request = command; + complete(&l_ctx.idle_complete); + } + + mutex_unlock(&l_ctx.request_mutex); + return 0; +} + +static inline void nq_update_time(void) +{ +#if KERNEL_VERSION(5, 8, 0) <= LINUX_VERSION_CODE + struct timespec64 tm1, tm2; + + ktime_get_ts64(&tm1); + ktime_get_raw_ts64(&tm2); +#else + struct timespec tm1, tm2; + + getnstimeofday(&tm1); + getrawmonotonic(&tm2); +#endif + + mutex_lock(&l_ctx.mcp_time_mutex); + /* set for REE time (current time, may be affected by user timezone) */ + l_ctx.time->wall_clock_seconds = tm1.tv_sec; + l_ctx.time->wall_clock_nsec = tm1.tv_nsec; + /* set for TEE time (absolute time, direct raw counter) */ + l_ctx.time->monotonic_seconds = tm2.tv_sec; + l_ctx.time->monotonic_nsec = tm2.tv_nsec; + mutex_unlock(&l_ctx.mcp_time_mutex); +} + +static inline void nq_notif_handler(u32 id, u32 payload) +{ + mc_dev_devel("NQ notif for id %x payload %x", id, payload); + if (is_iwp_id(id)) + l_ctx.iwp_notif_handler(id, payload); + else + l_ctx.mcp_notif_handler(id, payload); +} + +static int irq_bh_worker(void *arg) +{ + struct notification_queue *rx = l_ctx.nq.rx; + + while (1) { + wait_for_completion_killable(&l_ctx.irq_bh_complete); + + /* This thread can only be stopped with nq_stop */ + if (!l_ctx.irq_bh_thread_run) + break; + + /* Deal with all pending notifications in one go */ + while ((rx->hdr.write_cnt - rx->hdr.read_cnt) > 0) { + struct notification nf; + + nf = rx->notification[ + rx->hdr.read_cnt % rx->hdr.queue_size]; + + /* + * Ensure read_cnt writing happens after buffer read + * We want a ARM dmb() / ARM64 dmb(sy) here + */ + smp_mb(); + rx->hdr.read_cnt++; + /* + * Ensure read_cnt writing finishes before reader + * We want a ARM dsb() / ARM64 dsb(sy) here + */ + rmb(); + nq_notif_handler(nf.session_id, nf.payload); + } + + /* + * Finished processing notifications. It does not matter whether + * there actually were any notification or not. S-SIQs can also + * be triggered by an SWd driver which was waiting for a FIQ. + * In this case the S-SIQ tells NWd that SWd is no longer idle + * an will need scheduling again. + */ + nq_scheduler_command(NSIQ); + } + return 0; +} + +static irqreturn_t irq_handler(int intr, void *arg) +{ + /* wake up thread to continue handling this interrupt */ + complete(&l_ctx.irq_bh_complete); + return IRQ_HANDLED; +} + +void nq_session_init(struct nq_session *session, bool is_gp) +{ + session->id = SID_INVALID; + session->payload = 0; + INIT_LIST_HEAD(&session->list); + mutex_init(&session->mutex); + session->state = NQ_NOTIF_IDLE; + session->cpu_clk = 0; + session->is_gp = is_gp; +} + +void nq_session_exit(struct nq_session *session) +{ + mutex_lock(&l_ctx.notifications_mutex); + if (!list_empty(&session->list)) + list_del(&session->list); + mutex_unlock(&l_ctx.notifications_mutex); +} + +void nq_session_state_update(struct nq_session *session, + enum nq_notif_state state) +{ + if (state < NQ_NOTIF_RECEIVED) + return; + + session_state_update_internal(session, state); +} + +int nq_session_notify(struct nq_session *session, u32 id, u32 payload) +{ + int ret = 0; + + mutex_lock(&l_ctx.notifications_mutex); + session->id = id; + session->payload = payload; + if (!list_empty(&l_ctx.notifications) || notif_queue_full()) { + if (!list_empty(&session->list)) { + ret = -EAGAIN; + if (payload != session->payload) { + mc_dev_err(ret, + "skip %x payload change %x -> %x", + session->id, session->payload, + payload); + } else { + mc_dev_devel("skip %x payload %x", + session->id, payload); + } + } else { + mc_dev_devel("push %x payload %x", session->id, + payload); + /* session->payload = payload; */ + list_add_tail(&session->list, &l_ctx.notifications); + session_state_update_internal(session, NQ_NOTIF_QUEUED); + } + + nq_notifications_flush(); + + if (nq_scheduler_command(YIELD)) + ret = -EPROTO; + } else { + mc_dev_devel("send %x payload %x", session->id, payload); + notif_queue_push(session->id, payload); + session_state_update_internal(session, NQ_NOTIF_SENT); + if (nq_scheduler_command(NSIQ)) + ret = -EPROTO; + } + + mutex_unlock(&l_ctx.notifications_mutex); + return ret; +} + +const char *nq_session_state(const struct nq_session *session, u64 *cpu_clk) +{ + if (cpu_clk) + *cpu_clk = session->cpu_clk; + + switch (session->state) { + case NQ_NOTIF_IDLE: + return "idle"; + case NQ_NOTIF_QUEUED: + return "queued"; + case NQ_NOTIF_SENT: + return "sent"; + case NQ_NOTIF_RECEIVED: + return "received"; + case NQ_NOTIF_CONSUMED: + return "consumed"; + case NQ_NOTIF_DEAD: + return "dead"; + } + return "error"; +} + +static ssize_t debug_crashdump_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + if (l_ctx.dump.off) + return simple_read_from_buffer(user_buf, count, ppos, + l_ctx.dump.buf, l_ctx.dump.off); + + return 0; +} + +static const struct file_operations debug_crashdump_ops = { + .read = debug_crashdump_read, + .llseek = default_llseek, +}; + +static ssize_t debug_smclog_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + return debug_generic_read(file, user_buf, count, ppos, + mc_fastcall_debug_smclog); +} + +static const struct file_operations debug_smclog_ops = { + .read = debug_smclog_read, + .llseek = default_llseek, + .open = debug_generic_open, + .release = debug_generic_release, +}; + +static void nq_dump_status(void) +{ + static const struct { + unsigned int index; + const char *msg; + } status_map[] = { + /**< MobiCore control flags */ + { MC_EXT_INFO_ID_FLAGS, "flags"}, + /**< MobiCore halt condition code */ + { MC_EXT_INFO_ID_HALT_CODE, "haltCode"}, + /**< MobiCore halt condition instruction pointer */ + { MC_EXT_INFO_ID_HALT_IP, "haltIp"}, + /**< MobiCore fault counter */ + { MC_EXT_INFO_ID_FAULT_CNT, "faultRec.cnt"}, + /**< MobiCore last fault cause */ + { MC_EXT_INFO_ID_FAULT_CAUSE, "faultRec.cause"}, + /**< MobiCore last fault meta */ + { MC_EXT_INFO_ID_FAULT_META, "faultRec.meta"}, + /**< MobiCore last fault threadid */ + { MC_EXT_INFO_ID_FAULT_THREAD, "faultRec.thread"}, + /**< MobiCore last fault instruction pointer */ + { MC_EXT_INFO_ID_FAULT_IP, "faultRec.ip"}, + /**< MobiCore last fault stack pointer */ + { MC_EXT_INFO_ID_FAULT_SP, "faultRec.sp"}, + /**< MobiCore last fault ARM arch information */ + { MC_EXT_INFO_ID_FAULT_ARCH_DFSR, "faultRec.arch.dfsr"}, + /**< MobiCore last fault ARM arch information */ + { MC_EXT_INFO_ID_FAULT_ARCH_ADFSR, "faultRec.arch.adfsr"}, + /**< MobiCore last fault ARM arch information */ + { MC_EXT_INFO_ID_FAULT_ARCH_DFAR, "faultRec.arch.dfar"}, + /**< MobiCore last fault ARM arch information */ + { MC_EXT_INFO_ID_FAULT_ARCH_IFSR, "faultRec.arch.ifsr"}, + /**< MobiCore last fault ARM arch information */ + { MC_EXT_INFO_ID_FAULT_ARCH_AIFSR, "faultRec.arch.aifsr"}, + /**< MobiCore last fault ARM arch information */ + { MC_EXT_INFO_ID_FAULT_ARCH_IFAR, "faultRec.arch.ifar"}, + /**< MobiCore configured by Daemon via fc_init flag */ + { MC_EXT_INFO_ID_MC_CONFIGURED, "mcData.flags"}, + /**< MobiCore exception handler last partner */ + { MC_EXT_INFO_ID_MC_EXC_PARTNER, "mcExcep.partner"}, + /**< MobiCore exception handler last peer */ + { MC_EXT_INFO_ID_MC_EXC_IPCPEER, "mcExcep.peer"}, + /**< MobiCore exception handler last IPC message */ + { MC_EXT_INFO_ID_MC_EXC_IPCMSG, "mcExcep.cause"}, + /**< MobiCore exception handler last IPC data */ + {MC_EXT_INFO_ID_MC_EXC_IPCDATA, "mcExcep.meta"}, + /**< MobiCore last crashing task offset */ + {MC_EXT_INFO_ID_TASK_OFFSET, + "faultRec.offset.task"}, + /**< MobiCore last crashing task's mcLib offset */ + {MC_EXT_INFO_ID_MCLIB_OFFSET, + "faultRec.offset.mclib"}, + }; + + char uuid_str[33]; + int ret = 0; + size_t i; + + if (l_ctx.dump.off) + ret = -EBUSY; + + mc_dev_info("TEE HALTED"); + if (l_ctx.tee_version) { + mc_dev_info("TEE version: %s", l_ctx.tee_version); + if (ret >= 0) + ret = kasnprintf(&l_ctx.dump, "TEE version: %s\n", + l_ctx.tee_version); + } + + mc_dev_info("Status dump:"); + for (i = 0; i < (size_t)ARRAY_SIZE(status_map); i++) { + u32 info; + + if (fc_info(status_map[i].index, NULL, &info)) + return; + + mc_dev_info(" %-22s= 0x%08x", status_map[i].msg, info); + if (ret >= 0) + ret = kasnprintf(&l_ctx.dump, "%-22s= 0x%08x\n", + status_map[i].msg, info); + } + + /* construct UUID string */ + for (i = 0; i < 4; i++) { + u32 info; + size_t j; + + if (fc_info(MC_EXT_INFO_ID_MC_EXC_UUID + i, NULL, &info)) + return; + + for (j = 0; j < sizeof(info); j++) { + snprintf(&uuid_str[(i * sizeof(info) + j) * 2], 3, + "%02x", (info >> (j * 8)) & 0xff); + } + } + + mc_dev_info(" %-22s= 0x%s", "mcExcep.uuid", uuid_str); + if (ret >= 0) + ret = kasnprintf(&l_ctx.dump, "%-22s= 0x%s\n", "mcExcep.uuid", + uuid_str); + + if (ret < 0) { + kfree(l_ctx.dump.buf); + l_ctx.dump.off = 0; + return; + } + + debugfs_create_file("crashdump", 0400, g_ctx.debug_dir, NULL, + &debug_crashdump_ops); + debugfs_create_file("last_smc_commands", 0400, g_ctx.debug_dir, NULL, + &debug_smclog_ops); +} + +static void nq_handle_tee_crash(void) +{ + /* + * Do not change the call order: the debugfs nq status file needs + * to be created before requesting the Daemon to read it. + */ + nq_dump_status(); + blocking_notifier_call_chain(&l_ctx.tee_stop_notifiers, 0, NULL); +} + +static inline void set_sleep_mode_rq(u16 sleep_req) +{ + mutex_lock(&l_ctx.buffer_mutex); + l_ctx.mcp_buffer->flags.sleep_mode.sleep_req = sleep_req; + mutex_unlock(&l_ctx.buffer_mutex); +} + +static inline bool nq_suspended(void) +{ + struct mcp_flags *flags = &l_ctx.mcp_buffer->flags; + bool ret; + + mutex_lock(&l_ctx.buffer_mutex); + ret = flags->sleep_mode.ready_to_sleep & MC_STATE_READY_TO_SLEEP; + if (!ret) { + mc_dev_devel("IDLE=%d", flags->schedule); + mc_dev_devel("Request Sleep=%d", flags->sleep_mode.sleep_req); + mc_dev_devel("Sleep Ready=%d", + flags->sleep_mode.ready_to_sleep); + } + + mutex_unlock(&l_ctx.buffer_mutex); + return ret; +} + +/* + * Get the requested SWd sleep timeout value (ms) + * - if the timeout is -1, wait indefinitely + * - if the timeout is 0, re-schedule immediately (timeouts in µs in the SWd) + * - otherwise sleep for the required time + * returns true if sleep is required, false otherwise + */ +static inline bool nq_get_idle_timeout(s32 *timeout) +{ + u32 schedule; + bool ret; + + mutex_lock(&l_ctx.buffer_mutex); + schedule = l_ctx.mcp_buffer->flags.schedule; + if (schedule == MC_FLAG_SCHEDULE_IDLE) { + *timeout = l_ctx.mcp_buffer->flags.timeout_ms; + ret = true; + } else { + ret = false; + } + + mutex_unlock(&l_ctx.buffer_mutex); + return ret; +} + +union mcp_message *nq_get_mcp_buffer(void) +{ + return &l_ctx.mcp_buffer->message; +} + +struct interworld_session *nq_get_iwp_buffer(void) +{ + return l_ctx.iwp_buffer; +} + +void nq_set_version_ptr(char *version) +{ + l_ctx.tee_version = version; +} + +void nq_register_notif_handler(void (*handler)(u32 id, u32 payload), bool iwp) +{ + if (iwp) + l_ctx.iwp_notif_handler = handler; + else + l_ctx.mcp_notif_handler = handler; +} + +int nq_register_tee_stop_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&l_ctx.tee_stop_notifiers, nb); +} + +int nq_unregister_tee_stop_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&l_ctx.tee_stop_notifiers, + nb); +} + +ssize_t nq_get_stop_message(char __user *buffer, size_t size) +{ + size_t max_len = l_ctx.dump.size - l_ctx.dump.off; + char *buf = l_ctx.dump.buf; + int ret; + + if (!l_ctx.dump.off || !max_len) + return 0; + + if (size > max_len) + size = max_len; + + ret = copy_to_user(buffer, buf, size); + if (ret) + return -EFAULT; + + return size; +} + +void nq_signal_tee_hung(void) +{ + mc_dev_devel("force stop the notification queue"); + /* Stop the tee_scheduler thread */ + l_ctx.tee_hung = true; + l_ctx.tee_scheduler_run = false; + complete(&l_ctx.idle_complete); + nq_scheduler_command(NONE); +} + +static int nq_boot_tee(void) +{ + size_t q_len = ALIGN(2 * (sizeof(struct notification_queue_header) + + NQ_NUM_ELEMS * sizeof(struct notification)), 4); + struct irq_data *irq_d = irq_get_irq_data(l_ctx.irq); + int ret; + + /* Call the INIT fastcall to setup shared buffers */ + ret = fc_init(virt_to_phys(l_ctx.mci), + (uintptr_t)l_ctx.mcp_buffer - (uintptr_t)l_ctx.mci, q_len, + sizeof(*l_ctx.mcp_buffer)); + logging_run(); + if (ret) + return ret; + + /* Set initialization values */ +#if defined(MC_INTR_SSIQ_SWD) + l_ctx.mcp_buffer->message.init_values.flags |= MC_IV_FLAG_IRQ; + l_ctx.mcp_buffer->message.init_values.irq = MC_INTR_SSIQ_SWD; +#endif + l_ctx.mcp_buffer->message.init_values.flags |= MC_IV_FLAG_TIME; + if (irq_d) + l_ctx.mcp_buffer->message.init_values.irq = irq_d->hwirq; + l_ctx.mcp_buffer->message.init_values.time_ofs = + (u32)((uintptr_t)l_ctx.time - (uintptr_t)l_ctx.mci); + l_ctx.mcp_buffer->message.init_values.time_len = + sizeof(*l_ctx.time); + + l_ctx.mcp_buffer->message.init_values.flags |= MC_IV_FLAG_IWP; + l_ctx.mcp_buffer->message.init_values.iws_buf_ofs = + (u64)((uintptr_t)l_ctx.iwp_buffer - (uintptr_t)l_ctx.mci); + l_ctx.mcp_buffer->message.init_values.iws_buf_size = + MAX_IW_SESSION * sizeof(struct interworld_session); + + /* First empty N-SIQ to setup of the MCI structure */ + ret = fc_nsiq(0, 0); + logging_run(); + if (ret) + return ret; + + /* + * Wait until the TEE state switches to MC_STATUS_INITIALIZED + * It is assumed that it always switches state at some point + */ + do { + u32 status = 0; + u32 timeslice; + + ret = fc_info(MC_EXT_INFO_ID_MCI_VERSION, &status, NULL); + logging_run(); + if (ret) + return ret; + + switch (status) { + case MC_STATUS_NOT_INITIALIZED: + /* Switch to the TEE to give it more CPU time. */ + ret = EAGAIN; + for (timeslice = 0; timeslice < 10; timeslice++) { + int tmp_ret = fc_yield(timeslice); + + logging_run(); + if (tmp_ret) + return tmp_ret; + } + + /* No need to loop like mad */ + if (ret == EAGAIN) + usleep_range(100, 500); + + break; + case MC_STATUS_HALT: + ret = -ENODEV; + nq_handle_tee_crash(); + mc_dev_err(ret, "halt during init, state 0x%x", status); + return ret; + case MC_STATUS_INITIALIZED: + mc_dev_devel("ready"); + break; + default: + /* MC_STATUS_BAD_INIT or anything else */ + ret = -EIO; + mc_dev_err(ret, "MCI init failed, state 0x%x", status); + return ret; + } + } while (ret == EAGAIN); + + return ret; +} + +static inline bool tee_sleep(s32 timeout_ms) +{ + bool infinite_timeout = timeout_ms < 0; + + /* TEE is going to sleep */ + mc_clock_disable(); + do { + s32 local_timeout_ms; + unsigned long jiffies; + + if (infinite_timeout) { + local_timeout_ms = DEFAULT_TIMEOUT_MS; + } else { + local_timeout_ms = timeout_ms; + if (local_timeout_ms > DEFAULT_TIMEOUT_MS) + local_timeout_ms = DEFAULT_TIMEOUT_MS; + } + + jiffies = msecs_to_jiffies(local_timeout_ms); + if (wait_for_completion_timeout(&l_ctx.idle_complete, jiffies)) + break; + + if (!infinite_timeout) + timeout_ms -= local_timeout_ms; + } while (timeout_ms); + + /* TEE is getting back to work */ + mc_clock_enable(); + return timeout_ms == 0; +} + +/* + * This thread, and only this thread, schedules the SWd. Hence, reading the idle + * status and its associated timeout is safe from race conditions. + */ +static int tee_scheduler(void *arg) +{ + bool swd_notify = false; + int ret = 0; + + /* Enable TEE clock */ + mc_clock_enable(); + + /* Logging */ + if (l_ctx.log_buffer_size) { + ret = fc_trace_init(l_ctx.log_buffer, l_ctx.log_buffer_size); + if (!ret) { + logging_run(); + l_ctx.log_buffer_busy = true; + mc_dev_info("registered log buffer of size %d", + l_ctx.log_buffer_size); + } else { + mc_dev_err(ret, "failed to register log buffer"); + /* Ignore error */ + ret = 0; + } + } else { + mc_dev_info("no log buffer to register"); + } + + /* Bootup */ + l_ctx.boot_ret = nq_boot_tee(); + complete(&l_ctx.boot_complete); + if (l_ctx.boot_ret) { + mc_clock_disable(); + return l_ctx.boot_ret; + } + + /* Run */ + while (1) { + s32 timeout_ms = -1; + bool pm_request = false; + u8 tee_flags; + + if (l_ctx.suspended || nq_get_idle_timeout(&timeout_ms)) { + /* If timeout is 0 we keep scheduling the SWd */ + if (!timeout_ms) + nq_scheduler_command(NSIQ); + else if (tee_sleep(timeout_ms)) + /* Timed out, force SWd schedule */ + nq_scheduler_command(NSIQ); + } + + /* + * Potential exit causes: + * 1) nq_stop is called: just stop the thread (no crash dump) + * 2) nq_signal_tee_hung: breaks the loop and handle the hang as + * a crash + * 3) The thread detects a TEE crash and breaks the loop + */ + if (!l_ctx.tee_scheduler_run) + break; + + /* Get requested command if any */ + mutex_lock(&l_ctx.request_mutex); + switch (l_ctx.request) { + case NONE: + break; + case YIELD: + swd_notify = false; + break; + case NSIQ: + swd_notify = true; + break; + } + + l_ctx.request = NONE; + nq_update_time(); + mutex_unlock(&l_ctx.request_mutex); + + /* Reset timeout so we don't loop if SWd halted */ + mutex_lock(&l_ctx.buffer_mutex); + l_ctx.mcp_buffer->flags.timeout_ms = -1; + mutex_unlock(&l_ctx.buffer_mutex); + + if (swd_notify) { + u32 session_id = 0; + u32 payload = 0; + + retrieve_last_session_payload(&session_id, &payload); + swd_notify = false; + + /* Call SWd scheduler */ + fc_nsiq(session_id, payload); + } else { + /* Resume SWd from where it was */ + fc_yield(0); + } + + /* Always flush log buffer after the SWd has run */ + logging_run(); + + /* Check crash */ + mutex_lock(&l_ctx.buffer_mutex); + tee_flags = l_ctx.mcp_buffer->flags.tee_flags; + mutex_unlock(&l_ctx.buffer_mutex); + if (tee_flags & MC_STATE_FLAG_TEE_HALT_MASK) { + ret = -EHOSTUNREACH; + mc_dev_err(ret, "TEE halted, exiting"); + break; + } + + /* Should have suspended by now if requested */ + mutex_lock(&l_ctx.request_mutex); + if (pm_request) { + l_ctx.suspended = nq_suspended(); + complete(&l_ctx.sleep_complete); + } + + mutex_unlock(&l_ctx.request_mutex); + + /* Flush pending notifications if possible */ + mutex_lock(&l_ctx.notifications_mutex); + if (nq_notifications_flush()) + complete(&l_ctx.idle_complete); + + mutex_unlock(&l_ctx.notifications_mutex); + } + + mc_dev_devel("loop exit, ret is %d", ret); + if (ret || l_ctx.tee_hung) { + /* There is an error, the tee must have crashed */ + nq_handle_tee_crash(); + } + + /* Logging */ + ret = fc_trace_deinit(); + if (!ret) + l_ctx.log_buffer_busy = false; + else + mc_dev_err(ret, "failed to unregister log buffer"); + + mc_clock_disable(); + return ret; +} + +int nq_cpu_off(unsigned int cpu) +{ + if ((PLAT_DEFAULT_TEE_AFFINITY_MASK >> cpu) & 0x1) + remove_core_from_mask(cpu); + return nq_scheduler_command(NSIQ); +} + +int nq_cpu_on(unsigned int cpu) +{ + if ((PLAT_DEFAULT_TEE_AFFINITY_MASK >> cpu) & 0x1) + add_core_to_mask(cpu); + return 0; +} + +int nq_suspend(void) +{ + mc_dev_devel("%s called", __func__); + return nq_cpu_off(get_cpu()); +} + +int nq_resume(void) +{ + mc_dev_devel("%s called", __func__); + return nq_cpu_on(get_cpu()); +} + +int nq_start(void) +{ + int ret; + /* Make sure we have the interrupt before going on */ +#if defined(CONFIG_OF) + l_ctx.irq = irq_of_parse_and_map(g_ctx.mcd->of_node, 0); +#endif +#if defined(MC_INTR_SSIQ) + if (l_ctx.irq <= 0) + l_ctx.irq = MC_INTR_SSIQ; +#endif + + if (l_ctx.irq <= 0) { + ret = -EINVAL; + mc_dev_err(ret, "No IRQ number, aborting"); + return ret; + } + + ret = request_irq(l_ctx.irq, irq_handler, IRQF_TRIGGER_RISING, + "trustonic", NULL); + if (ret) + return ret; + + /* + * Initialize the time structure for SWd + * At this stage, we don't know if the SWd needs to get the REE time and + * we set it anyway. + */ + nq_update_time(); + + /* Setup S-SIQ interrupt handler and its bottom-half */ + l_ctx.irq_bh_thread_run = true; + l_ctx.irq_bh_thread = kthread_run(irq_bh_worker, NULL, "tee_irq_bh"); + if (IS_ERR(l_ctx.irq_bh_thread)) { + ret = PTR_ERR(l_ctx.irq_bh_thread); + mc_dev_err(ret, "irq_bh_worker thread creation failed"); + return ret; + } + + /* Scheduler */ + l_ctx.tee_scheduler_run = true; + l_ctx.tee_scheduler_thread = kthread_create(tee_scheduler, NULL, + "tee_scheduler"); + if (IS_ERR(l_ctx.tee_scheduler_thread)) { + ret = PTR_ERR(l_ctx.tee_scheduler_thread); + mc_dev_err(ret, "tee_scheduler thread creation failed"); + return ret; + } +#if defined(BIG_CORE_SWITCH_AFFINITY_MASK) + set_tee_worker_threads_on_big_core(false); +#endif + wake_up_process(l_ctx.tee_scheduler_thread); + + wait_for_completion(&l_ctx.boot_complete); + if (l_ctx.boot_ret) + return l_ctx.boot_ret; + + complete(&l_ctx.idle_complete); + return 0; +} + +void nq_stop(void) +{ + /* Scheduler */ + l_ctx.tee_scheduler_run = false; + complete(&l_ctx.idle_complete); + kthread_stop(l_ctx.tee_scheduler_thread); + + /* NQ */ + l_ctx.irq_bh_thread_run = false; + complete(&l_ctx.irq_bh_complete); + kthread_stop(l_ctx.irq_bh_thread); + free_irq(l_ctx.irq, NULL); +} + +void add_core_to_mask(unsigned int cpu_id) +{ + struct cpumask new_mask; + + mutex_lock(&l_ctx.cpumask_mutex); + new_mask = l_ctx.current_cpumask; + mc_dev_devel("set cpu %u", cpu_id); + cpumask_set_cpu(cpu_id, &new_mask); + if (l_ctx.tee_scheduler_thread) + set_cpus_allowed_ptr(l_ctx.tee_scheduler_thread, &new_mask); + l_ctx.current_cpumask = new_mask; + mutex_unlock(&l_ctx.cpumask_mutex); +} + +void remove_core_from_mask(unsigned int cpu_id) +{ + struct cpumask new_mask; + + mutex_lock(&l_ctx.cpumask_mutex); + new_mask = l_ctx.current_cpumask; + mc_dev_devel("remove cpu %u", cpu_id); + cpumask_clear_cpu(cpu_id, &new_mask); + if (l_ctx.tee_scheduler_thread) + set_cpus_allowed_ptr(l_ctx.tee_scheduler_thread, &new_mask); + l_ctx.current_cpumask = new_mask; + mutex_unlock(&l_ctx.cpumask_mutex); +} + +#if defined(BIG_CORE_SWITCH_AFFINITY_MASK) +void set_tee_worker_threads_on_big_core(bool big_core) +{ + struct cpumask new_mask; + unsigned int i; + + mc_dev_devel("%s ", big_core ? + "big_affinity" : "default_affinity"); + + mc_dev_devel("nr_cpu_ids %d", nr_cpu_ids); + cpumask_clear(&new_mask); + if (big_core) { + for (i = 0; i < nr_cpu_ids; i++) { + if ((BIG_CORE_SWITCH_AFFINITY_MASK >> i) & 0x1) { + mc_dev_devel("set cpu %u", i); + cpumask_set_cpu(i, &new_mask); + } + } + } else { + for (i = 0; i < nr_cpu_ids; i++) { + if ((PLAT_DEFAULT_TEE_AFFINITY_MASK >> i) & 0x1) { + mc_dev_devel("set cpu %u", i); + cpumask_set_cpu(i, &new_mask); + } + } + } + set_cpus_allowed_ptr(l_ctx.tee_scheduler_thread, &new_mask); +} +#endif + +int nq_init(void) +{ + size_t q_len, mci_len; + unsigned long mci; + int ret; + unsigned int i; + + ret = mc_clock_init(); + if (ret) + goto err_clock; + + ret = logging_init(&l_ctx.log_buffer, &l_ctx.log_buffer_size); + if (ret) + goto err_logging; + + /* Setup crash handler function list */ + BLOCKING_INIT_NOTIFIER_HEAD(&l_ctx.tee_stop_notifiers); + + mutex_init(&l_ctx.buffer_mutex); + init_completion(&l_ctx.irq_bh_complete); + /* Setup notification queue mutex */ + mutex_init(&l_ctx.notifications_mutex); + INIT_LIST_HEAD(&l_ctx.notifications); + + /* NQ_NUM_ELEMS must be power of 2 */ + q_len = ALIGN(2 * (sizeof(struct notification_queue_header) + + NQ_NUM_ELEMS * sizeof(struct notification)), 4); + + mci_len = q_len + + sizeof(*l_ctx.time) + + sizeof(*l_ctx.mcp_buffer) + + MAX_IW_SESSION * sizeof(struct interworld_session); + + l_ctx.order = get_order(mci_len); + + mci = __get_free_pages(GFP_USER | __GFP_ZERO, l_ctx.order); + if (!mci) + goto err_mci; + + l_ctx.nq.tx = (struct notification_queue *)mci; + l_ctx.nq.tx->hdr.queue_size = NQ_NUM_ELEMS; + mci += sizeof(struct notification_queue_header) + + l_ctx.nq.tx->hdr.queue_size * sizeof(struct notification); + + l_ctx.nq.rx = (struct notification_queue *)mci; + l_ctx.nq.rx->hdr.queue_size = NQ_NUM_ELEMS; + mci += sizeof(struct notification_queue_header) + + l_ctx.nq.rx->hdr.queue_size * sizeof(struct notification); + + l_ctx.mcp_buffer = (void *)ALIGN(mci, 8); + mci += sizeof(struct mcp_buffer); + + /* interworld_buffer contains: + * MAX_IW_SESSION session, and for each session S(i), we could have + * D(i) extra data, NB: D(i) could be different from D(j) + * + * v0: D(i) = 0 + */ + /* mci should be already 8 bytes aligned */ + l_ctx.iwp_buffer = (void *)ALIGN(mci, 8); + mci += MAX_IW_SESSION * sizeof(struct interworld_session); + + l_ctx.time = (void *)ALIGN(mci, 8); + + /* Scheduler */ + init_completion(&l_ctx.boot_complete); + init_completion(&l_ctx.idle_complete); + mutex_init(&l_ctx.request_mutex); + mutex_init(&l_ctx.cpumask_mutex); + mutex_lock(&l_ctx.cpumask_mutex); + cpumask_clear(&l_ctx.current_cpumask); + for (i = 0; i < nr_cpu_ids; i++) { + if ((PLAT_DEFAULT_TEE_AFFINITY_MASK >> i) & 0x1) { + mc_dev_devel("set cpu %u", i); + cpumask_set_cpu(i, &l_ctx.current_cpumask); + } + } + mutex_unlock(&l_ctx.cpumask_mutex); + return 0; + +err_mci: + logging_exit(l_ctx.log_buffer_busy); +err_logging: + mc_clock_exit(); +err_clock: + return ret; +} + +void nq_exit(void) +{ + if (l_ctx.dump.off) + kfree(l_ctx.dump.buf); + + free_pages((unsigned long)l_ctx.mci, l_ctx.order); + logging_exit(l_ctx.log_buffer_busy); + mc_clock_exit(); +} diff --git a/drivers/gud/MobiCoreDriver/nq.h b/drivers/gud/MobiCoreDriver/nq.h new file mode 100644 index 000000000000..b60308bfa126 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/nq.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2020 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef _MC_NQ_H_ +#define _MC_NQ_H_ + +#include +#include +#include + +/** Max number of interworld session allocated in MCI buffer */ +#define MAX_IW_SESSION 256 + +enum nq_notif_state { + NQ_NOTIF_IDLE, /* Nothing happened yet */ + NQ_NOTIF_QUEUED, /* Notification in overflow queue */ + NQ_NOTIF_SENT, /* Notification in send queue */ + NQ_NOTIF_RECEIVED, /* Notification received */ + NQ_NOTIF_CONSUMED, /* Notification reported to CA */ + NQ_NOTIF_DEAD, /* Error reported to CA */ +}; + +/* FIXME to be renamed */ +struct nq_session { + /* Notification id */ + u32 id; + /* Notification payload */ + u32 payload; + /* Notifications list */ + struct list_head list; + /* Notification debug mutex */ + struct mutex mutex; + /* Current notification/session state */ + enum nq_notif_state state; + /* Time at notification state change */ + u64 cpu_clk; + /* This TA is of Global Platform type, set by upper layer */ + int is_gp; +}; + +/* Notification queue channel */ +void nq_session_init(struct nq_session *session, bool is_gp); +void nq_session_exit(struct nq_session *session); +void nq_session_state_update(struct nq_session *session, + enum nq_notif_state state); +int nq_session_notify(struct nq_session *session, u32 id, u32 payload); +const char *nq_session_state(const struct nq_session *session, u64 *cpu_clk); + +/* Services */ +union mcp_message *nq_get_mcp_buffer(void); +struct interworld_session *nq_get_iwp_buffer(void); +void nq_set_version_ptr(char *version); +void nq_register_notif_handler(void (*handler)(u32 id, u32 payload), bool iwp); +int nq_register_tee_stop_notifier(struct notifier_block *nb); +int nq_unregister_tee_stop_notifier(struct notifier_block *nb); +ssize_t nq_get_stop_message(char __user *buffer, size_t size); +void nq_signal_tee_hung(void); + +/* SWd suspend/resume */ +void add_core_to_mask(unsigned int cpu); +void remove_core_from_mask(unsigned int cpu); +int nq_cpu_on(unsigned int cpu); +int nq_cpu_off(unsigned int cpu); +int nq_suspend(void); +int nq_resume(void); + +/* Start/stop TEE */ +int nq_start(void); +void nq_stop(void); + +/* Initialisation/cleanup */ +int nq_init(void); +void nq_exit(void); + +#endif /* _MC_NQ_H_ */ diff --git a/drivers/gud/MobiCoreDriver/platform.h b/drivers/gud/MobiCoreDriver/platform.h new file mode 100644 index 000000000000..e56f37b26294 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/platform.h @@ -0,0 +1,178 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2020 TRUSTONIC LIMITED + * 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 + * 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. + */ +#ifndef _MC_PLATFORM_H_ +#define _MC_PLATFORM_H_ + +#include +#include +#include +#include +#include +#include +#include + +#if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE +#define USE_SHM_BRIDGE +#endif + +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +#include +#include +#if defined USE_SHM_BRIDGE +#include +#endif +#elif KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE +#include +#include +#if defined USE_SHM_BRIDGE +#include +#endif +#endif + +/*--------------- Implementation -------------- */ +/* MobiCore Interrupt for Qualcomm (DT IRQ has priority if present) */ +#define MC_INTR_SSIQ 280 + +/* Use SMC for fastcalls */ +#define MC_SMC_FASTCALL + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(5, 4, 0) +#define SCM_MOBIOS_FNID(s, c) (((((s) & 0xFF) << 8) | ((c) & 0xFF)) \ + | 0x33000000) + +#define TZ_EXECUTIVE_EXT_ID_PARAM_ID \ + TZ_SYSCALL_CREATE_PARAM_ID_4( \ + TZ_SYSCALL_PARAM_TYPE_BUF_RW, \ + TZ_SYSCALL_PARAM_TYPE_VAL, \ + TZ_SYSCALL_PARAM_TYPE_BUF_RW, \ + TZ_SYSCALL_PARAM_TYPE_VAL) +#endif + +/* from following file */ +#define SCM_SVC_MOBICORE 250 +#define SCM_CMD_MOBICORE 1 + +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +extern int trustonic_smc_fastcall(void *fc_generic, size_t size); +static inline int smc_fastcall(void *fc_generic, size_t size) +{ + return trustonic_smc_fastcall(fc_generic, size); +} + +#elif KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE +static inline int smc_fastcall(void *fc_generic, size_t size) +{ +#if !defined(USE_SHM_BRIDGE) + if (is_scm_armv8()) { +#endif + struct scm_desc desc = {0}; + int ret; + static void *scm_buf; + static phys_addr_t scm_buf_pa; + + if (!scm_buf) { +#if defined USE_SHM_BRIDGE + static struct qtee_shm scm_shm = {0}; + + ret = qtee_shmbridge_allocate_shm(PAGE_ALIGN(size), + &scm_shm); + scm_buf = scm_shm.vaddr; + scm_buf_pa = scm_shm.paddr; +#else + scm_buf = kzalloc(PAGE_ALIGN(size), GFP_KERNEL); + scm_buf_pa = virt_to_phys(scm_buf); +#endif + } + if (!scm_buf) + return -ENOMEM; + memcpy(scm_buf, fc_generic, size); + dmac_flush_range(scm_buf, scm_buf + size); + + desc.arginfo = TZ_EXECUTIVE_EXT_ID_PARAM_ID; + desc.args[0] = scm_buf_pa; + desc.args[1] = (u32)size; + desc.args[2] = scm_buf_pa; + desc.args[3] = (u32)size; + + ret = scm_call2( + SCM_MOBIOS_FNID(SCM_SVC_MOBICORE, SCM_CMD_MOBICORE), + &desc); + + dmac_flush_range(scm_buf, scm_buf + size); + + memcpy(fc_generic, scm_buf, size); + return ret; +#if !defined(USE_SHM_BRIDGE) + } + return scm_call(SCM_SVC_MOBICORE, SCM_CMD_MOBICORE, + fc_generic, size, + fc_generic, size); +#endif +} +#endif + +/* + * Do not start the TEE at driver init + */ +#define MC_DELAYED_TEE_START + +/* + * Perform crypto clock enable/disable + * of clocks + * "bus_clk" + * "core_clk" + * "iface_clk" + */ +#ifndef RSU_INTERNAL_CLOCK +#define MC_CRYPTO_CLOCK_MANAGEMENT +#endif +/* + * This should be defined only on platforms without ICE clock e.g SDM845, SM6150, SM7225 + * #define TT_CRYPTO_NO_CLOCK_SUPPORT_FEATURE "qcom,no-clock-support" + */ +#define MC_CRYPTO_CLOCK_CORESRC_PROPNAME "qcom,ce-opp-freq" + +#if defined USE_SHM_BRIDGE +#define MC_CLOCK_CORESRC_DEFAULTRATE 192000000 +#else +#define MC_CLOCK_CORESRC_DEFAULTRATE 100000000 +#endif + +/* + * Perform clock enable/disable for clock "core_clk_src" + */ +#define MC_DEVICE_PROPNAME "qcom,mcd" +/* + * Platform Node + */ +#define TT_CLOCK_DEVICE_NAME "qcom,qseecom" + +/* All TZBSPv4 targets are using AARCH32_FC flag */ +#define MC_AARCH32_FC + +/* This should be defined only on some platforms e.g SM8150, SM6150, SM7225 + * from which the Gold cores do not support TEE interfaces + * so that CPU_IDS should list only Silver cores. + */ +/* Enforce/restrict statically CPUs potentially running TEE + * (Customize to match platform CPU layout... 0xF0 for big cores only for ex). + * If not defined TEE dynamically using all platform CPUs (recommended) + * Warning: Both PLAT_DEFAULT_TEE_AFFINITY_MASK and + * BIG_CORE_SWITCH_AFFINITY_MASK have to be defined + */ +#define PLAT_DEFAULT_TEE_AFFINITY_MASK (0xF) +#define BIG_CORE_SWITCH_AFFINITY_MASK (0xF) + +#endif /* _MC_PLATFORM_H_ */ diff --git a/drivers/gud/MobiCoreDriver/session.c b/drivers/gud/MobiCoreDriver/session.c new file mode 100644 index 000000000000..672efbdba063 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/session.c @@ -0,0 +1,1013 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2020 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* sockfd_lookup */ +#include +#if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE +#include /* local_clock */ +#include /* put_task_struct */ +#endif + +#include "mc_user.h" +#include "mc_admin.h" + +#if KERNEL_VERSION(3, 5, 0) <= LINUX_VERSION_CODE +#include +#else +#define kuid_t uid_t +#define kgid_t gid_t +#define KGIDT_INIT(value) ((kgid_t)value) + +static inline uid_t __kuid_val(kuid_t uid) +{ + return uid; +} + +static inline gid_t __kgid_val(kgid_t gid) +{ + return gid; +} + +static inline bool gid_eq(kgid_t left, kgid_t right) +{ + return __kgid_val(left) == __kgid_val(right); +} + +static inline bool gid_gt(kgid_t left, kgid_t right) +{ + return __kgid_val(left) > __kgid_val(right); +} + +static inline bool gid_lt(kgid_t left, kgid_t right) +{ + return __kgid_val(left) < __kgid_val(right); +} +#endif +#include "main.h" +#include "mmu.h" /* tee_mmu_buffer, tee_mmu_debug_structs */ +#include "iwp.h" +#include "mcp.h" +#include "client.h" /* *cbuf* */ +#include "session.h" +#include "mcimcp.h" /* WSM_INVALID */ + +#define SHA1_HASH_SIZE 20 + +static int wsm_create(struct tee_session *session, struct tee_wsm *wsm, + const struct mc_ioctl_buffer *buf) +{ + if (wsm->in_use) { + mc_dev_err(-EINVAL, "wsm already in use"); + return -EINVAL; + } + + if (buf->len > BUFFER_LENGTH_MAX) { + mc_dev_err(-EINVAL, "buffer size %u too big", buf->len); + return -EINVAL; + } + + wsm->mmu = client_mmu_create(session->client, buf, &wsm->cbuf); + if (IS_ERR(wsm->mmu)) + return PTR_ERR(wsm->mmu); + + /* Increment debug counter */ + atomic_inc(&g_ctx.c_wsms); + wsm->va = buf->va; + wsm->len = buf->len; + wsm->flags = buf->flags; + wsm->in_use = true; + return 0; +} + +static int wsm_wrap(struct tee_session *session, struct tee_wsm *wsm, + struct tee_mmu *mmu) +{ + struct mcp_buffer_map map; + + if (wsm->in_use) { + mc_dev_err(-EINVAL, "wsm already in use"); + return -EINVAL; + } + + wsm->mmu = mmu; + tee_mmu_get(wsm->mmu); + + /* Increment debug counter */ + atomic_inc(&g_ctx.c_wsms); + tee_mmu_buffer(wsm->mmu, &map); + wsm->va = 0; + wsm->len = map.length; + wsm->flags = map.flags; + wsm->in_use = true; + return 0; +} + +/* + * Free a WSM object, must be called under the session's wsms_lock + */ +static void wsm_free(struct tee_session *session, struct tee_wsm *wsm) +{ + if (!wsm->in_use) { + mc_dev_err(-EINVAL, "wsm not in use"); + return; + } + + mc_dev_devel("free wsm %p: mmu %p cbuf %p va %lx len %u sva %x", + wsm, wsm->mmu, wsm->cbuf, wsm->va, wsm->len, wsm->sva); + /* Free MMU table */ + tee_mmu_put(wsm->mmu); + if (wsm->cbuf) + tee_cbuf_put(wsm->cbuf); + + /* Decrement debug counter */ + atomic_dec(&g_ctx.c_wsms); + wsm->in_use = false; +} + +#if KERNEL_VERSION(4, 6, 0) <= LINUX_VERSION_CODE +static int hash_path_and_data(struct task_struct *task, u8 *hash, + const void *data, unsigned int data_len) +{ + struct file *exe_file; + struct crypto_shash *tfm; + struct shash_desc *desc; + size_t desc_size; + char *buf; + char *path; + unsigned int path_len; + int ret = 0; + + buf = (char *)__get_free_page(GFP_KERNEL); + if (!buf) + return -ENOMEM; + + exe_file = get_task_exe_file(task); + if (!exe_file) { + ret = -ENOENT; + goto end; + } + + path = d_path(&exe_file->f_path, buf, PAGE_SIZE); + if (IS_ERR(path)) { + ret = PTR_ERR(path); + goto end; + } + + mc_dev_devel("process path ="); + { + char *c; + + for (c = path; *c; c++) + mc_dev_devel("%c %d", *c, *c); + } + + path_len = (unsigned int)strnlen(path, PAGE_SIZE); + mc_dev_devel("path_len = %u", path_len); + /* Compute hash of path */ + tfm = crypto_alloc_shash("sha1", 0, 0); + if (IS_ERR(tfm)) { + ret = PTR_ERR(tfm); + mc_dev_err(ret, "cannot allocate shash"); + goto end; + } + + desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); + desc = kzalloc(desc_size, GFP_KERNEL); + if (!desc) { + ret = -ENOMEM; + goto err_desc; + } + + desc->tfm = tfm; + crypto_shash_init(desc); + crypto_shash_update(desc, (u8 *)path, path_len); + if (data) { + mc_dev_devel("hashing additional data"); + crypto_shash_update(desc, data, data_len); + } + + crypto_shash_final(desc, hash); + shash_desc_zero(desc); + kfree(desc); +err_desc: + crypto_free_shash(tfm); +end: + free_page((unsigned long)buf); + + return ret; +} +#else +static int hash_path_and_data(struct task_struct *task, u8 *hash, + const void *data, unsigned int data_len) +{ + struct mm_struct *mm = task->mm; + struct hash_desc desc; + struct scatterlist sg; + char *buf; + char *path; + unsigned int path_len; + int ret = 0; + + buf = (char *)__get_free_page(GFP_KERNEL); + if (!buf) + return -ENOMEM; + + down_read(&mm->mmap_sem); + if (!mm->exe_file) { + ret = -ENOENT; + goto end; + } + + path = d_path(&mm->exe_file->f_path, buf, PAGE_SIZE); + if (IS_ERR(path)) { + ret = PTR_ERR(path); + goto end; + } + + mc_dev_devel("process path ="); + { + char *c; + + for (c = path; *c; c++) + mc_dev_devel("%c %d", *c, *c); + } + + path_len = (unsigned int)strnlen(path, PAGE_SIZE); + mc_dev_devel("path_len = %u", path_len); + /* Compute hash of path */ + desc.tfm = crypto_alloc_hash("sha1", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(desc.tfm)) { + ret = PTR_ERR(desc.tfm); + mc_dev_devel("could not alloc hash = %d", ret); + goto end; + } + + sg_init_one(&sg, path, path_len); + crypto_hash_init(&desc); + crypto_hash_update(&desc, &sg, path_len); + if (data) { + mc_dev_devel("current process path: hashing additional data"); + sg_init_one(&sg, data, data_len); + crypto_hash_update(&desc, &sg, data_len); + } + + crypto_hash_final(&desc, hash); + crypto_free_hash(desc.tfm); + +end: + up_read(&mm->mmap_sem); + free_page((unsigned long)buf); + + return ret; +} +#endif + +#if KERNEL_VERSION(4, 9, 0) <= LINUX_VERSION_CODE +#define GROUP_AT(gi, i) ((gi)->gid[i]) +#endif + +/* + * groups_search is not EXPORTed so copied from kernel/groups.c + * a simple bsearch + */ +static int has_group(const struct cred *cred, gid_t id_gid) +{ + const struct group_info *group_info = cred->group_info; + unsigned int left, right; + kgid_t gid = KGIDT_INIT(id_gid); + + if (gid_eq(gid, cred->fsgid) || gid_eq(gid, cred->egid)) + return 1; + + if (!group_info) + return 0; + + left = 0; + right = group_info->ngroups; + while (left < right) { + unsigned int mid = (left + right) / 2; + + if (gid_gt(gid, GROUP_AT(group_info, mid))) + left = mid + 1; + else if (gid_lt(gid, GROUP_AT(group_info, mid))) + right = mid; + else + return 1; + } + return 0; +} + +static int check_prepare_identity(const struct mc_identity *identity, + struct identity *mcp_identity, + struct task_struct *task) +{ + struct mc_identity *mcp_id = (struct mc_identity *)mcp_identity; + u8 hash[SHA1_HASH_SIZE] = { 0 }; + bool application = false; + bool supplied_ca_identity = false; + const void *data; + unsigned int data_len; + static const u8 zero_buffer[sizeof(identity->login_data)] = { 0 }; + + /* Copy login type */ + mcp_identity->login_type = identity->login_type; + + if (identity->login_type == LOGIN_PUBLIC || + identity->login_type == TEEC_TT_LOGIN_KERNEL) + return 0; + + /* Fill in uid field */ + if (identity->login_type == LOGIN_USER || + identity->login_type == LOGIN_USER_APPLICATION) { + /* Set euid and ruid of the process. */ + mcp_id->uid.euid = __kuid_val(task_euid(task)); + mcp_id->uid.ruid = __kuid_val(task_uid(task)); + } + + /* Check gid field */ + if (identity->login_type == LOGIN_GROUP || + identity->login_type == LOGIN_GROUP_APPLICATION) { + const struct cred *cred = __task_cred(task); + + /* + * Check if gid is one of: egid of the process, its rgid or one + * of its supplementary groups + */ + if (!has_group(cred, identity->gid)) { + mc_dev_err(-EACCES, "group %d not allowed", + identity->gid); + return -EACCES; + } + + mc_dev_devel("group %d found", identity->gid); + mcp_id->gid = identity->gid; + } + + switch (identity->login_type) { + case LOGIN_PUBLIC: + case LOGIN_GROUP: + break; + case LOGIN_USER: + data = NULL; + data_len = 0; + break; + case LOGIN_APPLICATION: + application = true; + supplied_ca_identity = true; + data = NULL; + data_len = 0; + break; + case LOGIN_USER_APPLICATION: + application = true; + supplied_ca_identity = true; + data = &mcp_id->uid; + data_len = sizeof(mcp_id->uid); + break; + case LOGIN_GROUP_APPLICATION: + application = true; + data = &identity->gid; + data_len = sizeof(identity->gid); + break; + default: + /* Any other login_type value is invalid. */ + mc_dev_err(-EINVAL, "Invalid login type %d", + identity->login_type); + return -EINVAL; + } + + /* let the supplied login_data pass through if it is LOGIN_APPLICATION + * or LOGIN_USER_APPLICATION and not a zero-filled buffer + * That buffer is expected to contain a NWd computed hash containing the + * CA identity + */ + if (supplied_ca_identity && + memcmp(identity->login_data, zero_buffer, + sizeof(identity->login_data)) != 0) { + memcpy(&mcp_id->login_data, identity->login_data, + sizeof(mcp_id->login_data)); + } else if (application) { + int ret = hash_path_and_data(task, hash, data, data_len); + + if (ret) { + mc_dev_devel("hash calculation returned %d", ret); + return ret; + } + + memcpy(&mcp_id->login_data, hash, sizeof(mcp_id->login_data)); + } + + return 0; +} + +/* + * Create a session object. + * Note: object is not attached to client yet. + */ +struct tee_session *session_create(struct tee_client *client, + const struct mc_identity *identity) +{ + struct tee_session *session; + struct identity mcp_identity; + + memset(&mcp_identity, 0, sizeof(mcp_identity)); + if (!IS_ERR_OR_NULL(identity)) { + /* Check identity method and data. */ + int ret; + + ret = check_prepare_identity(identity, &mcp_identity, current); + if (ret) + return ERR_PTR(ret); + } + + /* Allocate session object */ + session = kzalloc(sizeof(*session), GFP_KERNEL); + if (!session) + return ERR_PTR(-ENOMEM); + + /* Increment debug counter */ + atomic_inc(&g_ctx.c_sessions); + /* Initialise object members */ + if (identity) { + session->is_gp = true; + iwp_session_init(&session->iwp_session, &mcp_identity); + } else { + session->is_gp = false; + mcp_session_init(&session->mcp_session); + } + + client_get(client); + session->client = client; + kref_init(&session->kref); + INIT_LIST_HEAD(&session->list); + mutex_init(&session->wsms_lock); + mc_dev_devel("created session %p: client %p", + session, session->client); + return session; +} + +/* + * Free session object and all objects it contains (wsm). + */ +static void session_release(struct kref *kref) +{ + struct tee_session *session; + int i; + + /* Remove remaining shared buffers (unmapped in SWd by mcp_close) */ + session = container_of(kref, struct tee_session, kref); + for (i = 0; i < MC_MAP_MAX; i++) { + if (!session->wsms[i].in_use) + continue; + + mc_dev_devel("session %p: free wsm #%d", session, i); + wsm_free(session, &session->wsms[i]); + /* Buffer unmapped by SWd */ + atomic_dec(&g_ctx.c_maps); + } + + if (session->tci.in_use) { + mc_dev_devel("session %p: free tci", session); + wsm_free(session, &session->tci); + } + + if (session->is_gp) + mc_dev_devel("freed GP session %p: client %p id %x", session, + session->client, session->iwp_session.sid); + else + mc_dev_devel("freed MC session %p: client %p id %x", session, + session->client, session->mcp_session.sid); + + client_put(session->client); + kfree(session); + /* Decrement debug counter */ + atomic_dec(&g_ctx.c_sessions); +} + +/* + * Unreference session. + * Free session object if reference reaches 0. + */ +int session_put(struct tee_session *session) +{ + return kref_put(&session->kref, session_release); +} + +static int wsm_debug_structs(struct kasnprintf_buf *buf, struct tee_wsm *wsm, + int no) +{ + ssize_t ret; + + if (!wsm->in_use) + return 0; + + ret = kasnprintf(buf, "\t\t"); + if (no < 0) + ret = kasnprintf(buf, "tci %pK: cbuf %pK va %pK len %u\n", + wsm, wsm->cbuf, (void *)wsm->va, wsm->len); + else if (wsm->in_use) + ret = kasnprintf(buf, + "wsm #%d: cbuf %pK va %pK len %u sva %x\n", + no, wsm->cbuf, (void *)wsm->va, wsm->len, + wsm->sva); + + if (ret < 0) + return ret; + + if (wsm->mmu) { + ret = tee_mmu_debug_structs(buf, wsm->mmu); + if (ret < 0) + return ret; + } + + return 0; +} + +int session_mc_open_session(struct tee_session *session, + struct mcp_open_info *info) +{ + struct tee_wsm *wsm = &session->tci; + bool tci_in_use = false; + int ret; + + /* Check that tci and its length make sense */ + if (info->tci_len > MC_MAX_TCI_LEN) + return -EINVAL; + + if (!info->tci_va != !info->tci_len) { + mc_dev_devel("TCI pointer and length are inconsistent"); + return -EINVAL; + } + + /* Add existing TCI map */ + if (info->tci_mmu) { + ret = wsm_wrap(session, wsm, info->tci_mmu); + if (ret) + return ret; + + tci_in_use = true; + mc_dev_devel("wrapped tci: mmu %p len %u flags %x", + wsm->mmu, wsm->len, wsm->flags); + } + + /* Create mapping for TCI */ + if (info->tci_va) { + struct mc_ioctl_buffer buf = { + .va = info->tci_va, + .len = info->tci_len, + .flags = MC_IO_MAP_INPUT_OUTPUT, + }; + + ret = wsm_create(session, wsm, &buf); + if (ret) + return ret; + + tci_in_use = true; + info->tci_mmu = wsm->mmu; + mc_dev_devel( + "created tci: mmu %p cbuf %p va %lx len %u flags %x", + wsm->mmu, wsm->cbuf, wsm->va, wsm->len, wsm->flags); + } + + ret = mcp_open_session(&session->mcp_session, info, &tci_in_use); + if (info->tci_va && (ret || !tci_in_use)) + wsm_free(session, &session->tci); + + return ret; +} + +/* + * Close session and unreference session object. + * Session object is assumed to have been removed from main list, which means + * that session_close cannot be called anymore. + */ +int session_close(struct tee_session *session) +{ + int ret; + + if (session->is_gp) { + ret = iwp_close_session(&session->iwp_session); + if (!ret) + mc_dev_devel("closed GP session %x", + session->iwp_session.sid); + } else { + ret = mcp_close_session(&session->mcp_session); + if (!ret) + mc_dev_devel("closed MC session %x", + session->mcp_session.sid); + } + return ret; +} + +/* + * Session is to be removed from NWd records as SWd is dead + */ +int session_mc_cleanup_session(struct tee_session *session) +{ + mcp_cleanup_session(&session->mcp_session); + return session_put(session); +} + +/* + * Send a notification to TA + */ +int session_mc_notify(struct tee_session *session) +{ + if (!session) { + mc_dev_devel("Session pointer is null"); + return -EINVAL; + } + + return mcp_notify(&session->mcp_session); +} + +/* + * Sleep until next notification from SWd. + */ +int session_mc_wait(struct tee_session *session, s32 timeout, + int silent_expiry) +{ + return mcp_wait(&session->mcp_session, timeout, silent_expiry); +} + +/* + * Share buffers with SWd and add corresponding WSM objects to session. + * This may involve some re-use or cleanup of inactive mappings. + */ +int session_mc_map(struct tee_session *session, struct tee_mmu *mmu, + struct mc_ioctl_buffer *buf) +{ + struct tee_wsm *wsm; + u32 sva; + int i, ret; + + mutex_lock(&session->wsms_lock); + /* Look for an available slot in the session WSMs array */ + for (i = 0; i < MC_MAP_MAX; i++) + if (!session->wsms[i].in_use) + break; + + if (i == MC_MAP_MAX) { + ret = -EPERM; + mc_dev_devel("no available WSM slot in session %x", + session->mcp_session.sid); + goto out; + } + + wsm = &session->wsms[i]; + if (!mmu) + ret = wsm_create(session, wsm, buf); + else + ret = wsm_wrap(session, wsm, mmu); + + if (ret) { + mc_dev_devel("maps[%d] va=%llx create failed: %d", + i, buf->va, ret); + goto out; + } + + mc_dev_devel("created wsm #%d: mmu %p cbuf %p va %lx len %u flags %x", + i, wsm->mmu, wsm->cbuf, wsm->va, wsm->len, wsm->flags); + ret = mcp_map(session->mcp_session.sid, wsm->mmu, &sva); + if (ret) { + wsm_free(session, wsm); + } else { + buf->sva = sva; + wsm->sva = sva; + } + +out: + mutex_unlock(&session->wsms_lock); + mc_dev_devel("ret=%d", ret); + return ret; +} + +/* + * In theory, stop sharing buffers with the SWd. In fact, mark them inactive. + */ +int session_mc_unmap(struct tee_session *session, + const struct mc_ioctl_buffer *buf) +{ + struct tee_wsm *wsm; + struct mcp_buffer_map map; + int i, ret = -EINVAL; + + mutex_lock(&session->wsms_lock); + /* Look for buffer in the session WSMs array */ + for (i = 0; i < MC_MAP_MAX; i++) + if (session->wsms[i].in_use && + buf->va == session->wsms[i].va && + buf->len == session->wsms[i].len && + buf->sva == session->wsms[i].sva) + break; + + if (i == MC_MAP_MAX) { + ret = -EINVAL; + mc_dev_devel("maps[%d] va=%llx sva=%llx not found", + i, buf[i].va, buf[i].sva); + goto out; + } + + wsm = &session->wsms[i]; + tee_mmu_buffer(wsm->mmu, &map); + map.secure_va = wsm->sva; + + ret = mcp_unmap(session->mcp_session.sid, &map); + if (!ret) + wsm_free(session, wsm); + +out: + mutex_unlock(&session->wsms_lock); + return ret; +} + +/* + * Read and clear last notification received from TA + */ +int session_mc_get_err(struct tee_session *session, s32 *err) +{ + return mcp_get_err(&session->mcp_session, err); +} + +static void unmap_gp_bufs(struct tee_session *session, + struct iwp_buffer_map *maps) +{ + int i; + + /* Create WSMs from bufs */ + mutex_lock(&session->wsms_lock); + for (i = 0; i < MC_MAP_MAX; i++) { + if (session->wsms[i].in_use) + wsm_free(session, &session->wsms[i]); + + if (maps[i].sva) + client_put_cwsm_sva(session->client, maps[i].sva); + } + mutex_unlock(&session->wsms_lock); +} + +static int map_gp_bufs(struct tee_session *session, + const struct mc_ioctl_buffer *bufs, + struct gp_shared_memory **parents, + struct iwp_buffer_map *maps) +{ + int i, ret = 0; + + /* Create WSMs from bufs */ + mutex_lock(&session->wsms_lock); + for (i = 0; i < MC_MAP_MAX; i++) { + /* Reset reference for temporary memory */ + maps[i].map.addr = 0; + /* Reset reference for registered memory */ + maps[i].sva = 0; + if (bufs[i].va) { + /* Temporary memory, needs mapping */ + ret = wsm_create(session, &session->wsms[i], &bufs[i]); + if (ret) { + mc_dev_devel( + "maps[%d] va=%llx create failed: %d", + i, bufs[i].va, ret); + break; + } + + tee_mmu_buffer(session->wsms[i].mmu, &maps[i].map); + } else if (parents[i]) { + /* Registered memory, already mapped */ + maps[i].sva = client_get_cwsm_sva(session->client, + parents[i]); + if (!maps[i].sva) { + ret = -EINVAL; + mc_dev_devel("couldn't find shared mem"); + break; + } + + mc_dev_devel("param[%d] has sva %x", i, maps[i].sva); + } + } + mutex_unlock(&session->wsms_lock); + + /* Failed above */ + if (i < MC_MAP_MAX) + unmap_gp_bufs(session, maps); + + return ret; +} + +int session_gp_open_session(struct tee_session *session, + const struct mc_uuid_t *uuid, + struct gp_operation *operation, + struct gp_return *gp_ret) +{ + /* TEEC_MEMREF_TEMP_* buffers to map */ + struct mc_ioctl_buffer bufs[MC_MAP_MAX]; + struct iwp_buffer_map maps[MC_MAP_MAX]; + struct gp_shared_memory *parents[MC_MAP_MAX] = { NULL }; + struct client_gp_operation client_operation; + int ret = 0; + + ret = iwp_open_session_prepare(&session->iwp_session, operation, bufs, + parents, gp_ret); + if (ret) + return ret; + + /* Create WSMs from bufs */ + ret = map_gp_bufs(session, bufs, parents, maps); + if (ret) { + iwp_open_session_abort(&session->iwp_session); + return iwp_set_ret(ret, gp_ret); + } + + /* Tell client about operation */ + client_operation.started = operation->started; + client_operation.slot = iwp_session_slot(&session->iwp_session); + client_operation.cancelled = false; + if (!client_gp_operation_add(session->client, &client_operation)) { + iwp_open_session_abort(&session->iwp_session); + return iwp_set_ret(-ECANCELED, gp_ret); + } + + /* Open/call TA */ + ret = iwp_open_session(&session->iwp_session, uuid, operation, maps, + NULL, NULL, gp_ret); + /* Cleanup */ + client_gp_operation_remove(session->client, &client_operation); + unmap_gp_bufs(session, maps); + return ret; +} + +int session_gp_open_session_domu(struct tee_session *session, + const struct mc_uuid_t *uuid, u64 started, + struct interworld_session *iws, + struct tee_mmu **mmus, + struct gp_return *gp_ret) +{ + /* TEEC_MEMREF_TEMP_* buffers to map */ + struct client_gp_operation client_operation; + int ret = 0; + + ret = iwp_open_session_prepare(&session->iwp_session, NULL, NULL, NULL, + gp_ret); + if (ret) + return ret; + + /* Tell client about operation */ + client_operation.started = started; + client_operation.slot = iwp_session_slot(&session->iwp_session); + client_operation.cancelled = false; + if (!client_gp_operation_add(session->client, &client_operation)) { + iwp_open_session_abort(&session->iwp_session); + return iwp_set_ret(-ECANCELED, gp_ret); + } + + /* Open/call TA */ + ret = iwp_open_session(&session->iwp_session, uuid, NULL, NULL, iws, + mmus, gp_ret); + /* Cleanup */ + client_gp_operation_remove(session->client, &client_operation); + return ret; +} + +int session_gp_invoke_command(struct tee_session *session, u32 command_id, + struct gp_operation *operation, + struct gp_return *gp_ret) +{ + /* TEEC_MEMREF_TEMP_* buffers to map */ + struct mc_ioctl_buffer bufs[4]; + struct iwp_buffer_map maps[MC_MAP_MAX]; + struct gp_shared_memory *parents[MC_MAP_MAX] = { NULL }; + struct client_gp_operation client_operation; + int ret = 0; + + ret = iwp_invoke_command_prepare(&session->iwp_session, command_id, + operation, bufs, parents, gp_ret); + if (ret) + return ret; + + /* Create WSMs from bufs */ + ret = map_gp_bufs(session, bufs, parents, maps); + if (ret) { + iwp_invoke_command_abort(&session->iwp_session); + return iwp_set_ret(ret, gp_ret); + } + + /* Tell client about operation */ + client_operation.started = operation->started; + client_operation.slot = iwp_session_slot(&session->iwp_session); + client_operation.cancelled = false; + if (!client_gp_operation_add(session->client, &client_operation)) { + iwp_invoke_command_abort(&session->iwp_session); + return iwp_set_ret(-ECANCELED, gp_ret); + } + + /* Call TA */ + ret = iwp_invoke_command(&session->iwp_session, operation, maps, NULL, + NULL, gp_ret); + /* Cleanup */ + client_gp_operation_remove(session->client, &client_operation); + unmap_gp_bufs(session, maps); + return ret; +} + +int session_gp_invoke_command_domu(struct tee_session *session, + u64 started, struct interworld_session *iws, + struct tee_mmu **mmus, + struct gp_return *gp_ret) +{ + struct client_gp_operation client_operation; + int ret = 0; + + ret = iwp_invoke_command_prepare(&session->iwp_session, 0, NULL, NULL, + NULL, gp_ret); + if (ret) + return ret; + + /* Tell client about operation */ + client_operation.started = started; + client_operation.slot = iwp_session_slot(&session->iwp_session); + client_operation.cancelled = false; + if (!client_gp_operation_add(session->client, &client_operation)) { + iwp_invoke_command_abort(&session->iwp_session); + return iwp_set_ret(-ECANCELED, gp_ret); + } + + /* Call TA */ + ret = iwp_invoke_command(&session->iwp_session, NULL, NULL, iws, mmus, + gp_ret); + /* Cleanup */ + client_gp_operation_remove(session->client, &client_operation); + return ret; +} + +int session_gp_request_cancellation(u64 slot) +{ + return iwp_request_cancellation(slot); +} + +int session_debug_structs(struct kasnprintf_buf *buf, + struct tee_session *session, bool is_closing) +{ + const char *type; + u32 session_id; + s32 err = 0; + int i, ret; + + if (session->is_gp) { + session_id = session->iwp_session.sid; + err = 0; + type = "GP"; + } else { + session_id = session->mcp_session.sid; + session_mc_get_err(session, &err); + type = "MC"; + } + + ret = kasnprintf(buf, "\tsession %pK [%d]: %4x %s ec %d%s\n", + session, kref_read(&session->kref), session_id, type, + err, is_closing ? " " : ""); + if (ret < 0) + return ret; + + /* TCI */ + if (session->tci.in_use) { + ret = wsm_debug_structs(buf, &session->tci, -1); + if (ret < 0) + return ret; + } + + /* WMSs */ + mutex_lock(&session->wsms_lock); + for (i = 0; i < MC_MAP_MAX; i++) { + ret = wsm_debug_structs(buf, &session->wsms[i], i); + if (ret < 0) + break; + } + mutex_unlock(&session->wsms_lock); + if (ret < 0) + return ret; + + return 0; +} diff --git a/drivers/gud/MobiCoreDriver/session.h b/drivers/gud/MobiCoreDriver/session.h new file mode 100644 index 000000000000..32dc69a09d54 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/session.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2020 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef _SESSION_H_ +#define _SESSION_H_ + +#include + +#include "mcp.h" +#include "iwp.h" + +struct tee_object; +struct tee_mmu; +struct mc_ioctl_buffer; + +struct tee_wsm { + /* Buffer NWd address (uva or kva, used only for lookup) */ + uintptr_t va; + /* Buffer length */ + u32 len; + /* Buffer flags */ + u32 flags; + /* Buffer SWd address */ + u32 sva; + union { + /* MMU table */ + struct tee_mmu *mmu; + /* Index of re-used buffer (temporary) */ + int index; + }; + /* Pointer to associated cbuf, if relevant */ + struct cbuf *cbuf; + /* State of this WSM */ + int in_use; +}; + +struct tee_session { + /* Session descriptor */ + union { + struct mcp_session mcp_session; + struct iwp_session iwp_session; + }; + /* Owner */ + struct tee_client *client; + /* Number of references kept to this object */ + struct kref kref; + /* WSM for the TCI */ + struct tee_wsm tci; + /* The list entry to attach to session list of owner */ + struct list_head list; + /* Session WSMs lock */ + struct mutex wsms_lock; + /* WSMs for a session */ + struct tee_wsm wsms[MC_MAP_MAX]; + /* This TA is of Global Platform type */ + int is_gp; +}; + +struct tee_session *session_create(struct tee_client *client, + const struct mc_identity *identity); +static inline void session_get(struct tee_session *session) +{ + kref_get(&session->kref); +} + +int session_put(struct tee_session *session); +int session_close(struct tee_session *session); + +int session_mc_open_session(struct tee_session *session, + struct mcp_open_info *info); +int session_mc_cleanup_session(struct tee_session *session); +int session_mc_notify(struct tee_session *session); +int session_mc_wait(struct tee_session *session, s32 timeout, + int silent_expiry); +int session_mc_map(struct tee_session *session, struct tee_mmu *mmu, + struct mc_ioctl_buffer *bufs); +int session_mc_unmap(struct tee_session *session, + const struct mc_ioctl_buffer *bufs); +int session_mc_get_err(struct tee_session *session, s32 *err); + +int session_gp_open_session(struct tee_session *session, + const struct mc_uuid_t *uuid, + struct gp_operation *operation, + struct gp_return *gp_ret); +int session_gp_open_session_domu(struct tee_session *session, + const struct mc_uuid_t *uuid, u64 started, + struct interworld_session *iws, + struct tee_mmu **mmus, + struct gp_return *gp_ret); +int session_gp_invoke_command(struct tee_session *session, u32 command_id, + struct gp_operation *operation, + struct gp_return *gp_ret); +int session_gp_invoke_command_domu(struct tee_session *session, + u64 started, struct interworld_session *iws, + struct tee_mmu **mmus, + struct gp_return *gp_ret); +int session_gp_request_cancellation(u64 slot); + +int session_debug_structs(struct kasnprintf_buf *buf, + struct tee_session *session, bool is_closing); + +#endif /* _SESSION_H_ */ diff --git a/drivers/gud/MobiCoreDriver/tee_client_api.h b/drivers/gud/MobiCoreDriver/tee_client_api.h new file mode 100644 index 000000000000..395936a0884b --- /dev/null +++ b/drivers/gud/MobiCoreDriver/tee_client_api.h @@ -0,0 +1,162 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +/* + * This header file corresponds to V1.0 of the GlobalPlatform + * TEE Client API Specification + */ +#ifndef __TEE_CLIENT_API_H__ +#define __TEE_CLIENT_API_H__ + +#include "tee_client_types.h" +#include "tee_client_error.h" + +#include "tee_client_api_imp.h" + +#define TEEC_EXPORT + +/* + * The header tee_client_api_imp.h must define implementation-dependent types, + * constants and macros. + * + * The implementation-dependent types are: + * - teec_context_imp + * - teec_session_imp + * - teec_shared_memory_imp + * - teec_operation_imp + * + * The implementation-dependent constants are: + * - TEEC_CONFIG_SHAREDMEM_MAX_SIZE + * The implementation-dependent macros are: + * - TEEC_PARAM_TYPES + */ + +struct teec_value { + u32 a; + u32 b; +}; + +/* Type definitions */ +struct teec_context { + struct teec_context_imp imp; +}; + +struct teec_session { + struct teec_session_imp imp; +}; + +struct teec_shared_memory { + void *buffer; + size_t size; + u32 flags; + struct teec_shared_memory_imp imp; +}; + +struct teec_temp_memory_reference { + void *buffer; + size_t size; +}; + +struct teec_registered_memory_reference { + struct teec_shared_memory *parent; + size_t size; + size_t offset; +}; + +union teec_parameter { + struct teec_temp_memory_reference tmpref; + struct teec_registered_memory_reference memref; + struct teec_value value; +}; + +struct teec_operation { + u32 started; + u32 param_types; + union teec_parameter params[4]; + struct teec_operation_imp imp; +}; + +#define TEEC_ORIGIN_API 0x00000001 +#define TEEC_ORIGIN_COMMS 0x00000002 +#define TEEC_ORIGIN_TEE 0x00000003 +#define TEEC_ORIGIN_TRUSTED_APP 0x00000004 + +#define TEEC_MEM_INPUT 0x00000001 +#define TEEC_MEM_OUTPUT 0x00000002 + +#define TEEC_NONE 0x0 +#define TEEC_VALUE_INPUT 0x1 +#define TEEC_VALUE_OUTPUT 0x2 +#define TEEC_VALUE_INOUT 0x3 +#define TEEC_MEMREF_TEMP_INPUT 0x5 +#define TEEC_MEMREF_TEMP_OUTPUT 0x6 +#define TEEC_MEMREF_TEMP_INOUT 0x7 +#define TEEC_MEMREF_WHOLE 0xC +#define TEEC_MEMREF_PARTIAL_INPUT 0xD +#define TEEC_MEMREF_PARTIAL_OUTPUT 0xE +#define TEEC_MEMREF_PARTIAL_INOUT 0xF + +#define TEEC_LOGIN_PUBLIC 0x00000000 +#define TEEC_LOGIN_USER 0x00000001 +#define TEEC_LOGIN_GROUP 0x00000002 +#define TEEC_LOGIN_APPLICATION 0x00000004 +#define TEEC_LOGIN_USER_APPLICATION 0x00000005 +#define TEEC_LOGIN_GROUP_APPLICATION 0x00000006 + +#define TEEC_TIMEOUT_INFINITE 0xFFFFFFFF + +#pragma GCC visibility push(default) + +TEEC_EXPORT u32 +teec_initialize_context(const char *name, struct teec_context *context); + +TEEC_EXPORT void +teec_finalize_context(struct teec_context *context); + +TEEC_EXPORT u32 +teec_register_shared_memory(struct teec_context *context, + struct teec_shared_memory *shared_mem); + +TEEC_EXPORT u32 +teec_allocate_shared_memory(struct teec_context *context, + struct teec_shared_memory *shared_mem); + +TEEC_EXPORT void +teec_release_shared_memory(struct teec_shared_memory *shared_mem); + +TEEC_EXPORT u32 +teec_open_session(struct teec_context *context, + struct teec_session *session, + const struct teec_uuid *destination, + u32 connection_method, /* Should be 0 */ + const void *connection_data, + struct teec_operation *operation, + u32 *return_origin); + +TEEC_EXPORT void +teec_close_session(struct teec_session *session); + +TEEC_EXPORT u32 +teec_invoke_command(struct teec_session *session, + u32 command_id, + struct teec_operation *operation, + u32 *return_origin); + +TEEC_EXPORT void +teec_request_cancellation(struct teec_operation *operation); + +#pragma GCC visibility pop + +#endif /* __TEE_CLIENT_API_H__ */ diff --git a/drivers/gud/MobiCoreDriver/tee_client_api_imp.h b/drivers/gud/MobiCoreDriver/tee_client_api_imp.h new file mode 100644 index 000000000000..7ee5ef0ee4e2 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/tee_client_api_imp.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +/* + * This header file defines the implementation-dependent types, + * constants and macros for all the Kinibi implementations of the TEE Client API + */ +#ifndef __TEE_CLIENT_API_IMP_H__ +#define __TEE_CLIENT_API_IMP_H__ + +#define TEEC_MEM_INOUT (TEEC_MEM_INPUT | TEEC_MEM_OUTPUT) + +struct tee_client; + +struct teec_context_imp { + struct tee_client *client; +}; + +struct teec_session_imp { + u32 session_id; + struct teec_context_imp context; + int active; +}; + +struct teec_shared_memory_imp { + struct tee_client *client; + int implementation_allocated; +}; + +struct teec_operation_imp { + struct teec_session_imp *session; +}; + +/* + * There is no natural, compile-time limit on the shared memory, but a specific + * implementation may introduce a limit (in particular on TrustZone) + */ +#define TEEC_CONFIG_SHAREDMEM_MAX_SIZE ((size_t)0xFFFFFFFF) + +#define TEEC_PARAM_TYPES(entry0_type, entry1_type, entry2_type, entry3_type) \ + ((entry0_type) | ((entry1_type) << 4) | \ + ((entry2_type) << 8) | ((entry3_type) << 12)) + +#endif /* __TEE_CLIENT_API_IMP_H__ */ diff --git a/drivers/gud/MobiCoreDriver/tee_client_error.h b/drivers/gud/MobiCoreDriver/tee_client_error.h new file mode 100644 index 000000000000..33553c3e0bcc --- /dev/null +++ b/drivers/gud/MobiCoreDriver/tee_client_error.h @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef __TEE_CLIENT_ERROR_H__ +#define __TEE_CLIENT_ERROR_H__ + +#define TEEC_SUCCESS ((uint32_t)0x00000000) + +/** + * Generic error code : Generic error + **/ +#define TEEC_ERROR_GENERIC ((uint32_t)0xFFFF0000) + +/** + * Generic error code : The underlying security system denies the access to the + * object + **/ +#define TEEC_ERROR_ACCESS_DENIED ((uint32_t)0xFFFF0001) + +/** + * Generic error code : The pending operation is cancelled. + **/ +#define TEEC_ERROR_CANCEL ((uint32_t)0xFFFF0002) + +/** + * Generic error code : The underlying system detects a conflict + **/ +#define TEEC_ERROR_ACCESS_CONFLICT ((uint32_t)0xFFFF0003) + +/** + * Generic error code : Too much data for the operation or some data remain + * unprocessed by the operation. + **/ +#define TEEC_ERROR_EXCESS_DATA ((uint32_t)0xFFFF0004) + +/** + * Generic error code : Error of data format + **/ +#define TEEC_ERROR_BAD_FORMAT ((uint32_t)0xFFFF0005) + +/** + * Generic error code : The specified parameters are invalid + **/ +#define TEEC_ERROR_BAD_PARAMETERS ((uint32_t)0xFFFF0006) + +/** + * Generic error code : Illegal state for the operation. + **/ +#define TEEC_ERROR_BAD_STATE ((uint32_t)0xFFFF0007) + +/** + * Generic error code : The item is not found + **/ +#define TEEC_ERROR_ITEM_NOT_FOUND ((uint32_t)0xFFFF0008) + +/** + * Generic error code : The specified operation is not implemented + **/ +#define TEEC_ERROR_NOT_IMPLEMENTED ((uint32_t)0xFFFF0009) + +/** + * Generic error code : The specified operation is not supported + **/ +#define TEEC_ERROR_NOT_SUPPORTED ((uint32_t)0xFFFF000A) + +/** + * Generic error code : Insufficient data is available for the operation. + **/ +#define TEEC_ERROR_NO_DATA ((uint32_t)0xFFFF000B) + +/** + * Generic error code : Not enough memory to perform the operation + **/ +#define TEEC_ERROR_OUT_OF_MEMORY ((uint32_t)0xFFFF000C) + +/** + * Generic error code : The service is currently unable to handle the request; + * try later + **/ +#define TEEC_ERROR_BUSY ((uint32_t)0xFFFF000D) + +/** + * Generic communication error + **/ +#define TEEC_ERROR_COMMUNICATION ((uint32_t)0xFFFF000E) + +/** + * Generic error code : security violation + **/ +#define TEEC_ERROR_SECURITY ((uint32_t)0xFFFF000F) + +/** + * Generic error code : the buffer is too short + **/ +#define TEEC_ERROR_SHORT_BUFFER ((uint32_t)0xFFFF0010) + +/** + * Error of communication: The target of the connection is dead + **/ +#define TEEC_ERROR_TARGET_DEAD ((uint32_t)0xFFFF3024) + +/** + * File system error code: not enough space to complete the operation. + **/ +#define TEEC_ERROR_STORAGE_NO_SPACE ((uint32_t)0xFFFF3041) + +#endif /* __TEE_CLIENT_ERROR_H__ */ + diff --git a/drivers/gud/MobiCoreDriver/tee_client_types.h b/drivers/gud/MobiCoreDriver/tee_client_types.h new file mode 100644 index 000000000000..47ea2f9b144e --- /dev/null +++ b/drivers/gud/MobiCoreDriver/tee_client_types.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef __TEE_CLIENT_TYPES_H__ +#define __TEE_CLIENT_TYPES_H__ + +/* Definition of an UUID (from RFC 4122 http://www.ietf.org/rfc/rfc4122.txt) */ +struct teec_uuid { + u32 time_low; + u16 time_mid; + u16 time_hi_and_version; + u8 clock_seq_and_node[8]; +}; + +/* Type definition for a TEE Identity */ +struct tee_identity { + u32 login; + struct teec_uuid uuid; +}; + +#endif /* __TEE_CLIENT_TYPES_H__ */ diff --git a/drivers/gud/MobiCoreDriver/teeclientapi.c b/drivers/gud/MobiCoreDriver/teeclientapi.c new file mode 100644 index 000000000000..f12ecce399b0 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/teeclientapi.c @@ -0,0 +1,596 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2018 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#include +#include +#include +#include + +#include "tee_client_api.h" +#include "mc_user.h" + +#include "main.h" +#include "mcinq.h" /* TA termination codes */ +#include "client.h" + +/* Macros */ +#define _TEEC_GET_PARAM_TYPE(t, i) (((t) >> (4 * (i))) & 0xF) + +/* Parameter number */ +#define _TEEC_PARAMETER_NUMBER 4 + +/**teec_shared_memory + * These error codes are still to be decided by GP and as we do not wish to + * expose any part of the GP TAF as of yet, for now they will have to live here + * until we decide what to do about them. + */ +#define TEEC_ERROR_TA_LOCKED 0xFFFF0257 +#define TEEC_ERROR_SD_BLOCKED 0xFFFF0258 +#define TEEC_ERROR_TARGET_KILLED 0xFFFF0259 + +static DECLARE_WAIT_QUEUE_HEAD(operations_wq); + +static void _lib_uuid_to_array(const struct teec_uuid *uuid, u8 *uuid_array) +{ + u8 *identifier_cursor = (u8 *)uuid; + /* offsets and syntax constants. See explanations above */ +#ifdef S_BIG_ENDIAN + u32 offsets = 0; +#else + u32 offsets = 0xF1F1DF13; +#endif + u32 i; + + for (i = 0; i < sizeof(struct teec_uuid); i++) { + /* Two-digit hex number */ + s32 offset = ((s32)((offsets & 0xF) << 28)) >> 28; + u8 number = identifier_cursor[offset]; + + offsets >>= 4; + identifier_cursor++; + + uuid_array[i] = number; + } +} + +static u32 _teec_to_gp_operation(struct teec_operation *teec_op, + struct gp_operation *gp_op) +{ + int i; + int ret = 0; + + for (i = 0; i < _TEEC_PARAMETER_NUMBER; i++) { + switch (_TEEC_GET_PARAM_TYPE(teec_op->param_types, i)) { + case TEEC_VALUE_INPUT: + case TEEC_VALUE_INOUT: + gp_op->params[i].value.a = teec_op->params[i].value.a; + gp_op->params[i].value.b = teec_op->params[i].value.b; + break; + case TEEC_MEMREF_TEMP_INPUT: + case TEEC_MEMREF_TEMP_OUTPUT: + case TEEC_MEMREF_TEMP_INOUT: + gp_op->params[i].tmpref.buffer = + (uintptr_t)teec_op->params[i].tmpref.buffer; + gp_op->params[i].tmpref.size = + teec_op->params[i].tmpref.size; + break; + case TEEC_MEMREF_WHOLE: + case TEEC_MEMREF_PARTIAL_INPUT: + case TEEC_MEMREF_PARTIAL_OUTPUT: + case TEEC_MEMREF_PARTIAL_INOUT: + gp_op->params[i].memref.offset = + teec_op->params[i].memref.offset; + gp_op->params[i].memref.size = + teec_op->params[i].memref.size; + gp_op->params[i].memref.parent.buffer = + (uintptr_t)teec_op->params[i].memref.parent->buffer; + gp_op->params[i].memref.parent.size = + teec_op->params[i].memref.parent->size; + gp_op->params[i].memref.parent.flags = + teec_op->params[i].memref.parent->flags; + break; + case TEEC_NONE: + case TEEC_VALUE_OUTPUT: + break; + default: + ret = -EINVAL; + } + } + gp_op->param_types = teec_op->param_types; + return ret; +} + +static void _teec_from_gp_operation(struct gp_operation *gp_op, + struct teec_operation *teec_op) +{ + int i; + + for (i = 0; i < _TEEC_PARAMETER_NUMBER; i++) { + switch (_TEEC_GET_PARAM_TYPE(gp_op->param_types, i)) { + case TEEC_VALUE_OUTPUT: + case TEEC_VALUE_INOUT: + teec_op->params[i].value.a = gp_op->params[i].value.a; + teec_op->params[i].value.b = gp_op->params[i].value.b; + break; + case TEEC_MEMREF_TEMP_INPUT: + case TEEC_MEMREF_TEMP_OUTPUT: + case TEEC_MEMREF_TEMP_INOUT: + teec_op->params[i].tmpref.size = + gp_op->params[i].tmpref.size; + break; + case TEEC_MEMREF_WHOLE: + break; + case TEEC_MEMREF_PARTIAL_INPUT: + case TEEC_MEMREF_PARTIAL_OUTPUT: + case TEEC_MEMREF_PARTIAL_INOUT: + teec_op->params[i].memref.size = + gp_op->params[i].memref.size; + break; + case TEEC_NONE: + case TEEC_VALUE_INPUT: + break; + default: + break; + } + } +} + +static u32 _teec_convert_error(int errno) +{ + switch (errno) { + case ENOENT: + return TEEC_ERROR_ITEM_NOT_FOUND; + case EACCES: + return TEEC_ERROR_ACCESS_DENIED; + case EINVAL: + return TEEC_ERROR_BAD_PARAMETERS; + case ENOSPC: + return TEEC_ERROR_OUT_OF_MEMORY; + case ECONNREFUSED: + return TEEC_ERROR_SD_BLOCKED; + case ECONNABORTED: + return TEEC_ERROR_TA_LOCKED; + case ECONNRESET: + return TEEC_ERROR_TARGET_KILLED; + case EBUSY: + return TEEC_ERROR_BUSY; + case EKEYREJECTED: + return TEEC_ERROR_SECURITY; + case ETIME: + return TEEC_ERROR_TARGET_DEAD; + default: + return TEEC_ERROR_GENERIC; + } +} + +/* teec_initialize_context: TEEC_SUCCESS, Another error code from Table 4-2 */ +u32 teec_initialize_context(const char *name, struct teec_context *context) +{ + struct tee_client *client; + int ret; + (void)name; + + mc_dev_devel("== %s() ==============", __func__); + + if (!context) { + mc_dev_devel("context is NULL"); + return TEEC_ERROR_BAD_PARAMETERS; + } + + /* Make sure TEE was started */ + ret = mc_wait_tee_start(); + if (ret) { + mc_dev_err(ret, "TEE failed to start, now or in the past"); + return TEEC_ERROR_BAD_STATE; + } + + /* Create client */ + client = client_create(true); + if (!client) + return TEEC_ERROR_OUT_OF_MEMORY; + + /* Store client in context */ + context->imp.client = client; + + return TEEC_SUCCESS; +} +EXPORT_SYMBOL(teec_initialize_context); + +/* + * The implementation of this function MUST NOT be able to fail: after this + * function returns the Client Application must be able to consider that the + * Context has been closed + */ +void teec_finalize_context(struct teec_context *context) +{ + mc_dev_devel("== %s() ==============", __func__); + + /* The parameter context MUST point to an initialized TEE Context */ + if (!context) { + mc_dev_devel("context is NULL"); + return; + } + + /* The implementation of this function MUST NOT be able to fail: after + * this function returns the Client Application must be able to + * consider that the Context has been closed + */ + client_close(context->imp.client); + context->imp.client = NULL; +} +EXPORT_SYMBOL(teec_finalize_context); + +/* + * If the return_origin is different from TEEC_ORIGIN_TRUSTED_APP, an error code + * from Table 4-2. If the return_origin is equal to TEEC_ORIGIN_TRUSTED_APP, a + * return code defined by the protocol between the Client Application and the + * Trusted Application + */ +u32 teec_open_session(struct teec_context *context, + struct teec_session *session, + const struct teec_uuid *destination, + u32 connection_method, + const void *connection_data, + struct teec_operation *operation, + u32 *return_origin) +{ + struct mc_uuid_t uuid; + struct mc_identity identity = {0}; + struct tee_client *client = NULL; + struct gp_operation gp_op; + struct gp_return gp_ret; + int ret = 0, timeout; + + mc_dev_devel("== %s() ==============", __func__); + gp_ret.value = TEEC_SUCCESS; + if (return_origin) + *return_origin = TEEC_ORIGIN_API; + + /* The parameter context MUST point to an initialized TEE Context */ + if (!context) { + mc_dev_devel("context is NULL"); + return TEEC_ERROR_BAD_PARAMETERS; + } + + if (!context->imp.client) { + mc_dev_devel("context not initialized"); + return TEEC_ERROR_BAD_PARAMETERS; + } + client = context->imp.client; + + if (!session) { + mc_dev_devel("session is NULL"); + return TEEC_ERROR_BAD_PARAMETERS; + } + + connection_method = TEEC_TT_LOGIN_KERNEL; + session->imp.active = false; + + _lib_uuid_to_array(destination, uuid.value); + + memset(&gp_op, 0, sizeof(gp_op)); + if (operation) { + operation->imp.session = &session->imp; + ret = _teec_to_gp_operation(operation, &gp_op); + if (ret) + return TEEC_ERROR_BAD_PARAMETERS; + } + + identity.login_type = (enum mc_login_type)connection_method; + + /* Wait for GP loading to be possible, maximum 30s */ + timeout = 30; + do { + ret = client_gp_open_session(client, &uuid, &gp_op, &identity, + &gp_ret, &session->imp.session_id); + if (!ret || ret != EAGAIN) + break; + + msleep(1000); + } while (--timeout); + + if (ret || gp_ret.value != TEEC_SUCCESS) { + mc_dev_devel("client_gp_open_session failed(%08x) %08x", ret, + gp_ret.value); + if (ret && ret != -ECHILD) + gp_ret.value = _teec_convert_error(-ret); + else if (return_origin) + /* Update origin as it's not the API */ + *return_origin = gp_ret.origin; + } else { + mc_dev_devel(" created session ID %x", session->imp.session_id); + session->imp.context = context->imp; + session->imp.active = true; + if (operation) + _teec_from_gp_operation(&gp_op, operation); + } + + mc_dev_devel(" %s() = 0x%x", __func__, gp_ret.value); + return gp_ret.value; +} +EXPORT_SYMBOL(teec_open_session); + +u32 teec_invoke_command(struct teec_session *session, + u32 command_id, + struct teec_operation *operation, + u32 *return_origin) +{ + struct tee_client *client = NULL; + struct gp_operation gp_op = {0}; + struct gp_return gp_ret = {0}; + int ret = 0; + + mc_dev_devel("== %s() ==============", __func__); + + gp_ret.value = TEEC_SUCCESS; + if (return_origin) + *return_origin = TEEC_ORIGIN_API; + + if (!session) { + mc_dev_devel("session is NULL"); + return TEEC_ERROR_BAD_PARAMETERS; + } + + if (!session->imp.active) { + mc_dev_devel("session is inactive"); + return TEEC_ERROR_BAD_STATE; + } + client = session->imp.context.client; + + if (operation) { + operation->imp.session = &session->imp; + if (_teec_to_gp_operation(operation, &gp_op)) + return TEEC_ERROR_BAD_PARAMETERS; + } else { + gp_op.param_types = 0; + } + + ret = client_gp_invoke_command(client, session->imp.session_id, + command_id, &gp_op, &gp_ret); + + if (ret || gp_ret.value != TEEC_SUCCESS) { + mc_dev_devel("client_gp_invoke_command failed(%08x) %08x", ret, + gp_ret.value); + if (ret && ret != -ECHILD) + gp_ret.value = _teec_convert_error(-ret); + else if (return_origin) + /* Update origin as it's not the API */ + *return_origin = gp_ret.origin; + } else if (operation) { + _teec_from_gp_operation(&gp_op, operation); + } + + mc_dev_devel(" %s() = 0x%x", __func__, gp_ret.value); + return gp_ret.value; +} +EXPORT_SYMBOL(teec_invoke_command); + +void teec_close_session(struct teec_session *session) +{ + int ret = 0; + struct tee_client *client = NULL; + + mc_dev_devel("== %s() ==============", __func__); + + /* The implementation MUST do nothing if session is NULL */ + if (!session) { + mc_dev_devel("session is NULL"); + return; + } + client = session->imp.context.client; + + if (session->imp.active) { + ret = client_gp_close_session(client, session->imp.session_id); + + if (ret) + /* continue even in case of error */ + mc_dev_devel("client_gp_close failed(%08x)", ret); + + session->imp.active = false; + } + + mc_dev_devel(" %s() = 0x%x", __func__, ret); +} +EXPORT_SYMBOL(teec_close_session); + +/* + * Implementation note. We handle internally 2 kind of pointers : kernel memory + * (kmalloc, get_pages, ...) and dynamic memory (vmalloc). A global pointer from + * a kernel module has the same format as a vmalloc buffer. However, our code + * cannot detect that, so it considers it a kmalloc buffer. The TA trying to use + * that shared buffer is likely to crash + */ +u32 teec_register_shared_memory(struct teec_context *context, + struct teec_shared_memory *shared_mem) +{ + struct gp_shared_memory memref; + struct gp_return gp_ret; + int ret = 0; + + mc_dev_devel("== %s() ==============", __func__); + + /* The parameter context MUST point to an initialized TEE Context */ + if (!context) { + mc_dev_devel("context is NULL"); + return TEEC_ERROR_BAD_PARAMETERS; + } + /* + * The parameter shared_mem MUST point to the Shared Memory structure + * defining the memory region to register + */ + if (!shared_mem) { + mc_dev_devel("shared_mem is NULL"); + return TEEC_ERROR_BAD_PARAMETERS; + } + /* + * The buffer field MUST point to the memory region to be shared, + * and MUST not be NULL + */ + if (!shared_mem->buffer) { + mc_dev_devel("shared_mem->buffer is NULL"); + return TEEC_ERROR_BAD_PARAMETERS; + } + if (shared_mem->flags & ~TEEC_MEM_INOUT) { + mc_dev_devel("shared_mem->flags is incorrect"); + return TEEC_ERROR_BAD_PARAMETERS; + } + if (!shared_mem->flags) { + mc_dev_devel("shared_mem->flags is incorrect"); + return TEEC_ERROR_BAD_PARAMETERS; + } + + memref.buffer = (uintptr_t)shared_mem->buffer; + memref.flags = shared_mem->flags; + memref.size = shared_mem->size; + ret = client_gp_register_shared_mem(context->imp.client, NULL, NULL, + &memref, &gp_ret); + + if (ret) + return _teec_convert_error(-ret); + + shared_mem->imp.client = context->imp.client; + shared_mem->imp.implementation_allocated = false; + + return TEEC_SUCCESS; +} +EXPORT_SYMBOL(teec_register_shared_memory); + +u32 teec_allocate_shared_memory(struct teec_context *context, + struct teec_shared_memory *shared_mem) +{ + struct gp_shared_memory memref; + struct gp_return gp_ret; + int ret = 0; + + /* No connection to "context"? */ + mc_dev_devel("== %s() ==============", __func__); + + /* The parameter context MUST point to an initialized TEE Context */ + if (!context) { + mc_dev_devel("context is NULL"); + return TEEC_ERROR_BAD_PARAMETERS; + } + /* + * The parameter shared_mem MUST point to the Shared Memory structure + * defining the memory region to register + */ + if (!shared_mem) { + mc_dev_devel("shared_mem is NULL"); + return TEEC_ERROR_BAD_PARAMETERS; + } + if (shared_mem->flags & ~TEEC_MEM_INOUT) { + mc_dev_devel("shared_mem->flags is incorrect"); + return TEEC_ERROR_BAD_PARAMETERS; + } + if (!shared_mem->flags) { + mc_dev_devel("shared_mem->flags is incorrect"); + return TEEC_ERROR_BAD_PARAMETERS; + } + + shared_mem->buffer = vmalloc(shared_mem->size); + if (!shared_mem->buffer) + return TEEC_ERROR_OUT_OF_MEMORY; + + memref.buffer = (uintptr_t)shared_mem->buffer; + memref.flags = shared_mem->flags; + memref.size = shared_mem->size; + ret = client_gp_register_shared_mem(context->imp.client, NULL, NULL, + &memref, &gp_ret); + + if (ret) { + vfree(shared_mem->buffer); + shared_mem->buffer = NULL; + shared_mem->size = 0; + return _teec_convert_error(-ret); + } + + shared_mem->imp.client = context->imp.client; + shared_mem->imp.implementation_allocated = true; + + return TEEC_SUCCESS; +} +EXPORT_SYMBOL(teec_allocate_shared_memory); + +void teec_release_shared_memory(struct teec_shared_memory *shared_mem) +{ + struct gp_shared_memory memref; + + /* No connection to "context"? */ + mc_dev_devel("== %s() ==============", __func__); + + /* The implementation MUST do nothing if shared_mem is NULL */ + if (!shared_mem) { + mc_dev_devel("shared_mem is NULL"); + return; + } + + memref.buffer = (uintptr_t)shared_mem->buffer; + memref.flags = shared_mem->flags; + memref.size = shared_mem->size; + (void)client_gp_release_shared_mem(shared_mem->imp.client, &memref); + + /* + * For a memory buffer allocated using teec_allocate_shared_memory the + * Implementation MUST free the underlying memory + */ + if (shared_mem->imp.implementation_allocated) { + if (shared_mem->buffer) { + vfree(shared_mem->buffer); + shared_mem->buffer = NULL; + shared_mem->size = 0; + } + } +} +EXPORT_SYMBOL(teec_release_shared_memory); + +void teec_request_cancellation(struct teec_operation *operation) +{ + struct teec_session_imp *session; + int ret; + + mc_dev_devel("== %s() ==============", __func__); + + ret = wait_event_interruptible(operations_wq, operation->started); + if (ret == -ERESTARTSYS) { + mc_dev_devel("signal received"); + return; + } + + mc_dev_devel("operation->started changed from 0 to %d", + operation->started); + + if (operation->started > 1) { + mc_dev_devel("the operation has finished"); + return; + } + + session = operation->imp.session; + operation->started = 2; + wake_up_interruptible(&operations_wq); + + if (!session->active) { + mc_dev_devel("Corresponding session is not active"); + return; + } + + /* TODO: handle cancellation */ + + /* Signal the Trustlet */ + ret = client_notify_session(session->context.client, + session->session_id); + if (ret) + mc_dev_devel("Notify failed: %d", ret); +} +EXPORT_SYMBOL(teec_request_cancellation); diff --git a/drivers/gud/MobiCoreDriver/user.c b/drivers/gud/MobiCoreDriver/user.c new file mode 100644 index 000000000000..8b50853cce23 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/user.c @@ -0,0 +1,423 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2018 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#include +#include +#include +#include +#include /* struct vm_area_struct */ +#include + +#include "mc_user.h" + +#include "main.h" +#include "user.h" +#include "client.h" +#include "mcp.h" /* mcp_get_version */ + +/* + * Get client object from file pointer + */ +static inline struct tee_client *get_client(struct file *file) +{ + return (struct tee_client *)file->private_data; +} + +/* + * Callback for system open() + * A set of internal client data are created and initialized. + * + * @inode + * @file + * Returns 0 if OK or -ENOMEM if no allocation was possible. + */ +static int user_open(struct inode *inode, struct file *file) +{ + struct tee_client *client; + + /* Create client */ + mc_dev_devel("from %s (%d)", current->comm, current->pid); + client = client_create(false); + if (!client) + return -ENOMEM; + + /* Store client in user file */ + file->private_data = client; + return 0; +} + +/* + * Callback for system close() + * The client object is freed. + * @inode + * @file + * Returns 0 + */ +static int user_release(struct inode *inode, struct file *file) +{ + struct tee_client *client = get_client(file); + + /* Close client */ + mc_dev_devel("from %s (%d)", current->comm, current->pid); + if (!client) + return -EPROTO; + + /* Detach client from user file */ + file->private_data = NULL; + + /* Destroy client, including remaining sessions */ + client_close(client); + return 0; +} + +/* + * Check r/w access to referenced memory + */ +static inline int ioctl_check_pointer(unsigned int cmd, int __user *uarg) +{ + int err = 0; + + if (_IOC_DIR(cmd) & _IOC_READ) +#if KERNEL_VERSION(5, 0, 0) > LINUX_VERSION_CODE + err = !access_ok(VERIFY_WRITE, uarg, _IOC_SIZE(cmd)); +#else + err = !access_ok(uarg, _IOC_SIZE(cmd)); +#endif + else if (_IOC_DIR(cmd) & _IOC_WRITE) +#if KERNEL_VERSION(5, 0, 0) > LINUX_VERSION_CODE + err = !access_ok(VERIFY_READ, uarg, _IOC_SIZE(cmd)); +#else + err = !access_ok(uarg, _IOC_SIZE(cmd)); +#endif + + if (err) + return -EFAULT; + + return 0; +} + +/* + * Callback for system ioctl() + * Implement most of ClientLib API functions + * @file pointer to file + * @cmd command + * @arg arguments + * + * Returns 0 for OK and an errno in case of error + */ +static long user_ioctl(struct file *file, unsigned int id, unsigned long arg) +{ + struct tee_client *client = get_client(file); + int __user *uarg = (int __user *)arg; + int ret = -EINVAL; + + mc_dev_devel("%u from %s", _IOC_NR(id), current->comm); + + if (!client) + return -EPROTO; + + if (ioctl_check_pointer(id, uarg)) + return -EFAULT; + + switch (id) { + case MC_IO_HAS_SESSIONS: + /* Freeze the client */ + if (client_has_sessions(client)) + ret = -ENOTEMPTY; + else + ret = 0; + break; + + case MC_IO_OPEN_SESSION: { + struct mc_ioctl_open_session session; + + if (copy_from_user(&session, uarg, sizeof(session))) { + ret = -EFAULT; + break; + } + + ret = client_mc_open_session(client, &session.uuid, + session.tci, session.tcilen, + &session.sid); + if (ret) + break; + + if (copy_to_user(uarg, &session, sizeof(session))) { + ret = -EFAULT; + client_remove_session(client, session.sid); + break; + } + break; + } + case MC_IO_OPEN_TRUSTLET: { + struct mc_ioctl_open_trustlet trustlet; + + if (copy_from_user(&trustlet, uarg, sizeof(trustlet))) { + ret = -EFAULT; + break; + } + + ret = client_mc_open_trustlet(client, trustlet.spid, + trustlet.buffer, trustlet.tlen, + trustlet.tci, trustlet.tcilen, + &trustlet.sid); + if (ret) + break; + + if (copy_to_user(uarg, &trustlet, sizeof(trustlet))) { + ret = -EFAULT; + client_remove_session(client, trustlet.sid); + break; + } + break; + } + case MC_IO_CLOSE_SESSION: { + u32 sid = (u32)arg; + + ret = client_remove_session(client, sid); + break; + } + case MC_IO_NOTIFY: { + u32 sid = (u32)arg; + + ret = client_notify_session(client, sid); + break; + } + case MC_IO_WAIT: { + struct mc_ioctl_wait wait; + + if (copy_from_user(&wait, uarg, sizeof(wait))) { + ret = -EFAULT; + break; + } + ret = client_waitnotif_session(client, wait.sid, wait.timeout, + wait.partial); + break; + } + case MC_IO_MAP: { + struct mc_ioctl_map map; + + if (copy_from_user(&map, uarg, sizeof(map))) { + ret = -EFAULT; + break; + } + + ret = client_mc_map(client, map.sid, NULL, &map.buf); + if (ret) + break; + + /* Fill in return struct */ + if (copy_to_user(uarg, &map, sizeof(map))) { + ret = -EFAULT; + break; + } + break; + } + case MC_IO_UNMAP: { + struct mc_ioctl_map map; + + if (copy_from_user(&map, uarg, sizeof(map))) { + ret = -EFAULT; + break; + } + + ret = client_mc_unmap(client, map.sid, &map.buf); + break; + } + case MC_IO_ERR: { + struct mc_ioctl_geterr __user *uerr = + (struct mc_ioctl_geterr __user *)uarg; + u32 sid; + s32 exit_code; + + if (get_user(sid, &uerr->sid)) { + ret = -EFAULT; + break; + } + + ret = client_get_session_exitcode(client, sid, &exit_code); + if (ret) + break; + + /* Fill in return struct */ + if (put_user(exit_code, &uerr->value)) { + ret = -EFAULT; + break; + } + + break; + } + case MC_IO_VERSION: { + struct mc_version_info version_info; + + ret = mcp_get_version(&version_info); + if (ret) + break; + + if (copy_to_user(uarg, &version_info, sizeof(version_info))) + ret = -EFAULT; + + break; + } + case MC_IO_GP_INITIALIZE_CONTEXT: { + struct mc_ioctl_gp_initialize_context context; + + if (copy_from_user(&context, uarg, sizeof(context))) { + ret = -EFAULT; + break; + } + + ret = client_gp_initialize_context(client, &context.ret); + + if (copy_to_user(uarg, &context, sizeof(context))) { + ret = -EFAULT; + break; + } + break; + } + case MC_IO_GP_REGISTER_SHARED_MEM: { + struct mc_ioctl_gp_register_shared_mem shared_mem; + + if (copy_from_user(&shared_mem, uarg, sizeof(shared_mem))) { + ret = -EFAULT; + break; + } + + ret = client_gp_register_shared_mem(client, NULL, NULL, + &shared_mem.memref, + &shared_mem.ret); + + if (copy_to_user(uarg, &shared_mem, sizeof(shared_mem))) { + ret = -EFAULT; + break; + } + break; + } + case MC_IO_GP_RELEASE_SHARED_MEM: { + struct mc_ioctl_gp_release_shared_mem shared_mem; + + if (copy_from_user(&shared_mem, uarg, sizeof(shared_mem))) { + ret = -EFAULT; + break; + } + + ret = client_gp_release_shared_mem(client, &shared_mem.memref); + break; + } + case MC_IO_GP_OPEN_SESSION: { + struct mc_ioctl_gp_open_session session; + + if (copy_from_user(&session, uarg, sizeof(session))) { + ret = -EFAULT; + break; + } + + ret = client_gp_open_session(client, &session.uuid, + &session.operation, + &session.identity, + &session.ret, &session.session_id); + + if (copy_to_user(uarg, &session, sizeof(session))) { + ret = -EFAULT; + break; + } + break; + } + case MC_IO_GP_CLOSE_SESSION: { + struct mc_ioctl_gp_close_session session; + + if (copy_from_user(&session, uarg, sizeof(session))) { + ret = -EFAULT; + break; + } + + ret = client_gp_close_session(client, session.session_id); + break; + } + case MC_IO_GP_INVOKE_COMMAND: { + struct mc_ioctl_gp_invoke_command command; + + if (copy_from_user(&command, uarg, sizeof(command))) { + ret = -EFAULT; + break; + } + + ret = client_gp_invoke_command(client, command.session_id, + command.command_id, + &command.operation, + &command.ret); + + if (copy_to_user(uarg, &command, sizeof(command))) { + ret = -EFAULT; + break; + } + break; + } + case MC_IO_GP_REQUEST_CANCELLATION: { + struct mc_ioctl_gp_request_cancellation cancel; + + if (copy_from_user(&cancel, uarg, sizeof(cancel))) { + ret = -EFAULT; + break; + } + + client_gp_request_cancellation(client, + cancel.operation.started); + ret = 0; + break; + } + default: + ret = -ENOIOCTLCMD; + mc_dev_err(ret, "unsupported command no %d", id); + } + + return ret; +} + +/* + * Callback for system mmap() + */ +static int user_mmap(struct file *file, struct vm_area_struct *vmarea) +{ + struct tee_client *client = get_client(file); + + if ((vmarea->vm_end - vmarea->vm_start) > BUFFER_LENGTH_MAX) { + mc_dev_err(-EINVAL, "buffer size %lu too big", + vmarea->vm_end - vmarea->vm_start); + return -EINVAL; + } + + /* Alloc contiguous buffer for this client */ + return client_cbuf_create(client, + (u32)(vmarea->vm_end - vmarea->vm_start), + NULL, vmarea); +} + +static const struct file_operations mc_user_fops = { + .owner = THIS_MODULE, + .open = user_open, + .release = user_release, + .unlocked_ioctl = user_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = user_ioctl, +#endif + .mmap = user_mmap, +}; + +int mc_user_init(struct cdev *cdev) +{ + cdev_init(cdev, &mc_user_fops); + return 0; +} diff --git a/drivers/gud/MobiCoreDriver/user.h b/drivers/gud/MobiCoreDriver/user.h new file mode 100644 index 000000000000..e6972304ece5 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/user.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2013-2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef _USER_H_ +#define _USER_H_ + +struct cdev; + +int mc_user_init(struct cdev *cdev); +static inline void mc_user_exit(void) +{ +} + +#endif /* _USER_H_ */ diff --git a/drivers/gud/MobiCoreDriver/xen_be.c b/drivers/gud/MobiCoreDriver/xen_be.c new file mode 100644 index 000000000000..efa0d71810dc --- /dev/null +++ b/drivers/gud/MobiCoreDriver/xen_be.c @@ -0,0 +1,1163 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017-2018 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifdef CONFIG_XEN + +#include +#include +#include +#include +#include + +#if KERNEL_VERSION(4, 0, 0) > LINUX_VERSION_CODE +#include +#endif + +#include "platform.h" /* MC_XENBUS_MAP_RING_VALLOC_4_1 */ +#include "main.h" +#include "admin.h" /* tee_object* */ +#include "client.h" /* Consider other VMs as clients */ +#include "mmu.h" +#include "mcp.h" /* mcp_get_version */ +#include "nq.h" +#include "xen_common.h" +#include "xen_be.h" + +#define vaddr(page) ((unsigned long)pfn_to_kaddr(page_to_pfn(page))) + +static struct { + struct list_head xfes; + struct mutex xfes_mutex; /* Protect the above */ +} l_ctx; + +/* Maps */ + +struct xen_be_map { + struct page **pages; + grant_handle_t *handles; + unsigned long nr_pages; + u32 flags; + bool pages_allocd; + bool refs_mapped; + /* To auto-delete */ + struct tee_deleter deleter; +}; + +static void xen_be_map_delete(struct xen_be_map *map) +{ + int i; + + if (map->refs_mapped) { + struct gnttab_unmap_grant_ref *unmaps; + + unmaps = kcalloc(map->nr_pages, sizeof(*unmaps), GFP_KERNEL); + if (!unmaps) + /* Cannot go on */ + return; + + for (i = 0; i < map->nr_pages; i++) + gnttab_set_unmap_op(&unmaps[i], vaddr(map->pages[i]), + map->flags, map->handles[i]); + + if (gnttab_unmap_refs(unmaps, NULL, map->pages, map->nr_pages)) + /* Cannot go on */ + return; + + for (i = 0; i < map->nr_pages; i++) + put_page(map->pages[i]); + + kfree(unmaps); + } + + if (map->pages_allocd) +#if KERNEL_VERSION(4, 0, 0) <= LINUX_VERSION_CODE + gnttab_free_pages(map->nr_pages, map->pages); +#else + free_xenballooned_pages(map->nr_pages, map->pages); +#endif + + kfree(map->handles); + kfree(map->pages); + kfree(map); + mc_dev_devel("freed xen map %p", map); + atomic_dec(&g_ctx.c_xen_maps); +} + +static struct xen_be_map *be_map_create(const struct xen_be_map *pte_map, + grant_ref_t *refs, int nr_refs, + int pte_entries_max, int dom_id, + bool readonly) +{ + struct xen_be_map *map; + struct gnttab_map_grant_ref *maps = NULL; + int i, ret = -ENOMEM; + + map = kzalloc(sizeof(*map), GFP_KERNEL); + if (!map) + return ERR_PTR(-ENOMEM); + + atomic_inc(&g_ctx.c_xen_maps); + map->flags = GNTMAP_host_map; + if (readonly) + map->flags |= GNTMAP_readonly; + + map->nr_pages = nr_refs; + map->pages = kcalloc(map->nr_pages, sizeof(*map->pages), GFP_KERNEL); + if (!map->pages) + goto out; + + map->handles = kcalloc(map->nr_pages, sizeof(*map->handles), + GFP_KERNEL); + if (!map->handles) + goto out; + +#if KERNEL_VERSION(4, 0, 0) <= LINUX_VERSION_CODE + if (gnttab_alloc_pages(map->nr_pages, map->pages)) + goto out; +#else + if (alloc_xenballooned_pages(map->nr_pages, map->pages, false)) + goto out; +#endif + + map->pages_allocd = true; + maps = kcalloc(map->nr_pages, sizeof(*maps), GFP_KERNEL); + if (!maps) + goto out; + + if (pte_map) { + int k = 0, nr_refs_left = nr_refs; + + for (i = 0; i < pte_map->nr_pages; i++) { + int j, nr_refs = nr_refs_left; + grant_ref_t *refs = (void *)vaddr(pte_map->pages[i]); + + if (nr_refs > pte_entries_max) + nr_refs = pte_entries_max; + + for (j = 0; j < nr_refs; j++) { + mc_dev_devel("map [%d, %d] -> %d ref %u", + i, j, k, refs[j]); +#ifdef DEBUG + /* Relax serial interface to not kill the USB */ + usleep_range(100, 200); +#endif + gnttab_set_map_op( + &maps[k], vaddr(map->pages[k]), + map->flags, refs[j], dom_id); + nr_refs_left--; + k++; + } + } + } else { + for (i = 0; i < map->nr_pages; i++) { + mc_dev_devel("map table %d ref %u", i, refs[i]); + gnttab_set_map_op(&maps[i], vaddr(map->pages[i]), + map->flags, refs[i], dom_id); + } + } + + ret = gnttab_map_refs(maps, NULL, map->pages, map->nr_pages); + if (ret) + goto out; + + map->refs_mapped = true; + /* Pin pages */ + for (i = 0; i < map->nr_pages; i++) { + get_page(map->pages[i]); + map->handles[i] = maps[i].handle; + } + +out: + kfree(maps); + + if (ret) { + xen_be_map_delete(map); + return ERR_PTR(-ret); + } + + mc_dev_devel("created %s xen map %p", pte_map ? "buffer" : "ptes", map); + return map; +} + +static struct xen_be_map *xen_be_map_create(struct tee_xen_buffer *buffer, + int pte_entries_max, int dom_id) +{ + struct xen_be_map *map; + struct xen_be_map *pte_map; + int nr_pte_refs = + (buffer->info->nr_refs + pte_entries_max - 1) / pte_entries_max; + + /* First map the PTE pages */ + pte_map = be_map_create(NULL, buffer->data.refs, nr_pte_refs, + pte_entries_max, dom_id, true); + if (IS_ERR(pte_map)) + return pte_map; + + /* Now map the pages */ + map = be_map_create(pte_map, NULL, buffer->info->nr_refs, + pte_entries_max, dom_id, + buffer->info->flags == MC_IO_MAP_INPUT); + /* PTE pages mapping not needed any more */ + xen_be_map_delete(pte_map); + if (!IS_ERR(map)) { + /* Auto-delete */ + map->deleter.object = map; + map->deleter.delete = (void(*)(void *))xen_be_map_delete; + } + + return map; +} + +/* Dom0 call to DomU */ + +/* Must be called under xfe->ring_mutex */ +static inline void call_domu(struct tee_xfe *xfe, enum tee_xen_dom0_cmd cmd, + u32 id, int ret) +{ + WARN_ON(!xfe->ring_busy); + + /* Set command and ID */ + xfe->ring->dom0.cmd = cmd; + xfe->ring->dom0.id = id; + xfe->ring->dom0.cmd_ret = ret; + mc_dev_devel("Dom0 -> DomU request %u id %u ret %d", + xfe->ring->dom0.cmd, xfe->ring->dom0.id, + xfe->ring->dom0.cmd_ret); + /* Call */ + notify_remote_via_irq(xfe->irq_dom0); + wait_for_completion(&xfe->ring_completion); +} + +/* Will be called back under xfe->ring_mutex */ +static irqreturn_t xen_be_irq_handler_dom0_th(int intr, void *arg) +{ + struct tee_xfe *xfe = arg; + + if (!xfe->ring->dom0.cmd) { + mc_dev_devel("Ignore IRQ with no command (on DomU connect)"); + return IRQ_HANDLED; + } + + WARN_ON(!xfe->ring_busy); + + /* Response to a dom0 command, our side of ring locked by us */ + mc_dev_devel("Dom0 -> DomU response %u id %u ret %d", + xfe->ring->dom0.cmd, xfe->ring->dom0.id, + xfe->ring->dom0.cmd_ret); + xfe->ring->dom0.cmd = TEE_XEN_DOM0_NONE; + xfe->ring->dom0.id = 0; + xfe->ring->dom0.cmd_ret = -EPERM; + complete(&xfe->ring_completion); + + return IRQ_HANDLED; +} + +/* MC protocol interface */ + +static inline int xen_be_get_version(struct tee_xfe *xfe) +{ + struct mc_version_info version_info; + int ret; + + ret = mcp_get_version(&version_info); + if (ret) + return ret; + + xfe->ring->domu.version_info = version_info; + return 0; +} + +static inline int xen_be_mc_has_sessions(struct tee_xfe *xfe) +{ + return client_has_sessions(xfe->client) ? -EBUSY : 0; +} + +static inline int xen_be_mc_open_session(struct tee_xfe *xfe) +{ + struct tee_xen_buffer *tci_buffer = &xfe->buffers[0]; + struct mcp_open_info info = { + .type = TEE_MC_UUID, + .uuid = &xfe->ring->domu.uuid, + }; + int ret; + + if (tci_buffer->info->flags) { + struct xen_be_map *map; + struct mcp_buffer_map b_map = { + .offset = tci_buffer->info->offset, + .length = tci_buffer->info->length, + .flags = tci_buffer->info->flags, + }; + + map = xen_be_map_create(tci_buffer, xfe->pte_entries_max, + xfe->xdev->otherend_id); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + goto out; + } + + /* Shall be freed by session */ + b_map.nr_pages = map->nr_pages; + info.tci_mmu = tee_mmu_wrap(&map->deleter, map->pages, + &b_map); + if (IS_ERR(info.tci_mmu)) { + ret = PTR_ERR(info.tci_mmu); + info.tci_mmu = NULL; + goto out; + } + } + + /* Open session */ + ret = client_mc_open_common(xfe->client, &info, + &xfe->ring->domu.session_id); + +out: + if (info.tci_mmu) + tee_mmu_put(info.tci_mmu); + + mc_dev_devel("session %x, exit with %d", + xfe->ring->domu.session_id, ret); + return ret; +} + +static inline int xen_be_mc_open_trustlet(struct tee_xfe *xfe) +{ + struct tee_xen_buffer *ta_buffer = &xfe->buffers[1]; + struct tee_xen_buffer *tci_buffer = &xfe->buffers[0]; + struct mcp_open_info info = { + .type = TEE_MC_TA, + }; + struct xen_be_map *ta_map; + void *addr = NULL; + int ret = -ENOMEM; + + ta_map = xen_be_map_create(ta_buffer, xfe->pte_entries_max, + xfe->xdev->otherend_id); + if (IS_ERR(ta_map)) + return PTR_ERR(ta_map); + + info.spid = xfe->ring->domu.spid; + addr = vmap(ta_map->pages, ta_map->nr_pages, + VM_MAP | VM_IOREMAP | VM_USERMAP, PAGE_KERNEL); + if (!addr) + goto out; + + info.va = (uintptr_t)addr + ta_buffer->info->offset; + info.len = ta_buffer->info->length; + + if (tci_buffer->info->flags) { + struct xen_be_map *map; + struct mcp_buffer_map b_map = { + .offset = tci_buffer->info->offset, + .length = tci_buffer->info->length, + .flags = tci_buffer->info->flags, + }; + + map = xen_be_map_create(tci_buffer, xfe->pte_entries_max, + xfe->xdev->otherend_id); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + goto out; + } + + /* Shall be freed by session */ + b_map.nr_pages = map->nr_pages; + info.tci_mmu = tee_mmu_wrap(&map->deleter, map->pages, &b_map); + if (IS_ERR(info.tci_mmu)) { + ret = PTR_ERR(info.tci_mmu); + info.tci_mmu = NULL; + goto out; + } + } + + /* Open session */ + ret = client_mc_open_common(xfe->client, &info, + &xfe->ring->domu.session_id); + +out: + if (info.tci_mmu) + tee_mmu_put(info.tci_mmu); + + if (addr) + vunmap(addr); + + xen_be_map_delete(ta_map); + + mc_dev_devel("session %x, exit with %d", + xfe->ring->domu.session_id, ret); + return ret; +} + +static inline int xen_be_mc_close_session(struct tee_xfe *xfe) +{ + return client_remove_session(xfe->client, xfe->ring->domu.session_id); +} + +static inline int xen_be_mc_notify(struct tee_xfe *xfe) +{ + return client_notify_session(xfe->client, xfe->ring->domu.session_id); +} + +/* mc_wait cannot keep the ring busy while waiting, so we use a worker */ +struct mc_wait_work { + struct work_struct work; + struct tee_xfe *xfe; + u32 session_id; + s32 timeout; + u32 id; +}; + +static void xen_be_mc_wait_worker(struct work_struct *work) +{ + struct mc_wait_work *wait_work = + container_of(work, struct mc_wait_work, work); + struct tee_xfe *xfe = wait_work->xfe; + int ret; + + ret = client_waitnotif_session(wait_work->xfe->client, + wait_work->session_id, + wait_work->timeout, false); + + /* Send return code */ + mc_dev_devel("MC wait session done %x, ret %d", + wait_work->session_id, ret); + ring_get(xfe); + /* In */ + xfe->ring->dom0.session_id = wait_work->session_id; + /* Call */ + call_domu(xfe, TEE_XEN_MC_WAIT_DONE, wait_work->id, ret); + /* Out */ + ring_put(xfe); + kfree(wait_work); + tee_xfe_put(xfe); +} + +static inline int xen_be_mc_wait(struct tee_xfe *xfe) +{ + struct mc_wait_work *wait_work; + + /* Wait in a separate thread to release the communication ring */ + wait_work = kzalloc(sizeof(*wait_work), GFP_KERNEL); + if (!wait_work) + return -ENOMEM; + + tee_xfe_get(xfe); + wait_work->xfe = xfe; + wait_work->session_id = xfe->ring->domu.session_id; + wait_work->timeout = xfe->ring->domu.timeout; + wait_work->id = xfe->ring->domu.id; + INIT_WORK(&wait_work->work, xen_be_mc_wait_worker); + schedule_work(&wait_work->work); + return 0; +} + +static inline int xen_be_mc_map(struct tee_xfe *xfe) +{ + struct tee_xen_buffer *buffer = &xfe->buffers[0]; + struct xen_be_map *map; + struct tee_mmu *mmu = NULL; + struct mc_ioctl_buffer buf; + struct mcp_buffer_map b_map = { + .offset = buffer->info->offset, + .length = buffer->info->length, + .flags = buffer->info->flags, + }; + int ret; + + map = xen_be_map_create(buffer, xfe->pte_entries_max, + xfe->xdev->otherend_id); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + return ret; + } + + /* Shall be freed by session */ + b_map.nr_pages = map->nr_pages; + mmu = tee_mmu_wrap(&map->deleter, map->pages, &b_map); + if (IS_ERR(mmu)) { + xen_be_map_delete(map); + return PTR_ERR(mmu); + } + + ret = client_mc_map(xfe->client, xfe->ring->domu.session_id, mmu, &buf); + /* Releasing the MMU shall also clear the map */ + tee_mmu_put(mmu); + if (!ret) + buffer->info->sva = buf.sva; + + mc_dev_devel("session %x, exit with %d", + xfe->ring->domu.session_id, ret); + return ret; +} + +static inline int xen_be_mc_unmap(struct tee_xfe *xfe) +{ + struct tee_xen_buffer *buffer = &xfe->buffers[0]; + struct mc_ioctl_buffer buf = { + .len = buffer->info->length, + .sva = buffer->info->sva, + }; + int ret; + + ret = client_mc_unmap(xfe->client, xfe->ring->domu.session_id, &buf); + + mc_dev_devel("session %x, exit with %d", + xfe->ring->domu.session_id, ret); + return ret; +} + +static inline int xen_be_mc_get_err(struct tee_xfe *xfe) +{ + int ret; + + ret = client_get_session_exitcode(xfe->client, + xfe->ring->domu.session_id, + &xfe->ring->domu.err); + mc_dev_devel("session %x err %d, exit with %d", + xfe->ring->domu.session_id, xfe->ring->domu.err, ret); + return ret; +} + +/* GP protocol interface */ + +static inline int xen_be_gp_register_shared_mem(struct tee_xfe *xfe) +{ + struct tee_xen_buffer *buffer = &xfe->buffers[0]; + struct xen_be_map *map; + struct tee_mmu *mmu = NULL; + struct gp_shared_memory memref = { + .buffer = buffer->info->addr, + .size = buffer->info->length, + .flags = buffer->info->flags, + }; + struct mcp_buffer_map b_map = { + .offset = buffer->info->offset, + .length = buffer->info->length, + .flags = buffer->info->flags, + }; + int ret; + + map = xen_be_map_create(buffer, xfe->pte_entries_max, + xfe->xdev->otherend_id); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + return ret; + } + + /* Shall be freed by session */ + b_map.nr_pages = map->nr_pages; + mmu = tee_mmu_wrap(&map->deleter, map->pages, &b_map); + if (IS_ERR(mmu)) { + xen_be_map_delete(map); + return PTR_ERR(mmu); + } + + ret = client_gp_register_shared_mem(xfe->client, mmu, + &buffer->info->sva, &memref, + &xfe->ring->domu.gp_ret); + /* Releasing the MMU shall also clear the map */ + tee_mmu_put(mmu); + mc_dev_devel("session %x, exit with %d", + xfe->ring->domu.session_id, ret); + return ret; +} + +static inline int xen_be_gp_release_shared_mem(struct tee_xfe *xfe) +{ + struct tee_xen_buffer *buffer = &xfe->buffers[0]; + struct gp_shared_memory memref = { + .buffer = buffer->info->addr, + .size = buffer->info->length, + .flags = buffer->info->flags, + }; + int ret; + + ret = client_gp_release_shared_mem(xfe->client, &memref); + + mc_dev_devel("exit with %d", ret); + return ret; +} + +/* GP functions cannot keep the ring busy while waiting, so we use a worker */ +struct gp_work { + struct work_struct work; + struct tee_xfe *xfe; + u64 operation_id; + struct interworld_session iws; + struct tee_mmu *mmus[4]; + struct mc_uuid_t uuid; + u32 session_id; + u32 id; +}; + +static void xen_be_gp_open_session_worker(struct work_struct *work) +{ + struct gp_work *gp_work = container_of(work, struct gp_work, work); + struct tee_xfe *xfe = gp_work->xfe; + struct gp_return gp_ret; + int i, ret; + + ret = client_gp_open_session_domu(xfe->client, &gp_work->uuid, + gp_work->operation_id, &gp_work->iws, + gp_work->mmus, &gp_ret); + mc_dev_devel("GP open session done, ret %d", ret); + for (i = 0; i < TEE_BUFFERS; i++) + if (gp_work->mmus[i]) + tee_mmu_put(gp_work->mmus[i]); + + /* Send return code */ + ring_get(xfe); + /* In */ + xfe->ring->dom0.operation_id = gp_work->operation_id; + xfe->ring->dom0.iws = gp_work->iws; + xfe->ring->dom0.gp_ret = gp_ret; + /* Call */ + call_domu(xfe, TEE_XEN_GP_OPEN_SESSION_DONE, gp_work->id, ret); + /* Out */ + ring_put(xfe); + kfree(gp_work); + tee_xfe_put(xfe); +} + +static inline int xen_be_gp_open_session(struct tee_xfe *xfe) +{ + struct gp_work *gp_work; + int i, ret = 0; + + gp_work = kzalloc(sizeof(*gp_work), GFP_KERNEL); + if (!gp_work) + return -ENOMEM; + + /* Map tmpref buffers */ + for (i = 0; i < TEE_BUFFERS; i++) { + struct tee_xen_buffer *buffer = &xfe->buffers[i]; + struct xen_be_map *map; + struct mcp_buffer_map b_map = { + .offset = buffer->info->offset, + .length = buffer->info->length, + .flags = buffer->info->flags, + }; + + if (!buffer->info->flags) + continue; + + map = xen_be_map_create(buffer, xfe->pte_entries_max, + xfe->xdev->otherend_id); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + goto err_map; + } + + /* Shall be freed by session */ + b_map.nr_pages = map->nr_pages; + gp_work->mmus[i] = tee_mmu_wrap(&map->deleter, map->pages, + &b_map); + if (IS_ERR(gp_work->mmus[i])) { + xen_be_map_delete(map); + ret = PTR_ERR(gp_work->mmus[i]); + goto err_mmus; + } + } + + tee_xfe_get(xfe); + gp_work->xfe = xfe; + gp_work->operation_id = xfe->ring->domu.operation_id; + gp_work->iws = xfe->ring->domu.iws; + gp_work->uuid = xfe->ring->domu.uuid; + gp_work->id = xfe->ring->domu.id; + INIT_WORK(&gp_work->work, xen_be_gp_open_session_worker); + schedule_work(&gp_work->work); + return 0; + +err_mmus: + for (i = 0; i < TEE_BUFFERS; i++) + if (!IS_ERR_OR_NULL(gp_work->mmus[i])) + tee_mmu_put(gp_work->mmus[i]); +err_map: + kfree(gp_work); + return ret; +} + +static void xen_be_gp_close_session_worker(struct work_struct *work) +{ + struct gp_work *gp_work = container_of(work, struct gp_work, work); + struct tee_xfe *xfe = gp_work->xfe; + int ret; + + ret = client_gp_close_session(xfe->client, gp_work->session_id); + mc_dev_devel("GP close session done, ret %d", ret); + + /* Send return code */ + ring_get(xfe); + /* In */ + xfe->ring->dom0.operation_id = gp_work->operation_id; + /* Call */ + call_domu(xfe, TEE_XEN_GP_CLOSE_SESSION_DONE, gp_work->id, ret); + /* Out */ + ring_put(xfe); + kfree(gp_work); + tee_xfe_put(xfe); +} + +static inline int xen_be_gp_close_session(struct tee_xfe *xfe) +{ + struct gp_work *gp_work; + + gp_work = kzalloc(sizeof(*gp_work), GFP_KERNEL); + if (!gp_work) + return -ENOMEM; + + tee_xfe_get(xfe); + gp_work->xfe = xfe; + gp_work->operation_id = xfe->ring->domu.operation_id; + gp_work->session_id = xfe->ring->domu.session_id; + gp_work->id = xfe->ring->domu.id; + INIT_WORK(&gp_work->work, xen_be_gp_close_session_worker); + schedule_work(&gp_work->work); + return 0; +} + +static void xen_be_gp_invoke_command_worker(struct work_struct *work) +{ + struct gp_work *gp_work = container_of(work, struct gp_work, work); + struct tee_xfe *xfe = gp_work->xfe; + struct gp_return gp_ret; + int i, ret; + + ret = client_gp_invoke_command_domu(xfe->client, gp_work->session_id, + gp_work->operation_id, + &gp_work->iws, gp_work->mmus, + &gp_ret); + mc_dev_devel("GP invoke command done, ret %d", ret); + for (i = 0; i < TEE_BUFFERS; i++) + if (gp_work->mmus[i]) + tee_mmu_put(gp_work->mmus[i]); + + /* Send return code */ + ring_get(xfe); + /* In */ + xfe->ring->dom0.operation_id = gp_work->operation_id; + xfe->ring->dom0.iws = gp_work->iws; + xfe->ring->dom0.gp_ret = gp_ret; + /* Call */ + call_domu(xfe, TEE_XEN_GP_INVOKE_COMMAND_DONE, gp_work->id, ret); + /* Out */ + ring_put(xfe); + kfree(gp_work); + tee_xfe_put(xfe); +} + +static inline int xen_be_gp_invoke_command(struct tee_xfe *xfe) +{ + struct gp_work *gp_work; + int i, ret = 0; + + gp_work = kzalloc(sizeof(*gp_work), GFP_KERNEL); + if (!gp_work) + return -ENOMEM; + + /* Map tmpref buffers */ + for (i = 0; i < TEE_BUFFERS; i++) { + struct tee_xen_buffer *buffer = &xfe->buffers[i]; + struct xen_be_map *map; + struct mcp_buffer_map b_map = { + .offset = buffer->info->offset, + .length = buffer->info->length, + .flags = buffer->info->flags, + }; + + if (!buffer->info->flags) + continue; + + map = xen_be_map_create(buffer, xfe->pte_entries_max, + xfe->xdev->otherend_id); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + goto err_map; + } + + /* Shall be freed by session */ + b_map.nr_pages = map->nr_pages; + gp_work->mmus[i] = tee_mmu_wrap(&map->deleter, map->pages, + &b_map); + if (IS_ERR(gp_work->mmus[i])) { + xen_be_map_delete(map); + ret = PTR_ERR(gp_work->mmus[i]); + goto err_mmus; + } + } + + tee_xfe_get(xfe); + gp_work->xfe = xfe; + gp_work->operation_id = xfe->ring->domu.operation_id; + gp_work->iws = xfe->ring->domu.iws; + gp_work->session_id = xfe->ring->domu.session_id; + gp_work->id = xfe->ring->domu.id; + INIT_WORK(&gp_work->work, xen_be_gp_invoke_command_worker); + schedule_work(&gp_work->work); + return 0; + +err_mmus: + for (i = 0; i < TEE_BUFFERS; i++) + if (!IS_ERR_OR_NULL(gp_work->mmus[i])) + tee_mmu_put(gp_work->mmus[i]); +err_map: + kfree(gp_work); + return ret; +} + +static inline int xen_be_gp_request_cancellation(struct tee_xfe *xfe) +{ + client_gp_request_cancellation(xfe->client, + xfe->ring->domu.operation_id); + return 0; +} + +static irqreturn_t xen_be_irq_handler_domu_th(int intr, void *arg) +{ + struct tee_xfe *xfe = arg; + + if (!xfe->ring->domu.cmd) { + mc_dev_devel("Ignore IRQ with no command (on DomU connect)"); + return IRQ_HANDLED; + } + + /* DomU event, their side of ring locked by them */ + schedule_work(&xfe->work); + + return IRQ_HANDLED; +} + +static void xen_be_irq_handler_domu_bh(struct work_struct *data) +{ + struct tee_xfe *xfe = container_of(data, struct tee_xfe, work); + + xfe->ring->domu.otherend_ret = -EINVAL; + mc_dev_devel("DomU -> Dom0 command %u id %u", + xfe->ring->domu.cmd, xfe->ring->domu.id); + switch (xfe->ring->domu.cmd) { + case TEE_XEN_DOMU_NONE: + return; + /* MC */ + case TEE_XEN_MC_HAS_SESSIONS: + xfe->ring->domu.otherend_ret = xen_be_mc_has_sessions(xfe); + break; + case TEE_XEN_GET_VERSION: + xfe->ring->domu.otherend_ret = xen_be_get_version(xfe); + break; + case TEE_XEN_MC_OPEN_SESSION: + xfe->ring->domu.otherend_ret = xen_be_mc_open_session(xfe); + break; + case TEE_XEN_MC_OPEN_TRUSTLET: + xfe->ring->domu.otherend_ret = xen_be_mc_open_trustlet(xfe); + break; + case TEE_XEN_MC_CLOSE_SESSION: + xfe->ring->domu.otherend_ret = xen_be_mc_close_session(xfe); + break; + case TEE_XEN_MC_NOTIFY: + xfe->ring->domu.otherend_ret = xen_be_mc_notify(xfe); + break; + case TEE_XEN_MC_WAIT: + xfe->ring->domu.otherend_ret = xen_be_mc_wait(xfe); + break; + case TEE_XEN_MC_MAP: + xfe->ring->domu.otherend_ret = xen_be_mc_map(xfe); + break; + case TEE_XEN_MC_UNMAP: + xfe->ring->domu.otherend_ret = xen_be_mc_unmap(xfe); + break; + case TEE_XEN_MC_GET_ERR: + xfe->ring->domu.otherend_ret = xen_be_mc_get_err(xfe); + break; + /* GP */ + case TEE_XEN_GP_REGISTER_SHARED_MEM: + xfe->ring->domu.otherend_ret = + xen_be_gp_register_shared_mem(xfe); + break; + case TEE_XEN_GP_RELEASE_SHARED_MEM: + xfe->ring->domu.otherend_ret = + xen_be_gp_release_shared_mem(xfe); + break; + case TEE_XEN_GP_OPEN_SESSION: + xfe->ring->domu.otherend_ret = xen_be_gp_open_session(xfe); + break; + case TEE_XEN_GP_CLOSE_SESSION: + xfe->ring->domu.otherend_ret = xen_be_gp_close_session(xfe); + break; + case TEE_XEN_GP_INVOKE_COMMAND: + xfe->ring->domu.otherend_ret = xen_be_gp_invoke_command(xfe); + break; + case TEE_XEN_GP_REQUEST_CANCELLATION: + xfe->ring->domu.otherend_ret = + xen_be_gp_request_cancellation(xfe); + break; + } + + mc_dev_devel("DomU -> Dom0 result %u id %u ret %d", + xfe->ring->domu.cmd, xfe->ring->domu.id, + xfe->ring->domu.otherend_ret); + notify_remote_via_irq(xfe->irq_domu); +} + +/* Device */ + +static const struct xenbus_device_id xen_be_ids[] = { + { "tee_xen" }, + { "" } +}; + +/* Called when a front-end is created */ +static int xen_be_probe(struct xenbus_device *xdev, + const struct xenbus_device_id *id) +{ + struct tee_xfe *xfe; + int ret = 0; + + ret = xenbus_switch_state(xdev, XenbusStateInitWait); + if (ret) { + xenbus_dev_fatal(xdev, ret, + "failed to change state to initwait"); + return ret; + } + + xfe = tee_xfe_create(xdev); + if (!xfe) { + ret = -ENOMEM; + xenbus_dev_fatal(xdev, ret, "failed to create FE struct"); + goto err_xfe_create; + } + + xfe->client = client_create(true); + if (!xfe->client) { + ret = -ENOMEM; + xenbus_dev_fatal(xdev, ret, "failed to create FE client"); + goto err_client_create; + } + + INIT_WORK(&xfe->work, xen_be_irq_handler_domu_bh); + + mutex_lock(&l_ctx.xfes_mutex); + list_add_tail(&xfe->list, &l_ctx.xfes); + mutex_unlock(&l_ctx.xfes_mutex); + + ret = xenbus_switch_state(xdev, XenbusStateInitialised); + if (ret) { + xenbus_dev_fatal(xdev, ret, + "failed to change state to initialised"); + goto err_switch_state; + } + + return 0; + +err_switch_state: + mutex_lock(&l_ctx.xfes_mutex); + list_del(&xfe->list); + mutex_unlock(&l_ctx.xfes_mutex); +err_client_create: + tee_xfe_put(xfe); +err_xfe_create: + return ret; +} + +/* Called when device is unregistered */ +static int xen_be_remove(struct xenbus_device *xdev) +{ + struct tee_xfe *xfe = dev_get_drvdata(&xdev->dev); + + xenbus_switch_state(xdev, XenbusStateClosed); + + mutex_lock(&l_ctx.xfes_mutex); + list_del(&xfe->list); + mutex_unlock(&l_ctx.xfes_mutex); + + tee_xfe_put(xfe); + return 0; +} + +static inline int xen_be_map_ring_valloc(struct xenbus_device *dev, + grant_ref_t ref, void **vaddr) +{ +#if KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE || \ + defined(MC_XENBUS_MAP_RING_VALLOC_4_1) + return xenbus_map_ring_valloc(dev, &ref, 1, vaddr); +#else + return xenbus_map_ring_valloc(dev, ref, vaddr); +#endif +} + +static inline void frontend_attach(struct tee_xfe *xfe) +{ + int domu_version; + int ret; + int i; + + if (xenbus_read_driver_state(xfe->xdev->nodename) != + XenbusStateInitialised) + return; + + ret = xenbus_gather(XBT_NIL, xfe->xdev->otherend, + "ring-ref", "%u", &xfe->ring_ref, + "pte-entries-max", "%u", &xfe->pte_entries_max, + "event-channel-domu", "%u", &xfe->evtchn_domu, + "event-channel-dom0", "%u", &xfe->evtchn_dom0, + "domu-version", "%u", &domu_version, NULL); + if (ret) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to gather other domain info"); + return; + } + + mc_dev_devel("ring ref %u evtchn domu=%u dom0=%u version=%u", + xfe->ring_ref, xfe->evtchn_domu, xfe->evtchn_dom0, + domu_version); + + if (domu_version != TEE_XEN_VERSION) { + xenbus_dev_fatal( + xfe->xdev, ret, + "front- and back-end versions do not match: %d vs %d", + domu_version, TEE_XEN_VERSION); + return; + } + + ret = xen_be_map_ring_valloc(xfe->xdev, xfe->ring_ref, &xfe->ring_p); + if (ret < 0) { + xenbus_dev_fatal(xfe->xdev, ret, "failed to map ring"); + return; + } + mc_dev_devel("mapped ring %p", xfe->ring_p); + + /* Map buffers individually */ + for (i = 0; i < TEE_BUFFERS; i++) { + ret = xen_be_map_ring_valloc(xfe->xdev, + xfe->ring->domu.buffers[i].pmd_ref, + &xfe->buffers[i].data.addr); + if (ret < 0) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to map buffer page"); + return; + } + + xfe->buffers[i].info = &xfe->ring->domu.buffers[i]; + } +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE + ret = bind_interdomain_evtchn_to_irqhandler_lateeoi( + xfe->xdev->otherend_id, xfe->evtchn_domu, + xen_be_irq_handler_domu_th, 0, "tee_be_domu", xfe); +#else + ret = bind_interdomain_evtchn_to_irqhandler( + xfe->xdev->otherend_id, xfe->evtchn_domu, + xen_be_irq_handler_domu_th, 0, "tee_be_domu", xfe); +#endif + if (ret < 0) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to bind event channel to DomU IRQ"); + return; + } + + xfe->irq_domu = ret; + mc_dev_devel("bound DomU IRQ %d", xfe->irq_domu); + +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE + ret = bind_interdomain_evtchn_to_irqhandler_lateeoi( + xfe->xdev->otherend_id, xfe->evtchn_dom0, + xen_be_irq_handler_dom0_th, 0, "tee_be_dom0", xfe); +#else + ret = bind_interdomain_evtchn_to_irqhandler( + xfe->xdev->otherend_id, xfe->evtchn_dom0, + xen_be_irq_handler_dom0_th, 0, "tee_be_dom0", xfe); +#endif + if (ret < 0) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to bind event channel to Dom0 IRQ"); + return; + } + + xfe->irq_dom0 = ret; + mc_dev_devel("bound Dom0 IRQ %d", xfe->irq_dom0); + + ret = xenbus_switch_state(xfe->xdev, XenbusStateConnected); + if (ret) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to change state to connected"); + return; + } +} + +static inline void frontend_detach(struct tee_xfe *xfe) +{ + int i; + + xenbus_switch_state(xfe->xdev, XenbusStateClosing); + if (xfe->irq_domu >= 0) + unbind_from_irqhandler(xfe->irq_domu, xfe); + + if (xfe->irq_dom0 >= 0) + unbind_from_irqhandler(xfe->irq_dom0, xfe); + + for (i = 0; i < TEE_BUFFERS; i++) + xenbus_unmap_ring_vfree(xfe->xdev, xfe->buffers[i].data.addr); + + if (xfe->ring_p) + xenbus_unmap_ring_vfree(xfe->xdev, xfe->ring_p); +} + +static void xen_be_frontend_changed(struct xenbus_device *xdev, + enum xenbus_state fe_state) +{ + struct tee_xfe *xfe = dev_get_drvdata(&xdev->dev); + + mc_dev_devel("fe state changed to %d", fe_state); + switch (fe_state) { + case XenbusStateInitialising: + case XenbusStateInitWait: + break; + case XenbusStateInitialised: + frontend_attach(xfe); + break; + case XenbusStateConnected: + break; + case XenbusStateClosing: + frontend_detach(xfe); + break; + case XenbusStateUnknown: + case XenbusStateClosed: + device_unregister(&xfe->xdev->dev); + break; + case XenbusStateReconfiguring: + case XenbusStateReconfigured: + break; + } +} + +static struct xenbus_driver xen_be_driver = { + .ids = xen_be_ids, + .probe = xen_be_probe, + .remove = xen_be_remove, + .otherend_changed = xen_be_frontend_changed, +}; + +int xen_be_init(void) +{ + INIT_LIST_HEAD(&l_ctx.xfes); + mutex_init(&l_ctx.xfes_mutex); + return xenbus_register_backend(&xen_be_driver); +} + +void xen_be_exit(void) +{ + xenbus_unregister_driver(&xen_be_driver); +} + +#endif diff --git a/drivers/gud/MobiCoreDriver/xen_be.h b/drivers/gud/MobiCoreDriver/xen_be.h new file mode 100644 index 000000000000..8e5cdf66d3c7 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/xen_be.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef _MC_XEN_BE_H_ +#define _MC_XEN_BE_H_ + +#include + +struct xen_be_map; + +#ifdef CONFIG_XEN +int xen_be_init(void); +void xen_be_exit(void); +#else +static inline int xen_be_init(void) +{ + return 0; +} + +static inline void xen_be_exit(void) +{ +} +#endif + +#endif /* _MC_XEN_BE_H_ */ diff --git a/drivers/gud/MobiCoreDriver/xen_common.c b/drivers/gud/MobiCoreDriver/xen_common.c new file mode 100644 index 000000000000..80b56f0f627c --- /dev/null +++ b/drivers/gud/MobiCoreDriver/xen_common.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifdef CONFIG_XEN + +#include "main.h" +#include "client.h" +#include "xen_common.h" + +struct tee_xfe *tee_xfe_create(struct xenbus_device *xdev) +{ + struct tee_xfe *xfe; + + /* Alloc */ + xfe = kzalloc(sizeof(*xfe), GFP_KERNEL); + if (!xfe) + return NULL; + + atomic_inc(&g_ctx.c_xen_fes); + /* Init */ + dev_set_drvdata(&xdev->dev, xfe); + xfe->xdev = xdev; + kref_init(&xfe->kref); + xfe->evtchn_domu = -1; + xfe->evtchn_dom0 = -1; + xfe->irq_domu = -1; + xfe->irq_dom0 = -1; + INIT_LIST_HEAD(&xfe->list); + mutex_init(&xfe->ring_mutex); + init_completion(&xfe->ring_completion); + return xfe; +} + +static void tee_xfe_release(struct kref *kref) +{ + struct tee_xfe *xfe = container_of(kref, struct tee_xfe, kref); + + if (xfe->client) + client_close(xfe->client); + + kfree(xfe); + atomic_dec(&g_ctx.c_xen_fes); +} + +void tee_xfe_put(struct tee_xfe *xfe) +{ + kref_put(&xfe->kref, tee_xfe_release); +} + +#endif diff --git a/drivers/gud/MobiCoreDriver/xen_common.h b/drivers/gud/MobiCoreDriver/xen_common.h new file mode 100644 index 000000000000..215c264eaa54 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/xen_common.h @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef _MC_XEN_COMMON_H_ +#define _MC_XEN_COMMON_H_ + +#include +#include +#include +#include +#include +#include + +#include "mc_user.h" /* many types */ +#include "mciiwp.h" +#include "mcimcp.h" +#include "mmu.h" /* PMD/PTE max entries */ +#include "client.h" /* For BE to treat other VMs as clients */ + +#define TEE_XEN_VERSION 3 + +#define TEE_BUFFERS 4 + +enum tee_xen_domu_cmd { + TEE_XEN_DOMU_NONE, + TEE_XEN_GET_VERSION, + /* TEE_XEN_MC_OPEN_DEVICE = 11, SWd does not support this */ + /* TEE_XEN_MC_CLOSE_DEVICE, SWd does not support this */ + TEE_XEN_MC_HAS_SESSIONS = 13, + TEE_XEN_MC_OPEN_SESSION, + TEE_XEN_MC_OPEN_TRUSTLET, + TEE_XEN_MC_CLOSE_SESSION, + TEE_XEN_MC_NOTIFY, + TEE_XEN_MC_WAIT, + TEE_XEN_MC_MAP, + TEE_XEN_MC_UNMAP, + TEE_XEN_MC_GET_ERR, + /* TEE_XEN_GP_INITIALIZE_CONTEXT = 21, SWd does not support this */ + /* TEE_XEN_GP_FINALIZE_CONTEXT, SWd does not support this */ + TEE_XEN_GP_REGISTER_SHARED_MEM = 23, + TEE_XEN_GP_RELEASE_SHARED_MEM, + TEE_XEN_GP_OPEN_SESSION, + TEE_XEN_GP_CLOSE_SESSION, + TEE_XEN_GP_INVOKE_COMMAND, + TEE_XEN_GP_REQUEST_CANCELLATION, +}; + +enum tee_xen_dom0_cmd { + TEE_XEN_DOM0_NONE, + TEE_XEN_MC_WAIT_DONE = TEE_XEN_MC_WAIT, + TEE_XEN_GP_OPEN_SESSION_DONE = TEE_XEN_GP_OPEN_SESSION, + TEE_XEN_GP_CLOSE_SESSION_DONE = TEE_XEN_GP_CLOSE_SESSION, + TEE_XEN_GP_INVOKE_COMMAND_DONE = TEE_XEN_GP_INVOKE_COMMAND, +}; + +union tee_xen_mmu_table { + /* Array of references to pages (PTE_ENTRIES_MAX or PMD_ENTRIES_MAX) */ + grant_ref_t *refs; + /* Address of table */ + void *addr; + /* Page for table */ + unsigned long page; +}; + +struct tee_xen_buffer_info { + /* Page Middle Directory, refs to tee_xen_pte_table's (full pages) */ + grant_ref_t pmd_ref; + /* Total number of refs for buffer */ + u32 nr_refs; + u64 addr; /* Unique VM address */ + u32 offset; + u32 length; + u32 flags; + u32 sva; +}; + +/* Convenience structure to get buffer info and contents in one place */ +struct tee_xen_buffer { + struct tee_xen_buffer_info *info; + union tee_xen_mmu_table data; +}; + +struct tee_xen_ring { + /* DomU side, synchronous and asynchronous commands */ + struct { + enum tee_xen_domu_cmd cmd; /* in */ + u32 id; /* in (debug) */ + /* Return code of this command from Dom0 */ + int otherend_ret; /* out */ + struct mc_uuid_t uuid; /* in */ + u32 session_id; /* in/out */ + /* Buffers to share (4 for GP, 2 for mcOpenTrustlet) */ + struct tee_xen_buffer_info buffers[TEE_BUFFERS]; /* in */ + /* MC */ + struct mc_version_info version_info; /* out */ + u32 spid; /* in */ + s32 timeout; /* in */ + s32 err; /* out */ + /* GP */ + u64 operation_id; /* in */ + struct gp_return gp_ret; /* out */ + struct interworld_session iws; /* in */ + } domu; + + /* Dom0 side, response to asynchronous command, never read by Dom0 */ + struct { + enum tee_xen_dom0_cmd cmd; /* in */ + u32 id; /* in (debug) */ + /* Return code from command */ + int cmd_ret; /* in */ + /* The operation id is used to match GP request and response */ + u64 operation_id; /* in */ + struct gp_return gp_ret; /* in */ + struct interworld_session iws; /* in */ + /* The session id is used to match MC request and response */ + u32 session_id; /* in */ + } dom0; +}; + +struct tee_xfe { + struct xenbus_device *xdev; + struct kref kref; + grant_ref_t ring_ref; + int pte_entries_max; + int evtchn_domu; + int evtchn_dom0; + int irq_domu; + int irq_dom0; + struct list_head list; + struct tee_client *client; + struct work_struct work; + /* Ring page */ + union { + unsigned long ring_ul; + void *ring_p; + struct tee_xen_ring *ring; + }; + /* Buffer pages */ + struct tee_xen_buffer buffers[TEE_BUFFERS]; + struct mutex ring_mutex; /* Protect our side of ring */ + struct completion ring_completion; + int ring_busy; + /* Unique ID for commands */ + u32 domu_cmd_id; +}; + +struct tee_xfe *tee_xfe_create(struct xenbus_device *xdev); +static inline void tee_xfe_get(struct tee_xfe *xfe) +{ + kref_get(&xfe->kref); +} + +void tee_xfe_put(struct tee_xfe *xfe); + +static inline void ring_get(struct tee_xfe *xfe) +{ + mutex_lock(&xfe->ring_mutex); + xfe->ring_busy = true; +} + +static inline void ring_put(struct tee_xfe *xfe) +{ + xfe->ring_busy = false; + mutex_unlock(&xfe->ring_mutex); +} + +#endif /* _MC_XEN_COMMON_H_ */ diff --git a/drivers/gud/MobiCoreDriver/xen_fe.c b/drivers/gud/MobiCoreDriver/xen_fe.c new file mode 100644 index 000000000000..f60a345a2d64 --- /dev/null +++ b/drivers/gud/MobiCoreDriver/xen_fe.c @@ -0,0 +1,1194 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#include + +#include "mciiwp.h" /* struct interworld_session */ + +#include "main.h" + +#ifdef TRUSTONIC_XEN_DOMU + +#include "admin.h" /* tee_object* */ +#include "client.h" +#include "iwp.h" +#include "mcp.h" +#include "xen_common.h" +#include "xen_fe.h" + +#define page_to_gfn(p) (pfn_to_gfn(page_to_phys(p) >> XEN_PAGE_SHIFT)) + +static struct { + int (*probe)(void); + int (*start)(void); + struct tee_xfe *xfe; + /* MC sessions */ + struct mutex mc_sessions_lock; + struct list_head mc_sessions; + /* GP operations */ + struct mutex gp_operations_lock; + struct list_head gp_operations; + /* Last back-end state, + * to overcome an issue in some Xen implementations + */ + int last_be_state; +} l_ctx; + +struct xen_fe_mc_session { + struct list_head list; + struct completion completion; + int ret; + struct mcp_session *session; +}; + +struct xen_fe_gp_operation { + struct list_head list; + struct completion completion; + int ret; + u64 slot; + struct gp_return *gp_ret; + struct interworld_session *iws; +}; + +static inline struct xen_fe_mc_session *find_mc_session(u32 session_id) +{ + struct xen_fe_mc_session *session = ERR_PTR(-ENXIO), *candidate; + + mutex_lock(&l_ctx.mc_sessions_lock); + list_for_each_entry(candidate, &l_ctx.mc_sessions, list) { + struct mcp_session *mcp_session = candidate->session; + + if (mcp_session->sid == session_id) { + session = candidate; + break; + } + } + mutex_unlock(&l_ctx.mc_sessions_lock); + + WARN(IS_ERR(session), "MC session not found for ID %u", session_id); + return session; +} + +static inline int xen_fe_mc_wait_done(struct tee_xfe *xfe) +{ + struct xen_fe_mc_session *session; + + mc_dev_devel("received response to mc_wait for session %x: %d", + xfe->ring->dom0.session_id, xfe->ring->dom0.cmd_ret); + session = find_mc_session(xfe->ring->dom0.session_id); + if (IS_ERR(session)) + return PTR_ERR(session); + + session->ret = xfe->ring->dom0.cmd_ret; + complete(&session->completion); + return 0; +} + +static struct xen_fe_gp_operation *find_gp_operation(u64 operation_id) +{ + struct xen_fe_gp_operation *operation = ERR_PTR(-ENXIO), *candidate; + + mutex_lock(&l_ctx.gp_operations_lock); + list_for_each_entry(candidate, &l_ctx.gp_operations, list) { + if (candidate->slot == operation_id) { + operation = candidate; + list_del(&operation->list); + break; + } + } + mutex_unlock(&l_ctx.gp_operations_lock); + + WARN(IS_ERR(operation), "GP operation not found for op id %llx", + operation_id); + return operation; +} + +static inline int xen_fe_gp_open_session_done(struct tee_xfe *xfe) +{ + struct xen_fe_gp_operation *operation; + + mc_dev_devel("received response to gp_open_session for op id %llx", + xfe->ring->dom0.operation_id); + operation = find_gp_operation(xfe->ring->dom0.operation_id); + if (IS_ERR(operation)) + return PTR_ERR(operation); + + operation->ret = xfe->ring->dom0.cmd_ret; + *operation->iws = xfe->ring->dom0.iws; + *operation->gp_ret = xfe->ring->dom0.gp_ret; + complete(&operation->completion); + return 0; +} + +static inline int xen_fe_gp_close_session_done(struct tee_xfe *xfe) +{ + struct xen_fe_gp_operation *operation; + + mc_dev_devel("received response to gp_close_session for op id %llx", + xfe->ring->dom0.operation_id); + operation = find_gp_operation(xfe->ring->dom0.operation_id); + if (IS_ERR(operation)) + return PTR_ERR(operation); + + operation->ret = xfe->ring->dom0.cmd_ret; + complete(&operation->completion); + return 0; +} + +static inline int xen_fe_gp_invoke_command_done(struct tee_xfe *xfe) +{ + struct xen_fe_gp_operation *operation; + + mc_dev_devel("received response to gp_invoke_command for op id %llx", + xfe->ring->dom0.operation_id); + operation = find_gp_operation(xfe->ring->dom0.operation_id); + if (IS_ERR(operation)) + return PTR_ERR(operation); + + operation->ret = xfe->ring->dom0.cmd_ret; + *operation->iws = xfe->ring->dom0.iws; + *operation->gp_ret = xfe->ring->dom0.gp_ret; + complete(&operation->completion); + return 0; +} + +static irqreturn_t xen_fe_irq_handler_dom0_th(int intr, void *arg) +{ + struct tee_xfe *xfe = arg; + + /* Dom0 event, their side of ring locked by them */ + schedule_work(&xfe->work); + + return IRQ_HANDLED; +} + +static void xen_fe_irq_handler_dom0_bh(struct work_struct *data) +{ + struct tee_xfe *xfe = container_of(data, struct tee_xfe, work); + int ret = -EINVAL; + + mc_dev_devel("Dom0 -> DomU command %u id %u cmd ret %d", + xfe->ring->dom0.cmd, xfe->ring->dom0.id, + xfe->ring->dom0.cmd_ret); + switch (xfe->ring->dom0.cmd) { + case TEE_XEN_DOM0_NONE: + return; + case TEE_XEN_MC_WAIT_DONE: + ret = xen_fe_mc_wait_done(xfe); + break; + case TEE_XEN_GP_OPEN_SESSION_DONE: + ret = xen_fe_gp_open_session_done(xfe); + break; + case TEE_XEN_GP_CLOSE_SESSION_DONE: + ret = xen_fe_gp_close_session_done(xfe); + break; + case TEE_XEN_GP_INVOKE_COMMAND_DONE: + ret = xen_fe_gp_invoke_command_done(xfe); + break; + } + + if (ret) + mc_dev_err(ret, "Dom0 -> DomU result %u id %u", + xfe->ring->dom0.cmd, xfe->ring->dom0.id); + else + mc_dev_devel("Dom0 -> DomU result %u id %u", + xfe->ring->dom0.cmd, xfe->ring->dom0.id); + + notify_remote_via_evtchn(xfe->evtchn_dom0); +} + +/* Buffer management */ + +struct xen_fe_map { + /* Array of PTE tables, so we can release the associated buffer refs */ + union tee_xen_mmu_table *pte_tables; + int nr_pte_tables; + int nr_refs; + bool readonly; + int pages_created; /* Leak check */ + int refs_granted; /* Leak check */ + /* To auto-delete */ + struct tee_deleter deleter; +}; + +static void xen_fe_map_release_pmd(struct xen_fe_map *map, + const struct tee_xen_buffer *buffer) +{ + int i; + + if (IS_ERR_OR_NULL(map)) + return; + + for (i = 0; i < map->nr_pte_tables; i++) { + gnttab_end_foreign_access(buffer->data.refs[i], true, 0); + map->refs_granted--; + mc_dev_devel("unmapped table %d ref %u", + i, buffer->data.refs[i]); + } +} + +static void xen_fe_map_release(struct xen_fe_map *map, + const struct tee_xen_buffer *buffer) +{ + int nr_refs_left = map->nr_refs; + int i; + + if (buffer) + xen_fe_map_release_pmd(map, buffer); + + for (i = 0; i < map->nr_pte_tables; i++) { + int j, nr_refs = nr_refs_left; + + if (nr_refs > PTE_ENTRIES_MAX) + nr_refs = PTE_ENTRIES_MAX; + + for (j = 0; j < nr_refs; j++) { + gnttab_end_foreign_access(map->pte_tables[i].refs[j], + map->readonly, 0); + map->refs_granted--; + nr_refs_left--; + mc_dev_devel("unmapped [%d, %d] ref %u, left %d", + i, j, map->pte_tables[i].refs[j], + nr_refs_left); + } + + free_page(map->pte_tables[i].page); + map->pages_created--; + } + + kfree(map->pte_tables); + if (map->pages_created || map->refs_granted) + mc_dev_err(-EUCLEAN, + "leak detected: still in use %d, still ref'd %d", + map->pages_created, map->refs_granted); + + kfree(map); + atomic_dec(&g_ctx.c_xen_maps); + mc_dev_devel("freed map %p: refs=%u nr_pte_tables=%d", + map, map->nr_refs, map->nr_pte_tables); +} + +static void xen_fe_map_delete(void *arg) +{ + struct xen_fe_map *map = arg; + + xen_fe_map_release(map, NULL); +} + +static struct xen_fe_map *xen_fe_map_create(struct tee_xen_buffer *buffer, + const struct mcp_buffer_map *b_map, + int dom_id) +{ + /* b_map describes the PMD which contains pointers to PTE tables */ + uintptr_t *pte_tables = (uintptr_t *)(uintptr_t)b_map->addr; + struct xen_fe_map *map; + unsigned long nr_pte_tables = + (b_map->nr_pages + PTE_ENTRIES_MAX - 1) / PTE_ENTRIES_MAX; + unsigned long nr_pages_left = b_map->nr_pages; + int readonly = !(b_map->flags & MC_IO_MAP_OUTPUT); + int ret, i; + + /* + * We always map the same way, to simplify: + * * the buffer contains references to PTE pages + * * PTE pages contain references to the buffer pages + */ + map = kzalloc(sizeof(*map), GFP_KERNEL); + if (!map) + return ERR_PTR(-ENOMEM); + + atomic_inc(&g_ctx.c_xen_maps); + map->readonly = readonly; + + map->pte_tables = kcalloc(nr_pte_tables, + sizeof(union tee_xen_mmu_table), GFP_KERNEL); + if (!map->pte_tables) { + ret = -ENOMEM; + goto err; + } + + for (i = 0; i < nr_pte_tables; i++) { + /* As expected, PTE tables contain pointers to buffer pages */ + struct page **pages = (struct page **)pte_tables[i]; + unsigned long nr_pages = nr_pages_left; + int j; + + map->pte_tables[i].page = get_zeroed_page(GFP_KERNEL); + if (!map->pte_tables[i].page) { + ret = -ENOMEM; + goto err; + } + map->pages_created++; + map->nr_pte_tables++; + + if (nr_pages > PTE_ENTRIES_MAX) + nr_pages = PTE_ENTRIES_MAX; + + /* Create ref for this PTE table */ + ret = gnttab_grant_foreign_access( + dom_id, virt_to_gfn(map->pte_tables[i].addr), true); + if (ret < 0) { + mc_dev_err( + ret, + "gnttab_grant_foreign_access failed:\t" + "PTE table %d", i); + goto err; + } + + map->refs_granted++; + buffer->data.refs[i] = ret; + mc_dev_devel("mapped table %d ref %u for %lu pages", + i, buffer->data.refs[i], nr_pages); + + /* Create refs for pages */ + for (j = 0; j < nr_pages; j++) { + ret = gnttab_grant_foreign_access( + dom_id, page_to_gfn(pages[j]), readonly); + if (ret < 0) { + mc_dev_err( + ret, + "gnttab_grant_foreign_access failed:\t" + "PTE %d pg %d", i, j); + goto err; + } + + map->refs_granted++; + map->pte_tables[i].refs[j] = ret; + map->nr_refs++; + nr_pages_left--; + mc_dev_devel("mapped [%d, %d] ref %u, left %lu", + i, j, map->pte_tables[i].refs[j], + nr_pages_left); + } + } + + buffer->info->nr_refs = map->nr_refs; + buffer->info->addr = (uintptr_t)b_map->mmu; + buffer->info->offset = b_map->offset; + buffer->info->length = b_map->length; + buffer->info->flags = b_map->flags; + + /* Auto-delete */ + map->deleter.object = map; + map->deleter.delete = xen_fe_map_delete; + tee_mmu_set_deleter(b_map->mmu, &map->deleter); + + mc_dev_devel("created map %p: refs=%u nr_pte_tables=%d", + map, map->nr_refs, map->nr_pte_tables); + return map; + +err: + xen_fe_map_release(map, buffer); + return ERR_PTR(ret); +} + +/* DomU call to Dom0 */ + +/* Must be called under xfe->ring_mutex */ +static inline void call_dom0(struct tee_xfe *xfe, enum tee_xen_domu_cmd cmd) +{ + WARN_ON(!xfe->ring_busy); + + xfe->domu_cmd_id++; + if (!xfe->domu_cmd_id) + xfe->domu_cmd_id++; + + /* Set command and ID */ + xfe->ring->domu.cmd = cmd; + xfe->ring->domu.id = xfe->domu_cmd_id; + mc_dev_devel("DomU -> Dom0 request %u id %u pid %d", + xfe->ring->domu.cmd, xfe->ring->domu.id, current->pid); + /* Call */ + notify_remote_via_evtchn(xfe->evtchn_domu); + wait_for_completion(&xfe->ring_completion); +} + +/* Will be called back under xfe->ring_mutex */ +static irqreturn_t xen_fe_irq_handler_domu_th(int intr, void *arg) +{ + struct tee_xfe *xfe = arg; + + WARN_ON(!xfe->ring_busy); + + /* Response to a domU command, our side of ring locked by us */ + mc_dev_devel("DomU -> Dom0 response %u id %u ret %d", + xfe->ring->domu.cmd, xfe->ring->domu.id, + xfe->ring->domu.otherend_ret); + xfe->ring->domu.cmd = TEE_XEN_DOMU_NONE; + xfe->ring->domu.id = 0; + complete(&xfe->ring_completion); + + return IRQ_HANDLED; +} + +/* MC protocol interface */ + +int xen_mc_get_version(struct mc_version_info *version_info) +{ + struct tee_xfe *xfe = l_ctx.xfe; + + ring_get(xfe); + /* Call */ + call_dom0(xfe, TEE_XEN_GET_VERSION); + /* Out */ + memcpy(version_info, &xfe->ring->domu.version_info, + sizeof(*version_info)); + ring_put(xfe); + return xfe->ring->domu.otherend_ret; +} + +int xen_mc_open_session(struct mcp_session *session, + struct mcp_open_info *info) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct xen_fe_mc_session *fe_mc_session; + struct tee_xen_buffer *ta_buffer = &xfe->buffers[1]; + struct tee_xen_buffer *tci_buffer = &xfe->buffers[0]; + struct xen_fe_map *ta_map = NULL; + struct xen_fe_map *tci_map = NULL; + struct tee_mmu *mmu = NULL; + enum tee_xen_domu_cmd cmd; + int ret; + + fe_mc_session = kzalloc(sizeof(*fe_mc_session), GFP_KERNEL); + if (!fe_mc_session) + return -ENOMEM; + + INIT_LIST_HEAD(&fe_mc_session->list); + init_completion(&fe_mc_session->completion); + fe_mc_session->session = session; + + ring_get(xfe); + /* In */ + if (info->type == TEE_MC_UUID) { + cmd = TEE_XEN_MC_OPEN_SESSION; + xfe->ring->domu.uuid = *info->uuid; + } else { + struct mc_ioctl_buffer buf = { + .va = info->va, + .len = info->len, + .flags = MC_IO_MAP_INPUT, + }; + struct mcp_buffer_map b_map; + + cmd = TEE_XEN_MC_OPEN_TRUSTLET; + /* Use an otherwise unused field to pass the SPID */ + xfe->ring->domu.spid = info->spid; + mmu = tee_mmu_create(info->user ? current->mm : NULL, &buf); + if (IS_ERR(mmu)) { + ret = PTR_ERR(mmu); + mmu = NULL; + goto out; + } + + tee_mmu_buffer(mmu, &b_map); + ta_map = xen_fe_map_create(ta_buffer, &b_map, + xfe->xdev->otherend_id); + if (IS_ERR(ta_map)) { + ret = PTR_ERR(ta_map); + goto out; + } + } + + /* Convert IPAs to grant references in-place */ + if (info->tci_mmu) { + struct mcp_buffer_map b_map; + + tee_mmu_buffer(info->tci_mmu, &b_map); + tci_map = xen_fe_map_create(tci_buffer, &b_map, + xfe->xdev->otherend_id); + if (IS_ERR(tci_map)) { + ret = PTR_ERR(tci_map); + goto out; + } + } else { + tci_buffer->info->flags = 0; + } + + /* Call */ + call_dom0(xfe, cmd); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + if (!ret) + session->sid = xfe->ring->domu.session_id; + +out: + if (!ret) { + mutex_lock(&l_ctx.mc_sessions_lock); + list_add_tail(&fe_mc_session->list, &l_ctx.mc_sessions); + mutex_unlock(&l_ctx.mc_sessions_lock); + } else { + kfree(fe_mc_session); + } + + xen_fe_map_release_pmd(ta_map, ta_buffer); + xen_fe_map_release_pmd(tci_map, tci_buffer); + if (mmu) + tee_mmu_put(mmu); + + ring_put(xfe); + return ret; +} + +int xen_mc_close_session(struct mcp_session *session) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct xen_fe_mc_session *fe_mc_session; + int ret; + + fe_mc_session = find_mc_session(session->sid); + if (!fe_mc_session) + return -ENXIO; + + ring_get(xfe); + /* In */ + xfe->ring->domu.session_id = session->sid; + /* Call */ + call_dom0(xfe, TEE_XEN_MC_CLOSE_SESSION); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + if (!ret) { + mutex_lock(&l_ctx.mc_sessions_lock); + session->state = MCP_SESSION_CLOSED; + list_del(&fe_mc_session->list); + mutex_unlock(&l_ctx.mc_sessions_lock); + kfree(fe_mc_session); + } + + ring_put(xfe); + return ret; +} + +int xen_mc_notify(struct mcp_session *session) +{ + struct tee_xfe *xfe = l_ctx.xfe; + int ret; + + mc_dev_devel("MC notify session %x", session->sid); + ring_get(xfe); + /* In */ + xfe->ring->domu.session_id = session->sid; + /* Call */ + call_dom0(xfe, TEE_XEN_MC_NOTIFY); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + ring_put(xfe); + return ret; +} + +int xen_mc_wait(struct mcp_session *session, s32 timeout, bool silent_expiry) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct xen_fe_mc_session *fe_mc_session; + int ret; + + /* Locked by caller so no two waits can happen on one session */ + fe_mc_session = find_mc_session(session->sid); + if (!fe_mc_session) + return -ENXIO; + + fe_mc_session->ret = 0; + + mc_dev_devel("MC wait session %x", session->sid); + ring_get(xfe); + /* In */ + xfe->ring->domu.session_id = session->sid; + xfe->ring->domu.timeout = timeout; + /* Call */ + call_dom0(xfe, TEE_XEN_MC_WAIT); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + ring_put(xfe); + + if (ret) + return ret; + + /* Now wait for notification from Dom0 */ + ret = wait_for_completion_interruptible(&fe_mc_session->completion); + if (!ret) + ret = fe_mc_session->ret; + + return ret; +} + +int xen_mc_map(u32 session_id, struct tee_mmu *mmu, u32 *sva) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct tee_xen_buffer *buffer = &xfe->buffers[0]; + struct mcp_buffer_map b_map; + struct xen_fe_map *map = NULL; + int ret; + + ring_get(xfe); + /* In */ + xfe->ring->domu.session_id = session_id; + tee_mmu_buffer(mmu, &b_map); + map = xen_fe_map_create(buffer, &b_map, xfe->xdev->otherend_id); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + goto out; + } + + /* Call */ + call_dom0(xfe, TEE_XEN_MC_MAP); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + if (!ret) { + *sva = buffer->info->sva; + atomic_inc(&g_ctx.c_maps); + } + +out: + xen_fe_map_release_pmd(map, buffer); + ring_put(xfe); + return ret; +} + +int xen_mc_unmap(u32 session_id, const struct mcp_buffer_map *map) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct tee_xen_buffer *buffer = &xfe->buffers[0]; + int ret; + + ring_get(xfe); + /* In */ + xfe->ring->domu.session_id = session_id; + buffer->info->length = map->length; + buffer->info->sva = map->secure_va; + /* Call */ + call_dom0(xfe, TEE_XEN_MC_UNMAP); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + if (!ret) + atomic_dec(&g_ctx.c_maps); + + ring_put(xfe); + return ret; +} + +int xen_mc_get_err(struct mcp_session *session, s32 *err) +{ + struct tee_xfe *xfe = l_ctx.xfe; + int ret; + + ring_get(xfe); + /* In */ + xfe->ring->domu.session_id = session->sid; + /* Call */ + call_dom0(xfe, TEE_XEN_MC_GET_ERR); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + if (!ret) + *err = xfe->ring->domu.err; + + mc_dev_devel("MC get_err session %x err %d", session->sid, *err); + ring_put(xfe); + return ret; +} + +/* GP protocol interface */ + +int xen_gp_register_shared_mem(struct tee_mmu *mmu, u32 *sva, + struct gp_return *gp_ret) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct tee_xen_buffer *buffer = &xfe->buffers[0]; + struct mcp_buffer_map b_map; + struct xen_fe_map *map = NULL; + int ret; + + ring_get(xfe); + /* In */ + tee_mmu_buffer(mmu, &b_map); + map = xen_fe_map_create(buffer, &b_map, xfe->xdev->otherend_id); + if (IS_ERR(map)) { + ret = PTR_ERR(map); + goto out; + } + + /* Call */ + call_dom0(xfe, TEE_XEN_GP_REGISTER_SHARED_MEM); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + if (!ret) { + *sva = buffer->info->sva; + atomic_inc(&g_ctx.c_maps); + } + + if (xfe->ring->domu.gp_ret.origin) + *gp_ret = xfe->ring->domu.gp_ret; + +out: + xen_fe_map_release_pmd(map, buffer); + ring_put(xfe); + return ret; +} + +int xen_gp_release_shared_mem(struct mcp_buffer_map *map) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct tee_xen_buffer *buffer = &xfe->buffers[0]; + int ret; + + ring_get(xfe); + /* In */ + buffer->info->addr = (uintptr_t)map->mmu; + buffer->info->length = map->length; + buffer->info->flags = map->flags; + buffer->info->sva = map->secure_va; + /* Call */ + call_dom0(xfe, TEE_XEN_GP_RELEASE_SHARED_MEM); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + if (!ret) + atomic_dec(&g_ctx.c_maps); + + ring_put(xfe); + return ret; +} + +int xen_gp_open_session(struct iwp_session *session, + const struct mc_uuid_t *uuid, + const struct iwp_buffer_map *b_maps, + struct interworld_session *iws, + struct interworld_session *op_iws, + struct gp_return *gp_ret) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct xen_fe_gp_operation operation = { .ret = 0 }; + struct xen_fe_map *maps[4] = { NULL, NULL, NULL, NULL }; + int i, ret; + + /* Prepare operation first not to be racey */ + INIT_LIST_HEAD(&operation.list); + init_completion(&operation.completion); + /* Note: slot is a unique identifier for a session/operation */ + operation.slot = session->slot; + operation.gp_ret = gp_ret; + operation.iws = iws; + mutex_lock(&l_ctx.gp_operations_lock); + list_add_tail(&operation.list, &l_ctx.gp_operations); + mutex_unlock(&l_ctx.gp_operations_lock); + + ring_get(xfe); + /* The operation may contain tmpref's to map */ + for (i = 0; i < TEE_BUFFERS; i++) { + if (!b_maps[i].map.addr) { + xfe->buffers[i].info->flags = 0; + continue; + } + + maps[i] = xen_fe_map_create(&xfe->buffers[i], &b_maps[i].map, + xfe->xdev->otherend_id); + if (IS_ERR(maps[i])) { + ret = PTR_ERR(maps[i]); + goto err; + } + } + + /* In */ + xfe->ring->domu.uuid = *uuid; + xfe->ring->domu.operation_id = session->slot; + xfe->ring->domu.iws = *op_iws; + /* Call */ + call_dom0(xfe, TEE_XEN_GP_OPEN_SESSION); + /* Out */ + ret = xfe->ring->domu.otherend_ret; +err: + for (i = 0; i < TEE_BUFFERS; i++) + xen_fe_map_release_pmd(maps[i], &xfe->buffers[i]); + + ring_put(xfe); + if (ret) { + mutex_lock(&l_ctx.gp_operations_lock); + list_del(&operation.list); + mutex_unlock(&l_ctx.gp_operations_lock); + return ret; + } + + /* Now wait for notification from Dom0 */ + wait_for_completion(&operation.completion); + /* FIXME origins? */ + return operation.ret; +} + +int xen_gp_close_session(struct iwp_session *session) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct xen_fe_gp_operation operation = { .ret = 0 }; + int ret; + + /* Prepare operation first not to be racey */ + INIT_LIST_HEAD(&operation.list); + init_completion(&operation.completion); + /* Note: slot is a unique identifier for a session/operation */ + operation.slot = session->slot; + mutex_lock(&l_ctx.gp_operations_lock); + list_add_tail(&operation.list, &l_ctx.gp_operations); + mutex_unlock(&l_ctx.gp_operations_lock); + + ring_get(xfe); + /* In */ + xfe->ring->domu.session_id = session->sid; + xfe->ring->domu.operation_id = session->slot; + /* Call */ + call_dom0(xfe, TEE_XEN_GP_CLOSE_SESSION); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + ring_put(xfe); + if (ret) { + mutex_lock(&l_ctx.gp_operations_lock); + list_del(&operation.list); + mutex_unlock(&l_ctx.gp_operations_lock); + return ret; + } + + /* Now wait for notification from Dom0 */ + wait_for_completion(&operation.completion); + return operation.ret; +} + +int xen_gp_invoke_command(struct iwp_session *session, + const struct iwp_buffer_map *b_maps, + struct interworld_session *iws, + struct gp_return *gp_ret) +{ + struct tee_xfe *xfe = l_ctx.xfe; + struct xen_fe_gp_operation operation = { .ret = 0 }; + struct xen_fe_map *maps[4] = { NULL, NULL, NULL, NULL }; + int i, ret; + + /* Prepare operation first not to be racey */ + INIT_LIST_HEAD(&operation.list); + init_completion(&operation.completion); + /* Note: slot is a unique identifier for a session/operation */ + operation.slot = session->slot; + operation.gp_ret = gp_ret; + operation.iws = iws; + mutex_lock(&l_ctx.gp_operations_lock); + list_add_tail(&operation.list, &l_ctx.gp_operations); + mutex_unlock(&l_ctx.gp_operations_lock); + + ring_get(xfe); + /* The operation is in op_iws and may contain tmpref's to map */ + for (i = 0; i < TEE_BUFFERS; i++) { + if (!b_maps[i].map.addr) { + xfe->buffers[i].info->flags = 0; + continue; + } + + maps[i] = xen_fe_map_create(&xfe->buffers[i], &b_maps[i].map, + xfe->xdev->otherend_id); + if (IS_ERR(maps[i])) { + ret = PTR_ERR(maps[i]); + goto err; + } + } + + /* In */ + xfe->ring->domu.session_id = session->sid; + xfe->ring->domu.operation_id = session->slot; + xfe->ring->domu.iws = *iws; + /* Call */ + call_dom0(xfe, TEE_XEN_GP_INVOKE_COMMAND); + /* Out */ + ret = xfe->ring->domu.otherend_ret; +err: + for (i = 0; i < TEE_BUFFERS; i++) + xen_fe_map_release_pmd(maps[i], &xfe->buffers[i]); + + ring_put(xfe); + if (ret) { + mutex_lock(&l_ctx.gp_operations_lock); + list_del(&operation.list); + mutex_unlock(&l_ctx.gp_operations_lock); + return ret; + } + + /* Now wait for notification from Dom0 */ + wait_for_completion(&operation.completion); + /* FIXME origins? */ + return operation.ret; +} + +int xen_gp_request_cancellation(u64 slot) +{ + struct tee_xfe *xfe = l_ctx.xfe; + int ret; + + ring_get(xfe); + /* In */ + xfe->ring->domu.operation_id = slot; + /* Call */ + call_dom0(xfe, TEE_XEN_GP_REQUEST_CANCELLATION); + /* Out */ + ret = xfe->ring->domu.otherend_ret; + ring_put(xfe); + return ret; +} + +/* Device */ + +static inline void xfe_release(struct tee_xfe *xfe) +{ + int i; + + if (xfe->irq_domu >= 0) + unbind_from_irqhandler(xfe->irq_domu, xfe); + + if (xfe->irq_dom0 >= 0) + unbind_from_irqhandler(xfe->irq_dom0, xfe); + + if (xfe->evtchn_domu >= 0) + xenbus_free_evtchn(xfe->xdev, xfe->evtchn_domu); + + if (xfe->evtchn_dom0 >= 0) + xenbus_free_evtchn(xfe->xdev, xfe->evtchn_dom0); + + for (i = 0; i < TEE_BUFFERS; i++) { + if (!xfe->buffers[i].data.page) + break; + + gnttab_end_foreign_access(xfe->ring->domu.buffers[i].pmd_ref, 0, + xfe->buffers[i].data.page); + free_page(xfe->buffers[i].data.page); + } + + if (xfe->ring_ul) { + gnttab_end_foreign_access(xfe->ring_ref, 0, xfe->ring_ul); + free_page(xfe->ring_ul); + } + + kfree(xfe); +} + +static inline struct tee_xfe *xfe_create(struct xenbus_device *xdev) +{ + struct tee_xfe *xfe; + struct xenbus_transaction trans; + int i, ret = -ENOMEM; + + /* Alloc */ + xfe = tee_xfe_create(xdev); + if (!xfe) + return ERR_PTR(-ENOMEM); + + /* Create shared information buffer */ + xfe->ring_ul = get_zeroed_page(GFP_KERNEL); + if (!xfe->ring_ul) + goto err; + + /* Connect */ + ret = xenbus_grant_ring(xfe->xdev, xfe->ring, 1, &xfe->ring_ref); + if (ret < 0) + goto err; + + for (i = 0; i < TEE_BUFFERS; i++) { + xfe->buffers[i].data.page = get_zeroed_page(GFP_KERNEL); + if (!xfe->buffers[i].data.page) + goto err; + + ret = xenbus_grant_ring(xfe->xdev, xfe->buffers[i].data.addr, 1, + &xfe->ring->domu.buffers[i].pmd_ref); + if (ret < 0) + goto err; + + xfe->buffers[i].info = &xfe->ring->domu.buffers[i]; + } + + ret = xenbus_alloc_evtchn(xfe->xdev, &xfe->evtchn_domu); + if (ret) + goto err; + + ret = xenbus_alloc_evtchn(xfe->xdev, &xfe->evtchn_dom0); + if (ret) + goto err; + + ret = bind_evtchn_to_irqhandler(xfe->evtchn_domu, + xen_fe_irq_handler_domu_th, 0, + "tee_fe_domu", xfe); + if (ret < 0) + goto err; + + xfe->irq_domu = ret; + + ret = bind_evtchn_to_irqhandler(xfe->evtchn_dom0, + xen_fe_irq_handler_dom0_th, 0, + "tee_fe_dom0", xfe); + if (ret < 0) + goto err; + + xfe->irq_dom0 = ret; + + /* Publish */ + do { + ret = xenbus_transaction_start(&trans); + if (ret) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to start transaction"); + goto err_transaction; + } + + /* Ring is one page to support older kernels */ + ret = xenbus_printf(trans, xfe->xdev->nodename, + "ring-ref", "%u", xfe->ring_ref); + if (ret) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to write ring ref"); + goto err_transaction; + } + + ret = xenbus_printf(trans, xfe->xdev->nodename, + "pte-entries-max", "%u", + PTE_ENTRIES_MAX); + if (ret) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to write PTE entries max"); + goto err_transaction; + } + + ret = xenbus_printf(trans, xfe->xdev->nodename, + "event-channel-domu", "%u", + xfe->evtchn_domu); + if (ret) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to write event channel domu"); + goto err_transaction; + } + + ret = xenbus_printf(trans, xfe->xdev->nodename, + "event-channel-dom0", "%u", + xfe->evtchn_dom0); + if (ret) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to write event channel dom0"); + goto err_transaction; + } + + ret = xenbus_printf(trans, xfe->xdev->nodename, + "domu-version", "%u", TEE_XEN_VERSION); + if (ret) { + xenbus_dev_fatal(xfe->xdev, ret, + "failed to write version"); + goto err_transaction; + } + + ret = xenbus_transaction_end(trans, 0); + if (ret) { + if (ret == -EAGAIN) + mc_dev_devel("retry"); + else + xenbus_dev_fatal(xfe->xdev, ret, + "failed to end transaction"); + } + } while (ret == -EAGAIN); + + mc_dev_devel("evtchn domu=%u dom0=%u version=%u", + xfe->evtchn_domu, xfe->evtchn_dom0, TEE_XEN_VERSION); + xenbus_switch_state(xfe->xdev, XenbusStateInitialised); + return xfe; + +err_transaction: +err: + xenbus_switch_state(xfe->xdev, XenbusStateClosed); + xfe_release(xfe); + return ERR_PTR(ret); +} + +static const struct xenbus_device_id xen_fe_ids[] = { + { "tee_xen" }, + { "" } +}; + +static int xen_fe_probe(struct xenbus_device *xdev, + const struct xenbus_device_id *id) +{ + int ret; + + ret = l_ctx.probe(); + if (ret) + return ret; + + l_ctx.xfe = xfe_create(xdev); + if (IS_ERR(l_ctx.xfe)) + return PTR_ERR(l_ctx.xfe); + + INIT_WORK(&l_ctx.xfe->work, xen_fe_irq_handler_dom0_bh); + + return 0; +} + +static void xen_fe_backend_changed(struct xenbus_device *xdev, + enum xenbus_state be_state) +{ + struct tee_xfe *xfe = l_ctx.xfe; + + mc_dev_devel("be state changed to %d", be_state); + + if (be_state == l_ctx.last_be_state) { + /* Protection against duplicated notifications (TBUG-1387) */ + mc_dev_devel("be state (%d) already set... ignoring", be_state); + return; + } + + switch (be_state) { + case XenbusStateUnknown: + case XenbusStateInitialising: + case XenbusStateInitWait: + case XenbusStateInitialised: + break; + case XenbusStateConnected: + if (l_ctx.start()) + xenbus_switch_state(xfe->xdev, XenbusStateClosing); + else + xenbus_switch_state(xfe->xdev, XenbusStateConnected); + break; + case XenbusStateClosing: + case XenbusStateClosed: + case XenbusStateReconfiguring: + case XenbusStateReconfigured: + break; + } + + /* Refresh last back-end state */ + l_ctx.last_be_state = be_state; +} + +static struct xenbus_driver xen_fe_driver = { + .ids = xen_fe_ids, + .probe = xen_fe_probe, + .otherend_changed = xen_fe_backend_changed, +}; + +int xen_fe_init(int (*probe)(void), int (*start)(void)) +{ + l_ctx.probe = probe; + l_ctx.start = start; + mutex_init(&l_ctx.mc_sessions_lock); + INIT_LIST_HEAD(&l_ctx.mc_sessions); + mutex_init(&l_ctx.gp_operations_lock); + INIT_LIST_HEAD(&l_ctx.gp_operations); + return xenbus_register_frontend(&xen_fe_driver); +} + +void xen_fe_exit(void) +{ + struct tee_xfe *xfe = l_ctx.xfe; + + tee_xfe_put(xfe); + xenbus_unregister_driver(&xen_fe_driver); +} + +#endif /* TRUSTONIC_XEN_DOMU */ diff --git a/drivers/gud/MobiCoreDriver/xen_fe.h b/drivers/gud/MobiCoreDriver/xen_fe.h new file mode 100644 index 000000000000..f4eec823d2cc --- /dev/null +++ b/drivers/gud/MobiCoreDriver/xen_fe.h @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2017 TRUSTONIC LIMITED + * 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 + * 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. + */ + +#ifndef _MC_XEN_FE_H_ +#define _MC_XEN_FE_H_ + +#include + +#include "main.h" +#include "client.h" +#include "iwp.h" +#include "mcp.h" + +#ifdef TRUSTONIC_XEN_DOMU +/* MC protocol interface */ +int xen_mc_get_version(struct mc_version_info *version_info); +int xen_mc_open_session(struct mcp_session *session, + struct mcp_open_info *info); +int xen_mc_close_session(struct mcp_session *session); +int xen_mc_map(u32 session_id, struct tee_mmu *mmu, u32 *sva); +int xen_mc_unmap(u32 session_id, const struct mcp_buffer_map *map); +int xen_mc_notify(struct mcp_session *session); +int xen_mc_wait(struct mcp_session *session, s32 timeout, bool silent_expiry); +int xen_mc_get_err(struct mcp_session *session, s32 *err); +/* GP protocol interface */ +int xen_gp_register_shared_mem(struct tee_mmu *mmu, u32 *sva, + struct gp_return *gp_ret); +int xen_gp_release_shared_mem(struct mcp_buffer_map *map); +int xen_gp_open_session(struct iwp_session *session, + const struct mc_uuid_t *uuid, + const struct iwp_buffer_map *maps, + struct interworld_session *iws, + struct interworld_session *op_iws, + struct gp_return *gp_ret); +int xen_gp_close_session(struct iwp_session *session); +int xen_gp_invoke_command(struct iwp_session *session, + const struct iwp_buffer_map *maps, + struct interworld_session *iws, + struct gp_return *gp_ret); +int xen_gp_request_cancellation(u64 slot); + +int xen_fe_init(int (*probe)(void), int (*start)(void)); +void xen_fe_exit(void); +#else +static inline int xen_fe_init(int (*probe)(void), int (*start)(void)) +{ + return 0; +} + +static inline void xen_fe_exit(void) +{ +} +#endif + +#endif /* _MC_XEN_FE_H_ */ diff --git a/drivers/gud/README b/drivers/gud/README new file mode 100644 index 000000000000..52954aab1f42 --- /dev/null +++ b/drivers/gud/README @@ -0,0 +1,8 @@ + +Trustonic TEE is an operating system being shipped with TZBSP +on MSM chipsets. t-base consists of several components in +the secure world(TrustZone) and non-secure world(linux +kernel, Android user space). The t-base driver +communicates with the Trustonic TEE kernel that exists in +TrustZone. + diff --git a/drivers/input/misc/anc_fps_mmi/Android.mk b/drivers/input/misc/anc_fps_mmi/Android.mk new file mode 100644 index 000000000000..5f76c89d90b4 --- /dev/null +++ b/drivers/input/misc/anc_fps_mmi/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := anc_fps_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +ifneq ($(findstring mmi_relay.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KERNEL_CFLAGS += CONFIG_MMI_RELAY=y + LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_relay.ko +endif +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/misc/anc_fps_mmi/Kbuild b/drivers/input/misc/anc_fps_mmi/Kbuild new file mode 100644 index 000000000000..4ab72750bb2c --- /dev/null +++ b/drivers/input/misc/anc_fps_mmi/Kbuild @@ -0,0 +1,14 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -DCONFIG_PM_WAKELOCKS +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + +ifneq ($(filter m y,$(CONFIG_MMI_RELAY)),) + EXTRA_CFLAGS += -DMMI_RELAY_MODULE +endif + +anc_fps_mmi-y += jiiov_platform.o jiiov_netlink.o custom.o + +obj-m += anc_fps_mmi.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../mmi_relay/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/misc/anc_fps_mmi/Makefile b/drivers/input/misc/anc_fps_mmi/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/input/misc/anc_fps_mmi/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/input/misc/anc_fps_mmi/custom.c b/drivers/input/misc/anc_fps_mmi/custom.c new file mode 100644 index 000000000000..a98038782a05 --- /dev/null +++ b/drivers/input/misc/anc_fps_mmi/custom.c @@ -0,0 +1,12 @@ +#define LOG_TAG "[ANC][custom]" + +#include + +// clang-format off +#include "jiiov_log.h" +#include "jiiov_platform.h" +// clang-format on + +int custom_send_command(CUSTOM_COMMAND cmd, void *p_param) { + return 0; +} diff --git a/drivers/input/misc/anc_fps_mmi/jiiov_config.h b/drivers/input/misc/anc_fps_mmi/jiiov_config.h new file mode 100644 index 000000000000..3451fed113ad --- /dev/null +++ b/drivers/input/misc/anc_fps_mmi/jiiov_config.h @@ -0,0 +1,20 @@ +#ifndef __JIIOV_CONFIG_H__ +#define __JIIOV_CONFIG_H__ + +/*Define the macro when using ree spi*/ +// #define ANC_USE_REE_SPI + +/*Define the macro when using navigation function*/ +// #define ANC_SUPPORT_NAVIGATION_EVENT + +/*Define the macro to adapt to the project platform*/ +// #define QSEE_PLATFORM +// #define MTK_PLATFORM +// #define TRUSTY_PLATFORM +// #define SAMSUNG_PLATFORM + +/*Define the macro to adapt to sensor type*/ +// #define CAPACITIVE +// #define OPTICAL + +#endif \ No newline at end of file diff --git a/drivers/input/misc/anc_fps_mmi/jiiov_log.h b/drivers/input/misc/anc_fps_mmi/jiiov_log.h new file mode 100644 index 000000000000..ff66783cfb7e --- /dev/null +++ b/drivers/input/misc/anc_fps_mmi/jiiov_log.h @@ -0,0 +1,13 @@ +#ifndef __JIIOV_LOG_H__ +#define __JIIOV_LOG_H__ + +#define ANC_LOGD(format, ...) \ + printk(KERN_DEBUG "[D][ANC_DRIVER][%s] " format "\n", __func__, ##__VA_ARGS__) +#define ANC_LOGI(format, ...) \ + printk(KERN_INFO "[I][ANC_DRIVER][%s] " format "\n", __func__, ##__VA_ARGS__) +#define ANC_LOGW(format, ...) \ + printk(KERN_WARNING "[W][ANC_DRIVER][%s] " format "\n", __func__, ##__VA_ARGS__) +#define ANC_LOGE(format, ...) \ + printk(KERN_ERR "[E][ANC_DRIVER][%s] " format "\n", __func__, ##__VA_ARGS__) + +#endif \ No newline at end of file diff --git a/drivers/input/misc/anc_fps_mmi/jiiov_netlink.c b/drivers/input/misc/anc_fps_mmi/jiiov_netlink.c new file mode 100644 index 000000000000..be96310973eb --- /dev/null +++ b/drivers/input/misc/anc_fps_mmi/jiiov_netlink.c @@ -0,0 +1,108 @@ +/** + * Netlink interface for JIIOV's fingerprint sensor. + * + * Copyright (C) 2020 JIIOV Corporation. + * + * 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 +#include +#include +#include + +// clang-format off +#include "jiiov_log.h" +#include "jiiov_netlink.h" +// clang-format on + +#define NETLINK_ANC 30 +#define USER_PORT 100 + +static struct sock *gp_netlink_sock = NULL; +extern struct net init_net; // by default + +static int netlink_send_message(const char *p_buffer, uint16_t length) { + int ret = 0; + struct sk_buff *p_sk_buff = NULL; + struct nlmsghdr *p_nlmsghdr = NULL; + + /* 创建sk_buff 空间 */ + p_sk_buff = nlmsg_new(length, GFP_ATOMIC); + if (NULL == p_sk_buff) { + ANC_LOGE("netlink alloc failure"); + return -1; + } + + /* 设置netlink消息头部 */ + p_nlmsghdr = nlmsg_put(p_sk_buff, 0, 0, NETLINK_ANC, length, 0); + if (NULL == p_nlmsghdr) { + ANC_LOGE("nlmsg_put failaure"); + nlmsg_free(p_sk_buff); + return -1; + } + + /* 拷贝数据发送 */ + memcpy(nlmsg_data(p_nlmsghdr), p_buffer, length); + ret = netlink_unicast(gp_netlink_sock, p_sk_buff, USER_PORT, MSG_DONTWAIT); + + return ret; +} + +int netlink_send_message_to_user(const char *p_buffer, size_t length) { + int ret = -1; + + if ((NULL != p_buffer) && (length > 0)) { + ANC_LOGE("send message to user: %d", *p_buffer); + ret = netlink_send_message(p_buffer, length); + } + + return ret; +} + +static void netlink_receive_message(struct sk_buff *p_sk_buffer) { + struct nlmsghdr *nlh = NULL; + char *p_user_message = NULL; + + if (p_sk_buffer->len >= nlmsg_total_size(0)) { + nlh = nlmsg_hdr(p_sk_buffer); + p_user_message = NLMSG_DATA(nlh); + if (p_user_message) { + ANC_LOGE("received message:%s, length:%lu", p_user_message, strlen(p_user_message)); + netlink_send_message_to_user(p_user_message, strlen(p_user_message)); + } + } +} + +static struct netlink_kernel_cfg g_netlink_kernel_config = { + .input = netlink_receive_message, /* set recv callback */ +}; + +int anc_netlink_init(void) { + /* create netlink socket */ + gp_netlink_sock = + (struct sock *)netlink_kernel_create(&init_net, NETLINK_ANC, &g_netlink_kernel_config); + if (NULL == gp_netlink_sock) { + ANC_LOGE("netlink_kernel_create error !"); + return -1; + } + ANC_LOGD("anc_netlink_init"); + + return 0; +} + +void anc_netlink_exit(void) { + if (NULL != gp_netlink_sock) { + netlink_kernel_release(gp_netlink_sock); /* release ..*/ + gp_netlink_sock = NULL; + } + ANC_LOGD("anc_netlink_exit!"); +} \ No newline at end of file diff --git a/drivers/input/misc/anc_fps_mmi/jiiov_netlink.h b/drivers/input/misc/anc_fps_mmi/jiiov_netlink.h new file mode 100644 index 000000000000..e75ccff399f1 --- /dev/null +++ b/drivers/input/misc/anc_fps_mmi/jiiov_netlink.h @@ -0,0 +1,21 @@ +#ifndef __JIIOV_NETLINK_H__ +#define __JIIOV_NETLINK_H__ + +typedef enum { + ANC_NETLINK_EVENT_TEST = 0, + ANC_NETLINK_EVENT_IRQ, + ANC_NETLINK_EVENT_SCR_OFF, + ANC_NETLINK_EVENT_SCR_ON, + ANC_NETLINK_EVENT_TOUCH_DOWN, + ANC_NETLINK_EVENT_TOUCH_UP, + ANC_NETLINK_EVENT_UI_READY, + ANC_NETLINK_EVENT_EXIT, + ANC_NETLINK_EVENT_INVALID, + ANC_NETLINK_EVENT_MAX +} ANC_NETLINK_EVENT_TYPE; + +int netlink_send_message_to_user(const char *p_buffer, size_t length); +int anc_netlink_init(void); +void anc_netlink_exit(void); + +#endif \ No newline at end of file diff --git a/drivers/input/misc/anc_fps_mmi/jiiov_platform.c b/drivers/input/misc/anc_fps_mmi/jiiov_platform.c new file mode 100644 index 000000000000..f2af27608cf8 --- /dev/null +++ b/drivers/input/misc/anc_fps_mmi/jiiov_platform.c @@ -0,0 +1,1941 @@ +/** + * The device control driver for JIIOV's fingerprint sensor. + * + * Copyright (C) 2020 JIIOV Corporation. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// clang-format off +#include "jiiov_config.h" +#include "jiiov_log.h" +#include "jiiov_netlink.h" +#include "jiiov_platform.h" +// clang-format on + +#ifdef SAMSUNG_PLATFORM +#include +#include +#endif + +#ifdef CONFIG_PM_WAKELOCKS +#include +#include +#else +#include "../include/wakelock.h" +#endif + +#if defined(ANC_USE_REE_SPI) || defined(MTK_PLATFORM) +#include +#include +#endif + +#ifdef ANC_SUPPORT_NAVIGATION_EVENT +#include +#define ANC_INPUT_NAME "jiiov-keys" +#endif + +typedef enum { + ANC_KEY_NONE = 0, + ANC_KEY_HOME, + ANC_KEY_MENU, + ANC_KEY_BACK, + ANC_KEY_UP, + ANC_KEY_LEFT, + ANC_KEY_RIGHT, + ANC_KEY_DOWN, + ANC_KEY_POWER, + ANC_KEY_WAKEUP, + ANC_KEY_CAMERA, +} ANC_KEY_TYPE; + +typedef struct { + ANC_KEY_TYPE key; + int value; /* 0 = key up, 1 = key down */ +} ANC_KEY_EVENT; + +typedef struct { + char product_id[16]; +} __attribute__((packed)) ANC_SENSOR_PRODUCT_INFO; + +#define ANC_IOC_MAGIC 'a' + +#define ANC_IOC_RESET _IO(ANC_IOC_MAGIC, 0) +#define ANC_IOC_REQUEST_RESOURCE _IO(ANC_IOC_MAGIC, 1) +#define ANC_IOC_RELEASE_RESOURCE _IO(ANC_IOC_MAGIC, 2) +#define ANC_IOC_ENABLE_IRQ _IO(ANC_IOC_MAGIC, 3) +#define ANC_IOC_DISABLE_IRQ _IO(ANC_IOC_MAGIC, 4) +#define ANC_IOC_INIT_IRQ _IO(ANC_IOC_MAGIC, 5) +#define ANC_IOC_DEINIT_IRQ _IO(ANC_IOC_MAGIC, 6) +#define ANC_IOC_SET_IRQ_FLAG_MASK _IO(ANC_IOC_MAGIC, 7) +#define ANC_IOC_CLEAR_IRQ_FLAG_MASK _IO(ANC_IOC_MAGIC, 8) +#define ANC_IOC_ENABLE_SPI_CLK _IO(ANC_IOC_MAGIC, 9) +#define ANC_IOC_DISABLE_SPI_CLK _IO(ANC_IOC_MAGIC, 10) +#define ANC_IOC_ENABLE_POWER _IO(ANC_IOC_MAGIC, 11) +#define ANC_IOC_DISABLE_POWER _IO(ANC_IOC_MAGIC, 12) +#define ANC_IOC_SPI_SPEED _IOW(ANC_IOC_MAGIC, 13, uint32_t) +#define ANC_IOC_CLEAR_FLAG _IO(ANC_IOC_MAGIC, 14) +#define ANC_IOC_WAKE_LOCK _IO(ANC_IOC_MAGIC, 15) +#define ANC_IOC_WAKE_UNLOCK _IO(ANC_IOC_MAGIC, 16) +#define ANC_IOC_REPORT_KEY_EVENT _IOW(ANC_IOC_MAGIC, 17, ANC_KEY_EVENT) +#define ANC_IOC_SET_PRODUCT_ID _IOW(ANC_IOC_MAGIC, 18, ANC_SENSOR_PRODUCT_INFO) +#define ANC_IOC_GET_IRQ_PIN_LEVEL _IOR(ANC_IOC_MAGIC, 19, uint8_t) + +#define ANC_COMPATIBLE_SW_FP "jiiov,fingerprint" +#define ANC_DEVICE_NAME "jiiov_fp" + +#ifdef SAMSUNG_PLATFORM +enum anc_spi_speed { + ANC_LOW_SPEED = 0, + ANC_HIGH_SPEED = 1, + ANC_SUPER_SPEED = 2, +}; + +#define SPI_LOW_SPEED (1000000 * 4 * 6) /* 6M */ +#define SPI_HIGH_SPEED (1000000 * 4 * 20) /* 20M */ +#define SPI_SUPER_SPEED (1000000 * 4 * 25) /* 25M */ +#endif + +#if defined(ANC_USE_REE_SPI) || defined(MTK_PLATFORM) +#ifdef ANC_USE_REE_SPI +#define SPI_BUFFER_SIZE (50 * 1024) +#define ANC_DEFAULT_SPI_SPEED (18 * 1000 * 1000) +static uint8_t *spi_buffer = NULL; +#endif +typedef struct spi_device anc_device_t; +typedef struct spi_driver anc_driver_t; +#else +typedef struct platform_device anc_device_t; +typedef struct platform_driver anc_driver_t; +#endif + +static anc_device_t *g_anc_spi_device = NULL; + +struct vreg_config { + char *name; + unsigned int vmin; + unsigned int vmax; + int ua_load; +}; + +#define ANC_VREG_VDD_NAME "vdd" +#define ANC_VDD_CONFIG_NAME "anc,vdd_config" +static struct vreg_config vreg_conf; + +static const char *const pctl_names[] = { + "anc_reset_low", + "anc_reset_high", + "anc_irq_default", +}; + +struct anc_data { + struct device *dev; + struct class *dev_class; + dev_t dev_num; + struct cdev cdev; + ANC_SENSOR_PRODUCT_INFO product_info; +#ifdef ANC_SUPPORT_NAVIGATION_EVENT + struct input_dev *input; + ANC_KEY_EVENT key_event; +#endif + + struct pinctrl *fingerprint_pinctrl; + struct pinctrl_state *pinctrl_state[ARRAY_SIZE(pctl_names)]; + struct regulator *vreg; +#ifdef CONFIG_PM_WAKELOCKS + struct wakeup_source *fp_wakelock; +#else + struct wake_lock fp_wakelock; +#endif + struct work_struct work_queue; + int irq_gpio; + int irq; + atomic_t irq_enabled; + bool irq_mask_flag; + int irq_init; + int pwr_gpio; + int rst_gpio; + struct mutex lock; + bool resource_requested; + + bool vdd_use_gpio; + bool vdd_use_pmic; + bool use_gpio_init; + +#ifdef SAMSUNG_PLATFORM + struct clk *core_clk; + struct clk *iface_clk; + int clk_enabled; +#endif +}; + +static int vreg_setup(struct anc_data *p_data, const char *name, bool enable) { + int ret_val = 0; + + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return -EINVAL; + } + ret_val = strncmp(vreg_conf.name, name, strlen(vreg_conf.name)); + if (ret_val != 0) { + ANC_LOGE("Regulator %s not found", name); + return -EINVAL; + } + + if (enable) { + if (p_data->vreg == NULL) { + p_data->vreg = regulator_get(p_data->dev, name); + if (IS_ERR(p_data->vreg)) { + ANC_LOGE("Unable to get %s", name); + return PTR_ERR(p_data->vreg); + } + } + + if (regulator_count_voltages(p_data->vreg) > 0) { + ret_val = regulator_set_voltage(p_data->vreg, vreg_conf.vmin, vreg_conf.vmax); + if (ret_val) { + ANC_LOGE("Unable to set voltage on %s, %d", name, ret_val); + } + } + + ret_val = regulator_set_load(p_data->vreg, vreg_conf.ua_load); + if (ret_val < 0) { + ANC_LOGE("Unable to set current on %s, %d", name, ret_val); + } + + ret_val = regulator_enable(p_data->vreg); + if (ret_val) { + ANC_LOGE("error enabling %s: %d", name, ret_val); + regulator_put(p_data->vreg); + p_data->vreg = NULL; + } + } else { + if (p_data->vreg != NULL) { + ret_val = regulator_is_enabled(p_data->vreg); + if (ret_val != 0) { + regulator_disable(p_data->vreg); + ANC_LOGD("disabled %s", name); + } + regulator_put(p_data->vreg); + p_data->vreg = NULL; + } + } + + return ret_val; +} + +/** + * sysfs node to forward netlink event + */ +static ssize_t forward_netlink_event_set(struct device *p_dev, struct device_attribute *p_attr, + const char *p_buffer, size_t count) { + char netlink_msg = (char)ANC_NETLINK_EVENT_INVALID; + + ANC_LOGD("forward netlink event: %s", p_buffer); + if (!strncmp(p_buffer, "test", strlen("test"))) { + netlink_msg = (char)ANC_NETLINK_EVENT_TEST; + } else if (!strncmp(p_buffer, "irq", strlen("irq"))) { + netlink_msg = (char)ANC_NETLINK_EVENT_IRQ; + } else if (!strncmp(p_buffer, "screen_off", strlen("screen_off"))) { + netlink_msg = (char)ANC_NETLINK_EVENT_SCR_OFF; + } else if (!strncmp(p_buffer, "screen_on", strlen("screen_on"))) { + netlink_msg = (char)ANC_NETLINK_EVENT_SCR_ON; + } else if (!strncmp(p_buffer, "touch_down", strlen("touch_down"))) { + netlink_msg = (char)ANC_NETLINK_EVENT_TOUCH_DOWN; + } else if (!strncmp(p_buffer, "touch_up", strlen("touch_up"))) { + netlink_msg = (char)ANC_NETLINK_EVENT_TOUCH_UP; + } else if (!strncmp(p_buffer, "ui_ready", strlen("ui_ready"))) { + netlink_msg = (char)ANC_NETLINK_EVENT_UI_READY; + } else if (!strncmp(p_buffer, "exit", strlen("exit"))) { + netlink_msg = (char)ANC_NETLINK_EVENT_EXIT; + } else { + ANC_LOGE("don't support the netlink evnet: %s", p_buffer); + return -EINVAL; + } + + return netlink_send_message_to_user(&netlink_msg, sizeof(netlink_msg)); +} +static DEVICE_ATTR(netlink_event, S_IWUSR, NULL, forward_netlink_event_set); + +/** + * sysfs node to select the set of pins (GPIOS) defined in a pin control node of + * the device tree + */ +static int select_pin_ctl(struct anc_data *p_data, const char *name) { + int ret_val = 0; + size_t i = 0; + + if ((p_data == NULL) || (name == NULL)) { + ANC_LOGE("parameter is NULL, p_data:%p, name:%p", p_data, name); + return -EINVAL; + } + for (i = 0; i < ARRAY_SIZE(p_data->pinctrl_state); i++) { + const char *n = pctl_names[i]; + + if (!strncmp(n, name, strlen(n))) { + ret_val = pinctrl_select_state(p_data->fingerprint_pinctrl, p_data->pinctrl_state[i]); + if (ret_val) { + ANC_LOGE("cannot select %s, ret_val: %d", name, ret_val); + } else { + ANC_LOGD("Selected %s success", name); + } + goto exit; + } + } + + ret_val = -EINVAL; + ANC_LOGE("%s not found", name); + +exit: + return ret_val; +} + +static ssize_t pinctl_set(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) { + int ret_val = 0; + struct anc_data *p_data = dev_get_drvdata(dev); + if (p_data == NULL) { + ANC_LOGE("dev get data failed"); + return -EINVAL; + } + + mutex_lock(&p_data->lock); + ANC_LOGD("pinctl select %s", buf); + ret_val = select_pin_ctl(p_data, buf); + mutex_unlock(&p_data->lock); + + return ret_val ? ret_val : count; +} +static DEVICE_ATTR(pinctl_set, S_IWUSR, NULL, pinctl_set); + +static int set_reset_level(struct anc_data *p_data, int level) { + int ret_val = 0; + + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return -EINVAL; + } + if (p_data->use_gpio_init) { + gpio_set_value(p_data->rst_gpio, level); + } else { + if (level == 0) { + ret_val = select_pin_ctl(p_data, "anc_reset_low"); + } else { + ret_val = select_pin_ctl(p_data, "anc_reset_high"); + } + } + + if (ret_val != 0) { + ANC_LOGE("set reset level:%d failed, ret_val: %d", level, ret_val); + } + + return ret_val; +} + +static int anc_reset(struct anc_data *p_data) { + int ret_val = 0; + + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return -EINVAL; + } + ANC_LOGD("hardware reset"); + mutex_lock(&p_data->lock); + do { + ret_val = set_reset_level(p_data, 0); + if (ret_val != 0) { + break; + } + // T2 >= 10ms + mdelay(10); + ret_val = set_reset_level(p_data, 1); + if (ret_val != 0) { + break; + } + mdelay(10); + } while (0); + mutex_unlock(&p_data->lock); + + return ret_val; +} + +static ssize_t hw_reset_set(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) { + int ret_val = 0; + struct anc_data *p_data = dev_get_drvdata(dev); + if (p_data == NULL) { + ANC_LOGE("dev get data failed"); + return -EINVAL; + } + + if (!strncmp(buf, "reset", strlen("reset"))) { + ret_val = anc_reset(p_data); + if (ret_val != 0) { + ANC_LOGE("hardware reset failed, ret_val: %d", ret_val); + } + } else { + ret_val = -EINVAL; + ANC_LOGE("the reset name is not matched, name: %s", buf); + } + + return ret_val ? ret_val : count; +} +static DEVICE_ATTR(hw_reset, S_IWUSR, NULL, hw_reset_set); + +static void anc_power_onoff(struct anc_data *p_data, bool power_onoff) { + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return; + } + + ANC_LOGD("power_onoff = %d ", power_onoff); + if (!p_data->resource_requested) { + ANC_LOGW("warning: resource is not requested, return!"); + return; + } + + if (p_data->vdd_use_gpio) { + gpio_set_value(p_data->pwr_gpio, power_onoff); + } + + if (p_data->vdd_use_pmic) { + int ret_val = vreg_setup(p_data, ANC_VREG_VDD_NAME, power_onoff); + if (ret_val != 0) { + ANC_LOGE("use pmic to contrl power failed, ret_val: %d", ret_val); + } + } +} + +static void device_power_up(struct anc_data *p_data) { + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return; + } + + ANC_LOGD("device power up"); + anc_power_onoff(p_data, true); +} + +/** + * sysfs node to power on/power off the sensor + */ +static ssize_t device_power_set(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) { + ssize_t ret_val = count; + struct anc_data *p_data = dev_get_drvdata(dev); + if (p_data == NULL) { + ANC_LOGE("dev get data failed"); + return -EINVAL; + } + + mutex_lock(&p_data->lock); + if (!strncmp(buf, "on", strlen("on"))) { + ANC_LOGD("device power on"); + anc_power_onoff(p_data, true); + } else if (!strncmp(buf, "off", strlen("off"))) { + ANC_LOGD("device power off"); + anc_power_onoff(p_data, false); + } else { + ret_val = -EINVAL; + ANC_LOGE("the power name is not matched, name: %s", buf); + } + mutex_unlock(&p_data->lock); + + return ret_val; +} +static DEVICE_ATTR(device_power, S_IWUSR, NULL, device_power_set); + +#ifdef ANC_USE_REE_SPI +static int anc_spi_transfer(const uint8_t *txbuf, uint8_t *rxbuf, int len) { + struct spi_transfer t; + struct spi_message m; + + memset(&t, 0, sizeof(t)); + spi_message_init(&m); + t.tx_buf = txbuf; + t.rx_buf = rxbuf; + t.bits_per_word = 8; + t.len = len; + t.speed_hz = g_anc_spi_device->max_speed_hz; + spi_message_add_tail(&t, &m); + return spi_sync(g_anc_spi_device, &m); +} + +static uint32_t anc_read_sensor_id(struct anc_data *p_data) { + int ret_val = -1; + int trytimes = 3; + uint32_t sensor_chip_id = 0; + + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return -EINVAL; + } + do { + memset(spi_buffer, 0, 4); + spi_buffer[0] = 0x30; + spi_buffer[1] = (char)(~0x30); + + anc_reset(p_data); + + ret_val = anc_spi_transfer(spi_buffer, spi_buffer, 4); + if (ret_val != 0) { + ANC_LOGE("spi_transfer failed"); + continue; + } + + sensor_chip_id = (uint32_t)((spi_buffer[3] & 0x00FF) | ((spi_buffer[2] << 8) & 0xFF00)); + ANC_LOGD("sensor chip_id = %#x", sensor_chip_id); + + if (sensor_chip_id == 0x6311) { + ANC_LOGD("Read Sensor Id Success"); + return 0; + } else { + ANC_LOGE("Read Sensor Id Fail"); + } + } while (trytimes--); + + return sensor_chip_id; +} + +/** + * sysfs node to read sensor id + */ +static ssize_t sensor_id_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct anc_data *p_data = dev_get_drvdata(dev); + uint32_t sensor_chip_id = anc_read_sensor_id(p_data); + + return scnprintf(buf, PAGE_SIZE, "0x%04x", sensor_chip_id); +} +static DEVICE_ATTR(sensor_id, S_IRUSR, sensor_id_show, NULL); + +static int set_spi_speed(uint32_t speed) { + ANC_LOGD("set spi speed : %dhz", speed); + g_anc_spi_device->max_speed_hz = speed; + + return spi_setup(g_anc_spi_device); +} + +static ssize_t anc_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) { + ssize_t status = 0; + // struct anc_data *dev_data = filp->private_data; + + ANC_LOGD("count = %zu", count); + + if (count > SPI_BUFFER_SIZE) { + return (-EMSGSIZE); + } + + if (copy_from_user(spi_buffer, buf, count)) { + ANC_LOGE("copy_from_user failed"); + return (-EMSGSIZE); + } + + status = anc_spi_transfer(spi_buffer, spi_buffer, count); + + if (status == 0) { + status = copy_to_user(buf, spi_buffer, count); + + if (status != 0) { + status = -EFAULT; + } else { + status = count; + } + } else { + ANC_LOGE("spi_transfer failed"); + } + + return status; +} + +static ssize_t anc_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos) { + ssize_t status = 0; + // struct anc_data *dev_data = filp->private_data; + + ANC_LOGD("count = %zu", count); + + if (count > SPI_BUFFER_SIZE) { + return (-EMSGSIZE); + } + + if (copy_from_user(spi_buffer, buf, count)) { + ANC_LOGE("copy_from_user failed"); + return (-EMSGSIZE); + } + + status = anc_spi_transfer(spi_buffer, NULL, count); + + if (status == 0) { + status = count; + } else { + ANC_LOGE("spi_transfer failed"); + } + + return status; +} + +static int anc_config_spi(void) { + g_anc_spi_device->mode = SPI_MODE_0; + g_anc_spi_device->bits_per_word = 8; + g_anc_spi_device->max_speed_hz = ANC_DEFAULT_SPI_SPEED; + + return spi_setup(g_anc_spi_device); +} +#endif + +static void anc_enable_irq(struct anc_data *p_data) { + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return; + } + ANC_LOGD("enable irq"); + + if (!p_data->resource_requested) { + ANC_LOGW("warning: resource is not requested, return!"); + return; + } + + if (atomic_read(&p_data->irq_enabled)) { + ANC_LOGW("IRQ has been enabled"); + } else { + enable_irq(p_data->irq); + atomic_set(&p_data->irq_enabled, 1); + } +} + +static void anc_disable_irq(struct anc_data *p_data) { + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return; + } + ANC_LOGD("disable irq"); + + if (!p_data->resource_requested) { + ANC_LOGW("warning: resource is not requested, return!"); + return; + } + + if (atomic_read(&p_data->irq_enabled)) { + disable_irq(p_data->irq); + atomic_set(&p_data->irq_enabled, 0); + } else { + ANC_LOGW("IRQ has been disabled"); + } +} + +static void anc_wake_lock(struct anc_data *p_data) { + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return; + } + ANC_LOGD("wake lock"); +#ifdef CONFIG_PM_WAKELOCKS + __pm_stay_awake(p_data->fp_wakelock); +#else + wake_lock(&p_data->fp_wakelock); +#endif +} + +static void anc_wake_unlock(struct anc_data *p_data) { + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return; + } + ANC_LOGD("wake unlock"); +#ifdef CONFIG_PM_WAKELOCKS + __pm_relax(p_data->fp_wakelock); + __pm_wakeup_event(p_data->fp_wakelock, msecs_to_jiffies(ANC_WAKELOCK_HOLD_TIME)); +#else + wake_unlock(&p_data->fp_wakelock); + wake_lock_timeout(&p_data->fp_wakelock, msecs_to_jiffies(ANC_WAKELOCK_HOLD_TIME)); +#endif +} + +/** + * sysfs node for controlling whether the driver is allowed + * to wake up the platform on interrupt. + */ +static ssize_t irq_control_set(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) { + ssize_t ret_val = count; + struct anc_data *p_data = dev_get_drvdata(dev); + + mutex_lock(&p_data->lock); + if (!strncmp(buf, "enable", strlen("enable"))) { + anc_enable_irq(p_data); + } else if (!strncmp(buf, "disable", strlen("disable"))) { + anc_disable_irq(p_data); + } else { + ret_val = -EINVAL; + ANC_LOGE("the irq name is not matched, name: %s", buf); + } + mutex_unlock(&p_data->lock); + + return ret_val ? ret_val : count; +} +static DEVICE_ATTR(irq_set, S_IWUSR, NULL, irq_control_set); + +static int anc_platform_init(struct anc_data *p_data); +static int anc_platform_free(struct anc_data *p_data); +/** + * sysfs node to request/release resource + */ +static ssize_t resource_control_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { + ssize_t ret_val = count; + struct anc_data *p_data = dev_get_drvdata(dev); + if (p_data == NULL) { + ANC_LOGE("dev get data failed"); + return -EINVAL; + } + + mutex_lock(&p_data->lock); + if (!strncmp(buf, "request", strlen("request"))) { + ANC_LOGD("request resource"); + anc_platform_init(p_data); + + } else if (!strncmp(buf, "release", strlen("release"))) { + ANC_LOGD("release resource"); + anc_platform_free(p_data); + } else { + ret_val = -EINVAL; + ANC_LOGE("the resource name is not matched, name: %s", buf); + } + mutex_unlock(&p_data->lock); + + return ret_val; +} +static DEVICE_ATTR(resource_set, S_IWUSR, NULL, resource_control_set); + +static struct attribute *attributes[] = {&dev_attr_pinctl_set.attr, + &dev_attr_device_power.attr, + &dev_attr_hw_reset.attr, + &dev_attr_irq_set.attr, + &dev_attr_netlink_event.attr, + &dev_attr_resource_set.attr, +#ifdef ANC_USE_REE_SPI + &dev_attr_sensor_id.attr, +#endif + NULL}; + +static const struct attribute_group attribute_group = { + .attrs = attributes, +}; + +static void anc_do_irq_work(struct work_struct *ws) { + char netlink_msg = (char)ANC_NETLINK_EVENT_IRQ; + + netlink_send_message_to_user(&netlink_msg, sizeof(netlink_msg)); +} + +static irqreturn_t anc_irq_handler(int irq, void *handle) { + struct anc_data *p_data = handle; + + ANC_LOGD("irq handler, %d", p_data->irq_mask_flag); + if (p_data->irq_mask_flag) { + return IRQ_HANDLED; + } +#ifdef CONFIG_PM_WAKELOCKS + __pm_wakeup_event(p_data->fp_wakelock, msecs_to_jiffies(ANC_WAKELOCK_HOLD_TIME)); +#else + wake_lock_timeout(&p_data->fp_wakelock, msecs_to_jiffies(ANC_WAKELOCK_HOLD_TIME)); +#endif + schedule_work(&p_data->work_queue); + + return IRQ_HANDLED; +} + +static int anc_request_named_gpio(struct anc_data *p_data, const char *label, int *gpio) { + int ret_val = 0; + + if ((p_data == NULL) || (label == NULL) || (gpio == NULL)) { + ANC_LOGE("parameter is NULL, p_data:%p, label:%p, gpio:%p", p_data, label, gpio); + return -EINVAL; + } + ret_val = of_get_named_gpio(p_data->dev->of_node, label, 0); + if (ret_val < 0) { + ANC_LOGE("failed to get '%s'", label); + return ret_val; + } + *gpio = ret_val; + + ret_val = devm_gpio_request(p_data->dev, *gpio, label); + if (ret_val) { + ANC_LOGE("failed to request gpio %d", *gpio); + return ret_val; + } + ANC_LOGD("%s %d", label, *gpio); + + return 0; +} + +static int anc_irq_init(struct anc_data *p_data) { + int ret_val = 0; + int irqf = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; // IRQF_TRIGGER_FALLING or IRQF_TRIGGER_RISING + + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return -EINVAL; + } + p_data->irq = gpio_to_irq(p_data->irq_gpio); + if (p_data->irq_init == 0) { + ret_val = devm_request_threaded_irq( + p_data->dev, p_data->irq, NULL, anc_irq_handler, irqf, dev_name(p_data->dev), p_data); + if (ret_val != 0) { + ANC_LOGE("Could not request irq %d, ret_val: %d", p_data->irq, ret_val); + return ret_val; + } + p_data->irq_init = 1; + + /* Request that the interrupt should be wakeable */ + enable_irq_wake(p_data->irq); + atomic_set(&p_data->irq_enabled, 1); + } + + return ret_val; +} + +static int anc_irq_deinit(struct anc_data *p_data) { + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return -EINVAL; + } + + if (p_data->irq_init == 1) { + disable_irq_wake(p_data->irq); + devm_free_irq(p_data->dev, p_data->irq, p_data); + p_data->irq_init = 0; + } + + return 0; +} + +static int anc_copy_from_user(void *to, const void *from, unsigned long n) { + int ret_val = 0; + + if (!access_ok(from, n)) { + ANC_LOGE("the parameter is invalid"); + return -EFAULT; + } + + ret_val = copy_from_user(to, from, n); + if (ret_val != 0) { + ANC_LOGE("copy_from_user failed, %d", ret_val); + ret_val = -EFAULT; + } + + return ret_val; +} + +#ifdef ANC_SUPPORT_NAVIGATION_EVENT +static int anc_report_key_event(struct anc_data *p_data, const void *arg) { + int ret_val = 0; + unsigned int key_code = KEY_UNKNOWN; + + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return -EINVAL; + } + + ret_val = anc_copy_from_user(&(p_data->key_event), arg, sizeof(ANC_KEY_EVENT)); + if (ret_val != 0) { + ANC_LOGE("Failed to copy input key event from user to kernel, ret_val: %d", ret_val); + return -EFAULT; + } + + ANC_LOGD("key = %d, value = %d", p_data->key_event.key, p_data->key_event.value); + switch (p_data->key_event.key) { + case ANC_KEY_HOME: + key_code = KEY_HOME; + break; + case ANC_KEY_MENU: + key_code = KEY_MENU; + break; + case ANC_KEY_BACK: + key_code = KEY_BACK; + break; + case ANC_KEY_UP: + key_code = KEY_UP; + break; + case ANC_KEY_DOWN: + key_code = KEY_DOWN; + break; + case ANC_KEY_LEFT: + key_code = KEY_LEFT; + break; + case ANC_KEY_RIGHT: + key_code = KEY_RIGHT; + break; + case ANC_KEY_POWER: + key_code = KEY_POWER; + break; + case ANC_KEY_WAKEUP: + key_code = KEY_WAKEUP; + break; + case ANC_KEY_CAMERA: + key_code = KEY_CAMERA; + break; + default: + key_code = (unsigned int)(p_data->key_event.key); + break; + } + + input_report_key(p_data->input, key_code, p_data->key_event.value); + input_sync(p_data->input); + + return ret_val; +} +#endif + +static int use_pinctrl_init(struct anc_data *p_data) { + int ret_val = 0; + size_t i = 0; + + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return -EINVAL; + } + p_data->fingerprint_pinctrl = devm_pinctrl_get(p_data->dev); + if (IS_ERR(p_data->fingerprint_pinctrl)) { + if (PTR_ERR(p_data->fingerprint_pinctrl) == -EPROBE_DEFER) { + ANC_LOGD("pinctrl not ready"); + return -EPROBE_DEFER; + } + ANC_LOGE("Target does not use pinctrl"); + p_data->fingerprint_pinctrl = NULL; + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(p_data->pinctrl_state); i++) { + struct pinctrl_state *state = + pinctrl_lookup_state(p_data->fingerprint_pinctrl, pctl_names[i]); + if (IS_ERR(state)) { + ANC_LOGE("cannot find '%s'", pctl_names[i]); + return -EINVAL; + } + ANC_LOGD("found pin control %s", pctl_names[i]); + p_data->pinctrl_state[i] = state; + } + + ret_val = select_pin_ctl(p_data, "anc_irq_default"); + if (ret_val) { + ANC_LOGD("set default irq failed, ret_val:%d", ret_val); + return ret_val; + } + + ret_val = select_pin_ctl(p_data, "anc_reset_high"); + if (ret_val) { + ANC_LOGD("set reset low failed, ret_val:%d", ret_val); + return ret_val; + } + + return ret_val; +} + +static int use_gpio_init(struct anc_data *p_data) { + int ret_val = 0; + + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return -EINVAL; + } + ret_val = anc_request_named_gpio(p_data, "anc,gpio_rst", &p_data->rst_gpio); + if (ret_val) { + ANC_LOGD("request reset gpio failed, ret_val:%d", ret_val); + return ret_val; + } + + ret_val = gpio_direction_output(p_data->rst_gpio, 1); + if (ret_val) { + ANC_LOGD("reset gpio direction output failed, ret_val:%d", ret_val); + return ret_val; + } + + ret_val = anc_request_named_gpio(p_data, "anc,gpio_irq", &p_data->irq_gpio); + if (ret_val) { + ANC_LOGD("request irq gpio failed, ret_val:%d", ret_val); + return ret_val; + } + + ret_val = gpio_direction_input(p_data->irq_gpio); + if (ret_val) { + ANC_LOGD("irq gpio direction input failed, ret_val:%d", ret_val); + return ret_val; + } + + return ret_val; +} + +static int anc_gpio_init(struct anc_data *p_data) { + int ret_val = 0; + + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return -EINVAL; + } + ANC_LOGD("init gpio"); + if (p_data->resource_requested) { + ANC_LOGW("warning: resource is already requested, return!"); + return 0; + } + + if (p_data->vdd_use_gpio) { + ret_val = anc_request_named_gpio(p_data, "anc,gpio_pwr", &p_data->pwr_gpio); + if (ret_val) { + ANC_LOGE("request power gpio failed, ret_val:%d", ret_val); + return ret_val; + } + + ret_val = gpio_direction_output(p_data->pwr_gpio, 0); + if (ret_val) { + ANC_LOGE("power gpio direction output failed, ret_val:%d", ret_val); + return ret_val; + } + } + + if (p_data->use_gpio_init == false) { + ret_val = use_pinctrl_init(p_data); + if (ret_val) { + ANC_LOGE("use pinctrl init failed, ret_val:%d", ret_val); + return ret_val; + } + } + + ret_val = use_gpio_init(p_data); + if (ret_val) { + ANC_LOGE("use gpio init failed, ret_val:%d", ret_val); + return ret_val; + } + +#ifdef SAMSUNG_PLATFORM + /* Init spi clock */ + ret_val = anc_spi_clk_init(p_data); + if (ret_val) { + ANC_LOGE("spi init failed, ret_val:%d", ret_val); + return ret_val; + } +#endif + + if (of_property_read_bool(p_data->dev->of_node, "anc,enable-on-boot")) { + ANC_LOGD("Enabling hardware"); + device_power_up(p_data); + } + + p_data->resource_requested = true; + ANC_LOGD("success"); + + return 0; +} + +static int anc_gpio_deinit(struct anc_data *p_data) { + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return -EINVAL; + } + + ANC_LOGD("deinit gpio"); + if (!p_data->resource_requested) { + ANC_LOGW("warning: resource is already released, return!"); + return 0; + } + + if (p_data->vdd_use_gpio) { + if (gpio_is_valid(p_data->pwr_gpio)) { + gpio_free(p_data->pwr_gpio); + } + } + + /* Release pinctl */ + if (p_data->fingerprint_pinctrl) { + ANC_LOGD("devm_pinctrl_put"); + devm_pinctrl_put(p_data->fingerprint_pinctrl); + p_data->fingerprint_pinctrl = NULL; + } + + if (gpio_is_valid(p_data->rst_gpio)) { + gpio_free(p_data->rst_gpio); + } + + if (gpio_is_valid(p_data->irq_gpio)) { + gpio_free(p_data->irq_gpio); + } + +#ifdef SAMSUNG_PLATFORM + /* Deinit spi clock */ + anc_spi_clk_deinit(p_data); +#endif + + p_data->resource_requested = false; + ANC_LOGD("success"); + + return 0; +} + +static int anc_open(struct inode *inode, struct file *filp) { + struct anc_data *p_data = NULL; + p_data = container_of(inode->i_cdev, struct anc_data, cdev); + if (p_data == NULL) { + ANC_LOGE("get anc date failed"); + return -EINVAL; + } + filp->private_data = p_data; + + return 0; +} + +static int anc_platform_init(struct anc_data *p_data) { + int ret_val = 0; + + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return -EINVAL; + } + ret_val = anc_gpio_init(p_data); + if (ret_val) { + ANC_LOGE("Failed to init gpio, ret_val: %d", ret_val); + return ret_val; + } + + return ret_val; +} + +static int anc_platform_free(struct anc_data *p_data) { + int ret_val = 0; + + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return -EINVAL; + } + ret_val = anc_gpio_deinit(p_data); + if (ret_val) { + ANC_LOGE("Failed to deinit gpio"); + return ret_val; + } + + return ret_val; +} + +#ifdef MTK_PLATFORM +extern void mt_spi_enable_master_clk(struct spi_device *spidev); +extern void mt_spi_disable_master_clk(struct spi_device *spidev); + +void spi_clk_enable(bool enable_flag) { + static bool is_spi_clk_open = false; + + if (enable_flag) { + if (!is_spi_clk_open) { + ANC_LOGD("enable spi clk"); + mt_spi_enable_master_clk(g_anc_spi_device); + is_spi_clk_open = true; + } else { + ANC_LOGD("spi clk already enable"); + } + } else { + if (is_spi_clk_open) { + ANC_LOGD("disable spi clk "); + mt_spi_disable_master_clk(g_anc_spi_device); + is_spi_clk_open = false; + } else { + ANC_LOGD("spi clk already disable "); + } + } +} +#endif + +#ifdef SAMSUNG_PLATFORM +static void spi_clock_set(struct anc_data *data, int speed) { +#if 0 + unsigned long rate_core; + unsigned long rate_iface; + + rate_core = clk_get_rate(data->core_clk); + rate_iface = clk_get_rate(data->iface_clk); + ANC_LOGD("%s, spi clock speed (%lld, %lld)", __func__, rate_core, rate_iface); +#endif + clk_set_rate(data->core_clk, speed); + clk_set_rate(data->iface_clk, speed); +} + +static int anc_spi_clk_init(struct anc_data *data) { + ANC_LOGD("%s, enter", __func__); + + data->clk_enabled = 0; + data->core_clk = clk_get(data->dev, "gate_spi_clk" /*"spi"*/); + if (IS_ERR_OR_NULL(data->core_clk)) { + ANC_LOGE("%s: fail to get core_clk", __func__); + return -EPERM; + } + data->iface_clk = clk_get(data->dev, "ipclk_spi" /*"spi_busclk0"*/); + if (IS_ERR_OR_NULL(data->iface_clk)) { + ANC_LOGE("%s: fail to get iface_clk", __func__); + clk_put(data->core_clk); + data->core_clk = NULL; + return -ENOENT; + } + return 0; +} + +static int anc_spi_clk_enable(struct anc_data *data, uint32_t flag) { + int ret_val = 0; + + ANC_LOGD("%s, enter", __func__); + + if (data->clk_enabled) { + ANC_LOGW("%s warning: spi clock is already enabled, return!", __func__); + return 0; + } + + ret_val = clk_prepare_enable(data->core_clk); + if (ret_val) { + ANC_LOGE("%s: fail to enable core_clk", __func__); + return -EPERM; + } + + ret_val = clk_prepare_enable(data->iface_clk); + if (ret_val) { + ANC_LOGE("%s: fail to enable iface_clk", __func__); + clk_disable_unprepare(data->core_clk); + return -ENOENT; + } + + // anc_pm_qos_opp_hold_int_mif(); + + if (ANC_LOW_SPEED == flag) { + spi_clock_set(data, SPI_LOW_SPEED); + } else if (ANC_HIGH_SPEED == flag) { + spi_clock_set(data, SPI_HIGH_SPEED); + } else if (ANC_SUPER_SPEED == flag) { + spi_clock_set(data, SPI_SUPER_SPEED); + } else { + ANC_LOGE("%s: fail to enable clock, flag(%d) is not in range[0-2]", __func__, flag); + } + + data->clk_enabled = 1; + + return 0; +} + +static int anc_spi_clk_disable(struct anc_data *data) { + ANC_LOGD("%s, enter", __func__); + + if (!data->clk_enabled) { + ANC_LOGW("%s warning: spi clock is disabled, return!", __func__); + return 0; + } + + clk_disable_unprepare(data->core_clk); + clk_disable_unprepare(data->iface_clk); + data->clk_enabled = 0; + + // anc_pm_qos_opp_release_int_mif(); + + return 0; +} + +static int anc_spi_clk_deinit(struct anc_data *data) { + ANC_LOGD("%s, enter", __func__); + + if (data->clk_enabled) { + anc_spi_clk_disable(data); + } + + if (!IS_ERR_OR_NULL(data->core_clk)) { + clk_put(data->core_clk); + data->core_clk = NULL; + } + + if (!IS_ERR_OR_NULL(data->iface_clk)) { + clk_put(data->iface_clk); + data->iface_clk = NULL; + } + + return 0; +} + +#if defined(CONFIG_EXYNOS_PM_QOS) || defined(CONFIG_EXYNOS_PM_QOS_MODULE) +static struct exynos_pm_qos_request pm_qos_req_cpu[2]; +static struct exynos_pm_qos_request pm_qos_req_mif; +static struct exynos_pm_qos_request pm_qos_req_int; +#endif + +static int anc_pm_qos_opp_init(void) { + ANC_LOGD("%s: add qos class", __func__); + +#if IS_ENABLED(CONFIG_EXYNOS_PM_QOS) || IS_ENABLED(CONFIG_EXYNOS_PM_QOS_MODULE) + exynos_pm_qos_add_request(&pm_qos_req_cpu[0], PM_QOS_CLUSTER0_FREQ_MIN, 0); + exynos_pm_qos_add_request(&pm_qos_req_cpu[1], PM_QOS_CLUSTER1_FREQ_MIN, 0); + exynos_pm_qos_add_request(&pm_qos_req_mif, PM_QOS_BUS_THROUGHPUT, 0); + exynos_pm_qos_add_request(&pm_qos_req_int, PM_QOS_DEVICE_THROUGHPUT, 0); +#endif + return 0; +} + +static int anc_pm_qos_opp_deinit(void) { + ANC_LOGD("%s: remove qos class", __func__); + +#if IS_ENABLED(CONFIG_EXYNOS_PM_QOS) || IS_ENABLED(CONFIG_EXYNOS_PM_QOS_MODULE) + exynos_pm_qos_remove_request(&pm_qos_req_cpu[0]); + exynos_pm_qos_remove_request(&pm_qos_req_cpu[1]); + exynos_pm_qos_remove_request(&pm_qos_req_mif); + exynos_pm_qos_remove_request(&pm_qos_req_int); +#endif + return 0; +} + +static int anc_pm_qos_opp_hold(void) { + ANC_LOGD("%s: Request MAX QOS", __func__); + +#if IS_ENABLED(CONFIG_EXYNOS_PM_QOS) || IS_ENABLED(CONFIG_EXYNOS_PM_QOS_MODULE) + if (exynos_pm_qos_request_active(&pm_qos_req_int)) { + exynos_pm_qos_update_request(&pm_qos_req_int, INT_MAX); + } + + if (exynos_pm_qos_request_active(&pm_qos_req_mif)) { + exynos_pm_qos_update_request(&pm_qos_req_mif, INT_MAX); + } + + if (exynos_pm_qos_request_active(&pm_qos_req_cpu[0])) { + exynos_pm_qos_update_request(&pm_qos_req_cpu[0], INT_MAX); + } + + if (exynos_pm_qos_request_active(&pm_qos_req_cpu[1])) { + exynos_pm_qos_update_request(&pm_qos_req_cpu[1], INT_MAX); + } +#endif + return 0; +} + +static int anc_pm_qos_opp_release(void) { + ANC_LOGD("%s: Release normal QOS", __func__); + +#if IS_ENABLED(CONFIG_EXYNOS_PM_QOS) || IS_ENABLED(CONFIG_EXYNOS_PM_QOS_MODULE) + if (exynos_pm_qos_request_active(&pm_qos_req_int)) { + exynos_pm_qos_update_request(&pm_qos_req_int, 0); + } + + if (exynos_pm_qos_request_active(&pm_qos_req_mif)) { + exynos_pm_qos_update_request(&pm_qos_req_mif, 0); + } + + if (exynos_pm_qos_request_active(&pm_qos_req_cpu[0])) { + exynos_pm_qos_update_request(&pm_qos_req_cpu[0], 0); + } + + if (exynos_pm_qos_request_active(&pm_qos_req_cpu[1])) { + exynos_pm_qos_update_request(&pm_qos_req_cpu[1], 0); + } +#endif + return 0; +} + +static int anc_pm_qos_opp_hold_int_mif(void) { + ANC_LOGD("%s: Request int and mif MAX QOS", __func__); + +// dma memory frequency update to max +#if IS_ENABLED(CONFIG_EXYNOS_PM_QOS) || IS_ENABLED(CONFIG_EXYNOS_PM_QOS_MODULE) + if (exynos_pm_qos_request_active(&pm_qos_req_int)) { + // exynos_pm_qos_update_request(&pm_qos_req_int, INT_MAX); + exynos_pm_qos_update_request(&pm_qos_req_int, 400000); + } + + if (exynos_pm_qos_request_active(&pm_qos_req_mif)) { + // exynos_pm_qos_update_request(&pm_qos_req_mif, INT_MAX); + exynos_pm_qos_update_request(&pm_qos_req_mif, 2093000); + } +#endif + return 0; +} + +static int anc_pm_qos_opp_release_int_mif(void) { + ANC_LOGD("%s: Release int and mif normal QOS", __func__); + +#if IS_ENABLED(CONFIG_EXYNOS_PM_QOS) || IS_ENABLED(CONFIG_EXYNOS_PM_QOS_MODULE) + if (exynos_pm_qos_request_active(&pm_qos_req_int)) { + exynos_pm_qos_update_request(&pm_qos_req_int, 0); + } + + if (exynos_pm_qos_request_active(&pm_qos_req_mif)) { + exynos_pm_qos_update_request(&pm_qos_req_mif, 0); + } +#endif + return 0; +} +#endif + +static void set_irq_flag(struct anc_data *p_data, bool flag) { + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return; + } + ANC_LOGD("set irq flag mask : %d", flag); + p_data->irq_mask_flag = flag; +} + +static int init_irq(struct anc_data *p_data) { + int ret_val = 0; + + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return -EINVAL; + } + ANC_LOGD("init irq"); + ret_val = anc_irq_init(p_data); + INIT_WORK(&p_data->work_queue, anc_do_irq_work); + + return ret_val; +} + +static void deinit_irq(struct anc_data *p_data) { + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return; + } + + ANC_LOGD("deinit irq"); + anc_irq_deinit(p_data); + cancel_work_sync(&p_data->work_queue); +} + +static int set_product_id(struct anc_data *p_data, const void *arg) { + int ret_val = 0; + + ret_val = anc_copy_from_user(&(p_data->product_info), arg, sizeof(ANC_SENSOR_PRODUCT_INFO)); + if (ret_val != 0) { + ANC_LOGE("Failed to copy product info from user to kernel"); + return ret_val; + } + ANC_LOGD("set product id: %s", p_data->product_info.product_id); + custom_send_command(CUSTOM_COMMAND_SET_PRODUCT_ID, p_data->product_info.product_id); + + return ret_val; +} + +static int get_irq_pin_level(struct anc_data *p_data, uint8_t *pin_level) { + int ret_val = 0; + int irq_pin_levl = 0; + uint8_t irq_pin_levl_u8 = 0; + + irq_pin_levl = gpio_get_value(p_data->irq_gpio); + if (irq_pin_levl >= 0) { + ANC_LOGD("read irq pin level : %d", irq_pin_levl); + } else { + ANC_LOGE("read irq pin level fail, level:%d", irq_pin_levl); + return -EINVAL; + } + + irq_pin_levl_u8 = irq_pin_levl; + ret_val = copy_to_user(pin_level, &irq_pin_levl_u8, sizeof(uint8_t)); + if (ret_val != 0) { + ANC_LOGE("Failed to copy irq pin level from kernel to user"); + return ret_val; + } + ANC_LOGD("get irq pin level:%d", *pin_level); + + return ret_val; +} + +static long anc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { + int ret_val = 0; + struct anc_data *p_data = filp->private_data; + if (p_data == NULL) { + ANC_LOGE("get anc data failed"); + return -EINVAL; + } + + if (_IOC_TYPE(cmd) != ANC_IOC_MAGIC) { + ANC_LOGE("magic number does not match"); + return -ENOTTY; + } + + ANC_LOGD("cmd = %d", _IOC_NR(cmd)); + + switch (cmd) { + case ANC_IOC_RESET: + ret_val = anc_reset(p_data); + break; + case ANC_IOC_ENABLE_POWER: + anc_power_onoff(p_data, true); + break; + case ANC_IOC_DISABLE_POWER: + anc_power_onoff(p_data, false); + break; + case ANC_IOC_CLEAR_FLAG: + custom_send_command(CUSTOM_COMMAND_CLEAR_TOUCH_STATE, NULL); + break; + case ANC_IOC_ENABLE_IRQ: + anc_enable_irq(p_data); + break; + case ANC_IOC_DISABLE_IRQ: + anc_disable_irq(p_data); + break; + case ANC_IOC_SET_IRQ_FLAG_MASK: + set_irq_flag(p_data, true); + break; + case ANC_IOC_CLEAR_IRQ_FLAG_MASK: + set_irq_flag(p_data, false); + break; +#ifdef ANC_USE_REE_SPI + case ANC_IOC_SPI_SPEED: + ret_val = set_spi_speed(arg); + break; +#endif + case ANC_IOC_WAKE_LOCK: + anc_wake_lock(p_data); + break; + case ANC_IOC_WAKE_UNLOCK: + anc_wake_unlock(p_data); + break; + +#ifdef MTK_PLATFORM + case ANC_IOC_ENABLE_SPI_CLK: + spi_clk_enable(true); + break; + case ANC_IOC_DISABLE_SPI_CLK: + spi_clk_enable(false); + break; +#elif defined SAMSUNG_PLATFORM + case ANC_IOC_ENABLE_SPI_CLK: + anc_spi_clk_enable(p_data, ANC_LOW_SPEED); + break; + case ANC_IOC_DISABLE_SPI_CLK: + anc_spi_clk_disable(p_data); + break; +#endif + case ANC_IOC_INIT_IRQ: + ret_val = init_irq(p_data); + break; + case ANC_IOC_DEINIT_IRQ: + deinit_irq(p_data); + break; + case ANC_IOC_REQUEST_RESOURCE: + ret_val = anc_platform_init(p_data); + break; + case ANC_IOC_RELEASE_RESOURCE: + ret_val = anc_platform_free(p_data); + break; +#ifdef ANC_SUPPORT_NAVIGATION_EVENT + case ANC_IOC_REPORT_KEY_EVENT: + ret_val = anc_report_key_event(p_data, (const void *)arg); + break; +#endif + case ANC_IOC_SET_PRODUCT_ID: + ret_val = set_product_id(p_data, (const void *)arg); + break; + case ANC_IOC_GET_IRQ_PIN_LEVEL: + ret_val = get_irq_pin_level(p_data, (uint8_t *)arg); + break; + default: + ret_val = -EINVAL; + break; + } + + return ret_val; +} + +#ifdef CONFIG_COMPAT +static long anc_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { + return anc_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif + +static const struct file_operations anc_fops = { + .owner = THIS_MODULE, + .open = anc_open, + .unlocked_ioctl = anc_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = anc_compat_ioctl, +#endif +#ifdef ANC_USE_REE_SPI + .write = anc_write, + .read = anc_read, +#endif +}; + +static int anc_malloc(struct anc_data **pp_data, struct device *dev) { + if ((pp_data == NULL) || (dev == NULL)) { + ANC_LOGE("parameter is NULL, pp_data:%p, dev:%p", pp_data, dev); + return -EINVAL; + } + + /* Allocate device data */ + *pp_data = devm_kzalloc(dev, sizeof(struct anc_data), GFP_KERNEL); + if (*pp_data == NULL) { + ANC_LOGE("Failed to allocate memory for device data"); + return -ENOMEM; + } + +#ifdef ANC_USE_REE_SPI + /* Allocate SPI transfer DMA buffer */ + spi_buffer = devm_kzalloc(dev, SPI_BUFFER_SIZE, GFP_KERNEL | GFP_DMA); + if (!spi_buffer) { + ANC_LOGE("Failed to allocate memory for spi buffer"); + return -ENOMEM; + } +#endif + + return 0; +} + +static void anc_free(struct anc_data *p_data, struct device *dev) { + if (dev == NULL) { + ANC_LOGE("dev is NULL"); + return; + } + + if (p_data) { + devm_kfree(dev, p_data); + p_data = NULL; + } + +#ifdef ANC_USE_REE_SPI + if (spi_buffer) { + devm_kfree(dev, spi_buffer); + spi_buffer = NULL; + } +#endif +} + +static int anc_create_device(struct anc_data *p_data) { + int ret_val = 0; + struct device *device_ptr = NULL; + + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return -EINVAL; + } + p_data->dev_class = class_create(THIS_MODULE, ANC_DEVICE_NAME); + if (IS_ERR(p_data->dev_class)) { + ANC_LOGE("class_create failed"); + return -ENODEV; + } + + ret_val = alloc_chrdev_region(&p_data->dev_num, 0, 1, ANC_DEVICE_NAME); + if (ret_val < 0) { + ANC_LOGE("Failed to alloc char device region, ret_val: %d", ret_val); + goto device_region_err; + } + ANC_LOGD("Major number of device = %d", MAJOR(p_data->dev_num)); + + device_ptr = device_create(p_data->dev_class, NULL, p_data->dev_num, p_data, ANC_DEVICE_NAME); + if (IS_ERR(device_ptr)) { + ANC_LOGE("Failed to create char device"); + ret_val = -ENODEV; + goto device_create_err; + } + + cdev_init(&p_data->cdev, &anc_fops); + p_data->cdev.owner = THIS_MODULE; + ret_val = cdev_add(&p_data->cdev, p_data->dev_num, 1); + if (ret_val < 0) { + ANC_LOGE("Failed to add char device"); + goto cdev_add_err; + } + + return 0; + +cdev_add_err: + device_destroy(p_data->dev_class, p_data->dev_num); +device_create_err: + unregister_chrdev_region(p_data->dev_num, 1); +device_region_err: + class_destroy(p_data->dev_class); + return ret_val; +} + +static void anc_destroy_device(struct anc_data *p_data) { + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return; + } + + cdev_del(&p_data->cdev); + device_destroy(p_data->dev_class, p_data->dev_num); + unregister_chrdev_region(p_data->dev_num, 1); + class_destroy(p_data->dev_class); +} + +#ifdef ANC_SUPPORT_NAVIGATION_EVENT +static int anc_input_init(struct anc_data *p_data) { + int rc = 0; + + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return -EINVAL; + } + p_data->input = input_allocate_device(); + if (!p_data->input) { + ANC_LOGE("Failed to allocate input device"); + return -ENOMEM; + } + + p_data->input->name = ANC_INPUT_NAME; + __set_bit(EV_KEY, p_data->input->evbit); + __set_bit(KEY_HOME, p_data->input->keybit); + __set_bit(KEY_MENU, p_data->input->keybit); + __set_bit(KEY_BACK, p_data->input->keybit); + __set_bit(KEY_UP, p_data->input->keybit); + __set_bit(KEY_DOWN, p_data->input->keybit); + __set_bit(KEY_LEFT, p_data->input->keybit); + __set_bit(KEY_RIGHT, p_data->input->keybit); + __set_bit(KEY_POWER, p_data->input->keybit); + __set_bit(KEY_WAKEUP, p_data->input->keybit); + __set_bit(KEY_CAMERA, p_data->input->keybit); + + rc = input_register_device(p_data->input); + if (rc) { + ANC_LOGE("Failed to register input device"); + input_free_device(p_data->input); + p_data->input = NULL; + return -ENODEV; + } + + return rc; +} +#endif + +static int anc_parse_dts(struct anc_data *p_data) { + int ret_val = 0; + + if (p_data == NULL) { + ANC_LOGE("p_data is NULL"); + return -EINVAL; + } + p_data->vdd_use_gpio = of_property_read_bool(p_data->dev->of_node, "anc,vdd_use_gpio"); + p_data->vdd_use_pmic = of_property_read_bool(p_data->dev->of_node, "anc,vdd_use_pmic"); + p_data->use_gpio_init = of_property_read_bool(p_data->dev->of_node, "anc,use_gpio_init"); + ANC_LOGD("vdd_use_gpio = %d, vdd_use_pmic = %d, use_gpio_init = %d", + p_data->vdd_use_gpio, + p_data->vdd_use_pmic, + p_data->use_gpio_init); + + if (p_data->vdd_use_pmic) { + vreg_conf.name = ANC_VREG_VDD_NAME; + ret_val = of_property_read_u32_index( + p_data->dev->of_node, ANC_VDD_CONFIG_NAME, 0, &vreg_conf.vmin); + if (ret_val != 0) { + ANC_LOGE("read anc,vdd_config index 0 failed"); + return ret_val; + } + + ret_val = of_property_read_u32_index( + p_data->dev->of_node, ANC_VDD_CONFIG_NAME, 1, &vreg_conf.vmax); + if (ret_val != 0) { + ANC_LOGE("read anc,vdd_config index 1 failed"); + return ret_val; + } + + ret_val = of_property_read_u32_index( + p_data->dev->of_node, ANC_VDD_CONFIG_NAME, 2, &vreg_conf.ua_load); + if (ret_val != 0) { + ANC_LOGE("read anc,vdd_config index 2 failed"); + return ret_val; + } + ANC_LOGD("vreg config: name = %s, vmin = %d, vmax = %d, ua_load = %d", + vreg_conf.name, + vreg_conf.vmin, + vreg_conf.vmax, + vreg_conf.ua_load); + } + + return ret_val; +} + +static int anc_probe(anc_device_t *pdev) { + struct device *dev = &pdev->dev; + struct anc_data *p_data = NULL; + int ret_val = 0; + + ANC_LOGD("entry"); + + ret_val = anc_malloc(&p_data, dev); + if (ret_val != 0) { + goto out_free; + } + p_data->dev = dev; + p_data->resource_requested = false; + g_anc_spi_device = pdev; + p_data->irq_mask_flag = false; + p_data->irq_init = 0; + dev_set_drvdata(dev, p_data); + + ret_val = custom_send_command(CUSTOM_COMMAND_INIT, p_data); + if (ret_val != 0) { + ANC_LOGE("custom init failed"); + goto out_free; + } + + ret_val = anc_parse_dts(p_data); + if (ret_val != 0) { + ANC_LOGE("parse dts failed"); + goto out_deinit_custom; + } + + ret_val = anc_create_device(p_data); + if (ret_val != 0) { + goto out_deinit_custom; + } + + mutex_init(&p_data->lock); + +#ifdef CONFIG_PM_WAKELOCKS + p_data->fp_wakelock = wakeup_source_register(dev, "anc_fp_wakelock"); +#else + wake_lock_init(&p_data->fp_wakelock, WAKE_LOCK_SUSPEND, "anc_fp_wakelock"); +#endif + +#ifdef ANC_SUPPORT_NAVIGATION_EVENT + ret_val = anc_input_init(p_data); + if (ret_val != 0) { + ANC_LOGE("Failed to init input dev"); + goto out_destroy_device; + } +#endif + +#ifdef ANC_USE_REE_SPI + ret_val = anc_config_spi(); + if (ret_val != 0) { + ANC_LOGE("config spi failed"); + goto out; + } +#endif + + ANC_LOGD("Create sysfs path = %s", (&dev->kobj)->name); + ret_val = sysfs_create_group(&dev->kobj, &attribute_group); + if (ret_val != 0) { + ANC_LOGE("Could not create sysfs"); + goto out; + } + + ANC_LOGD("Success"); + return 0; + +out: +#ifdef ANC_SUPPORT_NAVIGATION_EVENT + input_unregister_device(p_data->input); +out_destroy_device: +#endif + anc_destroy_device(p_data); +out_deinit_custom: + custom_send_command(CUSTOM_COMMAND_DEINIT, NULL); +out_free: + anc_free(p_data, dev); + ANC_LOGE("Probe Failed, ret_val = %d", ret_val); + return ret_val; +} + +static int anc_remove(anc_device_t *pdev) { + struct anc_data *p_data = dev_get_drvdata(&pdev->dev); + if (p_data == NULL) { + ANC_LOGE("get data handle failed"); + return -EINVAL; + } + + sysfs_remove_group(&pdev->dev.kobj, &attribute_group); + mutex_destroy(&p_data->lock); + +#ifdef CONFIG_PM_WAKELOCKS + wakeup_source_unregister(p_data->fp_wakelock); +#else + wake_lock_destroy(&p_data->fp_wakelock); +#endif + + custom_send_command(CUSTOM_COMMAND_DEINIT, NULL); + anc_destroy_device(p_data); + anc_free(p_data, &pdev->dev); + return 0; +} + +static void anc_shutdown(anc_device_t *pdev) { + struct anc_data *p_data = NULL; + + ANC_LOGD("entry"); + p_data = dev_get_drvdata(&pdev->dev); + if (p_data == NULL) { + ANC_LOGE("get data handle failed"); + return; + } + + anc_power_onoff(p_data, false); +} + +static struct of_device_id anc_of_match[] = { + { + .compatible = ANC_COMPATIBLE_SW_FP, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, anc_of_match); + +static anc_driver_t anc_driver = { + .driver = + { + .name = ANC_DEVICE_NAME, + .owner = THIS_MODULE, + .of_match_table = anc_of_match, +#if defined(MTK_PLATFORM) + .bus = &spi_bus_type, +#endif + }, + .probe = anc_probe, + .remove = anc_remove, + .shutdown = anc_shutdown, +}; + +static int __init ancfp_init(void) { + int ret_val = 0; + + ANC_LOGD("entry"); + +#if defined(ANC_USE_REE_SPI) || defined(MTK_PLATFORM) + ret_val = spi_register_driver(&anc_driver); +#else + ret_val = platform_driver_register(&anc_driver); +#endif + if (!ret_val) { + ANC_LOGD("success"); + } else { + ANC_LOGE("register spi driver failed, ret_val:%d", ret_val); + return ret_val; + } + + anc_netlink_init(); + return ret_val; +} + +static void __exit ancfp_exit(void) { + ANC_LOGD("entry"); + + anc_netlink_exit(); +#if defined(ANC_USE_REE_SPI) || defined(MTK_PLATFORM) + spi_unregister_driver(&anc_driver); +#else + platform_driver_unregister(&anc_driver); +#endif +} + +module_init(ancfp_init); +module_exit(ancfp_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("JIIOV"); +MODULE_DESCRIPTION("JIIOV fingerprint sensor device driver"); diff --git a/drivers/input/misc/anc_fps_mmi/jiiov_platform.h b/drivers/input/misc/anc_fps_mmi/jiiov_platform.h new file mode 100644 index 000000000000..257d7cc3bbda --- /dev/null +++ b/drivers/input/misc/anc_fps_mmi/jiiov_platform.h @@ -0,0 +1,30 @@ +/** + * User space driver API for JIIOV's fingerprint sensor. + * + * Copyright (C) 2020 JIIOV Corporation. + * + * 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 __JIIOV_PLATFORM_H__ +#define __JIIOV_PLATFORM_H__ + +#define ANC_WAKELOCK_HOLD_TIME 500 /* ms */ +typedef enum { + CUSTOM_COMMAND_INIT, + CUSTOM_COMMAND_CLEAR_TOUCH_STATE, + CUSTOM_COMMAND_SET_PRODUCT_ID, + CUSTOM_COMMAND_DEINIT, +} CUSTOM_COMMAND; + +int custom_send_command(CUSTOM_COMMAND cmd, void *p_param); + +#endif /* __JIIOV_PLATFORM_H__ */ \ No newline at end of file diff --git a/drivers/input/misc/bos0614_mmi/Android.mk b/drivers/input/misc/bos0614_mmi/Android.mk new file mode 100644 index 000000000000..a8a426802f45 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/Android.mk @@ -0,0 +1,10 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := bos0614_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/misc/bos0614_mmi/Kbuild b/drivers/input/misc/bos0614_mmi/Kbuild new file mode 100644 index 000000000000..abbef9d908e4 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/Kbuild @@ -0,0 +1,14 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/bos0614_mmi \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/bos0614_mmi/libs/dk-core/src \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/bos0614_mmi/libs/dk-core + +EXTRA_CFLAGS += -DMOTOROLA +EXTRA_CFLAGS += -DLITTLE_ENDIAN=1234 -DBYTE_ORDER=LITTLE_ENDIAN + +obj-m := bos0614_mmi.o +bos0614_mmi-objs := i2cLinux.o bosDriver.o +bos0614_mmi-objs += libs/dk-core/src/bsp/drivers/haptic/bos0614Driver.o +bos0614_mmi-objs += libs/dk-core/contribs/comm-stack-dk/data.o diff --git a/drivers/input/misc/bos0614_mmi/LICENSE b/drivers/input/misc/bos0614_mmi/LICENSE new file mode 100644 index 000000000000..d159169d1050 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/drivers/input/misc/bos0614_mmi/Makefile b/drivers/input/misc/bos0614_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/misc/bos0614_mmi/README.md b/drivers/input/misc/bos0614_mmi/README.md new file mode 100644 index 000000000000..4c8199a5b4da --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/README.md @@ -0,0 +1,177 @@ +# BOS0614 Kernel Driver + +![GitHub Logo](/images/logo.png) + +_Version: 1.1.0_ + +_Copyright (c) 2021 Boreas Technologies All rights reserved._ + +_GNU GENERAL PUBLIC V2 LICENSE_ + +Allow to configure the button press & release detection with feedback generated with the synthesizer. This linux kernel +driver provides a file system API (String base). The binary file API & GPIO configuration will be supported in the +subsequent release. + +A device tree support example is available within the dts folder. The default configuration enables the button behaviour +for a specific set of channel on driver probe. This allows the system to remain in a knows state at boot up. + +BOS0614 Revision. B & C are supported by this driver. + +_Tested against the Linux Kernel V4.14_ + +## File System API (user space) + +### chip_id + +_Access: Read only_ + +Return the chip id in hexadecimal value + +* 0x0D: BOS0614 Rev B + +### sensing_config + +_Access: Write only_ + +Configure the hardware press or release detection on chip + +**Arguments** + +* channel id : The channel number starting from 0 +* mode : The detection mode such threshold (0) or slope (1) detection +* direction: The button direction such press (0) or release (1) +* debouncing: The debouncing duration in microseconds +* stabilization: The stabilization duration in milliseconds + +### sensing_autoplay + +_Access: Write only_ + +Setup autoplay configuration. The autoplay mechanism allows the BOS0614 to play automatically a feedback once a press or +release event is detected. + +**Arguments** + +* channel id : The channel number starting from 0 +* waveform id: The waveform id to play as a feedback +* direction: The button direction which the specified feedback will be played + +### sensing_stop + +_Access: Write only_ + +Deactivate the button detection for a specific channel & direction + +**Arguments** + +* channel id : The channel number starting from 0 + +### set_slice + +_Access: Write only_ + +Create a slice (simple waveform). Slices are allocated into the BOS0614 RAM using the slice id. If you declare multiple +time the same slice id, only the latest set slice configuration will remain into the RAM. + +**Arguments** + +* slice id: The slice id (must be unique) +* amplitude: The sinus amplitude in milliVolt +* frequency: The sinus frequency in milliHertz. +* cycle: Duration of the sinus in period (only integer) +* channel mask: The list of channel to play the waveform. Each bit represent a channel #. The LSB reprensents the + channel #0 and the fourth bit (from LSB) the channel #4. The maximum value is 15d. + +### set_waveform + +_Access: Write only_ + +Create a waveform by composing it with slices. Same as the slice. Waveforms are allocated into the BOS0614 RAM. Waveform +unicity is based on the waveform id. + +**Arguments** + +* waveform id: The waveform id +* start slice id: The first slice of the sequence to play +* number of slice: The number of slice to include starting from the start slice id param. +* cycle: The amount of time the sequence of slices is repeated +* channel mask: The list of channel to play the waveform. Each bit represent a channel #. The LSB reprensents the + channel #0 and the fourth bit (from LSB) the channel #4. The maximum value is 15d. + +### ctrl_output + +_Access: Write only_ + +Control the BOS0614 output drive + +**Arguments** + +*state: Output state: On (1) and Off (0) + +### ic_errors + +_Access: Read only_ + +Read the IC operational status. Operational status are defined in the datasheet +> SC: 0 UVLO: 0 IDAC: 0 MAX_POWER: 0 OVT: 0 OVV: 0 + +### synth_play + +_Access: Write only_ + +Arm the synthesizer for playing a specific sequence of waveforms. The waveform sequence is continuous sequence. + +**Arguments** + +* start: The waveform id to start with. +* stop: The waveform id to end with (inclusively). + +## Virtual Button Example - on chip detection + +This example configures the button detection mechanism on the channel #0 with a specific feedback for the press and one +for the release event. This example uses an i2c device instance on the bus i2c #1 with the address 0x002c. + +Note: The stabilization slice is not required anymore with this bos0614 revision C. The stabilization setting accept +values between 0 & 103 ms for the release and press event. + +### 1. Configure the feedback for press event. + +Press Feedback: + +Press Slice (Slice #0) : 60 volts amplitude @175 Hz 2 Cycle on channel #0 + +`echo 0 60000 175000 2 1 > /sys/bus/i2c/devices/1-002c/set_slice` + +Setup the waveform #0 using the two previous slices + +`echo 0 0 2 1 1 > /sys/bus/i2c/devices/1-002c/set_waveform` + +### 2. Configure the feedback for release event. + +Release Slice (Slice #2) : 60 volts amplitude @170 Hz 2 Cycle on channel #0 + +`echo 2 60000 170000 2 1 > /sys/bus/i2c/devices/1-002c/set_slice` + +Setup the waveform #1 using the two previous slices + +`echo 1 2 1 1 1 >/sys/bus/i2c/devices/1-002c/set_waveform` + +### 3. Activate Press Detection and Autoplay on button press event + +Setup press detection with threshold mode, 1.2 Volt threshold, 4000 uS debounce time and 25 milliseconds stabilization + +`echo 0 0 0 4000 1200 25 > /sys/bus/i2c/devices/1-002c/sensing_config` + +`echo 0 0 0 >/sys/bus/i2c/devices/1-002c/sensing_autoplay` + +### 4. Activate Release Detection and Autoplay on button release event + +Setup release detection with slope mode, -11 mV gradient, 8000 uS debounce time and 32 milliseconds stabilization + +`echo 0 1 1 8000 -11 32 > /sys/bus/i2c/devices/1-002c/sensing_config` + +`echo 0 1 1 > /sys/bus/i2c/devices/1-002c/sensing_autoplay` + + + + diff --git a/drivers/input/misc/bos0614_mmi/bosDriver.c b/drivers/input/misc/bos0614_mmi/bosDriver.c new file mode 100644 index 000000000000..725d9704b98a --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/bosDriver.c @@ -0,0 +1,1024 @@ +// +// Description: Linux Boréas Haptic Driver for I2C base IC +// +// Copyright (c) 2021 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +#define pr_fmt(fmt) "bos0614: %s: " fmt, __func__ + +#define DEBUG +//#undef DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libs/dk-core/src/bsp/drivers/haptic/bosDriver.h" +#include "i2cLinux.h" +#include "libs/dk-core/src/bsp/drivers/i2c/i2c.h" +#include "libs/dk-core/src/bsp/drivers/haptic/bos0614Driver.h" + +#ifdef DEBUG +#undef pr_debug +#define pr_debug pr_err +#undef dev_dbg +#define dev_dbg dev_info +#endif +#define MAXIMUM_SLICE_CONF (4) + +typedef struct +{ + SynthSlice slice[MAXIMUM_SLICE_CONF]; + SensingConfig detection; + size_t nbrSlice; +} HwButtonFeedback; + +typedef struct +{ + bool present; + ChannelId channelId; + SensingConfig detection[SENSING_DIRECTION_ENUM_LENGTH]; +} Channel; + +typedef struct +{ + HwButtonFeedback feedback[SENSING_DIRECTION_ENUM_LENGTH]; + size_t nextSliceId; + Channel channel[BOS0614_NBR_OF_CHANNEL]; +} DefaultButtonConfig; + +typedef struct +{ + I2c *i2c; + const struct i2c_device_id *id; + struct mutex lock; + struct device *dev; + HapticDriver *hapticDriver; + DefaultButtonConfig defaultButtonConfig; +} Context; + +#define SET_WAVEFORM_PARAM_LENGTH (5) +#define SET_SLICE_PARAM_LENGTH (5) +#define SET_SHAPED_SLICE_PARAM_LENGTH (6) +#define SYNTH_PLAY_PARAM_LENGTH (2) +#define CTRL_OUTPUT_PARAM_LENGTH (1) +#define SENSING_CONFIG_PARAM_LENGTH (6) +#define SENSING_AUTO_FEEDBACK_PARAM_LENGTH (3) +#define STOP_SENSING_PARAM_LENGTH (1) + +#define ATTRIBUTE_CHANNEL_PREFIX_LENGTH (50) + +#define FEEDBACK_SLICE_LENGTH (5) + +static ssize_t getChipId(struct device *dev, + struct device_attribute *attr, char *buf) +{ + Context *ctx = dev_get_drvdata(dev); + u32 chipId = 0; + (void) attr; + + if (ctx != NULL) + { + mutex_lock(&ctx->lock); + + if (ctx->hapticDriver != NULL) + { + chipId = ctx->hapticDriver->getChipId(ctx->hapticDriver); + } + + mutex_unlock(&ctx->lock); + } + + return snprintf(buf, PAGE_SIZE, "0x%x\n", chipId); +} + +static DEVICE_ATTR(chip_id, 0444, getChipId, NULL); + +static ssize_t getRegsDump(struct device *dev, + struct device_attribute *attr, char *buf) +{ + Context *ctx = dev_get_drvdata(dev); + ssize_t blen = 0; + (void) attr; + + if (ctx != NULL) + { + mutex_lock(&ctx->lock); + + if (ctx->hapticDriver != NULL) + { + bool res; + u16 r, rval; + /* dump first 42 registers */ + for (r = 0; r < ADDRESS_BOS0614_BIST_REG; r++) { + rval = 0x2bad; + res = ctx->hapticDriver->getRegister(ctx->hapticDriver, r, &rval); + blen += snprintf(buf + blen, PAGE_SIZE - blen, + "%02X: 0x%02X 0x%02X\n", r, + (rval & 0xFF00) >> 8, + (rval & 0x00FF)); + } + } + + mutex_unlock(&ctx->lock); + } + + return blen; +} + +static DEVICE_ATTR(regs, 0444, getRegsDump, NULL); + +static bool hasError(BosError *errors, size_t length, BosError errorType) +{ + size_t index; + bool res = false; + + if (errors != NULL) + { + for (index = 0; index < length; index++) + { + if (errors[index] == errorType) + { + res = true; + break; + } + } + } + + return res; +} + + +static ssize_t getIcErrors(struct device *dev, + struct device_attribute *attr, char *buf) +{ + Context *ctx = dev_get_drvdata(dev); + ssize_t res = -EIO; + + if (ctx != NULL && ctx->hapticDriver != NULL) + { + HapticDriver *driver = ctx->hapticDriver; + size_t nbrErros; + BosError *errors; + + errors = kzalloc(sizeof(BosError) * BOS_ERROR_LENGTH, GFP_KERNEL); + if (!errors) + return res; + + mutex_lock(&ctx->lock); + + nbrErros = driver->getError(driver, errors, BOS_ERROR_LENGTH); + + res = snprintf(buf, PAGE_SIZE, "SC: %d UVLO: %d IDAC: %d MAX_POWER: %d OVT: %d OVV: %d\n", + hasError(errors, nbrErros, BOS_ERROR_SHORT_CIRCUIT), + hasError(errors, nbrErros, BOS_ERROR_UVLO), + hasError(errors, nbrErros, BOS_ERROR_IDAC), + hasError(errors, nbrErros, BOS_ERROR_MAX_POWER), + hasError(errors, nbrErros, BOS_ERROR_OVT), + hasError(errors, nbrErros, BOS_ERROR_OVV)); + + kfree(errors); + + mutex_unlock(&ctx->lock); + } + + return res; +} + +static DEVICE_ATTR(ic_errors, 0444, getIcErrors, NULL); + +static ssize_t getIcSensingState(struct device *dev, + struct device_attribute *attr, char *buf) +{ + Context *ctx = dev_get_drvdata(dev); + ssize_t res = -EIO; + + if (ctx != NULL && ctx->hapticDriver != NULL) + { + HapticDriver *driver = ctx->hapticDriver; + uint16_t regVal = 0; + + mutex_lock(&ctx->lock); + driver->getRegister(driver, ADDRESS_BOS0614_SENSECONFIG_REG, ®Val); + mutex_unlock(&ctx->lock); + + res = snprintf(buf, PAGE_SIZE, "%d\n", (regVal & 0x000F) ? 1 : 0); + } + + return res; +} + +static DEVICE_ATTR(is_sensing, 0444, getIcSensingState, NULL); + +static ssize_t setSynthWaveform(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + Context *ctx = dev_get_drvdata(dev); + ssize_t res = -EIO; + (void) attr; + + if (ctx != NULL && ctx->hapticDriver != NULL) + { + HapticDriver *driver = ctx->hapticDriver; + WaveformId id = 0; + uint8_t startSliceId = 0; + size_t nbrOfSlices = 0; + uint16_t cycle = 0; + uint8_t outputChannel = 0; + size_t paramLength = 0; + int numP = 0; + ParamsLst params[SET_WAVEFORM_PARAM_LENGTH]; + + PARAM_ADD(PARAM_UCHAR8, &id); + PARAM_ADD(PARAM_UCHAR8, &startSliceId); + PARAM_ADD(PARAM_UINT32, &nbrOfSlices); + PARAM_ADD(PARAM_UINT16, &cycle); + PARAM_ADD(PARAM_UCHAR8, &outputChannel); + + paramLength = process_params(params, numP, buf); + if (paramLength != SET_WAVEFORM_PARAM_LENGTH) { + return (ssize_t)paramLength; + } + + dev_dbg(ctx->dev, + "[Set Synth Waveform] Waveform Id: %d Start Slice Id: %d Nbr Of Slices: %zu Cycle: %d Output Channel: %d\n", + id, startSliceId, nbrOfSlices, cycle, outputChannel); + + mutex_lock(&ctx->lock); + + if (driver->synthSetWaveform(driver, id, startSliceId, nbrOfSlices, cycle, outputChannel)) + { + res = count; + } + + mutex_unlock(&ctx->lock); + } + + return res; +} + +static DEVICE_ATTR(set_waveform, 0220, NULL, setSynthWaveform); + +static ssize_t setSynthSlice(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + Context *ctx = dev_get_drvdata(dev); + ssize_t res = -EIO; + (void) attr; + + if (ctx != NULL && ctx->hapticDriver != NULL) + { + HapticDriver *driver = ctx->hapticDriver; + SynthSlice slice = {0}; + uint8_t outputChannel = 0; + size_t paramLength = 0; + uint8_t shape = 0; + int numP = 0; + ParamsLst params[SET_SHAPED_SLICE_PARAM_LENGTH]; + + PARAM_ADD(PARAM_UINT32, &slice.sliceId); + PARAM_ADD(PARAM_INT32, &slice.mVAmp); + PARAM_ADD(PARAM_UINT32, &slice.mHzFreq); + PARAM_ADD(PARAM_UINT32, &slice.cycle); + PARAM_ADD(PARAM_UCHAR8, &outputChannel); + PARAM_ADD(PARAM_UCHAR8, &shape); + + paramLength = process_params(params, numP, buf); + if (paramLength != SET_SLICE_PARAM_LENGTH && + paramLength != SET_SHAPED_SLICE_PARAM_LENGTH) { + return (ssize_t)paramLength; + } + + dev_dbg(ctx->dev, + "[Set Slice] Slice Id: %d Amplitude: %d mV Frequency: %d milliHertz Cycle: %d Shape: %d Output Channel: %d\n", + slice.sliceId, slice.mVAmp, slice.mHzFreq, slice.cycle, shape, outputChannel); + + mutex_lock(&ctx->lock); + + if (driver->synthSetSlice(driver, (const SynthSlice *) &slice, shape, outputChannel)) + { + res = count; + } + + mutex_unlock(&ctx->lock); + } + + return res; +} + +static DEVICE_ATTR(set_slice, 0220, NULL, setSynthSlice); + + +static ssize_t synthPlay(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + Context *ctx = dev_get_drvdata(dev); + ssize_t res = -EIO; + (void) attr; + + if (ctx != NULL && ctx->hapticDriver != NULL) + { + HapticDriver *driver = ctx->hapticDriver; + WaveformId start = 0; + WaveformId stop = 0; + size_t paramLength = 0; + int numP = 0; + ParamsLst params[SYNTH_PLAY_PARAM_LENGTH]; + + PARAM_ADD(PARAM_UCHAR8, &start); + PARAM_ADD(PARAM_UCHAR8, &stop); + + paramLength = process_params(params, numP, buf); + if (paramLength != SYNTH_PLAY_PARAM_LENGTH) { + return (ssize_t)paramLength; + } + + mutex_lock(&ctx->lock); + + if (driver->wfsPlay(driver, start, stop)) + { + res = count; + } + + mutex_unlock(&ctx->lock); + } + + return res; +} + +static DEVICE_ATTR(synth_play, 0220, NULL, synthPlay); + +static ssize_t setOutput(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + Context *ctx = dev_get_drvdata(dev); + ssize_t res = -EIO; + (void) attr; + + if (ctx != NULL && ctx->hapticDriver != NULL) + { + HapticDriver *driver = ctx->hapticDriver; + int outputState = 0; + size_t paramLength = 0; + bool bOutputState; + int numP = 0; + ParamsLst params[CTRL_OUTPUT_PARAM_LENGTH]; + + PARAM_ADD(PARAM_INT32, &outputState); + + paramLength = process_params(params, numP, buf); + if (paramLength != CTRL_OUTPUT_PARAM_LENGTH) { + return (ssize_t)paramLength; + } + + bOutputState = outputState > 0 ? true : false; + + mutex_lock(&ctx->lock); + + if (driver->ctrlOutput(driver, bOutputState)) + { + dev_dbg(ctx->dev, + "[Set Output] State: %d \n", bOutputState); + res = count; + } + + mutex_unlock(&ctx->lock); + } + + return res; +} + +static DEVICE_ATTR(ctrl_output, 0220, NULL, setOutput); + +const char *slopeDetection = "Slope"; +const char *thresholdDetection = "Threshold"; + +static const char *getSensingDetectionMode(SensingDetectionMode mode) +{ + const char *stringDetectionMode = NULL; + switch (mode) + { + case SENSING_DETECTION_MODE_SLOPE: + stringDetectionMode = slopeDetection; + break; + case SENSING_DETECTION_MODE_THRESHOLD: + stringDetectionMode = thresholdDetection; + break; + } + + return stringDetectionMode; +} + +const char *sensingPressDirection = "Press"; +const char *sensingReleaseDirection = "Release"; + +static const char *getSensingDirection(SensingDirection direction) +{ + const char *directionString = NULL; + + switch (direction) + { + case SENSING_DIRECTION_PRESS: + directionString = sensingPressDirection; + break; + case SENSING_DIRECTION_RELEASE: + directionString = sensingReleaseDirection; + break; + case SENSING_DIRECTION_ENUM_LENGTH: + directionString = NULL; + break; + } + + return directionString; +} + +static ssize_t setSensingConfig(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + Context *ctx = dev_get_drvdata(dev); + ssize_t res = -EIO; + (void) attr; + + if (ctx != NULL && ctx->hapticDriver != NULL) + { + HapticDriver *driver = ctx->hapticDriver; + SensingConfig config = {0}; + ChannelId channelId = 0; + size_t paramLength = 0; + const char *directionS; + const char *modeS; + int numP = 0; + ParamsLst params[SENSING_CONFIG_PARAM_LENGTH]; + + PARAM_ADD(PARAM_UCHAR8, &channelId); + PARAM_ADD(PARAM_UCHAR8, &config.mode); + PARAM_ADD(PARAM_UCHAR8, &config.direction); + PARAM_ADD(PARAM_UINT16, &config.debounceUs); + PARAM_ADD(PARAM_INT16, &config.thresholdMv); + PARAM_ADD(PARAM_UCHAR8, &config.stabilisationMs); + + paramLength = process_params(params, numP, buf); + if (paramLength != SENSING_CONFIG_PARAM_LENGTH) { + return (ssize_t)paramLength; + } + + directionS = getSensingDirection(config.direction); + modeS = getSensingDetectionMode(config.mode); + + mutex_lock(&ctx->lock); + + if (directionS != NULL && modeS != NULL && + driver->buttonSensing(driver, channelId, config) && + driver->configGPO(driver, channelId, GPO_BUTTON_STATE)) + { + dev_dbg(ctx->dev, + "[Sensing config] Success Channel #: %d Mode: %s Direction: %s Debouncing Duration: %d microS Threshold: %d Stabilization Duration: %d ms\n", + channelId, modeS, directionS, config.debounceUs, + config.thresholdMv, config.stabilisationMs); + res = count; + } + + mutex_unlock(&ctx->lock); + } + + return res; +} + +static DEVICE_ATTR(sensing_config, 0220, NULL, setSensingConfig); + +static ssize_t setSensingAutoPlay(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + Context *ctx = dev_get_drvdata(dev); + ssize_t res = -EIO; + (void) attr; + + if (ctx != NULL && ctx->hapticDriver != NULL) + { + HapticDriver *driver = ctx->hapticDriver; + ChannelId channelId = 0; + WaveformId id = 0; + SensingDirection direction = 0; + size_t paramLength = 0; + const char *directionS; + int numP = 0; + ParamsLst params[SENSING_AUTO_FEEDBACK_PARAM_LENGTH]; + + PARAM_ADD(PARAM_UCHAR8, &channelId); + PARAM_ADD(PARAM_UCHAR8, &id); + PARAM_ADD(PARAM_UCHAR8, &direction); + + paramLength = process_params(params, numP, buf); + if (paramLength != SENSING_AUTO_FEEDBACK_PARAM_LENGTH) { + return (ssize_t)paramLength; + } + + directionS = getSensingDirection(direction); + + mutex_lock(&ctx->lock); + + if (directionS != NULL && + driver->sensingAutoPlayWave(driver, channelId, id, direction)) + { + dev_dbg(ctx->dev, "[Set Sensing Automatic Feedback] Channel #: %d Waveform #: %d Direction: %s", channelId, + id, directionS); + res = count; + } + + mutex_unlock(&ctx->lock); + } + + + return res; +} + +static DEVICE_ATTR(sensing_autoplay, 0220, NULL, setSensingAutoPlay); + +static ssize_t stopSensing(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + Context *ctx = dev_get_drvdata(dev); + ssize_t res = -EIO; + (void) attr; + + if (ctx != NULL && ctx->hapticDriver != NULL) + { + HapticDriver *driver = ctx->hapticDriver; + ChannelId channelId = 0; + size_t paramLength = 0; + int numP = 0; + ParamsLst params[STOP_SENSING_PARAM_LENGTH]; + + PARAM_ADD(PARAM_UCHAR8, &channelId); + + paramLength = process_params(params, numP, buf); + if (paramLength != STOP_SENSING_PARAM_LENGTH) { + return (ssize_t)paramLength; + } + + mutex_lock(&ctx->lock); + + if (driver->stopSensing(driver, channelId)) + { + dev_dbg(ctx->dev, "[Remove Sensing Profile] Channel #: %d", channelId); + res = count; + } + + + mutex_unlock(&ctx->lock); + } + + + return res; +} + +static DEVICE_ATTR(sensing_stop, 0220, NULL, stopSensing); +#define FREQ_INDEX (0) +#define MV_AMP_INDEX (1) +#define CYCLE_INDEX (2) + +static bool +decodeSlicesDefaultConfig(Context* ctx, const char* attr, HwButtonFeedback* feedback) +{ + bool res = false; + + u32 dataFeedback[4 * FEEDBACK_SLICE_LENGTH]; + + if (attr) + { + int count = of_property_read_variable_u32_array(ctx->dev->of_node, + attr, dataFeedback, FEEDBACK_SLICE_LENGTH, sizeof(dataFeedback)); + + if (count > 0 && (count % FEEDBACK_SLICE_LENGTH) == 0) + { + uint sliceId, baseIndex; + SynthSlice *slice; + uint nbrSlice = count / FEEDBACK_SLICE_LENGTH; + + feedback->nbrSlice = 0; + + for (sliceId = 0; sliceId < nbrSlice && nbrSlice < ARRAY_SIZE(feedback->slice); sliceId++) + { + baseIndex = sliceId * FEEDBACK_SLICE_LENGTH; + + slice = &feedback->slice[sliceId]; + feedback->nbrSlice++; + + slice->sliceId = ctx->defaultButtonConfig.nextSliceId++; + slice->mHzFreq = dataFeedback[baseIndex + FREQ_INDEX]; + slice->mVAmp = dataFeedback[baseIndex + MV_AMP_INDEX]; + slice->cycle = dataFeedback[baseIndex + CYCLE_INDEX]; + + dev_dbg(ctx->dev, "Slice id: %d Freq: %d (milliHertz) Amplitude: %d (mV) Cycle: %d\n", + slice->sliceId, slice->mHzFreq, slice->mVAmp, slice->cycle); + } + + res = true; + } + } + + return res; +} + + +#define BUTTON_CONFIG_ELEMENT_LENGTH (5) + +#define DIRECTION_INDEX (0) +#define DETECTION_MODE_INDEX (1) +#define THRESHOLD_INDEX (2) +#define DEBOUNCING_INDEX (3) +#define STABILISATION_INDEX (4) + +static bool +decodeDetectionDefaultConf(Context* ctx, const char* channelAttr, const char* suffixAttr, SensingConfig* conf) +{ + bool res = false; + + char attr[ATTRIBUTE_CHANNEL_PREFIX_LENGTH]; + size_t expectedLength = strlen(channelAttr) + strlen(suffixAttr); + + u32 buttonConf[BUTTON_CONFIG_ELEMENT_LENGTH * 10]; + + if (expectedLength < ATTRIBUTE_CHANNEL_PREFIX_LENGTH && + sprintf(attr, "%s,%s", channelAttr, suffixAttr)) + { + const char *directionS; + const char *modeS; + int count = of_property_read_variable_u32_array(ctx->dev->of_node, attr, buttonConf, + BUTTON_CONFIG_ELEMENT_LENGTH, + sizeof(buttonConf)); + + if (count > 0 && (count % BUTTON_CONFIG_ELEMENT_LENGTH) == 0) + { + conf->direction = buttonConf[DIRECTION_INDEX]; + conf->mode = buttonConf[DETECTION_MODE_INDEX]; + conf->thresholdMv = (int16_t) buttonConf[THRESHOLD_INDEX]; + conf->debounceUs = buttonConf[DEBOUNCING_INDEX]; + conf->stabilisationMs = buttonConf[STABILISATION_INDEX]; + + directionS = getSensingDirection(conf->direction); + modeS = getSensingDetectionMode(conf->mode); + + if (directionS != NULL && modeS != NULL) + { + dev_dbg(ctx->dev, + "Mode: %s Direction: %s Debouncing Duration: %d μs Threshold: %d Stabilization Duration: %d ms\n", + modeS, directionS, conf->debounceUs, + conf->thresholdMv, conf->stabilisationMs); + res = true; + } + } + } + + return res; +} + +const char* feedbackPress = "button,feedback,press"; +const char* feedbackRelease = "button,feedback,release"; + +const char* buttonPress = "button,press"; +const char* buttonRelease = "button,release"; + +static bool readDefaultConfiguration(Context* ctx) +{ + bool res = true; + uint channel; + + ctx->defaultButtonConfig.nextSliceId = 0; + + //Decode feedbacks + res = res && decodeSlicesDefaultConfig(ctx, feedbackPress, + &ctx->defaultButtonConfig.feedback[SENSING_DIRECTION_PRESS]); + res = res && decodeSlicesDefaultConfig(ctx, feedbackRelease, + &ctx->defaultButtonConfig.feedback[SENSING_DIRECTION_RELEASE]); + + for (channel = 0; channel < BOS0614_NBR_OF_CHANNEL; channel++) + { + char attrChannel[ATTRIBUTE_CHANNEL_PREFIX_LENGTH]; + const char* status; + + sprintf(attrChannel, "channel,%d", channel); + + if (of_property_read_string(ctx->dev->of_node, attrChannel, &status) == 0) + { + ctx->defaultButtonConfig.channel[channel].present = strcmp(status, "active") == 0; + } + else + { + dev_warn(ctx->dev, "No default configuration for channel %d\n", channel); + } + + + if (ctx->defaultButtonConfig.channel[channel].present) + { + dev_dbg(ctx->dev, "Channel %d is Activated\n", channel); + + res = res && decodeDetectionDefaultConf(ctx, attrChannel, buttonPress, + &ctx->defaultButtonConfig.channel[channel].detection[SENSING_DIRECTION_PRESS]); + res = res && decodeDetectionDefaultConf(ctx, attrChannel, buttonRelease, + &ctx->defaultButtonConfig.channel[channel].detection[SENSING_DIRECTION_RELEASE]); + } + else + { + dev_dbg(ctx->dev, "Channel %d is Deactivated\n", channel); + } + } + + return res; +} + +#define IGNORE_OUPUT_CHANNEL (0) +#define DEFAULT_PRESS_WAVEFORM_ID (0) +#define DEFAULT_RELEASE_WAVEFORM_ID (1) + + +static bool applyingFeedbackButtonConf(Context* ctx, HwButtonFeedback* feedbacks, WaveformId waveformId) +{ + bool res = true; + uint sliceId; + HapticDriver *driver; + + if (feedbacks->nbrSlice == 0) + { + dev_err(ctx->dev, "No default slices detected for the HW button feedbacks related to waveform id %d", + waveformId); + res = false; + } + + driver = ctx->hapticDriver; + + //Slice configuration + for (sliceId = 0; sliceId < feedbacks->nbrSlice; sliceId++) + { + res = res && driver->synthSetSlice(driver, &feedbacks->slice[sliceId], SHAPE_NO_SHAPE, IGNORE_OUPUT_CHANNEL); + pr_debug("set_slice: %d\n", sliceId); + } + + res = res && driver->synthSetWaveform(driver, waveformId, feedbacks->slice->sliceId, feedbacks->nbrSlice, 1, + IGNORE_OUPUT_CHANNEL); + + if (!res) + { + dev_err(ctx->dev, "Failed to configure the HW button feedbacks for waveform id %d", waveformId); + } + + return res; +} + +static bool applyButtonSensingConfiguration(Context* ctx, uint buttonId, SensingConfig* conf, WaveformId waveformId) +{ + + bool res = true; + + HapticDriver* driver = ctx->hapticDriver; + + pr_debug("enter\n"); + res = res && driver->buttonSensing(driver, buttonId, *conf); + res = res && driver->sensingAutoPlayWave(driver, buttonId, waveformId, conf->direction); + + if (!res) + { + dev_err(ctx->dev, "Failed to configure the HW button sensing for button id %d \n", buttonId); + } + + return res; +} + +static bool applyDefaultConfiguration(Context* ctx) +{ + uint buttonId; + bool res = applyingFeedbackButtonConf(ctx, &ctx->defaultButtonConfig.feedback[SENSING_DIRECTION_PRESS], + DEFAULT_PRESS_WAVEFORM_ID); + pr_debug("enter\n"); + res = res && applyingFeedbackButtonConf(ctx, &ctx->defaultButtonConfig.feedback[SENSING_DIRECTION_RELEASE], + DEFAULT_RELEASE_WAVEFORM_ID); + for (buttonId = 0; buttonId < ARRAY_SIZE(ctx->defaultButtonConfig.channel) && res; buttonId++) + { + if (ctx->defaultButtonConfig.channel[buttonId].present) + { + dev_dbg(ctx->dev, "Applying default configuration for Channel %d \n", buttonId); + + res = res && applyButtonSensingConfiguration(ctx, buttonId, + &ctx->defaultButtonConfig.channel[buttonId].detection[SENSING_DIRECTION_PRESS], + DEFAULT_PRESS_WAVEFORM_ID); + res = res && applyButtonSensingConfiguration(ctx, buttonId, + &ctx->defaultButtonConfig.channel[buttonId].detection[SENSING_DIRECTION_RELEASE], + DEFAULT_RELEASE_WAVEFORM_ID); + } + } + + return res; +} + +/* + * Public Section + */ + +static struct attribute *bosDriverAttrs[] = { + &dev_attr_chip_id.attr, + &dev_attr_regs.attr, + &dev_attr_set_waveform.attr, + &dev_attr_set_slice.attr, + &dev_attr_synth_play.attr, + &dev_attr_ic_errors.attr, + &dev_attr_ctrl_output.attr, + &dev_attr_sensing_config.attr, + &dev_attr_sensing_autoplay.attr, + &dev_attr_sensing_stop.attr, + &dev_attr_is_sensing.attr, + NULL, +}; + +static struct attribute_group bosDriverAttrGroup = { + .attrs = bosDriverAttrs, +}; + +static void freeResources(Context *ctx) +{ + if (ctx != NULL) + { + sysfs_remove_group(&ctx->dev->kobj, &bosDriverAttrGroup); + + if (ctx->i2c != NULL) + { + i2cBoreasLinuxFree(ctx->i2c); + } + + mutex_destroy(&ctx->lock); + } + +} + +static const struct i2c_device_id bosDriverI2cId[] = { + {.name = "bos0614",}, +}; + +static struct of_device_id bos_dt_match[] = { + {.compatible = "boreas,bos0614",}, + {}, +}; + +static int bosDriverI2cProbe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int res = -ENODEV; + int err; + Context *ctx; + const struct of_device_id *match; + Bos0614Resource resource = { + .gpioA = NULL, + .gpioB = NULL, + .gpioC = NULL, + .gpioD = NULL + }; + + dev_info(&client->dev, "%s called\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + { + dev_err(&client->dev, "%s:I2C check failed\n", __func__); + res = -ENODEV; + goto bosDriverProbeError; + } + + ctx = devm_kzalloc(&client->dev, sizeof(*ctx), GFP_KERNEL); + + if (ctx == NULL) + { + dev_err(&client->dev, "%s:no memory\n", __func__); + res = -ENOMEM; + goto bosDriverProbeError; + } + + ctx->dev = &client->dev; + i2c_set_clientdata(client, ctx); + dev_set_drvdata(&client->dev, ctx); + + ctx->i2c = i2cBoreasLinuxInit(client); + resource.i2c = ctx->i2c; + ctx->hapticDriver = bos0614DriverI2cInit(resource); + + pr_debug("i2c: %p, driver: %p\n", ctx->i2c, ctx->hapticDriver); + + if (ctx->i2c == NULL || ctx->hapticDriver == NULL) + { + dev_err(&client->dev, "Failed to instantiate BOS0614 driver\n"); + goto bosDriverProbeError; + } + + match = of_match_device(of_match_ptr(bos_dt_match), &client->dev); + if (match) + { + dev_info(&client->dev, "Reading Configuration from DT\n"); + + if (!readDefaultConfiguration(ctx) || !applyDefaultConfiguration(ctx)) + { + dev_err(&client->dev, "Failed to decode DT configuration\n"); + goto bosDriverProbeError; + } + } else + pr_info("cannot find matching device tree\n"); + + mutex_init(&ctx->lock); + + err = sysfs_create_group(&ctx->dev->kobj, &bosDriverAttrGroup); + + if (err) + { + dev_err(&client->dev, "Failed to instantiate FS attributes\n"); + goto bosDriverProbeError; + } + + if (ctx->i2c == NULL || ctx->hapticDriver == NULL) + { + dev_err(&client->dev, "Boreas Haptic Driver failed to instantiate I2C or Haptic Driver\n"); + goto bosDriverProbeError; + } + + res = 0; + + dev_info(&client->dev, "BosDriver Successfully Configured \n"); + + bosDriverProbeError: + + if (res != 0) + { + dev_err(&client->dev, "Boreas Haptic Driver failed to probe\n"); + freeResources(ctx); + } + + return res; +} + +static int bosDriverI2cRemove(struct i2c_client *client) +{ + int res = -ENODEV; + Context *ctx = i2c_get_clientdata(client); + + dev_info(&client->dev, "%s called \n", __func__); + + freeResources(ctx); + + return res; +} + + +static const unsigned short addrList[] = { + 0x2c, +}; + +static int detect(struct i2c_client *client, struct i2c_board_info *info) +{ + dev_info(&client->dev, "%s Remove bosDriver \n", __func__); + return 0; +} + +static struct i2c_driver bosDriverI2c = { + .class = I2C_CLASS_HWMON, + .driver = { + .owner = THIS_MODULE, + .name = "bos0614_mmi", + .of_match_table = of_match_ptr(bos_dt_match), + }, + .probe = bosDriverI2cProbe, + .remove = bosDriverI2cRemove, + .detect = detect, + .id_table = bosDriverI2cId, + .address_list = addrList +}; + +module_i2c_driver(bosDriverI2c); + +MODULE_AUTHOR("Pascal-Frédéric St-Laurent "); +MODULE_DESCRIPTION("I2C Driver for Boréas Haptic Technologies"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/misc/bos0614_mmi/bsp.h b/drivers/input/misc/bos0614_mmi/bsp.h new file mode 100644 index 000000000000..1a08d0deea25 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/bsp.h @@ -0,0 +1,30 @@ +// +// Description: Boréas Board Support Package for Linux Kernel +// +// Copyright (c) 2020 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#ifndef BORÉAS_HAPTIC_DRIVER_LINUX_BSP_H +#define BORÉAS_HAPTIC_DRIVER_LINUX_BSP_H + +#define BOS0614_NBR_OF_INSTANCE (1) + +#endif //BORÉAS_HAPTIC_DRIVER_LINUX_BSP_H diff --git a/drivers/input/misc/bos0614_mmi/coreDefOption.h b/drivers/input/misc/bos0614_mmi/coreDefOption.h new file mode 100644 index 000000000000..9582e65aebfb --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/coreDefOption.h @@ -0,0 +1,27 @@ +// Copyright (c) 2020 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef DKCORE_COREDEFOPTION_H +#define DKCORE_COREDEFOPTION_H + +#define BOS0614_NBR_OF_INSTANCE (1) +#define BOS0614_NBR_OF_CHANNEL (4) + +#endif //DKCORE_COREDEFOPTION_H diff --git a/drivers/input/misc/bos0614_mmi/envSetup.sh b/drivers/input/misc/bos0614_mmi/envSetup.sh new file mode 100755 index 000000000000..05df50803811 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/envSetup.sh @@ -0,0 +1,29 @@ +#To configure device i2c bus +i2c_bus="i2c-2" +device="2-002c" + +adb root + +alias new_device="adb shell \"echo bos0614 0x2c > /sys/bus/i2c/devices/$i2c_bus/new_device\"" +alias del_device="adb shell \"echo bos0614 0x2c > /sys/bus/i2c/devices/$i2c_bus/delete_device\"" + +alias status="adb shell \"cat /sys/bus/i2c/devices/$device/ic_errors\"" +alias pressSensing="adb shell \"echo 0 0 0 4000 1200 25 > /sys/bus/i2c/devices/$device/sensing_config\"" +alias pressAutoplay="adb shell \"echo 0 0 0 > /sys/bus/i2c/devices/$device/sensing_autoplay\"" +alias releaseSensing="adb shell \"echo 0 1 1 8000 -11 32 > /sys/bus/i2c/devices/$device/sensing_config\"" +alias releaseAutoplay="adb shell \"echo 0 1 1 > /sys/bus/i2c/devices/$device/sensing_autoplay\"" +alias deactivateButton="adb shell \"echo 0 > /sys/bus/i2c/devices/$device/sensing_stop\"" + +alias sliceA="adb shell \"echo 0 60000 175000 2 1 > /sys/bus/i2c/devices/$device/set_slice\"" +alias waveformA="adb shell \"echo 0 0 2 1 1 > /sys/bus/i2c/devices/$device/set_waveform\"" +alias sliceB="adb shell \"echo 2 60000 175000 2 1 > /sys/bus/i2c/devices/$device/set_slice\"" +alias waveformB="adb shell \"echo 1 2 2 1 1 > /sys/bus/i2c/devices/$device/set_waveform\"" +alias sensingOn="sliceA &&waveformA && sliceB && waveformB && pressSensing && pressAutoplay && releaseSensing && releaseAutoplay" +alias sensingOff="deactivateButton" +alias play="adb shell \"echo 0 0 > /sys/bus/i2c/devices/$device/synth_play\"" +alias output_on="adb shell \"echo 1 > /sys/bus/i2c/devices/$device/ctrl_output\"" +alias output_off="adb shell \"echo 0 > /sys/bus/i2c/devices/$device/ctrl_output\"" +alias demoWave="sliceA && waveform && play && output_on" + +alias refresh="(push & rmmod) && load" +alias logdebug="adb shell \"echo 8 > /proc/sys/kernel/printk\"" diff --git a/drivers/input/misc/bos0614_mmi/i2cLinux.c b/drivers/input/misc/bos0614_mmi/i2cLinux.c new file mode 100644 index 000000000000..94c35bdcab73 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/i2cLinux.c @@ -0,0 +1,250 @@ +// +// Description: I2C Adapter for Boreas Haptic Driver on Linux Kernel +// +// Copyright (c) 2020 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +#define pr_fmt(fmt) "bos0614: %s: " fmt, __func__ + +#define DEBUG +//#undef DEBUG + +#include +#include +#include +#include + +#include "i2cLinux.h" + +#ifdef DEBUG +#undef pr_debug +#define pr_debug pr_err +#undef dev_dbg +#define dev_dbg dev_err +#endif + +typedef struct +{ + I2c driver; + struct i2c_client *client; +} Context; + +static void initDriverFct(Context *ctx); + +#define NBR_CHAR_PER_BYTE (5) +#define ESCAPE_CHAR (1) + +int process_params(ParamsLst *params, int numP, const char *buffer) +{ + char *arg, *buf, *p; + int n, err; + unsigned int valueUI; + int valueI; + + p = buf = kstrdup(buffer, GFP_KERNEL); + for (n = 0; n < numP && p && *p; n++, params++) { + valueI = INT16_MIN; + valueUI = UINT16_MAX; + arg = strsep(&p, " "); + if (!arg || !*arg) + break; + + pr_debug("arg=[%s] rest=[%s]\n", arg, p); + + switch (params->tid) { + case PARAM_UCHAR8: + err = kstrtou8(arg, 0, params->data_ptr); + valueUI = (unsigned int)*(unsigned char *)params->data_ptr; + break; + case PARAM_INT16: + err = kstrtos16(arg, 0, params->data_ptr); + valueI = (int)*(short *)params->data_ptr; + break; + case PARAM_UINT16: + err = kstrtou16(arg, 0, params->data_ptr); + valueUI = (unsigned int)*(unsigned short *)params->data_ptr; + break; + case PARAM_INT32: + err = kstrtoint(arg, 0, params->data_ptr); + valueI = (int)*(int *)params->data_ptr; + break; + case PARAM_UINT32: + err = kstrtouint(arg, 0, params->data_ptr); + valueUI = *(unsigned int *)params->data_ptr; + break; + } + + if (err) { + n = err; + break; + } + + if (valueUI != UINT16_MAX) + pr_debug("[%d]=%u\n", n, valueUI); + else if (valueI != INT16_MIN) + pr_debug("[%d]=%d\n", n, valueI); + } + kfree(buf); + pr_debug("processed %d input parameters\n", n); + return n; +} + +static void logBuffer(struct i2c_client* client, const char* message, + uint8_t addr, const void* data, size_t length) +{ + size_t bufferLength = length * NBR_CHAR_PER_BYTE + ESCAPE_CHAR; + char *buf = kzalloc(bufferLength, GFP_KERNEL); + + if (buf != NULL) + { + size_t index; + char *ptr = buf; + char *end = buf + bufferLength; + + uint8_t *_data = (uint8_t *) data; + + for (index = 0; index < length && ptr < end; index++) + { + ptr += sprintf(ptr, index < (length - 1) ? "0x%02x " : "0x%02x", _data[index]); + } + + dev_dbg(&client->dev, "%s addr: 0x%x length: %zu [%s]\n", message, addr, length, buf); + + kfree(buf); + } + +} + +/* + * Private Section + */ + +static int32_t i2cKernelSend(const I2c *i2c, uint8_t address, const void *data, size_t num) +{ + int32_t res = -EIO; + + if (i2c != NULL && data != NULL) + { + Context *ctx = container_of(i2c, Context, driver); + char *buf = kzalloc(num, GFP_KERNEL); + + logBuffer(ctx->client, "Write: ", address, data, num); + + if (buf != NULL) + { + int status; + + memcpy(buf, data, num); + status = i2c_master_send(ctx->client, (const char *) buf, (int) num); + res = status == num ? ARM_DRIVER_OK : status; + + kfree(buf); + } + } + + return res; +} + +int32_t i2cKernelRead(const I2c *i2c, uint8_t address, void *data, size_t num) +{ + int32_t res = -EIO; + + if (i2c != NULL && data != NULL) + { + Context *ctx = container_of(i2c, Context, driver); + char *buf = kzalloc(num, GFP_KERNEL); + + if (buf != NULL) + { + int status = i2c_master_recv(ctx->client, (char *) buf, num); + + res = status == num ? 0 : status; + + if (res == ARM_DRIVER_OK) + { + memcpy(data, buf, num); + logBuffer(ctx->client, "Read: ", address, data, num); + } + + kfree(buf); + } + + + } + + return res; +} + +/* + * Public Section + */ + +I2c *i2cBoreasLinuxInit(struct i2c_client *client) +{ + I2c *driver = NULL; + + if (client != NULL) + { + Context *ctx = kzalloc(sizeof(Context), GFP_KERNEL); + + if (ctx != NULL) + { + ctx->client = client; + initDriverFct(ctx); + driver = &ctx->driver; +#if 0 + if (ctx->client->dev.of_node) + readDevTree(ctx); +#endif + } + } + + pr_debug("I2C client: %p, driver: %p\n", client, driver); + + return driver; +} + +bool i2cBoreasLinuxFree(I2c *i2c) +{ + bool res = false; + + if (i2c != NULL) + { + Context *ctx = container_of(i2c, Context, driver); + + dev_info(&ctx->client->dev, "I2C Linux Wrapper Free\n"); + + kfree(ctx); + + res = true; + } + + return res; +} + +/* + * Private Section + */ + +void initDriverFct(Context *ctx) +{ + ctx->driver.write = i2cKernelSend; + ctx->driver.read = i2cKernelRead; +} diff --git a/drivers/input/misc/bos0614_mmi/i2cLinux.h b/drivers/input/misc/bos0614_mmi/i2cLinux.h new file mode 100644 index 000000000000..a4e14076a352 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/i2cLinux.h @@ -0,0 +1,58 @@ +// +// Description: Boréas I2C Wrapper for Linux Implementation +// +// Copyright (c) 2020 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#ifndef BOREAS_HAPTIC_DRIVER_LINUX_I2CLINUX_H +#define BOREAS_HAPTIC_DRIVER_LINUX_I2CLINUX_H + +#include +#include +#include "libs/dk-core/src/bsp/drivers/i2c/i2c.h" +#include "libs/dk-core/src/bsp/drivers/haptic/bos0614Driver.h" + +enum { + PARAM_UCHAR8 = 1, + PARAM_INT16, + PARAM_UINT16, + PARAM_INT32, + PARAM_UINT32 +}; + +typedef struct { + int tid; + void *data_ptr; +} ParamsLst; + +#define PARAM_ADD(t, v) {\ + params[numP].tid = t;\ + params[numP].data_ptr = v;\ + numP++;\ +} + +int process_params(ParamsLst *params, int numP, const char *buffer); + +I2c *i2cBoreasLinuxInit(struct i2c_client *client); + +bool i2cBoreasLinuxFree(I2c * i2c); + +#endif //BOREAS_HAPTIC_DRIVER_LINUX_I2CLINUX_H diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_CAN.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_CAN.h new file mode 100644 index 000000000000..5c567e6fc620 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_CAN.h @@ -0,0 +1,382 @@ +/* + * Copyright (c) 2015-2017 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Date: 13. Sept 2017 + * $Revision: V1.2 + * + * Project: CAN (Controller Area Network) Driver definitions + */ + +/* History: + * Version 1.2 + * Added ARM_CAN_UNIT_STATE_BUS_OFF unit state and + * ARM_CAN_EVENT_UNIT_INACTIVE unit event + * Version 1.1 + * ARM_CAN_STATUS made volatile + * Version 1.0 + * Initial release + */ + +#ifndef DRIVER_CAN_H_ +#define DRIVER_CAN_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Driver_Common.h" + +#define ARM_CAN_API_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(1,2) /* API version */ + + +/****** CAN Bitrate selection codes *****/ +typedef enum _ARM_CAN_BITRATE_SELECT { + ARM_CAN_BITRATE_NOMINAL, ///< Select nominal (flexible data-rate arbitration) bitrate + ARM_CAN_BITRATE_FD_DATA ///< Select flexible data-rate data bitrate +} ARM_CAN_BITRATE_SELECT; + +/****** CAN Bit Propagation Segment codes (PROP_SEG) *****/ +#define ARM_CAN_BIT_PROP_SEG_Pos 0UL ///< bits 7..0 +#define ARM_CAN_BIT_PROP_SEG_Msk (0xFFUL << ARM_CAN_BIT_PROP_SEG_Pos) +#define ARM_CAN_BIT_PROP_SEG(x) (((x) << ARM_CAN_BIT_PROP_SEG_Pos) & ARM_CAN_BIT_PROP_SEG_Msk) + +/****** CAN Bit Phase Buffer Segment 1 (PHASE_SEG1) codes *****/ +#define ARM_CAN_BIT_PHASE_SEG1_Pos 8UL ///< bits 15..8 +#define ARM_CAN_BIT_PHASE_SEG1_Msk (0xFFUL << ARM_CAN_BIT_PHASE_SEG1_Pos) +#define ARM_CAN_BIT_PHASE_SEG1(x) (((x) << ARM_CAN_BIT_PHASE_SEG1_Pos) & ARM_CAN_BIT_PHASE_SEG1_Msk) + +/****** CAN Bit Phase Buffer Segment 2 (PHASE_SEG2) codes *****/ +#define ARM_CAN_BIT_PHASE_SEG2_Pos 16UL ///< bits 23..16 +#define ARM_CAN_BIT_PHASE_SEG2_Msk (0xFFUL << ARM_CAN_BIT_PHASE_SEG2_Pos) +#define ARM_CAN_BIT_PHASE_SEG2(x) (((x) << ARM_CAN_BIT_PHASE_SEG2_Pos) & ARM_CAN_BIT_PHASE_SEG2_Msk) + +/****** CAN Bit (Re)Synchronization Jump Width Segment (SJW) *****/ +#define ARM_CAN_BIT_SJW_Pos 24UL ///< bits 28..24 +#define ARM_CAN_BIT_SJW_Msk (0x1FUL << ARM_CAN_BIT_SJW_Pos) +#define ARM_CAN_BIT_SJW(x) (((x) << ARM_CAN_BIT_SJW_Pos) & ARM_CAN_BIT_SJW_Msk) + +/****** CAN Mode codes *****/ +typedef enum _ARM_CAN_MODE { + ARM_CAN_MODE_INITIALIZATION, ///< Initialization mode + ARM_CAN_MODE_NORMAL, ///< Normal operation mode + ARM_CAN_MODE_RESTRICTED, ///< Restricted operation mode + ARM_CAN_MODE_MONITOR, ///< Bus monitoring mode + ARM_CAN_MODE_LOOPBACK_INTERNAL, ///< Loopback internal mode + ARM_CAN_MODE_LOOPBACK_EXTERNAL ///< Loopback external mode +} ARM_CAN_MODE; + +/****** CAN Filter Operation codes *****/ +typedef enum _ARM_CAN_FILTER_OPERATION { + ARM_CAN_FILTER_ID_EXACT_ADD, ///< Add exact id filter + ARM_CAN_FILTER_ID_EXACT_REMOVE, ///< Remove exact id filter + ARM_CAN_FILTER_ID_RANGE_ADD, ///< Add range id filter + ARM_CAN_FILTER_ID_RANGE_REMOVE, ///< Remove range id filter + ARM_CAN_FILTER_ID_MASKABLE_ADD, ///< Add maskable id filter + ARM_CAN_FILTER_ID_MASKABLE_REMOVE ///< Remove maskable id filter +} ARM_CAN_FILTER_OPERATION; + +/****** CAN Object Configuration codes *****/ +typedef enum _ARM_CAN_OBJ_CONFIG { + ARM_CAN_OBJ_INACTIVE, ///< CAN object inactive + ARM_CAN_OBJ_TX, ///< CAN transmit object + ARM_CAN_OBJ_RX, ///< CAN receive object + ARM_CAN_OBJ_RX_RTR_TX_DATA, ///< CAN object that on RTR reception automatically transmits Data Frame + ARM_CAN_OBJ_TX_RTR_RX_DATA ///< CAN object that transmits RTR and automatically receives Data Frame +} ARM_CAN_OBJ_CONFIG; + +/** +\brief CAN Object Capabilities +*/ +typedef struct _ARM_CAN_OBJ_CAPABILITIES { + uint32_t tx : 1; ///< Object supports transmission + uint32_t rx : 1; ///< Object supports reception + uint32_t rx_rtr_tx_data : 1; ///< Object supports RTR reception and automatic Data Frame transmission + uint32_t tx_rtr_rx_data : 1; ///< Object supports RTR transmission and automatic Data Frame reception + uint32_t multiple_filters : 1; ///< Object allows assignment of multiple filters to it + uint32_t exact_filtering : 1; ///< Object supports exact identifier filtering + uint32_t range_filtering : 1; ///< Object supports range identifier filtering + uint32_t mask_filtering : 1; ///< Object supports mask identifier filtering + uint32_t message_depth : 8; ///< Number of messages buffers (FIFO) for that object + uint32_t reserved : 16; ///< Reserved (must be zero) +} ARM_CAN_OBJ_CAPABILITIES; + +/****** CAN Control Function Operation codes *****/ +#define ARM_CAN_CONTROL_Pos 0UL +#define ARM_CAN_CONTROL_Msk (0xFFUL << ARM_CAN_CONTROL_Pos) +#define ARM_CAN_SET_FD_MODE (1UL << ARM_CAN_CONTROL_Pos) ///< Set FD operation mode; arg: 0 = disable, 1 = enable +#define ARM_CAN_ABORT_MESSAGE_SEND (2UL << ARM_CAN_CONTROL_Pos) ///< Abort sending of CAN message; arg = object +#define ARM_CAN_CONTROL_RETRANSMISSION (3UL << ARM_CAN_CONTROL_Pos) ///< Enable/disable automatic retransmission; arg: 0 = disable, 1 = enable (default state) +#define ARM_CAN_SET_TRANSCEIVER_DELAY (4UL << ARM_CAN_CONTROL_Pos) ///< Set transceiver delay; arg = delay in time quanta + +/****** CAN ID Frame Format codes *****/ +#define ARM_CAN_ID_IDE_Pos 31UL +#define ARM_CAN_ID_IDE_Msk (1UL << ARM_CAN_ID_IDE_Pos) + +/****** CAN Identifier encoding *****/ +#define ARM_CAN_STANDARD_ID(id) (id & 0x000007FFUL) ///< CAN identifier in standard format (11-bits) +#define ARM_CAN_EXTENDED_ID(id) ((id & 0x1FFFFFFFUL) | ARM_CAN_ID_IDE_Msk)///< CAN identifier in extended format (29-bits) + +/** +\brief CAN Message Information +*/ +typedef struct _ARM_CAN_MSG_INFO { + uint32_t id; ///< CAN identifier with frame format specifier (bit 31) + uint32_t rtr : 1; ///< Remote transmission request frame + uint32_t edl : 1; ///< Flexible data-rate format extended data length + uint32_t brs : 1; ///< Flexible data-rate format with bitrate switch + uint32_t esi : 1; ///< Flexible data-rate format error state indicator + uint32_t dlc : 4; ///< Data length code + uint32_t reserved : 24; +} ARM_CAN_MSG_INFO; + +/****** CAN specific error code *****/ +#define ARM_CAN_INVALID_BITRATE_SELECT (ARM_DRIVER_ERROR_SPECIFIC - 1) ///< Bitrate selection not supported +#define ARM_CAN_INVALID_BITRATE (ARM_DRIVER_ERROR_SPECIFIC - 2) ///< Requested bitrate not supported +#define ARM_CAN_INVALID_BIT_PROP_SEG (ARM_DRIVER_ERROR_SPECIFIC - 3) ///< Propagation segment value not supported +#define ARM_CAN_INVALID_BIT_PHASE_SEG1 (ARM_DRIVER_ERROR_SPECIFIC - 4) ///< Phase segment 1 value not supported +#define ARM_CAN_INVALID_BIT_PHASE_SEG2 (ARM_DRIVER_ERROR_SPECIFIC - 5) ///< Phase segment 2 value not supported +#define ARM_CAN_INVALID_BIT_SJW (ARM_DRIVER_ERROR_SPECIFIC - 6) ///< SJW value not supported +#define ARM_CAN_NO_MESSAGE_AVAILABLE (ARM_DRIVER_ERROR_SPECIFIC - 7) ///< Message is not available + +/****** CAN Status codes *****/ +#define ARM_CAN_UNIT_STATE_INACTIVE (0U) ///< Unit state: Not active on bus (initialization) +#define ARM_CAN_UNIT_STATE_ACTIVE (1U) ///< Unit state: Active on bus (can generate active error frame) +#define ARM_CAN_UNIT_STATE_PASSIVE (2U) ///< Unit state: Error passive (can not generate active error frame) +#define ARM_CAN_UNIT_STATE_BUS_OFF (3U) ///< Unit state: Bus-off (can recover to active state) +#define ARM_CAN_LEC_NO_ERROR (0U) ///< Last error code: No error +#define ARM_CAN_LEC_BIT_ERROR (1U) ///< Last error code: Bit error +#define ARM_CAN_LEC_STUFF_ERROR (2U) ///< Last error code: Bit stuffing error +#define ARM_CAN_LEC_CRC_ERROR (3U) ///< Last error code: CRC error +#define ARM_CAN_LEC_FORM_ERROR (4U) ///< Last error code: Illegal fixed-form bit +#define ARM_CAN_LEC_ACK_ERROR (5U) ///< Last error code: Acknowledgment error + +/** +\brief CAN Status +*/ +typedef volatile struct _ARM_CAN_STATUS { + uint32_t unit_state : 4; ///< Unit bus state + uint32_t last_error_code : 4; ///< Last error code + uint32_t tx_error_count : 8; ///< Transmitter error count + uint32_t rx_error_count : 8; ///< Receiver error count + uint32_t reserved : 8; +} ARM_CAN_STATUS; + + +/****** CAN Unit Event *****/ +#define ARM_CAN_EVENT_UNIT_INACTIVE (0U) ///< Unit entered Inactive state +#define ARM_CAN_EVENT_UNIT_ACTIVE (1U) ///< Unit entered Error Active state +#define ARM_CAN_EVENT_UNIT_WARNING (2U) ///< Unit entered Error Warning state (one or both error counters >= 96) +#define ARM_CAN_EVENT_UNIT_PASSIVE (3U) ///< Unit entered Error Passive state +#define ARM_CAN_EVENT_UNIT_BUS_OFF (4U) ///< Unit entered Bus-off state + +/****** CAN Send/Receive Event *****/ +#define ARM_CAN_EVENT_SEND_COMPLETE (1UL << 0) ///< Send complete +#define ARM_CAN_EVENT_RECEIVE (1UL << 1) ///< Message received +#define ARM_CAN_EVENT_RECEIVE_OVERRUN (1UL << 2) ///< Received message overrun + + +// Function documentation +/** + \fn ARM_DRIVER_VERSION ARM_CAN_GetVersion (void) + \brief Get driver version. + \return \ref ARM_DRIVER_VERSION + + \fn ARM_CAN_CAPABILITIES ARM_CAN_GetCapabilities (void) + \brief Get driver capabilities. + \return \ref ARM_CAN_CAPABILITIES + + \fn int32_t ARM_CAN_Initialize (ARM_CAN_SignalUnitEvent_t cb_unit_event, + ARM_CAN_SignalObjectEvent_t cb_object_event) + \brief Initialize CAN interface and register signal (callback) functions. + \param[in] cb_unit_event Pointer to \ref ARM_CAN_SignalUnitEvent callback function + \param[in] cb_object_event Pointer to \ref ARM_CAN_SignalObjectEvent callback function + \return \ref execution_status + + \fn int32_t ARM_CAN_Uninitialize (void) + \brief De-initialize CAN interface. + \return \ref execution_status + + \fn int32_t ARM_CAN_PowerControl (ARM_POWER_STATE state) + \brief Control CAN interface power. + \param[in] state Power state + - \ref ARM_POWER_OFF : power off: no operation possible + - \ref ARM_POWER_LOW : low power mode: retain state, detect and signal wake-up events + - \ref ARM_POWER_FULL : power on: full operation at maximum performance + \return \ref execution_status + + \fn uint32_t ARM_CAN_GetClock (void) + \brief Retrieve CAN base clock frequency. + \return base clock frequency + + \fn int32_t ARM_CAN_SetBitrate (ARM_CAN_BITRATE_SELECT select, uint32_t bitrate, uint32_t bit_segments) + \brief Set bitrate for CAN interface. + \param[in] select Bitrate selection + - \ref ARM_CAN_BITRATE_NOMINAL : nominal (flexible data-rate arbitration) bitrate + - \ref ARM_CAN_BITRATE_FD_DATA : flexible data-rate data bitrate + \param[in] bitrate Bitrate + \param[in] bit_segments Bit segments settings + - \ref ARM_CAN_BIT_PROP_SEG(x) : number of time quanta for propagation time segment + - \ref ARM_CAN_BIT_PHASE_SEG1(x) : number of time quanta for phase buffer segment 1 + - \ref ARM_CAN_BIT_PHASE_SEG2(x) : number of time quanta for phase buffer Segment 2 + - \ref ARM_CAN_BIT_SJW(x) : number of time quanta for (re-)synchronization jump width + \return \ref execution_status + + \fn int32_t ARM_CAN_SetMode (ARM_CAN_MODE mode) + \brief Set operating mode for CAN interface. + \param[in] mode Operating mode + - \ref ARM_CAN_MODE_INITIALIZATION : initialization mode + - \ref ARM_CAN_MODE_NORMAL : normal operation mode + - \ref ARM_CAN_MODE_RESTRICTED : restricted operation mode + - \ref ARM_CAN_MODE_MONITOR : bus monitoring mode + - \ref ARM_CAN_MODE_LOOPBACK_INTERNAL : loopback internal mode + - \ref ARM_CAN_MODE_LOOPBACK_EXTERNAL : loopback external mode + \return \ref execution_status + + \fn ARM_CAN_OBJ_CAPABILITIES ARM_CAN_ObjectGetCapabilities (uint32_t obj_idx) + \brief Retrieve capabilities of an object. + \param[in] obj_idx Object index + \return \ref ARM_CAN_OBJ_CAPABILITIES + + \fn int32_t ARM_CAN_ObjectSetFilter (uint32_t obj_idx, ARM_CAN_FILTER_OPERATION operation, uint32_t id, uint32_t arg) + \brief Add or remove filter for message reception. + \param[in] obj_idx Object index of object that filter should be or is assigned to + \param[in] operation Operation on filter + - \ref ARM_CAN_FILTER_ID_EXACT_ADD : add exact id filter + - \ref ARM_CAN_FILTER_ID_EXACT_REMOVE : remove exact id filter + - \ref ARM_CAN_FILTER_ID_RANGE_ADD : add range id filter + - \ref ARM_CAN_FILTER_ID_RANGE_REMOVE : remove range id filter + - \ref ARM_CAN_FILTER_ID_MASKABLE_ADD : add maskable id filter + - \ref ARM_CAN_FILTER_ID_MASKABLE_REMOVE : remove maskable id filter + \param[in] id ID or start of ID range (depending on filter type) + \param[in] arg Mask or end of ID range (depending on filter type) + \return \ref execution_status + + \fn int32_t ARM_CAN_ObjectConfigure (uint32_t obj_idx, ARM_CAN_OBJ_CONFIG obj_cfg) + \brief Configure object. + \param[in] obj_idx Object index + \param[in] obj_cfg Object configuration state + - \ref ARM_CAN_OBJ_INACTIVE : deactivate object + - \ref ARM_CAN_OBJ_RX : configure object for reception + - \ref ARM_CAN_OBJ_TX : configure object for transmission + - \ref ARM_CAN_OBJ_RX_RTR_TX_DATA : configure object that on RTR reception automatically transmits Data Frame + - \ref ARM_CAN_OBJ_TX_RTR_RX_DATA : configure object that transmits RTR and automatically receives Data Frame + \return \ref execution_status + + \fn int32_t ARM_CAN_MessageSend (uint32_t obj_idx, ARM_CAN_MSG_INFO *msg_info, const uint8_t *data, uint8_t size) + \brief Send message on CAN bus. + \param[in] obj_idx Object index + \param[in] msg_info Pointer to CAN message information + \param[in] data Pointer to data buffer + \param[in] size Number of data bytes to send + \return value >= 0 number of data bytes accepted to send + \return value < 0 \ref execution_status + + \fn int32_t ARM_CAN_MessageRead (uint32_t obj_idx, ARM_CAN_MSG_INFO *msg_info, uint8_t *data, uint8_t size) + \brief Read message received on CAN bus. + \param[in] obj_idx Object index + \param[out] msg_info Pointer to read CAN message information + \param[out] data Pointer to data buffer for read data + \param[in] size Maximum number of data bytes to read + \return value >= 0 number of data bytes read + \return value < 0 \ref execution_status + + \fn int32_t ARM_CAN_Control (uint32_t control, uint32_t arg) + \brief Control CAN interface. + \param[in] control Operation + - \ref ARM_CAN_SET_FD_MODE : set FD operation mode + - \ref ARM_CAN_ABORT_MESSAGE_SEND : abort sending of CAN message + - \ref ARM_CAN_CONTROL_RETRANSMISSION : enable/disable automatic retransmission + - \ref ARM_CAN_SET_TRANSCEIVER_DELAY : set transceiver delay + \param[in] arg Argument of operation + \return \ref execution_status + + \fn ARM_CAN_STATUS ARM_CAN_GetStatus (void) + \brief Get CAN status. + \return CAN status \ref ARM_CAN_STATUS + + \fn void ARM_CAN_SignalUnitEvent (uint32_t event) + \brief Signal CAN unit event. + \param[in] event \ref CAN_unit_events + \return none + + \fn void ARM_CAN_SignalObjectEvent (uint32_t obj_idx, uint32_t event) + \brief Signal CAN object event. + \param[in] obj_idx Object index + \param[in] event \ref CAN_events + \return none +*/ + +typedef void (*ARM_CAN_SignalUnitEvent_t) (uint32_t event); ///< Pointer to \ref ARM_CAN_SignalUnitEvent : Signal CAN Unit Event. +typedef void (*ARM_CAN_SignalObjectEvent_t) (uint32_t obj_idx, uint32_t event); ///< Pointer to \ref ARM_CAN_SignalObjectEvent : Signal CAN Object Event. + + +/** +\brief CAN Device Driver Capabilities. +*/ +typedef struct _ARM_CAN_CAPABILITIES { + uint32_t num_objects : 8; ///< Number of \ref can_objects available + uint32_t reentrant_operation : 1; ///< Support for reentrant calls to \ref ARM_CAN_MessageSend, \ref ARM_CAN_MessageRead, \ref ARM_CAN_ObjectConfigure and abort message sending used by \ref ARM_CAN_Control + uint32_t fd_mode : 1; ///< Support for CAN with flexible data-rate mode (CAN_FD) (set by \ref ARM_CAN_Control) + uint32_t restricted_mode : 1; ///< Support for restricted operation mode (set by \ref ARM_CAN_SetMode) + uint32_t monitor_mode : 1; ///< Support for bus monitoring mode (set by \ref ARM_CAN_SetMode) + uint32_t internal_loopback : 1; ///< Support for internal loopback mode (set by \ref ARM_CAN_SetMode) + uint32_t external_loopback : 1; ///< Support for external loopback mode (set by \ref ARM_CAN_SetMode) + uint32_t reserved : 18; ///< Reserved (must be zero) +} ARM_CAN_CAPABILITIES; + + +/** +\brief Access structure of the CAN Driver. +*/ +typedef struct _ARM_DRIVER_CAN { + ARM_DRIVER_VERSION (*GetVersion) (void); ///< Pointer to \ref ARM_CAN_GetVersion : Get driver version. + ARM_CAN_CAPABILITIES (*GetCapabilities) (void); ///< Pointer to \ref ARM_CAN_GetCapabilities : Get driver capabilities. + int32_t (*Initialize) (ARM_CAN_SignalUnitEvent_t cb_unit_event, + ARM_CAN_SignalObjectEvent_t cb_object_event); ///< Pointer to \ref ARM_CAN_Initialize : Initialize CAN interface. + int32_t (*Uninitialize) (void); ///< Pointer to \ref ARM_CAN_Uninitialize : De-initialize CAN interface. + int32_t (*PowerControl) (ARM_POWER_STATE state); ///< Pointer to \ref ARM_CAN_PowerControl : Control CAN interface power. + uint32_t (*GetClock) (void); ///< Pointer to \ref ARM_CAN_GetClock : Retrieve CAN base clock frequency. + int32_t (*SetBitrate) (ARM_CAN_BITRATE_SELECT select, + uint32_t bitrate, + uint32_t bit_segments); ///< Pointer to \ref ARM_CAN_SetBitrate : Set bitrate for CAN interface. + int32_t (*SetMode) (ARM_CAN_MODE mode); ///< Pointer to \ref ARM_CAN_SetMode : Set operating mode for CAN interface. + ARM_CAN_OBJ_CAPABILITIES (*ObjectGetCapabilities) (uint32_t obj_idx); ///< Pointer to \ref ARM_CAN_ObjectGetCapabilities : Retrieve capabilities of an object. + int32_t (*ObjectSetFilter) (uint32_t obj_idx, + ARM_CAN_FILTER_OPERATION operation, + uint32_t id, + uint32_t arg); ///< Pointer to \ref ARM_CAN_ObjectSetFilter : Add or remove filter for message reception. + int32_t (*ObjectConfigure) (uint32_t obj_idx, + ARM_CAN_OBJ_CONFIG obj_cfg); ///< Pointer to \ref ARM_CAN_ObjectConfigure : Configure object. + int32_t (*MessageSend) (uint32_t obj_idx, + ARM_CAN_MSG_INFO *msg_info, + const uint8_t *data, + uint8_t size); ///< Pointer to \ref ARM_CAN_MessageSend : Send message on CAN bus. + int32_t (*MessageRead) (uint32_t obj_idx, + ARM_CAN_MSG_INFO *msg_info, + uint8_t *data, + uint8_t size); ///< Pointer to \ref ARM_CAN_MessageRead : Read message received on CAN bus. + int32_t (*Control) (uint32_t control, + uint32_t arg); ///< Pointer to \ref ARM_CAN_Control : Control CAN interface. + ARM_CAN_STATUS (*GetStatus) (void); ///< Pointer to \ref ARM_CAN_GetStatus : Get CAN status. +} const ARM_DRIVER_CAN; + +#ifdef __cplusplus +} +#endif + +#endif /* DRIVER_CAN_H_ */ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_Common.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_Common.h new file mode 100644 index 000000000000..59d5b759cc16 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_Common.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2013-2017 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Date: 2. Feb 2017 + * $Revision: V2.0 + * + * Project: Common Driver definitions + */ + +/* History: + * Version 2.0 + * Changed prefix ARM_DRV -> ARM_DRIVER + * Added General return codes definitions + * Version 1.10 + * Namespace prefix ARM_ added + * Version 1.00 + * Initial release + */ + +#ifndef DRIVER_COMMON_H_ +#define DRIVER_COMMON_H_ + +#include +#include +#include + +#define ARM_DRIVER_VERSION_MAJOR_MINOR(major,minor) (((major) << 8) | (minor)) + +/** +\brief Driver Version +*/ +typedef struct _ARM_DRIVER_VERSION { + uint16_t api; ///< API version + uint16_t drv; ///< Driver version +} ARM_DRIVER_VERSION; + +/* General return codes */ +#define ARM_DRIVER_OK 0 ///< Operation succeeded +#define ARM_DRIVER_ERROR -1 ///< Unspecified error +#define ARM_DRIVER_ERROR_BUSY -2 ///< Driver is busy +#define ARM_DRIVER_ERROR_TIMEOUT -3 ///< Timeout occurred +#define ARM_DRIVER_ERROR_UNSUPPORTED -4 ///< Operation not supported +#define ARM_DRIVER_ERROR_PARAMETER -5 ///< Parameter error +#define ARM_DRIVER_ERROR_SPECIFIC -6 ///< Start of driver specific errors + +/** +\brief General power states +*/ +typedef enum _ARM_POWER_STATE { + ARM_POWER_OFF, ///< Power off: no operation possible + ARM_POWER_LOW, ///< Low Power mode: retain state, detect and signal wake-up events + ARM_POWER_FULL ///< Power on: full operation at maximum performance +} ARM_POWER_STATE; + +#endif /* DRIVER_COMMON_H_ */ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_ETH.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_ETH.h new file mode 100644 index 000000000000..d60f5fd55929 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_ETH.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2013-2017 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Date: 2. Feb 2017 + * $Revision: V2.1 + * + * Project: Ethernet PHY and MAC Driver common definitions + */ + +/* History: + * Version 2.1 + * ARM_ETH_LINK_INFO made volatile + * Version 2.0 + * Removed ARM_ETH_STATUS enumerator + * Removed ARM_ETH_MODE enumerator + * Version 1.10 + * Namespace prefix ARM_ added + * Version 1.00 + * Initial release + */ + +#ifndef DRIVER_ETH_H_ +#define DRIVER_ETH_H_ + +#include "Driver_Common.h" + +/** +\brief Ethernet Media Interface type +*/ +#define ARM_ETH_INTERFACE_MII (0) ///< Media Independent Interface (MII) +#define ARM_ETH_INTERFACE_RMII (1) ///< Reduced Media Independent Interface (RMII) +#define ARM_ETH_INTERFACE_SMII (2) ///< Serial Media Independent Interface (SMII) + +/** +\brief Ethernet link speed +*/ +#define ARM_ETH_SPEED_10M (0) ///< 10 Mbps link speed +#define ARM_ETH_SPEED_100M (1) ///< 100 Mbps link speed +#define ARM_ETH_SPEED_1G (2) ///< 1 Gpbs link speed + +/** +\brief Ethernet duplex mode +*/ +#define ARM_ETH_DUPLEX_HALF (0) ///< Half duplex link +#define ARM_ETH_DUPLEX_FULL (1) ///< Full duplex link + +/** +\brief Ethernet link state +*/ +typedef enum _ARM_ETH_LINK_STATE { + ARM_ETH_LINK_DOWN, ///< Link is down + ARM_ETH_LINK_UP ///< Link is up +} ARM_ETH_LINK_STATE; + +/** +\brief Ethernet link information +*/ +typedef volatile struct _ARM_ETH_LINK_INFO { + uint32_t speed : 2; ///< Link speed: 0= 10 MBit, 1= 100 MBit, 2= 1 GBit + uint32_t duplex : 1; ///< Duplex mode: 0= Half, 1= Full + uint32_t reserved : 29; +} ARM_ETH_LINK_INFO; + +/** +\brief Ethernet MAC Address +*/ +typedef struct _ARM_ETH_MAC_ADDR { + uint8_t b[6]; ///< MAC Address (6 bytes), MSB first +} ARM_ETH_MAC_ADDR; + +#endif /* DRIVER_ETH_H_ */ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_ETH_MAC.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_ETH_MAC.h new file mode 100644 index 000000000000..e26019f9cf7f --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_ETH_MAC.h @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2013-2017 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Date: 2. Feb 2017 + * $Revision: V2.1 + * + * Project: Ethernet MAC (Media Access Control) Driver definitions + */ + +/* History: + * Version 2.1 + * Added ARM_ETH_MAC_SLEEP Control + * Version 2.0 + * Changed MAC Address handling: + * moved from ARM_ETH_MAC_Initialize + * to new functions ARM_ETH_MAC_GetMacAddress and ARM_ETH_MAC_SetMacAddress + * Replaced ARM_ETH_MAC_SetMulticastAddr function with ARM_ETH_MAC_SetAddressFilter + * Extended ARM_ETH_MAC_SendFrame function with flags + * Added ARM_ETH_MAC_Control function: + * more control options (Broadcast, Multicast, Checksum offload, VLAN, ...) + * replaces ARM_ETH_MAC_SetMode + * replaces ARM_ETH_MAC_EnableTx, ARM_ETH_MAC_EnableRx + * Added optional event on transmitted frame + * Added support for PTP (Precision Time Protocol) through new functions: + * ARM_ETH_MAC_ControlTimer + * ARM_ETH_MAC_GetRxFrameTime + * ARM_ETH_MAC_GetTxFrameTime + * Changed prefix ARM_DRV -> ARM_DRIVER + * Changed return values of some functions to int32_t + * Version 1.10 + * Name space prefix ARM_ added + * Version 1.01 + * Renamed capabilities items for checksum offload + * Version 1.00 + * Initial release + */ + +#ifndef DRIVER_ETH_MAC_H_ +#define DRIVER_ETH_MAC_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Driver_ETH.h" + +#define ARM_ETH_MAC_API_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(2,1) /* API version */ + + +#define _ARM_Driver_ETH_MAC_(n) Driver_ETH_MAC##n +#define ARM_Driver_ETH_MAC_(n) _ARM_Driver_ETH_MAC_(n) + + +/****** Ethernet MAC Control Codes *****/ + +#define ARM_ETH_MAC_CONFIGURE (0x01) ///< Configure MAC; arg = configuration +#define ARM_ETH_MAC_CONTROL_TX (0x02) ///< Transmitter; arg: 0=disabled (default), 1=enabled +#define ARM_ETH_MAC_CONTROL_RX (0x03) ///< Receiver; arg: 0=disabled (default), 1=enabled +#define ARM_ETH_MAC_FLUSH (0x04) ///< Flush buffer; arg = ARM_ETH_MAC_FLUSH_... +#define ARM_ETH_MAC_SLEEP (0x05) ///< Sleep mode; arg: 1=enter and wait for Magic packet, 0=exit +#define ARM_ETH_MAC_VLAN_FILTER (0x06) ///< VLAN Filter for received frames; arg15..0: VLAN Tag; arg16: optional ARM_ETH_MAC_VLAN_FILTER_ID_ONLY; 0=disabled (default) + +/*----- Ethernet MAC Configuration -----*/ +#define ARM_ETH_MAC_SPEED_Pos 0 +#define ARM_ETH_MAC_SPEED_Msk (3UL << ARM_ETH_MAC_SPEED_Pos) +#define ARM_ETH_MAC_SPEED_10M (ARM_ETH_SPEED_10M << ARM_ETH_MAC_SPEED_Pos) ///< 10 Mbps link speed +#define ARM_ETH_MAC_SPEED_100M (ARM_ETH_SPEED_100M << ARM_ETH_MAC_SPEED_Pos) ///< 100 Mbps link speed +#define ARM_ETH_MAC_SPEED_1G (ARM_ETH_SPEED_1G << ARM_ETH_MAC_SPEED_Pos) ///< 1 Gpbs link speed +#define ARM_ETH_MAC_DUPLEX_Pos 2 +#define ARM_ETH_MAC_DUPLEX_Msk (1UL << ARM_ETH_MAC_DUPLEX_Pos) +#define ARM_ETH_MAC_DUPLEX_HALF (ARM_ETH_DUPLEX_HALF << ARM_ETH_MAC_DUPLEX_Pos) ///< Half duplex link +#define ARM_ETH_MAC_DUPLEX_FULL (ARM_ETH_DUPLEX_FULL << ARM_ETH_MAC_DUPLEX_Pos) ///< Full duplex link +#define ARM_ETH_MAC_LOOPBACK (1UL << 4) ///< Loop-back test mode +#define ARM_ETH_MAC_CHECKSUM_OFFLOAD_RX (1UL << 5) ///< Receiver Checksum offload +#define ARM_ETH_MAC_CHECKSUM_OFFLOAD_TX (1UL << 6) ///< Transmitter Checksum offload +#define ARM_ETH_MAC_ADDRESS_BROADCAST (1UL << 7) ///< Accept frames with Broadcast address +#define ARM_ETH_MAC_ADDRESS_MULTICAST (1UL << 8) ///< Accept frames with any Multicast address +#define ARM_ETH_MAC_ADDRESS_ALL (1UL << 9) ///< Accept frames with any address (Promiscuous Mode) + +/*----- Ethernet MAC Flush Flags -----*/ +#define ARM_ETH_MAC_FLUSH_RX (1UL << 0) ///< Flush Receive buffer +#define ARM_ETH_MAC_FLUSH_TX (1UL << 1) ///< Flush Transmit buffer + +/*----- Ethernet MAC VLAN Filter Flag -----*/ +#define ARM_ETH_MAC_VLAN_FILTER_ID_ONLY (1UL << 16) ///< Compare only the VLAN Identifier (12-bit) + + +/****** Ethernet MAC Frame Transmit Flags *****/ +#define ARM_ETH_MAC_TX_FRAME_FRAGMENT (1UL << 0) ///< Indicate frame fragment +#define ARM_ETH_MAC_TX_FRAME_EVENT (1UL << 1) ///< Generate event when frame is transmitted +#define ARM_ETH_MAC_TX_FRAME_TIMESTAMP (1UL << 2) ///< Capture frame time stamp + + +/****** Ethernet MAC Timer Control Codes *****/ +#define ARM_ETH_MAC_TIMER_GET_TIME (0x01) ///< Get current time +#define ARM_ETH_MAC_TIMER_SET_TIME (0x02) ///< Set new time +#define ARM_ETH_MAC_TIMER_INC_TIME (0x03) ///< Increment current time +#define ARM_ETH_MAC_TIMER_DEC_TIME (0x04) ///< Decrement current time +#define ARM_ETH_MAC_TIMER_SET_ALARM (0x05) ///< Set alarm time +#define ARM_ETH_MAC_TIMER_ADJUST_CLOCK (0x06) ///< Adjust clock frequency; time->ns: correction factor * 2^31 + + +/** +\brief Ethernet MAC Time +*/ +typedef struct _ARM_ETH_MAC_TIME { + uint32_t ns; ///< Nano seconds + uint32_t sec; ///< Seconds +} ARM_ETH_MAC_TIME; + + +/****** Ethernet MAC Event *****/ +#define ARM_ETH_MAC_EVENT_RX_FRAME (1UL << 0) ///< Frame Received +#define ARM_ETH_MAC_EVENT_TX_FRAME (1UL << 1) ///< Frame Transmitted +#define ARM_ETH_MAC_EVENT_WAKEUP (1UL << 2) ///< Wake-up (on Magic Packet) +#define ARM_ETH_MAC_EVENT_TIMER_ALARM (1UL << 3) ///< Timer Alarm + + +// Function documentation +/** + \fn ARM_DRIVER_VERSION ARM_ETH_MAC_GetVersion (void) + \brief Get driver version. + \return \ref ARM_DRIVER_VERSION +*/ +/** + \fn ARM_ETH_MAC_CAPABILITIES ARM_ETH_MAC_GetCapabilities (void) + \brief Get driver capabilities. + \return \ref ARM_ETH_MAC_CAPABILITIES +*/ +/** + \fn int32_t ARM_ETH_MAC_Initialize (ARM_ETH_MAC_SignalEvent_t cb_event) + \brief Initialize Ethernet MAC Device. + \param[in] cb_event Pointer to \ref ARM_ETH_MAC_SignalEvent + \return \ref execution_status +*/ +/** + \fn int32_t ARM_ETH_MAC_Uninitialize (void) + \brief De-initialize Ethernet MAC Device. + \return \ref execution_status +*/ +/** + \fn int32_t ARM_ETH_MAC_PowerControl (ARM_POWER_STATE state) + \brief Control Ethernet MAC Device Power. + \param[in] state Power state + \return \ref execution_status +*/ +/** + \fn int32_t ARM_ETH_MAC_GetMacAddress (ARM_ETH_MAC_ADDR *ptr_addr) + \brief Get Ethernet MAC Address. + \param[in] ptr_addr Pointer to address + \return \ref execution_status +*/ +/** + \fn int32_t ARM_ETH_MAC_SetMacAddress (const ARM_ETH_MAC_ADDR *ptr_addr) + \brief Set Ethernet MAC Address. + \param[in] ptr_addr Pointer to address + \return \ref execution_status +*/ +/** + \fn int32_t ARM_ETH_MAC_SetAddressFilter (const ARM_ETH_MAC_ADDR *ptr_addr, + uint32_t num_addr) + \brief Configure Address Filter. + \param[in] ptr_addr Pointer to addresses + \param[in] num_addr Number of addresses to configure + \return \ref execution_status +*/ +/** + \fn int32_t ARM_ETH_MAC_SendFrame (const uint8_t *frame, uint32_t len, uint32_t flags) + \brief Send Ethernet frame. + \param[in] frame Pointer to frame buffer with data to send + \param[in] len Frame buffer length in bytes + \param[in] flags Frame transmit flags (see ARM_ETH_MAC_TX_FRAME_...) + \return \ref execution_status +*/ +/** + \fn int32_t ARM_ETH_MAC_ReadFrame (uint8_t *frame, uint32_t len) + \brief Read data of received Ethernet frame. + \param[in] frame Pointer to frame buffer for data to read into + \param[in] len Frame buffer length in bytes + \return number of data bytes read or execution status + - value >= 0: number of data bytes read + - value < 0: error occurred, value is execution status as defined with \ref execution_status +*/ +/** + \fn uint32_t ARM_ETH_MAC_GetRxFrameSize (void) + \brief Get size of received Ethernet frame. + \return number of bytes in received frame +*/ +/** + \fn int32_t ARM_ETH_MAC_GetRxFrameTime (ARM_ETH_MAC_TIME *time) + \brief Get time of received Ethernet frame. + \param[in] time Pointer to time structure for data to read into + \return \ref execution_status +*/ +/** + \fn int32_t ARM_ETH_MAC_GetTxFrameTime (ARM_ETH_MAC_TIME *time) + \brief Get time of transmitted Ethernet frame. + \param[in] time Pointer to time structure for data to read into + \return \ref execution_status +*/ +/** + \fn int32_t ARM_ETH_MAC_Control (uint32_t control, uint32_t arg) + \brief Control Ethernet Interface. + \param[in] control Operation + \param[in] arg Argument of operation (optional) + \return \ref execution_status +*/ +/** + \fn int32_t ARM_ETH_MAC_ControlTimer (uint32_t control, ARM_ETH_MAC_TIME *time) + \brief Control Precision Timer. + \param[in] control Operation + \param[in] time Pointer to time structure + \return \ref execution_status +*/ +/** + \fn int32_t ARM_ETH_MAC_PHY_Read (uint8_t phy_addr, uint8_t reg_addr, uint16_t *data) + \brief Read Ethernet PHY Register through Management Interface. + \param[in] phy_addr 5-bit device address + \param[in] reg_addr 5-bit register address + \param[out] data Pointer where the result is written to + \return \ref execution_status +*/ +/** + \fn int32_t ARM_ETH_MAC_PHY_Write (uint8_t phy_addr, uint8_t reg_addr, uint16_t data) + \brief Write Ethernet PHY Register through Management Interface. + \param[in] phy_addr 5-bit device address + \param[in] reg_addr 5-bit register address + \param[in] data 16-bit data to write + \return \ref execution_status +*/ + +/** + \fn void ARM_ETH_MAC_SignalEvent (uint32_t event) + \brief Callback function that signals a Ethernet Event. + \param[in] event event notification mask + \return none +*/ + +typedef void (*ARM_ETH_MAC_SignalEvent_t) (uint32_t event); ///< Pointer to \ref ARM_ETH_MAC_SignalEvent : Signal Ethernet Event. + + +/** +\brief Ethernet MAC Capabilities +*/ +typedef struct _ARM_ETH_MAC_CAPABILITIES { + uint32_t checksum_offload_rx_ip4 : 1; ///< 1 = IPv4 header checksum verified on receive + uint32_t checksum_offload_rx_ip6 : 1; ///< 1 = IPv6 checksum verification supported on receive + uint32_t checksum_offload_rx_udp : 1; ///< 1 = UDP payload checksum verified on receive + uint32_t checksum_offload_rx_tcp : 1; ///< 1 = TCP payload checksum verified on receive + uint32_t checksum_offload_rx_icmp : 1; ///< 1 = ICMP payload checksum verified on receive + uint32_t checksum_offload_tx_ip4 : 1; ///< 1 = IPv4 header checksum generated on transmit + uint32_t checksum_offload_tx_ip6 : 1; ///< 1 = IPv6 checksum generation supported on transmit + uint32_t checksum_offload_tx_udp : 1; ///< 1 = UDP payload checksum generated on transmit + uint32_t checksum_offload_tx_tcp : 1; ///< 1 = TCP payload checksum generated on transmit + uint32_t checksum_offload_tx_icmp : 1; ///< 1 = ICMP payload checksum generated on transmit + uint32_t media_interface : 2; ///< Ethernet Media Interface type + uint32_t mac_address : 1; ///< 1 = driver provides initial valid MAC address + uint32_t event_rx_frame : 1; ///< 1 = callback event \ref ARM_ETH_MAC_EVENT_RX_FRAME generated + uint32_t event_tx_frame : 1; ///< 1 = callback event \ref ARM_ETH_MAC_EVENT_TX_FRAME generated + uint32_t event_wakeup : 1; ///< 1 = wakeup event \ref ARM_ETH_MAC_EVENT_WAKEUP generated + uint32_t precision_timer : 1; ///< 1 = Precision Timer supported + uint32_t reserved : 15; ///< Reserved (must be zero) +} ARM_ETH_MAC_CAPABILITIES; + + +/** +\brief Access structure of the Ethernet MAC Driver +*/ +typedef struct _ARM_DRIVER_ETH_MAC { + ARM_DRIVER_VERSION (*GetVersion) (void); ///< Pointer to \ref ARM_ETH_MAC_GetVersion : Get driver version. + ARM_ETH_MAC_CAPABILITIES (*GetCapabilities) (void); ///< Pointer to \ref ARM_ETH_MAC_GetCapabilities : Get driver capabilities. + int32_t (*Initialize) (ARM_ETH_MAC_SignalEvent_t cb_event); ///< Pointer to \ref ARM_ETH_MAC_Initialize : Initialize Ethernet MAC Device. + int32_t (*Uninitialize) (void); ///< Pointer to \ref ARM_ETH_MAC_Uninitialize : De-initialize Ethernet MAC Device. + int32_t (*PowerControl) (ARM_POWER_STATE state); ///< Pointer to \ref ARM_ETH_MAC_PowerControl : Control Ethernet MAC Device Power. + int32_t (*GetMacAddress) ( ARM_ETH_MAC_ADDR *ptr_addr); ///< Pointer to \ref ARM_ETH_MAC_GetMacAddress : Get Ethernet MAC Address. + int32_t (*SetMacAddress) (const ARM_ETH_MAC_ADDR *ptr_addr); ///< Pointer to \ref ARM_ETH_MAC_SetMacAddress : Set Ethernet MAC Address. + int32_t (*SetAddressFilter)(const ARM_ETH_MAC_ADDR *ptr_addr, uint32_t num_addr); ///< Pointer to \ref ARM_ETH_MAC_SetAddressFilter : Configure Address Filter. + int32_t (*SendFrame) (const uint8_t *frame, uint32_t len, uint32_t flags); ///< Pointer to \ref ARM_ETH_MAC_SendFrame : Send Ethernet frame. + int32_t (*ReadFrame) ( uint8_t *frame, uint32_t len); ///< Pointer to \ref ARM_ETH_MAC_ReadFrame : Read data of received Ethernet frame. + uint32_t (*GetRxFrameSize) (void); ///< Pointer to \ref ARM_ETH_MAC_GetRxFrameSize : Get size of received Ethernet frame. + int32_t (*GetRxFrameTime) (ARM_ETH_MAC_TIME *time); ///< Pointer to \ref ARM_ETH_MAC_GetRxFrameTime : Get time of received Ethernet frame. + int32_t (*GetTxFrameTime) (ARM_ETH_MAC_TIME *time); ///< Pointer to \ref ARM_ETH_MAC_GetTxFrameTime : Get time of transmitted Ethernet frame. + int32_t (*ControlTimer) (uint32_t control, ARM_ETH_MAC_TIME *time); ///< Pointer to \ref ARM_ETH_MAC_ControlTimer : Control Precision Timer. + int32_t (*Control) (uint32_t control, uint32_t arg); ///< Pointer to \ref ARM_ETH_MAC_Control : Control Ethernet Interface. + int32_t (*PHY_Read) (uint8_t phy_addr, uint8_t reg_addr, uint16_t *data); ///< Pointer to \ref ARM_ETH_MAC_PHY_Read : Read Ethernet PHY Register through Management Interface. + int32_t (*PHY_Write) (uint8_t phy_addr, uint8_t reg_addr, uint16_t data); ///< Pointer to \ref ARM_ETH_MAC_PHY_Write : Write Ethernet PHY Register through Management Interface. +} const ARM_DRIVER_ETH_MAC; + +#ifdef __cplusplus +} +#endif + +#endif /* DRIVER_ETH_MAC_H_ */ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_ETH_PHY.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_ETH_PHY.h new file mode 100644 index 000000000000..95635edbe5d9 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_ETH_PHY.h @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2013-2017 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Date: 2. Feb 2017 + * $Revision: V2.1 + * + * Project: Ethernet PHY (Physical Transceiver) Driver definitions + */ + +/* History: + * Version 2.1 + * ARM_ETH_LINK_INFO made volatile + * Version 2.0 + * changed parameter "mode" in function ARM_ETH_PHY_SetMode + * Changed prefix ARM_DRV -> ARM_DRIVER + * Changed return values of some functions to int32_t + * Version 1.10 + * Namespace prefix ARM_ added + * Version 1.00 + * Initial release + */ + +#ifndef DRIVER_ETH_PHY_H_ +#define DRIVER_ETH_PHY_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Driver_ETH.h" + +#define ARM_ETH_PHY_API_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(2,1) /* API version */ + + +#define _ARM_Driver_ETH_PHY_(n) Driver_ETH_PHY##n +#define ARM_Driver_ETH_PHY_(n) _ARM_Driver_ETH_PHY_(n) + + +/****** Ethernet PHY Mode *****/ +#define ARM_ETH_PHY_SPEED_Pos 0 +#define ARM_ETH_PHY_SPEED_Msk (3UL << ARM_ETH_PHY_SPEED_Pos) +#define ARM_ETH_PHY_SPEED_10M (ARM_ETH_SPEED_10M << ARM_ETH_PHY_SPEED_Pos) ///< 10 Mbps link speed +#define ARM_ETH_PHY_SPEED_100M (ARM_ETH_SPEED_100M << ARM_ETH_PHY_SPEED_Pos) ///< 100 Mbps link speed +#define ARM_ETH_PHY_SPEED_1G (ARM_ETH_SPEED_1G << ARM_ETH_PHY_SPEED_Pos) ///< 1 Gpbs link speed +#define ARM_ETH_PHY_DUPLEX_Pos 2 +#define ARM_ETH_PHY_DUPLEX_Msk (1UL << ARM_ETH_PHY_DUPLEX_Pos) +#define ARM_ETH_PHY_DUPLEX_HALF (ARM_ETH_DUPLEX_HALF << ARM_ETH_PHY_DUPLEX_Pos) ///< Half duplex link +#define ARM_ETH_PHY_DUPLEX_FULL (ARM_ETH_DUPLEX_FULL << ARM_ETH_PHY_DUPLEX_Pos) ///< Full duplex link +#define ARM_ETH_PHY_AUTO_NEGOTIATE (1UL << 3) ///< Auto Negotiation mode +#define ARM_ETH_PHY_LOOPBACK (1UL << 4) ///< Loop-back test mode +#define ARM_ETH_PHY_ISOLATE (1UL << 5) ///< Isolate PHY from MII/RMII interface + + +// Function documentation +/** + \fn ARM_DRIVER_VERSION ARM_ETH_PHY_GetVersion (void) + \brief Get driver version. + \return \ref ARM_DRIVER_VERSION +*/ +/** + \fn int32_t ARM_ETH_PHY_Initialize (ARM_ETH_PHY_Read_t fn_read, + ARM_ETH_PHY_Write_t fn_write) + \brief Initialize Ethernet PHY Device. + \param[in] fn_read Pointer to \ref ARM_ETH_MAC_PHY_Read + \param[in] fn_write Pointer to \ref ARM_ETH_MAC_PHY_Write + \return \ref execution_status +*/ +/** + \fn int32_t ARM_ETH_PHY_Uninitialize (void) + \brief De-initialize Ethernet PHY Device. + \return \ref execution_status +*/ +/** + \fn int32_t ARM_ETH_PHY_PowerControl (ARM_POWER_STATE state) + \brief Control Ethernet PHY Device Power. + \param[in] state Power state + \return \ref execution_status +*/ +/** + \fn int32_t ARM_ETH_PHY_SetInterface (uint32_t interface) + \brief Set Ethernet Media Interface. + \param[in] interface Media Interface type + \return \ref execution_status +*/ +/** + \fn int32_t ARM_ETH_PHY_SetMode (uint32_t mode) + \brief Set Ethernet PHY Device Operation mode. + \param[in] mode Operation Mode + \return \ref execution_status +*/ +/** + \fn ARM_ETH_LINK_STATE ARM_ETH_PHY_GetLinkState (void) + \brief Get Ethernet PHY Device Link state. + \return current link status \ref ARM_ETH_LINK_STATE +*/ +/** + \fn ARM_ETH_LINK_INFO ARM_ETH_PHY_GetLinkInfo (void) + \brief Get Ethernet PHY Device Link information. + \return current link parameters \ref ARM_ETH_LINK_INFO +*/ + + +typedef int32_t (*ARM_ETH_PHY_Read_t) (uint8_t phy_addr, uint8_t reg_addr, uint16_t *data); ///< Pointer to \ref ARM_ETH_MAC_PHY_Read : Read Ethernet PHY Register. +typedef int32_t (*ARM_ETH_PHY_Write_t) (uint8_t phy_addr, uint8_t reg_addr, uint16_t data); ///< Pointer to \ref ARM_ETH_MAC_PHY_Write : Write Ethernet PHY Register. + + +/** +\brief Access structure of the Ethernet PHY Driver +*/ +typedef struct _ARM_DRIVER_ETH_PHY { + ARM_DRIVER_VERSION (*GetVersion) (void); ///< Pointer to \ref ARM_ETH_PHY_GetVersion : Get driver version. + int32_t (*Initialize) (ARM_ETH_PHY_Read_t fn_read, + ARM_ETH_PHY_Write_t fn_write); ///< Pointer to \ref ARM_ETH_PHY_Initialize : Initialize PHY Device. + int32_t (*Uninitialize) (void); ///< Pointer to \ref ARM_ETH_PHY_Uninitialize : De-initialize PHY Device. + int32_t (*PowerControl) (ARM_POWER_STATE state); ///< Pointer to \ref ARM_ETH_PHY_PowerControl : Control PHY Device Power. + int32_t (*SetInterface) (uint32_t interface); ///< Pointer to \ref ARM_ETH_PHY_SetInterface : Set Ethernet Media Interface. + int32_t (*SetMode) (uint32_t mode); ///< Pointer to \ref ARM_ETH_PHY_SetMode : Set Ethernet PHY Device Operation mode. + ARM_ETH_LINK_STATE (*GetLinkState) (void); ///< Pointer to \ref ARM_ETH_PHY_GetLinkState : Get Ethernet PHY Device Link state. + ARM_ETH_LINK_INFO (*GetLinkInfo) (void); ///< Pointer to \ref ARM_ETH_PHY_GetLinkInfo : Get Ethernet PHY Device Link information. +} const ARM_DRIVER_ETH_PHY; + +#ifdef __cplusplus +} +#endif + +#endif /* DRIVER_ETH_PHY_H_ */ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_Flash.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_Flash.h new file mode 100644 index 000000000000..7053be281589 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_Flash.h @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2013-2018 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Date: 19. Apr 2018 + * $Revision: V2.2 + * + * Project: Flash Driver definitions + */ + +/* History: + * Version 2.2 + * Padding bytes added to ARM_FLASH_INFO + * Version 2.1 + * ARM_FLASH_STATUS made volatile + * Version 2.0 + * Renamed driver NOR -> Flash (more generic) + * Non-blocking operation + * Added Events, Status and Capabilities + * Linked Flash information (GetInfo) + * Version 1.11 + * Changed prefix ARM_DRV -> ARM_DRIVER + * Version 1.10 + * Namespace prefix ARM_ added + * Version 1.00 + * Initial release + */ + +#ifndef DRIVER_FLASH_H_ +#define DRIVER_FLASH_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Driver_Common.h" + +#define ARM_FLASH_API_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(2,2) /* API version */ + + +#define _ARM_Driver_Flash_(n) Driver_Flash##n +#define ARM_Driver_Flash_(n) _ARM_Driver_Flash_(n) + + +#define ARM_FLASH_SECTOR_INFO(addr,size) { (addr), (addr)+(size)-1 } + +/** +\brief Flash Sector information +*/ +typedef struct _ARM_FLASH_SECTOR { + uint32_t start; ///< Sector Start address + uint32_t end; ///< Sector End address (start+size-1) +} const ARM_FLASH_SECTOR; + +/** +\brief Flash information +*/ +typedef struct _ARM_FLASH_INFO { + ARM_FLASH_SECTOR *sector_info; ///< Sector layout information (NULL=Uniform sectors) + uint32_t sector_count; ///< Number of sectors + uint32_t sector_size; ///< Uniform sector size in bytes (0=sector_info used) + uint32_t page_size; ///< Optimal programming page size in bytes + uint32_t program_unit; ///< Smallest programmable unit in bytes + uint8_t erased_value; ///< Contents of erased memory (usually 0xFF) + uint8_t reserved[3]; ///< Reserved (must be zero) +} const ARM_FLASH_INFO; + + +/** +\brief Flash Status +*/ +typedef volatile struct _ARM_FLASH_STATUS { + uint32_t busy : 1; ///< Flash busy flag + uint32_t error : 1; ///< Read/Program/Erase error flag (cleared on start of next operation) + uint32_t reserved : 30; +} ARM_FLASH_STATUS; + + +/****** Flash Event *****/ +#define ARM_FLASH_EVENT_READY (1UL << 0) ///< Flash Ready +#define ARM_FLASH_EVENT_ERROR (1UL << 1) ///< Read/Program/Erase Error + + +// Function documentation +/** + \fn ARM_DRIVER_VERSION ARM_Flash_GetVersion (void) + \brief Get driver version. + \return \ref ARM_DRIVER_VERSION +*/ +/** + \fn ARM_FLASH_CAPABILITIES ARM_Flash_GetCapabilities (void) + \brief Get driver capabilities. + \return \ref ARM_FLASH_CAPABILITIES +*/ +/** + \fn int32_t ARM_Flash_Initialize (ARM_Flash_SignalEvent_t cb_event) + \brief Initialize the Flash Interface. + \param[in] cb_event Pointer to \ref ARM_Flash_SignalEvent + \return \ref execution_status +*/ +/** + \fn int32_t ARM_Flash_Uninitialize (void) + \brief De-initialize the Flash Interface. + \return \ref execution_status +*/ +/** + \fn int32_t ARM_Flash_PowerControl (ARM_POWER_STATE state) + \brief Control the Flash interface power. + \param[in] state Power state + \return \ref execution_status +*/ +/** + \fn int32_t ARM_Flash_ReadData (uint32_t addr, void *data, uint32_t cnt) + \brief Read data from Flash. + \param[in] addr Data address. + \param[out] data Pointer to a buffer storing the data read from Flash. + \param[in] cnt Number of data items to read. + \return number of data items read or \ref execution_status +*/ +/** + \fn int32_t ARM_Flash_ProgramData (uint32_t addr, const void *data, uint32_t cnt) + \brief Program data to Flash. + \param[in] addr Data address. + \param[in] data Pointer to a buffer containing the data to be programmed to Flash. + \param[in] cnt Number of data items to program. + \return number of data items programmed or \ref execution_status +*/ +/** + \fn int32_t ARM_Flash_EraseSector (uint32_t addr) + \brief Erase Flash Sector. + \param[in] addr Sector address + \return \ref execution_status +*/ +/** + \fn int32_t ARM_Flash_EraseChip (void) + \brief Erase complete Flash. + Optional function for faster full chip erase. + \return \ref execution_status +*/ +/** + \fn ARM_FLASH_STATUS ARM_Flash_GetStatus (void) + \brief Get Flash status. + \return Flash status \ref ARM_FLASH_STATUS +*/ +/** + \fn ARM_FLASH_INFO * ARM_Flash_GetInfo (void) + \brief Get Flash information. + \return Pointer to Flash information \ref ARM_FLASH_INFO +*/ + +/** + \fn void ARM_Flash_SignalEvent (uint32_t event) + \brief Signal Flash event. + \param[in] event Event notification mask + \return none +*/ + +typedef void (*ARM_Flash_SignalEvent_t) (uint32_t event); ///< Pointer to \ref ARM_Flash_SignalEvent : Signal Flash Event. + + +/** +\brief Flash Driver Capabilities. +*/ +typedef struct _ARM_FLASH_CAPABILITIES { + uint32_t event_ready : 1; ///< Signal Flash Ready event + uint32_t data_width : 2; ///< Data width: 0=8-bit, 1=16-bit, 2=32-bit + uint32_t erase_chip : 1; ///< Supports EraseChip operation + uint32_t reserved : 28; ///< Reserved (must be zero) +} ARM_FLASH_CAPABILITIES; + + +/** +\brief Access structure of the Flash Driver +*/ +typedef struct _ARM_DRIVER_FLASH { + ARM_DRIVER_VERSION (*GetVersion) (void); ///< Pointer to \ref ARM_Flash_GetVersion : Get driver version. + ARM_FLASH_CAPABILITIES (*GetCapabilities)(void); ///< Pointer to \ref ARM_Flash_GetCapabilities : Get driver capabilities. + int32_t (*Initialize) (ARM_Flash_SignalEvent_t cb_event); ///< Pointer to \ref ARM_Flash_Initialize : Initialize Flash Interface. + int32_t (*Uninitialize) (void); ///< Pointer to \ref ARM_Flash_Uninitialize : De-initialize Flash Interface. + int32_t (*PowerControl) (ARM_POWER_STATE state); ///< Pointer to \ref ARM_Flash_PowerControl : Control Flash Interface Power. + int32_t (*ReadData) (uint32_t addr, void *data, uint32_t cnt); ///< Pointer to \ref ARM_Flash_ReadData : Read data from Flash. + int32_t (*ProgramData) (uint32_t addr, const void *data, uint32_t cnt); ///< Pointer to \ref ARM_Flash_ProgramData : Program data to Flash. + int32_t (*EraseSector) (uint32_t addr); ///< Pointer to \ref ARM_Flash_EraseSector : Erase Flash Sector. + int32_t (*EraseChip) (void); ///< Pointer to \ref ARM_Flash_EraseChip : Erase complete Flash. + ARM_FLASH_STATUS (*GetStatus) (void); ///< Pointer to \ref ARM_Flash_GetStatus : Get Flash status. + ARM_FLASH_INFO * (*GetInfo) (void); ///< Pointer to \ref ARM_Flash_GetInfo : Get Flash information. +} const ARM_DRIVER_FLASH; + +#ifdef __cplusplus +} +#endif + +#endif /* DRIVER_FLASH_H_ */ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_I2C.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_I2C.h new file mode 100644 index 000000000000..b9ba9d025109 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_I2C.h @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2013-2017 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Date: 2. Feb 2017 + * $Revision: V2.3 + * + * Project: I2C (Inter-Integrated Circuit) Driver definitions + */ + +/* History: + * Version 2.3 + * ARM_I2C_STATUS made volatile + * Version 2.2 + * Removed function ARM_I2C_MasterTransfer in order to simplify drivers + * and added back parameter "xfer_pending" to functions + * ARM_I2C_MasterTransmit and ARM_I2C_MasterReceive + * Version 2.1 + * Added function ARM_I2C_MasterTransfer and removed parameter "xfer_pending" + * from functions ARM_I2C_MasterTransmit and ARM_I2C_MasterReceive + * Added function ARM_I2C_GetDataCount + * Removed flag "address_nack" from ARM_I2C_STATUS + * Replaced events ARM_I2C_EVENT_MASTER_DONE and ARM_I2C_EVENT_SLAVE_DONE + * with event ARM_I2C_EVENT_TRANSFER_DONE + * Added event ARM_I2C_EVENT_TRANSFER_INCOMPLETE + * Removed parameter "arg" from function ARM_I2C_SignalEvent + * Version 2.0 + * New simplified driver: + * complexity moved to upper layer (especially data handling) + * more unified API for different communication interfaces + * Added: + * Slave Mode + * Changed prefix ARM_DRV -> ARM_DRIVER + * Version 1.10 + * Namespace prefix ARM_ added + * Version 1.00 + * Initial release + */ + +#ifndef DRIVER_I2C_H_ +#define DRIVER_I2C_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Driver_Common.h" + +#define ARM_I2C_API_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(2,3) /* API version */ + + +/****** I2C Control Codes *****/ + +#define ARM_I2C_OWN_ADDRESS (0x01) ///< Set Own Slave Address; arg = address +#define ARM_I2C_BUS_SPEED (0x02) ///< Set Bus Speed; arg = speed +#define ARM_I2C_BUS_CLEAR (0x03) ///< Execute Bus clear: send nine clock pulses +#define ARM_I2C_ABORT_TRANSFER (0x04) ///< Abort Master/Slave Transmit/Receive + +/*----- I2C Bus Speed -----*/ +#define ARM_I2C_BUS_SPEED_STANDARD (0x01) ///< Standard Speed (100kHz) +#define ARM_I2C_BUS_SPEED_FAST (0x02) ///< Fast Speed (400kHz) +#define ARM_I2C_BUS_SPEED_FAST_PLUS (0x03) ///< Fast+ Speed ( 1MHz) +#define ARM_I2C_BUS_SPEED_HIGH (0x04) ///< High Speed (3.4MHz) + + +/****** I2C Address Flags *****/ + +#define ARM_I2C_ADDRESS_10BIT (0x0400) ///< 10-bit address flag +#define ARM_I2C_ADDRESS_GC (0x8000) ///< General Call flag + + +/** +\brief I2C Status +*/ +typedef volatile struct _ARM_I2C_STATUS { + uint32_t busy : 1; ///< Busy flag + uint32_t mode : 1; ///< Mode: 0=Slave, 1=Master + uint32_t direction : 1; ///< Direction: 0=Transmitter, 1=Receiver + uint32_t general_call : 1; ///< General Call indication (cleared on start of next Slave operation) + uint32_t arbitration_lost : 1; ///< Master lost arbitration (cleared on start of next Master operation) + uint32_t bus_error : 1; ///< Bus error detected (cleared on start of next Master/Slave operation) + uint32_t reserved : 26; +} ARM_I2C_STATUS; + + +/****** I2C Event *****/ +#define ARM_I2C_EVENT_TRANSFER_DONE (1UL << 0) ///< Master/Slave Transmit/Receive finished +#define ARM_I2C_EVENT_TRANSFER_INCOMPLETE (1UL << 1) ///< Master/Slave Transmit/Receive incomplete transfer +#define ARM_I2C_EVENT_SLAVE_TRANSMIT (1UL << 2) ///< Addressed as Slave Transmitter but transmit operation is not set. +#define ARM_I2C_EVENT_SLAVE_RECEIVE (1UL << 3) ///< Addressed as Slave Receiver but receive operation is not set. +#define ARM_I2C_EVENT_ADDRESS_NACK (1UL << 4) ///< Address not acknowledged from Slave +#define ARM_I2C_EVENT_GENERAL_CALL (1UL << 5) ///< Slave addressed with general call address +#define ARM_I2C_EVENT_ARBITRATION_LOST (1UL << 6) ///< Master lost arbitration +#define ARM_I2C_EVENT_BUS_ERROR (1UL << 7) ///< Bus error detected (START/STOP at illegal position) +#define ARM_I2C_EVENT_BUS_CLEAR (1UL << 8) ///< Bus clear finished + + +// Function documentation +/** + \fn ARM_DRIVER_VERSION ARM_I2C_GetVersion (void) + \brief Get driver version. + \return \ref ARM_DRIVER_VERSION + + \fn ARM_I2C_CAPABILITIES ARM_I2C_GetCapabilities (void) + \brief Get driver capabilities. + \return \ref ARM_I2C_CAPABILITIES + + \fn int32_t ARM_I2C_Initialize (ARM_I2C_SignalEvent_t cb_event) + \brief Initialize I2C Interface. + \param[in] cb_event Pointer to \ref ARM_I2C_SignalEvent + \return \ref execution_status + + \fn int32_t ARM_I2C_Uninitialize (void) + \brief De-initialize I2C Interface. + \return \ref execution_status + + \fn int32_t ARM_I2C_PowerControl (ARM_POWER_STATE state) + \brief Control I2C Interface Power. + \param[in] state Power state + \return \ref execution_status + + \fn int32_t ARM_I2C_MasterTransmit (uint32_t addr, const uint8_t *data, uint32_t num, bool xfer_pending) + \brief Start transmitting data as I2C Master. + \param[in] addr Slave address (7-bit or 10-bit) + \param[in] data Pointer to buffer with data to transmit to I2C Slave + \param[in] num Number of data bytes to transmit + \param[in] xfer_pending Transfer operation is pending - Stop condition will not be generated + \return \ref execution_status + + \fn int32_t ARM_I2C_MasterReceive (uint32_t addr, uint8_t *data, uint32_t num, bool xfer_pending) + \brief Start receiving data as I2C Master. + \param[in] addr Slave address (7-bit or 10-bit) + \param[out] data Pointer to buffer for data to receive from I2C Slave + \param[in] num Number of data bytes to receive + \param[in] xfer_pending Transfer operation is pending - Stop condition will not be generated + \return \ref execution_status + + \fn int32_t ARM_I2C_SlaveTransmit (const uint8_t *data, uint32_t num) + \brief Start transmitting data as I2C Slave. + \param[in] data Pointer to buffer with data to transmit to I2C Master + \param[in] num Number of data bytes to transmit + \return \ref execution_status + + \fn int32_t ARM_I2C_SlaveReceive (uint8_t *data, uint32_t num) + \brief Start receiving data as I2C Slave. + \param[out] data Pointer to buffer for data to receive from I2C Master + \param[in] num Number of data bytes to receive + \return \ref execution_status + + \fn int32_t ARM_I2C_GetDataCount (void) + \brief Get transferred data count. + \return number of data bytes transferred; -1 when Slave is not addressed by Master + + \fn int32_t ARM_I2C_Control (uint32_t control, uint32_t arg) + \brief Control I2C Interface. + \param[in] control Operation + \param[in] arg Argument of operation (optional) + \return \ref execution_status + + \fn ARM_I2C_STATUS ARM_I2C_GetStatus (void) + \brief Get I2C status. + \return I2C status \ref ARM_I2C_STATUS + + \fn void ARM_I2C_SignalEvent (uint32_t event) + \brief Signal I2C Events. + \param[in] event \ref I2C_events notification mask +*/ + +typedef void (*ARM_I2C_SignalEvent_t) (uint32_t event); ///< Pointer to \ref ARM_I2C_SignalEvent : Signal I2C Event. + + +/** +\brief I2C Driver Capabilities. +*/ +typedef struct _ARM_I2C_CAPABILITIES { + uint32_t address_10_bit : 1; ///< supports 10-bit addressing + uint32_t reserved : 31; ///< Reserved (must be zero) +} ARM_I2C_CAPABILITIES; + + +/** +\brief Access structure of the I2C Driver. +*/ +typedef struct _ARM_DRIVER_I2C { + ARM_DRIVER_VERSION (*GetVersion) (void); ///< Pointer to \ref ARM_I2C_GetVersion : Get driver version. + ARM_I2C_CAPABILITIES (*GetCapabilities)(void); ///< Pointer to \ref ARM_I2C_GetCapabilities : Get driver capabilities. + int32_t (*Initialize) (ARM_I2C_SignalEvent_t cb_event); ///< Pointer to \ref ARM_I2C_Initialize : Initialize I2C Interface. + int32_t (*Uninitialize) (void); ///< Pointer to \ref ARM_I2C_Uninitialize : De-initialize I2C Interface. + int32_t (*PowerControl) (ARM_POWER_STATE state); ///< Pointer to \ref ARM_I2C_PowerControl : Control I2C Interface Power. + int32_t (*MasterTransmit) (uint32_t addr, const uint8_t *data, uint32_t num, bool xfer_pending); ///< Pointer to \ref ARM_I2C_MasterTransmit : Start transmitting data as I2C Master. + int32_t (*MasterReceive) (uint32_t addr, uint8_t *data, uint32_t num, bool xfer_pending); ///< Pointer to \ref ARM_I2C_MasterReceive : Start receiving data as I2C Master. + int32_t (*SlaveTransmit) ( const uint8_t *data, uint32_t num); ///< Pointer to \ref ARM_I2C_SlaveTransmit : Start transmitting data as I2C Slave. + int32_t (*SlaveReceive) ( uint8_t *data, uint32_t num); ///< Pointer to \ref ARM_I2C_SlaveReceive : Start receiving data as I2C Slave. + int32_t (*GetDataCount) (void); ///< Pointer to \ref ARM_I2C_GetDataCount : Get transferred data count. + int32_t (*Control) (uint32_t control, uint32_t arg); ///< Pointer to \ref ARM_I2C_Control : Control I2C Interface. + ARM_I2C_STATUS (*GetStatus) (void); ///< Pointer to \ref ARM_I2C_GetStatus : Get I2C status. +} const ARM_DRIVER_I2C; + +#ifdef __cplusplus +} +#endif + +#endif /* DRIVER_I2C_H_ */ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_MCI.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_MCI.h new file mode 100644 index 000000000000..59caa11216d9 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_MCI.h @@ -0,0 +1,360 @@ +/* + * Copyright (c) 2013-2017 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Date: 2. Feb 2017 + * $Revision: V2.3 + * + * Project: MCI (Memory Card Interface) Driver definitions + */ + +/* History: + * Version 2.3 + * ARM_MCI_STATUS made volatile + * Version 2.2 + * Added timeout and error flags to ARM_MCI_STATUS + * Added support for controlling optional RST_n pin (eMMC) + * Removed explicit Clock Control (ARM_MCI_CONTROL_CLOCK) + * Removed event ARM_MCI_EVENT_BOOT_ACK_TIMEOUT + * Version 2.1 + * Decoupled SPI mode from MCI driver + * Replaced function ARM_MCI_CardSwitchRead with ARM_MCI_ReadCD and ARM_MCI_ReadWP + * Version 2.0 + * Added support for: + * SD UHS-I (Ultra High Speed) + * SD I/O Interrupt + * Read Wait (SD I/O) + * Suspend/Resume (SD I/O) + * MMC Interrupt + * MMC Boot + * Stream Data transfer (MMC) + * VCCQ Power Supply Control (eMMC) + * Command Completion Signal (CCS) for CE-ATA + * Added ARM_MCI_Control function + * Added ARM_MCI_GetStatus function + * Removed ARM_MCI_BusMode, ARM_MCI_BusDataWidth, ARM_MCI_BusSingaling functions + * (replaced by ARM_MCI_Control) + * Changed ARM_MCI_CardPower function (voltage parameter) + * Changed ARM_MCI_SendCommnad function (flags parameter) + * Changed ARM_MCI_SetupTransfer function (mode parameter) + * Removed ARM_MCI_ReadTransfer and ARM_MCI_WriteTransfer functions + * Changed prefix ARM_DRV -> ARM_DRIVER + * Changed return values of some functions to int32_t + * Version 1.10 + * Namespace prefix ARM_ added + * Version 1.00 + * Initial release + */ + +#ifndef DRIVER_MCI_H_ +#define DRIVER_MCI_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Driver_Common.h" + +#define ARM_MCI_API_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(2,3) /* API version */ + + +/****** MCI Send Command Flags *****/ +#define ARM_MCI_RESPONSE_Pos 0 +#define ARM_MCI_RESPONSE_Msk (3UL << ARM_MCI_RESPONSE_Pos) +#define ARM_MCI_RESPONSE_NONE (0UL << ARM_MCI_RESPONSE_Pos) ///< No response expected (default) +#define ARM_MCI_RESPONSE_SHORT (1UL << ARM_MCI_RESPONSE_Pos) ///< Short response (48-bit) +#define ARM_MCI_RESPONSE_SHORT_BUSY (2UL << ARM_MCI_RESPONSE_Pos) ///< Short response with busy signal (48-bit) +#define ARM_MCI_RESPONSE_LONG (3UL << ARM_MCI_RESPONSE_Pos) ///< Long response (136-bit) + +#define ARM_MCI_RESPONSE_INDEX (1UL << 2) ///< Check command index in response +#define ARM_MCI_RESPONSE_CRC (1UL << 3) ///< Check CRC in response + +#define ARM_MCI_WAIT_BUSY (1UL << 4) ///< Wait until busy before sending the command + +#define ARM_MCI_TRANSFER_DATA (1UL << 5) ///< Activate Data transfer + +#define ARM_MCI_CARD_INITIALIZE (1UL << 6) ///< Execute Memory Card initialization sequence + +#define ARM_MCI_INTERRUPT_COMMAND (1UL << 7) ///< Send Interrupt command (CMD40 - MMC only) +#define ARM_MCI_INTERRUPT_RESPONSE (1UL << 8) ///< Send Interrupt response (CMD40 - MMC only) + +#define ARM_MCI_BOOT_OPERATION (1UL << 9) ///< Execute Boot operation (MMC only) +#define ARM_MCI_BOOT_ALTERNATIVE (1UL << 10) ///< Execute Alternative Boot operation (MMC only) +#define ARM_MCI_BOOT_ACK (1UL << 11) ///< Expect Boot Acknowledge (MMC only) + +#define ARM_MCI_CCSD (1UL << 12) ///< Send Command Completion Signal Disable (CCSD) for CE-ATA device +#define ARM_MCI_CCS (1UL << 13) ///< Expect Command Completion Signal (CCS) for CE-ATA device + + +/****** MCI Setup Transfer Mode *****/ +#define ARM_MCI_TRANSFER_READ (0UL << 0) ///< Data Read Transfer (from MCI) +#define ARM_MCI_TRANSFER_WRITE (1UL << 0) ///< Data Write Transfer (to MCI) +#define ARM_MCI_TRANSFER_BLOCK (0UL << 1) ///< Block Data transfer (default) +#define ARM_MCI_TRANSFER_STREAM (1UL << 1) ///< Stream Data transfer (MMC only) + + +/****** MCI Control Codes *****/ +#define ARM_MCI_BUS_SPEED (0x01) ///< Set Bus Speed; arg = requested speed in bits/s; returns configured speed in bits/s +#define ARM_MCI_BUS_SPEED_MODE (0x02) ///< Set Bus Speed Mode as specified with arg +#define ARM_MCI_BUS_CMD_MODE (0x03) ///< Set CMD Line Mode as specified with arg +#define ARM_MCI_BUS_DATA_WIDTH (0x04) ///< Set Bus Data Width as specified with arg +#define ARM_MCI_DRIVER_STRENGTH (0x05) ///< Set SD UHS-I Driver Strength as specified with arg +#define ARM_MCI_CONTROL_RESET (0x06) ///< Control optional RST_n Pin (eMMC); arg: 0=inactive, 1=active +#define ARM_MCI_CONTROL_CLOCK_IDLE (0x07) ///< Control Clock generation on CLK Pin when idle; arg: 0=disabled, 1=enabled +#define ARM_MCI_UHS_TUNING_OPERATION (0x08) ///< Sampling clock Tuning operation (SD UHS-I); arg: 0=reset, 1=execute +#define ARM_MCI_UHS_TUNING_RESULT (0x09) ///< Sampling clock Tuning result (SD UHS-I); returns: 0=done, 1=in progress, -1=error +#define ARM_MCI_DATA_TIMEOUT (0x0A) ///< Set Data timeout; arg = timeout in bus cycles +#define ARM_MCI_CSS_TIMEOUT (0x0B) ///< Set Command Completion Signal (CCS) timeout; arg = timeout in bus cycles +#define ARM_MCI_MONITOR_SDIO_INTERRUPT (0x0C) ///< Monitor SD I/O interrupt: arg: 0=disabled, 1=enabled +#define ARM_MCI_CONTROL_READ_WAIT (0x0D) ///< Control Read/Wait for SD I/O; arg: 0=disabled, 1=enabled +#define ARM_MCI_SUSPEND_TRANSFER (0x0E) ///< Suspend Data transfer (SD I/O); returns number of remaining bytes to transfer +#define ARM_MCI_RESUME_TRANSFER (0x0F) ///< Resume Data transfer (SD I/O) + +/*----- MCI Bus Speed Mode -----*/ +#define ARM_MCI_BUS_DEFAULT_SPEED (0x00) ///< SD/MMC: Default Speed mode up to 25/26MHz +#define ARM_MCI_BUS_HIGH_SPEED (0x01) ///< SD/MMC: High Speed mode up to 50/52MHz +#define ARM_MCI_BUS_UHS_SDR12 (0x02) ///< SD: SDR12 (Single Data Rate) up to 25MHz, 12.5MB/s: UHS-I (Ultra High Speed) 1.8V signaling +#define ARM_MCI_BUS_UHS_SDR25 (0x03) ///< SD: SDR25 (Single Data Rate) up to 50MHz, 25 MB/s: UHS-I (Ultra High Speed) 1.8V signaling +#define ARM_MCI_BUS_UHS_SDR50 (0x04) ///< SD: SDR50 (Single Data Rate) up to 100MHz, 50 MB/s: UHS-I (Ultra High Speed) 1.8V signaling +#define ARM_MCI_BUS_UHS_SDR104 (0x05) ///< SD: SDR104 (Single Data Rate) up to 208MHz, 104 MB/s: UHS-I (Ultra High Speed) 1.8V signaling +#define ARM_MCI_BUS_UHS_DDR50 (0x06) ///< SD: DDR50 (Dual Data Rate) up to 50MHz, 50 MB/s: UHS-I (Ultra High Speed) 1.8V signaling + +/*----- MCI CMD Line Mode -----*/ +#define ARM_MCI_BUS_CMD_PUSH_PULL (0x00) ///< Push-Pull CMD line (default) +#define ARM_MCI_BUS_CMD_OPEN_DRAIN (0x01) ///< Open Drain CMD line (MMC only) + +/*----- MCI Bus Data Width -----*/ +#define ARM_MCI_BUS_DATA_WIDTH_1 (0x00) ///< Bus data width: 1 bit (default) +#define ARM_MCI_BUS_DATA_WIDTH_4 (0x01) ///< Bus data width: 4 bits +#define ARM_MCI_BUS_DATA_WIDTH_8 (0x02) ///< Bus data width: 8 bits +#define ARM_MCI_BUS_DATA_WIDTH_4_DDR (0x03) ///< Bus data width: 4 bits, DDR (Dual Data Rate) - MMC only +#define ARM_MCI_BUS_DATA_WIDTH_8_DDR (0x04) ///< Bus data width: 8 bits, DDR (Dual Data Rate) - MMC only + +/*----- MCI Driver Strength -----*/ +#define ARM_MCI_DRIVER_TYPE_A (0x01) ///< SD UHS-I Driver Type A +#define ARM_MCI_DRIVER_TYPE_B (0x00) ///< SD UHS-I Driver Type B (default) +#define ARM_MCI_DRIVER_TYPE_C (0x02) ///< SD UHS-I Driver Type C +#define ARM_MCI_DRIVER_TYPE_D (0x03) ///< SD UHS-I Driver Type D + + +/****** MCI Card Power *****/ +#define ARM_MCI_POWER_VDD_Pos 0 +#define ARM_MCI_POWER_VDD_Msk (0x0FUL << ARM_MCI_POWER_VDD_Pos) +#define ARM_MCI_POWER_VDD_OFF (0x01UL << ARM_MCI_POWER_VDD_Pos) ///< VDD (VCC) turned off +#define ARM_MCI_POWER_VDD_3V3 (0x02UL << ARM_MCI_POWER_VDD_Pos) ///< VDD (VCC) = 3.3V +#define ARM_MCI_POWER_VDD_1V8 (0x03UL << ARM_MCI_POWER_VDD_Pos) ///< VDD (VCC) = 1.8V +#define ARM_MCI_POWER_VCCQ_Pos 4 +#define ARM_MCI_POWER_VCCQ_Msk (0x0FUL << ARM_MCI_POWER_VCCQ_Pos) +#define ARM_MCI_POWER_VCCQ_OFF (0x01UL << ARM_MCI_POWER_VCCQ_Pos) ///< eMMC VCCQ turned off +#define ARM_MCI_POWER_VCCQ_3V3 (0x02UL << ARM_MCI_POWER_VCCQ_Pos) ///< eMMC VCCQ = 3.3V +#define ARM_MCI_POWER_VCCQ_1V8 (0x03UL << ARM_MCI_POWER_VCCQ_Pos) ///< eMMC VCCQ = 1.8V +#define ARM_MCI_POWER_VCCQ_1V2 (0x04UL << ARM_MCI_POWER_VCCQ_Pos) ///< eMMC VCCQ = 1.2V + + +/** +\brief MCI Status +*/ +typedef volatile struct _ARM_MCI_STATUS { + uint32_t command_active : 1; ///< Command active flag + uint32_t command_timeout : 1; ///< Command timeout flag (cleared on start of next command) + uint32_t command_error : 1; ///< Command error flag (cleared on start of next command) + uint32_t transfer_active : 1; ///< Transfer active flag + uint32_t transfer_timeout : 1; ///< Transfer timeout flag (cleared on start of next command) + uint32_t transfer_error : 1; ///< Transfer error flag (cleared on start of next command) + uint32_t sdio_interrupt : 1; ///< SD I/O Interrupt flag (cleared on start of monitoring) + uint32_t ccs : 1; ///< CCS flag (cleared on start of next command) + uint32_t reserved : 24; +} ARM_MCI_STATUS; + + +/****** MCI Card Event *****/ +#define ARM_MCI_EVENT_CARD_INSERTED (1UL << 0) ///< Memory Card inserted +#define ARM_MCI_EVENT_CARD_REMOVED (1UL << 1) ///< Memory Card removed +#define ARM_MCI_EVENT_COMMAND_COMPLETE (1UL << 2) ///< Command completed +#define ARM_MCI_EVENT_COMMAND_TIMEOUT (1UL << 3) ///< Command timeout +#define ARM_MCI_EVENT_COMMAND_ERROR (1UL << 4) ///< Command response error (CRC error or invalid response) +#define ARM_MCI_EVENT_TRANSFER_COMPLETE (1UL << 5) ///< Data transfer completed +#define ARM_MCI_EVENT_TRANSFER_TIMEOUT (1UL << 6) ///< Data transfer timeout +#define ARM_MCI_EVENT_TRANSFER_ERROR (1UL << 7) ///< Data transfer CRC failed +#define ARM_MCI_EVENT_SDIO_INTERRUPT (1UL << 8) ///< SD I/O Interrupt +#define ARM_MCI_EVENT_CCS (1UL << 9) ///< Command Completion Signal (CCS) +#define ARM_MCI_EVENT_CCS_TIMEOUT (1UL << 10) ///< Command Completion Signal (CCS) Timeout + + +// Function documentation +/** + \fn ARM_DRIVER_VERSION ARM_MCI_GetVersion (void) + \brief Get driver version. + \return \ref ARM_DRIVER_VERSION +*/ +/** + \fn ARM_MCI_CAPABILITIES ARM_MCI_GetCapabilities (void) + \brief Get driver capabilities. + \return \ref ARM_MCI_CAPABILITIES +*/ +/** + \fn int32_t ARM_MCI_Initialize (ARM_MCI_SignalEvent_t cb_event) + \brief Initialize the Memory Card Interface + \param[in] cb_event Pointer to \ref ARM_MCI_SignalEvent + \return \ref execution_status +*/ +/** + \fn int32_t ARM_MCI_Uninitialize (void) + \brief De-initialize Memory Card Interface. + \return \ref execution_status +*/ +/** + \fn int32_t ARM_MCI_PowerControl (ARM_POWER_STATE state) + \brief Control Memory Card Interface Power. + \param[in] state Power state \ref ARM_POWER_STATE + \return \ref execution_status +*/ +/** + \fn int32_t ARM_MCI_CardPower (uint32_t voltage) + \brief Set Memory Card Power supply voltage. + \param[in] voltage Memory Card Power supply voltage + \return \ref execution_status +*/ +/** + \fn int32_t ARM_MCI_ReadCD (void) + \brief Read Card Detect (CD) state. + \return 1:card detected, 0:card not detected, or error +*/ +/** + \fn int32_t ARM_MCI_ReadWP (void) + \brief Read Write Protect (WP) state. + \return 1:write protected, 0:not write protected, or error +*/ +/** + \fn int32_t ARM_MCI_SendCommand (uint32_t cmd, + uint32_t arg, + uint32_t flags, + uint32_t *response) + \brief Send Command to card and get the response. + \param[in] cmd Memory Card command + \param[in] arg Command argument + \param[in] flags Command flags + \param[out] response Pointer to buffer for response + \return \ref execution_status +*/ +/** + \fn int32_t ARM_MCI_SetupTransfer (uint8_t *data, + uint32_t block_count, + uint32_t block_size, + uint32_t mode) + \brief Setup read or write transfer operation. + \param[in,out] data Pointer to data block(s) to be written or read + \param[in] block_count Number of blocks + \param[in] block_size Size of a block in bytes + \param[in] mode Transfer mode + \return \ref execution_status +*/ +/** + \fn int32_t ARM_MCI_AbortTransfer (void) + \brief Abort current read/write data transfer. + \return \ref execution_status +*/ +/** + \fn int32_t ARM_MCI_Control (uint32_t control, uint32_t arg) + \brief Control MCI Interface. + \param[in] control Operation + \param[in] arg Argument of operation (optional) + \return \ref execution_status +*/ +/** + \fn ARM_MCI_STATUS ARM_MCI_GetStatus (void) + \brief Get MCI status. + \return MCI status \ref ARM_MCI_STATUS +*/ + +/** + \fn void ARM_MCI_SignalEvent (uint32_t event) + \brief Callback function that signals a MCI Card Event. + \param[in] event \ref mci_event_gr + \return none +*/ + +typedef void (*ARM_MCI_SignalEvent_t) (uint32_t event); ///< Pointer to \ref ARM_MCI_SignalEvent : Signal MCI Card Event. + + +/** +\brief MCI Driver Capabilities. +*/ +typedef struct _ARM_MCI_CAPABILITIES { + uint32_t cd_state : 1; ///< Card Detect State available + uint32_t cd_event : 1; ///< Signal Card Detect change event + uint32_t wp_state : 1; ///< Write Protect State available + uint32_t vdd : 1; ///< Supports VDD Card Power Supply Control + uint32_t vdd_1v8 : 1; ///< Supports 1.8 VDD Card Power Supply + uint32_t vccq : 1; ///< Supports VCCQ Card Power Supply Control (eMMC) + uint32_t vccq_1v8 : 1; ///< Supports 1.8 VCCQ Card Power Supply (eMMC) + uint32_t vccq_1v2 : 1; ///< Supports 1.2 VCCQ Card Power Supply (eMMC) + uint32_t data_width_4 : 1; ///< Supports 4-bit data + uint32_t data_width_8 : 1; ///< Supports 8-bit data + uint32_t data_width_4_ddr : 1; ///< Supports 4-bit data, DDR (Dual Data Rate) - MMC only + uint32_t data_width_8_ddr : 1; ///< Supports 8-bit data, DDR (Dual Data Rate) - MMC only + uint32_t high_speed : 1; ///< Supports SD/MMC High Speed Mode + uint32_t uhs_signaling : 1; ///< Supports SD UHS-I (Ultra High Speed) 1.8V signaling + uint32_t uhs_tuning : 1; ///< Supports SD UHS-I tuning + uint32_t uhs_sdr50 : 1; ///< Supports SD UHS-I SDR50 (Single Data Rate) up to 50MB/s + uint32_t uhs_sdr104 : 1; ///< Supports SD UHS-I SDR104 (Single Data Rate) up to 104MB/s + uint32_t uhs_ddr50 : 1; ///< Supports SD UHS-I DDR50 (Dual Data Rate) up to 50MB/s + uint32_t uhs_driver_type_a : 1; ///< Supports SD UHS-I Driver Type A + uint32_t uhs_driver_type_c : 1; ///< Supports SD UHS-I Driver Type C + uint32_t uhs_driver_type_d : 1; ///< Supports SD UHS-I Driver Type D + uint32_t sdio_interrupt : 1; ///< Supports SD I/O Interrupt + uint32_t read_wait : 1; ///< Supports Read Wait (SD I/O) + uint32_t suspend_resume : 1; ///< Supports Suspend/Resume (SD I/O) + uint32_t mmc_interrupt : 1; ///< Supports MMC Interrupt + uint32_t mmc_boot : 1; ///< Supports MMC Boot + uint32_t rst_n : 1; ///< Supports RST_n Pin Control (eMMC) + uint32_t ccs : 1; ///< Supports Command Completion Signal (CCS) for CE-ATA + uint32_t ccs_timeout : 1; ///< Supports Command Completion Signal (CCS) timeout for CE-ATA + uint32_t reserved : 3; ///< Reserved (must be zero) +} ARM_MCI_CAPABILITIES; + + +/** +\brief Access structure of the MCI Driver. +*/ +typedef struct _ARM_DRIVER_MCI { + ARM_DRIVER_VERSION (*GetVersion) (void); ///< Pointer to \ref ARM_MCI_GetVersion : Get driver version. + ARM_MCI_CAPABILITIES (*GetCapabilities)(void); ///< Pointer to \ref ARM_MCI_GetCapabilities : Get driver capabilities. + int32_t (*Initialize) (ARM_MCI_SignalEvent_t cb_event); ///< Pointer to \ref ARM_MCI_Initialize : Initialize MCI Interface. + int32_t (*Uninitialize) (void); ///< Pointer to \ref ARM_MCI_Uninitialize : De-initialize MCI Interface. + int32_t (*PowerControl) (ARM_POWER_STATE state); ///< Pointer to \ref ARM_MCI_PowerControl : Control MCI Interface Power. + int32_t (*CardPower) (uint32_t voltage); ///< Pointer to \ref ARM_MCI_CardPower : Set card power supply voltage. + int32_t (*ReadCD) (void); ///< Pointer to \ref ARM_MCI_ReadCD : Read Card Detect (CD) state. + int32_t (*ReadWP) (void); ///< Pointer to \ref ARM_MCI_ReadWP : Read Write Protect (WP) state. + int32_t (*SendCommand) (uint32_t cmd, + uint32_t arg, + uint32_t flags, + uint32_t *response); ///< Pointer to \ref ARM_MCI_SendCommand : Send Command to card and get the response. + int32_t (*SetupTransfer) (uint8_t *data, + uint32_t block_count, + uint32_t block_size, + uint32_t mode); ///< Pointer to \ref ARM_MCI_SetupTransfer : Setup data transfer operation. + int32_t (*AbortTransfer) (void); ///< Pointer to \ref ARM_MCI_AbortTransfer : Abort current data transfer. + int32_t (*Control) (uint32_t control, uint32_t arg); ///< Pointer to \ref ARM_MCI_Control : Control MCI Interface. + ARM_MCI_STATUS (*GetStatus) (void); ///< Pointer to \ref ARM_MCI_GetStatus : Get MCI status. +} const ARM_DRIVER_MCI; + +#ifdef __cplusplus +} +#endif + +#endif /* DRIVER_MCI_H_ */ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_NAND.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_NAND.h new file mode 100644 index 000000000000..827fd4bc208d --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_NAND.h @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2013-2017 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Date: 14. Nov 2017 + * $Revision: V2.3 + * + * Project: NAND Flash Driver definitions + */ + +/* History: + * Version 2.3 + * Extended ARM_NAND_ECC_INFO structure + * Version 2.2 + * ARM_NAND_STATUS made volatile + * Version 2.1 + * Updated ARM_NAND_ECC_INFO structure and ARM_NAND_ECC_xxx definitions + * Version 2.0 + * New simplified driver: + * complexity moved to upper layer (command agnostic) + * Added support for: + * NV-DDR & NV-DDR2 Interface (ONFI specification) + * VCC, VCCQ and VPP Power Supply Control + * WP (Write Protect) Control + * Version 1.11 + * Changed prefix ARM_DRV -> ARM_DRIVER + * Version 1.10 + * Namespace prefix ARM_ added + * Version 1.00 + * Initial release + */ + +#ifndef DRIVER_NAND_H_ +#define DRIVER_NAND_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Driver_Common.h" + +#define ARM_NAND_API_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(2,3) /* API version */ + + +/****** NAND Device Power *****/ +#define ARM_NAND_POWER_VCC_Pos 0 +#define ARM_NAND_POWER_VCC_Msk (0x07UL << ARM_NAND_POWER_VCC_Pos) +#define ARM_NAND_POWER_VCC_OFF (0x01UL << ARM_NAND_POWER_VCC_Pos) ///< VCC Power off +#define ARM_NAND_POWER_VCC_3V3 (0x02UL << ARM_NAND_POWER_VCC_Pos) ///< VCC = 3.3V +#define ARM_NAND_POWER_VCC_1V8 (0x03UL << ARM_NAND_POWER_VCC_Pos) ///< VCC = 1.8V +#define ARM_NAND_POWER_VCCQ_Pos 3 +#define ARM_NAND_POWER_VCCQ_Msk (0x07UL << ARM_NAND_POWER_VCCQ_Pos) +#define ARM_NAND_POWER_VCCQ_OFF (0x01UL << ARM_NAND_POWER_VCCQ_Pos) ///< VCCQ I/O Power off +#define ARM_NAND_POWER_VCCQ_3V3 (0x02UL << ARM_NAND_POWER_VCCQ_Pos) ///< VCCQ = 3.3V +#define ARM_NAND_POWER_VCCQ_1V8 (0x03UL << ARM_NAND_POWER_VCCQ_Pos) ///< VCCQ = 1.8V +#define ARM_NAND_POWER_VPP_OFF (1UL << 6) ///< VPP off +#define ARM_NAND_POWER_VPP_ON (1Ul << 7) ///< VPP on + + +/****** NAND Control Codes *****/ +#define ARM_NAND_BUS_MODE (0x01) ///< Set Bus Mode as specified with arg +#define ARM_NAND_BUS_DATA_WIDTH (0x02) ///< Set Bus Data Width as specified with arg +#define ARM_NAND_DRIVER_STRENGTH (0x03) ///< Set Driver Strength as specified with arg +#define ARM_NAND_DEVICE_READY_EVENT (0x04) ///< Generate \ref ARM_NAND_EVENT_DEVICE_READY; arg: 0=disabled (default), 1=enabled +#define ARM_NAND_DRIVER_READY_EVENT (0x05) ///< Generate \ref ARM_NAND_EVENT_DRIVER_READY; arg: 0=disabled (default), 1=enabled + +/*----- NAND Bus Mode (ONFI - Open NAND Flash Interface) -----*/ +#define ARM_NAND_BUS_INTERFACE_Pos 4 +#define ARM_NAND_BUS_INTERFACE_Msk (0x03UL << ARM_NAND_BUS_INTERFACE_Pos) +#define ARM_NAND_BUS_SDR (0x00UL << ARM_NAND_BUS_INTERFACE_Pos) ///< Data Interface: SDR (Single Data Rate) - Traditional interface (default) +#define ARM_NAND_BUS_DDR (0x01UL << ARM_NAND_BUS_INTERFACE_Pos) ///< Data Interface: NV-DDR (Double Data Rate) +#define ARM_NAND_BUS_DDR2 (0x02UL << ARM_NAND_BUS_INTERFACE_Pos) ///< Data Interface: NV-DDR2 (Double Data Rate) +#define ARM_NAND_BUS_TIMING_MODE_Pos 0 +#define ARM_NAND_BUS_TIMING_MODE_Msk (0x0FUL << ARM_NAND_BUS_TIMING_MODE_Pos) +#define ARM_NAND_BUS_TIMING_MODE_0 (0x00UL << ARM_NAND_BUS_TIMING_MODE_Pos) ///< Timing Mode 0 (default) +#define ARM_NAND_BUS_TIMING_MODE_1 (0x01UL << ARM_NAND_BUS_TIMING_MODE_Pos) ///< Timing Mode 1 +#define ARM_NAND_BUS_TIMING_MODE_2 (0x02UL << ARM_NAND_BUS_TIMING_MODE_Pos) ///< Timing Mode 2 +#define ARM_NAND_BUS_TIMING_MODE_3 (0x03UL << ARM_NAND_BUS_TIMING_MODE_Pos) ///< Timing Mode 3 +#define ARM_NAND_BUS_TIMING_MODE_4 (0x04UL << ARM_NAND_BUS_TIMING_MODE_Pos) ///< Timing Mode 4 (SDR EDO capable) +#define ARM_NAND_BUS_TIMING_MODE_5 (0x05UL << ARM_NAND_BUS_TIMING_MODE_Pos) ///< Timing Mode 5 (SDR EDO capable) +#define ARM_NAND_BUS_TIMING_MODE_6 (0x06UL << ARM_NAND_BUS_TIMING_MODE_Pos) ///< Timing Mode 6 (NV-DDR2 only) +#define ARM_NAND_BUS_TIMING_MODE_7 (0x07UL << ARM_NAND_BUS_TIMING_MODE_Pos) ///< Timing Mode 7 (NV-DDR2 only) +#define ARM_NAND_BUS_DDR2_DO_WCYC_Pos 8 +#define ARM_NAND_BUS_DDR2_DO_WCYC_Msk (0x0FUL << ARM_NAND_BUS_DDR2_DO_WCYC_Pos) +#define ARM_NAND_BUS_DDR2_DO_WCYC_0 (0x00UL << ARM_NAND_BUS_DDR2_DO_WCYC_Pos) ///< DDR2 Data Output Warm-up cycles: 0 (default) +#define ARM_NAND_BUS_DDR2_DO_WCYC_1 (0x01UL << ARM_NAND_BUS_DDR2_DO_WCYC_Pos) ///< DDR2 Data Output Warm-up cycles: 1 +#define ARM_NAND_BUS_DDR2_DO_WCYC_2 (0x02UL << ARM_NAND_BUS_DDR2_DO_WCYC_Pos) ///< DDR2 Data Output Warm-up cycles: 2 +#define ARM_NAND_BUS_DDR2_DO_WCYC_4 (0x03UL << ARM_NAND_BUS_DDR2_DO_WCYC_Pos) ///< DDR2 Data Output Warm-up cycles: 4 +#define ARM_NAND_BUS_DDR2_DI_WCYC_Pos 12 +#define ARM_NAND_BUS_DDR2_DI_WCYC_Msk (0x0FUL << ARM_NAND_BUS_DDR2_DI_WCYC_Pos) +#define ARM_NAND_BUS_DDR2_DI_WCYC_0 (0x00UL << ARM_NAND_BUS_DDR2_DI_WCYC_Pos) ///< DDR2 Data Input Warm-up cycles: 0 (default) +#define ARM_NAND_BUS_DDR2_DI_WCYC_1 (0x01UL << ARM_NAND_BUS_DDR2_DI_WCYC_Pos) ///< DDR2 Data Input Warm-up cycles: 1 +#define ARM_NAND_BUS_DDR2_DI_WCYC_2 (0x02UL << ARM_NAND_BUS_DDR2_DI_WCYC_Pos) ///< DDR2 Data Input Warm-up cycles: 2 +#define ARM_NAND_BUS_DDR2_DI_WCYC_4 (0x03UL << ARM_NAND_BUS_DDR2_DI_WCYC_Pos) ///< DDR2 Data Input Warm-up cycles: 4 +#define ARM_NAND_BUS_DDR2_VEN (1UL << 16) ///< DDR2 Enable external VREFQ as reference +#define ARM_NAND_BUS_DDR2_CMPD (1UL << 17) ///< DDR2 Enable complementary DQS (DQS_c) signal +#define ARM_NAND_BUS_DDR2_CMPR (1UL << 18) ///< DDR2 Enable complementary RE_n (RE_c) signal + +/*----- NAND Data Bus Width -----*/ +#define ARM_NAND_BUS_DATA_WIDTH_8 (0x00) ///< Bus Data Width: 8 bit (default) +#define ARM_NAND_BUS_DATA_WIDTH_16 (0x01) ///< Bus Data Width: 16 bit + +/*----- NAND Driver Strength (ONFI - Open NAND Flash Interface) -----*/ +#define ARM_NAND_DRIVER_STRENGTH_18 (0x00) ///< Driver Strength 2.0x = 18 Ohms +#define ARM_NAND_DRIVER_STRENGTH_25 (0x01) ///< Driver Strength 1.4x = 25 Ohms +#define ARM_NAND_DRIVER_STRENGTH_35 (0x02) ///< Driver Strength 1.0x = 35 Ohms (default) +#define ARM_NAND_DRIVER_STRENGTH_50 (0x03) ///< Driver Strength 0.7x = 50 Ohms + + +/****** NAND ECC for Read/Write Data Mode and Sequence Execution Code *****/ +#define ARM_NAND_ECC_INDEX_Pos 0 +#define ARM_NAND_ECC_INDEX_Msk (0xFFUL << ARM_NAND_ECC_INDEX_Pos) +#define ARM_NAND_ECC(n) ((n) & ARM_NAND_ECC_INDEX_Msk) ///< Select ECC +#define ARM_NAND_ECC0 (1UL << 8) ///< Use ECC0 of selected ECC +#define ARM_NAND_ECC1 (1UL << 9) ///< Use ECC1 of selected ECC + +/****** NAND Flag for Read/Write Data Mode and Sequence Execution Code *****/ +#define ARM_NAND_DRIVER_DONE_EVENT (1UL << 16) ///< Generate \ref ARM_NAND_EVENT_DRIVER_DONE + +/****** NAND Sequence Execution Code *****/ +#define ARM_NAND_CODE_SEND_CMD1 (1UL << 17) ///< Send Command 1 +#define ARM_NAND_CODE_SEND_ADDR_COL1 (1UL << 18) ///< Send Column Address 1 +#define ARM_NAND_CODE_SEND_ADDR_COL2 (1UL << 19) ///< Send Column Address 2 +#define ARM_NAND_CODE_SEND_ADDR_ROW1 (1UL << 20) ///< Send Row Address 1 +#define ARM_NAND_CODE_SEND_ADDR_ROW2 (1UL << 21) ///< Send Row Address 2 +#define ARM_NAND_CODE_SEND_ADDR_ROW3 (1UL << 22) ///< Send Row Address 3 +#define ARM_NAND_CODE_INC_ADDR_ROW (1UL << 23) ///< Auto-increment Row Address +#define ARM_NAND_CODE_WRITE_DATA (1UL << 24) ///< Write Data +#define ARM_NAND_CODE_SEND_CMD2 (1UL << 25) ///< Send Command 2 +#define ARM_NAND_CODE_WAIT_BUSY (1UL << 26) ///< Wait while R/Bn busy +#define ARM_NAND_CODE_READ_DATA (1UL << 27) ///< Read Data +#define ARM_NAND_CODE_SEND_CMD3 (1UL << 28) ///< Send Command 3 +#define ARM_NAND_CODE_READ_STATUS (1UL << 29) ///< Read Status byte and check FAIL bit (bit 0) + +/*----- NAND Sequence Execution Code: Command -----*/ +#define ARM_NAND_CODE_CMD1_Pos 0 +#define ARM_NAND_CODE_CMD1_Msk (0xFFUL << ARM_NAND_CODE_CMD1_Pos) +#define ARM_NAND_CODE_CMD2_Pos 8 +#define ARM_NAND_CODE_CMD2_Msk (0xFFUL << ARM_NAND_CODE_CMD2_Pos) +#define ARM_NAND_CODE_CMD3_Pos 16 +#define ARM_NAND_CODE_CMD3_Msk (0xFFUL << ARM_NAND_CODE_CMD3_Pos) + +/*----- NAND Sequence Execution Code: Column Address -----*/ +#define ARM_NAND_CODE_ADDR_COL1_Pos 0 +#define ARM_NAND_CODE_ADDR_COL1_Msk (0xFFUL << ARM_NAND_CODE_ADDR_COL1_Pos) +#define ARM_NAND_CODE_ADDR_COL2_Pos 8 +#define ARM_NAND_CODE_ADDR_COL2_Msk (0xFFUL << ARM_NAND_CODE_ADDR_COL2_Pos) + +/*----- NAND Sequence Execution Code: Row Address -----*/ +#define ARM_NAND_CODE_ADDR_ROW1_Pos 0 +#define ARM_NAND_CODE_ADDR_ROW1_Msk (0xFFUL << ARM_NAND_CODE_ADDR_ROW1_Pos) +#define ARM_NAND_CODE_ADDR_ROW2_Pos 8 +#define ARM_NAND_CODE_ADDR_ROW2_Msk (0xFFUL << ARM_NAND_CODE_ADDR_ROW2_Pos) +#define ARM_NAND_CODE_ADDR_ROW3_Pos 16 +#define ARM_NAND_CODE_ADDR_ROW3_Msk (0xFFUL << ARM_NAND_CODE_ADDR_ROW3_Pos) + + +/****** NAND specific error codes *****/ +#define ARM_NAND_ERROR_ECC (ARM_DRIVER_ERROR_SPECIFIC - 1) ///< ECC generation/correction failed + + +/** +\brief NAND ECC (Error Correction Code) Information +*/ +typedef struct _ARM_NAND_ECC_INFO { + uint32_t type : 2; ///< Type: 1=ECC0 over Main, 2=ECC0 over Main+Spare, 3=ECC0 over Main and ECC1 over Spare + uint32_t page_layout : 1; ///< Page layout: 0=|Main0|Spare0|...|MainN-1|SpareN-1|, 1=|Main0|...|MainN-1|Spare0|...|SpareN-1| + uint32_t page_count : 3; ///< Number of virtual pages: N = 2 ^ page_count + uint32_t page_size : 4; ///< Virtual Page size (Main+Spare): 0=512+16, 1=1k+32, 2=2k+64, 3=4k+128, 4=8k+256, 8=512+28, 9=1k+56, 10=2k+112, 11=4k+224, 12=8k+448, 15=Not used (extended description) + uint32_t reserved : 14; ///< Reserved (must be zero) + uint32_t correctable_bits : 8; ///< Number of correctable bits (based on 512 byte codeword size) + uint16_t codeword_size [2]; ///< Number of bytes over which ECC is calculated + uint16_t ecc_size [2]; ///< ECC size in bytes (rounded up) + uint16_t ecc_offset [2]; ///< ECC offset in bytes (where ECC starts in Spare) + /* Extended description */ + uint16_t virtual_page_size [2]; ///< Virtual Page size in bytes (Main/Spare) + uint16_t codeword_offset [2]; ///< Codeword offset in bytes (where ECC protected data starts in Main/Spare) + uint16_t codeword_gap [2]; ///< Codeword gap in bytes till next protected data + uint16_t ecc_gap [2]; ///< ECC gap in bytes till next generated ECC +} ARM_NAND_ECC_INFO; + + +/** +\brief NAND Status +*/ +typedef volatile struct _ARM_NAND_STATUS { + uint32_t busy : 1; ///< Driver busy flag + uint32_t ecc_error : 1; ///< ECC error detected (cleared on next Read/WriteData or ExecuteSequence) + uint32_t reserved : 30; +} ARM_NAND_STATUS; + + +/****** NAND Event *****/ +#define ARM_NAND_EVENT_DEVICE_READY (1UL << 0) ///< Device Ready: R/Bn rising edge +#define ARM_NAND_EVENT_DRIVER_READY (1UL << 1) ///< Driver Ready +#define ARM_NAND_EVENT_DRIVER_DONE (1UL << 2) ///< Driver operation done +#define ARM_NAND_EVENT_ECC_ERROR (1UL << 3) ///< ECC could not correct data + + +// Function documentation +/** + \fn ARM_DRIVER_VERSION ARM_NAND_GetVersion (void) + \brief Get driver version. + \return \ref ARM_DRIVER_VERSION +*/ +/** + \fn ARM_NAND_CAPABILITIES ARM_NAND_GetCapabilities (void) + \brief Get driver capabilities. + \return \ref ARM_NAND_CAPABILITIES +*/ +/** + \fn int32_t ARM_NAND_Initialize (ARM_NAND_SignalEvent_t cb_event) + \brief Initialize the NAND Interface. + \param[in] cb_event Pointer to \ref ARM_NAND_SignalEvent + \return \ref execution_status +*/ +/** + \fn int32_t ARM_NAND_Uninitialize (void) + \brief De-initialize the NAND Interface. + \return \ref execution_status +*/ +/** + \fn int32_t ARM_NAND_PowerControl (ARM_POWER_STATE state) + \brief Control the NAND interface power. + \param[in] state Power state + \return \ref execution_status +*/ +/** + \fn int32_t ARM_NAND_DevicePower (uint32_t voltage) + \brief Set device power supply voltage. + \param[in] voltage NAND Device supply voltage + \return \ref execution_status +*/ +/** + \fn int32_t ARM_NAND_WriteProtect (uint32_t dev_num, bool enable) + \brief Control WPn (Write Protect). + \param[in] dev_num Device number + \param[in] enable + - \b false Write Protect off + - \b true Write Protect on + \return \ref execution_status +*/ +/** + \fn int32_t ARM_NAND_ChipEnable (uint32_t dev_num, bool enable) + \brief Control CEn (Chip Enable). + \param[in] dev_num Device number + \param[in] enable + - \b false Chip Enable off + - \b true Chip Enable on + \return \ref execution_status +*/ +/** + \fn int32_t ARM_NAND_GetDeviceBusy (uint32_t dev_num) + \brief Get Device Busy pin state. + \param[in] dev_num Device number + \return 1=busy, 0=not busy, or error +*/ +/** + \fn int32_t ARM_NAND_SendCommand (uint32_t dev_num, uint8_t cmd) + \brief Send command to NAND device. + \param[in] dev_num Device number + \param[in] cmd Command + \return \ref execution_status +*/ +/** + \fn int32_t ARM_NAND_SendAddress (uint32_t dev_num, uint8_t addr) + \brief Send address to NAND device. + \param[in] dev_num Device number + \param[in] addr Address + \return \ref execution_status +*/ +/** + \fn int32_t ARM_NAND_ReadData (uint32_t dev_num, void *data, uint32_t cnt, uint32_t mode) + \brief Read data from NAND device. + \param[in] dev_num Device number + \param[out] data Pointer to buffer for data to read from NAND device + \param[in] cnt Number of data items to read + \param[in] mode Operation mode + \return number of data items read or \ref execution_status +*/ +/** + \fn int32_t ARM_NAND_WriteData (uint32_t dev_num, const void *data, uint32_t cnt, uint32_t mode) + \brief Write data to NAND device. + \param[in] dev_num Device number + \param[out] data Pointer to buffer with data to write to NAND device + \param[in] cnt Number of data items to write + \param[in] mode Operation mode + \return number of data items written or \ref execution_status +*/ +/** + \fn int32_t ARM_NAND_ExecuteSequence (uint32_t dev_num, uint32_t code, uint32_t cmd, + uint32_t addr_col, uint32_t addr_row, + void *data, uint32_t data_cnt, + uint8_t *status, uint32_t *count) + \brief Execute sequence of operations. + \param[in] dev_num Device number + \param[in] code Sequence code + \param[in] cmd Command(s) + \param[in] addr_col Column address + \param[in] addr_row Row address + \param[in,out] data Pointer to data to be written or read + \param[in] data_cnt Number of data items in one iteration + \param[out] status Pointer to status read + \param[in,out] count Number of iterations + \return \ref execution_status +*/ +/** + \fn int32_t ARM_NAND_AbortSequence (uint32_t dev_num) + \brief Abort sequence execution. + \param[in] dev_num Device number + \return \ref execution_status +*/ +/** + \fn int32_t ARM_NAND_Control (uint32_t dev_num, uint32_t control, uint32_t arg) + \brief Control NAND Interface. + \param[in] dev_num Device number + \param[in] control Operation + \param[in] arg Argument of operation + \return \ref execution_status +*/ +/** + \fn ARM_NAND_STATUS ARM_NAND_GetStatus (uint32_t dev_num) + \brief Get NAND status. + \param[in] dev_num Device number + \return NAND status \ref ARM_NAND_STATUS +*/ +/** + \fn int32_t ARM_NAND_InquireECC (int32_t index, ARM_NAND_ECC_INFO *info) + \brief Inquire about available ECC. + \param[in] index Inquire ECC index + \param[out] info Pointer to ECC information \ref ARM_NAND_ECC_INFO retrieved + \return \ref execution_status +*/ + +/** + \fn void ARM_NAND_SignalEvent (uint32_t dev_num, uint32_t event) + \brief Signal NAND event. + \param[in] dev_num Device number + \param[in] event Event notification mask + \return none +*/ + +typedef void (*ARM_NAND_SignalEvent_t) (uint32_t dev_num, uint32_t event); ///< Pointer to \ref ARM_NAND_SignalEvent : Signal NAND Event. + + +/** +\brief NAND Driver Capabilities. +*/ +typedef struct _ARM_NAND_CAPABILITIES { + uint32_t event_device_ready : 1; ///< Signal Device Ready event (R/Bn rising edge) + uint32_t reentrant_operation : 1; ///< Supports re-entrant operation (SendCommand/Address, Read/WriteData) + uint32_t sequence_operation : 1; ///< Supports Sequence operation (ExecuteSequence, AbortSequence) + uint32_t vcc : 1; ///< Supports VCC Power Supply Control + uint32_t vcc_1v8 : 1; ///< Supports 1.8 VCC Power Supply + uint32_t vccq : 1; ///< Supports VCCQ I/O Power Supply Control + uint32_t vccq_1v8 : 1; ///< Supports 1.8 VCCQ I/O Power Supply + uint32_t vpp : 1; ///< Supports VPP High Voltage Power Supply Control + uint32_t wp : 1; ///< Supports WPn (Write Protect) Control + uint32_t ce_lines : 4; ///< Number of CEn (Chip Enable) lines: ce_lines + 1 + uint32_t ce_manual : 1; ///< Supports manual CEn (Chip Enable) Control + uint32_t rb_monitor : 1; ///< Supports R/Bn (Ready/Busy) Monitoring + uint32_t data_width_16 : 1; ///< Supports 16-bit data + uint32_t ddr : 1; ///< Supports NV-DDR Data Interface (ONFI) + uint32_t ddr2 : 1; ///< Supports NV-DDR2 Data Interface (ONFI) + uint32_t sdr_timing_mode : 3; ///< Fastest (highest) SDR Timing Mode supported (ONFI) + uint32_t ddr_timing_mode : 3; ///< Fastest (highest) NV_DDR Timing Mode supported (ONFI) + uint32_t ddr2_timing_mode : 3; ///< Fastest (highest) NV_DDR2 Timing Mode supported (ONFI) + uint32_t driver_strength_18 : 1; ///< Supports Driver Strength 2.0x = 18 Ohms + uint32_t driver_strength_25 : 1; ///< Supports Driver Strength 1.4x = 25 Ohms + uint32_t driver_strength_50 : 1; ///< Supports Driver Strength 0.7x = 50 Ohms + uint32_t reserved : 2; ///< Reserved (must be zero) +} ARM_NAND_CAPABILITIES; + + +/** +\brief Access structure of the NAND Driver. +*/ +typedef struct _ARM_DRIVER_NAND { + ARM_DRIVER_VERSION (*GetVersion) (void); ///< Pointer to \ref ARM_NAND_GetVersion : Get driver version. + ARM_NAND_CAPABILITIES (*GetCapabilities)(void); ///< Pointer to \ref ARM_NAND_GetCapabilities : Get driver capabilities. + int32_t (*Initialize) (ARM_NAND_SignalEvent_t cb_event); ///< Pointer to \ref ARM_NAND_Initialize : Initialize NAND Interface. + int32_t (*Uninitialize) (void); ///< Pointer to \ref ARM_NAND_Uninitialize : De-initialize NAND Interface. + int32_t (*PowerControl) (ARM_POWER_STATE state); ///< Pointer to \ref ARM_NAND_PowerControl : Control NAND Interface Power. + int32_t (*DevicePower) (uint32_t voltage); ///< Pointer to \ref ARM_NAND_DevicePower : Set device power supply voltage. + int32_t (*WriteProtect) (uint32_t dev_num, bool enable); ///< Pointer to \ref ARM_NAND_WriteProtect : Control WPn (Write Protect). + int32_t (*ChipEnable) (uint32_t dev_num, bool enable); ///< Pointer to \ref ARM_NAND_ChipEnable : Control CEn (Chip Enable). + int32_t (*GetDeviceBusy) (uint32_t dev_num); ///< Pointer to \ref ARM_NAND_GetDeviceBusy : Get Device Busy pin state. + int32_t (*SendCommand) (uint32_t dev_num, uint8_t cmd); ///< Pointer to \ref ARM_NAND_SendCommand : Send command to NAND device. + int32_t (*SendAddress) (uint32_t dev_num, uint8_t addr); ///< Pointer to \ref ARM_NAND_SendAddress : Send address to NAND device. + int32_t (*ReadData) (uint32_t dev_num, void *data, uint32_t cnt, uint32_t mode); ///< Pointer to \ref ARM_NAND_ReadData : Read data from NAND device. + int32_t (*WriteData) (uint32_t dev_num, const void *data, uint32_t cnt, uint32_t mode); ///< Pointer to \ref ARM_NAND_WriteData : Write data to NAND device. + int32_t (*ExecuteSequence)(uint32_t dev_num, uint32_t code, uint32_t cmd, + uint32_t addr_col, uint32_t addr_row, + void *data, uint32_t data_cnt, + uint8_t *status, uint32_t *count); ///< Pointer to \ref ARM_NAND_ExecuteSequence : Execute sequence of operations. + int32_t (*AbortSequence) (uint32_t dev_num); ///< Pointer to \ref ARM_NAND_AbortSequence : Abort sequence execution. + int32_t (*Control) (uint32_t dev_num, uint32_t control, uint32_t arg); ///< Pointer to \ref ARM_NAND_Control : Control NAND Interface. + ARM_NAND_STATUS (*GetStatus) (uint32_t dev_num); ///< Pointer to \ref ARM_NAND_GetStatus : Get NAND status. + int32_t (*InquireECC) ( int32_t index, ARM_NAND_ECC_INFO *info); ///< Pointer to \ref ARM_NAND_InquireECC : Inquire about available ECC. +} const ARM_DRIVER_NAND; + +#ifdef __cplusplus +} +#endif + +#endif /* DRIVER_NAND_H_ */ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_SAI.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_SAI.h new file mode 100644 index 000000000000..bff3b95f7d5c --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_SAI.h @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2013-2017 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Date: 2. Feb 2017 + * $Revision: V1.1 + * + * Project: SAI (Serial Audio Interface) Driver definitions + */ + +/* History: + * Version 1.1 + * ARM_SAI_STATUS made volatile + * Version 1.0 + * Initial release + */ + +#ifndef DRIVER_SAI_H_ +#define DRIVER_SAI_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Driver_Common.h" + +#define ARM_SAI_API_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(1,1) /* API version */ + + +/****** SAI Control Codes *****/ + +#define ARM_SAI_CONTROL_Msk (0xFFU) +#define ARM_SAI_CONFIGURE_TX (0x01U) ///< Configure Transmitter; arg1 and arg2 provide additional configuration +#define ARM_SAI_CONFIGURE_RX (0x02U) ///< Configure Receiver; arg1 and arg2 provide additional configuration +#define ARM_SAI_CONTROL_TX (0x03U) ///< Control Transmitter; arg1.0: 0=disable (default), 1=enable; arg1.1: mute +#define ARM_SAI_CONTROL_RX (0x04U) ///< Control Receiver; arg1.0: 0=disable (default), 1=enable +#define ARM_SAI_MASK_SLOTS_TX (0x05U) ///< Mask Transmitter slots; arg1 = mask (bit: 0=active, 1=inactive); all configured slots are active by default +#define ARM_SAI_MASK_SLOTS_RX (0x06U) ///< Mask Receiver slots; arg1 = mask (bit: 0=active, 1=inactive); all configured slots are active by default +#define ARM_SAI_ABORT_SEND (0x07U) ///< Abort \ref ARM_SAI_Send +#define ARM_SAI_ABORT_RECEIVE (0x08U) ///< Abort \ref ARM_SAI_Receive + +/*----- SAI Control Codes: Configuration Parameters: Mode -----*/ +#define ARM_SAI_MODE_Pos 8 +#define ARM_SAI_MODE_Msk (1U << ARM_SAI_MODE_Pos) +#define ARM_SAI_MODE_MASTER (1U << ARM_SAI_MODE_Pos) ///< Master Mode +#define ARM_SAI_MODE_SLAVE (0U << ARM_SAI_MODE_Pos) ///< Slave Mode (default) + +/*----- SAI Control Codes: Configuration Parameters: Synchronization -----*/ +#define ARM_SAI_SYNCHRONIZATION_Pos 9 +#define ARM_SAI_SYNCHRONIZATION_Msk (1U << ARM_SAI_SYNCHRONIZATION_Pos) +#define ARM_SAI_ASYNCHRONOUS (0U << ARM_SAI_SYNCHRONIZATION_Pos) ///< Asynchronous (default) +#define ARM_SAI_SYNCHRONOUS (1U << ARM_SAI_SYNCHRONIZATION_Pos) ///< Synchronous + +/*----- SAI Control Codes: Configuration Parameters: Protocol -----*/ +#define ARM_SAI_PROTOCOL_Pos 10 +#define ARM_SAI_PROTOCOL_Msk (7U << ARM_SAI_PROTOCOL_Pos) +#define ARM_SAI_PROTOCOL_USER (0U << ARM_SAI_PROTOCOL_Pos) ///< User defined (default) +#define ARM_SAI_PROTOCOL_I2S (1U << ARM_SAI_PROTOCOL_Pos) ///< I2S +#define ARM_SAI_PROTOCOL_MSB_JUSTIFIED (2U << ARM_SAI_PROTOCOL_Pos) ///< MSB (left) justified +#define ARM_SAI_PROTOCOL_LSB_JUSTIFIED (3U << ARM_SAI_PROTOCOL_Pos) ///< LSB (right) justified +#define ARM_SAI_PROTOCOL_PCM_SHORT (4U << ARM_SAI_PROTOCOL_Pos) ///< PCM with short frame +#define ARM_SAI_PROTOCOL_PCM_LONG (5U << ARM_SAI_PROTOCOL_Pos) ///< PCM with long frame +#define ARM_SAI_PROTOCOL_AC97 (6U << ARM_SAI_PROTOCOL_Pos) ///< AC'97 + +/*----- SAI Control Codes: Configuration Parameters: Data Size -----*/ +#define ARM_SAI_DATA_SIZE_Pos 13 +#define ARM_SAI_DATA_SIZE_Msk (0x1FU << ARM_SAI_DATA_SIZE_Pos) +#define ARM_SAI_DATA_SIZE(n) ((((n)-1)&0x1FU) << ARM_SAI_DATA_SIZE_Pos) ///< Data size in bits (8..32) + +/*----- SAI Control Codes: Configuration Parameters: Bit Order -----*/ +#define ARM_SAI_BIT_ORDER_Pos 18 +#define ARM_SAI_BIT_ORDER_Msk (1U << ARM_SAI_BIT_ORDER_Pos) +#define ARM_SAI_MSB_FIRST (0U << ARM_SAI_BIT_ORDER_Pos) ///< Data is transferred with MSB first (default) +#define ARM_SAI_LSB_FIRST (1U << ARM_SAI_BIT_ORDER_Pos) ///< Data is transferred with LSB first; User Protocol only (ignored otherwise) + +/*----- SAI Control Codes: Configuration Parameters: Mono Mode -----*/ +#define ARM_SAI_MONO_MODE (1U << 19) ///< Mono Mode (only for I2S, MSB/LSB justified) + +/*----- SAI Control Codes:Configuration Parameters: Companding -----*/ +#define ARM_SAI_COMPANDING_Pos 20 +#define ARM_SAI_COMPANDING_Msk (3U << ARM_SAI_COMPANDING_Pos) +#define ARM_SAI_COMPANDING_NONE (0U << ARM_SAI_COMPANDING_Pos) ///< No companding (default) +#define ARM_SAI_COMPANDING_A_LAW (2U << ARM_SAI_COMPANDING_Pos) ///< A-Law companding +#define ARM_SAI_COMPANDING_U_LAW (3U << ARM_SAI_COMPANDING_Pos) ///< u-Law companding + +/*----- SAI Control Codes: Configuration Parameters: Clock Polarity -----*/ +#define ARM_SAI_CLOCK_POLARITY_Pos 23 +#define ARM_SAI_CLOCK_POLARITY_Msk (1U << ARM_SAI_CLOCK_POLARITY_Pos) +#define ARM_SAI_CLOCK_POLARITY_0 (0U << ARM_SAI_CLOCK_POLARITY_Pos) ///< Drive on falling edge, Capture on rising edge (default) +#define ARM_SAI_CLOCK_POLARITY_1 (1U << ARM_SAI_CLOCK_POLARITY_Pos) ///< Drive on rising edge, Capture on falling edge + +/*----- SAI Control Codes: Configuration Parameters: Master Clock Pin -----*/ +#define ARM_SAI_MCLK_PIN_Pos 24 +#define ARM_SAI_MCLK_PIN_Msk (3U << ARM_SAI_MCLK_PIN_Pos) +#define ARM_SAI_MCLK_PIN_INACTIVE (0U << ARM_SAI_MCLK_PIN_Pos) ///< MCLK not used (default) +#define ARM_SAI_MCLK_PIN_OUTPUT (1U << ARM_SAI_MCLK_PIN_Pos) ///< MCLK is output (Master only) +#define ARM_SAI_MCLK_PIN_INPUT (2U << ARM_SAI_MCLK_PIN_Pos) ///< MCLK is input (Master only) + + +/****** SAI Configuration (arg1) *****/ + +/*----- SAI Configuration (arg1): Frame Length -----*/ +#define ARM_SAI_FRAME_LENGTH_Pos 0 +#define ARM_SAI_FRAME_LENGTH_Msk (0x3FFU << ARM_SAI_FRAME_LENGTH_Pos) +#define ARM_SAI_FRAME_LENGTH(n) ((((n)-1)&0x3FFU) << ARM_SAI_FRAME_LENGTH_Pos) ///< Frame length in bits (8..1024); default depends on protocol and data + +/*----- SAI Configuration (arg1): Frame Sync Width -----*/ +#define ARM_SAI_FRAME_SYNC_WIDTH_Pos 10 +#define ARM_SAI_FRAME_SYNC_WIDTH_Msk (0xFFU << ARM_SAI_FRAME_SYNC_WIDTH_Pos) +#define ARM_SAI_FRAME_SYNC_WIDTH(n) ((((n)-1)&0xFFU) << ARM_SAI_FRAME_SYNC_WIDTH_Pos) ///< Frame Sync width in bits (1..256); default=1; User Protocol only (ignored otherwise) + +/*----- SAI Configuration (arg1): Frame Sync Polarity -----*/ +#define ARM_SAI_FRAME_SYNC_POLARITY_Pos 18 +#define ARM_SAI_FRAME_SYNC_POLARITY_Msk (1U << ARM_SAI_FRAME_SYNC_POLARITY_Pos) +#define ARM_SAI_FRAME_SYNC_POLARITY_HIGH (0U << ARM_SAI_FRAME_SYNC_POLARITY_Pos) ///< Frame Sync is active high (default); User Protocol only (ignored otherwise) +#define ARM_SAI_FRAME_SYNC_POLARITY_LOW (1U << ARM_SAI_FRAME_SYNC_POLARITY_Pos) ///< Frame Sync is active low; User Protocol only (ignored otherwise) + +/*----- SAI Configuration (arg1): Frame Sync Early -----*/ +#define ARM_SAI_FRAME_SYNC_EARLY (1U << 19) ///< Frame Sync one bit before the first bit of the frame; User Protocol only (ignored otherwise) + +/*----- SAI Configuration (arg1): Slot Count -----*/ +#define ARM_SAI_SLOT_COUNT_Pos 20 +#define ARM_SAI_SLOT_COUNT_Msk (0x1FU << ARM_SAI_SLOT_COUNT_Pos) +#define ARM_SAI_SLOT_COUNT(n) ((((n)-1)&0x1FU) << ARM_SAI_SLOT_COUNT_Pos) ///< Number of slots in frame (1..32); default=1; User Protocol only (ignored otherwise) + +/*----- SAI Configuration (arg1): Slot Size -----*/ +#define ARM_SAI_SLOT_SIZE_Pos 25 +#define ARM_SAI_SLOT_SIZE_Msk (3U << ARM_SAI_SLOT_SIZE_Pos) +#define ARM_SAI_SLOT_SIZE_DEFAULT (0U << ARM_SAI_SLOT_SIZE_Pos) ///< Slot size is equal to data size (default) +#define ARM_SAI_SLOT_SIZE_16 (1U << ARM_SAI_SLOT_SIZE_Pos) ///< Slot size = 16 bits; User Protocol only (ignored otherwise) +#define ARM_SAI_SLOT_SIZE_32 (3U << ARM_SAI_SLOT_SIZE_Pos) ///< Slot size = 32 bits; User Protocol only (ignored otherwise) + +/*----- SAI Configuration (arg1): Slot Offset -----*/ +#define ARM_SAI_SLOT_OFFSET_Pos 27 +#define ARM_SAI_SLOT_OFFSET_Msk (0x1FU << ARM_SAI_SLOT_OFFSET_Pos) +#define ARM_SAI_SLOT_OFFSET(n) (((n)&0x1FU) << ARM_SAI_SLOT_OFFSET_Pos) ///< Offset of first data bit in slot (0..31); default=0; User Protocol only (ignored otherwise) + +/****** SAI Configuration (arg2) *****/ + +/*----- SAI Control Codes: Configuration Parameters: Audio Frequency (Master only) -----*/ +#define ARM_SAI_AUDIO_FREQ_Msk (0x0FFFFFU) ///< Audio frequency mask + +/*----- SAI Control Codes: Configuration Parameters: Master Clock Prescaler (Master only and MCLK Pin) -----*/ +#define ARM_SAI_MCLK_PRESCALER_Pos 20 +#define ARM_SAI_MCLK_PRESCALER_Msk (0xFFFU << ARM_SAI_MCLK_PRESCALER_Pos) +#define ARM_SAI_MCLK_PRESCALER(n) ((((n)-1)&0xFFFU) << ARM_SAI_MCLK_PRESCALER_Pos) ///< MCLK prescaler; Audio_frequency = MCLK/n; n = 1..4096 (default=1) + + +/****** SAI specific error codes *****/ +#define ARM_SAI_ERROR_SYNCHRONIZATION (ARM_DRIVER_ERROR_SPECIFIC - 1) ///< Specified Synchronization not supported +#define ARM_SAI_ERROR_PROTOCOL (ARM_DRIVER_ERROR_SPECIFIC - 2) ///< Specified Protocol not supported +#define ARM_SAI_ERROR_DATA_SIZE (ARM_DRIVER_ERROR_SPECIFIC - 3) ///< Specified Data size not supported +#define ARM_SAI_ERROR_BIT_ORDER (ARM_DRIVER_ERROR_SPECIFIC - 4) ///< Specified Bit order not supported +#define ARM_SAI_ERROR_MONO_MODE (ARM_DRIVER_ERROR_SPECIFIC - 5) ///< Specified Mono mode not supported +#define ARM_SAI_ERROR_COMPANDING (ARM_DRIVER_ERROR_SPECIFIC - 6) ///< Specified Companding not supported +#define ARM_SAI_ERROR_CLOCK_POLARITY (ARM_DRIVER_ERROR_SPECIFIC - 7) ///< Specified Clock polarity not supported +#define ARM_SAI_ERROR_AUDIO_FREQ (ARM_DRIVER_ERROR_SPECIFIC - 8) ///< Specified Audio frequency not supported +#define ARM_SAI_ERROR_MCLK_PIN (ARM_DRIVER_ERROR_SPECIFIC - 9) ///< Specified MCLK Pin setting not supported +#define ARM_SAI_ERROR_MCLK_PRESCALER (ARM_DRIVER_ERROR_SPECIFIC - 10) ///< Specified MCLK Prescaler not supported +#define ARM_SAI_ERROR_FRAME_LENGHT (ARM_DRIVER_ERROR_SPECIFIC - 11) ///< Specified Frame length not supported +#define ARM_SAI_ERROR_FRAME_SYNC_WIDTH (ARM_DRIVER_ERROR_SPECIFIC - 12) ///< Specified Frame Sync width not supported +#define ARM_SAI_ERROR_FRAME_SYNC_POLARITY (ARM_DRIVER_ERROR_SPECIFIC - 13) ///< Specified Frame Sync polarity not supported +#define ARM_SAI_ERROR_FRAME_SYNC_EARLY (ARM_DRIVER_ERROR_SPECIFIC - 14) ///< Specified Frame Sync early not supported +#define ARM_SAI_ERROR_SLOT_COUNT (ARM_DRIVER_ERROR_SPECIFIC - 15) ///< Specified Slot count not supported +#define ARM_SAI_ERROR_SLOT_SIZE (ARM_DRIVER_ERROR_SPECIFIC - 16) ///< Specified Slot size not supported +#define ARM_SAI_ERROR_SLOT_OFFESET (ARM_DRIVER_ERROR_SPECIFIC - 17) ///< Specified Slot offset not supported + + +/** +\brief SAI Status +*/ +typedef volatile struct _ARM_SAI_STATUS { + uint32_t tx_busy : 1; ///< Transmitter busy flag + uint32_t rx_busy : 1; ///< Receiver busy flag + uint32_t tx_underflow : 1; ///< Transmit data underflow detected (cleared on start of next send operation) + uint32_t rx_overflow : 1; ///< Receive data overflow detected (cleared on start of next receive operation) + uint32_t frame_error : 1; ///< Sync Frame error detected (cleared on start of next send/receive operation) + uint32_t reserved : 27; +} ARM_SAI_STATUS; + + +/****** SAI Event *****/ +#define ARM_SAI_EVENT_SEND_COMPLETE (1U << 0) ///< Send completed +#define ARM_SAI_EVENT_RECEIVE_COMPLETE (1U << 1) ///< Receive completed +#define ARM_SAI_EVENT_TX_UNDERFLOW (1U << 2) ///< Transmit data not available +#define ARM_SAI_EVENT_RX_OVERFLOW (1U << 3) ///< Receive data overflow +#define ARM_SAI_EVENT_FRAME_ERROR (1U << 4) ///< Sync Frame error in Slave mode (optional) + + +// Function documentation +/** + \fn ARM_DRIVER_VERSION ARM_SAI_GetVersion (void) + \brief Get driver version. + \return \ref ARM_DRIVER_VERSION + + \fn ARM_SAI_CAPABILITIES ARM_SAI_GetCapabilities (void) + \brief Get driver capabilities. + \return \ref ARM_SAI_CAPABILITIES + + \fn int32_t ARM_SAI_Initialize (ARM_SAI_SignalEvent_t cb_event) + \brief Initialize SAI Interface. + \param[in] cb_event Pointer to \ref ARM_SAI_SignalEvent + \return \ref execution_status + + \fn int32_t ARM_SAI_Uninitialize (void) + \brief De-initialize SAI Interface. + \return \ref execution_status + + \fn int32_t ARM_SAI_PowerControl (ARM_POWER_STATE state) + \brief Control SAI Interface Power. + \param[in] state Power state + \return \ref execution_status + + \fn int32_t ARM_SAI_Send (const void *data, uint32_t num) + \brief Start sending data to SAI transmitter. + \param[in] data Pointer to buffer with data to send to SAI transmitter + \param[in] num Number of data items to send + \return \ref execution_status + + \fn int32_t ARM_SAI_Receive (void *data, uint32_t num) + \brief Start receiving data from SAI receiver. + \param[out] data Pointer to buffer for data to receive from SAI receiver + \param[in] num Number of data items to receive + \return \ref execution_status + + \fn uint32_t ARM_SAI_GetTxCount (void) + \brief Get transmitted data count. + \return number of data items transmitted + + \fn uint32_t ARM_SAI_GetRxCount (void) + \brief Get received data count. + \return number of data items received + + \fn int32_t ARM_SAI_Control (uint32_t control, uint32_t arg1, uint32_t arg2) + \brief Control SAI Interface. + \param[in] control Operation + \param[in] arg1 Argument 1 of operation (optional) + \param[in] arg2 Argument 2 of operation (optional) + \return common \ref execution_status and driver specific \ref sai_execution_status + + \fn ARM_SAI_STATUS ARM_SAI_GetStatus (void) + \brief Get SAI status. + \return SAI status \ref ARM_SAI_STATUS + + \fn void ARM_SAI_SignalEvent (uint32_t event) + \brief Signal SAI Events. + \param[in] event \ref SAI_events notification mask + \return none +*/ + +typedef void (*ARM_SAI_SignalEvent_t) (uint32_t event); ///< Pointer to \ref ARM_SAI_SignalEvent : Signal SAI Event. + + +/** +\brief SAI Driver Capabilities. +*/ +typedef struct _ARM_SAI_CAPABILITIES { + uint32_t asynchronous : 1; ///< supports asynchronous Transmit/Receive + uint32_t synchronous : 1; ///< supports synchronous Transmit/Receive + uint32_t protocol_user : 1; ///< supports user defined Protocol + uint32_t protocol_i2s : 1; ///< supports I2S Protocol + uint32_t protocol_justified : 1; ///< supports MSB/LSB justified Protocol + uint32_t protocol_pcm : 1; ///< supports PCM short/long frame Protocol + uint32_t protocol_ac97 : 1; ///< supports AC'97 Protocol + uint32_t mono_mode : 1; ///< supports Mono mode + uint32_t companding : 1; ///< supports Companding + uint32_t mclk_pin : 1; ///< supports MCLK (Master Clock) pin + uint32_t event_frame_error : 1; ///< supports Frame error event: \ref ARM_SAI_EVENT_FRAME_ERROR + uint32_t reserved : 21; ///< Reserved (must be zero) +} ARM_SAI_CAPABILITIES; + + +/** +\brief Access structure of the SAI Driver. +*/ +typedef struct _ARM_DRIVER_SAI { + ARM_DRIVER_VERSION (*GetVersion) (void); ///< Pointer to \ref ARM_SAI_GetVersion : Get driver version. + ARM_SAI_CAPABILITIES (*GetCapabilities) (void); ///< Pointer to \ref ARM_SAI_GetCapabilities : Get driver capabilities. + int32_t (*Initialize) (ARM_SAI_SignalEvent_t cb_event); ///< Pointer to \ref ARM_SAI_Initialize : Initialize SAI Interface. + int32_t (*Uninitialize) (void); ///< Pointer to \ref ARM_SAI_Uninitialize : De-initialize SAI Interface. + int32_t (*PowerControl) (ARM_POWER_STATE state); ///< Pointer to \ref ARM_SAI_PowerControl : Control SAI Interface Power. + int32_t (*Send) (const void *data, uint32_t num); ///< Pointer to \ref ARM_SAI_Send : Start sending data to SAI Interface. + int32_t (*Receive) ( void *data, uint32_t num); ///< Pointer to \ref ARM_SAI_Receive : Start receiving data from SAI Interface. + uint32_t (*GetTxCount) (void); ///< Pointer to \ref ARM_SAI_GetTxCount : Get transmitted data count. + uint32_t (*GetRxCount) (void); ///< Pointer to \ref ARM_SAI_GetRxCount : Get received data count. + int32_t (*Control) (uint32_t control, uint32_t arg1, uint32_t arg2); ///< Pointer to \ref ARM_SAI_Control : Control SAI Interface. + ARM_SAI_STATUS (*GetStatus) (void); ///< Pointer to \ref ARM_SAI_GetStatus : Get SAI status. +} const ARM_DRIVER_SAI; + +#ifdef __cplusplus +} +#endif + +#endif /* DRIVER_SAI_H_ */ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_SPI.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_SPI.h new file mode 100644 index 000000000000..61b4d806c484 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_SPI.h @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2013-2017 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Date: 2. Feb 2017 + * $Revision: V2.2 + * + * Project: SPI (Serial Peripheral Interface) Driver definitions + */ + +/* History: + * Version 2.2 + * ARM_SPI_STATUS made volatile + * Version 2.1 + * Renamed status flag "tx_rx_busy" to "busy" + * Version 2.0 + * New simplified driver: + * complexity moved to upper layer (especially data handling) + * more unified API for different communication interfaces + * Added: + * Slave Mode + * Half-duplex Modes + * Configurable number of data bits + * Support for TI Mode and Microwire + * Changed prefix ARM_DRV -> ARM_DRIVER + * Version 1.10 + * Namespace prefix ARM_ added + * Version 1.01 + * Added "send_done_event" to Capabilities + * Version 1.00 + * Initial release + */ + +#ifndef DRIVER_SPI_H_ +#define DRIVER_SPI_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Driver_Common.h" + +#define ARM_SPI_API_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(2,2) /* API version */ + + +/****** SPI Control Codes *****/ + +#define ARM_SPI_CONTROL_Pos 0 +#define ARM_SPI_CONTROL_Msk (0xFFUL << ARM_SPI_CONTROL_Pos) + +/*----- SPI Control Codes: Mode -----*/ +#define ARM_SPI_MODE_INACTIVE (0x00UL << ARM_SPI_CONTROL_Pos) ///< SPI Inactive +#define ARM_SPI_MODE_MASTER (0x01UL << ARM_SPI_CONTROL_Pos) ///< SPI Master (Output on MOSI, Input on MISO); arg = Bus Speed in bps +#define ARM_SPI_MODE_SLAVE (0x02UL << ARM_SPI_CONTROL_Pos) ///< SPI Slave (Output on MISO, Input on MOSI) +#define ARM_SPI_MODE_MASTER_SIMPLEX (0x03UL << ARM_SPI_CONTROL_Pos) ///< SPI Master (Output/Input on MOSI); arg = Bus Speed in bps +#define ARM_SPI_MODE_SLAVE_SIMPLEX (0x04UL << ARM_SPI_CONTROL_Pos) ///< SPI Slave (Output/Input on MISO) + +/*----- SPI Control Codes: Mode Parameters: Frame Format -----*/ +#define ARM_SPI_FRAME_FORMAT_Pos 8 +#define ARM_SPI_FRAME_FORMAT_Msk (7UL << ARM_SPI_FRAME_FORMAT_Pos) +#define ARM_SPI_CPOL0_CPHA0 (0UL << ARM_SPI_FRAME_FORMAT_Pos) ///< Clock Polarity 0, Clock Phase 0 (default) +#define ARM_SPI_CPOL0_CPHA1 (1UL << ARM_SPI_FRAME_FORMAT_Pos) ///< Clock Polarity 0, Clock Phase 1 +#define ARM_SPI_CPOL1_CPHA0 (2UL << ARM_SPI_FRAME_FORMAT_Pos) ///< Clock Polarity 1, Clock Phase 0 +#define ARM_SPI_CPOL1_CPHA1 (3UL << ARM_SPI_FRAME_FORMAT_Pos) ///< Clock Polarity 1, Clock Phase 1 +#define ARM_SPI_TI_SSI (4UL << ARM_SPI_FRAME_FORMAT_Pos) ///< Texas Instruments Frame Format +#define ARM_SPI_MICROWIRE (5UL << ARM_SPI_FRAME_FORMAT_Pos) ///< National Microwire Frame Format + +/*----- SPI Control Codes: Mode Parameters: Data Bits -----*/ +#define ARM_SPI_DATA_BITS_Pos 12 +#define ARM_SPI_DATA_BITS_Msk (0x3FUL << ARM_SPI_DATA_BITS_Pos) +#define ARM_SPI_DATA_BITS(n) (((n) & 0x3F) << ARM_SPI_DATA_BITS_Pos) ///< Number of Data bits + +/*----- SPI Control Codes: Mode Parameters: Bit Order -----*/ +#define ARM_SPI_BIT_ORDER_Pos 18 +#define ARM_SPI_BIT_ORDER_Msk (1UL << ARM_SPI_BIT_ORDER_Pos) +#define ARM_SPI_MSB_LSB (0UL << ARM_SPI_BIT_ORDER_Pos) ///< SPI Bit order from MSB to LSB (default) +#define ARM_SPI_LSB_MSB (1UL << ARM_SPI_BIT_ORDER_Pos) ///< SPI Bit order from LSB to MSB + +/*----- SPI Control Codes: Mode Parameters: Slave Select Mode -----*/ +#define ARM_SPI_SS_MASTER_MODE_Pos 19 +#define ARM_SPI_SS_MASTER_MODE_Msk (3UL << ARM_SPI_SS_MASTER_MODE_Pos) +#define ARM_SPI_SS_MASTER_UNUSED (0UL << ARM_SPI_SS_MASTER_MODE_Pos) ///< SPI Slave Select when Master: Not used (default) +#define ARM_SPI_SS_MASTER_SW (1UL << ARM_SPI_SS_MASTER_MODE_Pos) ///< SPI Slave Select when Master: Software controlled +#define ARM_SPI_SS_MASTER_HW_OUTPUT (2UL << ARM_SPI_SS_MASTER_MODE_Pos) ///< SPI Slave Select when Master: Hardware controlled Output +#define ARM_SPI_SS_MASTER_HW_INPUT (3UL << ARM_SPI_SS_MASTER_MODE_Pos) ///< SPI Slave Select when Master: Hardware monitored Input +#define ARM_SPI_SS_SLAVE_MODE_Pos 21 +#define ARM_SPI_SS_SLAVE_MODE_Msk (1UL << ARM_SPI_SS_SLAVE_MODE_Pos) +#define ARM_SPI_SS_SLAVE_HW (0UL << ARM_SPI_SS_SLAVE_MODE_Pos) ///< SPI Slave Select when Slave: Hardware monitored (default) +#define ARM_SPI_SS_SLAVE_SW (1UL << ARM_SPI_SS_SLAVE_MODE_Pos) ///< SPI Slave Select when Slave: Software controlled + + +/*----- SPI Control Codes: Miscellaneous Controls -----*/ +#define ARM_SPI_SET_BUS_SPEED (0x10UL << ARM_SPI_CONTROL_Pos) ///< Set Bus Speed in bps; arg = value +#define ARM_SPI_GET_BUS_SPEED (0x11UL << ARM_SPI_CONTROL_Pos) ///< Get Bus Speed in bps +#define ARM_SPI_SET_DEFAULT_TX_VALUE (0x12UL << ARM_SPI_CONTROL_Pos) ///< Set default Transmit value; arg = value +#define ARM_SPI_CONTROL_SS (0x13UL << ARM_SPI_CONTROL_Pos) ///< Control Slave Select; arg: 0=inactive, 1=active +#define ARM_SPI_ABORT_TRANSFER (0x14UL << ARM_SPI_CONTROL_Pos) ///< Abort current data transfer + + +/****** SPI Slave Select Signal definitions *****/ +#define ARM_SPI_SS_INACTIVE 0 ///< SPI Slave Select Signal Inactive +#define ARM_SPI_SS_ACTIVE 1 ///< SPI Slave Select Signal Active + + +/****** SPI specific error codes *****/ +#define ARM_SPI_ERROR_MODE (ARM_DRIVER_ERROR_SPECIFIC - 1) ///< Specified Mode not supported +#define ARM_SPI_ERROR_FRAME_FORMAT (ARM_DRIVER_ERROR_SPECIFIC - 2) ///< Specified Frame Format not supported +#define ARM_SPI_ERROR_DATA_BITS (ARM_DRIVER_ERROR_SPECIFIC - 3) ///< Specified number of Data bits not supported +#define ARM_SPI_ERROR_BIT_ORDER (ARM_DRIVER_ERROR_SPECIFIC - 4) ///< Specified Bit order not supported +#define ARM_SPI_ERROR_SS_MODE (ARM_DRIVER_ERROR_SPECIFIC - 5) ///< Specified Slave Select Mode not supported + + +/** +\brief SPI Status +*/ +typedef volatile struct _ARM_SPI_STATUS { + uint32_t busy : 1; ///< Transmitter/Receiver busy flag + uint32_t data_lost : 1; ///< Data lost: Receive overflow / Transmit underflow (cleared on start of transfer operation) + uint32_t mode_fault : 1; ///< Mode fault detected; optional (cleared on start of transfer operation) + uint32_t reserved : 29; +} ARM_SPI_STATUS; + + +/****** SPI Event *****/ +#define ARM_SPI_EVENT_TRANSFER_COMPLETE (1UL << 0) ///< Data Transfer completed +#define ARM_SPI_EVENT_DATA_LOST (1UL << 1) ///< Data lost: Receive overflow / Transmit underflow +#define ARM_SPI_EVENT_MODE_FAULT (1UL << 2) ///< Master Mode Fault (SS deactivated when Master) + + +// Function documentation +/** + \fn ARM_DRIVER_VERSION ARM_SPI_GetVersion (void) + \brief Get driver version. + \return \ref ARM_DRIVER_VERSION + + \fn ARM_SPI_CAPABILITIES ARM_SPI_GetCapabilities (void) + \brief Get driver capabilities. + \return \ref ARM_SPI_CAPABILITIES + + \fn int32_t ARM_SPI_Initialize (ARM_SPI_SignalEvent_t cb_event) + \brief Initialize SPI Interface. + \param[in] cb_event Pointer to \ref ARM_SPI_SignalEvent + \return \ref execution_status + + \fn int32_t ARM_SPI_Uninitialize (void) + \brief De-initialize SPI Interface. + \return \ref execution_status + + \fn int32_t ARM_SPI_PowerControl (ARM_POWER_STATE state) + \brief Control SPI Interface Power. + \param[in] state Power state + \return \ref execution_status + + \fn int32_t ARM_SPI_Send (const void *data, uint32_t num) + \brief Start sending data to SPI transmitter. + \param[in] data Pointer to buffer with data to send to SPI transmitter + \param[in] num Number of data items to send + \return \ref execution_status + + \fn int32_t ARM_SPI_Receive (void *data, uint32_t num) + \brief Start receiving data from SPI receiver. + \param[out] data Pointer to buffer for data to receive from SPI receiver + \param[in] num Number of data items to receive + \return \ref execution_status + + \fn int32_t ARM_SPI_Transfer (const void *data_out, + void *data_in, + uint32_t num) + \brief Start sending/receiving data to/from SPI transmitter/receiver. + \param[in] data_out Pointer to buffer with data to send to SPI transmitter + \param[out] data_in Pointer to buffer for data to receive from SPI receiver + \param[in] num Number of data items to transfer + \return \ref execution_status + + \fn uint32_t ARM_SPI_GetDataCount (void) + \brief Get transferred data count. + \return number of data items transferred + + \fn int32_t ARM_SPI_Control (uint32_t control, uint32_t arg) + \brief Control SPI Interface. + \param[in] control Operation + \param[in] arg Argument of operation (optional) + \return common \ref execution_status and driver specific \ref spi_execution_status + + \fn ARM_SPI_STATUS ARM_SPI_GetStatus (void) + \brief Get SPI status. + \return SPI status \ref ARM_SPI_STATUS + + \fn void ARM_SPI_SignalEvent (uint32_t event) + \brief Signal SPI Events. + \param[in] event \ref SPI_events notification mask + \return none +*/ + +typedef void (*ARM_SPI_SignalEvent_t) (uint32_t event); ///< Pointer to \ref ARM_SPI_SignalEvent : Signal SPI Event. + + +/** +\brief SPI Driver Capabilities. +*/ +typedef struct _ARM_SPI_CAPABILITIES { + uint32_t simplex : 1; ///< supports Simplex Mode (Master and Slave) + uint32_t ti_ssi : 1; ///< supports TI Synchronous Serial Interface + uint32_t microwire : 1; ///< supports Microwire Interface + uint32_t event_mode_fault : 1; ///< Signal Mode Fault event: \ref ARM_SPI_EVENT_MODE_FAULT + uint32_t reserved : 28; ///< Reserved (must be zero) +} ARM_SPI_CAPABILITIES; + + +/** +\brief Access structure of the SPI Driver. +*/ +typedef struct _ARM_DRIVER_SPI { + ARM_DRIVER_VERSION (*GetVersion) (void); ///< Pointer to \ref ARM_SPI_GetVersion : Get driver version. + ARM_SPI_CAPABILITIES (*GetCapabilities) (void); ///< Pointer to \ref ARM_SPI_GetCapabilities : Get driver capabilities. + int32_t (*Initialize) (ARM_SPI_SignalEvent_t cb_event); ///< Pointer to \ref ARM_SPI_Initialize : Initialize SPI Interface. + int32_t (*Uninitialize) (void); ///< Pointer to \ref ARM_SPI_Uninitialize : De-initialize SPI Interface. + int32_t (*PowerControl) (ARM_POWER_STATE state); ///< Pointer to \ref ARM_SPI_PowerControl : Control SPI Interface Power. + int32_t (*Send) (const void *data, uint32_t num); ///< Pointer to \ref ARM_SPI_Send : Start sending data to SPI Interface. + int32_t (*Receive) ( void *data, uint32_t num); ///< Pointer to \ref ARM_SPI_Receive : Start receiving data from SPI Interface. + int32_t (*Transfer) (const void *data_out, + void *data_in, + uint32_t num); ///< Pointer to \ref ARM_SPI_Transfer : Start sending/receiving data to/from SPI. + uint32_t (*GetDataCount) (void); ///< Pointer to \ref ARM_SPI_GetDataCount : Get transferred data count. + int32_t (*Control) (uint32_t control, uint32_t arg); ///< Pointer to \ref ARM_SPI_Control : Control SPI Interface. + ARM_SPI_STATUS (*GetStatus) (void); ///< Pointer to \ref ARM_SPI_GetStatus : Get SPI status. +} const ARM_DRIVER_SPI; + +#ifdef __cplusplus +} +#endif + +#endif /* DRIVER_SPI_H_ */ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_Storage.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_Storage.h new file mode 100644 index 000000000000..573d628cb587 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_Storage.h @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2006-2017, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DRIVER_STORAGE_H_ +#define DRIVER_STORAGE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "Driver_Common.h" + +#define ARM_STORAGE_API_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(1,1) /* API version */ + + +#define _ARM_Driver_Storage_(n) Driver_Storage##n +#define ARM_Driver_Storage_(n) _ARM_Driver_Storage_(n) + +#define ARM_STORAGE_INVALID_OFFSET (0xFFFFFFFFFFFFFFFFULL) ///< Invalid address (relative to a storage controller's + /// address space). A storage block may never start at this address. + +#define ARM_STORAGE_INVALID_ADDRESS (0xFFFFFFFFUL) ///< Invalid address within the processor's memory address space. + /// Refer to memory-mapped storage, i.e. \ref ARM_DRIVER_STORAGE::ResolveAddress(). + +/****** Storage specific error codes *****/ +#define ARM_STORAGE_ERROR_NOT_ERASABLE (ARM_DRIVER_ERROR_SPECIFIC - 1) ///< Part (or all) of the range provided to Erase() isn't erasable. +#define ARM_STORAGE_ERROR_NOT_PROGRAMMABLE (ARM_DRIVER_ERROR_SPECIFIC - 2) ///< Part (or all) of the range provided to ProgramData() isn't programmable. +#define ARM_STORAGE_ERROR_PROTECTED (ARM_DRIVER_ERROR_SPECIFIC - 3) ///< Part (or all) of the range to Erase() or ProgramData() is protected. + +/** + * \brief Attributes of the storage range within a storage block. + */ +typedef struct _ARM_STORAGE_BLOCK_ATTRIBUTES { + uint32_t erasable : 1; ///< Erasing blocks is permitted with a minimum granularity of 'erase_unit'. + /// @note if 'erasable' is 0 (i.e. the 'erase' operation isn't available) then + /// 'erase_unit' (see below) is immaterial and should be 0. + uint32_t programmable : 1; ///< Writing to ranges is permitted with a minimum granularity of 'program_unit'. + /// Writes are typically achieved through the ProgramData operation (following an erase); + /// if storage isn't erasable (see 'erasable' above) but is memory-mapped + /// (i.e. 'memory_mapped'), it can be written directly using memory-store operations. + uint32_t executable : 1; ///< This storage block can hold program data; the processor can fetch and execute code + /// sourced from it. Often this is accompanied with the device being 'memory_mapped' (see \ref ARM_STORAGE_INFO). + uint32_t protectable : 1; ///< The entire block can be protected from program and erase operations. Once protection + /// is enabled for a block, its 'erasable' and 'programmable' bits are turned off. + uint32_t reserved : 28; + uint32_t erase_unit; ///< Minimum erase size in bytes. + /// The offset of the start of the erase-range should also be aligned with this value. + /// Applicable if the 'erasable' attribute is set for the block. + /// @note if 'erasable' (see above) is 0 (i.e. the 'erase' operation isn't available) then + /// 'erase_unit' is immaterial and should be 0. + uint32_t protection_unit; ///< Minimum protectable size in bytes. Applicable if the 'protectable' + /// attribute is set for the block. This should be a divisor of the block's size. A + /// block can be considered to be made up of consecutive, individually-protectable fragments. +} ARM_STORAGE_BLOCK_ATTRIBUTES; + +/** + * \brief A storage block is a range of memory with uniform attributes. + */ +typedef struct _ARM_STORAGE_BLOCK { + uint64_t addr; ///< This is the start address of the storage block. It is + /// expressed as an offset from the start of the storage map + /// maintained by the owning storage controller. + uint64_t size; ///< This is the size of the storage block, in units of bytes. + /// Together with addr, it describes a range [addr, addr+size). + ARM_STORAGE_BLOCK_ATTRIBUTES attributes; ///< Attributes for this block. +} ARM_STORAGE_BLOCK; + +/** + * The check for a valid ARM_STORAGE_BLOCK. + */ +#define ARM_STORAGE_VALID_BLOCK(BLK) (((BLK)->addr != ARM_STORAGE_INVALID_OFFSET) && ((BLK)->size != 0)) + +/** + * \brief Values for encoding storage memory-types with respect to programmability. + * + * Please ensure that the maximum of the following memory types doesn't exceed 16; we + * encode this in a 4-bit field within ARM_STORAGE_INFO::programmability. + */ +#define ARM_STORAGE_PROGRAMMABILITY_RAM (0x0) +#define ARM_STORAGE_PROGRAMMABILITY_ROM (0x1) ///< Read-only memory. +#define ARM_STORAGE_PROGRAMMABILITY_WORM (0x2) ///< write-once-read-only-memory (WORM). +#define ARM_STORAGE_PROGRAMMABILITY_ERASABLE (0x3) ///< re-programmable based on erase. Supports multiple writes. + +/** + * Values for encoding data-retention levels for storage blocks. + * + * Please ensure that the maximum of the following retention types doesn't exceed 16; we + * encode this in a 4-bit field within ARM_STORAGE_INFO::retention_level. + */ +#define ARM_RETENTION_WHILE_DEVICE_ACTIVE (0x0) ///< Data is retained only during device activity. +#define ARM_RETENTION_ACROSS_SLEEP (0x1) ///< Data is retained across processor sleep. +#define ARM_RETENTION_ACROSS_DEEP_SLEEP (0x2) ///< Data is retained across processor deep-sleep. +#define ARM_RETENTION_BATTERY_BACKED (0x3) ///< Data is battery-backed. Device can be powered off. +#define ARM_RETENTION_NVM (0x4) ///< Data is retained in non-volatile memory. + +/** + * Device Data Security Protection Features. Applicable mostly to EXTERNAL_NVM. + */ +typedef struct _ARM_STORAGE_SECURITY_FEATURES { + uint32_t acls : 1; ///< Protection against internal software attacks using ACLs. + uint32_t rollback_protection : 1; ///< Roll-back protection. Set to true if the creator of the storage + /// can ensure that an external attacker can't force an + /// older firmware to run or to revert back to a previous state. + uint32_t tamper_proof : 1; ///< Tamper-proof memory (will be deleted on tamper-attempts using board level or chip level sensors). + uint32_t internal_flash : 1; ///< Internal flash. + uint32_t reserved1 : 12; + + /** + * Encode support for hardening against various classes of attacks. + */ + uint32_t software_attacks : 1; ///< device software (malware running on the device). + uint32_t board_level_attacks : 1; ///< board level attacks (debug probes, copy protection fuses.) + uint32_t chip_level_attacks : 1; ///< chip level attacks (tamper-protection). + uint32_t side_channel_attacks : 1; ///< side channel attacks. + uint32_t reserved2 : 12; +} ARM_STORAGE_SECURITY_FEATURES; + +#define ARM_STORAGE_PROGRAM_CYCLES_INFINITE (0UL) /**< Infinite or unknown endurance for reprogramming. */ + +/** + * Device level metadata regarding the Storage implementation. + */ +typedef struct _ARM_STORAGE_INFO { + uint64_t total_storage; ///< Total available storage, in bytes. + uint32_t program_unit; ///< Minimum programming size in bytes. + /// The offset of the start of the program-range should also be aligned with this value. + /// Applicable only if the 'programmable' attribute is set for a block. + /// @note setting program_unit to 0 has the effect of disabling the size and alignment + /// restrictions (setting it to 1 also has the same effect). + uint32_t optimal_program_unit; ///< Optimal programming page-size in bytes. Some storage controllers + /// have internal buffers into which to receive data. Writing in chunks of + /// 'optimal_program_unit' would achieve maximum programming speed. + /// Applicable only if the 'programmable' attribute is set for the underlying block(s). + uint32_t program_cycles; ///< A measure of endurance for reprogramming. + /// Use ARM_STORAGE_PROGRAM_CYCLES_INFINITE for infinite or unknown endurance. + uint32_t erased_value : 1; ///< Contents of erased memory (usually 1 to indicate erased bytes with state 0xFF). + uint32_t memory_mapped : 1; ///< This storage device has a mapping onto the processor's memory address space. + /// @note For a memory-mapped block which isn't erasable but is programmable (i.e. if + /// 'erasable' is set to 0, but 'programmable' is 1), writes should be possible directly to + /// the memory-mapped storage without going through the ProgramData operation. + uint32_t programmability : 4; ///< A value to indicate storage programmability. + uint32_t retention_level : 4; + uint32_t reserved : 22; + ARM_STORAGE_SECURITY_FEATURES security; ///< \ref ARM_STORAGE_SECURITY_FEATURES +} ARM_STORAGE_INFO; + +/** +\brief Operating status of the storage controller. +*/ +typedef volatile struct _ARM_STORAGE_STATUS { + uint32_t busy : 1; ///< Controller busy flag + uint32_t error : 1; ///< Read/Program/Erase error flag (cleared on start of next operation) + uint32_t reserved : 30; +} ARM_STORAGE_STATUS; + +/** + * \brief Storage Driver API Capabilities. + */ +typedef struct _ARM_STORAGE_CAPABILITIES { + uint32_t asynchronous_ops : 1; ///< Used to indicate if APIs like initialize, + /// read, erase, program, etc. can operate in asynchronous mode. + /// Setting this bit to 1 means that the driver is capable + /// of launching asynchronous operations; command completion is + /// signaled by the invocation of a completion callback. If + /// set to 1, drivers may still complete asynchronous + /// operations synchronously as necessary (in which case they + /// return a positive error code to indicate synchronous completion). + uint32_t erase_all : 1; ///< Supports EraseAll operation. + uint32_t reserved : 30; ///< Reserved (must be zero) +} ARM_STORAGE_CAPABILITIES; + +/** + * Command opcodes for Storage. + */ +typedef enum _ARM_STORAGE_OPERATION { + ARM_STORAGE_OPERATION_GET_VERSION, + ARM_STORAGE_OPERATION_GET_CAPABILITIES, + ARM_STORAGE_OPERATION_INITIALIZE, + ARM_STORAGE_OPERATION_UNINITIALIZE, + ARM_STORAGE_OPERATION_POWER_CONTROL, + ARM_STORAGE_OPERATION_READ_DATA, + ARM_STORAGE_OPERATION_PROGRAM_DATA, + ARM_STORAGE_OPERATION_ERASE, + ARM_STORAGE_OPERATION_ERASE_ALL, + ARM_STORAGE_OPERATION_GET_STATUS, + ARM_STORAGE_OPERATION_GET_INFO, + ARM_STORAGE_OPERATION_RESOLVE_ADDRESS, + ARM_STORAGE_OPERATION_GET_NEXT_BLOCK, + ARM_STORAGE_OPERATION_GET_BLOCK +} ARM_STORAGE_OPERATION; + +// Function documentation +/** + \fn ARM_DRIVER_VERSION ARM_Storage_GetVersion (void) + \brief Get driver version. + \return \ref ARM_DRIVER_VERSION +*/ +/** + \fn ARM_STORAGE_CAPABILITIES ARM_Storage_GetCapabilities (void) + \brief Get driver capabilities. + \return \ref ARM_STORAGE_CAPABILITIES +*/ +/** + \fn int32_t ARM_Storage_Initialize (ARM_Storage_Callback_t callback) + \brief Initialize the Storage interface. + \param [in] callback Pointer to \ref ARM_Storage_Callback_t. + Caller-defined callback to be invoked upon command completion + for asynchronous APIs (including the completion of + initialization). Use a NULL pointer when no callback + signals are required. + \return If asynchronous activity is launched, invocation + ARM_DRIVER_OK, and the caller can expect to receive a callback in the + future with a status value of ARM_DRIVER_OK or an error-code. In the + case of synchronous execution, control returns after completion with a + value of 1. Return values less than ARM_DRIVER_OK (0) signify errors. +*/ +/** + \fn int32_t ARM_Storage_Uninitialize (void) + \brief De-initialize the Storage Interface. + \return If asynchronous activity is launched, an invocation returns + ARM_DRIVER_OK, and the caller can expect to receive a callback in the + future with a status value of ARM_DRIVER_OK or an error-code. In the + case of synchronous execution, control returns after completion with a + value of 1. Return values less than ARM_DRIVER_OK (0) signify errors. +*/ +/** + \fn int32_t ARM_Storage_PowerControl (ARM_POWER_STATE state) + \brief Control the Storage interface power. + \param[in] state Power state + \return If asynchronous activity is launched, an invocation returns + ARM_DRIVER_OK, and the caller can expect to receive a callback in the + future with a status value of ARM_DRIVER_OK or an error-code. In the + case of synchronous execution, control returns after completion with a + value of 1. Return values less than ARM_DRIVER_OK (0) signify errors. +*/ +/** + \fn int32_t ARM_Storage_ReadData (uint64_t addr, void *data, uint32_t size) + \brief Read data from Storage. + \param[in] addr Data address. + \param[out] data Pointer to a buffer storing the data read from Storage. + \param[in] size Number of bytes to read. The data buffer + should be at least as large as this size. + \return If asynchronous activity is launched, an invocation returns + ARM_DRIVER_OK, and the caller can expect to receive a callback in the + future with the number of successfully transferred bytes passed in as + the 'status' parameter. In the case of synchronous execution, control + returns after completion with a positive transfer-count. Return values + less than ARM_DRIVER_OK (0) signify errors. +*/ +/** + \fn int32_t ARM_Storage_ProgramData (uint64_t addr, const void *data, uint32_t size) + \brief Program data to Storage. + \param [in] addr This is the start address of the range to be written into. It + needs to be aligned to the device's \em program_unit + specified in \ref ARM_STORAGE_INFO. + \param [in] data The source of the write operation. The buffer is owned by the + caller and should remain accessible for the lifetime of this + command. + \param [in] size The number of bytes requested to be written. The buffer + should be at least as large as this size. \note 'size' should + be a multiple of the device's 'program_unit' (see \ref + ARM_STORAGE_INFO). + \return If asynchronous activity is launched, an invocation returns + ARM_DRIVER_OK, and the caller can expect to receive a callback in the + future with the number of successfully transferred bytes passed in as + the 'status' parameter. In the case of synchronous execution, control + returns after completion with a positive transfer-count. Return values + less than ARM_DRIVER_OK (0) signify errors. +*/ +/** + \fn int32_t ARM_Storage_Erase (uint64_t addr, uint32_t size) + \brief Erase Storage range. + \param [in] addr This is the start-address of the range to be erased. It must + start at an 'erase_unit' boundary of the underlying block. + \param [in] size Size (in bytes) of the range to be erased. 'addr + size' + must be aligned with the 'erase_unit' of the underlying + block. + \return If the range to be erased doesn't align with the erase_units of the + respective start and end blocks, ARM_DRIVER_ERROR_PARAMETER is + returned. If any part of the range is protected, + ARM_STORAGE_ERROR_PROTECTED is returned. If any part of the range + is not erasable, ARM_STORAGE_ERROR_NOT_ERASABLE is returned. All + such sanity-check failures result in the error code being + returned synchronously and the storage bytes within the range + remain unaffected. Otherwise the function executes in the + following ways: If asynchronous activity is launched, an + invocation returns ARM_DRIVER_OK, and the caller can expect to + receive a callback in the future with the number of successfully + erased bytes passed in as the 'status' parameter. In the case of + synchronous execution, control returns after completion with a + positive erase-count. Return values less than ARM_DRIVER_OK (0) + signify errors. +*/ +/** + \fn int32_t ARM_Storage_EraseAll (void) + \brief Erase complete Storage. + \return If any part of the storage range is protected, + ARM_STORAGE_ERROR_PROTECTED is returned. If any part of the + storage range is not erasable, ARM_STORAGE_ERROR_NOT_ERASABLE is + returned. All such sanity-check failures result in the error code + being returned synchronously and the storage bytes within the + range remain unaffected. Otherwise the function executes in the + following ways: If asynchronous activity is launched, an + invocation returns ARM_DRIVER_OK, and the caller can expect to + receive a callback in the future with ARM_DRIVER_OK passed in as + the 'status' parameter. In the case of synchronous execution, + control returns after completion with a value of 1. Return values + less than ARM_DRIVER_OK (0) signify errors. +*/ +/** + \fn ARM_STORAGE_STATUS ARM_Storage_GetStatus (void) + \brief Get Storage status. + \return Storage status \ref ARM_STORAGE_STATUS +*/ +/** + \fn int32_t ARM_Storage_GetInfo (ARM_STORAGE_INFO *info) + \brief Get Storage information. + \param[out] info A caller-supplied buffer capable of being filled in with an \ref ARM_STORAGE_INFO. + \return ARM_DRIVER_OK if a ARM_STORAGE_INFO structure containing top level + metadata about the storage controller is filled into the supplied + buffer, else an appropriate error value. +*/ +/** + \fn uint32_t ARM_Storage_ResolveAddress(uint64_t addr) + \brief Resolve an address relative to the storage controller into a memory address. + \param[in] addr The address for which we want a resolution to the processor's physical address space. It is an offset from the + start of the storage map maintained by the owning storage + controller. + \return The resolved address in the processor's address space, else ARM_STORAGE_INVALID_ADDRESS. +*/ +/** + \fn int32_t ARM_Storage_GetNextBlock(const ARM_STORAGE_BLOCK* prev_block, ARM_STORAGE_BLOCK *next_block); + \brief Advance to the successor of the current block (iterator). + \param[in] prev_block An existing block (iterator) within the same storage + controller. The memory buffer holding this block is owned + by the caller. This pointer may be NULL; if so, the + invocation fills in the first block into the out parameter: + 'next_block'. + \param[out] next_block A caller-owned buffer large enough to be filled in with + the following ARM_STORAGE_BLOCK. It is legal to provide the + same buffer using 'next_block' as was passed in with 'prev_block'. It + is also legal to pass a NULL into this parameter if the + caller isn't interested in populating a buffer with the next + block, i.e. if the caller only wishes to establish the + presence of a next block. + \return ARM_DRIVER_OK if a valid next block is found (or first block, if + prev_block is passed as NULL); upon successful operation, the contents + of the next (or first) block are filled into the buffer pointed to by + the parameter 'next_block' and ARM_STORAGE_VALID_BLOCK(next_block) is + guaranteed to be true. Upon reaching the end of the sequence of blocks + (iterators), or in case the driver is unable to fetch information about + the next (or first) block, an error (negative) value is returned and an + invalid StorageBlock is populated into the supplied buffer. If + prev_block is NULL, the first block is returned. +*/ +/** + \fn int32_t ARM_Storage_GetBlock(uint64_t addr, ARM_STORAGE_BLOCK *block); + \brief Find the storage block (iterator) encompassing a given storage address. + \param[in] addr Storage address in bytes. + \param[out] block A caller-owned buffer large enough to be filled in with the + ARM_STORAGE_BLOCK encapsulating the given address. This value + can also be passed in as NULL if the caller isn't interested + in populating a buffer with the block, if the caller only + wishes to establish the presence of a containing storage + block. + \return ARM_DRIVER_OK if a containing storage-block is found. In this case, + if block is non-NULL, the buffer pointed to by it is populated with + the contents of the storage block, i.e. if block is valid and a block is + found, ARM_STORAGE_VALID_BLOCK(block) would return true following this + call. If there is no storage block containing the given offset, or in + case the driver is unable to resolve an address to a storage-block, an + error (negative) value is returned and an invalid StorageBlock is + populated into the supplied buffer. +*/ + +/** + * Provides the typedef for the callback function \ref ARM_Storage_Callback_t. + */ +typedef void (*ARM_Storage_Callback_t)(int32_t status, ARM_STORAGE_OPERATION operation); + +/** + * The set of operations constituting the Storage driver. + */ +typedef struct _ARM_DRIVER_STORAGE { + ARM_DRIVER_VERSION (*GetVersion) (void); ///< Pointer to \ref ARM_Storage_GetVersion : Get driver version. + ARM_STORAGE_CAPABILITIES (*GetCapabilities)(void); ///< Pointer to \ref ARM_Storage_GetCapabilities : Get driver capabilities. + int32_t (*Initialize) (ARM_Storage_Callback_t callback); ///< Pointer to \ref ARM_Storage_Initialize : Initialize the Storage Interface. + int32_t (*Uninitialize) (void); ///< Pointer to \ref ARM_Storage_Uninitialize : De-initialize the Storage Interface. + int32_t (*PowerControl) (ARM_POWER_STATE state); ///< Pointer to \ref ARM_Storage_PowerControl : Control the Storage interface power. + int32_t (*ReadData) (uint64_t addr, void *data, uint32_t size); ///< Pointer to \ref ARM_Storage_ReadData : Read data from Storage. + int32_t (*ProgramData) (uint64_t addr, const void *data, uint32_t size); ///< Pointer to \ref ARM_Storage_ProgramData : Program data to Storage. + int32_t (*Erase) (uint64_t addr, uint32_t size); ///< Pointer to \ref ARM_Storage_Erase : Erase Storage range. + int32_t (*EraseAll) (void); ///< Pointer to \ref ARM_Storage_EraseAll : Erase complete Storage. + ARM_STORAGE_STATUS (*GetStatus) (void); ///< Pointer to \ref ARM_Storage_GetStatus : Get Storage status. + int32_t (*GetInfo) (ARM_STORAGE_INFO *info); ///< Pointer to \ref ARM_Storage_GetInfo : Get Storage information. + uint32_t (*ResolveAddress) (uint64_t addr); ///< Pointer to \ref ARM_Storage_ResolveAddress : Resolve a storage address. + int32_t (*GetNextBlock) (const ARM_STORAGE_BLOCK* prev, ARM_STORAGE_BLOCK *next); ///< Pointer to \ref ARM_Storage_GetNextBlock : fetch successor for current block. + int32_t (*GetBlock) (uint64_t addr, ARM_STORAGE_BLOCK *block); ///< Pointer to \ref ARM_Storage_GetBlock : +} const ARM_DRIVER_STORAGE; + +#ifdef __cplusplus +} +#endif + +#endif /* DRIVER_STORAGE_H_ */ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_USART.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_USART.h new file mode 100644 index 000000000000..e60bf51a317a --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_USART.h @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2013-2017 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Date: 2. Feb 2017 + * $Revision: V2.3 + * + * Project: USART (Universal Synchronous Asynchronous Receiver Transmitter) + * Driver definitions + */ + +/* History: + * Version 2.3 + * ARM_USART_STATUS and ARM_USART_MODEM_STATUS made volatile + * Version 2.2 + * Corrected ARM_USART_CPOL_Pos and ARM_USART_CPHA_Pos definitions + * Version 2.1 + * Removed optional argument parameter from Signal Event + * Version 2.0 + * New simplified driver: + * complexity moved to upper layer (especially data handling) + * more unified API for different communication interfaces + * renamed driver UART -> USART (Asynchronous & Synchronous) + * Added modes: + * Synchronous + * Single-wire + * IrDA + * Smart Card + * Changed prefix ARM_DRV -> ARM_DRIVER + * Version 1.10 + * Namespace prefix ARM_ added + * Version 1.01 + * Added events: + * ARM_UART_EVENT_TX_EMPTY, ARM_UART_EVENT_RX_TIMEOUT + * ARM_UART_EVENT_TX_THRESHOLD, ARM_UART_EVENT_RX_THRESHOLD + * Added functions: SetTxThreshold, SetRxThreshold + * Added "rx_timeout_event" to capabilities + * Version 1.00 + * Initial release + */ + +#ifndef DRIVER_USART_H_ +#define DRIVER_USART_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Driver_Common.h" + +#define ARM_USART_API_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(2,3) /* API version */ + + +/****** USART Control Codes *****/ + +#define ARM_USART_CONTROL_Pos 0 +#define ARM_USART_CONTROL_Msk (0xFFUL << ARM_USART_CONTROL_Pos) + +/*----- USART Control Codes: Mode -----*/ +#define ARM_USART_MODE_ASYNCHRONOUS (0x01UL << ARM_USART_CONTROL_Pos) ///< UART (Asynchronous); arg = Baudrate +#define ARM_USART_MODE_SYNCHRONOUS_MASTER (0x02UL << ARM_USART_CONTROL_Pos) ///< Synchronous Master (generates clock signal); arg = Baudrate +#define ARM_USART_MODE_SYNCHRONOUS_SLAVE (0x03UL << ARM_USART_CONTROL_Pos) ///< Synchronous Slave (external clock signal) +#define ARM_USART_MODE_SINGLE_WIRE (0x04UL << ARM_USART_CONTROL_Pos) ///< UART Single-wire (half-duplex); arg = Baudrate +#define ARM_USART_MODE_IRDA (0x05UL << ARM_USART_CONTROL_Pos) ///< UART IrDA; arg = Baudrate +#define ARM_USART_MODE_SMART_CARD (0x06UL << ARM_USART_CONTROL_Pos) ///< UART Smart Card; arg = Baudrate + +/*----- USART Control Codes: Mode Parameters: Data Bits -----*/ +#define ARM_USART_DATA_BITS_Pos 8 +#define ARM_USART_DATA_BITS_Msk (7UL << ARM_USART_DATA_BITS_Pos) +#define ARM_USART_DATA_BITS_5 (5UL << ARM_USART_DATA_BITS_Pos) ///< 5 Data bits +#define ARM_USART_DATA_BITS_6 (6UL << ARM_USART_DATA_BITS_Pos) ///< 6 Data bit +#define ARM_USART_DATA_BITS_7 (7UL << ARM_USART_DATA_BITS_Pos) ///< 7 Data bits +#define ARM_USART_DATA_BITS_8 (0UL << ARM_USART_DATA_BITS_Pos) ///< 8 Data bits (default) +#define ARM_USART_DATA_BITS_9 (1UL << ARM_USART_DATA_BITS_Pos) ///< 9 Data bits + +/*----- USART Control Codes: Mode Parameters: Parity -----*/ +#define ARM_USART_PARITY_Pos 12 +#define ARM_USART_PARITY_Msk (3UL << ARM_USART_PARITY_Pos) +#define ARM_USART_PARITY_NONE (0UL << ARM_USART_PARITY_Pos) ///< No Parity (default) +#define ARM_USART_PARITY_EVEN (1UL << ARM_USART_PARITY_Pos) ///< Even Parity +#define ARM_USART_PARITY_ODD (2UL << ARM_USART_PARITY_Pos) ///< Odd Parity + +/*----- USART Control Codes: Mode Parameters: Stop Bits -----*/ +#define ARM_USART_STOP_BITS_Pos 14 +#define ARM_USART_STOP_BITS_Msk (3UL << ARM_USART_STOP_BITS_Pos) +#define ARM_USART_STOP_BITS_1 (0UL << ARM_USART_STOP_BITS_Pos) ///< 1 Stop bit (default) +#define ARM_USART_STOP_BITS_2 (1UL << ARM_USART_STOP_BITS_Pos) ///< 2 Stop bits +#define ARM_USART_STOP_BITS_1_5 (2UL << ARM_USART_STOP_BITS_Pos) ///< 1.5 Stop bits +#define ARM_USART_STOP_BITS_0_5 (3UL << ARM_USART_STOP_BITS_Pos) ///< 0.5 Stop bits + +/*----- USART Control Codes: Mode Parameters: Flow Control -----*/ +#define ARM_USART_FLOW_CONTROL_Pos 16 +#define ARM_USART_FLOW_CONTROL_Msk (3UL << ARM_USART_FLOW_CONTROL_Pos) +#define ARM_USART_FLOW_CONTROL_NONE (0UL << ARM_USART_FLOW_CONTROL_Pos) ///< No Flow Control (default) +#define ARM_USART_FLOW_CONTROL_RTS (1UL << ARM_USART_FLOW_CONTROL_Pos) ///< RTS Flow Control +#define ARM_USART_FLOW_CONTROL_CTS (2UL << ARM_USART_FLOW_CONTROL_Pos) ///< CTS Flow Control +#define ARM_USART_FLOW_CONTROL_RTS_CTS (3UL << ARM_USART_FLOW_CONTROL_Pos) ///< RTS/CTS Flow Control + +/*----- USART Control Codes: Mode Parameters: Clock Polarity (Synchronous mode) -----*/ +#define ARM_USART_CPOL_Pos 18 +#define ARM_USART_CPOL_Msk (1UL << ARM_USART_CPOL_Pos) +#define ARM_USART_CPOL0 (0UL << ARM_USART_CPOL_Pos) ///< CPOL = 0 (default) +#define ARM_USART_CPOL1 (1UL << ARM_USART_CPOL_Pos) ///< CPOL = 1 + +/*----- USART Control Codes: Mode Parameters: Clock Phase (Synchronous mode) -----*/ +#define ARM_USART_CPHA_Pos 19 +#define ARM_USART_CPHA_Msk (1UL << ARM_USART_CPHA_Pos) +#define ARM_USART_CPHA0 (0UL << ARM_USART_CPHA_Pos) ///< CPHA = 0 (default) +#define ARM_USART_CPHA1 (1UL << ARM_USART_CPHA_Pos) ///< CPHA = 1 + + +/*----- USART Control Codes: Miscellaneous Controls -----*/ +#define ARM_USART_SET_DEFAULT_TX_VALUE (0x10UL << ARM_USART_CONTROL_Pos) ///< Set default Transmit value (Synchronous Receive only); arg = value +#define ARM_USART_SET_IRDA_PULSE (0x11UL << ARM_USART_CONTROL_Pos) ///< Set IrDA Pulse in ns; arg: 0=3/16 of bit period +#define ARM_USART_SET_SMART_CARD_GUARD_TIME (0x12UL << ARM_USART_CONTROL_Pos) ///< Set Smart Card Guard Time; arg = number of bit periods +#define ARM_USART_SET_SMART_CARD_CLOCK (0x13UL << ARM_USART_CONTROL_Pos) ///< Set Smart Card Clock in Hz; arg: 0=Clock not generated +#define ARM_USART_CONTROL_SMART_CARD_NACK (0x14UL << ARM_USART_CONTROL_Pos) ///< Smart Card NACK generation; arg: 0=disabled, 1=enabled +#define ARM_USART_CONTROL_TX (0x15UL << ARM_USART_CONTROL_Pos) ///< Transmitter; arg: 0=disabled, 1=enabled +#define ARM_USART_CONTROL_RX (0x16UL << ARM_USART_CONTROL_Pos) ///< Receiver; arg: 0=disabled, 1=enabled +#define ARM_USART_CONTROL_BREAK (0x17UL << ARM_USART_CONTROL_Pos) ///< Continuous Break transmission; arg: 0=disabled, 1=enabled +#define ARM_USART_ABORT_SEND (0x18UL << ARM_USART_CONTROL_Pos) ///< Abort \ref ARM_USART_Send +#define ARM_USART_ABORT_RECEIVE (0x19UL << ARM_USART_CONTROL_Pos) ///< Abort \ref ARM_USART_Receive +#define ARM_USART_ABORT_TRANSFER (0x1AUL << ARM_USART_CONTROL_Pos) ///< Abort \ref ARM_USART_Transfer + + + +/****** USART specific error codes *****/ +#define ARM_USART_ERROR_MODE (ARM_DRIVER_ERROR_SPECIFIC - 1) ///< Specified Mode not supported +#define ARM_USART_ERROR_BAUDRATE (ARM_DRIVER_ERROR_SPECIFIC - 2) ///< Specified baudrate not supported +#define ARM_USART_ERROR_DATA_BITS (ARM_DRIVER_ERROR_SPECIFIC - 3) ///< Specified number of Data bits not supported +#define ARM_USART_ERROR_PARITY (ARM_DRIVER_ERROR_SPECIFIC - 4) ///< Specified Parity not supported +#define ARM_USART_ERROR_STOP_BITS (ARM_DRIVER_ERROR_SPECIFIC - 5) ///< Specified number of Stop bits not supported +#define ARM_USART_ERROR_FLOW_CONTROL (ARM_DRIVER_ERROR_SPECIFIC - 6) ///< Specified Flow Control not supported +#define ARM_USART_ERROR_CPOL (ARM_DRIVER_ERROR_SPECIFIC - 7) ///< Specified Clock Polarity not supported +#define ARM_USART_ERROR_CPHA (ARM_DRIVER_ERROR_SPECIFIC - 8) ///< Specified Clock Phase not supported + + +/** +\brief USART Status +*/ +typedef volatile struct _ARM_USART_STATUS { + uint32_t tx_busy : 1; ///< Transmitter busy flag + uint32_t rx_busy : 1; ///< Receiver busy flag + uint32_t tx_underflow : 1; ///< Transmit data underflow detected (cleared on start of next send operation) + uint32_t rx_overflow : 1; ///< Receive data overflow detected (cleared on start of next receive operation) + uint32_t rx_break : 1; ///< Break detected on receive (cleared on start of next receive operation) + uint32_t rx_framing_error : 1; ///< Framing error detected on receive (cleared on start of next receive operation) + uint32_t rx_parity_error : 1; ///< Parity error detected on receive (cleared on start of next receive operation) + uint32_t reserved : 25; +} ARM_USART_STATUS; + +/** +\brief USART Modem Control +*/ +typedef enum _ARM_USART_MODEM_CONTROL { + ARM_USART_RTS_CLEAR, ///< Deactivate RTS + ARM_USART_RTS_SET, ///< Activate RTS + ARM_USART_DTR_CLEAR, ///< Deactivate DTR + ARM_USART_DTR_SET ///< Activate DTR +} ARM_USART_MODEM_CONTROL; + +/** +\brief USART Modem Status +*/ +typedef volatile struct _ARM_USART_MODEM_STATUS { + uint32_t cts : 1; ///< CTS state: 1=Active, 0=Inactive + uint32_t dsr : 1; ///< DSR state: 1=Active, 0=Inactive + uint32_t dcd : 1; ///< DCD state: 1=Active, 0=Inactive + uint32_t ri : 1; ///< RI state: 1=Active, 0=Inactive + uint32_t reserved : 28; +} ARM_USART_MODEM_STATUS; + + +/****** USART Event *****/ +#define ARM_USART_EVENT_SEND_COMPLETE (1UL << 0) ///< Send completed; however USART may still transmit data +#define ARM_USART_EVENT_RECEIVE_COMPLETE (1UL << 1) ///< Receive completed +#define ARM_USART_EVENT_TRANSFER_COMPLETE (1UL << 2) ///< Transfer completed +#define ARM_USART_EVENT_TX_COMPLETE (1UL << 3) ///< Transmit completed (optional) +#define ARM_USART_EVENT_TX_UNDERFLOW (1UL << 4) ///< Transmit data not available (Synchronous Slave) +#define ARM_USART_EVENT_RX_OVERFLOW (1UL << 5) ///< Receive data overflow +#define ARM_USART_EVENT_RX_TIMEOUT (1UL << 6) ///< Receive character timeout (optional) +#define ARM_USART_EVENT_RX_BREAK (1UL << 7) ///< Break detected on receive +#define ARM_USART_EVENT_RX_FRAMING_ERROR (1UL << 8) ///< Framing error detected on receive +#define ARM_USART_EVENT_RX_PARITY_ERROR (1UL << 9) ///< Parity error detected on receive +#define ARM_USART_EVENT_CTS (1UL << 10) ///< CTS state changed (optional) +#define ARM_USART_EVENT_DSR (1UL << 11) ///< DSR state changed (optional) +#define ARM_USART_EVENT_DCD (1UL << 12) ///< DCD state changed (optional) +#define ARM_USART_EVENT_RI (1UL << 13) ///< RI state changed (optional) + + +// Function documentation +/** + \fn ARM_DRIVER_VERSION ARM_USART_GetVersion (void) + \brief Get driver version. + \return \ref ARM_DRIVER_VERSION + + \fn ARM_USART_CAPABILITIES ARM_USART_GetCapabilities (void) + \brief Get driver capabilities + \return \ref ARM_USART_CAPABILITIES + + \fn int32_t ARM_USART_Initialize (ARM_USART_SignalEvent_t cb_event) + \brief Initialize USART Interface. + \param[in] cb_event Pointer to \ref ARM_USART_SignalEvent + \return \ref execution_status + + \fn int32_t ARM_USART_Uninitialize (void) + \brief De-initialize USART Interface. + \return \ref execution_status + + \fn int32_t ARM_USART_PowerControl (ARM_POWER_STATE state) + \brief Control USART Interface Power. + \param[in] state Power state + \return \ref execution_status + + \fn int32_t ARM_USART_Send (const void *data, uint32_t num) + \brief Start sending data to USART transmitter. + \param[in] data Pointer to buffer with data to send to USART transmitter + \param[in] num Number of data items to send + \return \ref execution_status + + \fn int32_t ARM_USART_Receive (void *data, uint32_t num) + \brief Start receiving data from USART receiver. + \param[out] data Pointer to buffer for data to receive from USART receiver + \param[in] num Number of data items to receive + \return \ref execution_status + + \fn int32_t ARM_USART_Transfer (const void *data_out, + void *data_in, + uint32_t num) + \brief Start sending/receiving data to/from USART transmitter/receiver. + \param[in] data_out Pointer to buffer with data to send to USART transmitter + \param[out] data_in Pointer to buffer for data to receive from USART receiver + \param[in] num Number of data items to transfer + \return \ref execution_status + + \fn uint32_t ARM_USART_GetTxCount (void) + \brief Get transmitted data count. + \return number of data items transmitted + + \fn uint32_t ARM_USART_GetRxCount (void) + \brief Get received data count. + \return number of data items received + + \fn int32_t ARM_USART_Control (uint32_t control, uint32_t arg) + \brief Control USART Interface. + \param[in] control Operation + \param[in] arg Argument of operation (optional) + \return common \ref execution_status and driver specific \ref usart_execution_status + + \fn ARM_USART_STATUS ARM_USART_GetStatus (void) + \brief Get USART status. + \return USART status \ref ARM_USART_STATUS + + \fn int32_t ARM_USART_SetModemControl (ARM_USART_MODEM_CONTROL control) + \brief Set USART Modem Control line state. + \param[in] control \ref ARM_USART_MODEM_CONTROL + \return \ref execution_status + + \fn ARM_USART_MODEM_STATUS ARM_USART_GetModemStatus (void) + \brief Get USART Modem Status lines state. + \return modem status \ref ARM_USART_MODEM_STATUS + + \fn void ARM_USART_SignalEvent (uint32_t event) + \brief Signal USART Events. + \param[in] event \ref USART_events notification mask + \return none +*/ + +typedef void (*ARM_USART_SignalEvent_t) (uint32_t event); ///< Pointer to \ref ARM_USART_SignalEvent : Signal USART Event. + + +/** +\brief USART Device Driver Capabilities. +*/ +typedef struct _ARM_USART_CAPABILITIES { + uint32_t asynchronous : 1; ///< supports UART (Asynchronous) mode + uint32_t synchronous_master : 1; ///< supports Synchronous Master mode + uint32_t synchronous_slave : 1; ///< supports Synchronous Slave mode + uint32_t single_wire : 1; ///< supports UART Single-wire mode + uint32_t irda : 1; ///< supports UART IrDA mode + uint32_t smart_card : 1; ///< supports UART Smart Card mode + uint32_t smart_card_clock : 1; ///< Smart Card Clock generator available + uint32_t flow_control_rts : 1; ///< RTS Flow Control available + uint32_t flow_control_cts : 1; ///< CTS Flow Control available + uint32_t event_tx_complete : 1; ///< Transmit completed event: \ref ARM_USART_EVENT_TX_COMPLETE + uint32_t event_rx_timeout : 1; ///< Signal receive character timeout event: \ref ARM_USART_EVENT_RX_TIMEOUT + uint32_t rts : 1; ///< RTS Line: 0=not available, 1=available + uint32_t cts : 1; ///< CTS Line: 0=not available, 1=available + uint32_t dtr : 1; ///< DTR Line: 0=not available, 1=available + uint32_t dsr : 1; ///< DSR Line: 0=not available, 1=available + uint32_t dcd : 1; ///< DCD Line: 0=not available, 1=available + uint32_t ri : 1; ///< RI Line: 0=not available, 1=available + uint32_t event_cts : 1; ///< Signal CTS change event: \ref ARM_USART_EVENT_CTS + uint32_t event_dsr : 1; ///< Signal DSR change event: \ref ARM_USART_EVENT_DSR + uint32_t event_dcd : 1; ///< Signal DCD change event: \ref ARM_USART_EVENT_DCD + uint32_t event_ri : 1; ///< Signal RI change event: \ref ARM_USART_EVENT_RI + uint32_t reserved : 11; ///< Reserved (must be zero) +} ARM_USART_CAPABILITIES; + + +/** +\brief Access structure of the USART Driver. +*/ +typedef struct _ARM_DRIVER_USART { + ARM_DRIVER_VERSION (*GetVersion) (void); ///< Pointer to \ref ARM_USART_GetVersion : Get driver version. + ARM_USART_CAPABILITIES (*GetCapabilities) (void); ///< Pointer to \ref ARM_USART_GetCapabilities : Get driver capabilities. + int32_t (*Initialize) (ARM_USART_SignalEvent_t cb_event); ///< Pointer to \ref ARM_USART_Initialize : Initialize USART Interface. + int32_t (*Uninitialize) (void); ///< Pointer to \ref ARM_USART_Uninitialize : De-initialize USART Interface. + int32_t (*PowerControl) (ARM_POWER_STATE state); ///< Pointer to \ref ARM_USART_PowerControl : Control USART Interface Power. + int32_t (*Send) (const void *data, uint32_t num); ///< Pointer to \ref ARM_USART_Send : Start sending data to USART transmitter. + int32_t (*Receive) ( void *data, uint32_t num); ///< Pointer to \ref ARM_USART_Receive : Start receiving data from USART receiver. + int32_t (*Transfer) (const void *data_out, + void *data_in, + uint32_t num); ///< Pointer to \ref ARM_USART_Transfer : Start sending/receiving data to/from USART. + uint32_t (*GetTxCount) (void); ///< Pointer to \ref ARM_USART_GetTxCount : Get transmitted data count. + uint32_t (*GetRxCount) (void); ///< Pointer to \ref ARM_USART_GetRxCount : Get received data count. + int32_t (*Control) (uint32_t control, uint32_t arg); ///< Pointer to \ref ARM_USART_Control : Control USART Interface. + ARM_USART_STATUS (*GetStatus) (void); ///< Pointer to \ref ARM_USART_GetStatus : Get USART status. + int32_t (*SetModemControl) (ARM_USART_MODEM_CONTROL control); ///< Pointer to \ref ARM_USART_SetModemControl : Set USART Modem Control line state. + ARM_USART_MODEM_STATUS (*GetModemStatus) (void); ///< Pointer to \ref ARM_USART_GetModemStatus : Get USART Modem Status lines state. +} const ARM_DRIVER_USART; + +#ifdef __cplusplus +} +#endif + +#endif /* DRIVER_USART_H_ */ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_USB.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_USB.h new file mode 100644 index 000000000000..fbb3f4d73482 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_USB.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2013-2017 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Date: 2. Feb 2017 + * $Revision: V2.0 + * + * Project: USB Driver common definitions + */ + +/* History: + * Version 2.0 + * Version 1.10 + * Namespace prefix ARM_ added + * Version 1.01 + * Added PID Types + * Version 1.00 + * Initial release + */ + +#ifndef DRIVER_USB_H_ +#define DRIVER_USB_H_ + +#include "Driver_Common.h" + +/* USB Role */ +#define ARM_USB_ROLE_NONE (0) +#define ARM_USB_ROLE_HOST (1) +#define ARM_USB_ROLE_DEVICE (2) + +/* USB Pins */ +#define ARM_USB_PIN_DP (1 << 0) ///< USB D+ pin +#define ARM_USB_PIN_DM (1 << 1) ///< USB D- pin +#define ARM_USB_PIN_VBUS (1 << 2) ///< USB VBUS pin +#define ARM_USB_PIN_OC (1 << 3) ///< USB OverCurrent pin +#define ARM_USB_PIN_ID (1 << 4) ///< USB ID pin + +/* USB Speed */ +#define ARM_USB_SPEED_LOW (0) ///< Low-speed USB +#define ARM_USB_SPEED_FULL (1) ///< Full-speed USB +#define ARM_USB_SPEED_HIGH (2) ///< High-speed USB + +/* USB PID Types */ +#define ARM_USB_PID_OUT (1) +#define ARM_USB_PID_IN (9) +#define ARM_USB_PID_SOF (5) +#define ARM_USB_PID_SETUP (13) +#define ARM_USB_PID_DATA0 (3) +#define ARM_USB_PID_DATA1 (11) +#define ARM_USB_PID_DATA2 (7) +#define ARM_USB_PID_MDATA (15) +#define ARM_USB_PID_ACK (2) +#define ARM_USB_PID_NAK (10) +#define ARM_USB_PID_STALL (14) +#define ARM_USB_PID_NYET (6) +#define ARM_USB_PID_PRE (12) +#define ARM_USB_PID_ERR (12) +#define ARM_USB_PID_SPLIT (8) +#define ARM_USB_PID_PING (4) +#define ARM_USB_PID_RESERVED (0) + +/* USB Endpoint Address (bEndpointAddress) */ +#define ARM_USB_ENDPOINT_NUMBER_MASK (0x0F) +#define ARM_USB_ENDPOINT_DIRECTION_MASK (0x80) + +/* USB Endpoint Type */ +#define ARM_USB_ENDPOINT_CONTROL (0) ///< Control Endpoint +#define ARM_USB_ENDPOINT_ISOCHRONOUS (1) ///< Isochronous Endpoint +#define ARM_USB_ENDPOINT_BULK (2) ///< Bulk Endpoint +#define ARM_USB_ENDPOINT_INTERRUPT (3) ///< Interrupt Endpoint + +/* USB Endpoint Maximum Packet Size (wMaxPacketSize) */ +#define ARM_USB_ENDPOINT_MAX_PACKET_SIZE_MASK (0x07FF) +#define ARM_USB_ENDPOINT_MICROFRAME_TRANSACTIONS_MASK (0x1800) +#define ARM_USB_ENDPOINT_MICROFRAME_TRANSACTIONS_1 (0x0000) +#define ARM_USB_ENDPOINT_MICROFRAME_TRANSACTIONS_2 (0x0800) +#define ARM_USB_ENDPOINT_MICROFRAME_TRANSACTIONS_3 (0x1000) + +#endif /* DRIVER_USB_H_ */ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_USBD.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_USBD.h new file mode 100644 index 000000000000..f6b230aad3e0 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_USBD.h @@ -0,0 +1,273 @@ +/* + * Copyright (c) 2013-2017 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Date: 2. Feb 2017 + * $Revision: V2.2 + * + * Project: USB Device Driver definitions + */ + +/* History: + * Version 2.2 + * ARM_USBD_STATE made volatile + * Version 2.1 + * Added ARM_USBD_ReadSetupPacket function + * Version 2.0 + * Removed ARM_USBD_DeviceConfigure function + * Removed ARM_USBD_SET_ADDRESS_STAGE parameter from ARM_USBD_DeviceSetAddress function + * Removed ARM_USBD_EndpointReadStart function + * Replaced ARM_USBD_EndpointRead and ARM_USBD_EndpointWrite functions with ARM_USBD_EndpointTransfer + * Added ARM_USBD_EndpointTransferGetResult function + * Renamed ARM_USBD_EndpointAbort function to ARM_USBD_EndpointTransferAbort + * Changed prefix ARM_DRV -> ARM_DRIVER + * Changed return values of some functions to int32_t + * Version 1.10 + * Namespace prefix ARM_ added + * Version 1.00 + * Initial release + */ + +#ifndef DRIVER_USBD_H_ +#define DRIVER_USBD_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Driver_USB.h" + +#define ARM_USBD_API_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(2,2) /* API version */ + + +/** +\brief USB Device State +*/ +typedef volatile struct _ARM_USBD_STATE { + uint32_t vbus : 1; ///< USB Device VBUS flag + uint32_t speed : 2; ///< USB Device speed setting (ARM_USB_SPEED_xxx) + uint32_t active : 1; ///< USB Device active flag + uint32_t reserved : 28; +} ARM_USBD_STATE; + + +/****** USB Device Event *****/ +#define ARM_USBD_EVENT_VBUS_ON (1UL << 0) ///< USB Device VBUS On +#define ARM_USBD_EVENT_VBUS_OFF (1UL << 1) ///< USB Device VBUS Off +#define ARM_USBD_EVENT_RESET (1UL << 2) ///< USB Reset occurred +#define ARM_USBD_EVENT_HIGH_SPEED (1UL << 3) ///< USB switch to High Speed occurred +#define ARM_USBD_EVENT_SUSPEND (1UL << 4) ///< USB Suspend occurred +#define ARM_USBD_EVENT_RESUME (1UL << 5) ///< USB Resume occurred + +/****** USB Endpoint Event *****/ +#define ARM_USBD_EVENT_SETUP (1UL << 0) ///< SETUP Packet +#define ARM_USBD_EVENT_OUT (1UL << 1) ///< OUT Packet(s) +#define ARM_USBD_EVENT_IN (1UL << 2) ///< IN Packet(s) + + +#ifndef __DOXYGEN_MW__ // exclude from middleware documentation + +// Function documentation +/** + \fn ARM_DRIVER_VERSION ARM_USBD_GetVersion (void) + \brief Get driver version. + \return \ref ARM_DRIVER_VERSION +*/ +/** + \fn ARM_USBD_CAPABILITIES ARM_USBD_GetCapabilities (void) + \brief Get driver capabilities. + \return \ref ARM_USBD_CAPABILITIES +*/ +/** + \fn int32_t ARM_USBD_Initialize (ARM_USBD_SignalDeviceEvent_t cb_device_event, + ARM_USBD_SignalEndpointEvent_t cb_endpoint_event) + \brief Initialize USB Device Interface. + \param[in] cb_device_event Pointer to \ref ARM_USBD_SignalDeviceEvent + \param[in] cb_endpoint_event Pointer to \ref ARM_USBD_SignalEndpointEvent + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBD_Uninitialize (void) + \brief De-initialize USB Device Interface. + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBD_PowerControl (ARM_POWER_STATE state) + \brief Control USB Device Interface Power. + \param[in] state Power state + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBD_DeviceConnect (void) + \brief Connect USB Device. + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBD_DeviceDisconnect (void) + \brief Disconnect USB Device. + \return \ref execution_status +*/ +/** + \fn ARM_USBD_STATE ARM_USBD_DeviceGetState (void) + \brief Get current USB Device State. + \return Device State \ref ARM_USBD_STATE +*/ +/** + \fn int32_t ARM_USBD_DeviceRemoteWakeup (void) + \brief Trigger USB Remote Wakeup. + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBD_DeviceSetAddress (uint8_t dev_addr) + \brief Set USB Device Address. + \param[in] dev_addr Device Address + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBD_ReadSetupPacket (uint8_t *setup) + \brief Read setup packet received over Control Endpoint. + \param[out] setup Pointer to buffer for setup packet + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBD_EndpointConfigure (uint8_t ep_addr, + uint8_t ep_type, + uint16_t ep_max_packet_size) + \brief Configure USB Endpoint. + \param[in] ep_addr Endpoint Address + - ep_addr.0..3: Address + - ep_addr.7: Direction + \param[in] ep_type Endpoint Type (ARM_USB_ENDPOINT_xxx) + \param[in] ep_max_packet_size Endpoint Maximum Packet Size + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBD_EndpointUnconfigure (uint8_t ep_addr) + \brief Unconfigure USB Endpoint. + \param[in] ep_addr Endpoint Address + - ep_addr.0..3: Address + - ep_addr.7: Direction + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBD_EndpointStall (uint8_t ep_addr, bool stall) + \brief Set/Clear Stall for USB Endpoint. + \param[in] ep_addr Endpoint Address + - ep_addr.0..3: Address + - ep_addr.7: Direction + \param[in] stall Operation + - \b false Clear + - \b true Set + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBD_EndpointTransfer (uint8_t ep_addr, uint8_t *data, uint32_t num) + \brief Read data from or Write data to USB Endpoint. + \param[in] ep_addr Endpoint Address + - ep_addr.0..3: Address + - ep_addr.7: Direction + \param[out] data Pointer to buffer for data to read or with data to write + \param[in] num Number of data bytes to transfer + \return \ref execution_status +*/ +/** + \fn uint32_t ARM_USBD_EndpointTransferGetResult (uint8_t ep_addr) + \brief Get result of USB Endpoint transfer. + \param[in] ep_addr Endpoint Address + - ep_addr.0..3: Address + - ep_addr.7: Direction + \return number of successfully transferred data bytes +*/ +/** + \fn int32_t ARM_USBD_EndpointTransferAbort (uint8_t ep_addr) + \brief Abort current USB Endpoint transfer. + \param[in] ep_addr Endpoint Address + - ep_addr.0..3: Address + - ep_addr.7: Direction + \return \ref execution_status +*/ +/** + \fn uint16_t ARM_USBD_GetFrameNumber (void) + \brief Get current USB Frame Number. + \return Frame Number +*/ + +/** + \fn void ARM_USBD_SignalDeviceEvent (uint32_t event) + \brief Signal USB Device Event. + \param[in] event \ref USBD_dev_events + \return none +*/ +/** + \fn void ARM_USBD_SignalEndpointEvent (uint8_t ep_addr, uint32_t event) + \brief Signal USB Endpoint Event. + \param[in] ep_addr Endpoint Address + - ep_addr.0..3: Address + - ep_addr.7: Direction + \param[in] event \ref USBD_ep_events + \return none +*/ + +typedef void (*ARM_USBD_SignalDeviceEvent_t) (uint32_t event); ///< Pointer to \ref ARM_USBD_SignalDeviceEvent : Signal USB Device Event. +typedef void (*ARM_USBD_SignalEndpointEvent_t) (uint8_t ep_addr, uint32_t event); ///< Pointer to \ref ARM_USBD_SignalEndpointEvent : Signal USB Endpoint Event. + + +/** +\brief USB Device Driver Capabilities. +*/ +typedef struct _ARM_USBD_CAPABILITIES { + uint32_t vbus_detection : 1; ///< VBUS detection + uint32_t event_vbus_on : 1; ///< Signal VBUS On event + uint32_t event_vbus_off : 1; ///< Signal VBUS Off event + uint32_t reserved : 29; ///< Reserved (must be zero) +} ARM_USBD_CAPABILITIES; + + +/** +\brief Access structure of the USB Device Driver. +*/ +typedef struct _ARM_DRIVER_USBD { + ARM_DRIVER_VERSION (*GetVersion) (void); ///< Pointer to \ref ARM_USBD_GetVersion : Get driver version. + ARM_USBD_CAPABILITIES (*GetCapabilities) (void); ///< Pointer to \ref ARM_USBD_GetCapabilities : Get driver capabilities. + int32_t (*Initialize) (ARM_USBD_SignalDeviceEvent_t cb_device_event, + ARM_USBD_SignalEndpointEvent_t cb_endpoint_event); ///< Pointer to \ref ARM_USBD_Initialize : Initialize USB Device Interface. + int32_t (*Uninitialize) (void); ///< Pointer to \ref ARM_USBD_Uninitialize : De-initialize USB Device Interface. + int32_t (*PowerControl) (ARM_POWER_STATE state); ///< Pointer to \ref ARM_USBD_PowerControl : Control USB Device Interface Power. + int32_t (*DeviceConnect) (void); ///< Pointer to \ref ARM_USBD_DeviceConnect : Connect USB Device. + int32_t (*DeviceDisconnect) (void); ///< Pointer to \ref ARM_USBD_DeviceDisconnect : Disconnect USB Device. + ARM_USBD_STATE (*DeviceGetState) (void); ///< Pointer to \ref ARM_USBD_DeviceGetState : Get current USB Device State. + int32_t (*DeviceRemoteWakeup) (void); ///< Pointer to \ref ARM_USBD_DeviceRemoteWakeup : Trigger USB Remote Wakeup. + int32_t (*DeviceSetAddress) (uint8_t dev_addr); ///< Pointer to \ref ARM_USBD_DeviceSetAddress : Set USB Device Address. + int32_t (*ReadSetupPacket) (uint8_t *setup); ///< Pointer to \ref ARM_USBD_ReadSetupPacket : Read setup packet received over Control Endpoint. + int32_t (*EndpointConfigure) (uint8_t ep_addr, + uint8_t ep_type, + uint16_t ep_max_packet_size); ///< Pointer to \ref ARM_USBD_EndpointConfigure : Configure USB Endpoint. + int32_t (*EndpointUnconfigure) (uint8_t ep_addr); ///< Pointer to \ref ARM_USBD_EndpointUnconfigure : Unconfigure USB Endpoint. + int32_t (*EndpointStall) (uint8_t ep_addr, bool stall); ///< Pointer to \ref ARM_USBD_EndpointStall : Set/Clear Stall for USB Endpoint. + int32_t (*EndpointTransfer) (uint8_t ep_addr, uint8_t *data, uint32_t num); ///< Pointer to \ref ARM_USBD_EndpointTransfer : Read data from or Write data to USB Endpoint. + uint32_t (*EndpointTransferGetResult) (uint8_t ep_addr); ///< Pointer to \ref ARM_USBD_EndpointTransferGetResult : Get result of USB Endpoint transfer. + int32_t (*EndpointTransferAbort) (uint8_t ep_addr); ///< Pointer to \ref ARM_USBD_EndpointTransferAbort : Abort current USB Endpoint transfer. + uint16_t (*GetFrameNumber) (void); ///< Pointer to \ref ARM_USBD_GetFrameNumber : Get current USB Frame Number. +} const ARM_DRIVER_USBD; + +#endif /* __DOXYGEN_MW__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* DRIVER_USBD_H_ */ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_USBH.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_USBH.h new file mode 100644 index 000000000000..a30eacafe125 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_USBH.h @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2013-2017 ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Date: 2. Feb 2017 + * $Revision: V2.2 + * + * Project: USB Host Driver definitions +*/ + +/* History: + * Version 2.2 + * ARM_USBH_PORT_STATE made volatile + * Version 2.1 + * Renamed structure ARM_USBH_EP_HANDLE to ARM_USBH_PIPE_HANDLE + * Renamed functions ARM_USBH_Endpoint... to ARM_USBH_Pipe... + * Renamed function ARM_USBH_SignalEndpointEvent to ARM_USBH_SignalPipeEvent + * Version 2.0 + * Replaced function ARM_USBH_PortPowerOnOff with ARM_USBH_PortVbusOnOff + * Changed function ARM_USBH_EndpointCreate parameters + * Replaced function ARM_USBH_EndpointConfigure with ARM_USBH_EndpointModify + * Replaced function ARM_USBH_EndpointClearHalt with ARM_USBH_EndpointReset + * Replaced function ARM_USBH_URB_Submit with ARM_USBH_EndpointTransfer + * Replaced function ARM_USBH_URB_Abort with ARM_USBH_EndpointTransferAbort + * Added function ARM_USBH_EndpointTransferGetResult + * Added function ARM_USBH_GetFrameNumber + * Changed prefix ARM_DRV -> ARM_DRIVER + * Version 1.20 + * Added API for OHCI/EHCI Host Controller Interface (HCI) + * Version 1.10 + * Namespace prefix ARM_ added + * Version 1.00 + * Initial release + */ + +#ifndef DRIVER_USBH_H_ +#define DRIVER_USBH_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Driver_USB.h" + +#define ARM_USBH_API_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(2,2) /* API version */ + + +/** +\brief USB Host Port State +*/ +typedef volatile struct _ARM_USBH_PORT_STATE { + uint32_t connected : 1; ///< USB Host Port connected flag + uint32_t overcurrent : 1; ///< USB Host Port overcurrent flag + uint32_t speed : 2; ///< USB Host Port speed setting (ARM_USB_SPEED_xxx) + uint32_t reserved : 28; +} ARM_USBH_PORT_STATE; + +/** +\brief USB Host Pipe Handle +*/ +typedef uint32_t ARM_USBH_PIPE_HANDLE; +#define ARM_USBH_EP_HANDLE ARM_USBH_PIPE_HANDLE /* Legacy name */ + + +/****** USB Host Packet Information *****/ +#define ARM_USBH_PACKET_TOKEN_Pos 0 +#define ARM_USBH_PACKET_TOKEN_Msk (0x0FUL << ARM_USBH_PACKET_TOKEN_Pos) +#define ARM_USBH_PACKET_SETUP (0x01UL << ARM_USBH_PACKET_TOKEN_Pos) ///< SETUP Packet +#define ARM_USBH_PACKET_OUT (0x02UL << ARM_USBH_PACKET_TOKEN_Pos) ///< OUT Packet +#define ARM_USBH_PACKET_IN (0x03UL << ARM_USBH_PACKET_TOKEN_Pos) ///< IN Packet +#define ARM_USBH_PACKET_PING (0x04UL << ARM_USBH_PACKET_TOKEN_Pos) ///< PING Packet + +#define ARM_USBH_PACKET_DATA_Pos 4 +#define ARM_USBH_PACKET_DATA_Msk (0x0FUL << ARM_USBH_PACKET_DATA_Pos) +#define ARM_USBH_PACKET_DATA0 (0x01UL << ARM_USBH_PACKET_DATA_Pos) ///< DATA0 PID +#define ARM_USBH_PACKET_DATA1 (0x02UL << ARM_USBH_PACKET_DATA_Pos) ///< DATA1 PID + +#define ARM_USBH_PACKET_SPLIT_Pos 8 +#define ARM_USBH_PACKET_SPLIT_Msk (0x0FUL << ARM_USBH_PACKET_SPLIT_Pos) +#define ARM_USBH_PACKET_SSPLIT (0x08UL << ARM_USBH_PACKET_SPLIT_Pos) ///< SSPLIT Packet +#define ARM_USBH_PACKET_SSPLIT_S (0x09UL << ARM_USBH_PACKET_SPLIT_Pos) ///< SSPLIT Packet: Data Start +#define ARM_USBH_PACKET_SSPLIT_E (0x0AUL << ARM_USBH_PACKET_SPLIT_Pos) ///< SSPLIT Packet: Data End +#define ARM_USBH_PACKET_SSPLIT_S_E (0x0BUL << ARM_USBH_PACKET_SPLIT_Pos) ///< SSPLIT Packet: Data All +#define ARM_USBH_PACKET_CSPLIT (0x0CUL << ARM_USBH_PACKET_SPLIT_Pos) ///< CSPLIT Packet + +#define ARM_USBH_PACKET_PRE (1UL << 12) ///< PRE Token + + +/****** USB Host Port Event *****/ +#define ARM_USBH_EVENT_CONNECT (1UL << 0) ///< USB Device Connected to Port +#define ARM_USBH_EVENT_DISCONNECT (1UL << 1) ///< USB Device Disconnected from Port +#define ARM_USBH_EVENT_OVERCURRENT (1UL << 2) ///< USB Device caused Overcurrent +#define ARM_USBH_EVENT_RESET (1UL << 3) ///< USB Reset completed +#define ARM_USBH_EVENT_SUSPEND (1UL << 4) ///< USB Suspend occurred +#define ARM_USBH_EVENT_RESUME (1UL << 5) ///< USB Resume occurred +#define ARM_USBH_EVENT_REMOTE_WAKEUP (1UL << 6) ///< USB Device activated Remote Wakeup + +/****** USB Host Pipe Event *****/ +#define ARM_USBH_EVENT_TRANSFER_COMPLETE (1UL << 0) ///< Transfer completed +#define ARM_USBH_EVENT_HANDSHAKE_NAK (1UL << 1) ///< NAK Handshake received +#define ARM_USBH_EVENT_HANDSHAKE_NYET (1UL << 2) ///< NYET Handshake received +#define ARM_USBH_EVENT_HANDSHAKE_MDATA (1UL << 3) ///< MDATA Handshake received +#define ARM_USBH_EVENT_HANDSHAKE_STALL (1UL << 4) ///< STALL Handshake received +#define ARM_USBH_EVENT_HANDSHAKE_ERR (1UL << 5) ///< ERR Handshake received +#define ARM_USBH_EVENT_BUS_ERROR (1UL << 6) ///< Bus Error detected + + +#ifndef __DOXYGEN_MW__ // exclude from middleware documentation + +// Function documentation +/** + \fn ARM_DRIVER_VERSION ARM_USBH_GetVersion (void) + \brief Get driver version. + \return \ref ARM_DRIVER_VERSION +*/ +/** + \fn ARM_USBH_CAPABILITIES ARM_USBH_GetCapabilities (void) + \brief Get driver capabilities. + \return \ref ARM_USBH_CAPABILITIES +*/ +/** + \fn int32_t ARM_USBH_Initialize (ARM_USBH_SignalPortEvent_t cb_port_event, + ARM_USBH_SignalPipeEvent_t cb_pipe_event) + \brief Initialize USB Host Interface. + \param[in] cb_port_event Pointer to \ref ARM_USBH_SignalPortEvent + \param[in] cb_pipe_event Pointer to \ref ARM_USBH_SignalPipeEvent + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBH_Uninitialize (void) + \brief De-initialize USB Host Interface. + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBH_PowerControl (ARM_POWER_STATE state) + \brief Control USB Host Interface Power. + \param[in] state Power state + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBH_PortVbusOnOff (uint8_t port, bool vbus) + \brief Root HUB Port VBUS on/off. + \param[in] port Root HUB Port Number + \param[in] vbus + - \b false VBUS off + - \b true VBUS on + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBH_PortReset (uint8_t port) + \brief Do Root HUB Port Reset. + \param[in] port Root HUB Port Number + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBH_PortSuspend (uint8_t port) + \brief Suspend Root HUB Port (stop generating SOFs). + \param[in] port Root HUB Port Number + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBH_PortResume (uint8_t port) + \brief Resume Root HUB Port (start generating SOFs). + \param[in] port Root HUB Port Number + \return \ref execution_status +*/ +/** + \fn ARM_USBH_PORT_STATE ARM_USBH_PortGetState (uint8_t port) + \brief Get current Root HUB Port State. + \param[in] port Root HUB Port Number + \return Port State \ref ARM_USBH_PORT_STATE +*/ +/** + \fn ARM_USBH_PIPE_HANDLE ARM_USBH_PipeCreate (uint8_t dev_addr, + uint8_t dev_speed, + uint8_t hub_addr, + uint8_t hub_port, + uint8_t ep_addr, + uint8_t ep_type, + uint16_t ep_max_packet_size, + uint8_t ep_interval) + \brief Create Pipe in System. + \param[in] dev_addr Device Address + \param[in] dev_speed Device Speed + \param[in] hub_addr Hub Address + \param[in] hub_port Hub Port + \param[in] ep_addr Endpoint Address + - ep_addr.0..3: Address + - ep_addr.7: Direction + \param[in] ep_type Endpoint Type (ARM_USB_ENDPOINT_xxx) + \param[in] ep_max_packet_size Endpoint Maximum Packet Size + \param[in] ep_interval Endpoint Polling Interval + \return Pipe Handle \ref ARM_USBH_PIPE_HANDLE +*/ +/** + \fn int32_t ARM_USBH_PipeModify (ARM_USBH_PIPE_HANDLE pipe_hndl, + uint8_t dev_addr, + uint8_t dev_speed, + uint8_t hub_addr, + uint8_t hub_port, + uint16_t ep_max_packet_size) + \brief Modify Pipe in System. + \param[in] pipe_hndl Pipe Handle + \param[in] dev_addr Device Address + \param[in] dev_speed Device Speed + \param[in] hub_addr Hub Address + \param[in] hub_port Hub Port + \param[in] ep_max_packet_size Endpoint Maximum Packet Size + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBH_PipeDelete (ARM_USBH_PIPE_HANDLE pipe_hndl) + \brief Delete Pipe from System. + \param[in] pipe_hndl Pipe Handle + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBH_PipeReset (ARM_USBH_PIPE_HANDLE pipe_hndl) + \brief Reset Pipe. + \param[in] pipe_hndl Pipe Handle + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBH_PipeTransfer (ARM_USBH_PIPE_HANDLE pipe_hndl, + uint32_t packet, + uint8_t *data, + uint32_t num) + \brief Transfer packets through USB Pipe. + \param[in] pipe_hndl Pipe Handle + \param[in] packet Packet information + \param[in] data Pointer to buffer with data to send or for data to receive + \param[in] num Number of data bytes to transfer + \return \ref execution_status +*/ +/** + \fn uint32_t ARM_USBH_PipeTransferGetResult (ARM_USBH_PIPE_HANDLE pipe_hndl) + \brief Get result of USB Pipe transfer. + \param[in] pipe_hndl Pipe Handle + \return number of successfully transferred data bytes +*/ +/** + \fn int32_t ARM_USBH_PipeTransferAbort (ARM_USBH_PIPE_HANDLE pipe_hndl) + \brief Abort current USB Pipe transfer. + \param[in] pipe_hndl Pipe Handle + \return \ref execution_status +*/ +/** + \fn uint16_t ARM_USBH_GetFrameNumber (void) + \brief Get current USB Frame Number. + \return Frame Number +*/ + +/** + \fn void ARM_USBH_SignalPortEvent (uint8_t port, uint32_t event) + \brief Signal Root HUB Port Event. + \param[in] port Root HUB Port Number + \param[in] event \ref USBH_port_events + \return none +*/ +/** + \fn void ARM_USBH_SignalPipeEvent (ARM_USBH_PIPE_HANDLE pipe_hndl, uint32_t event) + \brief Signal Pipe Event. + \param[in] pipe_hndl Pipe Handle + \param[in] event \ref USBH_pipe_events + \return none +*/ + +typedef void (*ARM_USBH_SignalPortEvent_t) (uint8_t port, uint32_t event); ///< Pointer to \ref ARM_USBH_SignalPortEvent : Signal Root HUB Port Event. +typedef void (*ARM_USBH_SignalPipeEvent_t) (ARM_USBH_PIPE_HANDLE pipe_hndl, uint32_t event); ///< Pointer to \ref ARM_USBH_SignalPipeEvent : Signal Pipe Event. +#define ARM_USBH_SignalEndpointEvent_t ARM_USBH_SignalPipeEvent_t /* Legacy name */ + + +/** +\brief USB Host Driver Capabilities. +*/ +typedef struct _ARM_USBH_CAPABILITIES { + uint32_t port_mask : 15; ///< Root HUB available Ports Mask + uint32_t auto_split : 1; ///< Automatic SPLIT packet handling + uint32_t event_connect : 1; ///< Signal Connect event + uint32_t event_disconnect : 1; ///< Signal Disconnect event + uint32_t event_overcurrent : 1; ///< Signal Overcurrent event + uint32_t reserved : 13; ///< Reserved (must be zero) +} ARM_USBH_CAPABILITIES; + + +/** +\brief Access structure of USB Host Driver. +*/ +typedef struct _ARM_DRIVER_USBH { + ARM_DRIVER_VERSION (*GetVersion) (void); ///< Pointer to \ref ARM_USBH_GetVersion : Get driver version. + ARM_USBH_CAPABILITIES (*GetCapabilities) (void); ///< Pointer to \ref ARM_USBH_GetCapabilities : Get driver capabilities. + int32_t (*Initialize) (ARM_USBH_SignalPortEvent_t cb_port_event, + ARM_USBH_SignalPipeEvent_t cb_pipe_event); ///< Pointer to \ref ARM_USBH_Initialize : Initialize USB Host Interface. + int32_t (*Uninitialize) (void); ///< Pointer to \ref ARM_USBH_Uninitialize : De-initialize USB Host Interface. + int32_t (*PowerControl) (ARM_POWER_STATE state); ///< Pointer to \ref ARM_USBH_PowerControl : Control USB Host Interface Power. + int32_t (*PortVbusOnOff) (uint8_t port, bool vbus); ///< Pointer to \ref ARM_USBH_PortVbusOnOff : Root HUB Port VBUS on/off. + int32_t (*PortReset) (uint8_t port); ///< Pointer to \ref ARM_USBH_PortReset : Do Root HUB Port Reset. + int32_t (*PortSuspend) (uint8_t port); ///< Pointer to \ref ARM_USBH_PortSuspend : Suspend Root HUB Port (stop generating SOFs). + int32_t (*PortResume) (uint8_t port); ///< Pointer to \ref ARM_USBH_PortResume : Resume Root HUB Port (start generating SOFs). + ARM_USBH_PORT_STATE (*PortGetState) (uint8_t port); ///< Pointer to \ref ARM_USBH_PortGetState : Get current Root HUB Port State. + ARM_USBH_PIPE_HANDLE (*PipeCreate) (uint8_t dev_addr, + uint8_t dev_speed, + uint8_t hub_addr, + uint8_t hub_port, + uint8_t ep_addr, + uint8_t ep_type, + uint16_t ep_max_packet_size, + uint8_t ep_interval); ///< Pointer to \ref ARM_USBH_PipeCreate : Create Pipe in System. + int32_t (*PipeModify) (ARM_USBH_PIPE_HANDLE pipe_hndl, + uint8_t dev_addr, + uint8_t dev_speed, + uint8_t hub_addr, + uint8_t hub_port, + uint16_t ep_max_packet_size); ///< Pointer to \ref ARM_USBH_PipeModify : Modify Pipe in System. + int32_t (*PipeDelete) (ARM_USBH_PIPE_HANDLE pipe_hndl); ///< Pointer to \ref ARM_USBH_PipeDelete : Delete Pipe from System. + int32_t (*PipeReset) (ARM_USBH_PIPE_HANDLE pipe_hndl); ///< Pointer to \ref ARM_USBH_PipeReset : Reset Pipe. + int32_t (*PipeTransfer) (ARM_USBH_PIPE_HANDLE pipe_hndl, + uint32_t packet, + uint8_t *data, + uint32_t num); ///< Pointer to \ref ARM_USBH_PipeTransfer : Transfer packets through USB Pipe. + uint32_t (*PipeTransferGetResult) (ARM_USBH_PIPE_HANDLE pipe_hndl); ///< Pointer to \ref ARM_USBH_PipeTransferGetResult : Get result of USB Pipe transfer. + int32_t (*PipeTransferAbort) (ARM_USBH_PIPE_HANDLE pipe_hndl); ///< Pointer to \ref ARM_USBH_PipeTransferAbort : Abort current USB Pipe transfer. + uint16_t (*GetFrameNumber) (void); ///< Pointer to \ref ARM_USBH_GetFrameNumber : Get current USB Frame Number. +} const ARM_DRIVER_USBH; + + +// HCI (OHCI/EHCI) + +// Function documentation +/** + \fn ARM_DRIVER_VERSION ARM_USBH_HCI_GetVersion (void) + \brief Get USB Host HCI (OHCI/EHCI) driver version. + \return \ref ARM_DRIVER_VERSION +*/ +/** + \fn ARM_USBH_HCI_CAPABILITIES ARM_USBH_HCI_GetCapabilities (void) + \brief Get driver capabilities. + \return \ref ARM_USBH_HCI_CAPABILITIES +*/ +/** + \fn int32_t ARM_USBH_HCI_Initialize (ARM_USBH_HCI_Interrupt_t *cb_interrupt) + \brief Initialize USB Host HCI (OHCI/EHCI) Interface. + \param[in] cb_interrupt Pointer to Interrupt Handler Routine + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBH_HCI_Uninitialize (void) + \brief De-initialize USB Host HCI (OHCI/EHCI) Interface. + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBH_HCI_PowerControl (ARM_POWER_STATE state) + \brief Control USB Host HCI (OHCI/EHCI) Interface Power. + \param[in] state Power state + \return \ref execution_status +*/ +/** + \fn int32_t ARM_USBH_HCI_PortVbusOnOff (uint8_t port, bool vbus) + \brief USB Host HCI (OHCI/EHCI) Root HUB Port VBUS on/off. + \param[in] port Root HUB Port Number + \param[in] vbus + - \b false VBUS off + - \b true VBUS on + \return \ref execution_status +*/ + +/** + \fn void ARM_USBH_HCI_Interrupt (void) + \brief USB Host HCI Interrupt Handler. + \return none +*/ + +typedef void (*ARM_USBH_HCI_Interrupt_t) (void); ///< Pointer to Interrupt Handler Routine. + + +/** +\brief USB Host HCI (OHCI/EHCI) Driver Capabilities. +*/ +typedef struct _ARM_USBH_HCI_CAPABILITIES { + uint32_t port_mask : 15; ///< Root HUB available Ports Mask + uint32_t reserved : 17; ///< Reserved (must be zero) +} ARM_USBH_HCI_CAPABILITIES; + + +/** + \brief Access structure of USB Host HCI (OHCI/EHCI) Driver. +*/ +typedef struct _ARM_DRIVER_USBH_HCI { + ARM_DRIVER_VERSION (*GetVersion) (void); ///< Pointer to \ref ARM_USBH_HCI_GetVersion : Get USB Host HCI (OHCI/EHCI) driver version. + ARM_USBH_HCI_CAPABILITIES (*GetCapabilities) (void); ///< Pointer to \ref ARM_USBH_HCI_GetCapabilities : Get driver capabilities. + int32_t (*Initialize) (ARM_USBH_HCI_Interrupt_t cb_interrupt); ///< Pointer to \ref ARM_USBH_HCI_Initialize : Initialize USB Host HCI (OHCI/EHCI) Interface. + int32_t (*Uninitialize) (void); ///< Pointer to \ref ARM_USBH_HCI_Uninitialize : De-initialize USB Host HCI (OHCI/EHCI) Interface. + int32_t (*PowerControl) (ARM_POWER_STATE state); ///< Pointer to \ref ARM_USBH_HCI_PowerControl : Control USB Host HCI (OHCI/EHCI) Interface Power. + int32_t (*PortVbusOnOff) (uint8_t port, bool vbus); ///< Pointer to \ref ARM_USBH_HCI_PortVbusOnOff : USB Host HCI (OHCI/EHCI) Root HUB Port VBUS on/off. +} const ARM_DRIVER_USBH_HCI; + +#endif /* __DOXYGEN_MW__ */ + +#ifdef __cplusplus +} +#endif + +#endif /* DRIVER_USBH_H_ */ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_WiFi.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_WiFi.h new file mode 100644 index 000000000000..69a50139d1c7 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS/Driver/Include/Driver_WiFi.h @@ -0,0 +1,659 @@ +/* + * Copyright (c) 2019 Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * $Date: 31. May 2019 + * $Revision: V1.0 + * + * Project: WiFi (Wireless Fidelity Interface) Driver definitions + */ + +/* History: + * Version 1.0 + * Initial release + */ + +#ifndef DRIVER_WIFI_H_ +#define DRIVER_WIFI_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "Driver_Common.h" + +#define ARM_WIFI_API_VERSION ARM_DRIVER_VERSION_MAJOR_MINOR(1,0) /* API version */ + +#define _ARM_Driver_WiFi_(n) Driver_WiFi##n +#define ARM_Driver_WiFi_(n) _ARM_Driver_WiFi_(n) + + +/****** WiFi SetOption/GetOption Function Option Codes *****/ +#define ARM_WIFI_BSSID 1U ///< Station/AP Set/Get BSSID of AP to connect or of AP; data = &bssid, len = 6, uint8_t[6] +#define ARM_WIFI_TX_POWER 2U ///< Station/AP Set/Get transmit power; data = &power, len = 4, uint32_t: 0 .. 20 [dBm] +#define ARM_WIFI_LP_TIMER 3U ///< Station Set/Get low-power deep-sleep time; data = &time, len = 4, uint32_t [seconds]: 0 = disable (default) +#define ARM_WIFI_DTIM 4U ///< Station/AP Set/Get DTIM interval; data = &dtim, len = 4, uint32_t [beacons] +#define ARM_WIFI_BEACON 5U ///< AP Set/Get beacon interval; data = &interval, len = 4, uint32_t [ms] +#define ARM_WIFI_MAC 6U ///< Station/AP Set/Get MAC; data = &mac, len = 6, uint8_t[6] +#define ARM_WIFI_IP 7U ///< Station/AP Set/Get IPv4 static/assigned address; data = &ip, len = 4, uint8_t[4] +#define ARM_WIFI_IP_SUBNET_MASK 8U ///< Station/AP Set/Get IPv4 subnet mask; data = &mask, len = 4, uint8_t[4] +#define ARM_WIFI_IP_GATEWAY 9U ///< Station/AP Set/Get IPv4 gateway address; data = &ip, len = 4, uint8_t[4] +#define ARM_WIFI_IP_DNS1 10U ///< Station/AP Set/Get IPv4 primary DNS address; data = &ip, len = 4, uint8_t[4] +#define ARM_WIFI_IP_DNS2 11U ///< Station/AP Set/Get IPv4 secondary DNS address; data = &ip, len = 4, uint8_t[4] +#define ARM_WIFI_IP_DHCP 12U ///< Station/AP Set/Get IPv4 DHCP client/server enable/disable; data = &dhcp, len = 4, uint32_t: 0 = disable, non-zero = enable (default) +#define ARM_WIFI_IP_DHCP_POOL_BEGIN 13U ///< AP Set/Get IPv4 DHCP pool begin address; data = &ip, len = 4, uint8_t[4] +#define ARM_WIFI_IP_DHCP_POOL_END 14U ///< AP Set/Get IPv4 DHCP pool end address; data = &ip, len = 4, uint8_t[4] +#define ARM_WIFI_IP_DHCP_LEASE_TIME 15U ///< AP Set/Get IPv4 DHCP lease time; data = &time, len = 4, uint32_t [seconds] +#define ARM_WIFI_IP6_GLOBAL 16U ///< Station/AP Set/Get IPv6 global address; data = &ip6, len = 16, uint8_t[16] +#define ARM_WIFI_IP6_LINK_LOCAL 17U ///< Station/AP Set/Get IPv6 link local address; data = &ip6, len = 16, uint8_t[16] +#define ARM_WIFI_IP6_SUBNET_PREFIX_LEN 18U ///< Station/AP Set/Get IPv6 subnet prefix length; data = &len, len = 4, uint32_t: 1 .. 127 +#define ARM_WIFI_IP6_GATEWAY 19U ///< Station/AP Set/Get IPv6 gateway address; data = &ip6, len = 16, uint8_t[16] +#define ARM_WIFI_IP6_DNS1 20U ///< Station/AP Set/Get IPv6 primary DNS address; data = &ip6, len = 16, uint8_t[16] +#define ARM_WIFI_IP6_DNS2 21U ///< Station/AP Set/Get IPv6 secondary DNS address; data = &ip6, len = 16, uint8_t[16] +#define ARM_WIFI_IP6_DHCP_MODE 22U ///< Station/AP Set/Get IPv6 DHCPv6 client mode; data = &mode, len = 4, uint32_t: ARM_WIFI_IP6_DHCP_xxx (default Off) + +/****** WiFi Security Type *****/ +#define ARM_WIFI_SECURITY_OPEN 0U ///< Open +#define ARM_WIFI_SECURITY_WEP 1U ///< Wired Equivalent Privacy (WEP) with Pre-Sheared Key (PSK) +#define ARM_WIFI_SECURITY_WPA 2U ///< WiFi Protected Access (WPA) with PSK +#define ARM_WIFI_SECURITY_WPA2 3U ///< WiFi Protected Access II (WPA2) with PSK +#define ARM_WIFI_SECURITY_UNKNOWN 255U ///< Unknown + +/****** WiFi Protected Setup (WPS) Method *****/ +#define ARM_WIFI_WPS_METHOD_NONE 0U ///< Not used +#define ARM_WIFI_WPS_METHOD_PBC 1U ///< Push Button Configuration +#define ARM_WIFI_WPS_METHOD_PIN 2U ///< PIN + +/****** WiFi IPv6 Dynamic Host Configuration Protocol (DHCP) Mode *****/ +#define ARM_WIFI_IP6_DHCP_OFF 0U ///< Static Host Configuration (default) +#define ARM_WIFI_IP6_DHCP_STATELESS 1U ///< Dynamic Host Configuration stateless DHCPv6 +#define ARM_WIFI_IP6_DHCP_STATEFULL 2U ///< Dynamic Host Configuration statefull DHCPv6 + +/****** WiFi Event *****/ +#define ARM_WIFI_EVENT_AP_CONNECT (1UL << 0) ///< Access Point: Station has connected; arg = &mac, mac (uint8_t[6]) +#define ARM_WIFI_EVENT_AP_DISCONNECT (1UL << 1) ///< Access Point: Station has disconnected; arg = &mac, mac (uint8_t[6]) +#define ARM_WIFI_EVENT_ETH_RX_FRAME (1UL << 4) ///< Ethernet Frame Received (in bypass mode only); arg = interface (0 = Station, 1 = Access Point) + + +/** +\brief WiFi Configuration +*/ +typedef struct { + const char *ssid; ///< Pointer to Service Set Identifier (SSID) null-terminated string + const char *pass; ///< Pointer to Password null-terminated string + uint8_t security; ///< Security type (ARM_WIFI_SECURITY_xxx) + uint8_t ch; ///< WiFi Channel (0 = auto, otherwise = exact channel) + uint8_t reserved; ///< Reserved + uint8_t wps_method; ///< WiFi Protected Setup (WPS) method (ARM_WIFI_WPS_METHOD_xxx) + const char *wps_pin; ///< Pointer to WiFi Protected Setup (WPS) PIN null-terminated string +} ARM_WIFI_CONFIG_t; + +/** +\brief WiFi Scan Information +*/ +typedef struct { + char ssid[32+1]; ///< Service Set Identifier (SSID) null-terminated string + uint8_t bssid[6]; ///< Basic Service Set Identifier (BSSID) + uint8_t security; ///< Security type (ARM_WIFI_SECURITY_xxx) + uint8_t ch; ///< WiFi Channel + uint8_t rssi; ///< Received Signal Strength Indicator +} ARM_WIFI_SCAN_INFO_t; + +/** +\brief WiFi Network Information +*/ +typedef struct { + char ssid[32+1]; ///< Service Set Identifier (SSID) null-terminated string + char pass[64+1]; ///< Password null-terminated string + uint8_t security; ///< Security type (ARM_WIFI_SECURITY_xxx) + uint8_t ch; ///< WiFi Channel + uint8_t rssi; ///< Received Signal Strength Indicator +} ARM_WIFI_NET_INFO_t; + +/****** Socket Address Family definitions *****/ +#define ARM_SOCKET_AF_INET 1 ///< IPv4 +#define ARM_SOCKET_AF_INET6 2 ///< IPv6 + +/****** Socket Type definitions *****/ +#define ARM_SOCKET_SOCK_STREAM 1 ///< Stream socket +#define ARM_SOCKET_SOCK_DGRAM 2 ///< Datagram socket + +/****** Socket Protocol definitions *****/ +#define ARM_SOCKET_IPPROTO_TCP 1 ///< TCP +#define ARM_SOCKET_IPPROTO_UDP 2 ///< UDP + +/****** Socket Option definitions *****/ +#define ARM_SOCKET_IO_FIONBIO 1 ///< Non-blocking I/O (Set only, default = 0); opt_val = &nbio, opt_len = sizeof(nbio), nbio (integer): 0=blocking, non-blocking otherwise +#define ARM_SOCKET_SO_RCVTIMEO 2 ///< Receive timeout in ms (default = 0); opt_val = &timeout, opt_len = sizeof(timeout) +#define ARM_SOCKET_SO_SNDTIMEO 3 ///< Send timeout in ms (default = 0); opt_val = &timeout, opt_len = sizeof(timeout) +#define ARM_SOCKET_SO_KEEPALIVE 4 ///< Keep-alive messages (default = 0); opt_val = &keepalive, opt_len = sizeof(keepalive), keepalive (integer): 0=disabled, enabled otherwise +#define ARM_SOCKET_SO_TYPE 5 ///< Socket Type (Get only); opt_val = &socket_type, opt_len = sizeof(socket_type), socket_type (integer): ARM_SOCKET_SOCK_xxx + +/****** Socket Function return codes *****/ +#define ARM_SOCKET_ERROR (-1) ///< Unspecified error +#define ARM_SOCKET_ESOCK (-2) ///< Invalid socket +#define ARM_SOCKET_EINVAL (-3) ///< Invalid argument +#define ARM_SOCKET_ENOTSUP (-4) ///< Operation not supported +#define ARM_SOCKET_ENOMEM (-5) ///< Not enough memory +#define ARM_SOCKET_EAGAIN (-6) ///< Operation would block or timed out +#define ARM_SOCKET_EINPROGRESS (-7) ///< Operation in progress +#define ARM_SOCKET_ETIMEDOUT (-8) ///< Operation timed out +#define ARM_SOCKET_EISCONN (-9) ///< Socket is connected +#define ARM_SOCKET_ENOTCONN (-10) ///< Socket is not connected +#define ARM_SOCKET_ECONNREFUSED (-11) ///< Connection rejected by the peer +#define ARM_SOCKET_ECONNRESET (-12) ///< Connection reset by the peer +#define ARM_SOCKET_ECONNABORTED (-13) ///< Connection aborted locally +#define ARM_SOCKET_EALREADY (-14) ///< Connection already in progress +#define ARM_SOCKET_EADDRINUSE (-15) ///< Address in use +#define ARM_SOCKET_EHOSTNOTFOUND (-16) ///< Host not found + + +// Function documentation +/** + \fn ARM_DRIVER_VERSION ARM_WIFI_GetVersion (void) + \brief Get driver version. + \return \ref ARM_DRIVER_VERSION +*/ +/** + \fn ARM_WIFI_CAPABILITIES ARM_WIFI_GetCapabilities (void) + \brief Get driver capabilities. + \return \ref ARM_WIFI_CAPABILITIES +*/ +/** + \fn int32_t ARM_WIFI_Initialize (ARM_WIFI_SignalEvent_t cb_event) + \brief Initialize WiFi Module. + \param[in] cb_event Pointer to \ref ARM_WIFI_SignalEvent_t + \return execution status + - \ref ARM_DRIVER_OK : Operation successful + - \ref ARM_DRIVER_ERROR : Operation failed +*/ +/** + \fn int32_t ARM_WIFI_Uninitialize (void) + \brief De-initialize WiFi Module. + \return execution status + - \ref ARM_DRIVER_OK : Operation successful + - \ref ARM_DRIVER_ERROR : Operation failed +*/ +/** + \fn int32_t ARM_WIFI_PowerControl (ARM_POWER_STATE state) + \brief Control WiFi Module Power. + \param[in] state Power state + - \ref ARM_POWER_OFF : Power off: no operation possible + - \ref ARM_POWER_LOW : Low-power mode: sleep or deep-sleep depending on \ref ARM_WIFI_LP_TIMER option set + - \ref ARM_POWER_FULL : Power on: full operation at maximum performance + \return execution status + - \ref ARM_DRIVER_OK : Operation successful + - \ref ARM_DRIVER_ERROR : Operation failed + - \ref ARM_DRIVER_ERROR_UNSUPPORTED : Operation not supported + - \ref ARM_DRIVER_ERROR_PARAMETER : Parameter error (invalid state) +*/ +/** + \fn int32_t ARM_WIFI_GetModuleInfo (char *module_info, uint32_t max_len) + \brief Get Module information. + \param[out] module_info Pointer to character buffer were info string will be returned + \param[in] max_len Maximum length of string to return (including null terminator) + \return execution status + - \ref ARM_DRIVER_OK : Operation successful + - \ref ARM_DRIVER_ERROR : Operation failed + - \ref ARM_DRIVER_ERROR_UNSUPPORTED : Operation not supported + - \ref ARM_DRIVER_ERROR_PARAMETER : Parameter error (NULL module_info pointer or max_len equals to 0) +*/ +/** + \fn int32_t ARM_WIFI_SetOption (uint32_t interface, uint32_t option, const void *data, uint32_t len) + \brief Set WiFi Module Options. + \param[in] interface Interface (0 = Station, 1 = Access Point) + \param[in] option Option to set + \param[in] data Pointer to data relevant to selected option + \param[in] len Length of data (in bytes) + \return execution status + - \ref ARM_DRIVER_OK : Operation successful + - \ref ARM_DRIVER_ERROR : Operation failed + - \ref ARM_DRIVER_ERROR_UNSUPPORTED : Operation not supported + - \ref ARM_DRIVER_ERROR_PARAMETER : Parameter error (invalid interface, NULL data pointer or len less than option specifies) +*/ +/** + \fn int32_t ARM_WIFI_GetOption (uint32_t interface, uint32_t option, void *data, uint32_t *len) + \brief Get WiFi Module Options. + \param[in] interface Interface (0 = Station, 1 = Access Point) + \param[in] option Option to get + \param[out] data Pointer to memory where data for selected option will be returned + \param[in,out] len Pointer to length of data (input/output) + - input: maximum length of data that can be returned (in bytes) + - output: length of returned data (in bytes) + \return execution status + - \ref ARM_DRIVER_OK : Operation successful + - \ref ARM_DRIVER_ERROR : Operation failed + - \ref ARM_DRIVER_ERROR_UNSUPPORTED : Operation not supported + - \ref ARM_DRIVER_ERROR_PARAMETER : Parameter error (invalid interface, NULL data or len pointer, or *len less than option specifies) +*/ +/** + \fn int32_t ARM_WIFI_Scan (ARM_WIFI_SCAN_INFO_t scan_info[], uint32_t max_num) + \brief Scan for available networks in range. + \param[out] scan_info Pointer to array of ARM_WIFI_SCAN_INFO_t structures where available Scan Information will be returned + \param[in] max_num Maximum number of Network Information structures to return + \return number of ARM_WIFI_SCAN_INFO_t structures returned or error code + - value >= 0 : Number of ARM_WIFI_SCAN_INFO_t structures returned + - \ref ARM_DRIVER_ERROR : Operation failed + - \ref ARM_DRIVER_ERROR_PARAMETER : Parameter error (NULL scan_info pointer or max_num equal to 0) +*/ +/** + \fn int32_t ARM_WIFI_Activate (uint32_t interface, ARM_WIFI_CONFIG_t *config) + \brief Activate interface (Connect to a wireless network or activate an access point). + \param[in] interface Interface (0 = Station, 1 = Access Point) + \param[in] config Pointer to ARM_WIFI_CONFIG_t structure where Configuration parameters are located + \return execution status + - \ref ARM_DRIVER_OK : Operation successful + - \ref ARM_DRIVER_ERROR : Operation failed + - \ref ARM_DRIVER_ERROR_TIMEOUT : Timeout occurred + - \ref ARM_DRIVER_ERROR_UNSUPPORTED : Operation not supported (security type, channel autodetect or WPS not supported) + - \ref ARM_DRIVER_ERROR_PARAMETER : Parameter error (invalid interface, NULL config pointer or invalid configuration) +*/ +/** + \fn int32_t ARM_WIFI_Deactivate (uint32_t interface) + \brief Deactivate interface (Disconnect from a wireless network or deactivate an access point). + \param[in] interface Interface (0 = Station, 1 = Access Point) + \return execution status + - \ref ARM_DRIVER_OK : Operation successful + - \ref ARM_DRIVER_ERROR : Operation failed + - \ref ARM_DRIVER_ERROR_PARAMETER : Parameter error (invalid interface) +*/ +/** + \fn uint32_t ARM_WIFI_IsConnected (void) + \brief Get station connection status. + \return station connection status + - value != 0: Station connected + - value = 0: Station not connected +*/ +/** + \fn int32_t ARM_WIFI_GetNetInfo (ARM_WIFI_NET_INFO_t *net_info) + \brief Get station Network Information. + \param[out] net_info Pointer to ARM_WIFI_NET_INFO_t structure where station Network Information will be returned + \return execution status + - \ref ARM_DRIVER_OK : Operation successful + - \ref ARM_DRIVER_ERROR : Operation failed (station not connected) + - \ref ARM_DRIVER_ERROR_UNSUPPORTED : Operation not supported + - \ref ARM_DRIVER_ERROR_PARAMETER : Parameter error (invalid interface or NULL net_info pointer) +*/ +/** + \fn int32_t ARM_WIFI_BypassControl (uint32_t interface, uint32_t mode) + \brief Enable or disable bypass (pass-through) mode. Transmit and receive Ethernet frames (IP layer bypassed and WiFi/Ethernet translation). + \param[in] interface Interface (0 = Station, 1 = Access Point) + \param[in] mode + - value = 1: all packets bypass internal IP stack + - value = 0: all packets processed by internal IP stack + \return execution status + - \ref ARM_DRIVER_OK : Operation successful + - \ref ARM_DRIVER_ERROR : Operation failed + - \ref ARM_DRIVER_ERROR_UNSUPPORTED : Operation not supported + - \ref ARM_DRIVER_ERROR_PARAMETER : Parameter error (invalid interface or mode) +*/ +/** + \fn int32_t ARM_WIFI_EthSendFrame (uint32_t interface, const uint8_t *frame, uint32_t len) + \brief Send Ethernet frame (in bypass mode only). + \param[in] interface Interface (0 = Station, 1 = Access Point) + \param[in] frame Pointer to frame buffer with data to send + \param[in] len Frame buffer length in bytes + \return execution status + - \ref ARM_DRIVER_OK : Operation successful + - \ref ARM_DRIVER_ERROR : Operation failed + - \ref ARM_DRIVER_ERROR_BUSY : Driver is busy + - \ref ARM_DRIVER_ERROR_UNSUPPORTED : Operation not supported + - \ref ARM_DRIVER_ERROR_PARAMETER : Parameter error (invalid interface or NULL frame pointer) +*/ +/** + \fn int32_t ARM_WIFI_EthReadFrame (uint32_t interface, uint8_t *frame, uint32_t len) + \brief Read data of received Ethernet frame (in bypass mode only). + \param[in] interface Interface (0 = Station, 1 = Access Point) + \param[in] frame Pointer to frame buffer for data to read into + \param[in] len Frame buffer length in bytes + \return number of data bytes read or error code + - value >= 0 : Number of data bytes read + - \ref ARM_DRIVER_ERROR : Operation failed + - \ref ARM_DRIVER_ERROR_UNSUPPORTED : Operation not supported + - \ref ARM_DRIVER_ERROR_PARAMETER : Parameter error (invalid interface or NULL frame pointer) +*/ +/** + \fn uint32_t ARM_WIFI_EthGetRxFrameSize (uint32_t interface) + \brief Get size of received Ethernet frame (in bypass mode only). + \param[in] interface Interface (0 = Station, 1 = Access Point) + \return number of bytes in received frame +*/ +/** + \fn int32_t ARM_WIFI_SocketCreate (int32_t af, int32_t type, int32_t protocol) + \brief Create a communication socket. + \param[in] af Address family + \param[in] type Socket type + \param[in] protocol Socket protocol + \return status information + - Socket identification number (>=0) + - \ref ARM_SOCKET_EINVAL : Invalid argument + - \ref ARM_SOCKET_ENOTSUP : Operation not supported + - \ref ARM_SOCKET_ENOMEM : Not enough memory + - \ref ARM_SOCKET_ERROR : Unspecified error +*/ +/** + \fn int32_t ARM_WIFI_SocketBind (int32_t socket, const uint8_t *ip, uint32_t ip_len, uint16_t port) + \brief Assign a local address to a socket. + \param[in] socket Socket identification number + \param[in] ip Pointer to local IP address + \param[in] ip_len Length of 'ip' address in bytes + \param[in] port Local port number + \return status information + - 0 : Operation successful + - \ref ARM_SOCKET_ESOCK : Invalid socket + - \ref ARM_SOCKET_EINVAL : Invalid argument (address or socket already bound) + - \ref ARM_SOCKET_EADDRINUSE : Address already in use + - \ref ARM_SOCKET_ERROR : Unspecified error +*/ +/** + \fn int32_t ARM_WIFI_SocketListen (int32_t socket, int32_t backlog) + \brief Listen for socket connections. + \param[in] socket Socket identification number + \param[in] backlog Number of connection requests that can be queued + \return status information + - 0 : Operation successful + - \ref ARM_SOCKET_ESOCK : Invalid socket + - \ref ARM_SOCKET_EINVAL : Invalid argument (socket not bound) + - \ref ARM_SOCKET_ENOTSUP : Operation not supported + - \ref ARM_SOCKET_EISCONN : Socket is already connected + - \ref ARM_SOCKET_ERROR : Unspecified error +*/ +/** + \fn int32_t ARM_WIFI_SocketAccept (int32_t socket, uint8_t *ip, uint32_t *ip_len, uint16_t *port) + \brief Accept a new connection on a socket. + \param[in] socket Socket identification number + \param[out] ip Pointer to buffer where address of connecting socket shall be returned (NULL for none) + \param[in,out] ip_len Pointer to length of 'ip' (or NULL if 'ip' is NULL) + - length of supplied 'ip' on input + - length of stored 'ip' on output + \param[out] port Pointer to buffer where port of connecting socket shall be returned (NULL for none) + \return status information + - socket identification number of accepted socket (>=0) + - \ref ARM_SOCKET_ESOCK : Invalid socket + - \ref ARM_SOCKET_EINVAL : Invalid argument (socket not in listen mode) + - \ref ARM_SOCKET_ENOTSUP : Operation not supported (socket type does not support accepting connections) + - \ref ARM_SOCKET_ECONNRESET : Connection reset by the peer + - \ref ARM_SOCKET_ECONNABORTED : Connection aborted locally + - \ref ARM_SOCKET_EAGAIN : Operation would block or timed out (may be called again) + - \ref ARM_SOCKET_ERROR : Unspecified error +*/ +/** + \fn int32_t ARM_WIFI_SocketConnect (int32_t socket, const uint8_t *ip, uint32_t ip_len, uint16_t port) + \brief Connect a socket to a remote host. + \param[in] socket Socket identification number + \param[in] ip Pointer to remote IP address + \param[in] ip_len Length of 'ip' address in bytes + \param[in] port Remote port number + \return status information + - 0 : Operation successful + - \ref ARM_SOCKET_ESOCK : Invalid socket + - \ref ARM_SOCKET_EINVAL : Invalid argument + - \ref ARM_SOCKET_EALREADY : Connection already in progress + - \ref ARM_SOCKET_EINPROGRESS : Operation in progress + - \ref ARM_SOCKET_EISCONN : Socket is connected + - \ref ARM_SOCKET_ECONNREFUSED : Connection rejected by the peer + - \ref ARM_SOCKET_ECONNABORTED : Connection aborted locally + - \ref ARM_SOCKET_EADDRINUSE : Address already in use + - \ref ARM_SOCKET_ETIMEDOUT : Operation timed out + - \ref ARM_SOCKET_ERROR : Unspecified error +*/ +/** + \fn int32_t ARM_WIFI_SocketRecv (int32_t socket, void *buf, uint32_t len) + \brief Receive data on a connected socket. + \param[in] socket Socket identification number + \param[out] buf Pointer to buffer where data should be stored + \param[in] len Length of buffer (in bytes) + \return status information + - number of bytes received (>0) + - \ref ARM_SOCKET_ESOCK : Invalid socket + - \ref ARM_SOCKET_EINVAL : Invalid argument (pointer to buffer or length) + - \ref ARM_SOCKET_ENOTCONN : Socket is not connected + - \ref ARM_SOCKET_ECONNRESET : Connection reset by the peer + - \ref ARM_SOCKET_ECONNABORTED : Connection aborted locally + - \ref ARM_SOCKET_EAGAIN : Operation would block or timed out (may be called again) + - \ref ARM_SOCKET_ERROR : Unspecified error +*/ +/** + \fn int32_t ARM_WIFI_SocketRecvFrom (int32_t socket, void *buf, uint32_t len, uint8_t *ip, uint32_t *ip_len, uint16_t *port) + \brief Receive data on a socket. + \param[in] socket Socket identification number + \param[out] buf Pointer to buffer where data should be stored + \param[in] len Length of buffer (in bytes) + \param[out] ip Pointer to buffer where remote source address shall be returned (NULL for none) + \param[in,out] ip_len Pointer to length of 'ip' (or NULL if 'ip' is NULL) + - length of supplied 'ip' on input + - length of stored 'ip' on output + \param[out] port Pointer to buffer where remote source port shall be returned (NULL for none) + \return status information + - number of bytes received (>0) + - \ref ARM_SOCKET_ESOCK : Invalid socket + - \ref ARM_SOCKET_EINVAL : Invalid argument (pointer to buffer or length) + - \ref ARM_SOCKET_ENOTCONN : Socket is not connected + - \ref ARM_SOCKET_ECONNRESET : Connection reset by the peer + - \ref ARM_SOCKET_ECONNABORTED : Connection aborted locally + - \ref ARM_SOCKET_EAGAIN : Operation would block or timed out (may be called again) + - \ref ARM_SOCKET_ERROR : Unspecified error +*/ +/** + \fn int32_t ARM_WIFI_SocketSend (int32_t socket, const void *buf, uint32_t len) + \brief Send data on a connected socket. + \param[in] socket Socket identification number + \param[in] buf Pointer to buffer containing data to send + \param[in] len Length of data (in bytes) + \return status information + - number of bytes sent (>0) + - \ref ARM_SOCKET_ESOCK : Invalid socket + - \ref ARM_SOCKET_EINVAL : Invalid argument (pointer to buffer or length) + - \ref ARM_SOCKET_ENOTCONN : Socket is not connected + - \ref ARM_SOCKET_ECONNRESET : Connection reset by the peer + - \ref ARM_SOCKET_ECONNABORTED : Connection aborted locally + - \ref ARM_SOCKET_EAGAIN : Operation would block or timed out (may be called again) + - \ref ARM_SOCKET_ERROR : Unspecified error +*/ +/** + \fn int32_t ARM_WIFI_SocketSendTo (int32_t socket, const void *buf, uint32_t len, const uint8_t *ip, uint32_t ip_len, uint16_t port) + \brief Send data on a socket. + \param[in] socket Socket identification number + \param[in] buf Pointer to buffer containing data to send + \param[in] len Length of data (in bytes) + \param[in] ip Pointer to remote destination IP address + \param[in] ip_len Length of 'ip' address in bytes + \param[in] port Remote destination port number + \return status information + - number of bytes sent (>0) + - \ref ARM_SOCKET_ESOCK : Invalid socket + - \ref ARM_SOCKET_EINVAL : Invalid argument (pointer to buffer or length) + - \ref ARM_SOCKET_ENOTCONN : Socket is not connected + - \ref ARM_SOCKET_ECONNRESET : Connection reset by the peer + - \ref ARM_SOCKET_ECONNABORTED : Connection aborted locally + - \ref ARM_SOCKET_EAGAIN : Operation would block or timed out (may be called again) + - \ref ARM_SOCKET_ERROR : Unspecified error +*/ +/** + \fn int32_t ARM_WIFI_SocketGetSockName (int32_t socket, uint8_t *ip, uint32_t *ip_len, uint16_t *port) + \brief Retrieve local IP address and port of a socket. + \param[in] socket Socket identification number + \param[out] ip Pointer to buffer where local address shall be returned (NULL for none) + \param[in,out] ip_len Pointer to length of 'ip' (or NULL if 'ip' is NULL) + - length of supplied 'ip' on input + - length of stored 'ip' on output + \param[out] port Pointer to buffer where local port shall be returned (NULL for none) + \return status information + - 0 : Operation successful + - \ref ARM_SOCKET_ESOCK : Invalid socket + - \ref ARM_SOCKET_EINVAL : Invalid argument (pointer to buffer or length) + - \ref ARM_SOCKET_ERROR : Unspecified error +*/ +/** + \fn int32_t ARM_WIFI_SocketGetPeerName (int32_t socket, uint8_t *ip, uint32_t *ip_len, uint16_t *port) + \brief Retrieve remote IP address and port of a socket + \param[in] socket Socket identification number + \param[out] ip Pointer to buffer where remote address shall be returned (NULL for none) + \param[in,out] ip_len Pointer to length of 'ip' (or NULL if 'ip' is NULL) + - length of supplied 'ip' on input + - length of stored 'ip' on output + \param[out] port Pointer to buffer where remote port shall be returned (NULL for none) + \return status information + - 0 : Operation successful + - \ref ARM_SOCKET_ESOCK : Invalid socket + - \ref ARM_SOCKET_EINVAL : Invalid argument (pointer to buffer or length) + - \ref ARM_SOCKET_ENOTCONN : Socket is not connected + - \ref ARM_SOCKET_ERROR : Unspecified error +*/ +/** + \fn int32_t ARM_WIFI_SocketGetOpt (int32_t socket, int32_t opt_id, void *opt_val, uint32_t *opt_len) + \brief Get socket option. + \param[in] socket Socket identification number + \param[in] opt_id Option identifier + \param[out] opt_val Pointer to the buffer that will receive the option value + \param[in,out] opt_len Pointer to length of the option value + - length of buffer on input + - length of data on output + \return status information + - 0 : Operation successful + - \ref ARM_SOCKET_ESOCK : Invalid socket + - \ref ARM_SOCKET_EINVAL : Invalid argument + - \ref ARM_SOCKET_ENOTSUP : Operation not supported + - \ref ARM_SOCKET_ERROR : Unspecified error +*/ +/** + \fn int32_t ARM_WIFI_SocketSetOpt (int32_t socket, int32_t opt_id, const void *opt_val, uint32_t opt_len) + \brief Set socket option. + \param[in] socket Socket identification number + \param[in] opt_id Option identifier + \param[in] opt_val Pointer to the option value + \param[in] opt_len Length of the option value in bytes + \return status information + - 0 : Operation successful + - \ref ARM_SOCKET_ESOCK : Invalid socket + - \ref ARM_SOCKET_EINVAL : Invalid argument + - \ref ARM_SOCKET_ENOTSUP : Operation not supported + - \ref ARM_SOCKET_ERROR : Unspecified error +*/ +/** + \fn int32_t ARM_WIFI_SocketClose (int32_t socket) + \brief Close and release a socket. + \param[in] socket Socket identification number + \return status information + - 0 : Operation successful + - \ref ARM_SOCKET_ESOCK : Invalid socket + - \ref ARM_SOCKET_EAGAIN : Operation would block (may be called again) + - \ref ARM_SOCKET_ERROR : Unspecified error +*/ +/** + \fn int32_t ARM_WIFI_SocketGetHostByName (const char *name, int32_t af, uint8_t *ip, uint32_t *ip_len) + \brief Retrieve host IP address from host name. + \param[in] name Host name + \param[in] af Address family + \param[out] ip Pointer to buffer where resolved IP address shall be returned + \param[in,out] ip_len Pointer to length of 'ip' + - length of supplied 'ip' on input + - length of stored 'ip' on output + \return status information + - 0 : Operation successful + - \ref ARM_SOCKET_EINVAL : Invalid argument + - \ref ARM_SOCKET_ENOTSUP : Operation not supported + - \ref ARM_SOCKET_ETIMEDOUT : Operation timed out + - \ref ARM_SOCKET_EHOSTNOTFOUND : Host not found + - \ref ARM_SOCKET_ERROR : Unspecified error +*/ +/** + \fn int32_t ARM_WIFI_Ping (const uint8_t *ip, uint32_t ip_len) + \brief Probe remote host with Ping command. + \param[in] ip Pointer to remote host IP address + \param[in] ip_len Length of 'ip' address in bytes + \return execution status + - \ref ARM_DRIVER_OK : Operation successful + - \ref ARM_DRIVER_ERROR : Operation failed + - \ref ARM_DRIVER_ERROR_TIMEOUT : Timeout occurred + - \ref ARM_DRIVER_ERROR_UNSUPPORTED : Operation not supported + - \ref ARM_DRIVER_ERROR_PARAMETER : Parameter error (NULL ip pointer or ip_len different than 4 or 16) +*/ +/** + \fn void ARM_WIFI_SignalEvent (uint32_t event, void *arg) + \brief Signal WiFi Events. + \param[in] event \ref wifi_event notification mask + \param[in] arg Pointer to argument of signaled event + \return none +*/ + +typedef void (*ARM_WIFI_SignalEvent_t) (uint32_t event, void *arg); ///< Pointer to \ref ARM_WIFI_SignalEvent : Signal WiFi Event. + + +/** +\brief WiFi Driver Capabilities. +*/ +typedef struct { + uint32_t station : 1; ///< Station + uint32_t ap : 1; ///< Access Point + uint32_t station_ap : 1; ///< Concurrent Station and Access Point + uint32_t wps_station : 1; ///< WiFi Protected Setup (WPS) for Station + uint32_t wps_ap : 1; ///< WiFi Protected Setup (WPS) for Access Point + uint32_t event_ap_connect : 1; ///< Access Point: event generated on Station connect + uint32_t event_ap_disconnect : 1; ///< Access Point: event generated on Station disconnect + uint32_t event_eth_rx_frame : 1; ///< Event generated on Ethernet frame reception in bypass mode + uint32_t bypass_mode : 1; ///< Bypass or pass-through mode (Ethernet interface) + uint32_t ip : 1; ///< IP (UDP/TCP) (Socket interface) + uint32_t ip6 : 1; ///< IPv6 (Socket interface) + uint32_t ping : 1; ///< Ping (ICMP) + uint32_t reserved : 20; ///< Reserved (must be zero) +} ARM_WIFI_CAPABILITIES; + +/** +\brief Access structure of the WiFi Driver. +*/ +typedef struct { + ARM_DRIVER_VERSION (*GetVersion) (void); + ARM_WIFI_CAPABILITIES (*GetCapabilities) (void); + int32_t (*Initialize) (ARM_WIFI_SignalEvent_t cb_event); + int32_t (*Uninitialize) (void); + int32_t (*PowerControl) (ARM_POWER_STATE state); + int32_t (*GetModuleInfo) (char *module_info, uint32_t max_len); + int32_t (*SetOption) (uint32_t interface, uint32_t option, const void *data, uint32_t len); + int32_t (*GetOption) (uint32_t interface, uint32_t option, void *data, uint32_t *len); + int32_t (*Scan) (ARM_WIFI_SCAN_INFO_t scan_info[], uint32_t max_num); + int32_t (*Activate) (uint32_t interface, const ARM_WIFI_CONFIG_t *config); + int32_t (*Deactivate) (uint32_t interface); + uint32_t (*IsConnected) (void); + int32_t (*GetNetInfo) (ARM_WIFI_NET_INFO_t *net_info); + int32_t (*BypassControl) (uint32_t interface, uint32_t mode); + int32_t (*EthSendFrame) (uint32_t interface, const uint8_t *frame, uint32_t len); + int32_t (*EthReadFrame) (uint32_t interface, uint8_t *frame, uint32_t len); + uint32_t (*EthGetRxFrameSize) (uint32_t interface); + int32_t (*SocketCreate) (int32_t af, int32_t type, int32_t protocol); + int32_t (*SocketBind) (int32_t socket, const uint8_t *ip, uint32_t ip_len, uint16_t port); + int32_t (*SocketListen) (int32_t socket, int32_t backlog); + int32_t (*SocketAccept) (int32_t socket, uint8_t *ip, uint32_t *ip_len, uint16_t *port); + int32_t (*SocketConnect) (int32_t socket, const uint8_t *ip, uint32_t ip_len, uint16_t port); + int32_t (*SocketRecv) (int32_t socket, void *buf, uint32_t len); + int32_t (*SocketRecvFrom) (int32_t socket, void *buf, uint32_t len, uint8_t *ip, uint32_t *ip_len, uint16_t *port); + int32_t (*SocketSend) (int32_t socket, const void *buf, uint32_t len); + int32_t (*SocketSendTo) (int32_t socket, const void *buf, uint32_t len, const uint8_t *ip, uint32_t ip_len, uint16_t port); + int32_t (*SocketGetSockName) (int32_t socket, uint8_t *ip, uint32_t *ip_len, uint16_t *port); + int32_t (*SocketGetPeerName) (int32_t socket, uint8_t *ip, uint32_t *ip_len, uint16_t *port); + int32_t (*SocketGetOpt) (int32_t socket, int32_t opt_id, void *opt_val, uint32_t *opt_len); + int32_t (*SocketSetOpt) (int32_t socket, int32_t opt_id, const void *opt_val, uint32_t opt_len); + int32_t (*SocketClose) (int32_t socket); + int32_t (*SocketGetHostByName) (const char *name, int32_t af, uint8_t *ip, uint32_t *ip_len); + int32_t (*Ping) (const uint8_t *ip, uint32_t ip_len); +} const ARM_DRIVER_WIFI; + +#ifdef __cplusplus +} +#endif + +#endif /* DRIVER_WIFI_H_ */ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS_EW2019.pdf b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS_EW2019.pdf new file mode 100644 index 000000000000..dd89a60a87dd Binary files /dev/null and b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/CMSIS_EW2019.pdf differ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/LICENSE.txt b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/LICENSE.txt new file mode 100644 index 000000000000..8dada3edaf50 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/LICENSE.txt @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/README.md b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/README.md new file mode 100644 index 000000000000..b26786e79fc9 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/cmsis/README.md @@ -0,0 +1,2 @@ +# cmsis +CMSIS Driver API diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/comm-stack-dk/data.c b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/comm-stack-dk/data.c new file mode 100644 index 000000000000..985b095d7f26 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/comm-stack-dk/data.c @@ -0,0 +1,228 @@ +// +// Description: Data Operation Utils +// +// Copyright (c) 2019 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#include + +#include "data.h" + +/* + * Host to big endian + */ +void htoBe32(uint32_t value, uint8_t *data) +{ + data[0] = (value & 0xFF000000) >> 24; + data[1] = (value & 0x00FF0000) >> 16; + data[2] = (value & 0x0000FF00) >> 8; + data[3] = (value & 0x000000FF); +} + +void hToLe32(uint16_t value, uint8_t* data) +{ + data[0] = (value & 0x000000FF); + data[1] = (value >> 8) & 0x000000FF; + data[2] = (value >> 16) & 0x000000FF; + data[3] = (value >> 24) & 0x000000FF; +} + +void htoBe16(uint16_t value, uint8_t* data) +{ + data[0] = (value & 0xFF00) >> 8; + data[1] = (value & 0x00FF); +} + +void hToLe16(uint16_t value, uint8_t *data) +{ + data[0] = (value & 0x00FF); + data[1] = (value & 0xFF00) >> 8; +} + +uint32_t beToH32(uint8_t *data) +{ + + uint32_t value = 0; + + value |= data[0] << 24; + value |= data[1] << 16; + value |= data[2] << 8; + value |= data[3]; + + return value; +} + +uint32_t leToH32(uint8_t* data) +{ + uint32_t value = 0; + + value |= data[3] << 24; + value |= data[2] << 16; + value |= data[1] << 8; + value |= data[0]; + + return value; +} + + +uint16_t beToH16(uint8_t* data) +{ + uint32_t value = 0; + + value |= data[0] << 8; + value |= data[1]; + + return value; +} + +uint16_t leToH16(uint8_t *data) +{ + uint32_t value = 0; + + value |= data[1] << 8; + value |= data[0]; + + return value; +} + +uint8_t beToH8(uint8_t *data) +{ + uint8_t value = 0; + value |= data[0]; + + return value; +} + +int16_t toInt16(int16_t data, uint8_t numBit) +{ + int16_t mask; + int16_t res = data; + + if (numBit <= 16) + { + mask = (1 << numBit) - 1; + if (data >= (mask >> 1)) + { + // Value is negative + res = data | ~mask; + } + } + + return res; +} + +int32_t toInt32(int32_t data, uint8_t numBit) +{ + int32_t mask; + int32_t res = data; + + if (numBit <= 32) + { + mask = (1 << numBit) - 1; + if (data >= (mask >> 1)) + { + // Value is negative + res = data | ~mask; + } + } + + return res; +} + +static inline uint32_t powTwo(uint32_t order) +{ + uint32_t powOne = 1; + return (powOne << order); +} + +bool twoComplement(int32_t value, size_t bitLength, uint32_t *result) +{ + bool res = false; + + uint32_t subMaxValue = powTwo(bitLength - 1); + + int32_t maxValue = subMaxValue - 1; + int32_t minValue = -1 * subMaxValue; + + if (result != NULL && value <= maxValue && value >= minValue) + { + if (value >= 0) + { + *result = value; + } + if (value < 0) + { + *result = powTwo(bitLength) + value; + } + + res = true; + } + + return res; +} + +uint16_t endian_reverse16(uint16_t val) +{ + return ((val & 0xFF) < 8) | ((val & 0xFF00) > 8); + +} + +uint32_t endian_reverse32(uint32_t val) +{ + return ((val & 0xFF) < 24) | ((val & 0xFF00) < 8) | ((val & 0xFF0000) > 8) | ((val & 0xFF000000) > 24); + +} + +uint64_t endian_reverse64(uint64_t val) +{ + return ((((val) & 0xff00000000000000ull) >> 56) | + (((val) & 0x00ff000000000000ull) >> 40) | + (((val) & 0x0000ff0000000000ull) >> 24) | + (((val) & 0x000000ff00000000ull) >> 8) | + (((val) & 0x00000000ff000000ull) << 8) | + (((val) & 0x0000000000ff0000ull) << 24) | + (((val) & 0x000000000000ff00ull) << 40) | + (((val) & 0x00000000000000ffull) << 56)); +} + +void endian_reverse_array_enc(uint8_t *dst, uint8_t *array, uint8_t sizeInBytes) +{ + uint8_t dstIndex; + uint8_t valueIndex = sizeInBytes; + for (dstIndex = 0; dstIndex < sizeInBytes; dstIndex++) + { + dst[dstIndex] = array[--valueIndex]; + } +} + +void endian_reverse_array(uint8_t *array, uint8_t sizeInBytes) +{ + uint8_t endIndex = sizeInBytes; + uint8_t startIndex; + uint8_t value; + + for (startIndex = 0; startIndex < endIndex; startIndex++, endIndex--) + { + value = array[startIndex]; + array[startIndex] = array[endIndex]; + array[endIndex] = value; + } +} diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/comm-stack-dk/data.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/comm-stack-dk/data.h new file mode 100644 index 000000000000..ed5470856822 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/contribs/comm-stack-dk/data.h @@ -0,0 +1,218 @@ +// +// Description: Data Operation Utils +// +// Copyright (c) 2019 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#ifndef _DATA_H +#define _DATA_H + +#include + +#define DATA_ARRAY_LENGTH(array) (sizeof(array)/sizeof(array[0])) + +#ifdef __cplusplus +extern "C" { +#endif + +#define NANO_PER_UNITY (1.0e9) +#define MICRO_PER_UNITY (1.0e6) +#define MICRO_PER_MILLI (1.0e3) +#define MILLI_PER_UNITY (1.0e3) +#define UNITY_PER_KILO (1.0e3) +#define UNITY_PER_MEGA (1.0e6) +#define MEGA_PER_GIGA (1.0e3) + +#define MASK_1_BIT (0x00000001) +#define MASK_2_BIT (0x00000003) +#define MASK_3_BIT (0x00000007) +#define MASK_4_BIT (0x0000000F) +#define MASK_5_BIT (0x0000001F) +#define MASK_6_BIT (0x0000003F) +#define MASK_7_BIT (0x0000007F) +#define MASK_8_BIT (0x000000FF) +#define MASK_9_BIT (0x000001FF) +#define MASK_10_BIT (0x000003FF) +#define MASK_11_BIT (0x000007FF) +#define MASK_12_BIT (0x00000FFF) +#define MASK_13_BIT (0x00001FFF) +#define MASK_14_BIT (0x00003FFF) +#define MASK_15_BIT (0x00007FFF) +#define MASK_16_BIT (0x0000FFFF) +#define MASK_17_BIT (0x0001FFFF) +#define MASK_18_BIT (0x0003FFFF) +#define MASK_19_BIT (0x0007FFFF) +#define MASK_20_BIT (0x000FFFFF) +#define MASK_21_BIT (0x001FFFFF) +#define MASK_22_BIT (0x003FFFFF) +#define MASK_23_BIT (0x007FFFFF) +#define MASK_24_BIT (0x00FFFFFF) + +#define SHIFT_1_BIT (0x1) +#define SHIFT_2_BIT (0x2) +#define SHIFT_4_BIT (0x4) +#define SHIFT_8_BIT (0x8) +#define SHIFT_16_BIT (0x16) + +#define LSHIFT(X, POS) (X<>POS) + +#define BYTE_LENGTH_IN_BITS (8) + +#define PI (3.14159265) + +void htoBe32(uint32_t value, uint8_t *data); + +void hToLe32(uint16_t value, uint8_t *data); + +void htoBe16(uint16_t value, uint8_t *data); + +void hToLe16(uint16_t value, uint8_t *data); + +uint32_t beToH32(uint8_t *data); + +uint16_t beToH16(uint8_t *data); + +uint32_t leToH32(uint8_t *data); + +uint16_t leToH16(uint8_t *data); + +int16_t toInt16(int16_t data, uint8_t numBit); + +int32_t toInt32(int32_t data, uint8_t numBit); + +float be16ToFloat(const uint8_t *data); + +void floatToBe16(float value, uint8_t *data); + +bool twoComplement(int32_t value, size_t bitLength, uint32_t *result); + +#define COMMUNICATION_STACK_1_BYTE (1) +#define COMMUNICATION_STACK_2_BYTES (2) +#define COMMUNICATION_STACK_4_BYTES (4) + +uint16_t endian_reverse16(uint16_t val); + +uint32_t endian_reverse32(uint32_t val); + +uint64_t endian_reverse64(uint64_t val); + +void endian_reverse_array(uint8_t* array, uint8_t sizeInBytes); + +void endian_reverse_array_enc(uint8_t* dst, uint8_t* array, uint8_t sizeInBytes); + +#if BYTE_ORDER == LITTLE_ENDIAN + +#define htole16(x) (x) +#define le16toh(x) (x) +#if __GNUC__ +#define htobe16(x) __builtin_bswap16(x) +#define be16toh(x) __builtin_bswap16(x) +#else +#define htobe16(x) do{x=endian_reverse16(x);} while (0) +#define be16toh(x) do{x=endian_reverse16(x);} while (0) +#endif //__GNUC__ + +#define htole32(x) (x) +#define le32toh(x) (x) +#if __GNUC__ +#define htobe32(x) __builtin_bswap32(x) +#define be32toh(x) __builtin_bswap32(x) +#else +#define htobe32(x) do{x=endian_reverse32(x);} while (0) +#define be32toh(x) do{x=endian_reverse32(x);} while (0) +#endif //__GNUC__ + +#define htole64(x) (x) +#define le64toh(x) (x) +#if __GNUC__ +#define htobe64(x) __builtin_bswap64(x) +#define be64toh(x) __builtin_bswap64(x) +#else +#define htobe64(x) do{x=endian_reverse64(x);} while (0) +#define be64toh(x) do{x=endian_reverse64(x);} while (0) +#endif //__GNUC__ + +#define htolevalue(array, size) (array, size) +#define levaluetoh(array, size) (array, size) + +#define htolevalue_enc(dst, array, size) (dst, array, size) +#define levaluetoh_enc(dst, array, size) (dst, array, size) + +#define htobevalue(array, size) do{endian_reverse_array(array, size;} while (0) +#define bevaluetoh(array, size) do{endian_reverse_array(array, size);} while (0) + +#define htobevalue_enc(dst, array, size) do{endian_reverse_array_enc(dst, array, size;} while (0) +#define bevaluetoh_enc(dst, array, size) do{endian_reverse_array_enc(dst, array, size);} while (0) + +#elif BYTE_ORDER == BIG_ENDIAN +#define htobe16(x) (x) +#define be16toh(x) (x) +#if __GNUC__ +#define htole16(x) __builtin_bswap16(x) +#define le16toh(x) __builtin_bswap16(x) +#else +#define htole16(x) do{x=endian_reverse16(x);} while (0) +#define le16toh(x) do{x=endian_reverse16(x);} while (0) +#endif //__GNUC__ + +#define htobe32(x) (x) +#define be32toh(x) (x) +#if __GNUC__ +#define htole32(x) __builtin_bswap32(x) +#define le32toh(x) __builtin_bswap32(x) +#else +#define htole32(x) do{x=endian_reverse32(x);} while (0) +#define le32toh(x) do{x=endian_reverse32(x);} while (0) +#endif //__GNUC__ + +#define htobe64(x) (x) +#define be64toh(x) (x) +#if __GNUC__ +#define htole64(x) __builtin_bswap64(x) +#define le64toh(x) __builtin_bswap64(x) +#else +#define htole64(x) do{x=endian_reverse64(x);}while (0) +#define le64toh(x) do{x=endian_reverse64(x);}while (0) +#endif //__GNUC__ + + +#define htobevalue(array, size) (array, size) +#define bevaluetoh(array, size) (array, size) + +#define htobevalue_enc(dst, array, size) (dst, array, size) +#define bevaluetoh_end(dst, array, size) (dst, array, size) + +#define htolevalue(array, size) do{endian_reverse_array(array, size);}while (0) +#define levaluetoh(array, size) do{endian_reverse_array(array, size);}while (0) + +#define htolevalue_enc(dst, array, size) do{endian_reverse_array_enc(dst, array, size;} while (0) +#define levaluetoh_enc(dst, array, size) do{endian_reverse_array_enc(dst, array, size);} while (0) + + +#endif + + +#ifdef __cplusplus +} +#endif + +#endif //_DATA_H \ No newline at end of file diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/boards/boreasTime.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/boards/boreasTime.h new file mode 100644 index 000000000000..8e745cf1676d --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/boards/boreasTime.h @@ -0,0 +1,73 @@ +// +// Description: Generic Time API +// +// Copyright (c) 2019 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#ifndef DKCORE_LAUNCHPADTIME_H +#define DKCORE_LAUNCHPADTIME_H + +#include + +#define MICROSECOND_IN_MILLISECOND(_us) (_us / 1000) +#define MILLISECOND_MICROSECOND(_ms) (_ms*1000) +#define SECOND_IN_MILLISECOND(_s) (_s * 1000) +#define SECOND_IN_MICROSECOND(_s) (_s * 1000000) + +typedef uint32_t TimeStamp; + +/* + * @brief Initiate the time service + * + * @return True if the initialisation succeeded, otherwise false. + */ +bool timeInit(void); + +/** + * @brief Wait specific amount time in millisecond. + * + * @param[in] timeUs The time in millisecond to wait for + */ +void timeWaitMs(uint32_t timeMs); + +/** + * @brief Wait specific amount time in microsecond. + * + * @param[in] timeUs The time in microsecond to wait for + */ +void timeWaitUs(uint32_t timeUs); + +/** + * @brief Get the time since platform has been initiated. + * + * @return uint32_t The time in millisecond + */ +TimeStamp timeGet(void); + +/** + * @brief + * + * @return + */ +uint64_t timeGetUsCounter(void); + + +#endif //DKCORE_LAUNCHPADTIME_H diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/gpio/gpio.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/gpio/gpio.h new file mode 100644 index 000000000000..3c579eb9ef9a --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/gpio/gpio.h @@ -0,0 +1,68 @@ +// +// Description: Generic GPIO API +// Created on 1/10/2020 +// Copyright (c) 2020 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +#ifndef DKCORE_GPIO_H +#define DKCORE_GPIO_H + +#include + +typedef struct _GPIO Gpio; +typedef struct _GPIO Gpio_t; + +typedef enum +{ + GPIOState_High = 0, + GPIOState_Low, + GPIOState_Invalid +} GPIOState; + +typedef enum +{ + GPIOISR_Falling = 0, + GPIOISR_Rising, + GPIOISR_Both, + GPIOISR_None, +} GPIOIsr; + +typedef enum +{ + GPIODir_Input = 0, + GPIODir_Output, + GPIODir_Bidirectional +} GPIODir; + +typedef void (*GPIOIsrCb)(const Gpio *gpio, void *context, GPIOIsr event); +typedef bool (*GPIOSet)(const Gpio *gpio, GPIOState state); +typedef GPIOState (*GPIOGet)(const Gpio *gpio); +typedef bool (*GPIORegisterISR)(const Gpio *gpio, GPIOIsr isrMode, GPIOIsrCb cb, void *context); +typedef bool (*GPIOUnregisterISR)(const Gpio *gpio); + +struct _GPIO +{ + GPIOSet set; + GPIOGet get; + GPIORegisterISR registerIsr; + GPIOUnregisterISR unregisterIsr; +}; + +#endif //DKCORE_GPIO_H \ No newline at end of file diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/haptic/bos0614Driver.c b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/haptic/bos0614Driver.c new file mode 100644 index 000000000000..d96541d0c63a --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/haptic/bos0614Driver.c @@ -0,0 +1,2497 @@ +// +// Description: BOS0614 Driver Implementation +// Created on 5/5/2020 +// Copyright (c) 2020 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +#define pr_fmt(fmt) "bos0614: %s: " fmt, __func__ + +#define DEBUG +//#undef DEBUG + +#include +#include + +#include "contribs/cmsis/CMSIS/Driver/Include/Driver_Common.h" +#include "contribs/comm-stack-dk/data.h" + +#include "bsp/boards/boreasTime.h" +#include "bsp/drivers/i2c/i2c.h" +#include "bsp/drivers/haptic/bos0614Register.h" +#include "bsp/drivers/haptic/bos0614Driver.h" + +#ifdef DEBUG +#undef pr_debug +#define pr_debug pr_info +#undef dev_dbg +#define dev_dbg dev_info +#endif + +#define BOS0614_REV_B_RETURNED_CHIP_ID (0x0000) +#define BOS0614_REV_B_CHIP_ID (0x000D) +#define BOS0614_REV_C_CHIP_ID_BASE (0x0604) +#define BOS0614_REV_C_CHIP_ID_DEFAULT (0x0614) +#define BOS0614_REV_C_CHIP_ID_MASK (0xFF0F) +#define BOS0614_I2C_ADDRESS (0x2c) + +#define SFT_RESET_TIME_IN_US (100) +#define AMPLITUDE_MAX_VALUE_VOLT (60) +#define AMPLITUDE_MAX_VALUE (0xFFF) +#define FB_R_V_REF (68.4) + +#define FREQUENCY_RESOLUTION_HZ (3.90625) + +#define FIFO_MAX_AMPLITUDE (3689) + +#define BURST_WRITE_RAM_BANK_INDEX (1) +#define BURST_WRITE_RAM_ADDRESS_INDEX (3) +#define BURST_WRITE_RAM_LENGTH_INDEX (5) +#define BURST_WRITE_RAM_DATA_INDEX (7) +#define WRITE_RAM_BANK_INDEX (1) +#define WRITE_RAM_DATA_INDEX (3) + +#define RAM_ADDRESS_INDEX (0) +#define RAM_ADDRESS_LENGTH (1) +#define SEQUENCER_STOP_ADDRESS_SHIFT (12) + +#define MAXIMUM_DATA_LENGTH (1024) +#define MAXIMUM_SAMPLING_RATE (1024) + +#define REG_VALUE_LENGTH (2) +#define WRITE_REG_LENGTH (3) + +#define I2C_ADDRESS_INDEX (0) +#define I2C_ADDRESS_REG_DATA_INDEX (1) + +#define BOS0614_ENABLE (1) +#define BOS0614_DISABLE (0) + +#define BOS0614_THRESH_STEP_mV (1.66) +#define BOS0614_SLOPE_THRESH_STEP_mV (2.2) + +#define BOS0614_SENSEDATA_CONVERSION_uV (220) +#define BOS0614_SENSEDATA_VALID_RANGE (16384) + +#define THRESHOLD_NBR_OF_BITS (12) +#define SLOPE_THRESHOLD_NBR_OF_BITS (7) + +#define MAXIMUM_WAVEFORM_ID_FOR_AUTO_SENSING (3) + +#define MIN_TC_VALUE (1) +#define MAX_TC_VALUE (31) + +#define MAXIMUM_NUMBER_OF_SLICE (597) +#define MAXIMUM_WAVEFORM_ID_SEQUENCER (15) +#define WAVEFORM_METADATA_LENGTH (3) +#define MAXIMUM_WAVEFORM_NUMBER (85) + +#define RAMPLAYBACK_START_RAM_ADDRESS (0x0000) + +typedef enum +{ + REV_A, + REV_B, + REV_C +} Revision; + +typedef struct +{ + uint16_t sequencerCmd[MAXIMUM_WAVEFORM_ID_SEQUENCER]; +} Synthesizer; + +typedef struct +{ + const Gpio* gpio; + BOSEventCb cb; + void* ctx; + Bos0614GpioMode mode; +} ChannelCtx; + +typedef struct +{ + Revision rev; + bool isInitiated; + uint8_t txBuffer[MAXIMUM_DATA_LENGTH * sizeof(uint16_t)]; // Max size in words * 2 bytes + ChipId chipId; + uint8_t currentChannelMask; + const I2c* i2c; + const Spi* spi; + ChannelCtx channel[BOS0614_NBR_OF_CHANNEL]; + bool gpoSignalingAvailable; + uint8_t outputChanForWaveformId[MAXIMUM_WAVEFORM_NUMBER]; + HapticDriver hDriver; + Synthesizer synth; + Bos0614Registers reg; +} Context; + +typedef enum +{ + WSFBank_Ram = 0x1, + WSFBank_Sequencer = 0x2, + WSFBank_SequencerStartStop = 0x012, + WSFBank_RamPlayback = 0x013, + WSFBank_BurstRamWrite = 0x014 +} WSFBank; + +static const HapticDriver bos0614Driver; +static Context driverInstances[BOS0614_NBR_OF_INSTANCE]; + +/** + * Private Section + */ +static void initiateDriver(Context *ctx); + +static bool checkBos0614Revision(Context* ctx); + +static bool setDefaultConfig(Context* ctx); + +static bool writeI2cReg(Context *ctx, Bos0614Register *reg) +{ + bool res = false; + + uint8_t txData[WRITE_REG_LENGTH]; + txData[I2C_ADDRESS_INDEX] = reg->generic.addr; + htoBe16(reg->generic.value, &txData[I2C_ADDRESS_REG_DATA_INDEX]); + + res = ctx->i2c->write(ctx->i2c, BOS0614_I2C_ADDRESS, txData, sizeof(txData)) == ARM_DRIVER_OK; + + return res; +} + +static bool writeSpiReg(Context *ctx, Bos0614Register *reg, uint16_t *rxValue) +{ + bool res = false; + uint16_t dataIn[sizeof(reg->all)]; + uint16_t data[sizeof(reg->all)]; + data[0] = reg->generic.addr; + data[1] = reg->generic.value; + + res = ctx->spi->transfer(ctx->spi, data, dataIn, sizeof(data)) == ARM_DRIVER_OK; + + if (res == true && rxValue != NULL) + { + *rxValue = dataIn[1]; + } + + return res; +} + +static bool writeReg(Context *ctx, Bos0614Register *reg) +{ + bool res = false; + + if (ctx->spi != NULL) + { + res = writeSpiReg(ctx, reg, NULL); + } + else if (ctx->i2c != NULL) + { + res = writeI2cReg(ctx, reg); + } + + return res; +} + +static bool resetSoftware(Context *ctx) +{ + bool res; + + pr_debug("enter\n"); + ctx->reg.common.config0614.bit.rst = 0x1; + res = writeReg(ctx, &ctx->reg.common.config0614.reg); + ctx->reg.common.config0614.bit.rst = 0x0; + if (ctx->rev == REV_C) + { + res = res && writeReg(ctx, &ctx->reg.common.config0614.reg); + res = res && writeReg(ctx, &ctx->reg.common.config0614.reg); + } + + return res; +} + +static Context *getNewInstance() +{ + int index; + Context *ctx = NULL; + + for (index = 0; index < DATA_ARRAY_LENGTH(driverInstances); index++) + { + if (driverInstances[index].isInitiated == false) + { + ctx = &driverInstances[index]; + ctx->isInitiated = true; + break; + } + } + + return ctx; +} + +static Context* getContext(HapticDriver* driver) +{ + Context* ctx = NULL; + + if (driver != NULL) + { + size_t index; + + for (index = 0; index < DATA_ARRAY_LENGTH(driverInstances); index++) + { + if (&(driverInstances[index].hDriver) == driver && driverInstances[index].isInitiated) + { + ctx = &driverInstances[index]; + break; + } + } + } + + return ctx; +} + +static Bos0614Register* getRegister(Context* ctx, uint8_t addr) +{ + Bos0614Register *reg = NULL; + + switch (addr) + { + case ADDRESS_BOS0614_REFERENCE_REG: + reg = &ctx->reg.common.reference0614.reg; + break; + case ADDRESS_BOS0614_IC_STATUS_REG: + reg = &ctx->reg.common.icStatus0614.reg; + break; + case ADDRESS_BOS0614_READ_REG : + reg = &ctx->reg.common.read0614.reg; + break; + case ADDRESS_BOS0614_GPIOX_REG: + reg = &ctx->reg.common.gpiox0614.reg; + break; + case ADDRESS_BOS0614_TC_REG: + reg = &ctx->reg.common.tc0614.reg; + break; + case ADDRESS_BOS0614_CONFIG_REG: + reg = &ctx->reg.common.config0614.reg; + break; + case ADDRESS_BOS0614_SENSECONFIG_REG: + reg = &ctx->reg.common.senseConfig0614.reg; + break; + case ADDRESS_BOS0614_SENSE0_REG: + reg = &ctx->reg.common.sense00614.reg; + break; + case ADDRESS_BOS0614_SENSE0P_REG: + reg = &ctx->reg.common.sense0P0614.reg; + break; + case ADDRESS_BOS0614_SENSE0R_REG: + reg = &ctx->reg.common.sense0R0614.reg; + break; + case ADDRESS_BOS0614_SENSE0S_REG: + reg = &ctx->reg.common.sense0S0614.reg; + break; + case ADDRESS_BOS0614_SENSE1_REG: + reg = &ctx->reg.common.sense10614.reg; + break; + case ADDRESS_BOS0614_SENSE1P_REG: + reg = &ctx->reg.common.sense1P0614.reg; + break; + case ADDRESS_BOS0614_SENSE1R_REG: + reg = &ctx->reg.common.sense1R0614.reg; + break; + case ADDRESS_BOS0614_SENSE1S_REG: + reg = &ctx->reg.common.sense1S0614.reg; + break; + case ADDRESS_BOS0614_SENSE2_REG: + reg = &ctx->reg.common.sense20614.reg; + break; + case ADDRESS_BOS0614_SENSE2P_REG: + reg = &ctx->reg.common.sense2P0614.reg; + break; + case ADDRESS_BOS0614_SENSE2R_REG: + reg = &ctx->reg.common.sense2R0614.reg; + break; + case ADDRESS_BOS0614_SENSE2S_REG: + reg = &ctx->reg.common.sense2S0614.reg; + break; + case ADDRESS_BOS0614_SENSE3_REG: + reg = &ctx->reg.common.sense30614.reg; + break; + case ADDRESS_BOS0614_SENSE3P_REG: + reg = &ctx->reg.common.sense3P0614.reg; + break; + case ADDRESS_BOS0614_SENSE3R_REG: + reg = &ctx->reg.common.sense3R0614.reg; + break; + case ADDRESS_BOS0614_SENSE3S_REG: + reg = &ctx->reg.common.sense3S0614.reg; + break; + case ADDRESS_BOS0614_SENSESTATUS_REG: + reg = &ctx->reg.common.senseStatus0614.reg; + break; + case ADDRESS_BOS0614_SENSEDATA0_REG: + reg = &ctx->reg.common.senseData00614.reg; + break; + case ADDRESS_BOS0614_SENSEDATA1_REG: + reg = &ctx->reg.common.senseData10614.reg; + break; + case ADDRESS_BOS0614_SENSEDATA2_REG: + reg = &ctx->reg.common.senseData20614.reg; + break; + case ADDRESS_BOS0614_SENSEDATA3_REG: + reg = &ctx->reg.common.senseData30614.reg; + break; + case ADDRESS_BOS0614_SENSERAW0_REG: + reg = &ctx->reg.common.senseRaw00614.reg; + break; + case ADDRESS_BOS0614_SENSERAW1_REG: + reg = &ctx->reg.common.senseRaw10614.reg; + break; + case ADDRESS_BOS0614_SENSERAW2_REG: + reg = &ctx->reg.common.senseRaw20614.reg; + break; + case ADDRESS_BOS0614_SENSERAW3_REG: + reg = &ctx->reg.common.senseRaw30614.reg; + break; + case ADDRESS_BOS0614_KPA_REG: + reg = &ctx->reg.common.kpa0614.reg; + break; + case ADDRESS_BOS0614_KP_KI_REG: + reg = &ctx->reg.common.kpKi0614.reg; + break; + case ADDRESS_BOS0614_DEADTIME_REG: + reg = &ctx->reg.common.deadTime0614.reg; + break; + case ADDRESS_BOS0614_PARCAP_REG: + reg = &ctx->reg.common.parcap0614.reg; + break; + case ADDRESS_BOS0614_SUP_RISE_REG: + reg = &ctx->reg.common.supRise0614.reg; + break; + case ADDRESS_BOS0614_TRIM_REG: + reg = &ctx->reg.common.trim0614.reg; + break; + case ADDRESS_BOS0614_CHIP_ID_REG: + reg = &ctx->reg.common.chipId0614.reg; + break; + case ADDRESS_BOS0614_VFEEDBACK_REG: + reg = &ctx->reg.common.vFeedback0614.reg; + break; + case ADDRESS_BOS0614_FIFO_STATE_REG: + reg = &ctx->reg.common.fifoState0614.reg; + break; + case ADDRESS_BOS0614_AUTO_STATE_REG: + reg = &ctx->reg.common.autoState0614.reg; + break; + case ADDRESS_BOS0614_BIST_REG: + reg = &ctx->reg.common.bist0614.reg; + break; + case ADDRESS_BOS0614_BISTRES_REG: + reg = &ctx->reg.common.bistRes0614.reg; + break; + case ADDRESS_BOS0614_DEBUG_REG: + reg = &ctx->reg.common.debug0614.reg; + break; + case ADDRESS_BOS0614_THRESH_REG: + reg = &ctx->reg.common.thresh0614.reg; + break; + case ADDRESS_BOS0614_REG38_REG: + reg = &ctx->reg.common.reg380614.reg; + break; + case ADDRESS_BOS0614B_SENSE_OFFSET_REG: + case ADDRESS_BOS0614C_SENSE_OFFSET_REG: + reg = &ctx->reg.common.senseOffset0614.reg; + break; + case ADDRESS_BOS0614_CALIB_DATA_REG: + reg = &ctx->reg.common.calibData0614.reg; + break; + case ADDRESS_BOS0614_REG3B_REG: + reg = &ctx->reg.common.reg3B0614.reg; + break; + case ADDRESS_BOS0614_REG3C_REG: + reg = &ctx->reg.common.reg3C0614.reg; + break; + case ADDRESS_BOS0614_REG3D_REG: + reg = &ctx->reg.common.reg3D0614.reg; + break; + case ADDRESS_BOS0614_REG3E_REG: + reg = &ctx->reg.common.reg3E0614.reg; + break; + case ADDRESS_BOS0614_REG3F_REG: + reg = &ctx->reg.common.reg3F0614.reg; + break; + case ADDRESS_BOS0614_REG40_REG: + reg = &ctx->reg.common.reg400614.reg; + break; + case ADDRESS_BOS0614_REG41_REG: + reg = &ctx->reg.common.reg410614.reg; + break; + case ADDRESS_BOS0614_REG42_REG: + reg = &ctx->reg.common.reg420614.reg; + break; + case ADDRESS_BOS0614_REG43_REG: + reg = &ctx->reg.common.reg430614.reg; + break; + case ADDRESS_BOS0614B_RAM_DATA_REG: + case ADDRESS_BOS0614C_RAM_DATA_REG: + reg = &ctx->reg.common.ramData0614.reg; + break; + case ADDRESS_BOS0614_REG45_REG: + reg = &ctx->reg.common.reg450614.reg; + break; + case ADDRESS_BOS0614_REG46_REG: + reg = &ctx->reg.common.reg460614.reg; + break; + case ADDRESS_BOS0614_REG47_REG: + reg = &ctx->reg.common.reg470614.reg; + break; + default: + break; + } + + return reg; +} + +static bool readI2cRegister(Context *ctx, Bos0614Register *reg) +{ + bool res; + uint8_t rxData[REG_VALUE_LENGTH] = {0}; + + ctx->reg.common.read0614.bit.bc = reg->generic.addr; + res = writeReg(ctx, &ctx->reg.common.read0614.reg); + + res = res && ctx->i2c->read(ctx->i2c, BOS0614_I2C_ADDRESS, rxData, REG_VALUE_LENGTH) == ARM_DRIVER_OK; + + if (res) + { + reg->generic.value = beToH16(rxData); + } + + return res; +} + +static bool readSpiRegister(Context *ctx, Bos0614Register *reg) +{ + bool res = false; + + ctx->reg.common.read0614.bit.bc = reg->generic.addr; + + if (writeSpiReg(ctx, &ctx->reg.common.read0614.reg, NULL) && + writeSpiReg(ctx, &ctx->reg.common.read0614.reg, ®->generic.value)) + { + res = true; + } + + return res; +} + +static bool readRegister(Context *ctx, Bos0614Register *reg) +{ + bool res = false; + + if (ctx->i2c != NULL) + { + res = readI2cRegister(ctx, reg); + } + else if (ctx->spi != NULL) + { + res = readSpiRegister(ctx, reg); + } + + return res; +} + +static bool burstWriteRam(Context *driver, WSFBank bank, uint16_t address, uint16_t *data, size_t length) +{ + int i; + bool res = false; + if (length < MAXIMUM_DATA_LENGTH) + { + memset(driver->txBuffer, 0, sizeof(driver->txBuffer)); + driver->txBuffer[I2C_ADDRESS_INDEX] = ADDRESS_BOS0614_REFERENCE_REG; // [0] Main register map address + htoBe16(bank, &driver->txBuffer[BURST_WRITE_RAM_BANK_INDEX]); // [1-2] WFS bank + htoBe16(address, &driver->txBuffer[BURST_WRITE_RAM_ADDRESS_INDEX]); // [3-4] Burst start address + htoBe16(length, &driver->txBuffer[BURST_WRITE_RAM_LENGTH_INDEX]); // [5-6] Burst data length + for (i = 0; i < length; i++) // [7..] RAM Data to be forwarded to WFS + { + htoBe16(data[i], &driver->txBuffer[(i * 2) + BURST_WRITE_RAM_DATA_INDEX]); + } + + if (driver->i2c != NULL) + { + res = driver->i2c->write(driver->i2c, BOS0614_I2C_ADDRESS, driver->txBuffer, + BURST_WRITE_RAM_DATA_INDEX + (length * 2)) == ARM_DRIVER_OK; + } + else if (driver->spi != NULL) + { + res = driver->spi->send(driver->spi, driver->txBuffer, BURST_WRITE_RAM_DATA_INDEX + (length * 2)) == + ARM_DRIVER_OK; + } + } + + return res; +} +#if 0 +static void hexDump(char* message, uint8_t *data, size_t length) +{ + size_t bufferLength = length * 5 + 5; + char *buf = kzalloc(bufferLength, GFP_KERNEL); + + if (buf != NULL) + { + size_t index; + char *ptr = buf; + char *end = buf + bufferLength; + + uint8_t *_data = (uint8_t *) data; + + for (index = 0; index < length && ptr < end; index++) + { + ptr += sprintf(ptr, index < (length - 1) ? "0x%02x " : "0x%02x", _data[index]); + } + + pr_debug("%s [%s]\n", message, buf); + + kfree(buf); + } + +} +#endif +static bool writeRam(Context *driver, WSFBank bank, uint16_t *data, size_t length) +{ + int i; + bool res = false; +#if 0 + char buffer[64]; + snprintf(buffer, sizeof(buffer) - 1, "Bank: %d, length=%zu", bank, length); + hexDump(buffer, (uint8_t *)data, length * 2); +#endif + memset(driver->txBuffer, 0, sizeof(driver->txBuffer)); + driver->txBuffer[I2C_ADDRESS_INDEX] = ADDRESS_BOS0614_REFERENCE_REG; // [0] Main register map address + htoBe16(bank, &driver->txBuffer[WRITE_RAM_BANK_INDEX]); // [1-2] WFS bank address + for (i = 0; i < length; i += 2) // [3..] RAM Data to be forwarded to WFS + { + htoBe16(*(data++), &driver->txBuffer[i + WRITE_RAM_DATA_INDEX]); + } + + if (driver->i2c != NULL) + { + res = driver->i2c->write(driver->i2c, BOS0614_I2C_ADDRESS, driver->txBuffer, WRITE_RAM_DATA_INDEX + length) == + ARM_DRIVER_OK; + } + else if (driver->spi != NULL) + { + res = driver->spi->send(driver->spi, driver->txBuffer, WRITE_RAM_DATA_INDEX + length) == ARM_DRIVER_OK; + } + + return res; +} + +#define RAM_WAVEFORM_METADATA_ADDR (0x0000) +#define RAM_SLICE_WAVEFORM_ADDR (0x0100) + +#define SLICE_AMPLITUDE_INDEX (1) +#define SLICE_FREQ_CYCLE_INDEX (2) +#define SLICE_SHAPE_INDEX (3) +#define SLICE_LENGTH (3) +#define SLICE_CYCLE_SHIFT (8) +#define SLICE_NO_SHAPE (0) + +#define Hz_In_MilliHz (1000) +#define VOLT_TO_MILLI_VOLT (1000) + +static bool setSlice(Context *driver, SynthSlice const *slice, + SliceShape shape, uint8_t outputChannel) +{ + uint16_t buffer[SLICE_LENGTH + RAM_ADDRESS_LENGTH]; + uint32_t amplitude_computed = + (slice->mVAmp * AMPLITUDE_MAX_VALUE) / (AMPLITUDE_MAX_VALUE_VOLT * VOLT_TO_MILLI_VOLT); + uint8_t channel_computed = outputChannel; + uint16_t setShape; + + uint32_t freq_computed_den = FREQUENCY_RESOLUTION_HZ * Hz_In_MilliHz; + uint16_t freq = (uint16_t) (slice->mHzFreq / freq_computed_den); + + + buffer[RAM_ADDRESS_INDEX] = RAM_SLICE_WAVEFORM_ADDR + (slice->sliceId * SLICE_LENGTH); + buffer[SLICE_AMPLITUDE_INDEX] = (uint16_t) ((channel_computed << 12) | amplitude_computed); + buffer[SLICE_FREQ_CYCLE_INDEX] = (uint16_t) freq; + buffer[SLICE_FREQ_CYCLE_INDEX] |= (0xFF & slice->cycle) << SLICE_CYCLE_SHIFT; + + switch(shape) { + case SHAPE_SAW: + setShape = 0x0020; /* 64ms shapeUp only */ + break; + case SHAPE_TRIANGLE: + setShape = 0x0022; /* 64ms both ways */ + break; + case SHAPE_SLOW_RISE: + setShape = 0x0042; /* 128ms shapeUp and 64ms shapeDn */ + break; + case SHAPE_SLOW_DROP: + setShape = 0x0024; /* 64ms shapeUp and 128ms shapeDn */ + break; + default: + setShape = SLICE_NO_SHAPE; + break; + } + buffer[SLICE_SHAPE_INDEX] = setShape; + pr_debug("Shape = 0x%04x\n", setShape); + + return writeRam(driver, WSFBank_Ram, buffer, sizeof(buffer)); +} + +static bool setMode(Context *driver, Bos0614Mode mode) +{ + driver->reg.common.config0614.bit.ram = mode; + + return writeReg(driver, &driver->reg.common.config0614.reg); +} + +static bool setSamplingRate(Context *ctx, uint32_t samplingRateKsps) +{ + bool res = false; + bool found = true; + Bos0614SamplingRate rate; + + switch (samplingRateKsps) + { + case 1024: + rate = Bos0614SamplingRate_1024Ksps; + break; + case 512: + rate = Bos0614SamplingRate_512Ksps; + break; + case 256: + rate = Bos0614SamplingRate_256Ksps; + break; + case 128: + rate = Bos0614SamplingRate_128Ksps; + break; + case 64: + rate = Bos0614SamplingRate_64Ksps; + break; + case 32: + rate = Bos0614SamplingRate_32Ksps; + break; + case 16: + rate = Bos0614SamplingRate_16Ksps; + break; + case 8: + rate = Bos0614SamplingRate_8Ksps; + break; + default: + found = false; + } + + if (found) + { + ctx->reg.common.config0614.bit.play = rate; + res = writeReg(ctx, &ctx->reg.common.config0614.reg); + } + + return res; +} + +static bool activateChannel(Context *ctx, uint8_t channel) +{ + bool res = false; + if (channel < BOS0614_NBR_OF_CHANNEL) + { + ctx->reg.common.reference0614.bit.fifo = 0; + ctx->reg.common.reference0614.bit.channel = 0x1 << channel; + res = true; + } + + return res; +} + +static bool validateIcWorking(Context *ctx) +{ + bool res = false; + + ctx->reg.common.config0614.bit.play = Bos0614SamplingRate_512Ksps; + + res = writeReg(ctx, &ctx->reg.common.config0614.reg); + res = res && readRegister(ctx, &ctx->reg.common.config0614.reg); + res = res && ctx->reg.common.config0614.bit.play == Bos0614SamplingRate_512Ksps; + + pr_debug("ic is%s working!!!\n", res ? "" : " NOT"); + + return res; +} + +static BOSEvent getEventFromGpioState(Bos0614GpioMode mode, GPIOIsr state) +{ + BOSEvent event = BOS_EVENT_NO_EVENT; + + switch (mode) + { + case Bos0614GPOCtrl_WaveformFifoDone: + if (state == GPIOISR_Falling) + { + event = BOS_EVENT_WAVEFORM_DONE; + } + break; + case Bos0614GPOCtrl_ButtonState: + if (state == GPIOISR_Rising) + { + event = BOS_EVENT_BUTTON_RELEASE; + } + else if (state == GPIOISR_Falling) + { + event = BOS_EVENT_BUTTON_PRESS; + } + break; + case Bos0614GPOCtrl_RequestPlay: + if (state == GPIOISR_Rising) + { + event = BOS_EVENT_NO_REQUEST_TO_PLAY; + } + else if (state == GPIOISR_Falling) + { + event = BOS_EVENT_REQUEST_TO_PLAY; + } + break; + default: + event = BOS_EVENT_NO_EVENT; + } + + return event; +} + +static bool lookupGpioCtl(GPOCtrl ctrl, Bos0614GpioMode *bos0614Ctrl) +{ + bool res = false; + + switch (ctrl) + { + case GPO_WAVEFORM_FIFO_DONE: + *bos0614Ctrl = Bos0614GPOCtrl_WaveformFifoDone; + res = true; + break; + case GPO_SENSE_TRIGGER: + *bos0614Ctrl = Bos0614GPOCtrl_SenseTrigger; + res = true; + break; + case GPO_BUTTON_STATE: + *bos0614Ctrl = Bos0614GPOCtrl_ButtonState; + res = true; + break; + default: + res = false; + } + + return res; +} + +static bool isGpoSignalingAvailable(Context *ctx) +{ + size_t index; + bool res = false; + + for (index = 0; index < DATA_ARRAY_LENGTH(ctx->channel); index++) + { + if (ctx->channel[index].gpio != NULL) + { + res = true; + break; + } + } + + return res; +} + +static HapticDriver *genericDriverInit(Context *ctx) +{ + HapticDriver *driver = NULL; + + initiateDriver(ctx); + + if (resetSoftware(ctx) && validateIcWorking(ctx) && setDefaultConfig(ctx)) + { + ctx->gpoSignalingAvailable = isGpoSignalingAvailable(ctx); + driver = &ctx->hDriver; + } + else + { + ctx->isInitiated = false; + } + + return driver; +} + +static bool getSensingAutplayReg(Context* ctx, uint8_t channel, SenseBits0614** senseConfBitField) +{ + bool res = false; + + switch (channel) + { + case 0: + *senseConfBitField = &ctx->reg.common.sense00614.bit; + res = true; + break; + case 1: + *senseConfBitField = &ctx->reg.common.sense10614.bit; + res = true; + break; + + case 2: + *senseConfBitField = &ctx->reg.common.sense20614.bit; + res = true; + break; + case 3: + *senseConfBitField = &ctx->reg.common.sense30614.bit; + res = true; + break; + default: + res = false; + } + + return res; +} + +static bool getSensingRegistersPerChannel(Context* ctx, uint8_t channel, SensingDirection direction, + SenseBits0614** senseConfBitField, SenseThreshold** senseThreshold, + SenseSBits0614** senseSlope) +{ + bool res = false; + + switch (channel) + { + case 0: + *senseConfBitField = &ctx->reg.common.sense00614.bit; + *senseSlope = &ctx->reg.common.sense0S0614.bit; + + if (direction == SENSING_DIRECTION_PRESS) + { + *senseThreshold = &ctx->reg.common.sense0P0614.bit; + } + else if (direction == SENSING_DIRECTION_RELEASE) + { + *senseThreshold = &ctx->reg.common.sense0R0614.bit; + } + + res = true; + break; + case 1: + *senseConfBitField = &ctx->reg.common.sense10614.bit; + *senseSlope = &ctx->reg.common.sense1S0614.bit; + + if (direction == SENSING_DIRECTION_PRESS) + { + *senseThreshold = &ctx->reg.common.sense1P0614.bit; + } + else if (direction == SENSING_DIRECTION_RELEASE) + { + *senseThreshold = &ctx->reg.common.sense1R0614.bit; + } + + res = true; + break; + + case 2: + *senseConfBitField = &ctx->reg.common.sense20614.bit; + *senseSlope = &ctx->reg.common.sense2S0614.bit; + + if (direction == SENSING_DIRECTION_PRESS) + { + *senseThreshold = &ctx->reg.common.sense2P0614.bit; + } + else if (direction == SENSING_DIRECTION_RELEASE) + { + *senseThreshold = &ctx->reg.common.sense2R0614.bit; + } + + res = true; + break; + case 3: + *senseConfBitField = &ctx->reg.common.sense30614.bit; + *senseSlope = &ctx->reg.common.sense3S0614.bit; + + if (direction == SENSING_DIRECTION_PRESS) + { + *senseThreshold = &ctx->reg.common.sense3P0614.bit; + } + else if (direction == SENSING_DIRECTION_RELEASE) + { + *senseThreshold = &ctx->reg.common.sense3R0614.bit; + } + + res = true; + break; + default: + res = false; + } + + return res; +} + +static bool getDebouncingConfig(uint16_t debounceUs, DebouncingTime *debouncingTime) +{ + bool res = false; + + if (debouncingTime != NULL) + { + if (debounceUs == 1) + { + *debouncingTime = DebouncingTime_1us; + res = true; + } + else if (debounceUs <= 100) + { + *debouncingTime = DebouncingTime_100us; + res = true; + } + else if (debounceUs <= 500) + { + *debouncingTime = DebouncingTime_500us; + res = true; + } + else if (debounceUs <= 1000) + { + *debouncingTime = DebouncingTime_1ms; + res = true; + } + else if (debounceUs <= 2000) + { + *debouncingTime = DebouncingTime_2ms; + res = true; + } + else if (debounceUs <= 4000) + { + *debouncingTime = DebouncingTime_4ms; + res = true; + } + else if (debounceUs <= 8000) + { + *debouncingTime = DebouncingTime_8ms; + res = true; + } + else if (debounceUs > 8000) + { + *debouncingTime = DebouncingTime_16ms; + res = true; + } + } + + return res; +} + +#define MILLI_VOLT_TO_MICRO_VOLT(_mV) (_mV*1000) + +static int16_t getThresholdFromMV(int16_t thresholdMv) +{ + int32_t tempMicroV = MILLI_VOLT_TO_MICRO_VOLT(thresholdMv); + int16_t threshold = (int16_t) (tempMicroV / (int32_t) (MILLI_VOLT_TO_MICRO_VOLT(BOS0614_THRESH_STEP_mV))); + + return (int16_t) (threshold); +} + +static int16_t getSlopeThresholdFromMv(int16_t thresholdMv) +{ + float tempMv = (float) thresholdMv; + tempMv = tempMv / BOS0614_SLOPE_THRESH_STEP_mV; + + return (int16_t) tempMv; +} + +static bool activateSensingForChannel(Context *ctx, uint8_t channel, bool activate) +{ + bool res = false; + + switch (channel) + { + case 0: + ctx->reg.common.senseConfig0614.bit.ch0 = activate; + res = true; + break; + case 1: + ctx->reg.common.senseConfig0614.bit.ch1 = activate; + res = true; + break; + case 2: + ctx->reg.common.senseConfig0614.bit.ch2 = activate; + res = true; + break; + case 3: + ctx->reg.common.senseConfig0614.bit.ch3 = activate; + res = true; + break; + default: + break; + } + + if (res) + { + res = writeReg(ctx, &ctx->reg.common.senseConfig0614.reg); + } + + return res; +} + +bool bos0614WriteRegister(HapticDriver *driver, Register *reg); + +static bool +setThresholdSensing(Context* ctx, SensingConfig config, SenseBits0614* senseBitField, SenseThreshold* senseThreshold) +{ + bool res = false; + + uint32_t threshold; + DebouncingTime debouncingValue = DebouncingTime_1us; + + res = twoComplement(getThresholdFromMV(config.thresholdMv), THRESHOLD_NBR_OF_BITS, &threshold); + res = res && getDebouncingConfig(config.debounceUs, &debouncingValue); + + senseThreshold->threshold = threshold; + senseThreshold->rep = debouncingValue; + + if (config.direction == SENSING_DIRECTION_PRESS) + { + senseBitField->t1 = BOS0614_ENABLE; + senseThreshold->ab = SensingThresholdMode_Above; + + res = true; + } + + if (config.direction == SENSING_DIRECTION_RELEASE) + { + senseBitField->t2 = BOS0614_ENABLE; + senseThreshold->ab = SensingThresholdMode_Below; + + res = true; + } + + res = res && bos0614WriteRegister(&ctx->hDriver, (Register*) senseBitField); + res = res && bos0614WriteRegister(&ctx->hDriver, (Register*) senseThreshold); + + return res; +} + +static bool disableThresholdSensing(Context* ctx, SenseBits0614* senseBitField) +{ + senseBitField->t1 = BOS0614_DISABLE; + senseBitField->t2 = BOS0614_DISABLE; + + return bos0614WriteRegister(&ctx->hDriver, (Register*) senseBitField); +} + +static bool +setSlopeSensing(Context* ctx, SensingConfig config, SenseBits0614* senseBitField, SenseSBits0614* senseSlope) +{ + uint32_t slope; + bool res = twoComplement(getSlopeThresholdFromMv(config.thresholdMv), SLOPE_THRESHOLD_NBR_OF_BITS, &slope); + + if (config.direction == SENSING_DIRECTION_PRESS) + { + senseBitField->s1 = BOS0614_ENABLE; + senseSlope->slope1 = slope; + senseSlope->abs1 = SensingThresholdMode_Above; + } + + if (config.direction == SENSING_DIRECTION_RELEASE) + { + senseBitField->s2 = BOS0614_ENABLE; + senseSlope->slope2 = slope; + senseSlope->abs2 = SensingThresholdMode_Below; + } + + res = res && bos0614WriteRegister(&ctx->hDriver, (Register*) senseSlope); + res = res && bos0614WriteRegister(&ctx->hDriver, (Register*) senseBitField); + + return res; +} + +static bool disableSlopeSensing(Context* ctx, SenseBits0614* senseBitField) +{ + senseBitField->s1 = BOS0614_DISABLE; + senseBitField->s2 = BOS0614_DISABLE; + + return bos0614WriteRegister(&ctx->hDriver, (Register*) senseBitField); +} + +static size_t pushErrorInQueue(BosError* errors, size_t curr, size_t maxLength, BosError error) +{ + size_t length = curr; + + if (curr < maxLength) + { + errors[length++] = error; + } + + return length; +} + +/** + * Public Section + */ +HapticDriver* bos0614DriverSpiInit(const Spi* spi, const Gpio* gpioA, const Gpio* gpioD) +{ + HapticDriver* driver = NULL; + Context* ctx = getNewInstance(); + + if (spi != NULL && gpioA != NULL && gpioD != NULL && ctx != NULL) + { + ctx->spi = spi; + ctx->channel[0].gpio = gpioA; + ctx->channel[3].gpio = gpioD; + + driver = genericDriverInit(ctx); + } + + return driver; +} + +HapticDriver *bos0614DriverI2cInit(Bos0614Resource resources) +{ + HapticDriver *driver = NULL; + Context *ctx = getNewInstance(); + + if (ctx != NULL && resources.i2c != NULL) + { + ctx->i2c = resources.i2c; + ctx->channel[0].gpio = resources.gpioA; + ctx->channel[1].gpio = resources.gpioB; + ctx->channel[2].gpio = resources.gpioC; + ctx->channel[3].gpio = resources.gpioD; + + driver = genericDriverInit(ctx); + } + else if (ctx != NULL) + { + ctx->isInitiated = false; + } + + return driver; +} + + +bool bos0614DriverUninit(HapticDriver* driver) +{ + bool res = false; + + if (driver != NULL) + { + Context* ctx = getContext(driver); + + if (ctx != NULL) + { + memset(ctx, 0, sizeof(Context)); + res = true; + } + } + + return res; +} + +ChipId bos0614DriverGetChipId(HapticDriver* driver) +{ + ChipId chipId = 0; + + if (driver != NULL) + { + Context *ctx = container_of(driver, Context, hDriver); + + chipId = ctx->chipId; + } + + return chipId; +} + +bool bos0614GetRegister(HapticDriver *driver, uint8_t addr, uint16_t *value) +{ + bool res = false; + + if (driver != NULL) + { + Bos0614Register *reg; + Context *ctx = container_of(driver, Context, hDriver); + + + if (addr >= ADDRESS_BOS0614C_SENSE_OFFSET_REG) + { + ctx->reg.common.debug0614.bit.access = 0x3A; + writeReg(ctx, &ctx->reg.common.debug0614.reg); + } + + reg = getRegister(ctx, addr); + if (reg != NULL) + { + if (readRegister(ctx, reg)) + { + *value = reg->generic.value; + res = true; + } + } + } + return res; +} + +bool bos0614GetRegisters(HapticDriver* ctx, const uint8_t* addrArray, uint16_t* valueArray, const uint8_t nbRegisters) +{ + bool res = true; + bool getRes; + uint32_t i; + + if (ctx == NULL) + { + return false; + } + + for (i = 0; i < nbRegisters; i++) + { + getRes = bos0614GetRegister(ctx, addrArray[i], &valueArray[i]); + + /* + * Because of the current implementation of the read registers command send by the GUI + * This function loop can not be interrupted if an invalid register is read and the read + * function is returning false. + * + * The following code is to be sure this function returns false is on the read registers failed. + */ + //FIXME This function shall return false if one read register failed. + res = res ? getRes : false; + } + + return res; +} + +bool bos0614SetRegister(HapticDriver* driver, const uint8_t addr, const uint16_t value) +{ + bool res = false; + + if (driver != NULL) + { + Bos0614Register *reg; + Context *ctx = container_of(driver, Context, hDriver); + + + if (addr >= ADDRESS_BOS0614_BIST_REG) + { + ctx->reg.common.debug0614.bit.access = 0x3A; + writeReg(ctx, &ctx->reg.common.debug0614.reg); + } + + reg = getRegister(ctx, addr); + if (reg != NULL) + { + reg->generic.value = value; + res = writeReg(ctx, reg); + } + } + + return res; +} + +bool +bos0614SetRegisters(HapticDriver* ctx, const uint8_t* addrArray, const uint16_t* valueArray, + const uint8_t nbRegisters) +{ + bool res = true; + int i; + + if (ctx == NULL) + { + return false; + } + + for (i = 0; res && i < nbRegisters; i++) + { + res = bos0614SetRegister(ctx, addrArray[i], valueArray[i]); + } + + return res; +} + +bool bos0614WriteRegister(HapticDriver* driver, Register* reg) +{ + bool res = false; + + if (driver != NULL && reg != NULL) + { + Context *ctx = container_of(driver, Context, hDriver); + res = writeReg(ctx, ®->register0614); + + } + + return res; +} + +bool bos0614ReadRegister(HapticDriver *driver, Register *reg) +{ + bool res = false; + + if (driver != NULL && reg != NULL) + { + Context *ctx = container_of(driver, Context, hDriver); + res = readRegister(ctx, ®->register0614); + } + + return res; +} + +bool bos0614CtrlOutput(HapticDriver *driver, bool activate) +{ + bool res = false; + + if (driver != NULL) + { + Context *ctx = container_of(driver, Context, hDriver); + + ctx->reg.common.config0614.bit.oe = activate ? 1 : 0; //Enable waveform playback + + res = writeReg(ctx, &ctx->reg.common.config0614.reg); + } + return res; +} + +bool +bos0614SetSlice(HapticDriver *driver, const SynthSlice *slice, SliceShape shape, const uint8_t outputChannel) +{ + bool res = false; + + if (driver != NULL && slice != NULL && slice->sliceId < MAXIMUM_NUMBER_OF_SLICE && + outputChannel < BOS0614_CHANNEL_MASK) + { + Context *ctx = container_of(driver, Context, hDriver); + + res = setMode(ctx, Bos0614Mode_RAM_Synthesis); + res = res && setSlice(ctx, slice, shape, outputChannel); + } + + return res; +} + +bool +bos0614SetWaveforms(HapticDriver *driver, WaveformId id, uint8_t startSliceId, size_t nbrOfSlices, uint16_t cycle, + uint8_t outputChannel) +{ + bool res = false; + + if (driver != NULL && id < MAXIMUM_WAVEFORM_NUMBER && nbrOfSlices < MAXIMUM_NUMBER_OF_SLICE && + outputChannel < BOS0614_CHANNEL_MASK) + { + Context* ctx = container_of(driver, Context, hDriver); + + uint16_t waveformAddr = RAM_WAVEFORM_METADATA_ADDR + (id * WAVEFORM_METADATA_LENGTH); + + uint16_t command[] = {waveformAddr, + RAM_SLICE_WAVEFORM_ADDR + (startSliceId * SLICE_LENGTH), + RAM_SLICE_WAVEFORM_ADDR + ((startSliceId + nbrOfSlices) * SLICE_LENGTH) - 1, + (uint16_t) cycle}; + + ctx->outputChanForWaveformId[id] = outputChannel; + + res = writeRam(ctx, WSFBank_Ram, command, sizeof(command)); + + ctx->synth.sequencerCmd[id] = (waveformAddr); + + res = res && writeRam(ctx, WSFBank_Sequencer, ctx->synth.sequencerCmd, sizeof(ctx->synth.sequencerCmd)); + } + + return res; +} + +bool bos0614SynthesizerPlay(HapticDriver *driver, WaveformId start, WaveformId stop) +{ + bool res = false; + + if (driver != NULL && start < MAXIMUM_WAVEFORM_ID_SEQUENCER && stop < MAXIMUM_WAVEFORM_ID_SEQUENCER) + { + uint16_t startStopAddr; + Context *ctx = container_of(driver, Context, hDriver); + + ctx->currentChannelMask = ctx->outputChanForWaveformId[start]; + + startStopAddr = (start << SEQUENCER_STOP_ADDRESS_SHIFT) | stop; + + res = writeRam(ctx, WSFBank_SequencerStartStop, &startStopAddr, sizeof(startStopAddr)); + } + + return res; +} + +static bool fixRamPlayback(HapticDriver* driver) +{ + bool res = false; + + if (driver != NULL) + { + Context* ctx = container_of(driver, Context, hDriver); + + uint16_t cmd[] = {RAMPLAYBACK_START_RAM_ADDRESS, RAMPLAYBACK_START_RAM_ADDRESS}; + + res = writeRam(ctx, WSFBank_RamPlayback, cmd, sizeof(cmd)); + } + + return res; +} + +#define RAMPLAYBACK_DATA_MASK (0xFFF) +#define RAMPLAYBACK_CHANNEL_OFFSET (12) + +bool bos0614SetRamPlaybackMode(HapticDriver *driver, uint32_t samplingRate, void *data, size_t length, + uint8_t channelMask) +{ + int index; + uint16_t *buffer; + bool res = false; + + if (driver != NULL && samplingRate <= MAXIMUM_SAMPLING_RATE && data != NULL && length <= MAXIMUM_DATA_LENGTH && + channelMask < BOS0614_CHANNEL_MASK) + { + Context *ctx = container_of(driver, Context, hDriver); + + res = setMode(ctx, Bos0614Mode_RAM_Playback); + res = res && setSamplingRate(ctx, samplingRate); + + //Configure the channel output + buffer = (uint16_t *) data; + for (index = 0; index < length; index++) + { + buffer[index] &= RAMPLAYBACK_DATA_MASK; + buffer[index] |= (channelMask << RAMPLAYBACK_CHANNEL_OFFSET); + } + + if (ctx->rev == REV_C) + { + res = res && fixRamPlayback(driver); + } + + res = res && burstWriteRam(ctx, WSFBank_BurstRamWrite, RAMPLAYBACK_START_RAM_ADDRESS, data, length); + ctx->currentChannelMask = channelMask; + } + + return res; +} + +bool bos0614SetFifoMode(HapticDriver *driver, uint32_t samplingRate, uint8_t outputChannel) +{ + bool res = false; + + if (driver != NULL && samplingRate <= MAXIMUM_SAMPLING_RATE && outputChannel < BOS0614_NBR_OF_CHANNEL) + { + Context *ctx = container_of(driver, Context, hDriver); + + res = setMode(ctx, Bos0614Mode_FIFO); + res = res && setSamplingRate(ctx, samplingRate); + res = res && activateChannel(ctx, outputChannel); + } + + return res; +} + +bool bos0614GetFifoSpace(HapticDriver *driver, uint16_t *availableSpace) +{ + bool res = false; + + if (driver != NULL && availableSpace != NULL) + { + Context *ctx = container_of(driver, Context, hDriver); + + uint16_t fifoReg = 0; + + res = ctx->hDriver.getRegister(&ctx->hDriver, ADDRESS_BOS0614_FIFO_STATE_REG, &fifoReg); + + if (res) + { + if (ctx->reg.common.fifoState0614.bit.empty) + *availableSpace = MAXIMUM_DATA_LENGTH; + else if (ctx->reg.common.fifoState0614.bit.full) + *availableSpace = 0; + else + *availableSpace = ctx->reg.common.fifoState0614.bit.fifoSpace; + } + } + return res; +} + +uint16_t bos0614GetMaxFifoSpace() +{ + return MAXIMUM_DATA_LENGTH; +} + +static bool writeDataInFifo(Context *ctx, void *data, size_t length) +{ + int index; + bool res = false; + uint16_t *dataArray = (uint16_t *) data; + + for (index = 0; index < length; index++) + { + res = true; + ctx->reg.common.reference0614.bit.fifo = dataArray[index]; + + writeReg(ctx, &ctx->reg.common.reference0614.reg); + } + + return res; +} + +bool bos0614WriteFifo(HapticDriver *ctx, void *data, size_t length) +{ + bool res = false; + + if (ctx != NULL && data != NULL && length > 0) + { + Context *driver = container_of(ctx, Context, hDriver); + res = writeDataInFifo(driver, data, length); + } + + return res; +} + +bool bos0614PlayRamPlayback(HapticDriver *ctx, size_t length) +{ + bool res = false; + + if (ctx != NULL) + { + Context *driver = container_of(ctx, Context, hDriver); + + uint16_t cmd[] = {RAMPLAYBACK_START_RAM_ADDRESS, RAMPLAYBACK_START_RAM_ADDRESS + (uint16_t) length - 1}; + + res = writeRam(driver, WSFBank_RamPlayback, cmd, sizeof(cmd)); + } + + return res; +} + +static void gpoIsr(const Gpio *gpio, void *context, GPIOIsr isrEvent) +{ + if (context != NULL) + { + Context *ctx = (Context *) context; + uint32_t index; + + for (index = 0; index < DATA_ARRAY_LENGTH(ctx->channel); index++) + { + if (ctx->channel[index].gpio != NULL && + ctx->channel[index].gpio == gpio && + ctx->channel[index].cb != NULL) + { + BOSEvent event = getEventFromGpioState(ctx->channel[index].mode, isrEvent); + + if (event != BOS_EVENT_NO_EVENT) + { + ctx->channel[index].cb(&ctx->hDriver, (ChannelId) index, event, ctx->channel[index].ctx); + } + break; + } + } + } +} + +bool bos0614ConfigGPO(HapticDriver *driver, uint8_t channel, GPOCtrl state) +{ + bool res = false; + + if (driver != NULL) + { + Context *ctx = container_of(driver, Context, hDriver); + Bos0614GpioMode bos0614Mode; + + if (lookupGpioCtl(state, &bos0614Mode)) + { + ctx->channel[channel].mode = bos0614Mode; + + switch (channel) + { + case 0: + ctx->reg.common.gpiox0614.bit.gpio0 = bos0614Mode; + res = true; + break; + case 1: + ctx->reg.common.gpiox0614.bit.gpio1 = bos0614Mode; + res = true; + break; + case 2: + ctx->reg.common.gpiox0614.bit.gpio2 = bos0614Mode; + res = true; + break; + case 3: + ctx->reg.common.gpiox0614.bit.gpio3 = bos0614Mode; + res = true; + break; + default: + break; + } + + res = res && writeReg(ctx, &ctx->reg.common.gpiox0614.reg); + + } + } + + return res; +} + +bool bos0614RegisterOnEvents(HapticDriver *driver, ChannelId channelId, BOSEventCb cb, void *context) +{ + bool res = false; + + if (driver != NULL && channelId < BOS0614_NBR_OF_CHANNEL && cb != NULL) + { + Context *ctx = container_of(driver, Context, hDriver); + const Gpio *gpio = ctx->channel[channelId].gpio; + + GPIOIsr isrMode = GPIOISR_Both; + + if (gpio != NULL) + { + ctx->channel[channelId].cb = cb; + ctx->channel[channelId].ctx = context; + res = gpio->registerIsr(gpio, isrMode, gpoIsr, ctx); + } + } + + return res; +} + +bool bos0614UnregisterEvents(HapticDriver *driver, ChannelId channelId) +{ + bool res = false; + + if (driver != NULL && channelId < BOS0614_NBR_OF_CHANNEL) + { + Context *ctx = container_of(driver, Context, hDriver); + ChannelCtx *channelCtx = &ctx->channel[channelId]; + + if (channelCtx->gpio != NULL) + { + channelCtx->cb = NULL; + channelCtx->ctx = NULL; + res = channelCtx->gpio->unregisterIsr(channelCtx->gpio); + } + } + + return res; +} + +bool bos0614SoftwareReset(HapticDriver *driver) +{ + bool res = false; + + if (driver != NULL) + { + Context *ctx = container_of(driver, Context, hDriver); + resetSoftware(ctx); + } + + return res; +} + +bool bos0614DeepSleep(HapticDriver *ctx, bool enable) +{ + bool res = false; + + if (ctx != NULL) + { + Context *driver = container_of(ctx, Context, hDriver); + uint16_t dummyValue; + + res = bos0614GetRegister(ctx, ADDRESS_BOS0614_CONFIG_REG, &dummyValue); + driver->reg.common.config0614.bit.ds = enable ? 0x1 : 0x0; + res = res && bos0614SetRegister(ctx, ADDRESS_BOS0614_CONFIG_REG, driver->reg.common.config0614.reg.all); + } + + return res; +} + +bool bos0614Synch(HapticDriver *driver, bool activate) +{ + (void) driver; + (void) activate; + return false; +} + +size_t bos0614GetError(HapticDriver *driver, BosError *errors, size_t maxLength) +{ + size_t length = 0; + + if (driver != NULL && errors != NULL) + { + Context *ctx = container_of(driver, Context, hDriver); + uint16_t dummyData; + + if (bos0614GetRegister(driver, ADDRESS_BOS0614_IC_STATUS_REG, &dummyData)) + { + if (ctx->reg.common.icStatus0614.bit.sc != 0) + { + length = pushErrorInQueue(errors, length, maxLength, BOS_ERROR_SHORT_CIRCUIT); + } + + if (ctx->reg.common.icStatus0614.bit.uvlo != 0) + { + length = pushErrorInQueue(errors, length, maxLength, BOS_ERROR_UVLO); + } + + if (ctx->reg.common.icStatus0614.bit.idac != 0) + { + length = pushErrorInQueue(errors, length, maxLength, BOS_ERROR_IDAC); + } + + if (ctx->reg.common.icStatus0614.bit.maxPower != 0) + { + length = pushErrorInQueue(errors, length, maxLength, BOS_ERROR_MAX_POWER); + } + + if (ctx->reg.common.icStatus0614.bit.ovt != 0) + { + length = pushErrorInQueue(errors, length, maxLength, BOS_ERROR_OVT); + } + + if (ctx->reg.common.icStatus0614.bit.ovv != 0) + { + length = pushErrorInQueue(errors, length, maxLength, BOS_ERROR_OVV); + } + } + } + + return length; +} + +size_t bos0614NbrOfRegister() +{ + return (BOS0614_MAXIMUM_REG_ADDR + 1); +} + +bool bos0614ReferencingFromVolt(HapticDriver* ctx, int16_t* dataIn, size_t length, int16_t* dataOut) +{ + int index; + bool res = false; + + if (ctx != NULL) + { + for (index = 0; index < length; index++) + { + float num = (float) dataIn[index] * AMPLITUDE_MAX_VALUE; + float den = FB_R_V_REF; + float res = num / den; + + dataOut[index] = (int16_t) (res); + } + } + + return res; +} + + +bool bos0614ReferencingFromRelativeInt16(HapticDriver* driver, int16_t* dataIn, size_t length, + int16_t vMinVolt, int16_t vMaxVolt, int16_t* dataOut) +{ + bool res = false; + + if (driver != NULL) + { + size_t index; + + bos0614ReferencingFromVolt(driver, &vMinVolt, 1, &vMinVolt); + bos0614ReferencingFromVolt(driver, &vMaxVolt, 1, &vMaxVolt); + + for (index = 0; index < length; index++) + { + //Should we divide by UINT16_MAX and use the roundf function + int16_t value = ((dataIn[index] - INT16_MIN) * FIFO_MAX_AMPLITUDE) >> 16; + if (value < vMinVolt) + { + value = vMinVolt; + } + else if (value > vMaxVolt) + { + value = vMaxVolt; + } + dataOut[index] = value; + } + res = true; + } + return res; +} + +GPIOState bos0614GetGpioState(HapticDriver* driver, uint8_t channel) +{ + return GPIOState_Invalid; +} + +Registers *bos0614GetShadowRegisters(HapticDriver *driver) +{ + Registers *registers = NULL; + + if (driver != NULL) + { + Context *ctx = container_of(driver, Context, hDriver); + registers = (Registers *) &ctx->reg; + } + + return registers; + +} + +bool bos0614EnableSensing(HapticDriver* driver, uint8_t channel) +{ + bool res = false; + + if (driver != NULL && channel < BOS0614_NBR_OF_CHANNEL) + { + Context *ctx = container_of(driver, Context, hDriver); + ChannelCtx *channelCtx = &ctx->channel[channel]; + + SenseBits0614 *senseBitField; + SenseThreshold *senseThreshold; + SenseSBits0614 *senseSlope; + + if (getSensingRegistersPerChannel(ctx, channel, SENSING_DIRECTION_PRESS, + &senseBitField, &senseThreshold, &senseSlope) && + channelCtx != NULL) + { + res = disableSlopeSensing(ctx, senseBitField); + res = res && disableThresholdSensing(ctx, senseBitField); + } + + res = res && bos0614WriteRegister(driver, (Register*) &ctx->reg.common.senseConfig0614.reg); + + res = res && activateSensingForChannel(ctx, channel, BOS0614_ENABLE); + } + + return res; +} + +#define STABILIZATION_TIME_MICRO_SECOND_PER_BIT_REV_C (3200) +#define MAXIMUM_STABILIZATION_TIME_REV_C_MS (103) + +bool getStabilizationTimeRevC(uint8_t stabilizationMs, uint16_t* value) +{ + bool res = false; + + if (value != NULL && stabilizationMs < MAXIMUM_STABILIZATION_TIME_REV_C_MS) + { + uint16_t _value = MILLISECOND_MICROSECOND(stabilizationMs); + *value = _value / STABILIZATION_TIME_MICRO_SECOND_PER_BIT_REV_C; + res = true; + } + + return res; +} + +bool bos0614ButtonSensing(HapticDriver* driver, uint8_t channel, SensingConfig config) +{ + bool res = false; + + if (driver != NULL && channel < BOS0614_NBR_OF_CHANNEL) + { + Context* ctx = container_of(driver, Context, hDriver); + ChannelCtx* channelCtx = &ctx->channel[channel]; + + SenseBits0614 *senseBitField; + SenseThreshold *senseThreshold; + SenseSBits0614 *senseSlope; + + if (getSensingRegistersPerChannel(ctx, channel, config.direction, + &senseBitField, &senseThreshold, &senseSlope) && + channelCtx != NULL) + { + res = true; + + switch (config.mode) + { + case SENSING_DETECTION_MODE_THRESHOLD: + res = res && setThresholdSensing(ctx, config, senseBitField, senseThreshold); + break; + case SENSING_DETECTION_MODE_SLOPE: + res = res && setSlopeSensing(ctx, config, senseBitField, senseSlope); + break; + } + + //Setup Stabilization + if (ctx->rev == REV_B) + { + ctx->reg.revB.tc0614.bit.pol = 0; + ctx->reg.revB.tc0614.bit.pc = 0x1; + ctx->reg.revB.tc0614.bit.tc = MAX_TC_VALUE; + ctx->reg.revB.senseConfig0614.bit.scomp = 1; + } + else if (ctx->rev == REV_C) + { + uint16_t stabilizationMs; + res = res && getStabilizationTimeRevC(config.stabilisationMs, &stabilizationMs); + + switch (config.direction) + { + case SENSING_DIRECTION_PRESS: + ctx->reg.revC.tc0614.bit.tcp = stabilizationMs; + break; + case SENSING_DIRECTION_RELEASE: + ctx->reg.revC.tc0614.bit.tcr = stabilizationMs; + break; + default: + res = false; + break; + } + + ctx->reg.revC.tc0614.bit.pol = 0; + ctx->reg.revC.tc0614.bit.pc = 0x1; + ctx->reg.revC.senseConfig0614.bit.scomp = 1; + } + + res = res && bos0614WriteRegister(driver, (Register*) &ctx->reg.common.tc0614.reg); + res = res && bos0614WriteRegister(driver, (Register*) &ctx->reg.common.senseConfig0614.reg); + + res = res && activateSensingForChannel(ctx, channel, BOS0614_ENABLE); + } + } + + return res; +} + +bool +bos0614SensingAutoPlayWave(HapticDriver *driver, uint8_t channel, WaveformId id, SensingDirection direction) +{ + bool res = false; + + if (driver != NULL && channel < BOS0614_NBR_OF_CHANNEL) // && id <= MAXIMUM_WAVEFORM_ID_FOR_AUTO_SENSING) + { + Context *ctx = container_of(driver, Context, hDriver); + + SenseBits0614 *senseBitField; + SenseThreshold *senseThreshold; + SenseSBits0614 *senseSlope; + + if (getSensingRegistersPerChannel(ctx, channel, direction, &senseBitField, &senseThreshold, &senseSlope)) + { + if (direction == SENSING_DIRECTION_PRESS) + { + senseBitField->wvp = id; + senseBitField->autop = BOS0614_ENABLE; + res = true; + } + else if (direction == SENSING_DIRECTION_RELEASE) + { + senseBitField->wvr = id; + senseBitField->autor = BOS0614_ENABLE; + res = true; + } + + res = res && bos0614WriteRegister(driver, (Register*) senseBitField); + } + } + + return res; +} + +bool bos0614SensingStop(HapticDriver* driver, uint8_t channel) +{ + bool res = false; + + if (driver != NULL && channel < BOS0614_NBR_OF_CHANNEL) + { + Context *ctx = container_of(driver, Context, hDriver); + + SenseBits0614 *senseBitField; + + if (getSensingAutplayReg(ctx, channel, &senseBitField)) + { + senseBitField->autop = BOS0614_DISABLE; + senseBitField->t1 = BOS0614_DISABLE; + senseBitField->s1 = BOS0614_DISABLE; + + senseBitField->autor = BOS0614_DISABLE; + senseBitField->t2 = BOS0614_DISABLE; + senseBitField->s2 = BOS0614_DISABLE; + + res = bos0614WriteRegister(driver, (Register*) senseBitField); + res = res && activateSensingForChannel(ctx, channel, BOS0614_DISABLE); + } + } + + return res; +} + +bool bos0614FeatureSupport(HapticDriver *driver, BosFeature feature) +{ + bool res = false; + + if (driver != NULL && feature < BOS_FEATURE_LENGTH) + { + Context *ctx = container_of(driver, Context, hDriver); + + switch (feature) + { + case BOS_FEATURE_GPO_SIGNALING: + if (ctx->gpoSignalingAvailable) + { + res = true; + } + break; + case BOS_FEATURE_FIFO: + case BOS_FEATURE_SYNTHESIZER: + case BOS_FEATURE_RAM_PLAYBACK: + case BOS_FEATURE_SENSING_WITH_AUTOMATIC_FEEDBACK: + case BOS_FEATURE_SUPPORT_BOTH_SENSING_POLARITY: + res = true; + break; + default: + res = false; + } + } + return res; +} + +bool bos0614SenseOutput(HapticDriver* driver, uint8_t channel, int32_t* data) +{ + bool res = false; + + if (driver != NULL && channel < BOS0614_NBR_OF_CHANNEL && data != NULL) + { + Context* ctx = container_of(driver, Context, hDriver); + + uint16_t tempData; + + res = ctx->hDriver.getRegister(&ctx->hDriver, ADDRESS_BOS0614_SENSEDATA0_REG + channel, &tempData); + + if (res && (int16_t) tempData < BOS0614_SENSEDATA_VALID_RANGE && + (int16_t) tempData >= -BOS0614_SENSEDATA_VALID_RANGE) + { + *data = (int32_t) ((int16_t) tempData * BOS0614_SENSEDATA_CONVERSION_uV); + } + } + + return res; +} + +/** + * Private Section + */ + +Bos0614Registers bos0614RegsRevB = { + .revB.reference0614.reg.generic.addr = ADDRESS_BOS0614_REFERENCE_REG, .revB.icStatus0614.reg.generic.addr = ADDRESS_BOS0614_IC_STATUS_REG, .revB.read0614.reg.generic.addr = ADDRESS_BOS0614_READ_REG, .revB.gpiox0614.reg.generic.addr = ADDRESS_BOS0614_GPIOX_REG, .revB.tc0614.reg.generic.addr = ADDRESS_BOS0614_TC_REG, .revB.config0614.reg.generic.addr = ADDRESS_BOS0614_CONFIG_REG, .revB.senseConfig0614.reg.generic.addr = ADDRESS_BOS0614_SENSECONFIG_REG, .revB.sense00614.reg.generic.addr = ADDRESS_BOS0614_SENSE0_REG, .revB.sense0P0614.reg.generic.addr = ADDRESS_BOS0614_SENSE0P_REG, .revB.sense0R0614.reg.generic.addr = ADDRESS_BOS0614_SENSE0R_REG, .revB.sense0S0614.reg.generic.addr = ADDRESS_BOS0614_SENSE0S_REG, .revB.sense10614.reg.generic.addr = ADDRESS_BOS0614_SENSE1_REG, .revB.sense1P0614.reg.generic.addr = ADDRESS_BOS0614_SENSE1P_REG, .revB.sense1R0614.reg.generic.addr = ADDRESS_BOS0614_SENSE1R_REG, .revB.sense1S0614.reg.generic.addr = ADDRESS_BOS0614_SENSE1S_REG, .revB.sense20614.reg.generic.addr = ADDRESS_BOS0614_SENSE2_REG, .revB.sense2P0614.reg.generic.addr = ADDRESS_BOS0614_SENSE2P_REG, .revB.sense2R0614.reg.generic.addr = ADDRESS_BOS0614_SENSE2R_REG, .revB.sense2S0614.reg.generic.addr = ADDRESS_BOS0614_SENSE2S_REG, .revB.sense30614.reg.generic.addr = ADDRESS_BOS0614_SENSE3_REG, .revB.sense3P0614.reg.generic.addr = ADDRESS_BOS0614_SENSE3P_REG, .revB.sense3R0614.reg.generic.addr = ADDRESS_BOS0614_SENSE3R_REG, .revB.sense3S0614.reg.generic.addr = ADDRESS_BOS0614_SENSE3S_REG, .revB.senseStatus0614.reg.generic.addr = ADDRESS_BOS0614_SENSESTATUS_REG, .revB.senseData00614.reg.generic.addr = ADDRESS_BOS0614_SENSEDATA0_REG, .revB.senseData10614.reg.generic.addr = ADDRESS_BOS0614_SENSEDATA1_REG, .revB.senseData20614.reg.generic.addr = ADDRESS_BOS0614_SENSEDATA2_REG, .revB.senseData30614.reg.generic.addr = ADDRESS_BOS0614_SENSEDATA3_REG, .revB.senseRaw00614.reg.generic.addr = ADDRESS_BOS0614_SENSERAW0_REG, .revB.senseRaw10614.reg.generic.addr = ADDRESS_BOS0614_SENSERAW1_REG, .revB.senseRaw20614.reg.generic.addr = ADDRESS_BOS0614_SENSERAW2_REG, .revB.senseRaw30614.reg.generic.addr = ADDRESS_BOS0614_SENSERAW3_REG, .revB.kpa0614.reg.generic.addr = ADDRESS_BOS0614_KPA_REG, .revB.kpKi0614.reg.generic.addr = ADDRESS_BOS0614_KP_KI_REG, .revB.deadTime0614.reg.generic.addr = ADDRESS_BOS0614_DEADTIME_REG, .revB.parcap0614.reg.generic.addr = ADDRESS_BOS0614_PARCAP_REG, .revB.supRise0614.reg.generic.addr = ADDRESS_BOS0614_SUP_RISE_REG, .revB.trim0614.reg.generic.addr = ADDRESS_BOS0614_TRIM_REG, .revB.chipId0614.reg.generic.addr = ADDRESS_BOS0614_CHIP_ID_REG, .revB.vFeedback0614.reg.generic.addr = ADDRESS_BOS0614_VFEEDBACK_REG, .revB.fifoState0614.reg.generic.addr = ADDRESS_BOS0614_FIFO_STATE_REG, .revB.autoState0614.reg.generic.addr = ADDRESS_BOS0614_AUTO_STATE_REG, .revB.bist0614.reg.generic.addr = ADDRESS_BOS0614_BIST_REG, .revB.bistRes0614.reg.generic.addr = ADDRESS_BOS0614_BISTRES_REG, .revB.debug0614.reg.generic.addr = ADDRESS_BOS0614_DEBUG_REG, .revB.thresh0614.reg.generic.addr = ADDRESS_BOS0614_THRESH_REG, .revB.reg380614.reg.generic.addr = ADDRESS_BOS0614_REG38_REG, .revB.senseOffset0614.reg.generic.addr = ADDRESS_BOS0614B_SENSE_OFFSET_REG, .revB.calibData0614.reg.generic.addr = ADDRESS_BOS0614_CALIB_DATA_REG, .revB.reg3B0614.reg.generic.addr = ADDRESS_BOS0614_REG3B_REG, .revB.reg3C0614.reg.generic.addr = ADDRESS_BOS0614_REG3C_REG, .revB.reg3D0614.reg.generic.addr = ADDRESS_BOS0614_REG3D_REG, .revB.reg3E0614.reg.generic.addr = ADDRESS_BOS0614_REG3E_REG, .revB.reg3F0614.reg.generic.addr = ADDRESS_BOS0614_REG3F_REG, .revB.reg400614.reg.generic.addr = ADDRESS_BOS0614_REG40_REG, .revB.reg410614.reg.generic.addr = ADDRESS_BOS0614_REG41_REG, .revB.reg420614.reg.generic.addr = ADDRESS_BOS0614_REG42_REG, .revB.reg430614.reg.generic.addr = ADDRESS_BOS0614_REG43_REG, .revB.ramData0614.reg.generic.addr = ADDRESS_BOS0614B_RAM_DATA_REG, .revB.reg450614.reg.generic.addr = ADDRESS_BOS0614_REG45_REG, .revB.reg460614.reg.generic.addr = ADDRESS_BOS0614_REG46_REG, .revB.reg470614.reg.generic.addr = ADDRESS_BOS0614_REG47_REG, +}; + +Bos0614Registers bos0614RegsRevC = { + .revC.reference0614.reg.generic.addr = ADDRESS_BOS0614_REFERENCE_REG, .revC.icStatus0614.reg.generic.addr = ADDRESS_BOS0614_IC_STATUS_REG, .revC.read0614.reg.generic.addr = ADDRESS_BOS0614_READ_REG, .revC.gpiox0614.reg.generic.addr = ADDRESS_BOS0614_GPIOX_REG, .revC.tc0614.reg.generic.addr = ADDRESS_BOS0614_TC_REG, .revC.config0614.reg.generic.addr = ADDRESS_BOS0614_CONFIG_REG, .revC.senseConfig0614.reg.generic.addr = ADDRESS_BOS0614_SENSECONFIG_REG, .revC.sense00614.reg.generic.addr = ADDRESS_BOS0614_SENSE0_REG, .revC.sense0P0614.reg.generic.addr = ADDRESS_BOS0614_SENSE0P_REG, .revC.sense0R0614.reg.generic.addr = ADDRESS_BOS0614_SENSE0R_REG, .revC.sense0S0614.reg.generic.addr = ADDRESS_BOS0614_SENSE0S_REG, .revC.sense10614.reg.generic.addr = ADDRESS_BOS0614_SENSE1_REG, .revC.sense1P0614.reg.generic.addr = ADDRESS_BOS0614_SENSE1P_REG, .revC.sense1R0614.reg.generic.addr = ADDRESS_BOS0614_SENSE1R_REG, .revC.sense1S0614.reg.generic.addr = ADDRESS_BOS0614_SENSE1S_REG, .revC.sense20614.reg.generic.addr = ADDRESS_BOS0614_SENSE2_REG, .revC.sense2P0614.reg.generic.addr = ADDRESS_BOS0614_SENSE2P_REG, .revC.sense2R0614.reg.generic.addr = ADDRESS_BOS0614_SENSE2R_REG, .revC.sense2S0614.reg.generic.addr = ADDRESS_BOS0614_SENSE2S_REG, .revC.sense30614.reg.generic.addr = ADDRESS_BOS0614_SENSE3_REG, .revC.sense3P0614.reg.generic.addr = ADDRESS_BOS0614_SENSE3P_REG, .revC.sense3R0614.reg.generic.addr = ADDRESS_BOS0614_SENSE3R_REG, .revC.sense3S0614.reg.generic.addr = ADDRESS_BOS0614_SENSE3S_REG, .revC.senseStatus0614.reg.generic.addr = ADDRESS_BOS0614_SENSESTATUS_REG, .revC.senseData00614.reg.generic.addr = ADDRESS_BOS0614_SENSEDATA0_REG, .revC.senseData10614.reg.generic.addr = ADDRESS_BOS0614_SENSEDATA1_REG, .revC.senseData20614.reg.generic.addr = ADDRESS_BOS0614_SENSEDATA2_REG, .revC.senseData30614.reg.generic.addr = ADDRESS_BOS0614_SENSEDATA3_REG, .revC.senseRaw00614.reg.generic.addr = ADDRESS_BOS0614_SENSERAW0_REG, .revC.senseRaw10614.reg.generic.addr = ADDRESS_BOS0614_SENSERAW1_REG, .revC.senseRaw20614.reg.generic.addr = ADDRESS_BOS0614_SENSERAW2_REG, .revC.senseRaw30614.reg.generic.addr = ADDRESS_BOS0614_SENSERAW3_REG, .revC.kpa0614.reg.generic.addr = ADDRESS_BOS0614_KPA_REG, .revC.kpKi0614.reg.generic.addr = ADDRESS_BOS0614_KP_KI_REG, .revC.deadTime0614.reg.generic.addr = ADDRESS_BOS0614_DEADTIME_REG, .revC.parcap0614.reg.generic.addr = ADDRESS_BOS0614_PARCAP_REG, .revC.supRise0614.reg.generic.addr = ADDRESS_BOS0614_SUP_RISE_REG, .revC.trim0614.reg.generic.addr = ADDRESS_BOS0614_TRIM_REG, .revC.chipId0614.reg.generic.addr = ADDRESS_BOS0614_CHIP_ID_REG, .revC.vFeedback0614.reg.generic.addr = ADDRESS_BOS0614_VFEEDBACK_REG, .revC.fifoState0614.reg.generic.addr = ADDRESS_BOS0614_FIFO_STATE_REG, .revC.autoState0614.reg.generic.addr = ADDRESS_BOS0614_AUTO_STATE_REG, .revC.bist0614.reg.generic.addr = ADDRESS_BOS0614_BIST_REG, .revC.bistRes0614.reg.generic.addr = ADDRESS_BOS0614_BISTRES_REG, .revC.debug0614.reg.generic.addr = ADDRESS_BOS0614_DEBUG_REG, .revC.thresh0614.reg.generic.addr = ADDRESS_BOS0614_THRESH_REG, .revC.reg380614.reg.generic.addr = ADDRESS_BOS0614_REG38_REG, .revC.senseOffset0614.reg.generic.addr = ADDRESS_BOS0614C_SENSE_OFFSET_REG, .revC.calibData0614.reg.generic.addr = ADDRESS_BOS0614_CALIB_DATA_REG, .revC.reg3B0614.reg.generic.addr = ADDRESS_BOS0614_REG3B_REG, .revC.reg3C0614.reg.generic.addr = ADDRESS_BOS0614_REG3C_REG, .revC.reg3D0614.reg.generic.addr = ADDRESS_BOS0614_REG3D_REG, .revC.reg3E0614.reg.generic.addr = ADDRESS_BOS0614_REG3E_REG, .revC.reg3F0614.reg.generic.addr = ADDRESS_BOS0614_REG3F_REG, .revC.reg400614.reg.generic.addr = ADDRESS_BOS0614_REG40_REG, .revC.reg410614.reg.generic.addr = ADDRESS_BOS0614_REG41_REG, .revC.reg420614.reg.generic.addr = ADDRESS_BOS0614_REG42_REG, .revC.reg430614.reg.generic.addr = ADDRESS_BOS0614_REG43_REG, .revC.ramData0614.reg.generic.addr = ADDRESS_BOS0614C_RAM_DATA_REG, .revC.reg450614.reg.generic.addr = ADDRESS_BOS0614_REG45_REG, .revC.reg460614.reg.generic.addr = ADDRESS_BOS0614_REG46_REG, .revC.reg470614.reg.generic.addr = ADDRESS_BOS0614_REG47_REG, +}; + +static uint8_t regAddrToReadRevB[] = { + ADDRESS_BOS0614_REFERENCE_REG, + ADDRESS_BOS0614_IC_STATUS_REG, + ADDRESS_BOS0614_READ_REG, + ADDRESS_BOS0614_GPIOX_REG, + ADDRESS_BOS0614_TC_REG, + ADDRESS_BOS0614_CONFIG_REG, + ADDRESS_BOS0614_SENSECONFIG_REG, + ADDRESS_BOS0614_SENSE0_REG, + ADDRESS_BOS0614_SENSE0P_REG, + ADDRESS_BOS0614_SENSE0R_REG, + ADDRESS_BOS0614_SENSE0S_REG, + ADDRESS_BOS0614_SENSE1_REG, + ADDRESS_BOS0614_SENSE1P_REG, + ADDRESS_BOS0614_SENSE1R_REG, + ADDRESS_BOS0614_SENSE1S_REG, + ADDRESS_BOS0614_SENSE2_REG, + ADDRESS_BOS0614_SENSE2P_REG, + ADDRESS_BOS0614_SENSE2R_REG, + ADDRESS_BOS0614_SENSE2S_REG, + ADDRESS_BOS0614_SENSE3_REG, + ADDRESS_BOS0614_SENSE3P_REG, + ADDRESS_BOS0614_SENSE3R_REG, + ADDRESS_BOS0614_SENSE3S_REG, + ADDRESS_BOS0614_SENSESTATUS_REG, + ADDRESS_BOS0614_SENSEDATA0_REG, + ADDRESS_BOS0614_SENSEDATA1_REG, + ADDRESS_BOS0614_SENSEDATA2_REG, + ADDRESS_BOS0614_SENSEDATA3_REG, + ADDRESS_BOS0614_SENSERAW0_REG, + ADDRESS_BOS0614_SENSERAW1_REG, + ADDRESS_BOS0614_SENSERAW2_REG, + ADDRESS_BOS0614_SENSERAW3_REG, + ADDRESS_BOS0614_KPA_REG, + ADDRESS_BOS0614_KP_KI_REG, + ADDRESS_BOS0614_DEADTIME_REG, + ADDRESS_BOS0614_PARCAP_REG, + ADDRESS_BOS0614_SUP_RISE_REG, + ADDRESS_BOS0614_TRIM_REG, + ADDRESS_BOS0614_CHIP_ID_REG, + ADDRESS_BOS0614_VFEEDBACK_REG, + ADDRESS_BOS0614_FIFO_STATE_REG, + ADDRESS_BOS0614_AUTO_STATE_REG, + ADDRESS_BOS0614_BIST_REG, + ADDRESS_BOS0614_BISTRES_REG, + ADDRESS_BOS0614_DEBUG_REG, + ADDRESS_BOS0614_THRESH_REG, + ADDRESS_BOS0614_REG38_REG, + ADDRESS_BOS0614B_SENSE_OFFSET_REG, + ADDRESS_BOS0614_CALIB_DATA_REG, + ADDRESS_BOS0614_REG3B_REG, + ADDRESS_BOS0614_REG3C_REG, + ADDRESS_BOS0614_REG3D_REG, + ADDRESS_BOS0614_REG3E_REG, + ADDRESS_BOS0614_REG3F_REG, + ADDRESS_BOS0614_REG40_REG, + ADDRESS_BOS0614_REG41_REG, + ADDRESS_BOS0614_REG42_REG, + ADDRESS_BOS0614_REG43_REG, + ADDRESS_BOS0614B_RAM_DATA_REG, + ADDRESS_BOS0614_REG45_REG, + ADDRESS_BOS0614_REG46_REG, + ADDRESS_BOS0614_REG47_REG +}; + +static uint8_t regAddrToReadRevC[] = { + ADDRESS_BOS0614_REFERENCE_REG, + ADDRESS_BOS0614_IC_STATUS_REG, + ADDRESS_BOS0614_READ_REG, + ADDRESS_BOS0614_GPIOX_REG, + ADDRESS_BOS0614_TC_REG, + ADDRESS_BOS0614_CONFIG_REG, + ADDRESS_BOS0614_SENSECONFIG_REG, + ADDRESS_BOS0614_SENSE0_REG, + ADDRESS_BOS0614_SENSE0P_REG, + ADDRESS_BOS0614_SENSE0R_REG, + ADDRESS_BOS0614_SENSE0S_REG, + ADDRESS_BOS0614_SENSE1_REG, + ADDRESS_BOS0614_SENSE1P_REG, + ADDRESS_BOS0614_SENSE1R_REG, + ADDRESS_BOS0614_SENSE1S_REG, + ADDRESS_BOS0614_SENSE2_REG, + ADDRESS_BOS0614_SENSE2P_REG, + ADDRESS_BOS0614_SENSE2R_REG, + ADDRESS_BOS0614_SENSE2S_REG, + ADDRESS_BOS0614_SENSE3_REG, + ADDRESS_BOS0614_SENSE3P_REG, + ADDRESS_BOS0614_SENSE3R_REG, + ADDRESS_BOS0614_SENSE3S_REG, + ADDRESS_BOS0614_SENSESTATUS_REG, + ADDRESS_BOS0614_SENSEDATA0_REG, + ADDRESS_BOS0614_SENSEDATA1_REG, + ADDRESS_BOS0614_SENSEDATA2_REG, + ADDRESS_BOS0614_SENSEDATA3_REG, + ADDRESS_BOS0614_SENSERAW0_REG, + ADDRESS_BOS0614_SENSERAW1_REG, + ADDRESS_BOS0614_SENSERAW2_REG, + ADDRESS_BOS0614_SENSERAW3_REG, + ADDRESS_BOS0614_KPA_REG, + ADDRESS_BOS0614_KP_KI_REG, + ADDRESS_BOS0614_DEADTIME_REG, + ADDRESS_BOS0614_PARCAP_REG, + ADDRESS_BOS0614_SUP_RISE_REG, + ADDRESS_BOS0614_TRIM_REG, + ADDRESS_BOS0614_CHIP_ID_REG, + ADDRESS_BOS0614_VFEEDBACK_REG, + ADDRESS_BOS0614_FIFO_STATE_REG, + ADDRESS_BOS0614_AUTO_STATE_REG, + ADDRESS_BOS0614_BIST_REG, + ADDRESS_BOS0614_BISTRES_REG, + ADDRESS_BOS0614_DEBUG_REG, + ADDRESS_BOS0614_THRESH_REG, + ADDRESS_BOS0614_REG38_REG, + ADDRESS_BOS0614C_SENSE_OFFSET_REG, + ADDRESS_BOS0614_CALIB_DATA_REG, + ADDRESS_BOS0614_REG3B_REG, + ADDRESS_BOS0614_REG3C_REG, + ADDRESS_BOS0614_REG3D_REG, + ADDRESS_BOS0614_REG3E_REG, + ADDRESS_BOS0614_REG3F_REG, + ADDRESS_BOS0614_REG40_REG, + ADDRESS_BOS0614_REG41_REG, + ADDRESS_BOS0614_REG42_REG, + ADDRESS_BOS0614_REG43_REG, + ADDRESS_BOS0614C_RAM_DATA_REG, + ADDRESS_BOS0614_REG45_REG, + ADDRESS_BOS0614_REG46_REG, + ADDRESS_BOS0614_REG47_REG +}; +#if 0 +static BOS0614_REGS bOS0614Regs = + { + .REFERENCE_0614.reg.generic.addr = ADDRESS_BOS0614_REFERENCE_REG, + .IC_STATUS_0614.reg.generic.addr = ADDRESS_BOS0614_IC_STATUS_REG, + .READ_0614.reg.generic.addr = ADDRESS_BOS0614_READ_REG, + .GPIOX_0614.reg.generic.addr = ADDRESS_BOS0614_GPIOX_REG, + .TC_0614.reg.generic.addr = ADDRESS_BOS0614_TC_REG, + .CONFIG_0614.reg.generic.addr = ADDRESS_BOS0614_CONFIG_REG, + .SENSECONFIG_0614.reg.generic.addr = ADDRESS_BOS0614_SENSECONFIG_REG, + .SENSE0_0614.reg.generic.addr = ADDRESS_BOS0614_SENSE0_REG, + .SENSE0P_0614.reg.generic.addr = ADDRESS_BOS0614_SENSE0P_REG, + .SENSE0R_0614.reg.generic.addr = ADDRESS_BOS0614_SENSE0R_REG, + .SENSE0S_0614.reg.generic.addr = ADDRESS_BOS0614_SENSE0S_REG, + .SENSE1_0614.reg.generic.addr = ADDRESS_BOS0614_SENSE1_REG, + .SENSE1P_0614.reg.generic.addr = ADDRESS_BOS0614_SENSE1P_REG, + .SENSE1R_0614.reg.generic.addr = ADDRESS_BOS0614_SENSE1R_REG, + .SENSE1S_0614.reg.generic.addr = ADDRESS_BOS0614_SENSE1S_REG, + .SENSE2_0614.reg.generic.addr = ADDRESS_BOS0614_SENSE2_REG, + .SENSE2P_0614.reg.generic.addr = ADDRESS_BOS0614_SENSE2P_REG, + .SENSE2R_0614.reg.generic.addr = ADDRESS_BOS0614_SENSE2R_REG, + .SENSE2S_0614.reg.generic.addr = ADDRESS_BOS0614_SENSE2S_REG, + .SENSE3_0614.reg.generic.addr = ADDRESS_BOS0614_SENSE3_REG, + .SENSE3P_0614.reg.generic.addr = ADDRESS_BOS0614_SENSE3P_REG, + .SENSE3R_0614.reg.generic.addr = ADDRESS_BOS0614_SENSE3R_REG, + .SENSE3S_0614.reg.generic.addr = ADDRESS_BOS0614_SENSE3S_REG, + .SENSESTATUS_0614.reg.generic.addr = ADDRESS_BOS0614_SENSESTATUS_REG, + .SENSEDATA0_0614.reg.generic.addr = ADDRESS_BOS0614_SENSEDATA0_REG, + .SENSEDATA1_0614.reg.generic.addr = ADDRESS_BOS0614_SENSEDATA1_REG, + .SENSEDATA2_0614.reg.generic.addr = ADDRESS_BOS0614_SENSEDATA2_REG, + .SENSEDATA3_0614.reg.generic.addr = ADDRESS_BOS0614_SENSEDATA3_REG, + .SENSERAW0_0614.reg.generic.addr = ADDRESS_BOS0614_SENSERAW0_REG, + .SENSERAW1_0614.reg.generic.addr = ADDRESS_BOS0614_SENSERAW1_REG, + .SENSERAW2_0614.reg.generic.addr = ADDRESS_BOS0614_SENSERAW2_REG, + .SENSERAW3_0614.reg.generic.addr = ADDRESS_BOS0614_SENSERAW3_REG, + .KPA_0614.reg.generic.addr = ADDRESS_BOS0614_KPA_REG, + .KP_KI_0614.reg.generic.addr = ADDRESS_BOS0614_KP_KI_REG, + .DEADTIME_0614.reg.generic.addr = ADDRESS_BOS0614_DEADTIME_REG, + .PARCAP_0614.reg.generic.addr = ADDRESS_BOS0614_PARCAP_REG, + .SUP_RISE_0614.reg.generic.addr = ADDRESS_BOS0614_SUP_RISE_REG, + .TRIM_0614.reg.generic.addr = ADDRESS_BOS0614_TRIM_REG, + .CHIP_ID_0614.reg.generic.addr = ADDRESS_BOS0614_CHIP_ID_REG, + .VFEEDBACK_0614.reg.generic.addr = ADDRESS_BOS0614_VFEEDBACK_REG, + .FIFO_STATE_0614.reg.generic.addr = ADDRESS_BOS0614_FIFO_STATE_REG, + .AUTO_STATE_0614.reg.generic.addr = ADDRESS_BOS0614_AUTO_STATE_REG, + .BIST_0614.reg.generic.addr = ADDRESS_BOS0614_BIST_REG, + .BISTRES_0614.reg.generic.addr = ADDRESS_BOS0614_BISTRES_REG, + .DEBUG_0614.reg.generic.addr = ADDRESS_BOS0614_DEBUG_REG, + .THRESH_0614.reg.generic.addr = ADDRESS_BOS0614_THRESH_REG, + .REG38_0614.reg.generic.addr = ADDRESS_BOS0614_REG38_REG, + .REG39_0614.reg.generic.addr = ADDRESS_BOS0614_REG39_REG, + .CALIB_DATA_0614.reg.generic.addr = ADDRESS_BOS0614_CALIB_DATA_REG, + .REG3B_0614.reg.generic.addr = ADDRESS_BOS0614_REG3B_REG, + .REG3C_0614.reg.generic.addr = ADDRESS_BOS0614_REG3C_REG, + .REG3D_0614.reg.generic.addr = ADDRESS_BOS0614_REG3D_REG, + .REG3E_0614.reg.generic.addr = ADDRESS_BOS0614_REG3E_REG, + .REG3F_0614.reg.generic.addr = ADDRESS_BOS0614_REG3F_REG, + .REG40_0614.reg.generic.addr = ADDRESS_BOS0614_REG40_REG, + .REG41_0614.reg.generic.addr = ADDRESS_BOS0614_REG41_REG, + .REG42_0614.reg.generic.addr = ADDRESS_BOS0614_REG42_REG, + .REG43_0614.reg.generic.addr = ADDRESS_BOS0614_REG43_REG, + .REG44_0614.reg.generic.addr = ADDRESS_BOS0614_REG44_REG, + .REG45_0614.reg.generic.addr = ADDRESS_BOS0614_REG45_REG, + .REG46_0614.reg.generic.addr = ADDRESS_BOS0614_REG46_REG, + .REG47_0614.reg.generic.addr = ADDRESS_BOS0614_REG47_REG, + }; + +static Bos0614RegisterStruct regToRead[] = { + {.addr = ADDRESS_BOS0614_REFERENCE_REG,}, + {.addr = ADDRESS_BOS0614_IC_STATUS_REG,}, + {.addr = ADDRESS_BOS0614_READ_REG,}, + {.addr = ADDRESS_BOS0614_GPIOX_REG,}, + {.addr = ADDRESS_BOS0614_TC_REG,}, + {.addr = ADDRESS_BOS0614_CONFIG_REG,}, + {.addr = ADDRESS_BOS0614_SENSECONFIG_REG,}, + {.addr = ADDRESS_BOS0614_SENSE0_REG,}, + {.addr = ADDRESS_BOS0614_SENSE0P_REG,}, + {.addr = ADDRESS_BOS0614_SENSE0R_REG,}, + {.addr = ADDRESS_BOS0614_SENSE0S_REG,}, + {.addr = ADDRESS_BOS0614_SENSE1_REG,}, + {.addr = ADDRESS_BOS0614_SENSE1P_REG,}, + {.addr = ADDRESS_BOS0614_SENSE1R_REG,}, + {.addr = ADDRESS_BOS0614_SENSE1S_REG,}, + {.addr = ADDRESS_BOS0614_SENSE2_REG,}, + {.addr = ADDRESS_BOS0614_SENSE2P_REG,}, + {.addr = ADDRESS_BOS0614_SENSE2R_REG,}, + {.addr = ADDRESS_BOS0614_SENSE2S_REG,}, + {.addr = ADDRESS_BOS0614_SENSE3_REG,}, + {.addr = ADDRESS_BOS0614_SENSE3P_REG,}, + {.addr = ADDRESS_BOS0614_SENSE3R_REG,}, + {.addr = ADDRESS_BOS0614_SENSE3S_REG,}, + {.addr = ADDRESS_BOS0614_SENSESTATUS_REG,}, + {.addr = ADDRESS_BOS0614_SENSEDATA0_REG,}, + {.addr = ADDRESS_BOS0614_SENSEDATA1_REG,}, + {.addr = ADDRESS_BOS0614_SENSEDATA2_REG,}, + {.addr = ADDRESS_BOS0614_SENSEDATA3_REG,}, + {.addr = ADDRESS_BOS0614_SENSERAW0_REG,}, + {.addr = ADDRESS_BOS0614_SENSERAW1_REG,}, + {.addr = ADDRESS_BOS0614_SENSERAW2_REG,}, + {.addr = ADDRESS_BOS0614_SENSERAW3_REG,}, + {.addr = ADDRESS_BOS0614_KPA_REG,}, + {.addr = ADDRESS_BOS0614_KP_KI_REG,}, + {.addr = ADDRESS_BOS0614_DEADTIME_REG,}, + {.addr = ADDRESS_BOS0614_PARCAP_REG,}, + {.addr = ADDRESS_BOS0614_SUP_RISE_REG,}, + {.addr = ADDRESS_BOS0614_TRIM_REG,}, + {.addr = ADDRESS_BOS0614_CHIP_ID_REG,}, + {.addr = ADDRESS_BOS0614_VFEEDBACK_REG,}, + {.addr = ADDRESS_BOS0614_FIFO_STATE_REG,}, + {.addr = ADDRESS_BOS0614_AUTO_STATE_REG,}, + {.addr = ADDRESS_BOS0614_BIST_REG,}, + {.addr = ADDRESS_BOS0614_BISTRES_REG,}, + {.addr = ADDRESS_BOS0614_DEBUG_REG,}, + {.addr = ADDRESS_BOS0614_THRESH_REG,}, + {.addr = ADDRESS_BOS0614_REG38_REG,}, + {.addr = ADDRESS_BOS0614_REG39_REG,}, + {.addr = ADDRESS_BOS0614_CALIB_DATA_REG,}, + {.addr = ADDRESS_BOS0614_REG3B_REG,}, + {.addr = ADDRESS_BOS0614_REG3C_REG,}, + {.addr = ADDRESS_BOS0614_REG3D_REG,}, + {.addr = ADDRESS_BOS0614_REG3E_REG,}, + {.addr = ADDRESS_BOS0614_REG3F_REG,}, + {.addr = ADDRESS_BOS0614_REG40_REG,}, + {.addr = ADDRESS_BOS0614_REG41_REG,}, + {.addr = ADDRESS_BOS0614_REG42_REG,}, + {.addr = ADDRESS_BOS0614_REG43_REG,}, + {.addr = ADDRESS_BOS0614_REG44_REG,}, + {.addr = ADDRESS_BOS0614_REG45_REG,}, + {.addr = ADDRESS_BOS0614_REG46_REG,}, + {.addr = ADDRESS_BOS0614_REG47_REG,}, + }; + +Bos0614RegisterStruct *getAllRegsPtr(void) +{ + return regToRead; +} +#endif + +static bool readAllRegister(Context *ctx) +{ + uint32_t index; + bool res = true; + + for (index = 0; index < DATA_ARRAY_LENGTH(regAddrToReadRevB) && res; index++) + { + uint16_t dummy; + + if (ctx->rev == REV_B) + { + res = bos0614GetRegister(&ctx->hDriver, regAddrToReadRevB[index], &dummy); + } + else if (ctx->rev == REV_C) + { + res = bos0614GetRegister(&ctx->hDriver, regAddrToReadRevC[index], &dummy); + } + } + + return res; +} + +static bool setDefaultConfig(Context *ctx) +{ + bool res = false; + + //Reset configuration of the register; + if (ctx->rev == REV_B) + { + memcpy(&ctx->reg, &bos0614RegsRevB, sizeof(bos0614RegsRevB)); + } + else if (ctx->rev == REV_C) + { + memcpy(&ctx->reg, &bos0614RegsRevC, sizeof(bos0614RegsRevC)); + } + + //Why do we need to do a dummy read??? + res = readAllRegister(ctx); + + ctx->reg.common.senseConfig0614.bit.ch0 = BOS0614_DISABLE; + ctx->reg.common.senseConfig0614.bit.ch1 = BOS0614_DISABLE; + ctx->reg.common.senseConfig0614.bit.ch2 = BOS0614_DISABLE; + ctx->reg.common.senseConfig0614.bit.ch3 = BOS0614_DISABLE; + ctx->reg.common.senseConfig0614.bit.same = BOS0614_DISABLE; + + return res; +} + +static bool checkBos0614Revision(Context* ctx) +{ + bool res = false; + + if (&(ctx->hDriver) != NULL) + { + res = bos0614GetRegister(&ctx->hDriver, ADDRESS_BOS0614_CHIP_ID_REG, &ctx->chipId); + if ((ctx->chipId & BOS0614_REV_C_CHIP_ID_MASK) == BOS0614_REV_C_CHIP_ID_BASE && res) + { + ctx->rev = REV_C; + ctx->chipId = BOS0614_REV_C_CHIP_ID_DEFAULT; + } + else if (ctx->chipId == BOS0614_REV_B_RETURNED_CHIP_ID && res) + { + ctx->rev = REV_B; + ctx->chipId = BOS0614_REV_B_CHIP_ID; + } + else + { + ctx->chipId = INVALID_CHIP_ID; + res = false; + } + } + + return res; +} + +static void initiateDriver(Context *ctx) +{ + bool res = false; + + memcpy(&ctx->hDriver, &bos0614Driver, sizeof(bos0614Driver)); + memcpy(&ctx->reg, &bos0614RegsRevB, sizeof(Bos0614Registers)); + + res = checkBos0614Revision(ctx); + + if (ctx->chipId == BOS0614_REV_C_CHIP_ID_DEFAULT) + { + memcpy(&ctx->reg, &bos0614RegsRevC, sizeof(Bos0614Registers)); + } +} + +static const HapticDriver bos0614Driver = { + bos0614DriverGetChipId, + bos0614SoftwareReset, + bos0614DeepSleep, + + bos0614GetRegister, + bos0614GetRegisters, + + bos0614SetRegister, + bos0614SetRegisters, + + NULL, //BosSetGetRegister setGetRegister; + NULL, //BosSetGetRegisters setGetRegisters; + + bos0614SetSlice, + bos0614SetWaveforms, + bos0614SynthesizerPlay, + + bos0614RegisterOnEvents, + bos0614UnregisterEvents, + + bos0614CtrlOutput, + bos0614ConfigGPO, + + bos0614SetRamPlaybackMode, + bos0614PlayRamPlayback, + + bos0614GetFifoSpace, + bos0614GetMaxFifoSpace, + bos0614SetFifoMode, + bos0614WriteFifo, + + bos0614Synch, + bos0614GetError, + bos0614ReferencingFromVolt, + bos0614ReferencingFromRelativeInt16, + bos0614NbrOfRegister, + + bos0614ButtonSensing, + bos0614EnableSensing, + bos0614SensingAutoPlayWave, + bos0614SensingStop, + + bos0614FeatureSupport, + bos0614SenseOutput, + NULL, + + /* Engineering functions */ + bos0614GetShadowRegisters, + + bos0614WriteRegister, + bos0614ReadRegister, + + bos0614GetGpioState +}; diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/haptic/bos0614Driver.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/haptic/bos0614Driver.h new file mode 100644 index 000000000000..c57b3defd9db --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/haptic/bos0614Driver.h @@ -0,0 +1,76 @@ +// +// Description: BOS0614 Driver Instantiation +// Created on 5/5/2020 +// Copyright (c) 2020 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#ifndef DKCORE_BOS0614DRIVER_H +#define DKCORE_BOS0614DRIVER_H + +#include "coreDef.h" +#include "bsp/drivers/haptic/bosDriver.h" +#include "bsp/drivers/i2c/i2c.h" + +#define BOS0614_NBR_OF_CHANNEL (4) +#define BOS0614_CHANNEL_MASK (0xF) + +typedef struct +{ + const I2c* i2c; + const Gpio* gpioA; + const Gpio* gpioB; + const Gpio* gpioC; + const Gpio* gpioD; +} Bos0614Resource; + +/** + * @brief Init bos1614 haptic driver with I2C + * + * @param[in] resources I2C and GPIO drivers reference + * + * @return The Sensing Driver + */ +HapticDriver* bos0614DriverI2cInit(Bos0614Resource resources); + +/** + * @brief Uninit a bos1614 Haptic Driver + * + * @param driver The HapticDriver to unint + * + * @return True if the driver as been handled, false otherwise + */ +bool bos0614DriverUninit(HapticDriver* driver); + +/** + * @brief Init bos1614 haptic driver with SPI + * + * @param[in] spi SPI driver reference + * @param[in] gpioA GPIOA driver reference + * @param[in] gpioD GPIOD driver reference + * + * @return The Sensing Driver + */ +HapticDriver* bos0614DriverSpiInit(const Spi* spi, const Gpio* gpioA, const Gpio* gpioD); + +Bos0614RegisterStruct *getAllRegsPtr(void); +bool bos0614DriverUninit(HapticDriver* driver); + +#endif //DKCORE_BOS0614DRIVER_H diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/haptic/bos0614Register.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/haptic/bos0614Register.h new file mode 100644 index 000000000000..fc3fddfb2498 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/haptic/bos0614Register.h @@ -0,0 +1,1666 @@ +// +// Description: BOS0614 Register Definition +// Created on 5/5/2020 +// Copyright (c) 2020 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#ifndef BOS0614_REGISTER_H_ +#define BOS0614_REGISTER_H_ + +#include + +#define ADDRESS_BOS0614_REFERENCE_REG 0x0000 +#define ADDRESS_BOS0614_IC_STATUS_REG 0x0001 +#define ADDRESS_BOS0614_READ_REG 0x0002 +#define ADDRESS_BOS0614_GPIOX_REG 0x0003 +#define ADDRESS_BOS0614_TC_REG 0x0004 +#define ADDRESS_BOS0614_CONFIG_REG 0x0005 +#define ADDRESS_BOS0614_SENSECONFIG_REG 0x0006 +#define ADDRESS_BOS0614_SENSE0_REG 0x0007 +#define ADDRESS_BOS0614_SENSE0P_REG 0x0008 +#define ADDRESS_BOS0614_SENSE0R_REG 0x0009 +#define ADDRESS_BOS0614_SENSE0S_REG 0x000A +#define ADDRESS_BOS0614_SENSE1_REG 0x000B +#define ADDRESS_BOS0614_SENSE1P_REG 0x000C +#define ADDRESS_BOS0614_SENSE1R_REG 0x000D +#define ADDRESS_BOS0614_SENSE1S_REG 0x000E +#define ADDRESS_BOS0614_SENSE2_REG 0x000F +#define ADDRESS_BOS0614_SENSE2P_REG 0X0010 +#define ADDRESS_BOS0614_SENSE2R_REG 0x0011 +#define ADDRESS_BOS0614_SENSE2S_REG 0x0012 +#define ADDRESS_BOS0614_SENSE3_REG 0x0013 +#define ADDRESS_BOS0614_SENSE3P_REG 0x0014 +#define ADDRESS_BOS0614_SENSE3R_REG 0x0015 +#define ADDRESS_BOS0614_SENSE3S_REG 0x0016 +#define ADDRESS_BOS0614_SENSESTATUS_REG 0x0017 +#define ADDRESS_BOS0614_SENSEDATA0_REG 0x0018 +#define ADDRESS_BOS0614_SENSEDATA1_REG 0x0019 +#define ADDRESS_BOS0614_SENSEDATA2_REG 0x001A +#define ADDRESS_BOS0614_SENSEDATA3_REG 0x001B +#define ADDRESS_BOS0614_SENSERAW0_REG 0X001C +#define ADDRESS_BOS0614_SENSERAW1_REG 0x001D +#define ADDRESS_BOS0614_SENSERAW2_REG 0x001E +#define ADDRESS_BOS0614_SENSERAW3_REG 0x001F +#define ADDRESS_BOS0614_KPA_REG 0x0020 +#define ADDRESS_BOS0614_KP_KI_REG 0x0021 +#define ADDRESS_BOS0614_DEADTIME_REG 0x0022 +#define ADDRESS_BOS0614_PARCAP_REG 0x0023 +#define ADDRESS_BOS0614_SUP_RISE_REG 0x0024 +#define ADDRESS_BOS0614_TRIM_REG 0x0025 +#define ADDRESS_BOS0614_CHIP_ID_REG 0x0026 +#define ADDRESS_BOS0614_VFEEDBACK_REG 0x0028 +#define ADDRESS_BOS0614_FIFO_STATE_REG 0x0029 +#define ADDRESS_BOS0614_AUTO_STATE_REG 0x002A +#define ADDRESS_BOS0614C_RAM_DATA_REG 0x002B +#define ADDRESS_BOS0614C_SENSE_OFFSET_REG 0x002C +#define ADDRESS_BOS0614_BIST_REG 0x0030 +#define ADDRESS_BOS0614_BISTRES_REG 0x0031 +#define ADDRESS_BOS0614_DEBUG_REG 0x0032 +#define ADDRESS_BOS0614_THRESH_REG 0x0033 +#define ADDRESS_BOS0614_REG38_REG 0x0038 +#define ADDRESS_BOS0614B_SENSE_OFFSET_REG 0x0039 +#define ADDRESS_BOS0614_CALIB_DATA_REG 0x003A +#define ADDRESS_BOS0614_REG3B_REG 0x003B +#define ADDRESS_BOS0614_REG3C_REG 0x003C +#define ADDRESS_BOS0614_REG3D_REG 0x003D +#define ADDRESS_BOS0614_REG3E_REG 0x003E +#define ADDRESS_BOS0614_REG3F_REG 0x003F +#define ADDRESS_BOS0614_REG40_REG 0x0040 +#define ADDRESS_BOS0614_REG41_REG 0x0041 +#define ADDRESS_BOS0614_REG42_REG 0x0042 +#define ADDRESS_BOS0614_REG43_REG 0x0043 +#define ADDRESS_BOS0614B_RAM_DATA_REG 0x0044 +#define ADDRESS_BOS0614_REG45_REG 0x0045 +#define ADDRESS_BOS0614_REG46_REG 0x0046 +#define ADDRESS_BOS0614_REG47_REG 0x0047 + +#define BOS0614_MAXIMUM_REG_ADDR (ADDRESS_BOS0614_REG47_REG) + +typedef struct +{ + uint16_t value; + uint16_t addr; +} Bos0614RegisterStruct; + +typedef union +{ + const uint32_t all; + Bos0614RegisterStruct generic; +} Bos0614Register; + +typedef struct +{ + Bos0614Register reg; +} UndefReg; + +//*********************************************** +// REFERENCE register - address 0x00 +//*********************************************** +struct ReferenceBits0614 +{ + uint16_t fifo: 12; // 11:0 Input of the FIFO + uint16_t channel: 4; // 12:15 Channel 3 enable +}; + +union ReferenceReg0614 +{ + Bos0614Register reg; + struct ReferenceBits0614 bit; +}; + +//*********************************************** +// REFERENCE register - address 0x00 REMAP +//*********************************************** +struct ReferenceRmBits0614 +{ + uint16_t data: 16; // 15:0 Input of the RAM communication +}; + +union ReferenceRmReg0614 +{ + Bos0614Register reg; + struct ReferenceRmBits0614 bit; +}; + + +//*********************************************** +// IC_STATUS register - address 0x01 +//*********************************************** +struct IcStatusBits0614 +{ + uint16_t empty: 1; // 0 FIFO Empty + uint16_t full: 1; // 1 FIFO Full + uint16_t sc: 1; // 2 Short Circuit Status + uint16_t uvlo: 1; // 3 Under Voltage + uint16_t idac: 1; // 4 Status bit + uint16_t maxPower: 1; // 5 Amount of Power + uint16_t ovt: 1; // 6 Over Temperature + uint16_t ovv: 1; // 7 Over Voltage + uint16_t state: 2; // 9:8 State of controller + uint16_t pressRelease: 4; // 10:13 State of sense channels + uint16_t senseAll: 1; // 14 Trigger condition met + const uint16_t reserved: 1; // 15 Reserved +}; + +union IcStatusReg0614 +{ + Bos0614Register reg; + struct IcStatusBits0614 bit; +}; + +//*********************************************** +// READ register - address 0x02 +//*********************************************** +struct ReadBits0614 +{ + uint16_t bc: 8; // 7:0 Address of internal register whose content is returned on comm bus + const uint16_t reserved: 8; // 15:8 Reserved +}; + +union ReadReg0614 +{ + Bos0614Register reg; + struct ReadBits0614 bit; +}; + +//*********************************************** +// GPIOX register - address 0x03 +//*********************************************** + +typedef enum +{ + Bos0614GPOCtrl_ButtonState = 0, + Bos0614GPOCtrl_SenseTrigger, + Bos0614GPOCtrl_SenseTriggerAny, + Bos0614GPOCtrl_WaveformFifoDone, + Bos0614GPOCtrl_ErrorNotify, + Bos0614GPOCtrl_MaxPower, + Bos0614GPOCtrl_NextData, + Bos0614GPOCtrl_RequestPlay, +} Bos0614GpioMode; + +struct GpioxBits0614 +{ + Bos0614GpioMode gpio0: 4; // 3:0 Controls bit of GPIO0 + Bos0614GpioMode gpio1: 4; // 7:4 Controls bit of GPIO1 + Bos0614GpioMode gpio2: 4; // 11:8 Controls bit of GPIO2 + Bos0614GpioMode gpio3: 4; // 15:12 Controls bit of GPIO3 +}; + +union GpioxReg0614 +{ + Bos0614Register reg; + struct GpioxBits0614 bit; +}; + +//*********************************************** +// TC register - address 0x04 - REV_B +//*********************************************** +struct TcBits0614RevB +{ + uint16_t tc: 5; // 4:0 Time to short output + uint16_t pc: 1; // 5 Short piezo at end of waveform if = 1 + uint16_t pol: 1; // 6 If GPIO sensing + const uint16_t reserved: 9; // 15:7 Reserved +}; + +union TcReg0614RevB +{ + Bos0614Register reg; + struct TcBits0614RevB bit; +}; + +//*********************************************** +// TC register - address 0x04 - REV_C +//*********************************************** +struct TcBits0614RevC +{ + uint16_t tcr: 5; // 4:0 Time to short the output after end of a waveform for button press + uint16_t tcp: 5; // 9:6 Time to short the output after end of a waveform for button release + uint16_t pc: 1; // 10 Short piezo at end of waveform if = 1 + uint16_t pol: 1; // 11 If GPIO sensing + const uint16_t reserved: 4; // 15:7 Reserved +}; + +union TcReg0614RevC +{ + Bos0614Register reg; + struct TcBits0614RevC bit; +}; + +//*********************************************** +// CONFIG register - address 0x5 +//*********************************************** +typedef enum +{ + Bos0614SamplingRate_1024Ksps = 0, + Bos0614SamplingRate_512Ksps, + Bos0614SamplingRate_256Ksps, + Bos0614SamplingRate_128Ksps, + Bos0614SamplingRate_64Ksps, + Bos0614SamplingRate_32Ksps, + Bos0614SamplingRate_16Ksps, + Bos0614SamplingRate_8Ksps, +} Bos0614SamplingRate; + +typedef enum +{ + Bos0614OutputDisabled = 0, + Bos0614OutputEnabled, +} Bos0614OutputOE; + + +typedef enum +{ + Bos0614Mode_Direct = 0x0, + Bos0614Mode_FIFO = 0x1, + Bos0614Mode_RAM_Playback, + Bos0614Mode_RAM_Synthesis, +} Bos0614Mode; + +struct ConfigBits0614 +{ + Bos0614SamplingRate play: 3; // 2:0 Waveform play back speed + uint16_t ds: 1; // 3 Power mode when not playing waveforms + Bos0614OutputOE oe: 1; // 4 Enable/Disable waveform playback + uint16_t lock: 1; // 5 Register lock + uint16_t rst: 1; // 6 Software Reset + uint16_t upi: 1; // 7 UPI enable + uint16_t tout: 1; // 8 Auto TimeOut + Bos0614Mode ram: 2; // 10:9 Ram Behavior + uint16_t str: 1; // 11 Comm Address counter behavior + uint16_t SHORT: 2; // 13:12 Length of time to short + uint16_t od: 1; // 14 GPIOs Open Drain + uint16_t sc: 1; // 15 Channel selection + +}; + +union ConfigReg0614 +{ + Bos0614Register reg; + struct ConfigBits0614 bit; +}; + + +//*********************************************** +// SENSECONFIG register - address 0x06 - REV_B +//*********************************************** +struct SenseConfigBits0614RevB +{ + uint16_t ch0: 1; // 0 CH0 Sensing + uint16_t ch1: 1; // 1 CH1 Sensing + uint16_t ch2: 1; // 2 CH2 Sensing + uint16_t ch3: 1; // 3 CH3 Sensing + uint16_t cal: 1; // 4 Calibrate sense interface + uint16_t same: 1; // 5 All sense have the same configuration (Sense0) + uint16_t neg: 2; // 7:6 Reset point voltage negative + uint16_t pos: 2; // 9:8 Reset point voltage positive + uint16_t scomp: 3; // 12:10 Discharged on the sense channel + uint16_t sh: 1; // 13 Short Output during playback + uint16_t diff: 1; // 14 ShutDown sensing when not needed + uint16_t extTrig: 1; // 15 EXT trigger enable +}; + +union SenseConfigReg0614RevB +{ + Bos0614Register reg; + struct SenseConfigBits0614RevB bit; +}; + + +//*********************************************** +// SENSECONFIG register - address 0x06 - REV_C +//*********************************************** +struct SenseConfigBits0614RevC +{ + uint16_t ch0: 1; // 0 CH0 Sensing + uint16_t ch1: 1; // 1 CH1 Sensing + uint16_t ch2: 1; // 2 CH2 Sensing + uint16_t ch3: 1; // 3 CH3 Sensing + uint16_t cal: 1; // 4 Calibrate sense interface + uint16_t same: 1; // 5 All sense have the same configuration (Sense0) + uint16_t samp: 2; // 7:6 Set the reset point of sense voltage to + uint16_t scompAuto: 2; // 9:8 Change the behavior of SCOMP after the end of a waveform play + uint16_t scomp: 2; // 12:10 Defines time between zeroing of output channel to null the effect of discharge on the sensed channels + uint16_t seq: 1; // 13 Set if more than one channel with auto triggering can play haptic at the same time + uint16_t zps: 1; // 15 Sets if a ZPS detection is sufficient to be considered as a valid press + uint16_t zps_sens: 1; // 15 Sensitivity of the ZPS wakeup signal + uint16_t extTrig: 1; // 15 Activate external trigger + +}; + +union SenseConfigReg0614RevC +{ + Bos0614Register reg; + struct SenseConfigBits0614RevC bit; +}; + +//*********************************************** +// SENSECONFIG register - address 0x06 - Common +//*********************************************** +struct SenseConfigBits0614Common +{ + uint16_t ch0: 1; // 0 CH0 Sensing + uint16_t ch1: 1; // 1 CH1 Sensing + uint16_t ch2: 1; // 2 CH2 Sensing + uint16_t ch3: 1; // 3 CH3 Sensing + uint16_t cal: 1; // 4 Calibrate sense interface + uint16_t same: 1; // 5 All sense have the same configuration (Sense0) + const uint16_t reserved: 6; // 14:6 Reserved + uint16_t extTrig: 1; // 15 EXT trigger enable + +}; + +union SenseConfigReg0614Common +{ + Bos0614Register reg; + struct SenseConfigBits0614Common bit; +}; + + +//*********************************************** +// SENSE0 register - address 0x07 +//*********************************************** +typedef struct +{ + uint16_t t1: 1; // 0 Threshold Sensing + uint16_t t2: 1; // 1 Threshold Sensing + uint16_t s1: 1; // 2 Threshold Sensing + uint16_t s2: 1; // 3 Threshold Sensing + uint16_t autop: 1; // 4 Start Playing on senseP + uint16_t autor: 1; // 5 Start Playing on senseR + uint16_t wvp: 3; // 8:6 Waveform to play P + uint16_t wvr: 3; // 11:9 Waveform to play R + const uint16_t reserved: 4; // 15:12 Reserved +} SenseBits0614; + +union Sense0Reg0614 +{ + Bos0614Register reg; + SenseBits0614 bit; +}; + +//*********************************************** +// SENSE0P register - address 0x08 +//*********************************************** + +typedef enum +{ + SensingThresholdMode_Above = 0x0, + SensingThresholdMode_Below = 0x1 +} SensingThresholdMode; + +typedef enum +{ + DebouncingTime_1us = 0, + DebouncingTime_100us = 1, + DebouncingTime_500us = 2, + DebouncingTime_1ms = 3, + DebouncingTime_2ms = 4, + DebouncingTime_4ms = 5, + DebouncingTime_8ms = 6, + DebouncingTime_16ms = 7 +} DebouncingTime; + +typedef struct +{ + uint16_t threshold: 12; // 11:0 Threshold Sensing + SensingThresholdMode ab: 1; // 12 Above or Below + uint16_t rep: 3; // 15:13 Time for sense +} SenseThreshold; + +union Sense0PReg0614 +{ + Bos0614Register reg; + SenseThreshold bit; +}; + +//*********************************************** +// SENSE0R register - address 0x09 +//*********************************************** + +union Sense0RReg0614 +{ + Bos0614Register reg; + SenseThreshold bit; +}; + +//*********************************************** +// SENSE0S register - address 0x0A +//*********************************************** +typedef struct +{ + uint16_t slope1: 7; // 6:0 Slope 1 of force + uint16_t abs1: 1; // 1 Above or Below + uint16_t slope2: 7; // 14:8 Slope 2 of force + uint16_t abs2: 1; // 1 Above or Below +} SenseSBits0614; + +union Sense0SReg0614 +{ + Bos0614Register reg; + SenseSBits0614 bit; +}; + +//*********************************************** +// SENSE1 register - address 0x0B +//*********************************************** + +union Sense1Reg0614 +{ + Bos0614Register reg; + SenseBits0614 bit; +}; + +//*********************************************** +// SENSE1P register - address 0x0C +//*********************************************** + +union Sense1PReg0614 +{ + Bos0614Register reg; + SenseThreshold bit; +}; + +//*********************************************** +// SENSE1R register - address 0x0D +//*********************************************** + +union Sense1RReg0614 +{ + Bos0614Register reg; + SenseThreshold bit; +}; + +//*********************************************** +// SENSE1S register - address 0x0E +//*********************************************** + +union Sense1SReg0614 +{ + Bos0614Register reg; + SenseSBits0614 bit; +}; + +//*********************************************** +// SENSE2 register - address 0x0F +//*********************************************** + +union Sense2Reg0614 +{ + Bos0614Register reg; + SenseBits0614 bit; +}; + +//*********************************************** +// SENSE0P register - address 0x10 +//*********************************************** +union Sense2PReg0614 +{ + Bos0614Register reg; + SenseThreshold bit; +}; + +//*********************************************** +// SENSE2R register - address 0x11 +//*********************************************** + +union Sense2RReg0614 +{ + Bos0614Register reg; + SenseThreshold bit; +}; + +//*********************************************** +// SENSE2S register - address 0x12 +//*********************************************** + +union Sense2SReg0614 +{ + Bos0614Register reg; + SenseSBits0614 bit; +}; + +//*********************************************** +// SENSE3 register - address 0x13 +//*********************************************** + +union Sense3Reg0614 +{ + Bos0614Register reg; + SenseBits0614 bit; +}; + +//*********************************************** +// SENSE3P register - address 0x14 +//*********************************************** + +union Sense3PReg0614 +{ + Bos0614Register reg; + SenseThreshold bit; +}; + +//*********************************************** +// SENSE3R register - address 0x15 +//*********************************************** + +union Sense3RReg0614 +{ + Bos0614Register reg; + SenseThreshold bit; +}; + +//*********************************************** +// SENSE3S register - address 0x16 +//*********************************************** + +union Sense3SReg0614 +{ + Bos0614Register reg; + SenseSBits0614 bit; +}; + +//*********************************************** +// SENSESTATUS register - address 0x17 +//*********************************************** +struct SenseStatusBits0614 +{ + uint16_t t10: 1; // 1 Comparator status press threshold ch0 + uint16_t t20: 1; // 1 Comparator status release threshold ch0 + uint16_t s10: 1; // 1 Comparator status slope 1 ch0 + uint16_t s20: 1; // 1 Comparator status slope 2 ch0 + uint16_t t11: 1; // 1 Comparator status press threshold ch1 + uint16_t t21: 1; // 1 Comparator status release threshold ch1 + uint16_t s11: 1; // 1 Comparator status slope 1 ch1 + uint16_t s21: 1; // 1 Comparator status slope 2 ch1 + uint16_t t12: 1; // 1 Comparator status press threshold ch2 + uint16_t t22: 1; // 1 Comparator status release threshold ch2 + uint16_t s12: 1; // 1 Comparator status slope 1 ch2 + uint16_t s22: 1; // 1 Comparator status slope 2 ch2 + uint16_t t13: 1; // 1 Comparator status press threshold ch3 + uint16_t t23: 1; // 1 Comparator status release threshold ch3 + uint16_t s13: 1; // 1 Comparator status slope 1 ch3 + uint16_t s23: 1; // 1 Comparator status slope 2 ch3 +}; + +union SenseStatusReg0614 +{ + Bos0614Register reg; + struct SenseStatusBits0614 bit; +}; + +//*********************************************** +// SENSEDATA0 register - address 0x18 +//*********************************************** +struct SenseDataBits0614 +{ + uint16_t data: 16; // 16 Sensing data +}; + +union SenseData0Reg0614 +{ + Bos0614Register reg; + struct SenseDataBits0614 bit; +}; + +//*********************************************** +// SENSEDATA1 register - address 0x19 +//*********************************************** + +union SenseData1Reg0614 +{ + Bos0614Register reg; + struct SenseDataBits0614 bit; +}; + +//*********************************************** +// SENSEDATA2 register - address 0x1A +//*********************************************** + +union SenseData2Reg0614 +{ + Bos0614Register reg; + struct SenseDataBits0614 bit; +}; + +//*********************************************** +// SENSEDATA3 register - address 0x1B +//*********************************************** + +union SenseData3Reg0614 +{ + Bos0614Register reg; + struct SenseDataBits0614 bit; +}; + +//*********************************************** +// SENSERAW0 register - address 0x1C +//*********************************************** +struct SenseRawBits0614 +{ + uint16_t data: 10; // 9:0 Sensing data + const uint16_t reserved: 6; // 15:10 Reserved + +}; + +union SenseRaw0Reg0614 +{ + Bos0614Register reg; + struct SenseRawBits0614 bit; +}; + +//*********************************************** +// SENSERAW1 register - address 0x1D +//*********************************************** + +union SenseRaw1Reg0614 +{ + Bos0614Register reg; + struct SenseRawBits0614 bit; +}; + +//*********************************************** +// SENSERAW2 register - address 0x1E +//*********************************************** + +union SenseRaw2Reg0614 +{ + Bos0614Register reg; + struct SenseRawBits0614 bit; +}; + +//*********************************************** +// SENSERAW3 register - address 0x1F +//*********************************************** + +union SenseRaw3Reg0614 +{ + Bos0614Register reg; + struct SenseRawBits0614 bit; +}; + +//*********************************************** +// KPA register - address 0x20 +//*********************************************** +struct KpaBits0614 +{ + uint16_t kpa: 8; // 7:0 Internal Parameter + uint16_t fswMax: 2; // 9:8 Boost converter max frequency + uint16_t sb: 2; // 11:10 Boost converter blanking time + const uint16_t reserved: 4; // 15:12 Reserved +}; + +union KpaReg0614 +{ + Bos0614Register reg; + struct KpaBits0614 bit; +}; + +//*********************************************** +// KP_KI register - address 0x21 +//*********************************************** +struct KpKiBits0614 +{ + uint16_t kp: 11; // 10:0 Internal Parameter + uint16_t kiBase: 4; // 14:11 Internal Parameter + const uint16_t reserved: 1; // 15 Reserved +}; + +union KpKiReg0614 +{ + Bos0614Register reg; + struct KpKiBits0614 bit; +}; + +//*********************************************** +// KP_KI REMAP register - address 0x21 +//*********************************************** +struct KpKiRmBits0614 +{ + uint16_t ls: 1; // 0 Low Side switch control + uint16_t hs: 1; // 1 High Side switch control + uint16_t ic: 1; // 2 Turn on/off current source + uint16_t adcEn: 1; // 3 Turn on/off HV feedback ADC + uint16_t enfb: 1; // 4 Turn on/off HV feedback amplifier + uint16_t cp5: 1; // 5 Turn on/off CP5 + uint16_t cp10: 1; // 6 Turn on/off CP10 + uint16_t uvlo: 1; // 7 Turn on/off UVLO comparators + uint16_t temp: 1; // 8 Turn on/off temp sensor, current, zvs, UniPower comparator + uint16_t uni: 1; // 9 Controls UniPower switch + uint16_t adcEnSense: 1; // 10 Turn on/off sense feedback Adc + uint16_t senseAmp: 1; // 11 Turn on/off sense amplifier + uint16_t maxSense: 2; // 13:12 Select which channel is sensed + const uint16_t reserved: 2; // 15:14 Reserved +}; + +union KpKiRmReg0614 +{ + Bos0614Register reg; + struct KpKiRmBits0614 bit; +}; + +//*********************************************** +// DEADTIME register - address 0x22 - REV_B +//*********************************************** +struct DeadTimeBits0614RevB +{ + uint16_t dls: 5; // 4:0 Power switch deadtime (low side) + uint16_t dhs: 7; // 11:5 Power switch deadtime (high side) + const uint16_t reserved: 4; // 15:12 Reserved +}; + +union DeadTimeReg0614RevB +{ + Bos0614Register reg; + struct DeadTimeBits0614RevB bit; +}; + +//*********************************************** +// DEADTIME register - address 0x22 - REV_C +//*********************************************** +struct DeadTimeBits0614RevC +{ + uint16_t dls: 5; // 4:0 Power switch deadtime (low side) + uint16_t dhs: 7; // 11:5 Power switch deadtime (high side) + uint16_t adSense0: 1; // 12 Compensate charges sinked by the IC, to simulate a higher impedance device + uint16_t adSense1: 1; // 13 Takes into account signals that have a valid slope +/- 4.4 mv/ms for at least 10 ms + uint16_t adSense2: 1; // 14 Enable extrapolation of the points between segments + uint16_t adSense3: 1; // 15 Enable to stitch together segment of the sensed signal +}; + +union DeadTimeReg0614RevC +{ + Bos0614Register reg; + struct DeadTimeBits0614RevC bit; +}; + +//*********************************************** +// DEADTIME register - address 0x22 - Common +//*********************************************** +struct DeadTimeBits0614Common +{ + uint16_t dls: 5; // 4:0 Power switch deadtime (low side) + uint16_t dhs: 7; // 11:5 Power switch deadtime (high side) + const uint16_t reserved: 4; // 15:12 Reserved +}; + +union DeadTimeReg0614Common +{ + Bos0614Register reg; + struct DeadTimeBits0614Common bit; +}; + +//*********************************************** +// DEADTIME REMAP register - address 0x22 +//*********************************************** +struct DeadTimeRmBits0614 +{ + uint16_t dac_value: 9; // 8:0 Values of output current of the DAC + uint16_t s: 1; // 9 Sign bit of the current DAC + const uint16_t reserved: 6; // 15:10 Reserved +}; + +union DeadTimeRmReg0614 +{ + Bos0614Register reg; + struct DeadTimeRmBits0614 bit; +}; + +//*********************************************** +// PARCAP register - address 0x23 +//*********************************************** +struct ParcapBits0614 +{ + uint16_t IONSCALE: 8; // 7:0 Calculated parasitic capacitance + uint16_t PARCAP: 8; // 15:8 Low Side Gate Strength +}; + +union ParcapReg0614 +{ + Bos0614Register reg; + struct ParcapBits0614 bit; +}; + +//*********************************************** +// PARCAP REMAP register - address 0x23 +//*********************************************** +struct ParcapRmBits0614 +{ + uint16_t lsOut: 4; // 3:0 Set the low side switch of each channel + const uint16_t reserved: 12; // 15:4 Reserved +}; + +union ParcapRmReg0614 +{ + Bos0614Register reg; + struct ParcapRmBits0614 bit; +}; + +//*********************************************** +// SUP_RISE register - address 0x24 - REV_C +//*********************************************** +struct SupRiseBits0614RevB +{ + uint16_t tiRise: 6; // 5:0 Calculated proportional gain for the offset + uint16_t vin: 5; // 10:6 Calculated digital representation of the supply voltage + uint16_t ib: 1; // 11 Current boundary to Ion + uint16_t cp5: 1; // 12 5V pump + uint16_t ofs: 1; // 13 Offset generation + const uint16_t reserved: 2; // 15:14 Reserved +}; + +union SupRiseReg0614RevB +{ + Bos0614Register reg; + struct SupRiseBits0614RevB bit; +}; + +//*********************************************** +// SUP_RISE register - address 0x24 - REV_C +//*********************************************** +struct SupRiseBits0614RevC +{ + uint16_t tiRise: 6; // 5:0 Calculated proportional gain for the offset + uint16_t vin: 5; // 10:6 Calculated digital representation of the supply voltage + uint16_t lp: 1; // 11 Decides if analog runs in low power mode when sensing + uint16_t cp5: 1; // 12 5V pump + uint16_t ofs: 1; // 13 Offset generation + const uint16_t reserved: 2; // 15:14 Reserved +}; + +union SupRiseReg0614RevC +{ + Bos0614Register reg; + struct SupRiseBits0614RevC bit; +}; + +//*********************************************** +// SUP_RISE register - address 0x24 - Common +//*********************************************** +struct SupRiseBits0614Common +{ + uint16_t tiRise: 6; // 5:0 Calculated proportional gain for the offset + uint16_t vin: 5; // 10:6 Calculated digital representation of the supply voltage + const uint16_t reserved0: 1; // 11 Reserved + uint16_t cp5: 1; // 12 5V pump + uint16_t ofs: 1; // 13 Offset generation + const uint16_t reserved1: 2; // 15:14 Reserved +}; + +union SupRiseReg0614Common +{ + Bos0614Register reg; + struct SupRiseBits0614Common bit; +}; + +//*********************************************** +// TRIM register - address 0x25 +//*********************************************** +struct TrimBits0614 +{ + uint16_t trimReg: 3; // 2:0 1.8V regulator trimming bits in 2's complement + uint16_t trimOsc: 7; // 9:3 Oscillator trimming bits in 2's complement + uint16_t opt0: 1; // 10 Option number of channel 1=2 0=4 + uint16_t opt1: 1; // 11 Option sensing enabled + uint16_t opt2: 1; // 12 Option WFS enabled + uint16_t opt3: 1; // 13 Option SPI or I3C + uint16_t trimW: 2; // 15:14 On demand trim control +}; + +union TrimReg0614 +{ + Bos0614Register reg; + struct TrimBits0614 bit; +}; + +//*********************************************** +// CHIP_ID register - address 0x26 +//*********************************************** +struct ChipIdBits0614 +{ + uint16_t chipId: 16; // 15:0 REV A = 0 error +}; + +union ChipIdReg0614 +{ + Bos0614Register reg; + struct ChipIdBits0614 bit; +}; + +//*********************************************** +// VFEEDBACK register - address 0x28 +//*********************************************** +struct VFeedbackBits0614 +{ + uint16_t vFeedback: 10; // 9:0 Voltage feedback + uint16_t refChReg: 4; // 13:10 State of the four channel + const uint16_t reserved: 2; // 15:14 Reserved +}; + +union VFeedbackReg0614 +{ + Bos0614Register reg; + struct VFeedbackBits0614 bit; +}; + +//*********************************************** +// FIFO_STATE register - address 0x29 +//*********************************************** +struct FifoStateBits0614 +{ + uint16_t fifoSpace: 10; // 9:0 Space available + uint16_t empty: 1; // 10 FIFO is empty + uint16_t full: 1; // 11 FIFO is full + uint16_t error: 1; // 12 Error, any type + const uint16_t reserved: 3; // 15:13 Reserved +}; + +union FifoStateReg0614 +{ + Bos0614Register reg; + struct FifoStateBits0614 bit; +}; + +//*********************************************** +// AUTO_STATE register - address 0x2A +//*********************************************** +struct AutoStateBits0614 +{ + uint16_t playChannels: 4; // 3:0 State of the 4 channels + uint16_t wave: 3; // 6:4 Waveform ID being played + uint16_t rqsPlay: 1; // 7 Request to play triggered + uint16_t pressRelease: 4; // 11:8 State of sense channel x + const uint16_t reserved: 4; // 15:12 Reserved + +}; + +union AutoStateReg0614 +{ + Bos0614Register reg; + struct AutoStateBits0614 bit; +}; + +//*********************************************** +// RAM_DATA_RAW register - address 0x44 (Hidden) - REV_B +//*********************************************** +//*********************************************** +// RAM_DATA register - address 0x2B - REV_C +//*********************************************** +struct RamDataBits0614 +{ + uint16_t ramData: 16; // 16:0 Reserved +}; + +union RamDataReg0614 +{ + Bos0614Register reg; + struct RamDataBits0614 bit; +}; + +//*********************************************** +// SENS_OFFSET register - address 0x39 (Hidden) - REV_B +//*********************************************** +//*********************************************** +// SENSE_OFFSET register - address 0x2C - REV_C +//*********************************************** +struct SenseOffsetBits0614 +{ + uint16_t senseOffset: 9; // 8:0 Sense feedback path offset + const uint16_t reserved: 7; // 15:9 Reserved +}; + +union SenseOffsetReg0614 +{ + Bos0614Register reg; + struct SenseOffsetBits0614 bit; +}; + +//*********************************************** +// BIST register - address 0x30 (Hidden) +//*********************************************** +struct BistBits0614 +{ + uint16_t romAddr: 2; // 1:0 ROM results address + uint16_t romResult: 1; // 2 ROM result mode select + uint16_t romBistDone: 1; // 3 Bist done + uint16_t romBistStart: 1; // 4 Start ROM BIST + uint16_t ramBistFailed: 1; // 5 RAM BIST FAILED + uint16_t ramDone: 1; // 6 RAM BIST Completed + uint16_t ramStart: 1; // 7 Start RAM BIST + const uint16_t reserved: 8; // 15:8 Reserved +}; + +union BistReg0614 +{ + Bos0614Register reg; + struct BistBits0614 bit; +}; + +//*********************************************** +// BISTRES register - address 0x31 (Hidden) +//*********************************************** +struct BistResBits0614 +{ + uint16_t result: 12; // 11:0 ROM BIST signature +}; + +union BistResReg0614 +{ + Bos0614Register reg; + struct BistResBits0614 bit; +}; + +//*********************************************** +// DEBUG register - address 0x32 (Hidden) - REV_B +//*********************************************** +struct DebugBits0614RevB +{ + uint16_t db: 1; // 0 IC in debug mode + uint16_t ean: 3; // 3:1 Assign Internal node to TA + uint16_t zps: 1; // 4 Keep ZPS active + uint16_t gpio0M: 1; // 5 MSB settings of GPIO + uint16_t gpio1M: 1; // 6 MSB settings of GPIO + uint16_t gpio2M: 1; // 7 MSB settings of GPIO + uint16_t gpio3M: 1; // 8 MSB settings of GPIO + const uint16_t reserved: 1; // 9 Reserved + uint16_t access: 6; // 15:10 Enable/Disable Debug mode 0x3A pour mettre en mode cache 11 1010 + +}; + +union DebugReg0614RevB +{ + Bos0614Register reg; + struct DebugBits0614RevB bit; +}; + +//*********************************************** +// DEBUG register - address 0x32 (Hidden) - REV_C +//*********************************************** +struct DebugBits0614RevC +{ + uint16_t db: 1; // 0 IC in debug mode + uint16_t ean: 3; // 3:1 Assign Internal node to TA + uint16_t zps: 1; // 4 Keep ZPS active + uint16_t gpio0M: 1; // 5 MSB settings of GPIO + uint16_t gpio1M: 1; // 6 MSB settings of GPIO + uint16_t gpio2M: 1; // 7 MSB settings of GPIO + uint16_t gpio3M: 1; // 8 MSB settings of GPIO + uint16_t diff: 1; // 9 Active shutdown of sensing amplifier when it is not needed to sense + uint16_t access: 6; // 15:10 Enable/Disable Debug mode 0x3A pour mettre en mode cache 11 1010 + +}; + +union DebugReg0614RevC +{ + Bos0614Register reg; + struct DebugBits0614RevC bit; +}; + +//*********************************************** +// DEBUG register - address 0x32 (Hidden) - Common +//*********************************************** +struct DebugBits0614Common +{ + uint16_t db: 1; // 0 IC in debug mode + uint16_t ean: 3; // 3:1 Assign Internal node to TA + uint16_t zps: 1; // 4 Keep ZPS active + uint16_t gpio0M: 1; // 5 MSB settings of GPIO + uint16_t gpio1M: 1; // 6 MSB settings of GPIO + uint16_t gpio2M: 1; // 7 MSB settings of GPIO + uint16_t gpio3M: 1; // 8 MSB settings of GPIO + const uint16_t reserved: 1; // 9 Reserved + uint16_t access: 6; // 15:10 Enable/Disable Debug mode 0x3A pour mettre en mode cache 11 1010 + +}; + +union DebugReg0614Common +{ + Bos0614Register reg; + struct DebugBits0614Common bit; +}; + +//*********************************************** +// THRESH register - address 0x33 (Hidden) - REV_B +//*********************************************** +struct ThreshBits0614RevB +{ + uint16_t vThresh: 7; // 6:0 Internal parameter + uint16_t threshError: 5; // 11:7 + uint16_t trimMode: 2; // 13:12 Trimming operation mode + uint16_t slp: 1; // 14 Turn off ZVS and ADC + uint16_t vddh: 1; // 15 Charges through active switch +}; + +union ThreshReg0614RevB +{ + Bos0614Register reg; + struct ThreshBits0614RevB bit; +}; + +//*********************************************** +// THRESH register - address 0x33 (Hidden) - REV_C +//*********************************************** +struct ThreshBits0614RevC +{ + uint16_t vThresh: 7; // 6:0 Internal parameter + uint16_t threshError: 5; // 11:7 + uint16_t trimMode: 2; // 13:12 Trimming operation mode + const uint16_t reserved: 1; // 14 Reserved + uint16_t vddh: 1; // 15 Charges through active switch +}; + +union ThreshReg0614RevC +{ + Bos0614Register reg; + struct ThreshBits0614RevC bit; +}; + +//*********************************************** +// THRESH register - address 0x33 (Hidden) - Common +//*********************************************** +struct ThreshBits0614Common +{ + uint16_t vThresh: 7; // 6:0 Internal parameter + uint16_t threshError: 5; // 11:7 + uint16_t trimMode: 2; // 13:12 Trimming operation mode + const uint16_t reserved: 1; // 14 Reserved + uint16_t vddh: 1; // 15 Charges through active switch +}; + +union ThreshReg0614Common +{ + Bos0614Register reg; + struct ThreshBits0614Common bit; +}; + +//*********************************************** +// REG38 register - address 0x38 (Hidden) +//*********************************************** +struct Reg38Bits0614 +{ + uint16_t hvOffset: 10; // 9:0 Offset of the main ADC + const uint16_t reserved: 6; // 15:10 Reserved +}; + +union Reg38Reg0614 +{ + Bos0614Register reg; + struct Reg38Bits0614 bit; +}; + + +//*********************************************** +// CALIB_DATA register - address 0x3A (Hidden) +//*********************************************** +struct CalibDataBits0614 +{ + uint16_t dacLs: 6; // 5:0 Dac Calibration LS + uint16_t dacHs: 6; // 11:6 Dac Calibration HS + const uint16_t reserved: 4; // 15:12 Reserved +}; + +union CalibDataReg0614 +{ + Bos0614Register reg; + struct CalibDataBits0614 bit; +}; + +//*********************************************** +// REG3B register - address 0x3B (Hidden) +//*********************************************** +struct Reg3BBits0614 +{ + uint16_t err: 9; // 8:0 Sense signal ??? + uint16_t refCh: 4; // 12:9 State of the four channel + uint16_t ccm: 1; // 13 Driver in CCM or DCM mode + uint16_t dcm: 1; // 14 Switching controller + uint16_t maxPower: 1; // 15 Max Power +}; + +union Reg3BReg0614 +{ + Bos0614Register reg; + struct Reg3BBits0614 bit; +}; + +//*********************************************** +// REG3C register - address 0x3C (Hidden) +//*********************************************** +struct Reg3CBits0614 +{ + uint16_t currentAdc: 9; // 8:0 Current sent to the ADC mA + uint16_t s: 1; // 9 Sign for ADC + uint16_t ccm: 1; // 10 Driver operating mode + uint16_t dcm: 1; // 11 Switch Controller + uint16_t ovv: 1; // 12 Over Voltage Bit + uint16_t overState: 1; // 13 Converter Status + uint16_t mode: 2; // 15:14 Driver current operating mode +}; + +union Reg3CReg0614 +{ + Bos0614Register reg; + struct Reg3CBits0614 bit; +}; + +//*********************************************** +// REG3D register - address 0x3D (Hidden) +//*********************************************** +struct Reg3DBits0614 +{ + uint16_t minCurrent: 10; // 9:0 Minimum current to turn on LS + uint16_t ccm: 1; // 10 Driver operating mode + uint16_t dcm: 1; // 11 Switch Controller + uint16_t ovv: 1; // 12 Over Voltage Bit + uint16_t overState: 1; // 13 Converter Status + uint16_t mode: 2; // 15:14 Driver current operating mode +}; + +union Reg3DReg0614 +{ + Bos0614Register reg; + struct Reg3DBits0614 bit; +}; + +//*********************************************** +// REG3E register - address 0x3E (Hidden) +//*********************************************** +struct Reg3EBits0614 +{ + uint16_t err: 9; // 8:0 Error between reference and feedback + uint16_t pulseCtl: 7; // 15:9 Switch pulse length +}; + +union Reg3EReg0614 +{ + Bos0614Register reg; + struct Reg3EBits0614 bit; +}; + +//*********************************************** +// REG3F register - address 0x3F (Hidden) +//*********************************************** +struct Reg3FBits0614 +{ + uint16_t blankCtl: 7; // 6:0 Blanking time delay + uint16_t deadCtl: 7; // 13:7 Dead time delay + uint16_t mode: 2; // 15:14 Current operating mode +}; + +union Reg3FReg0614 +{ + Bos0614Register reg; + struct Reg3FBits0614 bit; +}; + +//*********************************************** +// REG40 register - address 0x40 (Hidden) +//*********************************************** +struct Reg40Bits0614 +{ + uint16_t refReg: 12; // 11:0 Desired amplitude on output + uint16_t refChReg: 4; // 15:12 State of the four channel +}; + +union Reg40Reg0614 +{ + Bos0614Register reg; + struct Reg40Bits0614 bit; +}; + +//*********************************************** +// REG41 register - address 0x41 (Hidden) +//*********************************************** +struct Reg41Bits0614 +{ + uint16_t refReg: 12; // 11:0 Desired amplitude on output + uint16_t empty: 1; // 12 FIFO state + uint16_t full: 1; // 13 FIFO state + const uint16_t reserved: 2; // 15:14 Reserved +}; + +union Reg41Reg0614 +{ + Bos0614Register reg; + struct Reg41Bits0614 bit; +}; + +//*********************************************** +// REG42 register - address 0x42 (Hidden) +//*********************************************** +struct Reg42Bits0614 +{ + uint16_t err: 9; // 8:0 Error between reference and feedback + uint16_t lout: 4; // 12:9 Desired amplitude on output + uint16_t ccm: 1; // 10 Driver operating mode + uint16_t dcm: 1; // 11 Switch Controller + uint16_t maxPower: 1; // 13 Maximum power status +}; + +union Reg42Reg0614 +{ + Bos0614Register reg; + struct Reg42Bits0614 bit; +}; + +//*********************************************** +// REG43 register - address 0x43 (Hidden) +//*********************************************** +struct Reg43Bits0614 +{ + uint16_t periode: 10; // 9:0 Error between reference and feedback + uint16_t zone: 3; // 12:10 Desired amplitude on output + const uint16_t reserved: 1; // 13 Reserved + uint16_t mode: 2; // 15:14 Driver operating mode +}; + +union Reg43Reg0614 +{ + Bos0614Register reg; + struct Reg43Bits0614 bit; +}; + +//*********************************************** +// REG45 register - address 0x45 (Hidden) +//*********************************************** +struct Reg45Bits0614 +{ + uint16_t err: 9; // 8:0 Error between reference and feedback + uint16_t stmState: 2; // 10:9 Main State machine State + uint16_t uvlo: 1; // 11 Under Voltage bit + uint16_t ovv: 1; // 12 Over Voltage bit + uint16_t overState: 1; // 13 Reason why the converter stopped + uint16_t mode: 2; // 15:14 Current operating mode +}; + +union Reg45Reg0614 +{ + Bos0614Register reg; + struct Reg45Bits0614 bit; +}; + +//*********************************************** +// REG46 register - address 0x46 (Hidden) +//*********************************************** +struct Reg46Bits0614 +{ + uint16_t currentPi: 10; // 9:0 Desired current from PI controller + uint16_t ccm: 1; // 10 Driver operating mode + uint16_t ccmState: 2; // 12:11 Approximate ripple + uint16_t flagCcmState: 2; // 14:13 CCM State control + uint16_t dcm: 1; // 15 Switch controller +}; + +union Reg46Reg0614 +{ + Bos0614Register reg; + struct Reg46Bits0614 bit; +}; + +//*********************************************** +// REG47 register - address 0x47 (Hidden) +//*********************************************** +struct Reg47Bits0614 +{ + uint16_t periode: 10; // 9:0 Switching period in clk cycles + uint16_t ccm: 1; // 10 Driver operating mode + uint16_t ccmState: 2; // 12:11 Approximate ripple + uint16_t flagCcmState: 2; // 14:13 CCM State control + uint16_t dcm: 1; // 15 Switch controller +}; + +union Reg47Reg0614 +{ + Bos0614Register reg; + struct Reg47Bits0614 bit; +}; + +//*********************************************** +// Common registers structure +//*********************************************** +typedef struct +{ + union ReferenceReg0614 reference0614; + union ReferenceRmReg0614 referenceRm0614; + union IcStatusReg0614 icStatus0614; + union ReadReg0614 read0614; + union GpioxReg0614 gpiox0614; + UndefReg tc0614; + union ConfigReg0614 config0614; + union SenseConfigReg0614Common senseConfig0614; + union Sense0Reg0614 sense00614; + union Sense0PReg0614 sense0P0614; + union Sense0RReg0614 sense0R0614; + union Sense0SReg0614 sense0S0614; + union Sense1Reg0614 sense10614; + union Sense1PReg0614 sense1P0614; + union Sense1RReg0614 sense1R0614; + union Sense1SReg0614 sense1S0614; + union Sense2Reg0614 sense20614; + union Sense2PReg0614 sense2P0614; + union Sense2RReg0614 sense2R0614; + union Sense2SReg0614 sense2S0614; + union Sense3Reg0614 sense30614; + union Sense3PReg0614 sense3P0614; + union Sense3RReg0614 sense3R0614; + union Sense3SReg0614 sense3S0614; + union SenseStatusReg0614 senseStatus0614; + union SenseData0Reg0614 senseData00614; + union SenseData1Reg0614 senseData10614; + union SenseData2Reg0614 senseData20614; + union SenseData3Reg0614 senseData30614; + union SenseRaw0Reg0614 senseRaw00614; + union SenseRaw1Reg0614 senseRaw10614; + union SenseRaw2Reg0614 senseRaw20614; + union SenseRaw3Reg0614 senseRaw30614; + union KpaReg0614 kpa0614; + union KpKiReg0614 kpKi0614; + union KpKiRmReg0614 kpKiRm0614; + union DeadTimeReg0614Common deadTime0614; + union DeadTimeRmReg0614 deadTimeRm0614; + union ParcapReg0614 parcap0614; + union ParcapRmReg0614 parcapRm0614; + union SupRiseReg0614Common supRise0614; + union TrimReg0614 trim0614; + union ChipIdReg0614 chipId0614; + union VFeedbackReg0614 vFeedback0614; + union FifoStateReg0614 fifoState0614; + union AutoStateReg0614 autoState0614; + union BistReg0614 bist0614; + union BistResReg0614 bistRes0614; + union DebugReg0614Common debug0614; + union ThreshReg0614Common thresh0614; + union Reg38Reg0614 reg380614; + union SenseOffsetReg0614 senseOffset0614; + union CalibDataReg0614 calibData0614; + union Reg3BReg0614 reg3B0614; + union Reg3CReg0614 reg3C0614; + union Reg3DReg0614 reg3D0614; + union Reg3EReg0614 reg3E0614; + union Reg3FReg0614 reg3F0614; + union Reg40Reg0614 reg400614; + union Reg41Reg0614 reg410614; + union Reg42Reg0614 reg420614; + union Reg43Reg0614 reg430614; + union RamDataReg0614 ramData0614; + union Reg45Reg0614 reg450614; + union Reg46Reg0614 reg460614; + union Reg47Reg0614 reg470614; +} Bos0614CommonRegister; + +//*********************************************** +// Rev B registers structure +//*********************************************** +typedef struct +{ + union ReferenceReg0614 reference0614; + union ReferenceRmReg0614 referenceRm0614; + union IcStatusReg0614 icStatus0614; + union ReadReg0614 read0614; + union GpioxReg0614 gpiox0614; + union TcReg0614RevB tc0614; + union ConfigReg0614 config0614; + union SenseConfigReg0614RevB senseConfig0614; + union Sense0Reg0614 sense00614; + union Sense0PReg0614 sense0P0614; + union Sense0RReg0614 sense0R0614; + union Sense0SReg0614 sense0S0614; + union Sense1Reg0614 sense10614; + union Sense1PReg0614 sense1P0614; + union Sense1RReg0614 sense1R0614; + union Sense1SReg0614 sense1S0614; + union Sense2Reg0614 sense20614; + union Sense2PReg0614 sense2P0614; + union Sense2RReg0614 sense2R0614; + union Sense2SReg0614 sense2S0614; + union Sense3Reg0614 sense30614; + union Sense3PReg0614 sense3P0614; + union Sense3RReg0614 sense3R0614; + union Sense3SReg0614 sense3S0614; + union SenseStatusReg0614 senseStatus0614; + union SenseData0Reg0614 senseData00614; + union SenseData1Reg0614 senseData10614; + union SenseData2Reg0614 senseData20614; + union SenseData3Reg0614 senseData30614; + union SenseRaw0Reg0614 senseRaw00614; + union SenseRaw1Reg0614 senseRaw10614; + union SenseRaw2Reg0614 senseRaw20614; + union SenseRaw3Reg0614 senseRaw30614; + union KpaReg0614 kpa0614; + union KpKiReg0614 kpKi0614; + union KpKiRmReg0614 kpKiRm0614; + union DeadTimeReg0614RevB deadTime0614; + union DeadTimeRmReg0614 deadTimeRm0614; + union ParcapReg0614 parcap0614; + union ParcapRmReg0614 parcapRm0614; + union SupRiseReg0614RevB supRise0614; + union TrimReg0614 trim0614; + union ChipIdReg0614 chipId0614; + union VFeedbackReg0614 vFeedback0614; + union FifoStateReg0614 fifoState0614; + union AutoStateReg0614 autoState0614; + union BistReg0614 bist0614; + union BistResReg0614 bistRes0614; + union DebugReg0614RevB debug0614; + union ThreshReg0614RevB thresh0614; + union Reg38Reg0614 reg380614; + union SenseOffsetReg0614 senseOffset0614; + union CalibDataReg0614 calibData0614; + union Reg3BReg0614 reg3B0614; + union Reg3CReg0614 reg3C0614; + union Reg3DReg0614 reg3D0614; + union Reg3EReg0614 reg3E0614; + union Reg3FReg0614 reg3F0614; + union Reg40Reg0614 reg400614; + union Reg41Reg0614 reg410614; + union Reg42Reg0614 reg420614; + union Reg43Reg0614 reg430614; + union RamDataReg0614 ramData0614; + union Reg45Reg0614 reg450614; + union Reg46Reg0614 reg460614; + union Reg47Reg0614 reg470614; +} Bos0614RevBRegisters; + +//*********************************************** +// Rev C registers structure +//*********************************************** +typedef struct +{ + union ReferenceReg0614 reference0614; + union ReferenceRmReg0614 referenceRm0614; + union IcStatusReg0614 icStatus0614; + union ReadReg0614 read0614; + union GpioxReg0614 gpiox0614; + union TcReg0614RevC tc0614; + union ConfigReg0614 config0614; + union SenseConfigReg0614RevC senseConfig0614; + union Sense0Reg0614 sense00614; + union Sense0PReg0614 sense0P0614; + union Sense0RReg0614 sense0R0614; + union Sense0SReg0614 sense0S0614; + union Sense1Reg0614 sense10614; + union Sense1PReg0614 sense1P0614; + union Sense1RReg0614 sense1R0614; + union Sense1SReg0614 sense1S0614; + union Sense2Reg0614 sense20614; + union Sense2PReg0614 sense2P0614; + union Sense2RReg0614 sense2R0614; + union Sense2SReg0614 sense2S0614; + union Sense3Reg0614 sense30614; + union Sense3PReg0614 sense3P0614; + union Sense3RReg0614 sense3R0614; + union Sense3SReg0614 sense3S0614; + union SenseStatusReg0614 senseStatus0614; + union SenseData0Reg0614 senseData00614; + union SenseData1Reg0614 senseData10614; + union SenseData2Reg0614 senseData20614; + union SenseData3Reg0614 senseData30614; + union SenseRaw0Reg0614 senseRaw00614; + union SenseRaw1Reg0614 senseRaw10614; + union SenseRaw2Reg0614 senseRaw20614; + union SenseRaw3Reg0614 senseRaw30614; + union KpaReg0614 kpa0614; + union KpKiReg0614 kpKi0614; + union KpKiRmReg0614 kpKiRm0614; + union DeadTimeReg0614RevC deadTime0614; + union DeadTimeRmReg0614 deadTimeRm0614; + union ParcapReg0614 parcap0614; + union ParcapRmReg0614 parcapRm0614; + union SupRiseReg0614RevC supRise0614; + union TrimReg0614 trim0614; + union ChipIdReg0614 chipId0614; + union VFeedbackReg0614 vFeedback0614; + union FifoStateReg0614 fifoState0614; + union AutoStateReg0614 autoState0614; + union BistReg0614 bist0614; + union BistResReg0614 bistRes0614; + union DebugReg0614RevC debug0614; + union ThreshReg0614RevC thresh0614; + union Reg38Reg0614 reg380614; + union SenseOffsetReg0614 senseOffset0614; + union CalibDataReg0614 calibData0614; + union Reg3BReg0614 reg3B0614; + union Reg3CReg0614 reg3C0614; + union Reg3DReg0614 reg3D0614; + union Reg3EReg0614 reg3E0614; + union Reg3FReg0614 reg3F0614; + union Reg40Reg0614 reg400614; + union Reg41Reg0614 reg410614; + union Reg42Reg0614 reg420614; + union Reg43Reg0614 reg430614; + union RamDataReg0614 ramData0614; + union Reg45Reg0614 reg450614; + union Reg46Reg0614 reg460614; + union Reg47Reg0614 reg470614; +} Bos0614RevCRegisters; + +typedef struct +{ + union + { + Bos0614CommonRegister common; + Bos0614RevBRegisters revB; + Bos0614RevCRegisters revC; + }; +} Bos0614Registers; + +#endif /* BOS0614_REGISTER_H_ */ diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/haptic/bos1211Register.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/haptic/bos1211Register.h new file mode 100644 index 000000000000..4f2ef0f09aad --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/haptic/bos1211Register.h @@ -0,0 +1,376 @@ +/* + * BOS1211_config.h + * + * Created on: 2019-10-01 + * Author: CarlMoreau + */ + +#ifndef GIT_INCLUDE_BOS1211_CONFIG_H_ +#define GIT_INCLUDE_BOS1211_CONFIG_H_ + +#include + +#define BOS1211_NBR_OF_REGISTER (16) + +#define BOS1211_ADDRESS_REFERENCE_REG (0x0) +#define BOS1211_ADDRESS_ION_BL_REG (0x1) +#define BOS1211_ADDRESS_DEADTIME_REG (0x2) +#define BOS1211_ADDRESS_KP_REG (0x3) +#define BOS1211_ADDRESS_KPA_KI_REG (0x4) +#define BOS1211_ADDRESS_CONFIG_REG (0x5) +#define BOS1211_ADDRESS_PARCAP_REG (0x6) +#define BOS1211_ADDRESS_SUP_RISE_REG (0x7) +#define BOS1211_ADDRESS_RAM_REG (0X8) +#define BOS1211_ADDRESS_SPI_REG (0x9) +#define BOS1211_ADDRESS_SENSING_REG (0xA) +#define BOS1211_ADDRESS_SENSE_REG (0xB) +#define BOS1211_ADDRESS_IC_STATUS_REG (0xC) +#define BOS1211_ADDRESS_FIFO_STATUS_REG (0xD) +#define BOS1211_ADDRESS_TRIM_REG (0xE) +#define BOS1211_ADDRESS_CHIP_ID (0xF) +#define BOS1211_LAST_REGISTER_ADDRESS (BOS1211_ADDRESS_CHIP_ID) + +typedef struct +{ + uint16_t value: 12; + uint16_t addr: 4; +} Bos1211RegisterStruct; + +typedef union +{ + const uint16_t all; + Bos1211RegisterStruct generic; +} Bos1211Register; + +//*********************************************** +// REFERENCE register - address 0x0 +//*********************************************** +struct REFERENCE_BITS +{ + uint16_t FIFO: 12; // 11:0 Input of the FIFO +}; + +union REFERENCE_REG +{ + Bos1211Register reg; + struct REFERENCE_BITS bit; +}; + +//*********************************************** +// ION_BL register - address 0x1 +//*********************************************** +struct ION_BL_BITS +{ + uint16_t IONSCALE: 8; // 7:0 Calculated minimum current required to turn ON HS + uint16_t SB: 2; // 9:8 Boost converter Blanking time + uint16_t FSWMAX: 2; // 11:10 Boost converter maximum switching frequency +}; + +union ION_BL_REG +{ + Bos1211Register reg; + struct ION_BL_BITS bit; +}; + +//*********************************************** +// DEADTIME register - address 0x2 +//*********************************************** +struct DEADTIME_BITS +{ + uint16_t DLS: 5; // 4:0 Power switch deadtime (low side) + uint16_t DHS: 7; // 11:5 Power switch deadtime (high side) +}; + +union DEADTIME_REG +{ + Bos1211Register reg; + struct DEADTIME_BITS bit; +}; + +//*********************************************** +// KP register - address 0x3 +//*********************************************** +struct KP_BITS +{ + uint16_t KP: 11; // 10:0 Physical value of the integrated PI controller proportional gain + uint16_t SQ: 1; // 11 Square wave playback +}; + +union KP_REG +{ + Bos1211Register reg; + struct KP_BITS bit; +}; +//*********************************************** +// KPA_KI register - address 0x4 +//*********************************************** +struct KPA_KI_BITS +{ + uint16_t KPA: 8; // 7:0 Calculated value of the integrated PI controller proportional gain + uint16_t KIBASE: 4; // 11:8 Calculated pole location of the integrated PI controller +}; + +union KPA_KI_REG +{ + Bos1211Register reg; + struct KPA_KI_BITS bit; +}; + +//*********************************************** +// CONFIG register - address 0x5 +//*********************************************** +typedef enum +{ + SamplingRate_1024Ksps = 0, + SamplingRate_512Ksps, + SamplingRate_256Ksps, + SamplingRate_128Ksps, + SamplingRate_64Ksps, + SamplingRate_32Ksps, + SamplingRate_16Ksps, + SamplingRate_8Ksps, +} SamplingRate; + + +struct CONFIG_BITS +{ + SamplingRate PLAY: 3; // 2:0 Waveform play back speed + uint16_t DS: 1; // 3 Power mode when not playing waveforms + uint16_t OE: 1; // 4 Enable/Disable waveform playback + uint16_t RST: 1; // 5 Software Reset + uint16_t LOCK: 1; // 6 Register lock + uint16_t SYNC: 1; // 7 Register Sync + uint16_t AUTO: 1; // 8 Register Auto + uint16_t SENSE: 1; // 9 Register Sense + uint16_t SIGN: 1; // 10 Register Sign + uint16_t ONCOMP: 1; // 11 Register OnComp +}; + +union CONFIG_REG +{ + Bos1211Register reg; + struct CONFIG_BITS bit; +}; + +//*********************************************** +// PARCAP register - address 0x6 +//*********************************************** +struct PARCAP_BITS +{ + uint16_t PARCAP: 8; // 7:0 Calculated parasitic capacitance + uint16_t SLS: 2; // 9:8 Low Side Gate Strength + uint16_t SHS: 2; // 11:10 High Side Gate Strength +}; + +union PARCAP_REG +{ + Bos1211Register reg; + struct PARCAP_BITS bit; +}; + +//*********************************************** +// SUP_RISE register - address 0x7 +//*********************************************** +struct SUP_RISE_BITS +{ + uint16_t TI_RISE: 6; // 5:0 Calculated proportional gain for the offset + uint16_t VIN: 5; // 10:6 Calculated digital representation of the supply voltage + uint16_t TOUT: 1; // 11 Automatic TimeOut of the waveform (1=Enable,0=Disable) +}; + +union SUP_RISE_REG +{ + Bos1211Register reg; + struct SUP_RISE_BITS bit; +}; + +typedef enum +{ + BOSMode_Direct = 0x0, + BOS1211Mode_FIFO, + BOS1211Mode_RAM_Playback, + BOS1211Mode_RAM_Synthesis, +} BOS1211Mode; + +typedef enum +{ + BOSRamMode_RegisterBank = 0x0, + BOSRamMode_WSFBank, +} BOSRamMode; + +//*********************************************** +// RAM register - address 0x8 +//*********************************************** +struct RAM_BITS +{ + BOS1211Mode MODE: 2; // 1:0 Ram Behavior (0=Direct, 1=FIFo, 2=Direct Play, 3=Synthesis) + BOSRamMode RAMSEL: 1; // 2 Select RAM (1= RAM controller, 0= Bank Register) + uint16_t rsvd1: 1; // 3 reserved + uint16_t ROMRESULTADD: 2; // 5:4 ROM bist signature Address (0-3) Signature (57ed8ec6) + uint16_t ROMRESULT: 1; // 6 ROM Test result ( 0=Not in result mode, 1= In result mode) + uint16_t ROMBISTDONE: 1; // 7 ROM Bist done (0= not done, 1=done) + uint16_t ROMBISTSTART: 1; // 8 ROM Bist Start (0=No test, 1 = RUN test) Only when OE=0 + uint16_t RAMBISTFAIL: 1; // 9 RAM Bist Failed (0=Test Failed, 1 = Test Passed) + uint16_t RAMBISTDONE: 1; // 10 RAM Bist Done (0=Not Done, 1=Done) + uint16_t RAMBISTSTART: 1; // 11 RAM Bist Start (0= No Test , 1 =RUn test) Only when OE=0 +}; + +union RAM_REG +{ + Bos1211Register reg; + struct RAM_BITS bit; +}; + +typedef enum +{ + Bos1211GpoMode_Not_Defined = 0, + Bos1211GpoMode_SenseTrigger, + BOS1211GpoMode_WaveformDone, + Bos1211GpoMode_Error, + Bos1211GpoMode_MaxPower, + Bos1211GpoMode_NextData = 7 +} Bos1211GpoMode; + +//*********************************************** +// SPI register - address 0x9 +//*********************************************** +struct SPI_BITS +{ + Bos1211GpoMode gpo: 3; // 4:0 Control the output on GPO pad + const uint16_t reserved: 2; + uint16_t SHORT: 2; // 6:5 Length of time to zero the piezo + uint16_t BC: 5; // 7:11 Address of internal register whose content is shifted out on SPI +}; + +union SPI_REG +{ + Bos1211Register reg; + struct SPI_BITS bit; +}; + +//*********************************************** +// SENSING register - address 0xA +//*********************************************** +struct SENSING_BITS +{ + uint16_t STHRESH: 9; // 8:0 Differential voltage required to detect an event + uint16_t REP: 3; // 11:9 Amount of time the sense voltage needs to be above STHRESH +}; + +union SENSING_REG +{ + Bos1211Register reg; + struct SENSING_BITS bit; +}; + +//*********************************************** +// SENSE register - address 0xB +//*********************************************** +struct SENSE_BITS +{ + uint16_t VFEEDBACK: 10; // 9:0 Voltage feedback on 10 bit + uint16_t STATE: 2; // 11:10 State of the controller +}; + +union SENSE_REG +{ + Bos1211Register reg; + struct SENSE_BITS bit; +}; + +//*********************************************** +// IC_STATUS register - address 0xC +//*********************************************** +struct IC_STATUS_BITS +{ + uint16_t EMPTY: 1; // 0 Voltage feedback on 10 bit + uint16_t FULL: 1; // 1 State of the controller + uint16_t SC: 1; // 2 Register address + uint16_t OVLO12: 1; // 3 Register address + uint16_t UVLO12: 1; // 4 Register address + uint16_t UVLO5: 1; // 5 Register address + uint16_t IDAC: 1; // 6 Register address + uint16_t MAX_POWER: 1; // 7 Register address + uint16_t OVT: 1; // 8 Register address + uint16_t OVV: 1; // 9 Register address + uint16_t STATE: 2; // 11:10 Register address +}; + +union IC_STATUS_REG +{ + Bos1211Register reg; + struct IC_STATUS_BITS bit; +}; + +//*********************************************** +// FIFO_STATUS register - address 0xD +//*********************************************** +struct FIFO_STATUS_BITS +{ + uint16_t FIFO_SPACE: 10; // 9:0 Space available + uint16_t EMPTY: 1; // 10 FIFO is empty + uint16_t ERROR: 1; // 11 ERROR (1=error, 0=no error) +}; + +union FIFO_STATUS_REG +{ + Bos1211Register reg; + struct FIFO_STATUS_BITS bit; +}; + +//*********************************************** +// TRIM register - address 0xE +//*********************************************** +struct TRIM_BITS +{ + uint16_t TRIM_REG: 3; // 2:0 1.8V regulator trimming bits in 2's complement + uint16_t TRIM_OSC: 7; // 9:3 Oscillator trimming bits in 2's complement + uint16_t TRIMRW: 2; // 11:10 On demand trim control +}; + +union TRIM_REG +{ + Bos1211Register reg; + struct TRIM_BITS bit; +}; + + +//*********************************************** +// CHIP_ID register - address 0xF +//*********************************************** +struct CHIP_ID_BITS +{ + uint16_t CHIPID: 6; // 5:0 ID (0x30= BOS1211, 0x32=BOS1212) + uint16_t rsvd: 6; // 6 Access bit 0 +}; + +union CHIP_ID_REG +{ + Bos1211Register reg; + struct CHIP_ID_BITS bit; +}; + +//*********************************************** +// Complete registers structure +//*********************************************** +typedef struct +{ + union REFERENCE_REG REFERENCE; // REFERENCE register + union ION_BL_REG ION_BL; // ION_BL register + union DEADTIME_REG DEADTIME; // DEADTIME register + union KP_REG KP; // KP register + union KPA_KI_REG KPA_KI; // KPA_KI register + union CONFIG_REG CONFIG; // CONFIG register + union PARCAP_REG PARCAP; // PARCAP register + union SUP_RISE_REG SUP_RISE; // SUP_RISE register + union RAM_REG RAM; // RAM register + union SPI_REG SPI; // SPI register + union SENSING_REG SENSING; // SENSING register + union SENSE_REG SENSE; // SENSE register + union IC_STATUS_REG IC_STATUS; // IC_STATUS register + union FIFO_STATUS_REG FIFO_STATUS; // FIFO_STATUS register + union TRIM_REG TRIM; // TRIM register + union CHIP_ID_REG CHIP_ID; // CHIP ID register +} BOS1211_REGS; + +#endif diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/haptic/bos1901Register.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/haptic/bos1901Register.h new file mode 100644 index 000000000000..e8f381b6a39a --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/haptic/bos1901Register.h @@ -0,0 +1,468 @@ +// +// Description: BOS1901 Registers +// Created on 05/07/2020 +// Copyright (c) 2020 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#ifndef DKCORE_BOS1901REGISTER_H +#define DKCORE_BOS1901REGISTER_H + +#include + +#include "utils/registerUtil.h" + +#define TI_RISE_DEFAULT_VALUE (0x27) +#define VDD_DEFAULT_VALUE (0x05) + +#define REGISTER_VALUE_MASK (0xFFF) + +typedef enum +{ + BOS1901AddressReference = 0, + BOS1901AddressIonBl, + BOS1901AddressDeadTime, + BOS1901AddressKp, + BOS1901AddressKpaKi, + BOS1901AddressConfig, + BOS1901AddressParCap, + BOS1901AddressSupRise, + BOS1901AddressDac, + BOS1901AddressThreshold, + BOS1901AddressTmKi, + BOS1901AddressDebug, + BOS1901AddressIcStatus, + BOS1901AddressSense, + BOS1901AddressTrim, + BOS1901AddressAccess, + BOS1901NbRegisters +} BOS1901AddressEnum; + + +#define ADDRESS_BOS1901_REFERENCE_REG (0x00) +#define ADDRESS_BOS1901_ION_BL_REG (0x01) +#define ADDRESS_BOS1901_DEADTIME_REG (0x02) +#define ADDRESS_BOS1901_KP_REG (0x03) +#define ADDRESS_BOS1901_KPA_KI_REG (0x04) +#define ADDRESS_BOS1901_CONFIG_REG (0x05) +#define ADDRESS_BOS1901_PARCAP_REG (0x06) +#define ADDRESS_BOS1901_SUP_RISE_REG (0x07) +#define ADDRESS_BOS1901_DAC_REG (0x08) +#define ADDRESS_BOS1901_THRESH_REG (0x09) +#define ADDRESS_BOS1901_TM_KI_REG (0x0A) +#define ADDRESS_BOS1901_DEBUG_REG (0x0B) +#define ADDRESS_BOS1901_IC_STATUS_REG (0x0C) +#define ADDRESS_BOS1901_SENSE_REG (0x0D) +#define ADDRESS_BOS1901_TRIM_REG (0x0E) +#define ADDRESS_BOS1901_ACCESS_REG (0x0F) +#define BOS1901_LAST_REGISTER_ADDRESS (ADDRESS_BOS1901_ACCESS_REG) + +#define BOS1901_NBR_OF_REGISTER (16) + + +typedef struct +{ + uint16_t value: 12; + uint16_t addr: 4; +} Bos1901RegisterStruct; + +typedef union +{ + const uint16_t all; + Bos1901RegisterStruct generic; +} Bos1901Register; + +//*********************************************** +// REFERENCE register - address 0x0 +//*********************************************** +struct REFERENCE_BITS_BOS1901 +{ + uint16_t FIFO: 12; // 11:0 Input of the FIFO +}; + +union REFERENCE_REG_BOS1901 +{ + Bos1901Register reg; + struct REFERENCE_BITS_BOS1901 bit; +}; + +//*********************************************** +// ION_BL register - address 0x1 +//*********************************************** +struct ION_BL_BITS_BOS1901 +{ + uint16_t IONSCALE: 8; // 7:0 Calculated minimum current required to turn ON HS + uint16_t SB: 2; // 9:8 Boost converter Blanking time + uint16_t FSWMAX: 2; // 11:10 Boost converter maximum switching frequency +}; + +union ION_BL_REG_BOS1901 +{ + Bos1901Register reg; + struct ION_BL_BITS_BOS1901 bit; +}; + +//*********************************************** +// DEADTIME register - address 0x2 +//*********************************************** +struct DEADTIME_BITS_BOS1901 +{ + uint16_t DLS: 5; // 4:0 Power switch deadtime (low side) + uint16_t DHS: 7; // 11:5 Power switch deadtime (high side) +}; + +union DEADTIME_REG_BOS1901 +{ + Bos1901Register reg; + struct DEADTIME_BITS_BOS1901 bit; +}; + +//*********************************************** +// KP register - address 0x3 +//*********************************************** +struct KP_BITS_BOS1901 +{ + uint16_t KP: 11; // 10:0 Physical value of the integrated PI controller proportional gain + uint16_t SQ: 1; // 11 Square wave playback +}; + +union KP_REG_BOS1901 +{ + Bos1901Register reg; + struct KP_BITS_BOS1901 bit; +}; +//*********************************************** +// KPA_KI register - address 0x4 +//*********************************************** +struct KPA_KI_BITS_BOS1901 +{ + uint16_t KPA: 8; // 7:0 Calculated value of the integrated PI controller proportional gain + uint16_t KIBASE: 4; // 11:8 Calculated pole location of the integrated PI controller +}; + +union KPA_KI_REG_BOS1901 +{ + Bos1901Register reg; + struct KPA_KI_BITS_BOS1901 bit; +}; + +//*********************************************** +// CONFIG register - address 0x5 +//*********************************************** + +typedef enum +{ + BOS1901PlayBackSpeed1024kbps = 0, + BOS1901PlayBackSpeed512kbps, + BOS1901PlayBackSpeed256kbps, + BOS1901PlayBackSpeed128kbps, + BOS1901PlayBackSpeed64kbps, + BOS1901PlayBackSpeed32kbps, + BOS1901PlayBackSpeed16kbps, + BOS1901PlayBackSpeed8kbps +} Bos1901PlaybackSpeedEnum; + +#define CONFIG_REG_PLAYBACK_SPEED_MASK (0x007) +#define CONFIG_REG_DEEP_SLEEP_MASK (0x008) +#define CONFIG_REG_OUTPUT_ENABLE_MASK (0x010) +#define CONFIG_REG_RESET_MASK (0x020) +#define CONFIG_REG_LOCK_MASK (0x040) +#define CONFIG_REG_BROADCAST_MASK (0xF80) + +#define CONFIG_REG_PLAYBACK_SPEED_SHIFT (0) +#define CONFIG_REG_DEEP_SLEEP_SHIFT (3) +#define CONFIG_REG_OUTPUT_ENABLE_SHIFT (4) +#define CONFIG_REG_RESET_SHIFT (5) +#define CONFIG_REG_LOCK_SHIFT (6) +#define CONFIG_REG_BROADCAST_SHIFT (7) + +#define CONFIG_REG_CLEAR_RESET(reg) CLEAR_BITS(reg, CONFIG_REG_RESET_MASK) +#define CONFIG_REG_CLEAR_BROADCAST(reg) CLEAR_BITS(reg, CONFIG_REG_BROADCAST_MASK) +#define CONFIG_REG_CLEAR_OUTPUT_ENABLE(reg) CLEAR_BITS(reg, CONFIG_REG_OUTPUT_ENABLE_MASK) + + +#define CONFIG_REG_WRITE_BROADCAST(reg, bc) (reg |= ((bc << CONFIG_REG_BROADCAST_SHIFT) & CONFIG_REG_BROADCAST_MASK)) +#define CONFIG_REG_WRITE_PLAYBACK_SPEED(reg, ps) (reg |= ((ps << CONFIG_REG_PLAYBACK_SPEED_SHIFT) & CONFIG_REG_PLAYBACK_SPEED_MASK)) + +struct CONFIG_BITS_BOS1901 +{ + Bos1901PlaybackSpeedEnum PLAY: 3; // 2:0 Waveform play back speed + uint16_t DS: 1; // 3 Power mode when not playing waveforms + uint16_t OE: 1; // 4 Enable/Disable waveform playback + uint16_t RST: 1; // 5 Software Reset + uint16_t LOCK: 1; // 6 Register lock + uint16_t BC: 5; // 7 Internal register address +}; + +union CONFIG_REG_BOS1901 +{ + Bos1901Register reg; + struct CONFIG_BITS_BOS1901 bit; +}; + +//*********************************************** +// PARCAP register - address 0x6 +//*********************************************** +struct PARCAP_BITS_BOS1901 +{ + uint16_t PARCAP: 8; // 7:0 Calculated parasitic capacitance + uint16_t CAL: 1; // 8 Internal calibration at start-up + uint16_t CP5: 1; // 9 Internal 5V charge pump + uint16_t LMI: 1; // High Side switch current limit + uint16_t UPI: 1; // Unidirectional power input +}; + +union PARCAP_REG_BOS1901 +{ + Bos1901Register reg; + struct PARCAP_BITS_BOS1901 bit; +}; + +//*********************************************** +// SUP_RISE register - address 0x7 +//*********************************************** +#define SUP_RISE_REG_TI_RISE_SHIFT (0) +#define SUP_RISE_REG_VDD_SHIFT (6) +#define SUP_RISE_REG_SENSE_SHIFT (11) + +#define SUP_RISE_REG_TI_RISE_MASK (0x03f) +#define SUP_RISE_REG_VDD_MASK (0x7C0) +#define SUP_RISE_REG_SENSE_MASK (0x800) + +#define SUP_RISE_REG_WRITE_VDD(value) ((value << SUP_RISE_REG_VDD_SHIFT) & SUP_RISE_REG_VDD_MASK) +#define SUP_RISE_REG_WRITE_TI_RISE(value) ((value << SUP_RISE_REG_TI_RISE_SHIFT) & SUP_RISE_REG_TI_RISE_MASK) + +#define SUP_RISE_REG_CLEAR_SENSE(value) (value & ~SUP_RISE_REG_SENSE_MASK) + + +#define TI_RIS + +struct SUP_RISE_BITS_BOS1901 +{ + uint16_t TI_RISE: 6; // 5:0 Calculated proportional gain for the offset + uint16_t VDD: 5; // 10:6 Calculated digital representation of the supply voltage + uint16_t SENSE: 1; // 11 Mute the output to only read the actuator voltage without driving it. Use when sensing a piezo. +}; + +union SUP_RISE_REG_BOS1901 +{ + Bos1901Register reg; + struct SUP_RISE_BITS_BOS1901 bit; +}; + +//*********************************************** +// DAC register - address 0x8 +//*********************************************** +struct DAC_BITS_BOS1901 +{ + uint16_t DAC_LS: 6; // Internal calibration value + uint16_t DAC_HS: 6; // Internal calibration value +}; + +union DAC_REG_BOS1901 +{ + Bos1901Register reg; + struct DAC_BITS_BOS1901 bit; +}; + +//*********************************************** +// THRESH register - address 0x9 +//*********************************************** +struct THRESH_BITS_BOS1901 +{ + uint16_t THRESH_ERROR: 5; // Consecutive time that the current required is above a threshold value defined in the IC to ensure clean transition between boost and buck operation of the driver. + uint16_t HB: 1; // HBridge behavior on startup + uint16_t TRIM_MODE: 2; // Controls the hardware trimming mode. + uint16_t PER: 1; // Enable progressive limitation of error + uint16_t IA: 1; // Enable adaptive integral controller. + uint16_t CPHB: 2; // CLK division for the Hbridge charge pump +}; + +union THRESH_REG_BOS1901 +{ + Bos1901Register reg; + struct THRESH_BITS_BOS1901 bit; +}; + +//*********************************************** +// TM_KI register - address 0xA +//*********************************************** +struct TM_KI_BITS_BOS1901 +{ + uint16_t RIZ: 1; // Reset Integral term at polarity change + uint16_t OFS: 1; // Generate a 30 mV offset between the supply level and the internal power level. + uint16_t IB: 1; // Relative current boundary to I_on for boost to buck transition. + uint16_t TMIN: 2; // Minimum period used to scale the integral gain. + uint16_t VTHRESH: 7; // 7-bit value that indicate when to stop operating the buck controller in pseudo resonant mode. +}; + +union TM_KI_REG_BOS1901 +{ + Bos1901Register reg; + struct TM_KI_BITS_BOS1901 bit; +}; + +//*********************************************** +// DEBUG register - address 0xB +//*********************************************** +struct DEBUG_BITS_BOS1901 +{ + uint16_t MULT1: 4; // Assign internal digital node to TD1 pin; + uint16_t MULT2: 4; // Assign internal digital node to TD2 pin; + uint16_t EAN: 3; // Assign internal analog node to TA pin; + uint16_t DB: 1; // IC in debug mode +}; + +union DEBUG_REG_BOS1901 +{ + Bos1901Register reg; + struct DEBUG_BITS_BOS1901 bit; +}; + +//*********************************************** +// IC_STATUS register - address 0xC +//*********************************************** + +#define IC_STATUS_REG_FIFO_SPACE_MASK (0x002F) +#define IC_STATUS_REG_FIFO_EMPTY_MASK (0x0040) +#define IC_STATUS_REG_FIFO_FULL_MASK (0x0080) +#define IC_STATUS_REG_OVER_TEMP_MASK (0x0100) +#define IC_STATUS_REG_OVER_VOLTAGE_EMPTY_MASK (0x0200) +#define IC_STATUS_REG_STATE_MASK (0x0C00) + +#define IC_STATUS_REG_FIFO_SPACE_SHIFT (0) +#define IC_STATUS_REG_FIFO_EMPTY_SHIFT (6) +#define IC_STATUS_REG_FIFO_FULL_SHIFT (7) +#define IC_STATUS_REG_OVER_TEMP_SHIFT (8) +#define IC_STATUS_REG_OVER_VOLTAGE_EMPTY_SHIFT (9) +#define IC_STATUS_REG_STATE_SHIFT (10) + +#define IC_STATUS_REG_READ_FIFO_SPACE(reg) ((reg & IC_STATUS_REG_FIFO_SPACE_MASK) >> IC_STATUS_REG_FIFO_SPACE_SHIFT) +#define IC_STATUS_REG_READ_FIFO_EMPTY(reg) ((reg & IC_STATUS_REG_FIFO_EMPTY_MASK) >> IC_STATUS_REG_FIFO_EMPTY_SHIFT) +#define IC_STATUS_REG_READ_FIFO_FULL(reg) ((reg & IC_STATUS_REG_FIFO_FULL_MASK) >> IC_STATUS_REG_FIFO_FULL_SHIFT) +#define IC_STATUS_REG_READ_OVER_TEMP(reg) ((reg & IC_STATUS_REG_OVER_TEMP_MASK) >> IC_STATUS_REG_OVER_TEMP_SHIFT) +#define IC_STATUS_REG_READ_OVER_VOLTAGE(reg) ((reg & IC_STATUS_REG_OVER_VOLTAGE_EMPTY_MASK) >> IC_STATUS_REG_OVER_VOLTAGE_EMPTY_SHIFT) +#define IC_STATUS_REG_READ_STATE(reg) ((reg & IC_STATUS_REG_STATE_MASK) >> IC_STATUS_REG_STATE_SHIFT) + +struct IC_STATUS_BITS_BOS1901 +{ + uint16_t FIFO_SPACE: 6; // Space available in FIFO for new data; + uint16_t EMPTY: 1; // Fifo is empty + uint16_t FULL: 1; // Fifo is full + uint16_t OVT: 1; // Overtemperature status bit + uint16_t OVV: 1; // Over voltage bit + uint16_t STATE: 2; // State of the controller +}; + +union IC_STATUS_REG_BOS1901 +{ + Bos1901Register reg; + struct IC_STATUS_BITS_BOS1901 bit; +}; + +//*********************************************** +// SENSE register - address 0xD +//*********************************************** + +#define SENSE_REG_VFEEDBACK_MASK (0x03FF) +#define SENSE_REG_STATE_MASK (0x0C00) + +#define SENSE_REG_VFEEDBACK_SHIFT (0) +#define SENSE_REG_STATE_SHIFT (10) + +#define SENSE_REG_READ_VFEEDBACK(reg) ((reg & SENSE_REG_VFEEDBACK_MASK) >> SENSE_REG_VFEEDBACK_SHIFT) +#define SENSE_REG_READ_STATE(reg) ((reg & SENSE_REG_STATE_MASK) >> SENSE_REG_STATE_SHIFT) + +#define VFEEDBACK_IN_MILLIVOLT(value) ((value * 3600 * 31) / (1024 - 1)) + +struct SENSE_BITS_BOS1901 +{ + uint16_t VFEEDBACK: 10; // Voltage feedback on 10 bit + uint16_t STATE: 2; // State of the controller +}; + +union SENSE_REG_BOS1901 +{ + Bos1901Register reg; + struct SENSE_BITS_BOS1901 bit; +}; + +//*********************************************** +// TRIM register - address 0xE +//*********************************************** +struct TRIM_BITS_BOS1901 +{ + uint16_t TRIM_REG: 2; // 2:0 1.8V regulator trimming bits in 2's complement + uint16_t TRIM_OSC: 6; // 9:3 Oscillator trimming bits in 2's complement + uint16_t SDOBP: 1; // Assign internal clock signal to SDO pin + uint16_t TRIMRW: 3; // 11:10 On demand trim control +}; + +union TRIM_REG_BOS1901 +{ + Bos1901Register reg; + struct TRIM_BITS_BOS1901 bit; +}; + + +//*********************************************** +// ACCESS register - address 0xF +//*********************************************** +struct ACCESS_BITS_BOS1901 +{ + uint16_t ACCESS0: 1; // Access bit 0 + uint16_t RSVD0: 1; + uint16_t ACCESS1: 1; // Access bit 1 + uint16_t RSVD1: 4; + uint16_t ACCESS2: 1; // Access bit 2 + uint16_t RSVD2: 1; + uint16_t ACCESS3: 1; // Access bit 3 + uint16_t RSVD3: 2; +}; + +union ACCESS_REG_BOS1901 +{ + Bos1901Register reg; + struct ACCESS_BITS_BOS1901 bit; +}; + +//*********************************************** +// Complete registers structure +//*********************************************** +typedef struct +{ + union REFERENCE_REG_BOS1901 REFERENCE; // REFERENCE register + union ION_BL_REG_BOS1901 ION_BL; // ION_BL register + union DEADTIME_REG_BOS1901 DEADTIME; // DEADTIME register + union KP_REG_BOS1901 KP; // KP register + union KPA_KI_REG_BOS1901 KPA_KI; // KPA_KI register + union CONFIG_REG_BOS1901 CONFIG; // CONFIG register + union PARCAP_REG_BOS1901 PARCAP; // PARCAP register + union SUP_RISE_REG_BOS1901 SUP_RISE; // SUP_RISE register + union DAC_REG_BOS1901 DAC_REG; // DAC register + union THRESH_REG_BOS1901 THRESH; // THRESH register + union TM_KI_REG_BOS1901 TM_KI; // TM_KI register + union DEBUG_REG_BOS1901 DEBUG_REG; // DEBUG register + union IC_STATUS_REG_BOS1901 IC_STATUS; // IC_STATUS register + union SENSE_REG_BOS1901 SENSE; // SENSE register + union TRIM_REG_BOS1901 TRIM; // TRIM register + union ACCESS_REG_BOS1901 ACCESS; // ACCESS register +} BOS1901_REGS; +#endif //DKCORE_BOS1901REGISTER_H diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/haptic/bosDriver.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/haptic/bosDriver.h new file mode 100644 index 000000000000..ed347e455d39 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/haptic/bosDriver.h @@ -0,0 +1,646 @@ +// +// Description: Boreas Generic Haptic Driver Interface +// Created on 2/18/2020 +// Copyright (c) 2020 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#ifndef DKCORE_BOSDRIVER_H +#define DKCORE_BOSDRIVER_H + +#include + +#include "bsp/drivers/spi/spi.h" +#include "bsp/drivers/gpio/gpio.h" +#include "bsp/drivers/haptic/bos0614Register.h" +#include "bsp/drivers/haptic/bos1211Register.h" +#include "bsp/drivers/haptic/bos1901Register.h" + +#define INT16_MIN (-32768) +#define UINT16_MAX (65535) +#define INVALID_CHIP_ID (UINT16_MAX) +#define HAPTIC_DRIVER_DEFAULT_CHANNEL (0) + +typedef uint16_t ChipId; +typedef uint8_t SliceId; +typedef uint8_t WaveformId; + +typedef union +{ + Bos0614Register register0614; + Bos1211Register register1211; + Bos1901Register register1901; +} Register; + +typedef union +{ + Bos0614Registers bos0614Regs; + BOS1211_REGS bos1211Regs; + BOS1901_REGS bos1901Regs; +} Registers; + +typedef enum +{ + GPO_INTERNAL_RESET = 0, + GPO_SENSE_TRIGGER, + GPO_WAVEFORM_FIFO_DONE, + GPO_ERROR_NOTIFY, + GPO_MAX_POWER, + GPO_BUTTON_STATE, + GPO_REQUEST_TO_PLAY, +} GPOCtrl; + +typedef enum +{ + SENSING_DIRECTION_PRESS = 0x00, + SENSING_DIRECTION_RELEASE = 0x01, + SENSING_DIRECTION_ENUM_LENGTH +} SensingDirection; + +typedef enum +{ + SENSING_DETECTION_MODE_THRESHOLD = 0x0, + SENSING_DETECTION_MODE_SLOPE = 0x1, +} SensingDetectionMode; + +typedef enum +{ + SENSING_POLARITY_INVALID = 0x00, + SENSING_POLARITY_POSITIVE = 0x01, + SENSING_POLARITY_NEGATIVE = 0x02, + SENSING_POLARITY_BOTH = 0x03, +} SensingPolarity; + +typedef struct +{ + uint16_t debounceUs; + int16_t thresholdMv; + SensingDirection direction; + SensingDetectionMode mode; + uint8_t stabilisationMs; + SensingPolarity polarity; +} SensingConfig; + +typedef enum +{ + BOS_FEATURE_FIFO, + BOS_FEATURE_SYNTHESIZER, + BOS_FEATURE_RAM_PLAYBACK, + BOS_FEATURE_SENSING_WITH_AUTOMATIC_FEEDBACK, + BOS_FEATURE_SENSING_THRESHOLD_DETECTION, + BOS_FEATURE_GPO_SIGNALING, + BOS_FEATURE_SUPPORT_BOTH_SENSING_POLARITY, + + BOS_FEATURE_LENGTH +} BosFeature; + +typedef struct +{ + uint32_t sliceId; + int32_t mVAmp; //milli volt amplitude + uint32_t mHzFreq; //milli-Hertz + uint32_t cycle; +} SynthSlice; + +typedef enum +{ + BOS_ERROR_NO_ERROR = 0, + BOS_ERROR_SHORT_CIRCUIT, + BOS_ERROR_UVLO, + BOS_ERROR_IDAC, + BOS_ERROR_OVT, + BOS_ERROR_OVV, + BOS_ERROR_MAX_POWER, + + BOS_ERROR_LENGTH, +} BosError; + +typedef enum +{ + BOS_EVENT_WAVEFORM_DONE, + BOS_EVENT_BUTTON_PRESS, + BOS_EVENT_BUTTON_RELEASE, + BOS_EVENT_SENSING_TRIGGER, + BOS_EVENT_REQUEST_TO_PLAY, + BOS_EVENT_NO_REQUEST_TO_PLAY, + BOS_EVENT_NO_EVENT +} BOSEvent; + +typedef enum +{ + SHAPE_NO_SHAPE, + SHAPE_SAW, + SHAPE_TRIANGLE, + SHAPE_SLOW_RISE, + SHAPE_SLOW_DROP +} SliceShape; + +typedef uint8_t ChannelId; + +typedef struct _HapticDriver HapticDriver; + +/* + * @brief Software Reset the chip + * + * @param[in] driver The haptic driver context + * + * @return True if the operation succeed, otherwise false + */ +typedef bool(* BosSoftwareReset)(HapticDriver* driver); + +/* + * @brief Put device to sleep or wake it up + * @param[in] driver The haptic driver context + * @param[in] toSleep true for sleep, false for wakeup + * @return True if the operation succeeded, otherwise false + */ +typedef bool(* BosDeepSleep)(HapticDriver* driver, bool toSleep); + +/** + * @brief Return the chip id + * + * @param[in] driver The haptic driver context + * + * @return ChipId Return the chip id + */ +typedef ChipId (* BosGetChipId)(HapticDriver* driver); + +/** + * @brief Reads a register value of a haptic IC. + * + * @param[in] driver A Haptic driver instance. + * @param[in] addr A register address. + * @param[out] value A variable pointer to return the register value. + * + * @return True if the operation succeed, otherwise false. + */ +typedef bool(* BosGetRegister)(HapticDriver* driver, uint8_t addr, uint16_t* value); + +/** + * @brief Read an array of registers value of a haptic IC. + * + * @param[in] driver A Haptic driver instance. + * @param[in] addrArray An array of register addresses. + * @param[out] valueArray A variable pointer to return the registers value. + * @param[in] nbRegisters Number of registers in @a addrArray + * + * @note addrArray and valueArray must have the same number of entries. + * + * @return True if the operation succeed, otherwise false. + */ +typedef bool(* BosGetRegisters)(HapticDriver* driver, const uint8_t* addrArray, uint16_t* valueArray, + const uint8_t nbRegisters); + +/** + * @brief Writes a value into a haptic IC's register. + * + * @param[in] driver A haptic driver instance. + * @param[in] addr A register address. + * @param[in] value A register value to set + * + * @return True if the operation succeed, otherwise false. + */ +typedef bool(* BosSetRegister)(HapticDriver* driver, uint8_t addr, uint16_t value); + +/** + * @brief Writes values into haptic IC's registers. + * + * @param[in] driver A haptic driver instance. + * @param[in] addrArray An array of registers address. + * @param[in] valueArray A array of registers value to set. + * @param[in] nbRegisters Number of registers into @a addrArray. + * + * @note addrArray and valueArray must have the same number of entries. + * + * @return True if the operation succeed, otherwise false. + */ +typedef bool(* BosSetRegisters)(HapticDriver* driver, const uint8_t* addrArray, const uint16_t* valueArray, + const uint8_t nbRegisters); + +/** + * @brief Writes a value into haptic IC's register and reads the current broadcast register value. + * + * @param[in] driver A haptic driver instance. + * @param[in] addr A register address. + * @param[in] value A register value to set. + * @param[out] broadcastValue A variable pointer to return the broadcast register value. + * + * @return True if the operation succeed, otherwise false. + */ +typedef bool(* BosSetGetRegister)(HapticDriver* driver, const uint8_t addr, uint16_t value, uint16_t* broadcastValue); + +/** + * @brief Writes values into haptic IC's registers and reads the current broadcast register values. + * + * @param[in] driver A haptic driver instance. + * @param[in] addrArray A array of registers address. + * @param[in] valueArray A array of registers value to set. + * @param[out] broadcastArray A pointer to an array to return the broadcast register values. + * @param[in] nbRegisters Number of registers into @a addrArray. + * + * @note addrArray, valueArray and broadcastArray must have the same number of entries. + * + * @return True if the operation succeed, otherwise false. + */ +typedef bool(* BosSetGetRegisters)(HapticDriver* driver, const uint8_t* addrArray, const uint16_t* valueArray, + uint16_t* broasdcastArray, const uint8_t nbRegisters); + + +/* + * @brief Get the number of register available + */ +typedef size_t (* BosGetNumberOfRegister)(void); + +/* + * @brief Configure a slice which is part of a waveform (group of slice) + * + * @param[in] driver The haptic driver context + * @param[in] slice Slice configuration + * @param[in] outputChannel The output channel of the slice + * + * @return True if the operation succeed, otherwise false + */ +typedef bool (* BosSynthesizerSetSlice)(HapticDriver* driver, const SynthSlice* slice, SliceShape shape, const uint8_t outputChannel); + +/** + * @brief Set the synthesizer mode + * + * @param[in] driver The haptic driver context + * @param[in] waveformId The waveform id to configure + * @param[in] frequency The frequency of the specified waveform + * @param[in] amplitude The amplitude of the specified waveform + * @param[in] cycle The amount of waveform period to play + * @param[in] shape Shape In/Out arrangement + * @param[in] outputChannel The output channel to play the waveform + * + * @return True if the operation succeed, otherwise false + */ +typedef bool(* BosSynthesizerSetWaveform)(HapticDriver* driver, WaveformId id, uint8_t startSliceId, + size_t nbrOfSlices, uint16_t cycle, uint8_t outputChannel); + + +/** + * @brief Activate or deactivate the output + * + * @param[in] driver The haptic driver context + * @param[in] activate True if activating the output, False if deactivating the output + * + * @return True if the operation succeed, otherwise false + */ +typedef bool(* BosCtrlOutput)(HapticDriver* driver, bool activate); + +/** + * @brief Configure the GPO mode + * + * @param[in] driver The haptic driver context + * @param[in] channel The channel specific to this IC + * @param[in] state The GPO mode to configure + * + * @return True if the operation succeed, otherwise false + */ +typedef bool(* BOSCtrlGPO)(HapticDriver* driver, ChannelId channelId, GPOCtrl state); + +/** + * @brief BOS Driver GPO Event Callback + * + * @param[in] driver The haptic driver instance + * @param[in] channelId The channel specific to the event occurrence + * @param[in] event The pushed event + * @param[in] context The client context specified on registering the callback + * + * @return void + */ +typedef void(* BOSEventCb)(HapticDriver* driver, ChannelId channelId, BOSEvent event, void* context); + +/** + * @brief Register for event callback registration + * + * @param[in] driver The haptic driver instance + * @param[in] channelId The channel specific to listen for event + * @param[in] cb Callback to register + * @param[in] context Context to forward to the callback + * + * @return True if the operation succeed, false otherwise + */ +typedef bool(* BOSRegisterEvents)(HapticDriver* driver, ChannelId channelId, BOSEventCb cb, void* context); + +/* + * @brief Remove from event callback registration for GPO signals + * + * @param[in] driver The haptic driver instance + * @param[in] channelId The channel specific to unregister for event + * + * @return True if the operation succeed, false otherwise + */ +typedef bool(* BOSUnregisterEvents)(HapticDriver* driver, ChannelId channelId); + +/* + * @brief Toggle the synthesizer play + * + * @param[in] driver The haptic driver context + * @param[in] start The waveform id where the sequencer starts to play + * @param[in] stop The waveform id where the sequencer stops to play + * + * @return True if the operation succeed, otherwise false + */ +typedef bool(* BosWaveSynthesizerPlay)(HapticDriver* driver, WaveformId start, WaveformId stop); + +/** + * @brief Configure the chip in RAM playback mode + * + * @param[in] driver The haptic driver context + * @param[in] samplingRate The number of kilo sample per second to play + * @param[in] data The data to write in the ram + * @param[in] length The length of the data to play + * @param[in] channelMask The bit mask of the channel configuration where each bit represents a channel to activate. + * LSB represent the channel #0 and MSB the channel #8 + * + * @return True if the operation succeed, otherwise false + */ +typedef bool(* BosRamplayback)(HapticDriver* driver, uint32_t samplingRate, void* data, size_t length, + uint8_t channelMask); + +/** + * @brief Play the content of the ram playback + * + * @param[in] driver The haptic driver context + * @param[in] length The length of the data to play + * + * @return True if the operation succeed, otherwise false + */ +typedef bool(* BosRamPlaybackToggle)(HapticDriver* driver, size_t length); + + +/** + * @brief Configure the chip in Fifo mode + * + * @param[in] driver The haptic driver context + * @param[in] samplingRate The number of kilo sample per second to play + * + * @return True if the operation succeed, otherwise false + */ +typedef bool(* BosSetFifoMode)(HapticDriver* driver, uint32_t samplingRate, uint8_t outputChannel); + +/** + * @brief Write data in the fifo + * + * @param[in] driver The haptic driver context + * @param[in] data The data to write in the fifo + * @param[in] length The length of the data to write + * + * @return True if the operation succeed, otherwise false + */ +typedef bool(* BosWriteFifoSamples)(HapticDriver* driver, void* data, size_t length); + +/** + * @brief Get the fifo space avaiable in the fifo + * + * @param[in] driver The haptic driver context + * @param[in] availableSpace The available space in the fifo + * + * @return True if the operation succeed, otherwise false + */ +typedef bool(* BosGetFifoSpace)(HapticDriver* ctx, uint16_t* availableSpace); + +/** + * @brief Get the FIFO max available space + * + * @return uint16_t The max space available + */ +typedef uint16_t (* BosGetMaximumFifoSpace)(void); + +/** + * @brief Activate or deactivate the synchronization mode between chip + * + * @param[in] driver The haptic driver context + * @param[in] activate True for activating and False for deactivating + * + * @return True if the operation succeed, otherwise false + */ +typedef bool(* BosSynch)(HapticDriver* driver, bool activate); + +/** + * @brief Referencing the voltage value passed in the array to values referencing in the chip domain + * + * @param[in] driver The haptic driver context + * @param[in] dataIn The data to reference + * @param[in] length The length of the array to reference + * @param[out] dataOut The data converted to the IC reference voltage. + * + * @return True if the operation succeed, otherwise false + */ +typedef bool(* BosReferencingFromVolt)(HapticDriver* driver, int16_t* dataIn, size_t length, int16_t* dataOut); + +/** + * @brief Referencing the relative int16 value passed in the array to values referencing in the chip domain + * + * @param[in] driver The haptic driver context + * @param[in] dataIn The relative data point (between INT16_MIN and INT16_MAX) + * @param[in] length The length of the array to reference + * @param[in] vMinVolt The minimum voltage supported by the piezo(s) + * @param[in] vMaxVolt The maximum voltage supported by the piezo(s) + * @param[out] dataOut The data converted to the IC reference voltage. + * + * @return True if the operation succeed, otherwise false + */ +typedef bool(* BosReferencingFromRelativeInt16)(HapticDriver* driver, int16_t* dataIn, size_t length, + int16_t vMinVolt, int16_t vMaxVolt, int16_t* dataOut); + +/* + * @brief Get the error status bits + * + * @param[in] driver The haptic driver context + * @param[in] errors Provided error array + * @param[in] maxLength Max number of error to copy in the errors array + * + * @return The amount of active errors + */ +typedef size_t(* BosGetError)(HapticDriver* driver, BosError* errors, size_t maxLength); + +/* + * @brief Sensing Event Callback + * + * @param[in] channel The IC channel from which the event has been detected + * @param[in] mode The sensing detection mode + * @param[in] direction The sensing threshold direction detection + * @param[in] arg Application Specific arg copied when registering the callback + * + * @return void + */ +typedef void (* BosSensingEvent)(uint8_t channel, SensingDetectionMode mode, SensingDirection direction, void* arg); + +/* + * @brief Configures the chip for sensing + * + * @param[in] driver The haptic driver concerned + * @param[in] channel The channel to configure the sensing for. + * @param[in] config Sensing configuration to apply + * @param[in] type Sensing type desired + * + * @return True if the operation succeed, otherwise false + */ +typedef bool (* BosButtonSensing)(HapticDriver* driver, uint8_t channel, SensingConfig config); + +/* + * @brief Set the automatic feedback by channel + * + * @param[in] driver The haptic driver concerned + * @param[in] channelId The channel driver which applies + * @param[in] id The synthesizer waveform id to play on detection event + * @param[in] direction The detection direction (press or release) which the waveform must be applied to. + * + * @return True if the operation succeed, otherwise false + */ +typedef bool (* BosSensingAutoPlayWave)(HapticDriver* driver, ChannelId channelId, WaveformId id, + SensingDirection direction); + +/* + * @brief Stops sensing without disabling the output + * + * @param[in] driver The haptic driver context + * @param[in] channel The channel to deactivate the sensing + * @param[in] direction The sensing direction (press or release) to deactivate + * + * @return True if the operation succeed, otherwise false + */ +typedef bool (* BosSensingStop)(HapticDriver* driver, ChannelId channelId); + +/* + * @brief Read output in Volt when in sensing + * + * @param[in] driver The haptic driver context + * @param[in] channel The channel to deactivate the sensing + * @param[in] *data The variable pointer in which the output value will be placed + * + * @return True if the operation succeed, otherwise false + */ +typedef bool (* BosSenseOutput)(HapticDriver* driver, ChannelId channelId, int32_t* data); + +/* + * @brief (Engineering feature) Access register shadow value of the driver + * + * @return Registers The register set corresponding to the IC + */ +typedef Registers* (* BosGetShadowRegisters)(HapticDriver* driver); + +/* + * @brief (Engineering feature) Set a specific register using the Register struct + * + * @param[in] driver The haptic driver context + * @param[in] reg The register to set + * + * @return bool True if the operation succeed and false if the operation failed + */ +typedef bool(* BosWriteRegister)(HapticDriver* driver, Register* reg); + +/* + * @brief (Engineering feature) Set a specific register using the Register struct + * + * @param[in] driver The haptic driver context + * @param[in|out] reg The register to read. If the operation succeed, the value of the register will be updated. + * + * @return bool True if the operation succeed and false if the operation failed + */ +typedef bool(* BosReadRegister)(HapticDriver* driver, Register* reg); + +/* + * @brief (Engineering feature) Read the GPIO pin state + * + * @param[in] driver The haptic driver context + * @param[in] channel The channel specific to this IC + * + * @return GPIOState The state of the Gpio + */ +typedef GPIOState(* BosGetGpioState)(HapticDriver* driver, ChannelId channelId); + +/** + * @brief Interrogate driver if a specific feature is supported + * + * @param[in] feature The feature request + * + * @return bool True if the feature is supported, false if not + */ +typedef bool (* SupportedFeature)(HapticDriver* driver, BosFeature feature); + +/** + * @brief Enable the sensing on the specified channel + * + * @param[in] driver The haptic driver context + * @param[in] channel The channel specific to this IC + * + * @return bool True if the operation succeed and false if the operation failed + */ +typedef bool (* BosEnableSensing)(HapticDriver* driver, ChannelId channelId); + +/** + * @brief Set the polarity of the sensing (Ex: Set the HBridge of the BOS1901 in the good direction for sensing positive or negative voltage) + * + * @param[in] driver The haptic driver context + * @param[in] channel The channel specific to this IC + * + * @return bool True if the operation succeed and false if the operation failed + */ +typedef bool (* BosSetSensingPolarity)(HapticDriver* driver, ChannelId channelId, SensingPolarity polarity); + +struct _HapticDriver +{ + BosGetChipId getChipId; + BosSoftwareReset softwareReset; + BosDeepSleep deepSleep; + BosGetRegister getRegister; + BosGetRegisters getRegisters; + BosSetRegister setRegister; + BosSetRegisters setRegisters; + BosSetGetRegister setGetRegister; + BosSetGetRegisters setGetRegisters; + BosSynthesizerSetSlice synthSetSlice; + BosSynthesizerSetWaveform synthSetWaveform; + BosWaveSynthesizerPlay wfsPlay; + BOSRegisterEvents registerOnEvents; + BOSUnregisterEvents unregisterEvents; + BosCtrlOutput ctrlOutput; + BOSCtrlGPO configGPO; + BosRamplayback setRamPlayback; + BosRamPlaybackToggle playRamPlayback; + BosGetFifoSpace getFifoSpace; + BosGetMaximumFifoSpace getMaximumFifoSpace; + BosSetFifoMode setFifoMode; + BosWriteFifoSamples writeFifoSamples; + BosSynch synch; + BosGetError getError; + BosReferencingFromVolt referencingFromVolt; + BosReferencingFromRelativeInt16 referencingFromInt16; + BosGetNumberOfRegister numberOfRegister; + BosButtonSensing buttonSensing; + BosEnableSensing enableSensing; + BosSensingAutoPlayWave sensingAutoPlayWave; + BosSensingStop stopSensing; + SupportedFeature isSupported; + BosSenseOutput senseOutput; + BosSetSensingPolarity setSensingPolarity; + + /* Engineering functions */ + BosGetShadowRegisters getShadowRegisters; + BosWriteRegister writeRegister; + BosReadRegister readRegister; + BosGetGpioState getGpioState; +}; + +#endif //DKCORE_BOSDRIVER_H diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/i2c/i2c.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/i2c/i2c.h new file mode 100644 index 000000000000..68a29be3c9a1 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/i2c/i2c.h @@ -0,0 +1,108 @@ +// +// Description: I2C Driver Interface +// Created on 1/23/2020 +// Copyright (c) 2020 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#ifndef DKCORE_I2C_H +#define DKCORE_I2C_H + +#include + +#include "contribs/cmsis/CMSIS/Driver/Include/Driver_Common.h" + +#define I2C_7BIT_ADDRESS_MASK (0x7F) + +typedef enum +{ + I2C_ADR_PIN_GND, + I2C_ADR_PIN_VCC, + I2C_ADR_PIN_SDA, + I2C_ADR_PIN_SCL, + I2C_ADR_PIN_INVALID +} I2cAdrPins; + +/** + * @typedef I2c driver Instance + */ +typedef struct _i2c I2c; +typedef struct _i2c I2c_t; + +/** + * @brief I2C Master Read Primitive + * + * @param[in] i2c Driver instance + * @param[in] address I2C slave address + * @param[out] data Buffer for read data + * @param[in] num Length to read + * + * @return ARM_DRIVER_OK on successful operation, ERRNO on failure + */ +typedef int32_t(*I2cRead)(const I2c *i2c, uint8_t address, void *data, size_t num); + +/** + * @brief 2C Master Read Primitive with stop configuration + * + * @param[in] i2c Driver instance + * @param[in] address I2C slave address + * @param[out] data Buffer for read data + * @param[in] num Length to read + * @param[in] writeStop Set to true for writing stop at the end of the transaction + * + * @return ARM_DRIVER_OK on successful operation, ERRNO on failure + */ +typedef int32_t(*I2cReadStop)(const I2c *i2c, uint8_t address, void *data, size_t num, bool writeStop); + +/** + * @brief I2C Master Write Primitive + * + * @param[in] i2c Driver instance + * @param[in] address I2C slave address + * @param[in] data Data buffer to write + * @param[in] num Length of the buffer to write + * + * @return ARM_DRIVER_OK on successful operation, ERRNO on failure + */ +typedef int32_t(*I2cWrite)(const I2c *i2c, uint8_t address, const void *data, size_t num); + +/** + * @brief I2C Master Write Primitive with stop configuration + * + * @param[in] i2c Driver instance + * @param[in] address I2C slave address + * @param[in] data Data buffer to write + * @param[in] num Length of the buffer to write + * @param[in] writeStop Set to true for writing stop at the end of the transaction + * + * @return ARM_DRIVER_OK on successful operation, ERRNO on failure + */ +typedef int32_t(*I2cWriteStop)(const I2c *i2c, uint8_t address, const void *data, size_t num, bool writeStop); + +struct _i2c +{ + I2cWrite write; + I2cWriteStop writeStop; + + I2cRead read; + I2cReadStop readStop; +}; + +#endif //DKCORE_I2C_H diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/spi/spi.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/spi/spi.h new file mode 100644 index 000000000000..a022676eb72e --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/bsp/drivers/spi/spi.h @@ -0,0 +1,32 @@ +// +// Description: Spi Driver Interface +// +// Created by Pascal Frederic St-Laurent +// Copyright (c) 2019 Boreas Technologies All rights reserved. +// + +#ifndef DKCORE_SPI_H +#define DKCORE_SPI_H + +#include + +typedef struct _spi Spi; +typedef struct _spi Spi_t; + + +typedef int32_t (*SpiSend)(const Spi *spi, const void *data, size_t num); +typedef int32_t (*SpiRead)(const Spi *spi, void *data, size_t num); +typedef int32_t (*SpiTransfer)(const Spi *spi, const void *data_out, void *data_in, size_t num); +typedef int32_t (*SpiChipSelect)(const Spi *spi, bool assertCs); + +struct _spi +{ + SpiSend send; + SpiRead read; + SpiTransfer transfer; + SpiChipSelect cs; +}; + +bool isSameSpiPeripheral(const Spi* const spiA, const Spi* const spiB); + +#endif //DKCORE_SPI_H diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/src/coreDef.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/coreDef.h new file mode 100644 index 000000000000..aa69dd8912e3 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/coreDef.h @@ -0,0 +1,197 @@ +// Copyright (c) 2020 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef DK_CORE_COREDEF_H +#define DK_CORE_COREDEF_H + +#include "coreDefOption.h" + +#ifndef NBR_OF_BUTTON +#define NBR_OF_BUTTON 0 +#endif + +#ifndef NBR_OF_TL1963A +#define NBR_OF_TL1963A 0 +#endif + +#ifndef NBR_OF_IQS263 +#define NBR_OF_IQS263 0 +#endif + +#ifndef NBR_OF_MCP45HV +#define NBR_OF_MCP45HV 0 +#endif + +#ifndef NBR_OF_TCA9535 +#define NBR_OF_TCA9535 0 +#endif + +#ifndef NBR_OF_TCA9554A +#define NBR_OF_TCA9554A 0 +#endif + +#ifndef NBR_OF_IO_EXPANDER_GPIO +#define NBR_OF_IO_EXPANDER_GPIO 0 +#endif + +#ifndef NBR_OF_TCA9548A +#define NBR_OF_TCA9548A 0 +#endif + +#ifndef NBR_OF_I2C_SWITCH +#define NBR_OF_I2C_SWITCH 0 +#endif + +#ifndef NBR_OF_I2C_SWITCH_CHANNEL +#define NBR_OF_I2C_SWITCH_CHANNEL 0 +#endif + +#ifndef NBR_OF_LED +#define NBR_OF_LED 0 +#endif + +#ifndef NBR_INA226 +#define NBR_INA226 0 +#endif + +#ifndef NBR_OF_TMP102 +#define NBR_OF_TMP102 0 +#endif + +#ifndef NBR_OF_TMP1075 +#define NBR_OF_TMP1075 0 +#endif + +#ifndef BOS0614_NBR_OF_INSTANCE +#define BOS0614_NBR_OF_INSTANCE 0 +#endif + +#ifndef BOS1211_NBR_OF_INSTANCE +#define BOS1211_NBR_OF_INSTANCE 0 +#endif + +#ifndef SENSING_INSTANCE_NBR +#define SENSING_INSTANCE_NBR 0 +#endif + +#ifndef SENSING_CALLBACK_SUBSCRIBER_DEPTH +#define SENSING_CALLBACK_SUBSCRIBER_DEPTH (0) +#endif + +#ifndef BOS1901_NBR_OF_INSTANCE +#define BOS1901_NBR_OF_INSTANCE 0 +#endif + +#ifndef NBR_OF_TDC7200 +#define NBR_OF_TDC7200 0 +#endif + + +//Trigger playback config +#ifndef MAXIMUM_NB_OF_CHANNEL_LINKED +#define MAXIMUM_NB_OF_CHANNEL_LINKED (3) +#endif + +#ifndef ISR_PLAYBACK_INSTANCE_NBR +#define ISR_PLAYBACK_INSTANCE_NBR (1) +#endif + + +//Streaming config +#ifndef STREAMING_INSTANCE_NBR +#define STREAMING_INSTANCE_NBR (8) +#endif + + +//Stack config +#ifndef STACK_MAX_TRANSACTION_LENGTH +#define STACK_MAX_TRANSACTION_LENGTH (128) +#endif + +#ifndef STACK_MAX_PENDING_TRANSACTION +#define STACK_MAX_PENDING_TRANSACTION (8) +#endif + + +//Player config +#ifndef PLAYER_FEEDBACK_NBR_OF_WAVEFORM +#define PLAYER_FEEDBACK_NBR_OF_WAVEFORM (16) +#endif + +#ifndef PLAYER_INSTANCE_NBR +#define PLAYER_INSTANCE_NBR (0) +#endif + +#ifndef PLAYER_BUFFER_SIZE +#define PLAYER_BUFFER_SIZE (5120) // 5120 * 16bitPerSample = 10KB +#endif + +#ifndef PLAYER_CALLBACK_SUBSCRIBER_DEPTH +#define PLAYER_CALLBACK_SUBSCRIBER_DEPTH (0) +#endif + + +//Sensing config +#ifndef SENSING_NBR_OF_CONFIG_PER_CHANNEL +#define SENSING_NBR_OF_CONFIG_PER_CHANNEL (2) +#endif + +#ifndef SENSING_CHANNEL_PER_DRIVER +#define SENSING_CHANNEL_PER_DRIVER (1) +#endif + + +//IC Status config +#ifndef NBR_OF_ERROR +#define NBR_OF_ERROR (0) +#endif + +#ifndef NBR_OF_HAPTIC_DRIVER +#define NBR_OF_HAPTIC_DRIVER (0) +#endif + + +// HFEM config +#ifndef HFEM_NBR_OF_DRIVERS +#define HFEM_NBR_OF_DRIVERS (0) +#endif + +#ifndef HFEM_MAXIMUM_NBR_OF_TRIGGER +#define HFEM_MAXIMUM_NBR_OF_TRIGGER (0) +#endif + +#ifndef HFEM_INSTANCE_MAXIMUM_NBR +#define HFEM_INSTANCE_MAXIMUM_NBR (0) +#endif + +// Captouch config +#ifndef CAPTOUCH_MAXIMUM_INSTANCE_NBR +#define CAPTOUCH_MAXIMUM_INSTANCE_NBR (0) +#endif + +#ifndef USER_BUTTON_INDEX +#define USER_BUTTON_INDEX (0) +#endif + +#ifndef FITNESS_TRACKER_CAPTOUCH_INDEX +#define FITNESS_TRACKER_CAPTOUCH_INDEX (0) +#endif + +#endif //DK_CORE_COREDEF_H diff --git a/drivers/input/misc/bos0614_mmi/libs/dk-core/src/utils/registerUtil.h b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/utils/registerUtil.h new file mode 100644 index 000000000000..32d185bf18f7 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/libs/dk-core/src/utils/registerUtil.h @@ -0,0 +1,31 @@ +// +// Copyright (c) 2020 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef ST_DK_BSP_REGISTERUTIL_H +#define ST_DK_BSP_REGISTERUTIL_H + +#define CLEAR_BITS(reg, mask) ((reg) &= (~(mask))) +#define SET_BITS(reg, mask) ((reg) |= (mask)) + +#define GET_VALUE(reg, mask, shift) (((reg) & (mask)) >> (shift)) +#define SET_VALUE(reg, mask, shift, value) ((CLEAR_BITS(reg, mask) & ((value << shift) & mask))) + +#endif //ST_DK_BSP_REGISTERUTIL_H diff --git a/drivers/input/misc/bos0614_mmi/stdint.h b/drivers/input/misc/bos0614_mmi/stdint.h new file mode 100644 index 000000000000..85be90b73105 --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/stdint.h @@ -0,0 +1,30 @@ +// +// Description: @TODO +// +// Copyright (c) 2020 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#ifndef BOREAS_HAPTIC_DRIVER_LINUX_STDINT_H +#define BOREAS_HAPTIC_DRIVER_LINUX_STDINT_H + +#include + +#endif //BOREAS_HAPTIC_DRIVER_LINUX_STDINT_H diff --git a/drivers/input/misc/bos0614_mmi/string.h b/drivers/input/misc/bos0614_mmi/string.h new file mode 100644 index 000000000000..e6922c31f27f --- /dev/null +++ b/drivers/input/misc/bos0614_mmi/string.h @@ -0,0 +1,30 @@ +// +// Description: Linux String Wrapper +// +// Copyright (c) 2020 Boreas Technologies All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +#ifndef BORÉAS_HAPTIC_DRIVER_LINUX_STRING_H +#define BORÉAS_HAPTIC_DRIVER_LINUX_STRING_H + +#include + +#endif //BORÉAS_HAPTIC_DRIVER_LINUX_STRING_H diff --git a/drivers/input/misc/chipone_fps_mmi/Android.mk b/drivers/input/misc/chipone_fps_mmi/Android.mk new file mode 100644 index 000000000000..bb205ebbf435 --- /dev/null +++ b/drivers/input/misc/chipone_fps_mmi/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := fpsensor_spi_tee.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/misc/chipone_fps_mmi/Kbuild b/drivers/input/misc/chipone_fps_mmi/Kbuild new file mode 100644 index 000000000000..9e3e67d9d2a1 --- /dev/null +++ b/drivers/input/misc/chipone_fps_mmi/Kbuild @@ -0,0 +1,14 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +prod_list := hawao penang pnangn +find_target := $(foreach find, $(prod_list), $(findstring $(find), $(TARGET_PRODUCT))) +find_target := $(strip $(find_target)) +$(warning find_target=$(find_target)) +ifneq ($(find_target),) +$(warning define SUPPORT_POWER_GPIO) +EXTRA_CFLAGS += -DSUPPORT_POWER_GPIO +endif + +obj-m += fpsensor_spi_tee.o diff --git a/drivers/input/misc/chipone_fps_mmi/Makefile b/drivers/input/misc/chipone_fps_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/misc/chipone_fps_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/misc/chipone_fps_mmi/fpsensor_spi_tee.c b/drivers/input/misc/chipone_fps_mmi/fpsensor_spi_tee.c new file mode 100644 index 000000000000..9e166a154152 --- /dev/null +++ b/drivers/input/misc/chipone_fps_mmi/fpsensor_spi_tee.c @@ -0,0 +1,944 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef SUPPORT_POWER_GPIO +#include +#endif +#ifdef CONFIG_COMPAT +#include +#endif + +#include "fpsensor_spi_tee.h" +#include "fpsensor_wakelock.h" +#define FPSENSOR_FB_DRM 1 +#define FPSENSOR_FB_OLD 2 + +#define FPSENSOR_WAKEUP_SOURCE 1 +#define FPSENSOR_WAKEUP_OLD 2 + +#define FPSENSOR_SPI_VERSION "fpsensor_spi_tee_qcomm_v1.23.1" +#ifdef SUPPORT_POWER_GPIO +#define FPSENSOR_USE_QCOM_POWER_GPIO 1 +#else +#define FPSENSOR_USE_QCOM_POWER_GPIO 0 +#endif +#define FPSENSOR_WAKEUP_TYPE FPSENSOR_WAKEUP_SOURCE +#define FP_NOTIFY 1 +#define FP_NOTIFY_TYPE FPSENSOR_FB_DRM + +#if FP_NOTIFY +#if FP_NOTIFY_TYPE == FPSENSOR_FB_DRM +#include +#define FP_NOTIFY_ON MSM_DRM_BLANK_UNBLANK +#define FP_NOTIFY_OFF MSM_DRM_BLANK_POWERDOWN +#define FP_NOTIFY_EVENT_BLANK MSM_DRM_EARLY_EVENT_BLANK //MSM_DRM_EVENT_BLANK +#define fpsensor_fb_register_client(client) msm_drm_register_client(client); +#define fpsensor_fb_unregister_client(client) msm_drm_unregister_client(client); +#else +#define FP_NOTIFY_ON FB_BLANK_UNBLANK +#define FP_NOTIFY_OFF FB_BLANK_POWERDOWN +#define FP_NOTIFY_EVENT_BLANK FB_EVENT_BLANK +#define fpsensor_fb_register_client(client) fb_register_client(client); +#define fpsensor_fb_unregister_client(client) fb_unregister_client(client) +#endif +#endif + +/* debug log setting */ +static u8 fpsensor_debug_level = INFO_LOG; +/* global variables */ +static fpsensor_data_t *g_fpsensor = NULL; +uint32_t g_cmd_sn = 0; + +#if FPSENSOR_WAKEUP_TYPE == FPSENSOR_WAKEUP_SOURCE +#include +#include +struct wakeup_source g_ttw_wl; +#else +#include +struct wake_lock g_ttw_wl; +#endif + +/* -------------------------------------------------------------------- */ +/* fingerprint chip hardware configuration */ +/* -------------------------------------------------------------------- */ +static void fpsensor_gpio_free(fpsensor_data_t *fpsensor) +{ + struct device *dev = &fpsensor->spi->dev; + + if (fpsensor->irq_gpio != 0 ) { + devm_gpio_free(dev, fpsensor->irq_gpio); + fpsensor->irq_gpio = 0; + } + if (fpsensor->reset_gpio != 0) { + devm_gpio_free(dev, fpsensor->reset_gpio); + fpsensor->reset_gpio = 0; + } +#if FPSENSOR_USE_QCOM_POWER_GPIO + if (fpsensor->power_gpio != 0) { + devm_gpio_free(dev, fpsensor->power_gpio); + fpsensor->power_gpio = 0; + } +#endif +} + +#if FPSENSOR_USE_QCOM_POWER_GPIO +static void fpsensor_power_on(fpsensor_data_t *fpsensor) +{ +// struct device *dev = &fpsensor->spi->dev; + int ret = 0; + int value = 0; + if(fpsensor->pinctrl){ + if(fpsensor->vcc_high){ + ret = pinctrl_select_state(fpsensor->pinctrl, fpsensor->vcc_high); + if(ret){ + fpsensor_debug(ERR_LOG, "Failed to pinctl vcc_high . ret = %d\n", ret); + } + } + } + fpsensor_debug(ERR_LOG, "fpsensor power_gpio=%d\n", fpsensor->power_gpio); + if (fpsensor->power_gpio != 0) { + // set power on + ret = gpio_direction_output(fpsensor->power_gpio, 1); + fpsensor_debug(ERR_LOG,"fpsensor power_gpio power on ret :%d\n",ret); + gpio_set_value(fpsensor->power_gpio, 1); + fpsensor_debug(ERR_LOG, "fpsensor fingerprint power on\n"); + value = gpio_get_value(fpsensor->power_gpio); + fpsensor_debug(ERR_LOG,"fpsensor power_gpio power on value :%d\n",value); + } + if (fpsensor->reset_gpio != 0) { + // set reset_gpio high + gpio_direction_output(fpsensor->reset_gpio, 1); + gpio_set_value(fpsensor->reset_gpio, 1); + fpsensor_debug(ERR_LOG, "fpsensor fingerprint reset_gpio high\n"); + } +} +static void fpsensor_power_off(fpsensor_data_t *fpsensor) +{ + int ret = 0; +// struct device *dev = &fpsensor->spi->dev; + if(fpsensor->pinctrl){ + if(fpsensor->vcc_low){ + ret = pinctrl_select_state(fpsensor->pinctrl, fpsensor->vcc_low); + if(ret){ + fpsensor_debug(ERR_LOG, "Failed to pinctl vcc_low . ret = %d\n", ret); + } + } + } + fpsensor_debug(ERR_LOG, "fpsensor power_gpio=%d\n", fpsensor->power_gpio); + if (fpsensor->power_gpio != 0) { + // set power off + gpio_direction_output(fpsensor->power_gpio, 0); + gpio_set_value(fpsensor->power_gpio, 0); + fpsensor_debug(ERR_LOG, "fpsensor fingerprint power off\n"); + } + if (fpsensor->reset_gpio != 0) { + // set reset_gpio low + gpio_direction_output(fpsensor->reset_gpio, 0); + gpio_set_value(fpsensor->reset_gpio, 0); + fpsensor_debug(ERR_LOG, "fpsensor fingerprint reset_gpio low\n"); + } +} +#endif + +static void fpsensor_irq_gpio_cfg(fpsensor_data_t *fpsensor) +{ + int error = 0; + + error = gpio_direction_input(fpsensor->irq_gpio); + if (error) { + fpsensor_debug(ERR_LOG, "setup fpsensor irq gpio for input failed!error[%d]\n", error); + return ; + } + + fpsensor->irq = gpio_to_irq(fpsensor->irq_gpio); + fpsensor_debug(DEBUG_LOG, "fpsensor irq number[%d]\n", fpsensor->irq); + if (fpsensor->irq <= 0) { + fpsensor_debug(ERR_LOG, "fpsensor irq gpio to irq failed!\n"); + return ; + } + + return; +} + +static int fpsensor_request_named_gpio(fpsensor_data_t *fpsensor_dev, const char *label, int *gpio) +{ + struct device *dev = &fpsensor_dev->spi->dev; + struct device_node *np = dev->of_node; + int ret = of_get_named_gpio(np, label, 0); + + if (ret < 0) { + fpsensor_debug(ERR_LOG, "failed to get '%s'\n", label); + return ret; + } + *gpio = ret; + ret = devm_gpio_request(dev, *gpio, label); + if (ret) { + fpsensor_debug(ERR_LOG, "failed to request gpio %d\n", *gpio); + return ret; + } + + fpsensor_debug(ERR_LOG, "%s %d\n", label, *gpio); + return ret; +} + +/* delay us after reset */ +static void fpsensor_hw_reset(int delay) +{ + FUNC_ENTRY(); + gpio_set_value(g_fpsensor->reset_gpio, 1); + + udelay(100); + gpio_set_value(g_fpsensor->reset_gpio, 0); + + udelay(1000); + gpio_set_value(g_fpsensor->reset_gpio, 1); + + if (delay) { + udelay(delay); + } + + FUNC_EXIT(); + return; +} + +#if FPSENSOR_USE_QCOM_POWER_GPIO +static int fpsensor_pinctrl_init(fpsensor_data_t *fpsensor) +{ + int rc = 0; + + fpsensor->pinctrl = devm_pinctrl_get(&fpsensor->spi->dev); + if (IS_ERR_OR_NULL(fpsensor->pinctrl)) { + fpsensor_debug(ERR_LOG, "Failed to get pinctrl handler"); + fpsensor->pinctrl = NULL; + return -EINVAL; + } + + + fpsensor->reset_high = pinctrl_lookup_state(fpsensor->pinctrl, "fps_rst_active"); + if (IS_ERR(fpsensor->reset_high)) + { + rc = PTR_ERR(fpsensor->reset_high); + fpsensor_debug(ERR_LOG," %s : can't find fingerprint pinctrl fps_rst_active \n", __func__); + goto fpsensorfp_pinctrl_fail; + } + fpsensor->reset_low = pinctrl_lookup_state(fpsensor->pinctrl, "fps_rst_suspend"); + if (IS_ERR(fpsensor->reset_low)) + { + rc = PTR_ERR(fpsensor->reset_low); + fpsensor_debug(ERR_LOG," %s : can't find fingerprint pinctrl fps_rst_suspend \n", __func__); + goto fpsensorfp_pinctrl_fail; + } + + fpsensor->vcc_high = pinctrl_lookup_state(fpsensor->pinctrl, "fps_ido1p8_en_active"); + if (IS_ERR(fpsensor->vcc_high)) + { + rc = PTR_ERR(fpsensor->vcc_high); + fpsensor_debug(ERR_LOG," %s : can't find fingerprint pinctrl fps_ido1p8_en_active \n", __func__); + goto fpsensorfp_pinctrl_fail; + } + fpsensor->vcc_low = pinctrl_lookup_state(fpsensor->pinctrl, "fps_ido1p8_en_suspend"); + if (IS_ERR(fpsensor->vcc_low)) + { + rc = PTR_ERR(fpsensor->vcc_low); + fpsensor_debug(ERR_LOG," %s : can't find fingerprint pinctrl fps_ido1p8_en_suspend \n", __func__); + goto fpsensorfp_pinctrl_fail; + } + return rc; + +fpsensorfp_pinctrl_fail: + fpsensor_debug(ERR_LOG, "Failed to pinctl lookup&select . rc = %d\n", rc); + devm_pinctrl_put(fpsensor->pinctrl); + fpsensor->pinctrl = NULL; + return rc; +} +#endif + +static int fpsensor_get_gpio_dts_info(fpsensor_data_t *fpsensor) +{ + int ret = 0; + FUNC_ENTRY(); + // get interrupt gpio resource +#if FPSENSOR_USE_QCOM_POWER_GPIO + + ret = fpsensor_pinctrl_init(fpsensor); + if(ret){ + fpsensor_debug(ERR_LOG, "Failed to pinctl request . ret = %d\n", ret); + } +#endif + ret = fpsensor_request_named_gpio(fpsensor, "fp-gpio-int", &fpsensor->irq_gpio); + if (ret) { + fpsensor_debug(ERR_LOG, "Failed to request irq GPIO. ret = %d\n", ret); + return -1; + } + + // get reest gpio resourece + ret = fpsensor_request_named_gpio(fpsensor, "fp-gpio-reset", &fpsensor->reset_gpio); + if (ret) { + fpsensor_debug(ERR_LOG, "Failed to request reset GPIO. ret = %d\n", ret); + return -1; + } +#if FPSENSOR_USE_QCOM_POWER_GPIO + if(fpsensor->pinctrl){ + if(fpsensor->vcc_high){ + ret = pinctrl_select_state(fpsensor->pinctrl, fpsensor->vcc_high); + if(ret){ + fpsensor_debug(ERR_LOG, "Failed to pinctl vcc_high . ret = %d\n", ret); + } + } + } + // get power gpio resourece + ret = fpsensor_request_named_gpio(fpsensor, "fp-gpio-power", &fpsensor->power_gpio); + if (ret) { + fpsensor_debug(ERR_LOG, "Failed to request power GPIO. ret = %d\n", ret); + return -1; + } + // set power direction output + ret = gpio_direction_output(fpsensor->power_gpio, 1); + fpsensor_debug(ERR_LOG,"fpsensor power_gpio power on dts ret:%d\n",ret); + gpio_set_value(fpsensor->power_gpio, 1); +#endif + // set reset direction output +#if FPSENSOR_USE_QCOM_POWER_GPIO + if(fpsensor->pinctrl){ + if(fpsensor->reset_high){ + ret = pinctrl_select_state(fpsensor->pinctrl, fpsensor->reset_high); + if(ret){ + fpsensor_debug(ERR_LOG, "Failed to pinctl reset_high . ret = %d\n", ret); + } + } + } + gpio_direction_output(fpsensor->reset_gpio, 1); + gpio_set_value(g_fpsensor->reset_gpio, 1); + mdelay(4); + //fpsensor_hw_reset(1250); +#else + gpio_direction_output(fpsensor->reset_gpio, 1); + fpsensor_hw_reset(1250); +#endif + return ret; +} + +static void setRcvIRQ(int val) +{ + fpsensor_data_t *fpsensor_dev = g_fpsensor; + fpsensor_dev->RcvIRQ = val; +} + +static void fpsensor_enable_irq(fpsensor_data_t *fpsensor_dev) +{ + FUNC_ENTRY(); + setRcvIRQ(0); + /* Request that the interrupt should be wakeable */ + if (fpsensor_dev->irq_enabled == 0) { + enable_irq(fpsensor_dev->irq); + fpsensor_dev->irq_enabled = 1; + } + FUNC_EXIT(); + return; +} + +static void fpsensor_disable_irq(fpsensor_data_t *fpsensor_dev) +{ + FUNC_ENTRY(); + + if (0 == fpsensor_dev->device_available) { + fpsensor_debug(ERR_LOG, "%s, devices not available\n", __func__); + goto out; + } + + if (0 == fpsensor_dev->irq_enabled) { + fpsensor_debug(ERR_LOG, "%s, irq already disabled\n", __func__); + goto out; + } + + if (fpsensor_dev->irq) { + disable_irq_nosync(fpsensor_dev->irq); + fpsensor_debug(DEBUG_LOG, "%s disable interrupt!\n", __func__); + } + fpsensor_dev->irq_enabled = 0; + +out: + setRcvIRQ(0); + FUNC_EXIT(); + return; +} + +static irqreturn_t fpsensor_irq(int irq, void *handle) +{ + fpsensor_data_t *fpsensor_dev = (fpsensor_data_t *)handle; + + /* Make sure 'wakeup_enabled' is updated before using it + ** since this is interrupt context (other thread...) */ + smp_rmb(); +#if FPSENSOR_WAKEUP_TYPE == FPSENSOR_WAKEUP_SOURCE + __pm_wakeup_event(&g_ttw_wl, 1000); +#else + wake_lock_timeout(&g_ttw_wl, msecs_to_jiffies(1000)); +#endif + setRcvIRQ(1); + wake_up_interruptible(&fpsensor_dev->wq_irq_return); + + return IRQ_HANDLED; +} + +// release and cleanup fpsensor char device +static void fpsensor_dev_cleanup(fpsensor_data_t *fpsensor) +{ + FUNC_ENTRY(); + + if (fpsensor->class == NULL) { + fpsensor_debug(INFO_LOG, "Cleanup with fpsensor = NULL\n"); + goto out; + } + cdev_del(&fpsensor->cdev); + unregister_chrdev_region(fpsensor->devno, FPSENSOR_NR_DEVS); + device_destroy(fpsensor->class, fpsensor->devno); + class_destroy(fpsensor->class); + fpsensor->class = NULL; + +out: + FUNC_EXIT(); +} + +static long fpsensor_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + fpsensor_data_t *fpsensor_dev = NULL; + int retval = 0; + uint32_t val = 0; + int irqf; + + fpsensor_debug(INFO_LOG, "[rickon]: fpsensor ioctl cmd : 0x%x \n", cmd ); + fpsensor_dev = (fpsensor_data_t *)filp->private_data; + fpsensor_dev->cancel = 0 ; + switch (cmd) { + case FPSENSOR_IOC_INIT: + fpsensor_debug(INFO_LOG, "%s: fpsensor init started======\n", __func__); + +#ifndef SUPPORT_POWER_GPIO + retval = fpsensor_get_gpio_dts_info(fpsensor_dev); + if (retval) { + break; + } + fpsensor_irq_gpio_cfg(fpsensor_dev); +#endif + //regist irq + irqf = IRQF_TRIGGER_RISING | IRQF_ONESHOT; + retval = devm_request_threaded_irq(&g_fpsensor->spi->dev, g_fpsensor->irq, fpsensor_irq, + NULL, irqf, dev_name(&g_fpsensor->spi->dev), g_fpsensor); + if (retval == 0) { + fpsensor_debug(INFO_LOG, " irq thread reqquest success!\n"); + } else { + fpsensor_debug(INFO_LOG, " irq thread request failed , retval =%d \n", retval); + break; + } + enable_irq_wake(g_fpsensor->irq); + fpsensor_dev->device_available = 1; + // fix Unbalanced enable for IRQ, disable irq at first + fpsensor_dev->irq_enabled = 1; + fpsensor_disable_irq(fpsensor_dev); + fpsensor_debug(INFO_LOG, "%s: fpsensor init finished======\n", __func__); + break; + + case FPSENSOR_IOC_EXIT: + if(fpsensor_dev->device_available) { + fpsensor_disable_irq(fpsensor_dev); + if (fpsensor_dev->irq) { + devm_free_irq(&g_fpsensor->spi->dev, fpsensor_dev->irq, fpsensor_dev); + fpsensor_dev->irq_enabled = 0; + } + fpsensor_dev->device_available = 0; + //fpsensor_gpio_free(fpsensor_dev); + fpsensor_debug(INFO_LOG, "%s: fpsensor exit finished======\n", __func__); + } else { + fpsensor_debug(INFO_LOG, "%s: IOC_EXIT with device_available = 0 ======\n", __func__); + } + break; + + case FPSENSOR_IOC_RESET: + fpsensor_debug(INFO_LOG, "%s: chip reset command\n", __func__); +#if FPSENSOR_USE_QCOM_POWER_GPIO + fpsensor_hw_reset(4000); +#else + fpsensor_hw_reset(1250); +#endif + break; + + case FPSENSOR_IOC_ENABLE_IRQ: + fpsensor_debug(INFO_LOG, "%s: chip ENable IRQ command\n", __func__); + fpsensor_enable_irq(fpsensor_dev); + break; + + case FPSENSOR_IOC_DISABLE_IRQ: + fpsensor_debug(INFO_LOG, "%s: chip disable IRQ command\n", __func__); + fpsensor_disable_irq(fpsensor_dev); + break; + case FPSENSOR_IOC_GET_INT_VAL: + val = gpio_get_value(fpsensor_dev->irq_gpio); + if (copy_to_user((void __user *)arg, (void *)&val, sizeof(uint32_t))) { + fpsensor_debug(ERR_LOG, "Failed to copy data to user\n"); + retval = -EFAULT; + break; + } + retval = 0; + break; + case FPSENSOR_IOC_ENABLE_SPI_CLK: + fpsensor_debug(INFO_LOG, "%s: ENABLE_SPI_CLK ======\n", __func__); + break; + case FPSENSOR_IOC_DISABLE_SPI_CLK: + fpsensor_debug(INFO_LOG, "%s: DISABLE_SPI_CLK ======\n", __func__); + break; + case FPSENSOR_IOC_ENABLE_POWER: + fpsensor_debug(INFO_LOG, "%s: FPSENSOR_IOC_ENABLE_POWER ======\n", __func__); +#if FPSENSOR_USE_QCOM_POWER_GPIO + fpsensor_power_on(fpsensor_dev); +#endif + break; + case FPSENSOR_IOC_DISABLE_POWER: + fpsensor_debug(INFO_LOG, "%s: FPSENSOR_IOC_DISABLE_POWER ======\n", __func__); +#if FPSENSOR_USE_QCOM_POWER_GPIO + fpsensor_power_off(fpsensor_dev); +#endif + break; + case FPSENSOR_IOC_REMOVE: + if(fpsensor_dev->device_available) { + fpsensor_disable_irq(fpsensor_dev); + if (fpsensor_dev->irq) { + devm_free_irq(&g_fpsensor->spi->dev, fpsensor_dev->irq, fpsensor_dev); + fpsensor_dev->irq_enabled = 0; + } + fpsensor_gpio_free(fpsensor_dev); +#if FPSENSOR_USE_QCOM_POWER_GPIO + if(fpsensor_dev->pinctrl){ + devm_pinctrl_put(fpsensor_dev->pinctrl); + fpsensor_dev->pinctrl = NULL; + } +#endif + fpsensor_dev->device_available = 0; + } else { + fpsensor_debug(INFO_LOG, "%s: IOC_REMOVE with device_available = 0 ======\n", __func__); + } + fpsensor_dev_cleanup(fpsensor_dev); +#if FP_NOTIFY + fpsensor_fb_unregister_client(&fpsensor_dev->notifier); +#endif + fpsensor_dev->free_flag = 1; + fpsensor_debug(INFO_LOG, "%s remove finished\n", __func__); + break; + case FPSENSOR_IOC_CANCEL_WAIT: + fpsensor_debug(INFO_LOG, "%s: FPSENSOR CANCEL WAIT\n", __func__); + wake_up_interruptible(&fpsensor_dev->wq_irq_return); + fpsensor_dev->cancel = 1; + break; +#if FP_NOTIFY + case FPSENSOR_IOC_GET_FP_STATUS : + val = fpsensor_dev->fb_status; + fpsensor_debug(INFO_LOG, "%s: FPSENSOR_IOC_GET_FP_STATUS %d \n",__func__, fpsensor_dev->fb_status); + if (copy_to_user((void __user *)arg, (void *)&val, sizeof(uint32_t))) { + fpsensor_debug(ERR_LOG, "Failed to copy data to user\n"); + retval = -EFAULT; + break; + } + retval = 0; + break; +#endif + case FPSENSOR_IOC_ENABLE_REPORT_BLANKON: + if (copy_from_user(&val, (void __user *)arg, sizeof(uint32_t))) { + retval = -EFAULT; + break; + } + fpsensor_dev->enable_report_blankon = val; + fpsensor_debug(INFO_LOG, "%s: FPSENSOR_IOC_ENABLE_REPORT_BLANKON: %d\n", __func__, val); + break; + case FPSENSOR_IOC_UPDATE_DRIVER_SN: + if (copy_from_user(&g_cmd_sn, (void __user *)arg, sizeof(uint32_t))) { + fpsensor_debug(ERR_LOG, "Failed to copy g_cmd_sn from user to kernel\n"); + retval = -EFAULT; + break; + } + //fpsensor_debug(INFO_LOG, "%s: FPSENSOR_IOC_UPDATE_DRIVER_SN: %d\n", __func__, g_cmd_sn); + break; + default: + fpsensor_debug(ERR_LOG, "fpsensor doesn't support this command(0x%x)\n", cmd); + break; + } + + //FUNC_EXIT(); + return retval; +} + +static long fpsensor_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + return fpsensor_ioctl(filp, cmd, (unsigned long)(arg)); +} + +static unsigned int fpsensor_poll(struct file *filp, struct poll_table_struct *wait) +{ + unsigned int ret = 0; + + ret |= POLLIN; + poll_wait(filp, &g_fpsensor->wq_irq_return, wait); + if (g_fpsensor->cancel == 1) { + fpsensor_debug(ERR_LOG, " cancle\n"); + ret = POLLERR; + g_fpsensor->cancel = 0; + return ret; + } + + if ( g_fpsensor->RcvIRQ) { + if (g_fpsensor->RcvIRQ == 2) { + fpsensor_debug(ERR_LOG, " get fp on notify\n"); + ret |= POLLHUP; + } else { + fpsensor_debug(ERR_LOG, " get irq\n"); + ret |= POLLRDNORM; + } + } else { + ret = 0; + } + return ret; +} + +static int fpsensor_open(struct inode *inode, struct file *filp) +{ + fpsensor_data_t *fpsensor_dev; + + FUNC_ENTRY(); + fpsensor_dev = container_of(inode->i_cdev, fpsensor_data_t, cdev); + fpsensor_dev->users++; + fpsensor_dev->device_available = 1; + filp->private_data = fpsensor_dev; + FUNC_EXIT(); + return 0; +} + +static int fpsensor_release(struct inode *inode, struct file *filp) +{ + fpsensor_data_t *fpsensor_dev; + int status = 0; + + FUNC_ENTRY(); + fpsensor_dev = filp->private_data; + filp->private_data = NULL; + + /*last close??*/ + fpsensor_dev->users--; + if (fpsensor_dev->users <= 0) { + fpsensor_debug(INFO_LOG, "%s, disble_irq. irq = %d\n", __func__, fpsensor_dev->irq); + fpsensor_disable_irq(fpsensor_dev); + } + fpsensor_dev->device_available = 0; + FUNC_EXIT(); + return status; +} + +static ssize_t fpsensor_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + fpsensor_debug(ERR_LOG, "Not support read opertion in TEE version\n"); + return -EFAULT; +} + +static ssize_t fpsensor_write(struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos) +{ + fpsensor_debug(ERR_LOG, "Not support write opertion in TEE version\n"); + return -EFAULT; +} + +static const struct file_operations fpsensor_fops = { + .owner = THIS_MODULE, + .write = fpsensor_write, + .read = fpsensor_read, + .unlocked_ioctl = fpsensor_ioctl, + .compat_ioctl = fpsensor_compat_ioctl, + .open = fpsensor_open, + .release = fpsensor_release, + .poll = fpsensor_poll, + +}; + +// create and register a char device for fpsensor +static int fpsensor_dev_setup(fpsensor_data_t *fpsensor) +{ + int ret = 0; + dev_t dev_no = 0; + struct device *dev = NULL; + int fpsensor_dev_major = FPSENSOR_DEV_MAJOR; + int fpsensor_dev_minor = 0; + + FUNC_ENTRY(); + + if (fpsensor_dev_major) { + dev_no = MKDEV(fpsensor_dev_major, fpsensor_dev_minor); + ret = register_chrdev_region(dev_no, FPSENSOR_NR_DEVS, FPSENSOR_DEV_NAME); + } else { + ret = alloc_chrdev_region(&dev_no, fpsensor_dev_minor, FPSENSOR_NR_DEVS, FPSENSOR_DEV_NAME); + fpsensor_dev_major = MAJOR(dev_no); + fpsensor_dev_minor = MINOR(dev_no); + fpsensor_debug(INFO_LOG, "fpsensor device major is %d, minor is %d\n", + fpsensor_dev_major, fpsensor_dev_minor); + } + + if (ret < 0) { + fpsensor_debug(ERR_LOG, "can not get device major number %d\n", fpsensor_dev_major); + goto out; + } + + cdev_init(&fpsensor->cdev, &fpsensor_fops); + fpsensor->cdev.owner = THIS_MODULE; + fpsensor->cdev.ops = &fpsensor_fops; + fpsensor->devno = dev_no; + ret = cdev_add(&fpsensor->cdev, dev_no, FPSENSOR_NR_DEVS); + if (ret) { + fpsensor_debug(ERR_LOG, "add char dev for fpsensor failed\n"); + goto release_region; + } + + fpsensor->class = class_create(THIS_MODULE, FPSENSOR_CLASS_NAME); + if (IS_ERR(fpsensor->class)) { + fpsensor_debug(ERR_LOG, "create fpsensor class failed\n"); + ret = PTR_ERR(fpsensor->class); + goto release_cdev; + } + + dev = device_create(fpsensor->class, &fpsensor->spi->dev, dev_no, fpsensor, FPSENSOR_DEV_NAME); + if (IS_ERR(dev)) { + fpsensor_debug(ERR_LOG, "create device for fpsensor failed\n"); + ret = PTR_ERR(dev); + goto release_class; + } + FUNC_EXIT(); + return ret; + +release_class: + class_destroy(fpsensor->class); + fpsensor->class = NULL; +release_cdev: + cdev_del(&fpsensor->cdev); +release_region: + unregister_chrdev_region(dev_no, FPSENSOR_NR_DEVS); +out: + FUNC_EXIT(); + return ret; +} + +#if FP_NOTIFY +static int fpsensor_fb_notifier_callback(struct notifier_block* self, unsigned long event, void* data) +{ + int retval = 0; + static char screen_status[64] = { '\0' }; + struct fb_event* evdata = data; + unsigned int blank; + fpsensor_data_t *fpsensor_dev = g_fpsensor; + + fpsensor_debug(INFO_LOG,"%s enter, event: 0x%x\n", __func__, (unsigned)event); + if (event != FP_NOTIFY_EVENT_BLANK) { + return 0; + } + + blank = *(int*)evdata->data; + fpsensor_debug(INFO_LOG,"%s enter, blank=0x%x\n", __func__, blank); + + switch (blank) { + case FP_NOTIFY_ON: + fpsensor_debug(INFO_LOG,"%s: lcd on notify\n", __func__); + sprintf(screen_status, "SCREEN_STATUS=%s", "ON"); + fpsensor_dev->fb_status = 1; + if( fpsensor_dev->enable_report_blankon) { + fpsensor_dev->RcvIRQ = 2; + wake_up_interruptible(&fpsensor_dev->wq_irq_return); + } + break; + + case FP_NOTIFY_OFF: + fpsensor_debug(INFO_LOG,"%s: lcd off notify\n", __func__); + sprintf(screen_status, "SCREEN_STATUS=%s", "OFF"); + fpsensor_dev->fb_status = 0; + break; + + default: + fpsensor_debug(INFO_LOG,"%s: other notifier, ignore\n", __func__); + break; + } + + fpsensor_debug(INFO_LOG,"%s %s leave.\n", screen_status, __func__); + return retval; +} +#endif + +static int fpsensor_probe(struct platform_device *pdev) +{ + int status = 0; + fpsensor_data_t *fpsensor_dev = NULL; + + FUNC_ENTRY(); + + /* Allocate driver data */ + fpsensor_dev = kzalloc(sizeof(*fpsensor_dev), GFP_KERNEL); + if (!fpsensor_dev) { + status = -ENOMEM; + fpsensor_debug(ERR_LOG, "%s, Failed to alloc memory for fpsensor device.\n", __func__); + goto out; + } + + /* Initialize the driver data */ + g_fpsensor = fpsensor_dev; + fpsensor_dev->spi = pdev ; + fpsensor_dev->device_available = 0; + fpsensor_dev->users = 0; + fpsensor_dev->irq = 0; + fpsensor_dev->power_gpio = 0; + fpsensor_dev->reset_gpio = 0; + fpsensor_dev->irq_gpio = 0; + fpsensor_dev->irq_enabled = 0; + fpsensor_dev->free_flag = 0; + fpsensor_dev->fb_status = 1; + /* setup a char device for fpsensor */ + status = fpsensor_dev_setup(fpsensor_dev); + if (status) { + fpsensor_debug(ERR_LOG, "fpsensor setup char device failed, %d", status); + goto release_drv_data; + } +#if FPSENSOR_USE_QCOM_POWER_GPIO + status = fpsensor_get_gpio_dts_info(fpsensor_dev); + if (status) { + fpsensor_debug(ERR_LOG, "fpsensor setup char device failed, %d", status); + goto release_drv_data; + } + fpsensor_irq_gpio_cfg(fpsensor_dev); +#endif + init_waitqueue_head(&fpsensor_dev->wq_irq_return); +#if FPSENSOR_WAKEUP_TYPE == FPSENSOR_WAKEUP_SOURCE + wakeup_source_init_internal(&g_ttw_wl, "fpsensor_ttw_wl"); +#else + wake_lock_init(&g_ttw_wl, WAKE_LOCK_SUSPEND, "fpsensor_ttw_wl"); +#endif + fpsensor_dev->device_available = 1; +#if FP_NOTIFY + fpsensor_dev->notifier.notifier_call = fpsensor_fb_notifier_callback; + fpsensor_fb_register_client(&fpsensor_dev->notifier); +#endif + + fpsensor_debug(INFO_LOG, "%s finished, driver version: %s\n", __func__, FPSENSOR_SPI_VERSION); + goto out; + +release_drv_data: + kfree(fpsensor_dev); + fpsensor_dev = NULL; +out: + FUNC_EXIT(); + return status; +} + +static int fpsensor_remove(struct platform_device *pdev) +{ + fpsensor_data_t *fpsensor_dev = g_fpsensor; + int ret = 0; + + FUNC_ENTRY(); + + if (g_fpsensor == NULL) { + fpsensor_debug(ERR_LOG, "%s with g_fpsensor = NULL\n", __func__); + ret = -EINVAL; + goto out; + } + + if(fpsensor_dev->device_available) { + fpsensor_disable_irq(fpsensor_dev); + if (fpsensor_dev->irq) + devm_free_irq(&g_fpsensor->spi->dev, fpsensor_dev->irq, fpsensor_dev); + + fpsensor_gpio_free(fpsensor_dev); +#if FPSENSOR_USE_QCOM_POWER_GPIO + if(fpsensor_dev->pinctrl){ + devm_pinctrl_put(fpsensor_dev->pinctrl); + fpsensor_dev->pinctrl = NULL; + } +#endif + fpsensor_dev->device_available = 0; + } + +#if FP_NOTIFY + fpsensor_fb_unregister_client(&fpsensor_dev->notifier); +#endif + + fpsensor_dev_cleanup(fpsensor_dev); + +#if FPSENSOR_WAKEUP_TYPE == FPSENSOR_WAKEUP_SOURCE + wakeup_source_trash_internal(&g_ttw_wl); +#else + wake_lock_destroy(&g_ttw_wl); +#endif + + kfree(fpsensor_dev); + g_fpsensor = NULL; + +out: + FUNC_EXIT(); + return ret; +} + +static int fpsensor_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +static int fpsensor_resume( struct platform_device *pdev) +{ + return 0; +} + +#ifdef CONFIG_OF +static struct of_device_id fpsensor_of_match[] = { + { .compatible = "qcom,fingerprint-gpio" }, + {} +}; +//MODULE_DEVICE_TABLE(of, fpsensor_of_match); +#endif + +static struct platform_driver fpsensor_driver = { + .driver = { + .name = FPSENSOR_DEV_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = fpsensor_of_match, +#endif + }, + .probe = fpsensor_probe, + .remove = fpsensor_remove, + .suspend = fpsensor_suspend, + .resume = fpsensor_resume, +}; + +static int __init fpsensor_init(void) +{ + int status; + + status = platform_driver_register(&fpsensor_driver); + if (status < 0) { + fpsensor_debug(ERR_LOG, "%s, Failed to register SPI driver.\n", __func__); + } + + return status; +} +module_init(fpsensor_init); + +static void __exit fpsensor_exit(void) +{ + platform_driver_unregister(&fpsensor_driver); +} +module_exit(fpsensor_exit); + +MODULE_AUTHOR("xhli"); +MODULE_DESCRIPTION(" Fingerprint chip TEE driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:fpsensor-drivers"); diff --git a/drivers/input/misc/chipone_fps_mmi/fpsensor_spi_tee.h b/drivers/input/misc/chipone_fps_mmi/fpsensor_spi_tee.h new file mode 100644 index 000000000000..9f98eae221cf --- /dev/null +++ b/drivers/input/misc/chipone_fps_mmi/fpsensor_spi_tee.h @@ -0,0 +1,74 @@ +#ifndef __FPSENSOR_SPI_TEE_H +#define __FPSENSOR_SPI_TEE_H + +#include +#include +#include +#include +#include + +#define FPSENSOR_DEV_NAME "fpsensor" +#define FPSENSOR_CLASS_NAME "fpsensor" +#define FPSENSOR_DEV_MAJOR 0 +#define N_SPI_MINORS 32 /* ... up to 256 */ +#define FPSENSOR_NR_DEVS 1 + +#define ERR_LOG (0) +#define INFO_LOG (1) +#define DEBUG_LOG (2) +#define fpsensor_debug(level, fmt, args...) do { \ + if (fpsensor_debug_level >= level) {\ + printk("[fpCoreDriver][SN=%d] " fmt, g_cmd_sn, ##args); \ + } \ + } while (0) +#define FUNC_ENTRY() fpsensor_debug(DEBUG_LOG, "%s, %d, entry\n", __func__, __LINE__) +#define FUNC_EXIT() fpsensor_debug(DEBUG_LOG, "%s, %d, exit\n", __func__, __LINE__) + +/**********************IO Magic**********************/ +#define FPSENSOR_IOC_MAGIC 0xf0 //CHIP + +/* define commands */ +#define FPSENSOR_IOC_INIT _IOWR(FPSENSOR_IOC_MAGIC,0,uint32_t) +#define FPSENSOR_IOC_EXIT _IOWR(FPSENSOR_IOC_MAGIC,1,uint32_t) +#define FPSENSOR_IOC_RESET _IOWR(FPSENSOR_IOC_MAGIC,2,uint32_t) +#define FPSENSOR_IOC_ENABLE_IRQ _IOWR(FPSENSOR_IOC_MAGIC,3,uint32_t) +#define FPSENSOR_IOC_DISABLE_IRQ _IOWR(FPSENSOR_IOC_MAGIC,4,uint32_t) +#define FPSENSOR_IOC_GET_INT_VAL _IOWR(FPSENSOR_IOC_MAGIC,5,uint32_t) +#define FPSENSOR_IOC_DISABLE_SPI_CLK _IOWR(FPSENSOR_IOC_MAGIC,6,uint32_t) +#define FPSENSOR_IOC_ENABLE_SPI_CLK _IOWR(FPSENSOR_IOC_MAGIC,7,uint32_t) +#define FPSENSOR_IOC_ENABLE_POWER _IOWR(FPSENSOR_IOC_MAGIC,8,uint32_t) +#define FPSENSOR_IOC_DISABLE_POWER _IOWR(FPSENSOR_IOC_MAGIC,9,uint32_t) +/* fp sensor has change to sleep mode while screen off */ +#define FPSENSOR_IOC_ENTER_SLEEP_MODE _IOWR(FPSENSOR_IOC_MAGIC,11,uint32_t) +#define FPSENSOR_IOC_REMOVE _IOWR(FPSENSOR_IOC_MAGIC,12,uint32_t) +#define FPSENSOR_IOC_CANCEL_WAIT _IOWR(FPSENSOR_IOC_MAGIC,13,uint32_t) +#define FPSENSOR_IOC_GET_FP_STATUS _IOWR(FPSENSOR_IOC_MAGIC,19,uint32_t) +#define FPSENSOR_IOC_ENABLE_REPORT_BLANKON _IOWR(FPSENSOR_IOC_MAGIC,21,uint32_t) +#define FPSENSOR_IOC_UPDATE_DRIVER_SN _IOWR(FPSENSOR_IOC_MAGIC,22,uint32_t) +typedef struct { + dev_t devno; + struct class *class; + struct cdev cdev; + struct platform_device *spi; + unsigned int users; + u8 device_available; /* changed during fingerprint chip sleep and wakeup phase */ + u8 irq_enabled; + volatile unsigned int RcvIRQ; + int irq; + int irq_gpio; + int reset_gpio; + int power_gpio; + wait_queue_head_t wq_irq_return; + int cancel; + struct notifier_block notifier; + u8 fb_status; + int enable_report_blankon; + int free_flag; + struct pinctrl *pinctrl; + struct pinctrl_state *reset_high; + struct pinctrl_state *reset_low; + struct pinctrl_state *vcc_high; + struct pinctrl_state *vcc_low; +} fpsensor_data_t; + +#endif /* __FPSENSOR_SPI_TEE_H */ diff --git a/drivers/input/misc/chipone_fps_mmi/fpsensor_wakelock.h b/drivers/input/misc/chipone_fps_mmi/fpsensor_wakelock.h new file mode 100644 index 000000000000..f588b585e9f6 --- /dev/null +++ b/drivers/input/misc/chipone_fps_mmi/fpsensor_wakelock.h @@ -0,0 +1,87 @@ +/* include/linux/wakelock.h + * + * Copyright (C) 2007-2012 Google, Inc. + * + * 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. + * + */ + +#ifndef _BF_FP_WAKELOCK_H +#define _BF_FP_WAKELOCK_H + +#include +#include +#include + +/* A wake_lock prevents the system from entering suspend or other low power + * states when active. If the type is set to WAKE_LOCK_SUSPEND, the wake_lock + * prevents a full system suspend. + */ + +enum { + WAKE_LOCK_SUSPEND, /* Prevent suspend */ + WAKE_LOCK_TYPE_COUNT +}; + +struct wake_lock { + struct wakeup_source ws; +}; + +static inline void wakeup_source_init_internal(struct wakeup_source *ws, + const char *name) +{ + if(ws){ + memset(ws, 0, sizeof(*ws)); + ws->name = name; + } + wakeup_source_add(ws); +} + +static inline void wake_lock_init(struct wake_lock *lock, int type, + const char *name) +{ + wakeup_source_init_internal(&lock->ws, name); +} + +static inline void wakeup_source_trash_internal(struct wakeup_source *ws) +{ + wakeup_source_remove(ws); + if(!ws) + return; + __pm_relax(ws); +} + +static inline void wake_lock_destroy(struct wake_lock *lock) +{ + wakeup_source_trash_internal(&lock->ws); +} + +static inline void wake_lock(struct wake_lock *lock) +{ + __pm_stay_awake(&lock->ws); +} + +static inline void wake_lock_timeout(struct wake_lock *lock, long timeout) +{ + __pm_wakeup_event(&lock->ws, jiffies_to_msecs(timeout)); +} + +static inline void wake_unlock(struct wake_lock *lock) +{ + __pm_relax(&lock->ws); +} + +static inline int wake_lock_active(struct wake_lock *lock) +{ + return lock->ws.active; +} + +#endif + diff --git a/drivers/input/misc/chipone_fps_mmi_v1/Android.mk b/drivers/input/misc/chipone_fps_mmi_v1/Android.mk new file mode 100644 index 000000000000..e309d1e81489 --- /dev/null +++ b/drivers/input/misc/chipone_fps_mmi_v1/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := fpsensor_spi_tee_v1.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/misc/chipone_fps_mmi_v1/Kbuild b/drivers/input/misc/chipone_fps_mmi_v1/Kbuild new file mode 100644 index 000000000000..f3a9670e81ee --- /dev/null +++ b/drivers/input/misc/chipone_fps_mmi_v1/Kbuild @@ -0,0 +1,16 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +prod_list := hawao penang pnangn fogona +find_target := $(foreach find, $(prod_list), $(findstring $(find), $(TARGET_PRODUCT))) +find_target := $(strip $(find_target)) +$(warning find_target=$(find_target)) +ifneq ($(find_target),) +$(warning define SUPPORT_POWER_GPIO) +EXTRA_CFLAGS += -DSUPPORT_POWER_GPIO +endif + +obj-m := fpsensor_spi_tee_v1.o +fpsensor_spi_tee_v1-objs := \ + fpsensor_spi_tee.o diff --git a/drivers/input/misc/chipone_fps_mmi_v1/Makefile b/drivers/input/misc/chipone_fps_mmi_v1/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/input/misc/chipone_fps_mmi_v1/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/input/misc/chipone_fps_mmi_v1/fpsensor_spi_tee.c b/drivers/input/misc/chipone_fps_mmi_v1/fpsensor_spi_tee.c new file mode 100644 index 000000000000..0db57d7a4061 --- /dev/null +++ b/drivers/input/misc/chipone_fps_mmi_v1/fpsensor_spi_tee.c @@ -0,0 +1,944 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef SUPPORT_POWER_GPIO +#include +#endif +#ifdef CONFIG_COMPAT +#include +#endif + +#include "fpsensor_spi_tee.h" +#include "fpsensor_wakelock.h" +#define FPSENSOR_FB_DRM 1 +#define FPSENSOR_FB_OLD 2 + +#define FPSENSOR_WAKEUP_SOURCE 1 +#define FPSENSOR_WAKEUP_OLD 2 + +#define FPSENSOR_SPI_VERSION "fpsensor_spi_tee_qcomm_v1.23.1" +#ifdef SUPPORT_POWER_GPIO +#define FPSENSOR_USE_QCOM_POWER_GPIO 1 +#else +#define FPSENSOR_USE_QCOM_POWER_GPIO 0 +#endif +#define FPSENSOR_WAKEUP_TYPE FPSENSOR_WAKEUP_SOURCE +#define FP_NOTIFY 0 +#define FP_NOTIFY_TYPE FPSENSOR_FB_DRM + +#if FP_NOTIFY +#if FP_NOTIFY_TYPE == FPSENSOR_FB_DRM +#include +#define FP_NOTIFY_ON MSM_DRM_BLANK_UNBLANK +#define FP_NOTIFY_OFF MSM_DRM_BLANK_POWERDOWN +#define FP_NOTIFY_EVENT_BLANK MSM_DRM_EARLY_EVENT_BLANK //MSM_DRM_EVENT_BLANK +#define fpsensor_fb_register_client(client) msm_drm_register_client(client); +#define fpsensor_fb_unregister_client(client) msm_drm_unregister_client(client); +#else +#define FP_NOTIFY_ON FB_BLANK_UNBLANK +#define FP_NOTIFY_OFF FB_BLANK_POWERDOWN +#define FP_NOTIFY_EVENT_BLANK FB_EVENT_BLANK +#define fpsensor_fb_register_client(client) fb_register_client(client); +#define fpsensor_fb_unregister_client(client) fb_unregister_client(client) +#endif +#endif + +/* debug log setting */ +static u8 fpsensor_debug_level = INFO_LOG; +/* global variables */ +static fpsensor_data_t *g_fpsensor = NULL; +uint32_t g_cmd_sn = 0; + +#if FPSENSOR_WAKEUP_TYPE == FPSENSOR_WAKEUP_SOURCE +#include +#include +struct wakeup_source g_ttw_wl; +#else +#include +struct wake_lock g_ttw_wl; +#endif + +/* -------------------------------------------------------------------- */ +/* fingerprint chip hardware configuration */ +/* -------------------------------------------------------------------- */ +static void fpsensor_gpio_free(fpsensor_data_t *fpsensor) +{ + struct device *dev = &fpsensor->spi->dev; + + if (fpsensor->irq_gpio != 0 ) { + devm_gpio_free(dev, fpsensor->irq_gpio); + fpsensor->irq_gpio = 0; + } + if (fpsensor->reset_gpio != 0) { + devm_gpio_free(dev, fpsensor->reset_gpio); + fpsensor->reset_gpio = 0; + } +#if FPSENSOR_USE_QCOM_POWER_GPIO + if (fpsensor->power_gpio != 0) { + devm_gpio_free(dev, fpsensor->power_gpio); + fpsensor->power_gpio = 0; + } +#endif +} + +#if FPSENSOR_USE_QCOM_POWER_GPIO +static void fpsensor_power_on(fpsensor_data_t *fpsensor) +{ +// struct device *dev = &fpsensor->spi->dev; + int ret = 0; + int value = 0; + if(fpsensor->pinctrl){ + if(fpsensor->vcc_high){ + ret = pinctrl_select_state(fpsensor->pinctrl, fpsensor->vcc_high); + if(ret){ + fpsensor_debug(ERR_LOG, "Failed to pinctl vcc_high . ret = %d\n", ret); + } + } + } + fpsensor_debug(ERR_LOG, "fpsensor power_gpio=%d\n", fpsensor->power_gpio); + if (fpsensor->power_gpio != 0) { + // set power on + ret = gpio_direction_output(fpsensor->power_gpio, 1); + fpsensor_debug(ERR_LOG,"fpsensor power_gpio power on ret :%d\n",ret); + gpio_set_value(fpsensor->power_gpio, 1); + fpsensor_debug(ERR_LOG, "fpsensor fingerprint power on\n"); + value = gpio_get_value(fpsensor->power_gpio); + fpsensor_debug(ERR_LOG,"fpsensor power_gpio power on value :%d\n",value); + } + if (fpsensor->reset_gpio != 0) { + // set reset_gpio high + gpio_direction_output(fpsensor->reset_gpio, 1); + gpio_set_value(fpsensor->reset_gpio, 1); + fpsensor_debug(ERR_LOG, "fpsensor fingerprint reset_gpio high\n"); + } +} +static void fpsensor_power_off(fpsensor_data_t *fpsensor) +{ + int ret = 0; +// struct device *dev = &fpsensor->spi->dev; + if(fpsensor->pinctrl){ + if(fpsensor->vcc_low){ + ret = pinctrl_select_state(fpsensor->pinctrl, fpsensor->vcc_low); + if(ret){ + fpsensor_debug(ERR_LOG, "Failed to pinctl vcc_low . ret = %d\n", ret); + } + } + } + fpsensor_debug(ERR_LOG, "fpsensor power_gpio=%d\n", fpsensor->power_gpio); + if (fpsensor->power_gpio != 0) { + // set power off + gpio_direction_output(fpsensor->power_gpio, 0); + gpio_set_value(fpsensor->power_gpio, 0); + fpsensor_debug(ERR_LOG, "fpsensor fingerprint power off\n"); + } + if (fpsensor->reset_gpio != 0) { + // set reset_gpio low + gpio_direction_output(fpsensor->reset_gpio, 0); + gpio_set_value(fpsensor->reset_gpio, 0); + fpsensor_debug(ERR_LOG, "fpsensor fingerprint reset_gpio low\n"); + } +} +#endif + +static void fpsensor_irq_gpio_cfg(fpsensor_data_t *fpsensor) +{ + int error = 0; + + error = gpio_direction_input(fpsensor->irq_gpio); + if (error) { + fpsensor_debug(ERR_LOG, "setup fpsensor irq gpio for input failed!error[%d]\n", error); + return ; + } + + fpsensor->irq = gpio_to_irq(fpsensor->irq_gpio); + fpsensor_debug(DEBUG_LOG, "fpsensor irq number[%d]\n", fpsensor->irq); + if (fpsensor->irq <= 0) { + fpsensor_debug(ERR_LOG, "fpsensor irq gpio to irq failed!\n"); + return ; + } + + return; +} + +static int fpsensor_request_named_gpio(fpsensor_data_t *fpsensor_dev, const char *label, int *gpio) +{ + struct device *dev = &fpsensor_dev->spi->dev; + struct device_node *np = dev->of_node; + int ret = of_get_named_gpio(np, label, 0); + + if (ret < 0) { + fpsensor_debug(ERR_LOG, "failed to get '%s'\n", label); + return ret; + } + *gpio = ret; + ret = devm_gpio_request(dev, *gpio, label); + if (ret) { + fpsensor_debug(ERR_LOG, "failed to request gpio %d\n", *gpio); + return ret; + } + + fpsensor_debug(ERR_LOG, "%s %d\n", label, *gpio); + return ret; +} + +/* delay us after reset */ +static void fpsensor_hw_reset(int delay) +{ + FUNC_ENTRY(); + gpio_set_value(g_fpsensor->reset_gpio, 1); + + udelay(100); + gpio_set_value(g_fpsensor->reset_gpio, 0); + + udelay(1000); + gpio_set_value(g_fpsensor->reset_gpio, 1); + + if (delay) { + udelay(delay); + } + + FUNC_EXIT(); + return; +} + +#if FPSENSOR_USE_QCOM_POWER_GPIO +static int fpsensor_pinctrl_init(fpsensor_data_t *fpsensor) +{ + int rc = 0; + + fpsensor->pinctrl = devm_pinctrl_get(&fpsensor->spi->dev); + if (IS_ERR_OR_NULL(fpsensor->pinctrl)) { + fpsensor_debug(ERR_LOG, "Failed to get pinctrl handler"); + fpsensor->pinctrl = NULL; + return -EINVAL; + } + + + fpsensor->reset_high = pinctrl_lookup_state(fpsensor->pinctrl, "fps_rst_active"); + if (IS_ERR(fpsensor->reset_high)) + { + rc = PTR_ERR(fpsensor->reset_high); + fpsensor_debug(ERR_LOG," %s : can't find fingerprint pinctrl fps_rst_active \n", __func__); + goto fpsensorfp_pinctrl_fail; + } + fpsensor->reset_low = pinctrl_lookup_state(fpsensor->pinctrl, "fps_rst_suspend"); + if (IS_ERR(fpsensor->reset_low)) + { + rc = PTR_ERR(fpsensor->reset_low); + fpsensor_debug(ERR_LOG," %s : can't find fingerprint pinctrl fps_rst_suspend \n", __func__); + goto fpsensorfp_pinctrl_fail; + } + + fpsensor->vcc_high = pinctrl_lookup_state(fpsensor->pinctrl, "fps_ido1p8_en_active"); + if (IS_ERR(fpsensor->vcc_high)) + { + rc = PTR_ERR(fpsensor->vcc_high); + fpsensor_debug(ERR_LOG," %s : can't find fingerprint pinctrl fps_ido1p8_en_active \n", __func__); + goto fpsensorfp_pinctrl_fail; + } + fpsensor->vcc_low = pinctrl_lookup_state(fpsensor->pinctrl, "fps_ido1p8_en_suspend"); + if (IS_ERR(fpsensor->vcc_low)) + { + rc = PTR_ERR(fpsensor->vcc_low); + fpsensor_debug(ERR_LOG," %s : can't find fingerprint pinctrl fps_ido1p8_en_suspend \n", __func__); + goto fpsensorfp_pinctrl_fail; + } + return rc; + +fpsensorfp_pinctrl_fail: + fpsensor_debug(ERR_LOG, "Failed to pinctl lookup&select . rc = %d\n", rc); + devm_pinctrl_put(fpsensor->pinctrl); + fpsensor->pinctrl = NULL; + return rc; +} +#endif + +static int fpsensor_get_gpio_dts_info(fpsensor_data_t *fpsensor) +{ + int ret = 0; + FUNC_ENTRY(); + // get interrupt gpio resource +#if FPSENSOR_USE_QCOM_POWER_GPIO + + ret = fpsensor_pinctrl_init(fpsensor); + if(ret){ + fpsensor_debug(ERR_LOG, "Failed to pinctl request . ret = %d\n", ret); + } +#endif + ret = fpsensor_request_named_gpio(fpsensor, "fp-gpio-int", &fpsensor->irq_gpio); + if (ret) { + fpsensor_debug(ERR_LOG, "Failed to request irq GPIO. ret = %d\n", ret); + return -1; + } + + // get reest gpio resourece + ret = fpsensor_request_named_gpio(fpsensor, "fp-gpio-reset", &fpsensor->reset_gpio); + if (ret) { + fpsensor_debug(ERR_LOG, "Failed to request reset GPIO. ret = %d\n", ret); + return -1; + } +#if FPSENSOR_USE_QCOM_POWER_GPIO + if(fpsensor->pinctrl){ + if(fpsensor->vcc_high){ + ret = pinctrl_select_state(fpsensor->pinctrl, fpsensor->vcc_high); + if(ret){ + fpsensor_debug(ERR_LOG, "Failed to pinctl vcc_high . ret = %d\n", ret); + } + } + } + // get power gpio resourece + ret = fpsensor_request_named_gpio(fpsensor, "fp-gpio-power", &fpsensor->power_gpio); + if (ret) { + fpsensor_debug(ERR_LOG, "Failed to request power GPIO. ret = %d\n", ret); + return -1; + } + // set power direction output + ret = gpio_direction_output(fpsensor->power_gpio, 1); + fpsensor_debug(ERR_LOG,"fpsensor power_gpio power on dts ret:%d\n",ret); + gpio_set_value(fpsensor->power_gpio, 1); +#endif + // set reset direction output +#if FPSENSOR_USE_QCOM_POWER_GPIO + if(fpsensor->pinctrl){ + if(fpsensor->reset_high){ + ret = pinctrl_select_state(fpsensor->pinctrl, fpsensor->reset_high); + if(ret){ + fpsensor_debug(ERR_LOG, "Failed to pinctl reset_high . ret = %d\n", ret); + } + } + } + gpio_direction_output(fpsensor->reset_gpio, 1); + gpio_set_value(g_fpsensor->reset_gpio, 1); + mdelay(4); + //fpsensor_hw_reset(1250); +#else + gpio_direction_output(fpsensor->reset_gpio, 1); + fpsensor_hw_reset(1250); +#endif + return ret; +} + +static void setRcvIRQ(int val) +{ + fpsensor_data_t *fpsensor_dev = g_fpsensor; + fpsensor_dev->RcvIRQ = val; +} + +static void fpsensor_enable_irq(fpsensor_data_t *fpsensor_dev) +{ + FUNC_ENTRY(); + setRcvIRQ(0); + /* Request that the interrupt should be wakeable */ + if (fpsensor_dev->irq_enabled == 0) { + enable_irq(fpsensor_dev->irq); + fpsensor_dev->irq_enabled = 1; + } + FUNC_EXIT(); + return; +} + +static void fpsensor_disable_irq(fpsensor_data_t *fpsensor_dev) +{ + FUNC_ENTRY(); + + if (0 == fpsensor_dev->device_available) { + fpsensor_debug(ERR_LOG, "%s, devices not available\n", __func__); + goto out; + } + + if (0 == fpsensor_dev->irq_enabled) { + fpsensor_debug(ERR_LOG, "%s, irq already disabled\n", __func__); + goto out; + } + + if (fpsensor_dev->irq) { + disable_irq_nosync(fpsensor_dev->irq); + fpsensor_debug(DEBUG_LOG, "%s disable interrupt!\n", __func__); + } + fpsensor_dev->irq_enabled = 0; + +out: + setRcvIRQ(0); + FUNC_EXIT(); + return; +} + +static irqreturn_t fpsensor_irq(int irq, void *handle) +{ + fpsensor_data_t *fpsensor_dev = (fpsensor_data_t *)handle; + + /* Make sure 'wakeup_enabled' is updated before using it + ** since this is interrupt context (other thread...) */ + smp_rmb(); +#if FPSENSOR_WAKEUP_TYPE == FPSENSOR_WAKEUP_SOURCE + __pm_wakeup_event(&g_ttw_wl, 1000); +#else + wake_lock_timeout(&g_ttw_wl, msecs_to_jiffies(1000)); +#endif + setRcvIRQ(1); + wake_up_interruptible(&fpsensor_dev->wq_irq_return); + + return IRQ_HANDLED; +} + +// release and cleanup fpsensor char device +static void fpsensor_dev_cleanup(fpsensor_data_t *fpsensor) +{ + FUNC_ENTRY(); + + if (fpsensor->class == NULL) { + fpsensor_debug(INFO_LOG, "Cleanup with fpsensor = NULL\n"); + goto out; + } + cdev_del(&fpsensor->cdev); + unregister_chrdev_region(fpsensor->devno, FPSENSOR_NR_DEVS); + device_destroy(fpsensor->class, fpsensor->devno); + class_destroy(fpsensor->class); + fpsensor->class = NULL; + +out: + FUNC_EXIT(); +} + +static long fpsensor_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + fpsensor_data_t *fpsensor_dev = NULL; + int retval = 0; + uint32_t val = 0; + int irqf; + + fpsensor_debug(INFO_LOG, "[rickon]: fpsensor ioctl cmd : 0x%x \n", cmd ); + fpsensor_dev = (fpsensor_data_t *)filp->private_data; + fpsensor_dev->cancel = 0 ; + switch (cmd) { + case FPSENSOR_IOC_INIT: + fpsensor_debug(INFO_LOG, "%s: fpsensor init started======\n", __func__); + +#ifndef SUPPORT_POWER_GPIO + retval = fpsensor_get_gpio_dts_info(fpsensor_dev); + if (retval) { + break; + } + fpsensor_irq_gpio_cfg(fpsensor_dev); +#endif + //regist irq + irqf = IRQF_TRIGGER_RISING | IRQF_ONESHOT; + retval = devm_request_threaded_irq(&g_fpsensor->spi->dev, g_fpsensor->irq, fpsensor_irq, + NULL, irqf, dev_name(&g_fpsensor->spi->dev), g_fpsensor); + if (retval == 0) { + fpsensor_debug(INFO_LOG, " irq thread reqquest success!\n"); + } else { + fpsensor_debug(INFO_LOG, " irq thread request failed , retval =%d \n", retval); + break; + } + enable_irq_wake(g_fpsensor->irq); + fpsensor_dev->device_available = 1; + // fix Unbalanced enable for IRQ, disable irq at first + fpsensor_dev->irq_enabled = 1; + fpsensor_disable_irq(fpsensor_dev); + fpsensor_debug(INFO_LOG, "%s: fpsensor init finished======\n", __func__); + break; + + case FPSENSOR_IOC_EXIT: + if(fpsensor_dev->device_available) { + fpsensor_disable_irq(fpsensor_dev); + if (fpsensor_dev->irq) { + devm_free_irq(&g_fpsensor->spi->dev, fpsensor_dev->irq, fpsensor_dev); + fpsensor_dev->irq_enabled = 0; + } + fpsensor_dev->device_available = 0; + //fpsensor_gpio_free(fpsensor_dev); + fpsensor_debug(INFO_LOG, "%s: fpsensor exit finished======\n", __func__); + } else { + fpsensor_debug(INFO_LOG, "%s: IOC_EXIT with device_available = 0 ======\n", __func__); + } + break; + + case FPSENSOR_IOC_RESET: + fpsensor_debug(INFO_LOG, "%s: chip reset command\n", __func__); +#if FPSENSOR_USE_QCOM_POWER_GPIO + fpsensor_hw_reset(4000); +#else + fpsensor_hw_reset(1250); +#endif + break; + + case FPSENSOR_IOC_ENABLE_IRQ: + fpsensor_debug(INFO_LOG, "%s: chip ENable IRQ command\n", __func__); + fpsensor_enable_irq(fpsensor_dev); + break; + + case FPSENSOR_IOC_DISABLE_IRQ: + fpsensor_debug(INFO_LOG, "%s: chip disable IRQ command\n", __func__); + fpsensor_disable_irq(fpsensor_dev); + break; + case FPSENSOR_IOC_GET_INT_VAL: + val = gpio_get_value(fpsensor_dev->irq_gpio); + if (copy_to_user((void __user *)arg, (void *)&val, sizeof(uint32_t))) { + fpsensor_debug(ERR_LOG, "Failed to copy data to user\n"); + retval = -EFAULT; + break; + } + retval = 0; + break; + case FPSENSOR_IOC_ENABLE_SPI_CLK: + fpsensor_debug(INFO_LOG, "%s: ENABLE_SPI_CLK ======\n", __func__); + break; + case FPSENSOR_IOC_DISABLE_SPI_CLK: + fpsensor_debug(INFO_LOG, "%s: DISABLE_SPI_CLK ======\n", __func__); + break; + case FPSENSOR_IOC_ENABLE_POWER: + fpsensor_debug(INFO_LOG, "%s: FPSENSOR_IOC_ENABLE_POWER ======\n", __func__); +#if FPSENSOR_USE_QCOM_POWER_GPIO + fpsensor_power_on(fpsensor_dev); +#endif + break; + case FPSENSOR_IOC_DISABLE_POWER: + fpsensor_debug(INFO_LOG, "%s: FPSENSOR_IOC_DISABLE_POWER ======\n", __func__); +#if FPSENSOR_USE_QCOM_POWER_GPIO + fpsensor_power_off(fpsensor_dev); +#endif + break; + case FPSENSOR_IOC_REMOVE: + if(fpsensor_dev->device_available) { + fpsensor_disable_irq(fpsensor_dev); + if (fpsensor_dev->irq) { + devm_free_irq(&g_fpsensor->spi->dev, fpsensor_dev->irq, fpsensor_dev); + fpsensor_dev->irq_enabled = 0; + } + fpsensor_gpio_free(fpsensor_dev); +#if FPSENSOR_USE_QCOM_POWER_GPIO + if(fpsensor_dev->pinctrl){ + devm_pinctrl_put(fpsensor_dev->pinctrl); + fpsensor_dev->pinctrl = NULL; + } +#endif + fpsensor_dev->device_available = 0; + } else { + fpsensor_debug(INFO_LOG, "%s: IOC_REMOVE with device_available = 0 ======\n", __func__); + } + fpsensor_dev_cleanup(fpsensor_dev); +#if FP_NOTIFY + fpsensor_fb_unregister_client(&fpsensor_dev->notifier); +#endif + fpsensor_dev->free_flag = 1; + fpsensor_debug(INFO_LOG, "%s remove finished\n", __func__); + break; + case FPSENSOR_IOC_CANCEL_WAIT: + fpsensor_debug(INFO_LOG, "%s: FPSENSOR CANCEL WAIT\n", __func__); + wake_up_interruptible(&fpsensor_dev->wq_irq_return); + fpsensor_dev->cancel = 1; + break; +#if FP_NOTIFY + case FPSENSOR_IOC_GET_FP_STATUS : + val = fpsensor_dev->fb_status; + fpsensor_debug(INFO_LOG, "%s: FPSENSOR_IOC_GET_FP_STATUS %d \n",__func__, fpsensor_dev->fb_status); + if (copy_to_user((void __user *)arg, (void *)&val, sizeof(uint32_t))) { + fpsensor_debug(ERR_LOG, "Failed to copy data to user\n"); + retval = -EFAULT; + break; + } + retval = 0; + break; +#endif + case FPSENSOR_IOC_ENABLE_REPORT_BLANKON: + if (copy_from_user(&val, (void __user *)arg, sizeof(uint32_t))) { + retval = -EFAULT; + break; + } + fpsensor_dev->enable_report_blankon = val; + fpsensor_debug(INFO_LOG, "%s: FPSENSOR_IOC_ENABLE_REPORT_BLANKON: %d\n", __func__, val); + break; + case FPSENSOR_IOC_UPDATE_DRIVER_SN: + if (copy_from_user(&g_cmd_sn, (void __user *)arg, sizeof(uint32_t))) { + fpsensor_debug(ERR_LOG, "Failed to copy g_cmd_sn from user to kernel\n"); + retval = -EFAULT; + break; + } + //fpsensor_debug(INFO_LOG, "%s: FPSENSOR_IOC_UPDATE_DRIVER_SN: %d\n", __func__, g_cmd_sn); + break; + default: + fpsensor_debug(ERR_LOG, "fpsensor doesn't support this command(0x%x)\n", cmd); + break; + } + + //FUNC_EXIT(); + return retval; +} + +static long fpsensor_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + return fpsensor_ioctl(filp, cmd, (unsigned long)(arg)); +} + +static unsigned int fpsensor_poll(struct file *filp, struct poll_table_struct *wait) +{ + unsigned int ret = 0; + + ret |= POLLIN; + poll_wait(filp, &g_fpsensor->wq_irq_return, wait); + if (g_fpsensor->cancel == 1) { + fpsensor_debug(ERR_LOG, " cancle\n"); + ret = POLLERR; + g_fpsensor->cancel = 0; + return ret; + } + + if ( g_fpsensor->RcvIRQ) { + if (g_fpsensor->RcvIRQ == 2) { + fpsensor_debug(ERR_LOG, " get fp on notify\n"); + ret |= POLLHUP; + } else { + fpsensor_debug(ERR_LOG, " get irq\n"); + ret |= POLLRDNORM; + } + } else { + ret = 0; + } + return ret; +} + +static int fpsensor_open(struct inode *inode, struct file *filp) +{ + fpsensor_data_t *fpsensor_dev; + + FUNC_ENTRY(); + fpsensor_dev = container_of(inode->i_cdev, fpsensor_data_t, cdev); + fpsensor_dev->users++; + fpsensor_dev->device_available = 1; + filp->private_data = fpsensor_dev; + FUNC_EXIT(); + return 0; +} + +static int fpsensor_release(struct inode *inode, struct file *filp) +{ + fpsensor_data_t *fpsensor_dev; + int status = 0; + + FUNC_ENTRY(); + fpsensor_dev = filp->private_data; + filp->private_data = NULL; + + /*last close??*/ + fpsensor_dev->users--; + if (fpsensor_dev->users <= 0) { + fpsensor_debug(INFO_LOG, "%s, disble_irq. irq = %d\n", __func__, fpsensor_dev->irq); + fpsensor_disable_irq(fpsensor_dev); + } + fpsensor_dev->device_available = 0; + FUNC_EXIT(); + return status; +} + +static ssize_t fpsensor_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + fpsensor_debug(ERR_LOG, "Not support read opertion in TEE version\n"); + return -EFAULT; +} + +static ssize_t fpsensor_write(struct file *filp, const char __user *buf, size_t count, + loff_t *f_pos) +{ + fpsensor_debug(ERR_LOG, "Not support write opertion in TEE version\n"); + return -EFAULT; +} + +static const struct file_operations fpsensor_fops = { + .owner = THIS_MODULE, + .write = fpsensor_write, + .read = fpsensor_read, + .unlocked_ioctl = fpsensor_ioctl, + .compat_ioctl = fpsensor_compat_ioctl, + .open = fpsensor_open, + .release = fpsensor_release, + .poll = fpsensor_poll, + +}; + +// create and register a char device for fpsensor +static int fpsensor_dev_setup(fpsensor_data_t *fpsensor) +{ + int ret = 0; + dev_t dev_no = 0; + struct device *dev = NULL; + int fpsensor_dev_major = FPSENSOR_DEV_MAJOR; + int fpsensor_dev_minor = 0; + + FUNC_ENTRY(); + + if (fpsensor_dev_major) { + dev_no = MKDEV(fpsensor_dev_major, fpsensor_dev_minor); + ret = register_chrdev_region(dev_no, FPSENSOR_NR_DEVS, FPSENSOR_DEV_NAME); + } else { + ret = alloc_chrdev_region(&dev_no, fpsensor_dev_minor, FPSENSOR_NR_DEVS, FPSENSOR_DEV_NAME); + fpsensor_dev_major = MAJOR(dev_no); + fpsensor_dev_minor = MINOR(dev_no); + fpsensor_debug(INFO_LOG, "fpsensor device major is %d, minor is %d\n", + fpsensor_dev_major, fpsensor_dev_minor); + } + + if (ret < 0) { + fpsensor_debug(ERR_LOG, "can not get device major number %d\n", fpsensor_dev_major); + goto out; + } + + cdev_init(&fpsensor->cdev, &fpsensor_fops); + fpsensor->cdev.owner = THIS_MODULE; + fpsensor->cdev.ops = &fpsensor_fops; + fpsensor->devno = dev_no; + ret = cdev_add(&fpsensor->cdev, dev_no, FPSENSOR_NR_DEVS); + if (ret) { + fpsensor_debug(ERR_LOG, "add char dev for fpsensor failed\n"); + goto release_region; + } + + fpsensor->class = class_create(THIS_MODULE, FPSENSOR_CLASS_NAME); + if (IS_ERR(fpsensor->class)) { + fpsensor_debug(ERR_LOG, "create fpsensor class failed\n"); + ret = PTR_ERR(fpsensor->class); + goto release_cdev; + } + + dev = device_create(fpsensor->class, &fpsensor->spi->dev, dev_no, fpsensor, FPSENSOR_DEV_NAME); + if (IS_ERR(dev)) { + fpsensor_debug(ERR_LOG, "create device for fpsensor failed\n"); + ret = PTR_ERR(dev); + goto release_class; + } + FUNC_EXIT(); + return ret; + +release_class: + class_destroy(fpsensor->class); + fpsensor->class = NULL; +release_cdev: + cdev_del(&fpsensor->cdev); +release_region: + unregister_chrdev_region(dev_no, FPSENSOR_NR_DEVS); +out: + FUNC_EXIT(); + return ret; +} + +#if FP_NOTIFY +static int fpsensor_fb_notifier_callback(struct notifier_block* self, unsigned long event, void* data) +{ + int retval = 0; + static char screen_status[64] = { '\0' }; + struct fb_event* evdata = data; + unsigned int blank; + fpsensor_data_t *fpsensor_dev = g_fpsensor; + + fpsensor_debug(INFO_LOG,"%s enter, event: 0x%x\n", __func__, (unsigned)event); + if (event != FP_NOTIFY_EVENT_BLANK) { + return 0; + } + + blank = *(int*)evdata->data; + fpsensor_debug(INFO_LOG,"%s enter, blank=0x%x\n", __func__, blank); + + switch (blank) { + case FP_NOTIFY_ON: + fpsensor_debug(INFO_LOG,"%s: lcd on notify\n", __func__); + sprintf(screen_status, "SCREEN_STATUS=%s", "ON"); + fpsensor_dev->fb_status = 1; + if( fpsensor_dev->enable_report_blankon) { + fpsensor_dev->RcvIRQ = 2; + wake_up_interruptible(&fpsensor_dev->wq_irq_return); + } + break; + + case FP_NOTIFY_OFF: + fpsensor_debug(INFO_LOG,"%s: lcd off notify\n", __func__); + sprintf(screen_status, "SCREEN_STATUS=%s", "OFF"); + fpsensor_dev->fb_status = 0; + break; + + default: + fpsensor_debug(INFO_LOG,"%s: other notifier, ignore\n", __func__); + break; + } + + fpsensor_debug(INFO_LOG,"%s %s leave.\n", screen_status, __func__); + return retval; +} +#endif + +static int fpsensor_probe(struct platform_device *pdev) +{ + int status = 0; + fpsensor_data_t *fpsensor_dev = NULL; + + FUNC_ENTRY(); + + /* Allocate driver data */ + fpsensor_dev = kzalloc(sizeof(*fpsensor_dev), GFP_KERNEL); + if (!fpsensor_dev) { + status = -ENOMEM; + fpsensor_debug(ERR_LOG, "%s, Failed to alloc memory for fpsensor device.\n", __func__); + goto out; + } + + /* Initialize the driver data */ + g_fpsensor = fpsensor_dev; + fpsensor_dev->spi = pdev ; + fpsensor_dev->device_available = 0; + fpsensor_dev->users = 0; + fpsensor_dev->irq = 0; + fpsensor_dev->power_gpio = 0; + fpsensor_dev->reset_gpio = 0; + fpsensor_dev->irq_gpio = 0; + fpsensor_dev->irq_enabled = 0; + fpsensor_dev->free_flag = 0; + fpsensor_dev->fb_status = 1; + /* setup a char device for fpsensor */ + status = fpsensor_dev_setup(fpsensor_dev); + if (status) { + fpsensor_debug(ERR_LOG, "fpsensor setup char device failed, %d", status); + goto release_drv_data; + } +#if FPSENSOR_USE_QCOM_POWER_GPIO + status = fpsensor_get_gpio_dts_info(fpsensor_dev); + if (status) { + fpsensor_debug(ERR_LOG, "fpsensor setup char device failed, %d", status); + goto release_drv_data; + } + fpsensor_irq_gpio_cfg(fpsensor_dev); +#endif + init_waitqueue_head(&fpsensor_dev->wq_irq_return); +#if FPSENSOR_WAKEUP_TYPE == FPSENSOR_WAKEUP_SOURCE + wakeup_source_init_internal(&g_ttw_wl, "fpsensor_ttw_wl"); +#else + wake_lock_init(&g_ttw_wl, WAKE_LOCK_SUSPEND, "fpsensor_ttw_wl"); +#endif + fpsensor_dev->device_available = 1; +#if FP_NOTIFY + fpsensor_dev->notifier.notifier_call = fpsensor_fb_notifier_callback; + fpsensor_fb_register_client(&fpsensor_dev->notifier); +#endif + + fpsensor_debug(INFO_LOG, "%s finished, driver version: %s\n", __func__, FPSENSOR_SPI_VERSION); + goto out; + +release_drv_data: + kfree(fpsensor_dev); + fpsensor_dev = NULL; +out: + FUNC_EXIT(); + return status; +} + +static int fpsensor_remove(struct platform_device *pdev) +{ + fpsensor_data_t *fpsensor_dev = g_fpsensor; + int ret = 0; + + FUNC_ENTRY(); + + if (g_fpsensor == NULL) { + fpsensor_debug(ERR_LOG, "%s with g_fpsensor = NULL\n", __func__); + ret = -EINVAL; + goto out; + } + + if(fpsensor_dev->device_available) { + fpsensor_disable_irq(fpsensor_dev); + if (fpsensor_dev->irq) + devm_free_irq(&g_fpsensor->spi->dev, fpsensor_dev->irq, fpsensor_dev); + + fpsensor_gpio_free(fpsensor_dev); +#if FPSENSOR_USE_QCOM_POWER_GPIO + if(fpsensor_dev->pinctrl){ + devm_pinctrl_put(fpsensor_dev->pinctrl); + fpsensor_dev->pinctrl = NULL; + } +#endif + fpsensor_dev->device_available = 0; + } + +#if FP_NOTIFY + fpsensor_fb_unregister_client(&fpsensor_dev->notifier); +#endif + + fpsensor_dev_cleanup(fpsensor_dev); + +#if FPSENSOR_WAKEUP_TYPE == FPSENSOR_WAKEUP_SOURCE + wakeup_source_trash_internal(&g_ttw_wl); +#else + wake_lock_destroy(&g_ttw_wl); +#endif + + kfree(fpsensor_dev); + g_fpsensor = NULL; + +out: + FUNC_EXIT(); + return ret; +} + +static int fpsensor_suspend(struct platform_device *pdev, pm_message_t state) +{ + return 0; +} + +static int fpsensor_resume( struct platform_device *pdev) +{ + return 0; +} + +#ifdef CONFIG_OF +static struct of_device_id fpsensor_of_match[] = { + { .compatible = "qcom,fingerprint-gpio" }, + {} +}; +//MODULE_DEVICE_TABLE(of, fpsensor_of_match); +#endif + +static struct platform_driver fpsensor_driver = { + .driver = { + .name = FPSENSOR_DEV_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = fpsensor_of_match, +#endif + }, + .probe = fpsensor_probe, + .remove = fpsensor_remove, + .suspend = fpsensor_suspend, + .resume = fpsensor_resume, +}; + +static int __init fpsensor_init(void) +{ + int status; + + status = platform_driver_register(&fpsensor_driver); + if (status < 0) { + fpsensor_debug(ERR_LOG, "%s, Failed to register SPI driver.\n", __func__); + } + + return status; +} +module_init(fpsensor_init); + +static void __exit fpsensor_exit(void) +{ + platform_driver_unregister(&fpsensor_driver); +} +module_exit(fpsensor_exit); + +MODULE_AUTHOR("xhli"); +MODULE_DESCRIPTION(" Fingerprint chip TEE driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:fpsensor-drivers"); diff --git a/drivers/input/misc/chipone_fps_mmi_v1/fpsensor_spi_tee.h b/drivers/input/misc/chipone_fps_mmi_v1/fpsensor_spi_tee.h new file mode 100644 index 000000000000..9f98eae221cf --- /dev/null +++ b/drivers/input/misc/chipone_fps_mmi_v1/fpsensor_spi_tee.h @@ -0,0 +1,74 @@ +#ifndef __FPSENSOR_SPI_TEE_H +#define __FPSENSOR_SPI_TEE_H + +#include +#include +#include +#include +#include + +#define FPSENSOR_DEV_NAME "fpsensor" +#define FPSENSOR_CLASS_NAME "fpsensor" +#define FPSENSOR_DEV_MAJOR 0 +#define N_SPI_MINORS 32 /* ... up to 256 */ +#define FPSENSOR_NR_DEVS 1 + +#define ERR_LOG (0) +#define INFO_LOG (1) +#define DEBUG_LOG (2) +#define fpsensor_debug(level, fmt, args...) do { \ + if (fpsensor_debug_level >= level) {\ + printk("[fpCoreDriver][SN=%d] " fmt, g_cmd_sn, ##args); \ + } \ + } while (0) +#define FUNC_ENTRY() fpsensor_debug(DEBUG_LOG, "%s, %d, entry\n", __func__, __LINE__) +#define FUNC_EXIT() fpsensor_debug(DEBUG_LOG, "%s, %d, exit\n", __func__, __LINE__) + +/**********************IO Magic**********************/ +#define FPSENSOR_IOC_MAGIC 0xf0 //CHIP + +/* define commands */ +#define FPSENSOR_IOC_INIT _IOWR(FPSENSOR_IOC_MAGIC,0,uint32_t) +#define FPSENSOR_IOC_EXIT _IOWR(FPSENSOR_IOC_MAGIC,1,uint32_t) +#define FPSENSOR_IOC_RESET _IOWR(FPSENSOR_IOC_MAGIC,2,uint32_t) +#define FPSENSOR_IOC_ENABLE_IRQ _IOWR(FPSENSOR_IOC_MAGIC,3,uint32_t) +#define FPSENSOR_IOC_DISABLE_IRQ _IOWR(FPSENSOR_IOC_MAGIC,4,uint32_t) +#define FPSENSOR_IOC_GET_INT_VAL _IOWR(FPSENSOR_IOC_MAGIC,5,uint32_t) +#define FPSENSOR_IOC_DISABLE_SPI_CLK _IOWR(FPSENSOR_IOC_MAGIC,6,uint32_t) +#define FPSENSOR_IOC_ENABLE_SPI_CLK _IOWR(FPSENSOR_IOC_MAGIC,7,uint32_t) +#define FPSENSOR_IOC_ENABLE_POWER _IOWR(FPSENSOR_IOC_MAGIC,8,uint32_t) +#define FPSENSOR_IOC_DISABLE_POWER _IOWR(FPSENSOR_IOC_MAGIC,9,uint32_t) +/* fp sensor has change to sleep mode while screen off */ +#define FPSENSOR_IOC_ENTER_SLEEP_MODE _IOWR(FPSENSOR_IOC_MAGIC,11,uint32_t) +#define FPSENSOR_IOC_REMOVE _IOWR(FPSENSOR_IOC_MAGIC,12,uint32_t) +#define FPSENSOR_IOC_CANCEL_WAIT _IOWR(FPSENSOR_IOC_MAGIC,13,uint32_t) +#define FPSENSOR_IOC_GET_FP_STATUS _IOWR(FPSENSOR_IOC_MAGIC,19,uint32_t) +#define FPSENSOR_IOC_ENABLE_REPORT_BLANKON _IOWR(FPSENSOR_IOC_MAGIC,21,uint32_t) +#define FPSENSOR_IOC_UPDATE_DRIVER_SN _IOWR(FPSENSOR_IOC_MAGIC,22,uint32_t) +typedef struct { + dev_t devno; + struct class *class; + struct cdev cdev; + struct platform_device *spi; + unsigned int users; + u8 device_available; /* changed during fingerprint chip sleep and wakeup phase */ + u8 irq_enabled; + volatile unsigned int RcvIRQ; + int irq; + int irq_gpio; + int reset_gpio; + int power_gpio; + wait_queue_head_t wq_irq_return; + int cancel; + struct notifier_block notifier; + u8 fb_status; + int enable_report_blankon; + int free_flag; + struct pinctrl *pinctrl; + struct pinctrl_state *reset_high; + struct pinctrl_state *reset_low; + struct pinctrl_state *vcc_high; + struct pinctrl_state *vcc_low; +} fpsensor_data_t; + +#endif /* __FPSENSOR_SPI_TEE_H */ diff --git a/drivers/input/misc/chipone_fps_mmi_v1/fpsensor_wakelock.h b/drivers/input/misc/chipone_fps_mmi_v1/fpsensor_wakelock.h new file mode 100644 index 000000000000..f588b585e9f6 --- /dev/null +++ b/drivers/input/misc/chipone_fps_mmi_v1/fpsensor_wakelock.h @@ -0,0 +1,87 @@ +/* include/linux/wakelock.h + * + * Copyright (C) 2007-2012 Google, Inc. + * + * 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. + * + */ + +#ifndef _BF_FP_WAKELOCK_H +#define _BF_FP_WAKELOCK_H + +#include +#include +#include + +/* A wake_lock prevents the system from entering suspend or other low power + * states when active. If the type is set to WAKE_LOCK_SUSPEND, the wake_lock + * prevents a full system suspend. + */ + +enum { + WAKE_LOCK_SUSPEND, /* Prevent suspend */ + WAKE_LOCK_TYPE_COUNT +}; + +struct wake_lock { + struct wakeup_source ws; +}; + +static inline void wakeup_source_init_internal(struct wakeup_source *ws, + const char *name) +{ + if(ws){ + memset(ws, 0, sizeof(*ws)); + ws->name = name; + } + wakeup_source_add(ws); +} + +static inline void wake_lock_init(struct wake_lock *lock, int type, + const char *name) +{ + wakeup_source_init_internal(&lock->ws, name); +} + +static inline void wakeup_source_trash_internal(struct wakeup_source *ws) +{ + wakeup_source_remove(ws); + if(!ws) + return; + __pm_relax(ws); +} + +static inline void wake_lock_destroy(struct wake_lock *lock) +{ + wakeup_source_trash_internal(&lock->ws); +} + +static inline void wake_lock(struct wake_lock *lock) +{ + __pm_stay_awake(&lock->ws); +} + +static inline void wake_lock_timeout(struct wake_lock *lock, long timeout) +{ + __pm_wakeup_event(&lock->ws, jiffies_to_msecs(timeout)); +} + +static inline void wake_unlock(struct wake_lock *lock) +{ + __pm_relax(&lock->ws); +} + +static inline int wake_lock_active(struct wake_lock *lock) +{ + return lock->ws.active; +} + +#endif + diff --git a/drivers/input/misc/cs_press_mmi/Android.mk b/drivers/input/misc/cs_press_mmi/Android.mk new file mode 100644 index 000000000000..50f1b35e1fd5 --- /dev/null +++ b/drivers/input/misc/cs_press_mmi/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := cs_press_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/misc/cs_press_mmi/Kbuild b/drivers/input/misc/cs_press_mmi/Kbuild new file mode 100644 index 000000000000..03a329957ed4 --- /dev/null +++ b/drivers/input/misc/cs_press_mmi/Kbuild @@ -0,0 +1,8 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m := cs_press_mmi.o + +cs_press_mmi-objs := cs_press.o + diff --git a/drivers/input/misc/cs_press_mmi/Makefile b/drivers/input/misc/cs_press_mmi/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/input/misc/cs_press_mmi/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/input/misc/cs_press_mmi/cs_press.c b/drivers/input/misc/cs_press_mmi/cs_press.c new file mode 100644 index 000000000000..efd7c0efee4e --- /dev/null +++ b/drivers/input/misc/cs_press_mmi/cs_press.c @@ -0,0 +1,3486 @@ +/* + * Driver for CHIPSEA forcetouch sensor + * Copyright (C) 2023 CHIPSEA + * + * 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. + * + * Release Information : CSA37F71 chip forcetouch fw driver head file + * version : v0.2 + */ + +#include "cs_press.h" + +//#define KERNEL_4_1_5 + +const char *cs_driver_ver = "1.1"; + +#define PROC_FOPS_NUM 9 +#define PROC_NAME_LEN 50 + +#ifndef KERNEL_4_1_5 +#define FOPS_ARRAY(_open, _write) \ +{\ + .proc_open = _open,\ + .proc_read = seq_read,\ + .proc_release = single_release,\ + .proc_write = _write,\ +} +#else +#define FOPS_ARRAY(_open, _write) \ +{\ + .owner = THIS_MODULE,\ + .open = _open,\ + .read = seq_read,\ + .llseek = seq_lseek,\ + .release = single_release,\ + .write = _write,\ +} +#endif + +static struct mutex i2c_rw_lock; +static DEFINE_MUTEX(i2c_rw_lock); + +static struct cs_press_t g_cs_press; +static struct cs_vtp_scroll vtp_scroll; +static struct cs_vtp_zoom vtp_zoom; +struct class *cs_press_class; + + +#ifdef INT_SET_EN +static DECLARE_WAIT_QUEUE_HEAD(cs_press_waiter); +static int cs_press_int_flag; +static int cs_press_stop_read; +int cs_press_irq_gpio; +int cs_press_irq_num; + +/*1 enable,0 disable, need to confirm after register eint*/ +static int cs_irq_flag = 1; +struct input_dev *cs_input_dev; +#endif +/******* fuction definition start **********/ +#ifdef INT_SET_EN +static void cs_irq_enable(void); +static void cs_irq_disable(void); +#endif + + +void cs_press_struct_init(void) +{ + g_cs_press.update_type = HIGH_VER_FILE_UPDATE;/*1:force update, 0:to higher ver update*/ + g_cs_press.updating_flag = 0; +} + +/**@brief read n reg datas from reg_addr. + * @param[in] dev: cs_press device struct. + * @param[in] reg_addr: register addr + * @param[in] len: read data lenth + * @param[out] buf: read data buffer addr. + * @return i2c_transfer status + * OK: return num of send data + * err: return err code + */ +static int cs_i2c_read_bytes(struct i2c_client *client, unsigned char reg_addr, void *buf, unsigned int len) +{ + int ret = 0; + struct i2c_msg msg[2]; + struct i2c_adapter *adapter = client->adapter; + int i = 5; + + if(buf == NULL){ + LOG_ERR("input buf is NULL\n"); + return -1; + } + if(len == 0){ + LOG_ERR("input len is invalid,len = 0\n"); + return -1; + } + /* msg[0] send first addr for read */ + msg[0].addr = client->addr; /* cs_press addr */ + msg[0].flags = 0; /* send flag */ + msg[0].buf = ®_addr; /* reg addr */ + msg[0].len = 1; /* reg lenth */ + + /* msg[1]read data */ + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; /* read flag */ + msg[1].buf = buf; + msg[1].len = len; + mutex_lock(&i2c_rw_lock); + do{ + ret = i2c_transfer(adapter, msg, sizeof(msg) / sizeof(struct i2c_msg)); + if(ret <= 0){ + LOG_ERR("i2c read failed! err_code:%d\n",ret); + }else{ + break; + } + i--; + }while(i > 0); + mutex_unlock(&i2c_rw_lock); + return ret; +} + + /**@brief send multi datas to reg_addr. + * @param[in] dev: cs_press device struct. + * @param[in] reg_addr: register addr + * @param[in] len: send data lenth + * @param[out] buf: send data buffer addr. + * @return i2c_transfer status + * OK: return num of send data + * err: return err code + */ +static int cs_i2c_write_bytes(struct i2c_client *client, unsigned char reg_addr, unsigned char *buf, unsigned char len) +{ + int ret = 0; + unsigned char *t_buf = NULL; + struct i2c_msg msg; + struct i2c_adapter *adapter = client->adapter; + int i= 5; + + if(buf == NULL){ + LOG_ERR("input buf is NULL\n"); + return -1; + } + if(len == 0){ + LOG_ERR("input len is invalid,len = 0\n"); + return -1; + } + t_buf = (unsigned char *)kmalloc(len + sizeof(reg_addr), GFP_KERNEL ); + if (!t_buf){ + LOG_ERR("kmalloc failed\n"); + return -1; + } + t_buf[0] = reg_addr; /* register first addr */ + memcpy(&t_buf[1],buf,len); /* copy send data to b[256]*/ + + msg.addr = client->addr; + msg.flags = 0; /* write flag*/ + msg.buf = t_buf; + msg.len = len + 1; + mutex_lock(&i2c_rw_lock); + do{ + ret = i2c_transfer(adapter, &msg, 1); + if(ret <= 0){ + LOG_ERR("i2c write failed! err_code:%d\n", ret); + }else if(ret > 0){ + break; + } + i--; + }while(i > 0); + mutex_unlock(&i2c_rw_lock); + kfree(t_buf); + return ret; +} + +/**@brief read n reg datas from reg_addr. + * @param[in] dev: cs_press device struct. + * @param[in] reg_addr: register addr,addr type is word type + * @param[in] len: read data lenth + * @param[out] buf: read data buffer addr. + * @return i2c_transfer status + * OK: return num of send data + * err: return err code + */ +static int cs_i2c_read_bytes_by_u16_addr(struct i2c_client *client, unsigned short reg_addr, void *buf, int len) +{ + int ret = 0; + unsigned char reg16[2]; + struct i2c_msg msg[2]; + struct i2c_adapter *adapter = client->adapter; + + if(buf == NULL){ + LOG_ERR("input buf is NULL\n"); + return -1; + } + if(len == 0){ + LOG_ERR("input len is invalid,len = 0\n"); + return -1; + } + + reg16[0] = (reg_addr >> 8)&0xff; + reg16[1] = reg_addr & 0xff; + /* msg[0] send first addr for read */ + msg[0].addr = client->addr; /* cs_press addr */ + msg[0].flags = 0; /* send flag */ + msg[0].buf = reg16; /* reg addr */ + msg[0].len = sizeof(reg16); /* reg lenth:2*byte */ + + /* msg[1]read data */ + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; /* read flag*/ + msg[1].buf = buf; + msg[1].len = len; + + ret = i2c_transfer(adapter, msg, 2); + if(ret == 2) { + ret = 0; + } else { + LOG_ERR("i2c read failed! err_code:%d\n",ret); + } + return ret; +} + + /**@brief send multi datas to reg_addr. + * @param[in] dev: cs_press device struct. + * @param[in] reg_addr: register addr + * @param[in] len: send data lenth + * @param[out] buf: send data buffer addr. + * @return i2c_transfer status + * OK: return num of send data + * err: return err code + */ +static int cs_i2c_write_bytes_by_u16_addr(struct i2c_client *client, unsigned short reg_addr, unsigned char *buf, unsigned char len) +{ + int ret = 0; + unsigned char *t_buf = NULL; + struct i2c_msg msg; + struct i2c_adapter *adapter = client->adapter; + + if(buf == NULL){ + LOG_ERR("input buf is NULL\n"); + return -1; + } + if(len == 0){ + LOG_ERR("input len is invalid,len = 0\n"); + return -1; + } + t_buf = (unsigned char *)kmalloc(len + sizeof(reg_addr), GFP_KERNEL ); + if (!t_buf){ + LOG_ERR("kmalloc failed\n"); + return -1; + } + + t_buf[0] = (reg_addr >> 8)&0xff; /* register first addr */ + t_buf[1] = reg_addr & 0xff; + memcpy(&t_buf[2],buf,len); /* copy send data to b[256]*/ + + msg.addr = client->addr; + msg.flags = 0; /* write flag*/ + + msg.buf = t_buf; + msg.len = len + 2; + ret = i2c_transfer(adapter, &msg, 1); + if(ret < 0) + { + LOG_ERR("i2c write failed! err_code:%d\n",ret); + } + kfree(t_buf); + return ret; +} + +/** + * @brief iic write funciton + * @param[in] regAddress: reg data + * @param[in] dat: point to data to write + * @param[in] length: write data length + * @retval 0:success, < 0: fail + */ +static int cs_press_iic_write(unsigned char regAddress, unsigned char *dat, unsigned int length) +{ + int ret = 0; + + if(dat == NULL){ + LOG_ERR("input buf is NULL\n"); + return -1; + } + if(length == 0){ + LOG_ERR("input len is invalid,len = 0\n"); + return -1; + } + ret = cs_i2c_write_bytes(g_cs_press.client, regAddress, dat, length); + if(ret < 0){ + return -1; + } + return 0; +} + +/** + * @brief iic read funciton + * @param[in] regAddress: reg data, + * @param[out] dat: read data buffer, + * @param[in] length: read data length + * @retval 0:success, -1: fail + */ +static int cs_press_iic_read(unsigned char regAddress, unsigned char *dat, unsigned int length) +{ + int ret = 0; + + if(dat == NULL){ + LOG_ERR("input buf is NULL\n"); + return -1; + } + if(length == 0){ + LOG_ERR("input len is invalid,len = 0\n"); + return -1; + } + /* user program */ + ret = cs_i2c_read_bytes(g_cs_press.client, regAddress, dat, length); + if(ret < 0){ + LOG_ERR("cs_i2c_read_bytes err\n"); + return -1; + } + return 0; +} + +/** + * @brief iic write funciton + * @param[in] regAddress: reg data, + * @param[in] *dat: point to data to write, + * @param[in] length: write data length + * @retval 0:success, -1: fail + */ +static int cs_press_iic_write_double_reg(unsigned short regAddress, unsigned char *dat, unsigned int length) +{ + int ret = 0; + + if(dat == NULL){ + LOG_ERR("input buf is NULL\n"); + return -1; + } + if(length == 0){ + LOG_ERR("input len is invalid,len = 0\n"); + return -1; + } + /* user program*/ + ret = cs_i2c_write_bytes_by_u16_addr(g_cs_press.client, regAddress, dat,length); + return ret; +} + +/** + * @brief iic read funciton + * @param[in] regAddress: reg data, + * @param[out] *dat: read data buffer, + * @param[in] length: read data length + * @retval 0:success, -1: fail + */ +static int cs_press_iic_read_double_reg(unsigned short regAddress, unsigned char *dat, unsigned int length) +{ + int ret = 0; + + if(dat == NULL){ + LOG_ERR("input buf is NULL\n"); + return -1; + } + if(length == 0){ + LOG_ERR("input len is invalid,len = 0\n"); + return -1; + } + /* user program*/ + ret = cs_i2c_read_bytes_by_u16_addr(g_cs_press.client, regAddress, dat, length); + return ret; +} + +/** + * @brief delay function + * @param[in] time_ms: delay time, unit:ms + * @retval None + */ +static void cs_press_delay_ms(unsigned int time_ms) +{ + msleep(time_ms); +} + +#if !RSTPIN_RESET_ENABLE +/** + * @brief cs_press_power_up + * @param None + * @retval None + */ +static void cs_press_power_up(void) +{ + // user program + #if 0 + if(gpio_is_valid(g_cs_press.power_gpio)){ + gpio_set_value(g_cs_press.power_gpio, RST_GPIO_HIGH); + }else{ + LOG_ERR("gpio rst is invalid\n"); + } + #endif +} + +/** + * @brief ic power down function + * @param None + * @retval None + */ +static void cs_press_power_down(void) +{ + // user program + #if 0 + if(gpio_is_valid(g_cs_press.power_gpio)){ + gpio_set_value(g_cs_press.power_gpio, RST_GPIO_LOW); + }else{ + LOG_ERR("gpio rst is invalid\n"); + } + #endif +} +#endif + +#if RSTPIN_RESET_ENABLE +/** + * @brief ic rst pin set high + * @param None + * @retval None + */ +static void cs_press_rstpin_high(void) +{ + /* user program*/ + #if 1 + if(gpio_is_valid(g_cs_press.rst_gpio)){ + gpio_set_value(g_cs_press.rst_gpio, RST_GPIO_HIGH); + }else{ + LOG_ERR("gpio rst is invalid\n"); + } + #endif +} + +/** + * @brief ic rst pin set low + * @param None + * @retval None + */ +static void cs_press_rstpin_low(void) +{ + /* user program*/ + #if 1 + if(gpio_is_valid(g_cs_press.rst_gpio)){ + gpio_set_value(g_cs_press.rst_gpio, RST_GPIO_LOW); + }else{ + LOG_ERR("gpio rst is invalid\n"); + } + #endif +} +#endif +/** + * @brief iic function test + * @param[in] test_data: test data + * @retval 0:success, -1: fail + */ +int cs_press_iic_rw_test(unsigned char test_data) +{ + int ret = 0; + unsigned char retry = RETRY_NUM; + unsigned char read_data = 0; + unsigned char write_data = test_data; + + do + { + cs_press_iic_write(AP_RW_TEST_REG, &write_data, 1); + cs_press_iic_read(AP_RW_TEST_REG, &read_data, 1); + ret = 0; + retry--; + if(read_data != write_data) + { + ret = -1; + LOG_ERR("iic test failed,w:%d,rd:%d %d\n",write_data,read_data,ret); + cs_press_delay_ms(1); + }else{ + LOG_INFO("iic test ok,w:%d,rd:%d\n",write_data,read_data); + retry = 0; + } + + }while(retry > 0); + return ret; +} + +/** + * @brief wakeup iic + * @param None + * @retval 0:success, -1: fail + */ +static char cs_press_wakeup_iic(void) +{ + int ret = 0; + + ret = cs_press_iic_rw_test(0x67); + return (char)ret; +} + +#ifdef INT_SET_EN +/** + * @brief input system register + * @param + * @retval none + */ +void fml_input_dev_init(void){ + int ret = 0; + + cs_input_dev = input_allocate_device(); + if (cs_input_dev != NULL) { + cs_input_dev->name = CS_PRESS_NAME; + cs_input_dev->phys = CS_INPUT_PHYS; + cs_input_dev->id.product = 0x1357; + cs_input_dev->id.vendor = 0x2468; + cs_input_dev->id.version = 10428; + + __set_bit(EV_KEY, cs_input_dev->evbit); + __set_bit(KEY_HOME, cs_input_dev->keybit); + __set_bit(KEY_VOLUMEUP, cs_input_dev->keybit); + __set_bit(KEY_VOLUMEDOWN, cs_input_dev->keybit); + __set_bit(KEY_POWER, cs_input_dev->keybit); + + __set_bit(EV_SYN, cs_input_dev->keybit); + __set_bit(EV_ABS, cs_input_dev->keybit); + __set_bit(BTN_TOUCH, cs_input_dev->keybit); + __set_bit(INPUT_PROP_DIRECT, cs_input_dev->propbit); + __set_bit(BTN_TOOL_FINGER, cs_input_dev->keybit); + + /* set input parameters */ + input_set_abs_params(cs_input_dev, ABS_MT_POSITION_X, + 0, g_cs_press.panel_max_x, 0, 0); + input_set_abs_params(cs_input_dev, ABS_MT_POSITION_Y, + 0, g_cs_press.panel_max_y, 0, 0); + input_set_abs_params(cs_input_dev, ABS_MT_TOUCH_MAJOR, + 0, g_cs_press.panel_max_w, 0, 0); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0) + input_mt_init_slots(cs_input_dev, CS_MAX_TOUCH, + INPUT_MT_DIRECT); +#else + input_mt_init_slots(cs_input_dev, CS_MAX_TOUCH); +#endif + + ret = input_register_device(cs_input_dev); + if (ret != 0) { + LOG_ERR("input register device error = %d\n", ret); + input_free_device(cs_input_dev); + } + } +} + +/** + * @brief input system unregister + * @param + * @retval none + */ +void fml_input_dev_exit(void){ + /* release input_dev */ + input_unregister_device(cs_input_dev); + input_free_device(cs_input_dev); +} + +/** + * @brief cs_irq_enable + * @param + * @retval + */ +static void cs_irq_enable(void) +{ + if (cs_irq_flag == 0) { + cs_irq_flag++; + enable_irq(cs_press_irq_num); + } else { + LOG_ERR("cs_press Eint already enabled!\n"); + } + LOG_ERR("Enable irq_flag=%d\n", cs_irq_flag); +} + +/** + * @brief cs_irq_disable + * @param + * @retval +*/ +static void cs_irq_disable(void) +{ + if (cs_irq_flag == 1) { + cs_irq_flag--; + disable_irq_nosync(cs_press_irq_num); + } else { + LOG_ERR("cs_press Eint already disabled!\n"); + } + LOG_ERR("Disable irq_flag=%d\n", cs_irq_flag); +} + +int cs_irq_stat(void) +{ + return gpio_get_value(cs_press_irq_gpio); +} + +static void* vtp_scroll_up_down(void *arg, int scroll_direction) +{ + struct cs_vtp_scroll *scroll = arg; + int i; + LOG_INFO("nav-gesture: start, p0=(%d,%d) L=%d s=%dpx t=%dms", scroll->x0, scroll->y0, + scroll->range, scroll->step, scroll->interval); + + for(i = 0; i < scroll->range; i++) { + input_mt_slot(cs_input_dev, 0); + input_mt_report_slot_state(cs_input_dev, MT_TOOL_FINGER, true); + + if(i == 0) { + input_report_abs(cs_input_dev, ABS_MT_POSITION_X, scroll->x0 - 1); + } else { + input_report_abs(cs_input_dev, ABS_MT_POSITION_X, scroll->x0); + } + + if(scroll_direction == CS_NAV_MODE_SCROLL_UP) { + input_report_abs(cs_input_dev, ABS_MT_POSITION_Y, scroll->y0 - (i * scroll->step)); + } else { + input_report_abs(cs_input_dev, ABS_MT_POSITION_Y, scroll->y0 + (i * scroll->step)); + } + input_report_abs(cs_input_dev, ABS_MT_TOUCH_MAJOR, 45); + input_report_key(cs_input_dev, BTN_TOUCH, 1); + input_sync(cs_input_dev); + udelay(scroll->interval * 1000); + } + //input_mt_slot(cs_input_dev, 0); + //input_mt_report_slot_state(cs_input_dev, MT_TOOL_FINGER, false); + //input_report_key(cs_input_dev, BTN_TOUCH, 0); + //input_sync(cs_input_dev); + LOG_INFO("nav-gesture scroll %d done", scroll_direction); + + return NULL; +} + +static void* vtp_zoom_in_out(void* arg, int zoom_direction) +{ + struct cs_vtp_zoom *zoom = arg; + int i; + LOG_INFO("nav-gesture: start, p1=(%d,%d) p2=(%d,%d) L=%d s=%dpx t=%dms", zoom->x0, + zoom->p1_y0, zoom->x0, zoom->p2_y0, zoom->range, zoom->step, zoom->interval); + + for(i = 0; i < zoom->range; i++) { + //finger 1 + input_mt_slot(cs_input_dev, 0); + input_mt_report_slot_state(cs_input_dev, MT_TOOL_FINGER, true); + + if(i == 0) { + input_report_abs(cs_input_dev, ABS_MT_POSITION_X, zoom->x0 - 1); + } else { + input_report_abs(cs_input_dev, ABS_MT_POSITION_X, zoom->x0); + } + + if(zoom_direction == CS_NAV_MODE_ZOOM_IN) { + input_report_abs(cs_input_dev, ABS_MT_POSITION_Y, zoom->p1_y0 - (i * zoom->step)); + } else { + input_report_abs(cs_input_dev, ABS_MT_POSITION_Y, zoom->p1_y0 + (i * zoom->step)); + } + input_report_abs(cs_input_dev, ABS_MT_TOUCH_MAJOR, 45); + input_report_key(cs_input_dev, BTN_TOUCH, 1); + input_sync(cs_input_dev); + + //finger 2 + input_mt_slot(cs_input_dev, 1); + input_mt_report_slot_state(cs_input_dev, MT_TOOL_FINGER, true); + + if(i == 0) { + input_report_abs(cs_input_dev, ABS_MT_POSITION_X, zoom->x0 - 1); + } else { + input_report_abs(cs_input_dev, ABS_MT_POSITION_X, zoom->x0); + } + + if(zoom_direction == CS_NAV_MODE_ZOOM_IN) { + input_report_abs(cs_input_dev, ABS_MT_POSITION_Y, zoom->p2_y0 + (i * zoom->step)); + } else { + input_report_abs(cs_input_dev, ABS_MT_POSITION_Y, zoom->p2_y0 - (i * zoom->step)); + } + input_report_abs(cs_input_dev, ABS_MT_TOUCH_MAJOR, 45); + input_report_key(cs_input_dev, BTN_TOUCH, 1); + input_sync(cs_input_dev); + + udelay(zoom->interval * 1000); + } + + LOG_INFO("nav-gesture zoom done"); + + return NULL; +} + +static void cs_press_worker_func(struct work_struct *w) +{ + int ret, i; + struct cs_press_coords raw_data = {0}; + int last_coord = -1; + int diff_coord; + + switch(g_cs_press.nav_mode) { + case CS_NAV_MODE_SCROLL: + LOG_INFO("CS_NAV_MODE_SCROLL mode\n"); + vtp_scroll.x0 = (g_cs_press.panel_max_x/2); + vtp_scroll.y0 = (g_cs_press.panel_max_y/2); + //vtp_scroll.range = g_nav_vtp_range_point; + vtp_scroll.step = 5; + vtp_scroll.interval = 4; + while(1) { + if(!kfifo_len(&g_cs_press.data_queue) && cs_irq_stat()) { + input_mt_slot(cs_input_dev, 0); + input_mt_report_slot_state(cs_input_dev, MT_TOOL_FINGER, false); + input_report_key(cs_input_dev, BTN_TOUCH, 0); + input_sync(cs_input_dev); + break; + } + + if (kfifo_len(&g_cs_press.data_queue)) { + ret = kfifo_out(&g_cs_press.data_queue, &raw_data, sizeof(struct cs_press_coords)); + if (!ret) { + LOG_ERR(" kfifo_out failed, it seems empty, ret=%d\n", ret); + } + LOG_INFO("kfifo_out coord: %d, pressure: %d\n", raw_data.coord, raw_data.pressure); + //simulate scroll touch events + + if (last_coord == -1) { + last_coord = raw_data.coord; + continue; + } + diff_coord = raw_data.coord - last_coord; + if(diff_coord < 0) { + //slide up + vtp_scroll.range = abs(diff_coord); + vtp_scroll_up_down(&vtp_scroll, CS_NAV_MODE_SCROLL_UP); + vtp_scroll.y0 -= vtp_scroll.range * vtp_scroll.step; + } else if(diff_coord > 0) { + //slide down + vtp_scroll.range = abs(diff_coord); + vtp_scroll_up_down(&vtp_scroll, CS_NAV_MODE_SCROLL_DOWN); + vtp_scroll.y0 += vtp_scroll.range * vtp_scroll.step; + } + last_coord = raw_data.coord; + } else { + //LOG_INFO("left raw_data is nothing\n"); + } + } + break; + + case CS_NAV_MODE_ZOOM: + LOG_INFO("CS_NAV_MODE_ZOOM mode\n"); + vtp_zoom.x0 = (g_cs_press.panel_max_x/2); + //vtp_zoom.p1_y0 = (g_cs_press.panel_max_y/2) - 100; + //vtp_zoom.p2_y0 = (g_cs_press.panel_max_y/2) + 100; + vtp_zoom.p1_y0 = (g_cs_press.panel_max_y/2) - 400; + vtp_zoom.p2_y0 = (g_cs_press.panel_max_y/2) + 400; + //vtp_zoom.range = g_nav_vtp_range_point; + vtp_zoom.step = 4; + vtp_zoom.interval = 5; + + while(1) { + if(!kfifo_len(&g_cs_press.data_queue) && cs_irq_stat()) { + for(i = 0; i < 2; i++) { + input_mt_slot(cs_input_dev, i); + input_mt_report_slot_state(cs_input_dev, MT_TOOL_FINGER, false); + input_report_key(cs_input_dev, BTN_TOUCH, 0); + input_sync(cs_input_dev); + } + break; + } + + if (kfifo_len(&g_cs_press.data_queue)) { + ret = kfifo_out(&g_cs_press.data_queue, &raw_data, sizeof(struct cs_press_coords)); + if (!ret) { + LOG_ERR(" kfifo_out failed, it seems empty, ret=%d\n", ret); + } + LOG_INFO("kfifo_out coord: %d, pressure: %d\n", raw_data.coord, raw_data.pressure); + //simulate zoom touch events + + if (last_coord == -1) { + last_coord = raw_data.coord; + continue; + } + diff_coord = raw_data.coord - last_coord; + if(diff_coord < 0) { + //slide up -> zoom in + vtp_zoom.range = abs(diff_coord); + vtp_zoom_in_out(&vtp_zoom, CS_NAV_MODE_ZOOM_IN); + vtp_zoom.p1_y0 -= vtp_zoom.range * vtp_zoom.step; + vtp_zoom.p2_y0 += vtp_zoom.range * vtp_zoom.step; + } else if(diff_coord > 0) { + //slide down -> zoom out + vtp_zoom.range = abs(diff_coord); + vtp_zoom_in_out(&vtp_zoom, CS_NAV_MODE_ZOOM_OUT); + vtp_zoom.p1_y0 += vtp_zoom.range * vtp_zoom.step; + vtp_zoom.p2_y0 -= vtp_zoom.range * vtp_zoom.step; + } + last_coord = raw_data.coord; + } else { + //LOG_INFO("left raw_data is nothing\n"); + } + } + break; + + default: + LOG_INFO("not supported navigation mode\n"); + } + +} + +/** + * @brief cs_press_interrupt_handler + * @param + * @retval +*/ +static irqreturn_t cs_press_interrupt_handler(int irq, void *dev_id) +{ + int irq_state = 0; + + printk("cs_press entry irq ok.\n"); + cs_press_int_flag = 1; + + irq_state = cs_irq_stat(); + + //cs_irq_disable(); + if (irq_state == IRQ_TYPE_EDGE_FALLING) { + cs_press_stop_read = 0; //clear flag + schedule_delayed_work(&g_cs_press.work, 0); //start process data on buffer + wake_up_interruptible(&cs_press_waiter); //start put data to buffer + } else if (irq_state == IRQ_TYPE_EDGE_RISING) { + cs_press_stop_read = 1; //need stop read data from IC + } + + return IRQ_HANDLED; +} +/** + * @brief get_key_event + * @param + * @retval +*/ +unsigned char get_key_event(void) +{ + unsigned char rbuf[1]; + unsigned char addr; + int len = 0; + + addr = IIC_KEY_EVENT; + len = 1; + rbuf[0] = 0x0; + if (cs_press_iic_read(addr, rbuf, len) <= 0) { + LOG_ERR("reg=%d,buf[0]=%d,len=%d,err\n", addr, rbuf[0], len); + } + return rbuf[0]; +} + +/** + * @brief key event handler + * @param + * @retval +*/ +void fml_key_report(void) +{ +#ifdef SIDE_KEY_MMI + unsigned char rbuf[6]; + unsigned char addr; + int len = 0; + + cs_press_wakeup_iic(); + + addr = IIC_KEY_EVENT_MMI; + len = 6; + memset(rbuf, 0x0, len); + if (cs_press_iic_read(addr, rbuf, len) != 0) { + LOG_ERR("reg=%02x, buf[0]=%02x, buf[1]=%02x, force=%d len=%d,err\n", + addr, rbuf[0], rbuf[1], (int16_t)((rbuf[5] << 8) + rbuf[4]), len); + return; + } else { + LOG_ERR("reg=%02x, buf[0]=%02x, buf[1]=%02x, force=%d len=%d,success\n", + addr, rbuf[0], rbuf[1], (int16_t)((rbuf[5] << 8) + rbuf[4]), len); + } + + if(rbuf[0] == 1) { + LOG_ERR("vol down key.\n"); + input_report_key(cs_input_dev, KEY_VOLUMEUP, 1); + input_sync(cs_input_dev); + input_report_key(cs_input_dev, KEY_VOLUMEUP, 0); + input_sync(cs_input_dev); + } else if(rbuf[1] == 1) { + LOG_ERR("vol up key.\n"); + input_report_key(cs_input_dev, KEY_VOLUMEDOWN, 1); + input_sync(cs_input_dev); + input_report_key(cs_input_dev, KEY_VOLUMEDOWN, 0); + input_sync(cs_input_dev); + } +#endif + +#ifdef SIDE_KEY + unsigned char event = 0; + event = get_key_event(); + if((event & 0x01) == 0x01){ + LOG_ERR("vol down key.\n"); + input_report_key(cs_input_dev, KEY_VOLUMEDOWN, 1); + input_sync(cs_input_dev); + input_report_key(cs_input_dev, KEY_VOLUMEDOWN, 0); + input_sync(cs_input_dev); + }else if((event & 0x02) == 0x02){ + LOG_ERR("vol up key.\n"); + input_report_key(cs_input_dev, KEY_VOLUMEUP, 1); + input_sync(cs_input_dev); + input_report_key(cs_input_dev, KEY_VOLUMEUP, 0); + input_sync(cs_input_dev); + }else if((event & 0x04) == 0x04){ + LOG_ERR("power key .\n"); + input_report_key(cs_input_dev, KEY_POWER, 1); + input_sync(cs_input_dev); + input_report_key(cs_input_dev, KEY_POWER, 0); + input_sync(cs_input_dev); + } +#endif + +#ifdef HOME_KEY + if(gpio_get_value(cs_press_irq_gpio) == 0){ + LOG_ERR("Home key down.\n"); + input_report_key(cs_input_dev, KEY_HOME, 1); + input_sync(cs_input_dev); + + }else if(gpio_get_value(cs_press_irq_gpio) == 1){ + LOG_ERR("Home key up.\n"); + input_report_key(cs_input_dev, KEY_HOME, 0); + input_sync(cs_input_dev); + } +#endif + +} + +/** + * @brief cs_press_event_handler + * @param + * @retval +*/ +static int cs_press_event_handler(void *unused) +{ + unsigned char read_data = 0; + unsigned char read_temp[FW_ONE_BLOCK_LENGTH_R] = {0}; + unsigned char read_data_cmd = 0x24; + unsigned char new_frame_cmd = 0x0; + unsigned char exit_read_cmd = 0x0; + struct cs_press_coords raw_data = {0}; + int queue_size; + + do { + LOG_ERR("cs_press_event_handler do wait\n"); + wait_event_interruptible(cs_press_waiter, + cs_press_int_flag != 0); + LOG_ERR("cs_press_event_handler enter wait\n"); + cs_press_int_flag = 0; + + cs_press_wakeup_iic(); + + cs_press_iic_write(DATA_MODE_REG, &read_data_cmd, 1); + + while(!cs_press_stop_read) { + //read raw press data from IC + cs_press_iic_write(DATA_READY_REG, &new_frame_cmd, 1); + + while(1) { + cs_press_iic_read(DATA_READY_REG, &read_data, 1); + if(read_data) + break; + cs_press_delay_ms(1); + } + + memset(read_temp, 0x0, sizeof(read_temp)); + cs_press_iic_read(DATA_READ_REG, read_temp, read_data); + + //put valid raw data to buffer + raw_data.coord = (read_temp[1] << 8)|read_temp[0]; + raw_data.pressure = (read_temp[3] << 8)|read_temp[2]; + LOG_INFO("coord: %d, press: %d", raw_data.coord, raw_data.pressure); + + if(raw_data.coord >= 0 && raw_data.coord <= 100) { + queue_size = (kfifo_len(&g_cs_press.data_queue) / sizeof(struct cs_press_coords)); + if (kfifo_avail(&g_cs_press.data_queue) && (queue_size < CS_DATA_MAX_QUEUE)) { + kfifo_in(&g_cs_press.data_queue, &raw_data, sizeof(struct cs_press_coords)); + LOG_INFO("push raw data: %d\n", raw_data.coord); + } else { + LOG_ERR("data_queue is full!!\n"); + kfifo_reset(&g_cs_press.data_queue); + LOG_ERR("data_queue is reset!!\n"); + } + } + } + + cs_press_iic_write(DATA_MODE_REG, &exit_read_cmd, 1); + //fml_key_report(); + //cs_irq_enable(); + } while (!kthread_should_stop()); + + return 0; +} + +/** + * @brief + * @param + * @retval +*/ +void eint_init(void) +{ + init_waitqueue_head(&cs_press_waiter); + INIT_DELAYED_WORK(&g_cs_press.work, cs_press_worker_func); + + kthread_run(cs_press_event_handler, 0, CS_PRESS_NAME); + cs_irq_disable(); + cs_irq_enable(); + LOG_ERR("init_irq ok"); + fml_input_dev_init(); +} +void eint_exit(void) +{ + fml_input_dev_exit(); +} +#endif +/** + * @brief clean debug mode reg, debug ready reg + * @param None + * @retval 0:success, -1: fail + */ +static char cs_press_clean_debugmode(void) +{ + char ret = 0; + unsigned char temp_data = 0; + + ret = cs_press_iic_write(DEBUG_MODE_REG, &temp_data, 1); + if(ret < 0){ + LOG_ERR("DEBUG_MODE_REG write failed\n"); + return -1; + } + ret = cs_press_iic_write(DEBUG_READY_REG, &temp_data, 1); + if(ret < 0){ + LOG_ERR("DEBUG_READY_REG write failed\n"); + return -1; + } + return 0; +} + +/** + * @brief set debug mode reg + * @param mode_num: debug mode num data + * @retval 0:success, -1: fail + */ +static char cs_press_set_debugmode(unsigned char mode_num) +{ + int ret = 0; + + ret = cs_press_iic_write(DEBUG_MODE_REG, &mode_num, 1); + if(ret < 0){ + LOG_ERR("DEBUG_MODE_REG write failed\n"); + return -1; + } + return 0; +} + +/** + * @brief set debug ready reg + * @param ready_num: debug ready num data + * @retval 0:success, -1: fail + */ +static char cs_press_set_debugready(unsigned char ready_num) +{ + int ret = 0; + + ret = cs_press_iic_write(DEBUG_READY_REG, &ready_num, 1); + if(ret < 0){ + LOG_ERR("DEBUG_READY_REG write failed\n"); + return -1; + } + return 0; +} + +/** + * @brief get debug ready reg data + * @param None + * @retval ready reg data + */ +static unsigned char cs_press_get_debugready(void) +{ + int ret = 0; + unsigned char ready_num = 0; + + ret = cs_press_iic_read(DEBUG_READY_REG, &ready_num, 1); + if(ret < 0) + { + ready_num = 0; + } + return ready_num; +} + +/** + * @brief write debug data + * @param[in] *debugdata: point to data buffer, + * @param[in] length: write data length + * @retval 0:success, -1: fail + */ +static char cs_press_write_debugdata(unsigned char *debugdata, unsigned char length) +{ + int ret = 0; + + ret = cs_press_iic_write(DEBUG_DATA_REG, debugdata, length); + if(ret < 0){ + LOG_ERR("DEBUG_DATA_REG write failed\n"); + return (char)-1; + } + return 0; +} + +/** + * @brief read debug data + * @param *debugdata: point to data buffer, length: write data length + * @retval 0:success, -1: fail + */ +static char cs_press_read_debugdata(unsigned char *debugdata, unsigned char length) +{ + int ret = 0; + + if(debugdata == NULL){ + LOG_ERR("ERR:buf is NULL\n"); + return (char)-1; + } + if(length == 0){ + LOG_ERR("ERR:read lenth = 0\n"); + return (char)-1; + } + ret = cs_press_iic_read(DEBUG_DATA_REG, debugdata, length); + if(ret < 0){ + LOG_ERR("DEBUG_DATA_REG read failed\n"); + return (char)-1; + } + return 0; +} + +#if 0 +/** + * @brief soft_reset the device + * @param None + * @retval 0:success, -1: fail + */ +static char cs_press_soft_reset_device(void) +{ + char ret = 0; + unsigned char retry = RETRY_NUM; + // M series required write value 0xcc + unsigned char temp_data = 0xcc; + + // repeatly reset including first time i2c wake up + do + { + if(ret!=0) + { + cs_press_delay_ms(1); + } + ret = cs_press_iic_write(AP_RESET_MCU_REG, &temp_data, 1); + }while((ret != 0 ) && (retry--)); + + return ret; +} +#endif + +/** + * @brief reset ic + * @param None + * @retval 0:success, -1: fail + */ +char cs_press_reset_ic(void) +{ + char ret = 0; + + rt_mutex_lock(&(g_cs_press.client->adapter->bus_lock)); + #if RSTPIN_RESET_ENABLE + cs_press_rstpin_high(); + cs_press_delay_ms(10); + cs_press_rstpin_low(); + cs_press_delay_ms(80); + + #else/* hw reset ic*/ + cs_press_power_down(); + cs_press_delay_ms(50); + cs_press_power_up(); + cs_press_delay_ms(80); + + #endif + rt_mutex_unlock(&(g_cs_press.client->adapter->bus_lock)); + return ret; +} + +void set_device_updating_flag(unsigned char val) +{ + g_cs_press.updating_flag = val; +} + +unsigned char get_device_updating_flag(void) +{ + return g_cs_press.updating_flag; +} +/** + * @brief forced firmware update + * @param[in] *fw_array: point to fw hex array + * @retval 0:success, -1: fail + */ +char cs_press_fw_force_update(const unsigned char *fw_array) +{ + unsigned int i = 0; + unsigned int j = 0; + int ret = 0; + char result = 0; + unsigned int fw_code_length = 0; + unsigned int fw_block_num_w = 0; + unsigned int fw_block_num_r = 0; + const unsigned char *fw_code_start = NULL; + unsigned int fw_count = 0; + unsigned char fw_read_code[FW_ONE_BLOCK_LENGTH_R] = {0}; + unsigned short fw_default_version = 0; + unsigned short fw_read_version = 0; + unsigned char boot_fw_write_cmd[BOOT_CMD_LENGTH] = BOOT_FW_WRITE_CMD; + unsigned char boot_fw_wflag_cmd[BOOT_CMD_LENGTH] = BOOT_FW_WFLAG_CMD; + unsigned char page_end = 0; + +#ifdef INT_SET_EN + cs_irq_disable(); /*close enit irq.*/ +#endif + /* fw init */ + fw_code_length = ((((unsigned short)fw_array[FW_ADDR_CODE_LENGTH+0]<<8)&0xff00)|fw_array[FW_ADDR_CODE_LENGTH+1]); + fw_code_start = &fw_array[FW_ADDR_CODE_START]; + fw_block_num_w = fw_code_length/FW_ONE_BLOCK_LENGTH_W; + fw_block_num_r = fw_code_length/FW_ONE_BLOCK_LENGTH_R; + fw_default_version = ((((unsigned short)fw_array[FW_ADDR_VERSION+0]<<8)&0xff00)|fw_array[FW_ADDR_VERSION+1]); + + if(fw_code_length % 128){ + LOG_INFO("fw is not 128*\n"); + goto FLAG_FW_FAIL; + } + page_end = fw_code_length % 256; + cs_press_reset_ic(); + /* send fw write cmd */ + cs_press_iic_write_double_reg(BOOT_CMD_REG ,boot_fw_write_cmd, BOOT_CMD_LENGTH); + cs_press_delay_ms(1500); /* waiting flash erase*/ + /* send fw code */ + fw_count = 0; + for(i = 0; i < fw_block_num_w; i++) + { + ret = cs_press_iic_write_double_reg(i*FW_ONE_BLOCK_LENGTH_W, (unsigned char*)fw_code_start+fw_count, FW_ONE_BLOCK_LENGTH_W); + fw_count += FW_ONE_BLOCK_LENGTH_W; + if(ret < 0) + { + LOG_ERR("ERR:iic write failed\n"); + result = (char)-1; + goto FLAG_FW_FAIL; + } + cs_press_delay_ms(10); + } + /* read & check fw code */ + fw_count = 0; + for(i = 0; i < fw_block_num_r; i++) + { + /* read code data */ + ret = cs_press_iic_read_double_reg(i*FW_ONE_BLOCK_LENGTH_R, fw_read_code, FW_ONE_BLOCK_LENGTH_R); + if(ret < 0) + { + LOG_ERR("ERR:iic write failed\n"); + result = (char)-1; + goto FLAG_FW_FAIL; + } + /* check code data */ + for(j = 0; j < FW_ONE_BLOCK_LENGTH_R; j++) + { + if(fw_read_code[j] != fw_code_start[fw_count+j]) + { + LOG_ERR("ERR:check code data failed\n"); + result = (char)-1; + goto FLAG_FW_FAIL; + } + } + fw_count += FW_ONE_BLOCK_LENGTH_R; + cs_press_delay_ms(5); + } + if(page_end > 0){ + /* read code data */ + ret = cs_press_iic_read_double_reg(fw_block_num_r*FW_ONE_BLOCK_LENGTH_R, fw_read_code, 128); + if(ret < 0) + { + LOG_ERR("ERR:iic write failed\n"); + result = (char)-1; + goto FLAG_FW_FAIL; + } + /* check code data */ + for(j = 0; j < 128; j++) + { + if(fw_read_code[j] != fw_code_start[fw_count+j]) + { + LOG_ERR("ERR:check code data failed\n"); + result = (char)-1; + goto FLAG_FW_FAIL; + } + } + cs_press_delay_ms(5); + } + + /* send fw flag cmd */ + cs_press_iic_write_double_reg(BOOT_CMD_REG ,boot_fw_wflag_cmd, BOOT_CMD_LENGTH); + cs_press_delay_ms(50); + /* reset */ + cs_press_reset_ic(); + /* check fw version */ + cs_press_delay_ms(300); /* skip boot */ + ret = cs_press_iic_read(AP_VERSION_REG, fw_read_code, CS_FW_VERSION_LENGTH); + fw_read_version = 0; + + if(ret >= 0){ + fw_read_version = ((((unsigned short)fw_read_code[2]<<8)&0xff00)|fw_read_code[3]); + } + LOG_INFO("update version:0x%04x,read version:0x%04x\n", fw_default_version, fw_read_version); + if(fw_read_version != fw_default_version){ + LOG_ERR("ERR:fw_read_version != fw_default_version\n"); + result = (char)-1; + goto FLAG_FW_FAIL; + } +FLAG_FW_FAIL: +#ifdef INT_SET_EN + cs_irq_enable(); /*open enit irq.*/ +#endif + return result; +} + + +/** + * @brief firmware high version update + * @param[in] *fw_array: point to fw hex array + * @retval 0:success, -1: fail, 1: no need update + */ +char cs_press_fw_high_version_update(const unsigned char *fw_array) +{ + int ret = 0; + char result = 0; + unsigned char read_temp[FW_ONE_BLOCK_LENGTH_R] = {0}; + unsigned short read_version = 0; + unsigned short default_version = 0; + char flag_update = 0; /* 0: no need update fw, 1: need update fw */ + unsigned char retry = RETRY_NUM; + + cs_press_delay_ms(300); /* skip boot jump time */ + /* read ap version */ + ret = cs_press_iic_read(AP_VERSION_REG, read_temp, CS_FW_VERSION_LENGTH); + + if(ret >= 0) + { + /* get driver ap version */ + default_version = ((((unsigned short)fw_array[FW_ADDR_VERSION+0]<<8)&0xff00)|fw_array[FW_ADDR_VERSION+1]); + /* get ic ap version */ + read_version = ((((unsigned short)read_temp[2]<<8)&0xff00)|read_temp[3]); + /* compare */ + if(read_version != default_version) + { + flag_update = 1; + } + } + else + { + flag_update = 1; + + LOG_ERR("read AP_VERSION_REG failed\n"); + } + + LOG_INFO("FW on file version:0x%04x, FW on IC version:0x%04x, need update flag:%d\n", + default_version, read_version, flag_update); + if(flag_update == 0) + { + LOG_INFO("no need update\n"); + return 1; /* no need update */ + } + /* update fw */ + retry = RETRY_NUM; + do + { + result = cs_press_fw_force_update(fw_array); + if(result < 0 || result >= 0x80){ + LOG_ERR("update failed,ret:%d, retry count:%d\n",result, retry); + } + }while((result!=0)&&(retry--)); + + return result; +} + +/** + * @brief fml_firmware_send_data + * @param + * @retval none + */ +int fml_firmware_send_data(unsigned char *data, int len) +{ + int fw_body_len = 0; + int fw_len = 0; + int ret = 0; + char result = 0; + + if(data == NULL){ + ret = -1; + LOG_ERR("data buffer null:\n"); + goto exit_fw_buf; + } + fw_body_len = ((int)data[FW_ADDR_CODE_LENGTH]<<8)|((int)data[FW_ADDR_CODE_LENGTH+1]); + fw_len = fw_body_len + 256; + LOG_INFO("[fw file len:%d,fw body len: %d]\n", len, fw_body_len); + if(fw_body_len <= 0 || fw_body_len > FW_UPDATA_MAX_LEN) + { + LOG_ERR("[err!fw body len err!len=%d]\n", len); + ret = -1; + goto exit_fw_buf; + } + if(fw_len != len) + { + LOG_ERR("[err!fw file len err! len=%d]\n", fw_len); + ret = -1; + goto exit_fw_buf; + } + set_device_updating_flag(1); + if(g_cs_press.update_type == FORCE_FILE_UPDATE) + { + result = cs_press_fw_force_update(data); + g_cs_press.update_type = HIGH_VER_FILE_UPDATE; + }else{ + result = cs_press_fw_high_version_update(data); + } + set_device_updating_flag(0); + if (result < 0 || result >= 0x80) + { + ret = -1; + LOG_ERR("Burning firmware fails\n"); + } else { + LOG_ERR("Burning firmware success\n"); + } +exit_fw_buf: + LOG_INFO("end\n"); + return ret; +} + +/** + * @brief fml_firmware_config_cb + * @param + * @retval none + */ +static void fml_firmware_config_cb(const struct firmware *cfg, void *ctx) +{ + int fw_error = 0; + + if(cfg) + { + fw_error = fml_firmware_send_data((unsigned char*)cfg->data, cfg->size); + if(fw_error) + { + LOG_ERR("firmware send data err:%d\n", fw_error); + goto err_release_cfg; + } + } +err_release_cfg: + release_firmware(cfg); + cs_press_delay_ms(10); + LOG_INFO("end\n"); +} + + +/** + * @brief firmware update by file + * @param + * @retval 0:success, -1: fail,1:no need update + */ +int fml_fw_update_by_file(void) +{ + int ret_error = 0; + struct cs_press_t *fw_st = NULL; + + if (strlen(g_cs_press.cs_fw_name) == 0) { + LOG_ERR("cs FW name is not specified\n"); + return -1; + } + + fw_st = devm_kzalloc(&(g_cs_press.client->dev), sizeof(*fw_st), GFP_KERNEL); + if(!fw_st) + { + LOG_ERR("devm_kzalloc failed\n"); + return -1; + } + fw_st->client = g_cs_press.client; + ret_error = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, g_cs_press.cs_fw_name, + &(g_cs_press.client->dev), GFP_KERNEL, fw_st, fml_firmware_config_cb); + devm_kfree(&(g_cs_press.client->dev), fw_st); + if(ret_error) + { + LOG_ERR("request_firmware_nowait failed:%d\n",ret_error); + return -1; + } + LOG_INFO("end\n"); + return 0; +} + +#if 0 +/** + * @brief fml_fw_update_by_array + * @param + * @retval 0:success, -1: fail + */ +int fml_fw_update_by_array(void) +{ + char ret = 0; + char retry = RETRY_NUM; + + cs_press_wakeup_iic(); + do{ + ret = cs_press_fw_high_version_update(cs_default_fw_array); + if(ret < 0){ + LOG_ERR("update failed,ret:%d\n",ret); + } + }while((ret!=0)&&(retry--)); + + return ret; +} +#endif + +/** + * @brief read fw info + * @param[out] *fw_info: point to info struct + * @retval 0:success, -1: fail + */ +char cs_press_read_fw_info(CS_FW_INFO_Def *fw_info) +{ + char ret = 0; + unsigned char read_temp[FW_ONE_BLOCK_LENGTH_R] = {0}; + + if(fw_info == NULL){ + LOG_ERR("ERR:point fw_info NULL\n"); + } + cs_press_wakeup_iic(); + /* read fw info*/ +#if 0 + ret |= cs_press_iic_read(AP_MANUFACTURER_ID_REG, read_temp, CS_MANUFACTURER_ID_LENGTH); // Manufacturer id + if(ret >= 0) + { + LOG_ERR("%x,%x\n",read_temp[0],read_temp[1]); + fw_info->manufacturer_id = ((unsigned short)read_temp[1]<<8) | read_temp[0]; + LOG_ERR("%x\n",fw_info->manufacturer_id); + } + ret |= cs_press_iic_read(AP_MODULE_ID_REG, read_temp, CS_MODULE_ID_LENGTH); // Module id + if(ret >= 0) + { + fw_info->module_id = ((unsigned short)read_temp[1]<<8) | read_temp[0]; + } +#endif + ret |= cs_press_iic_read(AP_VERSION_REG, read_temp, CS_FW_VERSION_LENGTH); /* FW Version*/ + if(ret==0) + { + fw_info->fw_version = ((unsigned short)read_temp[2]<<8) | read_temp[3]; + } + return ret; +} + +/** + * @brief wake up device + * @param None + * @retval 0:success, -1: fail + */ +char cs_press_set_device_wakeup(void) +{ + char ret = 0; + unsigned char retry = RETRY_NUM; + unsigned char temp_data = 1; + + do + { + if(ret!=0) + { + cs_press_delay_ms(1); + } + ret = cs_press_iic_write(AP_WAKEUP_REG, &temp_data, 1); + }while((ret!=0)&&(retry--)); + return ret; +} + +/** + * @brief put the device to sleep + * @param None + * @retval 0:success, -1: fail + */ +char cs_press_set_device_sleep(void) +{ + char ret = 0; + unsigned char retry = 5; + char i; + /*0=FTM 0x01=write flag 0xFF=check sum*/ + unsigned char sleep_cmd[3] = {0x00, 0x01, 0xFF}; + unsigned char read_data[3] = {0x00, 0xFF, 0xFF}; + + for(i = 0; i < retry; i++) + { + ret = cs_press_iic_write(AP_RW_WORK_MODE_REG, sleep_cmd, 3); + if(ret == 0) + { + ret = -1; + cs_press_delay_ms(1); + if(cs_press_iic_read(AP_RW_WORK_MODE_REG, read_data, 3) == 0) + { + if((read_data[0]==0)&&((read_data[1]+read_data[2]) == 0)) + { + ret = 0; + break; + } + } + } + cs_press_delay_ms(1); + } + return ret; +} + +/** + * @brief init read rawdata + * @param None + * @retval 0:success, -1: fail + */ +char cs_press_read_rawdata_init(void) +{ + char ret = 0; + + cs_press_wakeup_iic(); + + cs_press_clean_debugmode(); + + ret = cs_press_set_debugmode(AP_R_RAWDATA_DEBUG_MODE); + + return ret; +} + +/** + * @brief read rawdata + * @param[in] *rawdata: point to sensor data strcut + * @retval 0:none, -1: fail, >0:vaild data num + */ +char cs_press_read_rawdata(CS_RAWDATA_Def *rawdata) +{ + char ret = 0; + unsigned char i = 0; + unsigned char data_temp[AFE_MAX_CH*2] = {0}; + unsigned char byte_num; + + ret = (char)-1; + + byte_num = cs_press_get_debugready(); + if((byte_num <= (AFE_MAX_CH*2))&&(byte_num > 0)) + { + if((byte_num%2) == 0) + { + ret = cs_press_read_debugdata(data_temp, byte_num); + if(ret == 0) + { + for(i=0;i<(byte_num/2);i++) + { + rawdata->rawdata[i] = ((((unsigned short)data_temp[i*2+1]<<8)&0xff00)|data_temp[i*2]); + } + ret = byte_num/2; + } + } + else{ + ret = (char)-1; + } + }else{ + LOG_ERR("byte_num invalid:%d\n",byte_num); + } + cs_press_set_debugready(0); + return ret; +} + +/** + * @brief init read processed data + * @param None + * @retval 0:success, -1: fail + */ +char cs_press_read_processed_data_init(void) +{ + char ret = 0; + + cs_press_wakeup_iic(); + + cs_press_clean_debugmode(); + + ret = cs_press_set_debugmode(AP_R_PROCESSED_DEBUG_MODE); + + return ret; +} + +/** + * @brief read processeddata + * @param[in] *proce_data: point to processed data strcut + * @retval 0:success, -1: fail + */ +char cs_press_read_processed_data(CS_PROCESSED_DATA_Def *proce_data) +{ + char ret = 0; + unsigned char i = 0; + unsigned char data_temp[(AFE_USE_CH*4*2)+2]; /* 1 ch have 4 types of data*/ + short data_temp_s16[(AFE_USE_CH*4)+1]; + unsigned char byte_num; + short checksum; + + ret = (char)-1; + + byte_num = cs_press_get_debugready(); + + if((byte_num == ((AFE_USE_CH*4*2)+2))&&(byte_num > 0)) + { + ret = cs_press_read_debugdata(data_temp, byte_num); + if(ret == 0) + { + for(i=0;i<(AFE_USE_CH*4)+1;i++) + { + data_temp_s16[i] = (short)((((unsigned short)data_temp[i*2+1]<<8)&0xff00)|data_temp[i*2]); + } + checksum = 0; + for(i=0;i<(AFE_USE_CH*4);i++) + { + checksum += data_temp_s16[i]; + } + if(checksum == data_temp_s16[(AFE_USE_CH*4)]) /* check right*/ + { + for(i=0;iarith_rawdata[i] = data_temp_s16[i]; + proce_data->baseline[i] = data_temp_s16[AFE_USE_CH+i]; + proce_data->diffdata[i] = data_temp_s16[AFE_USE_CH*2+i]; + proce_data->energy_data[i] = data_temp_s16[AFE_USE_CH*3+i]; + } + ret = 0; + } + }else{ + LOG_ERR("byte_num invalid:%d\n",byte_num); + } + } + cs_press_set_debugready(0); + return ret; +} + +/** + * @brief init read processed data + * @param None + * @retval 0:success, -1: fail + */ +char cs_press_read_ndt_debug_init(void) +{ + char ret = 0; + unsigned char retry = RETRY_NUM; + cs_press_wakeup_iic(); + cs_press_clean_debugmode(); + do + { + ret = cs_press_set_debugmode(AP_R_NDT_DEBUG_MODE); + }while((ret != 0 ) && (retry--)); + + LOG_ERR("debug init %d\n",ret); + return ret; +} + + +/** + * @brief read calibration factor + * @param[in] read channel num + * @param[out] *cal_factor: point to calibration factor + * @retval 0:success, -1: fail + */ +char cs_press_read_calibration_factor(unsigned char ch, unsigned short *cal_factor) +{ + char ret = (char)-1; + unsigned char data_temp[4]={0,0,0,0}; + unsigned char num; + + if(cal_factor == NULL){ + LOG_ERR("point cal_factor if NULL\n"); + return -1; + } + cs_press_wakeup_iic(); + cs_press_clean_debugmode(); + cs_press_set_debugmode(AP_R_CAL_FACTOR_DEBUG_MODE); + data_temp[0] = ch; + cs_press_write_debugdata(data_temp, 2); + cs_press_set_debugready(2); + cs_press_delay_ms(DEBUG_MODE_DELAY_TIME); + num = cs_press_get_debugready(); + if(num == 4) + { + ret = cs_press_read_debugdata(data_temp, 4); + if(ret == 0) + { + *cal_factor = ((((unsigned short)data_temp[1]<<8)&0xff00)|data_temp[0]); + } + } + return ret; +} + +/** + * @brief write calibration factor + * @param[in] channel num + * @param[in] cal_factor: calibration factor data + * @retval 0:success, -1: fail + */ +char cs_press_write_calibration_factor(unsigned char ch, unsigned short cal_factor) +{ + char ret = 0; + unsigned char data_temp[7] = {0}; + unsigned short read_cal_factor = 0; + + cs_press_wakeup_iic(); + cs_press_clean_debugmode(); + cs_press_set_debugmode(AP_W_CAL_FACTOR_DEBUG_MODE); + + data_temp[0] = ch; + data_temp[1] = 0; + data_temp[2] = cal_factor; + data_temp[3] = cal_factor>>8; + data_temp[4] = 0; + data_temp[5] = 0; + + cs_press_write_debugdata(data_temp, 6); + cs_press_set_debugready(6); + cs_press_delay_ms(DEBUG_MODE_DELAY_TIME*3); + cs_press_read_calibration_factor(ch, &read_cal_factor); + if(read_cal_factor != cal_factor) + { + LOG_ERR("set calibration factor failed\n"); + ret = (char)-1; + } + return ret; +} +/** + * @brief enable calibration function + * @param[in] channel num + * @retval 0:success, -1: fail + */ +char cs_press_calibration_enable(unsigned char ch_num) +{ + char ret = 0; + unsigned char temp_data = 1 + ch_num; + + cs_press_wakeup_iic(); + cs_press_delay_ms(1); + ret = cs_press_iic_write(AP_CALIBRATION_REG, &temp_data, 1); + return ret; +} + +/** + * @brief disable calibration function + * @param None + * @retval 0:success, -1: fail + */ +char cs_press_calibration_disable(void) +{ + char ret = 0; + unsigned char temp_data = 0; + + cs_press_wakeup_iic(); + cs_press_delay_ms(1); + ret = cs_press_iic_write(AP_CALIBRATION_REG, &temp_data, 1); + return ret; + +} +/** + * @brief read press level + * @param[out] *press_level: point to press level buffer + * @retval 0:success, -1: fail + */ +char cs_press_read_press_level(unsigned short *press_level) +{ + char ret = (char)-1; + unsigned char data_temp[4]={0,0,0,0}; + unsigned char num; + + if(press_level == NULL){ + LOG_ERR("point press_level is NULL\n"); + return -1; + } + cs_press_wakeup_iic(); + cs_press_clean_debugmode(); + cs_press_set_debugmode(AP_R_PRESS_LEVEL_DEBUG_MODE); + cs_press_write_debugdata(data_temp, 2); + cs_press_set_debugready(2); + cs_press_delay_ms(DEBUG_MODE_DELAY_TIME); + num = cs_press_get_debugready(); + if(num == 4) + { + ret = cs_press_read_debugdata(data_temp, num); + if(ret == 0) + { + *press_level = ((((unsigned short)data_temp[1]<<8)&0xff00)|data_temp[0]); + } + } + return ret; +} + +/** + * @brief write press level + * @param[in] press_level: press level data + * @retval :success, -1: fail + */ +char cs_press_write_press_level(unsigned short press_level) +{ + char ret; + unsigned char data_temp[6]={0}; + unsigned short read_press_level = 0; + + cs_press_wakeup_iic(); + + cs_press_clean_debugmode(); + cs_press_set_debugmode(AP_W_PRESS_LEVEL_DEBUG_MODE); + + data_temp[0] = 0; + data_temp[1] = 0; + data_temp[2] = press_level; + data_temp[3] = press_level>>8; + data_temp[4] = 0; + data_temp[5] = 0; + + cs_press_write_debugdata(data_temp, 6); + cs_press_set_debugready(6); + cs_press_delay_ms(DEBUG_MODE_DELAY_TIME*3); + cs_press_read_press_level(&read_press_level); + + ret = 0; + if(read_press_level != press_level) + { + ret = (char)-1; + } + + return ret; +} +/** + * @brief check calibration result + * @param[out] check result data buf + * @retval 0:running, -1: error, 1: success, 2: fail, 3: overtime + */ +char cs_press_calibration_check(CS_CALIBRATION_RESULT_Def *calibration_result) +{ + char ret; + unsigned char num; + unsigned char data_temp[10]; + + if(calibration_result == NULL){ + LOG_ERR("point calibration_result is NULL\n"); + return -1; + } + ret = cs_press_set_debugmode(AP_CALIBRATION_DEBUG_MODE); + num = cs_press_get_debugready(); + if(num == 10) + { + ret = cs_press_read_debugdata(data_temp, 10); + + if(ret == 0) + { + calibration_result->calibration_channel = data_temp[0]; + calibration_result->calibration_progress = data_temp[1]; + calibration_result->calibration_factor = ((((unsigned short)data_temp[3]<<8)&0xff00)|data_temp[2]); + calibration_result->press_adc_1st = ((((short)data_temp[5]<<8)&0xff00)|data_temp[4]); + calibration_result->press_adc_2nd = ((((short)data_temp[7]<<8)&0xff00)|data_temp[6]); + calibration_result->press_adc_3rd = ((((short)data_temp[9]<<8)&0xff00)|data_temp[8]); + + if(calibration_result->calibration_progress >= CALIBRATION_SUCCESS_FALG) + { + ret = 1; + } + if(calibration_result->calibration_progress == CALIBRATION_FAIL_FALG) + { + ret = 2; + } + if(calibration_result->calibration_progress == CALIBRATION_OVERTIME_FALG) + { + ret = 3; + } + } + } + + return ret; +} + +/** + * @brief init read noise data + * @param[in] period_num: period num of calculation the noise + * @retval 0:success, -1: fail + */ +char cs_press_read_noise_init(unsigned short period_num) +{ + char ret = 0; + unsigned char data_temp[2]; + + cs_press_wakeup_iic(); + + ret |= cs_press_clean_debugmode(); + + ret |= cs_press_set_debugmode(AP_R_NOISE_DEBUG_MODE); + + data_temp[0] = period_num; + data_temp[1] = period_num>>8; + ret |= cs_press_write_debugdata(data_temp, 2); + + ret |= cs_press_set_debugready(2); + + return ret; +} + +/** + * @brief read noise data + * @param[out] noise_data:read data buf + * @retval 0:success, -1: fail + */ +char cs_press_read_noise(CS_NOISE_DATA_Def *noise_data) +{ + char ret = 0; + unsigned char i = 0; + unsigned char num = 0; + unsigned char data_temp[AFE_USE_CH*6]; + unsigned int dat_temp; + + if(noise_data == NULL){ + LOG_ERR("point noise_data is NULL\n"); + return -1; + } + ret = (char)-1; + + cs_press_wakeup_iic(); + + num = cs_press_get_debugready(); + + if(num == AFE_USE_CH*6) + { + cs_press_read_debugdata(data_temp, num); + + dat_temp = 0; + for(i=0;inoise_peak[i] = ((((short)data_temp[1+i*6]<<8)&0xff00)|data_temp[0+i*6]); + + dat_temp = data_temp[5+i*6]; + dat_temp <<= 8; + dat_temp |= data_temp[4+i*6]; + dat_temp <<= 8; + dat_temp |= data_temp[3+i*6]; + dat_temp <<= 8; + dat_temp |= data_temp[2+i*6]; + + noise_data->noise_std[i] = dat_temp; + } + ret = 0; + } + return ret; +} +/** + * @brief init read offset data + * @param None + * @retval 0:success, -1: fail + */ +static char cs_press_read_offset_init(void) +{ + char ret = 0; + + cs_press_wakeup_iic(); + ret |= cs_press_clean_debugmode(); + ret |= cs_press_set_debugmode(AP_R_OFFSET_DEBUG_MODE); + ret |= cs_press_set_debugready(0); + return ret; +} + +/** + * @brief read offset data + * @param[out] offset_data + * @retval 0:success, -1: fail + */ +char cs_press_read_offset(CS_OFFSET_DATA_Def *offset_data) +{ + char ret; + unsigned char i; + unsigned char num; + unsigned char data_temp[AFE_USE_CH*2]={0}; + + if(offset_data == NULL){ + LOG_ERR("point offset_data is NULL\n"); + return -1; + } + ret = (char)-1; + cs_press_read_offset_init(); + + cs_press_delay_ms(50); /* waitting for data ready*/ + + num = cs_press_get_debugready(); + + if(num == (AFE_USE_CH*2)) + { + cs_press_read_debugdata(data_temp, num); + for(i = 0; i < AFE_USE_CH; i++) + { + offset_data->offset[i] = ((((short)data_temp[i*2+1]<<8)&0xff00)|(short)data_temp[i*2]); + } + ret = 0; + } + return ret; +} +/** + * @brief init read sensor status + * @param None + * @retval 0:success, -1: fail + */ +static char cs_press_read_sensor_status_init(void) +{ + char ret = 0; + + cs_press_wakeup_iic(); + ret |= cs_press_clean_debugmode(); + ret |= cs_press_set_debugready(0); + ret |= cs_press_set_debugmode(AP_R_SENSOR_STATUS_DEBUG_MODE); + + return ret; +} + +/** + * @brief read noise data + * @param[out] sensor_status + * @retval 0:success, -1: fail + */ +char cs_press_read_sensor_status(CS_SENSOR_STATUS_Def *sensor_status) +{ + char ret; + unsigned char i; + unsigned char num; + unsigned char data_temp[AFE_USE_CH]={0}; + + if(sensor_status == NULL){ + LOG_ERR("point sensor_status is NULL\n"); + return -1; + } + ret = (char)-1; + + cs_press_read_sensor_status_init(); + + for(i=0;istatus[i] = (short)data_temp[i]; + } + ret = 0; + } + + return ret; +} + +char cs_write_calibration_command(unsigned char *data) +{ + char ret = 0; + + if(data == NULL){ + LOG_ERR("point data is NULL\n"); + return -1; + } + ret |= cs_press_wakeup_iic(); + ret |= cs_press_clean_debugmode(); + ret |= cs_press_set_debugmode(0x14); + ret |= cs_press_read_debugdata(data, 2); + return ret; +} + + +char cs_read_boot_version(unsigned char *version_data) +{ + char ret = 0 ; + + if(version_data == NULL){ + LOG_ERR("point version_data is NULL\n"); + return -1; + } + cs_press_reset_ic(); + ret = cs_press_iic_read_double_reg(0xF100, version_data, 4); + return ret; +} + +char cs_set_press_threshold(int left_val, int right_val) +{ + int i; + char ret = 0 ; + unsigned char retry = RETRY_NUM; + unsigned char temp_data[CS_FORCE_TRIG_LENGTH]; + + temp_data[0] = left_val&0xff; + temp_data[1] = (left_val>>8)&0xff; + temp_data[2] = right_val&0xff; + temp_data[3] = (right_val>>8)&0xff; + temp_data[4] = 0x03;/*set left & right*/ + temp_data[5] = 0; + for(i = 0; i < 5; i++) + { + temp_data[5] += temp_data[i]; + } + temp_data[5] = (0xFF - temp_data[5]) + 1; + do + { + if(ret!=0) + { + cs_press_delay_ms(1); + } + ret = cs_press_iic_write(AP_RW_BUTTON_FORCE_THR_REG, temp_data, CS_FORCE_TRIG_LENGTH); + }while((ret != 0 ) && (retry--)); + if(ret < 0) + { + LOG_ERR("err write press trig\n"); + }else{ + LOG_DEBUG("write press trig:ok %d %d\n",left_val, right_val); + } + return ret; +} + +#if 0 +/** + * @brief init + * @param None + * @retval 0:success, -1: fail + */ +char cs_press_init(void) +{ + char ret = 0; + char i; + unsigned char boot_ver_buf[4]; + LOG_DEBUG("cs driver ver %s\n", cs_driver_ver); + for(i = 0; i < 3; i++) + { + if(cs_read_boot_version(boot_ver_buf) >= 0) + { + if(boot_ver_buf[0] == 0x71) + { + LOG_INFO("boot version %02x %02x %02x %02x\n", boot_ver_buf[0], boot_ver_buf[1], + boot_ver_buf[2], boot_ver_buf[3]); + break; + } + }else{ + LOG_ERR("read boot version err %d\n",i); + } + } + if(i >= 3) + { + LOG_ERR("chipid err return\n"); + return ret; + } + /* reset ic */ + /* check whether need to update ap fw */ + ret = fml_fw_update_by_file(); + /*ret = fml_fw_update_by_array();*/ + + if(ret >= 0) + { + return 0; + } + return ret; +} +#endif +#if 0 +static ssize_t fml_fw_update_by_array_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + int result = 0; + + result = fml_fw_update_by_array(); + if (result >= 0) + ret = sprintf(buf, "%d,pass!\n", result); + else + ret = sprintf(buf, "%d,failed!\n", result); + + LOG_ERR("%s\n", buf); + return ret+1; +} +#endif + +static int cs_proc_rw_reg_show(struct seq_file *m,void *v) +{ + int i = 0; + + if ((g_cs_press.read_reg_len != 0)&&(g_cs_press.read_reg_len < 255)) { + for (i = 0; i < g_cs_press.read_reg_len; i++) + { + seq_printf(m, "%02x ", g_cs_press.read_reg_data[i]); + } + seq_printf(m, "\n"); + }else{ + seq_printf(m,"rw reg failed\n"); + } + return 0; +} + +static int cs_proc_rw_reg_open(struct inode *inode, struct file *filp) +{ + LOG_INFO("reg open start\n"); + return single_open(filp,cs_proc_rw_reg_show,NULL); +} + +static ssize_t cs_proc_rw_reg_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset) +{ + char *kbuf = NULL; + const char *startpos = NULL; + unsigned int tempdata = 0; + const char *lastc = NULL; + char *firstc = NULL; + int idx = 0; + int ret = 0; + int set_mode = 0; + unsigned char change_val[32] = {0}; + + LOG_INFO("reg write start\n"); + if(count <= 0){ + LOG_ERR("PARAM invalid\n"); + goto exit; + } + kbuf = kzalloc(count, GFP_KERNEL ); + if (!kbuf) { + goto exit; + } + if (copy_from_user(kbuf, buf, count)) { + goto exit_kfree; + } + startpos = kbuf; + lastc = kbuf + count; + while (startpos < lastc) + { + LOG_DEBUG("idx:%d\n", idx); + firstc = strstr(startpos, "0x"); + if (!firstc) { + LOG_ERR("cant find firstc\n"); + goto exit_kfree; + } + firstc[4] = 0; + ret = kstrtouint(startpos, 0, &tempdata); + if (ret) { + LOG_ERR("fail to covert digit\n"); + goto exit_kfree; + } + if (idx == 0) { + set_mode = tempdata; + LOG_ERR("set_mode:%d\n", set_mode); + } else { + change_val[idx - 1] = tempdata; + LOG_DEBUG("tempdata:%d\n", tempdata); + } + startpos = firstc + 5; + idx++; + if (set_mode == 0 && idx > 3 && idx >= change_val[0] + 3) + break; + else if (set_mode == 1 && idx > 3) + break; + else if (set_mode == 2 && idx > 4 && idx >= change_val[0] + 4) + break; + else if (set_mode == 3 && idx > 4) + break; + } + + cs_press_set_device_wakeup(); + if (set_mode == 0) { + cs_press_iic_write(change_val[1], + &change_val[2], (unsigned int)change_val[0]); + g_cs_press.read_reg_len = 0; + } else if (set_mode == 1) { + cs_press_iic_read(change_val[1], + &g_cs_press.read_reg_data[0], (unsigned int)change_val[0]); + g_cs_press.read_reg_len = change_val[0]; + } else if (set_mode == 2) { + cs_press_iic_write_double_reg(((change_val[1]<<8) | change_val[2]), + &change_val[3], (unsigned int)change_val[0]); + g_cs_press.read_reg_len = 0; + } else if (set_mode == 3) { + cs_press_iic_read_double_reg(((change_val[1]<<8) | change_val[2]), + &g_cs_press.read_reg_data[0], (unsigned int)change_val[0]); + g_cs_press.read_reg_len = change_val[0]; + } else { + g_cs_press.read_reg_len = 0; + } +exit_kfree: + kfree(kbuf); +exit: + return count; +} + +static ssize_t cs_proc_iic_rw_test_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset) +{ + char *kbuf = NULL; + int ret = 0; + const char *startpos = NULL; + char *firstc = NULL; + unsigned int tempdata = 0; + + if(count <= 0){ + LOG_ERR("argument err\n"); + goto exit; + } + kbuf = kzalloc(count, GFP_KERNEL ); + if (!kbuf) { + goto exit; + } + startpos = kbuf; + if (copy_from_user(kbuf, buf, count)) { + goto exit_kfree; + } + firstc = strstr(startpos, "0x"); + if (!firstc) { + LOG_ERR("param format invalid\n"); + goto exit_kfree; + } + firstc[4] = 0; + ret = kstrtouint(startpos, 0, &tempdata); + if (ret) { + LOG_ERR("param convert to int failed\n"); + goto exit_kfree; + } + ret = cs_press_iic_rw_test(tempdata); + if(ret < 0){ + LOG_ERR("iic_rw_test:err:%d\n",ret); + }else{ + LOG_ERR("iic_rw_test:ok:%d\n",ret); + } +exit_kfree: + kfree(kbuf); +exit: + return count; +} + +static ssize_t cs_proc_nav_mode_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset) +{ + char *kbuf = NULL; + int ret = 0; + const char *startpos = NULL; + char *firstc = NULL; + unsigned int tempdata = 0; + + if(count <= 0){ + LOG_ERR("argument err\n"); + goto exit; + } + kbuf = kzalloc(count, GFP_KERNEL ); + if (!kbuf) { + goto exit; + } + startpos = kbuf; + if (copy_from_user(kbuf, buf, count)) { + goto exit_kfree; + } + firstc = strstr(startpos, "0x"); + if (!firstc) { + LOG_ERR("param format invalid\n"); + goto exit_kfree; + } + firstc[4] = 0; + ret = kstrtouint(startpos, 0, &tempdata); + if (ret) { + LOG_ERR("param convert to int failed\n"); + goto exit_kfree; + } + + g_cs_press.nav_mode = tempdata; + LOG_INFO("navigation mode: %d\n", g_cs_press.nav_mode); + +exit_kfree: + kfree(kbuf); +exit: + return count; +} + +/** + * @brief set_debug_mode + * @param[in] reg addr, debug mode + * @retval 0: success, -1: fail + */ +int set_debug_mode(unsigned char addr, unsigned char data) +{ + int ret = 0; + int len = 2; + unsigned char reg_addr; + unsigned char reg_data[2]; + + reg_addr = addr; + len = 1; + reg_data[0] = data; + ret |= cs_press_iic_write(reg_addr, reg_data, len); + if (ret == 0) + { + LOG_DEBUG("reg = 0x%x, data = 0x%x, len = %d\n", + reg_addr, reg_data[0], len); + } + + reg_addr = DEBUG_READY_REG; + len = 1; + reg_data[0] = 0; /* clear ready num */ + ret |= cs_press_iic_write(reg_addr, reg_data, len); + if (ret == 0) { + LOG_DEBUG("reg = 0x%x, data = 0x%x, len = %d\n", + reg_addr, reg_data[0], len); + } + return ret; +} + +/** + * @brief get_debug_data_ready + * @param[in] reg addr + * @retval ready data num + */ +int get_debug_data_ready(unsigned char addr) +{ + int ret = 0; + int len = 1; + unsigned char reg_addr; + unsigned char reg_data[1]; + + reg_addr = addr; + len = 1; + reg_data[0] = 0; + ret |= cs_press_iic_read(reg_addr, reg_data, len); + if (ret == 0) { + LOG_DEBUG("reg = 0x%x, data = 0x%x, len = %d\n", reg_addr, reg_data[0], len); + }else{ + LOG_ERR("reg = 0x%x, data = 0x%x, len = %d, err\n", reg_addr, reg_data[0], len); + } + return (int)reg_data[0]; +} + +/** + * @brief get_debug_data + * @param[in] reg len + * @param[out] ready data + * @retval 0: success, -1: fail + */ +int get_debug_data(unsigned char addr, unsigned char* data, int len) +{ + int ret = 0; + unsigned char reg_addr; + unsigned char *reg_data; + int i; + + reg_data = (unsigned char *)kmalloc(len, GFP_KERNEL ); + if (NULL == reg_data) { + LOG_ERR("kmalloc fails. line : %d\n", __LINE__); + return -1; + } + memset(reg_data, 0, len); + reg_addr = addr; + reg_data[0] = 0; + ret = cs_press_iic_read(reg_addr, reg_data, len); + if (ret == 0) { + LOG_DEBUG("reg = 0x%x, data = 0x%x, len = %d, err\n", + reg_addr, reg_data[0], len); + } + for(i = 0 ; i < len ; i++) + data[i] = reg_data[i]; + + if (NULL != reg_data) { + kfree(reg_data); + } + return ret; +} + +/** + * @brief check calibration result + * @param[out] check result data buf + * @retval 0:running, -1: error, 1: success, 2: fail, 3: overtime + */ +static int cs_proc_get_rawdata_show(struct seq_file *m,void *v) +{ + int ret = 0; + int reg_len = 64; + unsigned char reg_addr; + unsigned char reg_data[64]; + int each_raw_size = 2; + int len = 16; + short raw_data[16]; + int i; + int retry = 50; + + cs_press_wakeup_iic(); + set_debug_mode(DEBUG_MODE_REG, DEBUG_RAW_MODE); + do { + msleep(5); + len = get_debug_data_ready((unsigned char)DEBUG_READY_REG); + if (len > 0) { + ret = get_debug_data(DEBUG_DATA_REG, reg_data, len); + seq_printf(m,"D0 : %d, D1 : %d, D2 : %d, len : %d\n", reg_data[0], reg_data[1], reg_data[2], len); + } + retry--; + } while ((len == 0) && retry > 0); + + if (len > reg_len) + { + len = reg_len; + } + if (len > 0) { + for (i = 0 ; i < CH_NUM ; i++) { + raw_data[i] = ((short)(reg_data[i * each_raw_size] & 0xff) + | (short)((reg_data[i * each_raw_size + 1] << 8) & 0xff00)); + seq_printf(m,"raw_data %d = %d\n", i, raw_data[i]); + } + seq_printf(m,"\n"); + } else { + seq_printf(m,"Data is not ready yet!\n"); + } + set_debug_mode(DEBUG_MODE_REG, DEBUG_CLEAR_MODE); + reg_addr = DEBUG_READY_REG; + len = 1; + reg_data[0] = 0x0; + cs_press_iic_write(reg_addr, reg_data, len); + return ret; +} + +static int cs_proc_get_rawdata_open(struct inode *inode, struct file *filp) +{ + if(filp == NULL){ + return -1; + } + return single_open(filp, cs_proc_get_rawdata_show, NULL); +} +/** + * @brief check calibration result + * @param[out] check result data buf + * @retval 0:running, -1: error, 1: success, 2: fail, 3: overtime + */ +static int cs_proc_get_forcedata_show(struct seq_file *m,void *v) +{ + int ret; + unsigned char reg_addr; + unsigned char reg_data[64]; + int each_raw_size = 2; + int len = 16; + short raw_data[16]; + int i; + int retry = 50; + + cs_press_wakeup_iic(); + + set_debug_mode(DEBUG_MODE_REG, DEBUG_FORCE_DATA_MODE); + + do { + msleep(5); + len = get_debug_data_ready((unsigned char)DEBUG_READY_REG); + if (len > 0) { + ret = get_debug_data(DEBUG_DATA_REG, reg_data, len); + seq_printf(m,"D0 : %d, D1 : %d, D2 : %d, len : %d\n", reg_data[0], reg_data[1], reg_data[2], len); + } + retry--; + } while (len == 0 && retry > 0); + + ret = 0; + if (len > 0) + { + for (i = 0 ; i < CH_NUM ; i++) + { + raw_data[i] = ((short)(reg_data[i * each_raw_size] & 0xff) + | ((short)(reg_data[i * each_raw_size + 1] & 0xff) << 8)); + + seq_printf(m, "diff_data:%05d ", raw_data[i]); + } + seq_printf(m, "\n"); + } else { + seq_printf(m, "Data is not ready yet!\n"); + } + set_debug_mode(DEBUG_MODE_REG, DEBUG_CLEAR_MODE); + reg_addr = DEBUG_READY_REG; + len = 1; + reg_data[0] = 0x0; + cs_press_iic_write(reg_addr, reg_data, len); + return 0; +} + +static int cs_proc_get_forcedata_open(struct inode *inode, struct file *filp) +{ + if(filp == NULL){ + LOG_ERR("filp is null\n"); + return -1; + } + return single_open(filp,cs_proc_get_forcedata_show,NULL); +} + + +static int cs_proc_calibration_param_show(struct seq_file *m,void *v) +{ + int ret = 0; + int read_coeffs[CH_NUM]; + int i; + unsigned char read_temp[FW_ONE_BLOCK_LENGTH_R] = {0}; + + cs_press_wakeup_iic(); + ret |= cs_press_iic_read(AP_RD_CAIL_REG, read_temp, CS_CALI_PARA_LENGTH); /* calibrate coef */ + + if (ret == 0) + { + for (i = 0 ; i < CH_NUM ; i++) + { + read_coeffs[i] = ((unsigned short)read_temp[i*2+0]<<8) | read_temp[i*2+1]; + seq_printf(m, "%05d ", read_coeffs[i]); + } + seq_printf(m, "\n"); + } else { + seq_printf(m, "read coefs fail!\n"); + } + return 0; +} + +static int cs_proc_calicoef_open(struct inode *inode, struct file *filp) +{ + if(filp == NULL){ + return -1; + } + return single_open(filp, cs_proc_calibration_param_show, NULL); +} + + +/** + * @brief check iic function + * @param + * @retval +*/ +int cs_check_i2c(void) +{ + int retry = 3; + unsigned char rbuf[2]; + unsigned char addr; + int len = 0; + + addr = 0x03; + len = 1; + rbuf[0] = 0; + rbuf[1] = 0; + + do { + if (cs_press_iic_rw_test(0x67) >= 0) + return 1; + msleep(50); + retry--; + LOG_INFO("read fw ver fail!retry:%d\n", retry); + } while (retry > 0); + + retry = 3; + do { + cs_press_reset_ic(); + msleep(300); + if (cs_press_iic_rw_test(0x67) >= 0) + return 1; + retry--; + LOG_INFO("reset fw fail!retry:%d\n", retry); + } while (retry > 0); + + return -1; +} + +/** + * @brief update function + * @param + * @retval +*/ + +static void __maybe_unused update_work_func(struct work_struct *worker) +{ + /* + int ret; + ret = cs_press_init(); + if (ret < 0){ + LOG_ERR("press init err\n"); + }else{ + LOG_DEBUG("press init ok\n"); + } + */ + cancel_delayed_work(&g_cs_press.update_worker); +} +/* +#ifdef I2C_CHECK_SCHEDULE + +static void i2c_check_func(struct work_struct *worker) +{ + int ret = 0; + ret = cs_check_i2c(); + if(ret != -1){ + schedule_delayed_work(&g_cs_press.i2c_check_worker, msecs_to_jiffies(CHECK_DELAY_TIME)); + LOG_ERR("i2c_check_func start,delay 20s.\n"); + } + +} +#endif +*/ + +/* procfs api*/ +static int cs_proc_fw_info_show(struct seq_file *m,void *v) +{ + char ret = 0; + + unsigned char read_temp[FW_ONE_BLOCK_LENGTH_R] = {0}; + cs_press_wakeup_iic(); + ret |= cs_press_iic_read(AP_VERSION_REG, read_temp, CS_FW_VERSION_LENGTH); /*FW Version*/ + if(ret==0) + { + seq_printf(m,"fw_ver: %d %d %d %d\n", + read_temp[0], read_temp[1], read_temp[2], read_temp[3]); + }else{ + seq_printf(m,"read fw info err\n"); + } + return 0; +} + +static int cs_proc_fw_info_open(struct inode *inode, struct file *filp) +{ + if(filp == NULL){ + + return -1; + } + return single_open(filp,cs_proc_fw_info_show,NULL); +} + +static ssize_t cs_proc_fw_file_update_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset) +{ + char *kbuf = NULL; + int err = 0; + + kbuf = kzalloc(count, GFP_KERNEL ); + if (!kbuf) { + goto exit; + } + if (copy_from_user(kbuf, buf, count)) { + goto exit_kfree; + } + + if(kbuf[0] == '2') + { + g_cs_press.update_type = FORCE_FILE_UPDATE; + }else + { + g_cs_press.update_type = HIGH_VER_FILE_UPDATE; + } + err = fml_fw_update_by_file(); + if (err == 0) + LOG_DEBUG("pass!\n"); + else + LOG_ERR("%d,failed!\n", err); + +exit_kfree: + kfree(kbuf); +exit: + return count; +} + +static int cs_read_boot_version_show(struct seq_file *m, void *v) +{ + char result = 0; + unsigned char boot_ver_buf[4]; + + result = cs_read_boot_version(boot_ver_buf); + if (result == 0){ + seq_printf(m,"%02X %02X %02X %02X\n", boot_ver_buf[0],boot_ver_buf[1],boot_ver_buf[2],boot_ver_buf[3]); + }else{ + seq_printf(m,"ERR\n"); + } + return 0; +} + +static int cs_proc_read_boot_version_open(struct inode *inode, struct file *filp) +{ + return single_open(filp,cs_read_boot_version_show,NULL); +} + + +static int cs_proc_show(struct seq_file *m,void *v) +{ + return 0; +} + +static int cs_proc_open(struct inode *inode, struct file *filp) +{ + return single_open(filp,cs_proc_show,NULL); +} + +static ssize_t cs_proc_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset) +{ + char *kbuf = NULL; + + kbuf = kzalloc(count, GFP_KERNEL ); + if (!kbuf) { + goto exit; + } + if (copy_from_user(kbuf, buf, count)) { + goto exit_kfree; + } +exit_kfree: + kfree(kbuf); +exit: + return count; +} + +/** + * @brief dts parse + * @param + * @retval +*/ +int cs_parse_dts(struct i2c_client *pdev) +{ + int ret = -1; + struct regulator *power_3v3 = NULL; + const char *name_tmp; + +#ifdef INT_SET_EN + + cs_press_irq_gpio = of_get_named_gpio((pdev->dev).of_node,"irq-gpio",0); + if(!gpio_is_valid(cs_press_irq_gpio)) + { + dev_err(&pdev->dev, "cs_press request_irq IRQ fail"); + } + else + { + ret = gpio_request(cs_press_irq_gpio, "irq-gpio"); + if(ret) + { + dev_err(&pdev->dev, "cspress request_irq IRQ fail !,ret=%d.\n", ret); + } + ret = gpio_direction_input(cs_press_irq_gpio); + msleep(50); + cs_press_irq_num = gpio_to_irq(cs_press_irq_gpio); + ret = request_irq(cs_press_irq_num, + (irq_handler_t)cs_press_interrupt_handler, + IRQF_TRIGGER_FALLING |IRQF_TRIGGER_RISING |IRQF_ONESHOT, + "CS_PRESS-eint", g_cs_press.device_irq); + if (ret > 0) { + ret = -1; + dev_err(&pdev->dev, "cs_press request_irq IRQ fail !,ret=%d.\n", ret); + } + } +#endif + g_cs_press.rst_gpio = of_get_named_gpio((pdev->dev).of_node, "rst-gpio", 0); + if(!gpio_is_valid(g_cs_press.rst_gpio)) + { + dev_err(&pdev->dev, "cs_press rst-gpio fail"); + } else { + ret = gpio_request(g_cs_press.rst_gpio, "rst-gpio"); + if(ret) + { + dev_err(&pdev->dev, "cs_press request rst fail !,ret=%d.\n", ret); + } + ret = gpio_direction_output(g_cs_press.rst_gpio,0); + msleep(50); + gpio_set_value(g_cs_press.rst_gpio, 0); + } + + g_cs_press.power_gpio = of_get_named_gpio((pdev->dev).of_node, "power-gpio", 0); + if(!gpio_is_valid(g_cs_press.power_gpio)) + { + dev_err(&pdev->dev, "cs_press power-gpio fail"); + } else { + ret = gpio_request(g_cs_press.power_gpio, "power-gpio"); + if(ret) + { + dev_err(&pdev->dev, "cs_press request power-gpio fail !,ret=%d.\n", ret); + } + ret = gpio_direction_output(g_cs_press.power_gpio,0); + msleep(50); + gpio_set_value(g_cs_press.power_gpio, 1); + } + + power_3v3 = regulator_get(&pdev->dev, "vdd_3v3"); + if (power_3v3 != NULL) { + dev_err(&pdev->dev,"%s:power_3v3 is not NULL\n", __func__); + ret = regulator_enable(power_3v3); + if (ret) + dev_err(&pdev->dev,"%s: power_3v3 enable fail\n", __func__); + } else { + dev_err(&pdev->dev,"%s: power_3v3 is NULL\n", __func__); + } + + ret = of_property_read_u32((pdev->dev).of_node, "cs,panel-max-x", + &g_cs_press.panel_max_x); + if (ret) { + LOG_ERR("failed get panel-max-x\n"); + return ret; + } + + ret = of_property_read_u32((pdev->dev).of_node, "cs,panel-max-y", + &g_cs_press.panel_max_y); + if (ret) { + LOG_ERR("failed get panel-max-y\n"); + return ret; + } + + ret = of_property_read_u32((pdev->dev).of_node, "cs,panel-max-w", + &g_cs_press.panel_max_w); + if (ret) { + LOG_ERR("failed get panel-max-w\n"); + return ret; + } + + ret = of_property_read_string((pdev->dev).of_node, "cs,fw-name", &name_tmp); + if (!ret) { + strncpy(g_cs_press.cs_fw_name, name_tmp, sizeof(g_cs_press.cs_fw_name)); + LOG_INFO("firmware name from dt: %s", g_cs_press.cs_fw_name); + } else { + strncpy(g_cs_press.cs_fw_name, FW_FILE_NAME, sizeof(g_cs_press.cs_fw_name)); + LOG_INFO("can't find firmware name, use default: %s", g_cs_press.cs_fw_name); + } + + LOG_ERR("end---\n"); + return 0; +} +/** + * @brief free resource from dts info + * @param + * @retval 0:success, -1: fail + */ +void cs_unregister_dts(void){ + /*GPIO unregister*/ + if(gpio_is_valid(g_cs_press.rst_gpio)){ + gpio_free(g_cs_press.rst_gpio); + LOG_DEBUG("reset gpio free\n"); + } + #ifdef INT_SET_EN + if(gpio_is_valid(cs_press_irq_gpio)){ + free_irq(cs_press_irq_num,g_cs_press.device_irq); + gpio_free(cs_press_irq_gpio); + LOG_DEBUG("irq gpio free\n"); + g_cs_press.device_irq = NULL; + } + #endif +} + +static const char proc_list[PROC_FOPS_NUM][PROC_NAME_LEN]={ + "fw_info", + "read_boot_version", + "fw_update_file", + "rw_reg", + "cs_iic_test", + "read_rawdata", + "read_cali_coef", + "read_force_data", + "nav_mode", +}; + +#ifndef KERNEL_4_1_5 +static const struct proc_ops proc_fops[PROC_FOPS_NUM] = { +#else +static const struct file_operations proc_fops[PROC_FOPS_NUM] = { +#endif + + FOPS_ARRAY(cs_proc_fw_info_open, NULL), /*fw_info*/ + FOPS_ARRAY(cs_proc_read_boot_version_open, cs_proc_write),/*read boot version*/ + FOPS_ARRAY(cs_proc_open, cs_proc_fw_file_update_write), /*update*/ + FOPS_ARRAY(cs_proc_rw_reg_open, cs_proc_rw_reg_write), /*rw_reg*/ + FOPS_ARRAY(cs_proc_open, cs_proc_iic_rw_test_write), /*IIC read write test*/ + FOPS_ARRAY(cs_proc_get_rawdata_open, NULL), + FOPS_ARRAY(cs_proc_calicoef_open, NULL), + FOPS_ARRAY(cs_proc_get_forcedata_open, NULL), + FOPS_ARRAY(cs_proc_open, cs_proc_nav_mode_write), +}; +/** + * @brief cs_sys_create + * @param + * @retval +*/ +static int cs_procfs_create(void) +{ + int ret = 0; + int i = 0; + struct proc_dir_entry *file; + + g_cs_press.p_proc_dir = proc_mkdir(CS_PRESS_NAME, NULL); + if(g_cs_press.p_proc_dir == NULL){ + ret = -1; + goto exit; + } + for(i = 0;i < PROC_FOPS_NUM;i++) + { + file = proc_create_data(proc_list[i], 0666, g_cs_press.p_proc_dir, &proc_fops[i], NULL); + /*file = proc_create(proc_list[i],0644,NULL,&proc_fops[i]);*/ + if(file == NULL){ + ret = -1; + LOG_ERR("proc %s create failed",proc_list[i]); + goto err_flag; + } + } + return 0; +err_flag: + remove_proc_entry(CS_PRESS_NAME,NULL); +exit: + return ret; +} + +static void cs_procfs_delete(void) +{ + int i = 0; + + for(i = 0;i < PROC_FOPS_NUM;i++) + { + remove_proc_entry(proc_list[i], g_cs_press.p_proc_dir); + LOG_DEBUG("proc %s is removed", proc_list[i]); + } + remove_proc_entry(CS_PRESS_NAME,NULL); +} + +/********sysfs node start*********/ +/** + * @brief show navigation mode + * @param[out] current nav mode type + * @retval + */ +static ssize_t cs_sysfs_nav_mode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "navigation mode: %u\n", g_cs_press.nav_mode); +} + +/** + * @brief switch navigation mode + * @param[in] nav mode type 2:zoom, 3:scroll + * @retval + */ +static ssize_t cs_sysfs_nav_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + unsigned long value = 0; + int err = 0; + + err = kstrtoul(buf, 10, &value); + if (err < 0) { + LOG_INFO("Failed to convert value"); + return -EINVAL; + } + + g_cs_press.nav_mode = value; + LOG_INFO("navigation mode: %d\n", g_cs_press.nav_mode); + + return size; +} +static DEVICE_ATTR(nav_mode, (S_IWUSR | S_IWGRP | S_IRUGO), cs_sysfs_nav_mode_show, + cs_sysfs_nav_mode_store); + +/** + * @brief upgrade IC firmware manually + * @param[in] upgrade type 2:force file update, others:high version update + * @retval + */ +static ssize_t cs_sysfs_fw_update_file_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + unsigned long value = 0; + int err = 0; + + err = kstrtoul(buf, 10, &value); + if (err < 0) { + LOG_INFO("Failed to convert value"); + return -EINVAL; + } + + if(value == 2) { + g_cs_press.update_type = FORCE_FILE_UPDATE; + } else { + g_cs_press.update_type = HIGH_VER_FILE_UPDATE; + } + err = fml_fw_update_by_file(); + if (err == 0) + LOG_DEBUG("pass!\n"); + else + LOG_ERR("%d,failed!\n", err); + + return size; +} +static DEVICE_ATTR(fw_update_file, (S_IWUSR | S_IWGRP), NULL, cs_sysfs_fw_update_file_store); + +/** + * @brief read firmware bootloader info from IC + * @param[out] check result data buf + * @retval firmware bootloader info + */ +static ssize_t cs_sysfs_read_boot_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + char result = 0; + unsigned char boot_ver_buf[4]; + + result = cs_read_boot_version(boot_ver_buf); + if (result == 0){ + return scnprintf(buf, PAGE_SIZE, "%02X %02X %02X %02X\n", + boot_ver_buf[0],boot_ver_buf[1],boot_ver_buf[2],boot_ver_buf[3]); + } else { + return scnprintf(buf, PAGE_SIZE, "ERR\n"); + } +} +static DEVICE_ATTR(read_boot_version, S_IRUGO, cs_sysfs_read_boot_version_show, NULL); + +/** + * @brief read firmware info from IC + * @param[out] check result data buf + * @retval firmware info + */ +static ssize_t cs_sysfs_fw_info_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + char ret = 0; + unsigned char read_temp[FW_ONE_BLOCK_LENGTH_R] = {0}; + + cs_press_wakeup_iic(); + ret |= cs_press_iic_read(AP_VERSION_REG, read_temp, CS_FW_VERSION_LENGTH); /*FW Version*/ + if(ret==0) + { + return scnprintf(buf, PAGE_SIZE, "fw_ver: %d %d %d %d\n", + read_temp[0], read_temp[1], read_temp[2], read_temp[3]); + } else { + return scnprintf(buf, PAGE_SIZE, "read fw info err\n"); + } +} +static DEVICE_ATTR(fw_info, S_IRUGO, cs_sysfs_fw_info_show, NULL); + +static struct attribute *cs_press_attributes[] = { + &dev_attr_fw_info.attr, + &dev_attr_read_boot_version.attr, + &dev_attr_fw_update_file.attr, + &dev_attr_nav_mode.attr, + NULL +}; + +static struct attribute_group cs_press_attribute_group = { + .attrs = cs_press_attributes +}; +/********sysfs node end*********/ + +/********misc node for ndt*********/ +static int cs_press_open(struct inode *inode, struct file *filp) +{ + LOG_DEBUG("cs_press_open\n"); + if(g_cs_press.client == NULL) + { + return -1; + } + return 0; +} + +static ssize_t cs_press_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset) +{ + int err; + char *kbuf = NULL; + char reg; + + kbuf = kzalloc(count, GFP_KERNEL ); + if (!kbuf) + { + err = -ENOMEM; + goto exit; + } + + if (copy_from_user(®, buf, 1) || copy_from_user(kbuf, buf+1, count)) + { + err = -EFAULT; + goto exit_kfree; + } + err = cs_press_iic_write(reg, kbuf, count); + if(err >= 0) + { + err = 1; + } + + exit_kfree: + kfree(kbuf); + exit: + return err; +} + +static ssize_t cs_press_read(struct file *filp, char __user *buf, size_t count, loff_t *off) +{ + int err = 0; + char *kbuf = NULL; + char reg = 0; + + kbuf = kzalloc(count, GFP_KERNEL ); + if (!kbuf) { + err = -ENOMEM; + goto exit; + } + /*get reg addr buf[0]*/ + if (copy_from_user(®, buf, 1)) { + err = -EFAULT; + goto exit_kfree; + } + err = cs_press_iic_read(reg, kbuf, count); + if (err < 0) + goto exit_kfree; + if (copy_to_user(buf+1, kbuf, count)) + { + err = -EFAULT; + }else{ + err = 1; + } + exit_kfree: + kfree(kbuf); + exit: + return err; +} + +static int cs_press_release(struct inode *inode, struct file *filp) +{ + return 0; +} +/* + * @ misc device file operation + */ +static const struct file_operations cs_press_fops = { + .owner = THIS_MODULE, + .open = cs_press_open, + .write = cs_press_write, + .read = cs_press_read, + .release = cs_press_release, +}; + +static struct miscdevice cs_press_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = MISC_DEVICE_NAME, + .fops = &cs_press_fops, +}; + +static int cs_press_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = -1; + struct device *parent = &client->dev; + + LOG_DEBUG("probe init\n"); + ret = misc_register(&cs_press_misc); /*dev node*/ + g_cs_press.client = client; + cs_parse_dts(g_cs_press.client); + cs_press_struct_init(); + cs_procfs_create(); /*proc node*/ +#ifdef INT_SET_EN + eint_init(); +#endif + + if (kfifo_alloc(&g_cs_press.data_queue, + CS_DATA_MAX_QUEUE * sizeof(struct cs_press_coords), GFP_KERNEL)) { + LOG_ERR("failed to alloc queue for raw data\n"); + goto ALLOC_KFIFO_FAILED; + } + + dev_set_drvdata(&client->dev, &g_cs_press); + //create sysfs node + ret = alloc_chrdev_region(&g_cs_press.class_dev_no, 0, 1, CS_SYSFS_NAME); + if (ret < 0) { + LOG_ERR("get device number failed\n"); + goto GET_NEW_MINOR_FAILED; + } + + g_cs_press.class_dev = device_create(cs_press_class, parent, g_cs_press.class_dev_no, + &g_cs_press, "%s", CS_SYSFS_NAME); + + ret = sysfs_create_group(&g_cs_press.class_dev->kobj, &cs_press_attribute_group); + if (ret < 0) { + LOG_ERR("error creating sysfs attr files"); + goto CLASS_DEVICE_ATTR_CREATE_FAILED; + } + + // INIT_DELAYED_WORK(&g_cs_press.update_worker, update_work_func); + // schedule_delayed_work(&g_cs_press.update_worker, msecs_to_jiffies(2000)); + // LOG_ERR("update_work_func start,delay 2s.\n"); + +/* +#ifdef I2C_CHECK_SCHEDULE + INIT_DELAYED_WORK(&g_cs_press.i2c_check_worker, i2c_check_func); + schedule_delayed_work(&g_cs_press.i2c_check_worker, msecs_to_jiffies(CHECK_DELAY_TIME)); + LOG_ERR("i2c_check_func start,delay 20s.\n"); +#endif +*/ + LOG_ERR("end!\n"); + return 0; + +CLASS_DEVICE_ATTR_CREATE_FAILED: + unregister_chrdev_region(g_cs_press.class_dev_no, 1); + +GET_NEW_MINOR_FAILED: + kfifo_free(&g_cs_press.data_queue); + +ALLOC_KFIFO_FAILED: + cs_unregister_dts(); + cs_procfs_delete(); +#ifdef INT_SET_EN + eint_exit(); +#endif + misc_deregister(&cs_press_misc); + g_cs_press.client = NULL; + + return ret; +} + +static int cs_press_remove(struct i2c_client *client) +{ + /* delete device */ + if(g_cs_press.client == NULL){ + return 0; + } + LOG_DEBUG("cs_remove\n"); + kfifo_free(&g_cs_press.data_queue); + sysfs_remove_group(&g_cs_press.class_dev->kobj, &cs_press_attribute_group); + unregister_chrdev_region(g_cs_press.class_dev_no, 1); + cs_unregister_dts(); + cs_procfs_delete(); +#ifdef INT_SET_EN + eint_exit(); +#endif + misc_deregister(&cs_press_misc); + g_cs_press.client = NULL; + return 0; +} + +/* + * @traditional match list,use thie when not using dts + */ +static const struct i2c_device_id cs_press_id[] = { + {CS_PRESS_NAME, 0}, + {/*northing to be done*/}, +}; + +/* + * @dts match list + */ +static const struct of_device_id cs_press_match[] = { + { .compatible = "chipsea,cs_press" }, + {/*northing to be done*/}, +}; + +/** + * @Support fast loading of hot swap devices + */ +MODULE_DEVICE_TABLE(i2c, cs_press_id); + +/*i2c driver struct*/ +static struct i2c_driver cs_press_driver = { + .probe = cs_press_probe, + .remove = cs_press_remove, + .driver = { + .owner = THIS_MODULE, + .name = CS_PRESS_NAME, + .of_match_table = of_match_ptr(cs_press_match),/*if use dts,use of_match_table to match*/ + }, + .id_table = cs_press_id, +}; + +static int __init cs_press_init(void) +{ + int ret = 0; + + cs_press_class = class_create(THIS_MODULE, "g_cs_press"); + if (IS_ERR(cs_press_class)) + { + LOG_ERR("cs press class init failed"); + return PTR_ERR(cs_press_class); + } + + ret = i2c_add_driver(&cs_press_driver); + return ret; +} + +static void __exit cs_press_exit(void) +{ + LOG_DEBUG("module exit\n"); + i2c_del_driver(&cs_press_driver); + cs_procfs_delete(); + if(cs_press_class) { + class_destroy(cs_press_class); + } +} + +module_init(cs_press_init); +module_exit(cs_press_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/cs_press_mmi/cs_press.h b/drivers/input/misc/cs_press_mmi/cs_press.h new file mode 100644 index 000000000000..cad9e295bc5d --- /dev/null +++ b/drivers/input/misc/cs_press_mmi/cs_press.h @@ -0,0 +1,355 @@ +/* + * Driver for CHIPSEA forcetouch sensor + * Copyright (C) 2023 CHIPSEA + * + * 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. + * + * Release Information : CSA37F71 chip forcetouch fw driver head file + * version : v0.2 + */ + +#ifndef _CS_PRESS_H_ +#define _CS_PRESS_H_ + +#define CS_I2C_ADDR 0x50 /* (0xA0>>1) */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef CONFIG_MTK_I2C_EXTENSION +#include +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) +#include +#endif + +#define FW_FILE_NAME "cs_press.nfw" +#define CS_MAX_STR_LABEL_LEN 64 + +/* +*@ LOG format +*/ +#define LOG_ERR(fmt, args...) pr_err("[cs_press][ERR] [%s: %d] "fmt, __func__, __LINE__, ##args) +#define LOG_INFO(fmt, args...) pr_err("[cs_press][INFO] [%s: %d] "fmt, __func__, __LINE__, ##args) +#define LOG_DEBUG(fmt, args...) pr_err("[cs_press][DEBUG] [%s: %d] "fmt, __func__, __LINE__, ##args) + +#define RSTPIN_RESET_ENABLE 1 +#define CS_PRESS_CNT 1 + +#define RETRY_NUM 3 +#define DEBUG_MODE_DELAY_TIME 20 +#define CS_MANUFACTURER_ID_LENGTH 2 +#define CS_MODULE_ID_LENGTH 2 +#define CS_FW_VERSION_LENGTH 6 +#define CS_CALI_PARA_LENGTH 8 +#define RST_GPIO_HIGH 1 +#define RST_GPIO_LOW 0 +#define POWER_GPIO_HIGH 1 +#define POWER_GPIO_LOW 0 +#define IIC_KEY_EVENT 0xD3 +#define IIC_KEY_EVENT_MMI 0xD4 +#define CS_FORCE_TRIG_LENGTH 6 +#define CS_SCANT_PERIOD_LENGTH 4 +#define CS_WORK_MODE_LENGTH 3 +#define CS_SHELL_TEMP_LENGTH 4 + +#define CS_PRESS_DEV_ADDR_M 0xE4 +#define CS_PRESS_DEV_ADDR_F 0xA0 +#define CS_PRESS_DEV_ADDR_HID 0x2A + +#define CH_NUM (4) + +#define BOOT_FW_WRITE_CMD {0xaa,0x55,0xa5,0x5a} +#define BOOT_FW_JUMP_CMD {0xA6,0x00,0x00,0x5A} +#define BOOT_FW_WFLAG_CMD {0x50,0x41,0x53,0x73} +/*reg addr*/ +/*ndt reg*/ +#define AP_RW_TEST_REG 0x00 /*R/W read write test len = 10*/ +#define AP_VERSION_REG 0x10 /*R firmware infomation len = 6*/ +#define AP_RD_CAIL_REG 0x24 /*R len = 8*/ +#define AP_RD_SONSOR_SATUS_REG 0x29 /*R len = 2*/ +#define AP_RD_GPIO_STATUS_REG 0x2E /*R gpio status len = 6*/ + +#define AP_RD_TP_INFO_REG 0x52 + +#define AP_RD_APPLICATION_SATUS_REG 0x55 /*R application status len = 1*/ +#define AP_RW_BUTTON_FORCE_RELEASE_THR_REG 0x5B /*R/W button force triggering/releasing threshold value len = 8*/ +#define AP_RW_BUTTON_FORCE_THR_REG 0x5D /*R/W button force triggeringthreshold value len = 6*/ +#define AP_RW_SCANT_PERIOD_REG 0x5E /*R/W scant period value len = 4*/ +#define AP_RW_WORK_MODE_REG 0x5F /*R/W work mode len = 4*/ +#define AP_RW_SHEll_TEMP_REG 0x61 /*R/W shell temp len = 4*/ + +#define AP_W_GPIO_TEST_REG 0x30 /*W io test len = 2*/ + +#define DEBUG_CLEAR_MODE 0x00 +#define DEBUG_RAW_MODE 0x10 /*rawdata*/ +#define DEBUG_FORCE_DATA_MODE 0x20 /*forcedata*/ +#define DEBUG_WRITE_COEF 0x30 +#define DEBUG_READ_COEF 0x31 +#define DEBUG_WRITE_THRESHOLD 0x32 +#define DEBUG_READ_THRESHOLD 0x33 + +#define DEBUG_MODE_REG 0xF6 +#define DEBUG_READY_REG 0xF7 +#define DEBUG_DATA_REG 0xF8 + +#define AP_RESET_MCU_REG 0x01 +#define AP_DEVICE_ID_REG 0x02 +#define AP_MANUFACTURER_ID_REG 0x03 +#define AP_MODULE_ID_REG 0x04 + +#define AP_WAKEUP_REG 0x06 +#define AP_SLEEP_REG 0x07 +#define AP_CALIBRATION_REG 0x1c +#define AP_WATCH_MODE_REG 0x1d + +#define AP_FORCEDATA_REG 0x20 + +#define BOOT_CMD_REG 0x0000 +#define BOOT_RESET_REG 0xF17C +#define BOOT_RESET_REG_HID 0x2010 + +/*updata info*/ +#define FW_ADDR_CODE_LENGTH 0x0e +#define FW_ADDR_VERSION 0x0A +#define FW_ADDR_CODE_START 0x100 +#define FW_ONE_BLOCK_LENGTH_W 128 +#define FW_ONE_BLOCK_LENGTH_R 256 +#define BOOT_CMD_LENGTH 4 +#define FW_UPDATA_MAX_LEN (64*1024) + +/*mode config*/ +#define AP_W_CAL_FACTOR_DEBUG_MODE 0x30 +#define AP_R_CAL_FACTOR_DEBUG_MODE 0x31 + +#define AP_W_PRESS_LEVEL_DEBUG_MODE 0x32 +#define AP_R_PRESS_LEVEL_DEBUG_MODE 0x33 + +#define AP_CALIBRATION_DEBUG_MODE 0x34 +#define AP_R_SENSOR_STATUS_DEBUG_MODE 0x36 +#define AP_R_OFFSET_DEBUG_MODE 0x37 + +#define AP_R_RAWDATA_DEBUG_MODE 0x01 + +#define AP_R_NOISE_DEBUG_MODE 0x11 +#define AP_R_PROCESSED_DEBUG_MODE 0x14 +#define AP_R_NDT_DEBUG_MODE 0xF8 + +/*linux driver info*/ +#define CS_PRESS_NAME "cs_press" +#define MISC_DEVICE_NAME "ndt" +#define CS_SYSFS_NAME "cs_press" + +#define SHELL_TEMP_DELAY_TIME (2000) +#define CHECK_DELAY_TIME (20000) +#define I2C_CHECK_SCHEDULE +#define INT_SET_EN +//#define SIDE_KEY +//#define SIDE_KEY_MMI +#define CALIBRATION_SUCCESS_FALG 0xf0 +#define CALIBRATION_FAIL_FALG 0xf2 +#define CALIBRATION_OVERTIME_FALG 0xff + +#define AFE_MAX_CH 8 +#define AFE_USE_CH 1 +#define KEY_NUM 1 + +#define HIGH_VER_FILE_UPDATE 0 +#define FORCE_FILE_UPDATE 1 + +#define PRESS_GEAR_LEVEL_LOW 1 +#define PRESS_GEAR_LEVEL_MID 2 +#define PRESS_GEAR_LEVEL_HIGH 3 + +#define SCENE_NUM 10 +#define PRESS_NUM 10 + +#define NAME_MAX_LENS 256 +#define MIN(A, B) ((A) < (B) ? (A) : (B)) + +#define DEFAULT_RUN_DELAY_TIME 50 + + +#define IRQ_TYPE_EDGE_FALLING 0 +#define IRQ_TYPE_EDGE_RISING 1 + +#define CS_DATA_MAX_QUEUE 200 + +#define DATA_MODE_REG 0xF1 +#define DATA_READY_REG 0xF2 +#define DATA_READ_REG 0xF3 + +#define CS_MAX_TOUCH 10 + +#define CS_INPUT_PHYS "cs_press/input0" + +typedef struct{ + unsigned short manufacturer_id; + unsigned short module_id; + unsigned short fw_version; +}CS_FW_INFO_Def; + +typedef struct{ + short rawdata[AFE_MAX_CH]; +}CS_RAWDATA_Def; + +typedef struct{ + short arith_rawdata[AFE_USE_CH]; + short baseline[AFE_USE_CH]; + short diffdata[AFE_USE_CH]; + short energy_data[AFE_USE_CH]; +}CS_PROCESSED_DATA_Def; + +typedef struct{ + short noise_peak[AFE_USE_CH]; + unsigned int noise_std[AFE_USE_CH]; +}CS_NOISE_DATA_Def; + +typedef struct{ + short offset[AFE_USE_CH]; +}CS_OFFSET_DATA_Def; + +typedef struct{ + short status[AFE_USE_CH]; +}CS_SENSOR_STATUS_Def; + +typedef struct{ + unsigned char calibration_channel; + unsigned char calibration_progress; + unsigned short calibration_factor; + short press_adc_1st; + short press_adc_2nd; + short press_adc_3rd; +}CS_CALIBRATION_RESULT_Def; + +struct cs_press_coords { + int coord; + int pressure; +}; + +typedef enum { + CS_NAV_MODE_NONE = 0, + CS_NAV_MODE_VOLUME, + CS_NAV_MODE_ZOOM, + CS_NAV_MODE_SCROLL, + CS_NAV_MODE_ROLL, + CS_NAV_MODE_MAX, +} cs_nav_mode_t; + +enum { + CS_NAV_MODE_SCROLL_UP = 0, + CS_NAV_MODE_SCROLL_DOWN, + CS_NAV_MODE_ZOOM_IN, + CS_NAV_MODE_ZOOM_OUT, +}; + +struct cs_vtp_scroll { + int x0; + int y0; + int range; + int step; + int interval; +}; + +struct cs_vtp_zoom{ + int x0; + int p1_y0; + int p2_y0; + int range; + int step; + int interval; +}; + +struct cs_press_t { + int major; + struct class *class; + struct device *device_irq; + struct i2c_client *client; + struct kobject *kobj; + struct device *class_dev; + dev_t class_dev_no; + + /*GPIO pin*/ + int rst_gpio; + int power_gpio; + + struct proc_dir_entry *p_proc_dir; + + unsigned short read_reg_len; + unsigned char read_reg_data[64]; + + unsigned char update_type; + unsigned char updating_flag; + + +#ifdef I2C_CHECK_SCHEDULE + struct delayed_work i2c_check_worker; +#endif + struct delayed_work update_worker; + + struct delayed_work delay_run_worker; + + struct kfifo data_queue; + struct delayed_work work; + unsigned int panel_max_x; + unsigned int panel_max_y; + unsigned int panel_max_w; /*major and minor*/ + unsigned int nav_mode; + char cs_fw_name[CS_MAX_STR_LABEL_LEN]; +}; + +#endif diff --git a/drivers/input/misc/ets_bix_mmi/Android.mk b/drivers/input/misc/ets_bix_mmi/Android.mk new file mode 100644 index 000000000000..8391813c45e5 --- /dev/null +++ b/drivers/input/misc/ets_bix_mmi/Android.mk @@ -0,0 +1,23 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +ETS_FPS_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +ETS_FPS_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +ifeq ($(EGIS_FPS_PANEL_NOTIFICATIONS),true) + KERNEL_CFLAGS += CONFIG_EGIS_PANEL_NOTIFICATIONS=y +endif + +ifeq ($(EGIS_FPS_DRM_PANEL_NOTIFICATIONS),true) + KERNEL_CFLAGS += CONFIG_EGIS_DRM_PANEL_NOTIFICATIONS=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := ets_bix_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(ETS_FPS_MMI_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/misc/ets_bix_mmi/Kbuild b/drivers/input/misc/ets_bix_mmi/Kbuild new file mode 100644 index 000000000000..de3c7a00bb43 --- /dev/null +++ b/drivers/input/misc/ets_bix_mmi/Kbuild @@ -0,0 +1,21 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/ets_bix_mmi + +ifneq ($(filter m y,$(CONFIG_EGIS_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_PANEL_NOTIFICATIONS +endif + +ifneq ($(filter m y,$(CONFIG_EGIS_DRM_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_NOTIFICATIONS +endif +#ifneq ($(filter m y,$(CONFIG_ET721_FOD)),) +# EXTRA_CFLAGS += -DET721_FOD +#endif + +ets_bix_mmi-y += etxxx_fp.o + + +obj-m += ets_bix_mmi.o + diff --git a/drivers/input/misc/ets_bix_mmi/Makefile b/drivers/input/misc/ets_bix_mmi/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/input/misc/ets_bix_mmi/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/input/misc/ets_bix_mmi/etxxx_fp.c b/drivers/input/misc/ets_bix_mmi/etxxx_fp.c new file mode 100755 index 000000000000..1384c113b690 --- /dev/null +++ b/drivers/input/misc/ets_bix_mmi/etxxx_fp.c @@ -0,0 +1,1569 @@ +/* + * Simple synchronous userspace interface to SPI devices + * + * Copyright (C) 2006 SWAPP + * Andrea Paterniani + * Copyright (C) 2007 David Brownell (simplification, cleanup) + * + * 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. + * + */ + +/* +* +* etxxx_fp.c +* Date: 2016/03/16 +* Version: 0.9.0.1 +* Revise Date: 2020/11/25 +* Copyright (C) 2007-2019 Egis Technology Inc. +* ----------------- version history ------------------------ +* +*Kevin.Liang 20181102 add powersetup for IOC +*Jacob.Kung 20201125 modify for support all sensor +*Jacob.Kung 20201126 modify struct define naming +*Jacob.Kung 20201130 add check_ioctl_permission +*Jacob.Kung 20201207 register platform driver with MTK platform +*Jacob.Kung 20210331 add register FB notifier +*Jacob.Kung 20210512 add register DRM notifier +* ----------------------------------------------------------- +* +**/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "etxxx_fp.h" +#include +#if IS_ENABLED(CONFIG_QCOM_PANEL_EVENT_NOTIFIER) +#define SET_QCOM_PANEL_EVENT_NOTIFIER +#endif + +#if defined(CONFIG_PANEL_NOTIFICATIONS) +#include +#define FP_NOTIFY_ON PANEL_EVENT_DISPLAY_ON +#define FP_NOTIFY_OFF PANEL_EVENT_DISPLAY_OFF +#define FP_NOTIFY_EVENT_BLANK PANEL_EVENT_DISPLAY_OFF //MSM_DRM_EVENT_BLANK +#define ets_fb_register_client(client) panel_register_notifier(client); +#define ets_fb_unregister_client(client) panel_unregister_notifier(client); +#elif defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +#include +#elif defined(SET_QCOM_PANEL_EVENT_NOTIFIER) +#include +#elif defined(CONFIG_FB) +#include +#define FP_NOTIFY_ON FB_BLANK_UNBLANK +#define FP_NOTIFY_OFF FB_BLANK_POWERDOWN +#define FP_NOTIFY_EVENT_BLANK FB_EVENT_BLANK +#define ets_fb_register_client(client) fb_register_client(client); +#define ets_fb_unregister_client(client) fb_unregister_client(client) +#else +#include +#define FP_NOTIFY_ON MSM_DRM_BLANK_UNBLANK +#define FP_NOTIFY_OFF MSM_DRM_BLANK_POWERDOWN +#define FP_NOTIFY_EVENT_BLANK MSM_DRM_EARLY_EVENT_BLANK +#define ets_fb_register_client(client) msm_drm_register_client(client); +#define ets_fb_unregister_client(client) msm_drm_unregister_client(client) +#endif + + + +struct egisfp_dev_t *g_data = NULL; +static int waitq_type = 0; +static int screenonoff_flag = 0; + +DECLARE_BITMAP(minors, N_SPI_MINORS); +LIST_HEAD(device_list); +DEFINE_MUTEX(device_list_lock); + +static struct egis_key_map_t key_maps[] = { + {EV_KEY, EGIS_NAV_INPUT_UP}, + {EV_KEY, EGIS_NAV_INPUT_DOWN}, + {EV_KEY, EGIS_NAV_INPUT_LEFT}, + {EV_KEY, EGIS_NAV_INPUT_RIGHT}, + {EV_KEY, EGIS_NAV_INPUT_CLICK}, + {EV_KEY, EGIS_NAV_INPUT_DOUBLE_CLICK}, + {EV_KEY, EGIS_NAV_INPUT_LONG_PRESS}, + {EV_KEY, EGIS_NAV_INPUT_FINGER_DOWN}, + {EV_KEY, EGIS_NAV_INPUT_FINGER_UP}, +}; + +int egisfp_probe(struct platform_device *pdev); +int egisfp_remove(struct platform_device *pdev); + +/* -------------------------------------------------------------------- */ + +struct of_device_id egistec_match_table[] = { + { + .compatible = "fp-egistec", + }, + {}, +}; + +static struct platform_driver egisfp_driver = { + .driver = { + .name = EGIS_DEV_NAME, + .owner = THIS_MODULE, + .of_match_table = egistec_match_table, + }, + .probe = egisfp_probe, + .remove = egisfp_remove, +}; + +/* add for clk enable from kernel*/ + + +/* add for clk enable from kernel*/ + +/* ------------------------------ Interrupt -----------------------------*/ + +void egisfp_interrupt_enable(struct egisfp_dev_t *egis_dev, int enable_mode) +{ + unsigned long irqflags; + if (egis_dev->request_irq_done == 0) + { + return; + } + spin_lock_irqsave(&egis_dev->irq_lock, irqflags); + DEBUG_PRINT(" %s : %s \n", __func__, enable_mode?"enable":"disable"); + if (enable_mode) + { + if (egis_dev->fps_ints.drdy_irq_flag == DRDY_IRQ_DISABLE) + { + egis_dev->fps_ints.drdy_irq_flag = DRDY_IRQ_ENABLE; + enable_irq_wake(egis_dev->gpio_irq); + enable_irq(egis_dev->gpio_irq); + } + } + else + { + if (egis_dev->fps_ints.drdy_irq_flag == DRDY_IRQ_ENABLE) + { + disable_irq_nosync(egis_dev->gpio_irq); + disable_irq_wake(egis_dev->gpio_irq); + egis_dev->fps_ints.drdy_irq_flag = DRDY_IRQ_DISABLE; + } + } + spin_unlock_irqrestore(&egis_dev->irq_lock, irqflags); +} + +static DECLARE_WAIT_QUEUE_HEAD(interrupt_waitq); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) +void egisfp_interrupt_timer_call(struct timer_list *t) +{ + struct egisfp_interrupt_desc_t *fps_int = from_timer(fps_int, t, timer); + struct egisfp_dev_t *egis_dev; + + egis_dev = container_of(fps_int, struct egisfp_dev_t, fps_ints); + INFO_PRINT(" %s : interrupt count = %d \n", __func__, egis_dev->fps_ints.int_count); + + if (egis_dev->fps_ints.int_count >= egis_dev->fps_ints.detect_threshold) + { + egis_dev->fps_ints.finger_on = 1; + INFO_PRINT(" %s : interrupt triggered \n", __func__); + } + else + { + INFO_PRINT(" %s : interrupt not triggered \n", __func__); + } + egis_dev->fps_ints.int_count = 0; + wake_up_interruptible(&interrupt_waitq); +} +#else +void egisfp_interrupt_timer_call(unsigned long _data) +{ + struct egisfp_interrupt_desc_t *fps_int = (struct egisfp_interrupt_desc_t *)_data; + struct egisfp_dev_t *egis_dev; + + egis_dev = container_of(fps_int, struct egisfp_dev_t, fps_ints); + INFO_PRINT(" %s : interrupt count = %d \n", __func__, egis_dev->fps_ints.int_count); + + if (egis_dev->fps_ints.int_count >= egis_dev->fps_ints.detect_threshold) + { + egis_dev->fps_ints.finger_on = 1; + INFO_PRINT(" %s : interrupt triggered \n", __func__); + } + else + { + INFO_PRINT(" %s : interrupt not triggered \n", __func__); + } + egis_dev->fps_ints.int_count = 0; + wake_up_interruptible(&interrupt_waitq); +} +#endif +irqreturn_t fp_eint_func(int irq, void *dev_id) +{ + struct egisfp_dev_t *egis_dev = dev_id; + if (!egis_dev->fps_ints.int_count) + mod_timer(&egis_dev->fps_ints.timer, jiffies + msecs_to_jiffies(egis_dev->fps_ints.detect_period)); + egis_dev->fps_ints.int_count++; + INFO_PRINT(" %s : fps_ints.int_count=%d \n", __func__, egis_dev->fps_ints.int_count); + pm_wakeup_event(&egis_dev->dd->dev, WAKE_HOLD_TIME); + return IRQ_HANDLED; +} + +irqreturn_t fp_eint_func_ll(int irq, void *dev_id) +{ + struct egisfp_dev_t *egis_dev = dev_id; + INFO_PRINT(" %s \n", __func__); + egis_dev->fps_ints.finger_on = 1; + egisfp_interrupt_enable(egis_dev, 0); + wake_up_interruptible(&interrupt_waitq); + pm_wakeup_event(&egis_dev->dd->dev, WAKE_HOLD_TIME); + return IRQ_RETVAL(IRQ_HANDLED); +} + +/* + * FUNCTION NAME. + * egisfp_interrupt_init + * + * FUNCTIONAL DESCRIPTION. + * button initial routine + * + * ENTRY PARAMETERS. + * int_mode - determine trigger mode + * EDGE_TRIGGER_FALLING 0x0 + * EDGE_TRIGGER_RAISING 0x1 + * LEVEL_TRIGGER_LOW 0x2 + * LEVEL_TRIGGER_HIGH 0x3 + * + * EXIT PARAMETERS. + * Function Return int + */ + +int egisfp_interrupt_init(struct egisfp_dev_t *egis_dev, int int_mode, int detect_period, int detect_threshold) +{ + + int err = 0; + INFO_PRINT(" %s : mode = %d period = %d threshold = %d \n", __func__, int_mode, detect_period, detect_threshold); + INFO_PRINT(" %s : request_irq_done = %d gpio_irq = %d pin = %d \n", __func__, egis_dev->request_irq_done, egis_dev->gpio_irq, egis_dev->irqPin); + + egis_dev->fps_ints.detect_period = detect_period; + egis_dev->fps_ints.detect_threshold = detect_threshold; + egis_dev->fps_ints.int_count = 0; + egis_dev->fps_ints.finger_on = 0; + egis_dev->fps_ints.drdy_irq_abort = 0; + + if (egis_dev->request_irq_done == 0) + { + if (gpio_is_valid(egis_dev->irqPin)) + { + egis_dev->gpio_irq = gpio_to_irq(egis_dev->irqPin); + INFO_PRINT(" %s : fp_irq number %d \n", __func__, egis_dev->gpio_irq); + } + else + { + ERROR_PRINT(" %s : irqPin is not valid \n", __func__); + return -EIO; + } + + if (egis_dev->gpio_irq < 0) + { + ERROR_PRINT(" %s : gpio_to_irq failed \n", __func__); + return -EIO; + } + + INFO_PRINT(" %s : current flag : %d disable: %d enable: %d \n", __func__, egis_dev->fps_ints.drdy_irq_flag, DRDY_IRQ_DISABLE, DRDY_IRQ_ENABLE); + if (int_mode == EDGE_TRIGGER_RISING) + { + INFO_PRINT(" %s : EDGE_TRIGGER_RISING \n", __func__); + err = request_irq(egis_dev->gpio_irq, fp_eint_func, IRQ_TYPE_EDGE_RISING, "fp_detect-eint", egis_dev); + } + else if (int_mode == EDGE_TRIGGER_FALLING) + { + INFO_PRINT(" %s : EDGE_TRIGGER_FALLING \n", __func__); + err = request_irq(egis_dev->gpio_irq, fp_eint_func, IRQ_TYPE_EDGE_FALLING, "fp_detect-eint", egis_dev); + } + else if (int_mode == LEVEL_TRIGGER_LOW) + { + INFO_PRINT(" %s : LEVEL_TRIGGER_LOW \n", __func__); + err = request_irq(egis_dev->gpio_irq, fp_eint_func_ll, IRQ_TYPE_LEVEL_LOW, "fp_detect-eint", egis_dev); + } + else if (int_mode == LEVEL_TRIGGER_HIGH) + { + INFO_PRINT(" %s : LEVEL_TRIGGER_HIGH \n", __func__); + err = request_irq(egis_dev->gpio_irq, fp_eint_func_ll, IRQ_TYPE_LEVEL_HIGH, "fp_detect-eint", egis_dev); + } + else + { + ERROR_PRINT(" %s : Invalid argument \n", __func__); + return -EINVAL; + } + + if (err) + { + ERROR_PRINT(" %s : request_irq failed return %d \n", __func__, err); + return err; + } + + disable_irq_nosync(egis_dev->gpio_irq); + INFO_PRINT(" %s : request_irq return %d \n", __func__, err); + egis_dev->request_irq_done = 1; + egis_dev->fps_ints.irq_mode = int_mode; + } + else if (egis_dev->fps_ints.irq_mode != int_mode) + { + + egisfp_interrupt_enable(egis_dev, 0); + + free_irq(egis_dev->gpio_irq, egis_dev); + egis_dev->request_irq_done = 0; + if (int_mode == EDGE_TRIGGER_RISING) + { + INFO_PRINT(" %s : EDGE_TRIGGER_RISING \n", __func__); + err = request_irq(egis_dev->gpio_irq, fp_eint_func, IRQ_TYPE_EDGE_RISING, "fp_detect-eint", egis_dev); + } + else if (int_mode == EDGE_TRIGGER_FALLING) + { + INFO_PRINT(" %s : EDGE_TRIGGER_FALLING \n", __func__); + err = request_irq(egis_dev->gpio_irq, fp_eint_func, IRQ_TYPE_EDGE_FALLING, "fp_detect-eint", egis_dev); + } + else if (int_mode == LEVEL_TRIGGER_LOW) + { + INFO_PRINT(" %s : LEVEL_TRIGGER_LOW \n", __func__); + err = request_irq(egis_dev->gpio_irq, fp_eint_func_ll, IRQ_TYPE_LEVEL_LOW, "fp_detect-eint", egis_dev); + } + else if (int_mode == LEVEL_TRIGGER_HIGH) + { + INFO_PRINT(" %s : LEVEL_TRIGGER_HIGH \n", __func__); + err = request_irq(egis_dev->gpio_irq, fp_eint_func_ll, IRQ_TYPE_LEVEL_HIGH, "fp_detect-eint", egis_dev); + } + else + { + ERROR_PRINT(" %s : Invalid argument \n", __func__); + return -EINVAL; + } + + if (err) + { + ERROR_PRINT(" %s : request_irq failed return %d \n", __func__, err); + return err; + } + + disable_irq_nosync(egis_dev->gpio_irq); + INFO_PRINT(" %s : request_irq return %d \n", __func__, err); + egis_dev->request_irq_done = 1; + egis_dev->fps_ints.irq_mode = int_mode; + } + + INFO_PRINT(" %s : current flag : %d disable: %d enable: %d \n", __func__, egis_dev->fps_ints.drdy_irq_flag, DRDY_IRQ_DISABLE, DRDY_IRQ_ENABLE); + INFO_PRINT(" %s : enable irq with mode = %d \n", __func__, int_mode); + egisfp_interrupt_enable(egis_dev, 1); + return err; +} + +/* + * FUNCTION NAME. + * egisfp_interrupt_free + * + * FUNCTIONAL DESCRIPTION. + * free all interrupt resource + * + * EXIT PARAMETERS. + * Function Return int + */ + +int egisfp_interrupt_free(struct egisfp_dev_t *egis_dev) +{ + DEBUG_PRINT(" %s \n", __func__); + egisfp_interrupt_enable(egis_dev, 0); + del_timer_sync(&egis_dev->fps_ints.timer); + egis_dev->fps_ints.finger_on = 0; + return 0; +} + +/* + * FUNCTION NAME. + * fps_interrupt_re d + * + * FUNCTIONAL DESCRIPTION. + * FPS interrupt read status + * + * ENTRY PARAMETERS. + * wait poll table structure + * + * EXIT PARAMETERS. + * Function Return int + */ + +unsigned int egisfp_interrupt_poll(struct file *file, struct poll_table_struct *wait) +{ + unsigned int mask = 0; + struct egisfp_dev_t *egis_dev; + egis_dev = file->private_data; + poll_wait(file, &interrupt_waitq, wait); + if (egis_dev->fps_ints.finger_on) + { + mask |= POLLIN | POLLRDNORM; + } + else if (egis_dev->fps_ints.drdy_irq_abort == 1) + { + mask |= 0x400 | POLLRDNORM; + egis_dev->fps_ints.drdy_irq_abort = 0; + } + else if(screenonoff_flag == 1){ + mask = waitq_type ; + screenonoff_flag = 0; + waitq_type = 0; + INFO_PRINT(" egisfp_screenonoff_poll_mask = %d \n", mask); + } + + return mask; +} + +void egisfp_interrupt_abort(struct egisfp_dev_t *egis_dev) +{ + DEBUG_PRINT(" %s \n", __func__); + egis_dev->fps_ints.finger_on = 0; + egis_dev->fps_ints.drdy_irq_abort = 1; + wake_up_interruptible(&interrupt_waitq); +} + +/*-------------------------------------------------------------------------*/ + +static void send_navi_event(struct egisfp_dev_t *egis_dev, int nav_event) +{ + uint32_t input_event; + + switch (nav_event) + { + case NAVI_EVENT_ON: + DEBUG_PRINT(" %s : finger down \n", __func__); + input_event = EGIS_NAV_INPUT_FINGER_DOWN; + break; + case NAVI_EVENT_OFF: + DEBUG_PRINT(" %s : finger up \n", __func__); + input_event = EGIS_NAV_INPUT_FINGER_UP; + break; + case NAVI_EVENT_UP: + DEBUG_PRINT(" %s : nav swip up \n", __func__); + input_event = EGIS_NAV_INPUT_UP; + break; + case NAVI_EVENT_DOWN: + DEBUG_PRINT(" %s : nav swip down \n", __func__); + input_event = EGIS_NAV_INPUT_DOWN; + break; + case NAVI_EVENT_LEFT: + DEBUG_PRINT(" %s : nav swip left \n", __func__); + input_event = EGIS_NAV_INPUT_LEFT; + break; + case NAVI_EVENT_RIGHT: + DEBUG_PRINT(" %s : nav swip right \n", __func__); + input_event = EGIS_NAV_INPUT_RIGHT; + break; + case NAVI_EVENT_CLICK: + DEBUG_PRINT(" %s : nav finger click \n", __func__); + input_event = EGIS_NAV_INPUT_CLICK; + break; + case NAVI_EVENT_DOUBLE_CLICK: + DEBUG_PRINT(" %s : nav finger double click \n", __func__); + input_event = EGIS_NAV_INPUT_DOUBLE_CLICK; + break; + case NAVI_EVENT_LONG_PRESS: + DEBUG_PRINT(" %s : nav finger long press \n", __func__); + input_event = EGIS_NAV_INPUT_LONG_PRESS; + break; + default: + DEBUG_PRINT(" %s : unknown nav event: %d \n", __func__, nav_event); + input_event = 0; + break; + } + + if (input_event) + { + input_report_key(egis_dev->input_dev, input_event, 1); + input_sync(egis_dev->input_dev); + input_report_key(egis_dev->input_dev, input_event, 0); + input_sync(egis_dev->input_dev); + } +} + +int do_egisfp_reset(struct egisfp_dev_t *egis_dev) +{ + int ret = 0; + ret |= pinctrl_select_state(egis_dev->pinctrl, egis_dev->reset_low); + mdelay(Rst_off_delay); + ret |= pinctrl_select_state(egis_dev->pinctrl, egis_dev->reset_high); + mdelay(Rst_on_delay); + if (ret) + ERROR_PRINT(" %s : failed ret = %d \n", __func__, ret); + else + DEBUG_PRINT(" %s : reset_pin = %d \n", __func__, gpio_get_value(egis_dev->rstPin)); + return ret; +} + +int egisfp_set_screen_onoff(int on) +{ + DEBUG_PRINT(" %s \n", __func__); + + if (!g_data) + return -ENXIO; + + g_data->screen_onoff = on; + + DEBUG_PRINT(" %s screen_onoff %d \n", __func__, g_data->screen_onoff); + + return 0; +} +EXPORT_SYMBOL(egisfp_set_screen_onoff); + +int do_egisfp_power_onoff(struct egisfp_dev_t *egis_dev, struct egisfp_ioctl_cmd_t *ioctl_data) +{ + int ret = 0; + uint32_t power_onoff = ioctl_data->power_on; + + if (!egis_dev->ctrl_power) + { + INFO_PRINT(" %s : power can not control by driver \n", __func__); + return ret; + } + + DEBUG_PRINT(" %s : power_onoff = %d \n", __func__, power_onoff); + + if (egis_dev->power_enable == power_onoff) + { + INFO_PRINT(" %s : duplicate set power on/off \n", __func__); + return ret; + } + + if (power_onoff) + { + if (egis_dev->pwr_by_gpio) + ret = pinctrl_select_state(egis_dev->pinctrl, egis_dev->vcc_high); + else + ret = regulator_enable(egis_dev->vcc); + + if (ret) + goto do_egisfp_power_onoff_failed; + + msleep(Tpwr_on_delay); + egis_dev->power_enable = 1; + } + else + { + if (egis_dev->pwr_by_gpio) + ret = pinctrl_select_state(egis_dev->pinctrl, egis_dev->vcc_low); + else + ret = regulator_disable(egis_dev->vcc); + + if (ret) + goto do_egisfp_power_onoff_failed; + + msleep(Tpwr_off_delay); + egis_dev->power_enable = 0; + } + if (egis_dev->pwr_by_gpio) + DEBUG_PRINT(" %s : power_pin value = %d \n", __func__, gpio_get_value(egis_dev->vcc_33v_Pin)); + else + DEBUG_PRINT(" %s : regulator enable = %d output voltage %d \n", __func__, regulator_is_enabled(egis_dev->vcc), regulator_get_voltage(egis_dev->vcc)); + + return ret; + +do_egisfp_power_onoff_failed: + ERROR_PRINT(" %s : failed ret = %d \n", __func__, ret); + return ret; +} +int do_egisfp_reset_set(struct egisfp_dev_t *egis_dev, int reset_high_low) +{ + int ret = 0; + DEBUG_PRINT(" %s : reset_high_low = %d \n", __func__, reset_high_low); + + if (reset_high_low) + { + ret = pinctrl_select_state(egis_dev->pinctrl, egis_dev->reset_high); + } + else + { + ret = pinctrl_select_state(egis_dev->pinctrl, egis_dev->reset_low); + } + + if (ret) + ERROR_PRINT(" %s : failed ret = %d \n", __func__, ret); + else + DEBUG_PRINT(" %s : reset_pin value = %d \n", __func__, gpio_get_value(egis_dev->rstPin)); + return ret; +} + +static void egis_get_io_stus(struct egisfp_dev_t *egis_dev) +{ + INFO_PRINT(" %s \n", __func__); + if (egis_dev->ctrl_power) + { + if (egis_dev->pwr_by_gpio) + INFO_PRINT(" %s : power_pin value = %d \n", __func__, gpio_get_value(egis_dev->vcc_33v_Pin)); + else + INFO_PRINT(" %s : regulator vcc enable = %d \n", __func__, regulator_is_enabled(egis_dev->vcc)); + } + + INFO_PRINT(" %s : reset_pin value = %d irq_pin value = %d \n", __func__, gpio_get_value(egis_dev->rstPin), gpio_get_value(egis_dev->irqPin)); +} + + +long egisfp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int retval = 0; + struct egisfp_dev_t *egis_dev = NULL; + struct egisfp_ioctl_cmd_t data = {0}; + + INFO_PRINT(" %s : cmd = 0x%X \n", __func__, cmd); + egis_dev = (struct egisfp_dev_t *)filp->private_data; + + if (!egis_dev->pars_dtsi_done) + { + ERROR_PRINT(" %s : egis_dev is NULL \n", __func__); + return -ENODEV; + } + + if (egisfp_check_ioctl_permission(egis_dev, cmd)) + return -EACCES; + + switch (cmd) + { + case FP_GET_RESOURCE: + DEBUG_PRINT(" %s : FP_GET_RESOURCE \n", __func__); + if (egisfp_platforminit(egis_dev)) + retval = -EIO; + break; + case FP_FREE_RESOURCE: + DEBUG_PRINT(" %s : FP_FREE_RESOURCE \n", __func__); + egisfp_platformfree(egis_dev); + break; + case FP_SET_SPI_CLOCK: + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + return -EFAULT; + } + egis_dev->clk_speed = data.int_mode * 1000000; + DEBUG_PRINT(" %s : FP_SET_SPI_CLOCK %d \n", __func__, egis_dev->clk_speed); + break; + case INT_TRIGGER_INIT: + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + return -EFAULT; + } + retval = egisfp_interrupt_init(egis_dev, data.int_mode, data.detect_period, data.detect_threshold); + DEBUG_PRINT(" %s : INT_TRIGGER_INIT %x \n", __func__, retval); + break; + case FP_SENSOR_RESET: + DEBUG_PRINT(" %s : FP_SENSOR_RESET \n", __func__); + do_egisfp_reset(egis_dev); + break; + case FP_POWER_ONOFF: + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + return -EFAULT; + } + DEBUG_PRINT(" %s : FP_POWER_ONOFF \n", __func__); + retval = do_egisfp_power_onoff(egis_dev, &data); + break; + case FP_RESET_SET: + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + return -EFAULT; + } + DEBUG_PRINT(" %s : FP_RESET_SET \n", __func__); + retval = do_egisfp_reset_set(egis_dev, data.int_mode); // Use data.int_mode as reset setting. 1 = on, 0 = off. + break; + case FP_WAKELOCK_ENABLE: + DEBUG_PRINT(" %s : FP_WAKELOCK_ENABLE \n", __func__); + pm_stay_awake(&egis_dev->dd->dev); + break; + case FP_WAKELOCK_DISABLE: + DEBUG_PRINT(" %s : FP_WAKELOCK_DISABLE \n", __func__); + pm_relax(&egis_dev->dd->dev); + break; + case GET_IO_STUS: + DEBUG_PRINT(" %s : GET_IO_STUS \n", __func__); + egis_get_io_stus(egis_dev); + break; + case SEND_NAVI_EVENT: + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + return -EFAULT; + } + DEBUG_PRINT(" %s : SEND_NAVI_EVENT \n", __func__); + send_navi_event(egis_dev, data.int_mode); + break; + case INT_TRIGGER_CLOSE: + retval = egisfp_interrupt_free(egis_dev); + DEBUG_PRINT(" %s : INT_TRIGGER_CLOSE %d \n", __func__, retval); + break; + case INT_TRIGGER_ABORT: + DEBUG_PRINT(" %s : INT_TRIGGER_ABORT \n", __func__); + egisfp_interrupt_abort(egis_dev); + break; + case FP_FREE_GPIO: + DEBUG_PRINT(" %s : FP_FREE_GPIO \n", __func__); + break; + case FP_WAKELOCK_TIMEOUT_ENABLE: //0Xb1 + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + data.int_mode = WAKE_HOLD_TIME; + } + if (data.int_mode == 0) + data.int_mode = WAKE_HOLD_TIME; + + DEBUG_PRINT(" %s : FP_WAKELOCK_TIMEOUT_ENABLE %d ms \n", __func__, data.int_mode); + pm_wakeup_event(&egis_dev->dd->dev, data.int_mode); + break; + case FP_WAKELOCK_TIMEOUT_DISABLE: //0Xb2 + DEBUG_PRINT(" %s : FP_WAKELOCK_TIMEOUT_DISABLE \n", __func__); + break; + case DELETE_DEVICE_NODE: + DEBUG_PRINT(" %s : DELETE_DEVICE_NODE \n", __func__); + //delete_device_node(); + break; + case GET_SCREEN_ONOFF: + DEBUG_PRINT(" %s : GET_SCREEN_ONOFF \n", __func__); + data.int_mode = egis_dev->screen_onoff; + if (copy_to_user((int __user *)arg, &data, sizeof(data))) + { + return -EFAULT; + } + break; + default: + retval = 0; + break; + } + DEBUG_PRINT(" %s done \n", __func__); + return retval; +} + +#define egisfp_compat_ioctl NULL + +int egisfp_open(struct inode *inode, struct file *filp) +{ + struct egisfp_dev_t *egis_dev = NULL; + int status = -ENXIO; + DEBUG_PRINT(" %s \n", __func__); + + mutex_lock(&device_list_lock); + list_for_each_entry(egis_dev, &device_list, device_entry) + { + if (egis_dev->devt == inode->i_rdev) + { + status = 0; + break; + } + } + + if (status == 0) + { + /* device tree call */ + if (egis_dev->dd->dev.of_node && !egis_dev->pars_dtsi_done) + { + status = egisfp_parse_dt(egis_dev); + } + + if (!status) + { + egis_dev->pars_dtsi_done = 1; + egis_dev->users++; + filp->private_data = egis_dev; + nonseekable_open(inode, filp); + INFO_PRINT(" %s : open ioctl user count = %d \n", __func__, egis_dev->users); + } + else + { + ERROR_PRINT(" %s : egisfp_parse_dt fail, status = %d \n", __func__, status); + } + } + else + { + ERROR_PRINT(" %s : nothing for minor %d \n", __func__, iminor(inode)); + } + mutex_unlock(&device_list_lock); + return status; +} + +int egisfp_release(struct inode *inode, struct file *filp) +{ + struct egisfp_dev_t *egis_dev; + DEBUG_PRINT(" %s \n", __func__); + mutex_lock(&device_list_lock); + egis_dev = filp->private_data; + filp->private_data = NULL; + /* last close? */ + egis_dev->users--; + if (egis_dev->users == 0) + { + INFO_PRINT(" %s : egis_dev->users == %d \n", __func__, egis_dev->users); + egisfp_platformfree(egis_dev); + } + mutex_unlock(&device_list_lock); + return 0; +} +int egisfp_platformfree(struct egisfp_dev_t *egis_dev) +{ + int status = 0; + struct egisfp_ioctl_cmd_t ioctl_data = {0}; + DEBUG_PRINT(" %s : enter \n", __func__); + if (egis_dev->platforminit_done != 1) + return status; + if (egis_dev != NULL) + { + if (egis_dev->request_irq_done == 1) + { + egisfp_interrupt_enable(egis_dev, 0); + free_irq(egis_dev->gpio_irq, egis_dev); + egis_dev->request_irq_done = 0; + } + status = do_egisfp_reset_set(egis_dev, 0); + + if (egis_dev->power_enable && egis_dev->ctrl_power) + status = do_egisfp_power_onoff(egis_dev, &ioctl_data); + + gpio_free(egis_dev->irqPin); + gpio_free(egis_dev->rstPin); + if (egis_dev->pwr_by_gpio) + gpio_free(egis_dev->vcc_33v_Pin); + + if (egis_dev->vcc) + { + DEBUG_PRINT(" %s : devm_regulator_put \n", __func__); + devm_regulator_put(egis_dev->vcc); + egis_dev->vcc = NULL; + } + + + if (egis_dev->pinctrl) + { + DEBUG_PRINT(" %s : devm_pinctrl_put \n", __func__); + devm_pinctrl_put(egis_dev->pinctrl); + egis_dev->pinctrl = NULL; + } + } + egis_dev->platforminit_done = 0; + DEBUG_PRINT(" %s : successful status = %d \n", __func__, status); + return status; +} + +int egisfp_platforminit(struct egisfp_dev_t *egis_dev) +{ + int status; + INFO_PRINT(" %s : Version %s, 0329 \n", __func__, DRIVER_VERSION); + if (egis_dev != NULL) + { + if (!egis_dev->platforminit_done) + { + if (egis_dev->ctrl_power) + { + if (egis_dev->pwr_by_gpio) + { + /* initial 33V power pin */ + status = gpio_request(egis_dev->vcc_33v_Pin, "egis_dev-33v-gpio"); + if (status < 0) + { + ERROR_PRINT(" %s : gpio_requset vcc_33v_Pin pin failed \n", __func__); + goto egisfp_power_request_fail; + } + + status = gpio_direction_output(egis_dev->vcc_33v_Pin, 0); + } + else + { + egis_dev->vcc = devm_regulator_get(&egis_dev->dd->dev, "vcc_fp"); + if (IS_ERR(egis_dev->vcc)) + { + status = PTR_ERR(egis_dev->vcc); + ERROR_PRINT(" %s : get vcc regulator failed %d \n", __func__, status); + goto egisfp_power_request_fail; + } + + if (regulator_count_voltages(egis_dev->vcc) > 0) + { + status = regulator_set_voltage(egis_dev->vcc, egis_dev->regulator_voltage_min, egis_dev->regulator_voltage_max); + if (status) + { + ERROR_PRINT(" %s : set vcc regulator voltage failed %d \n", __func__, status); + goto egisfp_power_setup_fail; + } + + status = regulator_set_load(egis_dev->vcc, egis_dev->regulator_current); + if (status) + { + ERROR_PRINT(" %s : set vcc regulator current failed %d \n", __func__, status); + goto egisfp_power_setup_fail; + } + } + } + } + + /* Initial Reset Pin */ + status = gpio_request(egis_dev->rstPin, "egis_dev-reset-gpio"); + if (status < 0) + { + ERROR_PRINT(" %s : gpio_requset reset pin failed \n", __func__); + goto egisfp_reset_request_fail; + } + + status = gpio_direction_output(egis_dev->rstPin, 0); + + status = gpio_request(egis_dev->irqPin, "egis_dev-irq-gpio"); + if (status < 0) + { + ERROR_PRINT(" %s : gpio_requset irq pin failed \n", __func__); + goto egisfp_irq_request_fail; + } + + gpio_direction_input(egis_dev->irqPin); + + if (egis_dev->dd) + { + INFO_PRINT(" %s : find node enter \n", __func__); + egis_dev->pinctrl = devm_pinctrl_get(&egis_dev->dd->dev); + if (IS_ERR(egis_dev->pinctrl)) + { + status = PTR_ERR(egis_dev->pinctrl); + ERROR_PRINT(" %s : can't find fingerprint pinctrl \n", __func__); + goto egisfp_pinctrl_fail; + } + egis_dev->reset_high = pinctrl_lookup_state(egis_dev->pinctrl, "egis_rst_high"); + if (IS_ERR(egis_dev->reset_high)) + { + status = PTR_ERR(egis_dev->reset_high); + ERROR_PRINT(" %s : can't find fingerprint pinctrl egis_rst_high \n", __func__); + goto egisfp_pinctrl_fail; + } + egis_dev->reset_low = pinctrl_lookup_state(egis_dev->pinctrl, "egis_rst_low"); + if (IS_ERR(egis_dev->reset_low)) + { + status = PTR_ERR(egis_dev->reset_low); + ERROR_PRINT(" %s : can't find fingerprint pinctrl egis_rst_low \n", __func__); + goto egisfp_pinctrl_fail; + } + if (pinctrl_select_state(egis_dev->pinctrl, egis_dev->reset_low)) + goto egisfp_pinctrl_fail; + egis_dev->irq_active = pinctrl_lookup_state(egis_dev->pinctrl, "egis_irq_active"); + if (IS_ERR(egis_dev->irq_active)) + { + status = PTR_ERR(egis_dev->irq_active); + ERROR_PRINT(" %s : can't find fingerprint pinctrl egis_irq_active \n", __func__); + goto egisfp_pinctrl_fail; + } + if (pinctrl_select_state(egis_dev->pinctrl, egis_dev->irq_active)) + goto egisfp_pinctrl_fail; + if (egis_dev->pwr_by_gpio && egis_dev->ctrl_power) + { + egis_dev->vcc_high = pinctrl_lookup_state(egis_dev->pinctrl, "egis_vcc_high"); + if (IS_ERR(egis_dev->vcc_high)) + { + status = PTR_ERR(egis_dev->vcc_high); + ERROR_PRINT(" %s : can't find fingerprint pinctrl egis_vcc_high \n", __func__); + goto egisfp_pinctrl_fail; + } + egis_dev->vcc_low = pinctrl_lookup_state(egis_dev->pinctrl, "egis_vcc_low"); + if (IS_ERR(egis_dev->vcc_low)) + { + status = PTR_ERR(egis_dev->vcc_low); + ERROR_PRINT(" %s : can't find fingerprint pinctrl egis_vcc_low \n", __func__); + goto egisfp_pinctrl_fail; + } + if (pinctrl_select_state(egis_dev->pinctrl, egis_dev->vcc_low)) + goto egisfp_pinctrl_fail; + } + } + + + + egis_dev->platforminit_done = 1; + if (egis_dev->pwr_by_gpio && egis_dev->ctrl_power) + INFO_PRINT(" %s : successful status = %d gpio num -> vcc = %d rst = %d Irq = %d \n", __func__, status, egis_dev->vcc_33v_Pin, egis_dev->rstPin, egis_dev->irqPin); + else + INFO_PRINT(" %s : successful status = %d gpio num -> rst = %d Irq = %d \n", __func__, status, egis_dev->rstPin, egis_dev->irqPin); + } + else + { + INFO_PRINT(" %s : platform already init \n", __func__); + } + return 0; + } + else + { + ERROR_PRINT(" %s : device node is null \n", __func__); + return -ENODEV; + } +egisfp_pinctrl_fail: + ERROR_PRINT(" %s : devm_pinctrl_put \n", __func__); + devm_pinctrl_put(egis_dev->pinctrl); + egis_dev->pinctrl = NULL; + gpio_free(egis_dev->irqPin); +egisfp_irq_request_fail: + gpio_free(egis_dev->rstPin); +egisfp_reset_request_fail: +egisfp_power_setup_fail: + if (egis_dev->ctrl_power) + { + if (egis_dev->pwr_by_gpio) + gpio_free(egis_dev->vcc_33v_Pin); + else + devm_regulator_put(egis_dev->vcc); + } +egisfp_power_request_fail: + return -EIO; +} + +int egisfp_check_ioctl_permission(struct egisfp_dev_t *egis_dev, unsigned int cmd) +{ + + if (cmd == FP_GET_RESOURCE || cmd == FP_FREE_RESOURCE) + { + return 0; + } + else if (egis_dev->platforminit_done) + { + return 0; + } + else + { + ERROR_PRINT(" %s : need get resource fisrt \n", __func__); + return -EACCES; + } +} +#if ((defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0))) || defined(SET_QCOM_PANEL_EVENT_NOTIFIER)) +static int drm_check_dt(struct egisfp_dev_t *egis_dev) +{ + int i = 0; + int count = 0; + struct device_node *np = egis_dev->dd->dev.of_node; + struct device_node *node = NULL; + struct drm_panel *panel = NULL; + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) { + ERROR_PRINT(" %s : find drm_panel fail count = %d ", __func__, count); + return -ENODEV; + } + 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)) { + ERROR_PRINT(" %s : ind drm_panel successfully ", __func__); + egis_dev->active_panel = panel; + return 0; + } + } + ERROR_PRINT(" %s : can not find drm_panel ", __func__); + return -ENODEV; +} +#endif + +#if defined(SET_QCOM_PANEL_EVENT_NOTIFIER) +static void fps_panel_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, + void *client_data) +{ + char *envp[2]; + int ret; + struct egisfp_dev_t *egis_dev = client_data; + + if (!notification) { + pr_err(" %s : Invalid notification \n", __func__); + return; + } + + if (!egis_dev) { + pr_err(" %s : Invalid egisfp_dev_t \n", __func__); + return; + } + + if ((notification->notif_type == DRM_PANEL_EVENT_UNBLANK) || (notification->notif_type == DRM_PANEL_EVENT_BLANK)) + { + DEBUG_PRINT(" %s : Notification type: %d, early_trigger: %d \n", __func__, notification->notif_type, notification->notif_data.early_trigger); + + switch (notification->notif_type) { + case DRM_PANEL_EVENT_UNBLANK: + if (!notification->notif_data.early_trigger) { + egis_dev->screen_onoff = 1; + envp[0] = "PANEL=1"; + } + break; + case DRM_PANEL_EVENT_BLANK: + egis_dev->screen_onoff = 0; + envp[0] = "PANEL=0"; + break; + default: + break; + } + DEBUG_PRINT(" %s : screen_onoff = %d \n", __func__, egis_dev->screen_onoff); + envp[1] = NULL; + ret = kobject_uevent_env(&egis_dev->dd->dev.kobj, KOBJ_CHANGE, envp); + } +} +#else +// Register FB notifier +++ +static int egisfp_fb_callback(struct notifier_block *nb, unsigned long val, void *data) +{ + struct egisfp_dev_t *egis_dev = NULL; + char *envp[2]; + int ret = 0; +#if defined(CONFIG_PANEL_NOTIFICATIONS) + INFO_PRINT(" %s : got notify value = %d \n", __func__, (int)val); + egis_dev = container_of(nb, struct egisfp_dev_t, notifier); + egis_dev->screen_onoff = 1; + switch(val) { + case PANEL_EVENT_DISPLAY_ON: + egis_dev->screen_onoff = 1; + envp[0] = "PANEL=1"; + break; + case PANEL_EVENT_DISPLAY_OFF: + egis_dev->screen_onoff = 0; + envp[0] = "PANEL=0"; + break; + case PANEL_EVENT_PRE_DISPLAY_OFF: + break; + case PANEL_EVENT_PRE_DISPLAY_ON: + break; + default: + break; + } + if(val == FP_NOTIFY_ON ||val == FP_NOTIFY_OFF){ + envp[1] = NULL; + ret = kobject_uevent_env(&egis_dev->dd->dev.kobj, KOBJ_CHANGE, envp); + INFO_PRINT(" %s : screen_onoff = %d val=%lu ret=%d\n", __func__, egis_dev->screen_onoff, val,ret); + } +#elif defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) + struct drm_panel_notifier *evdata = data; + unsigned int blank; + INFO_PRINT(" %s : got notify value = %d \n", __func__, (int)val); + if (val != DRM_PANEL_EARLY_EVENT_BLANK) + { + return NOTIFY_OK; + } + egis_dev = container_of(nb, struct egisfp_dev_t, notifier); + if (!evdata || !evdata->data || !egis_dev) { + ERROR_PRINT(" %s : some parameters is null \n", __func__); + return NOTIFY_OK; + } + blank = *(int *)(evdata->data); + switch (blank) { + case DRM_PANEL_BLANK_UNBLANK: + egis_dev->screen_onoff = 1; + envp[0] = "PANEL=1"; + break; + case DRM_PANEL_BLANK_POWERDOWN: + egis_dev->screen_onoff = 0; + envp[0] = "PANEL=0"; + break; + default: + break; + } + INFO_PRINT(" %s : screen_onoff = %d \n", __func__, egis_dev->screen_onoff); + envp[1] = NULL; + ret = kobject_uevent_env(&egis_dev->dd->dev.kobj, KOBJ_CHANGE, envp); +#elif defined(CONFIG_FB) + struct fb_event *evdata = data; + unsigned int blank; + + if (val != FP_NOTIFY_EVENT_BLANK) + return 0; + + INFO_PRINT(" %s : got notify value = %d \n", __func__, (int)val); + egis_dev = container_of(nb, struct egisfp_dev_t, notifier); + if (evdata && evdata->data && val == FB_EVENT_BLANK && egis_dev) { + blank = *(int *)(evdata->data); + INFO_PRINT(" %s : blank value = %d \n", __func__, (int)blank); + switch (blank) { + case FP_NOTIFY_OFF: + egis_dev->screen_onoff = 0; + envp[0] = "PANEL=0"; + break; + case FP_NOTIFY_ON: + egis_dev->screen_onoff = 1; + envp[0] = "PANEL=1"; + break; + default: + break; + } + INFO_PRINT(" %s : screen_onoff = %d \n", __func__, egis_dev->screen_onoff); + envp[1] = NULL; + ret = kobject_uevent_env(&egis_dev->dd->dev.kobj, KOBJ_CHANGE, envp); + } +#else + struct msm_drm_notifier *evdata = data; + unsigned int blank; + + INFO_PRINT(" %s : got notify value = %d \n", __func__, (int)val); + if (val != FP_NOTIFY_EVENT_BLANK) + { + return 0; + } + egis_dev = container_of(nb, struct egisfp_dev_t, notifier); + if (!evdata || !evdata->data || !egis_dev) + { + ERROR_PRINT(" %s : some parameters is null \n", __func__); + return 0; + } + + blank = *(int *)(evdata->data); + switch (blank) + { + case FP_NOTIFY_ON: + egis_dev->screen_onoff = 1; + envp[0] = "PANEL=1"; + break; + case FP_NOTIFY_OFF: + egis_dev->screen_onoff = 0; + envp[0] = "PANEL=0"; + break; + default: + break; + } + + INFO_PRINT(" %s : screen_onoff = %d \n", __func__, egis_dev->screen_onoff); + envp[1] = NULL; + ret = kobject_uevent_env(&egis_dev->dd->dev.kobj, KOBJ_CHANGE, envp); +#endif + return NOTIFY_OK; +} + +static struct notifier_block egisfp_noti_block = { + .notifier_call = egisfp_fb_callback, +}; +#endif +int egisfp_parse_dt(struct egisfp_dev_t *egis_dev) +{ + int ret; + struct device_node *node = egis_dev->dd->dev.of_node; + u32 voltage_supply[2]; + u32 current_supply; + INFO_PRINT(" %s : start \n", __func__); + + if (node) + { + egis_dev->ctrl_power = of_property_read_bool(node, "fp-ctrl-power"); + if (egis_dev->ctrl_power) + { + egis_dev->pwr_by_gpio = of_property_read_bool(node, "fp-gpio-vcc-enable"); + if (egis_dev->pwr_by_gpio) + { + egis_dev->vcc_33v_Pin = of_get_named_gpio(node, "egistec,gpio_vcc_en", 0); + INFO_PRINT(" %s : vcc_33v_pin gpio num is %d \n", __func__, egis_dev->vcc_33v_Pin); + if (!gpio_is_valid(egis_dev->vcc_33v_Pin)) + { + ERROR_PRINT(" %s : vcc_33v_pin gpio is invalid \n", __func__); + return -ENODEV; + } + } + else + { + ret = of_property_read_u32_array(node, "egis-fp,vcc-voltage", voltage_supply, 2); + if (ret < 0) + { + ERROR_PRINT(" %s : fail to get vcc regulator voltage \n", __func__); + return -ENODEV; + } + INFO_PRINT(" %s : vcc regulator voltage get Max = %d, Min = %d \n", __func__, voltage_supply[1], voltage_supply[0]); + egis_dev->regulator_voltage_max = voltage_supply[1]; + egis_dev->regulator_voltage_min = voltage_supply[0]; + + ret = of_property_read_u32_array(node, "egis-fp,vcc-current", ¤t_supply, 1); + if (ret < 0) + { + ERROR_PRINT(" %s : fail to get vcc regulator current_supply \n", __func__); + return -ENODEV; + } + INFO_PRINT(" %s : vcc regulator current get %d \n", __func__, current_supply); + egis_dev->regulator_current = current_supply; + } + } + + egis_dev->rstPin = of_get_named_gpio(node, "egistec,gpio_reset", 0); + INFO_PRINT(" %s : rst pin gpio num is %d \n", __func__, egis_dev->rstPin); + if (!gpio_is_valid(egis_dev->rstPin)) + { + ERROR_PRINT(" %s : rst pin gpio is invalid \n", __func__); + return -ENODEV; + } + + egis_dev->irqPin = of_get_named_gpio(node, "egistec,gpio_irq", 0); + INFO_PRINT(" %s : irq pin gpio num is %d \n", __func__, egis_dev->irqPin); + if (!gpio_is_valid(egis_dev->irqPin)) + { + ERROR_PRINT(" %s : irq pin gpio is invalid \n", __func__); + return -ENODEV; + } + } + else + { + ERROR_PRINT(" %s : device node is null \n", __func__); + return -ENODEV; + } + INFO_PRINT(" %s : successful \n", __func__); + + return 0; +} + +const struct file_operations egisfp_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = egisfp_ioctl, + .compat_ioctl = egisfp_compat_ioctl, + .open = egisfp_open, + .release = egisfp_release, + .llseek = no_llseek, + .poll = egisfp_interrupt_poll}; + +/*-------------------------------------------------------------------------*/ +struct class *egisfp_class; +/*-------------------------------------------------------------------------*/ + +int egisfp_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct egisfp_dev_t *egis_dev = dev_get_drvdata(dev); + INFO_PRINT(" %s : driver remove \n", __func__); + if (egis_dev->request_irq_done) + { + free_irq(egis_dev->gpio_irq, egis_dev); + } + if (egis_dev->call_back_registered) + { +#if defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) + if (egis_dev->active_panel) + { + drm_panel_notifier_unregister(egis_dev->active_panel, &egis_dev->notifier); + egis_dev->active_panel = NULL; + } +#elif defined(SET_QCOM_PANEL_EVENT_NOTIFIER) + if (egis_dev->notifier_cookie) + panel_event_notifier_unregister(egis_dev->notifier_cookie); +#else + ets_fb_unregister_client(&egis_dev->notifier); +#endif + } + del_timer_sync(&egis_dev->fps_ints.timer); + + device_init_wakeup(&egis_dev->dd->dev, 0); + + egis_dev->request_irq_done = 0; + + if (egis_dev->input_dev) + { + input_unregister_device(egis_dev->input_dev); + } + + device_destroy(egisfp_class, egis_dev->devt); + + list_del(&egis_dev->device_entry); + + class_destroy(egisfp_class); + + unregister_chrdev(EGIS_FP_MAJOR, egisfp_driver.driver.name); + + g_data = NULL; + return 0; +} + +int egisfp_probe(struct platform_device *pdev) +{ + struct egisfp_dev_t *egis_dev; + int status, i; + unsigned long minor; + + INFO_PRINT(" %s : driver init \n", __func__); + BUILD_BUG_ON(N_SPI_MINORS > 256); + status = register_chrdev(EGIS_FP_MAJOR, EGIS_CHRD_DRIVER_NAME, &egisfp_fops); + if (status < 0) + { + ERROR_PRINT(" %s : register_chrdev error \n", __func__); + return status; + } + egisfp_class = class_create(THIS_MODULE, EGIS_CLASS_NAME); + if (IS_ERR(egisfp_class)) + { + ERROR_PRINT(" %s : class_create error \n", __func__); + unregister_chrdev(EGIS_FP_MAJOR, egisfp_driver.driver.name); + return PTR_ERR(egisfp_class); + } + /* Allocate driver data */ + egis_dev = kzalloc(sizeof(struct egisfp_dev_t), GFP_KERNEL); + if (egis_dev == NULL) + { + ERROR_PRINT(" %s : Failed to kzalloc \n", __func__); + return -ENOMEM; + } +/* Initialize the driver data */ + dev_set_drvdata(&pdev->dev, egis_dev); + egis_dev->dd = pdev; + + + spin_lock_init(&egis_dev->irq_lock); + + device_init_wakeup(&egis_dev->dd->dev, 1); + + mutex_init(&device_list_lock); + + INIT_LIST_HEAD(&egis_dev->device_entry); + + /* init status */ + egis_dev->pars_dtsi_done = 0; + egis_dev->fps_ints.drdy_irq_flag = DRDY_IRQ_DISABLE; + egis_dev->clk_enabled = 0; + egis_dev->request_irq_done = 0; + egis_dev->platforminit_done = 0; + egis_dev->power_enable = 0; + egis_dev->screen_onoff = 1; // dafault set screen on + egis_dev->call_back_registered = 0; + egis_dev->fps_ints.irq_mode = -1; +#if defined(SET_QCOM_PANEL_EVENT_NOTIFIER) + egis_dev->notifier_cookie = NULL; +#endif +#if ((defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0))) || defined(SET_QCOM_PANEL_EVENT_NOTIFIER)) + egis_dev->active_panel = NULL; + drm_check_dt(egis_dev); +#endif + /* + * If we can allocate a minor number, hook up this device. + * Reusing minors is fine so long as udev or mdev is working. + */ + mutex_lock(&device_list_lock); + minor = find_first_zero_bit(minors, N_SPI_MINORS); + if (minor < N_SPI_MINORS) + { + struct device *fdev; + egis_dev->devt = MKDEV(EGIS_FP_MAJOR, minor); + fdev = device_create(egisfp_class, &egis_dev->dd->dev, egis_dev->devt, + egis_dev, EGIS_DEV_NAME); + status = IS_ERR(fdev) ? PTR_ERR(fdev) : 0; + } + else + { + ERROR_PRINT(" %s : no minor number available \n", __func__); + status = -ENODEV; + } + if (status == 0) + { + set_bit(minor, minors); + list_add(&egis_dev->device_entry, &device_list); + } + + mutex_unlock(&device_list_lock); + + if (status) + { + goto egistec_probe_failed; + } + + egis_dev->input_dev = input_allocate_device(); + if (egis_dev->input_dev == NULL) + { + ERROR_PRINT(" %s : failed to allocate input device \n", __func__); + status = -ENOMEM; + goto egistec_probe_failed; + } + for (i = 0; i < ARRAY_SIZE(key_maps); i++) + input_set_capability(egis_dev->input_dev, key_maps[i].type, key_maps[i].code); + + egis_dev->input_dev->name = EGIS_INPUT_NAME; + status = input_register_device(egis_dev->input_dev); + if (status) + { + ERROR_PRINT(" %s : failed to register input device %d \n", __func__, status); + goto egistec_input_failed; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + timer_setup(&egis_dev->fps_ints.timer, egisfp_interrupt_timer_call, 0); +#else + setup_timer(&egis_dev->fps_ints.timer, egisfp_interrupt_timer_call, (unsigned long)&egis_dev->fps_ints); +#endif + +#if defined(SET_QCOM_PANEL_EVENT_NOTIFIER) + DEBUG_PRINT(" %s : CONFIG_QCOM_PANEL_EVENT_NOTIFIER is enable \n", __func__); + if (egis_dev->active_panel) { + DEBUG_PRINT(" %s : register panel event notifier \n", __func__); + egis_dev->notifier_cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, PANEL_EVENT_NOTIFIER_CLIENT_FPS, + egis_dev->active_panel, &fps_panel_notifier_callback, egis_dev); + if (!egis_dev->notifier_cookie) { + ERROR_PRINT(" %s : register panel events fail: %d ", __func__, status); + } else { + egis_dev->call_back_registered = 1; + ERROR_PRINT(" %s : registered for panel notifications on panel\n", __func__); + } + + } + +#elif defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) + egis_dev->notifier = egisfp_noti_block; + if (egis_dev->active_panel) { + DEBUG_PRINT(" %s : register drm_panel_notifier \n", __func__); + status = drm_panel_notifier_register(egis_dev->active_panel, &egis_dev->notifier); + if (status) + ERROR_PRINT(" %s : drm_panel_notifier_register fail: %d ", __func__, status); + else + egis_dev->call_back_registered = 1; + } +#else + egis_dev->notifier = egisfp_noti_block; + status = ets_fb_register_client(&egis_dev->notifier); + if (status) + ERROR_PRINT(" %s : ets_fb_register_client fail: %d ", __func__, status); + else + egis_dev->call_back_registered = 1; +#endif + + g_data = egis_dev; + + DEBUG_PRINT(" %s : initialize success %d\n", __func__, status); + + return status; + +egistec_input_failed: + if (egis_dev->input_dev != NULL) + input_free_device(egis_dev->input_dev); +egistec_probe_failed: + device_destroy(egisfp_class, egis_dev->devt); + class_destroy(egisfp_class); + kfree(egis_dev); + ERROR_PRINT(" %s : driver probe failed %d \n", __func__, status); + return status; +} + +int __init egisfp_init(void) +{ + int status; + INFO_PRINT(" %s : module init \n", __func__); + status = platform_driver_register(&egisfp_driver); + if (status) + { + ERROR_PRINT(" %s : register Egis driver fail \n", __func__); + return -EINVAL; + } + INFO_PRINT(" %s : module init OK ! \n", __func__); + return status; +} +void __exit egisfp_exit(void) +{ + INFO_PRINT("module exit \n"); + platform_driver_unregister(&egisfp_driver); +} + +module_init(egisfp_init); +module_exit(egisfp_exit); + +MODULE_DESCRIPTION("egis fingerprint driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/ets_bix_mmi/etxxx_fp.h b/drivers/input/misc/ets_bix_mmi/etxxx_fp.h new file mode 100755 index 000000000000..6870234ba7a9 --- /dev/null +++ b/drivers/input/misc/ets_bix_mmi/etxxx_fp.h @@ -0,0 +1,221 @@ +#ifndef _FP_LINUX_DIRVER_H_ +#define _FP_LINUX_DIRVER_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "20220110" +#define USE_PIN_CTRL +//#define PLATFORM_SPI +//#define MTK_PLATFORM +//#define LSI_PLATFORM +#define EGIS_DEBUG + +#define DEBUG_PRINT(fmt, arg...) \ + printk(KERN_DEBUG "[EGISFP]" fmt, ##arg) + +#define INFO_PRINT(fmt, arg...) \ + printk("[EGISFP]" fmt, ##arg) +#define ERROR_PRINT(fmt, arg...) \ + printk(KERN_ERR "[EGISFP]" fmt, ##arg) + +#define EGIS_FP_MAJOR 100 /* assigned */ +#define N_SPI_MINORS 32 /* ... up to 256 */ + +#define EGIS_DEV_NAME "esfp0" +//#define EGIS_INPUT_NAME "egis_fp" /*"egis_fp" */ +#define EGIS_INPUT_NAME "uinput-egis" /*"uinput-egis" */ + +#define EGIS_CHRD_DRIVER_NAME "egis_fp" +#define EGIS_CLASS_NAME "egis_fp" + +/* ------------------------- Opcode -------------------------------------*/ +#define FP_REGISTER_READ 0x01 +#define FP_REGISTER_WRITE 0x02 +#define FP_GET_ONE_IMG 0x03 +#define FP_SENSOR_RESET 0x04 +#define FP_POWER_ONOFF 0x05 +#define FP_SET_SPI_CLOCK 0x06 +#define FP_RESET_SET 0x07 +#define FP_WAKELOCK_ENABLE 0x08 +#define FP_WAKELOCK_DISABLE 0x09 +#define FP_GET_RESOURCE 0x11 +#define FP_FREE_RESOURCE 0x12 + +#define GET_IO_STUS 0x21 +#define SEND_NAVI_EVENT 0x31 +/* trigger signal initial routine*/ +#define INT_TRIGGER_INIT 0xa4 +/* trigger signal close routine*/ +#define INT_TRIGGER_CLOSE 0xa5 +/* read trigger status*/ +#define INT_TRIGGER_READ 0xa6 +/* polling trigger status*/ +#define INT_TRIGGER_POLLING 0xa7 +/* polling abort*/ +#define INT_TRIGGER_ABORT 0xa8 + +#define FP_FREE_GPIO 0xaf +#define FP_SPICLK_ENABLE 0xaa +#define FP_SPICLK_DISABLE 0xab +#define DELETE_DEVICE_NODE 0xac + +#define FP_SPIPIN_SETTING 0xad +#define FP_SPIPIN_PULLLOW 0xae + +#define FP_POWERSETUP 0xb0 +#define FP_WAKELOCK_TIMEOUT_ENABLE 0xb1 +#define FP_WAKELOCK_TIMEOUT_DISABLE 0xb2 +#define GET_SCREEN_ONOFF 0xb3 + +#define DRDY_IRQ_ENABLE 1 +#define DRDY_IRQ_DISABLE 0 + +#define EDGE_TRIGGER_FALLING 0x0 +#define EDGE_TRIGGER_RISING 0x1 +#define LEVEL_TRIGGER_LOW 0x2 +#define LEVEL_TRIGGER_HIGH 0x3 +#define WAKE_HOLD_TIME 2000 //ms + +#define SPI_DEFAULT_SPEED (1000000 * 20) //20M + +#define Tpwr_off_delay 50 +#define Tpwr_on_delay 3 +#define Rst_off_delay 10 +#define Rst_on_delay 3 + +#define EGIS_NAV_INPUT_UP KEY_UP +#define EGIS_NAV_INPUT_DOWN KEY_DOWN +#define EGIS_NAV_INPUT_LEFT KEY_LEFT +#define EGIS_NAV_INPUT_RIGHT KEY_RIGHT +#define EGIS_NAV_INPUT_CLICK KEY_PAGEUP +//#define EGIS_NAV_INPUT_DOUBLE_CLICK KEY_PAGEDOWN +#define EGIS_NAV_INPUT_DOUBLE_CLICK 622 +#define EGIS_NAV_INPUT_LONG_PRESS KEY_HOME +#define EGIS_NAV_INPUT_FINGER_DOWN 254 +#define EGIS_NAV_INPUT_FINGER_UP KEY_MENU + +enum egis_navi_event +{ + NAVI_EVENT_ON, + NAVI_EVENT_OFF, + NAVI_EVENT_UP, + NAVI_EVENT_DOWN, + NAVI_EVENT_LEFT, + NAVI_EVENT_RIGHT, + NAVI_EVENT_CLICK, + NAVI_EVENT_DOUBLE_CLICK, + NAVI_EVENT_LONG_PRESS, +}; + +struct egis_key_map_t +{ + unsigned int type; + unsigned int code; +}; + +struct egisfp_interrupt_desc_t +{ + int gpio; + int number; + char *name; + int int_count; + int irq_mode; + struct timer_list timer; + int finger_on; + int detect_period; + int detect_threshold; + int drdy_irq_flag; + int drdy_irq_abort; +}; + +struct egisfp_ioctl_cmd_t +{ + uint32_t int_mode; + uint32_t detect_period; + uint32_t detect_threshold; + uint32_t power_on; +}; + +/* ------------------------- Structure ------------------------------*/ +struct egisfp_dev_t +{ + dev_t devt; + spinlock_t spi_lock; + struct platform_device *dd; + struct list_head device_entry; + /* buffer is NULL unless this device is open (users > 0) */ + struct mutex buf_lock; + unsigned users; + u8 *buffer; + unsigned int irqPin; /* interrupt GPIO pin number */ + unsigned int rstPin; /* Reset GPIO pin number */ + unsigned int vdd_18v_Pin; /* Vdd GPIO pin number */ + unsigned int vcc_33v_Pin; /* Vcc GPIO pin number */ + struct input_dev *input_dev; + int navigation_enable; + int pars_dtsi_done; + int platforminit_done; + int gpio_irq; + spinlock_t irq_lock; + int request_irq_done; + struct wakeup_source wakeup_source_fp; + int ctrl_power; + int pwr_by_gpio; + int power_enable; + int screen_onoff; + int call_back_registered; + struct regulator *vcc; + struct regulator *vdd; + uint32_t regulator_current; + uint32_t regulator_voltage_max; + uint32_t regulator_voltage_min; + int clk_enabled; + struct egisfp_interrupt_desc_t fps_ints; + int clk_speed; + struct pinctrl *pinctrl; + struct pinctrl_state *spi_active; + struct pinctrl_state *spi_default; + struct pinctrl_state *reset_high; + struct pinctrl_state *reset_low; + struct pinctrl_state *vcc_high; + struct pinctrl_state *vcc_low; + struct pinctrl_state *irq_active; + struct notifier_block notifier; +#if IS_ENABLED(CONFIG_QCOM_PANEL_EVENT_NOTIFIER) + void *notifier_cookie; +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + struct drm_panel *active_panel; +#endif +}; + +int egisfp_platforminit(struct egisfp_dev_t *egis_dev); +int egisfp_platformfree(struct egisfp_dev_t *egis_dev); +/* interrupt init */ +int egisfp_interrupt_init(struct egisfp_dev_t *egis_dev, int int_mode, int detect_period, int detect_threshold); +/* interrupt free */ +int egisfp_interrupt_free(struct egisfp_dev_t *egis_dev); +void egisfp_interrupt_abort(struct egisfp_dev_t *egis_dev); +unsigned int egisfp_interrupt_poll(struct file *file, struct poll_table_struct *wait); +int egisfp_parse_dt(struct egisfp_dev_t *egis_dev); +int do_egisfp_power_onoff(struct egisfp_dev_t *egis_dev, struct egisfp_ioctl_cmd_t *ioctl_data); +int do_egisfp_reset(struct egisfp_dev_t *egis_dev); +int do_egisfp_reset_set(struct egisfp_dev_t *egis_dev, int reset_high_low); +int egisfp_check_ioctl_permission(struct egisfp_dev_t *egis_dev, unsigned int cmd); +int egisfp_set_screen_onoff(int on); +#endif diff --git a/drivers/input/misc/ets_bix_mmi/generator_condition.txt b/drivers/input/misc/ets_bix_mmi/generator_condition.txt new file mode 100755 index 000000000000..370d0ecf2e67 --- /dev/null +++ b/drivers/input/misc/ets_bix_mmi/generator_condition.txt @@ -0,0 +1 @@ +lenovo;EC617;ET6XX;QCOM_PLATFORM;USE_PIN_CTRL;EGIS_DEBUG;CONFIG_DRM diff --git a/drivers/input/misc/ets_fps_mmi/Android.mk b/drivers/input/misc/ets_fps_mmi/Android.mk new file mode 100644 index 000000000000..c9746409bc89 --- /dev/null +++ b/drivers/input/misc/ets_fps_mmi/Android.mk @@ -0,0 +1,33 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +ETS_FPS_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +ETS_FPS_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + + + +ifeq ($(FPS_NAVI_ENABLE),true) + ifneq ($(FPS_SLIDE_NAV),true) + KERNEL_CFLAGS += CONFIG_INPUT_EGISTEC_FPS_NAVI=y + KERNEL_CFLAGS += CONFIG_INPUT_EGISTEC_FPS_NAVI_HORIZON=y + KERNEL_CFLAGS += CONFIG_INPUT_EGISTEC_FPS_NAVI_VERTICAL=y + endif +endif + +ifeq ($(EGIS_DISPLAY_SPEED_UP), true) + KERNEL_CFLAGS += CONFIG_EGIS_DISPLAY_SPEED_UP=y +endif + +ifeq ($(FPS_ENABLE_DOUBLE_TAP), true) + KERNEL_CFLAGS += CONFIG_NAV_DOUBLE_TAP=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := ets_fps_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(ETS_FPS_MMI_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/misc/ets_fps_mmi/Kbuild b/drivers/input/misc/ets_fps_mmi/Kbuild new file mode 100644 index 000000000000..43d13ca1ff94 --- /dev/null +++ b/drivers/input/misc/ets_fps_mmi/Kbuild @@ -0,0 +1,44 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/ets_fps_mmi + +ifneq ($(filter m y,$(CONFIG_INPUT_EGISTEC_FPS_NAVI_HORIZON)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_EGISTEC_FPS_NAVI_HORIZON +endif + +ets_fps_mmi-y += ets_fps.o + +ifneq ($(filter m y,$(CONFIG_INPUT_EGISTEC_FPS_NAVI)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_EGISTEC_FPS_NAVI + ets_fps_mmi-y += ets_navi_input.o +endif +ifneq ($(filter m y,$(CONFIG_INPUT_EGISTEC_FPS_NAVI_VERTICAL)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_EGISTEC_FPS_NAVI_VERTICAL +endif +ifeq ($(TARGET_BUILD_VARIANT),userdebug) + EXTRA_CFLAGS += -DFP_SPI_DEBUG +endif + +obj-m += ets_fps_mmi.o + +ifneq ($(filter doha%, $(TARGET_PRODUCT)),) +EXTRA_CFLAGS += -DCONFIG_INPUT_EGISTEC_FPS_POSE=0 +else +EXTRA_CFLAGS += -DCONFIG_INPUT_EGISTEC_FPS_POSE=270 +endif + +ifneq ($(filter sofia%, $(TARGET_PRODUCT)),) +EXTRA_CFLAGS += -DCONFIG_INPUT_EGISTEC_FPS_POSE=0 +endif +ifneq ($(filter rav%, $(TARGET_PRODUCT)),) +EXTRA_CFLAGS += -DCONFIG_INPUT_EGISTEC_FPS_POSE=0 +endif + +ifneq ($(filter m y, $(CONFIG_EGIS_DISPLAY_SPEED_UP)),) +EXTRA_CFLAGS += -DCONFIG_EGIS_DISPLAY_SPEED_UP +endif + +ifneq ($(filter m y, $(CONFIG_NAV_DOUBLE_TAP)),) +EXTRA_CFLAGS += -DCONFIG_NAV_DOUBLE_TAP +endif diff --git a/drivers/input/misc/ets_fps_mmi/Makefile b/drivers/input/misc/ets_fps_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/misc/ets_fps_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/misc/ets_fps_mmi/ets_fps.c b/drivers/input/misc/ets_fps_mmi/ets_fps.c new file mode 100644 index 000000000000..c62cccf5abd3 --- /dev/null +++ b/drivers/input/misc/ets_fps_mmi/ets_fps.c @@ -0,0 +1,1253 @@ +/* + * Simple synchronous userspace interface to SPI devices + * + * Copyright (C) 2006 SWAPP + * Andrea Paterniani + * Copyright (C) 2007 David Brownell (simplification, cleanup) + * + * 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. + */ + +/* +* +* ets_fps.c +* Date: 2016/03/16 +* Version: 0.9.0.1 +* Revise Date: 2017/12/05 +* Copyright (C) 2007-2018 Egis Technology Inc. +* +*/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#ifdef CONFIG_OF +#include +#include +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +#include +#else +#include +#endif +#ifdef CONFIG_HAS_WAKELOCK +#include +#else +#include +#endif + +#include + +#include "ets_fps.h" +#include "ets_navi_input.h" + +#define EDGE_TRIGGER_FALLING 0x0 +#define EDGE_TRIGGER_RAISING 0x1 +#define LEVEL_TRIGGER_LOW 0x2 +#define LEVEL_TRIGGER_HIGH 0x3 +#ifdef CONFIG_INPUT_EGISTEC_FPS_NAVI +#define EGIS_NAVI_INPUT 1 /* 1:open ; 0:close */ +#else +#define EGIS_NAVI_INPUT 0 +#endif +#ifdef CONFIG_HAS_WAKELOCK +static struct wake_lock ets_wake_lock; +#else +static struct wakeup_source *ets_wake_lock; +#endif +/* + * FPS interrupt table + */ + +struct interrupt_desc fps_ints = {0, 0, "BUT0", 0}; + +static unsigned int bufsiz = 4096; + +static int gpio_irq; +static int request_irq_done; +/* int t_mode = 255; */ + +#define EDGE_TRIGGER_FALLING 0x0 +#define EDGE_TRIGGER_RISING 0x1 +#define LEVEL_TRIGGER_LOW 0x2 +#define LEVEL_TRIGGER_HIGH 0x3 + + +struct ioctl_cmd { +int int_mode; +int detect_period; +int detect_threshold; +}; +/* To be compatible with fpc driver */ +#ifndef CONFIG_SENSORS_FPC_1020 +static struct FPS_data { + unsigned int enabled; + unsigned int state; + struct blocking_notifier_head nhead; +} *fpsData; + +struct FPS_data *FPS_init(void) +{ + struct FPS_data *mdata; + if (!fpsData) { + mdata = kzalloc( + sizeof(struct FPS_data), GFP_KERNEL); + if (mdata) { + BLOCKING_INIT_NOTIFIER_HEAD(&mdata->nhead); + pr_debug("%s: FPS notifier data structure init-ed\n", __func__); + } + fpsData = mdata; + } + return fpsData; +} +int FPS_register_notifier(struct notifier_block *nb, + unsigned long stype, bool report) +{ + int error; + struct FPS_data *mdata = fpsData; + + mdata = FPS_init(); + if (!mdata) + return -ENODEV; + mdata->enabled = (unsigned int)stype; + pr_debug("%s: FPS sensor %lu notifier enabled\n", __func__, stype); + + error = blocking_notifier_chain_register(&mdata->nhead, nb); + if (!error && report) { + int state = mdata->state; + /* send current FPS state on register request */ + blocking_notifier_call_chain(&mdata->nhead, + stype, (void *)&state); + pr_debug("%s: FPS reported state %d\n", __func__, state); + } + return error; +} +EXPORT_SYMBOL_GPL(FPS_register_notifier); + +int FPS_unregister_notifier(struct notifier_block *nb, + unsigned long stype) +{ + int error; + struct FPS_data *mdata = fpsData; + + if (!mdata) + return -ENODEV; + + error = blocking_notifier_chain_unregister(&mdata->nhead, nb); + pr_debug("%s: FPS sensor %lu notifier unregister\n", __func__, stype); + + if (!mdata->nhead.head) { + mdata->enabled = 0; + pr_debug("%s: FPS sensor %lu no clients\n", __func__, stype); + } + + return error; +} +EXPORT_SYMBOL_GPL(FPS_unregister_notifier); + +void FPS_notify(unsigned long stype, int state) +{ + struct FPS_data *mdata = fpsData; + + pr_debug("%s: Enter", __func__); + + if (!mdata) { + pr_err("%s: FPS notifier not initialized yet\n", __func__); + return; + } + + pr_debug("%s: FPS current state %d -> (0x%x)\n", __func__, + mdata->state, state); + + if (mdata->enabled && mdata->state != state) { + mdata->state = state; + blocking_notifier_call_chain(&mdata->nhead, + stype, (void *)&state); + pr_debug("%s: FPS notification sent\n", __func__); + } else if (!mdata->enabled) { + pr_err("%s: !mdata->enabled", __func__); + } else { + pr_err("%s: mdata->state==state", __func__); + } +} +#endif + +DECLARE_BITMAP(minors, N_SPI_MINORS); +LIST_HEAD(device_list); +static DEFINE_MUTEX(device_list_lock); + +/* ------------------------------ Interrupt -----------------------------*/ +/* + * Interrupt description + */ + +#define FP_INT_DETECTION_PERIOD 10 +#define FP_DETECTION_THRESHOLD 10 +/* struct interrupt_desc fps_ints; */ +static DECLARE_WAIT_QUEUE_HEAD(interrupt_waitq); +/* + * FUNCTION NAME. + * interrupt_timer_routine + * + * FUNCTIONAL DESCRIPTION. + * basic interrupt timer inital routine + * + * ENTRY PARAMETERS. + * gpio - gpio address + * + * EXIT PARAMETERS. + * Function Return + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0) +static void interrupt_timer_routine(struct timer_list *t) +{ + struct interrupt_desc *bdata = from_timer (bdata, t, timer); + + DEBUG_PRINT("FPS interrupt count = %d", bdata->int_count); + if (bdata->int_count >= bdata->detect_threshold) { + bdata->finger_on = 1; + DEBUG_PRINT("FPS triggered !!!!!!!\n"); + } else { + DEBUG_PRINT("FPS not triggered !!!!!!!\n"); + } + + bdata->int_count = 0; + wake_up_interruptible(&interrupt_waitq); +} +#else +void interrupt_timer_routine(unsigned long _data) +{ + struct interrupt_desc *bdata = (struct interrupt_desc *)_data; + + DEBUG_PRINT("FPS interrupt count = %d", bdata->int_count); + if (bdata->int_count >= bdata->detect_threshold) { + bdata->finger_on = 1; + DEBUG_PRINT("FPS triggered !!!!!!!\n"); + } else { + DEBUG_PRINT("FPS not triggered !!!!!!!\n"); + } + + bdata->int_count = 0; + wake_up_interruptible(&interrupt_waitq); +} +#endif + +#ifdef CONFIG_EGIS_DISPLAY_SPEED_UP +extern void ext_dsi_display_early_power_on(void); +static bool is_auth_ready = false; +#endif + +static irqreturn_t fp_eint_func(int irq, void *dev_id) +{ + + fps_ints.finger_on = 1; + wake_up_interruptible(&interrupt_waitq); + DEBUG_PRINT("etspi: %s int trigger\n", __func__); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&ets_wake_lock, msecs_to_jiffies(1500)); +#else + PM_WAKEUP_EVENT(ets_wake_lock, 1500); +#endif +#ifdef CONFIG_EGIS_DISPLAY_SPEED_UP + if (is_auth_ready) + pr_info("etspi: already in authentication mode"); + else + pr_info("etspi: not in authentication mode"); +#endif + return IRQ_HANDLED; +} + +static irqreturn_t fp_eint_func_ll(int irq, void *dev_id) +{ + pr_debug("etspi: fp_eint_func_ll\n"); + fps_ints.finger_on = 1; + /* fps_ints.int_count = 0; */ + disable_irq_nosync(gpio_irq); + fps_ints.drdy_irq_flag = DRDY_IRQ_DISABLE; + wake_up_interruptible(&interrupt_waitq); + /* printk_ratelimited(KERN_WARNING "----------- zq fp fp_eint_func ,fps_ints.int_count=%d",fps_ints.int_count);*/ +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&ets_wake_lock, msecs_to_jiffies(1500)); +#else + PM_WAKEUP_EVENT(ets_wake_lock, 1500); +#endif + return IRQ_RETVAL(IRQ_HANDLED); +} + +/* + * FUNCTION NAME. + * Interrupt_Init + * + * FUNCTIONAL DESCRIPTION. + * button initial routine + * + * ENTRY PARAMETERS. + * int_mode - determine trigger mode + * EDGE_TRIGGER_FALLING 0x0 + * EDGE_TRIGGER_RAISING 0x1 + * LEVEL_TRIGGER_LOW 0x2 + * LEVEL_TRIGGER_HIGH 0x3 + * + * EXIT PARAMETERS. + * Function Return int + */ + +int Interrupt_Init(struct etspi_data *etspi, int int_mode, int detect_period, int detect_threshold) +{ + + int err = 0; + int status = 0; + + fps_ints.detect_period = detect_period; + fps_ints.detect_threshold = detect_threshold; + fps_ints.int_count = 0; + fps_ints.finger_on = 0; + + + if (request_irq_done == 0) { + gpio_irq = gpio_to_irq(etspi->irqPin); + if (gpio_irq < 0) { + DEBUG_PRINT("etspi: %s gpio_to_irq failed\n", __func__); + status = gpio_irq; + goto done; + } + + DEBUG_PRINT("etspi:Interrupt_Init flag current: %d disable:\ + %d enable: %d\n", + fps_ints.drdy_irq_flag, DRDY_IRQ_DISABLE, DRDY_IRQ_ENABLE); + /* t_mode = int_mode; */ + if (int_mode == EDGE_TRIGGER_RISING) { + DEBUG_PRINT("etspi:%s EDGE_TRIGGER_RISING\n", __func__); + err = request_irq(gpio_irq, fp_eint_func, IRQ_TYPE_EDGE_RISING,\ + "fp_detect-eint", etspi); + if (err) { + pr_err("etspi:request_irq failed==========%s,%d\n", __func__, __LINE__); + } + } else if (int_mode == EDGE_TRIGGER_FALLING) { + DEBUG_PRINT("etspi:%s EDGE_TRIGGER_FALLING\n", __func__); + err = request_irq(gpio_irq, fp_eint_func, IRQ_TYPE_EDGE_FALLING,\ + "fp_detect-eint", etspi); + if (err) { + pr_err("etspi:request_irq failed==========%s,%d\n",\ + __func__, __LINE__); + } + } else if (int_mode == LEVEL_TRIGGER_LOW) { + DEBUG_PRINT("etspi:%s LEVEL_TRIGGER_LOW\n", __func__); + err = request_irq(gpio_irq, fp_eint_func_ll, IRQ_TYPE_LEVEL_LOW,\ + "fp_detect-eint", etspi); + if (err) { + pr_err("etspi:request_irq failed==========%s,%d\n", __func__, __LINE__); + } + } else if (int_mode == LEVEL_TRIGGER_HIGH) { + DEBUG_PRINT("etspi:%s LEVEL_TRIGGER_HIGH\n", __func__); + err = request_irq(gpio_irq, fp_eint_func_ll, IRQ_TYPE_LEVEL_HIGH,\ + "fp_detect-eint", etspi); + if (err) { + pr_err("etspi:request_irq failed==========%s,%d\n", __func__, __LINE__); + } + } + DEBUG_PRINT("etspi:Interrupt_Init:gpio_to_irq return: %d\n", gpio_irq); + DEBUG_PRINT("etspi:Interrupt_Init:request_irq return: %d\n", err); + /* disable_irq_nosync(gpio_irq); */ + fps_ints.drdy_irq_flag = DRDY_IRQ_ENABLE; + enable_irq_wake(gpio_irq); + request_irq_done = 1; + } + + + if (fps_ints.drdy_irq_flag == DRDY_IRQ_DISABLE) { + fps_ints.drdy_irq_flag = DRDY_IRQ_ENABLE; + enable_irq_wake(gpio_irq); + enable_irq(gpio_irq); + DEBUG_PRINT("etspi: Interrupt_Init: %s irq/done:%d %d mode:%d\ + period:%d \threshold:%d \n", __func__, gpio_irq, request_irq_done,\ + int_mode, detect_period, detect_threshold); + } +done: + return 0; +} + +/* + * FUNCTION NAME. + * Interrupt_Free + * + * FUNCTIONAL DESCRIPTION. + * free all interrupt resource + * + * EXIT PARAMETERS. + * Function Return int + */ + +int Interrupt_Free(struct etspi_data *etspi) +{ + DEBUG_PRINT("etspi: %s\n", __func__); + fps_ints.finger_on = 0; + + if (fps_ints.drdy_irq_flag == DRDY_IRQ_ENABLE) { + DEBUG_PRINT("etspi: %s (DISABLE IRQ)\n", __func__); + disable_irq_nosync(gpio_irq); + /* disable_irq(gpio_irq); */ + del_timer_sync(&fps_ints.timer); + fps_ints.drdy_irq_flag = DRDY_IRQ_DISABLE; + } + return 0; +} + + + + +/* + * FUNCTION NAME. + * fps_interrupt_re d + * + * FUNCTIONAL DESCRIPTION. + * FPS interrupt read status + * + * ENTRY PARAMETERS. + * wait poll table structure + * + * EXIT PARAMETERS. + * Function Return int + */ + +unsigned int fps_interrupt_poll( +struct file *file, +struct poll_table_struct *wait) +{ + unsigned int mask = 0; + + if (fps_ints.finger_on) { + mask |= POLLIN | POLLRDNORM; + } else { + poll_wait(file, &interrupt_waitq, wait); + if (fps_ints.finger_on) { + mask |= POLLIN | POLLRDNORM; + } + } + return mask; +} + +void fps_interrupt_abort(void) +{ + DEBUG_PRINT("etspi:%s\n", __func__); + fps_ints.finger_on = 0; + wake_up_interruptible(&interrupt_waitq); +} + +static void etspi_power_set(struct etspi_data *etspi, unsigned char power_state) +{ + int status = 0; + DEBUG_PRINT("etspi:%s\n", __func__); + if (!IS_ERR_OR_NULL(etspi->regulator_3v3_io)) { + if (power_state == 0) { + status = regulator_disable(etspi->regulator_3v3_io); + DEBUG_PRINT("etspi: regulator_3v3_io disable status %d\n", status); + } else { + status = regulator_enable(etspi->regulator_3v3_io); + DEBUG_PRINT("etspi: regulator_3v3_io enable status %d\n", status); + } + } + if (gpio_is_valid(etspi->vcc_33v_Pin)) { + if (power_state == 0) { + gpio_direction_output(etspi->vcc_33v_Pin, 0); + pr_info("etspi:power gpio set 0,%s,%d\n", __func__, __LINE__); + } else { + gpio_direction_output(etspi->vcc_33v_Pin, 1); + pr_info("etspi:power gpio set 1,%s,%d\n", __func__, __LINE__); + } + } + +} + +/*-------------------------------------------------------------------------*/ + +static void etspi_reset(struct etspi_data *etspi) +{ + DEBUG_PRINT("etspi:%s\n", __func__); + gpio_set_value(etspi->rstPin, 0); + msleep(30); + gpio_set_value(etspi->rstPin, 1); + msleep(20); +} + +static ssize_t etspi_read(struct file *filp, + char __user *buf, + size_t count, + loff_t *f_pos) +{ + /*Implement by vendor if needed*/ + return 0; +} + +static ssize_t etspi_write(struct file *filp, + const char __user *buf, + size_t count, + loff_t *f_pos) +{ + /*Implement by vendor if needed*/ + return 0; +} +static ssize_t etspi_enable_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int state = (*buf == '1') ? 1 : 0; + FPS_notify(0xbeef, state); + DEBUG_PRINT("%s state = %d\n", __func__, state); + return 1; +} +static DEVICE_ATTR(etspi_enable, S_IWUSR | S_IWGRP, NULL, etspi_enable_set); +static struct attribute *attributes[] = { + &dev_attr_etspi_enable.attr, + NULL +}; + +static const struct attribute_group attribute_group = { + .attrs = attributes, +}; + +static int disp_panel_on = -1; /* -1 for invalid, 0 for display-off, 1 for display-on */ + +static long etspi_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int retval = 0; + unsigned char power_state = 0; + struct etspi_data *etspi; + struct ioctl_cmd data; + + memset(&data, 0, sizeof(data)); + + pr_debug("etspi: %s, cmd=%d \n", __func__, cmd); + + etspi = filp->private_data; + + switch (cmd) { + case INT_TRIGGER_INIT: + DEBUG_PRINT("etspi:fp_ioctl >>> fp Trigger function init\n"); + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) { + retval = -EFAULT; + pr_err("etspi:INT_TRIGGER_INIT failed,%s,%d\n", __func__, __LINE__); + goto done; + } + retval = Interrupt_Init(etspi, data.int_mode, data.detect_period, data.detect_threshold); + DEBUG_PRINT("etspi:fp_ioctl trigger init = %x\n", retval); + break; + + case FP_SENSOR_RESET: + DEBUG_PRINT("etspi:fp_ioctl ioc->opcode == FP_SENSOR_RESET --"); + etspi_reset(etspi); + goto done; + case INT_TRIGGER_CLOSE: + pr_info("etspi: skip interrupt close request from HAL."); + goto done; + case FP_POWER_ONOFF: + if (copy_from_user(&power_state, (int __user *)arg, sizeof(power_state))) { + retval = -EFAULT; + pr_err("etspi:FP_POWER_ONOFF failed,%s,%d\n", __func__, __LINE__); + goto done; + } + DEBUG_PRINT("etspi:fp_ioctl >>> FP_POWER_ONOFF %d",power_state); + etspi_power_set(etspi, power_state); + goto done; + case INT_TRIGGER_ABORT: + DEBUG_PRINT("etspi:fp_ioctl <<< fp Trigger function abort\n"); + fps_interrupt_abort(); + goto done; +#ifdef CONFIG_EGIS_DISPLAY_SPEED_UP + case NOTIFY_AUTH_STATUS: + pr_info("etspi: - notify auth status: %lu", arg); + if (arg == AUTH_STATUS_NONE) + is_auth_ready = false; + else if (arg == AUTH_STATUS_READY) + is_auth_ready = true; + else if (arg == AUTH_STATUS_FINGER_DETECTED) { + if (is_auth_ready) { + pr_info("etspi: call ext_dsi_display_early_power_on()"); + ext_dsi_display_early_power_on(); + } + } + goto done; +#endif + case GET_DISP_PANEL_STATUS: + if (copy_to_user((int __user *) arg, &disp_panel_on, sizeof(disp_panel_on))) + pr_err("%s fail to copy", __func__); + goto done; + default: + retval = -ENOTTY; + break; + } +done: + return retval; +} + +#ifdef CONFIG_COMPAT +static long etspi_compat_ioctl(struct file *filp, + unsigned int cmd, + unsigned long arg) +{ + return etspi_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#else +#define etspi_compat_ioctl NULL +#endif /* CONFIG_COMPAT */ + +static int etspi_open(struct inode *inode, struct file *filp) +{ + struct etspi_data *etspi; + int status = -ENXIO; + + DEBUG_PRINT("%s\n", __func__); + + mutex_lock(&device_list_lock); + + list_for_each_entry(etspi, &device_list, device_entry) { + if (etspi->devt == inode->i_rdev) { + status = 0; + break; + } + } + if (status == 0) { + if (etspi->buffer == NULL) { + etspi->buffer = kmalloc(bufsiz, GFP_KERNEL); + if (etspi->buffer == NULL) { + /* dev_dbg(&etspi->spi->dev, "open/ENOMEM\n"); */ + status = -ENOMEM; + } + } + if (status == 0) { + etspi->users++; + filp->private_data = etspi; + nonseekable_open(inode, filp); + } + } else { + pr_info("%s nothing for minor %d\n" + , __func__, iminor(inode)); + } + mutex_unlock(&device_list_lock); + return status; +} + +static int etspi_release(struct inode *inode, struct file *filp) +{ + struct etspi_data *etspi; + + DEBUG_PRINT("%s\n", __func__); + + mutex_lock(&device_list_lock); + etspi = filp->private_data; + filp->private_data = NULL; + + /* last close? */ + etspi->users--; + if (etspi->users == 0) { + int dofree; + + kfree(etspi->buffer); + etspi->buffer = NULL; + + /* ... after we unbound from the underlying device? */ + spin_lock_irq(&etspi->spi_lock); + dofree = (etspi->spi == NULL); + spin_unlock_irq(&etspi->spi_lock); + + if (dofree) + kfree(etspi); + } + mutex_unlock(&device_list_lock); + return 0; + +} + + + +int etspi_platformInit(struct etspi_data *etspi, bool init) +{ + int status = 0; + DEBUG_PRINT("%s\n", __func__); + + if (!etspi) { + pr_err("%s etspi_data is empty.\n", __func__); + return -EBUSY; + } + + if (init) { + if (gpio_is_valid(etspi->vdd_18v_Pin)) { + /* initial 18V power pin */ + status = gpio_request(etspi->vdd_18v_Pin, "18v-gpio"); + if (status < 0) { + pr_err("%s gpio_requset vdd_18v_Pin failed\n", + __func__); + goto etspi_platformInit_18v_failed; + } + gpio_direction_output(etspi->vdd_18v_Pin, 1); + if (status < 0) { + pr_err("%s gpio_direction_output vdd_18v_Pin failed\n", + __func__); + status = -EBUSY; + goto etspi_platformInit_18v_set_failed; + } + + gpio_set_value(etspi->vdd_18v_Pin, 1); + pr_info("etspi: vdd_18v_Pin set to high\n"); + mdelay(1); + } + if (gpio_is_valid(etspi->vcc_33v_Pin)) { + /* initial 33V power pin */ + status = gpio_request(etspi->vcc_33v_Pin, "33v-gpio"); + if (status < 0) { + pr_err("%s gpio_requset vcc_33v_Pin failed\n", + __func__); + goto etspi_platformInit_33v_gpio_failed; + } + gpio_direction_output(etspi->vcc_33v_Pin, 1); + if (status < 0) { + pr_err("%s gpio_direction_output vcc_33v_Pin failed\n", + __func__); + status = -EBUSY; + goto etspi_platformInit_33v_gpio_set_failed; + } + gpio_set_value(etspi->vcc_33v_Pin, 1); + pr_info("etspi: vcc_33v_Pin set to high\n"); + mdelay(2); + } + if (!IS_ERR_OR_NULL(etspi->regulator_3v3_io)) { + /*status = regulator_set_voltage(etspi->regulator_3v3_io, 3000000, + 3000000); + if (status < 0) { + pr_err("etspi: regulator_3v3_io set voltage failed\n"); + status = -EBUSY; + goto etspi_platformInit_33v_set_failed; + } + pr_err("etspi: regulator_3v3_io set voltage status %d\n",status);*/ + + status = regulator_enable(etspi->regulator_3v3_io); + if (status < 0) { + pr_err("etspi: regulator_3v3_io enable failed\n"); + status = -EBUSY; + goto etspi_platformInit_33v_set_failed; + } + pr_err("etspi: regulator_3v3_io enable status %d\n",status); + mdelay(2); + } + /* Initial Reset Pin*/ + status = gpio_request(etspi->rstPin, "reset-gpio"); + if (status < 0) { + pr_err("%s gpio_requset etspi_Reset failed\n", + __func__); + goto etspi_platformInit_rst_failed; + } + gpio_direction_output(etspi->rstPin, 1); + if (status < 0) { + pr_err("%s gpio_direction_output Reset failed\n", + __func__); + status = -EBUSY; + goto etspi_platformInit_rst_set_failed; + } + /* gpio_set_value(etspi->rstPin, 1); */ + pr_info("etspi: reset to high\n"); + + /* Initial IRQ Pin*/ + status = gpio_request(etspi->irqPin, "irq-gpio"); + if (status < 0) { + pr_err("%s gpio_request etspi_irq failed\n", + __func__); + goto etspi_platformInit_irq_failed; + } + status = gpio_direction_input(etspi->irqPin); + if (status < 0) { + pr_err("%s gpio_direction_input IRQ failed\n", + __func__); + goto etspi_platformInit_set_irq_failed; + } + + DEBUG_PRINT("ets320: %s successful status=%d\n", __func__, status); + return 0; + } + +etspi_platformInit_set_irq_failed: + gpio_free(etspi->irqPin); +etspi_platformInit_irq_failed: +etspi_platformInit_rst_set_failed: + gpio_free(etspi->rstPin); +etspi_platformInit_rst_failed: +etspi_platformInit_33v_set_failed: + if (!IS_ERR(etspi->regulator_3v3_io)) + regulator_put(etspi->regulator_3v3_io); +etspi_platformInit_33v_gpio_set_failed: + if (gpio_is_valid(etspi->vcc_33v_Pin)) + gpio_free(etspi->vcc_33v_Pin); +etspi_platformInit_33v_gpio_failed: +etspi_platformInit_18v_set_failed: + if (gpio_is_valid(etspi->vdd_18v_Pin)) + gpio_free(etspi->vdd_18v_Pin); +etspi_platformInit_18v_failed: + return status; +} + +static int etspi_parse_dt(struct device *dev, + struct etspi_data *data) +{ + struct device_node *np = dev->of_node; + int errorno = 0; + int gpio; + gpio = of_get_named_gpio(np, "egistec,gpio_rst", 0); + if (gpio < 0) { + errorno = gpio; + goto dt_exit; + } else { + data->rstPin = gpio; + DEBUG_PRINT("%s: sleepPin=%d\n", __func__, data->rstPin); + } + gpio = of_get_named_gpio(np, "egistec,gpio_irq", 0); + if (gpio < 0) { + errorno = gpio; + goto dt_exit; + } else { + data->irqPin = gpio; + DEBUG_PRINT("%s: drdyPin=%d\n", __func__, data->irqPin); + } + + gpio = of_get_named_gpio(np, "egistec,gpio_ldo3p3_en", 0); + if (gpio < 0) { + data->vcc_33v_Pin = ARCH_NR_GPIOS; + pr_warn("%s: 3.3v power pin is not used\n", __func__); + } else { + data->vcc_33v_Pin = gpio; + pr_info("%s: 3.3v power pin=%d\n", __func__, data->vcc_33v_Pin); + } + gpio = of_get_named_gpio(np, "egistec,gpio_ldo1p8_en", 0); + if (gpio < 0) { + data->vdd_18v_Pin = ARCH_NR_GPIOS; + pr_warn("%s: 1.8v power pin is not used\n", __func__); + } else { + data->vdd_18v_Pin = gpio; + pr_info("%s: 18v power pin=%d\n", __func__, data->vdd_18v_Pin); + } + data->regulator_3v3_io = regulator_get(dev, "vcc_3v3_io"); + if (IS_ERR_OR_NULL(data->regulator_3v3_io)) { + data->regulator_3v3_io = NULL; + pr_err("%s: vcc_3v3_io regulator is not used\n", __func__); + } + DEBUG_PRINT("%s is successful\n", __func__); + return errorno; +dt_exit: + pr_err("%s is failed\n", __func__); + return errorno; +} + +static const struct file_operations etspi_fops = { + .owner = THIS_MODULE, + .write = etspi_write, + .read = etspi_read, + .unlocked_ioctl = etspi_ioctl, + .compat_ioctl = etspi_compat_ioctl, + .open = etspi_open, + .release = etspi_release, + .llseek = no_llseek, + .poll = fps_interrupt_poll +}; + +/*-------------------------------------------------------------------------*/ +static int etspi_probe(struct platform_device *pdev); +static int etspi_remove(struct platform_device *pdev); + +static struct of_device_id etspi_match_table[] = { + { .compatible = "egistec,et516",}, + {}, +}; +MODULE_DEVICE_TABLE(of, etspi_match_table); + +/* fp_id is used only when dual sensor need to be support */ +#if 0 +static struct of_device_id fp_id_match_table[] = { + { .compatible = "fp_id,fp_id",}, + {}, +}; +MODULE_DEVICE_TABLE(of, fp_id_match_table); +#endif + + + +static struct platform_driver etspi_driver = { + .driver = { + .name = "et320", + .owner = THIS_MODULE, + .of_match_table = etspi_match_table, + }, + .probe = etspi_probe, + .remove = etspi_remove, +}; +/* remark for dual sensors */ +/* module_platform_driver(etspi_driver); */ + + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "egis"); +} +static DEVICE_ATTR_RO(vendor); + +static struct attribute *class_attributes[] = { + &dev_attr_vendor.attr, + NULL +}; + +static const struct attribute_group class_attribute_group = { + .attrs = class_attributes, +}; + + +#define MAX_INSTANCE 5 +#define MAJOR_BASE 32 +static int etspi_create_sysfs(struct etspi_data *etspi, bool create) { + struct device *dev = &etspi->spi->dev; + static struct device *class_dev; + static struct class *fingerprint_class; + static dev_t dev_no; + int rc = 0; + + if (create) { + rc = alloc_chrdev_region(&dev_no, MAJOR_BASE, MAX_INSTANCE, "egis"); + if (rc < 0) { + dev_err(dev, "%s alloc fingerprint class device MAJOR failed.\n", __func__); + goto ALLOC_REGION; + } + if (!fingerprint_class) { + fingerprint_class = class_create(THIS_MODULE, "fingerprint"); + if (IS_ERR(fingerprint_class)) { + dev_err(dev, "%s create fingerprint class failed.\n", __func__); + rc = PTR_ERR(fingerprint_class); + fingerprint_class = NULL; + goto CLASS_CREATE_ERR; + } + } + class_dev = device_create(fingerprint_class, NULL, MAJOR(dev_no), + etspi, "et320"); + if (IS_ERR(class_dev)) { + dev_err(dev, "%s create fingerprint class device failed.\n", __func__); + rc = PTR_ERR(class_dev); + class_dev = NULL; + goto DEVICE_CREATE_ERR; + } + rc = sysfs_create_group(&class_dev->kobj, &class_attribute_group); + if (rc) { + dev_err(dev, "could not create sysfs\n"); + goto CREATE_SYSFS_ERR; + } + return 0; + } + + sysfs_remove_group(&class_dev->kobj, &class_attribute_group); +CREATE_SYSFS_ERR: + device_destroy(fingerprint_class, MAJOR(dev_no)); + class_dev = NULL; +DEVICE_CREATE_ERR: + class_destroy(fingerprint_class); + fingerprint_class = NULL; +CLASS_CREATE_ERR: + unregister_chrdev_region(dev_no, 1); +ALLOC_REGION: + return rc; +} +static int etspi_create_device(struct etspi_data *etspi, bool create) { + static struct class *etspi_class; + static struct device *fdev; + static unsigned long minor; + struct platform_device *pdev = etspi->spi; + int rc = 0; + + if (create) { + rc = register_chrdev(ET320_MAJOR, + etspi_driver.driver.name, &etspi_fops); + if (rc < 0) { + dev_err(&pdev->dev, "%s register_chrdev error.\n", __func__); + return rc; + } + etspi_class = class_create(THIS_MODULE, etspi_driver.driver.name); + if (IS_ERR(etspi_class)) { + rc = PTR_ERR(etspi_class); + dev_err(&pdev->dev, "%s class_create error.\n", __func__); + goto CREATE_DEVICE_CLASS_CREATE_FAILED; + } + /* + * If we can allocate a minor number, hook up this device. + * Reusing minors is fine so long as udev or mdev is working. + */ + minor = find_first_zero_bit(minors, N_SPI_MINORS); + if (minor >= N_SPI_MINORS) { + dev_err(&pdev->dev, "no minor number available!\n"); + rc = -ENODEV; + goto CREATE_DEVICE_CLASS_NO_MINOR; + } + etspi->devt = MKDEV(ET320_MAJOR, minor); + fdev = device_create(etspi_class, &pdev->dev, etspi->devt, etspi, "esfp0"); + rc = IS_ERR(fdev) ? PTR_ERR(fdev) : 0; + if (rc < 0) { + dev_err(&pdev->dev, "create esfp0 failed\n"); + goto CREATE_DEVICE_CLASS_CREATE_DEVICE_FAILED; + } + set_bit(minor, minors); + mutex_lock(&device_list_lock); + list_add(&etspi->device_entry, &device_list); + mutex_unlock(&device_list_lock); +#if EGIS_NAVI_INPUT + /* + * William Add. + */ + sysfs_egis_init(etspi); + uinput_egis_init(etspi); +#endif + return 0; + } + + +#if EGIS_NAVI_INPUT + uinput_egis_destroy(etspi); + sysfs_egis_destroy(etspi); +#endif + mutex_lock(&device_list_lock); + list_del(&etspi->device_entry); + mutex_unlock(&device_list_lock); + clear_bit(minor, minors); + device_destroy(etspi_class, etspi->devt); +CREATE_DEVICE_CLASS_CREATE_DEVICE_FAILED: +CREATE_DEVICE_CLASS_NO_MINOR: + class_destroy(etspi_class); + etspi_class = NULL; +CREATE_DEVICE_CLASS_CREATE_FAILED: + unregister_chrdev(ET320_MAJOR, etspi_driver.driver.name); + return rc; +} + +static int etspi_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct etspi_data *etspi = dev_get_drvdata(dev); + + DEBUG_PRINT("%s(#%d)\n", __func__, __LINE__); + etspi_create_sysfs(etspi, false); + sysfs_remove_group(&dev->kobj, &attribute_group); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&ets_wake_lock); +#else + PM_WAKEUP_UNREGISTER(ets_wake_lock); +#endif + del_timer_sync(&fps_ints.timer); + etspi_create_device(etspi, false); + //free_irq(gpio_irq, NULL); + etspi_platformInit(etspi, false); + request_irq_done = 0; + /* t_mode = 255; */ + return 0; +} + +static int etspi_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct etspi_data *etspi; + int status = 0; + /* int retval; */ + + BUILD_BUG_ON(N_SPI_MINORS > 256); + DEBUG_PRINT("%s initial\n", __func__); + + etspi = devm_kzalloc(dev, sizeof(*etspi), GFP_KERNEL); + dev_set_drvdata(dev, etspi); + etspi->spi = pdev; + + /* device tree call */ + if (pdev->dev.of_node) { + status = etspi_parse_dt(&pdev->dev, etspi); + if (status) { + pr_err("%s - Failed to parse DT\n", __func__); + goto etspi_probe_parse_dt_failed; + } + } + + /* platform init */ + status = etspi_platformInit(etspi, true); + if (status != 0) { + pr_err("%s platforminit failed\n", __func__); + goto etspi_probe_platformInit_failed; + } + + /* Initialize the driver data */ + mutex_init(&device_list_lock); + spin_lock_init(&etspi->spi_lock); + mutex_init(&etspi->buf_lock); + INIT_LIST_HEAD(&etspi->device_entry); + + status = etspi_create_device(etspi, true); + if (status < 0) { + pr_err("%s create device failed\n", __func__); + goto etspi_probe_create_device_failed; + } + + fps_ints.drdy_irq_flag = DRDY_IRQ_DISABLE; + +#ifdef ETSPI_NORMAL_MODE +/* + spi->bits_per_word = 8; + spi->max_speed_hz = SLOW_BAUD_RATE; + spi->mode = SPI_MODE_0; + spi->chip_select = 0; + status = spi_setup(spi); + if (status != 0) { + pr_err("%s spi_setup() is failed. status : %d\n", + __func__, status); + return status; + } +*/ +#endif + etspi_reset(etspi); + + /* the timer is for ET310 */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0) + timer_setup(&fps_ints.timer, interrupt_timer_routine, 0); +#else + setup_timer(&fps_ints.timer, interrupt_timer_routine, (unsigned long)&fps_ints); + add_timer(&fps_ints.timer); +#endif + +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&ets_wake_lock, WAKE_LOCK_SUSPEND, "ets_wake_lock"); +#else + PM_WAKEUP_REGISTER(dev, ets_wake_lock, "ets_wake_lock"); + if (!ets_wake_lock) { + pr_err("%s : failed to allocate wakeup source\n", __func__); + status = -ENOMEM; + goto etspi_register_wake_lock_failed; + } +#endif + DEBUG_PRINT(" add_timer ---- \n"); + DEBUG_PRINT("%s : initialize success %d\n", + __func__, status); + + status = sysfs_create_group(&dev->kobj, &attribute_group); + if (status) { + pr_err("%s could not create sysfs\n", __func__); + goto etspi_create_group_failed; + } + + status = etspi_create_sysfs(etspi, true); + if (status) { + pr_err("%s could not create sysfs\n", __func__); + goto etspi_sysfs_failed; + } + + return status; + +etspi_sysfs_failed: + sysfs_remove_group(&dev->kobj, &attribute_group); +etspi_create_group_failed: +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&ets_wake_lock); +#else + PM_WAKEUP_UNREGISTER(ets_wake_lock); +#endif +#ifndef CONFIG_HAS_WAKELOCK +etspi_register_wake_lock_failed: +#endif + del_timer_sync(&fps_ints.timer); + etspi_create_device(etspi, false); +etspi_probe_create_device_failed: + etspi_platformInit(etspi, false); +etspi_probe_platformInit_failed: +etspi_probe_parse_dt_failed: + pr_err("%s is failed\n", __func__); + return status; +} + +#ifdef CONFIG_PANEL_NOTIFICATIONS +#include +static struct notifier_block nb; + +static int panel_notifier_cb(struct notifier_block *nb, unsigned long action, void *data) +{ + if (action == PANEL_EVENT_DISPLAY_ON) { + disp_panel_on = 1; + pr_info("%s display panel is on", __func__); + } else if (action == PANEL_EVENT_DISPLAY_OFF) { + disp_panel_on = 0; + pr_info("%s display panel is off", __func__); + } + return 0; +} +#endif + +static int __init ets_init(void) +{ + int status = 0; + DEBUG_PRINT("%s enter\n", __func__); +#if 0 + /* fp_id is used only when dual sensor need to be support */ + struct device_node *fp_id_np = NULL; + int fp_id_gpio = 0, fp_id_gpio_value; + + fp_id_np = of_find_matching_node(fp_id_np, fp_id_match_table); + if (fp_id_np) + fp_id_gpio = of_get_named_gpio(fp_id_np, "fp,gpio-id", 0); + + if (fp_id_gpio < 0) { + /* errorno = gpio; */ + DEBUG_PRINT("%s: device tree error \n", __func__); + return status; + } else { + /* data->rstPin = gpio; */ + DEBUG_PRINT("%s: fp_id Pin=%d\n", __func__, fp_id_gpio); + } + gpio_direction_input(fp_id_gpio); + fp_id_gpio_value = gpio_get_value(fp_id_gpio); + if (fp_id_gpio_value == 0) + DEBUG_PRINT("%s: Load et320-int driver \n", __func__); + else { + DEBUG_PRINT("%s: Load fpc driver \n", __func__); + return status; + } +#endif + status = platform_driver_register(&etspi_driver); + DEBUG_PRINT("%s done\n", __func__); + +#ifdef CONFIG_PANEL_NOTIFICATIONS + nb.notifier_call = panel_notifier_cb; + if (panel_register_notifier(&nb)) + pr_warn("%s fail to register display panel notifier", __func__); +#endif + return status; +} + +static void __exit ets_exit(void) +{ +#ifdef CONFIG_PANEL_NOTIFICATIONS + panel_unregister_notifier(&nb); +#endif + platform_driver_unregister(&etspi_driver); +} + +module_init(ets_init); +module_exit(ets_exit); + +MODULE_AUTHOR("Wang YuWei, "); +MODULE_DESCRIPTION("SPI Interface for ET320"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/ets_fps_mmi/ets_fps.h b/drivers/input/misc/ets_fps_mmi/ets_fps.h new file mode 100644 index 000000000000..0c3971bcb686 --- /dev/null +++ b/drivers/input/misc/ets_fps_mmi/ets_fps.h @@ -0,0 +1,172 @@ +#ifndef _FP_LINUX_DIRVER_H_ +#define _FP_LINUX_DIRVER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef FP_SPI_DEBUG +#define DEBUG_PRINT(fmt, args...) pr_err(fmt, ## args) +#else +#define DEBUG_PRINT(fmt, args...) +#endif + +#define ET320_MAJOR 100 /* assigned */ +#define N_SPI_MINORS 32 /* ... up to 256 */ + +#define FP_ADDRESS_0 0x00 +#define FP_WRITE_ADDRESS 0xAC +#define FP_READ_DATA 0xAF +#define FP_WRITE_DATA 0xAE + +/* ------------------------- Register Definition ------------------------*/ +/* + * Sensor Registers + */ + +#define FDATA_FP_ADDR 0x00 +#define FSTATUS_FP_ADDR 0x01 +/* + * Detect Define + */ +#define FRAME_READY_MASK 0x01 + +/* ------------------------- Opcode -------------------------------------*/ +#define FP_REGISTER_READ 0x01 +#define FP_REGISTER_WRITE 0x02 +#define FP_GET_ONE_IMG 0x03 +#define FP_SENSOR_RESET 0x04 +#define FP_POWER_ONOFF 0x05 +#define FP_SET_SPI_CLOCK 0x06 +#define FP_RESET_SET 0x07 + +/* trigger signal initial routine*/ +#define INT_TRIGGER_INIT 0xa4 +/* trigger signal close routine*/ +#define INT_TRIGGER_CLOSE 0xa5 +/* read trigger status*/ +#define INT_TRIGGER_READ 0xa6 +/* polling trigger status*/ +#define INT_TRIGGER_POLLING 0xa7 +/* polling abort*/ +#define INT_TRIGGER_ABORT 0xa8 +#define FP_TRANSFER_SYNC 0xAA + +#ifdef CONFIG_EGIS_DISPLAY_SPEED_UP +#define NOTIFY_AUTH_STATUS 0xD1 + +#define AUTH_STATUS_NONE 0 +#define AUTH_STATUS_READY 1 +#define AUTH_STATUS_FINGER_DETECTED 2 +#define AUTH_STATUS_CAPTURED 3 +#endif + +#define GET_DISP_PANEL_STATUS 0xD2 + +#define DRDY_IRQ_ENABLE 1 +#define DRDY_IRQ_DISABLE 0 + +/* interrupt polling */ +unsigned int fps_interrupt_poll( + struct file *file, + struct poll_table_struct *wait +); +struct interrupt_desc { + int gpio; + int number; + char *name; + int int_count; + struct timer_list timer; + bool finger_on; + int detect_period; + int detect_threshold; + bool drdy_irq_flag; +}; + +/* ------------------------- Structure ------------------------------*/ +struct egis_ioc_transfer { + u8 *tx_buf; + u8 *rx_buf; + + __u32 len; + __u32 speed_hz; + + __u16 delay_usecs; + __u8 bits_per_word; + __u8 cs_change; + __u8 opcode; + __u8 pad[3]; + +}; + +#define EGIS_IOC_MAGIC 'k' +#define EGIS_MSGSIZE(N) \ + ((((N)*(sizeof(struct egis_ioc_transfer))) < (1 << _IOC_SIZEBITS)) \ + ? ((N)*(sizeof(struct egis_ioc_transfer))) : 0) +#define EGIS_IOC_MESSAGE(N) _IOW(EGIS_IOC_MAGIC, 0, char[EGIS_MSGSIZE(N)]) + +struct etspi_data { + dev_t devt; + spinlock_t spi_lock; + struct platform_device *spi; + struct list_head device_entry; + + /* buffer is NULL unless this device is open (users > 0) */ + struct mutex buf_lock; + unsigned users; + u8 *buffer; + + unsigned int irqPin; /* interrupt GPIO pin number */ + unsigned int rstPin; /* Reset GPIO pin number */ + + unsigned int vdd_18v_Pin; /* Reset GPIO pin number */ + unsigned int vcc_33v_Pin; /* Reset GPIO pin number */ + struct input_dev *input_dev; + struct regulator *regulator_3v3_io; + bool property_navigation_enable; + +}; + + +/* ------------------------- Interrupt ------------------------------*/ +/* interrupt init */ +int Interrupt_Init( + struct etspi_data *etspi, + int int_mode, + int detect_period, + int detect_threshold); + +/* interrupt free */ +int Interrupt_Free(struct etspi_data *etspi); + +void fps_interrupt_abort(void); + +/* ------------------------- Data Transfer --------------------------*/ +/*[REM] int fp_io_read_register(struct fp_data *fp, u8 *addr, u8 *buf); */ +/*[REM] int fp_io_write_register(struct fp_data *fp, u8 *buf);*/ +/*[REM] int fp_io_get_one_image(struct fp_data *fp, u8 *buf, u8 *image_buf);*/ +/*[REM] int fp_read_register(struct fp_data *fp, u8 addr, u8 *buf);*/ +/*[REM] int fp_mass_read(struct fp_data *fp, u8 addr, u8 *buf, int read_len);*/ +#endif diff --git a/drivers/input/misc/ets_fps_mmi/ets_navi_input.c b/drivers/input/misc/ets_fps_mmi/ets_navi_input.c new file mode 100644 index 000000000000..0b0b623d77be --- /dev/null +++ b/drivers/input/misc/ets_fps_mmi/ets_navi_input.c @@ -0,0 +1,819 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ets_fps.h" +#include "ets_navi_input.h" + + +enum { + KEY_RELEASE, + KEY_PRESS, + KEY_PRESS_RELEASE +}; + +#define DISABLE 0 +#define ENABLE 1 + +static struct task_struct *nav_kthread; +static DECLARE_WAIT_QUEUE_HEAD(nav_input_wait); +static unsigned int nav_input_sig; +static DEFINE_MUTEX(driver_mode_lock); +struct navi_cmd_struct { + char cmd; + struct list_head list; +}; + +struct navi_cmd_struct cmd_list; + +/***************************************************************** +* * +* Configuration * +* * +*****************************************************************/ + + +/* + * @ ENABLE_SWIPE_UP_DOWN + * ENABLE : Listening to swipe-up & swipe-down navigation events. + * Configure ENABLE_SWIPE_UP_DOWN properties below. + * + * DISABLE : Ignore swipe-up & swipe-down navigation events. + * Don't care properties. + * + * @ ENABLE_SWIPE_LEFT_RIGHT + * ENABLE : Listening to swipe-left & swipe-right navigation events. + * configure ENABLE_SWIPE_LEFT_RIGHT properties below. + * + * DISABLE : Ignore swipe-left & swipe-right navigation events. + * Don't care properties. + */ + +#ifdef CONFIG_INPUT_EGISTEC_FPS_NAVI_VERTICAL +#define ENABLE_SWIPE_UP_DOWN ENABLE +#else +#define ENABLE_SWIPE_UP_DOWN DISABLE +#endif +#ifdef CONFIG_INPUT_EGISTEC_FPS_NAVI_HORIZON +#define ENABLE_SWIPE_LEFT_RIGHT ENABLE +#else +#define ENABLE_SWIPE_LEFT_RIGHT DISABLE +#endif +#define ENABLE_FINGER_DOWN_UP DISABLE +#define KEY_FPS_DOWN 614 +#define KEY_FPS_UP 615 +#define KEY_FPS_TAP 616 +#define KEY_FPS_HOLD 617 +#define KEY_FPS_YPLUS 618 +#define KEY_FPS_YMINUS 619 +#define KEY_FPS_XPLUS 620 +#define KEY_FPS_XMINUS 621 +#define KEY_FPS_DOUBLE_TAP 622 + + +/* + * ENABLE_SWIPE_UP_DOWN properties + * + * If ENABLE_SWIPE_UP_DOWN set to DISABLE, these should neglected + * + * + * @ KEYEVENT_UP : The key-event should be sent when swipe-up. + * @ KEYEVENT_UP_ACTION : Action of KEYEVENT_UP. + * + * @ KEYEVENT_DOWN : The key-event should be sent when swipe-down. + * @ KEYEVENT_DOWN_ACTION : Action of KEYEVENT_UP. + * + * @ ACTION: + * KEY_PRESS : Press key button + * KEY_RELEASE : Release key button + * KEY_PRESS_RELEASE : Combined action of press-then-release + */ +#if CONFIG_INPUT_EGISTEC_FPS_POSE==0 +#define KEYEVENT_UP KEY_FPS_YMINUS /* KEY_UP */ +#define KEYEVENT_DOWN KEY_FPS_YPLUS /* KEY_DOWN */ +#elif CONFIG_INPUT_EGISTEC_FPS_POSE==90 +#define KEYEVENT_UP KEY_FPS_XPLUS /* KEY_UP */ +#define KEYEVENT_DOWN KEY_FPS_XMINUS /* KEY_DOWN */ +#elif CONFIG_INPUT_EGISTEC_FPS_POSE==180 +#define KEYEVENT_UP KEY_FPS_YPLUS /* KEY_UP */ +#define KEYEVENT_DOWN KEY_FPS_YMINUS /* KEY_DOWN */ +#else /* 270 */ +#define KEYEVENT_UP KEY_FPS_XMINUS /*KEY_UP*/ +#define KEYEVENT_DOWN KEY_FPS_XPLUS /*KEY_DOWN*/ +#endif +#define KEYEVENT_UP_ACTION KEY_PRESS_RELEASE +#define KEYEVENT_DOWN_ACTION KEY_PRESS_RELEASE + + +/* + *ENABLE_SWIPE_LEFT_RIGHT properties. + * + * If ENABLE_SWIPE_LEFT_RIGHT set to DISABLE, these should neglected + * + * + * @ KEYEVENT_RIGHT : The key-event should be sent when swipe-right. + * @ KEYEVENT_RIGHT_ACTION : Action of KEYEVENT_RIGHT. + * + * @ KEYEVENT_LEFT : The key-event should be sent when swipe-left. + * @ KEYEVENT_LEFT_ACTION : Action of KEYEVENT_LEFT. + * + * @ ACTION: + * KEY_PRESS : Press key button + * KEY_RELEASE : Release key button + * KEY_PRESS_RELEASE : Combined action of press-then-release + */ +#if CONFIG_INPUT_EGISTEC_FPS_POSE==0 +#define KEYEVENT_RIGHT KEY_FPS_XPLUS /* KEY_RIGHT */ +#define KEYEVENT_LEFT KEY_FPS_XMINUS /* KEY_LEFT */ +#elif CONFIG_INPUT_EGISTEC_FPS_POSE==90 +#define KEYEVENT_RIGHT KEY_FPS_YPLUS /* KEY_RIGHT */ +#define KEYEVENT_LEFT KEY_FPS_YMINUS /* KEY_LEFT */ +#elif CONFIG_INPUT_EGISTEC_FPS_POSE==180 +#define KEYEVENT_RIGHT KEY_FPS_XMINUS /* KEY_RIGHT */ +#define KEYEVENT_LEFT KEY_FPS_XPLUS /* KEY_LEFT */ +#else /* 270 */ +#define KEYEVENT_RIGHT KEY_FPS_YMINUS /* KEY_RIGHT */ +#define KEYEVENT_LEFT KEY_FPS_YPLUS /* KEY_LEFT */ +#endif +#define KEYEVENT_RIGHT_ACTION KEY_PRESS_RELEASE +#define KEYEVENT_LEFT_ACTION KEY_PRESS_RELEASE +#if ENABLE_FINGER_DOWN_UP +static unsigned int prev_keycode; +#endif +/* + * @ TRANSLATED_COMMAND + * ENABLE : TRANSLATED command. Navigation events will be translated to + * logical user-events. e.g. click, double-click, long-click + * Configure TRANSLATED properties. + * + * DISABLE : STRAIGHT command. Navigation events will be sent one-by-one + * directly. + * Configure STRAIGHT properties. + */ +#define TRANSLATED_COMMAND ENABLE + + + + +#if TRANSLATED_COMMAND + +/*-------------------TRANSLATED properties---------------------*/ + +/* + * @ ENABLE_TRANSLATED_SINGLE_CLICK + * ENABLE/DISABLE : enable/disable single-click event. + * + * @ ENABLE_TRANSLATED_DOUBLE_CLICK + * ENABLE/DISABLE : enable/disable double-click event. + * + * @ ENABLE_TRANSLATED_LONG_TOUCH + * ENABLE/DISABLE : enable/disable long-touch event. + */ +#ifdef CONFIG_NAV_DOUBLE_TAP +#define ENABLE_TRANSLATED_DOUBLE_CLICK ENABLE +#else +#define ENABLE_TRANSLATED_DOUBLE_CLICK DISABLE +#endif +#define ENABLE_TRANSLATED_SINGLE_CLICK DISABLE +#define ENABLE_TRANSLATED_LONG_TOUCH DISABLE + + +/* + * @ LONGTOUCH_INTERVAL : Minimum time finger stay-on that counted to long-touch. + * Only concerned while ENABLE_TRANSLATED_LONG_TOUCH set to ENABLE. + * In millisecond (ms) + * + * @ DOUBLECLICK_INTERVAL : Maximum time between two click that counted to double-click. + * Only concerned while ENABLE_TRANSLATED_DOUBLE_CLICK set to ENABLE. + * In millisecond (ms) + * + * @ KEYEVENT_CLICK : The key-event should be sent when single-click. + * @ KEYEVENT_CLICK_ACTION : Action of KEYEVENT_CLICK. + * Only concerned while ENABLE_TRANSLATED_SINGLE_CLICK set to ENABLE. + * + * @ KEYEVENT_DOUBLECLICK : The key-event should be sent when double-click. + * @ KEYEVENT_DOUBLECLICK_ACTION : Action of KEYEVENT_DOUBLECLICK. + * Only concerned while ENABLE_TRANSLATED_DOUBLE_CLICK set to ENABLE. + * + * @ KEYEVENT_LONGTOUCH : The key-event should be sent when long-touch. + * @ KEYEVENT_LONGTOUCH_ACTION : Action of KEYEVENT_LONGTOUCH. + * Only concerned while ENABLE_TRANSLATED_LONG_TOUCH set to ENABLE. + * + * @ ACTION: + * KEY_PRESS : Press key button + * KEY_RELEASE : Release key button + * KEY_PRESS_RELEASE : Combined action of press-then-release + */ +#define LONGTOUCH_INTERVAL 400 +#define SINGLECLICK_INTERVAL 150 +#define SINGLECLICK_INTERVAL_MIN 5 +#define DOUBLECLICK_INTERVAL 300 +#define DOUBLECLICK_INTERVAL_MIN 70 + +#define KEYEVENT_CLICK KEY_FPS_TAP /* 0x232 */ +#define KEYEVENT_CLICK_ACTION KEY_PRESS_RELEASE +#define KEYEVENT_DOUBLECLICK KEY_FPS_DOUBLE_TAP +#define KEYEVENT_DOUBLECLICK_ACTION KEY_PRESS_RELEASE +#define KEYEVENT_LONGTOUCH KEY_FPS_HOLD /* 0x233 */ +#define KEYEVENT_LONGTOUCH_ACTION KEY_PRESS_RELEASE + +#define KEYEVENT_ON KEY_FPS_DOWN +#define KEYEVENT_ON_ACTION KEY_PRESS_RELEASE +#define KEYEVENT_OFF KEY_FPS_UP +#define KEYEVENT_OFF_ACTION KEY_PRESS_RELEASE + + +/*---------------End of TRANSLATED properties-----------------*/ + + +#else /* STRAIGHT COMMAND */ + + +/*-------------------STRAIGHT properties----------------------*/ + +/* + * @ ENABLE_STRAIGHT_CANCEL + * ENABLE/DISABLE : enable/disable cancel event. + * + * @ ENABLE_STRAIGHT_ON + * ENABLE/DISABLE : enable/disable finger-on event. + * + * @ ENABLE_STRAIGHT_OFF + * ENABLE/DISABLE : enable/disable finger-off event. + */ +#define ENABLE_STRAIGHT_CANCEL ENABLE +#define ENABLE_STRAIGHT_ON ENABLE +#define ENABLE_STRAIGHT_OFF ENABLE + + +/* + * @ KEYEVENT_CANCEL : The key-event should be sent when cancel. + * @ KEYEVENT_CANCEL_ACTION : Action of KEYEVENT_CANCEL. + * Only concerned while ENABLE_STRAIGHT_CANCEL set to ENABLE. + * + * @ KEYEVENT_ON : The key-event should be sent when finger-on. + * @ KEYEVENT_ON_ACTION : Action of KEYEVENT_ON. + * Only concerned while ENABLE_STRAIGHT_ON set to ENABLE. + * + * @ KEYEVENT_OFF : The key-event should be sent when long-touch. + * @ KEYEVENT_OFF_ACTION : Action of KEYEVENT_OFF. + * Only concerned while ENABLE_STRAIGHT_OFF set to ENABLE. + * + * @ ACTION: + * KEY_PRESS : Press key button + * KEY_RELEASE : Release key button + * KEY_PRESS_RELEASE : Combined action of press-then-release + */ +#define KEYEVENT_CANCEL KEY_0 +#define KEYEVENT_CANCEL_ACTION KEY_PRESS_RELEASE +#define KEYEVENT_ON KEY_EXIT +#define KEYEVENT_ON_ACTION KEY_PRESS +#define KEYEVENT_OFF KEY_EXIT +#define KEYEVENT_OFF_ACTION KEY_RELEASE + +/*-----------------End of STRAIGHT properties-------------------*/ + + +#endif + + +/**************************************************************** +* * +* End of Configuration * +* * +****************************************************************/ + + + + + + + + + + +#define PROPERTY_NAVIGATION_ENABLE_DEFAULT true + +struct navi_struct { + char cmd; + struct etspi_data *etspi; + struct work_struct workq; +}; + +enum navi_event { + NAVI_EVENT_CANCEL, + NAVI_EVENT_ON, + NAVI_EVENT_OFF, + NAVI_EVENT_SWIPE, + NAVI_EVENT_UP, + NAVI_EVENT_DOWN, + NAVI_EVENT_RIGHT, + NAVI_EVENT_LEFT +}; +#if ENABLE_TRANSLATED_LONG_TOUCH +static struct timer_list long_touch_timer; +#endif +static bool g_KeyEventRaised = true; +static unsigned long g_DoubleClickJiffies; +static unsigned long g_SingleClickJiffies; +static unsigned int g_SingleClick; + + + +/* Set event bits according to what events we would generate */ +void init_event_enable(struct etspi_data *etspi) +{ + set_bit(EV_KEY, etspi->input_dev->evbit); + set_bit(EV_SYN, etspi->input_dev->evbit); +#if TRANSLATED_COMMAND + set_bit(KEYEVENT_ON, etspi->input_dev->keybit); + set_bit(KEYEVENT_OFF, etspi->input_dev->keybit); + set_bit(KEYEVENT_CLICK, etspi->input_dev->keybit); + set_bit(KEYEVENT_DOUBLECLICK, etspi->input_dev->keybit); + set_bit(KEYEVENT_LONGTOUCH, etspi->input_dev->keybit); + set_bit(KEYEVENT_UP, etspi->input_dev->keybit); + set_bit(KEYEVENT_DOWN, etspi->input_dev->keybit); + set_bit(KEYEVENT_RIGHT, etspi->input_dev->keybit); + set_bit(KEYEVENT_LEFT, etspi->input_dev->keybit); +#else + set_bit(KEYEVENT_CANCEL, etspi->input_dev->keybit); + set_bit(KEYEVENT_ON, etspi->input_dev->keybit); + set_bit(KEYEVENT_OFF, etspi->input_dev->keybit); + set_bit(KEYEVENT_UP, etspi->input_dev->keybit); + set_bit(KEYEVENT_DOWN, etspi->input_dev->keybit); + set_bit(KEYEVENT_RIGHT, etspi->input_dev->keybit); + set_bit(KEYEVENT_LEFT, etspi->input_dev->keybit); +#endif +} + + +static void send_key_event(struct etspi_data *etspi, unsigned int code, int value) +{ + struct etspi_data *obj = etspi; + + if (value == KEY_PRESS_RELEASE) { + input_report_key(obj->input_dev, code, 1); /* 1 is press */ + input_sync(obj->input_dev); + input_report_key(obj->input_dev, code, 0); /* 0 is release */ + input_sync(obj->input_dev); +#if ENABLE_FINGER_DOWN_UP + prev_keycode = code; +#endif + } else { + input_report_key(obj->input_dev, code, value); + input_sync(obj->input_dev); + } + + DEBUG_PRINT("Egis navigation driver, send key event: %d, action: %d\n", code, value); +} + +#if ENABLE_TRANSLATED_LONG_TOUCH +static void long_touch_handler(unsigned long arg) +{ + struct etspi_data *etspi = (struct etspi_data *)arg; + + if (g_KeyEventRaised == false) { + g_KeyEventRaised = true; + /* Long touch event */ + send_key_event(etspi, KEYEVENT_LONGTOUCH, KEYEVENT_LONGTOUCH_ACTION); + } +} +#endif + + +#if TRANSLATED_COMMAND +void translated_command_converter(char cmd, struct etspi_data *etspi) +{ + DEBUG_PRINT("Egis navigation driver, translated cmd: %d\n", cmd); + + switch (cmd) { + case NAVI_EVENT_CANCEL: + g_KeyEventRaised = true; + g_DoubleClickJiffies = 0; + g_SingleClickJiffies = 0; + g_SingleClick = 0; +#if ENABLE_TRANSLATED_LONG_TOUCH + del_timer(&long_touch_timer); +#endif + break; + + case NAVI_EVENT_ON: /* finger down */ + g_KeyEventRaised = false; +#if ENABLE_TRANSLATED_SINGLE_CLICK || ENABLE_TRANSLATED_DOUBLE_CLICK + g_SingleClickJiffies = jiffies; +#endif + +#if ENABLE_FINGER_DOWN_UP + send_key_event(etspi, KEYEVENT_ON, KEYEVENT_ON_ACTION); +#endif +#if ENABLE_TRANSLATED_LONG_TOUCH + long_touch_timer.data = (unsigned long)etspi; + mod_timer(&long_touch_timer, jiffies + (HZ * LONGTOUCH_INTERVAL / 1000)); +#endif + break; + + case NAVI_EVENT_OFF: /* finger up */ + if (g_KeyEventRaised == false) { + g_KeyEventRaised = true; + pr_info("Egis : g_SingleClick %u tap interval =%u double tap interval = %u time= %u", + g_SingleClick, jiffies_to_msecs(jiffies - g_SingleClickJiffies), + jiffies_to_msecs(g_SingleClickJiffies - g_DoubleClickJiffies), jiffies_to_msecs(jiffies)); +#if ENABLE_TRANSLATED_SINGLE_CLICK || ENABLE_TRANSLATED_DOUBLE_CLICK + if ((jiffies - g_SingleClickJiffies) < (HZ * SINGLECLICK_INTERVAL / 1000) + && (jiffies - g_SingleClickJiffies) > (HZ * SINGLECLICK_INTERVAL_MIN / 1000)) { + /* Click event */ +#if ENABLE_TRANSLATED_SINGLE_CLICK + send_key_event(etspi, KEYEVENT_CLICK, KEYEVENT_CLICK_ACTION); +#endif + g_SingleClick++; + if (g_SingleClick == 1) { + g_DoubleClickJiffies = jiffies; + } + } +#endif +#if ENABLE_TRANSLATED_DOUBLE_CLICK + if (g_SingleClick >= 2) { + if (((g_SingleClickJiffies - g_DoubleClickJiffies) < (HZ * DOUBLECLICK_INTERVAL / 1000)) + && ((g_SingleClickJiffies - g_DoubleClickJiffies) >= (HZ * DOUBLECLICK_INTERVAL_MIN / 1000))) { + /* Double click event */ + send_key_event(etspi, KEYEVENT_DOUBLECLICK, KEYEVENT_DOUBLECLICK_ACTION); + g_SingleClick = 0; + } else { + g_SingleClick = 1; + g_DoubleClickJiffies = jiffies; + } + + } +#endif + +#if ENABLE_FINGER_DOWN_UP + send_key_event(etspi, KEYEVENT_OFF, KEYEVENT_OFF_ACTION); +#endif + } +#if ENABLE_FINGER_DOWN_UP + else { + if (prev_keycode == KEYEVENT_LONGTOUCH) + send_key_event(etspi, KEYEVENT_OFF, KEYEVENT_OFF_ACTION); + } +#endif +#if ENABLE_TRANSLATED_LONG_TOUCH + del_timer(&long_touch_timer); +#endif + break; + + case NAVI_EVENT_UP: + if (g_KeyEventRaised == false) { + g_KeyEventRaised = true; +#if ENABLE_SWIPE_UP_DOWN + send_key_event(etspi, KEYEVENT_UP, KEYEVENT_UP_ACTION); + send_key_event(etspi, KEYEVENT_OFF, KEYEVENT_OFF_ACTION); + +#endif + } + break; + + case NAVI_EVENT_DOWN: + + if (g_KeyEventRaised == false) { + g_KeyEventRaised = true; +#if ENABLE_SWIPE_UP_DOWN + send_key_event(etspi, KEYEVENT_DOWN, KEYEVENT_DOWN_ACTION); + send_key_event(etspi, KEYEVENT_OFF, KEYEVENT_OFF_ACTION); + +#endif + } + + break; + + case NAVI_EVENT_RIGHT: + + #if ENABLE_SWIPE_LEFT_RIGHT + if (g_KeyEventRaised == false) { + g_KeyEventRaised = true; + send_key_event(etspi, KEYEVENT_RIGHT, KEYEVENT_RIGHT_ACTION); + send_key_event(etspi, KEYEVENT_OFF, KEYEVENT_OFF_ACTION); + } + #endif + + break; + + case NAVI_EVENT_LEFT: + + #if ENABLE_SWIPE_LEFT_RIGHT + if (g_KeyEventRaised == false) { + g_KeyEventRaised = true; + send_key_event(etspi, KEYEVENT_LEFT, KEYEVENT_LEFT_ACTION); + send_key_event(etspi, KEYEVENT_OFF, KEYEVENT_OFF_ACTION); + + } + #endif + + break; + + default: + pr_err("Egis navigation driver, cmd not match\n"); + } +} + +#else /* straight command (not define TRANSLATED_COMMAND) */ + +void straight_command_converter(char cmd, struct etspi_data *etspi) +{ + DEBUG_PRINT("Egis navigation driver, straight cmd: %d\n", cmd); + + switch (cmd) { + case NAVI_EVENT_CANCEL: + +#if ENABLE_STRAIGHT_CANCEL + send_key_event(etspi, KEYEVENT_CANCEL, KEYEVENT_CANCEL_ACTION); +#endif + + break; + + case NAVI_EVENT_ON: + +#if ENABLE_STRAIGHT_ON + send_key_event(etspi, KEYEVENT_ON, KEYEVENT_ON_ACTION); +#endif + + break; + + case NAVI_EVENT_OFF: + +#if ENABLE_STRAIGHT_OFF + send_key_event(etspi, KEYEVENT_OFF, KEYEVENT_OFF_ACTION); +#endif + + break; + + case NAVI_EVENT_UP: + +#if ENABLE_SWIPE_UP_DOWN + send_key_event(etspi, KEYEVENT_UP, KEYEVENT_UP_ACTION); +#endif + + break; + + case NAVI_EVENT_DOWN: + +#if ENABLE_SWIPE_UP_DOWN + send_key_event(etspi, KEYEVENT_DOWN, KEYEVENT_DOWN_ACTION); +#endif + + break; + + case NAVI_EVENT_RIGHT: + +#if ENABLE_SWIPE_LEFT_RIGHT + send_key_event(etspi, KEYEVENT_RIGHT, KEYEVENT_RIGHT_ACTION); +#endif + + break; + + case NAVI_EVENT_LEFT: + +#if ENABLE_SWIPE_LEFT_RIGHT + send_key_event(etspi, KEYEVENT_LEFT, KEYEVENT_LEFT_ACTION); +#endif + + break; + + default: + pr_err("Egis navigation driver, cmd not match\n"); + } +} + +#endif /* end of TRANSLATED_COMMAND */ + +void navi_operator(struct work_struct *work) +{ + struct navi_struct *command = container_of(work, struct navi_struct, workq); + +#if TRANSLATED_COMMAND + translated_command_converter(command->cmd, command->etspi); +#else + straight_command_converter(command->cmd, command->etspi); + +#endif +} + +static ssize_t navigation_event_func(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct etspi_data *etspi = dev_get_drvdata(dev); + struct navi_cmd_struct *tempcmd; + DEBUG_PRINT("Egis navigation driver, %s echo :'%d'\n", __func__, *buf); + + if (etspi) { + dev_dbg(&etspi->spi->dev, "%s spi_show\n", __func__); + if (etspi->spi) { + dev_dbg(&etspi->spi->dev, "%s spi show\n", __func__); + } + } else + pr_err("Egis navigation driver, etspi is NULL\n"); + + if (etspi->input_dev == NULL) + pr_err("Egis navigation driver, etspi->input_dev is NULL\n"); + tempcmd = kmalloc(sizeof(*tempcmd), GFP_KERNEL); + if (tempcmd != NULL) { + mutex_lock(&driver_mode_lock); + tempcmd->cmd = *buf; + list_add_tail(&tempcmd->list, &cmd_list.list); + nav_input_sig = 1; + mutex_unlock(&driver_mode_lock); + wake_up_interruptible(&nav_input_wait); + } else { + pr_err("navigation_event_func kmalloc failed\n"); + + } + return count; +} +static DEVICE_ATTR(navigation_event, S_IWUSR, NULL, navigation_event_func); + +static ssize_t property_navigation_enable_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct etspi_data *etspi = dev_get_drvdata(dev); + pr_err("Egis navigation driver, %s echo :'%d'\n", __func__, *buf); + + if (!strncmp(buf, "enable", strlen("enable"))) { + etspi->property_navigation_enable = 1; + } else if (!strncmp(buf, "disable", strlen("disable"))) { + etspi->property_navigation_enable = 0; + } else { + pr_err("strcmp not match\n"); + } + return count; +} + +static ssize_t property_navigation_enable_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct etspi_data *etspi = dev_get_drvdata(dev); + DEBUG_PRINT("Egis navigation driver, %s echo :'%d'\n", __func__, *buf); + + return scnprintf(buf, PAGE_SIZE, "%s", + etspi->property_navigation_enable ? "enable":"disable"); +} +static DEVICE_ATTR(navigation_enable, S_IRUSR|S_IWUSR, + property_navigation_enable_get, property_navigation_enable_set); + + +/*-------------------------------------------------------------------------*/ +/* + * Sysfs node creation + */ +static struct attribute *attributes[] = { + &dev_attr_navigation_event.attr, + &dev_attr_navigation_enable.attr, + NULL +}; +static const struct attribute_group attribute_group = { + .attrs = attributes, +}; + + +/*-------------------------------------------------------------------------*/ +static int nav_input_thread(void *et_spi) +{ + struct etspi_data *etspi = et_spi; + struct navi_cmd_struct *tempcmd, *acmd; + + set_user_nice(current, -20); + DEBUG_PRINT("nav_input_thread enter\n"); + + while (1) { + wait_event_interruptible(nav_input_wait, + nav_input_sig || kthread_should_stop()); + mutex_lock(&driver_mode_lock); + list_for_each_entry_safe(acmd, tempcmd, &cmd_list.list, list) { + translated_command_converter(acmd->cmd, etspi); + list_del(&acmd->list); + kfree(acmd); + } + nav_input_sig = 0; + mutex_unlock(&driver_mode_lock); + if (kthread_should_stop()) + break; + } + + + DEBUG_PRINT("nav_input_thread exit\n"); + + return 0; +} + + +void uinput_egis_init(struct etspi_data *etspi) +{ + int error = 0; + + DEBUG_PRINT("Egis navigation driver, %s\n", __func__); + + etspi->property_navigation_enable = PROPERTY_NAVIGATION_ENABLE_DEFAULT ; + etspi->input_dev = input_allocate_device(); + + if (!etspi->input_dev) { + pr_err("Egis navigation driver, Input_allocate_device failed.\n"); + return; + } + + + INIT_LIST_HEAD(&cmd_list.list); + nav_input_sig = 0; + if (!nav_kthread) { + nav_kthread = kthread_run(nav_input_thread, + (void *)etspi, "nav_thread"); + } +#if ENABLE_TRANSLATED_LONG_TOUCH + init_timer(&long_touch_timer); + long_touch_timer.function = long_touch_handler; +#endif + + + etspi->input_dev->name = "uinput-egis"; + etspi->input_dev->id.vendor = 0x1f; //common vendorid + + + init_event_enable(etspi); + + /* Register the input device */ + error = input_register_device(etspi->input_dev); + if (error) { + pr_err("Egis navigation driver, Input_register_device failed.\n"); + input_free_device(etspi->input_dev); + etspi->input_dev = NULL; + } + g_DoubleClickJiffies = 0; + g_SingleClickJiffies = 0; +} + +void uinput_egis_destroy(struct etspi_data *etspi) +{ + DEBUG_PRINT("Egis navigation driver, %s\n", __func__); + + + g_DoubleClickJiffies = 0; + g_SingleClickJiffies = 0; + + if (etspi->input_dev != NULL) { + input_unregister_device(etspi->input_dev); + etspi->input_dev = NULL; + } + +#if ENABLE_TRANSLATED_LONG_TOUCH + del_timer(&long_touch_timer); +#endif + + if (nav_kthread) + kthread_stop(nav_kthread); + + nav_kthread = NULL; +} + + +void sysfs_egis_init(struct etspi_data *etspi) +{ + int status; + + DEBUG_PRINT("Egis navigation driver, egis_input device init\n"); + etspi->spi = platform_device_alloc("egis_input", -1); + if (!etspi->spi) { + pr_err("Egis navigation driver, platform_device_alloc fail\n"); + return; + } + + status = platform_device_add(etspi->spi); + if (status != 0) { + pr_err("Egis navigation driver, platform_device_add fail\n"); + platform_device_put(etspi->spi); + return; + } + + dev_set_drvdata(&etspi->spi->dev, etspi); + status = sysfs_create_group(&etspi->spi->dev.kobj, &attribute_group); + if (status) { + pr_err("Egis navigation driver, could not create sysfs\n"); + platform_device_del(etspi->spi); + platform_device_put(etspi->spi); + return; + } + + kobject_uevent(&etspi->spi->dev.kobj, KOBJ_CHANGE); // chengql2 +} + +void sysfs_egis_destroy(struct etspi_data *etspi) +{ + DEBUG_PRINT("Egis navigation driver, %s\n", __func__); + + if (etspi->spi) { + sysfs_remove_group(&etspi->spi->dev.kobj, &attribute_group); + platform_device_del(etspi->spi); + platform_device_put(etspi->spi); + } +} diff --git a/drivers/input/misc/ets_fps_mmi/ets_navi_input.h b/drivers/input/misc/ets_fps_mmi/ets_navi_input.h new file mode 100644 index 000000000000..a6dc8efc8c14 --- /dev/null +++ b/drivers/input/misc/ets_fps_mmi/ets_navi_input.h @@ -0,0 +1,6 @@ + + +void uinput_egis_init(struct etspi_data *etspi); +void uinput_egis_destroy(struct etspi_data *etspi); +void sysfs_egis_init(struct etspi_data *etspi); +void sysfs_egis_destroy(struct etspi_data *etspi); \ No newline at end of file diff --git a/drivers/input/misc/focal_fps_mmi/Android.mk b/drivers/input/misc/focal_fps_mmi/Android.mk new file mode 100644 index 000000000000..5a1af6d23e7d --- /dev/null +++ b/drivers/input/misc/focal_fps_mmi/Android.mk @@ -0,0 +1,23 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +FOCAL_FPS_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +FOCAL_FPS_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +ifeq ($(FOCAL_SUPPORT_DRM_SCREEN),true) + KBUILD_OPTIONS += CONFIG_FOCAL_SUPPORT_DRM_SCREEN=y +endif + +ifeq ($(FOCAL_FPS_PANEL_NOTIFICATIONS),true) + KERNEL_CFLAGS += CONFIG_FOCAL_PANEL_NOTIFICATIONS=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := focal_fps_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(FOCAL_FPS_MMI_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/misc/focal_fps_mmi/Kbuild b/drivers/input/misc/focal_fps_mmi/Kbuild new file mode 100644 index 000000000000..4f9cb1ae2a70 --- /dev/null +++ b/drivers/input/misc/focal_fps_mmi/Kbuild @@ -0,0 +1,49 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/focal_fps_mmi + + + + + + + +# Early-logging level for ff_hal, ff_ta and driver as well. Note that this +# logging level only takes effect before ff_config module is loaded. +# +# Options: +# 0 - FF_LOG_LEVEL_ALL, All +# 1 - FF_LOG_LEVEL_VBS, Verbose +# 2 - FF_LOG_LEVEL_DBG, Debug +# 3 - FF_LOG_LEVEL_INF, Info +# 4 - FF_LOG_LEVEL_WRN, Warning +# 5 - FF_LOG_LEVEL_ERR, Error +EXTRA_CFLAGS += -D__FF_BUILD_DATE=\"$(shell date +%Y%m%d)\" +EXTRA_CFLAGS += -D__FF_EARLY_LOG_LEVEL=2 + + +focal_fps_mmi-y += ff_ctl.o plat-msm.o + +ifneq ($(filter m y,$(CONFIG_INPUT_EGISTEC_FPS_NAVI)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_EGISTEC_FPS_NAVI + ets_fps_mmi-y += ets_navi_input.o +endif +ifneq ($(filter m y,$(CONFIG_INPUT_EGISTEC_FPS_NAVI_VERTICAL)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_EGISTEC_FPS_NAVI_VERTICAL +endif +ifeq ($(TARGET_BUILD_VARIANT),userdebug) + EXTRA_CFLAGS += -DFP_SPI_DEBUG +endif + +ifneq ($(filter m y,$(CONFIG_FOCAL_SUPPORT_DRM_SCREEN)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_NOTIFICATIONS +endif + +ifneq ($(filter m y, $(CONFIG_FOCAL_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_PANEL_NOTIFICATIONS +endif + +obj-m += focal_fps_mmi.o + + diff --git a/drivers/input/misc/focal_fps_mmi/Makefile b/drivers/input/misc/focal_fps_mmi/Makefile new file mode 100644 index 000000000000..e2e207607578 --- /dev/null +++ b/drivers/input/misc/focal_fps_mmi/Makefile @@ -0,0 +1,13 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + + + diff --git a/drivers/input/misc/focal_fps_mmi/ff_ctl.c b/drivers/input/misc/focal_fps_mmi/ff_ctl.c new file mode 100644 index 000000000000..3b4c3f78fde8 --- /dev/null +++ b/drivers/input/misc/focal_fps_mmi/ff_ctl.c @@ -0,0 +1,883 @@ +/** + * The device control driver for FocalTech's fingerprint sensor. + * + * Copyright (C) 2016-2017 FocalTech Systems Co., Ltd. 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +#include +#else /* CONFIG_DRM_PANEL_NOTIFICATIONS */ +#if defined(CONFIG_PANEL_NOTIFICATIONS) +#include +#else +#include +#endif +#endif +#include +#if 0 +#ifdef CONFIG_PM_WAKELOCKS +#include +#else +#include +#endif +#endif + +#ifdef CONFIG_COMPAT +#include +#endif + +#include "ff_log.h" +#include "ff_err.h" +#include "ff_ctl.h" + +/* + * Define the driver version string. + */ +#define FF_DRV_VERSION "v2.1.3" + +/* + * Define the driver name. + */ +#define FF_DRV_NAME "focaltech_fp" + +/* + * Driver context definition and its singleton instance. + */ +typedef struct { + struct miscdevice miscdev; + int irq_num; + struct work_struct work_queue; + struct fasync_struct *async_queue; + struct input_dev *input; + struct notifier_block fb_notifier; +#if 0 + struct wakeup_source *wake_lock; +//#else + struct wake_lock *wake_lock; +#endif + bool b_driver_inited; + bool b_config_dirtied; + bool irq_wake_enabled; +#if defined(CONFIG_DRM_PANEL) && LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + struct drm_panel *active_panel; +#endif +} ff_ctl_context_t; +static ff_ctl_context_t *g_context = NULL; + +/* + * Driver configuration. + */ +static ff_driver_config_t driver_config; +ff_driver_config_t *g_config = NULL; +//////////////////////////////////////////////////////////////////////////////// +/// Logging driver to logcat through uevent mechanism. + +# undef LOG_TAG +#define LOG_TAG "ff_ctl" + +/* + * Log level can be runtime configurable by 'FF_IOC_SYNC_CONFIG'. + */ +ff_log_level_t g_log_level = 3;//__FF_EARLY_LOG_LEVEL; +#define MAX_UP_TIME (1 * MSEC_PER_SEC) +int ff_log_printf(ff_log_level_t level, const char *tag, const char *fmt, ...) +{ + /* Prepare the storage. */ + va_list ap; + static char uevent_env_buf[128]; + char *ptr = uevent_env_buf; + int n, available = sizeof(uevent_env_buf); + + /* Fill logging level. */ + available -= sprintf(uevent_env_buf, "FF_LOG=%1d", level); + ptr += strlen(uevent_env_buf); + + /* Fill logging message. */ + va_start(ap, fmt); + vsnprintf(ptr, available, fmt, ap); + va_end(ap); + + /* Send to ff_device. */ + if (likely(g_context) && likely(g_config) && unlikely(g_config->logcat_driver)) { + char *uevent_env[2] = {uevent_env_buf, NULL}; + kobject_uevent_env(&g_context->miscdev.this_device->kobj, KOBJ_CHANGE, uevent_env); + } + + /* Native output. */ + switch (level) { + case FF_LOG_LEVEL_ERR: + n = printk(KERN_ERR FF_DRV_NAME": %s\n", ptr); + break; + case FF_LOG_LEVEL_WRN: + n = printk(KERN_WARNING FF_DRV_NAME": %s\n", ptr); + break; + case FF_LOG_LEVEL_INF: + n = printk(KERN_INFO FF_DRV_NAME": %s\n", ptr); + break; + case FF_LOG_LEVEL_DBG: + case FF_LOG_LEVEL_VBS: + default: + n = printk(KERN_DEBUG FF_DRV_NAME": %s\n", ptr); + break; + } + return n; +} + +const char *ff_err_strerror(int err) +{ + static char errstr[32] = {'\0', }; + + switch (err) { + case FF_SUCCESS : return "Success"; + case FF_ERR_INTERNAL: return "Internal error"; + + /* Base on unix errno. */ + case FF_ERR_NOENT: return "No such file or directory"; + case FF_ERR_INTR : return "Interrupted"; + case FF_ERR_IO : return "I/O error"; + case FF_ERR_AGAIN: return "Try again"; + case FF_ERR_NOMEM: return "Out of memory"; + case FF_ERR_BUSY : return "Resource busy / Timeout"; + + /* Common error. */ + case FF_ERR_BAD_PARAMS : return "Bad parameter(s)"; + case FF_ERR_NULL_PTR : return "Null pointer"; + case FF_ERR_BUF_OVERFLOW: return "Buffer overflow"; + case FF_ERR_BAD_PROTOCOL: return "Bad protocol"; + case FF_ERR_SENSOR_SIZE : return "Wrong sensor dimension"; + case FF_ERR_NULL_DEVICE : return "Device not found"; + case FF_ERR_DEAD_DEVICE : return "Device is dead"; + case FF_ERR_REACH_LIMIT : return "Up to the limit"; + case FF_ERR_REE_TEMPLATE: return "Template store in REE"; + case FF_ERR_NOT_TRUSTED : return "Untrusted enrollment"; + + default: + sprintf(errstr, "%d", err); + break; + } + + return (const char *)errstr; +} + +//////////////////////////////////////////////////////////////////////////////// + +/* See plat-xxxx.c for platform dependent implementation. */ +extern int ff_ctl_init_pins(int *irq_num); +extern int ff_ctl_free_pins(void); +#ifdef FF_SPI_SET +extern int ff_ctl_enable_spiclk(bool on); +#endif +extern int ff_ctl_enable_power(bool on); +extern int ff_ctl_reset_device(uint32_t level); +extern const char *ff_ctl_arch_str(void); + + +#ifdef CHIP_TYPE_FT9304 +/* See chip-xxxx.c for chip dependent implementation. */ +extern int ff_chip_init(void); +#endif + +static int ff_ctl_enable_irq(bool on) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + FF_LOGD("irq: '%s'.", on ? "enable" : "disabled"); + + if (unlikely(!g_context)) { + return (-ENOSYS); + } + + if (on) { + enable_irq(g_context->irq_num); + } else { + disable_irq(g_context->irq_num); + } + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +static void ff_ctl_device_event(struct work_struct *ws) +{ + ff_ctl_context_t *ctx = container_of(ws, ff_ctl_context_t, work_queue); + char *uevent_env[2] = {"FF_INTERRUPT", NULL}; + FF_LOGV("'%s' enter.", __func__); + + FF_LOGD("%s(irq = %d, ..) toggled.", __func__, ctx->irq_num); +#if 0 + __pm_wakeup_event(g_context->wake_lock, jiffies_to_msecs(2*HZ)); +//#else + wake_lock_timeout(g_context->wake_lock, 2 * HZ); // 2 seconds. +#endif + pm_wakeup_event(g_context->miscdev.this_device, MAX_UP_TIME); + kobject_uevent_env(&ctx->miscdev.this_device->kobj, KOBJ_CHANGE, uevent_env); + + FF_LOGV("'%s' leave.", __func__); +} + +static irqreturn_t ff_ctl_device_irq(int irq, void *dev_id) +{ + ff_ctl_context_t *ctx = (ff_ctl_context_t *)dev_id; + disable_irq_nosync(irq); + + if (likely(irq == ctx->irq_num)) { + if (g_config && g_config->enable_fasync && g_context->async_queue) { + kill_fasync(&g_context->async_queue, SIGIO, POLL_IN); + } else { + schedule_work(&ctx->work_queue); + } + } + + enable_irq(irq); + return IRQ_HANDLED; +} + +static int ff_ctl_report_key_event(struct input_dev *input, ff_key_event_t *kevent) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + + input_report_key(input, kevent->code, kevent->value); + input_sync(input); + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +static const char *ff_ctl_get_version(void) +{ + static char version[FF_DRV_VERSION_LEN] = {'\0', }; + FF_LOGV("'%s' enter.", __func__); + + /* Version info. */ + version[0] = '\0'; + strcat(version, FF_DRV_VERSION); +#ifdef __FF_SVN_REV + if (strlen(__FF_SVN_REV) > 0) { + sprintf(version, "%s-r%s", version, __FF_SVN_REV); + } +#endif +#ifdef __FF_BUILD_DATE + strcat(version, "-"__FF_BUILD_DATE); +#endif + sprintf(version, "%s-%s", version, ff_ctl_arch_str()); + FF_LOGD("version: '%s'.", version); + + FF_LOGV("'%s' leave.", __func__); + return (const char *)version; +} +#if defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +static int drm_check_dt(ff_ctl_context_t *g_context_t) +{ + int i = 0; + int count = 0; + struct device_node *np = of_find_compatible_node(NULL, NULL, "focaltech,fingerprint"); + struct device_node *node = NULL; + struct drm_panel *panel = NULL; + if (!np) { + FF_LOGV("didn't find focal node"); + return -ENODEV; + } + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) { + FF_LOGE("find drm_panel fail count = %d ", count); + return -ENODEV; + } + 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)) { + FF_LOGE(" ind drm_panel successfully "); + g_context_t->active_panel = panel; + return 0; + } + } + FF_LOGD("can not find drm_panel "); + return -ENODEV; +} +#endif +static char screen_state[1]; +static int ff_ctl_fb_notifier_callback(struct notifier_block *nb, unsigned long action, void *data) +{ +#if defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) + struct drm_panel_notifier *evdata = data; + //ff_ctl_context_t *g_context_t = NULL; + char *uevent_env[2]; + unsigned int blank; + FF_LOGD("got notify value = %d \n", (int)action); + if (action != DRM_PANEL_EARLY_EVENT_BLANK) + { + return NOTIFY_OK; + } + //g_context_t = container_of(nb, ff_ctl_context_t, fb_notifier); + if (!evdata || !evdata->data || !g_context) { + FF_LOGE(" some parameters is null \n"); + return NOTIFY_OK; + } + blank = *(int *)(evdata->data); + switch (blank) { + case DRM_PANEL_BLANK_UNBLANK: + uevent_env[0] = "FF_SCREEN_ON"; + screen_state[0] = 0; + break; + case DRM_PANEL_BLANK_POWERDOWN: + uevent_env[0] = "FF_SCREEN_OFF"; + screen_state[0] = 1; + break; + default: + uevent_env[0] = "FF_SCREEN_??"; + break; + } + if(blank == DRM_PANEL_BLANK_UNBLANK || blank== DRM_PANEL_BLANK_POWERDOWN) { + uevent_env[1] = NULL; + kobject_uevent_env(&g_context->miscdev.this_device->kobj, KOBJ_CHANGE, uevent_env); + FF_LOGD("'%s' leave %s.", __func__,uevent_env[0] ); + } +#else /* CONFIG_DRM_PANEL_NOTIFICATIONS */ +#if defined(CONFIG_PANEL_NOTIFICATIONS) + char *uevent_env[2]; + FF_LOGI(" received a panel notification %d \n", (int)action); + switch(action) { + case PANEL_EVENT_DISPLAY_ON: + uevent_env[0] = "FF_SCREEN_ON"; + screen_state[0] = 0; + break; + case PANEL_EVENT_DISPLAY_OFF: + uevent_env[0] = "FF_SCREEN_OFF"; + screen_state[0] = 1; + break; + case PANEL_EVENT_PRE_DISPLAY_OFF: + break; + case PANEL_EVENT_PRE_DISPLAY_ON: + break; + default: + break; + } + if(action == PANEL_EVENT_DISPLAY_ON || action== PANEL_EVENT_DISPLAY_OFF) { + uevent_env[1] = NULL; + kobject_uevent_env(&g_context->miscdev.this_device->kobj, KOBJ_CHANGE, uevent_env); + FF_LOGI("'%s' leave %s.", __func__,uevent_env[0] ); + } +#else + struct fb_event *event; + int blank; + char *uevent_env[2]; + + /* If we aren't interested in this event, skip it immediately ... */ + if (action != FB_EVENT_BLANK /* FB_EARLY_EVENT_BLANK */) { + return NOTIFY_DONE; + } + + FF_LOGV("'%s' enter.", __func__); + + event = (struct fb_event *)data; + blank = *(int *)event->data; + + switch (blank) { + case FB_BLANK_UNBLANK: + uevent_env[0] = "FF_SCREEN_ON"; + screen_state[0] = 0; + break; + case FB_BLANK_POWERDOWN: + uevent_env[0] = "FF_SCREEN_OFF"; + screen_state[0] = 1; + break; + default: + uevent_env[0] = "FF_SCREEN_??"; + break; + } + uevent_env[1] = NULL; +// kobject_uevent_env(&g_context->miscdev.this_device->kobj, KOBJ_CHANGE, uevent_env); + kill_fasync(&g_context->async_queue, SIGIO, POLL_IN); + + FF_LOGV("'%s' leave.", __func__); +#endif +#endif + return NOTIFY_OK; +} + +static int ff_ctl_register_input(void) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + + /* Allocate the input device. */ + g_context->input = input_allocate_device(); + if (!g_context->input) { + FF_LOGE("input_allocate_device() failed."); + return (-ENOMEM); + } + + /* Register the key event capabilities. */ + if (g_config) { + input_set_capability(g_context->input, EV_KEY, g_config->keycode_nav_left ); + input_set_capability(g_context->input, EV_KEY, g_config->keycode_nav_right ); + input_set_capability(g_context->input, EV_KEY, g_config->keycode_nav_up ); + input_set_capability(g_context->input, EV_KEY, g_config->keycode_nav_down ); + input_set_capability(g_context->input, EV_KEY, g_config->keycode_double_click); + input_set_capability(g_context->input, EV_KEY, g_config->keycode_click ); + input_set_capability(g_context->input, EV_KEY, g_config->keycode_long_press ); + input_set_capability(g_context->input, EV_KEY, g_config->keycode_simulation ); + } + + /* Register the allocated input device. */ + g_context->input->name = "uinput-focal"; + err = input_register_device(g_context->input); + if (err) { + FF_LOGE("input_register_device(..) = %d.", err); + input_free_device(g_context->input); + g_context->input = NULL; + return (-ENODEV); + } + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +static int ff_ctl_free_driver(void) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + + /* Unregister framebuffer event notifier. */ +#if defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) + if (g_context->active_panel) + { + drm_panel_notifier_unregister(g_context->active_panel, &g_context->fb_notifier); + g_context->active_panel = NULL; + } +#else /* CONFIG_DRM_PANEL_NOTIFICATIONS */ +#if defined(CONFIG_PANEL_NOTIFICATIONS) + err = panel_unregister_notifier(&g_context->fb_notifier); +#else + err = fb_unregister_client(&g_context->fb_notifier); +#endif +#endif + +#ifdef FF_SPI_SET + /* Disable SPI clock. */ + err = ff_ctl_enable_spiclk(0); +#endif + + /* Disable the fingerprint module's power. */ + err = ff_ctl_enable_power(0); + +#ifdef FF_SPI_SET + /* Unregister the spidev device. */ + err = ff_spi_free(); +#endif + + /* De-initialize the input subsystem. */ + if (g_context->input) { + /* + * Once input device was registered use input_unregister_device() and + * memory will be freed once last reference to the device is dropped. + */ + input_unregister_device(g_context->input); + g_context->input = NULL; + } + + /* Release IRQ resource. */ + if (g_context->irq_num > 0) { + if (g_context->irq_wake_enabled) { + err = disable_irq_wake(g_context->irq_num); + if (err) { + FF_LOGE("disable_irq_wake(%d) = %d.", g_context->irq_num, err); + } + } + free_irq(g_context->irq_num, (void*)g_context); + g_context->irq_num = -1; + } + + /* Release pins resource. */ + err = ff_ctl_free_pins(); + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +static int ff_ctl_init_driver(void) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + + if (unlikely(!g_context)) { + return (-ENOSYS); + } + + g_context->irq_wake_enabled = false; + + do { + /* Initialize the PWR/SPI/RST/INT pins resource. */ + err = ff_ctl_init_pins(&g_context->irq_num); + if (err > 0) { + g_context->b_config_dirtied = true; + } else \ + if (err) { + FF_LOGE("ff_ctl_init_pins(..) = %d.", err); + break; + } + + /* Register IRQ. */ + err = request_irq(g_context->irq_num, ff_ctl_device_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, "ff_irq", (void*)g_context); + if (err) { + FF_LOGE("request_irq(..) = %d.", err); + break; + } + + /* Wake up the system while receiving the interrupt. */ + err = enable_irq_wake(g_context->irq_num); + if (err) { + FF_LOGE("enable_irq_wake(%d) = %d.", g_context->irq_num, err); + } + g_context->irq_wake_enabled = true; + + /* Register spidev device. For REE-Emulation solution only. */ + if (g_config && g_config->enable_spidev) { +#ifdef FF_SPI_SET + err = ff_spi_init(); + if (err) { + FF_LOGE("ff_spi_init(..) = %d.", err); + break; + } +#endif + } + } while (0); + + if (err) { + ff_ctl_free_driver(); + return err; + } + + /* Initialize the input subsystem. */ + err = ff_ctl_register_input(); + if (err) { + FF_LOGE("ff_ctl_init_input() = %d.", err); + //return err; + } + + /* Enable the fingerprint module's power at system startup. */ + err = ff_ctl_enable_power(1); + +#ifdef FF_SPI_SET + /* Enable SPI clock. */ + err = ff_ctl_enable_spiclk(1); +#endif + +#if defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) + g_context->active_panel = NULL; + drm_check_dt(g_context); +#endif + /* Register screen on/off callback. */ + g_context->fb_notifier.notifier_call = ff_ctl_fb_notifier_callback; +#if defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) + if (g_context->active_panel) { + FF_LOGV("register drm_panel_notifier \n"); + err = drm_panel_notifier_register(g_context->active_panel, &g_context->fb_notifier); + if (err) + FF_LOGV("drm_panel_notifier_register fail: %d ", err); + } +#else /* CONFIG_DRM_PANEL_NOTIFICATIONS */ +#if defined(CONFIG_PANEL_NOTIFICATIONS) + err = panel_register_notifier(&g_context->fb_notifier); + if (err) + FF_LOGE(" %s : panel_register_notifier fail: %d ", __func__, err); +#else + err = fb_register_client(&g_context->fb_notifier); +#endif +#endif + g_context->b_driver_inited = true; + FF_LOGV("'%s' leave.", __func__); + return err; +} + +//////////////////////////////////////////////////////////////////////////////// +// struct file_operations fields. + +static int ff_ctl_fasync(int fd, struct file *filp, int mode) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + + FF_LOGD("%s: mode = 0x%08x.", __func__, mode); + err = fasync_helper(fd, filp, mode, &g_context->async_queue); + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +static long ff_ctl_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int err = 0; + uint32_t level = 0; + struct miscdevice *dev = (struct miscdevice *)filp->private_data; + ff_ctl_context_t *ctx = container_of(dev, ff_ctl_context_t, miscdev); + FF_LOGV("'%s' enter.", __func__); + +#if 1 + if (g_log_level <= FF_LOG_LEVEL_DBG) { + static const char *cmd_names[] = { + "FF_IOC_INIT_DRIVER", "FF_IOC_FREE_DRIVER", + "FF_IOC_RESET_DEVICE", + "FF_IOC_ENABLE_IRQ", "FF_IOC_DISABLE_IRQ", + "FF_IOC_ENABLE_SPI_CLK", "FF_IOC_DISABLE_SPI_CLK", + "FF_IOC_ENABLE_POWER", "FF_IOC_DISABLE_POWER", + "FF_IOC_REPORT_KEY_EVENT", "FF_IOC_SYNC_CONFIG", + "FF_IOC_GET_VERSION", "unknown", + }; + unsigned int _cmd = _IOC_NR(cmd); + if (_cmd > FF_IOC_GET_VERSION) { + _cmd = FF_IOC_GET_VERSION + 1; + } + FF_LOGD("%s(.., %s, ..) invoke.", __func__, cmd_names[_cmd]); + } +#endif + + switch (cmd) { + case FF_IOC_INIT_DRIVER: { + if (g_context->b_driver_inited) { + err = ff_ctl_free_driver(); + } + if (!err) { + err = ff_ctl_init_driver(); + // TODO: Sync the dirty configuration back to HAL. + } + break; + } + case FF_IOC_FREE_DRIVER: + if (g_context->b_driver_inited) { + err = ff_ctl_free_driver(); + g_context->b_driver_inited = false; + } + break; + case FF_IOC_RESET_DEVICE: + level = (uint32_t) arg; + FF_LOGD("level = %d", level); + err = ff_ctl_reset_device(level); + break; + case FF_IOC_ENABLE_IRQ: + err = ff_ctl_enable_irq(1); + break; + case FF_IOC_DISABLE_IRQ: + err = ff_ctl_enable_irq(0); + break; + case FF_IOC_ENABLE_SPI_CLK: +#ifdef FF_SPI_SET + err = ff_ctl_enable_spiclk(1); +#endif + break; + case FF_IOC_DISABLE_SPI_CLK: +#ifdef FF_SPI_SET + err = ff_ctl_enable_spiclk(0); +#endif + break; + case FF_IOC_ENABLE_POWER: + err = ff_ctl_enable_power(1); + break; + case FF_IOC_DISABLE_POWER: + err = ff_ctl_enable_power(0); + break; + case FF_IOC_REPORT_KEY_EVENT: { + ff_key_event_t kevent; + if (copy_from_user(&kevent, (ff_key_event_t *)arg, sizeof(ff_key_event_t))) { + FF_LOGE("copy_from_user(..) failed."); + err = (-EFAULT); + break; + } + err = ff_ctl_report_key_event(ctx->input, &kevent); + break; + } + case FF_IOC_SYNC_CONFIG: { + if (copy_from_user(&driver_config, (ff_driver_config_t *)arg, sizeof(ff_driver_config_t))) { + FF_LOGE("copy_from_user(..) failed."); + err = (-EFAULT); + break; + } + g_config = &driver_config; + + /* Take logging level effect. */ + g_log_level = g_config->log_level; + break; + } + case FF_IOC_GET_VERSION: { + if (copy_to_user((void *)arg, ff_ctl_get_version(), FF_DRV_VERSION_LEN)) { + FF_LOGE("copy_to_user(..) failed."); + err = (-EFAULT); + break; + } + break; + } + case FF_IOC_GET_SCREEN_STATE: { + if (copy_to_user((void *)arg, (const char*)screen_state, 1)) { + FF_LOGE("copy_to_user(..) failed."); + err = (-EFAULT); + break; + } + break; + } + default: + err = (-EINVAL); + break; + } + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +static int ff_ctl_open(struct inode *inode, struct file *filp) +{ + FF_LOGD("'%s' nothing to do.", __func__); + return 0; +} + +static int ff_ctl_release(struct inode *inode, struct file *filp) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + + /* Remove this filp from the asynchronously notified filp's. */ + err = ff_ctl_fasync(-1, filp, 0); + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +#ifdef CONFIG_COMPAT +static long ff_ctl_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int err = 0; + FF_LOGV("focal '%s' enter.\n", __func__); + + err = ff_ctl_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); + + FF_LOGV("'%s' leave.", __func__); + return err; +} +#endif +/////////////////////////////////////////////////////////////////////////////// + +static struct file_operations ff_ctl_fops = { + .owner = THIS_MODULE, + .fasync = ff_ctl_fasync, + .unlocked_ioctl = ff_ctl_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ff_ctl_compat_ioctl, +#endif + .open = ff_ctl_open, + .release = ff_ctl_release, +}; + +static ff_ctl_context_t ff_ctl_context = { + .miscdev = { + .minor = MISC_DYNAMIC_MINOR, + .name = FF_DRV_NAME, + .fops = &ff_ctl_fops, + }, 0, +}; + +static int __init ff_ctl_driver_init(void) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + + /* Register as a miscellaneous device. */ + err = misc_register(&ff_ctl_context.miscdev); + if (err) { + FF_LOGE("misc_register(..) = %d.", err); + return err; + } + + /* Init the interrupt workqueue. */ + INIT_WORK(&ff_ctl_context.work_queue, ff_ctl_device_event); + +#if 0 + ff_ctl_context.wake_lock = wakeup_source_create("ff_wake_lock"); + if (!ff_ctl_context.wake_lock) { + FF_LOGE("wakeup_source_create failed."); + return -ENOMEM; + } else { + wakeup_source_add(ff_ctl_context.wake_lock); + } +//#else + wake_lock_init(ff_ctl_context.wake_lock, WAKE_LOCK_SUSPEND, "ff_wake_lock"); +#endif + + /* Assign the context instance. */ + g_context = &ff_ctl_context; + + g_context->b_driver_inited = false; + /* Init the wake lock. */ + err = device_init_wakeup(g_context->miscdev.this_device, true); + FF_LOGI("FocalTech fingerprint device control driver registered err=%d.", err); + FF_LOGV("'%s' leave.", __func__); + return err; +} + +static void __exit ff_ctl_driver_exit(void) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + + /* Release the HW resources if needs. */ + if (g_context->b_driver_inited) { + err = ff_ctl_free_driver(); + g_context->b_driver_inited = false; + } + + /* De-init the wake lock. */ + err = device_init_wakeup(g_context->miscdev.this_device, false); + #if 0 + wakeup_source_destroy(g_context->wake_lock); +//#else + wake_lock_destroy(g_context->wake_lock); +#endif + + /* Unregister the miscellaneous device. */ + misc_deregister(&g_context->miscdev); + + /* 'g_context' could not use any more. */ + g_context = NULL; + + FF_LOGI("FocalTech fingerprint device control driver released."); + FF_LOGV("'%s' leave.", __func__); +} + +module_init(ff_ctl_driver_init); +module_exit(ff_ctl_driver_exit); + +MODULE_DESCRIPTION("The device control driver for FocalTech's fingerprint sensor."); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("FocalTech Fingerprint R&D department"); + diff --git a/drivers/input/misc/focal_fps_mmi/ff_ctl.h b/drivers/input/misc/focal_fps_mmi/ff_ctl.h new file mode 100644 index 000000000000..57a844c7213f --- /dev/null +++ b/drivers/input/misc/focal_fps_mmi/ff_ctl.h @@ -0,0 +1,118 @@ +/** + * User space driver API for FocalTech's fingerprint device. + * ATTENTION: Do NOT edit this file unless the corresponding driver changed. + * + * Copyright (C) 2016-2017 FocalTech Systems Co., Ltd. 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 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 __FF_CTRL_API_H__ +#define __FF_CTRL_API_H__ + +#include + +/* Device node. */ +#define FF_CTL_DEV_NAME "/dev/focaltech_fp" + +/* Max driver version buffer length. */ +#define FF_DRV_VERSION_LEN 32 + +/* set chip type */ +/* +#define CHIP_TYPE_FT9304 +*/ +#define CHIP_TYPE_MSM + +/* set spi */ +//#define FF_SPI_SET + +/* set vdd gpio */ +//#define FF_VDD_GPIO + +/* set iovcc gpio */ +//#define FF_IOVCC_GPIO + +typedef struct { + unsigned int code; + int value; +} ff_key_event_t; + +typedef struct { + /* Using asynchronous notification mechanism instead of NETLINK. */ + bool enable_fasync; + + /* Gesture(Key emulation & Navigation) key codes. */ + int32_t keycode_nav_left; + int32_t keycode_nav_right; + int32_t keycode_nav_up; + int32_t keycode_nav_down; + int32_t keycode_double_click; + int32_t keycode_click; + int32_t keycode_long_press; + int32_t keycode_simulation; + + /* For '/dev/spidevB.C' of REE-Emulation. */ + bool enable_spidev; + int32_t spidev_bus; + int32_t spidev_c_s; + + /* For obsolete driver that doesn't support device tree. */ + int32_t gpio_mosi_pin; + int32_t gpio_miso_pin; + int32_t gpio_ck_pin; + int32_t gpio_cs_pin; + int32_t gpio_rst_pin; + int32_t gpio_int_pin; + int32_t gpio_power_pin; + int32_t gpio_iovcc_pin; + + + /* Logging driver to logcat through uevent mechanism. */ + int32_t log_level; + bool logcat_driver; +} ff_driver_config_t; + +/* Magic code for IOCTL-subsystem, 'f'(0x66) means '[F]ocalTech'. */ +#define FF_IOC_MAGIC 'f' + +/* Allocate/Release driver resource (GPIO/SPI etc.). */ +#define FF_IOC_INIT_DRIVER _IO(FF_IOC_MAGIC, 0x00) +#define FF_IOC_FREE_DRIVER _IO(FF_IOC_MAGIC, 0x01) + +/* HW reset the fingerprint module. */ +#define FF_IOC_RESET_DEVICE _IOW(FF_IOC_MAGIC, 0x02, uint32_t) + +/* Low-level IRQ control. */ +#define FF_IOC_ENABLE_IRQ _IO(FF_IOC_MAGIC, 0x03) +#define FF_IOC_DISABLE_IRQ _IO(FF_IOC_MAGIC, 0x04) + +/* SPI bus clock control, for power-saving purpose. */ +#define FF_IOC_ENABLE_SPI_CLK _IO(FF_IOC_MAGIC, 0x05) +#define FF_IOC_DISABLE_SPI_CLK _IO(FF_IOC_MAGIC, 0x06) + +/* Fingerprint module power control. */ +#define FF_IOC_ENABLE_POWER _IO(FF_IOC_MAGIC, 0x07) +#define FF_IOC_DISABLE_POWER _IO(FF_IOC_MAGIC, 0x08) + +/* Androind system-wide key event, for navigation purpose. */ +#define FF_IOC_REPORT_KEY_EVENT _IOW(FF_IOC_MAGIC, 0x09, ff_key_event_t) + +/* Sync 'ff_driver_config_t', the driver configuration. */ +#define FF_IOC_SYNC_CONFIG _IOWR(FF_IOC_MAGIC, 0x0a, ff_driver_config_t) + +/* Query the driver version string. */ +#define FF_IOC_GET_VERSION _IOR(FF_IOC_MAGIC, 0x0b, const char) + +/* get screen state. */ +#define FF_IOC_GET_SCREEN_STATE _IOR(FF_IOC_MAGIC, 0x0c, const char) + +#endif /* __FF_CTRL_API_H__ */ diff --git a/drivers/input/misc/focal_fps_mmi/ff_err.h b/drivers/input/misc/focal_fps_mmi/ff_err.h new file mode 100644 index 000000000000..fce3ac3ae277 --- /dev/null +++ b/drivers/input/misc/focal_fps_mmi/ff_err.h @@ -0,0 +1,101 @@ +/** + * ${ANDROID_BUILD_TOP}/vendor/focaltech/src/base/focaltech/ff_err.h + * + * Copyright (C) 2014-2017 FocalTech Systems Co., Ltd. All Rights Reserved. + * +**/ + +#ifndef __FF_ERROR_H__ +#define __FF_ERROR_H__ + +#include "ff_log.h" /* FF_LOGE(..) */ + +// +// All the following macros will return while error. +// + +#define FF_CHECK_ERR(expr) \ + do { \ + int __e = (expr); \ + if (__e) { \ + FF_LOGE("'%s'.", ff_err_strerror(__e)); \ + return (__e); \ + } \ + } while (0) +/* End of FF_CHECK_ERR */ + +#define FF_CHECK_PTR(ptr) \ + do { \ + if ((ptr) == NULL) { \ + FF_CHECK_ERR(FF_ERR_NULL_PTR); \ + } \ + } while (0) +/* End of FF_CHECK_PTR */ + +#define FF_FAILURE_RETRY(expr, retry) \ + do { \ + int __e, i = 0, j = retry; \ + do { \ + __e = (expr); \ + if (!__e) break; \ + if (++i <= j) { \ + FF_LOGW("'"#expr"' failed, try again (%d/%d).", i, j); \ + } else { \ + FF_LOGE("'%s'.", ff_err_strerror(__e)); \ + return (__e); \ + } \ + } while (true); \ + } while (0) +/* End of FF_FAILURE_RETRY */ + +typedef enum { + FF_SUCCESS = 0, /* No error. */ + FF_ERR_INTERNAL = -1, /* Generic error. */ + + /* Base on unix errno. */ + FF_ERR_NOENT = -2, + FF_ERR_INTR = -4, + FF_ERR_IO = -5, + FF_ERR_AGAIN = -11, + FF_ERR_NOMEM = -12, + FF_ERR_BUSY = -16, + + /* Common error. */ + FF_ERR_BAD_PARAMS = -200, + FF_ERR_NULL_PTR = -201, + FF_ERR_BUF_OVERFLOW = -202, + FF_ERR_BAD_PROTOCOL = -203, + FF_ERR_SENSOR_SIZE = -204, + FF_ERR_NULL_DEVICE = -205, + FF_ERR_DEAD_DEVICE = -206, + FF_ERR_REACH_LIMIT = -207, + FF_ERR_REE_TEMPLATE = -208, + FF_ERR_NOT_TRUSTED = -209, +} ff_err_t; + +/* + * For some functions that don't return an int(ff_err_t) type error + * code, 'ff_last_error' shall be set if there is an error happened. + */ +extern ff_err_t ff_last_error; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Return an error description string corresponding to $err. + * + * @params + * err: Error number. + * + * @return + * Static error description string. + */ +const char *ff_err_strerror(int err); + +#ifdef __cplusplus +} +#endif + +#endif /* __FF_ERROR_H__ */ diff --git a/drivers/input/misc/focal_fps_mmi/ff_log.h b/drivers/input/misc/focal_fps_mmi/ff_log.h new file mode 100644 index 000000000000..20bfb9f6736e --- /dev/null +++ b/drivers/input/misc/focal_fps_mmi/ff_log.h @@ -0,0 +1,109 @@ +/** + * ${ANDROID_BUILD_TOP}/vendor/focaltech/src/base/focaltech/ff_log.h + * + * Copyright (C) 2014-2017 FocalTech Systems Co., Ltd. All Rights Reserved. + * +**/ + +#ifndef __FF_LOGGING_H__ +#define __FF_LOGGING_H__ + +/* + * The default LOG_TAG is 'focaltech', using these two lines to define newtag. + * # undef LOG_TAG + * #define LOG_TAG "newtag" + */ +#define LOG_TAG "focaltech" + +/* + * Log level can be used in 'logcat [:priority]', and also be + * used in output control while '__FF_EARLY_LOG_LEVEL' is defined. + */ +typedef enum { + FF_LOG_LEVEL_ALL = 0, + FF_LOG_LEVEL_VBS = 1, /* Verbose */ + FF_LOG_LEVEL_DBG = 2, /* Debug */ + FF_LOG_LEVEL_INF = 3, /* Info */ + FF_LOG_LEVEL_WRN = 4, /* Warning */ + FF_LOG_LEVEL_ERR = 5, /* Error */ +} ff_log_level_t; + +/* + * __FF_EARLY_LOG_LEVEL can be defined as compilation option. + * default level is FF_LOG_LEVEL_ALL(all the logs will be output). + */ +#ifndef __FF_EARLY_LOG_LEVEL +#define __FF_EARLY_LOG_LEVEL FF_LOG_LEVEL_ALL +#endif + +/* + * Log level can be runtime configurable. + */ +extern ff_log_level_t g_log_level; /* = __FF_EARLY_LOG_LEVEL */ + +/* + * Using the following five macros for conveniently logging. + */ +#define FF_LOGV(...) \ + do { \ + if (g_log_level <= FF_LOG_LEVEL_VBS) { \ + ff_log_printf(FF_LOG_LEVEL_VBS, LOG_TAG, __VA_ARGS__); \ + }; \ + } while (0) + +#define FF_LOGD(...) \ + do { \ + if (g_log_level <= FF_LOG_LEVEL_DBG) { \ + ff_log_printf(FF_LOG_LEVEL_DBG, LOG_TAG, __VA_ARGS__); \ + }; \ + } while (0) + +#define FF_LOGI(...) \ + do { \ + if (g_log_level <= FF_LOG_LEVEL_INF) { \ + ff_log_printf(FF_LOG_LEVEL_INF, LOG_TAG, __VA_ARGS__); \ + }; \ + } while (0) + +#define FF_LOGW(...) \ + do { \ + if (g_log_level <= FF_LOG_LEVEL_WRN) { \ + ff_log_printf(FF_LOG_LEVEL_WRN, LOG_TAG, __VA_ARGS__); \ + }; \ + } while (0) + +#define FF_LOGE(format, ...) \ + do { \ + if (g_log_level <= FF_LOG_LEVEL_ERR) { \ + const char *__fname__ = __FILE__, *s = __fname__; \ + do { if (*s == '/') __fname__ = s + 1; } while (*s++); \ + ff_log_printf(FF_LOG_LEVEL_ERR, LOG_TAG, \ + "error at %s[%s:%d]: " format, __FUNCTION__, \ + __fname__, __LINE__, ##__VA_ARGS__); \ + }; \ + } while (0) + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Logging API. Do NOT use it directly but FF_LOF* instead. + * + * @params + * level: Logging level for logcat. + * tag : Logging tag for logcat. + * fmt : See POSIX printf(..). + * ... : See POSIX printf(..). + * + * @return + * The number of characters printed, or a negative value if there + * was an output error. + */ +int ff_log_printf(ff_log_level_t level, const char *tag, const char *fmt, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* __FF_LOGGING_H__ */ diff --git a/drivers/input/misc/focal_fps_mmi/plat-msm.c b/drivers/input/misc/focal_fps_mmi/plat-msm.c new file mode 100644 index 000000000000..3bdf2debe2ef --- /dev/null +++ b/drivers/input/misc/focal_fps_mmi/plat-msm.c @@ -0,0 +1,188 @@ +/** + * plat-msm.c + * +**/ + +#include +#include +#include +#include +#include +#include +#include + +#include "ff_log.h" +#include "ff_ctl.h" + +# undef LOG_TAG +#define LOG_TAG "msm" + +#define FF_COMPATIBLE_NODE "focaltech,fingerprint" + +/* Define pinctrl state types. */ +typedef enum { + FF_PINCTRL_STATE_PWR_ACT, + FF_PINCTRL_STATE_PWR_CLR, + FF_PINCTRL_STATE_RST_CLR, + FF_PINCTRL_STATE_RST_ACT, + FF_PINCTRL_STATE_INT_ACT, + FF_PINCTRL_STATE_MAXIMUM /* Array size */ +} ff_pinctrl_state_t; + +/* Define pinctrl state names. */ +static const char *g_pinctrl_state_names[FF_PINCTRL_STATE_MAXIMUM] = { + "fpsensor_finger_power_high","fpsensor_finger_power_low","fpsensor_finger_rst_low","fpsensor_finger_rst_high","fpsensor_eint_as_int" +}; + +/* Native context and its singleton instance. */ +typedef struct { + struct pinctrl *pinctrl; + struct pinctrl_state *pin_states[FF_PINCTRL_STATE_MAXIMUM]; +} ff_msm_context_t; +static ff_msm_context_t ff_msm_context, *g_context = &ff_msm_context; + +int ff_ctl_enable_power(bool on); + +int ff_ctl_init_pins(int *irq_num) +{ + int err = 0, i; + int irq_num1 = 0; + struct device_node *dev_node = NULL; + struct platform_device *pdev = NULL; + + FF_LOGV("'%s' enter.", __func__); + + /* Find device tree node. */ + dev_node = of_find_compatible_node(NULL, NULL, FF_COMPATIBLE_NODE); + if (!dev_node) { + FF_LOGE("of_find_compatible_node(.., '%s') failed.", FF_COMPATIBLE_NODE); + return (-ENODEV); + } + FF_LOGI("dev_node :%s",dev_node->name); + + irq_num1 = irq_of_parse_and_map(dev_node, 0); + *irq_num = irq_num1; + FF_LOGI("irq number is %d.", irq_num1); + + /* Convert to platform device. */ + pdev = of_find_device_by_node(dev_node); + if (!pdev) { + FF_LOGE("of_find_device_by_node(..) failed."); + return (-ENODEV); + } + + /* Retrieve the pinctrl handler. */ + g_context->pinctrl = devm_pinctrl_get(&pdev->dev); + if (!g_context->pinctrl) { + FF_LOGE("devm_pinctrl_get(..) failed."); + return (-ENODEV); + } + + FF_LOGI("register pins."); + /* Register all pins. */ + for (i = 0; i < FF_PINCTRL_STATE_MAXIMUM; ++i) { + g_context->pin_states[i] = pinctrl_lookup_state(g_context->pinctrl, g_pinctrl_state_names[i]); + if (!g_context->pin_states[i]) { + FF_LOGE("can't find pinctrl state for '%s'.", g_pinctrl_state_names[i]); + err = (-ENODEV); + break; + } + } + if (i < FF_PINCTRL_STATE_MAXIMUM) { + return (-ENODEV); + } + + /* Initialize the INT pin. */ + FF_LOGI("init int pin."); + err = pinctrl_select_state(g_context->pinctrl, g_context->pin_states[FF_PINCTRL_STATE_INT_ACT]); + + ff_ctl_enable_power(true); + /* Initialize the RST pin. */ + pinctrl_select_state(g_context->pinctrl, g_context->pin_states[FF_PINCTRL_STATE_RST_CLR]); + + /* Delay for 10ms. */ + mdelay(10); + + /* Initialize the RST pin. */ + pinctrl_select_state(g_context->pinctrl, g_context->pin_states[FF_PINCTRL_STATE_RST_ACT]); + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +int ff_ctl_free_pins(void) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + + // TODO: + if (g_context->pinctrl) { + pinctrl_put(g_context->pinctrl); + g_context->pinctrl = NULL; + } + FF_LOGV("'%s' leave.", __func__); + return err; +} + +int ff_ctl_enable_spiclk(bool on) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + FF_LOGD("clock: '%s'.", on ? "enable" : "disabled"); + + if (on) { + // TODO: + } else { + // TODO: + } + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +int ff_ctl_enable_power(bool on) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + FF_LOGI("power: '%s'.", on ? "on" : "off"); + + if (unlikely(!g_context->pinctrl)) { + return (-ENOSYS); + } + + if (on) { + err = pinctrl_select_state(g_context->pinctrl, g_context->pin_states[FF_PINCTRL_STATE_PWR_ACT]); + } else { + err = pinctrl_select_state(g_context->pinctrl, g_context->pin_states[FF_PINCTRL_STATE_PWR_CLR]); + } + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +int ff_ctl_reset_device(uint32_t level) +{ + int err = 0; + FF_LOGV("'%s' enter.", __func__); + + if (unlikely(!g_context->pinctrl)) { + return (-ENOSYS); + } + + if (level) { + /* Pull up RST pin. */ + err = pinctrl_select_state(g_context->pinctrl, g_context->pin_states[FF_PINCTRL_STATE_RST_ACT]); + } else { + /* Pull down RST pin. */ + err = pinctrl_select_state(g_context->pinctrl, g_context->pin_states[FF_PINCTRL_STATE_RST_CLR]); + } + + FF_LOGV("'%s' leave.", __func__); + return err; +} + +const char *ff_ctl_arch_str(void) +{ + return "msm"; +} + diff --git a/drivers/input/misc/fpc_fps_mmi/Android.mk b/drivers/input/misc/fpc_fps_mmi/Android.mk new file mode 100644 index 000000000000..4936f13c6c34 --- /dev/null +++ b/drivers/input/misc/fpc_fps_mmi/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(BOARD_HAS_MULTI_FPS),true) + KBUILD_OPTIONS += CONFIG_INPUT_MISC_FPC1020_SAVE_TO_CLASS_DEVICE=y + KERNEL_CFLAGS += CONFIG_INPUT_MISC_FPC1020_SAVE_TO_CLASS_DEVICE=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := fpc1020_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/misc/fpc_fps_mmi/Kbuild b/drivers/input/misc/fpc_fps_mmi/Kbuild new file mode 100644 index 000000000000..a130565a878d --- /dev/null +++ b/drivers/input/misc/fpc_fps_mmi/Kbuild @@ -0,0 +1,21 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +prod_list := devon +find_target := $(foreach find, $(prod_list), $(findstring $(find), $(TARGET_PRODUCT))) +find_target := $(strip $(find_target)) +$(warning find_target=$(find_target)) +ifneq ($(find_target),) +$(warning define SUPPORT_PIN_CTRL) +EXTRA_CFLAGS += -DSUPPORT_PIN_CTRL +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_MISC_FPC1020_SAVE_TO_CLASS_DEVICE)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_MISC_FPC1020_SAVE_TO_CLASS_DEVICE +endif + +fpc1020_mmi-y += fpc1020_tee.o + +obj-m += fpc1020_mmi.o + diff --git a/drivers/input/misc/fpc_fps_mmi/Makefile b/drivers/input/misc/fpc_fps_mmi/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/input/misc/fpc_fps_mmi/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/input/misc/fpc_fps_mmi/fpc1020_tee.c b/drivers/input/misc/fpc_fps_mmi/fpc1020_tee.c new file mode 100644 index 000000000000..ec76edff7a28 --- /dev/null +++ b/drivers/input/misc/fpc_fps_mmi/fpc1020_tee.c @@ -0,0 +1,671 @@ +/* + * FPC1020 Fingerprint sensor device driver + * + * Copyright (c) 2015 Fingerprint Cards AB + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RESET_LOW_SLEEP_MIN_US 5000 +#define RESET_LOW_SLEEP_MAX_US (RESET_LOW_SLEEP_MIN_US + 100) +#define RESET_HIGH_SLEEP1_MIN_US 100 +#define RESET_HIGH_SLEEP1_MAX_US (RESET_HIGH_SLEEP1_MIN_US + 100) +#define RESET_HIGH_SLEEP2_MIN_US 5000 +#define RESET_HIGH_SLEEP2_MAX_US (RESET_HIGH_SLEEP2_MIN_US + 100) + +struct FPS_data { + unsigned int enabled; + unsigned int state; + struct blocking_notifier_head nhead; +} *fpsData; + +struct FPS_data *FPS_init(struct device *dev) +{ + struct FPS_data *mdata = devm_kzalloc(dev, + sizeof(struct FPS_data), GFP_KERNEL); + if (mdata) { + BLOCKING_INIT_NOTIFIER_HEAD(&mdata->nhead); + pr_debug("%s: FPS notifier data structure init-ed\n", __func__); + } + return mdata; +} + +int FPS_register_notifier(struct notifier_block *nb, + unsigned long stype, bool report) +{ + int error; + struct FPS_data *mdata = fpsData; + + if (!mdata) + return -ENODEV; + + mdata->enabled = (unsigned int)stype; + pr_info("%s: FPS sensor %lu notifier enabled\n", __func__, stype); + + error = blocking_notifier_chain_register(&mdata->nhead, nb); + if (!error && report) { + int state = mdata->state; + /* send current FPS state on register request */ + blocking_notifier_call_chain(&mdata->nhead, + stype, (void *)&state); + pr_debug("%s: FPS reported state %d\n", __func__, state); + } + return error; +} +EXPORT_SYMBOL_GPL(FPS_register_notifier); + +int FPS_unregister_notifier(struct notifier_block *nb, + unsigned long stype) +{ + int error; + struct FPS_data *mdata = fpsData; + + if (!mdata) + return -ENODEV; + + error = blocking_notifier_chain_unregister(&mdata->nhead, nb); + pr_debug("%s: FPS sensor %lu notifier unregister\n", __func__, stype); + + if (!mdata->nhead.head) { + mdata->enabled = 0; + pr_info("%s: FPS sensor %lu no clients\n", __func__, stype); + } + + return error; +} +EXPORT_SYMBOL_GPL(FPS_unregister_notifier); + +void FPS_notify(unsigned long stype, int state) +{ + struct FPS_data *mdata = fpsData; + + pr_debug("%s: Enter", __func__); + + if (!mdata) { + pr_err("%s: FPS notifier not initialized yet\n", __func__); + return; + } else if (!mdata->enabled) { + pr_debug("%s: !mdata->enabled", __func__); + return; + } + + pr_debug("%s: FPS current state %d -> (0x%x)\n", __func__, + mdata->state, state); + + if (mdata->state != state) { + mdata->state = state; + blocking_notifier_call_chain(&mdata->nhead, + stype, (void *)&state); + pr_debug("%s: FPS notification sent\n", __func__); + } else + pr_warn("%s: mdata->state==state", __func__); +} + +struct fpc1020_data { + struct device *dev; +#ifdef CONFIG_INPUT_MISC_FPC1020_SAVE_TO_CLASS_DEVICE + struct device *class_dev; +#endif + struct platform_device *pdev; + struct notifier_block nb; + int irq_gpio; + int irq_num; + unsigned int irq_cnt; + int rst_gpio; + int pwr_gpio; + int power_enabled; + unsigned int rgltr_ctrl_support; //whether regulator control is supported + struct regulator *pwr_supply; + int pwr_voltage_range[2]; + int pwr_load[1]; +}; + +static int fpc1020_power_on(struct fpc1020_data *fpc1020) +{ + int rc = 0; + if(!fpc1020) return 0; + + if (!fpc1020->power_enabled) { + if(fpc1020->rgltr_ctrl_support && !IS_ERR_OR_NULL(fpc1020->pwr_supply)) { + rc = regulator_enable(fpc1020->pwr_supply); + pr_warn(" %s : enable pwr_supply return %d \n", __func__, rc); + } + if (gpio_is_valid(fpc1020->pwr_gpio)) { + gpio_direction_output(fpc1020->pwr_gpio, 1); + } + fpc1020->power_enabled = 1; + if(fpc1020->rgltr_ctrl_support ||gpio_is_valid(fpc1020->pwr_gpio)){ + if (!of_property_read_bool(fpc1020->dev->of_node, "delay-ctrl-support")) { + usleep_range(11000,12000); + } + } + } + return rc; +} + +int fpc1020_power_off(struct fpc1020_data *fpc1020) +{ + int rc = 0; + if(!fpc1020) return 0; + if (fpc1020->power_enabled) { + if (fpc1020->rgltr_ctrl_support && !IS_ERR_OR_NULL(fpc1020->pwr_supply)) { + rc = regulator_disable(fpc1020->pwr_supply); + pr_warn(" %s : disable pwr_supply return %d \n", __func__, rc); + } + if (gpio_is_valid(fpc1020->pwr_gpio)) { + gpio_direction_output(fpc1020->pwr_gpio, 0); + } + fpc1020->power_enabled = 0; + } + return rc; +} + +static int hw_reset(struct fpc1020_data *fpc1020) +{ + int irq_gpio; + struct device *dev = fpc1020->dev; + int rc = 0; + + if (!gpio_is_valid(fpc1020->rst_gpio)) { + dev_warn(dev, "reset pin is invalid\n"); + goto exit; + } + + rc = gpio_direction_output(fpc1020->rst_gpio, 1); + if (rc) + goto exit; + usleep_range(RESET_HIGH_SLEEP1_MIN_US, RESET_HIGH_SLEEP1_MAX_US); + + rc = gpio_direction_output(fpc1020->rst_gpio, 0); + if (rc) + goto exit; + usleep_range(RESET_LOW_SLEEP_MIN_US, RESET_LOW_SLEEP_MAX_US); + + rc = gpio_direction_output(fpc1020->rst_gpio, 1); + if (rc) + goto exit; + usleep_range(RESET_HIGH_SLEEP2_MIN_US, RESET_HIGH_SLEEP2_MAX_US); + + irq_gpio = gpio_get_value(fpc1020->irq_gpio); + dev_info(dev, "IRQ after reset %d\n", irq_gpio); + +exit: + return rc; +} +#ifdef SUPPORT_PIN_CTRL +static void fpc_pinctrl_on(struct device *dev) +{ + struct pinctrl *ptl = NULL; + struct pinctrl_state *ptl_state = NULL; + + pr_info("fpc fpc_pinctrl_on begin\n"); + ptl = devm_pinctrl_get(dev); + ptl_state = pinctrl_lookup_state(ptl, "fpc_irq_en"); + pinctrl_select_state(ptl, ptl_state); + ptl_state = pinctrl_lookup_state(ptl, "fpc_vdd_on"); + pinctrl_select_state(ptl, ptl_state); + ptl_state = pinctrl_lookup_state(ptl, "fpc_rst_hi"); + pinctrl_select_state(ptl, ptl_state); + msleep(10); + ptl_state = pinctrl_lookup_state(ptl, "fpc_rst_lo"); + pinctrl_select_state(ptl, ptl_state); + msleep(5); + ptl_state = pinctrl_lookup_state(ptl, "fpc_rst_hi"); + pinctrl_select_state(ptl, ptl_state); + msleep(3); + devm_pinctrl_put(ptl); + pr_info("fpc fpc_pinctrl_on end\n"); +} +#endif +static ssize_t hw_reset_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int rc; + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + dev_info(dev," %s : hw_reset_set %s\n", __func__, (buf == NULL) ? "":buf); + if (!strncmp(buf, "reset", strlen("reset"))) { + rc = hw_reset(fpc1020); + } else if (!strncmp(buf, "poweroff", strlen("poweroff"))) { + rc = fpc1020_power_off(fpc1020); + } else if (!strncmp(buf, "poweron", strlen("poweron"))) { + rc = fpc1020_power_on(fpc1020); + } + #ifdef SUPPORT_PIN_CTRL + else if (!strncmp(buf, "pinctrl", strlen("pinctrl"))) { + fpc_pinctrl_on(dev); + pr_info("fpc IRQ after reset %d\n", gpio_get_value(fpc1020->irq_gpio)); + rc = count; + } + #endif + else { + return -EINVAL; + } + return rc ? rc : count; +} +static DEVICE_ATTR(hw_reset, S_IWUSR, NULL, hw_reset_set); + +static ssize_t dev_enable_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(dev); + + int state = (*buf == '1') ? 1 : 0; + + FPS_notify(0xbeef, state); + dev_dbg(fpc1020->dev, "%s state = %d\n", __func__, state); + return 1; +} +static DEVICE_ATTR(dev_enable, S_IWUSR | S_IWGRP, NULL, dev_enable_set); + +static ssize_t irq_get(struct device *device, + struct device_attribute *attribute, + char *buffer) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(device); + int irq = gpio_get_value(fpc1020->irq_gpio); + + return scnprintf(buffer, PAGE_SIZE, "%i\n", irq); +} +static DEVICE_ATTR(irq, S_IRUSR | S_IRGRP, irq_get, NULL); + +static ssize_t irq_cnt_get(struct device *device, + struct device_attribute *attribute, + char *buffer) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(device); + + return scnprintf(buffer, PAGE_SIZE, "%u\n", fpc1020->irq_cnt); +} +static DEVICE_ATTR(irq_cnt, S_IRUSR, irq_cnt_get, NULL); + +#ifdef CONFIG_INPUT_MISC_FPC1020_SAVE_TO_CLASS_DEVICE +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "fpc"); +} +static DEVICE_ATTR_RO(vendor); + +static ssize_t modalias_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "fpc1020"); +} +static DEVICE_ATTR_RO(modalias); +#endif + +static struct attribute *attributes[] = { + &dev_attr_dev_enable.attr, + &dev_attr_irq.attr, + &dev_attr_irq_cnt.attr, + &dev_attr_hw_reset.attr, +#ifdef CONFIG_INPUT_MISC_FPC1020_SAVE_TO_CLASS_DEVICE + &dev_attr_vendor.attr, + &dev_attr_modalias.attr, +#endif + NULL +}; + +static const struct attribute_group attribute_group = { + .attrs = attributes, +}; + +#ifdef CONFIG_INPUT_MISC_FPC1020_SAVE_TO_CLASS_DEVICE +static const struct attribute_group *attribute_groups[] = { + &attribute_group, + NULL +}; +#endif + +#define MAX_UP_TIME (1 * MSEC_PER_SEC) + +static irqreturn_t fpc1020_irq_handler(int irq, void *handle) +{ + struct fpc1020_data *fpc1020 = handle; + + pm_wakeup_event(fpc1020->dev, MAX_UP_TIME); + dev_dbg(fpc1020->dev, "%s\n", __func__); + fpc1020->irq_cnt++; +#ifdef CONFIG_INPUT_MISC_FPC1020_SAVE_TO_CLASS_DEVICE + sysfs_notify(&fpc1020->class_dev->kobj, NULL, dev_attr_irq.attr.name); +#else + sysfs_notify(&fpc1020->dev->kobj, NULL, dev_attr_irq.attr.name); +#endif + return IRQ_HANDLED; +} + +static int fpc1020_request_named_gpio(struct fpc1020_data *fpc1020, + const char *label, int *gpio) +{ + struct device *dev = fpc1020->dev; + struct device_node *np = dev->of_node; + int rc; + + *gpio = of_get_named_gpio(np, label, 0); + if (!gpio_is_valid(*gpio)) { + dev_err(dev, "gpio %s is invalid\n", label); + return -EINVAL; + } + rc = devm_gpio_request(dev, *gpio, label); + if (rc) { + dev_err(dev, "failed to request gpio %d\n", *gpio); + return rc; + } + dev_dbg(dev, "%s %d\n", label, *gpio); + return 0; +} + +#ifdef CONFIG_INPUT_MISC_FPC1020_SAVE_TO_CLASS_DEVICE +#define MAX_INSTANCE 5 +#define MAJOR_BASE 32 +static int fpc1020_create_sysfs(struct fpc1020_data *fpc1020, bool create) { + struct device *dev = fpc1020->dev; + static struct class *fingerprint_class; + static dev_t dev_no; + int rc = 0; + + if (create) { + rc = alloc_chrdev_region(&dev_no, MAJOR_BASE, MAX_INSTANCE, "fpc"); + if (rc < 0) { + dev_err(dev, "%s alloc fingerprint class device MAJOR failed.\n", __func__); + goto ALLOC_REGION; + } + if (!fingerprint_class) { + fingerprint_class = class_create(THIS_MODULE, "fingerprint"); + if (IS_ERR(fingerprint_class)) { + dev_err(dev, "%s create fingerprint class failed.\n", __func__); + rc = PTR_ERR(fingerprint_class); + fingerprint_class = NULL; + goto CLASS_CREATE_ERR; + } + } + fpc1020->class_dev = device_create_with_groups(fingerprint_class, NULL, + MAJOR(dev_no), fpc1020, attribute_groups, "fpc1020"); + if (IS_ERR(fpc1020->class_dev)) { + dev_err(dev, "%s create fingerprint class device failed.\n", __func__); + rc = PTR_ERR(fpc1020->class_dev); + fpc1020->class_dev = NULL; + goto DEVICE_CREATE_ERR; + } + return 0; + } + + device_destroy(fingerprint_class, MAJOR(dev_no)); + fpc1020->class_dev = NULL; +DEVICE_CREATE_ERR: + class_destroy(fingerprint_class); + fingerprint_class = NULL; +CLASS_CREATE_ERR: + unregister_chrdev_region(dev_no, 1); +ALLOC_REGION: + return rc; +} +#endif + +static int fpc1020_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int rc = 0; + int irqf; + struct device_node *np = dev->of_node; + struct fpc1020_data *fpc1020 = devm_kzalloc(dev, sizeof(*fpc1020), + GFP_KERNEL); + if (!fpc1020) { + rc = -ENOMEM; + goto exit; + } + #ifdef SUPPORT_PIN_CTRL + if(of_property_read_bool(np,"fpc_pinctrl_on")) { + fpc_pinctrl_on(dev); + } + #endif + fpsData = FPS_init(dev); + + fpc1020->dev = dev; + dev_set_drvdata(dev, fpc1020); + fpc1020->pdev = pdev; + fpc1020->power_enabled = 0; + fpc1020->pwr_supply = NULL; + if (!np) { + dev_err(dev, "no of node found\n"); + rc = -EINVAL; + goto exit; + } + fpc1020->pwr_gpio = of_get_named_gpio(np, "fp-gpio-ven", 0); + if (fpc1020->pwr_gpio < 0) { + pr_warn("failed to get pwr gpio!\n"); + fpc1020->pwr_gpio = -1; + } else { + if (gpio_is_valid(fpc1020->pwr_gpio)) { + rc = devm_gpio_request(dev, fpc1020->pwr_gpio, "fpc_pwr"); + if (rc) { + pr_err("failed to request pwr gpio, rc = %d\n", rc); + goto err_pwr; + } + gpio_direction_output(fpc1020->pwr_gpio, 1); + } + } + if(of_property_read_bool(np,"rgltr-ctrl-support")) { + fpc1020->rgltr_ctrl_support = 1; + } else { + fpc1020->rgltr_ctrl_support = 0; + pr_err("No regulator control parameter defined\n"); + } + if (fpc1020->rgltr_ctrl_support) { + fpc1020->pwr_supply = regulator_get(dev, "fp,vdd"); + if (IS_ERR_OR_NULL(fpc1020->pwr_supply)) { + fpc1020->pwr_supply = NULL; + fpc1020->rgltr_ctrl_support = 0; + pr_warn("Unable to get fp,vdd-regulator"); + } else { + rc = of_property_read_u32_array(np, "fp,voltage-range", fpc1020->pwr_voltage_range, 2); + if (rc) { + fpc1020->pwr_voltage_range[0] = -1; + fpc1020->pwr_voltage_range[1] = -1; + } + if (regulator_count_voltages(fpc1020->pwr_supply) > 0) { + if((fpc1020->pwr_voltage_range[0] >0) && (fpc1020->pwr_voltage_range[1] > 0)) + rc = regulator_set_voltage(fpc1020->pwr_supply, fpc1020->pwr_voltage_range[0], fpc1020->pwr_voltage_range[1]); + if (rc) { + pr_warn(" %s : set vdd regulator voltage failed %d \n", __func__, rc); + } + } + } + } + + fpc1020_power_on(fpc1020); + + rc = fpc1020_request_named_gpio(fpc1020, "irq", + &fpc1020->irq_gpio); + gpio_direction_input(fpc1020->irq_gpio); + if (rc) + goto exit; + + rc = fpc1020_request_named_gpio(fpc1020, "rst", + &fpc1020->rst_gpio); + if (rc) + fpc1020->rst_gpio = -EINVAL; + + if (gpio_is_valid(fpc1020->rst_gpio)) { + if (!of_property_read_bool(np, "delay-ctrl-support")) { + usleep_range(RESET_LOW_SLEEP_MIN_US, RESET_LOW_SLEEP_MAX_US); + } + rc = gpio_direction_output(fpc1020->rst_gpio, 1); + if (rc) { + dev_err(dev, "cannot set reset pin direction\n"); + goto exit; + } + } + + rc = device_init_wakeup(fpc1020->dev, true); + if (rc) + goto exit; + +#ifdef CONFIG_INPUT_MISC_FPC1020_SAVE_TO_CLASS_DEVICE + rc = fpc1020_create_sysfs(fpc1020, true); +#else + rc = sysfs_create_group(&dev->kobj, &attribute_group); +#endif + if (rc) { + dev_err(dev, "could not create sysfs\n"); + goto exit; + } + + fpc1020->irq_cnt = 0; + irqf = IRQF_TRIGGER_RISING | IRQF_ONESHOT; + + rc = devm_request_threaded_irq(dev, gpio_to_irq(fpc1020->irq_gpio), + NULL, fpc1020_irq_handler, irqf, + dev_name(dev), fpc1020); + if (rc) { + dev_err(dev, "could not request irq %d\n", + gpio_to_irq(fpc1020->irq_gpio)); + goto irq_exit; + } + dev_dbg(dev, "requested irq %d\n", gpio_to_irq(fpc1020->irq_gpio)); + + /* Request that the interrupt should be wakeable */ + enable_irq_wake(gpio_to_irq(fpc1020->irq_gpio)); + + dev_info(dev, "%s: ok\n", __func__); + + return 0; + +irq_exit: +#ifdef CONFIG_INPUT_MISC_FPC1020_SAVE_TO_CLASS_DEVICE + fpc1020_create_sysfs(fpc1020, false); +#else + sysfs_remove_group(&pdev->dev.kobj, &attribute_group); +#endif +exit: +err_pwr: + if(!IS_ERR_OR_NULL(fpc1020->pwr_supply)) + { + pr_info(" %s : devm_regulator_put \n", __func__); + regulator_disable(fpc1020->pwr_supply); + regulator_put(fpc1020->pwr_supply); + fpc1020->pwr_supply= NULL; + } + + if (gpio_is_valid(fpc1020->pwr_gpio)) { + devm_gpio_free(fpc1020->dev,fpc1020->pwr_gpio); + fpc1020->pwr_gpio = -1; + pr_info("remove pwr_gpio success\n"); + } + return rc; +} + +static int fpc1020_remove(struct platform_device *pdev) +{ + struct fpc1020_data *fpc1020 = dev_get_drvdata(&pdev->dev); + + disable_irq(gpio_to_irq(fpc1020->irq_gpio)); +#ifdef CONFIG_INPUT_MISC_FPC1020_SAVE_TO_CLASS_DEVICE + fpc1020_create_sysfs(fpc1020, false); +#else + sysfs_remove_group(&pdev->dev.kobj, &attribute_group); +#endif + + device_init_wakeup(fpc1020->dev, false); + devm_free_irq(fpc1020->dev, gpio_to_irq(fpc1020->irq_gpio),fpc1020); + if(fpc1020->rgltr_ctrl_support ||gpio_is_valid(fpc1020->pwr_gpio)){ + fpc1020_power_off(fpc1020); + } + if (gpio_is_valid(fpc1020->irq_gpio)) { + devm_gpio_free(fpc1020->dev, fpc1020->irq_gpio); + fpc1020->irq_gpio = -1; + } + if (gpio_is_valid(fpc1020->rst_gpio)) { + devm_gpio_free(fpc1020->dev, fpc1020->rst_gpio); + fpc1020->rst_gpio = -1; + + } + if (fpc1020->rgltr_ctrl_support && !IS_ERR_OR_NULL(fpc1020->pwr_supply)) + { + regulator_put(fpc1020->pwr_supply); + fpc1020->pwr_supply= NULL; + pr_info(" %s : regulator_put vdd \n", __func__); + } + if (gpio_is_valid(fpc1020->pwr_gpio)) { + devm_gpio_free(fpc1020->dev,fpc1020->pwr_gpio); + fpc1020->pwr_gpio = -1; + pr_info("remove pwr_gpio success\n"); + } + dev_info(&pdev->dev, "%s\n", __func__); + return 0; +} + +static int fpc1020_suspend(struct device *dev) +{ + return 0; +} + +static int fpc1020_resume(struct device *dev) +{ + return 0; +} + +static const struct dev_pm_ops fpc1020_pm_ops = { + .suspend = fpc1020_suspend, + .resume = fpc1020_resume, +}; + +static const struct of_device_id fpc1020_of_match[] = { + { .compatible = "fpc,fpc1020", }, + {} +}; +MODULE_DEVICE_TABLE(of, fpc1020_of_match); + +static struct platform_driver fpc1020_driver = { + .driver = { + .name = "fpc1020", + .owner = THIS_MODULE, + .of_match_table = fpc1020_of_match, +#if defined(CONFIG_PM) + .pm = &fpc1020_pm_ops, +#endif + }, + .probe = fpc1020_probe, + .remove = fpc1020_remove, +}; + +static int __init fpc1020_init(void) +{ + int rc = platform_driver_register(&fpc1020_driver); + + if (!rc) + pr_debug("%s OK\n", __func__); + else + pr_err("%s %d\n", __func__, rc); + return rc; +} + +static void __exit fpc1020_exit(void) +{ + pr_debug("%s\n", __func__); + platform_driver_unregister(&fpc1020_driver); +} + +module_init(fpc1020_init); +module_exit(fpc1020_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Aleksej Makarov"); +MODULE_AUTHOR("Henrik Tillman "); +MODULE_DESCRIPTION("FPC1020 Fingerprint sensor device driver."); diff --git a/drivers/input/misc/goodix_fod_mmi/Android.mk b/drivers/input/misc/goodix_fod_mmi/Android.mk new file mode 100644 index 000000000000..95d49ba78d33 --- /dev/null +++ b/drivers/input/misc/goodix_fod_mmi/Android.mk @@ -0,0 +1,18 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(GOODIX_FPS_DRM_PANEL_NOTIFICATIONS),true) + KERNEL_CFLAGS += CONFIG_GOODIX_DRM_PANEL_NOTIFICATIONS=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := goodix_fod_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +ifneq ($(findstring mmi_relay.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KERNEL_CFLAGS += CONFIG_MMI_RELAY=y + LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_relay.ko +endif +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/misc/goodix_fod_mmi/Kbuild b/drivers/input/misc/goodix_fod_mmi/Kbuild new file mode 100644 index 000000000000..2be732aa5a56 --- /dev/null +++ b/drivers/input/misc/goodix_fod_mmi/Kbuild @@ -0,0 +1,21 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + +ifneq ($(filter m y,$(CONFIG_MMI_RELAY)),) + EXTRA_CFLAGS += -DMMI_RELAY_MODULE +endif + +ifneq ($(filter m y,$(CONFIG_GOODIX_DRM_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_GOODIX_DRM_PANEL_NOTIFICATIONS +endif + +ifneq ($(filter banks%, $(TARGET_PRODUCT)),) + EXTRA_CFLAGS += -DSOFTDEP_GPIO_PCAL6408 +endif + +goodix_fod_mmi-y += gf_spi.o netlink.o platform.o + +obj-m += goodix_fod_mmi.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../mmi_relay/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/misc/goodix_fod_mmi/Makefile b/drivers/input/misc/goodix_fod_mmi/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/input/misc/goodix_fod_mmi/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/input/misc/goodix_fod_mmi/gf_spi.c b/drivers/input/misc/goodix_fod_mmi/gf_spi.c new file mode 100644 index 000000000000..861a1a51a3b0 --- /dev/null +++ b/drivers/input/misc/goodix_fod_mmi/gf_spi.c @@ -0,0 +1,1071 @@ +/* + * TEE driver for goodix fingerprint sensor + * Copyright (C) 2016 Goodix + * + * 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 pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef MMI_RELAY_MODULE +#include +#endif +#include "gf_spi.h" + +#if defined(USE_SPI_BUS) +#include +#include +#elif defined(USE_PLATFORM_BUS) +#include +#endif +#include + +#if defined(CONFIG_GOODIX_DRM_PANEL_NOTIFICATIONS) +#include +#endif + +#define VER_MAJOR 1 +#define VER_MINOR 2 +#define PATCH_LEVEL 4 +#define EXTEND_VER 2 + +#define WAKELOCK_HOLD_TIME 500 /* in ms */ + +#define GF_SPIDEV_NAME "goodix,fingerprint" +/*device name after register in character*/ +#define GF_DEV_NAME "goodix_fp" +#define GF_INPUT_NAME "qwerty" /*"goodix_fp" */ + +#define CHRD_DRIVER_NAME "goodix_fp_spi" +#define CLASS_NAME "goodix_fp" + +#define N_SPI_MINORS 32 /* ... up to 256 */ +static int SPIDEV_MAJOR; + +static DECLARE_BITMAP(minors, N_SPI_MINORS); +static LIST_HEAD(device_list); +static DEFINE_MUTEX(device_list_lock); +static struct gf_dev gf; + +static struct gf_key_map maps[] = { + { EV_KEY, GF_KEY_INPUT_HOME }, + { EV_KEY, GF_KEY_INPUT_MENU }, + { EV_KEY, GF_KEY_INPUT_BACK }, + { EV_KEY, GF_KEY_INPUT_POWER }, +#if defined(SUPPORT_NAV_EVENT) + { EV_KEY, GF_NAV_INPUT_UP }, + { EV_KEY, GF_NAV_INPUT_DOWN }, + { EV_KEY, GF_NAV_INPUT_RIGHT }, + { EV_KEY, GF_NAV_INPUT_LEFT }, + { EV_KEY, GF_KEY_INPUT_CAMERA }, + { EV_KEY, GF_NAV_INPUT_CLICK }, + { EV_KEY, GF_NAV_INPUT_DOUBLE_CLICK }, + { EV_KEY, GF_NAV_INPUT_LONG_PRESS }, + { EV_KEY, GF_NAV_INPUT_HEAVY }, +#endif +}; +#ifdef MMI_RELAY_MODULE +struct FPS_data { + unsigned int enabled; + unsigned int state; + struct notifier_block relay_notif; +} *fpsData; +#endif +static void gf_enable_irq(struct gf_dev *gf_dev) +{ + if (gf_dev->irq_enabled) { + pr_warn("IRQ has been enabled.\n"); + } else { + enable_irq(gf_dev->irq); + gf_dev->irq_enabled = 1; + } +} + +static void gf_disable_irq(struct gf_dev *gf_dev) +{ + if (gf_dev->irq_enabled) { + gf_dev->irq_enabled = 0; + disable_irq(gf_dev->irq); + } else { + pr_warn("IRQ has been disabled.\n"); + } +} +#ifdef MMI_RELAY_MODULE +static int fps_mmi_relay_cb(struct notifier_block *self, + unsigned long event, void *p) +{ + struct FPS_data *mdata = container_of( + self, struct FPS_data, relay_notif); + + if (!mdata) + return -ENODEV; + + if (event == RELAY_NOTIFY_REGISTER) { + int state = mdata->state; + mdata->enabled = 1; + /* send current FPS state on register request */ + relay_notifier_fire(BLOCKING, FPS, 0xBEEF, (void *)&state); + pr_info("%s: FPS reported state %d\n", __func__, state); + + } else if (event == RELAY_NOTIFY_UNREGISTER) + mdata->enabled = 0; + + return 0; +} + + +static struct FPS_data *FPS_init(struct device *dev) +{ + struct FPS_data *mdata = devm_kzalloc(dev, + sizeof(struct FPS_data), GFP_KERNEL); + + mdata->relay_notif.notifier_call = fps_mmi_relay_cb; + /* register a blocking notification to receive notify form MMI_RELAY dev*/ + relay_register_action(BLOCKING, MMI_RELAY, &mdata->relay_notif); + + return mdata; +} + +void FPS_notify(unsigned long stype, int state) +{ + struct FPS_data *mdata = fpsData; + + pr_debug("%s: Enter", __func__); + + if (!mdata) { + pr_err("%s: FPS notifier not initialized yet\n", __func__); + return; + } else if (!mdata->enabled) { + pr_debug("%s: !mdata->enabled", __func__); + return; + } + + pr_debug("%s: FPS current state %d -> (0x%x)\n", __func__, + mdata->state, state); + + if (mdata->state != state) { + mdata->state = state; + relay_notifier_fire(BLOCKING, FPS, 0xBEEF, (void *)&state); + pr_debug("%s: FPS notification sent\n", __func__); + } else + pr_warn("%s: mdata->state==state", __func__); +} +#endif +static ssize_t dev_enable_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int state = (*buf == '1') ? 1 : 0; +#ifdef MMI_RELAY_MODULE + FPS_notify(0xbeef, state); +#endif + dev_dbg(dev, "%s state = %d\n", __func__, state); + return count; +} + +static DEVICE_ATTR(dev_enable, S_IWUSR | S_IWGRP, NULL, dev_enable_set); + + +static struct attribute *attributes[] = { + &dev_attr_dev_enable.attr, + NULL +}; + +static const struct attribute_group attribute_group = { + .attrs = attributes, +}; + +#ifdef AP_CONTROL_CLK +static long spi_clk_max_rate(struct clk *clk, unsigned long rate) +{ + long lowest_available, nearest_low, step_size, cur; + long step_direction = -1; + long guess = rate; + int max_steps = 10; + + cur = clk_round_rate(clk, rate); + if (cur == rate) + return rate; + + /* if we got here then: cur > rate */ + lowest_available = clk_round_rate(clk, 0); + if (lowest_available > rate) + return -EINVAL; + + step_size = (rate - lowest_available) >> 1; + nearest_low = lowest_available; + + while (max_steps-- && step_size) { + guess += step_size * step_direction; + cur = clk_round_rate(clk, guess); + + if ((cur < rate) && (cur > nearest_low)) + nearest_low = cur; + /* + * if we stepped too far, then start stepping in the other + * direction with half the step size + */ + if (((cur > rate) && (step_direction > 0)) + || ((cur < rate) && (step_direction < 0))) { + step_direction = -step_direction; + step_size >>= 1; + } + } + return nearest_low; +} + +static void spi_clock_set(struct gf_dev *gf_dev, int speed) +{ + long rate; + int rc; + + rate = spi_clk_max_rate(gf_dev->core_clk, speed); + if (rate < 0) { + pr_info("%s: no match found for requested clock frequency:%d", + __func__, speed); + return; + } + + rc = clk_set_rate(gf_dev->core_clk, rate); +} + +static int gfspi_ioctl_clk_init(struct gf_dev *data) +{ + pr_debug("%s: enter\n", __func__); + + data->clk_enabled = 0; + data->core_clk = clk_get(&data->spi->dev, "core_clk"); + if (IS_ERR_OR_NULL(data->core_clk)) { + pr_err("%s: fail to get core_clk\n", __func__); + return -EPERM; + } + data->iface_clk = clk_get(&data->spi->dev, "iface_clk"); + if (IS_ERR_OR_NULL(data->iface_clk)) { + pr_err("%s: fail to get iface_clk\n", __func__); + clk_put(data->core_clk); + data->core_clk = NULL; + return -ENOENT; + } + return 0; +} + +static int gfspi_ioctl_clk_enable(struct gf_dev *data) +{ + int err; + + pr_debug("%s: enter\n", __func__); + + if (data->clk_enabled) + return 0; + + err = clk_prepare_enable(data->core_clk); + if (err) { + pr_err("%s: fail to enable core_clk\n", __func__); + return -EPERM; + } + + err = clk_prepare_enable(data->iface_clk); + if (err) { + pr_err("%s: fail to enable iface_clk\n", __func__); + clk_disable_unprepare(data->core_clk); + return -ENOENT; + } + + data->clk_enabled = 1; + + return 0; +} + +static int gfspi_ioctl_clk_disable(struct gf_dev *data) +{ + pr_debug("%s: enter\n", __func__); + + if (!data->clk_enabled) + return 0; + + clk_disable_unprepare(data->core_clk); + clk_disable_unprepare(data->iface_clk); + data->clk_enabled = 0; + + return 0; +} + +static int gfspi_ioctl_clk_uninit(struct gf_dev *data) +{ + pr_debug("%s: enter\n", __func__); + + if (data->clk_enabled) + gfspi_ioctl_clk_disable(data); + + if (!IS_ERR_OR_NULL(data->core_clk)) { + clk_put(data->core_clk); + data->core_clk = NULL; + } + + if (!IS_ERR_OR_NULL(data->iface_clk)) { + clk_put(data->iface_clk); + data->iface_clk = NULL; + } + + return 0; +} +#endif + +static void nav_event_input(struct gf_dev *gf_dev, gf_nav_event_t nav_event) +{ + uint32_t nav_input = 0; + + switch (nav_event) { + case GF_NAV_FINGER_DOWN: + pr_debug("%s nav finger down\n", __func__); + break; + + case GF_NAV_FINGER_UP: + pr_debug("%s nav finger up\n", __func__); + break; + + case GF_NAV_DOWN: + nav_input = GF_NAV_INPUT_DOWN; + pr_debug("%s nav down\n", __func__); + break; + + case GF_NAV_UP: + nav_input = GF_NAV_INPUT_UP; + pr_debug("%s nav up\n", __func__); + break; + + case GF_NAV_LEFT: + nav_input = GF_NAV_INPUT_LEFT; + pr_debug("%s nav left\n", __func__); + break; + + case GF_NAV_RIGHT: + nav_input = GF_NAV_INPUT_RIGHT; + pr_debug("%s nav right\n", __func__); + break; + + case GF_NAV_CLICK: + nav_input = GF_NAV_INPUT_CLICK; + pr_debug("%s nav click\n", __func__); + break; + + case GF_NAV_HEAVY: + nav_input = GF_NAV_INPUT_HEAVY; + pr_debug("%s nav heavy\n", __func__); + break; + + case GF_NAV_LONG_PRESS: + nav_input = GF_NAV_INPUT_LONG_PRESS; + pr_debug("%s nav long press\n", __func__); + break; + + case GF_NAV_DOUBLE_CLICK: + nav_input = GF_NAV_INPUT_DOUBLE_CLICK; + pr_debug("%s nav double click\n", __func__); + break; + + default: + pr_warn("%s unknown nav event: %d\n", __func__, nav_event); + break; + } + + if ((nav_event != GF_NAV_FINGER_DOWN) && + (nav_event != GF_NAV_FINGER_UP)) { + input_report_key(gf_dev->input, nav_input, 1); + input_sync(gf_dev->input); + input_report_key(gf_dev->input, nav_input, 0); + input_sync(gf_dev->input); + } +} + + +static void gf_kernel_key_input(struct gf_dev *gf_dev, struct gf_key *gf_key) +{ + uint32_t key_input = 0; + + if (gf_key->key == GF_KEY_HOME) { + key_input = GF_KEY_INPUT_HOME; + } else if (gf_key->key == GF_KEY_POWER) { + key_input = GF_KEY_INPUT_POWER; + } else if (gf_key->key == GF_KEY_CAMERA) { + key_input = GF_KEY_INPUT_CAMERA; + } else { + /* add special key define */ + key_input = gf_key->key; + } + pr_info("%s: received key event[%d], key=%d, value=%d\n", + __func__, key_input, gf_key->key, gf_key->value); + + if ((GF_KEY_POWER == gf_key->key || GF_KEY_CAMERA == gf_key->key) + && (gf_key->value == 1)) { + input_report_key(gf_dev->input, key_input, 1); + input_sync(gf_dev->input); + input_report_key(gf_dev->input, key_input, 0); + input_sync(gf_dev->input); + } + + if (gf_key->key == GF_KEY_HOME) { + input_report_key(gf_dev->input, key_input, gf_key->value); + input_sync(gf_dev->input); + } +} + +static long gf_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct gf_dev *gf_dev = &gf; + struct gf_key gf_key; +#if defined(SUPPORT_NAV_EVENT) + gf_nav_event_t nav_event = GF_NAV_NONE; +#endif + int retval = 0; + u8 netlink_route = NETLINK_TEST; + u8 dev_enable = 0; + struct gf_ioc_chip_info info; + + if (_IOC_TYPE(cmd) != GF_IOC_MAGIC) + return -ENODEV; + + if (_IOC_DIR(cmd) & _IOC_READ){ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + retval = !access_ok((void __user *)arg, _IOC_SIZE(cmd)); +#else + retval = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); +#endif + } + else if (_IOC_DIR(cmd) & _IOC_WRITE){ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + retval = !access_ok((void __user *)arg, _IOC_SIZE(cmd)); +#else + retval = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); +#endif + } + if (retval) + return -EFAULT; + + switch (cmd) { + case GF_IOC_INIT: + pr_debug("%s GF_IOC_INIT\n", __func__); + if (copy_to_user((void __user *)arg, (void *)&netlink_route, sizeof(u8))) { + retval = -EFAULT; + break; + } + break; + + case GF_IOC_EXIT: + pr_debug("%s GF_IOC_EXIT\n", __func__); + break; + + case GF_IOC_DISABLE_IRQ: + pr_debug("%s GF_IOC_DISABEL_IRQ\n", __func__); + gf_disable_irq(gf_dev); + break; + + case GF_IOC_ENABLE_IRQ: + pr_debug("%s GF_IOC_ENABLE_IRQ\n", __func__); + gf_enable_irq(gf_dev); + break; + + case GF_IOC_RESET: + pr_debug("%s GF_IOC_RESET\n", __func__); + gf_hw_reset(gf_dev, 3); + break; + + case GF_IOC_INPUT_KEY_EVENT: + if (copy_from_user(&gf_key, (void __user *)arg, sizeof(struct gf_key))) { + pr_err("failed to copy input key event from user to kernel\n"); + retval = -EFAULT; + break; + } + + gf_kernel_key_input(gf_dev, &gf_key); + break; + +#if defined(SUPPORT_NAV_EVENT) + case GF_IOC_NAV_EVENT: + pr_debug("%s GF_IOC_NAV_EVENT\n", __func__); + if (copy_from_user(&nav_event, (void __user *)arg, sizeof(gf_nav_event_t))) { + pr_err("failed to copy nav event from user to kernel\n"); + retval = -EFAULT; + break; + } + + nav_event_input(gf_dev, nav_event); + break; +#endif + + case GF_IOC_ENABLE_SPI_CLK: + pr_debug("%s GF_IOC_ENABLE_SPI_CLK\n", __func__); +#ifdef AP_CONTROL_CLK + gfspi_ioctl_clk_enable(gf_dev); +#else + pr_debug("Doesn't support control clock.\n"); +#endif + break; + + case GF_IOC_DISABLE_SPI_CLK: + pr_debug("%s GF_IOC_DISABLE_SPI_CLK\n", __func__); +#ifdef AP_CONTROL_CLK + gfspi_ioctl_clk_disable(gf_dev); +#else + pr_debug("Doesn't support control clock\n"); +#endif + break; + + case GF_IOC_ENABLE_POWER: + pr_debug("%s GF_IOC_ENABLE_POWER\n", __func__); + gf_power_on(gf_dev); + break; + + case GF_IOC_DISABLE_POWER: + pr_debug("%s GF_IOC_DISABLE_POWER\n", __func__); + gf_power_off(gf_dev); + break; + + case GF_IOC_ENTER_SLEEP_MODE: + pr_debug("%s GF_IOC_ENTER_SLEEP_MODE\n", __func__); + break; + + case GF_IOC_GET_FW_INFO: + pr_debug("%s GF_IOC_GET_FW_INFO\n", __func__); + break; + + case GF_IOC_REMOVE: + pr_debug("%s GF_IOC_REMOVE\n", __func__); + break; + + case GF_IOC_CHIP_INFO: + pr_debug("%s GF_IOC_CHIP_INFO\n", __func__); + if (copy_from_user(&info, (void __user *)arg, sizeof(struct gf_ioc_chip_info))) { + retval = -EFAULT; + break; + } + pr_info("vendor_id : 0x%x\n", info.vendor_id); + pr_info("mode : 0x%x\n", info.mode); + pr_info("operation: 0x%x\n", info.operation); + break; + case GF_IOC_ENABLE_DEV: + pr_debug("%s GF_IOC_ENABLE_DEV\n", __func__); + if (copy_from_user(&dev_enable, (void __user *)arg, sizeof(uint8_t))) { + pr_err("failed to copy dev enable status from user to kernel\n"); + retval = -EFAULT; + break; + } +#ifdef MMI_RELAY_MODULE + FPS_notify(0xbeef, (dev_enable == 0)? 0: 1); +#endif + pr_debug("%s device enable status %d\n", __func__, dev_enable); + break; + + default: + pr_warn("unsupport cmd:0x%x\n", cmd); + break; + } + + return retval; +} + +#ifdef CONFIG_COMPAT +static long gf_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + return gf_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif /*CONFIG_COMPAT*/ + +static irqreturn_t gf_irq(int irq, void *handle) +{ +#if defined(GF_NETLINK_ENABLE) + char msg = GF_NET_EVENT_IRQ; + + struct gf_dev *gf_dev = handle; + struct device *dev = &gf_dev->spi->dev; + pm_wakeup_event(dev, WAKELOCK_HOLD_TIME); + + sendnlmsg(&msg); +#elif defined(GF_FASYNC) + struct gf_dev *gf_dev = &gf; + + if (gf_dev->async) + kill_fasync(&gf_dev->async, SIGIO, POLL_IN); +#endif + + return IRQ_HANDLED; +} + +static int gf_open(struct inode *inode, struct file *filp) +{ + struct gf_dev *gf_dev; + int status = -ENXIO; + + mutex_lock(&device_list_lock); + + list_for_each_entry(gf_dev, &device_list, device_entry) { + if (gf_dev->devt == inode->i_rdev) { + pr_info("Found\n"); + status = 0; + break; + } + } + + if (status == 0) { + gf_dev->users++; + filp->private_data = gf_dev; + nonseekable_open(inode, filp); + pr_info("Succeed to open device. irq = %d\n", + gf_dev->irq); + if (gf_dev->users == 1) + gf_enable_irq(gf_dev); + gf_hw_reset(gf_dev, 3); + } else { + pr_info("No device for minor %d\n", iminor(inode)); + } + mutex_unlock(&device_list_lock); + return status; +} + +#ifdef GF_FASYNC +static int gf_fasync(int fd, struct file *filp, int mode) +{ + struct gf_dev *gf_dev = filp->private_data; + int ret; + + ret = fasync_helper(fd, filp, mode, &gf_dev->async); + pr_info("ret = %d\n", ret); + return ret; +} +#endif + +static int gf_release(struct inode *inode, struct file *filp) +{ + struct gf_dev *gf_dev; + int status = 0; + + mutex_lock(&device_list_lock); + gf_dev = filp->private_data; + filp->private_data = NULL; + + /*last close?? */ + gf_dev->users--; + if (!gf_dev->users) { + pr_info("disable_irq. irq = %d\n", gf_dev->irq); + gf_disable_irq(gf_dev); + /*power off the sensor*/ + gf_power_off(gf_dev); + } + mutex_unlock(&device_list_lock); + return status; +} + +static const struct file_operations gf_fops = { + .owner = THIS_MODULE, + /* REVISIT switch to aio primitives, so that userspace + * gets more complete API coverage. It'll simplify things + * too, except for the locking. + */ + .unlocked_ioctl = gf_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = gf_compat_ioctl, +#endif /*CONFIG_COMPAT*/ + .open = gf_open, + .release = gf_release, +#ifdef GF_FASYNC + .fasync = gf_fasync, +#endif +}; + +#if defined(CONFIG_GOODIX_DRM_PANEL_NOTIFICATIONS) +static int drm_check_dt(struct gf_dev *gf_dev) +{ + int i = 0; + int count = 0; + struct device *dev = &gf_dev->spi->dev; + struct device_node *np = dev->of_node; + struct device_node *node = NULL; + struct drm_panel *panel = NULL; + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) { + pr_err(" %s : find drm_panel fail count = %d ", __func__, count); + return -ENODEV; + } + 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)) { + pr_info(" %s : find drm_panel successfully ", __func__); + gf_dev->active_panel = panel; + return 0; + } + } + pr_err(" %s : can not find drm_panel ", __func__); + return -ENODEV; +} +#endif + +static int goodix_fb_state_chg_callback(struct notifier_block *nb, + unsigned long val, void *data) +{ +#if defined(CONFIG_GOODIX_DRM_PANEL_NOTIFICATIONS) + struct gf_dev *gf_dev; + struct drm_panel_notifier *evdata = data; + int blank; + char msg = 0; + + if (val != DRM_PANEL_EARLY_EVENT_BLANK) + return 0; + pr_debug("[info] %s go to the goodix_fb_state_chg_callback value = %d\n", + __func__, (int)val); + gf_dev = container_of(nb, struct gf_dev, notifier); + if (evdata && evdata->data && val == DRM_PANEL_EARLY_EVENT_BLANK && gf_dev) { + blank = *(int *)(evdata->data); + switch (blank) { + case DRM_PANEL_BLANK_POWERDOWN: + gf_dev->fb_black = 1; + pr_debug("%s DRM_PANEL_BLANK_POWERDOWN\n", __func__); +#if defined(GF_NETLINK_ENABLE) + msg = GF_NET_EVENT_FB_BLACK; + sendnlmsg(&msg); +#elif defined(GF_FASYNC) + if (gf_dev->async) + kill_fasync(&gf_dev->async, SIGIO, POLL_IN); +#endif + break; + + case DRM_PANEL_BLANK_UNBLANK: + pr_debug("%s DRM_PANEL_BLANK_UNBLANK\n", __func__); + gf_dev->fb_black = 0; +#if defined(GF_NETLINK_ENABLE) + msg = GF_NET_EVENT_FB_UNBLACK; + sendnlmsg(&msg); +#elif defined(GF_FASYNC) + if (gf_dev->async) + kill_fasync(&gf_dev->async, SIGIO, POLL_IN); +#endif + break; + + default: + pr_debug("%s defalut\n", __func__); + break; + } + } +#endif + return NOTIFY_OK; +} + +static struct notifier_block goodix_noti_block = { + .notifier_call = goodix_fb_state_chg_callback, +}; + +static struct class *gf_class; +#if defined(USE_SPI_BUS) +static int gf_probe(struct spi_device *spi) +#elif defined(USE_PLATFORM_BUS) +static int gf_probe(struct platform_device *pdev) +#endif +{ + struct gf_dev *gf_dev = &gf; + int status = -EINVAL; + unsigned long minor; + struct device *dev = NULL; + int i; + + /* Initialize the driver data */ + INIT_LIST_HEAD(&gf_dev->device_entry); +#if defined(USE_SPI_BUS) + gf_dev->spi = spi; + dev = &spi->dev; +#elif defined(USE_PLATFORM_BUS) + gf_dev->spi = pdev; + dev = &pdev->dev; +#endif + gf_dev->irq_gpio = -EINVAL; + gf_dev->reset_gpio = -EINVAL; + gf_dev->pwr_gpio = -EINVAL; + + gf_dev->fb_black = 0; + + if (gf_parse_dts(gf_dev)) + goto error_hw; + + /* If we can allocate a minor number, hook up this device. + * Reusing minors is fine so long as udev or mdev is working. + */ + mutex_lock(&device_list_lock); + minor = find_first_zero_bit(minors, N_SPI_MINORS); + if (minor < N_SPI_MINORS) { + struct device *dev; + + gf_dev->devt = MKDEV(SPIDEV_MAJOR, minor); + dev = device_create(gf_class, &gf_dev->spi->dev, gf_dev->devt, + gf_dev, GF_DEV_NAME); + status = IS_ERR(dev) ? PTR_ERR(dev) : 0; + } else { + dev_dbg(&gf_dev->spi->dev, "no minor number available!\n"); + status = -ENODEV; + mutex_unlock(&device_list_lock); + goto error_hw; + } + + if (status == 0) { + set_bit(minor, minors); + list_add(&gf_dev->device_entry, &device_list); + } else { + gf_dev->devt = 0; + } + mutex_unlock(&device_list_lock); + + if (status == 0) { + /*input device subsystem */ + gf_dev->input = input_allocate_device(); + if (gf_dev->input == NULL) { + pr_err("%s, failed to allocate input device\n", __func__); + status = -ENOMEM; + goto error_dev; + } + for (i = 0; i < ARRAY_SIZE(maps); i++) + input_set_capability(gf_dev->input, maps[i].type, maps[i].code); + + gf_dev->input->name = GF_INPUT_NAME; + status = input_register_device(gf_dev->input); + if (status) { + pr_err("failed to register input device\n"); + goto error_input; + } + } +#ifdef AP_CONTROL_CLK + pr_info("Get the clk resource.\n"); + /* Enable spi clock */ + if (gfspi_ioctl_clk_init(gf_dev)) + goto gfspi_probe_clk_init_failed; + + if (gfspi_ioctl_clk_enable(gf_dev)) + goto gfspi_probe_clk_enable_failed; + + spi_clock_set(gf_dev, 1000000); +#endif + + gf_dev->notifier = goodix_noti_block; +#if defined(CONFIG_GOODIX_DRM_PANEL_NOTIFICATIONS) + gf_dev->active_panel = NULL; + status = drm_check_dt(gf_dev); + if (status >= 0) { + status = drm_panel_notifier_register(gf_dev->active_panel, &gf_dev->notifier); + pr_info("gf_probe drm_panel_notifier_register: status = %d\n", status); + } else { + pr_info("gf_probe drm_panel is NULL: = %d\n", status); + } +#else + fb_register_client(&gf_dev->notifier); +#endif + gf_dev->irq = gf_irq_num(gf_dev); + + status = request_threaded_irq(gf_dev->irq, NULL, gf_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "gf", gf_dev); + + if (status) { + pr_err("failed to request IRQ:%d\n", gf_dev->irq); + goto err_irq; + } + enable_irq_wake(gf_dev->irq); + gf_dev->irq_enabled = 1; + gf_disable_irq(gf_dev); + device_init_wakeup(dev, true); +#ifdef MMI_RELAY_MODULE + fpsData = FPS_init(dev); +#endif + status = sysfs_create_group(&dev->kobj, &attribute_group); + if (status) { + pr_err("failed to create sysfs group. %d\n", status); + goto err_sysfs; + } + + pr_info("version V%d.%d.%02d.%02d\n", VER_MAJOR, + VER_MINOR, PATCH_LEVEL, EXTEND_VER); + pr_info("gf_probe: status = %d\n", status); + + return status; +err_sysfs: + device_init_wakeup(dev, false); + if (gf_dev->irq) + free_irq(gf_dev->irq, gf_dev); +err_irq: + input_unregister_device(gf_dev->input); +#ifdef AP_CONTROL_CLK +gfspi_probe_clk_enable_failed: + gfspi_ioctl_clk_uninit(gf_dev); +gfspi_probe_clk_init_failed: +#endif + +error_input: + if (gf_dev->input != NULL) + input_free_device(gf_dev->input); +error_dev: + if (gf_dev->devt != 0) { + pr_info("Err: status = %d\n", status); + mutex_lock(&device_list_lock); + list_del(&gf_dev->device_entry); + device_destroy(gf_class, gf_dev->devt); + clear_bit(MINOR(gf_dev->devt), minors); + mutex_unlock(&device_list_lock); + } +error_hw: + gf_cleanup(gf_dev); + + + return status; +} + +#if defined(USE_SPI_BUS) +static int gf_remove(struct spi_device *spi) +#elif defined(USE_PLATFORM_BUS) +static int gf_remove(struct platform_device *pdev) +#endif +{ + struct gf_dev *gf_dev = &gf; + struct device *dev = &gf_dev->spi->dev; + + sysfs_remove_group(&dev->kobj, &attribute_group); + device_init_wakeup(dev, false); + /* make sure ops on existing fds can abort cleanly */ + if (gf_dev->irq) + free_irq(gf_dev->irq, gf_dev); + + if (gf_dev->input != NULL) + input_unregister_device(gf_dev->input); + + /* prevent new opens */ + mutex_lock(&device_list_lock); + list_del(&gf_dev->device_entry); + device_destroy(gf_class, gf_dev->devt); + clear_bit(MINOR(gf_dev->devt), minors); + if (gf_dev->users == 0) + gf_cleanup(gf_dev); +#if defined(CONFIG_GOODIX_DRM_PANEL_NOTIFICATIONS) + if (gf_dev->active_panel) + { + drm_panel_notifier_unregister(gf_dev->active_panel, &gf_dev->notifier); + gf_dev->active_panel = NULL; + } +#else + fb_unregister_client(&gf_dev->notifier); +#endif + mutex_unlock(&device_list_lock); + + return 0; +} + +static const struct of_device_id gx_match_table[] = { + { .compatible = GF_SPIDEV_NAME }, + {}, +}; + +#if defined(USE_SPI_BUS) +static struct spi_driver gf_driver = { +#elif defined(USE_PLATFORM_BUS) +static struct platform_driver gf_driver = { +#endif + .driver = { + .name = GF_DEV_NAME, + .owner = THIS_MODULE, + .of_match_table = gx_match_table, + }, + .probe = gf_probe, + .remove = gf_remove, +}; + +static int __init gf_init(void) +{ + int status; + + /* Claim our 256 reserved device numbers. Then register a class + * that will key udev/mdev to add/remove /dev nodes. Last, register + * the driver which manages those device numbers. + */ + + BUILD_BUG_ON(N_SPI_MINORS > 256); + status = register_chrdev(SPIDEV_MAJOR, CHRD_DRIVER_NAME, &gf_fops); + if (status < 0) { + pr_warn("Failed to register char device!\n"); + return status; + } + SPIDEV_MAJOR = status; + gf_class = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(gf_class)) { + unregister_chrdev(SPIDEV_MAJOR, gf_driver.driver.name); + pr_warn("Failed to create class.\n"); + return PTR_ERR(gf_class); + } +#if defined(USE_PLATFORM_BUS) + status = platform_driver_register(&gf_driver); +#elif defined(USE_SPI_BUS) + status = spi_register_driver(&gf_driver); +#endif + if (status < 0) { + class_destroy(gf_class); + unregister_chrdev(SPIDEV_MAJOR, gf_driver.driver.name); + pr_warn("Failed to register SPI driver.\n"); + } + +#ifdef GF_NETLINK_ENABLE + netlink_init(); +#endif + pr_info("status = 0x%x\n", status); + return 0; +} +module_init(gf_init); + +static void __exit gf_exit(void) +{ +#ifdef GF_NETLINK_ENABLE + netlink_exit(); +#endif +#if defined(USE_PLATFORM_BUS) + platform_driver_unregister(&gf_driver); +#elif defined(USE_SPI_BUS) + spi_unregister_driver(&gf_driver); +#endif + class_destroy(gf_class); + unregister_chrdev(SPIDEV_MAJOR, gf_driver.driver.name); +} +module_exit(gf_exit); + +MODULE_AUTHOR("Jiangtao Yi, "); +MODULE_AUTHOR("Jandy Gou, "); +MODULE_DESCRIPTION("goodix fingerprint sensor device driver"); +MODULE_LICENSE("GPL"); +#ifdef SOFTDEP_GPIO_PCAL6408 +MODULE_SOFTDEP("pre: gpio-pcal6408"); +#endif diff --git a/drivers/input/misc/goodix_fod_mmi/gf_spi.h b/drivers/input/misc/goodix_fod_mmi/gf_spi.h new file mode 100644 index 000000000000..cf0985b992cd --- /dev/null +++ b/drivers/input/misc/goodix_fod_mmi/gf_spi.h @@ -0,0 +1,192 @@ +/* + * driver definition for sensor driver + * Copyright (C) 2016 Goodix + * + * 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 __GF_SPI_H +#define __GF_SPI_H + +#include +#include +/**********************************************************/ +enum FP_MODE { + GF_IMAGE_MODE = 0, + GF_KEY_MODE, + GF_SLEEP_MODE, + GF_FF_MODE, + GF_DEBUG_MODE = 0x56 +}; + +#define SUPPORT_NAV_EVENT + +#if defined(SUPPORT_NAV_EVENT) +#define GF_NAV_INPUT_UP KEY_UP +#define GF_NAV_INPUT_DOWN KEY_DOWN +#define GF_NAV_INPUT_LEFT KEY_LEFT +#define GF_NAV_INPUT_RIGHT KEY_RIGHT +#define GF_NAV_INPUT_CLICK KEY_VOLUMEDOWN +#define GF_NAV_INPUT_DOUBLE_CLICK KEY_VOLUMEUP +#define GF_NAV_INPUT_LONG_PRESS KEY_SEARCH +#define GF_NAV_INPUT_HEAVY KEY_CHAT +#endif + +#define GF_KEY_INPUT_HOME KEY_HOME +#define GF_KEY_INPUT_MENU KEY_MENU +#define GF_KEY_INPUT_BACK KEY_BACK +#define GF_KEY_INPUT_POWER KEY_POWER +#define GF_KEY_INPUT_CAMERA KEY_CAMERA + +#if defined(SUPPORT_NAV_EVENT) +typedef enum gf_nav_event { + GF_NAV_NONE = 0, + GF_NAV_FINGER_UP, + GF_NAV_FINGER_DOWN, + GF_NAV_UP, + GF_NAV_DOWN, + GF_NAV_LEFT, + GF_NAV_RIGHT, + GF_NAV_CLICK, + GF_NAV_HEAVY, + GF_NAV_LONG_PRESS, + GF_NAV_DOUBLE_CLICK, +} gf_nav_event_t; +#endif + +typedef enum gf_key_event { + GF_KEY_NONE = 0, + GF_KEY_HOME, + GF_KEY_POWER, + GF_KEY_MENU, + GF_KEY_BACK, + GF_KEY_CAMERA, +} gf_key_event_t; + +struct gf_key { + enum gf_key_event key; + uint32_t value; /* key down = 1, key up = 0 */ +}; + +struct gf_key_map { + unsigned int type; + unsigned int code; +}; + +struct gf_ioc_chip_info { + unsigned char vendor_id; + unsigned char mode; + unsigned char operation; + unsigned char reserved[5]; +}; + +enum GF_IOC_COUNT { + GF_IOC_INIT_COUNT = 0, + GF_IOC_EXIT_COUNT, // 1 + GF_IOC_RESET_COUNT, // 2 + GF_IOC_ENABLE_IRQ_COUNT, // 3 + GF_IOC_DISABLE_IRQ_COUNT, // 4 + GF_IOC_ENABLE_SPI_CLK_COUNT, // 5 + GF_IOC_DISABLE_SPI_CLK_COUNT, // 6 + GF_IOC_ENABLE_POWER_COUNT, // 7 + GF_IOC_DISABLE_POWER_COUNT, // 8 + GF_IOC_INPUT_KEY_EVENT_COUNT, // 9 + GF_IOC_ENTER_SLEEP_MODE_COUNT, // 10 + GF_IOC_GET_FW_INFO_COUNT, // 11 + GF_IOC_REMOVE_COUNT, // 12 + GF_IOC_CHIP_INFO_COUNT, // 13 +#if defined(SUPPORT_NAV_EVENT) + GF_IOC_NAV_EVENT_COUNT, // 14 +#else + GF_IOC_14, // 14 +#endif + GF_IOC_ENABLE_DEV_COUNT, // 15 + GF_IOC_MAXNR, +}; + +#define GF_IOC_MAGIC 'g' +#define GF_IOC_INIT _IOR(GF_IOC_MAGIC, GF_IOC_INIT_COUNT, uint8_t) +#define GF_IOC_EXIT _IO(GF_IOC_MAGIC, GF_IOC_EXIT_COUNT) +#define GF_IOC_RESET _IO(GF_IOC_MAGIC, GF_IOC_RESET_COUNT) +#define GF_IOC_ENABLE_IRQ _IO(GF_IOC_MAGIC, GF_IOC_ENABLE_IRQ_COUNT) +#define GF_IOC_DISABLE_IRQ _IO(GF_IOC_MAGIC, GF_IOC_DISABLE_IRQ_COUNT) +#define GF_IOC_ENABLE_SPI_CLK _IOW(GF_IOC_MAGIC, GF_IOC_ENABLE_SPI_CLK_COUNT, uint32_t) +#define GF_IOC_DISABLE_SPI_CLK _IO(GF_IOC_MAGIC, GF_IOC_DISABLE_SPI_CLK_COUNT) +#define GF_IOC_ENABLE_POWER _IO(GF_IOC_MAGIC, GF_IOC_ENABLE_POWER_COUNT) +#define GF_IOC_DISABLE_POWER _IO(GF_IOC_MAGIC, GF_IOC_DISABLE_POWER_COUNT) +#define GF_IOC_INPUT_KEY_EVENT _IOW(GF_IOC_MAGIC, GF_IOC_INPUT_KEY_EVENT_COUNT, struct gf_key) +#define GF_IOC_ENTER_SLEEP_MODE _IO(GF_IOC_MAGIC, GF_IOC_ENTER_SLEEP_MODE_COUNT) +#define GF_IOC_GET_FW_INFO _IOR(GF_IOC_MAGIC, GF_IOC_GET_FW_INFO_COUNT, uint8_t) +#define GF_IOC_REMOVE _IO(GF_IOC_MAGIC, GF_IOC_REMOVE_COUNT) +#define GF_IOC_CHIP_INFO _IOW(GF_IOC_MAGIC, GF_IOC_CHIP_INFO_COUNT, struct gf_ioc_chip_info) +#if defined(SUPPORT_NAV_EVENT) +#define GF_IOC_NAV_EVENT _IOW(GF_IOC_MAGIC, GF_IOC_NAV_EVENT_COUNT, gf_nav_event_t) +#endif +#define GF_IOC_ENABLE_DEV _IOW(GF_IOC_MAGIC, GF_IOC_ENABLE_DEV_COUNT, uint8_t) + +/*#define AP_CONTROL_CLK 1*/ +#define USE_PLATFORM_BUS 1 +/*#define USE_SPI_BUS 1*/ +/* #define GF_FASYNC 1*/ /*If support fasync mechanism.*/ +#define GF_NETLINK_ENABLE 1 +#define GF_NET_EVENT_IRQ 1 +#define GF_NET_EVENT_FB_BLACK 2 +#define GF_NET_EVENT_FB_UNBLACK 3 +#define NETLINK_TEST 25 + +struct gf_dev { + dev_t devt; + struct list_head device_entry; +#if defined(USE_SPI_BUS) + struct spi_device *spi; +#elif defined(USE_PLATFORM_BUS) + struct platform_device *spi; +#endif + struct clk *core_clk; + struct clk *iface_clk; + + struct input_dev *input; + /* buffer is NULL unless this device is open (users > 0) */ + unsigned int users; + signed int irq_gpio; + signed int reset_gpio; + signed int pwr_gpio; + unsigned int rgltr_ctrl_support; //whether regulator control is supported + struct regulator *pwr_supply; + int pwr_voltage_range[2]; + int pwr_load[1]; + int irq; + int irq_enabled; + int clk_enabled; + int power_enabled; +#ifdef GF_FASYNC + struct fasync_struct *async; +#endif + struct notifier_block notifier; + char fb_black; +#if defined(CONFIG_GOODIX_DRM_PANEL_NOTIFICATIONS) + struct drm_panel *active_panel; +#endif +}; + +int gf_parse_dts(struct gf_dev *gf_dev); +void gf_cleanup(struct gf_dev *gf_dev); + +int gf_power_on(struct gf_dev *gf_dev); +int gf_power_off(struct gf_dev *gf_dev); + +int gf_hw_reset(struct gf_dev *gf_dev, unsigned int delay_ms); +int gf_irq_num(struct gf_dev *gf_dev); + +int sendnlmsg(char *msg); +int netlink_init(void); +void netlink_exit(void); +#endif /*__GF_SPI_H*/ diff --git a/drivers/input/misc/goodix_fod_mmi/netlink.c b/drivers/input/misc/goodix_fod_mmi/netlink.c new file mode 100644 index 000000000000..6ad42c1e7d23 --- /dev/null +++ b/drivers/input/misc/goodix_fod_mmi/netlink.c @@ -0,0 +1,112 @@ +/* + * netlink interface + * Copyright (C) 2016 Goodix + * + * 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 +#include +#include +#include +#include +#include +#include +#include "gf_spi.h" + +#define NETLINK_TEST 25 +#define MAX_MSGSIZE 32 + +static int pid = -1; +static struct sock *nl_sk; + +int sendnlmsg(char *msg) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + int len = NLMSG_SPACE(MAX_MSGSIZE); + int ret = 0; + + if (!msg || !nl_sk || !pid) + return -ENODEV; + + skb = alloc_skb(len, GFP_ATOMIC); + if (!skb) + return -ENOMEM; + + nlh = nlmsg_put(skb, 0, 0, 0, MAX_MSGSIZE, 0); + if (!nlh) { + kfree_skb(skb); + return -EMSGSIZE; + } + + NETLINK_CB(skb).portid = 0; + NETLINK_CB(skb).dst_group = 0; + + memcpy(NLMSG_DATA(nlh), msg, sizeof(char)); + pr_debug("send message: %d\n", *(char *)NLMSG_DATA(nlh)); + + ret = netlink_unicast(nl_sk, skb, pid, MSG_DONTWAIT); + if (ret > 0) + ret = 0; + + return ret; +} + +static void nl_data_ready(struct sk_buff *__skb) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + char str[100]; + + skb = skb_get(__skb); + if (skb->len >= NLMSG_SPACE(0)) { + nlh = nlmsg_hdr(skb); + + memcpy(str, NLMSG_DATA(nlh), sizeof(str)); + pid = nlh->nlmsg_pid; + } + + kfree_skb(skb); +} + + +int netlink_init(void) +{ + struct netlink_kernel_cfg netlink_cfg; + + memset(&netlink_cfg, 0, sizeof(struct netlink_kernel_cfg)); + + netlink_cfg.groups = 0; + netlink_cfg.flags = 0; + netlink_cfg.input = nl_data_ready; + netlink_cfg.cb_mutex = NULL; + + nl_sk = netlink_kernel_create(&init_net, NETLINK_TEST, + &netlink_cfg); + + if (!nl_sk) { + pr_err("create netlink socket error\n"); + return 1; + } + + return 0; +} + +void netlink_exit(void) +{ + if (nl_sk != NULL) { + netlink_kernel_release(nl_sk); + nl_sk = NULL; + } + + pr_info("self module exited\n"); +} diff --git a/drivers/input/misc/goodix_fod_mmi/platform.c b/drivers/input/misc/goodix_fod_mmi/platform.c new file mode 100644 index 000000000000..5e1d6b4830d4 --- /dev/null +++ b/drivers/input/misc/goodix_fod_mmi/platform.c @@ -0,0 +1,224 @@ +/* + * platform indepent driver interface + * Copyright (C) 2016 Goodix + * + * 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 +#include +#include +#include +#include +#include +#include + +#include "gf_spi.h" + +#if defined(USE_SPI_BUS) +#include +#include +#elif defined(USE_PLATFORM_BUS) +#include +#endif + +int gf_parse_dts(struct gf_dev *gf_dev) +{ + int rc = 0; + struct device *dev = &gf_dev->spi->dev; + struct device_node *np = dev->of_node; + gf_dev->power_enabled = 0; + gf_dev->pwr_supply = NULL; + if(of_property_read_bool(np,"rgltr-ctrl-support")) { + gf_dev->rgltr_ctrl_support = 1; + } else { + gf_dev->rgltr_ctrl_support = 0; + pr_err("goodix: No regulator control parameter defined\n"); + } + if (gf_dev->rgltr_ctrl_support) { + gf_dev->pwr_supply = regulator_get(dev, "fp,vdd"); + if (IS_ERR_OR_NULL(gf_dev->pwr_supply)) { + gf_dev->pwr_supply = NULL; + gf_dev->rgltr_ctrl_support = 0; + pr_warn("goodix Unable to get fp,vdd"); + } else { + rc = of_property_read_u32_array(np, "fp,voltage-range", gf_dev->pwr_voltage_range, 2); + if (rc) { + gf_dev->pwr_voltage_range[0] = -1; + gf_dev->pwr_voltage_range[1] = -1; + } + if (regulator_count_voltages(gf_dev->pwr_supply) > 0) { + if((gf_dev->pwr_voltage_range[0] >0) && (gf_dev->pwr_voltage_range[1] > 0)) + rc = regulator_set_voltage(gf_dev->pwr_supply, gf_dev->pwr_voltage_range[0], gf_dev->pwr_voltage_range[1]); + if (rc) { + pr_warn("goodix: %s : set vdd regulator voltage failed %d \n", __func__, rc); + } + } + } + } + + gf_dev->pwr_gpio = of_get_named_gpio(np, "fp-gpio-ven", 0); + if (gf_dev->pwr_gpio < 0) { + pr_warn("goodix: failed to get pwr gpio!\n"); + } else { + if (gpio_is_valid(gf_dev->pwr_gpio)) { + rc = devm_gpio_request(dev, gf_dev->pwr_gpio, "goodix_pwr"); + if (rc) { + pr_err("goodix: failed to request pwr gpio, rc = %d\n", rc); + goto err_pwr; + } + gpio_direction_output(gf_dev->pwr_gpio, 1); + } + } + gf_power_on(gf_dev); + gf_dev->reset_gpio = of_get_named_gpio(np, "fp-gpio-reset", 0); + if (gf_dev->reset_gpio < 0) { + pr_err("goodix: failed to get reset gpio!\n"); + return gf_dev->reset_gpio; + } + + rc = devm_gpio_request(dev, gf_dev->reset_gpio, "goodix_reset"); + if (rc) { + pr_err("goodix: failed to request reset gpio, rc = %d\n", rc); + goto err_reset; + } + gpio_direction_output(gf_dev->reset_gpio, 1); + + gf_dev->irq_gpio = of_get_named_gpio(np, "fp-gpio-irq", 0); + if (gf_dev->irq_gpio < 0) { + pr_err("goodix: failed to get irq gpio!\n"); + return gf_dev->irq_gpio; + } + + rc = devm_gpio_request(dev, gf_dev->irq_gpio, "goodix_irq"); + if (rc) { + pr_err("goodix: failed to request irq gpio, rc = %d\n", rc); + goto err_irq; + } + gpio_direction_input(gf_dev->irq_gpio); + return rc; +err_irq: + if (gpio_is_valid(gf_dev->reset_gpio)) { + devm_gpio_free(dev, gf_dev->reset_gpio); + gf_dev->reset_gpio = -1; + } +err_reset: + gf_power_off(gf_dev); + if (gpio_is_valid(gf_dev->pwr_gpio)) { + devm_gpio_free(dev, gf_dev->pwr_gpio); + gf_dev->pwr_gpio = -1; + pr_info("goodix: remove pwr_gpio success\n"); + } +err_pwr: + if (!IS_ERR_OR_NULL(gf_dev->pwr_supply)) + { + pr_info(" %s goodix: devm_regulator_put \n", __func__); + regulator_put(gf_dev->pwr_supply); + gf_dev->pwr_supply= NULL; + } + return rc; +} + +void gf_cleanup(struct gf_dev *gf_dev) +{ + struct device *dev = &gf_dev->spi->dev; + pr_info("goodix: %s\n", __func__); + + if (gpio_is_valid(gf_dev->irq_gpio)) { + devm_gpio_free(dev, gf_dev->irq_gpio); + gf_dev->irq_gpio = -1; + pr_info("goodix: remove irq_gpio success\n"); + } + if(gf_dev->rgltr_ctrl_support ||gpio_is_valid(gf_dev->pwr_gpio)){ + gf_power_off(gf_dev); + } + if (gpio_is_valid(gf_dev->pwr_gpio)) { + devm_gpio_free(dev, gf_dev->pwr_gpio); + gf_dev->pwr_gpio = -1; + pr_info("goodix: remove pwr_gpio success\n"); + } + if (gpio_is_valid(gf_dev->reset_gpio)) { + devm_gpio_free(dev, gf_dev->reset_gpio); + gf_dev->reset_gpio = -1; + pr_info("goodix: remove reset_gpio success\n"); + } + + if (gf_dev->rgltr_ctrl_support && !IS_ERR_OR_NULL(gf_dev->pwr_supply)) + { + pr_info(" goodix: %s : devm_regulator_put \n", __func__); + regulator_put(gf_dev->pwr_supply); + gf_dev->pwr_supply= NULL; + } +} + +int gf_power_on(struct gf_dev *gf_dev) +{ + int rc = 0; + if (!gf_dev->power_enabled) { + if(gf_dev->rgltr_ctrl_support && !IS_ERR_OR_NULL(gf_dev->pwr_supply)) { + rc = regulator_enable(gf_dev->pwr_supply); + pr_warn("goodix: %s : enable pwr_supply return %d \n", __func__, rc); + } + if (gpio_is_valid(gf_dev->pwr_gpio)) { + gpio_direction_output(gf_dev->pwr_gpio, 1); + pr_warn(" goodix: %s : set pwr_gpio:%d 1\n", __func__, gf_dev->pwr_gpio); + } + gf_dev->power_enabled = 1; + if(gf_dev->rgltr_ctrl_support ||gpio_is_valid(gf_dev->pwr_gpio)){ + usleep_range(11000,12000); + } + } + return rc; +} + +int gf_power_off(struct gf_dev *gf_dev) +{ + int rc = 0; + + if (gf_dev->power_enabled) { + if (gf_dev->rgltr_ctrl_support && !IS_ERR_OR_NULL(gf_dev->pwr_supply)) { + rc = regulator_disable(gf_dev->pwr_supply); + pr_warn(" goodix: %s : disable pwr_supply return %d \n", __func__, rc); + } + if (gpio_is_valid(gf_dev->pwr_gpio)) { + gpio_direction_output(gf_dev->pwr_gpio, 0); + pr_warn(" goodix: %s : set pwr_gpio:%d 0 \n", __func__,gf_dev->pwr_gpio); + } + gf_dev->power_enabled = 0; + } + return rc; +} + +int gf_hw_reset(struct gf_dev *gf_dev, unsigned int delay_ms) +{ + if (!gf_dev) { + pr_err("Input buff is NULL.\n"); + return -ENODEV; + } + pr_warn(" goodix: %s : gf_hw_reset \n", __func__); + gpio_direction_output(gf_dev->reset_gpio, 0); + gpio_set_value(gf_dev->reset_gpio, 0); + mdelay(3); + gpio_set_value(gf_dev->reset_gpio, 1); + mdelay(delay_ms); + return 0; +} + +int gf_irq_num(struct gf_dev *gf_dev) +{ + if (!gf_dev) { + pr_err("Input buff is NULL.\n"); + return -ENODEV; + } else { + return gpio_to_irq(gf_dev->irq_gpio); + } +} + diff --git a/drivers/input/misc/ldc2114_mmi/Android.mk b/drivers/input/misc/ldc2114_mmi/Android.mk new file mode 100644 index 000000000000..92f16292aa88 --- /dev/null +++ b/drivers/input/misc/ldc2114_mmi/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := ldc2114_keys.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/misc/ldc2114_mmi/Kbuild b/drivers/input/misc/ldc2114_mmi/Kbuild new file mode 100644 index 000000000000..db4897bbff02 --- /dev/null +++ b/drivers/input/misc/ldc2114_mmi/Kbuild @@ -0,0 +1,12 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +ldc2114_keys-objs := ldc2114.o ldc2114-debugfs.o + +ifeq ($(TARGET_BUILD_VARIANT),userdebug) + # Following line is in control of extra debug + EXTRA_CFLAGS += -DLDC2114_POLL_DEBUG -DLDC2114_CFG_DEBUG +endif + +obj-m = ldc2114_keys.o diff --git a/drivers/input/misc/ldc2114_mmi/Makefile b/drivers/input/misc/ldc2114_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/misc/ldc2114_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/misc/ldc2114_mmi/ldc2114-debugfs.c b/drivers/input/misc/ldc2114_mmi/ldc2114-debugfs.c new file mode 100644 index 000000000000..0205728612fa --- /dev/null +++ b/drivers/input/misc/ldc2114_mmi/ldc2114-debugfs.c @@ -0,0 +1,331 @@ +/* linux/drivers/input/misc/ldc2114-debugfs.c + * + * LDC2114 debugfs driver. Allows user space access to raw data. + * Copyright (C) 2017 Motorola, Inc. + * + * 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. + */ +#if defined(CONFIG_DEBUG_FS) + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ldc2114.h" + +#define DRVNAME "ldc2114_raw" + +static int buffer_init(struct buffer *buffer, size_t capacity) +{ + int i; + + buffer->data = kzalloc(capacity * sizeof(item_t *), GFP_KERNEL); + if (buffer->data == NULL) + return -ENOMEM; + for (i = 0; i < capacity; i++) { + buffer->data[i] = kzalloc(sizeof(item_t), GFP_KERNEL); + if (buffer->data[i] == NULL) + return -ENOMEM; + } + buffer->capacity = capacity; + buffer->opened = 0UL; + atomic_set(&buffer->start, 0); + atomic_set(&buffer->end, 0); + atomic_set(&buffer->active, 0); + mutex_init(&buffer->mutex); + init_waitqueue_head(&buffer->wait); + + return 0; +} + +static void inline buffer_destroy(struct buffer *buffer) +{ + int i; + + mutex_lock(&buffer->mutex); + buffer->capacity = 0; + atomic_set(&buffer->start, 0); + atomic_set(&buffer->end, 0); + atomic_set(&buffer->active, 0); + /* wait for remaining msgs */ + wait_event_interruptible(buffer->wait, + !atomic_read(&buffer->active)); + for (i = 0; i < buffer->capacity; i++) + kfree(buffer->data[i]); + mutex_unlock(&buffer->mutex); + kfree(buffer->data); +} + +static void buffer_push(struct buffer *buffer, + void *data, size_t length) +{ + struct ldc2114_drv *context = + container_of(buffer, struct ldc2114_drv, ds); + int cactive, cstart, cend; + + mutex_lock(&buffer->mutex); + + cactive = atomic_read(&buffer->active); + cstart = atomic_read(&buffer->start); + cend = atomic_read(&buffer->end); + + dev_dbg(context->dev, "%s: writing at pos %d\n", __func__, cend); + memcpy(&buffer->data[cend], data, length); + + cend = (cend + 1) % buffer->capacity; + atomic_set(&buffer->end, cend); + + if (cactive < buffer->capacity) { + cactive++; + atomic_set(&buffer->active, cactive); + } else { + cstart = (cstart + 1) % buffer->capacity; + atomic_set(&buffer->start, cstart); + } + + dev_dbg(context->dev, "%s: next write=%d, read=%d\n", + __func__, cend, cstart); + + mutex_unlock(&buffer->mutex); + + wake_up_interruptible(&buffer->wait); +} + +static size_t buffer_pop(struct buffer *buffer, item_t *item) +{ + struct ldc2114_drv *context = + container_of(buffer, struct ldc2114_drv, ds); + int cactive, cstart; + size_t count = 0; + + if (wait_event_interruptible(buffer->wait, + atomic_read(&buffer->active))) + return -ERESTARTSYS; + + mutex_lock(&buffer->mutex); + + cactive = atomic_read(&buffer->active); + cstart = atomic_read(&buffer->start); + + memcpy(item, &buffer->data[cstart], sizeof(item_t)); + dev_dbg(context->dev, "%s: reading from pos %d\n", + __func__, cstart); + + count += sizeof(item_t); + + cactive--; + cstart = (cstart + 1) % buffer->capacity; + dev_dbg(context->dev, "%s: next read pos %d\n", + __func__, cstart); + + atomic_set(&buffer->start, cstart); + atomic_set(&buffer->active, cactive); + + mutex_unlock(&buffer->mutex); + + return count; +} + +/* + * External function called by polling work + */ +int ldc2114_buffer(struct ldc2114_data *ldc, int type, ...) +{ + struct ldc2114_drv *cxt = dev_get_drvdata(ldc->dev); + va_list vp; + item_t data; + int rc = 0; + + if (!(cxt && test_bit(0, &cxt->ds.opened))) + return -ENODEV; + + va_start(vp, type); + data.type = type; + switch (type) { + case LDC2114_EV_DATA: + data.de = va_arg(vp, struct ldc2114_raw_ext); + break; + default: + rc = -EINVAL; + goto leave_now; + } + buffer_push(&cxt->ds, &data, sizeof(data)); + +leave_now: + va_end(vp); + + return rc; +} + +static int inline comp2_12b(struct ldc2114_16bit *data) +{ + int result; + /* 12bits 2's compliment data */ + if (data->msb & 0x8) + result = (data->lsb | (data->msb << 8) | 0xFFFFF000); + else + result = (data->lsb | (data->msb << 8)); + + return result; +} + +static ssize_t ldc2114_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct ldc2114_drv *context = file->private_data; + int i, ret; + item_t data; + char out[128]; + + ret = buffer_pop(&context->ds, &data); + if (ret < 0) + return ret; + + switch(data.type) { + case LDC2114_EV_DATA: + count = snprintf(out, sizeof(out), "I:%d", + (data.de.status & LDC2114_REG_STATUS_OUT) != 0); + for (i = 0; i < MAX_KEYS; i++) { + count += snprintf(out+count, sizeof(out)-count, " [%d]:%d/%d", + i, (data.de.data.out & (1 << i)) != 0, + comp2_12b(&data.de.data.values[i])); + } + count += snprintf(out+count, sizeof(out)-count, "\n"); + break; + } + + if (copy_to_user(buf, out, count)) + count = -EFAULT; + + return count; +} + +static int ldc2114_open(struct inode *inode, struct file *file) +{ + struct ldc2114_drv *cxt = inode->i_private; + int ret; + + if (!cxt) + return -ENODEV; + + file->private_data = cxt; + + ret = buffer_init(&cxt->ds, BUFFER_SIZE); + if (ret) + return -ENOMEM; + + if (test_and_set_bit_lock(0, &cxt->ds.opened)) { + buffer_destroy(&cxt->ds); + return -EBUSY; + } + + dev_dbg(cxt->dev, "%s: allocated circular buffer\n", __func__); + blocking_notifier_call_chain(&cxt->nhead, cxt->ds.opened, NULL); + + return nonseekable_open(inode, file); +} + +static int ldc2114_release(struct inode *inode, struct file *file) +{ + struct ldc2114_drv *context = file->private_data; + + clear_bit_unlock(0, &context->ds.opened); + dev_dbg(context->dev, "%s: opend flag %lu\n", __func__, context->ds.opened); + blocking_notifier_call_chain(&context->nhead, context->ds.opened, NULL); + buffer_destroy(&context->ds); + + dev_dbg(context->dev, "%s: destroyed circular buffer\n", __func__); + + return 0; +} + +const struct file_operations ldc2114_debugfs_fops = { + .owner = THIS_MODULE, + .open = ldc2114_open, + .read = ldc2114_read, + .release = ldc2114_release, + .llseek = no_llseek, +}; + +int ldc2114_register_client(struct ldc2114_data *ldc, struct notifier_block *nb) +{ + struct ldc2114_drv *cxt = dev_get_drvdata(ldc->dev); + + dev_dbg(ldc->dev, "cxt %p\n", cxt); + if (!cxt) + return -ENODEV; + dev_dbg(ldc->dev, "%s: registering notifier\n", __func__); + + return blocking_notifier_chain_register(&cxt->nhead, nb); +} + +static char *INSTANCE(char *string, struct ldc2114_data *ldc) +{ + char instance[64]; + snprintf(instance, sizeof(instance), "%s.%d", + string, ldc->instance); + return kstrdup(instance, GFP_KERNEL); +} + +static struct dentry *root_dir; + +int ldc2114_debugfs_init(struct ldc2114_data *ldc) +{ + struct ldc2114_drv *cxt; + int rc = 0; + + if (!root_dir) + root_dir = debugfs_create_dir(DRVNAME, NULL); + + if (!root_dir) + return -ENODEV; + + cxt = kzalloc(sizeof(*cxt), GFP_KERNEL); + if (!cxt) { + rc = -ENOMEM; + goto remove_dbg_root; + } + + cxt->name = INSTANCE(DRVNAME, ldc); + cxt->dev = ldc->dev; + dev_set_drvdata(ldc->dev, cxt); + + cxt->dentry = debugfs_create_file(cxt->name, + 0444, root_dir, cxt, &ldc2114_debugfs_fops); + dev_dbg(ldc->dev, "%s: debugfs file '%s' created\n", __func__, cxt->name); + + BLOCKING_INIT_NOTIFIER_HEAD(&cxt->nhead); + + return 0; + +remove_dbg_root: + debugfs_remove(root_dir); + + return rc; +} + +void ldc2114_debugfs_remove(struct ldc2114_data *ldc) +{ + struct ldc2114_drv *cxt = dev_get_drvdata(ldc->dev); + + if (!cxt) + return; + + debugfs_remove(cxt->dentry); + kfree(cxt); + dev_set_drvdata(ldc->dev, NULL); +} +#endif diff --git a/drivers/input/misc/ldc2114_mmi/ldc2114.c b/drivers/input/misc/ldc2114_mmi/ldc2114.c new file mode 100644 index 000000000000..82b3f30876c7 --- /dev/null +++ b/drivers/input/misc/ldc2114_mmi/ldc2114.c @@ -0,0 +1,1397 @@ +/* + * LDC2114 Metal Touch Inductance-to-Digital Converter + * + * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/ + * Andrew F. Davis + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ldc2114.h" + +#define LDC2114_DRIVER_NAME "ldc2114" + +#define LDC2114_EV_DATA 1 +#define LDC2114_EV_IRQ 2 +#define LDC2114_EV_PRESS 3 + +#define LDC2114_SHORT_DELAY 100 +#define LDC2114_LONG_DELAY (LDC2114_SHORT_DELAY * 5) +#define LDC2114_MAX_RETRIES 10 + +#define CONFIG_LDC2114_MAX_FAILURES 100 + +#define WAIT_USEC_50 (50 * USEC_PER_MSEC) +#define WAIT_USEC_60 (60 * USEC_PER_MSEC) +#define WAIT_USEC_LOW (LDC2114_SHORT_DELAY * USEC_PER_MSEC) +#define WAIT_USEC_HIGH (WAIT_USEC_LOW + (WAIT_USEC_LOW >> 3)) + +#define irq_to_gpio(irq) ((irq) - gpio_to_irq(0)) + +/* LDC2114 registers */ +#define LDC2114_STATUS 0x00 +#define LDC2114_OUT 0x01 +#define LDC2114_DATA0_LSB 0x02 +#define LDC2114_DATA0_MSB 0x03 +#define LDC2114_DATA1_LSB 0x04 +#define LDC2114_DATA1_MSB 0x05 +#define LDC2114_DATA2_LSB 0x06 +#define LDC2114_DATA2_MSB 0x07 +#define LDC2114_DATA3_LSB 0x08 +#define LDC2114_DATA3_MSB 0x09 +#define LDC2114_RESET 0x0A +#define LDC2114_EN 0x0C +#define LDC2114_NP_SCAN_RATE 0x0D +#define LDC2114_GAIN0 0x0E +#define LDC2114_LP_SCAN_RATE 0x0F +#define LDC2114_GAIN1 0x10 +#define LDC2114_INTPOL 0x11 +#define LDC2114_GAIN2 0x12 +#define LDC2114_LP_BASE_INC 0x13 +#define LDC2114_GAIN3 0x14 +#define LDC2114_NP_BASE_INC 0x15 +#define LDC2114_MAXWIN 0x16 +#define LDC2114_LC_DIVIDER 0x17 +#define LDC2114_HYST 0x18 +#define LDC2114_TWIST 0x19 +#define LDC2114_COMMON_DEFORM 0x1A +#define LDC2114_OPOL 0x1C +#define LDC2114_CNTSC 0x1E +#define LDC2114_SENSOR0_CONFIG 0x20 +#define LDC2114_SENSOR1_CONFIG 0x22 +#define LDC2114_SENSOR2_CONFIG 0x24 +#define LDC2114_FTF0 0x25 +#define LDC2114_SENSOR3_CONFIG 0x26 +#define LDC2114_FTF1_2 0x28 +#define LDC2114_FTF3 0x2B +#define LDC2114_MANUFACTURER_ID_LSB 0xFC +#define LDC2114_MANUFACTURER_ID_MSB 0xFD +#define LDC2114_DEVICE_ID_LSB 0xFE +#define LDC2114_DEVICE_ID_MSB 0xFF + +static int drv_instance_counter; + +static const struct reg_field ldc2114_reg_fields[] = { + [F_STATUS_TIMEOUT] = REG_FIELD(LDC2114_STATUS, 1, 1), + [F_STATUS_LC_WD] = REG_FIELD(LDC2114_STATUS, 2, 2), + [F_STATUS_FSM_WD] = REG_FIELD(LDC2114_STATUS, 3, 3), + [F_STATUS_MAXOUT] = REG_FIELD(LDC2114_STATUS, 4, 4), + [F_STATUS_RDY_TO_WRITE] = REG_FIELD(LDC2114_STATUS, 5, 5), + [F_STATUS_CHIP_READY] = REG_FIELD(LDC2114_STATUS, 6, 6), + [F_STATUS_OUT_STATUS] = REG_FIELD(LDC2114_STATUS, 7, 7), + [F_OUT_OUT0] = REG_FIELD(LDC2114_OUT, 0, 0), + [F_OUT_OUT1] = REG_FIELD(LDC2114_OUT, 1, 1), + [F_OUT_OUT2] = REG_FIELD(LDC2114_OUT, 2, 2), + [F_OUT_OUT3] = REG_FIELD(LDC2114_OUT, 3, 3), + [F_RESET_STATE_RESET] = REG_FIELD(LDC2114_RESET, 0, 0), + [F_RESET_FULL_RESET] = REG_FIELD(LDC2114_RESET, 4, 4), + [F_EN_EN0] = REG_FIELD(LDC2114_EN, 0, 0), + [F_EN_EN1] = REG_FIELD(LDC2114_EN, 1, 1), + [F_EN_EN2] = REG_FIELD(LDC2114_EN, 2, 2), + [F_EN_EN3] = REG_FIELD(LDC2114_EN, 3, 3), + [F_EN_LPEN0] = REG_FIELD(LDC2114_EN, 4, 4), + [F_EN_LPEN1] = REG_FIELD(LDC2114_EN, 5, 5), + [F_EN_LPEN2] = REG_FIELD(LDC2114_EN, 6, 6), + [F_EN_LPEN3] = REG_FIELD(LDC2114_EN, 7, 7), + [F_NP_SCAN_RATE_NPSR] = REG_FIELD(LDC2114_NP_SCAN_RATE, 0, 1), + [F_GAIN0_GAIN0] = REG_FIELD(LDC2114_GAIN0, 0, 5), + [F_LP_SCAN_RATE_LPSR] = REG_FIELD(LDC2114_LP_SCAN_RATE, 0, 1), + [F_GAIN1_GAIN1] = REG_FIELD(LDC2114_GAIN1, 0, 5), + [F_INTPOL_INTPOL] = REG_FIELD(LDC2114_INTPOL, 2, 2), + [F_GAIN2_GAIN2] = REG_FIELD(LDC2114_GAIN2, 0, 5), + [F_LP_BASE_INC_LPBI] = REG_FIELD(LDC2114_LP_BASE_INC, 0, 2), + [F_GAIN3_GAIN3] = REG_FIELD(LDC2114_GAIN3, 0, 5), + [F_NP_BASE_INC_NPBI] = REG_FIELD(LDC2114_NP_BASE_INC, 0, 2), + [F_MAXWIN_MAXWIN0] = REG_FIELD(LDC2114_MAXWIN, 0, 0), + [F_MAXWIN_MAXWIN1] = REG_FIELD(LDC2114_MAXWIN, 1, 1), + [F_MAXWIN_MAXWIN2] = REG_FIELD(LDC2114_MAXWIN, 2, 2), + [F_MAXWIN_MAXWIN3] = REG_FIELD(LDC2114_MAXWIN, 3, 3), + [F_LC_DIVIDER_LCDIV] = REG_FIELD(LDC2114_LC_DIVIDER, 0, 2), + [F_HYST_HYST] = REG_FIELD(LDC2114_HYST, 0, 3), + [F_TWIST_ANTITWST] = REG_FIELD(LDC2114_TWIST, 0, 2), + [F_COMMON_DEFORM_ANTIDFRM0] = REG_FIELD(LDC2114_COMMON_DEFORM, 0, 0), + [F_COMMON_DEFORM_ANTIDFRM1] = REG_FIELD(LDC2114_COMMON_DEFORM, 1, 1), + [F_COMMON_DEFORM_ANTIDFRM2] = REG_FIELD(LDC2114_COMMON_DEFORM, 2, 2), + [F_COMMON_DEFORM_ANTIDFRM3] = REG_FIELD(LDC2114_COMMON_DEFORM, 3, 3), + [F_COMMON_DEFORM_ANTICM0] = REG_FIELD(LDC2114_COMMON_DEFORM, 4, 4), + [F_COMMON_DEFORM_ANTICM1] = REG_FIELD(LDC2114_COMMON_DEFORM, 5, 5), + [F_COMMON_DEFORM_ANTICM2] = REG_FIELD(LDC2114_COMMON_DEFORM, 6, 6), + [F_COMMON_DEFORM_ANTICM3] = REG_FIELD(LDC2114_COMMON_DEFORM, 7, 7), + [F_OPOL_OPOL0] = REG_FIELD(LDC2114_OPOL, 4, 4), + [F_OPOL_OPOL1] = REG_FIELD(LDC2114_OPOL, 5, 5), + [F_OPOL_OPOL2] = REG_FIELD(LDC2114_OPOL, 6, 6), + [F_OPOL_OPOL3] = REG_FIELD(LDC2114_OPOL, 7, 7), + [F_CNTSC_CNTSC0] = REG_FIELD(LDC2114_CNTSC, 0, 1), + [F_CNTSC_CNTSC1] = REG_FIELD(LDC2114_CNTSC, 2, 3), + [F_CNTSC_CNTSC2] = REG_FIELD(LDC2114_CNTSC, 4, 5), + [F_CNTSC_CNTSC3] = REG_FIELD(LDC2114_CNTSC, 6, 7), + [F_SENSOR0_CONFIG_SENCYC0] = REG_FIELD(LDC2114_SENSOR0_CONFIG, 0, 4), + [F_SENSOR0_CONFIG_FREQ0] = REG_FIELD(LDC2114_SENSOR0_CONFIG, 5, 6), + [F_SENSOR0_CONFIG_RP0] = REG_FIELD(LDC2114_SENSOR0_CONFIG, 7, 7), + [F_SENSOR1_CONFIG_SENCYC1] = REG_FIELD(LDC2114_SENSOR1_CONFIG, 0, 4), + [F_SENSOR1_CONFIG_FREQ1] = REG_FIELD(LDC2114_SENSOR1_CONFIG, 5, 6), + [F_SENSOR1_CONFIG_RP1] = REG_FIELD(LDC2114_SENSOR1_CONFIG, 7, 7), + [F_SENSOR2_CONFIG_SENCYC2] = REG_FIELD(LDC2114_SENSOR2_CONFIG, 0, 4), + [F_SENSOR2_CONFIG_FREQ2] = REG_FIELD(LDC2114_SENSOR2_CONFIG, 5, 6), + [F_SENSOR2_CONFIG_RP2] = REG_FIELD(LDC2114_SENSOR2_CONFIG, 7, 7), + [F_FTF0_FTF0] = REG_FIELD(LDC2114_FTF0, 1, 2), + [F_SENSOR3_CONFIG_SENCYC3] = REG_FIELD(LDC2114_SENSOR3_CONFIG, 0, 4), + [F_SENSOR3_CONFIG_FREQ3] = REG_FIELD(LDC2114_SENSOR3_CONFIG, 5, 6), + [F_SENSOR3_CONFIG_RP3] = REG_FIELD(LDC2114_SENSOR3_CONFIG, 7, 7), + [F_FTF1_2_FTF1] = REG_FIELD(LDC2114_FTF1_2, 4, 5), + [F_FTF1_2_FTF2] = REG_FIELD(LDC2114_FTF1_2, 6, 7), + [F_FTF3_FTF3] = REG_FIELD(LDC2114_FTF3, 0, 1), +}; + +struct ldc2114_attr { + struct device_attribute dev_attr; + unsigned int field; +}; + +#define to_ldc2114_attr(_dev_attr) \ + container_of(_dev_attr, struct ldc2114_attr, dev_attr) + +#define LDC2114_ATTR(_name, _field) \ + struct ldc2114_attr ldc2114_attr_##_name = { \ + .dev_attr = __ATTR(_name, (S_IRUGO | S_IWUSR), \ + ldc2114_show_reg, \ + ldc2114_store_reg), \ + .field = _field, \ + } + +static ssize_t ldc2114_store_reg(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ldc2114_data *ldc = i2c_get_clientdata(client); + struct ldc2114_attr *ldc2114_attr = to_ldc2114_attr(attr); + unsigned int val; + int ret; + + ret = kstrtouint(buf, 0, &val); + if (ret) + return ret; + + ret = regmap_field_write(ldc->fields[ldc2114_attr->field], val); + if (ret) + return ret; + + return count; +} + +static ssize_t ldc2114_show_reg(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ldc2114_data *ldc = i2c_get_clientdata(client); + struct ldc2114_attr *ldc2114_attr = to_ldc2114_attr(attr); + unsigned int val; + int ret; + + ret = regmap_field_read(ldc->fields[ldc2114_attr->field], &val); + if (ret) + return ret; + + return sprintf(buf, "%d\n", val); +} + +static LDC2114_ATTR(timout, F_STATUS_TIMEOUT); +static LDC2114_ATTR(lc_wd, F_STATUS_LC_WD); +static LDC2114_ATTR(fsm_wd, F_STATUS_FSM_WD); +static LDC2114_ATTR(maxout, F_STATUS_MAXOUT); +static LDC2114_ATTR(rdy_to_write, F_STATUS_RDY_TO_WRITE); +static LDC2114_ATTR(chip_ready, F_STATUS_CHIP_READY); +static LDC2114_ATTR(out_status, F_STATUS_OUT_STATUS); +static LDC2114_ATTR(out0, F_OUT_OUT0); +static LDC2114_ATTR(out1, F_OUT_OUT1); +static LDC2114_ATTR(out2, F_OUT_OUT2); +static LDC2114_ATTR(out3, F_OUT_OUT3); +static LDC2114_ATTR(state_reset, F_RESET_STATE_RESET); +static LDC2114_ATTR(full_reset, F_RESET_FULL_RESET); +static LDC2114_ATTR(en0, F_EN_EN0); +static LDC2114_ATTR(en1, F_EN_EN1); +static LDC2114_ATTR(en2, F_EN_EN2); +static LDC2114_ATTR(en3, F_EN_EN3); +static LDC2114_ATTR(lpen0, F_EN_LPEN0); +static LDC2114_ATTR(lpen1, F_EN_LPEN1); +static LDC2114_ATTR(lpen2, F_EN_LPEN2); +static LDC2114_ATTR(lpen3, F_EN_LPEN3); +static LDC2114_ATTR(npsr, F_NP_SCAN_RATE_NPSR); +static LDC2114_ATTR(lpsr, F_LP_SCAN_RATE_LPSR); +static LDC2114_ATTR(gain0, F_GAIN0_GAIN0); +static LDC2114_ATTR(gain1, F_GAIN1_GAIN1); +static LDC2114_ATTR(gain2, F_GAIN2_GAIN2); +static LDC2114_ATTR(gain3, F_GAIN3_GAIN3); +static LDC2114_ATTR(intpol, F_INTPOL_INTPOL); +static LDC2114_ATTR(lpbi, F_LP_BASE_INC_LPBI); +static LDC2114_ATTR(npbi, F_NP_BASE_INC_NPBI); +static LDC2114_ATTR(maxwin0, F_MAXWIN_MAXWIN0); +static LDC2114_ATTR(maxwin1, F_MAXWIN_MAXWIN1); +static LDC2114_ATTR(maxwin2, F_MAXWIN_MAXWIN2); +static LDC2114_ATTR(maxwin3, F_MAXWIN_MAXWIN3); +static LDC2114_ATTR(lcdiv, F_LC_DIVIDER_LCDIV); +static LDC2114_ATTR(hyst, F_HYST_HYST); +static LDC2114_ATTR(antitwst, F_TWIST_ANTITWST); +static LDC2114_ATTR(antidfrm0, F_COMMON_DEFORM_ANTIDFRM0); +static LDC2114_ATTR(antidfrm1, F_COMMON_DEFORM_ANTIDFRM1); +static LDC2114_ATTR(antidfrm2, F_COMMON_DEFORM_ANTIDFRM2); +static LDC2114_ATTR(antidfrm3, F_COMMON_DEFORM_ANTIDFRM3); +static LDC2114_ATTR(anticm0, F_COMMON_DEFORM_ANTICM0); +static LDC2114_ATTR(anticm1, F_COMMON_DEFORM_ANTICM1); +static LDC2114_ATTR(anticm2, F_COMMON_DEFORM_ANTICM2); +static LDC2114_ATTR(anticm3, F_COMMON_DEFORM_ANTICM3); +static LDC2114_ATTR(opol0, F_OPOL_OPOL0); +static LDC2114_ATTR(opol1, F_OPOL_OPOL1); +static LDC2114_ATTR(opol2, F_OPOL_OPOL2); +static LDC2114_ATTR(opol3, F_OPOL_OPOL3); +static LDC2114_ATTR(cntsc0, F_CNTSC_CNTSC0); +static LDC2114_ATTR(cntsc1, F_CNTSC_CNTSC1); +static LDC2114_ATTR(cntsc2, F_CNTSC_CNTSC2); +static LDC2114_ATTR(cntsc3, F_CNTSC_CNTSC3); +static LDC2114_ATTR(sencyc0, F_SENSOR0_CONFIG_SENCYC0); +static LDC2114_ATTR(freq0, F_SENSOR0_CONFIG_FREQ0); +static LDC2114_ATTR(rp0, F_SENSOR0_CONFIG_RP0); +static LDC2114_ATTR(sencyc1, F_SENSOR1_CONFIG_SENCYC1); +static LDC2114_ATTR(freq1, F_SENSOR1_CONFIG_FREQ1); +static LDC2114_ATTR(rp1, F_SENSOR1_CONFIG_RP1); +static LDC2114_ATTR(sencyc2, F_SENSOR2_CONFIG_SENCYC2); +static LDC2114_ATTR(freq2, F_SENSOR2_CONFIG_FREQ2); +static LDC2114_ATTR(rp2, F_SENSOR2_CONFIG_RP2); +static LDC2114_ATTR(sencyc3, F_SENSOR3_CONFIG_SENCYC3); +static LDC2114_ATTR(freq3, F_SENSOR3_CONFIG_FREQ3); +static LDC2114_ATTR(rp3, F_SENSOR3_CONFIG_RP3); +static LDC2114_ATTR(ftf0, F_FTF0_FTF0); +static LDC2114_ATTR(ftf1, F_FTF1_2_FTF1); +static LDC2114_ATTR(ftf2, F_FTF1_2_FTF2); +static LDC2114_ATTR(ftf3, F_FTF3_FTF3); + +static ssize_t ldc2114_verno_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ldc2114_data *ldc = i2c_get_clientdata(client); + + return sprintf(buf, "%u\n", ldc->verno); +} + +static struct ldc2114_attr ldc2114_attr_verno = { + .dev_attr = __ATTR(verno, S_IRUGO, ldc2114_verno_show, NULL), + .field = 0, +}; + +static struct attribute *ldc2114_attributes[] = { + &ldc2114_attr_timout.dev_attr.attr, + &ldc2114_attr_lc_wd.dev_attr.attr, + &ldc2114_attr_fsm_wd.dev_attr.attr, + &ldc2114_attr_maxout.dev_attr.attr, + &ldc2114_attr_rdy_to_write.dev_attr.attr, + &ldc2114_attr_chip_ready.dev_attr.attr, + &ldc2114_attr_out_status.dev_attr.attr, + &ldc2114_attr_out0.dev_attr.attr, + &ldc2114_attr_out1.dev_attr.attr, + &ldc2114_attr_out2.dev_attr.attr, + &ldc2114_attr_out3.dev_attr.attr, + &ldc2114_attr_state_reset.dev_attr.attr, + &ldc2114_attr_full_reset.dev_attr.attr, + &ldc2114_attr_en0.dev_attr.attr, + &ldc2114_attr_en1.dev_attr.attr, + &ldc2114_attr_en2.dev_attr.attr, + &ldc2114_attr_en3.dev_attr.attr, + &ldc2114_attr_lpen0.dev_attr.attr, + &ldc2114_attr_lpen1.dev_attr.attr, + &ldc2114_attr_lpen2.dev_attr.attr, + &ldc2114_attr_lpen3.dev_attr.attr, + &ldc2114_attr_npsr.dev_attr.attr, + &ldc2114_attr_lpsr.dev_attr.attr, + &ldc2114_attr_gain0.dev_attr.attr, + &ldc2114_attr_gain1.dev_attr.attr, + &ldc2114_attr_gain2.dev_attr.attr, + &ldc2114_attr_gain3.dev_attr.attr, + &ldc2114_attr_intpol.dev_attr.attr, + &ldc2114_attr_lpbi.dev_attr.attr, + &ldc2114_attr_npbi.dev_attr.attr, + &ldc2114_attr_maxwin0.dev_attr.attr, + &ldc2114_attr_maxwin1.dev_attr.attr, + &ldc2114_attr_maxwin2.dev_attr.attr, + &ldc2114_attr_maxwin3.dev_attr.attr, + &ldc2114_attr_lcdiv.dev_attr.attr, + &ldc2114_attr_hyst.dev_attr.attr, + &ldc2114_attr_antitwst.dev_attr.attr, + &ldc2114_attr_antidfrm0.dev_attr.attr, + &ldc2114_attr_antidfrm1.dev_attr.attr, + &ldc2114_attr_antidfrm2.dev_attr.attr, + &ldc2114_attr_antidfrm3.dev_attr.attr, + &ldc2114_attr_anticm0.dev_attr.attr, + &ldc2114_attr_anticm1.dev_attr.attr, + &ldc2114_attr_anticm2.dev_attr.attr, + &ldc2114_attr_anticm3.dev_attr.attr, + &ldc2114_attr_opol0.dev_attr.attr, + &ldc2114_attr_opol1.dev_attr.attr, + &ldc2114_attr_opol2.dev_attr.attr, + &ldc2114_attr_opol3.dev_attr.attr, + &ldc2114_attr_cntsc0.dev_attr.attr, + &ldc2114_attr_cntsc1.dev_attr.attr, + &ldc2114_attr_cntsc2.dev_attr.attr, + &ldc2114_attr_cntsc3.dev_attr.attr, + &ldc2114_attr_sencyc0.dev_attr.attr, + &ldc2114_attr_freq0.dev_attr.attr, + &ldc2114_attr_rp0.dev_attr.attr, + &ldc2114_attr_sencyc1.dev_attr.attr, + &ldc2114_attr_freq1.dev_attr.attr, + &ldc2114_attr_rp1.dev_attr.attr, + &ldc2114_attr_sencyc2.dev_attr.attr, + &ldc2114_attr_freq2.dev_attr.attr, + &ldc2114_attr_rp2.dev_attr.attr, + &ldc2114_attr_sencyc3.dev_attr.attr, + &ldc2114_attr_freq3.dev_attr.attr, + &ldc2114_attr_rp3.dev_attr.attr, + &ldc2114_attr_ftf0.dev_attr.attr, + &ldc2114_attr_ftf1.dev_attr.attr, + &ldc2114_attr_ftf2.dev_attr.attr, + &ldc2114_attr_ftf3.dev_attr.attr, + &ldc2114_attr_verno.dev_attr.attr, + NULL +}; + +static const struct attribute_group ldc2114_attr_group = { + .attrs = ldc2114_attributes, +}; + +static int ldc2114_i2c_write_regmap(void *context, + const void *data, size_t count) +{ + struct device *dev = context; + struct i2c_client *client = to_i2c_client(dev); + struct i2c_msg xfer[2]; + uint8_t buffer[2]; + int ret; + + buffer[0] = LDC2114_RESET; + buffer[1] = BIT(0); + ret = i2c_master_send(client, buffer, 2); + if (ret < 0) + return ret; + else if (ret != 2) + return -EIO; + + do { + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 1; + buffer[0] = LDC2114_STATUS; + xfer[0].buf = (void *)&buffer[0]; + + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = 1; + xfer[1].buf = (void *)&buffer[1]; + + ret = i2c_transfer(client->adapter, xfer, 2); + if (ret < 0) + return ret; + else if (ret != 2) + return -EIO; + } while(!(buffer[1] & BIT(5))); + + ret = i2c_master_send(client, data, count); + if (ret < 0) + return ret; + else if (ret != count) + return -EIO; + + buffer[0] = LDC2114_RESET; + buffer[1] = 0; + ret = i2c_master_send(client, buffer, 2); + if (ret < 0) + return ret; + else if (ret != 2) + return -EIO; + + return 0; +} + +#define I2C_TRANSFER(adapter, msgs, count) \ + ret = i2c_transfer(adapter, msgs, count); \ + if (ret == count) \ + ret = 0 + +static int ldc2114_i2c_read_regmap(void *context, + const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + struct i2c_msg xfer[2]; + int ret; + + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + xfer[0].len = reg_size; + xfer[0].buf = (void *)reg; + + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = val_size; + xfer[1].buf = val; + + I2C_TRANSFER(i2c->adapter, xfer, ARRAY_SIZE(xfer)); + + return ret; +} + +static int ldc2114_write_reg8(void *context, uint8_t regaddr, uint8_t value) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + uint8_t wbuf[] = { regaddr, value }; + struct i2c_msg msgs[1]; + int ret; + + msgs[0].addr = i2c->addr; + msgs[0].flags = 0; + msgs[0].len = ARRAY_SIZE(wbuf); + msgs[0].buf = wbuf; + + I2C_TRANSFER(i2c->adapter, msgs, ARRAY_SIZE(msgs)); + + return ret; +} + +static int ldc2114_read_reg8(void *context, uint8_t regaddr, uint8_t *value) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + struct i2c_msg msgs[2]; + int ret; + + msgs[0].addr = i2c->addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(regaddr); + msgs[0].buf = ®addr; + + msgs[1].addr = i2c->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(*value); + msgs[1].buf = (uint8_t *)value; + + I2C_TRANSFER(i2c->adapter, msgs, ARRAY_SIZE(msgs)); + + return ret; +} + +static int ldc2114_read_bulk(void *context, + uint8_t regaddr, void *data, size_t count) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + struct i2c_msg msgs[2]; + int ret; + + msgs[0].addr = i2c->addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(regaddr); + msgs[0].buf = ®addr; + + msgs[1].addr = i2c->addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = count; + msgs[1].buf = (uint8_t *)data; + + I2C_TRANSFER(i2c->adapter, msgs, ARRAY_SIZE(msgs)); + + return ret; +} + +static int inline ldc2114_write_bulk(void *context, + uint8_t regaddr, void *data, size_t count) +{ + struct device *dev = context; + struct i2c_client *i2c = to_i2c_client(dev); + + return i2c_master_send(i2c, data, count); +} + +static int ldc2114_reset(void *context, uint8_t reset_val, uint8_t status_bit) +{ + struct device *dev = context; + int rc, loops = 0; + uint8_t status; + + rc = ldc2114_write_reg8(context, LDC2114_RESET, reset_val); + if (rc) + dev_err(dev, "error writing reset command 0x%02x\n", reset_val); + while (1) { + loops++; + rc = ldc2114_read_reg8(context, LDC2114_STATUS, &status); + if (rc) + dev_err(dev, "error reading status\n"); + else + dev_dbg(dev, "status=0x%02x\n", status); + + if (status & status_bit) { + dev_info(dev, "reset 0x%02x complete: loops=%d\n", reset_val, loops); + break; + } + + if (loops > CONFIG_LDC2114_MAX_FAILURES) { + dev_err(dev, "reset 0x%02x failed\n", reset_val); + return -EIO; + } + usleep_range(WAIT_USEC_50, WAIT_USEC_60); + } + + return 0; +} + +/* + * We cannot use regmap-i2c generic bus implementation here as the LDC2114 + * has a special handshake process to write registers. + */ +static struct regmap_bus regmap_ldc2114_bus = { + .write = ldc2114_i2c_write_regmap, + .read = ldc2114_i2c_read_regmap, +}; + +static const struct regmap_range ldc2114_yes_ranges[] = { + regmap_reg_range(LDC2114_STATUS, LDC2114_DATA3_MSB), +}; + +static const struct regmap_access_table ldc2114_volatile_table = { + .yes_ranges = ldc2114_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(ldc2114_yes_ranges), +}; + +static const struct regmap_config ldc2114_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = LDC2114_DEVICE_ID_MSB, + .cache_type = REGCACHE_RBTREE, + .volatile_table = &ldc2114_volatile_table, +}; + +static int ldc2114_input_device(struct ldc2114_data *ldc) +{ + int i, ret; + + ldc->input = devm_input_allocate_device(ldc->dev); + if (!ldc->input) { + dev_err(ldc->dev, "unable to allocate input device\n"); + return -ENOMEM; + } + + set_bit(EV_KEY, ldc->input->evbit); + for (i = 0; i < MAX_KEYS; i++) { + set_bit(ldc->button_map[i], ldc->input->keybit); + input_set_capability(ldc->input, EV_KEY, ldc->button_map[i]); + } + + ldc->input->name = LDC2114_DRIVER_NAME "_keys"; + ldc->input->phys = LDC2114_DRIVER_NAME "_keys/input0"; + + ret = input_register_device(ldc->input); + if(ret < 0) + dev_err(ldc->dev, "error registering input %s\n", ldc->input->name); + + return ret; +} + +#if defined(LDC2114_POLL_DEBUG) || defined(LDC2114_CFG_DEBUG) +static void ldc2114_toggle_gpio(struct ldc2114_data *ldc, + int gpio) +{ + int value = gpio_get_value(gpio); + dev_dbg(ldc->dev, "toggle gpio %d from state %d\n", gpio, value); + gpio_set_value(gpio, !value); +#else +static void inline ldc2114_toggle_gpio(struct ldc2114_data *ldc, + int gpio) +{ + gpio_set_value(gpio, !gpio_get_value(gpio)); +#endif +} + +static void ldc2114_toggle(struct ldc2114_data *ldc, + int gpio, int times) +{ + int i = 0; + + if (!times || times < 0) + times = 1; + + while(1) { + ldc2114_toggle_gpio(ldc, gpio); + if (i++ == times) + break; + usleep_range(WAIT_USEC_LOW, WAIT_USEC_HIGH); + } +} + +static int ldc2114_poll(struct ldc2114_data *ldc, uint8_t *out) +{ + int ret; + struct ldc2114_raw_ext de; + static unsigned long executed; + + if (ldc->failures >= CONFIG_LDC2114_MAX_FAILURES) + return -EFAULT; + + ret = ldc2114_read_bulk(ldc->dev, LDC2114_STATUS, &de, sizeof(de)); + if (out) + *out = de.data.out; + if (ret) { + ldc->failures++; + + if (ldc->failures == CONFIG_LDC2114_MAX_FAILURES) { + dev_err(ldc->dev, "Max failures, disabling polling\n"); + return -EIO; + } + } else { + ldc->failures = 0; + ret = ldc2114_buffer(ldc, LDC2114_EV_DATA, de); + if (ret) + dev_err(ldc->dev, "buffer is not ready\n"); + } + + if (!(executed++%10)) + dev_info(ldc->dev, "polled %lu\n", executed); + + return 0; +} + +#ifdef LDC2114_POLL_DEBUG +#define NANO_SEC 1000000000 +#define SEC_TO_MSEC 1000 +#define NANO_TO_MSEC 1000000 + +static inline unsigned long long timediff_ms(struct timespec start, + struct timespec end) +{ + struct timespec temp; + + if ((end.tv_nsec - start.tv_nsec) < 0) { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = NANO_SEC + end.tv_nsec - start.tv_nsec; + } else { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return (temp.tv_sec * SEC_TO_MSEC) + (temp.tv_nsec / NANO_TO_MSEC); +} +#endif + +static void ldc2114_polling_work(struct work_struct *work) +{ + struct delayed_work *dw = + container_of(work, struct delayed_work, work); + struct ldc2114_data *ldc = + container_of(dw, struct ldc2114_data, polling_work); + int ret; + + ret = ldc2114_poll(ldc, NULL); + + if (atomic_read(&ldc->poll_work_running)) + schedule_delayed_work(&ldc->polling_work, + msecs_to_jiffies(ldc->poll_interval)); +} + +#ifdef LDC2114_POLL_DEBUG +static ktime_t start_polling_tm; +static int polling_counter; +#endif + +static void ldc2114_input_report(struct ldc2114_data *ldc, + int output_bits) +{ + int s, i, status; + + for (i = 0, s = 0; i < MAX_KEYS; i++) { + status = (output_bits >> i) & 1; + if (status == ldc->buttons[i]) + continue; + ldc->buttons[i] = status; + if (ldc->btn_enabled) { + input_report_key(ldc->input, ldc->button_map[i], status); + s++; + dev_dbg(ldc->dev, "sent key code = %d(%d)\n", + ldc->button_map[i], status); + } + } + + if (s) { /* only send sync if there were keys reported */ + input_sync(ldc->input); + dev_dbg(ldc->dev, "sent SYNC\n"); + } + +} + +static void ldc2114_irq_work(struct work_struct *work) +{ + struct delayed_work *dw = + container_of(work, struct delayed_work, work); + struct ldc2114_data *ldc = + container_of(dw, struct ldc2114_data, irq_work); + struct ldc2114_raw data; + uint8_t status_reg; + + while (1) { + int ret; + + down(&ldc->semaphore); + + ret = ldc2114_read_reg8(ldc->dev, LDC2114_STATUS, &status_reg); + if (status_reg & ~(LDC2114_REG_STATUS_CHIP_READY)) + dev_info(ldc->dev, "STATUS bits 0x%x\n", status_reg); + + if (status_reg & LDC2114_REG_STATUS_OUT) { + ret = ldc2114_read_bulk(ldc->dev, + LDC2114_OUT, &data, sizeof(data)); + if (data.out != ldc->output_bits) { + ldc->output_bits = data.out; + dev_dbg(ldc->dev, "OUTPUT bits 0x%x\n", ldc->output_bits); + } + + ldc2114_input_report(ldc, ldc->output_bits); + } + + if (atomic_read(&ldc->irq_work_running)) { +#ifdef LDC2114_POLL_DEBUG + polling_counter++; +#endif + usleep_range(WAIT_USEC_LOW, WAIT_USEC_HIGH); + up(&ldc->semaphore); + } else { +#ifdef LDC2114_POLL_DEBUG + dev_info(ldc->dev, "%s: polled %d times within %llums\n", + __func__, polling_counter, + timediff_ms(ktime_to_timespec(start_polling_tm), + ktime_to_timespec(ktime_get()))); + polling_counter = 0; +#endif + /* TODO: make sure we never miss release :) */ + /* Work around missed release */ + if (ldc->btn_enabled) + ldc2114_input_report(ldc, 0); + + memset(ldc->buttons, 0, sizeof(ldc->buttons)); + } + } +} + +static irqreturn_t ldc2114_irq(int irq, void *data) +{ + struct ldc2114_data *ldc = data; + bool stop_irq_work = atomic_read(&ldc->irq_work_running) == 1; + uint8_t status; + int ret; + + ret = ldc2114_read_reg8(ldc->dev, LDC2114_STATUS, &status); + dev_dbg(ldc->dev, "IRQ triggered; status 0x%x\n", status); + + /* check OUT ready bit */ + if (status & LDC2114_REG_STATUS_OUT) { + dev_dbg(ldc->dev, "starting work...\n"); + atomic_set(&ldc->irq_work_running, 1); + up(&ldc->semaphore); +#ifdef LDC2114_POLL_DEBUG + start_polling_tm = ktime_get(); +#endif + } else if (stop_irq_work) { + dev_dbg(ldc->dev, "stopping work...\n"); + atomic_set(&ldc->irq_work_running, 0); + } else + return IRQ_HANDLED; + + if (gpio_is_valid(ldc->signal_gpio)) + ldc2114_toggle_gpio(ldc, ldc->signal_gpio); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_OF +static const struct of_device_id ldc2114_of_match[] = { + { .compatible = "ti,ldc2114", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, ldc2114_of_match); + +static struct ldc2114_config *read_config_data(struct ldc2114_data *ldc, + struct device_node *parent, const char *suffix) +{ + struct ldc2114_config *config = NULL; + struct device_node *node; + u32 length = 0; + const char *data; + char node_name[64]; + int npairs, i; + + scnprintf(node_name, 63, "config-%s", suffix); + node = of_find_node_by_name(parent, node_name); + if (!node) + return NULL; + + data = of_get_property(node, "config-data", &length); + if (!data) { +#ifdef LDC2114_CFG_DEBUG + dev_info(ldc->dev, "(config-%s) prop config-data not found\n", suffix); +#endif + goto out; + } + + npairs = length / 2; +#ifdef LDC2114_CFG_DEBUG + dev_info(ldc->dev, "(config-%s) array size %d\n", suffix, npairs); +#endif + config = devm_kzalloc(ldc->dev, sizeof(struct ldc2114_config) + + sizeof(struct ldc2114_cfg_data) * npairs, GFP_KERNEL); + if (!config) + goto out; + + for (i = 0; i < npairs; i++) { + config->data[i].address = (uint8_t)*data++; + config->data[i].value = (uint8_t)*data++; +#ifdef LDC2114_CFG_DEBUG + dev_info(ldc->dev, "[%d] addr=0x%02x, val=0x%02x\n", + i, config->data[i].address, config->data[i].value); +#endif + } + + config->size = npairs; + +out: + of_node_put(node); + + return config; +} + +static int ldc2114_of_init(struct i2c_client *client) +{ + int ret; + struct ldc2114_data *ldc = i2c_get_clientdata(client); + struct device_node *np = client->dev.of_node; + struct device_node *config_np; + + ret = of_get_gpio(np, 0); + if (ret < 0) { + dev_err(ldc->dev, "failed to get lwpm-gpio: %d\n", ret); + return ret; + } + + ldc->lpwm_gpio = ret; + ret = devm_gpio_request(ldc->dev, + ldc->lpwm_gpio, LDC2114_DRIVER_NAME "_lpwm"); + if (ret) { + dev_err(ldc->dev, "failed to request lpwm-gpio %d\n", ldc->lpwm_gpio); + return ret; + } + + ret = of_get_gpio(np, 1); + if (ret < 0) + ldc->signal_gpio = -EINVAL; + else { + ldc->signal_gpio = ret; + dev_info(ldc->dev, "using gpio %d as a signal\n", ldc->signal_gpio); + + ret = devm_gpio_request(ldc->dev, + ldc->signal_gpio, LDC2114_DRIVER_NAME "_signal"); + if (ret) { + dev_err(ldc->dev, "failed to request signal-gpio %d\n", ldc->signal_gpio); + return ret; + } + + /* start high */ + gpio_direction_output(ldc->signal_gpio, 1); + } + + /* Init LWPM gpio into LOW, since this is default power off state */ + /* Then toggle its state as part of apply configuration procedure */ + gpio_direction_output(ldc->lpwm_gpio, 0); + + ret = of_irq_get(np, 0); + if (ret < 0) { + dev_err(ldc->dev, "failed to get irq: %d\n", ret); + return ret; + } + + ldc->irq = ret; + ldc->intb_gpio = irq_to_gpio(ldc->irq); + ret = devm_gpio_request(ldc->dev, + ldc->intb_gpio, LDC2114_DRIVER_NAME "_irq"); + if (ret) { + dev_err(ldc->dev, "failed to request gpio %d\n", ldc->intb_gpio); + return ret; + } + + ret = of_property_read_u32_array(np, "ldc2114,button-map", + ldc->button_map, 4); + if (!ret) { + ldc->btn_enabled = true; + dev_info(ldc->dev, "has keymap\n"); + } + + if (of_property_read_bool(np, "ldc2114,do-polling")) { + ldc->data_polling = true; + dev_info(ldc->dev,"supports data polling\n"); + } + + config_np = of_find_node_by_name(np, "configs"); + if (!config_np) { + dev_info(ldc->dev, "does not support configs\n"); + return 0; + } + + of_property_read_u32(config_np, "config-ver", &ldc->verno); + dev_info(ldc->dev, "dt config Rev.%u\n", ldc->verno); + + ldc->config.normal = read_config_data(ldc, config_np, "normal"); + if (ldc->config.normal) + dev_info(ldc->dev, "has normal config\n"); + + ldc->config.lpm = read_config_data(ldc, config_np, "lpm"); + if (ldc->config.lpm) + dev_info(ldc->dev, "has lpm config\n"); + + of_node_put(config_np); + + return 0; +} +#else +static inline void ldc2114_of_init(struct i2c_client *client) +{ +} +#endif + +static void inline ldc2114_config_init(struct ldc2114_data *ldc, + struct ldc2114_config *cfg, int delay_ms, bool resched) +{ + dev_dbg(ldc->dev, "config_init entered\n"); + + ldc->config.active = cfg; + ldc->config.delay_ms = delay_ms; + ldc->config.attempts = 0; + ldc->config.toggled = false; + ldc->config.reschedulable = resched; + reinit_completion(&ldc->config.done); +} + +#define EXITIF_ERR(cmd) { \ + ret = cmd; \ + if (ret) { \ + dev_dbg(ldc->dev, "ret=%d; exiting...\n", ret); \ + return ret; \ + } \ + } + +#define JUMPIF_ERR(cmd) { \ + ret = cmd; \ + if (ret) \ + goto err_check; \ + } + +static void ldc2114_config_work(struct work_struct *work) +{ + struct delayed_work *dw = + container_of(work, struct delayed_work, work); + struct ldc2114_data *ldc = + container_of(dw, struct ldc2114_data, config_work); + bool partial_fail = false; + bool is_ready = ldc->config.active && ldc->config.active->size; + int i, ret; + + dev_dbg(ldc->dev, "config_work entered; is_ready=%d\n", is_ready); + + if (!ldc->not_connected && + ldc->config.reschedulable && + (gpio_get_value(ldc->intb_gpio) == 0)) + goto reschedule_me; + + if (!ldc->config.toggled) { + /* now it's time to toggle LWPM pin, but only toggle it once */ + ldc2114_toggle_gpio(ldc, ldc->lpwm_gpio); + ldc->config.toggled = true; + dev_info(ldc->dev, "LWPM toggled\n"); + + if (gpio_is_valid(ldc->signal_gpio)) + ldc2114_toggle(ldc, ldc->signal_gpio, 2); + } + + if (!is_ready) { + dev_dbg(ldc->dev, "nothing to apply\n"); + goto done; + } + + JUMPIF_ERR(ldc2114_reset(ldc->dev, LDC2114_REG_RESET_FULL, + LDC2114_REG_STATUS_CHIP_READY)); + JUMPIF_ERR(ldc2114_reset(ldc->dev, LDC2114_REG_RESET_CONFIG_MODE, + LDC2114_REG_STATUS_RDY_TO_WRITE)); + + for (i = 0; i < ldc->config.active->size; i++) { + ret = ldc2114_write_reg8(ldc->dev, + ldc->config.active->data[i].address, + ldc->config.active->data[i].value); + if (ret) { + partial_fail = true; + dev_err(ldc->dev, "config failed at i=%d\n", i); + break; + } + } + + JUMPIF_ERR(ldc2114_reset(ldc->dev, LDC2114_REG_RESET_NONE, + LDC2114_REG_STATUS_CHIP_READY)); +err_check: + + if (ret || partial_fail) { + /* In unlikely case when all LDC2114_MAX_RETRIES failed, */ + /* we just bail out and return an error. LPWM gpio will */ + /* be toggled anyway. And then we either fail probe or */ + /* leave IC in unknown state. Not much we can do here */ + if (++ldc->config.attempts > LDC2114_MAX_RETRIES) { + dev_err(ldc->dev, "bailed out on applying config\n"); + goto done; + } + } else { + dev_info(ldc->dev, "config Rev.%u applied\n", ldc->verno); + goto done; + } + +reschedule_me: + queue_delayed_work(ldc->wq, &ldc->config_work, + msecs_to_jiffies(ldc->config.delay_ms)); +#ifdef LDC2114_CFG_DEBUG + dev_info(ldc->dev, "re-scheduled\n"); +#endif + return; + +done: + complete(&ldc->config.done); + ldc->config.active = NULL; +#ifdef LDC2114_CFG_DEBUG + if (1) { + uint8_t value; + + ldc2114_read_reg8(ldc->dev, LDC2114_STATUS, &value); + dev_info(ldc->dev, "done: INTB %d, STATUS 0x%02x\n", + gpio_get_value(ldc->intb_gpio), value); + } +#endif + + if (gpio_is_valid(ldc->signal_gpio)) + ldc2114_toggle(ldc, ldc->signal_gpio, 2); +} + +static int ldc2114_initialize(struct ldc2114_data *ldc) +{ + struct ldc2114_16bit version; + uint8_t value; + int ret; + +error_workaround: + + EXITIF_ERR(ldc2114_read_reg8(ldc->dev, LDC2114_STATUS, &value)); + dev_info(ldc->dev, "STATUS 0x%02x\n", value); + + /* check connection issues */ + if (value & LDC2114_REG_STATUS_LC_WD) { + ldc->not_connected = true; + dev_info(ldc->dev, "sensor disconnected\n"); + if (ldc->btn_enabled) { + ldc->btn_enabled = false; + dev_info(ldc->dev, "disabling buttons\n"); + } + } else { + /* reading OUT helps with issues when INTB gets stuck due */ + /* to internal issues between LDC2114 and Silego parts */ + uint8_t out; + + ldc2114_poll(ldc, &out); +#ifdef LDC2114_CFG_DEBUG + dev_info(ldc->dev, "OUT 0x%02x, INTB %d\n", + out, gpio_get_value(ldc->intb_gpio)); +#endif + /* some errors require STATUS register to be read twice, */ + /* thus do STATUS read again here if errors detected */ + if (value & LDC2114_REG_STATUS_ERROR_OTHERS) { + dev_info(ldc->dev, "error condition detected; retrying...\n"); + goto error_workaround; + } + } + + EXITIF_ERR(ldc2114_read_bulk(ldc->dev, LDC2114_DEVICE_ID_LSB, + &version, sizeof(version))); + dev_info(ldc->dev, "TI LDC%d 0x%x\n", + 2114 - version.lsb * 2, + (version.lsb | (version.msb << 8))); + + EXITIF_ERR(ldc2114_read_reg8(ldc->dev, LDC2114_INTPOL, &value)); + ldc->intb_polarity = value & BIT(3) ? 1 : 0; + + dev_info(ldc->dev, "INTB active %d\n", ldc->intb_polarity); + dev_info(ldc->dev, "IRQ %d (gpio%d)\n", + ldc->irq, irq_to_gpio(ldc->irq)); + + ldc2114_config_init(ldc, ldc->config.normal, LDC2114_LONG_DELAY, true); + + queue_delayed_work(ldc->wq, &ldc->config_work, 0); + + return 0; +} + +static int ldc2114_poll_enable_cb(struct notifier_block *n, + unsigned long val, void *data) +{ + struct ldc2114_data *ldc = + container_of(n, struct ldc2114_data, poll_nb); + int state = test_bit(0, &val) ? 1 : 0; + + dev_info(ldc->dev, "polling flag %d; state=%d\n", + ldc->data_polling, state); + + if (!ldc->data_polling) + return 0; + + atomic_set(&ldc->poll_work_running, state); + + if (state) + schedule_work(&ldc->polling_work.work); + + return 0; +} + +#include + +static DEFINE_RATELIMIT_STATE(ldc2114_rl, HZ, 5); + +static int ldc2114_reboot_cb(struct notifier_block *nb, + unsigned long event, void *unused) +{ + struct ldc2114_data *ldc = + container_of(nb, struct ldc2114_data, reboot_nb); + static int runaway; + + if (runaway) + goto out; + + runaway++; + + ldc2114_config_init(ldc, ldc->config.lpm, LDC2114_SHORT_DELAY, false); + + /* Stop polling work if it's running */ + if (ldc->data_polling) + cancel_delayed_work_sync(&ldc->polling_work); + + if (ldc->irq_enabled) { + /* wait until active touch is cleared */ + while (atomic_read(&ldc->irq_work_running) == 1) { + + if (!__ratelimit(&ldc2114_rl)) + dev_warn(ldc->dev, "irq still busy\n"); + + usleep_range(WAIT_USEC_LOW, WAIT_USEC_HIGH); + } + + disable_irq(ldc->irq); + + if (gpio_is_valid(ldc->signal_gpio)) + ldc2114_toggle(ldc, ldc->signal_gpio, 2); + } + + ldc2114_config_work(&ldc->config_work.work); + /* set low at the end */ + if (gpio_is_valid(ldc->signal_gpio)) + gpio_set_value(ldc->signal_gpio, 0); +out: + return NOTIFY_DONE; +} + +static int ldc2114_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ldc2114_data *ldc; + struct pinctrl *pinctrl; + int i, ret; + + if (!i2c_check_functionality( + client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, "SMBus byte data not supported\n"); + return -EIO; + } + + ldc = devm_kzalloc(&client->dev, sizeof(*ldc), GFP_KERNEL); + if (!ldc) + return -ENOMEM; + + i2c_set_clientdata(client, ldc); + ldc->dev = &client->dev; + ldc->irq = client->irq; + ldc->data_polling = true; + + if (client->dev.of_node) { + ret = ldc2114_of_init(client); + if (ret < 0) + return -EINVAL; + } + + ldc->regmap = devm_regmap_init(ldc->dev, ®map_ldc2114_bus, + ldc->dev, &ldc2114_regmap_config); + if (IS_ERR(ldc->regmap)) { + dev_err(ldc->dev, "Unable to allocate register map\n"); + return PTR_ERR(ldc->regmap); + } + + for (i = 0; i < F_MAX_FIELDS; i++) { + ldc->fields[i] = devm_regmap_field_alloc(ldc->dev, + ldc->regmap, ldc2114_reg_fields[i]); + if (IS_ERR(ldc->fields[i])) { + dev_err(ldc->dev, "Unable to allocate regmap fields\n"); + return PTR_ERR(ldc->fields[i]); + } + } + + pinctrl = devm_pinctrl_get_select_default(&client->dev); + if (IS_ERR(pinctrl)) { + dev_err(ldc->dev, "pinctrl failed err: %ld\n", PTR_ERR(pinctrl)); + return PTR_ERR(pinctrl); + } + + ldc->wq = create_singlethread_workqueue("ldc2114_workqueue"); + init_completion(&ldc->config.done); + INIT_DELAYED_WORK(&ldc->config_work, ldc2114_config_work); + + ret = ldc2114_initialize(ldc); + if (ret) { + dev_err(ldc->dev, "Failed to init: %d\n", ret); + return ret; + } + + ldc->instance = ++drv_instance_counter; + sema_init(&ldc->semaphore, 0); + + if (!ldc->not_connected) { + /* + * Even though we setup edge triggered irq handler, genirq still + * checks for ONESHOT safety if no primary handler provided + */ + ret = devm_request_threaded_irq(ldc->dev, ldc->irq, NULL, ldc2114_irq, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, + LDC2114_DRIVER_NAME, ldc); + if (ret) { + dev_err(ldc->dev, "Failed to register irq handler: %d\n", ret); + return ret; + } + + ldc->irq_enabled = true; + INIT_DELAYED_WORK(&ldc->irq_work, ldc2114_irq_work); + queue_delayed_work(ldc->wq, &ldc->irq_work, + msecs_to_jiffies(LDC2114_SHORT_DELAY)); + } + + if (ldc->btn_enabled) + ldc2114_input_device(ldc); + + ret = ldc2114_debugfs_init(ldc); + if (ret < 0) { + ldc->data_polling = false; + if (ret != -ENODEV) + dev_warn(ldc->dev, "Error registering debugfs: %d\n", ret); + } else { + ldc->poll_interval = 250; + INIT_DELAYED_WORK(&ldc->polling_work, ldc2114_polling_work); + + ldc->poll_nb.notifier_call = ldc2114_poll_enable_cb; + ret = ldc2114_register_client(ldc, &ldc->poll_nb); + if (ret < 0) + dev_warn(ldc->dev, "Unable to register notifier: %d\n", ret); + } + + ret = sysfs_create_group(&ldc->dev->kobj, &ldc2114_attr_group); + if (ret) + dev_err(ldc->dev, "Unable to create sysfs group: %d\n", ret); + + ldc->reboot_nb.notifier_call = ldc2114_reboot_cb; + ldc->reboot_nb.next = NULL; + ldc->reboot_nb.priority = 1; + ret = register_reboot_notifier(&ldc->reboot_nb); + if (ret) + dev_err(ldc->dev, "register for reboot failed: %d\n", ret); + + return 0; +} + +static int ldc2114_remove(struct i2c_client *client) +{ + struct ldc2114_data *ldc = i2c_get_clientdata(client); + + if (ldc->irq_enabled) { + disable_irq(ldc->irq); + cancel_delayed_work_sync(&ldc->irq_work); + } + + if (ldc->data_polling) + cancel_delayed_work_sync(&ldc->polling_work); + + if (ldc->input) + input_unregister_device(ldc->input); + + ldc2114_debugfs_remove(ldc); + + sysfs_remove_group(&client->dev.kobj, &ldc2114_attr_group); + + unregister_reboot_notifier(&ldc->reboot_nb); + + return 0; +} + +static const struct i2c_device_id ldc2114_ids[] = { + { "ldc2114", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, ldc2114_ids); + +static struct i2c_driver ldc2114_i2c_driver = { + .driver = { + .name = LDC2114_DRIVER_NAME, +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(ldc2114_of_match), +#endif + }, + .probe = ldc2114_probe, + .remove = ldc2114_remove, + .id_table = ldc2114_ids, +}; + +static int __init ldc2114_init(void) +{ + return i2c_add_driver(&ldc2114_i2c_driver); +} + +static void __exit ldc2114_exit(void) +{ + i2c_del_driver(&ldc2114_i2c_driver); +} + +module_init(ldc2114_init); +module_exit(ldc2114_exit); + +MODULE_AUTHOR("Andrew F. Davis "); +MODULE_DESCRIPTION("TI LDC2114 Metal Touch Inductance-to-Digital Converter"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/misc/ldc2114_mmi/ldc2114.h b/drivers/input/misc/ldc2114_mmi/ldc2114.h new file mode 100644 index 000000000000..f6a3d47a3b15 --- /dev/null +++ b/drivers/input/misc/ldc2114_mmi/ldc2114.h @@ -0,0 +1,224 @@ +#ifndef _LDC2114_HDR_ +#define _LDC2114_HDR_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_KEYS 4 +#define BUFFER_SIZE 256 + +#define LDC2114_EV_DATA 1 +#define LDC2114_EV_IRQ 2 +#define LDC2114_EV_PRESS 3 + +#define LDC2114_REG_RESET_FULL (0x10) +#define LDC2114_REG_RESET_CONFIG_MODE (0x01) +#define LDC2114_REG_RESET_NONE (0x00) + +#define LDC2114_REG_STATUS_OUT (0x80) +#define LDC2114_REG_STATUS_CHIP_READY (0x40) +#define LDC2114_REG_STATUS_RDY_TO_WRITE (0x20) +#define LDC2114_REG_STATUS_MAXOUT (0x10) +#define LDC2114_REG_STATUS_FSM_WD (0x08) +#define LDC2114_REG_STATUS_LC_WD (0x04) +#define LDC2114_REG_STATUS_TIMEOUT (0x02) +#define LDC2114_REG_STATUS_INTEGRITY (0x01) +#define LDC2114_REG_STATUS_ERROR_MASK (0x0f) +#define LDC2114_REG_STATUS_ERROR_OTHERS (0x1b) /* (ERROR_MASK & ~LC_WD) | MAX_OUT */ + +enum ldc2114_fields { + /* Status */ + F_STATUS_TIMEOUT, F_STATUS_LC_WD, F_STATUS_FSM_WD, F_STATUS_MAXOUT, + F_STATUS_RDY_TO_WRITE, F_STATUS_CHIP_READY, F_STATUS_OUT_STATUS, + + /* Output */ + F_OUT_OUT0, F_OUT_OUT1, F_OUT_OUT2, F_OUT_OUT3, + + /* Reset */ + F_RESET_STATE_RESET, F_RESET_FULL_RESET, + + /* Enable */ + F_EN_EN0, F_EN_EN1, F_EN_EN2, F_EN_EN3, + F_EN_LPEN0, F_EN_LPEN1, F_EN_LPEN2, F_EN_LPEN3, + + /* Scan rate */ + F_NP_SCAN_RATE_NPSR, F_LP_SCAN_RATE_LPSR, + + /* Gain */ + F_GAIN0_GAIN0, F_GAIN1_GAIN1, F_GAIN2_GAIN2, F_GAIN3_GAIN3, + + /* Interrupt polarity */ + F_INTPOL_INTPOL, + + /* Base Increment */ + F_LP_BASE_INC_LPBI, F_NP_BASE_INC_NPBI, + + /* Max-win */ + F_MAXWIN_MAXWIN0, F_MAXWIN_MAXWIN1, F_MAXWIN_MAXWIN2, F_MAXWIN_MAXWIN3, + + /* Frequency divider */ + F_LC_DIVIDER_LCDIV, + + /* Hysteresis */ + F_HYST_HYST, + + /* Anti-twist */ + F_TWIST_ANTITWST, + + /* Anti-deform */ + F_COMMON_DEFORM_ANTIDFRM0, F_COMMON_DEFORM_ANTIDFRM1, + F_COMMON_DEFORM_ANTIDFRM2, F_COMMON_DEFORM_ANTIDFRM3, + + /* Anti-common */ + F_COMMON_DEFORM_ANTICM0, F_COMMON_DEFORM_ANTICM1, + F_COMMON_DEFORM_ANTICM2, F_COMMON_DEFORM_ANTICM3, + + /* Output Polarity */ + F_OPOL_OPOL0, F_OPOL_OPOL1, F_OPOL_OPOL2, F_OPOL_OPOL3, + + /* Counter Scale */ + F_CNTSC_CNTSC0, F_CNTSC_CNTSC1, F_CNTSC_CNTSC2, F_CNTSC_CNTSC3, + + /* Sensor configuration */ + F_SENSOR0_CONFIG_SENCYC0, F_SENSOR0_CONFIG_FREQ0, F_SENSOR0_CONFIG_RP0, + F_SENSOR1_CONFIG_SENCYC1, F_SENSOR1_CONFIG_FREQ1, F_SENSOR1_CONFIG_RP1, + F_SENSOR2_CONFIG_SENCYC2, F_SENSOR2_CONFIG_FREQ2, F_SENSOR2_CONFIG_RP2, + F_SENSOR3_CONFIG_SENCYC3, F_SENSOR3_CONFIG_FREQ3, F_SENSOR3_CONFIG_RP3, + + /* Fast Tracking Factor */ + F_FTF0_FTF0, F_FTF1_2_FTF1, F_FTF1_2_FTF2, F_FTF3_FTF3, + + /* sentinel */ + F_MAX_FIELDS +}; + +struct ldc2114_cfg_data { + uint8_t address, value; +}; + +struct ldc2114_config { + int size; + struct ldc2114_cfg_data data[0]; +}; + +struct ldc2114_cfg_info { + struct ldc2114_config *normal; + struct ldc2114_config *lpm; + struct ldc2114_config *active; + int attempts; + int delay_ms; + bool toggled; + bool reschedulable; + struct completion done; +}; + +struct ldc2114_16bit { + uint8_t lsb; + uint8_t msb; +} __packed; + +struct ldc2114_raw { + uint8_t out; + struct ldc2114_16bit values[MAX_KEYS]; +} __packed; + +struct ldc2114_raw_ext { + uint8_t status; + struct ldc2114_raw data; +} __packed; + +/** + * struct ldc2114_data - Instance data for LDC2114 + * @dev: Device structure + * @regmap - Register map of the device + * @fields: Register fields of the device + * @irq: INTB line interrupt number + */ +struct ldc2114_data { + struct device *dev; + struct regmap *regmap; + struct regmap_field *fields[F_MAX_FIELDS]; + atomic_t irq_work_running; + atomic_t poll_work_running; + struct semaphore semaphore; + int intb_gpio; + int lpwm_gpio; + int signal_gpio; + int intb_polarity; + int irq; + int failures; + int instance; + bool irq_enabled; + bool btn_enabled; + bool data_polling; + bool not_connected; + unsigned int poll_interval; + unsigned int button_map[MAX_KEYS]; + struct delayed_work polling_work; + struct delayed_work irq_work; + struct delayed_work config_work; + struct input_dev *input; + struct notifier_block poll_nb; + struct notifier_block reboot_nb; + unsigned int verno; + struct ldc2114_cfg_info config; + struct workqueue_struct *wq; + + int buttons[MAX_KEYS]; + int output_bits; +}; + +typedef struct data { + int type; + struct ldc2114_raw_ext de; +} item_t; + +struct buffer { + struct mutex mutex; + int capacity; + unsigned long opened; + atomic_t start; + atomic_t end; + atomic_t active; + item_t **data; + wait_queue_head_t wait; +}; + +struct ldc2114_drv { + struct dentry *dentry; + struct device *dev; + char *name; + struct buffer ds; + struct blocking_notifier_head nhead; +}; + +#if defined(CONFIG_DEBUG_FS) +int ldc2114_debugfs_init(struct ldc2114_data *ldc); +void ldc2114_debugfs_remove(struct ldc2114_data *ldc); +int ldc2114_buffer(struct ldc2114_data *ldc, int type, ...); +int ldc2114_register_client(struct ldc2114_data *ldc, struct notifier_block *nb); +#else +static int ldc2114_debugfs_init(struct ldc2114_data *ldc) +{ + return -ENODEV; +} +static void ldc2114_debugfs_remove(struct ldc2114_data *ldc) +{ +} +static int ldc2114_buffer(struct ldc2114_data *ldc, int type, ...) +{ + return 0; +} +static int ldc2114_register_client(struct ldc2114_data *ldc, struct notifier_block *nb) +{ + return -ENODEV; +} +#endif +#endif diff --git a/drivers/input/misc/qpnp_power_on_mmi/Android.mk b/drivers/input/misc/qpnp_power_on_mmi/Android.mk new file mode 100644 index 000000000000..2abb402bcca1 --- /dev/null +++ b/drivers/input/misc/qpnp_power_on_mmi/Android.mk @@ -0,0 +1,10 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := qpnp-power-on-mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS_GKI += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/misc/qpnp_power_on_mmi/Kbuild b/drivers/input/misc/qpnp_power_on_mmi/Kbuild new file mode 100644 index 000000000000..dd859e89187d --- /dev/null +++ b/drivers/input/misc/qpnp_power_on_mmi/Kbuild @@ -0,0 +1,7 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/../../kernel/msm-$(MODULE_KERNEL_VERSION)/Module.symvers + +obj-m += qpnp-power-on-mmi.o + diff --git a/drivers/input/misc/qpnp_power_on_mmi/Makefile b/drivers/input/misc/qpnp_power_on_mmi/Makefile new file mode 100644 index 000000000000..ac93cd77c088 --- /dev/null +++ b/drivers/input/misc/qpnp_power_on_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(M) clean diff --git a/drivers/input/misc/qpnp_power_on_mmi/qpnp-power-on-mmi.c b/drivers/input/misc/qpnp_power_on_mmi/qpnp-power-on-mmi.c new file mode 100644 index 000000000000..34ab794b8cab --- /dev/null +++ b/drivers/input/misc/qpnp_power_on_mmi/qpnp-power-on-mmi.c @@ -0,0 +1,273 @@ +/* Copyright (c) 2012-2018, The Linux Foundation. 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 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if KERNEL_VERSION(5, 15, 0) <= LINUX_VERSION_CODE +#include +#endif + +#define RESET_EXTRA_RESET_KUNPOW_REASON BIT(9) + +struct qpnp_pon_mmi { + struct platform_device *pdev; + struct delayed_work kpd_bark_work; + u32 kpd_bark_irq; + u32 kpd_hw_warmreset; +}; + +static bool is_hw_warmreset_enabled(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool value = false; + const char *bootargs = NULL; + + if (!np) + return false; + + if ((!of_property_read_string(np, "bootargs", &bootargs)) + && (strstr(bootargs, "hw_warmreset_enable=1"))) { + value = true; + } + + of_node_put(np); + + return value; +} + +static bool mmi_kungpow_check(void) +{ + struct device_node *soc = of_find_node_by_path("/soc"); + struct device_node *kungpow = NULL; + bool has_kungpow = false; + if (soc) + kungpow = of_find_node_by_name(soc, "kungpow_default"); + + has_kungpow = (kungpow != NULL); + + of_node_put(kungpow); + of_node_put(soc); + + return has_kungpow; +} + +static unsigned int get_boot_seq(void) +{ + unsigned int boot_seq = 0; + struct device_node *n = of_find_node_by_path("/chosen"); + + if (n == NULL) + return 0; + + of_property_read_u32(n, "mmi,boot_seq", &boot_seq); + of_node_put(n); + + return boot_seq; +} + +#if KERNEL_VERSION(5, 15, 0) > LINUX_VERSION_CODE +static int print_blocked_tasks(void) +{ + mm_segment_t fs; + char cmd; + size_t written; + loff_t pos = 0; + struct file *filep; + int rc = 0; + + fs = get_fs(); + set_fs(KERNEL_DS); + + filep = filp_open("/proc/sysrq-trigger", O_WRONLY, 0200); + if (IS_ERR_OR_NULL(filep)) { + rc = PTR_ERR(filep); + pr_err("opening sysrq errno=%d\n", rc); + set_fs(fs); + return rc; + } + + pr_info("set default console loglevel\n"); + cmd = '7'; +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE + written = kernel_write(filep, &cmd, 1, &pos); +#else + written = vfs_write(filep, &cmd, 1, &pos); +#endif + if (written < 1) { + pr_err("failed to write sysrq\n"); + rc = -EIO; + } + + pr_info("start to print block task\n"); + cmd = 'w'; +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE + written = kernel_write(filep, &cmd, 1, &pos); +#else + written = vfs_write(filep, &cmd, 1, &pos); +#endif + if (written < 1) { + pr_err("failed to write sysrq\n"); + rc = -EIO; + } + + set_fs(fs); + filp_close(filep, NULL); + + return rc; +} +#else +static int print_blocked_tasks(void) +{ + handle_sysrq('7'); + handle_sysrq('w'); + return 0; +} +#endif + +static void kpd_bark_work_func(struct work_struct *work) +{ + struct qpnp_pon_mmi *pon = + container_of(work, struct qpnp_pon_mmi, kpd_bark_work.work); + + if (pon->kpd_hw_warmreset) { + pr_err("trigger hw_warmreset, BOOT_SEQ=%d\n", get_boot_seq()); + /* print blocked tasks */ + print_blocked_tasks(); + kernel_restart("hw_warmreset"); + } else { + dev_err(&pon->pdev->dev, "HW User Reset! 2 sec to Reset!\n"); + qpnp_pon_store_extra_reset_info(RESET_EXTRA_RESET_KUNPOW_REASON, + mmi_kungpow_check() ? 0 : RESET_EXTRA_RESET_KUNPOW_REASON); + qpnp_pon_system_pwr_off(PON_POWER_OFF_HARD_RESET); +#if KERNEL_VERSION(5, 15, 0) > LINUX_VERSION_CODE + kernel_halt(); +#else + kernel_power_off(); +#endif + } +} + +static irqreturn_t qpnp_kpdpwr_bark_irq(int irq, void *_pon) +{ + struct qpnp_pon_mmi *pon = _pon; + + schedule_delayed_work(&pon->kpd_bark_work, msecs_to_jiffies(0)); + + return IRQ_HANDLED; +} + +static int qpnp_pon_mmi_probe(struct platform_device *pdev) +{ + struct qpnp_pon_mmi *pon; + int rc; + + pon = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_pon_mmi), + GFP_KERNEL); + if (!pon) + return -ENOMEM; + + pon->pdev = pdev; + + dev_set_drvdata(&pdev->dev, pon); + + INIT_DELAYED_WORK(&pon->kpd_bark_work, kpd_bark_work_func); + pon->kpd_bark_irq = platform_get_irq_byname(pon->pdev, "kpdpwr-bark"); + if (pon->kpd_bark_irq < 0) { + dev_err(&pon->pdev->dev, + "Unable to get kpdpwr-bark irq\n"); + return -ENODEV; + } + + dev_info(&pon->pdev->dev, "Request %d kpdpwr-bark IRQ\n", + pon->kpd_bark_irq); + rc = devm_request_irq(&pon->pdev->dev, pon->kpd_bark_irq, + qpnp_kpdpwr_bark_irq, + IRQF_TRIGGER_RISING, + "qpnp_kpdpwr_bark", pon); + if (rc < 0) { + dev_err(&pon->pdev->dev, + "Can't request %d IRQ\n", + pon->kpd_bark_irq); + return rc; + } + + if (is_hw_warmreset_enabled()) { + pon->kpd_hw_warmreset = 1; + pr_info("hw_warmreset feature is enabled\n"); + } + + return 0; +} + +static int qpnp_pon_mmi_remove(struct platform_device *pdev) +{ + struct qpnp_pon_mmi *pon = dev_get_drvdata(&pdev->dev); + + cancel_delayed_work_sync(&pon->kpd_bark_work); + + return 0; +} + +static const struct of_device_id spmi_match_table[] = { + { .compatible = "qcom,qpnp-power-on-mmi", }, + {} +}; + +static struct platform_driver qpnp_pon_mmi_driver = { + .driver = { + .name = "qcom,qpnp-power-on-mmi", + .of_match_table = spmi_match_table, + }, + .probe = qpnp_pon_mmi_probe, + .remove = qpnp_pon_mmi_remove, +}; + +static int __init qpnp_pon_mmi_init(void) +{ + return platform_driver_register(&qpnp_pon_mmi_driver); +} +module_init(qpnp_pon_mmi_init); + +static void __exit qpnp_pon_mmi_exit(void) +{ + return platform_driver_unregister(&qpnp_pon_mmi_driver); +} +module_exit(qpnp_pon_mmi_exit); + +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +MODULE_DESCRIPTION("QPNP MMI PMIC POWER-ON driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/misc/rbs_fod_mmi/Android.mk b/drivers/input/misc/rbs_fod_mmi/Android.mk new file mode 100644 index 000000000000..8ed377eaccbd --- /dev/null +++ b/drivers/input/misc/rbs_fod_mmi/Android.mk @@ -0,0 +1,25 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := rbs_fod_mmi.ko +LOCAL_MODULE_TAGS := optional +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +ETS_FPS_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +ETS_FPS_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif +LOCAL_MODULE_PATH := $(ETS_FPS_MMI_MODULE_PATH) + + +ifeq ($(EGIS_FPS_PANEL_NOTIFICATIONS),true) + KERNEL_CFLAGS += CONFIG_EGIS_PANEL_NOTIFICATIONS=y +endif + + +KERNEL_CFLAGS += CONFIG_MMI_RELAY=y + +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_relay.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/misc/rbs_fod_mmi/Kbuild b/drivers/input/misc/rbs_fod_mmi/Kbuild new file mode 100644 index 000000000000..8ad751e0ec30 --- /dev/null +++ b/drivers/input/misc/rbs_fod_mmi/Kbuild @@ -0,0 +1,20 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/rbs_fps_mmi + +ifneq ($(filter m y,$(CONFIG_EGIS_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_PANEL_NOTIFICATIONS +endif + + +ifneq ($(filter m y,$(CONFIG_MMI_RELAY)),) + EXTRA_CFLAGS += -DMMI_RELAY_MODULE +endif + +rbs_fod_mmi-y += etxxx_fp.o + +obj-m += rbs_fod_mmi.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../mmi_relay/$(GKI_OBJ_MODULE_DIR)/Module.symvers + diff --git a/drivers/input/misc/rbs_fod_mmi/Makefile b/drivers/input/misc/rbs_fod_mmi/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/input/misc/rbs_fod_mmi/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/input/misc/rbs_fod_mmi/etxxx_fp.c b/drivers/input/misc/rbs_fod_mmi/etxxx_fp.c new file mode 100644 index 000000000000..b4e4068acd8b --- /dev/null +++ b/drivers/input/misc/rbs_fod_mmi/etxxx_fp.c @@ -0,0 +1,1499 @@ +/* + * Simple synchronous userspace interface to SPI devices + * + * Copyright (C) 2006 SWAPP + * Andrea Paterniani + * Copyright (C) 2007 David Brownell (simplification, cleanup) + * + * 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. + * + */ + +/* +* +* etxxx_fp.c +* Date: 2016/03/16 +* Version: 0.9.0.1 +* Revise Date: 2020/11/25 +* Copyright (C) 2007-2019 Egis Technology Inc. +* ----------------- version history ------------------------ +* +*Kevin.Liang 20181102 add powersetup for IOC +*Jacob.Kung 20201125 modify for support all sensor +*Jacob.Kung 20201126 modify struct define naming +*Jacob.Kung 20201130 add check_ioctl_permission +*Jacob.Kung 20201207 register platform driver with MTK platform +*Jacob.Kung 20210331 add register FB notifier +*Jacob.Kung 20210512 add register DRM notifier +* ----------------------------------------------------------- +* +**/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "etxxx_fp.h" +#include +#ifdef MMI_RELAY_MODULE +#include +#endif +#ifdef MMI_RELAY_MODULE +struct FPS_data { + unsigned int enabled; + unsigned int state; + struct notifier_block relay_notif; +} *fpsData= NULL; +uint32_t g_fps_mode =0; +#endif + +#if defined(CONFIG_PANEL_NOTIFICATIONS) +#include +#define FP_NOTIFY_ON PANEL_EVENT_DISPLAY_ON +#define FP_NOTIFY_OFF PANEL_EVENT_DISPLAY_OFF +#define FP_NOTIFY_EVENT_BLANK PANEL_EVENT_DISPLAY_OFF //MSM_DRM_EVENT_BLANK +#define ets_fb_register_client(client) panel_register_notifier(client); +#define ets_fb_unregister_client(client) panel_unregister_notifier(client); +#elif defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +#include +#else +#include +#define FP_NOTIFY_ON FB_BLANK_UNBLANK +#define FP_NOTIFY_OFF FB_BLANK_POWERDOWN +#define FP_NOTIFY_EVENT_BLANK FB_EVENT_BLANK +#define ets_fb_register_client(client) fb_register_client(client); +#define ets_fb_unregister_client(client) fb_unregister_client(client) +#endif + +struct egisfp_dev_t *g_data = NULL; +DECLARE_BITMAP(minors, N_SPI_MINORS); +LIST_HEAD(device_list); +DEFINE_MUTEX(device_list_lock); + +static struct egis_key_map_t key_maps[] = { + {EV_KEY, EGIS_NAV_INPUT_UP}, + {EV_KEY, EGIS_NAV_INPUT_DOWN}, + {EV_KEY, EGIS_NAV_INPUT_LEFT}, + {EV_KEY, EGIS_NAV_INPUT_RIGHT}, + {EV_KEY, EGIS_NAV_INPUT_CLICK}, + {EV_KEY, EGIS_NAV_INPUT_DOUBLE_CLICK}, + {EV_KEY, EGIS_NAV_INPUT_LONG_PRESS}, + {EV_KEY, EGIS_NAV_INPUT_FINGER_DOWN}, + {EV_KEY, EGIS_NAV_INPUT_FINGER_UP}, +}; + +int egisfp_probe(struct platform_device *pdev); +int egisfp_remove(struct platform_device *pdev); + +/* -------------------------------------------------------------------- */ + +struct of_device_id egistec_match_table[] = { + { + .compatible = "fp-egistec", + }, + {}, +}; + +static struct platform_driver egisfp_driver = { + .driver = { + .name = EGIS_DEV_NAME, + .owner = THIS_MODULE, + .of_match_table = egistec_match_table, + }, + .probe = egisfp_probe, + .remove = egisfp_remove, +}; + +/* add for clk enable from kernel*/ + + +/* add for clk enable from kernel*/ + +/* ------------------------------ Interrupt -----------------------------*/ + +static DECLARE_WAIT_QUEUE_HEAD(interrupt_waitq); + +#ifdef MMI_RELAY_MODULE +static int fps_mmi_relay_cb(struct notifier_block *self, + unsigned long event, void *p) +{ + struct FPS_data *mdata = container_of( + self, struct FPS_data, relay_notif); + + if (!mdata) + return -ENODEV; + + if (event == RELAY_NOTIFY_REGISTER) { + int state = mdata->state; + mdata->enabled = 1; + /* send current FPS state on register request */ + relay_notifier_fire(BLOCKING, FPS, 0xBEEF, (void *)&state); + pr_info("%s: FPS reported state %d\n", __func__, state); + + } else if (event == RELAY_NOTIFY_UNREGISTER) + mdata->enabled = 0; + + return 0; +} + + +static struct FPS_data *FPS_init() +{ + int ret =0; + struct FPS_data *mdata = kzalloc(sizeof(struct FPS_data), GFP_KERNEL); + mdata = kzalloc(sizeof(struct egisfp_dev_t), GFP_KERNEL); + if (mdata == NULL) + { + ERROR_PRINT(" %s : Failed to FPS_init \n", __func__); + return NULL; + } + mdata->relay_notif.notifier_call = fps_mmi_relay_cb; + /* register a blocking notification to receive notify form MMI_RELAY dev*/ + ret = relay_register_action(BLOCKING, MMI_RELAY, &mdata->relay_notif); + INFO_PRINT("%s: relay_register_action ret= %d\n", __func__, ret); + return mdata; +} + +void FPS_notify(unsigned long stype, int state) +{ + struct FPS_data *mdata = fpsData; + int ret =0; + INFO_PRINT("%s: Enter", __func__); + + + if (!mdata) { + INFO_PRINT("%s: FPS notifier not initialized yet\n", __func__); + return; + } + + INFO_PRINT("%s: FPS current state %d -> (%d)\n", __func__, + mdata->state, state); + + if (mdata->state != state) { + mdata->state = state; + ret= relay_notifier_fire(BLOCKING, FPS, 0xBEEF, (void *)&state); + INFO_PRINT("%s: FPS notification sent ret= %d\n", __func__, ret); + } else + INFO_PRINT("%s: mdata->state==state", __func__); +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) +void egisfp_interrupt_timer_call(struct timer_list *t) +{ + struct egisfp_interrupt_desc_t *fps_int = from_timer(fps_int, t, timer); + struct egisfp_dev_t *egis_dev; + + egis_dev = container_of(fps_int, struct egisfp_dev_t, fps_ints); + INFO_PRINT(" %s : interrupt count = %d \n", __func__, egis_dev->fps_ints.int_count); + + if (egis_dev->fps_ints.int_count >= egis_dev->fps_ints.detect_threshold) + { + egis_dev->fps_ints.finger_on = 1; + INFO_PRINT(" %s : interrupt triggered \n", __func__); + } + else + { + INFO_PRINT(" %s : interrupt not triggered \n", __func__); + } + egis_dev->fps_ints.int_count = 0; + wake_up_interruptible(&interrupt_waitq); +} +#else +void egisfp_interrupt_timer_call(unsigned long _data) +{ + struct egisfp_interrupt_desc_t *fps_int = (struct egisfp_interrupt_desc_t *)_data; + struct egisfp_dev_t *egis_dev; + + egis_dev = container_of(fps_int, struct egisfp_dev_t, fps_ints); + INFO_PRINT(" %s : interrupt count = %d \n", __func__, egis_dev->fps_ints.int_count); + + if (egis_dev->fps_ints.int_count >= egis_dev->fps_ints.detect_threshold) + { + egis_dev->fps_ints.finger_on = 1; + INFO_PRINT(" %s : interrupt triggered \n", __func__); + } + else + { + INFO_PRINT(" %s : interrupt not triggered \n", __func__); + } + egis_dev->fps_ints.int_count = 0; + wake_up_interruptible(&interrupt_waitq); +} +#endif +irqreturn_t fp_eint_func(int irq, void *dev_id) +{ + struct egisfp_dev_t *egis_dev = dev_id; + if (!egis_dev->fps_ints.int_count) + mod_timer(&egis_dev->fps_ints.timer, jiffies + msecs_to_jiffies(egis_dev->fps_ints.detect_period)); + egis_dev->fps_ints.int_count++; + INFO_PRINT(" %s : fps_ints.int_count=%d \n", __func__, egis_dev->fps_ints.int_count); + pm_wakeup_event(&egis_dev->dd->dev, WAKE_HOLD_TIME); + return IRQ_HANDLED; +} + +irqreturn_t fp_eint_func_ll(int irq, void *dev_id) +{ + struct egisfp_dev_t *egis_dev = dev_id; + unsigned long irqflags; + //INFO_PRINT(" %s \n", __func__); + spin_lock_irqsave(&egis_dev->irq_lock, irqflags); + if (egis_dev->fps_ints.drdy_irq_flag == DRDY_IRQ_ENABLE) + { + egis_dev->fps_ints.finger_on = 1; + disable_irq_nosync(egis_dev->gpio_irq); + //INFO_PRINT(" %s : DRDY_IRQ_DISABLE \n", __func__); + egis_dev->fps_ints.drdy_irq_flag = DRDY_IRQ_DISABLE; + } + spin_unlock_irqrestore(&egis_dev->irq_lock, irqflags); + wake_up_interruptible(&interrupt_waitq); + pm_wakeup_event(&egis_dev->dd->dev, WAKE_HOLD_TIME); + + return IRQ_RETVAL(IRQ_HANDLED); +} + +/* + * FUNCTION NAME. + * egisfp_interrupt_init + * + * FUNCTIONAL DESCRIPTION. + * button initial routine + * + * ENTRY PARAMETERS. + * int_mode - determine trigger mode + * EDGE_TRIGGER_FALLING 0x0 + * EDGE_TRIGGER_RAISING 0x1 + * LEVEL_TRIGGER_LOW 0x2 + * LEVEL_TRIGGER_HIGH 0x3 + * + * EXIT PARAMETERS. + * Function Return int + */ + +int egisfp_interrupt_init(struct egisfp_dev_t *egis_dev, int int_mode, int detect_period, int detect_threshold) +{ + + int err = 0; + unsigned long irqflags; + + INFO_PRINT(" %s : mode = %d period = %d threshold = %d \n", __func__, int_mode, detect_period, detect_threshold); + INFO_PRINT(" %s : request_irq_done = %d gpio_irq = %d pin = %d \n", __func__, egis_dev->request_irq_done, egis_dev->gpio_irq, egis_dev->irqPin); + + egis_dev->fps_ints.detect_period = detect_period; + egis_dev->fps_ints.detect_threshold = detect_threshold; + egis_dev->fps_ints.int_count = 0; + egis_dev->fps_ints.finger_on = 0; + egis_dev->fps_ints.drdy_irq_abort = 0; + + if (egis_dev->request_irq_done == 0) + { + if (gpio_is_valid(egis_dev->irqPin)) + { + egis_dev->gpio_irq = gpio_to_irq(egis_dev->irqPin); + INFO_PRINT(" %s : fp_irq number %d \n", __func__, egis_dev->gpio_irq); + } + else + { + ERROR_PRINT(" %s : irqPin is not valid \n", __func__); + return -EIO; + } + + if (egis_dev->gpio_irq < 0) + { + ERROR_PRINT(" %s : gpio_to_irq failed \n", __func__); + return -EIO; + } + + INFO_PRINT(" %s : current flag : %d disable: %d enable: %d \n", __func__, egis_dev->fps_ints.drdy_irq_flag, DRDY_IRQ_DISABLE, DRDY_IRQ_ENABLE); + + if (int_mode == EDGE_TRIGGER_RISING) + { + INFO_PRINT(" %s : EDGE_TRIGGER_RISING \n", __func__); + err = request_irq(egis_dev->gpio_irq, fp_eint_func, IRQ_TYPE_EDGE_RISING, "fp_detect-eint", egis_dev); + } + else if (int_mode == EDGE_TRIGGER_FALLING) + { + INFO_PRINT(" %s : EDGE_TRIGGER_FALLING \n", __func__); + err = request_irq(egis_dev->gpio_irq, fp_eint_func, IRQ_TYPE_EDGE_FALLING, "fp_detect-eint", egis_dev); + } + else if (int_mode == LEVEL_TRIGGER_LOW) + { + INFO_PRINT(" %s : LEVEL_TRIGGER_LOW \n", __func__); + err = request_irq(egis_dev->gpio_irq, fp_eint_func_ll, IRQ_TYPE_LEVEL_LOW, "fp_detect-eint", egis_dev); + } + else if (int_mode == LEVEL_TRIGGER_HIGH) + { + INFO_PRINT(" %s : LEVEL_TRIGGER_HIGH \n", __func__); + err = request_irq(egis_dev->gpio_irq, fp_eint_func_ll, IRQ_TYPE_LEVEL_HIGH, "fp_detect-eint", egis_dev); + } + + if (err) + { + ERROR_PRINT(" %s : request_irq failed return %d \n", __func__, err); + return err; + } + + disable_irq_nosync(egis_dev->gpio_irq); + INFO_PRINT(" %s : request_irq return %d \n", __func__, err); + egis_dev->request_irq_done = 1; + } + INFO_PRINT(" %s : current flag : %d disable: %d enable: %d \n", __func__, egis_dev->fps_ints.drdy_irq_flag, DRDY_IRQ_DISABLE, DRDY_IRQ_ENABLE); + spin_lock_irqsave(&egis_dev->irq_lock, irqflags); + if (egis_dev->fps_ints.drdy_irq_flag == DRDY_IRQ_DISABLE) + { + egis_dev->fps_ints.drdy_irq_flag = DRDY_IRQ_ENABLE; + INFO_PRINT(" %s : enable irq with mode = %d \n", __func__, int_mode); + enable_irq_wake(egis_dev->gpio_irq); + enable_irq(egis_dev->gpio_irq); + } + spin_unlock_irqrestore(&egis_dev->irq_lock, irqflags); + + return err; +} + +/* + * FUNCTION NAME. + * egisfp_interrupt_free + * + * FUNCTIONAL DESCRIPTION. + * free all interrupt resource + * + * EXIT PARAMETERS. + * Function Return int + */ + +int egisfp_interrupt_free(struct egisfp_dev_t *egis_dev) +{ + unsigned long irqflags; + DEBUG_PRINT(" %s \n", __func__); + spin_lock_irqsave(&egis_dev->irq_lock, irqflags); + egis_dev->fps_ints.finger_on = 0; + if (egis_dev->fps_ints.drdy_irq_flag == DRDY_IRQ_ENABLE) + { + DEBUG_PRINT(" %s : irq disable \n", __func__); + disable_irq_nosync(egis_dev->gpio_irq); + del_timer_sync(&egis_dev->fps_ints.timer); + egis_dev->fps_ints.drdy_irq_flag = DRDY_IRQ_DISABLE; + } + spin_unlock_irqrestore(&egis_dev->irq_lock, irqflags); + return 0; +} + +/* + * FUNCTION NAME. + * fps_interrupt_re d + * + * FUNCTIONAL DESCRIPTION. + * FPS interrupt read status + * + * ENTRY PARAMETERS. + * wait poll table structure + * + * EXIT PARAMETERS. + * Function Return int + */ + +unsigned int egisfp_interrupt_poll(struct file *file, struct poll_table_struct *wait) +{ + unsigned int mask = 0; + struct egisfp_dev_t *egis_dev; + egis_dev = file->private_data; + poll_wait(file, &interrupt_waitq, wait); + if (egis_dev->fps_ints.finger_on) + { + mask |= POLLIN | POLLRDNORM; + } + else if (egis_dev->fps_ints.drdy_irq_abort == 1) + { + mask |= 0x400 | POLLRDNORM; + egis_dev->fps_ints.drdy_irq_abort = 0; + } + return mask; +} + +void egisfp_interrupt_abort(struct egisfp_dev_t *egis_dev) +{ + DEBUG_PRINT(" %s \n", __func__); + egis_dev->fps_ints.finger_on = 0; + egis_dev->fps_ints.drdy_irq_abort = 1; + wake_up_interruptible(&interrupt_waitq); +} + +/*-------------------------------------------------------------------------*/ + +static void send_navi_event(struct egisfp_dev_t *egis_dev, int nav_event) +{ + uint32_t input_event; + + switch (nav_event) + { + case NAVI_EVENT_ON: + DEBUG_PRINT(" %s : finger down \n", __func__); + input_event = EGIS_NAV_INPUT_FINGER_DOWN; + break; + case NAVI_EVENT_OFF: + DEBUG_PRINT(" %s : finger up \n", __func__); + input_event = EGIS_NAV_INPUT_FINGER_UP; + break; + case NAVI_EVENT_UP: + DEBUG_PRINT(" %s : nav swip up \n", __func__); + input_event = EGIS_NAV_INPUT_UP; + break; + case NAVI_EVENT_DOWN: + DEBUG_PRINT(" %s : nav swip down \n", __func__); + input_event = EGIS_NAV_INPUT_DOWN; + break; + case NAVI_EVENT_LEFT: + DEBUG_PRINT(" %s : nav swip left \n", __func__); + input_event = EGIS_NAV_INPUT_LEFT; + break; + case NAVI_EVENT_RIGHT: + DEBUG_PRINT(" %s : nav swip right \n", __func__); + input_event = EGIS_NAV_INPUT_RIGHT; + break; + case NAVI_EVENT_CLICK: + DEBUG_PRINT(" %s : nav finger click \n", __func__); + input_event = EGIS_NAV_INPUT_CLICK; + break; + case NAVI_EVENT_DOUBLE_CLICK: + DEBUG_PRINT(" %s : nav finger double click \n", __func__); + input_event = EGIS_NAV_INPUT_DOUBLE_CLICK; + break; + case NAVI_EVENT_LONG_PRESS: + DEBUG_PRINT(" %s : nav finger long press \n", __func__); + input_event = EGIS_NAV_INPUT_LONG_PRESS; + break; + default: + DEBUG_PRINT(" %s : unknown nav event: %d \n", __func__, nav_event); + input_event = 0; + break; + } + + if (input_event) + { + input_report_key(egis_dev->input_dev, input_event, 1); + input_sync(egis_dev->input_dev); + input_report_key(egis_dev->input_dev, input_event, 0); + input_sync(egis_dev->input_dev); + } +} + +int do_egisfp_reset(struct egisfp_dev_t *egis_dev) +{ + int ret = 0; + ret |= pinctrl_select_state(egis_dev->pinctrl, egis_dev->reset_low); + mdelay(Rst_off_delay); + ret |= pinctrl_select_state(egis_dev->pinctrl, egis_dev->reset_high); + mdelay(Rst_on_delay); + if (ret) + ERROR_PRINT(" %s : failed ret = %d \n", __func__, ret); + else + DEBUG_PRINT(" %s : reset_pin = %d \n", __func__, gpio_get_value(egis_dev->rstPin)); + return ret; +} + +int egisfp_set_screen_onoff(int on) +{ + DEBUG_PRINT(" %s \n", __func__); + + if (!g_data) + return -ENXIO; + + g_data->screen_onoff = on; + + DEBUG_PRINT(" %s screen_onoff %d \n", __func__, g_data->screen_onoff); + + return 0; +} +EXPORT_SYMBOL(egisfp_set_screen_onoff); + +int do_egisfp_power_onoff(struct egisfp_dev_t *egis_dev, struct egisfp_ioctl_cmd_t *ioctl_data) +{ + int ret = 0; + + uint32_t power_onoff = ioctl_data->int_mode; + + + if (!egis_dev->ctrl_power) + { + INFO_PRINT(" %s : power can not control by driver \n", __func__); + return ret; + } + + DEBUG_PRINT(" %s : power_onoff = %d \n", __func__, power_onoff); + + if (egis_dev->power_enable == power_onoff) + { + INFO_PRINT(" %s : duplicate set power on/off \n", __func__); + return ret; + } + + if (power_onoff) + { + if (egis_dev->pwr_by_gpio) + ret = pinctrl_select_state(egis_dev->pinctrl, egis_dev->vcc_high); + else + ret = regulator_enable(egis_dev->vcc); + + if (ret) + goto do_egisfp_power_onoff_failed; + + mdelay(Tpwr_on_delay); + egis_dev->power_enable = 1; + } + else + { + if (egis_dev->pwr_by_gpio) + ret = pinctrl_select_state(egis_dev->pinctrl, egis_dev->vcc_low); + else + ret = regulator_disable(egis_dev->vcc); + + if (ret) + goto do_egisfp_power_onoff_failed; + mdelay(Tpwr_off_delay); + egis_dev->power_enable = 0; + } + if (egis_dev->pwr_by_gpio) + DEBUG_PRINT(" %s : power_pin value = %d \n", __func__, gpio_get_value(egis_dev->vcc_33v_Pin)); + else + DEBUG_PRINT(" %s : regulator enable = %d output voltage %d \n", __func__, regulator_is_enabled(egis_dev->vcc), regulator_get_voltage(egis_dev->vcc)); + + return ret; + +do_egisfp_power_onoff_failed: + ERROR_PRINT(" %s : failed ret = %d \n", __func__, ret); + return ret; +} +int do_egisfp_reset_set(struct egisfp_dev_t *egis_dev, int reset_high_low) +{ + int ret = 0; + DEBUG_PRINT(" %s : reset_high_low = %d \n", __func__, reset_high_low); + + if (reset_high_low) + { + ret = pinctrl_select_state(egis_dev->pinctrl, egis_dev->reset_high); + } + else + { + ret = pinctrl_select_state(egis_dev->pinctrl, egis_dev->reset_low); + } + + if (ret) + ERROR_PRINT(" %s : failed ret = %d \n", __func__, ret); + else + DEBUG_PRINT(" %s : reset_pin value = %d \n", __func__, gpio_get_value(egis_dev->rstPin)); + return ret; +} + +static void egis_get_io_stus(struct egisfp_dev_t *egis_dev) +{ + INFO_PRINT(" %s \n", __func__); + if (egis_dev->ctrl_power) + { + if (egis_dev->pwr_by_gpio) + INFO_PRINT(" %s : power_pin value = %d \n", __func__, gpio_get_value(egis_dev->vcc_33v_Pin)); + else + INFO_PRINT(" %s : regulator vcc enable = %d \n", __func__, regulator_is_enabled(egis_dev->vcc)); + } + + INFO_PRINT(" %s : reset_pin value = %d irq_pin value = %d \n", __func__, gpio_get_value(egis_dev->rstPin), gpio_get_value(egis_dev->irqPin)); +} + + +long egisfp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int retval = 0; + struct egisfp_dev_t *egis_dev = NULL; + struct egisfp_ioctl_cmd_t data = {0}; + + INFO_PRINT(" %s : cmd = 0x%X \n", __func__, cmd); + egis_dev = (struct egisfp_dev_t *)filp->private_data; + + if (!egis_dev->pars_dtsi_done) + { + ERROR_PRINT(" %s : egis_dev is NULL \n", __func__); + return -ENODEV; + } + + if (egisfp_check_ioctl_permission(egis_dev, cmd)) + return -EACCES; + + switch (cmd) + { + case FP_GET_RESOURCE: + DEBUG_PRINT(" %s : FP_GET_RESOURCE \n", __func__); + if (egisfp_platforminit(egis_dev)) + retval = -EIO; + break; + case FP_FREE_RESOURCE: + DEBUG_PRINT(" %s : FP_FREE_RESOURCE \n", __func__); + egisfp_platformfree(egis_dev); + break; + case FP_SET_SPI_CLOCK: + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + return -EFAULT; + } + egis_dev->clk_speed = data.int_mode * 1000000; + DEBUG_PRINT(" %s : FP_SET_SPI_CLOCK %d \n", __func__, egis_dev->clk_speed); + break; + case INT_TRIGGER_INIT: + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + return -EFAULT; + } + retval = egisfp_interrupt_init(egis_dev, data.int_mode, data.detect_period, data.detect_threshold); + DEBUG_PRINT(" %s : INT_TRIGGER_INIT %x \n", __func__, retval); + break; + case FP_SENSOR_RESET: + DEBUG_PRINT(" %s : FP_SENSOR_RESET \n", __func__); + do_egisfp_reset(egis_dev); + break; + case FP_POWER_ONOFF: + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + return -EFAULT; + } + DEBUG_PRINT(" %s : FP_POWER_ONOFF \n", __func__); + retval = do_egisfp_power_onoff(egis_dev, &data); + break; + case FP_RESET_SET: + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + return -EFAULT; + } + DEBUG_PRINT(" %s : FP_RESET_SET \n", __func__); + retval = do_egisfp_reset_set(egis_dev, data.int_mode); // Use data.int_mode as reset setting. 1 = on, 0 = off. + break; + case FP_WAKELOCK_ENABLE: + DEBUG_PRINT(" %s : FP_WAKELOCK_ENABLE \n", __func__); + pm_stay_awake(&egis_dev->dd->dev); + break; + case FP_WAKELOCK_DISABLE: + DEBUG_PRINT(" %s : FP_WAKELOCK_DISABLE \n", __func__); + pm_relax(&egis_dev->dd->dev); + break; + case GET_IO_STUS: + DEBUG_PRINT(" %s : GET_IO_STUS \n", __func__); + egis_get_io_stus(egis_dev); + break; + case SEND_NAVI_EVENT: + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + return -EFAULT; + } + DEBUG_PRINT(" %s : SEND_NAVI_EVENT \n", __func__); + send_navi_event(egis_dev, data.int_mode); + break; + case INT_TRIGGER_CLOSE: + retval = egisfp_interrupt_free(egis_dev); + DEBUG_PRINT(" %s : INT_TRIGGER_CLOSE %d \n", __func__, retval); + break; + case INT_TRIGGER_ABORT: + DEBUG_PRINT(" %s : INT_TRIGGER_ABORT \n", __func__); + egisfp_interrupt_abort(egis_dev); + break; + case FP_FREE_GPIO: + DEBUG_PRINT(" %s : FP_FREE_GPIO \n", __func__); + break; + case FP_WAKELOCK_TIMEOUT_ENABLE: //0Xb1 + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + data.int_mode = WAKE_HOLD_TIME; + } + if (data.int_mode == 0) + data.int_mode = WAKE_HOLD_TIME; + + DEBUG_PRINT(" %s : FP_WAKELOCK_TIMEOUT_ENABLE %d ms \n", __func__, data.int_mode); + pm_wakeup_event(&egis_dev->dd->dev, data.int_mode); + break; + case FP_WAKELOCK_TIMEOUT_DISABLE: //0Xb2 + DEBUG_PRINT(" %s : FP_WAKELOCK_TIMEOUT_DISABLE \n", __func__); + break; + case DELETE_DEVICE_NODE: + DEBUG_PRINT(" %s : DELETE_DEVICE_NODE \n", __func__); + //delete_device_node(); + break; + case GET_SCREEN_ONOFF: + DEBUG_PRINT(" %s : GET_SCREEN_ONOFF \n", __func__); + data.int_mode = egis_dev->screen_onoff; + if (copy_to_user((int __user *)arg, &data, sizeof(data))) + { + return -EFAULT; + } + break; + case FP_SET_FPS_MODE: +#ifdef MMI_RELAY_MODULE + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + return -EFAULT; + } + g_fps_mode = data.int_mode; + DEBUG_PRINT(" %s : FP_SET_FPS_MODE to %d \n", __func__,g_fps_mode); + if(g_fps_mode < 5) { + FPS_notify(0xbeef, (g_fps_mode > 0)? 1: 0); + } +#endif + break; + default: + retval = 0; + break; + } + DEBUG_PRINT(" %s done \n", __func__); + return retval; +} + +#define egisfp_compat_ioctl NULL + +int egisfp_open(struct inode *inode, struct file *filp) +{ + struct egisfp_dev_t *egis_dev = NULL; + int status = -ENXIO; + DEBUG_PRINT(" %s \n", __func__); + + mutex_lock(&device_list_lock); + list_for_each_entry(egis_dev, &device_list, device_entry) + { + if (egis_dev->devt == inode->i_rdev) + { + status = 0; + break; + } + } + + if (status == 0) + { + /* device tree call */ + if (egis_dev->dd->dev.of_node && !egis_dev->pars_dtsi_done) + { + status = egisfp_parse_dt(egis_dev); + } + + if (!status) + { + egis_dev->pars_dtsi_done = 1; + egis_dev->users++; + filp->private_data = egis_dev; + nonseekable_open(inode, filp); + INFO_PRINT(" %s : open ioctl user count = %d \n", __func__, egis_dev->users); + } + else + { + ERROR_PRINT(" %s : egisfp_parse_dt fail, status = %d \n", __func__, status); + } + } + else + { + ERROR_PRINT(" %s : nothing for minor %d \n", __func__, iminor(inode)); + } + mutex_unlock(&device_list_lock); + return status; +} + +int egisfp_release(struct inode *inode, struct file *filp) +{ + struct egisfp_dev_t *egis_dev; + DEBUG_PRINT(" %s \n", __func__); + mutex_lock(&device_list_lock); + egis_dev = filp->private_data; + filp->private_data = NULL; + /* last close? */ + egis_dev->users--; + if (egis_dev->users == 0) + { + INFO_PRINT(" %s : egis_dev->users == %d \n", __func__, egis_dev->users); + egisfp_platformfree(egis_dev); + } + mutex_unlock(&device_list_lock); + return 0; +} +int egisfp_platformfree(struct egisfp_dev_t *egis_dev) +{ + int status = 0; + struct egisfp_ioctl_cmd_t ioctl_data = {0}; + DEBUG_PRINT(" %s : enter \n", __func__); + if (egis_dev->platforminit_done != 1) + return status; + if (egis_dev != NULL) + { + if (egis_dev->request_irq_done == 1) + { + if (egis_dev->fps_ints.drdy_irq_flag == DRDY_IRQ_ENABLE) + { + disable_irq_nosync(egis_dev->gpio_irq); + egis_dev->fps_ints.drdy_irq_flag = DRDY_IRQ_DISABLE; + } + free_irq(egis_dev->gpio_irq, egis_dev); + egis_dev->request_irq_done = 0; + } + status = do_egisfp_reset_set(egis_dev, 0); + + if (egis_dev->power_enable && egis_dev->ctrl_power) + status = do_egisfp_power_onoff(egis_dev, &ioctl_data); + + gpio_free(egis_dev->irqPin); + gpio_free(egis_dev->rstPin); + if (egis_dev->pwr_by_gpio) + gpio_free(egis_dev->vcc_33v_Pin); + + if (egis_dev->vcc) + { + DEBUG_PRINT(" %s : devm_regulator_put \n", __func__); + devm_regulator_put(egis_dev->vcc); + egis_dev->vcc = NULL; + } + + + if (egis_dev->pinctrl) + { + DEBUG_PRINT(" %s : devm_pinctrl_put \n", __func__); + devm_pinctrl_put(egis_dev->pinctrl); + egis_dev->pinctrl = NULL; + } + } + egis_dev->platforminit_done = 0; + DEBUG_PRINT(" %s : successful status = %d \n", __func__, status); + return status; +} + +int egisfp_platforminit(struct egisfp_dev_t *egis_dev) +{ + int status; + INFO_PRINT(" %s : Version %s \n", __func__, DRIVER_VERSION); + if (egis_dev != NULL) + { + if (!egis_dev->platforminit_done) + { + if (egis_dev->ctrl_power) + { + if (egis_dev->pwr_by_gpio) + { + /* initial 33V power pin */ + status = gpio_request(egis_dev->vcc_33v_Pin, "egis_dev-33v-gpio"); + if (status < 0) + { + ERROR_PRINT(" %s : gpio_requset vcc_33v_Pin pin failed \n", __func__); + goto egisfp_power_request_fail; + } + + status = gpio_direction_output(egis_dev->vcc_33v_Pin, 0); + } + else + { + egis_dev->vcc = devm_regulator_get(&egis_dev->dd->dev, "vcc_fp"); + if (IS_ERR(egis_dev->vcc)) + { + status = PTR_ERR(egis_dev->vcc); + ERROR_PRINT(" %s : get vcc regulator failed %d \n", __func__, status); + goto egisfp_power_request_fail; + } + + if (regulator_count_voltages(egis_dev->vcc) > 0) + { + status = regulator_set_voltage(egis_dev->vcc, egis_dev->regulator_voltage_min, egis_dev->regulator_voltage_max); + if (status) + { + ERROR_PRINT(" %s : set vcc regulator voltage failed %d \n", __func__, status); + goto egisfp_power_setup_fail; + } + + status = regulator_set_load(egis_dev->vcc, egis_dev->regulator_current); + if (status) + { + ERROR_PRINT(" %s : set vcc regulator current failed %d \n", __func__, status); + goto egisfp_power_setup_fail; + } + } + } + } + + /* Initial Reset Pin */ + status = gpio_request(egis_dev->rstPin, "egis_dev-reset-gpio"); + if (status < 0) + { + ERROR_PRINT(" %s : gpio_requset reset pin failed \n", __func__); + goto egisfp_reset_request_fail; + } + + status = gpio_direction_output(egis_dev->rstPin, 0); + + status = gpio_request(egis_dev->irqPin, "egis_dev-irq-gpio"); + if (status < 0) + { + ERROR_PRINT(" %s : gpio_requset irq pin failed \n", __func__); + goto egisfp_irq_request_fail; + } + + gpio_direction_input(egis_dev->irqPin); + + if (egis_dev->dd) + { + INFO_PRINT(" %s : find node enter \n", __func__); + egis_dev->pinctrl = devm_pinctrl_get(&egis_dev->dd->dev); + if (IS_ERR(egis_dev->pinctrl)) + { + status = PTR_ERR(egis_dev->pinctrl); + ERROR_PRINT(" %s : can't find fingerprint pinctrl \n", __func__); + goto egisfp_pinctrl_fail; + } + egis_dev->reset_high = pinctrl_lookup_state(egis_dev->pinctrl, "egis_rst_high"); + if (IS_ERR(egis_dev->reset_high)) + { + status = PTR_ERR(egis_dev->reset_high); + ERROR_PRINT(" %s : can't find fingerprint pinctrl egis_rst_high \n", __func__); + goto egisfp_pinctrl_fail; + } + egis_dev->reset_low = pinctrl_lookup_state(egis_dev->pinctrl, "egis_rst_low"); + if (IS_ERR(egis_dev->reset_low)) + { + status = PTR_ERR(egis_dev->reset_low); + ERROR_PRINT(" %s : can't find fingerprint pinctrl egis_rst_low \n", __func__); + goto egisfp_pinctrl_fail; + } + if (pinctrl_select_state(egis_dev->pinctrl, egis_dev->reset_low)) + goto egisfp_pinctrl_fail; + egis_dev->irq_active = pinctrl_lookup_state(egis_dev->pinctrl, "egis_irq_active"); + if (IS_ERR(egis_dev->irq_active)) + { + status = PTR_ERR(egis_dev->irq_active); + ERROR_PRINT(" %s : can't find fingerprint pinctrl egis_irq_active \n", __func__); + goto egisfp_pinctrl_fail; + } + if (pinctrl_select_state(egis_dev->pinctrl, egis_dev->irq_active)) + goto egisfp_pinctrl_fail; + if (egis_dev->pwr_by_gpio && egis_dev->ctrl_power) + { + egis_dev->vcc_high = pinctrl_lookup_state(egis_dev->pinctrl, "egis_vcc_high"); + if (IS_ERR(egis_dev->vcc_high)) + { + status = PTR_ERR(egis_dev->vcc_high); + ERROR_PRINT(" %s : can't find fingerprint pinctrl egis_vcc_high \n", __func__); + goto egisfp_pinctrl_fail; + } + egis_dev->vcc_low = pinctrl_lookup_state(egis_dev->pinctrl, "egis_vcc_low"); + if (IS_ERR(egis_dev->vcc_low)) + { + status = PTR_ERR(egis_dev->vcc_low); + ERROR_PRINT(" %s : can't find fingerprint pinctrl egis_vcc_low \n", __func__); + goto egisfp_pinctrl_fail; + } + if (pinctrl_select_state(egis_dev->pinctrl, egis_dev->vcc_low)) + goto egisfp_pinctrl_fail; + } + } + + + + egis_dev->platforminit_done = 1; + if (egis_dev->pwr_by_gpio && egis_dev->ctrl_power) + INFO_PRINT(" %s : successful status = %d gpio num -> vcc = %d rst = %d Irq = %d \n", __func__, status, egis_dev->vcc_33v_Pin, egis_dev->rstPin, egis_dev->irqPin); + else + INFO_PRINT(" %s : successful status = %d gpio num -> rst = %d Irq = %d \n", __func__, status, egis_dev->rstPin, egis_dev->irqPin); + } + else + { + INFO_PRINT(" %s : platform already init \n", __func__); + } + return 0; + } + else + { + ERROR_PRINT(" %s : device node is null \n", __func__); + return -ENODEV; + } +egisfp_pinctrl_fail: + ERROR_PRINT(" %s : devm_pinctrl_put \n", __func__); + devm_pinctrl_put(egis_dev->pinctrl); + egis_dev->pinctrl = NULL; + gpio_free(egis_dev->irqPin); +egisfp_irq_request_fail: + gpio_free(egis_dev->rstPin); +egisfp_reset_request_fail: +egisfp_power_setup_fail: + if (egis_dev->ctrl_power) + { + if (egis_dev->pwr_by_gpio) + gpio_free(egis_dev->vcc_33v_Pin); + else + devm_regulator_put(egis_dev->vcc); + } +egisfp_power_request_fail: + return -EIO; +} + +int egisfp_check_ioctl_permission(struct egisfp_dev_t *egis_dev, unsigned int cmd) +{ + + if (cmd == FP_GET_RESOURCE || cmd == FP_FREE_RESOURCE) + { + return 0; + } + else if (egis_dev->platforminit_done) + { + return 0; + } + else + { + ERROR_PRINT(" %s : need get resource fisrt \n", __func__); + return -EACCES; + } +} +#if defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +static int drm_check_dt(struct egisfp_dev_t *egis_dev) +{ + int i = 0; + int count = 0; + struct device_node *np = egis_dev->dd->dev.of_node; + struct device_node *node = NULL; + struct drm_panel *panel = NULL; + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) { + ERROR_PRINT(" %s : find drm_panel fail count = %d ", __func__, count); + return -ENODEV; + } + 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)) { + ERROR_PRINT(" %s : ind drm_panel successfully ", __func__); + egis_dev->active_panel = panel; + return 0; + } + } + ERROR_PRINT(" %s : can not find drm_panel ", __func__); + return -ENODEV; +} +#endif +// Register FB notifier +++ +static int egisfp_fb_callback(struct notifier_block *nb, unsigned long val, void *data) +{ + struct egisfp_dev_t *egis_dev = NULL; + char *envp[2]; + int ret = 0; +#if defined(CONFIG_PANEL_NOTIFICATIONS) + INFO_PRINT(" %s : got notify value = %d \n", __func__, (int)val); + egis_dev = container_of(nb, struct egisfp_dev_t, notifier); + egis_dev->screen_onoff = 1; + switch(val) { + case PANEL_EVENT_DISPLAY_ON: + egis_dev->screen_onoff = 1; + envp[0] = "PANEL=1"; + break; + case PANEL_EVENT_DISPLAY_OFF: + egis_dev->screen_onoff = 0; + envp[0] = "PANEL=0"; + break; + case PANEL_EVENT_PRE_DISPLAY_OFF: + break; + case PANEL_EVENT_PRE_DISPLAY_ON: + break; + default: + break; + } + if(val == FP_NOTIFY_ON ||val == FP_NOTIFY_OFF){ + envp[1] = NULL; + ret = kobject_uevent_env(&egis_dev->dd->dev.kobj, KOBJ_CHANGE, envp); + INFO_PRINT(" %s : screen_onoff = %d val=%lu ret=%d\n", __func__, egis_dev->screen_onoff, val,ret); + } +#elif defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) + struct drm_panel_notifier *evdata = data; + unsigned int blank; + INFO_PRINT(" %s : got notify value = %d \n", __func__, (int)val); + if (val != DRM_PANEL_EARLY_EVENT_BLANK) + { + return NOTIFY_OK; + } + egis_dev = container_of(nb, struct egisfp_dev_t, notifier); + if (!evdata || !evdata->data || !egis_dev) { + ERROR_PRINT(" %s : some parameters is null \n", __func__); + return NOTIFY_OK; + } + blank = *(int *)(evdata->data); + switch (blank) { + case DRM_PANEL_BLANK_UNBLANK: + egis_dev->screen_onoff = 1; + envp[0] = "PANEL=1"; + break; + case DRM_PANEL_BLANK_POWERDOWN: + egis_dev->screen_onoff = 0; + envp[0] = "PANEL=0"; + break; + default: + break; + } + INFO_PRINT(" %s : screen_onoff = %d \n", __func__, egis_dev->screen_onoff); + envp[1] = NULL; + ret = kobject_uevent_env(&egis_dev->dd->dev.kobj, KOBJ_CHANGE, envp); +#else + struct fb_event *evdata = data; + unsigned int blank; + + if (val != FP_NOTIFY_EVENT_BLANK) + return 0; + + INFO_PRINT(" %s : got notify value = %d \n", __func__, (int)val); + egis_dev = container_of(nb, struct egisfp_dev_t, notifier); + if (evdata && evdata->data && val == FB_EVENT_BLANK && egis_dev) { + blank = *(int *)(evdata->data); + INFO_PRINT(" %s : blank value = %d \n", __func__, (int)blank); + switch (blank) { + case FP_NOTIFY_OFF: + egis_dev->screen_onoff = 0; + envp[0] = "PANEL=0"; + break; + case FP_NOTIFY_ON: + egis_dev->screen_onoff = 1; + envp[0] = "PANEL=1"; + break; + default: + break; + } + INFO_PRINT(" %s : screen_onoff = %d \n", __func__, egis_dev->screen_onoff); + envp[1] = NULL; + ret = kobject_uevent_env(&egis_dev->dd->dev.kobj, KOBJ_CHANGE, envp); + } +#endif + return NOTIFY_OK; +} + +static struct notifier_block egisfp_noti_block = { + .notifier_call = egisfp_fb_callback, +}; +// Register FB notifier --- + +int egisfp_parse_dt(struct egisfp_dev_t *egis_dev) +{ + int ret; + struct device_node *node = egis_dev->dd->dev.of_node; + u32 voltage_supply[2]; + u32 current_supply; + INFO_PRINT(" %s : start \n", __func__); + + if (node) + { + egis_dev->ctrl_power = of_property_read_bool(node, "fp-ctrl-power"); + if (egis_dev->ctrl_power) + { + egis_dev->pwr_by_gpio = of_property_read_bool(node, "fp-gpio-vcc-enable"); + if (egis_dev->pwr_by_gpio) + { + egis_dev->vcc_33v_Pin = of_get_named_gpio(node, "egistec,gpio_vcc_en", 0); + INFO_PRINT(" %s : vcc_33v_pin gpio num is %d \n", __func__, egis_dev->vcc_33v_Pin); + if (!gpio_is_valid(egis_dev->vcc_33v_Pin)) + { + ERROR_PRINT(" %s : vcc_33v_pin gpio is invalid \n", __func__); + return -ENODEV; + } + } + else + { + ret = of_property_read_u32_array(node, "egis-fp,vcc-voltage", voltage_supply, 2); + if (ret < 0) + { + ERROR_PRINT(" %s : fail to get vcc regulator voltage \n", __func__); + return -ENODEV; + } + INFO_PRINT(" %s : vcc regulator voltage get Max = %d, Min = %d \n", __func__, voltage_supply[1], voltage_supply[0]); + egis_dev->regulator_voltage_max = voltage_supply[1]; + egis_dev->regulator_voltage_min = voltage_supply[0]; + + ret = of_property_read_u32_array(node, "egis-fp,vcc-current", ¤t_supply, 1); + if (ret < 0) + { + ERROR_PRINT(" %s : fail to get vcc regulator current_supply \n", __func__); + return -ENODEV; + } + INFO_PRINT(" %s : vcc regulator current get %d \n", __func__, current_supply); + egis_dev->regulator_current = current_supply; + } + } + + egis_dev->rstPin = of_get_named_gpio(node, "egistec,gpio_reset", 0); + INFO_PRINT(" %s : rst pin gpio num is %d \n", __func__, egis_dev->rstPin); + if (!gpio_is_valid(egis_dev->rstPin)) + { + ERROR_PRINT(" %s : rst pin gpio is invalid \n", __func__); + return -ENODEV; + } + + egis_dev->irqPin = of_get_named_gpio(node, "egistec,gpio_irq", 0); + INFO_PRINT(" %s : irq pin gpio num is %d \n", __func__, egis_dev->irqPin); + if (!gpio_is_valid(egis_dev->irqPin)) + { + ERROR_PRINT(" %s : irq pin gpio is invalid \n", __func__); + return -ENODEV; + } + } + else + { + ERROR_PRINT(" %s : device node is null \n", __func__); + return -ENODEV; + } + INFO_PRINT(" %s : successful \n", __func__); + + return 0; +} + +const struct file_operations egisfp_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = egisfp_ioctl, + .compat_ioctl = egisfp_compat_ioctl, + .open = egisfp_open, + .release = egisfp_release, + .llseek = no_llseek, + .poll = egisfp_interrupt_poll}; + +/*-------------------------------------------------------------------------*/ +struct class *egisfp_class; +/*-------------------------------------------------------------------------*/ + +int egisfp_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct egisfp_dev_t *egis_dev = dev_get_drvdata(dev); + INFO_PRINT(" %s : driver remove \n", __func__); + if (egis_dev->request_irq_done) + { + free_irq(egis_dev->gpio_irq, egis_dev); + } + if (egis_dev->call_back_registered) + { +#if defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) + if (egis_dev->active_panel) + { + drm_panel_notifier_unregister(egis_dev->active_panel, &egis_dev->notifier); + egis_dev->active_panel = NULL; + } +#else + ets_fb_unregister_client(&egis_dev->notifier); +#endif + } + del_timer_sync(&egis_dev->fps_ints.timer); + + device_init_wakeup(&egis_dev->dd->dev, 0); + + egis_dev->request_irq_done = 0; + + if (egis_dev->input_dev) + { + input_unregister_device(egis_dev->input_dev); + } + + device_destroy(egisfp_class, egis_dev->devt); + + list_del(&egis_dev->device_entry); + + class_destroy(egisfp_class); + + unregister_chrdev(EGIS_FP_MAJOR, egisfp_driver.driver.name); +#ifdef MMI_RELAY_MODULE + if(fpsData) { + relay_unregister_action(BLOCKING, MMI_RELAY, &fpsData->relay_notif); + kfree(fpsData); + fpsData = NULL; + } +#endif + g_data = NULL; + return 0; +} + +int egisfp_probe(struct platform_device *pdev) +{ + struct egisfp_dev_t *egis_dev; + int status, i; + unsigned long minor; + + INFO_PRINT(" %s : driver init \n", __func__); + BUILD_BUG_ON(N_SPI_MINORS > 256); + status = register_chrdev(EGIS_FP_MAJOR, EGIS_CHRD_DRIVER_NAME, &egisfp_fops); + if (status < 0) + { + ERROR_PRINT(" %s : register_chrdev error \n", __func__); + return status; + } + egisfp_class = class_create(THIS_MODULE, EGIS_CLASS_NAME); + if (IS_ERR(egisfp_class)) + { + ERROR_PRINT(" %s : class_create error \n", __func__); + unregister_chrdev(EGIS_FP_MAJOR, egisfp_driver.driver.name); + return PTR_ERR(egisfp_class); + } + /* Allocate driver data */ + egis_dev = kzalloc(sizeof(struct egisfp_dev_t), GFP_KERNEL); + if (egis_dev == NULL) + { + ERROR_PRINT(" %s : Failed to kzalloc \n", __func__); + return -ENOMEM; + } +/* Initialize the driver data */ + dev_set_drvdata(&pdev->dev, egis_dev); + egis_dev->dd = pdev; + + + spin_lock_init(&egis_dev->irq_lock); + + device_init_wakeup(&egis_dev->dd->dev, 1); + + mutex_init(&device_list_lock); + + INIT_LIST_HEAD(&egis_dev->device_entry); + + /* init status */ + egis_dev->pars_dtsi_done = 0; + egis_dev->fps_ints.drdy_irq_flag = DRDY_IRQ_DISABLE; + egis_dev->clk_enabled = 0; + egis_dev->request_irq_done = 0; + egis_dev->platforminit_done = 0; + egis_dev->power_enable = 0; + egis_dev->screen_onoff = 0; + egis_dev->call_back_registered = 0; +#if defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) + egis_dev->active_panel = NULL; + drm_check_dt(egis_dev); +#endif + /* + * If we can allocate a minor number, hook up this device. + * Reusing minors is fine so long as udev or mdev is working. + */ + mutex_lock(&device_list_lock); + minor = find_first_zero_bit(minors, N_SPI_MINORS); + if (minor < N_SPI_MINORS) + { + struct device *fdev; + egis_dev->devt = MKDEV(EGIS_FP_MAJOR, minor); + fdev = device_create(egisfp_class, &egis_dev->dd->dev, egis_dev->devt, + egis_dev, EGIS_DEV_NAME); + status = IS_ERR(fdev) ? PTR_ERR(fdev) : 0; + } + else + { + ERROR_PRINT(" %s : no minor number available \n", __func__); + status = -ENODEV; + } + if (status == 0) + { + set_bit(minor, minors); + list_add(&egis_dev->device_entry, &device_list); + } + + mutex_unlock(&device_list_lock); + + if (status) + { + goto egistec_probe_failed; + } + + egis_dev->input_dev = input_allocate_device(); + if (egis_dev->input_dev == NULL) + { + ERROR_PRINT(" %s : failed to allocate input device \n", __func__); + status = -ENOMEM; + goto egistec_probe_failed; + } + for (i = 0; i < ARRAY_SIZE(key_maps); i++) + input_set_capability(egis_dev->input_dev, key_maps[i].type, key_maps[i].code); + + egis_dev->input_dev->name = EGIS_INPUT_NAME; + egis_dev->input_dev->id.vendor = 0x001f; + + status = input_register_device(egis_dev->input_dev); + if (status) + { + ERROR_PRINT(" %s : failed to register input device %d \n", __func__, status); + goto egistec_input_failed; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + timer_setup(&egis_dev->fps_ints.timer, egisfp_interrupt_timer_call, 0); +#else + setup_timer(&egis_dev->fps_ints.timer, egisfp_interrupt_timer_call, (unsigned long)&egis_dev->fps_ints); +#endif + + //Register display notifier +++ + egis_dev->notifier = egisfp_noti_block; +#if defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) + if (egis_dev->active_panel) { + DEBUG_PRINT(" %s : register drm_panel_notifier \n", __func__); + status = drm_panel_notifier_register(egis_dev->active_panel, &egis_dev->notifier); + if (status) + ERROR_PRINT(" %s : drm_panel_notifier_register fail: %d ", __func__, status); + else + egis_dev->call_back_registered = 1; + } +#else + status = ets_fb_register_client(&egis_dev->notifier); + if (status) + ERROR_PRINT(" %s : ets_fb_register_client fail: %d ", __func__, status); + else + egis_dev->call_back_registered = 1; +#endif + + g_data = egis_dev; +#ifdef MMI_RELAY_MODULE + fpsData = FPS_init(); +#endif + + DEBUG_PRINT(" %s : initialize success %d\n", __func__, status); + + return status; + +egistec_input_failed: + if (egis_dev->input_dev != NULL) + input_free_device(egis_dev->input_dev); +egistec_probe_failed: + device_destroy(egisfp_class, egis_dev->devt); + class_destroy(egisfp_class); + kfree(egis_dev); + ERROR_PRINT(" %s : driver probe failed %d \n", __func__, status); + return status; +} + +int __init egisfp_init(void) +{ + int status; + INFO_PRINT(" %s : module init \n", __func__); + status = platform_driver_register(&egisfp_driver); + if (status) + { + ERROR_PRINT(" %s : register Egis driver fail \n", __func__); + return -EINVAL; + } + INFO_PRINT(" %s : module init OK ! \n", __func__); + return status; +} +void __exit egisfp_exit(void) +{ + INFO_PRINT("module exit \n"); + platform_driver_unregister(&egisfp_driver); +} + +module_init(egisfp_init); +module_exit(egisfp_exit); + +MODULE_DESCRIPTION("egis fingerprint driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/rbs_fod_mmi/etxxx_fp.h b/drivers/input/misc/rbs_fod_mmi/etxxx_fp.h new file mode 100644 index 000000000000..c2dae9564f1a --- /dev/null +++ b/drivers/input/misc/rbs_fod_mmi/etxxx_fp.h @@ -0,0 +1,230 @@ +#ifndef _FP_LINUX_DIRVER_H_ +#define _FP_LINUX_DIRVER_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "20210517" +#define USE_PIN_CTRL +//#define PLATFORM_SPI +//#define MTK_PLATFORM +//#define LSI_PLATFORM +#define EGIS_DEBUG + +#define DEBUG_PRINT(fmt, arg...) \ + printk(KERN_DEBUG "[EGISFP]" fmt, ##arg) + +#define INFO_PRINT(fmt, arg...) \ + printk("[EGISFP]" fmt, ##arg) +#define ERROR_PRINT(fmt, arg...) \ + printk(KERN_ERR "[EGISFP]" fmt, ##arg) + +#define EGIS_FP_MAJOR 100 /* assigned */ +#define N_SPI_MINORS 32 /* ... up to 256 */ + +#define EGIS_DEV_NAME "esfp0" +#define EGIS_INPUT_NAME "uinput-egis" /*"uinput-egis" */ + +#define EGIS_CHRD_DRIVER_NAME "egis_fp" +#define EGIS_CLASS_NAME "egis_fp" + +/* ------------------------- Opcode -------------------------------------*/ +#define FP_REGISTER_READ 0x01 +#define FP_REGISTER_WRITE 0x02 +#define FP_GET_ONE_IMG 0x03 +#define FP_SENSOR_RESET 0x04 +#define FP_POWER_ONOFF 0x05 +#define FP_SET_SPI_CLOCK 0x06 +#define FP_RESET_SET 0x07 +#define FP_WAKELOCK_ENABLE 0x08 +#define FP_WAKELOCK_DISABLE 0x09 +#define FP_GET_RESOURCE 0x11 +#define FP_FREE_RESOURCE 0x12 +#define FP_SET_FPS_MODE 0x14 + +#define GET_IO_STUS 0x21 +#define SEND_NAVI_EVENT 0x31 +/* trigger signal initial routine*/ +#define INT_TRIGGER_INIT 0xa4 +/* trigger signal close routine*/ +#define INT_TRIGGER_CLOSE 0xa5 +/* read trigger status*/ +#define INT_TRIGGER_READ 0xa6 +/* polling trigger status*/ +#define INT_TRIGGER_POLLING 0xa7 +/* polling abort*/ +#define INT_TRIGGER_ABORT 0xa8 + +#define FP_FREE_GPIO 0xaf +#define FP_SPICLK_ENABLE 0xaa +#define FP_SPICLK_DISABLE 0xab +#define DELETE_DEVICE_NODE 0xac + +#define FP_SPIPIN_SETTING 0xad +#define FP_SPIPIN_PULLLOW 0xae + +#define FP_POWERSETUP 0xb0 +#define FP_WAKELOCK_TIMEOUT_ENABLE 0xb1 +#define FP_WAKELOCK_TIMEOUT_DISABLE 0xb2 +#define GET_SCREEN_ONOFF 0xb3 + +#define DRDY_IRQ_ENABLE 1 +#define DRDY_IRQ_DISABLE 0 + +#define EDGE_TRIGGER_FALLING 0x0 +#define EDGE_TRIGGER_RISING 0x1 +#define LEVEL_TRIGGER_LOW 0x2 +#define LEVEL_TRIGGER_HIGH 0x3 +#define WAKE_HOLD_TIME 2000 //ms + +#define SPI_DEFAULT_SPEED (1000000 * 20) //20M + + +#define Tpwr_off_delay 50 +#define Tpwr_on_delay 4 +#define Rst_off_delay 4 +#define Rst_on_delay 6 + + +#define KEY_FPS_DOWN 614 +#define KEY_FPS_UP 615 +#define KEY_FPS_TAP 616 +#define KEY_FPS_HOLD 617 +#define KEY_FPS_YPLUS 618 +#define KEY_FPS_YMINUS 619 +#define KEY_FPS_XPLUS 620 +#define KEY_FPS_XMINUS 621 +#define KEY_FPS_DOUBLE_TAP 622 + +#define EGIS_NAV_INPUT_UP KEY_FPS_YPLUS +#define EGIS_NAV_INPUT_DOWN KEY_FPS_YMINUS +#define EGIS_NAV_INPUT_LEFT KEY_FPS_XMINUS +#define EGIS_NAV_INPUT_RIGHT KEY_FPS_XPLUS +#define EGIS_NAV_INPUT_CLICK KEY_FPS_TAP +#define EGIS_NAV_INPUT_DOUBLE_CLICK KEY_FPS_DOUBLE_TAP +#define EGIS_NAV_INPUT_LONG_PRESS KEY_HOME +#define EGIS_NAV_INPUT_FINGER_DOWN KEY_FPS_DOWN +#define EGIS_NAV_INPUT_FINGER_UP KEY_FPS_UP + + + +enum egis_navi_event +{ + NAVI_EVENT_ON, + NAVI_EVENT_OFF, + NAVI_EVENT_UP, + NAVI_EVENT_DOWN, + NAVI_EVENT_LEFT, + NAVI_EVENT_RIGHT, + NAVI_EVENT_CLICK, + NAVI_EVENT_DOUBLE_CLICK, + NAVI_EVENT_LONG_PRESS, +}; + +struct egis_key_map_t +{ + unsigned int type; + unsigned int code; +}; + +struct egisfp_interrupt_desc_t +{ + int gpio; + int number; + char *name; + int int_count; + struct timer_list timer; + int finger_on; + int detect_period; + int detect_threshold; + int drdy_irq_flag; + int drdy_irq_abort; +}; + +struct egisfp_ioctl_cmd_t +{ + uint32_t int_mode; + uint32_t detect_period; + uint32_t detect_threshold; + uint32_t power_on; +}; + +/* ------------------------- Structure ------------------------------*/ +struct egisfp_dev_t +{ + dev_t devt; + spinlock_t spi_lock; + struct platform_device *dd; + struct list_head device_entry; + /* buffer is NULL unless this device is open (users > 0) */ + struct mutex buf_lock; + unsigned users; + u8 *buffer; + unsigned int irqPin; /* interrupt GPIO pin number */ + unsigned int rstPin; /* Reset GPIO pin number */ + unsigned int vdd_18v_Pin; /* Vdd GPIO pin number */ + unsigned int vcc_33v_Pin; /* Vcc GPIO pin number */ + struct input_dev *input_dev; + int navigation_enable; + int pars_dtsi_done; + int platforminit_done; + int gpio_irq; + spinlock_t irq_lock; + int request_irq_done; + struct wakeup_source wakeup_source_fp; + int ctrl_power; + int pwr_by_gpio; + int power_enable; + int screen_onoff; + int call_back_registered; + struct regulator *vcc; + struct regulator *vdd; + uint32_t regulator_current; + uint32_t regulator_voltage_max; + uint32_t regulator_voltage_min; + int clk_enabled; + struct egisfp_interrupt_desc_t fps_ints; + int clk_speed; + struct pinctrl *pinctrl; + struct pinctrl_state *spi_active; + struct pinctrl_state *spi_default; + struct pinctrl_state *reset_high; + struct pinctrl_state *reset_low; + struct pinctrl_state *vcc_high; + struct pinctrl_state *vcc_low; + struct pinctrl_state *irq_active; + struct notifier_block notifier; +#if defined(CONFIG_DRM_PANEL) && LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + struct drm_panel *active_panel; +#endif +}; + +int egisfp_platforminit(struct egisfp_dev_t *egis_dev); +int egisfp_platformfree(struct egisfp_dev_t *egis_dev); +/* interrupt init */ +int egisfp_interrupt_init(struct egisfp_dev_t *egis_dev, int int_mode, int detect_period, int detect_threshold); +/* interrupt free */ +int egisfp_interrupt_free(struct egisfp_dev_t *egis_dev); +void egisfp_interrupt_abort(struct egisfp_dev_t *egis_dev); +unsigned int egisfp_interrupt_poll(struct file *file, struct poll_table_struct *wait); +int egisfp_parse_dt(struct egisfp_dev_t *egis_dev); +int do_egisfp_power_onoff(struct egisfp_dev_t *egis_dev, struct egisfp_ioctl_cmd_t *ioctl_data); +int do_egisfp_reset(struct egisfp_dev_t *egis_dev); +int do_egisfp_reset_set(struct egisfp_dev_t *egis_dev, int reset_high_low); +int egisfp_check_ioctl_permission(struct egisfp_dev_t *egis_dev, unsigned int cmd); +int egisfp_set_screen_onoff(int on); +#endif diff --git a/drivers/input/misc/rbs_fod_mmi/generator_condition.txt b/drivers/input/misc/rbs_fod_mmi/generator_condition.txt new file mode 100644 index 000000000000..eedc32c66173 --- /dev/null +++ b/drivers/input/misc/rbs_fod_mmi/generator_condition.txt @@ -0,0 +1 @@ +lenovo;ET528;ET5XX;QCOM_PLATFORM;USE_PIN_CTRL;EGIS_DEBUG diff --git a/drivers/input/misc/rbs_fps_mmi/Android.mk b/drivers/input/misc/rbs_fps_mmi/Android.mk new file mode 100644 index 000000000000..27f807f419e3 --- /dev/null +++ b/drivers/input/misc/rbs_fps_mmi/Android.mk @@ -0,0 +1,22 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +ETS_FPS_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +ETS_FPS_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +ifeq ($(EGIS_FPS_PANEL_NOTIFICATIONS),true) + KERNEL_CFLAGS += CONFIG_EGIS_PANEL_NOTIFICATIONS=y +endif + +ifeq ($(BOARD_SUPPORT_EGIS_FOD),true) + KERNEL_CFLAGS += CONFIG_ET721_FOD=y +endif +include $(CLEAR_VARS) +LOCAL_MODULE := rbs_fps_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(ETS_FPS_MMI_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/misc/rbs_fps_mmi/Kbuild b/drivers/input/misc/rbs_fps_mmi/Kbuild new file mode 100644 index 000000000000..b55d1b2190a6 --- /dev/null +++ b/drivers/input/misc/rbs_fps_mmi/Kbuild @@ -0,0 +1,18 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/rbs_fps_mmi + +ifneq ($(filter m y,$(CONFIG_EGIS_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_PANEL_NOTIFICATIONS +endif + +ifneq ($(filter m y,$(CONFIG_ET721_FOD)),) + EXTRA_CFLAGS += -DET721_FOD +endif + +rbs_fps_mmi-y += etxxx_fp.o + + +obj-m += rbs_fps_mmi.o + diff --git a/drivers/input/misc/rbs_fps_mmi/Makefile b/drivers/input/misc/rbs_fps_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/misc/rbs_fps_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/misc/rbs_fps_mmi/etxxx_fp.c b/drivers/input/misc/rbs_fps_mmi/etxxx_fp.c new file mode 100644 index 000000000000..f75ffeedd2e3 --- /dev/null +++ b/drivers/input/misc/rbs_fps_mmi/etxxx_fp.c @@ -0,0 +1,1415 @@ +/* + * Simple synchronous userspace interface to SPI devices + * + * Copyright (C) 2006 SWAPP + * Andrea Paterniani + * Copyright (C) 2007 David Brownell (simplification, cleanup) + * + * 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. + * + */ + +/* +* +* etxxx_fp.c +* Date: 2016/03/16 +* Version: 0.9.0.1 +* Revise Date: 2020/11/25 +* Copyright (C) 2007-2019 Egis Technology Inc. +* ----------------- version history ------------------------ +* +*Kevin.Liang 20181102 add powersetup for IOC +*Jacob.Kung 20201125 modify for support all sensor +*Jacob.Kung 20201126 modify struct define naming +*Jacob.Kung 20201130 add check_ioctl_permission +*Jacob.Kung 20201207 register platform driver with MTK platform +*Jacob.Kung 20210331 add register FB notifier +*Jacob.Kung 20210512 add register DRM notifier +* ----------------------------------------------------------- +* +**/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "etxxx_fp.h" +#include +#if defined(CONFIG_PANEL_NOTIFICATIONS) +#include +#define FP_NOTIFY_ON PANEL_EVENT_DISPLAY_ON +#define FP_NOTIFY_OFF PANEL_EVENT_DISPLAY_OFF +#define FP_NOTIFY_EVENT_BLANK PANEL_EVENT_DISPLAY_OFF //MSM_DRM_EVENT_BLANK +#define ets_fb_register_client(client) panel_register_notifier(client); +#define ets_fb_unregister_client(client) panel_unregister_notifier(client); +#elif defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +#include +#else +#include +#define FP_NOTIFY_ON FB_BLANK_UNBLANK +#define FP_NOTIFY_OFF FB_BLANK_POWERDOWN +#define FP_NOTIFY_EVENT_BLANK FB_EVENT_BLANK +#define ets_fb_register_client(client) fb_register_client(client); +#define ets_fb_unregister_client(client) fb_unregister_client(client) +#endif + +struct egisfp_dev_t *g_data = NULL; +DECLARE_BITMAP(minors, N_SPI_MINORS); +LIST_HEAD(device_list); +DEFINE_MUTEX(device_list_lock); + +static struct egis_key_map_t key_maps[] = { + {EV_KEY, EGIS_NAV_INPUT_UP}, + {EV_KEY, EGIS_NAV_INPUT_DOWN}, + {EV_KEY, EGIS_NAV_INPUT_LEFT}, + {EV_KEY, EGIS_NAV_INPUT_RIGHT}, + {EV_KEY, EGIS_NAV_INPUT_CLICK}, + {EV_KEY, EGIS_NAV_INPUT_DOUBLE_CLICK}, + {EV_KEY, EGIS_NAV_INPUT_LONG_PRESS}, + {EV_KEY, EGIS_NAV_INPUT_FINGER_DOWN}, + {EV_KEY, EGIS_NAV_INPUT_FINGER_UP}, +}; + +int egisfp_probe(struct platform_device *pdev); +int egisfp_remove(struct platform_device *pdev); + +/* -------------------------------------------------------------------- */ + +struct of_device_id egistec_match_table[] = { + { + .compatible = "fp-egistec", + }, + {}, +}; + +static struct platform_driver egisfp_driver = { + .driver = { + .name = EGIS_DEV_NAME, + .owner = THIS_MODULE, + .of_match_table = egistec_match_table, + }, + .probe = egisfp_probe, + .remove = egisfp_remove, +}; + +/* add for clk enable from kernel*/ + + +/* add for clk enable from kernel*/ + +/* ------------------------------ Interrupt -----------------------------*/ + +static DECLARE_WAIT_QUEUE_HEAD(interrupt_waitq); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) +void egisfp_interrupt_timer_call(struct timer_list *t) +{ + struct egisfp_interrupt_desc_t *fps_int = from_timer(fps_int, t, timer); + struct egisfp_dev_t *egis_dev; + + egis_dev = container_of(fps_int, struct egisfp_dev_t, fps_ints); + INFO_PRINT(" %s : interrupt count = %d \n", __func__, egis_dev->fps_ints.int_count); + + if (egis_dev->fps_ints.int_count >= egis_dev->fps_ints.detect_threshold) + { + egis_dev->fps_ints.finger_on = 1; + INFO_PRINT(" %s : interrupt triggered \n", __func__); + } + else + { + INFO_PRINT(" %s : interrupt not triggered \n", __func__); + } + egis_dev->fps_ints.int_count = 0; + wake_up_interruptible(&interrupt_waitq); +} +#else +void egisfp_interrupt_timer_call(unsigned long _data) +{ + struct egisfp_interrupt_desc_t *fps_int = (struct egisfp_interrupt_desc_t *)_data; + struct egisfp_dev_t *egis_dev; + + egis_dev = container_of(fps_int, struct egisfp_dev_t, fps_ints); + INFO_PRINT(" %s : interrupt count = %d \n", __func__, egis_dev->fps_ints.int_count); + + if (egis_dev->fps_ints.int_count >= egis_dev->fps_ints.detect_threshold) + { + egis_dev->fps_ints.finger_on = 1; + INFO_PRINT(" %s : interrupt triggered \n", __func__); + } + else + { + INFO_PRINT(" %s : interrupt not triggered \n", __func__); + } + egis_dev->fps_ints.int_count = 0; + wake_up_interruptible(&interrupt_waitq); +} +#endif +irqreturn_t fp_eint_func(int irq, void *dev_id) +{ + struct egisfp_dev_t *egis_dev = dev_id; + if (!egis_dev->fps_ints.int_count) + mod_timer(&egis_dev->fps_ints.timer, jiffies + msecs_to_jiffies(egis_dev->fps_ints.detect_period)); + egis_dev->fps_ints.int_count++; + INFO_PRINT(" %s : fps_ints.int_count=%d \n", __func__, egis_dev->fps_ints.int_count); + pm_wakeup_event(&egis_dev->dd->dev, WAKE_HOLD_TIME); + return IRQ_HANDLED; +} + +irqreturn_t fp_eint_func_ll(int irq, void *dev_id) +{ + struct egisfp_dev_t *egis_dev = dev_id; + unsigned long irqflags; + //INFO_PRINT(" %s \n", __func__); + spin_lock_irqsave(&egis_dev->irq_lock, irqflags); + if (egis_dev->fps_ints.drdy_irq_flag == DRDY_IRQ_ENABLE) + { + egis_dev->fps_ints.finger_on = 1; + disable_irq_nosync(egis_dev->gpio_irq); + //INFO_PRINT(" %s : DRDY_IRQ_DISABLE \n", __func__); + egis_dev->fps_ints.drdy_irq_flag = DRDY_IRQ_DISABLE; + } + spin_unlock_irqrestore(&egis_dev->irq_lock, irqflags); + wake_up_interruptible(&interrupt_waitq); + pm_wakeup_event(&egis_dev->dd->dev, WAKE_HOLD_TIME); + + return IRQ_RETVAL(IRQ_HANDLED); +} + +/* + * FUNCTION NAME. + * egisfp_interrupt_init + * + * FUNCTIONAL DESCRIPTION. + * button initial routine + * + * ENTRY PARAMETERS. + * int_mode - determine trigger mode + * EDGE_TRIGGER_FALLING 0x0 + * EDGE_TRIGGER_RAISING 0x1 + * LEVEL_TRIGGER_LOW 0x2 + * LEVEL_TRIGGER_HIGH 0x3 + * + * EXIT PARAMETERS. + * Function Return int + */ + +int egisfp_interrupt_init(struct egisfp_dev_t *egis_dev, int int_mode, int detect_period, int detect_threshold) +{ + + int err = 0; + unsigned long irqflags; + + INFO_PRINT(" %s : mode = %d period = %d threshold = %d \n", __func__, int_mode, detect_period, detect_threshold); + INFO_PRINT(" %s : request_irq_done = %d gpio_irq = %d pin = %d \n", __func__, egis_dev->request_irq_done, egis_dev->gpio_irq, egis_dev->irqPin); + + egis_dev->fps_ints.detect_period = detect_period; + egis_dev->fps_ints.detect_threshold = detect_threshold; + egis_dev->fps_ints.int_count = 0; + egis_dev->fps_ints.finger_on = 0; + egis_dev->fps_ints.drdy_irq_abort = 0; + + if (egis_dev->request_irq_done == 0) + { + if (gpio_is_valid(egis_dev->irqPin)) + { + egis_dev->gpio_irq = gpio_to_irq(egis_dev->irqPin); + INFO_PRINT(" %s : fp_irq number %d \n", __func__, egis_dev->gpio_irq); + } + else + { + ERROR_PRINT(" %s : irqPin is not valid \n", __func__); + return -EIO; + } + + if (egis_dev->gpio_irq < 0) + { + ERROR_PRINT(" %s : gpio_to_irq failed \n", __func__); + return -EIO; + } + + INFO_PRINT(" %s : current flag : %d disable: %d enable: %d \n", __func__, egis_dev->fps_ints.drdy_irq_flag, DRDY_IRQ_DISABLE, DRDY_IRQ_ENABLE); + + if (int_mode == EDGE_TRIGGER_RISING) + { + INFO_PRINT(" %s : EDGE_TRIGGER_RISING \n", __func__); + err = request_irq(egis_dev->gpio_irq, fp_eint_func, IRQ_TYPE_EDGE_RISING, "fp_detect-eint", egis_dev); + } + else if (int_mode == EDGE_TRIGGER_FALLING) + { + INFO_PRINT(" %s : EDGE_TRIGGER_FALLING \n", __func__); + err = request_irq(egis_dev->gpio_irq, fp_eint_func, IRQ_TYPE_EDGE_FALLING, "fp_detect-eint", egis_dev); + } + else if (int_mode == LEVEL_TRIGGER_LOW) + { + INFO_PRINT(" %s : LEVEL_TRIGGER_LOW \n", __func__); + err = request_irq(egis_dev->gpio_irq, fp_eint_func_ll, IRQ_TYPE_LEVEL_LOW, "fp_detect-eint", egis_dev); + } + else if (int_mode == LEVEL_TRIGGER_HIGH) + { + INFO_PRINT(" %s : LEVEL_TRIGGER_HIGH \n", __func__); + err = request_irq(egis_dev->gpio_irq, fp_eint_func_ll, IRQ_TYPE_LEVEL_HIGH, "fp_detect-eint", egis_dev); + } + + if (err) + { + ERROR_PRINT(" %s : request_irq failed return %d \n", __func__, err); + return err; + } + + disable_irq_nosync(egis_dev->gpio_irq); + INFO_PRINT(" %s : request_irq return %d \n", __func__, err); + egis_dev->request_irq_done = 1; + } + INFO_PRINT(" %s : current flag : %d disable: %d enable: %d \n", __func__, egis_dev->fps_ints.drdy_irq_flag, DRDY_IRQ_DISABLE, DRDY_IRQ_ENABLE); + spin_lock_irqsave(&egis_dev->irq_lock, irqflags); + if (egis_dev->fps_ints.drdy_irq_flag == DRDY_IRQ_DISABLE) + { + egis_dev->fps_ints.drdy_irq_flag = DRDY_IRQ_ENABLE; + INFO_PRINT(" %s : enable irq with mode = %d \n", __func__, int_mode); + enable_irq_wake(egis_dev->gpio_irq); + enable_irq(egis_dev->gpio_irq); + } + spin_unlock_irqrestore(&egis_dev->irq_lock, irqflags); + + return err; +} + +/* + * FUNCTION NAME. + * egisfp_interrupt_free + * + * FUNCTIONAL DESCRIPTION. + * free all interrupt resource + * + * EXIT PARAMETERS. + * Function Return int + */ + +int egisfp_interrupt_free(struct egisfp_dev_t *egis_dev) +{ + unsigned long irqflags; + DEBUG_PRINT(" %s \n", __func__); + spin_lock_irqsave(&egis_dev->irq_lock, irqflags); + egis_dev->fps_ints.finger_on = 0; + if (egis_dev->fps_ints.drdy_irq_flag == DRDY_IRQ_ENABLE) + { + DEBUG_PRINT(" %s : irq disable \n", __func__); + disable_irq_nosync(egis_dev->gpio_irq); + del_timer_sync(&egis_dev->fps_ints.timer); + egis_dev->fps_ints.drdy_irq_flag = DRDY_IRQ_DISABLE; + } + spin_unlock_irqrestore(&egis_dev->irq_lock, irqflags); + return 0; +} + +/* + * FUNCTION NAME. + * fps_interrupt_re d + * + * FUNCTIONAL DESCRIPTION. + * FPS interrupt read status + * + * ENTRY PARAMETERS. + * wait poll table structure + * + * EXIT PARAMETERS. + * Function Return int + */ + +unsigned int egisfp_interrupt_poll(struct file *file, struct poll_table_struct *wait) +{ + unsigned int mask = 0; + struct egisfp_dev_t *egis_dev; + egis_dev = file->private_data; + poll_wait(file, &interrupt_waitq, wait); + if (egis_dev->fps_ints.finger_on) + { + mask |= POLLIN | POLLRDNORM; + } + else if (egis_dev->fps_ints.drdy_irq_abort == 1) + { + mask |= 0x400 | POLLRDNORM; + egis_dev->fps_ints.drdy_irq_abort = 0; + } + return mask; +} + +void egisfp_interrupt_abort(struct egisfp_dev_t *egis_dev) +{ + DEBUG_PRINT(" %s \n", __func__); + egis_dev->fps_ints.finger_on = 0; + egis_dev->fps_ints.drdy_irq_abort = 1; + wake_up_interruptible(&interrupt_waitq); +} + +/*-------------------------------------------------------------------------*/ + +static void send_navi_event(struct egisfp_dev_t *egis_dev, int nav_event) +{ + uint32_t input_event; + + switch (nav_event) + { + case NAVI_EVENT_ON: + DEBUG_PRINT(" %s : finger down \n", __func__); + input_event = EGIS_NAV_INPUT_FINGER_DOWN; + break; + case NAVI_EVENT_OFF: + DEBUG_PRINT(" %s : finger up \n", __func__); + input_event = EGIS_NAV_INPUT_FINGER_UP; + break; + case NAVI_EVENT_UP: + DEBUG_PRINT(" %s : nav swip up \n", __func__); + input_event = EGIS_NAV_INPUT_UP; + break; + case NAVI_EVENT_DOWN: + DEBUG_PRINT(" %s : nav swip down \n", __func__); + input_event = EGIS_NAV_INPUT_DOWN; + break; + case NAVI_EVENT_LEFT: + DEBUG_PRINT(" %s : nav swip left \n", __func__); + input_event = EGIS_NAV_INPUT_LEFT; + break; + case NAVI_EVENT_RIGHT: + DEBUG_PRINT(" %s : nav swip right \n", __func__); + input_event = EGIS_NAV_INPUT_RIGHT; + break; + case NAVI_EVENT_CLICK: + DEBUG_PRINT(" %s : nav finger click \n", __func__); + input_event = EGIS_NAV_INPUT_CLICK; + break; + case NAVI_EVENT_DOUBLE_CLICK: + DEBUG_PRINT(" %s : nav finger double click \n", __func__); + input_event = EGIS_NAV_INPUT_DOUBLE_CLICK; + break; + case NAVI_EVENT_LONG_PRESS: + DEBUG_PRINT(" %s : nav finger long press \n", __func__); + input_event = EGIS_NAV_INPUT_LONG_PRESS; + break; + default: + DEBUG_PRINT(" %s : unknown nav event: %d \n", __func__, nav_event); + input_event = 0; + break; + } + + if (input_event) + { + input_report_key(egis_dev->input_dev, input_event, 1); + input_sync(egis_dev->input_dev); + input_report_key(egis_dev->input_dev, input_event, 0); + input_sync(egis_dev->input_dev); + } +} + +int do_egisfp_reset(struct egisfp_dev_t *egis_dev) +{ + int ret = 0; + ret |= pinctrl_select_state(egis_dev->pinctrl, egis_dev->reset_low); + mdelay(Rst_off_delay); + ret |= pinctrl_select_state(egis_dev->pinctrl, egis_dev->reset_high); + mdelay(Rst_on_delay); + if (ret) + ERROR_PRINT(" %s : failed ret = %d \n", __func__, ret); + else + DEBUG_PRINT(" %s : reset_pin = %d \n", __func__, gpio_get_value(egis_dev->rstPin)); + return ret; +} + +int egisfp_set_screen_onoff(int on) +{ + DEBUG_PRINT(" %s \n", __func__); + + if (!g_data) + return -ENXIO; + + g_data->screen_onoff = on; + + DEBUG_PRINT(" %s screen_onoff %d \n", __func__, g_data->screen_onoff); + + return 0; +} +EXPORT_SYMBOL(egisfp_set_screen_onoff); + +int do_egisfp_power_onoff(struct egisfp_dev_t *egis_dev, struct egisfp_ioctl_cmd_t *ioctl_data) +{ + int ret = 0; +#ifdef ET721_FOD + uint32_t power_onoff = ioctl_data->int_mode; +#else + uint32_t power_onoff = ioctl_data->power_on; +#endif + + if (!egis_dev->ctrl_power) + { + INFO_PRINT(" %s : power can not control by driver \n", __func__); + return ret; + } + + DEBUG_PRINT(" %s : power_onoff = %d \n", __func__, power_onoff); + + if (egis_dev->power_enable == power_onoff) + { + INFO_PRINT(" %s : duplicate set power on/off \n", __func__); + return ret; + } + + if (power_onoff) + { + if (egis_dev->pwr_by_gpio) + ret = pinctrl_select_state(egis_dev->pinctrl, egis_dev->vcc_high); + else + ret = regulator_enable(egis_dev->vcc); + + if (ret) + goto do_egisfp_power_onoff_failed; +#ifdef ET721_FOD + mdelay(Tpwr_on_delay); +#else + msleep(Tpwr_on_delay); +#endif + egis_dev->power_enable = 1; + } + else + { + if (egis_dev->pwr_by_gpio) + ret = pinctrl_select_state(egis_dev->pinctrl, egis_dev->vcc_low); + else + ret = regulator_disable(egis_dev->vcc); + + if (ret) + goto do_egisfp_power_onoff_failed; +#ifdef ET721_FOD + mdelay(Tpwr_off_delay); +#else + msleep(Tpwr_off_delay); +#endif + egis_dev->power_enable = 0; + } + if (egis_dev->pwr_by_gpio) + DEBUG_PRINT(" %s : power_pin value = %d \n", __func__, gpio_get_value(egis_dev->vcc_33v_Pin)); + else + DEBUG_PRINT(" %s : regulator enable = %d output voltage %d \n", __func__, regulator_is_enabled(egis_dev->vcc), regulator_get_voltage(egis_dev->vcc)); + + return ret; + +do_egisfp_power_onoff_failed: + ERROR_PRINT(" %s : failed ret = %d \n", __func__, ret); + return ret; +} +int do_egisfp_reset_set(struct egisfp_dev_t *egis_dev, int reset_high_low) +{ + int ret = 0; + DEBUG_PRINT(" %s : reset_high_low = %d \n", __func__, reset_high_low); + + if (reset_high_low) + { + ret = pinctrl_select_state(egis_dev->pinctrl, egis_dev->reset_high); +#ifndef ET721_FOD + mdelay(Rst_on_delay); +#endif + } + else + { + ret = pinctrl_select_state(egis_dev->pinctrl, egis_dev->reset_low); +#ifndef ET721_FOD + mdelay(Rst_off_delay); +#endif + } + + if (ret) + ERROR_PRINT(" %s : failed ret = %d \n", __func__, ret); + else + DEBUG_PRINT(" %s : reset_pin value = %d \n", __func__, gpio_get_value(egis_dev->rstPin)); + return ret; +} + +static void egis_get_io_stus(struct egisfp_dev_t *egis_dev) +{ + INFO_PRINT(" %s \n", __func__); + if (egis_dev->ctrl_power) + { + if (egis_dev->pwr_by_gpio) + INFO_PRINT(" %s : power_pin value = %d \n", __func__, gpio_get_value(egis_dev->vcc_33v_Pin)); + else + INFO_PRINT(" %s : regulator vcc enable = %d \n", __func__, regulator_is_enabled(egis_dev->vcc)); + } + + INFO_PRINT(" %s : reset_pin value = %d irq_pin value = %d \n", __func__, gpio_get_value(egis_dev->rstPin), gpio_get_value(egis_dev->irqPin)); +} + + +long egisfp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int retval = 0; + struct egisfp_dev_t *egis_dev = NULL; + struct egisfp_ioctl_cmd_t data = {0}; + + INFO_PRINT(" %s : cmd = 0x%X \n", __func__, cmd); + egis_dev = (struct egisfp_dev_t *)filp->private_data; + + if (!egis_dev->pars_dtsi_done) + { + ERROR_PRINT(" %s : egis_dev is NULL \n", __func__); + return -ENODEV; + } + + if (egisfp_check_ioctl_permission(egis_dev, cmd)) + return -EACCES; + + switch (cmd) + { + case FP_GET_RESOURCE: + DEBUG_PRINT(" %s : FP_GET_RESOURCE \n", __func__); + if (egisfp_platforminit(egis_dev)) + retval = -EIO; + break; + case FP_FREE_RESOURCE: + DEBUG_PRINT(" %s : FP_FREE_RESOURCE \n", __func__); + egisfp_platformfree(egis_dev); + break; + case FP_SET_SPI_CLOCK: + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + return -EFAULT; + } + egis_dev->clk_speed = data.int_mode * 1000000; + DEBUG_PRINT(" %s : FP_SET_SPI_CLOCK %d \n", __func__, egis_dev->clk_speed); + break; + case INT_TRIGGER_INIT: + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + return -EFAULT; + } + retval = egisfp_interrupt_init(egis_dev, data.int_mode, data.detect_period, data.detect_threshold); + DEBUG_PRINT(" %s : INT_TRIGGER_INIT %x \n", __func__, retval); + break; + case FP_SENSOR_RESET: + DEBUG_PRINT(" %s : FP_SENSOR_RESET \n", __func__); + do_egisfp_reset(egis_dev); + break; + case FP_POWER_ONOFF: + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + return -EFAULT; + } + DEBUG_PRINT(" %s : FP_POWER_ONOFF \n", __func__); + retval = do_egisfp_power_onoff(egis_dev, &data); + break; + case FP_RESET_SET: + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + return -EFAULT; + } + DEBUG_PRINT(" %s : FP_RESET_SET \n", __func__); + retval = do_egisfp_reset_set(egis_dev, data.int_mode); // Use data.int_mode as reset setting. 1 = on, 0 = off. + break; + case FP_WAKELOCK_ENABLE: + DEBUG_PRINT(" %s : FP_WAKELOCK_ENABLE \n", __func__); + pm_stay_awake(&egis_dev->dd->dev); + break; + case FP_WAKELOCK_DISABLE: + DEBUG_PRINT(" %s : FP_WAKELOCK_DISABLE \n", __func__); + pm_relax(&egis_dev->dd->dev); + break; + case GET_IO_STUS: + DEBUG_PRINT(" %s : GET_IO_STUS \n", __func__); + egis_get_io_stus(egis_dev); + break; + case SEND_NAVI_EVENT: + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + return -EFAULT; + } + DEBUG_PRINT(" %s : SEND_NAVI_EVENT \n", __func__); + send_navi_event(egis_dev, data.int_mode); + break; + case INT_TRIGGER_CLOSE: + retval = egisfp_interrupt_free(egis_dev); + DEBUG_PRINT(" %s : INT_TRIGGER_CLOSE %d \n", __func__, retval); + break; + case INT_TRIGGER_ABORT: + DEBUG_PRINT(" %s : INT_TRIGGER_ABORT \n", __func__); + egisfp_interrupt_abort(egis_dev); + break; + case FP_FREE_GPIO: + DEBUG_PRINT(" %s : FP_FREE_GPIO \n", __func__); + break; + case FP_WAKELOCK_TIMEOUT_ENABLE: //0Xb1 + if (copy_from_user(&data, (int __user *)arg, sizeof(data))) + { + data.int_mode = WAKE_HOLD_TIME; + } + if (data.int_mode == 0) + data.int_mode = WAKE_HOLD_TIME; + + DEBUG_PRINT(" %s : FP_WAKELOCK_TIMEOUT_ENABLE %d ms \n", __func__, data.int_mode); + pm_wakeup_event(&egis_dev->dd->dev, data.int_mode); + break; + case FP_WAKELOCK_TIMEOUT_DISABLE: //0Xb2 + DEBUG_PRINT(" %s : FP_WAKELOCK_TIMEOUT_DISABLE \n", __func__); + break; + case DELETE_DEVICE_NODE: + DEBUG_PRINT(" %s : DELETE_DEVICE_NODE \n", __func__); + //delete_device_node(); + break; + case GET_SCREEN_ONOFF: + DEBUG_PRINT(" %s : GET_SCREEN_ONOFF \n", __func__); + data.int_mode = egis_dev->screen_onoff; + if (copy_to_user((int __user *)arg, &data, sizeof(data))) + { + return -EFAULT; + } + break; + default: + retval = 0; + break; + } + DEBUG_PRINT(" %s done \n", __func__); + return retval; +} + +#define egisfp_compat_ioctl NULL + +int egisfp_open(struct inode *inode, struct file *filp) +{ + struct egisfp_dev_t *egis_dev = NULL; + int status = -ENXIO; + DEBUG_PRINT(" %s \n", __func__); + + mutex_lock(&device_list_lock); + list_for_each_entry(egis_dev, &device_list, device_entry) + { + if (egis_dev->devt == inode->i_rdev) + { + status = 0; + break; + } + } + + if (status == 0) + { + /* device tree call */ + if (egis_dev->dd->dev.of_node && !egis_dev->pars_dtsi_done) + { + status = egisfp_parse_dt(egis_dev); + } + + if (!status) + { + egis_dev->pars_dtsi_done = 1; + egis_dev->users++; + filp->private_data = egis_dev; + nonseekable_open(inode, filp); + INFO_PRINT(" %s : open ioctl user count = %d \n", __func__, egis_dev->users); + } + else + { + ERROR_PRINT(" %s : egisfp_parse_dt fail, status = %d \n", __func__, status); + } + } + else + { + ERROR_PRINT(" %s : nothing for minor %d \n", __func__, iminor(inode)); + } + mutex_unlock(&device_list_lock); + return status; +} + +int egisfp_release(struct inode *inode, struct file *filp) +{ + struct egisfp_dev_t *egis_dev; + DEBUG_PRINT(" %s \n", __func__); + mutex_lock(&device_list_lock); + egis_dev = filp->private_data; + filp->private_data = NULL; + /* last close? */ + egis_dev->users--; + if (egis_dev->users == 0) + { + INFO_PRINT(" %s : egis_dev->users == %d \n", __func__, egis_dev->users); + egisfp_platformfree(egis_dev); + } + mutex_unlock(&device_list_lock); + return 0; +} +int egisfp_platformfree(struct egisfp_dev_t *egis_dev) +{ + int status = 0; + struct egisfp_ioctl_cmd_t ioctl_data = {0}; + DEBUG_PRINT(" %s : enter \n", __func__); + if (egis_dev->platforminit_done != 1) + return status; + if (egis_dev != NULL) + { + if (egis_dev->request_irq_done == 1) + { + if (egis_dev->fps_ints.drdy_irq_flag == DRDY_IRQ_ENABLE) + { + disable_irq_nosync(egis_dev->gpio_irq); + egis_dev->fps_ints.drdy_irq_flag = DRDY_IRQ_DISABLE; + } + free_irq(egis_dev->gpio_irq, egis_dev); + egis_dev->request_irq_done = 0; + } + status = do_egisfp_reset_set(egis_dev, 0); + + if (egis_dev->power_enable && egis_dev->ctrl_power) + status = do_egisfp_power_onoff(egis_dev, &ioctl_data); + + gpio_free(egis_dev->irqPin); + gpio_free(egis_dev->rstPin); + if (egis_dev->pwr_by_gpio) + gpio_free(egis_dev->vcc_33v_Pin); + + if (egis_dev->vcc) + { + DEBUG_PRINT(" %s : devm_regulator_put \n", __func__); + devm_regulator_put(egis_dev->vcc); + egis_dev->vcc = NULL; + } + + + if (egis_dev->pinctrl) + { + DEBUG_PRINT(" %s : devm_pinctrl_put \n", __func__); + devm_pinctrl_put(egis_dev->pinctrl); + egis_dev->pinctrl = NULL; + } + } + egis_dev->platforminit_done = 0; + DEBUG_PRINT(" %s : successful status = %d \n", __func__, status); + return status; +} + +int egisfp_platforminit(struct egisfp_dev_t *egis_dev) +{ + int status; + INFO_PRINT(" %s : Version %s \n", __func__, DRIVER_VERSION); + if (egis_dev != NULL) + { + if (!egis_dev->platforminit_done) + { + if (egis_dev->ctrl_power) + { + if (egis_dev->pwr_by_gpio) + { + /* initial 33V power pin */ + status = gpio_request(egis_dev->vcc_33v_Pin, "egis_dev-33v-gpio"); + if (status < 0) + { + ERROR_PRINT(" %s : gpio_requset vcc_33v_Pin pin failed \n", __func__); + goto egisfp_power_request_fail; + } + + status = gpio_direction_output(egis_dev->vcc_33v_Pin, 0); + } + else + { + egis_dev->vcc = devm_regulator_get(&egis_dev->dd->dev, "vcc_fp"); + if (IS_ERR(egis_dev->vcc)) + { + status = PTR_ERR(egis_dev->vcc); + ERROR_PRINT(" %s : get vcc regulator failed %d \n", __func__, status); + goto egisfp_power_request_fail; + } + + if (regulator_count_voltages(egis_dev->vcc) > 0) + { + status = regulator_set_voltage(egis_dev->vcc, egis_dev->regulator_voltage_min, egis_dev->regulator_voltage_max); + if (status) + { + ERROR_PRINT(" %s : set vcc regulator voltage failed %d \n", __func__, status); + goto egisfp_power_setup_fail; + } + + status = regulator_set_load(egis_dev->vcc, egis_dev->regulator_current); + if (status) + { + ERROR_PRINT(" %s : set vcc regulator current failed %d \n", __func__, status); + goto egisfp_power_setup_fail; + } + } + } + } + + /* Initial Reset Pin */ + status = gpio_request(egis_dev->rstPin, "egis_dev-reset-gpio"); + if (status < 0) + { + ERROR_PRINT(" %s : gpio_requset reset pin failed \n", __func__); + goto egisfp_reset_request_fail; + } + + status = gpio_direction_output(egis_dev->rstPin, 0); + + status = gpio_request(egis_dev->irqPin, "egis_dev-irq-gpio"); + if (status < 0) + { + ERROR_PRINT(" %s : gpio_requset irq pin failed \n", __func__); + goto egisfp_irq_request_fail; + } + + gpio_direction_input(egis_dev->irqPin); + + if (egis_dev->dd) + { + INFO_PRINT(" %s : find node enter \n", __func__); + egis_dev->pinctrl = devm_pinctrl_get(&egis_dev->dd->dev); + if (IS_ERR(egis_dev->pinctrl)) + { + status = PTR_ERR(egis_dev->pinctrl); + ERROR_PRINT(" %s : can't find fingerprint pinctrl \n", __func__); + goto egisfp_pinctrl_fail; + } + egis_dev->reset_high = pinctrl_lookup_state(egis_dev->pinctrl, "egis_rst_high"); + if (IS_ERR(egis_dev->reset_high)) + { + status = PTR_ERR(egis_dev->reset_high); + ERROR_PRINT(" %s : can't find fingerprint pinctrl egis_rst_high \n", __func__); + goto egisfp_pinctrl_fail; + } + egis_dev->reset_low = pinctrl_lookup_state(egis_dev->pinctrl, "egis_rst_low"); + if (IS_ERR(egis_dev->reset_low)) + { + status = PTR_ERR(egis_dev->reset_low); + ERROR_PRINT(" %s : can't find fingerprint pinctrl egis_rst_low \n", __func__); + goto egisfp_pinctrl_fail; + } + if (pinctrl_select_state(egis_dev->pinctrl, egis_dev->reset_low)) + goto egisfp_pinctrl_fail; + egis_dev->irq_active = pinctrl_lookup_state(egis_dev->pinctrl, "egis_irq_active"); + if (IS_ERR(egis_dev->irq_active)) + { + status = PTR_ERR(egis_dev->irq_active); + ERROR_PRINT(" %s : can't find fingerprint pinctrl egis_irq_active \n", __func__); + goto egisfp_pinctrl_fail; + } + if (pinctrl_select_state(egis_dev->pinctrl, egis_dev->irq_active)) + goto egisfp_pinctrl_fail; + if (egis_dev->pwr_by_gpio && egis_dev->ctrl_power) + { + egis_dev->vcc_high = pinctrl_lookup_state(egis_dev->pinctrl, "egis_vcc_high"); + if (IS_ERR(egis_dev->vcc_high)) + { + status = PTR_ERR(egis_dev->vcc_high); + ERROR_PRINT(" %s : can't find fingerprint pinctrl egis_vcc_high \n", __func__); + goto egisfp_pinctrl_fail; + } + egis_dev->vcc_low = pinctrl_lookup_state(egis_dev->pinctrl, "egis_vcc_low"); + if (IS_ERR(egis_dev->vcc_low)) + { + status = PTR_ERR(egis_dev->vcc_low); + ERROR_PRINT(" %s : can't find fingerprint pinctrl egis_vcc_low \n", __func__); + goto egisfp_pinctrl_fail; + } + if (pinctrl_select_state(egis_dev->pinctrl, egis_dev->vcc_low)) + goto egisfp_pinctrl_fail; + } + } + + + + egis_dev->platforminit_done = 1; + if (egis_dev->pwr_by_gpio && egis_dev->ctrl_power) + INFO_PRINT(" %s : successful status = %d gpio num -> vcc = %d rst = %d Irq = %d \n", __func__, status, egis_dev->vcc_33v_Pin, egis_dev->rstPin, egis_dev->irqPin); + else + INFO_PRINT(" %s : successful status = %d gpio num -> rst = %d Irq = %d \n", __func__, status, egis_dev->rstPin, egis_dev->irqPin); + } + else + { + INFO_PRINT(" %s : platform already init \n", __func__); + } + return 0; + } + else + { + ERROR_PRINT(" %s : device node is null \n", __func__); + return -ENODEV; + } +egisfp_pinctrl_fail: + ERROR_PRINT(" %s : devm_pinctrl_put \n", __func__); + devm_pinctrl_put(egis_dev->pinctrl); + egis_dev->pinctrl = NULL; + gpio_free(egis_dev->irqPin); +egisfp_irq_request_fail: + gpio_free(egis_dev->rstPin); +egisfp_reset_request_fail: +egisfp_power_setup_fail: + if (egis_dev->ctrl_power) + { + if (egis_dev->pwr_by_gpio) + gpio_free(egis_dev->vcc_33v_Pin); + else + devm_regulator_put(egis_dev->vcc); + } +egisfp_power_request_fail: + return -EIO; +} + +int egisfp_check_ioctl_permission(struct egisfp_dev_t *egis_dev, unsigned int cmd) +{ + + if (cmd == FP_GET_RESOURCE || cmd == FP_FREE_RESOURCE) + { + return 0; + } + else if (egis_dev->platforminit_done) + { + return 0; + } + else + { + ERROR_PRINT(" %s : need get resource fisrt \n", __func__); + return -EACCES; + } +} +#if defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) +static int drm_check_dt(struct egisfp_dev_t *egis_dev) +{ + int i = 0; + int count = 0; + struct device_node *np = egis_dev->dd->dev.of_node; + struct device_node *node = NULL; + struct drm_panel *panel = NULL; + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) { + ERROR_PRINT(" %s : find drm_panel fail count = %d ", __func__, count); + return -ENODEV; + } + 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)) { + ERROR_PRINT(" %s : ind drm_panel successfully ", __func__); + egis_dev->active_panel = panel; + return 0; + } + } + ERROR_PRINT(" %s : can not find drm_panel ", __func__); + return -ENODEV; +} +#endif +// Register FB notifier +++ +static int egisfp_fb_callback(struct notifier_block *nb, unsigned long val, void *data) +{ + struct egisfp_dev_t *egis_dev = NULL; + char *envp[2]; + int ret = 0; +#if defined(CONFIG_PANEL_NOTIFICATIONS) + INFO_PRINT(" %s : got notify value = %d \n", __func__, (int)val); + egis_dev = container_of(nb, struct egisfp_dev_t, notifier); + egis_dev->screen_onoff = 1; + switch(val) { + case PANEL_EVENT_DISPLAY_ON: + egis_dev->screen_onoff = 1; + envp[0] = "PANEL=1"; + break; + case PANEL_EVENT_DISPLAY_OFF: + egis_dev->screen_onoff = 0; + envp[0] = "PANEL=0"; + break; + case PANEL_EVENT_PRE_DISPLAY_OFF: + break; + case PANEL_EVENT_PRE_DISPLAY_ON: + break; + default: + break; + } + if(val == FP_NOTIFY_ON ||val == FP_NOTIFY_OFF){ + envp[1] = NULL; + ret = kobject_uevent_env(&egis_dev->dd->dev.kobj, KOBJ_CHANGE, envp); + INFO_PRINT(" %s : screen_onoff = %d val=%lu ret=%d\n", __func__, egis_dev->screen_onoff, val,ret); + } +#elif defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) + struct drm_panel_notifier *evdata = data; + unsigned int blank; + INFO_PRINT(" %s : got notify value = %d \n", __func__, (int)val); + if (val != DRM_PANEL_EARLY_EVENT_BLANK) + { + return NOTIFY_OK; + } + egis_dev = container_of(nb, struct egisfp_dev_t, notifier); + if (!evdata || !evdata->data || !egis_dev) { + ERROR_PRINT(" %s : some parameters is null \n", __func__); + return NOTIFY_OK; + } + blank = *(int *)(evdata->data); + switch (blank) { + case DRM_PANEL_BLANK_UNBLANK: + egis_dev->screen_onoff = 1; + envp[0] = "PANEL=1"; + break; + case DRM_PANEL_BLANK_POWERDOWN: + egis_dev->screen_onoff = 0; + envp[0] = "PANEL=0"; + break; + default: + break; + } + INFO_PRINT(" %s : screen_onoff = %d \n", __func__, egis_dev->screen_onoff); + envp[1] = NULL; + ret = kobject_uevent_env(&egis_dev->dd->dev.kobj, KOBJ_CHANGE, envp); +#else + struct fb_event *evdata = data; + unsigned int blank; + + if (val != FP_NOTIFY_EVENT_BLANK) + return 0; + + INFO_PRINT(" %s : got notify value = %d \n", __func__, (int)val); + egis_dev = container_of(nb, struct egisfp_dev_t, notifier); + if (evdata && evdata->data && val == FB_EVENT_BLANK && egis_dev) { + blank = *(int *)(evdata->data); + INFO_PRINT(" %s : blank value = %d \n", __func__, (int)blank); + switch (blank) { + case FP_NOTIFY_OFF: + egis_dev->screen_onoff = 0; + envp[0] = "PANEL=0"; + break; + case FP_NOTIFY_ON: + egis_dev->screen_onoff = 1; + envp[0] = "PANEL=1"; + break; + default: + break; + } + INFO_PRINT(" %s : screen_onoff = %d \n", __func__, egis_dev->screen_onoff); + envp[1] = NULL; + ret = kobject_uevent_env(&egis_dev->dd->dev.kobj, KOBJ_CHANGE, envp); + } +#endif + return NOTIFY_OK; +} + +static struct notifier_block egisfp_noti_block = { + .notifier_call = egisfp_fb_callback, +}; +// Register FB notifier --- + +int egisfp_parse_dt(struct egisfp_dev_t *egis_dev) +{ + int ret; + struct device_node *node = egis_dev->dd->dev.of_node; + u32 voltage_supply[2]; + u32 current_supply; + INFO_PRINT(" %s : start \n", __func__); + + if (node) + { + egis_dev->ctrl_power = of_property_read_bool(node, "fp-ctrl-power"); + if (egis_dev->ctrl_power) + { + egis_dev->pwr_by_gpio = of_property_read_bool(node, "fp-gpio-vcc-enable"); + if (egis_dev->pwr_by_gpio) + { + egis_dev->vcc_33v_Pin = of_get_named_gpio(node, "egistec,gpio_vcc_en", 0); + INFO_PRINT(" %s : vcc_33v_pin gpio num is %d \n", __func__, egis_dev->vcc_33v_Pin); + if (!gpio_is_valid(egis_dev->vcc_33v_Pin)) + { + ERROR_PRINT(" %s : vcc_33v_pin gpio is invalid \n", __func__); + return -ENODEV; + } + } + else + { + ret = of_property_read_u32_array(node, "egis-fp,vcc-voltage", voltage_supply, 2); + if (ret < 0) + { + ERROR_PRINT(" %s : fail to get vcc regulator voltage \n", __func__); + return -ENODEV; + } + INFO_PRINT(" %s : vcc regulator voltage get Max = %d, Min = %d \n", __func__, voltage_supply[1], voltage_supply[0]); + egis_dev->regulator_voltage_max = voltage_supply[1]; + egis_dev->regulator_voltage_min = voltage_supply[0]; + + ret = of_property_read_u32_array(node, "egis-fp,vcc-current", ¤t_supply, 1); + if (ret < 0) + { + ERROR_PRINT(" %s : fail to get vcc regulator current_supply \n", __func__); + return -ENODEV; + } + INFO_PRINT(" %s : vcc regulator current get %d \n", __func__, current_supply); + egis_dev->regulator_current = current_supply; + } + } + + egis_dev->rstPin = of_get_named_gpio(node, "egistec,gpio_reset", 0); + INFO_PRINT(" %s : rst pin gpio num is %d \n", __func__, egis_dev->rstPin); + if (!gpio_is_valid(egis_dev->rstPin)) + { + ERROR_PRINT(" %s : rst pin gpio is invalid \n", __func__); + return -ENODEV; + } + + egis_dev->irqPin = of_get_named_gpio(node, "egistec,gpio_irq", 0); + INFO_PRINT(" %s : irq pin gpio num is %d \n", __func__, egis_dev->irqPin); + if (!gpio_is_valid(egis_dev->irqPin)) + { + ERROR_PRINT(" %s : irq pin gpio is invalid \n", __func__); + return -ENODEV; + } + } + else + { + ERROR_PRINT(" %s : device node is null \n", __func__); + return -ENODEV; + } + INFO_PRINT(" %s : successful \n", __func__); + + return 0; +} + +const struct file_operations egisfp_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = egisfp_ioctl, + .compat_ioctl = egisfp_compat_ioctl, + .open = egisfp_open, + .release = egisfp_release, + .llseek = no_llseek, + .poll = egisfp_interrupt_poll}; + +/*-------------------------------------------------------------------------*/ +struct class *egisfp_class; +/*-------------------------------------------------------------------------*/ + +int egisfp_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct egisfp_dev_t *egis_dev = dev_get_drvdata(dev); + INFO_PRINT(" %s : driver remove \n", __func__); + if (egis_dev->request_irq_done) + { + free_irq(egis_dev->gpio_irq, egis_dev); + } + if (egis_dev->call_back_registered) + { +#if defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) + if (egis_dev->active_panel) + { + drm_panel_notifier_unregister(egis_dev->active_panel, &egis_dev->notifier); + egis_dev->active_panel = NULL; + } +#else + ets_fb_unregister_client(&egis_dev->notifier); +#endif + } + del_timer_sync(&egis_dev->fps_ints.timer); + + device_init_wakeup(&egis_dev->dd->dev, 0); + + egis_dev->request_irq_done = 0; + + if (egis_dev->input_dev) + { + input_unregister_device(egis_dev->input_dev); + } + + device_destroy(egisfp_class, egis_dev->devt); + + list_del(&egis_dev->device_entry); + + class_destroy(egisfp_class); + + unregister_chrdev(EGIS_FP_MAJOR, egisfp_driver.driver.name); + + g_data = NULL; + return 0; +} + +int egisfp_probe(struct platform_device *pdev) +{ + struct egisfp_dev_t *egis_dev; + int status, i; + unsigned long minor; + + INFO_PRINT(" %s : driver init \n", __func__); + BUILD_BUG_ON(N_SPI_MINORS > 256); + status = register_chrdev(EGIS_FP_MAJOR, EGIS_CHRD_DRIVER_NAME, &egisfp_fops); + if (status < 0) + { + ERROR_PRINT(" %s : register_chrdev error \n", __func__); + return status; + } + egisfp_class = class_create(THIS_MODULE, EGIS_CLASS_NAME); + if (IS_ERR(egisfp_class)) + { + ERROR_PRINT(" %s : class_create error \n", __func__); + unregister_chrdev(EGIS_FP_MAJOR, egisfp_driver.driver.name); + return PTR_ERR(egisfp_class); + } + /* Allocate driver data */ + egis_dev = kzalloc(sizeof(struct egisfp_dev_t), GFP_KERNEL); + if (egis_dev == NULL) + { + ERROR_PRINT(" %s : Failed to kzalloc \n", __func__); + return -ENOMEM; + } +/* Initialize the driver data */ + dev_set_drvdata(&pdev->dev, egis_dev); + egis_dev->dd = pdev; + + + spin_lock_init(&egis_dev->irq_lock); + + device_init_wakeup(&egis_dev->dd->dev, 1); + + mutex_init(&device_list_lock); + + INIT_LIST_HEAD(&egis_dev->device_entry); + + /* init status */ + egis_dev->pars_dtsi_done = 0; + egis_dev->fps_ints.drdy_irq_flag = DRDY_IRQ_DISABLE; + egis_dev->clk_enabled = 0; + egis_dev->request_irq_done = 0; + egis_dev->platforminit_done = 0; + egis_dev->power_enable = 0; + egis_dev->screen_onoff = 0; + egis_dev->call_back_registered = 0; +#if defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) + egis_dev->active_panel = NULL; + drm_check_dt(egis_dev); +#endif + /* + * If we can allocate a minor number, hook up this device. + * Reusing minors is fine so long as udev or mdev is working. + */ + mutex_lock(&device_list_lock); + minor = find_first_zero_bit(minors, N_SPI_MINORS); + if (minor < N_SPI_MINORS) + { + struct device *fdev; + egis_dev->devt = MKDEV(EGIS_FP_MAJOR, minor); + fdev = device_create(egisfp_class, &egis_dev->dd->dev, egis_dev->devt, + egis_dev, EGIS_DEV_NAME); + status = IS_ERR(fdev) ? PTR_ERR(fdev) : 0; + } + else + { + ERROR_PRINT(" %s : no minor number available \n", __func__); + status = -ENODEV; + } + if (status == 0) + { + set_bit(minor, minors); + list_add(&egis_dev->device_entry, &device_list); + } + + mutex_unlock(&device_list_lock); + + if (status) + { + goto egistec_probe_failed; + } + + egis_dev->input_dev = input_allocate_device(); + if (egis_dev->input_dev == NULL) + { + ERROR_PRINT(" %s : failed to allocate input device \n", __func__); + status = -ENOMEM; + goto egistec_probe_failed; + } + for (i = 0; i < ARRAY_SIZE(key_maps); i++) + input_set_capability(egis_dev->input_dev, key_maps[i].type, key_maps[i].code); + + egis_dev->input_dev->name = EGIS_INPUT_NAME; + egis_dev->input_dev->id.vendor = 0x001f; + + status = input_register_device(egis_dev->input_dev); + if (status) + { + ERROR_PRINT(" %s : failed to register input device %d \n", __func__, status); + goto egistec_input_failed; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + timer_setup(&egis_dev->fps_ints.timer, egisfp_interrupt_timer_call, 0); +#else + setup_timer(&egis_dev->fps_ints.timer, egisfp_interrupt_timer_call, (unsigned long)&egis_dev->fps_ints); +#endif + + //Register display notifier +++ + egis_dev->notifier = egisfp_noti_block; +#if defined(CONFIG_DRM_PANEL_NOTIFICATIONS) && (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)) + if (egis_dev->active_panel) { + DEBUG_PRINT(" %s : register drm_panel_notifier \n", __func__); + status = drm_panel_notifier_register(egis_dev->active_panel, &egis_dev->notifier); + if (status) + ERROR_PRINT(" %s : drm_panel_notifier_register fail: %d ", __func__, status); + else + egis_dev->call_back_registered = 1; + } +#else + status = ets_fb_register_client(&egis_dev->notifier); + if (status) + ERROR_PRINT(" %s : ets_fb_register_client fail: %d ", __func__, status); + else + egis_dev->call_back_registered = 1; +#endif + + g_data = egis_dev; + + DEBUG_PRINT(" %s : initialize success %d\n", __func__, status); + + return status; + +egistec_input_failed: + if (egis_dev->input_dev != NULL) + input_free_device(egis_dev->input_dev); +egistec_probe_failed: + device_destroy(egisfp_class, egis_dev->devt); + class_destroy(egisfp_class); + kfree(egis_dev); + ERROR_PRINT(" %s : driver probe failed %d \n", __func__, status); + return status; +} + +int __init egisfp_init(void) +{ + int status; + INFO_PRINT(" %s : module init \n", __func__); + status = platform_driver_register(&egisfp_driver); + if (status) + { + ERROR_PRINT(" %s : register Egis driver fail \n", __func__); + return -EINVAL; + } + INFO_PRINT(" %s : module init OK ! \n", __func__); + return status; +} +void __exit egisfp_exit(void) +{ + INFO_PRINT("module exit \n"); + platform_driver_unregister(&egisfp_driver); +} + +module_init(egisfp_init); +module_exit(egisfp_exit); + +MODULE_DESCRIPTION("egis fingerprint driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/rbs_fps_mmi/etxxx_fp.h b/drivers/input/misc/rbs_fps_mmi/etxxx_fp.h new file mode 100644 index 000000000000..5ed47ed8d96e --- /dev/null +++ b/drivers/input/misc/rbs_fps_mmi/etxxx_fp.h @@ -0,0 +1,234 @@ +#ifndef _FP_LINUX_DIRVER_H_ +#define _FP_LINUX_DIRVER_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "20210517" +#define USE_PIN_CTRL +//#define PLATFORM_SPI +//#define MTK_PLATFORM +//#define LSI_PLATFORM +#define EGIS_DEBUG + +#define DEBUG_PRINT(fmt, arg...) \ + printk(KERN_DEBUG "[EGISFP]" fmt, ##arg) + +#define INFO_PRINT(fmt, arg...) \ + printk("[EGISFP]" fmt, ##arg) +#define ERROR_PRINT(fmt, arg...) \ + printk(KERN_ERR "[EGISFP]" fmt, ##arg) + +#define EGIS_FP_MAJOR 100 /* assigned */ +#define N_SPI_MINORS 32 /* ... up to 256 */ + +#define EGIS_DEV_NAME "esfp0" +#define EGIS_INPUT_NAME "uinput-egis" /*"uinput-egis" */ + +#define EGIS_CHRD_DRIVER_NAME "egis_fp" +#define EGIS_CLASS_NAME "egis_fp" + +/* ------------------------- Opcode -------------------------------------*/ +#define FP_REGISTER_READ 0x01 +#define FP_REGISTER_WRITE 0x02 +#define FP_GET_ONE_IMG 0x03 +#define FP_SENSOR_RESET 0x04 +#define FP_POWER_ONOFF 0x05 +#define FP_SET_SPI_CLOCK 0x06 +#define FP_RESET_SET 0x07 +#define FP_WAKELOCK_ENABLE 0x08 +#define FP_WAKELOCK_DISABLE 0x09 +#define FP_GET_RESOURCE 0x11 +#define FP_FREE_RESOURCE 0x12 + +#define GET_IO_STUS 0x21 +#define SEND_NAVI_EVENT 0x31 +/* trigger signal initial routine*/ +#define INT_TRIGGER_INIT 0xa4 +/* trigger signal close routine*/ +#define INT_TRIGGER_CLOSE 0xa5 +/* read trigger status*/ +#define INT_TRIGGER_READ 0xa6 +/* polling trigger status*/ +#define INT_TRIGGER_POLLING 0xa7 +/* polling abort*/ +#define INT_TRIGGER_ABORT 0xa8 + +#define FP_FREE_GPIO 0xaf +#define FP_SPICLK_ENABLE 0xaa +#define FP_SPICLK_DISABLE 0xab +#define DELETE_DEVICE_NODE 0xac + +#define FP_SPIPIN_SETTING 0xad +#define FP_SPIPIN_PULLLOW 0xae + +#define FP_POWERSETUP 0xb0 +#define FP_WAKELOCK_TIMEOUT_ENABLE 0xb1 +#define FP_WAKELOCK_TIMEOUT_DISABLE 0xb2 +#define GET_SCREEN_ONOFF 0xb3 + +#define DRDY_IRQ_ENABLE 1 +#define DRDY_IRQ_DISABLE 0 + +#define EDGE_TRIGGER_FALLING 0x0 +#define EDGE_TRIGGER_RISING 0x1 +#define LEVEL_TRIGGER_LOW 0x2 +#define LEVEL_TRIGGER_HIGH 0x3 +#define WAKE_HOLD_TIME 2000 //ms + +#define SPI_DEFAULT_SPEED (1000000 * 20) //20M + +#ifdef ET721_FOD +#define Tpwr_off_delay 50 +#define Tpwr_on_delay 4 +#define Rst_off_delay 4 +#define Rst_on_delay 6 +#else +#define Tpwr_off_delay 3 +#define Tpwr_on_delay 3 +#define Rst_off_delay 3 +#define Rst_on_delay 12 +#endif + +#define KEY_FPS_DOWN 614 +#define KEY_FPS_UP 615 +#define KEY_FPS_TAP 616 +#define KEY_FPS_HOLD 617 +#define KEY_FPS_YPLUS 618 +#define KEY_FPS_YMINUS 619 +#define KEY_FPS_XPLUS 620 +#define KEY_FPS_XMINUS 621 +#define KEY_FPS_DOUBLE_TAP 622 + +#define EGIS_NAV_INPUT_UP KEY_FPS_YPLUS +#define EGIS_NAV_INPUT_DOWN KEY_FPS_YMINUS +#define EGIS_NAV_INPUT_LEFT KEY_FPS_XMINUS +#define EGIS_NAV_INPUT_RIGHT KEY_FPS_XPLUS +#define EGIS_NAV_INPUT_CLICK KEY_FPS_TAP +#define EGIS_NAV_INPUT_DOUBLE_CLICK KEY_FPS_DOUBLE_TAP +#define EGIS_NAV_INPUT_LONG_PRESS KEY_HOME +#define EGIS_NAV_INPUT_FINGER_DOWN KEY_FPS_DOWN +#define EGIS_NAV_INPUT_FINGER_UP KEY_FPS_UP + + + +enum egis_navi_event +{ + NAVI_EVENT_ON, + NAVI_EVENT_OFF, + NAVI_EVENT_UP, + NAVI_EVENT_DOWN, + NAVI_EVENT_LEFT, + NAVI_EVENT_RIGHT, + NAVI_EVENT_CLICK, + NAVI_EVENT_DOUBLE_CLICK, + NAVI_EVENT_LONG_PRESS, +}; + +struct egis_key_map_t +{ + unsigned int type; + unsigned int code; +}; + +struct egisfp_interrupt_desc_t +{ + int gpio; + int number; + char *name; + int int_count; + struct timer_list timer; + int finger_on; + int detect_period; + int detect_threshold; + int drdy_irq_flag; + int drdy_irq_abort; +}; + +struct egisfp_ioctl_cmd_t +{ + uint32_t int_mode; + uint32_t detect_period; + uint32_t detect_threshold; + uint32_t power_on; +}; + +/* ------------------------- Structure ------------------------------*/ +struct egisfp_dev_t +{ + dev_t devt; + spinlock_t spi_lock; + struct platform_device *dd; + struct list_head device_entry; + /* buffer is NULL unless this device is open (users > 0) */ + struct mutex buf_lock; + unsigned users; + u8 *buffer; + unsigned int irqPin; /* interrupt GPIO pin number */ + unsigned int rstPin; /* Reset GPIO pin number */ + unsigned int vdd_18v_Pin; /* Vdd GPIO pin number */ + unsigned int vcc_33v_Pin; /* Vcc GPIO pin number */ + struct input_dev *input_dev; + int navigation_enable; + int pars_dtsi_done; + int platforminit_done; + int gpio_irq; + spinlock_t irq_lock; + int request_irq_done; + struct wakeup_source wakeup_source_fp; + int ctrl_power; + int pwr_by_gpio; + int power_enable; + int screen_onoff; + int call_back_registered; + struct regulator *vcc; + struct regulator *vdd; + uint32_t regulator_current; + uint32_t regulator_voltage_max; + uint32_t regulator_voltage_min; + int clk_enabled; + struct egisfp_interrupt_desc_t fps_ints; + int clk_speed; + struct pinctrl *pinctrl; + struct pinctrl_state *spi_active; + struct pinctrl_state *spi_default; + struct pinctrl_state *reset_high; + struct pinctrl_state *reset_low; + struct pinctrl_state *vcc_high; + struct pinctrl_state *vcc_low; + struct pinctrl_state *irq_active; + struct notifier_block notifier; +#if defined(CONFIG_DRM_PANEL) && LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + struct drm_panel *active_panel; +#endif +}; + +int egisfp_platforminit(struct egisfp_dev_t *egis_dev); +int egisfp_platformfree(struct egisfp_dev_t *egis_dev); +/* interrupt init */ +int egisfp_interrupt_init(struct egisfp_dev_t *egis_dev, int int_mode, int detect_period, int detect_threshold); +/* interrupt free */ +int egisfp_interrupt_free(struct egisfp_dev_t *egis_dev); +void egisfp_interrupt_abort(struct egisfp_dev_t *egis_dev); +unsigned int egisfp_interrupt_poll(struct file *file, struct poll_table_struct *wait); +int egisfp_parse_dt(struct egisfp_dev_t *egis_dev); +int do_egisfp_power_onoff(struct egisfp_dev_t *egis_dev, struct egisfp_ioctl_cmd_t *ioctl_data); +int do_egisfp_reset(struct egisfp_dev_t *egis_dev); +int do_egisfp_reset_set(struct egisfp_dev_t *egis_dev, int reset_high_low); +int egisfp_check_ioctl_permission(struct egisfp_dev_t *egis_dev, unsigned int cmd); +int egisfp_set_screen_onoff(int on); +#endif diff --git a/drivers/input/misc/rbs_fps_mmi/generator_condition.txt b/drivers/input/misc/rbs_fps_mmi/generator_condition.txt new file mode 100644 index 000000000000..eedc32c66173 --- /dev/null +++ b/drivers/input/misc/rbs_fps_mmi/generator_condition.txt @@ -0,0 +1 @@ +lenovo;ET528;ET5XX;QCOM_PLATFORM;USE_PIN_CTRL;EGIS_DEBUG diff --git a/drivers/input/misc/silead_fps_mmi/Android.mk b/drivers/input/misc/silead_fps_mmi/Android.mk new file mode 100644 index 000000000000..e078d6439738 --- /dev/null +++ b/drivers/input/misc/silead_fps_mmi/Android.mk @@ -0,0 +1,15 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +SILEAD_FPS_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +SILEAD_FPS_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := silead_fps_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SILEAD_FPS_MMI_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/misc/silead_fps_mmi/Kbuild b/drivers/input/misc/silead_fps_mmi/Kbuild new file mode 100644 index 000000000000..4a1f8fd9bb75 --- /dev/null +++ b/drivers/input/misc/silead_fps_mmi/Kbuild @@ -0,0 +1,21 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +ifdef CONFIG_ARCH_MSM + ccflags-y += -DBSP_SIL_PLAT_QCOM +endif + +ifdef CONFIG_ARCH_QCOM + ccflags-y += -DBSP_SIL_PLAT_QCOM +endif + +ifdef CONFIG_ARCH_SPRD + ccflags-y += -DBSP_SIL_PLAT_SPRD +endif + +silead_fps_mmi-y += silead_fp.o + + +obj-m += silead_fps_mmi.o + diff --git a/drivers/input/misc/silead_fps_mmi/Makefile b/drivers/input/misc/silead_fps_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/misc/silead_fps_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/misc/silead_fps_mmi/silead_fp.c b/drivers/input/misc/silead_fps_mmi/silead_fp.c new file mode 100644 index 000000000000..0c6fd62bf935 --- /dev/null +++ b/drivers/input/misc/silead_fps_mmi/silead_fp.c @@ -0,0 +1,1552 @@ +/* + * @file silead_fp_platform.c + * @brief Contains silead_fp device implementation. + * + * + * Copyright 2016-2021 Gigadevice/Silead Inc. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ------------------- Revision History ------------------------------ + * + * Bill Yu 2018/4/2 0.1.6 Init version + * Bill Yu 2018/5/2 0.1.7 Fix compile error for some platform + * Bill Yu 2018/5/20 0.1.8 Default wait 3ms after reset + * Bill Yu 2018/5/28 0.1.9 Support poll/read if netlink id = 0 + * Bill Yu 2018/6/1 0.2.0 Support wakelock + * Bill Yu 2018/6/5 0.2.1 Support chip enter power down + * Bill Yu 2018/6/7 0.2.2 Support create proc node + * Bill Yu 2018/6/27 0.2.3 Expand pwdn I/F + * Bill Yu 2018/8/5 0.2.4 Support TP Up/Down I/F + * Bill Yu 2018/12/5 0.2.5 Add Spreadtrum platform support + * Melvin 2019/03/18 0.2.6 Support dynamci spi + * Taobb 2019/4/8 0.2.7 Support qcom early event + * Taobb 2019/6/6 0.2.8 Support pin irq func change to pin reset + * Bill Yu 2019/8/10 0.2.9 Fix crash while parse dts fail + * Bill Yu 2019/8/20 0.3.0 Fix crash while g_fp_dev is invalid after resource deinit called. + * Rui Wu 2019/10/10 0.3.1 Support create class node + * Bill Yu 2020/5/11 0.3.2 Fix compile warning + * Bill Yu 2020/5/29 0.3.3 Adjust poll/read code + * Bill Yu 2020/6/8 0.3.4 Fix poll/read return unpredict value if dev_reinit + * Bill Yu 2020/12/16 0.3.5 Allow GPIO ID number to be zero + * Melvin cao 2021/01/29 0.3.6 Support combo spi+silead_fp drivers + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 5, 0)) +#include +#endif +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#else +#include +#endif + +#include "silead_fp.h" + +#define FP_DEV_NAME "silead_fp" +#define FP_DEV_MAJOR 0 /* assigned */ + +#define FP_CLASS_NAME "silead_fp" +#define FP_INPUT_NAME "fp-keys" + +#define FP_DEV_VERSION "v0.3.6" + +#define BSP_SIL_IRQ_ASYNC /* IRQ use asynchrous mode. */ + +#define KEY_FPS_TAP 616 +#define KEY_FPS_HOLD 617 +#define KEY_FPS_YPLUS 618 +#define KEY_FPS_YMINUS 619 +#define KEY_FPS_XPLUS 620 +#define KEY_FPS_XMINUS 621 +#define KEY_FPS_DOUBLE_TAP 622 + +#ifndef BSP_SIL_NETLINK +struct silfp_msg_list { + unsigned char msg; + struct list_head list; +}; +#endif /* !BSP_SIL_NETLINK */ + +struct silfp_data { + dev_t devt; + struct cdev cdev; + spinlock_t spi_lock; + struct platform_device *spi; + struct list_head device_entry; + + unsigned users; + + struct device *dev; + int ref; + + struct input_dev *input; + + spinlock_t irq_lock; + int int_port; + int irq; + s32 irq_is_disable; + int irq_no_use; + int irq_ignore; + s32 power_is_off; + int rst_port; + struct work_struct work; + struct completion done; + struct wake_lock wakelock; + struct wake_lock wakelock_hal; + +#ifdef BSP_SIL_NETLINK + /* for netlink use */ + struct sock *nl_sk; +#else + spinlock_t read_lock; + wait_queue_head_t read_queue; + struct list_head msg_q; +#endif + struct fp_dev_kmap_t keymap_cust; + + int scr_off; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend es; +#else + struct notifier_block notif; +#endif /* CONFIG_HAS_EARLYSUSPEND */ + + /* for power supply */ +#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR + struct regulator *avdd_ldo; + struct regulator *vddio_ldo; +#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */ +#ifdef BSP_SIL_POWER_SUPPLY_GPIO + int avdd_port; + int vddio_port; +#endif /* BSP_SIL_POWER_SUPPLY_GPIO */ +#ifdef PROC_NODE + struct proc_dir_entry *proc_root; + struct proc_dir_entry *proc_entry; +#endif /* PROC_NODE */ + + /* for spi enable/disable */ + atomic_t spionoff_count; + + struct fp_plat_t pin; + + atomic_t init; +}; + +typedef enum _fp_spi_speet_t { + SPEED_1M=1*1000*1000, + SPEED_LOW = SPEED_1M, + SPEED_5M=5*1000*1000, + SPEED_MEDIUM = SPEED_5M, + SPEED_8M=8*1000*1000, + SPEED_9M=9*1000*1000, + SPEED_HIGH=SPEED_8M, + SPEED_10M=10*1000*1000, +} fp_spi_speet_t ; + +static struct fp_dev_init_t silfp_dev_init_d = { + .mode = 0, + .bits = 8, + .speed = SPEED_HIGH, + .delay = 100, + .dev = DEVICE, + .nl_id = SIFP_NETLINK_ROUTE, + .dev_id = 0, +}; + +/* on some platform, home key has been redefine to KEY_HOMEPAGE! */ +/* double check the define on customer board!!! + out/target/product/.../system/usr/keylayout/Generic.kl + kernel/include/uapi/linux/input.h + + KEY_HOMEPAGE + KEY_HOME + KEY_MENU + KEY_BACK + KEY_POWER + KEY_CAMERA + KEY_VOLUMEUP + KEY_VOLUMEDOWN */ + +typedef struct _key_map { + int key_orig; + int key_new; +} nav_keymap_t; + +static nav_keymap_t keymap[] = { + { NAV_KEY_UP, KEY_FPS_YPLUS, }, /* KEY_RESERVED, ignore this key */ + { NAV_KEY_DOWN, KEY_FPS_YMINUS, }, + { NAV_KEY_RIGHT, KEY_FPS_XPLUS, }, + { NAV_KEY_LEFT, KEY_FPS_XMINUS, }, + { NAV_KEY_CLICK, KEY_FPS_TAP, }, + { NAV_KEY_DCLICK, KEY_FPS_DOUBLE_TAP,}, + { NAV_KEY_LONGPRESS,KEY_FPS_HOLD, }, +}; + +static LIST_HEAD(device_list); +static DEFINE_MUTEX(device_list_lock); + +#ifdef BSP_SIL_NETLINK +static int pid; +#endif /* BSP_SIL_NETLINK */ + +#ifdef PROC_NODE +static char vendor_name[PROC_VND_ID_LEN]; +#endif /* PROC_NODE */ + +struct class *silfp_class; + +static struct workqueue_struct *silfp_wq; +struct silfp_data *g_fp_dev = NULL; + +static void silfp_hw_reset(struct silfp_data *fp_dev, u8 delay); +static void silfp_irq_disable(struct silfp_data *fp_dev); +static void silfp_irq_enable(struct silfp_data *fp_dev); +static int silfp_wait_irq(struct silfp_data *fp_dev); +static int silfp_irq_status(struct silfp_data *fp_dev); +static int silfp_resource_init(struct silfp_data *fp_dev, struct fp_dev_init_t *dev_info); +static int silfp_resource_deinit(struct silfp_data *fp_dev); +static void silfp_power_deinit(struct silfp_data *fp_dev); +static void silfp_pwdn(struct silfp_data *fp_dev, u8 flag_avdd); +static void silfp_hw_poweron(struct silfp_data *fp_dev); +static int silfp_set_feature(struct silfp_data *fp_dev, u8 feature); + +/* debug log level */ +#ifndef BSP_SIL_DYNAMIC_SPI +static +#endif +fp_debug_level_t sil_debug_level = INFO_LOG; + +#include PLAT_H + +/* -------------------------------------------------------------------- */ +/* netlink functions */ +/* -------------------------------------------------------------------- */ +#ifdef BSP_SIL_NETLINK +static void silfp_netlink_send(struct silfp_data *fp_dev, const int cmd) +{ + struct nlmsghdr *nlh = NULL; + struct sk_buff *skb = NULL; + int ret; + + LOG_MSG_DEBUG(INFO_LOG, "[%s] send cmd %d\n", __func__, cmd); + if (!fp_dev || !fp_dev->nl_sk) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] invalid socket\n", __func__); + return; + } + + if (!pid) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] invalid PID\n", __func__); + return; + } + + /*alloc data buffer for sending to native*/ + /*malloc data space at least 1500 bytes, which is ethernet data length*/ + skb = alloc_skb(NL_MSG_LEN, GFP_ATOMIC); + if (!skb) { + return; + } + + nlh = nlmsg_put(skb, 0, 0, 0, NL_MSG_LEN, 0); + if (!nlh) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] nlmsg_put() failed\n", __func__); + kfree_skb(skb); + return; + } + + NETLINK_CB(skb).portid = 0; + NETLINK_CB(skb).dst_group = 0; + + *(char *)NLMSG_DATA(nlh) = cmd; + ret = netlink_unicast(fp_dev->nl_sk, skb, pid, MSG_DONTWAIT); + if (!ret) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] send failed\n", __func__); + kfree_skb(skb); + return; + } + + LOG_MSG_DEBUG(INFO_LOG, "[%s] sent, len=%d\n", __func__, ret); +} + +static void silfp_netlink_recv(struct sk_buff *__skb) +{ + struct sk_buff *skb = NULL; + struct nlmsghdr *nlh = NULL; + char str[128]; + + skb = skb_get(__skb); + if (!skb ) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] skb NULL\n", __func__); + return; + } + + /* presume there is 5byte payload at leaset */ + if (skb->len >= NLMSG_SPACE(0)) { + nlh = nlmsg_hdr(skb); + memcpy(str, NLMSG_DATA(nlh), sizeof(str)); + if ((strlen(str) == strlen("FP")) && !strncmp(str,"FP", strlen(str))) { + pid = nlh->nlmsg_pid; + LOG_MSG_DEBUG(ERR_LOG, "[%s] PID=%d, msg=%s\n", __func__, pid, str); + } else { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Unknown PID=%d, msg=%s\n", __func__, nlh->nlmsg_pid, str); + } + } else { + LOG_MSG_DEBUG(ERR_LOG, "[%s] data len incorrect\n", __func__); + } + + kfree_skb(skb); +} + +static int silfp_netlink_init(struct silfp_data *fp_dev) +{ + struct netlink_kernel_cfg cfg; + + memset(&cfg, 0, sizeof(struct netlink_kernel_cfg)); + cfg.input = silfp_netlink_recv; + + fp_dev->nl_sk = netlink_kernel_create(&init_net, SIFP_NETLINK_ROUTE, &cfg); + if (!fp_dev->nl_sk) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] netlink create failed\n", __func__); + return -1; + } + + LOG_MSG_DEBUG(INFO_LOG, "[%s] netlink create success\n", __func__); + return 0; +} + +static int silfp_netlink_reinit(struct silfp_data *fp_dev) +{ + return 0; +} + +static int silfp_netlink_destroy(struct silfp_data *fp_dev) +{ + if (fp_dev->nl_sk != NULL) { + netlink_kernel_release(fp_dev->nl_sk); + fp_dev->nl_sk = NULL; + return 0; + } + + LOG_MSG_DEBUG(ERR_LOG, "[%s] no netlink socket\n", __func__); + return -1; +} +#else +static void silfp_netlink_send(struct silfp_data *fp_dev, const int cmd) +{ + unsigned long flags; + struct silfp_msg_list *list; + + if (!fp_dev) { + return; + } + + list = kzalloc(sizeof(*list), GFP_ATOMIC); + if (!list) { + return; + } + + list->msg = (unsigned char)cmd; + + LOG_MSG_DEBUG(INFO_LOG, "silfp_poll cmd %d\n", cmd); + spin_lock_irqsave(&fp_dev->read_lock, flags); + list_add_tail(&list->list, &fp_dev->msg_q); + spin_unlock_irqrestore(&fp_dev->read_lock, flags); + + wake_up_interruptible(&fp_dev->read_queue); +} + +static int silfp_netlink_init(struct silfp_data *fp_dev) +{ + spin_lock_init(&fp_dev->read_lock); + init_waitqueue_head(&fp_dev->read_queue); + INIT_LIST_HEAD(&fp_dev->msg_q); + + return 0; +} + +static int silfp_netlink_destroy(struct silfp_data *fp_dev) +{ + struct silfp_msg_list *list, *next; + unsigned long flags; + + if (fp_dev && (!list_empty(&fp_dev->msg_q))) { + spin_lock_irqsave(&fp_dev->read_lock, flags); + list_for_each_entry_safe(list, next, &fp_dev->msg_q, list) { + list_del(&list->list); + kfree(list); + } + spin_unlock_irqrestore(&fp_dev->read_lock, flags); + } + return 0; +} + +static int silfp_netlink_reinit(struct silfp_data *fp_dev) +{ + // Free un-read messages + return silfp_netlink_destroy(fp_dev); +} + +static unsigned int silfp_poll(struct file *fd, poll_table *wait) +{ + struct silfp_data *fp_dev; + unsigned int mask = 0; + + if (!fd) { + return -EINVAL; + } + + fp_dev = fd->private_data; + poll_wait(fd, &fp_dev->read_queue, wait); + + if (!list_empty(&fp_dev->msg_q)) { + mask |= POLLIN | POLLRDNORM; + } + + return mask; +} + +static ssize_t silfp_read(struct file *fd, char __user *buf, size_t len,loff_t *ptr) +{ + struct silfp_data *fp_dev; + struct silfp_msg_list *list; + unsigned long flags; + int32_t msglen = sizeof(char); + + if (!fd || !buf || (len < msglen)) { + return -EINVAL; + } + + fp_dev = fd->private_data; + /*if (!list_empty(&fp_dev->msg_q)) { + return -EINVAL; + }*/ + + spin_lock_irqsave(&fp_dev->read_lock, flags); + while(list_empty(&fp_dev->msg_q)) { + spin_unlock_irqrestore(&fp_dev->read_lock, flags); + if (wait_event_interruptible(fp_dev->read_queue, !list_empty(&fp_dev->msg_q)) < 0) { + return -EINVAL; + } + spin_lock_irqsave(&fp_dev->read_lock, flags); + } + /* pick the first one */ + list = list_first_entry(&fp_dev->msg_q, + struct silfp_msg_list, list); + spin_unlock_irqrestore(&fp_dev->read_lock, flags); + + if (!list) { + return -EFAULT; + } + + if (copy_to_user(buf, &list->msg, msglen)) { + LOG_MSG_DEBUG(ERR_LOG, "copy_to fail\n"); + msglen = -EFAULT; + } else { + LOG_MSG_DEBUG(INFO_LOG, "[%s] %d\n", __func__, list->msg); + } + spin_lock_irqsave(&fp_dev->read_lock, flags); + list_del(&list->list); + kfree(list); + spin_unlock_irqrestore(&fp_dev->read_lock, flags); + + return msglen; +} +#endif /* BSP_SIL_NETLINK */ + + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void silfp_early_suspend(struct early_suspend *es) +{ + struct silfp_data *fp_dev = container_of(es, struct silfp_data, es); + + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter\n", __func__); + fp_dev->scr_off = 1; + silfp_netlink_send(fp_dev, SIFP_NETLINK_SCR_OFF); +} + +static void silfp_late_resume(struct early_suspend *es) +{ + struct silfp_data *fp_dev = container_of(es, struct silfp_data, es); + + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter\n", __func__); + fp_dev->scr_off = 0; + silfp_netlink_send(fp_dev, SIFP_NETLINK_SCR_ON); +} +#else + +static int silfp_fb_callback(struct notifier_block *notif, + unsigned long event, void *data) +{ + struct silfp_data *fp_dev = container_of(notif, struct silfp_data, notif); + struct fb_event *evdata = data; + unsigned int blank; + int retval = 0; + + /* If we aren't interested in this event, skip it immediately ... */ + if (event != SIL_EVENT_BLANK /* FB_EARLY_EVENT_BLANK */) { + return 0; + } + + blank = *(int *)evdata->data; + + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter, blank=0x%x\n", __func__, blank); + + switch (blank) { + case SIL_EVENT_UNBLANK: + LOG_MSG_DEBUG(INFO_LOG, "[%s] LCD ON\n", __func__); + fp_dev->scr_off = 0; + silfp_netlink_send(fp_dev, SIFP_NETLINK_SCR_ON); + break; + + case SIL_EVENT_POWERDOWN: + LOG_MSG_DEBUG(INFO_LOG, "[%s] LCD OFF\n", __func__); + fp_dev->scr_off = 1; + silfp_netlink_send(fp_dev, SIFP_NETLINK_SCR_OFF); + break; + + default: + LOG_MSG_DEBUG(INFO_LOG, "[%s] Unknown notifier\n", __func__); + break; + } + return retval; +} +#endif /* CONFIG_HAS_EARLYSUSPEND */ + +/* -------------------------------------------------------------------- */ +/* IRQ related functions */ +/* -------------------------------------------------------------------- */ +static void silfp_irq_disable(struct silfp_data *fp_dev) +{ + unsigned long irqflags; + + spin_lock_irqsave(&fp_dev->irq_lock, irqflags); + if (!fp_dev->irq_is_disable) { + fp_dev->irq_is_disable = 1; + disable_irq_nosync(fp_dev->irq); + LOG_MSG_DEBUG(DBG_LOG, "[%s] irq disabled\n", __func__); + } + spin_unlock_irqrestore(&fp_dev->irq_lock, irqflags); +} + +static void silfp_irq_enable(struct silfp_data *fp_dev) +{ + unsigned long irqflags = 0; + + spin_lock_irqsave(&fp_dev->irq_lock, irqflags); + if (fp_dev->irq_is_disable) { + enable_irq(fp_dev->irq); + fp_dev->irq_is_disable = 0; + reinit_completion(&fp_dev->done); + LOG_MSG_DEBUG(DBG_LOG, "[%s] irq enabled\n", __func__); + } + spin_unlock_irqrestore(&fp_dev->irq_lock, irqflags); +} + +static int silfp_wait_irq(struct silfp_data *fp_dev) +{ + // this function is obsolete, for test purpose only. + wait_for_completion(&fp_dev->done); + //return wait_for_completion_timeout(&fp_dev->done, msecs_to_jiffies(3000)); + return 1; +} + +static int silfp_irq_status(struct silfp_data *fp_dev) +{ + // this function is obsolete, for test purpose only. + if (fp_dev->int_port >= 0) { + return gpio_get_value(fp_dev->int_port); + } + return -1; +} + +static irqreturn_t silfp_irq_handler(int irq, void *dev_id) +{ + struct silfp_data *fp_dev = (struct silfp_data *)dev_id; + + if (fp_dev->irq_ignore) { + return IRQ_HANDLED; + } + +#ifdef BSP_SIL_IRQ_CONFIRM + if (gpio_get_value(fp_dev->int_port)) { +#else + if (true) { +#endif /* BSP_SIL_IRQ_CONFIRM */ + wake_lock_timeout(&fp_dev->wakelock, 10*HZ); /* set a little long for a poor MCU */ +#ifdef BSP_SIL_IRQ_ASYNC + queue_work(silfp_wq, &fp_dev->work); +#else + silfp_netlink_send(fp_dev, SIFP_NETLINK_IRQ); +#endif /* BSP_SIL_IRQ_ASYNC */ + complete(&fp_dev->done); + } else { + LOG_MSG_DEBUG(INFO_LOG, "[%s] irq ignore\n", __func__); + } + + return IRQ_HANDLED; +} + +static void silfp_work_func(struct work_struct *work) +{ + struct silfp_data *fp_dev = container_of(work, struct silfp_data, work); + + LOG_MSG_DEBUG(INFO_LOG, "[%s] running\n", __func__); + silfp_netlink_send(fp_dev, SIFP_NETLINK_IRQ); +} + +/* -------------------------------------------------------------------- */ +/* key event functions */ +/* -------------------------------------------------------------------- */ +static int silfp_keyevent(struct silfp_data *fp_dev, struct fp_dev_key_t *pkey) +{ + int ret = -EFAULT; + int i; + + //LOG_MSG_DEBUG(INFO_LOG, "[%s] key %d, flag %d\n", __func__,pkey->value,pkey->flag); + if (!fp_dev->input) { + LOG_MSG_DEBUG(INFO_LOG, "[%s] invalid input device\n",__func__); + return -1; + } + if (IS_KEY_VALID(pkey->value)) { + /* Translate Click Down/Up key to Click key. */ + switch( pkey->value ) { + case NAV_KEY_CLICK_DOWN: + pkey->value = NAV_KEY_CLICK; + pkey->flag = NAV_KEY_FLAG_DOWN; + break; + case NAV_KEY_CLICK_UP: + pkey->value = NAV_KEY_CLICK; + pkey->flag = NAV_KEY_FLAG_UP; + break; + default: + break; + } + + /* Check the custom define keymap */ + if (fp_dev->keymap_cust.k[pkey->value - NAV_KEY_START]) { + LOG_MSG_DEBUG(INFO_LOG, "[%s] custom-key %d\n", __func__,fp_dev->keymap_cust.k[pkey->value - NAV_KEY_START]); + if (KEY_RESERVED != fp_dev->keymap_cust.k[pkey->value - NAV_KEY_START]) { + if (NAV_KEY_FLAG_CLICK == pkey->flag) { + input_report_key(fp_dev->input, fp_dev->keymap_cust.k[pkey->value - NAV_KEY_START], NAV_KEY_FLAG_DOWN); + input_sync(fp_dev->input); + input_report_key(fp_dev->input, fp_dev->keymap_cust.k[pkey->value - NAV_KEY_START], NAV_KEY_FLAG_UP); + input_sync(fp_dev->input); + } else { + input_report_key(fp_dev->input, fp_dev->keymap_cust.k[pkey->value - NAV_KEY_START], pkey->flag); + input_sync(fp_dev->input); + } + } else { + // Here means this key is not set, simply ignore it. + } + ret = 0; + } + } + + for (i = 0; ret && i < ARRAY_SIZE(keymap); i++) { + if (keymap[i].key_orig == pkey->value) { + LOG_MSG_DEBUG(INFO_LOG, "[%s] key orig %d key_new %d\n", __func__, + keymap[i].key_orig, keymap[i].key_new); + if (KEY_RESERVED != keymap[i].key_new) { + if (NAV_KEY_FLAG_CLICK == pkey->flag) { + input_report_key(fp_dev->input, keymap[i].key_new, NAV_KEY_FLAG_DOWN); + input_sync(fp_dev->input); + input_report_key(fp_dev->input, keymap[i].key_new, NAV_KEY_FLAG_UP); + input_sync(fp_dev->input); + } else { + input_report_key(fp_dev->input, keymap[i].key_new, pkey->flag); + input_sync(fp_dev->input); + } + } else { + // Here means this key is not set, simply ignore it. + } + ret = 0; + } + } + + if (ret) { + LOG_MSG_DEBUG(INFO_LOG, "[%s] unregister custom-key %d\n", __func__,pkey->value); + input_report_key(fp_dev->input, pkey->value, pkey->flag); + input_sync(fp_dev->input); + ret = 0; + } + return ret; +} + +/* -------------------------------------------------------------------- */ +/* proc node functions */ +/* -------------------------------------------------------------------- */ +#ifdef PROC_NODE +static int silfp_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%s\n", vendor_name); + return 0; +} + +static int silfp_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, silfp_proc_show, NULL); +} + +static const struct file_operations silfp_proc_fops = { + .owner = THIS_MODULE, + .read = seq_read, + .open = silfp_proc_open, + .release = single_release, +}; + +static int silfp_proc_create_node(struct silfp_data *fp_dev) +{ + if (!fp_dev->proc_entry) { + fp_dev->proc_root = NULL; +#ifdef PROC_DIR + fp_dev->proc_root = proc_mkdir(PROC_DIR, NULL); + if (!fp_dev->proc_root) { + LOG_MSG_DEBUG(ERR_LOG, "Create dir %s under /proc error!\n", PROC_DIR); + goto err_out; + } +#endif /* PROC_DIR */ + fp_dev->proc_entry = proc_create(PROC_NODE, 0666, fp_dev->proc_root, &silfp_proc_fops); + if (!fp_dev->proc_entry) { + LOG_MSG_DEBUG(ERR_LOG, "Create entry %s under /proc error!\n", PROC_NODE); + goto err_out1; + } + } + + return 0; + +err_out1: + remove_proc_entry(PROC_NODE, fp_dev->proc_root); +#ifdef PROC_DIR + remove_proc_entry(PROC_DIR, NULL); +err_out: +#endif /* PROC_DIR */ + fp_dev->proc_root = NULL; + fp_dev->proc_entry = NULL; + return -ENOMEM; +} + +static int silfp_proc_init(struct silfp_data *fp_dev) +{ + if (fp_dev) { + fp_dev->proc_root = NULL; + fp_dev->proc_entry = NULL; + } + return 0; + +} + +static void silfp_proc_deinit(struct silfp_data *fp_dev) +{ + if (fp_dev) { + if (fp_dev->proc_entry) { + remove_proc_entry(PROC_NODE, fp_dev->proc_root); + } +#ifdef PROC_DIR + if (fp_dev->proc_root) { + remove_proc_entry(PROC_DIR, NULL); + } +#endif /* PROC_DIR */ + fp_dev->proc_root = NULL; + fp_dev->proc_entry = NULL; + } +} +#endif /* PROC_NODE */ + + +/* -------------------------------------------------------------------- */ +/* class node functions */ +/* -------------------------------------------------------------------- */ +#ifdef CLASS_NODE +#define CLASS_NODE_NAME adm +static struct class *fp_cls = NULL; + +static ssize_t silfp_class_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", 1); +} + +static DEVICE_ATTR(CLASS_NODE_NAME, S_IRUSR|S_IRGRP|S_IROTH, silfp_class_show, NULL); + +struct file_operations silfp_class_node_ops = { + .owner = THIS_MODULE, +}; + +static int silfp_class_init(void) +{ + int ret = 0; + struct device *silfp_class_device; + fp_cls = class_create(THIS_MODULE, CLASS_NODE); + + if (IS_ERR(fp_cls)) { + LOG_MSG_DEBUG(ERR_LOG, "Failed to create class %s!\n", CLASS_NODE); + return ENOMEM; + } + silfp_class_device = device_create(fp_cls, NULL, 0, NULL, CLASS_NODE); + if (IS_ERR(silfp_class_device)) { + LOG_MSG_DEBUG(ERR_LOG, "Failed to create class device %s!\n", CLASS_NODE); + return ENOMEM; + } + ret = sysfs_create_file(&(silfp_class_device->kobj), &dev_attr_CLASS_NODE_NAME.attr); + if (ret < 0) { + LOG_MSG_DEBUG(ERR_LOG, "Failed to create class device file!\n"); + } + return ret; +} + +static void silfp_class_deinit(void) +{ + if (fp_cls) { + device_destroy(fp_cls, 0); + class_destroy(fp_cls); + fp_cls = NULL; + } +} +#endif /* CLASS_NODE */ + +/* -------------------------------------------------------------------- */ +/* init/deinit functions */ +/* -------------------------------------------------------------------- */ +static int silfp_input_init(struct silfp_data *fp_dev) +{ + int i, status = 0; + + /*register device within input system.*/ + fp_dev->input = input_allocate_device(); + if (!fp_dev->input) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] input_allocate_device() fail!\n", __func__); + status = -ENOMEM; + return status; + } + + __set_bit(EV_KEY, fp_dev->input->evbit); + //__set_bit(KEY_Q, fp_dev->input->keybit); // it will cause Android think this is a physical keyboard. + __set_bit(KEY_HOME, fp_dev->input->keybit); + __set_bit(KEY_HOMEPAGE, fp_dev->input->keybit); + + __set_bit(KEY_MENU, fp_dev->input->keybit); + __set_bit(KEY_BACK, fp_dev->input->keybit); + __set_bit(KEY_CAMERA, fp_dev->input->keybit); + + for (i = 0; i < ARRAY_SIZE(keymap); i++) { + if (keymap[i].key_new != KEY_RESERVED) { + __set_bit(keymap[i].key_new, fp_dev->input->keybit); + } + } + + for (i = 0; i < ARRAY_SIZE(fp_dev->keymap_cust.k); i++) { + if (fp_dev->keymap_cust.k[i] && (fp_dev->keymap_cust.k[i] != KEY_RESERVED)) { + __set_bit(fp_dev->keymap_cust.k[i], fp_dev->input->keybit); + } + } + + fp_dev->input->name = FP_INPUT_NAME; + if (input_register_device(fp_dev->input)) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] input_register_device() fail!\n", __func__); + input_free_device(fp_dev->input); + fp_dev->input = NULL; + status = -ENODEV; + } + return status; +} + +static int silfp_input_deinit(struct silfp_data *fp_dev) +{ + if (fp_dev->input) { + input_unregister_device(fp_dev->input); + fp_dev->input = NULL; + } + return 0; +} + +static int silfp_input_reinit(struct silfp_data *fp_dev) +{ + if (fp_dev->input) { + silfp_input_deinit(fp_dev); + } + return silfp_input_init(fp_dev); +} + +static int silfp_init(struct silfp_data *fp_dev) +{ + int status = 0; + + init_completion(&fp_dev->done); + spin_lock_init(&fp_dev->irq_lock); + INIT_WORK(&fp_dev->work, silfp_work_func); + + /* netlink interface init */ + status = silfp_netlink_init(fp_dev); + if (status == -1) { + return status; + } + +#if defined(CONFIG_HAS_EARLYSUSPEND) + LOG_MSG_DEBUG(INFO_LOG, "[%s] register_early_suspend\n", __func__); + fp_dev->es.level = (EARLY_SUSPEND_LEVEL_DISABLE_FB - 1); + fp_dev->es.suspend = silfp_early_suspend; + fp_dev->es.resume = silfp_late_resume; + register_early_suspend(&fp_dev->es); +#else + /* register screen on/off callback */ + fp_dev->notif.notifier_call = silfp_fb_callback; + SIL_REGISTER_CLIENT(&fp_dev->notif); +#endif /* CONFIG_HAS_EARLYSUSPEND */ + + atomic_set(&fp_dev->spionoff_count,0); + LOG_MSG_DEBUG(INFO_LOG, "[%s] success\n", __func__); + + return status; +} + +static int silfp_resource_deinit(struct silfp_data *fp_dev) +{ + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter\n", __func__); + + if (atomic_read(&fp_dev->init)) { + atomic_dec(&fp_dev->init); + + if (!atomic_read(&fp_dev->init)) { + LOG_MSG_DEBUG(INFO_LOG, "[%s] no more users, free GPIOs\n", __func__); + if (!fp_dev->irq_no_use) { + silfp_irq_disable(fp_dev); + free_irq(fp_dev->irq, fp_dev); + } + + gpio_direction_input(fp_dev->int_port); +#if !defined(BSP_SIL_PLAT_MTK) + gpio_free(fp_dev->int_port); + if (fp_dev->rst_port >= 0) { + gpio_free(fp_dev->rst_port); + } +#endif /* !BSP_SIL_PLAT_MTK */ + fp_dev->irq_no_use = 0; + fp_dev->int_port = -1; + fp_dev->rst_port = -1; + + silfp_input_deinit(fp_dev); + silfp_power_deinit(fp_dev); +#ifdef PROC_NODE + silfp_proc_deinit(fp_dev); +#endif /* PROC_NODE */ +#ifdef CLASS_NODE + silfp_class_deinit(); +#endif /* CLASS_NODE */ + } + silfp_netlink_send(fp_dev, SIFP_NETLINK_DISCONNECT); + } + return 0; +} + +static void silfp_exit(struct silfp_data *fp_dev) +{ + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter\n", __func__); + +#ifdef CONFIG_HAS_EARLYSUSPEND + if (fp_dev->es.suspend) { + unregister_early_suspend(&fp_dev->es); + } +#else + fb_unregister_client(&fp_dev->notif); +#endif /* CONFIG_HAS_EARLYSUSPEND */ + + //silfp_set_spi(fp_dev, false); /* release SPI resources */ + + if (silfp_wq) { + destroy_workqueue(silfp_wq); + silfp_wq = NULL; + } + silfp_netlink_destroy(fp_dev); +#ifdef PROC_NODE + silfp_proc_deinit(fp_dev); +#endif /* PROC_NODE */ +#ifdef CLASS_NODE + silfp_class_deinit(); +#endif /* CLASS_NODE */ +} + +static void silfp_wakelock_ctl(struct silfp_data *fp_dev, unsigned char lock) +{ + if (lock) { + wake_lock_timeout(&fp_dev->wakelock_hal, 10*HZ); + } else { + wake_unlock(&fp_dev->wakelock_hal); + } +} +/*-------------------------------------------------------------------------*/ + +static long +silfp_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int retval = 0; + struct silfp_data *fp_dev; + struct fp_dev_key_t key; + unsigned char delay; + + /* Check type and command number */ + if (_IOC_TYPE(cmd) != SIFP_IOC_MAGIC) { + return -ENOTTY; + } + + /* guard against device removal before, or while, + * we issue this ioctl. + */ + fp_dev = filp->private_data; + + switch (cmd) { + case SIFP_IOC_INIT: + silfp_netlink_reinit(fp_dev); + silfp_resource_init(fp_dev,&silfp_dev_init_d); + if (copy_to_user((void __user *)arg, (void *)&silfp_dev_init_d, sizeof(struct fp_dev_init_t))) { + retval = -EFAULT; + } + g_fp_dev = fp_dev; + + break; + case SIFP_IOC_DEINIT: + g_fp_dev = NULL; + silfp_resource_deinit(fp_dev); + break; + + case SIFP_IOC_RESET: + delay = RESET_TIME; + LOG_MSG_DEBUG(INFO_LOG, "[%s] chip reset\n", __func__); + if (arg) { + if (copy_from_user(&delay, (void __user *)arg, sizeof(char))) { + retval = -EFAULT; + break; + } + } + + if (fp_dev->power_is_off) { + silfp_hw_poweron(fp_dev); + mdelay(5); + } + fp_dev->irq_ignore = 1; + if (fp_dev->irq_is_disable && !fp_dev->irq_no_use) { + silfp_irq_enable(fp_dev); + silfp_hw_reset(fp_dev, delay); + silfp_irq_disable(fp_dev); + } else { + silfp_hw_reset(fp_dev, delay); + } + fp_dev->irq_ignore = 0; + break; + + case SIFP_IOC_ENABLE_IRQ: + LOG_MSG_DEBUG(DBG_LOG, "[%s] enable irq\n", __func__); + silfp_irq_enable(fp_dev); + break; + + case SIFP_IOC_DISABLE_IRQ: + LOG_MSG_DEBUG(DBG_LOG, "[%s] disable irq\n", __func__); + silfp_irq_disable(fp_dev); + break; + + case SIFP_IOC_CLR_IRQ: + LOG_MSG_DEBUG(INFO_LOG, "[%s] clear irq\n", __func__); + reinit_completion(&fp_dev->done); + break; + + case SIFP_IOC_WAIT_IRQ: + LOG_MSG_DEBUG(INFO_LOG, "[%s] wait irq\n", __func__); + retval = __put_user(silfp_wait_irq(fp_dev)?1:0, (__u8 __user *)arg); + break; + + case SIFP_IOC_IRQ_STATUS: + LOG_MSG_DEBUG(INFO_LOG, "[%s] irq status\n", __func__); + retval = __put_user((char)silfp_irq_status(fp_dev), (__u8 __user *)arg); + break; + + case SIFP_IOC_KEY_EVENT: + if (copy_from_user(&key, (struct fp_dev_key_t *)arg, sizeof(struct fp_dev_key_t))) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] copy key fail?\n",__func__); + retval = -EFAULT; + } else { + retval = silfp_keyevent(fp_dev,&key); + } + break; + + case SIFP_IOC_SCR_STATUS: + if (arg) { + LOG_MSG_DEBUG(INFO_LOG, "[IOC_SCR_STATUS] put v = %d\n",(u8)(!fp_dev->scr_off)); + retval = __put_user((u8)(!fp_dev->scr_off), (__u8 __user *)arg); + } else { + LOG_MSG_DEBUG(INFO_LOG, "[IOC_SCR_STATUS] send v = %d\n",(u8)(!fp_dev->scr_off)); + silfp_netlink_send(fp_dev, fp_dev->scr_off?SIFP_NETLINK_SCR_OFF:SIFP_NETLINK_SCR_ON); + } + break; + + case SIFP_IOC_GET_VER: + if (copy_to_user((void __user *)arg, (void *)FP_DEV_VERSION, sizeof(char)*7)) { + LOG_MSG_DEBUG(ERR_LOG, "[IOC_GET_VER] copy_to fail\n"); + retval = -EFAULT; + } + break; + + case SIFP_IOC_SET_KMAP: + if (!arg) { + retval = -EFAULT; + break; + } + if (copy_from_user(&fp_dev->keymap_cust.k, (void __user *)arg, sizeof(struct fp_dev_kmap_t))) { + LOG_MSG_DEBUG(ERR_LOG, "[IOC_SET_KMAP] copy_from fail\n"); + retval = -EFAULT; + break; + } + + if (silfp_input_reinit(fp_dev)) { + retval = -EFAULT; + } + break; + + case SIFP_IOC_ACQ_SPI: + LOG_MSG_DEBUG(INFO_LOG, "[%s] acq spi\n", __func__); + retval = silfp_set_spi(fp_dev,true); + break; + + case SIFP_IOC_RLS_SPI: + LOG_MSG_DEBUG(INFO_LOG, "[%s] release spi\n", __func__); + retval = silfp_set_spi(fp_dev,false); + break; + + case SIFP_IOC_PKG_SIZE: + if (arg) { + LOG_MSG_DEBUG(DBG_LOG, "[IOC_PKG_SIZE] %d\n",(u8)(PKG_SIZE)); + retval = __put_user((u8)(PKG_SIZE), (__u8 __user *)arg); + } + break; + + case SIFP_IOC_DBG_LEVEL: + if (arg) { + unsigned char level = 0; + if (copy_from_user(&level, (void __user *)arg, sizeof(char)) || level > (uint8_t)ALL_LOG ) { + retval = -EFAULT; + break; + } + LOG_MSG_DEBUG(ERR_LOG, "debug level %d-->%d\n",sil_debug_level,level); + retval = __put_user((u8)(sil_debug_level), (__u8 __user *)arg); + sil_debug_level = (fp_debug_level_t)level; + } + break; + + case SIFP_IOC_WAKELOCK: + if (arg) { + unsigned char lock = 0; + if (copy_from_user(&lock, (void __user *)arg, sizeof(char))) { + LOG_MSG_DEBUG(ERR_LOG, "[SIFP_IOC_WAKELOCK] copy_from fail\n"); + retval = -EFAULT; + break; + } + silfp_wakelock_ctl(fp_dev, lock); + } + break; + + case SIFP_IOC_PWDN: + if (arg) { + unsigned char flag_avdd = 0; + if (copy_from_user(&flag_avdd, (void __user *)arg, sizeof(char))) { + LOG_MSG_DEBUG(ERR_LOG, "[SIFP_IOC_PWDN] copy_from fail\n"); + retval = -EFAULT; + break; + } + silfp_pwdn(fp_dev, flag_avdd); + } else { + silfp_pwdn(fp_dev, SIFP_PWDN_NONE); + } + + break; + +#if defined(PROC_NODE) || defined(CLASS_NODE) + case SIFP_IOC_PROC_NODE: +#ifdef PROC_NODE + if (arg) { + if (copy_from_user(vendor_name, (void __user *)arg, PROC_VND_ID_LEN)) { + LOG_MSG_DEBUG(ERR_LOG, "[SIFP_IOC_PROC_NODE] copy_from fail\n"); + retval = -EFAULT; + break; + } + retval = silfp_proc_create_node(fp_dev); + } +#endif /* PROC_NODE */ +#ifdef CLASS_NODE + retval = silfp_class_init(); +#endif /* CLASS_NODE */ + break; +#endif /* PROC_NODE | CLASS_NODE */ + + case SIFP_IOC_SET_FEATURE: + if (arg) { + unsigned char feature = 0; + if (copy_from_user(&feature, (void __user *)arg, sizeof(char))) { + LOG_MSG_DEBUG(ERR_LOG, "[SIFP_IOC_SET_FEATURE] copy_from fail\n"); + retval = -EFAULT; + break; + } + silfp_set_feature(fp_dev, feature); + } + break; + + default: + LOG_MSG_DEBUG(ERR_LOG, "Unknown IOCTL %d\n", cmd); + retval = -ENOTTY; + break; + } + + return retval; +} + +int silfp_touch_event_handler(struct fp_dev_touch_info* tp_info) +{ + static uint8_t lasttouchmode = 0; + + if (!g_fp_dev || !tp_info) { + return 0; + } + + LOG_MSG_DEBUG(INFO_LOG, "tp_info %d, %d, %d, %d \n", tp_info->touch_state, tp_info->area_rate, tp_info->x, tp_info->y); + + if (tp_info->touch_state == lasttouchmode) { + return 0; + } + + if (1 == tp_info->touch_state) { + LOG_MSG_DEBUG(INFO_LOG, "touch finger down\n"); + silfp_netlink_send(g_fp_dev, SIFP_NETLINK_TP_TOUCHDOWN); + lasttouchmode = tp_info->touch_state; + } else { + LOG_MSG_DEBUG(INFO_LOG, "touch finger up\n"); + silfp_netlink_send(g_fp_dev, SIFP_NETLINK_TP_TOUCHUP); + lasttouchmode = tp_info->touch_state; + } + + if (g_fp_dev) { + wake_lock_timeout(&g_fp_dev->wakelock, 10*HZ); + } + return 0; +} +EXPORT_SYMBOL(silfp_touch_event_handler); + +#ifdef CONFIG_COMPAT +static long +silfp_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + return silfp_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#else +#define silfp_compat_ioctl NULL +#endif /* CONFIG_COMPAT */ + +static int silfp_open(struct inode *inode, struct file *filp) +{ + struct silfp_data *fp_dev; + int status = -ENXIO; + + mutex_lock(&device_list_lock); + + list_for_each_entry(fp_dev, &device_list, device_entry) { + if (fp_dev->devt == inode->i_rdev) { + status = 0; + break; + } + } + if (status == 0) { + fp_dev->users++; + filp->private_data = fp_dev; + nonseekable_open(inode, filp); + } else { + LOG_MSG_DEBUG(ERR_LOG, "silfp: nothing for minor %d\n", iminor(inode)); + } + + mutex_unlock(&device_list_lock); + + return status; +} + +static int silfp_release(struct inode *inode, struct file *filp) +{ + struct silfp_data *fp_dev; + int status = 0; + + mutex_lock(&device_list_lock); + fp_dev = filp->private_data; + filp->private_data = NULL; + + /* last close? */ + fp_dev->users--; + if (!fp_dev->users) { + // todo + } + mutex_unlock(&device_list_lock); + + return status; +} + +static const struct file_operations silfp_dev_fops = { + .owner = THIS_MODULE, + /* REVISIT switch to aio primitives, so that userspace + * gets more complete API coverage. It'll simplify things + * too, except for the locking. + */ + .unlocked_ioctl = silfp_ioctl, + .compat_ioctl = silfp_compat_ioctl, + .open = silfp_open, + .release = silfp_release, +#ifndef BSP_SIL_NETLINK + .read = silfp_read, + .poll = silfp_poll, +#endif /* !BSP_SIL_NETLINK */ +}; + +/*-------------------------------------------------------------------------*/ + +/* The main reason to have this class is to make mdev/udev create the + * /dev/spidevB.C character device nodes exposing our userspace API. + * It also simplifies memory management. + */ + +/*-------------------------------------------------------------------------*/ + +static int silfp_probe(struct platform_device *spi) +{ + struct silfp_data *fp_dev; + int status = 0; + //unsigned long minor; + + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter.\n", __func__); + + /* Allocate driver data */ + fp_dev = kzalloc(sizeof(*fp_dev), GFP_KERNEL); + if (!fp_dev) { + return -ENOMEM; + } + + /* Initialize the driver data */ + fp_dev->spi = spi; + spin_lock_init(&fp_dev->spi_lock); + + wake_lock_init(&fp_dev->wakelock,WAKE_LOCK_SUSPEND,"silfp_wakelock"); + wake_lock_init(&fp_dev->wakelock_hal,WAKE_LOCK_SUSPEND,"silfp_wakelock_hal"); + INIT_LIST_HEAD(&fp_dev->device_entry); + + /* If we can allocate a minor number, hook up this device. + * Reusing minors is fine so long as udev or mdev is working. + */ + + if (FP_DEV_MAJOR > 0) { + fp_dev->devt = MKDEV(FP_DEV_MAJOR, fp_dev->ref++); + status = register_chrdev_region(fp_dev->devt, 1, FP_DEV_NAME); + } else { + status = alloc_chrdev_region(&fp_dev->devt, fp_dev->ref++, 1, FP_DEV_NAME); + } + if (status < 0) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] request devt fail, ret=%d.\n", __func__, status); + goto err_devt; + } else { + LOG_MSG_DEBUG(INFO_LOG, "[%s], major=%d, minor=%d\n", __func__, MAJOR(fp_dev->devt), MINOR(fp_dev->devt)); + } + + fp_dev->dev = device_create(silfp_class, &fp_dev->spi->dev, fp_dev->devt, fp_dev, FP_DEV_NAME); + if (IS_ERR(fp_dev->dev)) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] device_create() fail.\n", __func__); + status = -ENODEV; + goto err_dev; + } else { + mutex_lock(&device_list_lock); + list_add(&fp_dev->device_entry, &device_list); + mutex_unlock(&device_list_lock); + LOG_MSG_DEBUG(INFO_LOG, "[%s] device create success.\n", __func__); + } + + cdev_init(&fp_dev->cdev, &silfp_dev_fops); + fp_dev->cdev.owner = THIS_MODULE; + status = cdev_add(&fp_dev->cdev, fp_dev->devt, 1); + if (status) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] cdev_add() fail, ret=%d.\n", __func__, status); + goto err_cdev; + } + + atomic_set(&fp_dev->init,0); + status = silfp_init(fp_dev); + g_fp_dev = NULL; + + if (status) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] silfp_init fail ret=%d.\n", __func__, status); + goto err_cdev; + } + LOG_MSG_DEBUG(INFO_LOG, "%s: success ret:%d \n", __func__, status); + +#ifdef PROC_NODE + silfp_proc_init(fp_dev); +#endif /* PROC_NODE */ + platform_set_drvdata(spi, fp_dev); + + return status; + +err_cdev: + list_del(&fp_dev->device_entry); + +err_dev: + unregister_chrdev_region(fp_dev->devt, 1); + +err_devt: + platform_set_drvdata(spi, NULL); + fp_dev->spi = NULL; + kfree(fp_dev); + fp_dev = NULL; + + return status; +} + +static int silfp_remove(struct platform_device *spi) +{ + struct silfp_data *fp_dev = platform_get_drvdata(spi); + + wake_lock_destroy(&fp_dev->wakelock); + wake_lock_destroy(&fp_dev->wakelock_hal); + /* make sure ops on existing fds can abort cleanly */ + spin_lock_irq(&fp_dev->spi_lock); + fp_dev->spi = NULL; + spin_unlock_irq(&fp_dev->spi_lock); + + silfp_exit(fp_dev); + /* prevent new opens */ + mutex_lock(&device_list_lock); + cdev_del(&fp_dev->cdev); + list_del(&fp_dev->device_entry); + device_destroy(silfp_class, fp_dev->devt); + if (fp_dev->users == 0) + kfree(fp_dev); + mutex_unlock(&device_list_lock); + + return 0; +} + +static const struct of_device_id sildev_dt_ids[] = { + { .compatible = "sil,silead_fp" }, + { .compatible = "sil,silead-fp" }, + { .compatible = "sil,fingerprint" }, + { .compatible = "sil,silead_fp-pins" }, + {}, +}; + +MODULE_DEVICE_TABLE(of, sildev_dt_ids); + +static struct platform_driver silfp_driver = { + .driver = { + .name = "silead_fp", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(sildev_dt_ids), + }, + .probe = silfp_probe, + .remove = silfp_remove, + + /* NOTE: suspend/resume methods are not necessary here. + * We don't do anything except pass the requests to/from + * the underlying controller. The refrigerator handles + * most issues; the controller driver handles the rest. + */ +}; + +/*-------------------------------------------------------------------------*/ +#ifndef BSP_SIL_DYNAMIC_SPI +static +#endif +int silfp_dev_init(void) +{ + int status = 0; + + LOG_MSG_DEBUG(ERR_LOG, "SILEAD_FP Driver, Version: %s.\n", FP_DEV_VERSION); + /* Claim our 256 reserved device numbers. Then register a class + * that will key udev/mdev to add/remove /dev nodes. Last, register + * the driver which manages those device numbers. + */ + status = register_chrdev(FP_DEV_MAJOR, "sil", &silfp_dev_fops); + if (status < 0) { + return status; + } + + silfp_class = class_create(THIS_MODULE, "silead_fp"); + if (IS_ERR(silfp_class)) { + unregister_chrdev(FP_DEV_MAJOR, silfp_driver.driver.name); + return PTR_ERR(silfp_class); + } + + status = platform_driver_register(&silfp_driver); + if (status < 0) { + class_destroy(silfp_class); + unregister_chrdev(FP_DEV_MAJOR, silfp_driver.driver.name); + LOG_MSG_DEBUG(ERR_LOG, "[%s] spi_register_driver fail ret=%d.\n", __func__, status); + return status; + } + silfp_wq = create_singlethread_workqueue("silfp_wq"); + return status; +} + +#ifndef BSP_SIL_DYNAMIC_SPI +static +#endif +void silfp_dev_exit(void) +{ + platform_driver_unregister(&silfp_driver); + class_destroy(silfp_class); + unregister_chrdev(FP_DEV_MAJOR, silfp_driver.driver.name); +} + +#ifdef BSP_SIL_DYNAMIC_SPI +EXPORT_SYMBOL(silfp_dev_init); +EXPORT_SYMBOL(silfp_dev_exit); +EXPORT_SYMBOL(sil_debug_level); +#else +module_exit(silfp_dev_exit); +module_init(silfp_dev_init); + +MODULE_AUTHOR("Bill Yu "); +MODULE_DESCRIPTION("Gigadevice/Silead Fingerprint driver for GSL6xxx/GSL7xxx/GSL8xxx series."); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("sil:silead_fp"); +#endif /* BSP_SIL_DYNAMIC_SPI */ diff --git a/drivers/input/misc/silead_fps_mmi/silead_fp.h b/drivers/input/misc/silead_fps_mmi/silead_fp.h new file mode 100644 index 000000000000..b7202bf4484c --- /dev/null +++ b/drivers/input/misc/silead_fps_mmi/silead_fp.h @@ -0,0 +1,316 @@ +/* + * @file silead_fp.h + * @brief Contains silead_fp device head file. + * + * + * Copyright 2016-2021 GigaDevice/Silead Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + * + * ------------------- Revision History ------------------------------ + * + * Bill Yu 2018/5/2 0.1.0 Init version + * Bill Yu 2018/5/28 0.1.1 Disable netlink if netlink id = 0 + * Bill Yu 2018/6/1 0.1.2 Support wakelock + * Bill Yu 2018/6/5 0.1.3 Support chip enter power down + * Bill Yu 2018/6/7 0.1.4 Support create proc node + * Bill Yu 2018/6/27 0.1.5 Expand pwdn I/F + * Rui Wu 2019/10/10 0.1.6 Support create class node + * Bill Yu 2020/5/11 0.1.7 Netlink id should < 32 + * Bill Yu 2020/5/29 0.1.8 Default use poll + * Bill Yu 2020/12/22 0.1.9 Support 5.x version wakeup + * Melvin cao 2021/01/29 0.2.0 Support silead spi device driver + */ + +#ifndef __SILEAD_FP_H__ +#define __SILEAD_FP_H__ + +#include +#include + +#ifndef _LINUX_WAKELOCK_H + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +#define USE_WAKEUP_REG +#endif + +enum { + WAKE_LOCK_SUSPEND, /* Prevent suspend */ + WAKE_LOCK_TYPE_COUNT +}; + +#ifdef USE_WAKEUP_REG +struct wake_lock { + struct wakeup_source *ws; +}; + +#define ws_init(s, n) s = wakeup_source_register(NULL, n) +#define ws_deinit(s) wakeup_source_unregister(s) +#define ws_lock(s) __pm_stay_awake(s) +#define ws_lock_tm(s, t) __pm_wakeup_event(s, t) +#define ws_unlock(s) __pm_relax(s) + +#else +struct wake_lock { + struct wakeup_source ws; +}; + +#define ws_init(s, n) wakeup_source_init(&s, n) +#define ws_deinit(s) wakeup_source_trash(&s) +#define ws_lock(s) __pm_stay_awake(&s) +#define ws_lock_tm(s, t) __pm_wakeup_event(&s, t) +#define ws_unlock(s) __pm_relax(&s) + +#endif /* USE_WAKEUP_REG */ + +static inline void wake_lock_init(struct wake_lock *lock, int type, + const char *name) +{ + ws_init(lock->ws, name); +} + +static inline void wake_lock_destroy(struct wake_lock *lock) +{ + ws_deinit(lock->ws); +} + +static inline void wake_lock(struct wake_lock *lock) +{ + ws_lock(lock->ws); +} + +static inline void wake_lock_timeout(struct wake_lock *lock, long timeout) +{ + ws_lock_tm(lock->ws, jiffies_to_msecs(timeout)); +} + +static inline void wake_unlock(struct wake_lock *lock) +{ + ws_unlock(lock->ws); +} +#endif /* _LINUX_WAKELOCK_H */ + +enum _pwdn_mode_t { + SIFP_PWDN_NONE = 0, + SIFP_PWDN_POWEROFF = 1, /* shutdown the avdd power supply */ + SIFP_PWDN_FLASH = 2, /* shutdown avdd 200ms for H/W full reset */ + SIFP_PWDN_MAX, +}; + +enum _netlink_cmd_t { + SIFP_NETLINK_START = 0, + SIFP_NETLINK_IRQ = 1, + SIFP_NETLINK_SCR_OFF, + SIFP_NETLINK_SCR_ON, + SIFP_NETLINK_CONNECT, + SIFP_NETLINK_DISCONNECT, + SIFP_NETLINK_TP_TOUCHDOWN, + SIFP_NETLINK_TP_TOUCHUP, + SIFP_NETLINK_MAX, +}; + +enum _fp_nav_key_v_t { + NAV_KEY_UNKNOWN = 0, + NAV_KEY_START = 1, + NAV_KEY_UP = NAV_KEY_START, + NAV_KEY_DOWN, + NAV_KEY_RIGHT, + NAV_KEY_LEFT, + NAV_KEY_CLICK, + NAV_KEY_DCLICK, + NAV_KEY_LONGPRESS, + NAV_KEY_CLICK_DOWN, + NAV_KEY_CLICK_UP, + NAV_KEY_MAX, + NAV_KEY_WAITMORE = 1000, +}; + +#define IS_KEY_VALID(k) ((k) > NAV_KEY_UNKNOWN && (k) < NAV_KEY_MAX) + +enum _fp_nav_key_f_t { + NAV_KEY_FLAG_UP = 0, + NAV_KEY_FLAG_DOWN, + NAV_KEY_FLAG_CLICK, +}; + +struct fp_dev_key_t { + int value; + uint32_t flag; /* key down = 1, key up = 0, key down+up = 2 */ +}; + +#define DEVNAME_LEN 16 +struct fp_dev_init_t { + uint8_t mode; + uint8_t bits; + uint16_t delay; + uint32_t speed; + char dev[DEVNAME_LEN]; + uint8_t nl_id; + uint8_t dev_id; + uint16_t reserve; + uint32_t reg; + char ta[DEVNAME_LEN]; +}; + +struct fp_dev_debug_t { + uint8_t cmd[4]; +}; + +struct fp_dev_kmap_t { + uint16_t k[NAV_KEY_MAX-NAV_KEY_START]; /* Up/Down/Right/Left/Click/Double Click/Longpress */ +}; + +struct fp_dev_touch_info { + uint8_t touch_state; + uint8_t area_rate; + uint16_t x; + uint16_t y; +}; + +#define PROC_VND_ID_LEN 32 + +#define SIFP_IOC_MAGIC 's' + +#define SIFP_IOC_RESET _IOW(SIFP_IOC_MAGIC, 10, u8) + +#define SIFP_IOC_ENABLE_IRQ _IO(SIFP_IOC_MAGIC, 11) +#define SIFP_IOC_DISABLE_IRQ _IO(SIFP_IOC_MAGIC, 12) +#define SIFP_IOC_WAIT_IRQ _IOR(SIFP_IOC_MAGIC, 13, u8) +#define SIFP_IOC_CLR_IRQ _IO(SIFP_IOC_MAGIC, 14) +//#define SPFP_IOC_EXIT _IOR(SIFP_IOC_MAGIC, 1, u8) + +#define SIFP_IOC_KEY_EVENT _IOW(SIFP_IOC_MAGIC, 15, struct fp_dev_key_t) +#define SIFP_IOC_INIT _IOR(SIFP_IOC_MAGIC, 16, struct fp_dev_init_t) +#define SIFP_IOC_DEINIT _IO(SIFP_IOC_MAGIC, 17) +#define SIFP_IOC_IRQ_STATUS _IOR(SIFP_IOC_MAGIC, 18, u8) +#define SIFP_IOC_DEBUG _IOR(SIFP_IOC_MAGIC, 19, struct fp_dev_debug_t) +#define SIFP_IOC_SCR_STATUS _IOR(SIFP_IOC_MAGIC, 20, u8) +#define SIFP_IOC_GET_VER _IOR(SIFP_IOC_MAGIC, 21, char[10]) +#define SIFP_IOC_SET_KMAP _IOW(SIFP_IOC_MAGIC, 22, uint16_t[7]) +#define SIFP_IOC_ACQ_SPI _IO(SIFP_IOC_MAGIC, 23) +#define SIFP_IOC_RLS_SPI _IO(SIFP_IOC_MAGIC, 24) +#define SIFP_IOC_PKG_SIZE _IOR(SIFP_IOC_MAGIC, 25, u8) +#define SIFP_IOC_DBG_LEVEL _IOWR(SIFP_IOC_MAGIC,26, u8) +#define SIFP_IOC_WAKELOCK _IOW(SIFP_IOC_MAGIC, 27, u8) +#define SIFP_IOC_PWDN _IOW(SIFP_IOC_MAGIC, 28, u8) +#define SIFP_IOC_PROC_NODE _IOW(SIFP_IOC_MAGIC, 29, char[PROC_VND_ID_LEN]) +#define SIFP_IOC_SET_FEATURE _IOW(SIFP_IOC_MAGIC, 30, u8) + +#define FEATURE_FLASH_CS 0x01 + +#define RESET_TIME 2 /* Default chip reset wait time(ms) */ +#define RESET_TIME_MULTIPLE 1 /* Multiple for reset time multiple*wait_time */ +#define SIFP_NETLINK_ROUTE 0 /* 0: poll/1-31: netlink(cat /proc/net/netlink)/ > 31: epoll/ 30 */ +#define NL_MSG_LEN 16 + +//#define PROC_DIR "fp" /* if defined, create node under /proc/fp/xxx */ +#define PROC_NODE "fp_id" /* proc node name */ + +//#define CLASS_NODE "fingerprint" /* if defined, create class node /sys/class/fingerprint/fingerprint */ + +#if (SIFP_NETLINK_ROUTE > 0) && (SIFP_NETLINK_ROUTE < 32) + #define BSP_SIL_NETLINK +#endif + +#if !defined(BSP_SIL_PLAT_MTK) && !defined(BSP_SIL_PLAT_QCOM) + #define BSP_SIL_PLAT_COMM +#endif /* ! BSP_SIL_PLAT_MTK & ! BSP_SIL_PLAT_QCOM */ + +/* Todo: enable correct power supply mode */ +//#define BSP_SIL_POWER_SUPPLY_REGULATOR +//#define BSP_SIL_POWER_SUPPLY_PINCTRL +#define BSP_SIL_POWER_SUPPLY_GPIO + +/* AVDD voltage range 2.8v ~ 3.3v */ +#define AVDD_MAX 2800000 +#define AVDD_MIN 2800000 + +/* VDDIO voltage range 1.8v ~ AVDD */ +#define VDDIO_MAX 1800000 +#define VDDIO_MIN 1800000 + +#if defined(BSP_SIL_POWER_SUPPLY_REGULATOR) && defined(BSP_SIL_POWER_SUPPLY_PINCTRL) || defined(BSP_SIL_POWER_SUPPLY_REGULATOR) && defined(BSP_SIL_POWER_SUPPLY_GPIO) || defined(BSP_SIL_POWER_SUPPLY_GPIO) && defined(BSP_SIL_POWER_SUPPLY_PINCTRL) + #error "Don't define multiple power supply mode!" +#endif + +#ifdef BSP_SIL_PLAT_MTK + #include "silead_fp_mtk.h" + #define PLAT_H "silead_fp_mtk.c" + #define SILFP_SPI_REE /* REE silead_spi driver, disable it if CONFIG_SPIDEV is enabled */ + + #ifdef SILFP_SPI_REE + #define DEVICE "/dev/silead_spi" + #else + #define DEVICE "/dev/spidev1.0" + #endif + //#define BSP_SIL_IRQ_CONFIRM + #define PKG_SIZE 1 + #define BSP_SIL_DYNAMIC_SPI + #ifndef CONFIG_SILEAD_FP_PLATFORM + #define BSP_SIL_CTRL_SPI + #endif /* !CONFIG_SILEAD_FP_PLATFORM */ +#elif defined(BSP_SIL_PLAT_QCOM) + #define QSEE_V4 /* Enable it if QSEE v4 or higher */ + #include "silead_fp_qcom.h" + #define PLAT_H "silead_fp_qcom.c" + + #define DEVICE "/dev/spidev0.0" + //#define BSP_SIL_IRQ_CONFIRM + #define PKG_SIZE 4 + #define TANAME "sileadta" +#elif defined(BSP_SIL_PLAT_SPRD) + #include "silead_fp_sprd.h" + #define PLAT_H "silead_fp_sprd.c" + + #define DEVICE "/dev/spidev0.0" + //#define BSP_SIL_IRQ_CONFIRM + //#define BSP_SIL_QCOM_SPECIAL + #define PKG_SIZE 4 + #define TANAME "silead" +#else + #include "silead_fp_comm.h" + #define PLAT_H "silead_fp_comm.c" + + #define DEVICE "/dev/spidev0.0" + //#define BSP_SIL_IRQ_CONFIRM + #define PKG_SIZE 4 + #define TANAME "" +#endif /* BSP_SIL_PLAT_XXX */ + +#ifdef BSP_SIL_QCOM_SPECIAL + #define SIL_EVENT_BLANK MSM_DRM_EARLY_EVENT_BLANK + #define SIL_EVENT_UNBLANK MSM_DRM_BLANK_UNBLANK + #define SIL_EVENT_POWERDOWN MSM_DRM_BLANK_POWERDOWN + #define SIL_REGISTER_CLIENT(a) do{ msm_drm_register_client(a); }while(0) +#else + #define SIL_EVENT_BLANK FB_EVENT_BLANK + #define SIL_EVENT_UNBLANK FB_BLANK_UNBLANK + #define SIL_EVENT_POWERDOWN FB_BLANK_POWERDOWN + #define SIL_REGISTER_CLIENT(a) do{ fb_register_client(a); }while(0) +#endif + +/* -------------------------------------------------------------------- */ +/* debug settings */ +/* -------------------------------------------------------------------- */ +typedef enum { + ERR_LOG=0, + INFO_LOG, + DBG_LOG, + ALL_LOG, +} fp_debug_level_t; + +#define LOG_TAG "[+silead_fp-] " +#define LOG_MSG_DEBUG(level, fmt, args...) do { \ + if (sil_debug_level >= level) {\ + pr_warn(LOG_TAG fmt, ##args); \ + } \ + } while (0) + +#endif /* __SILEAD_FP_H__ */ + +/* End of file silead_fp.h */ diff --git a/drivers/input/misc/silead_fps_mmi/silead_fp_qcom.c b/drivers/input/misc/silead_fps_mmi/silead_fp_qcom.c new file mode 100644 index 000000000000..50b6c9c6193b --- /dev/null +++ b/drivers/input/misc/silead_fps_mmi/silead_fp_qcom.c @@ -0,0 +1,603 @@ +/* + * @file silead_fp_qcom.c + * @brief Contains silead_fp device implements for Qualcomm platform. + * + * + * Copyright 2016-2021 Gigadevice/Silead Inc. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ------------------- Revision History ------------------------------ + * + * Bill Yu 2018/5/2 0.1.0 Init version + * Bill Yu 2018/5/20 0.1.1 Default wait 3ms after reset + * Bill Yu 2018/6/5 0.1.2 Support chip enter power down + * Bill Yu 2018/6/27 0.1.3 Expand pwdn I/F + * Taobb 2019/6/6 0.1.4 Expand feature interface, irq pin set to reset pin + * Bill Yu 2020/12/16 0.1.5 Allow GPIO ID number to be zero + * + */ + +#ifdef BSP_SIL_PLAT_QCOM + +#ifndef __SILEAD_FP_QCOM__ +#define __SILEAD_FP_QCOM__ + +#include + +#ifndef INIT_COMPLETION +#define INIT_COMPLETION(x) ((x).done = 0) +#endif /* INIT_COMPLETION */ +/* Qualcomm MNC platform, need define this. */ +#define reinit_completion(x) INIT_COMPLETION(*(x)) + +#define SPI_PINCTRL_STATE_DEFAULT "spi_default" +#define SPI_PINCTRL_STATE_SLEEP "spi_sleep" + +static irqreturn_t silfp_irq_handler(int irq, void *dev_id); +static void silfp_work_func(struct work_struct *work); +static int silfp_input_init(struct silfp_data *fp_dev); + +/* -------------------------------------------------------------------- */ +/* power supply */ +/* -------------------------------------------------------------------- */ +static void silfp_hw_poweron(struct silfp_data *fp_dev) +{ + int err = 0; + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter.\n", __func__); + +#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR + /* Power control by Regulators(LDO) */ + if ( fp_dev->avdd_ldo ) { + err = regulator_set_voltage(fp_dev->avdd_ldo, AVDD_MIN, AVDD_MAX); /*set 2.8v*/ + err = regulator_enable(fp_dev->avdd_ldo); /*enable regulator*/ + //pmic_set_register_value(PMIC_RG_VCAMA_CAL,0x0A); + } + if ( fp_dev->vddio_ldo ) { + err = regulator_set_voltage(fp_dev->vddio_ldo, VDDIO_MIN, VDDIO_MAX); /*set 1.8v*/ + err = regulator_enable(fp_dev->vddio_ldo); /*enable regulator*/ + //pmic_set_register_value(PMIC_RG_VCAMA_CAL,0x0A); + } +#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */ + +#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL + /* Power control by GPIOs */ + if ( fp_dev->pin.pins_avdd_h ) { + err = pinctrl_select_state(fp_dev->pin.pinctrl, fp_dev->pin.pins_avdd_h); + } + if ( fp_dev->pin.pins_vddio_h ) { + err = pinctrl_select_state(fp_dev->pin.pinctrl, fp_dev->pin.pins_vddio_h); + } +#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */ + +#ifdef BSP_SIL_POWER_SUPPLY_GPIO + if ( fp_dev->avdd_port >= 0 ) { + err = gpio_direction_output(fp_dev->avdd_port, 1); + } + if ( fp_dev->vddio_port >= 0 ) { + err = gpio_direction_output(fp_dev->vddio_port, 1); + } +#endif /* BSP_SIL_POWER_SUPPLY_GPIO */ + fp_dev->power_is_off = 0; + LOG_MSG_DEBUG(INFO_LOG, "%s: power supply ret:%d \n", __func__, err); +} + +static void silfp_hw_poweroff(struct silfp_data *fp_dev) +{ + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter.\n", __func__); + +#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR + /* Power control by Regulators(LDO) */ + if ( fp_dev->avdd_ldo && (regulator_is_enabled(fp_dev->avdd_ldo) > 0)) { + regulator_disable(fp_dev->avdd_ldo); /*disable regulator*/ + } + if ( fp_dev->vddio_ldo && (regulator_is_enabled(fp_dev->vddio_ldo) > 0)) { + regulator_disable(fp_dev->vddio_ldo); /*disable regulator*/ + } +#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */ + +#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL + /* Power control by GPIOs */ + //fp_dev->pin.pins_avdd_h = NULL; + //fp_dev->pin.pins_vddio_h = NULL; +#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */ + +#ifdef BSP_SIL_POWER_SUPPLY_GPIO + if ( fp_dev->avdd_port >= 0 ) { + gpio_direction_output(fp_dev->avdd_port, 0); + } + if ( fp_dev->vddio_port >= 0 ) { + gpio_direction_output(fp_dev->vddio_port, 0); + } +#endif /* BSP_SIL_POWER_SUPPLY_GPIO */ + fp_dev->power_is_off = 1; +} + +static void silfp_power_deinit(struct silfp_data *fp_dev) +{ + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter.\n", __func__); +#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR + /* Power control by Regulators(LDO) */ + if ( fp_dev->avdd_ldo ) { + regulator_disable(fp_dev->avdd_ldo); /*disable regulator*/ + regulator_put(fp_dev->avdd_ldo); + fp_dev->avdd_ldo = NULL; + } + if ( fp_dev->vddio_ldo ) { + regulator_disable(fp_dev->vddio_ldo); /*disable regulator*/ + regulator_put(fp_dev->vddio_ldo); + fp_dev->vddio_ldo = NULL; + } +#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */ + +#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL + /* Power control by GPIOs */ + fp_dev->pin.pins_avdd_h = NULL; + fp_dev->pin.pins_vddio_h = NULL; + if (fp_dev->pin.pinctrl) { + devm_pinctrl_put(fp_dev->pin.pinctrl); + } +#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */ + +#ifdef BSP_SIL_POWER_SUPPLY_GPIO + if ( fp_dev->avdd_port >= 0 ) { + gpio_direction_output(fp_dev->avdd_port, 0); + gpio_free(fp_dev->avdd_port); + fp_dev->avdd_port = -1; + } + if ( fp_dev->vddio_port >= 0 ) { + gpio_direction_output(fp_dev->vddio_port, 0); + gpio_free(fp_dev->vddio_port); + fp_dev->vddio_port = -1; + } +#endif /* BSP_SIL_POWER_SUPPLY_GPIO */ +} + +/* -------------------------------------------------------------------- */ +/* hardware reset */ +/* -------------------------------------------------------------------- */ +static void silfp_hw_reset(struct silfp_data *fp_dev, u8 delay) +{ + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter, port=%d\n", __func__, fp_dev->rst_port); + + if ( fp_dev->rst_port >= 0 ) { + gpio_direction_output(fp_dev->rst_port, 0); + if (fp_dev->irq_no_use && fp_dev->int_port >= 0) { + gpio_direction_output(fp_dev->int_port, 1); + } + mdelay((delay?delay:5)*RESET_TIME_MULTIPLE); + gpio_direction_output(fp_dev->rst_port, 1); + if (fp_dev->irq_no_use && fp_dev->int_port >= 0) { + gpio_direction_output(fp_dev->int_port, 0); + } + + mdelay((delay?delay:3)*RESET_TIME_MULTIPLE); + } +} + +/* -------------------------------------------------------------------- */ +/* power down */ +/* -------------------------------------------------------------------- */ +static void silfp_pwdn(struct silfp_data *fp_dev, u8 flag_avdd) +{ + LOG_MSG_DEBUG(INFO_LOG, "[%s] enter, port=%d\n", __func__, fp_dev->rst_port); + + if (SIFP_PWDN_FLASH == flag_avdd) { + silfp_hw_poweroff(fp_dev); + msleep(200*RESET_TIME_MULTIPLE); + silfp_hw_poweron(fp_dev); + } + + if ( fp_dev->rst_port >= 0 ) { + gpio_direction_output(fp_dev->rst_port, 0); + if (fp_dev->irq_no_use && fp_dev->int_port >= 0) { + gpio_direction_output(fp_dev->int_port, 1); + } + } + + if (SIFP_PWDN_POWEROFF == flag_avdd) { + silfp_hw_poweroff(fp_dev); + } +} + +/* -------------------------------------------------------------------- */ +/* init/deinit functions */ +/* -------------------------------------------------------------------- */ +static int silfp_parse_dts(struct silfp_data* fp_dev) +{ + int ret; +#ifndef QSEE_V4 + /* Get the pinctrl node */ + fp_dev->pin.pinctrl = devm_pinctrl_get(&fp_dev->spi->dev); + if (IS_ERR_OR_NULL(fp_dev->pin.pinctrl)) { + LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get pinctrl\n", __func__); + return PTR_ERR(fp_dev->pin.pinctrl); + } + + /* Get the active setting */ + fp_dev->pin.active = pinctrl_lookup_state(fp_dev->pin.pinctrl, SPI_PINCTRL_STATE_DEFAULT); + if (IS_ERR_OR_NULL(fp_dev->pin.active)) { + fp_dev->pin.active = NULL; + fp_dev->pin.pinctrl = NULL; + LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get pinctrl state active\n",__func__); + return PTR_ERR(fp_dev->pin.active); + } + + /* Get power settings */ +#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL + fp_dev->pin.pins_avdd_h = pinctrl_lookup_state(fp_dev->pin.pinctrl, "avdd-enable"); + if (IS_ERR_OR_NULL(fp_dev->pin.pins_avdd_h)) { + fp_dev->pin.pins_avdd_h = NULL; + LOG_MSG_DEBUG(ERR_LOG, "%s can't find silfp avdd-enable\n", __func__); + // Ignore error + } + + fp_dev->pin.pins_vddio_h = pinctrl_lookup_state(fp_dev->pin.pinctrl, "vddio-enable"); + if (IS_ERR_OR_NULL(fp_dev->pin.pins_vddio_h)) { + fp_dev->pin.pins_vddio_h = NULL; + LOG_MSG_DEBUG(ERR_LOG, "%s can't find silfp vddio-enable\n", __func__); + // Ignore error + } +#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */ +#endif /* QSEE_V4 */ + +#ifdef BSP_SIL_POWER_SUPPLY_REGULATOR + // Todo: use correct settings. + fp_dev->avdd_ldo = regulator_get(&fp_dev->spi->dev, "avdd"); + fp_dev->vddio_ldo= regulator_get(&fp_dev->spi->dev, "vddio"); +#endif /* BSP_SIL_POWER_SUPPLY_REGULATOR */ + +#ifndef QSEE_V4 + /* Get sleep settings */ + fp_dev->pin.sleep = pinctrl_lookup_state(fp_dev->pin.pinctrl, SPI_PINCTRL_STATE_SLEEP); + if (IS_ERR_OR_NULL(fp_dev->pin.sleep)) { + fp_dev->pin.active = NULL; + fp_dev->pin.pinctrl = NULL; + LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get pinctrl state sleep\n",__func__); + return PTR_ERR(fp_dev->pin.sleep); + } + + /* Get iface_clk info */ + fp_dev->pin.iface_clk = clk_get(&fp_dev->spi->dev, "iface_clk"); + if (IS_ERR(fp_dev->pin.iface_clk)) { + fp_dev->pin.active = NULL; + fp_dev->pin.pinctrl = NULL; + LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get iface_clk %ld\n", __func__, PTR_ERR(fp_dev->pin.iface_clk)); + return PTR_ERR(fp_dev->pin.iface_clk); + } + + /* Get core_clk info */ + fp_dev->pin.core_clk = clk_get(&fp_dev->spi->dev, "core_clk"); + if (IS_ERR(fp_dev->pin.core_clk)) { + fp_dev->pin.active = NULL; + fp_dev->pin.pinctrl = NULL; + LOG_MSG_DEBUG(ERR_LOG, "%s: Failed to get core_clk %p\n", __func__, fp_dev->pin.core_clk); + return PTR_ERR(fp_dev->pin.core_clk); + } +#else + /* Get the SPI Max speed */ + ret = of_property_read_u32(fp_dev->spi->dev.of_node,"spi-max-frequency", &fp_dev->pin.max_speed_hz); + if (ret) { + fp_dev->pin.max_speed_hz = 0; + LOG_MSG_DEBUG(ERR_LOG, "Error getting spi max speed\n"); + } +#endif /* QSEE_V4 */ + + /* Get the QUP ID (#1-12) */ + ret = of_property_read_u32(fp_dev->spi->dev.of_node,"qcom,qup-id", &fp_dev->pin.qup_id); + if (ret) { + fp_dev->pin.qup_id = 0; + LOG_MSG_DEBUG(ERR_LOG, "Error getting qup_id\n"); + } + + LOG_MSG_DEBUG(INFO_LOG, "[%s] done (%d).\n",__func__,ret); + /* + Grab SPI master lock for exclusive access + call spi_bus_unlock to unlock the lock. + */ + //spi_bus_lock(fp_dev->spi->master); + return ret; +} + +#ifndef QSEE_V4 +static int spi_set_pinctrl(struct silfp_data* fp_dev, bool active) +{ + int ret = -1; + + if ( IS_ERR_OR_NULL(fp_dev->pin.pinctrl) || IS_ERR_OR_NULL(fp_dev->pin.active)) { + LOG_MSG_DEBUG(ERR_LOG, "%s: not support\n", __func__); + return ret; + } + if (active) { /* Change to active settings */ + ret = pinctrl_select_state(fp_dev->pin.pinctrl, fp_dev->pin.active); + } else { + ret = pinctrl_select_state(fp_dev->pin.pinctrl, fp_dev->pin.sleep); + } + + LOG_MSG_DEBUG(INFO_LOG, "%s: pinctrl_select_state ret:%d Setting:%d\n", __func__, ret, active); + return ret; +} + +static int spi_set_clks(struct silfp_data* fp_dev, bool enable) +{ + int ret = -1; + int clk = 19200000; + + if ( IS_ERR_OR_NULL(fp_dev->pin.pinctrl) || IS_ERR_OR_NULL(fp_dev->pin.active)) { + LOG_MSG_DEBUG(INFO_LOG, "%s: not support\n", __func__); + return ret; + } + if (enable) { + /* Enable the spi clocks */ + ret = clk_set_rate(fp_dev->pin.core_clk, fp_dev->spi->max_speed_hz); + if (ret) { + while ( clk > fp_dev->spi->max_speed_hz ) { + clk >>= 1; + } + if ( clk < 4800000 ) { + clk = 4800000; + } + ret = clk_set_rate(fp_dev->pin.core_clk, clk); + if ( ret ) { + LOG_MSG_DEBUG(ERR_LOG, "%s: Error setting clk_rate:%d, ret=%d\n",__func__, clk,ret); + } else { + fp_dev->spi->max_speed_hz = clk; + } + } + ret = clk_prepare_enable(fp_dev->pin.core_clk); + if (ret) { + LOG_MSG_DEBUG(ERR_LOG, "%s: Error enabling core clk, ret=%d\n",__func__,ret); + } + ret = clk_prepare_enable(fp_dev->pin.iface_clk); + if (ret) { + LOG_MSG_DEBUG(ERR_LOG, "%s: Error enabling iface clk, ret=%d\n",__func__,ret); + } + } else { + /* Disable the clocks */ + clk_disable_unprepare(fp_dev->pin.iface_clk); + clk_disable_unprepare(fp_dev->pin.core_clk); + ret = 0; + } + + LOG_MSG_DEBUG(DBG_LOG, "[%s] done (%d), speed = %d.\n",__func__,ret,fp_dev->spi->max_speed_hz ); + return ret; +} + +static int spi_set_fabric(struct silfp_data* fp_dev, bool active) +{ + int ret; + struct spi_master *master = fp_dev->spi->master; + + if (active) { + ret = master->prepare_transfer_hardware(master); + } else { + ret = master->unprepare_transfer_hardware(master); + } + LOG_MSG_DEBUG(DBG_LOG, "[%s] done (%d).\n",__func__,ret); + return ret; +} +#endif /* !QSEE_V4 */ + +static int silfp_set_spi(struct silfp_data *fp_dev, bool enable) +{ + int ret = -ENOENT; +#ifndef QSEE_V4 + if ( IS_ERR_OR_NULL(fp_dev->pin.pinctrl) || IS_ERR_OR_NULL(fp_dev->pin.active) || !fp_dev->pin.qup_id ) { + LOG_MSG_DEBUG(ERR_LOG, "%s: not support!\n", __func__); + return ret; + } + + if ( enable && !atomic_read(&fp_dev->spionoff_count) ) { + atomic_inc(&fp_dev->spionoff_count); + ret = spi_set_pinctrl(fp_dev, enable); + ret |= spi_set_fabric(fp_dev, enable); + ret |= spi_set_clks(fp_dev, enable); + } else if (atomic_read(&fp_dev->spionoff_count)) { + atomic_dec(&fp_dev->spionoff_count); + //spi_change_pipe_owner(false); + ret = spi_set_clks(fp_dev, enable); + ret |= spi_set_fabric(fp_dev, enable); + ret |= spi_set_pinctrl(fp_dev, enable); + } +#else + LOG_MSG_DEBUG(ERR_LOG, "%s: qsee4 no needed!\n", __func__); +#endif /* !QSEE_V4 */ + return ret; +} + +static int silfp_irq_to_reset_init(struct silfp_data *fp_dev) +{ + int ret = 0; + + silfp_irq_disable(fp_dev); + free_irq(fp_dev->irq, fp_dev); + + if (fp_dev->int_port >= 0) { + gpio_free(fp_dev->int_port); + ret = gpio_request(fp_dev->int_port, "SILFP_IRQ_TO_RST_PIN"); + if (ret < 0) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->int_port, ret); + return -ENODEV; + } else { + gpio_direction_output(fp_dev->int_port, 0); + } + } + + fp_dev->irq_no_use = 1; + + return ret; +} + +static int silfp_set_feature(struct silfp_data *fp_dev, u8 feature) +{ + int ret = 0; + + switch (feature) { + case FEATURE_FLASH_CS: + LOG_MSG_DEBUG(INFO_LOG, "%s set feature flash cs\n", __func__); + ret = silfp_irq_to_reset_init(fp_dev); + break; + + default: + break; + } + return ret; +} + +static int silfp_resource_init(struct silfp_data *fp_dev, struct fp_dev_init_t *dev_info) +{ + int status = 0; + int ret; + + LOG_MSG_DEBUG(ERR_LOG, "[%s] enter.\n", __func__); + printk(KERN_INFO "silfp_resource_init enter!\n"); + + if (atomic_read(&fp_dev->init)) { + atomic_inc(&fp_dev->init); + LOG_MSG_DEBUG(DBG_LOG, "[%s] dev already init(%d).\n",__func__,atomic_read(&fp_dev->init)); + return status; + } + + fp_dev->int_port = -1; + fp_dev->rst_port = -1; + silfp_parse_dts(fp_dev); +#ifdef BSP_SIL_POWER_SUPPLY_GPIO + fp_dev->avdd_port = of_get_named_gpio(fp_dev->spi->dev.of_node, "avdd-gpios", 0); + fp_dev->vddio_port = of_get_named_gpio(fp_dev->spi->dev.of_node, "vddio-gpios", 0); + if (fp_dev->avdd_port >= 0 ) { + gpio_free(fp_dev->avdd_port); + ret = gpio_request(fp_dev->avdd_port, "SILFP_AVDD_PIN"); + if (ret < 0) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->avdd_port, ret); + status = -ENODEV; + goto err_avdd; + } + } + + if (fp_dev->vddio_port >= 0 ) { + gpio_free(fp_dev->vddio_port); + ret = gpio_request(fp_dev->vddio_port, "SILFP_VDDIO_PIN"); + if (ret < 0) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->vddio_port, ret); + status = -ENODEV; + //goto err_vddio; + } + } +#endif /* BSP_SIL_POWER_SUPPLY_GPIO */ + silfp_hw_poweron(fp_dev); + fp_dev->int_port = of_get_named_gpio(fp_dev->spi->dev.of_node, "irq-gpios", 0); + fp_dev->rst_port = of_get_named_gpio(fp_dev->spi->dev.of_node, "rst-gpios", 0); + LOG_MSG_DEBUG(INFO_LOG, "[%s] int_port %d, rst_port %d.\n",__func__,fp_dev->int_port,fp_dev->rst_port); + if (fp_dev->int_port >= 0 ) { + gpio_free(fp_dev->int_port); + } + + if (fp_dev->rst_port >= 0 ) { + gpio_free(fp_dev->rst_port); + } + + ret = gpio_request(fp_dev->int_port, "SILFP_INT_IRQ"); + if (ret < 0) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->int_port, ret); + status = -ENODEV; + goto err; + } else { + gpio_direction_input(fp_dev->int_port); + fp_dev->irq = gpio_to_irq(fp_dev->int_port); + fp_dev->irq_is_disable = 0; + + ret = request_irq(fp_dev->irq, + silfp_irq_handler, + IRQ_TYPE_EDGE_RISING, //IRQ_TYPE_LEVEL_HIGH, //irq_table[ts->int_trigger_type], + "silfp", + fp_dev); + if ( ret < 0 ) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Filed to request_irq (%d), ert=%d",__func__,fp_dev->irq, ret); + status = -ENODEV; + goto err_irq; + } else { + LOG_MSG_DEBUG(INFO_LOG,"[%s] Enable_irq_wake.\n",__func__); + enable_irq_wake(fp_dev->irq); + silfp_irq_disable(fp_dev); + } + } + + if (fp_dev->rst_port >= 0 ) { + ret = gpio_request(fp_dev->rst_port, "SILFP_RST_PIN"); + if (ret < 0) { + LOG_MSG_DEBUG(ERR_LOG, "[%s] Failed to request GPIO=%d, ret=%d",__func__,(s32)fp_dev->rst_port, ret); + status = -ENODEV; + goto err_rst; + } else { + gpio_direction_output(fp_dev->rst_port, 1); + } + } + + if (!ret) { + if (silfp_input_init(fp_dev)) { + goto err_input; + } + atomic_set(&fp_dev->init,1); + } + fp_dev->irq_no_use = 0; + dev_info->reserve = PKG_SIZE; + dev_info->reserve <<= 12; +#ifdef QSEE_V4 + if (fp_dev->pin.max_speed_hz) { + dev_info->speed = fp_dev->pin.max_speed_hz; + } +#endif /* QSEE_V4 */ + + if (dev_info && fp_dev->pin.qup_id && (fp_dev->pin.qup_id <= 32)) { + dev_info->dev_id = fp_dev->pin.qup_id; + strncpy(dev_info->ta,TANAME,sizeof(dev_info->ta)); + } + + return status; + +err_input: + if (fp_dev->rst_port >= 0 ) { + gpio_free(fp_dev->rst_port); + } + +err_rst: + free_irq(fp_dev->irq, fp_dev); + gpio_direction_input(fp_dev->int_port); + +err_irq: + gpio_free(fp_dev->int_port); + +#ifdef BSP_SIL_POWER_SUPPLY_GPIO + gpio_free(fp_dev->vddio_port); +#if 0 +err_vddio: + gpio_free(fp_dev->avdd_port); +#endif +err_avdd: + fp_dev->avdd_port = -1; + fp_dev->vddio_port = -1; + +#endif /* BSP_SIL_POWER_SUPPLY_GPIO */ + +err: + fp_dev->int_port = -1; + fp_dev->rst_port = -1; + + return status; +} + +#endif /* __SILEAD_FP_QCOM__ */ + +#endif /* BSP_SIL_PLAT_QCOM */ + +/* End of file spilead_fp_qcom.c */ diff --git a/drivers/input/misc/silead_fps_mmi/silead_fp_qcom.h b/drivers/input/misc/silead_fps_mmi/silead_fp_qcom.h new file mode 100644 index 000000000000..6e8e2a0a6653 --- /dev/null +++ b/drivers/input/misc/silead_fps_mmi/silead_fp_qcom.h @@ -0,0 +1,52 @@ +/* + * @file silead_fp_qcom.h + * @brief Contains silead_fp Qualcomm platform specific head file. + * + * + * Copyright 2016-2021 Gigadevice/Silead Inc. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ------------------- Revision History ------------------------------ + * + * Bill Yu 2018/5/2 0.1.0 Init version + * + */ + +#ifndef __SILEAD_FP_QCOM_H__ +#define __SILEAD_FP_QCOM_H__ + +#include +#include + +struct fp_plat_t { + u32 qup_id; +#ifdef QSEE_V4 + u32 max_speed_hz; +#else + /* pinctrl info */ + struct pinctrl *pinctrl; + struct pinctrl_state *active; + struct pinctrl_state *sleep; +#ifdef BSP_SIL_POWER_SUPPLY_PINCTRL + struct pinctrl_state *pins_avdd_h, *pins_vddio_h; +#endif /* BSP_SIL_POWER_SUPPLY_PINCTRL */ + /* clock info */ + struct clk *core_clk; + struct clk *iface_clk; +#endif /* QSEE_V4 */ +}; + +#endif /* __SILEAD_FP_QCOM_H__ */ + +/* End of file silead_fp_qcom.h */ diff --git a/drivers/input/misc/vl53L0/Android.mk b/drivers/input/misc/vl53L0/Android.mk new file mode 100644 index 000000000000..fb4f59171eae --- /dev/null +++ b/drivers/input/misc/vl53L0/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := stmvl53l0.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/misc/vl53L0/Kbuild b/drivers/input/misc/vl53L0/Kbuild new file mode 100644 index 000000000000..f2a451a166a2 --- /dev/null +++ b/drivers/input/misc/vl53L0/Kbuild @@ -0,0 +1,16 @@ +# +# Makefile for the vl53L0 drivers. +# + +# Each configuration option enables a list of files. + +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/drivers/input/misc/vl53L0 +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/drivers/input/misc/vl53L0/inc + +obj-m += stmvl53l0.o +stmvl53l0-objs := stmvl53l0_module.o stmvl53l0_module-i2c.o +stmvl53l0-objs += src/vl53l0_api_calibration.o src/vl53l0_api_core.o +stmvl53l0-objs += src/vl53l0_api_histogram.o src/vl53l0_api_ranging.o +stmvl53l0-objs += src/vl53l0_api_strings.o src/vl53l0_api.o src/vl53l0_platform.o +stmvl53l0-objs += src/vl53l0_i2c_platform.o src/vl53l0_port_i2c.o diff --git a/drivers/input/misc/vl53L0/Makefile b/drivers/input/misc/vl53L0/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/misc/vl53L0/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_api.h b/drivers/input/misc/vl53L0/inc/vl53l0_api.h new file mode 100644 index 000000000000..5c38f5e3ae5c --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_api.h @@ -0,0 +1,1920 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. + All rights reserved. + + 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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND + NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. + IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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. + *****************************************************************************/ + +#ifndef _VL53L0_API_H_ +#define _VL53L0_API_H_ + +#include "vl53l0_api_strings.h" +#include "vl53l0_def.h" +#include "vl53l0_platform.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef _MSC_VER +# ifdef VL53L0_API_EXPORTS +# define VL53L0_API __declspec(dllexport) +# else +# define VL53L0_API +# endif +#else +# define VL53L0_API +#endif + +/** @defgroup VL53L0_cut11_group VL53L0 cut1.1 Function Definition + * @brief VL53L0 cut1.1 Function Definition + * @{ + */ + +/** @defgroup VL53L0_general_group VL53L0 General Functions + * @brief General functions and definitions + * @{ + */ + +/** + * @brief Return the VL53L0 PAL Implementation Version + * + * @note This function doesn't access to the device + * + * @param pVersion Pointer to current PAL Implementation Version + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetVersion(VL53L0_Version_t *pVersion); + +/** + * @brief Return the PAL Specification Version used for the current + * implementation. + * + * @note This function doesn't access to the device + * + * @param pPalSpecVersion Pointer to current PAL Specification Version + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetPalSpecVersion( + VL53L0_Version_t *pPalSpecVersion); + +/** + * @brief Reads the Product Revision for a for given Device + * This function can be used to distinguish cut1.0 from cut1.1. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pProductRevisionMajor Pointer to Product Revision Major + * for a given Device + * @param pProductRevisionMinor Pointer to Product Revision Minor + * for a given Device + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetProductRevision(VL53L0_DEV Dev, + uint8_t *pProductRevisionMajor, uint8_t *pProductRevisionMinor); + +/** + * @brief Reads the Device information for given Device + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pVL53L0_DeviceInfo Pointer to current device info for a given + * Device + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetDeviceInfo(VL53L0_DEV Dev, + VL53L0_DeviceInfo_t *pVL53L0_DeviceInfo); + +/** + * @brief Read current status of the error register for the selected device + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pDeviceErrorStatus Pointer to current error code of the device + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetDeviceErrorStatus(VL53L0_DEV Dev, + VL53L0_DeviceError * pDeviceErrorStatus); + +/** + * @brief Human readable Range Status string for a given RangeStatus + * + * @note This function doesn't access to the device + * + * @param RangeStatus The RangeStatus code as stored on + * @a VL53L0_RangingMeasurementData_t + * @param pRangeStatusString The returned RangeStatus string. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetRangeStatusString(uint8_t RangeStatus, + char *pRangeStatusString); + +/** + * @brief Human readable error string for a given Error Code + * + * @note This function doesn't access to the device + * + * @param ErrorCode The error code as stored on ::VL53L0_DeviceError + * @param pDeviceErrorString The error string corresponding to the ErrorCode + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetDeviceErrorString( + VL53L0_DeviceError ErrorCode, char *pDeviceErrorString); + +/** + * @brief Human readable error string for current PAL error status + * + * @note This function doesn't access to the device + * + * @param PalErrorCode The error code as stored on @a VL53L0_Error + * @param pPalErrorString The error string corresponding to the + * PalErrorCode + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetPalErrorString(VL53L0_Error PalErrorCode, + char *pPalErrorString); + +/** + * @brief Human readable PAL State string + * + * @note This function doesn't access to the device + * + * @param PalStateCode The State code as stored on @a VL53L0_State + * @param pPalStateString The State string corresponding to the + * PalStateCode + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetPalStateString(VL53L0_State PalStateCode, + char *pPalStateString); + +/** + * @brief Reads the internal state of the PAL for a given Device + * + * @note This function doesn't access to the device + * + * @param Dev Device Handle + * @param pPalState Pointer to current state of the PAL for a + * given Device + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetPalState(VL53L0_DEV Dev, + VL53L0_State * pPalState); + +/** + * @brief Set the power mode for a given Device + * The power mode can be Standby or Idle. Different level of both Standby and + * Idle can exists. + * This function should not be used when device is in Ranging state. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param PowerMode The value of the power mode to set. + * see ::VL53L0_PowerModes + * Valid values are: + * VL53L0_POWERMODE_STANDBY_LEVEL1, + * VL53L0_POWERMODE_IDLE_LEVEL1 + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_MODE_NOT_SUPPORTED This error occurs when PowerMode + * is not in the supported list + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetPowerMode(VL53L0_DEV Dev, + VL53L0_PowerModes PowerMode); + +/** + * @brief Get the power mode for a given Device + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pPowerMode Pointer to the current value of the power + * mode. see ::VL53L0_PowerModes + * Valid values are: + * VL53L0_POWERMODE_STANDBY_LEVEL1, + * VL53L0_POWERMODE_IDLE_LEVEL1 + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetPowerMode(VL53L0_DEV Dev, + VL53L0_PowerModes * pPowerMode); + +/** + * Set or over-hide part to part calibration offset + * \sa VL53L0_DataInit() VL53L0_GetOffsetCalibrationDataMicroMeter() + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param OffsetCalibrationDataMicroMeter Offset (microns) + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetOffsetCalibrationDataMicroMeter( + VL53L0_DEV Dev, int32_t OffsetCalibrationDataMicroMeter); + +/** + * @brief Get part to part calibration offset + * + * @par Function Description + * Should only be used after a successful call to @a VL53L0_DataInit to backup + * device NVM value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pOffsetCalibrationDataMicroMeter Return part to part + * calibration offset from device (microns) + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetOffsetCalibrationDataMicroMeter( + VL53L0_DEV Dev, int32_t *pOffsetCalibrationDataMicroMeter); + +/** + * Set the linearity corrective gain + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param LinearityCorrectiveGain Linearity corrective + * gain in x1000 + * if value is 1000 then no modification is applied. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetLinearityCorrectiveGain(VL53L0_DEV Dev, + int16_t LinearityCorrectiveGain); + +/** + * @brief Get the linearity corrective gain + * + * @par Function Description + * Should only be used after a successful call to @a VL53L0_DataInit to backup + * device NVM value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pLinearityCorrectiveGain Pointer to the linearity + * corrective gain in x1000 + * if value is 1000 then no modification is applied. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetLinearityCorrectiveGain(VL53L0_DEV Dev, + uint16_t *pLinearityCorrectiveGain); + +/** + * Set Group parameter Hold state + * + * @par Function Description + * Set or remove device internal group parameter hold + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @param GroupParamHold Group parameter Hold state to be set (on/off) + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L0_API VL53L0_Error VL53L0_SetGroupParamHold(VL53L0_DEV Dev, + uint8_t GroupParamHold); + +/** + * @brief Get the maximal distance for actual setup + * @par Function Description + * Device must be initialized through @a VL53L0_SetParameters() prior calling + * this function. + * + * Any range value more than the value returned is to be considered as + * "no target detected" or + * "no target in detectable range"\n + * @warning The maximal distance depends on the setup + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @param pUpperLimitMilliMeter The maximal range limit for actual setup + * (in millimeter) + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L0_API VL53L0_Error VL53L0_GetUpperLimitMilliMeter(VL53L0_DEV Dev, + uint16_t *pUpperLimitMilliMeter); + + +/** + * @brief Get the Total Signal Rate + * @par Function Description + * This function will return the Total Signal Rate after a good ranging is done. + * + * @note This function access to Device + * + * @param Dev Device Handle + * @param pTotalSignalRate Total Signal Rate value in Mega count per second + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_GetTotalSignalRate(VL53L0_DEV Dev, + FixPoint1616_t *pTotalSignalRate); + +/** @} VL53L0_general_group */ + +/** @defgroup VL53L0_init_group VL53L0 Init Functions + * @brief VL53L0 Init Functions + * @{ + */ + +/** + * @brief Set new device address + * + * After completion the device will answer to the new address programmed. + * This function should be called when several devices are used in parallel + * before start programming the sensor. + * When a single device us used, there is no need to call this function. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param DeviceAddress The new Device address + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetDeviceAddress(VL53L0_DEV Dev, + uint8_t DeviceAddress); + +/** + * + * @brief One time device initialization + * + * To be called once and only once after device is brought out of reset + * (Chip enable) and booted see @a VL53L0_WaitDeviceBooted() + * + * @par Function Description + * When not used after a fresh device "power up" or reset, it may return + * @a #VL53L0_ERROR_CALIBRATION_WARNING meaning wrong calibration data + * may have been fetched from device that can result in ranging offset error\n + * If application cannot execute device reset or need to run VL53L0_DataInit + * multiple time then it must ensure proper offset calibration saving and + * restore on its own by using @a VL53L0_GetOffsetCalibrationData() on first + * power up and then @a VL53L0_SetOffsetCalibrationData() in all subsequent init + * This function will change the VL53L0_State from VL53L0_STATE_POWERDOWN to + * VL53L0_STATE_WAIT_STATICINIT. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_DataInit(VL53L0_DEV Dev); + +/** + * @brief Set the tuning settings pointer + * + * This function is used to specify the Tuning settings buffer to be used + * for a given device. The buffer contains all the necessary data to permit + * the API to write tuning settings. + * This function permit to force the usage of either external or internal + * tuning settings. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pTuningSettingBuffer Pointer to tuning settings buffer. + * @param UseInternalTuningSettings Use internal tuning settings value. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetTuningSettingBuffer(VL53L0_DEV Dev, + uint8_t *pTuningSettingBuffer, uint8_t UseInternalTuningSettings); + +/** + * @brief Get the tuning settings pointer and the internal external switch + * value. + * + * This function is used to get the Tuning settings buffer pointer and the + * value. + * of the switch to select either external or internal tuning settings. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param ppTuningSettingBuffer Pointer to tuning settings buffer. + * @param pUseInternalTuningSettings Pointer to store Use internal tuning + * settings value. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetTuningSettingBuffer(VL53L0_DEV Dev, + uint8_t **ppTuningSettingBuffer, uint8_t *pUseInternalTuningSettings); + +/** + * @brief Do basic device init (and eventually patch loading) + * This function will change the VL53L0_State from + * VL53L0_STATE_WAIT_STATICINIT to VL53L0_STATE_IDLE. + * In this stage all default setting will be applied. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_StaticInit(VL53L0_DEV Dev); + +/** + * @brief Wait for device booted after chip enable (hardware standby) + * This function can be run only when VL53L0_State is VL53L0_STATE_POWERDOWN. + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + * + */ +VL53L0_API VL53L0_Error VL53L0_WaitDeviceBooted(VL53L0_DEV Dev); + +/** + * @brief Do an hard reset or soft reset (depending on implementation) of the + * device \nAfter call of this function, device must be in same state as right + * after a power-up sequence.This function will change the VL53L0_State to + * VL53L0_STATE_POWERDOWN. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_ResetDevice(VL53L0_DEV Dev); + +/** @} VL53L0_init_group */ + +/** @defgroup VL53L0_parameters_group VL53L0 Parameters Functions + * @brief Functions used to prepare and setup the device + * @{ + */ + +/** + * @brief Prepare device for operation + * @par Function Description + * Update device with provided parameters + * @li Then start ranging operation. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pDeviceParameters Pointer to store current device parameters. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetDeviceParameters(VL53L0_DEV Dev, + const VL53L0_DeviceParameters_t *pDeviceParameters); + +/** + * @brief Retrieve current device parameters + * @par Function Description + * Get actual parameters of the device + * @li Then start ranging operation. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pDeviceParameters Pointer to store current device parameters. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetDeviceParameters(VL53L0_DEV Dev, + VL53L0_DeviceParameters_t *pDeviceParameters); + +/** + * @brief Set a new device mode + * @par Function Description + * Set device to a new mode (ranging, histogram ...) + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param DeviceMode New device mode to apply + * Valid values are: + * VL53L0_DEVICEMODE_SINGLE_RANGING + * VL53L0_DEVICEMODE_CONTINUOUS_RANGING + * VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING + * VL53L0_DEVICEMODE_SINGLE_HISTOGRAM + * VL53L0_HISTOGRAMMODE_REFERENCE_ONLY + * VL53L0_HISTOGRAMMODE_RETURN_ONLY + * VL53L0_HISTOGRAMMODE_BOTH + * + * + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_MODE_NOT_SUPPORTED This error occurs when DeviceMode is + * not in the supported list + */ +VL53L0_API VL53L0_Error VL53L0_SetDeviceMode(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode); + +/** + * @brief Get current new device mode + * @par Function Description + * Get actual mode of the device(ranging, histogram ...) + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param pDeviceMode Pointer to current apply mode value + * Valid values are: + * VL53L0_DEVICEMODE_SINGLE_RANGING + * VL53L0_DEVICEMODE_CONTINUOUS_RANGING + * VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING + * VL53L0_DEVICEMODE_SINGLE_HISTOGRAM + * VL53L0_HISTOGRAMMODE_REFERENCE_ONLY + * VL53L0_HISTOGRAMMODE_RETURN_ONLY + * VL53L0_HISTOGRAMMODE_BOTH + * + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_MODE_NOT_SUPPORTED This error occurs when + * DeviceMode is not in the supported list + */ +VL53L0_API VL53L0_Error VL53L0_GetDeviceMode(VL53L0_DEV Dev, + VL53L0_DeviceModes * pDeviceMode); + +/** + * @brief Sets the resolution of range measurements. + * @par Function Description + * Set resolution of range measurements to either 0.25mm if + * fraction enabled or 1mm if not enabled. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param Enable Enable high resolution + * + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetRangeFractionEnable(VL53L0_DEV Dev, + uint8_t Enable); + +/** + * @brief Gets the fraction enable parameter indicating the resolution of + * range measurements. + * + * @par Function Description + * Gets the fraction enable state, which translates to the resolution of + * range measurements as follows :Enabled:=0.25mm resolution, + * Not Enabled:=1mm resolution. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param pEnable Output Parameter reporting the fraction enable state. + * + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetFractionEnable(VL53L0_DEV Dev, + uint8_t *pEnable); + +/** + * @brief Set a new Histogram mode + * @par Function Description + * Set device to a new Histogram mode + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param HistogramMode New device mode to apply + * Valid values are: + * VL53L0_HISTOGRAMMODE_DISABLED + * VL53L0_DEVICEMODE_SINGLE_HISTOGRAM + * VL53L0_HISTOGRAMMODE_REFERENCE_ONLY + * VL53L0_HISTOGRAMMODE_RETURN_ONLY + * VL53L0_HISTOGRAMMODE_BOTH + * + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_MODE_NOT_SUPPORTED This error occurs when + * HistogramMode is not in the supported list + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetHistogramMode(VL53L0_DEV Dev, + VL53L0_HistogramModes HistogramMode); + +/** + * @brief Get current new device mode + * @par Function Description + * Get current Histogram mode of a Device + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param pHistogramMode Pointer to current Histogram Mode value + * Valid values are: + * VL53L0_HISTOGRAMMODE_DISABLED + * VL53L0_DEVICEMODE_SINGLE_HISTOGRAM + * VL53L0_HISTOGRAMMODE_REFERENCE_ONLY + * VL53L0_HISTOGRAMMODE_RETURN_ONLY + * VL53L0_HISTOGRAMMODE_BOTH + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetHistogramMode(VL53L0_DEV Dev, + VL53L0_HistogramModes * pHistogramMode); + +/** + * @brief Set Ranging Timing Budget in microseconds + * + * @par Function Description + * Defines the maximum time allowed by the user to the device to run a + * full ranging sequence for the current mode (ranging, histogram, ASL ...) + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param MeasurementTimingBudgetMicroSeconds Max measurement time in + * microseconds. + * Valid values are: + * >= 17000 microsecs when wraparound enabled + * >= 12000 microsecs when wraparound disabled + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned if + MeasurementTimingBudgetMicroSeconds out of range + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetMeasurementTimingBudgetMicroSeconds( + VL53L0_DEV Dev, uint32_t MeasurementTimingBudgetMicroSeconds); + +/** + * @brief Get Ranging Timing Budget in microseconds + * + * @par Function Description + * Returns the programmed the maximum time allowed by the user to the + * device to run a full ranging sequence for the current mode + * (ranging, histogram, ASL ...) + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pMeasurementTimingBudgetMicroSeconds Max measurement time in + * microseconds. + * Valid values are: + * >= 17000 microsecs when wraparound enabled + * >= 12000 microsecs when wraparound disabled + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetMeasurementTimingBudgetMicroSeconds( + VL53L0_DEV Dev, uint32_t *pMeasurementTimingBudgetMicroSeconds); + +/** + * @brief Gets the VCSEL pulse period. + * + * @par Function Description + * This function retrieves the VCSEL pulse period for the given period type. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param VcselPeriodType VCSEL period identifier (pre-range|final). + * @param pVCSELPulsePeriod Pointer to VCSEL period value. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS Error VcselPeriodType parameter not + * supported. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetVcselPulsePeriod(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, uint8_t *pVCSELPulsePeriod); + +/** + * @brief Sets the VCSEL pulse period. + * + * @par Function Description + * This function retrieves the VCSEL pulse period for the given period type. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param VcselPeriodType VCSEL period identifier (pre-range|final). + * @param VCSELPulsePeriod VCSEL period value + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS Error VcselPeriodType parameter not + * supported. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetVcselPulsePeriod(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, uint8_t VCSELPulsePeriod); + +/** + * @brief Sets the (on/off) state of a requested sequence step. + * + * @par Function Description + * This function enables/disables a requested sequence step. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param SequenceStepId Sequence step identifier. + * @param SequenceStepEnabled Demanded state {0=Off,1=On} + * is enabled. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS Error SequenceStepId parameter not + * supported. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetSequenceStepEnable(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, uint8_t SequenceStepEnabled); + +/** + * @brief Gets the (on/off) state of a requested sequence step. + * + * @par Function Description + * This function retrieves the state of a requested sequence step, i.e. on/off. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param SequenceStepId Sequence step identifier. + * @param pSequenceStepEnabled Out parameter reporting if the sequence step + * is enabled {0=Off,1=On}. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS Error SequenceStepId parameter not + * supported. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetSequenceStepEnable(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, uint8_t *pSequenceStepEnabled); + +/** + * @brief Gets the (on/off) state of all sequence steps. + * + * @par Function Description + * This function retrieves the state of all sequence step in the scheduler. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param pSchedulerSequenceSteps Pointer to struct containing result. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetSequenceStepEnables(VL53L0_DEV Dev, + VL53L0_SchedulerSequenceSteps_t *pSchedulerSequenceSteps); + +/** + * @brief Sets the timeout of a requested sequence step. + * + * @par Function Description + * This function sets the timeout of a requested sequence step. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param SequenceStepId Sequence step identifier. + * @param TimeOutMilliSecs Demanded timeout + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS Error SequenceStepId parameter not + * supported. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetSequenceStepTimeout(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, FixPoint1616_t TimeOutMilliSecs); + +/** + * @brief Gets the timeout of a requested sequence step. + * + * @par Function Description + * This function retrieves the timeout of a requested sequence step. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param SequenceStepId Sequence step identifier. + * @param pTimeOutMilliSecs Timeout value. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS Error SequenceStepId parameter not + * supported. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetSequenceStepTimeout(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, + FixPoint1616_t *pTimeOutMilliSecs); + +/** + * @brief Gets number of sequence steps managed by the API. + * + * @par Function Description + * This function retrieves the number of sequence steps currently managed + * by the API + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param pNumberOfSequenceSteps Out parameter reporting the number of + * sequence steps. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetNumberOfSequenceSteps(VL53L0_DEV Dev, + uint8_t *pNumberOfSequenceSteps); + +/** + * @brief Gets the name of a given sequence step. + * + * @par Function Description + * This function retrieves the name of sequence steps corresponding to + * SequenceStepId. + * + * @note This function doesn't Accesses the device + * + * @param SequenceStepId Sequence step identifier. + * @param pSequenceStepsString Pointer to Info string + * + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetSequenceStepsInfo( + VL53L0_SequenceStepId SequenceStepId, char *pSequenceStepsString); + +/** + * Program continuous mode Inter-Measurement period in milliseconds + * + * @par Function Description + * When trying to set too short time return INVALID_PARAMS minimal value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param InterMeasurementPeriodMilliSeconds Inter-Measurement Period in ms. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetInterMeasurementPeriodMilliSeconds( + VL53L0_DEV Dev, uint32_t InterMeasurementPeriodMilliSeconds); + +/** + * Get continuous mode Inter-Measurement period in milliseconds + * + * @par Function Description + * When trying to set too short time return INVALID_PARAMS minimal value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pInterMeasurementPeriodMilliSeconds Pointer to programmed + * Inter-Measurement Period in milliseconds. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetInterMeasurementPeriodMilliSeconds( + VL53L0_DEV Dev, uint32_t *pInterMeasurementPeriodMilliSeconds); + +/** + * @brief Enable/Disable Cross talk compensation feature + * + * @note This function is not Implemented. + * Enable/Disable Cross Talk by set to zero the Cross Talk value + * by using @a VL53L0_SetXTalkCompensationRateMegaCps(). + * + * @param Dev Device Handle + * @param XTalkCompensationEnable Cross talk compensation + * to be set 0=disabled else = enabled + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L0_API VL53L0_Error VL53L0_SetXTalkCompensationEnable(VL53L0_DEV Dev, + uint8_t XTalkCompensationEnable); + +/** + * @brief Get Cross talk compensation rate + * + * @note This function is not Implemented. + * Enable/Disable Cross Talk by set to zero the Cross Talk value by + * using @a VL53L0_SetXTalkCompensationRateMegaCps(). + * + * @param Dev Device Handle + * @param pXTalkCompensationEnable Pointer to the Cross talk compensation + * state 0=disabled or 1 = enabled + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L0_API VL53L0_Error VL53L0_GetXTalkCompensationEnable(VL53L0_DEV Dev, + uint8_t *pXTalkCompensationEnable); + +/** + * @brief Set Cross talk compensation rate + * + * @par Function Description + * Set Cross talk compensation rate. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param XTalkCompensationRateMegaCps Compensation rate in + * Mega counts per second (16.16 fix point) see datasheet for details + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetXTalkCompensationRateMegaCps(VL53L0_DEV Dev, + FixPoint1616_t XTalkCompensationRateMegaCps); + +/** + * @brief Get Cross talk compensation rate + * + * @par Function Description + * Get Cross talk compensation rate. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pXTalkCompensationRateMegaCps Pointer to Compensation rate + in Mega counts per second (16.16 fix point) see datasheet for details + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetXTalkCompensationRateMegaCps(VL53L0_DEV Dev, + FixPoint1616_t *pXTalkCompensationRateMegaCps); + +/** + * @brief Set Reference Calibration Parameters + * + * @par Function Description + * Set Reference Calibration Parameters. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param VhvSettings Parameter for VHV + * @param PhaseCal Parameter for PhaseCal + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetRefCalibration(VL53L0_DEV Dev, + uint8_t VhvSettings, uint8_t PhaseCal); + +/** + * @brief Get Reference Calibration Parameters + * + * @par Function Description + * Get Reference Calibration Parameters. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pVhvSettings Pointer to VHV parameter + * @param pPhaseCal Pointer to PhaseCal Parameter + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetRefCalibration(VL53L0_DEV Dev, + uint8_t *pVhvSettings, uint8_t *pPhaseCal); + +/** + * @brief Get the number of the check limit managed by a given Device + * + * @par Function Description + * This function give the number of the check limit managed by the Device + * + * @note This function doesn't Access to the device + * + * @param pNumberOfLimitCheck Pointer to the number of check limit. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetNumberOfLimitCheck( + uint16_t *pNumberOfLimitCheck); + +/** + * @brief Return a description string for a given limit check number + * + * @par Function Description + * This function returns a description string for a given limit check number. + * The limit check is identified with the LimitCheckId. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + (0<= LimitCheckId < VL53L0_GetNumberOfLimitCheck() ). + * @param pLimitCheckString Pointer to the + description string of the given check limit. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is + returned when LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetLimitCheckInfo(VL53L0_DEV Dev, + uint16_t LimitCheckId, char *pLimitCheckString); + +/** + * @brief Return a the Status of the specified check limit + * + * @par Function Description + * This function returns the Status of the specified check limit. + * The value indicate if the check is fail or not. + * The limit check is identified with the LimitCheckId. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + (0<= LimitCheckId < VL53L0_GetNumberOfLimitCheck() ). + * @param pLimitCheckStatus Pointer to the + Limit Check Status of the given check limit. + * LimitCheckStatus : + * 0 the check is not fail + * 1 the check if fail or not enabled + * + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is + returned when LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetLimitCheckStatus(VL53L0_DEV Dev, + uint16_t LimitCheckId, uint8_t *pLimitCheckStatus); + +/** + * @brief Enable/Disable a specific limit check + * + * @par Function Description + * This function Enable/Disable a specific limit check. + * The limit check is identified with the LimitCheckId. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L0_GetNumberOfLimitCheck() ). + * @param LimitCheckEnable if 1 the check limit + * corresponding to LimitCheckId is Enabled + * if 0 the check limit + * corresponding to LimitCheckId is disabled + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned + * when LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetLimitCheckEnable(VL53L0_DEV Dev, + uint16_t LimitCheckId, uint8_t LimitCheckEnable); + +/** + * @brief Get specific limit check enable state + * + * @par Function Description + * This function get the enable state of a specific limit check. + * The limit check is identified with the LimitCheckId. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L0_GetNumberOfLimitCheck() ). + * @param pLimitCheckEnable Pointer to the check limit enable + * value. + * if 1 the check limit + * corresponding to LimitCheckId is Enabled + * if 0 the check limit + * corresponding to LimitCheckId is disabled + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned + * when LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetLimitCheckEnable(VL53L0_DEV Dev, + uint16_t LimitCheckId, uint8_t *pLimitCheckEnable); + +/** + * @brief Set a specific limit check value + * + * @par Function Description + * This function set a specific limit check value. + * The limit check is identified with the LimitCheckId. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L0_GetNumberOfLimitCheck() ). + * @param LimitCheckValue Limit check Value for a given + * LimitCheckId + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned when either + * LimitCheckId or LimitCheckValue value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetLimitCheckValue(VL53L0_DEV Dev, + uint16_t LimitCheckId, FixPoint1616_t LimitCheckValue); + +/** + * @brief Get a specific limit check value + * + * @par Function Description + * This function get a specific limit check value from device then it updates + * internal values and check enables. + * The limit check is identified with the LimitCheckId. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L0_GetNumberOfLimitCheck() ). + * @param pLimitCheckValue Pointer to Limit + * check Value for a given LimitCheckId. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned + * when LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetLimitCheckValue(VL53L0_DEV Dev, + uint16_t LimitCheckId, FixPoint1616_t *pLimitCheckValue); + +/** + * @brief Get the current value of the signal used for the limit check + * + * @par Function Description + * This function get a the current value of the signal used for the limit check. + * To obtain the latest value you should run a ranging before. + * The value reported is linked to the limit check identified with the + * LimitCheckId. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L0_GetNumberOfLimitCheck() ). + * @param pLimitCheckCurrent Pointer to current Value for a + * given LimitCheckId. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned when + * LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetLimitCheckCurrent(VL53L0_DEV Dev, + uint16_t LimitCheckId, FixPoint1616_t *pLimitCheckCurrent); + +/** + * @brief Enable (or disable) Wrap around Check + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param WrapAroundCheckEnable Wrap around Check to be set + * 0=disabled, other = enabled + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetWrapAroundCheckEnable(VL53L0_DEV Dev, + uint8_t WrapAroundCheckEnable); + +/** + * @brief Get setup of Wrap around Check + * + * @par Function Description + * This function get the wrapAround check enable parameters + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pWrapAroundCheckEnable Pointer to the Wrap around Check state + * 0=disabled or 1 = enabled + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetWrapAroundCheckEnable(VL53L0_DEV Dev, + uint8_t *pWrapAroundCheckEnable); + +/** @} VL53L0_parameters_group */ + +/** @defgroup VL53L0_measurement_group VL53L0 Measurement Functions + * @brief Functions used for the measurements + * @{ + */ + +/** + * @brief Single shot measurement. + * + * @par Function Description + * Perform simple measurement sequence (Start measure, Wait measure to end, + * and returns when measurement is done). + * Once function returns, user can get valid data by calling + * VL53L0_GetRangingMeasurement or VL53L0_GetHistogramMeasurement + * depending on defined measurement mode + * User should Clear the interrupt in case this are enabled by using the + * function VL53L0_ClearInterruptMask(). + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_PerformSingleMeasurement(VL53L0_DEV Dev); + +/** + * @brief Perform Reference Calibration + * + * @details Perform a reference calibration of the Device. + * This function should be run from time to time before doing + * a ranging measurement. + * This function will launch a special ranging measurement, so + * if interrupt are enable an interrupt will be done. + * This function will clear the interrupt generated automatically. + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pVhvSettings Pointer to vhv settings parameter. + * @param pPhaseCal Pointer to PhaseCal parameter. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_PerformRefCalibration(VL53L0_DEV Dev, + uint8_t *pVhvSettings, uint8_t *pPhaseCal); + +/** + * @brief Perform XTalk Measurement + * + * @details Measures the current cross talk from glass in front + * of the sensor. + * This functions performs a histogram measurement and uses the results + * to measure the crosstalk. For the function to be successful, there + * must be no target in front of the sensor. + * + * @warning This function is a blocking function + * + * @warning This function is not supported when the final range + * vcsel clock period is set below 10 PCLKS. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param TimeoutMs Histogram measurement duration. + * @param pXtalkPerSpad Output parameter containing the crosstalk + * measurement result, in MCPS/Spad. Format fixpoint 16:16. + * @param pAmbientTooHigh Output parameter which indicate that + * pXtalkPerSpad is not good if the Ambient is too high. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS vcsel clock period not supported + * for this operation. Must not be less than 10PCLKS. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_PerformXTalkMeasurement(VL53L0_DEV Dev, + uint32_t TimeoutMs, FixPoint1616_t *pXtalkPerSpad, + uint8_t *pAmbientTooHigh); + +/** + * @brief Perform XTalk Calibration + * + * @details Perform a XTalk calibration of the Device. + * This function will launch a ranging measurement, if interrupts + * are enabled an interrupt will be done. + * This function will clear the interrupt generated automatically. + * This function will program a new value for the XTalk compensation + * and it will enable the cross talk before exit. + * This function will disable the VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD. + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @note This function change the device mode to + * VL53L0_DEVICEMODE_SINGLE_RANGING + * + * @param Dev Device Handle + * @param XTalkCalDistance XTalkCalDistance value used for the XTalk + * computation. + * @param pXTalkCompensationRateMegaCps Pointer to new + * XTalkCompensation value. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_PerformXTalkCalibration(VL53L0_DEV Dev, + FixPoint1616_t XTalkCalDistance, + FixPoint1616_t *pXTalkCompensationRateMegaCps); + +/** + * @brief Perform Offset Calibration + * + * @details Perform a Offset calibration of the Device. + * This function will launch a ranging measurement, if interrupts are + * enabled an interrupt will be done. + * This function will clear the interrupt generated automatically. + * This function will program a new value for the Offset calibration value + * This function will disable the VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD. + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @note This function does not change the device mode. + * + * @param Dev Device Handle + * @param CalDistanceMilliMeter Calibration distance value used for the + * offset compensation. + * @param pOffsetMicroMeter Pointer to new Offset value computed by the + * function. + * + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_PerformOffsetCalibration(VL53L0_DEV Dev, + FixPoint1616_t CalDistanceMilliMeter, int32_t *pOffsetMicroMeter); + +/** + * @brief Start device measurement + * + * @details Started measurement will depend on device parameters set through + * @a VL53L0_SetParameters() + * This is a non-blocking function. + * This function will change the VL53L0_State from VL53L0_STATE_IDLE to + * VL53L0_STATE_RUNNING. + * + * @note This function Access to the device + * + + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_MODE_NOT_SUPPORTED This error occurs when + * DeviceMode programmed with @a VL53L0_SetDeviceMode is not in the supported + * list: + * Supported mode are: + * VL53L0_DEVICEMODE_SINGLE_RANGING, + * VL53L0_DEVICEMODE_CONTINUOUS_RANGING, + * VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING + * @return VL53L0_ERROR_TIME_OUT Time out on start measurement + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_StartMeasurement(VL53L0_DEV Dev); + +/** + * @brief Stop device measurement + * + * @details Will set the device in standby mode at end of current measurement\n + * Not necessary in single mode as device shall return automatically + * in standby mode at end of measurement. + * This function will change the VL53L0_State from VL53L0_STATE_RUNNING + * to VL53L0_STATE_IDLE. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_StopMeasurement(VL53L0_DEV Dev); + +/** + * @brief Return Measurement Data Ready + * + * @par Function Description + * This function indicate that a measurement data is ready. + * This function check if interrupt mode is used then check is done accordingly. + * If perform function clear the interrupt, this function will not work, + * like in case of @a VL53L0_PerformSingleRangingMeasurement(). + * The previous function is blocking function, VL53L0_GetMeasurementDataReady + * is used for non-blocking capture. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pMeasurementDataReady Pointer to Measurement Data Ready. + * 0=data not ready, 1 = data ready + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetMeasurementDataReady(VL53L0_DEV Dev, + uint8_t *pMeasurementDataReady); + +/** + * @brief Wait for device ready for a new measurement command. + * Blocking function. + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @param MaxLoop Max Number of polling loop (timeout). + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L0_API VL53L0_Error VL53L0_WaitDeviceReadyForNewMeasurement(VL53L0_DEV Dev, + uint32_t MaxLoop); + +/** + * @brief Retrieve the Reference Signal after a measurements + * + * @par Function Description + * Get Reference Signal from last successful Ranging measurement + * This function return a valid value after that you call the + * @a VL53L0_GetRangingMeasurementData(). + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pMeasurementRefSignal Pointer to the Ref Signal to fill up. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetMeasurementRefSignal(VL53L0_DEV Dev, + FixPoint1616_t *pMeasurementRefSignal); + +/** + * @brief Retrieve the measurements from device for a given setup + * + * @par Function Description + * Get data from last successful Ranging measurement + * @warning USER should take care about @a VL53L0_GetNumberOfROIZones() + * before get data. + * PAL will fill a NumberOfROIZones times the corresponding data + * structure used in the measurement function. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pRangingMeasurementData Pointer to the data structure to fill up. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetRangingMeasurementData(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData); + +/** + * @brief Retrieve the measurements from device for a given setup + * + * @par Function Description + * Get data from last successful Histogram measurement + * @warning USER should take care about @a VL53L0_GetNumberOfROIZones() + * before get data. + * PAL will fill a NumberOfROIZones times the corresponding data structure + * used in the measurement function. + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @param pHistogramMeasurementData Pointer to the histogram data structure. + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L0_API VL53L0_Error VL53L0_GetHistogramMeasurementData(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData); + +/** + * @brief Performs a single ranging measurement and retrieve the ranging + * measurement data + * + * @par Function Description + * This function will change the device mode to VL53L0_DEVICEMODE_SINGLE_RANGING + * with @a VL53L0_SetDeviceMode(), + * It performs measurement with @a VL53L0_PerformSingleMeasurement() + * It get data from last successful Ranging measurement with + * @a VL53L0_GetRangingMeasurementData. + * Finally it clear the interrupt with @a VL53L0_ClearInterruptMask(). + * + * @note This function Access to the device + * + * @note This function change the device mode to + * VL53L0_DEVICEMODE_SINGLE_RANGING + * + * @param Dev Device Handle + * @param pRangingMeasurementData Pointer to the data structure to fill up. + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_PerformSingleRangingMeasurement(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData); + +/** + * @brief Performs a single histogram measurement and retrieve the histogram + * measurement data + * Is equivalent to VL53L0_PerformSingleMeasurement + + * VL53L0_GetHistogramMeasurementData + * + * @par Function Description + * Get data from last successful Ranging measurement. + * This function will clear the interrupt in case of these are enabled. + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @param pHistogramMeasurementData Pointer to the data structure to fill up. + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L0_API VL53L0_Error VL53L0_PerformSingleHistogramMeasurement(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData); + +/** + * @brief Set the number of ROI Zones to be used for a specific Device + * + * @par Function Description + * Set the number of ROI Zones to be used for a specific Device. + * The programmed value should be less than the max number of ROI Zones given + * with @a VL53L0_GetMaxNumberOfROIZones(). + * This version of API manage only one zone. + * + * @param Dev Device Handle + * @param NumberOfROIZones Number of ROI Zones to be used for a + * specific Device. + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INVALID_PARAMS This error is returned if + * NumberOfROIZones != 1 + */ +VL53L0_API VL53L0_Error VL53L0_SetNumberOfROIZones(VL53L0_DEV Dev, + uint8_t NumberOfROIZones); + +/** + * @brief Get the number of ROI Zones managed by the Device + * + * @par Function Description + * Get number of ROI Zones managed by the Device + * USER should take care about @a VL53L0_GetNumberOfROIZones() + * before get data after a perform measurement. + * PAL will fill a NumberOfROIZones times the corresponding data + * structure used in the measurement function. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param pNumberOfROIZones Pointer to the Number of ROI Zones value. + * @return VL53L0_ERROR_NONE Success + */ +VL53L0_API VL53L0_Error VL53L0_GetNumberOfROIZones(VL53L0_DEV Dev, + uint8_t *pNumberOfROIZones); + +/** + * @brief Get the Maximum number of ROI Zones managed by the Device + * + * @par Function Description + * Get Maximum number of ROI Zones managed by the Device. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param pMaxNumberOfROIZones Pointer to the Maximum Number + * of ROI Zones value. + * @return VL53L0_ERROR_NONE Success + */ +VL53L0_API VL53L0_Error VL53L0_GetMaxNumberOfROIZones(VL53L0_DEV Dev, + uint8_t *pMaxNumberOfROIZones); + +/** @} VL53L0_measurement_group */ + +/** @defgroup VL53L0_interrupt_group VL53L0 Interrupt Functions + * @brief Functions used for interrupt managements + * @{ + */ + +/** + * @brief Set the configuration of GPIO pin for a given device + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param Pin ID of the GPIO Pin + * @param Functionality Select Pin functionality. + * Refer to ::VL53L0_GpioFunctionality + * @param DeviceMode Device Mode associated to the Gpio. + * @param Polarity Set interrupt polarity. Active high + * or active low see ::VL53L0_InterruptPolarity + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_GPIO_NOT_EXISTING Only Pin=0 is accepted. + * @return VL53L0_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED This error occurs + * when Functionality programmed is not in the supported list: + * Supported value are: + * VL53L0_GPIOFUNCTIONALITY_OFF, + * VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW, + * VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH, + VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT, + * VL53L0_GPIOFUNCTIONALITY_NEW_MEASURE_READY + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetGpioConfig(VL53L0_DEV Dev, uint8_t Pin, + VL53L0_DeviceModes DeviceMode, VL53L0_GpioFunctionality Functionality, + VL53L0_InterruptPolarity Polarity); + +/** + * @brief Get current configuration for GPIO pin for a given device + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param Pin ID of the GPIO Pin + * @param pDeviceMode Pointer to Device Mode associated to the Gpio. + * @param pFunctionality Pointer to Pin functionality. + * Refer to ::VL53L0_GpioFunctionality + * @param pPolarity Pointer to interrupt polarity. + * Active high or active low see ::VL53L0_InterruptPolarity + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_GPIO_NOT_EXISTING Only Pin=0 is accepted. + * @return VL53L0_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED This error occurs + * when Functionality programmed is not in the supported list: + * Supported value are: + * VL53L0_GPIOFUNCTIONALITY_OFF, + * VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW, + * VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH, + * VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT, + * VL53L0_GPIOFUNCTIONALITY_NEW_MEASURE_READY + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetGpioConfig(VL53L0_DEV Dev, uint8_t Pin, + VL53L0_DeviceModes * pDeviceMode, + VL53L0_GpioFunctionality * pFunctionality, + VL53L0_InterruptPolarity * pPolarity); + +/** + * @brief Set low and high Interrupt thresholds for a given mode + * (ranging, ALS, ...) for a given device + * + * @par Function Description + * Set low and high Interrupt thresholds for a given mode (ranging, ALS, ...) + * for a given device + * + * @note This function Access to the device + * + * @note DeviceMode is ignored for the current device + * + * @param Dev Device Handle + * @param DeviceMode Device Mode for which change thresholds + * @param ThresholdLow Low threshold (mm, lux ..., depending on the mode) + * @param ThresholdHigh High threshold (mm, lux ..., depending on the mode) + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetInterruptThresholds(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode, FixPoint1616_t ThresholdLow, + FixPoint1616_t ThresholdHigh); + +/** + * @brief Get high and low Interrupt thresholds for a given mode + * (ranging, ALS, ...) for a given device + * + * @par Function Description + * Get high and low Interrupt thresholds for a given mode (ranging, ALS, ...) + * for a given device + * + * @note This function Access to the device + * + * @note DeviceMode is ignored for the current device + * + * @param Dev Device Handle + * @param DeviceMode Device Mode from which read thresholds + * @param pThresholdLow Low threshold (mm, lux ..., depending on the mode) + * @param pThresholdHigh High threshold (mm, lux ..., depending on the mode) + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetInterruptThresholds(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode, FixPoint1616_t *pThresholdLow, + FixPoint1616_t *pThresholdHigh); + +/** + * @brief Return device stop completion status + * + * @par Function Description + * Returns stop completiob status. + * User shall call this function after a stop command + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pStopStatus Pointer to status variable to update + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetStopCompletedStatus(VL53L0_DEV Dev, + uint32_t *pStopStatus); + + +/** + * @brief Clear given system interrupt condition + * + * @par Function Description + * Clear given interrupt(s). + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param InterruptMask Mask of interrupts to clear + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_INTERRUPT_NOT_CLEARED Cannot clear interrupts + * + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_ClearInterruptMask(VL53L0_DEV Dev, + uint32_t InterruptMask); + +/** + * @brief Return device interrupt status + * + * @par Function Description + * Returns currently raised interrupts by the device. + * User shall be able to activate/deactivate interrupts through + * @a VL53L0_SetGpioConfig() + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pInterruptMaskStatus Pointer to status variable to update + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetInterruptMaskStatus(VL53L0_DEV Dev, + uint32_t *pInterruptMaskStatus); + +/** + * @brief Configure ranging interrupt reported to system + * + * @note This function is not Implemented + * + * @param Dev Device Handle + * @param InterruptMask Mask of interrupt to Enable/disable + * (0:interrupt disabled or 1: interrupt enabled) + * @return VL53L0_ERROR_NOT_IMPLEMENTED Not implemented + */ +VL53L0_API VL53L0_Error VL53L0_EnableInterruptMask(VL53L0_DEV Dev, + uint32_t InterruptMask); + +/** @} VL53L0_interrupt_group */ + +/** @defgroup VL53L0_SPADfunctions_group VL53L0 SPAD Functions + * @brief Functions used for SPAD managements + * @{ + */ + +/** + * @brief Set the SPAD Ambient Damper Threshold value + * + * @par Function Description + * This function set the SPAD Ambient Damper Threshold value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param SpadAmbientDamperThreshold SPAD Ambient Damper Threshold value + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetSpadAmbientDamperThreshold(VL53L0_DEV Dev, + uint16_t SpadAmbientDamperThreshold); + +/** + * @brief Get the current SPAD Ambient Damper Threshold value + * + * @par Function Description + * This function get the SPAD Ambient Damper Threshold value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pSpadAmbientDamperThreshold Pointer to programmed + * SPAD Ambient Damper Threshold value + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetSpadAmbientDamperThreshold(VL53L0_DEV Dev, + uint16_t *pSpadAmbientDamperThreshold); + +/** + * @brief Set the SPAD Ambient Damper Factor value + * + * @par Function Description + * This function set the SPAD Ambient Damper Factor value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param SpadAmbientDamperFactor SPAD Ambient Damper Factor value + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetSpadAmbientDamperFactor(VL53L0_DEV Dev, + uint16_t SpadAmbientDamperFactor); + +/** + * @brief Get the current SPAD Ambient Damper Factor value + * + * @par Function Description + * This function get the SPAD Ambient Damper Factor value + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pSpadAmbientDamperFactor Pointer to programmed SPAD Ambient + * Damper Factor value + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetSpadAmbientDamperFactor(VL53L0_DEV Dev, + uint16_t *pSpadAmbientDamperFactor); + +/** + * @brief Performs Reference Spad Management + * + * @par Function Description + * The reference SPAD initialization procedure determines the minimum amount + * of reference spads to be enables to achieve a target reference signal rate + * and should be performed once during initialization. + * + * @note This function Access to the device + * + * @note This function change the device mode to + * VL53L0_DEVICEMODE_SINGLE_RANGING + * + * @param Dev Device Handle + * @param refSpadCount Reports ref Spad Count + * @param isApertureSpads Reports if spads are of type + * aperture or non-aperture. + * 1:=aperture, 0:=Non-Aperture + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_REF_SPAD_INIT Error in the Ref Spad procedure. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_PerformRefSpadManagement(VL53L0_DEV Dev, + uint32_t *refSpadCount, uint8_t *isApertureSpads); + +/** + * @brief Applies Reference SPAD configuration + * + * @par Function Description + * This function applies a given number of reference spads, identified as + * either Aperture or Non-Aperture. + * The requested spad count and type are stored within the device specific + * parameters data for access by the host. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param refSpadCount Number of ref spads. + * @param isApertureSpads Defines if spads are of type + * aperture or non-aperture. + * 1:=aperture, 0:=Non-Aperture + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_REF_SPAD_INIT Error in the in the reference + * spad configuration. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_SetReferenceSpads(VL53L0_DEV Dev, + uint32_t refSpadCount, uint8_t isApertureSpads); + +/** + * @brief Retrieves SPAD configuration + * + * @par Function Description + * This function retrieves the current number of applied reference spads + * and also their type : Aperture or Non-Aperture. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param refSpadCount Number ref Spad Count + * @param isApertureSpads Reports if spads are of type + * aperture or non-aperture. + * 1:=aperture, 0:=Non-Aperture + * @return VL53L0_ERROR_NONE Success + * @return VL53L0_ERROR_REF_SPAD_INIT Error in the in the reference + * spad configuration. + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_API VL53L0_Error VL53L0_GetReferenceSpads(VL53L0_DEV Dev, + uint32_t *refSpadCount, uint8_t *isApertureSpads); + +/** @} VL53L0_SPADfunctions_group */ + +/** @} VL53L0_cut11_group */ + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L0_API_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_api_calibration.h b/drivers/input/misc/vl53L0/inc/vl53l0_api_calibration.h new file mode 100644 index 000000000000..df9b43987eb5 --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_api_calibration.h @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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. +*******************************************************************************/ + +#ifndef _VL53L0_API_CALIBRATION_H_ +#define _VL53L0_API_CALIBRATION_H_ + +#include "vl53l0_def.h" +#include "vl53l0_platform.h" + + +#ifdef __cplusplus +extern "C" { +#endif + +VL53L0_Error VL53L0_perform_xtalk_calibration(VL53L0_DEV Dev, + FixPoint1616_t XTalkCalDistance, + FixPoint1616_t *pXTalkCompensationRateMegaCps); + +VL53L0_Error VL53L0_perform_offset_calibration(VL53L0_DEV Dev, + FixPoint1616_t CalDistanceMilliMeter, + int32_t *pOffsetMicroMeter); + +VL53L0_Error VL53L0_set_offset_calibration_data_micro_meter(VL53L0_DEV Dev, + int32_t OffsetCalibrationDataMicroMeter); + +VL53L0_Error VL53L0_get_offset_calibration_data_micro_meter(VL53L0_DEV Dev, + int32_t *pOffsetCalibrationDataMicroMeter); + +VL53L0_Error VL53L0_apply_offset_adjustment(VL53L0_DEV Dev); + +VL53L0_Error VL53L0_perform_ref_spad_management(VL53L0_DEV Dev, + uint32_t *refSpadCount, uint8_t *isApertureSpads); + +VL53L0_Error VL53L0_set_reference_spads(VL53L0_DEV Dev, + uint32_t count, uint8_t isApertureSpads); + +VL53L0_Error VL53L0_get_reference_spads(VL53L0_DEV Dev, + uint32_t *pSpadCount, uint8_t *pIsApertureSpads); + +VL53L0_Error VL53L0_perform_phase_calibration(VL53L0_DEV Dev, + uint8_t *pPhaseCal, const uint8_t get_data_enable, + const uint8_t restore_config); + +VL53L0_Error VL53L0_perform_ref_calibration(VL53L0_DEV Dev, + uint8_t *pVhvSettings, uint8_t *pPhaseCal, uint8_t get_data_enable); + +VL53L0_Error VL53L0_set_ref_calibration(VL53L0_DEV Dev, + uint8_t VhvSettings, uint8_t PhaseCal); + +VL53L0_Error VL53L0_get_ref_calibration(VL53L0_DEV Dev, + uint8_t *pVhvSettings, uint8_t *pPhaseCal); + + + + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L0_API_CALIBRATION_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_api_core.h b/drivers/input/misc/vl53L0/inc/vl53l0_api_core.h new file mode 100644 index 000000000000..4596bbef20de --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_api_core.h @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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. +*******************************************************************************/ + +#ifndef _VL53L0_API_CORE_H_ +#define _VL53L0_API_CORE_H_ + +#include "vl53l0_def.h" +#include "vl53l0_platform.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +VL53L0_Error VL53L0_reverse_bytes(uint8_t *data, uint32_t size); + +VL53L0_Error VL53L0_measurement_poll_for_completion(VL53L0_DEV Dev); + +uint8_t VL53L0_encode_vcsel_period(uint8_t vcsel_period_pclks); + +uint8_t VL53L0_decode_vcsel_period(uint8_t vcsel_period_reg); + +uint32_t VL53L0_isqrt(uint32_t num); + +uint32_t VL53L0_quadrature_sum(uint32_t a, uint32_t b); + +VL53L0_Error VL53L0_get_info_from_device(VL53L0_DEV Dev, uint8_t option); + +VL53L0_Error VL53L0_set_vcsel_pulse_period(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, uint8_t VCSELPulsePeriodPCLK); + +VL53L0_Error VL53L0_get_vcsel_pulse_period(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, uint8_t *pVCSELPulsePeriodPCLK); + +uint32_t VL53L0_decode_timeout(uint16_t encoded_timeout); + +VL53L0_Error get_sequence_step_timeout(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, + uint32_t *pTimeOutMicroSecs); + +VL53L0_Error set_sequence_step_timeout(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, + uint32_t TimeOutMicroSecs); + +VL53L0_Error VL53L0_set_measurement_timing_budget_micro_seconds(VL53L0_DEV Dev, + uint32_t MeasurementTimingBudgetMicroSeconds); + +VL53L0_Error VL53L0_get_measurement_timing_budget_micro_seconds(VL53L0_DEV Dev, + uint32_t *pMeasurementTimingBudgetMicroSeconds); + +VL53L0_Error VL53L0_load_tuning_settings(VL53L0_DEV Dev, + uint8_t *pTuningSettingBuffer); + +VL53L0_Error VL53L0_calc_sigma_estimate(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData, + FixPoint1616_t *pSigmaEstimate); + +VL53L0_Error VL53L0_calc_dmax( + VL53L0_DEV Dev, FixPoint1616_t ambRateMeas, uint32_t *pdmax_mm); + +VL53L0_Error VL53L0_get_total_xtalk_rate(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData, + FixPoint1616_t *ptotal_xtalk_rate_mcps); + +VL53L0_Error VL53L0_get_total_signal_rate(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData, + FixPoint1616_t *ptotal_signal_rate_mcps); + +VL53L0_Error VL53L0_get_pal_range_status(VL53L0_DEV Dev, + uint8_t DeviceRangeStatus, + FixPoint1616_t SignalRate, + uint16_t EffectiveSpadRtnCount, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData, + uint8_t *pPalRangeStatus); + +uint32_t VL53L0_calc_timeout_mclks(VL53L0_DEV Dev, + uint32_t timeout_period_us, uint8_t vcsel_period_pclks); + +uint16_t VL53L0_encode_timeout(uint32_t timeout_macro_clks); + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L0_API_CORE_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_api_histogram.h b/drivers/input/misc/vl53L0/inc/vl53l0_api_histogram.h new file mode 100644 index 000000000000..e453d82eaec8 --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_api_histogram.h @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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. +*******************************************************************************/ + +#ifndef _VL53L0_API_HISTOGRAM_H_ +#define _VL53L0_API_HISTOGRAM_H_ + +#include "vl53l0_def.h" +#include "vl53l0_platform.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +VL53L0_Error VL53L0_confirm_measurement_start(VL53L0_DEV Dev); + +VL53L0_Error VL53L0_set_histogram_mode(VL53L0_DEV Dev, + VL53L0_HistogramModes HistogramMode); + +VL53L0_Error VL53L0_get_histogram_mode(VL53L0_DEV Dev, + VL53L0_HistogramModes * pHistogramMode); + +VL53L0_Error VL53L0_start_histogram_measurement(VL53L0_DEV Dev, + VL53L0_HistogramModes histoMode, + uint32_t count); + +VL53L0_Error VL53L0_perform_single_histogram_measurement(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData); + +VL53L0_Error VL53L0_get_histogram_measurement_data(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData); + +VL53L0_Error VL53L0_read_histo_measurement(VL53L0_DEV Dev, + uint32_t *histoData, uint32_t offset, VL53L0_HistogramModes histoMode); + +VL53L0_Error VL53L0_perform_xtalk_measurement(VL53L0_DEV dev, + uint32_t timeout_ms, FixPoint1616_t *pxtalk_per_spad, + uint8_t *pambient_too_high); + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L0_API_HISTOGRAM_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_api_ranging.h b/drivers/input/misc/vl53L0/inc/vl53l0_api_ranging.h new file mode 100644 index 000000000000..ba0bd03fe71f --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_api_ranging.h @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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. +*******************************************************************************/ + +#ifndef _VL53L0_API_RANGING_H_ +#define _VL53L0_API_RANGING_H_ + +#include "vl53l0_def.h" +#include "vl53l0_platform.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + + + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L0_API_RANGING_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_api_strings.h b/drivers/input/misc/vl53L0/inc/vl53l0_api_strings.h new file mode 100644 index 000000000000..2bb6a1e8ea33 --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_api_strings.h @@ -0,0 +1,277 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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. +*******************************************************************************/ + +#ifndef VL53L0_API_STRINGS_H_ +#define VL53L0_API_STRINGS_H_ + +#include "vl53l0_def.h" +#include "vl53l0_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +VL53L0_Error VL53L0_get_device_info(VL53L0_DEV Dev, + VL53L0_DeviceInfo_t *pVL53L0_DeviceInfo); + +VL53L0_Error VL53L0_get_device_error_string(VL53L0_DeviceError ErrorCode, + char *pDeviceErrorString); + +VL53L0_Error VL53L0_get_range_status_string(uint8_t RangeStatus, + char *pRangeStatusString); + +VL53L0_Error VL53L0_get_pal_error_string(VL53L0_Error PalErrorCode, + char *pPalErrorString); + +VL53L0_Error VL53L0_get_pal_state_string(VL53L0_State PalStateCode, + char *pPalStateString); + +VL53L0_Error VL53L0_get_sequence_steps_info( + VL53L0_SequenceStepId SequenceStepId, + char *pSequenceStepsString); + +VL53L0_Error VL53L0_get_limit_check_info(VL53L0_DEV Dev, uint16_t LimitCheckId, + char *pLimitCheckString); + + +#ifdef USE_EMPTY_STRING + #define VL53L0_STRING_DEVICE_INFO_NAME "" + #define VL53L0_STRING_DEVICE_INFO_NAME_TS0 "" + #define VL53L0_STRING_DEVICE_INFO_NAME_TS1 "" + #define VL53L0_STRING_DEVICE_INFO_NAME_TS2 "" + #define VL53L0_STRING_DEVICE_INFO_NAME_ES1 "" + #define VL53L0_STRING_DEVICE_INFO_TYPE "" + + /* PAL ERROR strings */ + #define VL53L0_STRING_ERROR_NONE "" + #define VL53L0_STRING_ERROR_CALIBRATION_WARNING "" + #define VL53L0_STRING_ERROR_MIN_CLIPPED "" + #define VL53L0_STRING_ERROR_UNDEFINED "" + #define VL53L0_STRING_ERROR_INVALID_PARAMS "" + #define VL53L0_STRING_ERROR_NOT_SUPPORTED "" + #define VL53L0_STRING_ERROR_RANGE_ERROR "" + #define VL53L0_STRING_ERROR_TIME_OUT "" + #define VL53L0_STRING_ERROR_MODE_NOT_SUPPORTED "" + #define VL53L0_STRING_ERROR_BUFFER_TOO_SMALL "" + #define VL53L0_STRING_ERROR_GPIO_NOT_EXISTING "" + #define VL53L0_STRING_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED "" + #define VL53L0_STRING_ERROR_CONTROL_INTERFACE "" + #define VL53L0_STRING_ERROR_INVALID_COMMAND "" + #define VL53L0_STRING_ERROR_DIVISION_BY_ZERO "" + #define VL53L0_STRING_ERROR_REF_SPAD_INIT "" + #define VL53L0_STRING_ERROR_NOT_IMPLEMENTED "" + + #define VL53L0_STRING_UNKNOW_ERROR_CODE "" + + + + /* Range Status */ + #define VL53L0_STRING_RANGESTATUS_NONE "" + #define VL53L0_STRING_RANGESTATUS_RANGEVALID "" + #define VL53L0_STRING_RANGESTATUS_SIGMA "" + #define VL53L0_STRING_RANGESTATUS_SIGNAL "" + #define VL53L0_STRING_RANGESTATUS_MINRANGE "" + #define VL53L0_STRING_RANGESTATUS_PHASE "" + #define VL53L0_STRING_RANGESTATUS_HW "" + + + /* Range Status */ + #define VL53L0_STRING_STATE_POWERDOWN "" + #define VL53L0_STRING_STATE_WAIT_STATICINIT "" + #define VL53L0_STRING_STATE_STANDBY "" + #define VL53L0_STRING_STATE_IDLE "" + #define VL53L0_STRING_STATE_RUNNING "" + #define VL53L0_STRING_STATE_UNKNOWN "" + #define VL53L0_STRING_STATE_ERROR "" + + + /* Device Specific */ + #define VL53L0_STRING_DEVICEERROR_NONE "" + #define VL53L0_STRING_DEVICEERROR_VCSELCONTINUITYTESTFAILURE "" + #define VL53L0_STRING_DEVICEERROR_VCSELWATCHDOGTESTFAILURE "" + #define VL53L0_STRING_DEVICEERROR_NOVHVVALUEFOUND "" + #define VL53L0_STRING_DEVICEERROR_MSRCNOTARGET "" + #define VL53L0_STRING_DEVICEERROR_SNRCHECK "" + #define VL53L0_STRING_DEVICEERROR_RANGEPHASECHECK "" + #define VL53L0_STRING_DEVICEERROR_SIGMATHRESHOLDCHECK "" + #define VL53L0_STRING_DEVICEERROR_TCC "" + #define VL53L0_STRING_DEVICEERROR_PHASECONSISTENCY "" + #define VL53L0_STRING_DEVICEERROR_MINCLIP "" + #define VL53L0_STRING_DEVICEERROR_RANGECOMPLETE "" + #define VL53L0_STRING_DEVICEERROR_ALGOUNDERFLOW "" + #define VL53L0_STRING_DEVICEERROR_ALGOOVERFLOW "" + #define VL53L0_STRING_DEVICEERROR_RANGEIGNORETHRESHOLD "" + #define VL53L0_STRING_DEVICEERROR_UNKNOWN "" + + /* Check Enable */ + #define VL53L0_STRING_CHECKENABLE_SIGMA_FINAL_RANGE "" + #define VL53L0_STRING_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE "" + #define VL53L0_STRING_CHECKENABLE_SIGNAL_REF_CLIP "" + #define VL53L0_STRING_CHECKENABLE_RANGE_IGNORE_THRESHOLD "" + + /* Sequence Step */ + #define VL53L0_STRING_SEQUENCESTEP_TCC "" + #define VL53L0_STRING_SEQUENCESTEP_DSS "" + #define VL53L0_STRING_SEQUENCESTEP_MSRC "" + #define VL53L0_STRING_SEQUENCESTEP_PRE_RANGE "" + #define VL53L0_STRING_SEQUENCESTEP_FINAL_RANGE "" +#else + #define VL53L0_STRING_DEVICE_INFO_NAME "VL53L0 cut1.0" + #define VL53L0_STRING_DEVICE_INFO_NAME_TS0 "VL53L0 TS0" + #define VL53L0_STRING_DEVICE_INFO_NAME_TS1 "VL53L0 TS1" + #define VL53L0_STRING_DEVICE_INFO_NAME_TS2 "VL53L0 TS2" + #define VL53L0_STRING_DEVICE_INFO_NAME_ES1 "VL53L0 ES1 or later" + #define VL53L0_STRING_DEVICE_INFO_TYPE "VL53L0" + + /* PAL ERROR strings */ + #define VL53L0_STRING_ERROR_NONE \ + "No Error" + #define VL53L0_STRING_ERROR_CALIBRATION_WARNING \ + "Calibration Warning Error" + #define VL53L0_STRING_ERROR_MIN_CLIPPED \ + "Min clipped error" + #define VL53L0_STRING_ERROR_UNDEFINED \ + "Undefined error" + #define VL53L0_STRING_ERROR_INVALID_PARAMS \ + "Invalid parameters error" + #define VL53L0_STRING_ERROR_NOT_SUPPORTED \ + "Not supported error" + #define VL53L0_STRING_ERROR_RANGE_ERROR \ + "Range error" + #define VL53L0_STRING_ERROR_TIME_OUT \ + "Time out error" + #define VL53L0_STRING_ERROR_MODE_NOT_SUPPORTED \ + "Mode not supported error" + #define VL53L0_STRING_ERROR_BUFFER_TOO_SMALL \ + "Buffer too small" + #define VL53L0_STRING_ERROR_GPIO_NOT_EXISTING \ + "GPIO not existing" + #define VL53L0_STRING_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED \ + "GPIO funct not supported" + #define VL53L0_STRING_ERROR_INTERRUPT_NOT_CLEARED \ + "Interrupt not Cleared" + #define VL53L0_STRING_ERROR_CONTROL_INTERFACE \ + "Control Interface Error" + #define VL53L0_STRING_ERROR_INVALID_COMMAND \ + "Invalid Command Error" + #define VL53L0_STRING_ERROR_DIVISION_BY_ZERO \ + "Division by zero Error" + #define VL53L0_STRING_ERROR_REF_SPAD_INIT \ + "Reference Spad Init Error" + #define VL53L0_STRING_ERROR_NOT_IMPLEMENTED \ + "Not implemented error" + + #define VL53L0_STRING_UNKNOW_ERROR_CODE \ + "Unknown Error Code" + + + + /* Range Status */ + #define VL53L0_STRING_RANGESTATUS_NONE "No Update" + #define VL53L0_STRING_RANGESTATUS_RANGEVALID "Range Valid" + #define VL53L0_STRING_RANGESTATUS_SIGMA "Sigma Fail" + #define VL53L0_STRING_RANGESTATUS_SIGNAL "Signal Fail" + #define VL53L0_STRING_RANGESTATUS_MINRANGE "Min Range Fail" + #define VL53L0_STRING_RANGESTATUS_PHASE "Phase Fail" + #define VL53L0_STRING_RANGESTATUS_HW "Hardware Fail" + + + /* Range Status */ + #define VL53L0_STRING_STATE_POWERDOWN "POWERDOWN State" + #define VL53L0_STRING_STATE_WAIT_STATICINIT \ + "Wait for staticinit State" + #define VL53L0_STRING_STATE_STANDBY "STANDBY State" + #define VL53L0_STRING_STATE_IDLE "IDLE State" + #define VL53L0_STRING_STATE_RUNNING "RUNNING State" + #define VL53L0_STRING_STATE_UNKNOWN "UNKNOWN State" + #define VL53L0_STRING_STATE_ERROR "ERROR State" + + + /* Device Specific */ + #define VL53L0_STRING_DEVICEERROR_NONE "No Update" + #define VL53L0_STRING_DEVICEERROR_VCSELCONTINUITYTESTFAILURE \ + "VCSEL Continuity Test Failure" + #define VL53L0_STRING_DEVICEERROR_VCSELWATCHDOGTESTFAILURE \ + "VCSEL Watchdog Test Failure" + #define VL53L0_STRING_DEVICEERROR_NOVHVVALUEFOUND \ + "No VHV Value found" + #define VL53L0_STRING_DEVICEERROR_MSRCNOTARGET \ + "MSRC No Target Error" + #define VL53L0_STRING_DEVICEERROR_SNRCHECK \ + "SNR Check Exit" + #define VL53L0_STRING_DEVICEERROR_RANGEPHASECHECK \ + "Range Phase Check Error" + #define VL53L0_STRING_DEVICEERROR_SIGMATHRESHOLDCHECK \ + "Sigma Threshold Check Error" + #define VL53L0_STRING_DEVICEERROR_TCC \ + "TCC Error" + #define VL53L0_STRING_DEVICEERROR_PHASECONSISTENCY \ + "Phase Consistency Error" + #define VL53L0_STRING_DEVICEERROR_MINCLIP \ + "Min Clip Error" + #define VL53L0_STRING_DEVICEERROR_RANGECOMPLETE \ + "Range Complete" + #define VL53L0_STRING_DEVICEERROR_ALGOUNDERFLOW \ + "Range Algo Underflow Error" + #define VL53L0_STRING_DEVICEERROR_ALGOOVERFLOW \ + "Range Algo Overlow Error" + #define VL53L0_STRING_DEVICEERROR_RANGEIGNORETHRESHOLD \ + "Range Ignore Threshold Error" + #define VL53L0_STRING_DEVICEERROR_UNKNOWN \ + "Unknown error code" + + /* Check Enable */ + #define VL53L0_STRING_CHECKENABLE_SIGMA_FINAL_RANGE \ + "SIGMA FINAL RANGE" + #define VL53L0_STRING_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE \ + "SIGNAL RATE FINAL RANGE" + #define VL53L0_STRING_CHECKENABLE_SIGNAL_REF_CLIP \ + "SIGNAL REF CLIP" + #define VL53L0_STRING_CHECKENABLE_RANGE_IGNORE_THRESHOLD \ + "RANGE IGNORE THRESHOLD" + #define VL53L0_STRING_CHECKENABLE_SIGNAL_RATE_MSRC \ + "SIGNAL RATE MSRC" + #define VL53L0_STRING_CHECKENABLE_SIGNAL_RATE_PRE_RANGE \ + "SIGNAL RATE PRE RANGE" + + /* Sequence Step */ + #define VL53L0_STRING_SEQUENCESTEP_TCC "TCC" + #define VL53L0_STRING_SEQUENCESTEP_DSS "DSS" + #define VL53L0_STRING_SEQUENCESTEP_MSRC "MSRC" + #define VL53L0_STRING_SEQUENCESTEP_PRE_RANGE "PRE RANGE" + #define VL53L0_STRING_SEQUENCESTEP_FINAL_RANGE "FINAL RANGE" +#endif /* USE_EMPTY_STRING */ + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_def.h b/drivers/input/misc/vl53L0/inc/vl53l0_def.h new file mode 100644 index 000000000000..cedfd5008055 --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_def.h @@ -0,0 +1,663 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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. +*******************************************************************************/ + +/** + * @file VL53L0_def.h + * + * @brief Type definitions for VL53L0 API. + * + */ + + +#ifndef _VL53L0_DEF_H_ +#define _VL53L0_DEF_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup VL53L0_globaldefine_group VL53L0 Defines + * @brief VL53L0 Defines + * @{ + */ + + +/** PAL SPECIFICATION major version */ +#define VL53L010_SPECIFICATION_VER_MAJOR 1 +/** PAL SPECIFICATION minor version */ +#define VL53L010_SPECIFICATION_VER_MINOR 2 +/** PAL SPECIFICATION sub version */ +#define VL53L010_SPECIFICATION_VER_SUB 7 +/** PAL SPECIFICATION sub version */ +#define VL53L010_SPECIFICATION_VER_REVISION 1440 + +/** VL53L0 PAL IMPLEMENTATION major version */ +#define VL53L010_IMPLEMENTATION_VER_MAJOR 1 +/** VL53L0 PAL IMPLEMENTATION minor version */ +#define VL53L010_IMPLEMENTATION_VER_MINOR 0 +/** VL53L0 PAL IMPLEMENTATION sub version */ +#define VL53L010_IMPLEMENTATION_VER_SUB 9 +/** VL53L0 PAL IMPLEMENTATION sub version */ +#define VL53L010_IMPLEMENTATION_VER_REVISION 3673 + +/** PAL SPECIFICATION major version */ +#define VL53L0_SPECIFICATION_VER_MAJOR 1 +/** PAL SPECIFICATION minor version */ +#define VL53L0_SPECIFICATION_VER_MINOR 2 +/** PAL SPECIFICATION sub version */ +#define VL53L0_SPECIFICATION_VER_SUB 7 +/** PAL SPECIFICATION sub version */ +#define VL53L0_SPECIFICATION_VER_REVISION 1440 + +/** VL53L0 PAL IMPLEMENTATION major version */ +#define VL53L0_IMPLEMENTATION_VER_MAJOR 1 +/** VL53L0 PAL IMPLEMENTATION minor version */ +#define VL53L0_IMPLEMENTATION_VER_MINOR 1 +/** VL53L0 PAL IMPLEMENTATION sub version */ +#define VL53L0_IMPLEMENTATION_VER_SUB 23 +/** VL53L0 PAL IMPLEMENTATION sub version */ +#define VL53L0_IMPLEMENTATION_VER_REVISION 4960 +#define VL53L0_DEFAULT_MAX_LOOP 2000 +#define VL53L0_MAX_STRING_LENGTH 32 + + +#include "vl53l0_device.h" +#include "vl53l0_types.h" + + +/**************************************** + * PRIVATE define do not edit + ****************************************/ + +/** @brief Defines the parameters of the Get Version Functions + */ +typedef struct { + uint32_t revision; /*!< revision number */ + uint8_t major; /*!< major number */ + uint8_t minor; /*!< minor number */ + uint8_t build; /*!< build number */ +} VL53L0_Version_t; + + +/** @brief Defines the parameters of the Get Device Info Functions + */ +typedef struct { + char Name[VL53L0_MAX_STRING_LENGTH]; + /*!< Name of the Device e.g. Left_Distance */ + char Type[VL53L0_MAX_STRING_LENGTH]; + /*!< Type of the Device e.g VL53L0 */ + char ProductId[VL53L0_MAX_STRING_LENGTH]; + /*!< Product Identifier String */ + uint8_t ProductType; + /*!< Product Type, VL53L0 = 1, VL53L1 = 2 */ + uint8_t ProductRevisionMajor; + /*!< Product revision major */ + uint8_t ProductRevisionMinor; + /*!< Product revision minor */ +} VL53L0_DeviceInfo_t; + + +/** @defgroup VL53L0_define_Error_group Error and Warning code returned by API + * The following DEFINE are used to identify the PAL ERROR + * @{ + */ + +typedef int8_t VL53L0_Error; + +#define VL53L0_ERROR_NONE ((VL53L0_Error) 0) +#define VL53L0_ERROR_CALIBRATION_WARNING ((VL53L0_Error)-1) + /*!< Warning invalid calibration data may be in used + * \a VL53L0_InitData() + * \a VL53L0_GetOffsetCalibrationData + * \a VL53L0_SetOffsetCalibrationData + */ +#define VL53L0_ERROR_MIN_CLIPPED ((VL53L0_Error)-2) + /*!< Warning parameter passed was clipped to min before to be applied */ + +#define VL53L0_ERROR_UNDEFINED ((VL53L0_Error)-3) + /*!< Unqualified error */ +#define VL53L0_ERROR_INVALID_PARAMS ((VL53L0_Error)-4) + /*!< Parameter passed is invalid or out of range */ +#define VL53L0_ERROR_NOT_SUPPORTED ((VL53L0_Error)-5) + /*!< Function is not supported in current mode or configuration */ +#define VL53L0_ERROR_RANGE_ERROR ((VL53L0_Error)-6) + /*!< Device report a ranging error interrupt status */ +#define VL53L0_ERROR_TIME_OUT ((VL53L0_Error)-7) + /*!< Aborted due to time out */ +#define VL53L0_ERROR_MODE_NOT_SUPPORTED ((VL53L0_Error)-8) + /*!< Asked mode is not supported by the device */ +#define VL53L0_ERROR_BUFFER_TOO_SMALL ((VL53L0_Error)-9) + /*!< ... */ +#define VL53L0_ERROR_GPIO_NOT_EXISTING ((VL53L0_Error)-10) + /*!< User tried to setup a non-existing GPIO pin */ +#define VL53L0_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED ((VL53L0_Error)-11) + /*!< unsupported GPIO functionality */ +#define VL53L0_ERROR_INTERRUPT_NOT_CLEARED ((VL53L0_Error)-12) + /*!< Error during interrupt clear */ +#define VL53L0_ERROR_CONTROL_INTERFACE ((VL53L0_Error)-20) + /*!< error reported from IO functions */ +#define VL53L0_ERROR_INVALID_COMMAND ((VL53L0_Error)-30) + /*!< The command is not allowed in the current device state + * (power down) + */ +#define VL53L0_ERROR_DIVISION_BY_ZERO ((VL53L0_Error)-40) + /*!< In the function a division by zero occurs */ +#define VL53L0_ERROR_REF_SPAD_INIT ((VL53L0_Error)-50) + /*!< Error during reference SPAD initialization */ +#define VL53L0_ERROR_NOT_IMPLEMENTED ((VL53L0_Error)-99) + /*!< Tells requested functionality has not been implemented yet or + * not compatible with the device + */ +/** @} VL53L0_define_Error_group */ + + +/** @defgroup VL53L0_define_DeviceModes_group Defines Device modes + * Defines all possible modes for the device + * @{ + */ +typedef uint8_t VL53L0_DeviceModes; + +#define VL53L0_DEVICEMODE_SINGLE_RANGING ((VL53L0_DeviceModes) 0) +#define VL53L0_DEVICEMODE_CONTINUOUS_RANGING ((VL53L0_DeviceModes) 1) +#define VL53L0_DEVICEMODE_SINGLE_HISTOGRAM ((VL53L0_DeviceModes) 2) +#define VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING ((VL53L0_DeviceModes) 3) +#define VL53L0_DEVICEMODE_SINGLE_ALS ((VL53L0_DeviceModes) 10) +#define VL53L0_DEVICEMODE_GPIO_DRIVE ((VL53L0_DeviceModes) 20) +#define VL53L0_DEVICEMODE_GPIO_OSC ((VL53L0_DeviceModes) 21) + /* ... Modes to be added depending on device */ +/** @} VL53L0_define_DeviceModes_group */ + + + +/** @defgroup VL53L0_define_HistogramModes_group Defines Histogram modes + * Defines all possible Histogram modes for the device + * @{ + */ +typedef uint8_t VL53L0_HistogramModes; + +#define VL53L0_HISTOGRAMMODE_DISABLED ((VL53L0_HistogramModes) 0) + /*!< Histogram Disabled */ +#define VL53L0_HISTOGRAMMODE_REFERENCE_ONLY ((VL53L0_HistogramModes) 1) + /*!< Histogram Reference array only */ +#define VL53L0_HISTOGRAMMODE_RETURN_ONLY ((VL53L0_HistogramModes) 2) + /*!< Histogram Return array only */ +#define VL53L0_HISTOGRAMMODE_BOTH ((VL53L0_HistogramModes) 3) + /*!< Histogram both Reference and Return Arrays */ + /* ... Modes to be added depending on device */ +/** @} VL53L0_define_HistogramModes_group */ + + +/** @defgroup VL53L0_define_PowerModes_group List of available Power Modes + * List of available Power Modes + * @{ + */ + +typedef uint8_t VL53L0_PowerModes; + +#define VL53L0_POWERMODE_STANDBY_LEVEL1 ((VL53L0_PowerModes) 0) + /*!< Standby level 1 */ +#define VL53L0_POWERMODE_STANDBY_LEVEL2 ((VL53L0_PowerModes) 1) + /*!< Standby level 2 */ +#define VL53L0_POWERMODE_IDLE_LEVEL1 ((VL53L0_PowerModes) 2) + /*!< Idle level 1 */ +#define VL53L0_POWERMODE_IDLE_LEVEL2 ((VL53L0_PowerModes) 3) + /*!< Idle level 2 */ + +/** @} VL53L0_define_PowerModes_group */ + + +#define VL53L0_DMAX_LUT_SIZE 7 + /*!< Defines the number of items in the DMAX lookup table */ + +/** @brief Structure defining data pair that makes up the DMAX Lookup table. + */ +typedef struct { + FixPoint1616_t ambRate_mcps[VL53L0_DMAX_LUT_SIZE]; + /*!< Ambient rate (mcps) */ + FixPoint1616_t dmax_mm[VL53L0_DMAX_LUT_SIZE]; + /*!< DMAX Value (mm) */ +} VL53L0_DMaxLUT_t; + +/** @brief Defines all parameters for the device + */ +typedef struct { + VL53L0_DeviceModes DeviceMode; + /*!< Defines type of measurement to be done for the next measure */ + VL53L0_HistogramModes HistogramMode; + /*!< Defines type of histogram measurement to be done for the next + * measure + */ + uint32_t MeasurementTimingBudgetMicroSeconds; + /*!< Defines the allowed total time for a single measurement */ + uint32_t InterMeasurementPeriodMilliSeconds; + /*!< Defines time between two consecutive measurements (between two + * measurement starts). If set to 0 means back-to-back mode + */ + uint8_t XTalkCompensationEnable; + /*!< Tells if Crosstalk compensation shall be enable or not */ + uint16_t XTalkCompensationRangeMilliMeter; + /*!< CrossTalk compensation range in millimeter */ + FixPoint1616_t XTalkCompensationRateMegaCps; + /*!< CrossTalk compensation rate in Mega counts per seconds. + * Expressed in 16.16 fixed point format. + */ + int32_t RangeOffsetMicroMeters; + /*!< Range offset adjustment (mm). */ + + uint8_t LimitChecksEnable[VL53L0_CHECKENABLE_NUMBER_OF_CHECKS]; + /*!< This Array store all the Limit Check enable for this device. */ + uint8_t LimitChecksStatus[VL53L0_CHECKENABLE_NUMBER_OF_CHECKS]; + /*!< This Array store all the Status of the check linked to last + * measurement. + */ + FixPoint1616_t LimitChecksValue[VL53L0_CHECKENABLE_NUMBER_OF_CHECKS]; + /*!< This Array store all the Limit Check value for this device */ + + VL53L0_DMaxLUT_t dmax_lut; + /*!< Lookup table defining ambient rates and associated + * dmax values. + */ + + uint8_t WrapAroundCheckEnable; + /*!< Tells if Wrap Around Check shall be enable or not */ +} VL53L0_DeviceParameters_t; + + +/** @defgroup VL53L0_define_State_group Defines the current status of the device + * Defines the current status of the device + * @{ + */ + +typedef uint8_t VL53L0_State; + +#define VL53L0_STATE_POWERDOWN ((VL53L0_State) 0) + /*!< Device is in HW reset */ +#define VL53L0_STATE_WAIT_STATICINIT ((VL53L0_State) 1) + /*!< Device is initialized and wait for static initialization */ +#define VL53L0_STATE_STANDBY ((VL53L0_State) 2) + /*!< Device is in Low power Standby mode */ +#define VL53L0_STATE_IDLE ((VL53L0_State) 3) + /*!< Device has been initialized and ready to do measurements */ +#define VL53L0_STATE_RUNNING ((VL53L0_State) 4) + /*!< Device is performing measurement */ +#define VL53L0_STATE_UNKNOWN ((VL53L0_State) 98) + /*!< Device is in unknown state and need to be rebooted */ +#define VL53L0_STATE_ERROR ((VL53L0_State) 99) + /*!< Device is in error state and need to be rebooted */ + +/** @} VL53L0_define_State_group */ + + +/** + * @struct VL53L0_RangeData_t + * @brief Range measurement data. + */ +typedef struct { + uint32_t TimeStamp; /*!< 32-bit time stamp. */ + uint32_t MeasurementTimeUsec; + /*!< Give the Measurement time needed by the device to do the + * measurement. + */ + + + uint16_t RangeMilliMeter; /*!< range distance in millimeter. */ + + uint16_t RangeDMaxMilliMeter; + /*!< Tells what is the maximum detection distance of the device + * in current setup and environment conditions (Filled when + * applicable) + */ + + FixPoint1616_t SignalRateRtnMegaCps; + /*!< Return signal rate (MCPS)\n these is a 16.16 fix point + * value, which is effectively a measure of target + * reflectance. + */ + FixPoint1616_t AmbientRateRtnMegaCps; + /*!< Return ambient rate (MCPS)\n these is a 16.16 fix point + * value, which is effectively a measure of the ambien + * t light. + */ + + uint16_t EffectiveSpadRtnCount; + /*!< Return the effective SPAD count for the return signal. + * To obtain Real value it should be divided by 256 + */ + + uint8_t ZoneId; + /*!< Denotes which zone and range scheduler stage the range + * data relates to. + */ + uint8_t RangeFractionalPart; + /*!< Fractional part of range distance. Final value is a + * FixPoint168 value. + */ + uint8_t RangeStatus; + /*!< Range Status for the current measurement. This is device + * dependent. Value = 0 means value is valid. + * See \ref RangeStatusPage + */ +} VL53L0_RangingMeasurementData_t; + + +#define VL53L0_HISTOGRAM_BUFFER_SIZE 24 + +/** + * @struct VL53L0_HistogramData_t + * @brief Histogram measurement data. + */ +typedef struct { + /* Histogram Measurement data */ + uint32_t HistogramData[VL53L0_HISTOGRAM_BUFFER_SIZE]; + /*!< Histogram data */ + /*!< Indicate the types of histogram data : + *Return only, Reference only, both Return and Reference + */ + uint8_t HistogramType; + uint8_t FirstBin; /*!< First Bin value */ + uint8_t BufferSize; /*!< Buffer Size - Set by the user.*/ + uint8_t NumberOfBins; + /*!< Number of bins filled by the histogram measurement */ + + VL53L0_DeviceError ErrorStatus; + /*!< Error status of the current measurement. \n + * see @a ::VL53L0_DeviceError @a VL53L0_GetStatusErrorString() + */ +} VL53L0_HistogramMeasurementData_t; + +#define VL53L0_REF_SPAD_BUFFER_SIZE 6 + +/** + * @struct VL53L0_SpadData_t + * @brief Spad Configuration Data. + */ +typedef struct { + uint8_t RefSpadEnables[VL53L0_REF_SPAD_BUFFER_SIZE]; + /*!< Reference Spad Enables */ + uint8_t RefGoodSpadMap[VL53L0_REF_SPAD_BUFFER_SIZE]; + /*!< Reference Spad Good Spad Map */ +} VL53L0_SpadData_t; + +typedef struct { + FixPoint1616_t OscFrequencyMHz; /* Frequency used */ + + uint16_t LastEncodedTimeout; + /* last encoded Time out used for timing budget*/ + + VL53L0_GpioFunctionality Pin0GpioFunctionality; + /* store the functionality of the GPIO: pin0 */ + + uint32_t FinalRangeTimeoutMicroSecs; + /*!< Execution time of the final range*/ + uint8_t FinalRangeVcselPulsePeriod; + /*!< Vcsel pulse period (pll clocks) for the final range measurement*/ + uint32_t PreRangeTimeoutMicroSecs; + /*!< Execution time of the final range*/ + uint8_t PreRangeVcselPulsePeriod; + /*!< Vcsel pulse period (pll clocks) for the pre-range measurement*/ + + uint16_t SigmaEstRefArray; + /*!< Reference array sigma value in 1/100th of [mm] e.g. 100 = 1mm */ + uint16_t SigmaEstEffPulseWidth; + /*!< Effective Pulse width for sigma estimate in 1/100th + * of ns e.g. 900 = 9.0ns + */ + uint16_t SigmaEstEffAmbWidth; + /*!< Effective Ambient width for sigma estimate in 1/100th of ns + * e.g. 500 = 5.0ns + */ + + + /* Indicate if read from device has been done (==1) or not (==0) */ + uint8_t ReadDataFromDeviceDone; + uint8_t ModuleId; /* Module ID */ + uint8_t Revision; /* test Revision */ + char ProductId[VL53L0_MAX_STRING_LENGTH]; + /* Product Identifier String */ + uint8_t ReferenceSpadCount; /* used for ref spad management */ + uint8_t ReferenceSpadType; /* used for ref spad management */ + uint8_t RefSpadsInitialised; /* reports if ref spads are initialised. */ + uint32_t PartUIDUpper; /*!< Unique Part ID Upper */ + uint32_t PartUIDLower; /*!< Unique Part ID Lower */ + /*!< Peek Signal rate at 400 mm*/ + FixPoint1616_t SignalRateMeasFixed400mm; + +} VL53L0_DeviceSpecificParameters_t; + +/** + * @struct VL53L0_DevData_t + * + * @brief VL53L0 PAL device ST private data structure \n + * End user should never access any of these field directly + * + * These must never access directly but only via macro + */ +typedef struct { + int32_t Part2PartOffsetNVMMicroMeter; + /*!< backed up NVM value */ + int32_t Part2PartOffsetAdjustmentNVMMicroMeter; + /*!< backed up NVM value representing additional offset adjustment */ + VL53L0_DeviceParameters_t CurrentParameters; + /*!< Current Device Parameter */ + VL53L0_RangingMeasurementData_t LastRangeMeasure; + /*!< Ranging Data */ + VL53L0_HistogramMeasurementData_t LastHistogramMeasure; + /*!< Histogram Data */ + VL53L0_DeviceSpecificParameters_t DeviceSpecificParameters; + /*!< Parameters specific to the device */ + VL53L0_SpadData_t SpadData; + /*!< Spad Data */ + uint8_t SequenceConfig; + /*!< Internal value for the sequence config */ + uint8_t RangeFractionalEnable; + /*!< Enable/Disable fractional part of ranging data */ + VL53L0_State PalState; + /*!< Current state of the PAL for this device */ + VL53L0_PowerModes PowerMode; + /*!< Current Power Mode */ + uint16_t SigmaEstRefArray; + /*!< Reference array sigma value in 1/100th of [mm] e.g. 100 = 1mm */ + uint16_t SigmaEstEffPulseWidth; + /*!< Effective Pulse width for sigma estimate in 1/100th + * of ns e.g. 900 = 9.0ns + */ + uint16_t SigmaEstEffAmbWidth; + /*!< Effective Ambient width for sigma estimate in 1/100th of ns + * e.g. 500 = 5.0ns + */ + uint8_t StopVariable; + /*!< StopVariable used during the stop sequence */ + uint16_t targetRefRate; + /*!< Target Ambient Rate for Ref spad management */ + FixPoint1616_t SigmaEstimate; + /*!< Sigma Estimate - based on ambient & VCSEL rates and + * signal_total_events + */ + FixPoint1616_t SignalEstimate; + /*!< Signal Estimate - based on ambient & VCSEL rates and cross talk */ + FixPoint1616_t LastSignalRefMcps; + /*!< Latest Signal ref in Mcps */ + uint8_t *pTuningSettingsPointer; + /*!< Pointer for Tuning Settings table */ + uint8_t UseInternalTuningSettings; + /*!< Indicate if we use Tuning Settings table */ + uint16_t LinearityCorrectiveGain; + /*!< Linearity Corrective Gain value in x1000 */ +} VL53L0_DevData_t; + + +/** @defgroup VL53L0_define_InterruptPolarity_group Defines the Polarity + * of the Interrupt + * Defines the Polarity of the Interrupt + * @{ + */ +typedef uint8_t VL53L0_InterruptPolarity; + +#define VL53L0_INTERRUPTPOLARITY_LOW ((VL53L0_InterruptPolarity) 0) +/*!< Set active low polarity best setup for falling edge. */ +#define VL53L0_INTERRUPTPOLARITY_HIGH ((VL53L0_InterruptPolarity) 1) +/*!< Set active high polarity best setup for rising edge. */ + +/** @} VL53L0_define_InterruptPolarity_group */ + + +/** @defgroup VL53L0_define_VcselPeriod_group Vcsel Period Defines + * Defines the range measurement for which to access the vcsel period. + * @{ + */ +typedef uint8_t VL53L0_VcselPeriod; + +#define VL53L0_VCSEL_PERIOD_PRE_RANGE ((VL53L0_VcselPeriod) 0) +/*!>9)&0xFFFF) +#define VL53L0_FIXPOINT97TOFIXPOINT1616(Value) \ + (FixPoint1616_t)(Value<<9) + +#define VL53L0_FIXPOINT1616TOFIXPOINT88(Value) \ + (uint16_t)((Value>>8)&0xFFFF) +#define VL53L0_FIXPOINT88TOFIXPOINT1616(Value) \ + (FixPoint1616_t)(Value<<8) + +#define VL53L0_FIXPOINT1616TOFIXPOINT412(Value) \ + (uint16_t)((Value>>4)&0xFFFF) +#define VL53L0_FIXPOINT412TOFIXPOINT1616(Value) \ + (FixPoint1616_t)(Value<<4) + +#define VL53L0_FIXPOINT1616TOFIXPOINT313(Value) \ + (uint16_t)((Value>>3)&0xFFFF) +#define VL53L0_FIXPOINT313TOFIXPOINT1616(Value) \ + (FixPoint1616_t)(Value<<3) + +#define VL53L0_FIXPOINT1616TOFIXPOINT08(Value) \ + (uint8_t)((Value>>8)&0x00FF) +#define VL53L0_FIXPOINT08TOFIXPOINT1616(Value) \ + (FixPoint1616_t)(Value<<8) + +#define VL53L0_FIXPOINT1616TOFIXPOINT53(Value) \ + (uint8_t)((Value>>13)&0x00FF) +#define VL53L0_FIXPOINT53TOFIXPOINT1616(Value) \ + (FixPoint1616_t)(Value<<13) + +#define VL53L0_FIXPOINT1616TOFIXPOINT102(Value) \ + (uint16_t)((Value>>14)&0x0FFF) +#define VL53L0_FIXPOINT102TOFIXPOINT1616(Value) \ + (FixPoint1616_t)(Value<<12) + +#define VL53L0_MAKEUINT16(lsb, msb) (uint16_t)((((uint16_t)msb)<<8) + \ + (uint16_t)lsb) + +/** @} VL53L0_define_GeneralMacro_group */ + +/** @} VL53L0_globaldefine_group */ + + + + + + + +#ifdef __cplusplus +} +#endif + + +#endif /* _VL53L0_DEF_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_device.h b/drivers/input/misc/vl53L0/inc/vl53l0_device.h new file mode 100644 index 000000000000..b69b9cf72279 --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_device.h @@ -0,0 +1,261 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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. +*******************************************************************************/ + +/** + * Device specific defines. To be adapted by implementer for the targeted + * device. + */ + +#ifndef _VL53L0_DEVICE_H_ +#define _VL53L0_DEVICE_H_ + +#include "vl53l0_types.h" + + +/** @defgroup VL53L0_DevSpecDefines_group VL53L0 cut1.1 Device Specific Defines + * @brief VL53L0 cut1.1 Device Specific Defines + * @{ + */ + + +/** @defgroup VL53L0_DeviceError_group Device Error + * @brief Device Error code + * + * This enum is Device specific it should be updated in the implementation + * Use @a VL53L0_GetStatusErrorString() to get the string. + * It is related to Status Register of the Device. + * @{ + */ +typedef uint8_t VL53L0_DeviceError; + +#define VL53L0_DEVICEERROR_NONE ((VL53L0_DeviceError) 0) + /*!< 0 NoError */ +#define VL53L0_DEVICEERROR_VCSELCONTINUITYTESTFAILURE ((VL53L0_DeviceError) 1) +#define VL53L0_DEVICEERROR_VCSELWATCHDOGTESTFAILURE ((VL53L0_DeviceError) 2) +#define VL53L0_DEVICEERROR_NOVHVVALUEFOUND ((VL53L0_DeviceError) 3) +#define VL53L0_DEVICEERROR_MSRCNOTARGET ((VL53L0_DeviceError) 4) +#define VL53L0_DEVICEERROR_SNRCHECK ((VL53L0_DeviceError) 5) +#define VL53L0_DEVICEERROR_RANGEPHASECHECK ((VL53L0_DeviceError) 6) +#define VL53L0_DEVICEERROR_SIGMATHRESHOLDCHECK ((VL53L0_DeviceError) 7) +#define VL53L0_DEVICEERROR_TCC ((VL53L0_DeviceError) 8) +#define VL53L0_DEVICEERROR_PHASECONSISTENCY ((VL53L0_DeviceError) 9) +#define VL53L0_DEVICEERROR_MINCLIP ((VL53L0_DeviceError) 10) +#define VL53L0_DEVICEERROR_RANGECOMPLETE ((VL53L0_DeviceError) 11) +#define VL53L0_DEVICEERROR_ALGOUNDERFLOW ((VL53L0_DeviceError) 12) +#define VL53L0_DEVICEERROR_ALGOOVERFLOW ((VL53L0_DeviceError) 13) +#define VL53L0_DEVICEERROR_RANGEIGNORETHRESHOLD ((VL53L0_DeviceError) 14) + +/** @} end of VL53L0_DeviceError_group */ + + +/** @defgroup VL53L0_CheckEnable_group Check Enable list + * @brief Check Enable code + * + * Define used to specify the LimitCheckId. + * Use @a VL53L0_GetLimitCheckInfo() to get the string. + * @{ + */ + +#define VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE 0 +#define VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE 1 +#define VL53L0_CHECKENABLE_SIGNAL_REF_CLIP 2 +#define VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD 3 +#define VL53L0_CHECKENABLE_SIGNAL_RATE_MSRC 4 +#define VL53L0_CHECKENABLE_SIGNAL_RATE_PRE_RANGE 5 + +#define VL53L0_CHECKENABLE_NUMBER_OF_CHECKS 6 + +/** @} end of VL53L0_CheckEnable_group */ + + +/** @defgroup VL53L0_GpioFunctionality_group Gpio Functionality + * @brief Defines the different functionalities for the device GPIO(s) + * @{ + */ +typedef uint8_t VL53L0_GpioFunctionality; + +#define VL53L0_GPIOFUNCTIONALITY_OFF \ + ((VL53L0_GpioFunctionality) 0) /*!< NO Interrupt */ +#define VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW \ + ((VL53L0_GpioFunctionality) 1) /*!< Level Low (value < thresh_low) */ +#define VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH \ + ((VL53L0_GpioFunctionality) 2) /*!< Level High (value > thresh_high) */ +#define VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT \ + ((VL53L0_GpioFunctionality) 3) + /*!< Out Of Window (value < thresh_low OR value > thresh_high) */ +#define VL53L0_GPIOFUNCTIONALITY_NEW_MEASURE_READY \ + ((VL53L0_GpioFunctionality) 4) /*!< New Sample Ready */ + +/** @} end of VL53L0_GpioFunctionality_group */ + + +/* Device register map */ + +/** @defgroup VL53L0_DefineRegisters_group Define Registers + * @brief List of all the defined registers + * @{ + */ +#define VL53L0_REG_SYSRANGE_START 0x000 + /** mask existing bit in #VL53L0_REG_SYSRANGE_START*/ + #define VL53L0_REG_SYSRANGE_MODE_MASK 0x0F + /** bit 0 in #VL53L0_REG_SYSRANGE_START write 1 toggle state in + * continuous mode and arm next shot in single shot mode + */ + #define VL53L0_REG_SYSRANGE_MODE_START_STOP 0x01 + /** bit 1 write 0 in #VL53L0_REG_SYSRANGE_START set single shot mode */ + #define VL53L0_REG_SYSRANGE_MODE_SINGLESHOT 0x00 + /** bit 1 write 1 in #VL53L0_REG_SYSRANGE_START set back-to-back + * operation mode + */ + #define VL53L0_REG_SYSRANGE_MODE_BACKTOBACK 0x02 + /** bit 2 write 1 in #VL53L0_REG_SYSRANGE_START set timed operation + * mode + */ + #define VL53L0_REG_SYSRANGE_MODE_TIMED 0x04 + /** bit 3 write 1 in #VL53L0_REG_SYSRANGE_START set histogram operation + * mode + */ + #define VL53L0_REG_SYSRANGE_MODE_HISTOGRAM 0x08 + + +#define VL53L0_REG_SYSTEM_THRESH_HIGH 0x000C +#define VL53L0_REG_SYSTEM_THRESH_LOW 0x000E + + +#define VL53L0_REG_SYSTEM_SEQUENCE_CONFIG 0x0001 +#define VL53L0_REG_SYSTEM_RANGE_CONFIG 0x0009 +#define VL53L0_REG_SYSTEM_INTERMEASUREMENT_PERIOD 0x0004 + + +#define VL53L0_REG_SYSTEM_INTERRUPT_CONFIG_GPIO 0x000A + #define VL53L0_REG_SYSTEM_INTERRUPT_GPIO_DISABLED 0x00 + #define VL53L0_REG_SYSTEM_INTERRUPT_GPIO_LEVEL_LOW 0x01 + #define VL53L0_REG_SYSTEM_INTERRUPT_GPIO_LEVEL_HIGH 0x02 + #define VL53L0_REG_SYSTEM_INTERRUPT_GPIO_OUT_OF_WINDOW 0x03 + #define VL53L0_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY 0x04 + +#define VL53L0_REG_GPIO_HV_MUX_ACTIVE_HIGH 0x0084 + + +#define VL53L0_REG_SYSTEM_INTERRUPT_CLEAR 0x000B + +/* Result registers */ +#define VL53L0_REG_RESULT_INTERRUPT_STATUS 0x0013 +#define VL53L0_REG_RESULT_RANGE_STATUS 0x0014 + +#define VL53L0_REG_RESULT_CORE_PAGE 1 +#define VL53L0_REG_RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN 0x00BC +#define VL53L0_REG_RESULT_CORE_RANGING_TOTAL_EVENTS_RTN 0x00C0 +#define VL53L0_REG_RESULT_CORE_AMBIENT_WINDOW_EVENTS_REF 0x00D0 +#define VL53L0_REG_RESULT_CORE_RANGING_TOTAL_EVENTS_REF 0x00D4 +#define VL53L0_REG_RESULT_PEAK_SIGNAL_RATE_REF 0x00B6 + +/* Algo register */ + +#define VL53L0_REG_ALGO_PART_TO_PART_RANGE_OFFSET_MM 0x0028 + +#define VL53L0_REG_I2C_SLAVE_DEVICE_ADDRESS 0x008a + +/* Check Limit registers */ +#define VL53L0_REG_MSRC_CONFIG_CONTROL 0x0060 + +#define VL53L0_REG_PRE_RANGE_CONFIG_MIN_SNR 0X0027 +#define VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_LOW 0x0056 +#define VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_HIGH 0x0057 +#define VL53L0_REG_PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT 0x0064 + +#define VL53L0_REG_FINAL_RANGE_CONFIG_MIN_SNR 0X0067 +#define VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_LOW 0x0047 +#define VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_HIGH 0x0048 +#define VL53L0_REG_FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT 0x0044 + + +#define VL53L0_REG_PRE_RANGE_CONFIG_SIGMA_THRESH_HI 0X0061 +#define VL53L0_REG_PRE_RANGE_CONFIG_SIGMA_THRESH_LO 0X0062 + +/* PRE RANGE registers */ +#define VL53L0_REG_PRE_RANGE_CONFIG_VCSEL_PERIOD 0x0050 +#define VL53L0_REG_PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI 0x0051 +#define VL53L0_REG_PRE_RANGE_CONFIG_TIMEOUT_MACROP_LO 0x0052 + +#define VL53L0_REG_SYSTEM_HISTOGRAM_BIN 0x0081 +#define VL53L0_REG_HISTOGRAM_CONFIG_INITIAL_PHASE_SELECT 0x0033 +#define VL53L0_REG_HISTOGRAM_CONFIG_READOUT_CTRL 0x0055 + +#define VL53L0_REG_FINAL_RANGE_CONFIG_VCSEL_PERIOD 0x0070 +#define VL53L0_REG_FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI 0x0071 +#define VL53L0_REG_FINAL_RANGE_CONFIG_TIMEOUT_MACROP_LO 0x0072 +#define VL53L0_REG_CROSSTALK_COMPENSATION_PEAK_RATE_MCPS 0x0020 + +#define VL53L0_REG_MSRC_CONFIG_TIMEOUT_MACROP 0x0046 + + +#define VL53L0_REG_SOFT_RESET_GO2_SOFT_RESET_N 0x00bf +#define VL53L0_REG_IDENTIFICATION_MODEL_ID 0x00c0 +#define VL53L0_REG_IDENTIFICATION_REVISION_ID 0x00c2 + +#define VL53L0_REG_OSC_CALIBRATE_VAL 0x00f8 + + +#define VL53L0_SIGMA_ESTIMATE_MAX_VALUE 65535 +/* equivalent to a range sigma of 655.35mm */ + +#define VL53L0_REG_GLOBAL_CONFIG_VCSEL_WIDTH 0x032 +#define VL53L0_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_0 0x0B0 +#define VL53L0_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_1 0x0B1 +#define VL53L0_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_2 0x0B2 +#define VL53L0_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_3 0x0B3 +#define VL53L0_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_4 0x0B4 +#define VL53L0_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_5 0x0B5 + +#define VL53L0_REG_GLOBAL_CONFIG_REF_EN_START_SELECT 0xB6 +#define VL53L0_REG_DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD 0x4E /* 0x14E */ +#define VL53L0_REG_DYNAMIC_SPAD_REF_EN_START_OFFSET 0x4F /* 0x14F */ +#define VL53L0_REG_POWER_MANAGEMENT_GO1_POWER_FORCE 0x80 + +/* + * Speed of light in um per 1E-10 Seconds + */ + +#define VL53L0_SPEED_OF_LIGHT_IN_AIR 2997 + +#define VL53L0_REG_VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV 0x0089 + +#define VL53L0_REG_ALGO_PHASECAL_LIM 0x0030 /* 0x130 */ +#define VL53L0_REG_ALGO_PHASECAL_CONFIG_TIMEOUT 0x0030 + +/** @} VL53L0_DefineRegisters_group */ + +/** @} VL53L0_DevSpecDefines_group */ + + +#endif + +/* _VL53L0_DEVICE_H_ */ + + diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_i2c_platform.h b/drivers/input/misc/vl53L0/inc/vl53l0_i2c_platform.h new file mode 100644 index 000000000000..9b1bdd7f3c54 --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_i2c_platform.h @@ -0,0 +1,402 @@ +/* + * vl53l0_i2c_platform.h - Linux kernel modules for STM VL53L0 FlightSense TOF + * sensor + * + * Copyright (C) 2016 STMicroelectronics Imaging Division. + * + * 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. + */ + +/** + * @file VL53L0_i2c_platform.h + * @brief Function prototype definitions for EWOK Platform layer. + * + */ + + +#ifndef _VL53L0_I2C_PLATFORM_H_ +#define _VL53L0_I2C_PLATFORM_H_ + +#include "vl53l0_def.h" + + +/** Maximum buffer size to be used in i2c */ +#define VL53L0_MAX_I2C_XFER_SIZE 64 + +/** + * @brief Typedef defining .\n + * The developer should modify this to suit the platform being deployed. + * + */ + +/** + * @brief Typedef defining 8 bit unsigned char type.\n + * The developer should modify this to suit the platform being deployed. + * + */ + +#ifndef bool_t +typedef unsigned char bool_t; +#endif + + +#define I2C 0x01 +#define SPI 0x00 + +#define COMMS_BUFFER_SIZE 64 +/*MUST be the same size as the SV task buffer */ + +#define BYTES_PER_WORD 2 +#define BYTES_PER_DWORD 4 + +#define VL53L0_MAX_STRING_LENGTH_PLT 256 + +/** + * @brief Initialise platform comms. + * + * @param comms_type - selects between I2C and SPI + * @param comms_speed_khz - unsigned short containing the I2C speed in kHz + * + * @return status - status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_comms_initialise(uint8_t comms_type, + uint16_t comms_speed_khz); + +/** + * @brief Close platform comms. + * + * @return status - status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_comms_close(void); + +/** + * @brief Cycle Power to Device + * + * @return status - status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_cycle_power(void); + +int32_t VL53L0_set_page(VL53L0_DEV dev, uint8_t page_data); + +/** + * @brief Writes the supplied byte buffer to the device + * + * Wrapper for SystemVerilog Write Multi task + * + * @code + * + * Example: + * + * uint8_t *spad_enables; + * + * int status = VL53L0_write_multi(RET_SPAD_EN_0, spad_enables, 36); + * + * @endcode + * + * @param address - uint8_t device address value + * @param index - uint8_t register index value + * @param pdata - pointer to uint8_t buffer containing the data to be written + * @param count - number of bytes in the supplied byte buffer + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_write_multi(VL53L0_DEV dev, uint8_t index, uint8_t *pdata, + int32_t count); + + +/** + * @brief Reads the requested number of bytes from the device + * + * Wrapper for SystemVerilog Read Multi task + * + * @code + * + * Example: + * + * uint8_t buffer[COMMS_BUFFER_SIZE]; + * + * int status = status = VL53L0_read_multi(DEVICE_ID, buffer, 2) + * + * @endcode + * + * @param address - uint8_t device address value + * @param index - uint8_t register index value + * @param pdata - pointer to the uint8_t buffer to store read data + * @param count - number of uint8_t's to read + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_read_multi(VL53L0_DEV dev, uint8_t index, uint8_t *pdata, + int32_t count); + + +/** + * @brief Writes a single byte to the device + * + * Wrapper for SystemVerilog Write Byte task + * + * @code + * + * Example: + * + * uint8_t page_number = MAIN_SELECT_PAGE; + * + * int status = VL53L0_write_byte(PAGE_SELECT, page_number); + * + * @endcode + * + * @param address - uint8_t device address value + * @param index - uint8_t register index value + * @param data - uint8_t data value to write + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_write_byte(VL53L0_DEV dev, uint8_t index, uint8_t data); + + +/** + * @brief Writes a single word (16-bit unsigned) to the device + * + * Manages the big-endian nature of the device (first byte written is the + * MS byte). + * Uses SystemVerilog Write Multi task. + * + * @code + * + * Example: + * + * uint16_t nvm_ctrl_pulse_width = 0x0004; + * + * int status = VL53L0_write_word(NVM_CTRL__PULSE_WIDTH_MSB, + * nvm_ctrl_pulse_width); + * + * @endcode + * + * @param address - uint8_t device address value + * @param index - uint8_t register index value + * @param data - uin16_t data value write + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_write_word(VL53L0_DEV dev, uint8_t index, uint16_t data); + + +/** + * @brief Writes a single dword (32-bit unsigned) to the device + * + * Manages the big-endian nature of the device (first byte written is the + * MS byte). + * Uses SystemVerilog Write Multi task. + * + * @code + * + * Example: + * + * uint32_t nvm_data = 0x0004; + * + * int status = VL53L0_write_dword(NVM_CTRL__DATAIN_MMM, nvm_data); + * + * @endcode + * + * @param address - uint8_t device address value + * @param index - uint8_t register index value + * @param data - uint32_t data value to write + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_write_dword(VL53L0_DEV dev, uint8_t index, uint32_t data); + + + +/** + * @brief Reads a single byte from the device + * + * Uses SystemVerilog Read Byte task. + * + * @code + * + * Example: + * + * uint8_t device_status = 0; + * + * int status = VL53L0_read_byte(STATUS, &device_status); + * + * @endcode + * + * @param address - uint8_t device address value + * @param index - uint8_t register index value + * @param pdata - pointer to uint8_t data value + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_read_byte(VL53L0_DEV dev, uint8_t index, uint8_t *pdata); + + +/** + * @brief Reads a single word (16-bit unsigned) from the device + * + * Manages the big-endian nature of the device (first byte read is the MS byte). + * Uses SystemVerilog Read Multi task. + * + * @code + * + * Example: + * + * uint16_t timeout = 0; + * + * int status = VL53L0_read_word(TIMEOUT_OVERALL_PERIODS_MSB, &timeout); + * + * @endcode + * + * @param address - uint8_t device address value + * @param index - uint8_t register index value + * @param pdata - pointer to uint16_t data value + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_read_word(VL53L0_DEV dev, uint8_t index, uint16_t *pdata); + + +/** + * @brief Reads a single dword (32-bit unsigned) from the device + * + * Manages the big-endian nature of the device (first byte read is the MS byte). + * Uses SystemVerilog Read Multi task. + * + * @code + * + * Example: + * + * uint32_t range_1 = 0; + * + * int status = VL53L0_read_dword(RANGE_1_MMM, &range_1); + * + * @endcode + * + * @param address - uint8_t device address value + * @param index - uint8_t register index value + * @param pdata - pointer to uint32_t data value + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_read_dword(VL53L0_DEV dev, uint8_t index, uint32_t *pdata); + + +/** + * @brief Implements a programmable wait in us + * + * Wrapper for SystemVerilog Wait in micro seconds task + * + * @param wait_us - integer wait in micro seconds + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_platform_wait_us(int32_t wait_us); + + +/** + * @brief Implements a programmable wait in ms + * + * Wrapper for SystemVerilog Wait in milli seconds task + * + * @param wait_ms - integer wait in milli seconds + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_wait_ms(int32_t wait_ms); + + +/** + * @brief Set GPIO value + * + * @param level - input level - either 0 or 1 + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_set_gpio(uint8_t level); + + +/** + * @brief Get GPIO value + * + * @param plevel - uint8_t pointer to store GPIO level (0 or 1) + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_get_gpio(uint8_t *plevel); + +/** + * @brief Release force on GPIO + * + * @return status - SystemVerilog status 0 = ok, 1 = error + * + */ + +int32_t VL53L0_release_gpio(void); + + +/** +* @brief Get the frequency of the timer used for ranging results time stamps +* +* @param[out] ptimer_freq_hz : pointer for timer frequency +* +* @return status : 0 = ok, 1 = error +* +*/ + +int32_t VL53L0_get_timer_frequency(int32_t *ptimer_freq_hz); + +/** +* @brief Get the timer value in units of timer_freq_hz +* (see VL53L0_get_timestamp_frequency()) +* +* @param[out] ptimer_count : pointer for timer count value +* +* @return status : 0 = ok, 1 = error +* +*/ + +int32_t VL53L0_get_timer_value(int32_t *ptimer_count); +int VL53L0_I2CWrite(VL53L0_DEV dev, uint8_t *buff, uint8_t len); +int VL53L0_I2CRead(VL53L0_DEV dev, uint8_t *buff, uint8_t len); + +#endif /* _VL53L0_I2C_PLATFORM_H_ */ + diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_interrupt_threshold_settings.h b/drivers/input/misc/vl53L0/inc/vl53l0_interrupt_threshold_settings.h new file mode 100644 index 000000000000..35acd8eb42e3 --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_interrupt_threshold_settings.h @@ -0,0 +1,194 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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. +*******************************************************************************/ + + +#ifndef _VL53L0_INTERRUPT_THRESHOLD_SETTINGS_H_ +#define _VL53L0_INTERRUPT_THRESHOLD_SETTINGS_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + + +uint8_t InterruptThresholdSettings[] = { + + /* Start of Interrupt Threshold Settings */ + 0x1, 0xff, 0x00, + 0x1, 0x80, 0x01, + 0x1, 0xff, 0x01, + 0x1, 0x00, 0x00, + 0x1, 0xff, 0x01, + 0x1, 0x4f, 0x02, + 0x1, 0xFF, 0x0E, + 0x1, 0x00, 0x03, + 0x1, 0x01, 0x84, + 0x1, 0x02, 0x0A, + 0x1, 0x03, 0x03, + 0x1, 0x04, 0x08, + 0x1, 0x05, 0xC8, + 0x1, 0x06, 0x03, + 0x1, 0x07, 0x8D, + 0x1, 0x08, 0x08, + 0x1, 0x09, 0xC6, + 0x1, 0x0A, 0x01, + 0x1, 0x0B, 0x02, + 0x1, 0x0C, 0x00, + 0x1, 0x0D, 0xD5, + 0x1, 0x0E, 0x18, + 0x1, 0x0F, 0x12, + 0x1, 0x10, 0x01, + 0x1, 0x11, 0x82, + 0x1, 0x12, 0x00, + 0x1, 0x13, 0xD5, + 0x1, 0x14, 0x18, + 0x1, 0x15, 0x13, + 0x1, 0x16, 0x03, + 0x1, 0x17, 0x86, + 0x1, 0x18, 0x0A, + 0x1, 0x19, 0x09, + 0x1, 0x1A, 0x08, + 0x1, 0x1B, 0xC2, + 0x1, 0x1C, 0x03, + 0x1, 0x1D, 0x8F, + 0x1, 0x1E, 0x0A, + 0x1, 0x1F, 0x06, + 0x1, 0x20, 0x01, + 0x1, 0x21, 0x02, + 0x1, 0x22, 0x00, + 0x1, 0x23, 0xD5, + 0x1, 0x24, 0x18, + 0x1, 0x25, 0x22, + 0x1, 0x26, 0x01, + 0x1, 0x27, 0x82, + 0x1, 0x28, 0x00, + 0x1, 0x29, 0xD5, + 0x1, 0x2A, 0x18, + 0x1, 0x2B, 0x0B, + 0x1, 0x2C, 0x28, + 0x1, 0x2D, 0x78, + 0x1, 0x2E, 0x28, + 0x1, 0x2F, 0x91, + 0x1, 0x30, 0x00, + 0x1, 0x31, 0x0B, + 0x1, 0x32, 0x00, + 0x1, 0x33, 0x0B, + 0x1, 0x34, 0x00, + 0x1, 0x35, 0xA1, + 0x1, 0x36, 0x00, + 0x1, 0x37, 0xA0, + 0x1, 0x38, 0x00, + 0x1, 0x39, 0x04, + 0x1, 0x3A, 0x28, + 0x1, 0x3B, 0x30, + 0x1, 0x3C, 0x0C, + 0x1, 0x3D, 0x04, + 0x1, 0x3E, 0x0F, + 0x1, 0x3F, 0x79, + 0x1, 0x40, 0x28, + 0x1, 0x41, 0x1E, + 0x1, 0x42, 0x2F, + 0x1, 0x43, 0x87, + 0x1, 0x44, 0x00, + 0x1, 0x45, 0x0B, + 0x1, 0x46, 0x00, + 0x1, 0x47, 0x0B, + 0x1, 0x48, 0x00, + 0x1, 0x49, 0xA7, + 0x1, 0x4A, 0x00, + 0x1, 0x4B, 0xA6, + 0x1, 0x4C, 0x00, + 0x1, 0x4D, 0x04, + 0x1, 0x4E, 0x01, + 0x1, 0x4F, 0x00, + 0x1, 0x50, 0x00, + 0x1, 0x51, 0x80, + 0x1, 0x52, 0x09, + 0x1, 0x53, 0x08, + 0x1, 0x54, 0x01, + 0x1, 0x55, 0x00, + 0x1, 0x56, 0x0F, + 0x1, 0x57, 0x79, + 0x1, 0x58, 0x09, + 0x1, 0x59, 0x05, + 0x1, 0x5A, 0x00, + 0x1, 0x5B, 0x60, + 0x1, 0x5C, 0x05, + 0x1, 0x5D, 0xD1, + 0x1, 0x5E, 0x0C, + 0x1, 0x5F, 0x3C, + 0x1, 0x60, 0x00, + 0x1, 0x61, 0xD0, + 0x1, 0x62, 0x0B, + 0x1, 0x63, 0x03, + 0x1, 0x64, 0x28, + 0x1, 0x65, 0x10, + 0x1, 0x66, 0x2A, + 0x1, 0x67, 0x39, + 0x1, 0x68, 0x0B, + 0x1, 0x69, 0x02, + 0x1, 0x6A, 0x28, + 0x1, 0x6B, 0x10, + 0x1, 0x6C, 0x2A, + 0x1, 0x6D, 0x61, + 0x1, 0x6E, 0x0C, + 0x1, 0x6F, 0x00, + 0x1, 0x70, 0x0F, + 0x1, 0x71, 0x79, + 0x1, 0x72, 0x00, + 0x1, 0x73, 0x0B, + 0x1, 0x74, 0x00, + 0x1, 0x75, 0x0B, + 0x1, 0x76, 0x00, + 0x1, 0x77, 0xA1, + 0x1, 0x78, 0x00, + 0x1, 0x79, 0xA0, + 0x1, 0x7A, 0x00, + 0x1, 0x7B, 0x04, + 0x1, 0xFF, 0x04, + 0x1, 0x79, 0x1D, + 0x1, 0x7B, 0x27, + 0x1, 0x96, 0x0E, + 0x1, 0x97, 0xFE, + 0x1, 0x98, 0x03, + 0x1, 0x99, 0xEF, + 0x1, 0x9A, 0x02, + 0x1, 0x9B, 0x44, + 0x1, 0x73, 0x07, + 0x1, 0x70, 0x01, + 0x1, 0xff, 0x01, + 0x1, 0x00, 0x01, + 0x1, 0xff, 0x00, + 0x00, 0x00, 0x00 +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L0_INTERRUPT_THRESHOLD_SETTINGS_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_platform.h b/drivers/input/misc/vl53L0/inc/vl53l0_platform.h new file mode 100644 index 000000000000..80724f99c3b2 --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_platform.h @@ -0,0 +1,230 @@ +/******************************************************************************* + * Copyright © 2015, STMicroelectronics International N.V. +All rights reserved. + +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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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. +*******************************************************************************/ + + +#ifndef _VL53L0_PLATFORM_H_ +#define _VL53L0_PLATFORM_H_ + +#include +#include "vl53l0_def.h" +#include "vl53l0_platform_log.h" + +#include "stmvl53l0-i2c.h" +#include "stmvl53l0.h" + +/** + * @file vl53l0_platform.h + * + * @brief All end user OS/platform/application porting + */ + +/** + * @defgroup VL53L0_platform_group VL53L0 Platform Functions + * @brief VL53L0 Platform Functions + * @{ + */ + +/** + * @struct VL53L0_Dev_t + * @brief Generic PAL device type that does link between API and platform + * abstraction layer + * + */ +typedef struct stmvl53l0_data *VL53L0_DEV; + +/** + * @def PALDevDataGet + * @brief Get ST private structure @a VL53L0_DevData_t data access + * + * @param Dev Device Handle + * @param field ST structure field name + * It maybe used and as real data "ref" not just as "get" for sub-structure item + * like PALDevDataGet(FilterData.field)[i] or + * PALDevDataGet(FilterData.MeasurementIndex)++ + */ +#define PALDevDataGet(Dev, field) (Dev->Data.field) + +/** + * @def PALDevDataSet(Dev, field, data) + * @brief Set ST private structure @a VL53L0_DevData_t data field + * @param Dev Device Handle + * @param field ST structure field na*me + * @param data Data to be set + */ +#define PALDevDataSet(Dev, field, data) ((Dev->Data.field) = (data)) + + +/** + * @defgroup VL53L0_registerAccess_group PAL Register Access Functions + * @brief PAL Register Access Functions + * @{ + */ + +/** + * Lock comms interface to serialize all commands to a shared I2C interface + * for a specific device + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_LockSequenceAccess(VL53L0_DEV Dev); + +/** + * Unlock comms interface to serialize all commands to a shared I2C interface + * for a specific device + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_UnlockSequenceAccess(VL53L0_DEV Dev); + + +/** + * Writes the supplied byte buffer to the device + * @param Dev Device Handle + * @param index The register index + * @param pdata Pointer to uint8_t buffer containing the data to be written + * @param count Number of bytes in the supplied byte buffer + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_WriteMulti(VL53L0_DEV Dev, uint8_t index, + uint8_t *pdata, uint32_t count); + +/** + * Reads the requested number of bytes from the device + * @param Dev Device Handle + * @param index The register index + * @param pdata Pointer to the uint8_t buffer to store read data + * @param count Number of uint8_t's to read + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_ReadMulti(VL53L0_DEV Dev, uint8_t index, + uint8_t *pdata, uint32_t count); + +/** + * Write single byte register + * @param Dev Device Handle + * @param index The register index + * @param data 8 bit register data + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_WrByte(VL53L0_DEV Dev, uint8_t index, uint8_t data); + +/** + * Write word register + * @param Dev Device Handle + * @param index The register index + * @param data 16 bit register data + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_WrWord(VL53L0_DEV Dev, uint8_t index, uint16_t data); + +/** + * Write double word (4 byte) register + * @param Dev Device Handle + * @param index The register index + * @param data 32 bit register data + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_WrDWord(VL53L0_DEV Dev, uint8_t index, uint32_t data); + +/** + * Read single byte register + * @param Dev Device Handle + * @param index The register index + * @param data pointer to 8 bit data + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_RdByte(VL53L0_DEV Dev, uint8_t index, uint8_t *data); + +/** + * Read word (2byte) register + * @param Dev Device Handle + * @param index The register index + * @param data pointer to 16 bit data + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_RdWord(VL53L0_DEV Dev, uint8_t index, uint16_t *data); + +/** + * Read dword (4byte) register + * @param Dev Device Handle + * @param index The register index + * @param data pointer to 32 bit data + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_RdDWord(VL53L0_DEV Dev, uint8_t index, uint32_t *data); + +/** + * Threat safe Update (read/modify/write) single byte register + * + * Final_reg = (Initial_reg & and_data) |or_data + * + * @param Dev Device Handle + * @param index The register index + * @param AndData 8 bit and data + * @param OrData 8 bit or data + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_UpdateByte(VL53L0_DEV Dev, uint8_t index, + uint8_t AndData, uint8_t OrData); + +/** @} end of VL53L0_registerAccess_group */ + + +/** + * @brief execute delay in all polling API call + * + * A typical multi-thread or RTOs implementation is to sleep the task for + * some 5ms (with 100Hz max rate faster polling is not needed) + * if nothing specific is need you can define it as an empty/void macro + * @code + * #define VL53L0_PollingDelay(...) (void)0 + * @endcode + * @param Dev Device Handle + * @return VL53L0_ERROR_NONE Success + * @return "Other error code" See ::VL53L0_Error + */ +VL53L0_Error VL53L0_PollingDelay(VL53L0_DEV Dev); +/* usually best implemented as a real function */ + +/** @} end of VL53L0_platform_group */ + +#endif /* _VL53L0_PLATFORM_H_ */ + + + diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_platform_log.h b/drivers/input/misc/vl53L0/inc/vl53l0_platform_log.h new file mode 100644 index 000000000000..8c38615239ad --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_platform_log.h @@ -0,0 +1,128 @@ +/******************************************************************************* + * Copyright © 2015, STMicroelectronics International N.V. +All rights reserved. + +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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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. +*******************************************************************************/ + + +#ifndef _VL53L0_PLATFORM_LOG_H_ +#define _VL53L0_PLATFORM_LOG_H_ + +#include +/* LOG Functions */ + + +/** + * @file vl53l0_platform_log.h + * + * @brief platform log function definition + */ + +/* #define VL53L0_LOG_ENABLE */ + +enum { + TRACE_LEVEL_NONE, + TRACE_LEVEL_ERRORS, + TRACE_LEVEL_WARNING, + TRACE_LEVEL_INFO, + TRACE_LEVEL_DEBUG, + TRACE_LEVEL_ALL, + TRACE_LEVEL_IGNORE +}; + +enum { + TRACE_FUNCTION_NONE = 0, + TRACE_FUNCTION_I2C = 1, + TRACE_FUNCTION_ALL = 0x7fffffff /* all bits except sign */ +}; + +enum { + TRACE_MODULE_NONE = 0x0, + TRACE_MODULE_API = 0x1, + TRACE_MODULE_PLATFORM = 0x2, + TRACE_MODULE_ALL = 0x7fffffff /* all bits except sign */ +}; + + +#ifdef VL53L0_LOG_ENABLE + +#include + + +extern uint32_t _trace_level; + + + +int32_t VL53L0_trace_config(char *filename, uint32_t modules, + uint32_t level, uint32_t functions); + +#if 0 +void trace_print_module_function(uint32_t module, uint32_t level, + uint32_t function, const char *format, ...); +#else +#define trace_print_module_function(...) +#endif + +#define LOG_GET_TIME() (int)0 +/* + * #define _LOG_FUNCTION_START(module, fmt, ...) \ + printk(KERN_INFO"beg %s start @%d\t" fmt "\n", \ + __func__, LOG_GET_TIME(), ##__VA_ARGS__) + + * #define _LOG_FUNCTION_END(module, status, ...)\ + printk(KERN_INFO"end %s @%d %d\n", \ + __func__, LOG_GET_TIME(), (int)status) + + * #define _LOG_FUNCTION_END_FMT(module, status, fmt, ...)\ + printk(KERN_INFO"End %s @%d %d\t"fmt"\n" , \ + __func__, LOG_GET_TIME(), (int)status, ##__VA_ARGS__) +*/ +#define _LOG_FUNCTION_START(module, fmt, ...) \ + pr_err("beg %s start @%d\t" fmt "\n", \ + __func__, LOG_GET_TIME(), ##__VA_ARGS__) + +#define _LOG_FUNCTION_END(module, status, ...)\ + pr_err("end %s start @%d Status %d\n", \ + __func__, LOG_GET_TIME(), (int)status) + +#define _LOG_FUNCTION_END_FMT(module, status, fmt, ...)\ + pr_err("End %s @%d %d\t"fmt"\n", \ + __func__, LOG_GET_TIME(), (int)status, ##__VA_ARGS__) + + +#else /* VL53L0_LOG_ENABLE no logging */ + #define VL53L0_ErrLog(...) (void)0 + #define _LOG_FUNCTION_START(module, fmt, ...) (void)0 + #define _LOG_FUNCTION_END(module, status, ...) (void)0 + #define _LOG_FUNCTION_END_FMT(module, status, fmt, ...) (void)0 +#endif /* else */ + +#define VL53L0_COPYSTRING(str, ...) strlcpy(str, ##__VA_ARGS__, sizeof(str)) + + +#endif /* _VL53L0_PLATFORM_LOG_H_ */ + + + diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_tuning.h b/drivers/input/misc/vl53L0/inc/vl53l0_tuning.h new file mode 100644 index 000000000000..a9f7ca70b5ac --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_tuning.h @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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. +*******************************************************************************/ + + +#ifndef _VL53L0_TUNING_H_ +#define _VL53L0_TUNING_H_ + +#include "vl53l0_def.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + +uint8_t DefaultTuningSettings[] = { + + /* update 02/11/2015_v36 */ + 0x01, 0xFF, 0x01, + 0x01, 0x00, 0x00, + + 0x01, 0xFF, 0x00, + 0x01, 0x09, 0x00, + 0x01, 0x10, 0x00, + 0x01, 0x11, 0x00, + + 0x01, 0x24, 0x01, + 0x01, 0x25, 0xff, + 0x01, 0x75, 0x00, + + 0x01, 0xFF, 0x01, + 0x01, 0x4e, 0x2c, + 0x01, 0x48, 0x00, + 0x01, 0x30, 0x20, + + 0x01, 0xFF, 0x00, + 0x01, 0x30, 0x09, /* mja changed from 0x64. */ + 0x01, 0x54, 0x00, + 0x01, 0x31, 0x04, + 0x01, 0x32, 0x03, + 0x01, 0x40, 0x83, + 0x01, 0x46, 0x25, + 0x01, 0x60, 0x00, + 0x01, 0x27, 0x00, + 0x01, 0x50, 0x06, + 0x01, 0x51, 0x00, + 0x01, 0x52, 0x96, + 0x01, 0x56, 0x08, + 0x01, 0x57, 0x30, + 0x01, 0x61, 0x00, + 0x01, 0x62, 0x00, + 0x01, 0x64, 0x00, + 0x01, 0x65, 0x00, + 0x01, 0x66, 0xa0, + + 0x01, 0xFF, 0x01, + 0x01, 0x22, 0x32, + 0x01, 0x47, 0x14, + 0x01, 0x49, 0xff, + 0x01, 0x4a, 0x00, + + 0x01, 0xFF, 0x00, + 0x01, 0x7a, 0x0a, + 0x01, 0x7b, 0x00, + 0x01, 0x78, 0x21, + + 0x01, 0xFF, 0x01, + 0x01, 0x23, 0x34, + 0x01, 0x42, 0x00, + 0x01, 0x44, 0xff, + 0x01, 0x45, 0x26, + 0x01, 0x46, 0x05, + 0x01, 0x40, 0x40, + 0x01, 0x0E, 0x06, + 0x01, 0x20, 0x1a, + 0x01, 0x43, 0x40, + + 0x01, 0xFF, 0x00, + 0x01, 0x34, 0x03, + 0x01, 0x35, 0x44, + + 0x01, 0xFF, 0x01, + 0x01, 0x31, 0x04, + 0x01, 0x4b, 0x09, + 0x01, 0x4c, 0x05, + 0x01, 0x4d, 0x04, + + + 0x01, 0xFF, 0x00, + 0x01, 0x44, 0x00, + 0x01, 0x45, 0x20, + 0x01, 0x47, 0x08, + 0x01, 0x48, 0x28, + 0x01, 0x67, 0x00, + 0x01, 0x70, 0x04, + 0x01, 0x71, 0x01, + 0x01, 0x72, 0xfe, + 0x01, 0x76, 0x00, + 0x01, 0x77, 0x00, + + 0x01, 0xFF, 0x01, + 0x01, 0x0d, 0x01, + + 0x01, 0xFF, 0x00, + 0x01, 0x80, 0x01, + 0x01, 0x01, 0xF8, + + 0x01, 0xFF, 0x01, + 0x01, 0x8e, 0x01, + 0x01, 0x00, 0x01, + 0x01, 0xFF, 0x00, + 0x01, 0x80, 0x00, + + 0x00, 0x00, 0x00 +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L0_TUNING_H_ */ diff --git a/drivers/input/misc/vl53L0/inc/vl53l0_types.h b/drivers/input/misc/vl53L0/inc/vl53l0_types.h new file mode 100644 index 000000000000..c509913146ed --- /dev/null +++ b/drivers/input/misc/vl53L0/inc/vl53l0_types.h @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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. +*******************************************************************************/ + +#ifndef VL53L0_TYPES_H_ +#define VL53L0_TYPES_H_ + +#include + +#ifndef NULL +#error "TODO review NULL definition or add required include " +#define NULL 0 +#endif +/** use where fractional values are expected + * + * Given a floating point value f it's .16 bit point is (int)(f*(1<<16)) + */ +typedef unsigned int FixPoint1616_t; + +#if !defined(STDINT_H) && !defined(_GCC_STDINT_H) \ + && !defined(_STDINT_H) && !defined(_LINUX_TYPES_H) + +#pragma message("Please review type definition of STDINT define for your" \ +"platform and add to list above ") + +/* +* target platform do not provide stdint or use a different #define than above +* to avoid seeing the message below addapt the #define list above or implement +* all type and delete these pragma +*/ + +typedef unsigned int uint32_t; +typedef int int32_t; + +typedef unsigned short uint16_t; +typedef short int16_t; + +typedef unsigned char uint8_t; + +typedef signed char int8_t; + + +#endif /* VL53L0_TYPES_H_ */ + +#endif /* VL6180x_TYPES_H_ */ diff --git a/drivers/input/misc/vl53L0/src/vl53l0_api.c b/drivers/input/misc/vl53L0/src/vl53l0_api.c new file mode 100644 index 000000000000..7b9167b17f2e --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l0_api.c @@ -0,0 +1,3132 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. + All rights reserved. + + 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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND + NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. + IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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 "vl53l0_api.h" +#include "vl53l0_tuning.h" +#include "vl53l0_interrupt_threshold_settings.h" +#include "vl53l0_api_core.h" +#include "vl53l0_api_histogram.h" +#include "vl53l0_api_calibration.h" +#include "vl53l0_api_strings.h" + +#ifndef __KERNEL__ +#include +#endif +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__) + +#ifdef VL53L0_LOG_ENABLE +#define trace_print(level, ...) trace_print_module_function(TRACE_MODULE_API, \ + level, TRACE_FUNCTION_NONE, ##__VA_ARGS__) +#endif + +/* Group PAL General Functions */ + +VL53L0_Error VL53L0_GetVersion(VL53L0_Version_t *pVersion) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + pVersion->major = VL53L0_IMPLEMENTATION_VER_MAJOR; + pVersion->minor = VL53L0_IMPLEMENTATION_VER_MINOR; + pVersion->build = VL53L0_IMPLEMENTATION_VER_SUB; + + pVersion->revision = VL53L0_IMPLEMENTATION_VER_REVISION; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetPalSpecVersion(VL53L0_Version_t *pPalSpecVersion) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + pPalSpecVersion->major = VL53L0_SPECIFICATION_VER_MAJOR; + pPalSpecVersion->minor = VL53L0_SPECIFICATION_VER_MINOR; + pPalSpecVersion->build = VL53L0_SPECIFICATION_VER_SUB; + + pPalSpecVersion->revision = VL53L0_SPECIFICATION_VER_REVISION; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetProductRevision(VL53L0_DEV Dev, + uint8_t *pProductRevisionMajor, uint8_t *pProductRevisionMinor) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t revision_id; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L0_REG_IDENTIFICATION_REVISION_ID, + &revision_id); + *pProductRevisionMajor = 1; + *pProductRevisionMinor = (revision_id & 0xF0) >> 4; + + LOG_FUNCTION_END(Status); + return Status; + +} + +VL53L0_Error VL53L0_GetDeviceInfo(VL53L0_DEV Dev, + VL53L0_DeviceInfo_t *pVL53L0_DeviceInfo) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_device_info(Dev, pVL53L0_DeviceInfo); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetDeviceErrorStatus(VL53L0_DEV Dev, + VL53L0_DeviceError *pDeviceErrorStatus) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t RangeStatus; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L0_REG_RESULT_RANGE_STATUS, + &RangeStatus); + + *pDeviceErrorStatus = (VL53L0_DeviceError)((RangeStatus & 0x78) >> 3); + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L0_Error VL53L0_GetDeviceErrorString(VL53L0_DeviceError ErrorCode, + char *pDeviceErrorString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_device_error_string(ErrorCode, pDeviceErrorString); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetRangeStatusString(uint8_t RangeStatus, + char *pRangeStatusString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_range_status_string(RangeStatus, + pRangeStatusString); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetPalErrorString(VL53L0_Error PalErrorCode, + char *pPalErrorString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_pal_error_string(PalErrorCode, pPalErrorString); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetPalStateString(VL53L0_State PalStateCode, + char *pPalStateString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_pal_state_string(PalStateCode, pPalStateString); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetPalState(VL53L0_DEV Dev, VL53L0_State *pPalState) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pPalState = PALDevDataGet(Dev, PalState); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetPowerMode(VL53L0_DEV Dev, VL53L0_PowerModes PowerMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + /* Only level1 of Power mode exists */ + if ((PowerMode != VL53L0_POWERMODE_STANDBY_LEVEL1) + && (PowerMode != VL53L0_POWERMODE_IDLE_LEVEL1)) { + Status = VL53L0_ERROR_MODE_NOT_SUPPORTED; + } else if (PowerMode == VL53L0_POWERMODE_STANDBY_LEVEL1) { + /* set the standby level1 of power mode */ + Status = VL53L0_WrByte(Dev, 0x80, 0x00); + if (Status == VL53L0_ERROR_NONE) { + /* Set PAL State to standby */ + PALDevDataSet(Dev, PalState, VL53L0_STATE_STANDBY); + PALDevDataSet(Dev, PowerMode, + VL53L0_POWERMODE_STANDBY_LEVEL1); + } + + } else { + /* VL53L0_POWERMODE_IDLE_LEVEL1 */ + Status = VL53L0_WrByte(Dev, 0x80, 0x00); + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_StaticInit(Dev); + + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, PowerMode, + VL53L0_POWERMODE_IDLE_LEVEL1); + + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetPowerMode(VL53L0_DEV Dev, VL53L0_PowerModes *pPowerMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + /* Only level1 of Power mode exists */ + Status = VL53L0_RdByte(Dev, 0x80, &Byte); + + if (Status == VL53L0_ERROR_NONE) { + if (Byte == 1) { + PALDevDataSet(Dev, PowerMode, + VL53L0_POWERMODE_IDLE_LEVEL1); + } else { + PALDevDataSet(Dev, PowerMode, + VL53L0_POWERMODE_STANDBY_LEVEL1); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetOffsetCalibrationDataMicroMeter(VL53L0_DEV Dev, + int32_t OffsetCalibrationDataMicroMeter) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_set_offset_calibration_data_micro_meter(Dev, + OffsetCalibrationDataMicroMeter); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetOffsetCalibrationDataMicroMeter(VL53L0_DEV Dev, + int32_t *pOffsetCalibrationDataMicroMeter) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_offset_calibration_data_micro_meter(Dev, + pOffsetCalibrationDataMicroMeter); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetLinearityCorrectiveGain(VL53L0_DEV Dev, + int16_t LinearityCorrectiveGain) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if ((LinearityCorrectiveGain < 0) || (LinearityCorrectiveGain > 1000)) + Status = VL53L0_ERROR_INVALID_PARAMS; + else { + PALDevDataSet(Dev, LinearityCorrectiveGain, + LinearityCorrectiveGain); + + if (LinearityCorrectiveGain != 1000) { + /* Disable FW Xtalk */ + Status = VL53L0_WrWord(Dev, + VL53L0_REG_CROSSTALK_COMPENSATION_PEAK_RATE_MCPS, 0); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetLinearityCorrectiveGain(VL53L0_DEV Dev, + uint16_t *pLinearityCorrectiveGain) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pLinearityCorrectiveGain = PALDevDataGet(Dev, LinearityCorrectiveGain); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetGroupParamHold(VL53L0_DEV Dev, uint8_t GroupParamHold) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + /* not implemented on VL53L0 */ + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetUpperLimitMilliMeter(VL53L0_DEV Dev, + uint16_t *pUpperLimitMilliMeter) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + /* not implemented on VL53L0 */ + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetTotalSignalRate(VL53L0_DEV Dev, + FixPoint1616_t *pTotalSignalRate) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_RangingMeasurementData_t LastRangeDataBuffer; + + LOG_FUNCTION_START(""); + + LastRangeDataBuffer = PALDevDataGet(Dev, LastRangeMeasure); + + Status = VL53L0_get_total_signal_rate( + Dev, &LastRangeDataBuffer, pTotalSignalRate); + + LOG_FUNCTION_END(Status); + return Status; +} + +/* End Group PAL General Functions */ + +/* Group PAL Init Functions */ +VL53L0_Error VL53L0_SetDeviceAddress(VL53L0_DEV Dev, uint8_t DeviceAddress) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_WrByte(Dev, VL53L0_REG_I2C_SLAVE_DEVICE_ADDRESS, + DeviceAddress / 2); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_DataInit(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + int i; + uint8_t StopVariable; + + LOG_FUNCTION_START(""); + + /* by default the I2C is running at 1V8 if you want to change it you + * need to include this define at compilation level. + */ +#ifdef USE_I2C_2V8 + Status = VL53L0_UpdateByte(Dev, + VL53L0_REG_VHV_CONFIG_PAD_SCL_SDA__EXTSUP_HV, + 0xFE, + 0x01); +#endif + + /* Set I2C standard mode */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, 0x88, 0x00); + + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, ReadDataFromDeviceDone, 0); + +#ifdef USE_IQC_STATION + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_apply_offset_adjustment(Dev); +#endif + + /* Default value is 1000 for Linearity Corrective Gain */ + PALDevDataSet(Dev, LinearityCorrectiveGain, 1000); + + /* Set Default static parameters + *set first temporary values 9.44MHz * 65536 = 618660 + */ + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, OscFrequencyMHz, 618660); + + /* Set Default XTalkCompensationRateMegaCps to 0 */ + VL53L0_SETPARAMETERFIELD(Dev, XTalkCompensationRateMegaCps, 0); + + /* Get default parameters */ + Status = VL53L0_GetDeviceParameters(Dev, &CurrentParameters); + if (Status == VL53L0_ERROR_NONE) { + /* initialize PAL values */ + CurrentParameters.DeviceMode = VL53L0_DEVICEMODE_SINGLE_RANGING; + CurrentParameters.HistogramMode = VL53L0_HISTOGRAMMODE_DISABLED; + + /* Dmax lookup table */ + /* 0.0 */ + CurrentParameters.dmax_lut.ambRate_mcps[0] = (FixPoint1616_t)0x00000000; + /* 1200 */ + CurrentParameters.dmax_lut.dmax_mm[0] = (FixPoint1616_t)0x04B00000; + /* 0.7 */ + CurrentParameters.dmax_lut.ambRate_mcps[1] = (FixPoint1616_t)0x0000B333; + /* 1100 */ + CurrentParameters.dmax_lut.dmax_mm[1] = (FixPoint1616_t)0x044C0000; + /* 2 */ + CurrentParameters.dmax_lut.ambRate_mcps[2] = (FixPoint1616_t)0x00020000; + /* 900 */ + CurrentParameters.dmax_lut.dmax_mm[2] = (FixPoint1616_t)0x03840000; + /* 3.8 */ + CurrentParameters.dmax_lut.ambRate_mcps[3] = (FixPoint1616_t)0x0003CCCC; + /* 750 */ + CurrentParameters.dmax_lut.dmax_mm[3] = (FixPoint1616_t)0x02EE0000; + /* 7.3 */ + CurrentParameters.dmax_lut.ambRate_mcps[4] = (FixPoint1616_t)0x00074CCC; + /* 550 */ + CurrentParameters.dmax_lut.dmax_mm[4] = (FixPoint1616_t)0x02260000; + /* 10 */ + CurrentParameters.dmax_lut.ambRate_mcps[5] = (FixPoint1616_t)0x000A0000; + /* 500 */ + CurrentParameters.dmax_lut.dmax_mm[5] = (FixPoint1616_t)0x01F40000; + /* 15 */ + CurrentParameters.dmax_lut.ambRate_mcps[6] = (FixPoint1616_t)0x000F0000; + /* 400 */ + CurrentParameters.dmax_lut.dmax_mm[6] = (FixPoint1616_t)0x01900000; + + PALDevDataSet(Dev, CurrentParameters, CurrentParameters); + } + + /* Sigma estimator variable */ + PALDevDataSet(Dev, SigmaEstRefArray, 100); + PALDevDataSet(Dev, SigmaEstEffPulseWidth, 900); + PALDevDataSet(Dev, SigmaEstEffAmbWidth, 500); + PALDevDataSet(Dev, targetRefRate, 0x0A00); /* 20 MCPS in 9:7 format */ + + /* Use internal default settings */ + PALDevDataSet(Dev, UseInternalTuningSettings, 1); + + Status |= VL53L0_WrByte(Dev, 0x80, 0x01); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x00); + Status |= VL53L0_RdByte(Dev, 0x91, &StopVariable); + PALDevDataSet(Dev, StopVariable, StopVariable); + Status |= VL53L0_WrByte(Dev, 0x00, 0x01); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + Status |= VL53L0_WrByte(Dev, 0x80, 0x00); + + /* Enable all check */ + for (i = 0; i < VL53L0_CHECKENABLE_NUMBER_OF_CHECKS; i++) { + if (Status == VL53L0_ERROR_NONE) + Status |= VL53L0_SetLimitCheckEnable(Dev, i, 1); + else + break; + + } + + /* Disable the following checks */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, 0); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, 0); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_SIGNAL_RATE_MSRC, 0); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_SIGNAL_RATE_PRE_RANGE, 0); + + /* Limit default values */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_SetLimitCheckValue(Dev, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + (FixPoint1616_t)(18 * 65536)); + } + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_SetLimitCheckValue(Dev, + VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + (FixPoint1616_t)(25 * 65536 / 100)); + /* 0.25 * 65536 */ + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_SetLimitCheckValue(Dev, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, + (FixPoint1616_t)(35 * 65536)); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_SetLimitCheckValue(Dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + (FixPoint1616_t)(0 * 65536)); + } + + if (Status == VL53L0_ERROR_NONE) { + + PALDevDataSet(Dev, SequenceConfig, 0xFF); + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, + 0xFF); + + /* Set PAL state to tell that we are waiting for call to + * VL53L0_StaticInit + */ + PALDevDataSet(Dev, PalState, VL53L0_STATE_WAIT_STATICINIT); + } + + if (Status == VL53L0_ERROR_NONE) + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, RefSpadsInitialised, 0); + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetTuningSettingBuffer(VL53L0_DEV Dev, + uint8_t *pTuningSettingBuffer, uint8_t UseInternalTuningSettings) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (UseInternalTuningSettings == 1) { + /* Force use internal settings */ + PALDevDataSet(Dev, UseInternalTuningSettings, 1); + } else { + + /* check that the first byte is not 0 */ + if (*pTuningSettingBuffer != 0) { + PALDevDataSet(Dev, pTuningSettingsPointer, + pTuningSettingBuffer); + PALDevDataSet(Dev, UseInternalTuningSettings, 0); + + } else { + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetTuningSettingBuffer(VL53L0_DEV Dev, + uint8_t **ppTuningSettingBuffer, uint8_t *pUseInternalTuningSettings) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *ppTuningSettingBuffer = PALDevDataGet(Dev, pTuningSettingsPointer); + *pUseInternalTuningSettings = PALDevDataGet(Dev, + UseInternalTuningSettings); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_StaticInit(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters = {0}; + uint8_t *pTuningSettingBuffer; + uint16_t tempword = 0; + uint8_t tempbyte = 0; + uint8_t UseInternalTuningSettings = 0; + uint32_t count = 0; + uint8_t isApertureSpads = 0; + uint32_t refSpadCount = 0; + uint8_t ApertureSpads = 0; + uint8_t vcselPulsePeriodPCLK; + uint32_t seqTimeoutMicroSecs; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_info_from_device(Dev, 1); + + /* set the ref spad from NVM */ + count = (uint32_t)VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadCount); + ApertureSpads = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadType); + + /* NVM value invalid */ + if ((ApertureSpads > 1) || + ((ApertureSpads == 1) && (count > 32)) || + ((ApertureSpads == 0) && (count > 12))) + Status = VL53L0_perform_ref_spad_management(Dev, &refSpadCount, + &isApertureSpads); + else + Status = VL53L0_set_reference_spads(Dev, count, ApertureSpads); + + + /* Initialize tuning settings buffer to prevent compiler warning. */ + pTuningSettingBuffer = DefaultTuningSettings; + + if (Status == VL53L0_ERROR_NONE) { + UseInternalTuningSettings = PALDevDataGet(Dev, + UseInternalTuningSettings); + + if (UseInternalTuningSettings == 0) + pTuningSettingBuffer = PALDevDataGet(Dev, + pTuningSettingsPointer); + else + pTuningSettingBuffer = DefaultTuningSettings; + + } + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_load_tuning_settings(Dev, pTuningSettingBuffer); + + + /* Set interrupt config to new sample ready */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_SetGpioConfig(Dev, 0, 0, + VL53L0_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY, + VL53L0_INTERRUPTPOLARITY_LOW); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_RdWord(Dev, 0x84, &tempword); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, OscFrequencyMHz, + VL53L0_FIXPOINT412TOFIXPOINT1616(tempword)); + } + + /* After static init, some device parameters may be changed, + * so update them + */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetDeviceParameters(Dev, &CurrentParameters); + + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_GetFractionEnable(Dev, &tempbyte); + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, RangeFractionalEnable, tempbyte); + + } + + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, CurrentParameters, CurrentParameters); + + + /* read the sequence config and save it */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdByte(Dev, + VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, &tempbyte); + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, SequenceConfig, tempbyte); + + } + + /* Disable MSRC and TCC by default */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_TCC, 0); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_MSRC, 0); + + + /* Set PAL State to standby */ + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, PalState, VL53L0_STATE_IDLE); + + + + /* Store pre-range vcsel period */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_GetVcselPulsePeriod( + Dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + &vcselPulsePeriodPCLK); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETDEVICESPECIFICPARAMETER( + Dev, + PreRangeVcselPulsePeriod, + vcselPulsePeriodPCLK); + } + + /* Store final-range vcsel period */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_GetVcselPulsePeriod( + Dev, + VL53L0_VCSEL_PERIOD_FINAL_RANGE, + &vcselPulsePeriodPCLK); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETDEVICESPECIFICPARAMETER( + Dev, + FinalRangeVcselPulsePeriod, + vcselPulsePeriodPCLK); + } + + /* Store pre-range timeout */ + if (Status == VL53L0_ERROR_NONE) { + Status = get_sequence_step_timeout( + Dev, + VL53L0_SEQUENCESTEP_PRE_RANGE, + &seqTimeoutMicroSecs); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETDEVICESPECIFICPARAMETER( + Dev, + PreRangeTimeoutMicroSecs, + seqTimeoutMicroSecs); + } + + /* Store final-range timeout */ + if (Status == VL53L0_ERROR_NONE) { + Status = get_sequence_step_timeout( + Dev, + VL53L0_SEQUENCESTEP_FINAL_RANGE, + &seqTimeoutMicroSecs); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETDEVICESPECIFICPARAMETER( + Dev, + FinalRangeTimeoutMicroSecs, + seqTimeoutMicroSecs); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_WaitDeviceBooted(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + /* not implemented on VL53L0 */ + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_ResetDevice(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + /* Set reset bit */ + Status = VL53L0_WrByte(Dev, VL53L0_REG_SOFT_RESET_GO2_SOFT_RESET_N, + 0x00); + + /* Wait for some time */ + if (Status == VL53L0_ERROR_NONE) { + do { + Status = VL53L0_RdByte(Dev, + VL53L0_REG_IDENTIFICATION_MODEL_ID, &Byte); + } while (Byte != 0x00); + } + + VL53L0_PollingDelay(Dev); + + /* Release reset */ + Status = VL53L0_WrByte(Dev, VL53L0_REG_SOFT_RESET_GO2_SOFT_RESET_N, + 0x01); + + /* Wait until correct boot-up of the device */ + if (Status == VL53L0_ERROR_NONE) { + do { + Status = VL53L0_RdByte(Dev, + VL53L0_REG_IDENTIFICATION_MODEL_ID, &Byte); + } while (Byte == 0x00); + } + + VL53L0_PollingDelay(Dev); + + /* Set PAL State to VL53L0_STATE_POWERDOWN */ + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, PalState, VL53L0_STATE_POWERDOWN); + + + LOG_FUNCTION_END(Status); + return Status; +} +/* End Group PAL Init Functions */ + +/* Group PAL Parameters Functions */ +VL53L0_Error VL53L0_SetDeviceParameters(VL53L0_DEV Dev, + const VL53L0_DeviceParameters_t *pDeviceParameters) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int i; + + LOG_FUNCTION_START(""); + Status = VL53L0_SetDeviceMode(Dev, pDeviceParameters->DeviceMode); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetHistogramMode(Dev, + pDeviceParameters->HistogramMode); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetInterMeasurementPeriodMilliSeconds(Dev, + pDeviceParameters->InterMeasurementPeriodMilliSeconds); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetXTalkCompensationRateMegaCps(Dev, + pDeviceParameters->XTalkCompensationRateMegaCps); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetOffsetCalibrationDataMicroMeter(Dev, + pDeviceParameters->RangeOffsetMicroMeters); + + + for (i = 0; i < VL53L0_CHECKENABLE_NUMBER_OF_CHECKS; i++) { + if (Status == VL53L0_ERROR_NONE) + Status |= VL53L0_SetLimitCheckEnable(Dev, i, + pDeviceParameters->LimitChecksEnable[i]); + else + break; + + if (Status == VL53L0_ERROR_NONE) + Status |= VL53L0_SetLimitCheckValue(Dev, i, + pDeviceParameters->LimitChecksValue[i]); + else + break; + + } + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetWrapAroundCheckEnable(Dev, + pDeviceParameters->WrapAroundCheckEnable); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetMeasurementTimingBudgetMicroSeconds(Dev, + pDeviceParameters->MeasurementTimingBudgetMicroSeconds); + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetDeviceParameters(VL53L0_DEV Dev, + VL53L0_DeviceParameters_t *pDeviceParameters) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int i; + + LOG_FUNCTION_START(""); + + Status = VL53L0_GetDeviceMode(Dev, &(pDeviceParameters->DeviceMode)); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetHistogramMode(Dev, + &(pDeviceParameters->HistogramMode)); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetInterMeasurementPeriodMilliSeconds(Dev, + &(pDeviceParameters->InterMeasurementPeriodMilliSeconds)); + + + if (Status == VL53L0_ERROR_NONE) + pDeviceParameters->XTalkCompensationEnable = 0; + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetXTalkCompensationRateMegaCps(Dev, + &(pDeviceParameters->XTalkCompensationRateMegaCps)); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetOffsetCalibrationDataMicroMeter(Dev, + &(pDeviceParameters->RangeOffsetMicroMeters)); + + + if (Status == VL53L0_ERROR_NONE) { + for (i = 0; i < VL53L0_CHECKENABLE_NUMBER_OF_CHECKS; i++) { + /* get first the values, then the enables. + * VL53L0_GetLimitCheckValue will modify the enable + * flags + */ + if (Status == VL53L0_ERROR_NONE) { + Status |= VL53L0_GetLimitCheckValue(Dev, i, + &(pDeviceParameters->LimitChecksValue[i])); + } else { + break; + } + if (Status == VL53L0_ERROR_NONE) { + Status |= VL53L0_GetLimitCheckEnable(Dev, i, + &(pDeviceParameters->LimitChecksEnable[i])); + } else { + break; + } + } + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_GetWrapAroundCheckEnable(Dev, + &(pDeviceParameters->WrapAroundCheckEnable)); + } + + /* Need to be done at the end as it uses VCSELPulsePeriod */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_GetMeasurementTimingBudgetMicroSeconds(Dev, + &(pDeviceParameters->MeasurementTimingBudgetMicroSeconds)); + } + + if (Status == VL53L0_ERROR_NONE) { + for (i = 0; i < VL53L0_DMAX_LUT_SIZE; i++) { + pDeviceParameters->dmax_lut.ambRate_mcps[i] = + Dev->Data.CurrentParameters.dmax_lut.ambRate_mcps[i]; + pDeviceParameters->dmax_lut.dmax_mm[i] = + Dev->Data.CurrentParameters.dmax_lut.dmax_mm[i]; + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetDeviceMode(VL53L0_DEV Dev, VL53L0_DeviceModes DeviceMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START("%d", (int)DeviceMode); + + switch (DeviceMode) { + case VL53L0_DEVICEMODE_SINGLE_RANGING: + case VL53L0_DEVICEMODE_CONTINUOUS_RANGING: + case VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING: + case VL53L0_DEVICEMODE_SINGLE_HISTOGRAM: + case VL53L0_DEVICEMODE_GPIO_DRIVE: + case VL53L0_DEVICEMODE_GPIO_OSC: + /* Supported modes */ + VL53L0_SETPARAMETERFIELD(Dev, DeviceMode, DeviceMode); + break; + default: + /* Unsupported mode */ + Status = VL53L0_ERROR_MODE_NOT_SUPPORTED; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetDeviceMode(VL53L0_DEV Dev, + VL53L0_DeviceModes *pDeviceMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + VL53L0_GETPARAMETERFIELD(Dev, DeviceMode, *pDeviceMode); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetRangeFractionEnable(VL53L0_DEV Dev, uint8_t Enable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START("%d", (int)Enable); + + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_RANGE_CONFIG, Enable); + + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, RangeFractionalEnable, Enable); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetFractionEnable(VL53L0_DEV Dev, uint8_t *pEnabled) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L0_REG_SYSTEM_RANGE_CONFIG, pEnabled); + + if (Status == VL53L0_ERROR_NONE) + *pEnabled = (*pEnabled & 1); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetHistogramMode(VL53L0_DEV Dev, + VL53L0_HistogramModes HistogramMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START("%d", (int)HistogramMode); + + Status = VL53L0_set_histogram_mode(Dev, HistogramMode); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetHistogramMode(VL53L0_DEV Dev, + VL53L0_HistogramModes *pHistogramMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_histogram_mode(Dev, pHistogramMode); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetMeasurementTimingBudgetMicroSeconds(VL53L0_DEV Dev, + uint32_t MeasurementTimingBudgetMicroSeconds) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_set_measurement_timing_budget_micro_seconds(Dev, + MeasurementTimingBudgetMicroSeconds); + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L0_Error VL53L0_GetMeasurementTimingBudgetMicroSeconds(VL53L0_DEV Dev, + uint32_t *pMeasurementTimingBudgetMicroSeconds) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_measurement_timing_budget_micro_seconds(Dev, + pMeasurementTimingBudgetMicroSeconds); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetVcselPulsePeriod(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, uint8_t VCSELPulsePeriodPCLK) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_set_vcsel_pulse_period(Dev, VcselPeriodType, + VCSELPulsePeriodPCLK); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetVcselPulsePeriod(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, uint8_t *pVCSELPulsePeriodPCLK) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_vcsel_pulse_period(Dev, VcselPeriodType, + pVCSELPulsePeriodPCLK); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetSequenceStepEnable(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, uint8_t SequenceStepEnabled) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t SequenceConfig = 0; + uint8_t SequenceConfigNew = 0; + uint32_t MeasurementTimingBudgetMicroSeconds; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, + &SequenceConfig); + + SequenceConfigNew = SequenceConfig; + + if (Status == VL53L0_ERROR_NONE) { + if (SequenceStepEnabled == 1) { + + /* Enable requested sequence step + */ + switch (SequenceStepId) { + case VL53L0_SEQUENCESTEP_TCC: + SequenceConfigNew |= 0x10; + break; + case VL53L0_SEQUENCESTEP_DSS: + SequenceConfigNew |= 0x28; + break; + case VL53L0_SEQUENCESTEP_MSRC: + SequenceConfigNew |= 0x04; + break; + case VL53L0_SEQUENCESTEP_PRE_RANGE: + SequenceConfigNew |= 0x40; + break; + case VL53L0_SEQUENCESTEP_FINAL_RANGE: + SequenceConfigNew |= 0x80; + break; + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } else { + /* Disable requested sequence step + */ + switch (SequenceStepId) { + case VL53L0_SEQUENCESTEP_TCC: + SequenceConfigNew &= 0xef; + break; + case VL53L0_SEQUENCESTEP_DSS: + SequenceConfigNew &= 0xd7; + break; + case VL53L0_SEQUENCESTEP_MSRC: + SequenceConfigNew &= 0xfb; + break; + case VL53L0_SEQUENCESTEP_PRE_RANGE: + SequenceConfigNew &= 0xbf; + break; + case VL53L0_SEQUENCESTEP_FINAL_RANGE: + SequenceConfigNew &= 0x7f; + break; + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } + } + + if (SequenceConfigNew != SequenceConfig) { + /* Apply New Setting */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_WrByte(Dev, + VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, SequenceConfigNew); + } + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, SequenceConfig, SequenceConfigNew); + + + /* Recalculate timing budget */ + if (Status == VL53L0_ERROR_NONE) { + VL53L0_GETPARAMETERFIELD(Dev, + MeasurementTimingBudgetMicroSeconds, + MeasurementTimingBudgetMicroSeconds); + + VL53L0_SetMeasurementTimingBudgetMicroSeconds(Dev, + MeasurementTimingBudgetMicroSeconds); + } + } + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L0_Error sequence_step_enabled(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, uint8_t SequenceConfig, + uint8_t *pSequenceStepEnabled) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + *pSequenceStepEnabled = 0; + LOG_FUNCTION_START(""); + + switch (SequenceStepId) { + case VL53L0_SEQUENCESTEP_TCC: + *pSequenceStepEnabled = (SequenceConfig & 0x10) >> 4; + break; + case VL53L0_SEQUENCESTEP_DSS: + *pSequenceStepEnabled = (SequenceConfig & 0x08) >> 3; + break; + case VL53L0_SEQUENCESTEP_MSRC: + *pSequenceStepEnabled = (SequenceConfig & 0x04) >> 2; + break; + case VL53L0_SEQUENCESTEP_PRE_RANGE: + *pSequenceStepEnabled = (SequenceConfig & 0x40) >> 6; + break; + case VL53L0_SEQUENCESTEP_FINAL_RANGE: + *pSequenceStepEnabled = (SequenceConfig & 0x80) >> 7; + break; + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetSequenceStepEnable(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, uint8_t *pSequenceStepEnabled) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t SequenceConfig = 0; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, + &SequenceConfig); + + if (Status == VL53L0_ERROR_NONE) { + Status = sequence_step_enabled(Dev, SequenceStepId, + SequenceConfig, pSequenceStepEnabled); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetSequenceStepEnables(VL53L0_DEV Dev, + VL53L0_SchedulerSequenceSteps_t *pSchedulerSequenceSteps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t SequenceConfig = 0; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, + &SequenceConfig); + + if (Status == VL53L0_ERROR_NONE) { + Status = sequence_step_enabled(Dev, + VL53L0_SEQUENCESTEP_TCC, SequenceConfig, + &pSchedulerSequenceSteps->TccOn); + } + if (Status == VL53L0_ERROR_NONE) { + Status = sequence_step_enabled(Dev, + VL53L0_SEQUENCESTEP_DSS, SequenceConfig, + &pSchedulerSequenceSteps->DssOn); + } + if (Status == VL53L0_ERROR_NONE) { + Status = sequence_step_enabled(Dev, + VL53L0_SEQUENCESTEP_MSRC, SequenceConfig, + &pSchedulerSequenceSteps->MsrcOn); + } + if (Status == VL53L0_ERROR_NONE) { + Status = sequence_step_enabled(Dev, + VL53L0_SEQUENCESTEP_PRE_RANGE, SequenceConfig, + &pSchedulerSequenceSteps->PreRangeOn); + } + if (Status == VL53L0_ERROR_NONE) { + Status = sequence_step_enabled(Dev, + VL53L0_SEQUENCESTEP_FINAL_RANGE, SequenceConfig, + &pSchedulerSequenceSteps->FinalRangeOn); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetNumberOfSequenceSteps(VL53L0_DEV Dev, + uint8_t *pNumberOfSequenceSteps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pNumberOfSequenceSteps = VL53L0_SEQUENCESTEP_NUMBER_OF_CHECKS; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetSequenceStepsInfo(VL53L0_SequenceStepId SequenceStepId, + char *pSequenceStepsString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_sequence_steps_info( + SequenceStepId, + pSequenceStepsString); + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L0_Error VL53L0_SetSequenceStepTimeout(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, FixPoint1616_t TimeOutMilliSecs) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_Error Status1 = VL53L0_ERROR_NONE; + uint32_t TimeoutMicroSeconds = ((TimeOutMilliSecs * 1000) + 0x8000) + >> 16; + uint32_t MeasurementTimingBudgetMicroSeconds; + FixPoint1616_t OldTimeOutMicroSeconds; + + LOG_FUNCTION_START(""); + + /* Read back the current value in case we need to revert back to this. + */ + Status = get_sequence_step_timeout(Dev, SequenceStepId, + &OldTimeOutMicroSeconds); + + if (Status == VL53L0_ERROR_NONE) { + Status = set_sequence_step_timeout(Dev, SequenceStepId, + TimeoutMicroSeconds); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_GETPARAMETERFIELD(Dev, + MeasurementTimingBudgetMicroSeconds, + MeasurementTimingBudgetMicroSeconds); + + /* At this point we don't know if the requested value is valid, + * therefore proceed to update the entire timing budget and + * if this fails, revert back to the previous value. + */ + Status = VL53L0_SetMeasurementTimingBudgetMicroSeconds(Dev, + MeasurementTimingBudgetMicroSeconds); + + if (Status != VL53L0_ERROR_NONE) { + Status1 = set_sequence_step_timeout(Dev, SequenceStepId, + OldTimeOutMicroSeconds); + + if (Status1 == VL53L0_ERROR_NONE) { + Status1 = + VL53L0_SetMeasurementTimingBudgetMicroSeconds( + Dev, + MeasurementTimingBudgetMicroSeconds); + } + + Status = Status1; + } + } + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L0_Error VL53L0_GetSequenceStepTimeout(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, FixPoint1616_t *pTimeOutMilliSecs) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint32_t TimeoutMicroSeconds; + + LOG_FUNCTION_START(""); + + Status = get_sequence_step_timeout(Dev, SequenceStepId, + &TimeoutMicroSeconds); + if (Status == VL53L0_ERROR_NONE) { + TimeoutMicroSeconds <<= 8; + *pTimeOutMilliSecs = (TimeoutMicroSeconds + 500)/1000; + *pTimeOutMilliSecs <<= 8; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetInterMeasurementPeriodMilliSeconds(VL53L0_DEV Dev, + uint32_t InterMeasurementPeriodMilliSeconds) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t osc_calibrate_val; + uint32_t IMPeriodMilliSeconds; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdWord(Dev, VL53L0_REG_OSC_CALIBRATE_VAL, + &osc_calibrate_val); + + if (Status == VL53L0_ERROR_NONE) { + if (osc_calibrate_val != 0) { + IMPeriodMilliSeconds = + InterMeasurementPeriodMilliSeconds + * osc_calibrate_val; + } else { + IMPeriodMilliSeconds = + InterMeasurementPeriodMilliSeconds; + } + Status = VL53L0_WrDWord(Dev, + VL53L0_REG_SYSTEM_INTERMEASUREMENT_PERIOD, + IMPeriodMilliSeconds); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETPARAMETERFIELD(Dev, + InterMeasurementPeriodMilliSeconds, + InterMeasurementPeriodMilliSeconds); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetInterMeasurementPeriodMilliSeconds(VL53L0_DEV Dev, + uint32_t *pInterMeasurementPeriodMilliSeconds) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t osc_calibrate_val; + uint32_t IMPeriodMilliSeconds; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdWord(Dev, VL53L0_REG_OSC_CALIBRATE_VAL, + &osc_calibrate_val); + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdDWord(Dev, + VL53L0_REG_SYSTEM_INTERMEASUREMENT_PERIOD, + &IMPeriodMilliSeconds); + } + + if (Status == VL53L0_ERROR_NONE) { + if (osc_calibrate_val != 0) { + *pInterMeasurementPeriodMilliSeconds = + IMPeriodMilliSeconds / osc_calibrate_val; + } + VL53L0_SETPARAMETERFIELD(Dev, + InterMeasurementPeriodMilliSeconds, + *pInterMeasurementPeriodMilliSeconds); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetXTalkCompensationEnable(VL53L0_DEV Dev, + uint8_t XTalkCompensationEnable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + FixPoint1616_t TempFix1616; + uint16_t LinearityCorrectiveGain; + + LOG_FUNCTION_START(""); + + LinearityCorrectiveGain = PALDevDataGet(Dev, LinearityCorrectiveGain); + + if ((XTalkCompensationEnable == 0) + || (LinearityCorrectiveGain != 1000)) { + TempFix1616 = 0; + } else { + VL53L0_GETPARAMETERFIELD(Dev, XTalkCompensationRateMegaCps, + TempFix1616); + } + + /* the following register has a format 3.13 */ + Status = VL53L0_WrWord(Dev, + VL53L0_REG_CROSSTALK_COMPENSATION_PEAK_RATE_MCPS, + VL53L0_FIXPOINT1616TOFIXPOINT313(TempFix1616)); + + if (Status == VL53L0_ERROR_NONE) { + if (XTalkCompensationEnable == 0) { + VL53L0_SETPARAMETERFIELD(Dev, XTalkCompensationEnable, + 0); + } else { + VL53L0_SETPARAMETERFIELD(Dev, XTalkCompensationEnable, + 1); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetXTalkCompensationEnable(VL53L0_DEV Dev, + uint8_t *pXTalkCompensationEnable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Temp8; + + LOG_FUNCTION_START(""); + + VL53L0_GETPARAMETERFIELD(Dev, XTalkCompensationEnable, Temp8); + *pXTalkCompensationEnable = Temp8; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetXTalkCompensationRateMegaCps(VL53L0_DEV Dev, + FixPoint1616_t XTalkCompensationRateMegaCps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Temp8; + uint16_t LinearityCorrectiveGain; + uint16_t data; + + LOG_FUNCTION_START(""); + + VL53L0_GETPARAMETERFIELD(Dev, XTalkCompensationEnable, Temp8); + LinearityCorrectiveGain = PALDevDataGet(Dev, LinearityCorrectiveGain); + + if (Temp8 == 0) { /* disabled write only internal value */ + VL53L0_SETPARAMETERFIELD(Dev, XTalkCompensationRateMegaCps, + XTalkCompensationRateMegaCps); + } else { + /* the following register has a format 3.13 */ + if (LinearityCorrectiveGain == 1000) { + data = VL53L0_FIXPOINT1616TOFIXPOINT313( + XTalkCompensationRateMegaCps); + } else { + data = 0; + } + + Status = VL53L0_WrWord(Dev, + VL53L0_REG_CROSSTALK_COMPENSATION_PEAK_RATE_MCPS, data); + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETPARAMETERFIELD(Dev, + XTalkCompensationRateMegaCps, + XTalkCompensationRateMegaCps); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetXTalkCompensationRateMegaCps(VL53L0_DEV Dev, + FixPoint1616_t *pXTalkCompensationRateMegaCps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t Value; + FixPoint1616_t TempFix1616; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdWord(Dev, + VL53L0_REG_CROSSTALK_COMPENSATION_PEAK_RATE_MCPS, (uint16_t *)&Value); + if (Status == VL53L0_ERROR_NONE) { + if (Value == 0) { + /* the Xtalk is disabled return value from memory */ + VL53L0_GETPARAMETERFIELD(Dev, + XTalkCompensationRateMegaCps, TempFix1616); + *pXTalkCompensationRateMegaCps = TempFix1616; + VL53L0_SETPARAMETERFIELD(Dev, XTalkCompensationEnable, + 0); + } else { + TempFix1616 = VL53L0_FIXPOINT313TOFIXPOINT1616(Value); + *pXTalkCompensationRateMegaCps = TempFix1616; + VL53L0_SETPARAMETERFIELD(Dev, + XTalkCompensationRateMegaCps, TempFix1616); + VL53L0_SETPARAMETERFIELD(Dev, XTalkCompensationEnable, + 1); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetRefCalibration(VL53L0_DEV Dev, uint8_t VhvSettings, + uint8_t PhaseCal) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_set_ref_calibration(Dev, VhvSettings, PhaseCal); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetRefCalibration(VL53L0_DEV Dev, uint8_t *pVhvSettings, + uint8_t *pPhaseCal) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_ref_calibration(Dev, pVhvSettings, pPhaseCal); + + LOG_FUNCTION_END(Status); + return Status; +} + +/* + * CHECK LIMIT FUNCTIONS + */ + +VL53L0_Error VL53L0_GetNumberOfLimitCheck(uint16_t *pNumberOfLimitCheck) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pNumberOfLimitCheck = VL53L0_CHECKENABLE_NUMBER_OF_CHECKS; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetLimitCheckInfo(VL53L0_DEV Dev, uint16_t LimitCheckId, + char *pLimitCheckString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_limit_check_info(Dev, LimitCheckId, + pLimitCheckString); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetLimitCheckStatus(VL53L0_DEV Dev, uint16_t LimitCheckId, + uint8_t *pLimitCheckStatus) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Temp8; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L0_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L0_ERROR_INVALID_PARAMS; + } else { + + VL53L0_GETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + LimitCheckId, Temp8); + + *pLimitCheckStatus = Temp8; + + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetLimitCheckEnable(VL53L0_DEV Dev, uint16_t LimitCheckId, + uint8_t LimitCheckEnable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + FixPoint1616_t TempFix1616 = 0; + uint8_t LimitCheckEnableInt = 0; + uint8_t LimitCheckDisable = 0; + uint8_t Temp8; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L0_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L0_ERROR_INVALID_PARAMS; + } else { + if (LimitCheckEnable == 0) { + TempFix1616 = 0; + LimitCheckEnableInt = 0; + LimitCheckDisable = 1; + + } else { + VL53L0_GETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + LimitCheckId, TempFix1616); + LimitCheckDisable = 0; + /* this to be sure to have either 0 or 1 */ + LimitCheckEnableInt = 1; + } + + switch (LimitCheckId) { + + case VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE: + /* internal computation: */ + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + LimitCheckEnableInt); + + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: + + Status = VL53L0_WrWord(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT, + VL53L0_FIXPOINT1616TOFIXPOINT97(TempFix1616)); + + break; + + case VL53L0_CHECKENABLE_SIGNAL_REF_CLIP: + + /* internal computation: */ + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, + LimitCheckEnableInt); + + break; + + case VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD: + + /* internal computation: */ + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + LimitCheckEnableInt); + + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_MSRC: + + Temp8 = (uint8_t)(LimitCheckDisable << 1); + Status = VL53L0_UpdateByte(Dev, + VL53L0_REG_MSRC_CONFIG_CONTROL, + 0xFE, Temp8); + + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_PRE_RANGE: + + Temp8 = (uint8_t)(LimitCheckDisable << 4); + Status = VL53L0_UpdateByte(Dev, + VL53L0_REG_MSRC_CONFIG_CONTROL, + 0xEF, Temp8); + + break; + + + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + + } + + } + + if (Status == VL53L0_ERROR_NONE) { + if (LimitCheckEnable == 0) { + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + LimitCheckId, 0); + } else { + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + LimitCheckId, 1); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetLimitCheckEnable(VL53L0_DEV Dev, uint16_t LimitCheckId, + uint8_t *pLimitCheckEnable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Temp8; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L0_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L0_ERROR_INVALID_PARAMS; + *pLimitCheckEnable = 0; + } else { + VL53L0_GETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + LimitCheckId, Temp8); + *pLimitCheckEnable = Temp8; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetLimitCheckValue(VL53L0_DEV Dev, uint16_t LimitCheckId, + FixPoint1616_t LimitCheckValue) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Temp8; + + LOG_FUNCTION_START(""); + + VL53L0_GETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, LimitCheckId, + Temp8); + + if (Temp8 == 0) { /* disabled write only internal value */ + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + LimitCheckId, LimitCheckValue); + } else { + + switch (LimitCheckId) { + + case VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE: + /* internal computation: */ + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + LimitCheckValue); + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: + + Status = VL53L0_WrWord(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT, + VL53L0_FIXPOINT1616TOFIXPOINT97( + LimitCheckValue)); + + break; + + case VL53L0_CHECKENABLE_SIGNAL_REF_CLIP: + + /* internal computation: */ + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, + LimitCheckValue); + + break; + + case VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD: + + /* internal computation: */ + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + LimitCheckValue); + + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_MSRC: + case VL53L0_CHECKENABLE_SIGNAL_RATE_PRE_RANGE: + + Status = VL53L0_WrWord(Dev, + VL53L0_REG_PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT, + VL53L0_FIXPOINT1616TOFIXPOINT97( + LimitCheckValue)); + + break; + + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + LimitCheckId, LimitCheckValue); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetLimitCheckValue(VL53L0_DEV Dev, uint16_t LimitCheckId, + FixPoint1616_t *pLimitCheckValue) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t EnableZeroValue = 0; + uint16_t Temp16; + FixPoint1616_t TempFix1616; + + LOG_FUNCTION_START(""); + + switch (LimitCheckId) { + + case VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE: + /* internal computation: */ + VL53L0_GETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, TempFix1616); + EnableZeroValue = 0; + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: + Status = VL53L0_RdWord(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_MIN_COUNT_RATE_RTN_LIMIT, + &Temp16); + if (Status == VL53L0_ERROR_NONE) + TempFix1616 = VL53L0_FIXPOINT97TOFIXPOINT1616(Temp16); + + + EnableZeroValue = 1; + break; + + case VL53L0_CHECKENABLE_SIGNAL_REF_CLIP: + /* internal computation: */ + VL53L0_GETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, TempFix1616); + EnableZeroValue = 0; + break; + + case VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD: + /* internal computation: */ + VL53L0_GETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, TempFix1616); + EnableZeroValue = 0; + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_MSRC: + case VL53L0_CHECKENABLE_SIGNAL_RATE_PRE_RANGE: + Status = VL53L0_RdWord(Dev, + VL53L0_REG_PRE_RANGE_MIN_COUNT_RATE_RTN_LIMIT, + &Temp16); + if (Status == VL53L0_ERROR_NONE) + TempFix1616 = VL53L0_FIXPOINT97TOFIXPOINT1616(Temp16); + + + EnableZeroValue = 0; + break; + + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + + } + + if (Status == VL53L0_ERROR_NONE) { + + if (EnableZeroValue == 1) { + + if (TempFix1616 == 0) { + /* disabled: return value from memory */ + VL53L0_GETARRAYPARAMETERFIELD(Dev, + LimitChecksValue, LimitCheckId, + TempFix1616); + *pLimitCheckValue = TempFix1616; + VL53L0_SETARRAYPARAMETERFIELD(Dev, + LimitChecksEnable, LimitCheckId, 0); + } else { + *pLimitCheckValue = TempFix1616; + VL53L0_SETARRAYPARAMETERFIELD(Dev, + LimitChecksValue, LimitCheckId, + TempFix1616); + VL53L0_SETARRAYPARAMETERFIELD(Dev, + LimitChecksEnable, LimitCheckId, 1); + } + } else { + *pLimitCheckValue = TempFix1616; + } + } + + LOG_FUNCTION_END(Status); + return Status; + +} + +VL53L0_Error VL53L0_GetLimitCheckCurrent(VL53L0_DEV Dev, uint16_t LimitCheckId, + FixPoint1616_t *pLimitCheckCurrent) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_RangingMeasurementData_t LastRangeDataBuffer; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L0_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L0_ERROR_INVALID_PARAMS; + } else { + switch (LimitCheckId) { + case VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE: + /* Need to run a ranging to have the latest values */ + *pLimitCheckCurrent = PALDevDataGet(Dev, SigmaEstimate); + + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: + /* Need to run a ranging to have the latest values */ + LastRangeDataBuffer = PALDevDataGet(Dev, + LastRangeMeasure); + *pLimitCheckCurrent = + LastRangeDataBuffer.SignalRateRtnMegaCps; + + break; + + case VL53L0_CHECKENABLE_SIGNAL_REF_CLIP: + /* Need to run a ranging to have the latest values */ + *pLimitCheckCurrent = PALDevDataGet(Dev, + LastSignalRefMcps); + + break; + + case VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD: + /* Need to run a ranging to have the latest values */ + LastRangeDataBuffer = PALDevDataGet(Dev, + LastRangeMeasure); + *pLimitCheckCurrent = + LastRangeDataBuffer.SignalRateRtnMegaCps; + + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_MSRC: + /* Need to run a ranging to have the latest values */ + LastRangeDataBuffer = PALDevDataGet(Dev, + LastRangeMeasure); + *pLimitCheckCurrent = + LastRangeDataBuffer.SignalRateRtnMegaCps; + + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_PRE_RANGE: + /* Need to run a ranging to have the latest values */ + LastRangeDataBuffer = PALDevDataGet(Dev, + LastRangeMeasure); + *pLimitCheckCurrent = + LastRangeDataBuffer.SignalRateRtnMegaCps; + + break; + + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } + + LOG_FUNCTION_END(Status); + return Status; + +} + +/* + * WRAPAROUND Check + */ +VL53L0_Error VL53L0_SetWrapAroundCheckEnable(VL53L0_DEV Dev, + uint8_t WrapAroundCheckEnable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte; + uint8_t WrapAroundCheckEnableInt; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, &Byte); + if (WrapAroundCheckEnable == 0) { + /* Disable wraparound */ + Byte = Byte & 0x7F; + WrapAroundCheckEnableInt = 0; + } else { + /*Enable wraparound */ + Byte = Byte | 0x80; + WrapAroundCheckEnableInt = 1; + } + + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, Byte); + + if (Status == VL53L0_ERROR_NONE) { + PALDevDataSet(Dev, SequenceConfig, Byte); + VL53L0_SETPARAMETERFIELD(Dev, WrapAroundCheckEnable, + WrapAroundCheckEnableInt); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetWrapAroundCheckEnable(VL53L0_DEV Dev, + uint8_t *pWrapAroundCheckEnable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t data; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, &data); + if (Status == VL53L0_ERROR_NONE) { + PALDevDataSet(Dev, SequenceConfig, data); + if (data & (0x01 << 7)) + *pWrapAroundCheckEnable = 0x01; + else + *pWrapAroundCheckEnable = 0x00; + } + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETPARAMETERFIELD(Dev, WrapAroundCheckEnable, + *pWrapAroundCheckEnable); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +/* End Group PAL Parameters Functions */ + +/* Group PAL Measurement Functions */ +VL53L0_Error VL53L0_PerformSingleMeasurement(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceModes DeviceMode; + + LOG_FUNCTION_START(""); + + /* Get Current DeviceMode */ + Status = VL53L0_GetDeviceMode(Dev, &DeviceMode); + + /* Start immediately to run a single ranging measurement in case of + * single ranging or single histogram + */ + if (Status == VL53L0_ERROR_NONE + && DeviceMode == VL53L0_DEVICEMODE_SINGLE_RANGING) + Status = VL53L0_StartMeasurement(Dev); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_measurement_poll_for_completion(Dev); + + + /* Change PAL State in case of single ranging or single histogram */ + if (Status == VL53L0_ERROR_NONE + && DeviceMode == VL53L0_DEVICEMODE_SINGLE_RANGING) + PALDevDataSet(Dev, PalState, VL53L0_STATE_IDLE); + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_PerformSingleHistogramMeasurement(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_perform_single_histogram_measurement(Dev, + pHistogramMeasurementData); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_PerformRefCalibration(VL53L0_DEV Dev, uint8_t *pVhvSettings, + uint8_t *pPhaseCal) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_perform_ref_calibration(Dev, pVhvSettings, + pPhaseCal, 1); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_PerformXTalkMeasurement(VL53L0_DEV Dev, + uint32_t TimeoutMs, FixPoint1616_t *pXtalkPerSpad, + uint8_t *pAmbientTooHigh) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_perform_xtalk_measurement(Dev, TimeoutMs, + pXtalkPerSpad, pAmbientTooHigh); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_PerformXTalkCalibration(VL53L0_DEV Dev, + FixPoint1616_t XTalkCalDistance, + FixPoint1616_t *pXTalkCompensationRateMegaCps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_perform_xtalk_calibration(Dev, XTalkCalDistance, + pXTalkCompensationRateMegaCps); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_PerformOffsetCalibration(VL53L0_DEV Dev, + FixPoint1616_t CalDistanceMilliMeter, int32_t *pOffsetMicroMeter) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_perform_offset_calibration(Dev, CalDistanceMilliMeter, + pOffsetMicroMeter); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_CheckAndLoadInterruptSettings(VL53L0_DEV Dev, + uint8_t StartNotStopFlag) +{ + uint8_t InterruptConfig; + FixPoint1616_t ThresholdLow; + FixPoint1616_t ThresholdHigh; + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + InterruptConfig = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + Pin0GpioFunctionality); + + switch (InterruptConfig) { + case VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW: + Status = VL53L0_GetInterruptThresholds(Dev, + VL53L0_DEVICEMODE_CONTINUOUS_RANGING, + &ThresholdLow, &ThresholdHigh); + + if ((ThresholdLow > 255*65536) && + (Status == VL53L0_ERROR_NONE)) { + + if (StartNotStopFlag != 0) { + Status = VL53L0_load_tuning_settings(Dev, + InterruptThresholdSettings); + } else { + Status |= VL53L0_WrByte(Dev, 0xFF, 0x04); + Status |= VL53L0_WrByte(Dev, 0x70, 0x00); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + Status |= VL53L0_WrByte(Dev, 0x80, 0x00); + } + } + break; + case VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH: + Status = VL53L0_GetInterruptThresholds(Dev, + VL53L0_DEVICEMODE_CONTINUOUS_RANGING, + &ThresholdLow, &ThresholdHigh); + + if ((ThresholdHigh > 0) && + (Status == VL53L0_ERROR_NONE)) { + + if (StartNotStopFlag != 0) { + Status = VL53L0_load_tuning_settings(Dev, + InterruptThresholdSettings); + } else { + Status |= VL53L0_WrByte(Dev, 0xFF, 0x04); + Status |= VL53L0_WrByte(Dev, 0x70, 0x00); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + Status |= VL53L0_WrByte(Dev, 0x80, 0x00); + } + } + break; + case VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT: + Status = VL53L0_GetInterruptThresholds(Dev, + VL53L0_DEVICEMODE_CONTINUOUS_RANGING, + &ThresholdLow, &ThresholdHigh); + + if (Status == VL53L0_ERROR_NONE) { + if (StartNotStopFlag != 0) { + Status = VL53L0_load_tuning_settings(Dev, + InterruptThresholdSettings); + } else { + Status |= VL53L0_WrByte(Dev, 0xFF, 0x04); + Status |= VL53L0_WrByte(Dev, 0x70, 0x00); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + Status |= VL53L0_WrByte(Dev, 0x80, 0x00); + } + } + break; + } + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L0_Error VL53L0_StartMeasurement(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceModes DeviceMode; + uint8_t Byte; + uint8_t StartStopByte = VL53L0_REG_SYSRANGE_MODE_START_STOP; + uint32_t LoopNb; + + LOG_FUNCTION_START(""); + + /* Get Current DeviceMode */ + VL53L0_GetDeviceMode(Dev, &DeviceMode); + + Status = VL53L0_WrByte(Dev, 0x80, 0x01); + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status = VL53L0_WrByte(Dev, 0x00, 0x00); + Status = VL53L0_WrByte(Dev, 0x91, PALDevDataGet(Dev, StopVariable)); + Status = VL53L0_WrByte(Dev, 0x00, 0x01); + Status = VL53L0_WrByte(Dev, 0xFF, 0x00); + Status = VL53L0_WrByte(Dev, 0x80, 0x00); + + switch (DeviceMode) { + case VL53L0_DEVICEMODE_SINGLE_RANGING: + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSRANGE_START, 0x01); + + Byte = StartStopByte; + if (Status == VL53L0_ERROR_NONE) { + /* Wait until start bit has been cleared */ + LoopNb = 0; + do { + if (LoopNb > 0) + Status = VL53L0_RdByte(Dev, + VL53L0_REG_SYSRANGE_START, &Byte); + LoopNb = LoopNb + 1; + } while (((Byte & StartStopByte) == StartStopByte) + && (Status == VL53L0_ERROR_NONE) + && (LoopNb < VL53L0_DEFAULT_MAX_LOOP)); + + if (LoopNb >= VL53L0_DEFAULT_MAX_LOOP) + Status = VL53L0_ERROR_TIME_OUT; + + } + + break; + case VL53L0_DEVICEMODE_CONTINUOUS_RANGING: + /* Back-to-back mode */ + + /* Check if need to apply interrupt settings */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_CheckAndLoadInterruptSettings(Dev, 1); + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_SYSRANGE_START, + VL53L0_REG_SYSRANGE_MODE_BACKTOBACK); + if (Status == VL53L0_ERROR_NONE) { + /* Set PAL State to Running */ + PALDevDataSet(Dev, PalState, VL53L0_STATE_RUNNING); + } + break; + case VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING: + /* Continuous mode */ + /* Check if need to apply interrupt settings */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_CheckAndLoadInterruptSettings(Dev, 1); + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_SYSRANGE_START, + VL53L0_REG_SYSRANGE_MODE_TIMED); + + if (Status == VL53L0_ERROR_NONE) { + /* Set PAL State to Running */ + PALDevDataSet(Dev, PalState, VL53L0_STATE_RUNNING); + } + break; + default: + /* Selected mode not supported */ + Status = VL53L0_ERROR_MODE_NOT_SUPPORTED; + } + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_StopMeasurement(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSRANGE_START, + VL53L0_REG_SYSRANGE_MODE_SINGLESHOT); + + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status = VL53L0_WrByte(Dev, 0x00, 0x00); + Status = VL53L0_WrByte(Dev, 0x91, 0x00); + Status = VL53L0_WrByte(Dev, 0x00, 0x01); + Status = VL53L0_WrByte(Dev, 0xFF, 0x00); + + if (Status == VL53L0_ERROR_NONE) { + /* Set PAL State to Idle */ + PALDevDataSet(Dev, PalState, VL53L0_STATE_IDLE); + } + + /* Check if need to apply interrupt settings */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_CheckAndLoadInterruptSettings(Dev, 0); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetMeasurementDataReady(VL53L0_DEV Dev, + uint8_t *pMeasurementDataReady) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t SysRangeStatusRegister; + uint8_t InterruptConfig; + uint32_t InterruptMask; + + LOG_FUNCTION_START(""); + + InterruptConfig = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + Pin0GpioFunctionality); + + if (InterruptConfig == + VL53L0_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY) { + Status = VL53L0_GetInterruptMaskStatus(Dev, &InterruptMask); + if (InterruptMask == + VL53L0_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY) + *pMeasurementDataReady = 1; + else + *pMeasurementDataReady = 0; + } else { + Status = VL53L0_RdByte(Dev, VL53L0_REG_RESULT_RANGE_STATUS, + &SysRangeStatusRegister); + if (Status == VL53L0_ERROR_NONE) { + if (SysRangeStatusRegister & 0x01) + *pMeasurementDataReady = 1; + else + *pMeasurementDataReady = 0; + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_WaitDeviceReadyForNewMeasurement(VL53L0_DEV Dev, + uint32_t MaxLoop) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + /* not implemented for VL53L0 */ + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L0_Error VL53L0_GetRangingMeasurementData(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t DeviceRangeStatus; + uint8_t RangeFractionalEnable; + uint8_t PalRangeStatus; + uint8_t XTalkCompensationEnable; + uint16_t AmbientRate; + FixPoint1616_t SignalRate; + uint16_t XTalkCompensationRateMegaCps; + uint16_t EffectiveSpadRtnCount; + uint16_t tmpuint16; + uint16_t XtalkRangeMilliMeter; + uint16_t LinearityCorrectiveGain; + uint8_t localBuffer[12]; + VL53L0_RangingMeasurementData_t LastRangeDataBuffer; + + LOG_FUNCTION_START(""); + + /* + * use multi read even if some registers are not useful, result will + * be more efficient + * start reading at 0x14 dec20 + * end reading at 0x21 dec33 total 14 bytes to read + */ + Status = VL53L0_ReadMulti(Dev, 0x14, localBuffer, 12); + + if (Status == VL53L0_ERROR_NONE) { + + pRangingMeasurementData->ZoneId = 0; /* Only one zone */ + pRangingMeasurementData->TimeStamp = 0; /* Not Implemented */ + + tmpuint16 = VL53L0_MAKEUINT16(localBuffer[11], localBuffer[10]); + /* cut1.1 if SYSTEM__RANGE_CONFIG if 1 range is 2bits fractional + *(format 11.2) else no fractional + */ + + pRangingMeasurementData->MeasurementTimeUsec = 0; + + + SignalRate = VL53L0_FIXPOINT97TOFIXPOINT1616( + VL53L0_MAKEUINT16(localBuffer[7], localBuffer[6])); + /* peak_signal_count_rate_rtn_mcps */ + pRangingMeasurementData->SignalRateRtnMegaCps = SignalRate; + + AmbientRate = VL53L0_MAKEUINT16(localBuffer[9], localBuffer[8]); + pRangingMeasurementData->AmbientRateRtnMegaCps = + VL53L0_FIXPOINT97TOFIXPOINT1616(AmbientRate); + + EffectiveSpadRtnCount = VL53L0_MAKEUINT16(localBuffer[3], + localBuffer[2]); + /* EffectiveSpadRtnCount is 8.8 format */ + pRangingMeasurementData->EffectiveSpadRtnCount = + EffectiveSpadRtnCount; + + DeviceRangeStatus = localBuffer[0]; + + /* Get Linearity Corrective Gain */ + LinearityCorrectiveGain = PALDevDataGet(Dev, + LinearityCorrectiveGain); + + /* Get ranging configuration */ + RangeFractionalEnable = PALDevDataGet(Dev, + RangeFractionalEnable); + + if (LinearityCorrectiveGain != 1000) { + + tmpuint16 = (uint16_t)((LinearityCorrectiveGain + * tmpuint16 + 500) / 1000); + + /* Implement Xtalk */ + VL53L0_GETPARAMETERFIELD(Dev, + XTalkCompensationRateMegaCps, + XTalkCompensationRateMegaCps); + VL53L0_GETPARAMETERFIELD(Dev, XTalkCompensationEnable, + XTalkCompensationEnable); + + if (XTalkCompensationEnable) { + + if ((SignalRate + - ((XTalkCompensationRateMegaCps + * EffectiveSpadRtnCount) >> 8)) + <= 0) { + if (RangeFractionalEnable) + XtalkRangeMilliMeter = 8888; + else + XtalkRangeMilliMeter = 8888 + << 2; + } else { + XtalkRangeMilliMeter = + (tmpuint16 * SignalRate) + / (SignalRate + - ((XTalkCompensationRateMegaCps + * EffectiveSpadRtnCount) + >> 8)); + } + + tmpuint16 = XtalkRangeMilliMeter; + } + + } + + if (RangeFractionalEnable) { + pRangingMeasurementData->RangeMilliMeter = + (uint16_t)((tmpuint16) >> 2); + pRangingMeasurementData->RangeFractionalPart = + (uint8_t)((tmpuint16 & 0x03) << 6); + } else { + pRangingMeasurementData->RangeMilliMeter = tmpuint16; + pRangingMeasurementData->RangeFractionalPart = 0; + } + + /* + * For a standard definition of RangeStatus, this should + * return 0 in case of good result after a ranging + * The range status depends on the device so call a device + * specific function to obtain the right Status. + */ + Status |= VL53L0_get_pal_range_status(Dev, DeviceRangeStatus, + SignalRate, EffectiveSpadRtnCount, + pRangingMeasurementData, &PalRangeStatus); + + if (Status == VL53L0_ERROR_NONE) + pRangingMeasurementData->RangeStatus = PalRangeStatus; + + } + + if (Status == VL53L0_ERROR_NONE) { + /* Copy last read data into Dev buffer */ + LastRangeDataBuffer = PALDevDataGet(Dev, LastRangeMeasure); + + LastRangeDataBuffer.RangeMilliMeter = + pRangingMeasurementData->RangeMilliMeter; + LastRangeDataBuffer.RangeFractionalPart = + pRangingMeasurementData->RangeFractionalPart; + LastRangeDataBuffer.RangeDMaxMilliMeter = + pRangingMeasurementData->RangeDMaxMilliMeter; + LastRangeDataBuffer.MeasurementTimeUsec = + pRangingMeasurementData->MeasurementTimeUsec; + LastRangeDataBuffer.SignalRateRtnMegaCps = + pRangingMeasurementData->SignalRateRtnMegaCps; + LastRangeDataBuffer.AmbientRateRtnMegaCps = + pRangingMeasurementData->AmbientRateRtnMegaCps; + LastRangeDataBuffer.EffectiveSpadRtnCount = + pRangingMeasurementData->EffectiveSpadRtnCount; + LastRangeDataBuffer.RangeStatus = + pRangingMeasurementData->RangeStatus; + + PALDevDataSet(Dev, LastRangeMeasure, LastRangeDataBuffer); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetMeasurementRefSignal(VL53L0_DEV Dev, + FixPoint1616_t *pMeasurementRefSignal) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t SignalRefClipLimitCheckEnable = 0; + + LOG_FUNCTION_START(""); + + Status = VL53L0_GetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, + &SignalRefClipLimitCheckEnable); + if (SignalRefClipLimitCheckEnable != 0) + *pMeasurementRefSignal = PALDevDataGet(Dev, LastSignalRefMcps); + else + Status = VL53L0_ERROR_INVALID_COMMAND; + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L0_Error VL53L0_GetHistogramMeasurementData(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_PerformSingleRangingMeasurement(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + /* This function will do a complete single ranging + * Here we fix the mode! + */ + Status = VL53L0_SetDeviceMode(Dev, VL53L0_DEVICEMODE_SINGLE_RANGING); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_PerformSingleMeasurement(Dev); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetRangingMeasurementData(Dev, + pRangingMeasurementData); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_ClearInterruptMask(Dev, 0); + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetNumberOfROIZones(VL53L0_DEV Dev, + uint8_t NumberOfROIZones) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (NumberOfROIZones != 1) + Status = VL53L0_ERROR_INVALID_PARAMS; + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetNumberOfROIZones(VL53L0_DEV Dev, + uint8_t *pNumberOfROIZones) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pNumberOfROIZones = 1; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetMaxNumberOfROIZones(VL53L0_DEV Dev, + uint8_t *pMaxNumberOfROIZones) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pMaxNumberOfROIZones = 1; + + LOG_FUNCTION_END(Status); + return Status; +} + +/* End Group PAL Measurement Functions */ + +VL53L0_Error VL53L0_SetGpioConfig(VL53L0_DEV Dev, uint8_t Pin, + VL53L0_DeviceModes DeviceMode, VL53L0_GpioFunctionality Functionality, + VL53L0_InterruptPolarity Polarity) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t data; + + LOG_FUNCTION_START(""); + + if (Pin != 0) { + Status = VL53L0_ERROR_GPIO_NOT_EXISTING; + } else if (DeviceMode == VL53L0_DEVICEMODE_GPIO_DRIVE) { + if (Polarity == VL53L0_INTERRUPTPOLARITY_LOW) + data = 0x10; + else + data = 1; + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_GPIO_HV_MUX_ACTIVE_HIGH, data); + + } else if (DeviceMode == VL53L0_DEVICEMODE_GPIO_OSC) { + + Status |= VL53L0_WrByte(Dev, 0xff, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x00); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x00); + Status |= VL53L0_WrByte(Dev, 0x80, 0x01); + Status |= VL53L0_WrByte(Dev, 0x85, 0x02); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x04); + Status |= VL53L0_WrByte(Dev, 0xcd, 0x00); + Status |= VL53L0_WrByte(Dev, 0xcc, 0x11); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x07); + Status |= VL53L0_WrByte(Dev, 0xbe, 0x00); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x06); + Status |= VL53L0_WrByte(Dev, 0xcc, 0x09); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x00); + Status |= VL53L0_WrByte(Dev, 0xff, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x00); + + } else { + + if (Status == VL53L0_ERROR_NONE) { + switch (Functionality) { + case VL53L0_GPIOFUNCTIONALITY_OFF: + data = 0x00; + break; + case VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW: + data = 0x01; + break; + case VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH: + data = 0x02; + break; + case VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT: + data = 0x03; + break; + case VL53L0_GPIOFUNCTIONALITY_NEW_MEASURE_READY: + data = 0x04; + break; + default: + Status = + VL53L0_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED; + } + } + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_SYSTEM_INTERRUPT_CONFIG_GPIO, data); + + if (Status == VL53L0_ERROR_NONE) { + if (Polarity == VL53L0_INTERRUPTPOLARITY_LOW) + data = 0; + else + data = (uint8_t)(1 << 4); + + Status = VL53L0_UpdateByte(Dev, + VL53L0_REG_GPIO_HV_MUX_ACTIVE_HIGH, 0xEF, data); + } + + if (Status == VL53L0_ERROR_NONE) + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + Pin0GpioFunctionality, Functionality); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_ClearInterruptMask(Dev, 0); + + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetGpioConfig(VL53L0_DEV Dev, uint8_t Pin, + VL53L0_DeviceModes *pDeviceMode, + VL53L0_GpioFunctionality *pFunctionality, + VL53L0_InterruptPolarity * pPolarity) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_GpioFunctionality GpioFunctionality; + uint8_t data; + + LOG_FUNCTION_START(""); + + /* pDeviceMode not managed by Ewok it return the current mode */ + + Status = VL53L0_GetDeviceMode(Dev, pDeviceMode); + + if (Status == VL53L0_ERROR_NONE) { + if (Pin != 0) { + Status = VL53L0_ERROR_GPIO_NOT_EXISTING; + } else { + Status = VL53L0_RdByte(Dev, + VL53L0_REG_SYSTEM_INTERRUPT_CONFIG_GPIO, &data); + } + } + + if (Status == VL53L0_ERROR_NONE) { + switch (data & 0x07) { + case 0x00: + GpioFunctionality = VL53L0_GPIOFUNCTIONALITY_OFF; + break; + case 0x01: + GpioFunctionality = + VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_LOW; + break; + case 0x02: + GpioFunctionality = + VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_HIGH; + break; + case 0x03: + GpioFunctionality = + VL53L0_GPIOFUNCTIONALITY_THRESHOLD_CROSSED_OUT; + break; + case 0x04: + GpioFunctionality = + VL53L0_GPIOFUNCTIONALITY_NEW_MEASURE_READY; + break; + default: + Status = VL53L0_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED; + } + } + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_RdByte(Dev, VL53L0_REG_GPIO_HV_MUX_ACTIVE_HIGH, + &data); + + if (Status == VL53L0_ERROR_NONE) { + if ((data & (uint8_t)(1 << 4)) == 0) + *pPolarity = VL53L0_INTERRUPTPOLARITY_LOW; + else + *pPolarity = VL53L0_INTERRUPTPOLARITY_HIGH; + } + + if (Status == VL53L0_ERROR_NONE) { + *pFunctionality = GpioFunctionality; + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, Pin0GpioFunctionality, + GpioFunctionality); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetInterruptThresholds(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode, FixPoint1616_t ThresholdLow, + FixPoint1616_t ThresholdHigh) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t Threshold16; + + LOG_FUNCTION_START(""); + + /* no dependency on DeviceMode for Ewok */ + /* Need to divide by 2 because the FW will apply a x2 */ + Threshold16 = (uint16_t)((ThresholdLow >> 17) & 0x00fff); + Status = VL53L0_WrWord(Dev, VL53L0_REG_SYSTEM_THRESH_LOW, Threshold16); + + if (Status == VL53L0_ERROR_NONE) { + /* Need to divide by 2 because the FW will apply a x2 */ + Threshold16 = (uint16_t)((ThresholdHigh >> 17) & 0x00fff); + Status = VL53L0_WrWord(Dev, VL53L0_REG_SYSTEM_THRESH_HIGH, + Threshold16); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetInterruptThresholds(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode, FixPoint1616_t *pThresholdLow, + FixPoint1616_t *pThresholdHigh) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t Threshold16; + + LOG_FUNCTION_START(""); + + /* no dependency on DeviceMode for Ewok */ + + Status = VL53L0_RdWord(Dev, VL53L0_REG_SYSTEM_THRESH_LOW, &Threshold16); + /* Need to multiply by 2 because the FW will apply a x2 */ + *pThresholdLow = (FixPoint1616_t)((0x00fff & Threshold16) << 17); + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdWord(Dev, VL53L0_REG_SYSTEM_THRESH_HIGH, + &Threshold16); + /* Need to multiply by 2 because the FW will apply a x2 */ + *pThresholdHigh = + (FixPoint1616_t)((0x00fff & Threshold16) << 17); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetStopCompletedStatus(VL53L0_DEV Dev, + uint32_t *pStopStatus) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte = 0; + + LOG_FUNCTION_START(""); + + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_RdByte(Dev, 0x04, &Byte); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, 0xFF, 0x0); + + *pStopStatus = Byte; + + if (Byte == 0) { + Status = VL53L0_WrByte(Dev, 0x80, 0x01); + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status = VL53L0_WrByte(Dev, 0x00, 0x00); + Status = VL53L0_WrByte(Dev, 0x91, + PALDevDataGet(Dev, StopVariable)); + Status = VL53L0_WrByte(Dev, 0x00, 0x01); + Status = VL53L0_WrByte(Dev, 0xFF, 0x00); + Status = VL53L0_WrByte(Dev, 0x80, 0x00); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +/* Group PAL Interrupt Functions */ +VL53L0_Error VL53L0_ClearInterruptMask(VL53L0_DEV Dev, uint32_t InterruptMask) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t LoopCount; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + /* clear bit 0 range interrupt, bit 1 error interrupt */ + LoopCount = 0; + do { + Status = VL53L0_WrByte(Dev, + VL53L0_REG_SYSTEM_INTERRUPT_CLEAR, 0x01); + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_SYSTEM_INTERRUPT_CLEAR, 0x00); + Status |= VL53L0_RdByte(Dev, + VL53L0_REG_RESULT_INTERRUPT_STATUS, &Byte); + LoopCount++; + } while (((Byte & 0x07) != 0x00) + && (LoopCount < 3) + && (Status == VL53L0_ERROR_NONE)); + + + if (LoopCount >= 3) + Status = VL53L0_ERROR_INTERRUPT_NOT_CLEARED; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetInterruptMaskStatus(VL53L0_DEV Dev, + uint32_t *pInterruptMaskStatus) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + Status = VL53L0_RdByte(Dev, VL53L0_REG_RESULT_INTERRUPT_STATUS, &Byte); + *pInterruptMaskStatus = Byte & 0x07; + + if (Byte & 0x18) + Status = VL53L0_ERROR_RANGE_ERROR; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_EnableInterruptMask(VL53L0_DEV Dev, uint32_t InterruptMask) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + /* not implemented for VL53L0 */ + + LOG_FUNCTION_END(Status); + return Status; +} + +/* End Group PAL Interrupt Functions */ + +/* Group SPAD functions */ + +VL53L0_Error VL53L0_SetSpadAmbientDamperThreshold(VL53L0_DEV Dev, + uint16_t SpadAmbientDamperThreshold) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrWord(Dev, 0x40, SpadAmbientDamperThreshold); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetSpadAmbientDamperThreshold(VL53L0_DEV Dev, + uint16_t *pSpadAmbientDamperThreshold) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_RdWord(Dev, 0x40, pSpadAmbientDamperThreshold); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_SetSpadAmbientDamperFactor(VL53L0_DEV Dev, + uint16_t SpadAmbientDamperFactor) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + Byte = (uint8_t)(SpadAmbientDamperFactor & 0x00FF); + + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x42, Byte); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_GetSpadAmbientDamperFactor(VL53L0_DEV Dev, + uint16_t *pSpadAmbientDamperFactor) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t Byte; + + LOG_FUNCTION_START(""); + + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_RdByte(Dev, 0x42, &Byte); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + *pSpadAmbientDamperFactor = (uint16_t)Byte; + + LOG_FUNCTION_END(Status); + return Status; +} + +/* END Group SPAD functions */ + +/***************************************************************************** + * Internal functions + *****************************************************************************/ + +VL53L0_Error VL53L0_SetReferenceSpads(VL53L0_DEV Dev, uint32_t count, + uint8_t isApertureSpads) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_set_reference_spads(Dev, count, isApertureSpads); + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L0_Error VL53L0_GetReferenceSpads(VL53L0_DEV Dev, uint32_t *pSpadCount, + uint8_t *pIsApertureSpads) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_reference_spads(Dev, pSpadCount, pIsApertureSpads); + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L0_Error VL53L0_PerformRefSpadManagement(VL53L0_DEV Dev, + uint32_t *refSpadCount, uint8_t *isApertureSpads) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L0_perform_ref_spad_management(Dev, refSpadCount, + isApertureSpads); + + LOG_FUNCTION_END(Status); + + return Status; +} diff --git a/drivers/input/misc/vl53L0/src/vl53l0_api_calibration.c b/drivers/input/misc/vl53L0/src/vl53l0_api_calibration.c new file mode 100644 index 000000000000..8f4d0ccf882a --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l0_api_calibration.c @@ -0,0 +1,1300 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. + All rights reserved. + + 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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND + NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. + IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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 "vl53l0_api.h" +#include "vl53l0_api_core.h" +#include "vl53l0_api_calibration.h" + +#ifndef __KERNEL__ +#include +#endif + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__) + +#define REF_ARRAY_SPAD_0 0 +#define REF_ARRAY_SPAD_5 5 +#define REF_ARRAY_SPAD_10 10 + +uint32_t refArrayQuadrants[4] = {REF_ARRAY_SPAD_10, REF_ARRAY_SPAD_5, + REF_ARRAY_SPAD_0, REF_ARRAY_SPAD_5 }; + +VL53L0_Error VL53L0_perform_xtalk_calibration(VL53L0_DEV Dev, + FixPoint1616_t XTalkCalDistance, + FixPoint1616_t *pXTalkCompensationRateMegaCps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t sum_ranging = 0; + uint16_t sum_spads = 0; + FixPoint1616_t sum_signalRate = 0; + FixPoint1616_t total_count = 0; + uint8_t xtalk_meas = 0; + VL53L0_RangingMeasurementData_t RangingMeasurementData; + FixPoint1616_t xTalkStoredMeanSignalRate; + FixPoint1616_t xTalkStoredMeanRange; + FixPoint1616_t xTalkStoredMeanRtnSpads; + uint32_t signalXTalkTotalPerSpad; + uint32_t xTalkStoredMeanRtnSpadsAsInt; + uint32_t xTalkCalDistanceAsInt; + FixPoint1616_t XTalkCompensationRateMegaCps; + + if (XTalkCalDistance <= 0) + Status = VL53L0_ERROR_INVALID_PARAMS; + + /* Disable the XTalk compensation */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetXTalkCompensationEnable(Dev, 0); + + /* Disable the RIT */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_SetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, 0); + } + + /* Perform 12 measurements and compute the averages */ + if (Status == VL53L0_ERROR_NONE) { + sum_ranging = 0; + sum_spads = 0; + sum_signalRate = 0; + total_count = 0; + for (xtalk_meas = 0; xtalk_meas < 12; xtalk_meas++) { + Status = VL53L0_PerformSingleRangingMeasurement(Dev, + &RangingMeasurementData); + + if (Status != VL53L0_ERROR_NONE) + break; + + /* The range is valid when RangeStatus = 0 */ + if (RangingMeasurementData.RangeStatus == 0) { + sum_ranging = sum_ranging + + RangingMeasurementData.RangeMilliMeter; + sum_signalRate = sum_signalRate + + RangingMeasurementData.SignalRateRtnMegaCps; + sum_spads = sum_spads + + RangingMeasurementData.EffectiveSpadRtnCount + / 256; + total_count = total_count + 1; + } + } + + /* no valid values found */ + if (total_count == 0) + Status = VL53L0_ERROR_RANGE_ERROR; + + } + + + if (Status == VL53L0_ERROR_NONE) { + /* FixPoint1616_t / uint16_t = FixPoint1616_t */ + xTalkStoredMeanSignalRate = sum_signalRate / total_count; + xTalkStoredMeanRange = (FixPoint1616_t)((uint32_t)( + sum_ranging << 16) / total_count); + xTalkStoredMeanRtnSpads = (FixPoint1616_t)((uint32_t)( + sum_spads << 16) / total_count); + + /* Round Mean Spads to Whole Number. + * Typically the calculated mean SPAD count is a whole number + * or very close to a whole + * number, therefore any truncation will not result in a + * significant loss in accuracy. + * Also, for a grey target at a typical distance of around + * 400mm, around 220 SPADs will + * be enabled, therefore, any truncation will result in a loss + * of accuracy of less than + * 0.5%. + */ + xTalkStoredMeanRtnSpadsAsInt = (xTalkStoredMeanRtnSpads + + 0x8000) >> 16; + + /* Round Cal Distance to Whole Number. + * Note that the cal distance is in mm, therefore no resolution + * is lost. + */ + xTalkCalDistanceAsInt = (XTalkCalDistance + 0x8000) >> 16; + + if (xTalkStoredMeanRtnSpadsAsInt == 0 || + xTalkCalDistanceAsInt == 0 || + xTalkStoredMeanRange >= XTalkCalDistance) { + XTalkCompensationRateMegaCps = 0; + } else { + /* Round Cal Distance to Whole Number. + * Note that the cal distance is in mm, therefore no + * resolution is lost. + */ + xTalkCalDistanceAsInt = (XTalkCalDistance + + 0x8000) >> 16; + + /* Apply division by mean spad count early in the + * calculation to keep the numbers small. + * This ensures we can maintain a 32bit calculation. + * Fixed1616 / int := Fixed1616 + */ + signalXTalkTotalPerSpad = (xTalkStoredMeanSignalRate) / + xTalkStoredMeanRtnSpadsAsInt; + + /* Complete the calculation for total Signal XTalk per + * SPAD + * Fixed1616 * (Fixed1616 - Fixed1616/int) := + * (2^16 * Fixed1616) + */ + signalXTalkTotalPerSpad *= ((1 << 16) - + (xTalkStoredMeanRange / xTalkCalDistanceAsInt)); + + /* Round from 2^16 * Fixed1616, to Fixed1616. */ + XTalkCompensationRateMegaCps = (signalXTalkTotalPerSpad + + 0x8000) >> 16; + } + + *pXTalkCompensationRateMegaCps = XTalkCompensationRateMegaCps; + + /* Enable the XTalk compensation */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetXTalkCompensationEnable(Dev, 1); + + /* Enable the XTalk compensation */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetXTalkCompensationRateMegaCps(Dev, + XTalkCompensationRateMegaCps); + + } + + return Status; +} + +VL53L0_Error VL53L0_perform_offset_calibration(VL53L0_DEV Dev, + FixPoint1616_t CalDistanceMilliMeter, + int32_t *pOffsetMicroMeter) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t sum_ranging = 0; + FixPoint1616_t total_count = 0; + VL53L0_RangingMeasurementData_t RangingMeasurementData; + FixPoint1616_t StoredMeanRange; + uint32_t StoredMeanRangeAsInt; + uint32_t CalDistanceAsInt_mm; + uint8_t SequenceStepEnabled; + int meas = 0; + uint32_t oldMeasurementTimingBudgetMicroSeconds; + uint32_t MeasurementTimingBudgetMicroSeconds = 100000; + + if (CalDistanceMilliMeter <= 0) + Status = VL53L0_ERROR_INVALID_PARAMS; + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetOffsetCalibrationDataMicroMeter(Dev, 0); + + + /* Get the value of the TCC */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_TCC, &SequenceStepEnabled); + + + /* Disable the TCC */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_TCC, 0); + + + /* Disable the RIT */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, 0); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetMeasurementTimingBudgetMicroSeconds(Dev, + &oldMeasurementTimingBudgetMicroSeconds); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetMeasurementTimingBudgetMicroSeconds(Dev, + MeasurementTimingBudgetMicroSeconds); + + /* Perform 3 measurements and compute the averages */ + if (Status == VL53L0_ERROR_NONE) { + sum_ranging = 0; + total_count = 0; + for (meas = 0; meas < 3; meas++) { + Status = VL53L0_PerformSingleRangingMeasurement(Dev, + &RangingMeasurementData); + + if (Status != VL53L0_ERROR_NONE) + break; + + /* The range is valid when RangeStatus = 0 */ + if (RangingMeasurementData.RangeStatus == 0) { + sum_ranging = sum_ranging + + RangingMeasurementData.RangeMilliMeter; + total_count = total_count + 1; + } + } + + /* no valid values found */ + if (total_count == 0) + Status = VL53L0_ERROR_RANGE_ERROR; + } + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_SetMeasurementTimingBudgetMicroSeconds(Dev, + oldMeasurementTimingBudgetMicroSeconds); + + if (Status == VL53L0_ERROR_NONE) { + /* FixPoint1616_t / uint16_t = FixPoint1616_t */ + StoredMeanRange = (FixPoint1616_t)((uint32_t)(sum_ranging << 16) + / total_count); + + StoredMeanRangeAsInt = (StoredMeanRange + 0x8000) >> 16; + + /* Round Cal Distance to Whole Number. + * Note that the cal distance is in mm, therefore no resolution + * is lost. + */ + CalDistanceAsInt_mm = (CalDistanceMilliMeter + 0x8000) >> 16; + + *pOffsetMicroMeter = (CalDistanceAsInt_mm - + StoredMeanRangeAsInt) * 1000; + + /* Apply the calculated offset */ + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETPARAMETERFIELD(Dev, RangeOffsetMicroMeters, + *pOffsetMicroMeter); + Status = VL53L0_SetOffsetCalibrationDataMicroMeter(Dev, + *pOffsetMicroMeter); + } + + } + + /* Restore the TCC */ + if (Status == VL53L0_ERROR_NONE) { + if (SequenceStepEnabled != 0) + Status = VL53L0_SetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_TCC, 1); + } + + return Status; +} + + +VL53L0_Error VL53L0_set_offset_calibration_data_micro_meter(VL53L0_DEV Dev, + int32_t OffsetCalibrationDataMicroMeter) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t cMaxOffsetMicroMeter = 511000; + int32_t cMinOffsetMicroMeter = -512000; + int16_t cOffsetRange = 4096; + uint32_t encodedOffsetVal; + + LOG_FUNCTION_START(""); + + if (OffsetCalibrationDataMicroMeter > cMaxOffsetMicroMeter) + OffsetCalibrationDataMicroMeter = cMaxOffsetMicroMeter; + else if (OffsetCalibrationDataMicroMeter < cMinOffsetMicroMeter) + OffsetCalibrationDataMicroMeter = cMinOffsetMicroMeter; + + /* The offset register is 10.2 format and units are mm + * therefore conversion is applied by a division of + * 250. + */ + if (OffsetCalibrationDataMicroMeter >= 0) { + encodedOffsetVal = + OffsetCalibrationDataMicroMeter/250; + } else { + encodedOffsetVal = + cOffsetRange + + OffsetCalibrationDataMicroMeter/250; + } + + Status = VL53L0_WrWord(Dev, + VL53L0_REG_ALGO_PART_TO_PART_RANGE_OFFSET_MM, + encodedOffsetVal); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_get_offset_calibration_data_micro_meter(VL53L0_DEV Dev, + int32_t *pOffsetCalibrationDataMicroMeter) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint16_t RangeOffsetRegister; + int16_t cMaxOffset = 2047; + int16_t cOffsetRange = 4096; + + /* Note that offset has 10.2 format */ + + Status = VL53L0_RdWord(Dev, + VL53L0_REG_ALGO_PART_TO_PART_RANGE_OFFSET_MM, + &RangeOffsetRegister); + + if (Status == VL53L0_ERROR_NONE) { + RangeOffsetRegister = (RangeOffsetRegister & 0x0fff); + + /* Apply 12 bit 2's compliment conversion */ + if (RangeOffsetRegister > cMaxOffset) + *pOffsetCalibrationDataMicroMeter = + (int16_t)(RangeOffsetRegister - cOffsetRange) + * 250; + else + *pOffsetCalibrationDataMicroMeter = + (int16_t)RangeOffsetRegister * 250; + + } + + return Status; +} + + +VL53L0_Error VL53L0_apply_offset_adjustment(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t CorrectedOffsetMicroMeters; + int32_t CurrentOffsetMicroMeters; + + /* if we run on this function we can read all the NVM info + * used by the API + */ + Status = VL53L0_get_info_from_device(Dev, 7); + + /* Read back current device offset */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_GetOffsetCalibrationDataMicroMeter(Dev, + &CurrentOffsetMicroMeters); + } + + /* Apply Offset Adjustment derived from 400mm measurements */ + if (Status == VL53L0_ERROR_NONE) { + + /* Store initial device offset */ + PALDevDataSet(Dev, Part2PartOffsetNVMMicroMeter, + CurrentOffsetMicroMeters); + + CorrectedOffsetMicroMeters = CurrentOffsetMicroMeters + + (int32_t)PALDevDataGet(Dev, + Part2PartOffsetAdjustmentNVMMicroMeter); + + Status = VL53L0_SetOffsetCalibrationDataMicroMeter(Dev, + CorrectedOffsetMicroMeters); + + /* store current, adjusted offset */ + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETPARAMETERFIELD(Dev, RangeOffsetMicroMeters, + CorrectedOffsetMicroMeters); + } + } + + return Status; +} + +void get_next_good_spad(uint8_t goodSpadArray[], uint32_t size, + uint32_t curr, int32_t *next) +{ + uint32_t startIndex; + uint32_t fineOffset; + uint32_t cSpadsPerByte = 8; + uint32_t coarseIndex; + uint32_t fineIndex; + uint8_t dataByte; + uint8_t success = 0; + + /* + * Starting with the current good spad, loop through the array to find + * the next. i.e. the next bit set in the sequence. + * + * The coarse index is the byte index of the array and the fine index is + * the index of the bit within each byte. + */ + + *next = -1; + + startIndex = curr / cSpadsPerByte; + fineOffset = curr % cSpadsPerByte; + + for (coarseIndex = startIndex; ((coarseIndex < size) && !success); + coarseIndex++) { + fineIndex = 0; + dataByte = goodSpadArray[coarseIndex]; + + if (coarseIndex == startIndex) { + /* locate the bit position of the provided current + * spad bit before iterating + */ + dataByte >>= fineOffset; + fineIndex = fineOffset; + } + + while (fineIndex < cSpadsPerByte) { + if ((dataByte & 0x1) == 1) { + success = 1; + *next = coarseIndex * cSpadsPerByte + fineIndex; + break; + } + dataByte >>= 1; + fineIndex++; + } + } +} + + +uint8_t is_aperture(uint32_t spadIndex) +{ + /* + * This function reports if a given spad index is an aperture SPAD by + * deriving the quadrant. + */ + uint32_t quadrant; + uint8_t isAperture = 1; + + quadrant = spadIndex >> 6; + if (refArrayQuadrants[quadrant] == REF_ARRAY_SPAD_0) + isAperture = 0; + + return isAperture; +} + + +VL53L0_Error enable_spad_bit(uint8_t spadArray[], uint32_t size, + uint32_t spadIndex) +{ + VL53L0_Error status = VL53L0_ERROR_NONE; + uint32_t cSpadsPerByte = 8; + uint32_t coarseIndex; + uint32_t fineIndex; + + coarseIndex = spadIndex / cSpadsPerByte; + fineIndex = spadIndex % cSpadsPerByte; + if (coarseIndex >= size) + status = VL53L0_ERROR_REF_SPAD_INIT; + else + spadArray[coarseIndex] |= (1 << fineIndex); + + return status; +} + +VL53L0_Error count_enabled_spads(uint8_t spadArray[], + uint32_t byteCount, uint32_t maxSpads, + uint32_t *pTotalSpadsEnabled, uint8_t *pIsAperture) +{ + VL53L0_Error status = VL53L0_ERROR_NONE; + uint32_t cSpadsPerByte = 8; + uint32_t lastByte; + uint32_t lastBit; + uint32_t byteIndex = 0; + uint32_t bitIndex = 0; + uint8_t tempByte; + uint8_t spadTypeIdentified = 0; + + /* The entire array will not be used for spads, therefore the last + * byte and last bit is determined from the max spads value. + */ + + lastByte = maxSpads / cSpadsPerByte; + lastBit = maxSpads % cSpadsPerByte; + + /* Check that the max spads value does not exceed the array bounds. */ + if (lastByte >= byteCount) + status = VL53L0_ERROR_REF_SPAD_INIT; + + *pTotalSpadsEnabled = 0; + + /* Count the bits enabled in the whole bytes */ + for (byteIndex = 0; byteIndex <= (lastByte - 1); byteIndex++) { + tempByte = spadArray[byteIndex]; + + for (bitIndex = 0; bitIndex <= cSpadsPerByte; bitIndex++) { + if ((tempByte & 0x01) == 1) { + (*pTotalSpadsEnabled)++; + + if (!spadTypeIdentified) { + *pIsAperture = 1; + if ((byteIndex < 2) && (bitIndex < 4)) + *pIsAperture = 0; + spadTypeIdentified = 1; + } + } + tempByte >>= 1; + } + } + + /* Count the number of bits enabled in the last byte accounting + * for the fact that not all bits in the byte may be used. + */ + tempByte = spadArray[lastByte]; + + for (bitIndex = 0; bitIndex <= lastBit; bitIndex++) { + if ((tempByte & 0x01) == 1) + (*pTotalSpadsEnabled)++; + } + + return status; +} + +VL53L0_Error set_ref_spad_map(VL53L0_DEV Dev, uint8_t *refSpadArray) +{ + VL53L0_Error status = VL53L0_WriteMulti(Dev, + VL53L0_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_0, + refSpadArray, 6); + return status; +} + +VL53L0_Error get_ref_spad_map(VL53L0_DEV Dev, uint8_t *refSpadArray) +{ + VL53L0_Error status = VL53L0_ReadMulti(Dev, + VL53L0_REG_GLOBAL_CONFIG_SPAD_ENABLES_REF_0, + refSpadArray, + 6); + return status; +} + +VL53L0_Error enable_ref_spads(VL53L0_DEV Dev, + uint8_t apertureSpads, + uint8_t goodSpadArray[], + uint8_t spadArray[], + uint32_t size, + uint32_t start, + uint32_t offset, + uint32_t spadCount, + uint32_t *lastSpad) +{ + VL53L0_Error status = VL53L0_ERROR_NONE; + uint32_t index; + uint32_t i; + int32_t nextGoodSpad = offset; + uint32_t currentSpad; + uint8_t checkSpadArray[6]; + + /* + * This function takes in a spad array which may or may not have SPADS + * already enabled and appends from a given offset a requested number + * of new SPAD enables. The 'good spad map' is applied to + * determine the next SPADs to enable. + * + * This function applies to only aperture or only non-aperture spads. + * Checks are performed to ensure this. + */ + + currentSpad = offset; + for (index = 0; index < spadCount; index++) { + get_next_good_spad(goodSpadArray, size, currentSpad, + &nextGoodSpad); + + if (nextGoodSpad == -1) { + status = VL53L0_ERROR_REF_SPAD_INIT; + break; + } + + /* Confirm that the next good SPAD is non-aperture */ + if (is_aperture(start + nextGoodSpad) != apertureSpads) { + /* if we can't get the required number of good aperture + * spads from the current quadrant then this is an error + */ + status = VL53L0_ERROR_REF_SPAD_INIT; + break; + } + currentSpad = (uint32_t)nextGoodSpad; + enable_spad_bit(spadArray, size, currentSpad); + currentSpad++; + } + *lastSpad = currentSpad; + + if (status == VL53L0_ERROR_NONE) + status = set_ref_spad_map(Dev, spadArray); + + + if (status == VL53L0_ERROR_NONE) { + status = get_ref_spad_map(Dev, checkSpadArray); + + i = 0; + + /* Compare spad maps. If not equal report error. */ + while (i < size) { + if (spadArray[i] != checkSpadArray[i]) { + status = VL53L0_ERROR_REF_SPAD_INIT; + break; + } + i++; + } + } + return status; +} + + +VL53L0_Error perform_ref_signal_measurement(VL53L0_DEV Dev, + uint16_t *refSignalRate) +{ + VL53L0_Error status = VL53L0_ERROR_NONE; + VL53L0_RangingMeasurementData_t rangingMeasurementData; + + uint8_t SequenceConfig = 0; + + /* store the value of the sequence config, + * this will be reset before the end of the function + */ + + SequenceConfig = PALDevDataGet(Dev, SequenceConfig); + + /* + * This function performs a reference signal rate measurement. + */ + if (status == VL53L0_ERROR_NONE) + status = VL53L0_WrByte(Dev, + VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, 0xC0); + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_PerformSingleRangingMeasurement(Dev, + &rangingMeasurementData); + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_WrByte(Dev, 0xFF, 0x01); + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_RdWord(Dev, + VL53L0_REG_RESULT_PEAK_SIGNAL_RATE_REF, + refSignalRate); + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_WrByte(Dev, 0xFF, 0x00); + + if (status == VL53L0_ERROR_NONE) { + /* restore the previous Sequence Config */ + status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, + SequenceConfig); + if (status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, SequenceConfig, SequenceConfig); + } + + return status; +} + +VL53L0_Error VL53L0_perform_ref_spad_management(VL53L0_DEV Dev, + uint32_t *refSpadCount, + uint8_t *isApertureSpads) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t lastSpadArray[6]; + uint8_t startSelect = 0xB4; + uint32_t minimumSpadCount = 3; + uint32_t maxSpadCount = 44; + uint32_t currentSpadIndex = 0; + uint32_t lastSpadIndex = 0; + int32_t nextGoodSpad = 0; + uint16_t targetRefRate = 0x0A00; /* 20 MCPS in 9:7 format */ + uint16_t peakSignalRateRef; + uint32_t needAptSpads = 0; + uint32_t index = 0; + uint32_t spadArraySize = 6; + uint32_t signalRateDiff = 0; + uint32_t lastSignalRateDiff = 0; + uint8_t complete = 0; + uint8_t VhvSettings = 0; + uint8_t PhaseCal = 0; + uint32_t refSpadCount_int = 0; + uint8_t isApertureSpads_int = 0; + + /* + * The reference SPAD initialization procedure determines the minimum + * amount of reference spads to be enables to achieve a target reference + * signal rate and should be performed once during initialization. + * + * Either aperture or non-aperture spads are applied but never both. + * Firstly non-aperture spads are set, begining with 5 spads, and + * increased one spad at a time until the closest measurement to the + * target rate is achieved. + * + * If the target rate is exceeded when 5 non-aperture spads are enabled, + * initialization is performed instead with aperture spads. + * + * When setting spads, a 'Good Spad Map' is applied. + * + * This procedure operates within a SPAD window of interest of a maximum + * 44 spads. + * The start point is currently fixed to 180, which lies towards the end + * of the non-aperture quadrant and runs in to the adjacent aperture + * quadrant. + */ + + + targetRefRate = PALDevDataGet(Dev, targetRefRate); + + /* + * Initialize Spad arrays. + * Currently the good spad map is initialised to 'All good'. + * This is a short term implementation. The good spad map will be + * provided as an input. + * Note that there are 6 bytes. Only the first 44 bits will be used to + * represent spads. + */ + for (index = 0; index < spadArraySize; index++) + Dev->Data.SpadData.RefSpadEnables[index] = 0; + + + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_DYNAMIC_SPAD_REF_EN_START_OFFSET, 0x00); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD, 0x2C); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, 0xFF, 0x00); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_GLOBAL_CONFIG_REF_EN_START_SELECT, + startSelect); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_POWER_MANAGEMENT_GO1_POWER_FORCE, 0); + + /* Perform ref calibration */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_perform_ref_calibration(Dev, &VhvSettings, + &PhaseCal, 0); + + if (Status == VL53L0_ERROR_NONE) { + /* Enable Minimum NON-APERTURE Spads */ + currentSpadIndex = 0; + lastSpadIndex = currentSpadIndex; + needAptSpads = 0; + Status = enable_ref_spads(Dev, + needAptSpads, + Dev->Data.SpadData.RefGoodSpadMap, + Dev->Data.SpadData.RefSpadEnables, + spadArraySize, + startSelect, + currentSpadIndex, + minimumSpadCount, + &lastSpadIndex); + } + + if (Status == VL53L0_ERROR_NONE) { + currentSpadIndex = lastSpadIndex; + + Status = perform_ref_signal_measurement(Dev, + &peakSignalRateRef); + if ((Status == VL53L0_ERROR_NONE) && + (peakSignalRateRef > targetRefRate)) { + /* Signal rate measurement too high, + * switch to APERTURE SPADs + */ + + for (index = 0; index < spadArraySize; index++) + Dev->Data.SpadData.RefSpadEnables[index] = 0; + + + /* Increment to the first APERTURE spad */ + while ((is_aperture(startSelect + currentSpadIndex) + == 0) && (currentSpadIndex < maxSpadCount)) { + currentSpadIndex++; + } + + needAptSpads = 1; + + Status = enable_ref_spads(Dev, + needAptSpads, + Dev->Data.SpadData.RefGoodSpadMap, + Dev->Data.SpadData.RefSpadEnables, + spadArraySize, + startSelect, + currentSpadIndex, + minimumSpadCount, + &lastSpadIndex); + + if (Status == VL53L0_ERROR_NONE) { + currentSpadIndex = lastSpadIndex; + Status = perform_ref_signal_measurement(Dev, + &peakSignalRateRef); + + if ((Status == VL53L0_ERROR_NONE) && + (peakSignalRateRef > targetRefRate)) { + /* Signal rate still too high after + * setting the minimum number of + * APERTURE spads. Can do no more + * therefore set the min number of + * aperture spads as the result. + */ + isApertureSpads_int = 1; + refSpadCount_int = minimumSpadCount; + } + } + } else { + needAptSpads = 0; + } + } + + if ((Status == VL53L0_ERROR_NONE) && + (peakSignalRateRef < targetRefRate)) { + /* At this point, the minimum number of either aperture + * or non-aperture spads have been set. Proceed to add + * spads and perform measurements until the target + * reference is reached. + */ + isApertureSpads_int = needAptSpads; + refSpadCount_int = minimumSpadCount; + + memcpy(lastSpadArray, Dev->Data.SpadData.RefSpadEnables, + spadArraySize); + lastSignalRateDiff = abs(peakSignalRateRef - + targetRefRate); + complete = 0; + + while (!complete) { + get_next_good_spad( + Dev->Data.SpadData.RefGoodSpadMap, + spadArraySize, currentSpadIndex, + &nextGoodSpad); + + if (nextGoodSpad == -1) { + Status = VL53L0_ERROR_REF_SPAD_INIT; + break; + } + + /* Cannot combine Aperture and Non-Aperture spads, so + * ensure the current spad is of the correct type. + */ + if (is_aperture((uint32_t)startSelect + nextGoodSpad) != + needAptSpads) { + /* At this point we have enabled the maximum + * number of Aperture spads. + */ + complete = 1; + break; + } + + (refSpadCount_int)++; + + currentSpadIndex = nextGoodSpad; + Status = enable_spad_bit( + Dev->Data.SpadData.RefSpadEnables, + spadArraySize, currentSpadIndex); + + if (Status == VL53L0_ERROR_NONE) { + currentSpadIndex++; + /* Proceed to apply the additional spad and + * perform measurement. + */ + Status = set_ref_spad_map(Dev, + Dev->Data.SpadData.RefSpadEnables); + } + + if (Status != VL53L0_ERROR_NONE) + break; + + Status = perform_ref_signal_measurement(Dev, + &peakSignalRateRef); + + if (Status != VL53L0_ERROR_NONE) + break; + + signalRateDiff = abs(peakSignalRateRef - targetRefRate); + + if (peakSignalRateRef > targetRefRate) { + /* Select the spad map that provides the + * measurement closest to the target rate, + * either above or below it. + */ + if (signalRateDiff > lastSignalRateDiff) { + /* Previous spad map produced a closer + * measurement, so choose this. + */ + Status = set_ref_spad_map(Dev, + lastSpadArray); + memcpy( + Dev->Data.SpadData.RefSpadEnables, + lastSpadArray, spadArraySize); + + (refSpadCount_int)--; + } + complete = 1; + } else { + /* Continue to add spads */ + lastSignalRateDiff = signalRateDiff; + memcpy(lastSpadArray, + Dev->Data.SpadData.RefSpadEnables, + spadArraySize); + } + + } /* while */ + } + + if (Status == VL53L0_ERROR_NONE) { + *refSpadCount = refSpadCount_int; + *isApertureSpads = isApertureSpads_int; + + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, RefSpadsInitialised, 1); + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadCount, (uint8_t)(*refSpadCount)); + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadType, *isApertureSpads); + } + + return Status; +} + +VL53L0_Error VL53L0_set_reference_spads(VL53L0_DEV Dev, + uint32_t count, uint8_t isApertureSpads) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint32_t currentSpadIndex = 0; + uint8_t startSelect = 0xB4; + uint32_t spadArraySize = 6; + uint32_t maxSpadCount = 44; + uint32_t lastSpadIndex; + uint32_t index; + + /* + * This function applies a requested number of reference spads, either + * aperture or + * non-aperture, as requested. + * The good spad map will be applied. + */ + + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_DYNAMIC_SPAD_REF_EN_START_OFFSET, 0x00); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_DYNAMIC_SPAD_NUM_REQUESTED_REF_SPAD, 0x2C); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, 0xFF, 0x00); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_GLOBAL_CONFIG_REF_EN_START_SELECT, + startSelect); + + for (index = 0; index < spadArraySize; index++) + Dev->Data.SpadData.RefSpadEnables[index] = 0; + + if (isApertureSpads) { + /* Increment to the first APERTURE spad */ + while ((is_aperture(startSelect + currentSpadIndex) == 0) && + (currentSpadIndex < maxSpadCount)) { + currentSpadIndex++; + } + } + Status = enable_ref_spads(Dev, + isApertureSpads, + Dev->Data.SpadData.RefGoodSpadMap, + Dev->Data.SpadData.RefSpadEnables, + spadArraySize, + startSelect, + currentSpadIndex, + count, + &lastSpadIndex); + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, RefSpadsInitialised, 1); + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadCount, (uint8_t)(count)); + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadType, isApertureSpads); + } + + return Status; +} + +VL53L0_Error VL53L0_get_reference_spads(VL53L0_DEV Dev, + uint32_t *pSpadCount, uint8_t *pIsApertureSpads) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t refSpadsInitialised; + uint8_t refSpadArray[6]; + uint32_t cMaxSpadCount = 44; + uint32_t cSpadArraySize = 6; + uint32_t spadsEnabled; + uint8_t isApertureSpads = 0; + + refSpadsInitialised = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + RefSpadsInitialised); + + if (refSpadsInitialised == 1) { + + *pSpadCount = (uint32_t)VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadCount); + *pIsApertureSpads = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadType); + } else { + + /* obtain spad info from device.*/ + Status = get_ref_spad_map(Dev, refSpadArray); + + if (Status == VL53L0_ERROR_NONE) { + /* count enabled spads within spad map array and + * determine if Aperture or Non-Aperture. + */ + Status = count_enabled_spads(refSpadArray, + cSpadArraySize, + cMaxSpadCount, + &spadsEnabled, + &isApertureSpads); + + if (Status == VL53L0_ERROR_NONE) { + + *pSpadCount = spadsEnabled; + *pIsApertureSpads = isApertureSpads; + + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + RefSpadsInitialised, 1); + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadCount, + (uint8_t)spadsEnabled); + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadType, isApertureSpads); + } + } + } + + return Status; +} + + +VL53L0_Error VL53L0_perform_single_ref_calibration(VL53L0_DEV Dev, + uint8_t vhv_init_byte) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSRANGE_START, + VL53L0_REG_SYSRANGE_MODE_START_STOP | + vhv_init_byte); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_measurement_poll_for_completion(Dev); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_ClearInterruptMask(Dev, 0); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSRANGE_START, 0x00); + + return Status; +} + + +VL53L0_Error VL53L0_ref_calibration_io(VL53L0_DEV Dev, uint8_t read_not_write, + uint8_t VhvSettings, uint8_t PhaseCal, + uint8_t *pVhvSettings, uint8_t *pPhaseCal, + const uint8_t vhv_enable, const uint8_t phase_enable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t PhaseCalint = 0; + + /* Read VHV from device */ + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x00); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + if (read_not_write) { + if (vhv_enable) + Status |= VL53L0_RdByte(Dev, 0xCB, pVhvSettings); + if (phase_enable) + Status |= VL53L0_RdByte(Dev, 0xEE, &PhaseCalint); + } else { + if (vhv_enable) + Status |= VL53L0_WrByte(Dev, 0xCB, VhvSettings); + if (phase_enable) + Status |= VL53L0_UpdateByte(Dev, 0xEE, 0x80, PhaseCal); + } + + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x01); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + *pPhaseCal = (uint8_t)(PhaseCalint&0xEF); + + return Status; +} + + +VL53L0_Error VL53L0_perform_vhv_calibration(VL53L0_DEV Dev, + uint8_t *pVhvSettings, const uint8_t get_data_enable, + const uint8_t restore_config) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t SequenceConfig = 0; + uint8_t VhvSettings = 0; + uint8_t PhaseCal = 0; + uint8_t PhaseCalInt = 0; + + /* store the value of the sequence config, + * this will be reset before the end of the function + */ + + if (restore_config) + SequenceConfig = PALDevDataGet(Dev, SequenceConfig); + + /* Run VHV */ + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, 0x01); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_perform_single_ref_calibration(Dev, 0x40); + + /* Read VHV from device */ + if ((Status == VL53L0_ERROR_NONE) && (get_data_enable == 1)) { + Status = VL53L0_ref_calibration_io(Dev, 1, + VhvSettings, PhaseCal, /* Not used here */ + pVhvSettings, &PhaseCalInt, + 1, 0); + } else + *pVhvSettings = 0; + + + if ((Status == VL53L0_ERROR_NONE) && restore_config) { + /* restore the previous Sequence Config */ + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, + SequenceConfig); + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, SequenceConfig, SequenceConfig); + + } + + return Status; +} + +VL53L0_Error VL53L0_perform_phase_calibration(VL53L0_DEV Dev, + uint8_t *pPhaseCal, const uint8_t get_data_enable, + const uint8_t restore_config) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t SequenceConfig = 0; + uint8_t VhvSettings = 0; + uint8_t PhaseCal = 0; + uint8_t VhvSettingsint; + + /* store the value of the sequence config, + * this will be reset before the end of the function + */ + + if (restore_config) + SequenceConfig = PALDevDataGet(Dev, SequenceConfig); + + /* Run PhaseCal */ + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, 0x02); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_perform_single_ref_calibration(Dev, 0x0); + + /* Read PhaseCal from device */ + if ((Status == VL53L0_ERROR_NONE) && (get_data_enable == 1)) { + Status = VL53L0_ref_calibration_io(Dev, 1, + VhvSettings, PhaseCal, /* Not used here */ + &VhvSettingsint, pPhaseCal, + 0, 1); + } else + *pPhaseCal = 0; + + + if ((Status == VL53L0_ERROR_NONE) && restore_config) { + /* restore the previous Sequence Config */ + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, + SequenceConfig); + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, SequenceConfig, SequenceConfig); + + } + + return Status; +} + +VL53L0_Error VL53L0_perform_ref_calibration(VL53L0_DEV Dev, + uint8_t *pVhvSettings, uint8_t *pPhaseCal, uint8_t get_data_enable) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t SequenceConfig = 0; + + /* store the value of the sequence config, + * this will be reset before the end of the function + */ + + SequenceConfig = PALDevDataGet(Dev, SequenceConfig); + + /* In the following function we don't save the config to optimize + * writes on device. Config is saved and restored only once. + */ + Status = VL53L0_perform_vhv_calibration( + Dev, pVhvSettings, get_data_enable, 0); + + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_perform_phase_calibration( + Dev, pPhaseCal, get_data_enable, 0); + + + if (Status == VL53L0_ERROR_NONE) { + /* restore the previous Sequence Config */ + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_SEQUENCE_CONFIG, + SequenceConfig); + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, SequenceConfig, SequenceConfig); + + } + + return Status; +} + +VL53L0_Error VL53L0_set_ref_calibration(VL53L0_DEV Dev, + uint8_t VhvSettings, uint8_t PhaseCal) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t pVhvSettings; + uint8_t pPhaseCal; + + Status = VL53L0_ref_calibration_io(Dev, 0, + VhvSettings, PhaseCal, + &pVhvSettings, &pPhaseCal, + 1, 1); + + return Status; +} + +VL53L0_Error VL53L0_get_ref_calibration(VL53L0_DEV Dev, + uint8_t *pVhvSettings, uint8_t *pPhaseCal) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t VhvSettings = 0; + uint8_t PhaseCal = 0; + + Status = VL53L0_ref_calibration_io(Dev, 1, + VhvSettings, PhaseCal, + pVhvSettings, pPhaseCal, + 1, 1); + + return Status; +} diff --git a/drivers/input/misc/vl53L0/src/vl53l0_api_core.c b/drivers/input/misc/vl53L0/src/vl53l0_api_core.c new file mode 100644 index 000000000000..2ccaf975b30e --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l0_api_core.c @@ -0,0 +1,2125 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. + All rights reserved. + + 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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND + NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. + IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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 "vl53l0_api.h" +#include "vl53l0_api_core.h" +#include "vl53l0_api_calibration.h" + + +#ifndef __KERNEL__ +#include +#endif +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__) + +VL53L0_Error VL53L0_reverse_bytes(uint8_t *data, uint32_t size) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t tempData; + uint32_t mirrorIndex; + uint32_t middle = size/2; + uint32_t index; + + for (index = 0; index < middle; index++) { + mirrorIndex = size - index - 1; + tempData = data[index]; + data[index] = data[mirrorIndex]; + data[mirrorIndex] = tempData; + } + return Status; +} + +VL53L0_Error VL53L0_measurement_poll_for_completion(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t NewDataReady = 0; + uint32_t LoopNb; + + LOG_FUNCTION_START(""); + + LoopNb = 0; + + do { + Status = VL53L0_GetMeasurementDataReady(Dev, &NewDataReady); + if (Status != 0) + break; /* the error is set */ + + if (NewDataReady == 1) + break; /* done note that status == 0 */ + + LoopNb++; + if (LoopNb >= VL53L0_DEFAULT_MAX_LOOP) { + Status = VL53L0_ERROR_TIME_OUT; + break; + } + + VL53L0_PollingDelay(Dev); + } while (1); + + LOG_FUNCTION_END(Status); + + return Status; +} + + +uint8_t VL53L0_decode_vcsel_period(uint8_t vcsel_period_reg) +{ + /*! + * Converts the encoded VCSEL period register value into the real + * period in PLL clocks + */ + + uint8_t vcsel_period_pclks = 0; + + vcsel_period_pclks = (vcsel_period_reg + 1) << 1; + + return vcsel_period_pclks; +} + +uint8_t VL53L0_encode_vcsel_period(uint8_t vcsel_period_pclks) +{ + /*! + * Converts the encoded VCSEL period register value into the real period + * in PLL clocks + */ + + uint8_t vcsel_period_reg = 0; + + vcsel_period_reg = (vcsel_period_pclks >> 1) - 1; + + return vcsel_period_reg; +} + + +uint32_t VL53L0_isqrt(uint32_t num) +{ + /* + * Implements an integer square root + * + * From: http://en.wikipedia.org/wiki/Methods_of_computing_square_roots + */ + + uint32_t res = 0; + uint32_t bit = 1 << 30; + /* The second-to-top bit is set: + * 1 << 14 for 16-bits, 1 << 30 for 32 bits + */ + + /* "bit" starts at the highest power of four <= the argument. */ + while (bit > num) + bit >>= 2; + + + while (bit != 0) { + if (num >= res + bit) { + num -= res + bit; + res = (res >> 1) + bit; + } else + res >>= 1; + + bit >>= 2; + } + + return res; +} + + +uint32_t VL53L0_quadrature_sum(uint32_t a, uint32_t b) +{ + /* + * Implements a quadrature sum + * + * rea = sqrt(a^2 + b^2) + * + * Trap overflow case max input value is 65535 (16-bit value) + * as internal calc are 32-bit wide + * + * If overflow then seta output to maximum + */ + uint32_t res = 0; + + if (a > 65535 || b > 65535) + res = 65535; + else + res = VL53L0_isqrt(a * a + b * b); + + return res; +} + + +VL53L0_Error VL53L0_device_read_strobe(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t strobe; + uint32_t LoopNb; + + LOG_FUNCTION_START(""); + + Status |= VL53L0_WrByte(Dev, 0x83, 0x00); + + /* polling + * use timeout to avoid deadlock + */ + if (Status == VL53L0_ERROR_NONE) { + LoopNb = 0; + do { + Status = VL53L0_RdByte(Dev, 0x83, &strobe); + if ((strobe != 0x00) || Status != VL53L0_ERROR_NONE) + break; + + LoopNb = LoopNb + 1; + } while (LoopNb < VL53L0_DEFAULT_MAX_LOOP); + + if (LoopNb >= VL53L0_DEFAULT_MAX_LOOP) + Status = VL53L0_ERROR_TIME_OUT; + + } + + Status |= VL53L0_WrByte(Dev, 0x83, 0x01); + + LOG_FUNCTION_END(Status); + return Status; + +} + +VL53L0_Error VL53L0_get_info_from_device(VL53L0_DEV Dev, uint8_t option) +{ + + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t byte; + uint32_t TmpDWord; + uint8_t ModuleId; + uint8_t Revision; + uint8_t ReferenceSpadCount = 0; + uint8_t ReferenceSpadType = 0; + uint32_t PartUIDUpper = 0; + uint32_t PartUIDLower = 0; + uint32_t OffsetFixed1104_mm = 0; + int16_t OffsetMicroMeters = 0; + uint32_t DistMeasTgtFixed1104_mm = 400 << 4; + uint32_t DistMeasFixed1104_400_mm = 0; + uint32_t SignalRateMeasFixed1104_400_mm = 0; + char ProductId[19]; + char *ProductId_tmp; + uint8_t ReadDataFromDeviceDone; + FixPoint1616_t SignalRateMeasFixed400mmFix = 0; + uint8_t NvmRefGoodSpadMap[VL53L0_REF_SPAD_BUFFER_SIZE]; + int i; + + + LOG_FUNCTION_START(""); + + ReadDataFromDeviceDone = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + ReadDataFromDeviceDone); + + /* This access is done only once after that a GetDeviceInfo or + * datainit is done + */ + if (ReadDataFromDeviceDone != 7) { + + Status |= VL53L0_WrByte(Dev, 0x80, 0x01); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x00); + + Status |= VL53L0_WrByte(Dev, 0xFF, 0x06); + Status |= VL53L0_RdByte(Dev, 0x83, &byte); + Status |= VL53L0_WrByte(Dev, 0x83, byte|4); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x07); + Status |= VL53L0_WrByte(Dev, 0x81, 0x01); + + Status |= VL53L0_PollingDelay(Dev); + + Status |= VL53L0_WrByte(Dev, 0x80, 0x01); + + if (((option & 1) == 1) && + ((ReadDataFromDeviceDone & 1) == 0)) { + Status |= VL53L0_WrByte(Dev, 0x94, 0x6b); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + ReferenceSpadCount = (uint8_t)((TmpDWord >> 8) & 0x07f); + ReferenceSpadType = (uint8_t)((TmpDWord >> 15) & 0x01); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x24); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + + NvmRefGoodSpadMap[0] = (uint8_t)((TmpDWord >> 24) + & 0xff); + NvmRefGoodSpadMap[1] = (uint8_t)((TmpDWord >> 16) + & 0xff); + NvmRefGoodSpadMap[2] = (uint8_t)((TmpDWord >> 8) + & 0xff); + NvmRefGoodSpadMap[3] = (uint8_t)(TmpDWord & 0xff); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x25); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + NvmRefGoodSpadMap[4] = (uint8_t)((TmpDWord >> 24) + & 0xff); + NvmRefGoodSpadMap[5] = (uint8_t)((TmpDWord >> 16) + & 0xff); + } + + if (((option & 2) == 2) && + ((ReadDataFromDeviceDone & 2) == 0)) { + + Status |= VL53L0_WrByte(Dev, 0x94, 0x02); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdByte(Dev, 0x90, &ModuleId); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x7B); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdByte(Dev, 0x90, &Revision); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x77); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + ProductId[0] = (char)((TmpDWord >> 25) & 0x07f); + ProductId[1] = (char)((TmpDWord >> 18) & 0x07f); + ProductId[2] = (char)((TmpDWord >> 11) & 0x07f); + ProductId[3] = (char)((TmpDWord >> 4) & 0x07f); + + byte = (uint8_t)((TmpDWord & 0x00f) << 3); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x78); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + ProductId[4] = (char)(byte + + ((TmpDWord >> 29) & 0x07f)); + ProductId[5] = (char)((TmpDWord >> 22) & 0x07f); + ProductId[6] = (char)((TmpDWord >> 15) & 0x07f); + ProductId[7] = (char)((TmpDWord >> 8) & 0x07f); + ProductId[8] = (char)((TmpDWord >> 1) & 0x07f); + + byte = (uint8_t)((TmpDWord & 0x001) << 6); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x79); + + Status |= VL53L0_device_read_strobe(Dev); + + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + ProductId[9] = (char)(byte + + ((TmpDWord >> 26) & 0x07f)); + ProductId[10] = (char)((TmpDWord >> 19) & 0x07f); + ProductId[11] = (char)((TmpDWord >> 12) & 0x07f); + ProductId[12] = (char)((TmpDWord >> 5) & 0x07f); + + byte = (uint8_t)((TmpDWord & 0x01f) << 2); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x7A); + + Status |= VL53L0_device_read_strobe(Dev); + + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + ProductId[13] = (char)(byte + + ((TmpDWord >> 30) & 0x07f)); + ProductId[14] = (char)((TmpDWord >> 23) & 0x07f); + ProductId[15] = (char)((TmpDWord >> 16) & 0x07f); + ProductId[16] = (char)((TmpDWord >> 9) & 0x07f); + ProductId[17] = (char)((TmpDWord >> 2) & 0x07f); + ProductId[18] = '\0'; + + } + + if (((option & 4) == 4) && + ((ReadDataFromDeviceDone & 4) == 0)) { + + Status |= VL53L0_WrByte(Dev, 0x94, 0x7B); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &PartUIDUpper); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x7C); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &PartUIDLower); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x73); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + SignalRateMeasFixed1104_400_mm = (TmpDWord & + 0x0000000ff) << 8; + + Status |= VL53L0_WrByte(Dev, 0x94, 0x74); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + SignalRateMeasFixed1104_400_mm |= ((TmpDWord & + 0xff000000) >> 24); + + Status |= VL53L0_WrByte(Dev, 0x94, 0x75); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + DistMeasFixed1104_400_mm = (TmpDWord & 0x0000000ff) + << 8; + + Status |= VL53L0_WrByte(Dev, 0x94, 0x76); + Status |= VL53L0_device_read_strobe(Dev); + Status |= VL53L0_RdDWord(Dev, 0x90, &TmpDWord); + + DistMeasFixed1104_400_mm |= ((TmpDWord & 0xff000000) + >> 24); + } + + Status |= VL53L0_WrByte(Dev, 0x81, 0x00); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x06); + Status |= VL53L0_RdByte(Dev, 0x83, &byte); + Status |= VL53L0_WrByte(Dev, 0x83, byte&0xfb); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x01); + Status |= VL53L0_WrByte(Dev, 0x00, 0x01); + + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + Status |= VL53L0_WrByte(Dev, 0x80, 0x00); + } + + if ((Status == VL53L0_ERROR_NONE) && + (ReadDataFromDeviceDone != 7)) { + /* Assign to variable if status is ok */ + if (((option & 1) == 1) && + ((ReadDataFromDeviceDone & 1) == 0)) { + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadCount, ReferenceSpadCount); + + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + ReferenceSpadType, ReferenceSpadType); + + for (i = 0; i < VL53L0_REF_SPAD_BUFFER_SIZE; i++) { + Dev->Data.SpadData.RefGoodSpadMap[i] = + NvmRefGoodSpadMap[i]; + } + } + + if (((option & 2) == 2) && + ((ReadDataFromDeviceDone & 2) == 0)) { + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + ModuleId, ModuleId); + + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + Revision, Revision); + + ProductId_tmp = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + ProductId); + VL53L0_COPYSTRING(ProductId_tmp, ProductId); + + } + + if (((option & 4) == 4) && + ((ReadDataFromDeviceDone & 4) == 0)) { + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + PartUIDUpper, PartUIDUpper); + + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + PartUIDLower, PartUIDLower); + + SignalRateMeasFixed400mmFix = + VL53L0_FIXPOINT97TOFIXPOINT1616( + SignalRateMeasFixed1104_400_mm); + + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + SignalRateMeasFixed400mm, + SignalRateMeasFixed400mmFix); + + OffsetMicroMeters = 0; + if (DistMeasFixed1104_400_mm != 0) { + OffsetFixed1104_mm = + DistMeasFixed1104_400_mm - + DistMeasTgtFixed1104_mm; + OffsetMicroMeters = (OffsetFixed1104_mm + * 1000) >> 4; + OffsetMicroMeters *= -1; + } + + PALDevDataSet(Dev, + Part2PartOffsetAdjustmentNVMMicroMeter, + OffsetMicroMeters); + } + byte = (uint8_t)(ReadDataFromDeviceDone|option); + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, ReadDataFromDeviceDone, + byte); + } + + LOG_FUNCTION_END(Status); + return Status; +} + + +uint32_t VL53L0_calc_macro_period_ps(VL53L0_DEV Dev, uint8_t vcsel_period_pclks) +{ + uint64_t PLL_period_ps; + uint32_t macro_period_vclks; + uint32_t macro_period_ps; + + LOG_FUNCTION_START(""); + + /* The above calculation will produce rounding errors, + * therefore set fixed value + */ + PLL_period_ps = 1655; + + macro_period_vclks = 2304; + macro_period_ps = (uint32_t)(macro_period_vclks + * vcsel_period_pclks * PLL_period_ps); + + LOG_FUNCTION_END(""); + return macro_period_ps; +} + +uint16_t VL53L0_encode_timeout(uint32_t timeout_macro_clks) +{ + /*! + * Encode timeout in macro periods in (LSByte * 2^MSByte) + 1 format + */ + + uint16_t encoded_timeout = 0; + uint32_t ls_byte = 0; + uint16_t ms_byte = 0; + + if (timeout_macro_clks > 0) { + ls_byte = timeout_macro_clks - 1; + + while ((ls_byte & 0xFFFFFF00) > 0) { + ls_byte = ls_byte >> 1; + ms_byte++; + } + + encoded_timeout = (ms_byte << 8) + + (uint16_t) (ls_byte & 0x000000FF); + } + + return encoded_timeout; + +} + +uint32_t VL53L0_decode_timeout(uint16_t encoded_timeout) +{ + /*! + * Decode 16-bit timeout register value - format (LSByte * 2^MSByte) + 1 + */ + + uint32_t timeout_macro_clks = 0; + + timeout_macro_clks = ((uint32_t) (encoded_timeout & 0x00FF) + << (uint32_t) ((encoded_timeout & 0xFF00) >> 8)) + 1; + + return timeout_macro_clks; +} + + +/* To convert ms into register value */ +uint32_t VL53L0_calc_timeout_mclks(VL53L0_DEV Dev, + uint32_t timeout_period_us, + uint8_t vcsel_period_pclks) +{ + uint32_t macro_period_ps; + uint32_t macro_period_ns; + uint32_t timeout_period_mclks = 0; + + macro_period_ps = VL53L0_calc_macro_period_ps(Dev, vcsel_period_pclks); + macro_period_ns = (macro_period_ps + 500) / 1000; + + timeout_period_mclks = + (uint32_t) (((timeout_period_us * 1000) + + (macro_period_ns / 2)) / macro_period_ns); + + return timeout_period_mclks; +} + +/* To convert register value into us */ +uint32_t VL53L0_calc_timeout_us(VL53L0_DEV Dev, + uint16_t timeout_period_mclks, + uint8_t vcsel_period_pclks) +{ + uint32_t macro_period_ps; + uint32_t macro_period_ns; + uint32_t actual_timeout_period_us = 0; + + macro_period_ps = VL53L0_calc_macro_period_ps(Dev, vcsel_period_pclks); + macro_period_ns = (macro_period_ps + 500) / 1000; + + actual_timeout_period_us = + ((timeout_period_mclks * macro_period_ns) + 500) / 1000; + + return actual_timeout_period_us; +} + + +VL53L0_Error get_sequence_step_timeout(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, + uint32_t *pTimeOutMicroSecs) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t CurrentVCSELPulsePeriodPClk; + uint8_t EncodedTimeOutByte = 0; + uint32_t TimeoutMicroSeconds = 0; + uint16_t PreRangeEncodedTimeOut = 0; + uint16_t MsrcTimeOutMClks; + uint16_t PreRangeTimeOutMClks; + uint16_t FinalRangeTimeOutMClks = 0; + uint16_t FinalRangeEncodedTimeOut; + VL53L0_SchedulerSequenceSteps_t SchedulerSequenceSteps; + + if ((SequenceStepId == VL53L0_SEQUENCESTEP_TCC) || + (SequenceStepId == VL53L0_SEQUENCESTEP_DSS) || + (SequenceStepId == VL53L0_SEQUENCESTEP_MSRC)) { + + Status = VL53L0_GetVcselPulsePeriod(Dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + &CurrentVCSELPulsePeriodPClk); + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdByte(Dev, + VL53L0_REG_MSRC_CONFIG_TIMEOUT_MACROP, + &EncodedTimeOutByte); + } + MsrcTimeOutMClks = VL53L0_decode_timeout(EncodedTimeOutByte); + + TimeoutMicroSeconds = VL53L0_calc_timeout_us(Dev, + MsrcTimeOutMClks, + CurrentVCSELPulsePeriodPClk); + } else if (SequenceStepId == VL53L0_SEQUENCESTEP_PRE_RANGE) { + /* Retrieve PRE-RANGE VCSEL Period */ + Status = VL53L0_GetVcselPulsePeriod(Dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + &CurrentVCSELPulsePeriodPClk); + + /* Retrieve PRE-RANGE Timeout in Macro periods (MCLKS) */ + if (Status == VL53L0_ERROR_NONE) { + + /* Retrieve PRE-RANGE VCSEL Period */ + Status = VL53L0_GetVcselPulsePeriod(Dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + &CurrentVCSELPulsePeriodPClk); + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdWord(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI, + &PreRangeEncodedTimeOut); + } + + PreRangeTimeOutMClks = VL53L0_decode_timeout( + PreRangeEncodedTimeOut); + + TimeoutMicroSeconds = VL53L0_calc_timeout_us(Dev, + PreRangeTimeOutMClks, + CurrentVCSELPulsePeriodPClk); + } + } else if (SequenceStepId == VL53L0_SEQUENCESTEP_FINAL_RANGE) { + + VL53L0_GetSequenceStepEnables(Dev, &SchedulerSequenceSteps); + PreRangeTimeOutMClks = 0; + + if (SchedulerSequenceSteps.PreRangeOn) { + /* Retrieve PRE-RANGE VCSEL Period */ + Status = VL53L0_GetVcselPulsePeriod(Dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + &CurrentVCSELPulsePeriodPClk); + + /* Retrieve PRE-RANGE Timeout in Macro periods + * (MCLKS) + */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdWord(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI, + &PreRangeEncodedTimeOut); + PreRangeTimeOutMClks = VL53L0_decode_timeout( + PreRangeEncodedTimeOut); + } + } + + if (Status == VL53L0_ERROR_NONE) { + /* Retrieve FINAL-RANGE VCSEL Period */ + Status = VL53L0_GetVcselPulsePeriod(Dev, + VL53L0_VCSEL_PERIOD_FINAL_RANGE, + &CurrentVCSELPulsePeriodPClk); + } + + /* Retrieve FINAL-RANGE Timeout in Macro periods (MCLKS) */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdWord(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI, + &FinalRangeEncodedTimeOut); + FinalRangeTimeOutMClks = VL53L0_decode_timeout( + FinalRangeEncodedTimeOut); + } + + FinalRangeTimeOutMClks -= PreRangeTimeOutMClks; + TimeoutMicroSeconds = VL53L0_calc_timeout_us(Dev, + FinalRangeTimeOutMClks, + CurrentVCSELPulsePeriodPClk); + } + + *pTimeOutMicroSecs = TimeoutMicroSeconds; + + return Status; +} + + +VL53L0_Error set_sequence_step_timeout(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, + uint32_t TimeOutMicroSecs) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t CurrentVCSELPulsePeriodPClk; + uint8_t MsrcEncodedTimeOut; + uint16_t PreRangeEncodedTimeOut; + uint16_t PreRangeTimeOutMClks; + uint16_t MsrcRangeTimeOutMClks; + uint32_t FinalRangeTimeOutMClks; + uint16_t FinalRangeEncodedTimeOut; + VL53L0_SchedulerSequenceSteps_t SchedulerSequenceSteps; + + if ((SequenceStepId == VL53L0_SEQUENCESTEP_TCC) || + (SequenceStepId == VL53L0_SEQUENCESTEP_DSS) || + (SequenceStepId == VL53L0_SEQUENCESTEP_MSRC)) { + + Status = VL53L0_GetVcselPulsePeriod(Dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + &CurrentVCSELPulsePeriodPClk); + + if (Status == VL53L0_ERROR_NONE) { + MsrcRangeTimeOutMClks = VL53L0_calc_timeout_mclks(Dev, + TimeOutMicroSecs, + (uint8_t)CurrentVCSELPulsePeriodPClk); + + if (MsrcRangeTimeOutMClks > 256) + MsrcEncodedTimeOut = 255; + else + MsrcEncodedTimeOut = + (uint8_t)MsrcRangeTimeOutMClks - 1; + + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + LastEncodedTimeout, + MsrcEncodedTimeOut); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_WrByte(Dev, + VL53L0_REG_MSRC_CONFIG_TIMEOUT_MACROP, + MsrcEncodedTimeOut); + } + } else { + + if (SequenceStepId == VL53L0_SEQUENCESTEP_PRE_RANGE) { + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_GetVcselPulsePeriod(Dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + &CurrentVCSELPulsePeriodPClk); + PreRangeTimeOutMClks = + VL53L0_calc_timeout_mclks(Dev, + TimeOutMicroSecs, + (uint8_t)CurrentVCSELPulsePeriodPClk); + PreRangeEncodedTimeOut = VL53L0_encode_timeout( + PreRangeTimeOutMClks); + + VL53L0_SETDEVICESPECIFICPARAMETER(Dev, + LastEncodedTimeout, + PreRangeEncodedTimeOut); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_WrWord(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_TIMEOUT_MACROP_HI, + PreRangeEncodedTimeOut); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETDEVICESPECIFICPARAMETER( + Dev, + PreRangeTimeoutMicroSecs, + TimeOutMicroSecs); + } + } else if (SequenceStepId == VL53L0_SEQUENCESTEP_FINAL_RANGE) { + + /* For the final range timeout, the pre-range timeout + * must be added. To do this both final and pre-range + * timeouts must be expressed in macro periods MClks + * because they have different vcsel periods. + */ + + VL53L0_GetSequenceStepEnables(Dev, + &SchedulerSequenceSteps); + PreRangeTimeOutMClks = 0; + if (SchedulerSequenceSteps.PreRangeOn) { + + /* Retrieve PRE-RANGE VCSEL Period */ + Status = VL53L0_GetVcselPulsePeriod(Dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + &CurrentVCSELPulsePeriodPClk); + + /* Retrieve PRE-RANGE Timeout in Macro periods + * (MCLKS) + */ + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdWord(Dev, 0x51, + &PreRangeEncodedTimeOut); + PreRangeTimeOutMClks = + VL53L0_decode_timeout( + PreRangeEncodedTimeOut); + } + } + + /* Calculate FINAL RANGE Timeout in Macro Periods + * (MCLKS) and add PRE-RANGE value + */ + if (Status == VL53L0_ERROR_NONE) { + + Status = VL53L0_GetVcselPulsePeriod(Dev, + VL53L0_VCSEL_PERIOD_FINAL_RANGE, + &CurrentVCSELPulsePeriodPClk); + } + if (Status == VL53L0_ERROR_NONE) { + + FinalRangeTimeOutMClks = + VL53L0_calc_timeout_mclks(Dev, + TimeOutMicroSecs, + (uint8_t) CurrentVCSELPulsePeriodPClk); + + FinalRangeTimeOutMClks += PreRangeTimeOutMClks; + + FinalRangeEncodedTimeOut = + VL53L0_encode_timeout(FinalRangeTimeOutMClks); + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_WrWord(Dev, 0x71, + FinalRangeEncodedTimeOut); + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETDEVICESPECIFICPARAMETER( + Dev, + FinalRangeTimeoutMicroSecs, + TimeOutMicroSecs); + } + } + } else + Status = VL53L0_ERROR_INVALID_PARAMS; + + } + return Status; +} + +VL53L0_Error VL53L0_set_vcsel_pulse_period(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, uint8_t VCSELPulsePeriodPCLK) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t vcsel_period_reg; + uint8_t MinPreVcselPeriodPCLK = 12; + uint8_t MaxPreVcselPeriodPCLK = 18; + uint8_t MinFinalVcselPeriodPCLK = 8; + uint8_t MaxFinalVcselPeriodPCLK = 14; + uint32_t MeasurementTimingBudgetMicroSeconds; + uint32_t FinalRangeTimeoutMicroSeconds; + uint32_t PreRangeTimeoutMicroSeconds; + uint32_t MsrcTimeoutMicroSeconds; + uint8_t PhaseCalInt = 0; + + /* Check if valid clock period requested */ + + if ((VCSELPulsePeriodPCLK % 2) != 0) { + /* Value must be an even number */ + Status = VL53L0_ERROR_INVALID_PARAMS; + } else if (VcselPeriodType == VL53L0_VCSEL_PERIOD_PRE_RANGE && + (VCSELPulsePeriodPCLK < MinPreVcselPeriodPCLK || + VCSELPulsePeriodPCLK > MaxPreVcselPeriodPCLK)) { + Status = VL53L0_ERROR_INVALID_PARAMS; + } else if (VcselPeriodType == VL53L0_VCSEL_PERIOD_FINAL_RANGE && + (VCSELPulsePeriodPCLK < MinFinalVcselPeriodPCLK || + VCSELPulsePeriodPCLK > MaxFinalVcselPeriodPCLK)) { + + Status = VL53L0_ERROR_INVALID_PARAMS; + } + + /* Apply specific settings for the requested clock period */ + + if (Status != VL53L0_ERROR_NONE) + return Status; + + + if (VcselPeriodType == VL53L0_VCSEL_PERIOD_PRE_RANGE) { + + /* Set phase check limits */ + if (VCSELPulsePeriodPCLK == 12) { + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_HIGH, + 0x18); + Status = VL53L0_WrByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_LOW, + 0x08); + } else if (VCSELPulsePeriodPCLK == 14) { + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_HIGH, + 0x30); + Status = VL53L0_WrByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_LOW, + 0x08); + } else if (VCSELPulsePeriodPCLK == 16) { + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_HIGH, + 0x40); + Status = VL53L0_WrByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_LOW, + 0x08); + } else if (VCSELPulsePeriodPCLK == 18) { + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_HIGH, + 0x50); + Status = VL53L0_WrByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VALID_PHASE_LOW, + 0x08); + } + } else if (VcselPeriodType == VL53L0_VCSEL_PERIOD_FINAL_RANGE) { + + if (VCSELPulsePeriodPCLK == 8) { + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, + 0x10); + Status = VL53L0_WrByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_LOW, + 0x08); + + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_GLOBAL_CONFIG_VCSEL_WIDTH, 0x02); + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_ALGO_PHASECAL_CONFIG_TIMEOUT, 0x0C); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x01); + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_ALGO_PHASECAL_LIM, + 0x30); + Status |= VL53L0_WrByte(Dev, 0xff, 0x00); + } else if (VCSELPulsePeriodPCLK == 10) { + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, + 0x28); + Status = VL53L0_WrByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_LOW, + 0x08); + + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_GLOBAL_CONFIG_VCSEL_WIDTH, 0x03); + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_ALGO_PHASECAL_CONFIG_TIMEOUT, 0x09); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x01); + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_ALGO_PHASECAL_LIM, + 0x20); + Status |= VL53L0_WrByte(Dev, 0xff, 0x00); + } else if (VCSELPulsePeriodPCLK == 12) { + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, + 0x38); + Status = VL53L0_WrByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_LOW, + 0x08); + + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_GLOBAL_CONFIG_VCSEL_WIDTH, 0x03); + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_ALGO_PHASECAL_CONFIG_TIMEOUT, 0x08); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x01); + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_ALGO_PHASECAL_LIM, + 0x20); + Status |= VL53L0_WrByte(Dev, 0xff, 0x00); + } else if (VCSELPulsePeriodPCLK == 14) { + + Status = VL53L0_WrByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_HIGH, + 0x048); + Status = VL53L0_WrByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VALID_PHASE_LOW, + 0x08); + + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_GLOBAL_CONFIG_VCSEL_WIDTH, 0x03); + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_ALGO_PHASECAL_CONFIG_TIMEOUT, 0x07); + + Status |= VL53L0_WrByte(Dev, 0xff, 0x01); + Status |= VL53L0_WrByte(Dev, + VL53L0_REG_ALGO_PHASECAL_LIM, + 0x20); + Status |= VL53L0_WrByte(Dev, 0xff, 0x00); + } + } + + + /* Re-calculate and apply timeouts, in macro periods */ + + if (Status == VL53L0_ERROR_NONE) { + vcsel_period_reg = VL53L0_encode_vcsel_period((uint8_t) + VCSELPulsePeriodPCLK); + + /* When the VCSEL period for the pre or final range is changed, + * the corresponding timeout must be read from the device using + * the current VCSEL period, then the new VCSEL period can be + * applied. The timeout then must be written back to the device + * using the new VCSEL period. + * + * For the MSRC timeout, the same applies - this timeout being + * dependant on the pre-range vcsel period. + */ + switch (VcselPeriodType) { + case VL53L0_VCSEL_PERIOD_PRE_RANGE: + Status = get_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_PRE_RANGE, + &PreRangeTimeoutMicroSeconds); + + if (Status == VL53L0_ERROR_NONE) + Status = get_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_MSRC, + &MsrcTimeoutMicroSeconds); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VCSEL_PERIOD, + vcsel_period_reg); + + + if (Status == VL53L0_ERROR_NONE) + Status = set_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_PRE_RANGE, + PreRangeTimeoutMicroSeconds); + + + if (Status == VL53L0_ERROR_NONE) + Status = set_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_MSRC, + MsrcTimeoutMicroSeconds); + + VL53L0_SETDEVICESPECIFICPARAMETER( + Dev, + PreRangeVcselPulsePeriod, + VCSELPulsePeriodPCLK); + break; + case VL53L0_VCSEL_PERIOD_FINAL_RANGE: + Status = get_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_FINAL_RANGE, + &FinalRangeTimeoutMicroSeconds); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VCSEL_PERIOD, + vcsel_period_reg); + + + if (Status == VL53L0_ERROR_NONE) + Status = set_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_FINAL_RANGE, + FinalRangeTimeoutMicroSeconds); + + VL53L0_SETDEVICESPECIFICPARAMETER( + Dev, + FinalRangeVcselPulsePeriod, + VCSELPulsePeriodPCLK); + break; + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } + + /* Finally, the timing budget must be re-applied */ + if (Status == VL53L0_ERROR_NONE) { + VL53L0_GETPARAMETERFIELD(Dev, + MeasurementTimingBudgetMicroSeconds, + MeasurementTimingBudgetMicroSeconds); + + Status = VL53L0_SetMeasurementTimingBudgetMicroSeconds(Dev, + MeasurementTimingBudgetMicroSeconds); + } + + /* Perform the phase calibration. This is needed after changing on + * vcsel period. + * get_data_enable = 0, restore_config = 1 + */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_perform_phase_calibration( + Dev, &PhaseCalInt, 0, 1); + + return Status; +} + +VL53L0_Error VL53L0_get_vcsel_pulse_period(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, uint8_t *pVCSELPulsePeriodPCLK) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t vcsel_period_reg; + + switch (VcselPeriodType) { + case VL53L0_VCSEL_PERIOD_PRE_RANGE: + Status = VL53L0_RdByte(Dev, + VL53L0_REG_PRE_RANGE_CONFIG_VCSEL_PERIOD, + &vcsel_period_reg); + break; + case VL53L0_VCSEL_PERIOD_FINAL_RANGE: + Status = VL53L0_RdByte(Dev, + VL53L0_REG_FINAL_RANGE_CONFIG_VCSEL_PERIOD, + &vcsel_period_reg); + break; + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + } + + if (Status == VL53L0_ERROR_NONE) + *pVCSELPulsePeriodPCLK = + VL53L0_decode_vcsel_period(vcsel_period_reg); + + return Status; +} + + + +VL53L0_Error VL53L0_set_measurement_timing_budget_micro_seconds(VL53L0_DEV Dev, + uint32_t MeasurementTimingBudgetMicroSeconds) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint32_t FinalRangeTimingBudgetMicroSeconds; + VL53L0_SchedulerSequenceSteps_t SchedulerSequenceSteps; + uint32_t MsrcDccTccTimeoutMicroSeconds = 2000; + uint32_t StartOverheadMicroSeconds = 1910; + uint32_t EndOverheadMicroSeconds = 960; + uint32_t MsrcOverheadMicroSeconds = 660; + uint32_t TccOverheadMicroSeconds = 590; + uint32_t DssOverheadMicroSeconds = 690; + uint32_t PreRangeOverheadMicroSeconds = 660; + uint32_t FinalRangeOverheadMicroSeconds = 550; + uint32_t PreRangeTimeoutMicroSeconds = 0; + uint32_t SubTimeout = 0; + + LOG_FUNCTION_START(""); + + FinalRangeTimingBudgetMicroSeconds = + MeasurementTimingBudgetMicroSeconds - + (StartOverheadMicroSeconds + EndOverheadMicroSeconds); + + Status = VL53L0_GetSequenceStepEnables(Dev, &SchedulerSequenceSteps); + + if (Status == VL53L0_ERROR_NONE && + (SchedulerSequenceSteps.TccOn || + SchedulerSequenceSteps.MsrcOn || + SchedulerSequenceSteps.DssOn)) { + + /* TCC, MSRC and DSS all share the same timeout */ + Status = get_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_MSRC, + &MsrcDccTccTimeoutMicroSeconds); + + /* Subtract the TCC, MSRC and DSS timeouts if they are + * enabled. + */ + + if (Status != VL53L0_ERROR_NONE) + return Status; + + /* TCC */ + if (SchedulerSequenceSteps.TccOn) { + + SubTimeout = MsrcDccTccTimeoutMicroSeconds + + TccOverheadMicroSeconds; + + if (SubTimeout < + FinalRangeTimingBudgetMicroSeconds) { + FinalRangeTimingBudgetMicroSeconds -= + SubTimeout; + } else { + /* Requested timeout too big. */ + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } + + if (Status != VL53L0_ERROR_NONE) { + LOG_FUNCTION_END(Status); + return Status; + } + + /* DSS */ + if (SchedulerSequenceSteps.DssOn) { + + SubTimeout = 2 * (MsrcDccTccTimeoutMicroSeconds + + DssOverheadMicroSeconds); + + if (SubTimeout < FinalRangeTimingBudgetMicroSeconds) { + FinalRangeTimingBudgetMicroSeconds + -= SubTimeout; + } else { + /* Requested timeout too big. */ + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } else if (SchedulerSequenceSteps.MsrcOn) { + /* MSRC */ + SubTimeout = MsrcDccTccTimeoutMicroSeconds + + MsrcOverheadMicroSeconds; + + if (SubTimeout < FinalRangeTimingBudgetMicroSeconds) { + FinalRangeTimingBudgetMicroSeconds + -= SubTimeout; + } else { + /* Requested timeout too big. */ + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } + + } + + if (Status != VL53L0_ERROR_NONE) { + LOG_FUNCTION_END(Status); + return Status; + } + + if (SchedulerSequenceSteps.PreRangeOn) { + + /* Subtract the Pre-range timeout if enabled. */ + + Status = get_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_PRE_RANGE, + &PreRangeTimeoutMicroSeconds); + + SubTimeout = PreRangeTimeoutMicroSeconds + + PreRangeOverheadMicroSeconds; + + if (SubTimeout < FinalRangeTimingBudgetMicroSeconds) { + FinalRangeTimingBudgetMicroSeconds -= SubTimeout; + } else { + /* Requested timeout too big. */ + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } + + + if (Status == VL53L0_ERROR_NONE && + SchedulerSequenceSteps.FinalRangeOn) { + + FinalRangeTimingBudgetMicroSeconds -= + FinalRangeOverheadMicroSeconds; + + /* Final Range Timeout + * Note that the final range timeout is determined by the timing + * budget and the sum of all other timeouts within the sequence. + * If there is no room for the final range timeout, then an + * error will be set. Otherwise the remaining time will be + * applied to the final range. + */ + Status = set_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_FINAL_RANGE, + FinalRangeTimingBudgetMicroSeconds); + + VL53L0_SETPARAMETERFIELD(Dev, + MeasurementTimingBudgetMicroSeconds, + MeasurementTimingBudgetMicroSeconds); + } + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L0_Error VL53L0_get_measurement_timing_budget_micro_seconds(VL53L0_DEV Dev, + uint32_t *pMeasurementTimingBudgetMicroSeconds) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_SchedulerSequenceSteps_t SchedulerSequenceSteps; + uint32_t FinalRangeTimeoutMicroSeconds; + uint32_t MsrcDccTccTimeoutMicroSeconds = 2000; + uint32_t StartOverheadMicroSeconds = 1910; + uint32_t EndOverheadMicroSeconds = 960; + uint32_t MsrcOverheadMicroSeconds = 660; + uint32_t TccOverheadMicroSeconds = 590; + uint32_t DssOverheadMicroSeconds = 690; + uint32_t PreRangeOverheadMicroSeconds = 660; + uint32_t FinalRangeOverheadMicroSeconds = 550; + uint32_t PreRangeTimeoutMicroSeconds = 0; + + LOG_FUNCTION_START(""); + + /* Start and end overhead times always present */ + *pMeasurementTimingBudgetMicroSeconds + = StartOverheadMicroSeconds + EndOverheadMicroSeconds; + + Status = VL53L0_GetSequenceStepEnables(Dev, &SchedulerSequenceSteps); + + if (Status != VL53L0_ERROR_NONE) { + LOG_FUNCTION_END(Status); + return Status; + } + + + if (SchedulerSequenceSteps.TccOn || + SchedulerSequenceSteps.MsrcOn || + SchedulerSequenceSteps.DssOn) { + + Status = get_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_MSRC, + &MsrcDccTccTimeoutMicroSeconds); + + if (Status == VL53L0_ERROR_NONE) { + if (SchedulerSequenceSteps.TccOn) { + *pMeasurementTimingBudgetMicroSeconds += + MsrcDccTccTimeoutMicroSeconds + + TccOverheadMicroSeconds; + } + + if (SchedulerSequenceSteps.DssOn) { + *pMeasurementTimingBudgetMicroSeconds += + 2 * (MsrcDccTccTimeoutMicroSeconds + + DssOverheadMicroSeconds); + } else if (SchedulerSequenceSteps.MsrcOn) { + *pMeasurementTimingBudgetMicroSeconds += + MsrcDccTccTimeoutMicroSeconds + + MsrcOverheadMicroSeconds; + } + } + } + + if (Status == VL53L0_ERROR_NONE) { + if (SchedulerSequenceSteps.PreRangeOn) { + Status = get_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_PRE_RANGE, + &PreRangeTimeoutMicroSeconds); + *pMeasurementTimingBudgetMicroSeconds += + PreRangeTimeoutMicroSeconds + + PreRangeOverheadMicroSeconds; + } + } + + if (Status == VL53L0_ERROR_NONE) { + if (SchedulerSequenceSteps.FinalRangeOn) { + Status = get_sequence_step_timeout(Dev, + VL53L0_SEQUENCESTEP_FINAL_RANGE, + &FinalRangeTimeoutMicroSeconds); + *pMeasurementTimingBudgetMicroSeconds += + (FinalRangeTimeoutMicroSeconds + + FinalRangeOverheadMicroSeconds); + } + } + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_SETPARAMETERFIELD(Dev, + MeasurementTimingBudgetMicroSeconds, + *pMeasurementTimingBudgetMicroSeconds); + } + + LOG_FUNCTION_END(Status); + return Status; +} + + + +VL53L0_Error VL53L0_load_tuning_settings(VL53L0_DEV Dev, + uint8_t *pTuningSettingBuffer) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int i; + int Index; + uint8_t msb; + uint8_t lsb; + uint8_t SelectParam; + uint8_t NumberOfWrites; + uint8_t Address; + uint8_t localBuffer[4]; /* max */ + uint16_t Temp16; + + LOG_FUNCTION_START(""); + + Index = 0; + + while ((*(pTuningSettingBuffer + Index) != 0) && + (Status == VL53L0_ERROR_NONE)) { + NumberOfWrites = *(pTuningSettingBuffer + Index); + Index++; + if (NumberOfWrites == 0xFF) { + /* internal parameters */ + SelectParam = *(pTuningSettingBuffer + Index); + Index++; + switch (SelectParam) { + case 0: /* uint16_t SigmaEstRefArray -> 2 bytes */ + msb = *(pTuningSettingBuffer + Index); + Index++; + lsb = *(pTuningSettingBuffer + Index); + Index++; + Temp16 = VL53L0_MAKEUINT16(lsb, msb); + PALDevDataSet(Dev, SigmaEstRefArray, Temp16); + break; + case 1: /* uint16_t SigmaEstEffPulseWidth -> 2 bytes */ + msb = *(pTuningSettingBuffer + Index); + Index++; + lsb = *(pTuningSettingBuffer + Index); + Index++; + Temp16 = VL53L0_MAKEUINT16(lsb, msb); + PALDevDataSet(Dev, SigmaEstEffPulseWidth, + Temp16); + break; + case 2: /* uint16_t SigmaEstEffAmbWidth -> 2 bytes */ + msb = *(pTuningSettingBuffer + Index); + Index++; + lsb = *(pTuningSettingBuffer + Index); + Index++; + Temp16 = VL53L0_MAKEUINT16(lsb, msb); + PALDevDataSet(Dev, SigmaEstEffAmbWidth, Temp16); + break; + case 3: /* uint16_t targetRefRate -> 2 bytes */ + msb = *(pTuningSettingBuffer + Index); + Index++; + lsb = *(pTuningSettingBuffer + Index); + Index++; + Temp16 = VL53L0_MAKEUINT16(lsb, msb); + PALDevDataSet(Dev, targetRefRate, Temp16); + break; + default: /* invalid parameter */ + Status = VL53L0_ERROR_INVALID_PARAMS; + } + + } else if (NumberOfWrites <= 4) { + Address = *(pTuningSettingBuffer + Index); + Index++; + + for (i = 0; i < NumberOfWrites; i++) { + localBuffer[i] = *(pTuningSettingBuffer + + Index); + Index++; + } + + Status = VL53L0_WriteMulti(Dev, Address, localBuffer, + NumberOfWrites); + + } else { + Status = VL53L0_ERROR_INVALID_PARAMS; + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_get_total_xtalk_rate(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData, + FixPoint1616_t *ptotal_xtalk_rate_mcps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + uint8_t xtalkCompEnable; + FixPoint1616_t totalXtalkMegaCps; + FixPoint1616_t xtalkPerSpadMegaCps; + + *ptotal_xtalk_rate_mcps = 0; + + Status = VL53L0_GetXTalkCompensationEnable(Dev, &xtalkCompEnable); + if (Status == VL53L0_ERROR_NONE) { + + if (xtalkCompEnable) { + + VL53L0_GETPARAMETERFIELD( + Dev, + XTalkCompensationRateMegaCps, + xtalkPerSpadMegaCps); + + /* FixPoint1616 * FixPoint 8:8 = FixPoint0824 */ + totalXtalkMegaCps = + pRangingMeasurementData->EffectiveSpadRtnCount * + xtalkPerSpadMegaCps; + + /* FixPoint0824 >> 8 = FixPoint1616 */ + *ptotal_xtalk_rate_mcps = + (totalXtalkMegaCps + 0x80) >> 8; + } + } + + return Status; +} + +VL53L0_Error VL53L0_get_total_signal_rate(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData, + FixPoint1616_t *ptotal_signal_rate_mcps) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + FixPoint1616_t totalXtalkMegaCps; + + LOG_FUNCTION_START(""); + + *ptotal_signal_rate_mcps = + pRangingMeasurementData->SignalRateRtnMegaCps; + + Status = VL53L0_get_total_xtalk_rate( + Dev, pRangingMeasurementData, &totalXtalkMegaCps); + + if (Status == VL53L0_ERROR_NONE) + *ptotal_signal_rate_mcps += totalXtalkMegaCps; + + return Status; +} + +VL53L0_Error get_dmax_lut_points(VL53L0_DMaxLUT_t data, uint32_t lut_size, + FixPoint1616_t input, int32_t *index0, int32_t *index1){ + VL53L0_Error Status = VL53L0_ERROR_NONE; + FixPoint1616_t index0_tmp = 0; + FixPoint1616_t index1_tmp = 0; + int index = 0; + + for (index = 0; index < lut_size; index++) { + if (input <= data.ambRate_mcps[index]) { + index1_tmp = index; + break; + } + } + + if (index == lut_size) { + /* input is higher than last x point */ + index0_tmp = index1_tmp = lut_size - 1; + } else if (index1_tmp == 0) { + /* input is lower than first x point */ + index0_tmp = 0; + } else{ + /* input is in between 2 points */ + index0_tmp = index1_tmp - 1; + } + + *index0 = index0_tmp; + *index1 = index1_tmp; + + return Status; +} + +VL53L0_Error VL53L0_calc_dmax( + VL53L0_DEV Dev, FixPoint1616_t ambRateMeas, uint32_t *pdmax_mm){ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceParameters_t CurrentParameters; + int32_t index0 = 0; + int32_t index1 = 0; + FixPoint1616_t amb0, amb1, dmax0, dmax1; + FixPoint1616_t dmax_mm; + FixPoint1616_t linearSlope; + + LOG_FUNCTION_START(""); + + Status = VL53L0_GetDeviceParameters(Dev, &CurrentParameters); + + if (ambRateMeas <= CurrentParameters.dmax_lut.ambRate_mcps[0]) { + dmax_mm = CurrentParameters.dmax_lut.dmax_mm[0]; + } else if (ambRateMeas >= + CurrentParameters.dmax_lut. + ambRate_mcps[VL53L0_DMAX_LUT_SIZE - 1]) { + dmax_mm = + CurrentParameters.dmax_lut.dmax_mm[VL53L0_DMAX_LUT_SIZE - + 1]; + } else{ + get_dmax_lut_points(CurrentParameters.dmax_lut, + VL53L0_DMAX_LUT_SIZE, ambRateMeas, &index0, &index1); + + if (index0 == index1) { + dmax_mm = CurrentParameters.dmax_lut.dmax_mm[index0]; + } else { + amb0 = CurrentParameters.dmax_lut.ambRate_mcps[index0]; + amb1 = CurrentParameters.dmax_lut.ambRate_mcps[index1]; + dmax0 = CurrentParameters.dmax_lut.dmax_mm[index0]; + dmax1 = CurrentParameters.dmax_lut.dmax_mm[index1]; + if ((amb1 - amb0) != 0) { + /* Fix16:16/Fix16:8 => Fix16:8 */ + linearSlope = (dmax0-dmax1)/((amb1-amb0) >> 8); + + /* Fix16:8 * Fix16:8 => Fix16:16 */ + dmax_mm = + (((amb1 - + ambRateMeas) >> 8) * linearSlope) + + dmax1; + } else{ + dmax_mm = dmax0; + } + } + } + *pdmax_mm = (uint32_t)(dmax_mm >> 16); + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L0_Error VL53L0_calc_sigma_estimate(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData, + FixPoint1616_t *pSigmaEstimate) +{ + /* Expressed in 100ths of a ns, i.e. centi-ns */ + const uint32_t cPulseEffectiveWidth_centi_ns = 800; + /* Expressed in 100ths of a ns, i.e. centi-ns */ + const uint32_t cAmbientEffectiveWidth_centi_ns = 600; + const FixPoint1616_t cDfltFinalRangeIntegrationTimeMilliSecs = + 0x00190000; /* 25ms */ + const uint32_t cVcselPulseWidth_ps = 4700; /* pico secs */ + const FixPoint1616_t cSigmaEstMax = 0x028F87AE; + const FixPoint1616_t cSigmaEstRtnMax = 0xF000; + const FixPoint1616_t cAmbToSignalRatioMax = 0xF0000000/ + cAmbientEffectiveWidth_centi_ns; + /* Time Of Flight per mm (6.6 pico secs) */ + const FixPoint1616_t cTOF_per_mm_ps = 0x0006999A; + const uint32_t c16BitRoundingParam = 0x00008000; + const FixPoint1616_t cMaxXTalk_kcps = 0x00320000; + const uint32_t cPllPeriod_ps = 1655; + + uint32_t vcselTotalEventsRtn; + uint32_t finalRangeTimeoutMicroSecs; + uint32_t preRangeTimeoutMicroSecs; + uint32_t finalRangeIntegrationTimeMilliSecs; + FixPoint1616_t sigmaEstimateP1; + FixPoint1616_t sigmaEstimateP2; + FixPoint1616_t sigmaEstimateP3; + FixPoint1616_t deltaT_ps; + FixPoint1616_t pwMult; + FixPoint1616_t sigmaEstRtn; + FixPoint1616_t sigmaEstimate; + FixPoint1616_t xTalkCorrection; + FixPoint1616_t ambientRate_kcps; + FixPoint1616_t peakSignalRate_kcps; + FixPoint1616_t xTalkCompRate_mcps; + uint32_t xTalkCompRate_kcps; + VL53L0_Error Status = VL53L0_ERROR_NONE; + FixPoint1616_t diff1_mcps; + FixPoint1616_t diff2_mcps; + FixPoint1616_t sqr1; + FixPoint1616_t sqr2; + FixPoint1616_t sqrSum; + FixPoint1616_t sqrtResult_centi_ns; + FixPoint1616_t sqrtResult; + FixPoint1616_t totalSignalRate_mcps; + FixPoint1616_t sigmaEstRef; + uint32_t vcselWidth; + uint32_t finalRangeMacroPCLKS; + uint32_t preRangeMacroPCLKS; + uint32_t peakVcselDuration_us; + uint8_t finalRangeVcselPCLKS; + uint8_t preRangeVcselPCLKS; + /*! \addtogroup calc_sigma_estimate + * @{ + * + * Estimates the range sigma + */ + + LOG_FUNCTION_START(""); + + VL53L0_GETPARAMETERFIELD(Dev, XTalkCompensationRateMegaCps, + xTalkCompRate_mcps); + + /* + * We work in kcps rather than mcps as this helps keep within the + * confines of the 32 Fix1616 type. + */ + + ambientRate_kcps = + (pRangingMeasurementData->AmbientRateRtnMegaCps * 1000) >> 16; + + Status = VL53L0_get_total_signal_rate( + Dev, pRangingMeasurementData, &totalSignalRate_mcps); + Status = VL53L0_get_total_xtalk_rate( + Dev, pRangingMeasurementData, &xTalkCompRate_mcps); + + + /* Signal rate measurement provided by device is the + * peak signal rate, not average. + */ + peakSignalRate_kcps = (totalSignalRate_mcps * 1000); + peakSignalRate_kcps = (peakSignalRate_kcps + 0x8000) >> 16; + + xTalkCompRate_kcps = xTalkCompRate_mcps * 1000; + + if (xTalkCompRate_kcps > cMaxXTalk_kcps) + xTalkCompRate_kcps = cMaxXTalk_kcps; + + if (Status == VL53L0_ERROR_NONE) { + + /* Calculate final range macro periods */ + finalRangeTimeoutMicroSecs = VL53L0_GETDEVICESPECIFICPARAMETER( + Dev, FinalRangeTimeoutMicroSecs); + + finalRangeVcselPCLKS = VL53L0_GETDEVICESPECIFICPARAMETER( + Dev, FinalRangeVcselPulsePeriod); + + finalRangeMacroPCLKS = VL53L0_calc_timeout_mclks( + Dev, finalRangeTimeoutMicroSecs, finalRangeVcselPCLKS); + + /* Calculate pre-range macro periods */ + preRangeTimeoutMicroSecs = VL53L0_GETDEVICESPECIFICPARAMETER( + Dev, PreRangeTimeoutMicroSecs); + + preRangeVcselPCLKS = VL53L0_GETDEVICESPECIFICPARAMETER( + Dev, PreRangeVcselPulsePeriod); + + preRangeMacroPCLKS = VL53L0_calc_timeout_mclks( + Dev, preRangeTimeoutMicroSecs, preRangeVcselPCLKS); + + vcselWidth = 3; + if (finalRangeVcselPCLKS == 8) + vcselWidth = 2; + + + peakVcselDuration_us = vcselWidth * 2048 * + (preRangeMacroPCLKS + finalRangeMacroPCLKS); + peakVcselDuration_us = (peakVcselDuration_us + 500)/1000; + peakVcselDuration_us *= cPllPeriod_ps; + peakVcselDuration_us = (peakVcselDuration_us + 500)/1000; + + /* Fix1616 >> 8 = Fix2408 */ + totalSignalRate_mcps = (totalSignalRate_mcps + 0x80) >> 8; + + /* Fix2408 * uint32 = Fix2408 */ + vcselTotalEventsRtn = totalSignalRate_mcps * + peakVcselDuration_us; + + /* Fix2408 >> 8 = uint32 */ + vcselTotalEventsRtn = (vcselTotalEventsRtn + 0x80) >> 8; + + /* Fix2408 << 8 = Fix1616 = */ + totalSignalRate_mcps <<= 8; + } + + if (Status != VL53L0_ERROR_NONE) { + LOG_FUNCTION_END(Status); + return Status; + } + + if (peakSignalRate_kcps == 0) { + *pSigmaEstimate = cSigmaEstMax; + PALDevDataSet(Dev, SigmaEstimate, cSigmaEstMax); + } else { + if (vcselTotalEventsRtn < 1) + vcselTotalEventsRtn = 1; + + sigmaEstimateP1 = cPulseEffectiveWidth_centi_ns; + + /* ((FixPoint1616 << 16)* uint32)/uint32 = FixPoint1616 */ + sigmaEstimateP2 = (ambientRate_kcps << 16)/peakSignalRate_kcps; + if (sigmaEstimateP2 > cAmbToSignalRatioMax) { + /* Clip to prevent overflow. Will ensure safe + * max result. + */ + sigmaEstimateP2 = cAmbToSignalRatioMax; + } + sigmaEstimateP2 *= cAmbientEffectiveWidth_centi_ns; + + sigmaEstimateP3 = 2 * VL53L0_isqrt(vcselTotalEventsRtn * 12); + + /* uint32 * FixPoint1616 = FixPoint1616 */ + deltaT_ps = pRangingMeasurementData->RangeMilliMeter * + cTOF_per_mm_ps; + + /* + * vcselRate - xtalkCompRate + * (uint32 << 16) - FixPoint1616 = FixPoint1616. + * Divide result by 1000 to convert to mcps. + * 500 is added to ensure rounding when integer division + * truncates. + */ + diff1_mcps = (((peakSignalRate_kcps << 16) - + 2 * xTalkCompRate_kcps) + 500)/1000; + + /* vcselRate + xtalkCompRate */ + diff2_mcps = ((peakSignalRate_kcps << 16) + 500)/1000; + + /* Shift by 8 bits to increase resolution prior to the + * division + */ + diff1_mcps <<= 8; + + /* FixPoint0824/FixPoint1616 = FixPoint2408 */ + xTalkCorrection = abs(diff1_mcps/diff2_mcps); + + /* FixPoint2408 << 8 = FixPoint1616 */ + xTalkCorrection <<= 8; + + if (pRangingMeasurementData->RangeStatus != 0) { + pwMult = 1 << 16; + } else { + /* FixPoint1616/uint32 = FixPoint1616 */ + /* smaller than 1.0f */ + pwMult = deltaT_ps/cVcselPulseWidth_ps; + + /* + * FixPoint1616 * FixPoint1616 = FixPoint3232, however + * both values are small enough such that32 bits will + * not be exceeded. + */ + pwMult *= ((1 << 16) - xTalkCorrection); + + /* (FixPoint3232 >> 16) = FixPoint1616 */ + pwMult = (pwMult + c16BitRoundingParam) >> 16; + + /* FixPoint1616 + FixPoint1616 = FixPoint1616 */ + pwMult += (1 << 16); + + /* + * At this point the value will be 1.xx, therefore if we + * square the value this will exceed 32 bits. To address + * this perform a single shift to the right before the + * multiplication. + */ + pwMult >>= 1; + /* FixPoint1715 * FixPoint1715 = FixPoint3430 */ + pwMult = pwMult * pwMult; + + /* (FixPoint3430 >> 14) = Fix1616 */ + pwMult >>= 14; + } + + /* FixPoint1616 * uint32 = FixPoint1616 */ + sqr1 = pwMult * sigmaEstimateP1; + + /* (FixPoint1616 >> 16) = FixPoint3200 */ + sqr1 = (sqr1 + 0x8000) >> 16; + + /* FixPoint3200 * FixPoint3200 = FixPoint6400 */ + sqr1 *= sqr1; + + sqr2 = sigmaEstimateP2; + + /* (FixPoint1616 >> 16) = FixPoint3200 */ + sqr2 = (sqr2 + 0x8000) >> 16; + + /* FixPoint3200 * FixPoint3200 = FixPoint6400 */ + sqr2 *= sqr2; + + /* FixPoint64000 + FixPoint6400 = FixPoint6400 */ + sqrSum = sqr1 + sqr2; + + /* SQRT(FixPoin6400) = FixPoint3200 */ + sqrtResult_centi_ns = VL53L0_isqrt(sqrSum); + + /* (FixPoint3200 << 16) = FixPoint1616 */ + sqrtResult_centi_ns <<= 16; + + /* + * Note that the Speed Of Light is expressed in um per 1E-10 + * seconds (2997) Therefore to get mm/ns we have to divide by + * 10000 + */ + sigmaEstRtn = (((sqrtResult_centi_ns+50)/100) / + sigmaEstimateP3); + sigmaEstRtn *= VL53L0_SPEED_OF_LIGHT_IN_AIR; + + /* Add 5000 before dividing by 10000 to ensure rounding. */ + sigmaEstRtn += 5000; + sigmaEstRtn /= 10000; + + if (sigmaEstRtn > cSigmaEstRtnMax) { + /* Clip to prevent overflow. Will ensure safe + * max result. + */ + sigmaEstRtn = cSigmaEstRtnMax; + } + finalRangeIntegrationTimeMilliSecs = + (finalRangeTimeoutMicroSecs + preRangeTimeoutMicroSecs + + 500) / 1000; + + /* sigmaEstRef = 1mm * 25ms/final range integration time + * (inc pre-range) + * sqrt(FixPoint1616/int) = FixPoint2408) + */ + sigmaEstRef = + VL53L0_isqrt((cDfltFinalRangeIntegrationTimeMilliSecs + + finalRangeIntegrationTimeMilliSecs/2)/ + finalRangeIntegrationTimeMilliSecs); + + /* FixPoint2408 << 8 = FixPoint1616 */ + sigmaEstRef <<= 8; + sigmaEstRef = (sigmaEstRef + 500)/1000; + + /* FixPoint1616 * FixPoint1616 = FixPoint3232 */ + sqr1 = sigmaEstRtn * sigmaEstRtn; + /* FixPoint1616 * FixPoint1616 = FixPoint3232 */ + sqr2 = sigmaEstRef * sigmaEstRef; + + /* sqrt(FixPoint3232) = FixPoint1616 */ + sqrtResult = VL53L0_isqrt((sqr1 + sqr2)); + /* + * Note that the Shift by 4 bits increases resolution prior to + * the sqrt, therefore the result must be shifted by 2 bits to + * the right to revert back to the FixPoint1616 format. + */ + + sigmaEstimate = 1000 * sqrtResult; + + if ((peakSignalRate_kcps < 1) || (vcselTotalEventsRtn < 1) || + (sigmaEstimate > cSigmaEstMax)) { + sigmaEstimate = cSigmaEstMax; + } + + *pSigmaEstimate = (uint32_t)(sigmaEstimate); + PALDevDataSet(Dev, SigmaEstimate, *pSigmaEstimate); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_get_pal_range_status(VL53L0_DEV Dev, + uint8_t DeviceRangeStatus, + FixPoint1616_t SignalRate, + uint16_t EffectiveSpadRtnCount, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData, + uint8_t *pPalRangeStatus) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t NoneFlag; + uint8_t SigmaLimitflag = 0; + uint8_t SignalRefClipflag = 0; + uint8_t RangeIgnoreThresholdflag = 0; + uint8_t SigmaLimitCheckEnable = 0; + uint8_t SignalRateFinalRangeLimitCheckEnable = 0; + uint8_t SignalRefClipLimitCheckEnable = 0; + uint8_t RangeIgnoreThresholdLimitCheckEnable = 0; + FixPoint1616_t SigmaEstimate; + FixPoint1616_t SigmaLimitValue; + FixPoint1616_t SignalRefClipValue; + FixPoint1616_t RangeIgnoreThresholdValue; + FixPoint1616_t SignalRatePerSpad; + uint8_t DeviceRangeStatusInternal = 0; + uint16_t tmpWord = 0; + uint8_t Temp8; + uint32_t Dmax_mm = 0; + FixPoint1616_t LastSignalRefMcps; + + LOG_FUNCTION_START(""); + + + /* + * VL53L0 has a good ranging when the value of the + * DeviceRangeStatus = 11. This function will replace the value 0 with + * the value 11 in the DeviceRangeStatus. + * In addition, the SigmaEstimator is not included in the VL53L0 + * DeviceRangeStatus, this will be added in the PalRangeStatus. + */ + + DeviceRangeStatusInternal = ((DeviceRangeStatus & 0x78) >> 3); + + if (DeviceRangeStatusInternal == 0 || + DeviceRangeStatusInternal == 5 || + DeviceRangeStatusInternal == 7 || + DeviceRangeStatusInternal == 12 || + DeviceRangeStatusInternal == 13 || + DeviceRangeStatusInternal == 14 || + DeviceRangeStatusInternal == 15 + ) { + NoneFlag = 1; + } else { + NoneFlag = 0; + } + + /* + * Check if Sigma limit is enabled, if yes then do comparison with limit + * value and put the result back into pPalRangeStatus. + */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + &SigmaLimitCheckEnable); + + if ((SigmaLimitCheckEnable != 0) && (Status == VL53L0_ERROR_NONE)) { + /* + * compute the Sigma and check with limit + */ + Status = VL53L0_calc_sigma_estimate( + Dev, + pRangingMeasurementData, + &SigmaEstimate); + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_calc_dmax( + Dev, + pRangingMeasurementData->AmbientRateRtnMegaCps, + &Dmax_mm); + if (Status == VL53L0_ERROR_NONE) + pRangingMeasurementData->RangeDMaxMilliMeter = Dmax_mm; + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_GetLimitCheckValue(Dev, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + &SigmaLimitValue); + + if ((SigmaLimitValue > 0) && + (SigmaEstimate > SigmaLimitValue)) + /* Limit Fail */ + SigmaLimitflag = 1; + } + } + + /* + * Check if Signal ref clip limit is enabled, if yes then do comparison + * with limit value and put the result back into pPalRangeStatus. + */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, + &SignalRefClipLimitCheckEnable); + + if ((SignalRefClipLimitCheckEnable != 0) && + (Status == VL53L0_ERROR_NONE)) { + + Status = VL53L0_GetLimitCheckValue(Dev, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, + &SignalRefClipValue); + + /* Read LastSignalRefMcps from device */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, 0xFF, 0x01); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_RdWord(Dev, + VL53L0_REG_RESULT_PEAK_SIGNAL_RATE_REF, + &tmpWord); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, 0xFF, 0x00); + + LastSignalRefMcps = VL53L0_FIXPOINT97TOFIXPOINT1616(tmpWord); + PALDevDataSet(Dev, LastSignalRefMcps, LastSignalRefMcps); + + if ((SignalRefClipValue > 0) && + (LastSignalRefMcps > SignalRefClipValue)) { + /* Limit Fail */ + SignalRefClipflag = 1; + } + } + + /* + * Check if Signal ref clip limit is enabled, if yes then do comparison + * with limit value and put the result back into pPalRangeStatus. + * EffectiveSpadRtnCount has a format 8.8 + * If (Return signal rate < (1.5 x Xtalk x number of Spads)) : FAIL + */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + &RangeIgnoreThresholdLimitCheckEnable); + + if ((RangeIgnoreThresholdLimitCheckEnable != 0) && + (Status == VL53L0_ERROR_NONE)) { + + /* Compute the signal rate per spad */ + if (EffectiveSpadRtnCount == 0) { + SignalRatePerSpad = 0; + } else { + SignalRatePerSpad = (FixPoint1616_t)((256 * SignalRate) + / EffectiveSpadRtnCount); + } + + Status = VL53L0_GetLimitCheckValue(Dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + &RangeIgnoreThresholdValue); + + if ((RangeIgnoreThresholdValue > 0) && + (SignalRatePerSpad < RangeIgnoreThresholdValue)) { + /* Limit Fail add 2^6 to range status */ + RangeIgnoreThresholdflag = 1; + } + } + + if (Status == VL53L0_ERROR_NONE) { + if (NoneFlag == 1) { + *pPalRangeStatus = 255; /* NONE */ + } else if (DeviceRangeStatusInternal == 1 || + DeviceRangeStatusInternal == 2 || + DeviceRangeStatusInternal == 3) { + *pPalRangeStatus = 5; /* HW fail */ + } else if (DeviceRangeStatusInternal == 6 || + DeviceRangeStatusInternal == 9) { + *pPalRangeStatus = 4; /* Phase fail */ + } else if (DeviceRangeStatusInternal == 8 || + DeviceRangeStatusInternal == 10 || + SignalRefClipflag == 1) { + *pPalRangeStatus = 3; /* Min range */ + } else if (DeviceRangeStatusInternal == 4 || + RangeIgnoreThresholdflag == 1) { + *pPalRangeStatus = 2; /* Signal Fail */ + } else if (SigmaLimitflag == 1) { + *pPalRangeStatus = 1; /* Sigma Fail */ + } else { + *pPalRangeStatus = 0; /* Range Valid */ + } + } + + /* fill the Limit Check Status */ + + Status = VL53L0_GetLimitCheckEnable(Dev, + VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + &SignalRateFinalRangeLimitCheckEnable); + + if (Status == VL53L0_ERROR_NONE) { + if ((SigmaLimitCheckEnable == 0) || (SigmaLimitflag == 1)) + Temp8 = 1; + else + Temp8 = 0; + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, Temp8); + + if ((DeviceRangeStatusInternal == 4) || + (SignalRateFinalRangeLimitCheckEnable == 0)) + Temp8 = 1; + else + Temp8 = 0; + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + Temp8); + + if ((SignalRefClipLimitCheckEnable == 0) || + (SignalRefClipflag == 1)) + Temp8 = 1; + else + Temp8 = 0; + + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, Temp8); + + if ((RangeIgnoreThresholdLimitCheckEnable == 0) || + (RangeIgnoreThresholdflag == 1)) + Temp8 = 1; + else + Temp8 = 0; + + VL53L0_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + Temp8); + } + + LOG_FUNCTION_END(Status); + return Status; + +} diff --git a/drivers/input/misc/vl53L0/src/vl53l0_api_histogram.c b/drivers/input/misc/vl53L0/src/vl53l0_api_histogram.c new file mode 100644 index 000000000000..21b0410dd6ae --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l0_api_histogram.c @@ -0,0 +1,750 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. + All rights reserved. + + 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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND + NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. + IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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 "vl53l0_api.h" +#include "vl53l0_api_core.h" +#include "vl53l0_api_histogram.h" + + +#ifndef __KERNEL__ +#include +#endif +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__) + + +typedef uint32_t WindowSelection; +#define VL53L0_AMBIENT_WINDOW_ONLY ((WindowSelection) 0) + /*!< Measure Ambient Signal only */ +#define VL53L0_AMBIENT_AND_SIGNAL_WINDOW ((WindowSelection) 1) + /*!< Measure Combined Ambient and Signal Rate. */ + + +VL53L0_Error VL53L0_start_histogram_measurement(VL53L0_DEV Dev, + VL53L0_HistogramModes histoMode, + uint32_t count) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t dataByte; + + LOG_FUNCTION_START(""); + + + dataByte = VL53L0_REG_SYSRANGE_MODE_SINGLESHOT | + VL53L0_REG_SYSRANGE_MODE_START_STOP; + + /* First histogram measurement must have bit 5 set */ + if (count == 0) + dataByte |= (1 << 5); + + switch (histoMode) { + case VL53L0_HISTOGRAMMODE_DISABLED: + /* Selected mode not supported */ + Status = VL53L0_ERROR_INVALID_COMMAND; + break; + + case VL53L0_HISTOGRAMMODE_REFERENCE_ONLY: + case VL53L0_HISTOGRAMMODE_RETURN_ONLY: + case VL53L0_HISTOGRAMMODE_BOTH: + dataByte |= (histoMode << 3); + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSRANGE_START, + dataByte); + if (Status == VL53L0_ERROR_NONE) { + /* Set PAL State to Running */ + PALDevDataSet(Dev, PalState, VL53L0_STATE_RUNNING); + } + break; + + default: + /* Selected mode not supported */ + Status = VL53L0_ERROR_MODE_NOT_SUPPORTED; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_confirm_measurement_start(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t NewDataReady = 0; + uint32_t LoopNb; + + LOG_FUNCTION_START(""); + + LoopNb = 0; + do { + Status = VL53L0_GetMeasurementDataReady(Dev, &NewDataReady); + if ((NewDataReady == 0x01) || Status != VL53L0_ERROR_NONE) + break; + + LoopNb = LoopNb + 1; + VL53L0_PollingDelay(Dev); + } while (LoopNb < VL53L0_DEFAULT_MAX_LOOP); + + if (LoopNb >= VL53L0_DEFAULT_MAX_LOOP) + Status = VL53L0_ERROR_TIME_OUT; + + LOG_FUNCTION_END(Status); + + return Status; +} + + +VL53L0_Error VL53L0_set_histogram_mode(VL53L0_DEV Dev, + VL53L0_HistogramModes HistogramMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + switch (HistogramMode) { + case VL53L0_HISTOGRAMMODE_DISABLED: + case VL53L0_HISTOGRAMMODE_REFERENCE_ONLY: + case VL53L0_HISTOGRAMMODE_RETURN_ONLY: + case VL53L0_HISTOGRAMMODE_BOTH: + /* Supported mode */ + VL53L0_SETPARAMETERFIELD(Dev, HistogramMode, HistogramMode); + break; + default: + /* Unsupported mode */ + Status = VL53L0_ERROR_MODE_NOT_SUPPORTED; + } + + return Status; +} + +VL53L0_Error VL53L0_get_histogram_mode(VL53L0_DEV Dev, + VL53L0_HistogramModes *pHistogramMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + VL53L0_GETPARAMETERFIELD(Dev, HistogramMode, *pHistogramMode); + + return Status; +} + + +VL53L0_Error VL53L0_perform_single_histogram_measurement(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceModes DeviceMode; + VL53L0_HistogramModes HistogramMode = VL53L0_HISTOGRAMMODE_DISABLED; + uint32_t MeasCount; + uint32_t Measurements; + + /* Get Current DeviceMode */ + Status = VL53L0_GetHistogramMode(Dev, &HistogramMode); + + + if (Status != VL53L0_ERROR_NONE) + return Status; + + + if (HistogramMode == VL53L0_HISTOGRAMMODE_BOTH) { + if (pHistogramMeasurementData->BufferSize < + VL53L0_HISTOGRAM_BUFFER_SIZE) { + Status = VL53L0_ERROR_BUFFER_TOO_SMALL; + } + } else { + if (pHistogramMeasurementData->BufferSize < + VL53L0_HISTOGRAM_BUFFER_SIZE/2) { + Status = VL53L0_ERROR_BUFFER_TOO_SMALL; + } + } + pHistogramMeasurementData->HistogramType = (uint8_t)HistogramMode; + pHistogramMeasurementData->ErrorStatus = VL53L0_DEVICEERROR_NONE; + pHistogramMeasurementData->FirstBin = 0; + pHistogramMeasurementData->NumberOfBins = 0; + + + /* Get Current DeviceMode */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetDeviceMode(Dev, &DeviceMode); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, VL53L0_REG_SYSTEM_HISTOGRAM_BIN, + 0x00); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_HISTOGRAM_CONFIG_INITIAL_PHASE_SELECT, 0x00); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_WrByte(Dev, + VL53L0_REG_HISTOGRAM_CONFIG_READOUT_CTRL, 0x01); + + if (Status != VL53L0_ERROR_NONE) + return Status; + + Measurements = 3; + if (HistogramMode == VL53L0_HISTOGRAMMODE_BOTH) + Measurements = 6; + + if (DeviceMode != VL53L0_DEVICEMODE_SINGLE_HISTOGRAM) { + Status = VL53L0_ERROR_INVALID_COMMAND; + return Status; + } + + /* DeviceMode == VL53L0_DEVICEMODE_SINGLE_HISTOGRAM */ + MeasCount = 0; + while ((MeasCount < Measurements) && (Status == VL53L0_ERROR_NONE)) { + Status = VL53L0_start_histogram_measurement(Dev, HistogramMode, + MeasCount); + + if (Status == VL53L0_ERROR_NONE) + VL53L0_confirm_measurement_start(Dev); + + if (Status == VL53L0_ERROR_NONE) + PALDevDataSet(Dev, PalState, VL53L0_STATE_RUNNING); + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_measurement_poll_for_completion(Dev); + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_read_histo_measurement(Dev, + pHistogramMeasurementData->HistogramData, + MeasCount, + HistogramMode); + + if (Status == VL53L0_ERROR_NONE) { + /* + * When reading both rtn and ref arrays, + * histograms are read two bins at a time. + * For rtn or ref only, histograms are read four + * bins at a time. + */ + if (HistogramMode == VL53L0_HISTOGRAMMODE_BOTH) + pHistogramMeasurementData->NumberOfBins + += 2; + else + pHistogramMeasurementData->NumberOfBins + += 4; + + } + } + + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_ClearInterruptMask(Dev, 0); + + MeasCount++; + } + + /* Change PAL State in case of single ranging or single histogram */ + if (Status == VL53L0_ERROR_NONE) { + pHistogramMeasurementData->NumberOfBins = 12; + PALDevDataSet(Dev, PalState, VL53L0_STATE_IDLE); + } + + return Status; +} + + +VL53L0_Error VL53L0_get_histogram_measurement_data(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData) +{ + VL53L0_Error Status = VL53L0_ERROR_NOT_IMPLEMENTED; + + LOG_FUNCTION_START(""); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_read_histo_measurement(VL53L0_DEV Dev, + uint32_t *histoData, + uint32_t offset, + VL53L0_HistogramModes histoMode) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t localBuffer[28]; + uint32_t cDataSize = 4; + uint32_t offset1; + + LOG_FUNCTION_START(""); + + Status = VL53L0_WrByte(Dev, 0xFF, VL53L0_REG_RESULT_CORE_PAGE); + Status = VL53L0_ReadMulti(Dev, + (uint8_t)VL53L0_REG_RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN, + localBuffer, + 28); + Status |= VL53L0_WrByte(Dev, 0xFF, 0x00); + + if (Status == VL53L0_ERROR_NONE) { + VL53L0_reverse_bytes(&localBuffer[0], cDataSize); + VL53L0_reverse_bytes(&localBuffer[4], cDataSize); + VL53L0_reverse_bytes(&localBuffer[20], cDataSize); + VL53L0_reverse_bytes(&localBuffer[24], cDataSize); + + offset1 = offset * cDataSize; + if (histoMode == VL53L0_HISTOGRAMMODE_BOTH) { + /* + * When reading both return and ref data, each + * measurement reads two ref values and two return + * values. Data is stored in an interleaved sequence, + * starting with the return histogram. + * + * Some result Core registers are reused for the + * histogram measurements + * + * The bin values are retrieved in the following order + * VL53L0_REG_RESULT_CORE_RANGING_TOTAL_EVENTS_RTN + * VL53L0_REG_RESULT_CORE_RANGING_TOTAL_EVENTS_REF + * VL53L0_REG_RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN + * VL53L0_REG_RESULT_CORE_AMBIENT_WINDOW_EVENTS_REF + */ + + memcpy(&histoData[offset1], &localBuffer[4], + cDataSize); /* rtn */ + memcpy(&histoData[offset1 + 1], &localBuffer[24], + cDataSize); /* ref */ + memcpy(&histoData[offset1 + 2], &localBuffer[0], + cDataSize); /* rtn */ + memcpy(&histoData[offset1 + 3], &localBuffer[20], + cDataSize); /* ref */ + + } else { + /* + * When reading either return and ref data, each + * measurement reads four bin values. + * + * The bin values are retrieved in the following order + * VL53L0_REG_RESULT_CORE_RANGING_TOTAL_EVENTS_RTN + * VL53L0_REG_RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN + * VL53L0_REG_RESULT_CORE_RANGING_TOTAL_EVENTS_REF + * VL53L0_REG_RESULT_CORE_AMBIENT_WINDOW_EVENTS_REF + */ + + memcpy(&histoData[offset1], &localBuffer[24], + cDataSize); + memcpy(&histoData[offset1 + 1], &localBuffer[20], + cDataSize); + memcpy(&histoData[offset1 + 2], &localBuffer[4], + cDataSize); + memcpy(&histoData[offset1 + 3], &localBuffer[0], + cDataSize); + + } + } + + LOG_FUNCTION_END(Status); + + return Status; +} + + +VL53L0_Error VL53L0_get_max_spads(VL53L0_DEV Dev, + uint32_t *pmax_spads, uint8_t *pambient_too_high) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t TCC_Enabled; + uint8_t MSRC_Enabled; + VL53L0_RangingMeasurementData_t RangingMeasurementData; + FixPoint1616_t ratio = 0; + uint32_t max_spads = 0; + + /* Get the value of the TCC */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_TCC, &TCC_Enabled); + + /* Get the value of the MSRC */ + if (Status == VL53L0_ERROR_NONE) + Status = VL53L0_GetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_MSRC, &MSRC_Enabled); + + /* Disable the TCC */ + if ((Status == VL53L0_ERROR_NONE) && (TCC_Enabled != 0)) + Status = VL53L0_SetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_TCC, 0); + + /* Disable the MSRC */ + if ((Status == VL53L0_ERROR_NONE) && (MSRC_Enabled != 0)) + Status = VL53L0_SetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_MSRC, 0); + + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_PerformSingleRangingMeasurement(Dev, + &RangingMeasurementData); + max_spads = (RangingMeasurementData.EffectiveSpadRtnCount + + 128)/256; + *pmax_spads = max_spads; + } + + /* Check Ambient per spad > 10 Kcps */ + if (Status == VL53L0_ERROR_NONE) { + if (max_spads <= 0) { + *pambient_too_high = 1; + Status = VL53L0_ERROR_DIVISION_BY_ZERO; + } else { + ratio = RangingMeasurementData.AmbientRateRtnMegaCps / + max_spads; + + /* ratio is given in mega count per second and + * FixPoint1616_t + */ + if (ratio > 65536/100) + *pambient_too_high = 1; + else + *pambient_too_high = 0; + + } + } + + + /* Restore the TCC */ + if (Status == VL53L0_ERROR_NONE) { + if (TCC_Enabled != 0) + Status = VL53L0_SetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_TCC, 1); + } + + /* Restore the MSRC */ + if (Status == VL53L0_ERROR_NONE) { + if (MSRC_Enabled != 0) + Status = VL53L0_SetSequenceStepEnable(Dev, + VL53L0_SEQUENCESTEP_MSRC, 1); + } + + return Status; + +} + + +VL53L0_Error calc_xtalk_mcps_per_spad( + uint32_t rtn_signal_events, + uint32_t timeout_ms, + uint32_t max_spads, + uint8_t vcsel_pulse_period_pclk, + FixPoint1616_t *pxtalk_mcps_per_spad) +{ + /* Calculate the X-Talk per Spad based on given inputs. + * + * To calculate x-talk, only a portion of the vcsel pulse period is + * used, therefore we use the ratio between vcsel_width_pclk and + * vcsel_pulse_period_pclks to determine the integration time. + * + * With the rtn signal events, and the integration time, + * the x-talk rate per spad is then determined. + */ + + const FixPoint1616_t cmin_xtalk_per_spad = 8; /* 0.000122 */ + const FixPoint1616_t ccompensation2 = 13;/* 0.0002 */ + const FixPoint1616_t ccompensation1 = 7; /* 0.0001; */ + const FixPoint1616_t ctalk_thresh = 66; /* 0.001 */ + const uint32_t c16BitRoundingParam = 0x00008000; + VL53L0_Error status = VL53L0_ERROR_NONE; + FixPoint1616_t xtalk_mcps; + FixPoint1616_t vcsel_width_to_period_ratio; + FixPoint1616_t integration_time_us; + uint32_t integration_time_us_int; + uint8_t vcsel_width_pclk = 3; + + LOG_FUNCTION_START(""); + + if (vcsel_pulse_period_pclk == 0 || timeout_ms == 0) + status = VL53L0_ERROR_DIVISION_BY_ZERO; + + + if (status == VL53L0_ERROR_NONE) { + + /* (FixPoint1616 + uint32)/uint32 = FixPoint1616 */ + vcsel_width_to_period_ratio = + ((vcsel_width_pclk << 16) + + (vcsel_pulse_period_pclk/2))/vcsel_pulse_period_pclk; + + /* uint32_t * FixPoint1616 = FixPoint1616 */ + integration_time_us = timeout_ms * vcsel_width_to_period_ratio + * 1000; + + /*FixPoint1616 >>16 = uint32_t */ + integration_time_us_int = (integration_time_us + + c16BitRoundingParam) >> 16; + + /* (uint32_t << 16)/uint32_t = FixPoint1616 */ + xtalk_mcps = rtn_signal_events << 16; + xtalk_mcps = (xtalk_mcps + + (integration_time_us_int/2))/integration_time_us_int; + + /* (FixPoint1616 + uint32)/uint32 = FixPoint1616 */ + *pxtalk_mcps_per_spad = (xtalk_mcps + (max_spads/2))/max_spads; + + /* Apply compensation to prevent overshoot. + */ + if (*pxtalk_mcps_per_spad < ctalk_thresh) + *pxtalk_mcps_per_spad = *pxtalk_mcps_per_spad + - ccompensation2; + else + *pxtalk_mcps_per_spad = *pxtalk_mcps_per_spad + - ccompensation1; + + if (*pxtalk_mcps_per_spad < cmin_xtalk_per_spad) + *pxtalk_mcps_per_spad = cmin_xtalk_per_spad; + + } + LOG_FUNCTION_END(""); + + return status; +} + + +uint32_t bytes_to_int(uint8_t *data_bytes) +{ + /* Convert an array of 4 bytes to an integer. + */ + uint32_t data = (uint32_t)data_bytes[0] << 24; + + data += ((uint32_t)data_bytes[1] << 16); + data += ((uint32_t)data_bytes[2] << 8); + data += ((uint32_t)data_bytes[3]); + return data; +} + +VL53L0_Error perform_histo_signal_meas(VL53L0_DEV dev, + WindowSelection window_select, + uint32_t *psignal_events) +{ + VL53L0_Error status = VL53L0_ERROR_NONE; + uint8_t data[8]; + uint8_t readout_ctrl_val; + uint32_t bin_width = 3; + + LOG_FUNCTION_START(""); + + /* Select Ambient or Total Signal Measurement + */ + if (status == VL53L0_ERROR_NONE) { + readout_ctrl_val = bin_width; + if (window_select == VL53L0_AMBIENT_WINDOW_ONLY) + readout_ctrl_val += 0x80; + + status = VL53L0_WrByte( + dev, VL53L0_REG_HISTOGRAM_CONFIG_READOUT_CTRL, + readout_ctrl_val); + } + + /* Perform Measurement. + */ + if (status == VL53L0_ERROR_NONE) + status = VL53L0_start_histogram_measurement( + dev, VL53L0_HISTOGRAMMODE_RETURN_ONLY, 0); + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_measurement_poll_for_completion(dev); + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_ClearInterruptMask(dev, 0); + + /* Read Measurement Data. + */ + if (status == VL53L0_ERROR_NONE) + status = VL53L0_WrByte(dev, 0xFF, VL53L0_REG_RESULT_CORE_PAGE); + + + if (status == VL53L0_ERROR_NONE) { + status = VL53L0_ReadMulti(dev, + (uint8_t)VL53L0_REG_RESULT_CORE_AMBIENT_WINDOW_EVENTS_RTN, + data, + 8); + } + + if (status == VL53L0_ERROR_NONE) + status |= VL53L0_WrByte(dev, 0xFF, 0x00); + + + /* Take the sum of the Ambient and Signal Window Event readings. + */ + if (status == VL53L0_ERROR_NONE) + *psignal_events = bytes_to_int(data) + + bytes_to_int(&(data[4])); + + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L0_Error set_final_range_timeout_us( + VL53L0_DEV dev, uint32_t timeout_microSecs, + uint16_t final_range_vcsel_period_pclks) +{ + VL53L0_Error status = VL53L0_ERROR_NONE; + uint16_t final_range_timeout_mclks; + uint16_t final_range_encoded_timeOut; + + LOG_FUNCTION_START(""); + + /* Calculate FINAL RANGE Timeout in Macro Periods (MCLKS) + */ + + /* convert timeout to mclks. */ + final_range_timeout_mclks = VL53L0_calc_timeout_mclks(dev, + timeout_microSecs, (uint8_t) final_range_vcsel_period_pclks); + + /* Encode timeout */ + final_range_encoded_timeOut = VL53L0_encode_timeout( + final_range_timeout_mclks); + + /* Write to device */ + status = VL53L0_WrWord(dev, + VL53L0_REG_FINAL_RANGE_CONFIG_TIMEOUT_MACROP_HI, + final_range_encoded_timeOut); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L0_Error perform_histogram_config(VL53L0_DEV dev, + uint32_t timeout_ms, uint16_t final_range_vcsel_period_pclks) +{ + VL53L0_Error status = VL53L0_ERROR_NONE; + uint8_t phaseSelect = 1; + + LOG_FUNCTION_START(""); + + if (status == VL53L0_ERROR_NONE) + status = set_final_range_timeout_us( + dev, timeout_ms * 1000, final_range_vcsel_period_pclks); + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_SetDeviceMode(dev, + VL53L0_DEVICEMODE_SINGLE_HISTOGRAM); + + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_SetHistogramMode(dev, + VL53L0_HISTOGRAMMODE_BOTH); + + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_WrByte(dev, VL53L0_REG_SYSTEM_HISTOGRAM_BIN, + 0x00); + + + /* Apply specific phase select for x-talk measurement */ + if (status == VL53L0_ERROR_NONE) + status = VL53L0_WrByte(dev, + VL53L0_REG_HISTOGRAM_CONFIG_INITIAL_PHASE_SELECT, + phaseSelect); + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L0_Error VL53L0_perform_xtalk_measurement(VL53L0_DEV dev, + uint32_t timeout_ms, FixPoint1616_t *pxtalk_per_spad, + uint8_t *pambient_too_high) +{ + VL53L0_Error status = VL53L0_ERROR_NONE; + uint32_t signal_events = 0; + uint32_t amb_events = 0; + uint32_t meas_timing_budget_us; + VL53L0_DeviceModes device_mode; + uint8_t final_range_vcsel_period_pclks; + uint32_t max_spads; + + /* Get Current DeviceMode */ + status = VL53L0_GetDeviceMode(dev, &device_mode); + + if (status == VL53L0_ERROR_NONE) + status = VL53L0_get_max_spads(dev, &max_spads, + pambient_too_high); + + if (status != VL53L0_ERROR_NONE) + return status; + + + if (status == VL53L0_ERROR_NONE) { + status = VL53L0_GetVcselPulsePeriod( + dev, + VL53L0_VCSEL_PERIOD_FINAL_RANGE, + &final_range_vcsel_period_pclks); + } + + if (status == VL53L0_ERROR_NONE) { + if (final_range_vcsel_period_pclks < 10) + status = VL53L0_ERROR_INVALID_PARAMS; + } + + if (status == VL53L0_ERROR_NONE) { + perform_histogram_config( + dev, timeout_ms, final_range_vcsel_period_pclks); + } + + if (status == VL53L0_ERROR_NONE) { + status = perform_histo_signal_meas( + dev, + VL53L0_AMBIENT_WINDOW_ONLY, + &amb_events); + } + + if (status == VL53L0_ERROR_NONE) { + status = perform_histo_signal_meas( + dev, + VL53L0_AMBIENT_AND_SIGNAL_WINDOW, + &signal_events); + } + + if (status == VL53L0_ERROR_NONE) { + status = calc_xtalk_mcps_per_spad( + (signal_events - amb_events), + timeout_ms, + max_spads, + final_range_vcsel_period_pclks, + pxtalk_per_spad); + } + + /* Revert previous device mode. */ + if (status == VL53L0_ERROR_NONE) + status = VL53L0_SetDeviceMode(dev, device_mode); + + /* Revert previous timing budget, to ensure previous final range vcsel + * period is applied. + */ + if (status == VL53L0_ERROR_NONE) { + VL53L0_GETPARAMETERFIELD( + dev, + MeasurementTimingBudgetMicroSeconds, + meas_timing_budget_us); + + status = VL53L0_SetMeasurementTimingBudgetMicroSeconds( + dev, meas_timing_budget_us); + } + + return status; +} + diff --git a/drivers/input/misc/vl53L0/src/vl53l0_api_ranging.c b/drivers/input/misc/vl53L0/src/vl53l0_api_ranging.c new file mode 100644 index 000000000000..f575aec7c5ed --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l0_api_ranging.c @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. + All rights reserved. + + 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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND + NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. + IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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 "vl53l0_api.h" +#include "vl53l0_api_core.h" + + +#ifndef __KERNEL__ +#include +#endif +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__) + diff --git a/drivers/input/misc/vl53L0/src/vl53l0_api_strings.c b/drivers/input/misc/vl53L0/src/vl53l0_api_strings.c new file mode 100644 index 000000000000..a5f2bbadd290 --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l0_api_strings.c @@ -0,0 +1,463 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. + All rights reserved. + + 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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND + NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. + IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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 "vl53l0_api.h" +#include "vl53l0_api_core.h" +#include "vl53l0_api_strings.h" + +#ifndef __KERNEL__ +#include +#endif + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(TRACE_MODULE_API, status, fmt, ##__VA_ARGS__) + + +VL53L0_Error VL53L0_check_part_used(VL53L0_DEV Dev, + uint8_t *Revision, + VL53L0_DeviceInfo_t *pVL53L0_DeviceInfo) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t ModuleIdInt; + char *ProductId_tmp; + + LOG_FUNCTION_START(""); + + Status = VL53L0_get_info_from_device(Dev, 2); + + if (Status == VL53L0_ERROR_NONE) { + ModuleIdInt = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, ModuleId); + + if (ModuleIdInt == 0) { + *Revision = 0; + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->ProductId, ""); + } else { + *Revision = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, Revision); + ProductId_tmp = VL53L0_GETDEVICESPECIFICPARAMETER(Dev, + ProductId); + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->ProductId, ProductId_tmp); + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L0_Error VL53L0_get_device_info(VL53L0_DEV Dev, + VL53L0_DeviceInfo_t *pVL53L0_DeviceInfo) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint8_t revision_id; + uint8_t Revision; + + Status = VL53L0_check_part_used(Dev, &Revision, pVL53L0_DeviceInfo); + + if (Status == VL53L0_ERROR_NONE) { + if (Revision == 0) { + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->Name, + VL53L0_STRING_DEVICE_INFO_NAME_TS0); + } else if ((Revision <= 34) && (Revision != 32)) { + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->Name, + VL53L0_STRING_DEVICE_INFO_NAME_TS1); + } else if (Revision < 39) { + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->Name, + VL53L0_STRING_DEVICE_INFO_NAME_TS2); + } else { + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->Name, + VL53L0_STRING_DEVICE_INFO_NAME_ES1); + } + + VL53L0_COPYSTRING(pVL53L0_DeviceInfo->Type, + VL53L0_STRING_DEVICE_INFO_TYPE); + + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdByte(Dev, VL53L0_REG_IDENTIFICATION_MODEL_ID, + &pVL53L0_DeviceInfo->ProductType); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = VL53L0_RdByte(Dev, + VL53L0_REG_IDENTIFICATION_REVISION_ID, + &revision_id); + pVL53L0_DeviceInfo->ProductRevisionMajor = 1; + pVL53L0_DeviceInfo->ProductRevisionMinor = + (revision_id & 0xF0) >> 4; + } + + return Status; +} + + +VL53L0_Error VL53L0_get_device_error_string(VL53L0_DeviceError ErrorCode, + char *pDeviceErrorString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + switch (ErrorCode) { + case VL53L0_DEVICEERROR_NONE: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_NONE); + break; + case VL53L0_DEVICEERROR_VCSELCONTINUITYTESTFAILURE: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_VCSELCONTINUITYTESTFAILURE); + break; + case VL53L0_DEVICEERROR_VCSELWATCHDOGTESTFAILURE: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_VCSELWATCHDOGTESTFAILURE); + break; + case VL53L0_DEVICEERROR_NOVHVVALUEFOUND: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_NOVHVVALUEFOUND); + break; + case VL53L0_DEVICEERROR_MSRCNOTARGET: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_MSRCNOTARGET); + break; + case VL53L0_DEVICEERROR_SNRCHECK: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_SNRCHECK); + break; + case VL53L0_DEVICEERROR_RANGEPHASECHECK: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_RANGEPHASECHECK); + break; + case VL53L0_DEVICEERROR_SIGMATHRESHOLDCHECK: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_SIGMATHRESHOLDCHECK); + break; + case VL53L0_DEVICEERROR_TCC: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_TCC); + break; + case VL53L0_DEVICEERROR_PHASECONSISTENCY: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_PHASECONSISTENCY); + break; + case VL53L0_DEVICEERROR_MINCLIP: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_MINCLIP); + break; + case VL53L0_DEVICEERROR_RANGECOMPLETE: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_RANGECOMPLETE); + break; + case VL53L0_DEVICEERROR_ALGOUNDERFLOW: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_ALGOUNDERFLOW); + break; + case VL53L0_DEVICEERROR_ALGOOVERFLOW: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_ALGOOVERFLOW); + break; + case VL53L0_DEVICEERROR_RANGEIGNORETHRESHOLD: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_DEVICEERROR_RANGEIGNORETHRESHOLD); + break; + + default: + VL53L0_COPYSTRING(pDeviceErrorString, + VL53L0_STRING_UNKNOW_ERROR_CODE); + + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_get_range_status_string(uint8_t RangeStatus, + char *pRangeStatusString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + switch (RangeStatus) { + case 0: + VL53L0_COPYSTRING(pRangeStatusString, + VL53L0_STRING_RANGESTATUS_RANGEVALID); + break; + case 1: + VL53L0_COPYSTRING(pRangeStatusString, + VL53L0_STRING_RANGESTATUS_SIGMA); + break; + case 2: + VL53L0_COPYSTRING(pRangeStatusString, + VL53L0_STRING_RANGESTATUS_SIGNAL); + break; + case 3: + VL53L0_COPYSTRING(pRangeStatusString, + VL53L0_STRING_RANGESTATUS_MINRANGE); + break; + case 4: + VL53L0_COPYSTRING(pRangeStatusString, + VL53L0_STRING_RANGESTATUS_PHASE); + break; + case 5: + VL53L0_COPYSTRING(pRangeStatusString, + VL53L0_STRING_RANGESTATUS_HW); + break; + + default: /**/ + VL53L0_COPYSTRING(pRangeStatusString, + VL53L0_STRING_RANGESTATUS_NONE); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_get_pal_error_string(VL53L0_Error PalErrorCode, + char *pPalErrorString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + switch (PalErrorCode) { + case VL53L0_ERROR_NONE: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_NONE); + break; + case VL53L0_ERROR_CALIBRATION_WARNING: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_CALIBRATION_WARNING); + break; + case VL53L0_ERROR_MIN_CLIPPED: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_MIN_CLIPPED); + break; + case VL53L0_ERROR_UNDEFINED: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_UNDEFINED); + break; + case VL53L0_ERROR_INVALID_PARAMS: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_INVALID_PARAMS); + break; + case VL53L0_ERROR_NOT_SUPPORTED: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_NOT_SUPPORTED); + break; + case VL53L0_ERROR_INTERRUPT_NOT_CLEARED: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_INTERRUPT_NOT_CLEARED); + break; + case VL53L0_ERROR_RANGE_ERROR: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_RANGE_ERROR); + break; + case VL53L0_ERROR_TIME_OUT: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_TIME_OUT); + break; + case VL53L0_ERROR_MODE_NOT_SUPPORTED: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_MODE_NOT_SUPPORTED); + break; + case VL53L0_ERROR_BUFFER_TOO_SMALL: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_BUFFER_TOO_SMALL); + break; + case VL53L0_ERROR_GPIO_NOT_EXISTING: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_GPIO_NOT_EXISTING); + break; + case VL53L0_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED); + break; + case VL53L0_ERROR_CONTROL_INTERFACE: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_CONTROL_INTERFACE); + break; + case VL53L0_ERROR_INVALID_COMMAND: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_INVALID_COMMAND); + break; + case VL53L0_ERROR_DIVISION_BY_ZERO: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_DIVISION_BY_ZERO); + break; + case VL53L0_ERROR_REF_SPAD_INIT: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_REF_SPAD_INIT); + break; + case VL53L0_ERROR_NOT_IMPLEMENTED: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_ERROR_NOT_IMPLEMENTED); + break; + + default: + VL53L0_COPYSTRING(pPalErrorString, + VL53L0_STRING_UNKNOW_ERROR_CODE); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_get_pal_state_string(VL53L0_State PalStateCode, + char *pPalStateString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + switch (PalStateCode) { + case VL53L0_STATE_POWERDOWN: + VL53L0_COPYSTRING(pPalStateString, + VL53L0_STRING_STATE_POWERDOWN); + break; + case VL53L0_STATE_WAIT_STATICINIT: + VL53L0_COPYSTRING(pPalStateString, + VL53L0_STRING_STATE_WAIT_STATICINIT); + break; + case VL53L0_STATE_STANDBY: + VL53L0_COPYSTRING(pPalStateString, + VL53L0_STRING_STATE_STANDBY); + break; + case VL53L0_STATE_IDLE: + VL53L0_COPYSTRING(pPalStateString, + VL53L0_STRING_STATE_IDLE); + break; + case VL53L0_STATE_RUNNING: + VL53L0_COPYSTRING(pPalStateString, + VL53L0_STRING_STATE_RUNNING); + break; + case VL53L0_STATE_UNKNOWN: + VL53L0_COPYSTRING(pPalStateString, + VL53L0_STRING_STATE_UNKNOWN); + break; + case VL53L0_STATE_ERROR: + VL53L0_COPYSTRING(pPalStateString, + VL53L0_STRING_STATE_ERROR); + break; + + default: + VL53L0_COPYSTRING(pPalStateString, + VL53L0_STRING_STATE_UNKNOWN); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L0_Error VL53L0_get_sequence_steps_info( + VL53L0_SequenceStepId SequenceStepId, + char *pSequenceStepsString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + switch (SequenceStepId) { + case VL53L0_SEQUENCESTEP_TCC: + VL53L0_COPYSTRING(pSequenceStepsString, + VL53L0_STRING_SEQUENCESTEP_TCC); + break; + case VL53L0_SEQUENCESTEP_DSS: + VL53L0_COPYSTRING(pSequenceStepsString, + VL53L0_STRING_SEQUENCESTEP_DSS); + break; + case VL53L0_SEQUENCESTEP_MSRC: + VL53L0_COPYSTRING(pSequenceStepsString, + VL53L0_STRING_SEQUENCESTEP_MSRC); + break; + case VL53L0_SEQUENCESTEP_PRE_RANGE: + VL53L0_COPYSTRING(pSequenceStepsString, + VL53L0_STRING_SEQUENCESTEP_PRE_RANGE); + break; + case VL53L0_SEQUENCESTEP_FINAL_RANGE: + VL53L0_COPYSTRING(pSequenceStepsString, + VL53L0_STRING_SEQUENCESTEP_FINAL_RANGE); + break; + + default: + Status = VL53L0_ERROR_INVALID_PARAMS; + } + + LOG_FUNCTION_END(Status); + + return Status; +} + + +VL53L0_Error VL53L0_get_limit_check_info(VL53L0_DEV Dev, uint16_t LimitCheckId, + char *pLimitCheckString) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + + switch (LimitCheckId) { + case VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE: + VL53L0_COPYSTRING(pLimitCheckString, + VL53L0_STRING_CHECKENABLE_SIGMA_FINAL_RANGE); + break; + case VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: + VL53L0_COPYSTRING(pLimitCheckString, + VL53L0_STRING_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE); + break; + case VL53L0_CHECKENABLE_SIGNAL_REF_CLIP: + VL53L0_COPYSTRING(pLimitCheckString, + VL53L0_STRING_CHECKENABLE_SIGNAL_REF_CLIP); + break; + case VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD: + VL53L0_COPYSTRING(pLimitCheckString, + VL53L0_STRING_CHECKENABLE_RANGE_IGNORE_THRESHOLD); + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_MSRC: + VL53L0_COPYSTRING(pLimitCheckString, + VL53L0_STRING_CHECKENABLE_SIGNAL_RATE_MSRC); + break; + + case VL53L0_CHECKENABLE_SIGNAL_RATE_PRE_RANGE: + VL53L0_COPYSTRING(pLimitCheckString, + VL53L0_STRING_CHECKENABLE_SIGNAL_RATE_PRE_RANGE); + break; + + default: + VL53L0_COPYSTRING(pLimitCheckString, + VL53L0_STRING_UNKNOW_ERROR_CODE); + + } + + LOG_FUNCTION_END(Status); + return Status; +} diff --git a/drivers/input/misc/vl53L0/src/vl53l0_i2c_platform.c b/drivers/input/misc/vl53L0/src/vl53l0_i2c_platform.c new file mode 100644 index 000000000000..1b5e4d4ab0ce --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l0_i2c_platform.c @@ -0,0 +1,380 @@ +/* + * COPYRIGHT (C) STMicroelectronics 2015. All rights reserved. + * + * This software is the confidential and proprietary information of + * STMicroelectronics ("Confidential Information"). You shall not + * disclose such Confidential Information and shall use it only in + * accordance with the terms of the license agreement you entered into + * with STMicroelectronics + * + * Programming Golden Rule: Keep it Simple! + * + */ + +/*! + * \file VL53L0_platform.c + * \brief Code function defintions for EWOK Platform Layer + * + */ + + +#include +#include +#include +#include "stmvl53l0-i2c.h" +#include "vl53l0_platform.h" +#include "vl53l0_i2c_platform.h" +#include "vl53l0_def.h" + +#include "vl53l0_platform_log.h" + +#ifdef VL53L0_LOG_ENABLE +#define trace_print(level, ...) \ + trace_print_module_function(TRACE_MODULE_PLATFORM, level,\ + TRACE_FUNCTION_NONE, ##__VA_ARGS__) +#define trace_i2c(...) \ + trace_print_module_function(TRACE_MODULE_NONE, \ + TRACE_LEVEL_NONE, TRACE_FUNCTION_I2C, ##__VA_ARGS__) +#endif + +/** + * @def I2C_BUFFER_CONFIG + * + * @brief Configure Device register I2C access + * + * @li 0 : one GLOBAL buffer \n + * Use one global buffer of MAX_I2C_XFER_SIZE byte in data space \n + * This solution is not multi-Device compliant nor multi-thread cpu safe \n + * It can be the best option for small 8/16 bit MCU without stack and limited + * ram (STM8s, 80C51 ...) + * + * @li 1 : ON_STACK/local \n + * Use local variable (on stack) buffer \n + * This solution is multi-thread with use of i2c resource lock or mutex see + * VL6180x_GetI2CAccess() \n + * + * @li 2 : User defined \n + * Per Device potentially dynamic allocated. Requires VL6180x_GetI2cBuffer() + * to be implemented. + * @ingroup Configuration + */ +#define I2C_BUFFER_CONFIG 1 + +#if I2C_BUFFER_CONFIG == 0 + /* GLOBAL config buffer */ + uint8_t i2c_global_buffer[VL53L0_MAX_I2C_XFER_SIZE]; + + #define DECL_I2C_BUFFER + #define VL53L0_GetLocalBuffer(Dev, n_byte) i2c_global_buffer + +#elif I2C_BUFFER_CONFIG == 1 + /* ON STACK */ + uint8_t LocBuffer[VL53L0_MAX_I2C_XFER_SIZE]; + #define VL53L0_GetLocalBuffer(Dev, n_byte) LocBuffer +#elif I2C_BUFFER_CONFIG == 2 + /* user define buffer type declare DECL_I2C_BUFFER as access via + * VL53L0_GetLocalBuffer + */ + #define DECL_I2C_BUFFER +#else +#error "invalid I2C_BUFFER_CONFIG " +#endif + + +/* none but could be for a flag var to + * get/pass to mutex interruptible return flags and try again + */ +#define VL53L0_I2C_USER_VAR +#define VL53L0_GetI2CAccess(Dev) /* todo mutex acquire */ +#define VL53L0_DoneI2CAcces(Dev) /* todo mutex release */ + + +char debug_string[VL53L0_MAX_STRING_LENGTH_PLT]; + + +#define MIN_COMMS_VERSION_MAJOR 1 +#define MIN_COMMS_VERSION_MINOR 8 +#define MIN_COMMS_VERSION_BUILD 1 +#define MIN_COMMS_VERSION_REVISION 0 + +#define STATUS_OK 0x00 +#define STATUS_FAIL 0x01 + +bool_t _check_min_version(void) +{ + bool_t min_version_comms_dll = false; + + min_version_comms_dll = true; + + return min_version_comms_dll; +} + +int32_t VL53L0_comms_initialise(uint8_t comms_type, uint16_t comms_speed_khz) +{ + int32_t status = STATUS_OK; + + return status; +} + +int32_t VL53L0_comms_close(void) +{ + int32_t status = STATUS_OK; + + + return status; +} + +int32_t VL53L0_set_page(VL53L0_DEV dev, uint8_t page_data) +{ + int32_t status = STATUS_OK; + uint16_t page_index = 0xFF; + uint8_t *buffer; + + buffer = VL53L0_GetLocalBuffer(dev, 3); + buffer[0] = page_index >> 8; + buffer[1] = page_index & 0xff; + buffer[2] = page_data; + + status = VL53L0_I2CWrite(dev, buffer, (uint8_t) 3); + return status; +} + +int32_t VL53L0_write_multi(VL53L0_DEV dev, uint8_t index, uint8_t *pdata, + int32_t count) +{ + int32_t status = STATUS_OK; + uint8_t *buffer; + +#ifdef VL53L0_LOG_ENABLE + int32_t i = 0; + char value_as_str[VL53L0_MAX_STRING_LENGTH_PLT]; + char *pvalue_as_str; + + pvalue_as_str = value_as_str; + + for (i = 0 ; i < count ; i++) { + snprintf(pvalue_as_str, sizeof(pvalue_as_str), + "%02X", *(pdata + i)); + + pvalue_as_str += 2; + } + trace_i2c("Write reg : 0x%04X, Val : 0x%s\n", index, value_as_str); +#endif + if ((count + 1) > VL53L0_MAX_I2C_XFER_SIZE) + return STATUS_FAIL; + buffer = VL53L0_GetLocalBuffer(dev, (count+1)); + buffer[0] = index; + memcpy(&buffer[1], pdata, count); + status = VL53L0_I2CWrite(dev, buffer, (count+1)); + + return status; +} + +int32_t VL53L0_read_multi(VL53L0_DEV dev, uint8_t index, uint8_t *pdata, + int32_t count) +{ + int32_t status = STATUS_OK; + uint8_t *buffer; + +#ifdef VL53L0_LOG_ENABLE + int32_t i = 0; + char value_as_str[VL53L0_MAX_STRING_LENGTH_PLT]; + char *pvalue_as_str; +#endif + + if ((count + 1) > VL53L0_MAX_I2C_XFER_SIZE) + return STATUS_FAIL; + + buffer = VL53L0_GetLocalBuffer(dev, 1); + buffer[0] = index; + status = VL53L0_I2CWrite(dev, (uint8_t *)buffer, (uint8_t)1); + if (!status) { + pdata[0] = index; + status = VL53L0_I2CRead(dev, pdata, count); + } + +#ifdef VL53L0_LOG_ENABLE + pvalue_as_str = value_as_str; + + for (i = 0 ; i < count ; i++) { + snprintf(pvalue_as_str, sizeof(value_as_str), + "%02X", *(pdata+i)); + pvalue_as_str += 2; + } + + trace_i2c("Read reg : 0x%04X, Val : 0x%s\n", index, value_as_str); +#endif + + return status; +} + + +int32_t VL53L0_write_byte(VL53L0_DEV dev, uint8_t index, uint8_t data) +{ + int32_t status = STATUS_OK; + const int32_t cbyte_count = 1; + + status = VL53L0_write_multi(dev, index, &data, cbyte_count); + + return status; + +} + + +int32_t VL53L0_write_word(VL53L0_DEV dev, uint8_t index, uint16_t data) +{ + int32_t status = STATUS_OK; + + uint8_t buffer[BYTES_PER_WORD]; + + /* Split 16-bit word into MS and LS uint8_t */ + buffer[0] = (uint8_t)(data >> 8); + buffer[1] = (uint8_t)(data & 0x00FF); + + status = VL53L0_write_multi(dev, index, buffer, BYTES_PER_WORD); + + return status; + +} + + +int32_t VL53L0_write_dword(VL53L0_DEV dev, uint8_t index, uint32_t data) +{ + int32_t status = STATUS_OK; + uint8_t buffer[BYTES_PER_DWORD]; + + /* Split 32-bit word into MS ... LS bytes */ + buffer[0] = (uint8_t) (data >> 24); + buffer[1] = (uint8_t)((data & 0x00FF0000) >> 16); + buffer[2] = (uint8_t)((data & 0x0000FF00) >> 8); + buffer[3] = (uint8_t) (data & 0x000000FF); + + status = VL53L0_write_multi(dev, index, buffer, BYTES_PER_DWORD); + + return status; + +} + + +int32_t VL53L0_read_byte(VL53L0_DEV dev, uint8_t index, uint8_t *pdata) +{ + int32_t status = STATUS_OK; + int32_t cbyte_count = 1; + + status = VL53L0_read_multi(dev, index, pdata, cbyte_count); + + return status; + +} + + +int32_t VL53L0_read_word(VL53L0_DEV dev, uint8_t index, uint16_t *pdata) +{ + int32_t status = STATUS_OK; + uint8_t buffer[BYTES_PER_WORD]; + + status = VL53L0_read_multi(dev, index, buffer, BYTES_PER_WORD); + *pdata = ((uint16_t)buffer[0]<<8) + (uint16_t)buffer[1]; + + return status; + +} + +int32_t VL53L0_read_dword(VL53L0_DEV dev, uint8_t index, uint32_t *pdata) +{ + int32_t status = STATUS_OK; + uint8_t buffer[BYTES_PER_DWORD]; + + status = VL53L0_read_multi(dev, index, buffer, BYTES_PER_DWORD); + *pdata = ((uint32_t)buffer[0]<<24) + ((uint32_t)buffer[1]<<16) + + ((uint32_t)buffer[2]<<8) + (uint32_t)buffer[3]; + + return status; + +} + +int32_t VL53L0_platform_wait_us(int32_t wait_us) +{ + int32_t status = STATUS_OK; + + msleep((wait_us/1000)); + +#ifdef VL53L0_LOG_ENABLE + trace_i2c("Wait us : %6d\n", wait_us); +#endif + + return status; + +} + + +int32_t VL53L0_wait_ms(int32_t wait_ms) +{ + int32_t status = STATUS_OK; + + msleep(wait_ms); + +#ifdef VL53L0_LOG_ENABLE + trace_i2c("Wait ms : %6d\n", wait_ms); +#endif + + return status; + +} + + +int32_t VL53L0_set_gpio(uint8_t level) +{ + int32_t status = STATUS_OK; +#ifdef VL53L0_LOG_ENABLE + trace_i2c("// Set GPIO = %d;\n", level); +#endif + + return status; + +} + + +int32_t VL53L0_get_gpio(uint8_t *plevel) +{ + int32_t status = STATUS_OK; +#ifdef VL53L0_LOG_ENABLE + trace_i2c("// Get GPIO = %d;\n", *plevel); +#endif + return status; +} + + +int32_t VL53L0_release_gpio(void) +{ + int32_t status = STATUS_OK; +#ifdef VL53L0_LOG_ENABLE + trace_i2c("// Releasing force on GPIO\n"); +#endif + return status; + +} + +int32_t VL53L0_cycle_power(void) +{ + int32_t status = STATUS_OK; +#ifdef VL53L0_LOG_ENABLE + trace_i2c("// cycle sensor power\n"); +#endif + + return status; +} + + +int32_t VL53L0_get_timer_frequency(int32_t *ptimer_freq_hz) +{ + *ptimer_freq_hz = 0; + return STATUS_FAIL; +} + + +int32_t VL53L0_get_timer_value(int32_t *ptimer_count) +{ + *ptimer_count = 0; + return STATUS_FAIL; +} diff --git a/drivers/input/misc/vl53L0/src/vl53l0_platform.c b/drivers/input/misc/vl53L0/src/vl53l0_platform.c new file mode 100644 index 000000000000..f7292ab6f8f2 --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l0_platform.c @@ -0,0 +1,242 @@ +/******************************************************************************* + * Copyright © 2016, STMicroelectronics International N.V. +All rights reserved. + +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 STMicroelectronics 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, FITNESS FOR A PARTICULAR PURPOSE, AND +NON-INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS ARE DISCLAIMED. +IN NO EVENT SHALL STMICROELECTRONICS INTERNATIONAL N.V. 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. +*******************************************************************************/ + +/** + * @file VL53L0_i2c.c + * + * Copyright (C) 2014 ST MicroElectronics + * + * provide variable word size byte/Word/dword VL6180x register access via i2c + * + */ +#include "vl53l0_platform.h" +#include "vl53l0_i2c_platform.h" +#include "vl53l0_api.h" + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(TRACE_MODULE_PLATFORM, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(TRACE_MODULE_PLATFORM, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...)\ + _LOG_FUNCTION_END_FMT(TRACE_MODULE_PLATFORM, status,\ + fmt, ##__VA_ARGS__) + + + +VL53L0_Error VL53L0_LockSequenceAccess(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + return Status; +} + +VL53L0_Error VL53L0_UnlockSequenceAccess(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + + return Status; +} + +/* the ranging_sensor_comms.dll will take care of the page selection */ +VL53L0_Error VL53L0_WriteMulti(VL53L0_DEV Dev, uint8_t index, + uint8_t *pdata, uint32_t count) +{ + + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t status_int = 0; + uint8_t deviceAddress; + + if (count >= VL53L0_MAX_I2C_XFER_SIZE) + Status = VL53L0_ERROR_INVALID_PARAMS; + + + deviceAddress = Dev->I2cDevAddr; + + status_int = VL53L0_write_multi(Dev, index, pdata, count); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + return Status; +} + +/* the ranging_sensor_comms.dll will take care of the page selection */ +VL53L0_Error VL53L0_ReadMulti(VL53L0_DEV Dev, uint8_t index, + uint8_t *pdata, uint32_t count) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t status_int; + uint8_t deviceAddress; + + if (count >= VL53L0_MAX_I2C_XFER_SIZE) + Status = VL53L0_ERROR_INVALID_PARAMS; + + + deviceAddress = Dev->I2cDevAddr; + + status_int = VL53L0_read_multi(Dev, index, pdata, count); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + return Status; +} + + +VL53L0_Error VL53L0_WrByte(VL53L0_DEV Dev, uint8_t index, uint8_t data) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t status_int; + uint8_t deviceAddress; + + deviceAddress = Dev->I2cDevAddr; + + status_int = VL53L0_write_byte(Dev, index, data); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + return Status; +} + +VL53L0_Error VL53L0_WrWord(VL53L0_DEV Dev, uint8_t index, uint16_t data) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t status_int; + uint8_t deviceAddress; + + deviceAddress = Dev->I2cDevAddr; + + status_int = VL53L0_write_word(Dev, index, data); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + return Status; +} + +VL53L0_Error VL53L0_WrDWord(VL53L0_DEV Dev, uint8_t index, uint32_t data) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t status_int; + uint8_t deviceAddress; + + deviceAddress = Dev->I2cDevAddr; + + status_int = VL53L0_write_dword(Dev, index, data); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + return Status; +} + +VL53L0_Error VL53L0_UpdateByte(VL53L0_DEV Dev, uint8_t index, + uint8_t AndData, uint8_t OrData) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t status_int; + uint8_t deviceAddress; + uint8_t data; + + deviceAddress = Dev->I2cDevAddr; + + status_int = VL53L0_read_byte(Dev, index, &data); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + if (Status == VL53L0_ERROR_NONE) { + data = (data & AndData) | OrData; + status_int = VL53L0_write_byte(Dev, index, data); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + } + + return Status; +} + +VL53L0_Error VL53L0_RdByte(VL53L0_DEV Dev, uint8_t index, uint8_t *data) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t status_int; + uint8_t deviceAddress; + + deviceAddress = Dev->I2cDevAddr; + + status_int = VL53L0_read_byte(Dev, index, data); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + return Status; +} + +VL53L0_Error VL53L0_RdWord(VL53L0_DEV Dev, uint8_t index, uint16_t *data) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t status_int; + uint8_t deviceAddress; + + deviceAddress = Dev->I2cDevAddr; + + status_int = VL53L0_read_word(Dev, index, data); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + return Status; +} + +VL53L0_Error VL53L0_RdDWord(VL53L0_DEV Dev, uint8_t index, uint32_t *data) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + int32_t status_int; + uint8_t deviceAddress; + + deviceAddress = Dev->I2cDevAddr; + + status_int = VL53L0_read_dword(Dev, index, data); + + if (status_int != 0) + Status = VL53L0_ERROR_CONTROL_INTERFACE; + + return Status; +} + +#define VL53L0_POLLINGDELAY_LOOPNB 250 +VL53L0_Error VL53L0_PollingDelay(VL53L0_DEV Dev) +{ + VL53L0_Error status = VL53L0_ERROR_NONE; + + LOG_FUNCTION_START(""); + usleep_range(950, 1000); + LOG_FUNCTION_END(status); + return status; +} diff --git a/drivers/input/misc/vl53L0/src/vl53l0_port_i2c.c b/drivers/input/misc/vl53L0/src/vl53l0_port_i2c.c new file mode 100644 index 000000000000..b204c776d803 --- /dev/null +++ b/drivers/input/misc/vl53L0/src/vl53l0_port_i2c.c @@ -0,0 +1,91 @@ +/* + * vl53l0_port_i2c.c + * + * Created on: July, 2015 + * Author: Teresa Tao + */ + +#include +#include +#include "stmvl53l0-i2c.h" +#include "vl53l0_platform.h" +#include "vl53l0_i2c_platform.h" +#include "stmvl53l0.h" + +#define I2C_M_WR 0x00 +#define STATUS_OK 0x00 +#define STATUS_FAIL (-1) +/** int VL53L0_I2CWrite(VL53L0_Dev_t dev, void *buff, uint8_t len); + * @brief Write data buffer to VL53L0 device via i2c + * @param dev The device to write to + * @param buff The data buffer + * @param len The length of the transaction in byte + * @return 0 on success + */ +int VL53L0_I2CWrite(VL53L0_DEV dev, uint8_t *buff, uint8_t len) +{ + int err = 0; + + if (dev->bus_type == CCI_BUS) { + + } else { + struct i2c_msg msg[1]; + struct i2c_data *i2c_client_obj = + (struct i2c_data *)dev->client_object; + struct i2c_client *client = + (struct i2c_client *)i2c_client_obj->client; + + msg[0].addr = client->addr; + msg[0].flags = I2C_M_WR; + msg[0].buf = buff; + msg[0].len = len; + + err = i2c_transfer(client->adapter, msg, 1); + /* return the actual messages transfer */ + if (err != 1) { + pr_err("%s: i2c_transfer err:%d, addr:0x%x, reg:0x%x\n", + __func__, err, client->addr, + (buff[0] << 8 | buff[1])); + return STATUS_FAIL; + } + } + + return 0; +} + + +/** int VL53L0_I2CRead(VL53L0_Dev_t dev, void *buff, uint8_t len); + * @brief Read data buffer from VL53L0 device via i2c + * @param dev The device to read from + * @param buff The data buffer to fill + * @param len The length of the transaction in byte + * @return transaction status + */ +int VL53L0_I2CRead(VL53L0_DEV dev, uint8_t *buff, uint8_t len) +{ + + int err = 0; + + if (dev->bus_type == CCI_BUS) { + } else { + struct i2c_msg msg[1]; + struct i2c_data *i2c_client_obj = + (struct i2c_data *)dev->client_object; + struct i2c_client *client = + (struct i2c_client *) i2c_client_obj->client; + + msg[0].addr = client->addr; + msg[0].flags = I2C_M_RD|client->flags; + msg[0].buf = buff; + msg[0].len = len; + + err = i2c_transfer(client->adapter, &msg[0], 1); + if (err != 1) { + pr_err("%s: Read i2c_transfer err:%d, addr:0x%x\n", + __func__, err, client->addr); + return STATUS_FAIL; + } + } + + return 0; +} diff --git a/drivers/input/misc/vl53L0/stmvl53l0-i2c.h b/drivers/input/misc/vl53L0/stmvl53l0-i2c.h new file mode 100644 index 000000000000..26968cba2e81 --- /dev/null +++ b/drivers/input/misc/vl53L0/stmvl53l0-i2c.h @@ -0,0 +1,36 @@ +/* + * stmvl53l0-i2c.h - Linux kernel modules for STM VL53L0 FlightSense TOF sensor + * + * Copyright (C) 2016 STMicroelectronics Imaging Division + * + * 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. + */ +/* + * Defines + */ +#ifndef STMVL53L0_I2C_H +#define STMVL53L0_I2C_H +#include +#include + +struct i2c_data { + struct i2c_client *client; + struct regulator *vana; + uint8_t power_up; + uint32_t xtalk; + struct msm_camera_gpio_conf gconf; + struct msm_pinctrl_info pinctrl_info; +}; +int stmvl53l0_init_i2c(void); +void stmvl53l0_exit_i2c(void *); +int stmvl53l0_power_up_i2c(void *, unsigned int *); +int stmvl53l0_power_down_i2c(void *); +#endif /* STMVL53L0_I2C_H */ diff --git a/drivers/input/misc/vl53L0/stmvl53l0.h b/drivers/input/misc/vl53L0/stmvl53l0.h new file mode 100644 index 000000000000..54ccff920a6c --- /dev/null +++ b/drivers/input/misc/vl53L0/stmvl53l0.h @@ -0,0 +1,221 @@ +/* + * stmvl53l0.h - Linux kernel modules for STM VL53L0 FlightSense TOF sensor + * + * Copyright (C) 2016 STMicroelectronics Imaging Division + * + * 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. + */ +/* + * Defines + */ +#ifndef STMVL53L0_H +#define STMVL53L0_H + +#include +#include +#include +#include +#include + +#define STMVL53L0_DRV_NAME "stmvl53l0" +#define STMVL53L0_SLAVE_ADDR (0x52>>1) + +#define DRIVER_VERSION "1.1.23" +#define I2C_M_WR 0x00 +/* #define INT_POLLING_DELAY 20 */ + +/* if don't want to have output from vl53l0_dbgmsg, comment out #DEBUG macro */ +//#define DEBUG +#ifdef DEBUG +#define vl53l0_dbgmsg(str, args...) \ + pr_err("%s: " str, __func__, ##args) +#else +#define vl53l0_dbgmsg(str, args...) \ + pr_debug("%s: " str, __func__, ##args) +#endif +#define vl53l0_errmsg(str, args...) \ + pr_err("%s: " str, __func__, ##args) + +#define VL53L0_VDD_MIN 2600000 +#define VL53L0_VDD_MAX 3000000 + +typedef enum { + NORMAL_MODE = 0, + OFFSETCALIB_MODE = 1, + XTALKCALIB_MODE = 2, +} init_mode_e; + +typedef enum { + OFFSET_PAR = 0, + XTALKRATE_PAR = 1, + XTALKENABLE_PAR = 2, + GPIOFUNC_PAR = 3, + LOWTHRESH_PAR = 4, + HIGHTHRESH_PAR = 5, + DEVICEMODE_PAR = 6, + INTERMEASUREMENT_PAR = 7, + REFERENCESPADS_PAR = 8, + REFCALIBRATION_PAR = 9, +} parameter_name_e; + +enum { + CCI_BUS = 0, + I2C_BUS = 1, +}; + +/* + * IOCTL register data structs + */ +struct stmvl53l0_register { + uint32_t is_read; /*1: read 0: write*/ + uint32_t reg_index; + uint32_t reg_bytes; + uint32_t reg_data; + int32_t status; +}; + +/* + * IOCTL parameter structs + */ +struct stmvl53l0_parameter { + uint32_t is_read; /*1: Get 0: Set*/ + parameter_name_e name; + int32_t value; + int32_t value2; + int32_t status; +}; + +/* + * IOCTL Custom Use Case + */ +struct stmvl53l0_custom_use_case { + FixPoint1616_t signalRateLimit; + FixPoint1616_t sigmaLimit; + uint32_t preRangePulsePeriod; + uint32_t finalRangePulsePeriod; + uint32_t timingBudget; +}; + +/* + * driver data structs + */ +struct stmvl53l0_data { + + /* ! +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * power specific includes + */ +#include +#include +#include +#include +#include +/* + * API includes + */ +#include "vl53l0_api.h" +#include "vl53l0_def.h" +#include "vl53l0_platform.h" +#include "stmvl53l0-i2c.h" +#include "stmvl53l0.h" + +#define LASER_SENSOR_PINCTRL_STATE_SLEEP "laser_suspend" +#define LASER_SENSOR_PINCTRL_STATE_DEFAULT "laser_default" + +/* + * Global data + */ +static int stmvl53l0_parse_vdd(struct device *dev, struct i2c_data *data); + +/* + * QCOM specific functions + */ +static int stmvl53l0_parse_vdd(struct device *dev, struct i2c_data *data) +{ + int ret = 0; + + vl53l0_dbgmsg("Enter\n"); + + if (dev->of_node) { + data->vana = regulator_get_optional(dev, "vdd"); + if (IS_ERR(data->vana)) { + vl53l0_errmsg("vdd supply is not provided\n"); + ret = -1; + } + } + vl53l0_dbgmsg("End\n"); + + return ret; +} +int get_dt_xtalk_data(struct device_node *of_node, int *xtalk) +{ + int rc = 0; + uint32_t count = 0; + uint32_t v_array[1]; + + count = of_property_count_strings(of_node, "st,xtalkval"); + + if (!count) + return 0; + + rc = of_property_read_u32_array(of_node, "st,xtalkval", + v_array, 1); + + if (rc != -EINVAL) { + if (rc < 0) + pr_err("%s failed %d\n", __func__, __LINE__); + else + *xtalk = v_array[0]; + } else + rc = 0; + + return rc; +} + +static int stmvl53l0_get_dt_gpio_req_tbl(struct device_node *of_node, + struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array, + uint16_t gpio_array_size) +{ + int rc = 0, i = 0; + uint32_t count = 0; + uint32_t *val_array = NULL; + + if (!of_get_property(of_node, "qcom,gpio-req-tbl-num", &count)) + return 0; + + count /= sizeof(uint32_t); + if (!count) { + pr_err("%s qcom,gpio-req-tbl-num 0\n", __func__); + return 0; + } + + val_array = kcalloc(count, sizeof(uint32_t), GFP_KERNEL); + if (!val_array) + return -ENOMEM; + + gconf->cam_gpio_req_tbl = kcalloc(count, sizeof(struct gpio), + GFP_KERNEL); + if (!gconf->cam_gpio_req_tbl) { + rc = -ENOMEM; + goto ERROR1; + } + gconf->cam_gpio_req_tbl_size = count; + + rc = of_property_read_u32_array(of_node, "qcom,gpio-req-tbl-num", + val_array, count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR2; + } + for (i = 0; i < count; i++) { + if (val_array[i] >= gpio_array_size) { + pr_err("%s gpio req tbl index %d invalid\n", + __func__, val_array[i]); + return -EINVAL; + } + gconf->cam_gpio_req_tbl[i].gpio = gpio_array[val_array[i]]; + vl53l0_dbgmsg("cam_gpio_req_tbl[%d].gpio = %d\n", i, + gconf->cam_gpio_req_tbl[i].gpio); + } + + rc = of_property_read_u32_array(of_node, "qcom,gpio-req-tbl-flags", + val_array, count); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR2; + } + for (i = 0; i < count; i++) { + gconf->cam_gpio_req_tbl[i].flags = val_array[i]; + vl53l0_dbgmsg("cam_gpio_req_tbl[%d].flags = %ld\n", i, + gconf->cam_gpio_req_tbl[i].flags); + } + + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(of_node, + "qcom,gpio-req-tbl-label", i, + &gconf->cam_gpio_req_tbl[i].label); + vl53l0_dbgmsg("cam_gpio_req_tbl[%d].label = %s\n", i, + gconf->cam_gpio_req_tbl[i].label); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + goto ERROR2; + } + } + + kfree(val_array); + return rc; + +ERROR2: + kfree(gconf->cam_gpio_req_tbl); +ERROR1: + kfree(val_array); + gconf->cam_gpio_req_tbl_size = 0; + return rc; +} + +static int stmvl53l0_init_gpio_pin_tbl(struct device_node *of_node, + struct msm_camera_gpio_conf *gconf, uint16_t *gpio_array, + uint16_t gpio_array_size) +{ + int rc = 0, val = 0; + + gconf->gpio_num_info = kzalloc(sizeof(struct msm_camera_gpio_num_info), + GFP_KERNEL); + if (!gconf->gpio_num_info) { + rc = -ENOMEM; + return rc; + } + + rc = of_property_read_u32(of_node, "qcom,gpio-vana", &val); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s:%d read qcom,gpio-vana failed rc %d\n", + __func__, __LINE__, rc); + goto ERROR; + } else if (val >= gpio_array_size) { + pr_err("%s:%d qcom,gpio-vana invalid %d\n", + __func__, __LINE__, val); + rc = -EINVAL; + goto ERROR; + } + gconf->gpio_num_info->gpio_num[1] = + gpio_array[val]; + gconf->gpio_num_info->valid[1] = 1; + vl53l0_dbgmsg("qcom,gpio-vana %d\n", + gconf->gpio_num_info->gpio_num[1]); + } else { + rc = 0; + } + + rc = of_property_read_u32(of_node, "qcom,gpio-reset", &val); + if (rc != -EINVAL) { + if (rc < 0) { + pr_err("%s:%d read qcom,gpio-reset failed rc %d\n", + __func__, __LINE__, rc); + goto ERROR; + } else if (val >= gpio_array_size) { + pr_err("%s:%d qcom,gpio-reset invalid %d\n", + __func__, __LINE__, val); + rc = -EINVAL; + goto ERROR; + } + gconf->gpio_num_info->gpio_num[0] = + gpio_array[val]; + gconf->gpio_num_info->valid[0] = 1; + vl53l0_dbgmsg("qcom,gpio-reset %d\n", + gconf->gpio_num_info->gpio_num[0]); + } else { + rc = 0; + } + + return rc; + +ERROR: + kfree(gconf->gpio_num_info); + gconf->gpio_num_info = NULL; + return rc; +} + + +static void stmvl53l0_request_gpio_table(struct gpio *gpio_tbl, uint8_t size, + int gpio_en) +{ + int i = 0, err = 1, retry_count = 0; + + if (!gpio_tbl || !size) { + pr_err("%s:%d invalid gpio_tbl %pK / size %d\n", __func__, + __LINE__, gpio_tbl, size); + } + for (i = 0; i < size; i++) { + vl53l0_dbgmsg("%d i %d, gpio %d dir %ld\n", __LINE__, i, + gpio_tbl[i].gpio, gpio_tbl[i].flags); + } + if (gpio_en) { + while (err && retry_count < 10) { + for (i = 0; i < size; i++) { + err = gpio_request_one(gpio_tbl[i].gpio, + gpio_tbl[i].flags, gpio_tbl[i].label); + if (err) { + /* + * After GPIO request fails, contine to + * apply new gpios, outout a error message + * for driver bringup debug + */ + pr_err("%s:%d gpio %d:%s request fails, retry_count %d\n", + __func__, __LINE__, + gpio_tbl[i].gpio, gpio_tbl[i].label, retry_count); + msleep(30); + retry_count++; + break; + } + } + } + } else { + gpio_free_array(gpio_tbl, size); + } +} + +static int stmvl53l0_get_dt_data(struct device *dev, struct i2c_data *data) +{ + int rc = 0; + struct msm_camera_gpio_conf *gconf; + uint16_t *gpio_array; + uint16_t gpio_array_size; + int i; + struct msm_pinctrl_info *sensor_pctrl; + + vl53l0_dbgmsg("Enter\n"); + + sensor_pctrl = &data->pinctrl_info; + sensor_pctrl->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(sensor_pctrl->pinctrl)) { + pr_err("%s:%d Getting pinctrl handle failed\n", + __func__, __LINE__); + return -EINVAL; + } + sensor_pctrl->gpio_state_active = + pinctrl_lookup_state(sensor_pctrl->pinctrl, + LASER_SENSOR_PINCTRL_STATE_DEFAULT); + + sensor_pctrl->gpio_state_suspend + = pinctrl_lookup_state(sensor_pctrl->pinctrl, + LASER_SENSOR_PINCTRL_STATE_SLEEP); + + if (dev->of_node) { + struct device_node *of_node = dev->of_node; + + gpio_array_size = of_gpio_count(of_node); + gconf = &data->gconf; + + if (gpio_array_size) { + gpio_array = kcalloc(gpio_array_size, sizeof(uint16_t), + GFP_KERNEL); + if (!gpio_array) + return -ENOMEM; + + for (i = 0; i < gpio_array_size; i++) { + gpio_array[i] = of_get_gpio(of_node, i); + pr_err("%s gpio_array[%d] = %d\n", __func__, i, + gpio_array[i]); + } + + rc = stmvl53l0_get_dt_gpio_req_tbl(of_node, gconf, + gpio_array, gpio_array_size); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + kfree(gpio_array); + return rc; + } + + rc = stmvl53l0_init_gpio_pin_tbl(of_node, gconf, + gpio_array, gpio_array_size); + if (rc < 0) { + pr_err("%s failed %d\n", __func__, __LINE__); + kfree(gpio_array); + return rc; + } + kfree(gpio_array); + } + rc = get_dt_xtalk_data(of_node, + &(data->xtalk)); + } + vl53l0_dbgmsg("End rc =%d\n", rc); + + return rc; +} +static int stmvl53l0_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0, i = 0; + struct stmvl53l0_data *vl53l0_data = NULL; + struct i2c_data *i2c_object = NULL; + int present; + + vl53l0_errmsg("Enter\n"); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { + rc = -EIO; + return rc; + } + + vl53l0_data = kzalloc(sizeof(struct stmvl53l0_data), GFP_KERNEL); + if (!vl53l0_data) { + rc = -ENOMEM; + return rc; + } + if (vl53l0_data) { + vl53l0_data->client_object = + kzalloc(sizeof(struct i2c_data), GFP_KERNEL); + i2c_object = (struct i2c_data *)vl53l0_data->client_object; + } + i2c_object->client = client; + + /* setup bus type */ + vl53l0_data->bus_type = I2C_BUS; + + /* setup regulator */ + stmvl53l0_parse_vdd(&i2c_object->client->dev, i2c_object); + + /* setup device name */ + vl53l0_data->dev_name = dev_name(&client->dev); + + /* setup device data */ + dev_set_drvdata(&client->dev, vl53l0_data); + + /* setup client data */ + i2c_set_clientdata(client, vl53l0_data); + + stmvl53l0_get_dt_data(&client->dev, i2c_object); + + /* fill xtalk data */ + vl53l0_data->xtalk= i2c_object->xtalk; + + rc = stmvl53l0_power_up_i2c(i2c_object, &present); + if (rc) { + vl53l0_errmsg("%d,error rc %d\n", __LINE__, rc); + return rc; + } + for (i = 0; i < 5; i++) { + rc = stmvl53l0_checkmoduleid + (vl53l0_data, i2c_object->client, I2C_BUS); + if (!rc) + break; + msleep(20); + } + if (rc != 0) { + stmvl53l0_power_down_i2c(i2c_object); + msleep(20); + stmvl53l0_power_up_i2c(i2c_object, &present); + for (i = 0; i < 10; i++) { + rc = stmvl53l0_checkmoduleid + (vl53l0_data, i2c_object->client, I2C_BUS); + if (!rc) + break; + msleep(20); + } + } + if (rc != 0) { + vl53l0_errmsg("%d,error rc %d\n", __LINE__, rc); + stmvl53l0_power_down_i2c(i2c_object); + } + + stmvl53l0_power_down_i2c(i2c_object); + + /* setup other stuff */ + rc = stmvl53l0_setup(vl53l0_data); + + /* init default value */ + i2c_object->power_up = 0; + + vl53l0_errmsg("End\n"); + return rc; +} + +static int stmvl53l0_remove(struct i2c_client *client) +{ + struct stmvl53l0_data *data = i2c_get_clientdata(client); + + vl53l0_dbgmsg("Enter\n"); + + /* Power down the device */ + stmvl53l0_power_down_i2c(data->client_object); + stmvl53l0_cleanup(data); + kfree(data->client_object); + kfree(data); + vl53l0_dbgmsg("End\n"); + return 0; +} + +static const struct i2c_device_id stmvl53l0_id[] = { + {STMVL53L0_DRV_NAME, 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, stmvl53l0_id); + +static const struct of_device_id st_stmvl53l0_dt_match[] = { + { .compatible = "st,stmvl53l0_i2c", }, + { }, +}; + +static struct i2c_driver stmvl53l0_driver = { + .driver = { + .name = STMVL53L0_DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = st_stmvl53l0_dt_match, + }, + .probe = stmvl53l0_probe, + .remove = stmvl53l0_remove, + .id_table = stmvl53l0_id, + +}; + +int stmvl53l0_power_up_i2c(void *i2c_object, unsigned int *preset_flag) +{ + int ret = 0; + + struct i2c_data *data = (struct i2c_data *)i2c_object; + + vl53l0_dbgmsg("Enter i2c powerup\n"); + + if (!IS_ERR(data->vana)) { + ret = regulator_set_voltage(data->vana, VL53L0_VDD_MIN, + VL53L0_VDD_MAX); + if (ret < 0) { + vl53l0_errmsg("set_vol(%p) fail %d\n", data->vana, ret); + return ret; + } + ret = regulator_enable(data->vana); + + if (ret < 0) { + vl53l0_errmsg("reg enable(%p) failed.rc=%d\n", + data->vana, ret); + return ret; + } + } + + stmvl53l0_request_gpio_table( + data->gconf.cam_gpio_req_tbl, + data->gconf.cam_gpio_req_tbl_size, 1); + + pinctrl_select_state(data->pinctrl_info.pinctrl, + data->pinctrl_info.gpio_state_active); + + gpio_set_value_cansleep(data->gconf.cam_gpio_req_tbl[1].gpio, 1); + vl53l0_dbgmsg("Enable gpio%d\n",data->gconf.cam_gpio_req_tbl[1].gpio ); + msleep(20); + gpio_set_value_cansleep(data->gconf.cam_gpio_req_tbl[0].gpio, 1); + vl53l0_dbgmsg("Enable gpio%d\n",data->gconf.cam_gpio_req_tbl[0].gpio ); + + stmvl53l0_request_gpio_table( + data->gconf.cam_gpio_req_tbl, + data->gconf.cam_gpio_req_tbl_size, 0); + + data->power_up = 1; + *preset_flag = 1; + + vl53l0_dbgmsg("End\n"); + return ret; +} + +int stmvl53l0_power_down_i2c(void *i2c_object) +{ + int ret = 0; + + struct i2c_data *data = (struct i2c_data *)i2c_object; + + vl53l0_dbgmsg("Enter\n"); + if (data->power_up) { + stmvl53l0_request_gpio_table( + data->gconf.cam_gpio_req_tbl, + data->gconf.cam_gpio_req_tbl_size, 1); + + pinctrl_select_state(data->pinctrl_info.pinctrl, + data->pinctrl_info.gpio_state_suspend); + + gpio_set_value_cansleep( + data->gconf.cam_gpio_req_tbl[0].gpio, 0); + vl53l0_dbgmsg("Disable gpio%d\n",data->gconf.cam_gpio_req_tbl[0].gpio ); + + /* HACK, no disable TOF_VANA as GPIO 70 is common for Prox and TOF*/ + //gpio_set_value_cansleep( + // data->gconf.cam_gpio_req_tbl[1].gpio, 0); + //vl53l0_dbgmsg("Disable gpio%d\n",data->gconf.cam_gpio_req_tbl[1].gpio ); + + stmvl53l0_request_gpio_table( + data->gconf.cam_gpio_req_tbl, + data->gconf.cam_gpio_req_tbl_size, 0); + + if (!IS_ERR(data->vana)) { + ret = regulator_disable(data->vana); + if (ret < 0) + vl53l0_errmsg("reg disable(%p) failed.rc=%d\n", + data->vana, ret); + } + + data->power_up = 0; + } + + vl53l0_dbgmsg("End\n"); + return ret; +} + +int stmvl53l0_init_i2c(void) +{ + int ret = 0; + + vl53l0_dbgmsg("Enter\n"); + + /* register as a i2c client device */ + ret = i2c_add_driver(&stmvl53l0_driver); + if (ret) + vl53l0_errmsg("%d erro ret:%d\n", __LINE__, ret); + + vl53l0_errmsg("End with rc:%d\n", ret); + + return ret; +} + +void stmvl53l0_exit_i2c(void *i2c_object) +{ + vl53l0_dbgmsg("Enter\n"); + i2c_del_driver(&stmvl53l0_driver); + + vl53l0_dbgmsg("End\n"); +} diff --git a/drivers/input/misc/vl53L0/stmvl53l0_module.c b/drivers/input/misc/vl53L0/stmvl53l0_module.c new file mode 100644 index 000000000000..63d9ddfc447b --- /dev/null +++ b/drivers/input/misc/vl53L0/stmvl53l0_module.c @@ -0,0 +1,2858 @@ +/* + * stmvl53l0_module.c - Linux kernel modules for STM VL53L0 FlightSense TOF + * sensor + * + * Copyright (C) 2016 STMicroelectronics Imaging Division. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * API includes + */ +#include "vl53l0_api.h" + +/*#define USE_INT */ +/* #define DEBUG_TIME_LOG */ +#ifdef DEBUG_TIME_LOG +struct timeval start_tv, stop_tv; +#endif + +/* + * Global data + */ +static char *devname = "laser"; +static struct stmvl53l0_module_fn_t stmvl53l0_module_func_tbl = { + .init = stmvl53l0_init_i2c, + .deinit = stmvl53l0_exit_i2c, + .power_up = stmvl53l0_power_up_i2c, + .power_down = stmvl53l0_power_down_i2c, +}; +struct stmvl53l0_module_fn_t *pmodule_func_tbl; + +struct stmvl53l0_api_fn_t { + int8_t (*GetVersion)(VL53L0_Version_t *pVersion); + int8_t (*GetPalSpecVersion)(VL53L0_Version_t *pPalSpecVersion); + + int8_t (*GetProductRevision)(VL53L0_DEV Dev, + uint8_t *pProductRevisionMajor, + uint8_t *pProductRevisionMinor); + int8_t (*GetDeviceInfo)(VL53L0_DEV Dev, + VL53L0_DeviceInfo_t *pVL53L0_DeviceInfo); + int8_t (*GetDeviceErrorStatus)(VL53L0_DEV Dev, + VL53L0_DeviceError *pDeviceErrorStatus); + int8_t (*GetRangeStatusString)(uint8_t RangeStatus, + char *pRangeStatusString); + int8_t (*GetDeviceErrorString)(VL53L0_DeviceError ErrorCode, + char *pDeviceErrorString); + int8_t (*GetPalErrorString)(VL53L0_Error PalErrorCode, + char *pPalErrorString); + int8_t (*GetPalStateString)(VL53L0_State PalStateCode, + char *pPalStateString); + int8_t (*GetPalState)(VL53L0_DEV Dev, VL53L0_State *pPalState); + int8_t (*SetPowerMode)(VL53L0_DEV Dev, + VL53L0_PowerModes PowerMode); + int8_t (*GetPowerMode)(VL53L0_DEV Dev, + VL53L0_PowerModes *pPowerMode); + int8_t (*SetOffsetCalibrationDataMicroMeter)(VL53L0_DEV Dev, + int32_t OffsetCalibrationDataMicroMeter); + int8_t (*GetOffsetCalibrationDataMicroMeter)(VL53L0_DEV Dev, + int32_t *pOffsetCalibrationDataMicroMeter); + int8_t (*SetLinearityCorrectiveGain)(VL53L0_DEV Dev, + int16_t LinearityCorrectiveGain); + int8_t (*GetLinearityCorrectiveGain)(VL53L0_DEV Dev, + uint16_t *pLinearityCorrectiveGain); + int8_t (*SetGroupParamHold)(VL53L0_DEV Dev, + uint8_t GroupParamHold); + int8_t (*GetUpperLimitMilliMeter)(VL53L0_DEV Dev, + uint16_t *pUpperLimitMilliMeter); + int8_t (*SetDeviceAddress)(VL53L0_DEV Dev, + uint8_t DeviceAddress); + int8_t (*DataInit)(VL53L0_DEV Dev); + int8_t (*SetTuningSettingBuffer)(VL53L0_DEV Dev, + uint8_t *pTuningSettingBuffer, + uint8_t UseInternalTuningSettings); + int8_t (*GetTuningSettingBuffer)(VL53L0_DEV Dev, + uint8_t **pTuningSettingBuffer, + uint8_t *pUseInternalTuningSettings); + int8_t (*StaticInit)(VL53L0_DEV Dev); + int8_t (*WaitDeviceBooted)(VL53L0_DEV Dev); + int8_t (*ResetDevice)(VL53L0_DEV Dev); + int8_t (*SetDeviceParameters)(VL53L0_DEV Dev, + const VL53L0_DeviceParameters_t *pDeviceParameters); + int8_t (*GetDeviceParameters)(VL53L0_DEV Dev, + VL53L0_DeviceParameters_t *pDeviceParameters); + int8_t (*SetDeviceMode)(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode); + int8_t (*GetDeviceMode)(VL53L0_DEV Dev, + VL53L0_DeviceModes *pDeviceMode); + int8_t (*SetHistogramMode)(VL53L0_DEV Dev, + VL53L0_HistogramModes HistogramMode); + int8_t (*GetHistogramMode)(VL53L0_DEV Dev, + VL53L0_HistogramModes *pHistogramMode); + int8_t (*SetMeasurementTimingBudgetMicroSeconds)(VL53L0_DEV Dev, + uint32_t MeasurementTimingBudgetMicroSeconds); + int8_t (*GetMeasurementTimingBudgetMicroSeconds)( + VL53L0_DEV Dev, + uint32_t *pMeasurementTimingBudgetMicroSeconds); + int8_t (*GetVcselPulsePeriod)(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, + uint8_t *pVCSELPulsePeriod); + int8_t (*SetVcselPulsePeriod)(VL53L0_DEV Dev, + VL53L0_VcselPeriod VcselPeriodType, + uint8_t VCSELPulsePeriod); + int8_t (*SetSequenceStepEnable)(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, + uint8_t SequenceStepEnabled); + int8_t (*GetSequenceStepEnable)(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, + uint8_t *pSequenceStepEnabled); + int8_t (*GetSequenceStepEnables)(VL53L0_DEV Dev, + VL53L0_SchedulerSequenceSteps_t *pSchedulerSequenceSteps); + int8_t (*SetSequenceStepTimeout)(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, + FixPoint1616_t TimeOutMilliSecs); + int8_t (*GetSequenceStepTimeout)(VL53L0_DEV Dev, + VL53L0_SequenceStepId SequenceStepId, + FixPoint1616_t *pTimeOutMilliSecs); + int8_t (*GetNumberOfSequenceSteps)(VL53L0_DEV Dev, + uint8_t *pNumberOfSequenceSteps); + int8_t (*GetSequenceStepsInfo)( + VL53L0_SequenceStepId SequenceStepId, + char *pSequenceStepsString); + int8_t (*SetInterMeasurementPeriodMilliSeconds)( + VL53L0_DEV Dev, + uint32_t InterMeasurementPeriodMilliSeconds); + int8_t (*GetInterMeasurementPeriodMilliSeconds)( + VL53L0_DEV Dev, + uint32_t *pInterMeasurementPeriodMilliSeconds); + int8_t (*SetXTalkCompensationEnable)(VL53L0_DEV Dev, + uint8_t XTalkCompensationEnable); + int8_t (*GetXTalkCompensationEnable)(VL53L0_DEV Dev, + uint8_t *pXTalkCompensationEnable); + int8_t (*SetXTalkCompensationRateMegaCps)( + VL53L0_DEV Dev, + FixPoint1616_t XTalkCompensationRateMegaCps); + int8_t (*GetXTalkCompensationRateMegaCps)( + VL53L0_DEV Dev, + FixPoint1616_t *pXTalkCompensationRateMegaCps); + int8_t (*GetNumberOfLimitCheck)( + uint16_t *pNumberOfLimitCheck); + int8_t (*GetLimitCheckInfo)(VL53L0_DEV Dev, + uint16_t LimitCheckId, char *pLimitCheckString); + int8_t (*SetLimitCheckEnable)(VL53L0_DEV Dev, + uint16_t LimitCheckId, + uint8_t LimitCheckEnable); + int8_t (*GetLimitCheckEnable)(VL53L0_DEV Dev, + uint16_t LimitCheckId, uint8_t *pLimitCheckEnable); + int8_t (*SetLimitCheckValue)(VL53L0_DEV Dev, + uint16_t LimitCheckId, + FixPoint1616_t LimitCheckValue); + int8_t (*GetLimitCheckValue)(VL53L0_DEV Dev, + uint16_t LimitCheckId, + FixPoint1616_t *pLimitCheckValue); + int8_t (*GetLimitCheckCurrent)(VL53L0_DEV Dev, + uint16_t LimitCheckId, FixPoint1616_t *pLimitCheckCurrent); + int8_t (*SetWrapAroundCheckEnable)(VL53L0_DEV Dev, + uint8_t WrapAroundCheckEnable); + int8_t (*GetWrapAroundCheckEnable)(VL53L0_DEV Dev, + uint8_t *pWrapAroundCheckEnable); + int8_t (*PerformSingleMeasurement)(VL53L0_DEV Dev); + int8_t (*PerformRefCalibration)(VL53L0_DEV Dev, + uint8_t *pVhvSettings, uint8_t *pPhaseCal); + int8_t (*SetRefCalibration)(VL53L0_DEV Dev, + uint8_t VhvSettings, + uint8_t PhaseCal); + int8_t (*GetRefCalibration)(VL53L0_DEV Dev, + uint8_t *pVhvSettings, + uint8_t *pPhaseCal); + int8_t (*PerformXTalkCalibration)(VL53L0_DEV Dev, + FixPoint1616_t XTalkCalDistance, + FixPoint1616_t *pXTalkCompensationRateMegaCps); + int8_t (*PerformOffsetCalibration)(VL53L0_DEV Dev, + FixPoint1616_t CalDistanceMilliMeter, + int32_t *pOffsetMicroMeter); + int8_t (*StartMeasurement)(VL53L0_DEV Dev); + int8_t (*StopMeasurement)(VL53L0_DEV Dev); + int8_t (*GetMeasurementDataReady)(VL53L0_DEV Dev, + uint8_t *pMeasurementDataReady); + int8_t (*WaitDeviceReadyForNewMeasurement)(VL53L0_DEV Dev, + uint32_t MaxLoop); + int8_t (*GetRangingMeasurementData)(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData); + int8_t (*GetHistogramMeasurementData)(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData); + int8_t (*PerformSingleRangingMeasurement)(VL53L0_DEV Dev, + VL53L0_RangingMeasurementData_t *pRangingMeasurementData); + int8_t (*PerformSingleHistogramMeasurement)(VL53L0_DEV Dev, + VL53L0_HistogramMeasurementData_t *pHistogramMeasurementData); + int8_t (*SetNumberOfROIZones)(VL53L0_DEV Dev, + uint8_t NumberOfROIZones); + int8_t (*GetNumberOfROIZones)(VL53L0_DEV Dev, + uint8_t *pNumberOfROIZones); + int8_t (*GetMaxNumberOfROIZones)(VL53L0_DEV Dev, + uint8_t *pMaxNumberOfROIZones); + int8_t (*SetGpioConfig)(VL53L0_DEV Dev, + uint8_t Pin, + VL53L0_DeviceModes DeviceMode, + VL53L0_GpioFunctionality Functionality, + VL53L0_InterruptPolarity Polarity); + int8_t (*GetGpioConfig)(VL53L0_DEV Dev, + uint8_t Pin, + VL53L0_DeviceModes *pDeviceMode, + VL53L0_GpioFunctionality *pFunctionality, + VL53L0_InterruptPolarity *pPolarity); + int8_t (*SetInterruptThresholds)(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode, + FixPoint1616_t ThresholdLow, + FixPoint1616_t ThresholdHigh); + int8_t (*GetInterruptThresholds)(VL53L0_DEV Dev, + VL53L0_DeviceModes DeviceMode, + FixPoint1616_t *pThresholdLow, + FixPoint1616_t *pThresholdHigh); + int8_t (*ClearInterruptMask)(VL53L0_DEV Dev, + uint32_t InterruptMask); + int8_t (*GetInterruptMaskStatus)(VL53L0_DEV Dev, + uint32_t *pInterruptMaskStatus); + int8_t (*EnableInterruptMask)(VL53L0_DEV Dev, uint32_t InterruptMask); + int8_t (*SetSpadAmbientDamperThreshold)(VL53L0_DEV Dev, + uint16_t SpadAmbientDamperThreshold); + int8_t (*GetSpadAmbientDamperThreshold)(VL53L0_DEV Dev, + uint16_t *pSpadAmbientDamperThreshold); + int8_t (*SetSpadAmbientDamperFactor)(VL53L0_DEV Dev, + uint16_t SpadAmbientDamperFactor); + int8_t (*GetSpadAmbientDamperFactor)(VL53L0_DEV Dev, + uint16_t *pSpadAmbientDamperFactor); + int8_t (*PerformRefSpadManagement)(VL53L0_DEV Dev, + uint32_t *refSpadCount, uint8_t *isApertureSpads); + int8_t (*SetReferenceSpads)(VL53L0_DEV Dev, + uint32_t count, uint8_t isApertureSpads); + int8_t (*GetReferenceSpads)(VL53L0_DEV Dev, + uint32_t *pSpadCount, uint8_t *pIsApertureSpads); + int8_t (*GetStopCompletedStatus)(VL53L0_DEV Dev, + uint32_t *pStopStatus); +}; + +static struct stmvl53l0_api_fn_t stmvl53l0_api_func_tbl = { + .GetVersion = VL53L0_GetVersion, + .GetPalSpecVersion = VL53L0_GetPalSpecVersion, + .GetProductRevision = VL53L0_GetProductRevision, + .GetDeviceInfo = VL53L0_GetDeviceInfo, + .GetDeviceErrorStatus = VL53L0_GetDeviceErrorStatus, + .GetRangeStatusString = VL53L0_GetRangeStatusString, + .GetDeviceErrorString = VL53L0_GetDeviceErrorString, + .GetPalErrorString = VL53L0_GetPalErrorString, + .GetPalState = VL53L0_GetPalState, + .SetPowerMode = VL53L0_SetPowerMode, + .GetPowerMode = VL53L0_GetPowerMode, + .SetOffsetCalibrationDataMicroMeter = + VL53L0_SetOffsetCalibrationDataMicroMeter, + .SetLinearityCorrectiveGain = + VL53L0_SetLinearityCorrectiveGain, + .GetLinearityCorrectiveGain = + VL53L0_GetLinearityCorrectiveGain, + .GetOffsetCalibrationDataMicroMeter = + VL53L0_GetOffsetCalibrationDataMicroMeter, + .SetGroupParamHold = VL53L0_SetGroupParamHold, + .GetUpperLimitMilliMeter = VL53L0_GetUpperLimitMilliMeter, + .SetDeviceAddress = VL53L0_SetDeviceAddress, + .DataInit = VL53L0_DataInit, + .SetTuningSettingBuffer = VL53L0_SetTuningSettingBuffer, + .GetTuningSettingBuffer = VL53L0_GetTuningSettingBuffer, + .StaticInit = VL53L0_StaticInit, + .WaitDeviceBooted = VL53L0_WaitDeviceBooted, + .ResetDevice = VL53L0_ResetDevice, + .SetDeviceParameters = VL53L0_SetDeviceParameters, + .SetDeviceMode = VL53L0_SetDeviceMode, + .GetDeviceMode = VL53L0_GetDeviceMode, + .SetHistogramMode = VL53L0_SetHistogramMode, + .GetHistogramMode = VL53L0_GetHistogramMode, + .SetMeasurementTimingBudgetMicroSeconds = + VL53L0_SetMeasurementTimingBudgetMicroSeconds, + .GetMeasurementTimingBudgetMicroSeconds = + VL53L0_GetMeasurementTimingBudgetMicroSeconds, + .GetVcselPulsePeriod = VL53L0_GetVcselPulsePeriod, + .SetVcselPulsePeriod = VL53L0_SetVcselPulsePeriod, + .SetSequenceStepEnable = VL53L0_SetSequenceStepEnable, + .GetSequenceStepEnable = VL53L0_GetSequenceStepEnable, + .GetSequenceStepEnables = VL53L0_GetSequenceStepEnables, + .SetSequenceStepTimeout = VL53L0_SetSequenceStepTimeout, + .GetSequenceStepTimeout = VL53L0_GetSequenceStepTimeout, + .GetNumberOfSequenceSteps = VL53L0_GetNumberOfSequenceSteps, + .GetSequenceStepsInfo = VL53L0_GetSequenceStepsInfo, + .SetInterMeasurementPeriodMilliSeconds = + VL53L0_SetInterMeasurementPeriodMilliSeconds, + .GetInterMeasurementPeriodMilliSeconds = + VL53L0_GetInterMeasurementPeriodMilliSeconds, + .SetXTalkCompensationEnable = VL53L0_SetXTalkCompensationEnable, + .GetXTalkCompensationEnable = VL53L0_GetXTalkCompensationEnable, + .SetXTalkCompensationRateMegaCps = + VL53L0_SetXTalkCompensationRateMegaCps, + .GetXTalkCompensationRateMegaCps = + VL53L0_GetXTalkCompensationRateMegaCps, + .GetNumberOfLimitCheck = VL53L0_GetNumberOfLimitCheck, + .GetLimitCheckInfo = VL53L0_GetLimitCheckInfo, + .SetLimitCheckEnable = VL53L0_SetLimitCheckEnable, + .GetLimitCheckEnable = VL53L0_GetLimitCheckEnable, + .SetLimitCheckValue = VL53L0_SetLimitCheckValue, + .GetLimitCheckValue = VL53L0_GetLimitCheckValue, + .GetLimitCheckCurrent = VL53L0_GetLimitCheckCurrent, + .SetWrapAroundCheckEnable = VL53L0_SetWrapAroundCheckEnable, + .GetWrapAroundCheckEnable = VL53L0_GetWrapAroundCheckEnable, + .PerformSingleMeasurement = VL53L0_PerformSingleMeasurement, + .PerformRefCalibration = VL53L0_PerformRefCalibration, + .SetRefCalibration = VL53L0_SetRefCalibration, + .GetRefCalibration = VL53L0_GetRefCalibration, + .PerformXTalkCalibration = VL53L0_PerformXTalkCalibration, + .PerformOffsetCalibration = VL53L0_PerformOffsetCalibration, + .StartMeasurement = VL53L0_StartMeasurement, + .StopMeasurement = VL53L0_StopMeasurement, + .GetMeasurementDataReady = VL53L0_GetMeasurementDataReady, + .WaitDeviceReadyForNewMeasurement = + VL53L0_WaitDeviceReadyForNewMeasurement, + .GetRangingMeasurementData = VL53L0_GetRangingMeasurementData, + .GetHistogramMeasurementData = VL53L0_GetHistogramMeasurementData, + .PerformSingleRangingMeasurement = + VL53L0_PerformSingleRangingMeasurement, + .PerformSingleHistogramMeasurement = + VL53L0_PerformSingleHistogramMeasurement, + .SetNumberOfROIZones = VL53L0_SetNumberOfROIZones, + .GetNumberOfROIZones = VL53L0_GetNumberOfROIZones, + .GetMaxNumberOfROIZones = VL53L0_GetMaxNumberOfROIZones, + .SetGpioConfig = VL53L0_SetGpioConfig, + .GetGpioConfig = VL53L0_GetGpioConfig, + .SetInterruptThresholds = VL53L0_SetInterruptThresholds, + .GetInterruptThresholds = VL53L0_GetInterruptThresholds, + .ClearInterruptMask = VL53L0_ClearInterruptMask, + .GetInterruptMaskStatus = VL53L0_GetInterruptMaskStatus, + .EnableInterruptMask = VL53L0_EnableInterruptMask, + .SetSpadAmbientDamperThreshold = VL53L0_SetSpadAmbientDamperThreshold, + .GetSpadAmbientDamperThreshold = VL53L0_GetSpadAmbientDamperThreshold, + .SetSpadAmbientDamperFactor = VL53L0_SetSpadAmbientDamperFactor, + .GetSpadAmbientDamperFactor = VL53L0_GetSpadAmbientDamperFactor, + .PerformRefSpadManagement = VL53L0_PerformRefSpadManagement, + .SetReferenceSpads = VL53L0_SetReferenceSpads, + .GetReferenceSpads = VL53L0_GetReferenceSpads, + .GetStopCompletedStatus = VL53L0_GetStopCompletedStatus, + +}; +struct stmvl53l0_api_fn_t *papi_func_tbl; + +/* + * IOCTL definitions + */ +#define VL53L0_IOCTL_INIT _IO('p', 0x01) +#define VL53L0_IOCTL_XTALKCALB _IOWR('p', 0x02, unsigned int) +#define VL53L0_IOCTL_OFFCALB _IOWR('p', 0x03, unsigned int) +#define VL53L0_IOCTL_STOP _IO('p', 0x05) +#define VL53L0_IOCTL_SETXTALK _IOW('p', 0x06, unsigned int) +#define VL53L0_IOCTL_SETOFFSET _IOW('p', 0x07, int8_t) +#define VL53L0_IOCTL_ACTIVATE_USE_CASE _IOW('p', 0x08, uint8_t) +#define VL53L0_IOCTL_ACTIVATE_CUSTOM_USE_CASE \ + _IOW('p', 0x09, struct stmvl53l0_custom_use_case) + +#define VL53L0_IOCTL_GETDATAS \ + _IOWR('p', 0x0b, VL53L0_RangingMeasurementData_t) +#define VL53L0_IOCTL_REGISTER \ + _IOWR('p', 0x0c, struct stmvl53l0_register) +#define VL53L0_IOCTL_PARAMETER \ + _IOWR('p', 0x0d, struct stmvl53l0_parameter) + +/* Mask fields to indicate Offset and Xtalk Comp + * values have been set by application + */ +#define SET_OFFSET_CALIB_DATA_MICROMETER_MASK 0x1 +#define SET_XTALK_COMP_RATE_MCPS_MASK 0x2 + +/* Macros used across different functions */ +#define USE_CASE_LONG_DISTANCE 1 +#define USE_CASE_HIGH_ACCURACY 2 +#define USE_CASE_HIGH_SPEED 3 +#define USE_CASE_CUSTOM 4 + +#define LONG_DISTANCE_TIMING_BUDGET 33000 +#define LONG_DISTANCE_SIGNAL_RATE_LIMIT (65536 / 10) /* 0.1 * 65536 */ +#define LONG_DISTANCE_SIGMA_LIMIT (60*65536) +#define LONG_DISTANCE_PRE_RANGE_PULSE_PERIOD 18 +#define LONG_DISTANCE_FINAL_RANGE_PULSE_PERIOD 14 + +#define HIGH_ACCURACY_TIMING_BUDGET 200000 +#define HIGH_ACCURACY_SIGNAL_RATE_LIMIT (25 * 65536 / 100) /* 0.25 * 65536 */ +#define HIGH_ACCURACY_SIGMA_LIMIT (18*65536) +#define HIGH_ACCURACY_PRE_RANGE_PULSE_PERIOD 14 +#define HIGH_ACCURACY_FINAL_RANGE_PULSE_PERIOD 10 + +#define HIGH_SPEED_TIMING_BUDGET 20000 +#define HIGH_SPEED_SIGNAL_RATE_LIMIT (25 * 65536 / 100) /* 0.25 * 65536 */ +#define HIGH_SPEED_SIGMA_LIMIT (32*65536) +#define HIGH_SPEED_PRE_RANGE_PULSE_PERIOD 14 +#define HIGH_SPEED_FINAL_RANGE_PULSE_PERIOD 10 + +static long stmvl53l0_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); +/*static int stmvl53l0_flush(struct file *file, fl_owner_t id);*/ +static int stmvl53l0_open(struct inode *inode, struct file *file); +static int stmvl53l0_init_client(struct stmvl53l0_data *data); +static int stmvl53l0_start(struct stmvl53l0_data *data, uint8_t scaling, + init_mode_e mode); +static int stmvl53l0_stop(struct stmvl53l0_data *data); +static int stmvl53l0_config_use_case(struct stmvl53l0_data *data); + +#ifdef DEBUG_TIME_LOG +static void stmvl53l0_DebugTimeGet(struct timeval *ptv) +{ + do_gettimeofday(ptv); +} + +static void stmvl53l0_DebugTimeDuration(struct timeval *pstart_tv, + struct timeval *pstop_tv) +{ + long total_sec, total_msec; + + total_sec = pstop_tv->tv_sec - pstart_tv->tv_sec; + total_msec = (pstop_tv->tv_usec - pstart_tv->tv_usec)/1000; + total_msec += total_sec * 1000; + pr_err("elapsedTime:%ld\n", total_msec); +} +#endif + +static uint8_t stmvl53l0_setupAPIFunctions(struct stmvl53l0_data *data) +{ + uint8_t revision = 0; + VL53L0_DEV vl53l0_dev = data; + + /* Read Revision ID */ + VL53L0_RdByte(vl53l0_dev, + VL53L0_REG_IDENTIFICATION_REVISION_ID, + &revision); + vl53l0_errmsg("read REVISION_ID: 0x%x\n", revision); + revision = (revision & 0xF0) >> 4; + if (revision == 1) { + /*cut 1.1*/ + vl53l0_errmsg("to setup API cut 1.1\n"); + papi_func_tbl->GetVersion = VL53L0_GetVersion; + papi_func_tbl->GetPalSpecVersion = VL53L0_GetPalSpecVersion; + papi_func_tbl->GetProductRevision = VL53L0_GetProductRevision; + papi_func_tbl->GetDeviceInfo = VL53L0_GetDeviceInfo; + papi_func_tbl->GetDeviceErrorStatus = + VL53L0_GetDeviceErrorStatus; + papi_func_tbl->GetRangeStatusString = + VL53L0_GetRangeStatusString; + papi_func_tbl->GetDeviceErrorString = + VL53L0_GetDeviceErrorString; + papi_func_tbl->GetPalErrorString = + VL53L0_GetPalErrorString; + papi_func_tbl->GetPalState = + VL53L0_GetPalState; + papi_func_tbl->SetPowerMode = + VL53L0_SetPowerMode; + papi_func_tbl->GetPowerMode = + VL53L0_GetPowerMode; + papi_func_tbl->SetOffsetCalibrationDataMicroMeter = + VL53L0_SetOffsetCalibrationDataMicroMeter; + papi_func_tbl->GetOffsetCalibrationDataMicroMeter = + VL53L0_GetOffsetCalibrationDataMicroMeter; + papi_func_tbl->SetLinearityCorrectiveGain = + VL53L0_SetLinearityCorrectiveGain; + papi_func_tbl->GetLinearityCorrectiveGain = + VL53L0_GetLinearityCorrectiveGain; + papi_func_tbl->SetGroupParamHold = VL53L0_SetGroupParamHold; + papi_func_tbl->GetUpperLimitMilliMeter = + VL53L0_GetUpperLimitMilliMeter; + papi_func_tbl->SetDeviceAddress = + VL53L0_SetDeviceAddress; + papi_func_tbl->DataInit = + VL53L0_DataInit; + papi_func_tbl->SetTuningSettingBuffer = + VL53L0_SetTuningSettingBuffer; + papi_func_tbl->GetTuningSettingBuffer = + VL53L0_GetTuningSettingBuffer; + papi_func_tbl->StaticInit = + VL53L0_StaticInit; + papi_func_tbl->WaitDeviceBooted = + VL53L0_WaitDeviceBooted; + papi_func_tbl->ResetDevice = + VL53L0_ResetDevice; + papi_func_tbl->SetDeviceParameters = + VL53L0_SetDeviceParameters; + papi_func_tbl->SetDeviceMode = VL53L0_SetDeviceMode; + papi_func_tbl->GetDeviceMode = VL53L0_GetDeviceMode; + papi_func_tbl->SetHistogramMode = VL53L0_SetHistogramMode; + papi_func_tbl->GetHistogramMode = VL53L0_GetHistogramMode; + papi_func_tbl->SetMeasurementTimingBudgetMicroSeconds = + VL53L0_SetMeasurementTimingBudgetMicroSeconds; + papi_func_tbl->GetMeasurementTimingBudgetMicroSeconds = + VL53L0_GetMeasurementTimingBudgetMicroSeconds; + papi_func_tbl->GetVcselPulsePeriod = VL53L0_GetVcselPulsePeriod; + papi_func_tbl->SetVcselPulsePeriod = VL53L0_SetVcselPulsePeriod; + papi_func_tbl->SetSequenceStepEnable = + VL53L0_SetSequenceStepEnable; + papi_func_tbl->GetSequenceStepEnable = + VL53L0_GetSequenceStepEnable; + papi_func_tbl->GetSequenceStepEnables = + VL53L0_GetSequenceStepEnables; + papi_func_tbl->SetSequenceStepTimeout = + VL53L0_SetSequenceStepTimeout; + papi_func_tbl->GetSequenceStepTimeout = + VL53L0_GetSequenceStepTimeout; + papi_func_tbl->GetNumberOfSequenceSteps = + VL53L0_GetNumberOfSequenceSteps; + papi_func_tbl->GetSequenceStepsInfo = + VL53L0_GetSequenceStepsInfo; + papi_func_tbl->SetInterMeasurementPeriodMilliSeconds = + VL53L0_SetInterMeasurementPeriodMilliSeconds; + papi_func_tbl->GetInterMeasurementPeriodMilliSeconds = + VL53L0_GetInterMeasurementPeriodMilliSeconds; + papi_func_tbl->SetXTalkCompensationEnable = + VL53L0_SetXTalkCompensationEnable; + papi_func_tbl->GetXTalkCompensationEnable = + VL53L0_GetXTalkCompensationEnable; + papi_func_tbl->SetXTalkCompensationRateMegaCps = + VL53L0_SetXTalkCompensationRateMegaCps; + papi_func_tbl->GetXTalkCompensationRateMegaCps = + VL53L0_GetXTalkCompensationRateMegaCps; + papi_func_tbl->GetNumberOfLimitCheck = + VL53L0_GetNumberOfLimitCheck; + papi_func_tbl->GetLimitCheckInfo = + VL53L0_GetLimitCheckInfo; + papi_func_tbl->SetLimitCheckEnable = + VL53L0_SetLimitCheckEnable; + papi_func_tbl->GetLimitCheckEnable = + VL53L0_GetLimitCheckEnable; + papi_func_tbl->SetLimitCheckValue = + VL53L0_SetLimitCheckValue; + papi_func_tbl->GetLimitCheckValue = + VL53L0_GetLimitCheckValue; + papi_func_tbl->GetLimitCheckCurrent = + VL53L0_GetLimitCheckCurrent; + papi_func_tbl->SetWrapAroundCheckEnable = + VL53L0_SetWrapAroundCheckEnable; + papi_func_tbl->GetWrapAroundCheckEnable = + VL53L0_GetWrapAroundCheckEnable; + papi_func_tbl->PerformSingleMeasurement = + VL53L0_PerformSingleMeasurement; + papi_func_tbl->PerformRefCalibration = + VL53L0_PerformRefCalibration; + papi_func_tbl->SetRefCalibration = + VL53L0_SetRefCalibration; + papi_func_tbl->GetRefCalibration = + VL53L0_GetRefCalibration; + papi_func_tbl->PerformXTalkCalibration = + VL53L0_PerformXTalkCalibration; + papi_func_tbl->PerformOffsetCalibration = + VL53L0_PerformOffsetCalibration; + papi_func_tbl->StartMeasurement = + VL53L0_StartMeasurement; + papi_func_tbl->StopMeasurement = + VL53L0_StopMeasurement; + papi_func_tbl->GetMeasurementDataReady = + VL53L0_GetMeasurementDataReady; + papi_func_tbl->WaitDeviceReadyForNewMeasurement = + VL53L0_WaitDeviceReadyForNewMeasurement; + papi_func_tbl->GetRangingMeasurementData = + VL53L0_GetRangingMeasurementData; + papi_func_tbl->GetHistogramMeasurementData = + VL53L0_GetHistogramMeasurementData; + papi_func_tbl->PerformSingleRangingMeasurement = + VL53L0_PerformSingleRangingMeasurement; + papi_func_tbl->PerformSingleHistogramMeasurement = + VL53L0_PerformSingleHistogramMeasurement; + papi_func_tbl->SetNumberOfROIZones = + VL53L0_SetNumberOfROIZones; + papi_func_tbl->GetNumberOfROIZones = + VL53L0_GetNumberOfROIZones; + papi_func_tbl->GetMaxNumberOfROIZones = + VL53L0_GetMaxNumberOfROIZones; + papi_func_tbl->SetGpioConfig = + VL53L0_SetGpioConfig; + papi_func_tbl->GetGpioConfig = + VL53L0_GetGpioConfig; + papi_func_tbl->SetInterruptThresholds = + VL53L0_SetInterruptThresholds; + papi_func_tbl->GetInterruptThresholds = + VL53L0_GetInterruptThresholds; + papi_func_tbl->ClearInterruptMask = + VL53L0_ClearInterruptMask; + papi_func_tbl->GetInterruptMaskStatus = + VL53L0_GetInterruptMaskStatus; + papi_func_tbl->EnableInterruptMask = VL53L0_EnableInterruptMask; + papi_func_tbl->SetSpadAmbientDamperThreshold = + VL53L0_SetSpadAmbientDamperThreshold; + papi_func_tbl->GetSpadAmbientDamperThreshold = + VL53L0_GetSpadAmbientDamperThreshold; + papi_func_tbl->SetSpadAmbientDamperFactor = + VL53L0_SetSpadAmbientDamperFactor; + papi_func_tbl->GetSpadAmbientDamperFactor = + VL53L0_GetSpadAmbientDamperFactor; + papi_func_tbl->PerformRefSpadManagement = + VL53L0_PerformRefSpadManagement; + papi_func_tbl->SetReferenceSpads = VL53L0_SetReferenceSpads; + papi_func_tbl->GetReferenceSpads = VL53L0_GetReferenceSpads; + } else if (revision == 0) { + /*cut 1.0*/ + vl53l0_errmsg("API cut 1.0 NOT SUPPORTED\n"); + } + + return revision; +} + +static void stmvl53l0_ps_read_measurement(struct stmvl53l0_data *data) +{ + struct timeval tv; + VL53L0_DEV vl53l0_dev = data; + VL53L0_Error Status = VL53L0_ERROR_NONE; + FixPoint1616_t LimitCheckCurrent; + + do_gettimeofday(&tv); + + data->ps_data = data->rangeData.RangeMilliMeter; + input_report_abs(data->input_dev_ps, ABS_DISTANCE, + (int)(data->ps_data + 5) / 10); + input_report_abs(data->input_dev_ps, ABS_HAT0X, tv.tv_sec); + input_report_abs(data->input_dev_ps, ABS_HAT0Y, tv.tv_usec); + input_report_abs(data->input_dev_ps, ABS_HAT1X, + data->rangeData.RangeMilliMeter); + input_report_abs(data->input_dev_ps, ABS_HAT1Y, + data->rangeData.RangeStatus); + input_report_abs(data->input_dev_ps, ABS_HAT2X, + data->rangeData.SignalRateRtnMegaCps); + input_report_abs(data->input_dev_ps, ABS_HAT2Y, + data->rangeData.AmbientRateRtnMegaCps); + input_report_abs(data->input_dev_ps, ABS_HAT3X, + data->rangeData.MeasurementTimeUsec); + input_report_abs(data->input_dev_ps, ABS_HAT3Y, + data->rangeData.RangeDMaxMilliMeter); + Status = papi_func_tbl->GetLimitCheckCurrent(vl53l0_dev, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + &LimitCheckCurrent); + if (Status == VL53L0_ERROR_NONE) { + input_report_abs(data->input_dev_ps, ABS_WHEEL, + LimitCheckCurrent); + } + input_report_abs(data->input_dev_ps, ABS_MISC, + data->rangeData.EffectiveSpadRtnCount); + input_sync(data->input_dev_ps); + + if (data->enableDebug) + vl53l0_errmsg("range:%d, RtnRateMcps:%d, \ + err:0x%x,Dmax:%d,rtnambr:%d,time:%d,Spad:%d,SigmaLimit:%d\n", + data->rangeData.RangeMilliMeter, + data->rangeData.SignalRateRtnMegaCps, + data->rangeData.RangeStatus, + data->rangeData.RangeDMaxMilliMeter, + data->rangeData.AmbientRateRtnMegaCps, + data->rangeData.MeasurementTimeUsec, + data->rangeData.EffectiveSpadRtnCount, + LimitCheckCurrent); +} + +static void stmvl53l0_cancel_handler(struct stmvl53l0_data *data) +{ + unsigned long flags; + bool ret; + + spin_lock_irqsave(&data->update_lock.wait_lock, flags); + /* + * If work is already scheduled then subsequent schedules will not + * change the scheduled time that's why we have to cancel it first. + */ + ret = cancel_delayed_work(&data->dwork); + if (ret == 0) + vl53l0_errmsg("cancel_delayed_work return FALSE\n"); + + spin_unlock_irqrestore(&data->update_lock.wait_lock, flags); + +} + +void stmvl53l0_schedule_handler(struct stmvl53l0_data *data) +{ + unsigned long flags; + + spin_lock_irqsave(&data->update_lock.wait_lock, flags); + /* + * If work is already scheduled then subsequent schedules will not + * change the scheduled time that's why we have to cancel it first. + */ + cancel_delayed_work(&data->dwork); + schedule_delayed_work(&data->dwork, 0); + spin_unlock_irqrestore(&data->update_lock.wait_lock, flags); + +} + + +#ifdef USE_INT +static irqreturn_t stmvl53l0_interrupt_handler(int vec, void *info) +{ + + struct stmvl53l0_data *data = (struct stmvl53l0_data *)info; + + if (data->irq == vec) { + data->interrupt_received = 1; + schedule_delayed_work(&data->dwork, 0); + } + return IRQ_HANDLED; +} +#else +/* Flag used to exit the thread when kthread_stop() is invoked */ +static int poll_thread_exit; +int stmvl53l0_poll_thread(void *data) +{ + VL53L0_DEV vl53l0_dev = data; + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint32_t sleep_time = 0; + uint32_t interruptStatus = 0; + + pr_err("%s(%d) : Starting Polling thread\n", __func__, __LINE__); + + while (!kthread_should_stop()) { + /* Check if enable_ps_sensor is true or exit request is made. + * If not block + */ + wait_event(vl53l0_dev->poll_thread_wq, + (vl53l0_dev->enable_ps_sensor || poll_thread_exit)); + if (poll_thread_exit) { + pr_err("%s(%d) : Exiting the poll thread\n", __func__, __LINE__); + break; + } + + mutex_lock(&vl53l0_dev->work_mutex); + + sleep_time = vl53l0_dev->delay_ms; + Status = VL53L0_GetInterruptMaskStatus(vl53l0_dev, + &interruptStatus); + if (Status == VL53L0_ERROR_NONE && + interruptStatus && + interruptStatus != vl53l0_dev->interruptStatus) { + vl53l0_dev->interruptStatus = interruptStatus; + vl53l0_dev->noInterruptCount = 0; + stmvl53l0_schedule_handler(vl53l0_dev); + } else { + vl53l0_dev->noInterruptCount++; + } + + /*Force Clear interrupt mask and restart if + *no interrupt after twice the timingBudget + */ + if ((vl53l0_dev->noInterruptCount * vl53l0_dev->delay_ms) > + (vl53l0_dev->timingBudget * 2)) { + pr_err("No interrupt after (%u) msec(TimingBudget = %u). Clear Interrupt Mask and restart\n", + (vl53l0_dev->noInterruptCount * vl53l0_dev->delay_ms), + vl53l0_dev->timingBudget); + Status = papi_func_tbl->ClearInterruptMask(vl53l0_dev,0); + if (vl53l0_dev->deviceMode == + VL53L0_DEVICEMODE_SINGLE_RANGING) { + Status = papi_func_tbl->StartMeasurement( + vl53l0_dev); + if (Status != VL53L0_ERROR_NONE) { + pr_err("%s(%d) : Status = %d\n", + __func__, __LINE__, Status); + } + } + } + mutex_unlock(&vl53l0_dev->work_mutex); + /* Sleep for delay_ms milliseconds */ + msleep(sleep_time); + } + + return 0; +} +#endif + +/* work handler */ +static void stmvl53l0_work_handler(struct work_struct *work) +{ + struct stmvl53l0_data *data = container_of(work, struct stmvl53l0_data, + dwork.work); + VL53L0_DEV vl53l0_dev = data; + + VL53L0_Error Status = VL53L0_ERROR_NONE; + + mutex_lock(&data->work_mutex); + /* vl53l0_dbgmsg("Enter\n"); */ + + + if (vl53l0_dev->enable_ps_sensor == 1) { +#ifdef DEBUG_TIME_LOG + stmvl53l0_DebugTimeGet(&stop_tv); + stmvl53l0_DebugTimeDuration(&start_tv, &stop_tv); +#endif + /* Check if ISR has scheduled this function */ + if (vl53l0_dev->interrupt_received == 1) { + Status = papi_func_tbl->GetInterruptMaskStatus( + vl53l0_dev, + &vl53l0_dev->interruptStatus); + if (Status != VL53L0_ERROR_NONE) + pr_err("%s(%d) : Status = %d\n", + __func__, __LINE__, Status); + vl53l0_dev->interrupt_received = 0; + } + if (data->enableDebug) + pr_err("interruptStatus:0x%x, interrupt_received:%d\n", + vl53l0_dev->interruptStatus, + vl53l0_dev->interrupt_received); + + if (vl53l0_dev->interruptStatus == vl53l0_dev->gpio_function) { + Status = + papi_func_tbl->GetRangingMeasurementData( + vl53l0_dev, + &(data->rangeData)); + /* to push the measurement */ + if (Status == VL53L0_ERROR_NONE) + stmvl53l0_ps_read_measurement(data); + else + pr_err("%s(%d) : Status = %d\n", + __func__, __LINE__, Status); + + if (data->enableDebug) + pr_err("Measured range:%d\n", + data->rangeData.RangeMilliMeter); + + Status = papi_func_tbl->ClearInterruptMask( + vl53l0_dev, 0); + if (Status != VL53L0_ERROR_NONE) { + pr_err("%s(%d) : Status = %d\n", + __func__, __LINE__, Status); + } + + if (data->deviceMode == + VL53L0_DEVICEMODE_SINGLE_RANGING) { + /* Before restarting measurement + * check if use case needs to be changed + */ + + if (data->updateUseCase) { + Status = + stmvl53l0_config_use_case(data); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "Failed to configure Use case = %u\n", + vl53l0_dev->useCase); + } else { + data->updateUseCase = 0; + } + } + Status = + papi_func_tbl->StartMeasurement( + vl53l0_dev); + } + } +#ifdef DEBUG_TIME_LOG + stmvl53l0_DebugTimeGet(&start_tv); +#endif + } + + vl53l0_dev->interruptStatus = 0; + + mutex_unlock(&data->work_mutex); + +} + + +/* + * SysFS support + */ +static ssize_t stmvl53l0_show_enable_ps_sensor(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + + return snprintf(buf, 5, "%d\n", data->enable_ps_sensor); +} + +static ssize_t stmvl53l0_store_enable_ps_sensor(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + unsigned long val = 0; + + int ret = kstrtoul(buf, 10, &val); + + if (ret != 0) + return ret; + + if ((val != 0) && (val != 1)) { + vl53l0_errmsg("store unvalid value = %lu\n", val); + return count; + } + mutex_lock(&data->work_mutex); + vl53l0_errmsg("Enter, enable_ps_sensor flag:%d\n", + data->enable_ps_sensor); + vl53l0_errmsg("enable ps senosr ( %ld)\n", val); + + if (val == 1) { + /* turn on tof sensor */ + if (data->enable_ps_sensor == 0) { + /* to start */ + stmvl53l0_start(data, 3, NORMAL_MODE); + } else { + vl53l0_errmsg("Already enabled. Skip !"); + } + } else { + /* turn off tof sensor */ + if (data->enable_ps_sensor == 1) { + data->enable_ps_sensor = 0; + /* to stop */ + stmvl53l0_stop(data); + } + } + vl53l0_dbgmsg("End\n"); + mutex_unlock(&data->work_mutex); + + return count; +} + +static DEVICE_ATTR(enable_ps_sensor, 0664/*S_IWUGO | S_IRUGO*/, + stmvl53l0_show_enable_ps_sensor, stmvl53l0_store_enable_ps_sensor); + +static ssize_t stmvl53l0_show_enable_debug(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + + return snprintf(buf, 5, "%d\n", data->enableDebug); +} + +/* for debug */ +static ssize_t stmvl53l0_store_enable_debug(struct device *dev, + struct device_attribute *attr, const + char *buf, size_t count) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + unsigned long on = 0; + + int ret = kstrtoul(buf, 10, &on); + + if (ret != 0) + return ret; + + if ((on != 0) && (on != 1)) { + vl53l0_errmsg("set debug=%lu\n", on); + return count; + } + data->enableDebug = on; + + return count; +} + +/* DEVICE_ATTR(name,mode,show,store) */ +static DEVICE_ATTR(enable_debug, 0660/*S_IWUSR | S_IRUGO*/, + stmvl53l0_show_enable_debug, stmvl53l0_store_enable_debug); + +static ssize_t stmvl53l0_show_set_delay_ms(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + + return snprintf(buf, 5, "%d\n", data->delay_ms); +} + +/* for work handler scheduler time */ +static ssize_t stmvl53l0_store_set_delay_ms(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + unsigned long delay_ms = 0; + + int ret = kstrtoul(buf, 10, &delay_ms); + + if (ret != 0) + return ret; + if (delay_ms == 0) { + vl53l0_errmsg("set delay_ms=%lu\n", delay_ms); + return count; + } + mutex_lock(&data->work_mutex); + data->delay_ms = delay_ms; + mutex_unlock(&data->work_mutex); + + return count; +} + +/* DEVICE_ATTR(name,mode,show,store) */ +static DEVICE_ATTR(set_delay_ms, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l0_show_set_delay_ms, stmvl53l0_store_set_delay_ms); + +/* Timing Budget */ +static ssize_t stmvl53l0_show_timing_budget(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + + return snprintf(buf, 10, "%d\n", data->timingBudget); +} + +static ssize_t stmvl53l0_store_set_timing_budget(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + unsigned long timingBudget = 0; + int ret = kstrtoul(buf, 10, &timingBudget); + + if (ret != 0) + return ret; + if (timingBudget == 0) { + vl53l0_errmsg("set timingBudget=%lu\n", timingBudget); + return count; + } + mutex_lock(&data->work_mutex); + data->timingBudget = timingBudget; + mutex_unlock(&data->work_mutex); + + return count; +} + +/* DEVICE_ATTR(name,mode,show,store) */ +static DEVICE_ATTR(set_timing_budget, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l0_show_timing_budget, stmvl53l0_store_set_timing_budget); + +/* Use case */ +static ssize_t stmvl53l0_show_use_case(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + + switch (data->useCase) { + case USE_CASE_LONG_DISTANCE: + return snprintf(buf, 20, "Long Distance\n"); + case USE_CASE_HIGH_ACCURACY: + return snprintf(buf, 20, "High Accuracy\n"); + case USE_CASE_HIGH_SPEED: + return snprintf(buf, 20, "High Speed\n"); + default: + break; + } + + return snprintf(buf, 25, "Unknown use case\n"); +} + +static ssize_t stmvl53l0_store_set_use_case(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + unsigned long useCase = 0; + int ret = kstrtoul(buf, 10, &useCase); + + if (ret != 0) + return ret; + + mutex_lock(&data->work_mutex); + + if (useCase == USE_CASE_LONG_DISTANCE) { + data->timingBudget = LONG_DISTANCE_TIMING_BUDGET; + } else if (useCase == USE_CASE_HIGH_SPEED) { + data->timingBudget = HIGH_SPEED_TIMING_BUDGET; + } else if (useCase == USE_CASE_HIGH_ACCURACY) { + data->timingBudget = HIGH_ACCURACY_TIMING_BUDGET; + } else { + count = -EINVAL; + mutex_unlock(&data->work_mutex); + return count; + } + + data->useCase = useCase; + mutex_unlock(&data->work_mutex); + + return count; +} + +/* DEVICE_ATTR(name,mode,show,store) */ +static DEVICE_ATTR(set_use_case, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l0_show_use_case, stmvl53l0_store_set_use_case); + + +/* Get Current configuration info */ +static ssize_t stmvl53l0_show_current_configuration(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l0_data *vl53l0_dev = dev_get_drvdata(dev); + VL53L0_Error Status = VL53L0_ERROR_NONE; + int ret = -1; + FixPoint1616_t LimitValue = 0; + uint8_t LimitEnable = 0; + uint32_t refSpadCount = 0; + uint8_t isApertureSpads = 0; + uint8_t VhvSettings = 0; + uint8_t PhaseCal = 0; + uint8_t pulsePeriod = 0; + uint32_t timingBudget = 0; + int32_t offsetCalibrationDataMicroMeter = -1; + uint8_t XTalkCompensationEnable = 0; + FixPoint1616_t xTalkCompensationRateMegaCps = 0; + + ret = scnprintf(buf, PAGE_SIZE, "VL53L0 current configuration:\n"); + + mutex_lock(&vl53l0_dev->work_mutex); + pr_err("Driver Config:UseCase:%d, offsetCalDistance:%u,xtalkCalDistance:%u,setCalibratedValue:0x%X\n", + vl53l0_dev->useCase, vl53l0_dev->offsetCalDistance, + vl53l0_dev->xtalkCalDistance, + vl53l0_dev->setCalibratedValue); + + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "Driver Config:UseCase:%d, offsetCalDistance:%u,xtalkCalDistance:%u,setCalibratedValue:0x%X\n", + vl53l0_dev->useCase, + vl53l0_dev->offsetCalDistance, + vl53l0_dev->xtalkCalDistance, + vl53l0_dev->setCalibratedValue); + + if (vl53l0_dev->useCase == USE_CASE_CUSTOM) { + pr_err("CustomUseCase : Sigma=%u :Signal=%u: Pre=%u :Final=%u\n", + vl53l0_dev->sigmaLimit, + vl53l0_dev->signalRateLimit, + vl53l0_dev->preRangePulsePeriod, + vl53l0_dev->finalRangePulsePeriod); + + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "CustomUseCase : Sigma=%u :Signal=%u: Pre=%u :Final=%u\n", + vl53l0_dev->sigmaLimit, + vl53l0_dev->signalRateLimit, + vl53l0_dev->preRangePulsePeriod, + vl53l0_dev->finalRangePulsePeriod); + } + + Status = papi_func_tbl->GetLimitCheckValue(vl53l0_dev, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + &LimitValue); + + if (Status == VL53L0_ERROR_NONE) { + Status = papi_func_tbl->GetLimitCheckEnable(vl53l0_dev, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + &LimitEnable); + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("Get LimitCheckValue SIGMA_FINAL_RANGE as:%d,Enable:%d\n", + (LimitValue>>16), LimitEnable); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "Sigma Limit:%u, Enable:%u\n", + (LimitValue>>16), + LimitEnable); + Status = papi_func_tbl->GetLimitCheckValue(vl53l0_dev, + VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + &LimitValue); + Status = papi_func_tbl->GetLimitCheckEnable(vl53l0_dev, + VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + &LimitEnable); + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("Get LimitCheckValue SIGNAL_FINAL_RANGE as:%d(Fix1616),Enable:%d\n", + (LimitValue), LimitEnable); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "SIGNAL Limit:%u, Enable:%u\n", + LimitValue, + LimitEnable); + + Status = papi_func_tbl->GetLimitCheckValue(vl53l0_dev, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, + &LimitValue); + Status = papi_func_tbl->GetLimitCheckEnable(vl53l0_dev, + VL53L0_CHECKENABLE_SIGNAL_REF_CLIP, + &LimitEnable); + + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("Get LimitCheckValue SIGNAL_REF_CLIP as:%d(fix1616),Enable:%d\n", + (LimitValue), LimitEnable); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "RefClipLimit:%u, Enable:%u\n", + LimitValue, + LimitEnable); + + Status = papi_func_tbl->GetLimitCheckValue(vl53l0_dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + &LimitValue); + Status = papi_func_tbl->GetLimitCheckEnable(vl53l0_dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + &LimitEnable); + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err( + "Get LimitCheckValue RANGE_IGNORE_THRESHOLDas:%d(fix1616),Enable:%d\n", + (LimitValue), + LimitEnable); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "RngIgnoreThresh:%u, Enable:%u\n", + LimitValue, + LimitEnable); + + Status = papi_func_tbl->GetRefCalibration(vl53l0_dev, + &VhvSettings, &PhaseCal); /* Ref calibration */ + + + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("GetRefCalibration - Vhv = %u, PhaseCal = %u\n", + VhvSettings, PhaseCal); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "Vhv:%u, PhCal:%u\n", + VhvSettings, + PhaseCal); + + /* Ref Spad Management */ + Status = papi_func_tbl->GetReferenceSpads(vl53l0_dev, + &refSpadCount, &isApertureSpads); + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("GetSpads - Count = %u, IsAperture = %u\n", + refSpadCount, isApertureSpads); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "SpadCount:%u, IsAperture:%u\n", + refSpadCount, + isApertureSpads); + + Status = papi_func_tbl->GetMeasurementTimingBudgetMicroSeconds( + vl53l0_dev, + &timingBudget); + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("TimingBudget = %u\n", timingBudget); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, "TimBudget:%u\n", + timingBudget); + Status = papi_func_tbl->GetVcselPulsePeriod(vl53l0_dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + &pulsePeriod); + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("GetVcselPulsePeriod - PRE_RANGE = %u\n", + pulsePeriod); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "PulsePreRange:%u\n", + pulsePeriod); + + Status = papi_func_tbl->GetVcselPulsePeriod(vl53l0_dev, + VL53L0_VCSEL_PERIOD_FINAL_RANGE, + &pulsePeriod); + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("GetVcselPulsePeriod - FINAL_RANGE = %u\n", + pulsePeriod); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, + "PulseFinalRange:%u\n", + pulsePeriod); + + Status = papi_func_tbl->GetOffsetCalibrationDataMicroMeter( + vl53l0_dev, + &offsetCalibrationDataMicroMeter); + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("OffsetCalibrationDataMicroMeter = %d\n", + offsetCalibrationDataMicroMeter); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, "Offset:%d\n", + offsetCalibrationDataMicroMeter); + + Status = papi_func_tbl->GetXTalkCompensationEnable(vl53l0_dev, + &XTalkCompensationEnable); + } + + if (Status == VL53L0_ERROR_NONE) { + pr_err("Xtalk Enable = %u\n", XTalkCompensationEnable); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, "XtalkEnable:%u\n", + XTalkCompensationEnable); + + Status = papi_func_tbl->GetXTalkCompensationRateMegaCps( + vl53l0_dev, + &xTalkCompensationRateMegaCps); + } + + + if (Status == VL53L0_ERROR_NONE) { + pr_err("XtalkComp MCPS = %u\n", xTalkCompensationRateMegaCps); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, "XtalkMcps:%u\n", + xTalkCompensationRateMegaCps); + + } else { + pr_err("Error = %d\n", Status); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, "Error:%d\n", + Status); + + } + + pr_err("Total Bytes returned = %d\n", ret); + + mutex_unlock(&vl53l0_dev->work_mutex); + + + return ret; +} + +/* DEVICE_ATTR(name,mode,show,store) */ +static DEVICE_ATTR(show_current_configuration, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l0_show_current_configuration, NULL); +/* for work handler scheduler time */ +static ssize_t stmvl53l0_do_flush(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + + int ret = 0; + /* Revisit : Avoid lock if it takes too long time */ + mutex_lock(&data->work_mutex); + + vl53l0_dbgmsg("Starting timer to fire in 1ms (%ld)\n", jiffies); + ret = mod_timer(&data->timer, jiffies + msecs_to_jiffies(1)); + if (ret) + pr_err("Error from mod_timer = %d\n", ret); + + mutex_unlock(&data->work_mutex); + return count; +} + +/* DEVICE_ATTR(name,mode,show,store) */ +static DEVICE_ATTR(do_flush, 0660/*S_IWUGO | S_IRUGO*/, + NULL, stmvl53l0_do_flush); + +static ssize_t stmvl53l0_show_offset(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + + return snprintf(buf, 5, "%d\n", data->offset); +} + +static ssize_t stmvl53l0_store_offset(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + long on; + + if (buf[0] == '-') { + if (kstrtoul(&buf[1], 10, &on)) + return count; + on = 0 - on; + } else { + if (kstrtoul(buf, 10, &on)) + return count; + } + vl53l0_errmsg("%d,set offset=%ld\n", __LINE__, on); + data->offset = on * 1000; + + return count; +} + +/* DEVICE_ATTR(name,mode,show,store) */ +static DEVICE_ATTR(offset, S_IWUSR | S_IRUGO, + stmvl53l0_show_offset, stmvl53l0_store_offset); + +static ssize_t stmvl53l0_show_xtalk(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + + return snprintf(buf, 5, "%d\n", data->xtalk); +} + +static ssize_t stmvl53l0_store_xtalk(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct stmvl53l0_data *data = dev_get_drvdata(dev); + long on; + + if (kstrtoul(buf, 10, &on)) + return count; + data->xtalk = on; + vl53l0_errmsg("%d,set xtalk=%ld\n", __LINE__, on); + return count; +} + +/* DEVICE_ATTR(name,mode,show,store) */ +static DEVICE_ATTR(xtalk, S_IWUSR | S_IRUGO, + stmvl53l0_show_xtalk, stmvl53l0_store_xtalk); + +static struct attribute *stmvl53l0_attributes[] = { + &dev_attr_enable_ps_sensor.attr, + &dev_attr_enable_debug.attr, + &dev_attr_set_delay_ms.attr, + &dev_attr_set_timing_budget.attr, + &dev_attr_set_use_case.attr, + &dev_attr_do_flush.attr, + &dev_attr_offset.attr, + &dev_attr_xtalk.attr, + &dev_attr_show_current_configuration.attr, + NULL +}; + +static const struct attribute_group stmvl53l0_attr_group = { + .attrs = stmvl53l0_attributes, +}; + +/* + * misc device file operation functions + */ +static int stmvl53l0_ioctl_handler(struct file *file, + unsigned int cmd, unsigned long arg, + void __user *p) +{ + int rc = 0; + unsigned int xtalkint = 0; + unsigned int targetDistance = 0; + int8_t offsetint = 0; + uint8_t useCase = 0; + struct stmvl53l0_custom_use_case customUseCase; + struct stmvl53l0_data *data = + container_of(file->private_data, + struct stmvl53l0_data, miscdev); + struct stmvl53l0_register reg; + struct stmvl53l0_parameter parameter; + VL53L0_DEV vl53l0_dev = data; + VL53L0_DeviceModes deviceMode; + uint8_t page_num = 0; + VL53L0_Error Status = VL53L0_ERROR_NONE; + + if (!data) + return -EINVAL; + + vl53l0_dbgmsg("Enter enable_ps_sensor:%d\n", data->enable_ps_sensor); + switch (cmd) { + /* enable */ + case VL53L0_IOCTL_INIT: + vl53l0_dbgmsg("VL53L0_IOCTL_INIT\n"); + /* turn on tof sensor only if it's not enabled by other + * client + */ + if (data->enable_ps_sensor == 0) { + /* to start */ + stmvl53l0_start(data, 3, NORMAL_MODE); + } else + rc = -EINVAL; + break; + /* crosstalk calibration */ + case VL53L0_IOCTL_XTALKCALB: + vl53l0_dbgmsg("VL53L0_IOCTL_XTALKCALB\n"); + data->xtalkCalDistance = 100; + if (copy_from_user(&targetDistance, (unsigned int *)p, + sizeof(unsigned int))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + data->xtalkCalDistance = targetDistance; + + /* turn on tof sensor only if it's not enabled by other + * client + */ + if (data->enable_ps_sensor == 0) { + /* to start */ + stmvl53l0_start(data, 3, XTALKCALIB_MODE); + } else + rc = -EINVAL; + break; + /* set up Xtalk value */ + case VL53L0_IOCTL_SETXTALK: + vl53l0_dbgmsg("VL53L0_IOCTL_SETXTALK\n"); + if (copy_from_user(&xtalkint, (unsigned int *)p, + sizeof(unsigned int))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + vl53l0_dbgmsg("SETXTALK as 0x%x\n", xtalkint); + + /* later + * SetXTalkCompensationRate(vl53l0_dev, xtalkint); + */ + break; + /* offset calibration */ + case VL53L0_IOCTL_OFFCALB: + vl53l0_dbgmsg("VL53L0_IOCTL_OFFCALB\n"); + data->offsetCalDistance = 50; + if (copy_from_user(&targetDistance, (unsigned int *)p, + sizeof(unsigned int))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + data->offsetCalDistance = targetDistance; + if (data->enable_ps_sensor == 0) { + /* to start */ + stmvl53l0_start(data, 3, OFFSETCALIB_MODE); + } else + rc = -EINVAL; + break; + /* set up offset value */ + case VL53L0_IOCTL_SETOFFSET: + vl53l0_dbgmsg("VL53L0_IOCTL_SETOFFSET\n"); + if (copy_from_user(&offsetint, (int8_t *)p, sizeof(int8_t))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + vl53l0_dbgmsg("SETOFFSET as %d\n", offsetint); + + /* later + * SetOffsetCalibrationData(vl53l0_dev, offsetint); + */ + break; + + /* Config use case */ + case VL53L0_IOCTL_ACTIVATE_USE_CASE: + vl53l0_dbgmsg("VL53L0_IOCTL_ACTIVATE_USE_CASE\n"); + if (copy_from_user(&useCase, (uint8_t *)p, sizeof(uint8_t))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + + /* Validate the user passed use case. + * Update the timingBudget value. The other + * parameters are updated approrpiately in config_use_case() + * Currently the timing budget can be updated through + * sysfs entry, and this needs additional steps to manage. + */ + switch (useCase) { + case USE_CASE_LONG_DISTANCE: + data->timingBudget = LONG_DISTANCE_TIMING_BUDGET; + break; + + case USE_CASE_HIGH_ACCURACY: + data->timingBudget = HIGH_ACCURACY_TIMING_BUDGET; + break; + + case USE_CASE_HIGH_SPEED: + data->timingBudget = HIGH_SPEED_TIMING_BUDGET; + break; + + default: + vl53l0_errmsg("%d, Unknown Use case = %u\n", __LINE__, + useCase); + return -EFAULT; + } + vl53l0_dbgmsg("useCase as %d\n", useCase); + /* record the use case */ + data->useCase = useCase; + + /* If ranging is in progress, let the work handler + * update the use case + */ + if (data->enable_ps_sensor) + data->updateUseCase = 1; + break; + + /* Config Custom use case */ + case VL53L0_IOCTL_ACTIVATE_CUSTOM_USE_CASE: + vl53l0_dbgmsg("VL53L0_IOCTL_ACTIVATE_CUSTOM_USE_CASE\n"); + if (copy_from_user(&customUseCase, + (struct stmvl53l0_custom_use_case *)p, + sizeof(struct stmvl53l0_custom_use_case))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + + data->sigmaLimit = customUseCase.sigmaLimit; + data->signalRateLimit = customUseCase.signalRateLimit; + data->preRangePulsePeriod = customUseCase.preRangePulsePeriod; + data->finalRangePulsePeriod = + customUseCase.finalRangePulsePeriod; + data->timingBudget = customUseCase.timingBudget; + vl53l0_dbgmsg( + "Sigma=%u,Signal=%u,Pre=%u,Final=%u,timingBudget=%u\n", + data->sigmaLimit, + data->signalRateLimit, + data->preRangePulsePeriod, + data->finalRangePulsePeriod, + data->timingBudget); + + /* record the use case */ + data->useCase = USE_CASE_CUSTOM; + /* If ranging is in progress, + * let the work handler update the use case + */ + if (data->enable_ps_sensor) + data->updateUseCase = 1; + + break; + + /* disable */ + case VL53L0_IOCTL_STOP: + vl53l0_dbgmsg("VL53L0_IOCTL_STOP\n"); + /* turn off tof sensor only if it's enabled by other client */ + if (data->enable_ps_sensor == 1) { + data->enable_ps_sensor = 0; + /* to stop */ + stmvl53l0_stop(data); + } + break; + /* Get all range data */ + case VL53L0_IOCTL_GETDATAS: + vl53l0_dbgmsg("VL53L0_IOCTL_GETDATAS\n"); + if (copy_to_user((VL53L0_RangingMeasurementData_t *)p, + &(data->rangeData), + sizeof(VL53L0_RangingMeasurementData_t))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + break; + /* Register tool */ + case VL53L0_IOCTL_REGISTER: + vl53l0_dbgmsg("VL53L0_IOCTL_REGISTER\n"); + if (copy_from_user(®, (struct stmvl53l0_register *)p, + sizeof(struct stmvl53l0_register))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + reg.status = 0; + page_num = (uint8_t)((reg.reg_index & 0x0000ff00) >> 8); + vl53l0_dbgmsg("VL53L0_IOCTL_REGISTER, page number:%d\n", page_num); + if (page_num != 0) + reg.status = VL53L0_WrByte(vl53l0_dev, 0xFF, page_num); + + switch (reg.reg_bytes) { + case(4): + if (reg.is_read) + reg.status = VL53L0_RdDWord(vl53l0_dev, + (uint8_t)reg.reg_index, + ®.reg_data); + else + reg.status = VL53L0_WrDWord(vl53l0_dev, + (uint8_t)reg.reg_index, + reg.reg_data); + break; + case(2): + if (reg.is_read) + reg.status = VL53L0_RdWord(vl53l0_dev, + (uint8_t)reg.reg_index, + (uint16_t *)®.reg_data); + else + reg.status = VL53L0_WrWord(vl53l0_dev, + (uint8_t)reg.reg_index, + (uint16_t)reg.reg_data); + break; + case(1): + if (reg.is_read) + reg.status = VL53L0_RdByte(vl53l0_dev, + (uint8_t)reg.reg_index, + (uint8_t *)®.reg_data); + else + reg.status = VL53L0_WrByte(vl53l0_dev, + (uint8_t)reg.reg_index, + (uint8_t)reg.reg_data); + break; + default: + reg.status = -1; + + } + if (page_num != 0) + reg.status = VL53L0_WrByte(vl53l0_dev, 0xFF, 0); + + + if (copy_to_user((struct stmvl53l0_register *)p, ®, + sizeof(struct stmvl53l0_register))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + break; + /* parameter access */ + case VL53L0_IOCTL_PARAMETER: + vl53l0_dbgmsg("VL53L0_IOCTL_PARAMETER\n"); + if (copy_from_user(¶meter, (struct stmvl53l0_parameter *)p, + sizeof(struct stmvl53l0_parameter))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + parameter.status = 0; + if (data->enableDebug) + vl53l0_dbgmsg( + "VL53L0_IOCTL_PARAMETER Name = %d\n", parameter.name); + switch (parameter.name) { + case (OFFSET_PAR): + if (parameter.is_read) + parameter.status = + papi_func_tbl->GetOffsetCalibrationDataMicroMeter( + vl53l0_dev, ¶meter.value); + else { + parameter.status = + papi_func_tbl->SetOffsetCalibrationDataMicroMeter( + vl53l0_dev, parameter.value); + data->OffsetMicroMeter = parameter.value; + data->setCalibratedValue + |= SET_OFFSET_CALIB_DATA_MICROMETER_MASK; + + } + vl53l0_dbgmsg("get parameter value as %d\n", + parameter.value); + break; + + case (REFERENCESPADS_PAR): + if (parameter.is_read) { + parameter.status = + papi_func_tbl->GetReferenceSpads(vl53l0_dev, + (uint32_t *)&(parameter.value), + (uint8_t *)&(parameter.value2)); + if (data->enableDebug) + vl53l0_dbgmsg( + "Get RefSpad : Count:%u, Type:%u\n", + parameter.value, + (uint8_t)parameter.value2); + } else { + if (data->enableDebug) + vl53l0_dbgmsg( + "Set RefSpad : Count:%u, Type:%u\n", + parameter.value, + (uint8_t)parameter.value2); + + parameter.status = + papi_func_tbl->SetReferenceSpads(vl53l0_dev, + (uint32_t)(parameter.value), + (uint8_t)(parameter.value2)); + + data->refSpadCount = parameter.value; + data->isApertureSpads = + (uint8_t)(parameter.value2); + } + break; + + case (REFCALIBRATION_PAR): + if (parameter.is_read) { + parameter.status = + papi_func_tbl->GetRefCalibration(vl53l0_dev, + (uint8_t *)&(parameter.value), + (uint8_t *)&(parameter.value2)); + if (data->enableDebug) + vl53l0_dbgmsg( + "Get Ref : Vhv:%u, PhaseCal:%u\n", + (uint8_t)parameter.value, + (uint8_t)parameter.value2); + } else { + if (data->enableDebug) + vl53l0_dbgmsg( + "Set Ref : Vhv:%u, PhaseCal:%u\n", + (uint8_t)parameter.value, + (uint8_t)parameter.value2); + parameter.status = + papi_func_tbl->SetRefCalibration( + vl53l0_dev, + (uint8_t)(parameter.value), + (uint8_t)(parameter.value2)); + data->VhvSettings = (uint8_t)parameter.value; + data->PhaseCal = (uint8_t)(parameter.value2); + } + break; + case (XTALKRATE_PAR): + if (parameter.is_read) + parameter.status = + papi_func_tbl->GetXTalkCompensationRateMegaCps( + vl53l0_dev, + (FixPoint1616_t *) + ¶meter.value); + else { + /* Range Ignore Threshold value */ + FixPoint1616_t ritValue = 0; + + parameter.status = + papi_func_tbl->SetXTalkCompensationRateMegaCps( + vl53l0_dev, + (FixPoint1616_t) + parameter.value); + data->XTalkCompensationRateMegaCps = + parameter.value; + data->setCalibratedValue |= + SET_XTALK_COMP_RATE_MCPS_MASK; + + + /*0.7 KCps converted to MCps */ + if (data->XTalkCompensationRateMegaCps < + 7*65536/10000) { + ritValue = 15 * 7 * 65536/100000; + } else { + ritValue = 15 * + vl53l0_dev->XTalkCompensationRateMegaCps + /10; + } + + if (papi_func_tbl->SetLimitCheckEnable + != NULL) { + Status = + papi_func_tbl->SetLimitCheckEnable( + vl53l0_dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + 1); + } + + if ((Status == VL53L0_ERROR_NONE) && + (papi_func_tbl->SetLimitCheckValue + != NULL)) { + vl53l0_dbgmsg( + "Set RIT - %u\n", ritValue); + Status = + papi_func_tbl->SetLimitCheckValue( + vl53l0_dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + ritValue); + } + + } + break; + case (XTALKENABLE_PAR): + if (parameter.is_read) + parameter.status = + papi_func_tbl->GetXTalkCompensationEnable( + vl53l0_dev, + (uint8_t *) ¶meter.value); + else + parameter.status = + papi_func_tbl->SetXTalkCompensationEnable( + vl53l0_dev, + (uint8_t) parameter.value); + break; + case (GPIOFUNC_PAR): + if (parameter.is_read) { + parameter.status = + papi_func_tbl->GetGpioConfig( + vl53l0_dev, + 0, + &deviceMode, + &data->gpio_function, + &data->gpio_polarity); + parameter.value = + data->gpio_function; + } else { + data->gpio_function = parameter.value; + parameter.status = + papi_func_tbl->SetGpioConfig( + vl53l0_dev, + 0, + 0, + data->gpio_function, + data->gpio_polarity); + } + break; + case (LOWTHRESH_PAR): + if (parameter.is_read) { + parameter.status = + papi_func_tbl->GetInterruptThresholds( + vl53l0_dev, + 0, + &(data->low_threshold), + &(data->high_threshold)); + parameter.value = + data->low_threshold >> 16; + } else { + data->low_threshold = parameter.value << 16; + parameter.status = + papi_func_tbl->SetInterruptThresholds( + vl53l0_dev, + 0, + data->low_threshold, + data->high_threshold); + } + break; + case (HIGHTHRESH_PAR): + if (parameter.is_read) { + parameter.status = + papi_func_tbl->GetInterruptThresholds( + vl53l0_dev, + 0, + &(data->low_threshold), + &(data->high_threshold)); + parameter.value = + data->high_threshold >> 16; + } else { + data->high_threshold = + parameter.value << 16; + parameter.status = + papi_func_tbl->SetInterruptThresholds( + vl53l0_dev, + 0, + data->low_threshold, + data->high_threshold); + } + break; + case (DEVICEMODE_PAR): + if (parameter.is_read) { + parameter.status = + papi_func_tbl->GetDeviceMode( + vl53l0_dev, + (VL53L0_DeviceModes *)&(parameter.value)); + } else { + parameter.status = + papi_func_tbl->SetDeviceMode( + vl53l0_dev, + (VL53L0_DeviceModes)(parameter.value)); + data->deviceMode = + (VL53L0_DeviceModes)(parameter.value); + } + break; + + + + case (INTERMEASUREMENT_PAR): + if (parameter.is_read) { + parameter.status = + papi_func_tbl->GetInterMeasurementPeriodMilliSeconds( + vl53l0_dev, + (uint32_t *)&(parameter.value)); + } else { + parameter.status = + papi_func_tbl->SetInterMeasurementPeriodMilliSeconds( + vl53l0_dev, + (uint32_t)(parameter.value)); + data->interMeasurems = parameter.value; + } + break; + + } + + if (copy_to_user((struct stmvl53l0_parameter *)p, ¶meter, + sizeof(struct stmvl53l0_parameter))) { + vl53l0_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + break; + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static int stmvl53l0_open(struct inode *inode, struct file *file) +{ + return 0; +} + +/* Flush will be invoked on close(device_fd) */ +static int stmvl53l0_flush(struct file *file, fl_owner_t id) +{ + struct stmvl53l0_data *data = container_of(file->private_data, + struct stmvl53l0_data, miscdev); + (void) file; + (void) id; + /* Revisit : Check if the + *instance are opened multiple times on some platforms + */ + mutex_lock(&data->work_mutex); + if (data) { + if (data->enable_ps_sensor == 1) { + /* turn off tof sensor if it's enabled */ + data->enable_ps_sensor = 0; + /* to stop */ + stmvl53l0_stop(data); + } + } + mutex_unlock(&data->work_mutex); + + return 0; +} + +static long stmvl53l0_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + long ret; + struct stmvl53l0_data *data = + container_of(file->private_data, + struct stmvl53l0_data, miscdev); + mutex_lock(&data->work_mutex); + ret = stmvl53l0_ioctl_handler(file, cmd, arg, (void __user *)arg); + mutex_unlock(&data->work_mutex); + + return ret; +} + +#ifdef CONFIG_COMPAT +static long stmvl53l0_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + long ret; + struct stmvl53l0_data *data = + container_of(file->private_data, + struct stmvl53l0_data, miscdev); + mutex_lock(&data->work_mutex); + ret = stmvl53l0_ioctl_handler(file, cmd, arg, compat_ptr(arg)); + mutex_unlock(&data->work_mutex); + + return ret; +} +#endif + +int stmvl53l0_checkmoduleid(struct stmvl53l0_data *data, + void *client, uint8_t type) +{ + uint8_t id = 0; + int err; + + vl53l0_dbgmsg("Enter\n"); + if (type == CCI_BUS) { + } else { + struct i2c_client *i2c_pclient = (struct i2c_client *)client; + struct i2c_msg msg[1]; + uint8_t buff; + + buff = 0xc0; + msg[0].addr = i2c_pclient->addr; + msg[0].flags = I2C_M_WR; + msg[0].buf = &buff; + msg[0].len = 1; + + err = i2c_transfer(i2c_pclient->adapter, msg, 1); + if (err != 1) { + pr_err("%s: i2c_transfer err:%d, addr:0x%x, reg:0x%x\n", + __func__, err, i2c_pclient->addr, buff); + err = -1; + return err; + } + + msg[0].addr = i2c_pclient->addr; + msg[0].flags = I2C_M_RD | i2c_pclient->flags; + msg[0].buf = &id; + msg[0].len = 1; + + err = i2c_transfer(i2c_pclient->adapter, &msg[0], 1); + if (err != 1) { + pr_err("%s: Read i2c_transfer err:%d, addr:0x%x\n", + __func__, err, i2c_pclient->addr); + err = -1; + return err; + } + } + /* Read Model ID */ + + vl53l0_dbgmsg("read MODLE_ID: 0x%x\n", id); + if (id == 0xee) { + vl53l0_errmsg("STM VL53L0 Found %d\n", __LINE__); + return 0; + } + + vl53l0_errmsg("Not found STM VL53L0 %d\n", __LINE__); + return -EIO; +} + +/* + * Initialization function + */ +static int stmvl53l0_init_client(struct stmvl53l0_data *data) +{ + + VL53L0_Error Status = VL53L0_ERROR_NONE; + VL53L0_DeviceInfo_t DeviceInfo; + VL53L0_DEV vl53l0_dev = data; + uint32_t refSpadCount; + uint8_t isApertureSpads; + uint8_t VhvSettings; + uint8_t PhaseCal; + uint8_t revision = 1; + uint8_t retry = 2; + + vl53l0_dbgmsg("Enter\n"); + + data->I2cDevAddr = 0x52; + data->comms_type = 1; + data->comms_speed_khz = 400; + + /* Setup API functions based on revision */ + do { + retry--; + revision = stmvl53l0_setupAPIFunctions(data); + if ((revision == 1) || (retry == 0)) { + break; + } else { + vl53l0_dbgmsg("retry to stmvl53l0_setupAPIFunctions\n"); + msleep(20); + } + } while (retry > 0); + + if (revision == 0) + return VL53L0_ERROR_CONTROL_INTERFACE; + + /* Perform Ref and RefSpad calibrations and save the values */ + + if (data->reset) { + pr_err("Call of VL53L0_DataInit\n"); + /* Data initialization */ + Status = papi_func_tbl->DataInit(vl53l0_dev); + /* data->reset = 0; */ + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "%d- error status %d\n", __LINE__, Status); + return Status; + } + } + + vl53l0_dbgmsg("VL53L0_GetDeviceInfo:\n"); + Status = papi_func_tbl->GetDeviceInfo(vl53l0_dev, &DeviceInfo); + if (Status == VL53L0_ERROR_NONE) { + pr_err("Device Name : %s\n", DeviceInfo.Name); + pr_err("Device Type : %s\n", DeviceInfo.Type); + pr_err("Device ID : %s\n", DeviceInfo.ProductId); + pr_err("Product type: %d\n", DeviceInfo.ProductType); + pr_err("ProductRevisionMajor : %d\n", + DeviceInfo.ProductRevisionMajor); + pr_err("ProductRevisionMinor : %d\n", + DeviceInfo.ProductRevisionMinor); + } + /* Device Initialization */ + vl53l0_dbgmsg("Call of VL53L0_StaticInit\n"); + Status = papi_func_tbl->StaticInit(vl53l0_dev); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg("%d- error status %d\n", __LINE__, Status); + return Status; + } + + if (papi_func_tbl->PerformRefCalibration != NULL && data->reset) { + vl53l0_dbgmsg("Call of VL53L0_PerformRefCalibration\n"); + Status = papi_func_tbl->PerformRefCalibration(vl53l0_dev, + &VhvSettings, &PhaseCal); /* Ref calibration */ + if (Status != + VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "%d- error status %d\n", __LINE__, Status); + return Status; + } + } + vl53l0_dbgmsg("VHV = %u, PhaseCal = %u\n", VhvSettings, PhaseCal); + vl53l0_dev->VhvSettings = VhvSettings; + vl53l0_dev->PhaseCal = PhaseCal; + + if (papi_func_tbl->PerformRefSpadManagement != NULL && data->reset) { + vl53l0_dbgmsg( + "Call of VL53L0_PerformRefSpadManagement\n"); + Status = papi_func_tbl->PerformRefSpadManagement(vl53l0_dev, + &refSpadCount, + &isApertureSpads); /* Ref Spad Management */ + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "%d- error status %d\n", __LINE__, Status); + return Status; + } + } + + vl53l0_dbgmsg( + "SpadCount = %u, isAperature = %u\n", + refSpadCount, isApertureSpads); + vl53l0_dev->refSpadCount = refSpadCount; + vl53l0_dev->isApertureSpads = isApertureSpads; + + if (Status == VL53L0_ERROR_NONE && data->reset) { + if ((papi_func_tbl->SetOffsetCalibrationDataMicroMeter != NULL) + && (vl53l0_dev->setCalibratedValue & + SET_OFFSET_CALIB_DATA_MICROMETER_MASK)) { + vl53l0_dbgmsg( + "Call of SetOffsetCalibrationDataMicroMeter\n"); + Status = + papi_func_tbl->SetOffsetCalibrationDataMicroMeter( + vl53l0_dev, + vl53l0_dev->OffsetMicroMeter); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "%d- error status %d\n", __LINE__, Status); + return Status; + } + } + } + + if (data->reset) { + if ((papi_func_tbl->SetXTalkCompensationRateMegaCps != NULL) && + (vl53l0_dev->setCalibratedValue & + SET_XTALK_COMP_RATE_MCPS_MASK)) { + vl53l0_dbgmsg( + "Call of SetXTalkCompensationRateMegaCps\n"); + Status = papi_func_tbl->SetXTalkCompensationRateMegaCps( + vl53l0_dev, + vl53l0_dev->XTalkCompensationRateMegaCps); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "%d- error status %d\n", + __LINE__, Status); + return Status; + } + } + } + + if (data->reset) { + if (vl53l0_dev->setCalibratedValue & + /* Xtalk calibration done*/ + SET_XTALK_COMP_RATE_MCPS_MASK) { + /* Range Ignore Threshold */ + FixPoint1616_t ritValue = 0; + + if (vl53l0_dev->XTalkCompensationRateMegaCps < + 7*65536/10000) { + /*0.7 KCps converted to MCps */ + ritValue = 15 * 7 * 65536/100000; + } else { + ritValue = 15 * + vl53l0_dev->XTalkCompensationRateMegaCps/10; + } + + if (papi_func_tbl->SetLimitCheckEnable != NULL) { + Status = papi_func_tbl->SetLimitCheckEnable( + vl53l0_dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + 1); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "%d- error status %d\n", __LINE__, + Status); + return Status; + } + } + + if (papi_func_tbl->SetLimitCheckValue != NULL) { + vl53l0_dbgmsg("Set RIT - %u\n", ritValue); + Status = papi_func_tbl->SetLimitCheckValue( + vl53l0_dev, + VL53L0_CHECKENABLE_RANGE_IGNORE_THRESHOLD, + ritValue); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "%d- error status %d\n", + __LINE__, Status); + return Status; + } + } + } + data->reset = 0; + } + + /* Setup in single ranging mode */ + pr_err("Call of VL53L0_SetDeviceMode\n"); + Status = papi_func_tbl->SetDeviceMode(vl53l0_dev, + VL53L0_DEVICEMODE_SINGLE_RANGING); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg("%d- error status %d\n", __LINE__, Status); + return Status; + } + + Status = papi_func_tbl->SetWrapAroundCheckEnable(vl53l0_dev, 1); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg("%d- error status %d\n", __LINE__, Status); + return Status; + } + + if (data->offset != 0) + papi_func_tbl->SetOffsetCalibrationDataMicroMeter( + vl53l0_dev, data->offset); + if (data->xtalk != 0) + papi_func_tbl->SetXTalkCompensationRateMegaCps( + vl53l0_dev, (FixPoint1616_t)data->xtalk); + papi_func_tbl->SetXTalkCompensationEnable(vl53l0_dev, 1); + + vl53l0_dbgmsg("End\n"); + + return 0; +} + +static int stmvl53l0_config_use_case(struct stmvl53l0_data *data) +{ + VL53L0_DEV vl53l0_dev = data; + VL53L0_Error Status = VL53L0_ERROR_NONE; + FixPoint1616_t signalRateLimit; + FixPoint1616_t sigmaLimit; + uint32_t preRangePulsePeriod; + uint32_t finalRangePulsePeriod; + + vl53l0_dbgmsg("Enter\n"); + + switch (vl53l0_dev->useCase) { + + case USE_CASE_LONG_DISTANCE: + sigmaLimit = LONG_DISTANCE_SIGMA_LIMIT; + signalRateLimit = LONG_DISTANCE_SIGNAL_RATE_LIMIT; + preRangePulsePeriod = LONG_DISTANCE_PRE_RANGE_PULSE_PERIOD; + finalRangePulsePeriod = LONG_DISTANCE_FINAL_RANGE_PULSE_PERIOD; + break; + + case USE_CASE_HIGH_ACCURACY: + sigmaLimit = HIGH_ACCURACY_SIGMA_LIMIT; + signalRateLimit = HIGH_ACCURACY_SIGNAL_RATE_LIMIT; + preRangePulsePeriod = HIGH_ACCURACY_PRE_RANGE_PULSE_PERIOD; + finalRangePulsePeriod = HIGH_ACCURACY_FINAL_RANGE_PULSE_PERIOD; + break; + + case USE_CASE_HIGH_SPEED: + sigmaLimit = HIGH_SPEED_SIGMA_LIMIT; + signalRateLimit = HIGH_SPEED_SIGNAL_RATE_LIMIT; + preRangePulsePeriod = HIGH_SPEED_PRE_RANGE_PULSE_PERIOD; + finalRangePulsePeriod = HIGH_SPEED_FINAL_RANGE_PULSE_PERIOD; + break; + + case USE_CASE_CUSTOM: + /* Set by application through IOCTL interface */ + sigmaLimit = vl53l0_dev->sigmaLimit; + signalRateLimit = vl53l0_dev->signalRateLimit; + preRangePulsePeriod = vl53l0_dev->preRangePulsePeriod; + finalRangePulsePeriod = vl53l0_dev->finalRangePulsePeriod; + break; + + default: + vl53l0_errmsg( + "Invalid use case = %d\n", vl53l0_dev->useCase); + /* Invalid parameter, should not reach here */ + return -EINVAL; + } + + vl53l0_dbgmsg( + "Configure UseCase(%d) : Sigma=%u,Signal=%u,Pre=%u,Final=%u,timingBudget=%u\n", + vl53l0_dev->useCase, + sigmaLimit, + signalRateLimit, + preRangePulsePeriod, + finalRangePulsePeriod, + vl53l0_dev->timingBudget); + + if (papi_func_tbl->SetLimitCheckEnable != NULL) { + Status = papi_func_tbl->SetLimitCheckEnable( + vl53l0_dev, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + 1); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = papi_func_tbl->SetLimitCheckEnable( + vl53l0_dev, + VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + 1); + } else { + vl53l0_errmsg( + "SetLimitCheckEnable(SIGMA_FINAL_RANGE) failed with errcode = %d\n", + Status); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = papi_func_tbl->SetLimitCheckValue( + vl53l0_dev, + VL53L0_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + signalRateLimit); + } else { + vl53l0_errmsg( + "SetLimitCheckEnable(SIGNAL_RATE_FINAL_RANGE) failed with errcode = %d\n", + Status); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = papi_func_tbl->SetLimitCheckValue(vl53l0_dev, + VL53L0_CHECKENABLE_SIGMA_FINAL_RANGE, + sigmaLimit); + } else { + vl53l0_dbgmsg( + "SIGNAL_RATE_FINAL_RANGE failed with errcode = %d\n", + Status); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = + papi_func_tbl->SetMeasurementTimingBudgetMicroSeconds( + vl53l0_dev, + vl53l0_dev->timingBudget); + } else { + vl53l0_dbgmsg( + "SIGMA_FINAL_RANGE failed with errcode = %d\n", + Status); + } + + + if (Status == VL53L0_ERROR_NONE) { + Status = papi_func_tbl->SetVcselPulsePeriod(vl53l0_dev, + VL53L0_VCSEL_PERIOD_PRE_RANGE, + preRangePulsePeriod); + } else { + vl53l0_dbgmsg( + "SetMeasurementTimingBudget failed with errcode = %d\n", + Status); + } + + if (Status == VL53L0_ERROR_NONE) { + Status = papi_func_tbl->SetVcselPulsePeriod(vl53l0_dev, + VL53L0_VCSEL_PERIOD_FINAL_RANGE, + finalRangePulsePeriod); + } else + vl53l0_dbgmsg( + "SetVcselPulsePeriod(PRE) failed with errcode = %d\n", Status); + + if (Status != VL53L0_ERROR_NONE) + vl53l0_dbgmsg( + "SetVcselPulsePeriod(FINAL)failed with errcode = %d\n", Status); + + vl53l0_dbgmsg("End\n"); + return Status; +} + +static int stmvl53l0_start(struct stmvl53l0_data *data, uint8_t scaling, + init_mode_e mode) +{ + int rc = 0; + VL53L0_DEV vl53l0_dev = data; + VL53L0_Error Status = VL53L0_ERROR_NONE; + + vl53l0_errmsg("Enter\n"); + + /* Power up */ + rc = pmodule_func_tbl->power_up(data->client_object, &data->reset); + if (rc) { + vl53l0_errmsg("%d,error rc %d\n", __LINE__, rc); + return rc; + } + + /* init */ + rc = stmvl53l0_init_client(data); + if (rc) { + vl53l0_errmsg("%d, error rc %d\n", __LINE__, rc); + pmodule_func_tbl->power_down(data->client_object); + return -EINVAL; + } + + /* check mode */ + if (mode != NORMAL_MODE) + papi_func_tbl->SetXTalkCompensationEnable(vl53l0_dev, 1); + + if (mode == OFFSETCALIB_MODE) { + /*VL53L0_SetOffsetCalibrationDataMicroMeter(vl53l0_dev, 0);*/ + FixPoint1616_t OffsetMicroMeter; + + papi_func_tbl->PerformOffsetCalibration(vl53l0_dev, + (data->offsetCalDistance<<16), + &OffsetMicroMeter); + pr_err("Offset calibration:%u\n", OffsetMicroMeter); + vl53l0_dev->OffsetMicroMeter = OffsetMicroMeter; + vl53l0_dev->setCalibratedValue |= + SET_OFFSET_CALIB_DATA_MICROMETER_MASK; + + return rc; + } else if (mode == XTALKCALIB_MODE) { + FixPoint1616_t XTalkCompensationRateMegaCps; + /*caltarget distance : 100mm and convert to + * fixed point 16 16 format + */ + papi_func_tbl->PerformXTalkCalibration(vl53l0_dev, + (data->xtalkCalDistance<<16), + &XTalkCompensationRateMegaCps); + pr_err("Xtalk calibration:%u\n", + XTalkCompensationRateMegaCps); + vl53l0_dev->XTalkCompensationRateMegaCps = + XTalkCompensationRateMegaCps; + vl53l0_dev->setCalibratedValue |= + SET_XTALK_COMP_RATE_MCPS_MASK; + + return rc; + } + + /* set up device parameters */ + data->gpio_polarity = VL53L0_INTERRUPTPOLARITY_LOW; + + /* Following two calls are made from IOCTL as well */ + Status = papi_func_tbl->SetGpioConfig(vl53l0_dev, 0, 0, + data->gpio_function, + VL53L0_INTERRUPTPOLARITY_LOW); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg("Failed to SetGpioConfig. Error = %d\n", Status); + return -EPERM; + } + + Status = papi_func_tbl->SetInterruptThresholds(vl53l0_dev, 0, + data->low_threshold, + data->high_threshold); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "Failed to SetInterruptThresholds. Error = %d\n", Status); + return -EPERM; + } + + if (data->deviceMode == VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING) { + Status = papi_func_tbl->SetInterMeasurementPeriodMilliSeconds( + vl53l0_dev, + data->interMeasurems); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "Failed to SetInterMeasurementPeriodMilliSeconds. Error = %d\n", + Status); + return -EPERM; + } + pr_err( + "DeviceMode:0x%x, interMeasurems:%d==\n", + data->deviceMode, + data->interMeasurems); + } + + Status = papi_func_tbl->SetDeviceMode( + vl53l0_dev, + data->deviceMode); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg("Failed to SetDeviceMode. Error = %d\n", Status); + return -EPERM; + } + + Status = papi_func_tbl->ClearInterruptMask(vl53l0_dev, + 0); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "Failed to ClearInterruptMask. Error = %d\n", Status); + return -EPERM; + } + + Status = stmvl53l0_config_use_case(vl53l0_dev); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "Failed to configure Use case = %u\n", + vl53l0_dev->useCase); + return -EPERM; + } + + /* start the ranging */ + Status = papi_func_tbl->StartMeasurement(vl53l0_dev); + if (Status != VL53L0_ERROR_NONE) { + vl53l0_errmsg( + "Failed to StartMeasurement. Error = %d\n", Status); + return -EPERM; + } + + data->enable_ps_sensor = 1; + +#ifndef USE_INT + /* Unblock the thread execution */ + wake_up(&vl53l0_dev->poll_thread_wq); +#endif + + vl53l0_dbgmsg("End\n"); + + return rc; +} + +VL53L0_Error WaitStopCompleted(VL53L0_DEV Dev) +{ + VL53L0_Error Status = VL53L0_ERROR_NONE; + uint32_t StopCompleted = 0; + uint32_t LoopNb; + + vl53l0_dbgmsg("Enter\n"); + + /* Wait until it finished + * use timeout to avoid deadlock + */ + if (Status == VL53L0_ERROR_NONE) { + LoopNb = 0; + do { + Status = papi_func_tbl->GetStopCompletedStatus(Dev, + &StopCompleted); + if ((StopCompleted == 0x00) || + (Status != VL53L0_ERROR_NONE)) + break; + + LoopNb = LoopNb + 1; + VL53L0_PollingDelay(Dev); + } while (LoopNb < VL53L0_DEFAULT_MAX_LOOP); + + if (LoopNb >= VL53L0_DEFAULT_MAX_LOOP) + Status = VL53L0_ERROR_TIME_OUT; + + } + + vl53l0_dbgmsg("End WaitStopCompleted - %d\n", Status); + return Status; +} + +static int stmvl53l0_stop(struct stmvl53l0_data *data) +{ + int rc = 0; + VL53L0_DEV vl53l0_dev = data; + + vl53l0_errmsg("Enter\n"); + + /* stop - if continuous mode */ + if (data->deviceMode == VL53L0_DEVICEMODE_CONTINUOUS_RANGING || + data->deviceMode == VL53L0_DEVICEMODE_CONTINUOUS_TIMED_RANGING) + papi_func_tbl->StopMeasurement(vl53l0_dev); + + /* clean interrupt */ + papi_func_tbl->ClearInterruptMask(vl53l0_dev, 0); + + + WaitStopCompleted(vl53l0_dev); + + /* cancel work handler */ + stmvl53l0_cancel_handler(data); + + /* Clear updateUseCase pending operation */ + data->updateUseCase = 0; + /* power down */ + rc = pmodule_func_tbl->power_down(data->client_object); + if (rc) { + vl53l0_errmsg("%d, error rc %d\n", __LINE__, rc); + return rc; + } + vl53l0_dbgmsg("End\n"); + + return rc; +} + +static void stmvl53l0_timer_fn(unsigned long data) +{ + VL53L0_DEV vl53l0_dev = (VL53L0_DEV)data; + + vl53l0_dev->flushCount++; + + input_report_abs(vl53l0_dev->input_dev_ps, ABS_GAS, + vl53l0_dev->flushCount); + + + input_sync(vl53l0_dev->input_dev_ps); + + vl53l0_dbgmsg("Sensor HAL Flush Count = %u\n", vl53l0_dev->flushCount); +} + +/* + * I2C init/probing/exit functions + */ +static const struct file_operations stmvl53l0_ranging_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = stmvl53l0_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = stmvl53l0_compat_ioctl, +#endif + .open = stmvl53l0_open, + .flush = stmvl53l0_flush, +}; + +int stmvl53l0_setup(struct stmvl53l0_data *data) +{ + int rc = 0; + +#ifdef USE_INT + int irq = 0; + int gpio; +#endif + + vl53l0_dbgmsg("Enter\n"); + + /* init mutex */ + mutex_init(&data->update_lock); + mutex_init(&data->work_mutex); + +#ifdef USE_INT + /* init interrupt */ + gpio = data->client_object.gconf.cam_gpio_req_tbl[2].gpio; + gpio_request(gpio, "vl53l0_gpio_int"); + vl53l0_dbgmsg("gpio_request %d to register_irq\n", gpio); + gpio_direction_input(gpio); + irq = gpio_to_irq(gpio); + if (irq < 0) { + vl53l0_errmsg("%d,filed to map GPIO: %d to interrupt:%d\n", + __LINE__, gpio, irq); + } else { + vl53l0_dbgmsg("register_irq:%d\n", irq); + /* IRQF_TRIGGER_FALLING- poliarity:0 IRQF_TRIGGER_RISNG - + * poliarty:1 + */ + rc = request_threaded_irq(irq, NULL, + stmvl53l0_interrupt_handler, + IRQF_TRIGGER_FALLING|IRQF_ONESHOT, + "vl53l0_interrupt", + (void *)data); + if (rc) { + vl53l0_errmsg("%d, Could not allocate STMVL53L0_INT ! result:%d\n", __LINE__, rc); + free_irq(irq, data); + goto exit_free_irq; + } + } + data->irq = irq; + vl53l0_errmsg("interrupt is hooked\n"); +#else + + init_waitqueue_head(&data->poll_thread_wq); + + data->poll_thread = kthread_run(&stmvl53l0_poll_thread, + (void *)data, + "STM-VL53L0"); + if (data->poll_thread == NULL) { + pr_err( + "%s(%d) - Failed to create Polling thread\n", __func__, __LINE__); + goto exit_free_irq; + } +#endif + + /* init work handler */ + INIT_DELAYED_WORK(&data->dwork, stmvl53l0_work_handler); + + /* Register to Input Device */ + data->input_dev_ps = input_allocate_device(); + if (!data->input_dev_ps) { + rc = -ENOMEM; + vl53l0_errmsg("%d error:%d\n", __LINE__, rc); + goto exit_free_irq; + } + set_bit(EV_ABS, data->input_dev_ps->evbit); + /* range in cm*/ + input_set_abs_params(data->input_dev_ps, ABS_DISTANCE, 0, 76, 0, 0); + /* tv_sec */ + input_set_abs_params(data->input_dev_ps, ABS_HAT0X, 0, 0xffffffff, + 0, 0); + /* tv_usec */ + input_set_abs_params(data->input_dev_ps, ABS_HAT0Y, 0, 0xffffffff, + 0, 0); + /* range in_mm */ + input_set_abs_params(data->input_dev_ps, ABS_HAT1X, 0, 765, 0, 0); + /* error code change maximum to 0xff for more flexibility */ + input_set_abs_params(data->input_dev_ps, ABS_HAT1Y, 0, 0xff, 0, 0); + /* rtnRate */ + input_set_abs_params(data->input_dev_ps, ABS_HAT2X, 0, 0xffffffff, + 0, 0); + /* rtn_amb_rate */ + input_set_abs_params(data->input_dev_ps, ABS_HAT2Y, 0, 0xffffffff, + 0, 0); + /* rtn_conv_time */ + input_set_abs_params(data->input_dev_ps, ABS_HAT3X, 0, 0xffffffff, + 0, 0); + /* dmax */ + input_set_abs_params(data->input_dev_ps, ABS_HAT3Y, 0, 0xffffffff, + 0, 0); + + input_set_abs_params(data->input_dev_ps, ABS_MISC, 0, 0xffffffff, + 0, 0); + + input_set_abs_params(data->input_dev_ps, ABS_WHEEL, 0, 0xffffffff, + 0, 0); + + input_set_abs_params(data->input_dev_ps, ABS_GAS, 0, 0xffffffff, + 0, 0); + data->input_dev_ps->name = "STM VL53L0 proximity sensor"; + + rc = input_register_device(data->input_dev_ps); + if (rc) { + rc = -ENOMEM; + vl53l0_errmsg("%d error:%d\n", __LINE__, rc); + goto exit_free_dev_ps; + } + /* setup drv data */ + input_set_drvdata(data->input_dev_ps, data); + + /* Register sysfs hooks */ + data->range_kobj = kobject_create_and_add("range", kernel_kobj); + if (!data->range_kobj) { + rc = -ENOMEM; + vl53l0_errmsg("%d error:%d\n", __LINE__, rc); + goto exit_unregister_dev_ps; + } + rc = sysfs_create_group(&data->input_dev_ps->dev.kobj, + &stmvl53l0_attr_group); + if (rc) { + rc = -ENOMEM; + vl53l0_errmsg("%d error:%d\n", __LINE__, rc); + goto exit_unregister_dev_ps_1; + } + + setup_timer(&data->timer, stmvl53l0_timer_fn, (unsigned long)data); + + /* to register as a misc device */ + data->miscdev.minor = MISC_DYNAMIC_MINOR; + data->dev_name = devname; + data->miscdev.name = data->dev_name; + data->miscdev.fops = &stmvl53l0_ranging_fops; + vl53l0_errmsg("Misc device registration name:%s\n", data->dev_name); + if (misc_register(&data->miscdev) != 0) + vl53l0_errmsg("Could not register misc. dev for stmvl53l0 ranging\n"); + + kobject_uevent_env(&data->input_dev_ps->dev.kobj, KOBJ_CHANGE, NULL); + + /* init default device parameter value */ + data->enable_ps_sensor = 0; + data->reset = 1; + data->delay_ms = 30; /* delay time to 30ms */ + data->enableDebug = 0; + data->gpio_polarity = VL53L0_INTERRUPTPOLARITY_LOW; + data->gpio_function = VL53L0_GPIOFUNCTIONALITY_NEW_MEASURE_READY; + data->low_threshold = 60; + data->high_threshold = 200; + data->deviceMode = VL53L0_DEVICEMODE_SINGLE_RANGING; + data->interMeasurems = 30; + data->useCase = USE_CASE_LONG_DISTANCE; + data->timingBudget = LONG_DISTANCE_TIMING_BUDGET; + + /* Set default values used in Custom Mode Use Case */ + data->signalRateLimit = LONG_DISTANCE_SIGNAL_RATE_LIMIT; + data->sigmaLimit = LONG_DISTANCE_SIGMA_LIMIT; + data->preRangePulsePeriod = LONG_DISTANCE_PRE_RANGE_PULSE_PERIOD; + data->finalRangePulsePeriod = LONG_DISTANCE_FINAL_RANGE_PULSE_PERIOD; + + vl53l0_errmsg("support ver. %s enabled\n", DRIVER_VERSION); + vl53l0_dbgmsg("End"); + + return 0; +exit_unregister_dev_ps_1: + kobject_put(data->range_kobj); +exit_unregister_dev_ps: + input_unregister_device(data->input_dev_ps); +exit_free_dev_ps: + input_free_device(data->input_dev_ps); +exit_free_irq: +#ifdef USE_INT + free_irq(irq, data); +#endif + kfree(data); + return rc; +} + +void stmvl53l0_cleanup(struct stmvl53l0_data *data) +{ +#ifndef USE_INT + pr_err("%s(%d) : Stop poll_thread\n", __func__, __LINE__); + poll_thread_exit = 1; + kthread_stop(data->poll_thread); +#endif +} + +static int __init stmvl53l0_init(void) +{ + int ret = -1; + + vl53l0_dbgmsg("Enter\n"); + + /* assign function table */ + pmodule_func_tbl = &stmvl53l0_module_func_tbl; + papi_func_tbl = &stmvl53l0_api_func_tbl; + + /* client specific init function */ + ret = pmodule_func_tbl->init(); + + if (ret) + vl53l0_errmsg("%d failed with %d\n", __LINE__, ret); + + vl53l0_dbgmsg("End\n"); + + return ret; +} + +static void __exit stmvl53l0_exit(void) +{ + vl53l0_dbgmsg("Enter\n"); + + vl53l0_dbgmsg("End\n"); +} + +MODULE_AUTHOR("STMicroelectronics Imaging Division"); +MODULE_DESCRIPTION("ST FlightSense Time-of-Flight sensor driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); + +module_init(stmvl53l0_init); +module_exit(stmvl53l0_exit); diff --git a/drivers/input/misc/vl53L1/Android.mk b/drivers/input/misc/vl53L1/Android.mk new file mode 100644 index 000000000000..318cf3dd9287 --- /dev/null +++ b/drivers/input/misc/vl53L1/Android.mk @@ -0,0 +1,17 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(PRODUCT_USE_TOF_vl53l3),true) + KERNEL_CFLAGS += CONFIG_INPUT_MISC_TOF_USE_VL53L3=y +endif + +ifeq ($(PRODUCT_USE_PMIC_WL2864C),true) + KERNEL_CFLAGS += CONFIG_INPUT_MISC_PMIC_WL2864C=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := stmvl53l1.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/misc/vl53L1/Kbuild b/drivers/input/misc/vl53L1/Kbuild new file mode 100644 index 000000000000..eedf64205a53 --- /dev/null +++ b/drivers/input/misc/vl53L1/Kbuild @@ -0,0 +1,42 @@ +# +# Kbuild for the vl53L1 drivers. +# + +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/vl53L1/inc \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/vl53L1/ipp \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/vl53L1 + +# define this environment variable if you want to compile driver for an old +# kernel +ifdef OLD_NETLINK_API +EXTRA_CFLAGS += -DOLD_NETLINK_API +endif + +ifdef VL53L1_LOG_ENABLE +EXTRA_CFLAGS += -DVL53L1_LOG_ENABLE +endif + +obj-m += stmvl53l1.o +stmvl53l1-objs := stmvl53l1_module.o stmvl53l1_module-i2c.o +stmvl53l1-objs += stmvl53l1_i2c.o stmvl53l1_ipp_nl.o stmvl53l1_log.o +stmvl53l1-objs += src/vl53l1_api.o src/vl53l1_api_core.o +stmvl53l1-objs += src/vl53l1_api_strings.o src/vl53l1_error_strings.o +stmvl53l1-objs += src/vl53l1_core.o src/vl53l1_register_funcs.o +stmvl53l1-objs += src/vl53l1_api_preset_modes.o +stmvl53l1-objs += src/vl53l1_api_calibration.o +stmvl53l1-objs += src/vl53l1_silicon_core.o +stmvl53l1-objs += src/vl53l1_zone_presets.o src/vl53l1_nvm.o +stmvl53l1-objs += src/vl53l1_api_debug.o src/vl53l1_core_support.o +stmvl53l1-objs += src/vl53l1_wait.o ipp/ipp_linux.o +stmvl53l1-objs += src/vl53l1_nvm_debug.o + +ifneq ($(filter m y,$(CONFIG_INPUT_MISC_TOF_USE_VL53L3)),) + EXTRA_CFLAGS += -DTOF_USE_VL53L3_EN +endif + + +ifneq ($(filter m y,$(CONFIG_INPUT_MISC_PMIC_WL2864C)),) + EXTRA_CFLAGS += -DTOF_USE_PMIC_WL2864C +endif \ No newline at end of file diff --git a/drivers/input/misc/vl53L1/Makefile b/drivers/input/misc/vl53L1/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/input/misc/vl53L1/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_api.h b/drivers/input/misc/vl53L1/inc/vl53l1_api.h new file mode 100644 index 000000000000..9440886cb870 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_api.h @@ -0,0 +1,1432 @@ + +/****************************************************************************** + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ******************************************************************************* + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + + ******************************************************************************* + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones mentioned above : + + ******************************************************************************* + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + + ******************************************************************************* + */ + +#ifndef _VL53L1_API_H_ +#define _VL53L1_API_H_ + +#include "vl53l1_api_strings.h" +#include "vl53l1_api_core.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if !defined(VL53L1DevDataGet) +#warning "PALDevDataGet is deprecated define VL53L1DevDataGet instead" +#define VL53L1DevDataGet(Dev, field) (Dev->Data.field) +#endif + +#if !defined(VL53L1DevDataSet) +#warning "PALDevDataSet is deprecated define VL53L1DevDataSet instead" +#define VL53L1DevDataSet(Dev, field, data) ((Dev->Data.field) = (data)) +#endif + +/** @defgroup VL53L1_cut11_group VL53L1 cut1.1 Function Definition + * @brief VL53L1 cut1.1 Function Definition + * @{ + */ + +/** @defgroup VL53L1_general_group VL53L1 General Functions + * @brief General functions and definitions + * @{ + */ + +/** + * @brief Return the VL53L1 driver Version + * + * @note This function doesn't access to the device + * + * @param pVersion Rer to current driver Version + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetVersion(VL53L1_Version_t *pVersion); + +/** + * @brief Reads the Product Revision for a for given Device + * This function can be used to distinguish cut1.0 from cut1.1. + * + * @param Dev Device Handle + * @param pProductRevisionMajor Pointer to Product Revision Major + * for a given Device + * @param pProductRevisionMinor Pointer to Product Revision Minor + * for a given Device + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetProductRevision(VL53L1_DEV Dev, + uint8_t *pProductRevisionMajor, uint8_t *pProductRevisionMinor); + +/** + * @brief Reads the Device information for given Device + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pVL53L1_DeviceInfo Pointer to current device info for a given + * Device + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetDeviceInfo(VL53L1_DEV Dev, + VL53L1_DeviceInfo_t *pVL53L1_DeviceInfo); + +/** + * @brief Human readable Range Status string for a given RangeStatus + * + * @note This function doesn't access to the device + * + * @param RangeStatus The RangeStatus code as stored on + * @a VL53L1_RangingMeasurementData_t + * @param pRangeStatusString The returned RangeStatus string. Shall be + * defined as char buf[VL53L1_MAX_STRING_LENGTH] + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetRangeStatusString(uint8_t RangeStatus, + char *pRangeStatusString); + +/** + * @brief Human readable error string for driver error status + * + * @note This function doesn't access to the device + * + * @param PalErrorCode The error code as stored on @a VL53L1_Error + * @param pPalErrorString The error string corresponding to the + * PalErrorCode. Shall be defined as char buf[VL53L1_MAX_STRING_LENGTH] + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetPalErrorString(VL53L1_Error PalErrorCode, + char *pPalErrorString); + +/** + * @brief Human readable driver State string + * + * @note This function doesn't access to the device + * + * @param PalStateCode The State code as stored on @a VL53L1_State + * @param pPalStateString The State string corresponding to the + * PalStateCode. Shall be defined as char buf[VL53L1_MAX_STRING_LENGTH] + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetPalStateString(VL53L1_State PalStateCode, + char *pPalStateString); + +/** + * @brief Reads the internal state of the driver for a given Device + * + * @note This function doesn't access to the device + * + * @param Dev Device Handle + * @param pPalState Pointer to current state of the PAL for a + * given Device + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetPalState(VL53L1_DEV Dev, + VL53L1_State *pPalState); + + + +/** @} VL53L1_general_group */ + +/** @defgroup VL53L1_init_group VL53L1 Init Functions + * @brief VL53L1 Init Functions + * @{ + */ + +/** + * @brief Set new device address + * + * After completion the device will answer to the new address programmed. + * This function should be called when several devices are used in parallel + * before start programming the sensor. + * When a single device us used, there is no need to call this function. + * + * When it is requested for multi devices system this function MUST be called + * prior to VL53L1_DataInit() + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param DeviceAddress The new Device address + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetDeviceAddress(VL53L1_DEV Dev, + uint8_t DeviceAddress); + +/** + * + * @brief One time device initialization + * + * To be called after device has been powered on and booted + * see @a VL53L1_WaitDeviceBooted() + * + * @par Function Description + * When not used after a fresh device "power up", it may return + * @a #VL53L1_ERROR_CALIBRATION_WARNING meaning wrong calibration data + * may have been fetched from device that can result in ranging offset error\n + * If VL53L1_DataInit is called several times then the application must restore + * calibration calling @a VL53L1_SetOffsetCalibrationData() + * It implies application has gathered calibration data thanks to + * @a VL53L1_GetOffsetCalibrationData() after an initial calibration stage. + * This function will change the VL53L1_State from VL53L1_STATE_POWERDOWN to + * VL53L1_STATE_WAIT_STATICINIT. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_DataInit(VL53L1_DEV Dev); + + +/** + * @brief Do basic device init (and eventually patch loading) + * This function will change the VL53L1_State from + * VL53L1_STATE_WAIT_STATICINIT to VL53L1_STATE_IDLE. + * In this stage all default setting will be applied. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_StaticInit(VL53L1_DEV Dev); + +/** + * @brief Wait for device booted after chip enable (hardware standby) + * This function can be run only when VL53L1_State is VL53L1_STATE_POWERDOWN. + * + * @param Dev Device Handle + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + * + */ +VL53L1_Error VL53L1_WaitDeviceBooted(VL53L1_DEV Dev); + + +/** @} VL53L1_init_group */ + +/** @defgroup VL53L1_parameters_group VL53L1 Parameters Functions + * @brief Functions used to prepare and setup the device + * @{ + */ + +/** + * @brief Set a new Preset Mode + * @par Function Description + * Set device to a new Operating Mode (High speed ranging, Multi objects ...) + * + * @note This function doesn't Access to the device + * + * @warning This function change the timing budget to 16 ms and the inter- + * measurement period to 1000 ms. Also the VL53L1_DISTANCEMODE_LONG is used. + * + * @param Dev Device Handle + * @param PresetMode New Preset mode to apply + *
Valid values are: + */ +/** + * @li VL53L1_PRESETMODE_MULTIZONES_SCANNING + * @li VL53L1_PRESETMODE_RANGING + * @li VL53L1_PRESETMODE_AUTONOMOUS + * @li VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS + * @li VL53L1_PRESETMODE_LITE_RANGING + * @li VL53L1_PRESETMODE_OLT + */ +/** + * + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_MODE_NOT_SUPPORTED This error occurs when PresetMode is + * not in the supported list + */ +VL53L1_Error VL53L1_SetPresetMode(VL53L1_DEV Dev, + VL53L1_PresetModes PresetMode); + +/** + * @brief Get current Preset Mode + * @par Function Description + * Get actual mode of the device(ranging, histogram ...) + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param pPresetMode Pointer to current apply mode value + * + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_MODE_NOT_SUPPORTED This error occurs when + * DeviceMode is not in the supported list + */ +VL53L1_Error VL53L1_GetPresetMode(VL53L1_DEV Dev, + VL53L1_PresetModes *pPresetMode); + + +/** + * @brief Set the distance mode + * @par Function Description + * Set the distance mode to be used for the next ranging.
+ * The modes Short, Medium and Long are used to optimize the ranging accuracy + * in a specific range of distance.
The user select one of these modes to + * select the distance range.
+ * Two additional modes are supported: AUTO and AUTO_LITE the difference between + * these modes is the following.
+ * The mode AUTO take into account both the ranging distance (RangeMilliMeter) + * and the dmax distance (DmaxMilliMeter).
The algorithm uses the ranging + * distance when the range status is ok and uses the dmax distance when the + * range status is not ok.
+ * The AUTO_LITE take into account only the ranging distance, so nothing is done + * in case of range error i.e. the distance mode will not be changed. + * @note This function doesn't Access to the device + * + * @warning This function should be called after @a VL53L1_SetPresetMode(). + + * @param Dev Device Handle + * @param DistanceMode Distance mode to apply valid values are: + * @li VL53L1_DISTANCEMODE_SHORT + * @li VL53L1_DISTANCEMODE_MEDIUM + * @li VL53L1_DISTANCEMODE_LONG + * @li VL53L1_DISTANCEMODE_AUTO_LITE + * @li VL53L1_DISTANCEMODE_AUTO + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_MODE_NOT_SUPPORTED This error occurs when DistanceMode + * is not in the supported list + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetDistanceMode(VL53L1_DEV Dev, + VL53L1_DistanceModes DistanceMode); + +/** + * @brief Get the distance mode + * @par Function Description + * Get the distance mode used for the next ranging. + * + * @param Dev Device Handle + * @param *pDistanceMode Pointer to Distance mode + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetDistanceMode(VL53L1_DEV Dev, + VL53L1_DistanceModes *pDistanceMode); + + +/** + * @brief Set the output mode + * @par Function Description + * Set the output mode to be used for the next ranging. The output mode is used + * to select, in case of multiple objects, which one will be used in + * function @a VL53L1_GetRangingMeasurementData(). + * VL53L1_SetOutputMode also sets the object used by automatic + * distance mode algorithm when @a VL53L1_SetDistanceMode() is + * set to automatic mode. + * + * @note This function doesn't Access to the device + * + * @warning This function should be called after @a VL53L1_SetPresetMode(). + + * @param Dev Device Handle + * @param OutputMode Output mode to apply valid values are: + * @li VL53L1_OUTPUTMODE_NEAREST + * @li VL53L1_OUTPUTMODE_STRONGEST + * + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_MODE_NOT_SUPPORTED This error occurs when OutputMode + * is not in the supported list + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetOutputMode(VL53L1_DEV Dev, + VL53L1_OutputModes OutputMode); + +/** + * @brief Get the output mode + * @par Function Description + * Get the output mode used for the next ranging. + * + * @param Dev Device Handle + * @param *pOutputMode Pointer to Output mode + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetOutputMode(VL53L1_DEV Dev, + VL53L1_OutputModes *pOutputMode); + + +/** + * @brief Set Ranging Timing Budget in microseconds + * + * @par Function Description + * Defines the maximum time allowed by the user to the device to run a + * full ranging sequence for the current mode (ranging, histogram, ASL ...) + * + * @param Dev Device Handle + * @param MeasurementTimingBudgetMicroSeconds Max measurement time in + * microseconds. + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_INVALID_PARAMS Error timing parameter not + * supported. + * The maximum accepted value for the + * computed timing budget is 10 seconds + * the minimum value depends on the preset + * mode selected. + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetMeasurementTimingBudgetMicroSeconds( + VL53L1_DEV Dev, uint32_t MeasurementTimingBudgetMicroSeconds); + +/** + * @brief Get Ranging Timing Budget in microseconds + * + * @par Function Description + * Returns the programmed the maximum time allowed by the user to the + * device to run a full ranging sequence for the current mode + * (ranging, histogram, ASL ...) + * + * @param Dev Device Handle + * @param pMeasurementTimingBudgetMicroSeconds Max measurement time in + * microseconds. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetMeasurementTimingBudgetMicroSeconds( + VL53L1_DEV Dev, uint32_t *pMeasurementTimingBudgetMicroSeconds); + + +/** + * Program continuous mode Inter-Measurement period in milliseconds + * + * @par Function Description + * When trying to set too short time return INVALID_PARAMS minimal value + * + * @param Dev Device Handle + * @param InterMeasurementPeriodMilliSeconds Inter-Measurement Period in ms. + * this value should be greater than the duration set in + * @a VL53L1_SetMeasurementTimingBudgetMicroSeconds() to ensure smooth ranging + * operation. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetInterMeasurementPeriodMilliSeconds( + VL53L1_DEV Dev, uint32_t InterMeasurementPeriodMilliSeconds); + +/** + * Get continuous mode Inter-Measurement period in milliseconds + * + * @par Function Description + * + * @param Dev Device Handle + * @param pInterMeasurementPeriodMilliSeconds Pointer to programmed + * Inter-Measurement Period in milliseconds. + * @return VL53L1_ERROR_NONE + */ +VL53L1_Error VL53L1_GetInterMeasurementPeriodMilliSeconds( + VL53L1_DEV Dev, uint32_t *pInterMeasurementPeriodMilliSeconds); + +/** + * @brief target reflectance for Dmax setting + * @par Function Description + * Allow user to set the value for target reflectance @ 940nm to calculate the + * ambient DMAX values for. Set to 50% by default by @a VL53L1_DataInit() + * + * @param Dev Device Handle + * @param DmaxReflectance Reflectance % in 16.16 fixed point + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_INVALID_PARAMS in case input value is not in range + * from 0 to 100. Note that this is a fix point value so the max value is + * 100 * 65536. + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetDmaxReflectance(VL53L1_DEV Dev, + FixPoint1616_t DmaxReflectance); + +/** + * @brief Get target reflectance for Dmax + * @par Function Description + * Retrieves the value for target reflectance @ 940nm to calculate the + * ambient DMAX values for. Set to 50% by default by @a VL53L1_DataInit() + * + * @param Dev Device Handle + * @param pDmaxReflectance pointer to Reflectance % in 16.16 fixed point + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetDmaxReflectance(VL53L1_DEV Dev, + FixPoint1616_t *pDmaxReflectance); +/** + * @brief Set function for ambient Dmax mode + * + * + * @param Dev Device Handle + * @param DmaxMode DMAX mode to be used in ranging + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + + +VL53L1_Error VL53L1_SetDmaxMode(VL53L1_DEV Dev, + VL53L1_DeviceDmaxModes DmaxMode); + +/** + * @brief Get function for ambient Dmax mode + * + * @param Dev Device Handle + * @param pDmaxMode output pointer to DMAX mode currently in use + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GetDmaxMode(VL53L1_DEV Dev, + VL53L1_DeviceDmaxModes *pDmaxMode); + +/** @} VL53L1_parameters_group */ + + +/** @defgroup VL53L1_limitcheck_group VL53L1 Limit Check Functions + * @brief Functions used for the Limit checks + * @{ + */ + + + +/** + * @brief Get the number of the check limit managed by a given Device + * + * @par Function Description + * This function give the number of the check limit managed by the Device + * + * @param pNumberOfLimitCheck Pointer to the number of check limit. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetNumberOfLimitCheck( + uint16_t *pNumberOfLimitCheck); + +/** + * @brief Return a description string for a given limit check number + * + * @par Function Description + * This function returns a description string for a given limit check number. + * The limit check is identified with the LimitCheckId. + * + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L1_GetNumberOfLimitCheck() ). + * @param pLimitCheckString Pointer to the description string of + * the given check limit. Shall be defined as char buf[VL53L1_MAX_STRING_LENGTH] + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetLimitCheckInfo(uint16_t LimitCheckId, + char *pLimitCheckString); + +/** + * @brief Return a the Status of the specified check limit + * + * @par Function Description + * This function returns the Status of the specified check limit. + * The value indicate if the check is fail or not. + * The limit check is identified with the LimitCheckId. + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + (0<= LimitCheckId < VL53L1_GetNumberOfLimitCheck() ). + * @param pLimitCheckStatus Pointer to the + Limit Check Status of the given check limit. + * LimitCheckStatus : + * 0 the check is not fail or not enabled + * 1 the check if fail + * + *

    + *
  • VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE: the sigma indicate the quality + * of the measure. The more it is little the better it is. + * The status is 1 when current sigma is greater then the limit.
  • + *
  • VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: the signal rate indicate + * the strength of the returned signal. The more it is big the better it is. + * The status is 1 when current signal is lower then the limit.
  • + *

+ * + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetLimitCheckStatus(VL53L1_DEV Dev, + uint16_t LimitCheckId, uint8_t *pLimitCheckStatus); + +/** + * @brief Enable/Disable a specific limit check + * + * @par Function Description + * This function Enable/Disable a specific limit check. + * The limit check is identified with the LimitCheckId. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L1_GetNumberOfLimitCheck() ). + * @param LimitCheckEnable + * @li set LimitCheckEnable=1 enables the LimitCheckId limit + * @li set LimitCheckEnable=0 disables the LimitCheckId limit + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_INVALID_PARAMS This error is returned + * when LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetLimitCheckEnable(VL53L1_DEV Dev, + uint16_t LimitCheckId, uint8_t LimitCheckEnable); + +/** + * @brief Get specific limit check enable state + * + * @par Function Description + * This function get the enable state of a specific limit check. + * The limit check is identified with the LimitCheckId. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L1_GetNumberOfLimitCheck() ). + * @param pLimitCheckEnable Pointer to the check limit enable + * value. + * @li if 1 the check limit corresponding to LimitCheckId is Enabled + * @li if 0 the check limit corresponding to LimitCheckId is disabled + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_INVALID_PARAMS This error is returned + * when LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetLimitCheckEnable(VL53L1_DEV Dev, + uint16_t LimitCheckId, uint8_t *pLimitCheckEnable); + +/** + * @brief Set a specific limit check value + * + * @par Function Description + * This function set a specific limit check value. + * The limit check is identified with the LimitCheckId. + * + * @note Note that the value written with that function will not be applied if + * the limit is not enabled. In other words this function will not enable the + * limit but change only the value. In case the limit is not enabled the value + * is saved internally and applied with VL53L1_SetLimitCheckEnable. + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L1_GetNumberOfLimitCheck() ). + * @param LimitCheckValue Limit check Value for a given + * LimitCheckId + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetLimitCheckValue(VL53L1_DEV Dev, + uint16_t LimitCheckId, FixPoint1616_t LimitCheckValue); + +/** + * @brief Get a specific limit check value + * + * @par Function Description + * This function get a specific limit check value from device then it updates + * internal values and check enables. + * The limit check is identified with the LimitCheckId. + * + * @note This function get the current value from device if zero then the value + * returned is the one stored by the user, but in that case the check is store + * as disabled. If the value from device is not zero, this is returned and set + * into the memory at the same way that user call VL53L1_SetLimitCheckValue() + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L1_GetNumberOfLimitCheck() ). + * @param pLimitCheckValue Pointer to Limit + * check Value for a given LimitCheckId. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetLimitCheckValue(VL53L1_DEV Dev, + uint16_t LimitCheckId, FixPoint1616_t *pLimitCheckValue); + +/** + * @brief Get the current value of the signal used for the limit check + * + * @par Function Description + * This function get a the current value of the signal used for the limit check. + * To obtain the latest value you should run a valid ranging before. + * The value reported is linked to the limit check identified with the + * LimitCheckId. + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L1_GetNumberOfLimitCheck() ). + * @param pLimitCheckCurrent Pointer to current Value for a + * given LimitCheckId. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetLimitCheckCurrent(VL53L1_DEV Dev, + uint16_t LimitCheckId, FixPoint1616_t *pLimitCheckCurrent); + +/** @} VL53L1_limitcheck_group */ + + + +/** @defgroup VL53L1_ROI_group VL53L1 ROI Functions + * @brief Functions used to select ROIs + * @{ + */ + +/** + * @brief Get the Maximum number of ROI Zones managed by the Device + * + * @par Function Description + * Get Maximum number of ROI Zones managed by the Device. + * + * @note The number of Zone depends on the preset mode used so to have the + * right number this function should be call after @a VL53L1_SetPresetMode() + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param pMaxNumberOfROI Pointer to the Maximum Number + * of ROI Zones value. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetMaxNumberOfROI(VL53L1_DEV Dev, + uint8_t *pMaxNumberOfROI); +/** + * @brief Set the ROI to be used for ranging + * + * @par Function Description + * The user defined ROIs are rectangles described as per the following system + * from the Top Left corner to the Bottom Right corner. + *
Minimal ROI size is 4x4 spads + * @image html roi_coord.png + * + * @param Dev Device Handle + * @param pRoiConfig Pointer to the Structure containing all the + * ROI to be used. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetROI(VL53L1_DEV Dev, + VL53L1_RoiConfig_t *pRoiConfig); + +/** + * @brief Get the ROI managed by the Device + * + * @par Function Description + * Get the ROI managed by the Device + * + * @param Dev Device Handle + * @param pRoiConfig Pointer to the Structure containing all the + * ROI to be used. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetROI(VL53L1_DEV Dev, + VL53L1_RoiConfig_t *pRoiConfig); + +/** @} VL53L1_ROI_group */ + +/* \internal */ +/** @defgroup VL53L1_sequencestep_group VL53L1 Sequence Step Functions + * @brief Functions used to select Steps done on each ranging + * @{ + */ + +/** + * @brief Gets number of sequence steps managed by the API. + * + * @par Function Description + * This function retrieves the number of sequence steps currently managed + * by the API + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param pNumberOfSequenceSteps Out parameter reporting the number of + * sequence steps. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetNumberOfSequenceSteps(VL53L1_DEV Dev, + uint8_t *pNumberOfSequenceSteps); + +/** + * @brief Gets the name of a given sequence step. + * + * @par Function Description + * This function retrieves the name of sequence steps corresponding to + * SequenceStepId. + * + * @note This function doesn't Accesses the device + * + * @param SequenceStepId Sequence step identifier. + * @param pSequenceStepsString Pointer to Info string. Shall be + * defined as char buf[VL53L1_MAX_STRING_LENGTH] + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetSequenceStepsInfo( + VL53L1_SequenceStepId SequenceStepId, char *pSequenceStepsString); + + + +/** + * @brief Sets the (on/off) state of a requested sequence step. + * + * @par Function Description + * This function enables/disables a requested sequence step. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param SequenceStepId Sequence step identifier. + * @param SequenceStepEnabled Demanded state {0=Off,1=On} + * is enabled. + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_INVALID_PARAMS Error SequenceStepId parameter not + * supported. + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetSequenceStepEnable(VL53L1_DEV Dev, + VL53L1_SequenceStepId SequenceStepId, uint8_t SequenceStepEnabled); + +/** + * @brief Gets the (on/off) state of a requested sequence step. + * + * @par Function Description + * This function retrieves the state of a requested sequence step, i.e. on/off. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param SequenceStepId Sequence step identifier. + * @param pSequenceStepEnabled Out parameter reporting if the sequence step + * is enabled {0=Off,1=On}. + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_INVALID_PARAMS Error SequenceStepId parameter not + * supported. + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetSequenceStepEnable(VL53L1_DEV Dev, + VL53L1_SequenceStepId SequenceStepId, uint8_t *pSequenceStepEnabled); + + +/** @} VL53L1_sequencestep_group */ +/* \endinternal */ + + + +/** @defgroup VL53L1_measurement_group VL53L1 Measurement Functions + * @brief Functions used for the measurements + * @{ + */ + +/** + * @brief Start device measurement + * + * @details Started measurement will depend on preset parameters set through + * @a VL53L1_SetPreseMode() + * This function will change the VL53L1_State from VL53L1_STATE_IDLE to + * VL53L1_STATE_RUNNING. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_MODE_NOT_SUPPORTED This error occurs when + * PresetMode programmed with @a VL53L1_SetPresetMode + * @return VL53L1_ERROR_TIME_OUT Time out on start measurement + * @return VL53L1_ERROR_INVALID_PARAMS This error might occur in timed mode + * when inter measurement period is smaller or too close to the timing budget. + * In such case measurements are not started and user must correct the timings + * passed to @a VL53L1_SetMeasurementTimingBudgetMicroSeconds() and + * @a VL53L1_SetInterMeasurementPeriodMilliSeconds() functions. + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_StartMeasurement(VL53L1_DEV Dev); + +/** + * @brief Stop device measurement + * + * @details Will set the device in standby mode at end of current measurement\n + * Not necessary in single mode as device shall return automatically + * in standby mode at end of measurement. + * This function will change the VL53L1_State from VL53L1_STATE_RUNNING + * to VL53L1_STATE_IDLE. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_StopMeasurement(VL53L1_DEV Dev); + +/** + * @brief Clear the Interrupt flag and start new measurement + * * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_ClearInterruptAndStartMeasurement(VL53L1_DEV Dev); + +/** + * @brief Return Measurement Data Ready + * + * @par Function Description + * This function indicate that a measurement data is ready. + * This function is used for non-blocking capture. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pMeasurementDataReady Pointer to Measurement Data Ready. + * 0 = data not ready, 1 = data ready + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetMeasurementDataReady(VL53L1_DEV Dev, + uint8_t *pMeasurementDataReady); + +/** + * @brief Wait for measurement data ready. + * Blocking function. + * Note that the timeout is given by: + * VL53L1_RANGE_COMPLETION_POLLING_TIMEOUT_MS defined in def.h + * + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_TIME_OUT In case of timeout + */ +VL53L1_Error VL53L1_WaitMeasurementDataReady(VL53L1_DEV Dev); + + +/** + * @brief Retrieve the measurements from device for a given setup + * + * @par Function Description + * Get data from last successful Ranging measurement + */ +/** + * @warning this function will return only the first ROI data and only the + * first object. For multi objects or multi ROI use: + * @a Vl53L1_GetMultiRangingData. + * In case of RANGING only one output is given, this can + * be selected with the help of @a VL53L1_SetOutputMode() + * In case of MULTIZONES_SCANNING and error will be raised because not + * supported in that function. + */ +/** + * + * @warning USER must call @a VL53L1_ClearInterruptAndStartMeasurement() prior + * to call again this function + * + * @note This function Access to the device + * + * @note The first valid value returned by this function will have a range + * status equal to VL53L1_RANGESTATUS_RANGE_VALID_NO_WRAP_CHECK which means that + * the data is valid but no wrap around check have been done. User should take + * care about that. + * + * @param Dev Device Handle + * @param pRangingMeasurementData Pointer to the data structure to fill up. + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_MODE_NOT_SUPPORTED in case of MULTIZONES_SCANNING + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetRangingMeasurementData(VL53L1_DEV Dev, + VL53L1_RangingMeasurementData_t *pRangingMeasurementData); + +/** + * @brief Retrieve all ROI's measurements from device for a given setup + * + * @par Function Description + * Get data from last successful Ranging measurement + * @warning USER should take care about @a VL53L1_GetNumberOfROI() + * before get data. + * Bare driver will fill a NumberOfROI times the corresponding data + * structure used in the measurement function. + * + * @warning USER must call @a VL53L1_ClearInterruptAndStartMeasurement() prior + * to call again this function + * + * @note This function Access to the device + * + * @note The first valid value returned by this function will have a range + * status equal to VL53L1_RANGESTATUS_RANGE_VALID_NO_WRAP_CHECK which means that + * the data is valid but no wrap around check have been done. User should take + * care about that. + * + * @param Dev Device Handle + * @param pMultiRangingData Pointer to the data structure to fill up. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetMultiRangingData(VL53L1_DEV Dev, + VL53L1_MultiRangingData_t *pMultiRangingData); + +/** + * @brief Get Additional Data + * + * @par Function Description + * This function is used to get lld debugging data on the last histogram + * measurement. shall be called when a new measurement is ready (interrupt or + * positive VL53L1_GetMeasurementDataReady() polling) and before a call to + * VL53L1_ClearInterruptAndStartMeasurement(). Depending on the PresetMode + * currently set parts of the returned data structure may be not relevant. + * + * @param Dev Device Handle + * @param pAdditionalData Pointer to Additional data + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetAdditionalData(VL53L1_DEV Dev, + VL53L1_AdditionalData_t *pAdditionalData); + + +/** @} VL53L1_measurement_group */ + +/** @defgroup VL53L1_Calibration_group VL53L1 Calibration Functions + * @brief Functions used for Calibration + * @{ + */ + + +/** + * @brief Set Tuning Parameter value for a given parameter ID + * + * @par Function Description + * This function is used to improve the performance of the device. It permit to + * change a particular value used for a timeout or a threshold or a constant + * in an algorithm. The function will change the value of the parameter + * identified by an unique ID. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param TuningParameterId Tuning Parameter ID + * @param TuningParameterValue Tuning Parameter Value + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetTuningParameter(VL53L1_DEV Dev, + uint16_t TuningParameterId, int32_t TuningParameterValue); + +/** + * @brief Get Tuning Parameter value for a given parameter ID + * + * @par Function Description + * This function is used to get the value of the parameter + * identified by an unique ID. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param TuningParameterId Tuning Parameter ID + * @param pTuningParameterValue Pointer to Tuning Parameter Value + * for a given TuningParameterId. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetTuningParameter(VL53L1_DEV Dev, + uint16_t TuningParameterId, int32_t *pTuningParameterValue); + +/** + * @brief Performs Reference Spad Management + * + * @par Function Description + * The reference SPAD initialization procedure determines the minimum amount + * of reference spads to be enables to achieve a target reference signal rate + * and should be performed once during initialization. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_PerformRefSpadManagement(VL53L1_DEV Dev); + +/** + * @brief Enable/Disable dynamic Xtalk compensation feature + * + * Enable/Disable dynamic Xtalk compensation (aka smudge correction). + * + * @param Dev Device Handle + * @param Mode Set the smudge correction mode + * See ::VL53L1_SmudgeCorrectionModes + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SmudgeCorrectionEnable(VL53L1_DEV Dev, + VL53L1_SmudgeCorrectionModes Mode); + +/** + * @brief Enable/Disable Cross talk compensation feature + * + * Enable/Disable Cross Talk correction. + * + * @param Dev Device Handle + * @param XTalkCompensationEnable Cross talk compensation + * to be set 0 = disabled or 1 = enabled. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetXTalkCompensationEnable(VL53L1_DEV Dev, +uint8_t XTalkCompensationEnable); + +/** + * @brief Get Cross talk compensation rate enable + * + * Get if the Cross Talk is Enabled or Disabled. + * + * @note This function doesn't access to the device + * + * @param Dev Device Handle + * @param pXTalkCompensationEnable Pointer to the Cross talk compensation + * state 0=disabled or 1 = enabled + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetXTalkCompensationEnable(VL53L1_DEV Dev, + uint8_t *pXTalkCompensationEnable); + + +/** + * @brief Perform XTalk Calibration + * + * @details Perform a XTalk calibration of the Device. + * This function will launch a ranging measurement, if interrupts + * are enabled an interrupt will be done. + * This function will clear the interrupt generated automatically. + * This function will program a new value for the XTalk compensation + * and it will enable the cross talk before exit. + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param CalibrationOption Select the Calibration to be run : + * @param CalibrationOption + * @li VL53L1_XTALKCALIBRATIONMODE_SINGLE_TARGET the calibration uses current + * preset and distance mode without altering them.
+ * User must call @a VL53L1_SetPresetMode() with VL53L1_PRESETMODE_AUTONOMOUS, + * VL53L1_PRESETMODE_LITE_RANGING or VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS + * parameter prior to launch calibration + * @li VL53L1_XTALKCALIBRATIONMODE_NO_TARGET the calibration sets appropriate + * preset and distance mode and thus override existing ones
+ * User must call @a VL53L1_SetPresetMode() again after calibration to set the + * desired one. during this calibration mode no object must be put below a 80cm + * distance from the target + * @li VL53L1_XTALKCALIBRATIONMODE_FULL_ROI the calibration sets appropriate + * preset and distance mode and thus override existing ones
+ * User must call @a VL53L1_SetPresetMode() again after calibration to set the + * desired one. + * The ROI settings must define a single 16x16 ROI before to launch this + * function. + * The calibration uses a target which should be located at least @60cm from the + * device. The actual location of the target shall be passed + * through the bare driver tuning parameters table + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_PerformXTalkCalibration(VL53L1_DEV Dev, + uint8_t CalibrationOption); + +/** + * @brief Define the mode to be used for the offset calibration + * + * Define the mode to be used for the offset calibration. This function should + * be called before run the @a VL53L1_PerformOffsetCalibration() + * + * @param Dev Device Handle + * @param OffsetCalibrationMode Offset Calibration Mode valid values are: + * @li VL53L1_OFFSETCALIBRATIONMODE_STANDARD + * @li VL53L1_OFFSETCALIBRATIONMODE_PRERANGE_ONLY + * @li VL53L1_OFFSETCALIBRATIONMODE_MULTI_ZONE + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetOffsetCalibrationMode(VL53L1_DEV Dev, + VL53L1_OffsetCalibrationModes OffsetCalibrationMode); + +/** + * @brief Define the mode to be used for the offset correction + * + * Define the mode to be used for the offset correction. + * + * @param Dev Device Handle + * @param OffsetCorrectionMode Offset Correction Mode valid values are: + * @li VL53L1_OFFSETCORRECTIONMODE_STANDARD + * @li VL53L1_OFFSETCORRECTIONMODE_PERZONE + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetOffsetCorrectionMode(VL53L1_DEV Dev, + VL53L1_OffsetCorrectionModes OffsetCorrectionMode); + + +/** + * @brief Perform Offset Calibration + * + * @details Perform a Offset calibration of the Device. + * This function will launch a ranging measurement, if interrupts are + * enabled interrupts will be done. + * This function will program a new value for the Offset calibration value + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param CalDistanceMilliMeter Calibration distance value used for the + * offset compensation. + * @param CalReflectancePercent Calibration Target reflectance @ 940nm + * in percentage. + * + * @return VL53L1_ERROR_NONE + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_PerformOffsetCalibration(VL53L1_DEV Dev, + int32_t CalDistanceMilliMeter, + FixPoint1616_t CalReflectancePercent); + +/** + * @brief Perform Offset simple Calibration + * + * @details Perform a very simple offset calibration of the Device. + * This function will launch few ranging measurements and computes offset + * calibration. The preset mode and the distance mode MUST be set by the + * application before to call this function. + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param CalDistanceMilliMeter Calibration distance value used for the + * offset compensation. + * + * @return VL53L1_ERROR_NONE + * @return VL53L1_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL the calibration failed by + * lack of valid measurements + * @return VL53L1_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH means that the target + * distance combined to the number of loops performed in the calibration lead to + * an internal overflow. Try to reduce the distance of the target (140 mm) + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_PerformOffsetSimpleCalibration(VL53L1_DEV Dev, + int32_t CalDistanceMilliMeter); + +/** + * @brief Sets the Calibration Data. + * + * @par Function Description + * This function set all the Calibration Data issued from the functions + * @a VL53L1_PerformRefSpadManagement(), @a VL53L1_PerformXTalkCalibration, + * @a VL53L1_PerformOffsetCalibration() + * + * @note This function doesn't Accesses the device + * + * @param Dev Device Handle + * @param *pCalibrationData Pointer to Calibration data to be set. + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_INVALID_PARAMS pCalibrationData points to an older + * version of the inner structure. Need for support to convert its content. + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetCalibrationData(VL53L1_DEV Dev, + VL53L1_CalibrationData_t *pCalibrationData); + +/** + * @brief Gets the Calibration Data. + * + * @par Function Description + * This function get all the Calibration Data issued from the functions + * @a VL53L1_PerformRefSpadManagement(), @a VL53L1_PerformXTalkCalibration, + * @a VL53L1_PerformOffsetCalibration() + * + * @note This function doesn't Accesses the device + * + * @param Dev Device Handle + * @param *pCalibrationData pointer where to store Calibration + * data. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetCalibrationData(VL53L1_DEV Dev, + VL53L1_CalibrationData_t *pCalibrationData); + +/** + * @brief Sets the Zone Calibration Data. + * + * @par Function Description + * This function set all the Zone nCalibration Data issued from the functions + * @a VL53L1_PerformOffsetCalibration() in multi zone + * + * @note This function doesn't Accesses the device + * + * @param Dev Device Handle + * @param *pZoneCalibrationData Pointer to Zone Calibration data to be + * set. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetZoneCalibrationData(VL53L1_DEV Dev, + VL53L1_ZoneCalibrationData_t *pZoneCalibrationData); + +/** + * @brief Gets the Zone Calibration Data. + * + * @par Function Description + * This function get all the Zone Calibration Data issued from the functions + * @a VL53L1_PerformOffsetCalibration() + * + * @note This function doesn't Accesses the device + * + * @param Dev Device Handle + * @param *pZoneCalibrationData pointer where to store Zone Calibration + * data. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetZoneCalibrationData(VL53L1_DEV Dev, + VL53L1_ZoneCalibrationData_t *pZoneCalibrationData); +/** + * @brief Gets the optical center. + * + * @par Function Description + * This function get the optical center issued from the nvm set at FTM stage + * expressed in the same coordinate system as the ROI are + * + * @note This function doesn't Accesses the device + * + * @param Dev Device Handle + * @param pOpticalCenterX pointer to the X position of center + * in 16.16 fix point + * @param pOpticalCenterY pointer to the Y position of center + * in 16.16 fix point + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetOpticalCenter(VL53L1_DEV Dev, + FixPoint1616_t *pOpticalCenterX, + FixPoint1616_t *pOpticalCenterY); + +/** @} VL53L1_Calibration_group */ + +/** @defgroup VL53L1_Thresholds_group VL53L1 IRQ Triggered events Functions + * @brief Functions used to configure interrupt to be triggered only when + * a measurement satisfies some thresholds parameters + * @{ + */ + +/** + * @brief Configure the interrupt config, from the given structure + * + * @param[in] Dev : Device Handle + * @param[in] pConfig : pointer to configuration structure + */ + +VL53L1_Error VL53L1_SetThresholdConfig(VL53L1_DEV Dev, + VL53L1_DetectionConfig_t *pConfig); + +/** + * @brief Retrieves the interrupt config structure currently programmed + * into the API + * + * @param[in] Dev : Device Handle + * @param[out] pConfig : pointer to configuration structure + */ + +VL53L1_Error VL53L1_GetThresholdConfig(VL53L1_DEV Dev, + VL53L1_DetectionConfig_t *pConfig); + + +/** @} VL53L1_Thresholds_group */ + + +/** @} VL53L1_cut11_group */ + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L1_API_H_ */ diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_api_calibration.h b/drivers/input/misc/vl53L1/inc/vl53l1_api_calibration.h new file mode 100644 index 000000000000..01c9aeb2b8a2 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_api_calibration.h @@ -0,0 +1,596 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_API_CALIBRATION_H_ +#define _VL53L1_API_CALIBRATION_H_ + +#include "vl53l1_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_run_ref_spad_char(VL53L1_DEV Dev, + VL53L1_Error * pcal_status); + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_run_device_test( + VL53L1_DEV Dev, + VL53L1_DeviceTestMode device_test_mode); + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_run_spad_rate_map( + VL53L1_DEV Dev, + VL53L1_DeviceTestMode device_test_mode, + VL53L1_DeviceSscArray array_select, + uint32_t ssc_config_timeout_us, + VL53L1_spad_rate_data_t *pspad_rate_data); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_run_xtalk_extraction( + VL53L1_DEV Dev, + VL53L1_Error *pcal_status); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_and_avg_xtalk_samples( + VL53L1_DEV Dev, + uint8_t num_of_samples, + uint8_t measurement_mode, + int16_t xtalk_filter_thresh_max_mm, + int16_t xtalk_filter_thresh_min_mm, + uint16_t xtalk_max_valid_rate_kcps, + uint8_t xtalk_result_id, + uint8_t xtalk_histo_id, + VL53L1_xtalk_range_results_t *pxtalk_results, + VL53L1_histogram_bin_data_t *psum_histo, + VL53L1_histogram_bin_data_t *pavg_histo); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_run_offset_calibration( + VL53L1_DEV Dev, + int16_t cal_distance_mm, + uint16_t cal_reflectance_pc, + VL53L1_Error *pcal_status); + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_run_phasecal_average( + VL53L1_DEV Dev, + uint8_t measurement_mode, + uint8_t phasecal_result__vcsel_start, + uint16_t phasecal_num_of_samples, + VL53L1_range_results_t *prange_results, + uint16_t *pphasecal_result__reference_phase, + uint16_t *pzero_distance_phase); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_run_zone_calibration( + VL53L1_DEV Dev, + VL53L1_DevicePresetModes device_preset_mode, + VL53L1_DeviceZonePreset zone_preset, + VL53L1_zone_config_t *pzone_cfg, + int16_t cal_distance_mm, + uint16_t cal_reflectance_pc, + VL53L1_Error *pcal_status); + + + + + + + + + + + + +void VL53L1_hist_xtalk_extract_data_init( + VL53L1_hist_xtalk_extract_data_t *pxtalk_data); + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_hist_xtalk_extract_update( + int16_t target_distance_mm, + uint16_t target_width_oversize, + VL53L1_histogram_bin_data_t *phist_bins, + VL53L1_hist_xtalk_extract_data_t *pxtalk_data); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_hist_xtalk_extract_fini( + VL53L1_histogram_bin_data_t *phist_bins, + VL53L1_hist_xtalk_extract_data_t *pxtalk_data, + VL53L1_xtalk_calibration_results_t *pxtalk_cal, + VL53L1_xtalk_histogram_shape_t *pxtalk_shape); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_run_hist_xtalk_extraction( + VL53L1_DEV Dev, + int16_t cal_distance_mm, + VL53L1_Error *pcal_status); + + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_api_core.h b/drivers/input/misc/vl53L1/inc/vl53l1_api_core.h new file mode 100644 index 000000000000..eb3122cb0516 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_api_core.h @@ -0,0 +1,1941 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_API_CORE_H_ +#define _VL53L1_API_CORE_H_ + +#include "vl53l1_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + + + + + + + + + +VL53L1_Error VL53L1_get_version( + VL53L1_DEV Dev, + VL53L1_ll_version_t *pversion); + + + + + + + + + + + + +VL53L1_Error VL53L1_get_device_firmware_version( + VL53L1_DEV Dev, + uint16_t *pfw_version); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_data_init( + VL53L1_DEV Dev, + uint8_t read_p2p_data); + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_read_p2p_data( + VL53L1_DEV Dev); + + + + + + + + + + + + + +VL53L1_Error VL53L1_software_reset( + VL53L1_DEV Dev); + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_part_to_part_data( + VL53L1_DEV Dev, + VL53L1_calibration_data_t *pcal_data); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_part_to_part_data( + VL53L1_DEV Dev, + VL53L1_calibration_data_t *pcal_data); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_tuning_debug_data( + VL53L1_DEV Dev, + VL53L1_tuning_parameters_t *ptun_data); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_inter_measurement_period_ms( + VL53L1_DEV Dev, + uint32_t inter_measurement_period_ms); + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_inter_measurement_period_ms( + VL53L1_DEV Dev, + uint32_t *pinter_measurement_period_ms); + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_timeouts_us( + VL53L1_DEV Dev, + uint32_t phasecal_config_timeout_us, + uint32_t mm_config_timeout_us, + uint32_t range_config_timeout_us); + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_timeouts_us( + VL53L1_DEV Dev, + uint32_t *pphasecal_config_timeout_us, + uint32_t *pmm_config_timeout_us, + uint32_t *prange_config_timeout_us); + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_calibration_repeat_period( + VL53L1_DEV Dev, + uint16_t cal_config__repeat_period); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_calibration_repeat_period( + VL53L1_DEV Dev, + uint16_t *pcal_config__repeat_period); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_sequence_config_bit( + VL53L1_DEV Dev, + VL53L1_DeviceSequenceConfig bit_id, + uint8_t value); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_sequence_config_bit( + VL53L1_DEV Dev, + VL53L1_DeviceSequenceConfig bit_id, + uint8_t *pvalue); + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_interrupt_polarity( + VL53L1_DEV Dev, + VL53L1_DeviceInterruptPolarity interrupt_polarity); + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_interrupt_polarity( + VL53L1_DEV Dev, + VL53L1_DeviceInterruptPolarity *pinterrupt_polarity); + + + + + + + + + + + + +VL53L1_Error VL53L1_get_refspadchar_config_struct( + VL53L1_DEV Dev, + VL53L1_refspadchar_config_t *pdata); + + + + + + + + + + + + +VL53L1_Error VL53L1_set_refspadchar_config_struct( + VL53L1_DEV Dev, + VL53L1_refspadchar_config_t *pdata); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_range_ignore_threshold( + VL53L1_DEV Dev, + uint8_t range_ignore_thresh_mult, + uint16_t range_ignore_threshold_mcps); + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_range_ignore_threshold( + VL53L1_DEV Dev, + uint8_t *prange_ignore_thresh_mult, + uint16_t *prange_ignore_threshold_mcps_internal, + uint16_t *prange_ignore_threshold_mcps_current); + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_user_zone( + VL53L1_DEV Dev, + VL53L1_user_zone_t *puser_zone); + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_user_zone( + VL53L1_DEV Dev, + VL53L1_user_zone_t *puser_zone); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_mode_mitigation_roi( + VL53L1_DEV Dev, + VL53L1_user_zone_t *pmm_roi); + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_zone_config( + VL53L1_DEV Dev, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_zone_config( + VL53L1_DEV Dev, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_preset_mode( + VL53L1_DEV Dev, + VL53L1_DevicePresetModes device_preset_mode, + uint16_t dss_config__target_total_rate_mcps, + uint32_t phasecal_config_timeout_us, + uint32_t mm_config_timeout_us, + uint32_t range_config_timeout_us, + uint32_t inter_measurement_period_ms); + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_preset_mode_timing_cfg( + VL53L1_DEV Dev, + VL53L1_DevicePresetModes device_preset_mode, + uint16_t *pdss_config__target_total_rate_mcps, + uint32_t *pphasecal_config_timeout_us, + uint32_t *pmm_config_timeout_us, + uint32_t *prange_config_timeout_us); + + + + + + + + + + + + +VL53L1_Error VL53L1_set_zone_preset( + VL53L1_DEV Dev, + VL53L1_DeviceZonePreset zone_preset); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_enable_xtalk_compensation( + VL53L1_DEV Dev); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_disable_xtalk_compensation( + VL53L1_DEV Dev); + + + + + + + + + + + + + + +void VL53L1_get_xtalk_compensation_enable( + VL53L1_DEV Dev, + uint8_t *pcrosstalk_compensation_enable); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_init_and_start_range( + VL53L1_DEV Dev, + uint8_t measurement_mode, + VL53L1_DeviceConfigLevel device_config_level); + + + + + + + + + + + + + +VL53L1_Error VL53L1_stop_range( + VL53L1_DEV Dev); + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_measurement_results( + VL53L1_DEV Dev, + VL53L1_DeviceResultsLevel device_result_level); + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_device_results( + VL53L1_DEV Dev, + VL53L1_DeviceResultsLevel device_result_level, + VL53L1_range_results_t *prange_results); + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_clear_interrupt_and_enable_next_range( + VL53L1_DEV Dev, + uint8_t measurement_mode); + + + + + + + + + + +VL53L1_Error VL53L1_get_histogram_bin_data( + VL53L1_DEV Dev, + VL53L1_histogram_bin_data_t *phist_data); + + + + + + + + + + + + +void VL53L1_copy_sys_and_core_results_to_range_results( + int32_t gain_factor, + VL53L1_system_results_t *psys, + VL53L1_core_results_t *pcore, + VL53L1_range_results_t *presults); + + + + + + + + + + +VL53L1_Error VL53L1_set_zone_dss_config( + VL53L1_DEV Dev, + VL53L1_zone_private_dyn_cfg_t *pzone_dyn_cfg); + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_calc_ambient_dmax( + VL53L1_DEV Dev, + uint16_t target_reflectance, + int16_t *pambient_dmax_mm); + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_GPIO_interrupt_config( + VL53L1_DEV Dev, + VL53L1_GPIO_Interrupt_Mode intr_mode_distance, + VL53L1_GPIO_Interrupt_Mode intr_mode_rate, + uint8_t intr_new_measure_ready, + uint8_t intr_no_target, + uint8_t intr_combined_mode, + uint16_t thresh_distance_high, + uint16_t thresh_distance_low, + uint16_t thresh_rate_high, + uint16_t thresh_rate_low + ); + + + + + + + + + +VL53L1_Error VL53L1_set_GPIO_interrupt_config_struct( + VL53L1_DEV Dev, + VL53L1_GPIO_interrupt_config_t intconf); + + + + + + + + + + +VL53L1_Error VL53L1_get_GPIO_interrupt_config( + VL53L1_DEV Dev, + VL53L1_GPIO_interrupt_config_t *pintconf); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_dmax_mode( + VL53L1_DEV Dev, + VL53L1_DeviceDmaxMode dmax_mode); + + + + + + + + + + + + +VL53L1_Error VL53L1_get_dmax_mode( + VL53L1_DEV Dev, + VL53L1_DeviceDmaxMode *pdmax_mode); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_dmax_calibration_data( + VL53L1_DEV Dev, + VL53L1_DeviceDmaxMode dmax_mode, + uint8_t zone_id, + VL53L1_dmax_calibration_data_t *pdmax_cal); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_hist_dmax_config( + VL53L1_DEV Dev, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg); + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_hist_dmax_config( + VL53L1_DEV Dev, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg); + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_offset_calibration_mode( + VL53L1_DEV Dev, + VL53L1_OffsetCalibrationMode offset_cal_mode); + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_offset_calibration_mode( + VL53L1_DEV Dev, + VL53L1_OffsetCalibrationMode *poffset_cal_mode); + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_offset_correction_mode( + VL53L1_DEV Dev, + VL53L1_OffsetCalibrationMode offset_cor_mode); + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_offset_correction_mode( + VL53L1_DEV Dev, + VL53L1_OffsetCorrectionMode *poffset_cor_mode); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_zone_calibration_data( + VL53L1_DEV Dev, + VL53L1_zone_calibration_results_t *pzone_cal); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_zone_calibration_data( + VL53L1_DEV Dev, + VL53L1_zone_calibration_results_t *pzone_cal); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_lite_xtalk_margin_kcps( + VL53L1_DEV Dev, + int16_t *pxtalk_margin); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_lite_xtalk_margin_kcps( + VL53L1_DEV Dev, + int16_t xtalk_margin); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_histogram_xtalk_margin_kcps( + VL53L1_DEV Dev, + int16_t *pxtalk_margin); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_histogram_xtalk_margin_kcps( + VL53L1_DEV Dev, + int16_t xtalk_margin); + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_histogram_phase_consistency( + VL53L1_DEV Dev, + uint8_t *pphase_consistency); + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_histogram_phase_consistency( + VL53L1_DEV Dev, + uint8_t phase_consistency); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_histogram_event_consistency( + VL53L1_DEV Dev, + uint8_t *pevent_consistency); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_histogram_event_consistency( + VL53L1_DEV Dev, + uint8_t event_consistency); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_histogram_ambient_threshold_sigma( + VL53L1_DEV Dev, + uint8_t *pamb_thresh_sigma); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_histogram_ambient_threshold_sigma( + VL53L1_DEV Dev, + uint8_t amb_thresh_sigma); + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_lite_min_count_rate( + VL53L1_DEV Dev, + uint16_t *plite_mincountrate); + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_lite_min_count_rate( + VL53L1_DEV Dev, + uint16_t lite_mincountrate); + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_lite_sigma_threshold( + VL53L1_DEV Dev, + uint16_t *plite_sigma); + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_lite_sigma_threshold( + VL53L1_DEV Dev, + uint16_t lite_sigma); + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_restore_xtalk_nvm_default( + VL53L1_DEV Dev); + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_xtalk_detect_config( + VL53L1_DEV Dev, + int16_t *pmax_valid_range_mm, + int16_t *pmin_valid_range_mm, + uint16_t *pmax_valid_rate_kcps, + uint16_t *pmax_sigma_mm); + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_xtalk_detect_config( + VL53L1_DEV Dev, + int16_t max_valid_range_mm, + int16_t min_valid_range_mm, + uint16_t max_valid_rate_kcps, + uint16_t max_sigma_mm); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_target_order_mode( + VL53L1_DEV Dev, + VL53L1_HistTargetOrder *phist_target_order); + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_target_order_mode( + VL53L1_DEV Dev, + VL53L1_HistTargetOrder hist_target_order); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_dmax_reflectance_values( + VL53L1_DEV Dev, + VL53L1_dmax_reflectance_array_t *pdmax_reflectances); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_dmax_reflectance_values( + VL53L1_DEV Dev, + VL53L1_dmax_reflectance_array_t *pdmax_reflectances); + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_vhv_config( + VL53L1_DEV Dev, + uint8_t vhv_init_en, + uint8_t vhv_init_value); + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_vhv_config( + VL53L1_DEV Dev, + uint8_t *pvhv_init_en, + uint8_t *pvhv_init_value); + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_vhv_loopbound( + VL53L1_DEV Dev, + uint8_t vhv_loopbound); + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_vhv_loopbound( + VL53L1_DEV Dev, + uint8_t *pvhv_loopbound); + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_tuning_parm( + VL53L1_DEV Dev, + VL53L1_TuningParms tuning_parm_key, + int32_t *ptuning_parm_value); + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_tuning_parm( + VL53L1_DEV Dev, + VL53L1_TuningParms tuning_parm_key, + int32_t tuning_parm_value); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_enable( + VL53L1_DEV Dev + ); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_disable( + VL53L1_DEV Dev + ); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_apply_enable( + VL53L1_DEV Dev + ); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_apply_disable( + VL53L1_DEV Dev + ); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_single_apply_enable( + VL53L1_DEV Dev + ); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_single_apply_disable( + VL53L1_DEV Dev + ); + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_set_scalers( + VL53L1_DEV Dev, + int16_t x_scaler_in, + int16_t y_scaler_in, + uint8_t user_scaler_set_in + ); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_current_xtalk_settings( + VL53L1_DEV Dev, + VL53L1_xtalk_calibration_results_t *pxtalk + ); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_current_xtalk_settings( + VL53L1_DEV Dev, + VL53L1_xtalk_calibration_results_t *pxtalk + ); + + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_api_debug.h b/drivers/input/misc/vl53L1/inc/vl53l1_api_debug.h new file mode 100644 index 000000000000..46239a784864 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_api_debug.h @@ -0,0 +1,756 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_API_DEBUG_H_ +#define _VL53L1_API_DEBUG_H_ + +#include "vl53l1_platform.h" +#include "vl53l1_nvm_structs.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_decode_calibration_data_buffer( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_calibration_data_t *pdata); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_nvm_debug_data( + VL53L1_DEV Dev, + VL53L1_decoded_nvm_data_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_histogram_debug_data( + VL53L1_DEV Dev, + VL53L1_histogram_bin_data_t *pdata); + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_additional_data( + VL53L1_DEV Dev, + VL53L1_additional_data_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_xtalk_debug_data( + VL53L1_DEV Dev, + VL53L1_xtalk_debug_data_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_offset_debug_data( + VL53L1_DEV Dev, + VL53L1_offset_debug_data_t *pdata); + +#ifdef VL53L1_LOG_ENABLE + + + + + + + + + + + + +void VL53L1_signed_fixed_point_sprintf( + int32_t fp_value, + uint8_t frac_bits, + uint16_t buf_size, + char *pbuffer); + + + + + + + + + + + + +void VL53L1_print_static_nvm_managed( + VL53L1_static_nvm_managed_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + +void VL53L1_print_customer_nvm_managed( + VL53L1_customer_nvm_managed_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + +void VL53L1_print_nvm_copy_data( + VL53L1_nvm_copy_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + +void VL53L1_print_histogram_bin_data( + VL53L1_histogram_bin_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + +void VL53L1_print_xtalk_histogram_data( + VL53L1_xtalk_histogram_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + +void VL53L1_print_xtalk_histogram_shape_data( + VL53L1_xtalk_histogram_shape_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + +void VL53L1_print_range_results( + VL53L1_range_results_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + +void VL53L1_print_range_data( + VL53L1_range_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + + +void VL53L1_print_offset_range_results( + VL53L1_offset_range_results_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + +void VL53L1_print_offset_range_data( + VL53L1_offset_range_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + +void VL53L1_print_cal_peak_rate_map( + VL53L1_cal_peak_rate_map_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + + +void VL53L1_print_additional_offset_cal_data( + VL53L1_additional_offset_cal_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + +void VL53L1_print_additional_data( + VL53L1_additional_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + + +void VL53L1_print_gain_calibration_data( + VL53L1_gain_calibration_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + + +void VL53L1_print_zone_calibration_data( + VL53L1_zone_calibration_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + + +void VL53L1_print_zone_calibration_results( + VL53L1_zone_calibration_results_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + +void VL53L1_print_xtalk_range_results( + VL53L1_xtalk_range_results_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + +void VL53L1_print_xtalk_range_data( + VL53L1_xtalk_range_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + + +void VL53L1_print_xtalk_calibration_results( + VL53L1_xtalk_calibration_results_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + +void VL53L1_print_xtalk_config( + VL53L1_xtalk_config_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + +void VL53L1_print_xtalk_extract_config( + VL53L1_xtalkextract_config_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + +void VL53L1_print_zone_cal_config( + VL53L1_zonecal_config_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + +void VL53L1_print_offset_cal_config( + VL53L1_offsetcal_config_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + +void VL53L1_print_dmax_calibration_data( + VL53L1_dmax_calibration_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + +void VL53L1_print_calibration_data( + VL53L1_calibration_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + +void VL53L1_print_xtalk_debug_data( + VL53L1_xtalk_debug_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + +void VL53L1_print_offset_debug_data( + VL53L1_offset_debug_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + +void VL53L1_print_optical_centre( + VL53L1_optical_centre_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + +void VL53L1_print_user_zone( + VL53L1_user_zone_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + +void VL53L1_print_zone_config( + VL53L1_zone_config_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + +void VL53L1_print_spad_rate_data( + VL53L1_spad_rate_data_t *pspad_rates, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + +void VL53L1_print_spad_rate_map( + VL53L1_spad_rate_data_t *pspad_rates, + char *pprefix, + uint32_t trace_flags); + + +#endif + + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_api_preset_modes.h b/drivers/input/misc/vl53L1/inc/vl53l1_api_preset_modes.h new file mode 100644 index 000000000000..e5e166aa2214 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_api_preset_modes.h @@ -0,0 +1,1637 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_API_PRESET_MODES_H_ +#define _VL53L1_API_PRESET_MODES_H_ + +#include "vl53l1_ll_def.h" +#include "vl53l1_dmax_structs.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + + + + + + + + + + +VL53L1_Error VL53L1_init_refspadchar_config_struct( + VL53L1_refspadchar_config_t *pdata); + + + + + + + + + + + + +VL53L1_Error VL53L1_init_ssc_config_struct( + VL53L1_ssc_config_t *pdata); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_init_xtalk_config_struct( + VL53L1_customer_nvm_managed_t *pnvm, + VL53L1_xtalk_config_t *pdata); + + + + + + + + + + + + +VL53L1_Error VL53L1_init_xtalk_extract_config_struct( + VL53L1_xtalkextract_config_t *pdata); + + + + + + + + + + + +VL53L1_Error VL53L1_init_offset_cal_config_struct( + VL53L1_offsetcal_config_t *pdata); + + + + + + + + + + + +VL53L1_Error VL53L1_init_zone_cal_config_struct( + VL53L1_zonecal_config_t *pdata); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_init_hist_post_process_config_struct( + uint8_t xtalk_compensation_enable, + VL53L1_hist_post_process_config_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_init_dmax_calibration_data_struct( + VL53L1_dmax_calibration_data_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_init_tuning_parm_storage_struct( + VL53L1_tuning_parm_storage_t *pdata); + + + + + + + + + + + + +VL53L1_Error VL53L1_init_hist_gen3_dmax_config_struct( + VL53L1_hist_gen3_dmax_config_t *pdata); + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_standard_ranging( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_standard_ranging_short_range( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_standard_ranging_long_range( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_standard_ranging_mm1_cal( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_standard_ranging_mm2_cal( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_timed_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_timed_ranging_short_range( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_timed_ranging_long_range( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_low_power_auto_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg, + VL53L1_low_power_auto_data_t *plpadata); + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_low_power_auto_short_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg, + VL53L1_low_power_auto_data_t *plpadata); + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_low_power_auto_long_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg, + VL53L1_low_power_auto_data_t *plpadata); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_with_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_with_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_mm1_cal( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_mm2_cal( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_ref( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_characterisation( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_xtalk_planar( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_xtalk_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_xtalk_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_multizone( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_multizone_short_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_multizone_long_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_short_timing( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_long_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_medium_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_short_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_special_histogram_short_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_long_range_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_long_range_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_medium_range_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_medium_range_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_short_range_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_histogram_short_range_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_olt( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_preset_mode_singleshot_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + + + + + +void VL53L1_copy_hist_cfg_to_static_cfg( + VL53L1_histogram_config_t *phistogram, + VL53L1_static_config_t *pstatic, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic); + + + + + + + + + + + + + + +void VL53L1_copy_hist_bins_to_static_cfg( + VL53L1_histogram_config_t *phistogram, + VL53L1_static_config_t *pstatic, + VL53L1_timing_config_t *ptiming); + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_api_strings.h b/drivers/input/misc/vl53L1/inc/vl53l1_api_strings.h new file mode 100644 index 000000000000..051fcb2b000c --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_api_strings.h @@ -0,0 +1,210 @@ + +/****************************************************************************** + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ******************************************************************************* + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + + ******************************************************************************* + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones mentioned above : + + ******************************************************************************* + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + + ******************************************************************************* + */ + +/** + * @file vl53l1_api_strings.h + * @brief VL53L1 API function declarations for decoding error codes to a + * text strings + */ + + +#ifndef VL53L1_API_STRINGS_H_ +#define VL53L1_API_STRINGS_H_ + +#include "vl53l1_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +/** + * @brief Generates a string for the input device range status code + * + * @param[in] RangeStatus : Device Range AStatus Code + * @param[out] pRangeStatusString : pointer to character buffer + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_get_range_status_string( + uint8_t RangeStatus, + char *pRangeStatusString); + +/** + * @brief Generates an error string for the input PAL error code + * + * @param[in] PalErrorCode : PAL Error Code + * @param[out] pPalErrorString : pointer to character buffer + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_get_pal_error_string( + VL53L1_Error PalErrorCode, + char *pPalErrorString); + +/** + * @brief Generates a string for the input PAL State code + * + * @param[in] PalStateCode : PAL State Code + * @param[out] pPalStateString : pointer to character buffer + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_get_pal_state_string( + VL53L1_State PalStateCode, + char *pPalStateString); + + +/** + * @brief Generates a string for the sequence step Id + * + * @param[in] SequenceStepId : Sequence Step Id + * @param[out] pSequenceStepsString : pointer to character buffer + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_get_sequence_steps_info( + VL53L1_SequenceStepId SequenceStepId, + char *pSequenceStepsString); + +/** + * @brief Generates a string for the limit check Id + * + * @param[in] LimitCheckId : Limit check Id + * @param[out] pLimitCheckString : pointer to character buffer + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_get_limit_check_info(uint16_t LimitCheckId, + char *pLimitCheckString); + +#ifndef VL53L1_USE_EMPTY_STRING + #define VL53L1_STRING_DEVICE_INFO_NAME0 "VL53L1 cut1.0" + #define VL53L1_STRING_DEVICE_INFO_NAME1 "VL53L1 cut1.1" + #define VL53L1_STRING_DEVICE_INFO_TYPE "VL53L1" + + /* Range Status */ + #define VL53L1_STRING_RANGESTATUS_NONE "No Update" + #define VL53L1_STRING_RANGESTATUS_RANGEVALID "Range Valid" + #define VL53L1_STRING_RANGESTATUS_SIGMA "Sigma Fail" + #define VL53L1_STRING_RANGESTATUS_SIGNAL "Signal Fail" + #define VL53L1_STRING_RANGESTATUS_MINRANGE "Min Range Fail" + #define VL53L1_STRING_RANGESTATUS_PHASE "Phase Fail" + #define VL53L1_STRING_RANGESTATUS_HW "Hardware Fail" + + + /* Range Status */ + #define VL53L1_STRING_STATE_POWERDOWN "POWERDOWN State" + #define VL53L1_STRING_STATE_WAIT_STATICINIT \ + "Wait for staticinit State" + #define VL53L1_STRING_STATE_STANDBY "STANDBY State" + #define VL53L1_STRING_STATE_IDLE "IDLE State" + #define VL53L1_STRING_STATE_RUNNING "RUNNING State" + #define VL53L1_STRING_STATE_RESET "RESET State" + #define VL53L1_STRING_STATE_UNKNOWN "UNKNOWN State" + #define VL53L1_STRING_STATE_ERROR "ERROR State" + + + + /* Check Enable */ + #define VL53L1_STRING_CHECKENABLE_SIGMA_FINAL_RANGE \ + "SIGMA FINAL RANGE" + #define VL53L1_STRING_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE \ + "SIGNAL RATE FINAL RANGE" + #define VL53L1_STRING_CHECKENABLE_SIGNAL_MIN_CLIP \ + "SIGNAL MIN CLIP" + #define VL53L1_STRING_CHECKENABLE_RANGE_IGNORE_THRESHOLD \ + "RANGE IGNORE THRESHOLD" + #define VL53L1_STRING_CHECKENABLE_RANGE_PHASE_HIGH \ + "RANGE PHASE HIGH" + #define VL53L1_STRING_CHECKENABLE_RANGE_PHASE_LOW \ + "RANGE PHASE LOW" + #define VL53L1_STRING_CHECKENABLE_RANGE_PHASE_CONSISTENCY \ + "RANGE PHASE CONSISTENCY" + + /* Sequence Step */ + #define VL53L1_STRING_SEQUENCESTEP_VHV "VHV" + #define VL53L1_STRING_SEQUENCESTEP_PHASECAL "PHASE CAL" + #define VL53L1_STRING_SEQUENCESTEP_REFPHASE "REF PHASE" + #define VL53L1_STRING_SEQUENCESTEP_DSS1 "DSS1" + #define VL53L1_STRING_SEQUENCESTEP_DSS2 "DSS2" + #define VL53L1_STRING_SEQUENCESTEP_MM1 "MM1" + #define VL53L1_STRING_SEQUENCESTEP_MM2 "MM2" + #define VL53L1_STRING_SEQUENCESTEP_RANGE "RANGE" +#endif /* VL53L1_USE_EMPTY_STRING */ + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_core.h b/drivers/input/misc/vl53L1/inc/vl53l1_core.h new file mode 100644 index 000000000000..fa6dc8925a55 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_core.h @@ -0,0 +1,1919 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_CORE_H_ +#define _VL53L1_CORE_H_ + +#include "vl53l1_platform.h" +#include "vl53l1_core_support.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + + + + + + +void VL53L1_init_version( + VL53L1_DEV Dev); + + + + + + + + + + +void VL53L1_init_ll_driver_state( + VL53L1_DEV Dev, + VL53L1_DeviceState ll_state); + + + + + + + + + + + + +VL53L1_Error VL53L1_update_ll_driver_rd_state( + VL53L1_DEV Dev); + + + + + + + + + + + + + +VL53L1_Error VL53L1_check_ll_driver_rd_state( + VL53L1_DEV Dev); + + + + + + + + + + + + +VL53L1_Error VL53L1_update_ll_driver_cfg_state( + VL53L1_DEV Dev); + + + + + + + + + + +void VL53L1_copy_rtn_good_spads_to_buffer( + VL53L1_nvm_copy_data_t *pdata, + uint8_t *pbuffer); + + + + + + + + + + + + +void VL53L1_init_system_results( + VL53L1_system_results_t *pdata); + + + + + + + + + + +void V53L1_init_zone_results_structure( + uint8_t active_zones, + VL53L1_zone_results_t *pdata); + + + + + + + + + +void V53L1_init_zone_dss_configs( + VL53L1_DEV Dev); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void VL53L1_init_histogram_config_structure( + uint8_t even_bin0, + uint8_t even_bin1, + uint8_t even_bin2, + uint8_t even_bin3, + uint8_t even_bin4, + uint8_t even_bin5, + uint8_t odd_bin0, + uint8_t odd_bin1, + uint8_t odd_bin2, + uint8_t odd_bin3, + uint8_t odd_bin4, + uint8_t odd_bin5, + VL53L1_histogram_config_t *pdata); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +void VL53L1_init_histogram_multizone_config_structure( + uint8_t even_bin0, + uint8_t even_bin1, + uint8_t even_bin2, + uint8_t even_bin3, + uint8_t even_bin4, + uint8_t even_bin5, + uint8_t odd_bin0, + uint8_t odd_bin1, + uint8_t odd_bin2, + uint8_t odd_bin3, + uint8_t odd_bin4, + uint8_t odd_bin5, + VL53L1_histogram_config_t *pdata); + + + + + + + + + + + +void VL53L1_init_xtalk_bin_data_struct( + uint32_t bin_value, + uint16_t VL53L1_p_024, + VL53L1_xtalk_histogram_shape_t *pdata); + + + + + + + + + + + + + +void VL53L1_i2c_encode_uint16_t( + uint16_t ip_value, + uint16_t count, + uint8_t *pbuffer); + + + + + + + + + + + + + + +uint16_t VL53L1_i2c_decode_uint16_t( + uint16_t count, + uint8_t *pbuffer); + + + + + + + + + + + + + +void VL53L1_i2c_encode_int16_t( + int16_t ip_value, + uint16_t count, + uint8_t *pbuffer); + + + + + + + + + + + + + + +int16_t VL53L1_i2c_decode_int16_t( + uint16_t count, + uint8_t *pbuffer); + + + + + + + + + + + + + +void VL53L1_i2c_encode_uint32_t( + uint32_t ip_value, + uint16_t count, + uint8_t *pbuffer); + + + + + + + + + + + + + + +uint32_t VL53L1_i2c_decode_uint32_t( + uint16_t count, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + + +uint32_t VL53L1_i2c_decode_with_mask( + uint16_t count, + uint8_t *pbuffer, + uint32_t bit_mask, + uint32_t down_shift, + uint32_t offset); + + + + + + + + + + + + + +void VL53L1_i2c_encode_int32_t( + int32_t ip_value, + uint16_t count, + uint8_t *pbuffer); + + + + + + + + + + + + + + +int32_t VL53L1_i2c_decode_int32_t( + uint16_t count, + uint8_t *pbuffer); + + + + + + + + + + + + + +VL53L1_Error VL53L1_start_test( + VL53L1_DEV Dev, + uint8_t test_mode__ctrl); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_firmware_enable_register( + VL53L1_DEV Dev, + uint8_t value); + + + + + + + + + + + + +VL53L1_Error VL53L1_enable_firmware( + VL53L1_DEV Dev); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_disable_firmware( + VL53L1_DEV Dev); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_powerforce_register( + VL53L1_DEV Dev, + uint8_t value); + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_enable_powerforce( + VL53L1_DEV Dev); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_disable_powerforce( + VL53L1_DEV Dev); + + + + + + + + + + + + + +VL53L1_Error VL53L1_clear_interrupt( + VL53L1_DEV Dev); + + + + + + + + + + + + + +VL53L1_Error VL53L1_force_shadow_stream_count_to_zero( + VL53L1_DEV Dev); + + + + + + + + + + + + + + + + + + + +uint32_t VL53L1_calc_macro_period_us( + uint16_t fast_osc_frequency, + uint8_t VL53L1_p_009); + + + + + + + + + + + + + + + + + + +uint16_t VL53L1_calc_range_ignore_threshold( + uint32_t central_rate, + int16_t x_gradient, + int16_t y_gradient, + uint8_t rate_mult); + + + + + + + + + + + + + +uint32_t VL53L1_calc_timeout_mclks( + uint32_t timeout_us, + uint32_t macro_period_us); + + + + + + + + + + + + +uint16_t VL53L1_calc_encoded_timeout( + uint32_t timeout_us, + uint32_t macro_period_us); + + + + + + + + + + + + + +uint32_t VL53L1_calc_timeout_us( + uint32_t timeout_mclks, + uint32_t macro_period_us); + + + + + + + + + + + + +uint32_t VL53L1_calc_decoded_timeout_us( + uint16_t timeout_encoded, + uint32_t macro_period_us); + + + + + + + + + + + +uint16_t VL53L1_encode_timeout( + uint32_t timeout_mclks); + + + + + + + + + + + + +uint32_t VL53L1_decode_timeout( + uint16_t encoded_timeout); + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_calc_timeout_register_values( + uint32_t phasecal_config_timeout_us, + uint32_t mm_config_timeout_us, + uint32_t range_config_timeout_us, + uint16_t fast_osc_frequency, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming); + + + + + + + + + + + + +uint8_t VL53L1_encode_vcsel_period( + uint8_t VL53L1_p_031); + + + + + + + + + + + + + + + +uint32_t VL53L1_decode_unsigned_integer( + uint8_t *pbuffer, + uint8_t no_of_bytes); + + + + + + + + + + + + +void VL53L1_encode_unsigned_integer( + uint32_t ip_value, + uint8_t no_of_bytes, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_hist_copy_and_scale_ambient_info( + VL53L1_zone_hist_info_t *pidata, + VL53L1_histogram_bin_data_t *podata); + + + + + + + + + + + + +void VL53L1_hist_get_bin_sequence_config( + VL53L1_DEV Dev, + VL53L1_histogram_bin_data_t *pdata); + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_hist_phase_consistency_check( + VL53L1_DEV Dev, + VL53L1_zone_hist_info_t *phist_prev, + VL53L1_zone_objects_t *prange_prev, + VL53L1_range_results_t *prange_curr); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_hist_events_consistency_check( + uint8_t event_sigma, + uint16_t min_effective_spad_count, + VL53L1_zone_hist_info_t *phist_prev, + VL53L1_object_data_t *prange_prev, + VL53L1_range_data_t *prange_curr, + int32_t *pevents_tolerance, + int32_t *pevents_delta, + VL53L1_DeviceError *prange_status); + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_hist_merged_pulse_check( + int16_t min_max_tolerance_mm, + VL53L1_range_data_t *pdata, + VL53L1_DeviceError *prange_status); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_hist_xmonitor_consistency_check( + VL53L1_DEV Dev, + VL53L1_zone_hist_info_t *phist_prev, + VL53L1_zone_objects_t *prange_prev, + VL53L1_range_data_t *prange_curr); + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_hist_wrap_dmax( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_histogram_bin_data_t *pcurrent, + int16_t *pwrap_dmax_mm); + + + + + + + + + + + + + + + + + + + + + +void VL53L1_hist_combine_mm1_mm2_offsets( + int16_t mm1_offset_mm, + int16_t mm2_offset_mm, + uint8_t encoded_mm_roi_centre, + uint8_t encoded_mm_roi_size, + uint8_t encoded_zone_centre, + uint8_t encoded_zone_size, + VL53L1_additional_offset_cal_data_t *pcal_data, + uint8_t *pgood_spads, + uint16_t aperture_attenuation, + int16_t *prange_offset_mm); + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_hist_xtalk_extract_calc_window( + int16_t target_distance_mm, + uint16_t target_width_oversize, + VL53L1_histogram_bin_data_t *phist_bins, + VL53L1_hist_xtalk_extract_data_t *pxtalk_data); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_hist_xtalk_extract_calc_event_sums( + VL53L1_histogram_bin_data_t *phist_bins, + VL53L1_hist_xtalk_extract_data_t *pxtalk_data); + + + + + + + + + + + + +VL53L1_Error VL53L1_hist_xtalk_extract_calc_rate_per_spad( + VL53L1_hist_xtalk_extract_data_t *pxtalk_data); + + + + + + + + + + + + + +VL53L1_Error VL53L1_hist_xtalk_extract_calc_shape( + VL53L1_hist_xtalk_extract_data_t *pxtalk_data, + VL53L1_xtalk_histogram_shape_t *pxtalk_shape); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_hist_xtalk_shape_model( + uint16_t events_per_bin, + uint16_t pulse_centre, + uint16_t pulse_width, + VL53L1_xtalk_histogram_shape_t *pxtalk_shape); + + + + + + + + + + + + + + +uint16_t VL53L1_hist_xtalk_shape_model_interp( + uint16_t events_per_bin, + uint32_t phase_delta); + + + + + + + + + + + + + + + + + + +void VL53L1_spad_number_to_byte_bit_index( + uint8_t spad_number, + uint8_t *pbyte_index, + uint8_t *pbit_index, + uint8_t *pbit_mask); + + + + + + + + + + + + + +void VL53L1_encode_row_col( + uint8_t row, + uint8_t col, + uint8_t *pspad_number); + + + + + + + + + + + + +void VL53L1_decode_zone_size( + uint8_t encoded_xy_size, + uint8_t *pwidth, + uint8_t *pheight); + + + + + + + + + + + + +void VL53L1_encode_zone_size( + uint8_t width, + uint8_t height, + uint8_t *pencoded_xy_size); + + + + + + + + + + + + + + + + + +void VL53L1_decode_zone_limits( + uint8_t encoded_xy_centre, + uint8_t encoded_xy_size, + int16_t *px_ll, + int16_t *py_ll, + int16_t *px_ur, + int16_t *py_ur); + + + + + + + + + + + + +uint8_t VL53L1_is_aperture_location( + uint8_t row, + uint8_t col); + + + + + + + + + + + + + + + +void VL53L1_calc_max_effective_spads( + uint8_t encoded_zone_centre, + uint8_t encoded_zone_size, + uint8_t *pgood_spads, + uint16_t aperture_attenuation, + uint16_t *pmax_effective_spads); + + + + + + + + + + + + + + + + + + + +void VL53L1_calc_mm_effective_spads( + uint8_t encoded_mm_roi_centre, + uint8_t encoded_mm_roi_size, + uint8_t encoded_zone_centre, + uint8_t encoded_zone_size, + uint8_t *pgood_spads, + uint16_t aperture_attenuation, + uint16_t *pmm_inner_effective_spads, + uint16_t *pmm_outer_effective_spads); + + + + + + + + + + + + + + +void VL53L1_hist_copy_results_to_sys_and_core( + VL53L1_histogram_bin_data_t *pbins, + VL53L1_range_results_t *phist, + VL53L1_system_results_t *psys, + VL53L1_core_results_t *pcore); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_sum_histogram_data( + VL53L1_histogram_bin_data_t *phist_input, + VL53L1_histogram_bin_data_t *phist_output); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_avg_histogram_data( + uint8_t no_of_samples, + VL53L1_histogram_bin_data_t *phist_sum, + VL53L1_histogram_bin_data_t *phist_avg); + + + + + + + + + + + + + +VL53L1_Error VL53L1_save_cfg_data( + VL53L1_DEV Dev); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_dynamic_zone_update( + VL53L1_DEV Dev, + VL53L1_range_results_t *presults); + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_update_internal_stream_counters( + VL53L1_DEV Dev, + uint8_t external_stream_count, + uint8_t *pinternal_stream_count, + uint8_t *pinternal_stream_count_val + ); + + + + + + + + + + + + +VL53L1_Error VL53L1_multizone_hist_bins_update( + VL53L1_DEV Dev); + + + + + + + +VL53L1_Error VL53L1_set_histogram_multizone_initial_bin_config( + VL53L1_zone_config_t *pzone_cfg, + VL53L1_histogram_config_t *phist_cfg, + VL53L1_histogram_config_t *pmulti_hist + ); + + + + + + + + + +uint8_t VL53L1_encode_GPIO_interrupt_config( + VL53L1_GPIO_interrupt_config_t *pintconf); + + + + + + + + + +VL53L1_GPIO_interrupt_config_t VL53L1_decode_GPIO_interrupt_config( + uint8_t system__interrupt_config); + + + + + + + + + + +VL53L1_Error VL53L1_set_GPIO_distance_threshold( + VL53L1_DEV Dev, + uint16_t threshold_high, + uint16_t threshold_low); + + + + + + + + + + +VL53L1_Error VL53L1_set_GPIO_rate_threshold( + VL53L1_DEV Dev, + uint16_t threshold_high, + uint16_t threshold_low); + + + + + + + + + + +VL53L1_Error VL53L1_set_GPIO_thresholds_from_struct( + VL53L1_DEV Dev, + VL53L1_GPIO_interrupt_config_t *pintconf); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_ref_spad_char_config( + VL53L1_DEV Dev, + uint8_t vcsel_period_a, + uint32_t phasecal_timeout_us, + uint16_t total_rate_target_mcps, + uint16_t max_count_rate_rtn_limit_mcps, + uint16_t min_count_rate_rtn_limit_mcps, + uint16_t fast_osc_frequency); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_ssc_config( + VL53L1_DEV Dev, + VL53L1_ssc_config_t *pssc_cfg, + uint16_t fast_osc_frequency); + + + + + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_spad_rate_data( + VL53L1_DEV Dev, + VL53L1_spad_rate_data_t *pspad_rates); + + + + + + + + + + + + + + + + +uint32_t VL53L1_calc_crosstalk_plane_offset_with_margin( + uint32_t plane_offset_kcps, + int16_t margin_offset_kcps); + + + + + + + + + + + + + +VL53L1_Error VL53L1_low_power_auto_data_init( + VL53L1_DEV Dev + ); + + + + + + + + + + + + + +VL53L1_Error VL53L1_low_power_auto_data_stop_range( + VL53L1_DEV Dev + ); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_calc_required_samples( + VL53L1_DEV Dev + ); + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_calc_new_xtalk( + VL53L1_DEV Dev, + uint32_t xtalk_offset_out, + VL53L1_smudge_corrector_config_t *pconfig, + VL53L1_smudge_corrector_data_t *pout, + uint8_t add_smudge, + uint8_t soft_update + ); + + + + + + + + + + + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_corrector( + VL53L1_DEV Dev + ); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_data_init( + VL53L1_DEV Dev + ); + + + + + + + + + + + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_output_init( + VL53L1_LLDriverResults_t *pres + ); + + + + + + + + + + + + + +VL53L1_Error VL53L1_xtalk_cal_data_init( + VL53L1_DEV Dev + ); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_config_low_power_auto_mode( + VL53L1_general_config_t *pgeneral, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_low_power_auto_data_t *plpadata + ); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_low_power_auto_setup_manual_calibration( + VL53L1_DEV Dev); + + + + + + + + + + + + + +VL53L1_Error VL53L1_low_power_auto_update_DSS( + VL53L1_DEV Dev); + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_core_support.h b/drivers/input/misc/vl53L1/inc/vl53l1_core_support.h new file mode 100644 index 000000000000..0b117491d6ce --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_core_support.h @@ -0,0 +1,414 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_CORE_SUPPORT_H_ +#define _VL53L1_CORE_SUPPORT_H_ + +#include "vl53l1_types.h" +#include "vl53l1_hist_structs.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + + + + + + + + + +uint32_t VL53L1_calc_pll_period_us( + uint16_t fast_osc_frequency); + + + + + + + + + + + + + + + + + + +uint32_t VL53L1_duration_maths( + uint32_t pll_period_us, + uint32_t vcsel_parm_pclks, + uint32_t window_vclks, + uint32_t periods_elapsed_mclks); + + + + + + + + + + + + + + + + +uint32_t VL53L1_events_per_spad_maths( + int32_t VL53L1_p_013, + uint16_t num_spads, + uint32_t duration); + + + + + + + + + + + + + +uint32_t VL53L1_isqrt( + uint32_t num); + + + + + + + + + + +void VL53L1_hist_calc_zero_distance_phase( + VL53L1_histogram_bin_data_t *pdata); + + + + + + + + + + + + + + + + + + + +void VL53L1_hist_estimate_ambient_from_thresholded_bins( + int32_t ambient_threshold_sigma, + VL53L1_histogram_bin_data_t *pdata); + + + + + + + + + + + + + +void VL53L1_hist_remove_ambient_bins( + VL53L1_histogram_bin_data_t *pdata); + + + + + + + + + + + + + + +uint32_t VL53L1_calc_pll_period_mm( + uint16_t fast_osc_frequency); + + + + + + + + + + + + + + + +uint16_t VL53L1_rate_maths( + int32_t VL53L1_p_008, + uint32_t time_us); + + + + + + + + + + + + + + + + + + +uint16_t VL53L1_rate_per_spad_maths( + uint32_t frac_bits, + uint32_t peak_count_rate, + uint16_t num_spads, + uint32_t max_output_value); + + + + + + + + + + + + + + + + + + + +int32_t VL53L1_range_maths( + uint16_t fast_osc_frequency, + uint16_t VL53L1_p_017, + uint16_t zero_distance_phase, + uint8_t fractional_bits, + int32_t gain_factor, + int32_t range_offset_mm); + + + + + + + + + + + + +uint8_t VL53L1_decode_vcsel_period( + uint8_t vcsel_period_reg); + + + + + + + + + + + +void VL53L1_copy_xtalk_bin_data_to_histogram_data_struct( + VL53L1_xtalk_histogram_shape_t *pxtalk, + VL53L1_histogram_bin_data_t *phist); + + + + + + + + + + + +void VL53L1_init_histogram_bin_data_struct( + int32_t bin_value, + uint16_t VL53L1_p_024, + VL53L1_histogram_bin_data_t *pdata); + + + + + + + + + + + + +void VL53L1_decode_row_col( + uint8_t spad_number, + uint8_t *prow, + uint8_t *pcol); + + + + + + + + + + + + + + + +void VL53L1_hist_find_min_max_bin_values( + VL53L1_histogram_bin_data_t *pdata); + + + + + + + + + + + + + + + +void VL53L1_hist_estimate_ambient_from_ambient_bins( + VL53L1_histogram_bin_data_t *pdata); + + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_def.h b/drivers/input/misc/vl53L1/inc/vl53l1_def.h new file mode 100644 index 000000000000..471c8712d7ec --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_def.h @@ -0,0 +1,846 @@ +/****************************************************************************** + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ******************************************************************************* + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + + ******************************************************************************* + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones mentioned above : + + ******************************************************************************* + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + + ******************************************************************************* + */ + +/** + * @file vl53l1_def.h + * + * @brief Type definitions for VL53L1 API. + * + */ + + +#ifndef _VL53L1_DEF_H_ +#define _VL53L1_DEF_H_ + +#include "vl53l1_ll_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup VL53L1_globaldefine_group VL53L1 Defines + * @brief VL53L1 Defines + * @{ + */ + + +/** VL53L1 IMPLEMENTATION major version */ +#define VL53L1_IMPLEMENTATION_VER_MAJOR 6 +/** VL53L1 IMPLEMENTATION minor version */ +#define VL53L1_IMPLEMENTATION_VER_MINOR 1 +/** VL53L1 IMPLEMENTATION sub version */ +#define VL53L1_IMPLEMENTATION_VER_SUB 1 +/** VL53L1 IMPLEMENTATION sub version */ +#define VL53L1_IMPLEMENTATION_VER_REVISION 2129 + +/**************************************** + * PRIVATE define do not edit + ****************************************/ + +/** @brief Defines the parameters of the Get Version Functions + */ +typedef struct { + uint32_t revision; /*!< revision number */ + uint8_t major; /*!< major number */ + uint8_t minor; /*!< minor number */ + uint8_t build; /*!< build number */ +} VL53L1_Version_t; + + +#define VL53L1_DEVINFO_STRLEN 32 + +/** @brief Defines the parameters of the Get Device Info Functions + */ +typedef struct { + char Name[VL53L1_DEVINFO_STRLEN]; + /*!< Full Name of the Device e.g. VL53L1 cut1.1 */ + char Type[VL53L1_DEVINFO_STRLEN]; + /*!< Type of the Device e.g VL53L1 */ + char ProductId[VL53L1_DEVINFO_STRLEN]; + /*!< Product Identifier String + * @warning Not yet implemented + */ + uint8_t ProductType; + /*!< Product Type, VL53L1 = 0xCC, VL53L3 = 0xAA + * Stands as module_type in the datasheet + */ + uint8_t ProductRevisionMajor; + /*!< Product revision major */ + uint8_t ProductRevisionMinor; + /*!< Product revision minor */ +} VL53L1_DeviceInfo_t; + + + +/** @defgroup VL53L1_define_PresetModes_group Defines Preset modes + * Defines all possible preset modes for the device + * @{ + */ +typedef uint8_t VL53L1_PresetModes; + +#define VL53L1_PRESETMODE_RANGING ((VL53L1_PresetModes) 1) +#define VL53L1_PRESETMODE_MULTIZONES_SCANNING ((VL53L1_PresetModes) 2) +#define VL53L1_PRESETMODE_AUTONOMOUS ((VL53L1_PresetModes) 3) +#define VL53L1_PRESETMODE_LITE_RANGING ((VL53L1_PresetModes) 4) +#define VL53L1_PRESETMODE_OLT ((VL53L1_PresetModes) 7) +#define VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS ((VL53L1_PresetModes) 8) +#define VL53L1_PRESETMODE_PROXY_RANGING_MODE ((VL53L1_PresetModes) 9) + + /* ... Modes to be added depending on device */ +/** @} VL53L1_define_PresetModes_group */ + + +/** @defgroup VL53L1_define_DistanceModes_group Defines Distance modes + * Defines all possible Distance modes for the device + * @{ + */ +typedef uint8_t VL53L1_DistanceModes; + +#define VL53L1_DISTANCEMODE_SHORT ((VL53L1_DistanceModes) 1) +#define VL53L1_DISTANCEMODE_MEDIUM ((VL53L1_DistanceModes) 2) +#define VL53L1_DISTANCEMODE_LONG ((VL53L1_DistanceModes) 3) +/** @} VL53L1_define_DistanceModes_group */ + +/** @defgroup VL53L1_define_OutputModes_group Defines Output modes + * Defines all possible Output modes for the device + * @{ + */ +typedef uint8_t VL53L1_OutputModes; + +#define VL53L1_OUTPUTMODE_NEAREST ((VL53L1_OutputModes) 1) +#define VL53L1_OUTPUTMODE_STRONGEST ((VL53L1_OutputModes) 2) + +/** @} VL53L1_define_OutputModes_group */ + +/** @defgroup VL53L1_define_XtalkCal_group Defines Xtalk Calibration modes + * Defines all possible Offset Calibration modes for the device + * @{ + */ +typedef uint8_t VL53L1_XtalkCalibrationModes; + +#define VL53L1_XTALKCALIBRATIONMODE_NO_TARGET \ + ((VL53L1_OffsetCalibrationModes) 0) +/*!< To perform Xtalk calibration with no target below 80 cm */ +#define VL53L1_XTALKCALIBRATIONMODE_SINGLE_TARGET \ + ((VL53L1_OffsetCalibrationModes) 1) +/*!< To perform Xtalk calibration with one target */ +#define VL53L1_XTALKCALIBRATIONMODE_FULL_ROI \ + ((VL53L1_OffsetCalibrationModes) 2) +/*!< To perform Xtalk calibration based on histogram with full ROI */ + +/** @} VL53L1_define_XtalkCal_group */ + +/** @defgroup VL53L1_define_OffsetCal_group Defines Offset Calibration modes + * Defines all possible Offset Calibration modes for the device + * @{ + */ +typedef uint8_t VL53L1_OffsetCalibrationModes; + +#define VL53L1_OFFSETCALIBRATIONMODE_STANDARD \ + ((VL53L1_OffsetCalibrationModes) 1) +#define VL53L1_OFFSETCALIBRATIONMODE_PRERANGE_ONLY \ + ((VL53L1_OffsetCalibrationModes) 2) +#define VL53L1_OFFSETCALIBRATIONMODE_MULTI_ZONE \ + ((VL53L1_OffsetCalibrationModes) 3) + +/** @} VL53L1_define_OffsetCal_group */ + +/** @defgroup VL53L1_define_DeviceDmaxModes_group Defines Dmax source modes + * Defines all possible sources for Dmax calibration for the device + * @{ + */ +typedef uint8_t VL53L1_DeviceDmaxModes; + +#define VL53L1_DMAXMODE_FMT_CAL_DATA ((VL53L1_DeviceDmaxModes) 1) +#define VL53L1_DMAXMODE_CUSTCAL_DATA ((VL53L1_DeviceDmaxModes) 2) +#define VL53L1_DMAXMODE_PER_ZONE_CAL_DATA ((VL53L1_DeviceDmaxModes) 3) + +/** @} VL53L1_define_DeviceDmaxModes_group */ + +/** @defgroup VL53L1_define_OffsetCorrectionModesBD_group + * Device Offset Correction Mode + * + * @brief Defines all possible offset correction modes for the device + * @{ + */ +typedef uint8_t VL53L1_OffsetCorrectionModes; + +#define VL53L1_OFFSETCORRECTIONMODE_STANDARD ((VL53L1_OffsetCorrectionMode) 1) +#define VL53L1_OFFSETCORRECTIONMODE_PERZONE ((VL53L1_OffsetCorrectionMode) 2) + +/** @} VL53L1_define_OffsetCorrectionModesBD_group */ + +/** @defgroup VL53L1_define_RoiStatus_group Defines Roi Status + * Defines the read status mode + * @{ + */ +typedef uint8_t VL53L1_RoiStatus; + +#define VL53L1_ROISTATUS_NOT_VALID ((VL53L1_RoiStatus) 0) +#define VL53L1_ROISTATUS_VALID_NOT_LAST ((VL53L1_RoiStatus) 1) +#define VL53L1_ROISTATUS_VALID_LAST ((VL53L1_RoiStatus) 2) +/** @} VL53L1_define_RoiStatus_group */ + + +/** @defgroup VL53L1_CheckEnable_group Check Enable list + * @brief Check Enable code + * + * Define used to specify the LimitCheckId. + * Use @a VL53L1_GetLimitCheckInfo() to get the string. + * @{ + */ + +#define VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE 0 +#define VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE 1 + +#define VL53L1_CHECKENABLE_NUMBER_OF_CHECKS 2 + +/** @} end of VL53L1_CheckEnable_group */ + + +/** @defgroup VL53L1_ThresholdMode_gropup Detection Functionality + * @brief Defines the different functionalities for the detection feature + * @{ + */ +typedef uint8_t VL53L1_ThresholdMode; + +#define VL53L1_THRESHOLD_CROSSED_LOW \ + ((VL53L1_ThresholdMode) 0) + /*!< Trigger interrupt if value < thresh_low */ +#define VL53L1_THRESHOLD_CROSSED_HIGH \ + ((VL53L1_ThresholdMode) 1) + /*!< Trigger interrupt if value > thresh_high */ +#define VL53L1_THRESHOLD_OUT_OF_WINDOW \ + ((VL53L1_ThresholdMode) 2) + /*!< Trigger interrupt if value < thresh_low OR value > thresh_high */ +#define VL53L1_THRESHOLD_IN_WINDOW \ + ((VL53L1_ThresholdMode) 3) + /*!< Trigger interrupt if value > thresh_low AND value < thresh_high */ + +/** @} end of VL53L1_ThresholdMode_gropup */ + +/** @brief Defines parameters for Distance detection Thresholds configuration + */ +typedef struct { + VL53L1_ThresholdMode CrossMode; + uint16_t High; /*!< Distance threshold high limit in mm */ + uint16_t Low; /*!< Distance threshold low limit in mm */ +} VL53L1_DistanceThreshold_t; + +/** @brief Defines parameters for Signal rate detection Thresholds configuration + */ +typedef struct { + VL53L1_ThresholdMode CrossMode; + FixPoint1616_t High; /*!< Signal rate threshold high limit */ + FixPoint1616_t Low; /*!< Signal rate threshold low limit */ +} VL53L1_RateThreshold_t; + +/** @defgroup VL53L1_DetectionMode_group Gpio Functionality + * @brief Defines conditions leading to device's IT on GPIO + * @{ + */ +typedef uint8_t VL53L1_DetectionMode; + +#define VL53L1_DETECTION_NORMAL_RUN \ + ((VL53L1_DetectionMode) 0) + /*!< Trigger interrupt on new measurement regardless of threshold + * just like after a VL53L1_SetPresetMode() call + */ +#define VL53L1_DETECTION_DISTANCE_ONLY \ + ((VL53L1_DetectionMode) 1) + /*!< Trigger interrupt if "threshold event" occurs on distance */ +#define VL53L1_DETECTION_RATE_ONLY \ + ((VL53L1_DetectionMode) 2) + /*!< Trigger interrupt if "threshold event" occurs on signal rate */ +#define VL53L1_DETECTION_DISTANCE_AND_RATE \ + ((VL53L1_DetectionMode) 3) + /*!< Trigger interrupt if "threshold event" occurs on distance AND rate + */ +#define VL53L1_DETECTION_DISTANCE_OR_RATE \ + ((VL53L1_DetectionMode) 4) + /*!< Trigger interrupt if "threshold event" occurs on distance OR rate + */ + +/** @} end of VL53L1_DetectionMode_group */ + +/** @brief Defines parameters for User/object Detection configuration + */ +typedef struct { + VL53L1_DetectionMode DetectionMode; /*!< See #VL53L1_DetectionMode*/ + uint8_t IntrNoTarget; /*!< 1 to trigger IT in case of no target found */ + VL53L1_DistanceThreshold_t Distance; /*!< limits in mm */ + VL53L1_RateThreshold_t Rate;/*!< limits in FixPoint1616_t */ +} VL53L1_DetectionConfig_t; + + +/** @brief Defines all parameters for the device + */ +typedef struct { + VL53L1_PresetModes PresetMode; + /*!< Defines the operating mode to be used for the next measure */ + VL53L1_OutputModes OutputMode; + /*!< Defines the Output mode to be used for the next measure */ + VL53L1_DistanceModes DistanceMode; + /*!< Defines the operating mode to be used for the next measure */ + uint32_t MeasurementTimingBudgetMicroSeconds; + /*!< Defines the allowed total time for a single measurement */ + uint8_t LimitChecksEnable[VL53L1_CHECKENABLE_NUMBER_OF_CHECKS]; + /*!< This Array store all the Limit Check enable for this device. */ + uint8_t LimitChecksStatus[VL53L1_CHECKENABLE_NUMBER_OF_CHECKS]; + /*!< This Array stores all the Status of the check linked to last + * measurement. + */ + FixPoint1616_t LimitChecksValue[VL53L1_CHECKENABLE_NUMBER_OF_CHECKS]; + /*!< This Array stores all the Limit Check value for this device */ + FixPoint1616_t LimitChecksCurrent[VL53L1_CHECKENABLE_NUMBER_OF_CHECKS]; + /*!< This Array stores all the Limit Check current value from latest + * ranging + */ +} VL53L1_DeviceParameters_t; + + +/** @defgroup VL53L1_define_State_group Defines the current status of the device + * Defines the current status of the device + * @{ + */ + +typedef uint8_t VL53L1_State; + +#define VL53L1_STATE_POWERDOWN ((VL53L1_State) 0) + /*!< Device is in HW reset */ +#define VL53L1_STATE_WAIT_STATICINIT ((VL53L1_State) 1) + /*!< Device is initialized and wait for static initialization */ +#define VL53L1_STATE_STANDBY ((VL53L1_State) 2) + /*!< Device is in Low power Standby mode */ +#define VL53L1_STATE_IDLE ((VL53L1_State) 3) + /*!< Device has been initialized and ready to do measurements */ +#define VL53L1_STATE_RUNNING ((VL53L1_State) 4) + /*!< Device is performing measurement */ +#define VL53L1_STATE_RESET ((VL53L1_State) 5) + /*!< Soft reset has been run on Device */ +#define VL53L1_STATE_UNKNOWN ((VL53L1_State) 98) + /*!< Device is in unknown state and need to be rebooted */ +#define VL53L1_STATE_ERROR ((VL53L1_State) 99) + /*!< Device is in error state and need to be rebooted */ + +/** @} VL53L1_define_State_group */ + +/** @defgroup VL53L1_define_Smudge_Mode_group Defines smudge correction modes + * Defines the smudge correction modes + * @{ + */ + +typedef uint8_t VL53L1_SmudgeCorrectionModes; + +#define VL53L1_SMUDGE_CORRECTION_NONE ((VL53L1_SmudgeCorrectionModes) 0) + /*!< Smudge correction is applied continously accross the rangings */ +#define VL53L1_SMUDGE_CORRECTION_CONTINUOUS ((VL53L1_SmudgeCorrectionModes) 1) + /*!< Smudge correction is applied continously accross the rangings */ +#define VL53L1_SMUDGE_CORRECTION_SINGLE ((VL53L1_SmudgeCorrectionModes) 2) + /*!< Smudge correction is applied only once accross the rangings */ +#define VL53L1_SMUDGE_CORRECTION_DEBUG ((VL53L1_SmudgeCorrectionModes) 3) + /*!< Smudge detection is applied continously but Xtalk values are not + * updated automatically within the driver + */ + +/** @} VL53L1_define_Smudge_Correction_Mode_group */ + + +/** + * @struct VL53L1_RangingMeasurementData_t + * @brief Single Range measurement data. + */ +typedef struct { + uint32_t TimeStamp; + /*!< 32-bit time stamp. + * @warning Not yet implemented + */ + + uint8_t StreamCount; + /*!< 8-bit Stream Count. */ + + uint8_t RangeQualityLevel; + /*!< indicate a quality level in percentage from 0 to 100 + * @warning Not yet implemented + */ + + FixPoint1616_t SignalRateRtnMegaCps; + /*!< Return signal rate (MCPS)\n these is a 16.16 fix point + * value, which is effectively a measure of target + * reflectance. + */ + + FixPoint1616_t AmbientRateRtnMegaCps; + /*!< Return ambient rate (MCPS)\n these is a 16.16 fix point + * value, which is effectively a measure of the ambien + * t light. + */ + + uint16_t EffectiveSpadRtnCount; + /*!< Return the effective SPAD count for the return signal. + * To obtain Real value it should be divided by 256 + */ + + FixPoint1616_t SigmaMilliMeter; + /*!< Return the Sigma value in millimeter */ + + int16_t RangeMilliMeter; + /*!< range distance in millimeter. This should be between + * RangeMinMilliMeter and RangeMaxMilliMeter + */ + + uint8_t RangeFractionalPart; + /*!< Fractional part of range distance. Final value is a + * RangeMilliMeter + RangeFractionalPart/256. + * @warning Not yet implemented + */ + + uint8_t RangeStatus; + /*!< Range Status for the current measurement. This is device + * dependent. Value = 0 means value is valid. + */ +} VL53L1_RangingMeasurementData_t; + +/** + * @struct VL53L1_TargetRangeData_t + * @brief One Range measurement data for each target. + */ +typedef struct { + uint8_t RangeQualityLevel; + /*!< indicate a quality level in percentage from 0 to 100 + * @warning Not yet implemented + */ + + int16_t RangeMaxMilliMeter; + /*!< Tells what is the maximum detection distance of the object + * in current setup and environment conditions (Filled when + * applicable) + */ + + int16_t RangeMinMilliMeter; + /*!< Tells what is the minimum detection distance of the object + * in current setup and environment conditions (Filled when + * applicable) + */ + + FixPoint1616_t SignalRateRtnMegaCps; + /*!< Return signal rate (MCPS)\n these is a 16.16 fix point + * value, which is effectively a measure of target + * reflectance. + */ + + FixPoint1616_t AmbientRateRtnMegaCps; + /*!< Return ambient rate (MCPS)\n these is a 16.16 fix point + * value, which is effectively a measure of the ambien + * t light. + */ + + FixPoint1616_t SigmaMilliMeter; + /*!< Return the Sigma value in millimeter */ + + int16_t RangeMilliMeter; + /*!< range distance in millimeter. This should be between + * RangeMinMilliMeter and RangeMaxMilliMeter + */ + + uint8_t RangeFractionalPart; + /*!< Fractional part of range distance. Final value is a + * RangeMilliMeter + RangeFractionalPart/256. + * @warning Not yet implemented + */ + + uint8_t RangeStatus; + /*!< Range Status for the current measurement. This is device + * dependent. Value = 0 means value is valid. + */ +} VL53L1_TargetRangeData_t; +/** + * @struct VL53L1_MultiRangingData_t + * @brief Structure for storing the set of range results for a single ROI + * + */ +typedef struct { + uint32_t TimeStamp; + /*!< 32-bit time stamp. + * @warning Not yet implemented + */ + + uint8_t StreamCount; + /*!< 8-bit Stream Count. */ + + uint8_t RoiNumber; + /*!< Denotes on which ROI the range data is related to. */ + uint8_t NumberOfObjectsFound; + /*!< Indicate the number of objects found in the current ROI. + * This is used to know how many ranging data should be get. + * NumberOfObjectsFound is in the range 0 to + * VL53L1_MAX_RANGE_RESULTS. + */ + VL53L1_RoiStatus RoiStatus; + /*!< Indicate if the data read is valid or not or if this is + * the last valid data in the ROI. + */ + VL53L1_TargetRangeData_t RangeData[VL53L1_MAX_RANGE_RESULTS]; + /*!< Range data each target distance */ + uint8_t HasXtalkValueChanged; + /*!< set to 1 if a new Xtalk value has been computed whilst + * smudge correction mode enable by with + * VL53L1_SmudgeCorrectionEnable() function is either + * VL53L1_SMUDGE_CORRECTION_CONTINUOUS or + * VL53L1_SMUDGE_CORRECTION_SINGLE. + */ + uint16_t EffectiveSpadRtnCount; + /*!< Return the effective SPAD count for the return signal. + * To obtain Real value it should be divided by 256 + */ + int16_t DmaxMilliMeter; + /*!< range Dmax distance in millimeter. + */ + VL53L1_DistanceModes RecommendedDistanceMode; + /*!< suggestion for a better distance mode choice to improve + * range accuracy. + */ +} VL53L1_MultiRangingData_t; + + +/** @brief Defines User Zone(ROI) parameters + * + */ +typedef struct { + + uint8_t TopLeftX; /*!< Top Left x coordinate: 0-15 range */ + uint8_t TopLeftY; /*!< Top Left y coordinate: 0-15 range */ + uint8_t BotRightX; /*!< Bot Right x coordinate: 0-15 range */ + uint8_t BotRightY; /*!< Bot Right y coordinate: 0-15 range */ + +} VL53L1_UserRoi_t; + + +/** @brief Defines ROI configuration parameters + * + * Support up a max of 16 zones, Each Zone has the same size + * + */ +typedef struct { + + uint8_t NumberOfRoi; /*!< Number of Rois defined*/ + + VL53L1_UserRoi_t UserRois[VL53L1_MAX_USER_ZONES]; + /*!< List of Rois */ + +} VL53L1_RoiConfig_t; + +/** + * @struct VL53L1_CustomerNvmManaged_t + * + */ + +typedef struct { + uint8_t global_config__spad_enables_ref_0; + uint8_t global_config__spad_enables_ref_1; + uint8_t global_config__spad_enables_ref_2; + uint8_t global_config__spad_enables_ref_3; + uint8_t global_config__spad_enables_ref_4; + uint8_t global_config__spad_enables_ref_5; + uint8_t global_config__ref_en_start_select; + uint8_t ref_spad_man__num_requested_ref_spads; + uint8_t ref_spad_man__ref_location; + uint32_t algo__crosstalk_compensation_plane_offset_kcps; + int16_t algo__crosstalk_compensation_x_plane_gradient_kcps; + int16_t algo__crosstalk_compensation_y_plane_gradient_kcps; + uint16_t ref_spad_char__total_rate_target_mcps; + int16_t algo__part_to_part_range_offset_mm; + int16_t mm_config__inner_offset_mm; + int16_t mm_config__outer_offset_mm; +} VL53L1_CustomerNvmManaged_t; + +/** + * @struct VL53L1_CalibrationData_t + * @brief Structure for storing the Calibration Data + * + */ + +typedef struct { + + uint32_t struct_version; + VL53L1_CustomerNvmManaged_t customer; + VL53L1_dmax_calibration_data_t fmt_dmax_cal; + VL53L1_dmax_calibration_data_t cust_dmax_cal; + VL53L1_additional_offset_cal_data_t add_off_cal_data; + VL53L1_optical_centre_t optical_centre; + VL53L1_xtalk_histogram_data_t xtalkhisto; + VL53L1_gain_calibration_data_t gain_cal; + VL53L1_cal_peak_rate_map_t cal_peak_rate_map; + +} VL53L1_CalibrationData_t; + +#define VL53L1_ADDITIONAL_CALIBRATION_DATA_STRUCT_VERSION 0x10 +/** VL53L1 additional Calibration Data struct version final struct version + * is given by adding it to VL53L1_LL_CALIBRATION_DATA_STRUCT_VERSION + */ + +#define VL53L1_CALIBRATION_DATA_STRUCT_VERSION \ + (VL53L1_LL_CALIBRATION_DATA_STRUCT_VERSION + \ + VL53L1_ADDITIONAL_CALIBRATION_DATA_STRUCT_VERSION) +/* VL53L1 Calibration Data struct version */ + +/** + * @struct VL53L1_AdditionalData_t + * @brief Structure for storing the Additional Data + * + */ +typedef VL53L1_additional_data_t VL53L1_AdditionalData_t; + +/** + * @struct VL53L1_ZoneCalibrationData_t + * @brief Structure for storing the Zone Calibration Data + * + */ +typedef VL53L1_zone_calibration_results_t VL53L1_ZoneCalibrationData_t; + +/** @defgroup VL53L1_define_SequenceStepId_group Defines the SequenceStep + * Defines the the sequence steps performed during ranging.. + * @{ + */ +typedef uint8_t VL53L1_SequenceStepId; + +#define VL53L1_SEQUENCESTEP_VHV ((VL53L1_SequenceStepId) 0) +/*!>12)&0xFFFF) +#define VL53L1_FIXPOINT44TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<12) + +#define VL53L1_FIXPOINT1616TOFIXPOINT72(Value) \ + (uint16_t)((Value>>14)&0xFFFF) +#define VL53L1_FIXPOINT72TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<14) + +#define VL53L1_FIXPOINT1616TOFIXPOINT97(Value) \ + (uint16_t)((Value>>9)&0xFFFF) +#define VL53L1_FIXPOINT97TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<9) + +#define VL53L1_FIXPOINT1616TOFIXPOINT88(Value) \ + (uint16_t)((Value>>8)&0xFFFF) +#define VL53L1_FIXPOINT88TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<8) + +#define VL53L1_FIXPOINT1616TOFIXPOINT412(Value) \ + (uint16_t)((Value>>4)&0xFFFF) +#define VL53L1_FIXPOINT412TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<4) + +#define VL53L1_FIXPOINT1616TOFIXPOINT313(Value) \ + (uint16_t)((Value>>3)&0xFFFF) +#define VL53L1_FIXPOINT313TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<3) + +#define VL53L1_FIXPOINT1616TOFIXPOINT08(Value) \ + (uint8_t)((Value>>8)&0x00FF) +#define VL53L1_FIXPOINT08TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<8) + +#define VL53L1_FIXPOINT1616TOFIXPOINT53(Value) \ + (uint8_t)((Value>>13)&0x00FF) +#define VL53L1_FIXPOINT53TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<13) + +#define VL53L1_FIXPOINT1616TOFIXPOINT102(Value) \ + (uint16_t)((Value>>14)&0x0FFF) +#define VL53L1_FIXPOINT102TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<14) + +#define VL53L1_FIXPOINT1616TOFIXPOINT142(Value) \ + (uint16_t)((Value>>14)&0xFFFF) +#define VL53L1_FIXPOINT142TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<14) + +#define VL53L1_FIXPOINT1616TOFIXPOINT160(Value) \ + (uint16_t)((Value>>16)&0xFFFF) +#define VL53L1_FIXPOINT160TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<16) + +#define VL53L1_MAKEUINT16(lsb, msb) (uint16_t)((((uint16_t)msb)<<8) + \ + (uint16_t)lsb) + +#ifndef SUPPRESS_UNUSED_WARNING +#define SUPPRESS_UNUSED_WARNING(x) ((void) (x)) +#endif + +/** @} VL53L1_define_GeneralMacro_group */ + +/** @} VL53L1_globaldefine_group */ + + + +#ifdef __cplusplus +} +#endif + + +#endif /* _VL53L1_DEF_H_ */ diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_dmax_structs.h b/drivers/input/misc/vl53L1/inc/vl53l1_dmax_structs.h new file mode 100644 index 000000000000..5b4d9b7f20e3 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_dmax_structs.h @@ -0,0 +1,210 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_DMAX_STRUCTS_H_ +#define _VL53L1_DMAX_STRUCTS_H_ + +#include "vl53l1_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + +#define VL53L1_MAX_AMBIENT_DMAX_VALUES 5 + + + + + + + + + + + + +typedef struct { + + + + + uint16_t ref__actual_effective_spads; + + + uint16_t ref__peak_signal_count_rate_mcps; + + + uint16_t ref__distance_mm; + + + uint16_t ref_reflectance_pc; + + + + + + + uint16_t coverglass_transmission; + + + +} VL53L1_dmax_calibration_data_t; + + + + + + + + + + +typedef struct { + + + + + uint8_t signal_thresh_sigma; + + + uint8_t ambient_thresh_sigma; + + + int32_t min_ambient_thresh_events; + + + int32_t signal_total_events_limit; + + + + uint16_t target_reflectance_for_dmax_calc[ + VL53L1_MAX_AMBIENT_DMAX_VALUES]; + + + uint16_t max_effective_spads; + + + + + + + uint16_t dss_config__target_total_rate_mcps; + + + uint8_t dss_config__aperture_attenuation; + + + +} VL53L1_hist_gen3_dmax_config_t; + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_error_codes.h b/drivers/input/misc/vl53L1/inc/vl53l1_error_codes.h new file mode 100644 index 000000000000..1bf727eaf560 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_error_codes.h @@ -0,0 +1,273 @@ +/****************************************************************************** + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ******************************************************************************* + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + + ******************************************************************************* + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones mentioned above : + + ******************************************************************************* + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + + ******************************************************************************* + */ + +/** + * @file vl53l1_error_codes.h + * + * @brief Error Code definitions for VL53L1 API. + * + */ + +#ifndef _VL53L1_ERROR_CODES_H_ +#define _VL53L1_ERROR_CODES_H_ + +#include "vl53l1_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + **************************************** + * PRIVATE define do not edit + *************************************** + */ + +/* + * @defgroup VL53L1_define_Error_group Error and Warning code returned by API + * The following DEFINE are used to identify the PAL ERROR + * @{ + */ + +typedef int8_t VL53L1_Error; + +#define VL53L1_ERROR_NONE ((VL53L1_Error) 0) +#define VL53L1_ERROR_CALIBRATION_WARNING ((VL53L1_Error) - 1) + /*!< Warning invalid calibration data may be in used + * \a VL53L1_InitData() + * \a VL53L1_GetOffsetCalibrationData + * \a VL53L1_SetOffsetCalibrationData + */ +#define VL53L1_ERROR_MIN_CLIPPED ((VL53L1_Error) - 2) + /*!< Warning parameter passed was clipped to min before to be applied */ + +#define VL53L1_ERROR_UNDEFINED ((VL53L1_Error) - 3) + /*!< Unqualified error */ +#define VL53L1_ERROR_INVALID_PARAMS ((VL53L1_Error) - 4) + /*!< Parameter passed is invalid or out of range */ +#define VL53L1_ERROR_NOT_SUPPORTED ((VL53L1_Error) - 5) + /*!< Function is not supported in current mode or configuration */ +#define VL53L1_ERROR_RANGE_ERROR ((VL53L1_Error) - 6) + /*!< Device report a ranging error interrupt status */ +#define VL53L1_ERROR_TIME_OUT ((VL53L1_Error) - 7) + /*!< Aborted due to time out */ +#define VL53L1_ERROR_MODE_NOT_SUPPORTED ((VL53L1_Error) - 8) + /*!< Asked mode is not supported by the device */ +#define VL53L1_ERROR_BUFFER_TOO_SMALL ((VL53L1_Error) - 9) + /*!< ... */ +#define VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL ((VL53L1_Error) - 10) + /*!< Supplied buffer is larger than I2C supports */ +#define VL53L1_ERROR_GPIO_NOT_EXISTING ((VL53L1_Error) - 11) + /*!< User tried to setup a non-existing GPIO pin */ +#define VL53L1_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED ((VL53L1_Error) - 12) + /*!< unsupported GPIO functionality */ +#define VL53L1_ERROR_CONTROL_INTERFACE ((VL53L1_Error) - 13) + /*!< error reported from IO functions */ +#define VL53L1_ERROR_INVALID_COMMAND ((VL53L1_Error) - 14) + /*!< The command is not allowed in the current device state + * (power down) + */ +#define VL53L1_ERROR_DIVISION_BY_ZERO ((VL53L1_Error) - 15) + /*!< In the function a division by zero occurs */ +#define VL53L1_ERROR_REF_SPAD_INIT ((VL53L1_Error) - 16) + /*!< Error during reference SPAD initialization */ +#define VL53L1_ERROR_GPH_SYNC_CHECK_FAIL ((VL53L1_Error) - 17) + /*!< GPH sync interrupt check fail - API out of sync with device*/ +#define VL53L1_ERROR_STREAM_COUNT_CHECK_FAIL ((VL53L1_Error) - 18) + /*!< Stream count check fail - API out of sync with device */ +#define VL53L1_ERROR_GPH_ID_CHECK_FAIL ((VL53L1_Error) - 19) + /*!< GPH ID check fail - API out of sync with device */ +#define VL53L1_ERROR_ZONE_STREAM_COUNT_CHECK_FAIL ((VL53L1_Error) - 20) + /*!< Zone dynamic config stream count check failed - API out of sync */ +#define VL53L1_ERROR_ZONE_GPH_ID_CHECK_FAIL ((VL53L1_Error) - 21) + /*!< Zone dynamic config GPH ID check failed - API out of sync */ + +#define VL53L1_ERROR_XTALK_EXTRACTION_NO_SAMPLE_FAIL ((VL53L1_Error) - 22) + /*!< Thrown when run_xtalk_extraction fn has 0 succesful samples + * when using the full array to sample the xtalk. In this case there is + * not enough information to generate new Xtalk parm info. The function + * will exit and leave the current xtalk parameters unaltered + */ +#define VL53L1_ERROR_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL ((VL53L1_Error) - 23) + /*!< Thrown when run_xtalk_extraction fn has found that the + * avg sigma estimate of the full array xtalk sample is > than the + * maximal limit allowed. In this case the xtalk sample is too noisy for + * measurement. The function will exit and leave the current xtalk + * parameters unaltered. + */ + + +#define VL53L1_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL ((VL53L1_Error) - 24) + /*!< Thrown if there one of stages has no valid offset calibration + * samples. A fatal error calibration not valid + */ +#define VL53L1_ERROR_OFFSET_CAL_NO_SPADS_ENABLED_FAIL ((VL53L1_Error) - 25) + /*!< Thrown if there one of stages has zero effective SPADS + * Traps the case when MM1 SPADs is zero. + * A fatal error calibration not valid + */ +#define VL53L1_ERROR_ZONE_CAL_NO_SAMPLE_FAIL ((VL53L1_Error) - 26) + /*!< Thrown if then some of the zones have no valid samples + * A fatal error calibration not valid + */ + +#define VL53L1_ERROR_TUNING_PARM_KEY_MISMATCH ((VL53L1_Error) - 27) + /*!< Thrown if the tuning file key table version does not match with + * expected value. The driver expects the key table version to match + * the compiled default version number in the define + * #VL53L1_TUNINGPARM_KEY_TABLE_VERSION_DEFAULT + */ + +#define VL53L1_WARNING_REF_SPAD_CHAR_NOT_ENOUGH_SPADS ((VL53L1_Error) - 28) + /*!< Thrown if there are less than 5 good SPADs are available. */ +#define VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_HIGH ((VL53L1_Error) - 29) + /*!< Thrown if the final reference rate is greater than + * the upper reference rate limit - default is 40 Mcps. + * Implies a minimum Q3 (x10) SPAD (5) selected + */ +#define VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_LOW ((VL53L1_Error) - 30) + /*!< Thrown if the final reference rate is less than + * the lower reference rate limit - default is 10 Mcps. + * Implies maximum Q1 (x1) SPADs selected + */ + + +#define VL53L1_WARNING_OFFSET_CAL_MISSING_SAMPLES ((VL53L1_Error) - 31) + /*!< Thrown if there is less than the requested number of + * valid samples. + */ +#define VL53L1_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH ((VL53L1_Error) - 32) + /*!< Thrown if the offset calibration range sigma estimate is greater + * than 8.0 mm. This is the recommended min value to yield a stable + * offset measurement + */ +#define VL53L1_WARNING_OFFSET_CAL_RATE_TOO_HIGH ((VL53L1_Error) - 33) + /*!< Thrown when VL53L1_run_offset_calibration() peak rate is greater + * than that 50.0Mcps. This is the recommended max rate to avoid + * pile-up influencing the offset measurement + */ +#define VL53L1_WARNING_OFFSET_CAL_SPAD_COUNT_TOO_LOW ((VL53L1_Error) - 34) + /*!< Thrown when VL53L1_run_offset_calibration() when one of stages + * range has less that 5.0 effective SPADS. This is the recommended + * min value to yield a stable offset + */ + + +#define VL53L1_WARNING_ZONE_CAL_MISSING_SAMPLES ((VL53L1_Error) - 35) + /*!< Thrown if one of more of the zones have less than + * the requested number of valid samples + */ +#define VL53L1_WARNING_ZONE_CAL_SIGMA_TOO_HIGH ((VL53L1_Error) - 36) + /*!< Thrown if one or more zones have sigma estimate value greater + * than 8.0 mm. This is the recommended min value to yield a stable + * offset measurement + */ +#define VL53L1_WARNING_ZONE_CAL_RATE_TOO_HIGH ((VL53L1_Error) - 37) + /*!< Thrown if one of more zones have peak rate higher than + * that 50.0Mcps. This is the recommended max rate to avoid + * pile-up influencing the offset measurement + */ + + +#define VL53L1_WARNING_XTALK_MISSING_SAMPLES ((VL53L1_Error) - 38) + /*!< Thrown to notify that some of the xtalk samples did not yield + * valid ranging pulse data while attempting to measure + * the xtalk signal in vl53l1_run_xtalk_extract(). This can signify any + * of the zones are missing samples, for further debug information the + * xtalk_results struct should be referred to. This warning is for + * notification only, xtalk pulse and shape have still been generated + */ +#define VL53L1_WARNING_XTALK_NO_SAMPLES_FOR_GRADIENT ((VL53L1_Error) - 39) + /*!< Thrown to notify that some of teh xtalk samples used for gradient + * generation did not yield valid ranging pulse data while attempting to + * measure the xtalk signal in vl53l1_run_xtalk_extract(). This can + * signify that any one of the zones 0-3 yielded no successful samples. + * xtalk_results struct should be referred to for further debug info. + * This warning is for notification only, the xtalk pulse and shape + * have still been generated. + */ +#define VL53L1_WARNING_XTALK_SIGMA_LIMIT_FOR_GRADIENT ((VL53L1_Error) - 40) + /*!< Thrown to notify that some of the xtalk samples used for gradient + * generation did not pass the sigma limit check while attempting to + * measure the xtalk signal in vl53l1_run_xtalk_extract(). This can + * signify that any one of the zones 0-3 yielded an avg sigma_mm + * value > the limit. The xtalk_results struct should be referred to for + * further debug info. + * This warning is for notification only, the xtalk pulse and shape + * have still been generated. + */ + +#define VL53L1_ERROR_NOT_IMPLEMENTED ((VL53L1_Error) - 41) + /*!< Tells requested functionality has not been implemented yet or + * not compatible with the device + */ +#define VL53L1_ERROR_PLATFORM_SPECIFIC_START ((VL53L1_Error) - 60) + /*!< Tells the starting code for platform */ +/** @} VL53L1_define_Error_group */ + + +#ifdef __cplusplus +} +#endif + + +#endif /* _VL53L1_ERROR_CODES_H_ */ diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_error_exceptions.h b/drivers/input/misc/vl53L1/inc/vl53l1_error_exceptions.h new file mode 100644 index 000000000000..0b2db2ba0e28 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_error_exceptions.h @@ -0,0 +1,126 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_ERROR_EXCEPTIONS_H_ +#define _VL53L1_ERROR_EXCEPTIONS_H_ + +#define IGNORE_DIVISION_BY_ZERO 0 + +#define IGNORE_XTALK_EXTRACTION_NO_SAMPLE_FAIL 0 +#define IGNORE_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL 0 +#define IGNORE_XTALK_EXTRACTION_NO_SAMPLE_FOR_GRADIENT_WARN 0 +#define IGNORE_XTALK_EXTRACTION_SIGMA_LIMIT_FOR_GRADIENT_WARN 0 +#define IGNORE_XTALK_EXTRACTION_MISSING_SAMPLES_WARN 0 + +#define IGNORE_REF_SPAD_CHAR_NOT_ENOUGH_SPADS 0 +#define IGNORE_REF_SPAD_CHAR_RATE_TOO_HIGH 0 +#define IGNORE_REF_SPAD_CHAR_RATE_TOO_LOW 0 + +#define IGNORE_OFFSET_CAL_MISSING_SAMPLES 0 +#define IGNORE_OFFSET_CAL_SIGMA_TOO_HIGH 0 +#define IGNORE_OFFSET_CAL_RATE_TOO_HIGH 0 +#define IGNORE_OFFSET_CAL_SPAD_COUNT_TOO_LOW 0 + +#define IGNORE_ZONE_CAL_MISSING_SAMPLES 0 +#define IGNORE_ZONE_CAL_SIGMA_TOO_HIGH 0 +#define IGNORE_ZONE_CAL_RATE_TOO_HIGH 0 + +#endif + + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_error_strings.h b/drivers/input/misc/vl53L1/inc/vl53l1_error_strings.h new file mode 100644 index 000000000000..02cba46adb53 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_error_strings.h @@ -0,0 +1,244 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef VL53L1_ERROR_STRINGS_H_ +#define VL53L1_ERROR_STRINGS_H_ + +#include "vl53l1_error_codes.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_pal_error_string( + VL53L1_Error PalErrorCode, + char *pPalErrorString); + + +#ifndef VL53L1_USE_EMPTY_STRING + + + + #define VL53L1_STRING_ERROR_NONE \ + "No Error" + #define VL53L1_STRING_ERROR_CALIBRATION_WARNING \ + "Calibration Warning Error" + #define VL53L1_STRING_ERROR_MIN_CLIPPED \ + "Min clipped error" + #define VL53L1_STRING_ERROR_UNDEFINED \ + "Undefined error" + #define VL53L1_STRING_ERROR_INVALID_PARAMS \ + "Invalid parameters error" + #define VL53L1_STRING_ERROR_NOT_SUPPORTED \ + "Not supported error" + #define VL53L1_STRING_ERROR_RANGE_ERROR \ + "Range error" + #define VL53L1_STRING_ERROR_TIME_OUT \ + "Time out error" + #define VL53L1_STRING_ERROR_MODE_NOT_SUPPORTED \ + "Mode not supported error" + #define VL53L1_STRING_ERROR_BUFFER_TOO_SMALL \ + "Buffer too small" + #define VL53L1_STRING_ERROR_COMMS_BUFFER_TOO_SMALL \ + "Comms Buffer too small" + #define VL53L1_STRING_ERROR_GPIO_NOT_EXISTING \ + "GPIO not existing" + #define VL53L1_STRING_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED \ + "GPIO funct not supported" + #define VL53L1_STRING_ERROR_CONTROL_INTERFACE \ + "Control Interface Error" + #define VL53L1_STRING_ERROR_INVALID_COMMAND \ + "Invalid Command Error" + #define VL53L1_STRING_ERROR_DIVISION_BY_ZERO \ + "Division by zero Error" + #define VL53L1_STRING_ERROR_REF_SPAD_INIT \ + "Reference Spad Init Error" + #define VL53L1_STRING_ERROR_GPH_SYNC_CHECK_FAIL \ + "GPH Sync Check Fail - API out of sync" + #define VL53L1_STRING_ERROR_STREAM_COUNT_CHECK_FAIL \ + "Stream Count Check Fail - API out of sync" + #define VL53L1_STRING_ERROR_GPH_ID_CHECK_FAIL \ + "GPH ID Check Fail - API out of sync" + #define VL53L1_STRING_ERROR_ZONE_STREAM_COUNT_CHECK_FAIL \ + "Zone Stream Count Check Fail - API out of sync" + #define VL53L1_STRING_ERROR_ZONE_GPH_ID_CHECK_FAIL \ + "Zone GPH ID Check Fail - API out of sync" + + #define VL53L1_STRING_ERROR_XTALK_EXTRACTION_NO_SAMPLES_FAIL \ + "No Xtalk using full array - Xtalk Extract Fail" + #define VL53L1_STRING_ERROR_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL \ + "Xtalk does not meet required VL53L1_p_011 limit - Xtalk Extract Fail" + + #define VL53L1_STRING_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL \ + "Offset Cal - one of more stages with no valid samples - fatal" + #define VL53L1_STRING_ERROR_OFFSET_CAL_NO_SPADS_ENABLED_FAIL \ + "Offset Cal - one of more stages with no SPADS enables - fatal" + #define VL53L1_STRING_ERROR_ZONE_CAL_NO_SAMPLE_FAIL \ + "Zone Cal - one of more zones with no valid samples - fatal" + + #define VL53L1_STRING_WARNING_REF_SPAD_CHAR_NOT_ENOUGH_SPADS \ + "Ref SPAD Char - Not Enough Good SPADs" + #define VL53L1_STRING_WARNING_REF_SPAD_CHAR_RATE_TOO_HIGH \ + "Ref SPAD Char - Final Ref Rate too high" + #define VL53L1_STRING_WARNING_REF_SPAD_CHAR_RATE_TOO_LOW \ + "Ref SPAD Char - Final Ref Rate too low" + + #define VL53L1_STRING_WARNING_OFFSET_CAL_MISSING_SAMPLES \ + "Offset Cal - Less than the requested number of valid samples" + #define VL53L1_STRING_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH \ + "Offset Cal - Sigma estimate value too high - offset not stable" + #define VL53L1_STRING_WARNING_OFFSET_CAL_RATE_TOO_HIGH \ + "Offset Cal - Rate too high - in pile up" + #define VL53L1_STRING_WARNING_OFFSET_CAL_SPAD_COUNT_TOO_LOW \ + "Offset Cal - Insufficient SPADs - offset may not be stable" + + #define VL53L1_STRING_WARNING_ZONE_CAL_MISSING_SAMPLES \ + "Zone Cal - One or more zone with less than requested valid samples" + #define VL53L1_STRING_WARNING_ZONE_CAL_SIGMA_TOO_HIGH \ + "Zone Cal - One of more zones the VL53L1_p_011 estimate too high" + #define VL53L1_STRING_WARNING_ZONE_CAL_RATE_TOO_HIGH \ + "Zone Cal - One of more zones with rate too high - in pile up" + + #define VL53L1_STRING_WARNING_XTALK_NO_SAMPLES_FOR_GRADIENT \ + "Xtalk - Gradient sample num = 0" + #define VL53L1_STRING_WARNING_XTALK_SIGMA_LIMIT_FOR_GRADIENT \ + "Xtalk - Gradient Sigma > Limit" + #define VL53L1_STRING_WARNING_XTALK_MISSING_SAMPLES \ + "Xtalk - Some missing and invalid samples" + + #define VL53L1_STRING_ERROR_DEVICE_FIRMWARE_TOO_OLD \ + "Device Firmware too old" + #define VL53L1_STRING_ERROR_DEVICE_FIRMWARE_TOO_NEW \ + "Device Firmware too new" + #define VL53L1_STRING_ERROR_UNIT_TEST_FAIL \ + "Unit Test Fail" + #define VL53L1_STRING_ERROR_FILE_READ_FAIL \ + "File Read Fail" + #define VL53L1_STRING_ERROR_FILE_WRITE_FAIL \ + "File Write Fail" + + #define VL53L1_STRING_ERROR_NOT_IMPLEMENTED \ + "Not implemented error" + #define VL53L1_STRING_UNKNOW_ERROR_CODE \ + "Unknown Error Code" + +#endif + + + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_hist_char.h b/drivers/input/misc/vl53L1/inc/vl53l1_hist_char.h new file mode 100644 index 000000000000..25d84d16240a --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_hist_char.h @@ -0,0 +1,176 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_HIST_CHAR_H_ +#define _VL53L1_HIST_CHAR_H_ + +#include "vl53l1_platform.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_calib_config( + VL53L1_DEV Dev, + uint8_t vcsel_delay__a0, + uint8_t calib_1, + uint8_t calib_2, + uint8_t calib_3, + uint8_t calib_2__a0, + uint8_t spad_readout); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_hist_calib_pulse_delay( + VL53L1_DEV Dev, + uint8_t calib_delay); + + + + + + + + + + + + +VL53L1_Error VL53L1_disable_calib_pulse_delay( + VL53L1_DEV Dev); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_hist_map.h b/drivers/input/misc/vl53L1/inc/vl53l1_hist_map.h new file mode 100644 index 000000000000..952dd16d6019 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_hist_map.h @@ -0,0 +1,156 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_HIST_MAP_H_ +#define _VL53L1_HIST_MAP_H_ + +#include "vl53l1_register_map.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + + + + +#define VL53L1_HISTOGRAM_CONFIG__OPCODE_SEQUENCE_0 \ + VL53L1_SIGMA_ESTIMATOR__EFFECTIVE_PULSE_WIDTH_NS + +#define VL53L1_HISTOGRAM_CONFIG__OPCODE_SEQUENCE_1 \ + VL53L1_SIGMA_ESTIMATOR__EFFECTIVE_AMBIENT_WIDTH_NS + +#define VL53L1_HISTOGRAM_CONFIG__OPCODE_SEQUENCE_2 \ + VL53L1_SIGMA_ESTIMATOR__SIGMA_REF_MM + +#define VL53L1_HISTOGRAM_CONFIG__AMB_THRESH_HIGH \ + VL53L1_ALGO__RANGE_IGNORE_THRESHOLD_MCPS + + + + + +#define VL53L1_RESULT__HISTOGRAM_BIN_0_2 0x008E +#define VL53L1_RESULT__HISTOGRAM_BIN_0_1 0x008F +#define VL53L1_RESULT__HISTOGRAM_BIN_0_0 0x0090 + +#define VL53L1_RESULT__HISTOGRAM_BIN_23_2 0x00D3 +#define VL53L1_RESULT__HISTOGRAM_BIN_23_1 0x00D4 +#define VL53L1_RESULT__HISTOGRAM_BIN_23_0 0x00D5 + +#define VL53L1_RESULT__HISTOGRAM_BIN_23_0_MSB 0x00D9 +#define VL53L1_RESULT__HISTOGRAM_BIN_23_0_LSB 0x00DA + + + + +#define VL53L1_HISTOGRAM_BIN_DATA_I2C_INDEX \ + VL53L1_RESULT__INTERRUPT_STATUS +#define VL53L1_HISTOGRAM_BIN_DATA_I2C_SIZE_BYTES \ + (VL53L1_RESULT__HISTOGRAM_BIN_23_0_LSB - \ + VL53L1_RESULT__INTERRUPT_STATUS + 1) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_hist_structs.h b/drivers/input/misc/vl53L1/inc/vl53l1_hist_structs.h new file mode 100644 index 000000000000..48909e091ec3 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_hist_structs.h @@ -0,0 +1,515 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_HIST_STRUCTS_H_ +#define _VL53L1_HIST_STRUCTS_H_ + +#include "vl53l1_ll_device.h" +#include "vl53l1_dmax_structs.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define VL53L1_MAX_BIN_SEQUENCE_LENGTH 6 +#define VL53L1_MAX_BIN_SEQUENCE_CODE 15 +#define VL53L1_HISTOGRAM_BUFFER_SIZE 24 +#define VL53L1_XTALK_HISTO_BINS 12 + + + + + + + + +typedef struct { + + uint8_t histogram_config__spad_array_selection; + + uint8_t histogram_config__low_amb_even_bin_0_1; + uint8_t histogram_config__low_amb_even_bin_2_3; + uint8_t histogram_config__low_amb_even_bin_4_5; + + uint8_t histogram_config__low_amb_odd_bin_0_1; + uint8_t histogram_config__low_amb_odd_bin_2_3; + uint8_t histogram_config__low_amb_odd_bin_4_5; + + uint8_t histogram_config__mid_amb_even_bin_0_1; + uint8_t histogram_config__mid_amb_even_bin_2_3; + uint8_t histogram_config__mid_amb_even_bin_4_5; + + uint8_t histogram_config__mid_amb_odd_bin_0_1; + uint8_t histogram_config__mid_amb_odd_bin_2; + uint8_t histogram_config__mid_amb_odd_bin_3_4; + uint8_t histogram_config__mid_amb_odd_bin_5; + + uint8_t histogram_config__user_bin_offset; + + uint8_t histogram_config__high_amb_even_bin_0_1; + uint8_t histogram_config__high_amb_even_bin_2_3; + uint8_t histogram_config__high_amb_even_bin_4_5; + + uint8_t histogram_config__high_amb_odd_bin_0_1; + uint8_t histogram_config__high_amb_odd_bin_2_3; + uint8_t histogram_config__high_amb_odd_bin_4_5; + + uint16_t histogram_config__amb_thresh_low; + + + + uint16_t histogram_config__amb_thresh_high; + + + + +} VL53L1_histogram_config_t; + + + + + + + + + +typedef struct { + + VL53L1_HistAlgoSelect hist_algo_select; + + + + VL53L1_HistTargetOrder hist_target_order; + + + + + + uint8_t filter_woi0; + + + + uint8_t filter_woi1; + + + + + VL53L1_HistAmbEstMethod hist_amb_est_method; + + + uint8_t ambient_thresh_sigma0; + + + + uint8_t ambient_thresh_sigma1; + + + + + + uint16_t ambient_thresh_events_scaler; + + + + + + + int32_t min_ambient_thresh_events; + + + uint16_t noise_threshold; + + + + int32_t signal_total_events_limit; + + + uint8_t sigma_estimator__sigma_ref_mm; + + + uint16_t sigma_thresh; + + + int16_t range_offset_mm; + + + uint16_t gain_factor; + + + + uint8_t valid_phase_low; + + + + uint8_t valid_phase_high; + + + + uint8_t algo__consistency_check__phase_tolerance; + + + + uint8_t algo__consistency_check__event_sigma; + + + + + + + uint16_t algo__consistency_check__event_min_spad_count; + + + + + + + uint16_t algo__consistency_check__min_max_tolerance; + + + + uint8_t algo__crosstalk_compensation_enable; + + + uint32_t algo__crosstalk_compensation_plane_offset_kcps; + + + int16_t algo__crosstalk_compensation_x_plane_gradient_kcps; + + + int16_t algo__crosstalk_compensation_y_plane_gradient_kcps; + + + + int16_t algo__crosstalk_detect_min_valid_range_mm; + + + int16_t algo__crosstalk_detect_max_valid_range_mm; + + + uint16_t algo__crosstalk_detect_max_valid_rate_kcps; + + + + uint16_t algo__crosstalk_detect_max_sigma_mm; + + + + + + + uint8_t algo__crosstalk_detect_event_sigma; + + + + + + + uint16_t algo__crosstalk_detect_min_max_tolerance; + + + + +} VL53L1_hist_post_process_config_t; + + + + + + + +typedef struct { + + + + VL53L1_DeviceState cfg_device_state; + + + VL53L1_DeviceState rd_device_state; + + + + uint8_t zone_id; + + + uint32_t time_stamp; + + + + uint8_t VL53L1_p_022; + + + uint8_t VL53L1_p_023; + + + uint8_t VL53L1_p_024; + + + + uint8_t number_of_ambient_bins; + + + + uint8_t bin_seq[VL53L1_MAX_BIN_SEQUENCE_LENGTH]; + + + uint8_t bin_rep[VL53L1_MAX_BIN_SEQUENCE_LENGTH]; + + + + + int32_t bin_data[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + + + uint8_t result__interrupt_status; + + + uint8_t result__range_status; + + + uint8_t result__report_status; + + + uint8_t result__stream_count; + + + uint16_t result__dss_actual_effective_spads; + + + + uint16_t phasecal_result__reference_phase; + + + uint8_t phasecal_result__vcsel_start; + + + uint8_t cal_config__vcsel_start; + + + uint16_t vcsel_width; + + + uint8_t VL53L1_p_009; + + + uint16_t VL53L1_p_019; + + + uint32_t total_periods_elapsed; + + + + uint32_t peak_duration_us; + + + uint32_t woi_duration_us; + + + + int32_t min_bin_value; + + + int32_t max_bin_value; + + + + uint16_t zero_distance_phase; + + + uint8_t number_of_ambient_samples; + + + int32_t ambient_events_sum; + + + int32_t VL53L1_p_004; + + + + uint8_t roi_config__user_roi_centre_spad; + + + uint8_t roi_config__user_roi_requested_global_xy_size; + + + +} VL53L1_histogram_bin_data_t; + + + + + + + + +typedef struct { + + + + uint8_t zone_id; + + + uint32_t time_stamp; + + + + uint8_t VL53L1_p_022; + + + uint8_t VL53L1_p_023; + + + uint8_t VL53L1_p_024; + + + uint32_t bin_data[VL53L1_XTALK_HISTO_BINS]; + + + + + uint16_t phasecal_result__reference_phase; + + + uint8_t phasecal_result__vcsel_start; + + + uint8_t cal_config__vcsel_start; + + + uint16_t vcsel_width; + + + uint16_t VL53L1_p_019; + + + uint16_t zero_distance_phase; + + + +} VL53L1_xtalk_histogram_shape_t; + + + + + + + + +typedef struct { + + + + VL53L1_xtalk_histogram_shape_t xtalk_shape; + + + VL53L1_histogram_bin_data_t xtalk_hist_removed; + +} VL53L1_xtalk_histogram_data_t; + + + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_ll_def.h b/drivers/input/misc/vl53L1/inc/vl53l1_ll_def.h new file mode 100644 index 000000000000..7e599ca26e0f --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_ll_def.h @@ -0,0 +1,2715 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_LL_DEF_H_ +#define _VL53L1_LL_DEF_H_ + +#include "vl53l1_error_codes.h" +#include "vl53l1_register_structs.h" +#include "vl53l1_platform_user_config.h" +#include "vl53l1_platform_user_defines.h" +#include "vl53l1_hist_structs.h" +#include "vl53l1_dmax_structs.h" +#include "vl53l1_error_exceptions.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + + + + + + +#define VL53L1_LL_API_IMPLEMENTATION_VER_MAJOR 1 + + +#define VL53L1_LL_API_IMPLEMENTATION_VER_MINOR 1 + + +#define VL53L1_LL_API_IMPLEMENTATION_VER_SUB 48 + + +#define VL53L1_LL_API_IMPLEMENTATION_VER_REVISION 12221 + +#define VL53L1_LL_API_IMPLEMENTATION_VER_STRING "1.1.48.12221" + + + +#define VL53L1_FIRMWARE_VER_MINIMUM 398 +#define VL53L1_FIRMWARE_VER_MAXIMUM 400 + + + + + + + +#define VL53L1_LL_CALIBRATION_DATA_STRUCT_VERSION 0xECAB0102 + + + + + + +#define VL53L1_LL_ZONE_CALIBRATION_DATA_STRUCT_VERSION 0xECAE0101 + + + + + + +#define VL53L1_MAX_XTALK_RANGE_RESULTS 5 + + + + + + +#define VL53L1_MAX_OFFSET_RANGE_RESULTS 3 + + + + + +#define VL53L1_NVM_MAX_FMT_RANGE_DATA 4 + + + +#define VL53L1_NVM_PEAK_RATE_MAP_SAMPLES 25 + + +#define VL53L1_NVM_PEAK_RATE_MAP_WIDTH 5 + + +#define VL53L1_NVM_PEAK_RATE_MAP_HEIGHT 5 + + + + + + + + + +#define VL53L1_ERROR_DEVICE_FIRMWARE_TOO_OLD ((VL53L1_Error) - 80) + + +#define VL53L1_ERROR_DEVICE_FIRMWARE_TOO_NEW ((VL53L1_Error) - 85) + + +#define VL53L1_ERROR_UNIT_TEST_FAIL ((VL53L1_Error) - 90) + + +#define VL53L1_ERROR_FILE_READ_FAIL ((VL53L1_Error) - 95) + + +#define VL53L1_ERROR_FILE_WRITE_FAIL ((VL53L1_Error) - 96) + + + + + + + + + + + + +typedef struct { + uint32_t ll_revision; + + uint8_t ll_major; + + uint8_t ll_minor; + + uint8_t ll_build; + +} VL53L1_ll_version_t; + + + + + + +typedef struct { + + uint8_t device_test_mode; + + uint8_t VL53L1_p_009; + + uint32_t timeout_us; + + uint16_t target_count_rate_mcps; + + + uint16_t min_count_rate_limit_mcps; + + + uint16_t max_count_rate_limit_mcps; + + + +} VL53L1_refspadchar_config_t; + + + + + + +typedef struct { + + uint16_t dss_config__target_total_rate_mcps; + + + uint32_t phasecal_config_timeout_us; + + + uint32_t mm_config_timeout_us; + + + uint32_t range_config_timeout_us; + + + uint8_t num_of_samples; + + + int16_t algo__crosstalk_extract_min_valid_range_mm; + + + int16_t algo__crosstalk_extract_max_valid_range_mm; + + + uint16_t algo__crosstalk_extract_max_valid_rate_kcps; + + + + uint16_t algo__crosstalk_extract_max_sigma_mm; + + + + + +} VL53L1_xtalkextract_config_t; + + + + + + +typedef struct { + + uint16_t dss_config__target_total_rate_mcps; + + + + uint32_t phasecal_config_timeout_us; + + + + uint32_t range_config_timeout_us; + + + + uint32_t mm_config_timeout_us; + + + + + uint8_t pre_num_of_samples; + + + + uint8_t mm1_num_of_samples; + + + + uint8_t mm2_num_of_samples; + + + + +} VL53L1_offsetcal_config_t; + + + + + + +typedef struct { + + uint16_t dss_config__target_total_rate_mcps; + + + + uint32_t phasecal_config_timeout_us; + + + + uint32_t mm_config_timeout_us; + + + + uint32_t range_config_timeout_us; + + + + uint16_t phasecal_num_of_samples; + + + + uint16_t zone_num_of_samples; + + + + +} VL53L1_zonecal_config_t; + + + + + + + +typedef struct { + + VL53L1_DeviceSscArray array_select; + + + + + uint8_t VL53L1_p_009; + + + uint8_t vcsel_start; + + + uint8_t vcsel_width; + + + uint32_t timeout_us; + + + uint16_t rate_limit_mcps; + + + + + +} VL53L1_ssc_config_t; + + + + + + +typedef struct { + + + uint32_t algo__crosstalk_compensation_plane_offset_kcps; + + + int16_t algo__crosstalk_compensation_x_plane_gradient_kcps; + + + int16_t algo__crosstalk_compensation_y_plane_gradient_kcps; + + + uint32_t nvm_default__crosstalk_compensation_plane_offset_kcps; + + + int16_t nvm_default__crosstalk_compensation_x_plane_gradient_kcps; + + + int16_t nvm_default__crosstalk_compensation_y_plane_gradient_kcps; + + + uint8_t global_crosstalk_compensation_enable; + + + int16_t histogram_mode_crosstalk_margin_kcps; + + + + + + + int16_t lite_mode_crosstalk_margin_kcps; + + + + + + + uint8_t crosstalk_range_ignore_threshold_mult; + + + uint16_t crosstalk_range_ignore_threshold_rate_mcps; + + + + + int16_t algo__crosstalk_detect_min_valid_range_mm; + + + int16_t algo__crosstalk_detect_max_valid_range_mm; + + + uint16_t algo__crosstalk_detect_max_valid_rate_kcps; + + + + uint16_t algo__crosstalk_detect_max_sigma_mm; + + + + + + +} VL53L1_xtalk_config_t; + + + + + + + + + + + + +typedef struct { + + + uint16_t tp_tuning_parm_version; + + + + uint16_t tp_tuning_parm_key_table_version; + + + + + uint16_t tp_tuning_parm_lld_version; + + + + + uint8_t tp_init_phase_rtn_lite_long; + + + + + uint8_t tp_init_phase_rtn_lite_med; + + + + + uint8_t tp_init_phase_rtn_lite_short; + + + + + uint8_t tp_init_phase_ref_lite_long; + + + + + uint8_t tp_init_phase_ref_lite_med; + + + + + uint8_t tp_init_phase_ref_lite_short; + + + + + + uint8_t tp_init_phase_rtn_hist_long; + + + + + uint8_t tp_init_phase_rtn_hist_med; + + + + + uint8_t tp_init_phase_rtn_hist_short; + + + + + uint8_t tp_init_phase_ref_hist_long; + + + + + uint8_t tp_init_phase_ref_hist_med; + + + + + uint8_t tp_init_phase_ref_hist_short; + + + + + + uint8_t tp_consistency_lite_phase_tolerance; + + + + + uint8_t tp_phasecal_target; + + + + + uint16_t tp_cal_repeat_rate; + + + + + uint8_t tp_lite_min_clip; + + + + + + uint16_t tp_lite_long_sigma_thresh_mm; + + + + + uint16_t tp_lite_med_sigma_thresh_mm; + + + + + uint16_t tp_lite_short_sigma_thresh_mm; + + + + + + uint16_t tp_lite_long_min_count_rate_rtn_mcps; + + + + + uint16_t tp_lite_med_min_count_rate_rtn_mcps; + + + + + uint16_t tp_lite_short_min_count_rate_rtn_mcps; + + + + + + uint8_t tp_lite_sigma_est_pulse_width_ns; + + + + uint8_t tp_lite_sigma_est_amb_width_ns; + + + + uint8_t tp_lite_sigma_ref_mm; + + + + uint8_t tp_lite_seed_cfg; + + + + uint8_t tp_timed_seed_cfg; + + + + + uint8_t tp_lite_quantifier; + + + + uint8_t tp_lite_first_order_select; + + + + + uint16_t tp_dss_target_lite_mcps; + + + + uint16_t tp_dss_target_histo_mcps; + + + + uint16_t tp_dss_target_histo_mz_mcps; + + + + uint16_t tp_dss_target_timed_mcps; + + + + uint16_t tp_dss_target_very_short_mcps; + + + + + uint32_t tp_phasecal_timeout_lite_us; + + + + uint32_t tp_phasecal_timeout_hist_long_us; + + + + uint32_t tp_phasecal_timeout_hist_med_us; + + + + uint32_t tp_phasecal_timeout_hist_short_us; + + + + + uint32_t tp_phasecal_timeout_mz_long_us; + + + + uint32_t tp_phasecal_timeout_mz_med_us; + + + + uint32_t tp_phasecal_timeout_mz_short_us; + + + + uint32_t tp_phasecal_timeout_timed_us; + + + + + uint32_t tp_mm_timeout_lite_us; + + + + uint32_t tp_mm_timeout_histo_us; + + + + uint32_t tp_mm_timeout_mz_us; + + + + uint32_t tp_mm_timeout_timed_us; + + + + uint32_t tp_mm_timeout_lpa_us; + + + + + uint32_t tp_range_timeout_lite_us; + + + + uint32_t tp_range_timeout_histo_us; + + + + uint32_t tp_range_timeout_mz_us; + + + + uint32_t tp_range_timeout_timed_us; + + + + uint32_t tp_range_timeout_lpa_us; + + + + +} VL53L1_tuning_parm_storage_t; + + + + + + + + +typedef struct { + + uint8_t x_centre; + + uint8_t y_centre; + + +} VL53L1_optical_centre_t; + + + + + + + +typedef struct { + + uint8_t x_centre; + + uint8_t y_centre; + + uint8_t width; + + uint8_t height; + + +} VL53L1_user_zone_t; + + + + + + + + + +typedef struct { + + uint8_t max_zones; + + uint8_t active_zones; + + + + + + + + + +VL53L1_histogram_config_t multizone_hist_cfg; + + VL53L1_user_zone_t user_zones[VL53L1_MAX_USER_ZONES]; + + + + uint8_t bin_config[VL53L1_MAX_USER_ZONES]; + + + +} VL53L1_zone_config_t; + + + + + + + + + +typedef struct { + + + + VL53L1_GPIO_Interrupt_Mode intr_mode_distance; + + + + VL53L1_GPIO_Interrupt_Mode intr_mode_rate; + + + + + + uint8_t intr_new_measure_ready; + + + + uint8_t intr_no_target; + + + + + + + uint8_t intr_combined_mode; + + + + + + + + + + + uint16_t threshold_distance_high; + + + + uint16_t threshold_distance_low; + + + + uint16_t threshold_rate_high; + + + + uint16_t threshold_rate_low; + +} VL53L1_GPIO_interrupt_config_t; + + + + + + + + + + + + +typedef struct { + + + + + + + uint8_t vhv_loop_bound; + + + + uint8_t is_low_power_auto_mode; + + + + + uint8_t low_power_auto_range_count; + + + + uint8_t saved_interrupt_config; + + + + uint8_t saved_vhv_init; + + + + uint8_t saved_vhv_timeout; + + + + uint8_t first_run_phasecal_result; + + + + uint32_t dss__total_rate_per_spad_mcps; + + + + uint16_t dss__required_spads; + +} VL53L1_low_power_auto_data_t; + + + + + + + + + + + + + + + +typedef struct { + + + + uint8_t smudge_corr_enabled; + + + + uint8_t smudge_corr_apply_enabled; + + + + uint8_t smudge_corr_single_apply; + + + + + + + uint16_t smudge_margin; + + + + uint32_t noise_margin; + + + + + uint32_t user_xtalk_offset_limit; + + + + + uint8_t user_xtalk_offset_limit_hi; + + + + + uint32_t sample_limit; + + + + + uint32_t single_xtalk_delta; + + + + + uint32_t averaged_xtalk_delta; + + + + + uint32_t smudge_corr_clip_limit; + + + + + uint32_t smudge_corr_ambient_threshold; + + + + + + + + + + + + + + + uint8_t scaler_calc_method; + + + + + + + int16_t x_gradient_scaler; + + + + + + + int16_t y_gradient_scaler; + + + + + + + uint8_t user_scaler_set; + + + + + uint32_t nodetect_ambient_threshold; + + + + + uint32_t nodetect_sample_limit; + + + + + uint32_t nodetect_xtalk_offset; + + + + + uint16_t nodetect_min_range_mm; + + +} VL53L1_smudge_corrector_config_t; + + + + + + + + + +typedef struct { + + + + uint32_t current_samples; + + + + uint32_t required_samples; + + + + uint64_t accumulator; + + + + uint32_t nodetect_counter; + +} VL53L1_smudge_corrector_internals_t; + + + + + + + + + +typedef struct { + + + + + + + + + uint8_t smudge_corr_valid; + + + + uint8_t smudge_corr_clipped; + + + + + + + + + uint8_t single_xtalk_delta_flag; + + + + + + + + + uint8_t averaged_xtalk_delta_flag; + + + + uint8_t sample_limit_exceeded_flag; + + + + + + + uint8_t gradient_zero_flag; + + + + uint8_t new_xtalk_applied_flag; + + + + uint32_t algo__crosstalk_compensation_plane_offset_kcps; + + + + int16_t algo__crosstalk_compensation_x_plane_gradient_kcps; + + + + int16_t algo__crosstalk_compensation_y_plane_gradient_kcps; + + +} VL53L1_smudge_corrector_data_t; + + + + + + + + + + + +typedef struct { + + + + + uint8_t range_id; + + + uint32_t time_stamp; + + + uint8_t VL53L1_p_015; + + + uint8_t VL53L1_p_022; + + + uint8_t VL53L1_p_025; + + + uint8_t VL53L1_p_026; + + + uint8_t VL53L1_p_016; + + + uint8_t VL53L1_p_027; + + + + uint16_t width; + + + uint8_t VL53L1_p_030; + + + + + uint16_t fast_osc_frequency; + + + uint16_t zero_distance_phase; + + + uint16_t VL53L1_p_006; + + + + uint32_t total_periods_elapsed; + + + + uint32_t peak_duration_us; + + + + uint32_t woi_duration_us; + + + + + + + + uint32_t VL53L1_p_020; + + + uint32_t VL53L1_p_021; + + + + int32_t VL53L1_p_013; + + + + + + + + uint16_t peak_signal_count_rate_mcps; + + + uint16_t avg_signal_count_rate_mcps; + + + uint16_t ambient_count_rate_mcps; + + + uint16_t total_rate_per_spad_mcps; + + + uint32_t VL53L1_p_012; + + + + + + + uint16_t VL53L1_p_005; + + + + + + + uint16_t VL53L1_p_028; + + + + uint16_t VL53L1_p_014; + + + uint16_t VL53L1_p_029; + + + + + + + + int16_t min_range_mm; + + + + + + int16_t median_range_mm; + + + + + int16_t max_range_mm; + + + + + + + + + + uint8_t range_status; + +} VL53L1_range_data_t; + + + + + + + + + + +typedef struct { + + VL53L1_DeviceState cfg_device_state; + + + VL53L1_DeviceState rd_device_state; + + + uint8_t zone_id; + + + uint8_t stream_count; + + + + int16_t VL53L1_p_007[VL53L1_MAX_AMBIENT_DMAX_VALUES]; + + + + + int16_t wrap_dmax_mm; + + + + uint8_t device_status; + + + + uint8_t max_results; + + + + uint8_t active_results; + + + VL53L1_range_data_t VL53L1_p_002[VL53L1_MAX_RANGE_RESULTS]; + + + VL53L1_range_data_t xmonitor; + + + VL53L1_smudge_corrector_data_t smudge_corrector_data; + + + + +} VL53L1_range_results_t; + + + + + + + + + + +typedef struct { + + uint8_t no_of_samples; + + + uint32_t rate_per_spad_kcps_sum; + + + + uint32_t rate_per_spad_kcps_avg; + + + int32_t signal_total_events_sum; + + + + int32_t signal_total_events_avg; + + + + uint32_t sigma_mm_sum; + + + + uint32_t sigma_mm_avg; + + + uint32_t median_phase_sum; + + + + + uint32_t median_phase_avg; + + + + +} VL53L1_xtalk_range_data_t; + + + + + + + + + + +typedef struct { + + VL53L1_Error cal_status; + + + uint8_t num_of_samples_status; + + + + + + + + + uint8_t zero_samples_status; + + + + + + + + + uint8_t max_sigma_status; + + + + + + + + + + + + + + + + + + + uint8_t max_results; + + + + uint8_t active_results; + + + + VL53L1_xtalk_range_data_t + VL53L1_p_002[VL53L1_MAX_XTALK_RANGE_RESULTS]; + + + VL53L1_histogram_bin_data_t central_histogram_sum; + + + + VL53L1_histogram_bin_data_t central_histogram_avg; + + + + uint8_t central_histogram__window_start; + + + + uint8_t central_histogram__window_end; + + + + VL53L1_histogram_bin_data_t + histogram_avg_1[VL53L1_MAX_XTALK_RANGE_RESULTS]; + + + + VL53L1_histogram_bin_data_t + histogram_avg_2[VL53L1_MAX_XTALK_RANGE_RESULTS]; + + + + VL53L1_histogram_bin_data_t + xtalk_avg[VL53L1_MAX_XTALK_RANGE_RESULTS]; + + + + +} VL53L1_xtalk_range_results_t; + + + + + + + + + + + +typedef struct { + + uint8_t preset_mode; + + + uint8_t dss_config__roi_mode_control; + + + uint16_t dss_config__manual_effective_spads_select; + + + uint8_t no_of_samples; + + + uint32_t effective_spads; + + + uint32_t peak_rate_mcps; + + + uint32_t VL53L1_p_005; + + + int32_t median_range_mm; + + + + int32_t range_mm_offset; + + + +} VL53L1_offset_range_data_t; + + + + + + + + + + +typedef struct { + + int16_t cal_distance_mm; + + + uint16_t cal_reflectance_pc; + + + VL53L1_Error cal_status; + + + uint8_t cal_report; + + + uint8_t max_results; + + + + uint8_t active_results; + + + VL53L1_offset_range_data_t + VL53L1_p_002[VL53L1_MAX_OFFSET_RANGE_RESULTS]; + + + +} VL53L1_offset_range_results_t; + + + + + + + + + + + + +typedef struct { + + uint16_t result__mm_inner_actual_effective_spads; + + + uint16_t result__mm_outer_actual_effective_spads; + + + uint16_t result__mm_inner_peak_signal_count_rtn_mcps; + + + uint16_t result__mm_outer_peak_signal_count_rtn_mcps; + + + +} VL53L1_additional_offset_cal_data_t; + + + + + + + + + + + + + +typedef struct { + + uint32_t VL53L1_p_020; + + + uint32_t VL53L1_p_021; + + + + uint16_t VL53L1_p_014; + + + uint8_t range_status; + + + +} VL53L1_object_data_t; + + + + + + + + + + +typedef struct { + + VL53L1_DeviceState cfg_device_state; + + + VL53L1_DeviceState rd_device_state; + + + uint8_t zone_id; + + + uint8_t stream_count; + + + uint8_t max_objects; + + + + uint8_t active_objects; + + + VL53L1_object_data_t VL53L1_p_002[VL53L1_MAX_RANGE_RESULTS]; + + + + VL53L1_object_data_t xmonitor; + + + +} VL53L1_zone_objects_t; + + + + + + + + + + + + + +typedef struct { + + uint8_t max_zones; + + + + uint8_t active_zones; + + + VL53L1_zone_objects_t VL53L1_p_002[VL53L1_MAX_USER_ZONES]; + + + +} VL53L1_zone_results_t; + + + + + + + + + +typedef struct { + + VL53L1_DeviceState rd_device_state; + + + + uint8_t number_of_ambient_bins; + + + + + uint16_t result__dss_actual_effective_spads; + + + uint8_t VL53L1_p_009; + + + uint32_t total_periods_elapsed; + + + + int32_t ambient_events_sum; + + + +} VL53L1_zone_hist_info_t; + + + + + + + + + + +typedef struct { + + uint8_t max_zones; + + + + uint8_t active_zones; + + + VL53L1_zone_hist_info_t VL53L1_p_002[VL53L1_MAX_USER_ZONES]; + + + +} VL53L1_zone_histograms_t; + + + + + + + + + +typedef struct { + + uint32_t no_of_samples; + + + uint32_t effective_spads; + + + uint32_t peak_rate_mcps; + + + uint32_t VL53L1_p_014; + + + uint32_t VL53L1_p_005; + + + + int32_t median_range_mm; + + + + int32_t range_mm_offset; + + + +} VL53L1_zone_calibration_data_t; + + + + + + + + + + + +typedef struct { + + uint32_t struct_version; + + + VL53L1_DevicePresetModes preset_mode; + + + VL53L1_DeviceZonePreset zone_preset; + + + int16_t cal_distance_mm; + + + uint16_t cal_reflectance_pc; + + + uint16_t phasecal_result__reference_phase; + + + uint16_t zero_distance_phase; + + + VL53L1_Error cal_status; + + + uint8_t max_zones; + + + + uint8_t active_zones; + + + VL53L1_zone_calibration_data_t VL53L1_p_002[VL53L1_MAX_USER_ZONES]; + + + +} VL53L1_zone_calibration_results_t; + + + + + + + + + + + + + +typedef struct { + + int16_t cal_distance_mm; + + + uint16_t cal_reflectance_pc; + + + uint16_t max_samples; + + + uint16_t width; + + + uint16_t height; + + + uint16_t peak_rate_mcps[VL53L1_NVM_PEAK_RATE_MAP_SAMPLES]; + + + +} VL53L1_cal_peak_rate_map_t; + + + + + + + + + +typedef struct { + + uint8_t expected_stream_count; + + + uint8_t expected_gph_id; + + + uint8_t dss_mode; + + + uint16_t dss_requested_effective_spad_count; + + + + uint8_t seed_cfg; + + + uint8_t initial_phase_seed; + + + + uint8_t roi_config__user_roi_centre_spad; + + + uint8_t roi_config__user_roi_requested_global_xy_size; + + + +} VL53L1_zone_private_dyn_cfg_t; + + + + + + + + + +typedef struct { + + uint8_t max_zones; + + + + uint8_t active_zones; + + + VL53L1_zone_private_dyn_cfg_t VL53L1_p_002[VL53L1_MAX_USER_ZONES]; + + + +} VL53L1_zone_private_dyn_cfgs_t; + + + + + + + + + +typedef struct { + + uint32_t algo__crosstalk_compensation_plane_offset_kcps; + + + int16_t algo__crosstalk_compensation_x_plane_gradient_kcps; + + + int16_t algo__crosstalk_compensation_y_plane_gradient_kcps; + + + +} VL53L1_xtalk_calibration_results_t; + + + + + + + + +typedef struct { + + + + uint32_t sample_count; + + + + uint32_t pll_period_mm; + + + + uint32_t peak_duration_us_sum; + + + + uint32_t effective_spad_count_sum; + + + + uint32_t zero_distance_phase_sum; + + + + uint32_t zero_distance_phase_avg; + + + + int32_t event_scaler_sum; + + + + int32_t event_scaler_avg; + + + + int32_t signal_events_sum; + + + + uint32_t xtalk_rate_kcps_per_spad; + + + + int32_t xtalk_start_phase; + + + + int32_t xtalk_end_phase; + + + + int32_t xtalk_width_phase; + + + + int32_t target_start_phase; + + + + int32_t target_end_phase; + + + + int32_t target_width_phase; + + + + int32_t effective_width; + + + + int32_t event_scaler; + + + + uint8_t VL53L1_p_015; + + + + uint8_t VL53L1_p_016; + + + + uint8_t target_start; + + + + int32_t max_shape_value; + + + + int32_t bin_data_sums[VL53L1_XTALK_HISTO_BINS]; + +} VL53L1_hist_xtalk_extract_data_t; + + + + + + + + + + +typedef struct { + + uint16_t standard_ranging_gain_factor; + + + uint16_t histogram_ranging_gain_factor; + + + +} VL53L1_gain_calibration_data_t; + + + + + + + + + + +typedef struct { + + VL53L1_DeviceState cfg_device_state; + + + uint8_t cfg_stream_count; + + + + uint8_t cfg_internal_stream_count; + + + uint8_t cfg_internal_stream_count_val; + + + uint8_t cfg_gph_id; + + + uint8_t cfg_timing_status; + + + uint8_t cfg_zone_id; + + + + VL53L1_DeviceState rd_device_state; + + + uint8_t rd_stream_count; + + + uint8_t rd_internal_stream_count; + + + uint8_t rd_internal_stream_count_val; + + + uint8_t rd_gph_id; + + + uint8_t rd_timing_status; + + + uint8_t rd_zone_id; + + + +} VL53L1_ll_driver_state_t; + + + + + + + + + + +typedef struct { + + uint8_t wait_method; + + + VL53L1_DevicePresetModes preset_mode; + + + VL53L1_DeviceZonePreset zone_preset; + + + VL53L1_DeviceMeasurementModes measurement_mode; + + + VL53L1_OffsetCalibrationMode offset_calibration_mode; + + + VL53L1_OffsetCorrectionMode offset_correction_mode; + + + VL53L1_DeviceDmaxMode dmax_mode; + + + uint32_t phasecal_config_timeout_us; + + + uint32_t mm_config_timeout_us; + + + uint32_t range_config_timeout_us; + + + uint32_t inter_measurement_period_ms; + + + uint16_t dss_config__target_total_rate_mcps; + + + + uint32_t fw_ready_poll_duration_ms; + + + uint8_t fw_ready; + + + uint8_t debug_mode; + + + + + + VL53L1_ll_version_t version; + + + + VL53L1_ll_driver_state_t ll_state; + + + + VL53L1_GPIO_interrupt_config_t gpio_interrupt_config; + + + + VL53L1_customer_nvm_managed_t customer; + VL53L1_cal_peak_rate_map_t cal_peak_rate_map; + VL53L1_additional_offset_cal_data_t add_off_cal_data; + VL53L1_dmax_calibration_data_t fmt_dmax_cal; + VL53L1_dmax_calibration_data_t cust_dmax_cal; + VL53L1_gain_calibration_data_t gain_cal; + VL53L1_user_zone_t mm_roi; + VL53L1_optical_centre_t optical_centre; + VL53L1_zone_config_t zone_cfg; + + + + VL53L1_tuning_parm_storage_t tuning_parms; + + + + uint8_t rtn_good_spads[VL53L1_RTN_SPAD_BUFFER_SIZE]; + + + + VL53L1_refspadchar_config_t refspadchar; + VL53L1_ssc_config_t ssc_cfg; + VL53L1_hist_post_process_config_t histpostprocess; + VL53L1_hist_gen3_dmax_config_t dmax_cfg; + VL53L1_xtalkextract_config_t xtalk_extract_cfg; + VL53L1_xtalk_config_t xtalk_cfg; + VL53L1_offsetcal_config_t offsetcal_cfg; + VL53L1_zonecal_config_t zonecal_cfg; + + + + VL53L1_static_nvm_managed_t stat_nvm; + VL53L1_histogram_config_t hist_cfg; + VL53L1_static_config_t stat_cfg; + VL53L1_general_config_t gen_cfg; + VL53L1_timing_config_t tim_cfg; + VL53L1_dynamic_config_t dyn_cfg; + VL53L1_system_control_t sys_ctrl; + VL53L1_system_results_t sys_results; + VL53L1_nvm_copy_data_t nvm_copy_data; + + + + VL53L1_histogram_bin_data_t hist_data; + VL53L1_histogram_bin_data_t hist_xtalk; + + + + VL53L1_xtalk_histogram_data_t xtalk_shapes; + VL53L1_xtalk_range_results_t xtalk_results; + VL53L1_xtalk_calibration_results_t xtalk_cal; + VL53L1_hist_xtalk_extract_data_t xtalk_extract; + + + + VL53L1_offset_range_results_t offset_results; + + + + VL53L1_core_results_t core_results; + VL53L1_debug_results_t dbg_results; + + VL53L1_smudge_corrector_config_t smudge_correct_config; + + + VL53L1_smudge_corrector_internals_t smudge_corrector_internals; + + + + + + + + VL53L1_low_power_auto_data_t low_power_auto_data; + + + +#ifdef PAL_EXTENDED + + + VL53L1_patch_results_t patch_results; + VL53L1_shadow_core_results_t shadow_core_results; + VL53L1_shadow_system_results_t shadow_sys_results; + VL53L1_prev_shadow_core_results_t prev_shadow_core_results; + VL53L1_prev_shadow_system_results_t prev_shadow_sys_results; +#endif + +} VL53L1_LLDriverData_t; + + + + + + + + + + +typedef struct { + + + + VL53L1_range_results_t range_results; + + + + VL53L1_zone_private_dyn_cfgs_t zone_dyn_cfgs; + + + + VL53L1_zone_results_t zone_results; + VL53L1_zone_histograms_t zone_hists; + VL53L1_zone_calibration_results_t zone_cal; + +} VL53L1_LLDriverResults_t; + + + + + + + + + + +typedef struct { + + uint32_t struct_version; + VL53L1_customer_nvm_managed_t customer; + VL53L1_dmax_calibration_data_t fmt_dmax_cal; + VL53L1_dmax_calibration_data_t cust_dmax_cal; + VL53L1_additional_offset_cal_data_t add_off_cal_data; + VL53L1_optical_centre_t optical_centre; + VL53L1_xtalk_histogram_data_t xtalkhisto; + VL53L1_gain_calibration_data_t gain_cal; + VL53L1_cal_peak_rate_map_t cal_peak_rate_map; + +} VL53L1_calibration_data_t; + + + + + + + + + + +typedef struct { + + VL53L1_customer_nvm_managed_t customer; + VL53L1_xtalkextract_config_t xtalk_extract_cfg; + VL53L1_xtalk_config_t xtalk_cfg; + VL53L1_histogram_bin_data_t hist_data; + VL53L1_xtalk_histogram_data_t xtalk_shapes; + VL53L1_xtalk_range_results_t xtalk_results; + +} VL53L1_xtalk_debug_data_t; + + + + + + + + + + +typedef struct { + + VL53L1_customer_nvm_managed_t customer; + VL53L1_dmax_calibration_data_t fmt_dmax_cal; + VL53L1_dmax_calibration_data_t cust_dmax_cal; + VL53L1_additional_offset_cal_data_t add_off_cal_data; + VL53L1_offset_range_results_t offset_results; + +} VL53L1_offset_debug_data_t; + + + + + + + + + + +typedef struct { + uint16_t vl53l1_tuningparm_version; + uint16_t vl53l1_tuningparm_key_table_version; + uint16_t vl53l1_tuningparm_lld_version; + uint8_t vl53l1_tuningparm_hist_algo_select; + uint8_t vl53l1_tuningparm_hist_target_order; + uint8_t vl53l1_tuningparm_hist_filter_woi_0; + uint8_t vl53l1_tuningparm_hist_filter_woi_1; + uint8_t vl53l1_tuningparm_hist_amb_est_method; + uint8_t vl53l1_tuningparm_hist_amb_thresh_sigma_0; + uint8_t vl53l1_tuningparm_hist_amb_thresh_sigma_1; + int32_t vl53l1_tuningparm_hist_min_amb_thresh_events; + uint16_t vl53l1_tuningparm_hist_amb_events_scaler; + uint16_t vl53l1_tuningparm_hist_noise_threshold; + int32_t vl53l1_tuningparm_hist_signal_total_events_limit; + uint8_t vl53l1_tuningparm_hist_sigma_est_ref_mm; + uint16_t vl53l1_tuningparm_hist_sigma_thresh_mm; + uint16_t vl53l1_tuningparm_hist_gain_factor; + uint8_t vl53l1_tuningparm_consistency_hist_phase_tolerance; + uint16_t vl53l1_tuningparm_consistency_hist_min_max_tolerance_mm; + uint8_t vl53l1_tuningparm_consistency_hist_event_sigma; + uint16_t vl53l1_tuningparm_consistency_hist_event_sigma_min_spad_limit; + uint8_t vl53l1_tuningparm_initial_phase_rtn_histo_long_range; + uint8_t vl53l1_tuningparm_initial_phase_rtn_histo_med_range; + uint8_t vl53l1_tuningparm_initial_phase_rtn_histo_short_range; + uint8_t vl53l1_tuningparm_initial_phase_ref_histo_long_range; + uint8_t vl53l1_tuningparm_initial_phase_ref_histo_med_range; + uint8_t vl53l1_tuningparm_initial_phase_ref_histo_short_range; + int16_t vl53l1_tuningparm_xtalk_detect_min_valid_range_mm; + int16_t vl53l1_tuningparm_xtalk_detect_max_valid_range_mm; + uint16_t vl53l1_tuningparm_xtalk_detect_max_sigma_mm; + uint16_t vl53l1_tuningparm_xtalk_detect_min_max_tolerance; + uint16_t vl53l1_tuningparm_xtalk_detect_max_valid_rate_kcps; + uint8_t vl53l1_tuningparm_xtalk_detect_event_sigma; + int16_t vl53l1_tuningparm_hist_xtalk_margin_kcps; + uint8_t vl53l1_tuningparm_consistency_lite_phase_tolerance; + uint8_t vl53l1_tuningparm_phasecal_target; + uint16_t vl53l1_tuningparm_lite_cal_repeat_rate; + uint16_t vl53l1_tuningparm_lite_ranging_gain_factor; + uint8_t vl53l1_tuningparm_lite_min_clip_mm; + uint16_t vl53l1_tuningparm_lite_long_sigma_thresh_mm; + uint16_t vl53l1_tuningparm_lite_med_sigma_thresh_mm; + uint16_t vl53l1_tuningparm_lite_short_sigma_thresh_mm; + uint16_t vl53l1_tuningparm_lite_long_min_count_rate_rtn_mcps; + uint16_t vl53l1_tuningparm_lite_med_min_count_rate_rtn_mcps; + uint16_t vl53l1_tuningparm_lite_short_min_count_rate_rtn_mcps; + uint8_t vl53l1_tuningparm_lite_sigma_est_pulse_width; + uint8_t vl53l1_tuningparm_lite_sigma_est_amb_width_ns; + uint8_t vl53l1_tuningparm_lite_sigma_ref_mm; + uint8_t vl53l1_tuningparm_lite_rit_mult; + uint8_t vl53l1_tuningparm_lite_seed_config; + uint8_t vl53l1_tuningparm_lite_quantifier; + uint8_t vl53l1_tuningparm_lite_first_order_select; + int16_t vl53l1_tuningparm_lite_xtalk_margin_kcps; + uint8_t vl53l1_tuningparm_initial_phase_rtn_lite_long_range; + uint8_t vl53l1_tuningparm_initial_phase_rtn_lite_med_range; + uint8_t vl53l1_tuningparm_initial_phase_rtn_lite_short_range; + uint8_t vl53l1_tuningparm_initial_phase_ref_lite_long_range; + uint8_t vl53l1_tuningparm_initial_phase_ref_lite_med_range; + uint8_t vl53l1_tuningparm_initial_phase_ref_lite_short_range; + uint8_t vl53l1_tuningparm_timed_seed_config; + uint8_t vl53l1_tuningparm_dmax_cfg_signal_thresh_sigma; + uint16_t vl53l1_tuningparm_dmax_cfg_reflectance_array_0; + uint16_t vl53l1_tuningparm_dmax_cfg_reflectance_array_1; + uint16_t vl53l1_tuningparm_dmax_cfg_reflectance_array_2; + uint16_t vl53l1_tuningparm_dmax_cfg_reflectance_array_3; + uint16_t vl53l1_tuningparm_dmax_cfg_reflectance_array_4; + uint8_t vl53l1_tuningparm_vhv_loopbound; + uint8_t vl53l1_tuningparm_refspadchar_device_test_mode; + uint8_t vl53l1_tuningparm_refspadchar_vcsel_period; + uint32_t vl53l1_tuningparm_refspadchar_phasecal_timeout_us; + uint16_t vl53l1_tuningparm_refspadchar_target_count_rate_mcps; + uint16_t vl53l1_tuningparm_refspadchar_min_countrate_limit_mcps; + uint16_t vl53l1_tuningparm_refspadchar_max_countrate_limit_mcps; + uint8_t vl53l1_tuningparm_xtalk_extract_num_of_samples; + int16_t vl53l1_tuningparm_xtalk_extract_min_filter_thresh_mm; + int16_t vl53l1_tuningparm_xtalk_extract_max_filter_thresh_mm; + uint16_t vl53l1_tuningparm_xtalk_extract_dss_rate_mcps; + uint32_t vl53l1_tuningparm_xtalk_extract_phasecal_timeout_us; + uint16_t vl53l1_tuningparm_xtalk_extract_max_valid_rate_kcps; + uint16_t vl53l1_tuningparm_xtalk_extract_sigma_threshold_mm; + uint32_t vl53l1_tuningparm_xtalk_extract_dss_timeout_us; + uint32_t vl53l1_tuningparm_xtalk_extract_bin_timeout_us; + uint16_t vl53l1_tuningparm_offset_cal_dss_rate_mcps; + uint32_t vl53l1_tuningparm_offset_cal_phasecal_timeout_us; + uint32_t vl53l1_tuningparm_offset_cal_mm_timeout_us; + uint32_t vl53l1_tuningparm_offset_cal_range_timeout_us; + uint8_t vl53l1_tuningparm_offset_cal_pre_samples; + uint8_t vl53l1_tuningparm_offset_cal_mm1_samples; + uint8_t vl53l1_tuningparm_offset_cal_mm2_samples; + uint16_t vl53l1_tuningparm_zone_cal_dss_rate_mcps; + uint32_t vl53l1_tuningparm_zone_cal_phasecal_timeout_us; + uint32_t vl53l1_tuningparm_zone_cal_dss_timeout_us; + uint16_t vl53l1_tuningparm_zone_cal_phasecal_num_samples; + uint32_t vl53l1_tuningparm_zone_cal_range_timeout_us; + uint16_t vl53l1_tuningparm_zone_cal_zone_num_samples; + uint8_t vl53l1_tuningparm_spadmap_vcsel_period; + uint8_t vl53l1_tuningparm_spadmap_vcsel_start; + uint16_t vl53l1_tuningparm_spadmap_rate_limit_mcps; + uint16_t vl53l1_tuningparm_lite_dss_config_target_total_rate_mcps; + uint16_t vl53l1_tuningparm_ranging_dss_config_target_total_rate_mcps; + uint16_t vl53l1_tuningparm_mz_dss_config_target_total_rate_mcps; + uint16_t vl53l1_tuningparm_timed_dss_config_target_total_rate_mcps; + uint32_t vl53l1_tuningparm_lite_phasecal_config_timeout_us; + uint32_t vl53l1_tuningparm_ranging_long_phasecal_config_timeout_us; + uint32_t vl53l1_tuningparm_ranging_med_phasecal_config_timeout_us; + uint32_t vl53l1_tuningparm_ranging_short_phasecal_config_timeout_us; + uint32_t vl53l1_tuningparm_mz_long_phasecal_config_timeout_us; + uint32_t vl53l1_tuningparm_mz_med_phasecal_config_timeout_us; + uint32_t vl53l1_tuningparm_mz_short_phasecal_config_timeout_us; + uint32_t vl53l1_tuningparm_timed_phasecal_config_timeout_us; + uint32_t vl53l1_tuningparm_lite_mm_config_timeout_us; + uint32_t vl53l1_tuningparm_ranging_mm_config_timeout_us; + uint32_t vl53l1_tuningparm_mz_mm_config_timeout_us; + uint32_t vl53l1_tuningparm_timed_mm_config_timeout_us; + uint32_t vl53l1_tuningparm_lite_range_config_timeout_us; + uint32_t vl53l1_tuningparm_ranging_range_config_timeout_us; + uint32_t vl53l1_tuningparm_mz_range_config_timeout_us; + uint32_t vl53l1_tuningparm_timed_range_config_timeout_us; + uint16_t vl53l1_tuningparm_dynxtalk_smudge_margin; + uint32_t vl53l1_tuningparm_dynxtalk_noise_margin; + uint32_t vl53l1_tuningparm_dynxtalk_xtalk_offset_limit; + uint8_t vl53l1_tuningparm_dynxtalk_xtalk_offset_limit_hi; + uint32_t vl53l1_tuningparm_dynxtalk_sample_limit; + uint32_t vl53l1_tuningparm_dynxtalk_single_xtalk_delta; + uint32_t vl53l1_tuningparm_dynxtalk_averaged_xtalk_delta; + uint32_t vl53l1_tuningparm_dynxtalk_clip_limit; + uint8_t vl53l1_tuningparm_dynxtalk_scaler_calc_method; + int16_t vl53l1_tuningparm_dynxtalk_xgradient_scaler; + int16_t vl53l1_tuningparm_dynxtalk_ygradient_scaler; + uint8_t vl53l1_tuningparm_dynxtalk_user_scaler_set; + uint8_t vl53l1_tuningparm_dynxtalk_smudge_cor_single_apply; + uint32_t vl53l1_tuningparm_dynxtalk_xtalk_amb_threshold; + uint32_t vl53l1_tuningparm_dynxtalk_nodetect_amb_threshold_kcps; + uint32_t vl53l1_tuningparm_dynxtalk_nodetect_sample_limit; + uint32_t vl53l1_tuningparm_dynxtalk_nodetect_xtalk_offset_kcps; + uint16_t vl53l1_tuningparm_dynxtalk_nodetect_min_range_mm; + uint8_t vl53l1_tuningparm_lowpowerauto_vhv_loop_bound; + uint32_t vl53l1_tuningparm_lowpowerauto_mm_config_timeout_us; + uint32_t vl53l1_tuningparm_lowpowerauto_range_config_timeout_us; + uint16_t vl53l1_tuningparm_very_short_dss_rate_mcps; +} VL53L1_tuning_parameters_t; + + + + + + + + + + + + +typedef struct { + + uint16_t target_reflectance_for_dmax[VL53L1_MAX_AMBIENT_DMAX_VALUES]; + +} VL53L1_dmax_reflectance_array_t; + + + + + + + + + + + + + + +typedef struct { + + uint8_t spad_type; + + + uint16_t VL53L1_p_023; + + + uint16_t rate_data[VL53L1_NO_OF_SPAD_ENABLES]; + + + uint16_t no_of_values; + + + uint8_t fractional_bits; + + + uint8_t error_status; + + + +} VL53L1_spad_rate_data_t; + + + + + + + + + + + + + + +typedef struct { + + VL53L1_DevicePresetModes preset_mode; + + + VL53L1_DeviceZonePreset zone_preset; + + + VL53L1_DeviceMeasurementModes measurement_mode; + + + VL53L1_OffsetCalibrationMode offset_calibration_mode; + + + VL53L1_OffsetCorrectionMode offset_correction_mode; + + + VL53L1_DeviceDmaxMode dmax_mode; + + + + uint32_t phasecal_config_timeout_us; + + + uint32_t mm_config_timeout_us; + + + uint32_t range_config_timeout_us; + + + uint32_t inter_measurement_period_ms; + + + uint16_t dss_config__target_total_rate_mcps; + + + + VL53L1_histogram_bin_data_t VL53L1_p_010; + + + +} VL53L1_additional_data_t; + + + + + + + + + + +#define SUPPRESS_UNUSED_WARNING(x) \ + ((void) (x)) + + +#define IGNORE_STATUS(__FUNCTION_ID__, __ERROR_STATUS_CHECK__, __STATUS__) \ + do { \ + DISABLE_WARNINGS(); \ + if (__FUNCTION_ID__) { \ + if (__STATUS__ == __ERROR_STATUS_CHECK__) { \ + __STATUS__ = VL53L1_ERROR_NONE; \ + WARN_OVERRIDE_STATUS(__FUNCTION_ID__); \ + } \ + } \ + ENABLE_WARNINGS(); \ + } \ + while (0) + +#define VL53L1_COPYSTRING(str, ...) \ + (strncpy(str, ##__VA_ARGS__, VL53L1_MAX_STRING_LENGTH-1)) + +#ifdef __cplusplus +} +#endif + +#endif + + + + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_ll_device.h b/drivers/input/misc/vl53L1/inc/vl53l1_ll_device.h new file mode 100644 index 000000000000..900c7df606cb --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_ll_device.h @@ -0,0 +1,1286 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_LL_DEVICE_H_ +#define _VL53L1_LL_DEVICE_H_ + +#include "vl53l1_types.h" +#include "vl53l1_platform_user_config.h" + +#define VL53L1_I2C 0x01 +#define VL53L1_SPI 0x00 + + + + + + + + + + + + + +typedef uint8_t VL53L1_WaitMethod; + +#define VL53L1_WAIT_METHOD_BLOCKING ((VL53L1_WaitMethod) 0) +#define VL53L1_WAIT_METHOD_NON_BLOCKING ((VL53L1_WaitMethod) 1) + + + + + + + + + + +typedef uint8_t VL53L1_DeviceState; + +#define VL53L1_DEVICESTATE_POWERDOWN ((VL53L1_DeviceState) 0) +#define VL53L1_DEVICESTATE_HW_STANDBY ((VL53L1_DeviceState) 1) +#define VL53L1_DEVICESTATE_FW_COLDBOOT ((VL53L1_DeviceState) 2) +#define VL53L1_DEVICESTATE_SW_STANDBY ((VL53L1_DeviceState) 3) +#define VL53L1_DEVICESTATE_RANGING_DSS_AUTO ((VL53L1_DeviceState) 4) +#define VL53L1_DEVICESTATE_RANGING_DSS_MANUAL ((VL53L1_DeviceState) 5) +#define VL53L1_DEVICESTATE_RANGING_WAIT_GPH_SYNC ((VL53L1_DeviceState) 6) +#define VL53L1_DEVICESTATE_RANGING_GATHER_DATA ((VL53L1_DeviceState) 7) +#define VL53L1_DEVICESTATE_RANGING_OUTPUT_DATA ((VL53L1_DeviceState) 8) + +#define VL53L1_DEVICESTATE_UNKNOWN ((VL53L1_DeviceState) 98) +#define VL53L1_DEVICESTATE_ERROR ((VL53L1_DeviceState) 99) + + + + + + + + + + + +typedef uint8_t VL53L1_DeviceZonePreset; + +#define VL53L1_DEVICEZONEPRESET_NONE \ + ((VL53L1_DeviceZonePreset) 0) + +#define VL53L1_DEVICEZONEPRESET_XTALK_PLANAR \ + ((VL53L1_DeviceZonePreset) 1) +#define VL53L1_DEVICEZONEPRESET_1X1_SIZE_16X16 \ + ((VL53L1_DeviceZonePreset) 2) +#define VL53L1_DEVICEZONEPRESET_1X2_SIZE_16X8 \ + ((VL53L1_DeviceZonePreset) 3) +#define VL53L1_DEVICEZONEPRESET_2X1_SIZE_8X16 \ + ((VL53L1_DeviceZonePreset) 4) +#define VL53L1_DEVICEZONEPRESET_2X2_SIZE_8X8 \ + ((VL53L1_DeviceZonePreset) 5) +#define VL53L1_DEVICEZONEPRESET_3X3_SIZE_5X5 \ + ((VL53L1_DeviceZonePreset) 6) +#define VL53L1_DEVICEZONEPRESET_4X4_SIZE_4X4 \ + ((VL53L1_DeviceZonePreset) 7) +#define VL53L1_DEVICEZONEPRESET_5X5_SIZE_4X4 \ + ((VL53L1_DeviceZonePreset) 8) +#define VL53L1_DEVICEZONEPRESET_11X11_SIZE_5X5 \ + ((VL53L1_DeviceZonePreset) 9) +#define VL53L1_DEVICEZONEPRESET_13X13_SIZE_4X4 \ + ((VL53L1_DeviceZonePreset) 10) + +#define VL53L1_DEVICEZONEPRESET_1X1_SIZE_4X4_POS_8X8 \ + ((VL53L1_DeviceZonePreset) 11) + +#define VL53L1_DEVICEZONEPRESET_CUSTOM \ + ((VL53L1_DeviceZonePreset) 255) + + + + + + + + + + + +typedef uint8_t VL53L1_DevicePresetModes; + +#define VL53L1_DEVICEPRESETMODE_NONE \ + ((VL53L1_DevicePresetModes) 0) +#define VL53L1_DEVICEPRESETMODE_STANDARD_RANGING \ + ((VL53L1_DevicePresetModes) 1) +#define VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_SHORT_RANGE \ + ((VL53L1_DevicePresetModes) 2) +#define VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_LONG_RANGE \ + ((VL53L1_DevicePresetModes) 3) +#define VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_MM1_CAL \ + ((VL53L1_DevicePresetModes) 4) +#define VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_MM2_CAL \ + ((VL53L1_DevicePresetModes) 5) +#define VL53L1_DEVICEPRESETMODE_TIMED_RANGING \ + ((VL53L1_DevicePresetModes) 6) +#define VL53L1_DEVICEPRESETMODE_TIMED_RANGING_SHORT_RANGE \ + ((VL53L1_DevicePresetModes) 7) +#define VL53L1_DEVICEPRESETMODE_TIMED_RANGING_LONG_RANGE \ + ((VL53L1_DevicePresetModes) 8) +#define VL53L1_DEVICEPRESETMODE_NEAR_FARRANGING \ + ((VL53L1_DevicePresetModes) 9) +#define VL53L1_DEVICEPRESETMODE_QUADRANT_RANGING \ + ((VL53L1_DevicePresetModes) 10) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING \ + ((VL53L1_DevicePresetModes) 11) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_SHORT_TIMING \ + ((VL53L1_DevicePresetModes) 12) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_CHARACTERISATION \ + ((VL53L1_DevicePresetModes) 13) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_XTALK_PLANAR \ + ((VL53L1_DevicePresetModes) 14) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_XTALK_MM1 \ + ((VL53L1_DevicePresetModes) 15) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_XTALK_MM2 \ + ((VL53L1_DevicePresetModes) 16) +#define VL53L1_DEVICEPRESETMODE_OLT \ + ((VL53L1_DevicePresetModes) 17) +#define VL53L1_DEVICEPRESETMODE_SINGLESHOT_RANGING \ + ((VL53L1_DevicePresetModes) 18) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_REF_ARRAY \ + ((VL53L1_DevicePresetModes) 19) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_WITH_MM1 \ + ((VL53L1_DevicePresetModes) 20) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_WITH_MM2 \ + ((VL53L1_DevicePresetModes) 21) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_MM1_CAL \ + ((VL53L1_DevicePresetModes) 22) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_MM2_CAL \ + ((VL53L1_DevicePresetModes) 23) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE \ + ((VL53L1_DevicePresetModes) 24) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE_SHORT_RANGE \ + ((VL53L1_DevicePresetModes) 25) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE_LONG_RANGE \ + ((VL53L1_DevicePresetModes) 26) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE \ + ((VL53L1_DevicePresetModes) 27) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE_MM1 \ + ((VL53L1_DevicePresetModes) 28) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE_MM2 \ + ((VL53L1_DevicePresetModes) 29) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE \ + ((VL53L1_DevicePresetModes) 30) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE_MM1 \ + ((VL53L1_DevicePresetModes) 31) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE_MM2 \ + ((VL53L1_DevicePresetModes) 32) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE \ + ((VL53L1_DevicePresetModes) 33) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE_MM1 \ + ((VL53L1_DevicePresetModes) 34) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE_MM2 \ + ((VL53L1_DevicePresetModes) 35) +#define VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_SHORT_RANGE \ + ((VL53L1_DevicePresetModes) 36) +#define VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_MEDIUM_RANGE \ + ((VL53L1_DevicePresetModes) 37) +#define VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_LONG_RANGE \ + ((VL53L1_DevicePresetModes) 38) +#define VL53L1_DEVICEPRESETMODE_SPECIAL_HISTOGRAM_SHORT_RANGE \ + ((VL53L1_DevicePresetModes) 39) + + + + + + + + + + + +typedef uint8_t VL53L1_DeviceMeasurementModes; + +#define VL53L1_DEVICEMEASUREMENTMODE_STOP \ + ((VL53L1_DeviceMeasurementModes) 0x00) +#define VL53L1_DEVICEMEASUREMENTMODE_SINGLESHOT \ + ((VL53L1_DeviceMeasurementModes) 0x10) +#define VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK \ + ((VL53L1_DeviceMeasurementModes) 0x20) +#define VL53L1_DEVICEMEASUREMENTMODE_TIMED \ + ((VL53L1_DeviceMeasurementModes) 0x40) +#define VL53L1_DEVICEMEASUREMENTMODE_ABORT \ + ((VL53L1_DeviceMeasurementModes) 0x80) + + + + + + + + + + + +typedef uint8_t VL53L1_OffsetCalibrationMode; + +#define VL53L1_OFFSETCALIBRATIONMODE__NONE \ + ((VL53L1_OffsetCalibrationMode) 0) +#define VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD \ + ((VL53L1_OffsetCalibrationMode) 1) +#define VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__HISTOGRAM \ + ((VL53L1_OffsetCalibrationMode) 2) +#define VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD_PRE_RANGE_ONLY \ + ((VL53L1_OffsetCalibrationMode) 3) +#define VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__HISTOGRAM_PRE_RANGE_ONLY \ + ((VL53L1_OffsetCalibrationMode) 4) +#define VL53L1_OFFSETCALIBRATIONMODE__PER_ZONE \ + ((VL53L1_OffsetCalibrationMode) 5) + + + + + + + + + + + +typedef uint8_t VL53L1_OffsetCorrectionMode; + +#define VL53L1_OFFSETCORRECTIONMODE__NONE \ + ((VL53L1_OffsetCorrectionMode) 0) +#define VL53L1_OFFSETCORRECTIONMODE__MM1_MM2_OFFSETS \ + ((VL53L1_OffsetCorrectionMode) 1) +#define VL53L1_OFFSETCORRECTIONMODE__PER_ZONE_OFFSETS \ + ((VL53L1_OffsetCorrectionMode) 2) + + + + + + + + + + + +typedef uint8_t VL53L1_DeviceDmaxMode; + +#define VL53L1_DEVICEDMAXMODE__NONE \ + ((VL53L1_DeviceDmaxMode) 0) +#define VL53L1_DEVICEDMAXMODE__FMT_CAL_DATA \ + ((VL53L1_DeviceDmaxMode) 1) +#define VL53L1_DEVICEDMAXMODE__CUST_CAL_DATA \ + ((VL53L1_DeviceDmaxMode) 2) +#define VL53L1_DEVICEDMAXMODE__PER_ZONE_CAL_DATA \ + ((VL53L1_DeviceDmaxMode) 3) + + + + + + + + + + + + +typedef uint8_t VL53L1_DeviceSequenceConfig; + +#define VL53L1_DEVICESEQUENCECONFIG_VHV \ + ((VL53L1_DeviceSequenceConfig) 0) +#define VL53L1_DEVICESEQUENCECONFIG_PHASECAL \ + ((VL53L1_DeviceSequenceConfig) 1) +#define VL53L1_DEVICESEQUENCECONFIG_REFERENCE_PHASE \ + ((VL53L1_DeviceSequenceConfig) 2) +#define VL53L1_DEVICESEQUENCECONFIG_DSS1 \ + ((VL53L1_DeviceSequenceConfig) 3) +#define VL53L1_DEVICESEQUENCECONFIG_DSS2 \ + ((VL53L1_DeviceSequenceConfig) 4) +#define VL53L1_DEVICESEQUENCECONFIG_MM1 \ + ((VL53L1_DeviceSequenceConfig) 5) +#define VL53L1_DEVICESEQUENCECONFIG_MM2 \ + ((VL53L1_DeviceSequenceConfig) 6) +#define VL53L1_DEVICESEQUENCECONFIG_RANGE \ + ((VL53L1_DeviceSequenceConfig) 7) + + + + + + + + + + + +typedef uint8_t VL53L1_DeviceInterruptPolarity; + +#define VL53L1_DEVICEINTERRUPTPOLARITY_ACTIVE_HIGH \ + ((VL53L1_DeviceInterruptPolarity) 0x00) +#define VL53L1_DEVICEINTERRUPTPOLARITY_ACTIVE_LOW \ + ((VL53L1_DeviceInterruptPolarity) 0x10) +#define VL53L1_DEVICEINTERRUPTPOLARITY_BIT_MASK \ + ((VL53L1_DeviceInterruptPolarity) 0x10) +#define VL53L1_DEVICEINTERRUPTPOLARITY_CLEAR_MASK \ + ((VL53L1_DeviceInterruptPolarity) 0xEF) + + + + + + + + + + + +typedef uint8_t VL53L1_DeviceGpioMode; + +#define VL53L1_DEVICEGPIOMODE_OUTPUT_CONSTANT_ZERO \ + ((VL53L1_DeviceGpioMode) 0x00) +#define VL53L1_DEVICEGPIOMODE_OUTPUT_RANGE_AND_ERROR_INTERRUPTS \ + ((VL53L1_DeviceGpioMode) 0x01) +#define VL53L1_DEVICEGPIOMODE_OUTPUT_TIMIER_INTERRUPTS \ + ((VL53L1_DeviceGpioMode) 0x02) +#define VL53L1_DEVICEGPIOMODE_OUTPUT_RANGE_MODE_INTERRUPT_STATUS \ + ((VL53L1_DeviceGpioMode) 0x03) +#define VL53L1_DEVICEGPIOMODE_OUTPUT_SLOW_OSCILLATOR_CLOCK \ + ((VL53L1_DeviceGpioMode) 0x04) +#define VL53L1_DEVICEGPIOMODE_BIT_MASK \ + ((VL53L1_DeviceGpioMode) 0x0F) +#define VL53L1_DEVICEGPIOMODE_CLEAR_MASK \ + ((VL53L1_DeviceGpioMode) 0xF0) + + + + + + + + + + + + + + + +typedef uint8_t VL53L1_DeviceError; + +#define VL53L1_DEVICEERROR_NOUPDATE \ + ((VL53L1_DeviceError) 0) + + +#define VL53L1_DEVICEERROR_VCSELCONTINUITYTESTFAILURE \ + ((VL53L1_DeviceError) 1) +#define VL53L1_DEVICEERROR_VCSELWATCHDOGTESTFAILURE \ + ((VL53L1_DeviceError) 2) +#define VL53L1_DEVICEERROR_NOVHVVALUEFOUND \ + ((VL53L1_DeviceError) 3) +#define VL53L1_DEVICEERROR_MSRCNOTARGET \ + ((VL53L1_DeviceError) 4) +#define VL53L1_DEVICEERROR_RANGEPHASECHECK \ + ((VL53L1_DeviceError) 5) +#define VL53L1_DEVICEERROR_SIGMATHRESHOLDCHECK \ + ((VL53L1_DeviceError) 6) +#define VL53L1_DEVICEERROR_PHASECONSISTENCY \ + ((VL53L1_DeviceError) 7) +#define VL53L1_DEVICEERROR_MINCLIP \ + ((VL53L1_DeviceError) 8) +#define VL53L1_DEVICEERROR_RANGECOMPLETE \ + ((VL53L1_DeviceError) 9) +#define VL53L1_DEVICEERROR_ALGOUNDERFLOW \ + ((VL53L1_DeviceError) 10) +#define VL53L1_DEVICEERROR_ALGOOVERFLOW \ + ((VL53L1_DeviceError) 11) +#define VL53L1_DEVICEERROR_RANGEIGNORETHRESHOLD \ + ((VL53L1_DeviceError) 12) +#define VL53L1_DEVICEERROR_USERROICLIP \ + ((VL53L1_DeviceError) 13) +#define VL53L1_DEVICEERROR_REFSPADCHARNOTENOUGHDPADS \ + ((VL53L1_DeviceError) 14) +#define VL53L1_DEVICEERROR_REFSPADCHARMORETHANTARGET \ + ((VL53L1_DeviceError) 15) +#define VL53L1_DEVICEERROR_REFSPADCHARLESSTHANTARGET \ + ((VL53L1_DeviceError) 16) +#define VL53L1_DEVICEERROR_MULTCLIPFAIL \ + ((VL53L1_DeviceError) 17) +#define VL53L1_DEVICEERROR_GPHSTREAMCOUNT0READY \ + ((VL53L1_DeviceError) 18) +#define VL53L1_DEVICEERROR_RANGECOMPLETE_NO_WRAP_CHECK \ + ((VL53L1_DeviceError) 19) +#define VL53L1_DEVICEERROR_EVENTCONSISTENCY \ + ((VL53L1_DeviceError) 20) +#define VL53L1_DEVICEERROR_MINSIGNALEVENTCHECK \ + ((VL53L1_DeviceError) 21) +#define VL53L1_DEVICEERROR_RANGECOMPLETE_MERGED_PULSE \ + ((VL53L1_DeviceError) 22) + + + +#define VL53L1_DEVICEERROR_PREV_RANGE_NO_TARGETS \ + ((VL53L1_DeviceError) 23) + + + + + + + + + + + +typedef uint8_t VL53L1_DeviceReportStatus; + +#define VL53L1_DEVICEREPORTSTATUS_NOUPDATE \ + ((VL53L1_DeviceReportStatus) 0) + + +#define VL53L1_DEVICEREPORTSTATUS_ROI_SETUP \ + ((VL53L1_DeviceReportStatus) 1) +#define VL53L1_DEVICEREPORTSTATUS_VHV \ + ((VL53L1_DeviceReportStatus) 2) +#define VL53L1_DEVICEREPORTSTATUS_PHASECAL \ + ((VL53L1_DeviceReportStatus) 3) +#define VL53L1_DEVICEREPORTSTATUS_REFERENCE_PHASE \ + ((VL53L1_DeviceReportStatus) 4) +#define VL53L1_DEVICEREPORTSTATUS_DSS1 \ + ((VL53L1_DeviceReportStatus) 5) +#define VL53L1_DEVICEREPORTSTATUS_DSS2 \ + ((VL53L1_DeviceReportStatus) 6) +#define VL53L1_DEVICEREPORTSTATUS_MM1 \ + ((VL53L1_DeviceReportStatus) 7) +#define VL53L1_DEVICEREPORTSTATUS_MM2 \ + ((VL53L1_DeviceReportStatus) 8) +#define VL53L1_DEVICEREPORTSTATUS_RANGE \ + ((VL53L1_DeviceReportStatus) 9) +#define VL53L1_DEVICEREPORTSTATUS_HISTOGRAM \ + ((VL53L1_DeviceReportStatus) 10) + + + + + + + + + + +typedef uint8_t VL53L1_DeviceDssMode; + +#define VL53L1_DEVICEDSSMODE__DISABLED \ + ((VL53L1_DeviceDssMode) 0) +#define VL53L1_DEVICEDSSMODE__TARGET_RATE \ + ((VL53L1_DeviceDssMode) 1) +#define VL53L1_DEVICEDSSMODE__REQUESTED_EFFFECTIVE_SPADS \ + ((VL53L1_DeviceDssMode) 2) +#define VL53L1_DEVICEDSSMODE__BLOCK_SELECT \ + ((VL53L1_DeviceDssMode) 3) + + + + + + + + + + + + +typedef uint8_t VL53L1_HistAlgoSelect; + +#define VL53L1_HIST_ALGO_SELECT__PW_HIST_GEN1 \ + ((VL53L1_HistAlgoSelect) 1) +#define VL53L1_HIST_ALGO_SELECT__PW_HIST_GEN2 \ + ((VL53L1_HistAlgoSelect) 2) +#define VL53L1_HIST_ALGO_SELECT__PW_HIST_GEN3 \ + ((VL53L1_HistAlgoSelect) 3) +#define VL53L1_HIST_ALGO_SELECT__PW_HIST_GEN4 \ + ((VL53L1_HistAlgoSelect) 4) + + + + + + + + + + + +typedef uint8_t VL53L1_HistTargetOrder; + +#define VL53L1_HIST_TARGET_ORDER__INCREASING_DISTANCE \ + ((VL53L1_HistTargetOrder) 1) +#define VL53L1_HIST_TARGET_ORDER__STRONGEST_FIRST \ + ((VL53L1_HistTargetOrder) 2) + + + + + + + + + + + +typedef uint8_t VL53L1_HistAmbEstMethod; + +#define VL53L1_HIST_AMB_EST_METHOD__AMBIENT_BINS \ + ((VL53L1_HistAmbEstMethod) 1) +#define VL53L1_HIST_AMB_EST_METHOD__THRESHOLDED_BINS \ + ((VL53L1_HistAmbEstMethod) 2) + + + + + + + + + + + + +typedef uint8_t VL53L1_HistXtalkCompEnable; + +#define VL53L1_HIST_XTALK_COMP__DIS \ + ((VL53L1_HistXtalkCompEnable) 0) +#define VL53L1_HIST_XTALK_COMP__EN \ + ((VL53L1_HistXtalkCompEnable) 1) + + + + + + + + + +typedef uint8_t VL53L1_DeviceConfigLevel; + +#define VL53L1_DEVICECONFIGLEVEL_SYSTEM_CONTROL \ + ((VL53L1_DeviceConfigLevel) 0) + + +#define VL53L1_DEVICECONFIGLEVEL_DYNAMIC_ONWARDS \ + ((VL53L1_DeviceConfigLevel) 1) + + +#define VL53L1_DEVICECONFIGLEVEL_TIMING_ONWARDS \ + ((VL53L1_DeviceConfigLevel) 2) + + + +#define VL53L1_DEVICECONFIGLEVEL_GENERAL_ONWARDS \ + ((VL53L1_DeviceConfigLevel) 3) + + + +#define VL53L1_DEVICECONFIGLEVEL_STATIC_ONWARDS \ + ((VL53L1_DeviceConfigLevel) 4) + + + +#define VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS \ + ((VL53L1_DeviceConfigLevel) 5) + + + +#define VL53L1_DEVICECONFIGLEVEL_FULL \ + ((VL53L1_DeviceConfigLevel) 6) + + + + + + + + + + + + + + +typedef uint8_t VL53L1_DeviceResultsLevel; + +#define VL53L1_DEVICERESULTSLEVEL_SYSTEM_RESULTS \ + ((VL53L1_DeviceResultsLevel) 0) + + +#define VL53L1_DEVICERESULTSLEVEL_UPTO_CORE \ + ((VL53L1_DeviceResultsLevel) 1) + + +#define VL53L1_DEVICERESULTSLEVEL_FULL \ + ((VL53L1_DeviceResultsLevel) 2) + + + + + + + + + + + + + + + +typedef uint8_t VL53L1_DeviceTestMode; + +#define VL53L1_DEVICETESTMODE_NONE \ + ((VL53L1_DeviceTestMode) 0x00) + + +#define VL53L1_DEVICETESTMODE_NVM_ZERO \ + ((VL53L1_DeviceTestMode) 0x01) + + +#define VL53L1_DEVICETESTMODE_NVM_COPY \ + ((VL53L1_DeviceTestMode) 0x02) + + +#define VL53L1_DEVICETESTMODE_PATCH \ + ((VL53L1_DeviceTestMode) 0x03) + + +#define VL53L1_DEVICETESTMODE_DCR \ + ((VL53L1_DeviceTestMode) 0x04) + + +#define VL53L1_DEVICETESTMODE_LCR_VCSEL_OFF \ + ((VL53L1_DeviceTestMode) 0x05) + + + +#define VL53L1_DEVICETESTMODE_LCR_VCSEL_ON \ + ((VL53L1_DeviceTestMode) 0x06) + + + +#define VL53L1_DEVICETESTMODE_SPOT_CENTRE_LOCATE \ + ((VL53L1_DeviceTestMode) 0x07) + + +#define VL53L1_DEVICETESTMODE_REF_SPAD_CHAR_WITH_PRE_VHV \ + ((VL53L1_DeviceTestMode) 0x08) + + +#define VL53L1_DEVICETESTMODE_REF_SPAD_CHAR_ONLY \ + ((VL53L1_DeviceTestMode) 0x09) + + + + + + + + + + + + + +typedef uint8_t VL53L1_DeviceSscArray; + +#define VL53L1_DEVICESSCARRAY_RTN ((VL53L1_DeviceSscArray) 0x00) + + +#define VL53L1_DEVICETESTMODE_REF ((VL53L1_DeviceSscArray) 0x01) + + + + + + + + + + + + + +#define VL53L1_RETURN_ARRAY_ONLY 0x01 + + +#define VL53L1_REFERENCE_ARRAY_ONLY 0x10 + + +#define VL53L1_BOTH_RETURN_AND_REFERENCE_ARRAYS 0x11 + + +#define VL53L1_NEITHER_RETURN_AND_REFERENCE_ARRAYS 0x00 + + + + + + + + + + + + +#define VL53L1_DEVICEINTERRUPTLEVEL_ACTIVE_HIGH 0x00 + + +#define VL53L1_DEVICEINTERRUPTLEVEL_ACTIVE_LOW 0x10 + + +#define VL53L1_DEVICEINTERRUPTLEVEL_ACTIVE_MASK 0x10 + + + + + + + + + + + + +#define VL53L1_POLLING_DELAY_US 1000 + + +#define VL53L1_SOFTWARE_RESET_DURATION_US 100 + + +#define VL53L1_FIRMWARE_BOOT_TIME_US 1200 + + + +#define VL53L1_ENABLE_POWERFORCE_SETTLING_TIME_US 250 + + + + +#define VL53L1_SPAD_ARRAY_WIDTH 16 + + +#define VL53L1_SPAD_ARRAY_HEIGHT 16 + + +#define VL53L1_NVM_SIZE_IN_BYTES 512 + + +#define VL53L1_NO_OF_SPAD_ENABLES 256 + + +#define VL53L1_RTN_SPAD_BUFFER_SIZE 32 + + +#define VL53L1_REF_SPAD_BUFFER_SIZE 6 + + +#define VL53L1_AMBIENT_WINDOW_VCSEL_PERIODS 256 + + +#define VL53L1_RANGING_WINDOW_VCSEL_PERIODS 2048 + + +#define VL53L1_MACRO_PERIOD_VCSEL_PERIODS \ + (VL53L1_AMBIENT_WINDOW_VCSEL_PERIODS + \ + VL53L1_RANGING_WINDOW_VCSEL_PERIODS) + + +#define VL53L1_MAX_ALLOWED_PHASE 0xFFFF + + + +#define VL53L1_RTN_SPAD_UNITY_TRANSMISSION 0x0100 + + +#define VL53L1_RTN_SPAD_APERTURE_TRANSMISSION 0x0038 + + + + + +#define VL53L1_SPAD_TOTAL_COUNT_MAX ((0x01 << 29) - 1) + + +#define VL53L1_SPAD_TOTAL_COUNT_RES_THRES (0x01 << 24) + + +#define VL53L1_COUNT_RATE_INTERNAL_MAX ((0x01 << 24) - 1) + + +#define VL53L1_SPEED_OF_LIGHT_IN_AIR 299704 + + +#define VL53L1_SPEED_OF_LIGHT_IN_AIR_DIV_8 (299704 >> 3) + + + + + + + + + + + + + + + + +typedef uint8_t VL53L1_ZoneConfig_BinConfig_select; + +#define VL53L1_ZONECONFIG_BINCONFIG__LOWAMB \ + ((VL53L1_ZoneConfig_BinConfig_select) 1) +#define VL53L1_ZONECONFIG_BINCONFIG__MIDAMB \ + ((VL53L1_ZoneConfig_BinConfig_select) 2) +#define VL53L1_ZONECONFIG_BINCONFIG__HIGHAMB \ + ((VL53L1_ZoneConfig_BinConfig_select) 3) + + + + + + + + + + +typedef uint8_t VL53L1_GPIO_Interrupt_Mode; + +#define VL53L1_GPIOINTMODE_LEVEL_LOW \ + ((VL53L1_GPIO_Interrupt_Mode) 0) + + +#define VL53L1_GPIOINTMODE_LEVEL_HIGH \ + ((VL53L1_GPIO_Interrupt_Mode) 1) + + +#define VL53L1_GPIOINTMODE_OUT_OF_WINDOW \ + ((VL53L1_GPIO_Interrupt_Mode) 2) + + +#define VL53L1_GPIOINTMODE_IN_WINDOW \ + ((VL53L1_GPIO_Interrupt_Mode) 3) + + + + + + + + + + + + + +typedef uint16_t VL53L1_TuningParms; + +#define VL53L1_TUNINGPARMS_LLD_PUBLIC_MIN_ADDRESS \ + ((VL53L1_TuningParms) VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS) +#define VL53L1_TUNINGPARMS_LLD_PUBLIC_MAX_ADDRESS \ + ((VL53L1_TuningParms) VL53L1_TUNINGPARM_VERY_SHORT_DSS_RATE_MCPS) + +#define VL53L1_TUNINGPARMS_LLD_PRIVATE_MIN_ADDRESS \ + ((VL53L1_TuningParms) VL53L1_TUNINGPARM_PRIVATE_PAGE_BASE_ADDRESS) +#define VL53L1_TUNINGPARMS_LLD_PRIVATE_MAX_ADDRESS \ + ((VL53L1_TuningParms) VL53L1_TUNINGPARMS_LLD_PRIVATE_MIN_ADDRESS) + +#define VL53L1_TUNINGPARM_VERSION \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 0)) +#define VL53L1_TUNINGPARM_KEY_TABLE_VERSION \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 1)) +#define VL53L1_TUNINGPARM_LLD_VERSION \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 2)) +#define VL53L1_TUNINGPARM_HIST_ALGO_SELECT \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 3)) +#define VL53L1_TUNINGPARM_HIST_TARGET_ORDER \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 4)) +#define VL53L1_TUNINGPARM_HIST_FILTER_WOI_0 \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 5)) +#define VL53L1_TUNINGPARM_HIST_FILTER_WOI_1 \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 6)) +#define VL53L1_TUNINGPARM_HIST_AMB_EST_METHOD \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 7)) +#define VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_0 \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 8)) +#define VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_1 \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 9)) +#define VL53L1_TUNINGPARM_HIST_MIN_AMB_THRESH_EVENTS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 10)) +#define VL53L1_TUNINGPARM_HIST_AMB_EVENTS_SCALER \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 11)) +#define VL53L1_TUNINGPARM_HIST_NOISE_THRESHOLD \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 12)) +#define VL53L1_TUNINGPARM_HIST_SIGNAL_TOTAL_EVENTS_LIMIT \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 13)) +#define VL53L1_TUNINGPARM_HIST_SIGMA_EST_REF_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 14)) +#define VL53L1_TUNINGPARM_HIST_SIGMA_THRESH_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 15)) +#define VL53L1_TUNINGPARM_HIST_GAIN_FACTOR \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 16)) +#define VL53L1_TUNINGPARM_CONSISTENCY_HIST_PHASE_TOLERANCE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 17)) +#define VL53L1_TUNINGPARM_CONSISTENCY_HIST_MIN_MAX_TOLERANCE_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 18)) +#define VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 19)) +#define VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA_MIN_SPAD_LIMIT \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 20)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_LONG_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 21)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_MED_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 22)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_SHORT_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 23)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_LONG_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 24)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_MED_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 25)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_SHORT_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 26)) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MIN_VALID_RANGE_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 27)) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RANGE_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 28)) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MAX_SIGMA_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 29)) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MIN_MAX_TOLERANCE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 30)) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RATE_KCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 31)) +#define VL53L1_TUNINGPARM_XTALK_DETECT_EVENT_SIGMA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 32)) +#define VL53L1_TUNINGPARM_HIST_XTALK_MARGIN_KCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 33)) +#define VL53L1_TUNINGPARM_CONSISTENCY_LITE_PHASE_TOLERANCE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 34)) +#define VL53L1_TUNINGPARM_PHASECAL_TARGET \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 35)) +#define VL53L1_TUNINGPARM_LITE_CAL_REPEAT_RATE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 36)) +#define VL53L1_TUNINGPARM_LITE_RANGING_GAIN_FACTOR \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 37)) +#define VL53L1_TUNINGPARM_LITE_MIN_CLIP_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 38)) +#define VL53L1_TUNINGPARM_LITE_LONG_SIGMA_THRESH_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 39)) +#define VL53L1_TUNINGPARM_LITE_MED_SIGMA_THRESH_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 40)) +#define VL53L1_TUNINGPARM_LITE_SHORT_SIGMA_THRESH_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 41)) +#define VL53L1_TUNINGPARM_LITE_LONG_MIN_COUNT_RATE_RTN_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 42)) +#define VL53L1_TUNINGPARM_LITE_MED_MIN_COUNT_RATE_RTN_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 43)) +#define VL53L1_TUNINGPARM_LITE_SHORT_MIN_COUNT_RATE_RTN_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 44)) +#define VL53L1_TUNINGPARM_LITE_SIGMA_EST_PULSE_WIDTH \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 45)) +#define VL53L1_TUNINGPARM_LITE_SIGMA_EST_AMB_WIDTH_NS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 46)) +#define VL53L1_TUNINGPARM_LITE_SIGMA_REF_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 47)) +#define VL53L1_TUNINGPARM_LITE_RIT_MULT \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 48)) +#define VL53L1_TUNINGPARM_LITE_SEED_CONFIG \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 49)) +#define VL53L1_TUNINGPARM_LITE_QUANTIFIER \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 50)) +#define VL53L1_TUNINGPARM_LITE_FIRST_ORDER_SELECT \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 51)) +#define VL53L1_TUNINGPARM_LITE_XTALK_MARGIN_KCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 52)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_LONG_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 53)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_MED_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 54)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_SHORT_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 55)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_LONG_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 56)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_MED_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 57)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_SHORT_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 58)) +#define VL53L1_TUNINGPARM_TIMED_SEED_CONFIG \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 59)) +#define VL53L1_TUNINGPARM_DMAX_CFG_SIGNAL_THRESH_SIGMA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 60)) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_0 \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 61)) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_1 \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 62)) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_2 \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 63)) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_3 \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 64)) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_4 \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 65)) +#define VL53L1_TUNINGPARM_VHV_LOOPBOUND \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 66)) +#define VL53L1_TUNINGPARM_REFSPADCHAR_DEVICE_TEST_MODE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 67)) +#define VL53L1_TUNINGPARM_REFSPADCHAR_VCSEL_PERIOD \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 68)) +#define VL53L1_TUNINGPARM_REFSPADCHAR_PHASECAL_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 69)) +#define VL53L1_TUNINGPARM_REFSPADCHAR_TARGET_COUNT_RATE_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 70)) +#define VL53L1_TUNINGPARM_REFSPADCHAR_MIN_COUNTRATE_LIMIT_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 71)) +#define VL53L1_TUNINGPARM_REFSPADCHAR_MAX_COUNTRATE_LIMIT_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 72)) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_NUM_OF_SAMPLES \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 73)) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_MIN_FILTER_THRESH_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 74)) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_FILTER_THRESH_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 75)) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_RATE_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 76)) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_PHASECAL_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 77)) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_VALID_RATE_KCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 78)) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_SIGMA_THRESHOLD_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 79)) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 80)) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_BIN_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 81)) +#define VL53L1_TUNINGPARM_OFFSET_CAL_DSS_RATE_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 82)) +#define VL53L1_TUNINGPARM_OFFSET_CAL_PHASECAL_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 83)) +#define VL53L1_TUNINGPARM_OFFSET_CAL_MM_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 84)) +#define VL53L1_TUNINGPARM_OFFSET_CAL_RANGE_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 85)) +#define VL53L1_TUNINGPARM_OFFSET_CAL_PRE_SAMPLES \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 86)) +#define VL53L1_TUNINGPARM_OFFSET_CAL_MM1_SAMPLES \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 87)) +#define VL53L1_TUNINGPARM_OFFSET_CAL_MM2_SAMPLES \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 88)) +#define VL53L1_TUNINGPARM_ZONE_CAL_DSS_RATE_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 89)) +#define VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 90)) +#define VL53L1_TUNINGPARM_ZONE_CAL_DSS_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 91)) +#define VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_NUM_SAMPLES \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 92)) +#define VL53L1_TUNINGPARM_ZONE_CAL_RANGE_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 93)) +#define VL53L1_TUNINGPARM_ZONE_CAL_ZONE_NUM_SAMPLES \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 94)) +#define VL53L1_TUNINGPARM_SPADMAP_VCSEL_PERIOD \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 95)) +#define VL53L1_TUNINGPARM_SPADMAP_VCSEL_START \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 96)) +#define VL53L1_TUNINGPARM_SPADMAP_RATE_LIMIT_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 97)) +#define VL53L1_TUNINGPARM_LITE_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 98)) +#define VL53L1_TUNINGPARM_RANGING_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 99)) +#define VL53L1_TUNINGPARM_MZ_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 100)) +#define VL53L1_TUNINGPARM_TIMED_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 101)) +#define VL53L1_TUNINGPARM_LITE_PHASECAL_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 102)) +#define VL53L1_TUNINGPARM_RANGING_LONG_PHASECAL_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 103)) +#define VL53L1_TUNINGPARM_RANGING_MED_PHASECAL_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 104)) +#define VL53L1_TUNINGPARM_RANGING_SHORT_PHASECAL_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 105)) +#define VL53L1_TUNINGPARM_MZ_LONG_PHASECAL_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 106)) +#define VL53L1_TUNINGPARM_MZ_MED_PHASECAL_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 107)) +#define VL53L1_TUNINGPARM_MZ_SHORT_PHASECAL_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 108)) +#define VL53L1_TUNINGPARM_TIMED_PHASECAL_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 109)) +#define VL53L1_TUNINGPARM_LITE_MM_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 110)) +#define VL53L1_TUNINGPARM_RANGING_MM_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 111)) +#define VL53L1_TUNINGPARM_MZ_MM_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 112)) +#define VL53L1_TUNINGPARM_TIMED_MM_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 113)) +#define VL53L1_TUNINGPARM_LITE_RANGE_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 114)) +#define VL53L1_TUNINGPARM_RANGING_RANGE_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 115)) +#define VL53L1_TUNINGPARM_MZ_RANGE_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 116)) +#define VL53L1_TUNINGPARM_TIMED_RANGE_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 117)) +#define VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_MARGIN \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 118)) +#define VL53L1_TUNINGPARM_DYNXTALK_NOISE_MARGIN \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 119)) +#define VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 120)) +#define VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT_HI \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 121)) +#define VL53L1_TUNINGPARM_DYNXTALK_SAMPLE_LIMIT \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 122)) +#define VL53L1_TUNINGPARM_DYNXTALK_SINGLE_XTALK_DELTA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 123)) +#define VL53L1_TUNINGPARM_DYNXTALK_AVERAGED_XTALK_DELTA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 124)) +#define VL53L1_TUNINGPARM_DYNXTALK_CLIP_LIMIT \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 125)) +#define VL53L1_TUNINGPARM_DYNXTALK_SCALER_CALC_METHOD \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 126)) +#define VL53L1_TUNINGPARM_DYNXTALK_XGRADIENT_SCALER \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 127)) +#define VL53L1_TUNINGPARM_DYNXTALK_YGRADIENT_SCALER \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 128)) +#define VL53L1_TUNINGPARM_DYNXTALK_USER_SCALER_SET \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 129)) +#define VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_COR_SINGLE_APPLY \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 130)) +#define VL53L1_TUNINGPARM_DYNXTALK_XTALK_AMB_THRESHOLD \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 131)) +#define VL53L1_TUNINGPARM_DYNXTALK_NODETECT_AMB_THRESHOLD_KCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 132)) +#define VL53L1_TUNINGPARM_DYNXTALK_NODETECT_SAMPLE_LIMIT \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 133)) +#define VL53L1_TUNINGPARM_DYNXTALK_NODETECT_XTALK_OFFSET_KCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 134)) +#define VL53L1_TUNINGPARM_DYNXTALK_NODETECT_MIN_RANGE_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 135)) +#define VL53L1_TUNINGPARM_LOWPOWERAUTO_VHV_LOOP_BOUND \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 136)) +#define VL53L1_TUNINGPARM_LOWPOWERAUTO_MM_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 137)) +#define VL53L1_TUNINGPARM_LOWPOWERAUTO_RANGE_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 138)) +#define VL53L1_TUNINGPARM_VERY_SHORT_DSS_RATE_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 139)) + + + + + + +#endif + + + + + + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_nvm.h b/drivers/input/misc/vl53L1/inc/vl53l1_nvm.h new file mode 100644 index 000000000000..53ccad1c112e --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_nvm.h @@ -0,0 +1,459 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_NVM_H_ +#define _VL53L1_NVM_H_ + +#include "vl53l1_ll_def.h" +#include "vl53l1_platform.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define VL53L1_NVM_POWER_UP_DELAY_US 50 +#define VL53L1_NVM_READ_TRIGGER_DELAY_US 5 + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_nvm_enable( + VL53L1_DEV Dev, + uint16_t nvm_ctrl_pulse_width, + int32_t nvm_power_up_delay_us); + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_nvm_read( + VL53L1_DEV Dev, + uint8_t start_address, + uint8_t count, + uint8_t *pdata); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_nvm_disable( + VL53L1_DEV Dev); + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_nvm_format_decode( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_decoded_nvm_data_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_nvm_decode_optical_centre( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_optical_centre_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_nvm_decode_cal_peak_rate_map( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_cal_peak_rate_map_t *pdata); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_nvm_decode_additional_offset_cal_data( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_additional_offset_cal_data_t *pdata); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_nvm_decode_fmt_range_results_data( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_decoded_nvm_fmt_range_data_t *pdata); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_nvm_decode_fmt_info( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_decoded_nvm_fmt_info_t *pdata); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_nvm_decode_ews_info( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_decoded_nvm_ews_info_t *pdata); + + + + + + + + + + + + + + +void VL53L1_nvm_format_encode( + VL53L1_decoded_nvm_data_t *pnvm_info, + uint8_t *pnvm_data); + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_read_nvm_raw_data( + VL53L1_DEV Dev, + uint8_t start_address, + uint8_t count, + uint8_t *pnvm_raw_data); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_read_nvm( + VL53L1_DEV Dev, + uint8_t nvm_format, + VL53L1_decoded_nvm_data_t *pnvm_info); + + + + + + + + + + + + + +VL53L1_Error VL53L1_read_nvm_optical_centre( + VL53L1_DEV Dev, + VL53L1_optical_centre_t *pcentre); + + + + + + + + + + + + + +VL53L1_Error VL53L1_read_nvm_cal_peak_rate_map( + VL53L1_DEV Dev, + VL53L1_cal_peak_rate_map_t *pcal_data); + + + + + + + + + + + + + +VL53L1_Error VL53L1_read_nvm_additional_offset_cal_data( + VL53L1_DEV Dev, + VL53L1_additional_offset_cal_data_t *pcal_data); + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_read_nvm_fmt_range_results_data( + VL53L1_DEV Dev, + uint16_t range_results_select, + VL53L1_decoded_nvm_fmt_range_data_t *prange_data); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_nvm_debug.h b/drivers/input/misc/vl53L1/inc/vl53l1_nvm_debug.h new file mode 100644 index 000000000000..0524c0c169f5 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_nvm_debug.h @@ -0,0 +1,202 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_NVM_DEBUG_H_ +#define _VL53L1_NVM_DEBUG_H_ + +#include "vl53l1_ll_def.h" +#include "vl53l1_nvm_structs.h" + + + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef VL53L1_LOG_ENABLE + + + + + + + + + + + + +void VL53L1_print_nvm_raw_data( + uint8_t *pnvm_raw_data, + uint32_t trace_flags); + + + + + + + + + + + + +void VL53L1_print_decoded_nvm_data( + VL53L1_decoded_nvm_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + +void VL53L1_print_decoded_nvm_fmt_range_data( + VL53L1_decoded_nvm_fmt_range_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + + +void VL53L1_print_decoded_nvm_fmt_info( + VL53L1_decoded_nvm_fmt_info_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + + + + + + + + +void VL53L1_print_decoded_nvm_ews_info( + VL53L1_decoded_nvm_ews_info_t *pdata, + char *pprefix, + uint32_t trace_flags); + +#endif + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_nvm_map.h b/drivers/input/misc/vl53L1/inc/vl53l1_nvm_map.h new file mode 100644 index 000000000000..15b5e1276665 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_nvm_map.h @@ -0,0 +1,3254 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_NVM_MAP_H_ +#define _VL53L1_NVM_MAP_H_ + + +#ifdef __cplusplus +extern "C" +{ +#endif + + + + + + + +#define VL53L1_NVM__IDENTIFICATION__MODEL_ID 0x0008 + + + + + + + + + + + + + + + +#define VL53L1_NVM__IDENTIFICATION__MODULE_TYPE 0x000C + + + + + + + + + + + + + + + +#define VL53L1_NVM__IDENTIFICATION__REVISION_ID 0x000D + + + + + + + + + + + + + + + +#define VL53L1_NVM__IDENTIFICATION__MODULE_ID 0x000E + + + + + + + + + + + + + + + +#define VL53L1_NVM__I2C_VALID 0x0010 + + + + + + + + + + + + + + + +#define VL53L1_NVM__I2C_SLAVE__DEVICE_ADDRESS 0x0011 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__OSC_MEASURED__FAST_OSC_FREQUENCY 0x0014 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__FAST_OSC_TRIM_MAX 0x0016 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__FAST_OSC_FREQ_SET 0x0017 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SLOW_OSC_CALIBRATION 0x0018 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__OSC_MEASURED__FAST_OSC_FREQUENCY 0x001C + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__FAST_OSC_TRIM_MAX 0x001E + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__FAST_OSC_FREQ_SET 0x001F + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SLOW_OSC_CALIBRATION 0x0020 + + + + + + + + + + + + + + + +#define VL53L1_NVM__VHV_CONFIG_UNLOCK 0x0028 + + + + + + + + + + + + + + + +#define VL53L1_NVM__REF_SELVDDPIX 0x0029 + + + + + + + + + + + + + + + +#define VL53L1_NVM__REF_SELVQUENCH 0x002A + + + + + + + + + + + + + + + +#define VL53L1_NVM__REGAVDD1V2_SEL_REGDVDD1V2_SEL 0x002B + + + + + + + + + + + + + + + + +#define VL53L1_NVM__VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND 0x002C + + + + + + + + + + + + + + + + +#define VL53L1_NVM__VHV_CONFIG__COUNT_THRESH 0x002D + + + + + + + + + + + + + + + +#define VL53L1_NVM__VHV_CONFIG__OFFSET 0x002E + + + + + + + + + + + + + + + +#define VL53L1_NVM__VHV_CONFIG__INIT 0x002F + + + + + + + + + + + + + + + + +#define VL53L1_NVM__LASER_SAFETY__VCSEL_TRIM_LL 0x0030 + + + + + + + + + + + + + + + +#define VL53L1_NVM__LASER_SAFETY__VCSEL_SELION_LL 0x0031 + + + + + + + + + + + + + + + +#define VL53L1_NVM__LASER_SAFETY__VCSEL_SELION_MAX_LL 0x0032 + + + + + + + + + + + + + + + +#define VL53L1_NVM__LASER_SAFETY__MULT_LL 0x0034 + + + + + + + + + + + + + + + +#define VL53L1_NVM__LASER_SAFETY__CLIP_LL 0x0035 + + + + + + + + + + + + + + + +#define VL53L1_NVM__LASER_SAFETY__VCSEL_TRIM_LD 0x0038 + + + + + + + + + + + + + + + +#define VL53L1_NVM__LASER_SAFETY__VCSEL_SELION_LD 0x0039 + + + + + + + + + + + + + + + +#define VL53L1_NVM__LASER_SAFETY__VCSEL_SELION_MAX_LD 0x003A + + + + + + + + + + + + + + + +#define VL53L1_NVM__LASER_SAFETY__MULT_LD 0x003C + + + + + + + + + + + + + + + +#define VL53L1_NVM__LASER_SAFETY__CLIP_LD 0x003D + + + + + + + + + + + + + + + +#define VL53L1_NVM__LASER_SAFETY_LOCK_BYTE 0x0040 + + + + + + + + + + + + + + + +#define VL53L1_NVM__LASER_SAFETY_UNLOCK_BYTE 0x0044 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_0_ 0x0048 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_1_ 0x0049 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_2_ 0x004A + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_3_ 0x004B + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_4_ 0x004C + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_5_ 0x004D + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_6_ 0x004E + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_7_ 0x004F + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_8_ 0x0050 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_9_ 0x0051 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_10_ 0x0052 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_11_ 0x0053 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_12_ 0x0054 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_13_ 0x0055 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_14_ 0x0056 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_15_ 0x0057 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_16_ 0x0058 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_17_ 0x0059 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_18_ 0x005A + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_19_ 0x005B + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_20_ 0x005C + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_21_ 0x005D + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_22_ 0x005E + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_23_ 0x005F + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_24_ 0x0060 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_25_ 0x0061 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_26_ 0x0062 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_27_ 0x0063 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_28_ 0x0064 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_29_ 0x0065 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_30_ 0x0066 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_31_ 0x0067 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC1_0_ 0x0068 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC1_1_ 0x0069 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC1_2_ 0x006A + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC1_3_ 0x006B + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC1_4_ 0x006C + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC1_5_ 0x006D + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC2_0_ 0x0070 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC2_1_ 0x0071 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC2_2_ 0x0072 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC2_3_ 0x0073 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC2_4_ 0x0074 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC2_5_ 0x0075 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC3_0_ 0x0078 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC3_1_ 0x0079 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC3_2_ 0x007A + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC3_3_ 0x007B + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC3_4_ 0x007C + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC3_5_ 0x007D + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_0_ 0x0080 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_1_ 0x0081 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_2_ 0x0082 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_3_ 0x0083 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_4_ 0x0084 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_5_ 0x0085 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_6_ 0x0086 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_7_ 0x0087 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_8_ 0x0088 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_9_ 0x0089 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_10_ 0x008A + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_11_ 0x008B + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_12_ 0x008C + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_13_ 0x008D + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_14_ 0x008E + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_15_ 0x008F + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_16_ 0x0090 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_17_ 0x0091 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_18_ 0x0092 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_19_ 0x0093 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_20_ 0x0094 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_21_ 0x0095 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_22_ 0x0096 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_23_ 0x0097 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_24_ 0x0098 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_25_ 0x0099 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_26_ 0x009A + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_27_ 0x009B + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_28_ 0x009C + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_29_ 0x009D + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_30_ 0x009E + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_31_ 0x009F + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC1_0_ 0x00A0 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC1_1_ 0x00A1 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC1_2_ 0x00A2 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC1_3_ 0x00A3 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC1_4_ 0x00A4 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC1_5_ 0x00A5 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC2_0_ 0x00A8 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC2_1_ 0x00A9 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC2_2_ 0x00AA + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC2_3_ 0x00AB + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC2_4_ 0x00AC + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC2_5_ 0x00AD + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC3_0_ 0x00B0 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC3_1_ 0x00B1 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC3_2_ 0x00B2 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC3_3_ 0x00B3 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC3_4_ 0x00B4 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC3_5_ 0x00B5 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__ROI_CONFIG__MODE_ROI_CENTRE_SPAD 0x00B8 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__ROI_CONFIG__MODE_ROI_XY_SIZE 0x00B9 + + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__REF_SPAD_APPLY__NUM_REQUESTED_REF_SPAD 0x00BC + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__REF_SPAD_MAN__REF_LOCATION 0x00BD + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__MM_CONFIG__INNER_OFFSET_MM 0x00C0 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__MM_CONFIG__OUTER_OFFSET_MM 0x00C2 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__ALGO__PART_TO_PART_RANGE_OFFSET_MM 0x00C4 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS 0x00C8 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS \ + 0x00CA + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS \ + 0x00CC + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPARE_HOST_CONFIG__NVM_CONFIG_SPARE_0 0x00CE + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SPARE_HOST_CONFIG__NVM_CONFIG_SPARE_1 0x00CF + + + + + + + + + + + + + + + +#define VL53L1_NVM__CUSTOMER_NVM_SPACE_PROGRAMMED 0x00E0 + + + + + + + + + + + + + + + +#define VL53L1_NVM__CUST__I2C_SLAVE__DEVICE_ADDRESS 0x00E4 + + + + + + + + + + + + + + + +#define VL53L1_NVM__CUST__REF_SPAD_APPLY__NUM_REQUESTED_REF_SPAD 0x00E8 + + + + + + + + + + + + + + + +#define VL53L1_NVM__CUST__REF_SPAD_MAN__REF_LOCATION 0x00E9 + + + + + + + + + + + + + + + +#define VL53L1_NVM__CUST__MM_CONFIG__INNER_OFFSET_MM 0x00EC + + + + + + + + + + + + + + + +#define VL53L1_NVM__CUST__MM_CONFIG__OUTER_OFFSET_MM 0x00EE + + + + + + + + + + + + + + + +#define VL53L1_NVM__CUST__ALGO__PART_TO_PART_RANGE_OFFSET_MM 0x00F0 + + + + + + + + + + + + + + + +#define VL53L1_NVM__CUST__ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS 0x00F4 + + + + + + + + + + + + + + + +#define VL53L1_NVM__CUST__ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS \ + 0x00F6 + + + + + + + + + + + + + + + +#define VL53L1_NVM__CUST__ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS \ + 0x00F8 + + + + + + + + + + + + + + + +#define VL53L1_NVM__CUST__SPARE_HOST_CONFIG__NVM_CONFIG_SPARE_0 0x00FA + + + + + + + + + + + + + + + +#define VL53L1_NVM__CUST__SPARE_HOST_CONFIG__NVM_CONFIG_SPARE_1 0x00FB + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__FGC__BYTE_0 0x01DC + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__FGC__BYTE_1 0x01DD + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__FGC__BYTE_2 0x01DE + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__FGC__BYTE_3 0x01DF + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__FGC__BYTE_4 0x01E0 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__FGC__BYTE_5 0x01E1 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__FGC__BYTE_6 0x01E2 + + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__FGC__BYTE_7 0x01E3 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__FGC__BYTE_8 0x01E4 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__FGC__BYTE_9 0x01E5 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__FGC__BYTE_10 0x01E6 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__FGC__BYTE_11 0x01E7 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__FGC__BYTE_12 0x01E8 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__FGC__BYTE_13 0x01E9 + + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__FGC__BYTE_14 0x01EA + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__FGC__BYTE_15 0x01EB + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__TEST_PROGRAM_MAJOR_MINOR 0x01EC + + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__MAP_MAJOR_MINOR 0x01ED + + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__YEAR_MONTH 0x01EE + + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__DAY_MODULE_DATE_PHASE 0x01EF + + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__TIME 0x01F0 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__TESTER_ID 0x01F2 + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__SITE_ID 0x01F3 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__TEST_PROGRAM_MAJOR_MINOR 0x01F4 + + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__PROBE_CARD_MAJOR_MINOR 0x01F5 + + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__TESTER_ID 0x01F6 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__LOT__BYTE_0 0x01F8 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__LOT__BYTE_1 0x01F9 + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__LOT__BYTE_2 0x01FA + + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__LOT__BYTE_3 0x01FB + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__LOT__BYTE_4 0x01FC + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__LOT__BYTE_5 0x01FD + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__WAFER 0x01FD + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__XCOORD 0x01FE + + + + + + + + + + + + + + + +#define VL53L1_NVM__EWS__YCOORD 0x01FF + + + + + + + + + + + + + + + + +#define VL53L1_NVM__FMT__OPTICAL_CENTRE_DATA_INDEX 0x00B8 +#define VL53L1_NVM__FMT__OPTICAL_CENTRE_DATA_SIZE 4 + +#define VL53L1_NVM__FMT__CAL_PEAK_RATE_MAP_DATA_INDEX 0x015C +#define VL53L1_NVM__FMT__CAL_PEAK_RATE_MAP_DATA_SIZE 56 + +#define VL53L1_NVM__FMT__ADDITIONAL_OFFSET_CAL_DATA_INDEX 0x0194 +#define VL53L1_NVM__FMT__ADDITIONAL_OFFSET_CAL_DATA_SIZE 8 + +#define VL53L1_NVM__FMT__RANGE_RESULTS__140MM_MM_PRE_RANGE 0x019C +#define VL53L1_NVM__FMT__RANGE_RESULTS__140MM_DARK 0x01AC +#define VL53L1_NVM__FMT__RANGE_RESULTS__400MM_DARK 0x01BC +#define VL53L1_NVM__FMT__RANGE_RESULTS__400MM_AMBIENT 0x01CC +#define VL53L1_NVM__FMT__RANGE_RESULTS__SIZE_BYTES 16 + + + + + + + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_nvm_structs.h b/drivers/input/misc/vl53L1/inc/vl53l1_nvm_structs.h new file mode 100644 index 000000000000..4f9504034bc4 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_nvm_structs.h @@ -0,0 +1,809 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_NVM_STRUCTS_H_ +#define _VL53L1_NVM_STRUCTS_H_ + + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "vl53l1_platform.h" +#include "vl53l1_ll_def.h" + + + + + + + + + + +typedef struct { + + uint16_t result__actual_effective_rtn_spads; + + + uint8_t ref_spad_array__num_requested_ref_spads; + + + uint8_t ref_spad_array__ref_location; + + + uint16_t result__peak_signal_count_rate_rtn_mcps; + + + uint16_t result__ambient_count_rate_rtn_mcps; + + + uint16_t result__peak_signal_count_rate_ref_mcps; + + + uint16_t result__ambient_count_rate_ref_mcps; + + + uint16_t measured_distance_mm; + + + uint16_t measured_distance_stdev_mm; + + + +} VL53L1_decoded_nvm_fmt_range_data_t; + + + + + + + + + + +typedef struct { + + char nvm__fmt__fgc[19]; + + + uint8_t nvm__fmt__test_program_major; + + + + + + uint8_t nvm__fmt__test_program_minor; + + + + + + uint8_t nvm__fmt__map_major; + + + + + + uint8_t nvm__fmt__map_minor; + + + + + + uint8_t nvm__fmt__year; + + + + + + uint8_t nvm__fmt__month; + + + + + + uint8_t nvm__fmt__day; + + + + + + uint8_t nvm__fmt__module_date_phase; + + + + + + uint16_t nvm__fmt__time; + + + + + + uint8_t nvm__fmt__tester_id; + + + + + + uint8_t nvm__fmt__site_id; + + + + + + uint8_t nvm__ews__test_program_major; + + + + + + uint8_t nvm__ews__test_program_minor; + + + + + + uint8_t nvm__ews__probe_card_major; + + + + + + uint8_t nvm__ews__probe_card_minor; + + + + + + uint8_t nvm__ews__tester_id; + + + + + + + char nvm__ews__lot[8]; + + + uint8_t nvm__ews__wafer; + + + + + + uint8_t nvm__ews__xcoord; + + + + + + uint8_t nvm__ews__ycoord; + + + + + +} VL53L1_decoded_nvm_fmt_info_t; + + + + + + + + + + +typedef struct { + + uint8_t nvm__ews__test_program_major; + + + + + + uint8_t nvm__ews__test_program_minor; + + + + + + uint8_t nvm__ews__probe_card_major; + + + + + + uint8_t nvm__ews__probe_card_minor; + + + + + + uint8_t nvm__ews__tester_id; + + + + + + + char nvm__ews__lot[8]; + + + uint8_t nvm__ews__wafer; + + + + + + uint8_t nvm__ews__xcoord; + + + + + + uint8_t nvm__ews__ycoord; + + + + + + +} VL53L1_decoded_nvm_ews_info_t; + + + + + + + + + + +typedef struct { + uint8_t nvm__identification_model_id; + + + + + + uint8_t nvm__identification_module_type; + + + + + + uint8_t nvm__identification_revision_id; + + + + + + uint16_t nvm__identification_module_id; + + + + + + uint8_t nvm__i2c_valid; + + + + + + uint8_t nvm__i2c_device_address_ews; + + + + + + uint16_t nvm__ews__fast_osc_frequency; + + + + + + uint8_t nvm__ews__fast_osc_trim_max; + + + + + + uint8_t nvm__ews__fast_osc_freq_set; + + + + + + uint16_t nvm__ews__slow_osc_calibration; + + + + + + uint16_t nvm__fmt__fast_osc_frequency; + + + + + + uint8_t nvm__fmt__fast_osc_trim_max; + + + + + + uint8_t nvm__fmt__fast_osc_freq_set; + + + + + + uint16_t nvm__fmt__slow_osc_calibration; + + + + + + uint8_t nvm__vhv_config_unlock; + + + + + + uint8_t nvm__ref_selvddpix; + + + + + + uint8_t nvm__ref_selvquench; + + + + + + uint8_t nvm__regavdd1v2_sel; + + + + + + uint8_t nvm__regdvdd1v2_sel; + + + + + + uint8_t nvm__vhv_timeout__macrop; + + + + + + uint8_t nvm__vhv_loop_bound; + + + + + + uint8_t nvm__vhv_count_threshold; + + + + + + uint8_t nvm__vhv_offset; + + + + + + uint8_t nvm__vhv_init_enable; + + + + + + uint8_t nvm__vhv_init_value; + + + + + + uint8_t nvm__laser_safety_vcsel_trim_ll; + + + + + + uint8_t nvm__laser_safety_vcsel_selion_ll; + + + + + + uint8_t nvm__laser_safety_vcsel_selion_max_ll; + + + + + + uint8_t nvm__laser_safety_mult_ll; + + + + + + uint8_t nvm__laser_safety_clip_ll; + + + + + + uint8_t nvm__laser_safety_vcsel_trim_ld; + + + + + + uint8_t nvm__laser_safety_vcsel_selion_ld; + + + + + + uint8_t nvm__laser_safety_vcsel_selion_max_ld; + + + + + + uint8_t nvm__laser_safety_mult_ld; + + + + + + uint8_t nvm__laser_safety_clip_ld; + + + + + + uint8_t nvm__laser_safety_lock_byte; + + + + + + uint8_t nvm__laser_safety_unlock_byte; + + + + + + uint8_t nvm__ews__spad_enables_rtn[VL53L1_RTN_SPAD_BUFFER_SIZE]; + + + + + + uint8_t nvm__ews__spad_enables_ref__loc1[VL53L1_REF_SPAD_BUFFER_SIZE]; + + + + + + uint8_t nvm__ews__spad_enables_ref__loc2[VL53L1_REF_SPAD_BUFFER_SIZE]; + + + + + + uint8_t nvm__ews__spad_enables_ref__loc3[VL53L1_REF_SPAD_BUFFER_SIZE]; + + + + + + uint8_t nvm__fmt__spad_enables_rtn[VL53L1_RTN_SPAD_BUFFER_SIZE]; + + + + + + uint8_t nvm__fmt__spad_enables_ref__loc1[VL53L1_REF_SPAD_BUFFER_SIZE]; + + + + + + uint8_t nvm__fmt__spad_enables_ref__loc2[VL53L1_REF_SPAD_BUFFER_SIZE]; + + + + + + uint8_t nvm__fmt__spad_enables_ref__loc3[VL53L1_REF_SPAD_BUFFER_SIZE]; + + + + + + uint8_t nvm__fmt__roi_config__mode_roi_centre_spad; + + + + + + uint8_t nvm__fmt__roi_config__mode_roi_x_size; + + + + + + uint8_t nvm__fmt__roi_config__mode_roi_y_size; + + + + + + uint8_t nvm__fmt__ref_spad_apply__num_requested_ref_spad; + + + + + + uint8_t nvm__fmt__ref_spad_man__ref_location; + + + + + + uint16_t nvm__fmt__mm_config__inner_offset_mm; + + + + + + uint16_t nvm__fmt__mm_config__outer_offset_mm; + + + + + + uint16_t nvm__fmt__algo_part_to_part_range_offset_mm; + + + + + + uint16_t nvm__fmt__algo__crosstalk_compensation_plane_offset_kcps; + + + + + + uint16_t nvm__fmt__algo__crosstalk_compensation_x_plane_gradient_kcps; + + + + + + uint16_t nvm__fmt__algo__crosstalk_compensation_y_plane_gradient_kcps; + + + + + + uint8_t nvm__fmt__spare__host_config__nvm_config_spare_0; + + + + + + uint8_t nvm__fmt__spare__host_config__nvm_config_spare_1; + + + + + + uint8_t nvm__customer_space_programmed; + + + + + + uint8_t nvm__cust__i2c_device_address; + + + + + + uint8_t nvm__cust__ref_spad_apply__num_requested_ref_spad; + + + + + + uint8_t nvm__cust__ref_spad_man__ref_location; + + + + + + uint16_t nvm__cust__mm_config__inner_offset_mm; + + + + + + uint16_t nvm__cust__mm_config__outer_offset_mm; + + + + + + uint16_t nvm__cust__algo_part_to_part_range_offset_mm; + + + + + + uint16_t nvm__cust__algo__crosstalk_compensation_plane_offset_kcps; + + + + + + uint16_t nvm__cust__algo__crosstalk_compensation_x_plane_gradient_kcps; + + + + + + uint16_t nvm__cust__algo__crosstalk_compensation_y_plane_gradient_kcps; + + + + + + uint8_t nvm__cust__spare__host_config__nvm_config_spare_0; + + + + + + uint8_t nvm__cust__spare__host_config__nvm_config_spare_1; + + + + + + + VL53L1_optical_centre_t fmt_optical_centre; + VL53L1_cal_peak_rate_map_t fmt_peak_rate_map; + VL53L1_additional_offset_cal_data_t fmt_add_offset_data; + + VL53L1_decoded_nvm_fmt_range_data_t + fmt_range_data[VL53L1_NVM_MAX_FMT_RANGE_DATA]; + + VL53L1_decoded_nvm_fmt_info_t fmt_info; + VL53L1_decoded_nvm_ews_info_t ews_info; + +} VL53L1_decoded_nvm_data_t; + + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_preset_setup.h b/drivers/input/misc/vl53L1/inc/vl53l1_preset_setup.h new file mode 100644 index 000000000000..2a93fe70f330 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_preset_setup.h @@ -0,0 +1,110 @@ + +/****************************************************************************** + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ******************************************************************************* + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + + ******************************************************************************* + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones mentioned above : + + ******************************************************************************* + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + + ******************************************************************************* + */ + +#ifndef _VL53L1_PRESET_SETUP_H_ +#define _VL53L1_PRESET_SETUP_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* indexes for the bare driver tuning setting API function */ +enum VL53L1_Tuning_t { + VL53L1_TUNING_VERSION = 0, + VL53L1_TUNING_PROXY_MIN, + VL53L1_TUNING_SINGLE_TARGET_XTALK_TARGET_DISTANCE_MM, + VL53L1_TUNING_SINGLE_TARGET_XTALK_SAMPLE_NUMBER, + VL53L1_TUNING_MIN_AMBIENT_DMAX_VALID, + VL53L1_TUNING_MAX_SIMPLE_OFFSET_CALIBRATION_SAMPLE_NUMBER, + VL53L1_TUNING_XTALK_FULL_ROI_TARGET_DISTANCE_MM, + VL53L1_TUNING_SIMPLE_OFFSET_CALIBRATION_REPEAT, + + VL53L1_TUNING_MAX_TUNABLE_KEY +}; + +/* default values for the tuning settings parameters */ +#define TUNING_VERSION 0x0004 + +#define TUNING_PROXY_MIN -30 /* min distance in mm */ +#define TUNING_SINGLE_TARGET_XTALK_TARGET_DISTANCE_MM 600 +/* Target distance in mm for single target Xtalk */ +#define TUNING_SINGLE_TARGET_XTALK_SAMPLE_NUMBER 50 +/* Number of sample used for single target Xtalk */ +#define TUNING_MIN_AMBIENT_DMAX_VALID 8 +/* Minimum ambient level to state the Dmax returned by the device is valid */ +#define TUNING_MAX_SIMPLE_OFFSET_CALIBRATION_SAMPLE_NUMBER 5 +/* Maximum loops to perform simple offset calibration */ +#define TUNING_XTALK_FULL_ROI_TARGET_DISTANCE_MM 600 +/* Target distance in mm for target Xtalk from Bins method*/ +#define TUNING_SIMPLE_OFFSET_CALIBRATION_REPEAT 3 +/* Number of loops done during the simple offset calibration*/ + +/* the following table should actually be defined as static and shall be part + * of the VL53L1_StaticInit() function code + */ + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L1_PRESET_SETUP_H_ */ diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_register_funcs.h b/drivers/input/misc/vl53L1/inc/vl53l1_register_funcs.h new file mode 100644 index 000000000000..37f492699c46 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_register_funcs.h @@ -0,0 +1,1694 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_REGISTER_FUNCS_H_ +#define _VL53L1_REGISTER_FUNCS_H_ + +#include "vl53l1_platform.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_static_nvm_managed( + VL53L1_static_nvm_managed_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_static_nvm_managed( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_static_nvm_managed_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_static_nvm_managed( + VL53L1_DEV Dev, + VL53L1_static_nvm_managed_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_static_nvm_managed( + VL53L1_DEV Dev, + VL53L1_static_nvm_managed_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_customer_nvm_managed( + VL53L1_customer_nvm_managed_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_customer_nvm_managed( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_customer_nvm_managed_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_customer_nvm_managed( + VL53L1_DEV Dev, + VL53L1_customer_nvm_managed_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_customer_nvm_managed( + VL53L1_DEV Dev, + VL53L1_customer_nvm_managed_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_static_config( + VL53L1_static_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_static_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_static_config_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_static_config( + VL53L1_DEV Dev, + VL53L1_static_config_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_static_config( + VL53L1_DEV Dev, + VL53L1_static_config_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_general_config( + VL53L1_general_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_general_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_general_config_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_general_config( + VL53L1_DEV Dev, + VL53L1_general_config_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_general_config( + VL53L1_DEV Dev, + VL53L1_general_config_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_timing_config( + VL53L1_timing_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_timing_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_timing_config_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_timing_config( + VL53L1_DEV Dev, + VL53L1_timing_config_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_timing_config( + VL53L1_DEV Dev, + VL53L1_timing_config_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_dynamic_config( + VL53L1_dynamic_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_dynamic_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_dynamic_config_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_dynamic_config( + VL53L1_DEV Dev, + VL53L1_dynamic_config_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_dynamic_config( + VL53L1_DEV Dev, + VL53L1_dynamic_config_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_system_control( + VL53L1_system_control_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_system_control( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_system_control_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_system_control( + VL53L1_DEV Dev, + VL53L1_system_control_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_system_control( + VL53L1_DEV Dev, + VL53L1_system_control_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_system_results( + VL53L1_system_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_system_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_system_results_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_system_results( + VL53L1_DEV Dev, + VL53L1_system_results_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_system_results( + VL53L1_DEV Dev, + VL53L1_system_results_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_core_results( + VL53L1_core_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_core_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_core_results_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_core_results( + VL53L1_DEV Dev, + VL53L1_core_results_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_core_results( + VL53L1_DEV Dev, + VL53L1_core_results_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_debug_results( + VL53L1_debug_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_debug_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_debug_results_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_debug_results( + VL53L1_DEV Dev, + VL53L1_debug_results_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_debug_results( + VL53L1_DEV Dev, + VL53L1_debug_results_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_nvm_copy_data( + VL53L1_nvm_copy_data_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_nvm_copy_data( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_nvm_copy_data_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_nvm_copy_data( + VL53L1_DEV Dev, + VL53L1_nvm_copy_data_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_nvm_copy_data( + VL53L1_DEV Dev, + VL53L1_nvm_copy_data_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_prev_shadow_system_results( + VL53L1_prev_shadow_system_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_prev_shadow_system_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_prev_shadow_system_results_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_prev_shadow_system_results( + VL53L1_DEV Dev, + VL53L1_prev_shadow_system_results_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_prev_shadow_system_results( + VL53L1_DEV Dev, + VL53L1_prev_shadow_system_results_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_prev_shadow_core_results( + VL53L1_prev_shadow_core_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_prev_shadow_core_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_prev_shadow_core_results_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_prev_shadow_core_results( + VL53L1_DEV Dev, + VL53L1_prev_shadow_core_results_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_prev_shadow_core_results( + VL53L1_DEV Dev, + VL53L1_prev_shadow_core_results_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_patch_debug( + VL53L1_patch_debug_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_patch_debug( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_patch_debug_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_patch_debug( + VL53L1_DEV Dev, + VL53L1_patch_debug_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_patch_debug( + VL53L1_DEV Dev, + VL53L1_patch_debug_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_gph_general_config( + VL53L1_gph_general_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_gph_general_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_gph_general_config_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_gph_general_config( + VL53L1_DEV Dev, + VL53L1_gph_general_config_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_gph_general_config( + VL53L1_DEV Dev, + VL53L1_gph_general_config_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_gph_static_config( + VL53L1_gph_static_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_gph_static_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_gph_static_config_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_gph_static_config( + VL53L1_DEV Dev, + VL53L1_gph_static_config_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_gph_static_config( + VL53L1_DEV Dev, + VL53L1_gph_static_config_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_gph_timing_config( + VL53L1_gph_timing_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_gph_timing_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_gph_timing_config_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_gph_timing_config( + VL53L1_DEV Dev, + VL53L1_gph_timing_config_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_gph_timing_config( + VL53L1_DEV Dev, + VL53L1_gph_timing_config_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_fw_internal( + VL53L1_fw_internal_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_fw_internal( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_fw_internal_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_fw_internal( + VL53L1_DEV Dev, + VL53L1_fw_internal_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_fw_internal( + VL53L1_DEV Dev, + VL53L1_fw_internal_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_patch_results( + VL53L1_patch_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_patch_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_patch_results_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_patch_results( + VL53L1_DEV Dev, + VL53L1_patch_results_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_patch_results( + VL53L1_DEV Dev, + VL53L1_patch_results_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_shadow_system_results( + VL53L1_shadow_system_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_shadow_system_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_shadow_system_results_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_shadow_system_results( + VL53L1_DEV Dev, + VL53L1_shadow_system_results_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_shadow_system_results( + VL53L1_DEV Dev, + VL53L1_shadow_system_results_t *pdata); + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_encode_shadow_core_results( + VL53L1_shadow_core_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_i2c_decode_shadow_core_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_shadow_core_results_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_set_shadow_core_results( + VL53L1_DEV Dev, + VL53L1_shadow_core_results_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_get_shadow_core_results( + VL53L1_DEV Dev, + VL53L1_shadow_core_results_t *pdata); + + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_register_map.h b/drivers/input/misc/vl53L1/inc/vl53l1_register_map.h new file mode 100644 index 000000000000..5b73b703b453 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_register_map.h @@ -0,0 +1,13151 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_REGISTER_MAP_H_ +#define _VL53L1_REGISTER_MAP_H_ + + + + + + + +#define VL53L1_SOFT_RESET 0x0000 + + + + + + + +#define VL53L1_I2C_SLAVE__DEVICE_ADDRESS 0x0001 + + + + + + + + + + + + + + + +#define VL53L1_ANA_CONFIG__VHV_REF_SEL_VDDPIX 0x0002 + + + + + + + + + + + + + + + +#define VL53L1_ANA_CONFIG__VHV_REF_SEL_VQUENCH 0x0003 + + + + + + + + + + + + + + + +#define VL53L1_ANA_CONFIG__REG_AVDD1V2_SEL 0x0004 + + + + + + + + + + + + + + + +#define VL53L1_ANA_CONFIG__FAST_OSC__TRIM 0x0005 + + + + + + + + + + + + + + + +#define VL53L1_OSC_MEASURED__FAST_OSC__FREQUENCY 0x0006 + + + + + + + + + + + + + + + +#define VL53L1_OSC_MEASURED__FAST_OSC__FREQUENCY_HI 0x0006 + + + + + + + +#define VL53L1_OSC_MEASURED__FAST_OSC__FREQUENCY_LO 0x0007 + + + + + + + +#define VL53L1_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND 0x0008 + + + + + + + + + + + + + + + + +#define VL53L1_VHV_CONFIG__COUNT_THRESH 0x0009 + + + + + + + + + + + + + + + +#define VL53L1_VHV_CONFIG__OFFSET 0x000A + + + + + + + + + + + + + + + +#define VL53L1_VHV_CONFIG__INIT 0x000B + + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_0 0x000D + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_1 0x000E + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_2 0x000F + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_3 0x0010 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_4 0x0011 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_5 0x0012 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__REF_EN_START_SELECT 0x0013 + + + + + + + + + + + + + + + +#define VL53L1_REF_SPAD_MAN__NUM_REQUESTED_REF_SPADS 0x0014 + + + + + + + + + + + + + + + +#define VL53L1_REF_SPAD_MAN__REF_LOCATION 0x0015 + + + + + + + + + + + + + + + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS 0x0016 + + + + + + + + + + + + + + + + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS_HI 0x0016 + + + + + + + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS_LO 0x0017 + + + + + + + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS 0x0018 + + + + + + + + + + + + + + + + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS_HI 0x0018 + + + + + + + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS_LO 0x0019 + + + + + + + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS 0x001A + + + + + + + + + + + + + + + + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS_HI 0x001A + + + + + + + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS_LO 0x001B + + + + + + + +#define VL53L1_REF_SPAD_CHAR__TOTAL_RATE_TARGET_MCPS 0x001C + + + + + + + + + + + + + + + + +#define VL53L1_REF_SPAD_CHAR__TOTAL_RATE_TARGET_MCPS_HI 0x001C + + + + + + + +#define VL53L1_REF_SPAD_CHAR__TOTAL_RATE_TARGET_MCPS_LO 0x001D + + + + + + + +#define VL53L1_ALGO__PART_TO_PART_RANGE_OFFSET_MM 0x001E + + + + + + + + + + + + + + + +#define VL53L1_ALGO__PART_TO_PART_RANGE_OFFSET_MM_HI 0x001E + + + + + + + +#define VL53L1_ALGO__PART_TO_PART_RANGE_OFFSET_MM_LO 0x001F + + + + + + + +#define VL53L1_MM_CONFIG__INNER_OFFSET_MM 0x0020 + + + + + + + + + + + + + + + +#define VL53L1_MM_CONFIG__INNER_OFFSET_MM_HI 0x0020 + + + + + + + +#define VL53L1_MM_CONFIG__INNER_OFFSET_MM_LO 0x0021 + + + + + + + +#define VL53L1_MM_CONFIG__OUTER_OFFSET_MM 0x0022 + + + + + + + + + + + + + + + +#define VL53L1_MM_CONFIG__OUTER_OFFSET_MM_HI 0x0022 + + + + + + + +#define VL53L1_MM_CONFIG__OUTER_OFFSET_MM_LO 0x0023 + + + + + + + +#define VL53L1_DSS_CONFIG__TARGET_TOTAL_RATE_MCPS 0x0024 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CONFIG__TARGET_TOTAL_RATE_MCPS_HI 0x0024 + + + + + + + +#define VL53L1_DSS_CONFIG__TARGET_TOTAL_RATE_MCPS_LO 0x0025 + + + + + + + +#define VL53L1_DEBUG__CTRL 0x0026 + + + + + + + + + + + + + + + +#define VL53L1_TEST_MODE__CTRL 0x0027 + + + + + + + + + + + + + + + +#define VL53L1_CLK_GATING__CTRL 0x0028 + + + + + + + + + + + + + + + + + + +#define VL53L1_NVM_BIST__CTRL 0x0029 + + + + + + + + + + + + + + + + +#define VL53L1_NVM_BIST__NUM_NVM_WORDS 0x002A + + + + + + + + + + + + + + + +#define VL53L1_NVM_BIST__START_ADDRESS 0x002B + + + + + + + + + + + + + + + +#define VL53L1_HOST_IF__STATUS 0x002C + + + + + + + + + + + + + + + +#define VL53L1_PAD_I2C_HV__CONFIG 0x002D + + + + + + + + + + + + + + + + + + + + +#define VL53L1_PAD_I2C_HV__EXTSUP_CONFIG 0x002E + + + + + + + + + + + + + + + +#define VL53L1_GPIO_HV_PAD__CTRL 0x002F + + + + + + + + + + + + + + + + +#define VL53L1_GPIO_HV_MUX__CTRL 0x0030 + + + + + + + + + + + + + + + + +#define VL53L1_GPIO__TIO_HV_STATUS 0x0031 + + + + + + + + + + + + + + + + +#define VL53L1_GPIO__FIO_HV_STATUS 0x0032 + + + + + + + + + + + + + + + +#define VL53L1_ANA_CONFIG__SPAD_SEL_PSWIDTH 0x0033 + + + + + + + + + + + + + + + +#define VL53L1_ANA_CONFIG__VCSEL_PULSE_WIDTH_OFFSET 0x0034 + + + + + + + + + + + + + + + +#define VL53L1_ANA_CONFIG__FAST_OSC__CONFIG_CTRL 0x0035 + + + + + + + + + + + + + + + +#define VL53L1_SIGMA_ESTIMATOR__EFFECTIVE_PULSE_WIDTH_NS 0x0036 + + + + + + + + + + + + + + + +#define VL53L1_SIGMA_ESTIMATOR__EFFECTIVE_AMBIENT_WIDTH_NS 0x0037 + + + + + + + + + + + + + + + +#define VL53L1_SIGMA_ESTIMATOR__SIGMA_REF_MM 0x0038 + + + + + + + + + + + + + + + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_VALID_HEIGHT_MM 0x0039 + + + + + + + + + + + + + + + +#define VL53L1_SPARE_HOST_CONFIG__STATIC_CONFIG_SPARE_0 0x003A + + + + + + + + + + + + + + + +#define VL53L1_SPARE_HOST_CONFIG__STATIC_CONFIG_SPARE_1 0x003B + + + + + + + + + + + + + + + +#define VL53L1_ALGO__RANGE_IGNORE_THRESHOLD_MCPS 0x003C + + + + + + + + + + + + + + + +#define VL53L1_ALGO__RANGE_IGNORE_THRESHOLD_MCPS_HI 0x003C + + + + + + + +#define VL53L1_ALGO__RANGE_IGNORE_THRESHOLD_MCPS_LO 0x003D + + + + + + + +#define VL53L1_ALGO__RANGE_IGNORE_VALID_HEIGHT_MM 0x003E + + + + + + + + + + + + + + + +#define VL53L1_ALGO__RANGE_MIN_CLIP 0x003F + + + + + + + + + + + + + + + + +#define VL53L1_ALGO__CONSISTENCY_CHECK__TOLERANCE 0x0040 + + + + + + + + + + + + + + + +#define VL53L1_SPARE_HOST_CONFIG__STATIC_CONFIG_SPARE_2 0x0041 + + + + + + + + + + + + + + + +#define VL53L1_SD_CONFIG__RESET_STAGES_MSB 0x0042 + + + + + + + + + + + + + + + +#define VL53L1_SD_CONFIG__RESET_STAGES_LSB 0x0043 + + + + + + + + + + + + + + + + +#define VL53L1_GPH_CONFIG__STREAM_COUNT_UPDATE_VALUE 0x0044 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__STREAM_DIVIDER 0x0045 + + + + + + + + + + + + + + + +#define VL53L1_SYSTEM__INTERRUPT_CONFIG_GPIO 0x0046 + + + + + + + + + + + + + + + + + + + + +#define VL53L1_CAL_CONFIG__VCSEL_START 0x0047 + + + + + + + + + + + + + + + +#define VL53L1_CAL_CONFIG__REPEAT_RATE 0x0048 + + + + + + + + + + + + + + + +#define VL53L1_CAL_CONFIG__REPEAT_RATE_HI 0x0048 + + + + + + + +#define VL53L1_CAL_CONFIG__REPEAT_RATE_LO 0x0049 + + + + + + + +#define VL53L1_GLOBAL_CONFIG__VCSEL_WIDTH 0x004A + + + + + + + + + + + + + + + +#define VL53L1_PHASECAL_CONFIG__TIMEOUT_MACROP 0x004B + + + + + + + + + + + + + + + +#define VL53L1_PHASECAL_CONFIG__TARGET 0x004C + + + + + + + + + + + + + + + +#define VL53L1_PHASECAL_CONFIG__OVERRIDE 0x004D + + + + + + + + + + + + + + + +#define VL53L1_DSS_CONFIG__ROI_MODE_CONTROL 0x004F + + + + + + + + + + + + + + + + +#define VL53L1_SYSTEM__THRESH_RATE_HIGH 0x0050 + + + + + + + + + + + + + + + +#define VL53L1_SYSTEM__THRESH_RATE_HIGH_HI 0x0050 + + + + + + + +#define VL53L1_SYSTEM__THRESH_RATE_HIGH_LO 0x0051 + + + + + + + +#define VL53L1_SYSTEM__THRESH_RATE_LOW 0x0052 + + + + + + + + + + + + + + + +#define VL53L1_SYSTEM__THRESH_RATE_LOW_HI 0x0052 + + + + + + + +#define VL53L1_SYSTEM__THRESH_RATE_LOW_LO 0x0053 + + + + + + + +#define VL53L1_DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT 0x0054 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT_HI 0x0054 + + + + + + + +#define VL53L1_DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT_LO 0x0055 + + + + + + + +#define VL53L1_DSS_CONFIG__MANUAL_BLOCK_SELECT 0x0056 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CONFIG__APERTURE_ATTENUATION 0x0057 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CONFIG__MAX_SPADS_LIMIT 0x0058 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CONFIG__MIN_SPADS_LIMIT 0x0059 + + + + + + + + + + + + + + + +#define VL53L1_MM_CONFIG__TIMEOUT_MACROP_A_HI 0x005A + + + + + + + + + + + + + + + +#define VL53L1_MM_CONFIG__TIMEOUT_MACROP_A_LO 0x005B + + + + + + + + + + + + + + + +#define VL53L1_MM_CONFIG__TIMEOUT_MACROP_B_HI 0x005C + + + + + + + + + + + + + + + +#define VL53L1_MM_CONFIG__TIMEOUT_MACROP_B_LO 0x005D + + + + + + + + + + + + + + + +#define VL53L1_RANGE_CONFIG__TIMEOUT_MACROP_A_HI 0x005E + + + + + + + + + + + + + + + +#define VL53L1_RANGE_CONFIG__TIMEOUT_MACROP_A_LO 0x005F + + + + + + + + + + + + + + + +#define VL53L1_RANGE_CONFIG__VCSEL_PERIOD_A 0x0060 + + + + + + + + + + + + + + + +#define VL53L1_RANGE_CONFIG__TIMEOUT_MACROP_B_HI 0x0061 + + + + + + + + + + + + + + + +#define VL53L1_RANGE_CONFIG__TIMEOUT_MACROP_B_LO 0x0062 + + + + + + + + + + + + + + + +#define VL53L1_RANGE_CONFIG__VCSEL_PERIOD_B 0x0063 + + + + + + + + + + + + + + + +#define VL53L1_RANGE_CONFIG__SIGMA_THRESH 0x0064 + + + + + + + + + + + + + + + +#define VL53L1_RANGE_CONFIG__SIGMA_THRESH_HI 0x0064 + + + + + + + +#define VL53L1_RANGE_CONFIG__SIGMA_THRESH_LO 0x0065 + + + + + + + +#define VL53L1_RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS 0x0066 + + + + + + + + + + + + + + + + +#define VL53L1_RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS_HI 0x0066 + + + + + + + +#define VL53L1_RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS_LO 0x0067 + + + + + + + +#define VL53L1_RANGE_CONFIG__VALID_PHASE_LOW 0x0068 + + + + + + + + + + + + + + + +#define VL53L1_RANGE_CONFIG__VALID_PHASE_HIGH 0x0069 + + + + + + + + + + + + + + + +#define VL53L1_SYSTEM__INTERMEASUREMENT_PERIOD 0x006C + + + + + + + + + + + + + + + +#define VL53L1_SYSTEM__INTERMEASUREMENT_PERIOD_3 0x006C + + + + + + + +#define VL53L1_SYSTEM__INTERMEASUREMENT_PERIOD_2 0x006D + + + + + + + +#define VL53L1_SYSTEM__INTERMEASUREMENT_PERIOD_1 0x006E + + + + + + + +#define VL53L1_SYSTEM__INTERMEASUREMENT_PERIOD_0 0x006F + + + + + + + +#define VL53L1_SYSTEM__FRACTIONAL_ENABLE 0x0070 + + + + + + + + + + + + + + + +#define VL53L1_SYSTEM__GROUPED_PARAMETER_HOLD_0 0x0071 + + + + + + + + + + + + + + + + +#define VL53L1_SYSTEM__THRESH_HIGH 0x0072 + + + + + + + + + + + + + + + +#define VL53L1_SYSTEM__THRESH_HIGH_HI 0x0072 + + + + + + + +#define VL53L1_SYSTEM__THRESH_HIGH_LO 0x0073 + + + + + + + +#define VL53L1_SYSTEM__THRESH_LOW 0x0074 + + + + + + + + + + + + + + + +#define VL53L1_SYSTEM__THRESH_LOW_HI 0x0074 + + + + + + + +#define VL53L1_SYSTEM__THRESH_LOW_LO 0x0075 + + + + + + + +#define VL53L1_SYSTEM__ENABLE_XTALK_PER_QUADRANT 0x0076 + + + + + + + + + + + + + + + +#define VL53L1_SYSTEM__SEED_CONFIG 0x0077 + + + + + + + + + + + + + + + + +#define VL53L1_SD_CONFIG__WOI_SD0 0x0078 + + + + + + + + + + + + + + + +#define VL53L1_SD_CONFIG__WOI_SD1 0x0079 + + + + + + + + + + + + + + + +#define VL53L1_SD_CONFIG__INITIAL_PHASE_SD0 0x007A + + + + + + + + + + + + + + + +#define VL53L1_SD_CONFIG__INITIAL_PHASE_SD1 0x007B + + + + + + + + + + + + + + + +#define VL53L1_SYSTEM__GROUPED_PARAMETER_HOLD_1 0x007C + + + + + + + + + + + + + + + + +#define VL53L1_SD_CONFIG__FIRST_ORDER_SELECT 0x007D + + + + + + + + + + + + + + + + +#define VL53L1_SD_CONFIG__QUANTIFIER 0x007E + + + + + + + + + + + + + + + +#define VL53L1_ROI_CONFIG__USER_ROI_CENTRE_SPAD 0x007F + + + + + + + + + + + + + + + +#define VL53L1_ROI_CONFIG__USER_ROI_REQUESTED_GLOBAL_XY_SIZE 0x0080 + + + + + + + + + + + + + + + +#define VL53L1_SYSTEM__SEQUENCE_CONFIG 0x0081 + + + + + + + + + + + + + + + + + + + + + + +#define VL53L1_SYSTEM__GROUPED_PARAMETER_HOLD 0x0082 + + + + + + + + + + + + + + + + +#define VL53L1_POWER_MANAGEMENT__GO1_POWER_FORCE 0x0083 + + + + + + + + + + + + + + + +#define VL53L1_SYSTEM__STREAM_COUNT_CTRL 0x0084 + + + + + + + + + + + + + + + +#define VL53L1_FIRMWARE__ENABLE 0x0085 + + + + + + + + + + + + + + + +#define VL53L1_SYSTEM__INTERRUPT_CLEAR 0x0086 + + + + + + + + + + + + + + + + +#define VL53L1_SYSTEM__MODE_START 0x0087 + + + + + + + + + + + + + + + + + + + + +#define VL53L1_RESULT__INTERRUPT_STATUS 0x0088 + + + + + + + + + + + + + + + + + +#define VL53L1_RESULT__RANGE_STATUS 0x0089 + + + + + + + + + + + + + + + + + + +#define VL53L1_RESULT__REPORT_STATUS 0x008A + + + + + + + + + + + + + + + +#define VL53L1_RESULT__STREAM_COUNT 0x008B + + + + + + + + + + + + + + + +#define VL53L1_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0 0x008C + + + + + + + + + + + + + + + + +#define VL53L1_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_HI 0x008C + + + + + + + +#define VL53L1_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_LO 0x008D + + + + + + + +#define VL53L1_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0 0x008E + + + + + + + + + + + + + + + + +#define VL53L1_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_HI 0x008E + + + + + + + +#define VL53L1_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_LO 0x008F + + + + + + + +#define VL53L1_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0 0x0090 + + + + + + + + + + + + + + + + +#define VL53L1_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_HI 0x0090 + + + + + + + +#define VL53L1_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_LO 0x0091 + + + + + + + +#define VL53L1_RESULT__SIGMA_SD0 0x0092 + + + + + + + + + + + + + + + +#define VL53L1_RESULT__SIGMA_SD0_HI 0x0092 + + + + + + + +#define VL53L1_RESULT__SIGMA_SD0_LO 0x0093 + + + + + + + +#define VL53L1_RESULT__PHASE_SD0 0x0094 + + + + + + + + + + + + + + + +#define VL53L1_RESULT__PHASE_SD0_HI 0x0094 + + + + + + + +#define VL53L1_RESULT__PHASE_SD0_LO 0x0095 + + + + + + + +#define VL53L1_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 0x0096 + + + + + + + + + + + + + + + +#define VL53L1_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_HI 0x0096 + + + + + + + +#define VL53L1_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_LO 0x0097 + + + + + + + +#define VL53L1_PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0 0x0098 + + + + + + + + + + + + + +#define VL53L1__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_HI 0x0098 + + + + + + + +#define VL53L1___PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_LO 0x0099 + + + + + + + +#define VL53L1_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0 0x009A + + + + + + + + + + + + + + + + +#define VL53L1_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_HI 0x009A + + + + + + + +#define VL53L1_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_LO 0x009B + + + + + + + +#define VL53L1_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0 0x009C + + + + + + + + + + + + + + + + +#define VL53L1_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_HI 0x009C + + + + + + + +#define VL53L1_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_LO 0x009D + + + + + + + +#define VL53L1_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0 0x009E + + + + + + + + + + + + + + + + +#define VL53L1_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_HI 0x009E + + + + + + + +#define VL53L1_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_LO 0x009F + + + + + + + +#define VL53L1_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1 0x00A0 + + + + + + + + + + + + + + + + +#define VL53L1_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_HI 0x00A0 + + + + + + + +#define VL53L1_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_LO 0x00A1 + + + + + + + +#define VL53L1_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1 0x00A2 + + + + + + + + + + + + + + + + +#define VL53L1_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_HI 0x00A2 + + + + + + + +#define VL53L1_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_LO 0x00A3 + + + + + + + +#define VL53L1_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1 0x00A4 + + + + + + + + + + + + + + + +#define VL53L1_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_HI 0x00A4 + + + + + + + +#define VL53L1_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_LO 0x00A5 + + + + + + + +#define VL53L1_RESULT__SIGMA_SD1 0x00A6 + + + + + + + + + + + + + + + +#define VL53L1_RESULT__SIGMA_SD1_HI 0x00A6 + + + + + + + +#define VL53L1_RESULT__SIGMA_SD1_LO 0x00A7 + + + + + + + +#define VL53L1_RESULT__PHASE_SD1 0x00A8 + + + + + + + + + + + + + + + +#define VL53L1_RESULT__PHASE_SD1_HI 0x00A8 + + + + + + + +#define VL53L1_RESULT__PHASE_SD1_LO 0x00A9 + + + + + + + +#define VL53L1_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1 0x00AA + + + + + + + + + + + + + + + +#define VL53L1_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_HI 0x00AA + + + + + + + +#define VL53L1_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_LO 0x00AB + + + + + + + +#define VL53L1_RESULT__SPARE_0_SD1 0x00AC + + + + + + + + + + + + + + + +#define VL53L1_RESULT__SPARE_0_SD1_HI 0x00AC + + + + + + + +#define VL53L1_RESULT__SPARE_0_SD1_LO 0x00AD + + + + + + + +#define VL53L1_RESULT__SPARE_1_SD1 0x00AE + + + + + + + + + + + + + + + +#define VL53L1_RESULT__SPARE_1_SD1_HI 0x00AE + + + + + + + +#define VL53L1_RESULT__SPARE_1_SD1_LO 0x00AF + + + + + + + +#define VL53L1_RESULT__SPARE_2_SD1 0x00B0 + + + + + + + + + + + + + + + +#define VL53L1_RESULT__SPARE_2_SD1_HI 0x00B0 + + + + + + + +#define VL53L1_RESULT__SPARE_2_SD1_LO 0x00B1 + + + + + + + +#define VL53L1_RESULT__SPARE_3_SD1 0x00B2 + + + + + + + + + + + + + + + +#define VL53L1_RESULT__THRESH_INFO 0x00B3 + + + + + + + + + + + + + + + + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0 0x00B4 + + + + + + + + + + + + + + + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_3 0x00B4 + + + + + + + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_2 0x00B5 + + + + + + + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_1 0x00B6 + + + + + + + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_0 0x00B7 + + + + + + + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0 0x00B8 + + + + + + + + + + + + + + + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_3 0x00B8 + + + + + + + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_2 0x00B9 + + + + + + + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_1 0x00BA + + + + + + + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_0 0x00BB + + + + + + + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0 0x00BC + + + + + + + + + + + + + + + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_3 0x00BC + + + + + + + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_2 0x00BD + + + + + + + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_1 0x00BE + + + + + + + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_0 0x00BF + + + + + + + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0 0x00C0 + + + + + + + + + + + + + + + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_3 0x00C0 + + + + + + + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_2 0x00C1 + + + + + + + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_1 0x00C2 + + + + + + + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_0 0x00C3 + + + + + + + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1 0x00C4 + + + + + + + + + + + + + + + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_3 0x00C4 + + + + + + + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_2 0x00C5 + + + + + + + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_1 0x00C6 + + + + + + + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_0 0x00C7 + + + + + + + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1 0x00C8 + + + + + + + + + + + + + + + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_3 0x00C8 + + + + + + + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_2 0x00C9 + + + + + + + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_1 0x00CA + + + + + + + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_0 0x00CB + + + + + + + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1 0x00CC + + + + + + + + + + + + + + + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_3 0x00CC + + + + + + + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_2 0x00CD + + + + + + + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_1 0x00CE + + + + + + + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_0 0x00CF + + + + + + + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1 0x00D0 + + + + + + + + + + + + + + + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_3 0x00D0 + + + + + + + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_2 0x00D1 + + + + + + + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_1 0x00D2 + + + + + + + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_0 0x00D3 + + + + + + + +#define VL53L1_RESULT_CORE__SPARE_0 0x00D4 + + + + + + + + + + + + + + + +#define VL53L1_PHASECAL_RESULT__REFERENCE_PHASE 0x00D6 + + + + + + + + + + + + + + + +#define VL53L1_PHASECAL_RESULT__REFERENCE_PHASE_HI 0x00D6 + + + + + + + +#define VL53L1_PHASECAL_RESULT__REFERENCE_PHASE_LO 0x00D7 + + + + + + + +#define VL53L1_PHASECAL_RESULT__VCSEL_START 0x00D8 + + + + + + + + + + + + + + + +#define VL53L1_REF_SPAD_CHAR_RESULT__NUM_ACTUAL_REF_SPADS 0x00D9 + + + + + + + + + + + + + + + +#define VL53L1_REF_SPAD_CHAR_RESULT__REF_LOCATION 0x00DA + + + + + + + + + + + + + + + +#define VL53L1_VHV_RESULT__COLDBOOT_STATUS 0x00DB + + + + + + + + + + + + + + + +#define VL53L1_VHV_RESULT__SEARCH_RESULT 0x00DC + + + + + + + + + + + + + + + +#define VL53L1_VHV_RESULT__LATEST_SETTING 0x00DD + + + + + + + + + + + + + + + +#define VL53L1_RESULT__OSC_CALIBRATE_VAL 0x00DE + + + + + + + + + + + + + + + +#define VL53L1_RESULT__OSC_CALIBRATE_VAL_HI 0x00DE + + + + + + + +#define VL53L1_RESULT__OSC_CALIBRATE_VAL_LO 0x00DF + + + + + + + +#define VL53L1_ANA_CONFIG__POWERDOWN_GO1 0x00E0 + + + + + + + + + + + + + + + + +#define VL53L1_ANA_CONFIG__REF_BG_CTRL 0x00E1 + + + + + + + + + + + + + + + + +#define VL53L1_ANA_CONFIG__REGDVDD1V2_CTRL 0x00E2 + + + + + + + + + + + + + + + + + +#define VL53L1_ANA_CONFIG__OSC_SLOW_CTRL 0x00E3 + + + + + + + + + + + + + + + + + +#define VL53L1_TEST_MODE__STATUS 0x00E4 + + + + + + + + + + + + + + + +#define VL53L1_FIRMWARE__SYSTEM_STATUS 0x00E5 + + + + + + + + + + + + + + + + +#define VL53L1_FIRMWARE__MODE_STATUS 0x00E6 + + + + + + + + + + + + + + + +#define VL53L1_FIRMWARE__SECONDARY_MODE_STATUS 0x00E7 + + + + + + + + + + + + + + + +#define VL53L1_FIRMWARE__CAL_REPEAT_RATE_COUNTER 0x00E8 + + + + + + + + + + + + + + + +#define VL53L1_FIRMWARE__CAL_REPEAT_RATE_COUNTER_HI 0x00E8 + + + + + + + +#define VL53L1_FIRMWARE__CAL_REPEAT_RATE_COUNTER_LO 0x00E9 + + + + + + + +#define VL53L1_FIRMWARE__HISTOGRAM_BIN 0x00EA + + + + + + + +#define VL53L1_GPH__SYSTEM__THRESH_HIGH 0x00EC + + + + + + + + + + + + + + + +#define VL53L1_GPH__SYSTEM__THRESH_HIGH_HI 0x00EC + + + + + + + +#define VL53L1_GPH__SYSTEM__THRESH_HIGH_LO 0x00ED + + + + + + + +#define VL53L1_GPH__SYSTEM__THRESH_LOW 0x00EE + + + + + + + + + + + + + + + +#define VL53L1_GPH__SYSTEM__THRESH_LOW_HI 0x00EE + + + + + + + +#define VL53L1_GPH__SYSTEM__THRESH_LOW_LO 0x00EF + + + + + + + +#define VL53L1_GPH__SYSTEM__ENABLE_XTALK_PER_QUADRANT 0x00F0 + + + + + + + + + + + + + + + +#define VL53L1_GPH__SPARE_0 0x00F1 + + + + + + + + + + + + + + + + + +#define VL53L1_GPH__SD_CONFIG__WOI_SD0 0x00F2 + + + + + + + + + + + + + + + +#define VL53L1_GPH__SD_CONFIG__WOI_SD1 0x00F3 + + + + + + + + + + + + + + + +#define VL53L1_GPH__SD_CONFIG__INITIAL_PHASE_SD0 0x00F4 + + + + + + + + + + + + + + + +#define VL53L1_GPH__SD_CONFIG__INITIAL_PHASE_SD1 0x00F5 + + + + + + + + + + + + + + + +#define VL53L1_GPH__SD_CONFIG__FIRST_ORDER_SELECT 0x00F6 + + + + + + + + + + + + + + + + +#define VL53L1_GPH__SD_CONFIG__QUANTIFIER 0x00F7 + + + + + + + + + + + + + + + +#define VL53L1_GPH__ROI_CONFIG__USER_ROI_CENTRE_SPAD 0x00F8 + + + + + + + + + + + + + + + +#define VL53L1_GPH__ROI_CONFIG__USER_ROI_REQUESTED_GLOBAL_XY_SIZE 0x00F9 + + + + + + + + + + + + + + + +#define VL53L1_GPH__SYSTEM__SEQUENCE_CONFIG 0x00FA + + + + + + + + + + + + + + + + + + + + + + +#define VL53L1_GPH__GPH_ID 0x00FB + + + + + + + + + + + + + + + +#define VL53L1_SYSTEM__INTERRUPT_SET 0x00FC + + + + + + + + + + + + + + + + +#define VL53L1_INTERRUPT_MANAGER__ENABLES 0x00FD + + + + + + + + + + + + + + + + + + + +#define VL53L1_INTERRUPT_MANAGER__CLEAR 0x00FE + + + + + + + + + + + + + + + + + + + +#define VL53L1_INTERRUPT_MANAGER__STATUS 0x00FF + + + + + + + + + + + + + + + + + + + +#define VL53L1_MCU_TO_HOST_BANK__WR_ACCESS_EN 0x0100 + + + + + + + + + + + + + + + +#define VL53L1_POWER_MANAGEMENT__GO1_RESET_STATUS 0x0101 + + + + + + + + + + + + + + + +#define VL53L1_PAD_STARTUP_MODE__VALUE_RO 0x0102 + + + + + + + + + + + + + + + + +#define VL53L1_PAD_STARTUP_MODE__VALUE_CTRL 0x0103 + + + + + + + + + + + + + + + + + + +#define VL53L1_PLL_PERIOD_US 0x0104 + + + + + + + + + + + + + + + +#define VL53L1_PLL_PERIOD_US_3 0x0104 + + + + + + + +#define VL53L1_PLL_PERIOD_US_2 0x0105 + + + + + + + +#define VL53L1_PLL_PERIOD_US_1 0x0106 + + + + + + + +#define VL53L1_PLL_PERIOD_US_0 0x0107 + + + + + + + +#define VL53L1_INTERRUPT_SCHEDULER__DATA_OUT 0x0108 + + + + + + + + + + + + + + + +#define VL53L1_INTERRUPT_SCHEDULER__DATA_OUT_3 0x0108 + + + + + + + +#define VL53L1_INTERRUPT_SCHEDULER__DATA_OUT_2 0x0109 + + + + + + + +#define VL53L1_INTERRUPT_SCHEDULER__DATA_OUT_1 0x010A + + + + + + + +#define VL53L1_INTERRUPT_SCHEDULER__DATA_OUT_0 0x010B + + + + + + + +#define VL53L1_NVM_BIST__COMPLETE 0x010C + + + + + + + + + + + + + + + +#define VL53L1_NVM_BIST__STATUS 0x010D + + + + + + + + + + + + + + + +#define VL53L1_IDENTIFICATION__MODEL_ID 0x010F + + + + + + + + + + + + + + + +#define VL53L1_IDENTIFICATION__MODULE_TYPE 0x0110 + + + + + + + + + + + + + + + +#define VL53L1_IDENTIFICATION__REVISION_ID 0x0111 + + + + + + + + + + + + + + + + +#define VL53L1_IDENTIFICATION__MODULE_ID 0x0112 + + + + + + + + + + + + + + + +#define VL53L1_IDENTIFICATION__MODULE_ID_HI 0x0112 + + + + + + + +#define VL53L1_IDENTIFICATION__MODULE_ID_LO 0x0113 + + + + + + + +#define VL53L1_ANA_CONFIG__FAST_OSC__TRIM_MAX 0x0114 + + + + + + + + + + + + + + + +#define VL53L1_ANA_CONFIG__FAST_OSC__FREQ_SET 0x0115 + + + + + + + + + + + + + + + +#define VL53L1_ANA_CONFIG__VCSEL_TRIM 0x0116 + + + + + + + + + + + + + + + +#define VL53L1_ANA_CONFIG__VCSEL_SELION 0x0117 + + + + + + + + + + + + + + + +#define VL53L1_ANA_CONFIG__VCSEL_SELION_MAX 0x0118 + + + + + + + + + + + + + + + +#define VL53L1_PROTECTED_LASER_SAFETY__LOCK_BIT 0x0119 + + + + + + + + + + + + + + + +#define VL53L1_LASER_SAFETY__KEY 0x011A + + + + + + + + + + + + + + + +#define VL53L1_LASER_SAFETY__KEY_RO 0x011B + + + + + + + + + + + + + + + +#define VL53L1_LASER_SAFETY__CLIP 0x011C + + + + + + + + + + + + + + + +#define VL53L1_LASER_SAFETY__MULT 0x011D + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_0 0x011E + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_1 0x011F + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_2 0x0120 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_3 0x0121 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_4 0x0122 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_5 0x0123 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_6 0x0124 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_7 0x0125 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_8 0x0126 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_9 0x0127 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_10 0x0128 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_11 0x0129 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_12 0x012A + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_13 0x012B + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_14 0x012C + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_15 0x012D + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_16 0x012E + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_17 0x012F + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_18 0x0130 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_19 0x0131 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_20 0x0132 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_21 0x0133 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_22 0x0134 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_23 0x0135 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_24 0x0136 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_25 0x0137 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_26 0x0138 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_27 0x0139 + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_28 0x013A + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_29 0x013B + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_30 0x013C + + + + + + + + + + + + + + + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_31 0x013D + + + + + + + + + + + + + + + +#define VL53L1_ROI_CONFIG__MODE_ROI_CENTRE_SPAD 0x013E + + + + + + + + + + + + + + + +#define VL53L1_ROI_CONFIG__MODE_ROI_XY_SIZE 0x013F + + + + + + + + + + + + + + + +#define VL53L1_GO2_HOST_BANK_ACCESS__OVERRIDE 0x0300 + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLICAND 0x0400 + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLICAND_3 0x0400 + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLICAND_2 0x0401 + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLICAND_1 0x0402 + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLICAND_0 0x0403 + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLIER 0x0404 + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLIER_3 0x0404 + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLIER_2 0x0405 + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLIER_1 0x0406 + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLIER_0 0x0407 + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_HI 0x0408 + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_HI_3 0x0408 + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_HI_2 0x0409 + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_HI_1 0x040A + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_HI_0 0x040B + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_LO 0x040C + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_LO_3 0x040C + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_LO_2 0x040D + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_LO_1 0x040E + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_LO_0 0x040F + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__START 0x0410 + + + + + + + +#define VL53L1_MCU_UTIL_MULTIPLIER__STATUS 0x0411 + + + + + + + +#define VL53L1_MCU_UTIL_DIVIDER__START 0x0412 + + + + + + + +#define VL53L1_MCU_UTIL_DIVIDER__STATUS 0x0413 + + + + + + + +#define VL53L1_MCU_UTIL_DIVIDER__DIVIDEND 0x0414 + + + + + + + +#define VL53L1_MCU_UTIL_DIVIDER__DIVIDEND_3 0x0414 + + + + + + + +#define VL53L1_MCU_UTIL_DIVIDER__DIVIDEND_2 0x0415 + + + + + + + +#define VL53L1_MCU_UTIL_DIVIDER__DIVIDEND_1 0x0416 + + + + + + + +#define VL53L1_MCU_UTIL_DIVIDER__DIVIDEND_0 0x0417 + + + + + + + +#define VL53L1_MCU_UTIL_DIVIDER__DIVISOR 0x0418 + + + + + + + +#define VL53L1_MCU_UTIL_DIVIDER__DIVISOR_3 0x0418 + + + + + + + +#define VL53L1_MCU_UTIL_DIVIDER__DIVISOR_2 0x0419 + + + + + + + +#define VL53L1_MCU_UTIL_DIVIDER__DIVISOR_1 0x041A + + + + + + + +#define VL53L1_MCU_UTIL_DIVIDER__DIVISOR_0 0x041B + + + + + + + +#define VL53L1_MCU_UTIL_DIVIDER__QUOTIENT 0x041C + + + + + + + +#define VL53L1_MCU_UTIL_DIVIDER__QUOTIENT_3 0x041C + + + + + + + +#define VL53L1_MCU_UTIL_DIVIDER__QUOTIENT_2 0x041D + + + + + + + +#define VL53L1_MCU_UTIL_DIVIDER__QUOTIENT_1 0x041E + + + + + + + +#define VL53L1_MCU_UTIL_DIVIDER__QUOTIENT_0 0x041F + + + + + + + +#define VL53L1_TIMER0__VALUE_IN 0x0420 + + + + + + + +#define VL53L1_TIMER0__VALUE_IN_3 0x0420 + + + + + + + +#define VL53L1_TIMER0__VALUE_IN_2 0x0421 + + + + + + + +#define VL53L1_TIMER0__VALUE_IN_1 0x0422 + + + + + + + +#define VL53L1_TIMER0__VALUE_IN_0 0x0423 + + + + + + + +#define VL53L1_TIMER1__VALUE_IN 0x0424 + + + + + + + +#define VL53L1_TIMER1__VALUE_IN_3 0x0424 + + + + + + + +#define VL53L1_TIMER1__VALUE_IN_2 0x0425 + + + + + + + +#define VL53L1_TIMER1__VALUE_IN_1 0x0426 + + + + + + + +#define VL53L1_TIMER1__VALUE_IN_0 0x0427 + + + + + + + +#define VL53L1_TIMER0__CTRL 0x0428 + + + + + + + +#define VL53L1_TIMER1__CTRL 0x0429 + + + + + + + +#define VL53L1_MCU_GENERAL_PURPOSE__GP_0 0x042C + + + + + + + + + + + + + + + +#define VL53L1_MCU_GENERAL_PURPOSE__GP_1 0x042D + + + + + + + + + + + + + + + +#define VL53L1_MCU_GENERAL_PURPOSE__GP_2 0x042E + + + + + + + + + + + + + + + +#define VL53L1_MCU_GENERAL_PURPOSE__GP_3 0x042F + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__CONFIG 0x0430 + + + + + + + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__OFFSET_CORRECTED_RANGE 0x0432 + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__OFFSET_CORRECTED_RANGE_HI 0x0432 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__OFFSET_CORRECTED_RANGE_LO 0x0433 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__SPARE_4 0x0434 + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__SPARE_4_3 0x0434 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__SPARE_4_2 0x0435 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__SPARE_4_1 0x0436 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__SPARE_4_0 0x0437 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__AMBIENT_DURATION_PRE_CALC 0x0438 + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__AMBIENT_DURATION_PRE_CALC_HI 0x0438 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__AMBIENT_DURATION_PRE_CALC_LO 0x0439 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_VCSEL_PERIOD 0x043C + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__SPARE_5 0x043D + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_TOTAL_PERIODS 0x043E + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_TOTAL_PERIODS_HI 0x043E + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_TOTAL_PERIODS_LO 0x043F + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_ACCUM_PHASE 0x0440 + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_ACCUM_PHASE_3 0x0440 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_ACCUM_PHASE_2 0x0441 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_ACCUM_PHASE_1 0x0442 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_ACCUM_PHASE_0 0x0443 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS 0x0444 + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS_3 0x0444 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS_2 0x0445 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS_1 0x0446 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS_0 0x0447 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS 0x0448 + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS_3 0x0448 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS_2 0x0449 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS_1 0x044A + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS_0 0x044B + + + + + + + +#define VL53L1_MCU_RANGE_CALC__SPARE_6 0x044C + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__SPARE_6_HI 0x044C + + + + + + + +#define VL53L1_MCU_RANGE_CALC__SPARE_6_LO 0x044D + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_ADJUST_VCSEL_PERIOD 0x044E + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_ADJUST_VCSEL_PERIOD_HI 0x044E + + + + + + + +#define VL53L1_MCU_RANGE_CALC__ALGO_ADJUST_VCSEL_PERIOD_LO 0x044F + + + + + + + +#define VL53L1_MCU_RANGE_CALC__NUM_SPADS 0x0450 + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__NUM_SPADS_HI 0x0450 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__NUM_SPADS_LO 0x0451 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__PHASE_OUTPUT 0x0452 + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__PHASE_OUTPUT_HI 0x0452 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__PHASE_OUTPUT_LO 0x0453 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__RATE_PER_SPAD_MCPS 0x0454 + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__RATE_PER_SPAD_MCPS_3 0x0454 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__RATE_PER_SPAD_MCPS_2 0x0455 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__RATE_PER_SPAD_MCPS_1 0x0456 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__RATE_PER_SPAD_MCPS_0 0x0457 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__SPARE_7 0x0458 + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__SPARE_8 0x0459 + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__PEAK_SIGNAL_RATE_MCPS 0x045A + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__PEAK_SIGNAL_RATE_MCPS_HI 0x045A + + + + + + + +#define VL53L1_MCU_RANGE_CALC__PEAK_SIGNAL_RATE_MCPS_LO 0x045B + + + + + + + +#define VL53L1_MCU_RANGE_CALC__AVG_SIGNAL_RATE_MCPS 0x045C + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__AVG_SIGNAL_RATE_MCPS_HI 0x045C + + + + + + + +#define VL53L1_MCU_RANGE_CALC__AVG_SIGNAL_RATE_MCPS_LO 0x045D + + + + + + + +#define VL53L1_MCU_RANGE_CALC__AMBIENT_RATE_MCPS 0x045E + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__AMBIENT_RATE_MCPS_HI 0x045E + + + + + + + +#define VL53L1_MCU_RANGE_CALC__AMBIENT_RATE_MCPS_LO 0x045F + + + + + + + +#define VL53L1_MCU_RANGE_CALC__XTALK 0x0460 + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__XTALK_HI 0x0460 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__XTALK_LO 0x0461 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__CALC_STATUS 0x0462 + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__DEBUG 0x0463 + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__PEAK_SIGNAL_RATE_XTALK_CORR_MCPS 0x0464 + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__PEAK_SIGNAL_RATE_XTALK_CORR_MCPS_HI 0x0464 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__PEAK_SIGNAL_RATE_XTALK_CORR_MCPS_LO 0x0465 + + + + + + + +#define VL53L1_MCU_RANGE_CALC__SPARE_0 0x0468 + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__SPARE_1 0x0469 + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__SPARE_2 0x046A + + + + + + + + + + + + + + + +#define VL53L1_MCU_RANGE_CALC__SPARE_3 0x046B + + + + + + + + + + + + + + + +#define VL53L1_PATCH__CTRL 0x0470 + + + + + + + +#define VL53L1_PATCH__JMP_ENABLES 0x0472 + + + + + + + +#define VL53L1_PATCH__JMP_ENABLES_HI 0x0472 + + + + + + + +#define VL53L1_PATCH__JMP_ENABLES_LO 0x0473 + + + + + + + +#define VL53L1_PATCH__DATA_ENABLES 0x0474 + + + + + + + +#define VL53L1_PATCH__DATA_ENABLES_HI 0x0474 + + + + + + + +#define VL53L1_PATCH__DATA_ENABLES_LO 0x0475 + + + + + + + +#define VL53L1_PATCH__OFFSET_0 0x0476 + + + + + + + +#define VL53L1_PATCH__OFFSET_0_HI 0x0476 + + + + + + + +#define VL53L1_PATCH__OFFSET_0_LO 0x0477 + + + + + + + +#define VL53L1_PATCH__OFFSET_1 0x0478 + + + + + + + +#define VL53L1_PATCH__OFFSET_1_HI 0x0478 + + + + + + + +#define VL53L1_PATCH__OFFSET_1_LO 0x0479 + + + + + + + +#define VL53L1_PATCH__OFFSET_2 0x047A + + + + + + + +#define VL53L1_PATCH__OFFSET_2_HI 0x047A + + + + + + + +#define VL53L1_PATCH__OFFSET_2_LO 0x047B + + + + + + + +#define VL53L1_PATCH__OFFSET_3 0x047C + + + + + + + +#define VL53L1_PATCH__OFFSET_3_HI 0x047C + + + + + + + +#define VL53L1_PATCH__OFFSET_3_LO 0x047D + + + + + + + +#define VL53L1_PATCH__OFFSET_4 0x047E + + + + + + + +#define VL53L1_PATCH__OFFSET_4_HI 0x047E + + + + + + + +#define VL53L1_PATCH__OFFSET_4_LO 0x047F + + + + + + + +#define VL53L1_PATCH__OFFSET_5 0x0480 + + + + + + + +#define VL53L1_PATCH__OFFSET_5_HI 0x0480 + + + + + + + +#define VL53L1_PATCH__OFFSET_5_LO 0x0481 + + + + + + + +#define VL53L1_PATCH__OFFSET_6 0x0482 + + + + + + + +#define VL53L1_PATCH__OFFSET_6_HI 0x0482 + + + + + + + +#define VL53L1_PATCH__OFFSET_6_LO 0x0483 + + + + + + + +#define VL53L1_PATCH__OFFSET_7 0x0484 + + + + + + + +#define VL53L1_PATCH__OFFSET_7_HI 0x0484 + + + + + + + +#define VL53L1_PATCH__OFFSET_7_LO 0x0485 + + + + + + + +#define VL53L1_PATCH__OFFSET_8 0x0486 + + + + + + + +#define VL53L1_PATCH__OFFSET_8_HI 0x0486 + + + + + + + +#define VL53L1_PATCH__OFFSET_8_LO 0x0487 + + + + + + + +#define VL53L1_PATCH__OFFSET_9 0x0488 + + + + + + + +#define VL53L1_PATCH__OFFSET_9_HI 0x0488 + + + + + + + +#define VL53L1_PATCH__OFFSET_9_LO 0x0489 + + + + + + + +#define VL53L1_PATCH__OFFSET_10 0x048A + + + + + + + +#define VL53L1_PATCH__OFFSET_10_HI 0x048A + + + + + + + +#define VL53L1_PATCH__OFFSET_10_LO 0x048B + + + + + + + +#define VL53L1_PATCH__OFFSET_11 0x048C + + + + + + + +#define VL53L1_PATCH__OFFSET_11_HI 0x048C + + + + + + + +#define VL53L1_PATCH__OFFSET_11_LO 0x048D + + + + + + + +#define VL53L1_PATCH__OFFSET_12 0x048E + + + + + + + +#define VL53L1_PATCH__OFFSET_12_HI 0x048E + + + + + + + +#define VL53L1_PATCH__OFFSET_12_LO 0x048F + + + + + + + +#define VL53L1_PATCH__OFFSET_13 0x0490 + + + + + + + +#define VL53L1_PATCH__OFFSET_13_HI 0x0490 + + + + + + + +#define VL53L1_PATCH__OFFSET_13_LO 0x0491 + + + + + + + +#define VL53L1_PATCH__OFFSET_14 0x0492 + + + + + + + +#define VL53L1_PATCH__OFFSET_14_HI 0x0492 + + + + + + + +#define VL53L1_PATCH__OFFSET_14_LO 0x0493 + + + + + + + +#define VL53L1_PATCH__OFFSET_15 0x0494 + + + + + + + +#define VL53L1_PATCH__OFFSET_15_HI 0x0494 + + + + + + + +#define VL53L1_PATCH__OFFSET_15_LO 0x0495 + + + + + + + +#define VL53L1_PATCH__ADDRESS_0 0x0496 + + + + + + + +#define VL53L1_PATCH__ADDRESS_0_HI 0x0496 + + + + + + + +#define VL53L1_PATCH__ADDRESS_0_LO 0x0497 + + + + + + + +#define VL53L1_PATCH__ADDRESS_1 0x0498 + + + + + + + +#define VL53L1_PATCH__ADDRESS_1_HI 0x0498 + + + + + + + +#define VL53L1_PATCH__ADDRESS_1_LO 0x0499 + + + + + + + +#define VL53L1_PATCH__ADDRESS_2 0x049A + + + + + + + +#define VL53L1_PATCH__ADDRESS_2_HI 0x049A + + + + + + + +#define VL53L1_PATCH__ADDRESS_2_LO 0x049B + + + + + + + +#define VL53L1_PATCH__ADDRESS_3 0x049C + + + + + + + +#define VL53L1_PATCH__ADDRESS_3_HI 0x049C + + + + + + + +#define VL53L1_PATCH__ADDRESS_3_LO 0x049D + + + + + + + +#define VL53L1_PATCH__ADDRESS_4 0x049E + + + + + + + +#define VL53L1_PATCH__ADDRESS_4_HI 0x049E + + + + + + + +#define VL53L1_PATCH__ADDRESS_4_LO 0x049F + + + + + + + +#define VL53L1_PATCH__ADDRESS_5 0x04A0 + + + + + + + +#define VL53L1_PATCH__ADDRESS_5_HI 0x04A0 + + + + + + + +#define VL53L1_PATCH__ADDRESS_5_LO 0x04A1 + + + + + + + +#define VL53L1_PATCH__ADDRESS_6 0x04A2 + + + + + + + +#define VL53L1_PATCH__ADDRESS_6_HI 0x04A2 + + + + + + + +#define VL53L1_PATCH__ADDRESS_6_LO 0x04A3 + + + + + + + +#define VL53L1_PATCH__ADDRESS_7 0x04A4 + + + + + + + +#define VL53L1_PATCH__ADDRESS_7_HI 0x04A4 + + + + + + + +#define VL53L1_PATCH__ADDRESS_7_LO 0x04A5 + + + + + + + +#define VL53L1_PATCH__ADDRESS_8 0x04A6 + + + + + + + +#define VL53L1_PATCH__ADDRESS_8_HI 0x04A6 + + + + + + + +#define VL53L1_PATCH__ADDRESS_8_LO 0x04A7 + + + + + + + +#define VL53L1_PATCH__ADDRESS_9 0x04A8 + + + + + + + +#define VL53L1_PATCH__ADDRESS_9_HI 0x04A8 + + + + + + + +#define VL53L1_PATCH__ADDRESS_9_LO 0x04A9 + + + + + + + +#define VL53L1_PATCH__ADDRESS_10 0x04AA + + + + + + + +#define VL53L1_PATCH__ADDRESS_10_HI 0x04AA + + + + + + + +#define VL53L1_PATCH__ADDRESS_10_LO 0x04AB + + + + + + + +#define VL53L1_PATCH__ADDRESS_11 0x04AC + + + + + + + +#define VL53L1_PATCH__ADDRESS_11_HI 0x04AC + + + + + + + +#define VL53L1_PATCH__ADDRESS_11_LO 0x04AD + + + + + + + +#define VL53L1_PATCH__ADDRESS_12 0x04AE + + + + + + + +#define VL53L1_PATCH__ADDRESS_12_HI 0x04AE + + + + + + + +#define VL53L1_PATCH__ADDRESS_12_LO 0x04AF + + + + + + + +#define VL53L1_PATCH__ADDRESS_13 0x04B0 + + + + + + + +#define VL53L1_PATCH__ADDRESS_13_HI 0x04B0 + + + + + + + +#define VL53L1_PATCH__ADDRESS_13_LO 0x04B1 + + + + + + + +#define VL53L1_PATCH__ADDRESS_14 0x04B2 + + + + + + + +#define VL53L1_PATCH__ADDRESS_14_HI 0x04B2 + + + + + + + +#define VL53L1_PATCH__ADDRESS_14_LO 0x04B3 + + + + + + + +#define VL53L1_PATCH__ADDRESS_15 0x04B4 + + + + + + + +#define VL53L1_PATCH__ADDRESS_15_HI 0x04B4 + + + + + + + +#define VL53L1_PATCH__ADDRESS_15_LO 0x04B5 + + + + + + + +#define VL53L1_SPI_ASYNC_MUX__CTRL 0x04C0 + + + + + + + +#define VL53L1_CLK__CONFIG 0x04C4 + + + + + + + + + + + + + + + +#define VL53L1_GPIO_LV_MUX__CTRL 0x04CC + + + + + + + + + + + + + + + + +#define VL53L1_GPIO_LV_PAD__CTRL 0x04CD + + + + + + + + + + + + + + + +#define VL53L1_PAD_I2C_LV__CONFIG 0x04D0 + + + + + + + +#define VL53L1_PAD_STARTUP_MODE__VALUE_RO_GO1 0x04D4 + + + + + + + + + + + + + + + +#define VL53L1_HOST_IF__STATUS_GO1 0x04D5 + + + + + + + + + + + + + + + +#define VL53L1_MCU_CLK_GATING__CTRL 0x04D8 + + + + + + + + + + + + + + + + + + +#define VL53L1_TEST__BIST_ROM_CTRL 0x04E0 + + + + + + + +#define VL53L1_TEST__BIST_ROM_RESULT 0x04E1 + + + + + + + +#define VL53L1_TEST__BIST_ROM_MCU_SIG 0x04E2 + + + + + + + +#define VL53L1_TEST__BIST_ROM_MCU_SIG_HI 0x04E2 + + + + + + + +#define VL53L1_TEST__BIST_ROM_MCU_SIG_LO 0x04E3 + + + + + + + +#define VL53L1_TEST__BIST_RAM_CTRL 0x04E4 + + + + + + + +#define VL53L1_TEST__BIST_RAM_RESULT 0x04E5 + + + + + + + +#define VL53L1_TEST__TMC 0x04E8 + + + + + + + +#define VL53L1_TEST__PLL_BIST_MIN_THRESHOLD 0x04F0 + + + + + + + +#define VL53L1_TEST__PLL_BIST_MIN_THRESHOLD_HI 0x04F0 + + + + + + + +#define VL53L1_TEST__PLL_BIST_MIN_THRESHOLD_LO 0x04F1 + + + + + + + +#define VL53L1_TEST__PLL_BIST_MAX_THRESHOLD 0x04F2 + + + + + + + +#define VL53L1_TEST__PLL_BIST_MAX_THRESHOLD_HI 0x04F2 + + + + + + + +#define VL53L1_TEST__PLL_BIST_MAX_THRESHOLD_LO 0x04F3 + + + + + + + +#define VL53L1_TEST__PLL_BIST_COUNT_OUT 0x04F4 + + + + + + + +#define VL53L1_TEST__PLL_BIST_COUNT_OUT_HI 0x04F4 + + + + + + + +#define VL53L1_TEST__PLL_BIST_COUNT_OUT_LO 0x04F5 + + + + + + + +#define VL53L1_TEST__PLL_BIST_GONOGO 0x04F6 + + + + + + + +#define VL53L1_TEST__PLL_BIST_CTRL 0x04F7 + + + + + + + +#define VL53L1_RANGING_CORE__DEVICE_ID 0x0680 + + + + + + + +#define VL53L1_RANGING_CORE__REVISION_ID 0x0681 + + + + + + + +#define VL53L1_RANGING_CORE__CLK_CTRL1 0x0683 + + + + + + + +#define VL53L1_RANGING_CORE__CLK_CTRL2 0x0684 + + + + + + + +#define VL53L1_RANGING_CORE__WOI_1 0x0685 + + + + + + + +#define VL53L1_RANGING_CORE__WOI_REF_1 0x0686 + + + + + + + +#define VL53L1_RANGING_CORE__START_RANGING 0x0687 + + + + + + + +#define VL53L1_RANGING_CORE__LOW_LIMIT_1 0x0690 + + + + + + + +#define VL53L1_RANGING_CORE__HIGH_LIMIT_1 0x0691 + + + + + + + +#define VL53L1_RANGING_CORE__LOW_LIMIT_REF_1 0x0692 + + + + + + + +#define VL53L1_RANGING_CORE__HIGH_LIMIT_REF_1 0x0693 + + + + + + + +#define VL53L1_RANGING_CORE__QUANTIFIER_1_MSB 0x0694 + + + + + + + +#define VL53L1_RANGING_CORE__QUANTIFIER_1_LSB 0x0695 + + + + + + + +#define VL53L1_RANGING_CORE__QUANTIFIER_REF_1_MSB 0x0696 + + + + + + + +#define VL53L1_RANGING_CORE__QUANTIFIER_REF_1_LSB 0x0697 + + + + + + + +#define VL53L1_RANGING_CORE__AMBIENT_OFFSET_1_MSB 0x0698 + + + + + + + +#define VL53L1_RANGING_CORE__AMBIENT_OFFSET_1_LSB 0x0699 + + + + + + + +#define VL53L1_RANGING_CORE__AMBIENT_OFFSET_REF_1_MSB 0x069A + + + + + + + +#define VL53L1_RANGING_CORE__AMBIENT_OFFSET_REF_1_LSB 0x069B + + + + + + + +#define VL53L1_RANGING_CORE__FILTER_STRENGTH_1 0x069C + + + + + + + +#define VL53L1_RANGING_CORE__FILTER_STRENGTH_REF_1 0x069D + + + + + + + +#define VL53L1_RANGING_CORE__SIGNAL_EVENT_LIMIT_1_MSB 0x069E + + + + + + + +#define VL53L1_RANGING_CORE__SIGNAL_EVENT_LIMIT_1_LSB 0x069F + + + + + + + +#define VL53L1_RANGING_CORE__SIGNAL_EVENT_LIMIT_REF_1_MSB 0x06A0 + + + + + + + +#define VL53L1_RANGING_CORE__SIGNAL_EVENT_LIMIT_REF_1_LSB 0x06A1 + + + + + + + +#define VL53L1_RANGING_CORE__TIMEOUT_OVERALL_PERIODS_MSB 0x06A4 + + + + + + + +#define VL53L1_RANGING_CORE__TIMEOUT_OVERALL_PERIODS_LSB 0x06A5 + + + + + + + +#define VL53L1_RANGING_CORE__INVERT_HW 0x06A6 + + + + + + + +#define VL53L1_RANGING_CORE__FORCE_HW 0x06A7 + + + + + + + +#define VL53L1_RANGING_CORE__STATIC_HW_VALUE 0x06A8 + + + + + + + +#define VL53L1_RANGING_CORE__FORCE_CONTINUOUS_AMBIENT 0x06A9 + + + + + + + +#define VL53L1_RANGING_CORE__TEST_PHASE_SELECT_TO_FILTER 0x06AA + + + + + + + +#define VL53L1_RANGING_CORE__TEST_PHASE_SELECT_TO_TIMING_GEN 0x06AB + + + + + + + +#define VL53L1_RANGING_CORE__INITIAL_PHASE_VALUE_1 0x06AC + + + + + + + +#define VL53L1_RANGING_CORE__INITIAL_PHASE_VALUE_REF_1 0x06AD + + + + + + + +#define VL53L1_RANGING_CORE__FORCE_UP_IN 0x06AE + + + + + + + +#define VL53L1_RANGING_CORE__FORCE_DN_IN 0x06AF + + + + + + + +#define VL53L1_RANGING_CORE__STATIC_UP_VALUE_1 0x06B0 + + + + + + + +#define VL53L1_RANGING_CORE__STATIC_UP_VALUE_REF_1 0x06B1 + + + + + + + +#define VL53L1_RANGING_CORE__STATIC_DN_VALUE_1 0x06B2 + + + + + + + +#define VL53L1_RANGING_CORE__STATIC_DN_VALUE_REF_1 0x06B3 + + + + + + + +#define VL53L1_RANGING_CORE__MONITOR_UP_DN 0x06B4 + + + + + + + +#define VL53L1_RANGING_CORE__INVERT_UP_DN 0x06B5 + + + + + + + +#define VL53L1_RANGING_CORE__CPUMP_1 0x06B6 + + + + + + + +#define VL53L1_RANGING_CORE__CPUMP_2 0x06B7 + + + + + + + +#define VL53L1_RANGING_CORE__CPUMP_3 0x06B8 + + + + + + + +#define VL53L1_RANGING_CORE__OSC_1 0x06B9 + + + + + + + +#define VL53L1_RANGING_CORE__PLL_1 0x06BB + + + + + + + +#define VL53L1_RANGING_CORE__PLL_2 0x06BC + + + + + + + +#define VL53L1_RANGING_CORE__REFERENCE_1 0x06BD + + + + + + + +#define VL53L1_RANGING_CORE__REFERENCE_3 0x06BF + + + + + + + +#define VL53L1_RANGING_CORE__REFERENCE_4 0x06C0 + + + + + + + +#define VL53L1_RANGING_CORE__REFERENCE_5 0x06C1 + + + + + + + +#define VL53L1_RANGING_CORE__REGAVDD1V2 0x06C3 + + + + + + + +#define VL53L1_RANGING_CORE__CALIB_1 0x06C4 + + + + + + + +#define VL53L1_RANGING_CORE__CALIB_2 0x06C5 + + + + + + + +#define VL53L1_RANGING_CORE__CALIB_3 0x06C6 + + + + + + + +#define VL53L1_RANGING_CORE__TST_MUX_SEL1 0x06C9 + + + + + + + +#define VL53L1_RANGING_CORE__TST_MUX_SEL2 0x06CA + + + + + + + +#define VL53L1_RANGING_CORE__TST_MUX 0x06CB + + + + + + + +#define VL53L1_RANGING_CORE__GPIO_OUT_TESTMUX 0x06CC + + + + + + + +#define VL53L1_RANGING_CORE__CUSTOM_FE 0x06CD + + + + + + + +#define VL53L1_RANGING_CORE__CUSTOM_FE_2 0x06CE + + + + + + + +#define VL53L1_RANGING_CORE__SPAD_READOUT 0x06CF + + + + + + + +#define VL53L1_RANGING_CORE__SPAD_READOUT_1 0x06D0 + + + + + + + +#define VL53L1_RANGING_CORE__SPAD_READOUT_2 0x06D1 + + + + + + + +#define VL53L1_RANGING_CORE__SPAD_PS 0x06D2 + + + + + + + +#define VL53L1_RANGING_CORE__LASER_SAFETY_2 0x06D4 + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__MODE 0x0780 + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__PDN 0x0781 + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__PROGN 0x0782 + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__READN 0x0783 + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__PULSE_WIDTH_MSB 0x0784 + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__PULSE_WIDTH_LSB 0x0785 + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__HV_RISE_MSB 0x0786 + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__HV_RISE_LSB 0x0787 + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__HV_FALL_MSB 0x0788 + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__HV_FALL_LSB 0x0789 + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__TST 0x078A + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__TESTREAD 0x078B + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__DATAIN_MMM 0x078C + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__DATAIN_LMM 0x078D + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__DATAIN_LLM 0x078E + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__DATAIN_LLL 0x078F + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__DATAOUT_MMM 0x0790 + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__DATAOUT_LMM 0x0791 + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__DATAOUT_LLM 0x0792 + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__DATAOUT_LLL 0x0793 + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__ADDR 0x0794 + + + + + + + +#define VL53L1_RANGING_CORE__NVM_CTRL__DATAOUT_ECC 0x0795 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_0 0x0796 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_1 0x0797 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_2 0x0798 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_3 0x0799 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_4 0x079A + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_5 0x079B + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_6 0x079C + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_7 0x079D + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_8 0x079E + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_9 0x079F + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_10 0x07A0 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_11 0x07A1 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_12 0x07A2 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_13 0x07A3 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_14 0x07A4 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_15 0x07A5 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_16 0x07A6 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_17 0x07A7 + + + + + + + +#define VL53L1_RANGING_CORE__SPAD_SHIFT_EN 0x07BA + + + + + + + +#define VL53L1_RANGING_CORE__SPAD_DISABLE_CTRL 0x07BB + + + + + + + +#define VL53L1_RANGING_CORE__SPAD_EN_SHIFT_OUT_DEBUG 0x07BC + + + + + + + +#define VL53L1_RANGING_CORE__SPI_MODE 0x07BD + + + + + + + +#define VL53L1_RANGING_CORE__GPIO_DIR 0x07BE + + + + + + + +#define VL53L1_RANGING_CORE__VCSEL_PERIOD 0x0880 + + + + + + + +#define VL53L1_RANGING_CORE__VCSEL_START 0x0881 + + + + + + + +#define VL53L1_RANGING_CORE__VCSEL_STOP 0x0882 + + + + + + + +#define VL53L1_RANGING_CORE__VCSEL_1 0x0885 + + + + + + + +#define VL53L1_RANGING_CORE__VCSEL_STATUS 0x088D + + + + + + + +#define VL53L1_RANGING_CORE__STATUS 0x0980 + + + + + + + +#define VL53L1_RANGING_CORE__LASER_CONTINUITY_STATE 0x0981 + + + + + + + +#define VL53L1_RANGING_CORE__RANGE_1_MMM 0x0982 + + + + + + + +#define VL53L1_RANGING_CORE__RANGE_1_LMM 0x0983 + + + + + + + +#define VL53L1_RANGING_CORE__RANGE_1_LLM 0x0984 + + + + + + + +#define VL53L1_RANGING_CORE__RANGE_1_LLL 0x0985 + + + + + + + +#define VL53L1_RANGING_CORE__RANGE_REF_1_MMM 0x0986 + + + + + + + +#define VL53L1_RANGING_CORE__RANGE_REF_1_LMM 0x0987 + + + + + + + +#define VL53L1_RANGING_CORE__RANGE_REF_1_LLM 0x0988 + + + + + + + +#define VL53L1_RANGING_CORE__RANGE_REF_1_LLL 0x0989 + + + + + + + +#define VL53L1_RANGING_CORE__AMBIENT_WINDOW_EVENTS_1_MMM 0x098A + + + + + + + +#define VL53L1_RANGING_CORE__AMBIENT_WINDOW_EVENTS_1_LMM 0x098B + + + + + + + +#define VL53L1_RANGING_CORE__AMBIENT_WINDOW_EVENTS_1_LLM 0x098C + + + + + + + +#define VL53L1_RANGING_CORE__AMBIENT_WINDOW_EVENTS_1_LLL 0x098D + + + + + + + +#define VL53L1_RANGING_CORE__RANGING_TOTAL_EVENTS_1_MMM 0x098E + + + + + + + +#define VL53L1_RANGING_CORE__RANGING_TOTAL_EVENTS_1_LMM 0x098F + + + + + + + +#define VL53L1_RANGING_CORE__RANGING_TOTAL_EVENTS_1_LLM 0x0990 + + + + + + + +#define VL53L1_RANGING_CORE__RANGING_TOTAL_EVENTS_1_LLL 0x0991 + + + + + + + +#define VL53L1_RANGING_CORE__SIGNAL_TOTAL_EVENTS_1_MMM 0x0992 + + + + + + + +#define VL53L1_RANGING_CORE__SIGNAL_TOTAL_EVENTS_1_LMM 0x0993 + + + + + + + +#define VL53L1_RANGING_CORE__SIGNAL_TOTAL_EVENTS_1_LLM 0x0994 + + + + + + + +#define VL53L1_RANGING_CORE__SIGNAL_TOTAL_EVENTS_1_LLL 0x0995 + + + + + + + +#define VL53L1_RANGING_CORE__TOTAL_PERIODS_ELAPSED_1_MM 0x0996 + + + + + + + +#define VL53L1_RANGING_CORE__TOTAL_PERIODS_ELAPSED_1_LM 0x0997 + + + + + + + +#define VL53L1_RANGING_CORE__TOTAL_PERIODS_ELAPSED_1_LL 0x0998 + + + + + + + +#define VL53L1_RANGING_CORE__AMBIENT_MISMATCH_MM 0x0999 + + + + + + + +#define VL53L1_RANGING_CORE__AMBIENT_MISMATCH_LM 0x099A + + + + + + + +#define VL53L1_RANGING_CORE__AMBIENT_MISMATCH_LL 0x099B + + + + + + + +#define VL53L1_RANGING_CORE__AMBIENT_WINDOW_EVENTS_REF_1_MMM 0x099C + + + + + + + +#define VL53L1_RANGING_CORE__AMBIENT_WINDOW_EVENTS_REF_1_LMM 0x099D + + + + + + + +#define VL53L1_RANGING_CORE__AMBIENT_WINDOW_EVENTS_REF_1_LLM 0x099E + + + + + + + +#define VL53L1_RANGING_CORE__AMBIENT_WINDOW_EVENTS_REF_1_LLL 0x099F + + + + + + + +#define VL53L1_RANGING_CORE__RANGING_TOTAL_EVENTS_REF_1_MMM 0x09A0 + + + + + + + +#define VL53L1_RANGING_CORE__RANGING_TOTAL_EVENTS_REF_1_LMM 0x09A1 + + + + + + + +#define VL53L1_RANGING_CORE__RANGING_TOTAL_EVENTS_REF_1_LLM 0x09A2 + + + + + + + +#define VL53L1_RANGING_CORE__RANGING_TOTAL_EVENTS_REF_1_LLL 0x09A3 + + + + + + + +#define VL53L1_RANGING_CORE__SIGNAL_TOTAL_EVENTS_REF_1_MMM 0x09A4 + + + + + + + +#define VL53L1_RANGING_CORE__SIGNAL_TOTAL_EVENTS_REF_1_LMM 0x09A5 + + + + + + + +#define VL53L1_RANGING_CORE__SIGNAL_TOTAL_EVENTS_REF_1_LLM 0x09A6 + + + + + + + +#define VL53L1_RANGING_CORE__SIGNAL_TOTAL_EVENTS_REF_1_LLL 0x09A7 + + + + + + + +#define VL53L1_RANGING_CORE__TOTAL_PERIODS_ELAPSED_REF_1_MM 0x09A8 + + + + + + + +#define VL53L1_RANGING_CORE__TOTAL_PERIODS_ELAPSED_REF_1_LM 0x09A9 + + + + + + + +#define VL53L1_RANGING_CORE__TOTAL_PERIODS_ELAPSED_REF_1_LL 0x09AA + + + + + + + +#define VL53L1_RANGING_CORE__AMBIENT_MISMATCH_REF_MM 0x09AB + + + + + + + +#define VL53L1_RANGING_CORE__AMBIENT_MISMATCH_REF_LM 0x09AC + + + + + + + +#define VL53L1_RANGING_CORE__AMBIENT_MISMATCH_REF_LL 0x09AD + + + + + + + +#define VL53L1_RANGING_CORE__GPIO_CONFIG__A0 0x0A00 + + + + + + + +#define VL53L1_RANGING_CORE__RESET_CONTROL__A0 0x0A01 + + + + + + + +#define VL53L1_RANGING_CORE__INTR_MANAGER__A0 0x0A02 + + + + + + + +#define VL53L1_RANGING_CORE__POWER_FSM_TIME_OSC__A0 0x0A06 + + + + + + + +#define VL53L1_RANGING_CORE__VCSEL_ATEST__A0 0x0A07 + + + + + + + +#define VL53L1_RANGING_CORE__VCSEL_PERIOD_CLIPPED__A0 0x0A08 + + + + + + + +#define VL53L1_RANGING_CORE__VCSEL_STOP_CLIPPED__A0 0x0A09 + + + + + + + +#define VL53L1_RANGING_CORE__CALIB_2__A0 0x0A0A + + + + + + + +#define VL53L1_RANGING_CORE__STOP_CONDITION__A0 0x0A0B + + + + + + + +#define VL53L1_RANGING_CORE__STATUS_RESET__A0 0x0A0C + + + + + + + +#define VL53L1_RANGING_CORE__READOUT_CFG__A0 0x0A0D + + + + + + + +#define VL53L1_RANGING_CORE__WINDOW_SETTING__A0 0x0A0E + + + + + + + +#define VL53L1_RANGING_CORE__VCSEL_DELAY__A0 0x0A1A + + + + + + + +#define VL53L1_RANGING_CORE__REFERENCE_2__A0 0x0A1B + + + + + + + +#define VL53L1_RANGING_CORE__REGAVDD1V2__A0 0x0A1D + + + + + + + +#define VL53L1_RANGING_CORE__TST_MUX__A0 0x0A1F + + + + + + + +#define VL53L1_RANGING_CORE__CUSTOM_FE_2__A0 0x0A20 + + + + + + + +#define VL53L1_RANGING_CORE__SPAD_READOUT__A0 0x0A21 + + + + + + + +#define VL53L1_RANGING_CORE__CPUMP_1__A0 0x0A22 + + + + + + + +#define VL53L1_RANGING_CORE__SPARE_REGISTER__A0 0x0A23 + + + + + + + +#define VL53L1_RANGING_CORE__VCSEL_CONT_STAGE5_BYPASS__A0 0x0A24 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_18 0x0A25 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_19 0x0A26 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_20 0x0A27 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_21 0x0A28 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_22 0x0A29 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_23 0x0A2A + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_24 0x0A2B + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_25 0x0A2C + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_26 0x0A2D + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_27 0x0A2E + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_28 0x0A2F + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_29 0x0A30 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_30 0x0A31 + + + + + + + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_31 0x0A32 + + + + + + + +#define VL53L1_RANGING_CORE__REF_SPAD_EN_0__EWOK 0x0A33 + + + + + + + +#define VL53L1_RANGING_CORE__REF_SPAD_EN_1__EWOK 0x0A34 + + + + + + + +#define VL53L1_RANGING_CORE__REF_SPAD_EN_2__EWOK 0x0A35 + + + + + + + +#define VL53L1_RANGING_CORE__REF_SPAD_EN_3__EWOK 0x0A36 + + + + + + + +#define VL53L1_RANGING_CORE__REF_SPAD_EN_4__EWOK 0x0A37 + + + + + + + +#define VL53L1_RANGING_CORE__REF_SPAD_EN_5__EWOK 0x0A38 + + + + + + + +#define VL53L1_RANGING_CORE__REF_EN_START_SELECT 0x0A39 + + + + + + + +#define VL53L1_RANGING_CORE__REGDVDD1V2_ATEST__EWOK 0x0A41 + + + + + + + +#define VL53L1_SOFT_RESET_GO1 0x0B00 + + + + + + + +#define VL53L1_PRIVATE__PATCH_BASE_ADDR_RSLV 0x0E00 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__INTERRUPT_STATUS 0x0ED0 + + + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__RANGE_STATUS 0x0ED1 + + + + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__REPORT_STATUS 0x0ED2 + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__STREAM_COUNT 0x0ED3 + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0 0x0ED4 + + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_HI 0x0ED4 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_LO 0x0ED5 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0 0x0ED6 + + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_HI 0x0ED6 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_LO 0x0ED7 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0 0x0ED8 + + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_HI 0x0ED8 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_LO 0x0ED9 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__SIGMA_SD0 0x0EDA + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__SIGMA_SD0_HI 0x0EDA + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__SIGMA_SD0_LO 0x0EDB + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__PHASE_SD0 0x0EDC + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__PHASE_SD0_HI 0x0EDC + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__PHASE_SD0_LO 0x0EDD + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 0x0EDE + + + + + + + + + + + + + + + +#define VL53L1_PREV__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_HI 0x0EDE + + + + + + + +#define VL53L1_PREV__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_LO 0x0EDF + + + + + + + +#define VL53L1_PREV__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0 0x0EE0 + + + + + + + + + + + + + + + + +#define VL53L1_PPEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_HI 0x0EE0 + + + + + + + +#define VL53L1_PPEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_LO 0x0EE1 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0 0x0EE2 + + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_HI 0x0EE2 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_LO 0x0EE3 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0 0x0EE4 + + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_HI 0x0EE4 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_LO 0x0EE5 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0 0x0EE6 + + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_HI 0x0EE6 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_LO 0x0EE7 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1 0x0EE8 + + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_HI 0x0EE8 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_LO 0x0EE9 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1 0x0EEA + + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_HI 0x0EEA + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_LO 0x0EEB + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1 0x0EEC + + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_HI 0x0EEC + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_LO 0x0EED + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__SIGMA_SD1 0x0EEE + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__SIGMA_SD1_HI 0x0EEE + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__SIGMA_SD1_LO 0x0EEF + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__PHASE_SD1 0x0EF0 + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__PHASE_SD1_HI 0x0EF0 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__PHASE_SD1_LO 0x0EF1 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1 0x0EF2 + + + + + + + + + + + + + + + + +#define VL53L1_PFINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_HI 0x0EF2 + + + + + + + +#define VL53L1_PFINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_LO 0x0EF3 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_0_SD1 0x0EF4 + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_0_SD1_HI 0x0EF4 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_0_SD1_LO 0x0EF5 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_1_SD1 0x0EF6 + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_1_SD1_HI 0x0EF6 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_1_SD1_LO 0x0EF7 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_2_SD1 0x0EF8 + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_2_SD1_HI 0x0EF8 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_2_SD1_LO 0x0EF9 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_3_SD1 0x0EFA + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_3_SD1_HI 0x0EFA + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_3_SD1_LO 0x0EFB + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0 0x0EFC + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_3 0x0EFC + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_2 0x0EFD + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_1 0x0EFE + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_0 0x0EFF + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0 0x0F00 + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_3 0x0F00 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_2 0x0F01 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_1 0x0F02 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_0 0x0F03 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0 0x0F04 + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_3 0x0F04 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_2 0x0F05 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_1 0x0F06 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_0 0x0F07 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0 0x0F08 + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_3 0x0F08 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_2 0x0F09 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_1 0x0F0A + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_0 0x0F0B + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1 0x0F0C + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_3 0x0F0C + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_2 0x0F0D + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_1 0x0F0E + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_0 0x0F0F + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1 0x0F10 + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_3 0x0F10 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_2 0x0F11 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_1 0x0F12 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_0 0x0F13 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1 0x0F14 + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_3 0x0F14 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_2 0x0F15 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_1 0x0F16 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_0 0x0F17 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1 0x0F18 + + + + + + + + + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_3 0x0F18 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_2 0x0F19 + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_1 0x0F1A + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_0 0x0F1B + + + + + + + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SPARE_0 0x0F1C + + + + + + + + + + + + + + + +#define VL53L1_RESULT__DEBUG_STATUS 0x0F20 + + + + + + + + + + + + + + + +#define VL53L1_RESULT__DEBUG_STAGE 0x0F21 + + + + + + + + + + + + + + + +#define VL53L1_GPH__SYSTEM__THRESH_RATE_HIGH 0x0F24 + + + + + + + + + + + + + + + +#define VL53L1_GPH__SYSTEM__THRESH_RATE_HIGH_HI 0x0F24 + + + + + + + +#define VL53L1_GPH__SYSTEM__THRESH_RATE_HIGH_LO 0x0F25 + + + + + + + +#define VL53L1_GPH__SYSTEM__THRESH_RATE_LOW 0x0F26 + + + + + + + + + + + + + + + +#define VL53L1_GPH__SYSTEM__THRESH_RATE_LOW_HI 0x0F26 + + + + + + + +#define VL53L1_GPH__SYSTEM__THRESH_RATE_LOW_LO 0x0F27 + + + + + + + +#define VL53L1_GPH__SYSTEM__INTERRUPT_CONFIG_GPIO 0x0F28 + + + + + + + + + + + + + + + + + + + + +#define VL53L1_GPH__DSS_CONFIG__ROI_MODE_CONTROL 0x0F2F + + + + + + + + + + + + + + + + +#define VL53L1_GPH__DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT 0x0F30 + + + + + + + + + + + + + + + +#define VL53L1_GPH__DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT_HI 0x0F30 + + + + + + + +#define VL53L1_GPH__DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT_LO 0x0F31 + + + + + + + +#define VL53L1_GPH__DSS_CONFIG__MANUAL_BLOCK_SELECT 0x0F32 + + + + + + + + + + + + + + + +#define VL53L1_GPH__DSS_CONFIG__MAX_SPADS_LIMIT 0x0F33 + + + + + + + + + + + + + + + +#define VL53L1_GPH__DSS_CONFIG__MIN_SPADS_LIMIT 0x0F34 + + + + + + + + + + + + + + + +#define VL53L1_GPH__MM_CONFIG__TIMEOUT_MACROP_A_HI 0x0F36 + + + + + + + + + + + + + + + +#define VL53L1_GPH__MM_CONFIG__TIMEOUT_MACROP_A_LO 0x0F37 + + + + + + + + + + + + + + + +#define VL53L1_GPH__MM_CONFIG__TIMEOUT_MACROP_B_HI 0x0F38 + + + + + + + + + + + + + + + +#define VL53L1_GPH__MM_CONFIG__TIMEOUT_MACROP_B_LO 0x0F39 + + + + + + + + + + + + + + + +#define VL53L1_GPH__RANGE_CONFIG__TIMEOUT_MACROP_A_HI 0x0F3A + + + + + + + + + + + + + + + +#define VL53L1_GPH__RANGE_CONFIG__TIMEOUT_MACROP_A_LO 0x0F3B + + + + + + + + + + + + + + + +#define VL53L1_GPH__RANGE_CONFIG__VCSEL_PERIOD_A 0x0F3C + + + + + + + + + + + + + + + +#define VL53L1_GPH__RANGE_CONFIG__VCSEL_PERIOD_B 0x0F3D + + + + + + + + + + + + + + + +#define VL53L1_GPH__RANGE_CONFIG__TIMEOUT_MACROP_B_HI 0x0F3E + + + + + + + + + + + + + + + +#define VL53L1_GPH__RANGE_CONFIG__TIMEOUT_MACROP_B_LO 0x0F3F + + + + + + + + + + + + + + + +#define VL53L1_GPH__RANGE_CONFIG__SIGMA_THRESH 0x0F40 + + + + + + + + + + + + + + + +#define VL53L1_GPH__RANGE_CONFIG__SIGMA_THRESH_HI 0x0F40 + + + + + + + +#define VL53L1_GPH__RANGE_CONFIG__SIGMA_THRESH_LO 0x0F41 + + + + + + + +#define VL53L1_GPH__RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS 0x0F42 + + + + + + + + + + + + + + + + +#define VL53L1_GPH__RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS_HI 0x0F42 + + + + + + + +#define VL53L1_GPH__RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS_LO 0x0F43 + + + + + + + +#define VL53L1_GPH__RANGE_CONFIG__VALID_PHASE_LOW 0x0F44 + + + + + + + + + + + + + + + +#define VL53L1_GPH__RANGE_CONFIG__VALID_PHASE_HIGH 0x0F45 + + + + + + + + + + + + + + + +#define VL53L1_FIRMWARE__INTERNAL_STREAM_COUNT_DIV 0x0F46 + + + + + + + + + + + + + + + +#define VL53L1_FIRMWARE__INTERNAL_STREAM_COUNTER_VAL 0x0F47 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__ROI_CTRL 0x0F54 + + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__SPARE_1 0x0F55 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__SPARE_2 0x0F56 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__SPARE_3 0x0F57 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__SPARE_4 0x0F58 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__SPARE_5 0x0F59 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__SPARE_6 0x0F5A + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__SPARE_7 0x0F5B + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_0 0x0F5C + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_1 0x0F5D + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_2 0x0F5E + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_3 0x0F5F + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_4 0x0F60 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_5 0x0F61 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_6 0x0F62 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_7 0x0F63 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_8 0x0F64 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_9 0x0F65 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_10 0x0F66 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_11 0x0F67 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_12 0x0F68 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_13 0x0F69 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_14 0x0F6A + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_15 0x0F6B + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_16 0x0F6C + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_17 0x0F6D + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_18 0x0F6E + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_19 0x0F6F + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_20 0x0F70 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_21 0x0F71 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_22 0x0F72 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_23 0x0F73 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_24 0x0F74 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_25 0x0F75 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_26 0x0F76 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_27 0x0F77 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_28 0x0F78 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_29 0x0F79 + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_30 0x0F7A + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_31 0x0F7B + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_0 0x0F7C + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__USER_ROI_1 0x0F7D + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__MODE_ROI_0 0x0F7E + + + + + + + + + + + + + + + +#define VL53L1_DSS_CALC__MODE_ROI_1 0x0F7F + + + + + + + + + + + + + + + +#define VL53L1_SIGMA_ESTIMATOR_CALC__SPARE_0 0x0F80 + + + + + + + + + + + + + + + +#define VL53L1_VHV_RESULT__PEAK_SIGNAL_RATE_MCPS 0x0F82 + + + + + + + + + + + + + + + +#define VL53L1_VHV_RESULT__PEAK_SIGNAL_RATE_MCPS_HI 0x0F82 + + + + + + + +#define VL53L1_VHV_RESULT__PEAK_SIGNAL_RATE_MCPS_LO 0x0F83 + + + + + + + +#define VL53L1_VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF 0x0F84 + + + + + + + + + + + + + + + +#define VL53L1_VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF_3 0x0F84 + + + + + + + +#define VL53L1_VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF_2 0x0F85 + + + + + + + +#define VL53L1_VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF_1 0x0F86 + + + + + + + +#define VL53L1_VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF_0 0x0F87 + + + + + + + +#define VL53L1_PHASECAL_RESULT__PHASE_OUTPUT_REF 0x0F88 + + + + + + + + + + + + + + + +#define VL53L1_PHASECAL_RESULT__PHASE_OUTPUT_REF_HI 0x0F88 + + + + + + + +#define VL53L1_PHASECAL_RESULT__PHASE_OUTPUT_REF_LO 0x0F89 + + + + + + + +#define VL53L1_DSS_RESULT__TOTAL_RATE_PER_SPAD 0x0F8A + + + + + + + + + + + + + + + +#define VL53L1_DSS_RESULT__TOTAL_RATE_PER_SPAD_HI 0x0F8A + + + + + + + +#define VL53L1_DSS_RESULT__TOTAL_RATE_PER_SPAD_LO 0x0F8B + + + + + + + +#define VL53L1_DSS_RESULT__ENABLED_BLOCKS 0x0F8C + + + + + + + + + + + + + + + +#define VL53L1_DSS_RESULT__NUM_REQUESTED_SPADS 0x0F8E + + + + + + + + + + + + + + + +#define VL53L1_DSS_RESULT__NUM_REQUESTED_SPADS_HI 0x0F8E + + + + + + + +#define VL53L1_DSS_RESULT__NUM_REQUESTED_SPADS_LO 0x0F8F + + + + + + + +#define VL53L1_MM_RESULT__INNER_INTERSECTION_RATE 0x0F92 + + + + + + + + + + + + + + + +#define VL53L1_MM_RESULT__INNER_INTERSECTION_RATE_HI 0x0F92 + + + + + + + +#define VL53L1_MM_RESULT__INNER_INTERSECTION_RATE_LO 0x0F93 + + + + + + + +#define VL53L1_MM_RESULT__OUTER_COMPLEMENT_RATE 0x0F94 + + + + + + + + + + + + + + + +#define VL53L1_MM_RESULT__OUTER_COMPLEMENT_RATE_HI 0x0F94 + + + + + + + +#define VL53L1_MM_RESULT__OUTER_COMPLEMENT_RATE_LO 0x0F95 + + + + + + + +#define VL53L1_MM_RESULT__TOTAL_OFFSET 0x0F96 + + + + + + + + + + + + + + + +#define VL53L1_MM_RESULT__TOTAL_OFFSET_HI 0x0F96 + + + + + + + +#define VL53L1_MM_RESULT__TOTAL_OFFSET_LO 0x0F97 + + + + + + + +#define VL53L1_XTALK_CALC__XTALK_FOR_ENABLED_SPADS 0x0F98 + + + + + + + + + + + + + + + +#define VL53L1_XTALK_CALC__XTALK_FOR_ENABLED_SPADS_3 0x0F98 + + + + + + + +#define VL53L1_XTALK_CALC__XTALK_FOR_ENABLED_SPADS_2 0x0F99 + + + + + + + +#define VL53L1_XTALK_CALC__XTALK_FOR_ENABLED_SPADS_1 0x0F9A + + + + + + + +#define VL53L1_XTALK_CALC__XTALK_FOR_ENABLED_SPADS_0 0x0F9B + + + + + + + +#define VL53L1_XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS 0x0F9C + + + + + + + + + + + + + + + + +#define VL53L1_XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS_3 0x0F9C + + + + + + + +#define VL53L1_XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS_2 0x0F9D + + + + + + + +#define VL53L1_XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS_1 0x0F9E + + + + + + + +#define VL53L1_XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS_0 0x0F9F + + + + + + + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS 0x0FA0 + + + + + + + + + + + + + + + + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS_3 0x0FA0 + + + + + + + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS_2 0x0FA1 + + + + + + + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS_1 0x0FA2 + + + + + + + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS_0 0x0FA3 + + + + + + + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS 0x0FA4 + + + + + + + + + + + + + + + + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS_3 0x0FA4 + + + + + + + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS_2 0x0FA5 + + + + + + + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS_1 0x0FA6 + + + + + + + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS_0 0x0FA7 + + + + + + + +#define VL53L1_RANGE_RESULT__ACCUM_PHASE 0x0FA8 + + + + + + + + + + + + + + + +#define VL53L1_RANGE_RESULT__ACCUM_PHASE_3 0x0FA8 + + + + + + + +#define VL53L1_RANGE_RESULT__ACCUM_PHASE_2 0x0FA9 + + + + + + + +#define VL53L1_RANGE_RESULT__ACCUM_PHASE_1 0x0FAA + + + + + + + +#define VL53L1_RANGE_RESULT__ACCUM_PHASE_0 0x0FAB + + + + + + + +#define VL53L1_RANGE_RESULT__OFFSET_CORRECTED_RANGE 0x0FAC + + + + + + + + + + + + + + + +#define VL53L1_RANGE_RESULT__OFFSET_CORRECTED_RANGE_HI 0x0FAC + + + + + + + +#define VL53L1_RANGE_RESULT__OFFSET_CORRECTED_RANGE_LO 0x0FAD + + + + + + + +#define VL53L1_SHADOW_PHASECAL_RESULT__VCSEL_START 0x0FAE + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__INTERRUPT_STATUS 0x0FB0 + + + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__RANGE_STATUS 0x0FB1 + + + + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__REPORT_STATUS 0x0FB2 + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__STREAM_COUNT 0x0FB3 + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0 0x0FB4 + + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_HI 0x0FB4 + + + + + + + +#define VL53L1_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_LO 0x0FB5 + + + + + + + +#define VL53L1_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0 0x0FB6 + + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_HI 0x0FB6 + + + + + + + +#define VL53L1_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_LO 0x0FB7 + + + + + + + +#define VL53L1_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0 0x0FB8 + + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_HI 0x0FB8 + + + + + + + +#define VL53L1_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_LO 0x0FB9 + + + + + + + +#define VL53L1_SHADOW_RESULT__SIGMA_SD0 0x0FBA + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__SIGMA_SD0_HI 0x0FBA + + + + + + + +#define VL53L1_SHADOW_RESULT__SIGMA_SD0_LO 0x0FBB + + + + + + + +#define VL53L1_SHADOW_RESULT__PHASE_SD0 0x0FBC + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__PHASE_SD0_HI 0x0FBC + + + + + + + +#define VL53L1_SHADOW_RESULT__PHASE_SD0_LO 0x0FBD + + + + + + + +#define VL53L1_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 0x0FBE + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_HI 0x0FBE + + + + + + + +#define VL53L1_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_LO 0x0FBF + + + + + + + +#define VL53L1_SHPEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0 0x0FC0 + + + + + + + + + + + + + + + + +#define VL53L1_SHPEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_HI 0x0FC0 + + + + + + + +#define VL53L1_SHPEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_LO 0x0FC1 + + + + + + + +#define VL53L1_SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0 0x0FC2 + + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_HI 0x0FC2 + + + + + + + +#define VL53L1_SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_LO 0x0FC3 + + + + + + + +#define VL53L1_SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0 0x0FC4 + + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_HI 0x0FC4 + + + + + + + +#define VL53L1_SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_LO 0x0FC5 + + + + + + + +#define VL53L1_SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0 0x0FC6 + + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_HI 0x0FC6 + + + + + + + +#define VL53L1_SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_LO 0x0FC7 + + + + + + + +#define VL53L1_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1 0x0FC8 + + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_HI 0x0FC8 + + + + + + + +#define VL53L1_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_LO 0x0FC9 + + + + + + + +#define VL53L1_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1 0x0FCA + + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_HI 0x0FCA + + + + + + + +#define VL53L1_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_LO 0x0FCB + + + + + + + +#define VL53L1_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1 0x0FCC + + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_HI 0x0FCC + + + + + + + +#define VL53L1_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_LO 0x0FCD + + + + + + + +#define VL53L1_SHADOW_RESULT__SIGMA_SD1 0x0FCE + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__SIGMA_SD1_HI 0x0FCE + + + + + + + +#define VL53L1_SHADOW_RESULT__SIGMA_SD1_LO 0x0FCF + + + + + + + +#define VL53L1_SHADOW_RESULT__PHASE_SD1 0x0FD0 + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__PHASE_SD1_HI 0x0FD0 + + + + + + + +#define VL53L1_SHADOW_RESULT__PHASE_SD1_LO 0x0FD1 + + + + + + + +#define VL53L1_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1 0x0FD2 + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_HI 0x0FD2 + + + + + + + +#define VL53L1_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_LO 0x0FD3 + + + + + + + +#define VL53L1_SHADOW_RESULT__SPARE_0_SD1 0x0FD4 + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__SPARE_0_SD1_HI 0x0FD4 + + + + + + + +#define VL53L1_SHADOW_RESULT__SPARE_0_SD1_LO 0x0FD5 + + + + + + + +#define VL53L1_SHADOW_RESULT__SPARE_1_SD1 0x0FD6 + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__SPARE_1_SD1_HI 0x0FD6 + + + + + + + +#define VL53L1_SHADOW_RESULT__SPARE_1_SD1_LO 0x0FD7 + + + + + + + +#define VL53L1_SHADOW_RESULT__SPARE_2_SD1 0x0FD8 + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__SPARE_2_SD1_HI 0x0FD8 + + + + + + + +#define VL53L1_SHADOW_RESULT__SPARE_2_SD1_LO 0x0FD9 + + + + + + + +#define VL53L1_SHADOW_RESULT__SPARE_3_SD1 0x0FDA + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT__THRESH_INFO 0x0FDB + + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0 0x0FDC + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_3 0x0FDC + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_2 0x0FDD + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_1 0x0FDE + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_0 0x0FDF + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0 0x0FE0 + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_3 0x0FE0 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_2 0x0FE1 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_1 0x0FE2 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_0 0x0FE3 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0 0x0FE4 + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_3 0x0FE4 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_2 0x0FE5 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_1 0x0FE6 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_0 0x0FE7 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0 0x0FE8 + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_3 0x0FE8 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_2 0x0FE9 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_1 0x0FEA + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_0 0x0FEB + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1 0x0FEC + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_3 0x0FEC + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_2 0x0FED + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_1 0x0FEE + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_0 0x0FEF + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1 0x0FF0 + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_3 0x0FF0 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_2 0x0FF1 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_1 0x0FF2 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_0 0x0FF3 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1 0x0FF4 + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_3 0x0FF4 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_2 0x0FF5 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_1 0x0FF6 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_0 0x0FF7 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1 0x0FF8 + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_3 0x0FF8 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_2 0x0FF9 + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_1 0x0FFA + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_0 0x0FFB + + + + + + + +#define VL53L1_SHADOW_RESULT_CORE__SPARE_0 0x0FFC + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_PHASECAL_RESULT__REFERENCE_PHASE_HI 0x0FFE + + + + + + + + + + + + + + + +#define VL53L1_SHADOW_PHASECAL_RESULT__REFERENCE_PHASE_LO 0x0FFF + + + + + + + + + + + + + + + + + + + + +#endif + + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_register_settings.h b/drivers/input/misc/vl53L1/inc/vl53l1_register_settings.h new file mode 100644 index 000000000000..767367f08217 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_register_settings.h @@ -0,0 +1,274 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_REGISTER_SETTINGS_H_ +#define _VL53L1_REGISTER_SETTINGS_H_ + + + + + + + + + + + + + + + + +#define VL53L1_DEVICESCHEDULERMODE_PSEUDO_SOLO 0x00 +#define VL53L1_DEVICESCHEDULERMODE_STREAMING 0x01 +#define VL53L1_DEVICESCHEDULERMODE_HISTOGRAM 0x02 + + + + + + + + + + + +#define VL53L1_DEVICEREADOUTMODE_SINGLE_SD (0x00 << 2) +#define VL53L1_DEVICEREADOUTMODE_DUAL_SD (0x01 << 2) +#define VL53L1_DEVICEREADOUTMODE_SPLIT_READOUT (0x02 << 2) +#define VL53L1_DEVICEREADOUTMODE_SPLIT_MANUAL (0x03 << 2) + + + + + + + + + + + + + + + + + + + +#define VL53L1_DEVICEMEASUREMENTMODE_MODE_MASK 0xF0 +#define VL53L1_DEVICEMEASUREMENTMODE_STOP_MASK 0x0F + +#define VL53L1_GROUPEDPARAMETERHOLD_ID_MASK 0x02 + + + + +#define VL53L1_EWOK_I2C_DEV_ADDR_DEFAULT 0x29 + + +#define VL53L1_OSC_FREQUENCY 0x00 +#define VL53L1_OSC_TRIM_DEFAULT 0x00 +#define VL53L1_OSC_FREQ_SET_DEFAULT 0x00 + +#define VL53L1_RANGE_HISTOGRAM_REF 0x08 +#define VL53L1_RANGE_HISTOGRAM_RET 0x10 +#define VL53L1_RANGE_HISTOGRAM_BOTH 0x18 +#define VL53L1_RANGE_HISTOGRAM_INIT 0x20 +#define VL53L1_RANGE_VHV_INIT 0x40 + + + +#define VL53L1_RESULT_RANGE_STATUS 0x1F + + + +#define VL53L1_SYSTEM__SEED_CONFIG__MANUAL 0x00 +#define VL53L1_SYSTEM__SEED_CONFIG__STANDARD 0x01 +#define VL53L1_SYSTEM__SEED_CONFIG__EVEN_UPDATE_ONLY 0x02 + + + +#define VL53L1_INTERRUPT_CONFIG_LEVEL_LOW 0x00 +#define VL53L1_INTERRUPT_CONFIG_LEVEL_HIGH 0x01 +#define VL53L1_INTERRUPT_CONFIG_OUT_OF_WINDOW 0x02 +#define VL53L1_INTERRUPT_CONFIG_IN_WINDOW 0x03 +#define VL53L1_INTERRUPT_CONFIG_NEW_SAMPLE_READY 0x20 + + + +#define VL53L1_CLEAR_RANGE_INT 0x01 +#define VL53L1_CLEAR_ERROR_INT 0x02 + + + +#define VL53L1_SEQUENCE_VHV_EN 0x01 +#define VL53L1_SEQUENCE_PHASECAL_EN 0x02 +#define VL53L1_SEQUENCE_REFERENCE_PHASE_EN 0x04 +#define VL53L1_SEQUENCE_DSS1_EN 0x08 +#define VL53L1_SEQUENCE_DSS2_EN 0x10 +#define VL53L1_SEQUENCE_MM1_EN 0x20 +#define VL53L1_SEQUENCE_MM2_EN 0x40 +#define VL53L1_SEQUENCE_RANGE_EN 0x80 + + + +#define VL53L1_DSS_CONTROL__ROI_SUBTRACT 0x20 +#define VL53L1_DSS_CONTROL__ROI_INTERSECT 0x10 + +#define VL53L1_DSS_CONTROL__MODE_DISABLED 0x00 +#define VL53L1_DSS_CONTROL__MODE_TARGET_RATE 0x01 +#define VL53L1_DSS_CONTROL__MODE_EFFSPADS 0x02 +#define VL53L1_DSS_CONTROL__MODE_BLOCKSELECT 0x03 + + + + + + + + + + +#define VL53L1_RANGING_CORE__SPAD_READOUT__STANDARD 0x45 +#define VL53L1_RANGING_CORE__SPAD_READOUT__RETURN_ARRAY_ONLY 0x05 +#define VL53L1_RANGING_CORE__SPAD_READOUT__REFERENCE_ARRAY_ONLY 0x55 +#define VL53L1_RANGING_CORE__SPAD_READOUT__RETURN_SPLIT_ARRAY 0x25 +#define VL53L1_RANGING_CORE__SPAD_READOUT__CALIB_PULSES 0xF5 + + +#define VL53L1_LASER_SAFETY__KEY_VALUE 0x6C + + + + + + + + + + +#define VL53L1_RANGE_STATUS__RANGE_STATUS_MASK 0x1F +#define VL53L1_RANGE_STATUS__MAX_THRESHOLD_HIT_MASK 0x20 +#define VL53L1_RANGE_STATUS__MIN_THRESHOLD_HIT_MASK 0x40 +#define VL53L1_RANGE_STATUS__GPH_ID_RANGE_STATUS_MASK 0x80 + + + + + + + + + +#define VL53L1_INTERRUPT_STATUS__INT_STATUS_MASK 0x07 +#define VL53L1_INTERRUPT_STATUS__INT_ERROR_STATUS_MASK 0x18 +#define VL53L1_INTERRUPT_STATUS__GPH_ID_INT_STATUS_MASK 0x20 + + + + + +#endif + + + + + + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_register_structs.h b/drivers/input/misc/vl53L1/inc/vl53l1_register_structs.h new file mode 100644 index 000000000000..c5d70adf68f2 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_register_structs.h @@ -0,0 +1,4872 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_REGISTER_STRUCTS_H_ +#define _VL53L1_REGISTER_STRUCTS_H_ + +#include "vl53l1_types.h" +#include "vl53l1_register_map.h" + +#define VL53L1_STATIC_NVM_MANAGED_I2C_INDEX \ + VL53L1_I2C_SLAVE__DEVICE_ADDRESS +#define VL53L1_CUSTOMER_NVM_MANAGED_I2C_INDEX \ + VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_0 +#define VL53L1_STATIC_CONFIG_I2C_INDEX \ + VL53L1_DSS_CONFIG__TARGET_TOTAL_RATE_MCPS +#define VL53L1_GENERAL_CONFIG_I2C_INDEX \ + VL53L1_GPH_CONFIG__STREAM_COUNT_UPDATE_VALUE +#define VL53L1_TIMING_CONFIG_I2C_INDEX \ + VL53L1_MM_CONFIG__TIMEOUT_MACROP_A_HI +#define VL53L1_DYNAMIC_CONFIG_I2C_INDEX \ + VL53L1_SYSTEM__GROUPED_PARAMETER_HOLD_0 +#define VL53L1_SYSTEM_CONTROL_I2C_INDEX \ + VL53L1_POWER_MANAGEMENT__GO1_POWER_FORCE +#define VL53L1_SYSTEM_RESULTS_I2C_INDEX \ + VL53L1_RESULT__INTERRUPT_STATUS +#define VL53L1_CORE_RESULTS_I2C_INDEX \ + VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0 +#define VL53L1_DEBUG_RESULTS_I2C_INDEX \ + VL53L1_PHASECAL_RESULT__REFERENCE_PHASE +#define VL53L1_NVM_COPY_DATA_I2C_INDEX \ + VL53L1_IDENTIFICATION__MODEL_ID +#define VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_INDEX \ + VL53L1_PREV_SHADOW_RESULT__INTERRUPT_STATUS +#define VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_INDEX \ + VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0 +#define VL53L1_PATCH_DEBUG_I2C_INDEX \ + VL53L1_RESULT__DEBUG_STATUS +#define VL53L1_GPH_GENERAL_CONFIG_I2C_INDEX \ + VL53L1_GPH__SYSTEM__THRESH_RATE_HIGH +#define VL53L1_GPH_STATIC_CONFIG_I2C_INDEX \ + VL53L1_GPH__DSS_CONFIG__ROI_MODE_CONTROL +#define VL53L1_GPH_TIMING_CONFIG_I2C_INDEX \ + VL53L1_GPH__MM_CONFIG__TIMEOUT_MACROP_A_HI +#define VL53L1_FW_INTERNAL_I2C_INDEX \ + VL53L1_FIRMWARE__INTERNAL_STREAM_COUNT_DIV +#define VL53L1_PATCH_RESULTS_I2C_INDEX \ + VL53L1_DSS_CALC__ROI_CTRL +#define VL53L1_SHADOW_SYSTEM_RESULTS_I2C_INDEX \ + VL53L1_SHADOW_PHASECAL_RESULT__VCSEL_START +#define VL53L1_SHADOW_CORE_RESULTS_I2C_INDEX \ + VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0 + +#define VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES 11 +#define VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES 23 +#define VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES 32 +#define VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES 22 +#define VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES 23 +#define VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES 18 +#define VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES 5 +#define VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES 44 +#define VL53L1_CORE_RESULTS_I2C_SIZE_BYTES 33 +#define VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES 56 +#define VL53L1_NVM_COPY_DATA_I2C_SIZE_BYTES 49 +#define VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES 44 +#define VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES 33 +#define VL53L1_PATCH_DEBUG_I2C_SIZE_BYTES 2 +#define VL53L1_GPH_GENERAL_CONFIG_I2C_SIZE_BYTES 5 +#define VL53L1_GPH_STATIC_CONFIG_I2C_SIZE_BYTES 6 +#define VL53L1_GPH_TIMING_CONFIG_I2C_SIZE_BYTES 16 +#define VL53L1_FW_INTERNAL_I2C_SIZE_BYTES 2 +#define VL53L1_PATCH_RESULTS_I2C_SIZE_BYTES 90 +#define VL53L1_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES 82 +#define VL53L1_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES 33 + + + + + + + + + + + + +typedef struct { + uint8_t i2c_slave__device_address; + + + + + + + + + + + uint8_t ana_config__vhv_ref_sel_vddpix; + + + + + + + + + + + uint8_t ana_config__vhv_ref_sel_vquench; + + + + + + + + + + + uint8_t ana_config__reg_avdd1v2_sel; + + + + + + + + + + + uint8_t ana_config__fast_osc__trim; + + + + + + + + + + + uint16_t osc_measured__fast_osc__frequency; + + + + + + + + + + + uint8_t vhv_config__timeout_macrop_loop_bound; + + + + + + + + + + + + uint8_t vhv_config__count_thresh; + + + + + + + + + + + uint8_t vhv_config__offset; + + + + + + + + + + + uint8_t vhv_config__init; + + + + + + + + + + + +} VL53L1_static_nvm_managed_t; + + + + + + + + + + + + +typedef struct { + uint8_t global_config__spad_enables_ref_0; + + + + + + + + + + + uint8_t global_config__spad_enables_ref_1; + + + + + + + + + + + uint8_t global_config__spad_enables_ref_2; + + + + + + + + + + + uint8_t global_config__spad_enables_ref_3; + + + + + + + + + + + uint8_t global_config__spad_enables_ref_4; + + + + + + + + + + + uint8_t global_config__spad_enables_ref_5; + + + + + + + + + + + uint8_t global_config__ref_en_start_select; + + + + + + + + + + + uint8_t ref_spad_man__num_requested_ref_spads; + + + + + + + + + + + uint8_t ref_spad_man__ref_location; + + + + + + + + + + + uint16_t algo__crosstalk_compensation_plane_offset_kcps; + + + + + + + + + + + int16_t algo__crosstalk_compensation_x_plane_gradient_kcps; + + + + + + + + + + + int16_t algo__crosstalk_compensation_y_plane_gradient_kcps; + + + + + + + + + + + uint16_t ref_spad_char__total_rate_target_mcps; + + + + + + + + + + + int16_t algo__part_to_part_range_offset_mm; + + + + + + + + + + + int16_t mm_config__inner_offset_mm; + + + + + + + + + + + int16_t mm_config__outer_offset_mm; + + + + + + + + + + +} VL53L1_customer_nvm_managed_t; + + + + + + + + + + + + +typedef struct { + uint16_t dss_config__target_total_rate_mcps; + + + + + + + + + + + uint8_t debug__ctrl; + + + + + + + + + + + uint8_t test_mode__ctrl; + + + + + + + + + + + uint8_t clk_gating__ctrl; + + + + + + + + + + + + + + uint8_t nvm_bist__ctrl; + + + + + + + + + + + + uint8_t nvm_bist__num_nvm_words; + + + + + + + + + + + uint8_t nvm_bist__start_address; + + + + + + + + + + + uint8_t host_if__status; + + + + + + + + + + + uint8_t pad_i2c_hv__config; + + + + + + + + + + + + + + + + uint8_t pad_i2c_hv__extsup_config; + + + + + + + + + + + uint8_t gpio_hv_pad__ctrl; + + + + + + + + + + + + uint8_t gpio_hv_mux__ctrl; + + + + + + + + + + + + uint8_t gpio__tio_hv_status; + + + + + + + + + + + + uint8_t gpio__fio_hv_status; + + + + + + + + + + + uint8_t ana_config__spad_sel_pswidth; + + + + + + + + + + + uint8_t ana_config__vcsel_pulse_width_offset; + + + + + + + + + + + uint8_t ana_config__fast_osc__config_ctrl; + + + + + + + + + + + uint8_t sigma_estimator__effective_pulse_width_ns; + + + + + + + + + + + uint8_t sigma_estimator__effective_ambient_width_ns; + + + + + + + + + + + uint8_t sigma_estimator__sigma_ref_mm; + + + + + + + + + + + uint8_t algo__crosstalk_compensation_valid_height_mm; + + + + + + + + + + + uint8_t spare_host_config__static_config_spare_0; + + + + + + + + + + + uint8_t spare_host_config__static_config_spare_1; + + + + + + + + + + + uint16_t algo__range_ignore_threshold_mcps; + + + + + + + + + + + uint8_t algo__range_ignore_valid_height_mm; + + + + + + + + + + + uint8_t algo__range_min_clip; + + + + + + + + + + + + uint8_t algo__consistency_check__tolerance; + + + + + + + + + + + uint8_t spare_host_config__static_config_spare_2; + + + + + + + + + + + uint8_t sd_config__reset_stages_msb; + + + + + + + + + + + uint8_t sd_config__reset_stages_lsb; + + + + + + + + + + + +} VL53L1_static_config_t; + + + + + + + + + + + + +typedef struct { + uint8_t gph_config__stream_count_update_value; + + + + + + + + + + + uint8_t global_config__stream_divider; + + + + + + + + + + + uint8_t system__interrupt_config_gpio; + + + + + + + + + + + + + + + + uint8_t cal_config__vcsel_start; + + + + + + + + + + + uint16_t cal_config__repeat_rate; + + + + + + + + + + + uint8_t global_config__vcsel_width; + + + + + + + + + + + uint8_t phasecal_config__timeout_macrop; + + + + + + + + + + + uint8_t phasecal_config__target; + + + + + + + + + + + uint8_t phasecal_config__override; + + + + + + + + + + + uint8_t dss_config__roi_mode_control; + + + + + + + + + + + + uint16_t system__thresh_rate_high; + + + + + + + + + + + uint16_t system__thresh_rate_low; + + + + + + + + + + + uint16_t dss_config__manual_effective_spads_select; + + + + + + + + + + + uint8_t dss_config__manual_block_select; + + + + + + + + + + + uint8_t dss_config__aperture_attenuation; + + + + + + + + + + + uint8_t dss_config__max_spads_limit; + + + + + + + + + + + uint8_t dss_config__min_spads_limit; + + + + + + + + + + +} VL53L1_general_config_t; + + + + + + + + + + + + +typedef struct { + uint8_t mm_config__timeout_macrop_a_hi; + + + + + + + + + + + uint8_t mm_config__timeout_macrop_a_lo; + + + + + + + + + + + uint8_t mm_config__timeout_macrop_b_hi; + + + + + + + + + + + uint8_t mm_config__timeout_macrop_b_lo; + + + + + + + + + + + uint8_t range_config__timeout_macrop_a_hi; + + + + + + + + + + + uint8_t range_config__timeout_macrop_a_lo; + + + + + + + + + + + uint8_t range_config__vcsel_period_a; + + + + + + + + + + + uint8_t range_config__timeout_macrop_b_hi; + + + + + + + + + + + uint8_t range_config__timeout_macrop_b_lo; + + + + + + + + + + + uint8_t range_config__vcsel_period_b; + + + + + + + + + + + uint16_t range_config__sigma_thresh; + + + + + + + + + + + uint16_t range_config__min_count_rate_rtn_limit_mcps; + + + + + + + + + + + uint8_t range_config__valid_phase_low; + + + + + + + + + + + uint8_t range_config__valid_phase_high; + + + + + + + + + + + uint32_t system__intermeasurement_period; + + + + + + + + + + + uint8_t system__fractional_enable; + + + + + + + + + + +} VL53L1_timing_config_t; + + + + + + + + + + + + +typedef struct { + uint8_t system__grouped_parameter_hold_0; + + + + + + + + + + + + uint16_t system__thresh_high; + + + + + + + + + + + uint16_t system__thresh_low; + + + + + + + + + + + uint8_t system__enable_xtalk_per_quadrant; + + + + + + + + + + + uint8_t system__seed_config; + + + + + + + + + + + + uint8_t sd_config__woi_sd0; + + + + + + + + + + + uint8_t sd_config__woi_sd1; + + + + + + + + + + + uint8_t sd_config__initial_phase_sd0; + + + + + + + + + + + uint8_t sd_config__initial_phase_sd1; + + + + + + + + + + + uint8_t system__grouped_parameter_hold_1; + + + + + + + + + + + + uint8_t sd_config__first_order_select; + + + + + + + + + + + + uint8_t sd_config__quantifier; + + + + + + + + + + + uint8_t roi_config__user_roi_centre_spad; + + + + + + + + + + + uint8_t roi_config__user_roi_requested_global_xy_size; + + + + + + + + + + + uint8_t system__sequence_config; + + + + + + + + + + + + + + + + + + uint8_t system__grouped_parameter_hold; + + + + + + + + + + + +} VL53L1_dynamic_config_t; + + + + + + + + + + + + +typedef struct { + uint8_t power_management__go1_power_force; + + + + + + + + + + + uint8_t system__stream_count_ctrl; + + + + + + + + + + + uint8_t firmware__enable; + + + + + + + + + + + uint8_t system__interrupt_clear; + + + + + + + + + + + + uint8_t system__mode_start; + + + + + + + + + + + + + + + +} VL53L1_system_control_t; + + + + + + + + + + + + +typedef struct { + uint8_t result__interrupt_status; + + + + + + + + + + + + + uint8_t result__range_status; + + + + + + + + + + + + + + uint8_t result__report_status; + + + + + + + + + + + uint8_t result__stream_count; + + + + + + + + + + + uint16_t result__dss_actual_effective_spads_sd0; + + + + + + + + + + + uint16_t result__peak_signal_count_rate_mcps_sd0; + + + + + + + + + + + uint16_t result__ambient_count_rate_mcps_sd0; + + + + + + + + + + + uint16_t result__sigma_sd0; + + + + + + + + + + + uint16_t result__phase_sd0; + + + + + + + + + + + uint16_t result__final_crosstalk_corrected_range_mm_sd0; + + + + + + + + + + + uint16_t result__peak_signal_count_rate_crosstalk_corrected_mcps_sd0; + + + + + + + + + + + uint16_t result__mm_inner_actual_effective_spads_sd0; + + + + + + + + + + + uint16_t result__mm_outer_actual_effective_spads_sd0; + + + + + + + + + + + uint16_t result__avg_signal_count_rate_mcps_sd0; + + + + + + + + + + + uint16_t result__dss_actual_effective_spads_sd1; + + + + + + + + + + + uint16_t result__peak_signal_count_rate_mcps_sd1; + + + + + + + + + + + uint16_t result__ambient_count_rate_mcps_sd1; + + + + + + + + + + + uint16_t result__sigma_sd1; + + + + + + + + + + + uint16_t result__phase_sd1; + + + + + + + + + + + uint16_t result__final_crosstalk_corrected_range_mm_sd1; + + + + + + + + + + + uint16_t result__spare_0_sd1; + + + + + + + + + + + uint16_t result__spare_1_sd1; + + + + + + + + + + + uint16_t result__spare_2_sd1; + + + + + + + + + + + uint8_t result__spare_3_sd1; + + + + + + + + + + + uint8_t result__thresh_info; + + + + + + + + + + + +} VL53L1_system_results_t; + + + + + + + + + + + + +typedef struct { + uint32_t result_core__ambient_window_events_sd0; + + + + + + + + + + + uint32_t result_core__ranging_total_events_sd0; + + + + + + + + + + + int32_t result_core__signal_total_events_sd0; + + + + + + + + + + + uint32_t result_core__total_periods_elapsed_sd0; + + + + + + + + + + + uint32_t result_core__ambient_window_events_sd1; + + + + + + + + + + + uint32_t result_core__ranging_total_events_sd1; + + + + + + + + + + + int32_t result_core__signal_total_events_sd1; + + + + + + + + + + + uint32_t result_core__total_periods_elapsed_sd1; + + + + + + + + + + + uint8_t result_core__spare_0; + + + + + + + + + + +} VL53L1_core_results_t; + + + + + + + + + + + + +typedef struct { + uint16_t phasecal_result__reference_phase; + + + + + + + + + + + uint8_t phasecal_result__vcsel_start; + + + + + + + + + + + uint8_t ref_spad_char_result__num_actual_ref_spads; + + + + + + + + + + + uint8_t ref_spad_char_result__ref_location; + + + + + + + + + + + uint8_t vhv_result__coldboot_status; + + + + + + + + + + + uint8_t vhv_result__search_result; + + + + + + + + + + + uint8_t vhv_result__latest_setting; + + + + + + + + + + + uint16_t result__osc_calibrate_val; + + + + + + + + + + + uint8_t ana_config__powerdown_go1; + + + + + + + + + + + + uint8_t ana_config__ref_bg_ctrl; + + + + + + + + + + + + uint8_t ana_config__regdvdd1v2_ctrl; + + + + + + + + + + + + + uint8_t ana_config__osc_slow_ctrl; + + + + + + + + + + + + + uint8_t test_mode__status; + + + + + + + + + + + uint8_t firmware__system_status; + + + + + + + + + + + + uint8_t firmware__mode_status; + + + + + + + + + + + uint8_t firmware__secondary_mode_status; + + + + + + + + + + + uint16_t firmware__cal_repeat_rate_counter; + + + + + + + + + + + uint16_t gph__system__thresh_high; + + + + + + + + + + + uint16_t gph__system__thresh_low; + + + + + + + + + + + uint8_t gph__system__enable_xtalk_per_quadrant; + + + + + + + + + + + uint8_t gph__spare_0; + + + + + + + + + + + + + uint8_t gph__sd_config__woi_sd0; + + + + + + + + + + + uint8_t gph__sd_config__woi_sd1; + + + + + + + + + + + uint8_t gph__sd_config__initial_phase_sd0; + + + + + + + + + + + uint8_t gph__sd_config__initial_phase_sd1; + + + + + + + + + + + uint8_t gph__sd_config__first_order_select; + + + + + + + + + + + + uint8_t gph__sd_config__quantifier; + + + + + + + + + + + uint8_t gph__roi_config__user_roi_centre_spad; + + + + + + + + + + + uint8_t gph__roi_config__user_roi_requested_global_xy_size; + + + + + + + + + + + uint8_t gph__system__sequence_config; + + + + + + + + + + + + + + + + + + uint8_t gph__gph_id; + + + + + + + + + + + uint8_t system__interrupt_set; + + + + + + + + + + + + uint8_t interrupt_manager__enables; + + + + + + + + + + + + + + + uint8_t interrupt_manager__clear; + + + + + + + + + + + + + + + uint8_t interrupt_manager__status; + + + + + + + + + + + + + + + uint8_t mcu_to_host_bank__wr_access_en; + + + + + + + + + + + uint8_t power_management__go1_reset_status; + + + + + + + + + + + uint8_t pad_startup_mode__value_ro; + + + + + + + + + + + + uint8_t pad_startup_mode__value_ctrl; + + + + + + + + + + + + + + uint32_t pll_period_us; + + + + + + + + + + + uint32_t interrupt_scheduler__data_out; + + + + + + + + + + + uint8_t nvm_bist__complete; + + + + + + + + + + + uint8_t nvm_bist__status; + + + + + + + + + + +} VL53L1_debug_results_t; + + + + + + + + + + + + +typedef struct { + uint8_t identification__model_id; + + + + + + + + + + + uint8_t identification__module_type; + + + + + + + + + + + uint8_t identification__revision_id; + + + + + + + + + + + + uint16_t identification__module_id; + + + + + + + + + + + uint8_t ana_config__fast_osc__trim_max; + + + + + + + + + + + uint8_t ana_config__fast_osc__freq_set; + + + + + + + + + + + uint8_t ana_config__vcsel_trim; + + + + + + + + + + + uint8_t ana_config__vcsel_selion; + + + + + + + + + + + uint8_t ana_config__vcsel_selion_max; + + + + + + + + + + + uint8_t protected_laser_safety__lock_bit; + + + + + + + + + + + uint8_t laser_safety__key; + + + + + + + + + + + uint8_t laser_safety__key_ro; + + + + + + + + + + + uint8_t laser_safety__clip; + + + + + + + + + + + uint8_t laser_safety__mult; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_0; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_1; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_2; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_3; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_4; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_5; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_6; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_7; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_8; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_9; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_10; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_11; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_12; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_13; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_14; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_15; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_16; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_17; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_18; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_19; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_20; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_21; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_22; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_23; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_24; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_25; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_26; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_27; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_28; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_29; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_30; + + + + + + + + + + + uint8_t global_config__spad_enables_rtn_31; + + + + + + + + + + + uint8_t roi_config__mode_roi_centre_spad; + + + + + + + + + + + uint8_t roi_config__mode_roi_xy_size; + + + + + + + + + + +} VL53L1_nvm_copy_data_t; + + + + + + + + + + + + +typedef struct { + uint8_t prev_shadow_result__interrupt_status; + + + + + + + + + + + + + uint8_t prev_shadow_result__range_status; + + + + + + + + + + + + + + uint8_t prev_shadow_result__report_status; + + + + + + + + + + + uint8_t prev_shadow_result__stream_count; + + + + + + + + + + + uint16_t prev_shadow_result__dss_actual_effective_spads_sd0; + + + + + + + + + + + uint16_t prev_shadow_result__peak_signal_count_rate_mcps_sd0; + + + + + + + + + + + uint16_t prev_shadow_result__ambient_count_rate_mcps_sd0; + + + + + + + + + + + uint16_t prev_shadow_result__sigma_sd0; + + + + + + + + + + + uint16_t prev_shadow_result__phase_sd0; + + + + + + + + + + + uint16_t prev_shadow_result__final_crosstalk_corrected_range_mm_sd0; + + + + + + + + + + + uint16_t + psr__peak_signal_count_rate_crosstalk_corrected_mcps_sd0; + + + + + + + + + + + uint16_t prev_shadow_result__mm_inner_actual_effective_spads_sd0; + + + + + + + + + + + uint16_t prev_shadow_result__mm_outer_actual_effective_spads_sd0; + + + + + + + + + + + uint16_t prev_shadow_result__avg_signal_count_rate_mcps_sd0; + + + + + + + + + + + uint16_t prev_shadow_result__dss_actual_effective_spads_sd1; + + + + + + + + + + + uint16_t prev_shadow_result__peak_signal_count_rate_mcps_sd1; + + + + + + + + + + + uint16_t prev_shadow_result__ambient_count_rate_mcps_sd1; + + + + + + + + + + + uint16_t prev_shadow_result__sigma_sd1; + + + + + + + + + + + uint16_t prev_shadow_result__phase_sd1; + + + + + + + + + + + uint16_t prev_shadow_result__final_crosstalk_corrected_range_mm_sd1; + + + + + + + + + + + uint16_t prev_shadow_result__spare_0_sd1; + + + + + + + + + + + uint16_t prev_shadow_result__spare_1_sd1; + + + + + + + + + + + uint16_t prev_shadow_result__spare_2_sd1; + + + + + + + + + + + uint16_t prev_shadow_result__spare_3_sd1; + + + + + + + + + + +} VL53L1_prev_shadow_system_results_t; + + + + + + + + + + + + +typedef struct { + uint32_t prev_shadow_result_core__ambient_window_events_sd0; + + + + + + + + + + + uint32_t prev_shadow_result_core__ranging_total_events_sd0; + + + + + + + + + + + int32_t prev_shadow_result_core__signal_total_events_sd0; + + + + + + + + + + + uint32_t prev_shadow_result_core__total_periods_elapsed_sd0; + + + + + + + + + + + uint32_t prev_shadow_result_core__ambient_window_events_sd1; + + + + + + + + + + + uint32_t prev_shadow_result_core__ranging_total_events_sd1; + + + + + + + + + + + int32_t prev_shadow_result_core__signal_total_events_sd1; + + + + + + + + + + + uint32_t prev_shadow_result_core__total_periods_elapsed_sd1; + + + + + + + + + + + uint8_t prev_shadow_result_core__spare_0; + + + + + + + + + + +} VL53L1_prev_shadow_core_results_t; + + + + + + + + + + + + +typedef struct { + uint8_t result__debug_status; + + + + + + + + + + + uint8_t result__debug_stage; + + + + + + + + + + +} VL53L1_patch_debug_t; + + + + + + + + + + + + +typedef struct { + uint16_t gph__system__thresh_rate_high; + + + + + + + + + + + uint16_t gph__system__thresh_rate_low; + + + + + + + + + + + uint8_t gph__system__interrupt_config_gpio; + + + + + + + + + + + + + + + +} VL53L1_gph_general_config_t; + + + + + + + + + + + + +typedef struct { + uint8_t gph__dss_config__roi_mode_control; + + + + + + + + + + + + uint16_t gph__dss_config__manual_effective_spads_select; + + + + + + + + + + + uint8_t gph__dss_config__manual_block_select; + + + + + + + + + + + uint8_t gph__dss_config__max_spads_limit; + + + + + + + + + + + uint8_t gph__dss_config__min_spads_limit; + + + + + + + + + + +} VL53L1_gph_static_config_t; + + + + + + + + + + + + +typedef struct { + uint8_t gph__mm_config__timeout_macrop_a_hi; + + + + + + + + + + + uint8_t gph__mm_config__timeout_macrop_a_lo; + + + + + + + + + + + uint8_t gph__mm_config__timeout_macrop_b_hi; + + + + + + + + + + + uint8_t gph__mm_config__timeout_macrop_b_lo; + + + + + + + + + + + uint8_t gph__range_config__timeout_macrop_a_hi; + + + + + + + + + + + uint8_t gph__range_config__timeout_macrop_a_lo; + + + + + + + + + + + uint8_t gph__range_config__vcsel_period_a; + + + + + + + + + + + uint8_t gph__range_config__vcsel_period_b; + + + + + + + + + + + uint8_t gph__range_config__timeout_macrop_b_hi; + + + + + + + + + + + uint8_t gph__range_config__timeout_macrop_b_lo; + + + + + + + + + + + uint16_t gph__range_config__sigma_thresh; + + + + + + + + + + + uint16_t gph__range_config__min_count_rate_rtn_limit_mcps; + + + + + + + + + + + uint8_t gph__range_config__valid_phase_low; + + + + + + + + + + + uint8_t gph__range_config__valid_phase_high; + + + + + + + + + + +} VL53L1_gph_timing_config_t; + + + + + + + + + + + + +typedef struct { + uint8_t firmware__internal_stream_count_div; + + + + + + + + + + + uint8_t firmware__internal_stream_counter_val; + + + + + + + + + + +} VL53L1_fw_internal_t; + + + + + + + + + + + + +typedef struct { + uint8_t dss_calc__roi_ctrl; + + + + + + + + + + + + uint8_t dss_calc__spare_1; + + + + + + + + + + + uint8_t dss_calc__spare_2; + + + + + + + + + + + uint8_t dss_calc__spare_3; + + + + + + + + + + + uint8_t dss_calc__spare_4; + + + + + + + + + + + uint8_t dss_calc__spare_5; + + + + + + + + + + + uint8_t dss_calc__spare_6; + + + + + + + + + + + uint8_t dss_calc__spare_7; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_0; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_1; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_2; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_3; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_4; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_5; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_6; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_7; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_8; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_9; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_10; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_11; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_12; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_13; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_14; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_15; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_16; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_17; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_18; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_19; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_20; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_21; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_22; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_23; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_24; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_25; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_26; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_27; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_28; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_29; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_30; + + + + + + + + + + + uint8_t dss_calc__user_roi_spad_en_31; + + + + + + + + + + + uint8_t dss_calc__user_roi_0; + + + + + + + + + + + uint8_t dss_calc__user_roi_1; + + + + + + + + + + + uint8_t dss_calc__mode_roi_0; + + + + + + + + + + + uint8_t dss_calc__mode_roi_1; + + + + + + + + + + + uint8_t sigma_estimator_calc__spare_0; + + + + + + + + + + + uint16_t vhv_result__peak_signal_rate_mcps; + + + + + + + + + + + uint32_t vhv_result__signal_total_events_ref; + + + + + + + + + + + uint16_t phasecal_result__phase_output_ref; + + + + + + + + + + + uint16_t dss_result__total_rate_per_spad; + + + + + + + + + + + uint8_t dss_result__enabled_blocks; + + + + + + + + + + + uint16_t dss_result__num_requested_spads; + + + + + + + + + + + uint16_t mm_result__inner_intersection_rate; + + + + + + + + + + + uint16_t mm_result__outer_complement_rate; + + + + + + + + + + + uint16_t mm_result__total_offset; + + + + + + + + + + + uint32_t xtalk_calc__xtalk_for_enabled_spads; + + + + + + + + + + + uint32_t xtalk_result__avg_xtalk_user_roi_kcps; + + + + + + + + + + + uint32_t xtalk_result__avg_xtalk_mm_inner_roi_kcps; + + + + + + + + + + + uint32_t xtalk_result__avg_xtalk_mm_outer_roi_kcps; + + + + + + + + + + + uint32_t range_result__accum_phase; + + + + + + + + + + + uint16_t range_result__offset_corrected_range; + + + + + + + + + + +} VL53L1_patch_results_t; + + + + + + + + + + + + +typedef struct { + uint8_t shadow_phasecal_result__vcsel_start; + + + + + + + + + + + uint8_t shadow_result__interrupt_status; + + + + + + + + + + + + + uint8_t shadow_result__range_status; + + + + + + + + + + + + + + uint8_t shadow_result__report_status; + + + + + + + + + + + uint8_t shadow_result__stream_count; + + + + + + + + + + + uint16_t shadow_result__dss_actual_effective_spads_sd0; + + + + + + + + + + + uint16_t shadow_result__peak_signal_count_rate_mcps_sd0; + + + + + + + + + + + uint16_t shadow_result__ambient_count_rate_mcps_sd0; + + + + + + + + + + + uint16_t shadow_result__sigma_sd0; + + + + + + + + + + + uint16_t shadow_result__phase_sd0; + + + + + + + + + + + uint16_t shadow_result__final_crosstalk_corrected_range_mm_sd0; + + + + + + + + + + + uint16_t + shr__peak_signal_count_rate_crosstalk_corrected_mcps_sd0; + + + + + + + + + + + uint16_t shadow_result__mm_inner_actual_effective_spads_sd0; + + + + + + + + + + + uint16_t shadow_result__mm_outer_actual_effective_spads_sd0; + + + + + + + + + + + uint16_t shadow_result__avg_signal_count_rate_mcps_sd0; + + + + + + + + + + + uint16_t shadow_result__dss_actual_effective_spads_sd1; + + + + + + + + + + + uint16_t shadow_result__peak_signal_count_rate_mcps_sd1; + + + + + + + + + + + uint16_t shadow_result__ambient_count_rate_mcps_sd1; + + + + + + + + + + + uint16_t shadow_result__sigma_sd1; + + + + + + + + + + + uint16_t shadow_result__phase_sd1; + + + + + + + + + + + uint16_t shadow_result__final_crosstalk_corrected_range_mm_sd1; + + + + + + + + + + + uint16_t shadow_result__spare_0_sd1; + + + + + + + + + + + uint16_t shadow_result__spare_1_sd1; + + + + + + + + + + + uint16_t shadow_result__spare_2_sd1; + + + + + + + + + + + uint8_t shadow_result__spare_3_sd1; + + + + + + + + + + + uint8_t shadow_result__thresh_info; + + + + + + + + + + + + uint8_t shadow_phasecal_result__reference_phase_hi; + + + + + + + + + + + uint8_t shadow_phasecal_result__reference_phase_lo; + + + + + + + + + + +} VL53L1_shadow_system_results_t; + + + + + + + + + + + + +typedef struct { + uint32_t shadow_result_core__ambient_window_events_sd0; + + + + + + + + + + + uint32_t shadow_result_core__ranging_total_events_sd0; + + + + + + + + + + + int32_t shadow_result_core__signal_total_events_sd0; + + + + + + + + + + + uint32_t shadow_result_core__total_periods_elapsed_sd0; + + + + + + + + + + + uint32_t shadow_result_core__ambient_window_events_sd1; + + + + + + + + + + + uint32_t shadow_result_core__ranging_total_events_sd1; + + + + + + + + + + + int32_t shadow_result_core__signal_total_events_sd1; + + + + + + + + + + + uint32_t shadow_result_core__total_periods_elapsed_sd1; + + + + + + + + + + + uint8_t shadow_result_core__spare_0; + + + + + + + + + + +} VL53L1_shadow_core_results_t; + + +#endif + + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_silicon_core.h b/drivers/input/misc/vl53L1/inc/vl53l1_silicon_core.h new file mode 100644 index 000000000000..f9eea814d65f --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_silicon_core.h @@ -0,0 +1,134 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_SILICON_CORE_H_ +#define _VL53L1_SILICON_CORE_H_ + +#include "vl53l1_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_is_firmware_ready_silicon( + VL53L1_DEV Dev, + uint8_t *pready); + + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_tuning_parm_defaults.h b/drivers/input/misc/vl53L1/inc/vl53l1_tuning_parm_defaults.h new file mode 100644 index 000000000000..dc989c39f20e --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_tuning_parm_defaults.h @@ -0,0 +1,490 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_TUNING_PARM_DEFAULTS_H_ +#define _VL53L1_TUNING_PARM_DEFAULTS_H_ + + +#ifdef __cplusplus +extern "C" { +#endif + + + + + + + +#define VL53L1_TUNINGPARM_VERSION_DEFAULT \ +((uint16_t) 27) +#define VL53L1_TUNINGPARM_KEY_TABLE_VERSION_DEFAULT \ +((uint16_t) 14) +#define VL53L1_TUNINGPARM_LLD_VERSION_DEFAULT \ +((uint16_t) 12180) +#define VL53L1_TUNINGPARM_HIST_ALGO_SELECT_DEFAULT \ +((uint8_t) 4) +#define VL53L1_TUNINGPARM_HIST_TARGET_ORDER_DEFAULT \ +((uint8_t) 1) +#define VL53L1_TUNINGPARM_HIST_FILTER_WOI_0_DEFAULT \ +((uint8_t) 1) +#define VL53L1_TUNINGPARM_HIST_FILTER_WOI_1_DEFAULT \ +((uint8_t) 2) +#define VL53L1_TUNINGPARM_HIST_AMB_EST_METHOD_DEFAULT \ +((uint8_t) 1) +#define VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_0_DEFAULT \ +((uint8_t) 80) +#ifdef TOF_USE_VL53L3_EN +#define VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_1_DEFAULT \ +((uint8_t) 80) +#else +#define VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_1_DEFAULT \ +((uint8_t) 112) +#endif +#define VL53L1_TUNINGPARM_HIST_MIN_AMB_THRESH_EVENTS_DEFAULT \ +((int32_t) 16) +#define VL53L1_TUNINGPARM_HIST_AMB_EVENTS_SCALER_DEFAULT \ +((uint16_t) 4157) +#define VL53L1_TUNINGPARM_HIST_NOISE_THRESHOLD_DEFAULT \ +((uint16_t) 50) +#ifdef TOF_USE_VL53L3_EN +#define VL53L1_TUNINGPARM_HIST_SIGNAL_TOTAL_EVENTS_LIMIT_DEFAULT \ +((int32_t) 50) +#else +#define VL53L1_TUNINGPARM_HIST_SIGNAL_TOTAL_EVENTS_LIMIT_DEFAULT \ +((int32_t) 100) +#endif +#define VL53L1_TUNINGPARM_HIST_SIGMA_EST_REF_MM_DEFAULT \ +((uint8_t) 1) +#ifdef TOF_USE_VL53L3_EN +#define VL53L1_TUNINGPARM_HIST_SIGMA_THRESH_MM_DEFAULT \ +((uint16_t) 180) +#else +#define VL53L1_TUNINGPARM_HIST_SIGMA_THRESH_MM_DEFAULT \ +((uint16_t) 160) +#endif +#define VL53L1_TUNINGPARM_HIST_GAIN_FACTOR_DEFAULT \ +((uint16_t) 1987) +#define VL53L1_TUNINGPARM_CONSISTENCY_HIST_PHASE_TOLERANCE_DEFAULT \ +((uint8_t) 8) +#ifdef TOF_USE_VL53L3_EN +#define VL53L1_TUNINGPARM_CONSISTENCY_HIST_MIN_MAX_TOLERANCE_MM_DEFAULT \ +((uint16_t) 0) +#else +#define VL53L1_TUNINGPARM_CONSISTENCY_HIST_MIN_MAX_TOLERANCE_MM_DEFAULT \ +((uint16_t) 250) +#endif +#define VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA_DEFAULT \ +((uint8_t) 0) +#define VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA_MIN_SPAD_LIMIT_DEFAULT \ +((uint16_t) 2048) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_LONG_RANGE_DEFAULT \ +((uint8_t) 9) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_MED_RANGE_DEFAULT \ +((uint8_t) 5) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_SHORT_RANGE_DEFAULT \ +((uint8_t) 3) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_LONG_RANGE_DEFAULT \ +((uint8_t) 6) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_MED_RANGE_DEFAULT \ +((uint8_t) 6) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_SHORT_RANGE_DEFAULT \ +((uint8_t) 6) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MIN_VALID_RANGE_MM_DEFAULT \ +((int16_t) -50) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RANGE_MM_DEFAULT \ +((int16_t) 50) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MAX_SIGMA_MM_DEFAULT \ +((uint16_t) 140) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MIN_MAX_TOLERANCE_DEFAULT \ +((uint16_t) 50) +#ifdef TOF_USE_VL53L3_EN +#define VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RATE_KCPS_DEFAULT \ +((uint16_t) 400) +#else +#define VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RATE_KCPS_DEFAULT \ +((uint16_t) 64000) +#endif +#define VL53L1_TUNINGPARM_XTALK_DETECT_EVENT_SIGMA_DEFAULT \ +((uint8_t) 80) +#ifdef TOF_USE_VL53L3_EN +#define VL53L1_TUNINGPARM_HIST_XTALK_MARGIN_KCPS_DEFAULT \ +((int16_t) 0) +#else +#define VL53L1_TUNINGPARM_HIST_XTALK_MARGIN_KCPS_DEFAULT \ +((int16_t) 512) +#endif +#define VL53L1_TUNINGPARM_CONSISTENCY_LITE_PHASE_TOLERANCE_DEFAULT \ +((uint8_t) 2) +#define VL53L1_TUNINGPARM_PHASECAL_TARGET_DEFAULT \ +((uint8_t) 33) +#define VL53L1_TUNINGPARM_LITE_CAL_REPEAT_RATE_DEFAULT \ +((uint16_t) 0) +#define VL53L1_TUNINGPARM_LITE_RANGING_GAIN_FACTOR_DEFAULT \ +((uint16_t) 2011) +#define VL53L1_TUNINGPARM_LITE_MIN_CLIP_MM_DEFAULT \ +((uint8_t) 0) +#define VL53L1_TUNINGPARM_LITE_LONG_SIGMA_THRESH_MM_DEFAULT \ +((uint16_t) 60) +#define VL53L1_TUNINGPARM_LITE_MED_SIGMA_THRESH_MM_DEFAULT \ +((uint16_t) 60) +#define VL53L1_TUNINGPARM_LITE_SHORT_SIGMA_THRESH_MM_DEFAULT \ +((uint16_t) 60) +#define VL53L1_TUNINGPARM_LITE_LONG_MIN_COUNT_RATE_RTN_MCPS_DEFAULT \ +((uint16_t) 128) +#define VL53L1_TUNINGPARM_LITE_MED_MIN_COUNT_RATE_RTN_MCPS_DEFAULT \ +((uint16_t) 128) +#define VL53L1_TUNINGPARM_LITE_SHORT_MIN_COUNT_RATE_RTN_MCPS_DEFAULT \ +((uint16_t) 128) +#define VL53L1_TUNINGPARM_LITE_SIGMA_EST_PULSE_WIDTH_DEFAULT \ +((uint8_t) 8) +#define VL53L1_TUNINGPARM_LITE_SIGMA_EST_AMB_WIDTH_NS_DEFAULT \ +((uint8_t) 16) +#define VL53L1_TUNINGPARM_LITE_SIGMA_REF_MM_DEFAULT \ +((uint8_t) 1) +#define VL53L1_TUNINGPARM_LITE_RIT_MULT_DEFAULT \ +((uint8_t) 64) +#define VL53L1_TUNINGPARM_LITE_SEED_CONFIG_DEFAULT \ +((uint8_t) 2) +#define VL53L1_TUNINGPARM_LITE_QUANTIFIER_DEFAULT \ +((uint8_t) 2) +#define VL53L1_TUNINGPARM_LITE_FIRST_ORDER_SELECT_DEFAULT \ +((uint8_t) 0) +#define VL53L1_TUNINGPARM_LITE_XTALK_MARGIN_KCPS_DEFAULT \ +((int16_t) 0) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_LONG_RANGE_DEFAULT \ +((uint8_t) 14) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_MED_RANGE_DEFAULT \ +((uint8_t) 10) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_SHORT_RANGE_DEFAULT \ +((uint8_t) 6) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_LONG_RANGE_DEFAULT \ +((uint8_t) 14) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_MED_RANGE_DEFAULT \ +((uint8_t) 10) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_SHORT_RANGE_DEFAULT \ +((uint8_t) 6) +#define VL53L1_TUNINGPARM_TIMED_SEED_CONFIG_DEFAULT \ +((uint8_t) 1) +#define VL53L1_TUNINGPARM_DMAX_CFG_SIGNAL_THRESH_SIGMA_DEFAULT \ +((uint8_t) 32) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_0_DEFAULT \ +((uint16_t) 15) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_1_DEFAULT \ +((uint16_t) 52) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_2_DEFAULT \ +((uint16_t) 200) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_3_DEFAULT \ +((uint16_t) 364) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_4_DEFAULT \ +((uint16_t) 400) +#define VL53L1_TUNINGPARM_VHV_LOOPBOUND_DEFAULT \ +((uint8_t) 129) +#define VL53L1_TUNINGPARM_REFSPADCHAR_DEVICE_TEST_MODE_DEFAULT \ +((uint8_t) 8) +#define VL53L1_TUNINGPARM_REFSPADCHAR_VCSEL_PERIOD_DEFAULT \ +((uint8_t) 11) +#define VL53L1_TUNINGPARM_REFSPADCHAR_PHASECAL_TIMEOUT_US_DEFAULT \ +((uint32_t) 1000) +#define VL53L1_TUNINGPARM_REFSPADCHAR_TARGET_COUNT_RATE_MCPS_DEFAULT \ +((uint16_t) 2560) +#define VL53L1_TUNINGPARM_REFSPADCHAR_MIN_COUNTRATE_LIMIT_MCPS_DEFAULT \ +((uint16_t) 1280) +#define VL53L1_TUNINGPARM_REFSPADCHAR_MAX_COUNTRATE_LIMIT_MCPS_DEFAULT \ +((uint16_t) 5120) +#ifdef TOF_USE_VL53L3_EN +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_NUM_OF_SAMPLES_DEFAULT \ +((uint8_t) 13) +#else +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_NUM_OF_SAMPLES_DEFAULT \ +((uint8_t) 7) +#endif +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_MIN_FILTER_THRESH_MM_DEFAULT \ +((int16_t) -70) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_FILTER_THRESH_MM_DEFAULT \ +((int16_t) 70) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_RATE_MCPS_DEFAULT \ +((uint16_t) 5120) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_PHASECAL_TIMEOUT_US_DEFAULT \ +((uint32_t) 15000) +#ifdef TOF_USE_VL53L3_EN +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_VALID_RATE_KCPS_DEFAULT \ +((uint16_t) 640) +#else +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_VALID_RATE_KCPS_DEFAULT \ +((uint16_t) 64000) +#endif +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_SIGMA_THRESHOLD_MM_DEFAULT \ +((uint16_t) 140) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_TIMEOUT_US_DEFAULT \ +((uint32_t) 2000) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_BIN_TIMEOUT_US_DEFAULT \ +((uint32_t) 10000) +#define VL53L1_TUNINGPARM_OFFSET_CAL_DSS_RATE_MCPS_DEFAULT \ +((uint16_t) 2560) +#define VL53L1_TUNINGPARM_OFFSET_CAL_PHASECAL_TIMEOUT_US_DEFAULT \ +((uint32_t) 1000) +#define VL53L1_TUNINGPARM_OFFSET_CAL_MM_TIMEOUT_US_DEFAULT \ +((uint32_t) 13000) +#define VL53L1_TUNINGPARM_OFFSET_CAL_RANGE_TIMEOUT_US_DEFAULT \ +((uint32_t) 13000) +#define VL53L1_TUNINGPARM_OFFSET_CAL_PRE_SAMPLES_DEFAULT \ +((uint8_t) 8) +#define VL53L1_TUNINGPARM_OFFSET_CAL_MM1_SAMPLES_DEFAULT \ +((uint8_t) 40) +#define VL53L1_TUNINGPARM_OFFSET_CAL_MM2_SAMPLES_DEFAULT \ +((uint8_t) 9) +#define VL53L1_TUNINGPARM_ZONE_CAL_DSS_RATE_MCPS_DEFAULT \ +((uint16_t) 5120) +#define VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_TIMEOUT_US_DEFAULT \ +((uint32_t) 15000) +#define VL53L1_TUNINGPARM_ZONE_CAL_DSS_TIMEOUT_US_DEFAULT \ +((uint32_t) 2000) +#define VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_NUM_SAMPLES_DEFAULT \ +((uint16_t) 16) +#define VL53L1_TUNINGPARM_ZONE_CAL_RANGE_TIMEOUT_US_DEFAULT \ +((uint32_t) 1000) +#define VL53L1_TUNINGPARM_ZONE_CAL_ZONE_NUM_SAMPLES_DEFAULT \ +((uint16_t) 8) +#define VL53L1_TUNINGPARM_SPADMAP_VCSEL_PERIOD_DEFAULT \ +((uint8_t) 18) +#define VL53L1_TUNINGPARM_SPADMAP_VCSEL_START_DEFAULT \ +((uint8_t) 15) +#define VL53L1_TUNINGPARM_SPADMAP_RATE_LIMIT_MCPS_DEFAULT \ +((uint16_t) 12) +#define VL53L1_TUNINGPARM_LITE_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS_DEFAULT \ +((uint16_t) 2560) +#define VL53L1_TUNINGPARM_RANGING_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS_DEFAULT \ +((uint16_t) 5120) +#define VL53L1_TUNINGPARM_MZ_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS_DEFAULT \ +((uint16_t) 5120) +#define VL53L1_TUNINGPARM_TIMED_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS_DEFAULT \ +((uint16_t) 2560) +#define VL53L1_TUNINGPARM_LITE_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 1000) +#define VL53L1_TUNINGPARM_RANGING_LONG_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 15000) +#define VL53L1_TUNINGPARM_RANGING_MED_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 9000) +#define VL53L1_TUNINGPARM_RANGING_SHORT_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 6000) +#define VL53L1_TUNINGPARM_MZ_LONG_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 15000) +#define VL53L1_TUNINGPARM_MZ_MED_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 9000) +#define VL53L1_TUNINGPARM_MZ_SHORT_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 6000) +#define VL53L1_TUNINGPARM_TIMED_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 1000) +#define VL53L1_TUNINGPARM_LITE_MM_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 2000) +#define VL53L1_TUNINGPARM_RANGING_MM_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 2000) +#define VL53L1_TUNINGPARM_MZ_MM_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 2000) +#define VL53L1_TUNINGPARM_TIMED_MM_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 2000) +#define VL53L1_TUNINGPARM_LITE_RANGE_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 63000) +#define VL53L1_TUNINGPARM_RANGING_RANGE_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 2500) +#define VL53L1_TUNINGPARM_MZ_RANGE_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 2500) +#define VL53L1_TUNINGPARM_TIMED_RANGE_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 13000) +#ifdef TOF_USE_VL53L3_EN +#define VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_MARGIN_DEFAULT \ +((uint16_t) 0) +#else +#define VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_MARGIN_DEFAULT \ +((uint16_t) 512) +#endif +#ifdef TOF_USE_VL53L3_EN +#define VL53L1_TUNINGPARM_DYNXTALK_NOISE_MARGIN_DEFAULT \ +((uint32_t) 100) +#else +#define VL53L1_TUNINGPARM_DYNXTALK_NOISE_MARGIN_DEFAULT \ +((uint32_t) 500) +#endif +#define VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT_DEFAULT \ +((uint32_t) 0) +#define VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT_HI_DEFAULT \ +((uint8_t) 0) +#define VL53L1_TUNINGPARM_DYNXTALK_SAMPLE_LIMIT_DEFAULT \ +((uint32_t) 200) +#ifdef TOF_USE_VL53L3_EN +#define VL53L1_TUNINGPARM_DYNXTALK_SINGLE_XTALK_DELTA_DEFAULT \ +((uint32_t) 2048) +#else +#define VL53L1_TUNINGPARM_DYNXTALK_SINGLE_XTALK_DELTA_DEFAULT \ +((uint32_t) 10240) +#endif +#ifdef TOF_USE_VL53L3_EN +#define VL53L1_TUNINGPARM_DYNXTALK_AVERAGED_XTALK_DELTA_DEFAULT \ +((uint32_t) 308) +#else +#define VL53L1_TUNINGPARM_DYNXTALK_AVERAGED_XTALK_DELTA_DEFAULT \ +((uint32_t) 4096) +#endif +#ifdef TOF_USE_VL53L3_EN +#define VL53L1_TUNINGPARM_DYNXTALK_CLIP_LIMIT_DEFAULT \ +((uint32_t) 10240) +#else +#define VL53L1_TUNINGPARM_DYNXTALK_CLIP_LIMIT_DEFAULT \ +((uint32_t) 1046528) +#endif +#define VL53L1_TUNINGPARM_DYNXTALK_SCALER_CALC_METHOD_DEFAULT \ +((uint8_t) 0) +#define VL53L1_TUNINGPARM_DYNXTALK_XGRADIENT_SCALER_DEFAULT \ +((int16_t) 256) +#define VL53L1_TUNINGPARM_DYNXTALK_YGRADIENT_SCALER_DEFAULT \ +((int16_t) 256) +#define VL53L1_TUNINGPARM_DYNXTALK_USER_SCALER_SET_DEFAULT \ +((uint8_t) 0) +#define VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_COR_SINGLE_APPLY_DEFAULT \ +((uint8_t) 0) +#ifdef TOF_USE_VL53L3_EN +#define VL53L1_TUNINGPARM_DYNXTALK_XTALK_AMB_THRESHOLD_DEFAULT \ +((uint32_t) 128) +#else +#define VL53L1_TUNINGPARM_DYNXTALK_XTALK_AMB_THRESHOLD_DEFAULT \ +((uint32_t) 1280) +#endif +#define VL53L1_TUNINGPARM_DYNXTALK_NODETECT_AMB_THRESHOLD_KCPS_DEFAULT \ +((uint32_t) 57671680) +#ifdef TOF_USE_VL53L3_EN +#define VL53L1_TUNINGPARM_DYNXTALK_NODETECT_SAMPLE_LIMIT_DEFAULT \ +((uint32_t) 40) +#else +#define VL53L1_TUNINGPARM_DYNXTALK_NODETECT_SAMPLE_LIMIT_DEFAULT \ +((uint32_t) 10) +#endif +#ifdef TOF_USE_VL53L3_EN +#define VL53L1_TUNINGPARM_DYNXTALK_NODETECT_XTALK_OFFSET_KCPS_DEFAULT \ +((uint32_t) 410) +#else +#define VL53L1_TUNINGPARM_DYNXTALK_NODETECT_XTALK_OFFSET_KCPS_DEFAULT \ +((uint32_t) 2048) +#endif +#define VL53L1_TUNINGPARM_DYNXTALK_NODETECT_MIN_RANGE_MM_DEFAULT \ +((uint16_t) 900) +#define VL53L1_TUNINGPARM_LOWPOWERAUTO_VHV_LOOP_BOUND_DEFAULT \ +((uint8_t) 3) +#define VL53L1_TUNINGPARM_LOWPOWERAUTO_MM_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 1) +#define VL53L1_TUNINGPARM_LOWPOWERAUTO_RANGE_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 8000) +#define VL53L1_TUNINGPARM_VERY_SHORT_DSS_RATE_MCPS_DEFAULT \ +((uint16_t) 10240) + + +#ifdef __cplusplus +} +#endif + +#endif + + + + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_wait.h b/drivers/input/misc/vl53L1/inc/vl53l1_wait.h new file mode 100644 index 000000000000..eb19e3b9bcb4 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_wait.h @@ -0,0 +1,319 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_WAIT_H_ +#define _VL53L1_WAIT_H_ + +#include "vl53l1_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + + + + + + + + + + + +VL53L1_Error VL53L1_wait_for_boot_completion( + VL53L1_DEV Dev); + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_wait_for_firmware_ready( + VL53L1_DEV Dev); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_wait_for_range_completion( + VL53L1_DEV Dev); + + + + + + + + + + + + + + +VL53L1_Error VL53L1_wait_for_test_completion( + VL53L1_DEV Dev); + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_is_boot_complete( + VL53L1_DEV Dev, + uint8_t *pready); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_is_firmware_ready( + VL53L1_DEV Dev, + uint8_t *pready); + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_is_new_data_ready( + VL53L1_DEV Dev, + uint8_t *pready); + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_poll_for_boot_completion( + VL53L1_DEV Dev, + uint32_t timeout_ms); + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_poll_for_firmware_ready( + VL53L1_DEV Dev, + uint32_t timeout_ms); + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_poll_for_range_completion( + VL53L1_DEV Dev, + uint32_t timeout_ms); + + + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/drivers/input/misc/vl53L1/inc/vl53l1_zone_presets.h b/drivers/input/misc/vl53L1/inc/vl53l1_zone_presets.h new file mode 100644 index 000000000000..34f3652f0a95 --- /dev/null +++ b/drivers/input/misc/vl53L1/inc/vl53l1_zone_presets.h @@ -0,0 +1,183 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifndef _VL53L1_ZONE_PRESETS_H_ +#define _VL53L1_ZONE_PRESETS_H_ + +#include "vl53l1_ll_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + + + + + + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_init_zone_config_structure( + uint8_t x_off, + uint8_t x_inc, + uint8_t x_zones, + uint8_t y_off, + uint8_t y_inc, + uint8_t y_zones, + uint8_t width, + uint8_t height, + VL53L1_zone_config_t *pdata); + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_zone_preset_xtalk_planar( + VL53L1_general_config_t *pgeneral, + VL53L1_zone_config_t *pzone_cfg); + + + + + + + + + + + + + +VL53L1_Error VL53L1_init_zone_config_histogram_bins( + VL53L1_zone_config_t *pdata); + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/drivers/input/misc/vl53L1/ipp/ipp_linux.c b/drivers/input/misc/vl53L1/ipp/ipp_linux.c new file mode 100644 index 000000000000..12c63f0bce1b --- /dev/null +++ b/drivers/input/misc/vl53L1/ipp/ipp_linux.c @@ -0,0 +1,343 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + +/** + * @file ipp_linux.c kernel side implementation of vl53l1 protected processing + * + * @date Sep 1, 2016 + * @author : imaging + * + * @ingroup ipp_dev + */ + +#include "stmvl53l1.h" + +#define IPP_ERR_CODE (VL53L1_ERROR_PLATFORM_SPECIFIC_START-1) + +static int stmvl53l1_ipp_do_wrapper(struct stmvl53l1_data *data, + struct ipp_work_t *pin, struct ipp_work_t *pout, int payload_out) +{ + int rc; + + if (data->ipp.buzy) { + vl53l1_errmsg("try exec new ipp but still buzy on previous"); + /* TODO shall we discard it and push new ? */ + rc = IPP_ERR_CODE; + goto done; + } + WARN_ON(pin->payload > IPP_WORK_MAX_PAYLOAD); + stmvl531_ipp_tim_start(data); + rc = stmvl53l1_ipp_do(data, pin, pout); + if (rc != 0) { + vl53l1_errmsg("stmvl53l1_ipp_do err %d\n", rc); + rc = IPP_ERR_CODE; + goto done; + } + vl53l1_dbgmsg("ipp ok \n"); + /* check what we got back if valid answer error etc */ + if (pout->status) { + vl53l1_errmsg("ipp error status %d from user", pout->status); + if (pout->status >= stmvl53l1_ipp_status_proc_code) + rc = pout->status & (stmvl53l1_ipp_status_proc_code-1); + else + rc = IPP_ERR_CODE; + goto done; + } + WARN_ON(pout->payload > IPP_WORK_MAX_PAYLOAD); + if (pout->payload != payload_out) { + /* bad formated answer */ + vl53l1_errmsg("bad payload %d != %d in ipp work back", + pout->payload, payload_out); + rc = IPP_ERR_CODE; + goto done; + } + stmvl531_ipp_tim_stop(data); + stmvl531_ipp_stat(data, "ipp #%5x to=%3d fm=%3d in %5ld us", + pin->xfer_id, pin->payload, + pout->payload, + stmvl531_ipp_time(data)); + + rc = 0; +done: + + return rc; +} + +/** + * @file vl53l1_platform_ipp.h + * + * @brief EwokPlus25 IPP Wrapper Functions + */ + +/** + * @brief IPP Wrapper call for histogram post processing + * + * + * @param[in] Dev : Device handle + * @param[in] pdmax_cal : DMAX calibration data + * @param[in] pdmax_cfg : DMAX configuration data + * @param[in] ppost_cfg : VL53L1_hist_post_process_config_t + * @param[in] pbins : Input histogram raw bin data + * @param[in] pxtalk : Cross talk histogram data + * @param[out] presults : Output VL53L1_range_results_t + * structure + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_ipp_hist_process_data( + VL53L1_DEV dev, + VL53L1_dmax_calibration_data_t *pdmax_cal, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg, + VL53L1_hist_post_process_config_t *ppost_cfg, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_xtalk_histogram_data_t *pxtalk, + VL53L1_range_results_t *presults) +{ + struct stmvl53l1_data *data = (struct stmvl53l1_data *) + container_of(dev, struct stmvl53l1_data, stdev); + struct ipp_work_t *pout = &data->ipp.work_out; + struct ipp_work_t *pin = &data->ipp.work; + int rc; + VL53L1_range_results_t *presults_ipp; + + IPP_SERIALIZE_VAR; + + /* setup pin */ + IPP_SERIALIZE_START(pin->data, 5); + IPP_SET_ARG_PTR(pin->data, 0, pdmax_cal); + IPP_SET_ARG_PTR(pin->data, 1, pdmax_cfg); + IPP_SET_ARG_PTR(pin->data, 2, ppost_cfg); + IPP_SET_ARG_PTR(pin->data, 3, pbins); + IPP_SET_ARG_PTR(pin->data, 4, pxtalk); + pin->payload = IPP_SERIALIZE_PAYLAOD(); + pin->process_no = stmvl53l1_ipp_cal_hist; + + /* setup pout */ + IPP_SERIALIZE_START(pout->data, 1); + IPP_OUT_ARG_PTR(pout->data, 0, presults_ipp); + + /* do ipp */ + rc = stmvl53l1_ipp_do_wrapper(data, pin, pout, IPP_SERIALIZE_PAYLAOD()); + if (rc) + goto done; + + /* copy output */ + memcpy(presults, presults_ipp, sizeof(*presults)); + + rc = 0; +done: + + return rc; +} + +/** + * @brief IPP Wrapper call for histogram ambient dmax calc + * + * The target reflectance in percent for the DMAX calculation + * is set by target_reflectance input + * + * The fixed point format is 7.2 + * + * @param[in] Dev : Device handle + * @param[in] target_reflectance : target reflectance to report ambient DMAX + * Percentage in 7.2 fixed point format + * @param[in] pdmax_cal : DMAX calibration data + * @param[in] pdmax_cfg : DMAX configuration data + * @param[in] pbins : Input histogram raw bin data + * @param[out] pambient_dmax_mm : Output ambient DMAX distance in [mm] + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_ipp_hist_ambient_dmax( + VL53L1_DEV dev, + uint16_t target_reflectance, + VL53L1_dmax_calibration_data_t *pdmax_cal, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg, + VL53L1_histogram_bin_data_t *pbins, + int16_t *pambient_dmax_mm) +{ + struct stmvl53l1_data *data = (struct stmvl53l1_data *) + container_of(dev, struct stmvl53l1_data, stdev); + struct ipp_work_t *pout = &data->ipp.work_out; + struct ipp_work_t *pin = &data->ipp.work; + int rc; + int16_t *pambient_dmax_mm_ipp; + + IPP_SERIALIZE_VAR; + + /* setup pin */ + IPP_SERIALIZE_START(pin->data, 4); + IPP_SET_ARG(pin->data, 0, target_reflectance); + IPP_SET_ARG_PTR(pin->data, 1, pdmax_cal); + IPP_SET_ARG_PTR(pin->data, 2, pdmax_cfg); + IPP_SET_ARG_PTR(pin->data, 3, pbins); + pin->payload = IPP_SERIALIZE_PAYLAOD(); + pin->process_no = stmvl53l1_ipp_hist_ambient_dmax; + + /* setup pout */ + IPP_SERIALIZE_START(pout->data, 1); + IPP_OUT_ARG_PTR(pout->data, 0, pambient_dmax_mm_ipp); + + /* do ipp */ + rc = stmvl53l1_ipp_do_wrapper(data, pin, pout, IPP_SERIALIZE_PAYLAOD()); + if (rc) + goto done; + + /* copy output */ + memcpy(pambient_dmax_mm, pambient_dmax_mm_ipp, + sizeof(*pambient_dmax_mm)); + + rc = 0; +done: + + return rc; +} + +/** + * @brief IPP Wrapper call for xtalk calibration post processing + * + * @param[in] Dev : Device handle + * @param[in] pxtalk_ranges : Input VL53L1_xtalk_range_results_t + * Must contain 5 ranges, 4 quadrants + 1 + * full FoV + * @param[out] pxtalk_shape : Output normalised Cross talk histogram + * shape + * @param[out] pxtalk_cal : Output VL53L1_xtalk_calibration_results_t + * structure + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_ipp_xtalk_calibration_process_data( + VL53L1_DEV dev, + VL53L1_xtalk_range_results_t *pxtalk_ranges, + VL53L1_xtalk_histogram_data_t *pxtalk_shape, + VL53L1_xtalk_calibration_results_t *pxtalk_cal) +{ + struct stmvl53l1_data *data = (struct stmvl53l1_data *) + container_of(dev, struct stmvl53l1_data, stdev); + struct ipp_work_t *pout = &data->ipp.work_out; + struct ipp_work_t *pin = &data->ipp.work; + int rc; + VL53L1_xtalk_histogram_data_t *pxtalk_shape_ipp; + VL53L1_xtalk_calibration_results_t *pxtalk_cal_ipp; + + IPP_SERIALIZE_VAR; + + /* setup pin */ + IPP_SERIALIZE_START(pin->data, 1); + IPP_SET_ARG_PTR(pin->data, 0, pxtalk_ranges); + pin->payload = IPP_SERIALIZE_PAYLAOD(); + pin->process_no = stmvl53l1_ipp_xtalk_calibration; + + /* setup pout */ + IPP_SERIALIZE_START(pout->data, 2); + IPP_OUT_ARG_PTR(pout->data, 0, pxtalk_shape_ipp); + IPP_OUT_ARG_PTR(pout->data, 1, pxtalk_cal_ipp); + + /* do ipp */ + rc = stmvl53l1_ipp_do_wrapper(data, pin, pout, IPP_SERIALIZE_PAYLAOD()); + if (rc) + goto done; + + /* copy output */ + memcpy(pxtalk_shape, pxtalk_shape_ipp, sizeof(*pxtalk_shape)); + memcpy(pxtalk_cal, pxtalk_cal_ipp, sizeof(*pxtalk_cal)); + + rc = 0; +done: + + return rc; +} + +/** + * @brief IPP Wrapper call for Generating Xtalk data from dual reflectance + * histogram data + * + * @param[in] Dev : Device handle + * @param[in] pxtalk_results : Pointer to xtalk_results structure + * containing dual reflectance + * histogram data + * @param[in] expected_target_distance_mm : User input of true target distance + * @param[in] higher_reflectance : User input detailing which + * histogram data 1 or 2 has the + * highest reflectance. + * @param[out] pxtalk_avg_samples : Pointer to output xtalk histogram + * data + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_ipp_generate_dual_reflectance_xtalk_samples( + VL53L1_DEV dev, + VL53L1_xtalk_range_results_t *pxtalk_results, + uint16_t expected_target_distance_mm, + uint8_t higher_reflectance, + VL53L1_histogram_bin_data_t *pxtalk_avg_samples) +{ + struct stmvl53l1_data *data = (struct stmvl53l1_data *) + container_of(dev, struct stmvl53l1_data, stdev); + struct ipp_work_t *pout = &data->ipp.work_out; + struct ipp_work_t *pin = &data->ipp.work; + int rc; + VL53L1_histogram_bin_data_t *pxtalk_avg_samples_ipp; + + IPP_SERIALIZE_VAR; + + /* setup pin */ + IPP_SERIALIZE_START(pin->data, 3); + IPP_SET_ARG_PTR(pin->data, 0, pxtalk_results); + IPP_SET_ARG(pin->data, 1, expected_target_distance_mm); + IPP_SET_ARG(pin->data, 2, higher_reflectance); + pin->payload = IPP_SERIALIZE_PAYLAOD(); + pin->process_no = stmvl53l1_ipp_generate_dual_reflectance_xtalk_samples; + + /* setup pout */ + IPP_SERIALIZE_START(pout->data, 1); + IPP_OUT_ARG_PTR(pout->data, 0, pxtalk_avg_samples_ipp); + + /* do ipp */ + rc = stmvl53l1_ipp_do_wrapper(data, pin, pout, IPP_SERIALIZE_PAYLAOD()); + if (rc) + goto done; + + /* copy output */ + memcpy(pxtalk_avg_samples, pxtalk_avg_samples_ipp, + sizeof(*pxtalk_avg_samples)); + + rc = 0; +done: + + return rc; +} diff --git a/drivers/input/misc/vl53L1/src/vl53l1_api.c b/drivers/input/misc/vl53L1/src/vl53l1_api.c new file mode 100644 index 000000000000..9118ed809002 --- /dev/null +++ b/drivers/input/misc/vl53L1/src/vl53l1_api.c @@ -0,0 +1,3760 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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 "vl53l1_api.h" +#include "vl53l1_api_strings.h" +#include "vl53l1_register_settings.h" +#include "vl53l1_register_funcs.h" +#include "vl53l1_core.h" +#include "vl53l1_api_calibration.h" +#include "vl53l1_wait.h" +#include "vl53l1_preset_setup.h" +#include "vl53l1_api_debug.h" +#include "vl53l1_api_core.h" +#include "vl53l1_nvm.h" + + + +#define ZONE_CHECK VL53L1_MAX_USER_ZONES + +#if ZONE_CHECK < 5 +#error Must define at least 5 zones in MAX_USER_ZONES constant +#endif + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_API, status, \ + fmt, ##__VA_ARGS__) + +#ifdef VL53L1_LOG_ENABLE +#define trace_print(level, ...) trace_print_module_function(\ + VL53L1_TRACE_MODULE_API, level, VL53L1_TRACE_FUNCTION_NONE, \ + ##__VA_ARGS__) +#endif + +#ifndef MIN +#define MIN(v1, v2) ((v1) < (v2) ? (v1) : (v2)) +#endif +#ifndef MAX +#define MAX(v1, v2) ((v1) < (v2) ? (v2) : (v1)) +#endif + +#define DMAX_REFLECTANCE_IDX 2 + + + + + + + +#define LOWPOWER_AUTO_VHV_LOOP_DURATION_US 245 +#define LOWPOWER_AUTO_OVERHEAD_BEFORE_A_RANGING 1448 +#define LOWPOWER_AUTO_OVERHEAD_BETWEEN_A_B_RANGING 2100 + +#define FDA_MAX_TIMING_BUDGET_US 550000 + + + + + + + + + +static int32_t BDTable[VL53L1_TUNING_MAX_TUNABLE_KEY] = { + TUNING_VERSION, + TUNING_PROXY_MIN, + TUNING_SINGLE_TARGET_XTALK_TARGET_DISTANCE_MM, + TUNING_SINGLE_TARGET_XTALK_SAMPLE_NUMBER, + TUNING_MIN_AMBIENT_DMAX_VALID, + TUNING_MAX_SIMPLE_OFFSET_CALIBRATION_SAMPLE_NUMBER, + TUNING_XTALK_FULL_ROI_TARGET_DISTANCE_MM, + TUNING_SIMPLE_OFFSET_CALIBRATION_REPEAT +}; + + + +static VL53L1_Error SingleTargetXTalkCalibration(VL53L1_DEV Dev) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + uint32_t sum_ranging = 0; + uint32_t sum_spads = 0; + FixPoint1616_t sum_signalRate = 0; + FixPoint1616_t total_count = 0; + uint8_t xtalk_meas = 0; + uint8_t xtalk_measmax = + BDTable[VL53L1_TUNING_SINGLE_TARGET_XTALK_SAMPLE_NUMBER]; + VL53L1_RangingMeasurementData_t RMData; + FixPoint1616_t xTalkStoredMeanSignalRate; + FixPoint1616_t xTalkStoredMeanRange; + FixPoint1616_t xTalkStoredMeanRtnSpads; + uint32_t xTalkStoredMeanRtnSpadsAsInt; + uint32_t xTalkCalDistanceAsInt; + FixPoint1616_t XTalkCompensationRateMegaCps; + uint32_t signalXTalkTotalPerSpad; + VL53L1_PresetModes PresetMode; + VL53L1_CalibrationData_t CalibrationData; + VL53L1_CustomerNvmManaged_t *pC; + + + LOG_FUNCTION_START(""); + + + + + + + + PresetMode = VL53L1DevDataGet(Dev, CurrentParameters.PresetMode); + + if ((PresetMode != VL53L1_PRESETMODE_AUTONOMOUS) && + (PresetMode != VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS) && + (PresetMode != VL53L1_PRESETMODE_LITE_RANGING)) { + Status = VL53L1_ERROR_MODE_NOT_SUPPORTED; + goto ENDFUNC; + } + + + + Status = VL53L1_disable_xtalk_compensation(Dev); + + if (Status != VL53L1_ERROR_NONE) + goto ENDFUNC; + + Status = VL53L1_StartMeasurement(Dev); + + if (Status != VL53L1_ERROR_NONE) + goto ENDFUNC; + + + + VL53L1_WaitMeasurementDataReady(Dev); + VL53L1_GetRangingMeasurementData(Dev, &RMData); + VL53L1_ClearInterruptAndStartMeasurement(Dev); + + sum_ranging = 0; + sum_spads = 0; + sum_signalRate = 0; + total_count = 0; + for (xtalk_meas = 0; xtalk_meas < xtalk_measmax; xtalk_meas++) { + VL53L1_WaitMeasurementDataReady(Dev); + VL53L1_GetRangingMeasurementData(Dev, &RMData); + VL53L1_ClearInterruptAndStartMeasurement(Dev); + if (RMData.RangeStatus == VL53L1_RANGESTATUS_RANGE_VALID) { + sum_ranging += RMData.RangeMilliMeter; + sum_signalRate += RMData.SignalRateRtnMegaCps; + sum_spads += RMData.EffectiveSpadRtnCount / 256; + total_count++; + } + } + Status = VL53L1_StopMeasurement(Dev); + + if (total_count > 0) { + + + xTalkStoredMeanSignalRate = sum_signalRate / total_count; + xTalkStoredMeanRange = (FixPoint1616_t)(sum_ranging << 16); + xTalkStoredMeanRange /= total_count; + xTalkStoredMeanRtnSpads = (FixPoint1616_t)(sum_spads << 16); + xTalkStoredMeanRtnSpads /= total_count; + + + + + + + + + + + + + + xTalkStoredMeanRtnSpadsAsInt = (xTalkStoredMeanRtnSpads + + 0x8000) >> 16; + + + + + + + xTalkCalDistanceAsInt = ((uint32_t)BDTable[ + VL53L1_TUNING_SINGLE_TARGET_XTALK_TARGET_DISTANCE_MM]); + if (xTalkStoredMeanRtnSpadsAsInt == 0 || + xTalkCalDistanceAsInt == 0 || + xTalkStoredMeanRange >= (xTalkCalDistanceAsInt << 16)) { + XTalkCompensationRateMegaCps = 0; + } else { + + + + + + + signalXTalkTotalPerSpad = (xTalkStoredMeanSignalRate) / + xTalkStoredMeanRtnSpadsAsInt; + + + + + + + + signalXTalkTotalPerSpad *= (((uint32_t)1 << 16) - + (xTalkStoredMeanRange / xTalkCalDistanceAsInt)); + + + + XTalkCompensationRateMegaCps = (signalXTalkTotalPerSpad + + 0x8000) >> 16; + } + + + Status = VL53L1_GetCalibrationData(Dev, &CalibrationData); + + if (Status != VL53L1_ERROR_NONE) + goto ENDFUNC; + + pC = &CalibrationData.customer; + + pC->algo__crosstalk_compensation_plane_offset_kcps = + (uint32_t)(1000 * ((XTalkCompensationRateMegaCps + + ((uint32_t)1<<6)) >> (16-9))); + + Status = VL53L1_SetCalibrationData(Dev, &CalibrationData); + + if (Status != VL53L1_ERROR_NONE) + goto ENDFUNC; + + Status = VL53L1_enable_xtalk_compensation(Dev); + + } else + + + Status = VL53L1_ERROR_XTALK_EXTRACTION_NO_SAMPLE_FAIL; + +ENDFUNC: + LOG_FUNCTION_END(Status); + return Status; + +} + + + + + + + + + + +static VL53L1_Error CheckValidRectRoi(VL53L1_UserRoi_t ROI) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + if ((ROI.TopLeftX > 15) || (ROI.TopLeftY > 15) || + (ROI.BotRightX > 15) || (ROI.BotRightY > 15)) + Status = VL53L1_ERROR_INVALID_PARAMS; + + if ((ROI.TopLeftX > ROI.BotRightX) || (ROI.TopLeftY < ROI.BotRightY)) + Status = VL53L1_ERROR_INVALID_PARAMS; + + LOG_FUNCTION_END(Status); + return Status; +} + +static VL53L1_GPIO_Interrupt_Mode ConvertModeToLLD(VL53L1_Error *pStatus, + VL53L1_ThresholdMode CrossMode) +{ + VL53L1_GPIO_Interrupt_Mode Mode; + + switch (CrossMode) { + case VL53L1_THRESHOLD_CROSSED_LOW: + Mode = VL53L1_GPIOINTMODE_LEVEL_LOW; + break; + case VL53L1_THRESHOLD_CROSSED_HIGH: + Mode = VL53L1_GPIOINTMODE_LEVEL_HIGH; + break; + case VL53L1_THRESHOLD_OUT_OF_WINDOW: + Mode = VL53L1_GPIOINTMODE_OUT_OF_WINDOW; + break; + case VL53L1_THRESHOLD_IN_WINDOW: + Mode = VL53L1_GPIOINTMODE_IN_WINDOW; + break; + default: + + + Mode = VL53L1_GPIOINTMODE_LEVEL_HIGH; + *pStatus = VL53L1_ERROR_INVALID_PARAMS; + } + return Mode; +} + +static VL53L1_ThresholdMode ConvertModeFromLLD(VL53L1_Error *pStatus, + VL53L1_GPIO_Interrupt_Mode CrossMode) +{ + VL53L1_ThresholdMode Mode; + + switch (CrossMode) { + case VL53L1_GPIOINTMODE_LEVEL_LOW: + Mode = VL53L1_THRESHOLD_CROSSED_LOW; + break; + case VL53L1_GPIOINTMODE_LEVEL_HIGH: + Mode = VL53L1_THRESHOLD_CROSSED_HIGH; + break; + case VL53L1_GPIOINTMODE_OUT_OF_WINDOW: + Mode = VL53L1_THRESHOLD_OUT_OF_WINDOW; + break; + case VL53L1_GPIOINTMODE_IN_WINDOW: + Mode = VL53L1_THRESHOLD_IN_WINDOW; + break; + default: + + + Mode = VL53L1_THRESHOLD_CROSSED_HIGH; + *pStatus = VL53L1_ERROR_UNDEFINED; + } + return Mode; +} + + + + +VL53L1_Error VL53L1_GetVersion(VL53L1_Version_t *pVersion) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + pVersion->major = VL53L1_IMPLEMENTATION_VER_MAJOR; + pVersion->minor = VL53L1_IMPLEMENTATION_VER_MINOR; + pVersion->build = VL53L1_IMPLEMENTATION_VER_SUB; + + pVersion->revision = VL53L1_IMPLEMENTATION_VER_REVISION; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetProductRevision(VL53L1_DEV Dev, + uint8_t *pProductRevisionMajor, uint8_t *pProductRevisionMinor) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t revision_id; + VL53L1_LLDriverData_t *pLLData; + + LOG_FUNCTION_START(""); + + pLLData = VL53L1DevStructGetLLDriverHandle(Dev); + revision_id = pLLData->nvm_copy_data.identification__revision_id; + *pProductRevisionMajor = 1; + *pProductRevisionMinor = (revision_id & 0xF0) >> 4; + + LOG_FUNCTION_END(Status); + return Status; + +} + +VL53L1_Error VL53L1_GetDeviceInfo(VL53L1_DEV Dev, + VL53L1_DeviceInfo_t *pVL53L1_DeviceInfo) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t revision_id; + VL53L1_LLDriverData_t *pLLData; + + LOG_FUNCTION_START(""); + + pLLData = VL53L1DevStructGetLLDriverHandle(Dev); + + strncpy(pVL53L1_DeviceInfo->ProductId, "", + VL53L1_DEVINFO_STRLEN-1); + pVL53L1_DeviceInfo->ProductType = + pLLData->nvm_copy_data.identification__module_type; + + revision_id = pLLData->nvm_copy_data.identification__revision_id; + pVL53L1_DeviceInfo->ProductRevisionMajor = 1; + pVL53L1_DeviceInfo->ProductRevisionMinor = (revision_id & 0xF0) >> 4; + +#ifndef VL53L1_USE_EMPTY_STRING + if (pVL53L1_DeviceInfo->ProductRevisionMinor == 0) + strncpy(pVL53L1_DeviceInfo->Name, + VL53L1_STRING_DEVICE_INFO_NAME0, + VL53L1_DEVINFO_STRLEN-1); + else + strncpy(pVL53L1_DeviceInfo->Name, + VL53L1_STRING_DEVICE_INFO_NAME1, + VL53L1_DEVINFO_STRLEN-1); + strncpy(pVL53L1_DeviceInfo->Type, + VL53L1_STRING_DEVICE_INFO_TYPE, + VL53L1_DEVINFO_STRLEN-1); + + + if (pVL53L1_DeviceInfo->ProductType == 0xAA) { + pVL53L1_DeviceInfo->Name[5] = '3'; + pVL53L1_DeviceInfo->Type[5] = '3'; + } +#else + pVL53L1_DeviceInfo->Name[0] = 0; + pVL53L1_DeviceInfo->Type[0] = 0; +#endif + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_GetRangeStatusString(uint8_t RangeStatus, + char *pRangeStatusString) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_range_status_string(RangeStatus, + pRangeStatusString); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetPalErrorString(VL53L1_Error PalErrorCode, + char *pPalErrorString) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_pal_error_string(PalErrorCode, pPalErrorString); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetPalStateString(VL53L1_State PalStateCode, + char *pPalStateString) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_pal_state_string(PalStateCode, pPalStateString); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetPalState(VL53L1_DEV Dev, VL53L1_State *pPalState) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pPalState = VL53L1DevDataGet(Dev, PalState); + + LOG_FUNCTION_END(Status); + return Status; +} + + + + + + +VL53L1_Error VL53L1_SetDeviceAddress(VL53L1_DEV Dev, uint8_t DeviceAddress) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_WrByte(Dev, VL53L1_I2C_SLAVE__DEVICE_ADDRESS, + DeviceAddress / 2); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_DataInit(VL53L1_DEV Dev) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t i; + + LOG_FUNCTION_START(""); + + + +#ifdef USE_I2C_2V8 + Status = VL53L1_RdByte(Dev, VL53L1_PAD_I2C_HV__EXTSUP_CONFIG, &i); + if (Status == VL53L1_ERROR_NONE) { + i = (i & 0xfe) | 0x01; + Status = VL53L1_WrByte(Dev, VL53L1_PAD_I2C_HV__EXTSUP_CONFIG, + i); + } +#endif + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_data_init(Dev, 1); + + if (Status == VL53L1_ERROR_NONE) { + VL53L1DevDataSet(Dev, PalState, VL53L1_STATE_WAIT_STATICINIT); + VL53L1DevDataSet(Dev, CurrentParameters.PresetMode, + VL53L1_PRESETMODE_RANGING); + } + + + + for (i = 0; i < VL53L1_CHECKENABLE_NUMBER_OF_CHECKS; i++) { + if (Status == VL53L1_ERROR_NONE) + Status |= VL53L1_SetLimitCheckEnable(Dev, i, 1); + else + break; + + } + + + + if (Status == VL53L1_ERROR_NONE) { + Status = VL53L1_SetLimitCheckValue(Dev, + VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, + (FixPoint1616_t)(18 * 65536)); + } + if (Status == VL53L1_ERROR_NONE) { + Status = VL53L1_SetLimitCheckValue(Dev, + VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + (FixPoint1616_t)(25 * 65536 / 100)); + + + } + + + + + if (Status == VL53L1_ERROR_NONE) { + Status = VL53L1_set_dmax_mode(Dev, + VL53L1_DEVICEDMAXMODE__CUST_CAL_DATA); + } + + + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_StaticInit(VL53L1_DEV Dev) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t measurement_mode; + + LOG_FUNCTION_START(""); + + VL53L1DevDataSet(Dev, PalState, VL53L1_STATE_IDLE); + + measurement_mode = VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + VL53L1DevDataSet(Dev, LLData.measurement_mode, measurement_mode); + + VL53L1DevDataSet(Dev, CurrentParameters.DistanceMode, + VL53L1_DISTANCEMODE_LONG); + + VL53L1DevDataSet(Dev, CurrentParameters.OutputMode, + VL53L1_OUTPUTMODE_NEAREST); + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_WaitDeviceBooted(VL53L1_DEV Dev) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_poll_for_boot_completion(Dev, + VL53L1_BOOT_COMPLETION_POLLING_TIMEOUT_MS); + + LOG_FUNCTION_END(Status); + return Status; +} + + + + + + +static VL53L1_Error ComputeDevicePresetMode( + VL53L1_PresetModes PresetMode, + VL53L1_DistanceModes DistanceMode, + VL53L1_DevicePresetModes *pDevicePresetMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + uint8_t DistIdx; + VL53L1_DevicePresetModes LightModes[3] = { + VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_SHORT_RANGE, + VL53L1_DEVICEPRESETMODE_STANDARD_RANGING, + VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_LONG_RANGE}; + + VL53L1_DevicePresetModes RangingModes[3] = { + VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE, + VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE, + VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE}; + + VL53L1_DevicePresetModes ScanningModes[3] = { + VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE_SHORT_RANGE, + VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE, + VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE_LONG_RANGE}; + + VL53L1_DevicePresetModes TimedModes[3] = { + VL53L1_DEVICEPRESETMODE_TIMED_RANGING_SHORT_RANGE, + VL53L1_DEVICEPRESETMODE_TIMED_RANGING, + VL53L1_DEVICEPRESETMODE_TIMED_RANGING_LONG_RANGE}; + + VL53L1_DevicePresetModes LowPowerTimedModes[3] = { + VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_SHORT_RANGE, + VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_MEDIUM_RANGE, + VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_LONG_RANGE}; + + *pDevicePresetMode = VL53L1_DEVICEPRESETMODE_STANDARD_RANGING; + + switch (DistanceMode) { + case VL53L1_DISTANCEMODE_SHORT: + DistIdx = 0; + break; + case VL53L1_DISTANCEMODE_MEDIUM: + DistIdx = 1; + break; + default: + DistIdx = 2; + } + + switch (PresetMode) { + case VL53L1_PRESETMODE_LITE_RANGING: + *pDevicePresetMode = LightModes[DistIdx]; + break; + + case VL53L1_PRESETMODE_RANGING: + *pDevicePresetMode = RangingModes[DistIdx]; + break; + + case VL53L1_PRESETMODE_MULTIZONES_SCANNING: + *pDevicePresetMode = ScanningModes[DistIdx]; + break; + + case VL53L1_PRESETMODE_AUTONOMOUS: + *pDevicePresetMode = TimedModes[DistIdx]; + break; + + case VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS: + *pDevicePresetMode = LowPowerTimedModes[DistIdx]; + break; + case VL53L1_PRESETMODE_OLT: + *pDevicePresetMode = VL53L1_DEVICEPRESETMODE_OLT; + break; + case VL53L1_PRESETMODE_PROXY_RANGING_MODE: + *pDevicePresetMode = + VL53L1_DEVICEPRESETMODE_SPECIAL_HISTOGRAM_SHORT_RANGE; + break; + + default: + + + Status = VL53L1_ERROR_MODE_NOT_SUPPORTED; + } + + return Status; +} + +static VL53L1_Error SetPresetMode(VL53L1_DEV Dev, + VL53L1_PresetModes PresetMode, + VL53L1_DistanceModes DistanceMode, + uint32_t inter_measurement_period_ms) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_DevicePresetModes device_preset_mode; + uint8_t measurement_mode; + uint16_t dss_config__target_total_rate_mcps; + uint32_t phasecal_config_timeout_us; + uint32_t mm_config_timeout_us; + uint32_t lld_range_config_timeout_us; + + LOG_FUNCTION_START("%d", (int)PresetMode); + + if ((PresetMode == VL53L1_PRESETMODE_AUTONOMOUS) || + (PresetMode == VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS)) + measurement_mode = VL53L1_DEVICEMEASUREMENTMODE_TIMED; + else + measurement_mode = VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + + + Status = ComputeDevicePresetMode(PresetMode, DistanceMode, + &device_preset_mode); + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_get_preset_mode_timing_cfg(Dev, + device_preset_mode, + &dss_config__target_total_rate_mcps, + &phasecal_config_timeout_us, + &mm_config_timeout_us, + &lld_range_config_timeout_us); + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_set_preset_mode( + Dev, + device_preset_mode, + dss_config__target_total_rate_mcps, + phasecal_config_timeout_us, + mm_config_timeout_us, + lld_range_config_timeout_us, + inter_measurement_period_ms); + + if (Status == VL53L1_ERROR_NONE) + VL53L1DevDataSet(Dev, LLData.measurement_mode, + measurement_mode); + + if (Status == VL53L1_ERROR_NONE) + VL53L1DevDataSet(Dev, CurrentParameters.PresetMode, PresetMode); + + VL53L1DevDataSet(Dev, CurrentParameters.OutputMode, + VL53L1_OUTPUTMODE_NEAREST); + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetPresetMode(VL53L1_DEV Dev, VL53L1_PresetModes PresetMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_DistanceModes DistanceMode = VL53L1_DISTANCEMODE_LONG; + + LOG_FUNCTION_START("%d", (int)PresetMode); + + + + Status = VL53L1_low_power_auto_data_init(Dev); + + if (PresetMode == VL53L1_PRESETMODE_PROXY_RANGING_MODE) + DistanceMode = VL53L1_DISTANCEMODE_SHORT; + Status = SetPresetMode(Dev, + PresetMode, + DistanceMode, + 1000); + + if (Status == VL53L1_ERROR_NONE) { + if ((PresetMode == VL53L1_PRESETMODE_LITE_RANGING) || + (PresetMode == VL53L1_PRESETMODE_AUTONOMOUS) || + (PresetMode == VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS)) + Status = VL53L1_SetMeasurementTimingBudgetMicroSeconds( + Dev, 41000); + else + + + Status = VL53L1_SetMeasurementTimingBudgetMicroSeconds( + Dev, 33333); + } + + if (Status == VL53L1_ERROR_NONE) { + + + Status = VL53L1_SetInterMeasurementPeriodMilliSeconds(Dev, + 1000); + } + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_GetPresetMode(VL53L1_DEV Dev, + VL53L1_PresetModes *pPresetMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pPresetMode = VL53L1DevDataGet(Dev, CurrentParameters.PresetMode); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetDistanceMode(VL53L1_DEV Dev, + VL53L1_DistanceModes DistanceMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_PresetModes PresetMode; + uint32_t inter_measurement_period_ms; + uint32_t TimingBudget; + uint32_t MmTimeoutUs; + uint32_t PhaseCalTimeoutUs; + VL53L1_zone_config_t zone_config; + + LOG_FUNCTION_START("%d", (int)DistanceMode); + + PresetMode = VL53L1DevDataGet(Dev, CurrentParameters.PresetMode); + + + + + + + + if ((PresetMode == VL53L1_PRESETMODE_PROXY_RANGING_MODE) && + (DistanceMode != VL53L1_DISTANCEMODE_SHORT)) + return VL53L1_ERROR_INVALID_PARAMS; + if ((DistanceMode != VL53L1_DISTANCEMODE_SHORT) && + (DistanceMode != VL53L1_DISTANCEMODE_MEDIUM) && + (DistanceMode != VL53L1_DISTANCEMODE_LONG)) + return VL53L1_ERROR_INVALID_PARAMS; + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_get_zone_config(Dev, &zone_config); + + inter_measurement_period_ms = VL53L1DevDataGet(Dev, + LLData.inter_measurement_period_ms); + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_get_timeouts_us(Dev, &PhaseCalTimeoutUs, + &MmTimeoutUs, &TimingBudget); + + if (Status == VL53L1_ERROR_NONE) + Status = SetPresetMode(Dev, + PresetMode, + DistanceMode, + inter_measurement_period_ms); + + if (Status == VL53L1_ERROR_NONE) { + VL53L1DevDataSet(Dev, CurrentParameters.DistanceMode, + DistanceMode); + } + + if (Status == VL53L1_ERROR_NONE) { + Status = VL53L1_set_timeouts_us(Dev, PhaseCalTimeoutUs, + MmTimeoutUs, TimingBudget); + + if (Status == VL53L1_ERROR_NONE) + VL53L1DevDataSet(Dev, LLData.range_config_timeout_us, + TimingBudget); + } + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_set_zone_config(Dev, &zone_config); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetDistanceMode(VL53L1_DEV Dev, + VL53L1_DistanceModes *pDistanceMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pDistanceMode = VL53L1DevDataGet(Dev, CurrentParameters.DistanceMode); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetOutputMode(VL53L1_DEV Dev, + VL53L1_OutputModes OutputMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if ((OutputMode != VL53L1_OUTPUTMODE_NEAREST) && + (OutputMode != VL53L1_OUTPUTMODE_STRONGEST)) + Status = VL53L1_ERROR_MODE_NOT_SUPPORTED; + else + VL53L1DevDataSet(Dev, CurrentParameters.OutputMode, OutputMode); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetOutputMode(VL53L1_DEV Dev, + VL53L1_OutputModes *pOutputMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pOutputMode = VL53L1DevDataGet(Dev, CurrentParameters.OutputMode); + + LOG_FUNCTION_END(Status); + return Status; +} + + + +VL53L1_Error VL53L1_SetMeasurementTimingBudgetMicroSeconds(VL53L1_DEV Dev, + uint32_t MeasurementTimingBudgetMicroSeconds) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t Mm1Enabled; + uint8_t Mm2Enabled; + uint32_t TimingGuard; + uint32_t divisor; + uint32_t TimingBudget; + uint32_t MmTimeoutUs; + VL53L1_PresetModes PresetMode; + uint32_t PhaseCalTimeoutUs; + uint32_t vhv; + int32_t vhv_loops; + uint32_t FDAMaxTimingBudgetUs = FDA_MAX_TIMING_BUDGET_US; + + LOG_FUNCTION_START(""); + + + + if (MeasurementTimingBudgetMicroSeconds > 10000000) + Status = VL53L1_ERROR_INVALID_PARAMS; + + if (Status == VL53L1_ERROR_NONE) { + Status = VL53L1_GetSequenceStepEnable(Dev, + VL53L1_SEQUENCESTEP_MM1, &Mm1Enabled); + } + + if (Status == VL53L1_ERROR_NONE) { + Status = VL53L1_GetSequenceStepEnable(Dev, + VL53L1_SEQUENCESTEP_MM2, &Mm2Enabled); + } + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_get_timeouts_us(Dev, + &PhaseCalTimeoutUs, + &MmTimeoutUs, + &TimingBudget); + + if (Status == VL53L1_ERROR_NONE) { + PresetMode = VL53L1DevDataGet(Dev, + CurrentParameters.PresetMode); + + TimingGuard = 0; + divisor = 1; + switch (PresetMode) { + case VL53L1_PRESETMODE_LITE_RANGING: + if ((Mm1Enabled == 1) || (Mm2Enabled == 1)) + TimingGuard = 5000; + else + TimingGuard = 1000; + break; + + case VL53L1_PRESETMODE_AUTONOMOUS: + FDAMaxTimingBudgetUs *= 2; + if ((Mm1Enabled == 1) || (Mm2Enabled == 1)) + TimingGuard = 26600; + else + TimingGuard = 21600; + divisor = 2; + break; + + case VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS: + FDAMaxTimingBudgetUs *= 2; + vhv = LOWPOWER_AUTO_VHV_LOOP_DURATION_US; + VL53L1_get_tuning_parm(Dev, + VL53L1_TUNINGPARM_LOWPOWERAUTO_VHV_LOOP_BOUND, + &vhv_loops); + if (vhv_loops > 0) { + vhv += vhv_loops * + LOWPOWER_AUTO_VHV_LOOP_DURATION_US; + } + TimingGuard = LOWPOWER_AUTO_OVERHEAD_BEFORE_A_RANGING + + LOWPOWER_AUTO_OVERHEAD_BETWEEN_A_B_RANGING + + vhv; + divisor = 2; + break; + + case VL53L1_PRESETMODE_RANGING: + case VL53L1_PRESETMODE_MULTIZONES_SCANNING: + case VL53L1_PRESETMODE_PROXY_RANGING_MODE: + TimingGuard = 1700; + divisor = 6; + break; + + case VL53L1_PRESETMODE_OLT: + TimingGuard = MmTimeoutUs + 5000; + break; + default: + + + Status = VL53L1_ERROR_MODE_NOT_SUPPORTED; + } + + if (MeasurementTimingBudgetMicroSeconds <= TimingGuard) + Status = VL53L1_ERROR_INVALID_PARAMS; + else { + TimingBudget = (MeasurementTimingBudgetMicroSeconds + - TimingGuard); + } + + if (Status == VL53L1_ERROR_NONE) { + if (TimingBudget > FDAMaxTimingBudgetUs) + Status = VL53L1_ERROR_INVALID_PARAMS; + else { + TimingBudget /= divisor; + Status = VL53L1_set_timeouts_us( + Dev, + PhaseCalTimeoutUs, + MmTimeoutUs, + TimingBudget); + } + + if (Status == VL53L1_ERROR_NONE) + VL53L1DevDataSet(Dev, + LLData.range_config_timeout_us, + TimingBudget); + } + } + if (Status == VL53L1_ERROR_NONE) { + VL53L1DevDataSet(Dev, + CurrentParameters.MeasurementTimingBudgetMicroSeconds, + MeasurementTimingBudgetMicroSeconds); + } + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_GetMeasurementTimingBudgetMicroSeconds(VL53L1_DEV Dev, + uint32_t *pMeasurementTimingBudgetMicroSeconds) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t Mm1Enabled = 0; + uint8_t Mm2Enabled = 0; + uint32_t MmTimeoutUs = 0; + uint32_t RangeTimeoutUs = 0; + uint32_t MeasTimingBdg = 0; + uint32_t PhaseCalTimeoutUs = 0; + VL53L1_PresetModes PresetMode; + uint32_t TimingGuard; + uint32_t vhv; + int32_t vhv_loops; + + LOG_FUNCTION_START(""); + + *pMeasurementTimingBudgetMicroSeconds = 0; + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_GetSequenceStepEnable(Dev, + VL53L1_SEQUENCESTEP_MM1, &Mm1Enabled); + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_GetSequenceStepEnable(Dev, + VL53L1_SEQUENCESTEP_MM2, &Mm2Enabled); + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_get_timeouts_us(Dev, + &PhaseCalTimeoutUs, + &MmTimeoutUs, + &RangeTimeoutUs); + + if (Status == VL53L1_ERROR_NONE) { + PresetMode = VL53L1DevDataGet(Dev, + CurrentParameters.PresetMode); + + switch (PresetMode) { + case VL53L1_PRESETMODE_LITE_RANGING: + if ((Mm1Enabled == 1) || (Mm2Enabled == 1)) + MeasTimingBdg = RangeTimeoutUs + 5000; + else + MeasTimingBdg = RangeTimeoutUs + 1000; + + break; + + case VL53L1_PRESETMODE_AUTONOMOUS: + if ((Mm1Enabled == 1) || (Mm2Enabled == 1)) + MeasTimingBdg = 2 * RangeTimeoutUs + 26600; + else + MeasTimingBdg = 2 * RangeTimeoutUs + 21600; + + break; + + case VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS: + vhv = LOWPOWER_AUTO_VHV_LOOP_DURATION_US; + VL53L1_get_tuning_parm(Dev, + VL53L1_TUNINGPARM_LOWPOWERAUTO_VHV_LOOP_BOUND, + &vhv_loops); + if (vhv_loops > 0) { + vhv += vhv_loops * + LOWPOWER_AUTO_VHV_LOOP_DURATION_US; + } + TimingGuard = LOWPOWER_AUTO_OVERHEAD_BEFORE_A_RANGING + + LOWPOWER_AUTO_OVERHEAD_BETWEEN_A_B_RANGING + + vhv; + MeasTimingBdg = 2 * RangeTimeoutUs + TimingGuard; + break; + + case VL53L1_PRESETMODE_RANGING: + case VL53L1_PRESETMODE_MULTIZONES_SCANNING: + case VL53L1_PRESETMODE_PROXY_RANGING_MODE: + MeasTimingBdg = (6 * RangeTimeoutUs) + 1700; + break; + + case VL53L1_PRESETMODE_OLT: + MeasTimingBdg = RangeTimeoutUs + MmTimeoutUs + 5000; + break; + default: + + + Status = VL53L1_ERROR_MODE_NOT_SUPPORTED; + } + } + if (Status == VL53L1_ERROR_NONE) + *pMeasurementTimingBudgetMicroSeconds = MeasTimingBdg; + + LOG_FUNCTION_END(Status); + return Status; +} + + + +VL53L1_Error VL53L1_SetInterMeasurementPeriodMilliSeconds(VL53L1_DEV Dev, + uint32_t InterMeasurementPeriodMilliSeconds) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint32_t adjustedIMP; + + LOG_FUNCTION_START(""); + + + + adjustedIMP = InterMeasurementPeriodMilliSeconds; + adjustedIMP += (adjustedIMP * 64) / 1000; + + + Status = VL53L1_set_inter_measurement_period_ms(Dev, + adjustedIMP); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetInterMeasurementPeriodMilliSeconds(VL53L1_DEV Dev, + uint32_t *pInterMeasurementPeriodMilliSeconds) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint32_t adjustedIMP; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_inter_measurement_period_ms(Dev, &adjustedIMP); + + + adjustedIMP -= (adjustedIMP * 64) / 1000; + *pInterMeasurementPeriodMilliSeconds = adjustedIMP; + + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetDmaxReflectance(VL53L1_DEV Dev, + FixPoint1616_t DmaxReflectance) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_dmax_reflectance_array_t dmax_reflectances; + + LOG_FUNCTION_START(""); + + if (DmaxReflectance > 100*65536) + Status = VL53L1_ERROR_INVALID_PARAMS; + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_get_dmax_reflectance_values(Dev, + &dmax_reflectances); + + if (Status == VL53L1_ERROR_NONE) { + dmax_reflectances.target_reflectance_for_dmax[ + DMAX_REFLECTANCE_IDX] = + VL53L1_FIXPOINT1616TOFIXPOINT72(DmaxReflectance); + Status = VL53L1_set_dmax_reflectance_values(Dev, + &dmax_reflectances); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetDmaxReflectance(VL53L1_DEV Dev, + FixPoint1616_t *pDmaxReflectance) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_dmax_reflectance_array_t dmax_reflectances; + uint16_t r; + + LOG_FUNCTION_START(""); + Status = VL53L1_get_dmax_reflectance_values(Dev, &dmax_reflectances); + if (Status == VL53L1_ERROR_NONE) { + r = dmax_reflectances.target_reflectance_for_dmax[ + DMAX_REFLECTANCE_IDX]; + *pDmaxReflectance = VL53L1_FIXPOINT72TOFIXPOINT1616(r); + } + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_SetDmaxMode(VL53L1_DEV Dev, + VL53L1_DeviceDmaxModes DmaxMode) +{ + + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_DeviceDmaxMode dmax_mode; + + LOG_FUNCTION_START(""); + + switch (DmaxMode) { + case VL53L1_DMAXMODE_FMT_CAL_DATA: + dmax_mode = VL53L1_DEVICEDMAXMODE__FMT_CAL_DATA; + break; + case VL53L1_DMAXMODE_CUSTCAL_DATA: + dmax_mode = VL53L1_DEVICEDMAXMODE__CUST_CAL_DATA; + break; + case VL53L1_DMAXMODE_PER_ZONE_CAL_DATA: + dmax_mode = VL53L1_DEVICEDMAXMODE__PER_ZONE_CAL_DATA; + break; + default: + Status = VL53L1_ERROR_INVALID_PARAMS; + break; + } + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_set_dmax_mode(Dev, dmax_mode); + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_GetDmaxMode(VL53L1_DEV Dev, + VL53L1_DeviceDmaxModes *pDmaxMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_DeviceDmaxMode dmax_mode; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_dmax_mode(Dev, &dmax_mode); + if (Status == VL53L1_ERROR_NONE) { + switch (dmax_mode) { + case VL53L1_DEVICEDMAXMODE__FMT_CAL_DATA: + *pDmaxMode = VL53L1_DMAXMODE_FMT_CAL_DATA; + break; + case VL53L1_DEVICEDMAXMODE__CUST_CAL_DATA: + *pDmaxMode = VL53L1_DMAXMODE_CUSTCAL_DATA; + break; + case VL53L1_DEVICEDMAXMODE__PER_ZONE_CAL_DATA: + *pDmaxMode = VL53L1_DMAXMODE_PER_ZONE_CAL_DATA; + break; + default: + + + *pDmaxMode = VL53L1_ERROR_NOT_IMPLEMENTED; + break; + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + + + + + + + + +VL53L1_Error VL53L1_GetNumberOfLimitCheck(uint16_t *pNumberOfLimitCheck) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pNumberOfLimitCheck = VL53L1_CHECKENABLE_NUMBER_OF_CHECKS; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetLimitCheckInfo(uint16_t LimitCheckId, + char *pLimitCheckString) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_limit_check_info(LimitCheckId, + pLimitCheckString); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetLimitCheckStatus(VL53L1_DEV Dev, uint16_t LimitCheckId, + uint8_t *pLimitCheckStatus) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t Temp8; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L1_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L1_ERROR_INVALID_PARAMS; + } else { + VL53L1_GETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + LimitCheckId, Temp8); + *pLimitCheckStatus = Temp8; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +static VL53L1_Error SetLimitValue(VL53L1_DEV Dev, uint16_t LimitCheckId, + FixPoint1616_t value) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint16_t tmpuint16; + + + LOG_FUNCTION_START(""); + + switch (LimitCheckId) { + case VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE: + tmpuint16 = VL53L1_FIXPOINT1616TOFIXPOINT142(value); + VL53L1_set_lite_sigma_threshold(Dev, tmpuint16); + break; + case VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: + tmpuint16 = VL53L1_FIXPOINT1616TOFIXPOINT97(value); + VL53L1_set_lite_min_count_rate(Dev, tmpuint16); + break; + default: + Status = VL53L1_ERROR_INVALID_PARAMS; + } + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_SetLimitCheckEnable(VL53L1_DEV Dev, uint16_t LimitCheckId, + uint8_t LimitCheckEnable) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + FixPoint1616_t TempFix1616 = 0; + + LOG_FUNCTION_START(""); + + + if (LimitCheckId >= VL53L1_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L1_ERROR_INVALID_PARAMS; + } else { + + + if (LimitCheckEnable == 0) + TempFix1616 = 0; + else + VL53L1_GETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + LimitCheckId, TempFix1616); + + Status = SetLimitValue(Dev, LimitCheckId, TempFix1616); + } + + if (Status == VL53L1_ERROR_NONE) + VL53L1_SETARRAYPARAMETERFIELD(Dev, + LimitChecksEnable, + LimitCheckId, + ((LimitCheckEnable == 0) ? 0 : 1)); + + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetLimitCheckEnable(VL53L1_DEV Dev, uint16_t LimitCheckId, + uint8_t *pLimitCheckEnable) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t Temp8; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L1_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L1_ERROR_INVALID_PARAMS; + *pLimitCheckEnable = 0; + } else { + VL53L1_GETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + LimitCheckId, Temp8); + *pLimitCheckEnable = Temp8; + } + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetLimitCheckValue(VL53L1_DEV Dev, uint16_t LimitCheckId, + FixPoint1616_t LimitCheckValue) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t LimitChecksEnable; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L1_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L1_ERROR_INVALID_PARAMS; + } else { + + VL53L1_GETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + LimitCheckId, + LimitChecksEnable); + + if (LimitChecksEnable == 0) { + + + VL53L1_SETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + LimitCheckId, LimitCheckValue); + } else { + + Status = SetLimitValue(Dev, LimitCheckId, + LimitCheckValue); + + if (Status == VL53L1_ERROR_NONE) { + VL53L1_SETARRAYPARAMETERFIELD(Dev, + LimitChecksValue, + LimitCheckId, LimitCheckValue); + } + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetLimitCheckValue(VL53L1_DEV Dev, uint16_t LimitCheckId, + FixPoint1616_t *pLimitCheckValue) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint16_t MinCountRate; + FixPoint1616_t TempFix1616; + uint16_t SigmaThresh; + + LOG_FUNCTION_START(""); + + switch (LimitCheckId) { + case VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE: + Status = VL53L1_get_lite_sigma_threshold(Dev, &SigmaThresh); + TempFix1616 = VL53L1_FIXPOINT142TOFIXPOINT1616(SigmaThresh); + break; + case VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: + Status = VL53L1_get_lite_min_count_rate(Dev, &MinCountRate); + TempFix1616 = VL53L1_FIXPOINT97TOFIXPOINT1616(MinCountRate); + break; + default: + Status = VL53L1_ERROR_INVALID_PARAMS; + } + + if (Status == VL53L1_ERROR_NONE) { + + if (TempFix1616 == 0) { + + + VL53L1_GETARRAYPARAMETERFIELD(Dev, + LimitChecksValue, LimitCheckId, + TempFix1616); + *pLimitCheckValue = TempFix1616; + VL53L1_SETARRAYPARAMETERFIELD(Dev, + LimitChecksEnable, LimitCheckId, 0); + } else { + *pLimitCheckValue = TempFix1616; + VL53L1_SETARRAYPARAMETERFIELD(Dev, + LimitChecksValue, LimitCheckId, + TempFix1616); + VL53L1_SETARRAYPARAMETERFIELD(Dev, + LimitChecksEnable, LimitCheckId, 1); + } + } + LOG_FUNCTION_END(Status); + return Status; + +} + +VL53L1_Error VL53L1_GetLimitCheckCurrent(VL53L1_DEV Dev, uint16_t LimitCheckId, + FixPoint1616_t *pLimitCheckCurrent) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + FixPoint1616_t TempFix1616 = 0; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L1_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L1_ERROR_INVALID_PARAMS; + } else { + VL53L1_GETARRAYPARAMETERFIELD(Dev, LimitChecksCurrent, + LimitCheckId, TempFix1616); + *pLimitCheckCurrent = TempFix1616; + } + + LOG_FUNCTION_END(Status); + return Status; + +} + + + + + + + + + + +VL53L1_Error VL53L1_GetMaxNumberOfROI(VL53L1_DEV Dev, + uint8_t *pMaxNumberOfROI) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_PresetModes PresetMode; + + LOG_FUNCTION_START(""); + + PresetMode = VL53L1DevDataGet(Dev, CurrentParameters.PresetMode); + + + + if (PresetMode == VL53L1_PRESETMODE_MULTIZONES_SCANNING) + *pMaxNumberOfROI = VL53L1_MAX_USER_ZONES; + else + *pMaxNumberOfROI = 1; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetROI(VL53L1_DEV Dev, + VL53L1_RoiConfig_t *pRoiConfig) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_PresetModes PresetMode; + uint8_t MaxNumberOfROI = 1; + VL53L1_zone_config_t zone_cfg; + VL53L1_UserRoi_t CurrROI; + uint8_t i; + uint8_t x_centre; + uint8_t y_centre; + uint8_t width, height; + + LOG_FUNCTION_START(""); + + + + PresetMode = VL53L1DevDataGet(Dev, CurrentParameters.PresetMode); + + + + if (PresetMode == VL53L1_PRESETMODE_MULTIZONES_SCANNING) + MaxNumberOfROI = VL53L1_MAX_USER_ZONES; + + if ((pRoiConfig->NumberOfRoi > MaxNumberOfROI) || + (pRoiConfig->NumberOfRoi < 1)) + Status = VL53L1_ERROR_INVALID_PARAMS; + + if (Status == VL53L1_ERROR_NONE) { + + + + zone_cfg.max_zones = MaxNumberOfROI; + zone_cfg.active_zones = pRoiConfig->NumberOfRoi - 1; + + for (i = 0; i < pRoiConfig->NumberOfRoi; i++) { + CurrROI = pRoiConfig->UserRois[i]; + + + + + + + + + + Status = CheckValidRectRoi(CurrROI); + if (Status != VL53L1_ERROR_NONE) + break; + + x_centre = (CurrROI.BotRightX + CurrROI.TopLeftX + 1) + / 2; + y_centre = (CurrROI.TopLeftY + CurrROI.BotRightY + 1) + / 2; + width = (CurrROI.BotRightX - CurrROI.TopLeftX); + height = (CurrROI.TopLeftY - CurrROI.BotRightY); + if ((width < 3) || (height < 3)) { + Status = VL53L1_ERROR_INVALID_PARAMS; + break; + } + zone_cfg.user_zones[i].x_centre = x_centre; + zone_cfg.user_zones[i].y_centre = y_centre; + zone_cfg.user_zones[i].width = width; + zone_cfg.user_zones[i].height = height; + } + } + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_set_zone_config(Dev, &zone_cfg); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetROI(VL53L1_DEV Dev, + VL53L1_RoiConfig_t *pRoiConfig) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_zone_config_t zone_cfg; + uint8_t i; + uint8_t TopLeftX; + uint8_t TopLeftY; + uint8_t BotRightX; + uint8_t BotRightY; + + LOG_FUNCTION_START(""); + + VL53L1_get_zone_config(Dev, &zone_cfg); + + pRoiConfig->NumberOfRoi = zone_cfg.active_zones + 1; + + for (i = 0; i < pRoiConfig->NumberOfRoi; i++) { + TopLeftX = (2 * zone_cfg.user_zones[i].x_centre - + zone_cfg.user_zones[i].width) >> 1; + TopLeftY = (2 * zone_cfg.user_zones[i].y_centre + + zone_cfg.user_zones[i].height) >> 1; + BotRightX = (2 * zone_cfg.user_zones[i].x_centre + + zone_cfg.user_zones[i].width) >> 1; + BotRightY = (2 * zone_cfg.user_zones[i].y_centre - + zone_cfg.user_zones[i].height) >> 1; + pRoiConfig->UserRois[i].TopLeftX = TopLeftX; + pRoiConfig->UserRois[i].TopLeftY = TopLeftY; + pRoiConfig->UserRois[i].BotRightX = BotRightX; + pRoiConfig->UserRois[i].BotRightY = BotRightY; + } + + LOG_FUNCTION_END(Status); + return Status; +} + + + + + + + + + +VL53L1_Error VL53L1_GetNumberOfSequenceSteps(VL53L1_DEV Dev, + uint8_t *pNumberOfSequenceSteps) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + SUPPRESS_UNUSED_WARNING(Dev); + + LOG_FUNCTION_START(""); + + *pNumberOfSequenceSteps = VL53L1_SEQUENCESTEP_NUMBER_OF_ITEMS; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetSequenceStepsInfo(VL53L1_SequenceStepId SequenceStepId, + char *pSequenceStepsString) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_sequence_steps_info( + SequenceStepId, + pSequenceStepsString); + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L1_Error VL53L1_SetSequenceStepEnable(VL53L1_DEV Dev, + VL53L1_SequenceStepId SequenceStepId, uint8_t SequenceStepEnabled) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint32_t MeasurementTimingBudgetMicroSeconds; + + LOG_FUNCTION_START(""); + + + + + + + Status = VL53L1_set_sequence_config_bit(Dev, + (VL53L1_DeviceSequenceConfig)SequenceStepId, + SequenceStepEnabled); + + + + if (Status == VL53L1_ERROR_NONE) { + + + + MeasurementTimingBudgetMicroSeconds = VL53L1DevDataGet(Dev, + CurrentParameters.MeasurementTimingBudgetMicroSeconds); + + VL53L1_SetMeasurementTimingBudgetMicroSeconds(Dev, + MeasurementTimingBudgetMicroSeconds); + } + + LOG_FUNCTION_END(Status); + + return Status; +} + + +VL53L1_Error VL53L1_GetSequenceStepEnable(VL53L1_DEV Dev, + VL53L1_SequenceStepId SequenceStepId, uint8_t *pSequenceStepEnabled) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_sequence_config_bit(Dev, + (VL53L1_DeviceSequenceConfig)SequenceStepId, + pSequenceStepEnabled); + + LOG_FUNCTION_END(Status); + return Status; +} + + + + + + + + + + + + +VL53L1_Error VL53L1_StartMeasurement(VL53L1_DEV Dev) +{ +#define TIMED_MODE_TIMING_GUARD_MILLISECONDS 4 + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t DeviceMeasurementMode; + VL53L1_State CurrPalState; + VL53L1_Error lStatus; + uint32_t MTBus, IMPms; + + LOG_FUNCTION_START(""); + + CurrPalState = VL53L1DevDataGet(Dev, PalState); + switch (CurrPalState) { + case VL53L1_STATE_IDLE: + Status = VL53L1_ERROR_NONE; + break; + case VL53L1_STATE_POWERDOWN: + case VL53L1_STATE_WAIT_STATICINIT: + case VL53L1_STATE_STANDBY: + case VL53L1_STATE_RUNNING: + case VL53L1_STATE_RESET: + case VL53L1_STATE_UNKNOWN: + case VL53L1_STATE_ERROR: + Status = VL53L1_ERROR_INVALID_COMMAND; + break; + default: + Status = VL53L1_ERROR_UNDEFINED; + } + + DeviceMeasurementMode = VL53L1DevDataGet(Dev, LLData.measurement_mode); + + + + + if ((Status == VL53L1_ERROR_NONE) && + (DeviceMeasurementMode == VL53L1_DEVICEMEASUREMENTMODE_TIMED)) { + lStatus = VL53L1_GetMeasurementTimingBudgetMicroSeconds(Dev, + &MTBus); + + + MTBus /= 1000; + lStatus = VL53L1_GetInterMeasurementPeriodMilliSeconds(Dev, + &IMPms); + + + SUPPRESS_UNUSED_WARNING(lStatus); + if (IMPms < MTBus + TIMED_MODE_TIMING_GUARD_MILLISECONDS) + Status = VL53L1_ERROR_INVALID_PARAMS; + } + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_init_and_start_range( + Dev, + DeviceMeasurementMode, + VL53L1_DEVICECONFIGLEVEL_FULL); + + + + if (Status == VL53L1_ERROR_NONE) + VL53L1DevDataSet(Dev, PalState, VL53L1_STATE_RUNNING); + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_StopMeasurement(VL53L1_DEV Dev) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_stop_range(Dev); + + + + if (Status == VL53L1_ERROR_NONE) + VL53L1DevDataSet(Dev, PalState, VL53L1_STATE_IDLE); + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_ClearInterruptAndStartMeasurement(VL53L1_DEV Dev) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t DeviceMeasurementMode; + + LOG_FUNCTION_START(""); + + DeviceMeasurementMode = VL53L1DevDataGet(Dev, LLData.measurement_mode); + + Status = VL53L1_clear_interrupt_and_enable_next_range(Dev, + DeviceMeasurementMode); + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_GetMeasurementDataReady(VL53L1_DEV Dev, + uint8_t *pMeasurementDataReady) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_is_new_data_ready(Dev, pMeasurementDataReady); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_WaitMeasurementDataReady(VL53L1_DEV Dev) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + + Status = VL53L1_poll_for_range_completion(Dev, + VL53L1_RANGE_COMPLETION_POLLING_TIMEOUT_MS); + + LOG_FUNCTION_END(Status); + return Status; +} + +static void GenNewPresetMode(int16_t RefRange, + VL53L1_DistanceModes InternalDistanceMode, + VL53L1_DistanceModes *pNewDistanceMode) +{ + uint16_t HRLI = 600; + uint16_t HRLH = 700; + uint16_t MRLI = 1400; + uint16_t MRLH = 1500; + + switch (InternalDistanceMode) { + case VL53L1_DISTANCEMODE_SHORT: + + + + + + if (RefRange > MRLH) + *pNewDistanceMode = VL53L1_DISTANCEMODE_LONG; + else if (RefRange > HRLH) + *pNewDistanceMode = VL53L1_DISTANCEMODE_MEDIUM; + break; + case VL53L1_DISTANCEMODE_MEDIUM: + + + + + + if (RefRange > MRLH) + *pNewDistanceMode = VL53L1_DISTANCEMODE_LONG; + else if (RefRange < HRLI) + *pNewDistanceMode = VL53L1_DISTANCEMODE_SHORT; + break; + default: + + + + + + + if (RefRange < HRLI) + *pNewDistanceMode = VL53L1_DISTANCEMODE_SHORT; + else if (RefRange < MRLI) + *pNewDistanceMode = VL53L1_DISTANCEMODE_MEDIUM; + break; + } +} + +static void CheckAndChangeDistanceMode(VL53L1_DEV Dev, + VL53L1_TargetRangeData_t *pRangeData, + int16_t Ambient100DmaxMm, + VL53L1_DistanceModes *pNewDistanceMode +) +{ + VL53L1_DistanceModes DistanceMode; + uint8_t RangeStatus = pRangeData->RangeStatus; + uint8_t DmaxValid; + int32_t MinAmbient = BDTable[VL53L1_TUNING_MIN_AMBIENT_DMAX_VALID]; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + int32_t tmpint32; + + + + switch (RangeStatus) { + case VL53L1_RANGESTATUS_RANGE_VALID_NO_WRAP_CHECK_FAIL: + case VL53L1_RANGESTATUS_WRAP_TARGET_FAIL: + case VL53L1_RANGESTATUS_RANGE_VALID_MERGED_PULSE: + case VL53L1_RANGESTATUS_TARGET_PRESENT_LACK_OF_SIGNAL: + case VL53L1_RANGESTATUS_SYNCRONISATION_INT: + case VL53L1_RANGESTATUS_NONE: + return; + default: + + + break; + } + + DmaxValid = 1; + tmpint32 = pdev->hist_data.VL53L1_p_004; + if ((tmpint32 < MinAmbient) || (Ambient100DmaxMm == 0)) + DmaxValid = 0; + + DistanceMode = VL53L1DevDataGet(Dev, + CurrentParameters.DistanceMode); + + *pNewDistanceMode = DistanceMode; + + if (RangeStatus == VL53L1_RANGESTATUS_RANGE_VALID) + GenNewPresetMode(pRangeData->RangeMilliMeter, + DistanceMode, pNewDistanceMode); + else { + if (DmaxValid) + GenNewPresetMode(Ambient100DmaxMm, + DistanceMode, pNewDistanceMode); + else + *pNewDistanceMode = VL53L1_DISTANCEMODE_LONG; + } +} + +static uint8_t ComputeRQL(uint8_t active_results, + uint8_t FilteredRangeStatus, + VL53L1_range_data_t *presults_data) +{ + int16_t T_Wide = 150; + int16_t SRL = 300; + uint16_t SRAS = 30; + FixPoint1616_t RAS; + FixPoint1616_t SRQL; + FixPoint1616_t GI = 7713587; + + FixPoint1616_t GGm = 3198157; + + FixPoint1616_t LRAP = 6554; + + FixPoint1616_t partial; + uint8_t finalvalue; + uint8_t returnvalue; + + if (active_results == 0) + returnvalue = 0; + else if (((presults_data->max_range_mm - + presults_data->min_range_mm) >= T_Wide) || + (FilteredRangeStatus == VL53L1_DEVICEERROR_PHASECONSISTENCY)) + returnvalue = 50; + else { + if (presults_data->median_range_mm < SRL) + RAS = SRAS * 65536; + else + RAS = LRAP * presults_data->median_range_mm; + + + + if (RAS != 0) { + partial = (GGm * presults_data->VL53L1_p_005); + partial = partial + (RAS >> 1); + partial = partial / RAS; + partial = partial * 65536; + if (partial <= GI) + SRQL = GI - partial; + else + SRQL = 50 * 65536; + } else + SRQL = 100 * 65536; + + finalvalue = (uint8_t)(SRQL >> 16); + returnvalue = MAX(50, MIN(100, finalvalue)); + } + + return returnvalue; +} + + +static uint8_t ConvertStatusLite(uint8_t FilteredRangeStatus) +{ + uint8_t RangeStatus; + + switch (FilteredRangeStatus) { + case VL53L1_DEVICEERROR_GPHSTREAMCOUNT0READY: + RangeStatus = VL53L1_RANGESTATUS_SYNCRONISATION_INT; + break; + case VL53L1_DEVICEERROR_RANGECOMPLETE_NO_WRAP_CHECK: + RangeStatus = VL53L1_RANGESTATUS_RANGE_VALID_NO_WRAP_CHECK_FAIL; + break; + case VL53L1_DEVICEERROR_RANGEPHASECHECK: + RangeStatus = VL53L1_RANGESTATUS_OUTOFBOUNDS_FAIL; + break; + case VL53L1_DEVICEERROR_MSRCNOTARGET: + RangeStatus = VL53L1_RANGESTATUS_SIGNAL_FAIL; + break; + case VL53L1_DEVICEERROR_SIGMATHRESHOLDCHECK: + RangeStatus = VL53L1_RANGESTATUS_SIGMA_FAIL; + break; + case VL53L1_DEVICEERROR_PHASECONSISTENCY: + RangeStatus = VL53L1_RANGESTATUS_WRAP_TARGET_FAIL; + break; + case VL53L1_DEVICEERROR_RANGEIGNORETHRESHOLD: + RangeStatus = VL53L1_RANGESTATUS_XTALK_SIGNAL_FAIL; + break; + case VL53L1_DEVICEERROR_MINCLIP: + RangeStatus = VL53L1_RANGESTATUS_RANGE_VALID_MIN_RANGE_CLIPPED; + break; + case VL53L1_DEVICEERROR_RANGECOMPLETE: + RangeStatus = VL53L1_RANGESTATUS_RANGE_VALID; + break; + default: + RangeStatus = VL53L1_RANGESTATUS_NONE; + } + + return RangeStatus; +} + + +static uint8_t ConvertStatusHisto(uint8_t FilteredRangeStatus) +{ + uint8_t RangeStatus; + + switch (FilteredRangeStatus) { + case VL53L1_DEVICEERROR_RANGEPHASECHECK: + RangeStatus = VL53L1_RANGESTATUS_OUTOFBOUNDS_FAIL; + break; + case VL53L1_DEVICEERROR_SIGMATHRESHOLDCHECK: + RangeStatus = VL53L1_RANGESTATUS_SIGMA_FAIL; + break; + case VL53L1_DEVICEERROR_RANGECOMPLETE_NO_WRAP_CHECK: + RangeStatus = VL53L1_RANGESTATUS_RANGE_VALID_NO_WRAP_CHECK_FAIL; + break; + case VL53L1_DEVICEERROR_PHASECONSISTENCY: + RangeStatus = VL53L1_RANGESTATUS_WRAP_TARGET_FAIL; + break; + case VL53L1_DEVICEERROR_PREV_RANGE_NO_TARGETS: + RangeStatus = VL53L1_RANGESTATUS_TARGET_PRESENT_LACK_OF_SIGNAL; + break; + case VL53L1_DEVICEERROR_EVENTCONSISTENCY: + RangeStatus = VL53L1_RANGESTATUS_WRAP_TARGET_FAIL; + break; + case VL53L1_DEVICEERROR_RANGECOMPLETE_MERGED_PULSE: + RangeStatus = VL53L1_RANGESTATUS_RANGE_VALID_MERGED_PULSE; + break; + case VL53L1_DEVICEERROR_RANGECOMPLETE: + RangeStatus = VL53L1_RANGESTATUS_RANGE_VALID; + break; + default: + RangeStatus = VL53L1_RANGESTATUS_NONE; + } + + return RangeStatus; +} + +static VL53L1_Error SetSimpleData(VL53L1_DEV Dev, + uint8_t active_results, uint8_t device_status, + VL53L1_range_data_t *presults_data, + VL53L1_RangingMeasurementData_t *pRangeData) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t FilteredRangeStatus; + uint8_t SigmaLimitflag; + uint8_t SignalLimitflag; + uint8_t Temp8Enable; + uint8_t Temp8; + FixPoint1616_t AmbientRate; + FixPoint1616_t SignalRate; + FixPoint1616_t TempFix1616; + FixPoint1616_t LimitCheckValue; + VL53L1_PresetModes PresetMode; + int16_t Range; + + pRangeData->TimeStamp = presults_data->time_stamp; + + FilteredRangeStatus = presults_data->range_status & 0x1F; + + pRangeData->RangeQualityLevel = ComputeRQL(active_results, + FilteredRangeStatus, + presults_data); + + SignalRate = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->peak_signal_count_rate_mcps); + pRangeData->SignalRateRtnMegaCps + = SignalRate; + + AmbientRate = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->ambient_count_rate_mcps); + pRangeData->AmbientRateRtnMegaCps = AmbientRate; + + pRangeData->EffectiveSpadRtnCount = + presults_data->VL53L1_p_006; + + TempFix1616 = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->VL53L1_p_005); + + pRangeData->SigmaMilliMeter = TempFix1616; + + pRangeData->RangeMilliMeter = presults_data->median_range_mm; + + pRangeData->RangeFractionalPart = 0; + + + + switch (device_status) { + case VL53L1_DEVICEERROR_MULTCLIPFAIL: + case VL53L1_DEVICEERROR_VCSELWATCHDOGTESTFAILURE: + case VL53L1_DEVICEERROR_VCSELCONTINUITYTESTFAILURE: + case VL53L1_DEVICEERROR_NOVHVVALUEFOUND: + pRangeData->RangeStatus = VL53L1_RANGESTATUS_HARDWARE_FAIL; + break; + case VL53L1_DEVICEERROR_USERROICLIP: + pRangeData->RangeStatus = VL53L1_RANGESTATUS_MIN_RANGE_FAIL; + break; + default: + pRangeData->RangeStatus = VL53L1_RANGESTATUS_RANGE_VALID; + } + + + + if (pRangeData->RangeStatus == VL53L1_RANGESTATUS_RANGE_VALID) { + PresetMode = VL53L1DevDataGet(Dev, + CurrentParameters.PresetMode); + if ((PresetMode == VL53L1_PRESETMODE_MULTIZONES_SCANNING) || + (PresetMode == VL53L1_PRESETMODE_RANGING) || + (PresetMode == VL53L1_PRESETMODE_PROXY_RANGING_MODE)) + pRangeData->RangeStatus = + ConvertStatusHisto(FilteredRangeStatus); + else + pRangeData->RangeStatus = + ConvertStatusLite(FilteredRangeStatus); + } + + + + TempFix1616 = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->VL53L1_p_005); + VL53L1_SETARRAYPARAMETERFIELD(Dev, + LimitChecksCurrent, VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, + TempFix1616); + + TempFix1616 = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->peak_signal_count_rate_mcps); + VL53L1_SETARRAYPARAMETERFIELD(Dev, + LimitChecksCurrent, VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + TempFix1616); + + + + + + VL53L1_GetLimitCheckValue(Dev, + VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, + &LimitCheckValue); + + SigmaLimitflag = (FilteredRangeStatus == + VL53L1_DEVICEERROR_SIGMATHRESHOLDCHECK) + ? 1 : 0; + + VL53L1_GetLimitCheckEnable(Dev, + VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, + &Temp8Enable); + + Temp8 = ((Temp8Enable == 1) && (SigmaLimitflag == 1)) ? 1 : 0; + VL53L1_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, Temp8); + + + + VL53L1_GetLimitCheckValue(Dev, + VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + &LimitCheckValue); + + SignalLimitflag = (FilteredRangeStatus == + VL53L1_DEVICEERROR_MSRCNOTARGET) + ? 1 : 0; + + VL53L1_GetLimitCheckEnable(Dev, + VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + &Temp8Enable); + + Temp8 = ((Temp8Enable == 1) && (SignalLimitflag == 1)) ? 1 : 0; + VL53L1_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, Temp8); + + Range = pRangeData->RangeMilliMeter; + if ((pRangeData->RangeStatus == VL53L1_RANGESTATUS_RANGE_VALID) && + (Range < 0)) { + if (Range < BDTable[VL53L1_TUNING_PROXY_MIN]) + pRangeData->RangeStatus = + VL53L1_RANGESTATUS_RANGE_INVALID; + else + pRangeData->RangeMilliMeter = 0; + } + + return Status; +} + +static VL53L1_Error SetTargetData(VL53L1_DEV Dev, + uint8_t active_results, uint8_t device_status, + VL53L1_range_data_t *presults_data, + VL53L1_TargetRangeData_t *pRangeData) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t FilteredRangeStatus; + uint8_t SigmaLimitflag; + uint8_t SignalLimitflag; + uint8_t Temp8Enable; + uint8_t Temp8; + FixPoint1616_t AmbientRate; + FixPoint1616_t SignalRate; + FixPoint1616_t TempFix1616; + FixPoint1616_t LimitCheckValue; + VL53L1_PresetModes PresetMode; + int16_t Range; + + FilteredRangeStatus = presults_data->range_status & 0x1F; + + pRangeData->RangeQualityLevel = ComputeRQL(active_results, + FilteredRangeStatus, + presults_data); + + SignalRate = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->peak_signal_count_rate_mcps); + pRangeData->SignalRateRtnMegaCps + = SignalRate; + + AmbientRate = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->ambient_count_rate_mcps); + pRangeData->AmbientRateRtnMegaCps = AmbientRate; + + TempFix1616 = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->VL53L1_p_005); + + pRangeData->SigmaMilliMeter = TempFix1616; + + pRangeData->RangeMilliMeter = presults_data->median_range_mm; + pRangeData->RangeMaxMilliMeter = presults_data->max_range_mm; + pRangeData->RangeMinMilliMeter = presults_data->min_range_mm; + + pRangeData->RangeFractionalPart = 0; + + + + switch (device_status) { + case VL53L1_DEVICEERROR_MULTCLIPFAIL: + case VL53L1_DEVICEERROR_VCSELWATCHDOGTESTFAILURE: + case VL53L1_DEVICEERROR_VCSELCONTINUITYTESTFAILURE: + case VL53L1_DEVICEERROR_NOVHVVALUEFOUND: + pRangeData->RangeStatus = VL53L1_RANGESTATUS_HARDWARE_FAIL; + break; + case VL53L1_DEVICEERROR_USERROICLIP: + pRangeData->RangeStatus = VL53L1_RANGESTATUS_MIN_RANGE_FAIL; + break; + default: + pRangeData->RangeStatus = VL53L1_RANGESTATUS_RANGE_VALID; + } + + + + if ((pRangeData->RangeStatus == VL53L1_RANGESTATUS_RANGE_VALID) && + (active_results == 0)) { + pRangeData->RangeStatus = VL53L1_RANGESTATUS_NONE; + pRangeData->SignalRateRtnMegaCps = 0; + pRangeData->SigmaMilliMeter = 0; + pRangeData->RangeMilliMeter = 8191; + pRangeData->RangeMaxMilliMeter = 8191; + pRangeData->RangeMinMilliMeter = 8191; + } + + + + if (pRangeData->RangeStatus == VL53L1_RANGESTATUS_RANGE_VALID) { + PresetMode = VL53L1DevDataGet(Dev, + CurrentParameters.PresetMode); + if ((PresetMode == VL53L1_PRESETMODE_MULTIZONES_SCANNING) || + (PresetMode == VL53L1_PRESETMODE_RANGING) || + (PresetMode == VL53L1_PRESETMODE_PROXY_RANGING_MODE)) + pRangeData->RangeStatus = + ConvertStatusHisto(FilteredRangeStatus); + else + pRangeData->RangeStatus = + ConvertStatusLite(FilteredRangeStatus); + } + + + + TempFix1616 = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->VL53L1_p_005); + VL53L1_SETARRAYPARAMETERFIELD(Dev, + LimitChecksCurrent, VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, + TempFix1616); + + TempFix1616 = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->peak_signal_count_rate_mcps); + VL53L1_SETARRAYPARAMETERFIELD(Dev, + LimitChecksCurrent, VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + TempFix1616); + + + + + + VL53L1_GetLimitCheckValue(Dev, + VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, + &LimitCheckValue); + + SigmaLimitflag = (FilteredRangeStatus == + VL53L1_DEVICEERROR_SIGMATHRESHOLDCHECK) + ? 1 : 0; + + VL53L1_GetLimitCheckEnable(Dev, + VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, + &Temp8Enable); + + Temp8 = ((Temp8Enable == 1) && (SigmaLimitflag == 1)) ? 1 : 0; + VL53L1_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, Temp8); + + + + VL53L1_GetLimitCheckValue(Dev, + VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + &LimitCheckValue); + + SignalLimitflag = (FilteredRangeStatus == + VL53L1_DEVICEERROR_MSRCNOTARGET) + ? 1 : 0; + + VL53L1_GetLimitCheckEnable(Dev, + VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + &Temp8Enable); + + Temp8 = ((Temp8Enable == 1) && (SignalLimitflag == 1)) ? 1 : 0; + VL53L1_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, Temp8); + + Range = pRangeData->RangeMilliMeter; + if ((pRangeData->RangeStatus == VL53L1_RANGESTATUS_RANGE_VALID) && + (Range < 0)) { + if (Range < BDTable[VL53L1_TUNING_PROXY_MIN]) + pRangeData->RangeStatus = + VL53L1_RANGESTATUS_RANGE_INVALID; + else + pRangeData->RangeMilliMeter = 0; + } + + return Status; +} + +static uint8_t GetOutputDataIndex(VL53L1_DEV Dev, + VL53L1_range_results_t *presults) +{ + uint8_t i; + uint8_t index = 0; + VL53L1_OutputModes OutputMode; + + OutputMode = VL53L1DevDataGet(Dev, CurrentParameters.OutputMode); + + + + + if (OutputMode == VL53L1_OUTPUTMODE_NEAREST) + return 0; + + + + + + for (i = 1; i < presults->active_results; i++) { + if (presults->VL53L1_p_002[i].peak_signal_count_rate_mcps > + presults->VL53L1_p_002[index].peak_signal_count_rate_mcps) + index = i; + } + + return index; +} + +VL53L1_Error VL53L1_GetRangingMeasurementData(VL53L1_DEV Dev, + VL53L1_RangingMeasurementData_t *pRangingMeasurementData) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_range_results_t results; + VL53L1_range_results_t *presults = &results; + VL53L1_range_data_t *presults_data; + VL53L1_PresetModes PresetMode; + uint8_t index = 0; + + LOG_FUNCTION_START(""); + + + + PresetMode = VL53L1DevDataGet(Dev, CurrentParameters.PresetMode); + + if (PresetMode == VL53L1_PRESETMODE_MULTIZONES_SCANNING) { + Status = VL53L1_ERROR_MODE_NOT_SUPPORTED; + LOG_FUNCTION_END(Status); + return Status; + } + + + + memset(pRangingMeasurementData, 0xFF, + sizeof(VL53L1_RangingMeasurementData_t)); + + + + Status = VL53L1_get_device_results( + Dev, + VL53L1_DEVICERESULTSLEVEL_FULL, + presults); + + if (Status == VL53L1_ERROR_NONE) { + pRangingMeasurementData->StreamCount = presults->stream_count; + + + + + + index = GetOutputDataIndex(Dev, presults); + presults_data = &(presults->VL53L1_p_002[index]); + Status = SetSimpleData(Dev, presults->active_results, + presults->device_status, + presults_data, + pRangingMeasurementData); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +static VL53L1_Error SetMeasurementData(VL53L1_DEV Dev, + VL53L1_range_results_t *presults, + VL53L1_MultiRangingData_t *pMultiRangingData) +{ + uint8_t i; + uint8_t iteration; + VL53L1_TargetRangeData_t *pRangeData; + VL53L1_range_data_t *presults_data; + int16_t dmax_min; + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t Furthest_idx = 0; + int16_t Furthest_range = 0; + uint8_t ActiveResults; + + pMultiRangingData->NumberOfObjectsFound = presults->active_results; + pMultiRangingData->RoiNumber = presults->zone_id; + pMultiRangingData->HasXtalkValueChanged = + presults->smudge_corrector_data.new_xtalk_applied_flag; + dmax_min = MIN(presults->wrap_dmax_mm, + presults->VL53L1_p_007[DMAX_REFLECTANCE_IDX]); + pMultiRangingData->DmaxMilliMeter = dmax_min; + + + + + + pMultiRangingData->TimeStamp = 0; + + pMultiRangingData->StreamCount = presults->stream_count; + + pMultiRangingData->RecommendedDistanceMode = + VL53L1DevDataGet(Dev, CurrentParameters.DistanceMode); + ActiveResults = presults->active_results; + if (ActiveResults < 1) + + + + + iteration = 1; + else + iteration = ActiveResults; + for (i = 0; i < iteration; i++) { + pRangeData = &(pMultiRangingData->RangeData[i]); + + presults_data = &(presults->VL53L1_p_002[i]); + if (Status == VL53L1_ERROR_NONE) + Status = SetTargetData(Dev, ActiveResults, + presults->device_status, + presults_data, + pRangeData); + + pMultiRangingData->EffectiveSpadRtnCount = + presults_data->VL53L1_p_006; + + if ((pRangeData->RangeStatus == VL53L1_RANGESTATUS_RANGE_VALID) + && (pRangeData->RangeMilliMeter > Furthest_range)) { + Furthest_range = pRangeData->RangeMilliMeter; + Furthest_idx = i; + } + + } + + + if ((Status == VL53L1_ERROR_NONE) && (ActiveResults > 0)) { + pRangeData = &(pMultiRangingData->RangeData[Furthest_idx]); + CheckAndChangeDistanceMode(Dev, pRangeData, + presults->VL53L1_p_007[VL53L1_MAX_AMBIENT_DMAX_VALUES-1], + &pMultiRangingData->RecommendedDistanceMode); + } + + + return Status; +} + +VL53L1_Error VL53L1_GetMultiRangingData(VL53L1_DEV Dev, + VL53L1_MultiRangingData_t *pMultiRangingData) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_range_results_t results; + VL53L1_range_results_t *presults = &results; + + LOG_FUNCTION_START(""); + + + + memset(pMultiRangingData, 0xFF, + sizeof(VL53L1_MultiRangingData_t)); + + + + Status = VL53L1_get_device_results( + Dev, + VL53L1_DEVICERESULTSLEVEL_FULL, + presults); + + + if (Status == VL53L1_ERROR_NONE) { + + switch (presults->rd_device_state) { + case VL53L1_DEVICESTATE_RANGING_GATHER_DATA: + pMultiRangingData->RoiStatus = + VL53L1_ROISTATUS_VALID_NOT_LAST; + break; + case VL53L1_DEVICESTATE_RANGING_OUTPUT_DATA: + pMultiRangingData->RoiStatus = + VL53L1_ROISTATUS_VALID_LAST; + break; + default: + pMultiRangingData->RoiStatus = + VL53L1_ROISTATUS_NOT_VALID; + } + + Status = SetMeasurementData(Dev, + presults, + pMultiRangingData); + + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetAdditionalData(VL53L1_DEV Dev, + VL53L1_AdditionalData_t *pAdditionalData) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_additional_data(Dev, pAdditionalData); + + LOG_FUNCTION_END(Status); + return Status; +} + + + + + + + + +VL53L1_Error VL53L1_SetTuningParameter(VL53L1_DEV Dev, + uint16_t TuningParameterId, int32_t TuningParameterValue) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + if (TuningParameterId == + VL53L1_TUNINGPARM_DYNXTALK_NODETECT_XTALK_OFFSET_KCPS) + return VL53L1_ERROR_INVALID_PARAMS; + + + if (TuningParameterId >= 32768) + Status = VL53L1_set_tuning_parm(Dev, + TuningParameterId, + TuningParameterValue); + else { + if (TuningParameterId < VL53L1_TUNING_MAX_TUNABLE_KEY) + BDTable[TuningParameterId] = TuningParameterValue; + else + Status = VL53L1_ERROR_INVALID_PARAMS; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetTuningParameter(VL53L1_DEV Dev, + uint16_t TuningParameterId, int32_t *pTuningParameterValue) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (TuningParameterId >= 32768) + Status = VL53L1_get_tuning_parm(Dev, + TuningParameterId, + pTuningParameterValue); + else { + if (TuningParameterId < VL53L1_TUNING_MAX_TUNABLE_KEY) + *pTuningParameterValue = BDTable[TuningParameterId]; + else + Status = VL53L1_ERROR_INVALID_PARAMS; + } + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_PerformRefSpadManagement(VL53L1_DEV Dev) +{ +#ifdef VL53L1_NOCALIB + VL53L1_Error Status = VL53L1_ERROR_NOT_SUPPORTED; + + SUPPRESS_UNUSED_WARNING(Dev); + + LOG_FUNCTION_START(""); +#else + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_Error RawStatus; + uint8_t dcrbuffer[24]; + uint8_t *commbuf; + uint8_t numloc[2] = {5, 3}; + VL53L1_LLDriverData_t *pdev; + VL53L1_customer_nvm_managed_t *pc; + VL53L1_PresetModes PresetMode; + + LOG_FUNCTION_START(""); + + pdev = VL53L1DevStructGetLLDriverHandle(Dev); + pc = &pdev->customer; + + if (Status == VL53L1_ERROR_NONE) { + PresetMode = VL53L1DevDataGet(Dev, + CurrentParameters.PresetMode); + Status = VL53L1_run_ref_spad_char(Dev, &RawStatus); + + + + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_SetPresetMode(Dev, PresetMode); + } + + if (Status == VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_HIGH) { + + + + + Status = VL53L1_read_nvm_raw_data(Dev, + (uint8_t)(0xA0 >> 2), + (uint8_t)(24 >> 2), + dcrbuffer); + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_WriteMulti(Dev, + VL53L1_REF_SPAD_MAN__NUM_REQUESTED_REF_SPADS, + numloc, 2); + + if (Status == VL53L1_ERROR_NONE) { + pc->ref_spad_man__num_requested_ref_spads = numloc[0]; + pc->ref_spad_man__ref_location = numloc[1]; + } + + if (Status == VL53L1_ERROR_NONE) + commbuf = &dcrbuffer[16]; + + + + + + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_WriteMulti(Dev, + VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_0, + commbuf, 6); + + if (Status == VL53L1_ERROR_NONE) { + pc->global_config__spad_enables_ref_0 = commbuf[0]; + pc->global_config__spad_enables_ref_1 = commbuf[1]; + pc->global_config__spad_enables_ref_2 = commbuf[2]; + pc->global_config__spad_enables_ref_3 = commbuf[3]; + pc->global_config__spad_enables_ref_4 = commbuf[4]; + pc->global_config__spad_enables_ref_5 = commbuf[5]; + } + + + } + +#endif + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SmudgeCorrectionEnable(VL53L1_DEV Dev, + VL53L1_SmudgeCorrectionModes Mode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_Error s1 = VL53L1_ERROR_NONE; + VL53L1_Error s2 = VL53L1_ERROR_NONE; + VL53L1_Error s3 = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + switch (Mode) { + case VL53L1_SMUDGE_CORRECTION_NONE: + s1 = VL53L1_dynamic_xtalk_correction_disable(Dev); + s2 = VL53L1_dynamic_xtalk_correction_apply_disable(Dev); + s3 = VL53L1_dynamic_xtalk_correction_single_apply_disable(Dev); + break; + case VL53L1_SMUDGE_CORRECTION_CONTINUOUS: + s1 = VL53L1_dynamic_xtalk_correction_enable(Dev); + s2 = VL53L1_dynamic_xtalk_correction_apply_enable(Dev); + s3 = VL53L1_dynamic_xtalk_correction_single_apply_disable(Dev); + break; + case VL53L1_SMUDGE_CORRECTION_SINGLE: + s1 = VL53L1_dynamic_xtalk_correction_enable(Dev); + s2 = VL53L1_dynamic_xtalk_correction_apply_enable(Dev); + s3 = VL53L1_dynamic_xtalk_correction_single_apply_enable(Dev); + break; + case VL53L1_SMUDGE_CORRECTION_DEBUG: + s1 = VL53L1_dynamic_xtalk_correction_enable(Dev); + s2 = VL53L1_dynamic_xtalk_correction_apply_disable(Dev); + s3 = VL53L1_dynamic_xtalk_correction_single_apply_disable(Dev); + break; + default: + Status = VL53L1_ERROR_INVALID_PARAMS; + break; + } + + if (Status == VL53L1_ERROR_NONE) { + Status = s1; + if (Status == VL53L1_ERROR_NONE) + Status = s2; + if (Status == VL53L1_ERROR_NONE) + Status = s3; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetXTalkCompensationEnable(VL53L1_DEV Dev, + uint8_t XTalkCompensationEnable) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (XTalkCompensationEnable == 0) + Status = VL53L1_disable_xtalk_compensation(Dev); + else + Status = VL53L1_enable_xtalk_compensation(Dev); + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_GetXTalkCompensationEnable(VL53L1_DEV Dev, + uint8_t *pXTalkCompensationEnable) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + VL53L1_get_xtalk_compensation_enable( + Dev, + pXTalkCompensationEnable); + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_PerformXTalkCalibration(VL53L1_DEV Dev, + uint8_t CalibrationOption) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_Error UnfilteredStatus; + int16_t CalDistanceMm; + VL53L1_xtalk_calibration_results_t xtalk; + + int16_t i; + uint32_t sum = 0; + VL53L1_CalibrationData_t caldata; + + LOG_FUNCTION_START(""); + + switch (CalibrationOption) { + case VL53L1_XTALKCALIBRATIONMODE_NO_TARGET: + Status = VL53L1_run_xtalk_extraction(Dev, &UnfilteredStatus); + + + if (Status == VL53L1_ERROR_XTALK_EXTRACTION_NO_SAMPLE_FAIL) + VL53L1_xtalk_cal_data_init(Dev); + break; + case VL53L1_XTALKCALIBRATIONMODE_SINGLE_TARGET: + Status = SingleTargetXTalkCalibration(Dev); + break; + case VL53L1_XTALKCALIBRATIONMODE_FULL_ROI: + CalDistanceMm = (int16_t) + BDTable[VL53L1_TUNING_XTALK_FULL_ROI_TARGET_DISTANCE_MM]; + Status = VL53L1_run_hist_xtalk_extraction(Dev, CalDistanceMm, + &UnfilteredStatus); + + /*fix for low xtalk coverglass*/ + VL53L1_GetCalibrationData(Dev, &caldata); + for (i=0;i<12;i++){ + sum += caldata.xtalkhisto.xtalk_shape.bin_data[i]; + } + + if (((caldata.customer.algo__crosstalk_compensation_plane_offset_kcps) > 1024000)|| + ((sum>1048) || (sum<1000))) + { + caldata.customer.algo__crosstalk_compensation_plane_offset_kcps= 50; + Dev->LLData.xtalk_cal.algo__crosstalk_compensation_plane_offset_kcps = 50; + caldata.xtalkhisto.xtalk_shape.bin_data[0]=307; + caldata.xtalkhisto.xtalk_shape.bin_data[1]=410; + caldata.xtalkhisto.xtalk_shape.bin_data[2]=410; + caldata.xtalkhisto.xtalk_shape.bin_data[3]=307; + for (i=4;i<12;i++){ + caldata.xtalkhisto.xtalk_shape.bin_data[i]=0; + } + VL53L1_SetCalibrationData(Dev, &caldata); + } + /*end of fix for low xtalk coverglass*/ + break; + default: + Status = VL53L1_ERROR_INVALID_PARAMS; + } + + + if (Status == VL53L1_ERROR_NONE) { + Status = VL53L1_get_current_xtalk_settings(Dev, &xtalk); + Status = VL53L1_set_tuning_parm(Dev, + VL53L1_TUNINGPARM_DYNXTALK_NODETECT_XTALK_OFFSET_KCPS, + xtalk.algo__crosstalk_compensation_plane_offset_kcps); + } + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetOffsetCalibrationMode(VL53L1_DEV Dev, + VL53L1_OffsetCalibrationModes OffsetCalibrationMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_OffsetCalibrationMode offset_cal_mode; + + LOG_FUNCTION_START(""); + + if (OffsetCalibrationMode == VL53L1_OFFSETCALIBRATIONMODE_STANDARD) { + offset_cal_mode = + VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD; + } else if (OffsetCalibrationMode == + VL53L1_OFFSETCALIBRATIONMODE_PRERANGE_ONLY) { + offset_cal_mode = + VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD_PRE_RANGE_ONLY; + } else if (OffsetCalibrationMode == + VL53L1_OFFSETCALIBRATIONMODE_MULTI_ZONE) { + offset_cal_mode = + VL53L1_OFFSETCALIBRATIONMODE__PER_ZONE; + } else { + Status = VL53L1_ERROR_INVALID_PARAMS; + } + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_set_offset_calibration_mode(Dev, + offset_cal_mode); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetOffsetCorrectionMode(VL53L1_DEV Dev, + VL53L1_OffsetCorrectionModes OffsetCorrectionMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_OffsetCorrectionMode offset_cor_mode; + + LOG_FUNCTION_START(""); + + if (OffsetCorrectionMode == VL53L1_OFFSETCORRECTIONMODE_STANDARD) { + offset_cor_mode = + VL53L1_OFFSETCORRECTIONMODE__MM1_MM2_OFFSETS; + } else if (OffsetCorrectionMode == + VL53L1_OFFSETCORRECTIONMODE_PERZONE) { + offset_cor_mode = + VL53L1_OFFSETCORRECTIONMODE__PER_ZONE_OFFSETS; + } else { + Status = VL53L1_ERROR_INVALID_PARAMS; + } + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_set_offset_correction_mode(Dev, + offset_cor_mode); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_PerformOffsetCalibration(VL53L1_DEV Dev, + int32_t CalDistanceMilliMeter, FixPoint1616_t CalReflectancePercent) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_Error UnfilteredStatus; + VL53L1_OffsetCalibrationMode offset_cal_mode; + uint16_t CalReflectancePercent_int; + + + VL53L1_DevicePresetModes device_preset_mode; + VL53L1_DeviceZonePreset zone_preset; + VL53L1_zone_config_t zone_cfg; + + LOG_FUNCTION_START(""); + + CalReflectancePercent_int = + VL53L1_FIXPOINT1616TOFIXPOINT72(CalReflectancePercent); + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_get_offset_calibration_mode(Dev, + &offset_cal_mode); + + if (Status != VL53L1_ERROR_NONE) { + LOG_FUNCTION_END(Status); + return Status; + } + + + if ((offset_cal_mode == + VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD) || + (offset_cal_mode == + VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD_PRE_RANGE_ONLY + )) { + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_run_offset_calibration( + Dev, + (int16_t)CalDistanceMilliMeter, + CalReflectancePercent_int, + &UnfilteredStatus); + + } else if (offset_cal_mode == + VL53L1_OFFSETCALIBRATIONMODE__PER_ZONE) { + device_preset_mode = + VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE_LONG_RANGE; + zone_preset = VL53L1_DEVICEZONEPRESET_CUSTOM; + + Status = VL53L1_get_zone_config(Dev, &zone_cfg); + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_run_zone_calibration( + Dev, + device_preset_mode, + zone_preset, + &zone_cfg, + (int16_t)CalDistanceMilliMeter, + CalReflectancePercent_int, + &UnfilteredStatus); + + } else { + Status = VL53L1_ERROR_INVALID_PARAMS; + } + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_PerformOffsetSimpleCalibration(VL53L1_DEV Dev, + int32_t CalDistanceMilliMeter) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + int32_t sum_ranging; + uint8_t offset_meas; + int16_t Max, UnderMax, OverMax, Repeat; + int32_t total_count, inloopcount; + int32_t IncRounding; + int16_t meanDistance_mm; + int16_t offset; + VL53L1_RangingMeasurementData_t RangingMeasurementData; + VL53L1_LLDriverData_t *pdev; + uint8_t goodmeas; + VL53L1_Error SmudgeStatus = VL53L1_ERROR_NONE; + uint8_t smudge_corr_en; + + LOG_FUNCTION_START(""); + + pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + + smudge_corr_en = pdev->smudge_correct_config.smudge_corr_enabled; + SmudgeStatus = VL53L1_dynamic_xtalk_correction_disable(Dev); + + + pdev->customer.algo__part_to_part_range_offset_mm = 0; + pdev->customer.mm_config__inner_offset_mm = 0; + pdev->customer.mm_config__outer_offset_mm = 0; + + Repeat = BDTable[VL53L1_TUNING_SIMPLE_OFFSET_CALIBRATION_REPEAT]; + Max = BDTable[ + VL53L1_TUNING_MAX_SIMPLE_OFFSET_CALIBRATION_SAMPLE_NUMBER]; + UnderMax = 1 + (Max / 2); + OverMax = Max + (Max / 2); + sum_ranging = 0; + total_count = 0; + + while ((Repeat > 0) && (Status == VL53L1_ERROR_NONE)) { + Status = VL53L1_StartMeasurement(Dev); + + + if (Status == VL53L1_ERROR_NONE) { + VL53L1_WaitMeasurementDataReady(Dev); + VL53L1_GetRangingMeasurementData(Dev, &RangingMeasurementData); + VL53L1_ClearInterruptAndStartMeasurement(Dev); + } + + + inloopcount = 0; + offset_meas = 0; + while ((Status == VL53L1_ERROR_NONE) && (inloopcount < Max) && + (offset_meas < OverMax)) { + Status = VL53L1_WaitMeasurementDataReady(Dev); + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_GetRangingMeasurementData(Dev, + &RangingMeasurementData); + goodmeas = (RangingMeasurementData.RangeStatus == + VL53L1_RANGESTATUS_RANGE_VALID); + if ((Status == VL53L1_ERROR_NONE) && goodmeas) { + sum_ranging = sum_ranging + + RangingMeasurementData.RangeMilliMeter; + inloopcount++; + } + Status = VL53L1_ClearInterruptAndStartMeasurement(Dev); + offset_meas++; + } + total_count += inloopcount; + + + + if (inloopcount < UnderMax) + Status = VL53L1_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL; + + VL53L1_StopMeasurement(Dev); + + Repeat--; + + } + + + if ((SmudgeStatus == VL53L1_ERROR_NONE) && (smudge_corr_en == 1)) + SmudgeStatus = VL53L1_dynamic_xtalk_correction_enable(Dev); + + + if ((sum_ranging < 0) || + (sum_ranging > ((int32_t) total_count * 0xffff))) { + Status = VL53L1_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH; + } + + if ((Status == VL53L1_ERROR_NONE) && (total_count > 0)) { + IncRounding = total_count / 2; + meanDistance_mm = (int16_t)((sum_ranging + IncRounding) + / total_count); + offset = (int16_t)CalDistanceMilliMeter - meanDistance_mm; + pdev->customer.algo__part_to_part_range_offset_mm = 0; + pdev->customer.mm_config__inner_offset_mm = offset; + pdev->customer.mm_config__outer_offset_mm = offset; + + Status = VL53L1_set_customer_nvm_managed(Dev, + &(pdev->customer)); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetCalibrationData(VL53L1_DEV Dev, + VL53L1_CalibrationData_t *pCalibrationData) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_CustomerNvmManaged_t *pC; + VL53L1_calibration_data_t cal_data; + uint32_t x; + VL53L1_xtalk_calibration_results_t xtalk; + + LOG_FUNCTION_START(""); + + cal_data.struct_version = pCalibrationData->struct_version - + VL53L1_ADDITIONAL_CALIBRATION_DATA_STRUCT_VERSION; + + + + + + + memcpy( + &(cal_data.fmt_dmax_cal), + &(pCalibrationData->fmt_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + + + memcpy( + &(cal_data.cust_dmax_cal), + &(pCalibrationData->cust_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + + + + memcpy( + &(cal_data.add_off_cal_data), + &(pCalibrationData->add_off_cal_data), + sizeof(VL53L1_additional_offset_cal_data_t)); + + + + memcpy( + &(cal_data.optical_centre), + &(pCalibrationData->optical_centre), + sizeof(VL53L1_optical_centre_t)); + + + + memcpy( + &(cal_data.xtalkhisto), + &(pCalibrationData->xtalkhisto), + sizeof(VL53L1_xtalk_histogram_data_t)); + + + + memcpy( + &(cal_data.gain_cal), + &(pCalibrationData->gain_cal), + sizeof(VL53L1_gain_calibration_data_t)); + + + + memcpy( + &(cal_data.cal_peak_rate_map), + &(pCalibrationData->cal_peak_rate_map), + sizeof(VL53L1_cal_peak_rate_map_t)); + + pC = &pCalibrationData->customer; + x = pC->algo__crosstalk_compensation_plane_offset_kcps; + cal_data.customer.algo__crosstalk_compensation_plane_offset_kcps = + (uint16_t)(x&0x0000FFFF); + + cal_data.customer.global_config__spad_enables_ref_0 = + pC->global_config__spad_enables_ref_0; + cal_data.customer.global_config__spad_enables_ref_1 = + pC->global_config__spad_enables_ref_1; + cal_data.customer.global_config__spad_enables_ref_2 = + pC->global_config__spad_enables_ref_2; + cal_data.customer.global_config__spad_enables_ref_3 = + pC->global_config__spad_enables_ref_3; + cal_data.customer.global_config__spad_enables_ref_4 = + pC->global_config__spad_enables_ref_4; + cal_data.customer.global_config__spad_enables_ref_5 = + pC->global_config__spad_enables_ref_5; + cal_data.customer.global_config__ref_en_start_select = + pC->global_config__ref_en_start_select; + cal_data.customer.ref_spad_man__num_requested_ref_spads = + pC->ref_spad_man__num_requested_ref_spads; + cal_data.customer.ref_spad_man__ref_location = + pC->ref_spad_man__ref_location; + cal_data.customer.algo__crosstalk_compensation_x_plane_gradient_kcps = + pC->algo__crosstalk_compensation_x_plane_gradient_kcps; + cal_data.customer.algo__crosstalk_compensation_y_plane_gradient_kcps = + pC->algo__crosstalk_compensation_y_plane_gradient_kcps; + cal_data.customer.ref_spad_char__total_rate_target_mcps = + pC->ref_spad_char__total_rate_target_mcps; + cal_data.customer.algo__part_to_part_range_offset_mm = + pC->algo__part_to_part_range_offset_mm; + cal_data.customer.mm_config__inner_offset_mm = + pC->mm_config__inner_offset_mm; + cal_data.customer.mm_config__outer_offset_mm = + pC->mm_config__outer_offset_mm; + + Status = VL53L1_set_part_to_part_data(Dev, &cal_data); + if (Status != VL53L1_ERROR_NONE) + goto ENDFUNC; + + Status = VL53L1_get_current_xtalk_settings(Dev, &xtalk); + + if (Status != VL53L1_ERROR_NONE) + goto ENDFUNC; + + xtalk.algo__crosstalk_compensation_plane_offset_kcps = x; + + + Status = VL53L1_set_tuning_parm(Dev, + VL53L1_TUNINGPARM_DYNXTALK_NODETECT_XTALK_OFFSET_KCPS, + x); + + + + Status = VL53L1_set_current_xtalk_settings(Dev, &xtalk); + +ENDFUNC: + LOG_FUNCTION_END(Status); + return Status; + +} + +VL53L1_Error VL53L1_GetCalibrationData(VL53L1_DEV Dev, + VL53L1_CalibrationData_t *pCalibrationData) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_calibration_data_t cal_data; + VL53L1_CustomerNvmManaged_t *pC; + VL53L1_customer_nvm_managed_t *pC2; + VL53L1_xtalk_calibration_results_t xtalk; + uint32_t tmp; + VL53L1_PresetModes PresetMode; + + LOG_FUNCTION_START(""); + + + + Status = VL53L1_get_part_to_part_data(Dev, &cal_data); + + pCalibrationData->struct_version = cal_data.struct_version + + VL53L1_ADDITIONAL_CALIBRATION_DATA_STRUCT_VERSION; + + + + memcpy( + &(pCalibrationData->fmt_dmax_cal), + &(cal_data.fmt_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + + + memcpy( + &(pCalibrationData->cust_dmax_cal), + &(cal_data.cust_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + + + memcpy( + &(pCalibrationData->add_off_cal_data), + &(cal_data.add_off_cal_data), + sizeof(VL53L1_additional_offset_cal_data_t)); + + + + memcpy( + &(pCalibrationData->optical_centre), + &(cal_data.optical_centre), + sizeof(VL53L1_optical_centre_t)); + + + + memcpy( + &(pCalibrationData->xtalkhisto), + &(cal_data.xtalkhisto), + sizeof(VL53L1_xtalk_histogram_data_t)); + + + memcpy( + &(pCalibrationData->gain_cal), + &(cal_data.gain_cal), + sizeof(VL53L1_gain_calibration_data_t)); + + + + memcpy( + &(pCalibrationData->cal_peak_rate_map), + &(cal_data.cal_peak_rate_map), + sizeof(VL53L1_cal_peak_rate_map_t)); + + + pC = &pCalibrationData->customer; + pC2 = &cal_data.customer; + pC->global_config__spad_enables_ref_0 = + pC2->global_config__spad_enables_ref_0; + pC->global_config__spad_enables_ref_1 = + pC2->global_config__spad_enables_ref_1; + pC->global_config__spad_enables_ref_2 = + pC2->global_config__spad_enables_ref_2; + pC->global_config__spad_enables_ref_3 = + pC2->global_config__spad_enables_ref_3; + pC->global_config__spad_enables_ref_4 = + pC2->global_config__spad_enables_ref_4; + pC->global_config__spad_enables_ref_5 = + pC2->global_config__spad_enables_ref_5; + pC->global_config__ref_en_start_select = + pC2->global_config__ref_en_start_select; + pC->ref_spad_man__num_requested_ref_spads = + pC2->ref_spad_man__num_requested_ref_spads; + pC->ref_spad_man__ref_location = + pC2->ref_spad_man__ref_location; + pC->algo__crosstalk_compensation_x_plane_gradient_kcps = + pC2->algo__crosstalk_compensation_x_plane_gradient_kcps; + pC->algo__crosstalk_compensation_y_plane_gradient_kcps = + pC2->algo__crosstalk_compensation_y_plane_gradient_kcps; + pC->ref_spad_char__total_rate_target_mcps = + pC2->ref_spad_char__total_rate_target_mcps; + pC->algo__part_to_part_range_offset_mm = + pC2->algo__part_to_part_range_offset_mm; + pC->mm_config__inner_offset_mm = + pC2->mm_config__inner_offset_mm; + pC->mm_config__outer_offset_mm = + pC2->mm_config__outer_offset_mm; + + pC->algo__crosstalk_compensation_plane_offset_kcps = + (uint32_t)( + pC2->algo__crosstalk_compensation_plane_offset_kcps); + + + PresetMode = VL53L1DevDataGet(Dev, CurrentParameters.PresetMode); + + if ((PresetMode == VL53L1_PRESETMODE_RANGING) || + (PresetMode == VL53L1_PRESETMODE_MULTIZONES_SCANNING) || + (PresetMode == VL53L1_PRESETMODE_PROXY_RANGING_MODE) + ) { + + Status = VL53L1_get_current_xtalk_settings(Dev, &xtalk); + + if (Status != VL53L1_ERROR_NONE) + goto ENDFUNC; + + tmp = xtalk.algo__crosstalk_compensation_plane_offset_kcps; + pC->algo__crosstalk_compensation_plane_offset_kcps = tmp; + tmp = xtalk.algo__crosstalk_compensation_x_plane_gradient_kcps; + pC->algo__crosstalk_compensation_x_plane_gradient_kcps = tmp; + tmp = xtalk.algo__crosstalk_compensation_y_plane_gradient_kcps; + pC->algo__crosstalk_compensation_y_plane_gradient_kcps = tmp; + } +ENDFUNC: + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_SetZoneCalibrationData(VL53L1_DEV Dev, + VL53L1_ZoneCalibrationData_t *pZoneCalibrationData) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_set_zone_calibration_data(Dev, pZoneCalibrationData); + + LOG_FUNCTION_END(Status); + return Status; + +} + +VL53L1_Error VL53L1_GetZoneCalibrationData(VL53L1_DEV Dev, + VL53L1_ZoneCalibrationData_t *pZoneCalibrationData) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_zone_calibration_data(Dev, pZoneCalibrationData); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetOpticalCenter(VL53L1_DEV Dev, + FixPoint1616_t *pOpticalCenterX, + FixPoint1616_t *pOpticalCenterY) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_calibration_data_t CalibrationData; + + LOG_FUNCTION_START(""); + + *pOpticalCenterX = 0; + *pOpticalCenterY = 0; + Status = VL53L1_get_part_to_part_data(Dev, &CalibrationData); + if (Status == VL53L1_ERROR_NONE) { + *pOpticalCenterX = VL53L1_FIXPOINT44TOFIXPOINT1616( + CalibrationData.optical_centre.x_centre); + *pOpticalCenterY = VL53L1_FIXPOINT44TOFIXPOINT1616( + CalibrationData.optical_centre.y_centre); + } + + LOG_FUNCTION_END(Status); + return Status; +} + + + + + + + + +VL53L1_Error VL53L1_SetThresholdConfig(VL53L1_DEV Dev, + VL53L1_DetectionConfig_t *pConfig) +{ +#define BADTHRESBOUNDS(T) \ + (((T.CrossMode == VL53L1_THRESHOLD_OUT_OF_WINDOW) || \ + (T.CrossMode == VL53L1_THRESHOLD_IN_WINDOW)) && (T.Low > T.High)) + + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_GPIO_interrupt_config_t Cfg; + uint16_t g; + FixPoint1616_t gain, high1616, low1616; + VL53L1_LLDriverData_t *pdev; + + LOG_FUNCTION_START(""); + + pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + Status = VL53L1_get_GPIO_interrupt_config(Dev, &Cfg); + if (Status == VL53L1_ERROR_NONE) { + if (pConfig->DetectionMode == VL53L1_DETECTION_NORMAL_RUN) { + Cfg.intr_new_measure_ready = 1; + Status = VL53L1_set_GPIO_interrupt_config_struct(Dev, + Cfg); + } else { + if (BADTHRESBOUNDS(pConfig->Distance)) + Status = VL53L1_ERROR_INVALID_PARAMS; + if ((Status == VL53L1_ERROR_NONE) && + (BADTHRESBOUNDS(pConfig->Rate))) + Status = VL53L1_ERROR_INVALID_PARAMS; + if (Status == VL53L1_ERROR_NONE) { + Cfg.intr_new_measure_ready = 0; + Cfg.intr_no_target = pConfig->IntrNoTarget; + + + + g = pdev->gain_cal.standard_ranging_gain_factor; + + + gain = (FixPoint1616_t) ((uint32_t)g << 5); + high1616 = (FixPoint1616_t) ((uint32_t) + pConfig->Distance.High << 16); + low1616 = (FixPoint1616_t) ((uint32_t) + pConfig->Distance.Low << 16); + + + high1616 = (high1616 + 32768) / gain; + low1616 = (low1616 + 32768) / gain; + Cfg.threshold_distance_high = (uint16_t) + (high1616 & 0xFFFF); + Cfg.threshold_distance_low = (uint16_t) + (low1616 & 0xFFFF); + + + Cfg.threshold_rate_high = + VL53L1_FIXPOINT1616TOFIXPOINT97( + pConfig->Rate.High); + Cfg.threshold_rate_low = + VL53L1_FIXPOINT1616TOFIXPOINT97( + pConfig->Rate.Low); + + Cfg.intr_mode_distance = ConvertModeToLLD( + &Status, + pConfig->Distance.CrossMode); + if (Status == VL53L1_ERROR_NONE) + Cfg.intr_mode_rate = ConvertModeToLLD( + &Status, + pConfig->Rate.CrossMode); + } + + + + if (Status == VL53L1_ERROR_NONE) { + Cfg.intr_combined_mode = 1; + switch (pConfig->DetectionMode) { + case VL53L1_DETECTION_DISTANCE_ONLY: + Cfg.threshold_rate_high = 0; + Cfg.threshold_rate_low = 0; + break; + case VL53L1_DETECTION_RATE_ONLY: + Cfg.threshold_distance_high = 0; + Cfg.threshold_distance_low = 0; + break; + case VL53L1_DETECTION_DISTANCE_OR_RATE: + + + + + break; + case VL53L1_DETECTION_DISTANCE_AND_RATE: + Cfg.intr_combined_mode = 0; + break; + default: + Status = VL53L1_ERROR_INVALID_PARAMS; + } + } + + if (Status == VL53L1_ERROR_NONE) + Status = + VL53L1_set_GPIO_interrupt_config_struct(Dev, + Cfg); + + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_GetThresholdConfig(VL53L1_DEV Dev, + VL53L1_DetectionConfig_t *pConfig) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_GPIO_interrupt_config_t Cfg; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_GPIO_interrupt_config(Dev, &Cfg); + + if (Status != VL53L1_ERROR_NONE) { + LOG_FUNCTION_END(Status); + return Status; + } + + pConfig->IntrNoTarget = Cfg.intr_no_target; + pConfig->Distance.High = Cfg.threshold_distance_high; + pConfig->Distance.Low = Cfg.threshold_distance_low; + pConfig->Rate.High = + VL53L1_FIXPOINT97TOFIXPOINT1616( + Cfg.threshold_rate_high); + pConfig->Rate.Low = + VL53L1_FIXPOINT97TOFIXPOINT1616(Cfg.threshold_rate_low); + pConfig->Distance.CrossMode = + ConvertModeFromLLD(&Status, Cfg.intr_mode_distance); + if (Status == VL53L1_ERROR_NONE) + pConfig->Rate.CrossMode = + ConvertModeFromLLD(&Status, Cfg.intr_mode_rate); + + if (Cfg.intr_new_measure_ready == 1) { + pConfig->DetectionMode = VL53L1_DETECTION_NORMAL_RUN; + } else { + + + if (Status == VL53L1_ERROR_NONE) { + if (Cfg.intr_combined_mode == 0) + pConfig->DetectionMode = + VL53L1_DETECTION_DISTANCE_AND_RATE; + else { + if ((Cfg.threshold_distance_high == 0) && + (Cfg.threshold_distance_low == 0)) + pConfig->DetectionMode = + VL53L1_DETECTION_RATE_ONLY; + else if ((Cfg.threshold_rate_high == 0) && + (Cfg.threshold_rate_low == 0)) + pConfig->DetectionMode = + VL53L1_DETECTION_DISTANCE_ONLY; + else + pConfig->DetectionMode = + VL53L1_DETECTION_DISTANCE_OR_RATE; + } + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + + + + + + diff --git a/drivers/input/misc/vl53L1/src/vl53l1_api_calibration.c b/drivers/input/misc/vl53L1/src/vl53l1_api_calibration.c new file mode 100644 index 000000000000..b3b1a98a20ae --- /dev/null +++ b/drivers/input/misc/vl53L1/src/vl53l1_api_calibration.c @@ -0,0 +1,2765 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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 "vl53l1_ll_def.h" +#include "vl53l1_ll_device.h" +#include "vl53l1_platform.h" +#include "vl53l1_platform_ipp.h" +#include "vl53l1_register_map.h" +#include "vl53l1_register_funcs.h" +#include "vl53l1_register_settings.h" +#include "vl53l1_hist_map.h" +#include "vl53l1_hist_structs.h" +#include "vl53l1_core.h" +#include "vl53l1_wait.h" +#include "vl53l1_api_preset_modes.h" +#include "vl53l1_silicon_core.h" +#include "vl53l1_api_core.h" +#include "vl53l1_api_calibration.h" + +#ifdef VL53L1_LOG_ENABLE + #include "vl53l1_api_debug.h" +#endif + + + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_CORE, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_CORE, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_CORE, status, \ + fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_CORE, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_run_ref_spad_char( + VL53L1_DEV Dev, + VL53L1_Error *pcal_status) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t comms_buffer[6]; + + VL53L1_refspadchar_config_t *prefspadchar = &(pdev->refspadchar); + + LOG_FUNCTION_START(""); + + + + + + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_enable_powerforce(Dev); + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_ref_spad_char_config( + Dev, + prefspadchar->VL53L1_p_009, + prefspadchar->timeout_us, + prefspadchar->target_count_rate_mcps, + prefspadchar->max_count_rate_limit_mcps, + prefspadchar->min_count_rate_limit_mcps, + pdev->stat_nvm.osc_measured__fast_osc__frequency); + + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_run_device_test( + Dev, + prefspadchar->device_test_mode); + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_ReadMulti( + Dev, + VL53L1_REF_SPAD_CHAR_RESULT__NUM_ACTUAL_REF_SPADS, + comms_buffer, + 2); + + if (status == VL53L1_ERROR_NONE) { + pdev->dbg_results.ref_spad_char_result__num_actual_ref_spads = + comms_buffer[0]; + pdev->dbg_results.ref_spad_char_result__ref_location = + comms_buffer[1]; + } + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WriteMulti( + Dev, + VL53L1_REF_SPAD_MAN__NUM_REQUESTED_REF_SPADS, + comms_buffer, + 2); + + if (status == VL53L1_ERROR_NONE) { + pdev->customer.ref_spad_man__num_requested_ref_spads = + comms_buffer[0]; + pdev->customer.ref_spad_man__ref_location = + comms_buffer[1]; + } + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_ReadMulti( + Dev, + VL53L1_RESULT__SPARE_0_SD1, + comms_buffer, + 6); + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WriteMulti( + Dev, + VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_0, + comms_buffer, + 6); + + if (status == VL53L1_ERROR_NONE) { + pdev->customer.global_config__spad_enables_ref_0 = + comms_buffer[0]; + pdev->customer.global_config__spad_enables_ref_1 = + comms_buffer[1]; + pdev->customer.global_config__spad_enables_ref_2 = + comms_buffer[2]; + pdev->customer.global_config__spad_enables_ref_3 = + comms_buffer[3]; + pdev->customer.global_config__spad_enables_ref_4 = + comms_buffer[4]; + pdev->customer.global_config__spad_enables_ref_5 = + comms_buffer[5]; + } + +#ifdef VL53L1_LOG_ENABLE + + + if (status == VL53L1_ERROR_NONE) + VL53L1_print_customer_nvm_managed( + &(pdev->customer), + "run_ref_spad_char():pdev->lldata.customer.", + VL53L1_TRACE_MODULE_REF_SPAD_CHAR); +#endif + + if (status == VL53L1_ERROR_NONE) { + + switch (pdev->sys_results.result__range_status) { + + case VL53L1_DEVICEERROR_REFSPADCHARNOTENOUGHDPADS: + status = VL53L1_WARNING_REF_SPAD_CHAR_NOT_ENOUGH_SPADS; + break; + + case VL53L1_DEVICEERROR_REFSPADCHARMORETHANTARGET: + status = VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_HIGH; + break; + + case VL53L1_DEVICEERROR_REFSPADCHARLESSTHANTARGET: + status = VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_LOW; + break; + } + } + + + + + + + *pcal_status = status; + + + + + IGNORE_STATUS( + IGNORE_REF_SPAD_CHAR_NOT_ENOUGH_SPADS, + VL53L1_WARNING_REF_SPAD_CHAR_NOT_ENOUGH_SPADS, + status); + + IGNORE_STATUS( + IGNORE_REF_SPAD_CHAR_RATE_TOO_HIGH, + VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_HIGH, + status); + + IGNORE_STATUS( + IGNORE_REF_SPAD_CHAR_RATE_TOO_LOW, + VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_LOW, + status); + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_run_xtalk_extraction( + VL53L1_DEV Dev, + VL53L1_Error *pcal_status) +{ + + + + + + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + + + + + + VL53L1_xtalkextract_config_t *pX = &(pdev->xtalk_extract_cfg); + VL53L1_xtalk_config_t *pC = &(pdev->xtalk_cfg); + VL53L1_xtalk_calibration_results_t *pXC = &(pdev->xtalk_cal); + + uint8_t results_invalid = 0; + + uint8_t i = 0; + uint16_t tmp16 = 0; + + uint8_t measurement_mode = VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + + LOG_FUNCTION_START(""); + + + + + + + + VL53L1_init_histogram_bin_data_struct( + 0, + (uint16_t)VL53L1_HISTOGRAM_BUFFER_SIZE, + &(pdev->xtalk_results.central_histogram_avg)); + + VL53L1_init_histogram_bin_data_struct( + 0, + (uint16_t)VL53L1_HISTOGRAM_BUFFER_SIZE, + &(pdev->xtalk_results.central_histogram_sum)); + + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) + + status = + VL53L1_set_preset_mode( + Dev, + VL53L1_DEVICEPRESETMODE_HISTOGRAM_XTALK_PLANAR, + + + pX->dss_config__target_total_rate_mcps, + pX->phasecal_config_timeout_us, + pX->mm_config_timeout_us, + pX->range_config_timeout_us, + + + 100); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_xtalk_compensation(Dev); + + + + + + + pdev->xtalk_results.max_results = VL53L1_MAX_XTALK_RANGE_RESULTS; + pdev->xtalk_results.active_results = pdev->zone_cfg.active_zones+1; + + + + + pdev->xtalk_results.central_histogram__window_start = 0xFF; + pdev->xtalk_results.central_histogram__window_end = 0x00; + + pdev->xtalk_results.num_of_samples_status = 0x00; + pdev->xtalk_results.zero_samples_status = 0x00; + pdev->xtalk_results.max_sigma_status = 0x00; + + for (i = 0; i < pdev->xtalk_results.max_results; i++) { + pdev->xtalk_results.VL53L1_p_002[i].no_of_samples = 0; + pdev->xtalk_results.VL53L1_p_002[i].signal_total_events_avg = 0; + pdev->xtalk_results.VL53L1_p_002[i].signal_total_events_sum = 0; + pdev->xtalk_results.VL53L1_p_002[i].rate_per_spad_kcps_sum = 0; + pdev->xtalk_results.VL53L1_p_002[i].rate_per_spad_kcps_avg = 0; + pdev->xtalk_results.VL53L1_p_002[i].sigma_mm_sum = 0; + pdev->xtalk_results.VL53L1_p_002[i].sigma_mm_avg = 0; + + + pdev->xtalk_results.VL53L1_p_002[i].median_phase_sum = 0; + pdev->xtalk_results.VL53L1_p_002[i].median_phase_avg = 0; + + + } + + + + if (status == VL53L1_ERROR_NONE) { + + status = + VL53L1_get_and_avg_xtalk_samples( + Dev, + + + pX->num_of_samples, + + + measurement_mode, + + + pX->algo__crosstalk_extract_max_valid_range_mm, + pX->algo__crosstalk_extract_min_valid_range_mm, + pX->algo__crosstalk_extract_max_valid_rate_kcps, + + + 0x0, + + 0x4, + + &(pdev->xtalk_results), + &(pdev->xtalk_results.central_histogram_sum), + &(pdev->xtalk_results.central_histogram_avg)); + } + + + + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) + if ((pdev->xtalk_results.VL53L1_p_002[4].no_of_samples == 0) || + (pdev->xtalk_results.VL53L1_p_002[4].sigma_mm_avg > + ((uint32_t)pX->algo__crosstalk_extract_max_sigma_mm + << 5))) + results_invalid = 0x01; + + + + +#ifdef VL53L1_LOG_ENABLE + if (status == VL53L1_ERROR_NONE) + VL53L1_print_xtalk_range_results( + &(pdev->xtalk_results), + "pdev->xtalk_results", + VL53L1_TRACE_MODULE_CORE); +#endif + + if ((status == VL53L1_ERROR_NONE) && (results_invalid == 0)) { + + status = + VL53L1_ipp_xtalk_calibration_process_data( + Dev, + &(pdev->xtalk_results), + &(pdev->xtalk_shapes), + &(pdev->xtalk_cal)); + + if (status == VL53L1_ERROR_NONE) { + + pC->algo__crosstalk_compensation_x_plane_gradient_kcps = + pXC->algo__crosstalk_compensation_x_plane_gradient_kcps; + pC->algo__crosstalk_compensation_y_plane_gradient_kcps = + pXC->algo__crosstalk_compensation_y_plane_gradient_kcps; + pC->algo__crosstalk_compensation_plane_offset_kcps = + pXC->algo__crosstalk_compensation_plane_offset_kcps; + + + } + + + } + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_xtalk_compensation(Dev); + + + + + + if (status == VL53L1_ERROR_NONE) { + + for (i = 0; i < pdev->xtalk_results.max_results; i++) { + + + if (pdev->xtalk_results.VL53L1_p_002[i].no_of_samples != + + + pX->num_of_samples) { + + + pdev->xtalk_results.num_of_samples_status = + pdev->xtalk_results.num_of_samples_status | + (1 << i); + } + + + if (pdev->xtalk_results.VL53L1_p_002[i].no_of_samples == + 0x00) { + pdev->xtalk_results.zero_samples_status = + pdev->xtalk_results.zero_samples_status | + (1 << i); + } + + + + + + + + + + + + + tmp16 = pX->algo__crosstalk_extract_max_sigma_mm; + if (pdev->xtalk_results.VL53L1_p_002[i].sigma_mm_avg > + ((uint32_t)tmp16 << 5)) { + pdev->xtalk_results.max_sigma_status = + pdev->xtalk_results.max_sigma_status | + (1 << i); + } + + + } + } + + + + + + + + + + + + + + if (results_invalid > 0) { + + + if (pdev->xtalk_results.VL53L1_p_002[4].no_of_samples == 0) { + status = VL53L1_ERROR_XTALK_EXTRACTION_NO_SAMPLE_FAIL; + } else { + + + + if (pdev->xtalk_results.VL53L1_p_002[4].sigma_mm_avg > + (((uint32_t)pX->algo__crosstalk_extract_max_sigma_mm) + << 5)) { + status = + VL53L1_ERROR_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL; + } + + + } + } else { + + + if (pdev->xtalk_results.zero_samples_status != 0x00) { + status = VL53L1_WARNING_XTALK_NO_SAMPLES_FOR_GRADIENT; + } else { + if (pdev->xtalk_results.max_sigma_status != 0x00) { + status = + VL53L1_WARNING_XTALK_SIGMA_LIMIT_FOR_GRADIENT; + } else { + if (pdev->xtalk_results.num_of_samples_status != + 0x00) + status = + VL53L1_WARNING_XTALK_MISSING_SAMPLES; + } + } + } + + + + + + + pdev->xtalk_results.cal_status = status; + *pcal_status = pdev->xtalk_results.cal_status; + + + + + IGNORE_STATUS( + IGNORE_XTALK_EXTRACTION_NO_SAMPLE_FAIL, + VL53L1_ERROR_XTALK_EXTRACTION_NO_SAMPLE_FAIL, + status); + + IGNORE_STATUS( + IGNORE_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL, + VL53L1_ERROR_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL, + status); + + IGNORE_STATUS( + IGNORE_XTALK_EXTRACTION_NO_SAMPLE_FOR_GRADIENT_WARN, + VL53L1_WARNING_XTALK_NO_SAMPLES_FOR_GRADIENT, + status); + + IGNORE_STATUS( + IGNORE_XTALK_EXTRACTION_SIGMA_LIMIT_FOR_GRADIENT_WARN, + VL53L1_WARNING_XTALK_SIGMA_LIMIT_FOR_GRADIENT, + status); + + IGNORE_STATUS( + IGNORE_XTALK_EXTRACTION_MISSING_SAMPLES_WARN, + VL53L1_WARNING_XTALK_MISSING_SAMPLES, + status); + +#ifdef VL53L1_LOG_ENABLE + + + + + VL53L1_print_customer_nvm_managed( + &(pdev->customer), + "run_xtalk_extraction():pdev->lldata.customer.", + VL53L1_TRACE_MODULE_XTALK_DATA); + + VL53L1_print_xtalk_config( + &(pdev->xtalk_cfg), + "run_xtalk_extraction():pdev->lldata.xtalk_cfg.", + VL53L1_TRACE_MODULE_XTALK_DATA); + + VL53L1_print_xtalk_extract_config( + &(pdev->xtalk_extract_cfg), + "run_xtalk_extraction():pdev->lldata.xtalk_extract_cfg.", + VL53L1_TRACE_MODULE_XTALK_DATA); + + VL53L1_print_histogram_bin_data( + &(pdev->hist_data), + "run_xtalk_extraction():pdev->lldata.hist_data.", + VL53L1_TRACE_MODULE_XTALK_DATA); + + VL53L1_print_xtalk_histogram_data( + &(pdev->xtalk_shapes), + "pdev->lldata.xtalk_shapes.", + VL53L1_TRACE_MODULE_XTALK_DATA); + + VL53L1_print_xtalk_range_results( + &(pdev->xtalk_results), + "run_xtalk_extraction():pdev->lldata.xtalk_results.", + VL53L1_TRACE_MODULE_XTALK_DATA); + +#endif + + LOG_FUNCTION_END(status); + + return status; + +} + + + +VL53L1_Error VL53L1_get_and_avg_xtalk_samples( + VL53L1_DEV Dev, + uint8_t num_of_samples, + uint8_t measurement_mode, + int16_t xtalk_filter_thresh_max_mm, + int16_t xtalk_filter_thresh_min_mm, + uint16_t xtalk_max_valid_rate_kcps, + uint8_t xtalk_result_id, + uint8_t xtalk_histo_id, + VL53L1_xtalk_range_results_t *pXR, + VL53L1_histogram_bin_data_t *psum_histo, + VL53L1_histogram_bin_data_t *pavg_histo) +{ + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + +#ifdef VL53L1_LOG_ENABLE + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); +#endif + + VL53L1_range_results_t range_results; + VL53L1_range_results_t *prs = &range_results; + + VL53L1_range_data_t *prange_data; + VL53L1_xtalk_range_data_t *pxtalk_range_data; + + uint8_t i = 0; + uint8_t j = 0; + uint8_t zone_id = 0; + uint8_t final_zone = pdev->zone_cfg.active_zones+1; + uint8_t valid_result; + + uint8_t smudge_corr_en = 0; + + + + + + + smudge_corr_en = pdev->smudge_correct_config.smudge_corr_enabled; + + status = VL53L1_dynamic_xtalk_correction_disable(Dev); + + + + + + + if (status == VL53L1_ERROR_NONE) + + status = + VL53L1_init_and_start_range( + Dev, + measurement_mode, + VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS); + + + for (i = 0; i <= (final_zone*num_of_samples); i++) { + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_wait_for_range_completion(Dev); + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_get_device_results( + Dev, + VL53L1_DEVICERESULTSLEVEL_FULL, + prs); + + + + + + + if (status == VL53L1_ERROR_NONE && + pdev->ll_state.rd_device_state != + VL53L1_DEVICESTATE_RANGING_WAIT_GPH_SYNC) { + + zone_id = pdev->ll_state.rd_zone_id + xtalk_result_id; + prange_data = &(prs->VL53L1_p_002[0]); + + + + if (prs->active_results > 1) { + for (j = 1; + j < prs->active_results; j++) { + if (prs->VL53L1_p_002[j].median_range_mm + < + prange_data->median_range_mm) + prange_data = + &(prs->VL53L1_p_002[j]); + + } + } + + pxtalk_range_data = &(pXR->VL53L1_p_002[zone_id]); + + + + + if ((prs->active_results > 0) && + (prange_data->median_range_mm < + xtalk_filter_thresh_max_mm) && + (prange_data->median_range_mm > + xtalk_filter_thresh_min_mm) && + (prange_data->VL53L1_p_012 < + (uint32_t)(xtalk_max_valid_rate_kcps * 16))) + valid_result = 1; + else + valid_result = 0; + + if (valid_result == 1) { + + pxtalk_range_data->no_of_samples++; + + pxtalk_range_data->rate_per_spad_kcps_sum += + prange_data->VL53L1_p_012; + + pxtalk_range_data->signal_total_events_sum += + prange_data->VL53L1_p_013; + + pxtalk_range_data->sigma_mm_sum += + (uint32_t)prange_data->VL53L1_p_005; + + + + + pxtalk_range_data->median_phase_sum += + (uint32_t)prange_data->VL53L1_p_014; + + + + + + + + + + + } + + if ((valid_result == 1) && (zone_id >= 4)) { + status = VL53L1_sum_histogram_data( + &(pdev->hist_data), + psum_histo); + + + + + + if (prange_data->VL53L1_p_015 < + pXR->central_histogram__window_start) + pXR->central_histogram__window_start = + prange_data->VL53L1_p_015; + + + + if (prange_data->VL53L1_p_016 > + pXR->central_histogram__window_end) + pXR->central_histogram__window_end = + prange_data->VL53L1_p_016; + + } + + } + + + + + + + +#ifdef VL53L1_LOG_ENABLE + if (status == VL53L1_ERROR_NONE) { + VL53L1_print_range_results( + &(pres->range_results), + "pres->range_results.", + VL53L1_TRACE_MODULE_CORE); + } +#endif + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_wait_for_firmware_ready(Dev); + + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_clear_interrupt_and_enable_next_range( + Dev, + measurement_mode); + + + } + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_stop_range(Dev); + + + + + + for (i = 0; i < (pdev->zone_cfg.active_zones+1); i++) { + + pxtalk_range_data = &(pXR->VL53L1_p_002[i+xtalk_result_id]); + + if (pxtalk_range_data->no_of_samples > 0) { + pxtalk_range_data->rate_per_spad_kcps_avg = + pxtalk_range_data->rate_per_spad_kcps_sum / + (uint32_t)pxtalk_range_data->no_of_samples; + + pxtalk_range_data->signal_total_events_avg = + pxtalk_range_data->signal_total_events_sum / + (int32_t)pxtalk_range_data->no_of_samples; + + pxtalk_range_data->sigma_mm_avg = + pxtalk_range_data->sigma_mm_sum / + (uint32_t)pxtalk_range_data->no_of_samples; + + + + + pxtalk_range_data->median_phase_avg = + pxtalk_range_data->median_phase_sum / + (uint32_t)pxtalk_range_data->no_of_samples; + + + + + } else { + pxtalk_range_data->rate_per_spad_kcps_avg = + pxtalk_range_data->rate_per_spad_kcps_sum; + pxtalk_range_data->signal_total_events_avg = + pxtalk_range_data->signal_total_events_sum; + pxtalk_range_data->sigma_mm_avg = + pxtalk_range_data->sigma_mm_sum; + + + + + pxtalk_range_data->median_phase_avg = + pxtalk_range_data->median_phase_sum; + + + + } + } + + + + + memcpy(pavg_histo, &(pdev->hist_data), + sizeof(VL53L1_histogram_bin_data_t)); + + + + + if (status == VL53L1_ERROR_NONE) { + + pxtalk_range_data = &(pXR->VL53L1_p_002[xtalk_histo_id]); + + status = VL53L1_avg_histogram_data( + pxtalk_range_data->no_of_samples, + psum_histo, + pavg_histo); + } + + + + + + + if (status == VL53L1_ERROR_NONE) { + if (smudge_corr_en == 1) + status = VL53L1_dynamic_xtalk_correction_enable(Dev); + } + + + + LOG_FUNCTION_END(status); + + return status; + +} + + + +VL53L1_Error VL53L1_run_offset_calibration( + VL53L1_DEV Dev, + int16_t cal_distance_mm, + uint16_t cal_reflectance_pc, + VL53L1_Error *pcal_status) +{ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + VL53L1_DevicePresetModes device_preset_modes[ + VL53L1_MAX_OFFSET_RANGE_RESULTS]; + + VL53L1_range_results_t range_results; + VL53L1_range_results_t *prange_results = &range_results; + VL53L1_range_data_t *pRData = NULL; + VL53L1_offset_range_data_t *pfs = NULL; + VL53L1_general_config_t *pG = &(pdev->gen_cfg); + VL53L1_additional_offset_cal_data_t *pAO = &(pdev->add_off_cal_data); + + uint8_t i = 0; + uint8_t m = 0; + uint8_t measurement_mode = + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + uint16_t manual_effective_spads = + pG->dss_config__manual_effective_spads_select; + + uint8_t num_of_samples[VL53L1_MAX_OFFSET_RANGE_RESULTS]; + + uint8_t smudge_corr_en = 0; + + LOG_FUNCTION_START(""); + + + + + switch (pdev->offset_calibration_mode) { + + case VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__HISTOGRAM: + case VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__HISTOGRAM_PRE_RANGE_ONLY: + device_preset_modes[0] = + VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING; + device_preset_modes[1] = + VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_MM1_CAL; + device_preset_modes[2] = + VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_MM2_CAL; + break; + + default: + device_preset_modes[0] = + VL53L1_DEVICEPRESETMODE_STANDARD_RANGING; + device_preset_modes[1] = + VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_MM1_CAL; + device_preset_modes[2] = + VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_MM2_CAL; + break; + } + + + + + + num_of_samples[0] = pdev->offsetcal_cfg.pre_num_of_samples; + num_of_samples[1] = pdev->offsetcal_cfg.mm1_num_of_samples; + num_of_samples[2] = pdev->offsetcal_cfg.mm2_num_of_samples; + + + + + + + switch (pdev->offset_calibration_mode) { + + case VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD_PRE_RANGE_ONLY: + case VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__HISTOGRAM_PRE_RANGE_ONLY: + + + pdev->offset_results.active_results = 1; + + break; + + default: + + pdev->customer.mm_config__inner_offset_mm = 0; + pdev->customer.mm_config__outer_offset_mm = 0; + pdev->offset_results.active_results = + VL53L1_MAX_OFFSET_RANGE_RESULTS; + + break; + } + + pdev->customer.algo__part_to_part_range_offset_mm = 0; + + + + + pdev->offset_results.max_results = VL53L1_MAX_OFFSET_RANGE_RESULTS; + pdev->offset_results.cal_distance_mm = cal_distance_mm; + pdev->offset_results.cal_reflectance_pc = cal_reflectance_pc; + + for (m = 0; m < VL53L1_MAX_OFFSET_RANGE_RESULTS; m++) { + + pfs = &(pdev->offset_results.VL53L1_p_002[m]); + pfs->preset_mode = 0; + pfs->no_of_samples = 0; + pfs->effective_spads = 0; + pfs->peak_rate_mcps = 0; + pfs->VL53L1_p_005 = 0; + pfs->median_range_mm = 0; + } + + + + + + + smudge_corr_en = pdev->smudge_correct_config.smudge_corr_enabled; + + status = VL53L1_dynamic_xtalk_correction_disable(Dev); + + + + + for (m = 0; m < pdev->offset_results.active_results; m++) { + + pfs = &(pdev->offset_results.VL53L1_p_002[m]); + + pfs->preset_mode = device_preset_modes[m]; + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_preset_mode( + Dev, + device_preset_modes[m], + + + pdev->offsetcal_cfg.dss_config__target_total_rate_mcps, + pdev->offsetcal_cfg.phasecal_config_timeout_us, + pdev->offsetcal_cfg.mm_config_timeout_us, + pdev->offsetcal_cfg.range_config_timeout_us, + + + 100); + + pG->dss_config__manual_effective_spads_select = + manual_effective_spads; + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_init_and_start_range( + Dev, + measurement_mode, + VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS); + + for (i = 0; i <= (num_of_samples[m]+2); i++) { + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_wait_for_range_completion(Dev); + + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_get_device_results( + Dev, + VL53L1_DEVICERESULTSLEVEL_FULL, + prange_results); + + + + + + + + + + pRData = &(prange_results->VL53L1_p_002[0]); + + if ((prange_results->active_results > 0 && + prange_results->stream_count > 1) && + (pRData->range_status == + VL53L1_DEVICEERROR_RANGECOMPLETE)) { + + pfs->no_of_samples++; + pfs->effective_spads += + (uint32_t)pRData->VL53L1_p_006; + pfs->peak_rate_mcps += + (uint32_t)pRData->peak_signal_count_rate_mcps; + pfs->VL53L1_p_005 += + (uint32_t)pRData->VL53L1_p_005; + pfs->median_range_mm += + (int32_t)pRData->median_range_mm; + + pfs->dss_config__roi_mode_control = + pG->dss_config__roi_mode_control; + pfs->dss_config__manual_effective_spads_select = + pG->dss_config__manual_effective_spads_select; + + } + + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_wait_for_firmware_ready(Dev); + + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_clear_interrupt_and_enable_next_range( + Dev, + measurement_mode); + } + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_stop_range(Dev); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WaitUs(Dev, 1000); + + + + if (pfs->no_of_samples > 0) { + + pfs->effective_spads += (pfs->no_of_samples/2); + pfs->effective_spads /= pfs->no_of_samples; + + pfs->peak_rate_mcps += (pfs->no_of_samples/2); + pfs->peak_rate_mcps /= pfs->no_of_samples; + + pfs->VL53L1_p_005 += (pfs->no_of_samples/2); + pfs->VL53L1_p_005 /= pfs->no_of_samples; + + pfs->median_range_mm += (pfs->no_of_samples/2); + pfs->median_range_mm /= pfs->no_of_samples; + + pfs->range_mm_offset = (int32_t)cal_distance_mm; + pfs->range_mm_offset -= pfs->median_range_mm; + + + + if (pfs->preset_mode == + VL53L1_DEVICEPRESETMODE_STANDARD_RANGING) + manual_effective_spads = + (uint16_t)pfs->effective_spads; + } + } + + + + + switch (pdev->offset_calibration_mode) { + + case VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD_PRE_RANGE_ONLY: + case VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__HISTOGRAM_PRE_RANGE_ONLY: + + + + pdev->customer.mm_config__inner_offset_mm += + (int16_t)pdev->offset_results.VL53L1_p_002[0].range_mm_offset; + pdev->customer.mm_config__outer_offset_mm += + (int16_t)pdev->offset_results.VL53L1_p_002[0].range_mm_offset; + break; + + default: + + + pdev->customer.mm_config__inner_offset_mm = + (int16_t)pdev->offset_results.VL53L1_p_002[1].range_mm_offset; + pdev->customer.mm_config__outer_offset_mm = + (int16_t)pdev->offset_results.VL53L1_p_002[2].range_mm_offset; + pdev->customer.algo__part_to_part_range_offset_mm = 0; + + + + + + pAO->result__mm_inner_actual_effective_spads = + (uint16_t)pdev->offset_results.VL53L1_p_002[1].effective_spads; + pAO->result__mm_outer_actual_effective_spads = + (uint16_t)pdev->offset_results.VL53L1_p_002[2].effective_spads; + + pAO->result__mm_inner_peak_signal_count_rtn_mcps = + (uint16_t)pdev->offset_results.VL53L1_p_002[1].peak_rate_mcps; + pAO->result__mm_outer_peak_signal_count_rtn_mcps = + (uint16_t)pdev->offset_results.VL53L1_p_002[2].peak_rate_mcps; + + break; + } + + + + + + + + pdev->cust_dmax_cal.ref__actual_effective_spads = + (uint16_t)pdev->offset_results.VL53L1_p_002[0].effective_spads; + pdev->cust_dmax_cal.ref__peak_signal_count_rate_mcps = + (uint16_t)pdev->offset_results.VL53L1_p_002[0].peak_rate_mcps; + + + + pdev->cust_dmax_cal.ref__distance_mm = cal_distance_mm * 16; + + pdev->cust_dmax_cal.ref_reflectance_pc = cal_reflectance_pc; + pdev->cust_dmax_cal.coverglass_transmission = 0x0100; + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_customer_nvm_managed( + Dev, + &(pdev->customer)); + + + + + + + if (status == VL53L1_ERROR_NONE) { + if (smudge_corr_en == 1) + status = VL53L1_dynamic_xtalk_correction_enable(Dev); + } + + + + + + + + + for (m = 0; m < pdev->offset_results.active_results; m++) { + + pfs = &(pdev->offset_results.VL53L1_p_002[m]); + + if (status == VL53L1_ERROR_NONE) { + + pdev->offset_results.cal_report = m; + + if (pfs->no_of_samples < num_of_samples[m]) + status = + VL53L1_WARNING_OFFSET_CAL_MISSING_SAMPLES; + + + + + + + if (m == 0 && pfs->VL53L1_p_005 > + ((uint32_t)VL53L1_OFFSET_CAL_MAX_SIGMA_MM << 5)) + status = + VL53L1_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH; + + if (pfs->peak_rate_mcps > + VL53L1_OFFSET_CAL_MAX_PRE_PEAK_RATE_MCPS) + status = + VL53L1_WARNING_OFFSET_CAL_RATE_TOO_HIGH; + + if (pfs->dss_config__manual_effective_spads_select < + VL53L1_OFFSET_CAL_MIN_EFFECTIVE_SPADS) + status = + VL53L1_WARNING_OFFSET_CAL_SPAD_COUNT_TOO_LOW; + + if (pfs->dss_config__manual_effective_spads_select == 0) + status = + VL53L1_ERROR_OFFSET_CAL_NO_SPADS_ENABLED_FAIL; + + if (pfs->no_of_samples == 0) + status = VL53L1_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL; + } + } + + + + + + + pdev->offset_results.cal_status = status; + *pcal_status = pdev->offset_results.cal_status; + + + + + IGNORE_STATUS( + IGNORE_OFFSET_CAL_MISSING_SAMPLES, + VL53L1_WARNING_OFFSET_CAL_MISSING_SAMPLES, + status); + + IGNORE_STATUS( + IGNORE_OFFSET_CAL_SIGMA_TOO_HIGH, + VL53L1_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH, + status); + + IGNORE_STATUS( + IGNORE_OFFSET_CAL_RATE_TOO_HIGH, + VL53L1_WARNING_OFFSET_CAL_RATE_TOO_HIGH, + status); + + IGNORE_STATUS( + IGNORE_OFFSET_CAL_SPAD_COUNT_TOO_LOW, + VL53L1_WARNING_OFFSET_CAL_SPAD_COUNT_TOO_LOW, + status); + +#ifdef VL53L1_LOG_ENABLE + + + + + VL53L1_print_customer_nvm_managed( + &(pdev->customer), + "run_offset_calibration():pdev->lldata.customer.", + VL53L1_TRACE_MODULE_OFFSET_DATA); + + VL53L1_print_dmax_calibration_data( + &(pdev->fmt_dmax_cal), + "run_offset_calibration():pdev->lldata.fmt_dmax_cal.", + VL53L1_TRACE_MODULE_OFFSET_DATA); + + VL53L1_print_dmax_calibration_data( + &(pdev->cust_dmax_cal), + "run_offset_calibration():pdev->lldata.cust_dmax_cal.", + VL53L1_TRACE_MODULE_OFFSET_DATA); + + VL53L1_print_additional_offset_cal_data( + &(pdev->add_off_cal_data), + "run_offset_calibration():pdev->lldata.add_off_cal_data.", + VL53L1_TRACE_MODULE_OFFSET_DATA); + + VL53L1_print_offset_range_results( + &(pdev->offset_results), + "run_offset_calibration():pdev->lldata.offset_results.", + VL53L1_TRACE_MODULE_OFFSET_DATA); +#endif + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_run_phasecal_average( + VL53L1_DEV Dev, + uint8_t measurement_mode, + uint8_t phasecal_result__vcsel_start, + uint16_t phasecal_num_of_samples, + VL53L1_range_results_t *prange_results, + uint16_t *pphasecal_result__reference_phase, + uint16_t *pzero_distance_phase) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + uint16_t i = 0; + uint16_t m = 0; + uint32_t samples = 0; + + uint32_t period = 0; + uint32_t VL53L1_p_017 = 0; + uint32_t phasecal_result__reference_phase = 0; + uint32_t zero_distance_phase = 0; + + + + + for (m = 0; m < phasecal_num_of_samples; m++) { + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_init_and_start_range( + Dev, + measurement_mode, + VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS); + + for (i = 0; i <= 1; i++) { + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_wait_for_range_completion(Dev); + + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_get_device_results( + Dev, + VL53L1_DEVICERESULTSLEVEL_FULL, + prange_results); + + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_wait_for_firmware_ready(Dev); + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_clear_interrupt_and_enable_next_range( + Dev, + measurement_mode); + } + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_stop_range(Dev); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WaitUs(Dev, 1000); + + + + + if (status == VL53L1_ERROR_NONE) { + + samples++; + + + + + + + + period = 2048 * + (uint32_t)VL53L1_decode_vcsel_period( + pdev->hist_data.VL53L1_p_009); + + VL53L1_p_017 = period; + VL53L1_p_017 += (uint32_t)( + pdev->hist_data.phasecal_result__reference_phase); + VL53L1_p_017 += + (2048 * + (uint32_t)phasecal_result__vcsel_start); + VL53L1_p_017 -= (2048 * + (uint32_t)pdev->hist_data.cal_config__vcsel_start); + + VL53L1_p_017 = VL53L1_p_017 % period; + + phasecal_result__reference_phase += (uint32_t)( + pdev->hist_data.phasecal_result__reference_phase); + + zero_distance_phase += (uint32_t)VL53L1_p_017; + } + } + + + + + if (status == VL53L1_ERROR_NONE && samples > 0) { + + phasecal_result__reference_phase += (samples >> 1); + phasecal_result__reference_phase /= samples; + + zero_distance_phase += (samples >> 1); + zero_distance_phase /= samples; + + *pphasecal_result__reference_phase = + (uint16_t)phasecal_result__reference_phase; + *pzero_distance_phase = + (uint16_t)zero_distance_phase; + } + + return status; +} + + +VL53L1_Error VL53L1_run_zone_calibration( + VL53L1_DEV Dev, + VL53L1_DevicePresetModes device_preset_mode, + VL53L1_DeviceZonePreset zone_preset, + VL53L1_zone_config_t *pzone_cfg, + int16_t cal_distance_mm, + uint16_t cal_reflectance_pc, + VL53L1_Error *pcal_status) +{ + + + + + + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + VL53L1_range_results_t range_results; + VL53L1_range_results_t *pRR = &range_results; + VL53L1_range_data_t *prange_data = NULL; + VL53L1_zone_calibration_data_t *pzone_data = NULL; + + uint16_t i = 0; + uint16_t m = 0; + + uint8_t z = 0; + uint8_t measurement_mode = + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + + VL53L1_OffsetCorrectionMode offset_cor_mode = + VL53L1_OFFSETCORRECTIONMODE__NONE; + + LOG_FUNCTION_START(""); + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_preset_mode( + Dev, + device_preset_mode, + + + pdev->zonecal_cfg.dss_config__target_total_rate_mcps, + pdev->zonecal_cfg.phasecal_config_timeout_us, + pdev->zonecal_cfg.mm_config_timeout_us, + pdev->zonecal_cfg.range_config_timeout_us, + + + 100); + + + + + if (zone_preset == VL53L1_DEVICEZONEPRESET_CUSTOM) { + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_zone_config( + Dev, + pzone_cfg); + + } else if (zone_preset != VL53L1_DEVICEZONEPRESET_NONE) { + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_zone_preset( + Dev, + zone_preset); + } + + + + + + + pres->zone_cal.preset_mode = device_preset_mode; + pres->zone_cal.zone_preset = zone_preset; + + + pres->zone_cal.cal_distance_mm = cal_distance_mm * 16; + pres->zone_cal.cal_reflectance_pc = cal_reflectance_pc; + pres->zone_cal.max_zones = VL53L1_MAX_USER_ZONES; + pres->zone_cal.active_zones = pdev->zone_cfg.active_zones + 1; + + for (i = 0; i < VL53L1_MAX_USER_ZONES; i++) { + pres->zone_cal.VL53L1_p_002[i].no_of_samples = 0; + pres->zone_cal.VL53L1_p_002[i].effective_spads = 0; + pres->zone_cal.VL53L1_p_002[i].peak_rate_mcps = 0; + pres->zone_cal.VL53L1_p_002[i].VL53L1_p_014 = 0; + pres->zone_cal.VL53L1_p_002[i].VL53L1_p_005 = 0; + pres->zone_cal.VL53L1_p_002[i].median_range_mm = 0; + pres->zone_cal.VL53L1_p_002[i].range_mm_offset = 0; + } + + pres->zone_cal.phasecal_result__reference_phase = 0; + pres->zone_cal.zero_distance_phase = 0; + + + + + + + + status = + VL53L1_get_offset_correction_mode( + Dev, + &offset_cor_mode); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_offset_correction_mode( + Dev, + VL53L1_OFFSETCORRECTIONMODE__NONE); + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_init_and_start_range( + Dev, + measurement_mode, + VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS); + + + + + + + m = (pdev->zonecal_cfg.zone_num_of_samples + 2) * + (uint16_t)pres->zone_cal.active_zones; + + + + for (i = 0; i <= m; i++) { + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_wait_for_range_completion(Dev); + + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_get_device_results( + Dev, + VL53L1_DEVICERESULTSLEVEL_FULL, + pRR); + + + + + + + + + + prange_data = &(pRR->VL53L1_p_002[0]); + + if (pRR->active_results > 0 && + i > (uint16_t)pres->zone_cal.active_zones) { + + if (prange_data->range_status == + VL53L1_DEVICEERROR_RANGECOMPLETE) { + + pres->zone_cal.phasecal_result__reference_phase + = + pdev->hist_data.phasecal_result__reference_phase + ; + pres->zone_cal.zero_distance_phase = + pdev->hist_data.zero_distance_phase; + + pzone_data = + &(pres->zone_cal.VL53L1_p_002[pRR->zone_id]); + pzone_data->no_of_samples++; + pzone_data->effective_spads += + (uint32_t)prange_data->VL53L1_p_006; + pzone_data->peak_rate_mcps += (uint32_t)( + prange_data->peak_signal_count_rate_mcps); + pzone_data->VL53L1_p_014 += + (uint32_t)prange_data->VL53L1_p_014; + pzone_data->VL53L1_p_005 += + (uint32_t)prange_data->VL53L1_p_005; + pzone_data->median_range_mm += + (int32_t)prange_data->median_range_mm; + + } + } + + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_wait_for_firmware_ready(Dev); + + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_clear_interrupt_and_enable_next_range( + Dev, + measurement_mode); + } + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_stop_range(Dev); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WaitUs(Dev, 1000); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_run_phasecal_average( + Dev, + measurement_mode, + pdev->hist_data.phasecal_result__vcsel_start, + + + pdev->zonecal_cfg.phasecal_num_of_samples, + + + pRR, + &(pres->zone_cal.phasecal_result__reference_phase), + &(pres->zone_cal.zero_distance_phase)); + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_offset_correction_mode( + Dev, + offset_cor_mode); + + + + + if (status == VL53L1_ERROR_NONE) { + + for (z = 0; z < pres->zone_cal.active_zones; z++) { + + pzone_data = &(pres->zone_cal.VL53L1_p_002[z]); + + + + if (pzone_data->no_of_samples > 0) { + + pzone_data->effective_spads += + (pzone_data->no_of_samples/2); + pzone_data->effective_spads /= + pzone_data->no_of_samples; + + pzone_data->peak_rate_mcps += + (pzone_data->no_of_samples/2); + pzone_data->peak_rate_mcps /= + pzone_data->no_of_samples; + + pzone_data->VL53L1_p_014 += + (pzone_data->no_of_samples/2); + pzone_data->VL53L1_p_014 /= + pzone_data->no_of_samples; + + pzone_data->VL53L1_p_005 += + (pzone_data->no_of_samples/2); + pzone_data->VL53L1_p_005 /= + pzone_data->no_of_samples; + + + + + + + + pzone_data->median_range_mm = + VL53L1_range_maths( + pdev->stat_nvm.osc_measured__fast_osc__frequency + , (uint16_t)pzone_data->VL53L1_p_014, + pres->zone_cal.zero_distance_phase, + 2, + + 0x0800, + + 0); + + + pzone_data->range_mm_offset = + ((int32_t)cal_distance_mm) * 4; + pzone_data->range_mm_offset -= + pzone_data->median_range_mm; + + + + if (pzone_data->no_of_samples < + pdev->zonecal_cfg.zone_num_of_samples) + status = + VL53L1_WARNING_ZONE_CAL_MISSING_SAMPLES; + + + + if (pzone_data->VL53L1_p_005 > + ((uint32_t)VL53L1_ZONE_CAL_MAX_SIGMA_MM + << 5)) + status = + VL53L1_WARNING_ZONE_CAL_SIGMA_TOO_HIGH; + + if (pzone_data->peak_rate_mcps > + VL53L1_ZONE_CAL_MAX_PRE_PEAK_RATE_MCPS) + status = + VL53L1_WARNING_ZONE_CAL_RATE_TOO_HIGH; + + } else { + status = VL53L1_ERROR_ZONE_CAL_NO_SAMPLE_FAIL; + } + } + } + + + + + + + pres->zone_cal.cal_status = status; + *pcal_status = pres->zone_cal.cal_status; + + + + + IGNORE_STATUS( + IGNORE_ZONE_CAL_MISSING_SAMPLES, + VL53L1_WARNING_ZONE_CAL_MISSING_SAMPLES, + status); + + IGNORE_STATUS( + IGNORE_ZONE_CAL_SIGMA_TOO_HIGH, + VL53L1_WARNING_ZONE_CAL_SIGMA_TOO_HIGH, + status); + + IGNORE_STATUS( + IGNORE_ZONE_CAL_RATE_TOO_HIGH, + VL53L1_WARNING_ZONE_CAL_RATE_TOO_HIGH, + status); + +#ifdef VL53L1_LOG_ENABLE + + + + + VL53L1_print_zone_calibration_results( + &(pres->zone_cal), + "run_zone_calibration():pdev->llresults.zone_cal.", + VL53L1_TRACE_MODULE_OFFSET_DATA); + +#endif + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_run_spad_rate_map( + VL53L1_DEV Dev, + VL53L1_DeviceTestMode device_test_mode, + VL53L1_DeviceSscArray array_select, + uint32_t ssc_config_timeout_us, + VL53L1_spad_rate_data_t *pspad_rate_data) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_powerforce(Dev); + + + + + + + if (status == VL53L1_ERROR_NONE) { + pdev->ssc_cfg.array_select = array_select; + pdev->ssc_cfg.timeout_us = ssc_config_timeout_us; + status = + VL53L1_set_ssc_config( + Dev, + &(pdev->ssc_cfg), + pdev->stat_nvm.osc_measured__fast_osc__frequency); + } + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_run_device_test( + Dev, + device_test_mode); + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_get_spad_rate_data( + Dev, + pspad_rate_data); + + if (device_test_mode == VL53L1_DEVICETESTMODE_LCR_VCSEL_ON) + pspad_rate_data->fractional_bits = 7; + else + pspad_rate_data->fractional_bits = 15; + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_powerforce(Dev); + +#ifdef VL53L1_LOG_ENABLE + + + + if (status == VL53L1_ERROR_NONE) { + VL53L1_print_spad_rate_data( + pspad_rate_data, + "run_spad_rate_map():", + VL53L1_TRACE_MODULE_SPAD_RATE_MAP); + VL53L1_print_spad_rate_map( + pspad_rate_data, + "run_spad_rate_map():", + VL53L1_TRACE_MODULE_SPAD_RATE_MAP); + } +#endif + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_run_device_test( + VL53L1_DEV Dev, + VL53L1_DeviceTestMode device_test_mode) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t comms_buffer[2]; + uint8_t gpio_hv_mux__ctrl = 0; + + LOG_FUNCTION_START(""); + + + + + + + if (status == VL53L1_ERROR_NONE) + + status = + VL53L1_RdByte( + Dev, + VL53L1_GPIO_HV_MUX__CTRL, + &gpio_hv_mux__ctrl); + + if (status == VL53L1_ERROR_NONE) + pdev->stat_cfg.gpio_hv_mux__ctrl = gpio_hv_mux__ctrl; + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_start_test( + Dev, + device_test_mode); + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_wait_for_test_completion(Dev); + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_ReadMulti( + Dev, + VL53L1_RESULT__RANGE_STATUS, + comms_buffer, + 2); + + if (status == VL53L1_ERROR_NONE) { + pdev->sys_results.result__range_status = comms_buffer[0]; + pdev->sys_results.result__report_status = comms_buffer[1]; + } + + + + + pdev->sys_results.result__range_status &= + VL53L1_RANGE_STATUS__RANGE_STATUS_MASK; + + if (status == VL53L1_ERROR_NONE) { + trace_print( + VL53L1_TRACE_LEVEL_INFO, + " Device Test Complete:\n\t%-32s = %3u\n\t%-32s = %3u\n", + "result__range_status", + pdev->sys_results.result__range_status, + "result__report_status", + pdev->sys_results.result__report_status); + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_clear_interrupt(Dev); + } + + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_start_test( + Dev, + 0x00); + + LOG_FUNCTION_END(status); + + return status; +} + + +void VL53L1_hist_xtalk_extract_data_init( + VL53L1_hist_xtalk_extract_data_t *pxtalk_data) +{ + + + + + + int32_t lb = 0; + + pxtalk_data->sample_count = 0U; + pxtalk_data->pll_period_mm = 0U; + pxtalk_data->peak_duration_us_sum = 0U; + pxtalk_data->effective_spad_count_sum = 0U; + pxtalk_data->zero_distance_phase_sum = 0U; + pxtalk_data->zero_distance_phase_avg = 0U; + pxtalk_data->event_scaler_sum = 0U; + pxtalk_data->event_scaler_avg = 4096U; + pxtalk_data->signal_events_sum = 0; + pxtalk_data->xtalk_rate_kcps_per_spad = 0U; + pxtalk_data->VL53L1_p_015 = 0U; + pxtalk_data->VL53L1_p_016 = 0U; + pxtalk_data->target_start = 0U; + + for (lb = 0; lb < VL53L1_XTALK_HISTO_BINS; lb++) + pxtalk_data->bin_data_sums[lb] = 0; + +} + + +VL53L1_Error VL53L1_hist_xtalk_extract_update( + int16_t target_distance_mm, + uint16_t target_width_oversize, + VL53L1_histogram_bin_data_t *phist_bins, + VL53L1_hist_xtalk_extract_data_t *pxtalk_data) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + status = + VL53L1_hist_xtalk_extract_calc_window( + target_distance_mm, + target_width_oversize, + phist_bins, + pxtalk_data); + + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_hist_xtalk_extract_calc_event_sums( + phist_bins, + pxtalk_data); + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_hist_xtalk_extract_fini( + VL53L1_histogram_bin_data_t *phist_bins, + VL53L1_hist_xtalk_extract_data_t *pxtalk_data, + VL53L1_xtalk_calibration_results_t *pxtalk_cal, + VL53L1_xtalk_histogram_shape_t *pxtalk_shape) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_xtalk_calibration_results_t *pX = pxtalk_cal; + + LOG_FUNCTION_START(""); + + if (pxtalk_data->sample_count > 0) { + + + + pxtalk_data->event_scaler_avg = pxtalk_data->event_scaler_sum; + pxtalk_data->event_scaler_avg += + (pxtalk_data->sample_count >> 1); + pxtalk_data->event_scaler_avg /= pxtalk_data->sample_count; + + + + + + + status = + VL53L1_hist_xtalk_extract_calc_rate_per_spad( + pxtalk_data); + + + + + + + if (status == VL53L1_ERROR_NONE) { + + + + pxtalk_data->zero_distance_phase_avg = + pxtalk_data->zero_distance_phase_sum; + pxtalk_data->zero_distance_phase_avg += + (pxtalk_data->sample_count >> 1); + pxtalk_data->zero_distance_phase_avg /= + pxtalk_data->sample_count; + + + + status = + VL53L1_hist_xtalk_extract_calc_shape( + pxtalk_data, + pxtalk_shape); + + + + + + + + + + + + pxtalk_shape->phasecal_result__vcsel_start = + phist_bins->phasecal_result__vcsel_start; + pxtalk_shape->cal_config__vcsel_start = + phist_bins->cal_config__vcsel_start; + pxtalk_shape->vcsel_width = + phist_bins->vcsel_width; + pxtalk_shape->VL53L1_p_019 = + phist_bins->VL53L1_p_019; + } + + + + + + + if (status == VL53L1_ERROR_NONE) { + + + + pX->algo__crosstalk_compensation_plane_offset_kcps = + pxtalk_data->xtalk_rate_kcps_per_spad; + pX->algo__crosstalk_compensation_x_plane_gradient_kcps + = 0U; + pX->algo__crosstalk_compensation_y_plane_gradient_kcps + = 0U; + + } + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_run_hist_xtalk_extraction( + VL53L1_DEV Dev, + int16_t cal_distance_mm, + VL53L1_Error *pcal_status) +{ + + + + + + + + + + + + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_xtalkextract_config_t *pX = &(pdev->xtalk_extract_cfg); + VL53L1_xtalk_config_t *pC = &(pdev->xtalk_cfg); + VL53L1_xtalk_calibration_results_t *pXC = &(pdev->xtalk_cal); + + + + + + + uint8_t smudge_corr_en = 0; + uint8_t i = 0; + uint8_t measurement_mode = VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + + + + + VL53L1_range_results_t range_results; + VL53L1_range_results_t *prange_results = &range_results; + + LOG_FUNCTION_START(""); + + + + + VL53L1_hist_xtalk_extract_data_init( + &(pdev->xtalk_extract)); + + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) + + status = + VL53L1_set_preset_mode( + Dev, + VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE, + pX->dss_config__target_total_rate_mcps, + pX->phasecal_config_timeout_us, + pX->mm_config_timeout_us, + pX->range_config_timeout_us, + 100); + + + + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_disable_xtalk_compensation( + Dev); + } + + + + + smudge_corr_en = pdev->smudge_correct_config.smudge_corr_enabled; + + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_dynamic_xtalk_correction_disable(Dev); + } + + + + + if (status == VL53L1_ERROR_NONE) + + status = + VL53L1_init_and_start_range( + Dev, + measurement_mode, + VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS); + + + + + for (i = 0; i <= pX->num_of_samples; i++) { + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_wait_for_range_completion(Dev); + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_get_device_results( + Dev, + VL53L1_DEVICERESULTSLEVEL_FULL, + prange_results); + + + + + + + if (status == VL53L1_ERROR_NONE && + pdev->ll_state.rd_device_state != + VL53L1_DEVICESTATE_RANGING_WAIT_GPH_SYNC) { + + status = + VL53L1_hist_xtalk_extract_update( + cal_distance_mm, + 4, + + &(pdev->hist_data), + &(pdev->xtalk_extract)); + } + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_wait_for_firmware_ready(Dev); + + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_clear_interrupt_and_enable_next_range( + Dev, + measurement_mode); + } + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_stop_range(Dev); + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_hist_xtalk_extract_fini( + &(pdev->hist_data), + &(pdev->xtalk_extract), + &(pdev->xtalk_cal), + &(pdev->xtalk_shapes.xtalk_shape)); + + + + + + if (status == VL53L1_ERROR_NONE) { + pC->algo__crosstalk_compensation_x_plane_gradient_kcps = + pXC->algo__crosstalk_compensation_x_plane_gradient_kcps; + pC->algo__crosstalk_compensation_y_plane_gradient_kcps = + pXC->algo__crosstalk_compensation_y_plane_gradient_kcps; + pC->algo__crosstalk_compensation_plane_offset_kcps = + pXC->algo__crosstalk_compensation_plane_offset_kcps; + } + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_xtalk_compensation(Dev); + + + + + + if (status == VL53L1_ERROR_NONE) + if (smudge_corr_en == 1) + status = VL53L1_dynamic_xtalk_correction_enable(Dev); + + + + + + + pdev->xtalk_results.cal_status = status; + *pcal_status = pdev->xtalk_results.cal_status; + + +#ifdef VL53L1_LOG_ENABLE + + + + + VL53L1_print_customer_nvm_managed( + &(pdev->customer), + "run_xtalk_extraction():pdev->lldata.customer.", + VL53L1_TRACE_MODULE_XTALK_DATA); + + VL53L1_print_xtalk_config( + &(pdev->xtalk_cfg), + "run_xtalk_extraction():pdev->lldata.xtalk_cfg.", + VL53L1_TRACE_MODULE_XTALK_DATA); + + VL53L1_print_xtalk_histogram_data( + &(pdev->xtalk_shapes), + "pdev->lldata.xtalk_shapes.", + VL53L1_TRACE_MODULE_XTALK_DATA); + +#endif + + LOG_FUNCTION_END(status); + + return status; +} + diff --git a/drivers/input/misc/vl53L1/src/vl53l1_api_core.c b/drivers/input/misc/vl53L1/src/vl53l1_api_core.c new file mode 100644 index 000000000000..9697ecec82c1 --- /dev/null +++ b/drivers/input/misc/vl53L1/src/vl53l1_api_core.c @@ -0,0 +1,7438 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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 "vl53l1_ll_def.h" +#include "vl53l1_ll_device.h" +#include "vl53l1_platform.h" +#include "vl53l1_platform_ipp.h" +#include "vl53l1_register_map.h" +#include "vl53l1_register_settings.h" +#include "vl53l1_register_funcs.h" +#include "vl53l1_hist_map.h" +#include "vl53l1_hist_structs.h" +#include "vl53l1_nvm_map.h" +#include "vl53l1_nvm_structs.h" +#include "vl53l1_nvm.h" +#include "vl53l1_core.h" +#include "vl53l1_wait.h" +#include "vl53l1_zone_presets.h" +#include "vl53l1_api_preset_modes.h" +#include "vl53l1_silicon_core.h" +#include "vl53l1_api_core.h" +#include "vl53l1_tuning_parm_defaults.h" + +#ifdef VL53L1_LOG_ENABLE +#include "vl53l1_api_debug.h" +#endif + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_CORE, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_CORE, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_CORE, status, \ + fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_CORE, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + +#define VL53L1_MAX_I2C_XFER_SIZE 256 + +VL53L1_Error VL53L1_get_version( + VL53L1_DEV Dev, + VL53L1_ll_version_t *pdata) +{ + + + + + + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + VL53L1_init_version(Dev); + + memcpy(pdata, &(pdev->version), sizeof(VL53L1_ll_version_t)); + + return VL53L1_ERROR_NONE; +} + + +VL53L1_Error VL53L1_get_device_firmware_version( + VL53L1_DEV Dev, + uint16_t *pfw_version) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_RdWord( + Dev, + VL53L1_MCU_GENERAL_PURPOSE__GP_0, + pfw_version); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_data_init( + VL53L1_DEV Dev, + uint8_t read_p2p_data) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + + + + VL53L1_zone_objects_t *pobjects; + + uint8_t i = 0; + + LOG_FUNCTION_START(""); + + VL53L1_init_ll_driver_state( + Dev, + VL53L1_DEVICESTATE_UNKNOWN); + + pres->range_results.max_results = VL53L1_MAX_RANGE_RESULTS; + pres->range_results.active_results = 0; + pres->zone_results.max_zones = VL53L1_MAX_USER_ZONES; + pres->zone_results.active_zones = 0; + + for (i = 0; i < VL53L1_MAX_USER_ZONES; i++) { + pobjects = &(pres->zone_results.VL53L1_p_002[i]); + pobjects->xmonitor.VL53L1_p_020 = 0; + pobjects->xmonitor.VL53L1_p_021 = 0; + pobjects->xmonitor.VL53L1_p_014 = 0; + pobjects->xmonitor.range_status = + VL53L1_DEVICEERROR_NOUPDATE; + } + + + + + pres->zone_hists.max_zones = VL53L1_MAX_USER_ZONES; + pres->zone_hists.active_zones = 0; + + + + + pres->zone_cal.max_zones = VL53L1_MAX_USER_ZONES; + pres->zone_cal.active_zones = 0; + for (i = 0; i < VL53L1_MAX_USER_ZONES; i++) { + pres->zone_cal.VL53L1_p_002[i].no_of_samples = 0; + pres->zone_cal.VL53L1_p_002[i].effective_spads = 0; + pres->zone_cal.VL53L1_p_002[i].peak_rate_mcps = 0; + pres->zone_cal.VL53L1_p_002[i].median_range_mm = 0; + pres->zone_cal.VL53L1_p_002[i].range_mm_offset = 0; + } + + pdev->wait_method = VL53L1_WAIT_METHOD_BLOCKING; + pdev->preset_mode = VL53L1_DEVICEPRESETMODE_STANDARD_RANGING; + pdev->zone_preset = VL53L1_DEVICEZONEPRESET_NONE; + pdev->measurement_mode = VL53L1_DEVICEMEASUREMENTMODE_STOP; + + pdev->offset_calibration_mode = + VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD; + pdev->offset_correction_mode = + VL53L1_OFFSETCORRECTIONMODE__MM1_MM2_OFFSETS; + pdev->dmax_mode = + VL53L1_DEVICEDMAXMODE__FMT_CAL_DATA; + + pdev->phasecal_config_timeout_us = 1000; + pdev->mm_config_timeout_us = 2000; + pdev->range_config_timeout_us = 13000; + pdev->inter_measurement_period_ms = 100; + pdev->dss_config__target_total_rate_mcps = 0x0A00; + pdev->debug_mode = 0x00; + + pdev->offset_results.max_results = VL53L1_MAX_OFFSET_RANGE_RESULTS; + pdev->offset_results.active_results = 0; + + + + + + pdev->gain_cal.standard_ranging_gain_factor = + VL53L1_TUNINGPARM_LITE_RANGING_GAIN_FACTOR_DEFAULT; + pdev->gain_cal.histogram_ranging_gain_factor = + VL53L1_TUNINGPARM_HIST_GAIN_FACTOR_DEFAULT; + + + + + + VL53L1_init_version(Dev); + + + + + + + + + + + if (read_p2p_data > 0 && status == VL53L1_ERROR_NONE) + + status = VL53L1_read_p2p_data(Dev); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_refspadchar_config_struct( + &(pdev->refspadchar)); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_ssc_config_struct( + &(pdev->ssc_cfg)); + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_xtalk_config_struct( + &(pdev->customer), + &(pdev->xtalk_cfg)); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_xtalk_extract_config_struct( + &(pdev->xtalk_extract_cfg)); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_offset_cal_config_struct( + &(pdev->offsetcal_cfg)); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_zone_cal_config_struct( + &(pdev->zonecal_cfg)); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_hist_post_process_config_struct( + pdev->xtalk_cfg.global_crosstalk_compensation_enable, + &(pdev->histpostprocess)); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_hist_gen3_dmax_config_struct( + &(pdev->dmax_cfg)); + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_tuning_parm_storage_struct( + &(pdev->tuning_parms)); + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_set_preset_mode( + Dev, + pdev->preset_mode, + pdev->dss_config__target_total_rate_mcps, + + pdev->phasecal_config_timeout_us, + pdev->mm_config_timeout_us, + pdev->range_config_timeout_us, + pdev->inter_measurement_period_ms); + + + + VL53L1_init_histogram_bin_data_struct( + 0, + VL53L1_HISTOGRAM_BUFFER_SIZE, + &(pdev->hist_data)); + + VL53L1_init_histogram_bin_data_struct( + 0, + VL53L1_HISTOGRAM_BUFFER_SIZE, + &(pdev->hist_xtalk)); + + + + VL53L1_init_xtalk_bin_data_struct( + 0, + VL53L1_XTALK_HISTO_BINS, + &(pdev->xtalk_shapes.xtalk_shape)); + + + + + + VL53L1_xtalk_cal_data_init( + Dev + ); + + + + + + VL53L1_dynamic_xtalk_correction_data_init( + Dev + ); + + + + + + VL53L1_low_power_auto_data_init( + Dev + ); + +#ifdef VL53L1_LOG_ENABLE + + + + + VL53L1_print_static_nvm_managed( + &(pdev->stat_nvm), + "data_init():pdev->lldata.stat_nvm.", + VL53L1_TRACE_MODULE_DATA_INIT); + + VL53L1_print_customer_nvm_managed( + &(pdev->customer), + "data_init():pdev->lldata.customer.", + VL53L1_TRACE_MODULE_DATA_INIT); + + VL53L1_print_nvm_copy_data( + &(pdev->nvm_copy_data), + "data_init():pdev->lldata.nvm_copy_data.", + VL53L1_TRACE_MODULE_DATA_INIT); + + VL53L1_print_dmax_calibration_data( + &(pdev->fmt_dmax_cal), + "data_init():pdev->lldata.fmt_dmax_cal.", + VL53L1_TRACE_MODULE_DATA_INIT); + + VL53L1_print_dmax_calibration_data( + &(pdev->cust_dmax_cal), + "data_init():pdev->lldata.cust_dmax_cal.", + VL53L1_TRACE_MODULE_DATA_INIT); + + VL53L1_print_additional_offset_cal_data( + &(pdev->add_off_cal_data), + "data_init():pdev->lldata.add_off_cal_data.", + VL53L1_TRACE_MODULE_DATA_INIT); + + VL53L1_print_user_zone( + &(pdev->mm_roi), + "data_init():pdev->lldata.mm_roi.", + VL53L1_TRACE_MODULE_DATA_INIT); + + VL53L1_print_optical_centre( + &(pdev->optical_centre), + "data_init():pdev->lldata.optical_centre.", + VL53L1_TRACE_MODULE_DATA_INIT); + + VL53L1_print_cal_peak_rate_map( + &(pdev->cal_peak_rate_map), + "data_init():pdev->lldata.cal_peak_rate_map.", + VL53L1_TRACE_MODULE_DATA_INIT); + +#endif + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_read_p2p_data( + VL53L1_DEV Dev) +{ + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_hist_post_process_config_t *pHP = &(pdev->histpostprocess); + VL53L1_customer_nvm_managed_t *pN = &(pdev->customer); + VL53L1_additional_offset_cal_data_t *pCD = &(pdev->add_off_cal_data); + + VL53L1_decoded_nvm_fmt_range_data_t fmt_rrd; + + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_get_static_nvm_managed( + Dev, + &(pdev->stat_nvm)); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_get_customer_nvm_managed( + Dev, + &(pdev->customer)); + + if (status == VL53L1_ERROR_NONE) { + + status = VL53L1_get_nvm_copy_data( + Dev, + &(pdev->nvm_copy_data)); + + + + if (status == VL53L1_ERROR_NONE) + VL53L1_copy_rtn_good_spads_to_buffer( + &(pdev->nvm_copy_data), + &(pdev->rtn_good_spads[0])); + } + + + + + if (status == VL53L1_ERROR_NONE) { + pHP->algo__crosstalk_compensation_plane_offset_kcps = + pN->algo__crosstalk_compensation_plane_offset_kcps; + pHP->algo__crosstalk_compensation_x_plane_gradient_kcps = + pN->algo__crosstalk_compensation_x_plane_gradient_kcps; + pHP->algo__crosstalk_compensation_y_plane_gradient_kcps = + pN->algo__crosstalk_compensation_y_plane_gradient_kcps; + } + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_read_nvm_optical_centre( + Dev, + &(pdev->optical_centre)); + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_read_nvm_cal_peak_rate_map( + Dev, + &(pdev->cal_peak_rate_map)); + + + + + + + if (status == VL53L1_ERROR_NONE) { + + status = + VL53L1_read_nvm_additional_offset_cal_data( + Dev, + &(pdev->add_off_cal_data)); + + + + + + + if (pCD->result__mm_inner_peak_signal_count_rtn_mcps == 0 && + pCD->result__mm_outer_peak_signal_count_rtn_mcps == 0) { + + pCD->result__mm_inner_peak_signal_count_rtn_mcps + = 0x0080; + + pCD->result__mm_outer_peak_signal_count_rtn_mcps + = 0x0180; + + + + + + VL53L1_calc_mm_effective_spads( + pdev->nvm_copy_data.roi_config__mode_roi_centre_spad, + pdev->nvm_copy_data.roi_config__mode_roi_xy_size, + 0xC7, + + 0xFF, + &(pdev->rtn_good_spads[0]), + VL53L1_RTN_SPAD_APERTURE_TRANSMISSION, + &(pCD->result__mm_inner_actual_effective_spads), + &(pCD->result__mm_outer_actual_effective_spads)); + } + } + + + + + + if (status == VL53L1_ERROR_NONE) { + + status = + VL53L1_read_nvm_fmt_range_results_data( + Dev, + VL53L1_NVM__FMT__RANGE_RESULTS__140MM_DARK, + &fmt_rrd); + + if (status == VL53L1_ERROR_NONE) { + pdev->fmt_dmax_cal.ref__actual_effective_spads = + fmt_rrd.result__actual_effective_rtn_spads; + pdev->fmt_dmax_cal.ref__peak_signal_count_rate_mcps = + fmt_rrd.result__peak_signal_count_rate_rtn_mcps; + pdev->fmt_dmax_cal.ref__distance_mm = + fmt_rrd.measured_distance_mm; + + + + + + + if (pdev->cal_peak_rate_map.cal_reflectance_pc != 0) { + pdev->fmt_dmax_cal.ref_reflectance_pc = + pdev->cal_peak_rate_map.cal_reflectance_pc; + } else { + pdev->fmt_dmax_cal.ref_reflectance_pc = 0x0014; + } + + + + pdev->fmt_dmax_cal.coverglass_transmission = 0x0100; + } + } + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_RdWord( + Dev, + VL53L1_RESULT__OSC_CALIBRATE_VAL, + &(pdev->dbg_results.result__osc_calibrate_val)); + + + + + + + if (pdev->stat_nvm.osc_measured__fast_osc__frequency < 0x1000) { + trace_print( + VL53L1_TRACE_LEVEL_WARNING, + "\nInvalid %s value (0x%04X) - forcing to 0x%04X\n\n", + "pdev->stat_nvm.osc_measured__fast_osc__frequency", + pdev->stat_nvm.osc_measured__fast_osc__frequency, + 0xBCCC); + pdev->stat_nvm.osc_measured__fast_osc__frequency = 0xBCCC; + } + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_get_mode_mitigation_roi( + Dev, + &(pdev->mm_roi)); + + + + + + + if (pdev->optical_centre.x_centre == 0 && + pdev->optical_centre.y_centre == 0) { + pdev->optical_centre.x_centre = + pdev->mm_roi.x_centre << 4; + pdev->optical_centre.y_centre = + pdev->mm_roi.y_centre << 4; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_software_reset( + VL53L1_DEV Dev) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_WrByte( + Dev, + VL53L1_SOFT_RESET, + 0x00); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WaitUs( + Dev, + VL53L1_SOFTWARE_RESET_DURATION_US); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_SOFT_RESET, + 0x01); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_wait_for_boot_completion(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_part_to_part_data( + VL53L1_DEV Dev, + VL53L1_calibration_data_t *pcal_data) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_xtalk_config_t *pC = &(pdev->xtalk_cfg); + VL53L1_hist_post_process_config_t *pHP = &(pdev->histpostprocess); + VL53L1_customer_nvm_managed_t *pN = &(pdev->customer); + + uint32_t tempu32; + + LOG_FUNCTION_START(""); + + if (pcal_data->struct_version != + VL53L1_LL_CALIBRATION_DATA_STRUCT_VERSION) { + status = VL53L1_ERROR_INVALID_PARAMS; + } + + if (status == VL53L1_ERROR_NONE) { + + + + memcpy( + &(pdev->customer), + &(pcal_data->customer), + sizeof(VL53L1_customer_nvm_managed_t)); + + + + memcpy( + &(pdev->add_off_cal_data), + &(pcal_data->add_off_cal_data), + sizeof(VL53L1_additional_offset_cal_data_t)); + + + + memcpy( + &(pdev->fmt_dmax_cal), + &(pcal_data->fmt_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + + + memcpy( + &(pdev->cust_dmax_cal), + &(pcal_data->cust_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + + + memcpy( + &(pdev->xtalk_shapes), + &(pcal_data->xtalkhisto), + sizeof(VL53L1_xtalk_histogram_data_t)); + + + + memcpy( + &(pdev->gain_cal), + &(pcal_data->gain_cal), + sizeof(VL53L1_gain_calibration_data_t)); + + + + memcpy( + &(pdev->cal_peak_rate_map), + &(pcal_data->cal_peak_rate_map), + sizeof(VL53L1_cal_peak_rate_map_t)); + + + + + + + pC->algo__crosstalk_compensation_plane_offset_kcps = + pN->algo__crosstalk_compensation_plane_offset_kcps; + pC->algo__crosstalk_compensation_x_plane_gradient_kcps = + pN->algo__crosstalk_compensation_x_plane_gradient_kcps; + pC->algo__crosstalk_compensation_y_plane_gradient_kcps = + pN->algo__crosstalk_compensation_y_plane_gradient_kcps; + + pHP->algo__crosstalk_compensation_plane_offset_kcps = + VL53L1_calc_crosstalk_plane_offset_with_margin( + pC->algo__crosstalk_compensation_plane_offset_kcps, + pC->histogram_mode_crosstalk_margin_kcps); + + pHP->algo__crosstalk_compensation_x_plane_gradient_kcps = + pC->algo__crosstalk_compensation_x_plane_gradient_kcps; + pHP->algo__crosstalk_compensation_y_plane_gradient_kcps = + pC->algo__crosstalk_compensation_y_plane_gradient_kcps; + + + + + if (pC->global_crosstalk_compensation_enable == 0x00) { + pN->algo__crosstalk_compensation_plane_offset_kcps = + 0x00; + pN->algo__crosstalk_compensation_x_plane_gradient_kcps = + 0x00; + pN->algo__crosstalk_compensation_y_plane_gradient_kcps = + 0x00; + } else { + tempu32 = + VL53L1_calc_crosstalk_plane_offset_with_margin( + pC->algo__crosstalk_compensation_plane_offset_kcps, + pC->lite_mode_crosstalk_margin_kcps); + + + + if (tempu32 > 0xFFFF) + tempu32 = 0xFFFF; + + pN->algo__crosstalk_compensation_plane_offset_kcps = + (uint16_t)tempu32; + } + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_part_to_part_data( + VL53L1_DEV Dev, + VL53L1_calibration_data_t *pcal_data) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_xtalk_config_t *pC = &(pdev->xtalk_cfg); + VL53L1_customer_nvm_managed_t *pCN = &(pcal_data->customer); + + LOG_FUNCTION_START(""); + + pcal_data->struct_version = + VL53L1_LL_CALIBRATION_DATA_STRUCT_VERSION; + + + + memcpy( + &(pcal_data->customer), + &(pdev->customer), + sizeof(VL53L1_customer_nvm_managed_t)); + + + + + + + if (pC->algo__crosstalk_compensation_plane_offset_kcps > 0xFFFF) { + pCN->algo__crosstalk_compensation_plane_offset_kcps = + 0xFFFF; + } else { + pCN->algo__crosstalk_compensation_plane_offset_kcps = + (uint16_t)pC->algo__crosstalk_compensation_plane_offset_kcps; + } + pCN->algo__crosstalk_compensation_x_plane_gradient_kcps = + pC->algo__crosstalk_compensation_x_plane_gradient_kcps; + pCN->algo__crosstalk_compensation_y_plane_gradient_kcps = + pC->algo__crosstalk_compensation_y_plane_gradient_kcps; + + + + memcpy( + &(pcal_data->fmt_dmax_cal), + &(pdev->fmt_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + + + memcpy( + &(pcal_data->cust_dmax_cal), + &(pdev->cust_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + + + memcpy( + &(pcal_data->add_off_cal_data), + &(pdev->add_off_cal_data), + sizeof(VL53L1_additional_offset_cal_data_t)); + + + + memcpy( + &(pcal_data->optical_centre), + &(pdev->optical_centre), + sizeof(VL53L1_optical_centre_t)); + + + + memcpy( + &(pcal_data->xtalkhisto), + &(pdev->xtalk_shapes), + sizeof(VL53L1_xtalk_histogram_data_t)); + + + + memcpy( + &(pcal_data->gain_cal), + &(pdev->gain_cal), + sizeof(VL53L1_gain_calibration_data_t)); + + + + memcpy( + &(pcal_data->cal_peak_rate_map), + &(pdev->cal_peak_rate_map), + sizeof(VL53L1_cal_peak_rate_map_t)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_inter_measurement_period_ms( + VL53L1_DEV Dev, + uint32_t inter_measurement_period_ms) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + if (pdev->dbg_results.result__osc_calibrate_val == 0) + status = VL53L1_ERROR_DIVISION_BY_ZERO; + + if (status == VL53L1_ERROR_NONE) { + pdev->inter_measurement_period_ms = inter_measurement_period_ms; + pdev->tim_cfg.system__intermeasurement_period = + inter_measurement_period_ms * + (uint32_t)pdev->dbg_results.result__osc_calibrate_val; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_inter_measurement_period_ms( + VL53L1_DEV Dev, + uint32_t *pinter_measurement_period_ms) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + if (pdev->dbg_results.result__osc_calibrate_val == 0) + status = VL53L1_ERROR_DIVISION_BY_ZERO; + + if (status == VL53L1_ERROR_NONE) + *pinter_measurement_period_ms = + pdev->tim_cfg.system__intermeasurement_period / + (uint32_t)pdev->dbg_results.result__osc_calibrate_val; + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_timeouts_us( + VL53L1_DEV Dev, + uint32_t phasecal_config_timeout_us, + uint32_t mm_config_timeout_us, + uint32_t range_config_timeout_us) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + if (pdev->stat_nvm.osc_measured__fast_osc__frequency == 0) + status = VL53L1_ERROR_DIVISION_BY_ZERO; + + if (status == VL53L1_ERROR_NONE) { + + pdev->phasecal_config_timeout_us = phasecal_config_timeout_us; + pdev->mm_config_timeout_us = mm_config_timeout_us; + pdev->range_config_timeout_us = range_config_timeout_us; + + status = + VL53L1_calc_timeout_register_values( + phasecal_config_timeout_us, + mm_config_timeout_us, + range_config_timeout_us, + pdev->stat_nvm.osc_measured__fast_osc__frequency, + &(pdev->gen_cfg), + &(pdev->tim_cfg)); + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_timeouts_us( + VL53L1_DEV Dev, + uint32_t *pphasecal_config_timeout_us, + uint32_t *pmm_config_timeout_us, + uint32_t *prange_config_timeout_us) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + uint32_t macro_period_us = 0; + uint16_t timeout_encoded = 0; + + LOG_FUNCTION_START(""); + + if (pdev->stat_nvm.osc_measured__fast_osc__frequency == 0) + status = VL53L1_ERROR_DIVISION_BY_ZERO; + + if (status == VL53L1_ERROR_NONE) { + + + + macro_period_us = + VL53L1_calc_macro_period_us( + pdev->stat_nvm.osc_measured__fast_osc__frequency, + pdev->tim_cfg.range_config__vcsel_period_a); + + + + + *pphasecal_config_timeout_us = + VL53L1_calc_timeout_us( + (uint32_t)pdev->gen_cfg.phasecal_config__timeout_macrop, + macro_period_us); + + + + + timeout_encoded = + (uint16_t)pdev->tim_cfg.mm_config__timeout_macrop_a_hi; + timeout_encoded = (timeout_encoded << 8) + + (uint16_t)pdev->tim_cfg.mm_config__timeout_macrop_a_lo; + + *pmm_config_timeout_us = + VL53L1_calc_decoded_timeout_us( + timeout_encoded, + macro_period_us); + + + + + timeout_encoded = + (uint16_t)pdev->tim_cfg.range_config__timeout_macrop_a_hi; + timeout_encoded = (timeout_encoded << 8) + + (uint16_t)pdev->tim_cfg.range_config__timeout_macrop_a_lo; + + *prange_config_timeout_us = + VL53L1_calc_decoded_timeout_us( + timeout_encoded, + macro_period_us); + + pdev->phasecal_config_timeout_us = *pphasecal_config_timeout_us; + pdev->mm_config_timeout_us = *pmm_config_timeout_us; + pdev->range_config_timeout_us = *prange_config_timeout_us; + + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_calibration_repeat_period( + VL53L1_DEV Dev, + uint16_t cal_config__repeat_period) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + pdev->gen_cfg.cal_config__repeat_rate = cal_config__repeat_period; + + return status; + +} + + +VL53L1_Error VL53L1_get_calibration_repeat_period( + VL53L1_DEV Dev, + uint16_t *pcal_config__repeat_period) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + *pcal_config__repeat_period = pdev->gen_cfg.cal_config__repeat_rate; + + return status; + +} + + +VL53L1_Error VL53L1_set_sequence_config_bit( + VL53L1_DEV Dev, + VL53L1_DeviceSequenceConfig bit_id, + uint8_t value) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t bit_mask = 0x01; + uint8_t clr_mask = 0xFF - bit_mask; + uint8_t bit_value = value & bit_mask; + + if (bit_id <= VL53L1_DEVICESEQUENCECONFIG_RANGE) { + + if (bit_id > 0) { + bit_mask = 0x01 << bit_id; + bit_value = bit_value << bit_id; + clr_mask = 0xFF - bit_mask; + } + + pdev->dyn_cfg.system__sequence_config = + (pdev->dyn_cfg.system__sequence_config & clr_mask) | + bit_value; + + } else { + status = VL53L1_ERROR_INVALID_PARAMS; + } + + return status; + +} + + +VL53L1_Error VL53L1_get_sequence_config_bit( + VL53L1_DEV Dev, + VL53L1_DeviceSequenceConfig bit_id, + uint8_t *pvalue) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t bit_mask = 0x01; + + if (bit_id <= VL53L1_DEVICESEQUENCECONFIG_RANGE) { + + if (bit_id > 0) + bit_mask = 0x01 << bit_id; + + *pvalue = + pdev->dyn_cfg.system__sequence_config & bit_mask; + + if (bit_id > 0) + *pvalue = *pvalue >> bit_id; + + } else { + status = VL53L1_ERROR_INVALID_PARAMS; + } + + return status; +} + + +VL53L1_Error VL53L1_set_interrupt_polarity( + VL53L1_DEV Dev, + VL53L1_DeviceInterruptPolarity interrupt_polarity) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + pdev->stat_cfg.gpio_hv_mux__ctrl = + (pdev->stat_cfg.gpio_hv_mux__ctrl & + VL53L1_DEVICEINTERRUPTPOLARITY_CLEAR_MASK) | + (interrupt_polarity & + VL53L1_DEVICEINTERRUPTPOLARITY_BIT_MASK); + + return status; + +} + + +VL53L1_Error VL53L1_set_refspadchar_config_struct( + VL53L1_DEV Dev, + VL53L1_refspadchar_config_t *pdata) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->refspadchar.device_test_mode = pdata->device_test_mode; + pdev->refspadchar.VL53L1_p_009 = pdata->VL53L1_p_009; + pdev->refspadchar.timeout_us = pdata->timeout_us; + pdev->refspadchar.target_count_rate_mcps = + pdata->target_count_rate_mcps; + pdev->refspadchar.min_count_rate_limit_mcps = + pdata->min_count_rate_limit_mcps; + pdev->refspadchar.max_count_rate_limit_mcps = + pdata->max_count_rate_limit_mcps; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_get_refspadchar_config_struct( + VL53L1_DEV Dev, + VL53L1_refspadchar_config_t *pdata) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdata->device_test_mode = pdev->refspadchar.device_test_mode; + pdata->VL53L1_p_009 = pdev->refspadchar.VL53L1_p_009; + pdata->timeout_us = pdev->refspadchar.timeout_us; + pdata->target_count_rate_mcps = + pdev->refspadchar.target_count_rate_mcps; + pdata->min_count_rate_limit_mcps = + pdev->refspadchar.min_count_rate_limit_mcps; + pdata->max_count_rate_limit_mcps = + pdev->refspadchar.max_count_rate_limit_mcps; + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_set_range_ignore_threshold( + VL53L1_DEV Dev, + uint8_t range_ignore_thresh_mult, + uint16_t range_ignore_threshold_mcps) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + pdev->xtalk_cfg.crosstalk_range_ignore_threshold_rate_mcps = + range_ignore_threshold_mcps; + + pdev->xtalk_cfg.crosstalk_range_ignore_threshold_mult = + range_ignore_thresh_mult; + + return status; + +} + +VL53L1_Error VL53L1_get_range_ignore_threshold( + VL53L1_DEV Dev, + uint8_t *prange_ignore_thresh_mult, + uint16_t *prange_ignore_threshold_mcps_internal, + uint16_t *prange_ignore_threshold_mcps_current) +{ + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + *prange_ignore_thresh_mult = + pdev->xtalk_cfg.crosstalk_range_ignore_threshold_mult; + + *prange_ignore_threshold_mcps_current = + pdev->stat_cfg.algo__range_ignore_threshold_mcps; + + *prange_ignore_threshold_mcps_internal = + pdev->xtalk_cfg.crosstalk_range_ignore_threshold_rate_mcps; + + return status; + +} + + + +VL53L1_Error VL53L1_get_interrupt_polarity( + VL53L1_DEV Dev, + VL53L1_DeviceInterruptPolarity *pinterrupt_polarity) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + *pinterrupt_polarity = + pdev->stat_cfg.gpio_hv_mux__ctrl & + VL53L1_DEVICEINTERRUPTPOLARITY_BIT_MASK; + + return status; + +} + + +VL53L1_Error VL53L1_set_user_zone( + VL53L1_DEV Dev, + VL53L1_user_zone_t *puser_zone) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + VL53L1_encode_row_col( + puser_zone->y_centre, + puser_zone->x_centre, + &(pdev->dyn_cfg.roi_config__user_roi_centre_spad)); + + + + VL53L1_encode_zone_size( + puser_zone->width, + puser_zone->height, + &(pdev->dyn_cfg.roi_config__user_roi_requested_global_xy_size)); + + + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_user_zone( + VL53L1_DEV Dev, + VL53L1_user_zone_t *puser_zone) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + VL53L1_decode_row_col( + pdev->dyn_cfg.roi_config__user_roi_centre_spad, + &(puser_zone->y_centre), + &(puser_zone->x_centre)); + + + + VL53L1_decode_zone_size( + pdev->dyn_cfg.roi_config__user_roi_requested_global_xy_size, + &(puser_zone->width), + &(puser_zone->height)); + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_get_mode_mitigation_roi( + VL53L1_DEV Dev, + VL53L1_user_zone_t *pmm_roi) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t x = 0; + uint8_t y = 0; + uint8_t xy_size = 0; + + LOG_FUNCTION_START(""); + + + + VL53L1_decode_row_col( + pdev->nvm_copy_data.roi_config__mode_roi_centre_spad, + &y, + &x); + + pmm_roi->x_centre = x; + pmm_roi->y_centre = y; + + + + + + + + + + + xy_size = pdev->nvm_copy_data.roi_config__mode_roi_xy_size; + + pmm_roi->height = xy_size >> 4; + pmm_roi->width = xy_size & 0x0F; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_zone_config( + VL53L1_DEV Dev, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + memcpy(&(pdev->zone_cfg.user_zones), &(pzone_cfg->user_zones), + sizeof(pdev->zone_cfg.user_zones)); + + + + pdev->zone_cfg.max_zones = pzone_cfg->max_zones; + pdev->zone_cfg.active_zones = pzone_cfg->active_zones; + + status = VL53L1_init_zone_config_histogram_bins(&pdev->zone_cfg); + + + + + + + + + + + if (pzone_cfg->active_zones == 0) + pdev->gen_cfg.global_config__stream_divider = 0; + else if (pzone_cfg->active_zones < VL53L1_MAX_USER_ZONES) + pdev->gen_cfg.global_config__stream_divider = + pzone_cfg->active_zones + 1; + else + pdev->gen_cfg.global_config__stream_divider = + VL53L1_MAX_USER_ZONES + 1; + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_get_zone_config( + VL53L1_DEV Dev, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + memcpy(pzone_cfg, &(pdev->zone_cfg), sizeof(VL53L1_zone_config_t)); + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_get_preset_mode_timing_cfg( + VL53L1_DEV Dev, + VL53L1_DevicePresetModes device_preset_mode, + uint16_t *pdss_config__target_total_rate_mcps, + uint32_t *pphasecal_config_timeout_us, + uint32_t *pmm_config_timeout_us, + uint32_t *prange_config_timeout_us) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + switch (device_preset_mode) { + + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING: + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_SHORT_RANGE: + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_LONG_RANGE: + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_MM1_CAL: + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_MM2_CAL: + case VL53L1_DEVICEPRESETMODE_OLT: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_lite_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_lite_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_lite_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_lite_us; + break; + + case VL53L1_DEVICEPRESETMODE_TIMED_RANGING: + case VL53L1_DEVICEPRESETMODE_TIMED_RANGING_SHORT_RANGE: + case VL53L1_DEVICEPRESETMODE_TIMED_RANGING_LONG_RANGE: + case VL53L1_DEVICEPRESETMODE_SINGLESHOT_RANGING: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_timed_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_timed_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_timed_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_timed_us; + break; + + case VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_SHORT_RANGE: + case VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_MEDIUM_RANGE: + case VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_LONG_RANGE: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_timed_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_timed_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_lpa_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_lpa_us; + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_WITH_MM1: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_WITH_MM2: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_MM1_CAL: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_MM2_CAL: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_REF_ARRAY: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE_MM1: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE_MM2: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_CHARACTERISATION: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_histo_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_hist_long_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_histo_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_histo_us; + + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_histo_mz_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_mz_med_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_mz_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_mz_us; + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE_SHORT_RANGE: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_histo_mz_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_mz_short_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_mz_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_mz_us; + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE_LONG_RANGE: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_histo_mz_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_mz_long_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_mz_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_mz_us; + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_SHORT_TIMING: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_histo_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_hist_short_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_histo_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_histo_us; + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE_MM1: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE_MM2: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_histo_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_hist_med_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_histo_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_histo_us; + break; + + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE_MM1: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE_MM2: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_histo_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_hist_short_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_histo_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_histo_us; + break; + + case VL53L1_DEVICEPRESETMODE_SPECIAL_HISTOGRAM_SHORT_RANGE: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_very_short_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_hist_short_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_histo_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_histo_us; + break; + + default: + status = VL53L1_ERROR_INVALID_PARAMS; + break; + + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_preset_mode( + VL53L1_DEV Dev, + VL53L1_DevicePresetModes device_preset_mode, + uint16_t dss_config__target_total_rate_mcps, + uint32_t phasecal_config_timeout_us, + uint32_t mm_config_timeout_us, + uint32_t range_config_timeout_us, + uint32_t inter_measurement_period_ms) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + VL53L1_hist_post_process_config_t *phistpostprocess = + &(pdev->histpostprocess); + + VL53L1_static_config_t *pstatic = &(pdev->stat_cfg); + VL53L1_histogram_config_t *phistogram = &(pdev->hist_cfg); + VL53L1_general_config_t *pgeneral = &(pdev->gen_cfg); + VL53L1_timing_config_t *ptiming = &(pdev->tim_cfg); + VL53L1_dynamic_config_t *pdynamic = &(pdev->dyn_cfg); + VL53L1_system_control_t *psystem = &(pdev->sys_ctrl); + VL53L1_zone_config_t *pzone_cfg = &(pdev->zone_cfg); + VL53L1_tuning_parm_storage_t *ptuning_parms = &(pdev->tuning_parms); + VL53L1_low_power_auto_data_t *plpadata = + &(pdev->low_power_auto_data); + + LOG_FUNCTION_START(""); + + + + pdev->preset_mode = device_preset_mode; + pdev->mm_config_timeout_us = mm_config_timeout_us; + pdev->range_config_timeout_us = range_config_timeout_us; + pdev->inter_measurement_period_ms = inter_measurement_period_ms; + + + + + VL53L1_init_ll_driver_state( + Dev, + VL53L1_DEVICESTATE_SW_STANDBY); + + + + + switch (device_preset_mode) { + + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING: + status = VL53L1_preset_mode_standard_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_SHORT_RANGE: + status = VL53L1_preset_mode_standard_ranging_short_range( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_LONG_RANGE: + status = VL53L1_preset_mode_standard_ranging_long_range( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_MM1_CAL: + status = VL53L1_preset_mode_standard_ranging_mm1_cal( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_MM2_CAL: + status = VL53L1_preset_mode_standard_ranging_mm2_cal( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_TIMED_RANGING: + status = VL53L1_preset_mode_timed_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_TIMED_RANGING_SHORT_RANGE: + status = VL53L1_preset_mode_timed_ranging_short_range( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_TIMED_RANGING_LONG_RANGE: + status = VL53L1_preset_mode_timed_ranging_long_range( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING: + status = VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_WITH_MM1: + status = VL53L1_preset_mode_histogram_ranging_with_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_WITH_MM2: + status = VL53L1_preset_mode_histogram_ranging_with_mm2( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_MM1_CAL: + status = VL53L1_preset_mode_histogram_ranging_mm1_cal( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_MM2_CAL: + status = VL53L1_preset_mode_histogram_ranging_mm2_cal( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE: + status = VL53L1_preset_mode_histogram_multizone( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE_SHORT_RANGE: + status = VL53L1_preset_mode_histogram_multizone_short_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE_LONG_RANGE: + status = VL53L1_preset_mode_histogram_multizone_long_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_REF_ARRAY: + status = VL53L1_preset_mode_histogram_ranging_ref( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_SHORT_TIMING: + status = VL53L1_preset_mode_histogram_ranging_short_timing( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE: + status = VL53L1_preset_mode_histogram_long_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE_MM1: + status = VL53L1_preset_mode_histogram_long_range_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE_MM2: + status = VL53L1_preset_mode_histogram_long_range_mm2( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE: + status = VL53L1_preset_mode_histogram_medium_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE_MM1: + status = VL53L1_preset_mode_histogram_medium_range_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE_MM2: + status = VL53L1_preset_mode_histogram_medium_range_mm2( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE: + status = VL53L1_preset_mode_histogram_short_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE_MM1: + status = VL53L1_preset_mode_histogram_short_range_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE_MM2: + status = VL53L1_preset_mode_histogram_short_range_mm2( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_CHARACTERISATION: + status = VL53L1_preset_mode_histogram_characterisation( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_XTALK_PLANAR: + status = VL53L1_preset_mode_histogram_xtalk_planar( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_XTALK_MM1: + status = VL53L1_preset_mode_histogram_xtalk_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_XTALK_MM2: + status = VL53L1_preset_mode_histogram_xtalk_mm2( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_OLT: + status = VL53L1_preset_mode_olt( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_SINGLESHOT_RANGING: + status = VL53L1_preset_mode_singleshot_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_SHORT_RANGE: + status = VL53L1_preset_mode_low_power_auto_short_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg, + plpadata); + break; + + case VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_MEDIUM_RANGE: + status = VL53L1_preset_mode_low_power_auto_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg, + plpadata); + break; + + case VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_LONG_RANGE: + status = VL53L1_preset_mode_low_power_auto_long_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg, + plpadata); + break; + + + case VL53L1_DEVICEPRESETMODE_SPECIAL_HISTOGRAM_SHORT_RANGE: + status = VL53L1_preset_mode_special_histogram_short_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + default: + status = VL53L1_ERROR_INVALID_PARAMS; + break; + + } + + + + + if (status == VL53L1_ERROR_NONE) { + + pstatic->dss_config__target_total_rate_mcps = + dss_config__target_total_rate_mcps; + pdev->dss_config__target_total_rate_mcps = + dss_config__target_total_rate_mcps; + + } + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_timeouts_us( + Dev, + phasecal_config_timeout_us, + mm_config_timeout_us, + range_config_timeout_us); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_inter_measurement_period_ms( + Dev, + inter_measurement_period_ms); + + + + + V53L1_init_zone_results_structure( + pdev->zone_cfg.active_zones+1, + &(pres->zone_results)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_zone_preset( + VL53L1_DEV Dev, + VL53L1_DeviceZonePreset zone_preset) +{ + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + VL53L1_general_config_t *pgeneral = &(pdev->gen_cfg); + VL53L1_zone_config_t *pzone_cfg = &(pdev->zone_cfg); + + LOG_FUNCTION_START(""); + + + + pdev->zone_preset = zone_preset; + + + + + switch (zone_preset) { + + case VL53L1_DEVICEZONEPRESET_XTALK_PLANAR: + status = + VL53L1_zone_preset_xtalk_planar( + pgeneral, + pzone_cfg); + break; + + case VL53L1_DEVICEZONEPRESET_1X1_SIZE_16X16: + status = + VL53L1_init_zone_config_structure( + 8, 1, 1, + + 8, 1, 1, + + 15, 15, + + pzone_cfg); + break; + + case VL53L1_DEVICEZONEPRESET_1X2_SIZE_16X8: + status = + VL53L1_init_zone_config_structure( + 8, 1, 1, + + 4, 8, 2, + + 15, 7, + + pzone_cfg); + break; + + case VL53L1_DEVICEZONEPRESET_2X1_SIZE_8X16: + status = + VL53L1_init_zone_config_structure( + 4, 8, 2, + + 8, 1, 1, + + 7, 15, + + pzone_cfg); + break; + + case VL53L1_DEVICEZONEPRESET_2X2_SIZE_8X8: + status = + VL53L1_init_zone_config_structure( + 4, 8, 2, + + 4, 8, 2, + + 7, 7, + + pzone_cfg); + break; + + case VL53L1_DEVICEZONEPRESET_3X3_SIZE_5X5: + status = + VL53L1_init_zone_config_structure( + 2, 5, 3, + + 2, 5, 3, + + 4, 4, + + pzone_cfg); + break; + + case VL53L1_DEVICEZONEPRESET_4X4_SIZE_4X4: + status = + VL53L1_init_zone_config_structure( + 2, 4, 4, + + 2, 4, 4, + + 3, 3, + + pzone_cfg); + break; + + case VL53L1_DEVICEZONEPRESET_5X5_SIZE_4X4: + status = + VL53L1_init_zone_config_structure( + 2, 3, 5, + + 2, 3, 5, + + 3, 3, + + pzone_cfg); + break; + + case VL53L1_DEVICEZONEPRESET_11X11_SIZE_5X5: + status = + VL53L1_init_zone_config_structure( + 3, 1, 11, + + 3, 1, 11, + + 4, 4, + + pzone_cfg); + break; + + case VL53L1_DEVICEZONEPRESET_13X13_SIZE_4X4: + status = + VL53L1_init_zone_config_structure( + 2, 1, 13, + + 2, 1, 13, + + 3, 3, + + pzone_cfg); + + break; + + case VL53L1_DEVICEZONEPRESET_1X1_SIZE_4X4_POS_8X8: + status = + VL53L1_init_zone_config_structure( + 8, 1, 1, + + 8, 1, 1, + + 3, 3, + + pzone_cfg); + break; + + } + + + + + + + + + + + if (pzone_cfg->active_zones == 0) + pdev->gen_cfg.global_config__stream_divider = 0; + else if (pzone_cfg->active_zones < VL53L1_MAX_USER_ZONES) + pdev->gen_cfg.global_config__stream_divider = + pzone_cfg->active_zones + 1; + else + pdev->gen_cfg.global_config__stream_divider = + VL53L1_MAX_USER_ZONES + 1; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_enable_xtalk_compensation( + VL53L1_DEV Dev) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint32_t tempu32; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_xtalk_config_t *pC = &(pdev->xtalk_cfg); + VL53L1_hist_post_process_config_t *pHP = &(pdev->histpostprocess); + VL53L1_customer_nvm_managed_t *pN = &(pdev->customer); + + LOG_FUNCTION_START(""); + + + + tempu32 = VL53L1_calc_crosstalk_plane_offset_with_margin( + pC->algo__crosstalk_compensation_plane_offset_kcps, + pC->lite_mode_crosstalk_margin_kcps); + if (tempu32 > 0xFFFF) + tempu32 = 0xFFFF; + + pN->algo__crosstalk_compensation_plane_offset_kcps = + (uint16_t)tempu32; + + pN->algo__crosstalk_compensation_x_plane_gradient_kcps = + pC->algo__crosstalk_compensation_x_plane_gradient_kcps; + + pN->algo__crosstalk_compensation_y_plane_gradient_kcps = + pC->algo__crosstalk_compensation_y_plane_gradient_kcps; + + + + pHP->algo__crosstalk_compensation_plane_offset_kcps = + VL53L1_calc_crosstalk_plane_offset_with_margin( + pC->algo__crosstalk_compensation_plane_offset_kcps, + pC->histogram_mode_crosstalk_margin_kcps); + + pHP->algo__crosstalk_compensation_x_plane_gradient_kcps + = pC->algo__crosstalk_compensation_x_plane_gradient_kcps; + pHP->algo__crosstalk_compensation_y_plane_gradient_kcps + = pC->algo__crosstalk_compensation_y_plane_gradient_kcps; + + + + + pC->global_crosstalk_compensation_enable = 0x01; + + pHP->algo__crosstalk_compensation_enable = + pC->global_crosstalk_compensation_enable; + + + + + + if (status == VL53L1_ERROR_NONE) { + pC->crosstalk_range_ignore_threshold_rate_mcps = + VL53L1_calc_range_ignore_threshold( + pC->algo__crosstalk_compensation_plane_offset_kcps, + pC->algo__crosstalk_compensation_x_plane_gradient_kcps, + pC->algo__crosstalk_compensation_y_plane_gradient_kcps, + pC->crosstalk_range_ignore_threshold_mult); +} + + + + + if (status == VL53L1_ERROR_NONE) + + status = + VL53L1_set_customer_nvm_managed( + Dev, + &(pdev->customer)); + + LOG_FUNCTION_END(status); + + return status; + +} + +void VL53L1_get_xtalk_compensation_enable( + VL53L1_DEV Dev, + uint8_t *pcrosstalk_compensation_enable) +{ + + + + + + + + + + + + + + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + + *pcrosstalk_compensation_enable = + pdev->xtalk_cfg.global_crosstalk_compensation_enable; + +} + + +VL53L1_Error VL53L1_get_lite_xtalk_margin_kcps( + VL53L1_DEV Dev, + int16_t *pxtalk_margin) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *pxtalk_margin = pdev->xtalk_cfg.lite_mode_crosstalk_margin_kcps; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_lite_xtalk_margin_kcps( + VL53L1_DEV Dev, + int16_t xtalk_margin) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->xtalk_cfg.lite_mode_crosstalk_margin_kcps = xtalk_margin; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_histogram_xtalk_margin_kcps( + VL53L1_DEV Dev, + int16_t *pxtalk_margin) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *pxtalk_margin = pdev->xtalk_cfg.histogram_mode_crosstalk_margin_kcps; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_histogram_xtalk_margin_kcps( + VL53L1_DEV Dev, + int16_t xtalk_margin) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->xtalk_cfg.histogram_mode_crosstalk_margin_kcps = xtalk_margin; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_restore_xtalk_nvm_default( + VL53L1_DEV Dev) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_xtalk_config_t *pC = &(pdev->xtalk_cfg); + + LOG_FUNCTION_START(""); + + pC->algo__crosstalk_compensation_plane_offset_kcps = + pC->nvm_default__crosstalk_compensation_plane_offset_kcps; + pC->algo__crosstalk_compensation_x_plane_gradient_kcps = + pC->nvm_default__crosstalk_compensation_x_plane_gradient_kcps; + pC->algo__crosstalk_compensation_y_plane_gradient_kcps = + pC->nvm_default__crosstalk_compensation_y_plane_gradient_kcps; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_disable_xtalk_compensation( + VL53L1_DEV Dev) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_hist_post_process_config_t *pHP = &(pdev->histpostprocess); + VL53L1_customer_nvm_managed_t *pN = &(pdev->customer); + + LOG_FUNCTION_START(""); + + + + pN->algo__crosstalk_compensation_plane_offset_kcps = + 0x00; + + pN->algo__crosstalk_compensation_x_plane_gradient_kcps = + 0x00; + + pN->algo__crosstalk_compensation_y_plane_gradient_kcps = + 0x00; + + + + + pdev->xtalk_cfg.global_crosstalk_compensation_enable = 0x00; + + pHP->algo__crosstalk_compensation_enable = + pdev->xtalk_cfg.global_crosstalk_compensation_enable; + + + + + if (status == VL53L1_ERROR_NONE) { + pdev->xtalk_cfg.crosstalk_range_ignore_threshold_rate_mcps = + 0x0000; + } + + + + + if (status == VL53L1_ERROR_NONE) { + + status = + VL53L1_set_customer_nvm_managed( + Dev, + &(pdev->customer)); + } + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_get_histogram_phase_consistency( + VL53L1_DEV Dev, + uint8_t *pphase_consistency) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_hist_post_process_config_t *pHP = &(pdev->histpostprocess); + + LOG_FUNCTION_START(""); + + *pphase_consistency = + pHP->algo__consistency_check__phase_tolerance; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_histogram_phase_consistency( + VL53L1_DEV Dev, + uint8_t phase_consistency) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->histpostprocess.algo__consistency_check__phase_tolerance = + phase_consistency; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_get_histogram_event_consistency( + VL53L1_DEV Dev, + uint8_t *pevent_consistency) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *pevent_consistency = + pdev->histpostprocess.algo__consistency_check__event_sigma; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_histogram_event_consistency( + VL53L1_DEV Dev, + uint8_t event_consistency) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->histpostprocess.algo__consistency_check__event_sigma = + event_consistency; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_get_histogram_ambient_threshold_sigma( + VL53L1_DEV Dev, + uint8_t *pamb_thresh_sigma) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *pamb_thresh_sigma = + pdev->histpostprocess.ambient_thresh_sigma1; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_histogram_ambient_threshold_sigma( + VL53L1_DEV Dev, + uint8_t amb_thresh_sigma) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->histpostprocess.ambient_thresh_sigma1 = + amb_thresh_sigma; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_get_lite_sigma_threshold( + VL53L1_DEV Dev, + uint16_t *plite_sigma) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *plite_sigma = + pdev->tim_cfg.range_config__sigma_thresh; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_lite_sigma_threshold( + VL53L1_DEV Dev, + uint16_t lite_sigma) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->tim_cfg.range_config__sigma_thresh = lite_sigma; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_get_lite_min_count_rate( + VL53L1_DEV Dev, + uint16_t *plite_mincountrate) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *plite_mincountrate = + pdev->tim_cfg.range_config__min_count_rate_rtn_limit_mcps; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_lite_min_count_rate( + VL53L1_DEV Dev, + uint16_t lite_mincountrate) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->tim_cfg.range_config__min_count_rate_rtn_limit_mcps = + lite_mincountrate; + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_get_xtalk_detect_config( + VL53L1_DEV Dev, + int16_t *pmax_valid_range_mm, + int16_t *pmin_valid_range_mm, + uint16_t *pmax_valid_rate_kcps, + uint16_t *pmax_sigma_mm) +{ + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *pmax_valid_range_mm = + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_range_mm; + *pmin_valid_range_mm = + pdev->xtalk_cfg.algo__crosstalk_detect_min_valid_range_mm; + *pmax_valid_rate_kcps = + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_rate_kcps; + *pmax_sigma_mm = + pdev->xtalk_cfg.algo__crosstalk_detect_max_sigma_mm; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_xtalk_detect_config( + VL53L1_DEV Dev, + int16_t max_valid_range_mm, + int16_t min_valid_range_mm, + uint16_t max_valid_rate_kcps, + uint16_t max_sigma_mm) +{ + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_range_mm = + max_valid_range_mm; + pdev->xtalk_cfg.algo__crosstalk_detect_min_valid_range_mm = + min_valid_range_mm; + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_rate_kcps = + max_valid_rate_kcps; + pdev->xtalk_cfg.algo__crosstalk_detect_max_sigma_mm = + max_sigma_mm; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_get_target_order_mode( + VL53L1_DEV Dev, + VL53L1_HistTargetOrder *phist_target_order) +{ + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *phist_target_order = + pdev->histpostprocess.hist_target_order; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_target_order_mode( + VL53L1_DEV Dev, + VL53L1_HistTargetOrder hist_target_order) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->histpostprocess.hist_target_order = hist_target_order; + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_get_dmax_reflectance_values( + VL53L1_DEV Dev, + VL53L1_dmax_reflectance_array_t *pdmax_reflectances) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i = 0; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + + + + + + for (i = 0; i < VL53L1_MAX_AMBIENT_DMAX_VALUES; i++) { + pdmax_reflectances->target_reflectance_for_dmax[i] = + pdev->dmax_cfg.target_reflectance_for_dmax_calc[i]; + } + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_dmax_reflectance_values( + VL53L1_DEV Dev, + VL53L1_dmax_reflectance_array_t *pdmax_reflectances) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i = 0; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + + + + + + for (i = 0; i < VL53L1_MAX_AMBIENT_DMAX_VALUES; i++) { + pdev->dmax_cfg.target_reflectance_for_dmax_calc[i] = + pdmax_reflectances->target_reflectance_for_dmax[i]; + } + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_get_vhv_loopbound( + VL53L1_DEV Dev, + uint8_t *pvhv_loopbound) +{ + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *pvhv_loopbound = + pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound / 4; + + LOG_FUNCTION_END(status); + + return status; + +} + + + +VL53L1_Error VL53L1_set_vhv_loopbound( + VL53L1_DEV Dev, + uint8_t vhv_loopbound) +{ + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound = + (pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound & 0x03) + + (vhv_loopbound * 4); + + LOG_FUNCTION_END(status); + + return status; + +} + + + +VL53L1_Error VL53L1_get_vhv_config( + VL53L1_DEV Dev, + uint8_t *pvhv_init_en, + uint8_t *pvhv_init_value) +{ + + + + + + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *pvhv_init_en = (pdev->stat_nvm.vhv_config__init & 0x80) >> 7; + *pvhv_init_value = + (pdev->stat_nvm.vhv_config__init & 0x7F); + + LOG_FUNCTION_END(status); + + return status; + +} + + + +VL53L1_Error VL53L1_set_vhv_config( + VL53L1_DEV Dev, + uint8_t vhv_init_en, + uint8_t vhv_init_value) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->stat_nvm.vhv_config__init = + ((vhv_init_en & 0x01) << 7) + + (vhv_init_value & 0x7F); + + LOG_FUNCTION_END(status); + + return status; + +} + + + +VL53L1_Error VL53L1_init_and_start_range( + VL53L1_DEV Dev, + uint8_t measurement_mode, + VL53L1_DeviceConfigLevel device_config_level) +{ + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + uint8_t buffer[VL53L1_MAX_I2C_XFER_SIZE]; + + VL53L1_static_nvm_managed_t *pstatic_nvm = &(pdev->stat_nvm); + VL53L1_customer_nvm_managed_t *pcustomer_nvm = &(pdev->customer); + VL53L1_static_config_t *pstatic = &(pdev->stat_cfg); + VL53L1_general_config_t *pgeneral = &(pdev->gen_cfg); + VL53L1_timing_config_t *ptiming = &(pdev->tim_cfg); + VL53L1_dynamic_config_t *pdynamic = &(pdev->dyn_cfg); + VL53L1_system_control_t *psystem = &(pdev->sys_ctrl); + + VL53L1_ll_driver_state_t *pstate = &(pdev->ll_state); + VL53L1_customer_nvm_managed_t *pN = &(pdev->customer); + + uint8_t *pbuffer = &buffer[0]; + uint16_t i = 0; + uint16_t i2c_index = 0; + uint16_t i2c_buffer_offset_bytes = 0; + uint16_t i2c_buffer_size_bytes = 0; + + LOG_FUNCTION_START(""); + + + + pdev->measurement_mode = measurement_mode; + + + + + psystem->system__mode_start = + (psystem->system__mode_start & + VL53L1_DEVICEMEASUREMENTMODE_STOP_MASK) | + measurement_mode; + + + + + + + status = + VL53L1_set_user_zone( + Dev, + &(pdev->zone_cfg.user_zones[pdev->ll_state.cfg_zone_id])); + + + + + + if (pdev->zone_cfg.active_zones > 0) { + status = + VL53L1_set_zone_dss_config( + Dev, + &(pres->zone_dyn_cfgs.VL53L1_p_002[pdev->ll_state.cfg_zone_id]) + ); + } + + + + + + + if (((pdev->sys_ctrl.system__mode_start & + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM) == 0x00) && + (pdev->xtalk_cfg.global_crosstalk_compensation_enable + == 0x01)) { + pdev->stat_cfg.algo__range_ignore_threshold_mcps = + pdev->xtalk_cfg.crosstalk_range_ignore_threshold_rate_mcps; + } + + + + + + + + + if (pdev->low_power_auto_data.low_power_auto_range_count == 0xFF) + pdev->low_power_auto_data.low_power_auto_range_count = 0x0; + + + + if ((pdev->low_power_auto_data.is_low_power_auto_mode == 1) && + (pdev->low_power_auto_data.low_power_auto_range_count == 0)) { + + + pdev->low_power_auto_data.saved_interrupt_config = + pdev->gen_cfg.system__interrupt_config_gpio; + + + pdev->gen_cfg.system__interrupt_config_gpio = 1 << 5; + + + if ((pdev->dyn_cfg.system__sequence_config & ( + VL53L1_SEQUENCE_MM1_EN | VL53L1_SEQUENCE_MM2_EN)) == + 0x0) { + pN->algo__part_to_part_range_offset_mm = + (pN->mm_config__outer_offset_mm << 2); + } else { + pN->algo__part_to_part_range_offset_mm = 0x0; + } + + + + if (device_config_level < + VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS) { + device_config_level = + VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS; + } + } + + if ((pdev->low_power_auto_data.is_low_power_auto_mode == 1) && + (pdev->low_power_auto_data.low_power_auto_range_count == 1)) { + + + pdev->gen_cfg.system__interrupt_config_gpio = + pdev->low_power_auto_data.saved_interrupt_config; + + + + device_config_level = VL53L1_DEVICECONFIGLEVEL_FULL; + } + + + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_save_cfg_data(Dev); + + + + + + + switch (device_config_level) { + case VL53L1_DEVICECONFIGLEVEL_FULL: + i2c_index = VL53L1_STATIC_NVM_MANAGED_I2C_INDEX; + break; + case VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS: + i2c_index = VL53L1_CUSTOMER_NVM_MANAGED_I2C_INDEX; + break; + case VL53L1_DEVICECONFIGLEVEL_STATIC_ONWARDS: + i2c_index = VL53L1_STATIC_CONFIG_I2C_INDEX; + break; + case VL53L1_DEVICECONFIGLEVEL_GENERAL_ONWARDS: + i2c_index = VL53L1_GENERAL_CONFIG_I2C_INDEX; + break; + case VL53L1_DEVICECONFIGLEVEL_TIMING_ONWARDS: + i2c_index = VL53L1_TIMING_CONFIG_I2C_INDEX; + break; + case VL53L1_DEVICECONFIGLEVEL_DYNAMIC_ONWARDS: + i2c_index = VL53L1_DYNAMIC_CONFIG_I2C_INDEX; + break; + default: + i2c_index = VL53L1_SYSTEM_CONTROL_I2C_INDEX; + break; + } + + + + + i2c_buffer_size_bytes = + (VL53L1_SYSTEM_CONTROL_I2C_INDEX + + VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES) - + i2c_index; + + + + + pbuffer = &buffer[0]; + for (i = 0; i < i2c_buffer_size_bytes; i++) + *pbuffer++ = 0; + + + + + if (device_config_level >= VL53L1_DEVICECONFIGLEVEL_FULL && + status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = + VL53L1_STATIC_NVM_MANAGED_I2C_INDEX - i2c_index; + + status = + VL53L1_i2c_encode_static_nvm_managed( + pstatic_nvm, + VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes]); + } + + if (device_config_level >= VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS && + status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = + VL53L1_CUSTOMER_NVM_MANAGED_I2C_INDEX - i2c_index; + + status = + VL53L1_i2c_encode_customer_nvm_managed( + pcustomer_nvm, + VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes]); + } + + if (device_config_level >= VL53L1_DEVICECONFIGLEVEL_STATIC_ONWARDS && + status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = + VL53L1_STATIC_CONFIG_I2C_INDEX - i2c_index; + + status = + VL53L1_i2c_encode_static_config( + pstatic, + VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes]); + } + + if (device_config_level >= VL53L1_DEVICECONFIGLEVEL_GENERAL_ONWARDS && + status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = + VL53L1_GENERAL_CONFIG_I2C_INDEX - i2c_index; + + status = + VL53L1_i2c_encode_general_config( + pgeneral, + VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes]); + } + + if (device_config_level >= VL53L1_DEVICECONFIGLEVEL_TIMING_ONWARDS && + status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = + VL53L1_TIMING_CONFIG_I2C_INDEX - i2c_index; + + status = + VL53L1_i2c_encode_timing_config( + ptiming, + VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes]); + } + + if (device_config_level >= VL53L1_DEVICECONFIGLEVEL_DYNAMIC_ONWARDS && + status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = + VL53L1_DYNAMIC_CONFIG_I2C_INDEX - i2c_index; + + + + if ((psystem->system__mode_start & + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK) == + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK) { + pdynamic->system__grouped_parameter_hold_0 = + pstate->cfg_gph_id | 0x01; + pdynamic->system__grouped_parameter_hold_1 = + pstate->cfg_gph_id | 0x01; + pdynamic->system__grouped_parameter_hold = + pstate->cfg_gph_id; + } + status = + VL53L1_i2c_encode_dynamic_config( + pdynamic, + VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes]); + } + + if (status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = + VL53L1_SYSTEM_CONTROL_I2C_INDEX - i2c_index; + + status = + VL53L1_i2c_encode_system_control( + psystem, + VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes]); + } + + + + + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_WriteMulti( + Dev, + i2c_index, + buffer, + (uint32_t)i2c_buffer_size_bytes); + } + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_update_ll_driver_rd_state(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_update_ll_driver_cfg_state(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_stop_range( + VL53L1_DEV Dev) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + + + + pdev->sys_ctrl.system__mode_start = + (pdev->sys_ctrl.system__mode_start & + VL53L1_DEVICEMEASUREMENTMODE_STOP_MASK) | + VL53L1_DEVICEMEASUREMENTMODE_ABORT; + + status = VL53L1_set_system_control( + Dev, + &pdev->sys_ctrl); + + + + pdev->sys_ctrl.system__mode_start = + (pdev->sys_ctrl.system__mode_start & + VL53L1_DEVICEMEASUREMENTMODE_STOP_MASK); + + + + VL53L1_init_ll_driver_state( + Dev, + VL53L1_DEVICESTATE_SW_STANDBY); + + + + V53L1_init_zone_results_structure( + pdev->zone_cfg.active_zones+1, + &(pres->zone_results)); + + + + V53L1_init_zone_dss_configs(Dev); + + + + if (pdev->low_power_auto_data.is_low_power_auto_mode == 1) + VL53L1_low_power_auto_data_stop_range(Dev); + + return status; +} + + +VL53L1_Error VL53L1_get_measurement_results( + VL53L1_DEV Dev, + VL53L1_DeviceResultsLevel device_results_level) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t buffer[VL53L1_MAX_I2C_XFER_SIZE]; + + VL53L1_system_results_t *psystem_results = &(pdev->sys_results); + VL53L1_core_results_t *pcore_results = &(pdev->core_results); + VL53L1_debug_results_t *pdebug_results = &(pdev->dbg_results); + + uint16_t i2c_index = VL53L1_SYSTEM_RESULTS_I2C_INDEX; + uint16_t i2c_buffer_offset_bytes = 0; + uint16_t i2c_buffer_size_bytes = 0; + + LOG_FUNCTION_START(""); + + + + + switch (device_results_level) { + case VL53L1_DEVICERESULTSLEVEL_FULL: + i2c_buffer_size_bytes = + (VL53L1_DEBUG_RESULTS_I2C_INDEX + + VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES) - + i2c_index; + break; + case VL53L1_DEVICERESULTSLEVEL_UPTO_CORE: + i2c_buffer_size_bytes = + (VL53L1_CORE_RESULTS_I2C_INDEX + + VL53L1_CORE_RESULTS_I2C_SIZE_BYTES) - + i2c_index; + break; + default: + i2c_buffer_size_bytes = + VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES; + break; + } + + + + + if (status == VL53L1_ERROR_NONE) + + status = + VL53L1_ReadMulti( + Dev, + i2c_index, + buffer, + (uint32_t)i2c_buffer_size_bytes); + + + + + if (device_results_level >= VL53L1_DEVICERESULTSLEVEL_FULL && + status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = + VL53L1_DEBUG_RESULTS_I2C_INDEX - i2c_index; + + status = + VL53L1_i2c_decode_debug_results( + VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes], + pdebug_results); + } + + if (device_results_level >= VL53L1_DEVICERESULTSLEVEL_UPTO_CORE && + status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = + VL53L1_CORE_RESULTS_I2C_INDEX - i2c_index; + + status = + VL53L1_i2c_decode_core_results( + VL53L1_CORE_RESULTS_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes], + pcore_results); + } + + if (status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = 0; + status = + VL53L1_i2c_decode_system_results( + VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes], + psystem_results); + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_device_results( + VL53L1_DEV Dev, + VL53L1_DeviceResultsLevel device_results_level, + VL53L1_range_results_t *prange_results) +{ + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + VL53L1_range_results_t *presults = + &(pres->range_results); + VL53L1_zone_objects_t *pobjects = + &(pres->zone_results.VL53L1_p_002[0]); + VL53L1_ll_driver_state_t *pstate = + &(pdev->ll_state); + VL53L1_zone_config_t *pzone_cfg = + &(pdev->zone_cfg); + VL53L1_zone_hist_info_t *phist_info = + &(pres->zone_hists.VL53L1_p_002[0]); + + VL53L1_dmax_calibration_data_t dmax_cal; + VL53L1_dmax_calibration_data_t *pdmax_cal = &dmax_cal; + VL53L1_hist_post_process_config_t *pHP = &(pdev->histpostprocess); + VL53L1_xtalk_config_t *pC = &(pdev->xtalk_cfg); + VL53L1_low_power_auto_data_t *pL = &(pdev->low_power_auto_data); + VL53L1_histogram_bin_data_t *pHD = &(pdev->hist_data); + VL53L1_customer_nvm_managed_t *pN = &(pdev->customer); + VL53L1_zone_histograms_t *pZH = &(pres->zone_hists); + + uint8_t i; + uint8_t tmp8; + uint8_t zid; + + LOG_FUNCTION_START(""); + + + + + if ((pdev->sys_ctrl.system__mode_start & + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM) + == VL53L1_DEVICESCHEDULERMODE_HISTOGRAM) { + + + + + + + status = VL53L1_get_histogram_bin_data( + Dev, + &(pdev->hist_data)); + + + + + + + + if (status == VL53L1_ERROR_NONE && + pHD->number_of_ambient_bins == 0) { + zid = pdev->ll_state.rd_zone_id; + status = VL53L1_hist_copy_and_scale_ambient_info( + &(pZH->VL53L1_p_002[zid]), + &(pdev->hist_data)); + } + + + + + + + if (status != VL53L1_ERROR_NONE) + goto UPDATE_DYNAMIC_CONFIG; + + + pHP->gain_factor = + pdev->gain_cal.histogram_ranging_gain_factor; + + pHP->algo__crosstalk_compensation_plane_offset_kcps = + VL53L1_calc_crosstalk_plane_offset_with_margin( + pC->algo__crosstalk_compensation_plane_offset_kcps, + pC->histogram_mode_crosstalk_margin_kcps); + + pHP->algo__crosstalk_compensation_x_plane_gradient_kcps = + pC->algo__crosstalk_compensation_x_plane_gradient_kcps; + pHP->algo__crosstalk_compensation_y_plane_gradient_kcps = + pC->algo__crosstalk_compensation_y_plane_gradient_kcps; + + pdev->dmax_cfg.ambient_thresh_sigma = + pHP->ambient_thresh_sigma1; + pdev->dmax_cfg.min_ambient_thresh_events = + pHP->min_ambient_thresh_events; + pdev->dmax_cfg.signal_total_events_limit = + pHP->signal_total_events_limit; + pdev->dmax_cfg.dss_config__target_total_rate_mcps = + pdev->stat_cfg.dss_config__target_total_rate_mcps; + pdev->dmax_cfg.dss_config__aperture_attenuation = + pdev->gen_cfg.dss_config__aperture_attenuation; + + pHP->algo__crosstalk_detect_max_valid_range_mm = + pC->algo__crosstalk_detect_max_valid_range_mm; + pHP->algo__crosstalk_detect_min_valid_range_mm = + pC->algo__crosstalk_detect_min_valid_range_mm; + pHP->algo__crosstalk_detect_max_valid_rate_kcps = + pC->algo__crosstalk_detect_max_valid_rate_kcps; + pHP->algo__crosstalk_detect_max_sigma_mm = + pC->algo__crosstalk_detect_max_sigma_mm; + + + + + VL53L1_copy_rtn_good_spads_to_buffer( + &(pdev->nvm_copy_data), + &(pdev->rtn_good_spads[0])); + + + + + switch (pdev->offset_correction_mode) { + + case VL53L1_OFFSETCORRECTIONMODE__MM1_MM2_OFFSETS: + tmp8 = pdev->gen_cfg.dss_config__aperture_attenuation; + + VL53L1_hist_combine_mm1_mm2_offsets( + pN->mm_config__inner_offset_mm, + pN->mm_config__outer_offset_mm, + pdev->nvm_copy_data.roi_config__mode_roi_centre_spad, + pdev->nvm_copy_data.roi_config__mode_roi_xy_size, + pHD->roi_config__user_roi_centre_spad, + pHD->roi_config__user_roi_requested_global_xy_size, + &(pdev->add_off_cal_data), + &(pdev->rtn_good_spads[0]), + (uint16_t)tmp8, + &(pHP->range_offset_mm)); + break; + case VL53L1_OFFSETCORRECTIONMODE__PER_ZONE_OFFSETS: + zid = pdev->ll_state.rd_zone_id; + pHP->range_offset_mm = (int16_t)( + pres->zone_cal.VL53L1_p_002[zid].range_mm_offset); + break; + default: + pHP->range_offset_mm = 0; + break; + + } + + + + + + + if (status != VL53L1_ERROR_NONE) + goto UPDATE_DYNAMIC_CONFIG; + + + VL53L1_calc_max_effective_spads( + pHD->roi_config__user_roi_centre_spad, + pHD->roi_config__user_roi_requested_global_xy_size, + &(pdev->rtn_good_spads[0]), + (uint16_t)pdev->gen_cfg.dss_config__aperture_attenuation, + &(pdev->dmax_cfg.max_effective_spads)); + + status = + VL53L1_get_dmax_calibration_data( + Dev, + pdev->dmax_mode, + pdev->ll_state.rd_zone_id, + pdmax_cal); + + + + + + + if (status != VL53L1_ERROR_NONE) + goto UPDATE_DYNAMIC_CONFIG; + + status = VL53L1_ipp_hist_process_data( + Dev, + pdmax_cal, + &(pdev->dmax_cfg), + &(pdev->histpostprocess), + &(pdev->hist_data), + &(pdev->xtalk_shapes), + presults); + + + + if (status != VL53L1_ERROR_NONE) + goto UPDATE_DYNAMIC_CONFIG; + + status = VL53L1_hist_wrap_dmax( + &(pdev->histpostprocess), + &(pdev->hist_data), + &(presults->wrap_dmax_mm)); + + + + if (status != VL53L1_ERROR_NONE) + goto UPDATE_DYNAMIC_CONFIG; + + zid = pdev->ll_state.rd_zone_id; + status = VL53L1_hist_phase_consistency_check( + Dev, + &(pZH->VL53L1_p_002[zid]), + &(pres->zone_results.VL53L1_p_002[zid]), + presults); + + + + if (status != VL53L1_ERROR_NONE) + goto UPDATE_DYNAMIC_CONFIG; + + zid = pdev->ll_state.rd_zone_id; + status = VL53L1_hist_xmonitor_consistency_check( + Dev, + &(pZH->VL53L1_p_002[zid]), + &(pres->zone_results.VL53L1_p_002[zid]), + &(presults->xmonitor)); + + + + + + if (status != VL53L1_ERROR_NONE) + goto UPDATE_DYNAMIC_CONFIG; + + + zid = pdev->ll_state.rd_zone_id; + pZH->max_zones = VL53L1_MAX_USER_ZONES; + pZH->active_zones = + pdev->zone_cfg.active_zones+1; + pHD->zone_id = zid; + + if (zid < + pres->zone_results.max_zones) { + + phist_info = + &(pZH->VL53L1_p_002[zid]); + + phist_info->rd_device_state = + pHD->rd_device_state; + + phist_info->number_of_ambient_bins = + pHD->number_of_ambient_bins; + + phist_info->result__dss_actual_effective_spads = + pHD->result__dss_actual_effective_spads; + + phist_info->VL53L1_p_009 = + pHD->VL53L1_p_009; + + phist_info->total_periods_elapsed = + pHD->total_periods_elapsed; + + phist_info->ambient_events_sum = + pHD->ambient_events_sum; + } + + + + + + + + if (status != VL53L1_ERROR_NONE) + goto UPDATE_DYNAMIC_CONFIG; + + VL53L1_hist_copy_results_to_sys_and_core( + &(pdev->hist_data), + presults, + &(pdev->sys_results), + &(pdev->core_results)); + + + + + + +UPDATE_DYNAMIC_CONFIG: + if (pzone_cfg->active_zones > 0) { + if (pstate->rd_device_state != + VL53L1_DEVICESTATE_RANGING_WAIT_GPH_SYNC) { + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_dynamic_zone_update( + Dev, presults); + } + } + + + + + + for (i = 0; i < VL53L1_MAX_USER_ZONES; i++) { + pzone_cfg->bin_config[i] = + ((pdev->ll_state.cfg_internal_stream_count) + & 0x01) ? + VL53L1_ZONECONFIG_BINCONFIG__HIGHAMB : + VL53L1_ZONECONFIG_BINCONFIG__LOWAMB; + } + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_multizone_hist_bins_update(Dev); + + } + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_dynamic_xtalk_correction_corrector(Dev); + +#ifdef VL53L1_LOG_ENABLE + if (status == VL53L1_ERROR_NONE) + VL53L1_print_histogram_bin_data( + &(pdev->hist_data), + "get_device_results():pdev->lldata.hist_data.", + VL53L1_TRACE_MODULE_HISTOGRAM_DATA); +#endif + + } else { + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_get_measurement_results( + Dev, + device_results_level); + + if (status == VL53L1_ERROR_NONE) + VL53L1_copy_sys_and_core_results_to_range_results( + (int32_t)pdev->gain_cal.standard_ranging_gain_factor, + &(pdev->sys_results), + &(pdev->core_results), + presults); + + + + + + if (pL->is_low_power_auto_mode == 1) { + + + + if ((status == VL53L1_ERROR_NONE) && + (pL->low_power_auto_range_count == 0)) { + + status = + VL53L1_low_power_auto_setup_manual_calibration( + Dev); + pL->low_power_auto_range_count = 1; + } else if ((status == VL53L1_ERROR_NONE) && + (pL->low_power_auto_range_count == 1)) { + pL->low_power_auto_range_count = 2; + } + + + + if ((pL->low_power_auto_range_count != 0xFF) && + (status == VL53L1_ERROR_NONE)) { + status = VL53L1_low_power_auto_update_DSS( + Dev); + } + } + + + } + + + + presults->cfg_device_state = pdev->ll_state.cfg_device_state; + presults->rd_device_state = pdev->ll_state.rd_device_state; + presults->zone_id = pdev->ll_state.rd_zone_id; + + if (status == VL53L1_ERROR_NONE) { + + + + pres->zone_results.max_zones = VL53L1_MAX_USER_ZONES; + pres->zone_results.active_zones = pdev->zone_cfg.active_zones+1; + zid = pdev->ll_state.rd_zone_id; + + if (zid < pres->zone_results.max_zones) { + + pobjects = + &(pres->zone_results.VL53L1_p_002[zid]); + + pobjects->cfg_device_state = + presults->cfg_device_state; + pobjects->rd_device_state = presults->rd_device_state; + pobjects->zone_id = presults->zone_id; + pobjects->stream_count = presults->stream_count; + + + + + pobjects->xmonitor.VL53L1_p_020 = + presults->xmonitor.VL53L1_p_020; + pobjects->xmonitor.VL53L1_p_021 = + presults->xmonitor.VL53L1_p_021; + pobjects->xmonitor.VL53L1_p_014 = + presults->xmonitor.VL53L1_p_014; + pobjects->xmonitor.range_status = + presults->xmonitor.range_status; + + pobjects->max_objects = presults->max_results; + pobjects->active_objects = presults->active_results; + + for (i = 0; i < presults->active_results; i++) { + pobjects->VL53L1_p_002[i].VL53L1_p_020 = + presults->VL53L1_p_002[i].VL53L1_p_020; + pobjects->VL53L1_p_002[i].VL53L1_p_021 = + presults->VL53L1_p_002[i].VL53L1_p_021; + pobjects->VL53L1_p_002[i].VL53L1_p_014 = + presults->VL53L1_p_002[i].VL53L1_p_014; + pobjects->VL53L1_p_002[i].range_status = + presults->VL53L1_p_002[i].range_status; + } + + + + } + } + + + + + memcpy( + prange_results, + presults, + sizeof(VL53L1_range_results_t)); + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_check_ll_driver_rd_state(Dev); + +#ifdef VL53L1_LOG_ENABLE + if (status == VL53L1_ERROR_NONE) + VL53L1_print_range_results( + presults, + "get_device_results():pdev->llresults.range_results.", + VL53L1_TRACE_MODULE_RANGE_RESULTS_DATA); +#endif + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_clear_interrupt_and_enable_next_range( + VL53L1_DEV Dev, + uint8_t measurement_mode) +{ + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_init_and_start_range( + Dev, + measurement_mode, + VL53L1_DEVICECONFIGLEVEL_GENERAL_ONWARDS); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_histogram_bin_data( + VL53L1_DEV Dev, + VL53L1_histogram_bin_data_t *pdata) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + VL53L1_zone_private_dyn_cfg_t *pzone_dyn_cfg; + + VL53L1_static_nvm_managed_t *pstat_nvm = &(pdev->stat_nvm); + VL53L1_static_config_t *pstat_cfg = &(pdev->stat_cfg); + VL53L1_general_config_t *pgen_cfg = &(pdev->gen_cfg); + VL53L1_timing_config_t *ptim_cfg = &(pdev->tim_cfg); + VL53L1_range_results_t *presults = &(pres->range_results); + + uint8_t buffer[VL53L1_MAX_I2C_XFER_SIZE]; + uint8_t *pbuffer = &buffer[0]; + uint8_t bin_23_0 = 0x00; + uint16_t bin = 0; + uint16_t i2c_buffer_offset_bytes = 0; + uint16_t encoded_timeout = 0; + + uint32_t pll_period_us = 0; + uint32_t periods_elapsed_tmp = 0; + + uint8_t i = 0; + + LOG_FUNCTION_START(""); + + + + + + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_ReadMulti( + Dev, + VL53L1_HISTOGRAM_BIN_DATA_I2C_INDEX, + pbuffer, + VL53L1_HISTOGRAM_BIN_DATA_I2C_SIZE_BYTES); + + + + + + + pdata->result__interrupt_status = *(pbuffer + 0); + pdata->result__range_status = *(pbuffer + 1); + pdata->result__report_status = *(pbuffer + 2); + pdata->result__stream_count = *(pbuffer + 3); + pdata->result__dss_actual_effective_spads = + VL53L1_i2c_decode_uint16_t(2, pbuffer + 4); + + + + + + + i2c_buffer_offset_bytes = + VL53L1_PHASECAL_RESULT__REFERENCE_PHASE - + VL53L1_HISTOGRAM_BIN_DATA_I2C_INDEX; + + pbuffer = &buffer[i2c_buffer_offset_bytes]; + + pdata->phasecal_result__reference_phase = + VL53L1_i2c_decode_uint16_t(2, pbuffer); + + i2c_buffer_offset_bytes = + VL53L1_PHASECAL_RESULT__VCSEL_START - + VL53L1_HISTOGRAM_BIN_DATA_I2C_INDEX; + + pdata->phasecal_result__vcsel_start = buffer[i2c_buffer_offset_bytes]; + + + + + pdev->dbg_results.phasecal_result__reference_phase = + pdata->phasecal_result__reference_phase; + pdev->dbg_results.phasecal_result__vcsel_start = + pdata->phasecal_result__vcsel_start; + + + + + + + + i2c_buffer_offset_bytes = + VL53L1_RESULT__HISTOGRAM_BIN_23_0_MSB - + VL53L1_HISTOGRAM_BIN_DATA_I2C_INDEX; + + bin_23_0 = buffer[i2c_buffer_offset_bytes] << 2; + + i2c_buffer_offset_bytes = + VL53L1_RESULT__HISTOGRAM_BIN_23_0_LSB - + VL53L1_HISTOGRAM_BIN_DATA_I2C_INDEX; + + bin_23_0 += buffer[i2c_buffer_offset_bytes]; + + i2c_buffer_offset_bytes = + VL53L1_RESULT__HISTOGRAM_BIN_23_0 - + VL53L1_HISTOGRAM_BIN_DATA_I2C_INDEX; + + buffer[i2c_buffer_offset_bytes] = bin_23_0; + + + + + + + + i2c_buffer_offset_bytes = + VL53L1_RESULT__HISTOGRAM_BIN_0_2 - + VL53L1_HISTOGRAM_BIN_DATA_I2C_INDEX; + + pbuffer = &buffer[i2c_buffer_offset_bytes]; + for (bin = 0; bin < VL53L1_HISTOGRAM_BUFFER_SIZE; bin++) { + pdata->bin_data[bin] = + (int32_t)VL53L1_i2c_decode_uint32_t(3, pbuffer); + pbuffer += 3; + } + + + + pdata->zone_id = pdev->ll_state.rd_zone_id; + pdata->VL53L1_p_022 = 0; + pdata->VL53L1_p_023 = VL53L1_HISTOGRAM_BUFFER_SIZE; + pdata->VL53L1_p_024 = VL53L1_HISTOGRAM_BUFFER_SIZE; + + pdata->cal_config__vcsel_start = pgen_cfg->cal_config__vcsel_start; + + + + + pdata->vcsel_width = + ((uint16_t)pgen_cfg->global_config__vcsel_width) << 4; + pdata->vcsel_width += + (uint16_t)pstat_cfg->ana_config__vcsel_pulse_width_offset; + + + + pdata->VL53L1_p_019 = + pstat_nvm->osc_measured__fast_osc__frequency; + + + + + VL53L1_hist_get_bin_sequence_config(Dev, pdata); + + + + + + + if (pdev->ll_state.rd_timing_status == 0) { + + encoded_timeout = + (ptim_cfg->range_config__timeout_macrop_a_hi << 8) + + ptim_cfg->range_config__timeout_macrop_a_lo; + pdata->VL53L1_p_009 = ptim_cfg->range_config__vcsel_period_a; + } else { + + encoded_timeout = + (ptim_cfg->range_config__timeout_macrop_b_hi << 8) + + ptim_cfg->range_config__timeout_macrop_b_lo; + pdata->VL53L1_p_009 = ptim_cfg->range_config__vcsel_period_b; + } + + + + + pdata->number_of_ambient_bins = 0; + + for (i = 0; i < 6; i++) { + if ((pdata->bin_seq[i] & 0x07) == 0x07) + pdata->number_of_ambient_bins = + pdata->number_of_ambient_bins + 0x04; + } + + pdata->total_periods_elapsed = + VL53L1_decode_timeout(encoded_timeout); + + + + + + + + pll_period_us = + VL53L1_calc_pll_period_us(pdata->VL53L1_p_019); + + + + + periods_elapsed_tmp = pdata->total_periods_elapsed + 1; + + + + + + + pdata->peak_duration_us = + VL53L1_duration_maths( + pll_period_us, + (uint32_t)pdata->vcsel_width, + VL53L1_RANGING_WINDOW_VCSEL_PERIODS, + periods_elapsed_tmp); + + pdata->woi_duration_us = 0; + + + + + VL53L1_hist_calc_zero_distance_phase(pdata); + + + + + + + VL53L1_hist_estimate_ambient_from_ambient_bins(pdata); + + + + + pdata->cfg_device_state = pdev->ll_state.cfg_device_state; + pdata->rd_device_state = pdev->ll_state.rd_device_state; + + + + + pzone_dyn_cfg = &(pres->zone_dyn_cfgs.VL53L1_p_002[pdata->zone_id]); + + pdata->roi_config__user_roi_centre_spad = + pzone_dyn_cfg->roi_config__user_roi_centre_spad; + pdata->roi_config__user_roi_requested_global_xy_size = + pzone_dyn_cfg->roi_config__user_roi_requested_global_xy_size; + + + + + + + presults->device_status = VL53L1_DEVICEERROR_NOUPDATE; + + + + + switch (pdata->result__range_status & + VL53L1_RANGE_STATUS__RANGE_STATUS_MASK) { + + case VL53L1_DEVICEERROR_VCSELCONTINUITYTESTFAILURE: + case VL53L1_DEVICEERROR_VCSELWATCHDOGTESTFAILURE: + case VL53L1_DEVICEERROR_NOVHVVALUEFOUND: + case VL53L1_DEVICEERROR_USERROICLIP: + case VL53L1_DEVICEERROR_MULTCLIPFAIL: + + presults->device_status = (pdata->result__range_status & + VL53L1_RANGE_STATUS__RANGE_STATUS_MASK); + + status = VL53L1_ERROR_RANGE_ERROR; + + break; + + } + + LOG_FUNCTION_END(status); + + return status; +} + + +void VL53L1_copy_sys_and_core_results_to_range_results( + int32_t gain_factor, + VL53L1_system_results_t *psys, + VL53L1_core_results_t *pcore, + VL53L1_range_results_t *presults) +{ + uint8_t i = 0; + + VL53L1_range_data_t *pdata; + int32_t range_mm = 0; + uint32_t tmpu32 = 0; + uint16_t rpscr_crosstalk_corrected_mcps_sd0; + uint16_t rmmo_effective_spads_sd0; + uint16_t rmmi_effective_spads_sd0; + + LOG_FUNCTION_START(""); + + + + + presults->zone_id = 0; + presults->stream_count = psys->result__stream_count; + presults->wrap_dmax_mm = 0; + presults->max_results = VL53L1_MAX_RANGE_RESULTS; + presults->active_results = 1; + rpscr_crosstalk_corrected_mcps_sd0 = + psys->result__peak_signal_count_rate_crosstalk_corrected_mcps_sd0; + rmmo_effective_spads_sd0 = + psys->result__mm_outer_actual_effective_spads_sd0; + rmmi_effective_spads_sd0 = + psys->result__mm_inner_actual_effective_spads_sd0; + + + for (i = 0; i < VL53L1_MAX_AMBIENT_DMAX_VALUES; i++) + presults->VL53L1_p_007[i] = 0; + + pdata = &(presults->VL53L1_p_002[0]); + + for (i = 0; i < 2; i++) { + + pdata->range_id = i; + pdata->time_stamp = 0; + + if ((psys->result__stream_count == 0) && + ((psys->result__range_status & + VL53L1_RANGE_STATUS__RANGE_STATUS_MASK) == + VL53L1_DEVICEERROR_RANGECOMPLETE)) { + pdata->range_status = + VL53L1_DEVICEERROR_RANGECOMPLETE_NO_WRAP_CHECK; + } else { + pdata->range_status = + psys->result__range_status & + VL53L1_RANGE_STATUS__RANGE_STATUS_MASK; + } + + pdata->VL53L1_p_015 = 0; + pdata->VL53L1_p_022 = 0; + pdata->VL53L1_p_025 = 0; + pdata->VL53L1_p_026 = 0; + pdata->VL53L1_p_016 = 0; + pdata->VL53L1_p_027 = 0; + + switch (i) { + + case 0: + if (psys->result__report_status == + VL53L1_DEVICEREPORTSTATUS_MM1) + pdata->VL53L1_p_006 = + rmmi_effective_spads_sd0; + else if (psys->result__report_status == + VL53L1_DEVICEREPORTSTATUS_MM2) + pdata->VL53L1_p_006 = + rmmo_effective_spads_sd0; + else + pdata->VL53L1_p_006 = + psys->result__dss_actual_effective_spads_sd0; + + pdata->peak_signal_count_rate_mcps = + rpscr_crosstalk_corrected_mcps_sd0; + pdata->avg_signal_count_rate_mcps = + psys->result__avg_signal_count_rate_mcps_sd0; + pdata->ambient_count_rate_mcps = + psys->result__ambient_count_rate_mcps_sd0; + + + + + + + tmpu32 = ((uint32_t)psys->result__sigma_sd0 << 5); + if (tmpu32 > 0xFFFF) + tmpu32 = 0xFFFF; + + pdata->VL53L1_p_005 = (uint16_t)tmpu32; + + + + + pdata->VL53L1_p_014 = + psys->result__phase_sd0; + + range_mm = (int32_t)( + psys->result__final_crosstalk_corrected_range_mm_sd0); + + + + range_mm *= gain_factor; + range_mm += 0x0400; + range_mm /= 0x0800; + + pdata->median_range_mm = (int16_t)range_mm; + + pdata->VL53L1_p_021 = + pcore->result_core__ranging_total_events_sd0; + pdata->VL53L1_p_013 = + pcore->result_core__signal_total_events_sd0; + pdata->total_periods_elapsed = + pcore->result_core__total_periods_elapsed_sd0; + pdata->VL53L1_p_020 = + pcore->result_core__ambient_window_events_sd0; + + break; + case 1: + + pdata->VL53L1_p_006 = + psys->result__dss_actual_effective_spads_sd1; + pdata->peak_signal_count_rate_mcps = + psys->result__peak_signal_count_rate_mcps_sd1; + pdata->avg_signal_count_rate_mcps = + 0xFFFF; + pdata->ambient_count_rate_mcps = + psys->result__ambient_count_rate_mcps_sd1; + + + + + + + tmpu32 = ((uint32_t)psys->result__sigma_sd1 << 5); + if (tmpu32 > 0xFFFF) + tmpu32 = 0xFFFF; + + pdata->VL53L1_p_005 = (uint16_t)tmpu32; + + + + + pdata->VL53L1_p_014 = + psys->result__phase_sd1; + + range_mm = (int32_t)( + psys->result__final_crosstalk_corrected_range_mm_sd1); + + + + range_mm *= gain_factor; + range_mm += 0x0400; + range_mm /= 0x0800; + + pdata->median_range_mm = (int16_t)range_mm; + + pdata->VL53L1_p_021 = + pcore->result_core__ranging_total_events_sd1; + pdata->VL53L1_p_013 = + pcore->result_core__signal_total_events_sd1; + pdata->total_periods_elapsed = + pcore->result_core__total_periods_elapsed_sd1; + pdata->VL53L1_p_020 = + pcore->result_core__ambient_window_events_sd1; + + break; + } + + + + + + pdata->VL53L1_p_028 = pdata->VL53L1_p_014; + pdata->VL53L1_p_029 = pdata->VL53L1_p_014; + pdata->min_range_mm = pdata->median_range_mm; + pdata->max_range_mm = pdata->median_range_mm; + + pdata++; + } + + + + + + + presults->device_status = VL53L1_DEVICEERROR_NOUPDATE; + + + + + + + + + switch (psys->result__range_status & + VL53L1_RANGE_STATUS__RANGE_STATUS_MASK) { + + case VL53L1_DEVICEERROR_VCSELCONTINUITYTESTFAILURE: + case VL53L1_DEVICEERROR_VCSELWATCHDOGTESTFAILURE: + case VL53L1_DEVICEERROR_NOVHVVALUEFOUND: + case VL53L1_DEVICEERROR_USERROICLIP: + case VL53L1_DEVICEERROR_MULTCLIPFAIL: + + presults->device_status = (psys->result__range_status & + VL53L1_RANGE_STATUS__RANGE_STATUS_MASK); + + presults->VL53L1_p_002[0].range_status = + VL53L1_DEVICEERROR_NOUPDATE; + break; + + } + + LOG_FUNCTION_END(0); +} + + +VL53L1_Error VL53L1_set_zone_dss_config( + VL53L1_DEV Dev, + VL53L1_zone_private_dyn_cfg_t *pzone_dyn_cfg) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_ll_driver_state_t *pstate = &(pdev->ll_state); + + LOG_FUNCTION_START(""); + + if (pstate->cfg_device_state == + VL53L1_DEVICESTATE_RANGING_DSS_MANUAL) { + pdev->gen_cfg.dss_config__roi_mode_control = + VL53L1_DSS_CONTROL__MODE_EFFSPADS; + pdev->gen_cfg.dss_config__manual_effective_spads_select = + pzone_dyn_cfg->dss_requested_effective_spad_count; + } else { + pdev->gen_cfg.dss_config__roi_mode_control = + VL53L1_DSS_CONTROL__MODE_TARGET_RATE; + } + + LOG_FUNCTION_END(status); + return status; +} + + +VL53L1_Error VL53L1_calc_ambient_dmax( + VL53L1_DEV Dev, + uint16_t target_reflectance, + int16_t *pambient_dmax_mm) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + VL53L1_dmax_calibration_data_t dmax_cal; + VL53L1_dmax_calibration_data_t *pdmax_cal = &dmax_cal; + + LOG_FUNCTION_START(""); + + + + + + + status = + VL53L1_get_dmax_calibration_data( + Dev, + pdev->debug_mode, + pdev->ll_state.rd_zone_id, + pdmax_cal); + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_ipp_hist_ambient_dmax( + Dev, + target_reflectance, + &(pdev->fmt_dmax_cal), + &(pdev->dmax_cfg), + &(pdev->hist_data), + pambient_dmax_mm); + + LOG_FUNCTION_END(status); + + return status; +} + + + + + + +VL53L1_Error VL53L1_set_GPIO_interrupt_config( + VL53L1_DEV Dev, + VL53L1_GPIO_Interrupt_Mode intr_mode_distance, + VL53L1_GPIO_Interrupt_Mode intr_mode_rate, + uint8_t intr_new_measure_ready, + uint8_t intr_no_target, + uint8_t intr_combined_mode, + uint16_t thresh_distance_high, + uint16_t thresh_distance_low, + uint16_t thresh_rate_high, + uint16_t thresh_rate_low + ) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_GPIO_interrupt_config_t *pintconf = + &(pdev->gpio_interrupt_config); + + LOG_FUNCTION_START(""); + + + + pintconf->intr_mode_distance = intr_mode_distance; + pintconf->intr_mode_rate = intr_mode_rate; + pintconf->intr_new_measure_ready = intr_new_measure_ready; + pintconf->intr_no_target = intr_no_target; + pintconf->intr_combined_mode = intr_combined_mode; + pintconf->threshold_distance_high = thresh_distance_high; + pintconf->threshold_distance_low = thresh_distance_low; + pintconf->threshold_rate_high = thresh_rate_high; + pintconf->threshold_rate_low = thresh_rate_low; + + + + pdev->gen_cfg.system__interrupt_config_gpio = + VL53L1_encode_GPIO_interrupt_config(pintconf); + + + + + status = VL53L1_set_GPIO_thresholds_from_struct( + Dev, + pintconf); + + LOG_FUNCTION_END(status); + return status; +} + + + + + + +VL53L1_Error VL53L1_set_GPIO_interrupt_config_struct( + VL53L1_DEV Dev, + VL53L1_GPIO_interrupt_config_t intconf) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_GPIO_interrupt_config_t *pintconf = + &(pdev->gpio_interrupt_config); + + LOG_FUNCTION_START(""); + + + + memcpy(pintconf, &(intconf), sizeof(VL53L1_GPIO_interrupt_config_t)); + + + + pdev->gen_cfg.system__interrupt_config_gpio = + VL53L1_encode_GPIO_interrupt_config(pintconf); + + + + status = VL53L1_set_GPIO_thresholds_from_struct( + Dev, + pintconf); + + LOG_FUNCTION_END(status); + return status; +} + + + + + + +VL53L1_Error VL53L1_get_GPIO_interrupt_config( + VL53L1_DEV Dev, + VL53L1_GPIO_interrupt_config_t *pintconf) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + + + + pdev->gpio_interrupt_config = VL53L1_decode_GPIO_interrupt_config( + pdev->gen_cfg.system__interrupt_config_gpio); + + + + + + pdev->gpio_interrupt_config.threshold_distance_high = + pdev->dyn_cfg.system__thresh_high; + pdev->gpio_interrupt_config.threshold_distance_low = + pdev->dyn_cfg.system__thresh_low; + + pdev->gpio_interrupt_config.threshold_rate_high = + pdev->gen_cfg.system__thresh_rate_high; + pdev->gpio_interrupt_config.threshold_rate_low = + pdev->gen_cfg.system__thresh_rate_low; + + if (pintconf == &(pdev->gpio_interrupt_config)) { + + + } else { + + + + memcpy(pintconf, &(pdev->gpio_interrupt_config), + sizeof(VL53L1_GPIO_interrupt_config_t)); + } + + LOG_FUNCTION_END(status); + return status; +} + + +VL53L1_Error VL53L1_set_dmax_mode( + VL53L1_DEV Dev, + VL53L1_DeviceDmaxMode dmax_mode) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->dmax_mode = dmax_mode; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_dmax_mode( + VL53L1_DEV Dev, + VL53L1_DeviceDmaxMode *pdmax_mode) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *pdmax_mode = pdev->dmax_mode; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_dmax_calibration_data( + VL53L1_DEV Dev, + VL53L1_DeviceDmaxMode dmax_mode, + uint8_t zone_id, + VL53L1_dmax_calibration_data_t *pdmax_cal) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + LOG_FUNCTION_START(""); + + switch (dmax_mode) { + + case VL53L1_DEVICEDMAXMODE__PER_ZONE_CAL_DATA: + pdmax_cal->ref__actual_effective_spads = + (uint16_t)pres->zone_cal.VL53L1_p_002[zone_id].effective_spads; + pdmax_cal->ref__peak_signal_count_rate_mcps = + (uint16_t)pres->zone_cal.VL53L1_p_002[zone_id].peak_rate_mcps; + pdmax_cal->ref__distance_mm = + pres->zone_cal.cal_distance_mm; + pdmax_cal->ref_reflectance_pc = + pres->zone_cal.cal_reflectance_pc; + pdmax_cal->coverglass_transmission = 0x0100; + break; + + case VL53L1_DEVICEDMAXMODE__CUST_CAL_DATA: + memcpy( + pdmax_cal, + &(pdev->cust_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + break; + + case VL53L1_DEVICEDMAXMODE__FMT_CAL_DATA: + memcpy( + pdmax_cal, + &(pdev->fmt_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + break; + + default: + status = VL53L1_ERROR_INVALID_PARAMS; + break; + + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_hist_dmax_config( + VL53L1_DEV Dev, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + memcpy( + &(pdev->dmax_cfg), + pdmax_cfg, + sizeof(VL53L1_hist_gen3_dmax_config_t)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_hist_dmax_config( + VL53L1_DEV Dev, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + memcpy( + pdmax_cfg, + &(pdev->dmax_cfg), + sizeof(VL53L1_hist_gen3_dmax_config_t)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_offset_calibration_mode( + VL53L1_DEV Dev, + VL53L1_OffsetCalibrationMode offset_cal_mode) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->offset_calibration_mode = offset_cal_mode; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_offset_calibration_mode( + VL53L1_DEV Dev, + VL53L1_OffsetCalibrationMode *poffset_cal_mode) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *poffset_cal_mode = pdev->offset_calibration_mode; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_offset_correction_mode( + VL53L1_DEV Dev, + VL53L1_OffsetCorrectionMode offset_cor_mode) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->offset_correction_mode = offset_cor_mode; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_offset_correction_mode( + VL53L1_DEV Dev, + VL53L1_OffsetCorrectionMode *poffset_cor_mode) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *poffset_cor_mode = pdev->offset_correction_mode; + + LOG_FUNCTION_END(status); + + return status; +} + + + + + +VL53L1_Error VL53L1_set_zone_calibration_data( + VL53L1_DEV Dev, + VL53L1_zone_calibration_results_t *pzone_cal) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverResults_t *pres = VL53L1DevStructGetLLResultsHandle(Dev); + + LOG_FUNCTION_START(""); + + if (pzone_cal->struct_version != + VL53L1_LL_ZONE_CALIBRATION_DATA_STRUCT_VERSION) + status = VL53L1_ERROR_INVALID_PARAMS; + + + if (status == VL53L1_ERROR_NONE) + + + memcpy( + &(pres->zone_cal), + pzone_cal, + sizeof(VL53L1_zone_calibration_results_t)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_zone_calibration_data( + VL53L1_DEV Dev, + VL53L1_zone_calibration_results_t *pzone_cal) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverResults_t *pres = VL53L1DevStructGetLLResultsHandle(Dev); + + LOG_FUNCTION_START(""); + + + + memcpy( + pzone_cal, + &(pres->zone_cal), + sizeof(VL53L1_zone_calibration_results_t)); + + pzone_cal->struct_version = + VL53L1_LL_ZONE_CALIBRATION_DATA_STRUCT_VERSION; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_tuning_debug_data( + VL53L1_DEV Dev, + VL53L1_tuning_parameters_t *ptun_data) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_hist_post_process_config_t *pHP = &(pdev->histpostprocess); + VL53L1_xtalkextract_config_t *pXC = &(pdev->xtalk_extract_cfg); + + LOG_FUNCTION_START(""); + + ptun_data->vl53l1_tuningparm_version = + pdev->tuning_parms.tp_tuning_parm_version; + + ptun_data->vl53l1_tuningparm_key_table_version = + pdev->tuning_parms.tp_tuning_parm_key_table_version; + + + ptun_data->vl53l1_tuningparm_lld_version = + pdev->tuning_parms.tp_tuning_parm_lld_version; + + ptun_data->vl53l1_tuningparm_hist_algo_select = + pHP->hist_algo_select; + + ptun_data->vl53l1_tuningparm_hist_target_order = + pHP->hist_target_order; + + ptun_data->vl53l1_tuningparm_hist_filter_woi_0 = + pHP->filter_woi0; + + ptun_data->vl53l1_tuningparm_hist_filter_woi_1 = + pHP->filter_woi1; + + ptun_data->vl53l1_tuningparm_hist_amb_est_method = + pHP->hist_amb_est_method; + + ptun_data->vl53l1_tuningparm_hist_amb_thresh_sigma_0 = + pHP->ambient_thresh_sigma0; + + ptun_data->vl53l1_tuningparm_hist_amb_thresh_sigma_1 = + pHP->ambient_thresh_sigma1; + + ptun_data->vl53l1_tuningparm_hist_min_amb_thresh_events = + pHP->min_ambient_thresh_events; + + ptun_data->vl53l1_tuningparm_hist_amb_events_scaler = + pHP->ambient_thresh_events_scaler; + + ptun_data->vl53l1_tuningparm_hist_noise_threshold = + pHP->noise_threshold; + + ptun_data->vl53l1_tuningparm_hist_signal_total_events_limit = + pHP->signal_total_events_limit; + + ptun_data->vl53l1_tuningparm_hist_sigma_est_ref_mm = + pHP->sigma_estimator__sigma_ref_mm; + + ptun_data->vl53l1_tuningparm_hist_sigma_thresh_mm = + pHP->sigma_thresh; + + ptun_data->vl53l1_tuningparm_hist_gain_factor = + pdev->gain_cal.histogram_ranging_gain_factor; + + ptun_data->vl53l1_tuningparm_consistency_hist_phase_tolerance = + pHP->algo__consistency_check__phase_tolerance; + + ptun_data->vl53l1_tuningparm_consistency_hist_min_max_tolerance_mm = + pHP->algo__consistency_check__min_max_tolerance; + + ptun_data->vl53l1_tuningparm_consistency_hist_event_sigma = + pHP->algo__consistency_check__event_sigma; + + ptun_data->vl53l1_tuningparm_consistency_hist_event_sigma_min_spad_limit + = pHP->algo__consistency_check__event_min_spad_count; + + ptun_data->vl53l1_tuningparm_initial_phase_rtn_histo_long_range = + pdev->tuning_parms.tp_init_phase_rtn_hist_long; + + ptun_data->vl53l1_tuningparm_initial_phase_rtn_histo_med_range = + pdev->tuning_parms.tp_init_phase_rtn_hist_med; + + ptun_data->vl53l1_tuningparm_initial_phase_rtn_histo_short_range = + pdev->tuning_parms.tp_init_phase_rtn_hist_short; + + ptun_data->vl53l1_tuningparm_initial_phase_ref_histo_long_range = + pdev->tuning_parms.tp_init_phase_ref_hist_long; + + ptun_data->vl53l1_tuningparm_initial_phase_ref_histo_med_range = + pdev->tuning_parms.tp_init_phase_ref_hist_med; + + ptun_data->vl53l1_tuningparm_initial_phase_ref_histo_short_range = + pdev->tuning_parms.tp_init_phase_ref_hist_short; + + ptun_data->vl53l1_tuningparm_xtalk_detect_min_valid_range_mm = + pdev->xtalk_cfg.algo__crosstalk_detect_min_valid_range_mm; + + ptun_data->vl53l1_tuningparm_xtalk_detect_max_valid_range_mm = + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_range_mm; + + ptun_data->vl53l1_tuningparm_xtalk_detect_max_sigma_mm = + pdev->xtalk_cfg.algo__crosstalk_detect_max_sigma_mm; + + ptun_data->vl53l1_tuningparm_xtalk_detect_min_max_tolerance = + pHP->algo__crosstalk_detect_min_max_tolerance; + + ptun_data->vl53l1_tuningparm_xtalk_detect_max_valid_rate_kcps = + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_rate_kcps; + + ptun_data->vl53l1_tuningparm_xtalk_detect_event_sigma = + pHP->algo__crosstalk_detect_event_sigma; + + ptun_data->vl53l1_tuningparm_hist_xtalk_margin_kcps = + pdev->xtalk_cfg.histogram_mode_crosstalk_margin_kcps; + + ptun_data->vl53l1_tuningparm_consistency_lite_phase_tolerance = + pdev->tuning_parms.tp_consistency_lite_phase_tolerance; + + ptun_data->vl53l1_tuningparm_phasecal_target = + pdev->tuning_parms.tp_phasecal_target; + + ptun_data->vl53l1_tuningparm_lite_cal_repeat_rate = + pdev->tuning_parms.tp_cal_repeat_rate; + + ptun_data->vl53l1_tuningparm_lite_ranging_gain_factor = + pdev->gain_cal.standard_ranging_gain_factor; + + ptun_data->vl53l1_tuningparm_lite_min_clip_mm = + pdev->tuning_parms.tp_lite_min_clip; + + ptun_data->vl53l1_tuningparm_lite_long_sigma_thresh_mm = + pdev->tuning_parms.tp_lite_long_sigma_thresh_mm; + + ptun_data->vl53l1_tuningparm_lite_med_sigma_thresh_mm = + pdev->tuning_parms.tp_lite_med_sigma_thresh_mm; + + ptun_data->vl53l1_tuningparm_lite_short_sigma_thresh_mm = + pdev->tuning_parms.tp_lite_short_sigma_thresh_mm; + + ptun_data->vl53l1_tuningparm_lite_long_min_count_rate_rtn_mcps = + pdev->tuning_parms.tp_lite_long_min_count_rate_rtn_mcps; + + ptun_data->vl53l1_tuningparm_lite_med_min_count_rate_rtn_mcps = + pdev->tuning_parms.tp_lite_med_min_count_rate_rtn_mcps; + + ptun_data->vl53l1_tuningparm_lite_short_min_count_rate_rtn_mcps = + pdev->tuning_parms.tp_lite_short_min_count_rate_rtn_mcps; + + ptun_data->vl53l1_tuningparm_lite_sigma_est_pulse_width = + pdev->tuning_parms.tp_lite_sigma_est_pulse_width_ns; + + ptun_data->vl53l1_tuningparm_lite_sigma_est_amb_width_ns = + pdev->tuning_parms.tp_lite_sigma_est_amb_width_ns; + + ptun_data->vl53l1_tuningparm_lite_sigma_ref_mm = + pdev->tuning_parms.tp_lite_sigma_ref_mm; + + ptun_data->vl53l1_tuningparm_lite_rit_mult = + pdev->xtalk_cfg.crosstalk_range_ignore_threshold_mult; + + ptun_data->vl53l1_tuningparm_lite_seed_config = + pdev->tuning_parms.tp_lite_seed_cfg; + + ptun_data->vl53l1_tuningparm_lite_quantifier = + pdev->tuning_parms.tp_lite_quantifier; + + ptun_data->vl53l1_tuningparm_lite_first_order_select = + pdev->tuning_parms.tp_lite_first_order_select; + + ptun_data->vl53l1_tuningparm_lite_xtalk_margin_kcps = + pdev->xtalk_cfg.lite_mode_crosstalk_margin_kcps; + + ptun_data->vl53l1_tuningparm_initial_phase_rtn_lite_long_range = + pdev->tuning_parms.tp_init_phase_rtn_lite_long; + + ptun_data->vl53l1_tuningparm_initial_phase_rtn_lite_med_range = + pdev->tuning_parms.tp_init_phase_rtn_lite_med; + + ptun_data->vl53l1_tuningparm_initial_phase_rtn_lite_short_range = + pdev->tuning_parms.tp_init_phase_rtn_lite_short; + + ptun_data->vl53l1_tuningparm_initial_phase_ref_lite_long_range = + pdev->tuning_parms.tp_init_phase_ref_lite_long; + + ptun_data->vl53l1_tuningparm_initial_phase_ref_lite_med_range = + pdev->tuning_parms.tp_init_phase_ref_lite_med; + + ptun_data->vl53l1_tuningparm_initial_phase_ref_lite_short_range = + pdev->tuning_parms.tp_init_phase_ref_lite_short; + + ptun_data->vl53l1_tuningparm_timed_seed_config = + pdev->tuning_parms.tp_timed_seed_cfg; + + ptun_data->vl53l1_tuningparm_dmax_cfg_signal_thresh_sigma = + pdev->dmax_cfg.signal_thresh_sigma; + + ptun_data->vl53l1_tuningparm_dmax_cfg_reflectance_array_0 = + pdev->dmax_cfg.target_reflectance_for_dmax_calc[0]; + + ptun_data->vl53l1_tuningparm_dmax_cfg_reflectance_array_1 = + pdev->dmax_cfg.target_reflectance_for_dmax_calc[1]; + + ptun_data->vl53l1_tuningparm_dmax_cfg_reflectance_array_2 = + pdev->dmax_cfg.target_reflectance_for_dmax_calc[2]; + + ptun_data->vl53l1_tuningparm_dmax_cfg_reflectance_array_3 = + pdev->dmax_cfg.target_reflectance_for_dmax_calc[3]; + + ptun_data->vl53l1_tuningparm_dmax_cfg_reflectance_array_4 = + pdev->dmax_cfg.target_reflectance_for_dmax_calc[4]; + + ptun_data->vl53l1_tuningparm_vhv_loopbound = + pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound; + + ptun_data->vl53l1_tuningparm_refspadchar_device_test_mode = + pdev->refspadchar.device_test_mode; + + ptun_data->vl53l1_tuningparm_refspadchar_vcsel_period = + pdev->refspadchar.VL53L1_p_009; + + ptun_data->vl53l1_tuningparm_refspadchar_phasecal_timeout_us = + pdev->refspadchar.timeout_us; + + ptun_data->vl53l1_tuningparm_refspadchar_target_count_rate_mcps = + pdev->refspadchar.target_count_rate_mcps; + + ptun_data->vl53l1_tuningparm_refspadchar_min_countrate_limit_mcps = + pdev->refspadchar.min_count_rate_limit_mcps; + + ptun_data->vl53l1_tuningparm_refspadchar_max_countrate_limit_mcps = + pdev->refspadchar.max_count_rate_limit_mcps; + + ptun_data->vl53l1_tuningparm_xtalk_extract_num_of_samples = + pXC->num_of_samples; + + ptun_data->vl53l1_tuningparm_xtalk_extract_min_filter_thresh_mm = + pXC->algo__crosstalk_extract_min_valid_range_mm; + + ptun_data->vl53l1_tuningparm_xtalk_extract_max_filter_thresh_mm = + pXC->algo__crosstalk_extract_max_valid_range_mm; + + ptun_data->vl53l1_tuningparm_xtalk_extract_dss_rate_mcps = + pXC->dss_config__target_total_rate_mcps; + + ptun_data->vl53l1_tuningparm_xtalk_extract_phasecal_timeout_us = + pXC->phasecal_config_timeout_us; + + ptun_data->vl53l1_tuningparm_xtalk_extract_max_valid_rate_kcps = + pXC->algo__crosstalk_extract_max_valid_rate_kcps; + + ptun_data->vl53l1_tuningparm_xtalk_extract_sigma_threshold_mm = + pXC->algo__crosstalk_extract_max_sigma_mm; + + ptun_data->vl53l1_tuningparm_xtalk_extract_dss_timeout_us = + pXC->mm_config_timeout_us; + + ptun_data->vl53l1_tuningparm_xtalk_extract_bin_timeout_us = + pXC->range_config_timeout_us; + + ptun_data->vl53l1_tuningparm_offset_cal_dss_rate_mcps = + pdev->offsetcal_cfg.dss_config__target_total_rate_mcps; + + ptun_data->vl53l1_tuningparm_offset_cal_phasecal_timeout_us = + pdev->offsetcal_cfg.phasecal_config_timeout_us; + + ptun_data->vl53l1_tuningparm_offset_cal_mm_timeout_us = + pdev->offsetcal_cfg.mm_config_timeout_us; + + ptun_data->vl53l1_tuningparm_offset_cal_range_timeout_us = + pdev->offsetcal_cfg.range_config_timeout_us; + + ptun_data->vl53l1_tuningparm_offset_cal_pre_samples = + pdev->offsetcal_cfg.pre_num_of_samples; + + ptun_data->vl53l1_tuningparm_offset_cal_mm1_samples = + pdev->offsetcal_cfg.mm1_num_of_samples; + + ptun_data->vl53l1_tuningparm_offset_cal_mm2_samples = + pdev->offsetcal_cfg.mm2_num_of_samples; + + ptun_data->vl53l1_tuningparm_zone_cal_dss_rate_mcps = + pdev->zonecal_cfg.dss_config__target_total_rate_mcps; + + ptun_data->vl53l1_tuningparm_zone_cal_phasecal_timeout_us = + pdev->zonecal_cfg.phasecal_config_timeout_us; + + ptun_data->vl53l1_tuningparm_zone_cal_dss_timeout_us = + pdev->zonecal_cfg.mm_config_timeout_us; + + ptun_data->vl53l1_tuningparm_zone_cal_phasecal_num_samples = + pdev->zonecal_cfg.phasecal_num_of_samples; + + ptun_data->vl53l1_tuningparm_zone_cal_range_timeout_us = + pdev->zonecal_cfg.range_config_timeout_us; + + ptun_data->vl53l1_tuningparm_zone_cal_zone_num_samples = + pdev->zonecal_cfg.zone_num_of_samples; + + ptun_data->vl53l1_tuningparm_spadmap_vcsel_period = + pdev->ssc_cfg.VL53L1_p_009; + + ptun_data->vl53l1_tuningparm_spadmap_vcsel_start = + pdev->ssc_cfg.vcsel_start; + + ptun_data->vl53l1_tuningparm_spadmap_rate_limit_mcps = + pdev->ssc_cfg.rate_limit_mcps; + + ptun_data->vl53l1_tuningparm_lite_dss_config_target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_lite_mcps; + + ptun_data->vl53l1_tuningparm_ranging_dss_config_target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_histo_mcps; + + ptun_data->vl53l1_tuningparm_mz_dss_config_target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_histo_mz_mcps; + + ptun_data->vl53l1_tuningparm_timed_dss_config_target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_timed_mcps; + + ptun_data->vl53l1_tuningparm_lite_phasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_lite_us; + + ptun_data->vl53l1_tuningparm_ranging_long_phasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_hist_long_us; + + ptun_data->vl53l1_tuningparm_ranging_med_phasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_hist_med_us; + + ptun_data->vl53l1_tuningparm_ranging_short_phasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_hist_short_us; + + ptun_data->vl53l1_tuningparm_mz_long_phasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_mz_long_us; + + ptun_data->vl53l1_tuningparm_mz_med_phasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_mz_med_us; + + ptun_data->vl53l1_tuningparm_mz_short_phasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_mz_short_us; + + ptun_data->vl53l1_tuningparm_timed_phasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_timed_us; + + ptun_data->vl53l1_tuningparm_lite_mm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_lite_us; + + ptun_data->vl53l1_tuningparm_ranging_mm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_histo_us; + + ptun_data->vl53l1_tuningparm_mz_mm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_mz_us; + + ptun_data->vl53l1_tuningparm_timed_mm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_timed_us; + + ptun_data->vl53l1_tuningparm_lite_range_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_lite_us; + + ptun_data->vl53l1_tuningparm_ranging_range_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_histo_us; + + ptun_data->vl53l1_tuningparm_mz_range_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_mz_us; + + ptun_data->vl53l1_tuningparm_timed_range_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_timed_us; + + ptun_data->vl53l1_tuningparm_dynxtalk_smudge_margin = + pdev->smudge_correct_config.smudge_margin; + + ptun_data->vl53l1_tuningparm_dynxtalk_noise_margin = + pdev->smudge_correct_config.noise_margin; + + ptun_data->vl53l1_tuningparm_dynxtalk_xtalk_offset_limit = + pdev->smudge_correct_config.user_xtalk_offset_limit; + + ptun_data->vl53l1_tuningparm_dynxtalk_xtalk_offset_limit_hi = + pdev->smudge_correct_config.user_xtalk_offset_limit_hi; + + ptun_data->vl53l1_tuningparm_dynxtalk_sample_limit = + pdev->smudge_correct_config.sample_limit; + + ptun_data->vl53l1_tuningparm_dynxtalk_single_xtalk_delta = + pdev->smudge_correct_config.single_xtalk_delta; + + ptun_data->vl53l1_tuningparm_dynxtalk_averaged_xtalk_delta = + pdev->smudge_correct_config.averaged_xtalk_delta; + + ptun_data->vl53l1_tuningparm_dynxtalk_clip_limit = + pdev->smudge_correct_config.smudge_corr_clip_limit; + + ptun_data->vl53l1_tuningparm_dynxtalk_scaler_calc_method = + pdev->smudge_correct_config.scaler_calc_method; + + ptun_data->vl53l1_tuningparm_dynxtalk_xgradient_scaler = + pdev->smudge_correct_config.x_gradient_scaler; + + ptun_data->vl53l1_tuningparm_dynxtalk_ygradient_scaler = + pdev->smudge_correct_config.y_gradient_scaler; + + ptun_data->vl53l1_tuningparm_dynxtalk_user_scaler_set = + pdev->smudge_correct_config.user_scaler_set; + + ptun_data->vl53l1_tuningparm_dynxtalk_smudge_cor_single_apply = + pdev->smudge_correct_config.smudge_corr_single_apply; + + ptun_data->vl53l1_tuningparm_dynxtalk_xtalk_amb_threshold = + pdev->smudge_correct_config.smudge_corr_ambient_threshold; + + ptun_data->vl53l1_tuningparm_dynxtalk_nodetect_amb_threshold_kcps = + pdev->smudge_correct_config.nodetect_ambient_threshold; + + ptun_data->vl53l1_tuningparm_dynxtalk_nodetect_sample_limit = + pdev->smudge_correct_config.nodetect_sample_limit; + + ptun_data->vl53l1_tuningparm_dynxtalk_nodetect_xtalk_offset_kcps = + pdev->smudge_correct_config.nodetect_xtalk_offset; + + ptun_data->vl53l1_tuningparm_dynxtalk_nodetect_min_range_mm = + pdev->smudge_correct_config.nodetect_min_range_mm; + + ptun_data->vl53l1_tuningparm_lowpowerauto_vhv_loop_bound = + pdev->low_power_auto_data.vhv_loop_bound; + + ptun_data->vl53l1_tuningparm_lowpowerauto_mm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_lpa_us; + + ptun_data->vl53l1_tuningparm_lowpowerauto_range_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_lpa_us; + + ptun_data->vl53l1_tuningparm_very_short_dss_rate_mcps = + pdev->tuning_parms.tp_dss_target_very_short_mcps; + + LOG_FUNCTION_END(status); + + return status; +} + + + + + + + +VL53L1_Error VL53L1_get_tuning_parm( + VL53L1_DEV Dev, + VL53L1_TuningParms tuning_parm_key, + int32_t *ptuning_parm_value) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_hist_post_process_config_t *pHP = &(pdev->histpostprocess); + VL53L1_xtalkextract_config_t *pXC = &(pdev->xtalk_extract_cfg); + + LOG_FUNCTION_START(""); + + switch (tuning_parm_key) { + + case VL53L1_TUNINGPARM_VERSION: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_tuning_parm_version; + break; + case VL53L1_TUNINGPARM_KEY_TABLE_VERSION: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_tuning_parm_key_table_version; + break; + case VL53L1_TUNINGPARM_LLD_VERSION: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_tuning_parm_lld_version; + break; + case VL53L1_TUNINGPARM_HIST_ALGO_SELECT: + *ptuning_parm_value = + (int32_t)pHP->hist_algo_select; + break; + case VL53L1_TUNINGPARM_HIST_TARGET_ORDER: + *ptuning_parm_value = + (int32_t)pHP->hist_target_order; + break; + case VL53L1_TUNINGPARM_HIST_FILTER_WOI_0: + *ptuning_parm_value = + (int32_t)pHP->filter_woi0; + break; + case VL53L1_TUNINGPARM_HIST_FILTER_WOI_1: + *ptuning_parm_value = + (int32_t)pHP->filter_woi1; + break; + case VL53L1_TUNINGPARM_HIST_AMB_EST_METHOD: + *ptuning_parm_value = + (int32_t)pHP->hist_amb_est_method; + break; + case VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_0: + *ptuning_parm_value = + (int32_t)pHP->ambient_thresh_sigma0; + break; + case VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_1: + *ptuning_parm_value = + (int32_t)pHP->ambient_thresh_sigma1; + break; + case VL53L1_TUNINGPARM_HIST_MIN_AMB_THRESH_EVENTS: + *ptuning_parm_value = + (int32_t)pHP->min_ambient_thresh_events; + break; + case VL53L1_TUNINGPARM_HIST_AMB_EVENTS_SCALER: + *ptuning_parm_value = + (int32_t)pHP->ambient_thresh_events_scaler; + break; + case VL53L1_TUNINGPARM_HIST_NOISE_THRESHOLD: + *ptuning_parm_value = + (int32_t)pHP->noise_threshold; + break; + case VL53L1_TUNINGPARM_HIST_SIGNAL_TOTAL_EVENTS_LIMIT: + *ptuning_parm_value = + (int32_t)pHP->signal_total_events_limit; + break; + case VL53L1_TUNINGPARM_HIST_SIGMA_EST_REF_MM: + *ptuning_parm_value = + (int32_t)pHP->sigma_estimator__sigma_ref_mm; + break; + case VL53L1_TUNINGPARM_HIST_SIGMA_THRESH_MM: + *ptuning_parm_value = + (int32_t)pHP->sigma_thresh; + break; + case VL53L1_TUNINGPARM_HIST_GAIN_FACTOR: + *ptuning_parm_value = + (int32_t)pdev->gain_cal.histogram_ranging_gain_factor; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_HIST_PHASE_TOLERANCE: + *ptuning_parm_value = + (int32_t)pHP->algo__consistency_check__phase_tolerance; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_HIST_MIN_MAX_TOLERANCE_MM: + *ptuning_parm_value = + (int32_t)pHP->algo__consistency_check__min_max_tolerance; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA: + *ptuning_parm_value = + (int32_t)pHP->algo__consistency_check__event_sigma; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA_MIN_SPAD_LIMIT: + *ptuning_parm_value = + (int32_t)pHP->algo__consistency_check__event_min_spad_count; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_LONG_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_rtn_hist_long; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_MED_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_rtn_hist_med; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_SHORT_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_rtn_hist_short; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_LONG_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_ref_hist_long; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_MED_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_ref_hist_med; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_SHORT_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_ref_hist_short; + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MIN_VALID_RANGE_MM: + *ptuning_parm_value = (int32_t)( + pdev->xtalk_cfg.algo__crosstalk_detect_min_valid_range_mm); + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RANGE_MM: + *ptuning_parm_value = (int32_t)( + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_range_mm); + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MAX_SIGMA_MM: + *ptuning_parm_value = + (int32_t)pdev->xtalk_cfg.algo__crosstalk_detect_max_sigma_mm; + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MIN_MAX_TOLERANCE: + *ptuning_parm_value = + (int32_t)pHP->algo__crosstalk_detect_min_max_tolerance; + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RATE_KCPS: + *ptuning_parm_value = (int32_t)( + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_rate_kcps); + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_EVENT_SIGMA: + *ptuning_parm_value = + (int32_t)pHP->algo__crosstalk_detect_event_sigma; + break; + case VL53L1_TUNINGPARM_HIST_XTALK_MARGIN_KCPS: + *ptuning_parm_value = + (int32_t)pdev->xtalk_cfg.histogram_mode_crosstalk_margin_kcps; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_LITE_PHASE_TOLERANCE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_consistency_lite_phase_tolerance; + break; + case VL53L1_TUNINGPARM_PHASECAL_TARGET: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_phasecal_target; + break; + case VL53L1_TUNINGPARM_LITE_CAL_REPEAT_RATE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_cal_repeat_rate; + break; + case VL53L1_TUNINGPARM_LITE_RANGING_GAIN_FACTOR: + *ptuning_parm_value = + (int32_t)pdev->gain_cal.standard_ranging_gain_factor; + break; + case VL53L1_TUNINGPARM_LITE_MIN_CLIP_MM: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_min_clip; + break; + case VL53L1_TUNINGPARM_LITE_LONG_SIGMA_THRESH_MM: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_long_sigma_thresh_mm; + break; + case VL53L1_TUNINGPARM_LITE_MED_SIGMA_THRESH_MM: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_med_sigma_thresh_mm; + break; + case VL53L1_TUNINGPARM_LITE_SHORT_SIGMA_THRESH_MM: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_short_sigma_thresh_mm; + break; + case VL53L1_TUNINGPARM_LITE_LONG_MIN_COUNT_RATE_RTN_MCPS: + *ptuning_parm_value = (int32_t)( + pdev->tuning_parms.tp_lite_long_min_count_rate_rtn_mcps); + break; + case VL53L1_TUNINGPARM_LITE_MED_MIN_COUNT_RATE_RTN_MCPS: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_med_min_count_rate_rtn_mcps; + break; + case VL53L1_TUNINGPARM_LITE_SHORT_MIN_COUNT_RATE_RTN_MCPS: + *ptuning_parm_value = (int32_t)( + pdev->tuning_parms.tp_lite_short_min_count_rate_rtn_mcps); + break; + case VL53L1_TUNINGPARM_LITE_SIGMA_EST_PULSE_WIDTH: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_sigma_est_pulse_width_ns; + break; + case VL53L1_TUNINGPARM_LITE_SIGMA_EST_AMB_WIDTH_NS: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_sigma_est_amb_width_ns; + break; + case VL53L1_TUNINGPARM_LITE_SIGMA_REF_MM: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_sigma_ref_mm; + break; + case VL53L1_TUNINGPARM_LITE_RIT_MULT: + *ptuning_parm_value = + (int32_t)pdev->xtalk_cfg.crosstalk_range_ignore_threshold_mult; + break; + case VL53L1_TUNINGPARM_LITE_SEED_CONFIG: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_seed_cfg; + break; + case VL53L1_TUNINGPARM_LITE_QUANTIFIER: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_quantifier; + break; + case VL53L1_TUNINGPARM_LITE_FIRST_ORDER_SELECT: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_first_order_select; + break; + case VL53L1_TUNINGPARM_LITE_XTALK_MARGIN_KCPS: + *ptuning_parm_value = + (int32_t)pdev->xtalk_cfg.lite_mode_crosstalk_margin_kcps; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_LONG_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_rtn_lite_long; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_MED_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_rtn_lite_med; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_SHORT_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_rtn_lite_short; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_LONG_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_ref_lite_long; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_MED_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_ref_lite_med; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_SHORT_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_ref_lite_short; + break; + case VL53L1_TUNINGPARM_TIMED_SEED_CONFIG: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_timed_seed_cfg; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_SIGNAL_THRESH_SIGMA: + *ptuning_parm_value = + (int32_t)pdev->dmax_cfg.signal_thresh_sigma; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_0: + *ptuning_parm_value = + (int32_t)pdev->dmax_cfg.target_reflectance_for_dmax_calc[0]; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_1: + *ptuning_parm_value = + (int32_t)pdev->dmax_cfg.target_reflectance_for_dmax_calc[1]; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_2: + *ptuning_parm_value = + (int32_t)pdev->dmax_cfg.target_reflectance_for_dmax_calc[2]; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_3: + *ptuning_parm_value = + (int32_t)pdev->dmax_cfg.target_reflectance_for_dmax_calc[3]; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_4: + *ptuning_parm_value = + (int32_t)pdev->dmax_cfg.target_reflectance_for_dmax_calc[4]; + break; + case VL53L1_TUNINGPARM_VHV_LOOPBOUND: + *ptuning_parm_value = + (int32_t)pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_DEVICE_TEST_MODE: + *ptuning_parm_value = + (int32_t)pdev->refspadchar.device_test_mode; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_VCSEL_PERIOD: + *ptuning_parm_value = + (int32_t)pdev->refspadchar.VL53L1_p_009; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_PHASECAL_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->refspadchar.timeout_us; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_TARGET_COUNT_RATE_MCPS: + *ptuning_parm_value = + (int32_t)pdev->refspadchar.target_count_rate_mcps; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_MIN_COUNTRATE_LIMIT_MCPS: + *ptuning_parm_value = + (int32_t)pdev->refspadchar.min_count_rate_limit_mcps; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_MAX_COUNTRATE_LIMIT_MCPS: + *ptuning_parm_value = + (int32_t)pdev->refspadchar.max_count_rate_limit_mcps; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_NUM_OF_SAMPLES: + *ptuning_parm_value = + (int32_t)pXC->num_of_samples; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_MIN_FILTER_THRESH_MM: + *ptuning_parm_value = + (int32_t)pXC->algo__crosstalk_extract_min_valid_range_mm; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_FILTER_THRESH_MM: + *ptuning_parm_value = + (int32_t)pXC->algo__crosstalk_extract_max_valid_range_mm; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_RATE_MCPS: + *ptuning_parm_value = + (int32_t)pXC->dss_config__target_total_rate_mcps; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_PHASECAL_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pXC->phasecal_config_timeout_us; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_VALID_RATE_KCPS: + *ptuning_parm_value = + (int32_t)pXC->algo__crosstalk_extract_max_valid_rate_kcps; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_SIGMA_THRESHOLD_MM: + *ptuning_parm_value = + (int32_t)pXC->algo__crosstalk_extract_max_sigma_mm; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pXC->mm_config_timeout_us; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_BIN_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pXC->range_config_timeout_us; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_DSS_RATE_MCPS: + *ptuning_parm_value = + (int32_t)pdev->offsetcal_cfg.dss_config__target_total_rate_mcps; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_PHASECAL_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->offsetcal_cfg.phasecal_config_timeout_us; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_MM_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->offsetcal_cfg.mm_config_timeout_us; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_RANGE_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->offsetcal_cfg.range_config_timeout_us; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_PRE_SAMPLES: + *ptuning_parm_value = + (int32_t)pdev->offsetcal_cfg.pre_num_of_samples; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_MM1_SAMPLES: + *ptuning_parm_value = + (int32_t)pdev->offsetcal_cfg.mm1_num_of_samples; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_MM2_SAMPLES: + *ptuning_parm_value = + (int32_t)pdev->offsetcal_cfg.mm2_num_of_samples; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_DSS_RATE_MCPS: + *ptuning_parm_value = + (int32_t)pdev->zonecal_cfg.dss_config__target_total_rate_mcps; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->zonecal_cfg.phasecal_config_timeout_us; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_DSS_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->zonecal_cfg.mm_config_timeout_us; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_NUM_SAMPLES: + *ptuning_parm_value = + (int32_t)pdev->zonecal_cfg.phasecal_num_of_samples; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_RANGE_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->zonecal_cfg.range_config_timeout_us; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_ZONE_NUM_SAMPLES: + *ptuning_parm_value = + (int32_t)pdev->zonecal_cfg.zone_num_of_samples; + break; + case VL53L1_TUNINGPARM_SPADMAP_VCSEL_PERIOD: + *ptuning_parm_value = + (int32_t)pdev->ssc_cfg.VL53L1_p_009; + break; + case VL53L1_TUNINGPARM_SPADMAP_VCSEL_START: + *ptuning_parm_value = + (int32_t)pdev->ssc_cfg.vcsel_start; + break; + case VL53L1_TUNINGPARM_SPADMAP_RATE_LIMIT_MCPS: + *ptuning_parm_value = + (int32_t)pdev->ssc_cfg.rate_limit_mcps; + break; + case VL53L1_TUNINGPARM_LITE_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_dss_target_lite_mcps; + break; + case VL53L1_TUNINGPARM_RANGING_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_dss_target_histo_mcps; + break; + case VL53L1_TUNINGPARM_MZ_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_dss_target_histo_mz_mcps; + break; + case VL53L1_TUNINGPARM_TIMED_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_dss_target_timed_mcps; + break; + case VL53L1_TUNINGPARM_LITE_PHASECAL_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_phasecal_timeout_lite_us; + break; + case VL53L1_TUNINGPARM_RANGING_LONG_PHASECAL_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_phasecal_timeout_hist_long_us; + break; + case VL53L1_TUNINGPARM_RANGING_MED_PHASECAL_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_phasecal_timeout_hist_med_us; + break; + case VL53L1_TUNINGPARM_RANGING_SHORT_PHASECAL_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_phasecal_timeout_hist_short_us; + break; + case VL53L1_TUNINGPARM_MZ_LONG_PHASECAL_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_phasecal_timeout_mz_long_us; + break; + case VL53L1_TUNINGPARM_MZ_MED_PHASECAL_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_phasecal_timeout_mz_med_us; + break; + case VL53L1_TUNINGPARM_MZ_SHORT_PHASECAL_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_phasecal_timeout_mz_short_us; + break; + case VL53L1_TUNINGPARM_TIMED_PHASECAL_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_phasecal_timeout_timed_us; + break; + case VL53L1_TUNINGPARM_LITE_MM_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_mm_timeout_lite_us; + break; + case VL53L1_TUNINGPARM_RANGING_MM_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_mm_timeout_histo_us; + break; + case VL53L1_TUNINGPARM_MZ_MM_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_mm_timeout_mz_us; + break; + case VL53L1_TUNINGPARM_TIMED_MM_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_mm_timeout_timed_us; + break; + case VL53L1_TUNINGPARM_LITE_RANGE_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_range_timeout_lite_us; + break; + case VL53L1_TUNINGPARM_RANGING_RANGE_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_range_timeout_histo_us; + break; + case VL53L1_TUNINGPARM_MZ_RANGE_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_range_timeout_mz_us; + break; + case VL53L1_TUNINGPARM_TIMED_RANGE_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_range_timeout_timed_us; + break; + case VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_MARGIN: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.smudge_margin; + break; + case VL53L1_TUNINGPARM_DYNXTALK_NOISE_MARGIN: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.noise_margin; + break; + case VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.user_xtalk_offset_limit; + break; + case VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT_HI: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.user_xtalk_offset_limit_hi; + break; + case VL53L1_TUNINGPARM_DYNXTALK_SAMPLE_LIMIT: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.sample_limit; + break; + case VL53L1_TUNINGPARM_DYNXTALK_SINGLE_XTALK_DELTA: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.single_xtalk_delta; + break; + case VL53L1_TUNINGPARM_DYNXTALK_AVERAGED_XTALK_DELTA: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.averaged_xtalk_delta; + break; + case VL53L1_TUNINGPARM_DYNXTALK_CLIP_LIMIT: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.smudge_corr_clip_limit; + break; + case VL53L1_TUNINGPARM_DYNXTALK_SCALER_CALC_METHOD: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.scaler_calc_method; + break; + case VL53L1_TUNINGPARM_DYNXTALK_XGRADIENT_SCALER: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.x_gradient_scaler; + break; + case VL53L1_TUNINGPARM_DYNXTALK_YGRADIENT_SCALER: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.y_gradient_scaler; + break; + case VL53L1_TUNINGPARM_DYNXTALK_USER_SCALER_SET: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.user_scaler_set; + break; + case VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_COR_SINGLE_APPLY: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.smudge_corr_single_apply; + break; + case VL53L1_TUNINGPARM_DYNXTALK_XTALK_AMB_THRESHOLD: + *ptuning_parm_value = (int32_t)( + pdev->smudge_correct_config.smudge_corr_ambient_threshold); + break; + case VL53L1_TUNINGPARM_DYNXTALK_NODETECT_AMB_THRESHOLD_KCPS: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.nodetect_ambient_threshold; + break; + case VL53L1_TUNINGPARM_DYNXTALK_NODETECT_SAMPLE_LIMIT: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.nodetect_sample_limit; + break; + case VL53L1_TUNINGPARM_DYNXTALK_NODETECT_XTALK_OFFSET_KCPS: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.nodetect_xtalk_offset; + break; + case VL53L1_TUNINGPARM_DYNXTALK_NODETECT_MIN_RANGE_MM: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.nodetect_min_range_mm; + break; + case VL53L1_TUNINGPARM_LOWPOWERAUTO_VHV_LOOP_BOUND: + *ptuning_parm_value = + (int32_t)pdev->low_power_auto_data.vhv_loop_bound; + break; + case VL53L1_TUNINGPARM_LOWPOWERAUTO_MM_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_mm_timeout_lpa_us; + break; + case VL53L1_TUNINGPARM_LOWPOWERAUTO_RANGE_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_range_timeout_lpa_us; + break; + case VL53L1_TUNINGPARM_VERY_SHORT_DSS_RATE_MCPS: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_dss_target_very_short_mcps; + break; + + + default: + *ptuning_parm_value = 0x7FFFFFFF; + status = VL53L1_ERROR_INVALID_PARAMS; + break; + + } + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_set_tuning_parm( + VL53L1_DEV Dev, + VL53L1_TuningParms tuning_parm_key, + int32_t tuning_parm_value) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_hist_post_process_config_t *pHP = &(pdev->histpostprocess); + VL53L1_xtalkextract_config_t *pXC = &(pdev->xtalk_extract_cfg); + + LOG_FUNCTION_START(""); + + switch (tuning_parm_key) { + + case VL53L1_TUNINGPARM_VERSION: + pdev->tuning_parms.tp_tuning_parm_version = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_KEY_TABLE_VERSION: + pdev->tuning_parms.tp_tuning_parm_key_table_version = + (uint16_t)tuning_parm_value; + + + + + + + + + + + if ((uint16_t)tuning_parm_value + != VL53L1_TUNINGPARM_KEY_TABLE_VERSION_DEFAULT) + status = VL53L1_ERROR_TUNING_PARM_KEY_MISMATCH; + + break; + case VL53L1_TUNINGPARM_LLD_VERSION: + pdev->tuning_parms.tp_tuning_parm_lld_version = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_ALGO_SELECT: + pHP->hist_algo_select = + (VL53L1_HistAlgoSelect)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_TARGET_ORDER: + pHP->hist_target_order = + (VL53L1_HistTargetOrder)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_FILTER_WOI_0: + pHP->filter_woi0 = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_FILTER_WOI_1: + pHP->filter_woi1 = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_AMB_EST_METHOD: + pHP->hist_amb_est_method = + (VL53L1_HistAmbEstMethod)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_0: + pHP->ambient_thresh_sigma0 = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_1: + pHP->ambient_thresh_sigma1 = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_MIN_AMB_THRESH_EVENTS: + pHP->min_ambient_thresh_events = + (int32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_AMB_EVENTS_SCALER: + pHP->ambient_thresh_events_scaler = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_NOISE_THRESHOLD: + pHP->noise_threshold = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_SIGNAL_TOTAL_EVENTS_LIMIT: + pHP->signal_total_events_limit = + (int32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_SIGMA_EST_REF_MM: + pHP->sigma_estimator__sigma_ref_mm = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_SIGMA_THRESH_MM: + pHP->sigma_thresh = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_GAIN_FACTOR: + pdev->gain_cal.histogram_ranging_gain_factor = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_HIST_PHASE_TOLERANCE: + pHP->algo__consistency_check__phase_tolerance = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_HIST_MIN_MAX_TOLERANCE_MM: + pHP->algo__consistency_check__min_max_tolerance = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA: + pHP->algo__consistency_check__event_sigma = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA_MIN_SPAD_LIMIT: + pHP->algo__consistency_check__event_min_spad_count = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_LONG_RANGE: + pdev->tuning_parms.tp_init_phase_rtn_hist_long = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_MED_RANGE: + pdev->tuning_parms.tp_init_phase_rtn_hist_med = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_SHORT_RANGE: + pdev->tuning_parms.tp_init_phase_rtn_hist_short = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_LONG_RANGE: + pdev->tuning_parms.tp_init_phase_ref_hist_long = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_MED_RANGE: + pdev->tuning_parms.tp_init_phase_ref_hist_med = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_SHORT_RANGE: + pdev->tuning_parms.tp_init_phase_ref_hist_short = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MIN_VALID_RANGE_MM: + pdev->xtalk_cfg.algo__crosstalk_detect_min_valid_range_mm = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RANGE_MM: + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_range_mm = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MAX_SIGMA_MM: + pdev->xtalk_cfg.algo__crosstalk_detect_max_sigma_mm = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MIN_MAX_TOLERANCE: + pHP->algo__crosstalk_detect_min_max_tolerance = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RATE_KCPS: + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_rate_kcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_EVENT_SIGMA: + pHP->algo__crosstalk_detect_event_sigma = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_XTALK_MARGIN_KCPS: + pdev->xtalk_cfg.histogram_mode_crosstalk_margin_kcps = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_LITE_PHASE_TOLERANCE: + pdev->tuning_parms.tp_consistency_lite_phase_tolerance = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_PHASECAL_TARGET: + pdev->tuning_parms.tp_phasecal_target = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_CAL_REPEAT_RATE: + pdev->tuning_parms.tp_cal_repeat_rate = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_RANGING_GAIN_FACTOR: + pdev->gain_cal.standard_ranging_gain_factor = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_MIN_CLIP_MM: + pdev->tuning_parms.tp_lite_min_clip = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_LONG_SIGMA_THRESH_MM: + pdev->tuning_parms.tp_lite_long_sigma_thresh_mm = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_MED_SIGMA_THRESH_MM: + pdev->tuning_parms.tp_lite_med_sigma_thresh_mm = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_SHORT_SIGMA_THRESH_MM: + pdev->tuning_parms.tp_lite_short_sigma_thresh_mm = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_LONG_MIN_COUNT_RATE_RTN_MCPS: + pdev->tuning_parms.tp_lite_long_min_count_rate_rtn_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_MED_MIN_COUNT_RATE_RTN_MCPS: + pdev->tuning_parms.tp_lite_med_min_count_rate_rtn_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_SHORT_MIN_COUNT_RATE_RTN_MCPS: + pdev->tuning_parms.tp_lite_short_min_count_rate_rtn_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_SIGMA_EST_PULSE_WIDTH: + pdev->tuning_parms.tp_lite_sigma_est_pulse_width_ns = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_SIGMA_EST_AMB_WIDTH_NS: + pdev->tuning_parms.tp_lite_sigma_est_amb_width_ns = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_SIGMA_REF_MM: + pdev->tuning_parms.tp_lite_sigma_ref_mm = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_RIT_MULT: + pdev->xtalk_cfg.crosstalk_range_ignore_threshold_mult = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_SEED_CONFIG: + pdev->tuning_parms.tp_lite_seed_cfg = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_QUANTIFIER: + pdev->tuning_parms.tp_lite_quantifier = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_FIRST_ORDER_SELECT: + pdev->tuning_parms.tp_lite_first_order_select = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_XTALK_MARGIN_KCPS: + pdev->xtalk_cfg.lite_mode_crosstalk_margin_kcps = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_LONG_RANGE: + pdev->tuning_parms.tp_init_phase_rtn_lite_long = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_MED_RANGE: + pdev->tuning_parms.tp_init_phase_rtn_lite_med = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_SHORT_RANGE: + pdev->tuning_parms.tp_init_phase_rtn_lite_short = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_LONG_RANGE: + pdev->tuning_parms.tp_init_phase_ref_lite_long = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_MED_RANGE: + pdev->tuning_parms.tp_init_phase_ref_lite_med = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_SHORT_RANGE: + pdev->tuning_parms.tp_init_phase_ref_lite_short = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_TIMED_SEED_CONFIG: + pdev->tuning_parms.tp_timed_seed_cfg = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_SIGNAL_THRESH_SIGMA: + pdev->dmax_cfg.signal_thresh_sigma = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_0: + pdev->dmax_cfg.target_reflectance_for_dmax_calc[0] = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_1: + pdev->dmax_cfg.target_reflectance_for_dmax_calc[1] = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_2: + pdev->dmax_cfg.target_reflectance_for_dmax_calc[2] = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_3: + pdev->dmax_cfg.target_reflectance_for_dmax_calc[3] = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_4: + pdev->dmax_cfg.target_reflectance_for_dmax_calc[4] = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_VHV_LOOPBOUND: + pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_DEVICE_TEST_MODE: + pdev->refspadchar.device_test_mode = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_VCSEL_PERIOD: + pdev->refspadchar.VL53L1_p_009 = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_PHASECAL_TIMEOUT_US: + pdev->refspadchar.timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_TARGET_COUNT_RATE_MCPS: + pdev->refspadchar.target_count_rate_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_MIN_COUNTRATE_LIMIT_MCPS: + pdev->refspadchar.min_count_rate_limit_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_MAX_COUNTRATE_LIMIT_MCPS: + pdev->refspadchar.max_count_rate_limit_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_NUM_OF_SAMPLES: + pXC->num_of_samples = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_MIN_FILTER_THRESH_MM: + pXC->algo__crosstalk_extract_min_valid_range_mm = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_FILTER_THRESH_MM: + pXC->algo__crosstalk_extract_max_valid_range_mm = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_RATE_MCPS: + pXC->dss_config__target_total_rate_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_PHASECAL_TIMEOUT_US: + pXC->phasecal_config_timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_VALID_RATE_KCPS: + pXC->algo__crosstalk_extract_max_valid_rate_kcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_SIGMA_THRESHOLD_MM: + pXC->algo__crosstalk_extract_max_sigma_mm = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_TIMEOUT_US: + pXC->mm_config_timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_BIN_TIMEOUT_US: + pXC->range_config_timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_DSS_RATE_MCPS: + pdev->offsetcal_cfg.dss_config__target_total_rate_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_PHASECAL_TIMEOUT_US: + pdev->offsetcal_cfg.phasecal_config_timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_MM_TIMEOUT_US: + pdev->offsetcal_cfg.mm_config_timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_RANGE_TIMEOUT_US: + pdev->offsetcal_cfg.range_config_timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_PRE_SAMPLES: + pdev->offsetcal_cfg.pre_num_of_samples = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_MM1_SAMPLES: + pdev->offsetcal_cfg.mm1_num_of_samples = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_MM2_SAMPLES: + pdev->offsetcal_cfg.mm2_num_of_samples = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_DSS_RATE_MCPS: + pdev->zonecal_cfg.dss_config__target_total_rate_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_TIMEOUT_US: + pdev->zonecal_cfg.phasecal_config_timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_DSS_TIMEOUT_US: + pdev->zonecal_cfg.mm_config_timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_NUM_SAMPLES: + pdev->zonecal_cfg.phasecal_num_of_samples = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_RANGE_TIMEOUT_US: + pdev->zonecal_cfg.range_config_timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_ZONE_NUM_SAMPLES: + pdev->zonecal_cfg.zone_num_of_samples = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_SPADMAP_VCSEL_PERIOD: + pdev->ssc_cfg.VL53L1_p_009 = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_SPADMAP_VCSEL_START: + pdev->ssc_cfg.vcsel_start = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_SPADMAP_RATE_LIMIT_MCPS: + pdev->ssc_cfg.rate_limit_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS: + pdev->tuning_parms.tp_dss_target_lite_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_RANGING_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS: + pdev->tuning_parms.tp_dss_target_histo_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_MZ_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS: + pdev->tuning_parms.tp_dss_target_histo_mz_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_TIMED_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS: + pdev->tuning_parms.tp_dss_target_timed_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_PHASECAL_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_phasecal_timeout_lite_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_RANGING_LONG_PHASECAL_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_phasecal_timeout_hist_long_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_RANGING_MED_PHASECAL_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_phasecal_timeout_hist_med_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_RANGING_SHORT_PHASECAL_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_phasecal_timeout_hist_short_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_MZ_LONG_PHASECAL_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_phasecal_timeout_mz_long_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_MZ_MED_PHASECAL_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_phasecal_timeout_mz_med_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_MZ_SHORT_PHASECAL_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_phasecal_timeout_mz_short_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_TIMED_PHASECAL_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_phasecal_timeout_timed_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_MM_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_mm_timeout_lite_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_RANGING_MM_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_mm_timeout_histo_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_MZ_MM_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_mm_timeout_mz_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_TIMED_MM_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_mm_timeout_timed_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_RANGE_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_range_timeout_lite_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_RANGING_RANGE_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_range_timeout_histo_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_MZ_RANGE_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_range_timeout_mz_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_TIMED_RANGE_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_range_timeout_timed_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_MARGIN: + pdev->smudge_correct_config.smudge_margin = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_NOISE_MARGIN: + pdev->smudge_correct_config.noise_margin = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT: + pdev->smudge_correct_config.user_xtalk_offset_limit = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT_HI: + pdev->smudge_correct_config.user_xtalk_offset_limit_hi = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_SAMPLE_LIMIT: + pdev->smudge_correct_config.sample_limit = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_SINGLE_XTALK_DELTA: + pdev->smudge_correct_config.single_xtalk_delta = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_AVERAGED_XTALK_DELTA: + pdev->smudge_correct_config.averaged_xtalk_delta = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_CLIP_LIMIT: + pdev->smudge_correct_config.smudge_corr_clip_limit = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_SCALER_CALC_METHOD: + pdev->smudge_correct_config.scaler_calc_method = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_XGRADIENT_SCALER: + pdev->smudge_correct_config.x_gradient_scaler = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_YGRADIENT_SCALER: + pdev->smudge_correct_config.y_gradient_scaler = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_USER_SCALER_SET: + pdev->smudge_correct_config.user_scaler_set = + (uint8_t)tuning_parm_value; + break; + + case VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_COR_SINGLE_APPLY: + pdev->smudge_correct_config.smudge_corr_single_apply = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_XTALK_AMB_THRESHOLD: + pdev->smudge_correct_config.smudge_corr_ambient_threshold = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_NODETECT_AMB_THRESHOLD_KCPS: + pdev->smudge_correct_config.nodetect_ambient_threshold = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_NODETECT_SAMPLE_LIMIT: + pdev->smudge_correct_config.nodetect_sample_limit = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_NODETECT_XTALK_OFFSET_KCPS: + pdev->smudge_correct_config.nodetect_xtalk_offset = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_NODETECT_MIN_RANGE_MM: + pdev->smudge_correct_config.nodetect_min_range_mm = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LOWPOWERAUTO_VHV_LOOP_BOUND: + pdev->low_power_auto_data.vhv_loop_bound = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LOWPOWERAUTO_MM_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_mm_timeout_lpa_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LOWPOWERAUTO_RANGE_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_range_timeout_lpa_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_VERY_SHORT_DSS_RATE_MCPS: + pdev->tuning_parms.tp_dss_target_very_short_mcps = + (uint16_t)tuning_parm_value; + break; + + + default: + status = VL53L1_ERROR_INVALID_PARAMS; + break; + + } + + LOG_FUNCTION_END(status); + + return status; +} + + + + + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_enable( + VL53L1_DEV Dev + ) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->smudge_correct_config.smudge_corr_enabled = 1; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_dynamic_xtalk_correction_disable( + VL53L1_DEV Dev + ) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->smudge_correct_config.smudge_corr_enabled = 0; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_dynamic_xtalk_correction_apply_enable( + VL53L1_DEV Dev + ) +{ + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->smudge_correct_config.smudge_corr_apply_enabled = 1; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_dynamic_xtalk_correction_apply_disable( + VL53L1_DEV Dev + ) +{ + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->smudge_correct_config.smudge_corr_apply_enabled = 0; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_dynamic_xtalk_correction_single_apply_enable( + VL53L1_DEV Dev + ) +{ + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->smudge_correct_config.smudge_corr_single_apply = 1; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_dynamic_xtalk_correction_single_apply_disable( + VL53L1_DEV Dev + ) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->smudge_correct_config.smudge_corr_single_apply = 0; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_set_scalers( + VL53L1_DEV Dev, + int16_t x_scaler_in, + int16_t y_scaler_in, + uint8_t user_scaler_set_in + ) +{ + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->smudge_correct_config.x_gradient_scaler = x_scaler_in; + pdev->smudge_correct_config.y_gradient_scaler = y_scaler_in; + pdev->smudge_correct_config.user_scaler_set = user_scaler_set_in; + + LOG_FUNCTION_END(status); + + return status; +} + + + + + + + + +VL53L1_Error VL53L1_get_current_xtalk_settings( + VL53L1_DEV Dev, + VL53L1_xtalk_calibration_results_t *pxtalk + ) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pxtalk->algo__crosstalk_compensation_plane_offset_kcps = + pdev->xtalk_cfg.algo__crosstalk_compensation_plane_offset_kcps; + pxtalk->algo__crosstalk_compensation_x_plane_gradient_kcps = + pdev->xtalk_cfg.algo__crosstalk_compensation_x_plane_gradient_kcps; + pxtalk->algo__crosstalk_compensation_y_plane_gradient_kcps = + pdev->xtalk_cfg.algo__crosstalk_compensation_y_plane_gradient_kcps; + + + + LOG_FUNCTION_END(status); + + return status; + +} + + + + + + + +VL53L1_Error VL53L1_set_current_xtalk_settings( + VL53L1_DEV Dev, + VL53L1_xtalk_calibration_results_t *pxtalk + ) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->xtalk_cfg.algo__crosstalk_compensation_plane_offset_kcps = + pxtalk->algo__crosstalk_compensation_plane_offset_kcps; + pdev->xtalk_cfg.algo__crosstalk_compensation_x_plane_gradient_kcps = + pxtalk->algo__crosstalk_compensation_x_plane_gradient_kcps; + pdev->xtalk_cfg.algo__crosstalk_compensation_y_plane_gradient_kcps = + pxtalk->algo__crosstalk_compensation_y_plane_gradient_kcps; + + + + LOG_FUNCTION_END(status); + + return status; + +} + + + + diff --git a/drivers/input/misc/vl53L1/src/vl53l1_api_debug.c b/drivers/input/misc/vl53L1/src/vl53l1_api_debug.c new file mode 100644 index 000000000000..46f7f0daa5d3 --- /dev/null +++ b/drivers/input/misc/vl53L1/src/vl53l1_api_debug.c @@ -0,0 +1,3629 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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 "vl53l1_ll_def.h" +#include "vl53l1_ll_device.h" +#include "vl53l1_register_structs.h" +#include "vl53l1_hist_structs.h" +#include "vl53l1_nvm_structs.h" +#include "vl53l1_nvm.h" +#include "vl53l1_core.h" +#include "vl53l1_api_debug.h" + +#ifdef VL53L1_LOG_ENABLE +#include "vl53l1_nvm_debug.h" +#endif + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_CORE, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_CORE, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_CORE, status, \ + fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(trace_flags, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_decode_calibration_data_buffer( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_calibration_data_t *pdata) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (sizeof(VL53L1_calibration_data_t) > buf_size) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + memcpy(pdata, pbuffer, sizeof(VL53L1_calibration_data_t)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_nvm_debug_data( + VL53L1_DEV Dev, + VL53L1_decoded_nvm_data_t *pdata) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + status = VL53L1_read_nvm(Dev, 0, pdata); + +#ifdef VL53L1_LOG_ENABLE + if (status == VL53L1_ERROR_NONE) + VL53L1_print_decoded_nvm_data( + pdata, + "get_nvm_debug_data():pnvm_info.", + VL53L1_TRACE_MODULE_NVM_DATA); +#endif + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_histogram_debug_data( + VL53L1_DEV Dev, + VL53L1_histogram_bin_data_t *pdata) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + memcpy( + pdata, + &(pdev->hist_data), + sizeof(VL53L1_histogram_bin_data_t)); + + LOG_FUNCTION_END(status); + + return status; +} + + + + + +VL53L1_Error VL53L1_get_additional_data( + VL53L1_DEV Dev, + VL53L1_additional_data_t *pdata) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + + pdata->preset_mode = pdev->preset_mode; + pdata->zone_preset = pdev->zone_preset; + pdata->measurement_mode = pdev->measurement_mode; + pdata->offset_calibration_mode = pdev->offset_calibration_mode; + pdata->offset_correction_mode = pdev->offset_correction_mode; + pdata->dmax_mode = pdev->dmax_mode; + + pdata->phasecal_config_timeout_us = pdev->phasecal_config_timeout_us; + pdata->mm_config_timeout_us = pdev->mm_config_timeout_us; + pdata->range_config_timeout_us = pdev->range_config_timeout_us; + pdata->inter_measurement_period_ms = pdev->inter_measurement_period_ms; + pdata->dss_config__target_total_rate_mcps = + pdev->dss_config__target_total_rate_mcps; + + + + + status = + VL53L1_get_histogram_debug_data( + Dev, + &(pdata->VL53L1_p_010)); + + LOG_FUNCTION_END(status); + + return status; +} + + + + + +VL53L1_Error VL53L1_get_xtalk_debug_data( + VL53L1_DEV Dev, + VL53L1_xtalk_debug_data_t *pdata) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + memcpy( + &(pdata->customer), + &(pdev->customer), + sizeof(VL53L1_customer_nvm_managed_t)); + + memcpy( + &(pdata->xtalk_cfg), + &(pdev->xtalk_cfg), + sizeof(VL53L1_xtalk_config_t)); + + memcpy( + &(pdata->hist_data), + &(pdev->hist_data), + sizeof(VL53L1_histogram_bin_data_t)); + + memcpy( + &(pdata->xtalk_shapes), + &(pdev->xtalk_shapes), + sizeof(VL53L1_xtalk_histogram_data_t)); + + memcpy( + &(pdata->xtalk_results), + &(pdev->xtalk_results), + sizeof(VL53L1_xtalk_range_results_t)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_offset_debug_data( + VL53L1_DEV Dev, + VL53L1_offset_debug_data_t *pdata) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + memcpy( + &(pdata->customer), + &(pdev->customer), + sizeof(VL53L1_customer_nvm_managed_t)); + + memcpy( + &(pdata->fmt_dmax_cal), + &(pdev->fmt_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + memcpy( + &(pdata->cust_dmax_cal), + &(pdev->cust_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + memcpy( + &(pdata->add_off_cal_data), + &(pdev->add_off_cal_data), + sizeof(VL53L1_additional_offset_cal_data_t)); + + memcpy( + &(pdata->offset_results), + &(pdev->offset_results), + sizeof(VL53L1_offset_range_results_t)); + + LOG_FUNCTION_END(status); + + return status; +} + +#ifdef VL53L1_LOG_ENABLE + +void VL53L1_signed_fixed_point_sprintf( + int32_t signed_fp_value, + uint8_t frac_bits, + uint16_t buf_size, + char *pbuffer) +{ + + + + + + uint32_t fp_value = 0; + uint32_t unity_fp_value = 0; + uint32_t sign_bit = 0; + uint32_t int_part = 0; + uint32_t frac_part = 0; + uint32_t dec_points = 0; + uint32_t dec_scaler = 0; + uint32_t dec_part = 0; + + uint64_t tmp_long_int = 0; + + char fmt[VL53L1_MAX_STRING_LENGTH]; + + SUPPRESS_UNUSED_WARNING(buf_size); + + + + + sign_bit = signed_fp_value >> 31; + + if (sign_bit > 0) { + fp_value = 0x80000000 - + (0x7FFFFFFF & (uint32_t)signed_fp_value); + } else + fp_value = (uint32_t)signed_fp_value; + + int_part = fp_value >> frac_bits; + unity_fp_value = 0x01 << frac_bits; + frac_part = fp_value & (unity_fp_value-1); + + + + + + dec_points = 2; + dec_scaler = 100; + + while (dec_scaler < unity_fp_value) { + dec_points++; + dec_scaler *= 10; + } + + + + if (sign_bit > 0) + sprintf(fmt, "-%%u.%%0%uu", dec_points); + else + sprintf(fmt, "%%u.%%0%uu", dec_points); + + + + + + tmp_long_int = (uint64_t)frac_part * (uint64_t)dec_scaler; + tmp_long_int += (uint64_t)unity_fp_value/2; + + tmp_long_int = do_division_u(tmp_long_int, (uint64_t)unity_fp_value); + + dec_part = (uint32_t)tmp_long_int; + + + + sprintf( + pbuffer, + fmt, + int_part, + dec_part); +} + + +void VL53L1_print_static_nvm_managed( + VL53L1_static_nvm_managed_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = 0x%02X\n", + pprefix, + "i2c_slave__device_address", + pdata->i2c_slave__device_address); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ana_config__vhv_ref_sel_vddpix", + pdata->ana_config__vhv_ref_sel_vddpix); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ana_config__vhv_ref_sel_vquench", + pdata->ana_config__vhv_ref_sel_vquench); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ana_config__reg_avdd1v2_sel", + pdata->ana_config__reg_avdd1v2_sel); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ana_config__fast_osc__trim", + pdata->ana_config__fast_osc__trim); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->osc_measured__fast_osc__frequency, + 12, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "osc_measured__fast_osc__frequency", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "vhv_config__timeout_macrop_loop_bound", + pdata->vhv_config__timeout_macrop_loop_bound); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "vhv_config__count_thresh", + pdata->vhv_config__count_thresh); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "vhv_config__offset", + pdata->vhv_config__offset); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "vhv_config__init", + pdata->vhv_config__init); +} + + +void VL53L1_print_customer_nvm_managed( + VL53L1_customer_nvm_managed_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + int16_t tmpi16; + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_ref_0", + pdata->global_config__spad_enables_ref_0); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_ref_1", + pdata->global_config__spad_enables_ref_1); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_ref_2", + pdata->global_config__spad_enables_ref_2); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_ref_3", + pdata->global_config__spad_enables_ref_3); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_ref_4", + pdata->global_config__spad_enables_ref_4); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_ref_5", + pdata->global_config__spad_enables_ref_5); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__ref_en_start_select", + pdata->global_config__ref_en_start_select); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ref_spad_man__num_requested_ref_spads", + pdata->ref_spad_man__num_requested_ref_spads); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ref_spad_man__ref_location", + pdata->ref_spad_man__ref_location); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->algo__crosstalk_compensation_plane_offset_kcps, + 9, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_compensation_plane_offset_kcps", + fp_text); + + tmpi16 = pdata->algo__crosstalk_compensation_x_plane_gradient_kcps; + VL53L1_signed_fixed_point_sprintf( + (int32_t)tmpi16, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_compensation_x_plane_gradient_kcps", + fp_text); + + tmpi16 = pdata->algo__crosstalk_compensation_y_plane_gradient_kcps; + VL53L1_signed_fixed_point_sprintf( + (int32_t)tmpi16, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_compensation_y_plane_gradient_kcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->ref_spad_char__total_rate_target_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "ref_spad_char__total_rate_target_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->algo__part_to_part_range_offset_mm, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__part_to_part_range_offset_mm", + fp_text); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "mm_config__inner_offset_mm", + pdata->mm_config__inner_offset_mm); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "mm_config__outer_offset_mm", + pdata->mm_config__outer_offset_mm); +} + + +void VL53L1_print_nvm_copy_data( + VL53L1_nvm_copy_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "identification__model_id", + pdata->identification__model_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "identification__module_type", + pdata->identification__module_type); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "identification__revision_id", + pdata->identification__revision_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "identification__module_id", + pdata->identification__module_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ana_config__fast_osc__trim_max", + pdata->ana_config__fast_osc__trim_max); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ana_config__fast_osc__freq_set", + pdata->ana_config__fast_osc__freq_set); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ana_config__vcsel_trim", + pdata->ana_config__vcsel_trim); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ana_config__vcsel_selion", + pdata->ana_config__vcsel_selion); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ana_config__vcsel_selion_max", + pdata->ana_config__vcsel_selion_max); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "protected_laser_safety__lock_bit", + pdata->protected_laser_safety__lock_bit); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "laser_safety__key", + pdata->laser_safety__key); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "laser_safety__key_ro", + pdata->laser_safety__key_ro); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "laser_safety__clip", + pdata->laser_safety__clip); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "laser_safety__mult", + pdata->laser_safety__mult); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_0", + pdata->global_config__spad_enables_rtn_0); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_1", + pdata->global_config__spad_enables_rtn_1); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_2", + pdata->global_config__spad_enables_rtn_2); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_3", + pdata->global_config__spad_enables_rtn_3); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_4", + pdata->global_config__spad_enables_rtn_4); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_5", + pdata->global_config__spad_enables_rtn_5); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_6", + pdata->global_config__spad_enables_rtn_6); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_7", + pdata->global_config__spad_enables_rtn_7); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_8", + pdata->global_config__spad_enables_rtn_8); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_9", + pdata->global_config__spad_enables_rtn_9); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_10", + pdata->global_config__spad_enables_rtn_10); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_11", + pdata->global_config__spad_enables_rtn_11); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_12", + pdata->global_config__spad_enables_rtn_12); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_13", + pdata->global_config__spad_enables_rtn_13); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_14", + pdata->global_config__spad_enables_rtn_14); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_15", + pdata->global_config__spad_enables_rtn_15); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_16", + pdata->global_config__spad_enables_rtn_16); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_17", + pdata->global_config__spad_enables_rtn_17); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_18", + pdata->global_config__spad_enables_rtn_18); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_19", + pdata->global_config__spad_enables_rtn_19); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_20", + pdata->global_config__spad_enables_rtn_20); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_21", + pdata->global_config__spad_enables_rtn_21); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_22", + pdata->global_config__spad_enables_rtn_22); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_23", + pdata->global_config__spad_enables_rtn_23); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_24", + pdata->global_config__spad_enables_rtn_24); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_25", + pdata->global_config__spad_enables_rtn_25); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_26", + pdata->global_config__spad_enables_rtn_26); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_27", + pdata->global_config__spad_enables_rtn_27); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_28", + pdata->global_config__spad_enables_rtn_28); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_29", + pdata->global_config__spad_enables_rtn_29); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_30", + pdata->global_config__spad_enables_rtn_30); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_31", + pdata->global_config__spad_enables_rtn_31); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "roi_config__mode_roi_centre_spad", + pdata->roi_config__mode_roi_centre_spad); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = 0x%02X\n", + pprefix, + "roi_config__mode_roi_xy_size", + pdata->roi_config__mode_roi_xy_size); +} + + +void VL53L1_print_histogram_bin_data( + VL53L1_histogram_bin_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + uint8_t i = 0; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cfg_device_state", + pdata->cfg_device_state); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "rd_device_state", + pdata->rd_device_state); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_022", + pdata->VL53L1_p_022); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_023", + pdata->VL53L1_p_023); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_024", + pdata->VL53L1_p_024); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "number_of_ambient_bins", + pdata->number_of_ambient_bins); + + for (i = 0; i < VL53L1_MAX_BIN_SEQUENCE_LENGTH; i++) { + sprintf(ppre_text, "%sbin_seq[%u]", pprefix, i); + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->bin_seq[i]); + } + + for (i = 0; i < VL53L1_MAX_BIN_SEQUENCE_LENGTH; i++) { + sprintf(ppre_text, "%sbin_rep[%u]", pprefix, i); + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->bin_rep[i]); + } + + for (i = 0; i < pdata->VL53L1_p_024; i++) { + sprintf(ppre_text, "%sbin_data[%u]", pprefix, i); + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %d\n", + ppre_text, + pdata->bin_data[i]); + } + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "result__interrupt_status", + pdata->result__interrupt_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "result__range_status", + pdata->result__range_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "result__report_status", + pdata->result__report_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "result__stream_count", + pdata->result__stream_count); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__dss_actual_effective_spads, + 8, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "result__dss_actual_effective_spads", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->phasecal_result__reference_phase, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "phasecal_result__reference_phase", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "phasecal_result__vcsel_start", + pdata->phasecal_result__vcsel_start); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cal_config__vcsel_start", + pdata->cal_config__vcsel_start); + + VL53L1_signed_fixed_point_sprintf( + (uint32_t)pdata->vcsel_width, + 4, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "vcsel_width", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_009", + pdata->VL53L1_p_009); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_019, + 12, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_019", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "total_periods_elapsed", + pdata->total_periods_elapsed); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "peak_duration_us", + pdata->peak_duration_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "woi_duration_us", + pdata->woi_duration_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "min_bin_value", + pdata->min_bin_value); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "max_bin_value", + pdata->max_bin_value); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->zero_distance_phase, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "zero_distance_phase", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "number_of_ambient_samples", + pdata->number_of_ambient_samples); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "ambient_events_sum", + pdata->ambient_events_sum); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "VL53L1_p_004", + pdata->VL53L1_p_004); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = 0x%02X\n", + pprefix, + "roi_config__user_roi_centre_spad", + pdata->roi_config__user_roi_centre_spad); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = 0x%02X\n", + pprefix, + "roi_config__user_roi_requested_global_xy_size", + pdata->roi_config__user_roi_requested_global_xy_size); +} + + +void VL53L1_print_xtalk_histogram_shape_data( + VL53L1_xtalk_histogram_shape_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + uint8_t i = 0; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_022", + pdata->VL53L1_p_022); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_023", + pdata->VL53L1_p_023); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_024", + pdata->VL53L1_p_024); + + for (i = 0; i < pdata->VL53L1_p_024; i++) { + + sprintf(ppre_text, "%sbin_data[%u]", pprefix, i); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->bin_data[i], + 10, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %s\n", + ppre_text, + fp_text); + } + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->phasecal_result__reference_phase, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "phasecal_result__reference_phase", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "phasecal_result__vcsel_start", + pdata->phasecal_result__vcsel_start); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cal_config__vcsel_start", + pdata->cal_config__vcsel_start); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->vcsel_width, + 4, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "vcsel_width", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_019, + 12, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_019", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->zero_distance_phase, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "zero_distance_phase", + fp_text); +} + + +void VL53L1_print_xtalk_histogram_data( + VL53L1_xtalk_histogram_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + + + sprintf(ppre_text, "%sxtalk_shape.", pprefix); + VL53L1_print_xtalk_histogram_shape_data( + &(pdata->xtalk_shape), + ppre_text, trace_flags); + + + + sprintf(ppre_text, "%sxtalk_hist_removed.", pprefix); + VL53L1_print_histogram_bin_data( + &(pdata->xtalk_hist_removed), + ppre_text, trace_flags); +} + + +void VL53L1_print_range_data( + VL53L1_range_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "range_id", + pdata->range_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "time_stamp", + pdata->time_stamp); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_015", + pdata->VL53L1_p_015); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_022", + pdata->VL53L1_p_022); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_025", + pdata->VL53L1_p_025); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_026", + pdata->VL53L1_p_026); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_016", + pdata->VL53L1_p_016); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_027", + pdata->VL53L1_p_027); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->width, + 4, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "width", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_030", + pdata->VL53L1_p_030); + + + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->fast_osc_frequency, + 12, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "fast_osc_frequency", + fp_text); + + + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->zero_distance_phase, + 11, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "zero_distance_phase", + fp_text); + + + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_006, + 8, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "actual_effective_spad", + fp_text); + + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "total_periods_elapsed", + pdata->total_periods_elapsed); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "peak_duration_us", + pdata->peak_duration_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "woi_duration_us", + pdata->woi_duration_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "VL53L1_p_020", + pdata->VL53L1_p_020); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "VL53L1_p_021", + pdata->VL53L1_p_021); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "VL53L1_p_013", + pdata->VL53L1_p_013); + + + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->peak_signal_count_rate_mcps, + 7, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "peak_signal_count_rate_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->avg_signal_count_rate_mcps, + 7, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "avg_signal_count_rate_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->ambient_count_rate_mcps, + 7, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "ambient_count_rate_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->total_rate_per_spad_mcps, + 13, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "total_rate_per_spad_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_012, + 11, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_012", + fp_text); + + + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_005, + 2, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_005", + fp_text); + + + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_028, + 11, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_028", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_014, + 11, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_014", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_029, + 11, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_029", + fp_text); + + + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "min_range_mm", + pdata->min_range_mm); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "median_range_mm", + pdata->median_range_mm); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "max_range_mm", + pdata->max_range_mm); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "range_status", + pdata->range_status); +} + + +void VL53L1_print_range_results( + VL53L1_range_results_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + uint8_t i = 0; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cfg_device_state", + pdata->cfg_device_state); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "rd_device_state", + pdata->rd_device_state); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "zone_id", + pdata->zone_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "stream_count", + pdata->stream_count); + + for (i = 0; i < VL53L1_MAX_AMBIENT_DMAX_VALUES; i++) { + sprintf( + ppre_text, + "%sambient_dmax_mm[%u]", + pprefix, i); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->VL53L1_p_007[i]); + } + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "device_status", + pdata->device_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "wrap_dmax_mm", + pdata->wrap_dmax_mm); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "max_results", + pdata->max_results); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "active_results", + pdata->active_results); + + for (i = 0; i < pdata->active_results; i++) { + sprintf(ppre_text, "%sdata[%u].", pprefix, i); + VL53L1_print_range_data( + &pdata->VL53L1_p_002[i], + ppre_text, trace_flags); + } + + sprintf(ppre_text, "%sxmonitor.", pprefix); + VL53L1_print_range_data( + &pdata->xmonitor, + ppre_text, trace_flags); +} + + +void VL53L1_print_offset_range_results( + VL53L1_offset_range_results_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + uint8_t i = 0; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cal_distance_mm", + pdata->cal_distance_mm); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->cal_reflectance_pc, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "cal_reflectance_pc", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cal_status", + pdata->cal_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cal_report", + pdata->cal_report); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "max_results", + pdata->max_results); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "active_results", + pdata->active_results); + + for (i = 0; i < pdata->active_results; i++) { + sprintf(ppre_text, "%sdata[%u].", pprefix, i); + VL53L1_print_offset_range_data( + &(pdata->VL53L1_p_002[i]), + ppre_text, trace_flags); + } +} + + +void VL53L1_print_offset_range_data( + VL53L1_offset_range_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "preset_mode", + pdata->preset_mode); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "dss_config__roi_mode_control", + pdata->dss_config__roi_mode_control); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->dss_config__manual_effective_spads_select, + 8, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "dss_config__manual_effective_spads_select", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "no_of_samples", + pdata->no_of_samples); + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->effective_spads, + 8, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "effective_spads", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->peak_rate_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "peak_rate_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_005, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_005", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "median_range_mm", + pdata->median_range_mm); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "range_mm_offset", + pdata->range_mm_offset); +} + + +void VL53L1_print_cal_peak_rate_map( + VL53L1_cal_peak_rate_map_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + uint8_t i = 0; + uint8_t x = 0; + uint8_t y = 0; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->cal_distance_mm, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "cal_distance_mm", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->cal_reflectance_pc, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "cal_reflectance_pc", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "max_samples", + pdata->max_samples); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "width", + pdata->width); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "height", + pdata->height); + + i = 0; + for (y = 0; y < pdata->height; y++) { + for (x = 0; x < pdata->width; x++) { + + sprintf(ppre_text, "%speak_rate_mcps[%u]", pprefix, i); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->peak_rate_mcps[i], + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %s\n", + ppre_text, + fp_text); + + i++; + } + } +} + +void VL53L1_print_additional_data( + VL53L1_additional_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "preset_mode", + pdata->preset_mode); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "zone_preset", + pdata->zone_preset); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "measurement_mode", + pdata->measurement_mode); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "offset_calibration_mode", + pdata->offset_calibration_mode); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "offset_correction_mode", + pdata->offset_correction_mode); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "dmax_mode", + pdata->dmax_mode); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "phasecal_config_timeout_us", + pdata->phasecal_config_timeout_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "mm_config_timeout_us", + pdata->mm_config_timeout_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "range_config_timeout_us", + pdata->range_config_timeout_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "inter_measurement_period_ms", + pdata->inter_measurement_period_ms); + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->dss_config__target_total_rate_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "dss_config__target_total_rate_mcps", + fp_text); + + sprintf(ppre_text, "%s VL53L1_p_010.", pprefix); + VL53L1_print_histogram_bin_data( + &pdata->VL53L1_p_010, + ppre_text, trace_flags); + + +} + + +void VL53L1_print_additional_offset_cal_data( + VL53L1_additional_offset_cal_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__mm_inner_actual_effective_spads, + 8, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "result__mm_inner_actual_effective_spads", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__mm_outer_actual_effective_spads, + 8, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "result__mm_outer_actual_effective_spads", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__mm_inner_peak_signal_count_rtn_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "result__mm_inner_peak_signal_count_rtn_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__mm_outer_peak_signal_count_rtn_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "result__mm_outer_peak_signal_count_rtn_mcps", + fp_text); +} + + +void VL53L1_print_gain_calibration_data( + VL53L1_gain_calibration_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->standard_ranging_gain_factor, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "standard_ranging_gain_factor", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->histogram_ranging_gain_factor, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "histogram_ranging_gain_factor", + fp_text); +} + + +void VL53L1_print_zone_calibration_data( + VL53L1_zone_calibration_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "no_of_samples", + pdata->no_of_samples); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->effective_spads, + 8, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "effective_spads", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->peak_rate_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "peak_rate_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_014, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_014", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_005, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_005", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->median_range_mm, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "median_range_mm", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->range_mm_offset, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "range_mm_offset", + fp_text); +} + + +void VL53L1_print_zone_calibration_results( + VL53L1_zone_calibration_results_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + uint8_t i = 0; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "preset_mode", + pdata->preset_mode); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "zone_preset", + pdata->zone_preset); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cal_distance_mm", + pdata->cal_distance_mm); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->cal_reflectance_pc, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "cal_reflectance_pc", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->phasecal_result__reference_phase, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "phasecal_result__reference_phase", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->zero_distance_phase, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "zero_distance_phase", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cal_status", + pdata->cal_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "max_zones", + pdata->max_zones); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "active_zones", + pdata->active_zones); + + for (i = 0; i < pdata->active_zones; i++) { + sprintf(ppre_text, "%sdata[%u].", pprefix, i); + VL53L1_print_zone_calibration_data( + &(pdata->VL53L1_p_002[i]), + ppre_text, trace_flags); + } +} + +void VL53L1_print_xtalk_range_results( + VL53L1_xtalk_range_results_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + uint8_t i = 0; + + VL53L1_histogram_bin_data_t *pbin_data; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cal_status", + pdata->cal_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "num_of_samples_status", + pdata->num_of_samples_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "zero_samples_status", + pdata->zero_samples_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "max_sigma_status", + pdata->max_sigma_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "max_results", + pdata->max_results); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "active_results", + pdata->active_results); + + for (i = 0; i < pdata->active_results; i++) { + sprintf(ppre_text, "%sdata[%u].", pprefix, i); + VL53L1_print_xtalk_range_data( + &(pdata->VL53L1_p_002[i]), + ppre_text, trace_flags); + } + + sprintf(ppre_text, "%scentral_histogram_sum.", pprefix); + VL53L1_print_histogram_bin_data( + &pdata->central_histogram_sum, + ppre_text, trace_flags); + + sprintf(ppre_text, "%scentral_histogram_avg.", pprefix); + VL53L1_print_histogram_bin_data( + &pdata->central_histogram_avg, + ppre_text, trace_flags); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_015", + pdata->central_histogram__window_start); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_016", + pdata->central_histogram__window_end); + + pbin_data = &(pdata->histogram_avg_1[0]); + + for (i = 0; i < 5; i++) { + sprintf(ppre_text, "%shistogram_avg_1[%u].", pprefix, i); + VL53L1_print_histogram_bin_data( + pbin_data, + ppre_text, trace_flags); + pbin_data++; + } + + pbin_data = &(pdata->histogram_avg_2[0]); + + for (i = 0; i < 5; i++) { + sprintf(ppre_text, "%shistogram_avg_2[%u].", pprefix, i); + VL53L1_print_histogram_bin_data( + pbin_data, + ppre_text, trace_flags); + pbin_data++; + } + + pbin_data = &(pdata->xtalk_avg[0]); + + for (i = 0; i < 5; i++) { + sprintf(ppre_text, "%sxtalk_avg[%u].", pprefix, i); + VL53L1_print_histogram_bin_data( + pbin_data, + ppre_text, trace_flags); + pbin_data++; + } +} + + +void VL53L1_print_xtalk_range_data( + VL53L1_xtalk_range_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "no_of_samples", + pdata->no_of_samples); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "signal_total_events_sum", + pdata->signal_total_events_sum); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "signal_total_events_avg", + pdata->signal_total_events_avg); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->rate_per_spad_kcps_sum, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "rate_per_spad_kcps_sum", + fp_text); + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->rate_per_spad_kcps_avg, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "rate_per_spad_kcps_avg", + fp_text); +} + + +void VL53L1_print_xtalk_calibration_results( + VL53L1_xtalk_calibration_results_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + int16_t tmpi16; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->algo__crosstalk_compensation_plane_offset_kcps, + 9, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_compensation_plane_offset_kcps", + fp_text); + + tmpi16 = pdata->algo__crosstalk_compensation_x_plane_gradient_kcps; + VL53L1_signed_fixed_point_sprintf( + (int32_t)tmpi16, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_compensation_x_plane_gradient_kcps", + fp_text); + + tmpi16 = pdata->algo__crosstalk_compensation_y_plane_gradient_kcps; + VL53L1_signed_fixed_point_sprintf( + (int32_t)tmpi16, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_compensation_y_plane_gradient_kcps", + fp_text); +} + + +void VL53L1_print_xtalk_config( + VL53L1_xtalk_config_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + int16_t tmpi16; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->algo__crosstalk_compensation_plane_offset_kcps, + 9, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_compensation_plane_offset_kcps", + fp_text); + + tmpi16 = pdata->algo__crosstalk_compensation_x_plane_gradient_kcps; + VL53L1_signed_fixed_point_sprintf( + (int32_t)tmpi16, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_compensation_x_plane_gradient_kcps", + fp_text); + + tmpi16 = pdata->algo__crosstalk_compensation_y_plane_gradient_kcps; + VL53L1_signed_fixed_point_sprintf( + (int32_t)tmpi16, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_compensation_y_plane_gradient_kcps", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_crosstalk_compensation_enable", + pdata->global_crosstalk_compensation_enable); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->histogram_mode_crosstalk_margin_kcps, + 9, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "histogram_mode_crosstalk_margin_kcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->lite_mode_crosstalk_margin_kcps, + 9, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "lite_mode_crosstalk_margin_kcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->crosstalk_range_ignore_threshold_mult, + 5, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "crosstalk_range_ignore_threshold_mult", + fp_text); + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->crosstalk_range_ignore_threshold_rate_mcps, + 13, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "crosstalk_range_ignore_threshold_rate_mcps", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "algo__crosstalk_detect_max_valid_range_mm", + pdata->algo__crosstalk_detect_max_valid_range_mm); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "algo__crosstalk_detect_min_valid_range_mm", + pdata->algo__crosstalk_detect_min_valid_range_mm); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->algo__crosstalk_detect_max_valid_rate_kcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_detect_max_valid_rate_kcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->algo__crosstalk_detect_max_sigma_mm, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_detect_max_sigma_mm", + fp_text); + +} + + +void VL53L1_print_xtalk_extract_config( + VL53L1_xtalkextract_config_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->dss_config__target_total_rate_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "dss_config__target_total_rate_mcps", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "mm_config_timeout_us", + pdata->mm_config_timeout_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "range_config_timeout_us", + pdata->range_config_timeout_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "num_of_samples", + pdata->num_of_samples); + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "algo__crosstalk_extract_max_valid_range_mm", + pdata->algo__crosstalk_extract_max_valid_range_mm); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "algo__crosstalk_extract_min_valid_range_mm", + pdata->algo__crosstalk_extract_min_valid_range_mm); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->algo__crosstalk_extract_max_valid_rate_kcps, + 9, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_extract_max_valid_rate_kcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->algo__crosstalk_extract_max_sigma_mm, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_extract_max_sigma_mm", + fp_text); + +} + + +void VL53L1_print_zone_cal_config( + VL53L1_zonecal_config_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->dss_config__target_total_rate_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "dss_config__target_total_rate_mcps", + fp_text); + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "mm_config_timeout_us", + pdata->mm_config_timeout_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "range_config_timeout_us", + pdata->range_config_timeout_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "phasecal_config_timeout_us", + pdata->phasecal_config_timeout_us); + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "phasecal_num_of_samples", + pdata->phasecal_num_of_samples); + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "zone_num_of_samples", + pdata->zone_num_of_samples); + +} + +void VL53L1_print_offset_cal_config( + VL53L1_offsetcal_config_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->dss_config__target_total_rate_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "dss_config__target_total_rate_mcps", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "phasecal_config_timeout_us", + pdata->phasecal_config_timeout_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "range_config_timeout_us", + pdata->range_config_timeout_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "pre_num_of_samples", + pdata->pre_num_of_samples); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "mm1_num_of_samples", + pdata->mm1_num_of_samples); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "mm2_num_of_samples", + pdata->mm2_num_of_samples); + + +} + + +void VL53L1_print_dmax_calibration_data( + VL53L1_dmax_calibration_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->ref__actual_effective_spads, + 8, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "ref__actual_effective_spads", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->ref__peak_signal_count_rate_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "ref__peak_signal_count_rate_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->ref__distance_mm, + 4, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "ref__distance_mm", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->ref_reflectance_pc, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "ref_reflectance_pc", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->coverglass_transmission, + 8, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "coverglass_transmission", + fp_text); +} + + +void VL53L1_print_calibration_data( + VL53L1_calibration_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = 0x%08X\n", + pprefix, + "struct_version", + pdata->struct_version); + + sprintf(ppre_text, "%scustomer.", pprefix); + VL53L1_print_customer_nvm_managed( + &(pdata->customer), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sfmt_dmax_cal.", pprefix); + VL53L1_print_dmax_calibration_data( + &(pdata->fmt_dmax_cal), + ppre_text, trace_flags); + + sprintf(ppre_text, "%scust_dmax_cal.", pprefix); + VL53L1_print_dmax_calibration_data( + &(pdata->cust_dmax_cal), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sadd_off_cal_data.", pprefix); + VL53L1_print_additional_offset_cal_data( + &(pdata->add_off_cal_data), + ppre_text, trace_flags); + + sprintf(ppre_text, "%soptical_centre.", pprefix); + VL53L1_print_optical_centre( + &(pdata->optical_centre), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sxtalkhisto.", pprefix); + VL53L1_print_xtalk_histogram_data( + &(pdata->xtalkhisto), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sgain_cal.", pprefix); + VL53L1_print_gain_calibration_data( + &(pdata->gain_cal), + ppre_text, trace_flags); + + sprintf(ppre_text, "%scal_peak_rate_map.", pprefix); + VL53L1_print_cal_peak_rate_map( + &(pdata->cal_peak_rate_map), + ppre_text, trace_flags); +} + + +void VL53L1_print_xtalk_debug_data( + VL53L1_xtalk_debug_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + sprintf(ppre_text, "%scustomer.", pprefix); + VL53L1_print_customer_nvm_managed( + &(pdata->customer), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sxtalk_cfg.", pprefix); + VL53L1_print_xtalk_config( + &(pdata->xtalk_cfg), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sxtalk_extract_cfg.", pprefix); + VL53L1_print_xtalk_extract_config( + &(pdata->xtalk_extract_cfg), + ppre_text, trace_flags); + + sprintf(ppre_text, "%shist_data.", pprefix); + VL53L1_print_histogram_bin_data( + &(pdata->hist_data), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sxtalk_shapes.", pprefix); + VL53L1_print_xtalk_histogram_data( + &(pdata->xtalk_shapes), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sgain_cal.", pprefix); + VL53L1_print_xtalk_range_results( + &(pdata->xtalk_results), + ppre_text, trace_flags); +} + + +void VL53L1_print_offset_debug_data( + VL53L1_offset_debug_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + sprintf(ppre_text, "%scustomer.", pprefix); + VL53L1_print_customer_nvm_managed( + &(pdata->customer), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sfmt_dmax_cal.", pprefix); + VL53L1_print_dmax_calibration_data( + &(pdata->fmt_dmax_cal), + ppre_text, trace_flags); + + sprintf(ppre_text, "%scust_dmax_cal.", pprefix); + VL53L1_print_dmax_calibration_data( + &(pdata->cust_dmax_cal), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sadd_off_cal_data.", pprefix); + VL53L1_print_additional_offset_cal_data( + &(pdata->add_off_cal_data), + ppre_text, trace_flags); + + sprintf(ppre_text, "%soffset_results.", pprefix); + VL53L1_print_offset_range_results( + &(pdata->offset_results), + ppre_text, trace_flags); +} + + +void VL53L1_print_zone_config( + VL53L1_zone_config_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + uint8_t i = 0; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "max_zones", + pdata->max_zones); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "active_zones", + pdata->active_zones); + + for (i = 0; i < pdata->active_zones; i++) { + sprintf(ppre_text, "%suser_zones[%u].", pprefix, i); + VL53L1_print_user_zone( + &pdata->user_zones[i], + ppre_text, + trace_flags); + } +} + + +void VL53L1_print_optical_centre( + VL53L1_optical_centre_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->x_centre, + 4, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "x_centre", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->y_centre, + 4, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "y_centre", + fp_text); +} + + +void VL53L1_print_user_zone( + VL53L1_user_zone_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "x_centre", + pdata->x_centre); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "y_centre", + pdata->y_centre); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "width", + pdata->width); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "height", + pdata->height); +} + + +void VL53L1_print_spad_rate_data( + VL53L1_spad_rate_data_t *pspad_rates, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + + uint16_t spad_no = 0; + uint8_t row = 0; + uint8_t col = 0; + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%8s,%4s,%4s, %s\n", + pprefix, + "spad_no", + "row", + "col", + "peak_rate_mcps"); + + for (spad_no = 0; spad_no < pspad_rates->no_of_values; spad_no++) { + + + + VL53L1_decode_row_col( + (uint8_t)spad_no, + &row, + &col); + + + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pspad_rates->rate_data[spad_no], + pspad_rates->fractional_bits, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%8u,%4u,%4u, %s\n", + pprefix, + spad_no, + row, + col, + fp_text); + } +} + + +void VL53L1_print_spad_rate_map( + VL53L1_spad_rate_data_t *pspad_rates, + char *pprefix, + uint32_t trace_flags) +{ + + + + + + + uint8_t spad_no = 0; + uint8_t row = 0; + uint8_t col = 0; + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%4s", + pprefix, + " "); + + for (col = 0; col < VL53L1_SPAD_ARRAY_WIDTH; col++) + trace_print( + VL53L1_TRACE_LEVEL_INFO, + ",%8u", + col); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "\n"); + + + + + for (row = 0; row < VL53L1_SPAD_ARRAY_HEIGHT; row++) { + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%4u", + pprefix, + row); + + for (col = 0; col < VL53L1_SPAD_ARRAY_HEIGHT; col++) { + + + + + VL53L1_encode_row_col( + row, + col, + &spad_no); + + + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pspad_rates->rate_data[spad_no], + pspad_rates->fractional_bits, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + ",%8s", + fp_text); + } + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "\n"); + } +} + + +#endif + + + diff --git a/drivers/input/misc/vl53L1/src/vl53l1_api_preset_modes.c b/drivers/input/misc/vl53L1/src/vl53l1_api_preset_modes.c new file mode 100644 index 000000000000..e00f48830d2d --- /dev/null +++ b/drivers/input/misc/vl53L1/src/vl53l1_api_preset_modes.c @@ -0,0 +1,4927 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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 "vl53l1_ll_def.h" +#include "vl53l1_platform_log.h" +#include "vl53l1_register_structs.h" +#include "vl53l1_register_settings.h" +#include "vl53l1_hist_structs.h" +#include "vl53l1_zone_presets.h" +#include "vl53l1_core.h" +#include "vl53l1_api_preset_modes.h" +#include "vl53l1_tuning_parm_defaults.h" + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_API,\ + status, fmt, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_init_refspadchar_config_struct( + VL53L1_refspadchar_config_t *pdata) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + + + + + + + pdata->device_test_mode = + VL53L1_TUNINGPARM_REFSPADCHAR_DEVICE_TEST_MODE_DEFAULT; + pdata->VL53L1_p_009 = + VL53L1_TUNINGPARM_REFSPADCHAR_VCSEL_PERIOD_DEFAULT; + pdata->timeout_us = + VL53L1_TUNINGPARM_REFSPADCHAR_PHASECAL_TIMEOUT_US_DEFAULT; + pdata->target_count_rate_mcps = + VL53L1_TUNINGPARM_REFSPADCHAR_TARGET_COUNT_RATE_MCPS_DEFAULT; + pdata->min_count_rate_limit_mcps = + VL53L1_TUNINGPARM_REFSPADCHAR_MIN_COUNTRATE_LIMIT_MCPS_DEFAULT; + pdata->max_count_rate_limit_mcps = + VL53L1_TUNINGPARM_REFSPADCHAR_MAX_COUNTRATE_LIMIT_MCPS_DEFAULT; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_init_ssc_config_struct( + VL53L1_ssc_config_t *pdata) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + + + + pdata->array_select = VL53L1_DEVICESSCARRAY_RTN; + + + + pdata->VL53L1_p_009 = + VL53L1_TUNINGPARM_SPADMAP_VCSEL_PERIOD_DEFAULT; + + + + pdata->vcsel_start = + VL53L1_TUNINGPARM_SPADMAP_VCSEL_START_DEFAULT; + + + + pdata->vcsel_width = 0x02; + + + + pdata->timeout_us = 36000; + + + + + + + pdata->rate_limit_mcps = + VL53L1_TUNINGPARM_SPADMAP_RATE_LIMIT_MCPS_DEFAULT; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_init_xtalk_config_struct( + VL53L1_customer_nvm_managed_t *pnvm, + VL53L1_xtalk_config_t *pdata) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + + + + + + + + + + + + pdata->algo__crosstalk_compensation_plane_offset_kcps = + pnvm->algo__crosstalk_compensation_plane_offset_kcps; + pdata->algo__crosstalk_compensation_x_plane_gradient_kcps = + pnvm->algo__crosstalk_compensation_x_plane_gradient_kcps; + pdata->algo__crosstalk_compensation_y_plane_gradient_kcps = + pnvm->algo__crosstalk_compensation_y_plane_gradient_kcps; + + + + + pdata->nvm_default__crosstalk_compensation_plane_offset_kcps = + (uint32_t)pnvm->algo__crosstalk_compensation_plane_offset_kcps; + pdata->nvm_default__crosstalk_compensation_x_plane_gradient_kcps = + pnvm->algo__crosstalk_compensation_x_plane_gradient_kcps; + pdata->nvm_default__crosstalk_compensation_y_plane_gradient_kcps = + pnvm->algo__crosstalk_compensation_y_plane_gradient_kcps; + + pdata->histogram_mode_crosstalk_margin_kcps = + VL53L1_TUNINGPARM_HIST_XTALK_MARGIN_KCPS_DEFAULT; + pdata->lite_mode_crosstalk_margin_kcps = + VL53L1_TUNINGPARM_LITE_XTALK_MARGIN_KCPS_DEFAULT; + + + + + pdata->crosstalk_range_ignore_threshold_mult = + VL53L1_TUNINGPARM_LITE_RIT_MULT_DEFAULT; + + if ((pdata->algo__crosstalk_compensation_plane_offset_kcps == 0x00) + && (pdata->algo__crosstalk_compensation_x_plane_gradient_kcps + == 0x00) + && (pdata->algo__crosstalk_compensation_y_plane_gradient_kcps + == 0x00)) + pdata->global_crosstalk_compensation_enable = 0x00; + else + pdata->global_crosstalk_compensation_enable = 0x01; + + + if ((status == VL53L1_ERROR_NONE) && + (pdata->global_crosstalk_compensation_enable == 0x01)) { + pdata->crosstalk_range_ignore_threshold_rate_mcps = + VL53L1_calc_range_ignore_threshold( + pdata->algo__crosstalk_compensation_plane_offset_kcps, + pdata->algo__crosstalk_compensation_x_plane_gradient_kcps, + pdata->algo__crosstalk_compensation_y_plane_gradient_kcps, + pdata->crosstalk_range_ignore_threshold_mult); + } else { + pdata->crosstalk_range_ignore_threshold_rate_mcps = 0; + } + + + + + + + + pdata->algo__crosstalk_detect_min_valid_range_mm = + VL53L1_TUNINGPARM_XTALK_DETECT_MIN_VALID_RANGE_MM_DEFAULT; + pdata->algo__crosstalk_detect_max_valid_range_mm = + VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RANGE_MM_DEFAULT; + pdata->algo__crosstalk_detect_max_valid_rate_kcps = + VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RATE_KCPS_DEFAULT; + pdata->algo__crosstalk_detect_max_sigma_mm = + VL53L1_TUNINGPARM_XTALK_DETECT_MAX_SIGMA_MM_DEFAULT; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_init_xtalk_extract_config_struct( + VL53L1_xtalkextract_config_t *pdata) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + pdata->dss_config__target_total_rate_mcps = + VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_RATE_MCPS_DEFAULT; + + + pdata->mm_config_timeout_us = + VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_TIMEOUT_US_DEFAULT; + + + pdata->num_of_samples = + VL53L1_TUNINGPARM_XTALK_EXTRACT_NUM_OF_SAMPLES_DEFAULT; + + pdata->phasecal_config_timeout_us = + VL53L1_TUNINGPARM_XTALK_EXTRACT_PHASECAL_TIMEOUT_US_DEFAULT; + + + pdata->range_config_timeout_us = + VL53L1_TUNINGPARM_XTALK_EXTRACT_BIN_TIMEOUT_US_DEFAULT; + + + + + + + pdata->algo__crosstalk_extract_min_valid_range_mm = + VL53L1_TUNINGPARM_XTALK_EXTRACT_MIN_FILTER_THRESH_MM_DEFAULT; + pdata->algo__crosstalk_extract_max_valid_range_mm = + VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_FILTER_THRESH_MM_DEFAULT; + pdata->algo__crosstalk_extract_max_valid_rate_kcps = + VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_VALID_RATE_KCPS_DEFAULT; + pdata->algo__crosstalk_extract_max_sigma_mm = + VL53L1_TUNINGPARM_XTALK_EXTRACT_SIGMA_THRESHOLD_MM_DEFAULT; + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_init_offset_cal_config_struct( + VL53L1_offsetcal_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + pdata->dss_config__target_total_rate_mcps = + VL53L1_TUNINGPARM_OFFSET_CAL_DSS_RATE_MCPS_DEFAULT; + + + pdata->phasecal_config_timeout_us = + VL53L1_TUNINGPARM_OFFSET_CAL_PHASECAL_TIMEOUT_US_DEFAULT; + + + pdata->range_config_timeout_us = + VL53L1_TUNINGPARM_OFFSET_CAL_RANGE_TIMEOUT_US_DEFAULT; + + + pdata->mm_config_timeout_us = + VL53L1_TUNINGPARM_OFFSET_CAL_MM_TIMEOUT_US_DEFAULT; + + + + + + + pdata->pre_num_of_samples = + VL53L1_TUNINGPARM_OFFSET_CAL_PRE_SAMPLES_DEFAULT; + pdata->mm1_num_of_samples = + VL53L1_TUNINGPARM_OFFSET_CAL_MM1_SAMPLES_DEFAULT; + pdata->mm2_num_of_samples = + VL53L1_TUNINGPARM_OFFSET_CAL_MM2_SAMPLES_DEFAULT; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_init_zone_cal_config_struct( + VL53L1_zonecal_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + pdata->dss_config__target_total_rate_mcps = + VL53L1_TUNINGPARM_ZONE_CAL_DSS_RATE_MCPS_DEFAULT; + + + pdata->phasecal_config_timeout_us = + VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_TIMEOUT_US_DEFAULT; + + + pdata->range_config_timeout_us = + VL53L1_TUNINGPARM_ZONE_CAL_RANGE_TIMEOUT_US_DEFAULT; + + + pdata->mm_config_timeout_us = + VL53L1_TUNINGPARM_ZONE_CAL_DSS_TIMEOUT_US_DEFAULT; + + + + + + + pdata->phasecal_num_of_samples = + VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_NUM_SAMPLES_DEFAULT; + pdata->zone_num_of_samples = + VL53L1_TUNINGPARM_ZONE_CAL_ZONE_NUM_SAMPLES_DEFAULT; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_init_hist_post_process_config_struct( + uint8_t xtalk_compensation_enable, + VL53L1_hist_post_process_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + + pdata->hist_algo_select = + VL53L1_TUNINGPARM_HIST_ALGO_SELECT_DEFAULT; + + + + + pdata->hist_target_order = + VL53L1_TUNINGPARM_HIST_TARGET_ORDER_DEFAULT; + + + + + pdata->filter_woi0 = + VL53L1_TUNINGPARM_HIST_FILTER_WOI_0_DEFAULT; + + pdata->filter_woi1 = + VL53L1_TUNINGPARM_HIST_FILTER_WOI_1_DEFAULT; + + + + + pdata->hist_amb_est_method = + VL53L1_TUNINGPARM_HIST_AMB_EST_METHOD_DEFAULT; + + pdata->ambient_thresh_sigma0 = + VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_0_DEFAULT; + + pdata->ambient_thresh_sigma1 = + VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_1_DEFAULT; + + + + + pdata->ambient_thresh_events_scaler = + VL53L1_TUNINGPARM_HIST_AMB_EVENTS_SCALER_DEFAULT; + + + + pdata->min_ambient_thresh_events = + VL53L1_TUNINGPARM_HIST_MIN_AMB_THRESH_EVENTS_DEFAULT; + + + pdata->noise_threshold = + VL53L1_TUNINGPARM_HIST_NOISE_THRESHOLD_DEFAULT; + + + pdata->signal_total_events_limit = + VL53L1_TUNINGPARM_HIST_SIGNAL_TOTAL_EVENTS_LIMIT_DEFAULT; + + pdata->sigma_estimator__sigma_ref_mm = + VL53L1_TUNINGPARM_HIST_SIGMA_EST_REF_MM_DEFAULT; + + + + + + + + + + + + + + + pdata->sigma_thresh = + VL53L1_TUNINGPARM_HIST_SIGMA_THRESH_MM_DEFAULT; + + pdata->range_offset_mm = 0; + + + pdata->gain_factor = + VL53L1_TUNINGPARM_HIST_GAIN_FACTOR_DEFAULT; + + + + + + + + + + + + + + + + pdata->valid_phase_low = 0x08; + pdata->valid_phase_high = 0x88; + + + + + + + + + + pdata->algo__consistency_check__phase_tolerance = + VL53L1_TUNINGPARM_CONSISTENCY_HIST_PHASE_TOLERANCE_DEFAULT; + + + + + + + + + + pdata->algo__consistency_check__event_sigma = + VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA_DEFAULT; + + + + pdata->algo__consistency_check__event_min_spad_count = + VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA_MIN_SPAD_LIMIT_DEFAULT; + + + + + + pdata->algo__consistency_check__min_max_tolerance = + VL53L1_TUNINGPARM_CONSISTENCY_HIST_MIN_MAX_TOLERANCE_MM_DEFAULT; + + + + pdata->algo__crosstalk_compensation_enable = xtalk_compensation_enable; + + + + + + + + + + + + pdata->algo__crosstalk_detect_min_valid_range_mm = + VL53L1_TUNINGPARM_XTALK_DETECT_MIN_VALID_RANGE_MM_DEFAULT; + pdata->algo__crosstalk_detect_max_valid_range_mm = + VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RANGE_MM_DEFAULT; + pdata->algo__crosstalk_detect_max_valid_rate_kcps = + VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RATE_KCPS_DEFAULT; + pdata->algo__crosstalk_detect_max_sigma_mm = + VL53L1_TUNINGPARM_XTALK_DETECT_MAX_SIGMA_MM_DEFAULT; + + + + + + + + + + + + + + pdata->algo__crosstalk_detect_event_sigma = + VL53L1_TUNINGPARM_XTALK_DETECT_EVENT_SIGMA_DEFAULT; + + + + + + pdata->algo__crosstalk_detect_min_max_tolerance = + VL53L1_TUNINGPARM_XTALK_DETECT_MIN_MAX_TOLERANCE_DEFAULT; + + + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_init_dmax_calibration_data_struct( + VL53L1_dmax_calibration_data_t *pdata) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + + pdata->ref__actual_effective_spads = 0x5F2D; + + + pdata->ref__peak_signal_count_rate_mcps = 0x0844; + + + pdata->ref__distance_mm = 0x08A5; + + + + + pdata->ref_reflectance_pc = 0x0014; + + + + pdata->coverglass_transmission = 0x0100; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_init_tuning_parm_storage_struct( + VL53L1_tuning_parm_storage_t *pdata) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + + + + pdata->tp_tuning_parm_version = + VL53L1_TUNINGPARM_VERSION_DEFAULT; + pdata->tp_tuning_parm_key_table_version = + VL53L1_TUNINGPARM_KEY_TABLE_VERSION_DEFAULT; + pdata->tp_tuning_parm_lld_version = + VL53L1_TUNINGPARM_LLD_VERSION_DEFAULT; + pdata->tp_init_phase_rtn_lite_long = + VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_LONG_RANGE_DEFAULT; + pdata->tp_init_phase_rtn_lite_med = + VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_MED_RANGE_DEFAULT; + pdata->tp_init_phase_rtn_lite_short = + VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_SHORT_RANGE_DEFAULT; + pdata->tp_init_phase_ref_lite_long = + VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_LONG_RANGE_DEFAULT; + pdata->tp_init_phase_ref_lite_med = + VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_MED_RANGE_DEFAULT; + pdata->tp_init_phase_ref_lite_short = + VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_SHORT_RANGE_DEFAULT; + pdata->tp_init_phase_rtn_hist_long = + VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_LONG_RANGE_DEFAULT; + pdata->tp_init_phase_rtn_hist_med = + VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_MED_RANGE_DEFAULT; + pdata->tp_init_phase_rtn_hist_short = + VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_SHORT_RANGE_DEFAULT; + pdata->tp_init_phase_ref_hist_long = + VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_LONG_RANGE_DEFAULT; + pdata->tp_init_phase_ref_hist_med = + VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_MED_RANGE_DEFAULT; + pdata->tp_init_phase_ref_hist_short = + VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_SHORT_RANGE_DEFAULT; + pdata->tp_consistency_lite_phase_tolerance = + VL53L1_TUNINGPARM_CONSISTENCY_LITE_PHASE_TOLERANCE_DEFAULT; + pdata->tp_phasecal_target = + VL53L1_TUNINGPARM_PHASECAL_TARGET_DEFAULT; + pdata->tp_cal_repeat_rate = + VL53L1_TUNINGPARM_LITE_CAL_REPEAT_RATE_DEFAULT; + pdata->tp_lite_min_clip = + VL53L1_TUNINGPARM_LITE_MIN_CLIP_MM_DEFAULT; + pdata->tp_lite_long_sigma_thresh_mm = + VL53L1_TUNINGPARM_LITE_LONG_SIGMA_THRESH_MM_DEFAULT; + pdata->tp_lite_med_sigma_thresh_mm = + VL53L1_TUNINGPARM_LITE_MED_SIGMA_THRESH_MM_DEFAULT; + pdata->tp_lite_short_sigma_thresh_mm = + VL53L1_TUNINGPARM_LITE_SHORT_SIGMA_THRESH_MM_DEFAULT; + pdata->tp_lite_long_min_count_rate_rtn_mcps = + VL53L1_TUNINGPARM_LITE_LONG_MIN_COUNT_RATE_RTN_MCPS_DEFAULT; + pdata->tp_lite_med_min_count_rate_rtn_mcps = + VL53L1_TUNINGPARM_LITE_MED_MIN_COUNT_RATE_RTN_MCPS_DEFAULT; + pdata->tp_lite_short_min_count_rate_rtn_mcps = + VL53L1_TUNINGPARM_LITE_SHORT_MIN_COUNT_RATE_RTN_MCPS_DEFAULT; + pdata->tp_lite_sigma_est_pulse_width_ns = + VL53L1_TUNINGPARM_LITE_SIGMA_EST_PULSE_WIDTH_DEFAULT; + pdata->tp_lite_sigma_est_amb_width_ns = + VL53L1_TUNINGPARM_LITE_SIGMA_EST_AMB_WIDTH_NS_DEFAULT; + pdata->tp_lite_sigma_ref_mm = + VL53L1_TUNINGPARM_LITE_SIGMA_REF_MM_DEFAULT; + pdata->tp_lite_seed_cfg = + VL53L1_TUNINGPARM_LITE_SEED_CONFIG_DEFAULT; + pdata->tp_timed_seed_cfg = + VL53L1_TUNINGPARM_TIMED_SEED_CONFIG_DEFAULT; + pdata->tp_lite_quantifier = + VL53L1_TUNINGPARM_LITE_QUANTIFIER_DEFAULT; + pdata->tp_lite_first_order_select = + VL53L1_TUNINGPARM_LITE_FIRST_ORDER_SELECT_DEFAULT; + + + + + + + pdata->tp_dss_target_lite_mcps = + VL53L1_TUNINGPARM_LITE_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS_DEFAULT; + pdata->tp_dss_target_histo_mcps = + VL53L1_TUNINGPARM_RANGING_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS_DEFAULT; + pdata->tp_dss_target_histo_mz_mcps = + VL53L1_TUNINGPARM_MZ_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS_DEFAULT; + pdata->tp_dss_target_timed_mcps = + VL53L1_TUNINGPARM_TIMED_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS_DEFAULT; + pdata->tp_phasecal_timeout_lite_us = + VL53L1_TUNINGPARM_LITE_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_phasecal_timeout_hist_long_us = + VL53L1_TUNINGPARM_RANGING_LONG_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_phasecal_timeout_hist_med_us = + VL53L1_TUNINGPARM_RANGING_MED_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_phasecal_timeout_hist_short_us = + VL53L1_TUNINGPARM_RANGING_SHORT_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_phasecal_timeout_mz_long_us = + VL53L1_TUNINGPARM_MZ_LONG_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_phasecal_timeout_mz_med_us = + VL53L1_TUNINGPARM_MZ_MED_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_phasecal_timeout_mz_short_us = + VL53L1_TUNINGPARM_MZ_SHORT_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_phasecal_timeout_timed_us = + VL53L1_TUNINGPARM_TIMED_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_mm_timeout_lite_us = + VL53L1_TUNINGPARM_LITE_MM_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_mm_timeout_histo_us = + VL53L1_TUNINGPARM_RANGING_MM_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_mm_timeout_mz_us = + VL53L1_TUNINGPARM_MZ_MM_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_mm_timeout_timed_us = + VL53L1_TUNINGPARM_TIMED_MM_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_range_timeout_lite_us = + VL53L1_TUNINGPARM_LITE_RANGE_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_range_timeout_histo_us = + VL53L1_TUNINGPARM_RANGING_RANGE_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_range_timeout_mz_us = + VL53L1_TUNINGPARM_MZ_RANGE_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_range_timeout_timed_us = + VL53L1_TUNINGPARM_TIMED_RANGE_CONFIG_TIMEOUT_US_DEFAULT; + + + + + pdata->tp_mm_timeout_lpa_us = + VL53L1_TUNINGPARM_LOWPOWERAUTO_MM_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_range_timeout_lpa_us = + VL53L1_TUNINGPARM_LOWPOWERAUTO_RANGE_CONFIG_TIMEOUT_US_DEFAULT; + + pdata->tp_dss_target_very_short_mcps = + VL53L1_TUNINGPARM_VERY_SHORT_DSS_RATE_MCPS_DEFAULT; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_init_hist_gen3_dmax_config_struct( + VL53L1_hist_gen3_dmax_config_t *pdata) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + pdata->dss_config__target_total_rate_mcps = 0x1400; + pdata->dss_config__aperture_attenuation = 0x38; + + pdata->signal_thresh_sigma = + VL53L1_TUNINGPARM_DMAX_CFG_SIGNAL_THRESH_SIGMA_DEFAULT; + pdata->ambient_thresh_sigma = 0x70; + pdata->min_ambient_thresh_events = 16; + pdata->signal_total_events_limit = 100; + pdata->max_effective_spads = 0xFFFF; + + + + + + + + + + + + pdata->target_reflectance_for_dmax_calc[0] = + VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_0_DEFAULT; + pdata->target_reflectance_for_dmax_calc[1] = + VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_1_DEFAULT; + pdata->target_reflectance_for_dmax_calc[2] = + VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_2_DEFAULT; + pdata->target_reflectance_for_dmax_calc[3] = + VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_3_DEFAULT; + pdata->target_reflectance_for_dmax_calc[4] = + VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_4_DEFAULT; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_standard_ranging( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + + pstatic->dss_config__target_total_rate_mcps = 0x0A00; + pstatic->debug__ctrl = 0x00; + pstatic->test_mode__ctrl = 0x00; + pstatic->clk_gating__ctrl = 0x00; + pstatic->nvm_bist__ctrl = 0x00; + pstatic->nvm_bist__num_nvm_words = 0x00; + pstatic->nvm_bist__start_address = 0x00; + pstatic->host_if__status = 0x00; + pstatic->pad_i2c_hv__config = 0x00; + pstatic->pad_i2c_hv__extsup_config = 0x00; + + + + + + + pstatic->gpio_hv_pad__ctrl = 0x00; + + + + + + + + + pstatic->gpio_hv_mux__ctrl = + VL53L1_DEVICEINTERRUPTPOLARITY_ACTIVE_LOW | + VL53L1_DEVICEGPIOMODE_OUTPUT_RANGE_AND_ERROR_INTERRUPTS; + + pstatic->gpio__tio_hv_status = 0x02; + pstatic->gpio__fio_hv_status = 0x00; + pstatic->ana_config__spad_sel_pswidth = 0x02; + pstatic->ana_config__vcsel_pulse_width_offset = 0x08; + pstatic->ana_config__fast_osc__config_ctrl = 0x00; + + pstatic->sigma_estimator__effective_pulse_width_ns = + ptuning_parms->tp_lite_sigma_est_pulse_width_ns; + pstatic->sigma_estimator__effective_ambient_width_ns = + ptuning_parms->tp_lite_sigma_est_amb_width_ns; + pstatic->sigma_estimator__sigma_ref_mm = + ptuning_parms->tp_lite_sigma_ref_mm; + + + pstatic->algo__crosstalk_compensation_valid_height_mm = 0x01; + pstatic->spare_host_config__static_config_spare_0 = 0x00; + pstatic->spare_host_config__static_config_spare_1 = 0x00; + + pstatic->algo__range_ignore_threshold_mcps = 0x0000; + + + + pstatic->algo__range_ignore_valid_height_mm = 0xff; + pstatic->algo__range_min_clip = + ptuning_parms->tp_lite_min_clip; + + + + + + + pstatic->algo__consistency_check__tolerance = + ptuning_parms->tp_consistency_lite_phase_tolerance; + pstatic->spare_host_config__static_config_spare_2 = 0x00; + pstatic->sd_config__reset_stages_msb = 0x00; + pstatic->sd_config__reset_stages_lsb = 0x00; + + pgeneral->gph_config__stream_count_update_value = 0x00; + pgeneral->global_config__stream_divider = 0x00; + pgeneral->system__interrupt_config_gpio = + VL53L1_INTERRUPT_CONFIG_NEW_SAMPLE_READY; + pgeneral->cal_config__vcsel_start = 0x0B; + + + + + + + + + pgeneral->cal_config__repeat_rate = + ptuning_parms->tp_cal_repeat_rate; + pgeneral->global_config__vcsel_width = 0x02; + + + pgeneral->phasecal_config__timeout_macrop = 0x0D; + + + pgeneral->phasecal_config__target = + ptuning_parms->tp_phasecal_target; + pgeneral->phasecal_config__override = 0x00; + pgeneral->dss_config__roi_mode_control = + VL53L1_DEVICEDSSMODE__TARGET_RATE; + + + pgeneral->system__thresh_rate_high = 0x0000; + pgeneral->system__thresh_rate_low = 0x0000; + + + pgeneral->dss_config__manual_effective_spads_select = 0x8C00; + pgeneral->dss_config__manual_block_select = 0x00; + + + + + + + + + pgeneral->dss_config__aperture_attenuation = 0x38; + pgeneral->dss_config__max_spads_limit = 0xFF; + pgeneral->dss_config__min_spads_limit = 0x01; + + + + + + + ptiming->mm_config__timeout_macrop_a_hi = 0x00; + ptiming->mm_config__timeout_macrop_a_lo = 0x1a; + ptiming->mm_config__timeout_macrop_b_hi = 0x00; + ptiming->mm_config__timeout_macrop_b_lo = 0x20; + + + ptiming->range_config__timeout_macrop_a_hi = 0x01; + ptiming->range_config__timeout_macrop_a_lo = 0xCC; + + + ptiming->range_config__vcsel_period_a = 0x0B; + + + ptiming->range_config__timeout_macrop_b_hi = 0x01; + ptiming->range_config__timeout_macrop_b_lo = 0xF5; + + + ptiming->range_config__vcsel_period_b = 0x09; + + + + + + + + ptiming->range_config__sigma_thresh = + ptuning_parms->tp_lite_med_sigma_thresh_mm; + + + + + + + ptiming->range_config__min_count_rate_rtn_limit_mcps = + ptuning_parms->tp_lite_med_min_count_rate_rtn_mcps; + + + + + + + ptiming->range_config__valid_phase_low = 0x08; + ptiming->range_config__valid_phase_high = 0x78; + ptiming->system__intermeasurement_period = 0x00000000; + ptiming->system__fractional_enable = 0x00; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + phistogram->histogram_config__low_amb_even_bin_0_1 = 0x07; + phistogram->histogram_config__low_amb_even_bin_2_3 = 0x21; + phistogram->histogram_config__low_amb_even_bin_4_5 = 0x43; + + phistogram->histogram_config__low_amb_odd_bin_0_1 = 0x10; + phistogram->histogram_config__low_amb_odd_bin_2_3 = 0x32; + phistogram->histogram_config__low_amb_odd_bin_4_5 = 0x54; + + phistogram->histogram_config__mid_amb_even_bin_0_1 = 0x07; + phistogram->histogram_config__mid_amb_even_bin_2_3 = 0x21; + phistogram->histogram_config__mid_amb_even_bin_4_5 = 0x43; + + phistogram->histogram_config__mid_amb_odd_bin_0_1 = 0x10; + phistogram->histogram_config__mid_amb_odd_bin_2 = 0x02; + phistogram->histogram_config__mid_amb_odd_bin_3_4 = 0x43; + phistogram->histogram_config__mid_amb_odd_bin_5 = 0x05; + + phistogram->histogram_config__user_bin_offset = 0x00; + + phistogram->histogram_config__high_amb_even_bin_0_1 = 0x07; + phistogram->histogram_config__high_amb_even_bin_2_3 = 0x21; + phistogram->histogram_config__high_amb_even_bin_4_5 = 0x43; + + phistogram->histogram_config__high_amb_odd_bin_0_1 = 0x10; + phistogram->histogram_config__high_amb_odd_bin_2_3 = 0x32; + phistogram->histogram_config__high_amb_odd_bin_4_5 = 0x54; + + phistogram->histogram_config__amb_thresh_low = 0xFFFF; + phistogram->histogram_config__amb_thresh_high = 0xFFFF; + + phistogram->histogram_config__spad_array_selection = 0x00; + + + + + + + + + pzone_cfg->max_zones = VL53L1_MAX_USER_ZONES; + pzone_cfg->active_zones = 0x00; + pzone_cfg->user_zones[0].height = 0x0f; + pzone_cfg->user_zones[0].width = 0x0f; + pzone_cfg->user_zones[0].x_centre = 0x08; + pzone_cfg->user_zones[0].y_centre = 0x08; + + + + + pdynamic->system__grouped_parameter_hold_0 = 0x01; + + pdynamic->system__thresh_high = 0x0000; + pdynamic->system__thresh_low = 0x0000; + pdynamic->system__enable_xtalk_per_quadrant = 0x00; + pdynamic->system__seed_config = + ptuning_parms->tp_lite_seed_cfg; + + + + pdynamic->sd_config__woi_sd0 = 0x0B; + + + pdynamic->sd_config__woi_sd1 = 0x09; + + pdynamic->sd_config__initial_phase_sd0 = + ptuning_parms->tp_init_phase_rtn_lite_med; + pdynamic->sd_config__initial_phase_sd1 = + ptuning_parms->tp_init_phase_ref_lite_med; + + pdynamic->system__grouped_parameter_hold_1 = 0x01; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pdynamic->sd_config__first_order_select = + ptuning_parms->tp_lite_first_order_select; + pdynamic->sd_config__quantifier = + ptuning_parms->tp_lite_quantifier; + + + + + + + pdynamic->roi_config__user_roi_centre_spad = 0xC7; + + + pdynamic->roi_config__user_roi_requested_global_xy_size = 0xFF; + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM2_EN | + VL53L1_SEQUENCE_RANGE_EN; + + pdynamic->system__grouped_parameter_hold = 0x02; + + + + + + psystem->system__stream_count_ctrl = 0x00; + psystem->firmware__enable = 0x01; + psystem->system__interrupt_clear = + VL53L1_CLEAR_RANGE_INT; + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_STREAMING | + VL53L1_DEVICEREADOUTMODE_SINGLE_SD | + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_standard_ranging_short_range( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + + status = VL53L1_preset_mode_standard_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + + + + + + + ptiming->range_config__vcsel_period_a = 0x07; + ptiming->range_config__vcsel_period_b = 0x05; + ptiming->range_config__sigma_thresh = + ptuning_parms->tp_lite_short_sigma_thresh_mm; + ptiming->range_config__min_count_rate_rtn_limit_mcps = + ptuning_parms->tp_lite_short_min_count_rate_rtn_mcps; + ptiming->range_config__valid_phase_low = 0x08; + ptiming->range_config__valid_phase_high = 0x38; + + + + + + + + pdynamic->sd_config__woi_sd0 = 0x07; + pdynamic->sd_config__woi_sd1 = 0x05; + pdynamic->sd_config__initial_phase_sd0 = + ptuning_parms->tp_init_phase_rtn_lite_short; + pdynamic->sd_config__initial_phase_sd1 = + ptuning_parms->tp_init_phase_ref_lite_short; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_standard_ranging_long_range( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + + status = VL53L1_preset_mode_standard_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + + + + + + + ptiming->range_config__vcsel_period_a = 0x0F; + ptiming->range_config__vcsel_period_b = 0x0D; + ptiming->range_config__sigma_thresh = + ptuning_parms->tp_lite_long_sigma_thresh_mm; + ptiming->range_config__min_count_rate_rtn_limit_mcps = + ptuning_parms->tp_lite_long_min_count_rate_rtn_mcps; + ptiming->range_config__valid_phase_low = 0x08; + ptiming->range_config__valid_phase_high = 0xB8; + + + + + + + + pdynamic->sd_config__woi_sd0 = 0x0F; + pdynamic->sd_config__woi_sd1 = 0x0D; + pdynamic->sd_config__initial_phase_sd0 = + ptuning_parms->tp_init_phase_rtn_lite_long; + pdynamic->sd_config__initial_phase_sd1 = + ptuning_parms->tp_init_phase_ref_lite_long; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_standard_ranging_mm1_cal( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + + status = VL53L1_preset_mode_standard_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + pgeneral->dss_config__roi_mode_control = + VL53L1_DEVICEDSSMODE__REQUESTED_EFFFECTIVE_SPADS; + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM1_EN; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_standard_ranging_mm2_cal( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + + status = VL53L1_preset_mode_standard_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + pgeneral->dss_config__roi_mode_control = + VL53L1_DEVICEDSSMODE__REQUESTED_EFFFECTIVE_SPADS; + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM2_EN; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_timed_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = VL53L1_preset_mode_standard_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + pdynamic->system__grouped_parameter_hold = 0x00; + + + + ptiming->range_config__timeout_macrop_a_hi = 0x00; + ptiming->range_config__timeout_macrop_a_lo = 0xB1; + + + ptiming->range_config__timeout_macrop_b_hi = 0x00; + ptiming->range_config__timeout_macrop_b_lo = 0xD4; + + + + + ptiming->system__intermeasurement_period = 0x00000600; + pdynamic->system__seed_config = + ptuning_parms->tp_timed_seed_cfg; + + + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_PSEUDO_SOLO | + VL53L1_DEVICEREADOUTMODE_SINGLE_SD | + VL53L1_DEVICEMEASUREMENTMODE_TIMED; + } + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_preset_mode_timed_ranging_short_range( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = VL53L1_preset_mode_standard_ranging_short_range( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + pdynamic->system__grouped_parameter_hold = 0x00; + + + + + + + + ptiming->range_config__timeout_macrop_a_hi = 0x01; + ptiming->range_config__timeout_macrop_a_lo = 0x84; + + + ptiming->range_config__timeout_macrop_b_hi = 0x01; + ptiming->range_config__timeout_macrop_b_lo = 0xB1; + + ptiming->system__intermeasurement_period = 0x00000600; + pdynamic->system__seed_config = + ptuning_parms->tp_timed_seed_cfg; + + + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_PSEUDO_SOLO | + VL53L1_DEVICEREADOUTMODE_SINGLE_SD | + VL53L1_DEVICEMEASUREMENTMODE_TIMED; + } + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_preset_mode_timed_ranging_long_range( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = VL53L1_preset_mode_standard_ranging_long_range( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + pdynamic->system__grouped_parameter_hold = 0x00; + + + + + + + + ptiming->range_config__timeout_macrop_a_hi = 0x00; + ptiming->range_config__timeout_macrop_a_lo = 0x97; + + + ptiming->range_config__timeout_macrop_b_hi = 0x00; + ptiming->range_config__timeout_macrop_b_lo = 0xB1; + + ptiming->system__intermeasurement_period = 0x00000600; + pdynamic->system__seed_config = + ptuning_parms->tp_timed_seed_cfg; + + + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_PSEUDO_SOLO | + VL53L1_DEVICEREADOUTMODE_SINGLE_SD | + VL53L1_DEVICEMEASUREMENTMODE_TIMED; + } + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_preset_mode_low_power_auto_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg, + VL53L1_low_power_auto_data_t *plpadata) +{ + + + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = VL53L1_preset_mode_timed_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_config_low_power_auto_mode( + pgeneral, + pdynamic, + plpadata + ); + } + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_preset_mode_low_power_auto_short_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg, + VL53L1_low_power_auto_data_t *plpadata) +{ + + + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = VL53L1_preset_mode_timed_ranging_short_range( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_config_low_power_auto_mode( + pgeneral, + pdynamic, + plpadata + ); + } + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_preset_mode_low_power_auto_long_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg, + VL53L1_low_power_auto_data_t *plpadata) +{ + + + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = VL53L1_preset_mode_timed_ranging_long_range( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_config_low_power_auto_mode( + pgeneral, + pdynamic, + plpadata + ); + } + + LOG_FUNCTION_END(status); + + return status; +} + + + + +VL53L1_Error VL53L1_preset_mode_singleshot_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = VL53L1_preset_mode_standard_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + pdynamic->system__grouped_parameter_hold = 0x00; + + + + + + + ptiming->range_config__timeout_macrop_a_hi = 0x00; + ptiming->range_config__timeout_macrop_a_lo = 0xB1; + + + ptiming->range_config__timeout_macrop_b_hi = 0x00; + ptiming->range_config__timeout_macrop_b_lo = 0xD4; + + pdynamic->system__seed_config = + ptuning_parms->tp_timed_seed_cfg; + + + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_PSEUDO_SOLO | + VL53L1_DEVICEREADOUTMODE_SINGLE_SD | + VL53L1_DEVICEMEASUREMENTMODE_SINGLESHOT; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_standard_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + pstatic->dss_config__target_total_rate_mcps = 0x1400; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + VL53L1_init_histogram_config_structure( + 7, 0, 1, 2, 3, 4, + 0, 1, 2, 3, 4, 5, + phistogram); + + + + + + + VL53L1_init_histogram_multizone_config_structure( + 7, 0, 1, 2, 3, 4, + 0, 1, 2, 3, 4, 5, + &(pzone_cfg->multizone_hist_cfg)); + + + + + + + + + + + ptiming->range_config__vcsel_period_a = 0x09; + ptiming->range_config__vcsel_period_b = 0x0B; + pdynamic->sd_config__woi_sd0 = 0x09; + pdynamic->sd_config__woi_sd1 = 0x0B; + + + + + + + ptiming->mm_config__timeout_macrop_a_hi = 0x00; + ptiming->mm_config__timeout_macrop_a_lo = 0x20; + ptiming->mm_config__timeout_macrop_b_hi = 0x00; + ptiming->mm_config__timeout_macrop_b_lo = 0x1A; + + + + ptiming->range_config__timeout_macrop_a_hi = 0x00; + ptiming->range_config__timeout_macrop_a_lo = 0x28; + + + + ptiming->range_config__timeout_macrop_b_hi = 0x00; + ptiming->range_config__timeout_macrop_b_lo = 0x21; + + + + pgeneral->phasecal_config__timeout_macrop = 0xF5; + + + + + + + + + phistpostprocess->valid_phase_low = 0x08; + phistpostprocess->valid_phase_high = 0x88; + + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + + + + + VL53L1_SEQUENCE_RANGE_EN; + + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM | + VL53L1_DEVICEREADOUTMODE_DUAL_SD | + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_with_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + VL53L1_init_histogram_config_structure( + 7, 0, 1, 2, 3, 4, + 8+0, 8+1, 8+2, 3, 4, 5, + phistogram); + + + + + + + VL53L1_init_histogram_multizone_config_structure( + 7, 0, 1, 2, 3, 4, + 8+0, 8+1, 8+2, 3, 4, 5, + &(pzone_cfg->multizone_hist_cfg)); + + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM1_EN | + VL53L1_SEQUENCE_RANGE_EN; + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM | + VL53L1_DEVICEREADOUTMODE_DUAL_SD | + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_with_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_ranging_with_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM2_EN | + VL53L1_SEQUENCE_RANGE_EN; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_mm1_cal( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + + + + + + + + + + + VL53L1_init_histogram_config_structure( + 7, 8+0, 8+1, 8+2, 8+3, 8+4, + 8+0, 8+1, 8+2, 8+3, 8+4, 8+5, + phistogram); + + + + + + + VL53L1_init_histogram_multizone_config_structure( + 7, 8+0, 8+1, 8+2, 8+3, 8+4, + 8+0, 8+1, 8+2, 8+3, 8+4, 8+5, + &(pzone_cfg->multizone_hist_cfg)); + + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + + pgeneral->dss_config__roi_mode_control = + VL53L1_DEVICEDSSMODE__REQUESTED_EFFFECTIVE_SPADS; + + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM1_EN | + VL53L1_SEQUENCE_RANGE_EN; + + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_mm2_cal( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_ranging_mm1_cal( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + if (status == VL53L1_ERROR_NONE) { + + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM2_EN | + VL53L1_SEQUENCE_RANGE_EN; + + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_short_timing( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + pstatic->dss_config__target_total_rate_mcps = 0x1400; + + + + + VL53L1_init_histogram_config_structure( + 7, 0, 1, 2, 3, 4, + 7, 0, 1, 2, 3, 4, + phistogram); + + + + + + + VL53L1_init_histogram_multizone_config_structure( + 7, 0, 1, 2, 3, 4, + 7, 0, 1, 2, 3, 4, + &(pzone_cfg->multizone_hist_cfg)); + + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + + + + ptiming->range_config__vcsel_period_a = 0x04; + ptiming->range_config__vcsel_period_b = 0x03; + ptiming->mm_config__timeout_macrop_a_hi = 0x00; + ptiming->mm_config__timeout_macrop_a_lo = 0x42; + ptiming->mm_config__timeout_macrop_b_hi = 0x00; + ptiming->mm_config__timeout_macrop_b_lo = 0x42; + ptiming->range_config__timeout_macrop_a_hi = 0x00; + ptiming->range_config__timeout_macrop_a_lo = 0x52; + ptiming->range_config__timeout_macrop_b_hi = 0x00; + ptiming->range_config__timeout_macrop_b_lo = 0x66; + + pgeneral->cal_config__vcsel_start = 0x04; + + + + + + + + + + + pgeneral->phasecal_config__timeout_macrop = 0xa4; + + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + + + + + VL53L1_SEQUENCE_RANGE_EN; + + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM | + VL53L1_DEVICEREADOUTMODE_DUAL_SD | + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_long_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + + + + VL53L1_init_histogram_config_structure( + 7, 0, 1, 2, 3, 4, + 0, 1, 2, 3, 4, 5, + phistogram); + + + + + + + VL53L1_init_histogram_multizone_config_structure( + 7, 0, 1, 2, 3, 4, + + 0, 1, 2, 3, 4, 5, + + &(pzone_cfg->multizone_hist_cfg)); + + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + + + + ptiming->range_config__vcsel_period_a = 0x09; + ptiming->range_config__vcsel_period_b = 0x0b; + + + + + + + ptiming->mm_config__timeout_macrop_a_hi = 0x00; + ptiming->mm_config__timeout_macrop_a_lo = 0x21; + ptiming->mm_config__timeout_macrop_b_hi = 0x00; + ptiming->mm_config__timeout_macrop_b_lo = 0x1b; + + + + + ptiming->range_config__timeout_macrop_a_hi = 0x00; + ptiming->range_config__timeout_macrop_a_lo = 0x29; + ptiming->range_config__timeout_macrop_b_hi = 0x00; + ptiming->range_config__timeout_macrop_b_lo = 0x22; + + + + + pgeneral->cal_config__vcsel_start = 0x09; + + + + + + + + + + + + + + + pgeneral->phasecal_config__timeout_macrop = 0xF5; + + + + + pdynamic->sd_config__woi_sd0 = 0x09; + pdynamic->sd_config__woi_sd1 = 0x0B; + pdynamic->sd_config__initial_phase_sd0 = + ptuning_parms->tp_init_phase_rtn_hist_long; + pdynamic->sd_config__initial_phase_sd1 = + ptuning_parms->tp_init_phase_ref_hist_long; + + + + + + + + + phistpostprocess->valid_phase_low = 0x08; + phistpostprocess->valid_phase_high = 0x88; + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_RANGE_EN; + + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM | + VL53L1_DEVICEREADOUTMODE_DUAL_SD | + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_long_range_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_long_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + + + + VL53L1_init_histogram_config_structure( + 7, 0, 1, 2, 3, 4, + 8+0, 8+1, 8+2, 3, 4, 5, + phistogram); + + + + + + + VL53L1_init_histogram_multizone_config_structure( + 7, 0, 1, 2, 3, 4, + 8+0, 8+1, 8+2, 3, 4, 5, + &(pzone_cfg->multizone_hist_cfg)); + + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM1_EN | + VL53L1_SEQUENCE_RANGE_EN; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_long_range_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_long_range_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM2_EN | + VL53L1_SEQUENCE_RANGE_EN; + } + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_preset_mode_histogram_medium_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + + + + VL53L1_init_histogram_config_structure( + 7, 0, 1, 1, 2, 2, + 0, 1, 2, 1, 2, 3, + phistogram); + + + + + + + VL53L1_init_histogram_multizone_config_structure( + 7, 0, 1, 1, 2, 2, + 0, 1, 2, 1, 2, 3, + &(pzone_cfg->multizone_hist_cfg)); + + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + + + + ptiming->range_config__vcsel_period_a = 0x05; + ptiming->range_config__vcsel_period_b = 0x07; + + + + + + + ptiming->mm_config__timeout_macrop_a_hi = 0x00; + ptiming->mm_config__timeout_macrop_a_lo = 0x36; + ptiming->mm_config__timeout_macrop_b_hi = 0x00; + ptiming->mm_config__timeout_macrop_b_lo = 0x28; + + + + + ptiming->range_config__timeout_macrop_a_hi = 0x00; + ptiming->range_config__timeout_macrop_a_lo = 0x44; + ptiming->range_config__timeout_macrop_b_hi = 0x00; + ptiming->range_config__timeout_macrop_b_lo = 0x33; + + + + + pgeneral->cal_config__vcsel_start = 0x05; + + + + + + + + + + + + + + + pgeneral->phasecal_config__timeout_macrop = 0xF5; + + + + + pdynamic->sd_config__woi_sd0 = 0x05; + pdynamic->sd_config__woi_sd1 = 0x07; + pdynamic->sd_config__initial_phase_sd0 = + ptuning_parms->tp_init_phase_rtn_hist_med; + pdynamic->sd_config__initial_phase_sd1 = + ptuning_parms->tp_init_phase_ref_hist_med; + + + + + + + + + phistpostprocess->valid_phase_low = 0x08; + phistpostprocess->valid_phase_high = 0x48; + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_RANGE_EN; + + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM | + VL53L1_DEVICEREADOUTMODE_DUAL_SD | + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_medium_range_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_medium_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + VL53L1_init_histogram_config_structure( + 7, 0, 1, 1, 2, 2, + 8+0, 8+1, 8+2, 1, 2, 3, + phistogram); + + + + + + + VL53L1_init_histogram_multizone_config_structure( + 7, 0, 1, 1, 2, 2, + 8+0, 8+1, 8+2, 1, 2, 3, + &(pzone_cfg->multizone_hist_cfg)); + + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM1_EN | + VL53L1_SEQUENCE_RANGE_EN; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_medium_range_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_medium_range_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM2_EN | + VL53L1_SEQUENCE_RANGE_EN; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_short_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + + + + VL53L1_init_histogram_config_structure( + 7, 7, 0, 1, 1, 1, + 0, 1, 1, 1, 2, 2, + phistogram); + + + + + + + VL53L1_init_histogram_multizone_config_structure( + 7, 7, 0, 1, 1, 1, + 0, 1, 1, 1, 2, 2, + &(pzone_cfg->multizone_hist_cfg)); + + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + + + + ptiming->range_config__vcsel_period_a = 0x03; + ptiming->range_config__vcsel_period_b = 0x05; + + + + + + + ptiming->mm_config__timeout_macrop_a_hi = 0x00; + ptiming->mm_config__timeout_macrop_a_lo = 0x52; + ptiming->mm_config__timeout_macrop_b_hi = 0x00; + ptiming->mm_config__timeout_macrop_b_lo = 0x37; + + + + + ptiming->range_config__timeout_macrop_a_hi = 0x00; + ptiming->range_config__timeout_macrop_a_lo = 0x66; + ptiming->range_config__timeout_macrop_b_hi = 0x00; + ptiming->range_config__timeout_macrop_b_lo = 0x44; + + + + + pgeneral->cal_config__vcsel_start = 0x03; + + + + + + + + + + + + + + + pgeneral->phasecal_config__timeout_macrop = 0xF5; + + + + + pdynamic->sd_config__woi_sd0 = 0x03; + pdynamic->sd_config__woi_sd1 = 0x05; + pdynamic->sd_config__initial_phase_sd0 = + ptuning_parms->tp_init_phase_rtn_hist_short; + pdynamic->sd_config__initial_phase_sd1 = + ptuning_parms->tp_init_phase_ref_hist_short; + + + + + + + + phistpostprocess->valid_phase_low = 0x08; + phistpostprocess->valid_phase_high = 0x28; + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM1_EN | + + + VL53L1_SEQUENCE_RANGE_EN; + + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM | + VL53L1_DEVICEREADOUTMODE_DUAL_SD | + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + } + + LOG_FUNCTION_END(status); + + return status; +} + + + + +VL53L1_Error VL53L1_preset_mode_special_histogram_short_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_short_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + + + + VL53L1_init_histogram_config_structure( + 7, 7, 0, 0, 1, 1, + 0, 0, 0, 1, 1, 1, + phistogram); + + + + + + + VL53L1_init_histogram_multizone_config_structure( + 7, 7, 0, 0, 1, 1, + 0, 0, 0, 1, 1, 1, + &(pzone_cfg->multizone_hist_cfg)); + + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + + + + ptiming->range_config__vcsel_period_a = 0x02; + ptiming->range_config__vcsel_period_b = 0x03; + + + + + pgeneral->cal_config__vcsel_start = 0x00; + + + + + + pgeneral->phasecal_config__target = 0x31; + + + + + pdynamic->sd_config__woi_sd0 = 0x02; + pdynamic->sd_config__woi_sd1 = 0x03; + pdynamic->sd_config__initial_phase_sd0 = + ptuning_parms->tp_init_phase_rtn_hist_short; + pdynamic->sd_config__initial_phase_sd1 = + ptuning_parms->tp_init_phase_ref_hist_short; + + + + + + + + + phistpostprocess->valid_phase_low = 0x10; + phistpostprocess->valid_phase_high = 0x18; + + } + + LOG_FUNCTION_END(status); + + return status; +} + + + + +VL53L1_Error VL53L1_preset_mode_histogram_short_range_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_short_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + + + + VL53L1_init_histogram_config_structure( + 7, 7, 0, 1, 1, 1, + 8+0, 8+1, 1, 1, 2, 2, + phistogram); + + + + + + + VL53L1_init_histogram_multizone_config_structure( + 7, 7, 0, 1, 1, 1, + 8+0, 8+1, 1, 1, 2, 2, + &(pzone_cfg->multizone_hist_cfg)); + + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM1_EN | + VL53L1_SEQUENCE_RANGE_EN; + + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_short_range_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_short_range_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM2_EN | + VL53L1_SEQUENCE_RANGE_EN; + } + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_preset_mode_histogram_characterisation( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + pstatic->debug__ctrl = 0x01; + psystem->power_management__go1_power_force = 0x01; + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_RANGE_EN; + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM | + VL53L1_DEVICEREADOUTMODE_SPLIT_MANUAL | + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_xtalk_planar( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_multizone_long_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + status = + VL53L1_zone_preset_xtalk_planar( + pgeneral, + pzone_cfg); + + + + + + + + ptiming->range_config__vcsel_period_a = 0x09; + ptiming->range_config__vcsel_period_b = 0x09; + + + + + VL53L1_init_histogram_config_structure( + 7, 0, 1, 2, 3, 4, + 7, 0, 1, 2, 3, 4, + phistogram); + + + + + + + + VL53L1_init_histogram_multizone_config_structure( + 7, 0, 1, 2, 3, 4, + + 7, 0, 1, 2, 3, 4, + + &(pzone_cfg->multizone_hist_cfg)); + + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_set_histogram_multizone_initial_bin_config( + pzone_cfg, + phistogram, + &(pzone_cfg->multizone_hist_cfg)); + } + + + + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + } + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_preset_mode_histogram_xtalk_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + + + + VL53L1_init_histogram_config_structure( + 8+7, 8+0, 8+1, 8+2, 8+3, 8+4, + 8+7, 8+0, 8+1, 8+2, 8+3, 8+4, + phistogram); + + + + + + + VL53L1_init_histogram_multizone_config_structure( + 8+7, 8+0, 8+1, 8+2, 8+3, 8+4, + 8+7, 8+0, 8+1, 8+2, 8+3, 8+4, + &(pzone_cfg->multizone_hist_cfg)); + + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + + + + ptiming->range_config__vcsel_period_a = 0x09; + ptiming->range_config__vcsel_period_b = 0x09; + + + + + + + ptiming->mm_config__timeout_macrop_a_hi = 0x00; + ptiming->mm_config__timeout_macrop_a_lo = 0x21; + ptiming->mm_config__timeout_macrop_b_hi = 0x00; + ptiming->mm_config__timeout_macrop_b_lo = 0x21; + + + + + ptiming->range_config__timeout_macrop_a_hi = 0x00; + ptiming->range_config__timeout_macrop_a_lo = 0x29; + ptiming->range_config__timeout_macrop_b_hi = 0x00; + ptiming->range_config__timeout_macrop_b_lo = 0x29; + + + + + pgeneral->cal_config__vcsel_start = 0x09; + + + + + + + + + + + + + + pgeneral->phasecal_config__timeout_macrop = 0xF5; + + + + + pdynamic->sd_config__woi_sd0 = 0x09; + pdynamic->sd_config__woi_sd1 = 0x09; + pdynamic->sd_config__initial_phase_sd0 = 0x09; + pdynamic->sd_config__initial_phase_sd1 = 0x06; + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM1_EN | + VL53L1_SEQUENCE_RANGE_EN; + + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM | + VL53L1_DEVICEREADOUTMODE_DUAL_SD | + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_xtalk_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_xtalk_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM2_EN | + VL53L1_SEQUENCE_RANGE_EN; + + + + LOG_FUNCTION_END(status); + + return status; +} + + + + +VL53L1_Error VL53L1_preset_mode_histogram_multizone( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_medium_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + status = + VL53L1_init_zone_config_structure( + 4, 8, 2, + + 4, 8, 2, + + 7, 7, + + pzone_cfg); + + pgeneral->global_config__stream_divider = + pzone_cfg->active_zones + 1; + + + + + + + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_set_histogram_multizone_initial_bin_config( + pzone_cfg, + phistogram, + &(pzone_cfg->multizone_hist_cfg)); + } + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + } + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_preset_mode_histogram_multizone_short_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_short_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + status = + VL53L1_init_zone_config_structure( + 4, 8, 2, + + 4, 8, 2, + + 7, 7, + + pzone_cfg); + + pgeneral->global_config__stream_divider = + pzone_cfg->active_zones + 1; + + + + + + + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_set_histogram_multizone_initial_bin_config( + pzone_cfg, + phistogram, + &(pzone_cfg->multizone_hist_cfg) + ); + } + + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_multizone_long_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_long_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + status = + VL53L1_init_zone_config_structure( + 4, 8, 2, + + 4, 8, 2, + + 7, 7, + + pzone_cfg); + + pgeneral->global_config__stream_divider = + pzone_cfg->active_zones + 1; + + + + + + + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_set_histogram_multizone_initial_bin_config( + pzone_cfg, + phistogram, + &(pzone_cfg->multizone_hist_cfg)); + } + + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + } + + LOG_FUNCTION_END(status); + + return status; +} + + + + +VL53L1_Error VL53L1_preset_mode_olt( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = VL53L1_preset_mode_standard_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) + + + psystem->system__stream_count_ctrl = 0x01; + + LOG_FUNCTION_END(status); + + return status; +} + + +void VL53L1_copy_hist_cfg_to_static_cfg( + VL53L1_histogram_config_t *phistogram, + VL53L1_static_config_t *pstatic, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic) +{ + + + + + + + LOG_FUNCTION_START(""); + + SUPPRESS_UNUSED_WARNING(pgeneral); + + pstatic->sigma_estimator__effective_pulse_width_ns = + phistogram->histogram_config__high_amb_even_bin_0_1; + pstatic->sigma_estimator__effective_ambient_width_ns = + phistogram->histogram_config__high_amb_even_bin_2_3; + pstatic->sigma_estimator__sigma_ref_mm = + phistogram->histogram_config__high_amb_even_bin_4_5; + + pstatic->algo__crosstalk_compensation_valid_height_mm = + phistogram->histogram_config__high_amb_odd_bin_0_1; + + pstatic->spare_host_config__static_config_spare_0 = + phistogram->histogram_config__high_amb_odd_bin_2_3; + pstatic->spare_host_config__static_config_spare_1 = + phistogram->histogram_config__high_amb_odd_bin_4_5; + + pstatic->algo__range_ignore_threshold_mcps = + (((uint16_t)phistogram->histogram_config__mid_amb_even_bin_0_1) + << 8) + + (uint16_t)phistogram->histogram_config__mid_amb_even_bin_2_3; + + pstatic->algo__range_ignore_valid_height_mm = + phistogram->histogram_config__mid_amb_even_bin_4_5; + pstatic->algo__range_min_clip = + phistogram->histogram_config__mid_amb_odd_bin_0_1; + pstatic->algo__consistency_check__tolerance = + phistogram->histogram_config__mid_amb_odd_bin_2; + + pstatic->spare_host_config__static_config_spare_2 = + phistogram->histogram_config__mid_amb_odd_bin_3_4; + pstatic->sd_config__reset_stages_msb = + phistogram->histogram_config__mid_amb_odd_bin_5; + + pstatic->sd_config__reset_stages_lsb = + phistogram->histogram_config__user_bin_offset; + + ptiming->range_config__sigma_thresh = + (((uint16_t)phistogram->histogram_config__low_amb_even_bin_0_1) + << 8) + + (uint16_t)phistogram->histogram_config__low_amb_even_bin_2_3; + + ptiming->range_config__min_count_rate_rtn_limit_mcps = + (((uint16_t)phistogram->histogram_config__low_amb_even_bin_4_5) + << 8) + + (uint16_t)phistogram->histogram_config__low_amb_odd_bin_0_1; + + ptiming->range_config__valid_phase_low = + phistogram->histogram_config__low_amb_odd_bin_2_3; + ptiming->range_config__valid_phase_high = + phistogram->histogram_config__low_amb_odd_bin_4_5; + + pdynamic->system__thresh_high = + phistogram->histogram_config__amb_thresh_low; + + pdynamic->system__thresh_low = + phistogram->histogram_config__amb_thresh_high; + + pdynamic->system__enable_xtalk_per_quadrant = + phistogram->histogram_config__spad_array_selection; + + LOG_FUNCTION_END(0); + +} + +void VL53L1_copy_hist_bins_to_static_cfg( + VL53L1_histogram_config_t *phistogram, + VL53L1_static_config_t *pstatic, + VL53L1_timing_config_t *ptiming) +{ + + + + + + + LOG_FUNCTION_START(""); + + pstatic->sigma_estimator__effective_pulse_width_ns = + phistogram->histogram_config__high_amb_even_bin_0_1; + pstatic->sigma_estimator__effective_ambient_width_ns = + phistogram->histogram_config__high_amb_even_bin_2_3; + pstatic->sigma_estimator__sigma_ref_mm = + phistogram->histogram_config__high_amb_even_bin_4_5; + + pstatic->algo__crosstalk_compensation_valid_height_mm = + phistogram->histogram_config__high_amb_odd_bin_0_1; + + pstatic->spare_host_config__static_config_spare_0 = + phistogram->histogram_config__high_amb_odd_bin_2_3; + pstatic->spare_host_config__static_config_spare_1 = + phistogram->histogram_config__high_amb_odd_bin_4_5; + + pstatic->algo__range_ignore_threshold_mcps = + (((uint16_t)phistogram->histogram_config__mid_amb_even_bin_0_1) + << 8) + + (uint16_t)phistogram->histogram_config__mid_amb_even_bin_2_3; + + pstatic->algo__range_ignore_valid_height_mm = + phistogram->histogram_config__mid_amb_even_bin_4_5; + pstatic->algo__range_min_clip = + phistogram->histogram_config__mid_amb_odd_bin_0_1; + pstatic->algo__consistency_check__tolerance = + phistogram->histogram_config__mid_amb_odd_bin_2; + + pstatic->spare_host_config__static_config_spare_2 = + phistogram->histogram_config__mid_amb_odd_bin_3_4; + pstatic->sd_config__reset_stages_msb = + phistogram->histogram_config__mid_amb_odd_bin_5; + + ptiming->range_config__sigma_thresh = + (((uint16_t)phistogram->histogram_config__low_amb_even_bin_0_1) + << 8) + + (uint16_t)phistogram->histogram_config__low_amb_even_bin_2_3; + + ptiming->range_config__min_count_rate_rtn_limit_mcps = + (((uint16_t)phistogram->histogram_config__low_amb_even_bin_4_5) + << 8) + + (uint16_t)phistogram->histogram_config__low_amb_odd_bin_0_1; + + ptiming->range_config__valid_phase_low = + phistogram->histogram_config__low_amb_odd_bin_2_3; + ptiming->range_config__valid_phase_high = + phistogram->histogram_config__low_amb_odd_bin_4_5; + + LOG_FUNCTION_END(0); + +} + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_ref( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + phistogram->histogram_config__spad_array_selection = 0x01; + + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + } + + LOG_FUNCTION_END(status); + + return status; +} + + + + + diff --git a/drivers/input/misc/vl53L1/src/vl53l1_api_strings.c b/drivers/input/misc/vl53L1/src/vl53l1_api_strings.c new file mode 100644 index 000000000000..70266e4418a7 --- /dev/null +++ b/drivers/input/misc/vl53L1/src/vl53l1_api_strings.c @@ -0,0 +1,334 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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 "vl53l1_api_core.h" +#include "vl53l1_api_strings.h" +#include "vl53l1_error_codes.h" +#include "vl53l1_error_strings.h" + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_API, status, fmt, \ + ##__VA_ARGS__) + + +VL53L1_Error VL53L1_get_range_status_string( + uint8_t RangeStatus, + char *pRangeStatusString) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + +#ifdef VL53L1_USE_EMPTY_STRING + VL53L1_COPYSTRING(pRangeStatusString, ""); +#else + switch (RangeStatus) { + case 0: + VL53L1_COPYSTRING(pRangeStatusString, + VL53L1_STRING_RANGESTATUS_RANGEVALID); + break; + case 1: + VL53L1_COPYSTRING(pRangeStatusString, + VL53L1_STRING_RANGESTATUS_SIGMA); + break; + case 2: + VL53L1_COPYSTRING(pRangeStatusString, + VL53L1_STRING_RANGESTATUS_SIGNAL); + break; + case 3: + VL53L1_COPYSTRING(pRangeStatusString, + VL53L1_STRING_RANGESTATUS_MINRANGE); + break; + case 4: + VL53L1_COPYSTRING(pRangeStatusString, + VL53L1_STRING_RANGESTATUS_PHASE); + break; + case 5: + VL53L1_COPYSTRING(pRangeStatusString, + VL53L1_STRING_RANGESTATUS_HW); + break; + + default: + + VL53L1_COPYSTRING(pRangeStatusString, + VL53L1_STRING_RANGESTATUS_NONE); + } +#endif + + LOG_FUNCTION_END(status); + return status; +} + + +VL53L1_Error VL53L1_get_pal_state_string( + VL53L1_State PalStateCode, + char *pPalStateString) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + +#ifdef VL53L1_USE_EMPTY_STRING + VL53L1_COPYSTRING(pPalStateString, ""); +#else + switch (PalStateCode) { + case VL53L1_STATE_POWERDOWN: + VL53L1_COPYSTRING(pPalStateString, + VL53L1_STRING_STATE_POWERDOWN); + break; + case VL53L1_STATE_WAIT_STATICINIT: + VL53L1_COPYSTRING(pPalStateString, + VL53L1_STRING_STATE_WAIT_STATICINIT); + break; + case VL53L1_STATE_STANDBY: + VL53L1_COPYSTRING(pPalStateString, + VL53L1_STRING_STATE_STANDBY); + break; + case VL53L1_STATE_IDLE: + VL53L1_COPYSTRING(pPalStateString, + VL53L1_STRING_STATE_IDLE); + break; + case VL53L1_STATE_RUNNING: + VL53L1_COPYSTRING(pPalStateString, + VL53L1_STRING_STATE_RUNNING); + break; + case VL53L1_STATE_RESET: + VL53L1_COPYSTRING(pPalStateString, + VL53L1_STRING_STATE_RESET); + break; + case VL53L1_STATE_UNKNOWN: + VL53L1_COPYSTRING(pPalStateString, + VL53L1_STRING_STATE_UNKNOWN); + break; + case VL53L1_STATE_ERROR: + VL53L1_COPYSTRING(pPalStateString, + VL53L1_STRING_STATE_ERROR); + break; + + default: + VL53L1_COPYSTRING(pPalStateString, + VL53L1_STRING_STATE_UNKNOWN); + } +#endif + + LOG_FUNCTION_END(status); + return status; +} + +VL53L1_Error VL53L1_get_sequence_steps_info( + VL53L1_SequenceStepId SequenceStepId, + char *pSequenceStepsString) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + +#ifdef VL53L1_USE_EMPTY_STRING + VL53L1_COPYSTRING(pSequenceStepsString, ""); +#else + switch (SequenceStepId) { + case VL53L1_SEQUENCESTEP_VHV: + VL53L1_COPYSTRING(pSequenceStepsString, + VL53L1_STRING_SEQUENCESTEP_VHV); + break; + case VL53L1_SEQUENCESTEP_PHASECAL: + VL53L1_COPYSTRING(pSequenceStepsString, + VL53L1_STRING_SEQUENCESTEP_PHASECAL); + break; + case VL53L1_SEQUENCESTEP_REFPHASE: + VL53L1_COPYSTRING(pSequenceStepsString, + VL53L1_STRING_SEQUENCESTEP_DSS1); + break; + case VL53L1_SEQUENCESTEP_DSS1: + VL53L1_COPYSTRING(pSequenceStepsString, + VL53L1_STRING_SEQUENCESTEP_DSS1); + break; + case VL53L1_SEQUENCESTEP_DSS2: + VL53L1_COPYSTRING(pSequenceStepsString, + VL53L1_STRING_SEQUENCESTEP_DSS2); + break; + case VL53L1_SEQUENCESTEP_MM1: + VL53L1_COPYSTRING(pSequenceStepsString, + VL53L1_STRING_SEQUENCESTEP_MM1); + break; + case VL53L1_SEQUENCESTEP_MM2: + VL53L1_COPYSTRING(pSequenceStepsString, + VL53L1_STRING_SEQUENCESTEP_MM2); + break; + case VL53L1_SEQUENCESTEP_RANGE: + VL53L1_COPYSTRING(pSequenceStepsString, + VL53L1_STRING_SEQUENCESTEP_RANGE); + break; + default: + Status = VL53L1_ERROR_INVALID_PARAMS; + } +#endif + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L1_Error VL53L1_get_limit_check_info(uint16_t LimitCheckId, + char *pLimitCheckString) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + +#ifdef VL53L1_USE_EMPTY_STRING + VL53L1_COPYSTRING(pLimitCheckString, ""); +#else + switch (LimitCheckId) { + case VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE: + VL53L1_COPYSTRING(pLimitCheckString, + VL53L1_STRING_CHECKENABLE_SIGMA_FINAL_RANGE); + break; + case VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: + VL53L1_COPYSTRING(pLimitCheckString, + VL53L1_STRING_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE); + break; + default: + VL53L1_COPYSTRING(pLimitCheckString, + VL53L1_STRING_UNKNOW_ERROR_CODE); + } +#endif + + LOG_FUNCTION_END(Status); + return Status; +} + + diff --git a/drivers/input/misc/vl53L1/src/vl53l1_core.c b/drivers/input/misc/vl53L1/src/vl53l1_core.c new file mode 100644 index 000000000000..6d1fa6c3117f --- /dev/null +++ b/drivers/input/misc/vl53L1/src/vl53l1_core.c @@ -0,0 +1,5888 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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 "vl53l1_ll_def.h" +#include "vl53l1_ll_device.h" +#include "vl53l1_platform.h" +#include "vl53l1_register_map.h" +#include "vl53l1_register_funcs.h" +#include "vl53l1_register_settings.h" +#include "vl53l1_hist_structs.h" +#include "vl53l1_api_preset_modes.h" +#include "vl53l1_core.h" +#include "vl53l1_tuning_parm_defaults.h" + + + + + + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_CORE, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_CORE, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_CORE, \ + status, fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_CORE, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + + +void VL53L1_init_version( + VL53L1_DEV Dev) +{ + + + + + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + pdev->version.ll_major = VL53L1_LL_API_IMPLEMENTATION_VER_MAJOR; + pdev->version.ll_minor = VL53L1_LL_API_IMPLEMENTATION_VER_MINOR; + pdev->version.ll_build = VL53L1_LL_API_IMPLEMENTATION_VER_SUB; + pdev->version.ll_revision = VL53L1_LL_API_IMPLEMENTATION_VER_REVISION; +} + + +void VL53L1_init_ll_driver_state( + VL53L1_DEV Dev, + VL53L1_DeviceState device_state) +{ + + + + + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_ll_driver_state_t *pstate = &(pdev->ll_state); + + pstate->cfg_device_state = device_state; + pstate->cfg_stream_count = 0; + pstate->cfg_gph_id = VL53L1_GROUPEDPARAMETERHOLD_ID_MASK; + pstate->cfg_timing_status = 0; + pstate->cfg_zone_id = 0; + + pstate->rd_device_state = device_state; + pstate->rd_stream_count = 0; + pstate->rd_gph_id = VL53L1_GROUPEDPARAMETERHOLD_ID_MASK; + pstate->rd_timing_status = 0; + pstate->rd_zone_id = 0; + +} + + +VL53L1_Error VL53L1_update_ll_driver_rd_state( + VL53L1_DEV Dev) +{ + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_ll_driver_state_t *pstate = &(pdev->ll_state); + + + + + LOG_FUNCTION_START(""); + + + + + + if ((pdev->sys_ctrl.system__mode_start & + VL53L1_DEVICEMEASUREMENTMODE_MODE_MASK) == 0x00) { + + pstate->rd_device_state = VL53L1_DEVICESTATE_SW_STANDBY; + pstate->rd_stream_count = 0; + pstate->rd_internal_stream_count = 0; + pstate->rd_internal_stream_count_val = 0; + pstate->rd_gph_id = VL53L1_GROUPEDPARAMETERHOLD_ID_MASK; + pstate->rd_timing_status = 0; + pstate->rd_zone_id = 0; + + } else { + + + + + + + if (pstate->rd_stream_count == 0xFF) + pstate->rd_stream_count = 0x80; + else + pstate->rd_stream_count++; + + + + + + status = VL53L1_update_internal_stream_counters(Dev, + pstate->rd_stream_count, + &(pstate->rd_internal_stream_count), + &(pstate->rd_internal_stream_count_val)); + + + + + + + pstate->rd_gph_id ^= VL53L1_GROUPEDPARAMETERHOLD_ID_MASK; + + + + + switch (pstate->rd_device_state) { + + case VL53L1_DEVICESTATE_SW_STANDBY: + + if ((pdev->dyn_cfg.system__grouped_parameter_hold & + VL53L1_GROUPEDPARAMETERHOLD_ID_MASK) > 0) { + pstate->rd_device_state = + VL53L1_DEVICESTATE_RANGING_WAIT_GPH_SYNC; + } else { + if (pstate->rd_zone_id >= + pdev->zone_cfg.active_zones) + pstate->rd_device_state = + VL53L1_DEVICESTATE_RANGING_OUTPUT_DATA; + else + pstate->rd_device_state = + VL53L1_DEVICESTATE_RANGING_GATHER_DATA; + } + + pstate->rd_stream_count = 0; + pstate->rd_internal_stream_count = 0; + pstate->rd_internal_stream_count_val = 0; + pstate->rd_timing_status = 0; + pstate->rd_zone_id = 0; + + break; + + case VL53L1_DEVICESTATE_RANGING_WAIT_GPH_SYNC: + pstate->rd_stream_count = 0; + pstate->rd_internal_stream_count = 0; + pstate->rd_internal_stream_count_val = 0; + pstate->rd_zone_id = 0; + if (pstate->rd_zone_id >= + pdev->zone_cfg.active_zones) + pstate->rd_device_state = + VL53L1_DEVICESTATE_RANGING_OUTPUT_DATA; + else + pstate->rd_device_state = + VL53L1_DEVICESTATE_RANGING_GATHER_DATA; + + break; + + case VL53L1_DEVICESTATE_RANGING_GATHER_DATA: + pstate->rd_zone_id++; + if (pstate->rd_zone_id >= + pdev->zone_cfg.active_zones) + pstate->rd_device_state = + VL53L1_DEVICESTATE_RANGING_OUTPUT_DATA; + else + pstate->rd_device_state = + VL53L1_DEVICESTATE_RANGING_GATHER_DATA; + + break; + + case VL53L1_DEVICESTATE_RANGING_OUTPUT_DATA: + pstate->rd_zone_id = 0; + pstate->rd_timing_status ^= 0x01; + + if (pstate->rd_zone_id >= + pdev->zone_cfg.active_zones) + pstate->rd_device_state = + VL53L1_DEVICESTATE_RANGING_OUTPUT_DATA; + else + pstate->rd_device_state = + VL53L1_DEVICESTATE_RANGING_GATHER_DATA; + break; + + default: + pstate->rd_device_state = + VL53L1_DEVICESTATE_SW_STANDBY; + pstate->rd_stream_count = 0; + pstate->rd_internal_stream_count = 0; + pstate->rd_internal_stream_count_val = 0; + pstate->rd_gph_id = VL53L1_GROUPEDPARAMETERHOLD_ID_MASK; + pstate->rd_timing_status = 0; + pstate->rd_zone_id = 0; + break; + } + } + + + + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_check_ll_driver_rd_state( + VL53L1_DEV Dev) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + VL53L1_ll_driver_state_t *pstate = &(pdev->ll_state); + VL53L1_system_results_t *psys_results = &(pdev->sys_results); + VL53L1_histogram_bin_data_t *phist_data = &(pdev->hist_data); + VL53L1_zone_private_dyn_cfgs_t *pZ = &(pres->zone_dyn_cfgs); + + uint8_t device_range_status = 0; + uint8_t device_stream_count = 0; + uint8_t device_gph_id = 0; + uint8_t histogram_mode = 0; + uint8_t expected_stream_count = 0; + uint8_t expected_gph_id = 0; + + LOG_FUNCTION_START(""); + + + + + + device_range_status = + psys_results->result__range_status & + VL53L1_RANGE_STATUS__RANGE_STATUS_MASK; + + device_stream_count = psys_results->result__stream_count; + + + + + histogram_mode = + (pdev->sys_ctrl.system__mode_start & + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM) == + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM; + + + + device_gph_id = (psys_results->result__interrupt_status & + VL53L1_INTERRUPT_STATUS__GPH_ID_INT_STATUS_MASK) >> 4; + + if (histogram_mode) + device_gph_id = (phist_data->result__interrupt_status & + VL53L1_INTERRUPT_STATUS__GPH_ID_INT_STATUS_MASK) >> 4; + + + + + if (!((pdev->sys_ctrl.system__mode_start & + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK) == + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK)) + goto ENDFUNC; + + + + + + + + + + + + + + + if (pstate->rd_device_state == + VL53L1_DEVICESTATE_RANGING_WAIT_GPH_SYNC) { + + if (histogram_mode == 0) { + if (device_range_status != + VL53L1_DEVICEERROR_GPHSTREAMCOUNT0READY) + status = + VL53L1_ERROR_GPH_SYNC_CHECK_FAIL; + + } + } else { + if (pstate->rd_stream_count != device_stream_count) + status = VL53L1_ERROR_STREAM_COUNT_CHECK_FAIL; + + + + + + if (pstate->rd_gph_id != device_gph_id) + status = VL53L1_ERROR_GPH_ID_CHECK_FAIL; + + + + + + + + + + + + + + + + + + + + + + expected_stream_count = + pZ->VL53L1_p_002[pstate->rd_zone_id].expected_stream_count; + expected_gph_id = + pZ->VL53L1_p_002[pstate->rd_zone_id].expected_gph_id; + + + + + + + if (expected_stream_count != device_stream_count) { + + + + + + if (!((pdev->zone_cfg.active_zones == 0) && + (device_stream_count == 255))) + status = + VL53L1_ERROR_ZONE_STREAM_COUNT_CHECK_FAIL; + + + + + + + + + + + + + + + + + + + } + + + + + + + if (expected_gph_id != device_gph_id) + status = VL53L1_ERROR_ZONE_GPH_ID_CHECK_FAIL; + + + + + + + + + + + } + + + + + + + + + + + + + + +ENDFUNC: + LOG_FUNCTION_END(status); + return status; +} + + +VL53L1_Error VL53L1_update_ll_driver_cfg_state( + VL53L1_DEV Dev) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + VL53L1_ll_driver_state_t *pstate = &(pdev->ll_state); + VL53L1_zone_private_dyn_cfgs_t *pZ = &(pres->zone_dyn_cfgs); + + uint8_t prev_cfg_zone_id; + uint8_t prev_cfg_gph_id; + uint8_t prev_cfg_stream_count; + + LOG_FUNCTION_START(""); + + + + + + + + + if ((pdev->sys_ctrl.system__mode_start & + VL53L1_DEVICEMEASUREMENTMODE_MODE_MASK) == 0x00) { + + pstate->cfg_device_state = VL53L1_DEVICESTATE_SW_STANDBY; + pstate->cfg_stream_count = 0; + pstate->cfg_internal_stream_count = 0; + pstate->cfg_internal_stream_count_val = 0; + pstate->cfg_gph_id = VL53L1_GROUPEDPARAMETERHOLD_ID_MASK; + pstate->cfg_timing_status = 0; + pstate->cfg_zone_id = 0; + prev_cfg_zone_id = 0; + prev_cfg_gph_id = 0; + prev_cfg_stream_count = 0; + + } else { + + + + + prev_cfg_gph_id = pstate->cfg_gph_id; + prev_cfg_zone_id = pstate->cfg_zone_id; + prev_cfg_stream_count = pstate->cfg_stream_count; + + + + + + + if (pstate->cfg_stream_count == 0xFF) + pstate->cfg_stream_count = 0x80; + else + pstate->cfg_stream_count++; + + + + + + status = VL53L1_update_internal_stream_counters( + Dev, + pstate->cfg_stream_count, + &(pstate->cfg_internal_stream_count), + &(pstate->cfg_internal_stream_count_val)); + + + + + + + pstate->cfg_gph_id ^= VL53L1_GROUPEDPARAMETERHOLD_ID_MASK; + + + + + + + switch (pstate->cfg_device_state) { + + case VL53L1_DEVICESTATE_SW_STANDBY: + pstate->cfg_zone_id = 1; + if (pstate->cfg_zone_id > + pdev->zone_cfg.active_zones) { + pstate->cfg_zone_id = 0; + pstate->cfg_timing_status ^= 0x01; + } + pstate->cfg_stream_count = 1; + + if (pdev->gen_cfg.global_config__stream_divider == 0) { + pstate->cfg_internal_stream_count = 1; + pstate->cfg_internal_stream_count_val = 0; + } else { + pstate->cfg_internal_stream_count = 0; + pstate->cfg_internal_stream_count_val = 1; + } + pstate->cfg_device_state = + VL53L1_DEVICESTATE_RANGING_DSS_AUTO; + break; + + case VL53L1_DEVICESTATE_RANGING_DSS_AUTO: + pstate->cfg_zone_id++; + if (pstate->cfg_zone_id > + pdev->zone_cfg.active_zones) { + + pstate->cfg_zone_id = 0; + pstate->cfg_timing_status ^= 0x01; + + + + + + + if (pdev->zone_cfg.active_zones > 0) { + pstate->cfg_device_state = + VL53L1_DEVICESTATE_RANGING_DSS_MANUAL; + } + } + break; + + case VL53L1_DEVICESTATE_RANGING_DSS_MANUAL: + pstate->cfg_zone_id++; + if (pstate->cfg_zone_id > + pdev->zone_cfg.active_zones) { + pstate->cfg_zone_id = 0; + pstate->cfg_timing_status ^= 0x01; + } + break; + + default: + pstate->cfg_device_state = + VL53L1_DEVICESTATE_SW_STANDBY; + pstate->cfg_stream_count = 0; + pstate->cfg_internal_stream_count = 0; + pstate->cfg_internal_stream_count_val = 0; + pstate->cfg_gph_id = + VL53L1_GROUPEDPARAMETERHOLD_ID_MASK; + pstate->cfg_timing_status = 0; + pstate->cfg_zone_id = 0; + break; + } + } + + + + + + if (pdev->zone_cfg.active_zones == 0) { + + + + + + pZ->VL53L1_p_002[prev_cfg_zone_id].expected_stream_count + = prev_cfg_stream_count - 1; + + pZ->VL53L1_p_002[pstate->rd_zone_id].expected_gph_id = + prev_cfg_gph_id ^ VL53L1_GROUPEDPARAMETERHOLD_ID_MASK; + } else { + pZ->VL53L1_p_002[prev_cfg_zone_id].expected_stream_count + = prev_cfg_stream_count; + pZ->VL53L1_p_002[prev_cfg_zone_id].expected_gph_id = + prev_cfg_gph_id; + } + + + + + + LOG_FUNCTION_END(status); + + return status; +} + + +void VL53L1_copy_rtn_good_spads_to_buffer( + VL53L1_nvm_copy_data_t *pdata, + uint8_t *pbuffer) +{ + + + + + + *(pbuffer + 0) = pdata->global_config__spad_enables_rtn_0; + *(pbuffer + 1) = pdata->global_config__spad_enables_rtn_1; + *(pbuffer + 2) = pdata->global_config__spad_enables_rtn_2; + *(pbuffer + 3) = pdata->global_config__spad_enables_rtn_3; + *(pbuffer + 4) = pdata->global_config__spad_enables_rtn_4; + *(pbuffer + 5) = pdata->global_config__spad_enables_rtn_5; + *(pbuffer + 6) = pdata->global_config__spad_enables_rtn_6; + *(pbuffer + 7) = pdata->global_config__spad_enables_rtn_7; + *(pbuffer + 8) = pdata->global_config__spad_enables_rtn_8; + *(pbuffer + 9) = pdata->global_config__spad_enables_rtn_9; + *(pbuffer + 10) = pdata->global_config__spad_enables_rtn_10; + *(pbuffer + 11) = pdata->global_config__spad_enables_rtn_11; + *(pbuffer + 12) = pdata->global_config__spad_enables_rtn_12; + *(pbuffer + 13) = pdata->global_config__spad_enables_rtn_13; + *(pbuffer + 14) = pdata->global_config__spad_enables_rtn_14; + *(pbuffer + 15) = pdata->global_config__spad_enables_rtn_15; + *(pbuffer + 16) = pdata->global_config__spad_enables_rtn_16; + *(pbuffer + 17) = pdata->global_config__spad_enables_rtn_17; + *(pbuffer + 18) = pdata->global_config__spad_enables_rtn_18; + *(pbuffer + 19) = pdata->global_config__spad_enables_rtn_19; + *(pbuffer + 20) = pdata->global_config__spad_enables_rtn_20; + *(pbuffer + 21) = pdata->global_config__spad_enables_rtn_21; + *(pbuffer + 22) = pdata->global_config__spad_enables_rtn_22; + *(pbuffer + 23) = pdata->global_config__spad_enables_rtn_23; + *(pbuffer + 24) = pdata->global_config__spad_enables_rtn_24; + *(pbuffer + 25) = pdata->global_config__spad_enables_rtn_25; + *(pbuffer + 26) = pdata->global_config__spad_enables_rtn_26; + *(pbuffer + 27) = pdata->global_config__spad_enables_rtn_27; + *(pbuffer + 28) = pdata->global_config__spad_enables_rtn_28; + *(pbuffer + 29) = pdata->global_config__spad_enables_rtn_29; + *(pbuffer + 30) = pdata->global_config__spad_enables_rtn_30; + *(pbuffer + 31) = pdata->global_config__spad_enables_rtn_31; +} + + +void VL53L1_init_system_results( + VL53L1_system_results_t *pdata) +{ + + + + + + + pdata->result__interrupt_status = 0xFF; + pdata->result__range_status = 0xFF; + pdata->result__report_status = 0xFF; + pdata->result__stream_count = 0xFF; + + pdata->result__dss_actual_effective_spads_sd0 = 0xFFFF; + pdata->result__peak_signal_count_rate_mcps_sd0 = 0xFFFF; + pdata->result__ambient_count_rate_mcps_sd0 = 0xFFFF; + pdata->result__sigma_sd0 = 0xFFFF; + pdata->result__phase_sd0 = 0xFFFF; + pdata->result__final_crosstalk_corrected_range_mm_sd0 = 0xFFFF; + pdata->result__peak_signal_count_rate_crosstalk_corrected_mcps_sd0 = + 0xFFFF; + pdata->result__mm_inner_actual_effective_spads_sd0 = 0xFFFF; + pdata->result__mm_outer_actual_effective_spads_sd0 = 0xFFFF; + pdata->result__avg_signal_count_rate_mcps_sd0 = 0xFFFF; + + pdata->result__dss_actual_effective_spads_sd1 = 0xFFFF; + pdata->result__peak_signal_count_rate_mcps_sd1 = 0xFFFF; + pdata->result__ambient_count_rate_mcps_sd1 = 0xFFFF; + pdata->result__sigma_sd1 = 0xFFFF; + pdata->result__phase_sd1 = 0xFFFF; + pdata->result__final_crosstalk_corrected_range_mm_sd1 = 0xFFFF; + pdata->result__spare_0_sd1 = 0xFFFF; + pdata->result__spare_1_sd1 = 0xFFFF; + pdata->result__spare_2_sd1 = 0xFFFF; + pdata->result__spare_3_sd1 = 0xFF; + +} + + +void V53L1_init_zone_results_structure( + uint8_t active_zones, + VL53L1_zone_results_t *pdata) +{ + + + + + uint8_t z = 0; + VL53L1_zone_objects_t *pobjects; + + pdata->max_zones = VL53L1_MAX_USER_ZONES; + pdata->active_zones = active_zones; + + for (z = 0; z < pdata->max_zones; z++) { + pobjects = &(pdata->VL53L1_p_002[z]); + pobjects->cfg_device_state = VL53L1_DEVICESTATE_SW_STANDBY; + pobjects->rd_device_state = VL53L1_DEVICESTATE_SW_STANDBY; + pobjects->max_objects = VL53L1_MAX_RANGE_RESULTS; + pobjects->active_objects = 0; + } +} + +void V53L1_init_zone_dss_configs( + VL53L1_DEV Dev) +{ + + + + + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + uint8_t z = 0; + uint8_t max_zones = VL53L1_MAX_USER_ZONES; + VL53L1_zone_private_dyn_cfgs_t *pdata = &(pres->zone_dyn_cfgs); + + for (z = 0; z < max_zones; z++) { + pdata->VL53L1_p_002[z].dss_mode = + VL53L1_DSS_CONTROL__MODE_TARGET_RATE; + pdata->VL53L1_p_002[z].dss_requested_effective_spad_count = 0; + } +} + + +void VL53L1_init_histogram_config_structure( + uint8_t even_bin0, + uint8_t even_bin1, + uint8_t even_bin2, + uint8_t even_bin3, + uint8_t even_bin4, + uint8_t even_bin5, + uint8_t odd_bin0, + uint8_t odd_bin1, + uint8_t odd_bin2, + uint8_t odd_bin3, + uint8_t odd_bin4, + uint8_t odd_bin5, + VL53L1_histogram_config_t *pdata) +{ + + + + + + + pdata->histogram_config__low_amb_even_bin_0_1 = + (even_bin1 << 4) + even_bin0; + pdata->histogram_config__low_amb_even_bin_2_3 = + (even_bin3 << 4) + even_bin2; + pdata->histogram_config__low_amb_even_bin_4_5 = + (even_bin5 << 4) + even_bin4; + + pdata->histogram_config__low_amb_odd_bin_0_1 = + (odd_bin1 << 4) + odd_bin0; + pdata->histogram_config__low_amb_odd_bin_2_3 = + (odd_bin3 << 4) + odd_bin2; + pdata->histogram_config__low_amb_odd_bin_4_5 = + (odd_bin5 << 4) + odd_bin4; + + pdata->histogram_config__mid_amb_even_bin_0_1 = + pdata->histogram_config__low_amb_even_bin_0_1; + pdata->histogram_config__mid_amb_even_bin_2_3 = + pdata->histogram_config__low_amb_even_bin_2_3; + pdata->histogram_config__mid_amb_even_bin_4_5 = + pdata->histogram_config__low_amb_even_bin_4_5; + + pdata->histogram_config__mid_amb_odd_bin_0_1 = + pdata->histogram_config__low_amb_odd_bin_0_1; + pdata->histogram_config__mid_amb_odd_bin_2 = odd_bin2; + pdata->histogram_config__mid_amb_odd_bin_3_4 = + (odd_bin4 << 4) + odd_bin3; + pdata->histogram_config__mid_amb_odd_bin_5 = odd_bin5; + + pdata->histogram_config__user_bin_offset = 0x00; + + pdata->histogram_config__high_amb_even_bin_0_1 = + pdata->histogram_config__low_amb_even_bin_0_1; + pdata->histogram_config__high_amb_even_bin_2_3 = + pdata->histogram_config__low_amb_even_bin_2_3; + pdata->histogram_config__high_amb_even_bin_4_5 = + pdata->histogram_config__low_amb_even_bin_4_5; + + pdata->histogram_config__high_amb_odd_bin_0_1 = + pdata->histogram_config__low_amb_odd_bin_0_1; + pdata->histogram_config__high_amb_odd_bin_2_3 = + pdata->histogram_config__low_amb_odd_bin_2_3; + pdata->histogram_config__high_amb_odd_bin_4_5 = + pdata->histogram_config__low_amb_odd_bin_4_5; + + + + + pdata->histogram_config__amb_thresh_low = 0xFFFF; + pdata->histogram_config__amb_thresh_high = 0xFFFF; + + + + + pdata->histogram_config__spad_array_selection = 0x00; + +} + +void VL53L1_init_histogram_multizone_config_structure( + uint8_t even_bin0, + uint8_t even_bin1, + uint8_t even_bin2, + uint8_t even_bin3, + uint8_t even_bin4, + uint8_t even_bin5, + uint8_t odd_bin0, + uint8_t odd_bin1, + uint8_t odd_bin2, + uint8_t odd_bin3, + uint8_t odd_bin4, + uint8_t odd_bin5, + VL53L1_histogram_config_t *pdata) +{ + + + + + + + + pdata->histogram_config__low_amb_even_bin_0_1 = + (even_bin1 << 4) + even_bin0; + pdata->histogram_config__low_amb_even_bin_2_3 = + (even_bin3 << 4) + even_bin2; + pdata->histogram_config__low_amb_even_bin_4_5 = + (even_bin5 << 4) + even_bin4; + + pdata->histogram_config__low_amb_odd_bin_0_1 = + pdata->histogram_config__low_amb_even_bin_0_1; + pdata->histogram_config__low_amb_odd_bin_2_3 + = pdata->histogram_config__low_amb_even_bin_2_3; + pdata->histogram_config__low_amb_odd_bin_4_5 + = pdata->histogram_config__low_amb_even_bin_4_5; + + pdata->histogram_config__mid_amb_even_bin_0_1 = + pdata->histogram_config__low_amb_even_bin_0_1; + pdata->histogram_config__mid_amb_even_bin_2_3 + = pdata->histogram_config__low_amb_even_bin_2_3; + pdata->histogram_config__mid_amb_even_bin_4_5 + = pdata->histogram_config__low_amb_even_bin_4_5; + + pdata->histogram_config__mid_amb_odd_bin_0_1 + = pdata->histogram_config__low_amb_odd_bin_0_1; + pdata->histogram_config__mid_amb_odd_bin_2 = odd_bin2; + pdata->histogram_config__mid_amb_odd_bin_3_4 = + (odd_bin4 << 4) + odd_bin3; + pdata->histogram_config__mid_amb_odd_bin_5 = odd_bin5; + + pdata->histogram_config__user_bin_offset = 0x00; + + pdata->histogram_config__high_amb_even_bin_0_1 = + (odd_bin1 << 4) + odd_bin0; + pdata->histogram_config__high_amb_even_bin_2_3 = + (odd_bin3 << 4) + odd_bin2; + pdata->histogram_config__high_amb_even_bin_4_5 = + (odd_bin5 << 4) + odd_bin4; + + pdata->histogram_config__high_amb_odd_bin_0_1 + = pdata->histogram_config__high_amb_even_bin_0_1; + pdata->histogram_config__high_amb_odd_bin_2_3 + = pdata->histogram_config__high_amb_even_bin_2_3; + pdata->histogram_config__high_amb_odd_bin_4_5 + = pdata->histogram_config__high_amb_even_bin_4_5; + + + + + pdata->histogram_config__amb_thresh_low = 0xFFFF; + pdata->histogram_config__amb_thresh_high = 0xFFFF; + + + + + pdata->histogram_config__spad_array_selection = 0x00; +} + + +void VL53L1_init_xtalk_bin_data_struct( + uint32_t bin_value, + uint16_t VL53L1_p_024, + VL53L1_xtalk_histogram_shape_t *pdata) +{ + + + + + + + uint16_t i = 0; + + pdata->zone_id = 0; + pdata->time_stamp = 0; + + pdata->VL53L1_p_022 = 0; + pdata->VL53L1_p_023 = VL53L1_XTALK_HISTO_BINS; + pdata->VL53L1_p_024 = (uint8_t)VL53L1_p_024; + + pdata->phasecal_result__reference_phase = 0; + pdata->phasecal_result__vcsel_start = 0; + pdata->cal_config__vcsel_start = 0; + + pdata->vcsel_width = 0; + pdata->VL53L1_p_019 = 0; + + pdata->zero_distance_phase = 0; + + for (i = 0; i < VL53L1_XTALK_HISTO_BINS; i++) { + if (i < VL53L1_p_024) + pdata->bin_data[i] = bin_value; + else + pdata->bin_data[i] = 0; + } +} + + +void VL53L1_i2c_encode_uint16_t( + uint16_t ip_value, + uint16_t count, + uint8_t *pbuffer) +{ + + + + + + + uint16_t i = 0; + uint16_t VL53L1_p_002 = 0; + + VL53L1_p_002 = ip_value; + + for (i = 0; i < count; i++) { + pbuffer[count-i-1] = (uint8_t)(VL53L1_p_002 & 0x00FF); + VL53L1_p_002 = VL53L1_p_002 >> 8; + } +} + +uint16_t VL53L1_i2c_decode_uint16_t( + uint16_t count, + uint8_t *pbuffer) +{ + + + + + + + uint16_t value = 0x00; + + while (count-- > 0) + value = (value << 8) | (uint16_t)*pbuffer++; + + return value; +} + + +void VL53L1_i2c_encode_int16_t( + int16_t ip_value, + uint16_t count, + uint8_t *pbuffer) +{ + + + + + + + uint16_t i = 0; + int16_t VL53L1_p_002 = 0; + + VL53L1_p_002 = ip_value; + + for (i = 0; i < count; i++) { + pbuffer[count-i-1] = (uint8_t)(VL53L1_p_002 & 0x00FF); + VL53L1_p_002 = VL53L1_p_002 >> 8; + } +} + +int16_t VL53L1_i2c_decode_int16_t( + uint16_t count, + uint8_t *pbuffer) +{ + + + + + + + int16_t value = 0x00; + + + + if (*pbuffer >= 0x80) + value = 0xFFFF; + + while (count-- > 0) + value = (value << 8) | (int16_t)*pbuffer++; + + return value; +} + +void VL53L1_i2c_encode_uint32_t( + uint32_t ip_value, + uint16_t count, + uint8_t *pbuffer) +{ + + + + + + + uint16_t i = 0; + uint32_t VL53L1_p_002 = 0; + + VL53L1_p_002 = ip_value; + + for (i = 0; i < count; i++) { + pbuffer[count-i-1] = (uint8_t)(VL53L1_p_002 & 0x00FF); + VL53L1_p_002 = VL53L1_p_002 >> 8; + } +} + +uint32_t VL53L1_i2c_decode_uint32_t( + uint16_t count, + uint8_t *pbuffer) +{ + + + + + + + uint32_t value = 0x00; + + while (count-- > 0) + value = (value << 8) | (uint32_t)*pbuffer++; + + return value; +} + + +uint32_t VL53L1_i2c_decode_with_mask( + uint16_t count, + uint8_t *pbuffer, + uint32_t bit_mask, + uint32_t down_shift, + uint32_t offset) +{ + + + + + + + uint32_t value = 0x00; + + + + while (count-- > 0) + value = (value << 8) | (uint32_t)*pbuffer++; + + + + value = value & bit_mask; + if (down_shift > 0) + value = value >> down_shift; + + + + value = value + offset; + + return value; +} + + +void VL53L1_i2c_encode_int32_t( + int32_t ip_value, + uint16_t count, + uint8_t *pbuffer) +{ + + + + + + + uint16_t i = 0; + int32_t VL53L1_p_002 = 0; + + VL53L1_p_002 = ip_value; + + for (i = 0; i < count; i++) { + pbuffer[count-i-1] = (uint8_t)(VL53L1_p_002 & 0x00FF); + VL53L1_p_002 = VL53L1_p_002 >> 8; + } +} + +int32_t VL53L1_i2c_decode_int32_t( + uint16_t count, + uint8_t *pbuffer) +{ + + + + + + + int32_t value = 0x00; + + + + if (*pbuffer >= 0x80) + value = 0xFFFFFFFF; + + while (count-- > 0) + value = (value << 8) | (int32_t)*pbuffer++; + + return value; +} + + +VL53L1_Error VL53L1_start_test( + VL53L1_DEV Dev, + uint8_t test_mode__ctrl) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) { + + status = VL53L1_WrByte( + Dev, + VL53L1_TEST_MODE__CTRL, + test_mode__ctrl); + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_firmware_enable_register( + VL53L1_DEV Dev, + uint8_t value) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + pdev->sys_ctrl.firmware__enable = value; + + status = VL53L1_WrByte( + Dev, + VL53L1_FIRMWARE__ENABLE, + pdev->sys_ctrl.firmware__enable); + + return status; +} + +VL53L1_Error VL53L1_enable_firmware( + VL53L1_DEV Dev) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + status = VL53L1_set_firmware_enable_register(Dev, 0x01); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_disable_firmware( + VL53L1_DEV Dev) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + status = VL53L1_set_firmware_enable_register(Dev, 0x00); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_powerforce_register( + VL53L1_DEV Dev, + uint8_t value) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + pdev->sys_ctrl.power_management__go1_power_force = value; + + status = VL53L1_WrByte( + Dev, + VL53L1_POWER_MANAGEMENT__GO1_POWER_FORCE, + pdev->sys_ctrl.power_management__go1_power_force); + + return status; +} + + +VL53L1_Error VL53L1_enable_powerforce( + VL53L1_DEV Dev) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + status = VL53L1_set_powerforce_register(Dev, 0x01); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_disable_powerforce( + VL53L1_DEV Dev) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + status = VL53L1_set_powerforce_register(Dev, 0x00); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_clear_interrupt( + VL53L1_DEV Dev) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->sys_ctrl.system__interrupt_clear = VL53L1_CLEAR_RANGE_INT; + + status = VL53L1_WrByte( + Dev, + VL53L1_SYSTEM__INTERRUPT_CLEAR, + pdev->sys_ctrl.system__interrupt_clear); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_force_shadow_stream_count_to_zero( + VL53L1_DEV Dev) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_WrByte( + Dev, + VL53L1_SHADOW_RESULT__STREAM_COUNT, + 0x00); + } + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + return status; +} + + +uint32_t VL53L1_calc_macro_period_us( + uint16_t fast_osc_frequency, + uint8_t VL53L1_p_009) +{ + + + + + + + + + uint32_t pll_period_us = 0; + uint8_t VL53L1_p_031 = 0; + uint32_t macro_period_us = 0; + + LOG_FUNCTION_START(""); + + + + + + + pll_period_us = VL53L1_calc_pll_period_us(fast_osc_frequency); + + + + + + + VL53L1_p_031 = VL53L1_decode_vcsel_period(VL53L1_p_009); + + + + + + + + + + + + + + + macro_period_us = + (uint32_t)VL53L1_MACRO_PERIOD_VCSEL_PERIODS * + pll_period_us; + macro_period_us = macro_period_us >> 6; + + macro_period_us = macro_period_us * (uint32_t)VL53L1_p_031; + macro_period_us = macro_period_us >> 6; + + + + + + + + + + + + + + LOG_FUNCTION_END(0); + + return macro_period_us; +} + + +uint16_t VL53L1_calc_range_ignore_threshold( + uint32_t central_rate, + int16_t x_gradient, + int16_t y_gradient, + uint8_t rate_mult) +{ + + + + + + + + + + + + + + + + int32_t range_ignore_thresh_int = 0; + uint16_t range_ignore_thresh_kcps = 0; + int32_t central_rate_int = 0; + int16_t x_gradient_int = 0; + int16_t y_gradient_int = 0; + + LOG_FUNCTION_START(""); + + + + + central_rate_int = ((int32_t)central_rate * (1 << 4)) / (1000); + + if (x_gradient < 0) + x_gradient_int = x_gradient * -1; + + if (y_gradient < 0) + y_gradient_int = y_gradient * -1; + + + + + + + + + range_ignore_thresh_int = (8 * x_gradient_int * 4) + + (8 * y_gradient_int * 4); + + + + + range_ignore_thresh_int = range_ignore_thresh_int / 1000; + + + + + range_ignore_thresh_int = range_ignore_thresh_int + central_rate_int; + + + + + range_ignore_thresh_int = (int32_t)rate_mult * range_ignore_thresh_int; + + range_ignore_thresh_int = (range_ignore_thresh_int + (1<<4)) / (1<<5); + + + + + if (range_ignore_thresh_int > 0xFFFF) + range_ignore_thresh_kcps = 0xFFFF; + else + range_ignore_thresh_kcps = (uint16_t)range_ignore_thresh_int; + + + + + + + + LOG_FUNCTION_END(0); + + return range_ignore_thresh_kcps; +} + + +uint32_t VL53L1_calc_timeout_mclks( + uint32_t timeout_us, + uint32_t macro_period_us) +{ + + + + + + + + + + + uint32_t timeout_mclks = 0; + + LOG_FUNCTION_START(""); + + timeout_mclks = + ((timeout_us << 12) + (macro_period_us>>1)) / + macro_period_us; + + LOG_FUNCTION_END(0); + + return timeout_mclks; +} + + +uint16_t VL53L1_calc_encoded_timeout( + uint32_t timeout_us, + uint32_t macro_period_us) +{ + + + + + + + + + + + uint32_t timeout_mclks = 0; + uint16_t timeout_encoded = 0; + + LOG_FUNCTION_START(""); + + timeout_mclks = + VL53L1_calc_timeout_mclks(timeout_us, macro_period_us); + + timeout_encoded = + VL53L1_encode_timeout(timeout_mclks); + + + + + + + + + + + LOG_FUNCTION_END(0); + + return timeout_encoded; +} + + +uint32_t VL53L1_calc_timeout_us( + uint32_t timeout_mclks, + uint32_t macro_period_us) +{ + + + + + + + + + + + uint32_t timeout_us = 0; + uint64_t tmp = 0; + + LOG_FUNCTION_START(""); + + tmp = (uint64_t)timeout_mclks * (uint64_t)macro_period_us; + tmp += 0x00800; + tmp = tmp >> 12; + + timeout_us = (uint32_t)tmp; + + + + + + + + + + + + LOG_FUNCTION_END(0); + + return timeout_us; +} + +uint32_t VL53L1_calc_crosstalk_plane_offset_with_margin( + uint32_t plane_offset_kcps, + int16_t margin_offset_kcps) +{ + uint32_t plane_offset_with_margin = 0; + int32_t plane_offset_kcps_temp = 0; + + LOG_FUNCTION_START(""); + + plane_offset_kcps_temp = + (int32_t)plane_offset_kcps + + (int32_t)margin_offset_kcps; + + if (plane_offset_kcps_temp < 0) + plane_offset_kcps_temp = 0; + else + if (plane_offset_kcps_temp > 0x3FFFF) + plane_offset_kcps_temp = 0x3FFFF; + + plane_offset_with_margin = (uint32_t) plane_offset_kcps_temp; + + LOG_FUNCTION_END(0); + + return plane_offset_with_margin; + +} + +uint32_t VL53L1_calc_decoded_timeout_us( + uint16_t timeout_encoded, + uint32_t macro_period_us) +{ + + + + + + + + + + + uint32_t timeout_mclks = 0; + uint32_t timeout_us = 0; + + LOG_FUNCTION_START(""); + + timeout_mclks = + VL53L1_decode_timeout(timeout_encoded); + + timeout_us = + VL53L1_calc_timeout_us(timeout_mclks, macro_period_us); + + LOG_FUNCTION_END(0); + + return timeout_us; +} + + +uint16_t VL53L1_encode_timeout(uint32_t timeout_mclks) +{ + + + + + + uint16_t encoded_timeout = 0; + uint32_t ls_byte = 0; + uint16_t ms_byte = 0; + + if (timeout_mclks > 0) { + ls_byte = timeout_mclks - 1; + + while ((ls_byte & 0xFFFFFF00) > 0) { + ls_byte = ls_byte >> 1; + ms_byte++; + } + + encoded_timeout = (ms_byte << 8) + + (uint16_t) (ls_byte & 0x000000FF); + } + + return encoded_timeout; +} + + +uint32_t VL53L1_decode_timeout(uint16_t encoded_timeout) +{ + + + + + + + uint32_t timeout_macro_clks = 0; + + timeout_macro_clks = ((uint32_t) (encoded_timeout & 0x00FF) + << (uint32_t) ((encoded_timeout & 0xFF00) >> 8)) + 1; + + return timeout_macro_clks; +} + + +VL53L1_Error VL53L1_calc_timeout_register_values( + uint32_t phasecal_config_timeout_us, + uint32_t mm_config_timeout_us, + uint32_t range_config_timeout_us, + uint16_t fast_osc_frequency, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint32_t macro_period_us = 0; + uint32_t timeout_mclks = 0; + uint16_t timeout_encoded = 0; + + LOG_FUNCTION_START(""); + + if (fast_osc_frequency == 0) { + status = VL53L1_ERROR_DIVISION_BY_ZERO; + } else { + + + macro_period_us = + VL53L1_calc_macro_period_us( + fast_osc_frequency, + ptiming->range_config__vcsel_period_a); + + + + timeout_mclks = + VL53L1_calc_timeout_mclks( + phasecal_config_timeout_us, + macro_period_us); + + + + if (timeout_mclks > 0xFF) + timeout_mclks = 0xFF; + + pgeneral->phasecal_config__timeout_macrop = + (uint8_t)timeout_mclks; + + + + timeout_encoded = + VL53L1_calc_encoded_timeout( + mm_config_timeout_us, + macro_period_us); + + ptiming->mm_config__timeout_macrop_a_hi = + (uint8_t)((timeout_encoded & 0xFF00) >> 8); + ptiming->mm_config__timeout_macrop_a_lo = + (uint8_t) (timeout_encoded & 0x00FF); + + + + timeout_encoded = + VL53L1_calc_encoded_timeout( + range_config_timeout_us, + macro_period_us); + + ptiming->range_config__timeout_macrop_a_hi = + (uint8_t)((timeout_encoded & 0xFF00) >> 8); + ptiming->range_config__timeout_macrop_a_lo = + (uint8_t) (timeout_encoded & 0x00FF); + + + + macro_period_us = + VL53L1_calc_macro_period_us( + fast_osc_frequency, + ptiming->range_config__vcsel_period_b); + + + + timeout_encoded = + VL53L1_calc_encoded_timeout( + mm_config_timeout_us, + macro_period_us); + + ptiming->mm_config__timeout_macrop_b_hi = + (uint8_t)((timeout_encoded & 0xFF00) >> 8); + ptiming->mm_config__timeout_macrop_b_lo = + (uint8_t) (timeout_encoded & 0x00FF); + + + + timeout_encoded = VL53L1_calc_encoded_timeout( + range_config_timeout_us, + macro_period_us); + + ptiming->range_config__timeout_macrop_b_hi = + (uint8_t)((timeout_encoded & 0xFF00) >> 8); + ptiming->range_config__timeout_macrop_b_lo = + (uint8_t) (timeout_encoded & 0x00FF); + } + + LOG_FUNCTION_END(0); + + return status; + +} + + +uint8_t VL53L1_encode_vcsel_period(uint8_t VL53L1_p_031) +{ + + + + + + + uint8_t vcsel_period_reg = 0; + + vcsel_period_reg = (VL53L1_p_031 >> 1) - 1; + + return vcsel_period_reg; +} + + +uint32_t VL53L1_decode_unsigned_integer( + uint8_t *pbuffer, + uint8_t no_of_bytes) +{ + + + + + + uint8_t i = 0; + uint32_t decoded_value = 0; + + for (i = 0; i < no_of_bytes; i++) + decoded_value = (decoded_value << 8) + (uint32_t)pbuffer[i]; + + return decoded_value; +} + + +void VL53L1_encode_unsigned_integer( + uint32_t ip_value, + uint8_t no_of_bytes, + uint8_t *pbuffer) +{ + + + + + + uint8_t i = 0; + uint32_t VL53L1_p_002 = 0; + + VL53L1_p_002 = ip_value; + for (i = 0; i < no_of_bytes; i++) { + pbuffer[no_of_bytes-i-1] = VL53L1_p_002 & 0x00FF; + VL53L1_p_002 = VL53L1_p_002 >> 8; + } +} + + +VL53L1_Error VL53L1_hist_copy_and_scale_ambient_info( + VL53L1_zone_hist_info_t *pidata, + VL53L1_histogram_bin_data_t *podata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + int64_t VL53L1_p_008 = 0; + int64_t tmpi = 0; + int64_t tmpo = 0; + + LOG_FUNCTION_START(""); + + + if (pidata->result__dss_actual_effective_spads == 0) { + status = VL53L1_ERROR_DIVISION_BY_ZERO; + } else { + if (pidata->number_of_ambient_bins > 0 && + podata->number_of_ambient_bins == 0) { + + + + + + + + + + + tmpo = 1 + (int64_t)podata->total_periods_elapsed; + tmpo *= + (int64_t)podata->result__dss_actual_effective_spads; + + tmpi = 1 + (int64_t)pidata->total_periods_elapsed; + tmpi *= + (int64_t)pidata->result__dss_actual_effective_spads; + + VL53L1_p_008 = tmpo * + (int64_t)pidata->ambient_events_sum; + VL53L1_p_008 += (tmpi/2); + + + + + VL53L1_p_008 = do_division_s(VL53L1_p_008, tmpi); + + podata->ambient_events_sum = (int32_t)VL53L1_p_008; + + + + + + + podata->VL53L1_p_004 = + podata->ambient_events_sum; + podata->VL53L1_p_004 += + ((int32_t)pidata->number_of_ambient_bins / 2); + podata->VL53L1_p_004 /= + (int32_t)pidata->number_of_ambient_bins; + } + } + + LOG_FUNCTION_END(0); + + return status; +} + + +void VL53L1_hist_get_bin_sequence_config( + VL53L1_DEV Dev, + VL53L1_histogram_bin_data_t *pdata) +{ + + + + + + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + int32_t amb_thresh_low = 0; + int32_t amb_thresh_high = 0; + + uint8_t i = 0; + + LOG_FUNCTION_START(""); + + + + + amb_thresh_low = 1024 * + (int32_t)pdev->hist_cfg.histogram_config__amb_thresh_low; + amb_thresh_high = 1024 * + (int32_t)pdev->hist_cfg.histogram_config__amb_thresh_high; + + + + + + + + + + + if ((pdev->ll_state.rd_stream_count & 0x01) == 0) { + + pdata->bin_seq[5] = + pdev->hist_cfg.histogram_config__mid_amb_even_bin_4_5 >> 4; + pdata->bin_seq[4] = + pdev->hist_cfg.histogram_config__mid_amb_even_bin_4_5 & 0x0F; + pdata->bin_seq[3] = + pdev->hist_cfg.histogram_config__mid_amb_even_bin_2_3 >> 4; + pdata->bin_seq[2] = + pdev->hist_cfg.histogram_config__mid_amb_even_bin_2_3 & 0x0F; + pdata->bin_seq[1] = + pdev->hist_cfg.histogram_config__mid_amb_even_bin_0_1 >> 4; + pdata->bin_seq[0] = + pdev->hist_cfg.histogram_config__mid_amb_even_bin_0_1 & 0x0F; + + if (pdata->ambient_events_sum > amb_thresh_high) { + pdata->bin_seq[5] = + pdev->hist_cfg.histogram_config__high_amb_even_bin_4_5 + >> 4; + pdata->bin_seq[4] = + pdev->hist_cfg.histogram_config__high_amb_even_bin_4_5 + & 0x0F; + pdata->bin_seq[3] = + pdev->hist_cfg.histogram_config__high_amb_even_bin_2_3 + >> 4; + pdata->bin_seq[2] = + pdev->hist_cfg.histogram_config__high_amb_even_bin_2_3 + & 0x0F; + pdata->bin_seq[1] = + pdev->hist_cfg.histogram_config__high_amb_even_bin_0_1 + >> 4; + pdata->bin_seq[0] = + pdev->hist_cfg.histogram_config__high_amb_even_bin_0_1 + & 0x0F; + } + + if (pdata->ambient_events_sum < amb_thresh_low) { + pdata->bin_seq[5] = + pdev->hist_cfg.histogram_config__low_amb_even_bin_4_5 + >> 4; + pdata->bin_seq[4] = + pdev->hist_cfg.histogram_config__low_amb_even_bin_4_5 + & 0x0F; + pdata->bin_seq[3] = + pdev->hist_cfg.histogram_config__low_amb_even_bin_2_3 + >> 4; + pdata->bin_seq[2] = + pdev->hist_cfg.histogram_config__low_amb_even_bin_2_3 + & 0x0F; + pdata->bin_seq[1] = + pdev->hist_cfg.histogram_config__low_amb_even_bin_0_1 + >> 4; + pdata->bin_seq[0] = + pdev->hist_cfg.histogram_config__low_amb_even_bin_0_1 + & 0x0F; + } + + } else { + pdata->bin_seq[5] = + pdev->hist_cfg.histogram_config__mid_amb_odd_bin_5 + & 0x0F; + pdata->bin_seq[4] = + pdev->hist_cfg.histogram_config__mid_amb_odd_bin_3_4 + & 0x0F; + pdata->bin_seq[3] = + pdev->hist_cfg.histogram_config__mid_amb_odd_bin_3_4 + >> 4; + pdata->bin_seq[2] = + pdev->hist_cfg.histogram_config__mid_amb_odd_bin_2 & + 0x0F; + pdata->bin_seq[1] = + pdev->hist_cfg.histogram_config__mid_amb_odd_bin_0_1 + >> 4; + pdata->bin_seq[0] = + pdev->hist_cfg.histogram_config__mid_amb_odd_bin_0_1 + & 0x0F; + + if (pdata->ambient_events_sum > amb_thresh_high) { + pdata->bin_seq[5] = + pdev->hist_cfg.histogram_config__high_amb_odd_bin_4_5 + >> 4; + pdata->bin_seq[4] = + pdev->hist_cfg.histogram_config__high_amb_odd_bin_4_5 + & 0x0F; + pdata->bin_seq[3] = + pdev->hist_cfg.histogram_config__high_amb_odd_bin_2_3 + >> 4; + pdata->bin_seq[2] = + pdev->hist_cfg.histogram_config__high_amb_odd_bin_2_3 + & 0x0F; + pdata->bin_seq[1] = + pdev->hist_cfg.histogram_config__high_amb_odd_bin_0_1 + >> 4; + pdata->bin_seq[0] = + pdev->hist_cfg.histogram_config__high_amb_odd_bin_0_1 + & 0x0F; + } + + if (pdata->ambient_events_sum < amb_thresh_low) { + pdata->bin_seq[5] = + pdev->hist_cfg.histogram_config__low_amb_odd_bin_4_5 + >> 4; + pdata->bin_seq[4] = + pdev->hist_cfg.histogram_config__low_amb_odd_bin_4_5 + & 0x0F; + pdata->bin_seq[3] = + pdev->hist_cfg.histogram_config__low_amb_odd_bin_2_3 + >> 4; + pdata->bin_seq[2] = + pdev->hist_cfg.histogram_config__low_amb_odd_bin_2_3 + & 0x0F; + pdata->bin_seq[1] = + pdev->hist_cfg.histogram_config__low_amb_odd_bin_0_1 + >> 4; + pdata->bin_seq[0] = + pdev->hist_cfg.histogram_config__low_amb_odd_bin_0_1 + & 0x0F; + } + } + + + + + for (i = 0; i < VL53L1_MAX_BIN_SEQUENCE_LENGTH; i++) + pdata->bin_rep[i] = 1; + + LOG_FUNCTION_END(0); + +} + + +VL53L1_Error VL53L1_hist_phase_consistency_check( + VL53L1_DEV Dev, + VL53L1_zone_hist_info_t *phist_prev, + VL53L1_zone_objects_t *prange_prev, + VL53L1_range_results_t *prange_curr) +{ + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t lc = 0; + uint8_t p = 0; + + uint16_t phase_delta = 0; + uint16_t phase_tolerance = 0; + + int32_t events_delta = 0; + int32_t events_tolerance = 0; + + + + uint8_t event_sigma; + uint16_t event_min_spad_count; + uint16_t min_max_tolerance; + uint8_t pht; + + VL53L1_DeviceError range_status = 0; + + LOG_FUNCTION_START(""); + + event_sigma = + pdev->histpostprocess.algo__consistency_check__event_sigma; + event_min_spad_count = + pdev->histpostprocess.algo__consistency_check__event_min_spad_count; + min_max_tolerance = + pdev->histpostprocess.algo__consistency_check__min_max_tolerance; + + + + pht = pdev->histpostprocess.algo__consistency_check__phase_tolerance; + phase_tolerance = (uint16_t)pht; + phase_tolerance = phase_tolerance << 8; + + + + + if (prange_prev->rd_device_state != + VL53L1_DEVICESTATE_RANGING_GATHER_DATA && + prange_prev->rd_device_state != + VL53L1_DEVICESTATE_RANGING_OUTPUT_DATA) + return status; + + + + + if (phase_tolerance == 0) + return status; + + for (lc = 0; lc < prange_curr->active_results; lc++) { + + if (!((prange_curr->VL53L1_p_002[lc].range_status == + VL53L1_DEVICEERROR_RANGECOMPLETE) || + (prange_curr->VL53L1_p_002[lc].range_status == + VL53L1_DEVICEERROR_RANGECOMPLETE_NO_WRAP_CHECK))) + continue; + + + + + + + + + if (prange_prev->active_objects == 0) + prange_curr->VL53L1_p_002[lc].range_status = + VL53L1_DEVICEERROR_PREV_RANGE_NO_TARGETS; + else + prange_curr->VL53L1_p_002[lc].range_status = + VL53L1_DEVICEERROR_PHASECONSISTENCY; + + + + + + + + + + + for (p = 0; p < prange_prev->active_objects; p++) { + + if (prange_curr->VL53L1_p_002[lc].VL53L1_p_014 > + prange_prev->VL53L1_p_002[p].VL53L1_p_014) { + phase_delta = + prange_curr->VL53L1_p_002[lc].VL53L1_p_014 - + prange_prev->VL53L1_p_002[p].VL53L1_p_014; + } else { + phase_delta = + prange_prev->VL53L1_p_002[p].VL53L1_p_014 - + prange_curr->VL53L1_p_002[lc].VL53L1_p_014; + } + + if (phase_delta < phase_tolerance) { + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_hist_events_consistency_check( + event_sigma, + event_min_spad_count, + phist_prev, + &(prange_prev->VL53L1_p_002[p]), + &(prange_curr->VL53L1_p_002[lc]), + &events_tolerance, + &events_delta, + &range_status); + + + + + + + + + + if (status == VL53L1_ERROR_NONE && + range_status == + VL53L1_DEVICEERROR_RANGECOMPLETE) + status = + VL53L1_hist_merged_pulse_check( + min_max_tolerance, + &(prange_curr->VL53L1_p_002[lc]), + &range_status); + + prange_curr->VL53L1_p_002[lc].range_status = + range_status; + } + } + + } + + LOG_FUNCTION_END(status); + + return status; +} + + + + +VL53L1_Error VL53L1_hist_events_consistency_check( + uint8_t event_sigma, + uint16_t min_effective_spad_count, + VL53L1_zone_hist_info_t *phist_prev, + VL53L1_object_data_t *prange_prev, + VL53L1_range_data_t *prange_curr, + int32_t *pevents_tolerance, + int32_t *pevents_delta, + VL53L1_DeviceError *prange_status) +{ + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + int64_t tmpp = 0; + int64_t tmpc = 0; + int64_t events_scaler = 0; + int64_t events_scaler_sq = 0; + int64_t c_signal_events = 0; + int64_t c_sig_noise_sq = 0; + int64_t c_amb_noise_sq = 0; + int64_t p_amb_noise_sq = 0; + + int32_t p_signal_events = 0; + uint32_t noise_sq_sum = 0; + + + + + if (event_sigma == 0) { + *prange_status = VL53L1_DEVICEERROR_RANGECOMPLETE; + return status; + } + + + + + tmpp = 1 + (int64_t)phist_prev->total_periods_elapsed; + tmpp *= (int64_t)phist_prev->result__dss_actual_effective_spads; + + + + + tmpc = 1 + (int64_t)prange_curr->total_periods_elapsed; + tmpc *= (int64_t)prange_curr->VL53L1_p_006; + + + + + events_scaler = tmpp * 4096; + events_scaler += (tmpc/2); + events_scaler = do_division_s(events_scaler, tmpc); + + events_scaler_sq = events_scaler * events_scaler; + events_scaler_sq += 2048; + events_scaler_sq /= 4096; + + + + + c_signal_events = (int64_t)prange_curr->VL53L1_p_021; + c_signal_events -= (int64_t)prange_curr->VL53L1_p_020; + c_signal_events *= (int64_t)events_scaler; + c_signal_events += 2048; + c_signal_events /= 4096; + + c_sig_noise_sq = (int64_t)events_scaler_sq; + c_sig_noise_sq *= (int64_t)prange_curr->VL53L1_p_021; + c_sig_noise_sq += 2048; + c_sig_noise_sq /= 4096; + + c_amb_noise_sq = (int64_t)events_scaler_sq; + c_amb_noise_sq *= (int64_t)prange_curr->VL53L1_p_020; + c_amb_noise_sq += 2048; + c_amb_noise_sq /= 4096; + + + + c_amb_noise_sq += 2; + c_amb_noise_sq /= 4; + + + + + + + + p_amb_noise_sq = + (int64_t)prange_prev->VL53L1_p_020; + + + + p_amb_noise_sq += 2; + p_amb_noise_sq /= 4; + + noise_sq_sum = + (uint32_t)prange_prev->VL53L1_p_021 + + (uint32_t)c_sig_noise_sq + + (uint32_t)p_amb_noise_sq + + (uint32_t)c_amb_noise_sq; + + *pevents_tolerance = + (int32_t)VL53L1_isqrt(noise_sq_sum * 16); + + *pevents_tolerance *= (int32_t)event_sigma; + *pevents_tolerance += 32; + *pevents_tolerance /= 64; + + p_signal_events = (int32_t)prange_prev->VL53L1_p_021; + p_signal_events -= (int32_t)prange_prev->VL53L1_p_020; + + if ((int32_t)c_signal_events > p_signal_events) + *pevents_delta = + (int32_t)c_signal_events - p_signal_events; + else + *pevents_delta = + p_signal_events - (int32_t)c_signal_events; + + if (*pevents_delta > *pevents_tolerance && + prange_curr->VL53L1_p_006 > min_effective_spad_count) + *prange_status = VL53L1_DEVICEERROR_EVENTCONSISTENCY; + else + *prange_status = VL53L1_DEVICEERROR_RANGECOMPLETE; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + return status; +} + + + + + +VL53L1_Error VL53L1_hist_merged_pulse_check( + int16_t min_max_tolerance_mm, + VL53L1_range_data_t *pdata, + VL53L1_DeviceError *prange_status) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + int16_t delta_mm = 0; + + if (pdata->max_range_mm > pdata->min_range_mm) + delta_mm = + pdata->max_range_mm - pdata->min_range_mm; + else + delta_mm = + pdata->min_range_mm - pdata->max_range_mm; + + if (min_max_tolerance_mm > 0 && + delta_mm > min_max_tolerance_mm) + *prange_status = VL53L1_DEVICEERROR_RANGECOMPLETE_MERGED_PULSE; + else + *prange_status = VL53L1_DEVICEERROR_RANGECOMPLETE; + + return status; +} + + + + + +VL53L1_Error VL53L1_hist_xmonitor_consistency_check( + VL53L1_DEV Dev, + VL53L1_zone_hist_info_t *phist_prev, + VL53L1_zone_objects_t *prange_prev, + VL53L1_range_data_t *prange_curr) +{ + + + + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + int32_t events_delta = 0; + int32_t events_tolerance = 0; + uint8_t event_sigma; + uint16_t min_spad_count; + + event_sigma = pdev->histpostprocess.algo__crosstalk_detect_event_sigma; + min_spad_count = + pdev->histpostprocess.algo__consistency_check__event_min_spad_count; + + if (prange_curr->range_status == VL53L1_DEVICEERROR_RANGECOMPLETE || + prange_curr->range_status == + VL53L1_DEVICEERROR_RANGECOMPLETE_NO_WRAP_CHECK || + prange_curr->range_status == + VL53L1_DEVICEERROR_EVENTCONSISTENCY) { + + if (prange_prev->xmonitor.range_status == + VL53L1_DEVICEERROR_RANGECOMPLETE || + prange_prev->xmonitor.range_status == + VL53L1_DEVICEERROR_RANGECOMPLETE_NO_WRAP_CHECK || + prange_prev->xmonitor.range_status == + VL53L1_DEVICEERROR_EVENTCONSISTENCY) { + + prange_curr->range_status = + VL53L1_DEVICEERROR_RANGECOMPLETE; + + status = + VL53L1_hist_events_consistency_check( + event_sigma, + min_spad_count, + phist_prev, + &(prange_prev->xmonitor), + prange_curr, + &events_tolerance, + &events_delta, + &(prange_curr->range_status)); + + } + } + + return status; +} + + + + + +VL53L1_Error VL53L1_hist_wrap_dmax( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_histogram_bin_data_t *pcurrent, + int16_t *pwrap_dmax_mm) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint32_t pll_period_mm = 0; + uint32_t wrap_dmax_phase = 0; + uint32_t range_mm = 0; + + LOG_FUNCTION_START(""); + + *pwrap_dmax_mm = 0; + + + if (pcurrent->VL53L1_p_019 != 0) { + + + + + pll_period_mm = + VL53L1_calc_pll_period_mm( + pcurrent->VL53L1_p_019); + + + + + wrap_dmax_phase = + (uint32_t)phistpostprocess->valid_phase_high << 8; + + + + + + range_mm = wrap_dmax_phase * pll_period_mm; + range_mm = (range_mm + (1<<14)) >> 15; + + *pwrap_dmax_mm = (int16_t)range_mm; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +void VL53L1_hist_combine_mm1_mm2_offsets( + int16_t mm1_offset_mm, + int16_t mm2_offset_mm, + uint8_t encoded_mm_roi_centre, + uint8_t encoded_mm_roi_size, + uint8_t encoded_zone_centre, + uint8_t encoded_zone_size, + VL53L1_additional_offset_cal_data_t *pcal_data, + uint8_t *pgood_spads, + uint16_t aperture_attenuation, + int16_t *prange_offset_mm) +{ + + + + + + + + + + + + + + + + uint16_t max_mm_inner_effective_spads = 0; + uint16_t max_mm_outer_effective_spads = 0; + uint16_t mm_inner_effective_spads = 0; + uint16_t mm_outer_effective_spads = 0; + + uint32_t scaled_mm1_peak_rate_mcps = 0; + uint32_t scaled_mm2_peak_rate_mcps = 0; + + int32_t tmp0 = 0; + int32_t tmp1 = 0; + + + + + VL53L1_calc_mm_effective_spads( + encoded_mm_roi_centre, + encoded_mm_roi_size, + 0xC7, + + 0xFF, + pgood_spads, + aperture_attenuation, + &max_mm_inner_effective_spads, + &max_mm_outer_effective_spads); + + + + + VL53L1_calc_mm_effective_spads( + encoded_mm_roi_centre, + encoded_mm_roi_size, + encoded_zone_centre, + encoded_zone_size, + pgood_spads, + aperture_attenuation, + &mm_inner_effective_spads, + &mm_outer_effective_spads); + + + + + scaled_mm1_peak_rate_mcps = + (uint32_t)pcal_data->result__mm_inner_peak_signal_count_rtn_mcps; + scaled_mm1_peak_rate_mcps *= (uint32_t)mm_inner_effective_spads; + scaled_mm1_peak_rate_mcps /= (uint32_t)max_mm_inner_effective_spads; + + scaled_mm2_peak_rate_mcps = + (uint32_t)pcal_data->result__mm_outer_peak_signal_count_rtn_mcps; + scaled_mm2_peak_rate_mcps *= (uint32_t)mm_outer_effective_spads; + scaled_mm2_peak_rate_mcps /= (uint32_t)max_mm_outer_effective_spads; + + + + + tmp0 = ((int32_t)mm1_offset_mm * (int32_t)scaled_mm1_peak_rate_mcps); + tmp0 += ((int32_t)mm2_offset_mm * (int32_t)scaled_mm2_peak_rate_mcps); + + tmp1 = (int32_t)scaled_mm1_peak_rate_mcps + + (int32_t)scaled_mm2_peak_rate_mcps; + + + + + + + if (tmp1 != 0) + tmp0 = (tmp0 * 4) / tmp1; + + *prange_offset_mm = (int16_t)tmp0; + +} + + +VL53L1_Error VL53L1_hist_xtalk_extract_calc_window( + int16_t target_distance_mm, + uint16_t target_width_oversize, + VL53L1_histogram_bin_data_t *phist_bins, + VL53L1_hist_xtalk_extract_data_t *pxtalk_data) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + pxtalk_data->pll_period_mm = + VL53L1_calc_pll_period_mm(phist_bins->VL53L1_p_019); + + + + pxtalk_data->xtalk_width_phase = + (int32_t)phist_bins->vcsel_width * 128; + pxtalk_data->target_width_phase = + pxtalk_data->xtalk_width_phase + + (int32_t)target_width_oversize * 128; + + + + + + + + pxtalk_data->xtalk_start_phase = + (int32_t)phist_bins->zero_distance_phase - + (pxtalk_data->xtalk_width_phase / 2); + pxtalk_data->xtalk_end_phase = + (int32_t)pxtalk_data->xtalk_start_phase + + pxtalk_data->xtalk_width_phase; + + if (pxtalk_data->xtalk_start_phase < 0) + pxtalk_data->xtalk_start_phase = 0; + + + + + + + + + pxtalk_data->VL53L1_p_015 = + (uint8_t)(pxtalk_data->xtalk_start_phase / 2048); + + + + pxtalk_data->VL53L1_p_016 = + (uint8_t)((pxtalk_data->xtalk_end_phase + 2047) / 2048); + + + + + + + + pxtalk_data->target_start_phase = + (int32_t)target_distance_mm * 2048 * 16; + pxtalk_data->target_start_phase += + ((int32_t)pxtalk_data->pll_period_mm / 2); + pxtalk_data->target_start_phase /= (int32_t)pxtalk_data->pll_period_mm; + pxtalk_data->target_start_phase += + (int32_t)phist_bins->zero_distance_phase; + + + + + + + pxtalk_data->target_start_phase -= + (pxtalk_data->target_width_phase / 2); + pxtalk_data->target_end_phase = + (int32_t)pxtalk_data->target_start_phase + + pxtalk_data->target_width_phase; + + if (pxtalk_data->target_start_phase < 0) + pxtalk_data->target_start_phase = 0; + + + + pxtalk_data->target_start = + (uint8_t)(pxtalk_data->target_start_phase / 2048); + + + + if (pxtalk_data->VL53L1_p_016 > (pxtalk_data->target_start-1)) + pxtalk_data->VL53L1_p_016 = pxtalk_data->target_start-1; + + + + pxtalk_data->effective_width = + (2048 * ((int32_t)pxtalk_data->VL53L1_p_016+1)); + pxtalk_data->effective_width -= pxtalk_data->xtalk_start_phase; + + + + + + if (pxtalk_data->effective_width > pxtalk_data->xtalk_width_phase) + pxtalk_data->effective_width = pxtalk_data->xtalk_width_phase; + + if (pxtalk_data->effective_width < 1) + pxtalk_data->effective_width = 1; + + + + pxtalk_data->event_scaler = pxtalk_data->xtalk_width_phase * 1000; + pxtalk_data->event_scaler += (pxtalk_data->effective_width / 2); + pxtalk_data->event_scaler /= pxtalk_data->effective_width; + + + + + + if (pxtalk_data->event_scaler < 1000) + pxtalk_data->event_scaler = 1000; + + if (pxtalk_data->event_scaler > 4000) + pxtalk_data->event_scaler = 4000; + + + + pxtalk_data->event_scaler_sum += pxtalk_data->event_scaler; + + + + pxtalk_data->peak_duration_us_sum += + (uint32_t)phist_bins->peak_duration_us; + + + + pxtalk_data->effective_spad_count_sum += + (uint32_t)phist_bins->result__dss_actual_effective_spads; + + + + pxtalk_data->zero_distance_phase_sum += + (uint32_t)phist_bins->zero_distance_phase; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_hist_xtalk_extract_calc_event_sums( + VL53L1_histogram_bin_data_t *phist_bins, + VL53L1_hist_xtalk_extract_data_t *pxtalk_data) +{ + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t lb = 0; + uint8_t i = 0; + + LOG_FUNCTION_START(""); + + + + + for (lb = pxtalk_data->VL53L1_p_015; + lb <= pxtalk_data->VL53L1_p_016; + lb++) { + + + + i = (lb + phist_bins->number_of_ambient_bins + + phist_bins->VL53L1_p_024) % + phist_bins->VL53L1_p_024; + + + + pxtalk_data->signal_events_sum += phist_bins->bin_data[i]; + pxtalk_data->signal_events_sum -= + phist_bins->VL53L1_p_004; + } + + + + + for (lb = 0; lb < VL53L1_XTALK_HISTO_BINS && + lb < phist_bins->VL53L1_p_024; lb++) { + + + + i = (lb + phist_bins->number_of_ambient_bins + + phist_bins->VL53L1_p_024) % + phist_bins->VL53L1_p_024; + + + + pxtalk_data->bin_data_sums[lb] += phist_bins->bin_data[i]; + pxtalk_data->bin_data_sums[lb] -= + phist_bins->VL53L1_p_004; + } + + pxtalk_data->sample_count += 1; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_hist_xtalk_extract_calc_rate_per_spad( + VL53L1_hist_xtalk_extract_data_t *pxtalk_data) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint64_t tmp64_0 = 0; + uint64_t tmp64_1 = 0; + uint64_t xtalk_per_spad = 0; + + LOG_FUNCTION_START(""); + + + + + + + + + + + + + + + + + + tmp64_0 = + ((uint64_t)pxtalk_data->signal_events_sum * + (uint64_t)pxtalk_data->sample_count * + (uint64_t)pxtalk_data->event_scaler_avg * 256U) << 9U; + tmp64_1 = + (uint64_t)pxtalk_data->effective_spad_count_sum * + (uint64_t)pxtalk_data->peak_duration_us_sum; + + + + + if (tmp64_1 > 0U) { + + + tmp64_0 = tmp64_0 + (tmp64_1 >> 1U); + xtalk_per_spad = do_division_u(tmp64_0, tmp64_1); + } else { + xtalk_per_spad = (uint64_t)tmp64_0; + } + + pxtalk_data->xtalk_rate_kcps_per_spad = (uint32_t)xtalk_per_spad; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_hist_xtalk_extract_calc_shape( + VL53L1_hist_xtalk_extract_data_t *pxtalk_data, + VL53L1_xtalk_histogram_shape_t *pxtalk_shape) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + int32_t lb = 0; + uint64_t total_events = 0U; + uint64_t tmp64_0 = 0U; + int32_t remaining_area = 1024; + + LOG_FUNCTION_START(""); + + + + + pxtalk_shape->VL53L1_p_022 = 0; + pxtalk_shape->VL53L1_p_023 = VL53L1_XTALK_HISTO_BINS; + pxtalk_shape->VL53L1_p_024 = VL53L1_XTALK_HISTO_BINS; + + pxtalk_shape->zero_distance_phase = + (uint16_t)pxtalk_data->zero_distance_phase_avg; + pxtalk_shape->phasecal_result__reference_phase = + (uint16_t)pxtalk_data->zero_distance_phase_avg + (3*2048); + + + + + if (pxtalk_data->signal_events_sum > 0) + total_events = + (uint64_t)pxtalk_data->signal_events_sum * + (uint64_t)pxtalk_data->event_scaler_avg; + else + total_events = 1; + + + + remaining_area = 1024; + pxtalk_data->max_shape_value = 0; + + for (lb = 0; lb < VL53L1_XTALK_HISTO_BINS; lb++) { + + if ((lb < (int32_t)pxtalk_data->VL53L1_p_015 || + lb > (int32_t)pxtalk_data->VL53L1_p_016) || + pxtalk_data->bin_data_sums[lb] < 0) { + + + + + + + if (remaining_area > 0 && remaining_area < 1024) { + if (remaining_area > + pxtalk_data->max_shape_value) { + pxtalk_shape->bin_data[lb] = + (uint32_t)pxtalk_data->max_shape_value; + remaining_area -= + pxtalk_data->max_shape_value; + } else { + pxtalk_shape->bin_data[lb] = + (uint32_t)remaining_area; + remaining_area = 0; + } + } else { + pxtalk_shape->bin_data[lb] = 0; + } + + } else { + + + + + tmp64_0 = + (uint64_t)pxtalk_data->bin_data_sums[lb] + * 1024U * 1000U; + tmp64_0 += (total_events >> 1); + tmp64_0 = do_division_u(tmp64_0, total_events); + if (tmp64_0 > 0xFFFFU) + tmp64_0 = 0xFFFFU; + + pxtalk_shape->bin_data[lb] = (uint32_t)tmp64_0; + + + + + + if ((int32_t)pxtalk_shape->bin_data[lb] > + pxtalk_data->max_shape_value) + pxtalk_data->max_shape_value = + (int32_t)pxtalk_shape->bin_data[lb]; + + remaining_area -= (int32_t)pxtalk_shape->bin_data[lb]; + } + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_hist_xtalk_shape_model( + uint16_t events_per_bin, + uint16_t pulse_centre, + uint16_t pulse_width, + VL53L1_xtalk_histogram_shape_t *pxtalk_shape) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint32_t phase_start = 0; + uint32_t phase_stop = 0; + uint32_t phase_bin = 0; + + uint32_t bin_start = 0; + uint32_t bin_stop = 0; + + uint32_t lb = 0; + uint16_t VL53L1_p_008 = 0; + + LOG_FUNCTION_START(""); + + + + + pxtalk_shape->VL53L1_p_022 = 0; + pxtalk_shape->VL53L1_p_023 = VL53L1_XTALK_HISTO_BINS; + pxtalk_shape->VL53L1_p_024 = VL53L1_XTALK_HISTO_BINS; + + pxtalk_shape->zero_distance_phase = pulse_centre; + pxtalk_shape->phasecal_result__reference_phase = + pulse_centre + (3*2048); + + + + if (pulse_centre > (pulse_width >> 1)) + phase_start = (uint32_t)pulse_centre - + ((uint32_t)pulse_width >> 1); + else + phase_start = 0; + + phase_stop = (uint32_t)pulse_centre + + ((uint32_t)pulse_width >> 1); + + + + bin_start = (phase_start / 2048); + bin_stop = (phase_stop / 2048); + + for (lb = 0; lb < VL53L1_XTALK_HISTO_BINS; lb++) { + VL53L1_p_008 = 0; + + + + if (lb == bin_start && lb == bin_stop) { + VL53L1_p_008 = + VL53L1_hist_xtalk_shape_model_interp( + events_per_bin, + phase_stop - phase_start); + + } else if (lb > bin_start && lb < bin_stop) { + + + + VL53L1_p_008 = events_per_bin; + + } else if (lb == bin_start) { + + + + phase_bin = (lb + 1) * 2048; + VL53L1_p_008 = + VL53L1_hist_xtalk_shape_model_interp( + events_per_bin, + (phase_bin - phase_start)); + + } else if (lb == bin_stop) { + + + + phase_bin = lb * 2048; + VL53L1_p_008 = + VL53L1_hist_xtalk_shape_model_interp( + events_per_bin, + (phase_stop - phase_bin)); + } + + pxtalk_shape->bin_data[lb] = VL53L1_p_008; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +uint16_t VL53L1_hist_xtalk_shape_model_interp( + uint16_t events_per_bin, + uint32_t phase_delta) +{ + + + + + + + + + + uint32_t VL53L1_p_008 = 0; + + LOG_FUNCTION_START(""); + + + + VL53L1_p_008 = (uint32_t)events_per_bin * phase_delta; + VL53L1_p_008 += 1024; + VL53L1_p_008 /= 2048; + + + + if (VL53L1_p_008 > 0xFFFFU) + VL53L1_p_008 = 0xFFFFU; + + LOG_FUNCTION_END(0); + + return (uint16_t)VL53L1_p_008; +} + + +void VL53L1_spad_number_to_byte_bit_index( + uint8_t spad_number, + uint8_t *pbyte_index, + uint8_t *pbit_index, + uint8_t *pbit_mask) +{ + + + + + + + + + + + *pbyte_index = spad_number >> 3; + *pbit_index = spad_number & 0x07; + *pbit_mask = 0x01 << *pbit_index; + +} + + +void VL53L1_encode_row_col( + uint8_t row, + uint8_t col, + uint8_t *pspad_number) +{ + + + + + + if (row > 7) + *pspad_number = 128 + (col << 3) + (15-row); + else + *pspad_number = ((15-col) << 3) + row; + +} + + +void VL53L1_decode_zone_size( + uint8_t encoded_xy_size, + uint8_t *pwidth, + uint8_t *pheight) +{ + + + + + + + + + + + + *pheight = encoded_xy_size >> 4; + *pwidth = encoded_xy_size & 0x0F; + +} + + +void VL53L1_encode_zone_size( + uint8_t width, + uint8_t height, + uint8_t *pencoded_xy_size) +{ + + + + + + + + + + + *pencoded_xy_size = (height << 4) + width; + +} + + +void VL53L1_decode_zone_limits( + uint8_t encoded_xy_centre, + uint8_t encoded_xy_size, + int16_t *px_ll, + int16_t *py_ll, + int16_t *px_ur, + int16_t *py_ur) +{ + + + + + + + + + + uint8_t x_centre = 0; + uint8_t y_centre = 0; + uint8_t width = 0; + uint8_t height = 0; + + + + + VL53L1_decode_row_col( + encoded_xy_centre, + &y_centre, + &x_centre); + + VL53L1_decode_zone_size( + encoded_xy_size, + &width, + &height); + + + + + *px_ll = (int16_t)x_centre - ((int16_t)width + 1) / 2; + if (*px_ll < 0) + *px_ll = 0; + + *px_ur = *px_ll + (int16_t)width; + if (*px_ur > (VL53L1_SPAD_ARRAY_WIDTH-1)) + *px_ur = VL53L1_SPAD_ARRAY_WIDTH-1; + + *py_ll = (int16_t)y_centre - ((int16_t)height + 1) / 2; + if (*py_ll < 0) + *py_ll = 0; + + *py_ur = *py_ll + (int16_t)height; + if (*py_ur > (VL53L1_SPAD_ARRAY_HEIGHT-1)) + *py_ur = VL53L1_SPAD_ARRAY_HEIGHT-1; +} + + +uint8_t VL53L1_is_aperture_location( + uint8_t row, + uint8_t col) +{ + + + + + + uint8_t is_aperture = 0; + uint8_t mod_row = row % 4; + uint8_t mod_col = col % 4; + + if (mod_row == 0 && mod_col == 2) + is_aperture = 1; + + if (mod_row == 2 && mod_col == 0) + is_aperture = 1; + + return is_aperture; +} + + +void VL53L1_calc_max_effective_spads( + uint8_t encoded_zone_centre, + uint8_t encoded_zone_size, + uint8_t *pgood_spads, + uint16_t aperture_attenuation, + uint16_t *pmax_effective_spads) +{ + + + + + + + int16_t x = 0; + int16_t y = 0; + + int16_t zone_x_ll = 0; + int16_t zone_y_ll = 0; + int16_t zone_x_ur = 0; + int16_t zone_y_ur = 0; + + uint8_t spad_number = 0; + uint8_t byte_index = 0; + uint8_t bit_index = 0; + uint8_t bit_mask = 0; + + uint8_t is_aperture = 0; + + + + + VL53L1_decode_zone_limits( + encoded_zone_centre, + encoded_zone_size, + &zone_x_ll, + &zone_y_ll, + &zone_x_ur, + &zone_y_ur); + + + + + + + + *pmax_effective_spads = 0; + + for (y = zone_y_ll; y <= zone_y_ur; y++) { + for (x = zone_x_ll; x <= zone_x_ur; x++) { + + + + + VL53L1_encode_row_col( + (uint8_t)y, + (uint8_t)x, + &spad_number); + + + + + + + + + VL53L1_spad_number_to_byte_bit_index( + spad_number, + &byte_index, + &bit_index, + &bit_mask); + + + + + if ((pgood_spads[byte_index] & bit_mask) > 0) { + + + + is_aperture = VL53L1_is_aperture_location( + (uint8_t)y, + (uint8_t)x); + + if (is_aperture > 0) + *pmax_effective_spads += + aperture_attenuation; + else + *pmax_effective_spads += 0x0100; + + } + } + } +} + + +void VL53L1_calc_mm_effective_spads( + uint8_t encoded_mm_roi_centre, + uint8_t encoded_mm_roi_size, + uint8_t encoded_zone_centre, + uint8_t encoded_zone_size, + uint8_t *pgood_spads, + uint16_t aperture_attenuation, + uint16_t *pmm_inner_effective_spads, + uint16_t *pmm_outer_effective_spads) +{ + + + + + + + + int16_t x = 0; + int16_t y = 0; + + int16_t mm_x_ll = 0; + int16_t mm_y_ll = 0; + int16_t mm_x_ur = 0; + int16_t mm_y_ur = 0; + + int16_t zone_x_ll = 0; + int16_t zone_y_ll = 0; + int16_t zone_x_ur = 0; + int16_t zone_y_ur = 0; + + uint8_t spad_number = 0; + uint8_t byte_index = 0; + uint8_t bit_index = 0; + uint8_t bit_mask = 0; + + uint8_t is_aperture = 0; + uint16_t spad_attenuation = 0; + + + + + VL53L1_decode_zone_limits( + encoded_mm_roi_centre, + encoded_mm_roi_size, + &mm_x_ll, + &mm_y_ll, + &mm_x_ur, + &mm_y_ur); + + VL53L1_decode_zone_limits( + encoded_zone_centre, + encoded_zone_size, + &zone_x_ll, + &zone_y_ll, + &zone_x_ur, + &zone_y_ur); + + + + + + + + + + *pmm_inner_effective_spads = 0; + *pmm_outer_effective_spads = 0; + + for (y = zone_y_ll; y <= zone_y_ur; y++) { + for (x = zone_x_ll; x <= zone_x_ur; x++) { + + + + + VL53L1_encode_row_col( + (uint8_t)y, + (uint8_t)x, + &spad_number); + + + + + + + + + VL53L1_spad_number_to_byte_bit_index( + spad_number, + &byte_index, + &bit_index, + &bit_mask); + + + + + if ((pgood_spads[byte_index] & bit_mask) > 0) { + + + + is_aperture = VL53L1_is_aperture_location( + (uint8_t)y, + (uint8_t)x); + + if (is_aperture > 0) + spad_attenuation = aperture_attenuation; + else + spad_attenuation = 0x0100; + + + + + + + + if (x >= mm_x_ll && x <= mm_x_ur && + y >= mm_y_ll && y <= mm_y_ur) + *pmm_inner_effective_spads += + spad_attenuation; + else + *pmm_outer_effective_spads += + spad_attenuation; + } + } + } +} + + +void VL53L1_hist_copy_results_to_sys_and_core( + VL53L1_histogram_bin_data_t *pbins, + VL53L1_range_results_t *phist, + VL53L1_system_results_t *psys, + VL53L1_core_results_t *pcore) +{ + + + + + + uint8_t i = 0; + + VL53L1_range_data_t *pdata; + + LOG_FUNCTION_START(""); + + + + + VL53L1_init_system_results(psys); + + + + + psys->result__interrupt_status = pbins->result__interrupt_status; + psys->result__range_status = phist->active_results; + psys->result__report_status = pbins->result__report_status; + psys->result__stream_count = pbins->result__stream_count; + + pdata = &(phist->VL53L1_p_002[0]); + + for (i = 0; i < phist->active_results; i++) { + + switch (i) { + case 0: + psys->result__dss_actual_effective_spads_sd0 = + pdata->VL53L1_p_006; + psys->result__peak_signal_count_rate_mcps_sd0 = + pdata->peak_signal_count_rate_mcps; + psys->result__avg_signal_count_rate_mcps_sd0 = + pdata->avg_signal_count_rate_mcps; + psys->result__ambient_count_rate_mcps_sd0 = + pdata->ambient_count_rate_mcps; + + psys->result__sigma_sd0 = pdata->VL53L1_p_005; + psys->result__phase_sd0 = pdata->VL53L1_p_014; + + psys->result__final_crosstalk_corrected_range_mm_sd0 = + (uint16_t)pdata->median_range_mm; + + psys->result__phase_sd1 = pdata->zero_distance_phase; + + pcore->result_core__ranging_total_events_sd0 = + pdata->VL53L1_p_021; + pcore->result_core__signal_total_events_sd0 = + pdata->VL53L1_p_013; + pcore->result_core__total_periods_elapsed_sd0 = + pdata->total_periods_elapsed; + pcore->result_core__ambient_window_events_sd0 = + pdata->VL53L1_p_020; + + break; + case 1: + psys->result__dss_actual_effective_spads_sd1 = + pdata->VL53L1_p_006; + psys->result__peak_signal_count_rate_mcps_sd1 = + pdata->peak_signal_count_rate_mcps; + psys->result__ambient_count_rate_mcps_sd1 = + pdata->ambient_count_rate_mcps; + + psys->result__sigma_sd1 = pdata->VL53L1_p_005; + psys->result__phase_sd1 = pdata->VL53L1_p_014; + + psys->result__final_crosstalk_corrected_range_mm_sd1 = + (uint16_t)pdata->median_range_mm; + + pcore->result_core__ranging_total_events_sd1 = + pdata->VL53L1_p_021; + pcore->result_core__signal_total_events_sd1 = + pdata->VL53L1_p_013; + pcore->result_core__total_periods_elapsed_sd1 = + pdata->total_periods_elapsed; + pcore->result_core__ambient_window_events_sd1 = + pdata->VL53L1_p_020; + break; + } + + pdata++; + } + + LOG_FUNCTION_END(0); + +} + + +VL53L1_Error VL53L1_sum_histogram_data( + VL53L1_histogram_bin_data_t *phist_input, + VL53L1_histogram_bin_data_t *phist_output) +{ + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i = 0; + uint8_t smallest_bin_num = 0; + + LOG_FUNCTION_START(""); + + + + + if (status == VL53L1_ERROR_NONE) { + + if (phist_output->VL53L1_p_024 >= + phist_input->VL53L1_p_024) + smallest_bin_num = phist_input->VL53L1_p_024; + else + smallest_bin_num = phist_output->VL53L1_p_024; + } + + + + + + + + + if (status == VL53L1_ERROR_NONE) + + for (i = 0; i < smallest_bin_num; i++) + + + phist_output->bin_data[i] += phist_input->bin_data[i]; + + if (status == VL53L1_ERROR_NONE) + + phist_output->VL53L1_p_004 += + phist_input->VL53L1_p_004; + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_avg_histogram_data( + uint8_t no_of_samples, + VL53L1_histogram_bin_data_t *phist_sum, + VL53L1_histogram_bin_data_t *phist_avg) +{ + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i = 0; + + LOG_FUNCTION_START(""); + + + + + + + + + if (status == VL53L1_ERROR_NONE) { + + for (i = 0; i < phist_sum->VL53L1_p_024; i++) { + + + + + if (no_of_samples > 0) + phist_avg->bin_data[i] = + phist_sum->bin_data[i] / + (int32_t)no_of_samples; + else + phist_avg->bin_data[i] = phist_sum->bin_data[i]; + } + } + + if (status == VL53L1_ERROR_NONE) { + + if (no_of_samples > 0) + phist_avg->VL53L1_p_004 = + phist_sum->VL53L1_p_004 / + (int32_t)no_of_samples; + else + phist_avg->VL53L1_p_004 = + phist_sum->VL53L1_p_004; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_save_cfg_data( + VL53L1_DEV Dev) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + VL53L1_zone_private_dyn_cfg_t *pzone_dyn_cfg; + VL53L1_dynamic_config_t *pdynamic = &(pdev->dyn_cfg); + + LOG_FUNCTION_START(""); + + pzone_dyn_cfg = + &(pres->zone_dyn_cfgs.VL53L1_p_002[pdev->ll_state.cfg_zone_id]); + + pzone_dyn_cfg->expected_stream_count = + pdev->ll_state.cfg_stream_count; + + pzone_dyn_cfg->expected_gph_id = + pdev->ll_state.cfg_gph_id; + + pzone_dyn_cfg->roi_config__user_roi_centre_spad = + pdynamic->roi_config__user_roi_centre_spad; + + pzone_dyn_cfg->roi_config__user_roi_requested_global_xy_size = + pdynamic->roi_config__user_roi_requested_global_xy_size; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_dynamic_zone_update( + VL53L1_DEV Dev, + VL53L1_range_results_t *presults) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + VL53L1_zone_private_dyn_cfgs_t *pZ = &(pres->zone_dyn_cfgs); + + uint8_t zone_id = pdev->ll_state.rd_zone_id; + uint8_t i; + uint16_t max_total_rate_per_spads; + uint16_t target_rate = + pdev->stat_cfg.dss_config__target_total_rate_mcps; + uint32_t temp = 0xFFFF; +#ifdef VL53L1_LOG_ENABLE + uint16_t eff_spad_cnt = + pZ->VL53L1_p_002[zone_id].dss_requested_effective_spad_count; +#endif + + LOG_FUNCTION_START(""); + + pZ->VL53L1_p_002[zone_id].dss_requested_effective_spad_count = 0; + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + " DYNZONEUPDATE: peak signal count rate mcps:"); + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + "%u actual effective spads: %u\n", + presults->VL53L1_p_002[0].peak_signal_count_rate_mcps, + presults->VL53L1_p_002[0].VL53L1_p_006); + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + " DYNZONEUPDATE: active results: %u\n", + presults->active_results); + + max_total_rate_per_spads = + presults->VL53L1_p_002[0].total_rate_per_spad_mcps; + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + " DYNZONEUPDATE: max total rate per spad at start: %u\n", + max_total_rate_per_spads); + + for (i = 1; i < presults->active_results; i++) { + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + " DYNZONEUPDATE: zone total rate per spad: zone_id: %u,", + i); + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + "total rate per spad: %u\n", + presults->VL53L1_p_002[i].total_rate_per_spad_mcps); + + if (presults->VL53L1_p_002[i].total_rate_per_spad_mcps > + max_total_rate_per_spads) + max_total_rate_per_spads = + presults->VL53L1_p_002[i].total_rate_per_spad_mcps; + + } + + if (max_total_rate_per_spads == 0) { + + + + + + temp = 0xFFFF; + } else { + + + + + + temp = target_rate << 14; + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + " DYNZONEUPDATE: 1: temp: %u\n", + temp); + + + + + + temp = temp / max_total_rate_per_spads; + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + " DYNZONEUPDATE: 2: temp: %u\n", + temp); + + + + + + if (temp > 0xFFFF) + temp = 0xFFFF; + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + " DYNZONEUPDATE: 3: temp: %u\n", + temp); + } + + pZ->VL53L1_p_002[zone_id].dss_requested_effective_spad_count = + (uint16_t)temp; + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + " DYNZONEUPDATE: zone_id: %u, target_rate: %u,", + zone_id, + target_rate); + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + "max_total_rate_per_spads: %u, requested_spads: %u\n", + max_total_rate_per_spads, + eff_spad_cnt); + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_multizone_hist_bins_update( + VL53L1_DEV Dev) +{ + + + + + + + + + + + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_ll_driver_state_t *pstate = &(pdev->ll_state); + VL53L1_zone_config_t *pzone_cfg = &(pdev->zone_cfg); + VL53L1_histogram_config_t *phist_cfg = &(pdev->hist_cfg); + VL53L1_histogram_config_t *pmulti_hist = + &(pzone_cfg->multizone_hist_cfg); + + uint8_t next_range_is_odd_timing = (pstate->cfg_stream_count) % 2; + + LOG_FUNCTION_START(""); + + + + + + if (pzone_cfg->bin_config[pdev->ll_state.cfg_zone_id] == + VL53L1_ZONECONFIG_BINCONFIG__LOWAMB) { + if (!next_range_is_odd_timing) { + trace_print (VL53L1_TRACE_LEVEL_DEBUG, + " HISTBINCONFIGUPDATE: Setting LOWAMB EVEN timing\n"); + phist_cfg->histogram_config__low_amb_even_bin_0_1 = + pmulti_hist->histogram_config__low_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_even_bin_2_3 = + pmulti_hist->histogram_config__low_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_even_bin_4_5 = + pmulti_hist->histogram_config__low_amb_even_bin_4_5; + } + + if (next_range_is_odd_timing) { + trace_print (VL53L1_TRACE_LEVEL_DEBUG, + " HISTBINCONFIGUPDATE: Setting LOWAMB ODD timing\n"); + phist_cfg->histogram_config__low_amb_odd_bin_0_1 = + pmulti_hist->histogram_config__low_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_odd_bin_2_3 = + pmulti_hist->histogram_config__low_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_odd_bin_4_5 = + pmulti_hist->histogram_config__low_amb_even_bin_4_5; + } + } else if (pzone_cfg->bin_config[pdev->ll_state.cfg_zone_id] == + VL53L1_ZONECONFIG_BINCONFIG__MIDAMB) { + + trace_print (VL53L1_TRACE_LEVEL_DEBUG, + " HISTBINCONFIGUPDATE: Setting MIDAMB timing\n"); + if (!next_range_is_odd_timing) { + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + " HISTBINCONFIGUPDATE: Setting MIDAMB EVEN timing\n"); + phist_cfg->histogram_config__low_amb_even_bin_0_1 = + pmulti_hist->histogram_config__mid_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_even_bin_2_3 = + pmulti_hist->histogram_config__mid_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_even_bin_4_5 = + pmulti_hist->histogram_config__mid_amb_even_bin_4_5; + } + + if (next_range_is_odd_timing) { + trace_print (VL53L1_TRACE_LEVEL_DEBUG, + " HISTBINCONFIGUPDATE: Setting MIDAMB ODD timing\n"); + phist_cfg->histogram_config__low_amb_odd_bin_0_1 = + pmulti_hist->histogram_config__mid_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_odd_bin_2_3 = + pmulti_hist->histogram_config__mid_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_odd_bin_4_5 = + pmulti_hist->histogram_config__mid_amb_even_bin_4_5; + } + } else if (pzone_cfg->bin_config[pdev->ll_state.cfg_zone_id] == + VL53L1_ZONECONFIG_BINCONFIG__HIGHAMB) { + if (!next_range_is_odd_timing) { + trace_print (VL53L1_TRACE_LEVEL_DEBUG, + " HISTBINCONFIGUPDATE: Setting HIGHAMB EVEN timing\n" + ); + phist_cfg->histogram_config__low_amb_even_bin_0_1 = + pmulti_hist->histogram_config__high_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_even_bin_2_3 = + pmulti_hist->histogram_config__high_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_even_bin_4_5 = + pmulti_hist->histogram_config__high_amb_even_bin_4_5; + } + + if (next_range_is_odd_timing) { + trace_print (VL53L1_TRACE_LEVEL_DEBUG, + " HISTBINCONFIGUPDATE: Setting HIGHAMB ODD timing\n"); + phist_cfg->histogram_config__low_amb_odd_bin_0_1 = + pmulti_hist->histogram_config__high_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_odd_bin_2_3 = + pmulti_hist->histogram_config__high_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_odd_bin_4_5 = + pmulti_hist->histogram_config__high_amb_even_bin_4_5; + } + } + + + + + + + if (status == VL53L1_ERROR_NONE) { + + VL53L1_copy_hist_bins_to_static_cfg( + phist_cfg, + &(pdev->stat_cfg), + &(pdev->tim_cfg)); + } + + LOG_FUNCTION_END(status); + + return status; +} + + + + + + + + + + + + + + + + +VL53L1_Error VL53L1_update_internal_stream_counters( + VL53L1_DEV Dev, + uint8_t external_stream_count, + uint8_t *pinternal_stream_count, + uint8_t *pinternal_stream_count_val) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t stream_divider; + + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + stream_divider = pdev->gen_cfg.global_config__stream_divider; + + if (stream_divider == 0) { + + + + + + + *pinternal_stream_count = external_stream_count; + + } else if (*pinternal_stream_count_val == (stream_divider-1)) { + + + + + + if (*pinternal_stream_count == 0xFF) + *pinternal_stream_count = 0x80; + else + *pinternal_stream_count = *pinternal_stream_count + 1; + + + + + + *pinternal_stream_count_val = 0; + + } else { + + + + + + *pinternal_stream_count_val = *pinternal_stream_count_val + 1; + } + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + "UPDINTSTREAMCOUNT internal_steam_count: %d,", + *pinternal_stream_count); + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + "internal_stream_count_val: %d, divider: %d\n", + *pinternal_stream_count_val, + stream_divider); + + LOG_FUNCTION_END(status); + + return status; +} + + + + + + + +VL53L1_Error VL53L1_set_histogram_multizone_initial_bin_config( + VL53L1_zone_config_t *pzone_cfg, + VL53L1_histogram_config_t *phist_cfg, + VL53L1_histogram_config_t *pmulti_hist) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + if (pzone_cfg->bin_config[0] == + VL53L1_ZONECONFIG_BINCONFIG__LOWAMB) { + phist_cfg->histogram_config__low_amb_even_bin_0_1 = + pmulti_hist->histogram_config__low_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_even_bin_2_3 = + pmulti_hist->histogram_config__low_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_even_bin_4_5 = + pmulti_hist->histogram_config__low_amb_even_bin_4_5; + + phist_cfg->histogram_config__low_amb_odd_bin_0_1 = + pmulti_hist->histogram_config__low_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_odd_bin_2_3 = + pmulti_hist->histogram_config__low_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_odd_bin_4_5 = + pmulti_hist->histogram_config__low_amb_even_bin_4_5; + } else if (pzone_cfg->bin_config[0] == + VL53L1_ZONECONFIG_BINCONFIG__MIDAMB) { + + phist_cfg->histogram_config__low_amb_even_bin_0_1 = + pmulti_hist->histogram_config__mid_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_even_bin_2_3 = + pmulti_hist->histogram_config__mid_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_even_bin_4_5 = + pmulti_hist->histogram_config__mid_amb_even_bin_4_5; + + phist_cfg->histogram_config__low_amb_odd_bin_0_1 = + pmulti_hist->histogram_config__mid_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_odd_bin_2_3 = + pmulti_hist->histogram_config__mid_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_odd_bin_4_5 = + pmulti_hist->histogram_config__mid_amb_even_bin_4_5; + } else if (pzone_cfg->bin_config[0] == + VL53L1_ZONECONFIG_BINCONFIG__HIGHAMB) { + phist_cfg->histogram_config__low_amb_even_bin_0_1 = + pmulti_hist->histogram_config__high_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_even_bin_2_3 = + pmulti_hist->histogram_config__high_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_even_bin_4_5 = + pmulti_hist->histogram_config__high_amb_even_bin_4_5; + phist_cfg->histogram_config__low_amb_odd_bin_0_1 = + pmulti_hist->histogram_config__high_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_odd_bin_2_3 = + pmulti_hist->histogram_config__high_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_odd_bin_4_5 = + pmulti_hist->histogram_config__high_amb_even_bin_4_5; + } + + LOG_FUNCTION_END(status); + return status; +} + + + + + + +uint8_t VL53L1_encode_GPIO_interrupt_config( + VL53L1_GPIO_interrupt_config_t *pintconf) +{ + uint8_t system__interrupt_config; + + system__interrupt_config = pintconf->intr_mode_distance; + system__interrupt_config |= ((pintconf->intr_mode_rate) << 2); + system__interrupt_config |= ((pintconf->intr_new_measure_ready) << 5); + system__interrupt_config |= ((pintconf->intr_no_target) << 6); + system__interrupt_config |= ((pintconf->intr_combined_mode) << 7); + + return system__interrupt_config; +} + + + + + + +VL53L1_GPIO_interrupt_config_t VL53L1_decode_GPIO_interrupt_config( + uint8_t system__interrupt_config) +{ + VL53L1_GPIO_interrupt_config_t intconf; + + intconf.intr_mode_distance = system__interrupt_config & 0x03; + intconf.intr_mode_rate = (system__interrupt_config >> 2) & 0x03; + intconf.intr_new_measure_ready = (system__interrupt_config >> 5) & 0x01; + intconf.intr_no_target = (system__interrupt_config >> 6) & 0x01; + intconf.intr_combined_mode = (system__interrupt_config >> 7) & 0x01; + + + + intconf.threshold_rate_low = 0; + intconf.threshold_rate_high = 0; + intconf.threshold_distance_low = 0; + intconf.threshold_distance_high = 0; + + return intconf; +} + + + + + + +VL53L1_Error VL53L1_set_GPIO_distance_threshold( + VL53L1_DEV Dev, + uint16_t threshold_high, + uint16_t threshold_low) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->dyn_cfg.system__thresh_high = threshold_high; + pdev->dyn_cfg.system__thresh_low = threshold_low; + + LOG_FUNCTION_END(status); + return status; +} + + + + + + +VL53L1_Error VL53L1_set_GPIO_rate_threshold( + VL53L1_DEV Dev, + uint16_t threshold_high, + uint16_t threshold_low) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->gen_cfg.system__thresh_rate_high = threshold_high; + pdev->gen_cfg.system__thresh_rate_low = threshold_low; + + LOG_FUNCTION_END(status); + return status; +} + + + + + + +VL53L1_Error VL53L1_set_GPIO_thresholds_from_struct( + VL53L1_DEV Dev, + VL53L1_GPIO_interrupt_config_t *pintconf) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + status = VL53L1_set_GPIO_distance_threshold( + Dev, + pintconf->threshold_distance_high, + pintconf->threshold_distance_low); + + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_set_GPIO_rate_threshold( + Dev, + pintconf->threshold_rate_high, + pintconf->threshold_rate_low); + } + + LOG_FUNCTION_END(status); + return status; +} + + +VL53L1_Error VL53L1_set_ref_spad_char_config( + VL53L1_DEV Dev, + uint8_t vcsel_period_a, + uint32_t phasecal_timeout_us, + uint16_t total_rate_target_mcps, + uint16_t max_count_rate_rtn_limit_mcps, + uint16_t min_count_rate_rtn_limit_mcps, + uint16_t fast_osc_frequency) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t buffer[2]; + + uint32_t macro_period_us = 0; + uint32_t timeout_mclks = 0; + + LOG_FUNCTION_START(""); + + + + + + macro_period_us = + VL53L1_calc_macro_period_us( + fast_osc_frequency, + vcsel_period_a); + + + + + + + timeout_mclks = phasecal_timeout_us << 12; + timeout_mclks = timeout_mclks + (macro_period_us>>1); + timeout_mclks = timeout_mclks / macro_period_us; + + if (timeout_mclks > 0xFF) + pdev->gen_cfg.phasecal_config__timeout_macrop = 0xFF; + else + pdev->gen_cfg.phasecal_config__timeout_macrop = + (uint8_t)timeout_mclks; + + pdev->tim_cfg.range_config__vcsel_period_a = vcsel_period_a; + + + + + + + if (status == VL53L1_ERROR_NONE) + + status = + VL53L1_WrByte( + Dev, + VL53L1_PHASECAL_CONFIG__TIMEOUT_MACROP, + pdev->gen_cfg.phasecal_config__timeout_macrop); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WrByte( + Dev, + VL53L1_RANGE_CONFIG__VCSEL_PERIOD_A, + pdev->tim_cfg.range_config__vcsel_period_a); + + + + + + + + buffer[0] = pdev->tim_cfg.range_config__vcsel_period_a; + buffer[1] = pdev->tim_cfg.range_config__vcsel_period_a; + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WriteMulti( + Dev, + VL53L1_SD_CONFIG__WOI_SD0, + buffer, + 2); + + + + + + + + pdev->customer.ref_spad_char__total_rate_target_mcps = + total_rate_target_mcps; + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WrWord( + Dev, + VL53L1_REF_SPAD_CHAR__TOTAL_RATE_TARGET_MCPS, + total_rate_target_mcps); + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WrWord( + Dev, + VL53L1_RANGE_CONFIG__SIGMA_THRESH, + max_count_rate_rtn_limit_mcps); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WrWord( + Dev, + VL53L1_RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS, + min_count_rate_rtn_limit_mcps); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_ssc_config( + VL53L1_DEV Dev, + VL53L1_ssc_config_t *pssc_cfg, + uint16_t fast_osc_frequency) +{ + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t buffer[5]; + + uint32_t macro_period_us = 0; + uint16_t timeout_encoded = 0; + + LOG_FUNCTION_START(""); + + + + + + macro_period_us = + VL53L1_calc_macro_period_us( + fast_osc_frequency, + pssc_cfg->VL53L1_p_009); + + + + + + timeout_encoded = + VL53L1_calc_encoded_timeout( + pssc_cfg->timeout_us, + macro_period_us); + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WrByte( + Dev, + VL53L1_CAL_CONFIG__VCSEL_START, + pssc_cfg->vcsel_start); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WrByte( + Dev, + VL53L1_GLOBAL_CONFIG__VCSEL_WIDTH, + pssc_cfg->vcsel_width); + + + + + buffer[0] = (uint8_t)((timeout_encoded & 0x0000FF00) >> 8); + buffer[1] = (uint8_t) (timeout_encoded & 0x000000FF); + buffer[2] = pssc_cfg->VL53L1_p_009; + buffer[3] = (uint8_t)((pssc_cfg->rate_limit_mcps & 0x0000FF00) >> 8); + buffer[4] = (uint8_t) (pssc_cfg->rate_limit_mcps & 0x000000FF); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WriteMulti( + Dev, + VL53L1_RANGE_CONFIG__TIMEOUT_MACROP_B_HI, + buffer, + 5); + + + + + + + + buffer[0] = pssc_cfg->VL53L1_p_009; + buffer[1] = pssc_cfg->VL53L1_p_009; + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WriteMulti( + Dev, + VL53L1_SD_CONFIG__WOI_SD0, + buffer, + 2); + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WrByte( + Dev, + VL53L1_NVM_BIST__CTRL, + pssc_cfg->array_select); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_spad_rate_data( + VL53L1_DEV Dev, + VL53L1_spad_rate_data_t *pspad_rates) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + int i = 0; + + uint8_t VL53L1_p_002[512]; + uint8_t *pdata = &VL53L1_p_002[0]; + + LOG_FUNCTION_START(""); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_ReadMulti( + Dev, + VL53L1_PRIVATE__PATCH_BASE_ADDR_RSLV, + pdata, + 512); + + + + pdata = &VL53L1_p_002[0]; + for (i = 0; i < VL53L1_NO_OF_SPAD_ENABLES; i++) { + pspad_rates->rate_data[i] = + (uint16_t)VL53L1_decode_unsigned_integer(pdata, 2); + pdata += 2; + } + + + + + pspad_rates->VL53L1_p_023 = VL53L1_NO_OF_SPAD_ENABLES; + pspad_rates->no_of_values = VL53L1_NO_OF_SPAD_ENABLES; + pspad_rates->fractional_bits = 15; + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_calc_required_samples( + VL53L1_DEV Dev + ) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = VL53L1DevStructGetLLResultsHandle(Dev); + VL53L1_smudge_corrector_config_t *pconfig = + &(pdev->smudge_correct_config); + VL53L1_smudge_corrector_internals_t *pint = + &(pdev->smudge_corrector_internals); + + VL53L1_range_results_t *presults = &(pres->range_results); + VL53L1_range_data_t *pxmonitor = &(presults->xmonitor); + + uint32_t peak_duration_us = pxmonitor->peak_duration_us; + + uint64_t temp64a; + uint64_t temp64z; + + LOG_FUNCTION_START(""); + + temp64a = pxmonitor->VL53L1_p_021 + + pxmonitor->VL53L1_p_020; + temp64a = do_division_u((temp64a * 1000), peak_duration_us); + temp64a = do_division_u((temp64a * 1000), peak_duration_us); + + temp64z = pconfig->noise_margin * pxmonitor->VL53L1_p_006; + temp64a = temp64a * 1000 * 256; + temp64a = do_division_u(temp64a, temp64z); + temp64a = temp64a * 1000 * 256; + temp64a = do_division_u(temp64a, temp64z); + + pint->required_samples = (uint32_t)temp64a; + + + + if (pint->required_samples < 2) + pint->required_samples = 2; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_dynamic_xtalk_correction_calc_new_xtalk( + VL53L1_DEV Dev, + uint32_t xtalk_offset_out, + VL53L1_smudge_corrector_config_t *pconfig, + VL53L1_smudge_corrector_data_t *pout, + uint8_t add_smudge, + uint8_t soft_update + ) +{ + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + int16_t x_gradient_scaler; + int16_t y_gradient_scaler; + uint32_t orig_xtalk_offset; + int16_t orig_x_gradient; + int16_t orig_y_gradient; + int32_t itemp32; + VL53L1_xtalk_config_t *pX = &(pdev->xtalk_cfg); + VL53L1_xtalk_calibration_results_t *pC = &(pdev->xtalk_cal); + + LOG_FUNCTION_START(""); + + + + if (add_smudge == 1) { + pout->algo__crosstalk_compensation_plane_offset_kcps = + (uint32_t)xtalk_offset_out + + (uint32_t)pconfig->smudge_margin; + } else { + pout->algo__crosstalk_compensation_plane_offset_kcps = + (uint32_t)xtalk_offset_out; + } + + + + orig_xtalk_offset = + pX->nvm_default__crosstalk_compensation_plane_offset_kcps; + + orig_x_gradient = + pX->nvm_default__crosstalk_compensation_x_plane_gradient_kcps; + + orig_y_gradient = + pX->nvm_default__crosstalk_compensation_y_plane_gradient_kcps; + + if (((pconfig->user_scaler_set == 0) + || (pconfig->scaler_calc_method == 1)) && + (pC->algo__crosstalk_compensation_plane_offset_kcps + != 0)) { + + + orig_xtalk_offset = + pC->algo__crosstalk_compensation_plane_offset_kcps; + + orig_x_gradient = + pC->algo__crosstalk_compensation_x_plane_gradient_kcps; + + orig_y_gradient = + pC->algo__crosstalk_compensation_y_plane_gradient_kcps; + } + + + + if ((pconfig->user_scaler_set == 0) && (orig_x_gradient == 0)) + pout->gradient_zero_flag |= 0x01; + + if ((pconfig->user_scaler_set == 0) && (orig_y_gradient == 0)) + pout->gradient_zero_flag |= 0x02; + + + + + + + if (orig_xtalk_offset == 0) + orig_xtalk_offset = 1; + + + + + if (pconfig->user_scaler_set == 1) { + x_gradient_scaler = pconfig->x_gradient_scaler; + y_gradient_scaler = pconfig->y_gradient_scaler; + } else { + + + + + x_gradient_scaler = (int16_t)do_division_s( + (((int32_t)orig_x_gradient) << 6), + orig_xtalk_offset); + pconfig->x_gradient_scaler = x_gradient_scaler; + y_gradient_scaler = (int16_t)do_division_s( + (((int32_t)orig_y_gradient) << 6), + orig_xtalk_offset); + pconfig->y_gradient_scaler = y_gradient_scaler; + } + + + + + if (pconfig->scaler_calc_method == 0) { + + + + + + + itemp32 = (int32_t)( + pout->algo__crosstalk_compensation_plane_offset_kcps * + x_gradient_scaler); + itemp32 = itemp32 >> 6; + if (itemp32 > 0xFFFF) + itemp32 = 0xFFFF; + + pout->algo__crosstalk_compensation_x_plane_gradient_kcps = + (int16_t)itemp32; + + itemp32 = (int32_t)( + pout->algo__crosstalk_compensation_plane_offset_kcps * + y_gradient_scaler); + itemp32 = itemp32 >> 6; + if (itemp32 > 0xFFFF) + itemp32 = 0xFFFF; + + pout->algo__crosstalk_compensation_y_plane_gradient_kcps = + (int16_t)itemp32; + } else if (pconfig->scaler_calc_method == 1) { + + + + + + itemp32 = (int32_t)(orig_xtalk_offset - + pout->algo__crosstalk_compensation_plane_offset_kcps); + itemp32 = (int32_t)(do_division_s(itemp32, 16)); + itemp32 = itemp32 << 2; + itemp32 = itemp32 + (int32_t)(orig_x_gradient); + if (itemp32 > 0xFFFF) + itemp32 = 0xFFFF; + + pout->algo__crosstalk_compensation_x_plane_gradient_kcps = + (int16_t)itemp32; + + itemp32 = (int32_t)(orig_xtalk_offset - + pout->algo__crosstalk_compensation_plane_offset_kcps); + itemp32 = (int32_t)(do_division_s(itemp32, 80)); + itemp32 = itemp32 << 2; + itemp32 = itemp32 + (int32_t)(orig_y_gradient); + if (itemp32 > 0xFFFF) + itemp32 = 0xFFFF; + + pout->algo__crosstalk_compensation_y_plane_gradient_kcps = + (int16_t)itemp32; + } + + + + if (pconfig->smudge_corr_apply_enabled == 1 && + (soft_update != 1) + ) { + + + pout->new_xtalk_applied_flag = 1; + + + + pX->algo__crosstalk_compensation_plane_offset_kcps = + pout->algo__crosstalk_compensation_plane_offset_kcps; + pX->algo__crosstalk_compensation_x_plane_gradient_kcps = + pout->algo__crosstalk_compensation_x_plane_gradient_kcps; + pX->algo__crosstalk_compensation_y_plane_gradient_kcps = + pout->algo__crosstalk_compensation_y_plane_gradient_kcps; + + + + if (pconfig->smudge_corr_single_apply == 1) { + + + pconfig->smudge_corr_apply_enabled = 0; + pconfig->smudge_corr_single_apply = 0; + } + } + + + + if (soft_update != 1) + pout->smudge_corr_valid = 1; + + LOG_FUNCTION_END(status); + + return status; +} + +#define CONT_CONTINUE 0 +#define CONT_NEXT_LOOP 1 +#define CONT_RESET 2 +VL53L1_Error VL53L1_dynamic_xtalk_correction_corrector( + VL53L1_DEV Dev + ) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = VL53L1DevStructGetLLResultsHandle(Dev); + VL53L1_smudge_corrector_config_t *pconfig = + &(pdev->smudge_correct_config); + VL53L1_smudge_corrector_internals_t *pint = + &(pdev->smudge_corrector_internals); + VL53L1_smudge_corrector_data_t *pout = + &(pres->range_results.smudge_corrector_data); + VL53L1_range_results_t *pR = &(pres->range_results); + VL53L1_xtalk_config_t *pX = &(pdev->xtalk_cfg); + + uint8_t run_smudge_detection = 0; + uint8_t run_nodetect = 0; + uint8_t ambient_check = 0; + int32_t itemp32 = 0; + uint64_t utemp64 = 0; + uint8_t continue_processing = CONT_CONTINUE; + uint32_t xtalk_offset_out = 0; + uint32_t xtalk_offset_in = 0; + uint32_t current_xtalk = 0; + uint32_t smudge_margin_adjusted = 0; + uint8_t i = 0; + uint8_t nodetect_index = 0; + uint16_t amr; + + uint32_t cco; + + + + LOG_FUNCTION_START(""); + + + + VL53L1_dynamic_xtalk_correction_output_init(pres); + + + + ambient_check = (pconfig->smudge_corr_ambient_threshold == 0) || + (pconfig->smudge_corr_ambient_threshold > + ((uint32_t)pR->xmonitor.ambient_count_rate_mcps)); + + + + + run_smudge_detection = (pconfig->smudge_corr_enabled == 1) && + ambient_check && + (pR->xmonitor.range_status + == VL53L1_DEVICEERROR_RANGECOMPLETE); + + + + + if ((pR->xmonitor.range_status + != VL53L1_DEVICEERROR_RANGECOMPLETE) && + (pconfig->smudge_corr_enabled == 1)) { + + + run_nodetect = 2; + for (i = 0; i < pR->active_results; i++) { + if (pR->VL53L1_p_002[i].range_status == + VL53L1_DEVICEERROR_RANGECOMPLETE) { + if (pR->VL53L1_p_002[i].median_range_mm + <= + pconfig->nodetect_min_range_mm) { + run_nodetect = 0; + } else { + if (run_nodetect == 2) { + run_nodetect = 1; + nodetect_index = i; + } + } + } + } + + if (run_nodetect == 2) + + + run_nodetect = 0; + + amr = + pR->VL53L1_p_002[nodetect_index].ambient_count_rate_mcps; + + if (run_nodetect == 1) { + + + + + + + + utemp64 = 1000 * ((uint64_t)amr); + + + + utemp64 = utemp64 << 9; + + + + if (utemp64 < pconfig->nodetect_ambient_threshold) + run_nodetect = 1; + else + run_nodetect = 0; + + } + } + + + if (run_smudge_detection == 1) { + + + pint->nodetect_counter = 0; + + + + VL53L1_dynamic_xtalk_correction_calc_required_samples(Dev); + + + + xtalk_offset_in = + pR->xmonitor.VL53L1_p_012; + + + + cco = pX->algo__crosstalk_compensation_plane_offset_kcps; + current_xtalk = ((uint32_t)cco) << 2; + + + + smudge_margin_adjusted = + ((uint32_t)(pconfig->smudge_margin)) << 2; + + + + itemp32 = xtalk_offset_in - current_xtalk + + smudge_margin_adjusted; + + if (itemp32 < 0) + itemp32 = itemp32 * (-1); + + + if (itemp32 > ((int32_t)pconfig->single_xtalk_delta)) { + if ((int32_t)xtalk_offset_in > + ((int32_t)current_xtalk - + (int32_t)smudge_margin_adjusted)) { + pout->single_xtalk_delta_flag = 1; + } else { + pout->single_xtalk_delta_flag = 2; + } + } + + + + pint->current_samples = pint->current_samples + 1; + + + + if (pint->current_samples > pconfig->sample_limit) { + pout->sample_limit_exceeded_flag = 1; + continue_processing = CONT_RESET; + } else { + + pint->accumulator = pint->accumulator + + xtalk_offset_in; + } + + if (pint->current_samples < pint->required_samples) + continue_processing = CONT_NEXT_LOOP; + + + + xtalk_offset_out = + (uint32_t)(do_division_u(pint->accumulator, + pint->current_samples)); + + + + itemp32 = xtalk_offset_out - current_xtalk + + smudge_margin_adjusted; + + if (itemp32 < 0) + itemp32 = itemp32 * (-1); + + if (continue_processing == CONT_CONTINUE && + (itemp32 >= ((int32_t)(pconfig->averaged_xtalk_delta))) + ) { + if ((int32_t)xtalk_offset_out > + ((int32_t)current_xtalk - + (int32_t)smudge_margin_adjusted)) + pout->averaged_xtalk_delta_flag = 1; + else + pout->averaged_xtalk_delta_flag = 2; + } + + if (continue_processing == CONT_CONTINUE && + (itemp32 < ((int32_t)(pconfig->averaged_xtalk_delta))) + ) + + + continue_processing = CONT_RESET; + + + + + pout->smudge_corr_clipped = 0; + if ((continue_processing == CONT_CONTINUE) && + (pconfig->smudge_corr_clip_limit != 0)) { + if (xtalk_offset_out > + pconfig->smudge_corr_clip_limit) { + pout->smudge_corr_clipped = 1; + continue_processing = CONT_RESET; + } + } + + + + + + if (pconfig->user_xtalk_offset_limit_hi && + (xtalk_offset_out > + pconfig->user_xtalk_offset_limit)) + xtalk_offset_out = + pconfig->user_xtalk_offset_limit; + + + + + if ((pconfig->user_xtalk_offset_limit_hi == 0) && + (xtalk_offset_out < + pconfig->user_xtalk_offset_limit)) + xtalk_offset_out = + pconfig->user_xtalk_offset_limit; + + + + + xtalk_offset_out = xtalk_offset_out >> 2; + if (xtalk_offset_out > 0x3FFFF) + xtalk_offset_out = 0x3FFFF; + + + + if (continue_processing == CONT_CONTINUE) { + + + VL53L1_dynamic_xtalk_correction_calc_new_xtalk( + Dev, + xtalk_offset_out, + pconfig, + pout, + 1, + + 0 + + ); + + + + continue_processing = CONT_RESET; + } else { + + + VL53L1_dynamic_xtalk_correction_calc_new_xtalk( + Dev, + xtalk_offset_out, + pconfig, + pout, + 1, + + 1 + + ); + } + + + + if (continue_processing == CONT_RESET) { + pint->accumulator = 0; + pint->current_samples = 0; + pint->nodetect_counter = 0; + } + + } + + continue_processing = CONT_CONTINUE; + if (run_nodetect == 1) { + + + pint->nodetect_counter += 1; + + + + if (pint->nodetect_counter < pconfig->nodetect_sample_limit) + continue_processing = CONT_NEXT_LOOP; + + + + xtalk_offset_out = (uint32_t)(pconfig->nodetect_xtalk_offset); + + if (continue_processing == CONT_CONTINUE) { + + + VL53L1_dynamic_xtalk_correction_calc_new_xtalk( + Dev, + xtalk_offset_out, + pconfig, + pout, + 0, + + 0 + + ); + + + + pout->smudge_corr_valid = 2; + + + + continue_processing = CONT_RESET; + } else { + + + VL53L1_dynamic_xtalk_correction_calc_new_xtalk( + Dev, + xtalk_offset_out, + pconfig, + pout, + 0, + + 1 + + ); + } + + + + if (continue_processing == CONT_RESET) { + pint->accumulator = 0; + pint->current_samples = 0; + pint->nodetect_counter = 0; + } + } + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_dynamic_xtalk_correction_data_init( + VL53L1_DEV Dev + ) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = VL53L1DevStructGetLLResultsHandle(Dev); + + LOG_FUNCTION_START(""); + + + + + pdev->smudge_correct_config.smudge_corr_enabled = 1; + pdev->smudge_correct_config.smudge_corr_apply_enabled = 1; + pdev->smudge_correct_config.smudge_corr_single_apply = + VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_COR_SINGLE_APPLY_DEFAULT; + + pdev->smudge_correct_config.smudge_margin = + VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_MARGIN_DEFAULT; + pdev->smudge_correct_config.noise_margin = + VL53L1_TUNINGPARM_DYNXTALK_NOISE_MARGIN_DEFAULT; + pdev->smudge_correct_config.user_xtalk_offset_limit = + VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT_DEFAULT; + pdev->smudge_correct_config.user_xtalk_offset_limit_hi = + VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT_HI_DEFAULT; + pdev->smudge_correct_config.sample_limit = + VL53L1_TUNINGPARM_DYNXTALK_SAMPLE_LIMIT_DEFAULT; + pdev->smudge_correct_config.single_xtalk_delta = + VL53L1_TUNINGPARM_DYNXTALK_SINGLE_XTALK_DELTA_DEFAULT; + pdev->smudge_correct_config.averaged_xtalk_delta = + VL53L1_TUNINGPARM_DYNXTALK_AVERAGED_XTALK_DELTA_DEFAULT; + pdev->smudge_correct_config.smudge_corr_clip_limit = + VL53L1_TUNINGPARM_DYNXTALK_CLIP_LIMIT_DEFAULT; + pdev->smudge_correct_config.smudge_corr_ambient_threshold = + VL53L1_TUNINGPARM_DYNXTALK_XTALK_AMB_THRESHOLD_DEFAULT; + pdev->smudge_correct_config.scaler_calc_method = + 0; + + pdev->smudge_correct_config.x_gradient_scaler = + VL53L1_TUNINGPARM_DYNXTALK_XGRADIENT_SCALER_DEFAULT; + pdev->smudge_correct_config.y_gradient_scaler = + VL53L1_TUNINGPARM_DYNXTALK_YGRADIENT_SCALER_DEFAULT; + pdev->smudge_correct_config.user_scaler_set = + VL53L1_TUNINGPARM_DYNXTALK_USER_SCALER_SET_DEFAULT; + pdev->smudge_correct_config.nodetect_ambient_threshold = + VL53L1_TUNINGPARM_DYNXTALK_NODETECT_AMB_THRESHOLD_KCPS_DEFAULT; + pdev->smudge_correct_config.nodetect_sample_limit = + VL53L1_TUNINGPARM_DYNXTALK_NODETECT_SAMPLE_LIMIT_DEFAULT; + pdev->smudge_correct_config.nodetect_xtalk_offset = + VL53L1_TUNINGPARM_DYNXTALK_NODETECT_XTALK_OFFSET_KCPS_DEFAULT; + pdev->smudge_correct_config.nodetect_min_range_mm = + VL53L1_TUNINGPARM_DYNXTALK_NODETECT_MIN_RANGE_MM_DEFAULT; + + + + pdev->smudge_corrector_internals.current_samples = 0; + pdev->smudge_corrector_internals.required_samples = 0; + pdev->smudge_corrector_internals.accumulator = 0; + pdev->smudge_corrector_internals.nodetect_counter = 0; + + + + VL53L1_dynamic_xtalk_correction_output_init(pres); + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_dynamic_xtalk_correction_output_init( + VL53L1_LLDriverResults_t *pres + ) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_smudge_corrector_data_t *pdata; + + LOG_FUNCTION_START(""); + + + + pdata = &(pres->range_results.smudge_corrector_data); + + pdata->smudge_corr_valid = 0; + pdata->smudge_corr_clipped = 0; + pdata->single_xtalk_delta_flag = 0; + pdata->averaged_xtalk_delta_flag = 0; + pdata->sample_limit_exceeded_flag = 0; + pdata->gradient_zero_flag = 0; + pdata->new_xtalk_applied_flag = 0; + + pdata->algo__crosstalk_compensation_plane_offset_kcps = 0; + pdata->algo__crosstalk_compensation_x_plane_gradient_kcps = 0; + pdata->algo__crosstalk_compensation_y_plane_gradient_kcps = 0; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_xtalk_cal_data_init( + VL53L1_DEV Dev + ) +{ + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + + pdev->xtalk_cal.algo__crosstalk_compensation_plane_offset_kcps = 0; + pdev->xtalk_cal.algo__crosstalk_compensation_x_plane_gradient_kcps = 0; + pdev->xtalk_cal.algo__crosstalk_compensation_y_plane_gradient_kcps = 0; + + LOG_FUNCTION_END(status); + + return status; +} + + + + + + + + +VL53L1_Error VL53L1_low_power_auto_data_init( + VL53L1_DEV Dev + ) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->low_power_auto_data.vhv_loop_bound = + VL53L1_TUNINGPARM_LOWPOWERAUTO_VHV_LOOP_BOUND_DEFAULT; + pdev->low_power_auto_data.is_low_power_auto_mode = 0; + pdev->low_power_auto_data.low_power_auto_range_count = 0; + pdev->low_power_auto_data.saved_interrupt_config = 0; + pdev->low_power_auto_data.saved_vhv_init = 0; + pdev->low_power_auto_data.saved_vhv_timeout = 0; + pdev->low_power_auto_data.first_run_phasecal_result = 0; + pdev->low_power_auto_data.dss__total_rate_per_spad_mcps = 0; + pdev->low_power_auto_data.dss__required_spads = 0; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_low_power_auto_data_stop_range( + VL53L1_DEV Dev + ) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + + + pdev->low_power_auto_data.low_power_auto_range_count = 0xFF; + + pdev->low_power_auto_data.first_run_phasecal_result = 0; + pdev->low_power_auto_data.dss__total_rate_per_spad_mcps = 0; + pdev->low_power_auto_data.dss__required_spads = 0; + + + + if (pdev->low_power_auto_data.saved_vhv_init != 0) + pdev->stat_nvm.vhv_config__init = + pdev->low_power_auto_data.saved_vhv_init; + if (pdev->low_power_auto_data.saved_vhv_timeout != 0) + pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound = + pdev->low_power_auto_data.saved_vhv_timeout; + + + + pdev->gen_cfg.phasecal_config__override = 0x00; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_config_low_power_auto_mode( + VL53L1_general_config_t *pgeneral, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_low_power_auto_data_t *plpadata + ) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + plpadata->is_low_power_auto_mode = 1; + + + + plpadata->low_power_auto_range_count = 0; + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + + + + + + + VL53L1_SEQUENCE_RANGE_EN; + + + + pgeneral->dss_config__manual_effective_spads_select = 200 << 8; + pgeneral->dss_config__roi_mode_control = + VL53L1_DEVICEDSSMODE__REQUESTED_EFFFECTIVE_SPADS; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_low_power_auto_setup_manual_calibration( + VL53L1_DEV Dev) +{ + + + + + + + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + pdev->low_power_auto_data.saved_vhv_init = + pdev->stat_nvm.vhv_config__init; + pdev->low_power_auto_data.saved_vhv_timeout = + pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound; + + + + pdev->stat_nvm.vhv_config__init &= 0x7F; + + + pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound = + (pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound & 0x03) + + (pdev->low_power_auto_data.vhv_loop_bound << 2); + + + pdev->gen_cfg.phasecal_config__override = 0x01; + pdev->low_power_auto_data.first_run_phasecal_result = + pdev->dbg_results.phasecal_result__vcsel_start; + pdev->gen_cfg.cal_config__vcsel_start = + pdev->low_power_auto_data.first_run_phasecal_result; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_low_power_auto_update_DSS( + VL53L1_DEV Dev) +{ + + + + + + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + VL53L1_system_results_t *pS = &(pdev->sys_results); + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint32_t utemp32a; + + LOG_FUNCTION_START(""); + + + + + + + utemp32a = + pS->result__peak_signal_count_rate_crosstalk_corrected_mcps_sd0 + + pS->result__ambient_count_rate_mcps_sd0; + + + + if (utemp32a > 0xFFFF) + utemp32a = 0xFFFF; + + + + + + utemp32a = utemp32a << 16; + + + + if (pdev->sys_results.result__dss_actual_effective_spads_sd0 == 0) + status = VL53L1_ERROR_DIVISION_BY_ZERO; + else { + + + utemp32a = utemp32a / + pdev->sys_results.result__dss_actual_effective_spads_sd0; + + + pdev->low_power_auto_data.dss__total_rate_per_spad_mcps = + utemp32a; + + + + + utemp32a = pdev->stat_cfg.dss_config__target_total_rate_mcps << + 16; + + + + if (pdev->low_power_auto_data.dss__total_rate_per_spad_mcps + == 0) + status = VL53L1_ERROR_DIVISION_BY_ZERO; + else { + + + + utemp32a = utemp32a / + pdev->low_power_auto_data.dss__total_rate_per_spad_mcps; + + + + if (utemp32a > 0xFFFF) + utemp32a = 0xFFFF; + + + + pdev->low_power_auto_data.dss__required_spads = + (uint16_t)utemp32a; + + + + pdev->gen_cfg.dss_config__manual_effective_spads_select + = pdev->low_power_auto_data.dss__required_spads; + pdev->gen_cfg.dss_config__roi_mode_control = + VL53L1_DEVICEDSSMODE__REQUESTED_EFFFECTIVE_SPADS; + } + + } + + if (status == VL53L1_ERROR_DIVISION_BY_ZERO) { + + + + + + + pdev->low_power_auto_data.dss__required_spads = 0x8000; + + + + pdev->gen_cfg.dss_config__manual_effective_spads_select = + pdev->low_power_auto_data.dss__required_spads; + pdev->gen_cfg.dss_config__roi_mode_control = + VL53L1_DEVICEDSSMODE__REQUESTED_EFFFECTIVE_SPADS; + + + + status = VL53L1_ERROR_NONE; + } + + LOG_FUNCTION_END(status); + + return status; +} + + + + + diff --git a/drivers/input/misc/vl53L1/src/vl53l1_core_support.c b/drivers/input/misc/vl53L1/src/vl53l1_core_support.c new file mode 100644 index 000000000000..8a8947b6cfe2 --- /dev/null +++ b/drivers/input/misc/vl53L1/src/vl53l1_core_support.c @@ -0,0 +1,938 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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 "vl53l1_ll_def.h" +#include "vl53l1_ll_device.h" +#include "vl53l1_platform_log.h" +#include "vl53l1_core_support.h" +#include "vl53l1_platform_user_data.h" +#include "vl53l1_platform_user_defines.h" + + + + + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_CORE, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_CORE, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_CORE, \ + status, fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_CORE, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + + +uint32_t VL53L1_calc_pll_period_us( + uint16_t fast_osc_frequency) +{ + + + + + + + + + + + + + + + uint32_t pll_period_us = 0; + + LOG_FUNCTION_START(""); + + pll_period_us = (0x01 << 30) / fast_osc_frequency; + + + + + + + + LOG_FUNCTION_END(0); + + return pll_period_us; +} + + +uint32_t VL53L1_duration_maths( + uint32_t pll_period_us, + uint32_t vcsel_parm_pclks, + uint32_t window_vclks, + uint32_t elapsed_mclks) +{ + + + + + + + + + + + uint64_t tmp_long_int = 0; + uint32_t duration_us = 0; + + + + + + + duration_us = window_vclks * pll_period_us; + + + + + + duration_us = duration_us >> 12; + + + + tmp_long_int = (uint64_t)duration_us; + + + + + + + duration_us = elapsed_mclks * vcsel_parm_pclks; + + + + + + duration_us = duration_us >> 4; + + + + + + tmp_long_int = tmp_long_int * (uint64_t)duration_us; + + + + + + tmp_long_int = tmp_long_int >> 12; + + + + if (tmp_long_int > 0xFFFFFFFF) + tmp_long_int = 0xFFFFFFFF; + + duration_us = (uint32_t)tmp_long_int; + + return duration_us; +} + + +uint32_t VL53L1_events_per_spad_maths( + int32_t VL53L1_p_013, + uint16_t num_spads, + uint32_t duration) +{ + uint64_t total_hist_counts = 0; + uint64_t xtalk_per_spad = 0; + uint32_t rate_per_spad_kcps = 0; + + + + + + + + + + + + + + + + + + uint64_t dividend = ((uint64_t)VL53L1_p_013 + * 1000 * 256); + + total_hist_counts = do_division_u(dividend, (uint64_t)num_spads); + + + + + if (duration > 0) { + + + + + + + + + uint64_t dividend = (((uint64_t)(total_hist_counts << 11)) + + ((uint64_t)duration / 2)); + + xtalk_per_spad = do_division_u(dividend, (uint64_t)duration); + } else { + xtalk_per_spad = (uint64_t)(total_hist_counts << 11); + } + + rate_per_spad_kcps = (uint32_t)xtalk_per_spad; + + return rate_per_spad_kcps; +} + + +uint32_t VL53L1_isqrt(uint32_t num) +{ + + + + + + + + + uint32_t res = 0; + uint32_t bit = 1 << 30; + + + + + while (bit > num) + bit >>= 2; + + while (bit != 0) { + if (num >= res + bit) { + num -= res + bit; + res = (res >> 1) + bit; + } else { + res >>= 1; + } + bit >>= 2; + } + + return res; +} + + +void VL53L1_hist_calc_zero_distance_phase( + VL53L1_histogram_bin_data_t *pdata) +{ + + + + + + + uint32_t period = 0; + uint32_t VL53L1_p_017 = 0; + + LOG_FUNCTION_START(""); + + period = 2048 * + (uint32_t)VL53L1_decode_vcsel_period(pdata->VL53L1_p_009); + + VL53L1_p_017 = period; + VL53L1_p_017 += (uint32_t)pdata->phasecal_result__reference_phase; + VL53L1_p_017 += (2048 * (uint32_t)pdata->phasecal_result__vcsel_start); + VL53L1_p_017 -= (2048 * (uint32_t)pdata->cal_config__vcsel_start); + + VL53L1_p_017 = VL53L1_p_017 % period; + + pdata->zero_distance_phase = (uint16_t)VL53L1_p_017; + + LOG_FUNCTION_END(0); +} + + +void VL53L1_hist_estimate_ambient_from_thresholded_bins( + int32_t ambient_threshold_sigma, + VL53L1_histogram_bin_data_t *pdata) +{ + + + + + + + + + + uint8_t bin = 0; + int32_t VL53L1_p_032 = 0; + + LOG_FUNCTION_START(""); + + + + + + + VL53L1_hist_find_min_max_bin_values(pdata); + + + + + + + + VL53L1_p_032 = + (int32_t)VL53L1_isqrt((uint32_t)pdata->min_bin_value); + VL53L1_p_032 *= ambient_threshold_sigma; + VL53L1_p_032 += 0x07; + VL53L1_p_032 = VL53L1_p_032 >> 4; + VL53L1_p_032 += pdata->min_bin_value; + + + + + + + pdata->number_of_ambient_samples = 0; + pdata->ambient_events_sum = 0; + + for (bin = 0; bin < pdata->VL53L1_p_024; bin++) + if (pdata->bin_data[bin] < VL53L1_p_032) { + pdata->ambient_events_sum += pdata->bin_data[bin]; + pdata->number_of_ambient_samples++; + } + + + + + + + if (pdata->number_of_ambient_samples > 0) { + pdata->VL53L1_p_004 = + pdata->ambient_events_sum; + pdata->VL53L1_p_004 += + ((int32_t)pdata->number_of_ambient_samples/2); + pdata->VL53L1_p_004 /= + (int32_t)pdata->number_of_ambient_samples; + } + + LOG_FUNCTION_END(0); +} + + +void VL53L1_hist_remove_ambient_bins( + VL53L1_histogram_bin_data_t *pdata) +{ + + + + + + + + uint8_t bin = 0; + uint8_t lc = 0; + uint8_t i = 0; + + + + + if ((pdata->bin_seq[0] & 0x07) == 0x07) { + + i = 0; + for (lc = 0; lc < VL53L1_MAX_BIN_SEQUENCE_LENGTH; lc++) { + if ((pdata->bin_seq[lc] & 0x07) != 0x07) { + pdata->bin_seq[i] = pdata->bin_seq[lc]; + pdata->bin_rep[i] = pdata->bin_rep[lc]; + i++; + } + } + + + + + + + for (lc = i; lc < VL53L1_MAX_BIN_SEQUENCE_LENGTH; lc++) { + pdata->bin_seq[lc] = VL53L1_MAX_BIN_SEQUENCE_CODE + 1; + pdata->bin_rep[lc] = 0; + } + } + + if (pdata->number_of_ambient_bins > 0) { + + + + for (bin = pdata->number_of_ambient_bins; + bin < pdata->VL53L1_p_023; bin++) { + pdata->bin_data[bin-pdata->number_of_ambient_bins] = + pdata->bin_data[bin]; + } + + + + pdata->VL53L1_p_024 = + pdata->VL53L1_p_024 - + pdata->number_of_ambient_bins; + pdata->number_of_ambient_bins = 0; + } +} + + +uint32_t VL53L1_calc_pll_period_mm( + uint16_t fast_osc_frequency) +{ + + + + + + + uint32_t pll_period_us = 0; + uint32_t pll_period_mm = 0; + + LOG_FUNCTION_START(""); + + + + + + + pll_period_us = VL53L1_calc_pll_period_us(fast_osc_frequency); + + + + + + + + + + + + + pll_period_mm = + VL53L1_SPEED_OF_LIGHT_IN_AIR_DIV_8 * + (pll_period_us >> 2); + + + + pll_period_mm = (pll_period_mm + (0x01<<15)) >> 16; + + LOG_FUNCTION_END(0); + + return pll_period_mm; +} + + +uint16_t VL53L1_rate_maths( + int32_t VL53L1_p_008, + uint32_t time_us) +{ + + + + + + + + + + + + + uint32_t tmp_int = 0; + uint32_t frac_bits = 7; + uint16_t rate_mcps = 0; + + + + + + + + if (VL53L1_p_008 > VL53L1_SPAD_TOTAL_COUNT_MAX) + tmp_int = VL53L1_SPAD_TOTAL_COUNT_MAX; + else if (VL53L1_p_008 > 0) + tmp_int = (uint32_t)VL53L1_p_008; + + + + + + + + + if (VL53L1_p_008 > VL53L1_SPAD_TOTAL_COUNT_RES_THRES) + frac_bits = 3; + else + frac_bits = 7; + + + + + + + + if (time_us > 0) + tmp_int = ((tmp_int << frac_bits) + (time_us / 2)) / time_us; + + + + + + if (VL53L1_p_008 > VL53L1_SPAD_TOTAL_COUNT_RES_THRES) + tmp_int = tmp_int << 4; + + + + + + + + if (tmp_int > 0xFFFF) + tmp_int = 0xFFFF; + + rate_mcps = (uint16_t)tmp_int; + + return rate_mcps; +} + + +uint16_t VL53L1_rate_per_spad_maths( + uint32_t frac_bits, + uint32_t peak_count_rate, + uint16_t num_spads, + uint32_t max_output_value) +{ + + uint32_t tmp_int = 0; + + + + uint16_t rate_per_spad = 0; + + + + + + + + + + if (num_spads > 0) { + tmp_int = (peak_count_rate << 8) << frac_bits; + tmp_int = (tmp_int + + ((uint32_t)num_spads / 2)) / + (uint32_t)num_spads; + } else { + tmp_int = ((peak_count_rate) << frac_bits); + } + + + + + if (tmp_int > max_output_value) + tmp_int = max_output_value; + + rate_per_spad = (uint16_t)tmp_int; + + return rate_per_spad; +} + + +int32_t VL53L1_range_maths( + uint16_t fast_osc_frequency, + uint16_t VL53L1_p_017, + uint16_t zero_distance_phase, + uint8_t fractional_bits, + int32_t gain_factor, + int32_t range_offset_mm) +{ + + + + + + uint32_t pll_period_us = 0; + + int64_t tmp_long_int = 0; + int32_t range_mm = 0; + int32_t range_mm_10 = 0; + + + + + pll_period_us = VL53L1_calc_pll_period_us(fast_osc_frequency); + + + + + + + + + + + tmp_long_int = (int64_t)VL53L1_p_017 - (int64_t)zero_distance_phase; + + + + + + + + + + + tmp_long_int = tmp_long_int * (int64_t)pll_period_us; + + + + + + + tmp_long_int = tmp_long_int / (0x01 << 9); + + + + + + + + + + + + tmp_long_int = tmp_long_int * VL53L1_SPEED_OF_LIGHT_IN_AIR_DIV_8; + + + + + + + tmp_long_int = tmp_long_int / (0x01 << 22); + + + + range_mm = (int32_t)tmp_long_int + range_offset_mm; + + + + range_mm *= gain_factor; + range_mm += 0x0400; + range_mm /= 0x0800; + + + + if (fractional_bits == 0) { + range_mm_10 = range_mm * 10; + range_mm_10 = range_mm_10 / (0x01 << 2); + if ((range_mm_10 % 10) < 5) + range_mm = (int16_t)(range_mm_10 / 10); + else + range_mm = (int16_t)(range_mm_10 / 10 + 1); + } else if (fractional_bits == 1) + range_mm = range_mm / (0x01 << 1); + + return range_mm; +} + + +uint8_t VL53L1_decode_vcsel_period(uint8_t vcsel_period_reg) +{ + + + + + + + uint8_t VL53L1_p_031 = 0; + + VL53L1_p_031 = (vcsel_period_reg + 1) << 1; + + return VL53L1_p_031; +} + + +void VL53L1_copy_xtalk_bin_data_to_histogram_data_struct( + VL53L1_xtalk_histogram_shape_t *pxtalk, + VL53L1_histogram_bin_data_t *phist) +{ + + + + + + + phist->cal_config__vcsel_start = + pxtalk->cal_config__vcsel_start; + phist->VL53L1_p_019 = + pxtalk->VL53L1_p_019; + phist->VL53L1_p_022 = + pxtalk->VL53L1_p_022; + + phist->phasecal_result__reference_phase = + pxtalk->phasecal_result__reference_phase; + phist->phasecal_result__vcsel_start = + pxtalk->phasecal_result__vcsel_start; + + phist->vcsel_width = + pxtalk->vcsel_width; + phist->zero_distance_phase = + pxtalk->zero_distance_phase; + + phist->zone_id = pxtalk->zone_id; + phist->VL53L1_p_023 = pxtalk->VL53L1_p_023; + phist->time_stamp = pxtalk->time_stamp; +} + + +void VL53L1_init_histogram_bin_data_struct( + int32_t bin_value, + uint16_t VL53L1_p_024, + VL53L1_histogram_bin_data_t *pdata) +{ + + + + + + + uint16_t i = 0; + + pdata->cfg_device_state = VL53L1_DEVICESTATE_SW_STANDBY; + pdata->rd_device_state = VL53L1_DEVICESTATE_SW_STANDBY; + + pdata->zone_id = 0; + pdata->time_stamp = 0; + + pdata->VL53L1_p_022 = 0; + pdata->VL53L1_p_023 = VL53L1_HISTOGRAM_BUFFER_SIZE; + pdata->VL53L1_p_024 = (uint8_t)VL53L1_p_024; + pdata->number_of_ambient_bins = 0; + + pdata->result__interrupt_status = 0; + pdata->result__range_status = 0; + pdata->result__report_status = 0; + pdata->result__stream_count = 0; + + pdata->result__dss_actual_effective_spads = 0; + pdata->phasecal_result__reference_phase = 0; + pdata->phasecal_result__vcsel_start = 0; + pdata->cal_config__vcsel_start = 0; + + pdata->vcsel_width = 0; + pdata->VL53L1_p_009 = 0; + pdata->VL53L1_p_019 = 0; + pdata->total_periods_elapsed = 0; + + pdata->min_bin_value = 0; + pdata->max_bin_value = 0; + + pdata->zero_distance_phase = 0; + pdata->number_of_ambient_samples = 0; + pdata->ambient_events_sum = 0; + pdata->VL53L1_p_004 = 0; + + for (i = 0; i < VL53L1_MAX_BIN_SEQUENCE_LENGTH; i++) + pdata->bin_seq[i] = (uint8_t)i; + + for (i = 0; i < VL53L1_MAX_BIN_SEQUENCE_LENGTH; i++) + pdata->bin_rep[i] = 1; + + + for (i = 0; i < VL53L1_HISTOGRAM_BUFFER_SIZE; i++) + if (i < VL53L1_p_024) + pdata->bin_data[i] = bin_value; + else + pdata->bin_data[i] = 0; + + +} + + +void VL53L1_decode_row_col( + uint8_t spad_number, + uint8_t *prow, + uint8_t *pcol) +{ + + + + + + + + if (spad_number > 127) { + *prow = 8 + ((255-spad_number) & 0x07); + *pcol = (spad_number-128) >> 3; + } else { + *prow = spad_number & 0x07; + *pcol = (127-spad_number) >> 3; + } +} + + +void VL53L1_hist_find_min_max_bin_values( + VL53L1_histogram_bin_data_t *pdata) +{ + + + + + + uint8_t bin = 0; + + LOG_FUNCTION_START(""); + + for (bin = 0; bin < pdata->VL53L1_p_024; bin++) { + + if (bin == 0 || pdata->min_bin_value >= pdata->bin_data[bin]) + pdata->min_bin_value = pdata->bin_data[bin]; + + if (bin == 0 || pdata->max_bin_value <= pdata->bin_data[bin]) + pdata->max_bin_value = pdata->bin_data[bin]; + + } + + LOG_FUNCTION_END(0); + +} + + +void VL53L1_hist_estimate_ambient_from_ambient_bins( + VL53L1_histogram_bin_data_t *pdata) +{ + + + + + + + uint8_t bin = 0; + + LOG_FUNCTION_START(""); + + if (pdata->number_of_ambient_bins > 0) { + + pdata->number_of_ambient_samples = + pdata->number_of_ambient_bins; + + + + + pdata->ambient_events_sum = 0; + for (bin = 0; bin < pdata->number_of_ambient_bins; bin++) + pdata->ambient_events_sum += pdata->bin_data[bin]; + + pdata->VL53L1_p_004 = pdata->ambient_events_sum; + pdata->VL53L1_p_004 += + ((int32_t)pdata->number_of_ambient_bins / 2); + pdata->VL53L1_p_004 /= + (int32_t)pdata->number_of_ambient_bins; + + } + + LOG_FUNCTION_END(0); +} + + diff --git a/drivers/input/misc/vl53L1/src/vl53l1_error_strings.c b/drivers/input/misc/vl53L1/src/vl53l1_error_strings.c new file mode 100644 index 000000000000..e8e9920e4a36 --- /dev/null +++ b/drivers/input/misc/vl53L1/src/vl53l1_error_strings.c @@ -0,0 +1,334 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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 "vl53l1_error_codes.h" +#include "vl53l1_error_strings.h" +#include "vl53l1_platform_log.h" +#include "vl53l1_ll_def.h" + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_API, \ + status, fmt, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_get_pal_error_string( +#ifndef VL53L1_USE_EMPTY_STRING + VL53L1_Error PalErrorCode, +#endif + char *pPalErrorString) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + +#ifdef VL53L1_USE_EMPTY_STRING + SUPPRESS_UNUSED_WARNING(PalErrorCode); +#endif + + LOG_FUNCTION_START(""); + +#ifdef VL53L1_USE_EMPTY_STRING + VL53L1_COPYSTRING(pPalErrorString, ""); +#else + + switch (PalErrorCode) { + case VL53L1_ERROR_NONE: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_NONE); + break; + case VL53L1_ERROR_CALIBRATION_WARNING: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_CALIBRATION_WARNING); + break; + case VL53L1_ERROR_MIN_CLIPPED: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_MIN_CLIPPED); + break; + case VL53L1_ERROR_UNDEFINED: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_UNDEFINED); + break; + case VL53L1_ERROR_INVALID_PARAMS: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_INVALID_PARAMS); + break; + case VL53L1_ERROR_NOT_SUPPORTED: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_NOT_SUPPORTED); + break; + case VL53L1_ERROR_RANGE_ERROR: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_RANGE_ERROR); + break; + case VL53L1_ERROR_TIME_OUT: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_TIME_OUT); + break; + case VL53L1_ERROR_MODE_NOT_SUPPORTED: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_MODE_NOT_SUPPORTED); + break; + case VL53L1_ERROR_BUFFER_TOO_SMALL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_BUFFER_TOO_SMALL); + break; + case VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_COMMS_BUFFER_TOO_SMALL); + break; + case VL53L1_ERROR_GPIO_NOT_EXISTING: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_GPIO_NOT_EXISTING); + break; + case VL53L1_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED); + break; + case VL53L1_ERROR_CONTROL_INTERFACE: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_CONTROL_INTERFACE); + break; + case VL53L1_ERROR_INVALID_COMMAND: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_INVALID_COMMAND); + break; + case VL53L1_ERROR_DIVISION_BY_ZERO: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_DIVISION_BY_ZERO); + break; + case VL53L1_ERROR_REF_SPAD_INIT: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_REF_SPAD_INIT); + break; + case VL53L1_ERROR_GPH_SYNC_CHECK_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_GPH_SYNC_CHECK_FAIL); + break; + case VL53L1_ERROR_STREAM_COUNT_CHECK_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_STREAM_COUNT_CHECK_FAIL); + break; + case VL53L1_ERROR_GPH_ID_CHECK_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_GPH_ID_CHECK_FAIL); + break; + case VL53L1_ERROR_ZONE_STREAM_COUNT_CHECK_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_ZONE_STREAM_COUNT_CHECK_FAIL); + break; + case VL53L1_ERROR_ZONE_GPH_ID_CHECK_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_ZONE_GPH_ID_CHECK_FAIL); + break; + + case VL53L1_ERROR_XTALK_EXTRACTION_NO_SAMPLE_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_XTALK_EXTRACTION_NO_SAMPLES_FAIL); + break; + case VL53L1_ERROR_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL); + break; + + case VL53L1_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL); + break; + case VL53L1_ERROR_OFFSET_CAL_NO_SPADS_ENABLED_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_OFFSET_CAL_NO_SPADS_ENABLED_FAIL); + break; + case VL53L1_ERROR_ZONE_CAL_NO_SAMPLE_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_ZONE_CAL_NO_SAMPLE_FAIL); + break; + + case VL53L1_WARNING_OFFSET_CAL_MISSING_SAMPLES: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_OFFSET_CAL_MISSING_SAMPLES); + break; + case VL53L1_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH); + break; + case VL53L1_WARNING_OFFSET_CAL_RATE_TOO_HIGH: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_OFFSET_CAL_RATE_TOO_HIGH); + break; + case VL53L1_WARNING_OFFSET_CAL_SPAD_COUNT_TOO_LOW: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_OFFSET_CAL_SPAD_COUNT_TOO_LOW); + break; + + case VL53L1_WARNING_ZONE_CAL_MISSING_SAMPLES: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_ZONE_CAL_MISSING_SAMPLES); + break; + case VL53L1_WARNING_ZONE_CAL_SIGMA_TOO_HIGH: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_ZONE_CAL_SIGMA_TOO_HIGH); + break; + case VL53L1_WARNING_ZONE_CAL_RATE_TOO_HIGH: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_ZONE_CAL_RATE_TOO_HIGH); + break; + + case VL53L1_WARNING_REF_SPAD_CHAR_NOT_ENOUGH_SPADS: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_REF_SPAD_CHAR_NOT_ENOUGH_SPADS); + break; + case VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_HIGH: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_REF_SPAD_CHAR_RATE_TOO_HIGH); + break; + case VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_LOW: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_REF_SPAD_CHAR_RATE_TOO_LOW); + break; + + case VL53L1_WARNING_XTALK_MISSING_SAMPLES: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_XTALK_MISSING_SAMPLES); + break; + case VL53L1_WARNING_XTALK_NO_SAMPLES_FOR_GRADIENT: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_XTALK_NO_SAMPLES_FOR_GRADIENT); + break; + case VL53L1_WARNING_XTALK_SIGMA_LIMIT_FOR_GRADIENT: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_XTALK_SIGMA_LIMIT_FOR_GRADIENT); + break; + + case VL53L1_ERROR_DEVICE_FIRMWARE_TOO_OLD: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_DEVICE_FIRMWARE_TOO_OLD); + break; + case VL53L1_ERROR_DEVICE_FIRMWARE_TOO_NEW: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_DEVICE_FIRMWARE_TOO_NEW); + break; + case VL53L1_ERROR_UNIT_TEST_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_UNIT_TEST_FAIL); + break; + case VL53L1_ERROR_FILE_READ_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_FILE_READ_FAIL); + break; + case VL53L1_ERROR_FILE_WRITE_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_FILE_WRITE_FAIL); + break; + case VL53L1_ERROR_NOT_IMPLEMENTED: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_NOT_IMPLEMENTED); + break; + default: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_UNKNOW_ERROR_CODE); + } + +#endif + + LOG_FUNCTION_END(Status); + + return Status; +} + diff --git a/drivers/input/misc/vl53L1/src/vl53l1_hist_char.c b/drivers/input/misc/vl53L1/src/vl53l1_hist_char.c new file mode 100644 index 000000000000..5f13482e28e5 --- /dev/null +++ b/drivers/input/misc/vl53L1/src/vl53l1_hist_char.c @@ -0,0 +1,282 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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 + +#include + + + + + + + +#include "vl53l1_core.h" +#include "vl53l1_register_settings.h" +#include "vl53l1_hist_char.h" + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_HISTOGRAM, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_HISTOGRAM, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_HISTOGRAM,\ + status, fmt, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_set_calib_config( + VL53L1_DEV Dev, + uint8_t vcsel_delay__a0, + uint8_t calib_1, + uint8_t calib_2, + uint8_t calib_3, + uint8_t calib_2__a0, + uint8_t spad_readout) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[3]; + + LOG_FUNCTION_START(""); + + + + + status = VL53L1_enable_powerforce(Dev); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + + + + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__VCSEL_DELAY__A0, + vcsel_delay__a0); + } + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + + + comms_buffer[0] = calib_1; + comms_buffer[1] = calib_2; + comms_buffer[2] = calib_3; + + status = VL53L1_WriteMulti( + Dev, + VL53L1_RANGING_CORE__CALIB_1, + comms_buffer, + 3); + } + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__CALIB_2__A0, + calib_2__a0); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__SPAD_READOUT, + spad_readout); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_set_hist_calib_pulse_delay( + VL53L1_DEV Dev, + uint8_t calib_delay) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + status = + VL53L1_set_calib_config( + Dev, + 0x01, + + calib_delay, + + 0x04, + + 0x08, + + 0x14, + + VL53L1_RANGING_CORE__SPAD_READOUT__CALIB_PULSES); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_disable_calib_pulse_delay( + VL53L1_DEV Dev) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + status = + VL53L1_set_calib_config( + Dev, + 0x00, + + 0x00, + + 0x00, + + 0x00, + + 0x00, + + VL53L1_RANGING_CORE__SPAD_READOUT__STANDARD); + + LOG_FUNCTION_END(status); + + return status; +} + + diff --git a/drivers/input/misc/vl53L1/src/vl53l1_nvm.c b/drivers/input/misc/vl53L1/src/vl53l1_nvm.c new file mode 100644 index 000000000000..1a0175fa6a3f --- /dev/null +++ b/drivers/input/misc/vl53L1/src/vl53l1_nvm.c @@ -0,0 +1,1751 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + + +******************************************************************************** + +*/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + + + + + + +#include "vl53l1_ll_def.h" +#include "vl53l1_platform.h" +#include "vl53l1_platform_log.h" +#include "vl53l1_register_map.h" +#include "vl53l1_core.h" +#include "vl53l1_nvm_structs.h" +#include "vl53l1_nvm_map.h" +#include "vl53l1_nvm.h" + + + + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_NVM, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_NVM, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_NVM,\ + status, fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_NVM, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_nvm_enable( + VL53L1_DEV Dev, + uint16_t nvm_ctrl_pulse_width, + int32_t nvm_power_up_delay_us) +{ + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_disable_firmware(Dev); + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_powerforce(Dev); + + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WaitUs( + Dev, + VL53L1_ENABLE_POWERFORCE_SETTLING_TIME_US); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__NVM_CTRL__PDN, + 0x01); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__CLK_CTRL1, + 0x05); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WaitUs( + Dev, + nvm_power_up_delay_us); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__NVM_CTRL__MODE, + 0x01); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrWord( + Dev, + VL53L1_RANGING_CORE__NVM_CTRL__PULSE_WIDTH_MSB, + nvm_ctrl_pulse_width); + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_nvm_read( + VL53L1_DEV Dev, + uint8_t start_address, + uint8_t count, + uint8_t *pdata) +{ + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t nvm_addr = 0; + + LOG_FUNCTION_START(""); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%-12s = 0x%02X (%3u)\n", + "nvm_addr", nvm_addr, nvm_addr); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%-12s = 0x%02X (%3u)\n", + "count", count, count); + + for (nvm_addr = start_address; + nvm_addr < (start_address+count) ; nvm_addr++) { + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__NVM_CTRL__ADDR, + nvm_addr); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__NVM_CTRL__READN, + 0x00); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WaitUs( + Dev, + VL53L1_NVM_READ_TRIGGER_DELAY_US); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__NVM_CTRL__READN, + 0x01); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_RANGING_CORE__NVM_CTRL__DATAOUT_MMM, + pdata, + 4); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "NVM address : 0x%02X = 0x%02X%02X%02X%02X\n", + nvm_addr, *pdata, *(pdata+1), *(pdata+2), *(pdata+3)); + + + + + pdata = pdata + 4; + + + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_nvm_disable( + VL53L1_DEV Dev) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__NVM_CTRL__READN, + 0x01); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__NVM_CTRL__PDN, + 0x00); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_powerforce(Dev); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_nvm_format_decode( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_decoded_nvm_data_t *pdata) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i = 0; + uint8_t *ptmp = NULL; + int pptmp[VL53L1_NVM_MAX_FMT_RANGE_DATA]; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_NVM_SIZE_IN_BYTES) + return VL53L1_ERROR_BUFFER_TOO_SMALL; + + pdata->nvm__identification_model_id = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__IDENTIFICATION__MODEL_ID, + 0x000000FF, + 0, + 0); + pdata->nvm__identification_module_type = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__IDENTIFICATION__MODULE_TYPE, + 0x000000FF, + 0, + 0); + pdata->nvm__identification_revision_id = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__IDENTIFICATION__REVISION_ID, + 0x0000000F, + 0, + 0); + pdata->nvm__identification_module_id = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__IDENTIFICATION__MODULE_ID, + 0x0000FFFF, + 0, + 0); + pdata->nvm__i2c_valid = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__I2C_VALID, + 0x000000FF, + 0, + 0); + pdata->nvm__i2c_device_address_ews = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__I2C_SLAVE__DEVICE_ADDRESS, + 0x000000FF, + 0, + 0); + pdata->nvm__ews__fast_osc_frequency = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__EWS__OSC_MEASURED__FAST_OSC_FREQUENCY, + 0x0000FFFF, + 0, + 0); + pdata->nvm__ews__fast_osc_trim_max = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__FAST_OSC_TRIM_MAX, + 0x0000007F, + 0, + 0); + pdata->nvm__ews__fast_osc_freq_set = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__FAST_OSC_FREQ_SET, + 0x00000007, + 0, + 0); + pdata->nvm__ews__slow_osc_calibration = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__EWS__SLOW_OSC_CALIBRATION, + 0x000003FF, + 0, + 0); + pdata->nvm__fmt__fast_osc_frequency = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__FMT__OSC_MEASURED__FAST_OSC_FREQUENCY, + 0x0000FFFF, + 0, + 0); + pdata->nvm__fmt__fast_osc_trim_max = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FAST_OSC_TRIM_MAX, + 0x0000007F, + 0, + 0); + pdata->nvm__fmt__fast_osc_freq_set = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FAST_OSC_FREQ_SET, + 0x00000007, + 0, + 0); + pdata->nvm__fmt__slow_osc_calibration = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__FMT__SLOW_OSC_CALIBRATION, + 0x000003FF, + 0, + 0); + pdata->nvm__vhv_config_unlock = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__VHV_CONFIG_UNLOCK, + 0x000000FF, + 0, + 0); + pdata->nvm__ref_selvddpix = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__REF_SELVDDPIX, + 0x0000000F, + 0, + 0); + pdata->nvm__ref_selvquench = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__REF_SELVQUENCH, + 0x00000078, + 3, + 0); + pdata->nvm__regavdd1v2_sel = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__REGAVDD1V2_SEL_REGDVDD1V2_SEL, + 0x0000000C, + 2, + 0); + pdata->nvm__regdvdd1v2_sel = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__REGAVDD1V2_SEL_REGDVDD1V2_SEL, + 0x00000003, + 0, + 0); + pdata->nvm__vhv_timeout__macrop = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + + VL53L1_NVM__VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND, + 0x00000003, + 0, + 0); + pdata->nvm__vhv_loop_bound = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + + VL53L1_NVM__VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND, + 0x000000FC, + 2, + 0); + pdata->nvm__vhv_count_threshold = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__VHV_CONFIG__COUNT_THRESH, + 0x000000FF, + 0, + 0); + pdata->nvm__vhv_offset = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__VHV_CONFIG__OFFSET, + 0x0000003F, + 0, + 0); + pdata->nvm__vhv_init_enable = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__VHV_CONFIG__INIT, + 0x00000080, + 7, + 0); + pdata->nvm__vhv_init_value = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__VHV_CONFIG__INIT, + 0x0000003F, + 0, + 0); + pdata->nvm__laser_safety_vcsel_trim_ll = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__VCSEL_TRIM_LL, + 0x00000007, + 0, + 0); + pdata->nvm__laser_safety_vcsel_selion_ll = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__VCSEL_SELION_LL, + 0x0000003F, + 0, + 0); + pdata->nvm__laser_safety_vcsel_selion_max_ll = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__VCSEL_SELION_MAX_LL, + 0x0000003F, + 0, + 0); + pdata->nvm__laser_safety_mult_ll = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__MULT_LL, + 0x0000003F, + 0, + 0); + pdata->nvm__laser_safety_clip_ll = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__CLIP_LL, + 0x0000003F, + 0, + 0); + pdata->nvm__laser_safety_vcsel_trim_ld = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__VCSEL_TRIM_LD, + 0x00000007, + 0, + 0); + pdata->nvm__laser_safety_vcsel_selion_ld = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__VCSEL_SELION_LD, + 0x0000003F, + 0, + 0); + pdata->nvm__laser_safety_vcsel_selion_max_ld = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__VCSEL_SELION_MAX_LD, + 0x0000003F, + 0, + 0); + pdata->nvm__laser_safety_mult_ld = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__MULT_LD, + 0x0000003F, + 0, + 0); + pdata->nvm__laser_safety_clip_ld = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__CLIP_LD, + 0x0000003F, + 0, + 0); + pdata->nvm__laser_safety_lock_byte = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY_LOCK_BYTE, + 0x000000FF, + 0, + 0); + pdata->nvm__laser_safety_unlock_byte = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY_UNLOCK_BYTE, + 0x000000FF, + 0, + 0); + + + + + ptmp = pbuffer + VL53L1_NVM__EWS__SPAD_ENABLES_RTN_0_; + for (i = 0 ; i < VL53L1_RTN_SPAD_BUFFER_SIZE ; i++) + pdata->nvm__ews__spad_enables_rtn[i] = *ptmp++; + + ptmp = pbuffer + VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC1_0_; + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) + pdata->nvm__ews__spad_enables_ref__loc1[i] = *ptmp++; + + ptmp = pbuffer + VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC2_0_; + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) + pdata->nvm__ews__spad_enables_ref__loc2[i] = *ptmp++; + + ptmp = pbuffer + VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC3_0_; + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) + pdata->nvm__ews__spad_enables_ref__loc3[i] = *ptmp++; + + + + + ptmp = pbuffer + VL53L1_NVM__FMT__SPAD_ENABLES_RTN_0_; + for (i = 0 ; i < VL53L1_RTN_SPAD_BUFFER_SIZE ; i++) + pdata->nvm__fmt__spad_enables_rtn[i] = *ptmp++; + + ptmp = pbuffer + VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC1_0_; + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) + pdata->nvm__fmt__spad_enables_ref__loc1[i] = *ptmp++; + + ptmp = pbuffer + VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC2_0_; + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) + pdata->nvm__fmt__spad_enables_ref__loc2[i] = *ptmp++; + + ptmp = pbuffer + VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC3_0_; + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) + pdata->nvm__fmt__spad_enables_ref__loc3[i] = *ptmp++; + + + pdata->nvm__fmt__roi_config__mode_roi_centre_spad = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + + VL53L1_NVM__FMT__ROI_CONFIG__MODE_ROI_CENTRE_SPAD, + 0x000000FF, + 0, + 0); + pdata->nvm__fmt__roi_config__mode_roi_x_size = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + + VL53L1_NVM__FMT__ROI_CONFIG__MODE_ROI_XY_SIZE, + 0x000000F0, + 4, + 0); + pdata->nvm__fmt__roi_config__mode_roi_y_size = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__ROI_CONFIG__MODE_ROI_XY_SIZE, + 0x0000000F, + 0, + 0); + pdata->nvm__fmt__ref_spad_apply__num_requested_ref_spad = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + + VL53L1_NVM__FMT__REF_SPAD_APPLY__NUM_REQUESTED_REF_SPAD, + 0x000000FF, + 0, + 0); + pdata->nvm__fmt__ref_spad_man__ref_location = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__REF_SPAD_MAN__REF_LOCATION, + 0x00000003, + 0, + 0); + pdata->nvm__fmt__mm_config__inner_offset_mm = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__FMT__MM_CONFIG__INNER_OFFSET_MM, + 0x0000FFFF, + 0, + 0); + pdata->nvm__fmt__mm_config__outer_offset_mm = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__FMT__MM_CONFIG__OUTER_OFFSET_MM, + 0x0000FFFF, + 0, + 0); + pdata->nvm__fmt__algo_part_to_part_range_offset_mm = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__FMT__ALGO__PART_TO_PART_RANGE_OFFSET_MM, + 0x00000FFF, + 0, + 0); + pdata->nvm__fmt__algo__crosstalk_compensation_plane_offset_kcps = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__FMT__ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS, + 0x0000FFFF, + 0, + 0); + pdata->nvm__fmt__algo__crosstalk_compensation_x_plane_gradient_kcps = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__FMT__ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS, + 0x0000FFFF, + 0, + 0); + pdata->nvm__fmt__algo__crosstalk_compensation_y_plane_gradient_kcps = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__FMT__ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS, + 0x0000FFFF, + 0, + 0); + pdata->nvm__fmt__spare__host_config__nvm_config_spare_0 = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + + VL53L1_NVM__FMT__SPARE_HOST_CONFIG__NVM_CONFIG_SPARE_0, + 0x000000FF, + 0, + 0); + pdata->nvm__fmt__spare__host_config__nvm_config_spare_1 = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + + VL53L1_NVM__FMT__SPARE_HOST_CONFIG__NVM_CONFIG_SPARE_1, + 0x000000FF, + 0, + 0); + pdata->nvm__customer_space_programmed = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__CUSTOMER_NVM_SPACE_PROGRAMMED, + 0x000000FF, + 0, + 0); + pdata->nvm__cust__i2c_device_address = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__CUST__I2C_SLAVE__DEVICE_ADDRESS, + 0x000000FF, + 0, + 0); + pdata->nvm__cust__ref_spad_apply__num_requested_ref_spad = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + + VL53L1_NVM__CUST__REF_SPAD_APPLY__NUM_REQUESTED_REF_SPAD, + 0x000000FF, + 0, + 0); + pdata->nvm__cust__ref_spad_man__ref_location = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__CUST__REF_SPAD_MAN__REF_LOCATION, + 0x00000003, + 0, + 0); + pdata->nvm__cust__mm_config__inner_offset_mm = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__CUST__MM_CONFIG__INNER_OFFSET_MM, + 0x0000FFFF, + 0, + 0); + pdata->nvm__cust__mm_config__outer_offset_mm = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__CUST__MM_CONFIG__OUTER_OFFSET_MM, + 0x0000FFFF, + 0, + 0); + pdata->nvm__cust__algo_part_to_part_range_offset_mm = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__CUST__ALGO__PART_TO_PART_RANGE_OFFSET_MM, + 0x00000FFF, + 0, + 0); + pdata->nvm__cust__algo__crosstalk_compensation_plane_offset_kcps = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__CUST__ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS, + 0x0000FFFF, + 0, + 0); + pdata->nvm__cust__algo__crosstalk_compensation_x_plane_gradient_kcps = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__CUST__ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS, + 0x0000FFFF, + 0, + 0); + pdata->nvm__cust__algo__crosstalk_compensation_y_plane_gradient_kcps = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__CUST__ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS, + 0x0000FFFF, + 0, + 0); + pdata->nvm__cust__spare__host_config__nvm_config_spare_0 = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__CUST__SPARE_HOST_CONFIG__NVM_CONFIG_SPARE_0, + 0x000000FF, + 0, + 0); + pdata->nvm__cust__spare__host_config__nvm_config_spare_1 = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + + VL53L1_NVM__CUST__SPARE_HOST_CONFIG__NVM_CONFIG_SPARE_1, + 0x000000FF, + 0, + 0); + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_nvm_decode_optical_centre( + buf_size, + pbuffer + VL53L1_NVM__FMT__OPTICAL_CENTRE_DATA_INDEX, + &(pdata->fmt_optical_centre)); + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_nvm_decode_cal_peak_rate_map( + buf_size, + pbuffer + VL53L1_NVM__FMT__CAL_PEAK_RATE_MAP_DATA_INDEX, + &(pdata->fmt_peak_rate_map)); + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_nvm_decode_additional_offset_cal_data( + buf_size, + pbuffer + + VL53L1_NVM__FMT__ADDITIONAL_OFFSET_CAL_DATA_INDEX, + &(pdata->fmt_add_offset_data)); + + + + + pptmp[0] = VL53L1_NVM__FMT__RANGE_RESULTS__140MM_MM_PRE_RANGE; + pptmp[1] = VL53L1_NVM__FMT__RANGE_RESULTS__140MM_DARK; + pptmp[2] = VL53L1_NVM__FMT__RANGE_RESULTS__400MM_DARK; + pptmp[3] = VL53L1_NVM__FMT__RANGE_RESULTS__400MM_AMBIENT; + + for (i = 0 ; i < VL53L1_NVM_MAX_FMT_RANGE_DATA ; i++) { + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_nvm_decode_fmt_range_results_data( + buf_size, + pbuffer + pptmp[i], + &(pdata->fmt_range_data[i])); + } + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_nvm_decode_fmt_info( + buf_size, + pbuffer, + &(pdata->fmt_info)); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_nvm_decode_ews_info( + buf_size, + pbuffer, + &(pdata->ews_info)); + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_nvm_decode_optical_centre( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_optical_centre_t *pdata) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint16_t tmp = 0; + + if (buf_size < VL53L1_NVM__FMT__OPTICAL_CENTRE_DATA_SIZE) + return VL53L1_ERROR_BUFFER_TOO_SMALL; + + + + + + tmp = 0x0100; + tmp -= (uint16_t)*(pbuffer + 2); + if (tmp > 0x0FF) + tmp = 0; + + pdata->x_centre = (uint8_t)tmp; + pdata->y_centre = *(pbuffer + 3); + + return status; +} + + +VL53L1_Error VL53L1_nvm_decode_cal_peak_rate_map( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_cal_peak_rate_map_t *pdata) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t *ptmp = NULL; + uint8_t i = 0; + + if (buf_size < VL53L1_NVM__FMT__CAL_PEAK_RATE_MAP_DATA_SIZE) + return VL53L1_ERROR_BUFFER_TOO_SMALL; + + pdata->cal_distance_mm = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer); + + pdata->cal_reflectance_pc = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 2); + pdata->cal_reflectance_pc = + pdata->cal_reflectance_pc >> 6; + + pdata->max_samples = VL53L1_NVM_PEAK_RATE_MAP_SAMPLES; + pdata->width = VL53L1_NVM_PEAK_RATE_MAP_WIDTH; + pdata->height = VL53L1_NVM_PEAK_RATE_MAP_HEIGHT; + + ptmp = pbuffer + 4; + for (i = 0 ; i < VL53L1_NVM_PEAK_RATE_MAP_SAMPLES ; i++) { + pdata->peak_rate_mcps[i] = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, ptmp); + ptmp += 2; + } + + return status; +} + + +VL53L1_Error VL53L1_nvm_decode_additional_offset_cal_data( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_additional_offset_cal_data_t *pdata) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + if (buf_size < VL53L1_NVM__FMT__ADDITIONAL_OFFSET_CAL_DATA_SIZE) + return VL53L1_ERROR_BUFFER_TOO_SMALL; + + pdata->result__mm_inner_actual_effective_spads = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer); + + pdata->result__mm_outer_actual_effective_spads = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 2); + + pdata->result__mm_inner_peak_signal_count_rtn_mcps = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 4); + + pdata->result__mm_outer_peak_signal_count_rtn_mcps = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 6); + + return status; +} + + +VL53L1_Error VL53L1_nvm_decode_fmt_range_results_data( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_decoded_nvm_fmt_range_data_t *pdata) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + if (buf_size < VL53L1_NVM__FMT__RANGE_RESULTS__SIZE_BYTES) + return VL53L1_ERROR_BUFFER_TOO_SMALL; + + pdata->result__actual_effective_rtn_spads = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer); + + pdata->ref_spad_array__num_requested_ref_spads = + *(pbuffer+2); + + pdata->ref_spad_array__ref_location = + *(pbuffer+3); + + pdata->result__peak_signal_count_rate_rtn_mcps = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 4); + + pdata->result__ambient_count_rate_rtn_mcps = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 6); + + pdata->result__peak_signal_count_rate_ref_mcps = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 8); + + pdata->result__ambient_count_rate_ref_mcps = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 10); + + pdata->measured_distance_mm = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 12); + + pdata->measured_distance_stdev_mm = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 14); + + return status; +} + + +VL53L1_Error VL53L1_nvm_decode_fmt_info( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_decoded_nvm_fmt_info_t *pdata) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + if (buf_size < VL53L1_NVM_SIZE_IN_BYTES) + return VL53L1_ERROR_BUFFER_TOO_SMALL; + + pdata->nvm__fmt__fgc[0] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_0, + 0x000000FE, + 1, + 0); + pdata->nvm__fmt__fgc[1] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_1, + 0x000001FC, + 2, + 0); + pdata->nvm__fmt__fgc[2] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_2 - 1, + 0x000003F8, + 3, + 0); + pdata->nvm__fmt__fgc[3] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_3 - 1, + 0x000007F0, + 4, + 0); + pdata->nvm__fmt__fgc[4] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_4 - 1, + 0x00000FE0, + 5, + 0); + pdata->nvm__fmt__fgc[5] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_5 - 1, + 0x00001FC0, + 6, + 0); + pdata->nvm__fmt__fgc[6] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_6 - 1, + 0x00003F80, + 7, + 0); + pdata->nvm__fmt__fgc[7] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_6, + 0x0000007F, + 0, + 0); + pdata->nvm__fmt__fgc[8] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_7, + 0x000000FE, + 1, + 0); + pdata->nvm__fmt__fgc[9] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_8, + 0x000001FC, + 2, + 0); + pdata->nvm__fmt__fgc[10] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_9 - 1, + 0x000003F8, + 3, + 0); + pdata->nvm__fmt__fgc[11] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_10 - 1, + 0x000007F0, + 4, + 0); + pdata->nvm__fmt__fgc[12] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_11 - 1, + 0x00000FE0, + 5, + 0); + pdata->nvm__fmt__fgc[13] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_12 - 1, + 0x00001FC0, + 6, + 0); + pdata->nvm__fmt__fgc[14] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_13 - 1, + 0x00003F80, + 7, + 0); + pdata->nvm__fmt__fgc[15] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_13, + 0x0000007F, + 0, + 0); + pdata->nvm__fmt__fgc[16] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_14, + 0x000000FE, + 1, + 0); + pdata->nvm__fmt__fgc[17] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_15, + 0x000001FC, + 2, + 0); + pdata->nvm__fmt__fgc[18] = 0x00; + + pdata->nvm__fmt__test_program_major = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__TEST_PROGRAM_MAJOR_MINOR, + 0x000000E0, + 5, + 0); + pdata->nvm__fmt__test_program_minor = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__TEST_PROGRAM_MAJOR_MINOR, + 0x0000001F, + 0, + 0); + pdata->nvm__fmt__map_major = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__MAP_MAJOR_MINOR, + 0x000000E0, + 5, + 0); + pdata->nvm__fmt__map_minor = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__MAP_MAJOR_MINOR, + 0x0000001F, + 0, + 0); + pdata->nvm__fmt__year = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__YEAR_MONTH, + 0x000000F0, + 4, + 0); + pdata->nvm__fmt__month = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__YEAR_MONTH, + 0x0000000F, + 0, + 0); + pdata->nvm__fmt__day = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__DAY_MODULE_DATE_PHASE, + 0x000000F8, + 3, + 0); + pdata->nvm__fmt__module_date_phase = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__DAY_MODULE_DATE_PHASE, + 0x00000007, + 0, + 0); + pdata->nvm__fmt__time = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__FMT__TIME, + 0x0000FFFF, + 0, + 0); + pdata->nvm__fmt__tester_id = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__TESTER_ID, + 0x000000FF, + 0, + 0); + pdata->nvm__fmt__site_id = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__SITE_ID, + 0x000000FF, + 0, + 0); + + return status; +} + + +VL53L1_Error VL53L1_nvm_decode_ews_info( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_decoded_nvm_ews_info_t *pdata) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + if (buf_size < VL53L1_NVM_SIZE_IN_BYTES) + return VL53L1_ERROR_BUFFER_TOO_SMALL; + + pdata->nvm__ews__test_program_major = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__TEST_PROGRAM_MAJOR_MINOR, + 0x000000E0, + 5, + 0); + pdata->nvm__ews__test_program_minor = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__TEST_PROGRAM_MAJOR_MINOR, + 0x0000001F, + 0, + 0); + pdata->nvm__ews__probe_card_major = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__PROBE_CARD_MAJOR_MINOR, + 0x000000F0, + 4, + 0); + pdata->nvm__ews__probe_card_minor = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__PROBE_CARD_MAJOR_MINOR, + 0x0000000F, + 0, + 0); + pdata->nvm__ews__tester_id = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__TESTER_ID, + 0x000000FF, + 0, + 0); + pdata->nvm__ews__lot[0] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__LOT__BYTE_0, + 0x000000FC, + 2, + 32); + pdata->nvm__ews__lot[1] = + (char)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__EWS__LOT__BYTE_1 - 1, + 0x000003F0, + 4, + 32); + pdata->nvm__ews__lot[2] = + (char)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__EWS__LOT__BYTE_2 - 1, + 0x00000FC0, + 6, + 32); + pdata->nvm__ews__lot[3] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__LOT__BYTE_2, + 0x0000003F, + 0, + 32); + pdata->nvm__ews__lot[4] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__LOT__BYTE_3, + 0x000000FC, + 2, + 32); + pdata->nvm__ews__lot[5] = + (char)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__EWS__LOT__BYTE_4 - 1, + 0x000003F0, + 4, + 32); + pdata->nvm__ews__lot[6] = + (char)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__EWS__LOT__BYTE_5 - 1, + 0x00000FC0, + 6, + 32); + + pdata->nvm__ews__lot[7] = 0x00; + + pdata->nvm__ews__wafer = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__WAFER, + 0x0000001F, + 0, + 0); + pdata->nvm__ews__xcoord = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__XCOORD, + 0x000000FF, + 0, + 0); + pdata->nvm__ews__ycoord = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__YCOORD, + 0x000000FF, + 0, + 0); + + return status; + +} + + +void VL53L1_nvm_format_encode( + VL53L1_decoded_nvm_data_t *pnvm_info, + uint8_t *pnvm_data) +{ + SUPPRESS_UNUSED_WARNING(pnvm_info); + SUPPRESS_UNUSED_WARNING(pnvm_data); +} + + +VL53L1_Error VL53L1_read_nvm_raw_data( + VL53L1_DEV Dev, + uint8_t start_address, + uint8_t count, + uint8_t *pnvm_raw_data) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_nvm_enable( + Dev, + 0x0004, + VL53L1_NVM_POWER_UP_DELAY_US); + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_nvm_read( + Dev, + start_address, + count, + pnvm_raw_data); + + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_nvm_disable(Dev); + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_read_nvm( + VL53L1_DEV Dev, + uint8_t nvm_format, + VL53L1_decoded_nvm_data_t *pnvm_info) +{ + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + + + uint8_t nvm_data[2*VL53L1_NVM_SIZE_IN_BYTES]; + + LOG_FUNCTION_START(""); + + SUPPRESS_UNUSED_WARNING(nvm_format); + + + + + status = VL53L1_read_nvm_raw_data( + Dev, + 0, + VL53L1_NVM_SIZE_IN_BYTES >> 2, + nvm_data); + + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_nvm_format_decode( + VL53L1_NVM_SIZE_IN_BYTES, + nvm_data, + pnvm_info); + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_read_nvm_optical_centre( + VL53L1_DEV Dev, + VL53L1_optical_centre_t *pcentre) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + + + uint8_t nvm_data[2*VL53L1_NVM__FMT__OPTICAL_CENTRE_DATA_SIZE]; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_read_nvm_raw_data( + Dev, + (uint8_t)(VL53L1_NVM__FMT__OPTICAL_CENTRE_DATA_INDEX + >> 2), + (uint8_t)(VL53L1_NVM__FMT__OPTICAL_CENTRE_DATA_SIZE + >> 2), + nvm_data); + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_nvm_decode_optical_centre( + VL53L1_NVM__FMT__OPTICAL_CENTRE_DATA_SIZE, + nvm_data, + pcentre); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_read_nvm_cal_peak_rate_map( + VL53L1_DEV Dev, + VL53L1_cal_peak_rate_map_t *pcal_data) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + + + uint8_t nvm_data[2*VL53L1_NVM__FMT__CAL_PEAK_RATE_MAP_DATA_SIZE]; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_read_nvm_raw_data( + Dev, + (uint8_t)(VL53L1_NVM__FMT__CAL_PEAK_RATE_MAP_DATA_INDEX + >> 2), + (uint8_t)(VL53L1_NVM__FMT__CAL_PEAK_RATE_MAP_DATA_SIZE + >> 2), + nvm_data); + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_nvm_decode_cal_peak_rate_map( + VL53L1_NVM__FMT__CAL_PEAK_RATE_MAP_DATA_SIZE, + nvm_data, + pcal_data); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_read_nvm_additional_offset_cal_data( + VL53L1_DEV Dev, + VL53L1_additional_offset_cal_data_t *pcal_data) +{ + + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + + + uint8_t nvm_data[2*VL53L1_NVM__FMT__ADDITIONAL_OFFSET_CAL_DATA_SIZE]; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_read_nvm_raw_data( + Dev, + (uint8_t)( + VL53L1_NVM__FMT__ADDITIONAL_OFFSET_CAL_DATA_INDEX >> 2), + (uint8_t)( + VL53L1_NVM__FMT__ADDITIONAL_OFFSET_CAL_DATA_SIZE >> 2), + nvm_data); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_nvm_decode_additional_offset_cal_data( + VL53L1_NVM__FMT__ADDITIONAL_OFFSET_CAL_DATA_SIZE, + nvm_data, + pcal_data); + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_read_nvm_fmt_range_results_data( + VL53L1_DEV Dev, + uint16_t range_results_select, + VL53L1_decoded_nvm_fmt_range_data_t *prange_data) +{ + + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + + + uint8_t nvm_data[2*VL53L1_NVM__FMT__RANGE_RESULTS__SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + + + + status = VL53L1_read_nvm_raw_data( + Dev, + (uint8_t)(range_results_select >> 2), + (uint8_t)(VL53L1_NVM__FMT__RANGE_RESULTS__SIZE_BYTES >> 2), + nvm_data); + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_nvm_decode_fmt_range_results_data( + VL53L1_NVM__FMT__RANGE_RESULTS__SIZE_BYTES, + nvm_data, + prange_data); + + LOG_FUNCTION_END(status); + + return status; + +} + + diff --git a/drivers/input/misc/vl53L1/src/vl53l1_nvm_debug.c b/drivers/input/misc/vl53L1/src/vl53l1_nvm_debug.c new file mode 100644 index 000000000000..b1acfd144577 --- /dev/null +++ b/drivers/input/misc/vl53L1/src/vl53l1_nvm_debug.c @@ -0,0 +1,1126 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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 "vl53l1_ll_def.h" +#include "vl53l1_platform.h" +#include "vl53l1_platform_log.h" +#include "vl53l1_register_map.h" +#include "vl53l1_api_debug.h" +#include "vl53l1_nvm_structs.h" +#include "vl53l1_nvm_debug.h" + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_NVM, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_NVM, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_NVM,\ + status, fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(trace_flags, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + +#ifdef VL53L1_LOG_ENABLE + +void VL53L1_print_nvm_raw_data( + uint8_t *pnvm_raw_data, + uint32_t trace_flags) +{ + + + + + + int i = 0; + + LOG_FUNCTION_START(""); + + for (i = 0 ; i < VL53L1_NVM_SIZE_IN_BYTES ; i++) { + if (i % 4 == 0) + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "\n NVM Addr 0x%02X : 0x", + i/4); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%02X", + *pnvm_raw_data++); + } + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "\n"); + + LOG_FUNCTION_END(0); +} + + +void VL53L1_print_decoded_nvm_data( + VL53L1_decoded_nvm_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + char fp_text[VL53L1_MAX_STRING_LENGTH]; + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + uint8_t i = 0; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__identification_model_id", + pdata->nvm__identification_model_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__identification_module_type", + pdata->nvm__identification_module_type); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__identification_revision_id", + pdata->nvm__identification_revision_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__identification_module_id", + pdata->nvm__identification_module_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__i2c_valid", + pdata->nvm__i2c_valid); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__i2c_device_address_ews", + pdata->nvm__i2c_device_address_ews); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->nvm__ews__fast_osc_frequency, + 12, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__ews__fast_osc_frequency", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__fast_osc_trim_max", + pdata->nvm__ews__fast_osc_trim_max); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__fast_osc_freq_set", + pdata->nvm__ews__fast_osc_freq_set); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__slow_osc_calibration", + pdata->nvm__ews__slow_osc_calibration); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->nvm__fmt__fast_osc_frequency, + 12, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__fmt__fast_osc_frequency", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__fast_osc_trim_max", + pdata->nvm__fmt__fast_osc_trim_max); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__fast_osc_freq_set", + pdata->nvm__fmt__fast_osc_freq_set); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__slow_osc_calibration", + pdata->nvm__fmt__slow_osc_calibration); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__vhv_config_unlock", + pdata->nvm__vhv_config_unlock); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ref_selvddpix", + pdata->nvm__ref_selvddpix); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ref_selvquench", + pdata->nvm__ref_selvquench); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__regavdd1v2_sel", + pdata->nvm__regavdd1v2_sel); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__regdvdd1v2_sel", + pdata->nvm__regdvdd1v2_sel); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__vhv_timeout__macrop", + pdata->nvm__vhv_timeout__macrop); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__vhv_loop_bound", + pdata->nvm__vhv_loop_bound); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__vhv_count_threshold", + pdata->nvm__vhv_count_threshold); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__vhv_offset", + pdata->nvm__vhv_offset); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__vhv_init_enable", + pdata->nvm__vhv_init_enable); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__vhv_init_value", + pdata->nvm__vhv_init_value); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_vcsel_trim_ll", + pdata->nvm__laser_safety_vcsel_trim_ll); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_vcsel_selion_ll", + pdata->nvm__laser_safety_vcsel_selion_ll); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_vcsel_selion_max_ll", + pdata->nvm__laser_safety_vcsel_selion_max_ll); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_mult_ll", + pdata->nvm__laser_safety_mult_ll); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_clip_ll", + pdata->nvm__laser_safety_clip_ll); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_vcsel_trim_ld", + pdata->nvm__laser_safety_vcsel_trim_ld); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_vcsel_selion_ld", + pdata->nvm__laser_safety_vcsel_selion_ld); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_vcsel_selion_max_ld", + pdata->nvm__laser_safety_vcsel_selion_max_ld); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_mult_ld", + pdata->nvm__laser_safety_mult_ld); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_clip_ld", + pdata->nvm__laser_safety_clip_ld); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_lock_byte", + pdata->nvm__laser_safety_lock_byte); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_unlock_byte", + pdata->nvm__laser_safety_unlock_byte); + + + + + for (i = 0 ; i < VL53L1_RTN_SPAD_BUFFER_SIZE ; i++) { + sprintf( + ppre_text, + "%snvm__ews__spad_enables_rtn[%u]", + pprefix, i); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->nvm__ews__spad_enables_rtn[i]); + } + + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) { + sprintf( + ppre_text, + "%snvm__ews__spad_enables_ref__loc1[%u]", + pprefix, i); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->nvm__ews__spad_enables_ref__loc1[i]); + } + + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) { + sprintf( + ppre_text, + "%snvm__ews__spad_enables_ref__loc2[%u]", + pprefix, i); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->nvm__ews__spad_enables_ref__loc2[i]); + } + + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) { + sprintf( + ppre_text, + "%snvm__ews__spad_enables_ref__loc3[%u]", + pprefix, i); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->nvm__ews__spad_enables_ref__loc3[i]); + } + + + + + + for (i = 0 ; i < VL53L1_RTN_SPAD_BUFFER_SIZE ; i++) { + sprintf( + ppre_text, + "%snvm__fmt__spad_enables_rtn[%u]", + pprefix, i); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->nvm__fmt__spad_enables_rtn[i]); + } + + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) { + sprintf( + ppre_text, + "%snvm__fmt__spad_enables_ref__loc1[%u]", + pprefix, i); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->nvm__fmt__spad_enables_ref__loc1[i]); + } + + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) { + sprintf( + ppre_text, + "%snvm__fmt__spad_enables_ref__loc2[%u]", + pprefix, i); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->nvm__fmt__spad_enables_ref__loc2[i]); + } + + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) { + sprintf( + ppre_text, + "%snvm__fmt__spad_enables_ref__loc3[%u]", + pprefix, i); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->nvm__fmt__spad_enables_ref__loc3[i]); + } + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__roi_config__mode_roi_centre_spad", + pdata->nvm__fmt__roi_config__mode_roi_centre_spad); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__roi_config__mode_roi_x_size", + pdata->nvm__fmt__roi_config__mode_roi_x_size); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__roi_config__mode_roi_y_size", + pdata->nvm__fmt__roi_config__mode_roi_y_size); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__ref_spad_apply__num_requested_ref_spad", + pdata->nvm__fmt__ref_spad_apply__num_requested_ref_spad); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__ref_spad_man__ref_location", + pdata->nvm__fmt__ref_spad_man__ref_location); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "nvm__fmt__mm_config__inner_offset_mm", + pdata->nvm__fmt__mm_config__inner_offset_mm); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "nvm__fmt__mm_config__outer_offset_mm", + pdata->nvm__fmt__mm_config__outer_offset_mm); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->nvm__fmt__algo_part_to_part_range_offset_mm, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__fmt__algo_part_to_part_range_offset_mm", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)( + pdata->nvm__fmt__algo__crosstalk_compensation_plane_offset_kcps), + 9, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__fmt__algo__crosstalk_compensation_plane_offset_kcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)( + pdata->nvm__fmt__algo__crosstalk_compensation_x_plane_gradient_kcps), + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__fmt__algo__crosstalk_compensation_x_plane_gradient_kcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)( + pdata->nvm__fmt__algo__crosstalk_compensation_y_plane_gradient_kcps), + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__fmt__algo__crosstalk_compensation_y_plane_gradient_kcps", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__spare__host_config__nvm_config_spare_0", + pdata->nvm__fmt__spare__host_config__nvm_config_spare_0); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__spare__host_config__nvm_config_spare_1", + pdata->nvm__fmt__spare__host_config__nvm_config_spare_1); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__customer_space_programmed", + pdata->nvm__customer_space_programmed); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__cust__i2c_device_address", + pdata->nvm__cust__i2c_device_address); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__cust__ref_spad_apply__num_requested_ref_spad", + pdata->nvm__cust__ref_spad_apply__num_requested_ref_spad); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__cust__ref_spad_man__ref_location", + pdata->nvm__cust__ref_spad_man__ref_location); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "nvm__cust__mm_config__inner_offset_mm", + pdata->nvm__cust__mm_config__inner_offset_mm); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "nvm__cust__mm_config__outer_offset_mm", + pdata->nvm__cust__mm_config__outer_offset_mm); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->nvm__cust__algo_part_to_part_range_offset_mm, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__cust__algo_part_to_part_range_offset_mm", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (uint32_t)( + pdata->nvm__cust__algo__crosstalk_compensation_plane_offset_kcps), + 9, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__cust__algo__crosstalk_compensation_plane_offset_kcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)( + pdata->nvm__cust__algo__crosstalk_compensation_x_plane_gradient_kcps), + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__cust__algo__crosstalk_compensation_x_plane_gradient_kcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)( + pdata->nvm__cust__algo__crosstalk_compensation_y_plane_gradient_kcps), + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__cust__algo__crosstalk_compensation_y_plane_gradient_kcps", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__cust__spare__host_config__nvm_config_spare_0", + pdata->nvm__cust__spare__host_config__nvm_config_spare_0); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__cust__spare__host_config__nvm_config_spare_1", + pdata->nvm__cust__spare__host_config__nvm_config_spare_1); + + + + + sprintf( + ppre_text, + "%sfmt_optical_centre.", pprefix); + + VL53L1_print_optical_centre( + &(pdata->fmt_optical_centre), + ppre_text, + VL53L1_TRACE_MODULE_NVM_DATA); + + + + + sprintf( + ppre_text, + "%sfmt_peak_rate_map.", pprefix); + + VL53L1_print_cal_peak_rate_map( + &(pdata->fmt_peak_rate_map), + ppre_text, + VL53L1_TRACE_MODULE_NVM_DATA); + + + + + sprintf( + ppre_text, + "%sfmt_add_offset_data.", + pprefix); + + VL53L1_print_additional_offset_cal_data( + &(pdata->fmt_add_offset_data), + ppre_text, + VL53L1_TRACE_MODULE_NVM_DATA); + + + + + for (i = 0 ; i < VL53L1_NVM_MAX_FMT_RANGE_DATA ; i++) { + sprintf( + ppre_text, + "%sfmt_range_data[%u].", + pprefix, i); + + VL53L1_print_decoded_nvm_fmt_range_data( + &(pdata->fmt_range_data[i]), + ppre_text, + trace_flags); + } + + sprintf( + ppre_text, + "%sfmt_info.", + pprefix); + + VL53L1_print_decoded_nvm_fmt_info( + &(pdata->fmt_info), + ppre_text, + trace_flags); + + sprintf( + ppre_text, + "%sews_info.", + pprefix); + + VL53L1_print_decoded_nvm_ews_info( + &(pdata->ews_info), + ppre_text, + trace_flags); +} + + +void VL53L1_print_decoded_nvm_fmt_range_data( + VL53L1_decoded_nvm_fmt_range_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__actual_effective_rtn_spads, + 8, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "result__actual_effective_rtn_spads", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ref_spad_array__num_requested_ref_spads", + pdata->ref_spad_array__num_requested_ref_spads); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ref_spad_array__ref_location", + pdata->ref_spad_array__ref_location); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__peak_signal_count_rate_rtn_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "result__peak_signal_count_rate_rtn_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__ambient_count_rate_rtn_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "result__ambient_count_rate_rtn_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__peak_signal_count_rate_ref_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "result__peak_signal_count_rate_ref_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__ambient_count_rate_ref_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "result__ambient_count_rate_ref_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->measured_distance_mm, + 4, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "measured_distance_mm", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (uint32_t)pdata->measured_distance_stdev_mm, + 4, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "measured_distance_stdev_mm", + fp_text); +} + + +void VL53L1_print_decoded_nvm_fmt_info( + VL53L1_decoded_nvm_fmt_info_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = \"%s\"\n", + pprefix, + "nvm__fmt__fgc", + pdata->nvm__fmt__fgc); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__test_program_major", + pdata->nvm__fmt__test_program_major); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__test_program_minor", + pdata->nvm__fmt__test_program_minor); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__map_major", + pdata->nvm__fmt__map_major); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__map_minor", + pdata->nvm__fmt__map_minor); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__year", + pdata->nvm__fmt__year); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__month", + pdata->nvm__fmt__month); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__day", + pdata->nvm__fmt__day); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__module_date_phase", + pdata->nvm__fmt__module_date_phase); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__time", + pdata->nvm__fmt__time); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__tester_id", + pdata->nvm__fmt__tester_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__site_id", + pdata->nvm__fmt__site_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__test_program_major", + pdata->nvm__ews__test_program_major); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__test_program_minor", + pdata->nvm__ews__test_program_minor); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__probe_card_major", + pdata->nvm__ews__probe_card_major); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__probe_card_minor", + pdata->nvm__ews__probe_card_minor); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__tester_id", + pdata->nvm__ews__tester_id); +} + + +void VL53L1_print_decoded_nvm_ews_info( + VL53L1_decoded_nvm_ews_info_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = \"%s\"\n", + pprefix, + "nvm__ews__lot", + pdata->nvm__ews__lot); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__wafer", + pdata->nvm__ews__wafer); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__xcoord", + pdata->nvm__ews__xcoord); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__ycoord", + pdata->nvm__ews__ycoord); +} + +#endif + + + diff --git a/drivers/input/misc/vl53L1/src/vl53l1_register_funcs.c b/drivers/input/misc/vl53L1/src/vl53l1_register_funcs.c new file mode 100644 index 000000000000..21d886e8ae77 --- /dev/null +++ b/drivers/input/misc/vl53L1/src/vl53l1_register_funcs.c @@ -0,0 +1,4596 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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 "vl53l1_ll_def.h" +#include "vl53l1_platform.h" +#include "vl53l1_platform_log.h" +#include "vl53l1_core.h" +#include "vl53l1_register_map.h" +#include "vl53l1_register_structs.h" +#include "vl53l1_register_funcs.h" + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_REGISTERS, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_REGISTERS, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_REGISTERS,\ + status, fmt, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_i2c_encode_static_nvm_managed( + VL53L1_static_nvm_managed_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->i2c_slave__device_address & 0x7F; + *(pbuffer + 1) = + pdata->ana_config__vhv_ref_sel_vddpix & 0xF; + *(pbuffer + 2) = + pdata->ana_config__vhv_ref_sel_vquench & 0x7F; + *(pbuffer + 3) = + pdata->ana_config__reg_avdd1v2_sel & 0x3; + *(pbuffer + 4) = + pdata->ana_config__fast_osc__trim & 0x7F; + VL53L1_i2c_encode_uint16_t( + pdata->osc_measured__fast_osc__frequency, + 2, + pbuffer + 5); + *(pbuffer + 7) = + pdata->vhv_config__timeout_macrop_loop_bound; + *(pbuffer + 8) = + pdata->vhv_config__count_thresh; + *(pbuffer + 9) = + pdata->vhv_config__offset & 0x3F; + *(pbuffer + 10) = + pdata->vhv_config__init; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_static_nvm_managed( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_static_nvm_managed_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->i2c_slave__device_address = + (*(pbuffer + 0)) & 0x7F; + pdata->ana_config__vhv_ref_sel_vddpix = + (*(pbuffer + 1)) & 0xF; + pdata->ana_config__vhv_ref_sel_vquench = + (*(pbuffer + 2)) & 0x7F; + pdata->ana_config__reg_avdd1v2_sel = + (*(pbuffer + 3)) & 0x3; + pdata->ana_config__fast_osc__trim = + (*(pbuffer + 4)) & 0x7F; + pdata->osc_measured__fast_osc__frequency = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 5)); + pdata->vhv_config__timeout_macrop_loop_bound = + (*(pbuffer + 7)); + pdata->vhv_config__count_thresh = + (*(pbuffer + 8)); + pdata->vhv_config__offset = + (*(pbuffer + 9)) & 0x3F; + pdata->vhv_config__init = + (*(pbuffer + 10)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_static_nvm_managed( + VL53L1_DEV Dev, + VL53L1_static_nvm_managed_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_static_nvm_managed( + pdata, + VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_I2C_SLAVE__DEVICE_ADDRESS, + comms_buffer, + VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_static_nvm_managed( + VL53L1_DEV Dev, + VL53L1_static_nvm_managed_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_ReadMulti( + Dev, + VL53L1_I2C_SLAVE__DEVICE_ADDRESS, + comms_buffer, + VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_static_nvm_managed( + VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_customer_nvm_managed( + VL53L1_customer_nvm_managed_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->global_config__spad_enables_ref_0; + *(pbuffer + 1) = + pdata->global_config__spad_enables_ref_1; + *(pbuffer + 2) = + pdata->global_config__spad_enables_ref_2; + *(pbuffer + 3) = + pdata->global_config__spad_enables_ref_3; + *(pbuffer + 4) = + pdata->global_config__spad_enables_ref_4; + *(pbuffer + 5) = + pdata->global_config__spad_enables_ref_5 & 0xF; + *(pbuffer + 6) = + pdata->global_config__ref_en_start_select; + *(pbuffer + 7) = + pdata->ref_spad_man__num_requested_ref_spads & 0x3F; + *(pbuffer + 8) = + pdata->ref_spad_man__ref_location & 0x3; + VL53L1_i2c_encode_uint16_t( + pdata->algo__crosstalk_compensation_plane_offset_kcps, + 2, + pbuffer + 9); + VL53L1_i2c_encode_int16_t( + pdata->algo__crosstalk_compensation_x_plane_gradient_kcps, + 2, + pbuffer + 11); + VL53L1_i2c_encode_int16_t( + pdata->algo__crosstalk_compensation_y_plane_gradient_kcps, + 2, + pbuffer + 13); + VL53L1_i2c_encode_uint16_t( + pdata->ref_spad_char__total_rate_target_mcps, + 2, + pbuffer + 15); + VL53L1_i2c_encode_int16_t( + pdata->algo__part_to_part_range_offset_mm & 0x1FFF, + 2, + pbuffer + 17); + VL53L1_i2c_encode_int16_t( + pdata->mm_config__inner_offset_mm, + 2, + pbuffer + 19); + VL53L1_i2c_encode_int16_t( + pdata->mm_config__outer_offset_mm, + 2, + pbuffer + 21); + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_customer_nvm_managed( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_customer_nvm_managed_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->global_config__spad_enables_ref_0 = + (*(pbuffer + 0)); + pdata->global_config__spad_enables_ref_1 = + (*(pbuffer + 1)); + pdata->global_config__spad_enables_ref_2 = + (*(pbuffer + 2)); + pdata->global_config__spad_enables_ref_3 = + (*(pbuffer + 3)); + pdata->global_config__spad_enables_ref_4 = + (*(pbuffer + 4)); + pdata->global_config__spad_enables_ref_5 = + (*(pbuffer + 5)) & 0xF; + pdata->global_config__ref_en_start_select = + (*(pbuffer + 6)); + pdata->ref_spad_man__num_requested_ref_spads = + (*(pbuffer + 7)) & 0x3F; + pdata->ref_spad_man__ref_location = + (*(pbuffer + 8)) & 0x3; + pdata->algo__crosstalk_compensation_plane_offset_kcps = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 9)); + pdata->algo__crosstalk_compensation_x_plane_gradient_kcps = + (VL53L1_i2c_decode_int16_t(2, pbuffer + 11)); + pdata->algo__crosstalk_compensation_y_plane_gradient_kcps = + (VL53L1_i2c_decode_int16_t(2, pbuffer + 13)); + pdata->ref_spad_char__total_rate_target_mcps = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 15)); + pdata->algo__part_to_part_range_offset_mm = + (VL53L1_i2c_decode_int16_t(2, pbuffer + 17)) & 0x1FFF; + pdata->mm_config__inner_offset_mm = + (VL53L1_i2c_decode_int16_t(2, pbuffer + 19)); + pdata->mm_config__outer_offset_mm = + (VL53L1_i2c_decode_int16_t(2, pbuffer + 21)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_customer_nvm_managed( + VL53L1_DEV Dev, + VL53L1_customer_nvm_managed_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_customer_nvm_managed( + pdata, + VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_0, + comms_buffer, + VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_customer_nvm_managed( + VL53L1_DEV Dev, + VL53L1_customer_nvm_managed_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_ReadMulti( + Dev, + VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_0, + comms_buffer, + VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_customer_nvm_managed( + VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_static_config( + VL53L1_static_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + VL53L1_i2c_encode_uint16_t( + pdata->dss_config__target_total_rate_mcps, + 2, + pbuffer + 0); + *(pbuffer + 2) = + pdata->debug__ctrl & 0x1; + *(pbuffer + 3) = + pdata->test_mode__ctrl & 0xF; + *(pbuffer + 4) = + pdata->clk_gating__ctrl & 0xF; + *(pbuffer + 5) = + pdata->nvm_bist__ctrl & 0x1F; + *(pbuffer + 6) = + pdata->nvm_bist__num_nvm_words & 0x7F; + *(pbuffer + 7) = + pdata->nvm_bist__start_address & 0x7F; + *(pbuffer + 8) = + pdata->host_if__status & 0x1; + *(pbuffer + 9) = + pdata->pad_i2c_hv__config; + *(pbuffer + 10) = + pdata->pad_i2c_hv__extsup_config & 0x1; + *(pbuffer + 11) = + pdata->gpio_hv_pad__ctrl & 0x3; + *(pbuffer + 12) = + pdata->gpio_hv_mux__ctrl & 0x1F; + *(pbuffer + 13) = + pdata->gpio__tio_hv_status & 0x3; + *(pbuffer + 14) = + pdata->gpio__fio_hv_status & 0x3; + *(pbuffer + 15) = + pdata->ana_config__spad_sel_pswidth & 0x7; + *(pbuffer + 16) = + pdata->ana_config__vcsel_pulse_width_offset & 0x1F; + *(pbuffer + 17) = + pdata->ana_config__fast_osc__config_ctrl & 0x1; + *(pbuffer + 18) = + pdata->sigma_estimator__effective_pulse_width_ns; + *(pbuffer + 19) = + pdata->sigma_estimator__effective_ambient_width_ns; + *(pbuffer + 20) = + pdata->sigma_estimator__sigma_ref_mm; + *(pbuffer + 21) = + pdata->algo__crosstalk_compensation_valid_height_mm; + *(pbuffer + 22) = + pdata->spare_host_config__static_config_spare_0; + *(pbuffer + 23) = + pdata->spare_host_config__static_config_spare_1; + VL53L1_i2c_encode_uint16_t( + pdata->algo__range_ignore_threshold_mcps, + 2, + pbuffer + 24); + *(pbuffer + 26) = + pdata->algo__range_ignore_valid_height_mm; + *(pbuffer + 27) = + pdata->algo__range_min_clip; + *(pbuffer + 28) = + pdata->algo__consistency_check__tolerance & 0xF; + *(pbuffer + 29) = + pdata->spare_host_config__static_config_spare_2; + *(pbuffer + 30) = + pdata->sd_config__reset_stages_msb & 0xF; + *(pbuffer + 31) = + pdata->sd_config__reset_stages_lsb; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_static_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_static_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->dss_config__target_total_rate_mcps = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 0)); + pdata->debug__ctrl = + (*(pbuffer + 2)) & 0x1; + pdata->test_mode__ctrl = + (*(pbuffer + 3)) & 0xF; + pdata->clk_gating__ctrl = + (*(pbuffer + 4)) & 0xF; + pdata->nvm_bist__ctrl = + (*(pbuffer + 5)) & 0x1F; + pdata->nvm_bist__num_nvm_words = + (*(pbuffer + 6)) & 0x7F; + pdata->nvm_bist__start_address = + (*(pbuffer + 7)) & 0x7F; + pdata->host_if__status = + (*(pbuffer + 8)) & 0x1; + pdata->pad_i2c_hv__config = + (*(pbuffer + 9)); + pdata->pad_i2c_hv__extsup_config = + (*(pbuffer + 10)) & 0x1; + pdata->gpio_hv_pad__ctrl = + (*(pbuffer + 11)) & 0x3; + pdata->gpio_hv_mux__ctrl = + (*(pbuffer + 12)) & 0x1F; + pdata->gpio__tio_hv_status = + (*(pbuffer + 13)) & 0x3; + pdata->gpio__fio_hv_status = + (*(pbuffer + 14)) & 0x3; + pdata->ana_config__spad_sel_pswidth = + (*(pbuffer + 15)) & 0x7; + pdata->ana_config__vcsel_pulse_width_offset = + (*(pbuffer + 16)) & 0x1F; + pdata->ana_config__fast_osc__config_ctrl = + (*(pbuffer + 17)) & 0x1; + pdata->sigma_estimator__effective_pulse_width_ns = + (*(pbuffer + 18)); + pdata->sigma_estimator__effective_ambient_width_ns = + (*(pbuffer + 19)); + pdata->sigma_estimator__sigma_ref_mm = + (*(pbuffer + 20)); + pdata->algo__crosstalk_compensation_valid_height_mm = + (*(pbuffer + 21)); + pdata->spare_host_config__static_config_spare_0 = + (*(pbuffer + 22)); + pdata->spare_host_config__static_config_spare_1 = + (*(pbuffer + 23)); + pdata->algo__range_ignore_threshold_mcps = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 24)); + pdata->algo__range_ignore_valid_height_mm = + (*(pbuffer + 26)); + pdata->algo__range_min_clip = + (*(pbuffer + 27)); + pdata->algo__consistency_check__tolerance = + (*(pbuffer + 28)) & 0xF; + pdata->spare_host_config__static_config_spare_2 = + (*(pbuffer + 29)); + pdata->sd_config__reset_stages_msb = + (*(pbuffer + 30)) & 0xF; + pdata->sd_config__reset_stages_lsb = + (*(pbuffer + 31)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_static_config( + VL53L1_DEV Dev, + VL53L1_static_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_static_config( + pdata, + VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_DSS_CONFIG__TARGET_TOTAL_RATE_MCPS, + comms_buffer, + VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_static_config( + VL53L1_DEV Dev, + VL53L1_static_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_ReadMulti( + Dev, + VL53L1_DSS_CONFIG__TARGET_TOTAL_RATE_MCPS, + comms_buffer, + VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_static_config( + VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_general_config( + VL53L1_general_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->gph_config__stream_count_update_value; + *(pbuffer + 1) = + pdata->global_config__stream_divider; + *(pbuffer + 2) = + pdata->system__interrupt_config_gpio; + *(pbuffer + 3) = + pdata->cal_config__vcsel_start & 0x7F; + VL53L1_i2c_encode_uint16_t( + pdata->cal_config__repeat_rate & 0xFFF, + 2, + pbuffer + 4); + *(pbuffer + 6) = + pdata->global_config__vcsel_width & 0x7F; + *(pbuffer + 7) = + pdata->phasecal_config__timeout_macrop; + *(pbuffer + 8) = + pdata->phasecal_config__target; + *(pbuffer + 9) = + pdata->phasecal_config__override & 0x1; + *(pbuffer + 11) = + pdata->dss_config__roi_mode_control & 0x7; + VL53L1_i2c_encode_uint16_t( + pdata->system__thresh_rate_high, + 2, + pbuffer + 12); + VL53L1_i2c_encode_uint16_t( + pdata->system__thresh_rate_low, + 2, + pbuffer + 14); + VL53L1_i2c_encode_uint16_t( + pdata->dss_config__manual_effective_spads_select, + 2, + pbuffer + 16); + *(pbuffer + 18) = + pdata->dss_config__manual_block_select; + *(pbuffer + 19) = + pdata->dss_config__aperture_attenuation; + *(pbuffer + 20) = + pdata->dss_config__max_spads_limit; + *(pbuffer + 21) = + pdata->dss_config__min_spads_limit; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_general_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_general_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->gph_config__stream_count_update_value = + (*(pbuffer + 0)); + pdata->global_config__stream_divider = + (*(pbuffer + 1)); + pdata->system__interrupt_config_gpio = + (*(pbuffer + 2)); + pdata->cal_config__vcsel_start = + (*(pbuffer + 3)) & 0x7F; + pdata->cal_config__repeat_rate = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 4)) & 0xFFF; + pdata->global_config__vcsel_width = + (*(pbuffer + 6)) & 0x7F; + pdata->phasecal_config__timeout_macrop = + (*(pbuffer + 7)); + pdata->phasecal_config__target = + (*(pbuffer + 8)); + pdata->phasecal_config__override = + (*(pbuffer + 9)) & 0x1; + pdata->dss_config__roi_mode_control = + (*(pbuffer + 11)) & 0x7; + pdata->system__thresh_rate_high = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 12)); + pdata->system__thresh_rate_low = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 14)); + pdata->dss_config__manual_effective_spads_select = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 16)); + pdata->dss_config__manual_block_select = + (*(pbuffer + 18)); + pdata->dss_config__aperture_attenuation = + (*(pbuffer + 19)); + pdata->dss_config__max_spads_limit = + (*(pbuffer + 20)); + pdata->dss_config__min_spads_limit = + (*(pbuffer + 21)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_general_config( + VL53L1_DEV Dev, + VL53L1_general_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_general_config( + pdata, + VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_GPH_CONFIG__STREAM_COUNT_UPDATE_VALUE, + comms_buffer, + VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_general_config( + VL53L1_DEV Dev, + VL53L1_general_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_ReadMulti( + Dev, + VL53L1_GPH_CONFIG__STREAM_COUNT_UPDATE_VALUE, + comms_buffer, + VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_general_config( + VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_timing_config( + VL53L1_timing_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->mm_config__timeout_macrop_a_hi & 0xF; + *(pbuffer + 1) = + pdata->mm_config__timeout_macrop_a_lo; + *(pbuffer + 2) = + pdata->mm_config__timeout_macrop_b_hi & 0xF; + *(pbuffer + 3) = + pdata->mm_config__timeout_macrop_b_lo; + *(pbuffer + 4) = + pdata->range_config__timeout_macrop_a_hi & 0xF; + *(pbuffer + 5) = + pdata->range_config__timeout_macrop_a_lo; + *(pbuffer + 6) = + pdata->range_config__vcsel_period_a & 0x3F; + *(pbuffer + 7) = + pdata->range_config__timeout_macrop_b_hi & 0xF; + *(pbuffer + 8) = + pdata->range_config__timeout_macrop_b_lo; + *(pbuffer + 9) = + pdata->range_config__vcsel_period_b & 0x3F; + VL53L1_i2c_encode_uint16_t( + pdata->range_config__sigma_thresh, + 2, + pbuffer + 10); + VL53L1_i2c_encode_uint16_t( + pdata->range_config__min_count_rate_rtn_limit_mcps, + 2, + pbuffer + 12); + *(pbuffer + 14) = + pdata->range_config__valid_phase_low; + *(pbuffer + 15) = + pdata->range_config__valid_phase_high; + VL53L1_i2c_encode_uint32_t( + pdata->system__intermeasurement_period, + 4, + pbuffer + 18); + *(pbuffer + 22) = + pdata->system__fractional_enable & 0x1; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_timing_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_timing_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->mm_config__timeout_macrop_a_hi = + (*(pbuffer + 0)) & 0xF; + pdata->mm_config__timeout_macrop_a_lo = + (*(pbuffer + 1)); + pdata->mm_config__timeout_macrop_b_hi = + (*(pbuffer + 2)) & 0xF; + pdata->mm_config__timeout_macrop_b_lo = + (*(pbuffer + 3)); + pdata->range_config__timeout_macrop_a_hi = + (*(pbuffer + 4)) & 0xF; + pdata->range_config__timeout_macrop_a_lo = + (*(pbuffer + 5)); + pdata->range_config__vcsel_period_a = + (*(pbuffer + 6)) & 0x3F; + pdata->range_config__timeout_macrop_b_hi = + (*(pbuffer + 7)) & 0xF; + pdata->range_config__timeout_macrop_b_lo = + (*(pbuffer + 8)); + pdata->range_config__vcsel_period_b = + (*(pbuffer + 9)) & 0x3F; + pdata->range_config__sigma_thresh = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 10)); + pdata->range_config__min_count_rate_rtn_limit_mcps = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 12)); + pdata->range_config__valid_phase_low = + (*(pbuffer + 14)); + pdata->range_config__valid_phase_high = + (*(pbuffer + 15)); + pdata->system__intermeasurement_period = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 18)); + pdata->system__fractional_enable = + (*(pbuffer + 22)) & 0x1; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_timing_config( + VL53L1_DEV Dev, + VL53L1_timing_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_timing_config( + pdata, + VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_MM_CONFIG__TIMEOUT_MACROP_A_HI, + comms_buffer, + VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_timing_config( + VL53L1_DEV Dev, + VL53L1_timing_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_ReadMulti( + Dev, + VL53L1_MM_CONFIG__TIMEOUT_MACROP_A_HI, + comms_buffer, + VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_timing_config( + VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_dynamic_config( + VL53L1_dynamic_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->system__grouped_parameter_hold_0 & 0x3; + VL53L1_i2c_encode_uint16_t( + pdata->system__thresh_high, + 2, + pbuffer + 1); + VL53L1_i2c_encode_uint16_t( + pdata->system__thresh_low, + 2, + pbuffer + 3); + *(pbuffer + 5) = + pdata->system__enable_xtalk_per_quadrant & 0x1; + *(pbuffer + 6) = + pdata->system__seed_config & 0x7; + *(pbuffer + 7) = + pdata->sd_config__woi_sd0; + *(pbuffer + 8) = + pdata->sd_config__woi_sd1; + *(pbuffer + 9) = + pdata->sd_config__initial_phase_sd0 & 0x7F; + *(pbuffer + 10) = + pdata->sd_config__initial_phase_sd1 & 0x7F; + *(pbuffer + 11) = + pdata->system__grouped_parameter_hold_1 & 0x3; + *(pbuffer + 12) = + pdata->sd_config__first_order_select & 0x3; + *(pbuffer + 13) = + pdata->sd_config__quantifier & 0xF; + *(pbuffer + 14) = + pdata->roi_config__user_roi_centre_spad; + *(pbuffer + 15) = + pdata->roi_config__user_roi_requested_global_xy_size; + *(pbuffer + 16) = + pdata->system__sequence_config; + *(pbuffer + 17) = + pdata->system__grouped_parameter_hold & 0x3; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_dynamic_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_dynamic_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->system__grouped_parameter_hold_0 = + (*(pbuffer + 0)) & 0x3; + pdata->system__thresh_high = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 1)); + pdata->system__thresh_low = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 3)); + pdata->system__enable_xtalk_per_quadrant = + (*(pbuffer + 5)) & 0x1; + pdata->system__seed_config = + (*(pbuffer + 6)) & 0x7; + pdata->sd_config__woi_sd0 = + (*(pbuffer + 7)); + pdata->sd_config__woi_sd1 = + (*(pbuffer + 8)); + pdata->sd_config__initial_phase_sd0 = + (*(pbuffer + 9)) & 0x7F; + pdata->sd_config__initial_phase_sd1 = + (*(pbuffer + 10)) & 0x7F; + pdata->system__grouped_parameter_hold_1 = + (*(pbuffer + 11)) & 0x3; + pdata->sd_config__first_order_select = + (*(pbuffer + 12)) & 0x3; + pdata->sd_config__quantifier = + (*(pbuffer + 13)) & 0xF; + pdata->roi_config__user_roi_centre_spad = + (*(pbuffer + 14)); + pdata->roi_config__user_roi_requested_global_xy_size = + (*(pbuffer + 15)); + pdata->system__sequence_config = + (*(pbuffer + 16)); + pdata->system__grouped_parameter_hold = + (*(pbuffer + 17)) & 0x3; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_dynamic_config( + VL53L1_DEV Dev, + VL53L1_dynamic_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_dynamic_config( + pdata, + VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_SYSTEM__GROUPED_PARAMETER_HOLD_0, + comms_buffer, + VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_dynamic_config( + VL53L1_DEV Dev, + VL53L1_dynamic_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_ReadMulti( + Dev, + VL53L1_SYSTEM__GROUPED_PARAMETER_HOLD_0, + comms_buffer, + VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_dynamic_config( + VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_system_control( + VL53L1_system_control_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->power_management__go1_power_force & 0x1; + *(pbuffer + 1) = + pdata->system__stream_count_ctrl & 0x1; + *(pbuffer + 2) = + pdata->firmware__enable & 0x1; + *(pbuffer + 3) = + pdata->system__interrupt_clear & 0x3; + *(pbuffer + 4) = + pdata->system__mode_start; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_system_control( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_system_control_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->power_management__go1_power_force = + (*(pbuffer + 0)) & 0x1; + pdata->system__stream_count_ctrl = + (*(pbuffer + 1)) & 0x1; + pdata->firmware__enable = + (*(pbuffer + 2)) & 0x1; + pdata->system__interrupt_clear = + (*(pbuffer + 3)) & 0x3; + pdata->system__mode_start = + (*(pbuffer + 4)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_system_control( + VL53L1_DEV Dev, + VL53L1_system_control_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_system_control( + pdata, + VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_POWER_MANAGEMENT__GO1_POWER_FORCE, + comms_buffer, + VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_system_control( + VL53L1_DEV Dev, + VL53L1_system_control_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_ReadMulti( + Dev, + VL53L1_POWER_MANAGEMENT__GO1_POWER_FORCE, + comms_buffer, + VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_system_control( + VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_system_results( + VL53L1_system_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->result__interrupt_status & 0x3F; + *(pbuffer + 1) = + pdata->result__range_status; + *(pbuffer + 2) = + pdata->result__report_status & 0xF; + *(pbuffer + 3) = + pdata->result__stream_count; + VL53L1_i2c_encode_uint16_t( + pdata->result__dss_actual_effective_spads_sd0, + 2, + pbuffer + 4); + VL53L1_i2c_encode_uint16_t( + pdata->result__peak_signal_count_rate_mcps_sd0, + 2, + pbuffer + 6); + VL53L1_i2c_encode_uint16_t( + pdata->result__ambient_count_rate_mcps_sd0, + 2, + pbuffer + 8); + VL53L1_i2c_encode_uint16_t( + pdata->result__sigma_sd0, + 2, + pbuffer + 10); + VL53L1_i2c_encode_uint16_t( + pdata->result__phase_sd0, + 2, + pbuffer + 12); + VL53L1_i2c_encode_uint16_t( + pdata->result__final_crosstalk_corrected_range_mm_sd0, + 2, + pbuffer + 14); + VL53L1_i2c_encode_uint16_t( + pdata->result__peak_signal_count_rate_crosstalk_corrected_mcps_sd0, + 2, + pbuffer + 16); + VL53L1_i2c_encode_uint16_t( + pdata->result__mm_inner_actual_effective_spads_sd0, + 2, + pbuffer + 18); + VL53L1_i2c_encode_uint16_t( + pdata->result__mm_outer_actual_effective_spads_sd0, + 2, + pbuffer + 20); + VL53L1_i2c_encode_uint16_t( + pdata->result__avg_signal_count_rate_mcps_sd0, + 2, + pbuffer + 22); + VL53L1_i2c_encode_uint16_t( + pdata->result__dss_actual_effective_spads_sd1, + 2, + pbuffer + 24); + VL53L1_i2c_encode_uint16_t( + pdata->result__peak_signal_count_rate_mcps_sd1, + 2, + pbuffer + 26); + VL53L1_i2c_encode_uint16_t( + pdata->result__ambient_count_rate_mcps_sd1, + 2, + pbuffer + 28); + VL53L1_i2c_encode_uint16_t( + pdata->result__sigma_sd1, + 2, + pbuffer + 30); + VL53L1_i2c_encode_uint16_t( + pdata->result__phase_sd1, + 2, + pbuffer + 32); + VL53L1_i2c_encode_uint16_t( + pdata->result__final_crosstalk_corrected_range_mm_sd1, + 2, + pbuffer + 34); + VL53L1_i2c_encode_uint16_t( + pdata->result__spare_0_sd1, + 2, + pbuffer + 36); + VL53L1_i2c_encode_uint16_t( + pdata->result__spare_1_sd1, + 2, + pbuffer + 38); + VL53L1_i2c_encode_uint16_t( + pdata->result__spare_2_sd1, + 2, + pbuffer + 40); + *(pbuffer + 42) = + pdata->result__spare_3_sd1; + *(pbuffer + 43) = + pdata->result__thresh_info; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_system_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_system_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->result__interrupt_status = + (*(pbuffer + 0)) & 0x3F; + pdata->result__range_status = + (*(pbuffer + 1)); + pdata->result__report_status = + (*(pbuffer + 2)) & 0xF; + pdata->result__stream_count = + (*(pbuffer + 3)); + pdata->result__dss_actual_effective_spads_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 4)); + pdata->result__peak_signal_count_rate_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 6)); + pdata->result__ambient_count_rate_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 8)); + pdata->result__sigma_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 10)); + pdata->result__phase_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 12)); + pdata->result__final_crosstalk_corrected_range_mm_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 14)); + pdata->result__peak_signal_count_rate_crosstalk_corrected_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 16)); + pdata->result__mm_inner_actual_effective_spads_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 18)); + pdata->result__mm_outer_actual_effective_spads_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 20)); + pdata->result__avg_signal_count_rate_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 22)); + pdata->result__dss_actual_effective_spads_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 24)); + pdata->result__peak_signal_count_rate_mcps_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 26)); + pdata->result__ambient_count_rate_mcps_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 28)); + pdata->result__sigma_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 30)); + pdata->result__phase_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 32)); + pdata->result__final_crosstalk_corrected_range_mm_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 34)); + pdata->result__spare_0_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 36)); + pdata->result__spare_1_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 38)); + pdata->result__spare_2_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 40)); + pdata->result__spare_3_sd1 = + (*(pbuffer + 42)); + pdata->result__thresh_info = + (*(pbuffer + 43)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_system_results( + VL53L1_DEV Dev, + VL53L1_system_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_system_results( + pdata, + VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_RESULT__INTERRUPT_STATUS, + comms_buffer, + VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_system_results( + VL53L1_DEV Dev, + VL53L1_system_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_ReadMulti( + Dev, + VL53L1_RESULT__INTERRUPT_STATUS, + comms_buffer, + VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_system_results( + VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_core_results( + VL53L1_core_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_CORE_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + VL53L1_i2c_encode_uint32_t( + pdata->result_core__ambient_window_events_sd0, + 4, + pbuffer + 0); + VL53L1_i2c_encode_uint32_t( + pdata->result_core__ranging_total_events_sd0, + 4, + pbuffer + 4); + VL53L1_i2c_encode_int32_t( + pdata->result_core__signal_total_events_sd0, + 4, + pbuffer + 8); + VL53L1_i2c_encode_uint32_t( + pdata->result_core__total_periods_elapsed_sd0, + 4, + pbuffer + 12); + VL53L1_i2c_encode_uint32_t( + pdata->result_core__ambient_window_events_sd1, + 4, + pbuffer + 16); + VL53L1_i2c_encode_uint32_t( + pdata->result_core__ranging_total_events_sd1, + 4, + pbuffer + 20); + VL53L1_i2c_encode_int32_t( + pdata->result_core__signal_total_events_sd1, + 4, + pbuffer + 24); + VL53L1_i2c_encode_uint32_t( + pdata->result_core__total_periods_elapsed_sd1, + 4, + pbuffer + 28); + *(pbuffer + 32) = + pdata->result_core__spare_0; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_core_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_core_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_CORE_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->result_core__ambient_window_events_sd0 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 0)); + pdata->result_core__ranging_total_events_sd0 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 4)); + pdata->result_core__signal_total_events_sd0 = + (VL53L1_i2c_decode_int32_t(4, pbuffer + 8)); + pdata->result_core__total_periods_elapsed_sd0 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 12)); + pdata->result_core__ambient_window_events_sd1 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 16)); + pdata->result_core__ranging_total_events_sd1 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 20)); + pdata->result_core__signal_total_events_sd1 = + (VL53L1_i2c_decode_int32_t(4, pbuffer + 24)); + pdata->result_core__total_periods_elapsed_sd1 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 28)); + pdata->result_core__spare_0 = + (*(pbuffer + 32)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_core_results( + VL53L1_DEV Dev, + VL53L1_core_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_CORE_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_core_results( + pdata, + VL53L1_CORE_RESULTS_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0, + comms_buffer, + VL53L1_CORE_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_core_results( + VL53L1_DEV Dev, + VL53L1_core_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_CORE_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_ReadMulti( + Dev, + VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0, + comms_buffer, + VL53L1_CORE_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_core_results( + VL53L1_CORE_RESULTS_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_debug_results( + VL53L1_debug_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + VL53L1_i2c_encode_uint16_t( + pdata->phasecal_result__reference_phase, + 2, + pbuffer + 0); + *(pbuffer + 2) = + pdata->phasecal_result__vcsel_start & 0x7F; + *(pbuffer + 3) = + pdata->ref_spad_char_result__num_actual_ref_spads & 0x3F; + *(pbuffer + 4) = + pdata->ref_spad_char_result__ref_location & 0x3; + *(pbuffer + 5) = + pdata->vhv_result__coldboot_status & 0x1; + *(pbuffer + 6) = + pdata->vhv_result__search_result & 0x3F; + *(pbuffer + 7) = + pdata->vhv_result__latest_setting & 0x3F; + VL53L1_i2c_encode_uint16_t( + pdata->result__osc_calibrate_val & 0x3FF, + 2, + pbuffer + 8); + *(pbuffer + 10) = + pdata->ana_config__powerdown_go1 & 0x3; + *(pbuffer + 11) = + pdata->ana_config__ref_bg_ctrl & 0x3; + *(pbuffer + 12) = + pdata->ana_config__regdvdd1v2_ctrl & 0xF; + *(pbuffer + 13) = + pdata->ana_config__osc_slow_ctrl & 0x7; + *(pbuffer + 14) = + pdata->test_mode__status & 0x1; + *(pbuffer + 15) = + pdata->firmware__system_status & 0x3; + *(pbuffer + 16) = + pdata->firmware__mode_status; + *(pbuffer + 17) = + pdata->firmware__secondary_mode_status; + VL53L1_i2c_encode_uint16_t( + pdata->firmware__cal_repeat_rate_counter & 0xFFF, + 2, + pbuffer + 18); + VL53L1_i2c_encode_uint16_t( + pdata->gph__system__thresh_high, + 2, + pbuffer + 22); + VL53L1_i2c_encode_uint16_t( + pdata->gph__system__thresh_low, + 2, + pbuffer + 24); + *(pbuffer + 26) = + pdata->gph__system__enable_xtalk_per_quadrant & 0x1; + *(pbuffer + 27) = + pdata->gph__spare_0 & 0x7; + *(pbuffer + 28) = + pdata->gph__sd_config__woi_sd0; + *(pbuffer + 29) = + pdata->gph__sd_config__woi_sd1; + *(pbuffer + 30) = + pdata->gph__sd_config__initial_phase_sd0 & 0x7F; + *(pbuffer + 31) = + pdata->gph__sd_config__initial_phase_sd1 & 0x7F; + *(pbuffer + 32) = + pdata->gph__sd_config__first_order_select & 0x3; + *(pbuffer + 33) = + pdata->gph__sd_config__quantifier & 0xF; + *(pbuffer + 34) = + pdata->gph__roi_config__user_roi_centre_spad; + *(pbuffer + 35) = + pdata->gph__roi_config__user_roi_requested_global_xy_size; + *(pbuffer + 36) = + pdata->gph__system__sequence_config; + *(pbuffer + 37) = + pdata->gph__gph_id & 0x1; + *(pbuffer + 38) = + pdata->system__interrupt_set & 0x3; + *(pbuffer + 39) = + pdata->interrupt_manager__enables & 0x1F; + *(pbuffer + 40) = + pdata->interrupt_manager__clear & 0x1F; + *(pbuffer + 41) = + pdata->interrupt_manager__status & 0x1F; + *(pbuffer + 42) = + pdata->mcu_to_host_bank__wr_access_en & 0x1; + *(pbuffer + 43) = + pdata->power_management__go1_reset_status & 0x1; + *(pbuffer + 44) = + pdata->pad_startup_mode__value_ro & 0x3; + *(pbuffer + 45) = + pdata->pad_startup_mode__value_ctrl & 0x3F; + VL53L1_i2c_encode_uint32_t( + pdata->pll_period_us & 0x3FFFF, + 4, + pbuffer + 46); + VL53L1_i2c_encode_uint32_t( + pdata->interrupt_scheduler__data_out, + 4, + pbuffer + 50); + *(pbuffer + 54) = + pdata->nvm_bist__complete & 0x1; + *(pbuffer + 55) = + pdata->nvm_bist__status & 0x1; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_debug_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_debug_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->phasecal_result__reference_phase = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 0)); + pdata->phasecal_result__vcsel_start = + (*(pbuffer + 2)) & 0x7F; + pdata->ref_spad_char_result__num_actual_ref_spads = + (*(pbuffer + 3)) & 0x3F; + pdata->ref_spad_char_result__ref_location = + (*(pbuffer + 4)) & 0x3; + pdata->vhv_result__coldboot_status = + (*(pbuffer + 5)) & 0x1; + pdata->vhv_result__search_result = + (*(pbuffer + 6)) & 0x3F; + pdata->vhv_result__latest_setting = + (*(pbuffer + 7)) & 0x3F; + pdata->result__osc_calibrate_val = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 8)) & 0x3FF; + pdata->ana_config__powerdown_go1 = + (*(pbuffer + 10)) & 0x3; + pdata->ana_config__ref_bg_ctrl = + (*(pbuffer + 11)) & 0x3; + pdata->ana_config__regdvdd1v2_ctrl = + (*(pbuffer + 12)) & 0xF; + pdata->ana_config__osc_slow_ctrl = + (*(pbuffer + 13)) & 0x7; + pdata->test_mode__status = + (*(pbuffer + 14)) & 0x1; + pdata->firmware__system_status = + (*(pbuffer + 15)) & 0x3; + pdata->firmware__mode_status = + (*(pbuffer + 16)); + pdata->firmware__secondary_mode_status = + (*(pbuffer + 17)); + pdata->firmware__cal_repeat_rate_counter = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 18)) & 0xFFF; + pdata->gph__system__thresh_high = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 22)); + pdata->gph__system__thresh_low = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 24)); + pdata->gph__system__enable_xtalk_per_quadrant = + (*(pbuffer + 26)) & 0x1; + pdata->gph__spare_0 = + (*(pbuffer + 27)) & 0x7; + pdata->gph__sd_config__woi_sd0 = + (*(pbuffer + 28)); + pdata->gph__sd_config__woi_sd1 = + (*(pbuffer + 29)); + pdata->gph__sd_config__initial_phase_sd0 = + (*(pbuffer + 30)) & 0x7F; + pdata->gph__sd_config__initial_phase_sd1 = + (*(pbuffer + 31)) & 0x7F; + pdata->gph__sd_config__first_order_select = + (*(pbuffer + 32)) & 0x3; + pdata->gph__sd_config__quantifier = + (*(pbuffer + 33)) & 0xF; + pdata->gph__roi_config__user_roi_centre_spad = + (*(pbuffer + 34)); + pdata->gph__roi_config__user_roi_requested_global_xy_size = + (*(pbuffer + 35)); + pdata->gph__system__sequence_config = + (*(pbuffer + 36)); + pdata->gph__gph_id = + (*(pbuffer + 37)) & 0x1; + pdata->system__interrupt_set = + (*(pbuffer + 38)) & 0x3; + pdata->interrupt_manager__enables = + (*(pbuffer + 39)) & 0x1F; + pdata->interrupt_manager__clear = + (*(pbuffer + 40)) & 0x1F; + pdata->interrupt_manager__status = + (*(pbuffer + 41)) & 0x1F; + pdata->mcu_to_host_bank__wr_access_en = + (*(pbuffer + 42)) & 0x1; + pdata->power_management__go1_reset_status = + (*(pbuffer + 43)) & 0x1; + pdata->pad_startup_mode__value_ro = + (*(pbuffer + 44)) & 0x3; + pdata->pad_startup_mode__value_ctrl = + (*(pbuffer + 45)) & 0x3F; + pdata->pll_period_us = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 46)) & 0x3FFFF; + pdata->interrupt_scheduler__data_out = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 50)); + pdata->nvm_bist__complete = + (*(pbuffer + 54)) & 0x1; + pdata->nvm_bist__status = + (*(pbuffer + 55)) & 0x1; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_debug_results( + VL53L1_DEV Dev, + VL53L1_debug_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_debug_results( + pdata, + VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_PHASECAL_RESULT__REFERENCE_PHASE, + comms_buffer, + VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_debug_results( + VL53L1_DEV Dev, + VL53L1_debug_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_ReadMulti( + Dev, + VL53L1_PHASECAL_RESULT__REFERENCE_PHASE, + comms_buffer, + VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_debug_results( + VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_nvm_copy_data( + VL53L1_nvm_copy_data_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_NVM_COPY_DATA_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->identification__model_id; + *(pbuffer + 1) = + pdata->identification__module_type; + *(pbuffer + 2) = + pdata->identification__revision_id; + VL53L1_i2c_encode_uint16_t( + pdata->identification__module_id, + 2, + pbuffer + 3); + *(pbuffer + 5) = + pdata->ana_config__fast_osc__trim_max & 0x7F; + *(pbuffer + 6) = + pdata->ana_config__fast_osc__freq_set & 0x7; + *(pbuffer + 7) = + pdata->ana_config__vcsel_trim & 0x7; + *(pbuffer + 8) = + pdata->ana_config__vcsel_selion & 0x3F; + *(pbuffer + 9) = + pdata->ana_config__vcsel_selion_max & 0x3F; + *(pbuffer + 10) = + pdata->protected_laser_safety__lock_bit & 0x1; + *(pbuffer + 11) = + pdata->laser_safety__key & 0x7F; + *(pbuffer + 12) = + pdata->laser_safety__key_ro & 0x1; + *(pbuffer + 13) = + pdata->laser_safety__clip & 0x3F; + *(pbuffer + 14) = + pdata->laser_safety__mult & 0x3F; + *(pbuffer + 15) = + pdata->global_config__spad_enables_rtn_0; + *(pbuffer + 16) = + pdata->global_config__spad_enables_rtn_1; + *(pbuffer + 17) = + pdata->global_config__spad_enables_rtn_2; + *(pbuffer + 18) = + pdata->global_config__spad_enables_rtn_3; + *(pbuffer + 19) = + pdata->global_config__spad_enables_rtn_4; + *(pbuffer + 20) = + pdata->global_config__spad_enables_rtn_5; + *(pbuffer + 21) = + pdata->global_config__spad_enables_rtn_6; + *(pbuffer + 22) = + pdata->global_config__spad_enables_rtn_7; + *(pbuffer + 23) = + pdata->global_config__spad_enables_rtn_8; + *(pbuffer + 24) = + pdata->global_config__spad_enables_rtn_9; + *(pbuffer + 25) = + pdata->global_config__spad_enables_rtn_10; + *(pbuffer + 26) = + pdata->global_config__spad_enables_rtn_11; + *(pbuffer + 27) = + pdata->global_config__spad_enables_rtn_12; + *(pbuffer + 28) = + pdata->global_config__spad_enables_rtn_13; + *(pbuffer + 29) = + pdata->global_config__spad_enables_rtn_14; + *(pbuffer + 30) = + pdata->global_config__spad_enables_rtn_15; + *(pbuffer + 31) = + pdata->global_config__spad_enables_rtn_16; + *(pbuffer + 32) = + pdata->global_config__spad_enables_rtn_17; + *(pbuffer + 33) = + pdata->global_config__spad_enables_rtn_18; + *(pbuffer + 34) = + pdata->global_config__spad_enables_rtn_19; + *(pbuffer + 35) = + pdata->global_config__spad_enables_rtn_20; + *(pbuffer + 36) = + pdata->global_config__spad_enables_rtn_21; + *(pbuffer + 37) = + pdata->global_config__spad_enables_rtn_22; + *(pbuffer + 38) = + pdata->global_config__spad_enables_rtn_23; + *(pbuffer + 39) = + pdata->global_config__spad_enables_rtn_24; + *(pbuffer + 40) = + pdata->global_config__spad_enables_rtn_25; + *(pbuffer + 41) = + pdata->global_config__spad_enables_rtn_26; + *(pbuffer + 42) = + pdata->global_config__spad_enables_rtn_27; + *(pbuffer + 43) = + pdata->global_config__spad_enables_rtn_28; + *(pbuffer + 44) = + pdata->global_config__spad_enables_rtn_29; + *(pbuffer + 45) = + pdata->global_config__spad_enables_rtn_30; + *(pbuffer + 46) = + pdata->global_config__spad_enables_rtn_31; + *(pbuffer + 47) = + pdata->roi_config__mode_roi_centre_spad; + *(pbuffer + 48) = + pdata->roi_config__mode_roi_xy_size; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_nvm_copy_data( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_nvm_copy_data_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_NVM_COPY_DATA_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->identification__model_id = + (*(pbuffer + 0)); + pdata->identification__module_type = + (*(pbuffer + 1)); + pdata->identification__revision_id = + (*(pbuffer + 2)); + pdata->identification__module_id = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 3)); + pdata->ana_config__fast_osc__trim_max = + (*(pbuffer + 5)) & 0x7F; + pdata->ana_config__fast_osc__freq_set = + (*(pbuffer + 6)) & 0x7; + pdata->ana_config__vcsel_trim = + (*(pbuffer + 7)) & 0x7; + pdata->ana_config__vcsel_selion = + (*(pbuffer + 8)) & 0x3F; + pdata->ana_config__vcsel_selion_max = + (*(pbuffer + 9)) & 0x3F; + pdata->protected_laser_safety__lock_bit = + (*(pbuffer + 10)) & 0x1; + pdata->laser_safety__key = + (*(pbuffer + 11)) & 0x7F; + pdata->laser_safety__key_ro = + (*(pbuffer + 12)) & 0x1; + pdata->laser_safety__clip = + (*(pbuffer + 13)) & 0x3F; + pdata->laser_safety__mult = + (*(pbuffer + 14)) & 0x3F; + pdata->global_config__spad_enables_rtn_0 = + (*(pbuffer + 15)); + pdata->global_config__spad_enables_rtn_1 = + (*(pbuffer + 16)); + pdata->global_config__spad_enables_rtn_2 = + (*(pbuffer + 17)); + pdata->global_config__spad_enables_rtn_3 = + (*(pbuffer + 18)); + pdata->global_config__spad_enables_rtn_4 = + (*(pbuffer + 19)); + pdata->global_config__spad_enables_rtn_5 = + (*(pbuffer + 20)); + pdata->global_config__spad_enables_rtn_6 = + (*(pbuffer + 21)); + pdata->global_config__spad_enables_rtn_7 = + (*(pbuffer + 22)); + pdata->global_config__spad_enables_rtn_8 = + (*(pbuffer + 23)); + pdata->global_config__spad_enables_rtn_9 = + (*(pbuffer + 24)); + pdata->global_config__spad_enables_rtn_10 = + (*(pbuffer + 25)); + pdata->global_config__spad_enables_rtn_11 = + (*(pbuffer + 26)); + pdata->global_config__spad_enables_rtn_12 = + (*(pbuffer + 27)); + pdata->global_config__spad_enables_rtn_13 = + (*(pbuffer + 28)); + pdata->global_config__spad_enables_rtn_14 = + (*(pbuffer + 29)); + pdata->global_config__spad_enables_rtn_15 = + (*(pbuffer + 30)); + pdata->global_config__spad_enables_rtn_16 = + (*(pbuffer + 31)); + pdata->global_config__spad_enables_rtn_17 = + (*(pbuffer + 32)); + pdata->global_config__spad_enables_rtn_18 = + (*(pbuffer + 33)); + pdata->global_config__spad_enables_rtn_19 = + (*(pbuffer + 34)); + pdata->global_config__spad_enables_rtn_20 = + (*(pbuffer + 35)); + pdata->global_config__spad_enables_rtn_21 = + (*(pbuffer + 36)); + pdata->global_config__spad_enables_rtn_22 = + (*(pbuffer + 37)); + pdata->global_config__spad_enables_rtn_23 = + (*(pbuffer + 38)); + pdata->global_config__spad_enables_rtn_24 = + (*(pbuffer + 39)); + pdata->global_config__spad_enables_rtn_25 = + (*(pbuffer + 40)); + pdata->global_config__spad_enables_rtn_26 = + (*(pbuffer + 41)); + pdata->global_config__spad_enables_rtn_27 = + (*(pbuffer + 42)); + pdata->global_config__spad_enables_rtn_28 = + (*(pbuffer + 43)); + pdata->global_config__spad_enables_rtn_29 = + (*(pbuffer + 44)); + pdata->global_config__spad_enables_rtn_30 = + (*(pbuffer + 45)); + pdata->global_config__spad_enables_rtn_31 = + (*(pbuffer + 46)); + pdata->roi_config__mode_roi_centre_spad = + (*(pbuffer + 47)); + pdata->roi_config__mode_roi_xy_size = + (*(pbuffer + 48)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_nvm_copy_data( + VL53L1_DEV Dev, + VL53L1_nvm_copy_data_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_NVM_COPY_DATA_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_nvm_copy_data( + pdata, + VL53L1_NVM_COPY_DATA_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_IDENTIFICATION__MODEL_ID, + comms_buffer, + VL53L1_NVM_COPY_DATA_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_nvm_copy_data( + VL53L1_DEV Dev, + VL53L1_nvm_copy_data_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_NVM_COPY_DATA_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_ReadMulti( + Dev, + VL53L1_IDENTIFICATION__MODEL_ID, + comms_buffer, + VL53L1_NVM_COPY_DATA_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_nvm_copy_data( + VL53L1_NVM_COPY_DATA_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_prev_shadow_system_results( + VL53L1_prev_shadow_system_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->prev_shadow_result__interrupt_status & 0x3F; + *(pbuffer + 1) = + pdata->prev_shadow_result__range_status; + *(pbuffer + 2) = + pdata->prev_shadow_result__report_status & 0xF; + *(pbuffer + 3) = + pdata->prev_shadow_result__stream_count; + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__dss_actual_effective_spads_sd0, + 2, + pbuffer + 4); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__peak_signal_count_rate_mcps_sd0, + 2, + pbuffer + 6); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__ambient_count_rate_mcps_sd0, + 2, + pbuffer + 8); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__sigma_sd0, + 2, + pbuffer + 10); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__phase_sd0, + 2, + pbuffer + 12); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__final_crosstalk_corrected_range_mm_sd0, + 2, + pbuffer + 14); + VL53L1_i2c_encode_uint16_t( + pdata->psr__peak_signal_count_rate_crosstalk_corrected_mcps_sd0, + 2, + pbuffer + 16); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__mm_inner_actual_effective_spads_sd0, + 2, + pbuffer + 18); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__mm_outer_actual_effective_spads_sd0, + 2, + pbuffer + 20); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__avg_signal_count_rate_mcps_sd0, + 2, + pbuffer + 22); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__dss_actual_effective_spads_sd1, + 2, + pbuffer + 24); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__peak_signal_count_rate_mcps_sd1, + 2, + pbuffer + 26); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__ambient_count_rate_mcps_sd1, + 2, + pbuffer + 28); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__sigma_sd1, + 2, + pbuffer + 30); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__phase_sd1, + 2, + pbuffer + 32); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__final_crosstalk_corrected_range_mm_sd1, + 2, + pbuffer + 34); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__spare_0_sd1, + 2, + pbuffer + 36); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__spare_1_sd1, + 2, + pbuffer + 38); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__spare_2_sd1, + 2, + pbuffer + 40); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__spare_3_sd1, + 2, + pbuffer + 42); + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_prev_shadow_system_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_prev_shadow_system_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->prev_shadow_result__interrupt_status = + (*(pbuffer + 0)) & 0x3F; + pdata->prev_shadow_result__range_status = + (*(pbuffer + 1)); + pdata->prev_shadow_result__report_status = + (*(pbuffer + 2)) & 0xF; + pdata->prev_shadow_result__stream_count = + (*(pbuffer + 3)); + pdata->prev_shadow_result__dss_actual_effective_spads_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 4)); + pdata->prev_shadow_result__peak_signal_count_rate_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 6)); + pdata->prev_shadow_result__ambient_count_rate_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 8)); + pdata->prev_shadow_result__sigma_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 10)); + pdata->prev_shadow_result__phase_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 12)); + pdata->prev_shadow_result__final_crosstalk_corrected_range_mm_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 14)); + pdata->psr__peak_signal_count_rate_crosstalk_corrected_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 16)); + pdata->prev_shadow_result__mm_inner_actual_effective_spads_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 18)); + pdata->prev_shadow_result__mm_outer_actual_effective_spads_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 20)); + pdata->prev_shadow_result__avg_signal_count_rate_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 22)); + pdata->prev_shadow_result__dss_actual_effective_spads_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 24)); + pdata->prev_shadow_result__peak_signal_count_rate_mcps_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 26)); + pdata->prev_shadow_result__ambient_count_rate_mcps_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 28)); + pdata->prev_shadow_result__sigma_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 30)); + pdata->prev_shadow_result__phase_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 32)); + pdata->prev_shadow_result__final_crosstalk_corrected_range_mm_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 34)); + pdata->prev_shadow_result__spare_0_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 36)); + pdata->prev_shadow_result__spare_1_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 38)); + pdata->prev_shadow_result__spare_2_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 40)); + pdata->prev_shadow_result__spare_3_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 42)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_prev_shadow_system_results( + VL53L1_DEV Dev, + VL53L1_prev_shadow_system_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_prev_shadow_system_results( + pdata, + VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_PREV_SHADOW_RESULT__INTERRUPT_STATUS, + comms_buffer, + VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_prev_shadow_system_results( + VL53L1_DEV Dev, + VL53L1_prev_shadow_system_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_PREV_SHADOW_RESULT__INTERRUPT_STATUS, + comms_buffer, + VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_prev_shadow_system_results( + VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_prev_shadow_core_results( + VL53L1_prev_shadow_core_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + VL53L1_i2c_encode_uint32_t( + pdata->prev_shadow_result_core__ambient_window_events_sd0, + 4, + pbuffer + 0); + VL53L1_i2c_encode_uint32_t( + pdata->prev_shadow_result_core__ranging_total_events_sd0, + 4, + pbuffer + 4); + VL53L1_i2c_encode_int32_t( + pdata->prev_shadow_result_core__signal_total_events_sd0, + 4, + pbuffer + 8); + VL53L1_i2c_encode_uint32_t( + pdata->prev_shadow_result_core__total_periods_elapsed_sd0, + 4, + pbuffer + 12); + VL53L1_i2c_encode_uint32_t( + pdata->prev_shadow_result_core__ambient_window_events_sd1, + 4, + pbuffer + 16); + VL53L1_i2c_encode_uint32_t( + pdata->prev_shadow_result_core__ranging_total_events_sd1, + 4, + pbuffer + 20); + VL53L1_i2c_encode_int32_t( + pdata->prev_shadow_result_core__signal_total_events_sd1, + 4, + pbuffer + 24); + VL53L1_i2c_encode_uint32_t( + pdata->prev_shadow_result_core__total_periods_elapsed_sd1, + 4, + pbuffer + 28); + *(pbuffer + 32) = + pdata->prev_shadow_result_core__spare_0; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_prev_shadow_core_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_prev_shadow_core_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->prev_shadow_result_core__ambient_window_events_sd0 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 0)); + pdata->prev_shadow_result_core__ranging_total_events_sd0 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 4)); + pdata->prev_shadow_result_core__signal_total_events_sd0 = + (VL53L1_i2c_decode_int32_t(4, pbuffer + 8)); + pdata->prev_shadow_result_core__total_periods_elapsed_sd0 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 12)); + pdata->prev_shadow_result_core__ambient_window_events_sd1 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 16)); + pdata->prev_shadow_result_core__ranging_total_events_sd1 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 20)); + pdata->prev_shadow_result_core__signal_total_events_sd1 = + (VL53L1_i2c_decode_int32_t(4, pbuffer + 24)); + pdata->prev_shadow_result_core__total_periods_elapsed_sd1 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 28)); + pdata->prev_shadow_result_core__spare_0 = + (*(pbuffer + 32)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_prev_shadow_core_results( + VL53L1_DEV Dev, + VL53L1_prev_shadow_core_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_prev_shadow_core_results( + pdata, + VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0, + comms_buffer, + VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_prev_shadow_core_results( + VL53L1_DEV Dev, + VL53L1_prev_shadow_core_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0, + comms_buffer, + VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_prev_shadow_core_results( + VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_patch_debug( + VL53L1_patch_debug_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_PATCH_DEBUG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->result__debug_status; + *(pbuffer + 1) = + pdata->result__debug_stage; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_patch_debug( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_patch_debug_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_PATCH_DEBUG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->result__debug_status = + (*(pbuffer + 0)); + pdata->result__debug_stage = + (*(pbuffer + 1)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_patch_debug( + VL53L1_DEV Dev, + VL53L1_patch_debug_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_PATCH_DEBUG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_patch_debug( + pdata, + VL53L1_PATCH_DEBUG_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_RESULT__DEBUG_STATUS, + comms_buffer, + VL53L1_PATCH_DEBUG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_patch_debug( + VL53L1_DEV Dev, + VL53L1_patch_debug_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_PATCH_DEBUG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_RESULT__DEBUG_STATUS, + comms_buffer, + VL53L1_PATCH_DEBUG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_patch_debug( + VL53L1_PATCH_DEBUG_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_gph_general_config( + VL53L1_gph_general_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_GPH_GENERAL_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + VL53L1_i2c_encode_uint16_t( + pdata->gph__system__thresh_rate_high, + 2, + pbuffer + 0); + VL53L1_i2c_encode_uint16_t( + pdata->gph__system__thresh_rate_low, + 2, + pbuffer + 2); + *(pbuffer + 4) = + pdata->gph__system__interrupt_config_gpio; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_gph_general_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_gph_general_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_GPH_GENERAL_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->gph__system__thresh_rate_high = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 0)); + pdata->gph__system__thresh_rate_low = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 2)); + pdata->gph__system__interrupt_config_gpio = + (*(pbuffer + 4)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_gph_general_config( + VL53L1_DEV Dev, + VL53L1_gph_general_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_GPH_GENERAL_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_gph_general_config( + pdata, + VL53L1_GPH_GENERAL_CONFIG_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_GPH__SYSTEM__THRESH_RATE_HIGH, + comms_buffer, + VL53L1_GPH_GENERAL_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_gph_general_config( + VL53L1_DEV Dev, + VL53L1_gph_general_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_GPH_GENERAL_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_GPH__SYSTEM__THRESH_RATE_HIGH, + comms_buffer, + VL53L1_GPH_GENERAL_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_gph_general_config( + VL53L1_GPH_GENERAL_CONFIG_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_gph_static_config( + VL53L1_gph_static_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_GPH_STATIC_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->gph__dss_config__roi_mode_control & 0x7; + VL53L1_i2c_encode_uint16_t( + pdata->gph__dss_config__manual_effective_spads_select, + 2, + pbuffer + 1); + *(pbuffer + 3) = + pdata->gph__dss_config__manual_block_select; + *(pbuffer + 4) = + pdata->gph__dss_config__max_spads_limit; + *(pbuffer + 5) = + pdata->gph__dss_config__min_spads_limit; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_gph_static_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_gph_static_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_GPH_STATIC_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->gph__dss_config__roi_mode_control = + (*(pbuffer + 0)) & 0x7; + pdata->gph__dss_config__manual_effective_spads_select = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 1)); + pdata->gph__dss_config__manual_block_select = + (*(pbuffer + 3)); + pdata->gph__dss_config__max_spads_limit = + (*(pbuffer + 4)); + pdata->gph__dss_config__min_spads_limit = + (*(pbuffer + 5)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_gph_static_config( + VL53L1_DEV Dev, + VL53L1_gph_static_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_GPH_STATIC_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_gph_static_config( + pdata, + VL53L1_GPH_STATIC_CONFIG_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_GPH__DSS_CONFIG__ROI_MODE_CONTROL, + comms_buffer, + VL53L1_GPH_STATIC_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_gph_static_config( + VL53L1_DEV Dev, + VL53L1_gph_static_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_GPH_STATIC_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_GPH__DSS_CONFIG__ROI_MODE_CONTROL, + comms_buffer, + VL53L1_GPH_STATIC_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_gph_static_config( + VL53L1_GPH_STATIC_CONFIG_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_gph_timing_config( + VL53L1_gph_timing_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_GPH_TIMING_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->gph__mm_config__timeout_macrop_a_hi & 0xF; + *(pbuffer + 1) = + pdata->gph__mm_config__timeout_macrop_a_lo; + *(pbuffer + 2) = + pdata->gph__mm_config__timeout_macrop_b_hi & 0xF; + *(pbuffer + 3) = + pdata->gph__mm_config__timeout_macrop_b_lo; + *(pbuffer + 4) = + pdata->gph__range_config__timeout_macrop_a_hi & 0xF; + *(pbuffer + 5) = + pdata->gph__range_config__timeout_macrop_a_lo; + *(pbuffer + 6) = + pdata->gph__range_config__vcsel_period_a & 0x3F; + *(pbuffer + 7) = + pdata->gph__range_config__vcsel_period_b & 0x3F; + *(pbuffer + 8) = + pdata->gph__range_config__timeout_macrop_b_hi & 0xF; + *(pbuffer + 9) = + pdata->gph__range_config__timeout_macrop_b_lo; + VL53L1_i2c_encode_uint16_t( + pdata->gph__range_config__sigma_thresh, + 2, + pbuffer + 10); + VL53L1_i2c_encode_uint16_t( + pdata->gph__range_config__min_count_rate_rtn_limit_mcps, + 2, + pbuffer + 12); + *(pbuffer + 14) = + pdata->gph__range_config__valid_phase_low; + *(pbuffer + 15) = + pdata->gph__range_config__valid_phase_high; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_gph_timing_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_gph_timing_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_GPH_TIMING_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->gph__mm_config__timeout_macrop_a_hi = + (*(pbuffer + 0)) & 0xF; + pdata->gph__mm_config__timeout_macrop_a_lo = + (*(pbuffer + 1)); + pdata->gph__mm_config__timeout_macrop_b_hi = + (*(pbuffer + 2)) & 0xF; + pdata->gph__mm_config__timeout_macrop_b_lo = + (*(pbuffer + 3)); + pdata->gph__range_config__timeout_macrop_a_hi = + (*(pbuffer + 4)) & 0xF; + pdata->gph__range_config__timeout_macrop_a_lo = + (*(pbuffer + 5)); + pdata->gph__range_config__vcsel_period_a = + (*(pbuffer + 6)) & 0x3F; + pdata->gph__range_config__vcsel_period_b = + (*(pbuffer + 7)) & 0x3F; + pdata->gph__range_config__timeout_macrop_b_hi = + (*(pbuffer + 8)) & 0xF; + pdata->gph__range_config__timeout_macrop_b_lo = + (*(pbuffer + 9)); + pdata->gph__range_config__sigma_thresh = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 10)); + pdata->gph__range_config__min_count_rate_rtn_limit_mcps = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 12)); + pdata->gph__range_config__valid_phase_low = + (*(pbuffer + 14)); + pdata->gph__range_config__valid_phase_high = + (*(pbuffer + 15)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_gph_timing_config( + VL53L1_DEV Dev, + VL53L1_gph_timing_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_GPH_TIMING_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_gph_timing_config( + pdata, + VL53L1_GPH_TIMING_CONFIG_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_GPH__MM_CONFIG__TIMEOUT_MACROP_A_HI, + comms_buffer, + VL53L1_GPH_TIMING_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_gph_timing_config( + VL53L1_DEV Dev, + VL53L1_gph_timing_config_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_GPH_TIMING_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_GPH__MM_CONFIG__TIMEOUT_MACROP_A_HI, + comms_buffer, + VL53L1_GPH_TIMING_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_gph_timing_config( + VL53L1_GPH_TIMING_CONFIG_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_fw_internal( + VL53L1_fw_internal_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_FW_INTERNAL_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->firmware__internal_stream_count_div; + *(pbuffer + 1) = + pdata->firmware__internal_stream_counter_val; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_fw_internal( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_fw_internal_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_FW_INTERNAL_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->firmware__internal_stream_count_div = + (*(pbuffer + 0)); + pdata->firmware__internal_stream_counter_val = + (*(pbuffer + 1)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_fw_internal( + VL53L1_DEV Dev, + VL53L1_fw_internal_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_FW_INTERNAL_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_fw_internal( + pdata, + VL53L1_FW_INTERNAL_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_FIRMWARE__INTERNAL_STREAM_COUNT_DIV, + comms_buffer, + VL53L1_FW_INTERNAL_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_fw_internal( + VL53L1_DEV Dev, + VL53L1_fw_internal_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_FW_INTERNAL_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_FIRMWARE__INTERNAL_STREAM_COUNT_DIV, + comms_buffer, + VL53L1_FW_INTERNAL_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_fw_internal( + VL53L1_FW_INTERNAL_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_patch_results( + VL53L1_patch_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_PATCH_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->dss_calc__roi_ctrl & 0x3; + *(pbuffer + 1) = + pdata->dss_calc__spare_1; + *(pbuffer + 2) = + pdata->dss_calc__spare_2; + *(pbuffer + 3) = + pdata->dss_calc__spare_3; + *(pbuffer + 4) = + pdata->dss_calc__spare_4; + *(pbuffer + 5) = + pdata->dss_calc__spare_5; + *(pbuffer + 6) = + pdata->dss_calc__spare_6; + *(pbuffer + 7) = + pdata->dss_calc__spare_7; + *(pbuffer + 8) = + pdata->dss_calc__user_roi_spad_en_0; + *(pbuffer + 9) = + pdata->dss_calc__user_roi_spad_en_1; + *(pbuffer + 10) = + pdata->dss_calc__user_roi_spad_en_2; + *(pbuffer + 11) = + pdata->dss_calc__user_roi_spad_en_3; + *(pbuffer + 12) = + pdata->dss_calc__user_roi_spad_en_4; + *(pbuffer + 13) = + pdata->dss_calc__user_roi_spad_en_5; + *(pbuffer + 14) = + pdata->dss_calc__user_roi_spad_en_6; + *(pbuffer + 15) = + pdata->dss_calc__user_roi_spad_en_7; + *(pbuffer + 16) = + pdata->dss_calc__user_roi_spad_en_8; + *(pbuffer + 17) = + pdata->dss_calc__user_roi_spad_en_9; + *(pbuffer + 18) = + pdata->dss_calc__user_roi_spad_en_10; + *(pbuffer + 19) = + pdata->dss_calc__user_roi_spad_en_11; + *(pbuffer + 20) = + pdata->dss_calc__user_roi_spad_en_12; + *(pbuffer + 21) = + pdata->dss_calc__user_roi_spad_en_13; + *(pbuffer + 22) = + pdata->dss_calc__user_roi_spad_en_14; + *(pbuffer + 23) = + pdata->dss_calc__user_roi_spad_en_15; + *(pbuffer + 24) = + pdata->dss_calc__user_roi_spad_en_16; + *(pbuffer + 25) = + pdata->dss_calc__user_roi_spad_en_17; + *(pbuffer + 26) = + pdata->dss_calc__user_roi_spad_en_18; + *(pbuffer + 27) = + pdata->dss_calc__user_roi_spad_en_19; + *(pbuffer + 28) = + pdata->dss_calc__user_roi_spad_en_20; + *(pbuffer + 29) = + pdata->dss_calc__user_roi_spad_en_21; + *(pbuffer + 30) = + pdata->dss_calc__user_roi_spad_en_22; + *(pbuffer + 31) = + pdata->dss_calc__user_roi_spad_en_23; + *(pbuffer + 32) = + pdata->dss_calc__user_roi_spad_en_24; + *(pbuffer + 33) = + pdata->dss_calc__user_roi_spad_en_25; + *(pbuffer + 34) = + pdata->dss_calc__user_roi_spad_en_26; + *(pbuffer + 35) = + pdata->dss_calc__user_roi_spad_en_27; + *(pbuffer + 36) = + pdata->dss_calc__user_roi_spad_en_28; + *(pbuffer + 37) = + pdata->dss_calc__user_roi_spad_en_29; + *(pbuffer + 38) = + pdata->dss_calc__user_roi_spad_en_30; + *(pbuffer + 39) = + pdata->dss_calc__user_roi_spad_en_31; + *(pbuffer + 40) = + pdata->dss_calc__user_roi_0; + *(pbuffer + 41) = + pdata->dss_calc__user_roi_1; + *(pbuffer + 42) = + pdata->dss_calc__mode_roi_0; + *(pbuffer + 43) = + pdata->dss_calc__mode_roi_1; + *(pbuffer + 44) = + pdata->sigma_estimator_calc__spare_0; + VL53L1_i2c_encode_uint16_t( + pdata->vhv_result__peak_signal_rate_mcps, + 2, + pbuffer + 46); + VL53L1_i2c_encode_uint32_t( + pdata->vhv_result__signal_total_events_ref, + 4, + pbuffer + 48); + VL53L1_i2c_encode_uint16_t( + pdata->phasecal_result__phase_output_ref, + 2, + pbuffer + 52); + VL53L1_i2c_encode_uint16_t( + pdata->dss_result__total_rate_per_spad, + 2, + pbuffer + 54); + *(pbuffer + 56) = + pdata->dss_result__enabled_blocks; + VL53L1_i2c_encode_uint16_t( + pdata->dss_result__num_requested_spads, + 2, + pbuffer + 58); + VL53L1_i2c_encode_uint16_t( + pdata->mm_result__inner_intersection_rate, + 2, + pbuffer + 62); + VL53L1_i2c_encode_uint16_t( + pdata->mm_result__outer_complement_rate, + 2, + pbuffer + 64); + VL53L1_i2c_encode_uint16_t( + pdata->mm_result__total_offset, + 2, + pbuffer + 66); + VL53L1_i2c_encode_uint32_t( + pdata->xtalk_calc__xtalk_for_enabled_spads & 0xFFFFFF, + 4, + pbuffer + 68); + VL53L1_i2c_encode_uint32_t( + pdata->xtalk_result__avg_xtalk_user_roi_kcps & 0xFFFFFF, + 4, + pbuffer + 72); + VL53L1_i2c_encode_uint32_t( + pdata->xtalk_result__avg_xtalk_mm_inner_roi_kcps & 0xFFFFFF, + 4, + pbuffer + 76); + VL53L1_i2c_encode_uint32_t( + pdata->xtalk_result__avg_xtalk_mm_outer_roi_kcps & 0xFFFFFF, + 4, + pbuffer + 80); + VL53L1_i2c_encode_uint32_t( + pdata->range_result__accum_phase, + 4, + pbuffer + 84); + VL53L1_i2c_encode_uint16_t( + pdata->range_result__offset_corrected_range, + 2, + pbuffer + 88); + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_patch_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_patch_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_PATCH_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->dss_calc__roi_ctrl = + (*(pbuffer + 0)) & 0x3; + pdata->dss_calc__spare_1 = + (*(pbuffer + 1)); + pdata->dss_calc__spare_2 = + (*(pbuffer + 2)); + pdata->dss_calc__spare_3 = + (*(pbuffer + 3)); + pdata->dss_calc__spare_4 = + (*(pbuffer + 4)); + pdata->dss_calc__spare_5 = + (*(pbuffer + 5)); + pdata->dss_calc__spare_6 = + (*(pbuffer + 6)); + pdata->dss_calc__spare_7 = + (*(pbuffer + 7)); + pdata->dss_calc__user_roi_spad_en_0 = + (*(pbuffer + 8)); + pdata->dss_calc__user_roi_spad_en_1 = + (*(pbuffer + 9)); + pdata->dss_calc__user_roi_spad_en_2 = + (*(pbuffer + 10)); + pdata->dss_calc__user_roi_spad_en_3 = + (*(pbuffer + 11)); + pdata->dss_calc__user_roi_spad_en_4 = + (*(pbuffer + 12)); + pdata->dss_calc__user_roi_spad_en_5 = + (*(pbuffer + 13)); + pdata->dss_calc__user_roi_spad_en_6 = + (*(pbuffer + 14)); + pdata->dss_calc__user_roi_spad_en_7 = + (*(pbuffer + 15)); + pdata->dss_calc__user_roi_spad_en_8 = + (*(pbuffer + 16)); + pdata->dss_calc__user_roi_spad_en_9 = + (*(pbuffer + 17)); + pdata->dss_calc__user_roi_spad_en_10 = + (*(pbuffer + 18)); + pdata->dss_calc__user_roi_spad_en_11 = + (*(pbuffer + 19)); + pdata->dss_calc__user_roi_spad_en_12 = + (*(pbuffer + 20)); + pdata->dss_calc__user_roi_spad_en_13 = + (*(pbuffer + 21)); + pdata->dss_calc__user_roi_spad_en_14 = + (*(pbuffer + 22)); + pdata->dss_calc__user_roi_spad_en_15 = + (*(pbuffer + 23)); + pdata->dss_calc__user_roi_spad_en_16 = + (*(pbuffer + 24)); + pdata->dss_calc__user_roi_spad_en_17 = + (*(pbuffer + 25)); + pdata->dss_calc__user_roi_spad_en_18 = + (*(pbuffer + 26)); + pdata->dss_calc__user_roi_spad_en_19 = + (*(pbuffer + 27)); + pdata->dss_calc__user_roi_spad_en_20 = + (*(pbuffer + 28)); + pdata->dss_calc__user_roi_spad_en_21 = + (*(pbuffer + 29)); + pdata->dss_calc__user_roi_spad_en_22 = + (*(pbuffer + 30)); + pdata->dss_calc__user_roi_spad_en_23 = + (*(pbuffer + 31)); + pdata->dss_calc__user_roi_spad_en_24 = + (*(pbuffer + 32)); + pdata->dss_calc__user_roi_spad_en_25 = + (*(pbuffer + 33)); + pdata->dss_calc__user_roi_spad_en_26 = + (*(pbuffer + 34)); + pdata->dss_calc__user_roi_spad_en_27 = + (*(pbuffer + 35)); + pdata->dss_calc__user_roi_spad_en_28 = + (*(pbuffer + 36)); + pdata->dss_calc__user_roi_spad_en_29 = + (*(pbuffer + 37)); + pdata->dss_calc__user_roi_spad_en_30 = + (*(pbuffer + 38)); + pdata->dss_calc__user_roi_spad_en_31 = + (*(pbuffer + 39)); + pdata->dss_calc__user_roi_0 = + (*(pbuffer + 40)); + pdata->dss_calc__user_roi_1 = + (*(pbuffer + 41)); + pdata->dss_calc__mode_roi_0 = + (*(pbuffer + 42)); + pdata->dss_calc__mode_roi_1 = + (*(pbuffer + 43)); + pdata->sigma_estimator_calc__spare_0 = + (*(pbuffer + 44)); + pdata->vhv_result__peak_signal_rate_mcps = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 46)); + pdata->vhv_result__signal_total_events_ref = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 48)); + pdata->phasecal_result__phase_output_ref = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 52)); + pdata->dss_result__total_rate_per_spad = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 54)); + pdata->dss_result__enabled_blocks = + (*(pbuffer + 56)); + pdata->dss_result__num_requested_spads = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 58)); + pdata->mm_result__inner_intersection_rate = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 62)); + pdata->mm_result__outer_complement_rate = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 64)); + pdata->mm_result__total_offset = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 66)); + pdata->xtalk_calc__xtalk_for_enabled_spads = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 68)) & 0xFFFFFF; + pdata->xtalk_result__avg_xtalk_user_roi_kcps = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 72)) & 0xFFFFFF; + pdata->xtalk_result__avg_xtalk_mm_inner_roi_kcps = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 76)) & 0xFFFFFF; + pdata->xtalk_result__avg_xtalk_mm_outer_roi_kcps = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 80)) & 0xFFFFFF; + pdata->range_result__accum_phase = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 84)); + pdata->range_result__offset_corrected_range = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 88)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_patch_results( + VL53L1_DEV Dev, + VL53L1_patch_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_PATCH_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_patch_results( + pdata, + VL53L1_PATCH_RESULTS_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_DSS_CALC__ROI_CTRL, + comms_buffer, + VL53L1_PATCH_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_patch_results( + VL53L1_DEV Dev, + VL53L1_patch_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_PATCH_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_DSS_CALC__ROI_CTRL, + comms_buffer, + VL53L1_PATCH_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_patch_results( + VL53L1_PATCH_RESULTS_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_shadow_system_results( + VL53L1_shadow_system_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->shadow_phasecal_result__vcsel_start; + *(pbuffer + 2) = + pdata->shadow_result__interrupt_status & 0x3F; + *(pbuffer + 3) = + pdata->shadow_result__range_status; + *(pbuffer + 4) = + pdata->shadow_result__report_status & 0xF; + *(pbuffer + 5) = + pdata->shadow_result__stream_count; + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__dss_actual_effective_spads_sd0, + 2, + pbuffer + 6); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__peak_signal_count_rate_mcps_sd0, + 2, + pbuffer + 8); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__ambient_count_rate_mcps_sd0, + 2, + pbuffer + 10); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__sigma_sd0, + 2, + pbuffer + 12); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__phase_sd0, + 2, + pbuffer + 14); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__final_crosstalk_corrected_range_mm_sd0, + 2, + pbuffer + 16); + VL53L1_i2c_encode_uint16_t( + pdata->shr__peak_signal_count_rate_crosstalk_corrected_mcps_sd0, + 2, + pbuffer + 18); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__mm_inner_actual_effective_spads_sd0, + 2, + pbuffer + 20); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__mm_outer_actual_effective_spads_sd0, + 2, + pbuffer + 22); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__avg_signal_count_rate_mcps_sd0, + 2, + pbuffer + 24); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__dss_actual_effective_spads_sd1, + 2, + pbuffer + 26); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__peak_signal_count_rate_mcps_sd1, + 2, + pbuffer + 28); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__ambient_count_rate_mcps_sd1, + 2, + pbuffer + 30); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__sigma_sd1, + 2, + pbuffer + 32); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__phase_sd1, + 2, + pbuffer + 34); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__final_crosstalk_corrected_range_mm_sd1, + 2, + pbuffer + 36); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__spare_0_sd1, + 2, + pbuffer + 38); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__spare_1_sd1, + 2, + pbuffer + 40); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__spare_2_sd1, + 2, + pbuffer + 42); + *(pbuffer + 44) = + pdata->shadow_result__spare_3_sd1; + *(pbuffer + 45) = + pdata->shadow_result__thresh_info; + *(pbuffer + 80) = + pdata->shadow_phasecal_result__reference_phase_hi; + *(pbuffer + 81) = + pdata->shadow_phasecal_result__reference_phase_lo; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_shadow_system_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_shadow_system_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->shadow_phasecal_result__vcsel_start = + (*(pbuffer + 0)); + pdata->shadow_result__interrupt_status = + (*(pbuffer + 2)) & 0x3F; + pdata->shadow_result__range_status = + (*(pbuffer + 3)); + pdata->shadow_result__report_status = + (*(pbuffer + 4)) & 0xF; + pdata->shadow_result__stream_count = + (*(pbuffer + 5)); + pdata->shadow_result__dss_actual_effective_spads_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 6)); + pdata->shadow_result__peak_signal_count_rate_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 8)); + pdata->shadow_result__ambient_count_rate_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 10)); + pdata->shadow_result__sigma_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 12)); + pdata->shadow_result__phase_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 14)); + pdata->shadow_result__final_crosstalk_corrected_range_mm_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 16)); + pdata->shr__peak_signal_count_rate_crosstalk_corrected_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 18)); + pdata->shadow_result__mm_inner_actual_effective_spads_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 20)); + pdata->shadow_result__mm_outer_actual_effective_spads_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 22)); + pdata->shadow_result__avg_signal_count_rate_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 24)); + pdata->shadow_result__dss_actual_effective_spads_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 26)); + pdata->shadow_result__peak_signal_count_rate_mcps_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 28)); + pdata->shadow_result__ambient_count_rate_mcps_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 30)); + pdata->shadow_result__sigma_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 32)); + pdata->shadow_result__phase_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 34)); + pdata->shadow_result__final_crosstalk_corrected_range_mm_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 36)); + pdata->shadow_result__spare_0_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 38)); + pdata->shadow_result__spare_1_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 40)); + pdata->shadow_result__spare_2_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 42)); + pdata->shadow_result__spare_3_sd1 = + (*(pbuffer + 44)); + pdata->shadow_result__thresh_info = + (*(pbuffer + 45)); + pdata->shadow_phasecal_result__reference_phase_hi = + (*(pbuffer + 80)); + pdata->shadow_phasecal_result__reference_phase_lo = + (*(pbuffer + 81)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_shadow_system_results( + VL53L1_DEV Dev, + VL53L1_shadow_system_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_shadow_system_results( + pdata, + VL53L1_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_SHADOW_PHASECAL_RESULT__VCSEL_START, + comms_buffer, + VL53L1_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_shadow_system_results( + VL53L1_DEV Dev, + VL53L1_shadow_system_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_SHADOW_PHASECAL_RESULT__VCSEL_START, + comms_buffer, + VL53L1_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_shadow_system_results( + VL53L1_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_shadow_core_results( + VL53L1_shadow_core_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + VL53L1_i2c_encode_uint32_t( + pdata->shadow_result_core__ambient_window_events_sd0, + 4, + pbuffer + 0); + VL53L1_i2c_encode_uint32_t( + pdata->shadow_result_core__ranging_total_events_sd0, + 4, + pbuffer + 4); + VL53L1_i2c_encode_int32_t( + pdata->shadow_result_core__signal_total_events_sd0, + 4, + pbuffer + 8); + VL53L1_i2c_encode_uint32_t( + pdata->shadow_result_core__total_periods_elapsed_sd0, + 4, + pbuffer + 12); + VL53L1_i2c_encode_uint32_t( + pdata->shadow_result_core__ambient_window_events_sd1, + 4, + pbuffer + 16); + VL53L1_i2c_encode_uint32_t( + pdata->shadow_result_core__ranging_total_events_sd1, + 4, + pbuffer + 20); + VL53L1_i2c_encode_int32_t( + pdata->shadow_result_core__signal_total_events_sd1, + 4, + pbuffer + 24); + VL53L1_i2c_encode_uint32_t( + pdata->shadow_result_core__total_periods_elapsed_sd1, + 4, + pbuffer + 28); + *(pbuffer + 32) = + pdata->shadow_result_core__spare_0; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_shadow_core_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_shadow_core_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->shadow_result_core__ambient_window_events_sd0 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 0)); + pdata->shadow_result_core__ranging_total_events_sd0 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 4)); + pdata->shadow_result_core__signal_total_events_sd0 = + (VL53L1_i2c_decode_int32_t(4, pbuffer + 8)); + pdata->shadow_result_core__total_periods_elapsed_sd0 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 12)); + pdata->shadow_result_core__ambient_window_events_sd1 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 16)); + pdata->shadow_result_core__ranging_total_events_sd1 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 20)); + pdata->shadow_result_core__signal_total_events_sd1 = + (VL53L1_i2c_decode_int32_t(4, pbuffer + 24)); + pdata->shadow_result_core__total_periods_elapsed_sd1 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 28)); + pdata->shadow_result_core__spare_0 = + (*(pbuffer + 32)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_shadow_core_results( + VL53L1_DEV Dev, + VL53L1_shadow_core_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_i2c_encode_shadow_core_results( + pdata, + VL53L1_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0, + comms_buffer, + VL53L1_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_shadow_core_results( + VL53L1_DEV Dev, + VL53L1_shadow_core_results_t *pdata) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0, + comms_buffer, + VL53L1_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_shadow_core_results( + VL53L1_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + diff --git a/drivers/input/misc/vl53L1/src/vl53l1_silicon_core.c b/drivers/input/misc/vl53L1/src/vl53l1_silicon_core.c new file mode 100644 index 000000000000..2c3112b841bd --- /dev/null +++ b/drivers/input/misc/vl53L1/src/vl53l1_silicon_core.c @@ -0,0 +1,187 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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 "vl53l1_ll_def.h" +#include "vl53l1_platform.h" +#include "vl53l1_register_map.h" +#include "vl53l1_core.h" +#include "vl53l1_silicon_core.h" + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_CORE, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_CORE, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_CORE,\ + status, fmt, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_is_firmware_ready_silicon( + VL53L1_DEV Dev, + uint8_t *pready) +{ + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t comms_buffer[5]; + + LOG_FUNCTION_START(""); + + + + + status = VL53L1_ReadMulti( + Dev, + VL53L1_INTERRUPT_MANAGER__ENABLES, + comms_buffer, + 5); + + if (status != VL53L1_ERROR_NONE) + goto ENDFUNC; + + pdev->dbg_results.interrupt_manager__enables = + comms_buffer[0]; + pdev->dbg_results.interrupt_manager__clear = + comms_buffer[1]; + pdev->dbg_results.interrupt_manager__status = + comms_buffer[2]; + pdev->dbg_results.mcu_to_host_bank__wr_access_en = + comms_buffer[3]; + pdev->dbg_results.power_management__go1_reset_status = + comms_buffer[4]; + + if ((pdev->sys_ctrl.power_management__go1_power_force & 0x01) + == 0x01) { + + if (((pdev->dbg_results.interrupt_manager__enables & + 0x1F) == 0x1F) && + ((pdev->dbg_results.interrupt_manager__clear + & 0x1F) == 0x1F)) + *pready = 0x01; + else + *pready = 0x00; + + } else { + + + + if ((pdev->dbg_results.power_management__go1_reset_status + & 0x01) == 0x00) + *pready = 0x01; + else + *pready = 0x00; + } + + +ENDFUNC: + LOG_FUNCTION_END(status); + + return status; +} + diff --git a/drivers/input/misc/vl53L1/src/vl53l1_wait.c b/drivers/input/misc/vl53L1/src/vl53l1_wait.c new file mode 100644 index 000000000000..083e53d584a3 --- /dev/null +++ b/drivers/input/misc/vl53L1/src/vl53l1_wait.c @@ -0,0 +1,621 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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 "vl53l1_ll_def.h" +#include "vl53l1_ll_device.h" +#include "vl53l1_platform.h" +#include "vl53l1_core.h" +#include "vl53l1_silicon_core.h" +#include "vl53l1_wait.h" +#include "vl53l1_register_settings.h" + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_CORE, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_CORE, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_CORE, status, \ + fmt, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_wait_for_boot_completion( + VL53L1_DEV Dev) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t fw_ready = 0; + + LOG_FUNCTION_START(""); + + if (pdev->wait_method == VL53L1_WAIT_METHOD_BLOCKING) { + + + + + status = + VL53L1_poll_for_boot_completion( + Dev, + VL53L1_BOOT_COMPLETION_POLLING_TIMEOUT_MS); + + } else { + + + + + fw_ready = 0; + while (fw_ready == 0x00 && status == VL53L1_ERROR_NONE) { + status = VL53L1_is_boot_complete( + Dev, + &fw_ready); + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_WaitMs( + Dev, + VL53L1_POLLING_DELAY_MS); + } + } + } + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_wait_for_firmware_ready( + VL53L1_DEV Dev) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t fw_ready = 0; + uint8_t mode_start = 0; + + LOG_FUNCTION_START(""); + + + + + + mode_start = + pdev->sys_ctrl.system__mode_start & + VL53L1_DEVICEMEASUREMENTMODE_MODE_MASK; + + + + + + + + if ((mode_start == VL53L1_DEVICEMEASUREMENTMODE_TIMED) || + (mode_start == VL53L1_DEVICEMEASUREMENTMODE_SINGLESHOT)) { + + if (pdev->wait_method == VL53L1_WAIT_METHOD_BLOCKING) { + + + + + status = + VL53L1_poll_for_firmware_ready( + Dev, + VL53L1_RANGE_COMPLETION_POLLING_TIMEOUT_MS); + + } else { + + + + + fw_ready = 0; + while (fw_ready == 0x00 && status == + VL53L1_ERROR_NONE) { + status = VL53L1_is_firmware_ready( + Dev, + &fw_ready); + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_WaitMs( + Dev, + VL53L1_POLLING_DELAY_MS); + } + } + } + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_wait_for_range_completion( + VL53L1_DEV Dev) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t data_ready = 0; + + LOG_FUNCTION_START(""); + + if (pdev->wait_method == VL53L1_WAIT_METHOD_BLOCKING) { + + + + + status = + VL53L1_poll_for_range_completion( + Dev, + VL53L1_RANGE_COMPLETION_POLLING_TIMEOUT_MS); + + } else { + + + + + data_ready = 0; + while (data_ready == 0x00 && status == VL53L1_ERROR_NONE) { + status = VL53L1_is_new_data_ready( + Dev, + &data_ready); + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_WaitMs( + Dev, + VL53L1_POLLING_DELAY_MS); + } + } + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_wait_for_test_completion( + VL53L1_DEV Dev) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t data_ready = 0; + + LOG_FUNCTION_START(""); + + if (pdev->wait_method == VL53L1_WAIT_METHOD_BLOCKING) { + + + + + status = + VL53L1_poll_for_range_completion( + Dev, + VL53L1_TEST_COMPLETION_POLLING_TIMEOUT_MS); + + } else { + + + + + data_ready = 0; + while (data_ready == 0x00 && status == VL53L1_ERROR_NONE) { + status = VL53L1_is_new_data_ready( + Dev, + &data_ready); + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_WaitMs( + Dev, + VL53L1_POLLING_DELAY_MS); + } + } + } + + LOG_FUNCTION_END(status); + + return status; +} + + + + +VL53L1_Error VL53L1_is_boot_complete( + VL53L1_DEV Dev, + uint8_t *pready) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t firmware__system_status = 0; + + LOG_FUNCTION_START(""); + + + + + status = + VL53L1_RdByte( + Dev, + VL53L1_FIRMWARE__SYSTEM_STATUS, + &firmware__system_status); + + + + + + + if ((firmware__system_status & 0x01) == 0x01) { + *pready = 0x01; + VL53L1_init_ll_driver_state( + Dev, + VL53L1_DEVICESTATE_SW_STANDBY); + } else { + *pready = 0x00; + VL53L1_init_ll_driver_state( + Dev, + VL53L1_DEVICESTATE_FW_COLDBOOT); + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_is_firmware_ready( + VL53L1_DEV Dev, + uint8_t *pready) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + status = VL53L1_is_firmware_ready_silicon( + Dev, + pready); + + pdev->fw_ready = *pready; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_is_new_data_ready( + VL53L1_DEV Dev, + uint8_t *pready) +{ + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t gpio__mux_active_high_hv = 0; + uint8_t gpio__tio_hv_status = 0; + uint8_t interrupt_ready = 0; + + LOG_FUNCTION_START(""); + + gpio__mux_active_high_hv = + pdev->stat_cfg.gpio_hv_mux__ctrl & + VL53L1_DEVICEINTERRUPTLEVEL_ACTIVE_MASK; + + if (gpio__mux_active_high_hv == VL53L1_DEVICEINTERRUPTLEVEL_ACTIVE_HIGH) + interrupt_ready = 0x01; + else + interrupt_ready = 0x00; + + + + + status = VL53L1_RdByte( + Dev, + VL53L1_GPIO__TIO_HV_STATUS, + &gpio__tio_hv_status); + + + + + if ((gpio__tio_hv_status & 0x01) == interrupt_ready) + *pready = 0x01; + else + *pready = 0x00; + + LOG_FUNCTION_END(status); + + return status; +} + + + + +VL53L1_Error VL53L1_poll_for_boot_completion( + VL53L1_DEV Dev, + uint32_t timeout_ms) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + + + + status = VL53L1_WaitUs( + Dev, + VL53L1_FIRMWARE_BOOT_TIME_US); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WaitValueMaskEx( + Dev, + timeout_ms, + VL53L1_FIRMWARE__SYSTEM_STATUS, + 0x01, + 0x01, + VL53L1_POLLING_DELAY_MS); + + if (status == VL53L1_ERROR_NONE) + VL53L1_init_ll_driver_state(Dev, VL53L1_DEVICESTATE_SW_STANDBY); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_poll_for_firmware_ready( + VL53L1_DEV Dev, + uint32_t timeout_ms) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint32_t start_time_ms = 0; + uint32_t current_time_ms = 0; + int32_t poll_delay_ms = VL53L1_POLLING_DELAY_MS; + uint8_t fw_ready = 0; + + + + + VL53L1_GetTickCount(&start_time_ms); + + pdev->fw_ready_poll_duration_ms = 0; + + + + + while ((status == VL53L1_ERROR_NONE) && + (pdev->fw_ready_poll_duration_ms < timeout_ms) && + (fw_ready == 0)) { + + status = VL53L1_is_firmware_ready( + Dev, + &fw_ready); + + if (status == VL53L1_ERROR_NONE && + fw_ready == 0 && + poll_delay_ms > 0) { + status = VL53L1_WaitMs( + Dev, + poll_delay_ms); + } + + + + + + + VL53L1_GetTickCount(¤t_time_ms); + + pdev->fw_ready_poll_duration_ms = + current_time_ms - start_time_ms; + } + + if (fw_ready == 0 && status == VL53L1_ERROR_NONE) + status = VL53L1_ERROR_TIME_OUT; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_poll_for_range_completion( + VL53L1_DEV Dev, + uint32_t timeout_ms) +{ + + + + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t gpio__mux_active_high_hv = 0; + uint8_t interrupt_ready = 0; + + LOG_FUNCTION_START(""); + + gpio__mux_active_high_hv = + pdev->stat_cfg.gpio_hv_mux__ctrl & + VL53L1_DEVICEINTERRUPTLEVEL_ACTIVE_MASK; + + if (gpio__mux_active_high_hv == VL53L1_DEVICEINTERRUPTLEVEL_ACTIVE_HIGH) + interrupt_ready = 0x01; + else + interrupt_ready = 0x00; + + status = + VL53L1_WaitValueMaskEx( + Dev, + timeout_ms, + VL53L1_GPIO__TIO_HV_STATUS, + interrupt_ready, + 0x01, + VL53L1_POLLING_DELAY_MS); + + LOG_FUNCTION_END(status); + + return status; +} + + diff --git a/drivers/input/misc/vl53L1/src/vl53l1_zone_presets.c b/drivers/input/misc/vl53L1/src/vl53l1_zone_presets.c new file mode 100644 index 000000000000..e35f395a233f --- /dev/null +++ b/drivers/input/misc/vl53L1/src/vl53l1_zone_presets.c @@ -0,0 +1,253 @@ + +/******************************************************************************* + * Copyright (c) 2017, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Core and is dual licensed, + either 'STMicroelectronics + Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + +******************************************************************************** + + 'STMicroelectronics Proprietary license' + +******************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + STMicroelectronics confidential + Reproduction and Communication of this document is strictly prohibited unless + specifically authorized in writing by STMicroelectronics. + + +******************************************************************************** + + Alternatively, VL53L1 Core may be distributed under the terms of + 'BSD 3-clause "New" or "Revised" License', in which case the following + provisions apply instead of the ones + mentioned above : + +******************************************************************************** + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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 "vl53l1_ll_def.h" +#include "vl53l1_ll_device.h" +#include "vl53l1_platform_log.h" +#include "vl53l1_zone_presets.h" + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_CORE, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_CORE, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_CORE,\ + status, fmt, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_init_zone_config_structure( + uint8_t x_off, + uint8_t x_inc, + uint8_t x_zones, + uint8_t y_off, + uint8_t y_inc, + uint8_t y_zones, + uint8_t width, + uint8_t height, + VL53L1_zone_config_t *pdata) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t x = 0; + uint8_t y = 0; + uint16_t i = 0; + + LOG_FUNCTION_START(""); + + pdata->max_zones = VL53L1_MAX_USER_ZONES; + + i = 0; + + for (x = 0 ; x < x_zones ; x++) { + for (y = 0 ; y < y_zones ; y++) { + + if (i < VL53L1_MAX_USER_ZONES) { + + pdata->active_zones = (uint8_t)i; + pdata->user_zones[i].height = height; + pdata->user_zones[i].width = width; + pdata->user_zones[i].x_centre = + x_off + (x * x_inc); + pdata->user_zones[i].y_centre = + y_off + (y * y_inc); + } + + i++; + } + } + + status = VL53L1_init_zone_config_histogram_bins(pdata); + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_zone_preset_xtalk_planar( + VL53L1_general_config_t *pgeneral, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + + pgeneral->global_config__stream_divider = 0x05; + + + + pzone_cfg->active_zones = 0x04; + + pzone_cfg->user_zones[0].height = 15; + pzone_cfg->user_zones[0].width = 7; + pzone_cfg->user_zones[0].x_centre = 4; + pzone_cfg->user_zones[0].y_centre = 8; + + pzone_cfg->user_zones[1].height = 15; + pzone_cfg->user_zones[1].width = 7; + pzone_cfg->user_zones[1].x_centre = 12; + pzone_cfg->user_zones[1].y_centre = 8; + + pzone_cfg->user_zones[2].height = 7; + pzone_cfg->user_zones[2].width = 15; + pzone_cfg->user_zones[2].x_centre = 8; + pzone_cfg->user_zones[2].y_centre = 4; + + pzone_cfg->user_zones[3].height = 7; + pzone_cfg->user_zones[3].width = 15; + pzone_cfg->user_zones[3].x_centre = 8; + pzone_cfg->user_zones[3].y_centre = 12; + + + + + pzone_cfg->user_zones[4].height = 15; + pzone_cfg->user_zones[4].width = 15; + pzone_cfg->user_zones[4].x_centre = 8; + pzone_cfg->user_zones[4].y_centre = 8; + + status = VL53L1_init_zone_config_histogram_bins(pzone_cfg); + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_init_zone_config_histogram_bins( + VL53L1_zone_config_t *pdata) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i; + + LOG_FUNCTION_START(""); + + for (i = 0; i < pdata->max_zones; i++) + pdata->bin_config[i] = VL53L1_ZONECONFIG_BINCONFIG__LOWAMB; + + LOG_FUNCTION_END(status); + + return status; +} + diff --git a/drivers/input/misc/vl53L1/stmvl53l1-i2c.h b/drivers/input/misc/vl53L1/stmvl53l1-i2c.h new file mode 100644 index 000000000000..65f2cc05475a --- /dev/null +++ b/drivers/input/misc/vl53L1/stmvl53l1-i2c.h @@ -0,0 +1,115 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + +/** @file stmvl53l1-i2c.h + * Linux kernel i2c/cci wrapper for ST VL53L1 sensor i2c interface + **/ + +#ifndef STMVL53L1_I2C_H +#define STMVL53L1_I2C_H +#include +#include "stmvl53l1.h" + +struct i2c_data { + struct i2c_client *client; + /** back link to driver for interrupt and clean-up */ + struct stmvl53l1_data *vl53l1_data; + + /* reference counter */ + struct kref ref; + + /*!< if null no regulator use for power ctrl */ + struct regulator *vdd; + + /*!< power enable gpio number + * + * if -1 no gpio if vdd not avl pwr is not controllable + */ + int pwren_gpio; + + /*!< xsdn reset (low active) gpio number to device + * + * -1 mean none assume no "resetable" + */ + int xsdn_gpio; + + /*!< intr gpio number to device + * + * intr is active/low negative edge by default + * + * -1 mean none assume use polling + * @warning if the dev tree and intr gpio is require please adapt code + */ + int intr_gpio; + + /*!< device boot i2c register address + * + * boot_reg is the value of device i2c address after it is bring out + * of reset. + */ + int boot_reg; + + /*!< is set if above irq gpio got acquired */ + struct i2d_data_flags_t { + unsigned pwr_owned:1; /*!< set if pwren gpio is owned*/ + unsigned xsdn_owned:1; /*!< set if sxdn gpio is owned*/ + unsigned intr_owned:1; /*!< set if intr gpio is owned*/ + unsigned intr_started:1; /*!< set if irq is hanlde */ + } io_flag; + + /** the irq vectore assigned to gpio + * -1 if no irq hanled + */ + int irq; + + struct msgtctrl_t { + unsigned unhandled_irq_vec:1; + } msg_flag; + + struct i2c_dma_data { + struct mutex lock; + uint8_t *data; + unsigned int len; + } dma_data; +}; + +int stmvl53l1_init_i2c(void); +void __exit stmvl53l1_exit_i2c(void *arg); +int stmvl53l1_power_up_i2c(void *arg); +int stmvl53l1_power_down_i2c(void *arg); +int stmvl53l1_reset_release_i2c(void *arg); +int stmvl53l1_reset_hold_i2c(void *arg); +void stmvl53l1_clean_up_i2c(void); +int stmvl53l1_start_intr(void *object, int *poll_mode); +void *stmvl53l1_get(void *arg); +void stmvl53l1_put(void *arg); + +#endif /* STMVL53L1_I2C_H */ diff --git a/drivers/input/misc/vl53L1/stmvl53l1.h b/drivers/input/misc/vl53L1/stmvl53l1.h new file mode 100644 index 000000000000..11cd38cb5fb5 --- /dev/null +++ b/drivers/input/misc/vl53L1/stmvl53l1.h @@ -0,0 +1,427 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ +/** + * @file stmvl53l1.h header for vl53l1 sensor driver + */ +#ifndef STMVL53L1_H +#define STMVL53L1_H + +#include +#include +#include +#include +#include +#include + +#include "vl53l1_api.h" + +/** + * IPP adapt + */ +#ifdef DEBUG +# define IPP_PRINT(...) printk(__VA_ARGS__) +#else +# define IPP_PRINT(...) (void)0 +#endif + +#include "stmvl53l1_ipp.h" +#include "stmvl53l1_if.h" + +/** + * Configure the Netlink-id use + */ +#ifndef STMVL531_CFG_NETLINK_USER +#define STMVL531_CFG_NETLINK_USER 8 +#endif + +#define STMVL53L1_MAX_CCI_XFER_SZ 256 +#define STMVL53L1_DRV_NAME "stmvl53l1" + +/** + * configure usage of regulator device from device tree info + * to enable/disable sensor power + * see module-i2c or module-cci file + */ +/* define CFG_STMVL53L1_HAVE_REGULATOR */ + +#define DRIVER_VERSION "13.1.1" + +/** @ingroup vl53l1_config + * @{ + */ +/** + * Configure max number of device the driver can support + */ +#define STMVL53L1_CFG_MAX_DEV 2 +/** @} */ /* ingroup vl53l1_config */ + +/** @ingroup vl53l1_mod_dbg + * @{ + */ +#if 0 +#define DEBUG 1 +#endif +#if 0 +#define FORCE_CONSOLE_DEBUG +#endif + +extern int stmvl53l1_enable_debug; + +#ifndef do_gettimeofday +#define do_gettimeofday(target_time, ...) \ + do { \ + struct timespec64 now_time; \ + ktime_get_real_ts64(&now_time); \ + (target_time)->tv_sec = now_time.tv_sec; \ + (target_time)->tv_usec = now_time.tv_nsec/1000; \ + } while (0) +#endif + +#ifdef DEBUG +# ifdef FORCE_CONSOLE_DEBUG +#define vl53l1_dbgmsg(str, ...) do { \ + if (stmvl53l1_enable_debug) \ + pr_info("%s: " str, __func__, ##__VA_ARGS__); \ +} while (0) +# else +#define vl53l1_dbgmsg(str, ...) do { \ + if (stmvl53l1_enable_debug) \ + pr_debug("%s: " str, __func__, ##__VA_ARGS__); \ +} while (0) +# endif +#else +# define vl53l1_dbgmsg(...) (void)0 +#endif + +/** + * set to 0 1 activate or not debug from work (data interrupt/polling) + */ +#define WORK_DEBUG 0 +#if WORK_DEBUG +# define work_dbg(msg, ...)\ + printk("[D WK53L1] :" msg "\n", ##__VA_ARGS__) +#else +# define work_dbg(...) (void)0 +#endif + +#define vl53l1_info(str, args...) \ + pr_info("%s: " str, __func__, ##args) + +#define vl53l1_errmsg(str, args...) \ + pr_err("%s: " str, __func__, ##args) + +#define vl53l1_wanrmsg(str, args...) \ + pr_warn("%s: " str, __func__, ##args) + +/* turn off poll log if not defined */ +#ifndef STMVL53L1_LOG_POLL_TIMING +# define STMVL53L1_LOG_POLL_TIMING 0 +#endif +/* turn off cci log timing if not defined */ +#ifndef STMVL53L1_LOG_CCI_TIMING +# define STMVL53L1_LOG_CCI_TIMING 0 +#endif + +/**@} */ /* ingroup mod_dbg*/ + +#include +#include +#include + +/** if set to 1 enable ipp execution timing (if debug enabled) + * @ingroup vl53l1_mod_dbg + */ +#define IPP_LOG_TIMING 1 + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 5, 0) +struct timeval { + time64_t tv_sec; /* seconds */ + long tv_usec; /* microseconds */ +}; +#endif + +struct ipp_data_t { + struct ipp_work_t work; + struct ipp_work_t work_out; + int test_n; + /*!< buzy state 0 is idle + *any other value do not try to use (state value defined in source) + */ + int buzy; + int waited_xfer_id; + /*!< when buzy is set that is the id we are expecting + * note that value 0 is reserved and stand for "not waiting" + * as such never id 0 will be in any round trip exchange + * it's ok for daemon to use 0 in "ping" when it identify himself + */ + int status; /** if that is not 0 do not look at out work data */ + wait_queue_head_t waitq; + /*!< ipp caller are put in that queue wait while job is posted to user + * @warning ipp and dev mutex will be released before waiting + * see @ref ipp_abort + */ +#if IPP_LOG_TIMING + struct timeval start_tv, stop_tv; +#endif +}; + +struct stmvl53l1_waiters { + struct list_head list; + pid_t pid; +}; + +/* + * driver data structs + */ +struct stmvl53l1_data { + int id; /*!< multiple device id 0 based*/ + char name[64]; /*!< misc device name */ + int32_t hw_rev; /*!< hardware revision number*/ + + VL53L1_DevData_t stdev; /*!ipp.stop_tv) +# define stmvl531_ipp_tim_start(data)\ + do_gettimeofday(&data->ipp.start_tv) +# define stmvl531_ipp_time(data)\ + stmvl53l1_tv_dif(&data->ipp.start_tv, &data->ipp.stop_tv) +# define stmvl531_ipp_stat(data, fmt, ...)\ + vl53l1_dbgmsg("IPPSTAT " fmt "\n", ##__VA_ARGS__) +#else +# define stmvl531_ipp_tim_stop(data) (void)0 +# define stmvl531_ipp_tim_start(data) (void)0 +# define stmvl531_ipp_stat(...) (void)0 +#endif +}; + + +/** + * timeval diff in us + * + * @param pstart_tv + * @param pstop_tv + */ +long stmvl53l1_tv_dif(struct timeval *pstart_tv, struct timeval *pstop_tv); + + +/** + * The device table list table is update as device get added + * we do not support adding removing device mutiple time ! + * use for clean "unload" purpose + */ +extern struct stmvl53l1_data *stmvl53l1_dev_table[]; + +int stmvl53l1_setup(struct stmvl53l1_data *data); +void stmvl53l1_cleanup(struct stmvl53l1_data *data); +#ifdef CONFIG_PM_SLEEP +void stmvl53l1_pm_suspend_stop(struct stmvl53l1_data *data); +#endif +int stmvl53l1_intr_handler(struct stmvl53l1_data *data); + +int stmvl53l1_sysfs_laser(struct stmvl53l1_data *data, bool create); + +/** + * request ipp to abort or stop + * + * require dev work_mutex held + * + * @warning because the "waiting" work can't be aborted we must wake it up + * it will happen and at some later time not earlier than release of lock + * if after lock release we have a new request to start the race may not be + * handled correctly + * + * @param data the device + * @return 0 if no ipp got canceled, @warning this is maybe not grant we + * can't re-sched "dev work" and re-run the worker back + */ +int stmvl53l1_ipp_stop(struct stmvl53l1_data *data); + +int stmvl53l1_ipp_do(struct stmvl53l1_data *data, struct ipp_work_t *work_in, + struct ipp_work_t *work_out); + +/** + * per device netlink init + * @param data + * @return + */ +int stmvl53l1_ipp_setup(struct stmvl53l1_data *data); +/** + * per device ipp netlink cleaning + * @param data + * @return + */ +void stmvl53l1_ipp_cleanup(struct stmvl53l1_data *data); + +/** + * Module init for netlink + * @return 0 on success + */ +int stmvl53l1_ipp_init(void); + +/** + * Module exit for netlink + * @return 0 on success + */ +void stmvl53l1_ipp_exit(void); + +/** + * enable and start ipp exhange + * @param n_dev number of device to run on + * @param data dev struct + * @return 0 on success + */ +int stmvl53l1_ipp_enable(int n_dev, struct stmvl53l1_data *data); + + +/* + * function pointer structs + */ + + + +#endif /* STMVL53L1_H */ diff --git a/drivers/input/misc/vl53L1/stmvl53l1_i2c.c b/drivers/input/misc/vl53L1/stmvl53l1_i2c.c new file mode 100644 index 000000000000..32b41a7e7bdd --- /dev/null +++ b/drivers/input/misc/vl53L1/stmvl53l1_i2c.c @@ -0,0 +1,391 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + +/** + * @file stmvl53l1_i2c.c vl53l1 linux native i2c interface + * + */ +#include "stmvl53l1.h" +#include "stmvl53l1-i2c.h" +#include + +#if STMVL53L1_LOG_POLL_TIMING +/** + * helper to elapse time in polling + * @param ptv pointer to start time_val + + */ +# define poll_timing_log(ptv) \ + vl53l1_dbgmsg("poll in %d us\n", tv_elapsed_us(ptv)) +#else +# define poll_timing_log(...) (void)0 +#endif + +#if STMVL53L1_LOG_CCI_TIMING +/** + * compute elapsed time in in micro sec based on do_gettimeofday + * @param tv pointer to start time_val + * @return time elapsed in micro seconde + */ + +/** + * compute elapsed time in in micro sec based on do_gettimeofday + * @param tv pointer to start time_val + * @return time elapsed in micro seconde + */ +static uint32_t tv_elapsed_us(struct timeval *tv) +{ + struct timeval now; + + do_gettimeofday(&now); + return (now.tv_sec - tv->tv_sec) * 1000000 + (now.tv_usec - + tv->tv_usec); +} + +# define cci_access_var struct timeval cci_log_start_tv +# define cci_access_start()\ + do_gettimeofday(&cci_log_start_tv) +# define cci_access_over(fmt, ...) \ + vl53l1_dbgmsg("cci_timing %d us" fmt "\n", \ + tv_elapsed_us(&cci_log_start_tv), ##__VA_ARGS__) +#else +# define cci_access_var +# define cci_access_start(...) (void)0 +# define cci_access_over(...) (void)0 +#endif + +#ifdef STMVL53L1_DEBUG_I2C +# define i2c_debug(fmt, ...) vl53l1_dbgmsg(fmt, ##__VA_ARGS__) +#else +# define i2c_debug(fmt, ...) (void)0 +#endif + +VL53L1_Error VL53L1_GetTickCount(uint32_t *ptime_ms) +{ + (void)ptime_ms; + BUG_ON(1); +} + +/** + * compute elapsed time in in milli sec based on do_gettimeofday + * @param tv pointer to start time_val + * @return time elapsed in milli seconde + */ +static uint32_t tv_elapsed_ms(struct timeval *tv) +{ + struct timeval now; + + do_gettimeofday(&now); + return (now.tv_sec - tv->tv_sec) * 1000 + + (now.tv_usec - tv->tv_usec) / 1000; +} + +static int cci_write(struct stmvl53l1_data *dev, int index, + uint8_t *data, uint16_t len) +{ + struct i2c_msg msg; + struct i2c_data *i2c_client_obj = (struct i2c_data *)dev->client_object; + struct i2c_client *client = (struct i2c_client *)i2c_client_obj->client; + int rc; + + cci_access_var; + if (len > STMVL53L1_MAX_CCI_XFER_SZ || len == 0) { + vl53l1_errmsg("invalid len %d\n", len); + return -1; + } + + mutex_lock(&i2c_client_obj->dma_data.lock); + if (i2c_client_obj->dma_data.len < len + 2) { + if (i2c_client_obj->dma_data.data) + kfree(i2c_client_obj->dma_data.data); + i2c_client_obj->dma_data.data = kzalloc(len + 2, GFP_KERNEL); + if (!i2c_client_obj->dma_data.data) { + i2c_client_obj->dma_data.len = 0; + mutex_unlock(&i2c_client_obj->dma_data.lock); + return -1; + } + i2c_client_obj->dma_data.len = len + 2; + } + + cci_access_start(); + /* build up little endian index in buffer */ + i2c_client_obj->dma_data.data[0] = (index >> 8) & 0xFF; + i2c_client_obj->dma_data.data[1] = (index >> 0) & 0xFF; + /* copy write data to buffer after index */ + memcpy(i2c_client_obj->dma_data.data + 2, data, len); + /* set i2c msg */ + msg.addr = client->addr; + msg.flags = client->flags; + msg.buf = i2c_client_obj->dma_data.data; + msg.len = len + 2; + + rc = i2c_transfer(client->adapter, &msg, 1); + if (rc != 1) { + vl53l1_errmsg("wr i2c_transfer err:%d, index 0x%x len %d\n", + rc, index, len); + } + + mutex_unlock(&i2c_client_obj->dma_data.lock); + + cci_access_over("rd status %d long %d ", rc != 1, len); + return rc != 1; +} + +static int cci_read(struct stmvl53l1_data *dev, int index, + uint8_t *data, uint16_t len) +{ + uint8_t buffer[2]; + struct i2c_msg msg[2]; + struct i2c_data *i2c_client_obj = (struct i2c_data *)dev->client_object; + struct i2c_client *client = (struct i2c_client *)i2c_client_obj->client; + int rc; + + cci_access_var; + if (len > STMVL53L1_MAX_CCI_XFER_SZ || len == 0) { + vl53l1_errmsg("invalid len %d\n", len); + return -1; + } + + mutex_lock(&i2c_client_obj->dma_data.lock); + if (i2c_client_obj->dma_data.len < len) { + if (i2c_client_obj->dma_data.data) + kfree(i2c_client_obj->dma_data.data); + i2c_client_obj->dma_data.data = kzalloc(len, GFP_KERNEL); + if (!i2c_client_obj->dma_data.data) { + i2c_client_obj->dma_data.len = 0; + mutex_unlock(&i2c_client_obj->dma_data.lock); + return -1; + } + i2c_client_obj->dma_data.len = len; + } + + cci_access_start(); + + /* build up little endian index in buffer */ + buffer[0] = (index >> 8) & 0xFF; + buffer[1] = (index >> 0) & 0xFF; + + msg[0].addr = client->addr; + msg[0].flags = client->flags; /* Write */ + msg[0].buf = buffer; + msg[0].len = 2; + /* read part of the i2c transaction */ + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD | client->flags; + msg[1].buf = i2c_client_obj->dma_data.data; + msg[1].len = len; + + rc = i2c_transfer(client->adapter, msg, 2); + if (rc != 2) { + pr_err("%s: i2c_transfer :%d, @%x index 0x%x len %d\n", + __func__, rc, client->addr, index, len); + + } + + memcpy(data, i2c_client_obj->dma_data.data, len); + mutex_unlock(&i2c_client_obj->dma_data.lock); + + cci_access_over(" wr len %d status %d", rc != 2, len); + return rc != 2; +} + +VL53L1_Error VL53L1_WrByte(VL53L1_DEV pdev, uint16_t index, uint8_t data) +{ + struct stmvl53l1_data *dev; + + dev = (struct stmvl53l1_data *)container_of(pdev, + struct stmvl53l1_data, + stdev); + + return cci_write(dev, index, &data, 1); + +} + +VL53L1_Error VL53L1_RdByte(VL53L1_DEV pdev, uint16_t index, uint8_t *pdata) +{ + struct stmvl53l1_data *dev; + + dev = (struct stmvl53l1_data *)container_of(pdev, + struct stmvl53l1_data, + stdev); + + return cci_read(dev, index, pdata, 1) ? + VL53L1_ERROR_CONTROL_INTERFACE : VL53L1_ERROR_NONE; +} + +VL53L1_Error VL53L1_WrWord(VL53L1_DEV pdev, uint16_t index, uint16_t data) +{ + VL53L1_Error status; + uint8_t buffer[2]; + + /* Split 16-bit word into MS and L* stmvl53l1 FlightSense sensor */ + + buffer[0] = (uint8_t) (data >> 8); + buffer[1] = (uint8_t) (data & 0x00FF); + i2c_debug(" @%x d= %x => [ %x , %x ] ", index, data, buffer[0], + buffer[1]); + status = VL53L1_WriteMulti(pdev, index, buffer, 2); + + return status; +} + +VL53L1_Error VL53L1_RdWord(VL53L1_DEV pdev, uint16_t index, uint16_t *pdata) +{ + VL53L1_Error status; + uint8_t buffer[2]; + + status = VL53L1_ReadMulti(pdev, index, buffer, 2); + + *pdata = ((uint16_t) buffer[0] << 8) + (uint16_t) buffer[1]; + + return status; +} + +VL53L1_Error VL53L1_WrDWord(VL53L1_DEV pdev, uint16_t index, uint32_t data) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t buffer[4]; + + /* Split 32-bit word into MS ... LS bytes */ + buffer[0] = (uint8_t) (data >> 24); + buffer[1] = (uint8_t) ((data & 0x00FF0000) >> 16); + buffer[2] = (uint8_t) ((data & 0x0000FF00) >> 8); + buffer[3] = (uint8_t) (data & 0x000000FF); + + status = VL53L1_WriteMulti(pdev, index, buffer, 4); + + return status; +} + +VL53L1_Error VL53L1_RdDWord(VL53L1_DEV pdev, uint16_t index, uint32_t *pdata) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t buffer[4]; + + status = VL53L1_ReadMulti(pdev, index, buffer, 4); + + *pdata = ((uint32_t) buffer[0] << 24) + ((uint32_t) buffer[1] << 16) + + ((uint32_t) buffer[2] << 8) + (uint32_t) buffer[3]; + + return status; +} + +VL53L1_Error VL53L1_WriteMulti(VL53L1_DEV pdev, uint16_t index, + uint8_t *pdata, uint32_t count) +{ + struct stmvl53l1_data *dev; + + dev = (struct stmvl53l1_data *)container_of(pdev, + struct stmvl53l1_data, + stdev); + + return cci_write(dev, index, pdata, count) ? + VL53L1_ERROR_CONTROL_INTERFACE : VL53L1_ERROR_NONE; +} + +VL53L1_Error VL53L1_ReadMulti(VL53L1_DEV pdev, uint16_t index, + uint8_t *pdata, uint32_t count) +{ + struct stmvl53l1_data *dev; + + dev = (struct stmvl53l1_data *)container_of(pdev, + struct stmvl53l1_data, + stdev); + + return cci_read(dev, index, pdata, count) ? + VL53L1_ERROR_CONTROL_INTERFACE : VL53L1_ERROR_NONE; +} + +static int is_time_over(struct timeval *tv, uint32_t msec) +{ + return tv_elapsed_ms(tv) >= msec; +} + +VL53L1_Error VL53L1_WaitValueMaskEx(VL53L1_DEV pdev, + uint32_t timeout_ms, + uint16_t index, + uint8_t value, + uint8_t mask, uint32_t poll_delay_ms) +{ + struct timeval start_tv; + struct stmvl53l1_data *dev; + int rc, time_over; + uint8_t rd_val; + + dev = (struct stmvl53l1_data *)container_of(pdev, + struct stmvl53l1_data, + stdev); + + do_gettimeofday(&start_tv); + do { + rc = cci_read(dev, index, &rd_val, 1); + if (rc) + return VL53L1_ERROR_CONTROL_INTERFACE; + if ((rd_val & mask) == value) { + poll_timing_log(&start_tv); + return VL53L1_ERROR_NONE; + } + vl53l1_dbgmsg("poll @%x %x & %d != %x", index, + rd_val, mask, value); + time_over = is_time_over(&start_tv, timeout_ms); + if (!time_over) + msleep(poll_delay_ms); + } while (!time_over); + vl53l1_errmsg("time over %d ms", timeout_ms); + return VL53L1_ERROR_TIME_OUT; +} + +VL53L1_Error VL53L1_WaitUs(VL53L1_DEV pdev, int32_t wait_us) +{ + struct stmvl53l1_data *data; + + data = (struct stmvl53l1_data *)container_of(pdev, + struct stmvl53l1_data, + stdev); + + if (!data->is_delay_allowed) + return VL53L1_ERROR_PLATFORM_SPECIFIC_START; + + /* follow Documentation/timers/timers-howto.txt recommendations */ + if (wait_us < 10) + udelay(wait_us); + else if (wait_us < 20000) + usleep_range(wait_us, wait_us + 1); + else + msleep(wait_us / 1000); + + return VL53L1_ERROR_NONE; +} + +VL53L1_Error VL53L1_WaitMs(VL53L1_DEV pdev, int32_t wait_ms) +{ + return VL53L1_WaitUs(pdev, wait_ms * 1000); +} diff --git a/drivers/input/misc/vl53L1/stmvl53l1_if.h b/drivers/input/misc/vl53L1/stmvl53l1_if.h new file mode 100644 index 000000000000..b1847b1a98a6 --- /dev/null +++ b/drivers/input/misc/vl53L1/stmvl53l1_if.h @@ -0,0 +1,723 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + + +/** + * @file stmvl53l1_if.h vl53l1 kernel driver user interface + * + * @note to use this header in a user space application it requires + * all st bare/ll driver platform wrapper files (for data struct def) + * this files (types etc ..) shall be same or compliant with bar driver version + * used in the kernel module + */ + +#ifndef STMVL53L1_IF_H +#define STMVL53L1_IF_H + + +#include "vl53l1_def.h" +/** + * @addtogroup vl53l1_ioctl + * @{ + */ + +/** + * misc device name for ioctl device + * + * for mutli instance all device 2nd and next instance are basic name +"1"+"2" + * @li stmvl53l1_ranging + * @li stmvl53l1_ranging1 + * @li stmvl53l1_ranging2 + */ +#define VL53L1_MISC_DEV_NAME "laser" +/** + * register data use for simple/single ranging data @ref VL53L1_IOCTL_GETDATAS + * + * @warning this definition is subject to change ! + */ +#define stmvl531_range_data_t VL53L1_RangingMeasurementData_t + +/** + * parameter name in @ref stmvl53l1_parameter when using + * @ref VL53L1_IOCTL_PARAMETER + */ +enum __stmv53l1_parameter_name_e { + VL53L1_XTALKENABLE_PAR = 2, + /*!< VL53L1_XTALKENABLE_PAR enable/disable crosstalk compensation\n + * valid value : + * @li 0 disable crosstalk compensation + * @li 1 enable crosstalk compensation + * + * @warning mode can only be set while not ranging + */ + + VL53L1_DEVICEMODE_PAR = 6, + /*!< DEVICEMODE_PAR set ranging mode  \n + * valid mode value : + * @li 1 @a VL53L1_PRESETMODE_RANGING default ranging + * @li 2 @a VL53L1_PRESETMODE_MULTIZONES_SCANNING multiple zone + * @li 3 @a VL53L1_PRESETMODE_AUTONOMOUS autonomous mode + * @li 4 @a VL53L1_PRESETMODE_LITE_RANGING low mips ranging mode + * @li 8 @a VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS low power autonomous + * mode + * + * @warning mode can only be set while not ranging + */ + + VL53L1_POLLDELAY_PAR = 10, + /*!< set the polling delay (msec)\n + * + * @note apply only when operates in polling mode as no effect + * otherwise + */ + VL53L1_TIMINGBUDGET_PAR = 11, + /*!< VL53L1_TIMINGBUDGET_PAR + * @ref stmvl53l1_parameter.value field is timing budget in micro second + * + * @note Please refer to VL53L1 user manual for minimum timing budget according to the selected mode + * + * Mode / timing budget | Min | Typical + * ------------------------------------- | ----- | ------- + * VL53L1_PRESETMODE_RANGING | 8 ms | 16 ms + * VL53L1_PRESETMODE_MULTIZONES_SCANNING | 8 ms | 16 ms + * VL53L1_PRESETMODE_AUTONOMOUS | 41 ms | 76 ms + * VL53L1_PRESETMODE_LITE_RANGING | 17 ms | 66 ms + * VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS | 20 ms | 20 ms + * + */ + + VL53L1_DISTANCEMODE_PAR = 12, + /*!< VL53L1_DISTANCEMODE_PAR + * valid distance mode value : + * @li 1 @a VL53L1_DISTANCEMODE_SHORT + * @li 2 @a VL53L1_DISTANCEMODE_MEDIUM + * @li 3 @a VL53L1_DISTANCEMODE_LONG + * + * @warning distance mode can only be set while not ranging + */ + + VL53L1_OUTPUTMODE_PAR = 13, + /*!< VL53L1_OUTPUTMODE_PAR + * valid output mode value : + * @li 1 @a VL53L1_OUTPUTMODE_NEAREST + * @li 2 @a VL53L1_OUTPUTMODE_STRONGEST + * + * @warning distance mode can only be set while not ranging + */ + + VL53L1_FORCEDEVICEONEN_PAR = 14, + /*!< VL53L1_FORCEDEVICEONEN_PAR + * This parameter will control if device is put under reset when + * stopped. + * valid force device on value : + * @li 0 feature is disable. Device is put under reset when stopped. + * @li 1 feature is enable. Device is not put under reset when stopped. + */ + + VL53L1_LASTERROR_PAR = 15, + /*!< VL53L1_LASTERROR_PAR + * This is a read only parameter. It will return last device internal + * error. It's valid only after an ioctl/sysfs return an -EIO error. + */ + + VL53L1_OFFSETCORRECTIONMODE_PAR = 16, + /*!< VL53L1_OFFSETCORRECTIONMODE_PAR + * This parameter will define which mode to use for the offset + * correction. + * valid force device on value : + * @li 1 @a VL53L1_OFFSETCORRECTIONMODE_STANDARD + * @li 2 @a VL53L1_OFFSETCORRECTIONMODE_PERZONE + * + * @warning offset correction mode can only be set while not ranging + */ + + VL53L1_OPTICALCENTER_PAR = 17, + /*!< VL53L1_OPTICALCENTER_PAR + * This is a read only parameter. It will return optical center issued + * from the nvm set at FTM stage. value will contain X position of + * center. value2 will contain Y position of center. + * Return values have FixPoint1616_t type. + */ + + VL53L1_DMAXREFLECTANCE_PAR = 18, + /*!< VL53L1_DMAXREFLECTANCE_PAR + * This parameter will define target reflectance @ 940nm used to + * calculate the ambient DMAX. Parameter is of type FixPoint1616_t. + * + * @warning dmax reflectance can only be be set while not ranging + */ + + VL53L1_DMAXMODE_PAR = 19, + /*!< VL53L1_DMAXMODE_PAR + * This parameter will select Dmax mode. + * valid Dmax mode value : + * @li 1 @a VL53L1_DMAXMODE_FMT_CAL_DATA + * @li 2 @a VL53L1_DMAXMODE_CUSTCAL_DATA + * @li 3 @a VL53L1_DMAXMODE_PER_ZONE_CAL_DATA + * + * @warning Dmax mode can only be set while not ranging + */ + + VL53L1_TUNING_PAR = 20, + /*!< VL53L1_DMAXMODE_PAR + * This parameter is a write only parameter. It will allow to provide + * low level layer with a configuration parameter. + * value will be use as a key parameter. + * value2 will be use as value parameter. + * + * @warning those configuration parameter settings are only allowed + * before device is start once. + */ + + VL53L1_SMUDGECORRECTIONMODE_PAR = 21, + /*!< VL53L1_SMUDGECORRECTIONMODE_PAR + * This parameter will control if smudge correction is enable and how + * crosstalk values are updated. + * @li 0 @a VL53L1_SMUDGE_CORRECTION_NONE + * @li 1 @a VL53L1_SMUDGE_CORRECTION_CONTINUOUS + * @li 2 @a VL53L1_SMUDGE_CORRECTION_SINGLE + * @li 3 @a VL53L1_SMUDGE_CORRECTION_DEBUG + */ + + VL53L1_ISXTALKVALUECHANGED_PAR = 22, + /*!< VL53L1_ISXTALKCHANGED_PAR + * This is a read only parameter. It will return if Xtalk value has + * been updated while ranging. This parameter is reset each time device + * start to range. + * @li 0 Xtalk values has not been changed. + * @li 1 Xtalk values has been changed. + */ + + VL53L1_CAMERAMODE_PAR = 23, + /*!< VL53L1_CAMERAMODE_PAR + * valid camera mode value : + * @li 0 device is out of camera mode. + * @li 1 device is in camera mode. + */ + + VL53L1_HWREV_PAR = 24, + /*!< VL53L1_HWREV_PAR + * This is a read only parameter. It will return HW revision number + */ +}; +#define stmv53l1_parameter_name_e enum __stmv53l1_parameter_name_e + +/** + * parameter structure use in @ref VL53L1_IOCTL_PARAMETER + */ +struct stmvl53l1_parameter { + uint32_t is_read; /*!< [in] 1: Get 0: Set*/ + /*!< [in] parameter to set/get + * see @ref stmv53l1_parameter_name_e + */ + stmv53l1_parameter_name_e name; + int32_t value; /*!< [in/out] value to set /get */ + int32_t value2; /*!< [in/out] optional 2nd value */ + int32_t status; /*!< [out] status of the operation */ +}; + + +/** + * roi structure use as @ref VL53L1_IOCTL_ROI arg + * + * see @ref stmvl53l1_roi_full_t for easy to use type variable declaration + * required + */ +struct stmvl53l1_roi_t { + int32_t is_read; + /*!< specify roi transfer direction \n + * @li 0 to get roi + * @li !0 to set roi + */ + /*! roi data and count type use in @ VL53L1_IOCTL_ROI */ + struct roi_cfg_t { + uint8_t NumberOfRoi; + /*!< [in/out] Number of Rois to set/get + * + * on set :\n + * [in] number of roi to set + * @note 0 set can be used to return to device default roi usage + * + * on get :\n + * [in] max number provided\n + * [out] number of ROI copied back to user\n + * @warning 0 will not return any roi datas! + */ + VL53L1_UserRoi_t UserRois[1]; + /*!< roi data array length definition is 1 but + * NumberOfRoi+ FirstRoiToScan in array are required + * and will be effectively copy to/from user space + * + * @sa stmvl53l1_roi_full_t + */ + } roi_cfg /*! [in/out] roi data and count */; +}; + +/** + * full roi struct use in @ref VL53L1_IOCTL_ROI arg + * + * this definition make easier variable declaration with the max roi storage + * capabilities. + * + * @sa stmvl53l1_roi_t for field details + */ +struct stmvl53l1_roi_full_t { + int32_t is_read; + /*!< specify roi transfer direction \n + * @li 0 to get roi + * @li !0 to set roi + */ + VL53L1_RoiConfig_t roi_cfg; + /*!< roi data array of max length but only requested copy to/from user + * space effectively used + * see @a stmvl53l1_roi_t::roi_cfg for details + */ +}; + +/** + * parameter structure use in @ref VL53L1_IOCTL_CALIBRATION_DATA + */ +struct stmvl53l1_ioctl_calibration_data_t { + int32_t is_read; /*!< [in] 1: Get 0: Set*/ + VL53L1_CalibrationData_t data; + /*!< [in/out] data to set /get. Caller + * should consider this structure as an opaque one + */ +}; + +/** + * Opaque structure use to hold content of zone offset calibration result. + */ +#define stmvl531_zone_calibration_data_t \ + struct _stmvl531_zone_calibration_data_t + +struct _stmvl531_zone_calibration_data_t { + uint32_t id; + VL53L1_ZoneCalibrationData_t data; +}; + +/** + * parameter structure use in @ref VL53L1_IOCTL_ZONE_CALIBRATION_DATA + */ +struct stmvl53l1_ioctl_zone_calibration_data_t { + int32_t is_read; /*!< [in] 1: Get 0: Set*/ + stmvl531_zone_calibration_data_t data; + /*!< [in/out] data to set /get. Caller + * should consider this structure as an opaque one + */ +}; + +/** Select reference spad calibration in @ref VL53L1_IOCTL_PERFORM_CALIBRATION. + * + * param1, param2 and param3 not use + */ +#define VL53L1_CALIBRATION_REF_SPAD 0 + +/** Select crosstalk calibration in @ref VL53L1_IOCTL_PERFORM_CALIBRATION. + * + * param1 is calibration method. param2 and param3 not use. + * @li VL53L1_XTALKCALIBRATIONMODE_NO_TARGET + * @li VL53L1_XTALKCALIBRATIONMODE_SINGLE_TARGET + * @li VL53L1_XTALKCALIBRATIONMODE_FULL_ROI + */ +#define VL53L1_CALIBRATION_CROSSTALK 1 + +/** Select offset calibration @ref VL53L1_IOCTL_PERFORM_CALIBRATION. + * param1 is offset calibration mode. Parameter is either: + * - VL53L1_OFFSETCALIBRATIONMODE_STANDARD + * - VL53L1_OFFSETCALIBRATIONMODE_PRERANGE_ONLY + * - VL53L1_OFFSETCALIBRATIONMODE_MULTI_ZONE (deprecated) + * param2 is target distance in mm. + * param3 is target reflectance in percent. Parameter is of type FixPoint1616_t. + * + * Note that VL53L1_OFFSETCALIBRATIONMODE_MULTI_ZONE usage is deprecated. Per + * zone offset calibration should use VL53L1_CALIBRATION_OFFSET_PER_ZONE + * instead. + */ +#define VL53L1_CALIBRATION_OFFSET 2 + +/** Select offset calibration per zone @ref VL53L1_IOCTL_PERFORM_CALIBRATION. + * param1 is offset calibration mode. Parameter is: + * - VL53L1_OFFSETCALIBRATIONMODE_MULTI_ZONE + * param2 is target distance in mm. + * param3 is target reflectance in percent. Parameter is of type FixPoint1616_t. + * + * Note that region of interest should be defined by a prior call to + * VL53L1_IOCTL_ROI before calling VL53L1_IOCTL_PERFORM_CALIBRATION / + * VL53L1_CALIBRATION_OFFSET combinaison. + */ +#define VL53L1_CALIBRATION_OFFSET_PER_ZONE 3 + +/** Select simple offset calibration @ref VL53L1_IOCTL_PERFORM_CALIBRATION. + * param1 is target distance in mm. + * param2 and param3 are not used + */ +#define VL53L1_CALIBRATION_OFFSET_SIMPLE 4 + +/** + * parameter structure use in @ref VL53L1_IOCTL_PERFORM_CALIBRATION + */ +struct stmvl53l1_ioctl_perform_calibration_t { + uint32_t calibration_type; + /*!< [in] select which calibration to do : + * @li @ref VL53L1_CALIBRATION_REF_SPAD + * @li @ref VL53L1_CALIBRATION_CROSSTALK + * @li @ref VL53L1_CALIBRATION_OFFSET + * @li @ref VL53L1_CALIBRATION_OFFSET_SIMPLE + * @li @ref VL53L1_CALIBRATION_OFFSET_PER_ZONE + */ + uint32_t param1; + /*!< [in] first param. Usage depends on calibration_type */ + uint32_t param2; + /*!< [in] second param. Usage depends on calibration_type */ + uint32_t param3; + /*!< [in] third param. Usage depends on calibration_type */ +}; + +/** + * parameter structure use in @ref VL53L1_IOCTL_AUTONOMOUS_CONFIG + */ +struct stmvl53l1_autonomous_config_t { + int32_t is_read; + /*!< [in] 1: Get 0: Set*/ + uint32_t pollingTimeInMs; + /*!< [in/out] interval between two measure in ms */ + VL53L1_DetectionConfig_t config; + /*!< [int/out] autonomous mode configuration structure */ +}; + +/* + * IOCTL definitions + */ + + +/** + * Start ranging (no argument) + * + * @note sysfs and ioctl control are assumed mutual exclusive use + * control from ioctl execute with no consideration of sysfs path. + * + * @return : + * @li 0 on success + * @li -EBUSY if already started + * @li -ENXIO failed to change i2c address change after reset release + * @li -EIO. Read last_error to get device error code + * @li -ENODEV. Device has been removed. + * + * example user land : + @code + int smtvl53l1_start(int fd){error + int rc; + rc= ioctl(fd, VL53L1_IOCTL_START,NULL); + if( rc ){ + if( errno == EBUSY){ + //the device is already started + ioctl_warn("already started"); + return EBUSY; + } + } + if( rc ){ + ioctl_error("%d %s", rc,strerror(errno)); + } + return rc; +} + @endcode +*/ + +#define VL53L1_IOCTL_START _IO('p', 0x01) + +/** + * stop ranging (no argument) + + * @note sysfs and ioctl control are assumed mutual exclusive use + * control from ioctl execute action with no consideration of sysfs path. + * + * @return + * @li 0 on success + * @li -EBUSY if it was already + * @li -EIO. Read last_error to get device error code + * @li -ENODEV. Device has been removed. + * + * c example userland : + @code +int smtvl53l1_stop(int fd){ + int rc; + rc= ioctl(fd, VL53L1_IOCTL_STOP,NULL); + if( rc ){ + if( errno == EBUSY ){ + ioctl_warn("already stopped"); + return errno; + } + ioctl_error("%d %s", rc,strerror(errno)); + } + return rc; +} +@endcode + */ +#define VL53L1_IOCTL_STOP _IO('p', 0x05) + +/** + * get single ranging data @sa for multi zone/objet + * + * retrieve the last range data available form the device + * + * @param in/out data struct ptr of type @ref stmvl531_range_data_t + * it may come in but is out as of now + * + * @return 0 on success else o, error check errno + * @li -EFAULT fault in cpy to f/m user out range data not copied + * @li -ENODEV. Device has been removed. + * + * @warning this ioctl will not wait for a new range sample acquisition + * it will return what available at time it get called . Hence same data maybe + * returned many time when doing fast polling.\n + * End user must inspect the data structure (time stamp etc )to find about it\n + * Despite it's non "waiting" nature this ioctl may still block/sleep shortly + * to ensure race free usage acquiring mutex and/or locks. + */ +#define VL53L1_IOCTL_GETDATAS \ + _IOWR('p', 0x0b, stmvl531_range_data_t) + +/** + * set or get parameter + * + * @param parameter in/out @ref stmvl53l1_parameter + * @sa stmv53l1_parameter_name_e + * + * for get if ioctl fail do not check for out params it is not valid + * for set theirs not copy back only see ioctl status, errno to get error case + * + * @return 0 on success else o, error check errno + * @li -ENODEV. Device has been removed. + * + * @note a set parameter may not be absorbed straight aways ! + */ +#define VL53L1_IOCTL_PARAMETER \ + _IOWR('p', 0x0d, struct stmvl53l1_parameter) + + +/** + * set/get roi + * + * shall only be use while device is stopped (EBUSY error otherwise) + * setting 0 rois stand for "disable user define roi usage, use device default" + * + * @param roi_cfg [in/out] type @ref stmvl53l1_roi_t and + * @ref stmvl53l1_roi_full_t + * @note when getting roi the returned roi cnt is set to available number + * of roi in driver but at most requested number or available one + * will be set in returned structure + * @warning the coordinate system is not usual image x,y (y down)but traditional + * ecludian x,y (y up) + * + * @warning once defined the user roi is kept alive until unset by user . + * User shall update roi when required (mode change etc ..)\n + * To return to default unset roi by setting none, device will return to default + * at next start + * + * @note roi validity is only checked at start ranging , as such invalid roi set + * can make start to fail + * + * @return 0 on success , see errno for error detail + * @li EBUSY when trying to set roi while ranging + * @li ENODEV never device get started and trying to get more rois than set + * @li other errno code could be ll driver specific + */ +#define VL53L1_IOCTL_ROI\ + _IOWR('p', 0x0e, struct stmvl53l1_roi_t) + +/** + * Get multi object/zone ranging data + * + * this call is non blocking and will return what available internally + * in all case (veen error) + * + * @param [out] multi zone range @ref VL53L1_MultiRangingData_t always update + * but -EFAULT error case + * + * @return 0 on success else o, error check errno + * @li -EFAULT fault in cpy to f/m user out range data not copyed + * @li -ENOEXEC active mode is not mutli-zone + * @li -ENODEV device is not ranging or device has been removed. + * as in that case MZ data may not be fully valid + */ +#define VL53L1_IOCTL_MZ_DATA\ + _IOR('p', 0x0f, VL53L1_MultiRangingData_t) + +/** + * get single ranging data @sa for multi zone/objet + * + * this call is equivalent to VL53L1_IOCTL_GETDATAS but will block until + * new data are available since previous call. + * + * @param in/out data struct ptr of type @ref stmvl531_range_data_t + * it may come in but is out as of now + * + * @return 0 on success else o, error check errno + * @li -EFAULT fault in cpy to f/m user out range data not copied + * @li -ENODEV device is not ranging or device has been removed. + * @li -ERESTARTSYS interrupt while sleeping. + */ +#define VL53L1_IOCTL_GETDATAS_BLOCKING\ + _IOWR('p', 0x10, stmvl531_range_data_t) + +/** + * Get multi object/zone ranging data + * + * this call is equivalent to VL53L1_IOCTL_MZ_DATA but will block until + * new data are available since previous call. + * + * @param [out] multi zone range @ref VL53L1_MultiRangingData_t always update + * but -EFAULT error case + * + * @return 0 on success else o, error check errno + * @li -EFAULT fault in cpy to f/m user out range data not copyed + * @li -ENOEXEC active mode is not mutli-zone + * @li -ENODEV device is not ranging or device has been removed. + * @li -ERESTARTSYS interrupt while sleeping. + * as in that case MZ data may not be fully valid + */ +#define VL53L1_IOCTL_MZ_DATA_BLOCKING\ + _IOR('p', 0x11, VL53L1_MultiRangingData_t) + +/** + * Get / set calibration data + * + * this call allow client to either read calibration data after calibration + * has been performed to store them in the host filesystem or push calibration + * data before ranging at each start-up. + * + * @param [in/out] data struct ptr of type + * @ref stmvl53l1_ioctl_calibration_data_t. Caller should consider it as an + * opaque structure. + * + * use this after either VL53L1_CALIBRATION_REF_SPAD, + * VL53L1_CALIBRATION_CROSSTALK or VL53L1_CALIBRATION_OFFSET. + * + * @return 0 on success else o, error check errno + * @li -EFAULT fault in cpy to f/m user out range data not copied + * @li -EBUSY when trying to set calibration data while ranging + * @li -EIO. Read last_error to get device error code + * @li -ENODEV. Device has been removed. + */ +#define VL53L1_IOCTL_CALIBRATION_DATA\ + _IOWR('p', 0x12, struct stmvl53l1_ioctl_calibration_data_t) + +/** + * Get / set zone calibration data + * + * this call allow client to either read zone calibration data after calibration + * has been performed to store them in the host filesystem or push zone + * calibration data before ranging at each start-up. + * + * use this after VL53L1_CALIBRATION_OFFSET_PER_ZONE calibration. + * + * @param [in/out] data struct ptr of type + * @ref stmvl53l1_ioctl_zone_calibration_data_t. Caller should consider it as an + * opaque structure. + * + * @return 0 on success else o, error check errno + * @li -EFAULT fault in cpy to f/m user out range data not copied + * @li -EBUSY when trying to set calibration data while ranging + * @li -EIO. Read last_error to get device error code + * @li -ENODEV. Device has been removed. + */ +#define VL53L1_IOCTL_ZONE_CALIBRATION_DATA\ + _IOWR('p', 0x12, struct stmvl53l1_ioctl_zone_calibration_data_t) + +/** + * perform calibration squence according to calibration_type + * + * this call is attended to be used during factory calibration. You select + * calibration to issue using calibration_type. + * + * @param [in] data struct ptr of type + * @ref stmvl53l1_ioctl_perform_calibration_t. + * + * @return 0 on success else o, error check errno + * @li -EFAULT fault in cpy to f/m user out range data not copied + * @li -EBUSY when trying to perform calibration data while ranging + * @li -EIO. Read last_error to get device error code + * @li -ENODEV. Device has been removed. + */ +#define VL53L1_IOCTL_PERFORM_CALIBRATION\ + _IOW('p', 0x13, struct stmvl53l1_ioctl_perform_calibration_t) + +/** + * set/get configure autonomous mode parameters + * + * Allow to get or set autonomous configuration. Change it only when device + * is stopped otherwise you will receive an EBUSY error. + * + * @param stmvl53l1_autonomous_config_t [in/out] + * + * @note autonomous config validity is only checked at start ranging , as such + * invalid autonomous config set can make start to fail. + * + * @return 0 on success , see errno for error detail + * @li -EFAULT failed to copy from/to configuration. + * @li -EBUSY when trying to change configuration while ranging. + * @li -ENODEV. Device has been removed. + */ +#define VL53L1_IOCTL_AUTONOMOUS_CONFIG\ + _IOWR('p', 0x14, struct stmvl53l1_autonomous_config_t) + +/** + * suspend ranging (no argument) + + * @note sysfs and ioctl control are assumed mutual exclusive use + * control from ioctl execute action with no consideration of sysfs path. + * + * @return + * @li 0 on success + * @li -EBUSY if it was already + * @li other specific error code shall be >0 + * + * c example userland : + @code +int smtvl53l1_stop(int fd){ + int rc; + rc= ioctl(fd, VL53L1_IOCTL_SUSPEND,NULL); + if( rc ){ + if( errno == EBUSY ){ + ioctl_warn("already suspended"); + return errno; + } + ioctl_error("%d %s", rc,strerror(errno)); + } + return rc; +} +@endcode + */ +#define VL53L1_IOCTL_SUSPEND _IO('p', 0x15) + +/** @} */ /* ioctl group */ +#endif /* STMVL53L1_IF_H */ \ No newline at end of file diff --git a/drivers/input/misc/vl53L1/stmvl53l1_internal_if.h b/drivers/input/misc/vl53L1/stmvl53l1_internal_if.h new file mode 100644 index 000000000000..5a4c004c1ffd --- /dev/null +++ b/drivers/input/misc/vl53L1/stmvl53l1_internal_if.h @@ -0,0 +1,120 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + +#ifndef STMVL53L1_INTERNAL_IF_H +#define STMVL53L1_INTERNAL_IF_H + +#include "vl53l1_def.h" + +/* interface definition move in this file is not supposed to be use by a normal + * client. It's only here for internal testing purpose. + */ + +/* structure and ioctl that allow raw access to vl53l1 register */ +struct stmvl53l1_register { + uint32_t is_read; /*!< type of the access 1: read 0: write*/ + uint32_t index; /*!< register index */ + uint32_t cnt; /*!< register size shall be 1 to n */ + int32_t status; /*!< operation status 0 ok else error */ + + union reg_data_t { + uint8_t b; /*!< single data byte*/ + uint16_t w; /*!< single data word (16 bits)*/ + uint32_t dw; /*!< single data dword (32 bits)*/ + /*!< any size byte array + * @note only effectively used array size is needed and will be + * set/used another possible register definition is + * @ref stmvl53l1_register_flexi + */ + uint8_t bytes[256]; + /*!< data only *@warning device is big endian and + * no endianess adaptation is performed by + * @ref VL53L1_IOCTL_REGISTER + */ + } data; +}; + +struct stmvl53l1_register_flexi { + uint32_t is_read; /*!< [in] type of the access 1: read 0: write*/ + uint32_t index; /*!< [in] register index */ + uint32_t cnt; /*!< [în] register size shall be 1 to n */ + int32_t status; /*!< [out] operation status 0 ok else error */ + uint8_t data[]; /*!< [in/out] flexible array size data */ + /*!< data only *@warning device is big endian and + * no endianess adaptation is performed by @ref VL53L1_IOCTL_REGISTER + */ +}; + +#define VL53L1_IOCTL_REGISTER _IOWR('p', 0x0c, struct stmvl53l1_register) + +struct stmvl53l1_data_with_additional { + VL53L1_MultiRangingData_t data; + VL53L1_AdditionalData_t additional_data; +}; + +/** + * Get multi object/zone ranging data with additional data for debug + * + * this call is non blocking and will return what available internally + * in all case (veen error) + * + * @param [out] multi zone range @ref VL53L1_MultiRangingData_t always update + * but -EFAULT error case + * + * @return 0 on success else o, error check errno + * @li -EFAULT fault in cpy to f/m user out range data not copyed + * @li -ENOEXEC active mode is not mutli-zone + * @li -ENODEV device is not ranging or device has been removed. + * as in that case MZ data may not be fully valid + */ +#define VL53L1_IOCTL_MZ_DATA_ADDITIONAL\ + _IOR('p', 0x15, struct stmvl53l1_data_with_additional) + +/** + * Get multi object/zone ranging data + * + * this call is equivalent to VL53L1_IOCTL_MZ_DATA_ADDITIONAL but will block + * until new data are available since previous call. + * + * @param [out] multi zone range @ref VL53L1_MultiRangingData_t always update + * but -EFAULT error case + * + * @return 0 on success else o, error check errno + * @li -EFAULT fault in cpy to f/m user out range data not copyed + * @li -ENOEXEC active mode is not mutli-zone + * @li -ENODEV device is not ranging or device has been removed. + * @li -ERESTARTSYS interrupt while sleeping. + * as in that case MZ data may not be fully valid + */ +#define VL53L1_IOCTL_MZ_DATA_ADDITIONAL_BLOCKING\ + _IOR('p', 0x16, struct stmvl53l1_data_with_additional) + +#endif diff --git a/drivers/input/misc/vl53L1/stmvl53l1_ipp.h b/drivers/input/misc/vl53L1/stmvl53l1_ipp.h new file mode 100644 index 000000000000..31e75a196deb --- /dev/null +++ b/drivers/input/misc/vl53L1/stmvl53l1_ipp.h @@ -0,0 +1,386 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + +/** + * @file stmvl53l1_ipp.h + * + * helper to serialize de-serialize data in ipp exchange between user/kernel + * + * @date Sep 2, 2016 + * @author imaging + */ + +#ifndef _STMVL53L1_IPP_H_ +#define _STMVL53L1_IPP_H_ + +#include "vl53l1_types.h" + + +/** @ingroup ipp_dev + * @{ + */ + +/** + * @defgroup ipp_serialize ST IPP serialization helper + * + * to use the dump help you lust define first a few extra "specific" + * + * @li IPP_PRINT(fmt,..) with typical printf/printk interface + */ + /** @{ */ +/** + * generic data type to serialized scalar and offset for pointer + * + * serialized data can be seen as a first array of n ipp_art_t + * where each input are arg value for scaler type + * and an offset with respect to the ipp_arg base array where the data + * + * @note if all ipp argument can fit on 32 bit then ipp_arg_t is best set as + * a 32 bit base type '(i e uint32_t) to save space + * on 64 bit cpu it can remain 64 bit type to get better alignment + */ +typedef uint64_t ipp_arg_t; + +/** + * set to the cpu structure alignment and packing constrain + * + * all serialized argument passed by pointer will be stored with + * this memory align constrain + * + * + * @note if target cpu is ok to access unaligned data or less constrain ie 64 + * bit data align 32 are ok then it can be set to 4 to save space in data + * packing and copy\n + * We may be over constraining in many cases as a struct is only required to + * be aligned on i'st biggest item size + * + * @warning it must be a 2 power of 2 (& operation instead of mudulo used in + * align calculation) + * @warning using 8 byte constrain require that the @a ipp_arg_t is already + * aligned on that constrain + */ +#define IPP_ALIGN_REQ 8 + +/** + * set x to it's nearest aligned offset (not that 0 is fine as a valid offset + * than will not be up aligned to next align chunk ) + */ +#define IPP_ALIGN_OFFSET(x) (((x)+(IPP_ALIGN_REQ-1)) & (~(IPP_ALIGN_REQ-1))) + +/** + * declare variable needed for ipp serialization + */ +#define IPP_SERIALIZE_VAR int ipp_offset +/** + * Put in function start to init the serialization coding + * + * @param ipp_args buffer for data serialization + * @param n_args is the total number of argument scalar and ptr to serialized + * dot not count out only arg that will be returned + * + * it define local var "ipp_offset" used to accumulate all input args that must + * be serialize. It reserve header room in the buffer to place for scalar args + * and offset for args ptr copy + * + * args pass by pointer must be serialized by @ref IPP_SET_ARG_PTR in order they + * are declare\n + * scalar args can be serialized in any order with @ref IPP_SET_ARG + * + * scalar args that can't be cast to basic @ref ipp_arg_t shall be serialized + * Manually or using a macro similar to ptr that can be pass by copy + * + *@note @ref IPP_SERIALIZE_VAR must be use first + * @code + * int ipp_to _ser(int arg0, struct st_t *pdata1){ + * char *local var; + * ipp_arg_t args[256]; + * IPP_SERIALIZE_START(2); + * IPP_SET_ARG(args, 0, arg0); + * IPP_SET_ARG_PTR(args, 1, pdata); + * do_one_ipp(args); + * } + * @endcode + * + */ +#define IPP_SERIALIZE_START(ipp_args, n_args)\ + (ipp_offset = IPP_ALIGN_OFFSET((char *)(&ipp_args[n_args]) - \ + (char *)ipp_args)) + +/** + * @brief Serialize scalar argument + * + * serialized arg number n into ipp_args\n + * can be used in any order + * + * @param ipp_args the args buffer + * @param n 0 base arg number + * @param v the scalar argument variable + * @warning usable only for scalar type that can be copy on a ipp_arg_t + */ +#define IPP_SET_ARG(ipp_args, n, v) memcpy(&ipp_args[n], &v, sizeof(v)) + +/** + * @brief Serialize an arg passed by pointer + * + * @param ipp_args the arg ptr array + * @param n the arg number to serialize + * @param pdata argument it must be a type like struct x_t * int [n] etc ... + * that size is given by size of and and be copied by a single memcpy + */ +#define IPP_SET_ARG_PTR(ipp_args, n, pdata)\ + do {\ + ipp_args[n] = ipp_offset;\ + memcpy(((char *)ipp_args) + ipp_offset, pdata, sizeof(*pdata));\ + ipp_offset = IPP_ALIGN_OFFSET(ipp_offset + sizeof(*pdata));\ + } while (0) + +/** + * serialize out get ptr for pdata + * + * @note it does not cpy data just set ptr and update the offset + * @warning to be use in order + * + * @param ipp_args the arg ptr array + * @param n the arg number to serialize (unused) + * @param pdata init to ptr in ipp_args type is used for offset computation + * @warning to use sequential in order agr are serialized + */ +#define IPP_OUT_ARG_PTR(ipp_args, n, pdata)\ + do {\ + pdata = (void *)(((char *)ipp_args) + ipp_offset);\ + ipp_offset = IPP_ALIGN_OFFSET(ipp_offset + \ + (int)sizeof(*pdata));\ + } while (0) + + +/** + * @brief ipp get payload + * + * @return paylaod at time used \n + * when all done it's overall out payload + * + * require @ref IPP_SERIALIZE_VAR and @ref IPP_SERIALIZE_START used first\n + * best use after all @ref IPP_OUT_ARG_PTR or @ref IPP_SET_ARG_PTR done to get + * full payload + **/ +#define IPP_SERIALIZE_PAYLAOD() (ipp_offset + IPP_WORK_HDR_SIZE) + +/** + * de-serialize and argument that was passed by value + * @param ipp_args the args array + * @param n the 0 base argument number + * @param v argument it must be exact type + * + * + * @code + * f_deserialize(ipp_arg_t args[]) + * { + * // f_ser is like (uint16_t arg0, struct s_arg_t * arg1, int arg2) + * uint16_t arg0; + * void * parg1; + * int arg2; + * + * IPP_GET_ARG( args, 0, arg0) + * IPP_GET_ARG( args, 2, arg2) + * } + * @endcode + */ +#define IPP_GET_ARG(ipp_args, n, v) memcpy(&v, &ipp_args[n], sizeof(v)) + + +/** + * de-serialize an argument passed by pointer + * + * @param ipp_args the serialized argument array + * @param n 0 base argument number + * @param p ptr to arg to be set + * + * @note unlike serializing de-serializing pointer args data can be done in any + * order + * + * @code + * struct some_struct_t *parg2; + * IPP_GET_ARG_PTR(args,2,parg2); + * @endcode + */ +#define IPP_GET_ARG_PTR(ipp_args, n, p) (p = (void *)((char *)ipp_args + \ + ipp_args[n])) + + + + +/** + * debug macro to pint out all serialized args + * + * Implementation shall define IPP_PRINT_FUNC function to use + */ +#define IPP_PRINT_ARGS(ipp_args, n) \ + do {\ + int i;\ + for (i = 0; i < n; i++)\ + IPP_PRINT("arg#%d/%d is %8d 0x%08x\n", i, n,\ + ipp_args[i], ipp_args[i]);\ + IPP_PRINT("used data size %d\n", ipp_offset);\ + } while (0) + + +/** + * processing code type of proccesing + * + * used in @a ipp_work_t::process_no + */ +enum stmvl53l1_ipp_proccesing_e { + stmvl53l1_ipp_ping = 0, + /*!< stmvl53l1_ipp_ping + * @li can be sent by driver to check client is alive + * @li daemon sent it to identify and register himself to the driver + */ + stmvl53l1_ipp_cal_hist = 1, + /*!< stmvl53l1_ipp_cal_hist process cal hist*/ + + stmvl53l1_ipp_xtalk_calibration = 2, + /*!< stmvl53l1_ipp_xtalk_calibration process crosstalk calibration data + */ + + stmvl53l1_ipp_hist_ambient_dmax = 3, + /*!< stmvl53l1_ipp_hist_ambient_dmax process ambient dmac calculation + * from histogram + */ + + stmvl53l1_ipp_generate_dual_reflectance_xtalk_samples = 4, + /*!< stmvl53l1_ipp_generate_dual_reflectance_xtalk_samples process + * Xtalk data from dual reflectance histogram data + */ + + /** keep last*/ + stmvl53l1_ipp_max /*!< stmvl53l1_ipp_max */ +}; + +/** + * status use on @a ipp_work_t::status + */ +enum stmvl53l1_ipp_status_e { + stmvl53l1_ipp_status_ok = 0, /*!< ok work done */ + stmvl53l1_ipp_status_inv_id, /*!< dev id not supported or invalid */ + stmvl53l1_ipp_status_inv_proc, + /*!< process_no asked not supported or not implemented */ + stmvl53l1_ipp_status_inv_payload, + /*!< data payload for asked processing incorrect*/ + + stmvl53l1_ipp_status_proc_code = 0x100, + /*!< the lowest 8 bit is the error code form the processing */ +}; + +/** + * Ipp work (job) struct + * + * containing header with sequenc control information plus serialized data + */ +struct ipp_work_t { + int8_t dev_id; /*!< [in]/[out] device id */ + /*!< Identify the work do be run see @a stmvl53l1_ipp_proccesing_e */ + uint8_t process_no; + /*!< [out] status from daemon */ + int16_t status; + /*!< [in/out] unique xfer id */ + uint32_t xfer_id; + /*!< [in/out] effective data length including header*/ + uint32_t payload; + +/** max IPP data payload (not including header) + * + * we substract size of of item above + * must be lesss than one netlink packet + */ +#define MAX_IPP_DATA ((4096-4*3)/8) + ipp_arg_t data[MAX_IPP_DATA]; /*!< [in][out] */ +}; + +/** + * size of header only message ( no payload) + * require \#include in user land + */ +#define IPP_WORK_HDR_SIZE (offsetof(struct ipp_work_t, data[0])) +/** + * max payload per ipp transfer + */ +#define IPP_WORK_MAX_PAYLOAD sizeof(struct ipp_work_t) + +/** copy ipp header from src to dest + * + * used to prepare return work using incoming work header + * @param dest dest work ptr + * @param src src work ptr + */ +#define IPP_CPY_HEADER(dest, src) memcpy(dest, src, IPP_WORK_HDR_SIZE) + +/** + * dump in human readble way ipp struct + * + * @note require IPP_PRINT macro + * + * @param pw ipp_work struct to dump + * @param max_data max amount of data to be dump + * @param max_dev max number of dev (for check) + */ +static inline void ipp_dump_work(struct ipp_work_t *pw, uint32_t max_data, + int max_dev) +{ + uint32_t data_cnt; + uint32_t i; + uint8_t *pbdata; + + (void)max_dev; /* avoid warning when not used */ + (void)pbdata; /*avoid warning in case no print no use*/ + + IPP_PRINT("dev #%d (%s)\n", pw->dev_id, pw->dev_id < max_dev ? + "ok" : "bad"); + IPP_PRINT("process #%d (%s)\n", pw->process_no, + pw->process_no < stmvl53l1_ipp_max ? "ok" : "bad"); + IPP_PRINT("status %d\n", pw->status); + IPP_PRINT("Xfer id 0x%08X payload %d bytes (%s)\n", pw->xfer_id, + pw->payload, + pw->payload > IPP_WORK_MAX_PAYLOAD ? "invalid" : "ok"); + data_cnt = pw->payload > IPP_WORK_MAX_PAYLOAD ? + IPP_WORK_MAX_PAYLOAD : pw->payload; + data_cnt = data_cnt > max_data ? max_data : data_cnt; + for (i = 0, pbdata = (uint8_t *)pw->data; i < data_cnt; i++) { + if (i%16 == 0) + IPP_PRINT("\n%4X\t", i); + IPP_PRINT("%02X ", pbdata[i]); + } + IPP_PRINT("\n"); +} + +/** @} */ /* ingroup helper */ + +/** @} */ /* ingroup ipp_dev */ +#endif /* _STMVL53L1_IPP_H_ */ diff --git a/drivers/input/misc/vl53L1/stmvl53l1_ipp_nl.c b/drivers/input/misc/vl53L1/stmvl53l1_ipp_nl.c new file mode 100644 index 000000000000..af4dc93bf0cc --- /dev/null +++ b/drivers/input/misc/vl53L1/stmvl53l1_ipp_nl.c @@ -0,0 +1,383 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + +/** + * @file stmvl53l1_ipp_nl.c vl53l1 ipp proxy over netlink kernel side + */ +#include +#include +#include +#include +#include + +#include "stmvl53l1.h" + +#include "vl53l1_platform_ipp.h" +#include "stmvl53l1_ipp.h" + +#define IPP_DEBUG 1 +#ifndef IPP_DEBUG +# define _ipp_dump_work(...) (void)0 +#else +# define _ipp_dump_work(...) ipp_dump_work(__VA_ARGS__) +#endif + +#define IPP_STATE_PENDING 1 +#define IPP_STATE_COMPLETED 2 +#define IPP_STATE_CANCELED 4 + +#define IPP_TIMEOUT_MS 100 + +/** the single netlink strut use by all instance + * @note is NULL until set + */ +static struct sock *nl_sk; + +static DEFINE_MUTEX(ipp_mutex); + +/** + * current registered daemon pid + * @note default value 0 or later 1 is kind of invalid and will require + * user space to connect before we can send any packet + */ +static int daemon_pid; + +/** + * next xfer_id (shared other all dev) + * no direct us used @@ref get_next_xfer_id (will get lock) + * @note default to 0 what is "reserved" + */ +static int next_xfer_id; + + +#define ipp_err(fmt, ...) pr_err("STMVL53L1 IPP Err in %s %d :" fmt "\n", \ + __func__, __LINE__, ##__VA_ARGS__) + +#define ipp_warn(fmt, ...) pr_warn("STMVL53L1 IPP wng in %s %d : "fmt"\n",\ + __func__, __LINE__, ##__VA_ARGS__) + +#if 0 +# define ipp_dbg(fmt, ...) pr_info("IPP %s %d " fmt "\n",\ + __func__, __LINE__, ##__VA_ARGS__) +#else +# define ipp_dbg(...) (void)0 +#endif + +/** + * get and managed increment of next xfer_id + * @note will get ipp_mutex + * @return the xfer_id to be used + */ +static int get_next_xfer_id(void) +{ + mutex_lock(&ipp_mutex); + next_xfer_id++; + /*0 is reserved skip it*/ + if (next_xfer_id == 0) + next_xfer_id = 1; + mutex_unlock(&ipp_mutex); + + return next_xfer_id; +} + +static int send_client_msg(void *msg_data, int msg_size) +{ + int rc; + struct sk_buff *skb_out; + struct nlmsghdr *nlh; + void *nl_data; + + ipp_dbg("to send %d byte", msg_size); + skb_out = nlmsg_new(msg_size, 0); + if (!skb_out) { + ipp_err("nlmsg_new fail\n"); + return -1; + } + + nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0); + NETLINK_CB(skb_out).dst_group = 0; /* not in mcast group */ + + nl_data = nlmsg_data(nlh); /*get data ptr from header*/ + memcpy(nl_data, msg_data, msg_size); + + /* FIXME do we real need to lock to send a data other nl_sk ? */ + mutex_lock(&ipp_mutex); + rc = nlmsg_unicast(nl_sk, skb_out, daemon_pid); + if (rc < 0) + ipp_err("fail to send data size %d to pid %d\n", + msg_size, daemon_pid); + /* stat can be done here in else case */ + mutex_unlock(&ipp_mutex); + + return rc; +} + +/* + * ipp lock is held ping already handled + */ +int ipp_in_process(struct ipp_work_t *pwork) +{ + struct stmvl53l1_data *data; + + ipp_dbg("enter"); + _ipp_dump_work(pwork, IPP_WORK_MAX_PAYLOAD, STMVL53L1_CFG_MAX_DEV); + + /* work id check already done */ + data = stmvl53l1_dev_table[pwork->dev_id]; + ipp_dbg("to lock "); + /* Release now useless ipp_mutex for below work since we may deadlock + * with irq path. + */ + mutex_unlock(&ipp_mutex); + mutex_lock(&data->work_mutex); + if (data->ipp.buzy == IPP_STATE_PENDING) { + /* if it was already handled ignore it */ + if (data->ipp.waited_xfer_id == pwork->xfer_id) { + /* ok that is what we are expecting back */ + memcpy(&data->ipp.work_out, pwork, pwork->payload); + data->ipp.buzy |= IPP_STATE_COMPLETED; + ipp_dbg("to wake ipp waiter as buzy state %d", + data->ipp.buzy); + wake_up(&data->ipp.waitq); + goto done_lock; + } + } + /* either not waiting any more or not the expected id drop it */ + ipp_err("dev #%d ippp buzy %d xfer id %d rcv id %d droping it", + data->id, data->ipp.buzy, data->ipp.waited_xfer_id, + pwork->xfer_id); +done_lock: + mutex_unlock(&data->work_mutex); + mutex_lock(&ipp_mutex); + + return 0; +} + +int stmvl53l1_ipp_stop(struct stmvl53l1_data *data) +{ + int rc; + + rc = data->ipp.buzy; + ipp_dbg("#%d to stop buzy %d", data->id, data->ipp.buzy); + if (data->ipp.buzy) { + /* set invalid wait id to discard canceled job when back */ + data->ipp.waited_xfer_id = 0; + data->ipp.buzy |= IPP_STATE_CANCELED|IPP_STATE_COMPLETED; + ipp_dbg("#%dto wake up worker", data->id); + /* wake up worker or abort the thread */ + wake_up(&data->ipp.waitq); + } + + return rc; +} + +/* + * ipp and dev lock are held + * release and re-grabbed here + */ +int stmvl53l1_ipp_do(struct stmvl53l1_data *data, + struct ipp_work_t *pin, struct ipp_work_t *pout) +{ + int xfer_id; + int rc; + bool has_timeout; + + ipp_dbg("enter"); + + xfer_id = get_next_xfer_id(); + /* set xfer and device dependent part of the work */ + pin->dev_id = data->id; + pin->xfer_id = xfer_id; + data->ipp.waited_xfer_id = xfer_id; + /* try to do it */ + rc = send_client_msg(pin, pin->payload); + /* shall we retry if fail to send for some time or number of try ? */ + if (rc < 0) { + rc = -1; + ipp_err("fail to send msg %d", rc); + } else if (data->ipp.buzy == 0) { + /* send ok put the ipp on buzy state while locked */ + data->ipp.buzy = IPP_STATE_PENDING; + /* unlock now that state is marked buzy */ + mutex_unlock(&data->work_mutex); + + /* put task to wait for completion */ + ipp_dbg("to wait"); + has_timeout = !wait_event_timeout(data->ipp.waitq, + (data->ipp.buzy != IPP_STATE_PENDING), + msecs_to_jiffies(IPP_TIMEOUT_MS)); + + /* relock the main lock */ + mutex_lock(&data->work_mutex); + + rc = (data->ipp.buzy & IPP_STATE_CANCELED) || has_timeout ? + -1 : 0; + if (rc) { + ipp_dbg("waking up with from canceled/timeout ipp"); + } else { + /* return status from the ipp itself */ + ipp_dbg("ip back with status %d", data->ipp.status); + rc = data->ipp.status; + } + data->ipp.buzy = 0;/* buzy clear but locked so safe */ + } else { + ipp_dbg("buzy still not zero %d", data->ipp.buzy); + rc = -1; + } + + +/* done_lock: */ + return rc; +} + + +static void stmvl53l1_nl_recv_msg(struct sk_buff *skb_in) +{ + int pid_chg = 0; + int pid; + struct nlmsghdr *nlh; + struct ipp_work_t *pwork; + + ipp_dbg("Entering"); + + nlh = (struct nlmsghdr *)skb_in->data; + pid = nlh->nlmsg_pid; /*pid of sending process */ + + pwork = nlmsg_data(nlh); + if (pwork->payload < IPP_WORK_HDR_SIZE || + pwork->payload > IPP_WORK_MAX_PAYLOAD){ + /* invalid header size */ + ipp_err("invalid msg header size %d", pwork->payload); + _ipp_dump_work(pwork, IPP_WORK_MAX_PAYLOAD, + STMVL53L1_CFG_MAX_DEV); + return; + } + + mutex_lock(&ipp_mutex); + + if ((pwork->dev_id >= STMVL53L1_CFG_MAX_DEV) || (pwork->dev_id < 0)) { + ipp_err("invalid dev id on msg %d", pwork->dev_id); + _ipp_dump_work(pwork, IPP_WORK_MAX_PAYLOAD, + STMVL53L1_CFG_MAX_DEV); + goto done_locked; + } + + if (pwork->process_no == stmvl53l1_ipp_ping) { + /* in that case the payload must be exact status size only + * if not it is a badly format message or bad message + */ + if (pwork->payload != IPP_WORK_HDR_SIZE) { + ipp_err("invalid ping msg size %d!=%zu ", + pwork->payload, IPP_WORK_HDR_SIZE); + _ipp_dump_work(pwork, IPP_WORK_MAX_PAYLOAD, + STMVL53L1_CFG_MAX_DEV); + goto done_locked; + } + /* if pid was not set or change resent all ongoing ipp */ + if (pid != daemon_pid) + ipp_warn("pid chg %d => %d\n", daemon_pid, pid); + else + ipp_dbg("got ping fm pid %d\n", daemon_pid); + daemon_pid = pid; + pid_chg = 1; + } else { + ipp_in_process(pwork); + } + done_locked: + mutex_unlock(&ipp_mutex); +} + +int stmvl53l1_ipp_setup(struct stmvl53l1_data *data) +{ + int rc; + + mutex_lock(&ipp_mutex); + + data->ipp.buzy = 0; + init_waitqueue_head(&data->ipp.waitq); + ipp_dbg("now %d dev daemon pid is %d", STMVL53L1_CFG_MAX_DEV, + daemon_pid); + rc = 0; + + mutex_unlock(&ipp_mutex); + + return rc; +} + +void stmvl53l1_ipp_cleanup(struct stmvl53l1_data *data) +{ + /* nothink to do */ +} + +#if !defined(OLD_NETLINK_API) +struct netlink_kernel_cfg cfg = { + .input = stmvl53l1_nl_recv_msg +}; +#endif + +static int netlink_protocol_type = STMVL531_CFG_NETLINK_USER; + +module_param(netlink_protocol_type, int, 0444); +MODULE_PARM_DESC(netlink_protocol_type, + "select netlink protocol type for ipp communications"); + +int stmvl53l1_ipp_init(void) +{ + mutex_init(&ipp_mutex); + daemon_pid = 1; /* pid 1 is safe should not be use for user space */ + +#if defined(OLD_NETLINK_API) + nl_sk = netlink_kernel_create(&init_net, + netlink_protocol_type, + 0, + stmvl53l1_nl_recv_msg, + NULL, + THIS_MODULE); +#else + nl_sk = netlink_kernel_create(&init_net, + netlink_protocol_type, + &cfg); +#endif + + return nl_sk ? 0 : -1; +} + + +void stmvl53l1_ipp_exit(void) +{ + if (nl_sk != NULL) { + vl53l1_dbgmsg("releasing netlink socket"); + netlink_kernel_release(nl_sk); + nl_sk = NULL; + } +} + + diff --git a/drivers/input/misc/vl53L1/stmvl53l1_log.c b/drivers/input/misc/vl53L1/stmvl53l1_log.c new file mode 100644 index 000000000000..ecedcb446dda --- /dev/null +++ b/drivers/input/misc/vl53L1/stmvl53l1_log.c @@ -0,0 +1,78 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ +/** + * @file /stmvl53l1_module.c vl53l1_module ST VL53L1 linux kernel module + * + * This is implementation of low level driver trace support + */ +#include +#include + +#include "stmvl53l1.h" + +#ifdef VL53L1_LOG_ENABLE + +static bool trace_function; +static int trace_module; +static int trace_level; + +module_param(trace_function, bool, 0644); +MODULE_PARM_DESC(trace_function, + "allow tracing of low level function entry and exit"); + +module_param(trace_module, int, 0644); +MODULE_PARM_DESC(trace_module, + "control tracing of low level per module"); + +module_param(trace_level, int, 0644); +MODULE_PARM_DESC(trace_level, + "control tracing of low level per level"); + +void log_trace_print(uint32_t module, uint32_t level, uint32_t function, + const char *format, ...) +{ + va_list args; + + if (function && !trace_function) + return; + + if (!(module & trace_module)) + return; + + if (level > trace_level) + return; + + va_start(args, format); + vprintk(format, args); + va_end(args); +} + +#endif diff --git a/drivers/input/misc/vl53L1/stmvl53l1_module-i2c.c b/drivers/input/misc/vl53L1/stmvl53l1_module-i2c.c new file mode 100644 index 000000000000..a20b1ad55045 --- /dev/null +++ b/drivers/input/misc/vl53L1/stmvl53l1_module-i2c.c @@ -0,0 +1,959 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + +/** + * @file stmvl53l1_module-i2c.c + * + * implement STM VL53L1 module interface i2c wrapper + control + * using linux native i2c + gpio + reg api + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * power specific includes + */ +#include +#include +#include +#include +#include +#include + +#include "stmvl53l1-i2c.h" +#include "stmvl53l1.h" + +#define STMVL53L1_SLAVE_ADDR (0x52>>1) + +/** @ingroup drv_port + * @{ + */ + +/** + * control specific debug message echo + * + * Set to 0/1 do not remove + * + * most dbg warn err messages goes true main driver macro + * this one permit some specific debug without activating all main dbg + */ +#define MODI2C_DEBUG 0 + +/* + * mutex to handle device i2c address changes. It allow to avoid multiple + * device active with same i2c addresses at the same time. Note that we don't + * support case where boot_reg has the same value as a final i2c address of + * another device. + */ +static DEFINE_MUTEX(dev_addr_change_mutex); + +/** + * i2c client assigned to our driver + * + * this is use for stm test purpose as we fake client create and regstration + * we stores the i2c client for release in clean-up overwise we wan't reload + * the module multiple time + * + * in a normal dev tree prod system this is not required + */ +static struct i2c_client *stm_test_i2c_client; + +/* + * pi3: + * insmod stmvl53l1.ko force_device=1 adapter_nb=1 xsdn_gpio_nb=19 + * intr_gpio_nb=16 pwren_gpio_nb=12 + * + * panda + * insmod stmvl53l1.ko force_device=1 adapter_nb=4 xsdn_gpio_nb=56 + * intr_gpio_nb=59 pwren_gpio_nb=55 + */ + +static int force_device; +static int adapter_nb = -1; +static int xsdn_gpio_nb = -1; +static int pwren_gpio_nb = -1; +static int intr_gpio_nb = -1; + +module_param(force_device, int, 0000); +MODULE_PARM_DESC(force_device, "force device insertion at module init"); + +module_param(adapter_nb, int, 0000); +MODULE_PARM_DESC(adapter_nb, "i2c adapter to use"); + +module_param(xsdn_gpio_nb, int, 0000); +MODULE_PARM_DESC(xsdn_gpio_nb, "select gpio numer to use for vl53l1 reset"); + +module_param(pwren_gpio_nb, int, 0000); +MODULE_PARM_DESC(pwren_gpio_nb, "select gpio numer to use for vl53l1 power"); + +module_param(intr_gpio_nb, int, 0000); +MODULE_PARM_DESC(intr_gpio_nb, "select gpio numer to use for vl53l1 interrupt"); + +/** + * warn message + * + * @warning use only in scope where i2c_data ptr is present + **/ +#define modi2c_warn(fmt, ...)\ + dev_WARN(&i2c_data->client->dev, fmt, ##__VA_ARGS__) + +/** + * err message + * + * @warning use only in scope where i2c_data ptr is present + */ +#define modi2c_err(fmt, ...)\ + dev_err(&i2c_data->client->dev, fmt, ##__VA_ARGS__) + + + +#if MODI2C_DEBUG +# define modi2c_dbg(fmt, ...)\ + pr_devel("%s "fmt"\n", __func__, ##__VA_ARGS__) +#else +# define modi2c_dbg(...) (void)0 +#endif + +struct vl53l1_pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; +} g_pinctrl_info; + +static int insert_device(void) +{ + int ret = 0; + struct i2c_adapter *adapter; + struct i2c_board_info info = { + .type = "stmvl53l1", + .addr = STMVL53L1_SLAVE_ADDR, + }; + + memset(&info, 0, sizeof(info)); + strcpy(info.type, "stmvl53l1"); + info.addr = STMVL53L1_SLAVE_ADDR; + adapter = i2c_get_adapter(adapter_nb); + if (!adapter) { + ret = -EINVAL; + goto done; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0) + stm_test_i2c_client = i2c_new_device(adapter, &info); +#else + stm_test_i2c_client = i2c_new_client_device(adapter, &info); +#endif + if (!stm_test_i2c_client) + ret = -EINVAL; + +done: + return ret; +} + +static int get_xsdn(struct device *dev, struct i2c_data *i2c_data) +{ + int rc = 0; + + i2c_data->io_flag.xsdn_owned = 0; + if (i2c_data->xsdn_gpio == -1) { + vl53l1_errmsg("reset gpio is required"); + rc = -ENODEV; + goto no_gpio; + } + + vl53l1_dbgmsg("request xsdn_gpio %d", i2c_data->xsdn_gpio); + rc = gpio_request(i2c_data->xsdn_gpio, "vl53l1_xsdn"); + if (rc) { + vl53l1_errmsg("fail to acquire xsdn %d", rc); + goto request_failed; + } + + rc = gpio_direction_output(i2c_data->xsdn_gpio, 0); + if (rc) { + vl53l1_errmsg("fail to configure xsdn as output %d", rc); + goto direction_failed; + } + i2c_data->io_flag.xsdn_owned = 1; + + return rc; + +direction_failed: + gpio_free(i2c_data->xsdn_gpio); + +request_failed: +no_gpio: + return rc; +} + +static void put_xsdn(struct i2c_data *i2c_data) +{ + if (i2c_data->io_flag.xsdn_owned) { + vl53l1_dbgmsg("release xsdn_gpio %d", i2c_data->xsdn_gpio); + gpio_free(i2c_data->xsdn_gpio); + i2c_data->io_flag.xsdn_owned = 0; + i2c_data->xsdn_gpio = -1; + } + i2c_data->xsdn_gpio = -1; +} + +static int get_pwren(struct device *dev, struct i2c_data *i2c_data) +{ + int rc = 0; + + i2c_data->io_flag.pwr_owned = 0; + if (i2c_data->pwren_gpio == -1) { + vl53l1_wanrmsg("pwren gpio disable"); + goto no_gpio; + } + + vl53l1_dbgmsg("request pwren_gpio %d", i2c_data->pwren_gpio); + rc = gpio_request(i2c_data->pwren_gpio, "vl53l1_pwren"); + if (rc) { + vl53l1_errmsg("fail to acquire pwren %d", rc); + goto request_failed; + } + + rc = gpio_direction_output(i2c_data->pwren_gpio, 0); + if (rc) { + vl53l1_errmsg("fail to configure pwren as output %d", rc); + goto direction_failed; + } + i2c_data->io_flag.pwr_owned = 1; + + return rc; + +direction_failed: + gpio_free(i2c_data->xsdn_gpio); + +request_failed: +no_gpio: + return rc; +} + +static void put_pwren(struct i2c_data *i2c_data) +{ + if (i2c_data->io_flag.pwr_owned) { + vl53l1_dbgmsg("release pwren_gpio %d", i2c_data->pwren_gpio); + gpio_free(i2c_data->pwren_gpio); + i2c_data->io_flag.pwr_owned = 0; + i2c_data->pwren_gpio = -1; + } + i2c_data->pwren_gpio = -1; +} + +static int get_intr(struct device *dev, struct i2c_data *i2c_data) +{ + int rc = 0; + + i2c_data->io_flag.intr_owned = 0; + if (i2c_data->intr_gpio == -1) { + vl53l1_wanrmsg("no interrupt gpio"); + goto no_gpio; + } + + vl53l1_dbgmsg("request intr_gpio %d", i2c_data->intr_gpio); + rc = gpio_request(i2c_data->intr_gpio, "vl53l1_intr"); + if (rc) { + vl53l1_errmsg("fail to acquire intr %d", rc); + goto request_failed; + } + + rc = gpio_direction_input(i2c_data->intr_gpio); + if (rc) { + vl53l1_errmsg("fail to configure intr as input %d", rc); + goto direction_failed; + } + + i2c_data->irq = gpio_to_irq(i2c_data->intr_gpio); + if (i2c_data->irq < 0) { + vl53l1_errmsg("fail to map GPIO: %d to interrupt:%d\n", + i2c_data->intr_gpio, i2c_data->irq); + goto irq_failed; + } + i2c_data->io_flag.intr_owned = 1; + + return rc; + +irq_failed: +direction_failed: + gpio_free(i2c_data->intr_gpio); + +request_failed: +no_gpio: + return rc; +} + +static void put_intr(struct i2c_data *i2c_data) +{ + if (i2c_data->io_flag.intr_owned) { + if (i2c_data->io_flag.intr_started) { + free_irq(i2c_data->irq, i2c_data); + i2c_data->io_flag.intr_started = 0; + } + vl53l1_dbgmsg("release intr_gpio %d", i2c_data->intr_gpio); + gpio_free(i2c_data->intr_gpio); + i2c_data->io_flag.intr_owned = 0; + } + i2c_data->intr_gpio = -1; +} + +static int get_dt_xtalk_data(struct device_node *of_node, + struct stmvl53l1_data *vl53l1_data) +{ + int rc = 0; + uint32_t x_array[3] = {0}; + + if(of_node == NULL || vl53l1_data == NULL) + return -EINVAL; + + rc = of_property_read_u32_array(of_node, "st,xtalkval", + x_array, 3); + if (rc) { + vl53l1_errmsg("failed to read xtalk-offset\n"); + vl53l1_data->xtalk_offset = 0; + vl53l1_data->xtalk_x = 0; + vl53l1_data->xtalk_y = 0; + } else { + vl53l1_data->xtalk_offset = (uint16_t)x_array[0] & 0xFFFF; + vl53l1_data->xtalk_x = (int16_t)(x_array[1] & 0xFFFF); + vl53l1_data->xtalk_y = (int16_t)(x_array[2] & 0xFFFF); + } + + vl53l1_info("xtalk info: %u %d %d\n", + vl53l1_data->xtalk_offset, + vl53l1_data->xtalk_x, + vl53l1_data->xtalk_y); + return rc; +} + +/** + * parse dev tree for all platform specific input + */ +static int stmvl53l1_parse_tree(struct device *dev, struct i2c_data *i2c_data) +{ + int rc = 0; + + /* if force device is in use then gpio nb comes from module param else + * we use devicetree. + */ + i2c_data->vdd = NULL; + i2c_data->pwren_gpio = -1; + i2c_data->xsdn_gpio = -1; + i2c_data->intr_gpio = -1; + i2c_data->boot_reg = STMVL53L1_SLAVE_ADDR; + if (force_device) { + i2c_data->xsdn_gpio = xsdn_gpio_nb; + i2c_data->pwren_gpio = pwren_gpio_nb; + i2c_data->intr_gpio = intr_gpio_nb; + } else if (dev->of_node) { + /* power : either vdd or pwren_gpio. try reulator first */ + i2c_data->vdd = regulator_get_optional(dev, "vdd-vl53l1"); + if (IS_ERR(i2c_data->vdd) || i2c_data->vdd == NULL) { + i2c_data->vdd = NULL; + /* try gpio */ + i2c_data->pwren_gpio = of_get_named_gpio(dev->of_node, "pwren-gpio", 0); + if (i2c_data->pwren_gpio < 0) { + i2c_data->pwren_gpio = -1; + vl53l1_errmsg(" get i2c_data->pwren_gpio failed\n"); + } + } + + g_pinctrl_info.pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR_OR_NULL(g_pinctrl_info.pinctrl)) { + g_pinctrl_info.gpio_state_active = + pinctrl_lookup_state(g_pinctrl_info.pinctrl, + "laser_default"); + + g_pinctrl_info.gpio_state_suspend = + pinctrl_lookup_state(g_pinctrl_info.pinctrl, + "laser_suspend"); + } else + vl53l1_errmsg("Getting pinctrl handle failed\n"); + + rc = of_property_read_u32_array(dev->of_node, "xsdn-gpio", + &i2c_data->xsdn_gpio, 1); + if (rc) { + vl53l1_wanrmsg("Unable to find xsdn-gpio %d %d", + rc, i2c_data->xsdn_gpio); + if (of_gpio_count(dev->of_node) >= 2) + i2c_data->xsdn_gpio = of_get_gpio(dev->of_node, + 0); + else + i2c_data->xsdn_gpio = -1; + } + rc = of_property_read_u32_array(dev->of_node, "intr-gpio", + &i2c_data->intr_gpio, 1); + if (rc) { + vl53l1_wanrmsg("Unable to find intr-gpio %d %d", + rc, i2c_data->intr_gpio); + if (of_gpio_count(dev->of_node) >= 2) + i2c_data->intr_gpio = of_get_gpio(dev->of_node, + 1); + else + i2c_data->intr_gpio = -1; + } + rc = of_property_read_u32_array(dev->of_node, "boot-reg", + &i2c_data->boot_reg, 1); + if (rc) { + vl53l1_wanrmsg("Unable to find boot-reg %d %d", + rc, i2c_data->boot_reg); + i2c_data->boot_reg = STMVL53L1_SLAVE_ADDR; + } + + get_dt_xtalk_data(dev->of_node, i2c_data->vl53l1_data); + + i2c_data->vl53l1_data->force_device_on_en_default = + of_property_read_bool(dev->of_node, "force-device-on-en-default"); + } + + /* configure gpios */ + rc = get_xsdn(dev, i2c_data); + if (rc) + goto no_xsdn; + rc = get_pwren(dev, i2c_data); + if (rc) + goto no_pwren; + rc = get_intr(dev, i2c_data); + if (rc) + goto no_intr; + + return rc; + +no_intr: + if (i2c_data->vdd) { + regulator_put(i2c_data->vdd); + i2c_data->vdd = NULL; + } + put_pwren(i2c_data); +no_pwren: + put_xsdn(i2c_data); +no_xsdn: + return rc; +} + +static void stmvl53l1_release_gpios(struct i2c_data *i2c_data) +{ + put_xsdn(i2c_data); + if (i2c_data->vdd) { + regulator_put(i2c_data->vdd); + i2c_data->vdd = NULL; + } + put_pwren(i2c_data); + put_intr(i2c_data); +} + +static int stmvl53l1_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + struct stmvl53l1_data *vl53l1_data = NULL; + struct i2c_data *i2c_data = NULL; + + vl53l1_dbgmsg("Enter %s : 0x%02x\n", client->name, client->addr); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { + rc = -EIO; + return rc; + } + + vl53l1_data = kzalloc(sizeof(struct stmvl53l1_data), GFP_KERNEL); + if (!vl53l1_data) { + rc = -ENOMEM; + return rc; + } + + vl53l1_data->client_object = + kzalloc(sizeof(struct i2c_data), GFP_KERNEL); + if (!vl53l1_data->client_object) + goto done_freemem; + i2c_data = (struct i2c_data *)vl53l1_data->client_object; + + i2c_data->client = client; + i2c_data->vl53l1_data = vl53l1_data; + i2c_data->irq = -1 ; /* init to no irq */ + i2c_data->dma_data.len = 0; + mutex_init(&i2c_data->dma_data.lock); + + /* parse and configure hardware */ + rc = stmvl53l1_parse_tree(&i2c_data->client->dev, i2c_data); + if (rc) + goto done_freemem; + + /* setup device name */ + /* vl53l1_data->dev_name = dev_name(&client->dev); */ + + /* setup client data */ + i2c_set_clientdata(client, vl53l1_data); + + /* end up by core driver setup */ + rc = stmvl53l1_setup(vl53l1_data); + if (rc) + goto release_gpios; + + vl53l1_data->sysfs_base = i2c_data->client->addr; + rc = stmvl53l1_sysfs_laser(vl53l1_data, true); + if (rc) + goto release_gpios; + vl53l1_dbgmsg("End\n"); + + kref_init(&i2c_data->ref); + + return rc; + +release_gpios: + stmvl53l1_release_gpios(i2c_data); + +done_freemem: + /* kfree safe against NULL */ + kfree(vl53l1_data); + kfree(i2c_data); + + return -1; +} + +static int stmvl53l1_remove(struct i2c_client *client) +{ + struct stmvl53l1_data *data = i2c_get_clientdata(client); + struct i2c_data *i2c_data = (struct i2c_data *)data->client_object; + + vl53l1_dbgmsg("Enter\n"); + mutex_lock(&data->work_mutex); + + stmvl53l1_sysfs_laser(data, false); + + /* main driver cleanup */ + stmvl53l1_cleanup(data); + + /* release gpios */ + stmvl53l1_release_gpios(i2c_data); + + mutex_unlock(&data->work_mutex); + + stmvl53l1_put(data->client_object); + + vl53l1_dbgmsg("End\n"); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int stmvl53l1_suspend(struct device *dev) +{ + struct stmvl53l1_data *data = i2c_get_clientdata(to_i2c_client(dev)); + + vl53l1_dbgmsg("Enter\n"); + mutex_lock(&data->work_mutex); + /* Stop ranging */ + stmvl53l1_pm_suspend_stop(data); + + mutex_unlock(&data->work_mutex); + + vl53l1_dbgmsg("End\n"); + + return 0; +} + +static int stmvl53l1_resume(struct device *dev) +{ +#if 0 + struct stmvl53l1_data *data = i2c_get_clientdata(to_i2c_client(dev)); + + vl53l1_dbgmsg("Enter\n"); + + mutex_lock(&data->work_mutex); + + /* do nothing user will restart measurements */ + + mutex_unlock(&data->work_mutex); + + vl53l1_dbgmsg("End\n"); +#else + vl53l1_dbgmsg("Enter\n"); + vl53l1_dbgmsg("End\n"); +#endif + return 0; +} +#endif + + +static SIMPLE_DEV_PM_OPS(stmvl53l1_pm_ops, stmvl53l1_suspend, stmvl53l1_resume); + +static const struct i2c_device_id stmvl53l1_id[] = { + { STMVL53L1_DRV_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, stmvl53l1_id); + +static const struct of_device_id st_stmvl53l1_dt_match[] = { + { .compatible = "st,"STMVL53L1_DRV_NAME, }, + { }, +}; + +static struct i2c_driver stmvl53l1_driver = { + .driver = { + .name = STMVL53L1_DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = st_stmvl53l1_dt_match, + .pm = &stmvl53l1_pm_ops, + }, + .probe = stmvl53l1_probe, + .remove = stmvl53l1_remove, + .id_table = stmvl53l1_id, + +}; + +/** + * give power to device + * + * @param object the i2c layer object + * @return + */ +int stmvl53l1_power_up_i2c(void *object) +{ + int rc = 0; + struct i2c_data *data = (struct i2c_data *) object; + + vl53l1_dbgmsg("Enter\n"); + + /* turn on power */ + if (data->vdd) { + rc = regulator_enable(data->vdd); + if (rc) { + vl53l1_errmsg("fail to turn on regulator"); + return rc; + } + } else if (data->pwren_gpio != -1) { + gpio_set_value(data->pwren_gpio, 1); + vl53l1_info("slow power on"); + } else + vl53l1_wanrmsg("no power control"); +#ifdef TOF_USE_PMIC_WL2864C + /* TOF VDD provided by camera pmic wl2864c need more ramp-up time to be stable */ + usleep_range(5000, 5000 + 10); +#endif + return rc; +} + +/** + * remove power to device (reset it) + * + * @param i2c_object the i2c layer object + * @return 0 on success + */ +int stmvl53l1_power_down_i2c(void *i2c_object) +{ + struct i2c_data *data = (struct i2c_data *) i2c_object; + int rc = 0; + + vl53l1_dbgmsg("Enter\n"); + + /* turn off power */ + if (data->vdd) { + rc = regulator_disable(data->vdd); + if (rc) + vl53l1_errmsg("reg disable failed. rc=%d\n", + rc); + } else if (data->pwren_gpio != -1) { + gpio_set_value(data->pwren_gpio, 0); + } + vl53l1_dbgmsg("power off"); + + vl53l1_dbgmsg("End\n"); + + return rc; +} + +static int handle_i2c_address_device_change_lock(struct i2c_data *data) +{ + struct i2c_client *client = (struct i2c_client *) data->client; + uint8_t buffer[3]; + struct i2c_msg msg; + int rc = 0; + + vl53l1_dbgmsg("change device i2c address from 0x%02x to 0x%02x", + data->boot_reg, client->addr); + /* no i2c-access must occur before fw boot time */ + usleep_range(VL53L1_FIRMWARE_BOOT_TIME_US, + VL53L1_FIRMWARE_BOOT_TIME_US + 1); + + /* manually send message to update i2c address */ + buffer[0] = (VL53L1_I2C_SLAVE__DEVICE_ADDRESS >> 8) & 0xFF; + buffer[1] = (VL53L1_I2C_SLAVE__DEVICE_ADDRESS >> 0) & 0xFF; + buffer[2] = client->addr; + msg.addr = data->boot_reg; + msg.flags = client->flags; + msg.buf = buffer; + msg.len = 3; + if (i2c_transfer(client->adapter, &msg, 1) != 1) { + rc = -ENXIO; + vl53l1_errmsg("Fail to change i2c address to 0x%02x", + client->addr); + } + + return rc; +} + +/* reset release will also handle device address change. It will avoid state + * where multiple stm53l1 are bring out of reset at the same time with the + * same boot address. + * Note that we don't manage case where boot_reg has the same value as a final + * i2c address of another device. This case is not supported and will lead + * to unpredictable behavior. + */ +static int release_reset(struct i2c_data *data) +{ + struct i2c_client *client = (struct i2c_client *) data->client; + int rc = 0; + bool is_address_change = client->addr != data->boot_reg; + + if (is_address_change) + mutex_lock(&dev_addr_change_mutex); + + gpio_set_value(data->xsdn_gpio, 1); + if (is_address_change) { + rc = handle_i2c_address_device_change_lock(data); + if (rc) + gpio_set_value(data->xsdn_gpio, 0); + } + + if (is_address_change) + mutex_unlock(&dev_addr_change_mutex); + + return rc; +} + +/** + * release device reset + * + * @param i2c_object the i2c layer object + * @return 0 on success + */ +int stmvl53l1_reset_release_i2c(void *i2c_object) +{ + int rc; + struct i2c_data *data = (struct i2c_data *) i2c_object; + + vl53l1_dbgmsg("Enter\n"); + + rc = release_reset(data); + pinctrl_select_state(g_pinctrl_info.pinctrl, + g_pinctrl_info.gpio_state_active); + if (rc) + goto error; + + /* and now wait for device end of boot */ + data->vl53l1_data->is_delay_allowed = true; + rc = VL53L1_WaitDeviceBooted(&data->vl53l1_data->stdev); + data->vl53l1_data->is_delay_allowed = false; + if (rc) { + gpio_set_value(data->xsdn_gpio, 0); + vl53l1_errmsg("boot fail with error %d", rc); + data->vl53l1_data->last_error = rc; + rc = -EIO; + } + +error: + vl53l1_dbgmsg("End\n"); + + return rc; +} + +/** + * put device under reset + * + * @param i2c_object the i2c layer object + * @return 0 on success + */ +int stmvl53l1_reset_hold_i2c(void *i2c_object) +{ + struct i2c_data *data = (struct i2c_data *) i2c_object; + + vl53l1_dbgmsg("Enter\n"); + + gpio_set_value(data->xsdn_gpio, 0); + + pinctrl_select_state(g_pinctrl_info.pinctrl, + g_pinctrl_info.gpio_state_suspend); + + vl53l1_dbgmsg("End\n"); + + return 0; +} + +int stmvl53l1_init_i2c(void) +{ + int ret = 0; + + vl53l1_dbgmsg("Enter\n"); + + /* register as a i2c client device */ + ret = i2c_add_driver(&stmvl53l1_driver); + if (ret) + vl53l1_errmsg("%d erro ret:%d\n", __LINE__, ret); + + if (!ret && force_device) + ret = insert_device(); + + if (ret) + i2c_del_driver(&stmvl53l1_driver); + + vl53l1_dbgmsg("End with rc:%d\n", ret); + + return ret; +} + + +void stmvl53l1_clean_up_i2c(void) +{ + if (stm_test_i2c_client) { + vl53l1_dbgmsg("to unregister i2c client\n"); + i2c_unregister_device(stm_test_i2c_client); + } +} + +static irqreturn_t stmvl53l1_irq_handler_i2c(int vec, void *info) +{ + struct i2c_data *i2c_data = (struct i2c_data *)info; + + if (i2c_data->irq == vec) { + modi2c_dbg("irq"); + stmvl53l1_intr_handler(i2c_data->vl53l1_data); + modi2c_dbg("over"); + } else { + if (!i2c_data->msg_flag.unhandled_irq_vec) { + modi2c_warn("unmatching vec %d != %d\n", + vec, i2c_data->irq); + i2c_data->msg_flag.unhandled_irq_vec = 1; + } + } + + return IRQ_HANDLED; +} + +/** + * enable and start intr handling + * + * @param object our i2c_data specific object + * @param poll_mode [in/out] set to force mode clear to use irq + * @return 0 on success and set ->poll_mode if it faill ranging wan't start + */ +int stmvl53l1_start_intr(void *object, int *poll_mode) +{ + struct i2c_data *i2c_data; + int rc; + + i2c_data = (struct i2c_data *)object; + /* irq and gpio acquire config done in parse_tree */ + if (i2c_data->irq < 0) { + /* the i2c tree as no intr force polling mode */ + *poll_mode = -1; + return 0; + } + /* clear irq warning report enabe it again for this session */ + i2c_data->msg_flag.unhandled_irq_vec = 0; + /* if started do no nothing */ + if (i2c_data->io_flag.intr_started) { + /* nothing to do */ + *poll_mode = 0; + return 0; + } + + vl53l1_dbgmsg("to register_irq:%d\n", i2c_data->irq); + rc = request_threaded_irq(i2c_data->irq, NULL, + stmvl53l1_irq_handler_i2c, + IRQF_TRIGGER_FALLING|IRQF_ONESHOT, + "vl53l1_interrupt", + (void *)i2c_data); + if (rc) { + vl53l1_errmsg("fail to req threaded irq rc=%d\n", rc); + *poll_mode = 0; + } else { + vl53l1_dbgmsg("irq %d now handled\n", i2c_data->irq); + i2c_data->io_flag.intr_started = 1; + *poll_mode = 0; + } + return rc; +} + +void *stmvl53l1_get(void *object) +{ + struct i2c_data *data = (struct i2c_data *) object; + + vl53l1_dbgmsg("Enter\n"); + kref_get(&data->ref); + vl53l1_dbgmsg("End\n"); + + return object; +} + +static void memory_release(struct kref *kref) +{ + struct i2c_data *data = container_of(kref, struct i2c_data, ref); + + vl53l1_dbgmsg("Enter\n"); + mutex_lock(&data->dma_data.lock); + if (data->dma_data.len > 0 && data->dma_data.data) + kfree(data->dma_data.data); + mutex_unlock(&data->dma_data.lock); + kfree(data->vl53l1_data); + kfree(data); + vl53l1_dbgmsg("End\n"); +} + +void stmvl53l1_put(void *object) +{ + struct i2c_data *data = (struct i2c_data *) object; + + vl53l1_dbgmsg("Enter\n"); + kref_put(&data->ref, memory_release); + vl53l1_dbgmsg("End\n"); +} + +void __exit stmvl53l1_exit_i2c(void *i2c_object) +{ + vl53l1_dbgmsg("Enter\n"); + i2c_del_driver(&stmvl53l1_driver); + vl53l1_dbgmsg("End\n"); +} diff --git a/drivers/input/misc/vl53L1/stmvl53l1_module.c b/drivers/input/misc/vl53L1/stmvl53l1_module.c new file mode 100644 index 000000000000..9e80cddc71f0 --- /dev/null +++ b/drivers/input/misc/vl53L1/stmvl53l1_module.c @@ -0,0 +1,4901 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ +/** + * @file /stmvl53l1_module.c vl53l1_module ST VL53L1 linux kernel module + * + * main file + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * API includes + */ + +#include "stmvl53l1.h" +#include "stmvl53l1-i2c.h" +#include "stmvl53l1_ipp.h" + +#include "stmvl53l1_if.h" /* our device interface to user space */ +#include "stmvl53l1_internal_if.h" + +/* + * include default tuning file + */ +#include "stmvl53l1_tunings.h" + +/** @ingroup vl53l1_config + * @{ + */ +/** + * default polling period delay in millisecond + * + * It can be set at run time via @ref vl53l1_ioctl or @ref sysfs_attrib + * + * @note apply only for device operating in polling mode only + */ +#define STMVL53L1_CFG_POLL_DELAY_MS 5 + +/** + * default timing budget in microsecond + * + * Can be change at run time via @ref vl53l1_ioctl or @ref sysfs_attrib + */ +#define STMVL53L1_CFG_TIMING_BUDGET_US 16000 +#define STMVL53L1_CFG_SAR_MODE_TIMING_BUDGET_US 76000 + +/** default preset ranging mode */ +#define STMVL53L1_CFG_DEFAULT_MODE VL53L1_PRESETMODE_RANGING + +/** default distance mode */ +#define STMVL53L1_CFG_DEFAULT_DISTANCE_MODE VL53L1_DISTANCEMODE_LONG + +/** default crosstalk enable */ +#define STMVL53L1_CFG_DEFAULT_CROSSTALK_ENABLE 1 + +/** default crosstalk for autonomous mode */ +#define STMVL53L1_CFG_DEFAULT_CROSSTALK_AUTONOMOUS 1000 + +/** max distance value mm */ +#define STMVL53L1_MAX_DISTANCE 0x1FFF + +/** default output mode */ +#define STMVL53L1_CFG_DEFAULT_OUTPUT_MODE VL53L1_OUTPUTMODE_NEAREST + +#define STMVL53L1_CFG_DEFAULT_OFFSET_CORRECTION_MODE \ + VL53L1_OFFSETCORRECTIONMODE_STANDARD + +/** default Dmax mode */ +#define STMVL53L1_CFG_DEFAULT_DMAX_MODE VL53L1_DMAXMODE_FMT_CAL_DATA + +/** default smudge correction enable value */ +#define STMVL53L1_CFG_DEFAULT_SMUDGE_CORRECTION_MODE \ + VL53L1_SMUDGE_CORRECTION_NONE + +/** @} */ /* ingroup vl53l1_config */ + +/** @ingroup vl53l1_mod_dbg + * @{ + */ + +/** + * activate dump of roi in roi ctrl operation + * + * @note uses @a vl53l1_dbgmsg for output so make sure to enable debug + * to get roi dump + */ +#define STMVL53L1_CFG_ROI_DEBUG 0 + +/** @} */ /* ingroup vl53l1_mod_dbg*/ + +/* #define DEBUG_TIME_LOG */ + + +#ifdef DEBUG_TIME_LOG +struct timeval start_tv, stop_tv; +#endif + +/* Set default value to 1 to allow to see module insertion debug messages */ +int stmvl53l1_enable_debug = 1; + +#define VL53L1_INPUT_DEVICE_NAME "STM VL53L1 proximity sensor" + +static long stmvl53l1_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); +static int stmvl53l1_open(struct inode *inode, struct file *file); +static int stmvl53l1_release(struct inode *inode, struct file *file); +static int ctrl_start(struct stmvl53l1_data *data); +static int ctrl_stop(struct stmvl53l1_data *data); +static int ctrl_suspend(struct stmvl53l1_data *data); + +#ifdef CONFIG_COMPAT +static long stmvl53l1_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); +#endif + +// Turn off force_device_on flag and let ToF power supplies power down when ToF +// sensor ToF sensor powers down. +// If flag is set to true then VDD regulator will never turn down. +static bool force_device_on_en_default = false; + +module_param(force_device_on_en_default, bool, 0444); +MODULE_PARM_DESC(force_device_on_en_default, + "select whether force_device_on_en is true or false by default"); + +/* boilerplate for integer parameter */ +#define IMPLEMENT_PARAMETER_INTEGER(sysfs_name, info_name)\ +static ssize_t stmvl53l1_show_##sysfs_name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct stmvl53l1_data *data = dev_get_drvdata(dev); \ + int param; \ +\ + mutex_lock(&data->work_mutex); \ + param = data->sysfs_name; \ + mutex_unlock(&data->work_mutex);; \ +\ + return scnprintf(buf, PAGE_SIZE, "%d\n", param); \ +} \ +\ +static ssize_t stmvl53l1_store_##sysfs_name(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct stmvl53l1_data *data = dev_get_drvdata(dev); \ + int rc; \ + int param; \ +\ + mutex_lock(&data->work_mutex); \ +\ + if (kstrtoint(buf, 0, ¶m)) { \ + vl53l1_errmsg("invalid syntax in %s", buf); \ + rc = -EINVAL; \ + } else \ + rc = stmvl53l1_set_##sysfs_name(data, param); \ +\ + mutex_unlock(&data->work_mutex); \ +\ + return rc ? rc : count; \ +} \ +\ +static int ctrl_param_##sysfs_name(struct stmvl53l1_data *data, \ + struct stmvl53l1_parameter *param) \ +{ \ + int rc; \ +\ + if (param->is_read) { \ + param->value = data->sysfs_name; \ + param->status = 0; \ + vl53l1_dbgmsg("get " info_name " %d", param->value); \ + rc = 0; \ + } else { \ + rc = stmvl53l1_set_##sysfs_name(data, param->value); \ + vl53l1_dbgmsg("rc %d req %d now %d", rc, \ + param->value, data->sysfs_name); \ + } \ +\ + return rc; \ +} + +/** + * module interface struct + * interface to platform speficic device handling , concern power/reset ... + */ +struct stmvl53l1_module_fn_t { + int (*init)(void); /*!< init */ + /** + * clean up job + * @param data module specific data ptr + */ + void (*deinit)(void *data); + /** + * give device power + * @param data specific module storage ptr + * @return 0 on sucess + */ + int (*power_up)(void *data); + /** + * power down TOFO also stop intr + */ + int (*power_down)(void *data); + /* + * release reset so device start. + */ + int (*reset_release)(void *data); + /* + * put device under reset. + */ + int (*reset_hold)(void *data); + + /** + * enable interrupt + * + * @param object : interface speficic ptr + * @note "module specfic ptr is data->client_object + * @return 0 on success else error then drievr wan't start ranging! + * if no interrupt or it can't be hooked but still to operated in poll + * mode then return 0 and force data->poll_mode + * might have to clear poll_mode exlplcilty if to operate in real intr + * mode as pool mode + * is the default + */ + int (*start_intr)(void *object, int *poll_mode); + + void (*clean_up)(void); /*!< optional can be void */ + + /* increment reference counter */ + void *(*get)(void *object); + + /* decrement reference counter and deallocate memory when zero */ + void (*put)(void *object); +}; + +/** i2c module interface*/ +static struct stmvl53l1_module_fn_t stmvl53l1_module_func_tbl = { + .init = stmvl53l1_init_i2c, + .deinit = stmvl53l1_exit_i2c, + .power_up = stmvl53l1_power_up_i2c, + .power_down = stmvl53l1_power_down_i2c, + .reset_release = stmvl53l1_reset_release_i2c, + .reset_hold = stmvl53l1_reset_hold_i2c, + .clean_up = stmvl53l1_clean_up_i2c, + .start_intr = stmvl53l1_start_intr, + .get = stmvl53l1_get, + .put = stmvl53l1_put, +}; + + +#ifndef MIN +# define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +/* + * INPUT Subsys interface + */ + +static void stmvl53l1_input_push_data(struct stmvl53l1_data *data); + +/* + * Mutex to handle device id add/removal + */ +static DEFINE_MUTEX(dev_table_mutex); + +/** + * in-used device LUT + * we need this as the message reception from netlink message can't + * associate directly to a device instance that is as we look up id + * to device data structure + */ +struct stmvl53l1_data *stmvl53l1_dev_table[STMVL53L1_CFG_MAX_DEV]; + +/** + * Misc device device operations + */ +static const struct file_operations stmvl53l1_ranging_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = stmvl53l1_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = stmvl53l1_compat_ioctl, +#endif + .open = stmvl53l1_open, + .release = stmvl53l1_release, + /* .flush = stmvl53l0_flush, */ +}; + +static int store_last_error(struct stmvl53l1_data *data, int rc) +{ + data->last_error = rc; + + return -EIO; +} + +static int allocate_dev_id(void) +{ + int i; + + mutex_lock(&dev_table_mutex); + + for (i = 0; i < STMVL53L1_CFG_MAX_DEV; i++) + if (!stmvl53l1_dev_table[i]) + break; + i = i < STMVL53L1_CFG_MAX_DEV ? i : -1; + + mutex_unlock(&dev_table_mutex); + + return i; +} + +static void deallocate_dev_id(int id) +{ + mutex_lock(&dev_table_mutex); + + stmvl53l1_dev_table[id] = NULL; + + mutex_unlock(&dev_table_mutex); +} + +/* helpers to manage reader list for blockint ioctl */ +/* call them with lock */ +static void empty_and_free_list(struct list_head *head) +{ + struct stmvl53l1_waiters *waiter; + struct stmvl53l1_waiters *tmp; + + list_for_each_entry_safe(waiter, tmp, head, list) { + list_del(&waiter->list); + kfree(waiter); + } +} + +static int add_reader(pid_t pid, struct list_head *head) +{ + struct stmvl53l1_waiters *new_waiter; + + new_waiter = kmalloc(sizeof(struct stmvl53l1_waiters), GFP_KERNEL); + if (!new_waiter) + return -ENOMEM; + new_waiter->pid = pid; + list_add(&new_waiter->list, head); + + return 0; +} + +static bool is_pid_in_list(pid_t pid, struct list_head *head) +{ + struct stmvl53l1_waiters *waiter; + + list_for_each_entry(waiter, head, list) + if (waiter->pid == pid) + return true; + + return false; +} + +static void wake_up_data_waiters(struct stmvl53l1_data *data) +{ + empty_and_free_list(&data->simple_data_reader_list); + empty_and_free_list(&data->mz_data_reader_list); + wake_up(&data->waiter_for_data); +} + +static void stmvl53l1_insert_flush_events_lock(struct stmvl53l1_data *data) +{ + while (data->flush_todo_counter) { + data->flushCount++; + input_report_abs(data->input_dev_ps, ABS_GAS, data->flushCount); + input_sync(data->input_dev_ps); + vl53l1_dbgmsg("Sensor HAL Flush Count = %u\n", + data->flushCount); + data->flush_todo_counter--; + } +} + +static int reset_release(struct stmvl53l1_data *data) +{ + int rc; + + if (!data->reset_state) + return 0; + + vl53l1_dbgmsg("turn on vdd\n"); + rc = stmvl53l1_module_func_tbl.power_up(data->client_object); + if (rc) { + vl53l1_errmsg("%d,error rc %d\n", __LINE__, rc); + return rc; + } + + rc = stmvl53l1_module_func_tbl.reset_release(data->client_object); + if (rc) + vl53l1_errmsg("reset release fail rc=%d\n", rc); + else + data->reset_state = 0; + + return rc; +} + +static int reset_hold(struct stmvl53l1_data *data) +{ + int rc; + + if (data->reset_state) + return 0; + + if (data->force_device_on_en) + return 0; + + rc = stmvl53l1_module_func_tbl.reset_hold(data->client_object); + if (!rc) + data->reset_state = 1; + + vl53l1_dbgmsg("turn off vdd\n"); + rc = stmvl53l1_module_func_tbl.power_down(data->client_object); + if (rc) { + vl53l1_errmsg("%d,error rc %d\n", __LINE__, rc); + return rc; + } + + return rc; +} + +#ifdef DEBUG_TIME_LOG +static void stmvl53l0_DebugTimeGet(struct timeval *ptv) +{ + do_gettimeofday(ptv); +} + +#endif + +/** + * + * @param pstart_tv time val starting point + * @param pstop_tv time val end point + * @return time dif in usec + */ +long stmvl53l1_tv_dif(struct timeval *pstart_tv, struct timeval *pstop_tv) +{ + long total_sec, total_usec; + + total_sec = pstop_tv->tv_sec - pstart_tv->tv_sec; + total_usec = (pstop_tv->tv_usec - pstart_tv->tv_usec); + + return total_sec*1000000+total_usec; +} + +#if STMVL53L1_CFG_ROI_DEBUG +static void dump_roi(VL53L1_UserRoi_t *rois, uint32_t n) +{ + uint32_t i; + + vl53l1_dbgmsg("roi dump %d roi:\n", n); + for (i = 0; i < n ; i++) { + vl53l1_dbgmsg("ROI#%02d %2d %2d %2d %2d\n", (int)i, + (int)rois[i].TopLeftX, (int)rois[i].TopLeftY, + (int)rois[i].BotRightX, (int)rois[i].BotRightY); + } +} +#else +# define dump_roi(...) (void)0 +#endif + +static int setup_tunings(struct stmvl53l1_data *data) +{ + int rc = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(tunings); i++) { + rc = VL53L1_SetTuningParameter(&data->stdev, tunings[i][0], + tunings[i][1]); + if (rc) { + rc = store_last_error(data, rc); + break; + } + } + + return rc; +} + +/** + * + * @param data device data + * @return non 0 if current "preset mode" is a multi zone one + */ +static int is_mz_mode(struct stmvl53l1_data *data) +{ + return data->preset_mode == VL53L1_PRESETMODE_RANGING || + data->preset_mode == VL53L1_PRESETMODE_MULTIZONES_SCANNING; +} + +static void kill_mz_data(VL53L1_MultiRangingData_t *pdata) +{ + int i; + + memset(pdata, 0, sizeof(*pdata)); + for (i = 0; i < VL53L1_MAX_RANGE_RESULTS; i++) + pdata->RangeData[i].RangeStatus = VL53L1_RANGESTATUS_NONE; + pdata->RoiStatus = VL53L1_ROISTATUS_NOT_VALID; +} + +static void stmvl53l1_setup_auto_config(struct stmvl53l1_data *data) +{ + /* default config is detect object below 300mm with 1s period */ + data->auto_pollingTimeInMs = 1000; + data->auto_config.DetectionMode = VL53L1_DETECTION_DISTANCE_ONLY; + data->auto_config.IntrNoTarget = 0; + data->auto_config.Distance.CrossMode = VL53L1_THRESHOLD_CROSSED_LOW; + data->auto_config.Distance.High = 1000; + data->auto_config.Distance.Low = 300; + data->auto_config.Rate.CrossMode = VL53L1_THRESHOLD_CROSSED_LOW; + data->auto_config.Rate.High = 0; + data->auto_config.Rate.Low = 0; +} + +static uint32_t stmvl53l1_compute_hash(VL53L1_RoiConfig_t *roi_cfg) +{ + return jhash(roi_cfg->UserRois, + roi_cfg->NumberOfRoi * sizeof(VL53L1_UserRoi_t), + roi_cfg->NumberOfRoi); +} + +static int stmvl53l1_check_calibration_id(struct stmvl53l1_data *data) +{ + uint32_t roi_id; + int rc; + + if (data->offset_correction_mode != VL53L1_OFFSETCORRECTIONMODE_PERZONE) + return 0; + if (data->current_roi_id == 0) + return 0; + + roi_id = stmvl53l1_compute_hash(&data->roi_cfg); + rc = roi_id == data->current_roi_id ? 0 : -EINVAL; + + if (rc) + vl53l1_errmsg( + "Mismatch in zone calibration data 0x%08x != 0x%08x", + roi_id, data->current_roi_id); + + return rc; +} + +/** + * send params to sensor + * + * @warning must be used if only stopped + * @param data device data + * @return 0 on sucess + */ +static int stmvl53l1_sendparams(struct stmvl53l1_data *data) +{ + int rc; + VL53L1_CalibrationData_t cali; + + /* activated stored or last request defined mode */ + rc = VL53L1_SetPresetMode(&data->stdev, data->preset_mode); + if (rc) { + vl53l1_errmsg("VL53L1_SetPresetMode %d fail %d", + data->preset_mode, rc); + rc = store_last_error(data, rc); + goto done; + } + + if (data->preset_mode == VL53L1_PRESETMODE_AUTONOMOUS) { + /* disable xtalk and set short distance mode + * for autonomous mode */ + data->crosstalk_enable = 0; + data->distance_mode = VL53L1_DISTANCEMODE_SHORT; + } else { + /* restore xtalk and distance mode to default + * values for non-autonomous mode */ + data->crosstalk_enable = + STMVL53L1_CFG_DEFAULT_CROSSTALK_ENABLE; + data->distance_mode = + STMVL53L1_CFG_DEFAULT_DISTANCE_MODE; + } + + rc = VL53L1_SetXTalkCompensationEnable(&data->stdev, + data->crosstalk_enable); + if (rc) { + vl53l1_errmsg("VL53L1_SetXTalkCompensationEnable %d fail %d", + data->crosstalk_enable, rc); + rc = store_last_error(data, rc); + goto done; + } + + /* apply distance mode only in lite and standard ranging */ + rc = VL53L1_SetDistanceMode(&data->stdev, data->distance_mode); + if (rc) { + vl53l1_errmsg("VL53L1_SetDistanceMode %d fail %d", + data->distance_mode, rc); + rc = store_last_error(data, rc); + goto done; + } + + /* apply timing budget */ + rc = VL53L1_SetMeasurementTimingBudgetMicroSeconds(&data->stdev, + data->timing_budget); + if (rc) { + vl53l1_errmsg("SetTimingBudget %d fail %d", + data->timing_budget, rc); + rc = store_last_error(data, rc); + goto done; + } + vl53l1_dbgmsg("timing budget @%d\n", data->timing_budget); + + /* apply offset correction mode */ + rc = VL53L1_SetOffsetCorrectionMode(&data->stdev, + data->offset_correction_mode); + if (rc) { + vl53l1_errmsg("offset correction mode %d fail %d", + data->offset_correction_mode, rc); + rc = store_last_error(data, rc); + goto done; + } + vl53l1_dbgmsg("offset correction mode @%d\n", + data->offset_correction_mode); + + /* check zone calibration vs roi */ + rc = stmvl53l1_check_calibration_id(data); + if (rc) + goto done; + + /* apply Dmax reflectance */ + rc = VL53L1_SetDmaxReflectance(&data->stdev, data->dmax_reflectance); + if (rc) { + vl53l1_errmsg("dmax relectance %d fail %d", + data->dmax_reflectance, rc); + rc = store_last_error(data, rc); + goto done; + } + vl53l1_dbgmsg("dmax reflectance @%d\n", data->dmax_reflectance); + + /* apply Dmax mode */ + rc = VL53L1_SetDmaxMode(&data->stdev, data->dmax_mode); + if (rc) { + vl53l1_errmsg("dmax mode %d fail %d", data->dmax_mode, rc); + rc = store_last_error(data, rc); + goto done; + } + vl53l1_dbgmsg("dmax mode @%d\n", data->dmax_mode); + + /* apply smudge correction enable */ + rc = VL53L1_SmudgeCorrectionEnable(&data->stdev, + data->smudge_correction_mode); + if (rc) { + vl53l1_errmsg("smudge correction mode %d fail %d", + data->smudge_correction_mode, rc); + rc = store_last_error(data, rc); + goto done; + } + vl53l1_dbgmsg("smudge correction mode @%d\n", + data->smudge_correction_mode); + + /* apply roi if any set */ + if (data->roi_cfg.NumberOfRoi) { + rc = VL53L1_SetROI(&data->stdev, &data->roi_cfg); + if (rc) { + vl53l1_errmsg("VL53L1_SetROI fail %d\n", rc); + rc = store_last_error(data, rc); + goto done; + } + vl53l1_dbgmsg("#%d custom ROI set status\n", + data->roi_cfg.NumberOfRoi); + } else { + vl53l1_dbgmsg("using default ROI\n"); + } + + /* init the timing */ + do_gettimeofday(&data->start_tv); + data->meas.start_tv = data->start_tv; + /* init the ranging data => kill the previous ranging mz data */ + kill_mz_data(&data->meas.multi_range_data); + /* kill the single ranging data */ + memset(&data->meas.single_range_data, 0, + sizeof(VL53L1_RangingMeasurementData_t)); + + memset(&cali, 0, sizeof(cali)); + if (!data->cali_data) { + rc = VL53L1_GetCalibrationData(&data->stdev, &cali); + if (rc) { + vl53l1_errmsg("VL53L1_GetCalibrationData fail\n"); + rc = store_last_error(data, rc); + goto done; + } + } else { + memcpy(&cali, data->cali_data, sizeof(VL53L1_CalibrationData_t)); + } + + /* set autonomous mode configuration */ + if ((data->preset_mode == VL53L1_PRESETMODE_AUTONOMOUS) || + (data->preset_mode == VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS)) { + if (data->cam_mode == 0) { + rc = VL53L1_SetInterMeasurementPeriodMilliSeconds( + &data->stdev, + data->auto_pollingTimeInMs); + if (rc) { + vl53l1_errmsg("Fail to set auto period %d\n", + rc); + rc = store_last_error(data, rc); + goto done; + } + rc = VL53L1_SetThresholdConfig(&data->stdev, + &data->auto_config); + if (rc) { + vl53l1_errmsg("Fail to set auto config %d\n", + rc); + rc = store_last_error(data, rc); + goto done; + } + } else { + /* set camera autonomous configration */ + rc = VL53L1_SetInterMeasurementPeriodMilliSeconds( + &data->stdev, + data->auto_pollingTimeInMs_cam); + if (rc) { + vl53l1_errmsg("Fail to set auto period %d\n", + rc); + rc = store_last_error(data, rc); + goto done; + } + rc = VL53L1_SetThresholdConfig(&data->stdev, + &data->auto_config_cam); + if (rc) { + vl53l1_errmsg("Fail to set auto config %d\n", + rc); + rc = store_last_error(data, rc); + goto done; + } + } + cali.customer.algo__crosstalk_compensation_plane_offset_kcps + = STMVL53L1_CFG_DEFAULT_CROSSTALK_AUTONOMOUS; + } else { + cali.customer.algo__crosstalk_compensation_plane_offset_kcps + = data->xtalk_offset; + } + + rc = VL53L1_SetCalibrationData(&data->stdev, &cali); + if (rc) { + vl53l1_errmsg("VL53L1_SetCalibrationData fail\n"); + rc = store_last_error(data, rc); + goto done; + } + +done: + + return rc; +} + +/** + * start sensor + * + * @warning must be used if only stopped + * @param data device data + * @return 0 on sucess + */ +static int stmvl53l1_start(struct stmvl53l1_data *data) +{ + int rc; + + data->is_first_irq = true; + data->is_data_valid = false; + data->is_xtalk_value_changed = false; + + rc = reset_release(data); + if (rc) + goto done; + + /* full setup when out of reset or power up */ + rc = VL53L1_StaticInit(&data->stdev); + if (rc) { + vl53l1_errmsg("VL53L1_StaticInit @%d fail %d\n", + __LINE__, rc); + rc = store_last_error(data, rc); + goto done; + } + + rc = stmvl53l1_sendparams(data); + if (rc) + goto done; + + /* init the timing */ + do_gettimeofday(&data->start_tv); + data->meas.start_tv = data->start_tv; + /* init the ranging data => kill the previous ranging mz data */ + kill_mz_data(&data->meas.multi_range_data); + /* kill the single ranging data */ + memset(&data->meas.single_range_data, 0, + sizeof(VL53L1_RangingMeasurementData_t)); + + data->allow_hidden_start_stop = false; + /* kick off ranging */ + rc = VL53L1_StartMeasurement(&data->stdev); + if (rc) { + vl53l1_errmsg("VL53L1_StartMeasurement @%d fail %d", + __LINE__, rc); + rc = store_last_error(data, rc); + goto done; + } + + data->meas.cnt = 0; + data->meas.err_cnt = 0; + data->meas.err_tot = 0; + data->meas.poll_cnt = 0; + data->meas.intr = 0; + data->enable_sensor = 1; + if (data->poll_mode) { + /* kick off the periodical polling work */ + schedule_delayed_work(&data->dwork, + msecs_to_jiffies(data->poll_delay_ms)); + } +done: + data->is_first_start_done = true; + + return rc; +} + +/** + * stop sensor + * + * work lock must be held + * @warning to be used if only started! + */ +static int stmvl53l1_stop(struct stmvl53l1_data *data) +{ + int rc; + + rc = VL53L1_StopMeasurement(&data->stdev); + if (rc) { + vl53l1_errmsg("VL53L1_StopMeasurement @%d fail %d", + __LINE__, rc); + rc = store_last_error(data, rc); + } + /* put device under reset */ + /* do we ask explicit intr stop or just use stop */ + reset_hold(data); + + data->enable_sensor = 0; + if (data->poll_mode) { + /* cancel periodical polling work */ + cancel_delayed_work(&data->dwork); + } + + /* if we are in ipp waiting mode then abort it */ + stmvl53l1_ipp_stop(data); + /* wake up all waiters */ + /* they will receive -ENODEV error */ + wake_up_data_waiters(data); + + return rc; +} + +static int activate_sar_mode(struct stmvl53l1_data *data) +{ + int rc = VL53L1_ERROR_NONE; + + if (data == NULL) + return -EFAULT; + + if (data->enable_sensor == 0 && + !data->is_calibrating && + data->sar_mode == 1 && + data->cam_mode != 1) { + data->preset_mode = VL53L1_PRESETMODE_AUTONOMOUS; + data->timing_budget = STMVL53L1_CFG_SAR_MODE_TIMING_BUDGET_US; + rc = stmvl53l1_start(data); + if (rc == VL53L1_ERROR_NONE) + vl53l1_info("sensor in sar mode!\n"); + else + vl53l1_errmsg("fail to active sar err %d\n", rc); + } else + vl53l1_info("skip sar activation\n"); + + return rc; +} + +static int deactivate_sar_mode(struct stmvl53l1_data *data) +{ + int rc = VL53L1_ERROR_NONE; + + if (data == NULL) + return -EFAULT; + + if (data->enable_sensor == 1 && + !data->is_calibrating && + data->preset_mode == VL53L1_PRESETMODE_AUTONOMOUS && + data->cam_mode != 1) { + rc = stmvl53l1_stop(data); + if (rc == VL53L1_ERROR_NONE) { + vl53l1_info("sensor is out of sar mode!\n"); + /* set preset mode back to default ranging mode */ + data->preset_mode = VL53L1_PRESETMODE_RANGING; + } else + vl53l1_errmsg("fail to deactivate sar err %d\n", rc); + } else + vl53l1_info("sar is not actived, skip deactivation\n"); + + return rc; +} + +/* + * SysFS support + */ +static ssize_t stmvl53l1_show_enable_ps_sensor(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + + return snprintf(buf, 5, "%d\n", data->enable_sensor); +} + +static ssize_t stmvl53l1_store_enable_ps_sensor(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int rc = 0; + unsigned long val; + + rc = kstrtoul(buf, 10, &val); + if (rc) { + vl53l1_errmsg("enable sensor syntax in %s\n", buf); + return -EINVAL; + } + if (val == 1) { + rc = ctrl_start(data); + } else if (val == 0) { + rc = ctrl_stop(data); + } else { + //TODO: Remove this workaround after investigation + //see Codex - 479397 for details + vl53l1_dbgmsg("Unclog Input sub-system\n"); + /* Unclog the input device sub-system */ + input_report_abs(data->input_dev_ps, ABS_HAT0X, -1); + input_report_abs(data->input_dev_ps, ABS_HAT0Y, -1); + input_report_abs(data->input_dev_ps, ABS_HAT1X, -1); + input_report_abs(data->input_dev_ps, ABS_HAT1Y, -1); + input_report_abs(data->input_dev_ps, ABS_HAT2X, -1); + input_report_abs(data->input_dev_ps, ABS_HAT2Y, -1); + input_report_abs(data->input_dev_ps, ABS_HAT3X, -1); + input_report_abs(data->input_dev_ps, ABS_HAT3Y, -1); + input_report_abs(data->input_dev_ps, ABS_WHEEL, -1); + input_report_abs(data->input_dev_ps, ABS_BRAKE, -1); + input_report_abs(data->input_dev_ps, ABS_GAS, -1); + input_report_abs(data->input_dev_ps, ABS_TILT_X, -1); + input_report_abs(data->input_dev_ps, ABS_TILT_Y, -1); + input_report_abs(data->input_dev_ps, ABS_TOOL_WIDTH, -1); + input_report_abs(data->input_dev_ps, ABS_DISTANCE, -1); + input_report_abs(data->input_dev_ps, ABS_THROTTLE, -1); + input_report_abs(data->input_dev_ps, ABS_RUDDER, -1); + input_report_abs(data->input_dev_ps, ABS_MISC, -1); + input_report_abs(data->input_dev_ps, ABS_VOLUME, + -1); + input_sync(data->input_dev_ps); + vl53l1_dbgmsg("Unclog the input sub-system\n"); + rc = 0; + } + + vl53l1_dbgmsg("End\n"); + + return rc ? rc : count; +} + +/** + * sysfs attribute "enable_ps_sensor" [rd/wr] + * + * @li read show the current enable state + * @li write set the new state value "0" put sensor off "1" put it on + * + * @return 0 on success , EINVAL if fail to start + * + * @warning their's no check and assume exclusive usage of sysfs and ioctl\n + * Sensor will be put on/off disregard of any setup done by the ioctl channel. + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(enable_ps_sensor, 0664/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_enable_ps_sensor, + stmvl53l1_store_enable_ps_sensor); + +static int stmvl53l1_set_poll_delay_ms(struct stmvl53l1_data *data, int delay) +{ + int rc = 0; + + if (delay <= 0) + rc = -EINVAL; + else + data->poll_delay_ms = delay; + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(poll_delay_ms, "poll delay ms") + +/** + * sysfs attribute "poll_delay_ms" [rd/wr] + * + * @li read show the current polling delay in millisecond + * @li write set the new polling delay in millisecond + * + * @note apply only if device is in polling mode\n + * for best performances (minimal delay and cpu load ) set it to the device + * period operating period +1 millis + + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(set_delay_ms, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_poll_delay_ms, + stmvl53l1_store_poll_delay_ms); + +/* Timing Budget */ +static int stmvl53l1_set_timing_budget(struct stmvl53l1_data *data, int timing) +{ + int rc = 0; + + if (timing <= 0) { + vl53l1_errmsg("invalid timing valid %d\n", timing); + rc = -EINVAL; + } else if (data->enable_sensor) { + rc = VL53L1_SetMeasurementTimingBudgetMicroSeconds(&data->stdev, + timing); + if (rc) { + vl53l1_errmsg("SetTimingBudget %d fail %d", timing, rc); + rc = store_last_error(data, rc); + } else + data->timing_budget = timing; + } else + data->timing_budget = timing; + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(timing_budget, "timing budget") + +/** + * sysfs "timing_budget" [rd/wr] + * + * set or get the ranging timing budget in microsecond + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(timing_budget, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_timing_budget, + stmvl53l1_store_timing_budget); + + +static ssize_t stmvl53l1_show_roi(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int i; + int n; + + + mutex_lock(&data->work_mutex); + if (data->roi_cfg.NumberOfRoi == 0) { + /* none define by user */ + /* we could get what stored but may not even be default */ + n = scnprintf(buf, PAGE_SIZE, "device default\n"); + } else { + for (i = 0, n = 0; i < data->roi_cfg.NumberOfRoi; i++) { + n += scnprintf(buf+n, PAGE_SIZE-n, "%d %d %d %d%c", + data->roi_cfg.UserRois[i].TopLeftX, + data->roi_cfg.UserRois[i].TopLeftY, + data->roi_cfg.UserRois[i].BotRightX, + data->roi_cfg.UserRois[i].BotRightY, + i == data->roi_cfg.NumberOfRoi-1 ? + '\n' : ','); + } + } + mutex_unlock(&data->work_mutex); + return n; +} + + + +static ssize_t stmvl53l1_store_roi(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + VL53L1_UserRoi_t rois[VL53L1_MAX_USER_ZONES]; + int rc; + + mutex_lock(&data->work_mutex); + if (data->enable_sensor) { + vl53l1_errmsg(" cant set roi now\n"); + rc = -EBUSY; + } else { + int n, n_roi = 0; + const char *pc = buf; + int tlx, tly, brx, bry; + + while (n_roi < VL53L1_MAX_USER_ZONES && pc != NULL + && *pc != 0 && *pc != '\n') { + n = sscanf(pc, "%d %d %d %d", &tlx, &tly, &brx, &bry); + if (n == 4) { + rois[n_roi].TopLeftX = tlx; + rois[n_roi].TopLeftY = tly; + rois[n_roi].BotRightX = brx; + rois[n_roi].BotRightY = bry; + n_roi++; + } else { + vl53l1_errmsg( +"wrong roi #%d syntax around %s of %s", n_roi, pc, buf); + n_roi = -1; + break; + } + /* find next roi separator */ + pc = strchr(pc, ','); + if (pc) + pc++; + } + /*if any set them */ + if (n_roi >= 0) { + if (n_roi) + memcpy(data->roi_cfg.UserRois, rois, + n_roi*sizeof(rois[0])); + data->roi_cfg.NumberOfRoi = n_roi; + dump_roi(data->roi_cfg.UserRois, + data->roi_cfg.NumberOfRoi); + rc = count; + } else { + rc = -EINVAL; + } + } + mutex_unlock(&data->work_mutex); + vl53l1_dbgmsg("ret %d count %d\n", rc, (int)count); + + return rc; +} + +/** + * sysfs attribute "roi" [rd/wr] + * + * @li read show the current user customized roi setting + * @li write set user custom roi, it can only be done while not ranging. + * + * syntax for set input roi + * @li "[tlx tly brx bry,]\n" repeat n time require will set the n roi + * @li "\n" will reset + * + * @warning roi coordinate is not image x,y(down) but euclidian x,y(up) + * + * @warning roi validity is only check at next range start + * @warning user is responsible to set appropriate number an roi before each + * mode change + * @note roi can be return to default by setting none "" + * + *@code + * >#to set 2x roi + * >echo "0 15 15 0, 0 8 8 0" > /sys/class/input6/roi + * >echo $? + * 0 + * >cat /sys/class/input6/roi + * "0 15 15 0,0 8 8 0" + * #to cancel user define roi" + * >echo "" > /sys/class/input1/roi + * >echo $? + * 0 + * >echo "1" > /sys/class/input6/enable_ps_senspor + * #try to set roi while ranging + * >echo "0 15 15 0, 0 8 8 0" > /sys/class/input6/roi + * [58451.912109] stmvl53l1_store_roi: cant set roi now + * >echo $? + * 1 + *@endcode + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(roi, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_roi, + stmvl53l1_store_roi); + + +static int stmvl53l1_set_preset_mode(struct stmvl53l1_data *data, int mode) +{ + int rc = 0; + + if (data->enable_sensor) { + vl53l1_errmsg("can't change mode while ranging\n"); + rc = -EBUSY; + } else { + switch (mode) { + case VL53L1_PRESETMODE_RANGING: + case VL53L1_PRESETMODE_MULTIZONES_SCANNING: + case VL53L1_PRESETMODE_LITE_RANGING: + case VL53L1_PRESETMODE_AUTONOMOUS: + case VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS: + data->preset_mode = mode; + break; + default: + vl53l1_errmsg("invalid mode %d\n", mode); + rc = -EINVAL; + break; + } + } + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(preset_mode, "preset mode") + +/** + * sysfs attribute "mode " [rd/wr] + * + * set the mode value can only be used while: not ranging + * @li 1 @a VL53L1_PRESETMODE_RANGING default ranging + * @li 2 @a VL53L1_PRESETMODE_MULTIZONES_SCANNING multiple zone + * @li 3 @a VL53L1_PRESETMODE_AUTONOMOUS autonomous mode + * @li 4 @a VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS low Power autonomous mode + * @li 5 @a VL53L1_PRESETMODE_LITE_RANGING low mips ranging mode + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(mode, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_preset_mode, + stmvl53l1_store_preset_mode); + +static int stmvl53l1_set_distance_mode(struct stmvl53l1_data *data, + int distance_mode) +{ + int rc = 0; + + if (data->enable_sensor) { + vl53l1_errmsg("can't change distance mode while ranging\n"); + rc = -EBUSY; + } else { + switch (distance_mode) { + case VL53L1_DISTANCEMODE_SHORT: + case VL53L1_DISTANCEMODE_MEDIUM: + case VL53L1_DISTANCEMODE_LONG: + data->distance_mode = distance_mode; + break; + default: + vl53l1_errmsg("invalid distance mode %d\n", + distance_mode); + rc = -EINVAL; + break; + } + } + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(distance_mode, "distance mode") + +/** + * sysfs attribute " distance mode" [rd/wr] + * + * set the distance mode value can only be used while: not ranging + * @li 1 @a VL53L1_DISTANCEMODE_SHORT + * @li 2 @a VL53L1_DISTANCEMODE_MEDIUM + * @li 3 @a VL53L1_DISTANCEMODE_LONG + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(distance_mode, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_distance_mode, + stmvl53l1_store_distance_mode); + +static int stmvl53l1_set_crosstalk_enable(struct stmvl53l1_data *data, + int crosstalk_enable) +{ + int rc = 0; + + if (data->enable_sensor) { + vl53l1_errmsg("can't change crosstalk enable while ranging\n"); + rc = -EBUSY; + } else if (crosstalk_enable == 0 || crosstalk_enable == 1) { + data->crosstalk_enable = crosstalk_enable; + } else { + vl53l1_errmsg("invalid crosstalk enable %d\n", + crosstalk_enable); + rc = -EINVAL; + } + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(crosstalk_enable, "crosstalk enable") + +/** + * sysfs attribute " crosstalk enable" [rd/wr] + * + * control if crosstalk compensation is eanble or not + * @li 0 disable crosstalk compensation + * @li 1 enable crosstalk compensation + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(crosstalk_enable, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_crosstalk_enable, + stmvl53l1_store_crosstalk_enable); + +static int stmvl53l1_set_output_mode(struct stmvl53l1_data *data, + int output_mode) +{ + int rc = 0; + + if (data->enable_sensor) { + vl53l1_errmsg("can't change output mode while ranging\n"); + rc = -EBUSY; + } else { + switch (output_mode) { + case VL53L1_OUTPUTMODE_NEAREST: + case VL53L1_OUTPUTMODE_STRONGEST: + data->output_mode = output_mode; + break; + default: + vl53l1_errmsg("invalid output mode %d\n", output_mode); + rc = -EINVAL; + break; + } + } + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(output_mode, "output mode") + +/** + * sysfs attribute " output mode" [rd/wr] + * + * set the output mode value can only be used while: not ranging + * @li 1 @a VL53L1_OUTPUTMODE_NEAREST + * @li 2 @a VL53L1_OUTPUTMODE_STRONGEST + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(output_mode, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_output_mode, + stmvl53l1_store_output_mode); + + +static int stmvl53l1_set_force_device_on_en(struct stmvl53l1_data *data, + int force_device_on_en) +{ + int rc; + + if (force_device_on_en != 0 && force_device_on_en != 1) { + vl53l1_errmsg("invalid force_device_on_en mode %d\n", + force_device_on_en); + return -EINVAL; + } + + data->force_device_on_en = force_device_on_en; + + /* don't update reset if sensor is enable */ + if (data->enable_sensor) + return 0; + + /* ok update reset according force_device_on_en value */ + if (force_device_on_en) + rc = reset_release(data); + else + rc = reset_hold(data); + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(force_device_on_en, "force device on enable") + +/** + * sysfs attribute " force_device_on_enable" [rd/wr] + * + * Control if device is put under reset when stopped. + * @li 0 feature is disable. Device is put under reset when stopped. + * @li 1 feature is enable. Device is not put under reset when stopped. + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(force_device_on_enable, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_force_device_on_en, + stmvl53l1_store_force_device_on_en); + +static int stmvl53l1_set_offset_correction_mode(struct stmvl53l1_data *data, + int offset_correction_mode) +{ + int rc = 0; + + if (data->enable_sensor) { + vl53l1_errmsg( + "can't change offset correction mode while ranging\n"); + rc = -EBUSY; + } else { + switch (offset_correction_mode) { + case VL53L1_OFFSETCORRECTIONMODE_STANDARD: + case VL53L1_OFFSETCORRECTIONMODE_PERZONE: + data->offset_correction_mode = offset_correction_mode; + break; + default: + vl53l1_errmsg("invalid offset correction mode %d\n", + offset_correction_mode); + rc = -EINVAL; + break; + } + } + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(offset_correction_mode, "offset correction mode") + +/** + * sysfs attribute " offset_correction_mode" [rd/wr] + * + * Control which offset correction is apply on result. can only be used + * while: not ranging + * @li 1 @a VL53L1_OFFSETCORRECTIONMODE_STANDARD + * @li 2 @a VL53L1_OFFSETCORRECTIONMODE_PERZONE + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(offset_correction_mode, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_offset_correction_mode, + stmvl53l1_store_offset_correction_mode); + +static ssize_t stmvl53l1_do_flush(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + + mutex_lock(&data->work_mutex); + + data->flush_todo_counter++; + stmvl53l1_insert_flush_events_lock(data); + + mutex_unlock(&data->work_mutex); + + return count; +} + +static DEVICE_ATTR(do_flush, 0660/*S_IWUGO | S_IRUGO*/, + NULL, + stmvl53l1_do_flush); + +static ssize_t stmvl53l1_show_enable_debug(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", stmvl53l1_enable_debug); +} + +static ssize_t stmvl53l1_store_enable_debug(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int enable_debug; + int rc = 0; + + if (kstrtoint(buf, 0, &enable_debug)) { + vl53l1_errmsg("invalid syntax in %s", buf); + rc = -EINVAL; + } else + stmvl53l1_enable_debug = enable_debug; + + return rc ? rc : count; +} + +/** + * sysfs attribute " debug enable" [rd/wr] + * + * dynamic control of vl53l1_dbgmsg messages. Note that in any case your code + * must be enable with DEBUG in stmvl53l1.h at compile time. + * @li 0 disable vl53l1_dbgmsg messages + * @li 1 enable vl53l1_dbgmsg messages + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(enable_debug, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_enable_debug, + stmvl53l1_store_enable_debug); + +static ssize_t display_FixPoint1616(char *buf, size_t size, FixPoint1616_t fix) +{ + uint32_t msb = fix >> 16; + uint32_t lsb = fix & 0xffff; + + lsb = (lsb * 1000000ULL + 32768) / 65536; + + return scnprintf(buf, size, "%d.%06d", msb, (uint32_t) lsb); +} + +static ssize_t stmvl53l1_show_autonomous_config(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + ssize_t res = 0; + + res += scnprintf(&buf[res], PAGE_SIZE, "%d %d %d %d %d %d %d ", + data->auto_pollingTimeInMs, + data->auto_config.DetectionMode, + data->auto_config.IntrNoTarget, + data->auto_config.Distance.CrossMode, + data->auto_config.Distance.High, + data->auto_config.Distance.Low, + data->auto_config.Rate.CrossMode); + + res += display_FixPoint1616(&buf[res], PAGE_SIZE - res, + data->auto_config.Rate.High); + + res += scnprintf(&buf[res], PAGE_SIZE - res, " "); + + res += display_FixPoint1616(&buf[res], PAGE_SIZE - res, + data->auto_config.Rate.Low); + + res += scnprintf(&buf[res], PAGE_SIZE - res, "\n"); + + return res; +} + +static const char *parse_integer(const char *buf, int *res) +{ + int rc; + + while (*buf == ' ') + buf++; + rc = sscanf(buf, "%d ", res); + if (!rc) + return NULL; + + return strchr(buf, ' '); +} + +static bool is_float_format(const char *buf, bool is_last) +{ + char *dot = strchr(buf, '.'); + char *space_or_eos = strchr(buf, is_last ? '\0' : ' '); + + if (!space_or_eos) + return !!dot; + if (!dot) + return false; + + return dot < space_or_eos ? true : false; +} + +static int parse_FixPoint16x16_lsb(const char *lsb_char) +{ + int lsb = 0; + int digit_nb = 0; + + /* parse at most 6 digits */ + lsb_char++; + while (isdigit(*lsb_char) && digit_nb < 6) { + lsb = lsb * 10 + (*lsb_char - '0'); + lsb_char++; + digit_nb++; + } + while (digit_nb++ < 6) + lsb = lsb * 10; + + return div64_s64(lsb * 65536ULL + 500000, 1000000); +} + +/* parse next fix point value and return a pointer to next blank or newline + * character according to is_last parameter. + * parse string must have digit for integer part (something like '.125' will + * return an error) or an error will be return. Only the first 6 digit of the + * decimal part will be parsed. + */ +static const char *parse_FixPoint16x16(const char *buf, FixPoint1616_t *res, + bool is_last) +{ + bool is_float; + int msb; + int lsb = 0; + int rc; + + while (*buf == ' ') + buf++; + is_float = is_float_format(buf, is_last); + + /* scan msb */ + rc = sscanf(buf, "%d ", &msb); + if (!rc) + return NULL; + /* then lsb if present */ + if (is_float) + lsb = parse_FixPoint16x16_lsb(strchr(buf, '.')); + *res = (msb << 16) + lsb; + + return strchr(buf, is_last ? '\0' : ' '); +} + +static ssize_t stmvl53l1_store_autonomous_config(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int pollingTimeInMs, DetectionMode, IntrNoTarget; + int d_CrossMode, d_High, d_Low; + int r_CrossMode; + FixPoint1616_t r_High, r_Low; + const char *buf_ori = buf; + int rc; + + mutex_lock(&data->work_mutex); + + buf = parse_integer(buf, &pollingTimeInMs); + if (!buf) + goto invalid; + buf = parse_integer(buf, &DetectionMode); + if (!buf) + goto invalid; + buf = parse_integer(buf, &IntrNoTarget); + if (!buf) + goto invalid; + buf = parse_integer(buf, &d_CrossMode); + if (!buf) + goto invalid; + buf = parse_integer(buf, &d_High); + if (!buf) + goto invalid; + buf = parse_integer(buf, &d_Low); + if (!buf) + goto invalid; + buf = parse_integer(buf, &r_CrossMode); + if (!buf) + goto invalid; + buf = parse_FixPoint16x16(buf, &r_High, false); + if (!buf) + goto invalid; + buf = parse_FixPoint16x16(buf, &r_Low, true); + if (!buf) + goto invalid; + + data->auto_pollingTimeInMs = pollingTimeInMs; + data->auto_config.DetectionMode = DetectionMode; + data->auto_config.IntrNoTarget = IntrNoTarget; + data->auto_config.Distance.CrossMode = d_CrossMode; + data->auto_config.Distance.High = d_High; + data->auto_config.Distance.Low = d_Low; + data->auto_config.Rate.CrossMode = r_CrossMode; + data->auto_config.Rate.High = r_High; + data->auto_config.Rate.Low = r_Low; + + mutex_unlock(&data->work_mutex); + + return count; + +invalid: + vl53l1_errmsg("invalid syntax in %s", buf_ori); + rc = -EINVAL; + goto error; + +error: + mutex_unlock(&data->work_mutex); + + return rc; +} + +/** + * sysfs attribute " autonomous_config" [rd/wr] + * + * Will set/get autonomous configuration using sysfs. + * + * format is the following : + * + * + * + *@code + * > echo "1000 1 0 0 1000 300 0 2.2 1.001" > autonomous_config + *@endcode + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(autonomous_config, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_autonomous_config, + stmvl53l1_store_autonomous_config); + +static ssize_t stmvl53l1_show_last_error_config(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", data->last_error); +} + +/** + * sysfs attribute " last_error" [rd] + * + * Will get last internal error using sysfs. + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(last_error, 0440/*S_IRUGO*/, + stmvl53l1_show_last_error_config, + NULL); + +static ssize_t stmvl53l1_show_optical_center_config(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + ssize_t res = 0; + + res += display_FixPoint1616(&buf[res], PAGE_SIZE - res, + data->optical_offset_x); + res += scnprintf(&buf[res], PAGE_SIZE - res, " "); + res += display_FixPoint1616(&buf[res], PAGE_SIZE - res, + data->optical_offset_y); + + res += scnprintf(&buf[res], PAGE_SIZE - res, "\n"); + + return res; +} + +/** + * sysfs attribute " optical_center" [rd] + * + * Will get optical_center using sysfs. + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(optical_center, 0440/*S_IRUGO*/, + stmvl53l1_show_optical_center_config, + NULL); + +static int stmvl53l1_set_dmax_reflectance(struct stmvl53l1_data *data, + int dmax_reflectance) +{ + int rc = 0; + + if (data->enable_sensor) { + vl53l1_errmsg( + "can't change dmax reflectance while ranging\n"); + rc = -EBUSY; + } else + data->dmax_reflectance = dmax_reflectance; + + return rc; +} + +static ssize_t stmvl53l1_show_dmax_reflectance(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + ssize_t res = 0; + + res += display_FixPoint1616(&buf[res], PAGE_SIZE - res, + data->dmax_reflectance); + + res += scnprintf(&buf[res], PAGE_SIZE - res, "\n"); + + return res; +} + +static ssize_t stmvl53l1_store_dmax_reflectance(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + FixPoint1616_t dmax_reflectance; + const char *buf_ori = buf; + int rc; + + mutex_lock(&data->work_mutex); + + buf = parse_FixPoint16x16(buf, &dmax_reflectance, true); + if (!buf) + goto invalid; + rc = stmvl53l1_set_dmax_reflectance(data, dmax_reflectance); + if (rc) + goto error; + + mutex_unlock(&data->work_mutex); + + return count; + +invalid: + vl53l1_errmsg("invalid syntax in %s", buf_ori); + rc = -EINVAL; + goto error; + +error: + mutex_unlock(&data->work_mutex); + + return rc; +} + +/** + * sysfs attribute "dmax_reflectance" [rd/wr] + * + * target reflectance use for calculate the ambient DMAX. can only be used + * while: not ranging + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(dmax_reflectance, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_dmax_reflectance, + stmvl53l1_store_dmax_reflectance); + +static int stmvl53l1_set_dmax_mode(struct stmvl53l1_data *data, + int dmax_mode) +{ + int rc = 0; + + if (data->enable_sensor) { + vl53l1_errmsg("can't change dmax mode while ranging\n"); + rc = -EBUSY; + } else { + switch (dmax_mode) { + case VL53L1_DMAXMODE_FMT_CAL_DATA: + case VL53L1_DMAXMODE_CUSTCAL_DATA: + case VL53L1_DMAXMODE_PER_ZONE_CAL_DATA: + data->dmax_mode = dmax_mode; + break; + default: + vl53l1_errmsg("invalid dmax mode %d\n", dmax_mode); + rc = -EINVAL; + break; + } + } + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(dmax_mode, "dmax mode") + +/** + * sysfs attribute " dmax mode" [rd/wr] + * + * set the dmax mode value can only be used while: not ranging + * @li 1 @a VL53L1_DMAXMODE_FMT_CAL_DATA + * @li 2 @a VL53L1_DMAXMODE_CUSTCAL_DATA + * @li 3 @a VL53L1_DMAXMODE_PER_ZONE_CAL_DATA + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(dmax_mode, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_dmax_mode, + stmvl53l1_store_dmax_mode); + +static int stmvl53l1_set_tuning(struct stmvl53l1_data *data, int key, + int value) +{ + int rc; + + if (data->enable_sensor) { + vl53l1_errmsg("can't change tuning params while ranging\n"); + return -EBUSY; + } + + if (data->is_calibrating) { + vl53l1_errmsg("can't change tuning params while calibrating\n"); + return -EBUSY; + } + + if (key & ~0xffff) + return -EINVAL; + + vl53l1_dbgmsg("trying to set %d with key %d", value, key); + + rc = VL53L1_SetTuningParameter(&data->stdev, key, value); + if (rc) + rc = store_last_error(data, rc); + + return rc; +} + +static ssize_t stmvl53l1_store_tuning(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int key; + int value; + int n; + int rc; + + mutex_lock(&data->work_mutex); + + n = sscanf(buf, "%d %d", &key, &value); + if (n != 2) { + rc = -EINVAL; + goto error; + } + rc = stmvl53l1_set_tuning(data, key, value); + if (rc) + goto error; + + mutex_unlock(&data->work_mutex); + + return count; + +error: + mutex_unlock(&data->work_mutex); + + return rc; +} + +/** + * sysfs attribute "tuning" [wr] + * + * write a tuning parameter. Two integer parameters are given. First one + * is a key that specify which tuning parameter is update. Other one is the + * value which is write. + * + * writing a tuning parameter is only allowed before the first start. + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(tuning, 0220/*S_IWUGO */, + NULL, + stmvl53l1_store_tuning); + +static int stmvl53l1_display_tuning_key(struct stmvl53l1_data *data, char *buf, + int *pos, int key) +{ + int rc = 0; + int value = 0; + int sz; + + rc = VL53L1_GetTuningParameter(&data->stdev, key, &value); + if (rc) + return 0; + + sz = snprintf(&buf[*pos], PAGE_SIZE - *pos, "%d %d\n", key, value); + if (sz >= PAGE_SIZE - *pos) + return -ENOSPC; /* FIXME : another better error ? */ + + *pos += sz; + + return 0; +} + +static ssize_t stmvl53l1_show_tuning_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const int max_tuning_key = 65535; + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int rc; + int i; + int pos = 0; + + mutex_lock(&data->work_mutex); + + for (i = 0; i < max_tuning_key; ++i) { + rc = stmvl53l1_display_tuning_key(data, buf, &pos, i); + if (rc) + break; + } + + mutex_unlock(&data->work_mutex); + + return rc ? rc : pos; +} + +/** + * sysfs attribute "tuning_status" [rd] + * + * write a tuning parameter. Two integer parameters are given. First one + * is a key that specify which tuning parameter is update. Other one is the + * value which is write. + * + * writing a tuning parameter is only allowed before the first start. + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(tuning_status, 0440/*S_IRUGO */, + stmvl53l1_show_tuning_status, + NULL); + +static int stmvl53l1_set_smudge_correction_mode(struct stmvl53l1_data *data, + int smudge_correction_mode) +{ + int rc = 0; + + if (data->enable_sensor) { + vl53l1_errmsg("can't change smudge corr mode while ranging\n"); + rc = -EBUSY; + } else { + switch (smudge_correction_mode) { + case VL53L1_SMUDGE_CORRECTION_NONE: + case VL53L1_SMUDGE_CORRECTION_CONTINUOUS: + case VL53L1_SMUDGE_CORRECTION_SINGLE: + case VL53L1_SMUDGE_CORRECTION_DEBUG: + data->smudge_correction_mode = smudge_correction_mode; + break; + default: + vl53l1_errmsg("invalid smudge correction mode %d\n", + smudge_correction_mode); + rc = -EINVAL; + break; + } + } + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(smudge_correction_mode, "smudge correction mode") + +/** + * sysfs attribute " smudge_correction_mode" [rd/wr] + * + * This parameter will control if smudge correction is enable and how crosstalk + * values are updated. + * @li 0 @a VL53L1_SMUDGE_CORRECTION_NONE + * @li 1 @a VL53L1_SMUDGE_CORRECTION_CONTINUOUS + * @li 2 @a VL53L1_SMUDGE_CORRECTION_SINGLE + * @li 3 @a VL53L1_SMUDGE_CORRECTION_DEBUG + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(smudge_correction_mode, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_smudge_correction_mode, + stmvl53l1_store_smudge_correction_mode); + +static ssize_t stmvl53l1_show_is_xtalk_value_changed_config(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int param; + + mutex_lock(&data->work_mutex); + param = data->is_xtalk_value_changed; + mutex_unlock(&data->work_mutex); + + return scnprintf(buf, PAGE_SIZE, "%d\n", param); +} + +/** + * sysfs attribute " optical_center" [rd] + * + * Will get optical_center using sysfs. + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(is_xtalk_value_changed, 0440/*S_IRUGO*/, + stmvl53l1_show_is_xtalk_value_changed_config, + NULL); + +static ssize_t stmvl53l1_show_enable_sar(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", data->sar_mode); +} + +static ssize_t stmvl53l1_store_enable_sar(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int rc = 0; + int mode; + + mutex_lock(&data->work_mutex); + + if (kstrtoint(buf, 0, &mode)) { + vl53l1_errmsg("invalid syntax in %s\n", buf); + rc = -EINVAL; + } else { + data->sar_mode = mode?1:0; + vl53l1_dbgmsg("sar mode is set to %d\n", data->sar_mode); + + if (data->sar_mode == 1) { + if (mode == 1) { + /* report far distance as initial value */ + struct input_dev *input = data->input_dev_ps; + + input_report_abs(input, ABS_HAT1X, 100); + input_report_abs(input, ABS_HAT1X, 101); + input_report_abs(input, ABS_HAT1X, 100); + input_sync(input); + } + activate_sar_mode(data); + } else + deactivate_sar_mode(data); + } + + mutex_unlock(&data->work_mutex); + + return rc ? rc : count; +} + +/** + * sysfs attribute "enable_sar" [rd/wr] + * + * To enable/disable the sar mode + * @li 0 disable sar mode + * @li 1 enable sar mode + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(enable_sar, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_enable_sar, + stmvl53l1_store_enable_sar); + +static ssize_t stmvl53l1_show_offset(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int rc; + struct stmvl53l1_data *data = dev_get_drvdata(dev); + VL53L1_CalibrationData_t cali_data; + + mutex_lock(&data->work_mutex); + memset(&cali_data, 0, sizeof(cali_data)); + rc = VL53L1_GetCalibrationData(&data->stdev, &cali_data); + if (rc) { + mutex_unlock(&data->work_mutex); + vl53l1_errmsg("VL53L1_GetCalibrationData fail\n"); + return -EINVAL; + } + mutex_unlock(&data->work_mutex); + + return scnprintf(buf, PAGE_SIZE, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + cali_data.customer.mm_config__inner_offset_mm, + cali_data.customer.mm_config__outer_offset_mm, + cali_data.customer.global_config__spad_enables_ref_0, + cali_data.customer.global_config__spad_enables_ref_1, + cali_data.customer.global_config__spad_enables_ref_2, + cali_data.customer.global_config__spad_enables_ref_3, + cali_data.customer.global_config__spad_enables_ref_4, + cali_data.customer.global_config__spad_enables_ref_5, + cali_data.customer.ref_spad_man__num_requested_ref_spads, + cali_data.customer.ref_spad_man__ref_location, + cali_data.cust_dmax_cal.ref__actual_effective_spads, + cali_data.cust_dmax_cal.ref__peak_signal_count_rate_mcps, + cali_data.cust_dmax_cal.ref__distance_mm, + cali_data.cust_dmax_cal.ref_reflectance_pc, + cali_data.cust_dmax_cal.coverglass_transmission); +} + +static ssize_t stmvl53l1_store_offset(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + VL53L1_CalibrationData_t cali_data; + int rc = 0; + int n = 0; + int inner = 0; + int outer = 0; + int refspad_0 = 0; + int refspad_1 = 0; + int refspad_2 = 0; + int refspad_3 = 0; + int refspad_4 = 0; + int refspad_5 = 0; + int refspad_num = 0; + int refspad_location = 0; + int ref__actual_effective_spads = 0; + int ref__peak_signal_count_rate_mcps = 0; + int ref__distance_mm = 0; + int ref_reflectance_pc = 0; + int coverglass_transmission = 0; + mutex_lock(&data->work_mutex); + + if (data->enable_sensor) { + vl53l1_errmsg("can't set offset while ranging\n"); + rc = -EBUSY; + goto finish; + } + + n = sscanf(buf, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d", + &inner, &outer,&refspad_0,&refspad_1,&refspad_2,&refspad_3, + &refspad_4, &refspad_5,&refspad_num,&refspad_location, + &ref__actual_effective_spads,&ref__peak_signal_count_rate_mcps, + &ref__distance_mm,&ref_reflectance_pc,&coverglass_transmission); + if(n != 15) { + vl53l1_errmsg("wrong offset syntax around %s\n", buf); + rc = -EINVAL; + goto finish; + } + + memset(&cali_data, 0, sizeof(cali_data)); + rc = VL53L1_GetCalibrationData(&data->stdev, &cali_data); + if (rc) { + vl53l1_errmsg("VL53L1_GetCalibrationData fail\n"); + rc = -EINVAL; + goto finish; + } + + /* update offset values */ + vl53l1_info("previous offset is %d,%d\n", + cali_data.customer.mm_config__inner_offset_mm, + cali_data.customer.mm_config__outer_offset_mm); + cali_data.customer.mm_config__inner_offset_mm = inner; + cali_data.customer.mm_config__outer_offset_mm = outer; + cali_data.customer.global_config__spad_enables_ref_0 = refspad_0; + cali_data.customer.global_config__spad_enables_ref_1 = refspad_1; + cali_data.customer.global_config__spad_enables_ref_2 = refspad_2; + cali_data.customer.global_config__spad_enables_ref_3 = refspad_3; + cali_data.customer.global_config__spad_enables_ref_4 = refspad_4; + cali_data.customer.global_config__spad_enables_ref_5 = refspad_5; + cali_data.customer.ref_spad_man__num_requested_ref_spads = refspad_num; + cali_data.customer.ref_spad_man__ref_location = ref_reflectance_pc; + cali_data.cust_dmax_cal.ref__actual_effective_spads = ref__actual_effective_spads; + cali_data.cust_dmax_cal.ref__peak_signal_count_rate_mcps = ref__peak_signal_count_rate_mcps; + cali_data.cust_dmax_cal.ref__distance_mm = ref__distance_mm; + cali_data.cust_dmax_cal.ref_reflectance_pc = ref_reflectance_pc; + cali_data.cust_dmax_cal.coverglass_transmission = coverglass_transmission; + + rc = VL53L1_SetCalibrationData(&data->stdev, &cali_data); + if (rc) { + vl53l1_errmsg("VL53L1_SetCalibrationData fail\n"); + rc = -EINVAL; + goto finish; + } + + data->inner_offset = (int16_t)inner; + data->outer_offset = (int16_t)outer; + vl53l1_info("offset is set to %d,%d\n", inner, outer); +finish: + mutex_unlock(&data->work_mutex); + return rc ? rc : count; +} + +/** + * sysfs attribute "offset" [rd/wr] + * + * To read/write offset calibration data + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(offset, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_offset, + stmvl53l1_store_offset); + +static ssize_t stmvl53l1_show_xtalk(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int rc; + struct stmvl53l1_data *data = dev_get_drvdata(dev); + VL53L1_CalibrationData_t cali_data; + + mutex_lock(&data->work_mutex); + memset(&cali_data, 0, sizeof(cali_data)); + rc = VL53L1_GetCalibrationData(&data->stdev, &cali_data); + if (rc) { + mutex_unlock(&data->work_mutex); + vl53l1_errmsg("VL53L1_GetCalibrationData fail\n"); + return -EINVAL; + } + mutex_unlock(&data->work_mutex); + + return scnprintf(buf, PAGE_SIZE, "%d,%d,%d", + cali_data.customer.algo__crosstalk_compensation_plane_offset_kcps, + cali_data.customer.algo__crosstalk_compensation_x_plane_gradient_kcps, + cali_data.customer.algo__crosstalk_compensation_y_plane_gradient_kcps); +} + +static ssize_t stmvl53l1_store_xtalk(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + VL53L1_CalibrationData_t cali_data; + int rc = 0; + int n = 0; + int xtalk_offset = 0; + int xtalk_x = 0; + int xtalk_y = 0; + + mutex_lock(&data->work_mutex); + if (data->enable_sensor) { + vl53l1_errmsg("can't set offset while ranging\n"); + rc = -EBUSY; + goto finish; + } + + n = sscanf(buf, "%d,%d,%d", &xtalk_offset, &xtalk_x, &xtalk_y); + if(n != 3) { + vl53l1_errmsg("wrong xtalk syntax around %s\n", buf); + rc = -EINVAL; + goto finish; + } + + memset(&cali_data, 0, sizeof(cali_data)); + rc = VL53L1_GetCalibrationData(&data->stdev, &cali_data); + if (rc) { + vl53l1_errmsg("VL53L1_GetCalibrationData fail\n"); + rc = -EINVAL; + goto finish; + } + + /* update xtalk values */ + vl53l1_info("previous xtalk is %d,%d,%d\n", + cali_data.customer.algo__crosstalk_compensation_plane_offset_kcps, + cali_data.customer.algo__crosstalk_compensation_x_plane_gradient_kcps, + cali_data.customer.algo__crosstalk_compensation_y_plane_gradient_kcps); + + cali_data.customer.algo__crosstalk_compensation_plane_offset_kcps = xtalk_offset; + cali_data.customer.algo__crosstalk_compensation_x_plane_gradient_kcps = xtalk_x; + cali_data.customer.algo__crosstalk_compensation_y_plane_gradient_kcps = xtalk_y; + rc = VL53L1_SetCalibrationData(&data->stdev, &cali_data); + if (rc) { + vl53l1_errmsg("VL53L1_SetCalibrationData fail\n"); + rc = -EINVAL; + goto finish; + } + + data->xtalk_offset = xtalk_offset; + data->xtalk_x = xtalk_x; + data->xtalk_y = xtalk_y; + + vl53l1_info("xtalk is set to %d,%d,%d\n", xtalk_offset, xtalk_x, xtalk_y); +finish: + mutex_unlock(&data->work_mutex); + return rc ? rc : count; +} + +/** + * sysfs attribute "xtalk" [rd/wr] + * + * To read/write xtalk calibration data + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(xtalk, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_xtalk, + stmvl53l1_store_xtalk); + +static struct attribute *stmvl53l1_attributes[] = { + &dev_attr_enable_ps_sensor.attr, + &dev_attr_set_delay_ms.attr, + &dev_attr_timing_budget.attr, + &dev_attr_roi.attr, + &dev_attr_mode.attr, + &dev_attr_do_flush.attr, + &dev_attr_distance_mode.attr, + &dev_attr_crosstalk_enable.attr, + &dev_attr_enable_debug.attr, + &dev_attr_output_mode.attr, + &dev_attr_force_device_on_enable.attr, + &dev_attr_autonomous_config.attr, + &dev_attr_last_error.attr, + &dev_attr_offset_correction_mode.attr, + &dev_attr_optical_center.attr, + &dev_attr_dmax_reflectance.attr, + &dev_attr_dmax_mode.attr, + &dev_attr_tuning.attr, + &dev_attr_tuning_status.attr, + &dev_attr_smudge_correction_mode.attr, + &dev_attr_is_xtalk_value_changed.attr, + &dev_attr_enable_sar.attr, + &dev_attr_offset.attr, + &dev_attr_xtalk.attr, + NULL +}; + +static const struct attribute_group stmvl53l1_attr_group = { + .attrs = stmvl53l1_attributes, +}; + +static ssize_t stmvl53l1_calib_data_read(struct file *filp, + struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct stmvl53l1_data *data = dev_get_drvdata(dev); + VL53L1_CalibrationData_t calib; + int rc; + void *src = (void *) &calib; + + mutex_lock(&data->work_mutex); + + vl53l1_dbgmsg("off = %llu / count = %lu", off, count); + + /* sanity check */ + if (off < 0 || off > sizeof(VL53L1_CalibrationData_t)) + goto invalid; + + /* got current calibration data */ + memset(&calib, 0, sizeof(calib)); + rc = VL53L1_GetCalibrationData(&data->stdev, &calib); + if (rc) { + vl53l1_errmsg("VL53L1_GetCalibrationData fail %d", rc); + rc = store_last_error(data, rc); + goto error; + } + + /* copy to buffer */ + if (off + count > sizeof(VL53L1_CalibrationData_t)) + count = sizeof(VL53L1_CalibrationData_t) - off; + memcpy(buf, src + off, count); + + mutex_unlock(&data->work_mutex); + + return count; + +invalid: + vl53l1_errmsg("invalid syntax"); + rc = -EINVAL; + goto error; + +error: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static ssize_t stmvl53l1_calib_data_write(struct file *filp, + struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct stmvl53l1_data *data = dev_get_drvdata(dev); + VL53L1_CalibrationData_t *cali_data = (VL53L1_CalibrationData_t *)buf; + int rc; + + mutex_lock(&data->work_mutex); + + vl53l1_dbgmsg("off = %llu / count = %lu", off, count); + + if (data->enable_sensor) { + rc = -EBUSY; + vl53l1_errmsg("can't set calib data while ranging\n"); + goto error; + } + + /* we only support one time write */ + if (off != 0 || count != sizeof(VL53L1_CalibrationData_t)) { + vl53l1_errmsg("calib data size not match: count=%lu\n", count); + goto invalid; + } + + rc = VL53L1_SetCalibrationData(&data->stdev, cali_data); + if (rc) { + vl53l1_errmsg("VL53L1_SetCalibrationData fail %d", rc); + rc = store_last_error(data, rc); + goto error; + } + + if (!data->cali_data) + data->cali_data = kmalloc(sizeof(VL53L1_CalibrationData_t), GFP_KERNEL); + if (data->cali_data) + memcpy(data->cali_data, cali_data, sizeof(VL53L1_CalibrationData_t)); + + data->xtalk_offset = cali_data->customer.algo__crosstalk_compensation_plane_offset_kcps; + data->xtalk_x = cali_data->customer.algo__crosstalk_compensation_x_plane_gradient_kcps; + data->xtalk_y = cali_data->customer.algo__crosstalk_compensation_y_plane_gradient_kcps; + + mutex_unlock(&data->work_mutex); + + return count; + +invalid: + vl53l1_errmsg("invalid syntax"); + rc = -EINVAL; + goto error; + +error: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static struct bin_attribute stmvl53l1_calib_data_attr = { + .attr = { + .name = "calibration_data", + .mode = 0660/*S_IWUGO | S_IRUGO*/, + }, + .size = sizeof(VL53L1_CalibrationData_t), + .read = stmvl53l1_calib_data_read, + .write = stmvl53l1_calib_data_write, +}; + +static ssize_t stmvl53l1_zone_calib_data_read(struct file *filp, + struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int rc; + void *src = (void *) &data->calib.data; + + mutex_lock(&data->work_mutex); + + vl53l1_dbgmsg("off = %llu / count = %lu", off, count); + + /* sanity check */ + if (off < 0 || off > sizeof(stmvl531_zone_calibration_data_t)) + goto invalid; + + /* got current zone calibration data */ + rc = VL53L1_GetZoneCalibrationData(&data->stdev, + &data->calib.data.data); + if (rc) { + vl53l1_errmsg("VL53L1_GetZoneCalibrationData fail %d", rc); + rc = store_last_error(data, rc); + goto error; + } + data->calib.data.id = data->current_roi_id; + + /* copy to buffer */ + if (off + count > sizeof(stmvl531_zone_calibration_data_t)) + count = sizeof(stmvl531_zone_calibration_data_t) - off; + memcpy(buf, src + off, count); + + mutex_unlock(&data->work_mutex); + + return count; + +invalid: + vl53l1_errmsg("invalid syntax"); + rc = -EINVAL; + goto error; + +error: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static ssize_t stmvl53l1_zone_calib_data_write(struct file *filp, + struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int rc; + void *dst = &data->calib.data; + + mutex_lock(&data->work_mutex); + + vl53l1_dbgmsg("off = %llu / count = %lu", off, count); + + /* implementation if quite fragile. We suppose successive access. We + * trigger set on last byte write if amount is exact. + */ + if (off < 0 || off > sizeof(stmvl531_zone_calibration_data_t)) + goto invalid; + if (off + count > sizeof(stmvl531_zone_calibration_data_t)) + goto invalid; + + memcpy(dst + off, buf, count); + if (off + count == sizeof(stmvl531_zone_calibration_data_t)) { + vl53l1_dbgmsg("trigger zone calib setting"); + rc = VL53L1_SetZoneCalibrationData(&data->stdev, + &data->calib.data.data); + if (rc) { + vl53l1_errmsg("VL53L1_SetZoneCalibrationData fail %d", + rc); + rc = store_last_error(data, rc); + goto error; + } + data->current_roi_id = data->calib.data.id; + } + + mutex_unlock(&data->work_mutex); + + return count; + +invalid: + vl53l1_errmsg("invalid syntax"); + rc = -EINVAL; + goto error; + +error: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static struct bin_attribute stmvl53l1_zone_calib_data_attr = { + .attr = { + .name = "zone_calibration_data", + .mode = 0660/*S_IWUGO | S_IRUGO*/, + }, + .size = sizeof(stmvl531_zone_calibration_data_t), + .read = stmvl53l1_zone_calib_data_read, + .write = stmvl53l1_zone_calib_data_write, +}; + +static int ctrl_reg_access(struct stmvl53l1_data *data, void *p) +{ + struct stmvl53l1_register reg; + size_t total_byte; + int rc; + + if (data->is_device_remove) + return -ENODEV; + + total_byte = offsetof(struct stmvl53l1_register, data.b); + if (copy_from_user(®, p, total_byte)) { + vl53l1_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + + if (reg.cnt > STMVL53L1_MAX_CCI_XFER_SZ) { + vl53l1_errmsg("reg len %d > size limit\n", reg.cnt); + return -EINVAL; + } + + total_byte = offsetof(struct stmvl53l1_register, data.bytes[reg.cnt]); + /* for write get the effective data part of the structure */ + if (!reg.is_read) { + if (copy_from_user(®, p, total_byte)) { + vl53l1_errmsg(" data cpy fail\n"); + return -EFAULT; + } + } + + /* put back to user only needed amount of data */ + if (!reg.is_read) { + rc = VL53L1_WriteMulti(&data->stdev, (uint16_t)reg.index, + reg.data.bytes, reg.cnt); + reg.status = rc; + /* for write only write back status no data */ + total_byte = offsetof(struct stmvl53l1_register, data.b); + vl53l1_dbgmsg("wr %x %d bytes statu %d\n", + reg.index, reg.cnt, rc); + if (rc) + rc = store_last_error(data, rc); + } else { + rc = VL53L1_ReadMulti(&data->stdev, (uint16_t)reg.index, + reg.data.bytes, reg.cnt); + reg.status = rc; + vl53l1_dbgmsg("rd %x %d bytes status %d\n", + reg.index, reg.cnt, rc); + /* if fail do not copy back data only status */ + if (rc) { + total_byte = offsetof(struct stmvl53l1_register, + data.b); + rc = store_last_error(data, rc); + } + /* else the total byte is already the full pay-load with data */ + } + + if (copy_to_user(p, ®, total_byte)) { + vl53l1_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + return rc; +} + + +/* + * + */ +static int ctrl_start(struct stmvl53l1_data *data) +{ + int rc; + + mutex_lock(&data->work_mutex); + + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + + vl53l1_dbgmsg(" state = %d\n", data->enable_sensor); + + /* turn on tof sensor only if it's not already started */ + if (data->enable_sensor == 0 && !data->is_calibrating) { + /* to start */ + rc = stmvl53l1_start(data); + } else{ + rc = -EBUSY; + } + vl53l1_dbgmsg(" final state = %d\n", data->enable_sensor); + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +/** + * no lock version of ctrl_stop (mutex shall be held) + * + * @warning exist only for use in device exit to ensure "locked and started" + * may also beuse in soem erro handling when mutex is already locked + * @return 0 on success and was running >0 if already off <0 on error + */ +static int _ctrl_stop(struct stmvl53l1_data *data) +{ + int rc; + + vl53l1_dbgmsg("enter state = %d\n", data->enable_sensor); + /* be sure waiters are woken */ + data->is_data_valid = true; + /* turn on tof sensor only if it's not enabled by other client */ + if (data->enable_sensor == 1) { + /* to stop */ + rc = stmvl53l1_stop(data); + } else { + vl53l1_dbgmsg("already off did nothing\n"); + rc = 0; + } + stmvl53l1_insert_flush_events_lock(data); + vl53l1_dbgmsg(" final state = %d\n", data->enable_sensor); + + return rc; +} + +/** + * get work lock and stop sensor + * + * see @ref _ctrl_stop + * + * @param data device + * @return 0 on success EBUSY if arleady off + */ +static int ctrl_stop(struct stmvl53l1_data *data) +{ + int rc; + + mutex_lock(&data->work_mutex); + + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + if (data->enable_sensor) { + rc = _ctrl_stop(data); + + if (data->cam_mode == 1) { + /* only restore sar mode when + it quits from camera mode */ + data->cam_mode = 0; + + if (data->sar_mode == 1) { + vl53l1_info("restore sar mode\n"); + activate_sar_mode(data); + } + } + } else + rc = -EBUSY; + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +/** + * get work lock and suspend sensor without checking sar mode + * + * @param data device + * @return 0 on success EBUSY if arleady off + */ +static int ctrl_suspend(struct stmvl53l1_data *data) +{ + int rc; + + mutex_lock(&data->work_mutex); + vl53l1_info("enter ctrl_suspend\n"); + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + if (data->enable_sensor) + rc = _ctrl_stop(data); + else + rc = -EBUSY; + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static int ctrl_getdata(struct stmvl53l1_data *data, void __user *p) +{ + int rc; + + mutex_lock(&data->work_mutex); + + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + rc = copy_to_user(p, + &data->meas.single_range_data, + sizeof(stmvl531_range_data_t)); + if (rc) { + vl53l1_dbgmsg("copy to user fail %d", rc); + rc = -EFAULT; + } + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static bool is_new_data_for_me(struct stmvl53l1_data *data, pid_t pid, + struct list_head *head) +{ + return data->is_data_valid && !is_pid_in_list(pid, head); +} + +static bool sleep_for_data_condition(struct stmvl53l1_data *data, pid_t pid, + struct list_head *head) +{ + bool res; + + mutex_lock(&data->work_mutex); + res = is_new_data_for_me(data, pid, head); + mutex_unlock(&data->work_mutex); + + return res; +} + +static int sleep_for_data(struct stmvl53l1_data *data, pid_t pid, + struct list_head *head) +{ + int rc; + + mutex_unlock(&data->work_mutex); + rc = wait_event_killable(data->waiter_for_data, + sleep_for_data_condition(data, pid, head)); + mutex_lock(&data->work_mutex); + + return data->enable_sensor ? rc : -ENODEV; +} + +static int ctrl_getdata_blocking(struct stmvl53l1_data *data, void __user *p) +{ + int rc = 0; + pid_t pid = current->pid; + + mutex_lock(&data->work_mutex); + /* If device not ranging then exit on error */ + if (data->is_device_remove || !data->enable_sensor) { + rc = -ENODEV; + goto done; + } + /* sleep if data already read */ + if (!is_new_data_for_me(data, pid, &data->simple_data_reader_list)) + rc = sleep_for_data(data, pid, &data->simple_data_reader_list); + if (rc) + goto done; + + /* unless we got interrupted we return data to user and note read */ + rc = copy_to_user(p, &data->meas.single_range_data, + sizeof(stmvl531_range_data_t)); + if (rc) + goto done; + rc = add_reader(pid, &data->simple_data_reader_list); + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static int ctrl_mz_data_common(struct stmvl53l1_data *data, void __user *p, + bool is_additional) +{ + struct stmvl53l1_data_with_additional __user *d = p; + int rc; + + mutex_lock(&data->work_mutex); + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + rc = copy_to_user(&d->data, &data->meas.multi_range_data, + sizeof(VL53L1_MultiRangingData_t)); + if (rc) { + vl53l1_dbgmsg("copy to user fail %d", rc); + rc = -EFAULT; + goto done; + } + if (is_additional) { + rc = copy_to_user(&d->additional_data, + &data->meas.additional_data, + sizeof(VL53L1_AdditionalData_t)); + if (rc) { + vl53l1_dbgmsg("copy to user fail %d", rc); + rc = -EFAULT; + goto done; + } + } + if (!data->enable_sensor) + rc = -ENODEV; + else if (!is_mz_mode(data)) + rc = -ENOEXEC; + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static int ctrl_mz_data_blocking_common(struct stmvl53l1_data *data, + void __user *p, bool is_additional) +{ + int rc = 0; + struct stmvl53l1_data_with_additional __user *d = p; + pid_t pid = current->pid; + + mutex_lock(&data->work_mutex); + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + /* mode 2 or 3 */ + if (!is_mz_mode(data)) { + rc = -ENOEXEC; + goto done; + } + /* If device not ranging then exit on error */ + if (!data->enable_sensor) { + rc = -ENODEV; + goto done; + } + /* sleep if data already read */ + if (!is_new_data_for_me(data, pid, &data->mz_data_reader_list)) { + rc = sleep_for_data(data, pid, &data->mz_data_reader_list); + } + if (rc) + goto done; + + /* unless we got interrupted we return data to user and note read */ + rc = copy_to_user(&d->data, &data->meas.multi_range_data, + sizeof(VL53L1_MultiRangingData_t)); + if (rc) + goto done; + if (is_additional) { + rc = copy_to_user(&d->additional_data, + &data->meas.additional_data, + sizeof(VL53L1_AdditionalData_t)); + if (rc) + goto done; + } + rc = add_reader(pid, &data->mz_data_reader_list); + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +/** + * Get multi zone data + * @param data + * @param p [out] user ptr to @ref VL53L1_MultiRangingData_t structure\n + * is always set but on EFAULT error + * + * @return + * @li 0 on success + * @li ENODEV if not ranging + * @li ENOEXEC not in multi zone mode + * @li EFAULT copy to user error + */ +static int ctrl_mz_data(struct stmvl53l1_data *data, void __user *p) +{ + return ctrl_mz_data_common(data, p, false); +} + +static int ctrl_mz_data_blocking(struct stmvl53l1_data *data, void __user *p) +{ + return ctrl_mz_data_blocking_common(data, p, false); +} + +/** + * Get multi zone data with histogram debug data + * @param data + * @param p [out] user ptr to @ref struct stmvl53l1_data_with_additional + * structure is always set but on EFAULT error + * + * @return + * @li 0 on success + * @li ENODEV if not ranging + * @li ENOEXEC not in multi zone mode + * @li EFAULT copy to user error + */ +static int ctrl_mz_data_additional(struct stmvl53l1_data *data, void __user *p) +{ + return ctrl_mz_data_common(data, p, true); +} + +static int ctrl_mz_data_blocking_additional(struct stmvl53l1_data *data, + void __user *p) +{ + return ctrl_mz_data_blocking_common(data, p, true); +} + +static int ctrl_param_last_error(struct stmvl53l1_data *data, + struct stmvl53l1_parameter *param) +{ + int rc; + + if (param->is_read) { + param->value = data->last_error; + param->status = 0; + vl53l1_dbgmsg("get last error %d", param->value); + rc = 0; + } else { + rc = -EINVAL; + } + + return rc; +} + +static int ctrl_param_optical_center(struct stmvl53l1_data *data, + struct stmvl53l1_parameter *param) +{ + if (!param->is_read) + return -EINVAL; + + param->value = data->optical_offset_x; + param->value2 = data->optical_offset_y; + + return 0; +} + +static int ctrl_param_dmax_reflectance(struct stmvl53l1_data *data, + struct stmvl53l1_parameter *param) +{ + int rc; + + if (param->is_read) { + param->value = data->dmax_reflectance; + param->status = 0; + vl53l1_dbgmsg("get dmax reflectance %d", param->value); + rc = 0; + } else { + rc = stmvl53l1_set_dmax_reflectance(data, param->value); + vl53l1_dbgmsg("rc %d req %d now %d", rc, + param->value, data->dmax_reflectance); + } + + return rc; +} + +static int ctrl_param_tuning(struct stmvl53l1_data *data, + struct stmvl53l1_parameter *param) +{ + if (param->is_read) + return -EINVAL; + + return stmvl53l1_set_tuning(data, param->value, param->value2); +} + +static int ctrl_param_is_xtalk_value_changed(struct stmvl53l1_data *data, + struct stmvl53l1_parameter *param) +{ + if (!param->is_read) + return -EINVAL; + + param->value = data->is_xtalk_value_changed; + + return 0; +} + +static int ctrl_param_camera_mode(struct stmvl53l1_data *data, + struct stmvl53l1_parameter *param) +{ + if (param->is_read) { + param->value = data->cam_mode; + param->status = 0; + vl53l1_dbgmsg("get camera mode %d", param->value); + } else { + data->cam_mode = param->value; + vl53l1_dbgmsg("set camera mode %d", param->value); + } + + return 0; +} + +static int ctrl_param_hw_rev(struct stmvl53l1_data *data, + struct stmvl53l1_parameter *param) +{ + int rc; + + if (param->is_read) { + param->value = data->hw_rev; + param->status = 0; + vl53l1_dbgmsg("get hw rev %d", param->value); + rc = 0; + } else { + rc = -EINVAL; + } + + return rc; +} + +/** + * handle ioctl set param mode + * + * @param data + * @param p + * @return 0 on success + */ +static int ctrl_params(struct stmvl53l1_data *data, void __user *p) +{ + int rc, rc2; + struct stmvl53l1_parameter param; + + mutex_lock(&data->work_mutex); + + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + rc = copy_from_user(¶m, p, sizeof(param)); + param.status = 0; + if (rc) { + rc = -EFAULT; + goto done; /* no need for status in user struct */ + } + switch (param.name) { + case VL53L1_POLLDELAY_PAR: + rc = ctrl_param_poll_delay_ms(data, ¶m); + break; + case VL53L1_TIMINGBUDGET_PAR: + rc = ctrl_param_timing_budget(data, ¶m); + break; + case VL53L1_DEVICEMODE_PAR: + rc = ctrl_param_preset_mode(data, ¶m); + break; + case VL53L1_DISTANCEMODE_PAR: + rc = ctrl_param_distance_mode(data, ¶m); + break; + case VL53L1_XTALKENABLE_PAR: + rc = ctrl_param_crosstalk_enable(data, ¶m); + break; + case VL53L1_OUTPUTMODE_PAR: + rc = ctrl_param_output_mode(data, ¶m); + break; + case VL53L1_FORCEDEVICEONEN_PAR: + rc = ctrl_param_force_device_on_en(data, ¶m); + break; + case VL53L1_LASTERROR_PAR: + rc = ctrl_param_last_error(data, ¶m); + break; + case VL53L1_OFFSETCORRECTIONMODE_PAR: + rc = ctrl_param_offset_correction_mode(data, ¶m); + break; + case VL53L1_OPTICALCENTER_PAR: + rc = ctrl_param_optical_center(data, ¶m); + break; + case VL53L1_DMAXREFLECTANCE_PAR: + rc = ctrl_param_dmax_reflectance(data, ¶m); + break; + case VL53L1_DMAXMODE_PAR: + rc = ctrl_param_dmax_mode(data, ¶m); + break; + case VL53L1_TUNING_PAR: + rc = ctrl_param_tuning(data, ¶m); + break; + case VL53L1_SMUDGECORRECTIONMODE_PAR: + rc = ctrl_param_smudge_correction_mode(data, ¶m); + break; + case VL53L1_ISXTALKVALUECHANGED_PAR: + rc = ctrl_param_is_xtalk_value_changed(data, ¶m); + break; + case VL53L1_CAMERAMODE_PAR: + rc = ctrl_param_camera_mode(data, ¶m); + break; + case VL53L1_HWREV_PAR: + rc = ctrl_param_hw_rev(data, ¶m); + break; + default: + vl53l1_errmsg("unknown or unsupported %d\n", param.name); + rc = -EINVAL; + } + /* copy back (status at least ) to user */ + if (param.is_read && rc == 0) { + rc2 = copy_to_user(p, ¶m, sizeof(param)); + if (rc2) { + rc = -EFAULT; /* kill prev status if that fail */ + vl53l1_errmsg("copy to user fail %d\n", rc); + } + } +done: + mutex_unlock(&data->work_mutex); + return rc; +} + +/** + * implement set/get roi ioctl + * @param data device + * @param p user space ioctl arg ptr + * @return 0 on success <0 errno code + * @li -EINVAL invalid number of roi + * @li -EBUSY when trying to set roi while ranging + * @li -EFAULT if cpy to/fm user fail for requested number of roi + */ +static int ctrl_roi(struct stmvl53l1_data *data, void __user *p) +{ + int rc; + int roi_cnt; + struct stmvl53l1_roi_full_t rois; + + mutex_lock(&data->work_mutex); + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + /* copy fixed part of args at first */ + rc = copy_from_user(&rois, p, + offsetof(struct stmvl53l1_roi_full_t, + roi_cfg.UserRois[0])); + if (rc) { + rc = -EFAULT; + goto done; + } + /* check no of roi limit is ok */ + roi_cnt = rois.roi_cfg.NumberOfRoi; + if (roi_cnt > VL53L1_MAX_USER_ZONES) { + vl53l1_errmsg("invalid roi spec cnt=%d > %d", + rois.roi_cfg.NumberOfRoi, + VL53L1_MAX_USER_ZONES); + rc = -EINVAL; + goto done; + } + + if (rois.is_read) { + int cpy_size; + + roi_cnt = MIN(rois.roi_cfg.NumberOfRoi, + data->roi_cfg.NumberOfRoi); + cpy_size = offsetof(VL53L1_RoiConfig_t, UserRois[roi_cnt]); + /* copy from local to user only effective part requested */ + rc = copy_to_user(&((struct stmvl53l1_roi_full_t *)p)->roi_cfg, + &data->roi_cfg, cpy_size); + vl53l1_dbgmsg("return %d of %d\n", roi_cnt, + data->roi_cfg.NumberOfRoi); + } else { + /* SET check cnt roi is ok */ + if (data->enable_sensor) { + rc = -EBUSY; + vl53l1_errmsg("can't set roi while ranging\n"); + goto done; + } + /* get full data that required from user */ + rc = copy_from_user(&rois, p, + offsetof(struct stmvl53l1_roi_full_t, + roi_cfg.UserRois[roi_cnt])); + if (rc) { + vl53l1_errmsg("get %d roi fm user fail", roi_cnt); + rc = -EFAULT; + goto done; + } + dump_roi(data->roi_cfg.UserRois, data->roi_cfg.NumberOfRoi); + /* we may ask ll driver to check but check is mode dependent + * and so we could get erroneous error back + */ + memcpy(&data->roi_cfg, &rois.roi_cfg, sizeof(data->roi_cfg)); + } +done: + mutex_unlock(&data->work_mutex); + return rc; +} + +static int ctrl_autonomous_config(struct stmvl53l1_data *data, void __user *p) +{ + int rc = 0; + struct stmvl53l1_autonomous_config_t full; + + mutex_lock(&data->work_mutex); + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + /* first copy all data */ + rc = copy_from_user(&full, p, sizeof(full)); + if (rc) { + rc = -EFAULT; + goto done; + } + + if (full.is_read) { + full.pollingTimeInMs = data->auto_pollingTimeInMs; + full.config = data->auto_config; + rc = copy_to_user(p, &full, sizeof(full)); + if (rc) + rc = -EFAULT; + } else { + if (data->enable_sensor) { + rc = -EBUSY; + vl53l1_errmsg("can't change config while ranging\n"); + goto done; + } + data->auto_pollingTimeInMs = full.pollingTimeInMs; + data->auto_config = full.config; + } + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static int ctrl_autonomous_config_cam(struct stmvl53l1_data *data, + void __user *p) +{ + int rc = 0; + struct stmvl53l1_autonomous_config_t full; + + mutex_lock(&data->work_mutex); + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + /* first copy all data */ + rc = copy_from_user(&full, p, sizeof(full)); + if (rc) { + rc = -EFAULT; + goto done; + } + + if (full.is_read) { + full.pollingTimeInMs = data->auto_pollingTimeInMs_cam; + full.config = data->auto_config_cam; + rc = copy_to_user(p, &full, sizeof(full)); + if (rc) + rc = -EFAULT; + } else { + if (data->enable_sensor) { + rc = -EBUSY; + vl53l1_errmsg("can't change config while ranging\n"); + goto done; + } + data->auto_pollingTimeInMs_cam = full.pollingTimeInMs; + data->auto_config_cam = full.config; + } + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static int ctrl_calibration_data(struct stmvl53l1_data *data, void __user *p) +{ + int rc; + struct stmvl53l1_ioctl_calibration_data_t calib; + int data_offset = offsetof(struct stmvl53l1_ioctl_calibration_data_t, + data); + + mutex_lock(&data->work_mutex); + + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + rc = copy_from_user(&calib, p, data_offset); + if (rc) { + vl53l1_errmsg("fail to detect read or write %d", rc); + rc = -EFAULT; + goto done; + } + + if (calib.is_read) { + memset(&calib.data, 0, sizeof(calib.data)); + rc = VL53L1_GetCalibrationData(&data->stdev, &calib.data); + if (rc) { + vl53l1_errmsg("VL53L1_GetCalibrationData fail %d", rc); + rc = store_last_error(data, rc); + goto done; + } + rc = copy_to_user(p + data_offset, &calib.data, + sizeof(calib.data)); + } else { + if (data->enable_sensor) { + rc = -EBUSY; + vl53l1_errmsg("can't set calib data while ranging\n"); + goto done; + } + rc = copy_from_user(&calib.data, p + data_offset, + sizeof(calib.data)); + if (rc) { + vl53l1_errmsg("fail to copy calib data"); + rc = -EFAULT; + goto done; + } + rc = VL53L1_SetCalibrationData(&data->stdev, &calib.data); + if (rc) { + vl53l1_errmsg("VL53L1_SetCalibrationData fail %d", rc); + rc = store_last_error(data, rc); + } + } + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static int ctrl_zone_calibration_data(struct stmvl53l1_data *data, + void __user *p) +{ + int rc; + struct stmvl53l1_ioctl_zone_calibration_data_t *calib; + int data_offset = + offsetof(struct stmvl53l1_ioctl_zone_calibration_data_t, data); + + mutex_lock(&data->work_mutex); + + calib = &data->calib; + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + rc = copy_from_user(calib, p, data_offset); + if (rc) { + vl53l1_errmsg("fail to detect read or write %d", rc); + rc = -EFAULT; + goto done; + } + + if (calib->is_read) { + memset(&calib->data, 0, sizeof(calib->data)); + rc = VL53L1_GetZoneCalibrationData(&data->stdev, + &calib->data.data); + if (rc) { + vl53l1_errmsg("VL53L1_GetZoneCalibrationData fail %d", + rc); + rc = store_last_error(data, rc); + goto done; + } + calib->data.id = data->current_roi_id; + rc = copy_to_user(p + data_offset, &calib->data, + sizeof(calib->data)); + } else { + if (data->enable_sensor) { + rc = -EBUSY; + vl53l1_errmsg("can't set calib data while ranging\n"); + goto done; + } + rc = copy_from_user(&calib->data, p + data_offset, + sizeof(calib->data)); + if (rc) { + vl53l1_errmsg("fail to copy calib data"); + rc = -EFAULT; + goto done; + } + rc = VL53L1_SetZoneCalibrationData(&data->stdev, + &calib->data.data); + if (rc) { + vl53l1_errmsg("VL53L1_SetZoneCalibrationData fail %d", + rc); + rc = store_last_error(data, rc); + goto done; + } + data->current_roi_id = calib->data.id; + } + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static int ctrl_perform_calibration_ref_spad_lock(struct stmvl53l1_data *data, + struct stmvl53l1_ioctl_perform_calibration_t *calib) +{ + int rc = VL53L1_PerformRefSpadManagement(&data->stdev); + + if (rc) { + vl53l1_errmsg("VL53L1_PerformRefSpadManagement fail => %d", rc); + rc = store_last_error(data, rc); + } + + return rc; +} + +static int ctrl_perform_calibration_crosstalk_lock(struct stmvl53l1_data *data, + struct stmvl53l1_ioctl_perform_calibration_t *calib) +{ + int rc = 0; + + /* Set the preset mode passed on param2 */ + data->preset_mode = (VL53L1_PresetModes)(calib->param2); + + rc = stmvl53l1_sendparams(data); + if (rc) + goto done; + + rc = VL53L1_PerformXTalkCalibration(&data->stdev, calib->param1); + if (rc) { + vl53l1_errmsg("VL53L1_PerformXTalkCalibration fail => %d", rc); + rc = store_last_error(data, rc); + } + +done: + return rc; +} + +static int ctrl_perform_zone_calibration_offset_lock( + struct stmvl53l1_data *data, + struct stmvl53l1_ioctl_perform_calibration_t *calib) +{ + int rc; + + /* first sanity check */ + if (calib->param1 != VL53L1_OFFSETCALIBRATIONMODE_MULTI_ZONE) { + vl53l1_errmsg("invalid param1"); + rc = -EINVAL; + goto done; + } + + /* setup offset calibration mode */ + rc = VL53L1_SetOffsetCalibrationMode(&data->stdev, calib->param1); + if (rc) { + vl53l1_errmsg("VL53L1_SetOffsetCalibrationMode fail => %d", rc); + rc = store_last_error(data, rc); + goto done; + } + + /* set mode to multi zone to allow roi settings */ + rc = VL53L1_SetPresetMode(&data->stdev, + VL53L1_PRESETMODE_MULTIZONES_SCANNING); + if (rc) { + vl53l1_errmsg("VL53L1_SetPresetMode fail => %d", rc); + rc = store_last_error(data, rc); + goto done; + } + + /* setup roi */ + rc = VL53L1_SetROI(&data->stdev, &data->roi_cfg); + if (rc) { + vl53l1_errmsg("VL53L1_SetROI fail => %d", rc); + rc = store_last_error(data, rc); + goto done; + } + + /* finally perform calibration */ + /* allow delay add after stop in VL53L1_run__calibration */ + data->is_delay_allowed = 1; + rc = VL53L1_PerformOffsetCalibration(&data->stdev, calib->param2, + calib->param3); + data->is_delay_allowed = 0; + if (rc) { + vl53l1_errmsg("VL53L1_PerformOffsetCalibration fail => %d", rc); + rc = store_last_error(data, rc); + } + + /* save roi hash for later use */ + data->current_roi_id = stmvl53l1_compute_hash(&data->roi_cfg); + +done: + return rc; +} + +static int ctrl_perform_calibration_offset_lock(struct stmvl53l1_data *data, + struct stmvl53l1_ioctl_perform_calibration_t *calib) +{ + int rc; + + /* for legacy purpose we still support mz */ + if (calib->param1 == VL53L1_OFFSETCALIBRATIONMODE_MULTI_ZONE) + return ctrl_perform_zone_calibration_offset_lock(data, calib); + + /* setup offset calibration mode */ + rc = VL53L1_SetOffsetCalibrationMode(&data->stdev, calib->param1); + if (rc) { + vl53l1_errmsg("VL53L1_SetOffsetCalibrationMode fail => %d", rc); + rc = store_last_error(data, rc); + goto done; + } + + /* finally perform calibration */ + /* allow delay add after stop in VL53L1_run__calibration */ + data->is_delay_allowed = 1; + rc = VL53L1_PerformOffsetCalibration(&data->stdev, calib->param2, + calib->param3); + data->is_delay_allowed = 0; + if (rc) { + vl53l1_errmsg("VL53L1_PerformOffsetCalibration fail => %d", rc); + rc = store_last_error(data, rc); + } + +done: + return rc; +} + +static int ctrl_perform_simple_calibration_offset_lock( + struct stmvl53l1_data *data, + struct stmvl53l1_ioctl_perform_calibration_t *calib) +{ + int rc; + + /* finally perform calibration */ + /* allow delay add after stop in VL53L1_run__calibration */ + data->is_delay_allowed = 1; + + rc = stmvl53l1_sendparams(data); + if (rc) + goto done; + + rc = VL53L1_PerformOffsetSimpleCalibration(&data->stdev, calib->param1); + data->is_delay_allowed = 0; + if (rc) { + vl53l1_errmsg( + "VL53L1_PerformOffsetSimpleCalibration fail => %d", rc); + rc = store_last_error(data, rc); + } + +done: + return rc; +} + +static int ctrl_perform_calibration(struct stmvl53l1_data *data, void __user *p) +{ + int rc; + struct stmvl53l1_ioctl_perform_calibration_t calib; + + mutex_lock(&data->work_mutex); + + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + data->is_calibrating = true; + rc = copy_from_user(&calib, p, sizeof(calib)); + if (rc) { + rc = -EFAULT; + goto done; + } + if (data->enable_sensor) { + rc = -EBUSY; + vl53l1_errmsg("can't perform calibration while ranging\n"); + goto done; + } + + rc = reset_release(data); + if (rc) + goto done; + + rc = VL53L1_StaticInit(&data->stdev); + if (rc) { + vl53l1_errmsg("VL53L1_StaticInit fail => %d", rc); + rc = store_last_error(data, rc); + goto done; + } + + switch (calib.calibration_type) { + case VL53L1_CALIBRATION_REF_SPAD: + rc = ctrl_perform_calibration_ref_spad_lock(data, + &calib); + break; + case VL53L1_CALIBRATION_CROSSTALK: + rc = ctrl_perform_calibration_crosstalk_lock(data, + &calib); + break; + case VL53L1_CALIBRATION_OFFSET: + rc = ctrl_perform_calibration_offset_lock(data, + &calib); + break; + case VL53L1_CALIBRATION_OFFSET_SIMPLE: + rc = ctrl_perform_simple_calibration_offset_lock(data, + &calib); + break; + case VL53L1_CALIBRATION_OFFSET_PER_ZONE: + rc = ctrl_perform_zone_calibration_offset_lock(data, + &calib); + break; + default: + rc = -EINVAL; + break; + } + + reset_hold(data); + +done: + data->is_calibrating = false; + data->is_first_start_done = true; + mutex_unlock(&data->work_mutex); + + return rc; +} + +static int stmvl53l1_ioctl_handler( + struct stmvl53l1_data *data, + unsigned int cmd, unsigned long arg, + void __user *p) +{ + int rc = 0; + + if (!data) + return -EINVAL; + + switch (cmd) { + + case VL53L1_IOCTL_START: + vl53l1_dbgmsg("VL53L1_IOCTL_START\n"); + rc = ctrl_start(data); + break; + + case VL53L1_IOCTL_STOP: + vl53l1_dbgmsg("VL53L1_IOCTL_STOP\n"); + rc = ctrl_stop(data); + break; + + case VL53L1_IOCTL_SUSPEND: + vl53l1_dbgmsg("VL53L1_IOCTL_SUSPEND\n"); + rc = ctrl_suspend(data); + break; + + case VL53L1_IOCTL_GETDATAS: + /* vl53l1_dbgmsg("VL53L1_IOCTL_GETDATAS\n"); */ + rc = ctrl_getdata(data, p); + break; + + case VL53L1_IOCTL_GETDATAS_BLOCKING: + /* vl53l1_dbgmsg("VL53L1_IOCTL_GETDATAS_BLOCKING\n"); */ + rc = ctrl_getdata_blocking(data, p); + break; + + /* Register tool */ + case VL53L1_IOCTL_REGISTER: + vl53l1_dbgmsg("VL53L1_IOCTL_REGISTER\n"); + rc = ctrl_reg_access(data, p); + break; + + case VL53L1_IOCTL_PARAMETER: + vl53l1_dbgmsg("VL53L1_IOCTL_PARAMETER\n"); + rc = ctrl_params(data, p); + break; + + case VL53L1_IOCTL_ROI: + vl53l1_dbgmsg("VL53L1_IOCTL_ROI\n"); + rc = ctrl_roi(data, p); + break; + case VL53L1_IOCTL_MZ_DATA: + /* vl53l1_dbgmsg("VL53L1_IOCTL_MZ_DATA\n"); */ + rc = ctrl_mz_data(data, p); + break; + case VL53L1_IOCTL_MZ_DATA_BLOCKING: + /* vl53l1_dbgmsg("VL53L1_IOCTL_MZ_DATA_BLOCKING\n"); */ + rc = ctrl_mz_data_blocking(data, p); + break; + case VL53L1_IOCTL_CALIBRATION_DATA: + vl53l1_dbgmsg("VL53L1_IOCTL_CALIBRATION_DATA\n"); + rc = ctrl_calibration_data(data, p); + break; + case VL53L1_IOCTL_PERFORM_CALIBRATION: + vl53l1_dbgmsg("VL53L1_IOCTL_PERFORM_CALIBRATION\n"); + rc = ctrl_perform_calibration(data, p); + break; + case VL53L1_IOCTL_AUTONOMOUS_CONFIG: + vl53l1_dbgmsg("VL53L1_IOCTL_AUTONOMOUS_CONFIG\n"); + if (data->cam_mode == 0) + rc = ctrl_autonomous_config(data, p); + else + rc = ctrl_autonomous_config_cam(data, p); + break; + case VL53L1_IOCTL_ZONE_CALIBRATION_DATA: + vl53l1_dbgmsg("VL53L1_IOCTL_ZONE_CALIBRATION_DATA\n"); + rc = ctrl_zone_calibration_data(data, p); + break; + case VL53L1_IOCTL_MZ_DATA_ADDITIONAL: + /* vl53l1_dbgmsg("VL53L1_IOCTL_MZ_DATA_ADDITIONAL\n"); */ + rc = ctrl_mz_data_additional(data, p); + break; + case VL53L1_IOCTL_MZ_DATA_ADDITIONAL_BLOCKING: + /* vl53l1_dbgmsg("VL53L1_IOCTL_MZ_DATA_ADDITIONAL_BLOCKING\n"); + */ + rc = ctrl_mz_data_blocking_additional(data, p); + break; + default: + rc = -EINVAL; + break; + } + + return rc; +} + + + +static int stmvl53l1_open(struct inode *inode, struct file *file) +{ + struct stmvl53l1_data *data = container_of(file->private_data, + struct stmvl53l1_data, miscdev); + + vl53l1_dbgmsg("Start\n"); + stmvl53l1_module_func_tbl.get(data->client_object); + vl53l1_dbgmsg("End\n"); + + return 0; +} + +static int stmvl53l1_release(struct inode *inode, struct file *file) +{ + struct stmvl53l1_data *data = container_of(file->private_data, + struct stmvl53l1_data, miscdev); + + vl53l1_dbgmsg("Start\n"); + stmvl53l1_module_func_tbl.put(data->client_object); + vl53l1_dbgmsg("End\n"); + + return 0; +} + + +/** max number or error per measure too abort */ +#define stvm531_get_max_meas_err(...) 3 +/** max number or error per stream too abort */ +#define stvm531_get_max_stream_err(...) 6 + +static void detect_xtalk_value_change(struct stmvl53l1_data *data, + VL53L1_MultiRangingData_t *meas) +{ + data->is_xtalk_value_changed = meas->HasXtalkValueChanged ? true : + data->is_xtalk_value_changed; +} + +/** + * handle data retrieval and dispatch + * + * work lock must be held + * + * called form work or interrupt thread it must be a blocable context ! + * @param data the device + */ +static void stmvl53l1_on_newdata_event(struct stmvl53l1_data *data) +{ + int rc; + VL53L1_RangingMeasurementData_t *pmsinglerange; + VL53L1_MultiRangingData_t *pmrange; + VL53L1_MultiRangingData_t *tmprange; + VL53L1_TargetRangeData_t RangeData[VL53L1_MAX_RANGE_RESULTS]; + VL53L1_RangingMeasurementData_t singledata; + long ts_msec; + int i; + + do_gettimeofday(&data->meas.comp_tv); + ts_msec = stmvl53l1_tv_dif(&data->start_tv, &data->meas.comp_tv)/1000; + + pmrange = &data->meas.multi_range_data; + tmprange = &data->meas.tmp_range_data; + pmsinglerange = &data->meas.single_range_data; + + memcpy(&singledata, pmsinglerange, + sizeof(VL53L1_RangingMeasurementData_t)); + for (i = 0; i < VL53L1_MAX_RANGE_RESULTS; i++) + memcpy(&RangeData[i], &pmrange->RangeData[i], + sizeof(VL53L1_TargetRangeData_t)); + + data->meas.intr++; + + /* use get data method based on active mode */ + switch (data->preset_mode) { + case VL53L1_PRESETMODE_LITE_RANGING: + case VL53L1_PRESETMODE_AUTONOMOUS: + case VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS: + rc = VL53L1_GetRangingMeasurementData(&data->stdev, + pmsinglerange); + break; + case VL53L1_PRESETMODE_RANGING: + case VL53L1_PRESETMODE_MULTIZONES_SCANNING: + /* IMPORTANT : during VL53L1_GetMultiRangingData() call + * work_mutex is release during ipp. This is why we use + * tmp_range_data which is not access somewhere else. When we are + * back we then copy tmp_range_data in multi_range_data. + */ + + rc = VL53L1_GetMultiRangingData(&data->stdev, + &data->meas.tmp_range_data); + + /* be sure we got VL53L1_RANGESTATUS_NONE for object 0 if we got + * invalid roi or no object. So if client read data using + * VL53L1_IOCTL_GETDATAS we got correct status. + */ + if (tmprange->RoiStatus == VL53L1_ROISTATUS_NOT_VALID || + tmprange->NumberOfObjectsFound == 0) + tmprange->RangeData[0].RangeStatus = + VL53L1_RANGESTATUS_NONE; + + memcpy(pmrange, tmprange, sizeof(VL53L1_MultiRangingData_t)); + + /* got histogram debug data in case user want it later on */ + if (!rc) + rc = VL53L1_GetAdditionalData(&data->stdev, + &data->meas.additional_data); + detect_xtalk_value_change(data, pmrange); + break; + default: + /* that must be some bug or data corruption stop now */ + rc = -1; + vl53l1_errmsg("unsorted mode %d=>stop\n", data->preset_mode); + _ctrl_stop(data); + } + /* check if not stopped yet + * as we may have been unlocked we must re-check + */ + if (data->enable_sensor == 0) { + vl53l1_dbgmsg("at meas #%d we got stopped\n", data->meas.cnt); + return; + } + if (rc) { + vl53l1_errmsg("VL53L1_GetRangingMeasurementData @%d %d", + __LINE__, rc); + data->meas.err_cnt++; + data->meas.err_tot++; + if (data->meas.err_cnt > stvm531_get_max_meas_err(data) || + data->meas.err_tot > stvm531_get_max_stream_err(data)) { + vl53l1_errmsg("on #%d %d err %d tot stop", + data->meas.cnt, data->meas.err_cnt, + data->meas.err_tot); + _ctrl_stop(data); + } + return; + } + + /* FIXME: remove when implemented by ll or bare driver */ + pmrange->TimeStamp = ts_msec; + pmsinglerange->TimeStamp = ts_msec; + for (i = 1; i < pmrange->NumberOfObjectsFound; i++) + pmrange->TimeStamp = ts_msec; + + data->meas.cnt++; + vl53l1_dbgmsg("#%3d %2d poll ts %5d status=%d obj cnt=%d\n", + data->meas.cnt, + data->meas.poll_cnt, + pmrange->TimeStamp, + pmrange->RangeData[0].RangeStatus, + pmrange->NumberOfObjectsFound); +#if 0 + vl53l1_dbgmsg( +"meas m#%04d i#%04d p#%04d in %d ms data range status %d range %d\n", + (int)data->meas.cnt, + (int)data->meas.intr, + (int)data->meas.poll_cnt, + (int)stmvl53l1_tv_dif(&data->meas.start_tv, + &data->meas.comp_tv)/1000, + (int)data->meas.range_data.RangeStatus, + (int)data->meas.range_data.RangeMilliMeter); +#endif + /* ready that is not always on each new data event */ + + /* mark data as valid from now */ + data->is_data_valid = true; + + /* wake up sleeping client */ + wake_up_data_waiters(data); + + /* push data to input subsys and only and make val for ioctl*/ + stmvl53l1_input_push_data(data); + stmvl53l1_insert_flush_events_lock(data); + + /* roll time now data got used */ + data->meas.start_tv = data->meas.comp_tv; + data->meas.poll_cnt = 0; + data->meas.err_cnt = 0; +} + + +/** + * * handle interrupt/pusdo irq by polling handling + * + * work lock must be held + * + * @param data driver + * @return 0 on success + */ +static int stmvl53l1_intr_process(struct stmvl53l1_data *data) +{ + uint8_t data_rdy; + int rc = 0; + struct timeval tv_now; + + if (!data->enable_sensor) + goto done; + + data->meas.poll_cnt++; + rc = VL53L1_GetMeasurementDataReady(&data->stdev, &data_rdy); + if (rc) { + vl53l1_errmsg("GetMeasurementDataReady @%d %d, fail\n", + __LINE__, rc); + /* too many successive fail => stop but do not try to do any new + * i/o + */ + goto stop_io; + } + + if (!data_rdy) { + /* FIXME this part to completely skip + * if using interrupt and sure we have + * no false interrupt to handle or no to do any timing check + */ + long poll_us; + + do_gettimeofday(&tv_now); + poll_us = stmvl53l1_tv_dif(&data->meas.start_tv, &tv_now); + if (poll_us > data->timing_budget*4) { + vl53l1_errmsg("we're polling %ld ms too long\n", + poll_us/1000); + /* fixme stop or just warn ? */ + goto stop_io; + } + /* keep trying it could be intr with no processing */ + work_dbg("intr with no data rdy"); + goto done; + } + /* we have data to handle */ + /* first irq after reset has no data so we skip it */ + if (data->is_first_irq) { + data->is_first_irq = false; + + if (data->preset_mode == + VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS) { + /* + * If VL53L1_GetRangingMeasurementData() + * is not called after + * for the first ranging measurement, + * the thresholds do not seem + * to work for ALP mode + */ + VL53L1_RangingMeasurementData_t RangingMeasurementData; + + /* printk("Test Workaround for ALP mode\n"); */ + VL53L1_GetRangingMeasurementData(&data->stdev, + &RangingMeasurementData); + } + + } else + stmvl53l1_on_newdata_event(data); + /* enable_sensor could change on event handling check again */ + if (data->enable_sensor) { + /* clear interrupt and continue ranging */ + work_dbg("intr clr"); + /* In autonomous mode, bare driver will trigger stop/start + * sequence. In that case it wall call platform delay functions. + * So allow delay in VL53L1_ClearInterruptAndStartMeasurement() + * call. + */ + data->is_delay_allowed = data->allow_hidden_start_stop; + rc = VL53L1_ClearInterruptAndStartMeasurement(&data->stdev); + data->is_delay_allowed = 0; + if (rc) { + /* go to stop but stop any new i/o for dbg */ + vl53l1_errmsg("Cltr intr restart fail %d\n", rc); + goto stop_io; + } + } +done: + return rc; +stop_io: + /* too many successive fail take action => stop but do not try to do + * any new i/o + */ + vl53l1_errmsg("GetDatardy fail stop\n"); + _ctrl_stop(data); + return rc; + +} + +static void stmvl53l1_work_handler(struct work_struct *work) +{ + struct stmvl53l1_data *data; + + data = container_of(work, struct stmvl53l1_data, dwork.work); + work_dbg("enter"); + mutex_lock(&data->work_mutex); + stmvl53l1_intr_process(data); + if (data->poll_mode && data->enable_sensor) { + /* re-sched ourself */ + schedule_delayed_work(&data->dwork, + msecs_to_jiffies(data->poll_delay_ms)); + } + mutex_unlock(&data->work_mutex); +} + +static void stmvl53l1_input_push_data_singleobject(struct stmvl53l1_data *data) +{ + struct input_dev *input = data->input_dev_ps; + VL53L1_RangingMeasurementData_t *meas = &data->meas.single_range_data; + FixPoint1616_t LimitCheckCurrent; + VL53L1_Error st = VL53L1_ERROR_NONE; + + vl53l1_dbgmsg("******* FIXME!!! ************\n"); + vl53l1_dbgmsg("Sensor HAL in Lite ranging mode not yet updated\n"); + vl53l1_dbgmsg("******* FIXME!!! ************\n"); + + /* Do not send the events till this if fixed properly */ + //return; + + /* set the distance value to max if range + * status is invalid in autonomous mode */ + if (data->preset_mode == VL53L1_PRESETMODE_AUTONOMOUS && + meas->RangeStatus != VL53L1_RANGESTATUS_RANGE_VALID) + meas->RangeMilliMeter = STMVL53L1_MAX_DISTANCE; + + input_report_abs(input, ABS_DISTANCE, (meas->RangeMilliMeter + 5) / 10); + input_report_abs(input, ABS_HAT0X, meas->TimeStamp / 1000); + input_report_abs(input, ABS_HAT0Y, (meas->TimeStamp % 1000) * 1000); + input_report_abs(input, ABS_HAT1X, meas->RangeMilliMeter); + input_report_abs(input, ABS_HAT1Y, meas->RangeStatus); + input_report_abs(input, ABS_HAT2X, meas->SignalRateRtnMegaCps); + input_report_abs(input, ABS_HAT2Y, meas->AmbientRateRtnMegaCps); + input_report_abs(input, ABS_HAT3X, meas->SigmaMilliMeter); + st = VL53L1_GetLimitCheckCurrent(&data->stdev, + VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, &LimitCheckCurrent); + if (st == VL53L1_ERROR_NONE) + input_report_abs(input, ABS_WHEEL, LimitCheckCurrent); + input_report_abs(input, ABS_MISC, meas->EffectiveSpadRtnCount); + input_report_abs(input, ABS_TOOL_WIDTH, meas->RangeQualityLevel); + + input_sync(input); +} + +static void stmvl53l1_input_push_data_multiobject(struct stmvl53l1_data *data) +{ + VL53L1_MultiRangingData_t *mmeas = &data->meas.multi_range_data; + int i; + int rc; + VL53L1_TargetRangeData_t *meas_array[4]; + VL53L1_CalibrationData_t calibration_data; + struct timeval tv; + struct input_dev *input = data->input_dev_ps; + + do_gettimeofday(&tv); + + for (i = 0; i < 4; i++) + meas_array[i] = &mmeas->RangeData[i]; + + /************************************************************* + * INPUT EVENT CODE L1/L3 Data + ABS_HAT0X Time in Sec(32) + ABS_HAT0Y Time in uSec(32) + ABS_HAT1X Obj0_Distance(16) : Obj0_Sigma(16) + ABS_HAT1Y Obj0_MinRange(16) : Obj0_MaxRange(16) + ABS_HAT2X Obj1_Distance(16) : Obj1_Sigma(16) + ABS_HAT2Y Obj1_ MinRange (16) : Obj1_ MaxRange (16) + ABS_HAT3X Obj0_SignalRate_Spad(32) + ABS_HAT3Y Obj1_SignalRate_Spad(32) + ABS_WHEEL AmbientRate(32) + ABS_BRAKE EffectiveSpadRtnCount(16):RangeStatus_1(8): + Range_status_0(8) + ABS_TILT_X XtalkChange(8) :StreamCount(8) : + NumberofObjects(2) : RoiNumber(4) : + RoiStatus(2) + ABS_TILT_Y DMAX + ABS_TOOL_WIDTH XtalkValue + ABS_DISTANCE + ABS_THROTTLE + ABS_RUDDER + ABS_MISC + ABS_VOLUME + ************************************************************/ + + rc = VL53L1_GetCalibrationData(&data->stdev, &calibration_data); + if (rc) { + //This should not happen + vl53l1_errmsg("%d error:%d\n", __LINE__, rc); + return; + } + + //ABS_HAT0X - Time in Sec(32) + + input_report_abs(input, ABS_HAT0X, tv.tv_sec); +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 5, 0) + vl53l1_dbgmsg("ABS_HAT0X : %lld, %zu\n", tv.tv_sec, sizeof(tv.tv_sec)); +#else + vl53l1_dbgmsg("ABS_HAT0X : %ld, %zu\n", tv.tv_sec, sizeof(tv.tv_sec)); +#endif + //ABS_HAT0Y - Time in uSec(32) + //REVISIT : The following code may cause loss of data due to + //8 bytes to 32 bits conversion + input_report_abs(input, ABS_HAT0Y, tv.tv_usec); + vl53l1_dbgmsg("ABS_HAT0Y : %ld\n", tv.tv_usec); + + //ABS_WHEEL - AmbientRate(32) + input_report_abs(input, ABS_WHEEL, + meas_array[0]->AmbientRateRtnMegaCps); + vl53l1_dbgmsg("ABS_WHEEL : AmbRate = %d\n", + meas_array[0]->AmbientRateRtnMegaCps); + + //ABS_TILT_X XtalkChange(8) :StreamCount(8) : + //Number of Objects(2) : RoiNumber(4) : RoiStatus(2) + input_report_abs(input, ABS_TILT_X, + (mmeas->HasXtalkValueChanged << 16) + | (mmeas->StreamCount << 8) + | ((mmeas->NumberOfObjectsFound & 0x3) << 6) + | ((mmeas->RoiNumber & 0xF) << 2) + | (mmeas->RoiStatus & 0x3)); + vl53l1_dbgmsg("ABS_TILT_X :(%d):(%d):(%d):(%d):(%d)\n\n", + mmeas->HasXtalkValueChanged, + mmeas->StreamCount, + mmeas->NumberOfObjectsFound, + mmeas->RoiNumber, + mmeas->RoiStatus + ); + + + //ABS_TILT_Y DMAX + input_report_abs(input, ABS_TILT_Y, mmeas->DmaxMilliMeter); + vl53l1_dbgmsg("ABS_TILT_Y DMAX = %d\n", mmeas->DmaxMilliMeter); + + //ABS_TOOL_WIDTH + input_report_abs(input, ABS_TOOL_WIDTH, +calibration_data.customer.algo__crosstalk_compensation_plane_offset_kcps); + vl53l1_dbgmsg("ABS_TOOL_WIDTH Xtalk = %d\n", +calibration_data.customer.algo__crosstalk_compensation_plane_offset_kcps); + + + if (mmeas->NumberOfObjectsFound == 0) { + input_sync(input); + return; + } + + //ABS_HAT1X - Obj0_Distance(16) : Obj0_Sigma(16) + input_report_abs(input, ABS_HAT1X, meas_array[0]->RangeMilliMeter << 16 + | (meas_array[0]->SigmaMilliMeter/65536)); + vl53l1_dbgmsg("ABS_HAT1X : 0x%X(%d:%d)\n", + meas_array[0]->RangeMilliMeter << 16 + | (meas_array[0]->SigmaMilliMeter/65536), + meas_array[0]->RangeMilliMeter, + (meas_array[0]->SigmaMilliMeter/65536)); + + //ABS_HAT1Y - Obj0_MinRange(16) : Obj0_MaxRange(16) + input_report_abs(input, ABS_HAT1Y, + meas_array[0]->RangeMinMilliMeter << 16 + | meas_array[0]->RangeMaxMilliMeter); + + vl53l1_dbgmsg("ABS_HAT1Y : 0x%X(%d:%d)\n", + meas_array[0]->RangeMinMilliMeter << 16 + | meas_array[0]->RangeMaxMilliMeter, + meas_array[0]->RangeMinMilliMeter, + meas_array[0]->RangeMaxMilliMeter); + + if (mmeas->NumberOfObjectsFound > 1) { + //ABS_HAT2X - Obj1_Distance(16) : Obj1_Sigma(16) + input_report_abs(input, ABS_HAT2X, + meas_array[1]->RangeMilliMeter << 16 + | (meas_array[1]->SigmaMilliMeter/65536)); + vl53l1_dbgmsg("ABS_HAT2X : 0x%x(%d:%d)\n", + meas_array[1]->RangeMilliMeter << 16 + | (meas_array[1]->SigmaMilliMeter/65536), + meas_array[1]->RangeMilliMeter, + (meas_array[1]->SigmaMilliMeter/65536)); + + //ABS_HAT2Y - Obj1_ MinRange (16) : Obj1_ MaxRange (16) + input_report_abs(input, ABS_HAT2Y, + meas_array[1]->RangeMinMilliMeter << 16 + | meas_array[1]->RangeMaxMilliMeter); + + vl53l1_dbgmsg("ABS_HAT1Y : 0x%X(%d:%d)\n", + meas_array[1]->RangeMinMilliMeter << 16 + | meas_array[1]->RangeMaxMilliMeter, + meas_array[1]->RangeMinMilliMeter, + meas_array[1]->RangeMaxMilliMeter); + + } + + // ABS_HAT3X - Obj0_SignalRate_Spad(32) + input_report_abs(input, ABS_HAT3X, + meas_array[0]->SignalRateRtnMegaCps); + vl53l1_dbgmsg("ABS_HAT3X : SignalRateRtnMegaCps_0(%d)\n", + meas_array[0]->SignalRateRtnMegaCps); + if (mmeas->NumberOfObjectsFound > 1) { + //ABS_HAT3Y - Obj1_SignalRate_Spad(32) + input_report_abs(input, ABS_HAT3Y, + meas_array[1]->SignalRateRtnMegaCps); + vl53l1_dbgmsg("ABS_HAT3Y : SignalRateRtnMegaCps_1(%d)\n", + meas_array[1]->SignalRateRtnMegaCps); + } + + //ABS_BRAKE - EffectiveSpadRtnCount(16):RangeStatus_3(1): + //Range_status_2(0) + input_report_abs(input, ABS_BRAKE, + mmeas->EffectiveSpadRtnCount << 16 + | ((meas_array[1]->RangeStatus) << 8) + | meas_array[0]->RangeStatus); + + vl53l1_dbgmsg("ABS_BRAKE : (%d):(%d):(%d)\n", + mmeas->EffectiveSpadRtnCount, + meas_array[1]->RangeStatus, + meas_array[0]->RangeStatus); + + vl53l1_dbgmsg("ABS_BRAKE : 0x%X\n", + (mmeas->EffectiveSpadRtnCount & 0xFFFF) << 16 + | ((meas_array[1]->RangeStatus) << 8) + | meas_array[0]->RangeStatus); + + + input_sync(input); + + +} + +static void stmvl53l1_input_push_data(struct stmvl53l1_data *data) +{ + /* use get data method based on active mode */ + switch (data->preset_mode) { + case VL53L1_PRESETMODE_LITE_RANGING: + case VL53L1_PRESETMODE_AUTONOMOUS: + case VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS: + stmvl53l1_input_push_data_singleobject(data); + break; + default: + /* VL53L1_PRESETMODE_RANGING: + * VL53L1_PRESETMODE_MULTIZONES_SCANNING: + */ + stmvl53l1_input_push_data_multiobject(data); + } +} + +static int stmvl53l1_input_setup(struct stmvl53l1_data *data) +{ + int rc; + struct input_dev *idev; + /* Register to Input Device */ + idev = input_allocate_device(); + if (idev == NULL) { + rc = -ENOMEM; + vl53l1_errmsg("%d error:%d\n", __LINE__, rc); + goto exit_err; + } + /* setup all event */ + set_bit(EV_ABS, idev->evbit); + + input_set_abs_params(idev, ABS_DISTANCE, 0, 0xff, 0, 0); + + input_set_abs_params(idev, ABS_HAT0X, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_HAT0Y, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_HAT1X, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_HAT1Y, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_HAT2X, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_HAT2Y, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_HAT3X, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_HAT3Y, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_WHEEL, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_MISC, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_BRAKE, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_TILT_X, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_TOOL_WIDTH, 0, 0xffffffff, 0, 0); + + input_set_abs_params(idev, ABS_THROTTLE, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_RUDDER, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_MISC, 0, 0xffffffff, 0, 0); + + input_set_abs_params(idev, ABS_VOLUME, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_GAS, 0, 0xffffffff, 0, 0); + + idev->name = "STM VL53L1 proximity sensor"; + rc = input_register_device(idev); + if (rc) { + rc = -ENOMEM; + vl53l1_errmsg("%d error:%d\n", __LINE__, rc); + goto exit_free_dev_ps; + } + /* setup drv data */ + input_set_drvdata(idev, data); + data->input_dev_ps = idev; + return 0; + + +exit_free_dev_ps: + input_free_device(data->input_dev_ps); +exit_err: + return rc; +} + +/** + * handler to be called by interface module on interrupt + * + * managed poll/irq filtering in case poll/irq can be soft forced + * and the module side still fire interrupt + * + * @param data + * @return 0 if all ok else for error + */ +int stmvl53l1_intr_handler(struct stmvl53l1_data *data) +{ + int rc; + + mutex_lock(&data->work_mutex); + + /* handle it only if if we are not stopped */ + if (data->enable_sensor) { + rc = stmvl53l1_intr_process(data); + } else { + /* it's likely race/last unhandled interrupt after + * stop. + * Such dummy irq also occured during offset and crosstalk + * calibration procedures. + */ + vl53l1_dbgmsg("got intr but not on (dummy or calibration)\n"); + rc = 0; + } + + mutex_unlock(&data->work_mutex); + return rc; +} + + +/** + * One time device setup + * + * call by bus (i2c/cci) level probe to finalize non bus related device setup + * + * @param data The device data + * @return 0 on success + */ +int stmvl53l1_setup(struct stmvl53l1_data *data) +{ + int rc = 0; + VL53L1_CalibrationData_t cali_data; + VL53L1_DeviceInfo_t dev_info; + + vl53l1_dbgmsg("Enter\n"); + + data->hw_rev = 0; + + /* acquire an id */ + data->id = allocate_dev_id(); + if (data->id < 0) { + vl53l1_errmsg("too many device already created"); + return -1; + } + vl53l1_dbgmsg("Dev id %d is @%p\n", data->id, data); + stmvl53l1_dev_table[data->id] = data; + + /* init mutex */ + /* mutex_init(&data->update_lock); */ + mutex_init(&data->work_mutex); + + /* init work handler */ + INIT_DELAYED_WORK(&data->dwork, stmvl53l1_work_handler); + + /* init ipp side */ + stmvl53l1_ipp_setup(data); + + if (data->force_device_on_en_default) + force_device_on_en_default = true; + data->force_device_on_en = force_device_on_en_default; + data->reset_state = 1; + data->is_calibrating = false; + data->last_error = VL53L1_ERROR_NONE; + data->is_device_remove = false; + + /* vdd will be controlled in reset_release() */ + rc = reset_release(data); + if (rc) + goto exit_ipp_cleanup; + + rc = stmvl53l1_input_setup(data); + if (rc) + goto exit_ipp_cleanup; + + /* init blocking ioctl stuff */ + INIT_LIST_HEAD(&data->simple_data_reader_list); + INIT_LIST_HEAD(&data->mz_data_reader_list); + init_waitqueue_head(&data->waiter_for_data); + data->is_data_valid = false; + + /* Register sysfs hooks under input dev */ + rc = sysfs_create_group(&data->input_dev_ps->dev.kobj, + &stmvl53l1_attr_group); + if (rc) { + rc = -ENOMEM; + vl53l1_errmsg("%d error:%d\n", __LINE__, rc); + goto exit_unregister_dev_ps; + } + rc = sysfs_create_bin_file(&data->input_dev_ps->dev.kobj, + &stmvl53l1_calib_data_attr); + if (rc) { + rc = -ENOMEM; + vl53l1_errmsg("%d error:%d\n", __LINE__, rc); + goto exit_unregister_dev_ps; + } + rc = sysfs_create_bin_file(&data->input_dev_ps->dev.kobj, + &stmvl53l1_zone_calib_data_attr); + if (rc) { + rc = -ENOMEM; + vl53l1_errmsg("%d error:%d\n", __LINE__, rc); + goto exit_unregister_dev_ps; + } + + kobject_uevent_env(&data->input_dev_ps->dev.kobj, KOBJ_CHANGE, NULL); + + data->enable_sensor = 0; + + data->poll_delay_ms = STMVL53L1_CFG_POLL_DELAY_MS; + data->timing_budget = STMVL53L1_CFG_TIMING_BUDGET_US; + data->preset_mode = STMVL53L1_CFG_DEFAULT_MODE; + data->distance_mode = STMVL53L1_CFG_DEFAULT_DISTANCE_MODE; + data->crosstalk_enable = STMVL53L1_CFG_DEFAULT_CROSSTALK_ENABLE; + data->output_mode = STMVL53L1_CFG_DEFAULT_OUTPUT_MODE; + data->offset_correction_mode = + STMVL53L1_CFG_DEFAULT_OFFSET_CORRECTION_MODE; + data->cam_mode = 0; + data->sar_mode = 0; + stmvl53l1_setup_auto_config(data); + data->dmax_mode = STMVL53L1_CFG_DEFAULT_DMAX_MODE; + data->smudge_correction_mode = + STMVL53L1_CFG_DEFAULT_SMUDGE_CORRECTION_MODE; + data->current_roi_id = 0; + data->is_xtalk_value_changed = false; + + data->is_delay_allowed = true; + /* need to be done once */ + rc = VL53L1_DataInit(&data->stdev); + data->is_delay_allowed = false; + if (rc) { + vl53l1_errmsg("VL53L1_DataInit %d\n", rc); + goto exit_unregister_dev_ps; + } + + rc = VL53L1_GetDeviceInfo(&data->stdev, &dev_info); + if (rc) { + vl53l1_errmsg("VL53L1_GetDeviceInfo %d\n", rc); + goto exit_unregister_dev_ps; + } + vl53l1_errmsg("device name %s\ntype %s\n", + dev_info.Name, dev_info.Type); + + /* get managed data here */ + rc = VL53L1_GetDmaxReflectance(&data->stdev, &data->dmax_reflectance); + if (rc) { + vl53l1_errmsg("VL53L1_GetDmaxReflectance %d\n", rc); + goto exit_unregister_dev_ps; + } + rc = VL53L1_GetOpticalCenter(&data->stdev, &data->optical_offset_x, + &data->optical_offset_y); + if (rc) { + vl53l1_errmsg("VL53L1_GetOpticalCenter %d\n", rc); + goto exit_unregister_dev_ps; + } + + /* set tuning from stmvl53l1_tunings.h */ + rc = setup_tunings(data); + if (rc) { + vl53l1_errmsg("setup_tunings %d\n", rc); + goto exit_unregister_dev_ps; + } + + /* Set special parameters for VL53L3 (ticket 513812) */ + if (dev_info.ProductType == 0xAA) + { + data->timing_budget = 30000; + data->crosstalk_enable = 1; + data->dmax_mode = VL53L1_DMAXMODE_CUSTCAL_DATA; + data->smudge_correction_mode = VL53L1_SMUDGE_CORRECTION_CONTINUOUS; + data->dmax_reflectance = (5 << 16); + } + + data->hw_rev = dev_info.ProductRevisionMinor; + + memset(&cali_data, 0, sizeof(cali_data)); + rc = VL53L1_GetCalibrationData(&data->stdev, &cali_data); + if (rc) + vl53l1_errmsg("VL53L1_GetCalibrationData fail\n"); + else { + data->inner_offset = cali_data.customer.mm_config__inner_offset_mm; + data->outer_offset = cali_data.customer.mm_config__outer_offset_mm; + + /* update xtalk values */ + cali_data.customer.algo__crosstalk_compensation_plane_offset_kcps + = data->xtalk_offset; + cali_data.customer.algo__crosstalk_compensation_x_plane_gradient_kcps + = data->xtalk_x; + cali_data.customer.algo__crosstalk_compensation_y_plane_gradient_kcps + = data->xtalk_y; + + cali_data.xtalkhisto.xtalk_shape.VL53L1_p_022 = 0 ; + cali_data.xtalkhisto.xtalk_shape.VL53L1_p_023 = 24; + cali_data.xtalkhisto.xtalk_shape.VL53L1_p_024 = 12; + cali_data.xtalkhisto.xtalk_shape.bin_data[0] = 353; + cali_data.xtalkhisto.xtalk_shape.bin_data[1] = 472; + cali_data.xtalkhisto.xtalk_shape.bin_data[2] = 472; + cali_data.xtalkhisto.xtalk_shape.bin_data[3] = 353; + cali_data.xtalkhisto.xtalk_shape.bin_data[4] = 0; + cali_data.xtalkhisto.xtalk_shape.bin_data[5] = 0; + cali_data.xtalkhisto.xtalk_shape.bin_data[6] = 0; + cali_data.xtalkhisto.xtalk_shape.bin_data[7] = 0; + cali_data.xtalkhisto.xtalk_shape.bin_data[8] = 0; + cali_data.xtalkhisto.xtalk_shape.bin_data[9] = 0; + cali_data.xtalkhisto.xtalk_shape.bin_data[10] = 0; + cali_data.xtalkhisto.xtalk_shape.bin_data[11] = 0; + cali_data.xtalkhisto.xtalk_shape.phasecal_result__reference_phase = 10240; + cali_data.xtalkhisto.xtalk_shape.phasecal_result__vcsel_start = 6; + cali_data.xtalkhisto.xtalk_shape.cal_config__vcsel_start = 9; + cali_data.xtalkhisto.xtalk_shape.vcsel_width = 40; + cali_data.xtalkhisto.xtalk_shape.VL53L1_p_019 = 48332; + cali_data.xtalkhisto.xtalk_shape.zero_distance_phase = 4096; + cali_data.gain_cal.standard_ranging_gain_factor = 0x07D7; + cali_data.gain_cal.histogram_ranging_gain_factor = 0x07CC; + + rc = VL53L1_SetCalibrationData(&data->stdev, &cali_data); + if (rc) + vl53l1_errmsg("VL53L1_SetCalibrationData fail\n"); + else { + vl53l1_info("device crosstalk data updated: %u %d %d\n", + data->xtalk_offset, data->xtalk_x, data->xtalk_y); + } + } + + /* if working in interrupt ask intr to enable and hook the handler */ + data->poll_mode = 0; + rc = stmvl53l1_module_func_tbl.start_intr(data->client_object, + &data->poll_mode); + if (rc < 0) { + vl53l1_errmsg("can't start no intr\n"); + goto exit_unregister_dev_ps; + } + + data->is_first_irq = true; + data->is_first_start_done = false; + data->is_delay_allowed = false; + + /* to register as a misc device */ + data->miscdev.minor = MISC_DYNAMIC_MINOR; + /* multiple dev name use id in name but 1st */ + if (data->id == 0) + strcpy(data->name, VL53L1_MISC_DEV_NAME); + else + sprintf(data->name, "%s%d", VL53L1_MISC_DEV_NAME, data->id); + + data->miscdev.name = data->name; + data->miscdev.fops = &stmvl53l1_ranging_fops; + vl53l1_errmsg("Misc device registration name:%s\n", data->miscdev.name); + rc = misc_register(&data->miscdev); + if (rc != 0) { + vl53l1_errmsg("misc dev reg fail\n"); + goto exit_unregister_dev_ps; + } + /* bring back device under reset */ + reset_hold(data); + + return 0; + +exit_unregister_dev_ps: + sysfs_remove_bin_file(&data->input_dev_ps->dev.kobj, + &stmvl53l1_zone_calib_data_attr); + sysfs_remove_bin_file(&data->input_dev_ps->dev.kobj, + &stmvl53l1_calib_data_attr); + sysfs_remove_group(&data->input_dev_ps->dev.kobj, + &stmvl53l1_attr_group); + input_unregister_device(data->input_dev_ps); +exit_ipp_cleanup: + stmvl53l1_ipp_cleanup(data); + + return rc; +} + +/* Attribute: name (RO) */ +static ssize_t name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, VL53L1_INPUT_DEVICE_NAME); +} + +static struct device_attribute laser_name_attributes = __ATTR_RO(name); + +#define LASERDEV_MINOR_BASE 256 +#define LASERDEV_MINOR_MAX 32 + +int stmvl53l1_sysfs_laser(struct stmvl53l1_data *data, bool create) +{ + int i, error = 0; + static struct class *laser_class; + static struct device *laser_class_dev; + static int minor; + + if (data == NULL) { + vl53l1_errmsg("stmvl53l1 dev data is null!\n"); + return -ENODEV; + } + + if (create) { +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 5, 0) + dev_t devid; + error = alloc_chrdev_region(&devid, 0, 1, "laser"); + vl53l1_errmsg("MAJOR = %d MINOR = %d\n", MAJOR(devid),MINOR(devid)); + minor = MINOR(devid); + if (minor < 0) + vl53l1_errmsg("alloc_chrdev_region error\n"); +#else + minor = input_get_new_minor(data->sysfs_base, 1, false); + if (minor < 0) + minor = input_get_new_minor(LASERDEV_MINOR_BASE, + LASERDEV_MINOR_MAX, true); +#endif + vl53l1_info("assigned minor %d\n", minor); + + laser_class = class_create(THIS_MODULE, "laser"); + if (IS_ERR(laser_class)) { + error = PTR_ERR(laser_class); + laser_class = NULL; + return error; + } + + laser_class_dev = device_create(laser_class, NULL, + MKDEV(INPUT_MAJOR, minor), + data, STMVL53L1_DRV_NAME); + if (IS_ERR(laser_class_dev)) { + error = PTR_ERR(laser_class_dev); + laser_class_dev = NULL; + return error; + } + + for (i = 0; stmvl53l1_attributes[i] != NULL; ++i) { + error = sysfs_create_file(&laser_class_dev->kobj, + stmvl53l1_attributes[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + + error = device_create_file(laser_class_dev, + &laser_name_attributes); + if (error) + goto device_destroy; + + error = sysfs_create_bin_file(&laser_class_dev->kobj, + &stmvl53l1_calib_data_attr); + if (error) { + device_remove_file(laser_class_dev, + &laser_name_attributes); + goto device_destroy; + } + } else { + if (!laser_class || !laser_class_dev) + return -ENODEV; + + sysfs_remove_bin_file(&laser_class_dev->kobj, + &stmvl53l1_calib_data_attr); + device_remove_file(laser_class_dev, + &laser_name_attributes); + for (i = 0; stmvl53l1_attributes[i] != NULL; ++i) + sysfs_remove_file(&laser_class_dev->kobj, + stmvl53l1_attributes[i]); + + device_unregister(laser_class_dev); + class_unregister(laser_class); + } + + vl53l1_info("laser sysfs %s(0x%x) created\n", + STMVL53L1_DRV_NAME, data->sysfs_base); + return 0; + +device_destroy: + for (--i; i >= 0; --i) + sysfs_remove_file(&laser_class_dev->kobj, + stmvl53l1_attributes[i]); + device_destroy(laser_class, MKDEV(INPUT_MAJOR, minor)); + laser_class_dev = NULL; + class_unregister(laser_class); + vl53l1_errmsg("error creating laser class\n"); + + return -ENODEV; +} + +void stmvl53l1_cleanup(struct stmvl53l1_data *data) +{ + int rc; + + vl53l1_dbgmsg("enter\n"); + rc = _ctrl_stop(data); + if (rc < 0) + vl53l1_errmsg("stop failed %d aborting anyway\n", rc); + + if (data->input_dev_ps) { + vl53l1_dbgmsg("to remove sysfs group\n"); + sysfs_remove_group(&data->input_dev_ps->dev.kobj, + &stmvl53l1_attr_group); + sysfs_remove_bin_file(&data->input_dev_ps->dev.kobj, + &stmvl53l1_calib_data_attr); + sysfs_remove_bin_file(&data->input_dev_ps->dev.kobj, + &stmvl53l1_zone_calib_data_attr); + + vl53l1_dbgmsg("to unregister input dev\n"); + input_unregister_device(data->input_dev_ps); + } + + if (!IS_ERR(data->miscdev.this_device) && + data->miscdev.this_device != NULL) { + vl53l1_dbgmsg("to unregister misc dev\n"); + misc_deregister(&data->miscdev); + } + stmvl53l1_ipp_cleanup(data); + /* be sure device is put under reset */ + data->force_device_on_en = false; + reset_hold(data); + vl53l1_dbgmsg("done\n"); + deallocate_dev_id(data->id); + data->is_device_remove = true; + if (data->cali_data) { + kfree(data->cali_data); + data->cali_data = NULL; + } +} + +#ifdef CONFIG_PM_SLEEP +void stmvl53l1_pm_suspend_stop(struct stmvl53l1_data *data) +{ + int rc; + + vl53l1_dbgmsg("Enter\n"); + + rc = _ctrl_stop(data); + if (rc < 0) + vl53l1_errmsg("stop failed %d aborting anyway\n", rc); + + vl53l1_dbgmsg("done\n"); +} +#endif + +#ifdef CONFIG_COMPAT +static long stmvl53l1_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret; + struct stmvl53l1_data *data = + container_of(file->private_data, + struct stmvl53l1_data, miscdev); + ret = stmvl53l1_ioctl_handler(data, cmd, arg, compat_ptr(arg)); + return ret; +} +#endif + +static long stmvl53l1_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + long ret; + struct stmvl53l1_data *data = + container_of(file->private_data, + struct stmvl53l1_data, miscdev); + ret = stmvl53l1_ioctl_handler(data, cmd, arg, (void __user *)arg); + return ret; +} + +static int __init stmvl53l1_init(void) +{ + int rc = -1; + + vl53l1_dbgmsg("Enter\n"); + + rc = stmvl53l1_ipp_init(); + if (rc) + goto done; + /* i2c/cci client specific init function */ + rc = stmvl53l1_module_func_tbl.init(); + if (rc) + stmvl53l1_ipp_exit(); +done: + vl53l1_dbgmsg("End %d\n", rc); + + return rc; +} + +static void __exit stmvl53l1_exit(void) +{ + vl53l1_dbgmsg("Enter\n"); + stmvl53l1_module_func_tbl.deinit(NULL); + if (stmvl53l1_module_func_tbl.clean_up != NULL) + stmvl53l1_module_func_tbl.clean_up(); + stmvl53l1_ipp_exit(); + vl53l1_dbgmsg("End\n"); +} + + +/* MODULE_DEVICE_TABLE(i2c, stmvl53l1_id); */ +MODULE_AUTHOR("STMicroelectronics Imaging Division"); +MODULE_DESCRIPTION("ST FlightSense Time-of-Flight sensor driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); + +module_init(stmvl53l1_init); +module_exit(stmvl53l1_exit); diff --git a/drivers/input/misc/vl53L1/stmvl53l1_tunings.h b/drivers/input/misc/vl53L1/stmvl53l1_tunings.h new file mode 100644 index 000000000000..6a92a3b2c3c2 --- /dev/null +++ b/drivers/input/misc/vl53L1/stmvl53l1_tunings.h @@ -0,0 +1,42 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + +/* + * THIS IS A GENERATED FILE + */ + +#ifndef STMVL53L1_TUNINGS_H +#define STMVL53L1_TUNINGS_H + +static const int tunings[][2] = { +}; + +#endif /* STMVL53L1_TUNINGS_H */ diff --git a/drivers/input/misc/vl53L1/vl53l1_platform.h b/drivers/input/misc/vl53L1/vl53l1_platform.h new file mode 100644 index 000000000000..742e25aa66da --- /dev/null +++ b/drivers/input/misc/vl53L1/vl53l1_platform.h @@ -0,0 +1,444 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + + +#ifndef _VL53L1_PLATFORM_H_ +#define _VL53L1_PLATFORM_H_ + +#include "vl53l1_ll_def.h" +#include "vl53l1_platform_log.h" + +#define VL53L1_IPP_API +#include "vl53l1_platform_ipp_imports.h" +#include "vl53l1_platform_user_data.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @file vl53l1_platform.h + * + * @brief All end user OS/platform/application porting + */ + + + +/** + * @brief Initialise platform comms. + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] comms_type : selects between I2C and SPI + * @param[in] comms_speed_khz : unsigned short containing the I2C speed in kHz + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_CommsInitialise( + VL53L1_Dev_t *pdev, + uint8_t comms_type, + uint16_t comms_speed_khz); + + +/** + * @brief Close platform comms. + * + * @param[in] pdev : pointer to device structure (device handle) + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_CommsClose( + VL53L1_Dev_t *pdev); + + +/** + * @brief Writes the supplied byte buffer to the device + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] index : uint16_t register index value + * @param[in] pdata : pointer to uint8_t (byte) buffer containing the data + * to be written + * @param[in] count : number of bytes in the supplied byte buffer + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_WriteMulti( + VL53L1_Dev_t *pdev, + uint16_t index, + uint8_t *pdata, + uint32_t count); + + +/** + * @brief Reads the requested number of bytes from the device + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] index : uint16_t register index value + * @param[out] pdata : pointer to the uint8_t (byte) buffer to store read + * data + * @param[in] count : number of bytes to read + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_ReadMulti( + VL53L1_Dev_t *pdev, + uint16_t index, + uint8_t *pdata, + uint32_t count); + + +/** + * @brief Writes a single byte to the device + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] index : uint16_t register index value + * @param[in] data : uint8_t data value to write + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_WrByte( + VL53L1_Dev_t *pdev, + uint16_t index, + uint8_t data); + + +/** + * @brief Writes a single word (16-bit unsigned) to the device + * + * Manages the big-endian nature of the device register map + * (first byte written is the MS byte). + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] index : uint16_t register index value + * @param[in] data : uin16_t data value write + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_WrWord( + VL53L1_Dev_t *pdev, + uint16_t index, + uint16_t data); + + +/** + * @brief Writes a single dword (32-bit unsigned) to the device + * + * Manages the big-endian nature of the device register map + * (first byte written is the MS byte). + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] index : uint16_t register index value + * @param[in] data : uint32_t data value to write + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_WrDWord( + VL53L1_Dev_t *pdev, + uint16_t index, + uint32_t data); + + + +/** + * @brief Reads a single byte from the device + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] index : uint16_t register index + * @param[out] pdata : pointer to uint8_t data value + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + * + */ + +VL53L1_Error VL53L1_RdByte( + VL53L1_Dev_t *pdev, + uint16_t index, + uint8_t *pdata); + + +/** + * @brief Reads a single word (16-bit unsigned) from the device + * + * Manages the big-endian nature of the device (first byte read is the MS byte). + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] index : uint16_t register index value + * @param[out] pdata : pointer to uint16_t data value + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_RdWord( + VL53L1_Dev_t *pdev, + uint16_t index, + uint16_t *pdata); + + +/** + * @brief Reads a single dword (32-bit unsigned) from the device + * + * Manages the big-endian nature of the device (first byte read is the MS byte). + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] index : uint16_t register index value + * @param[out] pdata : pointer to uint32_t data value + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_RdDWord( + VL53L1_Dev_t *pdev, + uint16_t index, + uint32_t *pdata); + + + +/** + * @brief Implements a programmable wait in us + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] wait_us : integer wait in micro seconds + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_WaitUs( + VL53L1_Dev_t *pdev, + int32_t wait_us); + + +/** + * @brief Implements a programmable wait in ms + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] wait_ms : integer wait in milliseconds + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_WaitMs( + VL53L1_Dev_t *pdev, + int32_t wait_ms); + + +/** + * @brief Get the frequency of the timer used for ranging results time stamps + * + * @param[out] ptimer_freq_hz : pointer for timer frequency + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GetTimerFrequency(int32_t *ptimer_freq_hz); + +/** + * @brief Get the timer value in units of timer_freq_hz (see + * VL53L1_get_timestamp_frequency()) + * + * @param[out] ptimer_count : pointer for timer count value + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GetTimerValue(int32_t *ptimer_count); + + +/** + * @brief Set the mode of a specified GPIO pin + * + * @param pin - an identifier specifying the pin being modified - defined per + * platform + * + * @param mode - an identifier specifying the requested mode - defined per + * platform + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GpioSetMode(uint8_t pin, uint8_t mode); + + +/** + * @brief Set the value of a specified GPIO pin + * + * @param pin - an identifier specifying the pin being modified - defined per + * platform + * + * @param value - a value to set on the GPIO pin - typically 0 or 1 + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GpioSetValue(uint8_t pin, uint8_t value); + + +/** + * @brief Get the value of a specified GPIO pin + * + * @param pin - an identifier specifying the pin being modified - defined per + * platform + * + * @param pvalue - a value retrieved from the GPIO pin - typically 0 or 1 + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GpioGetValue(uint8_t pin, uint8_t *pvalue); + + +/** + * @brief Sets and clears the XShutdown pin on the Ewok + * + * @param value - the value for xshutdown - 0 = in reset, 1 = operational + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GpioXshutdown(uint8_t value); + + +/** + * @brief Sets and clears the Comms Mode pin (NCS) on the Ewok + * + * @param value - the value for comms select - 0 = I2C, 1 = SPI + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GpioCommsSelect(uint8_t value); + + +/** + * @brief Enables and disables the power to the Ewok module + * + * @param value - the state of the power supply - 0 = power off, 1 = power on + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GpioPowerEnable(uint8_t value); + +/** + * @brief Enables callbacks to the supplied funtion pointer when Ewok interrupts + * ocurr + * + * @param function - a function callback supplies by the caller, for interrupt + * notification + * @param edge_type - falling edge or rising edge interrupt detection + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GpioInterruptEnable(void (*function)(void), + uint8_t edge_type); + + +/** + * @brief Disables the callback on Ewok interrupts + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GpioInterruptDisable(void); + + +/* + * @brief Gets current system tick count in [ms] + * + * @return time_ms : current time in [ms] + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GetTickCount( + uint32_t *ptime_ms); + + +/** + * @brief Register "wait for value" polling routine + * + * Port of the V2WReg Script function WaitValueMaskEx() + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] timeout_ms : timeout in [ms] + * @param[in] index : uint16_t register index value + * @param[in] value : value to wait for + * @param[in] mask : mask to be applied before comparison with value + * @param[in] poll_delay_ms : polling delay been each read transaction in [ms] + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_WaitValueMaskEx( + VL53L1_Dev_t *pdev, + uint32_t timeout_ms, + uint16_t index, + uint8_t value, + uint8_t mask, + uint32_t poll_delay_ms); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1/vl53l1_platform_ipp.h b/drivers/input/misc/vl53L1/vl53l1_platform_ipp.h new file mode 100644 index 000000000000..b187272fe7b3 --- /dev/null +++ b/drivers/input/misc/vl53L1/vl53l1_platform_ipp.h @@ -0,0 +1,188 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + + +#ifndef _VL53L1_PLATFORM_IPP_H_ +#define _VL53L1_PLATFORM_IPP_H_ + +#include "vl53l1_ll_def.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @file vl53l1_platform_ipp.h + * + * @brief EwokPlus25 IPP Wrapper Functions + */ + +/** + * @brief IPP Wrapper call for histogram post processing + * + * + * @param[in] Dev : Device handle + * @param[in] pdmax_cal : DMAX calibration data + * @param[in] pdmax_cfg : DMAX configuration data + * @param[in] ppost_cfg : VL53L1_hist_post_process_config_t + * @param[in] pbins : Input histogram raw bin data + * @param[in] pxtalk : Cross talk histogram data + * @param[out] presults : Output VL53L1_range_results_t + * structure + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_ipp_hist_process_data( + VL53L1_DEV Dev, + VL53L1_dmax_calibration_data_t *pdmax_cal, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg, + VL53L1_hist_post_process_config_t *ppost_cfg, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_xtalk_histogram_data_t *pxtalk, + VL53L1_range_results_t *presults); + + +/** + * @brief IPP Wrapper call for histogram ambient dmax calc + * + * The target reflectance in percent for the DMAX calculation + * is set by target_reflectance input + * + * The fixed point format is 7.2 + * + * @param[in] Dev : Device handle + * @param[in] target_reflectance : target reflectance to report ambient DMAX + * Percentage in 7.2 fixed point format + * @param[in] pdmax_cal : DMAX calibration data + * @param[in] pdmax_cfg : DMAX configuration data + * @param[in] pbins : Input histogram raw bin data + * @param[out] pambient_dmax_mm : Output ambient DMAX distance in [mm] + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_ipp_hist_ambient_dmax( + VL53L1_DEV Dev, + uint16_t target_reflectance, + VL53L1_dmax_calibration_data_t *pdmax_cal, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg, + VL53L1_histogram_bin_data_t *pbins, + int16_t *pambient_dmax_mm); + + +/** + * @brief IPP Wrapper call for xtalk calibration post processing + * + * @param[in] Dev : Device handle + * @param[in] pxtalk_ranges : Input VL53L1_xtalk_range_results_t + * Must contain 5 ranges, 4 quadrants + 1 + * full FoV + * @param[out] pxtalk_shape : Output normalised Cross talk histogram + * shape + * @param[out] pxtalk_cal : Output VL53L1_xtalk_calibration_results_t + * structure + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_ipp_xtalk_calibration_process_data( + VL53L1_DEV Dev, + VL53L1_xtalk_range_results_t *pxtalk_ranges, + VL53L1_xtalk_histogram_data_t *pxtalk_shape, + VL53L1_xtalk_calibration_results_t *pxtalk_cal); + + +/** + * @brief IPP Wrapper call for applying histogram xtalk correction + * + * @param[in] Dev : Device handle + * @param[in] pcustomer : Pointer to input customer data structure + * @param[in] pdyn_cfg : Pointer to input dynamic parameters + * structure + * @param[in] pxtalk_shape : Pointer to input normalised xtalk + * histogram shape + * @param[in] pip_hist_data : Pointer to input histogram data struct + * @param[out] pop_hist_data : Pointer to output xtalk corrected + * histogram data struct + * @param[out] pxtalk_count_data : Pointer to output xtalk histogram + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_ipp_hist_xtalk_correction( + VL53L1_DEV Dev, + VL53L1_customer_nvm_managed_t *pcustomer, + VL53L1_dynamic_config_t *pdyn_cfg, + VL53L1_xtalk_histogram_data_t *pxtalk_shape, + VL53L1_histogram_bin_data_t *pip_hist_data, + VL53L1_histogram_bin_data_t *pop_hist_data, + VL53L1_histogram_bin_data_t *pxtalk_count_data); + +/** + * @brief IPP Wrapper call for Generating Xtalk data from dual reflectance + * histogram data + * + * @param[in] Dev : Device handle + * @param[in] pxtalk_results : Pointer to xtalk_results structure + * containing dual reflectance + * histogram data + * @param[in] expected_target_distance_mm : User input of true target distance + * @param[in] higher_reflectance : User input detailing which + * histogram data 1 or 2 has the + * highest reflectance. + * @param[out] pxtalk_avg_samples : Pointer to output xtalk histogram + * data + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_ipp_generate_dual_reflectance_xtalk_samples( + VL53L1_DEV Dev, + VL53L1_xtalk_range_results_t *pxtalk_results, + uint16_t expected_target_distance_mm, + uint8_t higher_reflectance, + VL53L1_histogram_bin_data_t *pxtalk_avg_samples); + + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1/vl53l1_platform_ipp_imports.h b/drivers/input/misc/vl53L1/vl53l1_platform_ipp_imports.h new file mode 100644 index 000000000000..b7700a919089 --- /dev/null +++ b/drivers/input/misc/vl53L1/vl53l1_platform_ipp_imports.h @@ -0,0 +1,36 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + + +#ifndef _VL53L1_PLATFORM_IPP_IMPORTS_H_ +#define _VL53L1_PLATFORM_IPP_IMPORTS_H_ + +#endif diff --git a/drivers/input/misc/vl53L1/vl53l1_platform_log.h b/drivers/input/misc/vl53L1/vl53l1_platform_log.h new file mode 100644 index 000000000000..357e0abb9773 --- /dev/null +++ b/drivers/input/misc/vl53L1/vl53l1_platform_log.h @@ -0,0 +1,177 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + +/** + * @file vl53l1_platform_log.h + * + * @brief EwokPlus25 platform logging function definition + */ + + +#ifndef _VL53L1_PLATFORM_LOG_H_ +#define _VL53L1_PLATFORM_LOG_H_ + +#include + +#ifdef VL53L1_LOG_ENABLE + #include "vl53l1_platform_user_config.h" + + #ifdef _MSC_VER + # define EWOKPLUS_EXPORTS __declspec(dllexport) + #else + # define EWOKPLUS_EXPORTS + #endif + + #include "vl53l1_types.h" + + #ifdef __cplusplus + extern "C" { + #endif + + #include + + /** + * @brief Set the level, output and specific functions for module + * logging. + * + * + * @param filename - full path of output log file, NULL for print to + * stdout + * + * @param modules - Module or None or All to trace + * VL53L1_TRACE_MODULE_NONE + * VL53L1_TRACE_MODULE_API + * VL53L1_TRACE_MODULE_CORE + * VL53L1_TRACE_MODULE_TUNING + * VL53L1_TRACE_MODULE_CHARACTERISATION + * VL53L1_TRACE_MODULE_PLATFORM + * VL53L1_TRACE_MODULE_ALL + * + * @param level - trace level + * VL53L1_TRACE_LEVEL_NONE + * VL53L1_TRACE_LEVEL_ERRORS + * VL53L1_TRACE_LEVEL_WARNING + * VL53L1_TRACE_LEVEL_INFO + * VL53L1_TRACE_LEVEL_DEBUG + * VL53L1_TRACE_LEVEL_ALL + * VL53L1_TRACE_LEVEL_IGNORE + * + * @param functions - function level to trace; + * VL53L1_TRACE_FUNCTION_NONE + * VL53L1_TRACE_FUNCTION_I2C + * VL53L1_TRACE_FUNCTION_ALL + * + * @return status - always VL53L1_ERROR_NONE + * + */ + + #define VL53L1_TRACE_LEVEL_NONE 0x00000000 + #define VL53L1_TRACE_LEVEL_ERRORS 0x00000001 + #define VL53L1_TRACE_LEVEL_WARNING 0x00000002 + #define VL53L1_TRACE_LEVEL_INFO 0x00000004 + #define VL53L1_TRACE_LEVEL_DEBUG 0x00000008 + #define VL53L1_TRACE_LEVEL_ALL 0x00000010 + #define VL53L1_TRACE_LEVEL_IGNORE 0x00000020 + + #define VL53L1_TRACE_FUNCTION_NONE 0x00000000 + #define VL53L1_TRACE_FUNCTION_I2C 0x00000001 + #define VL53L1_TRACE_FUNCTION_ALL 0x7fffffff + + #define VL53L1_TRACE_MODULE_NONE 0x00000000 + #define VL53L1_TRACE_MODULE_API 0x00000001 + #define VL53L1_TRACE_MODULE_CORE 0x00000002 + #define VL53L1_TRACE_MODULE_PROTECTED 0x00000004 + #define VL53L1_TRACE_MODULE_HISTOGRAM 0x00000008 + #define VL53L1_TRACE_MODULE_REGISTERS 0x00000010 + #define VL53L1_TRACE_MODULE_PLATFORM 0x00000020 + #define VL53L1_TRACE_MODULE_NVM 0x00000040 + #define VL53L1_TRACE_MODULE_CALIBRATION_DATA 0x00000080 + #define VL53L1_TRACE_MODULE_NVM_DATA 0x00000100 + #define VL53L1_TRACE_MODULE_HISTOGRAM_DATA 0x00000200 + #define VL53L1_TRACE_MODULE_RANGE_RESULTS_DATA 0x00000400 + #define VL53L1_TRACE_MODULE_XTALK_DATA 0x00000800 + #define VL53L1_TRACE_MODULE_OFFSET_DATA 0x00001000 + #define VL53L1_TRACE_MODULE_DATA_INIT 0x00002000 + #define VL53L1_TRACE_MODULE_REF_SPAD_CHAR 0x00004000 + #define VL53L1_TRACE_MODULE_SPAD_RATE_MAP 0x00008000 + #ifdef PAL_EXTENDED + #define VL53L1_TRACE_MODULE_SPAD 0x01000000 + #define VL53L1_TRACE_MODULE_FMT 0x02000000 + #define VL53L1_TRACE_MODULE_UTILS 0x04000000 + #define VL53L1_TRACE_MODULE_BENCH_FUNCS 0x08000000 + #endif + #define VL53L1_TRACE_MODULE_CUSTOMER_API 0x40000000 + #define VL53L1_TRACE_MODULE_ALL 0x7fffffff + + extern void log_trace_print(uint32_t module, uint32_t level, + uint32_t function, const char *format, ...); + + #define _LOG_TRACE_PRINT_FMT(module, level, function, format, ...) \ + log_trace_print(module, level, function, \ + KERN_INFO " " format, ##__VA_ARGS__) + #define _LOG_TRACE_PRINT(module, level, function, ...) \ + _LOG_TRACE_PRINT_FMT(module, level, function, ##__VA_ARGS__) + #define _LOG_FUNCTION_START(module, fmt, ...) \ + log_trace_print(module, VL53L1_TRACE_LEVEL_NONE, \ + VL53L1_TRACE_FUNCTION_ALL, \ + KERN_INFO " %s "fmt"\n", __func__, ##__VA_ARGS__) + #define _LOG_FUNCTION_END(module, status, ...) \ + log_trace_print(module, VL53L1_TRACE_LEVEL_NONE, \ + VL53L1_TRACE_FUNCTION_ALL, \ + KERN_INFO " %s %d\n", __func__, status) + #define _LOG_FUNCTION_END_FMT(module, status, fmt, ...) \ + log_trace_print(module, VL53L1_TRACE_LEVEL_NONE, \ + VL53L1_TRACE_FUNCTION_ALL, \ + KERN_INFO " %s %d"fmt"\n", __func__, status, \ + ##__VA_ARGS__) + #define _LOG_GET_TRACE_FUNCTIONS() 0 + #define _LOG_SET_TRACE_FUNCTIONS(functions) + + #define _LOG_STRING_BUFFER(x) char x[VL53L1_MAX_STRING_LENGTH] + + #ifdef __cplusplus + } + #endif + +#else /* VL53L1_LOG_ENABLE - no logging */ + #include "vl53l1_platform_user_config.h" + + #define _LOG_TRACE_PRINT(module, level, function, ...) + #define _LOG_FUNCTION_START(module, fmt, ...) + #define _LOG_FUNCTION_END(module, status, ...) + #define _LOG_FUNCTION_END_FMT(module, status, fmt, ...) + #define _LOG_GET_TRACE_FUNCTIONS() 0 + #define _LOG_SET_TRACE_FUNCTIONS(functions) + #define _LOG_STRING_BUFFER(x) + +#endif /* VL53L1_LOG_ENABLE */ + +#endif /* _VL53L1_PLATFORM_LOG_H_ */ diff --git a/drivers/input/misc/vl53L1/vl53l1_platform_user_config.h b/drivers/input/misc/vl53L1/vl53l1_platform_user_config.h new file mode 100644 index 000000000000..396d25762a5d --- /dev/null +++ b/drivers/input/misc/vl53L1/vl53l1_platform_user_config.h @@ -0,0 +1,117 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + +/** + * @file vl53l1_platform_user_config.h + * + * @brief EwokPlus compile time user modifiable configuration + */ + + +#ifndef _VL53L1_PLATFORM_USER_CONFIG_H_ +#define _VL53L1_PLATFORM_USER_CONFIG_H_ + +#define VL53L1_BYTES_PER_WORD 2 +#define VL53L1_BYTES_PER_DWORD 4 + +/* Define polling delays */ +#define VL53L1_BOOT_COMPLETION_POLLING_TIMEOUT_MS 500 +#define VL53L1_RANGE_COMPLETION_POLLING_TIMEOUT_MS 2000 +#define VL53L1_TEST_COMPLETION_POLLING_TIMEOUT_MS 60000 + +#define VL53L1_POLLING_DELAY_MS 1 + +/* Define LLD TuningParms Page Base Address + * - Part of Patch_AddedTuningParms_11761 + */ +#define VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS 0x8000 +#define VL53L1_TUNINGPARM_PRIVATE_PAGE_BASE_ADDRESS 0xC000 + +#define VL53L1_GAIN_FACTOR__STANDARD_DEFAULT 0x0800 + /*!< Default standard ranging gain correction factor + * 1.11 format. 1.0 = 0x0800, 0.980 = 0x07D7 + */ +#define VL53L1_GAIN_FACTOR__HISTOGRAM_DEFAULT 0x0800 + /*!< Default histogram ranging gain correction factor + * 1.11 format. 1.0 = 0x0800, 0.975 = 0x07CC + */ + + +#define VL53L1_OFFSET_CAL_MIN_EFFECTIVE_SPADS 0x0500 + /*!< Lower Limit for the MM1 effective SPAD count during offset + * calibration Format 8.8 0x0500 -> 5.0 effective SPADs + */ + +#define VL53L1_OFFSET_CAL_MAX_PRE_PEAK_RATE_MCPS 0x1900 + /*!< Max Limit for the pre range preak rate during offset + * calibration Format 9.7 0x1900 -> 50.0 Mcps. + * If larger then in pile up + */ + +#define VL53L1_OFFSET_CAL_MAX_SIGMA_MM 0x0040 + /*!< Max sigma estimate limit during offset calibration + * Check applies to pre-range, mm1 and mm2 ranges + * Format 14.2 0x0040 -> 16.0mm. + */ + + +#define VL53L1_ZONE_CAL_MAX_PRE_PEAK_RATE_MCPS 0x1900 + /*!< Max Peak Rate Limit for the during zone calibration + * Format 9.7 0x1900 -> 50.0 Mcps. + * If larger then in pile up + */ + +#define VL53L1_ZONE_CAL_MAX_SIGMA_MM 0x0040 + /*!< Max sigma estimate limit during zone calibration + * Format 14.2 0x0040 -> 16.0mm. + */ + + +#define VL53L1_XTALK_EXTRACT_MAX_SIGMA_MM 0x008C + /*!< Max Sigma value allowed for a successful xtalk extraction + * Format 14.2 0x008C -> 35.0 mm. + */ + + +#define VL53L1_MAX_USER_ZONES 169 + /*!< Max number of user Zones - maximal limitation from + * FW stream divide - value of 254 + */ + +#define VL53L1_MAX_RANGE_RESULTS 4 + /*!< Sets the maximum number of targets distances the histogram + * post processing can generate + */ + +#define VL53L1_MAX_STRING_LENGTH 512 + +#endif /* _VL53L1_PLATFORM_USER_CONFIG_H_ */ + diff --git a/drivers/input/misc/vl53L1/vl53l1_platform_user_data.h b/drivers/input/misc/vl53L1/vl53l1_platform_user_data.h new file mode 100644 index 000000000000..41da1dda1235 --- /dev/null +++ b/drivers/input/misc/vl53L1/vl53l1_platform_user_data.h @@ -0,0 +1,63 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + + +#ifndef _VL53L1_PLATFORM_USER_DATA_H_ +#define _VL53L1_PLATFORM_USER_DATA_H_ + +#include "vl53l1_ll_def.h" + +#include +#include "vl53l1_def.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +#define VL53L1_Dev_t VL53L1_DevData_t +#define VL53L1_DEV VL53L1_DevData_t * + +#define VL53L1DevDataGet(Dev, field) (Dev->field) +#define VL53L1DevDataSet(Dev, field, data) ((Dev->field) = (data)) + +#define VL53L1DevStructGetLLDriverHandle(Dev) (&VL53L1DevDataGet(Dev, LLData)) +#define VL53L1DevStructGetLLResultsHandle(Dev) (&VL53L1DevDataGet(Dev,\ + llresults)) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1/vl53l1_platform_user_defines.h b/drivers/input/misc/vl53L1/vl53l1_platform_user_defines.h new file mode 100644 index 000000000000..731c48341756 --- /dev/null +++ b/drivers/input/misc/vl53L1/vl53l1_platform_user_defines.h @@ -0,0 +1,92 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + + +#ifndef _VL53L1_PLATFORM_USER_DEFINES_H_ +#define _VL53L1_PLATFORM_USER_DEFINES_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef __KERNEL__ +#include +#endif + +/** + * @file vl53l1_platform_user_defines.h + * + * @brief All end user OS/platform/application definitions + */ + + +/** + * @def do_division_u + * @brief customer supplied division operation - 64-bit unsigned + * + * @param dividend unsigned 64-bit numerator + * @param divisor unsigned 64-bit denominator + */ +#ifdef __KERNEL__ +#define do_division_u(dividend, divisor) div64_u64(dividend, divisor) +#else +#define do_division_u(dividend, divisor) (dividend / divisor) +#endif + +/** + * @def do_division_s + * @brief customer supplied division operation - 64-bit signed + * + * @param dividend signed 64-bit numerator + * @param divisor signed 64-bit denominator + */ +#ifdef __KERNEL__ +#define do_division_s(dividend, divisor) div64_s64(dividend, divisor) +#else +#define do_division_s(dividend, divisor) (dividend / divisor) +#endif + +#define WARN_OVERRIDE_STATUS(__X__)\ + trace_print(VL53L1_TRACE_LEVEL_WARNING, #__X__) + + +#define DISABLE_WARNINGS() +#define ENABLE_WARNINGS() + + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1/vl53l1_types.h b/drivers/input/misc/vl53L1/vl53l1_types.h new file mode 100644 index 000000000000..3d78e2559204 --- /dev/null +++ b/drivers/input/misc/vl53L1/vl53l1_types.h @@ -0,0 +1,46 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ +/** + * @file vl53l1_types.h + * @brief VL53L1 types definition + */ + +#ifndef _VL53L1_TYPES_H_ +#define _VL53L1_TYPES_H_ + +#include +/** use where fractional values are expected + * + * Given a floating point value f it's .16 bit point is (int)(f*(1<<16)) + */ +typedef uint32_t FixPoint1616_t; + +#endif /* VL53L1_TYPES_H_ */ diff --git a/drivers/input/misc/vl53L1_14_1_2/Android.mk b/drivers/input/misc/vl53L1_14_1_2/Android.mk new file mode 100644 index 000000000000..d2c85992bf76 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/Android.mk @@ -0,0 +1,25 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(PRODUCT_USE_TOF_vl53l3),true) + KERNEL_CFLAGS += CONFIG_INPUT_MISC_TOF_USE_VL53L3=y +endif + +ifeq ($(PRODUCT_USE_PMIC_WL2864C),true) + KERNEL_CFLAGS += CONFIG_INPUT_MISC_PMIC_WL2864C=y +endif + +ifeq ($(TOF_KERNEL_COMPILE_FIX),true) + KBUILD_OPTIONS += CONFIG_TOF_COMPILE_FIX=y +endif + +ifeq ($(TOF_PHIO_TIMEOUT_FIX),true) + KBUILD_OPTIONS += CONFIG_PHIO_TIMEOUT_FIX=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := stmvl53l3.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/misc/vl53L1_14_1_2/Kbuild b/drivers/input/misc/vl53L1_14_1_2/Kbuild new file mode 100644 index 000000000000..36d80f0664c1 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/Kbuild @@ -0,0 +1,58 @@ +# +# Kbuild for the vl53L1 drivers. +# + +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/vl53L1_14_1_2/inc \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/vl53L1_14_1_2/ipp \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/vl53L1_14_1_2 \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/misc/vl53L1_14_1_2/protected/inc + +# define this environment variable if you want to compile driver for an old +# kernel +ifdef OLD_NETLINK_API +EXTRA_CFLAGS += -DOLD_NETLINK_API +endif + +ifdef VL53L1_LOG_ENABLE +EXTRA_CFLAGS += -DVL53L1_LOG_ENABLE +endif + +obj-m += stmvl53l3.o +stmvl53l3-objs := stmvl53l1_module.o stmvl53l1_module-i2c.o +stmvl53l3-objs += vl53l1_platform_ipp.o +stmvl53l3-objs += protected/src/vl53l1_hist_funcs.o +stmvl53l3-objs += protected/src/vl53l1_xtalk.o +stmvl53l3-objs += protected/src/vl53l1_hist_algos_gen3.o +stmvl53l3-objs += protected/src/vl53l1_hist_core.o +stmvl53l3-objs += protected/src/vl53l1_sigma_estimate.o +stmvl53l3-objs += protected/src/vl53l1_dmax.o +stmvl53l3-objs += protected/src/vl53l1_hist_algos_gen4.o +stmvl53l3-objs += stmvl53l1_i2c.o stmvl53l1_log.o +stmvl53l3-objs += src/vl53l1_api.o src/vl53l1_api_core.o +stmvl53l3-objs += src/vl53l1_api_strings.o src/vl53l1_error_strings.o +stmvl53l3-objs += src/vl53l1_core.o src/vl53l1_register_funcs.o +stmvl53l3-objs += src/vl53l1_api_preset_modes.o +stmvl53l3-objs += src/vl53l1_api_calibration.o +stmvl53l3-objs += src/vl53l1_silicon_core.o +stmvl53l3-objs += src/vl53l1_zone_presets.o src/vl53l1_nvm.o +stmvl53l3-objs += src/vl53l1_api_debug.o src/vl53l1_core_support.o +stmvl53l3-objs += src/vl53l1_wait.o +stmvl53l3-objs += src/vl53l1_nvm_debug.o + +ifneq ($(filter m y,$(CONFIG_INPUT_MISC_TOF_USE_VL53L3)),) + EXTRA_CFLAGS += -DTOF_USE_VL53L3_EN +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_MISC_PMIC_WL2864C)),) + EXTRA_CFLAGS += -DTOF_USE_PMIC_WL2864C +endif + +ifneq ($(filter m y,$(CONFIG_TOF_COMPILE_FIX)),) + EXTRA_CFLAGS += -Wno-unused-but-set-variable +endif + +ifneq ($(filter m y,$(CONFIG_PHIO_TIMEOUT_FIX)),) + EXTRA_CFLAGS += -DPHIO_TIMEOUT_FIX +endif \ No newline at end of file diff --git a/drivers/input/misc/vl53L1_14_1_2/Makefile b/drivers/input/misc/vl53L1_14_1_2/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_api.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_api.h new file mode 100644 index 000000000000..c280d7eb5e19 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_api.h @@ -0,0 +1,1433 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +#ifndef _VL53L1_API_H_ +#define _VL53L1_API_H_ + +#include "vl53l1_api_strings.h" +#include "vl53l1_api_core.h" +#include "vl53l1_preset_setup.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if !defined(VL53L1DevDataGet) +#warning "PALDevDataGet is deprecated define VL53L1DevDataGet instead" +#define VL53L1DevDataGet(Dev, field) (Dev->Data.field) +#endif + +#if !defined(VL53L1DevDataSet) +#warning "PALDevDataSet is deprecated define VL53L1DevDataSet instead" +#define VL53L1DevDataSet(Dev, field, data) ((Dev->Data.field) = (data)) +#endif + +/** @defgroup VL53L1_cut11_group VL53L1 cut1.1 Function Definition + * @brief VL53L1 cut1.1 Function Definition + * @{ + */ + +/** @defgroup VL53L1_general_group VL53L1 General Functions + * @brief General functions and definitions + * @{ + */ + +/** + * @brief Return the VL53L1 driver Version + * + * @note This function doesn't access to the device + * + * @param pVersion Rer to current driver Version + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetVersion(VL53L1_Version_t *pVersion); + +/** + * @brief Reads the Product Revision for a for given Device + * This function can be used to distinguish cut1.0 from cut1.1. + * + * @param Dev Device Handle + * @param pProductRevisionMajor Pointer to Product Revision Major + * for a given Device + * @param pProductRevisionMinor Pointer to Product Revision Minor + * for a given Device + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetProductRevision(VL53L1_DEV Dev, + uint8_t *pProductRevisionMajor, uint8_t *pProductRevisionMinor); + +/** + * @brief Reads the Device information for given Device + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pVL53L1_DeviceInfo Pointer to current device info for a given + * Device + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetDeviceInfo(VL53L1_DEV Dev, + VL53L1_DeviceInfo_t *pVL53L1_DeviceInfo); + +/** + * @brief Reads the Device unique identifier + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pUid Pointer to current device unique ID + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetUID(VL53L1_DEV Dev, uint64_t *pUid); + +/** + * @brief Human readable Range Status string for a given RangeStatus + * + * @note This function doesn't access to the device + * + * @param RangeStatus The RangeStatus code as stored on + * @a VL53L1_RangingMeasurementData_t + * @param pRangeStatusString The returned RangeStatus string. Shall be + * defined as char buf[VL53L1_MAX_STRING_LENGTH] + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetRangeStatusString(uint8_t RangeStatus, + char *pRangeStatusString); + +/** + * @brief Human readable error string for driver error status + * + * @note This function doesn't access to the device + * + * @param PalErrorCode The error code as stored on @a VL53L1_Error + * @param pPalErrorString The error string corresponding to the + * PalErrorCode. Shall be defined as char buf[VL53L1_MAX_STRING_LENGTH] + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetPalErrorString(VL53L1_Error PalErrorCode, + char *pPalErrorString); + +/** + * @brief Human readable driver State string + * + * @note This function doesn't access to the device + * + * @param PalStateCode The State code as stored on @a VL53L1_State + * @param pPalStateString The State string corresponding to the + * PalStateCode. Shall be defined as char buf[VL53L1_MAX_STRING_LENGTH] + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetPalStateString(VL53L1_State PalStateCode, + char *pPalStateString); + +/** + * @brief Reads the internal state of the driver for a given Device + * + * @note This function doesn't access to the device + * + * @param Dev Device Handle + * @param pPalState Pointer to current state of the PAL for a + * given Device + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetPalState(VL53L1_DEV Dev, + VL53L1_State *pPalState); + + + +/** @} VL53L1_general_group */ + +/** @defgroup VL53L1_init_group VL53L1 Init Functions + * @brief VL53L1 Init Functions + * @{ + */ + +/** + * @brief Set new device address + * + * After completion the device will answer to the new address programmed. + * This function should be called when several devices are used in parallel + * before start programming the sensor. + * When a single device us used, there is no need to call this function. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param DeviceAddress The new Device address + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetDeviceAddress(VL53L1_DEV Dev, + uint8_t DeviceAddress); + +/** + * + * @brief One time device initialization + * + * To be called after device has been powered on and booted + * see @a VL53L1_WaitDeviceBooted() + * + * @par Function Description + * When not used after a fresh device "power up", it may return + * @a #VL53L1_ERROR_CALIBRATION_WARNING meaning wrong calibration data + * may have been fetched from device that can result in ranging offset error\n + * If VL53L1_DataInit is called several times then the application must restore + * calibration calling @a VL53L1_SetOffsetCalibrationData() + * It implies application has gathered calibration data thanks to + * @a VL53L1_GetOffsetCalibrationData() after an initial calibration stage. + * This function will change the VL53L1_State from VL53L1_STATE_POWERDOWN to + * VL53L1_STATE_WAIT_STATICINIT. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_DataInit(VL53L1_DEV Dev); + + +/** + * @brief Do basic device init (and eventually patch loading) + * This function will change the VL53L1_State from + * VL53L1_STATE_WAIT_STATICINIT to VL53L1_STATE_IDLE. + * In this stage all default setting will be applied. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_StaticInit(VL53L1_DEV Dev); + +/** + * @brief Wait for device booted after chip enable (hardware standby) + * This function can be run only when VL53L1_State is VL53L1_STATE_POWERDOWN. + * + * @param Dev Device Handle + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + * + */ +VL53L1_Error VL53L1_WaitDeviceBooted(VL53L1_DEV Dev); + + +/** @} VL53L1_init_group */ + +/** @defgroup VL53L1_parameters_group VL53L1 Parameters Functions + * @brief Functions used to prepare and setup the device + * @{ + */ + +/** + * @brief Set a new Preset Mode + * @par Function Description + * Set device to a new Operating Mode (High speed ranging, Multi objects ...) + * + * @note This function doesn't Access to the device + * + * @warning This function change the timing budget to 16 ms and the inter- + * measurement period to 1000 ms. Also the VL53L1_DISTANCEMODE_LONG is used. + * + * @param Dev Device Handle + * @param PresetMode New Preset mode to apply + *
Valid values are: + */ +/** + * @li VL53L1_PRESETMODE_MULTIZONES_SCANNING + * @li VL53L1_PRESETMODE_RANGING + * @li VL53L1_PRESETMODE_AUTONOMOUS + * @li VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS + * @li VL53L1_PRESETMODE_LITE_RANGING + * @li VL53L1_PRESETMODE_OLT + */ +/** + * + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_MODE_NOT_SUPPORTED This error occurs when PresetMode is + * not in the supported list + */ +VL53L1_Error VL53L1_SetPresetMode(VL53L1_DEV Dev, + VL53L1_PresetModes PresetMode); + +/** + * @brief Get current Preset Mode + * @par Function Description + * Get actual mode of the device(ranging, histogram ...) + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param pPresetMode Pointer to current apply mode value + * + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_MODE_NOT_SUPPORTED This error occurs when + * DeviceMode is not in the supported list + */ +VL53L1_Error VL53L1_GetPresetMode(VL53L1_DEV Dev, + VL53L1_PresetModes *pPresetMode); + + +/** + * @brief Set the distance mode + * @par Function Description + * Set the distance mode to be used for the next ranging.
+ * The modes Short, Medium and Long are used to optimize the ranging accuracy + * in a specific range of distance.
The user select one of these modes to + * select the distance range. + * @note This function doesn't Access to the device + * + * @warning This function should be called after @a VL53L1_SetPresetMode(). + + * @param Dev Device Handle + * @param DistanceMode Distance mode to apply, valid values are: + * @li VL53L1_DISTANCEMODE_SHORT + * @li VL53L1_DISTANCEMODE_MEDIUM + * @li VL53L1_DISTANCEMODE_LONG + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_MODE_NOT_SUPPORTED This error occurs when DistanceMode + * is not in the supported list + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetDistanceMode(VL53L1_DEV Dev, + VL53L1_DistanceModes DistanceMode); + +/** + * @brief Get the distance mode + * @par Function Description + * Get the distance mode used for the next ranging. + * + * @param Dev Device Handle + * @param *pDistanceMode Pointer to Distance mode + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetDistanceMode(VL53L1_DEV Dev, + VL53L1_DistanceModes *pDistanceMode); + + +/** + * @brief Set the output mode + * @par Function Description + * Set the output mode to be used for the next ranging. The output mode is used + * to select, in case of multiple objects, which one will be used in + * function @a VL53L1_GetRangingMeasurementData(). + * VL53L1_SetOutputMode also sets the object used by automatic + * distance mode algorithm when @a VL53L1_SetDistanceMode() is + * set to automatic mode. + * + * @note This function doesn't Access to the device + * + * @warning This function should be called after @a VL53L1_SetPresetMode(). + + * @param Dev Device Handle + * @param OutputMode Output mode to apply valid values are: + * @li VL53L1_OUTPUTMODE_NEAREST + * @li VL53L1_OUTPUTMODE_STRONGEST + * + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_MODE_NOT_SUPPORTED This error occurs when OutputMode + * is not in the supported list + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetOutputMode(VL53L1_DEV Dev, + VL53L1_OutputModes OutputMode); + +/** + * @brief Get the output mode + * @par Function Description + * Get the output mode used for the next ranging. + * + * @param Dev Device Handle + * @param *pOutputMode Pointer to Output mode + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetOutputMode(VL53L1_DEV Dev, + VL53L1_OutputModes *pOutputMode); + + +/** + * @brief Set Ranging Timing Budget in microseconds + * + * @par Function Description + * Defines the maximum time allowed by the user to the device to run a + * full ranging sequence for the current mode (ranging, histogram, ASL ...) + * + * @param Dev Device Handle + * @param MeasurementTimingBudgetMicroSeconds Max measurement time in + * microseconds. + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_INVALID_PARAMS Error timing parameter not + * supported. + * The maximum accepted value for the + * computed timing budget is 10 seconds + * the minimum value depends on the preset + * mode selected. + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetMeasurementTimingBudgetMicroSeconds( + VL53L1_DEV Dev, uint32_t MeasurementTimingBudgetMicroSeconds); + +/** + * @brief Get Ranging Timing Budget in microseconds + * + * @par Function Description + * Returns the programmed the maximum time allowed by the user to the + * device to run a full ranging sequence for the current mode + * (ranging, histogram, ASL ...) + * + * @param Dev Device Handle + * @param pMeasurementTimingBudgetMicroSeconds Max measurement time in + * microseconds. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetMeasurementTimingBudgetMicroSeconds( + VL53L1_DEV Dev, uint32_t *pMeasurementTimingBudgetMicroSeconds); + + +/** + * Program continuous mode Inter-Measurement period in milliseconds + * + * @par Function Description + * When trying to set too short time return INVALID_PARAMS minimal value + * + * @param Dev Device Handle + * @param InterMeasurementPeriodMilliSeconds Inter-Measurement Period in ms. + * this value should be greater than the duration set in + * @a VL53L1_SetMeasurementTimingBudgetMicroSeconds() to ensure smooth ranging + * operation. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetInterMeasurementPeriodMilliSeconds( + VL53L1_DEV Dev, uint32_t InterMeasurementPeriodMilliSeconds); + +/** + * Get continuous mode Inter-Measurement period in milliseconds + * + * @par Function Description + * + * @param Dev Device Handle + * @param pInterMeasurementPeriodMilliSeconds Pointer to programmed + * Inter-Measurement Period in milliseconds. + * @return VL53L1_ERROR_NONE + */ +VL53L1_Error VL53L1_GetInterMeasurementPeriodMilliSeconds( + VL53L1_DEV Dev, uint32_t *pInterMeasurementPeriodMilliSeconds); + +/** + * @brief target reflectance for Dmax setting + * @par Function Description + * Allow user to set the value for target reflectance @ 940nm to calculate the + * ambient DMAX values for. Set to 50% by default by @a VL53L1_DataInit() + * + * @param Dev Device Handle + * @param DmaxReflectance Reflectance % in 16.16 fixed point + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_INVALID_PARAMS in case input value is not in range + * from 0 to 100. Note that this is a fix point value so the max value is + * 100 * 65536. + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetDmaxReflectance(VL53L1_DEV Dev, + FixPoint1616_t DmaxReflectance); + +/** + * @brief Get target reflectance for Dmax + * @par Function Description + * Retrieves the value for target reflectance @ 940nm to calculate the + * ambient DMAX values for. Set to 50% by default by @a VL53L1_DataInit() + * + * @param Dev Device Handle + * @param pDmaxReflectance pointer to Reflectance % in 16.16 fixed point + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetDmaxReflectance(VL53L1_DEV Dev, + FixPoint1616_t *pDmaxReflectance); +/** + * @brief Set function for ambient Dmax mode + * + * + * @param Dev Device Handle + * @param DmaxMode DMAX mode to be used in ranging + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + + +VL53L1_Error VL53L1_SetDmaxMode(VL53L1_DEV Dev, + VL53L1_DeviceDmaxModes DmaxMode); + +/** + * @brief Get function for ambient Dmax mode + * + * @param Dev Device Handle + * @param pDmaxMode output pointer to DMAX mode currently in use + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GetDmaxMode(VL53L1_DEV Dev, + VL53L1_DeviceDmaxModes *pDmaxMode); + +/** @} VL53L1_parameters_group */ + + +/** @defgroup VL53L1_limitcheck_group VL53L1 Limit Check Functions + * @brief Functions used for the Limit checks + * @{ + */ + + + +/** + * @brief Get the number of the check limit managed by a given Device + * + * @par Function Description + * This function give the number of the check limit managed by the Device + * + * @param pNumberOfLimitCheck Pointer to the number of check limit. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetNumberOfLimitCheck( + uint16_t *pNumberOfLimitCheck); + +/** + * @brief Return a description string for a given limit check number + * + * @par Function Description + * This function returns a description string for a given limit check number. + * The limit check is identified with the LimitCheckId. + * + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L1_GetNumberOfLimitCheck() ). + * @param pLimitCheckString Pointer to the description string of + * the given check limit. Shall be defined as char buf[VL53L1_MAX_STRING_LENGTH] + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetLimitCheckInfo(uint16_t LimitCheckId, + char *pLimitCheckString); + +/** + * @brief Return a the Status of the specified check limit + * + * @par Function Description + * This function returns the Status of the specified check limit. + * The value indicate if the check is fail or not. + * The limit check is identified with the LimitCheckId. + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + (0<= LimitCheckId < VL53L1_GetNumberOfLimitCheck() ). + * @param pLimitCheckStatus Pointer to the + Limit Check Status of the given check limit. + * LimitCheckStatus : + * 0 the check is not fail or not enabled + * 1 the check if fail + * + *

    + *
  • VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE: the sigma indicate the quality + * of the measure. The more it is little the better it is. + * The status is 1 when current sigma is greater then the limit.
  • + *
  • VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: the signal rate indicate + * the strength of the returned signal. The more it is big the better it is. + * The status is 1 when current signal is lower then the limit.
  • + *

+ * + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetLimitCheckStatus(VL53L1_DEV Dev, + uint16_t LimitCheckId, uint8_t *pLimitCheckStatus); + +/** + * @brief Enable/Disable a specific limit check + * + * @par Function Description + * This function Enable/Disable a specific limit check. + * The limit check is identified with the LimitCheckId. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L1_GetNumberOfLimitCheck() ). + * @param LimitCheckEnable + * @li set LimitCheckEnable=1 enables the LimitCheckId limit + * @li set LimitCheckEnable=0 disables the LimitCheckId limit + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_INVALID_PARAMS This error is returned + * when LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetLimitCheckEnable(VL53L1_DEV Dev, + uint16_t LimitCheckId, uint8_t LimitCheckEnable); + +/** + * @brief Get specific limit check enable state + * + * @par Function Description + * This function get the enable state of a specific limit check. + * The limit check is identified with the LimitCheckId. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L1_GetNumberOfLimitCheck() ). + * @param pLimitCheckEnable Pointer to the check limit enable + * value. + * @li if 1 the check limit corresponding to LimitCheckId is Enabled + * @li if 0 the check limit corresponding to LimitCheckId is disabled + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_INVALID_PARAMS This error is returned + * when LimitCheckId value is out of range. + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetLimitCheckEnable(VL53L1_DEV Dev, + uint16_t LimitCheckId, uint8_t *pLimitCheckEnable); + +/** + * @brief Set a specific limit check value + * + * @par Function Description + * This function set a specific limit check value. + * The limit check is identified with the LimitCheckId. + * + * @note Note that the value written with that function will not be applied if + * the limit is not enabled. In other words this function will not enable the + * limit but change only the value. In case the limit is not enabled the value + * is saved internally and applied with VL53L1_SetLimitCheckEnable. + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L1_GetNumberOfLimitCheck() ). + * @param LimitCheckValue Limit check Value for a given + * LimitCheckId + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetLimitCheckValue(VL53L1_DEV Dev, + uint16_t LimitCheckId, FixPoint1616_t LimitCheckValue); + +/** + * @brief Get a specific limit check value + * + * @par Function Description + * This function get a specific limit check value from device then it updates + * internal values and check enables. + * The limit check is identified with the LimitCheckId. + * + * @note This function get the current value from device if zero then the value + * returned is the one stored by the user, but in that case the check is store + * as disabled. If the value from device is not zero, this is returned and set + * into the memory at the same way that user call VL53L1_SetLimitCheckValue() + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L1_GetNumberOfLimitCheck() ). + * @param pLimitCheckValue Pointer to Limit + * check Value for a given LimitCheckId. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetLimitCheckValue(VL53L1_DEV Dev, + uint16_t LimitCheckId, FixPoint1616_t *pLimitCheckValue); + +/** + * @brief Get the current value of the signal used for the limit check + * + * @par Function Description + * This function get a the current value of the signal used for the limit check. + * To obtain the latest value you should run a valid ranging before. + * The value reported is linked to the limit check identified with the + * LimitCheckId. + * + * @param Dev Device Handle + * @param LimitCheckId Limit Check ID + * (0<= LimitCheckId < VL53L1_GetNumberOfLimitCheck() ). + * @param pLimitCheckCurrent Pointer to current Value for a + * given LimitCheckId. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetLimitCheckCurrent(VL53L1_DEV Dev, + uint16_t LimitCheckId, FixPoint1616_t *pLimitCheckCurrent); + +/** @} VL53L1_limitcheck_group */ + + + +/** @defgroup VL53L1_ROI_group VL53L1 ROI Functions + * @brief Functions used to select ROIs + * @{ + */ + +/** + * @brief Get the Maximum number of ROI Zones managed by the Device + * + * @par Function Description + * Get Maximum number of ROI Zones managed by the Device. + * + * @note The number of Zone depends on the preset mode used so to have the + * right number this function should be call after @a VL53L1_SetPresetMode() + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param pMaxNumberOfROI Pointer to the Maximum Number + * of ROI Zones value. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetMaxNumberOfROI(VL53L1_DEV Dev, + uint8_t *pMaxNumberOfROI); +/** + * @brief Set the ROI to be used for ranging + * + * @par Function Description + * The user defined ROIs are rectangles described as per the following system + * from the Top Left corner to the Bottom Right corner. + *
Minimal ROI size is 4x4 spads + * @image html roi_coord.png + * + * @param Dev Device Handle + * @param pRoiConfig Pointer to the Structure containing all the + * ROI to be used. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetROI(VL53L1_DEV Dev, + VL53L1_RoiConfig_t *pRoiConfig); + +/** + * @brief Get the ROI managed by the Device + * + * @par Function Description + * Get the ROI managed by the Device + * + * @param Dev Device Handle + * @param pRoiConfig Pointer to the Structure containing all the + * ROI to be used. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetROI(VL53L1_DEV Dev, + VL53L1_RoiConfig_t *pRoiConfig); + +/** @} VL53L1_ROI_group */ + +/* \internal */ +/** @defgroup VL53L1_sequencestep_group VL53L1 Sequence Step Functions + * @brief Functions used to select Steps done on each ranging + * @{ + */ + +/** + * @brief Gets number of sequence steps managed by the API. + * + * @par Function Description + * This function retrieves the number of sequence steps currently managed + * by the API + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param pNumberOfSequenceSteps Out parameter reporting the number of + * sequence steps. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetNumberOfSequenceSteps(VL53L1_DEV Dev, + uint8_t *pNumberOfSequenceSteps); + +/** + * @brief Gets the name of a given sequence step. + * + * @par Function Description + * This function retrieves the name of sequence steps corresponding to + * SequenceStepId. + * + * @note This function doesn't Accesses the device + * + * @param SequenceStepId Sequence step identifier. + * @param pSequenceStepsString Pointer to Info string. Shall be + * defined as char buf[VL53L1_MAX_STRING_LENGTH] + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetSequenceStepsInfo( + VL53L1_SequenceStepId SequenceStepId, char *pSequenceStepsString); + + + +/** + * @brief Sets the (on/off) state of a requested sequence step. + * + * @par Function Description + * This function enables/disables a requested sequence step. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param SequenceStepId Sequence step identifier. + * @param SequenceStepEnabled Demanded state {0=Off,1=On} + * is enabled. + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_INVALID_PARAMS Error SequenceStepId parameter not + * supported. + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetSequenceStepEnable(VL53L1_DEV Dev, + VL53L1_SequenceStepId SequenceStepId, uint8_t SequenceStepEnabled); + +/** + * @brief Gets the (on/off) state of a requested sequence step. + * + * @par Function Description + * This function retrieves the state of a requested sequence step, i.e. on/off. + * + * @note This function Accesses the device + * + * @param Dev Device Handle + * @param SequenceStepId Sequence step identifier. + * @param pSequenceStepEnabled Out parameter reporting if the sequence step + * is enabled {0=Off,1=On}. + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_INVALID_PARAMS Error SequenceStepId parameter not + * supported. + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetSequenceStepEnable(VL53L1_DEV Dev, + VL53L1_SequenceStepId SequenceStepId, uint8_t *pSequenceStepEnabled); + + +/** @} VL53L1_sequencestep_group */ +/* \endinternal */ + + + +/** @defgroup VL53L1_measurement_group VL53L1 Measurement Functions + * @brief Functions used for the measurements + * @{ + */ + +/** + * @brief Start device measurement + * + * @details Started measurement will depend on preset parameters set through + * @a VL53L1_SetPreseMode() + * This function will change the VL53L1_State from VL53L1_STATE_IDLE to + * VL53L1_STATE_RUNNING. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_MODE_NOT_SUPPORTED This error occurs when + * PresetMode programmed with @a VL53L1_SetPresetMode + * @return VL53L1_ERROR_TIME_OUT Time out on start measurement + * @return VL53L1_ERROR_INVALID_PARAMS This error might occur in timed mode + * when inter measurement period is smaller or too close to the timing budget. + * In such case measurements are not started and user must correct the timings + * passed to @a VL53L1_SetMeasurementTimingBudgetMicroSeconds() and + * @a VL53L1_SetInterMeasurementPeriodMilliSeconds() functions. + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_StartMeasurement(VL53L1_DEV Dev); + +/** + * @brief Stop device measurement + * + * @details Will set the device in standby mode at end of current measurement\n + * Not necessary in single mode as device shall return automatically + * in standby mode at end of measurement. + * This function will change the VL53L1_State from VL53L1_STATE_RUNNING + * to VL53L1_STATE_IDLE. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_StopMeasurement(VL53L1_DEV Dev); + +/** + * @brief Clear the Interrupt flag and start new measurement + * * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_ClearInterruptAndStartMeasurement(VL53L1_DEV Dev); + +/** + * @brief Return Measurement Data Ready + * + * @par Function Description + * This function indicate that a measurement data is ready. + * This function is used for non-blocking capture. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param pMeasurementDataReady Pointer to Measurement Data Ready. + * 0 = data not ready, 1 = data ready + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetMeasurementDataReady(VL53L1_DEV Dev, + uint8_t *pMeasurementDataReady); + +/** + * @brief Wait for measurement data ready. + * Blocking function. + * Note that the timeout is given by: + * VL53L1_RANGE_COMPLETION_POLLING_TIMEOUT_MS defined in def.h + * + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_TIME_OUT In case of timeout + */ +VL53L1_Error VL53L1_WaitMeasurementDataReady(VL53L1_DEV Dev); + + +/** + * @brief Retrieve the measurements from device for a given setup + * + * @par Function Description + * Get data from last successful Ranging measurement + */ +/** + * @warning this function will return only the first ROI data and only the + * first object. For multi objects or multi ROI use: + * @a Vl53L1_GetMultiRangingData. + * In case of RANGING only one output is given, this can + * be selected with the help of @a VL53L1_SetOutputMode() + * In case of MULTIZONES_SCANNING and error will be raised because not + * supported in that function. + */ +/** + * + * @warning USER must call @a VL53L1_ClearInterruptAndStartMeasurement() prior + * to call again this function + * + * @note This function Access to the device + * + * @note The first valid value returned by this function will have a range + * status equal to VL53L1_RANGESTATUS_RANGE_VALID_NO_WRAP_CHECK which means that + * the data is valid but no wrap around check have been done. User should take + * care about that. + * + * @param Dev Device Handle + * @param pRangingMeasurementData Pointer to the data structure to fill up. + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_MODE_NOT_SUPPORTED in case of MULTIZONES_SCANNING + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetRangingMeasurementData(VL53L1_DEV Dev, + VL53L1_RangingMeasurementData_t *pRangingMeasurementData); + +/** + * @brief Retrieve all ROI's measurements from device for a given setup + * + * @par Function Description + * Get data from last successful Ranging measurement + * @warning USER should take care about @a VL53L1_GetNumberOfROI() + * before get data. + * Bare driver will fill a NumberOfROI times the corresponding data + * structure used in the measurement function. + * + * @warning USER must call @a VL53L1_ClearInterruptAndStartMeasurement() prior + * to call again this function + * + * @note This function Access to the device + * + * @note The first valid value returned by this function will have a range + * status equal to VL53L1_RANGESTATUS_RANGE_VALID_NO_WRAP_CHECK which means that + * the data is valid but no wrap around check have been done. User should take + * care about that. + * + * @param Dev Device Handle + * @param pMultiRangingData Pointer to the data structure to fill up. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetMultiRangingData(VL53L1_DEV Dev, + VL53L1_MultiRangingData_t *pMultiRangingData); + +/** + * @brief Get Additional Data + * + * @par Function Description + * This function is used to get lld debugging data on the last histogram + * measurement. shall be called when a new measurement is ready (interrupt or + * positive VL53L1_GetMeasurementDataReady() polling) and before a call to + * VL53L1_ClearInterruptAndStartMeasurement(). Depending on the PresetMode + * currently set parts of the returned data structure may be not relevant. + * + * @param Dev Device Handle + * @param pAdditionalData Pointer to Additional data + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetAdditionalData(VL53L1_DEV Dev, + VL53L1_AdditionalData_t *pAdditionalData); + + +/** @} VL53L1_measurement_group */ + +/** @defgroup VL53L1_Calibration_group VL53L1 Calibration Functions + * @brief Functions used for Calibration + * @{ + */ + + +/** + * @brief Set Tuning Parameter value for a given parameter ID + * + * @par Function Description + * This function is used to improve the performance of the device. It permit to + * change a particular value used for a timeout or a threshold or a constant + * in an algorithm. The function will change the value of the parameter + * identified by an unique ID. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param TuningParameterId Tuning Parameter ID + * @param TuningParameterValue Tuning Parameter Value + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetTuningParameter(VL53L1_DEV Dev, + uint16_t TuningParameterId, int32_t TuningParameterValue); + +/** + * @brief Get Tuning Parameter value for a given parameter ID + * + * @par Function Description + * This function is used to get the value of the parameter + * identified by an unique ID. + * + * @note This function doesn't Access to the device + * + * @param Dev Device Handle + * @param TuningParameterId Tuning Parameter ID + * @param pTuningParameterValue Pointer to Tuning Parameter Value + * for a given TuningParameterId. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetTuningParameter(VL53L1_DEV Dev, + uint16_t TuningParameterId, int32_t *pTuningParameterValue); + +/** + * @brief Performs Reference Spad Management + * + * @par Function Description + * The reference SPAD initialization procedure determines the minimum amount + * of reference spads to be enables to achieve a target reference signal rate + * and should be performed once during initialization. + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_PerformRefSpadManagement(VL53L1_DEV Dev); + +/** + * @brief Enable/Disable dynamic Xtalk compensation feature + * + * Enable/Disable dynamic Xtalk compensation (aka smudge correction). + * + * @param Dev Device Handle + * @param Mode Set the smudge correction mode + * See ::VL53L1_SmudgeCorrectionModes + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SmudgeCorrectionEnable(VL53L1_DEV Dev, + VL53L1_SmudgeCorrectionModes Mode); + +/** + * @brief Enable/Disable Cross talk compensation feature + * + * Enable/Disable Cross Talk correction. + * + * @param Dev Device Handle + * @param XTalkCompensationEnable Cross talk compensation + * to be set 0 = disabled or 1 = enabled. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetXTalkCompensationEnable(VL53L1_DEV Dev, +uint8_t XTalkCompensationEnable); + +/** + * @brief Get Cross talk compensation rate enable + * + * Get if the Cross Talk is Enabled or Disabled. + * + * @note This function doesn't access to the device + * + * @param Dev Device Handle + * @param pXTalkCompensationEnable Pointer to the Cross talk compensation + * state 0=disabled or 1 = enabled + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetXTalkCompensationEnable(VL53L1_DEV Dev, + uint8_t *pXTalkCompensationEnable); + + +/** + * @brief Perform XTalk Calibration + * + * @details Perform a XTalk calibration of the Device. + * This function will launch a ranging measurement, if interrupts + * are enabled an interrupt will be done. + * This function will clear the interrupt generated automatically. + * This function will program a new value for the XTalk compensation + * and it will enable the cross talk before exit. + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param CalibrationOption Select the Calibration to be run : + * @li VL53L1_XTALKCALIBRATIONMODE_SINGLE_TARGET the calibration uses current + * preset and distance mode without altering them.
+ * User must call @a VL53L1_SetPresetMode() with VL53L1_PRESETMODE_AUTONOMOUS, + * VL53L1_PRESETMODE_LITE_RANGING or VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS + * parameter prior to launch calibration + * @li VL53L1_XTALKCALIBRATIONMODE_NO_TARGET the calibration sets appropriate + * preset and distance mode and thus override existing ones
+ * User must call @a VL53L1_SetPresetMode() again after calibration to set the + * desired one. during this calibration mode no object must be put below a 80cm + * distance from the target + * @li VL53L1_XTALKCALIBRATIONMODE_FULL_ROI the calibration sets appropriate + * preset and distance mode and thus override existing ones
+ * User must call @a VL53L1_SetPresetMode() again after calibration to set the + * desired one. + * The ROI settings must define a single 16x16 ROI before to launch this + * function. + * The calibration uses a target which should be located at least @60cm from the + * device. The actual location of the target shall be passed + * through the bare driver tuning parameters table + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_PerformXTalkCalibration(VL53L1_DEV Dev, + uint8_t CalibrationOption); + +/** + * @brief Define the mode to be used for the offset calibration + * + * Define the mode to be used for the offset calibration. This function should + * be called before run the @a VL53L1_PerformOffsetCalibration() + * + * @param Dev Device Handle + * @param OffsetCalibrationMode Offset Calibration Mode valid values are: + * @li VL53L1_OFFSETCALIBRATIONMODE_STANDARD + * @li VL53L1_OFFSETCALIBRATIONMODE_PRERANGE_ONLY + * @li VL53L1_OFFSETCALIBRATIONMODE_MULTI_ZONE + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetOffsetCalibrationMode(VL53L1_DEV Dev, + VL53L1_OffsetCalibrationModes OffsetCalibrationMode); + +/** + * @brief Define the mode to be used for the offset correction + * + * Define the mode to be used for the offset correction. + * + * @param Dev Device Handle + * @param OffsetCorrectionMode Offset Correction Mode valid values are: + * @li VL53L1_OFFSETCORRECTIONMODE_STANDARD + * @li VL53L1_OFFSETCORRECTIONMODE_PERZONE + * @li VL53L1_OFFSETCORRECTIONMODE_PERVCSEL + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetOffsetCorrectionMode(VL53L1_DEV Dev, + VL53L1_OffsetCorrectionModes OffsetCorrectionMode); + + +/** + * @brief Perform Offset Calibration + * + * @details Perform a Offset calibration of the Device. + * This function will launch a ranging measurement, if interrupts are + * enabled interrupts will be done. + * This function will program a new value for the Offset calibration value + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param CalDistanceMilliMeter Calibration distance value used for the + * offset compensation. + * @param CalReflectancePercent Calibration Target reflectance @ 940nm + * in percentage. + * + * @return VL53L1_ERROR_NONE + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_PerformOffsetCalibration(VL53L1_DEV Dev, + int32_t CalDistanceMilliMeter, + FixPoint1616_t CalReflectancePercent); + +/** + * @brief Perform Offset simple Calibration + * + * @details Perform a very simple offset calibration of the Device. + * This function will launch few ranging measurements and computes offset + * calibration. The preset mode and the distance mode MUST be set by the + * application before to call this function. + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param CalDistanceMilliMeter Calibration distance value used for the + * offset compensation. + * + * @return VL53L1_ERROR_NONE + * @return VL53L1_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL the calibration failed by + * lack of valid measurements + * @return VL53L1_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH means that the target + * distance combined to the number of loops performed in the calibration lead to + * an internal overflow. Try to reduce the distance of the target (140 mm) + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_PerformOffsetSimpleCalibration(VL53L1_DEV Dev, + int32_t CalDistanceMilliMeter); + +/** + * @brief Perform Offset simple Calibration with a "zero distance" target + * + * @details Perform a simple offset calibration of the Device. + * This function will launch few ranging measurements and computes offset + * calibration. The preset mode and the distance mode MUST be set by the + * application before to call this function. + * A target must be place very close to the device. + * Ideally the target shall be touching the coverglass. + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @param Dev Device Handle + * + * @return VL53L1_ERROR_NONE + * @return VL53L1_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL the calibration failed by + * lack of valid measurements + * @return VL53L1_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH means that the target + * distance is too large, try to put the target closer to the device + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_PerformOffsetZeroDistanceCalibration(VL53L1_DEV Dev); + +/** + * @brief Perform Offset per Vcsel Calibration. i.e. per distance mode + * + * @details Perform offset calibration of the Device depending on the + * three distance mode settings: short, medium and long. + * This function will launch few ranging measurements and computes offset + * calibration in each of the three distance modes. + * The preset mode MUST be set by the application before to call this function. + * + * @warning This function is a blocking function + * + * @note This function Access to the device + * + * @param Dev Device Handle + * @param CalDistanceMilliMeter Distance of the target used for the + * offset compensation calibration. + * + * @return VL53L1_ERROR_NONE + * @return VL53L1_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL the calibration failed by + * lack of valid measurements + * @return VL53L1_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH means that the target + * distance combined to the number of loops performed in the calibration lead to + * an internal overflow. Try to reduce the distance of the target (140 mm) + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_PerformOffsetPerVcselCalibration(VL53L1_DEV Dev, + int32_t CalDistanceMilliMeter); +/** + * @brief Sets the Calibration Data. + * + * @par Function Description + * This function set all the Calibration Data issued from the functions + * @a VL53L1_PerformRefSpadManagement(), @a VL53L1_PerformXTalkCalibration, + * @a VL53L1_PerformOffsetCalibration() + * + * @note This function doesn't Accesses the device + * + * @param Dev Device Handle + * @param *pCalibrationData Pointer to Calibration data to be set. + * @return VL53L1_ERROR_NONE Success + * @return VL53L1_ERROR_INVALID_PARAMS pCalibrationData points to an older + * version of the inner structure. Need for support to convert its content. + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetCalibrationData(VL53L1_DEV Dev, + VL53L1_CalibrationData_t *pCalibrationData); + +/** + * @brief Gets the Calibration Data. + * + * @par Function Description + * This function get all the Calibration Data issued from the functions + * @a VL53L1_PerformRefSpadManagement(), @a VL53L1_PerformXTalkCalibration, + * @a VL53L1_PerformOffsetCalibration() + * + * @note This function doesn't Accesses the device + * + * @param Dev Device Handle + * @param *pCalibrationData pointer where to store Calibration + * data. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetCalibrationData(VL53L1_DEV Dev, + VL53L1_CalibrationData_t *pCalibrationData); + +/** + * @brief Sets the Zone Calibration Data. + * + * @par Function Description + * This function set all the Zone nCalibration Data issued from the functions + * @a VL53L1_PerformOffsetCalibration() in multi zone + * + * @note This function doesn't Accesses the device + * + * @param Dev Device Handle + * @param *pZoneCalibrationData Pointer to Zone Calibration data to be + * set. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_SetZoneCalibrationData(VL53L1_DEV Dev, + VL53L1_ZoneCalibrationData_t *pZoneCalibrationData); + +/** + * @brief Gets the Zone Calibration Data. + * + * @par Function Description + * This function get all the Zone Calibration Data issued from the functions + * @a VL53L1_PerformOffsetCalibration() + * + * @note This function doesn't Accesses the device + * + * @param Dev Device Handle + * @param *pZoneCalibrationData pointer where to store Zone Calibration + * data. + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetZoneCalibrationData(VL53L1_DEV Dev, + VL53L1_ZoneCalibrationData_t *pZoneCalibrationData); +/** + * @brief Gets the optical center. + * + * @par Function Description + * This function get the optical center issued from the nvm set at FTM stage + * expressed in the same coordinate system as the ROI are + * + * @note This function doesn't Accesses the device + * + * @param Dev Device Handle + * @param pOpticalCenterX pointer to the X position of center + * in 16.16 fix point + * @param pOpticalCenterY pointer to the Y position of center + * in 16.16 fix point + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_GetOpticalCenter(VL53L1_DEV Dev, + FixPoint1616_t *pOpticalCenterX, + FixPoint1616_t *pOpticalCenterY); + +/** @} VL53L1_Calibration_group */ + +/** @defgroup VL53L1_Thresholds_group VL53L1 IRQ Triggered events Functions + * @brief Functions used to configure interrupt to be triggered only when + * a measurement satisfies some thresholds parameters + * @{ + */ + +/** + * @brief Configure the interrupt config, from the given structure + * + * @param[in] Dev : Device Handle + * @param[in] pConfig : pointer to configuration structure + */ + +VL53L1_Error VL53L1_SetThresholdConfig(VL53L1_DEV Dev, + VL53L1_DetectionConfig_t *pConfig); + +/** + * @brief Retrieves the interrupt config structure currently programmed + * into the API + * + * @param[in] Dev : Device Handle + * @param[out] pConfig : pointer to configuration structure + */ + +VL53L1_Error VL53L1_GetThresholdConfig(VL53L1_DEV Dev, + VL53L1_DetectionConfig_t *pConfig); + + +/** @} VL53L1_Thresholds_group */ + + +/** @} VL53L1_cut11_group */ + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L1_API_H_ */ diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_api_calibration.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_api_calibration.h new file mode 100644 index 000000000000..6bce3dd06994 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_api_calibration.h @@ -0,0 +1,137 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#ifndef _VL53L1_API_CALIBRATION_H_ +#define _VL53L1_API_CALIBRATION_H_ + +#include "vl53l1_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + +VL53L1_Error VL53L1_run_ref_spad_char(VL53L1_DEV Dev, + VL53L1_Error * pcal_status); + + + + +VL53L1_Error VL53L1_run_device_test( + VL53L1_DEV Dev, + VL53L1_DeviceTestMode device_test_mode); + + + + +VL53L1_Error VL53L1_run_spad_rate_map( + VL53L1_DEV Dev, + VL53L1_DeviceTestMode device_test_mode, + VL53L1_DeviceSscArray array_select, + uint32_t ssc_config_timeout_us, + VL53L1_spad_rate_data_t *pspad_rate_data); + + + + +VL53L1_Error VL53L1_run_xtalk_extraction( + VL53L1_DEV Dev, + VL53L1_Error *pcal_status); + + + +VL53L1_Error VL53L1_get_and_avg_xtalk_samples( + VL53L1_DEV Dev, + uint8_t num_of_samples, + uint8_t measurement_mode, + int16_t xtalk_filter_thresh_max_mm, + int16_t xtalk_filter_thresh_min_mm, + uint16_t xtalk_max_valid_rate_kcps, + uint8_t xtalk_result_id, + uint8_t xtalk_histo_id, + VL53L1_xtalk_range_results_t *pxtalk_results, + VL53L1_histogram_bin_data_t *psum_histo, + VL53L1_histogram_bin_data_t *pavg_histo); + + + +VL53L1_Error VL53L1_run_offset_calibration( + VL53L1_DEV Dev, + int16_t cal_distance_mm, + uint16_t cal_reflectance_pc, + VL53L1_Error *pcal_status); + + + + +VL53L1_Error VL53L1_run_phasecal_average( + VL53L1_DEV Dev, + uint8_t measurement_mode, + uint8_t phasecal_result__vcsel_start, + uint16_t phasecal_num_of_samples, + VL53L1_range_results_t *prange_results, + uint16_t *pphasecal_result__reference_phase, + uint16_t *pzero_distance_phase); + + + + +VL53L1_Error VL53L1_run_zone_calibration( + VL53L1_DEV Dev, + VL53L1_DevicePresetModes device_preset_mode, + VL53L1_DeviceZonePreset zone_preset, + VL53L1_zone_config_t *pzone_cfg, + int16_t cal_distance_mm, + uint16_t cal_reflectance_pc, + VL53L1_Error *pcal_status); + + + + +void VL53L1_hist_xtalk_extract_data_init( + VL53L1_hist_xtalk_extract_data_t *pxtalk_data); + + + +VL53L1_Error VL53L1_hist_xtalk_extract_update( + int16_t target_distance_mm, + uint16_t target_width_oversize, + VL53L1_histogram_bin_data_t *phist_bins, + VL53L1_hist_xtalk_extract_data_t *pxtalk_data); + + + +VL53L1_Error VL53L1_hist_xtalk_extract_fini( + VL53L1_histogram_bin_data_t *phist_bins, + VL53L1_hist_xtalk_extract_data_t *pxtalk_data, + VL53L1_xtalk_calibration_results_t *pxtalk_cal, + VL53L1_xtalk_histogram_shape_t *pxtalk_shape); + + + + +VL53L1_Error VL53L1_run_hist_xtalk_extraction( + VL53L1_DEV Dev, + int16_t cal_distance_mm, + VL53L1_Error *pcal_status); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_api_core.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_api_core.h new file mode 100644 index 000000000000..4a90a269ec40 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_api_core.h @@ -0,0 +1,680 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#ifndef _VL53L1_API_CORE_H_ +#define _VL53L1_API_CORE_H_ + +#include "vl53l1_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + +VL53L1_Error VL53L1_get_version( + VL53L1_DEV Dev, + VL53L1_ll_version_t *pversion); + + + + +VL53L1_Error VL53L1_get_device_firmware_version( + VL53L1_DEV Dev, + uint16_t *pfw_version); + + + + +VL53L1_Error VL53L1_data_init( + VL53L1_DEV Dev, + uint8_t read_p2p_data); + + + + +VL53L1_Error VL53L1_read_p2p_data( + VL53L1_DEV Dev); + + + + +VL53L1_Error VL53L1_software_reset( + VL53L1_DEV Dev); + + + + +VL53L1_Error VL53L1_set_part_to_part_data( + VL53L1_DEV Dev, + VL53L1_calibration_data_t *pcal_data); + + + + +VL53L1_Error VL53L1_get_part_to_part_data( + VL53L1_DEV Dev, + VL53L1_calibration_data_t *pcal_data); + + + + +VL53L1_Error VL53L1_get_tuning_debug_data( + VL53L1_DEV Dev, + VL53L1_tuning_parameters_t *ptun_data); + + + + +VL53L1_Error VL53L1_set_inter_measurement_period_ms( + VL53L1_DEV Dev, + uint32_t inter_measurement_period_ms); + + + + +VL53L1_Error VL53L1_get_inter_measurement_period_ms( + VL53L1_DEV Dev, + uint32_t *pinter_measurement_period_ms); + + + + +VL53L1_Error VL53L1_set_timeouts_us( + VL53L1_DEV Dev, + uint32_t phasecal_config_timeout_us, + uint32_t mm_config_timeout_us, + uint32_t range_config_timeout_us); + + + + +VL53L1_Error VL53L1_get_timeouts_us( + VL53L1_DEV Dev, + uint32_t *pphasecal_config_timeout_us, + uint32_t *pmm_config_timeout_us, + uint32_t *prange_config_timeout_us); + + + + +VL53L1_Error VL53L1_set_calibration_repeat_period( + VL53L1_DEV Dev, + uint16_t cal_config__repeat_period); + + + + +VL53L1_Error VL53L1_get_calibration_repeat_period( + VL53L1_DEV Dev, + uint16_t *pcal_config__repeat_period); + + + + +VL53L1_Error VL53L1_set_sequence_config_bit( + VL53L1_DEV Dev, + VL53L1_DeviceSequenceConfig bit_id, + uint8_t value); + + + + +VL53L1_Error VL53L1_get_sequence_config_bit( + VL53L1_DEV Dev, + VL53L1_DeviceSequenceConfig bit_id, + uint8_t *pvalue); + + + + +VL53L1_Error VL53L1_set_interrupt_polarity( + VL53L1_DEV Dev, + VL53L1_DeviceInterruptPolarity interrupt_polarity); + + + + +VL53L1_Error VL53L1_get_interrupt_polarity( + VL53L1_DEV Dev, + VL53L1_DeviceInterruptPolarity *pinterrupt_polarity); + + + +VL53L1_Error VL53L1_get_refspadchar_config_struct( + VL53L1_DEV Dev, + VL53L1_refspadchar_config_t *pdata); + + + +VL53L1_Error VL53L1_set_refspadchar_config_struct( + VL53L1_DEV Dev, + VL53L1_refspadchar_config_t *pdata); + + + +VL53L1_Error VL53L1_set_range_ignore_threshold( + VL53L1_DEV Dev, + uint8_t range_ignore_thresh_mult, + uint16_t range_ignore_threshold_mcps); + + + +VL53L1_Error VL53L1_get_range_ignore_threshold( + VL53L1_DEV Dev, + uint8_t *prange_ignore_thresh_mult, + uint16_t *prange_ignore_threshold_mcps_internal, + uint16_t *prange_ignore_threshold_mcps_current); + + + + +VL53L1_Error VL53L1_set_user_zone( + VL53L1_DEV Dev, + VL53L1_user_zone_t *puser_zone); + + + + +VL53L1_Error VL53L1_get_user_zone( + VL53L1_DEV Dev, + VL53L1_user_zone_t *puser_zone); + + + + +VL53L1_Error VL53L1_get_mode_mitigation_roi( + VL53L1_DEV Dev, + VL53L1_user_zone_t *pmm_roi); + + + + +VL53L1_Error VL53L1_set_zone_config( + VL53L1_DEV Dev, + VL53L1_zone_config_t *pzone_cfg); + + + + +VL53L1_Error VL53L1_get_zone_config( + VL53L1_DEV Dev, + VL53L1_zone_config_t *pzone_cfg); + + + + +VL53L1_Error VL53L1_set_preset_mode( + VL53L1_DEV Dev, + VL53L1_DevicePresetModes device_preset_mode, + uint16_t dss_config__target_total_rate_mcps, + uint32_t phasecal_config_timeout_us, + uint32_t mm_config_timeout_us, + uint32_t range_config_timeout_us, + uint32_t inter_measurement_period_ms); + + + + +VL53L1_Error VL53L1_get_preset_mode_timing_cfg( + VL53L1_DEV Dev, + VL53L1_DevicePresetModes device_preset_mode, + uint16_t *pdss_config__target_total_rate_mcps, + uint32_t *pphasecal_config_timeout_us, + uint32_t *pmm_config_timeout_us, + uint32_t *prange_config_timeout_us); + + + +VL53L1_Error VL53L1_set_zone_preset( + VL53L1_DEV Dev, + VL53L1_DeviceZonePreset zone_preset); + + + +VL53L1_Error VL53L1_enable_xtalk_compensation( + VL53L1_DEV Dev); + + + +VL53L1_Error VL53L1_disable_xtalk_compensation( + VL53L1_DEV Dev); + + + + +void VL53L1_get_xtalk_compensation_enable( + VL53L1_DEV Dev, + uint8_t *pcrosstalk_compensation_enable); + + + +VL53L1_Error VL53L1_init_and_start_range( + VL53L1_DEV Dev, + uint8_t measurement_mode, + VL53L1_DeviceConfigLevel device_config_level); + + + + +VL53L1_Error VL53L1_stop_range( + VL53L1_DEV Dev); + + + + +VL53L1_Error VL53L1_get_measurement_results( + VL53L1_DEV Dev, + VL53L1_DeviceResultsLevel device_result_level); + + + + +VL53L1_Error VL53L1_get_device_results( + VL53L1_DEV Dev, + VL53L1_DeviceResultsLevel device_result_level, + VL53L1_range_results_t *prange_results); + + + + +VL53L1_Error VL53L1_clear_interrupt_and_enable_next_range( + VL53L1_DEV Dev, + uint8_t measurement_mode); + + + + +VL53L1_Error VL53L1_get_histogram_bin_data( + VL53L1_DEV Dev, + VL53L1_histogram_bin_data_t *phist_data); + + + + +void VL53L1_copy_sys_and_core_results_to_range_results( + int32_t gain_factor, + VL53L1_system_results_t *psys, + VL53L1_core_results_t *pcore, + VL53L1_range_results_t *presults); + + + +VL53L1_Error VL53L1_set_zone_dss_config( + VL53L1_DEV Dev, + VL53L1_zone_private_dyn_cfg_t *pzone_dyn_cfg); + + + + +VL53L1_Error VL53L1_calc_ambient_dmax( + VL53L1_DEV Dev, + uint16_t target_reflectance, + int16_t *pambient_dmax_mm); + + + + +VL53L1_Error VL53L1_set_GPIO_interrupt_config( + VL53L1_DEV Dev, + VL53L1_GPIO_Interrupt_Mode intr_mode_distance, + VL53L1_GPIO_Interrupt_Mode intr_mode_rate, + uint8_t intr_new_measure_ready, + uint8_t intr_no_target, + uint8_t intr_combined_mode, + uint16_t thresh_distance_high, + uint16_t thresh_distance_low, + uint16_t thresh_rate_high, + uint16_t thresh_rate_low + ); + + + +VL53L1_Error VL53L1_set_GPIO_interrupt_config_struct( + VL53L1_DEV Dev, + VL53L1_GPIO_interrupt_config_t intconf); + + + +VL53L1_Error VL53L1_get_GPIO_interrupt_config( + VL53L1_DEV Dev, + VL53L1_GPIO_interrupt_config_t *pintconf); + + + + +VL53L1_Error VL53L1_set_dmax_mode( + VL53L1_DEV Dev, + VL53L1_DeviceDmaxMode dmax_mode); + + + +VL53L1_Error VL53L1_get_dmax_mode( + VL53L1_DEV Dev, + VL53L1_DeviceDmaxMode *pdmax_mode); + + + + +VL53L1_Error VL53L1_get_dmax_calibration_data( + VL53L1_DEV Dev, + VL53L1_DeviceDmaxMode dmax_mode, + uint8_t zone_id, + VL53L1_dmax_calibration_data_t *pdmax_cal); + + + + +VL53L1_Error VL53L1_set_hist_dmax_config( + VL53L1_DEV Dev, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg); + + + +VL53L1_Error VL53L1_get_hist_dmax_config( + VL53L1_DEV Dev, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg); + + + + +VL53L1_Error VL53L1_set_offset_calibration_mode( + VL53L1_DEV Dev, + VL53L1_OffsetCalibrationMode offset_cal_mode); + + + + +VL53L1_Error VL53L1_get_offset_calibration_mode( + VL53L1_DEV Dev, + VL53L1_OffsetCalibrationMode *poffset_cal_mode); + + + + +VL53L1_Error VL53L1_set_offset_correction_mode( + VL53L1_DEV Dev, + VL53L1_OffsetCalibrationMode offset_cor_mode); + + + + +VL53L1_Error VL53L1_get_offset_correction_mode( + VL53L1_DEV Dev, + VL53L1_OffsetCorrectionMode *poffset_cor_mode); + + + + +VL53L1_Error VL53L1_set_zone_calibration_data( + VL53L1_DEV Dev, + VL53L1_zone_calibration_results_t *pzone_cal); + + + + +VL53L1_Error VL53L1_get_zone_calibration_data( + VL53L1_DEV Dev, + VL53L1_zone_calibration_results_t *pzone_cal); + + + + +VL53L1_Error VL53L1_get_lite_xtalk_margin_kcps( + VL53L1_DEV Dev, + int16_t *pxtalk_margin); + + + +VL53L1_Error VL53L1_set_lite_xtalk_margin_kcps( + VL53L1_DEV Dev, + int16_t xtalk_margin); + + + + +VL53L1_Error VL53L1_get_histogram_xtalk_margin_kcps( + VL53L1_DEV Dev, + int16_t *pxtalk_margin); + + + +VL53L1_Error VL53L1_set_histogram_xtalk_margin_kcps( + VL53L1_DEV Dev, + int16_t xtalk_margin); + + + +VL53L1_Error VL53L1_get_histogram_phase_consistency( + VL53L1_DEV Dev, + uint8_t *pphase_consistency); + + + +VL53L1_Error VL53L1_set_histogram_phase_consistency( + VL53L1_DEV Dev, + uint8_t phase_consistency); + + + +VL53L1_Error VL53L1_get_histogram_event_consistency( + VL53L1_DEV Dev, + uint8_t *pevent_consistency); + + + +VL53L1_Error VL53L1_set_histogram_event_consistency( + VL53L1_DEV Dev, + uint8_t event_consistency); + + + +VL53L1_Error VL53L1_get_histogram_ambient_threshold_sigma( + VL53L1_DEV Dev, + uint8_t *pamb_thresh_sigma); + + + +VL53L1_Error VL53L1_set_histogram_ambient_threshold_sigma( + VL53L1_DEV Dev, + uint8_t amb_thresh_sigma); + + + +VL53L1_Error VL53L1_get_lite_min_count_rate( + VL53L1_DEV Dev, + uint16_t *plite_mincountrate); + + + + +VL53L1_Error VL53L1_set_lite_min_count_rate( + VL53L1_DEV Dev, + uint16_t lite_mincountrate); + + + + + +VL53L1_Error VL53L1_get_lite_sigma_threshold( + VL53L1_DEV Dev, + uint16_t *plite_sigma); + + + + +VL53L1_Error VL53L1_set_lite_sigma_threshold( + VL53L1_DEV Dev, + uint16_t lite_sigma); + + + + +VL53L1_Error VL53L1_restore_xtalk_nvm_default( + VL53L1_DEV Dev); + + + +VL53L1_Error VL53L1_get_xtalk_detect_config( + VL53L1_DEV Dev, + int16_t *pmax_valid_range_mm, + int16_t *pmin_valid_range_mm, + uint16_t *pmax_valid_rate_kcps, + uint16_t *pmax_sigma_mm); + + + +VL53L1_Error VL53L1_set_xtalk_detect_config( + VL53L1_DEV Dev, + int16_t max_valid_range_mm, + int16_t min_valid_range_mm, + uint16_t max_valid_rate_kcps, + uint16_t max_sigma_mm); + + + +VL53L1_Error VL53L1_get_target_order_mode( + VL53L1_DEV Dev, + VL53L1_HistTargetOrder *phist_target_order); + + + +VL53L1_Error VL53L1_set_target_order_mode( + VL53L1_DEV Dev, + VL53L1_HistTargetOrder hist_target_order); + + + + +VL53L1_Error VL53L1_set_dmax_reflectance_values( + VL53L1_DEV Dev, + VL53L1_dmax_reflectance_array_t *pdmax_reflectances); + + + +VL53L1_Error VL53L1_get_dmax_reflectance_values( + VL53L1_DEV Dev, + VL53L1_dmax_reflectance_array_t *pdmax_reflectances); + + + +VL53L1_Error VL53L1_set_vhv_config( + VL53L1_DEV Dev, + uint8_t vhv_init_en, + uint8_t vhv_init_value); + + + +VL53L1_Error VL53L1_get_vhv_config( + VL53L1_DEV Dev, + uint8_t *pvhv_init_en, + uint8_t *pvhv_init_value); + + + +VL53L1_Error VL53L1_set_vhv_loopbound( + VL53L1_DEV Dev, + uint8_t vhv_loopbound); + + + +VL53L1_Error VL53L1_get_vhv_loopbound( + VL53L1_DEV Dev, + uint8_t *pvhv_loopbound); + + + +VL53L1_Error VL53L1_get_tuning_parm( + VL53L1_DEV Dev, + VL53L1_TuningParms tuning_parm_key, + int32_t *ptuning_parm_value); + + + +VL53L1_Error VL53L1_set_tuning_parm( + VL53L1_DEV Dev, + VL53L1_TuningParms tuning_parm_key, + int32_t tuning_parm_value); + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_enable( + VL53L1_DEV Dev + ); + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_disable( + VL53L1_DEV Dev + ); + + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_apply_enable( + VL53L1_DEV Dev + ); + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_apply_disable( + VL53L1_DEV Dev + ); + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_single_apply_enable( + VL53L1_DEV Dev + ); + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_single_apply_disable( + VL53L1_DEV Dev + ); + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_set_scalers( + VL53L1_DEV Dev, + int16_t x_scaler_in, + int16_t y_scaler_in, + uint8_t user_scaler_set_in + ); + + + +VL53L1_Error VL53L1_get_current_xtalk_settings( + VL53L1_DEV Dev, + VL53L1_xtalk_calibration_results_t *pxtalk + ); + + + +VL53L1_Error VL53L1_set_current_xtalk_settings( + VL53L1_DEV Dev, + VL53L1_xtalk_calibration_results_t *pxtalk + ); + +VL53L1_Error VL53L1_load_patch(VL53L1_DEV Dev); + +VL53L1_Error VL53L1_unload_patch(VL53L1_DEV Dev); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_api_debug.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_api_debug.h new file mode 100644 index 000000000000..c3ce689c719f --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_api_debug.h @@ -0,0 +1,341 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#ifndef _VL53L1_API_DEBUG_H_ +#define _VL53L1_API_DEBUG_H_ + +#include "vl53l1_platform.h" +#include "vl53l1_nvm_structs.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + + +VL53L1_Error VL53L1_decode_calibration_data_buffer( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_calibration_data_t *pdata); + + + + + + +VL53L1_Error VL53L1_get_nvm_debug_data( + VL53L1_DEV Dev, + VL53L1_decoded_nvm_data_t *pdata); + + + +VL53L1_Error VL53L1_get_histogram_debug_data( + VL53L1_DEV Dev, + VL53L1_histogram_bin_data_t *pdata); + + + + + + +VL53L1_Error VL53L1_get_additional_data( + VL53L1_DEV Dev, + VL53L1_additional_data_t *pdata); + + + + + + +VL53L1_Error VL53L1_get_xtalk_debug_data( + VL53L1_DEV Dev, + VL53L1_xtalk_debug_data_t *pdata); + + + + +VL53L1_Error VL53L1_get_offset_debug_data( + VL53L1_DEV Dev, + VL53L1_offset_debug_data_t *pdata); + +#ifdef VL53L1_LOG_ENABLE + + + +void VL53L1_signed_fixed_point_sprintf( + int32_t fp_value, + uint8_t frac_bits, + uint16_t buf_size, + char *pbuffer); + + + + +void VL53L1_print_static_nvm_managed( + VL53L1_static_nvm_managed_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_customer_nvm_managed( + VL53L1_customer_nvm_managed_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_nvm_copy_data( + VL53L1_nvm_copy_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_histogram_bin_data( + VL53L1_histogram_bin_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_xtalk_histogram_data( + VL53L1_xtalk_histogram_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_xtalk_histogram_shape_data( + VL53L1_xtalk_histogram_shape_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_range_results( + VL53L1_range_results_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + +void VL53L1_print_range_data( + VL53L1_range_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_offset_range_results( + VL53L1_offset_range_results_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_offset_range_data( + VL53L1_offset_range_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_cal_peak_rate_map( + VL53L1_cal_peak_rate_map_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_additional_offset_cal_data( + VL53L1_additional_offset_cal_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + +void VL53L1_print_additional_data( + VL53L1_additional_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_gain_calibration_data( + VL53L1_gain_calibration_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_zone_calibration_data( + VL53L1_zone_calibration_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_zone_calibration_results( + VL53L1_zone_calibration_results_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_xtalk_range_results( + VL53L1_xtalk_range_results_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_xtalk_range_data( + VL53L1_xtalk_range_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_xtalk_calibration_results( + VL53L1_xtalk_calibration_results_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_xtalk_config( + VL53L1_xtalk_config_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + +void VL53L1_print_xtalk_extract_config( + VL53L1_xtalkextract_config_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + +void VL53L1_print_zone_cal_config( + VL53L1_zonecal_config_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + +void VL53L1_print_offset_cal_config( + VL53L1_offsetcal_config_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_dmax_calibration_data( + VL53L1_dmax_calibration_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_calibration_data( + VL53L1_calibration_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_xtalk_debug_data( + VL53L1_xtalk_debug_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + +void VL53L1_print_offset_debug_data( + VL53L1_offset_debug_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_optical_centre( + VL53L1_optical_centre_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_user_zone( + VL53L1_user_zone_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + +void VL53L1_print_zone_config( + VL53L1_zone_config_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_spad_rate_data( + VL53L1_spad_rate_data_t *pspad_rates, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_spad_rate_map( + VL53L1_spad_rate_data_t *pspad_rates, + char *pprefix, + uint32_t trace_flags); + + +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_api_preset_modes.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_api_preset_modes.h new file mode 100644 index 000000000000..8efc203ca308 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_api_preset_modes.h @@ -0,0 +1,595 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#ifndef _VL53L1_API_PRESET_MODES_H_ +#define _VL53L1_API_PRESET_MODES_H_ + +#include "vl53l1_ll_def.h" +#include "vl53l1_dmax_structs.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + +VL53L1_Error VL53L1_init_refspadchar_config_struct( + VL53L1_refspadchar_config_t *pdata); + + + + +VL53L1_Error VL53L1_init_ssc_config_struct( + VL53L1_ssc_config_t *pdata); + + + + +VL53L1_Error VL53L1_init_xtalk_config_struct( + VL53L1_customer_nvm_managed_t *pnvm, + VL53L1_xtalk_config_t *pdata); + + + +VL53L1_Error VL53L1_init_xtalk_extract_config_struct( + VL53L1_xtalkextract_config_t *pdata); + + + +VL53L1_Error VL53L1_init_offset_cal_config_struct( + VL53L1_offsetcal_config_t *pdata); + + + +VL53L1_Error VL53L1_init_zone_cal_config_struct( + VL53L1_zonecal_config_t *pdata); + + + +VL53L1_Error VL53L1_init_hist_post_process_config_struct( + uint8_t xtalk_compensation_enable, + VL53L1_hist_post_process_config_t *pdata); + + + + +VL53L1_Error VL53L1_init_dmax_calibration_data_struct( + VL53L1_dmax_calibration_data_t *pdata); + + + + +VL53L1_Error VL53L1_init_tuning_parm_storage_struct( + VL53L1_tuning_parm_storage_t *pdata); + + + +VL53L1_Error VL53L1_init_hist_gen3_dmax_config_struct( + VL53L1_hist_gen3_dmax_config_t *pdata); + + + + +VL53L1_Error VL53L1_preset_mode_standard_ranging( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_standard_ranging_short_range( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + +VL53L1_Error VL53L1_preset_mode_standard_ranging_long_range( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + +VL53L1_Error VL53L1_preset_mode_standard_ranging_mm1_cal( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + +VL53L1_Error VL53L1_preset_mode_standard_ranging_mm2_cal( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + +VL53L1_Error VL53L1_preset_mode_timed_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_timed_ranging_short_range( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_timed_ranging_long_range( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_low_power_auto_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg, + VL53L1_low_power_auto_data_t *plpadata); + + + +VL53L1_Error VL53L1_preset_mode_low_power_auto_short_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg, + VL53L1_low_power_auto_data_t *plpadata); + + + +VL53L1_Error VL53L1_preset_mode_low_power_auto_long_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg, + VL53L1_low_power_auto_data_t *plpadata); + + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_with_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_with_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_mm1_cal( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_mm2_cal( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_ref( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_histogram_characterisation( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + +VL53L1_Error VL53L1_preset_mode_histogram_xtalk_planar( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + +VL53L1_Error VL53L1_preset_mode_histogram_xtalk_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + +VL53L1_Error VL53L1_preset_mode_histogram_xtalk_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_histogram_multizone( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_histogram_multizone_short_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_histogram_multizone_long_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_short_timing( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_histogram_long_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_histogram_medium_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_histogram_short_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_special_histogram_short_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_histogram_long_range_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_histogram_long_range_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_histogram_medium_range_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_histogram_medium_range_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_histogram_short_range_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_histogram_short_range_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_olt( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_preset_mode_singleshot_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg); + + + + +void VL53L1_copy_hist_cfg_to_static_cfg( + VL53L1_histogram_config_t *phistogram, + VL53L1_static_config_t *pstatic, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic); + + + +void VL53L1_copy_hist_bins_to_static_cfg( + VL53L1_histogram_config_t *phistogram, + VL53L1_static_config_t *pstatic, + VL53L1_timing_config_t *ptiming); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_api_strings.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_api_strings.h new file mode 100644 index 000000000000..eb9904cead67 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_api_strings.h @@ -0,0 +1,159 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +/** + * @file vl53l1_api_strings.h + * @brief VL53L1 API function declarations for decoding error codes to a + * text strings + */ + + +#ifndef VL53L1_API_STRINGS_H_ +#define VL53L1_API_STRINGS_H_ + +#include "vl53l1_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +/** + * @brief Generates a string for the input device range status code + * + * @param[in] RangeStatus : Device Range AStatus Code + * @param[out] pRangeStatusString : pointer to character buffer + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_get_range_status_string( + uint8_t RangeStatus, + char *pRangeStatusString); + +/** + * @brief Generates an error string for the input PAL error code + * + * @param[in] PalErrorCode : PAL Error Code + * @param[out] pPalErrorString : pointer to character buffer + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_get_pal_error_string( + VL53L1_Error PalErrorCode, + char *pPalErrorString); + +/** + * @brief Generates a string for the input PAL State code + * + * @param[in] PalStateCode : PAL State Code + * @param[out] pPalStateString : pointer to character buffer + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_get_pal_state_string( + VL53L1_State PalStateCode, + char *pPalStateString); + + +/** + * @brief Generates a string for the sequence step Id + * + * @param[in] SequenceStepId : Sequence Step Id + * @param[out] pSequenceStepsString : pointer to character buffer + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_get_sequence_steps_info( + VL53L1_SequenceStepId SequenceStepId, + char *pSequenceStepsString); + +/** + * @brief Generates a string for the limit check Id + * + * @param[in] LimitCheckId : Limit check Id + * @param[out] pLimitCheckString : pointer to character buffer + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_get_limit_check_info(uint16_t LimitCheckId, + char *pLimitCheckString); + +#ifndef VL53L1_USE_EMPTY_STRING + #define VL53L1_STRING_DEVICE_INFO_NAME0 "VL53L1 cut1.0" + #define VL53L1_STRING_DEVICE_INFO_NAME1 "VL53L1 cut1.1" + #define VL53L1_STRING_DEVICE_INFO_TYPE "VL53L1" + + /* Range Status */ + #define VL53L1_STRING_RANGESTATUS_NONE "No Update" + #define VL53L1_STRING_RANGESTATUS_RANGEVALID "Range Valid" + #define VL53L1_STRING_RANGESTATUS_SIGMA "Sigma Fail" + #define VL53L1_STRING_RANGESTATUS_SIGNAL "Signal Fail" + #define VL53L1_STRING_RANGESTATUS_MINRANGE "Min Range Fail" + #define VL53L1_STRING_RANGESTATUS_PHASE "Phase Fail" + #define VL53L1_STRING_RANGESTATUS_HW "Hardware Fail" + + + /* Range Status */ + #define VL53L1_STRING_STATE_POWERDOWN "POWERDOWN State" + #define VL53L1_STRING_STATE_WAIT_STATICINIT \ + "Wait for staticinit State" + #define VL53L1_STRING_STATE_STANDBY "STANDBY State" + #define VL53L1_STRING_STATE_IDLE "IDLE State" + #define VL53L1_STRING_STATE_RUNNING "RUNNING State" + #define VL53L1_STRING_STATE_RESET "RESET State" + #define VL53L1_STRING_STATE_UNKNOWN "UNKNOWN State" + #define VL53L1_STRING_STATE_ERROR "ERROR State" + + + + /* Check Enable */ + #define VL53L1_STRING_CHECKENABLE_SIGMA_FINAL_RANGE \ + "SIGMA FINAL RANGE" + #define VL53L1_STRING_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE \ + "SIGNAL RATE FINAL RANGE" + #define VL53L1_STRING_CHECKENABLE_SIGNAL_MIN_CLIP \ + "SIGNAL MIN CLIP" + #define VL53L1_STRING_CHECKENABLE_RANGE_IGNORE_THRESHOLD \ + "RANGE IGNORE THRESHOLD" + #define VL53L1_STRING_CHECKENABLE_RANGE_PHASE_HIGH \ + "RANGE PHASE HIGH" + #define VL53L1_STRING_CHECKENABLE_RANGE_PHASE_LOW \ + "RANGE PHASE LOW" + #define VL53L1_STRING_CHECKENABLE_RANGE_PHASE_CONSISTENCY \ + "RANGE PHASE CONSISTENCY" + + /* Sequence Step */ + #define VL53L1_STRING_SEQUENCESTEP_VHV "VHV" + #define VL53L1_STRING_SEQUENCESTEP_PHASECAL "PHASE CAL" + #define VL53L1_STRING_SEQUENCESTEP_REFPHASE "REF PHASE" + #define VL53L1_STRING_SEQUENCESTEP_DSS1 "DSS1" + #define VL53L1_STRING_SEQUENCESTEP_DSS2 "DSS2" + #define VL53L1_STRING_SEQUENCESTEP_MM1 "MM1" + #define VL53L1_STRING_SEQUENCESTEP_MM2 "MM2" + #define VL53L1_STRING_SEQUENCESTEP_RANGE "RANGE" +#endif /* VL53L1_USE_EMPTY_STRING */ + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_core.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_core.h new file mode 100644 index 000000000000..dc08caae5277 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_core.h @@ -0,0 +1,748 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#ifndef _VL53L1_CORE_H_ +#define _VL53L1_CORE_H_ + +#include "vl53l1_platform.h" +#include "vl53l1_core_support.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + +void VL53L1_init_version( + VL53L1_DEV Dev); + + + + +void VL53L1_init_ll_driver_state( + VL53L1_DEV Dev, + VL53L1_DeviceState ll_state); + + + + +VL53L1_Error VL53L1_update_ll_driver_rd_state( + VL53L1_DEV Dev); + + + + +VL53L1_Error VL53L1_check_ll_driver_rd_state( + VL53L1_DEV Dev); + + + + +VL53L1_Error VL53L1_update_ll_driver_cfg_state( + VL53L1_DEV Dev); + + + + +void VL53L1_copy_rtn_good_spads_to_buffer( + VL53L1_nvm_copy_data_t *pdata, + uint8_t *pbuffer); + + + + +void VL53L1_init_system_results( + VL53L1_system_results_t *pdata); + + + + +void V53L1_init_zone_results_structure( + uint8_t active_zones, + VL53L1_zone_results_t *pdata); + + + + +void V53L1_init_zone_dss_configs( + VL53L1_DEV Dev); + + + + +void VL53L1_init_histogram_config_structure( + uint8_t even_bin0, + uint8_t even_bin1, + uint8_t even_bin2, + uint8_t even_bin3, + uint8_t even_bin4, + uint8_t even_bin5, + uint8_t odd_bin0, + uint8_t odd_bin1, + uint8_t odd_bin2, + uint8_t odd_bin3, + uint8_t odd_bin4, + uint8_t odd_bin5, + VL53L1_histogram_config_t *pdata); + + + +void VL53L1_init_histogram_multizone_config_structure( + uint8_t even_bin0, + uint8_t even_bin1, + uint8_t even_bin2, + uint8_t even_bin3, + uint8_t even_bin4, + uint8_t even_bin5, + uint8_t odd_bin0, + uint8_t odd_bin1, + uint8_t odd_bin2, + uint8_t odd_bin3, + uint8_t odd_bin4, + uint8_t odd_bin5, + VL53L1_histogram_config_t *pdata); + + + + +void VL53L1_init_xtalk_bin_data_struct( + uint32_t bin_value, + uint16_t VL53L1_p_024, + VL53L1_xtalk_histogram_shape_t *pdata); + + + + +void VL53L1_i2c_encode_uint16_t( + uint16_t ip_value, + uint16_t count, + uint8_t *pbuffer); + + + + +uint16_t VL53L1_i2c_decode_uint16_t( + uint16_t count, + uint8_t *pbuffer); + + + + +void VL53L1_i2c_encode_int16_t( + int16_t ip_value, + uint16_t count, + uint8_t *pbuffer); + + + + +int16_t VL53L1_i2c_decode_int16_t( + uint16_t count, + uint8_t *pbuffer); + + + + +void VL53L1_i2c_encode_uint32_t( + uint32_t ip_value, + uint16_t count, + uint8_t *pbuffer); + + + + +uint32_t VL53L1_i2c_decode_uint32_t( + uint16_t count, + uint8_t *pbuffer); + + + + +uint32_t VL53L1_i2c_decode_with_mask( + uint16_t count, + uint8_t *pbuffer, + uint32_t bit_mask, + uint32_t down_shift, + uint32_t offset); + + + + +void VL53L1_i2c_encode_int32_t( + int32_t ip_value, + uint16_t count, + uint8_t *pbuffer); + + + + +int32_t VL53L1_i2c_decode_int32_t( + uint16_t count, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_start_test( + VL53L1_DEV Dev, + uint8_t test_mode__ctrl); + + + + +VL53L1_Error VL53L1_set_firmware_enable_register( + VL53L1_DEV Dev, + uint8_t value); + + + + +VL53L1_Error VL53L1_enable_firmware( + VL53L1_DEV Dev); + + + + +VL53L1_Error VL53L1_disable_firmware( + VL53L1_DEV Dev); + + + + +VL53L1_Error VL53L1_set_powerforce_register( + VL53L1_DEV Dev, + uint8_t value); + + + + + +VL53L1_Error VL53L1_enable_powerforce( + VL53L1_DEV Dev); + + + +VL53L1_Error VL53L1_disable_powerforce( + VL53L1_DEV Dev); + + + + + +VL53L1_Error VL53L1_clear_interrupt( + VL53L1_DEV Dev); + + + + + +VL53L1_Error VL53L1_force_shadow_stream_count_to_zero( + VL53L1_DEV Dev); + + + + +uint32_t VL53L1_calc_macro_period_us( + uint16_t fast_osc_frequency, + uint8_t VL53L1_p_009); + + + + +uint16_t VL53L1_calc_range_ignore_threshold( + uint32_t central_rate, + int16_t x_gradient, + int16_t y_gradient, + uint8_t rate_mult); + + + + +uint32_t VL53L1_calc_timeout_mclks( + uint32_t timeout_us, + uint32_t macro_period_us); + + + +uint16_t VL53L1_calc_encoded_timeout( + uint32_t timeout_us, + uint32_t macro_period_us); + + + + +uint32_t VL53L1_calc_timeout_us( + uint32_t timeout_mclks, + uint32_t macro_period_us); + + + +uint32_t VL53L1_calc_decoded_timeout_us( + uint16_t timeout_encoded, + uint32_t macro_period_us); + + + + +uint16_t VL53L1_encode_timeout( + uint32_t timeout_mclks); + + + + +uint32_t VL53L1_decode_timeout( + uint16_t encoded_timeout); + + + + +VL53L1_Error VL53L1_calc_timeout_register_values( + uint32_t phasecal_config_timeout_us, + uint32_t mm_config_timeout_us, + uint32_t range_config_timeout_us, + uint16_t fast_osc_frequency, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming); + + + + +uint8_t VL53L1_encode_vcsel_period( + uint8_t VL53L1_p_031); + + + + +uint32_t VL53L1_decode_unsigned_integer( + uint8_t *pbuffer, + uint8_t no_of_bytes); + + + + +void VL53L1_encode_unsigned_integer( + uint32_t ip_value, + uint8_t no_of_bytes, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_hist_copy_and_scale_ambient_info( + VL53L1_zone_hist_info_t *pidata, + VL53L1_histogram_bin_data_t *podata); + + + + +void VL53L1_hist_get_bin_sequence_config( + VL53L1_DEV Dev, + VL53L1_histogram_bin_data_t *pdata); + + + + +VL53L1_Error VL53L1_hist_phase_consistency_check( + VL53L1_DEV Dev, + VL53L1_zone_hist_info_t *phist_prev, + VL53L1_zone_objects_t *prange_prev, + VL53L1_range_results_t *prange_curr); + + + + + + + +VL53L1_Error VL53L1_hist_events_consistency_check( + uint8_t event_sigma, + uint16_t min_effective_spad_count, + VL53L1_zone_hist_info_t *phist_prev, + VL53L1_object_data_t *prange_prev, + VL53L1_range_data_t *prange_curr, + int32_t *pevents_tolerance, + int32_t *pevents_delta, + VL53L1_DeviceError *prange_status); + + + + + + + +VL53L1_Error VL53L1_hist_merged_pulse_check( + int16_t min_max_tolerance_mm, + VL53L1_range_data_t *pdata, + VL53L1_DeviceError *prange_status); + + + + + + +VL53L1_Error VL53L1_hist_xmonitor_consistency_check( + VL53L1_DEV Dev, + VL53L1_zone_hist_info_t *phist_prev, + VL53L1_zone_objects_t *prange_prev, + VL53L1_range_data_t *prange_curr); + + + + + + +VL53L1_Error VL53L1_hist_wrap_dmax( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_histogram_bin_data_t *pcurrent, + int16_t *pwrap_dmax_mm); + + + + +void VL53L1_hist_combine_mm1_mm2_offsets( + int16_t mm1_offset_mm, + int16_t mm2_offset_mm, + uint8_t encoded_mm_roi_centre, + uint8_t encoded_mm_roi_size, + uint8_t encoded_zone_centre, + uint8_t encoded_zone_size, + VL53L1_additional_offset_cal_data_t *pcal_data, + uint8_t *pgood_spads, + uint16_t aperture_attenuation, + int16_t *prange_offset_mm); + + + + +VL53L1_Error VL53L1_hist_xtalk_extract_calc_window( + int16_t target_distance_mm, + uint16_t target_width_oversize, + VL53L1_histogram_bin_data_t *phist_bins, + VL53L1_hist_xtalk_extract_data_t *pxtalk_data); + + + + +VL53L1_Error VL53L1_hist_xtalk_extract_calc_event_sums( + VL53L1_histogram_bin_data_t *phist_bins, + VL53L1_hist_xtalk_extract_data_t *pxtalk_data); + + + + +VL53L1_Error VL53L1_hist_xtalk_extract_calc_rate_per_spad( + VL53L1_hist_xtalk_extract_data_t *pxtalk_data); + + + +VL53L1_Error VL53L1_hist_xtalk_extract_calc_shape( + VL53L1_hist_xtalk_extract_data_t *pxtalk_data, + VL53L1_xtalk_histogram_shape_t *pxtalk_shape); + + + +VL53L1_Error VL53L1_hist_xtalk_shape_model( + uint16_t events_per_bin, + uint16_t pulse_centre, + uint16_t pulse_width, + VL53L1_xtalk_histogram_shape_t *pxtalk_shape); + + + + +uint16_t VL53L1_hist_xtalk_shape_model_interp( + uint16_t events_per_bin, + uint32_t phase_delta); + + + + +void VL53L1_spad_number_to_byte_bit_index( + uint8_t spad_number, + uint8_t *pbyte_index, + uint8_t *pbit_index, + uint8_t *pbit_mask); + + + + +void VL53L1_encode_row_col( + uint8_t row, + uint8_t col, + uint8_t *pspad_number); + + + + +void VL53L1_decode_zone_size( + uint8_t encoded_xy_size, + uint8_t *pwidth, + uint8_t *pheight); + + + + +void VL53L1_encode_zone_size( + uint8_t width, + uint8_t height, + uint8_t *pencoded_xy_size); + + + + +void VL53L1_decode_zone_limits( + uint8_t encoded_xy_centre, + uint8_t encoded_xy_size, + int16_t *px_ll, + int16_t *py_ll, + int16_t *px_ur, + int16_t *py_ur); + + + + +uint8_t VL53L1_is_aperture_location( + uint8_t row, + uint8_t col); + + + + +void VL53L1_calc_max_effective_spads( + uint8_t encoded_zone_centre, + uint8_t encoded_zone_size, + uint8_t *pgood_spads, + uint16_t aperture_attenuation, + uint16_t *pmax_effective_spads); + + + + +void VL53L1_calc_mm_effective_spads( + uint8_t encoded_mm_roi_centre, + uint8_t encoded_mm_roi_size, + uint8_t encoded_zone_centre, + uint8_t encoded_zone_size, + uint8_t *pgood_spads, + uint16_t aperture_attenuation, + uint16_t *pmm_inner_effective_spads, + uint16_t *pmm_outer_effective_spads); + + + + +void VL53L1_hist_copy_results_to_sys_and_core( + VL53L1_histogram_bin_data_t *pbins, + VL53L1_range_results_t *phist, + VL53L1_system_results_t *psys, + VL53L1_core_results_t *pcore); + + + + +VL53L1_Error VL53L1_sum_histogram_data( + VL53L1_histogram_bin_data_t *phist_input, + VL53L1_histogram_bin_data_t *phist_output); + + + + +VL53L1_Error VL53L1_avg_histogram_data( + uint8_t no_of_samples, + VL53L1_histogram_bin_data_t *phist_sum, + VL53L1_histogram_bin_data_t *phist_avg); + + + + +VL53L1_Error VL53L1_save_cfg_data( + VL53L1_DEV Dev); + + + + +VL53L1_Error VL53L1_dynamic_zone_update( + VL53L1_DEV Dev, + VL53L1_range_results_t *presults); + + + + +VL53L1_Error VL53L1_update_internal_stream_counters( + VL53L1_DEV Dev, + uint8_t external_stream_count, + uint8_t *pinternal_stream_count, + uint8_t *pinternal_stream_count_val + ); + + + +VL53L1_Error VL53L1_multizone_hist_bins_update( + VL53L1_DEV Dev); + + + +VL53L1_Error VL53L1_set_histogram_multizone_initial_bin_config( + VL53L1_zone_config_t *pzone_cfg, + VL53L1_histogram_config_t *phist_cfg, + VL53L1_histogram_config_t *pmulti_hist + ); + + + +uint8_t VL53L1_encode_GPIO_interrupt_config( + VL53L1_GPIO_interrupt_config_t *pintconf); + + + +VL53L1_GPIO_interrupt_config_t VL53L1_decode_GPIO_interrupt_config( + uint8_t system__interrupt_config); + + + +VL53L1_Error VL53L1_set_GPIO_distance_threshold( + VL53L1_DEV Dev, + uint16_t threshold_high, + uint16_t threshold_low); + + + +VL53L1_Error VL53L1_set_GPIO_rate_threshold( + VL53L1_DEV Dev, + uint16_t threshold_high, + uint16_t threshold_low); + + + +VL53L1_Error VL53L1_set_GPIO_thresholds_from_struct( + VL53L1_DEV Dev, + VL53L1_GPIO_interrupt_config_t *pintconf); + + + + + +VL53L1_Error VL53L1_set_ref_spad_char_config( + VL53L1_DEV Dev, + uint8_t vcsel_period_a, + uint32_t phasecal_timeout_us, + uint16_t total_rate_target_mcps, + uint16_t max_count_rate_rtn_limit_mcps, + uint16_t min_count_rate_rtn_limit_mcps, + uint16_t fast_osc_frequency); + + + + +VL53L1_Error VL53L1_set_ssc_config( + VL53L1_DEV Dev, + VL53L1_ssc_config_t *pssc_cfg, + uint16_t fast_osc_frequency); + + + + +VL53L1_Error VL53L1_get_spad_rate_data( + VL53L1_DEV Dev, + VL53L1_spad_rate_data_t *pspad_rates); + + + +uint32_t VL53L1_calc_crosstalk_plane_offset_with_margin( + uint32_t plane_offset_kcps, + int16_t margin_offset_kcps); + + + +VL53L1_Error VL53L1_low_power_auto_data_init( + VL53L1_DEV Dev + ); + + + +VL53L1_Error VL53L1_low_power_auto_data_stop_range( + VL53L1_DEV Dev + ); + + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_calc_required_samples( + VL53L1_DEV Dev + ); + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_calc_new_xtalk( + VL53L1_DEV Dev, + uint32_t xtalk_offset_out, + VL53L1_smudge_corrector_config_t *pconfig, + VL53L1_smudge_corrector_data_t *pout, + uint8_t add_smudge, + uint8_t soft_update + ); + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_corrector( + VL53L1_DEV Dev + ); + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_data_init( + VL53L1_DEV Dev + ); + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_output_init( + VL53L1_LLDriverResults_t *pres + ); + + + +VL53L1_Error VL53L1_xtalk_cal_data_init( + VL53L1_DEV Dev + ); + + + +VL53L1_Error VL53L1_config_low_power_auto_mode( + VL53L1_general_config_t *pgeneral, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_low_power_auto_data_t *plpadata + ); + + + +VL53L1_Error VL53L1_low_power_auto_setup_manual_calibration( + VL53L1_DEV Dev); + + + +VL53L1_Error VL53L1_low_power_auto_update_DSS( + VL53L1_DEV Dev); + + +VL53L1_Error VL53L1_compute_histo_merge_nb( + VL53L1_DEV Dev, uint8_t *histo_merge_nb); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_core_support.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_core_support.h new file mode 100644 index 000000000000..644c1ebc6699 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_core_support.h @@ -0,0 +1,152 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#ifndef _VL53L1_CORE_SUPPORT_H_ +#define _VL53L1_CORE_SUPPORT_H_ + +#include "vl53l1_types.h" +#include "vl53l1_hist_structs.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + +uint32_t VL53L1_calc_pll_period_us( + uint16_t fast_osc_frequency); + + + + + +uint32_t VL53L1_duration_maths( + uint32_t pll_period_us, + uint32_t vcsel_parm_pclks, + uint32_t window_vclks, + uint32_t periods_elapsed_mclks); + + + +uint32_t VL53L1_events_per_spad_maths( + int32_t VL53L1_p_013, + uint16_t num_spads, + uint32_t duration); + + + + +uint32_t VL53L1_isqrt( + uint32_t num); + + + + +void VL53L1_hist_calc_zero_distance_phase( + VL53L1_histogram_bin_data_t *pdata); + + + + +void VL53L1_hist_estimate_ambient_from_thresholded_bins( + int32_t ambient_threshold_sigma, + VL53L1_histogram_bin_data_t *pdata); + + + + +void VL53L1_hist_remove_ambient_bins( + VL53L1_histogram_bin_data_t *pdata); + + + + +uint32_t VL53L1_calc_pll_period_mm( + uint16_t fast_osc_frequency); + + + + +uint16_t VL53L1_rate_maths( + int32_t VL53L1_p_008, + uint32_t time_us); + + + + +uint16_t VL53L1_rate_per_spad_maths( + uint32_t frac_bits, + uint32_t peak_count_rate, + uint16_t num_spads, + uint32_t max_output_value); + + + + +int32_t VL53L1_range_maths( + uint16_t fast_osc_frequency, + uint16_t VL53L1_p_017, + uint16_t zero_distance_phase, + uint8_t fractional_bits, + int32_t gain_factor, + int32_t range_offset_mm); + + + + +uint8_t VL53L1_decode_vcsel_period( + uint8_t vcsel_period_reg); + + + +void VL53L1_copy_xtalk_bin_data_to_histogram_data_struct( + VL53L1_xtalk_histogram_shape_t *pxtalk, + VL53L1_histogram_bin_data_t *phist); + + + + +void VL53L1_init_histogram_bin_data_struct( + int32_t bin_value, + uint16_t VL53L1_p_024, + VL53L1_histogram_bin_data_t *pdata); + + + + +void VL53L1_decode_row_col( + uint8_t spad_number, + uint8_t *prow, + uint8_t *pcol); + + + + +void VL53L1_hist_find_min_max_bin_values( + VL53L1_histogram_bin_data_t *pdata); + + + + +void VL53L1_hist_estimate_ambient_from_ambient_bins( + VL53L1_histogram_bin_data_t *pdata); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_def.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_def.h new file mode 100644 index 000000000000..d59e75ab1e81 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_def.h @@ -0,0 +1,802 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +/** + * @file vl53l1_def.h + * + * @brief Type definitions for VL53L1 API. + * + */ + + +#ifndef _VL53L1_DEF_H_ +#define _VL53L1_DEF_H_ + +#include "vl53l1_ll_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup VL53L1_globaldefine_group VL53L1 Defines + * @brief VL53L1 Defines + * @{ + */ + + +/** VL53L1 IMPLEMENTATION major version */ +#define VL53L1_IMPLEMENTATION_VER_MAJOR 6 +/** VL53L1 IMPLEMENTATION minor version */ +#define VL53L1_IMPLEMENTATION_VER_MINOR 6 +/** VL53L1 IMPLEMENTATION sub version */ +#define VL53L1_IMPLEMENTATION_VER_SUB 11 +/** VL53L1 IMPLEMENTATION sub version */ +#define VL53L1_IMPLEMENTATION_VER_REVISION 2576 + +/**************************************** + * PRIVATE define do not edit + ****************************************/ + +/** @brief Defines the parameters of the Get Version Functions + */ +typedef struct { + uint32_t revision; /*!< revision number */ + uint8_t major; /*!< major number */ + uint8_t minor; /*!< minor number */ + uint8_t build; /*!< build number */ +} VL53L1_Version_t; + + +#define VL53L1_DEVINFO_STRLEN 32 + +/** @brief Defines the parameters of the Get Device Info Functions + */ +typedef struct { + char Name[VL53L1_DEVINFO_STRLEN]; + /*!< Full Name of the Device e.g. VL53L1 cut1.1 */ + char Type[VL53L1_DEVINFO_STRLEN]; + /*!< Type of the Device e.g VL53L1 */ + char ProductId[VL53L1_DEVINFO_STRLEN]; + /*!< Product Identifier String + * @warning Not yet implemented + */ + uint8_t ProductType; + /*!< Product Type, VL53L1 = 0xCC, VL53L3 = 0xAA + * Stands as module_type in the datasheet + */ + uint8_t ProductRevisionMajor; + /*!< Product revision major */ + uint8_t ProductRevisionMinor; + /*!< Product revision minor */ +} VL53L1_DeviceInfo_t; + + + +/** @defgroup VL53L1_define_PresetModes_group Defines Preset modes + * Defines all possible preset modes for the device + * @{ + */ +typedef uint8_t VL53L1_PresetModes; + +#define VL53L1_PRESETMODE_RANGING ((VL53L1_PresetModes) 1) +#define VL53L1_PRESETMODE_MULTIZONES_SCANNING ((VL53L1_PresetModes) 2) +#define VL53L1_PRESETMODE_AUTONOMOUS ((VL53L1_PresetModes) 3) +#define VL53L1_PRESETMODE_LITE_RANGING ((VL53L1_PresetModes) 4) +#define VL53L1_PRESETMODE_OLT ((VL53L1_PresetModes) 7) +#define VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS ((VL53L1_PresetModes) 8) +#define VL53L1_PRESETMODE_PROXY_RANGING_MODE ((VL53L1_PresetModes) 9) + + /* ... Modes to be added depending on device */ +/** @} VL53L1_define_PresetModes_group */ + + +/** @defgroup VL53L1_define_DistanceModes_group Defines Distance modes + * Defines all possible Distance modes for the device + * @{ + */ +typedef uint8_t VL53L1_DistanceModes; + +#define VL53L1_DISTANCEMODE_SHORT ((VL53L1_DistanceModes) 1) +#define VL53L1_DISTANCEMODE_MEDIUM ((VL53L1_DistanceModes) 2) +#define VL53L1_DISTANCEMODE_LONG ((VL53L1_DistanceModes) 3) +/** @} VL53L1_define_DistanceModes_group */ + +/** @defgroup VL53L1_define_OutputModes_group Defines Output modes + * Defines all possible Output modes for the device + * @{ + */ +typedef uint8_t VL53L1_OutputModes; + +#define VL53L1_OUTPUTMODE_NEAREST ((VL53L1_OutputModes) 1) +#define VL53L1_OUTPUTMODE_STRONGEST ((VL53L1_OutputModes) 2) + +/** @} VL53L1_define_OutputModes_group */ + +/** @defgroup VL53L1_define_XtalkCal_group Defines Xtalk Calibration modes + * Defines all possible Offset Calibration modes for the device + * @{ + */ +typedef uint8_t VL53L1_XtalkCalibrationModes; + +#define VL53L1_XTALKCALIBRATIONMODE_NO_TARGET \ + ((VL53L1_OffsetCalibrationModes) 0) +/*!< To perform Xtalk calibration with no target below 80 cm */ +#define VL53L1_XTALKCALIBRATIONMODE_SINGLE_TARGET \ + ((VL53L1_OffsetCalibrationModes) 1) +/*!< To perform Xtalk calibration with one target */ +#define VL53L1_XTALKCALIBRATIONMODE_FULL_ROI \ + ((VL53L1_OffsetCalibrationModes) 2) +/*!< To perform Xtalk calibration based on histogram with full ROI */ + +/** @} VL53L1_define_XtalkCal_group */ + +/** @defgroup VL53L1_define_OffsetCal_group Defines Offset Calibration modes + * Defines all possible Offset Calibration modes for the device + * @{ + */ +typedef uint8_t VL53L1_OffsetCalibrationModes; + +#define VL53L1_OFFSETCALIBRATIONMODE_STANDARD \ + ((VL53L1_OffsetCalibrationModes) 1) +#define VL53L1_OFFSETCALIBRATIONMODE_PRERANGE_ONLY \ + ((VL53L1_OffsetCalibrationModes) 2) +#define VL53L1_OFFSETCALIBRATIONMODE_MULTI_ZONE \ + ((VL53L1_OffsetCalibrationModes) 3) + +/** @} VL53L1_define_OffsetCal_group */ + +/** @defgroup VL53L1_define_DeviceDmaxModes_group Defines Dmax source modes + * Defines all possible sources for Dmax calibration for the device + * @{ + */ +typedef uint8_t VL53L1_DeviceDmaxModes; + +#define VL53L1_DMAXMODE_FMT_CAL_DATA ((VL53L1_DeviceDmaxModes) 1) +#define VL53L1_DMAXMODE_CUSTCAL_DATA ((VL53L1_DeviceDmaxModes) 2) +#define VL53L1_DMAXMODE_PER_ZONE_CAL_DATA ((VL53L1_DeviceDmaxModes) 3) + +/** @} VL53L1_define_DeviceDmaxModes_group */ + +/** @defgroup VL53L1_define_OffsetCorrectionModesBD_group Defines offset Mode + * @brief Defines all possible offset correction modes for the device + * @{ + */ +typedef uint8_t VL53L1_OffsetCorrectionModes; + +#define VL53L1_OFFSETCORRECTIONMODE_STANDARD ((VL53L1_OffsetCorrectionMode) 1) +#define VL53L1_OFFSETCORRECTIONMODE_PERZONE ((VL53L1_OffsetCorrectionMode) 2) +#define VL53L1_OFFSETCORRECTIONMODE_PERVCSEL ((VL53L1_OffsetCorrectionMode) 3) + +/** @} VL53L1_define_OffsetCorrectionModesBD_group */ + +/** @defgroup VL53L1_define_RoiStatus_group Defines Roi Status + * Defines the read status mode + * @{ + */ +typedef uint8_t VL53L1_RoiStatus; + +#define VL53L1_ROISTATUS_NOT_VALID ((VL53L1_RoiStatus) 0) +#define VL53L1_ROISTATUS_VALID_NOT_LAST ((VL53L1_RoiStatus) 1) +#define VL53L1_ROISTATUS_VALID_LAST ((VL53L1_RoiStatus) 2) +/** @} VL53L1_define_RoiStatus_group */ + + +/** @defgroup VL53L1_CheckEnable_group Check Enable list + * @brief Check Enable code + * + * Define used to specify the LimitCheckId. + * Use @a VL53L1_GetLimitCheckInfo() to get the string. + * @{ + */ + +#define VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE 0 +#define VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE 1 + +#define VL53L1_CHECKENABLE_NUMBER_OF_CHECKS 2 + +/** @} end of VL53L1_CheckEnable_group */ + + +/** @defgroup VL53L1_ThresholdMode_gropup Detection Functionality + * @brief Defines the different functionalities for the detection feature + * @{ + */ +typedef uint8_t VL53L1_ThresholdMode; + +#define VL53L1_THRESHOLD_CROSSED_LOW \ + ((VL53L1_ThresholdMode) 0) + /*!< Trigger interrupt if value < thresh_low */ +#define VL53L1_THRESHOLD_CROSSED_HIGH \ + ((VL53L1_ThresholdMode) 1) + /*!< Trigger interrupt if value > thresh_high */ +#define VL53L1_THRESHOLD_OUT_OF_WINDOW \ + ((VL53L1_ThresholdMode) 2) + /*!< Trigger interrupt if value < thresh_low OR value > thresh_high */ +#define VL53L1_THRESHOLD_IN_WINDOW \ + ((VL53L1_ThresholdMode) 3) + /*!< Trigger interrupt if value > thresh_low AND value < thresh_high */ + +/** @} end of VL53L1_ThresholdMode_gropup */ + +/** @brief Defines parameters for Distance detection Thresholds configuration + */ +typedef struct { + VL53L1_ThresholdMode CrossMode; + uint16_t High; /*!< Distance threshold high limit in mm */ + uint16_t Low; /*!< Distance threshold low limit in mm */ +} VL53L1_DistanceThreshold_t; + +/** @brief Defines parameters for Signal rate detection Thresholds configuration + */ +typedef struct { + VL53L1_ThresholdMode CrossMode; + FixPoint1616_t High; /*!< Signal rate threshold high limit */ + FixPoint1616_t Low; /*!< Signal rate threshold low limit */ +} VL53L1_RateThreshold_t; + +/** @defgroup VL53L1_DetectionMode_group Gpio Functionality + * @brief Defines conditions leading to device's IT on GPIO + * @{ + */ +typedef uint8_t VL53L1_DetectionMode; + +#define VL53L1_DETECTION_NORMAL_RUN \ + ((VL53L1_DetectionMode) 0) + /*!< Trigger interrupt on new measurement regardless of threshold + * just like after a VL53L1_SetPresetMode() call + */ +#define VL53L1_DETECTION_DISTANCE_ONLY \ + ((VL53L1_DetectionMode) 1) + /*!< Trigger interrupt if "threshold event" occurs on distance */ +#define VL53L1_DETECTION_RATE_ONLY \ + ((VL53L1_DetectionMode) 2) + /*!< Trigger interrupt if "threshold event" occurs on signal rate */ +#define VL53L1_DETECTION_DISTANCE_AND_RATE \ + ((VL53L1_DetectionMode) 3) + /*!< Trigger interrupt if "threshold event" occurs on distance AND rate + */ +#define VL53L1_DETECTION_DISTANCE_OR_RATE \ + ((VL53L1_DetectionMode) 4) + /*!< Trigger interrupt if "threshold event" occurs on distance OR rate + */ + +/** @} end of VL53L1_DetectionMode_group */ + +/** @brief Defines parameters for User/object Detection configuration + */ +typedef struct { + VL53L1_DetectionMode DetectionMode; /*!< See #VL53L1_DetectionMode*/ + uint8_t IntrNoTarget; /*!< 1 to trigger IT in case of no target found */ + VL53L1_DistanceThreshold_t Distance; /*!< limits in mm */ + VL53L1_RateThreshold_t Rate;/*!< limits in FixPoint1616_t */ +} VL53L1_DetectionConfig_t; + + +/** @brief Defines all parameters for the device + */ +typedef struct { + VL53L1_PresetModes PresetMode; + /*!< Defines the operating mode to be used for the next measure */ + VL53L1_OutputModes OutputMode; + /*!< Defines the Output mode to be used for the next measure */ + VL53L1_DistanceModes DistanceMode; + /*!< Defines the operating mode to be used for the next measure */ + uint32_t MeasurementTimingBudgetMicroSeconds; + /*!< Defines the allowed total time for a single measurement */ + uint8_t LimitChecksEnable[VL53L1_CHECKENABLE_NUMBER_OF_CHECKS]; + /*!< This Array store all the Limit Check enable for this device. */ + uint8_t LimitChecksStatus[VL53L1_CHECKENABLE_NUMBER_OF_CHECKS]; + /*!< This Array stores all the Status of the check linked to last + * measurement. + */ + FixPoint1616_t LimitChecksValue[VL53L1_CHECKENABLE_NUMBER_OF_CHECKS]; + /*!< This Array stores all the Limit Check value for this device */ + FixPoint1616_t LimitChecksCurrent[VL53L1_CHECKENABLE_NUMBER_OF_CHECKS]; + /*!< This Array stores all the Limit Check current value from latest + * ranging + */ +} VL53L1_DeviceParameters_t; + + +/** @defgroup VL53L1_define_State_group Defines the current status of the device + * Defines the current status of the device + * @{ + */ + +typedef uint8_t VL53L1_State; + +#define VL53L1_STATE_POWERDOWN ((VL53L1_State) 0) + /*!< Device is in HW reset */ +#define VL53L1_STATE_WAIT_STATICINIT ((VL53L1_State) 1) + /*!< Device is initialized and wait for static initialization */ +#define VL53L1_STATE_STANDBY ((VL53L1_State) 2) + /*!< Device is in Low power Standby mode */ +#define VL53L1_STATE_IDLE ((VL53L1_State) 3) + /*!< Device has been initialized and ready to do measurements */ +#define VL53L1_STATE_RUNNING ((VL53L1_State) 4) + /*!< Device is performing measurement */ +#define VL53L1_STATE_RESET ((VL53L1_State) 5) + /*!< Soft reset has been run on Device */ +#define VL53L1_STATE_UNKNOWN ((VL53L1_State) 98) + /*!< Device is in unknown state and need to be rebooted */ +#define VL53L1_STATE_ERROR ((VL53L1_State) 99) + /*!< Device is in error state and need to be rebooted */ + +/** @} VL53L1_define_State_group */ + +/** @defgroup VL53L1_define_Smudge_Mode_group Defines smudge correction modes + * Defines the smudge correction modes + * @{ + */ + +typedef uint8_t VL53L1_SmudgeCorrectionModes; + +#define VL53L1_SMUDGE_CORRECTION_NONE ((VL53L1_SmudgeCorrectionModes) 0) + /*!< Smudge correction is disabled */ +#define VL53L1_SMUDGE_CORRECTION_CONTINUOUS ((VL53L1_SmudgeCorrectionModes) 1) + /*!< Smudge correction is applied continuously across the rangings */ +#define VL53L1_SMUDGE_CORRECTION_SINGLE ((VL53L1_SmudgeCorrectionModes) 2) + /*!< Smudge correction is applied only once across the rangings */ +#define VL53L1_SMUDGE_CORRECTION_DEBUG ((VL53L1_SmudgeCorrectionModes) 3) + /*!< Smudge detection is applied continuously but Xtalk values are not + * updated automatically within the driver + */ + +/** @} VL53L1_define_Smudge_Correction_Mode_group */ + + +/** + * @struct VL53L1_RangingMeasurementData_t + * @brief Single Range measurement data. + */ +typedef struct { + uint32_t TimeStamp; + /*!< 32-bit time stamp. + * @warning Not yet implemented + */ + + uint8_t StreamCount; + /*!< 8-bit Stream Count. */ + + uint8_t RangeQualityLevel; + /*!< indicate a quality level in percentage from 0 to 100 + * @warning Not yet implemented + */ + + FixPoint1616_t SignalRateRtnMegaCps; + /*!< Return signal rate (MCPS)\n these is a 16.16 fix point + * value, which is effectively a measure of target + * reflectance. + */ + + FixPoint1616_t AmbientRateRtnMegaCps; + /*!< Return ambient rate (MCPS)\n these is a 16.16 fix point + * value, which is effectively a measure of the ambient + * light. + */ + + uint16_t EffectiveSpadRtnCount; + /*!< Return the effective SPAD count for the return signal. + * To obtain Real value it should be divided by 256 + */ + + FixPoint1616_t SigmaMilliMeter; + /*!< Return the Sigma value in millimeter */ + + int16_t RangeMilliMeter; + /*!< range distance in millimeter. This should be between + * RangeMinMilliMeter and RangeMaxMilliMeter + */ + + uint8_t RangeFractionalPart; + /*!< Fractional part of range distance. Final value is a + * RangeMilliMeter + RangeFractionalPart/256. + * @warning Not yet implemented + */ + + uint8_t RangeStatus; + /*!< Range Status for the current measurement. This is device + * dependent. Value = 0 means value is valid. + */ +} VL53L1_RangingMeasurementData_t; + +/** + * @struct VL53L1_TargetRangeData_t + * @brief One Range measurement data for each target. + */ +typedef struct { + uint8_t RangeQualityLevel; + /*!< indicate a quality level in percentage from 0 to 100 + * @warning Not yet implemented + */ + + int16_t RangeMaxMilliMeter; + /*!< Tells what is the maximum detection distance of the object + * in current setup and environment conditions (Filled when + * applicable) + */ + + int16_t RangeMinMilliMeter; + /*!< Tells what is the minimum detection distance of the object + * in current setup and environment conditions (Filled when + * applicable) + */ + + FixPoint1616_t SignalRateRtnMegaCps; + /*!< Return signal rate (MCPS)\n these is a 16.16 fix point + * value, which is effectively a measure of target + * reflectance. + */ + + FixPoint1616_t AmbientRateRtnMegaCps; + /*!< Return ambient rate (MCPS)\n these is a 16.16 fix point + * value, which is effectively a measure of the ambien + * t light. + */ + + FixPoint1616_t SigmaMilliMeter; + /*!< Return the Sigma value in millimeter */ + + int16_t RangeMilliMeter; + /*!< range distance in millimeter. This should be between + * RangeMinMilliMeter and RangeMaxMilliMeter + */ + + uint8_t RangeFractionalPart; + /*!< Fractional part of range distance. Final value is a + * RangeMilliMeter + RangeFractionalPart/256. + * @warning Not yet implemented + */ + + uint8_t RangeStatus; + /*!< Range Status for the current measurement. This is device + * dependent. Value = 0 means value is valid. + */ + + uint8_t ExtendedRange; + /*!< Extended range flag for the current measurement. + * Value = 1 means timings A&B are combined to increase the + * maximum distance range. + */ +} VL53L1_TargetRangeData_t; +/** + * @struct VL53L1_MultiRangingData_t + * @brief Structure for storing the set of range results for a single ROI + * + */ +typedef struct { + uint32_t TimeStamp; + /*!< 32-bit time stamp. + * @warning Not yet implemented + */ + + uint8_t StreamCount; + /*!< 8-bit Stream Count. */ + + uint8_t RoiNumber; + /*!< Denotes on which ROI the range data is related to. */ + uint8_t NumberOfObjectsFound; + /*!< Indicate the number of objects found in the current ROI. + * This is used to know how many ranging data should be get. + * NumberOfObjectsFound is in the range 0 to + * VL53L1_MAX_RANGE_RESULTS. + */ + VL53L1_RoiStatus RoiStatus; + /*!< Indicate if the data read is valid or not or if this is + * the last valid data in the ROI. + */ + VL53L1_TargetRangeData_t RangeData[VL53L1_MAX_RANGE_RESULTS]; + /*!< Range data each target distance */ + uint8_t HasXtalkValueChanged; + /*!< set to 1 if a new Xtalk value has been computed whilst + * smudge correction mode enable by with + * VL53L1_SmudgeCorrectionEnable() function is either + * VL53L1_SMUDGE_CORRECTION_CONTINUOUS or + * VL53L1_SMUDGE_CORRECTION_SINGLE. + */ + uint16_t EffectiveSpadRtnCount; + /*!< Return the effective SPAD count for the return signal. + * To obtain Real value it should be divided by 256 + */ + int16_t DmaxMilliMeter; + /*!< range Dmax distance in millimeter. + */ + VL53L1_DistanceModes RecommendedDistanceMode; + /*!< suggestion for a better distance mode choice to improve + * range accuracy. + */ +} VL53L1_MultiRangingData_t; + + +/** @brief Defines User Zone(ROI) parameters + * + */ +typedef struct { + + uint8_t TopLeftX; /*!< Top Left x coordinate: 0-15 range */ + uint8_t TopLeftY; /*!< Top Left y coordinate: 0-15 range */ + uint8_t BotRightX; /*!< Bot Right x coordinate: 0-15 range */ + uint8_t BotRightY; /*!< Bot Right y coordinate: 0-15 range */ + +} VL53L1_UserRoi_t; + + +/** @brief Defines ROI configuration parameters + * + * Support up a max of 16 zones, Each Zone has the same size + * + */ +typedef struct { + + uint8_t NumberOfRoi; /*!< Number of Rois defined*/ + + VL53L1_UserRoi_t UserRois[VL53L1_MAX_USER_ZONES]; + /*!< List of Rois */ + +} VL53L1_RoiConfig_t; + +/** + * @struct VL53L1_CustomerNvmManaged_t + * + */ + +typedef struct { + uint8_t global_config__spad_enables_ref_0; + uint8_t global_config__spad_enables_ref_1; + uint8_t global_config__spad_enables_ref_2; + uint8_t global_config__spad_enables_ref_3; + uint8_t global_config__spad_enables_ref_4; + uint8_t global_config__spad_enables_ref_5; + uint8_t global_config__ref_en_start_select; + uint8_t ref_spad_man__num_requested_ref_spads; + uint8_t ref_spad_man__ref_location; + uint32_t algo__crosstalk_compensation_plane_offset_kcps; + int16_t algo__crosstalk_compensation_x_plane_gradient_kcps; + int16_t algo__crosstalk_compensation_y_plane_gradient_kcps; + uint16_t ref_spad_char__total_rate_target_mcps; + int16_t algo__part_to_part_range_offset_mm; + int16_t mm_config__inner_offset_mm; + int16_t mm_config__outer_offset_mm; +} VL53L1_CustomerNvmManaged_t; + +/** + * @struct VL53L1_CalibrationData_t + * @brief Structure for storing the Calibration Data + * + */ + +typedef struct { + + uint32_t struct_version; + VL53L1_CustomerNvmManaged_t customer; + VL53L1_dmax_calibration_data_t fmt_dmax_cal; + VL53L1_dmax_calibration_data_t cust_dmax_cal; + VL53L1_additional_offset_cal_data_t add_off_cal_data; + VL53L1_optical_centre_t optical_centre; + VL53L1_xtalk_histogram_data_t xtalkhisto; + VL53L1_gain_calibration_data_t gain_cal; + VL53L1_cal_peak_rate_map_t cal_peak_rate_map; + VL53L1_per_vcsel_period_offset_cal_data_t per_vcsel_cal_data; + uint32_t algo__xtalk_cpo_HistoMerge_kcps[VL53L1_BIN_REC_SIZE]; +} VL53L1_CalibrationData_t; + +#define VL53L1_ADDITIONAL_CALIBRATION_DATA_STRUCT_VERSION 0x20 +/** VL53L1 additional Calibration Data struct version final struct version + * is given by adding it to VL53L1_LL_CALIBRATION_DATA_STRUCT_VERSION + */ + +#define VL53L1_CALIBRATION_DATA_STRUCT_VERSION \ + (VL53L1_LL_CALIBRATION_DATA_STRUCT_VERSION + \ + VL53L1_ADDITIONAL_CALIBRATION_DATA_STRUCT_VERSION) +/* VL53L1 Calibration Data struct version */ + +/** + * @struct VL53L1_AdditionalData_t + * @brief Structure for storing the Additional Data + * + */ +typedef VL53L1_additional_data_t VL53L1_AdditionalData_t; + +/** + * @struct VL53L1_ZoneCalibrationData_t + * @brief Structure for storing the Zone Calibration Data + * + */ +typedef VL53L1_zone_calibration_results_t VL53L1_ZoneCalibrationData_t; + +/** @defgroup VL53L1_define_SequenceStepId_group Defines the SequenceStep + * Configure the sequence steps performed during ranging + * @{ + */ +typedef uint8_t VL53L1_SequenceStepId; + +#define VL53L1_SEQUENCESTEP_VHV ((VL53L1_SequenceStepId) 0) +/*!>12)&0xFFFF) +#define VL53L1_FIXPOINT44TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<12) + +#define VL53L1_FIXPOINT1616TOFIXPOINT72(Value) \ + (uint16_t)((Value>>14)&0xFFFF) +#define VL53L1_FIXPOINT72TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<14) + +#define VL53L1_FIXPOINT1616TOFIXPOINT97(Value) \ + (uint16_t)((Value>>9)&0xFFFF) +#define VL53L1_FIXPOINT97TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<9) + +#define VL53L1_FIXPOINT1616TOFIXPOINT88(Value) \ + (uint16_t)((Value>>8)&0xFFFF) +#define VL53L1_FIXPOINT88TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<8) + +#define VL53L1_FIXPOINT1616TOFIXPOINT412(Value) \ + (uint16_t)((Value>>4)&0xFFFF) +#define VL53L1_FIXPOINT412TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<4) + +#define VL53L1_FIXPOINT1616TOFIXPOINT313(Value) \ + (uint16_t)((Value>>3)&0xFFFF) +#define VL53L1_FIXPOINT313TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<3) + +#define VL53L1_FIXPOINT1616TOFIXPOINT08(Value) \ + (uint8_t)((Value>>8)&0x00FF) +#define VL53L1_FIXPOINT08TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<8) + +#define VL53L1_FIXPOINT1616TOFIXPOINT53(Value) \ + (uint8_t)((Value>>13)&0x00FF) +#define VL53L1_FIXPOINT53TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<13) + +#define VL53L1_FIXPOINT1616TOFIXPOINT102(Value) \ + (uint16_t)((Value>>14)&0x0FFF) +#define VL53L1_FIXPOINT102TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<14) + +#define VL53L1_FIXPOINT1616TOFIXPOINT142(Value) \ + (uint16_t)((Value>>14)&0xFFFF) +#define VL53L1_FIXPOINT142TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<14) + +#define VL53L1_FIXPOINT1616TOFIXPOINT160(Value) \ + (uint16_t)((Value>>16)&0xFFFF) +#define VL53L1_FIXPOINT160TOFIXPOINT1616(Value) \ + (FixPoint1616_t)((uint32_t)Value<<16) + +#define VL53L1_MAKEUINT16(lsb, msb) (uint16_t)((((uint16_t)msb)<<8) + \ + (uint16_t)lsb) + +#ifndef SUPPRESS_UNUSED_WARNING +#define SUPPRESS_UNUSED_WARNING(x) ((void) (x)) +#endif + +/** @} VL53L1_define_GeneralMacro_group */ + +/** @} VL53L1_globaldefine_group */ + + + +#ifdef __cplusplus +} +#endif + + +#endif /* _VL53L1_DEF_H_ */ diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_dmax_structs.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_dmax_structs.h new file mode 100644 index 000000000000..2e4537b3653e --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_dmax_structs.h @@ -0,0 +1,92 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + + + + +#ifndef _VL53L1_DMAX_STRUCTS_H_ +#define _VL53L1_DMAX_STRUCTS_H_ + +#include "vl53l1_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + +#define VL53L1_MAX_AMBIENT_DMAX_VALUES 5 + + + + + +typedef struct { + + + + uint16_t ref__actual_effective_spads; + + uint16_t ref__peak_signal_count_rate_mcps; + + uint16_t ref__distance_mm; + + uint16_t ref_reflectance_pc; + + + + + uint16_t coverglass_transmission; + + +} VL53L1_dmax_calibration_data_t; + + + + +typedef struct { + + + + uint8_t signal_thresh_sigma; + + uint8_t ambient_thresh_sigma; + + int32_t min_ambient_thresh_events; + + int32_t signal_total_events_limit; + + + uint16_t target_reflectance_for_dmax_calc[ + VL53L1_MAX_AMBIENT_DMAX_VALUES]; + + uint16_t max_effective_spads; + + + + + uint16_t dss_config__target_total_rate_mcps; + + uint8_t dss_config__aperture_attenuation; + + +} VL53L1_hist_gen3_dmax_config_t; + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_error_codes.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_error_codes.h new file mode 100644 index 000000000000..de9a1eb9ceda --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_error_codes.h @@ -0,0 +1,223 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +/** + * @file vl53l1_error_codes.h + * + * @brief Error Code definitions for VL53L1 API. + * + */ + +#ifndef _VL53L1_ERROR_CODES_H_ +#define _VL53L1_ERROR_CODES_H_ + +#include "vl53l1_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/* + **************************************** + * PRIVATE define do not edit + *************************************** + */ + +/** + * @defgroup VL53L1_define_Error_group Error and Warning code returned by API + * The following DEFINE are used to identify the TOF device ERROR + * @{ + */ + +typedef int8_t VL53L1_Error; + +#define VL53L1_ERROR_NONE ((VL53L1_Error) 0) +#define VL53L1_ERROR_CALIBRATION_WARNING ((VL53L1_Error) - 1) + /*!< Warning invalid calibration data may be in used + * \a VL53L1_InitData() + * \a VL53L1_GetOffsetCalibrationData + * \a VL53L1_SetOffsetCalibrationData + */ +#define VL53L1_ERROR_MIN_CLIPPED ((VL53L1_Error) - 2) + /*!< Warning parameter passed was clipped to min before to be applied */ + +#define VL53L1_ERROR_UNDEFINED ((VL53L1_Error) - 3) + /*!< Unqualified error */ +#define VL53L1_ERROR_INVALID_PARAMS ((VL53L1_Error) - 4) + /*!< Parameter passed is invalid or out of range */ +#define VL53L1_ERROR_NOT_SUPPORTED ((VL53L1_Error) - 5) + /*!< Function is not supported in current mode or configuration */ +#define VL53L1_ERROR_RANGE_ERROR ((VL53L1_Error) - 6) + /*!< Device report a ranging error interrupt status */ +#define VL53L1_ERROR_TIME_OUT ((VL53L1_Error) - 7) + /*!< Aborted due to time out */ +#define VL53L1_ERROR_MODE_NOT_SUPPORTED ((VL53L1_Error) - 8) + /*!< Asked mode is not supported by the device */ +#define VL53L1_ERROR_BUFFER_TOO_SMALL ((VL53L1_Error) - 9) + /*!< ... */ +#define VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL ((VL53L1_Error) - 10) + /*!< Supplied buffer is larger than I2C supports */ +#define VL53L1_ERROR_GPIO_NOT_EXISTING ((VL53L1_Error) - 11) + /*!< User tried to setup a non-existing GPIO pin */ +#define VL53L1_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED ((VL53L1_Error) - 12) + /*!< unsupported GPIO functionality */ +#define VL53L1_ERROR_CONTROL_INTERFACE ((VL53L1_Error) - 13) + /*!< error reported from IO functions */ +#define VL53L1_ERROR_INVALID_COMMAND ((VL53L1_Error) - 14) + /*!< The command is not allowed in the current device state + * (power down) + */ +#define VL53L1_ERROR_DIVISION_BY_ZERO ((VL53L1_Error) - 15) + /*!< In the function a division by zero occurs */ +#define VL53L1_ERROR_REF_SPAD_INIT ((VL53L1_Error) - 16) + /*!< Error during reference SPAD initialization */ +#define VL53L1_ERROR_GPH_SYNC_CHECK_FAIL ((VL53L1_Error) - 17) + /*!< GPH sync interrupt check fail - API out of sync with device*/ +#define VL53L1_ERROR_STREAM_COUNT_CHECK_FAIL ((VL53L1_Error) - 18) + /*!< Stream count check fail - API out of sync with device */ +#define VL53L1_ERROR_GPH_ID_CHECK_FAIL ((VL53L1_Error) - 19) + /*!< GPH ID check fail - API out of sync with device */ +#define VL53L1_ERROR_ZONE_STREAM_COUNT_CHECK_FAIL ((VL53L1_Error) - 20) + /*!< Zone dynamic config stream count check failed - API out of sync */ +#define VL53L1_ERROR_ZONE_GPH_ID_CHECK_FAIL ((VL53L1_Error) - 21) + /*!< Zone dynamic config GPH ID check failed - API out of sync */ + +#define VL53L1_ERROR_XTALK_EXTRACTION_NO_SAMPLE_FAIL ((VL53L1_Error) - 22) + /*!< Thrown when run_xtalk_extraction fn has 0 successful samples + * when using the full array to sample the xtalk. In this case there is + * not enough information to generate new Xtalk parm info. The function + * will exit and leave the current xtalk parameters unaltered + */ +#define VL53L1_ERROR_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL ((VL53L1_Error) - 23) + /*!< Thrown when run_xtalk_extraction fn has found that the + * avg sigma estimate of the full array xtalk sample is > than the + * maximal limit allowed. In this case the xtalk sample is too noisy for + * measurement. The function will exit and leave the current xtalk + * parameters unaltered. + */ + + +#define VL53L1_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL ((VL53L1_Error) - 24) + /*!< Thrown if there one of stages has no valid offset calibration + * samples. A fatal error calibration not valid + */ +#define VL53L1_ERROR_OFFSET_CAL_NO_SPADS_ENABLED_FAIL ((VL53L1_Error) - 25) + /*!< Thrown if there one of stages has zero effective SPADS + * Traps the case when MM1 SPADs is zero. + * A fatal error calibration not valid + */ +#define VL53L1_ERROR_ZONE_CAL_NO_SAMPLE_FAIL ((VL53L1_Error) - 26) + /*!< Thrown if then some of the zones have no valid samples + * A fatal error calibration not valid + */ + +#define VL53L1_ERROR_TUNING_PARM_KEY_MISMATCH ((VL53L1_Error) - 27) + /*!< Thrown if the tuning file key table version does not match with + * expected value. The driver expects the key table version to match + * the compiled default version number in the define + * VL53L1_TUNINGPARM_KEY_TABLE_VERSION_DEFAULT + */ + +#define VL53L1_WARNING_REF_SPAD_CHAR_NOT_ENOUGH_SPADS ((VL53L1_Error) - 28) + /*!< Thrown if there are less than 5 good SPADs are available. */ +#define VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_HIGH ((VL53L1_Error) - 29) + /*!< Thrown if the final reference rate is greater than + * the upper reference rate limit - default is 40 Mcps. + * Implies a minimum Q3 (x10) SPAD (5) selected + */ +#define VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_LOW ((VL53L1_Error) - 30) + /*!< Thrown if the final reference rate is less than + * the lower reference rate limit - default is 10 Mcps. + * Implies maximum Q1 (x1) SPADs selected + */ + + +#define VL53L1_WARNING_OFFSET_CAL_MISSING_SAMPLES ((VL53L1_Error) - 31) + /*!< Thrown if there is less than the requested number of + * valid samples. + */ +#define VL53L1_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH ((VL53L1_Error) - 32) + /*!< Thrown if the offset calibration range sigma estimate is greater + * than 8.0 mm. This is the recommended min value to yield a stable + * offset measurement + */ +#define VL53L1_WARNING_OFFSET_CAL_RATE_TOO_HIGH ((VL53L1_Error) - 33) + /*!< Thrown when VL53L1_run_offset_calibration() peak rate is greater + * than that 50.0Mcps. This is the recommended max rate to avoid + * pile-up influencing the offset measurement + */ +#define VL53L1_WARNING_OFFSET_CAL_SPAD_COUNT_TOO_LOW ((VL53L1_Error) - 34) + /*!< Thrown when VL53L1_run_offset_calibration() when one of stages + * range has less that 5.0 effective SPADS. This is the recommended + * min value to yield a stable offset + */ + + +#define VL53L1_WARNING_ZONE_CAL_MISSING_SAMPLES ((VL53L1_Error) - 35) + /*!< Thrown if one of more of the zones have less than + * the requested number of valid samples + */ +#define VL53L1_WARNING_ZONE_CAL_SIGMA_TOO_HIGH ((VL53L1_Error) - 36) + /*!< Thrown if one or more zones have sigma estimate value greater + * than 8.0 mm. This is the recommended min value to yield a stable + * offset measurement + */ +#define VL53L1_WARNING_ZONE_CAL_RATE_TOO_HIGH ((VL53L1_Error) - 37) + /*!< Thrown if one of more zones have peak rate higher than + * that 50.0Mcps. This is the recommended max rate to avoid + * pile-up influencing the offset measurement + */ + + +#define VL53L1_WARNING_XTALK_MISSING_SAMPLES ((VL53L1_Error) - 38) + /*!< Thrown to notify that some of the xtalk samples did not yield + * valid ranging pulse data while attempting to measure + * the xtalk signal in vl53l1_run_xtalk_extract(). This can signify any + * of the zones are missing samples, for further debug information the + * xtalk_results struct should be referred to. This warning is for + * notification only, xtalk pulse and shape have still been generated + */ +#define VL53L1_WARNING_XTALK_NO_SAMPLES_FOR_GRADIENT ((VL53L1_Error) - 39) + /*!< Thrown to notify that some of the xtalk samples used for gradient + * generation did not yield valid ranging pulse data while attempting to + * measure the xtalk signal in vl53l1_run_xtalk_extract(). This can + * signify that any one of the zones 0-3 yielded no successful samples. + * xtalk_results struct should be referred to for further debug info. + * This warning is for notification only, the xtalk pulse and shape + * have still been generated. + */ +#define VL53L1_WARNING_XTALK_SIGMA_LIMIT_FOR_GRADIENT ((VL53L1_Error) - 40) + /*!< Thrown to notify that some of the xtalk samples used for gradient + * generation did not pass the sigma limit check while attempting to + * measure the xtalk signal in vl53l1_run_xtalk_extract(). This can + * signify that any one of the zones 0-3 yielded an avg sigma_mm + * value > the limit. The xtalk_results struct should be referred to for + * further debug info. + * This warning is for notification only, the xtalk pulse and shape + * have still been generated. + */ + +#define VL53L1_ERROR_NOT_IMPLEMENTED ((VL53L1_Error) - 41) + /*!< Tells requested functionality has not been implemented yet or + * not compatible with the device + */ +#define VL53L1_ERROR_PLATFORM_SPECIFIC_START ((VL53L1_Error) - 60) + /*!< Tells the starting code for platform */ +/** @} VL53L1_define_Error_group */ + + +#ifdef __cplusplus +} +#endif + + +#endif /* _VL53L1_ERROR_CODES_H_ */ diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_error_exceptions.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_error_exceptions.h new file mode 100644 index 000000000000..5508fa31a462 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_error_exceptions.h @@ -0,0 +1,40 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#ifndef _VL53L1_ERROR_EXCEPTIONS_H_ +#define _VL53L1_ERROR_EXCEPTIONS_H_ + +#define IGNORE_DIVISION_BY_ZERO 0 + +#define IGNORE_XTALK_EXTRACTION_NO_SAMPLE_FAIL 0 +#define IGNORE_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL 0 +#define IGNORE_XTALK_EXTRACTION_NO_SAMPLE_FOR_GRADIENT_WARN 0 +#define IGNORE_XTALK_EXTRACTION_SIGMA_LIMIT_FOR_GRADIENT_WARN 0 +#define IGNORE_XTALK_EXTRACTION_MISSING_SAMPLES_WARN 0 + +#define IGNORE_REF_SPAD_CHAR_NOT_ENOUGH_SPADS 0 +#define IGNORE_REF_SPAD_CHAR_RATE_TOO_HIGH 0 +#define IGNORE_REF_SPAD_CHAR_RATE_TOO_LOW 0 + +#define IGNORE_OFFSET_CAL_MISSING_SAMPLES 0 +#define IGNORE_OFFSET_CAL_SIGMA_TOO_HIGH 0 +#define IGNORE_OFFSET_CAL_RATE_TOO_HIGH 0 +#define IGNORE_OFFSET_CAL_SPAD_COUNT_TOO_LOW 0 + +#define IGNORE_ZONE_CAL_MISSING_SAMPLES 0 +#define IGNORE_ZONE_CAL_SIGMA_TOO_HIGH 0 +#define IGNORE_ZONE_CAL_RATE_TOO_HIGH 0 + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_error_strings.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_error_strings.h new file mode 100644 index 000000000000..3651c429429b --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_error_strings.h @@ -0,0 +1,148 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + + +#ifndef VL53L1_ERROR_STRINGS_H_ +#define VL53L1_ERROR_STRINGS_H_ + +#include "vl53l1_error_codes.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + +VL53L1_Error VL53L1_get_pal_error_string( + VL53L1_Error PalErrorCode, + char *pPalErrorString); + + +#ifndef VL53L1_USE_EMPTY_STRING + + + #define VL53L1_STRING_ERROR_NONE \ + "No Error" + #define VL53L1_STRING_ERROR_CALIBRATION_WARNING \ + "Calibration Warning Error" + #define VL53L1_STRING_ERROR_MIN_CLIPPED \ + "Min clipped error" + #define VL53L1_STRING_ERROR_UNDEFINED \ + "Undefined error" + #define VL53L1_STRING_ERROR_INVALID_PARAMS \ + "Invalid parameters error" + #define VL53L1_STRING_ERROR_NOT_SUPPORTED \ + "Not supported error" + #define VL53L1_STRING_ERROR_RANGE_ERROR \ + "Range error" + #define VL53L1_STRING_ERROR_TIME_OUT \ + "Time out error" + #define VL53L1_STRING_ERROR_MODE_NOT_SUPPORTED \ + "Mode not supported error" + #define VL53L1_STRING_ERROR_BUFFER_TOO_SMALL \ + "Buffer too small" + #define VL53L1_STRING_ERROR_COMMS_BUFFER_TOO_SMALL \ + "Comms Buffer too small" + #define VL53L1_STRING_ERROR_GPIO_NOT_EXISTING \ + "GPIO not existing" + #define VL53L1_STRING_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED \ + "GPIO funct not supported" + #define VL53L1_STRING_ERROR_CONTROL_INTERFACE \ + "Control Interface Error" + #define VL53L1_STRING_ERROR_INVALID_COMMAND \ + "Invalid Command Error" + #define VL53L1_STRING_ERROR_DIVISION_BY_ZERO \ + "Division by zero Error" + #define VL53L1_STRING_ERROR_REF_SPAD_INIT \ + "Reference Spad Init Error" + #define VL53L1_STRING_ERROR_GPH_SYNC_CHECK_FAIL \ + "GPH Sync Check Fail - API out of sync" + #define VL53L1_STRING_ERROR_STREAM_COUNT_CHECK_FAIL \ + "Stream Count Check Fail - API out of sync" + #define VL53L1_STRING_ERROR_GPH_ID_CHECK_FAIL \ + "GPH ID Check Fail - API out of sync" + #define VL53L1_STRING_ERROR_ZONE_STREAM_COUNT_CHECK_FAIL \ + "Zone Stream Count Check Fail - API out of sync" + #define VL53L1_STRING_ERROR_ZONE_GPH_ID_CHECK_FAIL \ + "Zone GPH ID Check Fail - API out of sync" + + #define VL53L1_STRING_ERROR_XTALK_EXTRACTION_NO_SAMPLES_FAIL \ + "No Xtalk using full array - Xtalk Extract Fail" + #define VL53L1_STRING_ERROR_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL \ + "Xtalk does not meet required VL53L1_p_011 limit - Xtalk Extract Fail" + + #define VL53L1_STRING_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL \ + "Offset Cal - one of more stages with no valid samples - fatal" + #define VL53L1_STRING_ERROR_OFFSET_CAL_NO_SPADS_ENABLED_FAIL \ + "Offset Cal - one of more stages with no SPADS enables - fatal" + #define VL53L1_STRING_ERROR_ZONE_CAL_NO_SAMPLE_FAIL \ + "Zone Cal - one of more zones with no valid samples - fatal" + + #define VL53L1_STRING_WARNING_REF_SPAD_CHAR_NOT_ENOUGH_SPADS \ + "Ref SPAD Char - Not Enough Good SPADs" + #define VL53L1_STRING_WARNING_REF_SPAD_CHAR_RATE_TOO_HIGH \ + "Ref SPAD Char - Final Ref Rate too high" + #define VL53L1_STRING_WARNING_REF_SPAD_CHAR_RATE_TOO_LOW \ + "Ref SPAD Char - Final Ref Rate too low" + + #define VL53L1_STRING_WARNING_OFFSET_CAL_MISSING_SAMPLES \ + "Offset Cal - Less than the requested number of valid samples" + #define VL53L1_STRING_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH \ + "Offset Cal - Sigma estimate value too high - offset not stable" + #define VL53L1_STRING_WARNING_OFFSET_CAL_RATE_TOO_HIGH \ + "Offset Cal - Rate too high - in pile up" + #define VL53L1_STRING_WARNING_OFFSET_CAL_SPAD_COUNT_TOO_LOW \ + "Offset Cal - Insufficient SPADs - offset may not be stable" + + #define VL53L1_STRING_WARNING_ZONE_CAL_MISSING_SAMPLES \ + "Zone Cal - One or more zone with less than requested valid samples" + #define VL53L1_STRING_WARNING_ZONE_CAL_SIGMA_TOO_HIGH \ + "Zone Cal - One of more zones the VL53L1_p_011 estimate too high" + #define VL53L1_STRING_WARNING_ZONE_CAL_RATE_TOO_HIGH \ + "Zone Cal - One of more zones with rate too high - in pile up" + + #define VL53L1_STRING_WARNING_XTALK_NO_SAMPLES_FOR_GRADIENT \ + "Xtalk - Gradient sample num = 0" + #define VL53L1_STRING_WARNING_XTALK_SIGMA_LIMIT_FOR_GRADIENT \ + "Xtalk - Gradient Sigma > Limit" + #define VL53L1_STRING_WARNING_XTALK_MISSING_SAMPLES \ + "Xtalk - Some missing and invalid samples" + + #define VL53L1_STRING_ERROR_DEVICE_FIRMWARE_TOO_OLD \ + "Device Firmware too old" + #define VL53L1_STRING_ERROR_DEVICE_FIRMWARE_TOO_NEW \ + "Device Firmware too new" + #define VL53L1_STRING_ERROR_UNIT_TEST_FAIL \ + "Unit Test Fail" + #define VL53L1_STRING_ERROR_FILE_READ_FAIL \ + "File Read Fail" + #define VL53L1_STRING_ERROR_FILE_WRITE_FAIL \ + "File Write Fail" + + #define VL53L1_STRING_ERROR_NOT_IMPLEMENTED \ + "Not implemented error" + #define VL53L1_STRING_UNKNOW_ERROR_CODE \ + "Unknown Error Code" + +#endif + + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_hist_char.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_hist_char.h new file mode 100644 index 000000000000..04861e794543 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_hist_char.h @@ -0,0 +1,57 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + + +#ifndef _VL53L1_HIST_CHAR_H_ +#define _VL53L1_HIST_CHAR_H_ + +#include "vl53l1_platform.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + + + +VL53L1_Error VL53L1_set_calib_config( + VL53L1_DEV Dev, + uint8_t vcsel_delay__a0, + uint8_t calib_1, + uint8_t calib_2, + uint8_t calib_3, + uint8_t calib_2__a0, + uint8_t spad_readout); + + + + +VL53L1_Error VL53L1_set_hist_calib_pulse_delay( + VL53L1_DEV Dev, + uint8_t calib_delay); + + + + +VL53L1_Error VL53L1_disable_calib_pulse_delay( + VL53L1_DEV Dev); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_hist_map.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_hist_map.h new file mode 100644 index 000000000000..19024b5015ce --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_hist_map.h @@ -0,0 +1,68 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + + +#ifndef _VL53L1_HIST_MAP_H_ +#define _VL53L1_HIST_MAP_H_ + +#include "vl53l1_register_map.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + + + +#define VL53L1_HISTOGRAM_CONFIG__OPCODE_SEQUENCE_0 \ + VL53L1_SIGMA_ESTIMATOR__EFFECTIVE_PULSE_WIDTH_NS + +#define VL53L1_HISTOGRAM_CONFIG__OPCODE_SEQUENCE_1 \ + VL53L1_SIGMA_ESTIMATOR__EFFECTIVE_AMBIENT_WIDTH_NS + +#define VL53L1_HISTOGRAM_CONFIG__OPCODE_SEQUENCE_2 \ + VL53L1_SIGMA_ESTIMATOR__SIGMA_REF_MM + +#define VL53L1_HISTOGRAM_CONFIG__AMB_THRESH_HIGH \ + VL53L1_ALGO__RANGE_IGNORE_THRESHOLD_MCPS + + + + +#define VL53L1_RESULT__HISTOGRAM_BIN_0_2 0x008E +#define VL53L1_RESULT__HISTOGRAM_BIN_0_1 0x008F +#define VL53L1_RESULT__HISTOGRAM_BIN_0_0 0x0090 + +#define VL53L1_RESULT__HISTOGRAM_BIN_23_2 0x00D3 +#define VL53L1_RESULT__HISTOGRAM_BIN_23_1 0x00D4 +#define VL53L1_RESULT__HISTOGRAM_BIN_23_0 0x00D5 + +#define VL53L1_RESULT__HISTOGRAM_BIN_23_0_MSB 0x00D9 +#define VL53L1_RESULT__HISTOGRAM_BIN_23_0_LSB 0x00DA + + + +#define VL53L1_HISTOGRAM_BIN_DATA_I2C_INDEX \ + VL53L1_RESULT__INTERRUPT_STATUS +#define VL53L1_HISTOGRAM_BIN_DATA_I2C_SIZE_BYTES \ + (VL53L1_RESULT__HISTOGRAM_BIN_23_0_LSB - \ + VL53L1_RESULT__INTERRUPT_STATUS + 1) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_hist_structs.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_hist_structs.h new file mode 100644 index 000000000000..3df3e8c7bd12 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_hist_structs.h @@ -0,0 +1,299 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + + +#ifndef _VL53L1_HIST_STRUCTS_H_ +#define _VL53L1_HIST_STRUCTS_H_ + +#include "vl53l1_ll_device.h" +#include "vl53l1_dmax_structs.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define VL53L1_MAX_BIN_SEQUENCE_LENGTH 6 +#define VL53L1_MAX_BIN_SEQUENCE_CODE 15 +#define VL53L1_HISTOGRAM_BUFFER_SIZE 24 +#define VL53L1_XTALK_HISTO_BINS 12 + + + +typedef struct { + + uint8_t histogram_config__spad_array_selection; + + uint8_t histogram_config__low_amb_even_bin_0_1; + uint8_t histogram_config__low_amb_even_bin_2_3; + uint8_t histogram_config__low_amb_even_bin_4_5; + + uint8_t histogram_config__low_amb_odd_bin_0_1; + uint8_t histogram_config__low_amb_odd_bin_2_3; + uint8_t histogram_config__low_amb_odd_bin_4_5; + + uint8_t histogram_config__mid_amb_even_bin_0_1; + uint8_t histogram_config__mid_amb_even_bin_2_3; + uint8_t histogram_config__mid_amb_even_bin_4_5; + + uint8_t histogram_config__mid_amb_odd_bin_0_1; + uint8_t histogram_config__mid_amb_odd_bin_2; + uint8_t histogram_config__mid_amb_odd_bin_3_4; + uint8_t histogram_config__mid_amb_odd_bin_5; + + uint8_t histogram_config__user_bin_offset; + + uint8_t histogram_config__high_amb_even_bin_0_1; + uint8_t histogram_config__high_amb_even_bin_2_3; + uint8_t histogram_config__high_amb_even_bin_4_5; + + uint8_t histogram_config__high_amb_odd_bin_0_1; + uint8_t histogram_config__high_amb_odd_bin_2_3; + uint8_t histogram_config__high_amb_odd_bin_4_5; + + uint16_t histogram_config__amb_thresh_low; + + uint16_t histogram_config__amb_thresh_high; + + +} VL53L1_histogram_config_t; + + + + +typedef struct { + + VL53L1_HistAlgoSelect hist_algo_select; + + + VL53L1_HistTargetOrder hist_target_order; + + + uint8_t filter_woi0; + + uint8_t filter_woi1; + + + VL53L1_HistAmbEstMethod hist_amb_est_method; + + uint8_t ambient_thresh_sigma0; + + uint8_t ambient_thresh_sigma1; + + + + uint16_t ambient_thresh_events_scaler; + + + + int32_t min_ambient_thresh_events; + + uint16_t noise_threshold; + + + int32_t signal_total_events_limit; + + uint8_t sigma_estimator__sigma_ref_mm; + + uint16_t sigma_thresh; + + int16_t range_offset_mm; + + uint16_t gain_factor; + + + uint8_t valid_phase_low; + + uint8_t valid_phase_high; + + uint8_t algo__consistency_check__phase_tolerance; + + uint8_t algo__consistency_check__event_sigma; + + + + uint16_t algo__consistency_check__event_min_spad_count; + + + + uint16_t algo__consistency_check__min_max_tolerance; + + + uint8_t algo__crosstalk_compensation_enable; + + uint32_t algo__crosstalk_compensation_plane_offset_kcps; + + int16_t algo__crosstalk_compensation_x_plane_gradient_kcps; + + int16_t algo__crosstalk_compensation_y_plane_gradient_kcps; + + + int16_t algo__crosstalk_detect_min_valid_range_mm; + + int16_t algo__crosstalk_detect_max_valid_range_mm; + + uint16_t algo__crosstalk_detect_max_valid_rate_kcps; + + uint16_t algo__crosstalk_detect_max_sigma_mm; + + + + uint8_t algo__crosstalk_detect_event_sigma; + + + + uint16_t algo__crosstalk_detect_min_max_tolerance; + + +} VL53L1_hist_post_process_config_t; + + + +typedef struct { + + + VL53L1_DeviceState cfg_device_state; + + VL53L1_DeviceState rd_device_state; + + + uint8_t zone_id; + + uint32_t time_stamp; + + + uint8_t VL53L1_p_022; + + uint8_t VL53L1_p_023; + + uint8_t VL53L1_p_024; + + uint8_t number_of_ambient_bins; + + uint8_t bin_seq[VL53L1_MAX_BIN_SEQUENCE_LENGTH]; + + uint8_t bin_rep[VL53L1_MAX_BIN_SEQUENCE_LENGTH]; + + int32_t bin_data[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + + uint8_t result__interrupt_status; + + uint8_t result__range_status; + + uint8_t result__report_status; + + uint8_t result__stream_count; + + uint16_t result__dss_actual_effective_spads; + + + uint16_t phasecal_result__reference_phase; + + uint8_t phasecal_result__vcsel_start; + + uint8_t cal_config__vcsel_start; + + uint16_t vcsel_width; + + uint8_t VL53L1_p_009; + + uint16_t VL53L1_p_019; + + uint32_t total_periods_elapsed; + + + uint32_t peak_duration_us; + + uint32_t woi_duration_us; + + + int32_t min_bin_value; + + int32_t max_bin_value; + + + uint16_t zero_distance_phase; + + uint8_t number_of_ambient_samples; + + int32_t ambient_events_sum; + + int32_t VL53L1_p_004; + + + uint8_t roi_config__user_roi_centre_spad; + + uint8_t roi_config__user_roi_requested_global_xy_size; + + +} VL53L1_histogram_bin_data_t; + + + + +typedef struct { + + + uint8_t zone_id; + + uint32_t time_stamp; + + + uint8_t VL53L1_p_022; + + uint8_t VL53L1_p_023; + + uint8_t VL53L1_p_024; + + uint32_t bin_data[VL53L1_XTALK_HISTO_BINS]; + + + + uint16_t phasecal_result__reference_phase; + + uint8_t phasecal_result__vcsel_start; + + uint8_t cal_config__vcsel_start; + + uint16_t vcsel_width; + + uint16_t VL53L1_p_019; + + uint16_t zero_distance_phase; + + +} VL53L1_xtalk_histogram_shape_t; + + + + +typedef struct { + + + VL53L1_xtalk_histogram_shape_t xtalk_shape; + + VL53L1_histogram_bin_data_t xtalk_hist_removed; + +} VL53L1_xtalk_histogram_data_t; + + + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_ll_def.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_ll_def.h new file mode 100644 index 000000000000..993cfe575233 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_ll_def.h @@ -0,0 +1,1756 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + + +#ifndef _VL53L1_LL_DEF_H_ +#define _VL53L1_LL_DEF_H_ + +#include "vl53l1_error_codes.h" +#include "vl53l1_register_structs.h" +#include "vl53l1_platform_user_config.h" +#include "vl53l1_platform_user_defines.h" +#include "vl53l1_hist_structs.h" +#include "vl53l1_dmax_structs.h" +#include "vl53l1_error_exceptions.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + +#define VL53L1_LL_API_IMPLEMENTATION_VER_MAJOR 1 + +#define VL53L1_LL_API_IMPLEMENTATION_VER_MINOR 1 + +#define VL53L1_LL_API_IMPLEMENTATION_VER_SUB 48 + +#define VL53L1_LL_API_IMPLEMENTATION_VER_REVISION 12224 + +#define VL53L1_LL_API_IMPLEMENTATION_VER_STRING "1.1.48.12224" + + +#define VL53L1_FIRMWARE_VER_MINIMUM 398 +#define VL53L1_FIRMWARE_VER_MAXIMUM 400 + + + + +#define VL53L1_LL_CALIBRATION_DATA_STRUCT_VERSION 0xECAB0102 + + + + +#define VL53L1_LL_ZONE_CALIBRATION_DATA_STRUCT_VERSION 0xECAE0101 + + + + + +#define VL53L1_BIN_REC_SIZE 6 + +#define VL53L1_TIMING_CONF_A_B_SIZE 2 + +#define VL53L1_FRAME_WAIT_EVENT 6 + + + +#define VL53L1_MAX_XTALK_RANGE_RESULTS 5 + + +#define VL53L1_MAX_OFFSET_RANGE_RESULTS 3 + + +#define VL53L1_NVM_MAX_FMT_RANGE_DATA 4 + + +#define VL53L1_NVM_PEAK_RATE_MAP_SAMPLES 25 + +#define VL53L1_NVM_PEAK_RATE_MAP_WIDTH 5 + +#define VL53L1_NVM_PEAK_RATE_MAP_HEIGHT 5 + + + + +#define VL53L1_ERROR_DEVICE_FIRMWARE_TOO_OLD ((VL53L1_Error) - 80) + +#define VL53L1_ERROR_DEVICE_FIRMWARE_TOO_NEW ((VL53L1_Error) - 85) + +#define VL53L1_ERROR_UNIT_TEST_FAIL ((VL53L1_Error) - 90) + +#define VL53L1_ERROR_FILE_READ_FAIL ((VL53L1_Error) - 95) + +#define VL53L1_ERROR_FILE_WRITE_FAIL ((VL53L1_Error) - 96) + + + + + + +typedef struct { + uint32_t ll_revision; + uint8_t ll_major; + uint8_t ll_minor; + uint8_t ll_build; +} VL53L1_ll_version_t; + + + + +typedef struct { + + uint8_t device_test_mode; + uint8_t VL53L1_p_009; + uint32_t timeout_us; + uint16_t target_count_rate_mcps; + + uint16_t min_count_rate_limit_mcps; + + uint16_t max_count_rate_limit_mcps; + + +} VL53L1_refspadchar_config_t; + + + + +typedef struct { + + uint16_t dss_config__target_total_rate_mcps; + + uint32_t phasecal_config_timeout_us; + + uint32_t mm_config_timeout_us; + + uint32_t range_config_timeout_us; + + uint8_t num_of_samples; + + int16_t algo__crosstalk_extract_min_valid_range_mm; + + int16_t algo__crosstalk_extract_max_valid_range_mm; + + uint16_t algo__crosstalk_extract_max_valid_rate_kcps; + + uint16_t algo__crosstalk_extract_max_sigma_mm; + + +} VL53L1_xtalkextract_config_t; + + + + +typedef struct { + + uint16_t dss_config__target_total_rate_mcps; + + uint32_t phasecal_config_timeout_us; + + uint32_t range_config_timeout_us; + + uint32_t mm_config_timeout_us; + + uint8_t pre_num_of_samples; + + uint8_t mm1_num_of_samples; + + uint8_t mm2_num_of_samples; + + +} VL53L1_offsetcal_config_t; + + + + +typedef struct { + + uint16_t dss_config__target_total_rate_mcps; + + uint32_t phasecal_config_timeout_us; + + uint32_t mm_config_timeout_us; + + uint32_t range_config_timeout_us; + + uint16_t phasecal_num_of_samples; + + uint16_t zone_num_of_samples; + + +} VL53L1_zonecal_config_t; + + + + + +typedef struct { + + VL53L1_DeviceSscArray array_select; + + uint8_t VL53L1_p_009; + + uint8_t vcsel_start; + + uint8_t vcsel_width; + + uint32_t timeout_us; + + uint16_t rate_limit_mcps; + + +} VL53L1_ssc_config_t; + + + + +typedef struct { + + + uint32_t algo__crosstalk_compensation_plane_offset_kcps; + + int16_t algo__crosstalk_compensation_x_plane_gradient_kcps; + + int16_t algo__crosstalk_compensation_y_plane_gradient_kcps; + + uint32_t nvm_default__crosstalk_compensation_plane_offset_kcps; + + int16_t nvm_default__crosstalk_compensation_x_plane_gradient_kcps; + + int16_t nvm_default__crosstalk_compensation_y_plane_gradient_kcps; + + uint8_t global_crosstalk_compensation_enable; + + int16_t histogram_mode_crosstalk_margin_kcps; + + int16_t lite_mode_crosstalk_margin_kcps; + + uint8_t crosstalk_range_ignore_threshold_mult; + + uint16_t crosstalk_range_ignore_threshold_rate_mcps; + + int16_t algo__crosstalk_detect_min_valid_range_mm; + + int16_t algo__crosstalk_detect_max_valid_range_mm; + + uint16_t algo__crosstalk_detect_max_valid_rate_kcps; + + uint16_t algo__crosstalk_detect_max_sigma_mm; + + + +} VL53L1_xtalk_config_t; + + + + +typedef struct { + + + uint16_t tp_tuning_parm_version; + + uint16_t tp_tuning_parm_key_table_version; + + uint16_t tp_tuning_parm_lld_version; + + uint8_t tp_init_phase_rtn_lite_long; + + uint8_t tp_init_phase_rtn_lite_med; + + uint8_t tp_init_phase_rtn_lite_short; + + uint8_t tp_init_phase_ref_lite_long; + + uint8_t tp_init_phase_ref_lite_med; + + uint8_t tp_init_phase_ref_lite_short; + + + uint8_t tp_init_phase_rtn_hist_long; + + uint8_t tp_init_phase_rtn_hist_med; + + uint8_t tp_init_phase_rtn_hist_short; + + uint8_t tp_init_phase_ref_hist_long; + + uint8_t tp_init_phase_ref_hist_med; + + uint8_t tp_init_phase_ref_hist_short; + + + uint8_t tp_consistency_lite_phase_tolerance; + + uint8_t tp_phasecal_target; + + uint16_t tp_cal_repeat_rate; + + uint8_t tp_lite_min_clip; + + + uint16_t tp_lite_long_sigma_thresh_mm; + + uint16_t tp_lite_med_sigma_thresh_mm; + + uint16_t tp_lite_short_sigma_thresh_mm; + + + uint16_t tp_lite_long_min_count_rate_rtn_mcps; + + uint16_t tp_lite_med_min_count_rate_rtn_mcps; + + uint16_t tp_lite_short_min_count_rate_rtn_mcps; + + + uint8_t tp_lite_sigma_est_pulse_width_ns; + + uint8_t tp_lite_sigma_est_amb_width_ns; + + uint8_t tp_lite_sigma_ref_mm; + + uint8_t tp_lite_seed_cfg; + + uint8_t tp_timed_seed_cfg; + + + uint8_t tp_lite_quantifier; + + uint8_t tp_lite_first_order_select; + + + uint16_t tp_dss_target_lite_mcps; + + uint16_t tp_dss_target_histo_mcps; + + uint16_t tp_dss_target_histo_mz_mcps; + + uint16_t tp_dss_target_timed_mcps; + + uint16_t tp_dss_target_very_short_mcps; + + + uint32_t tp_phasecal_timeout_lite_us; + + uint32_t tp_phasecal_timeout_hist_long_us; + + uint32_t tp_phasecal_timeout_hist_med_us; + + uint32_t tp_phasecal_timeout_hist_short_us; + + + uint32_t tp_phasecal_timeout_mz_long_us; + + uint32_t tp_phasecal_timeout_mz_med_us; + + uint32_t tp_phasecal_timeout_mz_short_us; + + uint32_t tp_phasecal_timeout_timed_us; + + + uint32_t tp_mm_timeout_lite_us; + + uint32_t tp_mm_timeout_histo_us; + + uint32_t tp_mm_timeout_mz_us; + + uint32_t tp_mm_timeout_timed_us; + + uint32_t tp_mm_timeout_lpa_us; + + + uint32_t tp_range_timeout_lite_us; + + uint32_t tp_range_timeout_histo_us; + + uint32_t tp_range_timeout_mz_us; + + uint32_t tp_range_timeout_timed_us; + + uint32_t tp_range_timeout_lpa_us; + + uint32_t tp_phasecal_patch_power; + + uint8_t tp_hist_merge; + + uint32_t tp_reset_merge_threshold; + + uint8_t tp_hist_merge_max_size; + + uint8_t tp_uwr_enable; + int16_t tp_uwr_med_z_1_min; + int16_t tp_uwr_med_z_1_max; + int16_t tp_uwr_med_z_2_min; + int16_t tp_uwr_med_z_2_max; + int16_t tp_uwr_med_z_3_min; + int16_t tp_uwr_med_z_3_max; + int16_t tp_uwr_med_z_4_min; + int16_t tp_uwr_med_z_4_max; + int16_t tp_uwr_med_z_5_min; + int16_t tp_uwr_med_z_5_max; + int16_t tp_uwr_med_z_6_min; + int16_t tp_uwr_med_z_6_max; + int16_t tp_uwr_med_corr_z_1_rangea; + int16_t tp_uwr_med_corr_z_1_rangeb; + int16_t tp_uwr_med_corr_z_2_rangea; + int16_t tp_uwr_med_corr_z_2_rangeb; + int16_t tp_uwr_med_corr_z_3_rangea; + int16_t tp_uwr_med_corr_z_3_rangeb; + int16_t tp_uwr_med_corr_z_4_rangea; + int16_t tp_uwr_med_corr_z_4_rangeb; + int16_t tp_uwr_med_corr_z_5_rangea; + int16_t tp_uwr_med_corr_z_5_rangeb; + int16_t tp_uwr_med_corr_z_6_rangea; + int16_t tp_uwr_med_corr_z_6_rangeb; + int16_t tp_uwr_lng_z_1_min; + int16_t tp_uwr_lng_z_1_max; + int16_t tp_uwr_lng_z_2_min; + int16_t tp_uwr_lng_z_2_max; + int16_t tp_uwr_lng_z_3_min; + int16_t tp_uwr_lng_z_3_max; + int16_t tp_uwr_lng_z_4_min; + int16_t tp_uwr_lng_z_4_max; + int16_t tp_uwr_lng_z_5_min; + int16_t tp_uwr_lng_z_5_max; + int16_t tp_uwr_lng_corr_z_1_rangea; + int16_t tp_uwr_lng_corr_z_1_rangeb; + int16_t tp_uwr_lng_corr_z_2_rangea; + int16_t tp_uwr_lng_corr_z_2_rangeb; + int16_t tp_uwr_lng_corr_z_3_rangea; + int16_t tp_uwr_lng_corr_z_3_rangeb; + int16_t tp_uwr_lng_corr_z_4_rangea; + int16_t tp_uwr_lng_corr_z_4_rangeb; + int16_t tp_uwr_lng_corr_z_5_rangea; + int16_t tp_uwr_lng_corr_z_5_rangeb; + uint32_t tp_min_signal_secondary_targets; + +} VL53L1_tuning_parm_storage_t; + + + + + +typedef struct { + + uint8_t x_centre; + uint8_t y_centre; + +} VL53L1_optical_centre_t; + + + + +typedef struct { + + uint8_t x_centre; + uint8_t y_centre; + uint8_t width; + uint8_t height; + +} VL53L1_user_zone_t; + + + + +typedef struct { + + uint8_t max_zones; + uint8_t active_zones; + + + +VL53L1_histogram_config_t multizone_hist_cfg; + + VL53L1_user_zone_t user_zones[VL53L1_MAX_USER_ZONES]; + + + uint8_t bin_config[VL53L1_MAX_USER_ZONES]; + + +} VL53L1_zone_config_t; + + + +typedef struct { + + + VL53L1_GPIO_Interrupt_Mode intr_mode_distance; + + + VL53L1_GPIO_Interrupt_Mode intr_mode_rate; + + + uint8_t intr_new_measure_ready; + + + uint8_t intr_no_target; + + + uint8_t intr_combined_mode; + + + + + + uint16_t threshold_distance_high; + + + uint16_t threshold_distance_low; + + + uint16_t threshold_rate_high; + + + uint16_t threshold_rate_low; + +} VL53L1_GPIO_interrupt_config_t; + + + + +typedef struct { + + + uint8_t vhv_loop_bound; + + + uint8_t is_low_power_auto_mode; + + + uint8_t low_power_auto_range_count; + + + uint8_t saved_interrupt_config; + + + uint8_t saved_vhv_init; + + + uint8_t saved_vhv_timeout; + + + uint8_t first_run_phasecal_result; + + + uint32_t dss__total_rate_per_spad_mcps; + + + uint16_t dss__required_spads; + +} VL53L1_low_power_auto_data_t; + + + + + + + +typedef struct { + + + uint8_t smudge_corr_enabled; + + + uint8_t smudge_corr_apply_enabled; + + + uint8_t smudge_corr_single_apply; + + + + + uint16_t smudge_margin; + + + uint32_t noise_margin; + + + uint32_t user_xtalk_offset_limit; + + + uint8_t user_xtalk_offset_limit_hi; + + + uint32_t sample_limit; + + + uint32_t single_xtalk_delta; + + + uint32_t averaged_xtalk_delta; + + + uint32_t smudge_corr_clip_limit; + + + uint32_t smudge_corr_ambient_threshold; + + + uint8_t scaler_calc_method; + + + int16_t x_gradient_scaler; + + + int16_t y_gradient_scaler; + + + uint8_t user_scaler_set; + + + uint32_t nodetect_ambient_threshold; + + + uint32_t nodetect_sample_limit; + + + uint32_t nodetect_xtalk_offset; + + + uint16_t nodetect_min_range_mm; + + + uint32_t max_smudge_factor; + +} VL53L1_smudge_corrector_config_t; + + + +typedef struct { + + + uint32_t current_samples; + + + uint32_t required_samples; + + + uint64_t accumulator; + + + uint32_t nodetect_counter; + +} VL53L1_smudge_corrector_internals_t; + + + +typedef struct { + + + uint8_t smudge_corr_valid; + + + uint8_t smudge_corr_clipped; + + + uint8_t single_xtalk_delta_flag; + + + uint8_t averaged_xtalk_delta_flag; + + + uint8_t sample_limit_exceeded_flag; + + + uint8_t gradient_zero_flag; + + + uint8_t new_xtalk_applied_flag; + + + uint32_t algo__crosstalk_compensation_plane_offset_kcps; + + + int16_t algo__crosstalk_compensation_x_plane_gradient_kcps; + + + int16_t algo__crosstalk_compensation_y_plane_gradient_kcps; + + +} VL53L1_smudge_corrector_data_t; + + + + + +typedef struct { + + + + uint8_t range_id; + + uint32_t time_stamp; + + uint8_t VL53L1_p_015; + + uint8_t VL53L1_p_022; + + uint8_t VL53L1_p_025; + + uint8_t VL53L1_p_026; + + uint8_t VL53L1_p_016; + + uint8_t VL53L1_p_027; + + + uint16_t width; + + uint8_t VL53L1_p_030; + + + uint16_t fast_osc_frequency; + + uint16_t zero_distance_phase; + + uint16_t VL53L1_p_006; + + + uint32_t total_periods_elapsed; + + + uint32_t peak_duration_us; + + + uint32_t woi_duration_us; + + + + + + uint32_t VL53L1_p_020; + + uint32_t VL53L1_p_021; + + int32_t VL53L1_p_013; + + + + + uint16_t peak_signal_count_rate_mcps; + + uint16_t avg_signal_count_rate_mcps; + + uint16_t ambient_count_rate_mcps; + + uint16_t total_rate_per_spad_mcps; + + uint32_t VL53L1_p_012; + + + + + uint16_t VL53L1_p_005; + + + + + uint16_t VL53L1_p_028; + + uint16_t VL53L1_p_014; + + uint16_t VL53L1_p_029; + + + + + int16_t min_range_mm; + + int16_t median_range_mm; + + int16_t max_range_mm; + + + + + uint8_t range_status; + +} VL53L1_range_data_t; + + + + +typedef struct { + + VL53L1_DeviceState cfg_device_state; + + VL53L1_DeviceState rd_device_state; + + uint8_t zone_id; + + uint8_t stream_count; + + + int16_t VL53L1_p_007[VL53L1_MAX_AMBIENT_DMAX_VALUES]; + + int16_t wrap_dmax_mm; + + + uint8_t device_status; + + + uint8_t max_results; + + uint8_t active_results; + + VL53L1_range_data_t VL53L1_p_002[VL53L1_MAX_RANGE_RESULTS]; + + VL53L1_range_data_t xmonitor; + + VL53L1_smudge_corrector_data_t smudge_corrector_data; + + + +} VL53L1_range_results_t; + + + + +typedef struct { + + uint8_t no_of_samples; + + uint32_t rate_per_spad_kcps_sum; + + uint32_t rate_per_spad_kcps_avg; + + int32_t signal_total_events_sum; + + int32_t signal_total_events_avg; + + uint32_t sigma_mm_sum; + + uint32_t sigma_mm_avg; + + uint32_t median_phase_sum; + + uint32_t median_phase_avg; + + +} VL53L1_xtalk_range_data_t; + + + + +typedef struct { + + VL53L1_Error cal_status; + + uint8_t num_of_samples_status; + + uint8_t zero_samples_status; + + uint8_t max_sigma_status; + + uint8_t max_results; + + uint8_t active_results; + + + VL53L1_xtalk_range_data_t + VL53L1_p_002[VL53L1_MAX_XTALK_RANGE_RESULTS]; + + VL53L1_histogram_bin_data_t central_histogram_sum; + + VL53L1_histogram_bin_data_t central_histogram_avg; + + uint8_t central_histogram__window_start; + + uint8_t central_histogram__window_end; + + VL53L1_histogram_bin_data_t + histogram_avg_1[VL53L1_MAX_XTALK_RANGE_RESULTS]; + + VL53L1_histogram_bin_data_t + histogram_avg_2[VL53L1_MAX_XTALK_RANGE_RESULTS]; + + VL53L1_histogram_bin_data_t + xtalk_avg[VL53L1_MAX_XTALK_RANGE_RESULTS]; + + +} VL53L1_xtalk_range_results_t; + + + + +typedef struct { + + uint8_t preset_mode; + + uint8_t dss_config__roi_mode_control; + + uint16_t dss_config__manual_effective_spads_select; + + uint8_t no_of_samples; + + uint32_t effective_spads; + + uint32_t peak_rate_mcps; + + uint32_t VL53L1_p_005; + + int32_t median_range_mm; + + int32_t range_mm_offset; + + +} VL53L1_offset_range_data_t; + + + + +typedef struct { + + int16_t cal_distance_mm; + + uint16_t cal_reflectance_pc; + + VL53L1_Error cal_status; + + uint8_t cal_report; + + uint8_t max_results; + + uint8_t active_results; + + VL53L1_offset_range_data_t + VL53L1_p_002[VL53L1_MAX_OFFSET_RANGE_RESULTS]; + + +} VL53L1_offset_range_results_t; + + + + +typedef struct { + + uint16_t result__mm_inner_actual_effective_spads; + + uint16_t result__mm_outer_actual_effective_spads; + + uint16_t result__mm_inner_peak_signal_count_rtn_mcps; + + uint16_t result__mm_outer_peak_signal_count_rtn_mcps; + + +} VL53L1_additional_offset_cal_data_t; + + + +typedef struct { + int16_t short_a_offset_mm; + int16_t short_b_offset_mm; + int16_t medium_a_offset_mm; + int16_t medium_b_offset_mm; + int16_t long_a_offset_mm; + int16_t long_b_offset_mm; +} VL53L1_per_vcsel_period_offset_cal_data_t; + + + + + +typedef struct { + + uint32_t VL53L1_p_020; + + uint32_t VL53L1_p_021; + + uint16_t VL53L1_p_014; + + uint8_t range_status; + + +} VL53L1_object_data_t; + + + + +typedef struct { + + VL53L1_DeviceState cfg_device_state; + + VL53L1_DeviceState rd_device_state; + + uint8_t zone_id; + + uint8_t stream_count; + + uint8_t max_objects; + + uint8_t active_objects; + + VL53L1_object_data_t VL53L1_p_002[VL53L1_MAX_RANGE_RESULTS]; + + + VL53L1_object_data_t xmonitor; + + +} VL53L1_zone_objects_t; + + + + + + +typedef struct { + + uint8_t max_zones; + + uint8_t active_zones; + + VL53L1_zone_objects_t VL53L1_p_002[VL53L1_MAX_USER_ZONES]; + + +} VL53L1_zone_results_t; + + + + +typedef struct { + + VL53L1_DeviceState rd_device_state; + + + uint8_t number_of_ambient_bins; + + + uint16_t result__dss_actual_effective_spads; + + uint8_t VL53L1_p_009; + + uint32_t total_periods_elapsed; + + + int32_t ambient_events_sum; + + +} VL53L1_zone_hist_info_t; + + + + +typedef struct { + + uint8_t max_zones; + + uint8_t active_zones; + + VL53L1_zone_hist_info_t VL53L1_p_002[VL53L1_MAX_USER_ZONES]; + + +} VL53L1_zone_histograms_t; + + + + +typedef struct { + + uint32_t no_of_samples; + + uint32_t effective_spads; + + uint32_t peak_rate_mcps; + + uint32_t VL53L1_p_014; + + uint32_t VL53L1_p_005; + + int32_t median_range_mm; + + int32_t range_mm_offset; + + +} VL53L1_zone_calibration_data_t; + + + + + + +typedef struct { + + uint32_t struct_version; + + VL53L1_DevicePresetModes preset_mode; + + VL53L1_DeviceZonePreset zone_preset; + + int16_t cal_distance_mm; + + uint16_t cal_reflectance_pc; + + uint16_t phasecal_result__reference_phase; + + uint16_t zero_distance_phase; + + VL53L1_Error cal_status; + + uint8_t max_zones; + + uint8_t active_zones; + + VL53L1_zone_calibration_data_t VL53L1_p_002[VL53L1_MAX_USER_ZONES]; + + +} VL53L1_zone_calibration_results_t; + + + + + +typedef struct { + + int16_t cal_distance_mm; + + uint16_t cal_reflectance_pc; + + uint16_t max_samples; + + uint16_t width; + + uint16_t height; + + uint16_t peak_rate_mcps[VL53L1_NVM_PEAK_RATE_MAP_SAMPLES]; + + +} VL53L1_cal_peak_rate_map_t; + + + + +typedef struct { + + uint8_t expected_stream_count; + + uint8_t expected_gph_id; + + uint8_t dss_mode; + + uint16_t dss_requested_effective_spad_count; + + uint8_t seed_cfg; + + uint8_t initial_phase_seed; + + + uint8_t roi_config__user_roi_centre_spad; + + uint8_t roi_config__user_roi_requested_global_xy_size; + + +} VL53L1_zone_private_dyn_cfg_t; + + + + +typedef struct { + + uint8_t max_zones; + + uint8_t active_zones; + + VL53L1_zone_private_dyn_cfg_t VL53L1_p_002[VL53L1_MAX_USER_ZONES]; + + +} VL53L1_zone_private_dyn_cfgs_t; + + + +typedef struct { + + uint32_t algo__crosstalk_compensation_plane_offset_kcps; + + int16_t algo__crosstalk_compensation_x_plane_gradient_kcps; + + int16_t algo__crosstalk_compensation_y_plane_gradient_kcps; + + uint32_t algo__xtalk_cpo_HistoMerge_kcps[VL53L1_BIN_REC_SIZE]; + + +} VL53L1_xtalk_calibration_results_t; + + + + +typedef struct { + + + uint32_t sample_count; + + + uint32_t pll_period_mm; + + + uint32_t peak_duration_us_sum; + + + uint32_t effective_spad_count_sum; + + + uint32_t zero_distance_phase_sum; + + + uint32_t zero_distance_phase_avg; + + + int32_t event_scaler_sum; + + + int32_t event_scaler_avg; + + + int32_t signal_events_sum; + + + uint32_t xtalk_rate_kcps_per_spad; + + + int32_t xtalk_start_phase; + + + int32_t xtalk_end_phase; + + + int32_t xtalk_width_phase; + + + int32_t target_start_phase; + + + int32_t target_end_phase; + + + int32_t target_width_phase; + + + int32_t effective_width; + + + int32_t event_scaler; + + + uint8_t VL53L1_p_015; + + + uint8_t VL53L1_p_016; + + + uint8_t target_start; + + + int32_t max_shape_value; + + + int32_t bin_data_sums[VL53L1_XTALK_HISTO_BINS]; + +} VL53L1_hist_xtalk_extract_data_t; + + + + +typedef struct { + + uint16_t standard_ranging_gain_factor; + + uint16_t histogram_ranging_gain_factor; + + +} VL53L1_gain_calibration_data_t; + + + + +typedef struct { + + VL53L1_DeviceState cfg_device_state; + + uint8_t cfg_stream_count; + + uint8_t cfg_internal_stream_count; + + uint8_t cfg_internal_stream_count_val; + + uint8_t cfg_gph_id; + + uint8_t cfg_timing_status; + + uint8_t cfg_zone_id; + + + VL53L1_DeviceState rd_device_state; + + uint8_t rd_stream_count; + + uint8_t rd_internal_stream_count; + + uint8_t rd_internal_stream_count_val; + + uint8_t rd_gph_id; + + uint8_t rd_timing_status; + + uint8_t rd_zone_id; + + +} VL53L1_ll_driver_state_t; + + + + +typedef struct { + + uint8_t wait_method; + + VL53L1_DevicePresetModes preset_mode; + + VL53L1_DeviceZonePreset zone_preset; + + VL53L1_DeviceMeasurementModes measurement_mode; + + VL53L1_OffsetCalibrationMode offset_calibration_mode; + + VL53L1_OffsetCorrectionMode offset_correction_mode; + + VL53L1_DeviceDmaxMode dmax_mode; + + uint32_t phasecal_config_timeout_us; + + uint32_t mm_config_timeout_us; + + uint32_t range_config_timeout_us; + + uint32_t inter_measurement_period_ms; + + uint16_t dss_config__target_total_rate_mcps; + + uint32_t fw_ready_poll_duration_ms; + + uint8_t fw_ready; + + uint8_t debug_mode; + + + + VL53L1_ll_version_t version; + + + VL53L1_ll_driver_state_t ll_state; + + + VL53L1_GPIO_interrupt_config_t gpio_interrupt_config; + + + VL53L1_customer_nvm_managed_t customer; + VL53L1_cal_peak_rate_map_t cal_peak_rate_map; + VL53L1_additional_offset_cal_data_t add_off_cal_data; + VL53L1_dmax_calibration_data_t fmt_dmax_cal; + VL53L1_dmax_calibration_data_t cust_dmax_cal; + VL53L1_gain_calibration_data_t gain_cal; + VL53L1_user_zone_t mm_roi; + VL53L1_optical_centre_t optical_centre; + VL53L1_zone_config_t zone_cfg; + + + VL53L1_tuning_parm_storage_t tuning_parms; + + + uint8_t rtn_good_spads[VL53L1_RTN_SPAD_BUFFER_SIZE]; + + + VL53L1_refspadchar_config_t refspadchar; + VL53L1_ssc_config_t ssc_cfg; + VL53L1_hist_post_process_config_t histpostprocess; + VL53L1_hist_gen3_dmax_config_t dmax_cfg; + VL53L1_xtalkextract_config_t xtalk_extract_cfg; + VL53L1_xtalk_config_t xtalk_cfg; + VL53L1_offsetcal_config_t offsetcal_cfg; + VL53L1_zonecal_config_t zonecal_cfg; + + + VL53L1_static_nvm_managed_t stat_nvm; + VL53L1_histogram_config_t hist_cfg; + VL53L1_static_config_t stat_cfg; + VL53L1_general_config_t gen_cfg; + VL53L1_timing_config_t tim_cfg; + VL53L1_dynamic_config_t dyn_cfg; + VL53L1_system_control_t sys_ctrl; + VL53L1_system_results_t sys_results; + VL53L1_nvm_copy_data_t nvm_copy_data; + + + VL53L1_histogram_bin_data_t hist_data; + VL53L1_histogram_bin_data_t hist_xtalk; + + + VL53L1_xtalk_histogram_data_t xtalk_shapes; + VL53L1_xtalk_range_results_t xtalk_results; + VL53L1_xtalk_calibration_results_t xtalk_cal; + VL53L1_hist_xtalk_extract_data_t xtalk_extract; + + + VL53L1_offset_range_results_t offset_results; + + + VL53L1_core_results_t core_results; + VL53L1_debug_results_t dbg_results; + + VL53L1_smudge_corrector_config_t smudge_correct_config; + + VL53L1_smudge_corrector_internals_t smudge_corrector_internals; + + + + + VL53L1_low_power_auto_data_t low_power_auto_data; + + +#ifdef PAL_EXTENDED + + VL53L1_patch_results_t patch_results; + VL53L1_shadow_core_results_t shadow_core_results; + VL53L1_shadow_system_results_t shadow_sys_results; + VL53L1_prev_shadow_core_results_t prev_shadow_core_results; + VL53L1_prev_shadow_system_results_t prev_shadow_sys_results; +#endif + uint8_t wArea1[1536]; + uint8_t wArea2[512]; + VL53L1_per_vcsel_period_offset_cal_data_t per_vcsel_cal_data; + + uint8_t bin_rec_pos; + + uint8_t pos_before_next_recom; + + int32_t multi_bins_rec[VL53L1_BIN_REC_SIZE] + [VL53L1_TIMING_CONF_A_B_SIZE][VL53L1_HISTOGRAM_BUFFER_SIZE]; + + int16_t PreviousRangeMilliMeter[VL53L1_MAX_RANGE_RESULTS]; + uint8_t PreviousRangeStatus[VL53L1_MAX_RANGE_RESULTS]; + uint8_t PreviousExtendedRange[VL53L1_MAX_RANGE_RESULTS]; + uint8_t PreviousRangeActiveResults; + uint8_t PreviousStreamCount; + +} VL53L1_LLDriverData_t; + + + + +typedef struct { + + + VL53L1_range_results_t range_results; + + + VL53L1_zone_private_dyn_cfgs_t zone_dyn_cfgs; + + + VL53L1_zone_results_t zone_results; + VL53L1_zone_histograms_t zone_hists; + VL53L1_zone_calibration_results_t zone_cal; + +} VL53L1_LLDriverResults_t; + + + + +typedef struct { + + uint32_t struct_version; + VL53L1_customer_nvm_managed_t customer; + VL53L1_dmax_calibration_data_t fmt_dmax_cal; + VL53L1_dmax_calibration_data_t cust_dmax_cal; + VL53L1_additional_offset_cal_data_t add_off_cal_data; + VL53L1_optical_centre_t optical_centre; + VL53L1_xtalk_histogram_data_t xtalkhisto; + VL53L1_gain_calibration_data_t gain_cal; + VL53L1_cal_peak_rate_map_t cal_peak_rate_map; + VL53L1_per_vcsel_period_offset_cal_data_t per_vcsel_cal_data; + +} VL53L1_calibration_data_t; + + + + +typedef struct { + + VL53L1_customer_nvm_managed_t customer; + VL53L1_xtalkextract_config_t xtalk_extract_cfg; + VL53L1_xtalk_config_t xtalk_cfg; + VL53L1_histogram_bin_data_t hist_data; + VL53L1_xtalk_histogram_data_t xtalk_shapes; + VL53L1_xtalk_range_results_t xtalk_results; + +} VL53L1_xtalk_debug_data_t; + + + + +typedef struct { + + VL53L1_customer_nvm_managed_t customer; + VL53L1_dmax_calibration_data_t fmt_dmax_cal; + VL53L1_dmax_calibration_data_t cust_dmax_cal; + VL53L1_additional_offset_cal_data_t add_off_cal_data; + VL53L1_offset_range_results_t offset_results; + +} VL53L1_offset_debug_data_t; + + + + +typedef struct { + uint16_t vl53l1_tuningparm_version; + uint16_t vl53l1_tuningparm_key_table_version; + uint16_t vl53l1_tuningparm_lld_version; + uint8_t vl53l1_tuningparm_hist_algo_select; + uint8_t vl53l1_tuningparm_hist_target_order; + uint8_t vl53l1_tuningparm_hist_filter_woi_0; + uint8_t vl53l1_tuningparm_hist_filter_woi_1; + uint8_t vl53l1_tuningparm_hist_amb_est_method; + uint8_t vl53l1_tuningparm_hist_amb_thresh_sigma_0; + uint8_t vl53l1_tuningparm_hist_amb_thresh_sigma_1; + int32_t vl53l1_tuningparm_hist_min_amb_thresh_events; + uint16_t vl53l1_tuningparm_hist_amb_events_scaler; + uint16_t vl53l1_tuningparm_hist_noise_threshold; + int32_t vl53l1_tuningparm_hist_signal_total_events_limit; + uint8_t vl53l1_tuningparm_hist_sigma_est_ref_mm; + uint16_t vl53l1_tuningparm_hist_sigma_thresh_mm; + uint16_t vl53l1_tuningparm_hist_gain_factor; + uint8_t vl53l1_tuningparm_consistency_hist_phase_tolerance; + uint16_t vl53l1_tuningparm_consistency_hist_min_max_tolerance_mm; + uint8_t vl53l1_tuningparm_consistency_hist_event_sigma; + uint16_t vl53l1_tuningparm_consistency_hist_event_sigma_min_spad_limit; + uint8_t vl53l1_tuningparm_initial_phase_rtn_histo_long_range; + uint8_t vl53l1_tuningparm_initial_phase_rtn_histo_med_range; + uint8_t vl53l1_tuningparm_initial_phase_rtn_histo_short_range; + uint8_t vl53l1_tuningparm_initial_phase_ref_histo_long_range; + uint8_t vl53l1_tuningparm_initial_phase_ref_histo_med_range; + uint8_t vl53l1_tuningparm_initial_phase_ref_histo_short_range; + int16_t vl53l1_tuningparm_xtalk_detect_min_valid_range_mm; + int16_t vl53l1_tuningparm_xtalk_detect_max_valid_range_mm; + uint16_t vl53l1_tuningparm_xtalk_detect_max_sigma_mm; + uint16_t vl53l1_tuningparm_xtalk_detect_min_max_tolerance; + uint16_t vl53l1_tuningparm_xtalk_detect_max_valid_rate_kcps; + uint8_t vl53l1_tuningparm_xtalk_detect_event_sigma; + int16_t vl53l1_tuningparm_hist_xtalk_margin_kcps; + uint8_t vl53l1_tuningparm_consistency_lite_phase_tolerance; + uint8_t vl53l1_tuningparm_phasecal_target; + uint16_t vl53l1_tuningparm_lite_cal_repeat_rate; + uint16_t vl53l1_tuningparm_lite_ranging_gain_factor; + uint8_t vl53l1_tuningparm_lite_min_clip_mm; + uint16_t vl53l1_tuningparm_lite_long_sigma_thresh_mm; + uint16_t vl53l1_tuningparm_lite_med_sigma_thresh_mm; + uint16_t vl53l1_tuningparm_lite_short_sigma_thresh_mm; + uint16_t vl53l1_tuningparm_lite_long_min_count_rate_rtn_mcps; + uint16_t vl53l1_tuningparm_lite_med_min_count_rate_rtn_mcps; + uint16_t vl53l1_tuningparm_lite_short_min_count_rate_rtn_mcps; + uint8_t vl53l1_tuningparm_lite_sigma_est_pulse_width; + uint8_t vl53l1_tuningparm_lite_sigma_est_amb_width_ns; + uint8_t vl53l1_tuningparm_lite_sigma_ref_mm; + uint8_t vl53l1_tuningparm_lite_rit_mult; + uint8_t vl53l1_tuningparm_lite_seed_config; + uint8_t vl53l1_tuningparm_lite_quantifier; + uint8_t vl53l1_tuningparm_lite_first_order_select; + int16_t vl53l1_tuningparm_lite_xtalk_margin_kcps; + uint8_t vl53l1_tuningparm_initial_phase_rtn_lite_long_range; + uint8_t vl53l1_tuningparm_initial_phase_rtn_lite_med_range; + uint8_t vl53l1_tuningparm_initial_phase_rtn_lite_short_range; + uint8_t vl53l1_tuningparm_initial_phase_ref_lite_long_range; + uint8_t vl53l1_tuningparm_initial_phase_ref_lite_med_range; + uint8_t vl53l1_tuningparm_initial_phase_ref_lite_short_range; + uint8_t vl53l1_tuningparm_timed_seed_config; + uint8_t vl53l1_tuningparm_dmax_cfg_signal_thresh_sigma; + uint16_t vl53l1_tuningparm_dmax_cfg_reflectance_array_0; + uint16_t vl53l1_tuningparm_dmax_cfg_reflectance_array_1; + uint16_t vl53l1_tuningparm_dmax_cfg_reflectance_array_2; + uint16_t vl53l1_tuningparm_dmax_cfg_reflectance_array_3; + uint16_t vl53l1_tuningparm_dmax_cfg_reflectance_array_4; + uint8_t vl53l1_tuningparm_vhv_loopbound; + uint8_t vl53l1_tuningparm_refspadchar_device_test_mode; + uint8_t vl53l1_tuningparm_refspadchar_vcsel_period; + uint32_t vl53l1_tuningparm_refspadchar_phasecal_timeout_us; + uint16_t vl53l1_tuningparm_refspadchar_target_count_rate_mcps; + uint16_t vl53l1_tuningparm_refspadchar_min_countrate_limit_mcps; + uint16_t vl53l1_tuningparm_refspadchar_max_countrate_limit_mcps; + uint8_t vl53l1_tuningparm_xtalk_extract_num_of_samples; + int16_t vl53l1_tuningparm_xtalk_extract_min_filter_thresh_mm; + int16_t vl53l1_tuningparm_xtalk_extract_max_filter_thresh_mm; + uint16_t vl53l1_tuningparm_xtalk_extract_dss_rate_mcps; + uint32_t vl53l1_tuningparm_xtalk_extract_phasecal_timeout_us; + uint16_t vl53l1_tuningparm_xtalk_extract_max_valid_rate_kcps; + uint16_t vl53l1_tuningparm_xtalk_extract_sigma_threshold_mm; + uint32_t vl53l1_tuningparm_xtalk_extract_dss_timeout_us; + uint32_t vl53l1_tuningparm_xtalk_extract_bin_timeout_us; + uint16_t vl53l1_tuningparm_offset_cal_dss_rate_mcps; + uint32_t vl53l1_tuningparm_offset_cal_phasecal_timeout_us; + uint32_t vl53l1_tuningparm_offset_cal_mm_timeout_us; + uint32_t vl53l1_tuningparm_offset_cal_range_timeout_us; + uint8_t vl53l1_tuningparm_offset_cal_pre_samples; + uint8_t vl53l1_tuningparm_offset_cal_mm1_samples; + uint8_t vl53l1_tuningparm_offset_cal_mm2_samples; + uint16_t vl53l1_tuningparm_zone_cal_dss_rate_mcps; + uint32_t vl53l1_tuningparm_zone_cal_phasecal_timeout_us; + uint32_t vl53l1_tuningparm_zone_cal_dss_timeout_us; + uint16_t vl53l1_tuningparm_zone_cal_phasecal_num_samples; + uint32_t vl53l1_tuningparm_zone_cal_range_timeout_us; + uint16_t vl53l1_tuningparm_zone_cal_zone_num_samples; + uint8_t vl53l1_tuningparm_spadmap_vcsel_period; + uint8_t vl53l1_tuningparm_spadmap_vcsel_start; + uint16_t vl53l1_tuningparm_spadmap_rate_limit_mcps; + uint16_t vl53l1_tuningparm_lite_dss_config_target_total_rate_mcps; + uint16_t vl53l1_tuningparm_ranging_dss_config_target_total_rate_mcps; + uint16_t vl53l1_tuningparm_mz_dss_config_target_total_rate_mcps; + uint16_t vl53l1_tuningparm_timed_dss_config_target_total_rate_mcps; + uint32_t vl53l1_tuningparm_lite_phasecal_config_timeout_us; + uint32_t vl53l1_tuningparm_ranging_long_phasecal_config_timeout_us; + uint32_t vl53l1_tuningparm_ranging_med_phasecal_config_timeout_us; + uint32_t vl53l1_tuningparm_ranging_short_phasecal_config_timeout_us; + uint32_t vl53l1_tuningparm_mz_long_phasecal_config_timeout_us; + uint32_t vl53l1_tuningparm_mz_med_phasecal_config_timeout_us; + uint32_t vl53l1_tuningparm_mz_short_phasecal_config_timeout_us; + uint32_t vl53l1_tuningparm_timed_phasecal_config_timeout_us; + uint32_t vl53l1_tuningparm_lite_mm_config_timeout_us; + uint32_t vl53l1_tuningparm_ranging_mm_config_timeout_us; + uint32_t vl53l1_tuningparm_mz_mm_config_timeout_us; + uint32_t vl53l1_tuningparm_timed_mm_config_timeout_us; + uint32_t vl53l1_tuningparm_lite_range_config_timeout_us; + uint32_t vl53l1_tuningparm_ranging_range_config_timeout_us; + uint32_t vl53l1_tuningparm_mz_range_config_timeout_us; + uint32_t vl53l1_tuningparm_timed_range_config_timeout_us; + uint16_t vl53l1_tuningparm_dynxtalk_smudge_margin; + uint32_t vl53l1_tuningparm_dynxtalk_noise_margin; + uint32_t vl53l1_tuningparm_dynxtalk_xtalk_offset_limit; + uint8_t vl53l1_tuningparm_dynxtalk_xtalk_offset_limit_hi; + uint32_t vl53l1_tuningparm_dynxtalk_sample_limit; + uint32_t vl53l1_tuningparm_dynxtalk_single_xtalk_delta; + uint32_t vl53l1_tuningparm_dynxtalk_averaged_xtalk_delta; + uint32_t vl53l1_tuningparm_dynxtalk_clip_limit; + uint8_t vl53l1_tuningparm_dynxtalk_scaler_calc_method; + int16_t vl53l1_tuningparm_dynxtalk_xgradient_scaler; + int16_t vl53l1_tuningparm_dynxtalk_ygradient_scaler; + uint8_t vl53l1_tuningparm_dynxtalk_user_scaler_set; + uint8_t vl53l1_tuningparm_dynxtalk_smudge_cor_single_apply; + uint32_t vl53l1_tuningparm_dynxtalk_xtalk_amb_threshold; + uint32_t vl53l1_tuningparm_dynxtalk_nodetect_amb_threshold_kcps; + uint32_t vl53l1_tuningparm_dynxtalk_nodetect_sample_limit; + uint32_t vl53l1_tuningparm_dynxtalk_nodetect_xtalk_offset_kcps; + uint16_t vl53l1_tuningparm_dynxtalk_nodetect_min_range_mm; + uint8_t vl53l1_tuningparm_lowpowerauto_vhv_loop_bound; + uint32_t vl53l1_tuningparm_lowpowerauto_mm_config_timeout_us; + uint32_t vl53l1_tuningparm_lowpowerauto_range_config_timeout_us; + uint16_t vl53l1_tuningparm_very_short_dss_rate_mcps; + uint32_t vl53l1_tuningparm_phasecal_patch_power; +} VL53L1_tuning_parameters_t; + + + + + +typedef struct { + + uint16_t target_reflectance_for_dmax[VL53L1_MAX_AMBIENT_DMAX_VALUES]; + +} VL53L1_dmax_reflectance_array_t; + + + + +typedef struct { + + uint8_t spad_type; + + uint16_t VL53L1_p_023; + + uint16_t rate_data[VL53L1_NO_OF_SPAD_ENABLES]; + + uint16_t no_of_values; + + uint8_t fractional_bits; + + uint8_t error_status; + + +} VL53L1_spad_rate_data_t; + + + + + + +typedef struct { + + VL53L1_DevicePresetModes preset_mode; + + VL53L1_DeviceZonePreset zone_preset; + + VL53L1_DeviceMeasurementModes measurement_mode; + + VL53L1_OffsetCalibrationMode offset_calibration_mode; + + VL53L1_OffsetCorrectionMode offset_correction_mode; + + VL53L1_DeviceDmaxMode dmax_mode; + + + uint32_t phasecal_config_timeout_us; + + uint32_t mm_config_timeout_us; + + uint32_t range_config_timeout_us; + + uint32_t inter_measurement_period_ms; + + uint16_t dss_config__target_total_rate_mcps; + + + VL53L1_histogram_bin_data_t VL53L1_p_010; + + +} VL53L1_additional_data_t; + + + + + + + + +#define SUPPRESS_UNUSED_WARNING(x) \ + ((void) (x)) + + +#define IGNORE_STATUS(__FUNCTION_ID__, __ERROR_STATUS_CHECK__, __STATUS__) \ + do { \ + DISABLE_WARNINGS(); \ + if (__FUNCTION_ID__) { \ + if (__STATUS__ == __ERROR_STATUS_CHECK__) { \ + __STATUS__ = VL53L1_ERROR_NONE; \ + WARN_OVERRIDE_STATUS(__FUNCTION_ID__); \ + } \ + } \ + ENABLE_WARNINGS(); \ + } \ + while (0) + +#define VL53L1_COPYSTRING(str, ...) \ + (strncpy(str, ##__VA_ARGS__, VL53L1_MAX_STRING_LENGTH-1)) + +#ifdef __cplusplus +} +#endif + +#endif + + + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_ll_device.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_ll_device.h new file mode 100644 index 000000000000..424dad4dd7ca --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_ll_device.h @@ -0,0 +1,1065 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#ifndef _VL53L1_LL_DEVICE_H_ +#define _VL53L1_LL_DEVICE_H_ + +#include "vl53l1_types.h" +#include "vl53l1_platform_user_config.h" + +#define VL53L1_I2C 0x01 +#define VL53L1_SPI 0x00 + + + + + +typedef uint8_t VL53L1_WaitMethod; + +#define VL53L1_WAIT_METHOD_BLOCKING ((VL53L1_WaitMethod) 0) +#define VL53L1_WAIT_METHOD_NON_BLOCKING ((VL53L1_WaitMethod) 1) + + + + +typedef uint8_t VL53L1_DeviceState; + +#define VL53L1_DEVICESTATE_POWERDOWN ((VL53L1_DeviceState) 0) +#define VL53L1_DEVICESTATE_HW_STANDBY ((VL53L1_DeviceState) 1) +#define VL53L1_DEVICESTATE_FW_COLDBOOT ((VL53L1_DeviceState) 2) +#define VL53L1_DEVICESTATE_SW_STANDBY ((VL53L1_DeviceState) 3) +#define VL53L1_DEVICESTATE_RANGING_DSS_AUTO ((VL53L1_DeviceState) 4) +#define VL53L1_DEVICESTATE_RANGING_DSS_MANUAL ((VL53L1_DeviceState) 5) +#define VL53L1_DEVICESTATE_RANGING_WAIT_GPH_SYNC ((VL53L1_DeviceState) 6) +#define VL53L1_DEVICESTATE_RANGING_GATHER_DATA ((VL53L1_DeviceState) 7) +#define VL53L1_DEVICESTATE_RANGING_OUTPUT_DATA ((VL53L1_DeviceState) 8) + +#define VL53L1_DEVICESTATE_UNKNOWN ((VL53L1_DeviceState) 98) +#define VL53L1_DEVICESTATE_ERROR ((VL53L1_DeviceState) 99) + + + + + +typedef uint8_t VL53L1_DeviceZonePreset; + +#define VL53L1_DEVICEZONEPRESET_NONE \ + ((VL53L1_DeviceZonePreset) 0) + +#define VL53L1_DEVICEZONEPRESET_XTALK_PLANAR \ + ((VL53L1_DeviceZonePreset) 1) +#define VL53L1_DEVICEZONEPRESET_1X1_SIZE_16X16 \ + ((VL53L1_DeviceZonePreset) 2) +#define VL53L1_DEVICEZONEPRESET_1X2_SIZE_16X8 \ + ((VL53L1_DeviceZonePreset) 3) +#define VL53L1_DEVICEZONEPRESET_2X1_SIZE_8X16 \ + ((VL53L1_DeviceZonePreset) 4) +#define VL53L1_DEVICEZONEPRESET_2X2_SIZE_8X8 \ + ((VL53L1_DeviceZonePreset) 5) +#define VL53L1_DEVICEZONEPRESET_3X3_SIZE_5X5 \ + ((VL53L1_DeviceZonePreset) 6) +#define VL53L1_DEVICEZONEPRESET_4X4_SIZE_4X4 \ + ((VL53L1_DeviceZonePreset) 7) +#define VL53L1_DEVICEZONEPRESET_5X5_SIZE_4X4 \ + ((VL53L1_DeviceZonePreset) 8) +#define VL53L1_DEVICEZONEPRESET_11X11_SIZE_5X5 \ + ((VL53L1_DeviceZonePreset) 9) +#define VL53L1_DEVICEZONEPRESET_13X13_SIZE_4X4 \ + ((VL53L1_DeviceZonePreset) 10) + +#define VL53L1_DEVICEZONEPRESET_1X1_SIZE_4X4_POS_8X8 \ + ((VL53L1_DeviceZonePreset) 11) + +#define VL53L1_DEVICEZONEPRESET_CUSTOM \ + ((VL53L1_DeviceZonePreset) 255) + + + + + +typedef uint8_t VL53L1_DevicePresetModes; + +#define VL53L1_DEVICEPRESETMODE_NONE \ + ((VL53L1_DevicePresetModes) 0) +#define VL53L1_DEVICEPRESETMODE_STANDARD_RANGING \ + ((VL53L1_DevicePresetModes) 1) +#define VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_SHORT_RANGE \ + ((VL53L1_DevicePresetModes) 2) +#define VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_LONG_RANGE \ + ((VL53L1_DevicePresetModes) 3) +#define VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_MM1_CAL \ + ((VL53L1_DevicePresetModes) 4) +#define VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_MM2_CAL \ + ((VL53L1_DevicePresetModes) 5) +#define VL53L1_DEVICEPRESETMODE_TIMED_RANGING \ + ((VL53L1_DevicePresetModes) 6) +#define VL53L1_DEVICEPRESETMODE_TIMED_RANGING_SHORT_RANGE \ + ((VL53L1_DevicePresetModes) 7) +#define VL53L1_DEVICEPRESETMODE_TIMED_RANGING_LONG_RANGE \ + ((VL53L1_DevicePresetModes) 8) +#define VL53L1_DEVICEPRESETMODE_NEAR_FARRANGING \ + ((VL53L1_DevicePresetModes) 9) +#define VL53L1_DEVICEPRESETMODE_QUADRANT_RANGING \ + ((VL53L1_DevicePresetModes) 10) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING \ + ((VL53L1_DevicePresetModes) 11) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_SHORT_TIMING \ + ((VL53L1_DevicePresetModes) 12) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_CHARACTERISATION \ + ((VL53L1_DevicePresetModes) 13) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_XTALK_PLANAR \ + ((VL53L1_DevicePresetModes) 14) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_XTALK_MM1 \ + ((VL53L1_DevicePresetModes) 15) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_XTALK_MM2 \ + ((VL53L1_DevicePresetModes) 16) +#define VL53L1_DEVICEPRESETMODE_OLT \ + ((VL53L1_DevicePresetModes) 17) +#define VL53L1_DEVICEPRESETMODE_SINGLESHOT_RANGING \ + ((VL53L1_DevicePresetModes) 18) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_REF_ARRAY \ + ((VL53L1_DevicePresetModes) 19) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_WITH_MM1 \ + ((VL53L1_DevicePresetModes) 20) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_WITH_MM2 \ + ((VL53L1_DevicePresetModes) 21) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_MM1_CAL \ + ((VL53L1_DevicePresetModes) 22) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_MM2_CAL \ + ((VL53L1_DevicePresetModes) 23) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE \ + ((VL53L1_DevicePresetModes) 24) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE_SHORT_RANGE \ + ((VL53L1_DevicePresetModes) 25) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE_LONG_RANGE \ + ((VL53L1_DevicePresetModes) 26) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE \ + ((VL53L1_DevicePresetModes) 27) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE_MM1 \ + ((VL53L1_DevicePresetModes) 28) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE_MM2 \ + ((VL53L1_DevicePresetModes) 29) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE \ + ((VL53L1_DevicePresetModes) 30) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE_MM1 \ + ((VL53L1_DevicePresetModes) 31) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE_MM2 \ + ((VL53L1_DevicePresetModes) 32) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE \ + ((VL53L1_DevicePresetModes) 33) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE_MM1 \ + ((VL53L1_DevicePresetModes) 34) +#define VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE_MM2 \ + ((VL53L1_DevicePresetModes) 35) +#define VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_SHORT_RANGE \ + ((VL53L1_DevicePresetModes) 36) +#define VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_MEDIUM_RANGE \ + ((VL53L1_DevicePresetModes) 37) +#define VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_LONG_RANGE \ + ((VL53L1_DevicePresetModes) 38) +#define VL53L1_DEVICEPRESETMODE_SPECIAL_HISTOGRAM_SHORT_RANGE \ + ((VL53L1_DevicePresetModes) 39) + + + + + +typedef uint8_t VL53L1_DeviceMeasurementModes; + +#define VL53L1_DEVICEMEASUREMENTMODE_STOP \ + ((VL53L1_DeviceMeasurementModes) 0x00) +#define VL53L1_DEVICEMEASUREMENTMODE_SINGLESHOT \ + ((VL53L1_DeviceMeasurementModes) 0x10) +#define VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK \ + ((VL53L1_DeviceMeasurementModes) 0x20) +#define VL53L1_DEVICEMEASUREMENTMODE_TIMED \ + ((VL53L1_DeviceMeasurementModes) 0x40) +#define VL53L1_DEVICEMEASUREMENTMODE_ABORT \ + ((VL53L1_DeviceMeasurementModes) 0x80) + + + + + +typedef uint8_t VL53L1_OffsetCalibrationMode; + +#define VL53L1_OFFSETCALIBRATIONMODE__NONE \ + ((VL53L1_OffsetCalibrationMode) 0) +#define VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD \ + ((VL53L1_OffsetCalibrationMode) 1) +#define VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__HISTOGRAM \ + ((VL53L1_OffsetCalibrationMode) 2) +#define VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD_PRE_RANGE_ONLY \ + ((VL53L1_OffsetCalibrationMode) 3) +#define VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__HISTOGRAM_PRE_RANGE_ONLY \ + ((VL53L1_OffsetCalibrationMode) 4) +#define VL53L1_OFFSETCALIBRATIONMODE__PER_ZONE \ + ((VL53L1_OffsetCalibrationMode) 5) + + + + + +typedef uint8_t VL53L1_OffsetCorrectionMode; + +#define VL53L1_OFFSETCORRECTIONMODE__NONE \ + ((VL53L1_OffsetCorrectionMode) 0) +#define VL53L1_OFFSETCORRECTIONMODE__MM1_MM2_OFFSETS \ + ((VL53L1_OffsetCorrectionMode) 1) +#define VL53L1_OFFSETCORRECTIONMODE__PER_ZONE_OFFSETS \ + ((VL53L1_OffsetCorrectionMode) 2) +#define VL53L1_OFFSETCORRECTIONMODE__PER_VCSEL_OFFSETS \ + ((VL53L1_OffsetCorrectionMode) 3) + + + + + +typedef uint8_t VL53L1_DeviceDmaxMode; + +#define VL53L1_DEVICEDMAXMODE__NONE \ + ((VL53L1_DeviceDmaxMode) 0) +#define VL53L1_DEVICEDMAXMODE__FMT_CAL_DATA \ + ((VL53L1_DeviceDmaxMode) 1) +#define VL53L1_DEVICEDMAXMODE__CUST_CAL_DATA \ + ((VL53L1_DeviceDmaxMode) 2) +#define VL53L1_DEVICEDMAXMODE__PER_ZONE_CAL_DATA \ + ((VL53L1_DeviceDmaxMode) 3) + + + + + +typedef uint8_t VL53L1_DeviceSequenceConfig; + +#define VL53L1_DEVICESEQUENCECONFIG_VHV \ + ((VL53L1_DeviceSequenceConfig) 0) +#define VL53L1_DEVICESEQUENCECONFIG_PHASECAL \ + ((VL53L1_DeviceSequenceConfig) 1) +#define VL53L1_DEVICESEQUENCECONFIG_REFERENCE_PHASE \ + ((VL53L1_DeviceSequenceConfig) 2) +#define VL53L1_DEVICESEQUENCECONFIG_DSS1 \ + ((VL53L1_DeviceSequenceConfig) 3) +#define VL53L1_DEVICESEQUENCECONFIG_DSS2 \ + ((VL53L1_DeviceSequenceConfig) 4) +#define VL53L1_DEVICESEQUENCECONFIG_MM1 \ + ((VL53L1_DeviceSequenceConfig) 5) +#define VL53L1_DEVICESEQUENCECONFIG_MM2 \ + ((VL53L1_DeviceSequenceConfig) 6) +#define VL53L1_DEVICESEQUENCECONFIG_RANGE \ + ((VL53L1_DeviceSequenceConfig) 7) + + + + + +typedef uint8_t VL53L1_DeviceInterruptPolarity; + +#define VL53L1_DEVICEINTERRUPTPOLARITY_ACTIVE_HIGH \ + ((VL53L1_DeviceInterruptPolarity) 0x00) +#define VL53L1_DEVICEINTERRUPTPOLARITY_ACTIVE_LOW \ + ((VL53L1_DeviceInterruptPolarity) 0x10) +#define VL53L1_DEVICEINTERRUPTPOLARITY_BIT_MASK \ + ((VL53L1_DeviceInterruptPolarity) 0x10) +#define VL53L1_DEVICEINTERRUPTPOLARITY_CLEAR_MASK \ + ((VL53L1_DeviceInterruptPolarity) 0xEF) + + + + + +typedef uint8_t VL53L1_DeviceGpioMode; + +#define VL53L1_DEVICEGPIOMODE_OUTPUT_CONSTANT_ZERO \ + ((VL53L1_DeviceGpioMode) 0x00) +#define VL53L1_DEVICEGPIOMODE_OUTPUT_RANGE_AND_ERROR_INTERRUPTS \ + ((VL53L1_DeviceGpioMode) 0x01) +#define VL53L1_DEVICEGPIOMODE_OUTPUT_TIMIER_INTERRUPTS \ + ((VL53L1_DeviceGpioMode) 0x02) +#define VL53L1_DEVICEGPIOMODE_OUTPUT_RANGE_MODE_INTERRUPT_STATUS \ + ((VL53L1_DeviceGpioMode) 0x03) +#define VL53L1_DEVICEGPIOMODE_OUTPUT_SLOW_OSCILLATOR_CLOCK \ + ((VL53L1_DeviceGpioMode) 0x04) +#define VL53L1_DEVICEGPIOMODE_BIT_MASK \ + ((VL53L1_DeviceGpioMode) 0x0F) +#define VL53L1_DEVICEGPIOMODE_CLEAR_MASK \ + ((VL53L1_DeviceGpioMode) 0xF0) + + + + + +typedef uint8_t VL53L1_DeviceError; + +#define VL53L1_DEVICEERROR_NOUPDATE \ + ((VL53L1_DeviceError) 0) + +#define VL53L1_DEVICEERROR_VCSELCONTINUITYTESTFAILURE \ + ((VL53L1_DeviceError) 1) +#define VL53L1_DEVICEERROR_VCSELWATCHDOGTESTFAILURE \ + ((VL53L1_DeviceError) 2) +#define VL53L1_DEVICEERROR_NOVHVVALUEFOUND \ + ((VL53L1_DeviceError) 3) +#define VL53L1_DEVICEERROR_MSRCNOTARGET \ + ((VL53L1_DeviceError) 4) +#define VL53L1_DEVICEERROR_RANGEPHASECHECK \ + ((VL53L1_DeviceError) 5) +#define VL53L1_DEVICEERROR_SIGMATHRESHOLDCHECK \ + ((VL53L1_DeviceError) 6) +#define VL53L1_DEVICEERROR_PHASECONSISTENCY \ + ((VL53L1_DeviceError) 7) +#define VL53L1_DEVICEERROR_MINCLIP \ + ((VL53L1_DeviceError) 8) +#define VL53L1_DEVICEERROR_RANGECOMPLETE \ + ((VL53L1_DeviceError) 9) +#define VL53L1_DEVICEERROR_ALGOUNDERFLOW \ + ((VL53L1_DeviceError) 10) +#define VL53L1_DEVICEERROR_ALGOOVERFLOW \ + ((VL53L1_DeviceError) 11) +#define VL53L1_DEVICEERROR_RANGEIGNORETHRESHOLD \ + ((VL53L1_DeviceError) 12) +#define VL53L1_DEVICEERROR_USERROICLIP \ + ((VL53L1_DeviceError) 13) +#define VL53L1_DEVICEERROR_REFSPADCHARNOTENOUGHDPADS \ + ((VL53L1_DeviceError) 14) +#define VL53L1_DEVICEERROR_REFSPADCHARMORETHANTARGET \ + ((VL53L1_DeviceError) 15) +#define VL53L1_DEVICEERROR_REFSPADCHARLESSTHANTARGET \ + ((VL53L1_DeviceError) 16) +#define VL53L1_DEVICEERROR_MULTCLIPFAIL \ + ((VL53L1_DeviceError) 17) +#define VL53L1_DEVICEERROR_GPHSTREAMCOUNT0READY \ + ((VL53L1_DeviceError) 18) +#define VL53L1_DEVICEERROR_RANGECOMPLETE_NO_WRAP_CHECK \ + ((VL53L1_DeviceError) 19) +#define VL53L1_DEVICEERROR_EVENTCONSISTENCY \ + ((VL53L1_DeviceError) 20) +#define VL53L1_DEVICEERROR_MINSIGNALEVENTCHECK \ + ((VL53L1_DeviceError) 21) +#define VL53L1_DEVICEERROR_RANGECOMPLETE_MERGED_PULSE \ + ((VL53L1_DeviceError) 22) + + +#define VL53L1_DEVICEERROR_PREV_RANGE_NO_TARGETS \ + ((VL53L1_DeviceError) 23) + + + + + +typedef uint8_t VL53L1_DeviceReportStatus; + +#define VL53L1_DEVICEREPORTSTATUS_NOUPDATE \ + ((VL53L1_DeviceReportStatus) 0) + +#define VL53L1_DEVICEREPORTSTATUS_ROI_SETUP \ + ((VL53L1_DeviceReportStatus) 1) +#define VL53L1_DEVICEREPORTSTATUS_VHV \ + ((VL53L1_DeviceReportStatus) 2) +#define VL53L1_DEVICEREPORTSTATUS_PHASECAL \ + ((VL53L1_DeviceReportStatus) 3) +#define VL53L1_DEVICEREPORTSTATUS_REFERENCE_PHASE \ + ((VL53L1_DeviceReportStatus) 4) +#define VL53L1_DEVICEREPORTSTATUS_DSS1 \ + ((VL53L1_DeviceReportStatus) 5) +#define VL53L1_DEVICEREPORTSTATUS_DSS2 \ + ((VL53L1_DeviceReportStatus) 6) +#define VL53L1_DEVICEREPORTSTATUS_MM1 \ + ((VL53L1_DeviceReportStatus) 7) +#define VL53L1_DEVICEREPORTSTATUS_MM2 \ + ((VL53L1_DeviceReportStatus) 8) +#define VL53L1_DEVICEREPORTSTATUS_RANGE \ + ((VL53L1_DeviceReportStatus) 9) +#define VL53L1_DEVICEREPORTSTATUS_HISTOGRAM \ + ((VL53L1_DeviceReportStatus) 10) + + + + + +typedef uint8_t VL53L1_DeviceDssMode; + +#define VL53L1_DEVICEDSSMODE__DISABLED \ + ((VL53L1_DeviceDssMode) 0) +#define VL53L1_DEVICEDSSMODE__TARGET_RATE \ + ((VL53L1_DeviceDssMode) 1) +#define VL53L1_DEVICEDSSMODE__REQUESTED_EFFFECTIVE_SPADS \ + ((VL53L1_DeviceDssMode) 2) +#define VL53L1_DEVICEDSSMODE__BLOCK_SELECT \ + ((VL53L1_DeviceDssMode) 3) + + + + + + +typedef uint8_t VL53L1_HistAlgoSelect; + +#define VL53L1_HIST_ALGO_SELECT__PW_HIST_GEN1 \ + ((VL53L1_HistAlgoSelect) 1) +#define VL53L1_HIST_ALGO_SELECT__PW_HIST_GEN2 \ + ((VL53L1_HistAlgoSelect) 2) +#define VL53L1_HIST_ALGO_SELECT__PW_HIST_GEN3 \ + ((VL53L1_HistAlgoSelect) 3) +#define VL53L1_HIST_ALGO_SELECT__PW_HIST_GEN4 \ + ((VL53L1_HistAlgoSelect) 4) + + + + + + +typedef uint8_t VL53L1_HistTargetOrder; + +#define VL53L1_HIST_TARGET_ORDER__INCREASING_DISTANCE \ + ((VL53L1_HistTargetOrder) 1) +#define VL53L1_HIST_TARGET_ORDER__STRONGEST_FIRST \ + ((VL53L1_HistTargetOrder) 2) + + + + + + +typedef uint8_t VL53L1_HistAmbEstMethod; + +#define VL53L1_HIST_AMB_EST_METHOD__AMBIENT_BINS \ + ((VL53L1_HistAmbEstMethod) 1) +#define VL53L1_HIST_AMB_EST_METHOD__THRESHOLDED_BINS \ + ((VL53L1_HistAmbEstMethod) 2) + + + + + + +typedef uint8_t VL53L1_HistXtalkCompEnable; + +#define VL53L1_HIST_XTALK_COMP__DIS \ + ((VL53L1_HistXtalkCompEnable) 0) +#define VL53L1_HIST_XTALK_COMP__EN \ + ((VL53L1_HistXtalkCompEnable) 1) + + + + +typedef uint8_t VL53L1_DeviceConfigLevel; + +#define VL53L1_DEVICECONFIGLEVEL_SYSTEM_CONTROL \ + ((VL53L1_DeviceConfigLevel) 0) + +#define VL53L1_DEVICECONFIGLEVEL_DYNAMIC_ONWARDS \ + ((VL53L1_DeviceConfigLevel) 1) + +#define VL53L1_DEVICECONFIGLEVEL_TIMING_ONWARDS \ + ((VL53L1_DeviceConfigLevel) 2) + +#define VL53L1_DEVICECONFIGLEVEL_GENERAL_ONWARDS \ + ((VL53L1_DeviceConfigLevel) 3) + +#define VL53L1_DEVICECONFIGLEVEL_STATIC_ONWARDS \ + ((VL53L1_DeviceConfigLevel) 4) + +#define VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS \ + ((VL53L1_DeviceConfigLevel) 5) + +#define VL53L1_DEVICECONFIGLEVEL_FULL \ + ((VL53L1_DeviceConfigLevel) 6) + + + + + + +typedef uint8_t VL53L1_DeviceResultsLevel; + +#define VL53L1_DEVICERESULTSLEVEL_SYSTEM_RESULTS \ + ((VL53L1_DeviceResultsLevel) 0) + +#define VL53L1_DEVICERESULTSLEVEL_UPTO_CORE \ + ((VL53L1_DeviceResultsLevel) 1) + +#define VL53L1_DEVICERESULTSLEVEL_FULL \ + ((VL53L1_DeviceResultsLevel) 2) + + + + + + + +typedef uint8_t VL53L1_DeviceTestMode; + +#define VL53L1_DEVICETESTMODE_NONE \ + ((VL53L1_DeviceTestMode) 0x00) + +#define VL53L1_DEVICETESTMODE_NVM_ZERO \ + ((VL53L1_DeviceTestMode) 0x01) + +#define VL53L1_DEVICETESTMODE_NVM_COPY \ + ((VL53L1_DeviceTestMode) 0x02) + +#define VL53L1_DEVICETESTMODE_PATCH \ + ((VL53L1_DeviceTestMode) 0x03) + +#define VL53L1_DEVICETESTMODE_DCR \ + ((VL53L1_DeviceTestMode) 0x04) + +#define VL53L1_DEVICETESTMODE_LCR_VCSEL_OFF \ + ((VL53L1_DeviceTestMode) 0x05) + +#define VL53L1_DEVICETESTMODE_LCR_VCSEL_ON \ + ((VL53L1_DeviceTestMode) 0x06) + +#define VL53L1_DEVICETESTMODE_SPOT_CENTRE_LOCATE \ + ((VL53L1_DeviceTestMode) 0x07) + +#define VL53L1_DEVICETESTMODE_REF_SPAD_CHAR_WITH_PRE_VHV \ + ((VL53L1_DeviceTestMode) 0x08) + +#define VL53L1_DEVICETESTMODE_REF_SPAD_CHAR_ONLY \ + ((VL53L1_DeviceTestMode) 0x09) + + + + + + + +typedef uint8_t VL53L1_DeviceSscArray; + +#define VL53L1_DEVICESSCARRAY_RTN ((VL53L1_DeviceSscArray) 0x00) + +#define VL53L1_DEVICETESTMODE_REF ((VL53L1_DeviceSscArray) 0x01) + + + + + + + +#define VL53L1_RETURN_ARRAY_ONLY 0x01 + +#define VL53L1_REFERENCE_ARRAY_ONLY 0x10 + +#define VL53L1_BOTH_RETURN_AND_REFERENCE_ARRAYS 0x11 + +#define VL53L1_NEITHER_RETURN_AND_REFERENCE_ARRAYS 0x00 + + + + + + +#define VL53L1_DEVICEINTERRUPTLEVEL_ACTIVE_HIGH 0x00 + +#define VL53L1_DEVICEINTERRUPTLEVEL_ACTIVE_LOW 0x10 + +#define VL53L1_DEVICEINTERRUPTLEVEL_ACTIVE_MASK 0x10 + + + + + + +#define VL53L1_POLLING_DELAY_US 1000 + +#define VL53L1_SOFTWARE_RESET_DURATION_US 100 + +#define VL53L1_FIRMWARE_BOOT_TIME_US 1200 + +#define VL53L1_ENABLE_POWERFORCE_SETTLING_TIME_US 250 + +#define VL53L1_SPAD_ARRAY_WIDTH 16 + +#define VL53L1_SPAD_ARRAY_HEIGHT 16 + +#define VL53L1_NVM_SIZE_IN_BYTES 512 + +#define VL53L1_NO_OF_SPAD_ENABLES 256 + +#define VL53L1_RTN_SPAD_BUFFER_SIZE 32 + +#define VL53L1_REF_SPAD_BUFFER_SIZE 6 + +#define VL53L1_AMBIENT_WINDOW_VCSEL_PERIODS 256 + +#define VL53L1_RANGING_WINDOW_VCSEL_PERIODS 2048 + +#define VL53L1_MACRO_PERIOD_VCSEL_PERIODS \ + (VL53L1_AMBIENT_WINDOW_VCSEL_PERIODS + \ + VL53L1_RANGING_WINDOW_VCSEL_PERIODS) + +#define VL53L1_MAX_ALLOWED_PHASE 0xFFFF + + +#define VL53L1_RTN_SPAD_UNITY_TRANSMISSION 0x0100 + +#define VL53L1_RTN_SPAD_APERTURE_TRANSMISSION 0x0038 + + +#define VL53L1_SPAD_TOTAL_COUNT_MAX ((0x01 << 29) - 1) + +#define VL53L1_SPAD_TOTAL_COUNT_RES_THRES (0x01 << 24) + +#define VL53L1_COUNT_RATE_INTERNAL_MAX ((0x01 << 24) - 1) + +#define VL53L1_SPEED_OF_LIGHT_IN_AIR 299704 + +#define VL53L1_SPEED_OF_LIGHT_IN_AIR_DIV_8 (299704 >> 3) + + + + + + + + +typedef uint8_t VL53L1_ZoneConfig_BinConfig_select; + +#define VL53L1_ZONECONFIG_BINCONFIG__LOWAMB \ + ((VL53L1_ZoneConfig_BinConfig_select) 1) +#define VL53L1_ZONECONFIG_BINCONFIG__MIDAMB \ + ((VL53L1_ZoneConfig_BinConfig_select) 2) +#define VL53L1_ZONECONFIG_BINCONFIG__HIGHAMB \ + ((VL53L1_ZoneConfig_BinConfig_select) 3) + + + + + +typedef uint8_t VL53L1_GPIO_Interrupt_Mode; + +#define VL53L1_GPIOINTMODE_LEVEL_LOW \ + ((VL53L1_GPIO_Interrupt_Mode) 0) + +#define VL53L1_GPIOINTMODE_LEVEL_HIGH \ + ((VL53L1_GPIO_Interrupt_Mode) 1) + +#define VL53L1_GPIOINTMODE_OUT_OF_WINDOW \ + ((VL53L1_GPIO_Interrupt_Mode) 2) + +#define VL53L1_GPIOINTMODE_IN_WINDOW \ + ((VL53L1_GPIO_Interrupt_Mode) 3) + + + + + + +typedef uint16_t VL53L1_TuningParms; + +#define VL53L1_TUNINGPARMS_LLD_PUBLIC_MIN_ADDRESS \ + ((VL53L1_TuningParms) VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS) +#define VL53L1_TUNINGPARMS_LLD_PUBLIC_MAX_ADDRESS \ + ((VL53L1_TuningParms) VL53L1_TUNINGPARM_MIN_SIGNAL_SECONDARY_TARGETS) + +#define VL53L1_TUNINGPARMS_LLD_PRIVATE_MIN_ADDRESS \ + ((VL53L1_TuningParms) VL53L1_TUNINGPARM_PRIVATE_PAGE_BASE_ADDRESS) +#define VL53L1_TUNINGPARMS_LLD_PRIVATE_MAX_ADDRESS \ + ((VL53L1_TuningParms) VL53L1_TUNINGPARMS_LLD_PRIVATE_MIN_ADDRESS) + +#define VL53L1_TUNINGPARM_VERSION \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 0)) +#define VL53L1_TUNINGPARM_KEY_TABLE_VERSION \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 1)) +#define VL53L1_TUNINGPARM_LLD_VERSION \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 2)) +#define VL53L1_TUNINGPARM_HIST_ALGO_SELECT \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 3)) +#define VL53L1_TUNINGPARM_HIST_TARGET_ORDER \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 4)) +#define VL53L1_TUNINGPARM_HIST_FILTER_WOI_0 \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 5)) +#define VL53L1_TUNINGPARM_HIST_FILTER_WOI_1 \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 6)) +#define VL53L1_TUNINGPARM_HIST_AMB_EST_METHOD \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 7)) +#define VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_0 \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 8)) +#define VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_1 \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 9)) +#define VL53L1_TUNINGPARM_HIST_MIN_AMB_THRESH_EVENTS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 10)) +#define VL53L1_TUNINGPARM_HIST_AMB_EVENTS_SCALER \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 11)) +#define VL53L1_TUNINGPARM_HIST_NOISE_THRESHOLD \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 12)) +#define VL53L1_TUNINGPARM_HIST_SIGNAL_TOTAL_EVENTS_LIMIT \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 13)) +#define VL53L1_TUNINGPARM_HIST_SIGMA_EST_REF_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 14)) +#define VL53L1_TUNINGPARM_HIST_SIGMA_THRESH_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 15)) +#define VL53L1_TUNINGPARM_HIST_GAIN_FACTOR \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 16)) +#define VL53L1_TUNINGPARM_CONSISTENCY_HIST_PHASE_TOLERANCE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 17)) +#define VL53L1_TUNINGPARM_CONSISTENCY_HIST_MIN_MAX_TOLERANCE_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 18)) +#define VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 19)) +#define VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA_MIN_SPAD_LIMIT \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 20)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_LONG_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 21)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_MED_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 22)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_SHORT_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 23)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_LONG_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 24)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_MED_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 25)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_SHORT_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 26)) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MIN_VALID_RANGE_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 27)) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RANGE_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 28)) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MAX_SIGMA_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 29)) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MIN_MAX_TOLERANCE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 30)) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RATE_KCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 31)) +#define VL53L1_TUNINGPARM_XTALK_DETECT_EVENT_SIGMA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 32)) +#define VL53L1_TUNINGPARM_HIST_XTALK_MARGIN_KCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 33)) +#define VL53L1_TUNINGPARM_CONSISTENCY_LITE_PHASE_TOLERANCE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 34)) +#define VL53L1_TUNINGPARM_PHASECAL_TARGET \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 35)) +#define VL53L1_TUNINGPARM_LITE_CAL_REPEAT_RATE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 36)) +#define VL53L1_TUNINGPARM_LITE_RANGING_GAIN_FACTOR \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 37)) +#define VL53L1_TUNINGPARM_LITE_MIN_CLIP_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 38)) +#define VL53L1_TUNINGPARM_LITE_LONG_SIGMA_THRESH_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 39)) +#define VL53L1_TUNINGPARM_LITE_MED_SIGMA_THRESH_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 40)) +#define VL53L1_TUNINGPARM_LITE_SHORT_SIGMA_THRESH_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 41)) +#define VL53L1_TUNINGPARM_LITE_LONG_MIN_COUNT_RATE_RTN_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 42)) +#define VL53L1_TUNINGPARM_LITE_MED_MIN_COUNT_RATE_RTN_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 43)) +#define VL53L1_TUNINGPARM_LITE_SHORT_MIN_COUNT_RATE_RTN_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 44)) +#define VL53L1_TUNINGPARM_LITE_SIGMA_EST_PULSE_WIDTH \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 45)) +#define VL53L1_TUNINGPARM_LITE_SIGMA_EST_AMB_WIDTH_NS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 46)) +#define VL53L1_TUNINGPARM_LITE_SIGMA_REF_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 47)) +#define VL53L1_TUNINGPARM_LITE_RIT_MULT \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 48)) +#define VL53L1_TUNINGPARM_LITE_SEED_CONFIG \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 49)) +#define VL53L1_TUNINGPARM_LITE_QUANTIFIER \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 50)) +#define VL53L1_TUNINGPARM_LITE_FIRST_ORDER_SELECT \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 51)) +#define VL53L1_TUNINGPARM_LITE_XTALK_MARGIN_KCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 52)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_LONG_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 53)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_MED_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 54)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_SHORT_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 55)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_LONG_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 56)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_MED_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 57)) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_SHORT_RANGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 58)) +#define VL53L1_TUNINGPARM_TIMED_SEED_CONFIG \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 59)) +#define VL53L1_TUNINGPARM_DMAX_CFG_SIGNAL_THRESH_SIGMA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 60)) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_0 \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 61)) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_1 \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 62)) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_2 \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 63)) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_3 \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 64)) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_4 \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 65)) +#define VL53L1_TUNINGPARM_VHV_LOOPBOUND \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 66)) +#define VL53L1_TUNINGPARM_REFSPADCHAR_DEVICE_TEST_MODE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 67)) +#define VL53L1_TUNINGPARM_REFSPADCHAR_VCSEL_PERIOD \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 68)) +#define VL53L1_TUNINGPARM_REFSPADCHAR_PHASECAL_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 69)) +#define VL53L1_TUNINGPARM_REFSPADCHAR_TARGET_COUNT_RATE_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 70)) +#define VL53L1_TUNINGPARM_REFSPADCHAR_MIN_COUNTRATE_LIMIT_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 71)) +#define VL53L1_TUNINGPARM_REFSPADCHAR_MAX_COUNTRATE_LIMIT_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 72)) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_NUM_OF_SAMPLES \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 73)) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_MIN_FILTER_THRESH_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 74)) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_FILTER_THRESH_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 75)) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_RATE_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 76)) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_PHASECAL_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 77)) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_VALID_RATE_KCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 78)) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_SIGMA_THRESHOLD_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 79)) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 80)) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_BIN_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 81)) +#define VL53L1_TUNINGPARM_OFFSET_CAL_DSS_RATE_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 82)) +#define VL53L1_TUNINGPARM_OFFSET_CAL_PHASECAL_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 83)) +#define VL53L1_TUNINGPARM_OFFSET_CAL_MM_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 84)) +#define VL53L1_TUNINGPARM_OFFSET_CAL_RANGE_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 85)) +#define VL53L1_TUNINGPARM_OFFSET_CAL_PRE_SAMPLES \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 86)) +#define VL53L1_TUNINGPARM_OFFSET_CAL_MM1_SAMPLES \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 87)) +#define VL53L1_TUNINGPARM_OFFSET_CAL_MM2_SAMPLES \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 88)) +#define VL53L1_TUNINGPARM_ZONE_CAL_DSS_RATE_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 89)) +#define VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 90)) +#define VL53L1_TUNINGPARM_ZONE_CAL_DSS_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 91)) +#define VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_NUM_SAMPLES \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 92)) +#define VL53L1_TUNINGPARM_ZONE_CAL_RANGE_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 93)) +#define VL53L1_TUNINGPARM_ZONE_CAL_ZONE_NUM_SAMPLES \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 94)) +#define VL53L1_TUNINGPARM_SPADMAP_VCSEL_PERIOD \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 95)) +#define VL53L1_TUNINGPARM_SPADMAP_VCSEL_START \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 96)) +#define VL53L1_TUNINGPARM_SPADMAP_RATE_LIMIT_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 97)) +#define VL53L1_TUNINGPARM_LITE_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 98)) +#define VL53L1_TUNINGPARM_RANGING_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 99)) +#define VL53L1_TUNINGPARM_MZ_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 100)) +#define VL53L1_TUNINGPARM_TIMED_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 101)) +#define VL53L1_TUNINGPARM_LITE_PHASECAL_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 102)) +#define VL53L1_TUNINGPARM_RANGING_LONG_PHASECAL_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 103)) +#define VL53L1_TUNINGPARM_RANGING_MED_PHASECAL_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 104)) +#define VL53L1_TUNINGPARM_RANGING_SHORT_PHASECAL_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 105)) +#define VL53L1_TUNINGPARM_MZ_LONG_PHASECAL_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 106)) +#define VL53L1_TUNINGPARM_MZ_MED_PHASECAL_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 107)) +#define VL53L1_TUNINGPARM_MZ_SHORT_PHASECAL_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 108)) +#define VL53L1_TUNINGPARM_TIMED_PHASECAL_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 109)) +#define VL53L1_TUNINGPARM_LITE_MM_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 110)) +#define VL53L1_TUNINGPARM_RANGING_MM_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 111)) +#define VL53L1_TUNINGPARM_MZ_MM_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 112)) +#define VL53L1_TUNINGPARM_TIMED_MM_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 113)) +#define VL53L1_TUNINGPARM_LITE_RANGE_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 114)) +#define VL53L1_TUNINGPARM_RANGING_RANGE_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 115)) +#define VL53L1_TUNINGPARM_MZ_RANGE_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 116)) +#define VL53L1_TUNINGPARM_TIMED_RANGE_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 117)) +#define VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_MARGIN \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 118)) +#define VL53L1_TUNINGPARM_DYNXTALK_NOISE_MARGIN \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 119)) +#define VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 120)) +#define VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT_HI \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 121)) +#define VL53L1_TUNINGPARM_DYNXTALK_SAMPLE_LIMIT \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 122)) +#define VL53L1_TUNINGPARM_DYNXTALK_SINGLE_XTALK_DELTA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 123)) +#define VL53L1_TUNINGPARM_DYNXTALK_AVERAGED_XTALK_DELTA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 124)) +#define VL53L1_TUNINGPARM_DYNXTALK_CLIP_LIMIT \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 125)) +#define VL53L1_TUNINGPARM_DYNXTALK_SCALER_CALC_METHOD \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 126)) +#define VL53L1_TUNINGPARM_DYNXTALK_XGRADIENT_SCALER \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 127)) +#define VL53L1_TUNINGPARM_DYNXTALK_YGRADIENT_SCALER \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 128)) +#define VL53L1_TUNINGPARM_DYNXTALK_USER_SCALER_SET \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 129)) +#define VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_COR_SINGLE_APPLY \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 130)) +#define VL53L1_TUNINGPARM_DYNXTALK_XTALK_AMB_THRESHOLD \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 131)) +#define VL53L1_TUNINGPARM_DYNXTALK_NODETECT_AMB_THRESHOLD_KCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 132)) +#define VL53L1_TUNINGPARM_DYNXTALK_NODETECT_SAMPLE_LIMIT \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 133)) +#define VL53L1_TUNINGPARM_DYNXTALK_NODETECT_XTALK_OFFSET_KCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 134)) +#define VL53L1_TUNINGPARM_DYNXTALK_NODETECT_MIN_RANGE_MM \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 135)) +#define VL53L1_TUNINGPARM_LOWPOWERAUTO_VHV_LOOP_BOUND \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 136)) +#define VL53L1_TUNINGPARM_LOWPOWERAUTO_MM_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 137)) +#define VL53L1_TUNINGPARM_LOWPOWERAUTO_RANGE_CONFIG_TIMEOUT_US \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 138)) +#define VL53L1_TUNINGPARM_VERY_SHORT_DSS_RATE_MCPS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 139)) +#define VL53L1_TUNINGPARM_PHASECAL_PATCH_POWER \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 140)) +#define VL53L1_TUNINGPARM_HIST_MERGE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 141)) +#define VL53L1_TUNINGPARM_RESET_MERGE_THRESHOLD \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 142)) +#define VL53L1_TUNINGPARM_HIST_MERGE_MAX_SIZE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 143)) +#define VL53L1_TUNINGPARM_DYNXTALK_MAX_SMUDGE_FACTOR \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 144)) +#define VL53L1_TUNINGPARM_UWR_ENABLE \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 145)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_1_MIN \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 146)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_1_MAX \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 147)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_2_MIN \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 148)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_2_MAX \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 149)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_3_MIN \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 150)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_3_MAX \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 151)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_4_MIN \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 152)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_4_MAX \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 153)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_5_MIN \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 154)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_5_MAX \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 155)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_6_MIN \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 156)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_6_MAX \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 157)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_1_RANGEA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 158)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_1_RANGEB \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 159)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_2_RANGEA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 160)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_2_RANGEB \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 161)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_3_RANGEA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 162)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_3_RANGEB \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 163)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_4_RANGEA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 164)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_4_RANGEB \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 165)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_5_RANGEA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 166)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_5_RANGEB \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 167)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_6_RANGEA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 168)) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_6_RANGEB \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 169)) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_1_MIN \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 170)) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_1_MAX \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 171)) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_2_MIN \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 172)) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_2_MAX \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 173)) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_3_MIN \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 174)) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_3_MAX \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 175)) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_4_MIN \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 176)) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_4_MAX \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 177)) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_5_MIN \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 178)) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_5_MAX \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 179)) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_1_RANGEA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 180)) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_1_RANGEB \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 181)) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_2_RANGEA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 182)) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_2_RANGEB \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 183)) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_3_RANGEA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 184)) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_3_RANGEB \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 185)) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_4_RANGEA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 186)) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_4_RANGEB \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 187)) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_5_RANGEA \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 188)) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_5_RANGEB \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 189)) +#define VL53L1_TUNINGPARM_MIN_SIGNAL_SECONDARY_TARGETS \ +((VL53L1_TuningParms) (VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS + 190)) + + + + + +#endif + + + + + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_nvm.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_nvm.h new file mode 100644 index 000000000000..58e136e77c3f --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_nvm.h @@ -0,0 +1,169 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + + + + +#ifndef _VL53L1_NVM_H_ +#define _VL53L1_NVM_H_ + +#include "vl53l1_ll_def.h" +#include "vl53l1_platform.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define VL53L1_NVM_POWER_UP_DELAY_US 50 +#define VL53L1_NVM_READ_TRIGGER_DELAY_US 5 + + + +VL53L1_Error VL53L1_nvm_enable( + VL53L1_DEV Dev, + uint16_t nvm_ctrl_pulse_width, + int32_t nvm_power_up_delay_us); + + + + +VL53L1_Error VL53L1_nvm_read( + VL53L1_DEV Dev, + uint8_t start_address, + uint8_t count, + uint8_t *pdata); + + + + +VL53L1_Error VL53L1_nvm_disable( + VL53L1_DEV Dev); + + + + +VL53L1_Error VL53L1_nvm_format_decode( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_decoded_nvm_data_t *pdata); + + + + +VL53L1_Error VL53L1_nvm_decode_optical_centre( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_optical_centre_t *pdata); + + + + +VL53L1_Error VL53L1_nvm_decode_cal_peak_rate_map( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_cal_peak_rate_map_t *pdata); + + + + +VL53L1_Error VL53L1_nvm_decode_additional_offset_cal_data( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_additional_offset_cal_data_t *pdata); + + + + +VL53L1_Error VL53L1_nvm_decode_fmt_range_results_data( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_decoded_nvm_fmt_range_data_t *pdata); + + + + +VL53L1_Error VL53L1_nvm_decode_fmt_info( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_decoded_nvm_fmt_info_t *pdata); + + + + +VL53L1_Error VL53L1_nvm_decode_ews_info( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_decoded_nvm_ews_info_t *pdata); + + + + +void VL53L1_nvm_format_encode( + VL53L1_decoded_nvm_data_t *pnvm_info, + uint8_t *pnvm_data); + + + + +VL53L1_Error VL53L1_read_nvm_raw_data( + VL53L1_DEV Dev, + uint8_t start_address, + uint8_t count, + uint8_t *pnvm_raw_data); + + + + +VL53L1_Error VL53L1_read_nvm( + VL53L1_DEV Dev, + uint8_t nvm_format, + VL53L1_decoded_nvm_data_t *pnvm_info); + + + + +VL53L1_Error VL53L1_read_nvm_optical_centre( + VL53L1_DEV Dev, + VL53L1_optical_centre_t *pcentre); + + + + +VL53L1_Error VL53L1_read_nvm_cal_peak_rate_map( + VL53L1_DEV Dev, + VL53L1_cal_peak_rate_map_t *pcal_data); + + + + +VL53L1_Error VL53L1_read_nvm_additional_offset_cal_data( + VL53L1_DEV Dev, + VL53L1_additional_offset_cal_data_t *pcal_data); + + + + +VL53L1_Error VL53L1_read_nvm_fmt_range_results_data( + VL53L1_DEV Dev, + uint16_t range_results_select, + VL53L1_decoded_nvm_fmt_range_data_t *prange_data); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_nvm_debug.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_nvm_debug.h new file mode 100644 index 000000000000..34259440eb70 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_nvm_debug.h @@ -0,0 +1,75 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + + +#ifndef _VL53L1_NVM_DEBUG_H_ +#define _VL53L1_NVM_DEBUG_H_ + +#include "vl53l1_ll_def.h" +#include "vl53l1_nvm_structs.h" + + + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef VL53L1_LOG_ENABLE + + + +void VL53L1_print_nvm_raw_data( + uint8_t *pnvm_raw_data, + uint32_t trace_flags); + + + + +void VL53L1_print_decoded_nvm_data( + VL53L1_decoded_nvm_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_decoded_nvm_fmt_range_data( + VL53L1_decoded_nvm_fmt_range_data_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + + +void VL53L1_print_decoded_nvm_fmt_info( + VL53L1_decoded_nvm_fmt_info_t *pdata, + char *pprefix, + uint32_t trace_flags); + + + +void VL53L1_print_decoded_nvm_ews_info( + VL53L1_decoded_nvm_ews_info_t *pdata, + char *pprefix, + uint32_t trace_flags); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_nvm_map.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_nvm_map.h new file mode 100644 index 000000000000..795bfe007d82 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_nvm_map.h @@ -0,0 +1,447 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + + + + +#ifndef _VL53L1_NVM_MAP_H_ +#define _VL53L1_NVM_MAP_H_ + + +#ifdef __cplusplus +extern "C" +{ +#endif + + + + +#define VL53L1_NVM__IDENTIFICATION__MODEL_ID 0x0008 + +#define VL53L1_NVM__IDENTIFICATION__MODULE_TYPE 0x000C + +#define VL53L1_NVM__IDENTIFICATION__REVISION_ID 0x000D + +#define VL53L1_NVM__IDENTIFICATION__MODULE_ID 0x000E + +#define VL53L1_NVM__I2C_VALID 0x0010 + +#define VL53L1_NVM__I2C_SLAVE__DEVICE_ADDRESS 0x0011 + +#define VL53L1_NVM__EWS__OSC_MEASURED__FAST_OSC_FREQUENCY 0x0014 + +#define VL53L1_NVM__EWS__FAST_OSC_TRIM_MAX 0x0016 + +#define VL53L1_NVM__EWS__FAST_OSC_FREQ_SET 0x0017 + +#define VL53L1_NVM__EWS__SLOW_OSC_CALIBRATION 0x0018 + +#define VL53L1_NVM__FMT__OSC_MEASURED__FAST_OSC_FREQUENCY 0x001C + +#define VL53L1_NVM__FMT__FAST_OSC_TRIM_MAX 0x001E + +#define VL53L1_NVM__FMT__FAST_OSC_FREQ_SET 0x001F + +#define VL53L1_NVM__FMT__SLOW_OSC_CALIBRATION 0x0020 + +#define VL53L1_NVM__VHV_CONFIG_UNLOCK 0x0028 + +#define VL53L1_NVM__REF_SELVDDPIX 0x0029 + +#define VL53L1_NVM__REF_SELVQUENCH 0x002A + +#define VL53L1_NVM__REGAVDD1V2_SEL_REGDVDD1V2_SEL 0x002B + +#define VL53L1_NVM__VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND 0x002C + +#define VL53L1_NVM__VHV_CONFIG__COUNT_THRESH 0x002D + +#define VL53L1_NVM__VHV_CONFIG__OFFSET 0x002E + +#define VL53L1_NVM__VHV_CONFIG__INIT 0x002F + +#define VL53L1_NVM__LASER_SAFETY__VCSEL_TRIM_LL 0x0030 + +#define VL53L1_NVM__LASER_SAFETY__VCSEL_SELION_LL 0x0031 + +#define VL53L1_NVM__LASER_SAFETY__VCSEL_SELION_MAX_LL 0x0032 + +#define VL53L1_NVM__LASER_SAFETY__MULT_LL 0x0034 + +#define VL53L1_NVM__LASER_SAFETY__CLIP_LL 0x0035 + +#define VL53L1_NVM__LASER_SAFETY__VCSEL_TRIM_LD 0x0038 + +#define VL53L1_NVM__LASER_SAFETY__VCSEL_SELION_LD 0x0039 + +#define VL53L1_NVM__LASER_SAFETY__VCSEL_SELION_MAX_LD 0x003A + +#define VL53L1_NVM__LASER_SAFETY__MULT_LD 0x003C + +#define VL53L1_NVM__LASER_SAFETY__CLIP_LD 0x003D + +#define VL53L1_NVM__LASER_SAFETY_LOCK_BYTE 0x0040 + +#define VL53L1_NVM__LASER_SAFETY_UNLOCK_BYTE 0x0044 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_0_ 0x0048 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_1_ 0x0049 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_2_ 0x004A + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_3_ 0x004B + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_4_ 0x004C + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_5_ 0x004D + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_6_ 0x004E + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_7_ 0x004F + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_8_ 0x0050 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_9_ 0x0051 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_10_ 0x0052 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_11_ 0x0053 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_12_ 0x0054 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_13_ 0x0055 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_14_ 0x0056 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_15_ 0x0057 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_16_ 0x0058 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_17_ 0x0059 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_18_ 0x005A + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_19_ 0x005B + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_20_ 0x005C + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_21_ 0x005D + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_22_ 0x005E + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_23_ 0x005F + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_24_ 0x0060 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_25_ 0x0061 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_26_ 0x0062 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_27_ 0x0063 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_28_ 0x0064 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_29_ 0x0065 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_30_ 0x0066 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_RTN_31_ 0x0067 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC1_0_ 0x0068 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC1_1_ 0x0069 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC1_2_ 0x006A + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC1_3_ 0x006B + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC1_4_ 0x006C + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC1_5_ 0x006D + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC2_0_ 0x0070 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC2_1_ 0x0071 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC2_2_ 0x0072 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC2_3_ 0x0073 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC2_4_ 0x0074 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC2_5_ 0x0075 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC3_0_ 0x0078 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC3_1_ 0x0079 + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC3_2_ 0x007A + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC3_3_ 0x007B + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC3_4_ 0x007C + +#define VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC3_5_ 0x007D + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_0_ 0x0080 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_1_ 0x0081 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_2_ 0x0082 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_3_ 0x0083 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_4_ 0x0084 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_5_ 0x0085 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_6_ 0x0086 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_7_ 0x0087 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_8_ 0x0088 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_9_ 0x0089 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_10_ 0x008A + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_11_ 0x008B + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_12_ 0x008C + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_13_ 0x008D + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_14_ 0x008E + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_15_ 0x008F + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_16_ 0x0090 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_17_ 0x0091 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_18_ 0x0092 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_19_ 0x0093 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_20_ 0x0094 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_21_ 0x0095 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_22_ 0x0096 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_23_ 0x0097 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_24_ 0x0098 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_25_ 0x0099 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_26_ 0x009A + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_27_ 0x009B + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_28_ 0x009C + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_29_ 0x009D + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_30_ 0x009E + +#define VL53L1_NVM__FMT__SPAD_ENABLES_RTN_31_ 0x009F + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC1_0_ 0x00A0 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC1_1_ 0x00A1 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC1_2_ 0x00A2 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC1_3_ 0x00A3 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC1_4_ 0x00A4 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC1_5_ 0x00A5 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC2_0_ 0x00A8 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC2_1_ 0x00A9 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC2_2_ 0x00AA + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC2_3_ 0x00AB + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC2_4_ 0x00AC + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC2_5_ 0x00AD + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC3_0_ 0x00B0 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC3_1_ 0x00B1 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC3_2_ 0x00B2 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC3_3_ 0x00B3 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC3_4_ 0x00B4 + +#define VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC3_5_ 0x00B5 + +#define VL53L1_NVM__FMT__ROI_CONFIG__MODE_ROI_CENTRE_SPAD 0x00B8 + +#define VL53L1_NVM__FMT__ROI_CONFIG__MODE_ROI_XY_SIZE 0x00B9 + +#define VL53L1_NVM__FMT__REF_SPAD_APPLY__NUM_REQUESTED_REF_SPAD 0x00BC + +#define VL53L1_NVM__FMT__REF_SPAD_MAN__REF_LOCATION 0x00BD + +#define VL53L1_NVM__FMT__MM_CONFIG__INNER_OFFSET_MM 0x00C0 + +#define VL53L1_NVM__FMT__MM_CONFIG__OUTER_OFFSET_MM 0x00C2 + +#define VL53L1_NVM__FMT__ALGO__PART_TO_PART_RANGE_OFFSET_MM 0x00C4 + +#define VL53L1_NVM__FMT__ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS 0x00C8 + +#define VL53L1_NVM__FMT__ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS \ + 0x00CA + +#define VL53L1_NVM__FMT__ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS \ + 0x00CC + +#define VL53L1_NVM__FMT__SPARE_HOST_CONFIG__NVM_CONFIG_SPARE_0 0x00CE + +#define VL53L1_NVM__FMT__SPARE_HOST_CONFIG__NVM_CONFIG_SPARE_1 0x00CF + +#define VL53L1_NVM__CUSTOMER_NVM_SPACE_PROGRAMMED 0x00E0 + +#define VL53L1_NVM__CUST__I2C_SLAVE__DEVICE_ADDRESS 0x00E4 + +#define VL53L1_NVM__CUST__REF_SPAD_APPLY__NUM_REQUESTED_REF_SPAD 0x00E8 + +#define VL53L1_NVM__CUST__REF_SPAD_MAN__REF_LOCATION 0x00E9 + +#define VL53L1_NVM__CUST__MM_CONFIG__INNER_OFFSET_MM 0x00EC + +#define VL53L1_NVM__CUST__MM_CONFIG__OUTER_OFFSET_MM 0x00EE + +#define VL53L1_NVM__CUST__ALGO__PART_TO_PART_RANGE_OFFSET_MM 0x00F0 + +#define VL53L1_NVM__CUST__ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS 0x00F4 + +#define VL53L1_NVM__CUST__ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS \ + 0x00F6 + +#define VL53L1_NVM__CUST__ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS \ + 0x00F8 + +#define VL53L1_NVM__CUST__SPARE_HOST_CONFIG__NVM_CONFIG_SPARE_0 0x00FA + +#define VL53L1_NVM__CUST__SPARE_HOST_CONFIG__NVM_CONFIG_SPARE_1 0x00FB + +#define VL53L1_NVM__FMT__FGC__BYTE_0 0x01DC + +#define VL53L1_NVM__FMT__FGC__BYTE_1 0x01DD + +#define VL53L1_NVM__FMT__FGC__BYTE_2 0x01DE + +#define VL53L1_NVM__FMT__FGC__BYTE_3 0x01DF + +#define VL53L1_NVM__FMT__FGC__BYTE_4 0x01E0 + +#define VL53L1_NVM__FMT__FGC__BYTE_5 0x01E1 + +#define VL53L1_NVM__FMT__FGC__BYTE_6 0x01E2 + +#define VL53L1_NVM__FMT__FGC__BYTE_7 0x01E3 + +#define VL53L1_NVM__FMT__FGC__BYTE_8 0x01E4 + +#define VL53L1_NVM__FMT__FGC__BYTE_9 0x01E5 + +#define VL53L1_NVM__FMT__FGC__BYTE_10 0x01E6 + +#define VL53L1_NVM__FMT__FGC__BYTE_11 0x01E7 + +#define VL53L1_NVM__FMT__FGC__BYTE_12 0x01E8 + +#define VL53L1_NVM__FMT__FGC__BYTE_13 0x01E9 + +#define VL53L1_NVM__FMT__FGC__BYTE_14 0x01EA + +#define VL53L1_NVM__FMT__FGC__BYTE_15 0x01EB + +#define VL53L1_NVM__FMT__TEST_PROGRAM_MAJOR_MINOR 0x01EC + +#define VL53L1_NVM__FMT__MAP_MAJOR_MINOR 0x01ED + +#define VL53L1_NVM__FMT__YEAR_MONTH 0x01EE + +#define VL53L1_NVM__FMT__DAY_MODULE_DATE_PHASE 0x01EF + +#define VL53L1_NVM__FMT__TIME 0x01F0 + +#define VL53L1_NVM__FMT__TESTER_ID 0x01F2 + +#define VL53L1_NVM__FMT__SITE_ID 0x01F3 + +#define VL53L1_NVM__EWS__TEST_PROGRAM_MAJOR_MINOR 0x01F4 + +#define VL53L1_NVM__EWS__PROBE_CARD_MAJOR_MINOR 0x01F5 + +#define VL53L1_NVM__EWS__TESTER_ID 0x01F6 + +#define VL53L1_NVM__EWS__LOT__BYTE_0 0x01F8 + +#define VL53L1_NVM__EWS__LOT__BYTE_1 0x01F9 + +#define VL53L1_NVM__EWS__LOT__BYTE_2 0x01FA + +#define VL53L1_NVM__EWS__LOT__BYTE_3 0x01FB + +#define VL53L1_NVM__EWS__LOT__BYTE_4 0x01FC + +#define VL53L1_NVM__EWS__LOT__BYTE_5 0x01FD + +#define VL53L1_NVM__EWS__WAFER 0x01FD + +#define VL53L1_NVM__EWS__XCOORD 0x01FE + +#define VL53L1_NVM__EWS__YCOORD 0x01FF + + +#define VL53L1_NVM__FMT__OPTICAL_CENTRE_DATA_INDEX 0x00B8 +#define VL53L1_NVM__FMT__OPTICAL_CENTRE_DATA_SIZE 4 + +#define VL53L1_NVM__FMT__CAL_PEAK_RATE_MAP_DATA_INDEX 0x015C +#define VL53L1_NVM__FMT__CAL_PEAK_RATE_MAP_DATA_SIZE 56 + +#define VL53L1_NVM__FMT__ADDITIONAL_OFFSET_CAL_DATA_INDEX 0x0194 +#define VL53L1_NVM__FMT__ADDITIONAL_OFFSET_CAL_DATA_SIZE 8 + +#define VL53L1_NVM__FMT__RANGE_RESULTS__140MM_MM_PRE_RANGE 0x019C +#define VL53L1_NVM__FMT__RANGE_RESULTS__140MM_DARK 0x01AC +#define VL53L1_NVM__FMT__RANGE_RESULTS__400MM_DARK 0x01BC +#define VL53L1_NVM__FMT__RANGE_RESULTS__400MM_AMBIENT 0x01CC +#define VL53L1_NVM__FMT__RANGE_RESULTS__SIZE_BYTES 16 + + + + + + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_nvm_structs.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_nvm_structs.h new file mode 100644 index 000000000000..caeac2add3f7 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_nvm_structs.h @@ -0,0 +1,297 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + + + + +#ifndef _VL53L1_NVM_STRUCTS_H_ +#define _VL53L1_NVM_STRUCTS_H_ + + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "vl53l1_platform.h" +#include "vl53l1_ll_def.h" + + + + +typedef struct { + + uint16_t result__actual_effective_rtn_spads; + + uint8_t ref_spad_array__num_requested_ref_spads; + + uint8_t ref_spad_array__ref_location; + + uint16_t result__peak_signal_count_rate_rtn_mcps; + + uint16_t result__ambient_count_rate_rtn_mcps; + + uint16_t result__peak_signal_count_rate_ref_mcps; + + uint16_t result__ambient_count_rate_ref_mcps; + + uint16_t measured_distance_mm; + + uint16_t measured_distance_stdev_mm; + + +} VL53L1_decoded_nvm_fmt_range_data_t; + + + + +typedef struct { + + char nvm__fmt__fgc[19]; + + uint8_t nvm__fmt__test_program_major; + + uint8_t nvm__fmt__test_program_minor; + + uint8_t nvm__fmt__map_major; + + uint8_t nvm__fmt__map_minor; + + uint8_t nvm__fmt__year; + + uint8_t nvm__fmt__month; + + uint8_t nvm__fmt__day; + + uint8_t nvm__fmt__module_date_phase; + + uint16_t nvm__fmt__time; + + uint8_t nvm__fmt__tester_id; + + uint8_t nvm__fmt__site_id; + + uint8_t nvm__ews__test_program_major; + + uint8_t nvm__ews__test_program_minor; + + uint8_t nvm__ews__probe_card_major; + + uint8_t nvm__ews__probe_card_minor; + + uint8_t nvm__ews__tester_id; + + + char nvm__ews__lot[8]; + + uint8_t nvm__ews__wafer; + + uint8_t nvm__ews__xcoord; + + uint8_t nvm__ews__ycoord; + +} VL53L1_decoded_nvm_fmt_info_t; + + + + +typedef struct { + + uint8_t nvm__ews__test_program_major; + + uint8_t nvm__ews__test_program_minor; + + uint8_t nvm__ews__probe_card_major; + + uint8_t nvm__ews__probe_card_minor; + + uint8_t nvm__ews__tester_id; + + + char nvm__ews__lot[8]; + + uint8_t nvm__ews__wafer; + + uint8_t nvm__ews__xcoord; + + uint8_t nvm__ews__ycoord; + + +} VL53L1_decoded_nvm_ews_info_t; + + + + +typedef struct { + uint8_t nvm__identification_model_id; + + uint8_t nvm__identification_module_type; + + uint8_t nvm__identification_revision_id; + + uint16_t nvm__identification_module_id; + + uint8_t nvm__i2c_valid; + + uint8_t nvm__i2c_device_address_ews; + + uint16_t nvm__ews__fast_osc_frequency; + + uint8_t nvm__ews__fast_osc_trim_max; + + uint8_t nvm__ews__fast_osc_freq_set; + + uint16_t nvm__ews__slow_osc_calibration; + + uint16_t nvm__fmt__fast_osc_frequency; + + uint8_t nvm__fmt__fast_osc_trim_max; + + uint8_t nvm__fmt__fast_osc_freq_set; + + uint16_t nvm__fmt__slow_osc_calibration; + + uint8_t nvm__vhv_config_unlock; + + uint8_t nvm__ref_selvddpix; + + uint8_t nvm__ref_selvquench; + + uint8_t nvm__regavdd1v2_sel; + + uint8_t nvm__regdvdd1v2_sel; + + uint8_t nvm__vhv_timeout__macrop; + + uint8_t nvm__vhv_loop_bound; + + uint8_t nvm__vhv_count_threshold; + + uint8_t nvm__vhv_offset; + + uint8_t nvm__vhv_init_enable; + + uint8_t nvm__vhv_init_value; + + uint8_t nvm__laser_safety_vcsel_trim_ll; + + uint8_t nvm__laser_safety_vcsel_selion_ll; + + uint8_t nvm__laser_safety_vcsel_selion_max_ll; + + uint8_t nvm__laser_safety_mult_ll; + + uint8_t nvm__laser_safety_clip_ll; + + uint8_t nvm__laser_safety_vcsel_trim_ld; + + uint8_t nvm__laser_safety_vcsel_selion_ld; + + uint8_t nvm__laser_safety_vcsel_selion_max_ld; + + uint8_t nvm__laser_safety_mult_ld; + + uint8_t nvm__laser_safety_clip_ld; + + uint8_t nvm__laser_safety_lock_byte; + + uint8_t nvm__laser_safety_unlock_byte; + + uint8_t nvm__ews__spad_enables_rtn[VL53L1_RTN_SPAD_BUFFER_SIZE]; + + uint8_t nvm__ews__spad_enables_ref__loc1[VL53L1_REF_SPAD_BUFFER_SIZE]; + + uint8_t nvm__ews__spad_enables_ref__loc2[VL53L1_REF_SPAD_BUFFER_SIZE]; + + uint8_t nvm__ews__spad_enables_ref__loc3[VL53L1_REF_SPAD_BUFFER_SIZE]; + + uint8_t nvm__fmt__spad_enables_rtn[VL53L1_RTN_SPAD_BUFFER_SIZE]; + + uint8_t nvm__fmt__spad_enables_ref__loc1[VL53L1_REF_SPAD_BUFFER_SIZE]; + + uint8_t nvm__fmt__spad_enables_ref__loc2[VL53L1_REF_SPAD_BUFFER_SIZE]; + + uint8_t nvm__fmt__spad_enables_ref__loc3[VL53L1_REF_SPAD_BUFFER_SIZE]; + + uint8_t nvm__fmt__roi_config__mode_roi_centre_spad; + + uint8_t nvm__fmt__roi_config__mode_roi_x_size; + + uint8_t nvm__fmt__roi_config__mode_roi_y_size; + + uint8_t nvm__fmt__ref_spad_apply__num_requested_ref_spad; + + uint8_t nvm__fmt__ref_spad_man__ref_location; + + uint16_t nvm__fmt__mm_config__inner_offset_mm; + + uint16_t nvm__fmt__mm_config__outer_offset_mm; + + uint16_t nvm__fmt__algo_part_to_part_range_offset_mm; + + uint16_t nvm__fmt__algo__crosstalk_compensation_plane_offset_kcps; + + uint16_t nvm__fmt__algo__crosstalk_compensation_x_plane_gradient_kcps; + + uint16_t nvm__fmt__algo__crosstalk_compensation_y_plane_gradient_kcps; + + uint8_t nvm__fmt__spare__host_config__nvm_config_spare_0; + + uint8_t nvm__fmt__spare__host_config__nvm_config_spare_1; + + uint8_t nvm__customer_space_programmed; + + uint8_t nvm__cust__i2c_device_address; + + uint8_t nvm__cust__ref_spad_apply__num_requested_ref_spad; + + uint8_t nvm__cust__ref_spad_man__ref_location; + + uint16_t nvm__cust__mm_config__inner_offset_mm; + + uint16_t nvm__cust__mm_config__outer_offset_mm; + + uint16_t nvm__cust__algo_part_to_part_range_offset_mm; + + uint16_t nvm__cust__algo__crosstalk_compensation_plane_offset_kcps; + + uint16_t nvm__cust__algo__crosstalk_compensation_x_plane_gradient_kcps; + + uint16_t nvm__cust__algo__crosstalk_compensation_y_plane_gradient_kcps; + + uint8_t nvm__cust__spare__host_config__nvm_config_spare_0; + + uint8_t nvm__cust__spare__host_config__nvm_config_spare_1; + + + VL53L1_optical_centre_t fmt_optical_centre; + VL53L1_cal_peak_rate_map_t fmt_peak_rate_map; + VL53L1_additional_offset_cal_data_t fmt_add_offset_data; + + VL53L1_decoded_nvm_fmt_range_data_t + fmt_range_data[VL53L1_NVM_MAX_FMT_RANGE_DATA]; + + VL53L1_decoded_nvm_fmt_info_t fmt_info; + VL53L1_decoded_nvm_ews_info_t ews_info; + +} VL53L1_decoded_nvm_data_t; + + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_preset_setup.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_preset_setup.h new file mode 100644 index 000000000000..834273efdc6a --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_preset_setup.h @@ -0,0 +1,67 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +#ifndef _VL53L1_PRESET_SETUP_H_ +#define _VL53L1_PRESET_SETUP_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* indexes for the bare driver tuning setting API function */ +enum VL53L1_Tuning_t { + VL53L1_TUNING_VERSION = 0, + VL53L1_TUNING_PROXY_MIN, + VL53L1_TUNING_SINGLE_TARGET_XTALK_TARGET_DISTANCE_MM, + VL53L1_TUNING_SINGLE_TARGET_XTALK_SAMPLE_NUMBER, + VL53L1_TUNING_MIN_AMBIENT_DMAX_VALID, + VL53L1_TUNING_MAX_SIMPLE_OFFSET_CALIBRATION_SAMPLE_NUMBER, + VL53L1_TUNING_XTALK_FULL_ROI_TARGET_DISTANCE_MM, + VL53L1_TUNING_SIMPLE_OFFSET_CALIBRATION_REPEAT, + VL53L1_TUNING_XTALK_FULL_ROI_BIN_SUM_MARGIN, + VL53L1_TUNING_XTALK_FULL_ROI_DEFAULT_OFFSET, + VL53L1_TUNING_ZERO_DISTANCE_OFFSET_NON_LINEAR_FACTOR, + VL53L1_TUNING_MAX_TUNABLE_KEY +}; + +/* default values for the tuning settings parameters */ +#define TUNING_VERSION 0x0007 + +#define TUNING_PROXY_MIN -30 /* min distance in mm */ +#define TUNING_SINGLE_TARGET_XTALK_TARGET_DISTANCE_MM 600 +/* Target distance in mm for single target Xtalk */ +#define TUNING_SINGLE_TARGET_XTALK_SAMPLE_NUMBER 50 +/* Number of sample used for single target Xtalk */ +#define TUNING_MIN_AMBIENT_DMAX_VALID 8 +/* Minimum ambient level to state the Dmax returned by the device is valid */ +#define TUNING_MAX_SIMPLE_OFFSET_CALIBRATION_SAMPLE_NUMBER 10 +/* Maximum loops to perform simple offset calibration */ +#define TUNING_XTALK_FULL_ROI_TARGET_DISTANCE_MM 600 +/* Target distance in mm for target Xtalk from Bins method*/ +#define TUNING_SIMPLE_OFFSET_CALIBRATION_REPEAT 3 +/* Number of loops done during the simple offset calibration*/ +#define TUNING_ZERO_DISTANCE_OFFSET_NON_LINEAR_FACTOR_DEFAULT 9 +/* zero distance offset calibration non linear compensation default value */ + +/* The following settings are related to the fix for ticket EwokP #558410 */ +#define TUNING_XTALK_FULL_ROI_BIN_SUM_MARGIN 24 +/* Acceptance margin for the xtalk_shape bin_data sum computation */ +#define TUNING_XTALK_FULL_ROI_DEFAULT_OFFSET 50 +/* Recovery value for Xtalk compensation plane offset in kcps */ +/* 50 stands for ~0.10 kcps cover glass in 7.9 format */ +/* End of settings related to the fix for ticket EwokP #558410 */ + +#ifdef __cplusplus +} +#endif + +#endif /* _VL53L1_PRESET_SETUP_H_ */ diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_register_funcs.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_register_funcs.h new file mode 100644 index 000000000000..7e1185b4cb47 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_register_funcs.h @@ -0,0 +1,662 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#ifndef _VL53L1_REGISTER_FUNCS_H_ +#define _VL53L1_REGISTER_FUNCS_H_ + +#include "vl53l1_platform.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + + + +VL53L1_Error VL53L1_i2c_encode_static_nvm_managed( + VL53L1_static_nvm_managed_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_static_nvm_managed( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_static_nvm_managed_t *pdata); + + + + +VL53L1_Error VL53L1_set_static_nvm_managed( + VL53L1_DEV Dev, + VL53L1_static_nvm_managed_t *pdata); + + + + +VL53L1_Error VL53L1_get_static_nvm_managed( + VL53L1_DEV Dev, + VL53L1_static_nvm_managed_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_customer_nvm_managed( + VL53L1_customer_nvm_managed_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_customer_nvm_managed( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_customer_nvm_managed_t *pdata); + + + + +VL53L1_Error VL53L1_set_customer_nvm_managed( + VL53L1_DEV Dev, + VL53L1_customer_nvm_managed_t *pdata); + + + + +VL53L1_Error VL53L1_get_customer_nvm_managed( + VL53L1_DEV Dev, + VL53L1_customer_nvm_managed_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_static_config( + VL53L1_static_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_static_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_static_config_t *pdata); + + + + +VL53L1_Error VL53L1_set_static_config( + VL53L1_DEV Dev, + VL53L1_static_config_t *pdata); + + + + +VL53L1_Error VL53L1_get_static_config( + VL53L1_DEV Dev, + VL53L1_static_config_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_general_config( + VL53L1_general_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_general_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_general_config_t *pdata); + + + + +VL53L1_Error VL53L1_set_general_config( + VL53L1_DEV Dev, + VL53L1_general_config_t *pdata); + + + + +VL53L1_Error VL53L1_get_general_config( + VL53L1_DEV Dev, + VL53L1_general_config_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_timing_config( + VL53L1_timing_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_timing_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_timing_config_t *pdata); + + + + +VL53L1_Error VL53L1_set_timing_config( + VL53L1_DEV Dev, + VL53L1_timing_config_t *pdata); + + + + +VL53L1_Error VL53L1_get_timing_config( + VL53L1_DEV Dev, + VL53L1_timing_config_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_dynamic_config( + VL53L1_dynamic_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_dynamic_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_dynamic_config_t *pdata); + + + + +VL53L1_Error VL53L1_set_dynamic_config( + VL53L1_DEV Dev, + VL53L1_dynamic_config_t *pdata); + + + + +VL53L1_Error VL53L1_get_dynamic_config( + VL53L1_DEV Dev, + VL53L1_dynamic_config_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_system_control( + VL53L1_system_control_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_system_control( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_system_control_t *pdata); + + + + +VL53L1_Error VL53L1_set_system_control( + VL53L1_DEV Dev, + VL53L1_system_control_t *pdata); + + + + +VL53L1_Error VL53L1_get_system_control( + VL53L1_DEV Dev, + VL53L1_system_control_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_system_results( + VL53L1_system_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_system_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_system_results_t *pdata); + + + + +VL53L1_Error VL53L1_set_system_results( + VL53L1_DEV Dev, + VL53L1_system_results_t *pdata); + + + + +VL53L1_Error VL53L1_get_system_results( + VL53L1_DEV Dev, + VL53L1_system_results_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_core_results( + VL53L1_core_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_core_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_core_results_t *pdata); + + + + +VL53L1_Error VL53L1_set_core_results( + VL53L1_DEV Dev, + VL53L1_core_results_t *pdata); + + + + +VL53L1_Error VL53L1_get_core_results( + VL53L1_DEV Dev, + VL53L1_core_results_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_debug_results( + VL53L1_debug_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_debug_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_debug_results_t *pdata); + + + + +VL53L1_Error VL53L1_set_debug_results( + VL53L1_DEV Dev, + VL53L1_debug_results_t *pdata); + + + + +VL53L1_Error VL53L1_get_debug_results( + VL53L1_DEV Dev, + VL53L1_debug_results_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_nvm_copy_data( + VL53L1_nvm_copy_data_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_nvm_copy_data( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_nvm_copy_data_t *pdata); + + + + +VL53L1_Error VL53L1_set_nvm_copy_data( + VL53L1_DEV Dev, + VL53L1_nvm_copy_data_t *pdata); + + + + +VL53L1_Error VL53L1_get_nvm_copy_data( + VL53L1_DEV Dev, + VL53L1_nvm_copy_data_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_prev_shadow_system_results( + VL53L1_prev_shadow_system_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_prev_shadow_system_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_prev_shadow_system_results_t *pdata); + + + + +VL53L1_Error VL53L1_set_prev_shadow_system_results( + VL53L1_DEV Dev, + VL53L1_prev_shadow_system_results_t *pdata); + + + + +VL53L1_Error VL53L1_get_prev_shadow_system_results( + VL53L1_DEV Dev, + VL53L1_prev_shadow_system_results_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_prev_shadow_core_results( + VL53L1_prev_shadow_core_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_prev_shadow_core_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_prev_shadow_core_results_t *pdata); + + + + +VL53L1_Error VL53L1_set_prev_shadow_core_results( + VL53L1_DEV Dev, + VL53L1_prev_shadow_core_results_t *pdata); + + + + +VL53L1_Error VL53L1_get_prev_shadow_core_results( + VL53L1_DEV Dev, + VL53L1_prev_shadow_core_results_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_patch_debug( + VL53L1_patch_debug_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_patch_debug( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_patch_debug_t *pdata); + + + + +VL53L1_Error VL53L1_set_patch_debug( + VL53L1_DEV Dev, + VL53L1_patch_debug_t *pdata); + + + + +VL53L1_Error VL53L1_get_patch_debug( + VL53L1_DEV Dev, + VL53L1_patch_debug_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_gph_general_config( + VL53L1_gph_general_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_gph_general_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_gph_general_config_t *pdata); + + + + +VL53L1_Error VL53L1_set_gph_general_config( + VL53L1_DEV Dev, + VL53L1_gph_general_config_t *pdata); + + + + +VL53L1_Error VL53L1_get_gph_general_config( + VL53L1_DEV Dev, + VL53L1_gph_general_config_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_gph_static_config( + VL53L1_gph_static_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_gph_static_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_gph_static_config_t *pdata); + + + + +VL53L1_Error VL53L1_set_gph_static_config( + VL53L1_DEV Dev, + VL53L1_gph_static_config_t *pdata); + + + + +VL53L1_Error VL53L1_get_gph_static_config( + VL53L1_DEV Dev, + VL53L1_gph_static_config_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_gph_timing_config( + VL53L1_gph_timing_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_gph_timing_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_gph_timing_config_t *pdata); + + + + +VL53L1_Error VL53L1_set_gph_timing_config( + VL53L1_DEV Dev, + VL53L1_gph_timing_config_t *pdata); + + + + +VL53L1_Error VL53L1_get_gph_timing_config( + VL53L1_DEV Dev, + VL53L1_gph_timing_config_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_fw_internal( + VL53L1_fw_internal_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_fw_internal( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_fw_internal_t *pdata); + + + + +VL53L1_Error VL53L1_set_fw_internal( + VL53L1_DEV Dev, + VL53L1_fw_internal_t *pdata); + + + + +VL53L1_Error VL53L1_get_fw_internal( + VL53L1_DEV Dev, + VL53L1_fw_internal_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_patch_results( + VL53L1_patch_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_patch_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_patch_results_t *pdata); + + + + +VL53L1_Error VL53L1_set_patch_results( + VL53L1_DEV Dev, + VL53L1_patch_results_t *pdata); + + + + +VL53L1_Error VL53L1_get_patch_results( + VL53L1_DEV Dev, + VL53L1_patch_results_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_shadow_system_results( + VL53L1_shadow_system_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_shadow_system_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_shadow_system_results_t *pdata); + + + + +VL53L1_Error VL53L1_set_shadow_system_results( + VL53L1_DEV Dev, + VL53L1_shadow_system_results_t *pdata); + + + + +VL53L1_Error VL53L1_get_shadow_system_results( + VL53L1_DEV Dev, + VL53L1_shadow_system_results_t *pdata); + + + + +VL53L1_Error VL53L1_i2c_encode_shadow_core_results( + VL53L1_shadow_core_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer); + + + + +VL53L1_Error VL53L1_i2c_decode_shadow_core_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_shadow_core_results_t *pdata); + + + + +VL53L1_Error VL53L1_set_shadow_core_results( + VL53L1_DEV Dev, + VL53L1_shadow_core_results_t *pdata); + + + + +VL53L1_Error VL53L1_get_shadow_core_results( + VL53L1_DEV Dev, + VL53L1_shadow_core_results_t *pdata); + + +#ifdef __cplusplus +} +#endif + +#endif + + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_register_map.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_register_map.h new file mode 100644 index 000000000000..148a9c15252e --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_register_map.h @@ -0,0 +1,2390 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#ifndef _VL53L1_REGISTER_MAP_H_ +#define _VL53L1_REGISTER_MAP_H_ + + + +#define VL53L1_SOFT_RESET 0x0000 + +#define VL53L1_I2C_SLAVE__DEVICE_ADDRESS 0x0001 + +#define VL53L1_ANA_CONFIG__VHV_REF_SEL_VDDPIX 0x0002 + +#define VL53L1_ANA_CONFIG__VHV_REF_SEL_VQUENCH 0x0003 + +#define VL53L1_ANA_CONFIG__REG_AVDD1V2_SEL 0x0004 + +#define VL53L1_ANA_CONFIG__FAST_OSC__TRIM 0x0005 + +#define VL53L1_OSC_MEASURED__FAST_OSC__FREQUENCY 0x0006 + +#define VL53L1_OSC_MEASURED__FAST_OSC__FREQUENCY_HI 0x0006 + +#define VL53L1_OSC_MEASURED__FAST_OSC__FREQUENCY_LO 0x0007 + +#define VL53L1_VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND 0x0008 + +#define VL53L1_VHV_CONFIG__COUNT_THRESH 0x0009 + +#define VL53L1_VHV_CONFIG__OFFSET 0x000A + +#define VL53L1_VHV_CONFIG__INIT 0x000B + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_0 0x000D + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_1 0x000E + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_2 0x000F + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_3 0x0010 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_4 0x0011 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_5 0x0012 + +#define VL53L1_GLOBAL_CONFIG__REF_EN_START_SELECT 0x0013 + +#define VL53L1_REF_SPAD_MAN__NUM_REQUESTED_REF_SPADS 0x0014 + +#define VL53L1_REF_SPAD_MAN__REF_LOCATION 0x0015 + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS 0x0016 + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS_HI 0x0016 + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS_LO 0x0017 + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS 0x0018 + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS_HI 0x0018 + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS_LO 0x0019 + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS 0x001A + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS_HI 0x001A + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS_LO 0x001B + +#define VL53L1_REF_SPAD_CHAR__TOTAL_RATE_TARGET_MCPS 0x001C + +#define VL53L1_REF_SPAD_CHAR__TOTAL_RATE_TARGET_MCPS_HI 0x001C + +#define VL53L1_REF_SPAD_CHAR__TOTAL_RATE_TARGET_MCPS_LO 0x001D + +#define VL53L1_ALGO__PART_TO_PART_RANGE_OFFSET_MM 0x001E + +#define VL53L1_ALGO__PART_TO_PART_RANGE_OFFSET_MM_HI 0x001E + +#define VL53L1_ALGO__PART_TO_PART_RANGE_OFFSET_MM_LO 0x001F + +#define VL53L1_MM_CONFIG__INNER_OFFSET_MM 0x0020 + +#define VL53L1_MM_CONFIG__INNER_OFFSET_MM_HI 0x0020 + +#define VL53L1_MM_CONFIG__INNER_OFFSET_MM_LO 0x0021 + +#define VL53L1_MM_CONFIG__OUTER_OFFSET_MM 0x0022 + +#define VL53L1_MM_CONFIG__OUTER_OFFSET_MM_HI 0x0022 + +#define VL53L1_MM_CONFIG__OUTER_OFFSET_MM_LO 0x0023 + +#define VL53L1_DSS_CONFIG__TARGET_TOTAL_RATE_MCPS 0x0024 + +#define VL53L1_DSS_CONFIG__TARGET_TOTAL_RATE_MCPS_HI 0x0024 + +#define VL53L1_DSS_CONFIG__TARGET_TOTAL_RATE_MCPS_LO 0x0025 + +#define VL53L1_DEBUG__CTRL 0x0026 + +#define VL53L1_TEST_MODE__CTRL 0x0027 + +#define VL53L1_CLK_GATING__CTRL 0x0028 + +#define VL53L1_NVM_BIST__CTRL 0x0029 + +#define VL53L1_NVM_BIST__NUM_NVM_WORDS 0x002A + +#define VL53L1_NVM_BIST__START_ADDRESS 0x002B + +#define VL53L1_HOST_IF__STATUS 0x002C + +#define VL53L1_PAD_I2C_HV__CONFIG 0x002D + +#define VL53L1_PAD_I2C_HV__EXTSUP_CONFIG 0x002E + +#define VL53L1_GPIO_HV_PAD__CTRL 0x002F + +#define VL53L1_GPIO_HV_MUX__CTRL 0x0030 + +#define VL53L1_GPIO__TIO_HV_STATUS 0x0031 + +#define VL53L1_GPIO__FIO_HV_STATUS 0x0032 + +#define VL53L1_ANA_CONFIG__SPAD_SEL_PSWIDTH 0x0033 + +#define VL53L1_ANA_CONFIG__VCSEL_PULSE_WIDTH_OFFSET 0x0034 + +#define VL53L1_ANA_CONFIG__FAST_OSC__CONFIG_CTRL 0x0035 + +#define VL53L1_SIGMA_ESTIMATOR__EFFECTIVE_PULSE_WIDTH_NS 0x0036 + +#define VL53L1_SIGMA_ESTIMATOR__EFFECTIVE_AMBIENT_WIDTH_NS 0x0037 + +#define VL53L1_SIGMA_ESTIMATOR__SIGMA_REF_MM 0x0038 + +#define VL53L1_ALGO__CROSSTALK_COMPENSATION_VALID_HEIGHT_MM 0x0039 + +#define VL53L1_SPARE_HOST_CONFIG__STATIC_CONFIG_SPARE_0 0x003A + +#define VL53L1_SPARE_HOST_CONFIG__STATIC_CONFIG_SPARE_1 0x003B + +#define VL53L1_ALGO__RANGE_IGNORE_THRESHOLD_MCPS 0x003C + +#define VL53L1_ALGO__RANGE_IGNORE_THRESHOLD_MCPS_HI 0x003C + +#define VL53L1_ALGO__RANGE_IGNORE_THRESHOLD_MCPS_LO 0x003D + +#define VL53L1_ALGO__RANGE_IGNORE_VALID_HEIGHT_MM 0x003E + +#define VL53L1_ALGO__RANGE_MIN_CLIP 0x003F + +#define VL53L1_ALGO__CONSISTENCY_CHECK__TOLERANCE 0x0040 + +#define VL53L1_SPARE_HOST_CONFIG__STATIC_CONFIG_SPARE_2 0x0041 + +#define VL53L1_SD_CONFIG__RESET_STAGES_MSB 0x0042 + +#define VL53L1_SD_CONFIG__RESET_STAGES_LSB 0x0043 + +#define VL53L1_GPH_CONFIG__STREAM_COUNT_UPDATE_VALUE 0x0044 + +#define VL53L1_GLOBAL_CONFIG__STREAM_DIVIDER 0x0045 + +#define VL53L1_SYSTEM__INTERRUPT_CONFIG_GPIO 0x0046 + +#define VL53L1_CAL_CONFIG__VCSEL_START 0x0047 + +#define VL53L1_CAL_CONFIG__REPEAT_RATE 0x0048 + +#define VL53L1_CAL_CONFIG__REPEAT_RATE_HI 0x0048 + +#define VL53L1_CAL_CONFIG__REPEAT_RATE_LO 0x0049 + +#define VL53L1_GLOBAL_CONFIG__VCSEL_WIDTH 0x004A + +#define VL53L1_PHASECAL_CONFIG__TIMEOUT_MACROP 0x004B + +#define VL53L1_PHASECAL_CONFIG__TARGET 0x004C + +#define VL53L1_PHASECAL_CONFIG__OVERRIDE 0x004D + +#define VL53L1_DSS_CONFIG__ROI_MODE_CONTROL 0x004F + +#define VL53L1_SYSTEM__THRESH_RATE_HIGH 0x0050 + +#define VL53L1_SYSTEM__THRESH_RATE_HIGH_HI 0x0050 + +#define VL53L1_SYSTEM__THRESH_RATE_HIGH_LO 0x0051 + +#define VL53L1_SYSTEM__THRESH_RATE_LOW 0x0052 + +#define VL53L1_SYSTEM__THRESH_RATE_LOW_HI 0x0052 + +#define VL53L1_SYSTEM__THRESH_RATE_LOW_LO 0x0053 + +#define VL53L1_DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT 0x0054 + +#define VL53L1_DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT_HI 0x0054 + +#define VL53L1_DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT_LO 0x0055 + +#define VL53L1_DSS_CONFIG__MANUAL_BLOCK_SELECT 0x0056 + +#define VL53L1_DSS_CONFIG__APERTURE_ATTENUATION 0x0057 + +#define VL53L1_DSS_CONFIG__MAX_SPADS_LIMIT 0x0058 + +#define VL53L1_DSS_CONFIG__MIN_SPADS_LIMIT 0x0059 + +#define VL53L1_MM_CONFIG__TIMEOUT_MACROP_A_HI 0x005A + +#define VL53L1_MM_CONFIG__TIMEOUT_MACROP_A_LO 0x005B + +#define VL53L1_MM_CONFIG__TIMEOUT_MACROP_B_HI 0x005C + +#define VL53L1_MM_CONFIG__TIMEOUT_MACROP_B_LO 0x005D + +#define VL53L1_RANGE_CONFIG__TIMEOUT_MACROP_A_HI 0x005E + +#define VL53L1_RANGE_CONFIG__TIMEOUT_MACROP_A_LO 0x005F + +#define VL53L1_RANGE_CONFIG__VCSEL_PERIOD_A 0x0060 + +#define VL53L1_RANGE_CONFIG__TIMEOUT_MACROP_B_HI 0x0061 + +#define VL53L1_RANGE_CONFIG__TIMEOUT_MACROP_B_LO 0x0062 + +#define VL53L1_RANGE_CONFIG__VCSEL_PERIOD_B 0x0063 + +#define VL53L1_RANGE_CONFIG__SIGMA_THRESH 0x0064 + +#define VL53L1_RANGE_CONFIG__SIGMA_THRESH_HI 0x0064 + +#define VL53L1_RANGE_CONFIG__SIGMA_THRESH_LO 0x0065 + +#define VL53L1_RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS 0x0066 + +#define VL53L1_RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS_HI 0x0066 + +#define VL53L1_RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS_LO 0x0067 + +#define VL53L1_RANGE_CONFIG__VALID_PHASE_LOW 0x0068 + +#define VL53L1_RANGE_CONFIG__VALID_PHASE_HIGH 0x0069 + +#define VL53L1_SYSTEM__INTERMEASUREMENT_PERIOD 0x006C + +#define VL53L1_SYSTEM__INTERMEASUREMENT_PERIOD_3 0x006C + +#define VL53L1_SYSTEM__INTERMEASUREMENT_PERIOD_2 0x006D + +#define VL53L1_SYSTEM__INTERMEASUREMENT_PERIOD_1 0x006E + +#define VL53L1_SYSTEM__INTERMEASUREMENT_PERIOD_0 0x006F + +#define VL53L1_SYSTEM__FRACTIONAL_ENABLE 0x0070 + +#define VL53L1_SYSTEM__GROUPED_PARAMETER_HOLD_0 0x0071 + +#define VL53L1_SYSTEM__THRESH_HIGH 0x0072 + +#define VL53L1_SYSTEM__THRESH_HIGH_HI 0x0072 + +#define VL53L1_SYSTEM__THRESH_HIGH_LO 0x0073 + +#define VL53L1_SYSTEM__THRESH_LOW 0x0074 + +#define VL53L1_SYSTEM__THRESH_LOW_HI 0x0074 + +#define VL53L1_SYSTEM__THRESH_LOW_LO 0x0075 + +#define VL53L1_SYSTEM__ENABLE_XTALK_PER_QUADRANT 0x0076 + +#define VL53L1_SYSTEM__SEED_CONFIG 0x0077 + +#define VL53L1_SD_CONFIG__WOI_SD0 0x0078 + +#define VL53L1_SD_CONFIG__WOI_SD1 0x0079 + +#define VL53L1_SD_CONFIG__INITIAL_PHASE_SD0 0x007A + +#define VL53L1_SD_CONFIG__INITIAL_PHASE_SD1 0x007B + +#define VL53L1_SYSTEM__GROUPED_PARAMETER_HOLD_1 0x007C + +#define VL53L1_SD_CONFIG__FIRST_ORDER_SELECT 0x007D + +#define VL53L1_SD_CONFIG__QUANTIFIER 0x007E + +#define VL53L1_ROI_CONFIG__USER_ROI_CENTRE_SPAD 0x007F + +#define VL53L1_ROI_CONFIG__USER_ROI_REQUESTED_GLOBAL_XY_SIZE 0x0080 + +#define VL53L1_SYSTEM__SEQUENCE_CONFIG 0x0081 + +#define VL53L1_SYSTEM__GROUPED_PARAMETER_HOLD 0x0082 + +#define VL53L1_POWER_MANAGEMENT__GO1_POWER_FORCE 0x0083 + +#define VL53L1_SYSTEM__STREAM_COUNT_CTRL 0x0084 + +#define VL53L1_FIRMWARE__ENABLE 0x0085 + +#define VL53L1_SYSTEM__INTERRUPT_CLEAR 0x0086 + +#define VL53L1_SYSTEM__MODE_START 0x0087 + +#define VL53L1_RESULT__INTERRUPT_STATUS 0x0088 + +#define VL53L1_RESULT__RANGE_STATUS 0x0089 + +#define VL53L1_RESULT__REPORT_STATUS 0x008A + +#define VL53L1_RESULT__STREAM_COUNT 0x008B + +#define VL53L1_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0 0x008C + +#define VL53L1_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_HI 0x008C + +#define VL53L1_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_LO 0x008D + +#define VL53L1_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0 0x008E + +#define VL53L1_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_HI 0x008E + +#define VL53L1_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_LO 0x008F + +#define VL53L1_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0 0x0090 + +#define VL53L1_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_HI 0x0090 + +#define VL53L1_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_LO 0x0091 + +#define VL53L1_RESULT__SIGMA_SD0 0x0092 + +#define VL53L1_RESULT__SIGMA_SD0_HI 0x0092 + +#define VL53L1_RESULT__SIGMA_SD0_LO 0x0093 + +#define VL53L1_RESULT__PHASE_SD0 0x0094 + +#define VL53L1_RESULT__PHASE_SD0_HI 0x0094 + +#define VL53L1_RESULT__PHASE_SD0_LO 0x0095 + +#define VL53L1_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 0x0096 + +#define VL53L1_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_HI 0x0096 + +#define VL53L1_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_LO 0x0097 + +#define VL53L1_PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0 0x0098 + +#define VL53L1__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_HI 0x0098 + +#define VL53L1___PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_LO 0x0099 + +#define VL53L1_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0 0x009A + +#define VL53L1_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_HI 0x009A + +#define VL53L1_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_LO 0x009B + +#define VL53L1_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0 0x009C + +#define VL53L1_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_HI 0x009C + +#define VL53L1_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_LO 0x009D + +#define VL53L1_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0 0x009E + +#define VL53L1_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_HI 0x009E + +#define VL53L1_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_LO 0x009F + +#define VL53L1_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1 0x00A0 + +#define VL53L1_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_HI 0x00A0 + +#define VL53L1_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_LO 0x00A1 + +#define VL53L1_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1 0x00A2 + +#define VL53L1_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_HI 0x00A2 + +#define VL53L1_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_LO 0x00A3 + +#define VL53L1_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1 0x00A4 + +#define VL53L1_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_HI 0x00A4 + +#define VL53L1_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_LO 0x00A5 + +#define VL53L1_RESULT__SIGMA_SD1 0x00A6 + +#define VL53L1_RESULT__SIGMA_SD1_HI 0x00A6 + +#define VL53L1_RESULT__SIGMA_SD1_LO 0x00A7 + +#define VL53L1_RESULT__PHASE_SD1 0x00A8 + +#define VL53L1_RESULT__PHASE_SD1_HI 0x00A8 + +#define VL53L1_RESULT__PHASE_SD1_LO 0x00A9 + +#define VL53L1_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1 0x00AA + +#define VL53L1_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_HI 0x00AA + +#define VL53L1_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_LO 0x00AB + +#define VL53L1_RESULT__SPARE_0_SD1 0x00AC + +#define VL53L1_RESULT__SPARE_0_SD1_HI 0x00AC + +#define VL53L1_RESULT__SPARE_0_SD1_LO 0x00AD + +#define VL53L1_RESULT__SPARE_1_SD1 0x00AE + +#define VL53L1_RESULT__SPARE_1_SD1_HI 0x00AE + +#define VL53L1_RESULT__SPARE_1_SD1_LO 0x00AF + +#define VL53L1_RESULT__SPARE_2_SD1 0x00B0 + +#define VL53L1_RESULT__SPARE_2_SD1_HI 0x00B0 + +#define VL53L1_RESULT__SPARE_2_SD1_LO 0x00B1 + +#define VL53L1_RESULT__SPARE_3_SD1 0x00B2 + +#define VL53L1_RESULT__THRESH_INFO 0x00B3 + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0 0x00B4 + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_3 0x00B4 + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_2 0x00B5 + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_1 0x00B6 + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_0 0x00B7 + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0 0x00B8 + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_3 0x00B8 + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_2 0x00B9 + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_1 0x00BA + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_0 0x00BB + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0 0x00BC + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_3 0x00BC + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_2 0x00BD + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_1 0x00BE + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_0 0x00BF + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0 0x00C0 + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_3 0x00C0 + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_2 0x00C1 + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_1 0x00C2 + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_0 0x00C3 + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1 0x00C4 + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_3 0x00C4 + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_2 0x00C5 + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_1 0x00C6 + +#define VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_0 0x00C7 + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1 0x00C8 + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_3 0x00C8 + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_2 0x00C9 + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_1 0x00CA + +#define VL53L1_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_0 0x00CB + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1 0x00CC + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_3 0x00CC + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_2 0x00CD + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_1 0x00CE + +#define VL53L1_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_0 0x00CF + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1 0x00D0 + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_3 0x00D0 + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_2 0x00D1 + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_1 0x00D2 + +#define VL53L1_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_0 0x00D3 + +#define VL53L1_RESULT_CORE__SPARE_0 0x00D4 + +#define VL53L1_PHASECAL_RESULT__REFERENCE_PHASE 0x00D6 + +#define VL53L1_PHASECAL_RESULT__REFERENCE_PHASE_HI 0x00D6 + +#define VL53L1_PHASECAL_RESULT__REFERENCE_PHASE_LO 0x00D7 + +#define VL53L1_PHASECAL_RESULT__VCSEL_START 0x00D8 + +#define VL53L1_REF_SPAD_CHAR_RESULT__NUM_ACTUAL_REF_SPADS 0x00D9 + +#define VL53L1_REF_SPAD_CHAR_RESULT__REF_LOCATION 0x00DA + +#define VL53L1_VHV_RESULT__COLDBOOT_STATUS 0x00DB + +#define VL53L1_VHV_RESULT__SEARCH_RESULT 0x00DC + +#define VL53L1_VHV_RESULT__LATEST_SETTING 0x00DD + +#define VL53L1_RESULT__OSC_CALIBRATE_VAL 0x00DE + +#define VL53L1_RESULT__OSC_CALIBRATE_VAL_HI 0x00DE + +#define VL53L1_RESULT__OSC_CALIBRATE_VAL_LO 0x00DF + +#define VL53L1_ANA_CONFIG__POWERDOWN_GO1 0x00E0 + +#define VL53L1_ANA_CONFIG__REF_BG_CTRL 0x00E1 + +#define VL53L1_ANA_CONFIG__REGDVDD1V2_CTRL 0x00E2 + +#define VL53L1_ANA_CONFIG__OSC_SLOW_CTRL 0x00E3 + +#define VL53L1_TEST_MODE__STATUS 0x00E4 + +#define VL53L1_FIRMWARE__SYSTEM_STATUS 0x00E5 + +#define VL53L1_FIRMWARE__MODE_STATUS 0x00E6 + +#define VL53L1_FIRMWARE__SECONDARY_MODE_STATUS 0x00E7 + +#define VL53L1_FIRMWARE__CAL_REPEAT_RATE_COUNTER 0x00E8 + +#define VL53L1_FIRMWARE__CAL_REPEAT_RATE_COUNTER_HI 0x00E8 + +#define VL53L1_FIRMWARE__CAL_REPEAT_RATE_COUNTER_LO 0x00E9 + +#define VL53L1_FIRMWARE__HISTOGRAM_BIN 0x00EA + +#define VL53L1_GPH__SYSTEM__THRESH_HIGH 0x00EC + +#define VL53L1_GPH__SYSTEM__THRESH_HIGH_HI 0x00EC + +#define VL53L1_GPH__SYSTEM__THRESH_HIGH_LO 0x00ED + +#define VL53L1_GPH__SYSTEM__THRESH_LOW 0x00EE + +#define VL53L1_GPH__SYSTEM__THRESH_LOW_HI 0x00EE + +#define VL53L1_GPH__SYSTEM__THRESH_LOW_LO 0x00EF + +#define VL53L1_GPH__SYSTEM__ENABLE_XTALK_PER_QUADRANT 0x00F0 + +#define VL53L1_GPH__SPARE_0 0x00F1 + +#define VL53L1_GPH__SD_CONFIG__WOI_SD0 0x00F2 + +#define VL53L1_GPH__SD_CONFIG__WOI_SD1 0x00F3 + +#define VL53L1_GPH__SD_CONFIG__INITIAL_PHASE_SD0 0x00F4 + +#define VL53L1_GPH__SD_CONFIG__INITIAL_PHASE_SD1 0x00F5 + +#define VL53L1_GPH__SD_CONFIG__FIRST_ORDER_SELECT 0x00F6 + +#define VL53L1_GPH__SD_CONFIG__QUANTIFIER 0x00F7 + +#define VL53L1_GPH__ROI_CONFIG__USER_ROI_CENTRE_SPAD 0x00F8 + +#define VL53L1_GPH__ROI_CONFIG__USER_ROI_REQUESTED_GLOBAL_XY_SIZE 0x00F9 + +#define VL53L1_GPH__SYSTEM__SEQUENCE_CONFIG 0x00FA + +#define VL53L1_GPH__GPH_ID 0x00FB + +#define VL53L1_SYSTEM__INTERRUPT_SET 0x00FC + +#define VL53L1_INTERRUPT_MANAGER__ENABLES 0x00FD + +#define VL53L1_INTERRUPT_MANAGER__CLEAR 0x00FE + +#define VL53L1_INTERRUPT_MANAGER__STATUS 0x00FF + +#define VL53L1_MCU_TO_HOST_BANK__WR_ACCESS_EN 0x0100 + +#define VL53L1_POWER_MANAGEMENT__GO1_RESET_STATUS 0x0101 + +#define VL53L1_PAD_STARTUP_MODE__VALUE_RO 0x0102 + +#define VL53L1_PAD_STARTUP_MODE__VALUE_CTRL 0x0103 + +#define VL53L1_PLL_PERIOD_US 0x0104 + +#define VL53L1_PLL_PERIOD_US_3 0x0104 + +#define VL53L1_PLL_PERIOD_US_2 0x0105 + +#define VL53L1_PLL_PERIOD_US_1 0x0106 + +#define VL53L1_PLL_PERIOD_US_0 0x0107 + +#define VL53L1_INTERRUPT_SCHEDULER__DATA_OUT 0x0108 + +#define VL53L1_INTERRUPT_SCHEDULER__DATA_OUT_3 0x0108 + +#define VL53L1_INTERRUPT_SCHEDULER__DATA_OUT_2 0x0109 + +#define VL53L1_INTERRUPT_SCHEDULER__DATA_OUT_1 0x010A + +#define VL53L1_INTERRUPT_SCHEDULER__DATA_OUT_0 0x010B + +#define VL53L1_NVM_BIST__COMPLETE 0x010C + +#define VL53L1_NVM_BIST__STATUS 0x010D + +#define VL53L1_IDENTIFICATION__MODEL_ID 0x010F + +#define VL53L1_IDENTIFICATION__MODULE_TYPE 0x0110 + +#define VL53L1_IDENTIFICATION__REVISION_ID 0x0111 + +#define VL53L1_IDENTIFICATION__MODULE_ID 0x0112 + +#define VL53L1_IDENTIFICATION__MODULE_ID_HI 0x0112 + +#define VL53L1_IDENTIFICATION__MODULE_ID_LO 0x0113 + +#define VL53L1_ANA_CONFIG__FAST_OSC__TRIM_MAX 0x0114 + +#define VL53L1_ANA_CONFIG__FAST_OSC__FREQ_SET 0x0115 + +#define VL53L1_ANA_CONFIG__VCSEL_TRIM 0x0116 + +#define VL53L1_ANA_CONFIG__VCSEL_SELION 0x0117 + +#define VL53L1_ANA_CONFIG__VCSEL_SELION_MAX 0x0118 + +#define VL53L1_PROTECTED_LASER_SAFETY__LOCK_BIT 0x0119 + +#define VL53L1_LASER_SAFETY__KEY 0x011A + +#define VL53L1_LASER_SAFETY__KEY_RO 0x011B + +#define VL53L1_LASER_SAFETY__CLIP 0x011C + +#define VL53L1_LASER_SAFETY__MULT 0x011D + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_0 0x011E + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_1 0x011F + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_2 0x0120 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_3 0x0121 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_4 0x0122 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_5 0x0123 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_6 0x0124 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_7 0x0125 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_8 0x0126 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_9 0x0127 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_10 0x0128 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_11 0x0129 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_12 0x012A + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_13 0x012B + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_14 0x012C + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_15 0x012D + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_16 0x012E + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_17 0x012F + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_18 0x0130 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_19 0x0131 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_20 0x0132 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_21 0x0133 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_22 0x0134 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_23 0x0135 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_24 0x0136 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_25 0x0137 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_26 0x0138 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_27 0x0139 + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_28 0x013A + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_29 0x013B + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_30 0x013C + +#define VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_RTN_31 0x013D + +#define VL53L1_ROI_CONFIG__MODE_ROI_CENTRE_SPAD 0x013E + +#define VL53L1_ROI_CONFIG__MODE_ROI_XY_SIZE 0x013F + +#define VL53L1_GO2_HOST_BANK_ACCESS__OVERRIDE 0x0300 + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLICAND 0x0400 + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLICAND_3 0x0400 + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLICAND_2 0x0401 + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLICAND_1 0x0402 + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLICAND_0 0x0403 + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLIER 0x0404 + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLIER_3 0x0404 + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLIER_2 0x0405 + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLIER_1 0x0406 + +#define VL53L1_MCU_UTIL_MULTIPLIER__MULTIPLIER_0 0x0407 + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_HI 0x0408 + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_HI_3 0x0408 + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_HI_2 0x0409 + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_HI_1 0x040A + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_HI_0 0x040B + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_LO 0x040C + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_LO_3 0x040C + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_LO_2 0x040D + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_LO_1 0x040E + +#define VL53L1_MCU_UTIL_MULTIPLIER__PRODUCT_LO_0 0x040F + +#define VL53L1_MCU_UTIL_MULTIPLIER__START 0x0410 + +#define VL53L1_MCU_UTIL_MULTIPLIER__STATUS 0x0411 + +#define VL53L1_MCU_UTIL_DIVIDER__START 0x0412 + +#define VL53L1_MCU_UTIL_DIVIDER__STATUS 0x0413 + +#define VL53L1_MCU_UTIL_DIVIDER__DIVIDEND 0x0414 + +#define VL53L1_MCU_UTIL_DIVIDER__DIVIDEND_3 0x0414 + +#define VL53L1_MCU_UTIL_DIVIDER__DIVIDEND_2 0x0415 + +#define VL53L1_MCU_UTIL_DIVIDER__DIVIDEND_1 0x0416 + +#define VL53L1_MCU_UTIL_DIVIDER__DIVIDEND_0 0x0417 + +#define VL53L1_MCU_UTIL_DIVIDER__DIVISOR 0x0418 + +#define VL53L1_MCU_UTIL_DIVIDER__DIVISOR_3 0x0418 + +#define VL53L1_MCU_UTIL_DIVIDER__DIVISOR_2 0x0419 + +#define VL53L1_MCU_UTIL_DIVIDER__DIVISOR_1 0x041A + +#define VL53L1_MCU_UTIL_DIVIDER__DIVISOR_0 0x041B + +#define VL53L1_MCU_UTIL_DIVIDER__QUOTIENT 0x041C + +#define VL53L1_MCU_UTIL_DIVIDER__QUOTIENT_3 0x041C + +#define VL53L1_MCU_UTIL_DIVIDER__QUOTIENT_2 0x041D + +#define VL53L1_MCU_UTIL_DIVIDER__QUOTIENT_1 0x041E + +#define VL53L1_MCU_UTIL_DIVIDER__QUOTIENT_0 0x041F + +#define VL53L1_TIMER0__VALUE_IN 0x0420 + +#define VL53L1_TIMER0__VALUE_IN_3 0x0420 + +#define VL53L1_TIMER0__VALUE_IN_2 0x0421 + +#define VL53L1_TIMER0__VALUE_IN_1 0x0422 + +#define VL53L1_TIMER0__VALUE_IN_0 0x0423 + +#define VL53L1_TIMER1__VALUE_IN 0x0424 + +#define VL53L1_TIMER1__VALUE_IN_3 0x0424 + +#define VL53L1_TIMER1__VALUE_IN_2 0x0425 + +#define VL53L1_TIMER1__VALUE_IN_1 0x0426 + +#define VL53L1_TIMER1__VALUE_IN_0 0x0427 + +#define VL53L1_TIMER0__CTRL 0x0428 + +#define VL53L1_TIMER1__CTRL 0x0429 + +#define VL53L1_MCU_GENERAL_PURPOSE__GP_0 0x042C + +#define VL53L1_MCU_GENERAL_PURPOSE__GP_1 0x042D + +#define VL53L1_MCU_GENERAL_PURPOSE__GP_2 0x042E + +#define VL53L1_MCU_GENERAL_PURPOSE__GP_3 0x042F + +#define VL53L1_MCU_RANGE_CALC__CONFIG 0x0430 + +#define VL53L1_MCU_RANGE_CALC__OFFSET_CORRECTED_RANGE 0x0432 + +#define VL53L1_MCU_RANGE_CALC__OFFSET_CORRECTED_RANGE_HI 0x0432 + +#define VL53L1_MCU_RANGE_CALC__OFFSET_CORRECTED_RANGE_LO 0x0433 + +#define VL53L1_MCU_RANGE_CALC__SPARE_4 0x0434 + +#define VL53L1_MCU_RANGE_CALC__SPARE_4_3 0x0434 + +#define VL53L1_MCU_RANGE_CALC__SPARE_4_2 0x0435 + +#define VL53L1_MCU_RANGE_CALC__SPARE_4_1 0x0436 + +#define VL53L1_MCU_RANGE_CALC__SPARE_4_0 0x0437 + +#define VL53L1_MCU_RANGE_CALC__AMBIENT_DURATION_PRE_CALC 0x0438 + +#define VL53L1_MCU_RANGE_CALC__AMBIENT_DURATION_PRE_CALC_HI 0x0438 + +#define VL53L1_MCU_RANGE_CALC__AMBIENT_DURATION_PRE_CALC_LO 0x0439 + +#define VL53L1_MCU_RANGE_CALC__ALGO_VCSEL_PERIOD 0x043C + +#define VL53L1_MCU_RANGE_CALC__SPARE_5 0x043D + +#define VL53L1_MCU_RANGE_CALC__ALGO_TOTAL_PERIODS 0x043E + +#define VL53L1_MCU_RANGE_CALC__ALGO_TOTAL_PERIODS_HI 0x043E + +#define VL53L1_MCU_RANGE_CALC__ALGO_TOTAL_PERIODS_LO 0x043F + +#define VL53L1_MCU_RANGE_CALC__ALGO_ACCUM_PHASE 0x0440 + +#define VL53L1_MCU_RANGE_CALC__ALGO_ACCUM_PHASE_3 0x0440 + +#define VL53L1_MCU_RANGE_CALC__ALGO_ACCUM_PHASE_2 0x0441 + +#define VL53L1_MCU_RANGE_CALC__ALGO_ACCUM_PHASE_1 0x0442 + +#define VL53L1_MCU_RANGE_CALC__ALGO_ACCUM_PHASE_0 0x0443 + +#define VL53L1_MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS 0x0444 + +#define VL53L1_MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS_3 0x0444 + +#define VL53L1_MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS_2 0x0445 + +#define VL53L1_MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS_1 0x0446 + +#define VL53L1_MCU_RANGE_CALC__ALGO_SIGNAL_EVENTS_0 0x0447 + +#define VL53L1_MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS 0x0448 + +#define VL53L1_MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS_3 0x0448 + +#define VL53L1_MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS_2 0x0449 + +#define VL53L1_MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS_1 0x044A + +#define VL53L1_MCU_RANGE_CALC__ALGO_AMBIENT_EVENTS_0 0x044B + +#define VL53L1_MCU_RANGE_CALC__SPARE_6 0x044C + +#define VL53L1_MCU_RANGE_CALC__SPARE_6_HI 0x044C + +#define VL53L1_MCU_RANGE_CALC__SPARE_6_LO 0x044D + +#define VL53L1_MCU_RANGE_CALC__ALGO_ADJUST_VCSEL_PERIOD 0x044E + +#define VL53L1_MCU_RANGE_CALC__ALGO_ADJUST_VCSEL_PERIOD_HI 0x044E + +#define VL53L1_MCU_RANGE_CALC__ALGO_ADJUST_VCSEL_PERIOD_LO 0x044F + +#define VL53L1_MCU_RANGE_CALC__NUM_SPADS 0x0450 + +#define VL53L1_MCU_RANGE_CALC__NUM_SPADS_HI 0x0450 + +#define VL53L1_MCU_RANGE_CALC__NUM_SPADS_LO 0x0451 + +#define VL53L1_MCU_RANGE_CALC__PHASE_OUTPUT 0x0452 + +#define VL53L1_MCU_RANGE_CALC__PHASE_OUTPUT_HI 0x0452 + +#define VL53L1_MCU_RANGE_CALC__PHASE_OUTPUT_LO 0x0453 + +#define VL53L1_MCU_RANGE_CALC__RATE_PER_SPAD_MCPS 0x0454 + +#define VL53L1_MCU_RANGE_CALC__RATE_PER_SPAD_MCPS_3 0x0454 + +#define VL53L1_MCU_RANGE_CALC__RATE_PER_SPAD_MCPS_2 0x0455 + +#define VL53L1_MCU_RANGE_CALC__RATE_PER_SPAD_MCPS_1 0x0456 + +#define VL53L1_MCU_RANGE_CALC__RATE_PER_SPAD_MCPS_0 0x0457 + +#define VL53L1_MCU_RANGE_CALC__SPARE_7 0x0458 + +#define VL53L1_MCU_RANGE_CALC__SPARE_8 0x0459 + +#define VL53L1_MCU_RANGE_CALC__PEAK_SIGNAL_RATE_MCPS 0x045A + +#define VL53L1_MCU_RANGE_CALC__PEAK_SIGNAL_RATE_MCPS_HI 0x045A + +#define VL53L1_MCU_RANGE_CALC__PEAK_SIGNAL_RATE_MCPS_LO 0x045B + +#define VL53L1_MCU_RANGE_CALC__AVG_SIGNAL_RATE_MCPS 0x045C + +#define VL53L1_MCU_RANGE_CALC__AVG_SIGNAL_RATE_MCPS_HI 0x045C + +#define VL53L1_MCU_RANGE_CALC__AVG_SIGNAL_RATE_MCPS_LO 0x045D + +#define VL53L1_MCU_RANGE_CALC__AMBIENT_RATE_MCPS 0x045E + +#define VL53L1_MCU_RANGE_CALC__AMBIENT_RATE_MCPS_HI 0x045E + +#define VL53L1_MCU_RANGE_CALC__AMBIENT_RATE_MCPS_LO 0x045F + +#define VL53L1_MCU_RANGE_CALC__XTALK 0x0460 + +#define VL53L1_MCU_RANGE_CALC__XTALK_HI 0x0460 + +#define VL53L1_MCU_RANGE_CALC__XTALK_LO 0x0461 + +#define VL53L1_MCU_RANGE_CALC__CALC_STATUS 0x0462 + +#define VL53L1_MCU_RANGE_CALC__DEBUG 0x0463 + +#define VL53L1_MCU_RANGE_CALC__PEAK_SIGNAL_RATE_XTALK_CORR_MCPS 0x0464 + +#define VL53L1_MCU_RANGE_CALC__PEAK_SIGNAL_RATE_XTALK_CORR_MCPS_HI 0x0464 + +#define VL53L1_MCU_RANGE_CALC__PEAK_SIGNAL_RATE_XTALK_CORR_MCPS_LO 0x0465 + +#define VL53L1_MCU_RANGE_CALC__SPARE_0 0x0468 + +#define VL53L1_MCU_RANGE_CALC__SPARE_1 0x0469 + +#define VL53L1_MCU_RANGE_CALC__SPARE_2 0x046A + +#define VL53L1_MCU_RANGE_CALC__SPARE_3 0x046B + +#define VL53L1_PATCH__CTRL 0x0470 + +#define VL53L1_PATCH__JMP_ENABLES 0x0472 + +#define VL53L1_PATCH__JMP_ENABLES_HI 0x0472 + +#define VL53L1_PATCH__JMP_ENABLES_LO 0x0473 + +#define VL53L1_PATCH__DATA_ENABLES 0x0474 + +#define VL53L1_PATCH__DATA_ENABLES_HI 0x0474 + +#define VL53L1_PATCH__DATA_ENABLES_LO 0x0475 + +#define VL53L1_PATCH__OFFSET_0 0x0476 + +#define VL53L1_PATCH__OFFSET_0_HI 0x0476 + +#define VL53L1_PATCH__OFFSET_0_LO 0x0477 + +#define VL53L1_PATCH__OFFSET_1 0x0478 + +#define VL53L1_PATCH__OFFSET_1_HI 0x0478 + +#define VL53L1_PATCH__OFFSET_1_LO 0x0479 + +#define VL53L1_PATCH__OFFSET_2 0x047A + +#define VL53L1_PATCH__OFFSET_2_HI 0x047A + +#define VL53L1_PATCH__OFFSET_2_LO 0x047B + +#define VL53L1_PATCH__OFFSET_3 0x047C + +#define VL53L1_PATCH__OFFSET_3_HI 0x047C + +#define VL53L1_PATCH__OFFSET_3_LO 0x047D + +#define VL53L1_PATCH__OFFSET_4 0x047E + +#define VL53L1_PATCH__OFFSET_4_HI 0x047E + +#define VL53L1_PATCH__OFFSET_4_LO 0x047F + +#define VL53L1_PATCH__OFFSET_5 0x0480 + +#define VL53L1_PATCH__OFFSET_5_HI 0x0480 + +#define VL53L1_PATCH__OFFSET_5_LO 0x0481 + +#define VL53L1_PATCH__OFFSET_6 0x0482 + +#define VL53L1_PATCH__OFFSET_6_HI 0x0482 + +#define VL53L1_PATCH__OFFSET_6_LO 0x0483 + +#define VL53L1_PATCH__OFFSET_7 0x0484 + +#define VL53L1_PATCH__OFFSET_7_HI 0x0484 + +#define VL53L1_PATCH__OFFSET_7_LO 0x0485 + +#define VL53L1_PATCH__OFFSET_8 0x0486 + +#define VL53L1_PATCH__OFFSET_8_HI 0x0486 + +#define VL53L1_PATCH__OFFSET_8_LO 0x0487 + +#define VL53L1_PATCH__OFFSET_9 0x0488 + +#define VL53L1_PATCH__OFFSET_9_HI 0x0488 + +#define VL53L1_PATCH__OFFSET_9_LO 0x0489 + +#define VL53L1_PATCH__OFFSET_10 0x048A + +#define VL53L1_PATCH__OFFSET_10_HI 0x048A + +#define VL53L1_PATCH__OFFSET_10_LO 0x048B + +#define VL53L1_PATCH__OFFSET_11 0x048C + +#define VL53L1_PATCH__OFFSET_11_HI 0x048C + +#define VL53L1_PATCH__OFFSET_11_LO 0x048D + +#define VL53L1_PATCH__OFFSET_12 0x048E + +#define VL53L1_PATCH__OFFSET_12_HI 0x048E + +#define VL53L1_PATCH__OFFSET_12_LO 0x048F + +#define VL53L1_PATCH__OFFSET_13 0x0490 + +#define VL53L1_PATCH__OFFSET_13_HI 0x0490 + +#define VL53L1_PATCH__OFFSET_13_LO 0x0491 + +#define VL53L1_PATCH__OFFSET_14 0x0492 + +#define VL53L1_PATCH__OFFSET_14_HI 0x0492 + +#define VL53L1_PATCH__OFFSET_14_LO 0x0493 + +#define VL53L1_PATCH__OFFSET_15 0x0494 + +#define VL53L1_PATCH__OFFSET_15_HI 0x0494 + +#define VL53L1_PATCH__OFFSET_15_LO 0x0495 + +#define VL53L1_PATCH__ADDRESS_0 0x0496 + +#define VL53L1_PATCH__ADDRESS_0_HI 0x0496 + +#define VL53L1_PATCH__ADDRESS_0_LO 0x0497 + +#define VL53L1_PATCH__ADDRESS_1 0x0498 + +#define VL53L1_PATCH__ADDRESS_1_HI 0x0498 + +#define VL53L1_PATCH__ADDRESS_1_LO 0x0499 + +#define VL53L1_PATCH__ADDRESS_2 0x049A + +#define VL53L1_PATCH__ADDRESS_2_HI 0x049A + +#define VL53L1_PATCH__ADDRESS_2_LO 0x049B + +#define VL53L1_PATCH__ADDRESS_3 0x049C + +#define VL53L1_PATCH__ADDRESS_3_HI 0x049C + +#define VL53L1_PATCH__ADDRESS_3_LO 0x049D + +#define VL53L1_PATCH__ADDRESS_4 0x049E + +#define VL53L1_PATCH__ADDRESS_4_HI 0x049E + +#define VL53L1_PATCH__ADDRESS_4_LO 0x049F + +#define VL53L1_PATCH__ADDRESS_5 0x04A0 + +#define VL53L1_PATCH__ADDRESS_5_HI 0x04A0 + +#define VL53L1_PATCH__ADDRESS_5_LO 0x04A1 + +#define VL53L1_PATCH__ADDRESS_6 0x04A2 + +#define VL53L1_PATCH__ADDRESS_6_HI 0x04A2 + +#define VL53L1_PATCH__ADDRESS_6_LO 0x04A3 + +#define VL53L1_PATCH__ADDRESS_7 0x04A4 + +#define VL53L1_PATCH__ADDRESS_7_HI 0x04A4 + +#define VL53L1_PATCH__ADDRESS_7_LO 0x04A5 + +#define VL53L1_PATCH__ADDRESS_8 0x04A6 + +#define VL53L1_PATCH__ADDRESS_8_HI 0x04A6 + +#define VL53L1_PATCH__ADDRESS_8_LO 0x04A7 + +#define VL53L1_PATCH__ADDRESS_9 0x04A8 + +#define VL53L1_PATCH__ADDRESS_9_HI 0x04A8 + +#define VL53L1_PATCH__ADDRESS_9_LO 0x04A9 + +#define VL53L1_PATCH__ADDRESS_10 0x04AA + +#define VL53L1_PATCH__ADDRESS_10_HI 0x04AA + +#define VL53L1_PATCH__ADDRESS_10_LO 0x04AB + +#define VL53L1_PATCH__ADDRESS_11 0x04AC + +#define VL53L1_PATCH__ADDRESS_11_HI 0x04AC + +#define VL53L1_PATCH__ADDRESS_11_LO 0x04AD + +#define VL53L1_PATCH__ADDRESS_12 0x04AE + +#define VL53L1_PATCH__ADDRESS_12_HI 0x04AE + +#define VL53L1_PATCH__ADDRESS_12_LO 0x04AF + +#define VL53L1_PATCH__ADDRESS_13 0x04B0 + +#define VL53L1_PATCH__ADDRESS_13_HI 0x04B0 + +#define VL53L1_PATCH__ADDRESS_13_LO 0x04B1 + +#define VL53L1_PATCH__ADDRESS_14 0x04B2 + +#define VL53L1_PATCH__ADDRESS_14_HI 0x04B2 + +#define VL53L1_PATCH__ADDRESS_14_LO 0x04B3 + +#define VL53L1_PATCH__ADDRESS_15 0x04B4 + +#define VL53L1_PATCH__ADDRESS_15_HI 0x04B4 + +#define VL53L1_PATCH__ADDRESS_15_LO 0x04B5 + +#define VL53L1_SPI_ASYNC_MUX__CTRL 0x04C0 + +#define VL53L1_CLK__CONFIG 0x04C4 + +#define VL53L1_GPIO_LV_MUX__CTRL 0x04CC + +#define VL53L1_GPIO_LV_PAD__CTRL 0x04CD + +#define VL53L1_PAD_I2C_LV__CONFIG 0x04D0 + +#define VL53L1_PAD_STARTUP_MODE__VALUE_RO_GO1 0x04D4 + +#define VL53L1_HOST_IF__STATUS_GO1 0x04D5 + +#define VL53L1_MCU_CLK_GATING__CTRL 0x04D8 + +#define VL53L1_TEST__BIST_ROM_CTRL 0x04E0 + +#define VL53L1_TEST__BIST_ROM_RESULT 0x04E1 + +#define VL53L1_TEST__BIST_ROM_MCU_SIG 0x04E2 + +#define VL53L1_TEST__BIST_ROM_MCU_SIG_HI 0x04E2 + +#define VL53L1_TEST__BIST_ROM_MCU_SIG_LO 0x04E3 + +#define VL53L1_TEST__BIST_RAM_CTRL 0x04E4 + +#define VL53L1_TEST__BIST_RAM_RESULT 0x04E5 + +#define VL53L1_TEST__TMC 0x04E8 + +#define VL53L1_TEST__PLL_BIST_MIN_THRESHOLD 0x04F0 + +#define VL53L1_TEST__PLL_BIST_MIN_THRESHOLD_HI 0x04F0 + +#define VL53L1_TEST__PLL_BIST_MIN_THRESHOLD_LO 0x04F1 + +#define VL53L1_TEST__PLL_BIST_MAX_THRESHOLD 0x04F2 + +#define VL53L1_TEST__PLL_BIST_MAX_THRESHOLD_HI 0x04F2 + +#define VL53L1_TEST__PLL_BIST_MAX_THRESHOLD_LO 0x04F3 + +#define VL53L1_TEST__PLL_BIST_COUNT_OUT 0x04F4 + +#define VL53L1_TEST__PLL_BIST_COUNT_OUT_HI 0x04F4 + +#define VL53L1_TEST__PLL_BIST_COUNT_OUT_LO 0x04F5 + +#define VL53L1_TEST__PLL_BIST_GONOGO 0x04F6 + +#define VL53L1_TEST__PLL_BIST_CTRL 0x04F7 + +#define VL53L1_RANGING_CORE__DEVICE_ID 0x0680 + +#define VL53L1_RANGING_CORE__REVISION_ID 0x0681 + +#define VL53L1_RANGING_CORE__CLK_CTRL1 0x0683 + +#define VL53L1_RANGING_CORE__CLK_CTRL2 0x0684 + +#define VL53L1_RANGING_CORE__WOI_1 0x0685 + +#define VL53L1_RANGING_CORE__WOI_REF_1 0x0686 + +#define VL53L1_RANGING_CORE__START_RANGING 0x0687 + +#define VL53L1_RANGING_CORE__LOW_LIMIT_1 0x0690 + +#define VL53L1_RANGING_CORE__HIGH_LIMIT_1 0x0691 + +#define VL53L1_RANGING_CORE__LOW_LIMIT_REF_1 0x0692 + +#define VL53L1_RANGING_CORE__HIGH_LIMIT_REF_1 0x0693 + +#define VL53L1_RANGING_CORE__QUANTIFIER_1_MSB 0x0694 + +#define VL53L1_RANGING_CORE__QUANTIFIER_1_LSB 0x0695 + +#define VL53L1_RANGING_CORE__QUANTIFIER_REF_1_MSB 0x0696 + +#define VL53L1_RANGING_CORE__QUANTIFIER_REF_1_LSB 0x0697 + +#define VL53L1_RANGING_CORE__AMBIENT_OFFSET_1_MSB 0x0698 + +#define VL53L1_RANGING_CORE__AMBIENT_OFFSET_1_LSB 0x0699 + +#define VL53L1_RANGING_CORE__AMBIENT_OFFSET_REF_1_MSB 0x069A + +#define VL53L1_RANGING_CORE__AMBIENT_OFFSET_REF_1_LSB 0x069B + +#define VL53L1_RANGING_CORE__FILTER_STRENGTH_1 0x069C + +#define VL53L1_RANGING_CORE__FILTER_STRENGTH_REF_1 0x069D + +#define VL53L1_RANGING_CORE__SIGNAL_EVENT_LIMIT_1_MSB 0x069E + +#define VL53L1_RANGING_CORE__SIGNAL_EVENT_LIMIT_1_LSB 0x069F + +#define VL53L1_RANGING_CORE__SIGNAL_EVENT_LIMIT_REF_1_MSB 0x06A0 + +#define VL53L1_RANGING_CORE__SIGNAL_EVENT_LIMIT_REF_1_LSB 0x06A1 + +#define VL53L1_RANGING_CORE__TIMEOUT_OVERALL_PERIODS_MSB 0x06A4 + +#define VL53L1_RANGING_CORE__TIMEOUT_OVERALL_PERIODS_LSB 0x06A5 + +#define VL53L1_RANGING_CORE__INVERT_HW 0x06A6 + +#define VL53L1_RANGING_CORE__FORCE_HW 0x06A7 + +#define VL53L1_RANGING_CORE__STATIC_HW_VALUE 0x06A8 + +#define VL53L1_RANGING_CORE__FORCE_CONTINUOUS_AMBIENT 0x06A9 + +#define VL53L1_RANGING_CORE__TEST_PHASE_SELECT_TO_FILTER 0x06AA + +#define VL53L1_RANGING_CORE__TEST_PHASE_SELECT_TO_TIMING_GEN 0x06AB + +#define VL53L1_RANGING_CORE__INITIAL_PHASE_VALUE_1 0x06AC + +#define VL53L1_RANGING_CORE__INITIAL_PHASE_VALUE_REF_1 0x06AD + +#define VL53L1_RANGING_CORE__FORCE_UP_IN 0x06AE + +#define VL53L1_RANGING_CORE__FORCE_DN_IN 0x06AF + +#define VL53L1_RANGING_CORE__STATIC_UP_VALUE_1 0x06B0 + +#define VL53L1_RANGING_CORE__STATIC_UP_VALUE_REF_1 0x06B1 + +#define VL53L1_RANGING_CORE__STATIC_DN_VALUE_1 0x06B2 + +#define VL53L1_RANGING_CORE__STATIC_DN_VALUE_REF_1 0x06B3 + +#define VL53L1_RANGING_CORE__MONITOR_UP_DN 0x06B4 + +#define VL53L1_RANGING_CORE__INVERT_UP_DN 0x06B5 + +#define VL53L1_RANGING_CORE__CPUMP_1 0x06B6 + +#define VL53L1_RANGING_CORE__CPUMP_2 0x06B7 + +#define VL53L1_RANGING_CORE__CPUMP_3 0x06B8 + +#define VL53L1_RANGING_CORE__OSC_1 0x06B9 + +#define VL53L1_RANGING_CORE__PLL_1 0x06BB + +#define VL53L1_RANGING_CORE__PLL_2 0x06BC + +#define VL53L1_RANGING_CORE__REFERENCE_1 0x06BD + +#define VL53L1_RANGING_CORE__REFERENCE_3 0x06BF + +#define VL53L1_RANGING_CORE__REFERENCE_4 0x06C0 + +#define VL53L1_RANGING_CORE__REFERENCE_5 0x06C1 + +#define VL53L1_RANGING_CORE__REGAVDD1V2 0x06C3 + +#define VL53L1_RANGING_CORE__CALIB_1 0x06C4 + +#define VL53L1_RANGING_CORE__CALIB_2 0x06C5 + +#define VL53L1_RANGING_CORE__CALIB_3 0x06C6 + +#define VL53L1_RANGING_CORE__TST_MUX_SEL1 0x06C9 + +#define VL53L1_RANGING_CORE__TST_MUX_SEL2 0x06CA + +#define VL53L1_RANGING_CORE__TST_MUX 0x06CB + +#define VL53L1_RANGING_CORE__GPIO_OUT_TESTMUX 0x06CC + +#define VL53L1_RANGING_CORE__CUSTOM_FE 0x06CD + +#define VL53L1_RANGING_CORE__CUSTOM_FE_2 0x06CE + +#define VL53L1_RANGING_CORE__SPAD_READOUT 0x06CF + +#define VL53L1_RANGING_CORE__SPAD_READOUT_1 0x06D0 + +#define VL53L1_RANGING_CORE__SPAD_READOUT_2 0x06D1 + +#define VL53L1_RANGING_CORE__SPAD_PS 0x06D2 + +#define VL53L1_RANGING_CORE__LASER_SAFETY_2 0x06D4 + +#define VL53L1_RANGING_CORE__NVM_CTRL__MODE 0x0780 + +#define VL53L1_RANGING_CORE__NVM_CTRL__PDN 0x0781 + +#define VL53L1_RANGING_CORE__NVM_CTRL__PROGN 0x0782 + +#define VL53L1_RANGING_CORE__NVM_CTRL__READN 0x0783 + +#define VL53L1_RANGING_CORE__NVM_CTRL__PULSE_WIDTH_MSB 0x0784 + +#define VL53L1_RANGING_CORE__NVM_CTRL__PULSE_WIDTH_LSB 0x0785 + +#define VL53L1_RANGING_CORE__NVM_CTRL__HV_RISE_MSB 0x0786 + +#define VL53L1_RANGING_CORE__NVM_CTRL__HV_RISE_LSB 0x0787 + +#define VL53L1_RANGING_CORE__NVM_CTRL__HV_FALL_MSB 0x0788 + +#define VL53L1_RANGING_CORE__NVM_CTRL__HV_FALL_LSB 0x0789 + +#define VL53L1_RANGING_CORE__NVM_CTRL__TST 0x078A + +#define VL53L1_RANGING_CORE__NVM_CTRL__TESTREAD 0x078B + +#define VL53L1_RANGING_CORE__NVM_CTRL__DATAIN_MMM 0x078C + +#define VL53L1_RANGING_CORE__NVM_CTRL__DATAIN_LMM 0x078D + +#define VL53L1_RANGING_CORE__NVM_CTRL__DATAIN_LLM 0x078E + +#define VL53L1_RANGING_CORE__NVM_CTRL__DATAIN_LLL 0x078F + +#define VL53L1_RANGING_CORE__NVM_CTRL__DATAOUT_MMM 0x0790 + +#define VL53L1_RANGING_CORE__NVM_CTRL__DATAOUT_LMM 0x0791 + +#define VL53L1_RANGING_CORE__NVM_CTRL__DATAOUT_LLM 0x0792 + +#define VL53L1_RANGING_CORE__NVM_CTRL__DATAOUT_LLL 0x0793 + +#define VL53L1_RANGING_CORE__NVM_CTRL__ADDR 0x0794 + +#define VL53L1_RANGING_CORE__NVM_CTRL__DATAOUT_ECC 0x0795 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_0 0x0796 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_1 0x0797 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_2 0x0798 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_3 0x0799 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_4 0x079A + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_5 0x079B + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_6 0x079C + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_7 0x079D + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_8 0x079E + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_9 0x079F + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_10 0x07A0 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_11 0x07A1 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_12 0x07A2 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_13 0x07A3 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_14 0x07A4 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_15 0x07A5 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_16 0x07A6 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_17 0x07A7 + +#define VL53L1_RANGING_CORE__SPAD_SHIFT_EN 0x07BA + +#define VL53L1_RANGING_CORE__SPAD_DISABLE_CTRL 0x07BB + +#define VL53L1_RANGING_CORE__SPAD_EN_SHIFT_OUT_DEBUG 0x07BC + +#define VL53L1_RANGING_CORE__SPI_MODE 0x07BD + +#define VL53L1_RANGING_CORE__GPIO_DIR 0x07BE + +#define VL53L1_RANGING_CORE__VCSEL_PERIOD 0x0880 + +#define VL53L1_RANGING_CORE__VCSEL_START 0x0881 + +#define VL53L1_RANGING_CORE__VCSEL_STOP 0x0882 + +#define VL53L1_RANGING_CORE__VCSEL_1 0x0885 + +#define VL53L1_RANGING_CORE__VCSEL_STATUS 0x088D + +#define VL53L1_RANGING_CORE__STATUS 0x0980 + +#define VL53L1_RANGING_CORE__LASER_CONTINUITY_STATE 0x0981 + +#define VL53L1_RANGING_CORE__RANGE_1_MMM 0x0982 + +#define VL53L1_RANGING_CORE__RANGE_1_LMM 0x0983 + +#define VL53L1_RANGING_CORE__RANGE_1_LLM 0x0984 + +#define VL53L1_RANGING_CORE__RANGE_1_LLL 0x0985 + +#define VL53L1_RANGING_CORE__RANGE_REF_1_MMM 0x0986 + +#define VL53L1_RANGING_CORE__RANGE_REF_1_LMM 0x0987 + +#define VL53L1_RANGING_CORE__RANGE_REF_1_LLM 0x0988 + +#define VL53L1_RANGING_CORE__RANGE_REF_1_LLL 0x0989 + +#define VL53L1_RANGING_CORE__AMBIENT_WINDOW_EVENTS_1_MMM 0x098A + +#define VL53L1_RANGING_CORE__AMBIENT_WINDOW_EVENTS_1_LMM 0x098B + +#define VL53L1_RANGING_CORE__AMBIENT_WINDOW_EVENTS_1_LLM 0x098C + +#define VL53L1_RANGING_CORE__AMBIENT_WINDOW_EVENTS_1_LLL 0x098D + +#define VL53L1_RANGING_CORE__RANGING_TOTAL_EVENTS_1_MMM 0x098E + +#define VL53L1_RANGING_CORE__RANGING_TOTAL_EVENTS_1_LMM 0x098F + +#define VL53L1_RANGING_CORE__RANGING_TOTAL_EVENTS_1_LLM 0x0990 + +#define VL53L1_RANGING_CORE__RANGING_TOTAL_EVENTS_1_LLL 0x0991 + +#define VL53L1_RANGING_CORE__SIGNAL_TOTAL_EVENTS_1_MMM 0x0992 + +#define VL53L1_RANGING_CORE__SIGNAL_TOTAL_EVENTS_1_LMM 0x0993 + +#define VL53L1_RANGING_CORE__SIGNAL_TOTAL_EVENTS_1_LLM 0x0994 + +#define VL53L1_RANGING_CORE__SIGNAL_TOTAL_EVENTS_1_LLL 0x0995 + +#define VL53L1_RANGING_CORE__TOTAL_PERIODS_ELAPSED_1_MM 0x0996 + +#define VL53L1_RANGING_CORE__TOTAL_PERIODS_ELAPSED_1_LM 0x0997 + +#define VL53L1_RANGING_CORE__TOTAL_PERIODS_ELAPSED_1_LL 0x0998 + +#define VL53L1_RANGING_CORE__AMBIENT_MISMATCH_MM 0x0999 + +#define VL53L1_RANGING_CORE__AMBIENT_MISMATCH_LM 0x099A + +#define VL53L1_RANGING_CORE__AMBIENT_MISMATCH_LL 0x099B + +#define VL53L1_RANGING_CORE__AMBIENT_WINDOW_EVENTS_REF_1_MMM 0x099C + +#define VL53L1_RANGING_CORE__AMBIENT_WINDOW_EVENTS_REF_1_LMM 0x099D + +#define VL53L1_RANGING_CORE__AMBIENT_WINDOW_EVENTS_REF_1_LLM 0x099E + +#define VL53L1_RANGING_CORE__AMBIENT_WINDOW_EVENTS_REF_1_LLL 0x099F + +#define VL53L1_RANGING_CORE__RANGING_TOTAL_EVENTS_REF_1_MMM 0x09A0 + +#define VL53L1_RANGING_CORE__RANGING_TOTAL_EVENTS_REF_1_LMM 0x09A1 + +#define VL53L1_RANGING_CORE__RANGING_TOTAL_EVENTS_REF_1_LLM 0x09A2 + +#define VL53L1_RANGING_CORE__RANGING_TOTAL_EVENTS_REF_1_LLL 0x09A3 + +#define VL53L1_RANGING_CORE__SIGNAL_TOTAL_EVENTS_REF_1_MMM 0x09A4 + +#define VL53L1_RANGING_CORE__SIGNAL_TOTAL_EVENTS_REF_1_LMM 0x09A5 + +#define VL53L1_RANGING_CORE__SIGNAL_TOTAL_EVENTS_REF_1_LLM 0x09A6 + +#define VL53L1_RANGING_CORE__SIGNAL_TOTAL_EVENTS_REF_1_LLL 0x09A7 + +#define VL53L1_RANGING_CORE__TOTAL_PERIODS_ELAPSED_REF_1_MM 0x09A8 + +#define VL53L1_RANGING_CORE__TOTAL_PERIODS_ELAPSED_REF_1_LM 0x09A9 + +#define VL53L1_RANGING_CORE__TOTAL_PERIODS_ELAPSED_REF_1_LL 0x09AA + +#define VL53L1_RANGING_CORE__AMBIENT_MISMATCH_REF_MM 0x09AB + +#define VL53L1_RANGING_CORE__AMBIENT_MISMATCH_REF_LM 0x09AC + +#define VL53L1_RANGING_CORE__AMBIENT_MISMATCH_REF_LL 0x09AD + +#define VL53L1_RANGING_CORE__GPIO_CONFIG__A0 0x0A00 + +#define VL53L1_RANGING_CORE__RESET_CONTROL__A0 0x0A01 + +#define VL53L1_RANGING_CORE__INTR_MANAGER__A0 0x0A02 + +#define VL53L1_RANGING_CORE__POWER_FSM_TIME_OSC__A0 0x0A06 + +#define VL53L1_RANGING_CORE__VCSEL_ATEST__A0 0x0A07 + +#define VL53L1_RANGING_CORE__VCSEL_PERIOD_CLIPPED__A0 0x0A08 + +#define VL53L1_RANGING_CORE__VCSEL_STOP_CLIPPED__A0 0x0A09 + +#define VL53L1_RANGING_CORE__CALIB_2__A0 0x0A0A + +#define VL53L1_RANGING_CORE__STOP_CONDITION__A0 0x0A0B + +#define VL53L1_RANGING_CORE__STATUS_RESET__A0 0x0A0C + +#define VL53L1_RANGING_CORE__READOUT_CFG__A0 0x0A0D + +#define VL53L1_RANGING_CORE__WINDOW_SETTING__A0 0x0A0E + +#define VL53L1_RANGING_CORE__VCSEL_DELAY__A0 0x0A1A + +#define VL53L1_RANGING_CORE__REFERENCE_2__A0 0x0A1B + +#define VL53L1_RANGING_CORE__REGAVDD1V2__A0 0x0A1D + +#define VL53L1_RANGING_CORE__TST_MUX__A0 0x0A1F + +#define VL53L1_RANGING_CORE__CUSTOM_FE_2__A0 0x0A20 + +#define VL53L1_RANGING_CORE__SPAD_READOUT__A0 0x0A21 + +#define VL53L1_RANGING_CORE__CPUMP_1__A0 0x0A22 + +#define VL53L1_RANGING_CORE__SPARE_REGISTER__A0 0x0A23 + +#define VL53L1_RANGING_CORE__VCSEL_CONT_STAGE5_BYPASS__A0 0x0A24 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_18 0x0A25 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_19 0x0A26 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_20 0x0A27 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_21 0x0A28 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_22 0x0A29 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_23 0x0A2A + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_24 0x0A2B + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_25 0x0A2C + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_26 0x0A2D + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_27 0x0A2E + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_28 0x0A2F + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_29 0x0A30 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_30 0x0A31 + +#define VL53L1_RANGING_CORE__RET_SPAD_EN_31 0x0A32 + +#define VL53L1_RANGING_CORE__REF_SPAD_EN_0__EWOK 0x0A33 + +#define VL53L1_RANGING_CORE__REF_SPAD_EN_1__EWOK 0x0A34 + +#define VL53L1_RANGING_CORE__REF_SPAD_EN_2__EWOK 0x0A35 + +#define VL53L1_RANGING_CORE__REF_SPAD_EN_3__EWOK 0x0A36 + +#define VL53L1_RANGING_CORE__REF_SPAD_EN_4__EWOK 0x0A37 + +#define VL53L1_RANGING_CORE__REF_SPAD_EN_5__EWOK 0x0A38 + +#define VL53L1_RANGING_CORE__REF_EN_START_SELECT 0x0A39 + +#define VL53L1_RANGING_CORE__REGDVDD1V2_ATEST__EWOK 0x0A41 + +#define VL53L1_SOFT_RESET_GO1 0x0B00 + +#define VL53L1_PRIVATE__PATCH_BASE_ADDR_RSLV 0x0E00 + +#define VL53L1_PREV_SHADOW_RESULT__INTERRUPT_STATUS 0x0ED0 + +#define VL53L1_PREV_SHADOW_RESULT__RANGE_STATUS 0x0ED1 + +#define VL53L1_PREV_SHADOW_RESULT__REPORT_STATUS 0x0ED2 + +#define VL53L1_PREV_SHADOW_RESULT__STREAM_COUNT 0x0ED3 + +#define VL53L1_PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0 0x0ED4 + +#define VL53L1_PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_HI 0x0ED4 + +#define VL53L1_PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_LO 0x0ED5 + +#define VL53L1_PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0 0x0ED6 + +#define VL53L1_PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_HI 0x0ED6 + +#define VL53L1_PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_LO 0x0ED7 + +#define VL53L1_PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0 0x0ED8 + +#define VL53L1_PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_HI 0x0ED8 + +#define VL53L1_PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_LO 0x0ED9 + +#define VL53L1_PREV_SHADOW_RESULT__SIGMA_SD0 0x0EDA + +#define VL53L1_PREV_SHADOW_RESULT__SIGMA_SD0_HI 0x0EDA + +#define VL53L1_PREV_SHADOW_RESULT__SIGMA_SD0_LO 0x0EDB + +#define VL53L1_PREV_SHADOW_RESULT__PHASE_SD0 0x0EDC + +#define VL53L1_PREV_SHADOW_RESULT__PHASE_SD0_HI 0x0EDC + +#define VL53L1_PREV_SHADOW_RESULT__PHASE_SD0_LO 0x0EDD + +#define VL53L1_PREV_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 0x0EDE + +#define VL53L1_PREV__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_HI 0x0EDE + +#define VL53L1_PREV__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_LO 0x0EDF + +#define VL53L1_PREV__PEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0 0x0EE0 + +#define VL53L1_PPEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_HI 0x0EE0 + +#define VL53L1_PPEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_LO 0x0EE1 + +#define VL53L1_PREV_SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0 0x0EE2 + +#define VL53L1_PREV_SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_HI 0x0EE2 + +#define VL53L1_PREV_SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_LO 0x0EE3 + +#define VL53L1_PREV_SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0 0x0EE4 + +#define VL53L1_PREV_SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_HI 0x0EE4 + +#define VL53L1_PREV_SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_LO 0x0EE5 + +#define VL53L1_PREV_SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0 0x0EE6 + +#define VL53L1_PREV_SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_HI 0x0EE6 + +#define VL53L1_PREV_SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_LO 0x0EE7 + +#define VL53L1_PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1 0x0EE8 + +#define VL53L1_PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_HI 0x0EE8 + +#define VL53L1_PREV_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_LO 0x0EE9 + +#define VL53L1_PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1 0x0EEA + +#define VL53L1_PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_HI 0x0EEA + +#define VL53L1_PREV_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_LO 0x0EEB + +#define VL53L1_PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1 0x0EEC + +#define VL53L1_PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_HI 0x0EEC + +#define VL53L1_PREV_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_LO 0x0EED + +#define VL53L1_PREV_SHADOW_RESULT__SIGMA_SD1 0x0EEE + +#define VL53L1_PREV_SHADOW_RESULT__SIGMA_SD1_HI 0x0EEE + +#define VL53L1_PREV_SHADOW_RESULT__SIGMA_SD1_LO 0x0EEF + +#define VL53L1_PREV_SHADOW_RESULT__PHASE_SD1 0x0EF0 + +#define VL53L1_PREV_SHADOW_RESULT__PHASE_SD1_HI 0x0EF0 + +#define VL53L1_PREV_SHADOW_RESULT__PHASE_SD1_LO 0x0EF1 + +#define VL53L1_PREV_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1 0x0EF2 + +#define VL53L1_PFINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_HI 0x0EF2 + +#define VL53L1_PFINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_LO 0x0EF3 + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_0_SD1 0x0EF4 + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_0_SD1_HI 0x0EF4 + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_0_SD1_LO 0x0EF5 + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_1_SD1 0x0EF6 + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_1_SD1_HI 0x0EF6 + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_1_SD1_LO 0x0EF7 + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_2_SD1 0x0EF8 + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_2_SD1_HI 0x0EF8 + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_2_SD1_LO 0x0EF9 + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_3_SD1 0x0EFA + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_3_SD1_HI 0x0EFA + +#define VL53L1_PREV_SHADOW_RESULT__SPARE_3_SD1_LO 0x0EFB + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0 0x0EFC + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_3 0x0EFC + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_2 0x0EFD + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_1 0x0EFE + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_0 0x0EFF + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0 0x0F00 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_3 0x0F00 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_2 0x0F01 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_1 0x0F02 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_0 0x0F03 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0 0x0F04 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_3 0x0F04 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_2 0x0F05 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_1 0x0F06 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_0 0x0F07 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0 0x0F08 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_3 0x0F08 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_2 0x0F09 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_1 0x0F0A + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_0 0x0F0B + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1 0x0F0C + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_3 0x0F0C + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_2 0x0F0D + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_1 0x0F0E + +#define VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_0 0x0F0F + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1 0x0F10 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_3 0x0F10 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_2 0x0F11 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_1 0x0F12 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_0 0x0F13 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1 0x0F14 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_3 0x0F14 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_2 0x0F15 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_1 0x0F16 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_0 0x0F17 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1 0x0F18 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_3 0x0F18 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_2 0x0F19 + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_1 0x0F1A + +#define VL53L1_PREV_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_0 0x0F1B + +#define VL53L1_PREV_SHADOW_RESULT_CORE__SPARE_0 0x0F1C + +#define VL53L1_RESULT__DEBUG_STATUS 0x0F20 + +#define VL53L1_RESULT__DEBUG_STAGE 0x0F21 + +#define VL53L1_GPH__SYSTEM__THRESH_RATE_HIGH 0x0F24 + +#define VL53L1_GPH__SYSTEM__THRESH_RATE_HIGH_HI 0x0F24 + +#define VL53L1_GPH__SYSTEM__THRESH_RATE_HIGH_LO 0x0F25 + +#define VL53L1_GPH__SYSTEM__THRESH_RATE_LOW 0x0F26 + +#define VL53L1_GPH__SYSTEM__THRESH_RATE_LOW_HI 0x0F26 + +#define VL53L1_GPH__SYSTEM__THRESH_RATE_LOW_LO 0x0F27 + +#define VL53L1_GPH__SYSTEM__INTERRUPT_CONFIG_GPIO 0x0F28 + +#define VL53L1_GPH__DSS_CONFIG__ROI_MODE_CONTROL 0x0F2F + +#define VL53L1_GPH__DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT 0x0F30 + +#define VL53L1_GPH__DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT_HI 0x0F30 + +#define VL53L1_GPH__DSS_CONFIG__MANUAL_EFFECTIVE_SPADS_SELECT_LO 0x0F31 + +#define VL53L1_GPH__DSS_CONFIG__MANUAL_BLOCK_SELECT 0x0F32 + +#define VL53L1_GPH__DSS_CONFIG__MAX_SPADS_LIMIT 0x0F33 + +#define VL53L1_GPH__DSS_CONFIG__MIN_SPADS_LIMIT 0x0F34 + +#define VL53L1_GPH__MM_CONFIG__TIMEOUT_MACROP_A_HI 0x0F36 + +#define VL53L1_GPH__MM_CONFIG__TIMEOUT_MACROP_A_LO 0x0F37 + +#define VL53L1_GPH__MM_CONFIG__TIMEOUT_MACROP_B_HI 0x0F38 + +#define VL53L1_GPH__MM_CONFIG__TIMEOUT_MACROP_B_LO 0x0F39 + +#define VL53L1_GPH__RANGE_CONFIG__TIMEOUT_MACROP_A_HI 0x0F3A + +#define VL53L1_GPH__RANGE_CONFIG__TIMEOUT_MACROP_A_LO 0x0F3B + +#define VL53L1_GPH__RANGE_CONFIG__VCSEL_PERIOD_A 0x0F3C + +#define VL53L1_GPH__RANGE_CONFIG__VCSEL_PERIOD_B 0x0F3D + +#define VL53L1_GPH__RANGE_CONFIG__TIMEOUT_MACROP_B_HI 0x0F3E + +#define VL53L1_GPH__RANGE_CONFIG__TIMEOUT_MACROP_B_LO 0x0F3F + +#define VL53L1_GPH__RANGE_CONFIG__SIGMA_THRESH 0x0F40 + +#define VL53L1_GPH__RANGE_CONFIG__SIGMA_THRESH_HI 0x0F40 + +#define VL53L1_GPH__RANGE_CONFIG__SIGMA_THRESH_LO 0x0F41 + +#define VL53L1_GPH__RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS 0x0F42 + +#define VL53L1_GPH__RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS_HI 0x0F42 + +#define VL53L1_GPH__RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS_LO 0x0F43 + +#define VL53L1_GPH__RANGE_CONFIG__VALID_PHASE_LOW 0x0F44 + +#define VL53L1_GPH__RANGE_CONFIG__VALID_PHASE_HIGH 0x0F45 + +#define VL53L1_FIRMWARE__INTERNAL_STREAM_COUNT_DIV 0x0F46 + +#define VL53L1_FIRMWARE__INTERNAL_STREAM_COUNTER_VAL 0x0F47 + +#define VL53L1_DSS_CALC__ROI_CTRL 0x0F54 + +#define VL53L1_DSS_CALC__SPARE_1 0x0F55 + +#define VL53L1_DSS_CALC__SPARE_2 0x0F56 + +#define VL53L1_DSS_CALC__SPARE_3 0x0F57 + +#define VL53L1_DSS_CALC__SPARE_4 0x0F58 + +#define VL53L1_DSS_CALC__SPARE_5 0x0F59 + +#define VL53L1_DSS_CALC__SPARE_6 0x0F5A + +#define VL53L1_DSS_CALC__SPARE_7 0x0F5B + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_0 0x0F5C + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_1 0x0F5D + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_2 0x0F5E + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_3 0x0F5F + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_4 0x0F60 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_5 0x0F61 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_6 0x0F62 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_7 0x0F63 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_8 0x0F64 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_9 0x0F65 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_10 0x0F66 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_11 0x0F67 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_12 0x0F68 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_13 0x0F69 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_14 0x0F6A + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_15 0x0F6B + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_16 0x0F6C + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_17 0x0F6D + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_18 0x0F6E + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_19 0x0F6F + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_20 0x0F70 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_21 0x0F71 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_22 0x0F72 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_23 0x0F73 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_24 0x0F74 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_25 0x0F75 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_26 0x0F76 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_27 0x0F77 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_28 0x0F78 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_29 0x0F79 + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_30 0x0F7A + +#define VL53L1_DSS_CALC__USER_ROI_SPAD_EN_31 0x0F7B + +#define VL53L1_DSS_CALC__USER_ROI_0 0x0F7C + +#define VL53L1_DSS_CALC__USER_ROI_1 0x0F7D + +#define VL53L1_DSS_CALC__MODE_ROI_0 0x0F7E + +#define VL53L1_DSS_CALC__MODE_ROI_1 0x0F7F + +#define VL53L1_SIGMA_ESTIMATOR_CALC__SPARE_0 0x0F80 + +#define VL53L1_VHV_RESULT__PEAK_SIGNAL_RATE_MCPS 0x0F82 + +#define VL53L1_VHV_RESULT__PEAK_SIGNAL_RATE_MCPS_HI 0x0F82 + +#define VL53L1_VHV_RESULT__PEAK_SIGNAL_RATE_MCPS_LO 0x0F83 + +#define VL53L1_VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF 0x0F84 + +#define VL53L1_VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF_3 0x0F84 + +#define VL53L1_VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF_2 0x0F85 + +#define VL53L1_VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF_1 0x0F86 + +#define VL53L1_VHV_RESULT__SIGNAL_TOTAL_EVENTS_REF_0 0x0F87 + +#define VL53L1_PHASECAL_RESULT__PHASE_OUTPUT_REF 0x0F88 + +#define VL53L1_PHASECAL_RESULT__PHASE_OUTPUT_REF_HI 0x0F88 + +#define VL53L1_PHASECAL_RESULT__PHASE_OUTPUT_REF_LO 0x0F89 + +#define VL53L1_DSS_RESULT__TOTAL_RATE_PER_SPAD 0x0F8A + +#define VL53L1_DSS_RESULT__TOTAL_RATE_PER_SPAD_HI 0x0F8A + +#define VL53L1_DSS_RESULT__TOTAL_RATE_PER_SPAD_LO 0x0F8B + +#define VL53L1_DSS_RESULT__ENABLED_BLOCKS 0x0F8C + +#define VL53L1_DSS_RESULT__NUM_REQUESTED_SPADS 0x0F8E + +#define VL53L1_DSS_RESULT__NUM_REQUESTED_SPADS_HI 0x0F8E + +#define VL53L1_DSS_RESULT__NUM_REQUESTED_SPADS_LO 0x0F8F + +#define VL53L1_MM_RESULT__INNER_INTERSECTION_RATE 0x0F92 + +#define VL53L1_MM_RESULT__INNER_INTERSECTION_RATE_HI 0x0F92 + +#define VL53L1_MM_RESULT__INNER_INTERSECTION_RATE_LO 0x0F93 + +#define VL53L1_MM_RESULT__OUTER_COMPLEMENT_RATE 0x0F94 + +#define VL53L1_MM_RESULT__OUTER_COMPLEMENT_RATE_HI 0x0F94 + +#define VL53L1_MM_RESULT__OUTER_COMPLEMENT_RATE_LO 0x0F95 + +#define VL53L1_MM_RESULT__TOTAL_OFFSET 0x0F96 + +#define VL53L1_MM_RESULT__TOTAL_OFFSET_HI 0x0F96 + +#define VL53L1_MM_RESULT__TOTAL_OFFSET_LO 0x0F97 + +#define VL53L1_XTALK_CALC__XTALK_FOR_ENABLED_SPADS 0x0F98 + +#define VL53L1_XTALK_CALC__XTALK_FOR_ENABLED_SPADS_3 0x0F98 + +#define VL53L1_XTALK_CALC__XTALK_FOR_ENABLED_SPADS_2 0x0F99 + +#define VL53L1_XTALK_CALC__XTALK_FOR_ENABLED_SPADS_1 0x0F9A + +#define VL53L1_XTALK_CALC__XTALK_FOR_ENABLED_SPADS_0 0x0F9B + +#define VL53L1_XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS 0x0F9C + +#define VL53L1_XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS_3 0x0F9C + +#define VL53L1_XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS_2 0x0F9D + +#define VL53L1_XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS_1 0x0F9E + +#define VL53L1_XTALK_RESULT__AVG_XTALK_USER_ROI_KCPS_0 0x0F9F + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS 0x0FA0 + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS_3 0x0FA0 + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS_2 0x0FA1 + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS_1 0x0FA2 + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_INNER_ROI_KCPS_0 0x0FA3 + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS 0x0FA4 + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS_3 0x0FA4 + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS_2 0x0FA5 + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS_1 0x0FA6 + +#define VL53L1_XTALK_RESULT__AVG_XTALK_MM_OUTER_ROI_KCPS_0 0x0FA7 + +#define VL53L1_RANGE_RESULT__ACCUM_PHASE 0x0FA8 + +#define VL53L1_RANGE_RESULT__ACCUM_PHASE_3 0x0FA8 + +#define VL53L1_RANGE_RESULT__ACCUM_PHASE_2 0x0FA9 + +#define VL53L1_RANGE_RESULT__ACCUM_PHASE_1 0x0FAA + +#define VL53L1_RANGE_RESULT__ACCUM_PHASE_0 0x0FAB + +#define VL53L1_RANGE_RESULT__OFFSET_CORRECTED_RANGE 0x0FAC + +#define VL53L1_RANGE_RESULT__OFFSET_CORRECTED_RANGE_HI 0x0FAC + +#define VL53L1_RANGE_RESULT__OFFSET_CORRECTED_RANGE_LO 0x0FAD + +#define VL53L1_SHADOW_PHASECAL_RESULT__VCSEL_START 0x0FAE + +#define VL53L1_SHADOW_RESULT__INTERRUPT_STATUS 0x0FB0 + +#define VL53L1_SHADOW_RESULT__RANGE_STATUS 0x0FB1 + +#define VL53L1_SHADOW_RESULT__REPORT_STATUS 0x0FB2 + +#define VL53L1_SHADOW_RESULT__STREAM_COUNT 0x0FB3 + +#define VL53L1_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0 0x0FB4 + +#define VL53L1_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_HI 0x0FB4 + +#define VL53L1_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD0_LO 0x0FB5 + +#define VL53L1_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0 0x0FB6 + +#define VL53L1_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_HI 0x0FB6 + +#define VL53L1_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD0_LO 0x0FB7 + +#define VL53L1_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0 0x0FB8 + +#define VL53L1_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_HI 0x0FB8 + +#define VL53L1_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD0_LO 0x0FB9 + +#define VL53L1_SHADOW_RESULT__SIGMA_SD0 0x0FBA + +#define VL53L1_SHADOW_RESULT__SIGMA_SD0_HI 0x0FBA + +#define VL53L1_SHADOW_RESULT__SIGMA_SD0_LO 0x0FBB + +#define VL53L1_SHADOW_RESULT__PHASE_SD0 0x0FBC + +#define VL53L1_SHADOW_RESULT__PHASE_SD0_HI 0x0FBC + +#define VL53L1_SHADOW_RESULT__PHASE_SD0_LO 0x0FBD + +#define VL53L1_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0 0x0FBE + +#define VL53L1_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_HI 0x0FBE + +#define VL53L1_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD0_LO 0x0FBF + +#define VL53L1_SHPEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0 0x0FC0 + +#define VL53L1_SHPEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_HI 0x0FC0 + +#define VL53L1_SHPEAK_SIGNAL_COUNT_RATE_CROSSTALK_CORRECTED_MCPS_SD0_LO 0x0FC1 + +#define VL53L1_SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0 0x0FC2 + +#define VL53L1_SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_HI 0x0FC2 + +#define VL53L1_SHADOW_RESULT__MM_INNER_ACTUAL_EFFECTIVE_SPADS_SD0_LO 0x0FC3 + +#define VL53L1_SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0 0x0FC4 + +#define VL53L1_SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_HI 0x0FC4 + +#define VL53L1_SHADOW_RESULT__MM_OUTER_ACTUAL_EFFECTIVE_SPADS_SD0_LO 0x0FC5 + +#define VL53L1_SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0 0x0FC6 + +#define VL53L1_SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_HI 0x0FC6 + +#define VL53L1_SHADOW_RESULT__AVG_SIGNAL_COUNT_RATE_MCPS_SD0_LO 0x0FC7 + +#define VL53L1_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1 0x0FC8 + +#define VL53L1_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_HI 0x0FC8 + +#define VL53L1_SHADOW_RESULT__DSS_ACTUAL_EFFECTIVE_SPADS_SD1_LO 0x0FC9 + +#define VL53L1_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1 0x0FCA + +#define VL53L1_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_HI 0x0FCA + +#define VL53L1_SHADOW_RESULT__PEAK_SIGNAL_COUNT_RATE_MCPS_SD1_LO 0x0FCB + +#define VL53L1_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1 0x0FCC + +#define VL53L1_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_HI 0x0FCC + +#define VL53L1_SHADOW_RESULT__AMBIENT_COUNT_RATE_MCPS_SD1_LO 0x0FCD + +#define VL53L1_SHADOW_RESULT__SIGMA_SD1 0x0FCE + +#define VL53L1_SHADOW_RESULT__SIGMA_SD1_HI 0x0FCE + +#define VL53L1_SHADOW_RESULT__SIGMA_SD1_LO 0x0FCF + +#define VL53L1_SHADOW_RESULT__PHASE_SD1 0x0FD0 + +#define VL53L1_SHADOW_RESULT__PHASE_SD1_HI 0x0FD0 + +#define VL53L1_SHADOW_RESULT__PHASE_SD1_LO 0x0FD1 + +#define VL53L1_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1 0x0FD2 + +#define VL53L1_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_HI 0x0FD2 + +#define VL53L1_SHADOW_RESULT__FINAL_CROSSTALK_CORRECTED_RANGE_MM_SD1_LO 0x0FD3 + +#define VL53L1_SHADOW_RESULT__SPARE_0_SD1 0x0FD4 + +#define VL53L1_SHADOW_RESULT__SPARE_0_SD1_HI 0x0FD4 + +#define VL53L1_SHADOW_RESULT__SPARE_0_SD1_LO 0x0FD5 + +#define VL53L1_SHADOW_RESULT__SPARE_1_SD1 0x0FD6 + +#define VL53L1_SHADOW_RESULT__SPARE_1_SD1_HI 0x0FD6 + +#define VL53L1_SHADOW_RESULT__SPARE_1_SD1_LO 0x0FD7 + +#define VL53L1_SHADOW_RESULT__SPARE_2_SD1 0x0FD8 + +#define VL53L1_SHADOW_RESULT__SPARE_2_SD1_HI 0x0FD8 + +#define VL53L1_SHADOW_RESULT__SPARE_2_SD1_LO 0x0FD9 + +#define VL53L1_SHADOW_RESULT__SPARE_3_SD1 0x0FDA + +#define VL53L1_SHADOW_RESULT__THRESH_INFO 0x0FDB + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0 0x0FDC + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_3 0x0FDC + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_2 0x0FDD + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_1 0x0FDE + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0_0 0x0FDF + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0 0x0FE0 + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_3 0x0FE0 + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_2 0x0FE1 + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_1 0x0FE2 + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD0_0 0x0FE3 + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0 0x0FE4 + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_3 0x0FE4 + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_2 0x0FE5 + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_1 0x0FE6 + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD0_0 0x0FE7 + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0 0x0FE8 + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_3 0x0FE8 + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_2 0x0FE9 + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_1 0x0FEA + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD0_0 0x0FEB + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1 0x0FEC + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_3 0x0FEC + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_2 0x0FED + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_1 0x0FEE + +#define VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD1_0 0x0FEF + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1 0x0FF0 + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_3 0x0FF0 + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_2 0x0FF1 + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_1 0x0FF2 + +#define VL53L1_SHADOW_RESULT_CORE__RANGING_TOTAL_EVENTS_SD1_0 0x0FF3 + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1 0x0FF4 + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_3 0x0FF4 + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_2 0x0FF5 + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_1 0x0FF6 + +#define VL53L1_SHADOW_RESULT_CORE__SIGNAL_TOTAL_EVENTS_SD1_0 0x0FF7 + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1 0x0FF8 + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_3 0x0FF8 + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_2 0x0FF9 + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_1 0x0FFA + +#define VL53L1_SHADOW_RESULT_CORE__TOTAL_PERIODS_ELAPSED_SD1_0 0x0FFB + +#define VL53L1_SHADOW_RESULT_CORE__SPARE_0 0x0FFC + +#define VL53L1_SHADOW_PHASECAL_RESULT__REFERENCE_PHASE_HI 0x0FFE + +#define VL53L1_SHADOW_PHASECAL_RESULT__REFERENCE_PHASE_LO 0x0FFF + + + + + +#endif + + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_register_settings.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_register_settings.h new file mode 100644 index 000000000000..856777bd93e5 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_register_settings.h @@ -0,0 +1,130 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#ifndef _VL53L1_REGISTER_SETTINGS_H_ +#define _VL53L1_REGISTER_SETTINGS_H_ + + + + + + +#define VL53L1_DEVICESCHEDULERMODE_PSEUDO_SOLO 0x00 +#define VL53L1_DEVICESCHEDULERMODE_STREAMING 0x01 +#define VL53L1_DEVICESCHEDULERMODE_HISTOGRAM 0x02 + + + + + +#define VL53L1_DEVICEREADOUTMODE_SINGLE_SD (0x00 << 2) +#define VL53L1_DEVICEREADOUTMODE_DUAL_SD (0x01 << 2) +#define VL53L1_DEVICEREADOUTMODE_SPLIT_READOUT (0x02 << 2) +#define VL53L1_DEVICEREADOUTMODE_SPLIT_MANUAL (0x03 << 2) + + + + + + +#define VL53L1_DEVICEMEASUREMENTMODE_MODE_MASK 0xF0 +#define VL53L1_DEVICEMEASUREMENTMODE_STOP_MASK 0x0F + +#define VL53L1_GROUPEDPARAMETERHOLD_ID_MASK 0x02 + + + +#define VL53L1_EWOK_I2C_DEV_ADDR_DEFAULT 0x29 + +#define VL53L1_OSC_FREQUENCY 0x00 +#define VL53L1_OSC_TRIM_DEFAULT 0x00 +#define VL53L1_OSC_FREQ_SET_DEFAULT 0x00 + +#define VL53L1_RANGE_HISTOGRAM_REF 0x08 +#define VL53L1_RANGE_HISTOGRAM_RET 0x10 +#define VL53L1_RANGE_HISTOGRAM_BOTH 0x18 +#define VL53L1_RANGE_HISTOGRAM_INIT 0x20 +#define VL53L1_RANGE_VHV_INIT 0x40 + + +#define VL53L1_RESULT_RANGE_STATUS 0x1F + + +#define VL53L1_SYSTEM__SEED_CONFIG__MANUAL 0x00 +#define VL53L1_SYSTEM__SEED_CONFIG__STANDARD 0x01 +#define VL53L1_SYSTEM__SEED_CONFIG__EVEN_UPDATE_ONLY 0x02 + + +#define VL53L1_INTERRUPT_CONFIG_LEVEL_LOW 0x00 +#define VL53L1_INTERRUPT_CONFIG_LEVEL_HIGH 0x01 +#define VL53L1_INTERRUPT_CONFIG_OUT_OF_WINDOW 0x02 +#define VL53L1_INTERRUPT_CONFIG_IN_WINDOW 0x03 +#define VL53L1_INTERRUPT_CONFIG_NEW_SAMPLE_READY 0x20 + + +#define VL53L1_CLEAR_RANGE_INT 0x01 +#define VL53L1_CLEAR_ERROR_INT 0x02 + + +#define VL53L1_SEQUENCE_VHV_EN 0x01 +#define VL53L1_SEQUENCE_PHASECAL_EN 0x02 +#define VL53L1_SEQUENCE_REFERENCE_PHASE_EN 0x04 +#define VL53L1_SEQUENCE_DSS1_EN 0x08 +#define VL53L1_SEQUENCE_DSS2_EN 0x10 +#define VL53L1_SEQUENCE_MM1_EN 0x20 +#define VL53L1_SEQUENCE_MM2_EN 0x40 +#define VL53L1_SEQUENCE_RANGE_EN 0x80 + + +#define VL53L1_DSS_CONTROL__ROI_SUBTRACT 0x20 +#define VL53L1_DSS_CONTROL__ROI_INTERSECT 0x10 + +#define VL53L1_DSS_CONTROL__MODE_DISABLED 0x00 +#define VL53L1_DSS_CONTROL__MODE_TARGET_RATE 0x01 +#define VL53L1_DSS_CONTROL__MODE_EFFSPADS 0x02 +#define VL53L1_DSS_CONTROL__MODE_BLOCKSELECT 0x03 + + + +#define VL53L1_RANGING_CORE__SPAD_READOUT__STANDARD 0x45 +#define VL53L1_RANGING_CORE__SPAD_READOUT__RETURN_ARRAY_ONLY 0x05 +#define VL53L1_RANGING_CORE__SPAD_READOUT__REFERENCE_ARRAY_ONLY 0x55 +#define VL53L1_RANGING_CORE__SPAD_READOUT__RETURN_SPLIT_ARRAY 0x25 +#define VL53L1_RANGING_CORE__SPAD_READOUT__CALIB_PULSES 0xF5 + + +#define VL53L1_LASER_SAFETY__KEY_VALUE 0x6C + + + +#define VL53L1_RANGE_STATUS__RANGE_STATUS_MASK 0x1F +#define VL53L1_RANGE_STATUS__MAX_THRESHOLD_HIT_MASK 0x20 +#define VL53L1_RANGE_STATUS__MIN_THRESHOLD_HIT_MASK 0x40 +#define VL53L1_RANGE_STATUS__GPH_ID_RANGE_STATUS_MASK 0x80 + + + +#define VL53L1_INTERRUPT_STATUS__INT_STATUS_MASK 0x07 +#define VL53L1_INTERRUPT_STATUS__INT_ERROR_STATUS_MASK 0x18 +#define VL53L1_INTERRUPT_STATUS__GPH_ID_INT_STATUS_MASK 0x20 + + + + +#endif + + + + + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_register_structs.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_register_structs.h new file mode 100644 index 000000000000..1a08dc7f6652 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_register_structs.h @@ -0,0 +1,999 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#ifndef _VL53L1_REGISTER_STRUCTS_H_ +#define _VL53L1_REGISTER_STRUCTS_H_ + +#include "vl53l1_types.h" +#include "vl53l1_register_map.h" + +#define VL53L1_STATIC_NVM_MANAGED_I2C_INDEX \ + VL53L1_I2C_SLAVE__DEVICE_ADDRESS +#define VL53L1_CUSTOMER_NVM_MANAGED_I2C_INDEX \ + VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_0 +#define VL53L1_STATIC_CONFIG_I2C_INDEX \ + VL53L1_DSS_CONFIG__TARGET_TOTAL_RATE_MCPS +#define VL53L1_GENERAL_CONFIG_I2C_INDEX \ + VL53L1_GPH_CONFIG__STREAM_COUNT_UPDATE_VALUE +#define VL53L1_TIMING_CONFIG_I2C_INDEX \ + VL53L1_MM_CONFIG__TIMEOUT_MACROP_A_HI +#define VL53L1_DYNAMIC_CONFIG_I2C_INDEX \ + VL53L1_SYSTEM__GROUPED_PARAMETER_HOLD_0 +#define VL53L1_SYSTEM_CONTROL_I2C_INDEX \ + VL53L1_POWER_MANAGEMENT__GO1_POWER_FORCE +#define VL53L1_SYSTEM_RESULTS_I2C_INDEX \ + VL53L1_RESULT__INTERRUPT_STATUS +#define VL53L1_CORE_RESULTS_I2C_INDEX \ + VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0 +#define VL53L1_DEBUG_RESULTS_I2C_INDEX \ + VL53L1_PHASECAL_RESULT__REFERENCE_PHASE +#define VL53L1_NVM_COPY_DATA_I2C_INDEX \ + VL53L1_IDENTIFICATION__MODEL_ID +#define VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_INDEX \ + VL53L1_PREV_SHADOW_RESULT__INTERRUPT_STATUS +#define VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_INDEX \ + VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0 +#define VL53L1_PATCH_DEBUG_I2C_INDEX \ + VL53L1_RESULT__DEBUG_STATUS +#define VL53L1_GPH_GENERAL_CONFIG_I2C_INDEX \ + VL53L1_GPH__SYSTEM__THRESH_RATE_HIGH +#define VL53L1_GPH_STATIC_CONFIG_I2C_INDEX \ + VL53L1_GPH__DSS_CONFIG__ROI_MODE_CONTROL +#define VL53L1_GPH_TIMING_CONFIG_I2C_INDEX \ + VL53L1_GPH__MM_CONFIG__TIMEOUT_MACROP_A_HI +#define VL53L1_FW_INTERNAL_I2C_INDEX \ + VL53L1_FIRMWARE__INTERNAL_STREAM_COUNT_DIV +#define VL53L1_PATCH_RESULTS_I2C_INDEX \ + VL53L1_DSS_CALC__ROI_CTRL +#define VL53L1_SHADOW_SYSTEM_RESULTS_I2C_INDEX \ + VL53L1_SHADOW_PHASECAL_RESULT__VCSEL_START +#define VL53L1_SHADOW_CORE_RESULTS_I2C_INDEX \ + VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0 + +#define VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES 11 +#define VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES 23 +#define VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES 32 +#define VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES 22 +#define VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES 23 +#define VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES 18 +#define VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES 5 +#define VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES 44 +#define VL53L1_CORE_RESULTS_I2C_SIZE_BYTES 33 +#define VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES 56 +#define VL53L1_NVM_COPY_DATA_I2C_SIZE_BYTES 49 +#define VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES 44 +#define VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES 33 +#define VL53L1_PATCH_DEBUG_I2C_SIZE_BYTES 2 +#define VL53L1_GPH_GENERAL_CONFIG_I2C_SIZE_BYTES 5 +#define VL53L1_GPH_STATIC_CONFIG_I2C_SIZE_BYTES 6 +#define VL53L1_GPH_TIMING_CONFIG_I2C_SIZE_BYTES 16 +#define VL53L1_FW_INTERNAL_I2C_SIZE_BYTES 2 +#define VL53L1_PATCH_RESULTS_I2C_SIZE_BYTES 90 +#define VL53L1_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES 82 +#define VL53L1_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES 33 + + + + +typedef struct { + uint8_t i2c_slave__device_address; + + uint8_t ana_config__vhv_ref_sel_vddpix; + + uint8_t ana_config__vhv_ref_sel_vquench; + + uint8_t ana_config__reg_avdd1v2_sel; + + uint8_t ana_config__fast_osc__trim; + + uint16_t osc_measured__fast_osc__frequency; + + uint8_t vhv_config__timeout_macrop_loop_bound; + + uint8_t vhv_config__count_thresh; + + uint8_t vhv_config__offset; + + uint8_t vhv_config__init; + +} VL53L1_static_nvm_managed_t; + + + + +typedef struct { + uint8_t global_config__spad_enables_ref_0; + + uint8_t global_config__spad_enables_ref_1; + + uint8_t global_config__spad_enables_ref_2; + + uint8_t global_config__spad_enables_ref_3; + + uint8_t global_config__spad_enables_ref_4; + + uint8_t global_config__spad_enables_ref_5; + + uint8_t global_config__ref_en_start_select; + + uint8_t ref_spad_man__num_requested_ref_spads; + + uint8_t ref_spad_man__ref_location; + + uint16_t algo__crosstalk_compensation_plane_offset_kcps; + + int16_t algo__crosstalk_compensation_x_plane_gradient_kcps; + + int16_t algo__crosstalk_compensation_y_plane_gradient_kcps; + + uint16_t ref_spad_char__total_rate_target_mcps; + + int16_t algo__part_to_part_range_offset_mm; + + int16_t mm_config__inner_offset_mm; + + int16_t mm_config__outer_offset_mm; + +} VL53L1_customer_nvm_managed_t; + + + + +typedef struct { + uint16_t dss_config__target_total_rate_mcps; + + uint8_t debug__ctrl; + + uint8_t test_mode__ctrl; + + uint8_t clk_gating__ctrl; + + uint8_t nvm_bist__ctrl; + + uint8_t nvm_bist__num_nvm_words; + + uint8_t nvm_bist__start_address; + + uint8_t host_if__status; + + uint8_t pad_i2c_hv__config; + + uint8_t pad_i2c_hv__extsup_config; + + uint8_t gpio_hv_pad__ctrl; + + uint8_t gpio_hv_mux__ctrl; + + uint8_t gpio__tio_hv_status; + + uint8_t gpio__fio_hv_status; + + uint8_t ana_config__spad_sel_pswidth; + + uint8_t ana_config__vcsel_pulse_width_offset; + + uint8_t ana_config__fast_osc__config_ctrl; + + uint8_t sigma_estimator__effective_pulse_width_ns; + + uint8_t sigma_estimator__effective_ambient_width_ns; + + uint8_t sigma_estimator__sigma_ref_mm; + + uint8_t algo__crosstalk_compensation_valid_height_mm; + + uint8_t spare_host_config__static_config_spare_0; + + uint8_t spare_host_config__static_config_spare_1; + + uint16_t algo__range_ignore_threshold_mcps; + + uint8_t algo__range_ignore_valid_height_mm; + + uint8_t algo__range_min_clip; + + uint8_t algo__consistency_check__tolerance; + + uint8_t spare_host_config__static_config_spare_2; + + uint8_t sd_config__reset_stages_msb; + + uint8_t sd_config__reset_stages_lsb; + +} VL53L1_static_config_t; + + + + +typedef struct { + uint8_t gph_config__stream_count_update_value; + + uint8_t global_config__stream_divider; + + uint8_t system__interrupt_config_gpio; + + uint8_t cal_config__vcsel_start; + + uint16_t cal_config__repeat_rate; + + uint8_t global_config__vcsel_width; + + uint8_t phasecal_config__timeout_macrop; + + uint8_t phasecal_config__target; + + uint8_t phasecal_config__override; + + uint8_t dss_config__roi_mode_control; + + uint16_t system__thresh_rate_high; + + uint16_t system__thresh_rate_low; + + uint16_t dss_config__manual_effective_spads_select; + + uint8_t dss_config__manual_block_select; + + uint8_t dss_config__aperture_attenuation; + + uint8_t dss_config__max_spads_limit; + + uint8_t dss_config__min_spads_limit; + +} VL53L1_general_config_t; + + + + +typedef struct { + uint8_t mm_config__timeout_macrop_a_hi; + + uint8_t mm_config__timeout_macrop_a_lo; + + uint8_t mm_config__timeout_macrop_b_hi; + + uint8_t mm_config__timeout_macrop_b_lo; + + uint8_t range_config__timeout_macrop_a_hi; + + uint8_t range_config__timeout_macrop_a_lo; + + uint8_t range_config__vcsel_period_a; + + uint8_t range_config__timeout_macrop_b_hi; + + uint8_t range_config__timeout_macrop_b_lo; + + uint8_t range_config__vcsel_period_b; + + uint16_t range_config__sigma_thresh; + + uint16_t range_config__min_count_rate_rtn_limit_mcps; + + uint8_t range_config__valid_phase_low; + + uint8_t range_config__valid_phase_high; + + uint32_t system__intermeasurement_period; + + uint8_t system__fractional_enable; + +} VL53L1_timing_config_t; + + + + +typedef struct { + uint8_t system__grouped_parameter_hold_0; + + uint16_t system__thresh_high; + + uint16_t system__thresh_low; + + uint8_t system__enable_xtalk_per_quadrant; + + uint8_t system__seed_config; + + uint8_t sd_config__woi_sd0; + + uint8_t sd_config__woi_sd1; + + uint8_t sd_config__initial_phase_sd0; + + uint8_t sd_config__initial_phase_sd1; + + uint8_t system__grouped_parameter_hold_1; + + uint8_t sd_config__first_order_select; + + uint8_t sd_config__quantifier; + + uint8_t roi_config__user_roi_centre_spad; + + uint8_t roi_config__user_roi_requested_global_xy_size; + + uint8_t system__sequence_config; + + uint8_t system__grouped_parameter_hold; + +} VL53L1_dynamic_config_t; + + + + +typedef struct { + uint8_t power_management__go1_power_force; + + uint8_t system__stream_count_ctrl; + + uint8_t firmware__enable; + + uint8_t system__interrupt_clear; + + uint8_t system__mode_start; + +} VL53L1_system_control_t; + + + + +typedef struct { + uint8_t result__interrupt_status; + + uint8_t result__range_status; + + uint8_t result__report_status; + + uint8_t result__stream_count; + + uint16_t result__dss_actual_effective_spads_sd0; + + uint16_t result__peak_signal_count_rate_mcps_sd0; + + uint16_t result__ambient_count_rate_mcps_sd0; + + uint16_t result__sigma_sd0; + + uint16_t result__phase_sd0; + + uint16_t result__final_crosstalk_corrected_range_mm_sd0; + + uint16_t result__peak_signal_count_rate_crosstalk_corrected_mcps_sd0; + + uint16_t result__mm_inner_actual_effective_spads_sd0; + + uint16_t result__mm_outer_actual_effective_spads_sd0; + + uint16_t result__avg_signal_count_rate_mcps_sd0; + + uint16_t result__dss_actual_effective_spads_sd1; + + uint16_t result__peak_signal_count_rate_mcps_sd1; + + uint16_t result__ambient_count_rate_mcps_sd1; + + uint16_t result__sigma_sd1; + + uint16_t result__phase_sd1; + + uint16_t result__final_crosstalk_corrected_range_mm_sd1; + + uint16_t result__spare_0_sd1; + + uint16_t result__spare_1_sd1; + + uint16_t result__spare_2_sd1; + + uint8_t result__spare_3_sd1; + + uint8_t result__thresh_info; + +} VL53L1_system_results_t; + + + + +typedef struct { + uint32_t result_core__ambient_window_events_sd0; + + uint32_t result_core__ranging_total_events_sd0; + + int32_t result_core__signal_total_events_sd0; + + uint32_t result_core__total_periods_elapsed_sd0; + + uint32_t result_core__ambient_window_events_sd1; + + uint32_t result_core__ranging_total_events_sd1; + + int32_t result_core__signal_total_events_sd1; + + uint32_t result_core__total_periods_elapsed_sd1; + + uint8_t result_core__spare_0; + +} VL53L1_core_results_t; + + + + +typedef struct { + uint16_t phasecal_result__reference_phase; + + uint8_t phasecal_result__vcsel_start; + + uint8_t ref_spad_char_result__num_actual_ref_spads; + + uint8_t ref_spad_char_result__ref_location; + + uint8_t vhv_result__coldboot_status; + + uint8_t vhv_result__search_result; + + uint8_t vhv_result__latest_setting; + + uint16_t result__osc_calibrate_val; + + uint8_t ana_config__powerdown_go1; + + uint8_t ana_config__ref_bg_ctrl; + + uint8_t ana_config__regdvdd1v2_ctrl; + + uint8_t ana_config__osc_slow_ctrl; + + uint8_t test_mode__status; + + uint8_t firmware__system_status; + + uint8_t firmware__mode_status; + + uint8_t firmware__secondary_mode_status; + + uint16_t firmware__cal_repeat_rate_counter; + + uint16_t gph__system__thresh_high; + + uint16_t gph__system__thresh_low; + + uint8_t gph__system__enable_xtalk_per_quadrant; + + uint8_t gph__spare_0; + + uint8_t gph__sd_config__woi_sd0; + + uint8_t gph__sd_config__woi_sd1; + + uint8_t gph__sd_config__initial_phase_sd0; + + uint8_t gph__sd_config__initial_phase_sd1; + + uint8_t gph__sd_config__first_order_select; + + uint8_t gph__sd_config__quantifier; + + uint8_t gph__roi_config__user_roi_centre_spad; + + uint8_t gph__roi_config__user_roi_requested_global_xy_size; + + uint8_t gph__system__sequence_config; + + uint8_t gph__gph_id; + + uint8_t system__interrupt_set; + + uint8_t interrupt_manager__enables; + + uint8_t interrupt_manager__clear; + + uint8_t interrupt_manager__status; + + uint8_t mcu_to_host_bank__wr_access_en; + + uint8_t power_management__go1_reset_status; + + uint8_t pad_startup_mode__value_ro; + + uint8_t pad_startup_mode__value_ctrl; + + uint32_t pll_period_us; + + uint32_t interrupt_scheduler__data_out; + + uint8_t nvm_bist__complete; + + uint8_t nvm_bist__status; + +} VL53L1_debug_results_t; + + + + +typedef struct { + uint8_t identification__model_id; + + uint8_t identification__module_type; + + uint8_t identification__revision_id; + + uint16_t identification__module_id; + + uint8_t ana_config__fast_osc__trim_max; + + uint8_t ana_config__fast_osc__freq_set; + + uint8_t ana_config__vcsel_trim; + + uint8_t ana_config__vcsel_selion; + + uint8_t ana_config__vcsel_selion_max; + + uint8_t protected_laser_safety__lock_bit; + + uint8_t laser_safety__key; + + uint8_t laser_safety__key_ro; + + uint8_t laser_safety__clip; + + uint8_t laser_safety__mult; + + uint8_t global_config__spad_enables_rtn_0; + + uint8_t global_config__spad_enables_rtn_1; + + uint8_t global_config__spad_enables_rtn_2; + + uint8_t global_config__spad_enables_rtn_3; + + uint8_t global_config__spad_enables_rtn_4; + + uint8_t global_config__spad_enables_rtn_5; + + uint8_t global_config__spad_enables_rtn_6; + + uint8_t global_config__spad_enables_rtn_7; + + uint8_t global_config__spad_enables_rtn_8; + + uint8_t global_config__spad_enables_rtn_9; + + uint8_t global_config__spad_enables_rtn_10; + + uint8_t global_config__spad_enables_rtn_11; + + uint8_t global_config__spad_enables_rtn_12; + + uint8_t global_config__spad_enables_rtn_13; + + uint8_t global_config__spad_enables_rtn_14; + + uint8_t global_config__spad_enables_rtn_15; + + uint8_t global_config__spad_enables_rtn_16; + + uint8_t global_config__spad_enables_rtn_17; + + uint8_t global_config__spad_enables_rtn_18; + + uint8_t global_config__spad_enables_rtn_19; + + uint8_t global_config__spad_enables_rtn_20; + + uint8_t global_config__spad_enables_rtn_21; + + uint8_t global_config__spad_enables_rtn_22; + + uint8_t global_config__spad_enables_rtn_23; + + uint8_t global_config__spad_enables_rtn_24; + + uint8_t global_config__spad_enables_rtn_25; + + uint8_t global_config__spad_enables_rtn_26; + + uint8_t global_config__spad_enables_rtn_27; + + uint8_t global_config__spad_enables_rtn_28; + + uint8_t global_config__spad_enables_rtn_29; + + uint8_t global_config__spad_enables_rtn_30; + + uint8_t global_config__spad_enables_rtn_31; + + uint8_t roi_config__mode_roi_centre_spad; + + uint8_t roi_config__mode_roi_xy_size; + +} VL53L1_nvm_copy_data_t; + + + + +typedef struct { + uint8_t prev_shadow_result__interrupt_status; + + uint8_t prev_shadow_result__range_status; + + uint8_t prev_shadow_result__report_status; + + uint8_t prev_shadow_result__stream_count; + + uint16_t prev_shadow_result__dss_actual_effective_spads_sd0; + + uint16_t prev_shadow_result__peak_signal_count_rate_mcps_sd0; + + uint16_t prev_shadow_result__ambient_count_rate_mcps_sd0; + + uint16_t prev_shadow_result__sigma_sd0; + + uint16_t prev_shadow_result__phase_sd0; + + uint16_t prev_shadow_result__final_crosstalk_corrected_range_mm_sd0; + + uint16_t + psr__peak_signal_count_rate_crosstalk_corrected_mcps_sd0; + + uint16_t prev_shadow_result__mm_inner_actual_effective_spads_sd0; + + uint16_t prev_shadow_result__mm_outer_actual_effective_spads_sd0; + + uint16_t prev_shadow_result__avg_signal_count_rate_mcps_sd0; + + uint16_t prev_shadow_result__dss_actual_effective_spads_sd1; + + uint16_t prev_shadow_result__peak_signal_count_rate_mcps_sd1; + + uint16_t prev_shadow_result__ambient_count_rate_mcps_sd1; + + uint16_t prev_shadow_result__sigma_sd1; + + uint16_t prev_shadow_result__phase_sd1; + + uint16_t prev_shadow_result__final_crosstalk_corrected_range_mm_sd1; + + uint16_t prev_shadow_result__spare_0_sd1; + + uint16_t prev_shadow_result__spare_1_sd1; + + uint16_t prev_shadow_result__spare_2_sd1; + + uint16_t prev_shadow_result__spare_3_sd1; + +} VL53L1_prev_shadow_system_results_t; + + + + +typedef struct { + uint32_t prev_shadow_result_core__ambient_window_events_sd0; + + uint32_t prev_shadow_result_core__ranging_total_events_sd0; + + int32_t prev_shadow_result_core__signal_total_events_sd0; + + uint32_t prev_shadow_result_core__total_periods_elapsed_sd0; + + uint32_t prev_shadow_result_core__ambient_window_events_sd1; + + uint32_t prev_shadow_result_core__ranging_total_events_sd1; + + int32_t prev_shadow_result_core__signal_total_events_sd1; + + uint32_t prev_shadow_result_core__total_periods_elapsed_sd1; + + uint8_t prev_shadow_result_core__spare_0; + +} VL53L1_prev_shadow_core_results_t; + + + + +typedef struct { + uint8_t result__debug_status; + + uint8_t result__debug_stage; + +} VL53L1_patch_debug_t; + + + + +typedef struct { + uint16_t gph__system__thresh_rate_high; + + uint16_t gph__system__thresh_rate_low; + + uint8_t gph__system__interrupt_config_gpio; + +} VL53L1_gph_general_config_t; + + + + +typedef struct { + uint8_t gph__dss_config__roi_mode_control; + + uint16_t gph__dss_config__manual_effective_spads_select; + + uint8_t gph__dss_config__manual_block_select; + + uint8_t gph__dss_config__max_spads_limit; + + uint8_t gph__dss_config__min_spads_limit; + +} VL53L1_gph_static_config_t; + + + + +typedef struct { + uint8_t gph__mm_config__timeout_macrop_a_hi; + + uint8_t gph__mm_config__timeout_macrop_a_lo; + + uint8_t gph__mm_config__timeout_macrop_b_hi; + + uint8_t gph__mm_config__timeout_macrop_b_lo; + + uint8_t gph__range_config__timeout_macrop_a_hi; + + uint8_t gph__range_config__timeout_macrop_a_lo; + + uint8_t gph__range_config__vcsel_period_a; + + uint8_t gph__range_config__vcsel_period_b; + + uint8_t gph__range_config__timeout_macrop_b_hi; + + uint8_t gph__range_config__timeout_macrop_b_lo; + + uint16_t gph__range_config__sigma_thresh; + + uint16_t gph__range_config__min_count_rate_rtn_limit_mcps; + + uint8_t gph__range_config__valid_phase_low; + + uint8_t gph__range_config__valid_phase_high; + +} VL53L1_gph_timing_config_t; + + + + +typedef struct { + uint8_t firmware__internal_stream_count_div; + + uint8_t firmware__internal_stream_counter_val; + +} VL53L1_fw_internal_t; + + + + +typedef struct { + uint8_t dss_calc__roi_ctrl; + + uint8_t dss_calc__spare_1; + + uint8_t dss_calc__spare_2; + + uint8_t dss_calc__spare_3; + + uint8_t dss_calc__spare_4; + + uint8_t dss_calc__spare_5; + + uint8_t dss_calc__spare_6; + + uint8_t dss_calc__spare_7; + + uint8_t dss_calc__user_roi_spad_en_0; + + uint8_t dss_calc__user_roi_spad_en_1; + + uint8_t dss_calc__user_roi_spad_en_2; + + uint8_t dss_calc__user_roi_spad_en_3; + + uint8_t dss_calc__user_roi_spad_en_4; + + uint8_t dss_calc__user_roi_spad_en_5; + + uint8_t dss_calc__user_roi_spad_en_6; + + uint8_t dss_calc__user_roi_spad_en_7; + + uint8_t dss_calc__user_roi_spad_en_8; + + uint8_t dss_calc__user_roi_spad_en_9; + + uint8_t dss_calc__user_roi_spad_en_10; + + uint8_t dss_calc__user_roi_spad_en_11; + + uint8_t dss_calc__user_roi_spad_en_12; + + uint8_t dss_calc__user_roi_spad_en_13; + + uint8_t dss_calc__user_roi_spad_en_14; + + uint8_t dss_calc__user_roi_spad_en_15; + + uint8_t dss_calc__user_roi_spad_en_16; + + uint8_t dss_calc__user_roi_spad_en_17; + + uint8_t dss_calc__user_roi_spad_en_18; + + uint8_t dss_calc__user_roi_spad_en_19; + + uint8_t dss_calc__user_roi_spad_en_20; + + uint8_t dss_calc__user_roi_spad_en_21; + + uint8_t dss_calc__user_roi_spad_en_22; + + uint8_t dss_calc__user_roi_spad_en_23; + + uint8_t dss_calc__user_roi_spad_en_24; + + uint8_t dss_calc__user_roi_spad_en_25; + + uint8_t dss_calc__user_roi_spad_en_26; + + uint8_t dss_calc__user_roi_spad_en_27; + + uint8_t dss_calc__user_roi_spad_en_28; + + uint8_t dss_calc__user_roi_spad_en_29; + + uint8_t dss_calc__user_roi_spad_en_30; + + uint8_t dss_calc__user_roi_spad_en_31; + + uint8_t dss_calc__user_roi_0; + + uint8_t dss_calc__user_roi_1; + + uint8_t dss_calc__mode_roi_0; + + uint8_t dss_calc__mode_roi_1; + + uint8_t sigma_estimator_calc__spare_0; + + uint16_t vhv_result__peak_signal_rate_mcps; + + uint32_t vhv_result__signal_total_events_ref; + + uint16_t phasecal_result__phase_output_ref; + + uint16_t dss_result__total_rate_per_spad; + + uint8_t dss_result__enabled_blocks; + + uint16_t dss_result__num_requested_spads; + + uint16_t mm_result__inner_intersection_rate; + + uint16_t mm_result__outer_complement_rate; + + uint16_t mm_result__total_offset; + + uint32_t xtalk_calc__xtalk_for_enabled_spads; + + uint32_t xtalk_result__avg_xtalk_user_roi_kcps; + + uint32_t xtalk_result__avg_xtalk_mm_inner_roi_kcps; + + uint32_t xtalk_result__avg_xtalk_mm_outer_roi_kcps; + + uint32_t range_result__accum_phase; + + uint16_t range_result__offset_corrected_range; + +} VL53L1_patch_results_t; + + + + +typedef struct { + uint8_t shadow_phasecal_result__vcsel_start; + + uint8_t shadow_result__interrupt_status; + + uint8_t shadow_result__range_status; + + uint8_t shadow_result__report_status; + + uint8_t shadow_result__stream_count; + + uint16_t shadow_result__dss_actual_effective_spads_sd0; + + uint16_t shadow_result__peak_signal_count_rate_mcps_sd0; + + uint16_t shadow_result__ambient_count_rate_mcps_sd0; + + uint16_t shadow_result__sigma_sd0; + + uint16_t shadow_result__phase_sd0; + + uint16_t shadow_result__final_crosstalk_corrected_range_mm_sd0; + + uint16_t + shr__peak_signal_count_rate_crosstalk_corrected_mcps_sd0; + + uint16_t shadow_result__mm_inner_actual_effective_spads_sd0; + + uint16_t shadow_result__mm_outer_actual_effective_spads_sd0; + + uint16_t shadow_result__avg_signal_count_rate_mcps_sd0; + + uint16_t shadow_result__dss_actual_effective_spads_sd1; + + uint16_t shadow_result__peak_signal_count_rate_mcps_sd1; + + uint16_t shadow_result__ambient_count_rate_mcps_sd1; + + uint16_t shadow_result__sigma_sd1; + + uint16_t shadow_result__phase_sd1; + + uint16_t shadow_result__final_crosstalk_corrected_range_mm_sd1; + + uint16_t shadow_result__spare_0_sd1; + + uint16_t shadow_result__spare_1_sd1; + + uint16_t shadow_result__spare_2_sd1; + + uint8_t shadow_result__spare_3_sd1; + + uint8_t shadow_result__thresh_info; + + uint8_t shadow_phasecal_result__reference_phase_hi; + + uint8_t shadow_phasecal_result__reference_phase_lo; + +} VL53L1_shadow_system_results_t; + + + + +typedef struct { + uint32_t shadow_result_core__ambient_window_events_sd0; + + uint32_t shadow_result_core__ranging_total_events_sd0; + + int32_t shadow_result_core__signal_total_events_sd0; + + uint32_t shadow_result_core__total_periods_elapsed_sd0; + + uint32_t shadow_result_core__ambient_window_events_sd1; + + uint32_t shadow_result_core__ranging_total_events_sd1; + + int32_t shadow_result_core__signal_total_events_sd1; + + uint32_t shadow_result_core__total_periods_elapsed_sd1; + + uint8_t shadow_result_core__spare_0; + +} VL53L1_shadow_core_results_t; + + +#endif + + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_silicon_core.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_silicon_core.h new file mode 100644 index 000000000000..7df54088e77a --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_silicon_core.h @@ -0,0 +1,37 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#ifndef _VL53L1_SILICON_CORE_H_ +#define _VL53L1_SILICON_CORE_H_ + +#include "vl53l1_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + +VL53L1_Error VL53L1_is_firmware_ready_silicon( + VL53L1_DEV Dev, + uint8_t *pready); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_tuning_parm_defaults.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_tuning_parm_defaults.h new file mode 100644 index 000000000000..cb1cbbb02b7c --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_tuning_parm_defaults.h @@ -0,0 +1,395 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + +#define VL53L1_TUNINGPARM_VERSION_DEFAULT \ +((uint16_t) 32) +#define VL53L1_TUNINGPARM_KEY_TABLE_VERSION_DEFAULT \ +((uint16_t) 14) +#define VL53L1_TUNINGPARM_LLD_VERSION_DEFAULT \ +((uint16_t) 12180) +#define VL53L1_TUNINGPARM_HIST_ALGO_SELECT_DEFAULT \ +((uint8_t) 4) +#define VL53L1_TUNINGPARM_HIST_TARGET_ORDER_DEFAULT \ +((uint8_t) 1) +#define VL53L1_TUNINGPARM_HIST_FILTER_WOI_0_DEFAULT \ +((uint8_t) 1) +#define VL53L1_TUNINGPARM_HIST_FILTER_WOI_1_DEFAULT \ +((uint8_t) 2) +#define VL53L1_TUNINGPARM_HIST_AMB_EST_METHOD_DEFAULT \ +((uint8_t) 1) +#define VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_0_DEFAULT \ +((uint8_t) 80) +#define VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_1_DEFAULT \ +((uint8_t) 100) +#define VL53L1_TUNINGPARM_HIST_MIN_AMB_THRESH_EVENTS_DEFAULT \ +((int32_t) 16) +#define VL53L1_TUNINGPARM_HIST_AMB_EVENTS_SCALER_DEFAULT \ +((uint16_t) 4157) +#define VL53L1_TUNINGPARM_HIST_NOISE_THRESHOLD_DEFAULT \ +((uint16_t) 50) +#define VL53L1_TUNINGPARM_HIST_SIGNAL_TOTAL_EVENTS_LIMIT_DEFAULT \ +((int32_t) 100) +#define VL53L1_TUNINGPARM_HIST_SIGMA_EST_REF_MM_DEFAULT \ +((uint8_t) 1) +#define VL53L1_TUNINGPARM_HIST_SIGMA_THRESH_MM_DEFAULT \ +((uint16_t) 180) +#define VL53L1_TUNINGPARM_HIST_GAIN_FACTOR_DEFAULT \ +((uint16_t) 1987) +#define VL53L1_TUNINGPARM_CONSISTENCY_HIST_PHASE_TOLERANCE_DEFAULT \ +((uint8_t) 8) +#define VL53L1_TUNINGPARM_CONSISTENCY_HIST_MIN_MAX_TOLERANCE_MM_DEFAULT \ +((uint16_t) 0) +#define VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA_DEFAULT \ +((uint8_t) 0) +#define VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA_MIN_SPAD_LIMIT_DEFAULT \ +((uint16_t) 2048) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_LONG_RANGE_DEFAULT \ +((uint8_t) 9) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_MED_RANGE_DEFAULT \ +((uint8_t) 5) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_SHORT_RANGE_DEFAULT \ +((uint8_t) 3) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_LONG_RANGE_DEFAULT \ +((uint8_t) 6) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_MED_RANGE_DEFAULT \ +((uint8_t) 6) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_SHORT_RANGE_DEFAULT \ +((uint8_t) 6) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MIN_VALID_RANGE_MM_DEFAULT \ +((int16_t) -50) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RANGE_MM_DEFAULT \ +((int16_t) 50) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MAX_SIGMA_MM_DEFAULT \ +((uint16_t) 140) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MIN_MAX_TOLERANCE_DEFAULT \ +((uint16_t) 50) +#define VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RATE_KCPS_DEFAULT \ +((uint16_t) 400) +#define VL53L1_TUNINGPARM_XTALK_DETECT_EVENT_SIGMA_DEFAULT \ +((uint8_t) 80) +#define VL53L1_TUNINGPARM_HIST_XTALK_MARGIN_KCPS_DEFAULT \ +((int16_t) 0) +#define VL53L1_TUNINGPARM_CONSISTENCY_LITE_PHASE_TOLERANCE_DEFAULT \ +((uint8_t) 2) +#define VL53L1_TUNINGPARM_PHASECAL_TARGET_DEFAULT \ +((uint8_t) 33) +#define VL53L1_TUNINGPARM_LITE_CAL_REPEAT_RATE_DEFAULT \ +((uint16_t) 0) +#define VL53L1_TUNINGPARM_LITE_RANGING_GAIN_FACTOR_DEFAULT \ +((uint16_t) 2011) +#define VL53L1_TUNINGPARM_LITE_MIN_CLIP_MM_DEFAULT \ +((uint8_t) 0) +#define VL53L1_TUNINGPARM_LITE_LONG_SIGMA_THRESH_MM_DEFAULT \ +((uint16_t) 60) +#define VL53L1_TUNINGPARM_LITE_MED_SIGMA_THRESH_MM_DEFAULT \ +((uint16_t) 60) +#define VL53L1_TUNINGPARM_LITE_SHORT_SIGMA_THRESH_MM_DEFAULT \ +((uint16_t) 60) +#define VL53L1_TUNINGPARM_LITE_LONG_MIN_COUNT_RATE_RTN_MCPS_DEFAULT \ +((uint16_t) 128) +#define VL53L1_TUNINGPARM_LITE_MED_MIN_COUNT_RATE_RTN_MCPS_DEFAULT \ +((uint16_t) 128) +#define VL53L1_TUNINGPARM_LITE_SHORT_MIN_COUNT_RATE_RTN_MCPS_DEFAULT \ +((uint16_t) 128) +#define VL53L1_TUNINGPARM_LITE_SIGMA_EST_PULSE_WIDTH_DEFAULT \ +((uint8_t) 8) +#define VL53L1_TUNINGPARM_LITE_SIGMA_EST_AMB_WIDTH_NS_DEFAULT \ +((uint8_t) 16) +#define VL53L1_TUNINGPARM_LITE_SIGMA_REF_MM_DEFAULT \ +((uint8_t) 1) +#define VL53L1_TUNINGPARM_LITE_RIT_MULT_DEFAULT \ +((uint8_t) 64) +#define VL53L1_TUNINGPARM_LITE_SEED_CONFIG_DEFAULT \ +((uint8_t) 2) +#define VL53L1_TUNINGPARM_LITE_QUANTIFIER_DEFAULT \ +((uint8_t) 2) +#define VL53L1_TUNINGPARM_LITE_FIRST_ORDER_SELECT_DEFAULT \ +((uint8_t) 0) +#define VL53L1_TUNINGPARM_LITE_XTALK_MARGIN_KCPS_DEFAULT \ +((int16_t) 0) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_LONG_RANGE_DEFAULT \ +((uint8_t) 14) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_MED_RANGE_DEFAULT \ +((uint8_t) 10) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_SHORT_RANGE_DEFAULT \ +((uint8_t) 6) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_LONG_RANGE_DEFAULT \ +((uint8_t) 14) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_MED_RANGE_DEFAULT \ +((uint8_t) 10) +#define VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_SHORT_RANGE_DEFAULT \ +((uint8_t) 6) +#define VL53L1_TUNINGPARM_TIMED_SEED_CONFIG_DEFAULT \ +((uint8_t) 1) +#define VL53L1_TUNINGPARM_DMAX_CFG_SIGNAL_THRESH_SIGMA_DEFAULT \ +((uint8_t) 32) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_0_DEFAULT \ +((uint16_t) 15) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_1_DEFAULT \ +((uint16_t) 52) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_2_DEFAULT \ +((uint16_t) 200) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_3_DEFAULT \ +((uint16_t) 364) +#define VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_4_DEFAULT \ +((uint16_t) 400) +#define VL53L1_TUNINGPARM_VHV_LOOPBOUND_DEFAULT \ +((uint8_t) 129) +#define VL53L1_TUNINGPARM_REFSPADCHAR_DEVICE_TEST_MODE_DEFAULT \ +((uint8_t) 8) +#define VL53L1_TUNINGPARM_REFSPADCHAR_VCSEL_PERIOD_DEFAULT \ +((uint8_t) 11) +#define VL53L1_TUNINGPARM_REFSPADCHAR_PHASECAL_TIMEOUT_US_DEFAULT \ +((uint32_t) 1000) +#define VL53L1_TUNINGPARM_REFSPADCHAR_TARGET_COUNT_RATE_MCPS_DEFAULT \ +((uint16_t) 2560) +#define VL53L1_TUNINGPARM_REFSPADCHAR_MIN_COUNTRATE_LIMIT_MCPS_DEFAULT \ +((uint16_t) 1280) +#define VL53L1_TUNINGPARM_REFSPADCHAR_MAX_COUNTRATE_LIMIT_MCPS_DEFAULT \ +((uint16_t) 5120) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_NUM_OF_SAMPLES_DEFAULT \ +((uint8_t) 7) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_MIN_FILTER_THRESH_MM_DEFAULT \ +((int16_t) -70) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_FILTER_THRESH_MM_DEFAULT \ +((int16_t) 70) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_RATE_MCPS_DEFAULT \ +((uint16_t) 5120) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_PHASECAL_TIMEOUT_US_DEFAULT \ +((uint32_t) 15000) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_VALID_RATE_KCPS_DEFAULT \ +((uint16_t) 640) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_SIGMA_THRESHOLD_MM_DEFAULT \ +((uint16_t) 140) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_TIMEOUT_US_DEFAULT \ +((uint32_t) 2000) +#define VL53L1_TUNINGPARM_XTALK_EXTRACT_BIN_TIMEOUT_US_DEFAULT \ +((uint32_t) 10000) +#define VL53L1_TUNINGPARM_OFFSET_CAL_DSS_RATE_MCPS_DEFAULT \ +((uint16_t) 2560) +#define VL53L1_TUNINGPARM_OFFSET_CAL_PHASECAL_TIMEOUT_US_DEFAULT \ +((uint32_t) 15000) +#define VL53L1_TUNINGPARM_OFFSET_CAL_MM_TIMEOUT_US_DEFAULT \ +((uint32_t) 13000) +#define VL53L1_TUNINGPARM_OFFSET_CAL_RANGE_TIMEOUT_US_DEFAULT \ +((uint32_t) 13000) +#define VL53L1_TUNINGPARM_OFFSET_CAL_PRE_SAMPLES_DEFAULT \ +((uint8_t) 8) +#define VL53L1_TUNINGPARM_OFFSET_CAL_MM1_SAMPLES_DEFAULT \ +((uint8_t) 40) +#define VL53L1_TUNINGPARM_OFFSET_CAL_MM2_SAMPLES_DEFAULT \ +((uint8_t) 9) +#define VL53L1_TUNINGPARM_ZONE_CAL_DSS_RATE_MCPS_DEFAULT \ +((uint16_t) 5120) +#define VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_TIMEOUT_US_DEFAULT \ +((uint32_t) 15000) +#define VL53L1_TUNINGPARM_ZONE_CAL_DSS_TIMEOUT_US_DEFAULT \ +((uint32_t) 2000) +#define VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_NUM_SAMPLES_DEFAULT \ +((uint16_t) 16) +#define VL53L1_TUNINGPARM_ZONE_CAL_RANGE_TIMEOUT_US_DEFAULT \ +((uint32_t) 1000) +#define VL53L1_TUNINGPARM_ZONE_CAL_ZONE_NUM_SAMPLES_DEFAULT \ +((uint16_t) 8) +#define VL53L1_TUNINGPARM_SPADMAP_VCSEL_PERIOD_DEFAULT \ +((uint8_t) 18) +#define VL53L1_TUNINGPARM_SPADMAP_VCSEL_START_DEFAULT \ +((uint8_t) 15) +#define VL53L1_TUNINGPARM_SPADMAP_RATE_LIMIT_MCPS_DEFAULT \ +((uint16_t) 12) +#define VL53L1_TUNINGPARM_LITE_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS_DEFAULT \ +((uint16_t) 2560) +#define VL53L1_TUNINGPARM_RANGING_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS_DEFAULT \ +((uint16_t) 5120) +#define VL53L1_TUNINGPARM_MZ_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS_DEFAULT \ +((uint16_t) 5120) +#define VL53L1_TUNINGPARM_TIMED_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS_DEFAULT \ +((uint16_t) 2560) +#define VL53L1_TUNINGPARM_LITE_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 1000) +#define VL53L1_TUNINGPARM_RANGING_LONG_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 15000) +#define VL53L1_TUNINGPARM_RANGING_MED_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 9000) +#define VL53L1_TUNINGPARM_RANGING_SHORT_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 6000) +#define VL53L1_TUNINGPARM_MZ_LONG_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 15000) +#define VL53L1_TUNINGPARM_MZ_MED_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 9000) +#define VL53L1_TUNINGPARM_MZ_SHORT_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 6000) +#define VL53L1_TUNINGPARM_TIMED_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 1000) +#define VL53L1_TUNINGPARM_LITE_MM_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 2000) +#define VL53L1_TUNINGPARM_RANGING_MM_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 2000) +#define VL53L1_TUNINGPARM_MZ_MM_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 2000) +#define VL53L1_TUNINGPARM_TIMED_MM_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 2000) +#define VL53L1_TUNINGPARM_LITE_RANGE_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 63000) +#define VL53L1_TUNINGPARM_RANGING_RANGE_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 2500) +#define VL53L1_TUNINGPARM_MZ_RANGE_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 2500) +#define VL53L1_TUNINGPARM_TIMED_RANGE_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 13000) +#define VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_MARGIN_DEFAULT \ +((uint16_t) 0) +#define VL53L1_TUNINGPARM_DYNXTALK_NOISE_MARGIN_DEFAULT \ +((uint32_t) 100) +#define VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT_DEFAULT \ +((uint32_t) 0) +#define VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT_HI_DEFAULT \ +((uint8_t) 0) +#define VL53L1_TUNINGPARM_DYNXTALK_SAMPLE_LIMIT_DEFAULT \ +((uint32_t) 200) +#define VL53L1_TUNINGPARM_DYNXTALK_SINGLE_XTALK_DELTA_DEFAULT \ +((uint32_t) 2048) +#define VL53L1_TUNINGPARM_DYNXTALK_AVERAGED_XTALK_DELTA_DEFAULT \ +((uint32_t) 308) +#define VL53L1_TUNINGPARM_DYNXTALK_CLIP_LIMIT_DEFAULT \ +((uint32_t) 10240) +#define VL53L1_TUNINGPARM_DYNXTALK_SCALER_CALC_METHOD_DEFAULT \ +((uint8_t) 0) +#define VL53L1_TUNINGPARM_DYNXTALK_XGRADIENT_SCALER_DEFAULT \ +((int16_t) 256) +#define VL53L1_TUNINGPARM_DYNXTALK_YGRADIENT_SCALER_DEFAULT \ +((int16_t) 256) +#define VL53L1_TUNINGPARM_DYNXTALK_USER_SCALER_SET_DEFAULT \ +((uint8_t) 0) +#define VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_COR_SINGLE_APPLY_DEFAULT \ +((uint8_t) 0) +#define VL53L1_TUNINGPARM_DYNXTALK_XTALK_AMB_THRESHOLD_DEFAULT \ +((uint32_t) 128) +#define VL53L1_TUNINGPARM_DYNXTALK_NODETECT_AMB_THRESHOLD_KCPS_DEFAULT \ +((uint32_t) 57671680) +#define VL53L1_TUNINGPARM_DYNXTALK_NODETECT_SAMPLE_LIMIT_DEFAULT \ +((uint32_t) 40) +#define VL53L1_TUNINGPARM_DYNXTALK_NODETECT_XTALK_OFFSET_KCPS_DEFAULT \ +((uint32_t) 410) +#define VL53L1_TUNINGPARM_DYNXTALK_NODETECT_MIN_RANGE_MM_DEFAULT \ +((uint16_t) 900) +#define VL53L1_TUNINGPARM_LOWPOWERAUTO_VHV_LOOP_BOUND_DEFAULT \ +((uint8_t) 3) +#define VL53L1_TUNINGPARM_LOWPOWERAUTO_MM_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 1) +#define VL53L1_TUNINGPARM_LOWPOWERAUTO_RANGE_CONFIG_TIMEOUT_US_DEFAULT \ +((uint32_t) 8000) +#define VL53L1_TUNINGPARM_VERY_SHORT_DSS_RATE_MCPS_DEFAULT \ +((uint16_t) 10240) +#define VL53L1_TUNINGPARM_PHASECAL_PATCH_POWER_DEFAULT \ +((uint32_t) 0) +#define VL53L1_TUNINGPARM_HIST_MERGE_DEFAULT \ +((uint8_t) 1) +#define VL53L1_TUNINGPARM_RESET_MERGE_THRESHOLD_DEFAULT \ +((uint32_t) 15000) +#define VL53L1_TUNINGPARM_HIST_MERGE_MAX_SIZE_DEFAULT \ +((uint8_t) 6) +#define VL53L1_TUNINGPARM_DYNXTALK_MAX_SMUDGE_FACTOR_DEFAULT \ +((uint32_t) 2000) +#define VL53L1_TUNINGPARM_UWR_ENABLE_DEFAULT \ +((uint8_t) 1) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_1_MIN_DEFAULT \ +((int16_t) 2000) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_1_MAX_DEFAULT \ +((int16_t) 2750) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_2_MIN_DEFAULT \ +((int16_t) 250) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_2_MAX_DEFAULT \ +((int16_t) 1000) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_3_MIN_DEFAULT \ +((int16_t) 1250) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_3_MAX_DEFAULT \ +((int16_t) 1750) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_4_MIN_DEFAULT \ +((int16_t) 1250) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_4_MAX_DEFAULT \ +((int16_t) 1750) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_5_MIN_DEFAULT \ +((int16_t) -200) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_5_MAX_DEFAULT \ +((int16_t) 200) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_6_MIN_DEFAULT \ +((int16_t) 250) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_6_MAX_DEFAULT \ +((int16_t) 1250) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_1_RANGEA_DEFAULT \ +((int16_t) 2300) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_1_RANGEB_DEFAULT \ +((int16_t) 0) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_2_RANGEA_DEFAULT \ +((int16_t) 2300) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_2_RANGEB_DEFAULT \ +((int16_t) 3050) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_3_RANGEA_DEFAULT \ +((int16_t) 4600) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_3_RANGEB_DEFAULT \ +((int16_t) 3050) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_4_RANGEA_DEFAULT \ +((int16_t) 4600) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_4_RANGEB_DEFAULT \ +((int16_t) 6200) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_5_RANGEA_DEFAULT \ +((int16_t) 0) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_5_RANGEB_DEFAULT \ +((int16_t) 0) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_6_RANGEA_DEFAULT \ +((int16_t) 6960) +#define VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_6_RANGEB_DEFAULT \ +((int16_t) 6190) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_1_MIN_DEFAULT \ +((int16_t) 250) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_1_MAX_DEFAULT \ +((int16_t) 1250) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_2_MIN_DEFAULT \ +((int16_t) 3250) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_2_MAX_DEFAULT \ +((int16_t) 4500) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_3_MIN_DEFAULT \ +((int16_t) -200) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_3_MAX_DEFAULT \ +((int16_t) 200) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_4_MIN_DEFAULT \ +((int16_t) 2850) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_4_MAX_DEFAULT \ +((int16_t) 3300) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_5_MIN_DEFAULT \ +((int16_t) 0) +#define VL53L1_TUNINGPARM_UWR_LONG_ZONE_5_MAX_DEFAULT \ +((int16_t) 0) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_1_RANGEA_DEFAULT \ +((int16_t) 3850) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_1_RANGEB_DEFAULT \ +((int16_t) 4600) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_2_RANGEA_DEFAULT \ +((int16_t) 3850) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_2_RANGEB_DEFAULT \ +((int16_t) 0) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_3_RANGEA_DEFAULT \ +((int16_t) 0) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_3_RANGEB_DEFAULT \ +((int16_t) 0) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_4_RANGEA_DEFAULT \ +((int16_t) 7700) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_4_RANGEB_DEFAULT \ +((int16_t) 4640) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_5_RANGEA_DEFAULT \ +((int16_t) 0) +#define VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_5_RANGEB_DEFAULT \ +((int16_t) 0) +#define VL53L1_TUNINGPARM_MIN_SIGNAL_SECONDARY_TARGETS_DEFAULT \ +((uint32_t) 0) + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_wait.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_wait.h new file mode 100644 index 000000000000..9c49305bc536 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_wait.h @@ -0,0 +1,100 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#ifndef _VL53L1_WAIT_H_ +#define _VL53L1_WAIT_H_ + +#include "vl53l1_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + +VL53L1_Error VL53L1_wait_for_boot_completion( + VL53L1_DEV Dev); + + + + +VL53L1_Error VL53L1_wait_for_firmware_ready( + VL53L1_DEV Dev); + + + + +VL53L1_Error VL53L1_wait_for_range_completion( + VL53L1_DEV Dev); + + + + +VL53L1_Error VL53L1_wait_for_test_completion( + VL53L1_DEV Dev); + + + + + + +VL53L1_Error VL53L1_is_boot_complete( + VL53L1_DEV Dev, + uint8_t *pready); + + + +VL53L1_Error VL53L1_is_firmware_ready( + VL53L1_DEV Dev, + uint8_t *pready); + + + + +VL53L1_Error VL53L1_is_new_data_ready( + VL53L1_DEV Dev, + uint8_t *pready); + + + + + + +VL53L1_Error VL53L1_poll_for_boot_completion( + VL53L1_DEV Dev, + uint32_t timeout_ms); + + + + +VL53L1_Error VL53L1_poll_for_firmware_ready( + VL53L1_DEV Dev, + uint32_t timeout_ms); + + + + +VL53L1_Error VL53L1_poll_for_range_completion( + VL53L1_DEV Dev, + uint32_t timeout_ms); + + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_zone_presets.h b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_zone_presets.h new file mode 100644 index 000000000000..4629ea72eed2 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/inc/vl53l1_zone_presets.h @@ -0,0 +1,55 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#ifndef _VL53L1_ZONE_PRESETS_H_ +#define _VL53L1_ZONE_PRESETS_H_ + +#include "vl53l1_ll_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + +VL53L1_Error VL53L1_init_zone_config_structure( + uint8_t x_off, + uint8_t x_inc, + uint8_t x_zones, + uint8_t y_off, + uint8_t y_inc, + uint8_t y_zones, + uint8_t width, + uint8_t height, + VL53L1_zone_config_t *pdata); + + + + +VL53L1_Error VL53L1_zone_preset_xtalk_planar( + VL53L1_general_config_t *pgeneral, + VL53L1_zone_config_t *pzone_cfg); + + + +VL53L1_Error VL53L1_init_zone_config_histogram_bins( + VL53L1_zone_config_t *pdata); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/ipp/ipp_linux.c b/drivers/input/misc/vl53L1_14_1_2/ipp/ipp_linux.c new file mode 100644 index 000000000000..b3a167915148 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/ipp/ipp_linux.c @@ -0,0 +1,336 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +/** + * @file ipp_linux.c kernel side implementation of vl53l1 protected processing + * + * @date Sep 1, 2016 + * @author : imaging + * + * @ingroup ipp_dev + */ + +#include "stmvl53l1.h" + +#define IPP_ERR_CODE (VL53L1_ERROR_PLATFORM_SPECIFIC_START-1) + +static int stmvl53l1_ipp_do_wrapper(struct stmvl53l1_data *data, + struct ipp_work_t *pin, struct ipp_work_t *pout, int payload_out) +{ + int rc; + + if (data->ipp.buzy) { + vl53l1_errmsg("try exec new ipp but still buzy on previous"); + /* TODO shall we discard it and push new ? */ + rc = IPP_ERR_CODE; + goto done; + } + WARN_ON(pin->payload > IPP_WORK_MAX_PAYLOAD); + stmvl531_ipp_tim_start(data); + rc = stmvl53l1_ipp_do(data, pin, pout); + if (rc != 0) { + vl53l1_errmsg("stmvl53l1_ipp_do err %d\n", rc); + rc = IPP_ERR_CODE; + goto done; + } + vl53l1_dbgmsg("ipp ok \n"); + /* check what we got back if valid answer error etc */ + if (pout->status) { + vl53l1_errmsg("ipp error status %d from user", pout->status); + if (pout->status >= stmvl53l1_ipp_status_proc_code) + rc = pout->status & (stmvl53l1_ipp_status_proc_code-1); + else + rc = IPP_ERR_CODE; + goto done; + } + WARN_ON(pout->payload > IPP_WORK_MAX_PAYLOAD); + if (pout->payload != payload_out) { + /* bad formated answer */ + vl53l1_errmsg("bad payload %d != %d in ipp work back", + pout->payload, payload_out); + rc = IPP_ERR_CODE; + goto done; + } + stmvl531_ipp_tim_stop(data); + stmvl531_ipp_stat(data, "ipp #%5x to=%3d fm=%3d in %5ld us", + pin->xfer_id, pin->payload, + pout->payload, + stmvl531_ipp_time(data)); + + rc = 0; +done: + + return rc; +} + +/** + * @file vl53l1_platform_ipp.h + * + * @brief EwokPlus25 IPP Wrapper Functions + */ + +/** + * @brief IPP Wrapper call for histogram post processing + * + * + * @param[in] Dev : Device handle + * @param[in] pdmax_cal : DMAX calibration data + * @param[in] pdmax_cfg : DMAX configuration data + * @param[in] ppost_cfg : VL53L1_hist_post_process_config_t + * @param[in] pbins : Input histogram raw bin data + * @param[in] pxtalk : Cross talk histogram data + * @param[in] pArea1 : Extern. allocated to save stack + * @param[in] pArea1 : Extern. allocated to save stack + * @param[out] presults : Output VL53L1_range_results_t + * structure + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_ipp_hist_process_data( + VL53L1_DEV dev, + VL53L1_dmax_calibration_data_t *pdmax_cal, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg, + VL53L1_hist_post_process_config_t *ppost_cfg, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_xtalk_histogram_data_t *pxtalk, + uint8_t *pArea1, + uint8_t *pArea2, + uint8_t *phisto_merge_nb, + VL53L1_range_results_t *presults) +{ + struct stmvl53l1_data *data = (struct stmvl53l1_data *) + container_of(dev, struct stmvl53l1_data, stdev); + struct ipp_work_t *pout = &data->ipp.work_out; + struct ipp_work_t *pin = &data->ipp.work; + int rc; + VL53L1_range_results_t *presults_ipp; + + IPP_SERIALIZE_VAR; + + /* These external workspace input params are useless with IPP managed in + * the daemon because the histogram processing function is actually + * allocating its data in USER stack within the daemon not the kernel one + */ + (void)pArea1; + (void)pArea2; + + /* setup pin */ + IPP_SERIALIZE_START(pin->data, 6); + IPP_SET_ARG_PTR(pin->data, 0, pdmax_cal); + IPP_SET_ARG_PTR(pin->data, 1, pdmax_cfg); + IPP_SET_ARG_PTR(pin->data, 2, ppost_cfg); + IPP_SET_ARG_PTR(pin->data, 3, pbins); + IPP_SET_ARG_PTR(pin->data, 4, pxtalk); + IPP_SET_ARG_PTR(pin->data, 5, phisto_merge_nb); + pin->payload = IPP_SERIALIZE_PAYLAOD(); + pin->process_no = stmvl53l1_ipp_cal_hist; + + /* setup pout */ + IPP_SERIALIZE_START(pout->data, 1); + IPP_OUT_ARG_PTR(pout->data, 0, presults_ipp); + + /* do ipp */ + rc = stmvl53l1_ipp_do_wrapper(data, pin, pout, IPP_SERIALIZE_PAYLAOD()); + if (rc) + goto done; + + /* copy output */ + memcpy(presults, presults_ipp, sizeof(*presults)); + + rc = 0; +done: + + return rc; +} + +/** + * @brief IPP Wrapper call for histogram ambient dmax calc + * + * The target reflectance in percent for the DMAX calculation + * is set by target_reflectance input + * + * The fixed point format is 7.2 + * + * @param[in] Dev : Device handle + * @param[in] target_reflectance : target reflectance to report ambient DMAX + * Percentage in 7.2 fixed point format + * @param[in] pdmax_cal : DMAX calibration data + * @param[in] pdmax_cfg : DMAX configuration data + * @param[in] pbins : Input histogram raw bin data + * @param[out] pambient_dmax_mm : Output ambient DMAX distance in [mm] + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_ipp_hist_ambient_dmax( + VL53L1_DEV dev, + uint16_t target_reflectance, + VL53L1_dmax_calibration_data_t *pdmax_cal, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg, + VL53L1_histogram_bin_data_t *pbins, + int16_t *pambient_dmax_mm) +{ + struct stmvl53l1_data *data = (struct stmvl53l1_data *) + container_of(dev, struct stmvl53l1_data, stdev); + struct ipp_work_t *pout = &data->ipp.work_out; + struct ipp_work_t *pin = &data->ipp.work; + int rc; + int16_t *pambient_dmax_mm_ipp; + + IPP_SERIALIZE_VAR; + + /* setup pin */ + IPP_SERIALIZE_START(pin->data, 4); + IPP_SET_ARG(pin->data, 0, target_reflectance); + IPP_SET_ARG_PTR(pin->data, 1, pdmax_cal); + IPP_SET_ARG_PTR(pin->data, 2, pdmax_cfg); + IPP_SET_ARG_PTR(pin->data, 3, pbins); + pin->payload = IPP_SERIALIZE_PAYLAOD(); + pin->process_no = stmvl53l1_ipp_hist_ambient_dmax; + + /* setup pout */ + IPP_SERIALIZE_START(pout->data, 1); + IPP_OUT_ARG_PTR(pout->data, 0, pambient_dmax_mm_ipp); + + /* do ipp */ + rc = stmvl53l1_ipp_do_wrapper(data, pin, pout, IPP_SERIALIZE_PAYLAOD()); + if (rc) + goto done; + + /* copy output */ + memcpy(pambient_dmax_mm, pambient_dmax_mm_ipp, + sizeof(*pambient_dmax_mm)); + + rc = 0; +done: + + return rc; +} + +/** + * @brief IPP Wrapper call for xtalk calibration post processing + * + * @param[in] Dev : Device handle + * @param[in] pxtalk_ranges : Input VL53L1_xtalk_range_results_t + * Must contain 5 ranges, 4 quadrants + 1 + * full FoV + * @param[out] pxtalk_shape : Output normalised Cross talk histogram + * shape + * @param[out] pxtalk_cal : Output VL53L1_xtalk_calibration_results_t + * structure + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ +VL53L1_Error VL53L1_ipp_xtalk_calibration_process_data( + VL53L1_DEV dev, + VL53L1_xtalk_range_results_t *pxtalk_ranges, + VL53L1_xtalk_histogram_data_t *pxtalk_shape, + VL53L1_xtalk_calibration_results_t *pxtalk_cal) +{ + struct stmvl53l1_data *data = (struct stmvl53l1_data *) + container_of(dev, struct stmvl53l1_data, stdev); + struct ipp_work_t *pout = &data->ipp.work_out; + struct ipp_work_t *pin = &data->ipp.work; + int rc; + VL53L1_xtalk_histogram_data_t *pxtalk_shape_ipp; + VL53L1_xtalk_calibration_results_t *pxtalk_cal_ipp; + + IPP_SERIALIZE_VAR; + + /* setup pin */ + IPP_SERIALIZE_START(pin->data, 1); + IPP_SET_ARG_PTR(pin->data, 0, pxtalk_ranges); + pin->payload = IPP_SERIALIZE_PAYLAOD(); + pin->process_no = stmvl53l1_ipp_xtalk_calibration; + + /* setup pout */ + IPP_SERIALIZE_START(pout->data, 2); + IPP_OUT_ARG_PTR(pout->data, 0, pxtalk_shape_ipp); + IPP_OUT_ARG_PTR(pout->data, 1, pxtalk_cal_ipp); + + /* do ipp */ + rc = stmvl53l1_ipp_do_wrapper(data, pin, pout, IPP_SERIALIZE_PAYLAOD()); + if (rc) + goto done; + + /* copy output */ + memcpy(pxtalk_shape, pxtalk_shape_ipp, sizeof(*pxtalk_shape)); + memcpy(pxtalk_cal, pxtalk_cal_ipp, sizeof(*pxtalk_cal)); + + rc = 0; +done: + + return rc; +} + +/** + * @brief IPP Wrapper call for Generating Xtalk data from dual reflectance + * histogram data + * + * @param[in] Dev : Device handle + * @param[in] pxtalk_results : Pointer to xtalk_results structure + * containing dual reflectance + * histogram data + * @param[in] expected_target_distance_mm : User input of true target distance + * @param[in] higher_reflectance : User input detailing which + * histogram data 1 or 2 has the + * highest reflectance. + * @param[out] pxtalk_avg_samples : Pointer to output xtalk histogram + * data + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_ipp_generate_dual_reflectance_xtalk_samples( + VL53L1_DEV dev, + VL53L1_xtalk_range_results_t *pxtalk_results, + uint16_t expected_target_distance_mm, + uint8_t higher_reflectance, + VL53L1_histogram_bin_data_t *pxtalk_avg_samples) +{ + struct stmvl53l1_data *data = (struct stmvl53l1_data *) + container_of(dev, struct stmvl53l1_data, stdev); + struct ipp_work_t *pout = &data->ipp.work_out; + struct ipp_work_t *pin = &data->ipp.work; + int rc; + VL53L1_histogram_bin_data_t *pxtalk_avg_samples_ipp; + + IPP_SERIALIZE_VAR; + + /* setup pin */ + IPP_SERIALIZE_START(pin->data, 3); + IPP_SET_ARG_PTR(pin->data, 0, pxtalk_results); + IPP_SET_ARG(pin->data, 1, expected_target_distance_mm); + IPP_SET_ARG(pin->data, 2, higher_reflectance); + pin->payload = IPP_SERIALIZE_PAYLAOD(); + pin->process_no = stmvl53l1_ipp_generate_dual_reflectance_xtalk_samples; + + /* setup pout */ + IPP_SERIALIZE_START(pout->data, 1); + IPP_OUT_ARG_PTR(pout->data, 0, pxtalk_avg_samples_ipp); + + /* do ipp */ + rc = stmvl53l1_ipp_do_wrapper(data, pin, pout, IPP_SERIALIZE_PAYLAOD()); + if (rc) + goto done; + + /* copy output */ + memcpy(pxtalk_avg_samples, pxtalk_avg_samples_ipp, + sizeof(*pxtalk_avg_samples)); + + rc = 0; +done: + + return rc; +} diff --git a/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_dmax.h b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_dmax.h new file mode 100644 index 000000000000..1e81a2305111 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_dmax.h @@ -0,0 +1,63 @@ + +/* SPDX-License-Identifier: BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Protected and is dual licensed, + either 'STMicroelectronics Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ****************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + ****************************************************************************** + */ + + + + +#ifndef _VL53L1_DMAX_H_ +#define _VL53L1_DMAX_H_ + +#include "vl53l1_types.h" +#include "vl53l1_hist_structs.h" +#include "vl53l1_dmax_private_structs.h" +#include "vl53l1_error_codes.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + +VL53L1_Error VL53L1_f_001( + uint16_t target_reflectance, + VL53L1_dmax_calibration_data_t *pcal, + VL53L1_hist_gen3_dmax_config_t *pcfg, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_hist_gen3_dmax_private_data_t *pdata, + int16_t *pambient_dmax_mm); + + + + +uint32_t VL53L1_f_002( + uint32_t events_threshold, + uint32_t ref_signal_events, + uint32_t ref_distance_mm, + uint32_t signal_thresh_sigma); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_dmax_private_structs.h b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_dmax_private_structs.h new file mode 100644 index 000000000000..d3395e3d2556 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_dmax_private_structs.h @@ -0,0 +1,89 @@ + +/* SPDX-License-Identifier: BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Protected and is dual licensed, + either 'STMicroelectronics Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ****************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + ****************************************************************************** + */ + + + + + + + +#ifndef _VL53L1_DMAX_PRIVATE_STRUCTS_H_ +#define _VL53L1_DMAX_PRIVATE_STRUCTS_H_ + +#include "vl53l1_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + + + +typedef struct { + + uint32_t VL53L1_p_036; + + + uint8_t VL53L1_p_064; + + uint8_t VL53L1_p_065; + + + uint16_t VL53L1_p_066; + + uint16_t VL53L1_p_067; + + uint16_t VL53L1_p_068; + + uint16_t VL53L1_p_037; + + + uint32_t VL53L1_p_012; + + uint32_t VL53L1_p_033; + + + uint16_t VL53L1_p_001; + + + uint16_t VL53L1_p_006; + + + uint32_t VL53L1_p_004; + + uint32_t VL53L1_p_034; + + + int16_t VL53L1_p_035; + + int16_t VL53L1_p_007; + + +} VL53L1_hist_gen3_dmax_private_data_t; + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_hist_algos_gen3.h b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_hist_algos_gen3.h new file mode 100644 index 000000000000..72ca3df9d773 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_hist_algos_gen3.h @@ -0,0 +1,156 @@ + +/* SPDX-License-Identifier: BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Protected and is dual licensed, + either 'STMicroelectronics Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ****************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + ****************************************************************************** + */ + + + + + +#ifndef _VL53L1_HIST_ALGOS_GEN3_H_ +#define _VL53L1_HIST_ALGOS_GEN3_H_ + +#include "vl53l1_types.h" +#include "vl53l1_ll_def.h" + +#include "vl53l1_hist_private_structs.h" +#include "vl53l1_dmax_private_structs.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + + + +void VL53L1_f_016( + VL53L1_hist_gen3_algo_private_data_t *palgo); + + + + + + +VL53L1_Error VL53L1_f_018( + uint16_t ambient_threshold_events_scaler, + int32_t ambient_threshold_sigma, + int32_t min_ambient_threshold_events, + uint8_t algo__crosstalk_compensation_enable, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_histogram_bin_data_t *pxtalk, + VL53L1_hist_gen3_algo_private_data_t *palgo); + + + + + + +VL53L1_Error VL53L1_f_019( + VL53L1_hist_gen3_algo_private_data_t *palgo); + + + + +VL53L1_Error VL53L1_f_020( + VL53L1_hist_gen3_algo_private_data_t *palgo); + + + + +VL53L1_Error VL53L1_f_021( + VL53L1_hist_gen3_algo_private_data_t *palgo); + + + + +VL53L1_Error VL53L1_f_028( + VL53L1_HistTargetOrder target_order, + VL53L1_hist_gen3_algo_private_data_t *palgo); + + + + +VL53L1_Error VL53L1_f_022( + uint8_t pulse_no, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_hist_gen3_algo_private_data_t *palgo); + + + +VL53L1_Error VL53L1_f_027( + uint8_t pulse_no, + uint8_t clip_events, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_hist_gen3_algo_private_data_t *palgo); + + + + +VL53L1_Error VL53L1_f_030( + int16_t VL53L1_p_022, + int16_t VL53L1_p_026, + uint8_t VL53L1_p_031, + uint8_t clip_events, + VL53L1_histogram_bin_data_t *pbins, + uint32_t *pphase); + + + + +VL53L1_Error VL53L1_f_023( + uint8_t pulse_no, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_hist_gen3_algo_private_data_t *palgo, + int32_t pad_value, + VL53L1_histogram_bin_data_t *ppulse); + + + + +VL53L1_Error VL53L1_f_026( + uint8_t bin, + uint8_t sigma_estimator__sigma_ref_mm, + uint8_t VL53L1_p_031, + uint8_t VL53L1_p_055, + uint8_t crosstalk_compensation_enable, + VL53L1_histogram_bin_data_t *phist_data_ap, + VL53L1_histogram_bin_data_t *phist_data_zp, + VL53L1_histogram_bin_data_t *pxtalk_hist, + uint16_t *psigma_est); + + + + +void VL53L1_f_029( + uint8_t range_id, + uint8_t valid_phase_low, + uint8_t valid_phase_high, + uint16_t sigma_thres, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_hist_pulse_data_t *ppulse, + VL53L1_range_data_t *pdata); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_hist_algos_gen4.h b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_hist_algos_gen4.h new file mode 100644 index 000000000000..1c4eb162419b --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_hist_algos_gen4.h @@ -0,0 +1,102 @@ + +/* SPDX-License-Identifier: BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Protected and is dual licensed, + either 'STMicroelectronics Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ****************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + ****************************************************************************** + */ + + + + + +#ifndef _VL53L1_HIST_ALGOS_GEN4_H_ +#define _VL53L1_HIST_ALGOS_GEN4_H_ + +#include "vl53l1_types.h" +#include "vl53l1_ll_def.h" + +#include "vl53l1_hist_private_structs.h" +#include "vl53l1_dmax_private_structs.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif + + + + +void VL53L1_f_032( + VL53L1_hist_gen4_algo_filtered_data_t *palgo); + + + + +VL53L1_Error VL53L1_f_033( + VL53L1_dmax_calibration_data_t *pdmax_cal, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg, + VL53L1_hist_post_process_config_t *ppost_cfg, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_histogram_bin_data_t *pxtalk, + VL53L1_hist_gen3_algo_private_data_t *palgo, + VL53L1_hist_gen4_algo_filtered_data_t *pfiltered, + VL53L1_hist_gen3_dmax_private_data_t *pdmax_algo, + VL53L1_range_results_t *presults, + uint8_t histo_merge_nb); + + + + + +VL53L1_Error VL53L1_f_034( + uint8_t pulse_no, + VL53L1_histogram_bin_data_t *ppulse, + VL53L1_hist_gen3_algo_private_data_t *palgo, + VL53L1_hist_gen4_algo_filtered_data_t *pfiltered); + + + + +VL53L1_Error VL53L1_f_035( + uint8_t pulse_no, + uint16_t noise_threshold, + VL53L1_hist_gen4_algo_filtered_data_t *pfiltered, + VL53L1_hist_gen3_algo_private_data_t *palgo); + + + + +VL53L1_Error VL53L1_f_036( + uint8_t bin, + int32_t VL53L1_p_003, + int32_t VL53L1_p_018, + int32_t VL53L1_p_001, + int32_t ax, + int32_t bx, + int32_t cx, + int32_t VL53L1_p_004, + uint8_t VL53L1_p_031, + uint32_t *pmedian_phase); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_hist_core.h b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_hist_core.h new file mode 100644 index 000000000000..e53c4d92512e --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_hist_core.h @@ -0,0 +1,100 @@ + +/* SPDX-License-Identifier: BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Protected and is dual licensed, + either 'STMicroelectronics Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ****************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + ****************************************************************************** + */ + + + + + +#ifndef _VL53L1_HIST_CORE_H_ +#define _VL53L1_HIST_CORE_H_ + +#include "vl53l1_types.h" +#include "vl53l1_ll_def.h" +#include "vl53l1_hist_private_structs.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + + + +void VL53L1_f_013( + uint8_t VL53L1_p_018, + uint8_t filter_woi, + VL53L1_histogram_bin_data_t *pbins, + int32_t *pa, + int32_t *pb, + int32_t *pc); + + + + +VL53L1_Error VL53L1_f_011( + uint16_t vcsel_width, + uint16_t fast_osc_frequency, + uint32_t total_periods_elapsed, + uint16_t VL53L1_p_006, + VL53L1_range_data_t *pdata, + uint8_t histo_merge_nb); + + + + +void VL53L1_f_012( + uint16_t gain_factor, + int16_t range_offset_mm, + VL53L1_range_data_t *pdata); + + + + +void VL53L1_f_037( + VL53L1_histogram_bin_data_t *pdata, + int32_t ambient_estimate_counts_per_bin); + + + + +void VL53L1_f_004( + VL53L1_histogram_bin_data_t *pxtalk, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_histogram_bin_data_t *pxtalk_realigned); + + + +int8_t VL53L1_f_038( + VL53L1_histogram_bin_data_t *pdata1, + VL53L1_histogram_bin_data_t *pdata2); + + + +VL53L1_Error VL53L1_f_039( + VL53L1_histogram_bin_data_t *pidata, + VL53L1_histogram_bin_data_t *podata); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_hist_funcs.h b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_hist_funcs.h new file mode 100644 index 000000000000..070c6880fe92 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_hist_funcs.h @@ -0,0 +1,67 @@ + +/* SPDX-License-Identifier: BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Protected and is dual licensed, + either 'STMicroelectronics Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ****************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + ****************************************************************************** + */ + + + + + +#ifndef _VL53L1_HIST_FUNCS_H_ +#define _VL53L1_HIST_FUNCS_H_ + +#include "vl53l1_types.h" +#include "vl53l1_ll_def.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + + + +VL53L1_Error VL53L1_hist_process_data( + VL53L1_dmax_calibration_data_t *pdmax_cal, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg, + VL53L1_hist_post_process_config_t *ppost_cfg, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_xtalk_histogram_data_t *pxtalk, + uint8_t *pArea1, + uint8_t *pArea2, + VL53L1_range_results_t *presults, + uint8_t *HistMergeNumber); + + + + +VL53L1_Error VL53L1_hist_ambient_dmax( + uint16_t target_reflectance, + VL53L1_dmax_calibration_data_t *pdmax_cal, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg, + VL53L1_histogram_bin_data_t *pbins, + int16_t *pambient_dmax_mm); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_hist_private_structs.h b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_hist_private_structs.h new file mode 100644 index 000000000000..2fd0fde0544f --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_hist_private_structs.h @@ -0,0 +1,269 @@ + +/* SPDX-License-Identifier: BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Protected and is dual licensed, + either 'STMicroelectronics Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ****************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + ****************************************************************************** + */ + + + + + +#ifndef _VL53L1_HIST_PRIVATE_STRUCTS_H_ +#define _VL53L1_HIST_PRIVATE_STRUCTS_H_ + +#include "vl53l1_types.h" +#include "vl53l1_hist_structs.h" + +#define VL53L1_D_001 8 + +#ifdef __cplusplus +extern "C" { +#endif + + + + +typedef struct { + + uint8_t VL53L1_p_022; + + uint8_t VL53L1_p_023; + + uint8_t VL53L1_p_024; + + uint8_t VL53L1_p_030; + + int32_t VL53L1_p_020; + + + int32_t VL53L1_p_048[VL53L1_HISTOGRAM_BUFFER_SIZE]; + int32_t VL53L1_p_069[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + uint8_t VL53L1_p_043[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + int32_t VL53L1_p_008[VL53L1_HISTOGRAM_BUFFER_SIZE]; + uint16_t VL53L1_p_017[VL53L1_HISTOGRAM_BUFFER_SIZE]; + uint16_t VL53L1_p_011[VL53L1_HISTOGRAM_BUFFER_SIZE]; + +} VL53L1_hist_gen1_algo_private_data_t; + + + + +typedef struct { + + uint8_t VL53L1_p_022; + + uint8_t VL53L1_p_023; + + uint8_t VL53L1_p_024; + + uint16_t VL53L1_p_019; + + uint8_t VL53L1_p_009; + + uint8_t VL53L1_p_030; + + int32_t VL53L1_p_004; + + int32_t VL53L1_p_020; + + + int32_t VL53L1_p_003[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + int32_t VL53L1_p_018[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + int32_t VL53L1_p_001[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + + int32_t VL53L1_p_008[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + int32_t VL53L1_p_041[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + int32_t VL53L1_p_039[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + int32_t VL53L1_p_040[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + +} VL53L1_hist_gen2_algo_filtered_data_t; + + + + +typedef struct { + + uint8_t VL53L1_p_022; + + uint8_t VL53L1_p_023; + + uint8_t VL53L1_p_024; + + int32_t VL53L1_p_032; + + + uint8_t VL53L1_p_042[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + uint8_t VL53L1_p_044[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + + uint32_t VL53L1_p_017[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + uint16_t VL53L1_p_011[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + + uint8_t VL53L1_p_043[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + +} VL53L1_hist_gen2_algo_detection_data_t; + + + + +typedef struct { + + uint8_t VL53L1_p_015; + + uint8_t VL53L1_p_022; + + uint8_t VL53L1_p_025; + + uint8_t VL53L1_p_026; + + uint8_t VL53L1_p_016; + + + uint8_t VL53L1_p_027; + + uint8_t VL53L1_p_055; + + + int32_t VL53L1_p_020; + + int32_t VL53L1_p_021; + + int32_t VL53L1_p_013; + + + uint32_t VL53L1_p_028; + + uint32_t VL53L1_p_014; + + uint32_t VL53L1_p_029; + + + uint16_t VL53L1_p_005; + + +} VL53L1_hist_pulse_data_t; + + + + +typedef struct { + + uint8_t VL53L1_p_022; + + uint8_t VL53L1_p_023; + + uint8_t VL53L1_p_024; + + uint8_t VL53L1_p_031; + + uint8_t VL53L1_p_045; + + int32_t VL53L1_p_004; + + int32_t VL53L1_p_032; + + + uint8_t VL53L1_p_043[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + uint8_t VL53L1_p_046[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + uint8_t VL53L1_p_047[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + + int32_t VL53L1_p_056[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + int32_t VL53L1_p_048[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + int32_t VL53L1_p_008[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + + uint8_t VL53L1_p_049; + + uint8_t VL53L1_p_050; + + uint8_t VL53L1_p_051; + + + VL53L1_hist_pulse_data_t VL53L1_p_002[VL53L1_D_001]; + + + + + VL53L1_histogram_bin_data_t VL53L1_p_010; + + VL53L1_histogram_bin_data_t VL53L1_p_038; + + VL53L1_histogram_bin_data_t VL53L1_p_052; + + VL53L1_histogram_bin_data_t VL53L1_p_053; + + VL53L1_histogram_bin_data_t VL53L1_p_054; + + + + +} VL53L1_hist_gen3_algo_private_data_t; + + + + +typedef struct { + + uint8_t VL53L1_p_022; + + uint8_t VL53L1_p_023; + + uint8_t VL53L1_p_024; + + + int32_t VL53L1_p_003[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + int32_t VL53L1_p_018[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + int32_t VL53L1_p_001[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + + int32_t VL53L1_p_039[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + int32_t VL53L1_p_040[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + + uint8_t VL53L1_p_043[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + +} VL53L1_hist_gen4_algo_filtered_data_t; + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_sigma_estimate.h b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_sigma_estimate.h new file mode 100644 index 000000000000..9aa88da09381 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_sigma_estimate.h @@ -0,0 +1,126 @@ + +/* SPDX-License-Identifier: BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Protected and is dual licensed, + either 'STMicroelectronics Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ****************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + ****************************************************************************** + */ + + + + +#ifndef _VL53L1_SIGMA_ESTIMATE_H_ +#define _VL53L1_SIGMA_ESTIMATE_H_ + +#include "vl53l1_types.h" +#include "vl53l1_ll_def.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define VL53L1_D_002 0xFFFF + + +#define VL53L1_D_008 0xFFFF +#define VL53L1_D_003 0xFFFFFF +#define VL53L1_D_007 0xFFFFFFFF +#define VL53L1_D_005 0x7FFFFFFFFF +#define VL53L1_D_009 0xFFFFFFFFFF +#define VL53L1_D_010 0xFFFFFFFFFFFF +#define VL53L1_D_004 0xFFFFFFFFFFFFFF +#define VL53L1_D_006 0x7FFFFFFFFFFFFFFF +#define VL53L1_D_011 0xFFFFFFFFFFFFFFFF + + + + +uint16_t VL53L1_f_042( + uint8_t sigma_estimator__effective_pulse_width_ns, + uint8_t sigma_estimator__effective_ambient_width_ns, + uint8_t sigma_estimator__sigma_ref_mm, + VL53L1_range_data_t *pdata); + + + + +uint16_t VL53L1_f_044( + uint8_t sigma_estimator__effective_pulse_width_ns, + uint8_t sigma_estimator__effective_ambient_width_ns, + uint8_t sigma_estimator__sigma_ref_mm, + VL53L1_range_data_t *pdata); + + + + + + +VL53L1_Error VL53L1_f_045( + uint8_t sigma_estimator__sigma_ref_mm, + uint32_t VL53L1_p_003, + uint32_t VL53L1_p_018, + uint32_t VL53L1_p_001, + uint32_t a_zp, + uint32_t c_zp, + uint32_t bx, + uint32_t ax_zp, + uint32_t cx_zp, + uint32_t VL53L1_p_004, + uint16_t fast_osc_frequency, + uint16_t *psigma_est); + + + + + + + +VL53L1_Error VL53L1_f_014( + uint8_t sigma_estimator__sigma_ref_mm, + uint32_t VL53L1_p_003, + uint32_t VL53L1_p_018, + uint32_t VL53L1_p_001, + uint32_t a_zp, + uint32_t c_zp, + uint32_t bx, + uint32_t ax_zp, + uint32_t cx_zp, + uint32_t VL53L1_p_004, + uint16_t fast_osc_frequency, + uint16_t *psigma_est); + + + +uint32_t VL53L1_f_046( + uint64_t VL53L1_p_003, + uint32_t size + ); + + + + + +uint32_t VL53L1_f_043( + uint32_t VL53L1_p_003, + uint32_t VL53L1_p_018); + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_xtalk.h b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_xtalk.h new file mode 100644 index 000000000000..936f76c82d2a --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_xtalk.h @@ -0,0 +1,168 @@ + +/* SPDX-License-Identifier: BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Protected and is dual licensed, + either 'STMicroelectronics Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ****************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + ****************************************************************************** + */ + + + + + + + +#ifndef _VL53L1_XTALK_H_ +#define _VL53L1_XTALK_H_ + +#include "vl53l1_types.h" +#include "vl53l1_ll_def.h" +#include "vl53l1_xtalk_private_structs.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + + +VL53L1_Error VL53L1_xtalk_calibration_process_data( + VL53L1_xtalk_range_results_t *pxtalk_ranges, + VL53L1_xtalk_histogram_data_t *pxtalk_shape, + VL53L1_xtalk_calibration_results_t *pxtalk_cal); + + + + +VL53L1_Error VL53L1_f_049( + VL53L1_histogram_bin_data_t *pavg_bins, + VL53L1_xtalk_algo_data_t *pdebug, + VL53L1_xtalk_range_data_t *pxtalk_data, + uint8_t histogram__window_start, + uint8_t histogram__window_end, + VL53L1_xtalk_histogram_shape_t *pxtalk_shape); + + + +VL53L1_Error VL53L1_f_047( + VL53L1_xtalk_range_results_t *pxtalk_results, + VL53L1_xtalk_algo_data_t *pdebug, + int16_t *xgradient, + int16_t *ygradient); + + + + +VL53L1_Error VL53L1_f_048( + VL53L1_xtalk_range_data_t *pxtalk_data, + VL53L1_xtalk_algo_data_t *pdebug, + uint32_t *xtalk_mean_offset_kcps); + + + + +VL53L1_Error VL53L1_f_053( + VL53L1_histogram_bin_data_t *phist_data, + VL53L1_xtalk_range_data_t *pxtalk_data, + VL53L1_xtalk_algo_data_t *pdebug, + VL53L1_xtalk_histogram_shape_t *pxtalk_histo); + + + + + +VL53L1_Error VL53L1_f_040( + uint32_t mean_offset, + int16_t xgradient, + int16_t ygradient, + int8_t centre_offset_x, + int8_t centre_offset_y, + uint16_t roi_effective_spads, + uint8_t roi_centre_spad, + uint8_t roi_xy_size, + uint32_t *xtalk_rate_kcps); + + + + +VL53L1_Error VL53L1_f_041( + VL53L1_histogram_bin_data_t *phist_data, + VL53L1_xtalk_histogram_shape_t *pxtalk_data, + uint32_t xtalk_rate_kcps, + VL53L1_histogram_bin_data_t *pxtalkcount_data); + + + + +VL53L1_Error VL53L1_f_055( + VL53L1_histogram_bin_data_t *phist_data, + VL53L1_histogram_bin_data_t *pxtalk_data, + uint8_t xtalk_bin_offset); + + + +VL53L1_Error VL53L1_f_052( + VL53L1_histogram_bin_data_t *pxtalk_data, + uint32_t amb_threshold, + uint8_t VL53L1_p_022, + uint8_t VL53L1_p_026); + + + +VL53L1_Error VL53L1_f_054( + VL53L1_customer_nvm_managed_t *pcustomer, + VL53L1_dynamic_config_t *pdyn_cfg, + VL53L1_xtalk_histogram_data_t *pxtalk_shape, + VL53L1_histogram_bin_data_t *pip_hist_data, + VL53L1_histogram_bin_data_t *pop_hist_data, + VL53L1_histogram_bin_data_t *pxtalk_count_data); + + + + +VL53L1_Error VL53L1_f_051( + uint8_t sigma_mult, + int32_t VL53L1_p_004, + uint32_t *ambient_noise); + + + +VL53L1_Error VL53L1_generate_dual_reflectance_xtalk_samples( + VL53L1_xtalk_range_results_t *pxtalk_results, + uint16_t expected_target_distance_mm, + uint8_t higher_reflectance, + VL53L1_histogram_bin_data_t *pxtalk_avg_samples +); + + + + + +VL53L1_Error VL53L1_f_050( + VL53L1_histogram_bin_data_t *pzone_avg_1, + VL53L1_histogram_bin_data_t *pzone_avg_2, + uint16_t expected_target_distance, + uint8_t subtract_amb, + uint8_t higher_reflectance, + VL53L1_histogram_bin_data_t *pxtalk_output +); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_xtalk_private_structs.h b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_xtalk_private_structs.h new file mode 100644 index 000000000000..788ace14da0d --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/protected/inc/vl53l1_xtalk_private_structs.h @@ -0,0 +1,75 @@ + +/* SPDX-License-Identifier: BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Protected and is dual licensed, + either 'STMicroelectronics Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ****************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + ****************************************************************************** + */ + + + + + + +#ifndef _VL53L1_XTALK_PRIVATE_STRUCTS_H_ +#define _VL53L1_XTALK_PRIVATE_STRUCTS_H_ + +#include "vl53l1_types.h" +#include "vl53l1_hist_structs.h" + + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define VL53L1_D_012 4 + + + +typedef struct { + + + + + uint32_t VL53L1_p_062[VL53L1_D_012]; + + + int16_t VL53L1_p_060; + + int16_t VL53L1_p_061; + + + VL53L1_histogram_bin_data_t VL53L1_p_057; + + VL53L1_histogram_bin_data_t VL53L1_p_058; + + + + uint32_t VL53L1_p_059; + + + uint32_t VL53L1_p_063[VL53L1_XTALK_HISTO_BINS]; + + +} VL53L1_xtalk_algo_data_t; + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_dmax.c b/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_dmax.c new file mode 100644 index 000000000000..23cda26565f4 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_dmax.c @@ -0,0 +1,299 @@ + +// SPDX-License-Identifier: BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Protected and is dual licensed, + either 'STMicroelectronics Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ****************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + ****************************************************************************** + */ + + + + +#include "vl53l1_types.h" +#include "vl53l1_platform_log.h" +#include "vl53l1_platform_user_defines.h" +#include "vl53l1_core_support.h" +#include "vl53l1_error_codes.h" + +#include "vl53l1_dmax.h" + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_PROTECTED, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_PROTECTED, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_PROTECTED, \ + status, fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_PROTECTED, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_f_001( + uint16_t target_reflectance, + VL53L1_dmax_calibration_data_t *pcal, + VL53L1_hist_gen3_dmax_config_t *pcfg, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_hist_gen3_dmax_private_data_t *pdata, + int16_t *pambient_dmax_mm) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint32_t pll_period_us = 0; + uint32_t periods_elapsed = 0; + + uint32_t tmp32 = 0; + uint64_t tmp64 = 0; + + uint32_t amb_thres_delta = 0; + + LOG_FUNCTION_START(""); + + + + pdata->VL53L1_p_006 = 0x0000; + pdata->VL53L1_p_033 = 0x0000; + pdata->VL53L1_p_001 = 0x0000; + pdata->VL53L1_p_012 = 0x0000; + pdata->VL53L1_p_004 = 0x0000; + pdata->VL53L1_p_034 = 0x0000; + pdata->VL53L1_p_035 = 0; + pdata->VL53L1_p_007 = 0; + + *pambient_dmax_mm = 0; + + + if ((pbins->VL53L1_p_019 != 0) && + (pbins->total_periods_elapsed != 0)) { + + + + pll_period_us = + VL53L1_calc_pll_period_us(pbins->VL53L1_p_019); + + + + periods_elapsed = pbins->total_periods_elapsed + 1; + + + + pdata->VL53L1_p_036 = + VL53L1_duration_maths( + pll_period_us, + 1<<4, + VL53L1_RANGING_WINDOW_VCSEL_PERIODS, + periods_elapsed); + + + pdata->VL53L1_p_001 = + VL53L1_rate_maths( + pbins->VL53L1_p_004, + pdata->VL53L1_p_036); + + + + pdata->VL53L1_p_033 = + VL53L1_events_per_spad_maths( + pbins->VL53L1_p_004, + pbins->result__dss_actual_effective_spads, + pdata->VL53L1_p_036); + + + + pdata->VL53L1_p_037 = pcfg->max_effective_spads; + pdata->VL53L1_p_006 = pcfg->max_effective_spads; + + if (pdata->VL53L1_p_033 > 0) { + tmp64 = + (uint64_t)pcfg->dss_config__target_total_rate_mcps; + tmp64 *= 1000; + tmp64 <<= (11+1); + tmp32 = pdata->VL53L1_p_033/2; + tmp64 += (uint64_t)tmp32; + tmp64 = do_division_u(tmp64, + (uint64_t)pdata->VL53L1_p_033); + + if (tmp64 < (uint64_t)pcfg->max_effective_spads) + pdata->VL53L1_p_006 = (uint16_t)tmp64; + } + } + + + + if ((pcal->ref__actual_effective_spads != 0) && + (pbins->VL53L1_p_019 != 0) && + (pcal->ref_reflectance_pc != 0) && + (pbins->total_periods_elapsed != 0)) { + + + + tmp64 = (uint64_t)pcal->ref__peak_signal_count_rate_mcps; + tmp64 *= (1000 * 256); + tmp32 = pcal->ref__actual_effective_spads/2; + tmp64 += (uint64_t)tmp32; + tmp64 = do_division_u(tmp64, + (uint64_t)pcal->ref__actual_effective_spads); + + pdata->VL53L1_p_012 = (uint32_t)tmp64; + pdata->VL53L1_p_012 <<= 4; + + + + tmp64 = (uint64_t)pdata->VL53L1_p_036; + tmp64 *= (uint64_t)pdata->VL53L1_p_033; + tmp64 *= (uint64_t)pdata->VL53L1_p_006; + tmp64 += (1<<(11+7)); + tmp64 >>= (11+8); + tmp64 += 500; + tmp64 = do_division_u(tmp64, 1000); + + + if (tmp64 > 0x00FFFFFF) + tmp64 = 0x00FFFFFF; + + pdata->VL53L1_p_004 = (uint32_t)tmp64; + + + + tmp64 = (uint64_t)pdata->VL53L1_p_036; + tmp64 *= (uint64_t)pdata->VL53L1_p_012; + tmp64 *= (uint64_t)pdata->VL53L1_p_006; + tmp64 += (1<<(11+7)); + tmp64 >>= (11+8); + + + + tmp64 *= ((uint64_t)target_reflectance * + (uint64_t)pcal->coverglass_transmission); + + tmp64 += ((uint64_t)pcal->ref_reflectance_pc * 128); + tmp64 = do_division_u(tmp64, + ((uint64_t)pcal->ref_reflectance_pc * 256)); + + tmp64 += 500; + tmp64 = do_division_u(tmp64, 1000); + + + if (tmp64 > 0x00FFFFFF) + tmp64 = 0x00FFFFFF; + + pdata->VL53L1_p_034 = (uint32_t)tmp64; + + + + tmp32 = VL53L1_isqrt(pdata->VL53L1_p_004 << 8); + tmp32 *= (uint32_t)pcfg->ambient_thresh_sigma; + + + + if (pdata->VL53L1_p_004 < + (uint32_t)pcfg->min_ambient_thresh_events) { + + amb_thres_delta = + pcfg->min_ambient_thresh_events - + (uint32_t)pdata->VL53L1_p_004; + + + amb_thres_delta <<= 8; + + if (tmp32 < amb_thres_delta) + tmp32 = amb_thres_delta; + } + + + + pdata->VL53L1_p_007 = + (int16_t)VL53L1_f_002( + tmp32, + pdata->VL53L1_p_034, + (uint32_t)pcal->ref__distance_mm, + (uint32_t)pcfg->signal_thresh_sigma); + + + + tmp32 = (uint32_t)pdata->VL53L1_p_034; + tmp32 *= (uint32_t)pbins->vcsel_width; + tmp32 += (1 << 3); + tmp32 /= (1 << 4); + + pdata->VL53L1_p_035 = + (int16_t)VL53L1_f_002( + 256 * (uint32_t)pcfg->signal_total_events_limit, + tmp32, + (uint32_t)pcal->ref__distance_mm, + (uint32_t)pcfg->signal_thresh_sigma); + + + + + if (pdata->VL53L1_p_035 < pdata->VL53L1_p_007) + *pambient_dmax_mm = pdata->VL53L1_p_035; + else + *pambient_dmax_mm = pdata->VL53L1_p_007; + + } + + LOG_FUNCTION_END(status); + + return status; + +} + + +uint32_t VL53L1_f_002( + uint32_t events_threshold, + uint32_t ref_signal_events, + uint32_t ref_distance_mm, + uint32_t signal_thresh_sigma) +{ + + + + uint32_t tmp32 = 0; + uint32_t range_mm = 0; + + tmp32 = 4 * events_threshold; + + + + tmp32 += ((uint32_t)signal_thresh_sigma * + (uint32_t)signal_thresh_sigma); + + + + tmp32 = VL53L1_isqrt(tmp32); + tmp32 += (uint32_t)signal_thresh_sigma; + + + + range_mm = + (uint32_t)VL53L1_isqrt(ref_signal_events << 4); + range_mm *= ref_distance_mm; + + if (tmp32 > 0) { + range_mm += (tmp32); + range_mm /= (2*tmp32); + } + + return range_mm; + +} + diff --git a/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_hist_algos_gen3.c b/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_hist_algos_gen3.c new file mode 100644 index 000000000000..f47c2625277e --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_hist_algos_gen3.c @@ -0,0 +1,857 @@ + +// SPDX-License-Identifier: BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Protected and is dual licensed, + either 'STMicroelectronics Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ****************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + ****************************************************************************** + */ + + + + +#include "vl53l1_types.h" +#include "vl53l1_platform_log.h" + +#include "vl53l1_core_support.h" +#include "vl53l1_error_codes.h" + +#include "vl53l1_hist_core.h" +#include "vl53l1_hist_algos_gen3.h" +#include "vl53l1_sigma_estimate.h" +#include "vl53l1_dmax.h" + + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_HISTOGRAM, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_HISTOGRAM, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_HISTOGRAM,\ + status, fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_HISTOGRAM, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + + +void VL53L1_f_016( + VL53L1_hist_gen3_algo_private_data_t *palgo) +{ + + + uint8_t lb = 0; + + palgo->VL53L1_p_023 = VL53L1_HISTOGRAM_BUFFER_SIZE; + palgo->VL53L1_p_022 = 0; + palgo->VL53L1_p_024 = 0; + palgo->VL53L1_p_045 = 0; + palgo->VL53L1_p_004 = 0; + palgo->VL53L1_p_032 = 0; + + for (lb = palgo->VL53L1_p_022; lb < palgo->VL53L1_p_023; lb++) { + palgo->VL53L1_p_043[lb] = 0; + palgo->VL53L1_p_046[lb] = 0; + palgo->VL53L1_p_047[lb] = 0; + palgo->VL53L1_p_048[lb] = 0; + palgo->VL53L1_p_008[lb] = 0; + } + + palgo->VL53L1_p_049 = 0; + palgo->VL53L1_p_050 = VL53L1_D_001; + palgo->VL53L1_p_051 = 0; + + + + VL53L1_init_histogram_bin_data_struct( + 0, + VL53L1_HISTOGRAM_BUFFER_SIZE, + &(palgo->VL53L1_p_010)); + VL53L1_init_histogram_bin_data_struct( + 0, + VL53L1_HISTOGRAM_BUFFER_SIZE, + &(palgo->VL53L1_p_038)); + VL53L1_init_histogram_bin_data_struct( + 0, + VL53L1_HISTOGRAM_BUFFER_SIZE, + &(palgo->VL53L1_p_052)); + VL53L1_init_histogram_bin_data_struct( + 0, + VL53L1_HISTOGRAM_BUFFER_SIZE, + &(palgo->VL53L1_p_053)); + VL53L1_init_histogram_bin_data_struct( + 0, + VL53L1_HISTOGRAM_BUFFER_SIZE, + &(palgo->VL53L1_p_054)); +} + + + +VL53L1_Error VL53L1_f_018( + uint16_t ambient_threshold_events_scaler, + int32_t ambient_threshold_sigma, + int32_t min_ambient_threshold_events, + uint8_t algo__crosstalk_compensation_enable, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_histogram_bin_data_t *pxtalk, + VL53L1_hist_gen3_algo_private_data_t *palgo) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t lb = 0; + uint8_t VL53L1_p_001 = 0; + int64_t tmp = 0; + int32_t amb_events = 0; + int32_t VL53L1_p_008 = 0; + int32_t samples = 0; + + LOG_FUNCTION_START(""); + + + palgo->VL53L1_p_023 = pbins->VL53L1_p_023; + palgo->VL53L1_p_022 = pbins->VL53L1_p_022; + palgo->VL53L1_p_024 = pbins->VL53L1_p_024; + palgo->VL53L1_p_004 = pbins->VL53L1_p_004; + + + + palgo->VL53L1_p_031 = + VL53L1_decode_vcsel_period(pbins->VL53L1_p_009); + + + + tmp = (int64_t)pbins->VL53L1_p_004; + tmp *= (int64_t)ambient_threshold_events_scaler; + tmp += 2048; + tmp = do_division_s(tmp, 4096); + amb_events = (int32_t)tmp; + + + + for (lb = 0; lb < pbins->VL53L1_p_024; lb++) { + + VL53L1_p_001 = lb >> 2; + samples = (int32_t)pbins->bin_rep[VL53L1_p_001]; + + if (samples > 0) { + + if (lb < pxtalk->VL53L1_p_024 && + algo__crosstalk_compensation_enable > 0) + VL53L1_p_008 = samples * (amb_events + + pxtalk->bin_data[lb]); + else + VL53L1_p_008 = samples * amb_events; + + VL53L1_p_008 = VL53L1_isqrt(VL53L1_p_008); + + VL53L1_p_008 += (samples/2); + VL53L1_p_008 /= samples; + VL53L1_p_008 *= ambient_threshold_sigma; + VL53L1_p_008 += 8; + VL53L1_p_008 /= 16; + VL53L1_p_008 += amb_events; + + if (VL53L1_p_008 < min_ambient_threshold_events) + VL53L1_p_008 = min_ambient_threshold_events; + + palgo->VL53L1_p_056[lb] = VL53L1_p_008; + palgo->VL53L1_p_032 = VL53L1_p_008; + } + + + + } + + + + palgo->VL53L1_p_045 = 0; + + for (lb = pbins->VL53L1_p_022; lb < pbins->VL53L1_p_024; lb++) { + + if (pbins->bin_data[lb] > palgo->VL53L1_p_056[lb]) { + palgo->VL53L1_p_043[lb] = 1; + palgo->VL53L1_p_046[lb] = 1; + palgo->VL53L1_p_045++; + } else { + palgo->VL53L1_p_043[lb] = 0; + palgo->VL53L1_p_046[lb] = 0; + } + } + + LOG_FUNCTION_END(status); + + return status; + +} + + + + +VL53L1_Error VL53L1_f_019( + VL53L1_hist_gen3_algo_private_data_t *palgo) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i = 0; + uint8_t j = 0; + uint8_t found = 0; + + LOG_FUNCTION_START(""); + + palgo->VL53L1_p_049 = 0; + + for (i = 0; i < palgo->VL53L1_p_031; i++) { + + j = (i + 1) % palgo->VL53L1_p_031; + + + + if (i < palgo->VL53L1_p_024 && j < palgo->VL53L1_p_024) { + if (palgo->VL53L1_p_046[i] == 0 && + palgo->VL53L1_p_046[j] == 1 && + found == 0) { + palgo->VL53L1_p_049 = i; + found = 1; + } + } + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_f_020( + VL53L1_hist_gen3_algo_private_data_t *palgo) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t i = 0; + uint8_t j = 0; + uint8_t lb = 0; + + LOG_FUNCTION_START(""); + + for (lb = palgo->VL53L1_p_049; + lb < (palgo->VL53L1_p_049 + + palgo->VL53L1_p_031); + lb++) { + + + + i = lb % palgo->VL53L1_p_031; + j = (lb + 1) % palgo->VL53L1_p_031; + + + + if (i < palgo->VL53L1_p_024 && j < palgo->VL53L1_p_024) { + + if (palgo->VL53L1_p_046[i] == 0 && + palgo->VL53L1_p_046[j] == 1) + palgo->VL53L1_p_051++; + + if (palgo->VL53L1_p_051 > palgo->VL53L1_p_050) + palgo->VL53L1_p_051 = palgo->VL53L1_p_050; + + if (palgo->VL53L1_p_046[i] > 0) + palgo->VL53L1_p_047[i] = palgo->VL53L1_p_051; + else + palgo->VL53L1_p_047[i] = 0; + } + + } + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_f_021( + VL53L1_hist_gen3_algo_private_data_t *palgo) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i = 0; + uint8_t j = 0; + uint8_t blb = 0; + uint8_t pulse_no = 0; + + uint8_t max_filter_half_width = 0; + + VL53L1_hist_pulse_data_t *pdata; + + LOG_FUNCTION_START(""); + + + + max_filter_half_width = palgo->VL53L1_p_031 - 1; + max_filter_half_width = max_filter_half_width >> 1; + + for (blb = palgo->VL53L1_p_049; + blb < (palgo->VL53L1_p_049 + + palgo->VL53L1_p_031); + blb++) { + + + + i = blb % palgo->VL53L1_p_031; + j = (blb + 1) % palgo->VL53L1_p_031; + + + + if (i < palgo->VL53L1_p_024 && + j < palgo->VL53L1_p_024) { + + + + if (palgo->VL53L1_p_047[i] == 0 && + palgo->VL53L1_p_047[j] > 0) { + + pulse_no = palgo->VL53L1_p_047[j] - 1; + + if (pulse_no < palgo->VL53L1_p_050) { + pdata = &(palgo->VL53L1_p_002[pulse_no]); + pdata->VL53L1_p_015 = blb; + pdata->VL53L1_p_022 = blb + 1; + pdata->VL53L1_p_025 = 0xFF; + pdata->VL53L1_p_026 = 0; + pdata->VL53L1_p_016 = 0; + } + } + + + + if (palgo->VL53L1_p_047[i] > 0 + && palgo->VL53L1_p_047[j] == 0) { + + pulse_no = palgo->VL53L1_p_047[i] - 1; + + if (pulse_no < palgo->VL53L1_p_050) { + pdata = &(palgo->VL53L1_p_002[pulse_no]); + pdata->VL53L1_p_026 = blb; + pdata->VL53L1_p_016 = blb + 1; + + pdata->VL53L1_p_027 = + (pdata->VL53L1_p_026 + 1) - + pdata->VL53L1_p_022; + pdata->VL53L1_p_055 = + (pdata->VL53L1_p_016 + 1) - + pdata->VL53L1_p_015; + + if (pdata->VL53L1_p_055 > + max_filter_half_width) + pdata->VL53L1_p_055 = + max_filter_half_width; + } + + } + } + } + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_f_028( + VL53L1_HistTargetOrder target_order, + VL53L1_hist_gen3_algo_private_data_t *palgo) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_hist_pulse_data_t tmp; + VL53L1_hist_pulse_data_t *ptmp = &tmp; + VL53L1_hist_pulse_data_t *p0; + VL53L1_hist_pulse_data_t *p1; + + uint8_t i = 0; + uint8_t swapped = 1; + + LOG_FUNCTION_START(""); + + if (!(palgo->VL53L1_p_051 > 1)) + goto ENDFUNC; + + while (swapped > 0) { + + swapped = 0; + + for (i = 1; i < palgo->VL53L1_p_051; i++) { + + p0 = &(palgo->VL53L1_p_002[i-1]); + p1 = &(palgo->VL53L1_p_002[i]); + + + + if (target_order + == VL53L1_HIST_TARGET_ORDER__STRONGEST_FIRST) { + + if (p0->VL53L1_p_013 < + p1->VL53L1_p_013) { + + + + memcpy(ptmp, + p1, sizeof(VL53L1_hist_pulse_data_t)); + memcpy(p1, + p0, sizeof(VL53L1_hist_pulse_data_t)); + memcpy(p0, + ptmp, sizeof(VL53L1_hist_pulse_data_t)); + + swapped = 1; + } + + } else { + + if (p0->VL53L1_p_014 > p1->VL53L1_p_014) { + + + + memcpy(ptmp, + p1, sizeof(VL53L1_hist_pulse_data_t)); + memcpy(p1, + p0, sizeof(VL53L1_hist_pulse_data_t)); + memcpy(p0, + ptmp, sizeof(VL53L1_hist_pulse_data_t)); + + swapped = 1; + } + + } + } + } + +ENDFUNC: + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_f_022( + uint8_t pulse_no, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_hist_gen3_algo_private_data_t *palgo) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i = 0; + uint8_t lb = 0; + + VL53L1_hist_pulse_data_t *pdata = &(palgo->VL53L1_p_002[pulse_no]); + + LOG_FUNCTION_START(""); + + + + pdata->VL53L1_p_021 = 0; + pdata->VL53L1_p_020 = 0; + + for (lb = pdata->VL53L1_p_015; lb <= pdata->VL53L1_p_016; lb++) { + i = lb % palgo->VL53L1_p_031; + pdata->VL53L1_p_021 += pbins->bin_data[i]; + pdata->VL53L1_p_020 += palgo->VL53L1_p_004; + } + + + + pdata->VL53L1_p_013 = + pdata->VL53L1_p_021 - pdata->VL53L1_p_020; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_f_027( + uint8_t pulse_no, + uint8_t clip_events, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_hist_gen3_algo_private_data_t *palgo) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i = 0; + int16_t VL53L1_p_015 = 0; + int16_t VL53L1_p_016 = 0; + int16_t window_width = 0; + uint32_t tmp_phase = 0; + + VL53L1_hist_pulse_data_t *pdata = &(palgo->VL53L1_p_002[pulse_no]); + + LOG_FUNCTION_START(""); + + + + if (pdata->VL53L1_p_025 == 0xFF) + pdata->VL53L1_p_025 = 1; + + i = pdata->VL53L1_p_025 % palgo->VL53L1_p_031; + + VL53L1_p_015 = (int16_t)i; + VL53L1_p_015 += (int16_t)pdata->VL53L1_p_015; + VL53L1_p_015 -= (int16_t)pdata->VL53L1_p_025; + + VL53L1_p_016 = (int16_t)i; + VL53L1_p_016 += (int16_t)pdata->VL53L1_p_016; + VL53L1_p_016 -= (int16_t)pdata->VL53L1_p_025; + + + window_width = VL53L1_p_016 - VL53L1_p_015; + if (window_width > 3) + window_width = 3; + + status = + VL53L1_f_030( + VL53L1_p_015, + VL53L1_p_015 + window_width, + palgo->VL53L1_p_031, + clip_events, + pbins, + &(pdata->VL53L1_p_028)); + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_f_030( + VL53L1_p_016 - window_width, + VL53L1_p_016, + palgo->VL53L1_p_031, + clip_events, + pbins, + &(pdata->VL53L1_p_029)); + + + if (pdata->VL53L1_p_028 > pdata->VL53L1_p_029) { + tmp_phase = pdata->VL53L1_p_028; + pdata->VL53L1_p_028 = pdata->VL53L1_p_029; + pdata->VL53L1_p_029 = tmp_phase; + } + + + if (pdata->VL53L1_p_014 < pdata->VL53L1_p_028) + pdata->VL53L1_p_028 = pdata->VL53L1_p_014; + + + if (pdata->VL53L1_p_014 > pdata->VL53L1_p_029) + pdata->VL53L1_p_029 = pdata->VL53L1_p_014; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_f_030( + int16_t VL53L1_p_022, + int16_t VL53L1_p_026, + uint8_t VL53L1_p_031, + uint8_t clip_events, + VL53L1_histogram_bin_data_t *pbins, + uint32_t *pphase) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + int16_t i = 0; + int16_t lb = 0; + + int64_t VL53L1_p_008 = 0; + int64_t event_sum = 0; + int64_t weighted_sum = 0; + + LOG_FUNCTION_START(""); + + *pphase = VL53L1_MAX_ALLOWED_PHASE; + + if (VL53L1_p_031 != 0) + for (lb = VL53L1_p_022; lb <= VL53L1_p_026; lb++) { + + if (lb < 0) + i = lb + (int16_t)VL53L1_p_031; + else + i = lb % (int16_t)VL53L1_p_031; + + if ((i >= 0) && (i < VL53L1_HISTOGRAM_BUFFER_SIZE)) { + VL53L1_p_008 = + (int64_t)pbins->bin_data[i] - + (int64_t)pbins->VL53L1_p_004; + + if (clip_events > 0 && VL53L1_p_008 < 0) + VL53L1_p_008 = 0; + event_sum += VL53L1_p_008; + weighted_sum += + (VL53L1_p_008 * (1024 + (2048*(int64_t)lb))); + } + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "\tb = %5d : i = %5d : VL53L1_p_008 = %8d,", + lb, i, VL53L1_p_008); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + " event_sum = %8d, weighted_sum = %8d\n", + event_sum, weighted_sum); + } + + if (event_sum > 0) { + weighted_sum += do_division_s(event_sum, 2); + weighted_sum = do_division_s(weighted_sum, event_sum); + if (weighted_sum < 0) + weighted_sum = 0; + *pphase = (uint32_t)weighted_sum; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_f_023( + uint8_t pulse_no, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_hist_gen3_algo_private_data_t *palgo, + int32_t pad_value, + VL53L1_histogram_bin_data_t *ppulse) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i = 0; + uint8_t lb = 0; + + VL53L1_hist_pulse_data_t *pdata = &(palgo->VL53L1_p_002[pulse_no]); + + LOG_FUNCTION_START(""); + + + + memcpy(ppulse, pbins, sizeof(VL53L1_histogram_bin_data_t)); + + + + for (lb = palgo->VL53L1_p_049; + lb < (palgo->VL53L1_p_049 + + palgo->VL53L1_p_031); + lb++) { + + if (lb < pdata->VL53L1_p_015 || lb > pdata->VL53L1_p_016) { + i = lb % palgo->VL53L1_p_031; + if (i < ppulse->VL53L1_p_024) + ppulse->bin_data[i] = pad_value; + } + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_f_026( + uint8_t bin, + uint8_t sigma_estimator__sigma_ref_mm, + uint8_t VL53L1_p_031, + uint8_t VL53L1_p_055, + uint8_t crosstalk_compensation_enable, + VL53L1_histogram_bin_data_t *phist_data_ap, + VL53L1_histogram_bin_data_t *phist_data_zp, + VL53L1_histogram_bin_data_t *pxtalk_hist, + uint16_t *psigma_est) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_Error func_status = VL53L1_ERROR_NONE; + + uint8_t i = 0; + int32_t VL53L1_p_003 = 0; + int32_t VL53L1_p_018 = 0; + int32_t VL53L1_p_001 = 0; + int32_t a_zp = 0; + int32_t c_zp = 0; + int32_t ax = 0; + int32_t bx = 0; + int32_t cx = 0; + + + if (VL53L1_p_031 == 0) { + *psigma_est = 0xFFFF; + return VL53L1_ERROR_DIVISION_BY_ZERO; + } + i = bin % VL53L1_p_031; + + + + VL53L1_f_013( + i, + VL53L1_p_055, + phist_data_zp, + &a_zp, + &VL53L1_p_018, + &c_zp); + + + + VL53L1_f_013( + i, + VL53L1_p_055, + phist_data_ap, + &VL53L1_p_003, + &VL53L1_p_018, + &VL53L1_p_001); + + if (crosstalk_compensation_enable > 0) + VL53L1_f_013( + i, + VL53L1_p_055, + pxtalk_hist, + &ax, + &bx, + &cx); + + + + + + + + func_status = + VL53L1_f_014( + sigma_estimator__sigma_ref_mm, + (uint32_t)VL53L1_p_003, + (uint32_t)VL53L1_p_018, + (uint32_t)VL53L1_p_001, + (uint32_t)a_zp, + (uint32_t)c_zp, + (uint32_t)bx, + (uint32_t)ax, + (uint32_t)cx, + (uint32_t)phist_data_ap->VL53L1_p_004, + phist_data_ap->VL53L1_p_019, + psigma_est); + + + + + if (func_status == VL53L1_ERROR_DIVISION_BY_ZERO) + *psigma_est = 0xFFFF; + + + return status; +} + + +void VL53L1_f_029( + uint8_t range_id, + uint8_t valid_phase_low, + uint8_t valid_phase_high, + uint16_t sigma_thres, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_hist_pulse_data_t *ppulse, + VL53L1_range_data_t *pdata) +{ + + uint16_t lower_phase_limit = 0; + uint16_t upper_phase_limit = 0; + + + + pdata->range_id = range_id; + pdata->time_stamp = 0; + + pdata->VL53L1_p_015 = ppulse->VL53L1_p_015; + pdata->VL53L1_p_022 = ppulse->VL53L1_p_022; + pdata->VL53L1_p_025 = ppulse->VL53L1_p_025; + pdata->VL53L1_p_026 = ppulse->VL53L1_p_026; + pdata->VL53L1_p_016 = ppulse->VL53L1_p_016; + pdata->VL53L1_p_027 = ppulse->VL53L1_p_027; + + + + pdata->VL53L1_p_030 = + (ppulse->VL53L1_p_016 + 1) - ppulse->VL53L1_p_015; + + + + pdata->zero_distance_phase = pbins->zero_distance_phase; + pdata->VL53L1_p_005 = ppulse->VL53L1_p_005; + pdata->VL53L1_p_028 = (uint16_t)ppulse->VL53L1_p_028; + pdata->VL53L1_p_014 = (uint16_t)ppulse->VL53L1_p_014; + pdata->VL53L1_p_029 = (uint16_t)ppulse->VL53L1_p_029; + pdata->VL53L1_p_021 = (uint32_t)ppulse->VL53L1_p_021; + pdata->VL53L1_p_013 = ppulse->VL53L1_p_013; + pdata->VL53L1_p_020 = (uint32_t)ppulse->VL53L1_p_020; + pdata->total_periods_elapsed = pbins->total_periods_elapsed; + + + + pdata->range_status = VL53L1_DEVICEERROR_RANGECOMPLETE_NO_WRAP_CHECK; + + + if (sigma_thres > 0 && + (uint32_t)ppulse->VL53L1_p_005 > ((uint32_t)sigma_thres << 5)) + pdata->range_status = VL53L1_DEVICEERROR_SIGMATHRESHOLDCHECK; + + + + lower_phase_limit = (uint8_t)valid_phase_low << 8; + if (lower_phase_limit < pdata->zero_distance_phase) + lower_phase_limit = + pdata->zero_distance_phase - + lower_phase_limit; + else + lower_phase_limit = 0; + + upper_phase_limit = (uint8_t)valid_phase_high << 8; + upper_phase_limit += pbins->zero_distance_phase; + + if (pdata->VL53L1_p_014 < lower_phase_limit || + pdata->VL53L1_p_014 > upper_phase_limit) + pdata->range_status = VL53L1_DEVICEERROR_RANGEPHASECHECK; + +} + + diff --git a/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_hist_algos_gen4.c b/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_hist_algos_gen4.c new file mode 100644 index 000000000000..bb67b0b18ba8 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_hist_algos_gen4.c @@ -0,0 +1,558 @@ + +// SPDX-License-Identifier: BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Protected and is dual licensed, + either 'STMicroelectronics Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ****************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + ****************************************************************************** + */ + + + + +#include "vl53l1_types.h" +#include "vl53l1_platform_log.h" + +#include "vl53l1_core_support.h" +#include "vl53l1_error_codes.h" + +#include "vl53l1_hist_core.h" +#include "vl53l1_hist_algos_gen3.h" +#include "vl53l1_hist_algos_gen4.h" +#include "vl53l1_sigma_estimate.h" +#include "vl53l1_dmax.h" +#ifdef __KERNEL__ +#include +#include +#endif + + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_HISTOGRAM, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_HISTOGRAM, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_HISTOGRAM, \ + status, fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_HISTOGRAM, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + + +void VL53L1_f_032( + VL53L1_hist_gen4_algo_filtered_data_t *palgo) +{ + + + uint8_t lb = 0; + + palgo->VL53L1_p_023 = VL53L1_HISTOGRAM_BUFFER_SIZE; + palgo->VL53L1_p_022 = 0; + palgo->VL53L1_p_024 = 0; + + for (lb = palgo->VL53L1_p_022; lb < palgo->VL53L1_p_023; lb++) { + palgo->VL53L1_p_003[lb] = 0; + palgo->VL53L1_p_018[lb] = 0; + palgo->VL53L1_p_001[lb] = 0; + palgo->VL53L1_p_039[lb] = 0; + palgo->VL53L1_p_040[lb] = 0; + palgo->VL53L1_p_043[lb] = 0; + } +} + + +VL53L1_Error VL53L1_f_033( + VL53L1_dmax_calibration_data_t *pdmax_cal, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg, + VL53L1_hist_post_process_config_t *ppost_cfg, + VL53L1_histogram_bin_data_t *pbins_input, + VL53L1_histogram_bin_data_t *pxtalk, + VL53L1_hist_gen3_algo_private_data_t *palgo3, + VL53L1_hist_gen4_algo_filtered_data_t *pfiltered, + VL53L1_hist_gen3_dmax_private_data_t *pdmax_algo, + VL53L1_range_results_t *presults, + uint8_t histo_merge_nb) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_hist_pulse_data_t *ppulse_data; + VL53L1_range_data_t *prange_data; + + uint8_t p = 0; + VL53L1_histogram_bin_data_t *pB = &(palgo3->VL53L1_p_010); + + LOG_FUNCTION_START(""); + + + + + + VL53L1_f_016(palgo3); + + + + memcpy( + &(palgo3->VL53L1_p_010), + pbins_input, + sizeof(VL53L1_histogram_bin_data_t)); + + + + presults->cfg_device_state = pbins_input->cfg_device_state; + presults->rd_device_state = pbins_input->rd_device_state; + presults->zone_id = pbins_input->zone_id; + presults->stream_count = pbins_input->result__stream_count; + presults->wrap_dmax_mm = 0; + presults->max_results = VL53L1_MAX_RANGE_RESULTS; + presults->active_results = 0; + + for (p = 0; p < VL53L1_MAX_AMBIENT_DMAX_VALUES; p++) + presults->VL53L1_p_007[p] = 0; + + + + VL53L1_hist_calc_zero_distance_phase(&(palgo3->VL53L1_p_010)); + + + + VL53L1_hist_estimate_ambient_from_thresholded_bins( + (int32_t)ppost_cfg->ambient_thresh_sigma0, + &(palgo3->VL53L1_p_010)); + + VL53L1_hist_estimate_ambient_from_ambient_bins( + &(palgo3->VL53L1_p_010)); + + + VL53L1_hist_remove_ambient_bins(&(palgo3->VL53L1_p_010)); + + + if (ppost_cfg->algo__crosstalk_compensation_enable > 0) + VL53L1_f_004( + pxtalk, + &(palgo3->VL53L1_p_010), + &(palgo3->VL53L1_p_038)); + + + pdmax_cfg->ambient_thresh_sigma = + ppost_cfg->ambient_thresh_sigma1; + + for (p = 0; p < VL53L1_MAX_AMBIENT_DMAX_VALUES; p++) { + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_f_001( + pdmax_cfg->target_reflectance_for_dmax_calc[p], + pdmax_cal, + pdmax_cfg, + &(palgo3->VL53L1_p_010), + pdmax_algo, + &(presults->VL53L1_p_007[p])); + } + } + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_f_018( + ppost_cfg->ambient_thresh_events_scaler, + (int32_t)pdmax_cfg->ambient_thresh_sigma, + (int32_t)ppost_cfg->min_ambient_thresh_events, + ppost_cfg->algo__crosstalk_compensation_enable, + &(palgo3->VL53L1_p_010), + &(palgo3->VL53L1_p_038), + palgo3); + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_f_019(palgo3); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_f_020(palgo3); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_f_021(palgo3); + + + + for (p = 0; p < palgo3->VL53L1_p_051; p++) { + + ppulse_data = &(palgo3->VL53L1_p_002[p]); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_f_022( + p, + &(palgo3->VL53L1_p_010), + palgo3); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_f_023( + p, + &(palgo3->VL53L1_p_010), + palgo3, + pB->VL53L1_p_004, + &(palgo3->VL53L1_p_052)); + + + + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_f_023( + p, + &(palgo3->VL53L1_p_010), + palgo3, + 0, + &(palgo3->VL53L1_p_053)); + } + + + + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_f_023( + p, + &(palgo3->VL53L1_p_038), + palgo3, + 0, + &(palgo3->VL53L1_p_054)); + } + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_f_034( + p, + &(palgo3->VL53L1_p_052), + palgo3, + pfiltered); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_f_035( + p, + ppost_cfg->noise_threshold, + pfiltered, + palgo3); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_f_026( + ppulse_data->VL53L1_p_025, + ppost_cfg->sigma_estimator__sigma_ref_mm, + palgo3->VL53L1_p_031, + ppulse_data->VL53L1_p_055, + ppost_cfg->algo__crosstalk_compensation_enable, + &(palgo3->VL53L1_p_052), + &(palgo3->VL53L1_p_053), + &(palgo3->VL53L1_p_054), + &(ppulse_data->VL53L1_p_005)); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_f_027( + p, + 1, + &(palgo3->VL53L1_p_010), + palgo3); + + } + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_f_028( + ppost_cfg->hist_target_order, + palgo3); + + + + for (p = 0; p < palgo3->VL53L1_p_051; p++) { + + ppulse_data = &(palgo3->VL53L1_p_002[p]); + + + if (!(presults->active_results < presults->max_results)) + continue; + + + + + if (ppulse_data->VL53L1_p_013 > + ppost_cfg->signal_total_events_limit && + ppulse_data->VL53L1_p_025 < 0xFF) { + + prange_data = + &(presults->VL53L1_p_002[presults->active_results]); + + if (status == VL53L1_ERROR_NONE) + VL53L1_f_029( + presults->active_results, + ppost_cfg->valid_phase_low, + ppost_cfg->valid_phase_high, + ppost_cfg->sigma_thresh, + &(palgo3->VL53L1_p_010), + ppulse_data, + prange_data); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_f_011( + pB->vcsel_width, + pB->VL53L1_p_019, + pB->total_periods_elapsed, + pB->result__dss_actual_effective_spads, + prange_data, + histo_merge_nb); + + if (status == VL53L1_ERROR_NONE) + VL53L1_f_012( + ppost_cfg->gain_factor, + ppost_cfg->range_offset_mm, + prange_data); + + presults->active_results++; + } + + } + + + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_f_034( + uint8_t pulse_no, + VL53L1_histogram_bin_data_t *ppulse, + VL53L1_hist_gen3_algo_private_data_t *palgo3, + VL53L1_hist_gen4_algo_filtered_data_t *pfiltered) +{ + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_hist_pulse_data_t *pdata = &(palgo3->VL53L1_p_002[pulse_no]); + + uint8_t lb = 0; + uint8_t i = 0; + int32_t suma = 0; + int32_t sumb = 0; + int32_t sumc = 0; + + LOG_FUNCTION_START(""); + + pfiltered->VL53L1_p_023 = palgo3->VL53L1_p_023; + pfiltered->VL53L1_p_022 = palgo3->VL53L1_p_022; + pfiltered->VL53L1_p_024 = palgo3->VL53L1_p_024; + + + + for (lb = pdata->VL53L1_p_015; lb <= pdata->VL53L1_p_016; lb++) { + + i = lb % palgo3->VL53L1_p_031; + + + VL53L1_f_013( + i, + pdata->VL53L1_p_055, + ppulse, + &suma, + &sumb, + &sumc); + + + pfiltered->VL53L1_p_003[i] = suma; + pfiltered->VL53L1_p_018[i] = sumb; + pfiltered->VL53L1_p_001[i] = sumc; + + + + pfiltered->VL53L1_p_039[i] = + (suma + sumb) - + (sumc + palgo3->VL53L1_p_004); + + + + pfiltered->VL53L1_p_040[i] = + (sumb + sumc) - + (suma + palgo3->VL53L1_p_004); + } + + return status; +} + + +VL53L1_Error VL53L1_f_035( + uint8_t pulse_no, + uint16_t noise_threshold, + VL53L1_hist_gen4_algo_filtered_data_t *pfiltered, + VL53L1_hist_gen3_algo_private_data_t *palgo3) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_Error func_status = VL53L1_ERROR_NONE; + + VL53L1_hist_pulse_data_t *pdata = &(palgo3->VL53L1_p_002[pulse_no]); + + uint8_t lb = 0; + uint8_t i = 0; + uint8_t j = 0; + + SUPPRESS_UNUSED_WARNING(noise_threshold); + + for (lb = pdata->VL53L1_p_015; lb < pdata->VL53L1_p_016; lb++) { + + i = lb % palgo3->VL53L1_p_031; + j = (lb+1) % palgo3->VL53L1_p_031; + + if (i < palgo3->VL53L1_p_024 && + j < palgo3->VL53L1_p_024) { + + if (pfiltered->VL53L1_p_039[i] == 0 && + pfiltered->VL53L1_p_040[i] == 0) + + pfiltered->VL53L1_p_043[i] = 0; + + else if (pfiltered->VL53L1_p_039[i] >= 0 && + pfiltered->VL53L1_p_040[i] >= 0) + pfiltered->VL53L1_p_043[i] = 1; + + else if (pfiltered->VL53L1_p_039[i] < 0 && + pfiltered->VL53L1_p_040[i] >= 0 && + pfiltered->VL53L1_p_039[j] >= 0 && + pfiltered->VL53L1_p_040[j] < 0) + pfiltered->VL53L1_p_043[i] = 1; + + else + pfiltered->VL53L1_p_043[i] = 0; + + + if (pfiltered->VL53L1_p_043[i] > 0) { + + pdata->VL53L1_p_025 = lb; + + func_status = + VL53L1_f_036( + lb, + pfiltered->VL53L1_p_003[i], + pfiltered->VL53L1_p_018[i], + pfiltered->VL53L1_p_001[i], + 0, + 0, + 0, + palgo3->VL53L1_p_004, + palgo3->VL53L1_p_031, + &(pdata->VL53L1_p_014)); + + if (func_status == + VL53L1_ERROR_DIVISION_BY_ZERO) + pfiltered->VL53L1_p_043[i] = 0; + + } + } + } + + return status; +} + + +VL53L1_Error VL53L1_f_036( + uint8_t bin, + int32_t VL53L1_p_003, + int32_t VL53L1_p_018, + int32_t VL53L1_p_001, + int32_t ax, + int32_t bx, + int32_t cx, + int32_t VL53L1_p_004, + uint8_t VL53L1_p_031, + uint32_t *pmean_phase) +{ + + + VL53L1_Error status = VL53L1_ERROR_DIVISION_BY_ZERO; + + int64_t mean_phase = VL53L1_MAX_ALLOWED_PHASE; + int32_t mean_phase32; + int64_t VL53L1_p_041 = 0; + int64_t half_b_minus_amb = 0; + + + VL53L1_p_041 = 4096 * ((int64_t)VL53L1_p_001 - + (int64_t)cx - (int64_t)VL53L1_p_003 - (int64_t)ax); + half_b_minus_amb = 4096 * ((int64_t)VL53L1_p_018 - + (int64_t)bx - (int64_t)VL53L1_p_004); + + if (half_b_minus_amb != 0) { + mean_phase = (4096 * VL53L1_p_041) + half_b_minus_amb; + mean_phase = do_division_s(mean_phase, (half_b_minus_amb * 2)); + mean_phase += 2048; + mean_phase += (4096 * (int64_t)bin); + + mean_phase = do_division_s((mean_phase + 1), 2); + + if (mean_phase < 0) + mean_phase = 0; + if (mean_phase > VL53L1_MAX_ALLOWED_PHASE) + mean_phase = VL53L1_MAX_ALLOWED_PHASE; + + mean_phase32 = (int32_t)mean_phase; + mean_phase32 = mean_phase32 % + ((int32_t)VL53L1_p_031 * 2048); + mean_phase = mean_phase32; + + status = VL53L1_ERROR_NONE; + } + + *pmean_phase = (uint32_t)mean_phase; + + return status; +} + + diff --git a/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_hist_core.c b/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_hist_core.c new file mode 100644 index 000000000000..872560c64416 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_hist_core.c @@ -0,0 +1,522 @@ + +// SPDX-License-Identifier: BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Protected and is dual licensed, + either 'STMicroelectronics Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ****************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + ****************************************************************************** + */ + + + + +#include "vl53l1_platform_log.h" + +#include "vl53l1_core_support.h" +#include "vl53l1_hist_structs.h" + +#include "vl53l1_xtalk.h" +#include "vl53l1_sigma_estimate.h" + +#include "vl53l1_hist_core.h" + + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_HISTOGRAM, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_HISTOGRAM, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_HISTOGRAM, \ + status, fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_HISTOGRAM, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + + +void VL53L1_f_013( + uint8_t VL53L1_p_018, + uint8_t filter_woi, + VL53L1_histogram_bin_data_t *pbins, + int32_t *pa, + int32_t *pb, + int32_t *pc) +{ + + + uint8_t w = 0; + uint8_t j = 0; + + *pa = 0; + *pb = pbins->bin_data[VL53L1_p_018]; + *pc = 0; + + for (w = 0 ; w < ((filter_woi << 1)+1) ; w++) { + + + j = ((VL53L1_p_018 + w + pbins->VL53L1_p_024) - + filter_woi) % pbins->VL53L1_p_024; + + + if (w < filter_woi) + *pa += pbins->bin_data[j]; + else if (w > filter_woi) + *pc += pbins->bin_data[j]; + } +} + + +VL53L1_Error VL53L1_f_011( + uint16_t vcsel_width, + uint16_t fast_osc_frequency, + uint32_t total_periods_elapsed, + uint16_t VL53L1_p_006, + VL53L1_range_data_t *pdata, + uint8_t histo_merge_nb) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint32_t pll_period_us = 0; + uint32_t periods_elapsed = 0; + uint32_t count_rate_total = 0; + + LOG_FUNCTION_START(""); + + + + pdata->width = vcsel_width; + pdata->fast_osc_frequency = fast_osc_frequency; + pdata->total_periods_elapsed = total_periods_elapsed; + pdata->VL53L1_p_006 = VL53L1_p_006; + + + + if (pdata->fast_osc_frequency == 0) + status = VL53L1_ERROR_DIVISION_BY_ZERO; + + if (pdata->total_periods_elapsed == 0) + status = VL53L1_ERROR_DIVISION_BY_ZERO; + + if (status == VL53L1_ERROR_NONE) { + + + + pll_period_us = + VL53L1_calc_pll_period_us(pdata->fast_osc_frequency); + + + + periods_elapsed = pdata->total_periods_elapsed + 1; + + + + pdata->peak_duration_us = VL53L1_duration_maths( + pll_period_us, + (uint32_t)pdata->width, + VL53L1_RANGING_WINDOW_VCSEL_PERIODS, + periods_elapsed); + + pdata->woi_duration_us = VL53L1_duration_maths( + pll_period_us, + ((uint32_t)pdata->VL53L1_p_030) << 4, + VL53L1_RANGING_WINDOW_VCSEL_PERIODS, + periods_elapsed); + + + + pdata->peak_signal_count_rate_mcps = VL53L1_rate_maths( + (int32_t)pdata->VL53L1_p_013, + pdata->peak_duration_us); + + pdata->avg_signal_count_rate_mcps = VL53L1_rate_maths( + (int32_t)pdata->VL53L1_p_013, + pdata->woi_duration_us); + + pdata->ambient_count_rate_mcps = VL53L1_rate_maths( + (int32_t)pdata->VL53L1_p_020, + pdata->woi_duration_us); + + + + count_rate_total = + (uint32_t)pdata->peak_signal_count_rate_mcps + + (uint32_t)pdata->ambient_count_rate_mcps; + + if (histo_merge_nb > 1) + count_rate_total /= histo_merge_nb; + + pdata->total_rate_per_spad_mcps = + VL53L1_rate_per_spad_maths( + 0x06, + count_rate_total, + pdata->VL53L1_p_006, + 0xFFFF); + + + + pdata->VL53L1_p_012 = + VL53L1_events_per_spad_maths( + pdata->VL53L1_p_013, + pdata->VL53L1_p_006, + pdata->peak_duration_us); + + + + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %d:%-46s : %10d\n", + pdata->range_id, "peak_duration_us", + pdata->peak_duration_us); + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %d:%-46s : %10d\n", + pdata->range_id, "woi_duration_us", + pdata->woi_duration_us); + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %d:%-46s : %10u\n", + pdata->range_id, "peak_signal_count_rate_mcps", + pdata->peak_signal_count_rate_mcps); + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %d:%-46s : %10u\n", + pdata->range_id, "ambient_count_rate_mcps", + pdata->ambient_count_rate_mcps); + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %d:%-46s : %10u\n", + pdata->range_id, "total_rate_per_spad_mcps", + pdata->total_rate_per_spad_mcps); + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %d:%-46s : %10u\n", + pdata->range_id, "VL53L1_p_012", + pdata->VL53L1_p_012); + + } + + LOG_FUNCTION_END(status); + + return status; +} + + +void VL53L1_f_012( + uint16_t gain_factor, + int16_t range_offset_mm, + VL53L1_range_data_t *pdata) +{ + + + LOG_FUNCTION_START(""); + + + + pdata->min_range_mm = + (int16_t)VL53L1_range_maths( + pdata->fast_osc_frequency, + pdata->VL53L1_p_028, + pdata->zero_distance_phase, + 0, + (int32_t)gain_factor, + (int32_t)range_offset_mm); + + pdata->median_range_mm = + (int16_t)VL53L1_range_maths( + pdata->fast_osc_frequency, + pdata->VL53L1_p_014, + pdata->zero_distance_phase, + 0, + (int32_t)gain_factor, + (int32_t)range_offset_mm); + + pdata->max_range_mm = + (int16_t)VL53L1_range_maths( + pdata->fast_osc_frequency, + pdata->VL53L1_p_029, + pdata->zero_distance_phase, + 0, + (int32_t)gain_factor, + (int32_t)range_offset_mm); + + + + + + LOG_FUNCTION_END(0); +} + + +void VL53L1_f_037( + VL53L1_histogram_bin_data_t *pdata, + int32_t ambient_estimate_counts_per_bin) +{ + + + uint8_t i = 0; + + for (i = 0 ; i < pdata->VL53L1_p_024 ; i++) + pdata->bin_data[i] = pdata->bin_data[i] - + ambient_estimate_counts_per_bin; +} + + +void VL53L1_f_004( + VL53L1_histogram_bin_data_t *pxtalk, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_histogram_bin_data_t *pxtalk_realigned) +{ + + + uint8_t i = 0; + uint8_t min_bins = 0; + int8_t bin_offset = 0; + int8_t bin_access = 0; + + LOG_FUNCTION_START(""); + + + + + + memcpy( + pxtalk_realigned, + pbins, + sizeof(VL53L1_histogram_bin_data_t)); + + for (i = 0 ; i < pxtalk_realigned->VL53L1_p_023 ; i++) + pxtalk_realigned->bin_data[i] = 0; + + + + bin_offset = VL53L1_f_038( + pbins, + pxtalk); + + + + if (pxtalk->VL53L1_p_024 < pbins->VL53L1_p_024) + min_bins = pxtalk->VL53L1_p_024; + else + min_bins = pbins->VL53L1_p_024; + + + for (i = 0 ; i < min_bins ; i++) { + + + + if (bin_offset >= 0) + bin_access = ((int8_t)i + (int8_t)bin_offset) + % (int8_t)pbins->VL53L1_p_024; + else + bin_access = ((int8_t)pbins->VL53L1_p_024 + + ((int8_t)i + (int8_t)bin_offset)) + % (int8_t)pbins->VL53L1_p_024; + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + "Subtract: %8d : %8d : %8d : %8d : %8d : %8d\n", + i, bin_access, bin_offset, pbins->VL53L1_p_024, + pbins->bin_data[(uint8_t)bin_access], + pxtalk->bin_data[i]); + + + + if (pbins->bin_data[(uint8_t)bin_access] > + pxtalk->bin_data[i]) { + + pbins->bin_data[(uint8_t)bin_access] = + pbins->bin_data[(uint8_t)bin_access] + - pxtalk->bin_data[i]; + + } else { + pbins->bin_data[(uint8_t)bin_access] = 0; + } + + + + + pxtalk_realigned->bin_data[(uint8_t)bin_access] = + pxtalk->bin_data[i]; + + + + } + + + + LOG_FUNCTION_END(0); +} + + +int8_t VL53L1_f_038( + VL53L1_histogram_bin_data_t *pdata1, + VL53L1_histogram_bin_data_t *pdata2) +{ + + + int32_t phase_delta = 0; + int8_t bin_offset = 0; + uint32_t period = 0; + uint32_t remapped_phase = 0; + + LOG_FUNCTION_START(""); + + + + period = 2048 * + (uint32_t)VL53L1_decode_vcsel_period(pdata1->VL53L1_p_009); + + if (period != 0) + remapped_phase = + (uint32_t)pdata2->zero_distance_phase % period; + + + phase_delta = (int32_t)pdata1->zero_distance_phase + - (int32_t)remapped_phase; + + + + if (phase_delta > 0) + bin_offset = (int8_t)((phase_delta + 1024) / 2048); + else + bin_offset = (int8_t)((phase_delta - 1024) / 2048); + + LOG_FUNCTION_END(0); + + return bin_offset; +} + + +VL53L1_Error VL53L1_f_039( + VL53L1_histogram_bin_data_t *pidata, + VL53L1_histogram_bin_data_t *podata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t bin_initial_index[VL53L1_MAX_BIN_SEQUENCE_CODE+1]; + uint8_t bin_repeat_count[VL53L1_MAX_BIN_SEQUENCE_CODE+1]; + + uint8_t bin_cfg = 0; + uint8_t bin_seq_length = 0; + int32_t repeat_count = 0; + + uint8_t VL53L1_p_018 = 0; + uint8_t lc = 0; + uint8_t i = 0; + + LOG_FUNCTION_START(""); + + + + memcpy(podata, pidata, sizeof(VL53L1_histogram_bin_data_t)); + + + podata->VL53L1_p_024 = 0; + + for (lc = 0 ; lc < VL53L1_MAX_BIN_SEQUENCE_LENGTH ; lc++) + podata->bin_seq[lc] = VL53L1_MAX_BIN_SEQUENCE_CODE+1; + + for (lc = 0 ; lc < podata->VL53L1_p_023 ; lc++) + podata->bin_data[lc] = 0; + + + + for (lc = 0 ; lc <= VL53L1_MAX_BIN_SEQUENCE_CODE ; lc++) { + bin_initial_index[lc] = 0x00; + bin_repeat_count[lc] = 0x00; + } + + + + + bin_seq_length = 0x00; + + for (lc = 0 ; lc < VL53L1_MAX_BIN_SEQUENCE_LENGTH ; lc++) { + + bin_cfg = pidata->bin_seq[lc]; + + + + if (bin_repeat_count[bin_cfg] == 0) { + bin_initial_index[bin_cfg] = bin_seq_length * 4; + podata->bin_seq[bin_seq_length] = bin_cfg; + bin_seq_length++; + } + + bin_repeat_count[bin_cfg]++; + + + + VL53L1_p_018 = bin_initial_index[bin_cfg]; + + for (i = 0 ; i < 4 ; i++) + podata->bin_data[VL53L1_p_018+i] += + pidata->bin_data[lc*4+i]; + + } + + + + for (lc = 0 ; lc < VL53L1_MAX_BIN_SEQUENCE_LENGTH ; lc++) { + + bin_cfg = podata->bin_seq[lc]; + + if (bin_cfg <= VL53L1_MAX_BIN_SEQUENCE_CODE) + podata->bin_rep[lc] = + bin_repeat_count[bin_cfg]; + else + podata->bin_rep[lc] = 0; + } + + podata->VL53L1_p_024 = bin_seq_length * 4; + + + + + + for (lc = 0 ; lc <= VL53L1_MAX_BIN_SEQUENCE_CODE ; lc++) { + + repeat_count = (int32_t)bin_repeat_count[lc]; + + if (repeat_count > 0) { + + VL53L1_p_018 = bin_initial_index[lc]; + + for (i = 0 ; i < 4 ; i++) { + podata->bin_data[VL53L1_p_018+i] += + (repeat_count/2); + podata->bin_data[VL53L1_p_018+i] /= + repeat_count; + } + } + } + + + + podata->number_of_ambient_bins = 0; + if ((bin_repeat_count[7] > 0) || + (bin_repeat_count[15] > 0)) + podata->number_of_ambient_bins = 4; + + LOG_FUNCTION_END(status); + + return status; +} + diff --git a/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_hist_funcs.c b/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_hist_funcs.c new file mode 100644 index 000000000000..a55d90eac7a2 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_hist_funcs.c @@ -0,0 +1,293 @@ + +// SPDX-License-Identifier: BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Protected and is dual licensed, + either 'STMicroelectronics Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ****************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + ****************************************************************************** + */ + + + + + + + +#include "vl53l1_types.h" +#include "vl53l1_platform_log.h" + +#include "vl53l1_core_support.h" +#include "vl53l1_error_codes.h" +#include "vl53l1_ll_def.h" + +#include "vl53l1_hist_funcs.h" +#include "vl53l1_hist_core.h" +#include "vl53l1_hist_private_structs.h" +#include "vl53l1_dmax_private_structs.h" +#include "vl53l1_xtalk.h" +#include "vl53l1_hist_algos_gen3.h" +#include "vl53l1_hist_algos_gen4.h" +#include "vl53l1_dmax.h" + + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_HISTOGRAM, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_HISTOGRAM, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_HISTOGRAM, \ + status, fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_HISTOGRAM, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_hist_process_data( + VL53L1_dmax_calibration_data_t *pdmax_cal, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg, + VL53L1_hist_post_process_config_t *ppost_cfg, + VL53L1_histogram_bin_data_t *pbins_input, + VL53L1_xtalk_histogram_data_t *pxtalk_shape, + uint8_t *pArea1, + uint8_t *pArea2, + VL53L1_range_results_t *presults, + uint8_t *HistMergeNumber) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_hist_gen3_algo_private_data_t *palgo_gen3 = + (VL53L1_hist_gen3_algo_private_data_t *) pArea1; + VL53L1_hist_gen4_algo_filtered_data_t *pfiltered4 = + (VL53L1_hist_gen4_algo_filtered_data_t *) pArea2; + + VL53L1_hist_gen3_dmax_private_data_t dmax_algo_gen3; + VL53L1_hist_gen3_dmax_private_data_t *pdmax_algo_gen3 = + &dmax_algo_gen3; + + VL53L1_histogram_bin_data_t bins_averaged; + VL53L1_histogram_bin_data_t *pbins_averaged = &bins_averaged; + + VL53L1_range_data_t *pdata; + + uint32_t xtalk_rate_kcps = 0; + uint32_t max_xtalk_rate_per_spad_kcps = 0; + uint8_t xtalk_enable = 0; + uint8_t r = 0; + uint8_t t = 0; + uint32_t XtalkDetectMaxSigma = 0; + + + int16_t delta_mm = 0; + + + LOG_FUNCTION_START(""); + + + + VL53L1_f_039( + pbins_input, + pbins_averaged); + + + + VL53L1_init_histogram_bin_data_struct( + 0, + pxtalk_shape->xtalk_shape.VL53L1_p_024, + &(pxtalk_shape->xtalk_hist_removed)); + + + + VL53L1_copy_xtalk_bin_data_to_histogram_data_struct( + &(pxtalk_shape->xtalk_shape), + &(pxtalk_shape->xtalk_hist_removed)); + + + + if ((status == VL53L1_ERROR_NONE) && + (ppost_cfg->algo__crosstalk_compensation_enable > 0)) + status = + VL53L1_f_040( + ppost_cfg->algo__crosstalk_compensation_plane_offset_kcps, + ppost_cfg->algo__crosstalk_compensation_x_plane_gradient_kcps, + ppost_cfg->algo__crosstalk_compensation_y_plane_gradient_kcps, + 0, + 0, + pbins_input->result__dss_actual_effective_spads, + pbins_input->roi_config__user_roi_centre_spad, + pbins_input->roi_config__user_roi_requested_global_xy_size, + &(xtalk_rate_kcps)); + + + + if ((status == VL53L1_ERROR_NONE) && + (ppost_cfg->algo__crosstalk_compensation_enable > 0)) + status = + VL53L1_f_041( + pbins_averaged, + &(pxtalk_shape->xtalk_shape), + xtalk_rate_kcps, + &(pxtalk_shape->xtalk_hist_removed)); + + + + + + presults->xmonitor.total_periods_elapsed = + pbins_averaged->total_periods_elapsed; + presults->xmonitor.VL53L1_p_006 = + pbins_averaged->result__dss_actual_effective_spads; + + presults->xmonitor.peak_signal_count_rate_mcps = 0; + presults->xmonitor.VL53L1_p_012 = 0; + + presults->xmonitor.range_id = 0; + presults->xmonitor.range_status = VL53L1_DEVICEERROR_NOUPDATE; + + + + xtalk_enable = 0; + if (ppost_cfg->algo__crosstalk_compensation_enable > 0) + xtalk_enable = 1; + + + + for (r = 0 ; r <= xtalk_enable ; r++) { + + + ppost_cfg->algo__crosstalk_compensation_enable = r; + + + + status = + VL53L1_f_033( + pdmax_cal, + pdmax_cfg, + ppost_cfg, + pbins_averaged, + &(pxtalk_shape->xtalk_hist_removed), + palgo_gen3, + pfiltered4, + pdmax_algo_gen3, + presults, + *HistMergeNumber); + + + if (!(status == VL53L1_ERROR_NONE && r == 0)) + continue; + + + + if (presults->active_results == 0) { + pdata = &(presults->VL53L1_p_002[0]); + pdata->ambient_count_rate_mcps = + pdmax_algo_gen3->VL53L1_p_001; + pdata->VL53L1_p_006 = + pdmax_algo_gen3->VL53L1_p_006; + } + + + + max_xtalk_rate_per_spad_kcps = (uint32_t)( + ppost_cfg->algo__crosstalk_detect_max_valid_rate_kcps); + max_xtalk_rate_per_spad_kcps *= (uint32_t)(*HistMergeNumber); + max_xtalk_rate_per_spad_kcps <<= 4; + + for (t = 0 ; t < presults->active_results ; t++) { + + pdata = &(presults->VL53L1_p_002[t]); + + + + if (pdata->max_range_mm > pdata->min_range_mm) + delta_mm = + pdata->max_range_mm - + pdata->min_range_mm; + else + delta_mm = + pdata->min_range_mm - + pdata->max_range_mm; + + XtalkDetectMaxSigma = + ppost_cfg->algo__crosstalk_detect_max_sigma_mm; + XtalkDetectMaxSigma *= (uint32_t)(*HistMergeNumber); + XtalkDetectMaxSigma <<= 5; + if (pdata->median_range_mm > + ppost_cfg->algo__crosstalk_detect_min_valid_range_mm && + pdata->median_range_mm < + ppost_cfg->algo__crosstalk_detect_max_valid_range_mm && + pdata->VL53L1_p_012 < + max_xtalk_rate_per_spad_kcps && + pdata->VL53L1_p_005 < XtalkDetectMaxSigma && + delta_mm < + ppost_cfg->algo__crosstalk_detect_min_max_tolerance) { + + + + memcpy( + &(presults->xmonitor), + pdata, + sizeof(VL53L1_range_data_t)); + + } + } + + } + + + + ppost_cfg->algo__crosstalk_compensation_enable = xtalk_enable; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_hist_ambient_dmax( + uint16_t target_reflectance, + VL53L1_dmax_calibration_data_t *pdmax_cal, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg, + VL53L1_histogram_bin_data_t *pbins, + int16_t *pambient_dmax_mm) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_hist_gen3_dmax_private_data_t dmax_algo; + VL53L1_hist_gen3_dmax_private_data_t *pdmax_algo = &dmax_algo; + + LOG_FUNCTION_START(""); + + status = + VL53L1_f_001( + target_reflectance, + pdmax_cal, + pdmax_cfg, + pbins, + pdmax_algo, + pambient_dmax_mm); + + LOG_FUNCTION_END(status); + + return status; +} + diff --git a/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_sigma_estimate.c b/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_sigma_estimate.c new file mode 100644 index 000000000000..725436b7cfce --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_sigma_estimate.c @@ -0,0 +1,550 @@ + +// SPDX-License-Identifier: BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Protected and is dual licensed, + either 'STMicroelectronics Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ****************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + ****************************************************************************** + */ + + + + +#include "vl53l1_types.h" +#include "vl53l1_platform_log.h" + +#include "vl53l1_core_support.h" +#include "vl53l1_error_codes.h" + +#include "vl53l1_sigma_estimate.h" + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_PROTECTED, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_PROTECTED, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_PROTECTED, \ + status, fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_PROTECTED, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + + +uint16_t VL53L1_f_042( + uint8_t sigma_estimator__effective_pulse_width_ns, + uint8_t sigma_estimator__effective_ambient_width_ns, + uint8_t sigma_estimator__sigma_ref_mm, + VL53L1_range_data_t *pdata) +{ + + + + uint16_t sigma_est = VL53L1_D_002; + + uint32_t tmp0 = 0; + uint32_t tmp1 = 0; + uint32_t tmp2 = 0; + + uint32_t sigma_est__rtn_array = 0; + uint32_t sigma_est__ref_array = 0; + + LOG_FUNCTION_START(""); + + if (pdata->peak_signal_count_rate_mcps > 0 && + pdata->VL53L1_p_013 > 0) { + + + + tmp0 = 100 * + (uint32_t)sigma_estimator__effective_pulse_width_ns; + + + + tmp1 = ((uint32_t)sigma_estimator__effective_pulse_width_ns * + 100 * + (uint32_t)sigma_estimator__effective_ambient_width_ns); + + tmp1 = (tmp1 + + (uint32_t)pdata->peak_signal_count_rate_mcps/2) / + (uint32_t)pdata->peak_signal_count_rate_mcps; + + + + sigma_est__rtn_array = + VL53L1_f_043(tmp0, tmp1); + + + + sigma_est__rtn_array = + ((VL53L1_SPEED_OF_LIGHT_IN_AIR + 1000) / 2000) * + sigma_est__rtn_array; + + + + tmp2 = + VL53L1_isqrt(12 * (uint32_t)pdata->VL53L1_p_013); + + if (tmp2 > 0) { + + sigma_est__rtn_array = + (sigma_est__rtn_array + tmp2/2) / tmp2; + + + + sigma_est__ref_array = + 100 * (uint32_t)sigma_estimator__sigma_ref_mm; + + sigma_est = + (uint16_t)VL53L1_f_043( + (uint32_t)sigma_est__ref_array, + sigma_est__rtn_array); + + } else { + sigma_est = VL53L1_D_002; + } + + } + + pdata->VL53L1_p_005 = sigma_est; + + LOG_FUNCTION_END(0); + + return sigma_est; + +} + + +uint16_t VL53L1_f_044( + uint8_t sigma_estimator__effective_pulse_width_ns, + uint8_t sigma_estimator__effective_ambient_width_ns, + uint8_t sigma_estimator__sigma_ref_mm, + VL53L1_range_data_t *pdata) +{ + + + uint16_t sigma_est = VL53L1_D_002; + + uint32_t eqn7 = 0; + uint32_t sigma_est__ref_sq = 0; + uint32_t sigma_est__rtn_sq = 0; + + uint64_t tmp0 = 0; + uint64_t tmp1 = 0; + + LOG_FUNCTION_START(""); + + if (pdata->peak_signal_count_rate_mcps > 0 && + pdata->VL53L1_p_013 > 0) { + + + + eqn7 = 4573 * 4573; + eqn7 = eqn7 / (3 * (uint32_t)pdata->VL53L1_p_013); + + + + tmp0 = ((uint64_t)sigma_estimator__effective_pulse_width_ns) + << 8; + + + + tmp1 = ((uint64_t)pdata->ambient_count_rate_mcps * + (uint64_t)sigma_estimator__effective_ambient_width_ns) + << 8; + + tmp1 = do_division_u(tmp1, + (uint64_t)pdata->peak_signal_count_rate_mcps); + + + + tmp1 = 16 * (uint64_t)eqn7 * (tmp0 * tmp0 + tmp1 * tmp1); + tmp1 = do_division_u(tmp1, (15625 * 15625)); + sigma_est__rtn_sq = (uint32_t)tmp1; + + + + sigma_est__ref_sq = ((uint32_t)sigma_estimator__sigma_ref_mm) + << 2; + + sigma_est__ref_sq = sigma_est__ref_sq * sigma_est__ref_sq; + + + + sigma_est = (uint16_t)VL53L1_isqrt(sigma_est__ref_sq + + sigma_est__rtn_sq); + + } + + pdata->VL53L1_p_005 = sigma_est; + + LOG_FUNCTION_END(0); + + return sigma_est; + +} + + + +VL53L1_Error VL53L1_f_045( + uint8_t sigma_estimator__sigma_ref_mm, + uint32_t VL53L1_p_003, + uint32_t VL53L1_p_018, + uint32_t VL53L1_p_001, + uint32_t a_zp, + uint32_t c_zp, + uint32_t bx, + uint32_t ax_zp, + uint32_t cx_zp, + uint32_t VL53L1_p_004, + uint16_t fast_osc_frequency, + uint16_t *psigma_est) +{ + + + VL53L1_Error status = VL53L1_ERROR_DIVISION_BY_ZERO; + uint32_t sigma_int = VL53L1_D_002; + + uint32_t pll_period_mm = 0; + + uint64_t tmp0 = 0; + uint64_t tmp1 = 0; + uint64_t b_minus_amb = 0; + uint64_t VL53L1_p_041 = 0; + + *psigma_est = VL53L1_D_002; + + + + if (fast_osc_frequency != 0) { + + + + pll_period_mm = VL53L1_calc_pll_period_mm(fast_osc_frequency); + + + pll_period_mm = (pll_period_mm + 0x02) >> 2; + + + + if (VL53L1_p_004 > VL53L1_p_018) + b_minus_amb = (uint64_t)VL53L1_p_004 - + (uint64_t)VL53L1_p_018; + else + b_minus_amb = (uint64_t)VL53L1_p_018 - + (uint64_t)VL53L1_p_004; + + + + if (VL53L1_p_003 > VL53L1_p_001) + VL53L1_p_041 = (uint64_t)VL53L1_p_003 - + (uint64_t)VL53L1_p_001; + else + VL53L1_p_041 = (uint64_t)VL53L1_p_001 - + (uint64_t)VL53L1_p_003; + + + + + + if (b_minus_amb != 0) { + + + + tmp0 = (uint64_t)pll_period_mm * + (uint64_t)pll_period_mm; + tmp0 = tmp0 * ((uint64_t)c_zp + + (uint64_t)cx_zp + (uint64_t)a_zp + + (uint64_t)ax_zp); + tmp0 = do_division_u((tmp0 + (b_minus_amb >> 1)), + b_minus_amb); + + + + tmp1 = (uint64_t)pll_period_mm * + (uint64_t)pll_period_mm * VL53L1_p_041; + tmp1 = do_division_u((tmp1 + (b_minus_amb >> 1)), + b_minus_amb); + + tmp1 = tmp1 * VL53L1_p_041; + tmp1 = do_division_u((tmp1 + (b_minus_amb >> 1)), + b_minus_amb); + + tmp1 = tmp1 * ((uint64_t)VL53L1_p_018 + (uint64_t)bx + + (uint64_t)VL53L1_p_004); + tmp1 = do_division_u((tmp1 + (b_minus_amb >> 1)), + b_minus_amb); + + + + tmp0 = tmp0 + tmp1; + tmp0 = do_division_u((tmp0 + (b_minus_amb >> 1)), + b_minus_amb); + tmp0 = (tmp0 + 0x01) >> 2; + + + + tmp1 = (uint64_t)sigma_estimator__sigma_ref_mm << 2; + tmp1 = tmp1 * tmp1; + tmp0 = tmp0 + tmp1; + + + + if (tmp0 > 0xFFFFFFFF) + tmp0 = 0xFFFFFFFF; + + sigma_int = VL53L1_isqrt((uint32_t)tmp0); + + + + if (sigma_int > VL53L1_D_002) + *psigma_est = + (uint16_t)VL53L1_D_002; + else + *psigma_est = (uint16_t)sigma_int; + + status = VL53L1_ERROR_NONE; + } + + } + + return status; +} + + + +VL53L1_Error VL53L1_f_014( + uint8_t sigma_estimator__sigma_ref_mm, + uint32_t VL53L1_p_003, + uint32_t VL53L1_p_018, + uint32_t VL53L1_p_001, + uint32_t a_zp, + uint32_t c_zp, + uint32_t bx, + uint32_t ax_zp, + uint32_t cx_zp, + uint32_t VL53L1_p_004, + uint16_t fast_osc_frequency, + uint16_t *psigma_est) +{ + + + VL53L1_Error status = VL53L1_ERROR_DIVISION_BY_ZERO; + uint32_t sigma_int = VL53L1_D_002; + + uint32_t pll_period_mm = 0; + + uint64_t tmp0 = 0; + uint64_t tmp1 = 0; + uint64_t b_minus_amb = 0; + uint64_t VL53L1_p_041 = 0; + + *psigma_est = VL53L1_D_002; + + + + if (fast_osc_frequency != 0) { + + + + pll_period_mm = VL53L1_calc_pll_period_mm(fast_osc_frequency); + + + + if (VL53L1_p_004 > VL53L1_p_018) + b_minus_amb = (uint64_t)VL53L1_p_004 - + (uint64_t)VL53L1_p_018; + else + b_minus_amb = (uint64_t)VL53L1_p_018 - + (uint64_t)VL53L1_p_004; + + + + if (VL53L1_p_003 > VL53L1_p_001) + VL53L1_p_041 = (uint64_t)VL53L1_p_003 - + (uint64_t)VL53L1_p_001; + else + VL53L1_p_041 = (uint64_t)VL53L1_p_001 - + (uint64_t)VL53L1_p_003; + + + + if (b_minus_amb != 0) { + + + + + tmp0 = (uint64_t)VL53L1_p_018 + (uint64_t)bx + + (uint64_t)VL53L1_p_004; + if (tmp0 > VL53L1_D_003) + tmp0 = VL53L1_D_003; + + + + tmp1 = (uint64_t)VL53L1_p_041 * (uint64_t)VL53L1_p_041; + tmp1 = tmp1 << 8; + + + if (tmp1 > VL53L1_D_004) + tmp1 = VL53L1_D_004; + + + tmp1 = do_division_u(tmp1, b_minus_amb); + tmp1 = do_division_u(tmp1, b_minus_amb); + + + if (tmp1 > (uint64_t)VL53L1_D_005) + tmp1 = (uint64_t)VL53L1_D_005; + + + tmp0 = tmp1 * tmp0; + + + tmp1 = (uint64_t)c_zp + (uint64_t)cx_zp + + (uint64_t)a_zp + (uint64_t)ax_zp; + + + if (tmp1 > (uint64_t)VL53L1_D_003) + tmp1 = (uint64_t)VL53L1_D_003; + + tmp1 = tmp1 << 8; + + + tmp0 = tmp1 + tmp0; + if (tmp0 > (uint64_t)VL53L1_D_006) + tmp0 = (uint64_t)VL53L1_D_006; + + + + + + + if (tmp0 > (uint64_t)VL53L1_D_007) { + tmp0 = do_division_u(tmp0, b_minus_amb); + tmp0 = tmp0 * pll_period_mm; + } else { + tmp0 = tmp0 * pll_period_mm; + tmp0 = do_division_u(tmp0, b_minus_amb); + } + + + if (tmp0 > (uint64_t)VL53L1_D_006) + tmp0 = (uint64_t)VL53L1_D_006; + + + + if (tmp0 > (uint64_t)VL53L1_D_007) { + tmp0 = do_division_u(tmp0, b_minus_amb); + tmp0 = do_division_u(tmp0, 4); + tmp0 = tmp0 * pll_period_mm; + } else { + tmp0 = tmp0 * pll_period_mm; + tmp0 = do_division_u(tmp0, b_minus_amb); + tmp0 = do_division_u(tmp0, 4); + } + + + if (tmp0 > (uint64_t)VL53L1_D_006) + tmp0 = (uint64_t)VL53L1_D_006; + + + tmp0 = tmp0 >> 2; + + + if (tmp0 > (uint64_t)VL53L1_D_007) + tmp0 = (uint64_t)VL53L1_D_007; + + + tmp1 = (uint64_t)sigma_estimator__sigma_ref_mm << 7; + tmp1 = tmp1 * tmp1; + tmp0 = tmp0 + tmp1; + + + if (tmp0 > (uint64_t)VL53L1_D_007) + tmp0 = (uint64_t)VL53L1_D_007; + + + sigma_int = VL53L1_isqrt((uint32_t)tmp0); + + *psigma_est = (uint16_t)sigma_int; + + status = VL53L1_ERROR_NONE; + } + + } + + return status; +} + +uint32_t VL53L1_f_046( + uint64_t VL53L1_p_003, + uint32_t size + ) +{ + + + uint64_t next; + uint64_t upper; + uint64_t lower; + uint32_t stepsize; + uint32_t count; + + + next = VL53L1_p_003; + upper = 0; + lower = 0; + stepsize = size/2; + count = 0; + + while (1) { + upper = next >> stepsize; + lower = next & ((1 << stepsize) - 1); + + if (upper != 0) { + count += stepsize; + next = upper; + } else { + next = lower; + } + + stepsize = stepsize / 2; + if (stepsize == 0) + break; + } + + return count; +} + + + +uint32_t VL53L1_f_043( + uint32_t VL53L1_p_003, + uint32_t VL53L1_p_018) +{ + + + uint32_t res = 0; + + if (VL53L1_p_003 > 65535 || VL53L1_p_018 > 65535) + res = 65535; + else + res = VL53L1_isqrt(VL53L1_p_003*VL53L1_p_003 + + VL53L1_p_018*VL53L1_p_018); + + return res; +} + + diff --git a/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_xtalk.c b/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_xtalk.c new file mode 100644 index 000000000000..2ddfcb28bc24 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/protected/src/vl53l1_xtalk.c @@ -0,0 +1,1195 @@ + +// SPDX-License-Identifier: BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 Protected and is dual licensed, + either 'STMicroelectronics Proprietary license' + or 'BSD 3-clause "New" or "Revised" License' , at your option. + + ****************************************************************************** + + 'STMicroelectronics Proprietary license' + + ****************************************************************************** + + License terms: STMicroelectronics Proprietary in accordance with licensing + terms at www.st.com/sla0081 + + ****************************************************************************** + */ + + + + +#include "vl53l1_types.h" +#include "vl53l1_platform_log.h" + +#include "vl53l1_core_support.h" +#include "vl53l1_error_codes.h" + +#include "vl53l1_xtalk.h" +#include "vl53l1_hist_core.h" + + + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_HISTOGRAM, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_HISTOGRAM, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_HISTOGRAM, \ + status, fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_HISTOGRAM, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_xtalk_calibration_process_data( + VL53L1_xtalk_range_results_t *pxtalk_results, + VL53L1_xtalk_histogram_data_t *pxtalk_shape, + VL53L1_xtalk_calibration_results_t *pxtalk_cal) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_xtalk_algo_data_t xtalk_debug; + VL53L1_xtalk_algo_data_t *pdebug = &xtalk_debug; + VL53L1_xtalk_range_data_t *pxtalk_data = NULL; + + VL53L1_histogram_bin_data_t avg_bins; + VL53L1_histogram_bin_data_t *pavg_bins = &avg_bins; + + LOG_FUNCTION_START(""); + + + + memcpy(pavg_bins, &(pxtalk_results->central_histogram_avg), + sizeof(VL53L1_histogram_bin_data_t)); + + + + if (status == VL53L1_ERROR_NONE) + VL53L1_init_histogram_bin_data_struct( + 0, 0, &(pdebug->VL53L1_p_057)); + + if (status == VL53L1_ERROR_NONE) + VL53L1_init_histogram_bin_data_struct( + 0, 0, &(pdebug->VL53L1_p_058)); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_f_047( + pxtalk_results, + pdebug, + &(pxtalk_cal->algo__crosstalk_compensation_x_plane_gradient_kcps + ), + &(pxtalk_cal->algo__crosstalk_compensation_y_plane_gradient_kcps + )); + + + + + + if (status != VL53L1_ERROR_NONE) + goto ENDFUNC; + + pxtalk_data = &(pxtalk_results->VL53L1_p_002[4]); + + if (pxtalk_data->no_of_samples > 0) { + + + + if (status == VL53L1_ERROR_NONE) { + memcpy(&(pdebug->VL53L1_p_057), + pavg_bins, + sizeof(VL53L1_histogram_bin_data_t)); + } + + + + status = VL53L1_f_048( + pxtalk_data, + pdebug, + &(pxtalk_cal->algo__crosstalk_compensation_plane_offset_kcps)); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_f_049( + pavg_bins, + pdebug, + pxtalk_data, + pxtalk_results->central_histogram__window_start, + pxtalk_results->central_histogram__window_end, + &(pxtalk_shape->xtalk_shape)); + + } else { + + + + pxtalk_cal->algo__crosstalk_compensation_plane_offset_kcps = 0; + + + + pdebug->VL53L1_p_059 = 0; + + + } + + +ENDFUNC: + + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_generate_dual_reflectance_xtalk_samples( + VL53L1_xtalk_range_results_t *pxtalk_results, + uint16_t expected_target_distance_mm, + uint8_t higher_reflectance, + VL53L1_histogram_bin_data_t *pxtalk_avg_samples +) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_histogram_bin_data_t *pzone_avg_1 = + &(pxtalk_results->histogram_avg_1[0]); + VL53L1_histogram_bin_data_t *pzone_avg_2 = + &(pxtalk_results->histogram_avg_2[0]); + + VL53L1_histogram_bin_data_t *pxtalk_output = pxtalk_avg_samples; + + + + int i = 0; + + + + for (i = 0 ; i < 5 ; i++) { + + if (status == VL53L1_ERROR_NONE) + VL53L1_init_histogram_bin_data_struct( + 0, 0, pzone_avg_1); + + if (status == VL53L1_ERROR_NONE) + VL53L1_init_histogram_bin_data_struct( + 0, 0, pzone_avg_2); + + pzone_avg_1++; + pzone_avg_2++; + } + + + + + pzone_avg_1 = &(pxtalk_results->histogram_avg_1[0]); + pzone_avg_2 = &(pxtalk_results->histogram_avg_2[0]); + + for (i = 0 ; i < 5 ; i++) { + + if (status == VL53L1_ERROR_NONE) { + + status = VL53L1_f_050( + pzone_avg_1, + pzone_avg_2, + expected_target_distance_mm, + 0x01, + higher_reflectance, + pxtalk_output + ); + + + + pzone_avg_1++; + pzone_avg_2++; + pxtalk_output++; + + } + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_f_050( + VL53L1_histogram_bin_data_t *pzone_avg_1, + VL53L1_histogram_bin_data_t *pzone_avg_2, + uint16_t expected_target_distance, + uint8_t subtract_amb, + uint8_t higher_reflectance, + VL53L1_histogram_bin_data_t *pxtalk_output +) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_histogram_bin_data_t zone_avg_realigned; + + + SUPPRESS_UNUSED_WARNING(pxtalk_output); + SUPPRESS_UNUSED_WARNING(expected_target_distance); + + + + if ((status == VL53L1_ERROR_NONE) && (subtract_amb == 0x01)) { + VL53L1_f_037( + pzone_avg_1, + pzone_avg_1->VL53L1_p_004); + + + pzone_avg_1->VL53L1_p_004 = 0x0; + } + + if ((status == VL53L1_ERROR_NONE) && (subtract_amb == 0x01)) { + VL53L1_f_037( + pzone_avg_2, + pzone_avg_2->VL53L1_p_004); + + + pzone_avg_2->VL53L1_p_004 = 0x0; + } + + + + + + if (status == VL53L1_ERROR_NONE) { + if (higher_reflectance == 0x01) { + VL53L1_f_004( + pzone_avg_2, + pzone_avg_1, + &zone_avg_realigned); + } else { + + + + VL53L1_f_004( + pzone_avg_1, + pzone_avg_2, + &zone_avg_realigned); + + + + } + } + + + + + + + + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_f_049( + VL53L1_histogram_bin_data_t *pavg_bins, + VL53L1_xtalk_algo_data_t *pdebug, + VL53L1_xtalk_range_data_t *pxtalk_data, + uint8_t histogram__window_start, + uint8_t histogram__window_end, + VL53L1_xtalk_histogram_shape_t *pxtalk_shape) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + + + uint32_t ambient_thresh = 0; + + + + if (status == VL53L1_ERROR_NONE) + VL53L1_f_037( + pavg_bins, + pavg_bins->VL53L1_p_004); + + + + if (status == VL53L1_ERROR_NONE) + VL53L1_f_051( + 6, + pavg_bins->VL53L1_p_004, + &ambient_thresh); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_f_052( + pavg_bins, + ambient_thresh, + histogram__window_start, + histogram__window_end); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_f_053( + pavg_bins, + pxtalk_data, + pdebug, + pxtalk_shape); + + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_f_047( + VL53L1_xtalk_range_results_t *pxtalk_results, + VL53L1_xtalk_algo_data_t *pdebug, + int16_t *xgradient, + int16_t *ygradient + ) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_xtalk_range_data_t *presults_int = NULL; + + int i = 0; + + uint32_t xtalk_per_spad[4]; + int32_t VL53L1_p_060 = 0; + int32_t VL53L1_p_061 = 0; + + uint8_t result_invalid = 0; + + + LOG_FUNCTION_START(""); + + + + *xgradient = 0; + *ygradient = 0; + + + + + for (i = 0; i < 4; i++) + xtalk_per_spad[i] = 0; + + + + for (i = 0; i < 4; i++) { + + if (status == VL53L1_ERROR_NONE) { + + presults_int = &(pxtalk_results->VL53L1_p_002[i]); + + + + + if (presults_int->no_of_samples == 0) { + + + result_invalid = 1; + pdebug->VL53L1_p_062[i] = 0; + + + } else { + + xtalk_per_spad[i] = + presults_int->rate_per_spad_kcps_avg; + + + + pdebug->VL53L1_p_062[i] = + (uint32_t)xtalk_per_spad[i]; + + } + } + + } + + + + if ((status == VL53L1_ERROR_NONE) && (result_invalid == 0)) { + + + + if (status == VL53L1_ERROR_NONE) { + VL53L1_p_060 = ((int32_t)xtalk_per_spad[1] + - (int32_t)xtalk_per_spad[0]) / (8); + VL53L1_p_061 = ((int32_t)xtalk_per_spad[3] + - (int32_t)xtalk_per_spad[2]) / (8); + } + + + + + if (status == VL53L1_ERROR_NONE) { + if (VL53L1_p_060 < -32767) { + VL53L1_p_060 = -32767; + } else { + if (VL53L1_p_060 > 32767) + VL53L1_p_060 = 32767; + } + + if (VL53L1_p_061 < -32767) { + VL53L1_p_061 = -32767; + } else { + if (VL53L1_p_061 > 32767) + VL53L1_p_061 = 32767; + } + + + + pdebug->VL53L1_p_060 = (int16_t)VL53L1_p_060; + pdebug->VL53L1_p_061 = (int16_t)VL53L1_p_061; + } + + } else { + + + + VL53L1_p_060 = 0; + VL53L1_p_061 = 0; + + pdebug->VL53L1_p_060 = 0; + pdebug->VL53L1_p_061 = 0; + } + + + + if (status == VL53L1_ERROR_NONE) { + *xgradient = (int16_t)VL53L1_p_060; + *ygradient = (int16_t)VL53L1_p_061; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_f_048( + VL53L1_xtalk_range_data_t *pxtalk_data, + VL53L1_xtalk_algo_data_t *pdebug, + uint32_t *xtalk_mean_offset_kcps + ) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint32_t xtalk_per_spad = 0; + uint8_t result_invalid = 0; + + LOG_FUNCTION_START(""); + + *xtalk_mean_offset_kcps = 0; + + + if (pxtalk_data->no_of_samples == 0) { + + + + result_invalid = 1; + + + + pdebug->VL53L1_p_059 = 0; + + } + + + + + if ((status == VL53L1_ERROR_NONE) && (result_invalid == 0)) { + + + + xtalk_per_spad = pxtalk_data->rate_per_spad_kcps_avg >> 2; + + + + pdebug->VL53L1_p_059 = xtalk_per_spad; + + + if (xtalk_per_spad < 0x3FFFF) + *xtalk_mean_offset_kcps = (uint32_t)xtalk_per_spad; + else + *xtalk_mean_offset_kcps = 0x3FFFF; + + } else { + + + + *xtalk_mean_offset_kcps = 0; + } + + LOG_FUNCTION_END(status); + + return status; + +} + + + + +VL53L1_Error VL53L1_f_053( + VL53L1_histogram_bin_data_t *phist_data, + VL53L1_xtalk_range_data_t *pxtalk_data, + VL53L1_xtalk_algo_data_t *pdebug, + VL53L1_xtalk_histogram_shape_t *pxtalk_histo + ) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t idx; + int32_t tmpi32; + uint8_t i = 0; + uint64_t bin_data[VL53L1_XTALK_HISTO_BINS]; + + LOG_FUNCTION_START(""); + + + + + + + pxtalk_histo->VL53L1_p_023 = + phist_data->VL53L1_p_023; + pxtalk_histo->cal_config__vcsel_start = + phist_data->cal_config__vcsel_start; + pxtalk_histo->VL53L1_p_019 = + phist_data->VL53L1_p_019; + pxtalk_histo->VL53L1_p_022 = + phist_data->VL53L1_p_022; + pxtalk_histo->time_stamp = + phist_data->time_stamp; + pxtalk_histo->vcsel_width = + phist_data->vcsel_width; + pxtalk_histo->zero_distance_phase = + phist_data->zero_distance_phase; + pxtalk_histo->zone_id = + phist_data->zone_id; + pxtalk_histo->VL53L1_p_024 = + VL53L1_XTALK_HISTO_BINS; + pxtalk_histo->phasecal_result__reference_phase = + phist_data->phasecal_result__reference_phase; + pxtalk_histo->phasecal_result__vcsel_start = + phist_data->phasecal_result__vcsel_start; + + memcpy(&(pdebug->VL53L1_p_058), + phist_data, sizeof(VL53L1_histogram_bin_data_t)); + + + + + + if (pxtalk_data->signal_total_events_avg == 0) { + for (i = 0; i < pxtalk_histo->VL53L1_p_024; i++) + bin_data[i] = 0; + goto FAIL; + } + + for (i = 0; i < pxtalk_histo->VL53L1_p_024; i++) { + idx = i + phist_data->number_of_ambient_bins; + if (phist_data->bin_data[idx] > 0) { + bin_data[i] = + ((uint64_t)phist_data->bin_data[idx] << 10); + tmpi32 = pxtalk_data->signal_total_events_avg / 2; + bin_data[i] = bin_data[i] + (uint64_t)tmpi32; + bin_data[i] = do_division_u(bin_data[i], + (uint64_t)pxtalk_data->signal_total_events_avg); + } else { + bin_data[i] = 0; + } + } + +FAIL: + + + for (i = 0; i < VL53L1_XTALK_HISTO_BINS; i++) + pxtalk_histo->bin_data[i] = (uint32_t)bin_data[i]; + + + + for (i = 0; i < pxtalk_histo->VL53L1_p_024; i++) + pdebug->VL53L1_p_063[i] = pxtalk_histo->bin_data[i]; + + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_f_054( + VL53L1_customer_nvm_managed_t *pcustomer, + VL53L1_dynamic_config_t *pdyn_cfg, + VL53L1_xtalk_histogram_data_t *pxtalk_shape, + VL53L1_histogram_bin_data_t *pip_hist_data, + VL53L1_histogram_bin_data_t *pop_hist_data, + VL53L1_histogram_bin_data_t *pxtalk_count_data) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + + + uint32_t xtalk_rate_kcps = 0; + + LOG_FUNCTION_START(""); + + + + memcpy(pop_hist_data, pip_hist_data, + sizeof(VL53L1_histogram_bin_data_t)); + + + + status = + VL53L1_f_040( + pcustomer->algo__crosstalk_compensation_plane_offset_kcps, + pcustomer->algo__crosstalk_compensation_x_plane_gradient_kcps, + pcustomer->algo__crosstalk_compensation_y_plane_gradient_kcps, + 0, + 0, + pip_hist_data->result__dss_actual_effective_spads, + + pdyn_cfg->roi_config__user_roi_centre_spad, + pdyn_cfg->roi_config__user_roi_requested_global_xy_size, + &(xtalk_rate_kcps)); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_f_041( + pip_hist_data, + &(pxtalk_shape->xtalk_shape), + xtalk_rate_kcps, + pxtalk_count_data); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_f_055( + pop_hist_data, + pxtalk_count_data, + pip_hist_data->number_of_ambient_bins); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_f_040( + uint32_t mean_offset, + int16_t xgradient, + int16_t ygradient, + int8_t centre_offset_x, + int8_t centre_offset_y, + uint16_t roi_effective_spads, + uint8_t roi_centre_spad, + uint8_t roi_xy_size, + uint32_t *xtalk_rate_kcps + ) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t row = 0; + uint8_t col = 0; + + + + int16_t bound_l_x = 0; + int16_t bound_r_x = 0; + int16_t bound_u_y = 0; + int16_t bound_d_y = 0; + + int64_t xtalk_rate_ll = 0; + int64_t xtalk_rate_ur = 0; + + int64_t xtalk_avg = 0; + + LOG_FUNCTION_START(""); + + SUPPRESS_UNUSED_WARNING(roi_effective_spads); + + + + + + + if (status == VL53L1_ERROR_NONE) { + VL53L1_decode_row_col( + roi_centre_spad, + &row, + &col); + } + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "Row", row); + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "Col", col); + + + + if (status == VL53L1_ERROR_NONE) { + if ((((int16_t)roi_xy_size / 16) & 0x01) == 1) + bound_l_x = (int16_t) col - + (((int16_t)roi_xy_size / 32) + 1); + else + bound_l_x = (int16_t) col - + ((int16_t)roi_xy_size / 32); + + bound_r_x = (int16_t) col + ((int16_t)roi_xy_size / 32); + + if ((((int16_t)roi_xy_size) & 0x01) == 1) + bound_d_y = (int16_t) row - + ((((int16_t)roi_xy_size & 0x0f) / 2) + 1); + else + bound_d_y = (int16_t) row - + (((int16_t)roi_xy_size & 0x0f) / 2); + + bound_u_y = (int16_t) row + + (((int16_t)roi_xy_size & 0xf) / 2); + } + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "Bound_l_x", bound_l_x); + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "Bound_r_x", bound_r_x); + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "Bound_u_y", bound_u_y); + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "Bound_d_y", bound_d_y); + + + + + if (status == VL53L1_ERROR_NONE) { + bound_l_x = (2 * bound_l_x) - 15 + + (2 * (int16_t)centre_offset_x); + bound_r_x = (2 * bound_r_x) - 15 + + (2 * (int16_t)centre_offset_x); + bound_u_y = (2 * bound_u_y) - 15 + + (2 * (int16_t)centre_offset_y); + bound_d_y = (2 * bound_d_y) - 15 + + (2 * (int16_t)centre_offset_y); + } + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "Bound_l_x", bound_l_x); + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "Bound_r_x", bound_r_x); + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "Bound_u_y", bound_u_y); + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "Bound_d_y", bound_d_y); + + + + + if (status == VL53L1_ERROR_NONE) { + xtalk_rate_ll = ((int64_t)bound_l_x * + ((int64_t)xgradient)) + ((int64_t)bound_d_y * + ((int64_t)ygradient)); + xtalk_rate_ll = do_division_s((xtalk_rate_ll + 1), 2); + xtalk_rate_ll += ((int64_t)mean_offset * 4); + + xtalk_rate_ur = ((int64_t)bound_r_x * + ((int64_t)xgradient)) + ((int64_t)bound_u_y * + ((int64_t)ygradient)); + xtalk_rate_ur = do_division_s((xtalk_rate_ur + 1), 2); + xtalk_rate_ur += ((int64_t)mean_offset * 4); + } + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "xtalk_rate_ll", xtalk_rate_ll); + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "xtalk_rate_ur", xtalk_rate_ur); + + + + if (status == VL53L1_ERROR_NONE) + xtalk_avg = do_division_s( + ((xtalk_rate_ll + xtalk_rate_ur) + 1), 2); + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "xtalk_avg", xtalk_avg); + + + + if (status == VL53L1_ERROR_NONE) + if (xtalk_avg < 0) + xtalk_avg = 0; + + + + + + *xtalk_rate_kcps = (uint32_t) xtalk_avg; + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "xtalk_rate_kcps", xtalk_avg); + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_f_041( + VL53L1_histogram_bin_data_t *phist_data, + VL53L1_xtalk_histogram_shape_t *pxtalk_data, + uint32_t xtalk_rate_kcps, + VL53L1_histogram_bin_data_t *pxtalkcount_data + ) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint64_t xtalk_events_per_spad = 0; + uint64_t xtalk_total_events = 0; + uint64_t xtalk_temp_bin = 0; + + uint8_t i = 0; + + LOG_FUNCTION_START(""); + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "pk_duration_internal", phist_data->peak_duration_us); + + + + xtalk_events_per_spad = do_division_u((((uint64_t)xtalk_rate_kcps * + (uint64_t)phist_data->peak_duration_us) + 500), 1000); + + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "xtalk_events_per_spad", xtalk_events_per_spad); + + + + + xtalk_total_events = xtalk_events_per_spad * + (uint64_t)phist_data->result__dss_actual_effective_spads; + + xtalk_total_events = do_division_u((xtalk_total_events), 256); + + xtalk_total_events = do_division_u((xtalk_total_events + 1024), 2048); + + if (xtalk_total_events > 0xFFFFFFFF) + xtalk_total_events = 0xFFFFFFFF; + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "xtalk_total_events", xtalk_total_events); + + + + + + + for (i = 0; i < pxtalk_data->VL53L1_p_024; i++) { + xtalk_temp_bin = (uint64_t)pxtalk_data->bin_data[i] * + (uint64_t)xtalk_total_events; + xtalk_temp_bin = do_division_u((xtalk_temp_bin + 512), 1024); + pxtalkcount_data->bin_data[i] = (uint32_t)xtalk_temp_bin; + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "bin_data", pxtalkcount_data->bin_data[i]); + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_f_055( + VL53L1_histogram_bin_data_t *phist_data, + VL53L1_histogram_bin_data_t *pxtalk_data, + uint8_t xtalk_bin_offset) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i = 0; + + int32_t temp_bin; + + LOG_FUNCTION_START(""); + + + + + if (status == VL53L1_ERROR_NONE) + for (i = xtalk_bin_offset; + i < pxtalk_data->VL53L1_p_024; i++) { + + temp_bin = (int32_t)phist_data->bin_data[i] - + (int32_t)pxtalk_data->bin_data[i - xtalk_bin_offset]; + + if (temp_bin < 0) + temp_bin = 0; + + phist_data->bin_data[i] = (uint32_t)temp_bin; + } + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_f_052( + VL53L1_histogram_bin_data_t *pxtalk_data, + uint32_t amb_threshold, + uint8_t VL53L1_p_022, + uint8_t VL53L1_p_026) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i = 0; + uint8_t first_bin_int = 0; + uint8_t first_bin_inc = 0; + uint8_t last_bin_int = 0; + uint8_t realign_bin = 0; + uint8_t realign_index = 0; + int32_t realign_bin_data[VL53L1_HISTOGRAM_BUFFER_SIZE]; + + LOG_FUNCTION_START(""); + + + + for (i = 0 ; i < VL53L1_HISTOGRAM_BUFFER_SIZE ; i++) + realign_bin_data[i] = 0; + + first_bin_int = VL53L1_p_022; + last_bin_int = VL53L1_p_026; + + + + + + VL53L1_hist_remove_ambient_bins(pxtalk_data); + + + + first_bin_int = (first_bin_int) % + pxtalk_data->VL53L1_p_024; + + last_bin_int = (last_bin_int) % + pxtalk_data->VL53L1_p_024; + + first_bin_inc = (first_bin_int + 1) % pxtalk_data->VL53L1_p_024; + + + + if (first_bin_inc > last_bin_int) { + + + realign_bin = pxtalk_data->VL53L1_p_024 - first_bin_inc; + + + first_bin_int = (first_bin_int + realign_bin) % + pxtalk_data->VL53L1_p_024; + last_bin_int = (last_bin_int + realign_bin) % + pxtalk_data->VL53L1_p_024; + + + pxtalk_data->zero_distance_phase = + pxtalk_data->zero_distance_phase + + ((uint16_t)realign_bin * 2048); + } + + if (realign_bin > 0) { + + for (i = 0; i < pxtalk_data->VL53L1_p_024; i++) + realign_bin_data[i] = pxtalk_data->bin_data[i]; + + + for (i = 0; i < pxtalk_data->VL53L1_p_024; i++) { + realign_index = (pxtalk_data->VL53L1_p_024 - + realign_bin + i) + % pxtalk_data->VL53L1_p_024; + + pxtalk_data->bin_data[i] = + realign_bin_data[realign_index]; + } + } + + + + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "first bin int", first_bin_int); + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "last bin int", last_bin_int); + + trace_print( + VL53L1_TRACE_LEVEL_DEBUG, + " %-48s : %10d\n", + "amb thresh", amb_threshold); + + + + + + for (i = 0; i < pxtalk_data->VL53L1_p_024; i++) { + + if (first_bin_int <= last_bin_int) { + if ((i >= first_bin_int) && (i <= last_bin_int)) { + if (pxtalk_data->bin_data[i] < + (int32_t)amb_threshold) + pxtalk_data->bin_data[i] = 0; + } else { + pxtalk_data->bin_data[i] = 0; + } + } else { + if ((i >= first_bin_int) || (i <= last_bin_int)) { + if (pxtalk_data->bin_data[i] < + (int32_t)amb_threshold) { + pxtalk_data->bin_data[i] = 0; + } + } else { + pxtalk_data->bin_data[i] = 0; + } + } + } + + + + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_f_051( + uint8_t sigma_mult, + int32_t VL53L1_p_004, + uint32_t *ambient_noise) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint32_t ambient_events_per_bin_int = 0; + + LOG_FUNCTION_START(""); + + if (VL53L1_p_004 <= 0) + ambient_events_per_bin_int = 1; + else + ambient_events_per_bin_int = (uint32_t)VL53L1_p_004; + + *ambient_noise = VL53L1_isqrt(ambient_events_per_bin_int); + + *ambient_noise = *ambient_noise * (uint32_t)sigma_mult; + + LOG_FUNCTION_END(status); + + return status; +} + + + + + + + + + + + + + + + + + + + + diff --git a/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_api.c b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_api.c new file mode 100644 index 000000000000..2674dfab10e9 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_api.c @@ -0,0 +1,3999 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#include "vl53l1_api.h" +#include "vl53l1_api_strings.h" +#include "vl53l1_register_settings.h" +#include "vl53l1_register_funcs.h" +#include "vl53l1_core.h" +#include "vl53l1_api_calibration.h" +#include "vl53l1_wait.h" +#include "vl53l1_preset_setup.h" +#include "vl53l1_api_debug.h" +#include "vl53l1_api_core.h" +#include "vl53l1_nvm.h" + + +#define ZONE_CHECK VL53L1_MAX_USER_ZONES + +#if ZONE_CHECK < 5 +#error Must define at least 5 zones in MAX_USER_ZONES constant +#endif + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_API, status, \ + fmt, ##__VA_ARGS__) + +#ifdef VL53L1_LOG_ENABLE +#define trace_print(level, ...) trace_print_module_function(\ + VL53L1_TRACE_MODULE_API, level, VL53L1_TRACE_FUNCTION_NONE, \ + ##__VA_ARGS__) +#endif + +#ifndef MIN +#define MIN(v1, v2) ((v1) < (v2) ? (v1) : (v2)) +#endif +#ifndef MAX +#define MAX(v1, v2) ((v1) < (v2) ? (v2) : (v1)) +#endif + +#define DMAX_REFLECTANCE_IDX 2 + + + +#define LOWPOWER_AUTO_VHV_LOOP_DURATION_US 245 +#define LOWPOWER_AUTO_OVERHEAD_BEFORE_A_RANGING 1448 +#define LOWPOWER_AUTO_OVERHEAD_BETWEEN_A_B_RANGING 2100 + +#define FDA_MAX_TIMING_BUDGET_US 550000 + + + + + + +static int32_t BDTable[VL53L1_TUNING_MAX_TUNABLE_KEY] = { + TUNING_VERSION, + TUNING_PROXY_MIN, + TUNING_SINGLE_TARGET_XTALK_TARGET_DISTANCE_MM, + TUNING_SINGLE_TARGET_XTALK_SAMPLE_NUMBER, + TUNING_MIN_AMBIENT_DMAX_VALID, + TUNING_MAX_SIMPLE_OFFSET_CALIBRATION_SAMPLE_NUMBER, + TUNING_XTALK_FULL_ROI_TARGET_DISTANCE_MM, + TUNING_SIMPLE_OFFSET_CALIBRATION_REPEAT, + TUNING_XTALK_FULL_ROI_BIN_SUM_MARGIN, + TUNING_XTALK_FULL_ROI_DEFAULT_OFFSET, + TUNING_ZERO_DISTANCE_OFFSET_NON_LINEAR_FACTOR_DEFAULT, +}; + + +static VL53L1_Error SingleTargetXTalkCalibration(VL53L1_DEV Dev) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + uint32_t sum_ranging = 0; + uint32_t sum_spads = 0; + FixPoint1616_t sum_signalRate = 0; + FixPoint1616_t total_count = 0; + uint8_t xtalk_meas = 0; + uint8_t xtalk_measmax = + BDTable[VL53L1_TUNING_SINGLE_TARGET_XTALK_SAMPLE_NUMBER]; + VL53L1_RangingMeasurementData_t RMData; + FixPoint1616_t xTalkStoredMeanSignalRate; + FixPoint1616_t xTalkStoredMeanRange; + FixPoint1616_t xTalkStoredMeanRtnSpads; + uint32_t xTalkStoredMeanRtnSpadsAsInt; + uint32_t xTalkCalDistanceAsInt; + FixPoint1616_t XTalkCompensationRateMegaCps; + uint32_t signalXTalkTotalPerSpad; + VL53L1_PresetModes PresetMode; + VL53L1_CalibrationData_t CalibrationData; + VL53L1_CustomerNvmManaged_t *pC; + + + LOG_FUNCTION_START(""); + + + PresetMode = VL53L1DevDataGet(Dev, CurrentParameters.PresetMode); + + if ((PresetMode != VL53L1_PRESETMODE_AUTONOMOUS) && + (PresetMode != VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS) && + (PresetMode != VL53L1_PRESETMODE_LITE_RANGING)) { + Status = VL53L1_ERROR_MODE_NOT_SUPPORTED; + goto ENDFUNC; + } + + + Status = VL53L1_disable_xtalk_compensation(Dev); + + if (Status != VL53L1_ERROR_NONE) + goto ENDFUNC; + + Status = VL53L1_StartMeasurement(Dev); + + if (Status != VL53L1_ERROR_NONE) + goto ENDFUNC; + + + VL53L1_WaitMeasurementDataReady(Dev); + VL53L1_GetRangingMeasurementData(Dev, &RMData); + VL53L1_ClearInterruptAndStartMeasurement(Dev); + + sum_ranging = 0; + sum_spads = 0; + sum_signalRate = 0; + total_count = 0; + for (xtalk_meas = 0; xtalk_meas < xtalk_measmax; xtalk_meas++) { + VL53L1_WaitMeasurementDataReady(Dev); + VL53L1_GetRangingMeasurementData(Dev, &RMData); + VL53L1_ClearInterruptAndStartMeasurement(Dev); + if (RMData.RangeStatus == VL53L1_RANGESTATUS_RANGE_VALID) { + sum_ranging += RMData.RangeMilliMeter; + sum_signalRate += RMData.SignalRateRtnMegaCps; + sum_spads += RMData.EffectiveSpadRtnCount / 256; + total_count++; + } + } + Status = VL53L1_StopMeasurement(Dev); + + if (total_count > 0) { + + xTalkStoredMeanSignalRate = sum_signalRate / total_count; + xTalkStoredMeanRange = (FixPoint1616_t)(sum_ranging << 16); + xTalkStoredMeanRange /= total_count; + xTalkStoredMeanRtnSpads = (FixPoint1616_t)(sum_spads << 16); + xTalkStoredMeanRtnSpads /= total_count; + + + xTalkStoredMeanRtnSpadsAsInt = (xTalkStoredMeanRtnSpads + + 0x8000) >> 16; + + + xTalkCalDistanceAsInt = ((uint32_t)BDTable[ + VL53L1_TUNING_SINGLE_TARGET_XTALK_TARGET_DISTANCE_MM]); + if (xTalkStoredMeanRtnSpadsAsInt == 0 || + xTalkCalDistanceAsInt == 0 || + xTalkStoredMeanRange >= (xTalkCalDistanceAsInt << 16)) { + XTalkCompensationRateMegaCps = 0; + } else { + + signalXTalkTotalPerSpad = (xTalkStoredMeanSignalRate) / + xTalkStoredMeanRtnSpadsAsInt; + + + signalXTalkTotalPerSpad *= (((uint32_t)1 << 16) - + (xTalkStoredMeanRange / xTalkCalDistanceAsInt)); + + + XTalkCompensationRateMegaCps = (signalXTalkTotalPerSpad + + 0x8000) >> 16; + } + + + Status = VL53L1_GetCalibrationData(Dev, &CalibrationData); + + if (Status != VL53L1_ERROR_NONE) + goto ENDFUNC; + + pC = &CalibrationData.customer; + + pC->algo__crosstalk_compensation_plane_offset_kcps = + (uint32_t)(1000 * ((XTalkCompensationRateMegaCps + + ((uint32_t)1<<6)) >> (16-9))); + + Status = VL53L1_SetCalibrationData(Dev, &CalibrationData); + + if (Status != VL53L1_ERROR_NONE) + goto ENDFUNC; + + Status = VL53L1_enable_xtalk_compensation(Dev); + + } else + + Status = VL53L1_ERROR_XTALK_EXTRACTION_NO_SAMPLE_FAIL; + +ENDFUNC: + LOG_FUNCTION_END(Status); + return Status; + +} + + +static VL53L1_Error CheckValidRectRoi(VL53L1_UserRoi_t ROI) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + if ((ROI.TopLeftX > 15) || (ROI.TopLeftY > 15) || + (ROI.BotRightX > 15) || (ROI.BotRightY > 15)) + Status = VL53L1_ERROR_INVALID_PARAMS; + + if ((ROI.TopLeftX > ROI.BotRightX) || (ROI.TopLeftY < ROI.BotRightY)) + Status = VL53L1_ERROR_INVALID_PARAMS; + + LOG_FUNCTION_END(Status); + return Status; +} + +static VL53L1_GPIO_Interrupt_Mode ConvertModeToLLD(VL53L1_Error *pStatus, + VL53L1_ThresholdMode CrossMode) +{ + VL53L1_GPIO_Interrupt_Mode Mode; + + switch (CrossMode) { + case VL53L1_THRESHOLD_CROSSED_LOW: + Mode = VL53L1_GPIOINTMODE_LEVEL_LOW; + break; + case VL53L1_THRESHOLD_CROSSED_HIGH: + Mode = VL53L1_GPIOINTMODE_LEVEL_HIGH; + break; + case VL53L1_THRESHOLD_OUT_OF_WINDOW: + Mode = VL53L1_GPIOINTMODE_OUT_OF_WINDOW; + break; + case VL53L1_THRESHOLD_IN_WINDOW: + Mode = VL53L1_GPIOINTMODE_IN_WINDOW; + break; + default: + + Mode = VL53L1_GPIOINTMODE_LEVEL_HIGH; + *pStatus = VL53L1_ERROR_INVALID_PARAMS; + } + return Mode; +} + +static VL53L1_ThresholdMode ConvertModeFromLLD(VL53L1_Error *pStatus, + VL53L1_GPIO_Interrupt_Mode CrossMode) +{ + VL53L1_ThresholdMode Mode; + + switch (CrossMode) { + case VL53L1_GPIOINTMODE_LEVEL_LOW: + Mode = VL53L1_THRESHOLD_CROSSED_LOW; + break; + case VL53L1_GPIOINTMODE_LEVEL_HIGH: + Mode = VL53L1_THRESHOLD_CROSSED_HIGH; + break; + case VL53L1_GPIOINTMODE_OUT_OF_WINDOW: + Mode = VL53L1_THRESHOLD_OUT_OF_WINDOW; + break; + case VL53L1_GPIOINTMODE_IN_WINDOW: + Mode = VL53L1_THRESHOLD_IN_WINDOW; + break; + default: + + Mode = VL53L1_THRESHOLD_CROSSED_HIGH; + *pStatus = VL53L1_ERROR_UNDEFINED; + } + return Mode; +} + + + +VL53L1_Error VL53L1_GetVersion(VL53L1_Version_t *pVersion) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + pVersion->major = VL53L1_IMPLEMENTATION_VER_MAJOR; + pVersion->minor = VL53L1_IMPLEMENTATION_VER_MINOR; + pVersion->build = VL53L1_IMPLEMENTATION_VER_SUB; + + pVersion->revision = VL53L1_IMPLEMENTATION_VER_REVISION; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetProductRevision(VL53L1_DEV Dev, + uint8_t *pProductRevisionMajor, uint8_t *pProductRevisionMinor) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t revision_id; + VL53L1_LLDriverData_t *pLLData; + + LOG_FUNCTION_START(""); + + pLLData = VL53L1DevStructGetLLDriverHandle(Dev); + revision_id = pLLData->nvm_copy_data.identification__revision_id; + *pProductRevisionMajor = 1; + *pProductRevisionMinor = (revision_id & 0xF0) >> 4; + + LOG_FUNCTION_END(Status); + return Status; + +} + +VL53L1_Error VL53L1_GetDeviceInfo(VL53L1_DEV Dev, + VL53L1_DeviceInfo_t *pVL53L1_DeviceInfo) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t revision_id; + VL53L1_LLDriverData_t *pLLData; + + LOG_FUNCTION_START(""); + + pLLData = VL53L1DevStructGetLLDriverHandle(Dev); + + strncpy(pVL53L1_DeviceInfo->ProductId, "", + VL53L1_DEVINFO_STRLEN-1); + pVL53L1_DeviceInfo->ProductType = + pLLData->nvm_copy_data.identification__module_type; + + revision_id = pLLData->nvm_copy_data.identification__revision_id; + pVL53L1_DeviceInfo->ProductRevisionMajor = 1; + pVL53L1_DeviceInfo->ProductRevisionMinor = (revision_id & 0xF0) >> 4; + +#ifndef VL53L1_USE_EMPTY_STRING + if (pVL53L1_DeviceInfo->ProductRevisionMinor == 0) + strncpy(pVL53L1_DeviceInfo->Name, + VL53L1_STRING_DEVICE_INFO_NAME0, + VL53L1_DEVINFO_STRLEN-1); + else + strncpy(pVL53L1_DeviceInfo->Name, + VL53L1_STRING_DEVICE_INFO_NAME1, + VL53L1_DEVINFO_STRLEN-1); + strncpy(pVL53L1_DeviceInfo->Type, + VL53L1_STRING_DEVICE_INFO_TYPE, + VL53L1_DEVINFO_STRLEN-1); + + if (pVL53L1_DeviceInfo->ProductType == 0xAA) { + pVL53L1_DeviceInfo->Name[5] = '3'; + pVL53L1_DeviceInfo->Type[5] = '3'; + } +#else + pVL53L1_DeviceInfo->Name[0] = 0; + pVL53L1_DeviceInfo->Type[0] = 0; +#endif + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetUID(VL53L1_DEV Dev, uint64_t *pUid) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t fmtdata[8]; + + LOG_FUNCTION_START(""); + + Status = VL53L1_read_nvm_raw_data(Dev, + (uint8_t)(0x1F8 >> 2), + (uint8_t)(8 >> 2), + fmtdata); + memcpy(pUid, fmtdata, sizeof(uint64_t)); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetRangeStatusString(uint8_t RangeStatus, + char *pRangeStatusString) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_range_status_string(RangeStatus, + pRangeStatusString); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetPalErrorString(VL53L1_Error PalErrorCode, + char *pPalErrorString) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_pal_error_string(PalErrorCode, pPalErrorString); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetPalStateString(VL53L1_State PalStateCode, + char *pPalStateString) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_pal_state_string(PalStateCode, pPalStateString); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetPalState(VL53L1_DEV Dev, VL53L1_State *pPalState) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pPalState = VL53L1DevDataGet(Dev, PalState); + + LOG_FUNCTION_END(Status); + return Status; +} + + + + +VL53L1_Error VL53L1_SetDeviceAddress(VL53L1_DEV Dev, uint8_t DeviceAddress) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_static_nvm_managed_t *pdata = &(pdev->stat_nvm); + + LOG_FUNCTION_START(""); + + Status = VL53L1_WrByte(Dev, VL53L1_I2C_SLAVE__DEVICE_ADDRESS, + DeviceAddress / 2); + + pdata->i2c_slave__device_address = (DeviceAddress / 2) & 0x7F; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_DataInit(VL53L1_DEV Dev) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t i; + VL53L1_LLDriverData_t *pdev; + + LOG_FUNCTION_START(""); + + +#ifdef USE_I2C_2V8 + Status = VL53L1_RdByte(Dev, VL53L1_PAD_I2C_HV__EXTSUP_CONFIG, &i); + if (Status == VL53L1_ERROR_NONE) { + i = (i & 0xfe) | 0x01; + Status = VL53L1_WrByte(Dev, VL53L1_PAD_I2C_HV__EXTSUP_CONFIG, + i); + } +#endif + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_data_init(Dev, 1); + + if (Status == VL53L1_ERROR_NONE) { + pdev = VL53L1DevStructGetLLDriverHandle(Dev); + memset(&pdev->per_vcsel_cal_data, 0, + sizeof(pdev->per_vcsel_cal_data)); + } + + if (Status == VL53L1_ERROR_NONE) + VL53L1DevDataSet(Dev, PalState, VL53L1_STATE_WAIT_STATICINIT); + + + for (i = 0; i < VL53L1_CHECKENABLE_NUMBER_OF_CHECKS; i++) { + if (Status == VL53L1_ERROR_NONE) + Status |= VL53L1_SetLimitCheckEnable(Dev, i, 1); + else + break; + + } + + + if (Status == VL53L1_ERROR_NONE) { + Status = VL53L1_set_dmax_mode(Dev, + VL53L1_DEVICEDMAXMODE__CUST_CAL_DATA); + } + + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_StaticInit(VL53L1_DEV Dev) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t measurement_mode; + + LOG_FUNCTION_START(""); + + VL53L1DevDataSet(Dev, PalState, VL53L1_STATE_IDLE); + + measurement_mode = VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + VL53L1DevDataSet(Dev, LLData.measurement_mode, measurement_mode); + + Status = VL53L1_SetPresetMode(Dev, + VL53L1_PRESETMODE_RANGING); + VL53L1_SetDistanceMode(Dev, + VL53L1_DISTANCEMODE_MEDIUM); + VL53L1DevDataSet(Dev, CurrentParameters.OutputMode, + VL53L1_OUTPUTMODE_NEAREST); + VL53L1_SmudgeCorrectionEnable(Dev, + VL53L1_SMUDGE_CORRECTION_NONE); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_WaitDeviceBooted(VL53L1_DEV Dev) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_poll_for_boot_completion(Dev, + VL53L1_BOOT_COMPLETION_POLLING_TIMEOUT_MS); + + LOG_FUNCTION_END(Status); + return Status; +} + + + + +static VL53L1_Error ComputeDevicePresetMode( + VL53L1_PresetModes PresetMode, + VL53L1_DistanceModes DistanceMode, + VL53L1_DevicePresetModes *pDevicePresetMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + uint8_t DistIdx; + VL53L1_DevicePresetModes LightModes[3] = { + VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_SHORT_RANGE, + VL53L1_DEVICEPRESETMODE_STANDARD_RANGING, + VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_LONG_RANGE}; + + VL53L1_DevicePresetModes RangingModes[3] = { + VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE, + VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE, + VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE}; + + VL53L1_DevicePresetModes ScanningModes[3] = { + VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE_SHORT_RANGE, + VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE, + VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE_LONG_RANGE}; + + VL53L1_DevicePresetModes TimedModes[3] = { + VL53L1_DEVICEPRESETMODE_TIMED_RANGING_SHORT_RANGE, + VL53L1_DEVICEPRESETMODE_TIMED_RANGING, + VL53L1_DEVICEPRESETMODE_TIMED_RANGING_LONG_RANGE}; + + VL53L1_DevicePresetModes LowPowerTimedModes[3] = { + VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_SHORT_RANGE, + VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_MEDIUM_RANGE, + VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_LONG_RANGE}; + + *pDevicePresetMode = VL53L1_DEVICEPRESETMODE_STANDARD_RANGING; + + switch (DistanceMode) { + case VL53L1_DISTANCEMODE_SHORT: + DistIdx = 0; + break; + case VL53L1_DISTANCEMODE_MEDIUM: + DistIdx = 1; + break; + default: + DistIdx = 2; + } + + switch (PresetMode) { + case VL53L1_PRESETMODE_LITE_RANGING: + *pDevicePresetMode = LightModes[DistIdx]; + break; + + case VL53L1_PRESETMODE_RANGING: + *pDevicePresetMode = RangingModes[DistIdx]; + break; + + case VL53L1_PRESETMODE_MULTIZONES_SCANNING: + *pDevicePresetMode = ScanningModes[DistIdx]; + break; + + case VL53L1_PRESETMODE_AUTONOMOUS: + *pDevicePresetMode = TimedModes[DistIdx]; + break; + + case VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS: + *pDevicePresetMode = LowPowerTimedModes[DistIdx]; + break; + case VL53L1_PRESETMODE_OLT: + *pDevicePresetMode = VL53L1_DEVICEPRESETMODE_OLT; + break; + case VL53L1_PRESETMODE_PROXY_RANGING_MODE: + *pDevicePresetMode = + VL53L1_DEVICEPRESETMODE_SPECIAL_HISTOGRAM_SHORT_RANGE; + break; + + default: + + Status = VL53L1_ERROR_MODE_NOT_SUPPORTED; + } + + return Status; +} + +static VL53L1_Error SetPresetMode(VL53L1_DEV Dev, + VL53L1_PresetModes PresetMode, + VL53L1_DistanceModes DistanceMode, + uint32_t inter_measurement_period_ms) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_DevicePresetModes device_preset_mode; + uint8_t measurement_mode; + uint16_t dss_config__target_total_rate_mcps = 0; + uint32_t phasecal_config_timeout_us = 0; + uint32_t mm_config_timeout_us = 0; + uint32_t lld_range_config_timeout_us = 0; + + LOG_FUNCTION_START("%d", (int)PresetMode); + + if ((PresetMode == VL53L1_PRESETMODE_AUTONOMOUS) || + (PresetMode == VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS)) + measurement_mode = VL53L1_DEVICEMEASUREMENTMODE_TIMED; + else + measurement_mode = VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + + + Status = ComputeDevicePresetMode(PresetMode, DistanceMode, + &device_preset_mode); + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_get_preset_mode_timing_cfg(Dev, + device_preset_mode, + &dss_config__target_total_rate_mcps, + &phasecal_config_timeout_us, + &mm_config_timeout_us, + &lld_range_config_timeout_us); + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_set_preset_mode( + Dev, + device_preset_mode, + dss_config__target_total_rate_mcps, + phasecal_config_timeout_us, + mm_config_timeout_us, + lld_range_config_timeout_us, + inter_measurement_period_ms); + + if (Status == VL53L1_ERROR_NONE) + VL53L1DevDataSet(Dev, LLData.measurement_mode, + measurement_mode); + + if (Status == VL53L1_ERROR_NONE) + VL53L1DevDataSet(Dev, CurrentParameters.PresetMode, PresetMode); + + VL53L1DevDataSet(Dev, CurrentParameters.OutputMode, + VL53L1_OUTPUTMODE_NEAREST); + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_SetPresetMode(VL53L1_DEV Dev, VL53L1_PresetModes PresetMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_DistanceModes DistanceMode = VL53L1_DISTANCEMODE_LONG; + + LOG_FUNCTION_START("%d", (int)PresetMode); + + + Status = VL53L1_low_power_auto_data_init(Dev); + + if (PresetMode == VL53L1_PRESETMODE_PROXY_RANGING_MODE) + DistanceMode = VL53L1_DISTANCEMODE_SHORT; + Status = SetPresetMode(Dev, + PresetMode, + DistanceMode, + 1000); + + if (Status == VL53L1_ERROR_NONE) { + if ((PresetMode == VL53L1_PRESETMODE_LITE_RANGING) || + (PresetMode == VL53L1_PRESETMODE_AUTONOMOUS) || + (PresetMode == VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS)) + Status = VL53L1_SetMeasurementTimingBudgetMicroSeconds( + Dev, 41000); + else + + Status = VL53L1_SetMeasurementTimingBudgetMicroSeconds( + Dev, 33333); + } + + if (Status == VL53L1_ERROR_NONE) { + + Status = VL53L1_SetInterMeasurementPeriodMilliSeconds(Dev, + 1000); + } + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_GetPresetMode(VL53L1_DEV Dev, + VL53L1_PresetModes *pPresetMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pPresetMode = VL53L1DevDataGet(Dev, CurrentParameters.PresetMode); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetDistanceMode(VL53L1_DEV Dev, + VL53L1_DistanceModes DistanceMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_PresetModes PresetMode; + uint32_t inter_measurement_period_ms; + uint32_t TimingBudget = 0; + uint32_t MmTimeoutUs = 0; + uint32_t PhaseCalTimeoutUs = 0; + VL53L1_zone_config_t zone_config; + + LOG_FUNCTION_START("%d", (int)DistanceMode); + + PresetMode = VL53L1DevDataGet(Dev, CurrentParameters.PresetMode); + + + + if ((PresetMode == VL53L1_PRESETMODE_PROXY_RANGING_MODE) && + (DistanceMode != VL53L1_DISTANCEMODE_SHORT)) + return VL53L1_ERROR_INVALID_PARAMS; + if ((DistanceMode != VL53L1_DISTANCEMODE_SHORT) && + (DistanceMode != VL53L1_DISTANCEMODE_MEDIUM) && + (DistanceMode != VL53L1_DISTANCEMODE_LONG)) + return VL53L1_ERROR_INVALID_PARAMS; + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_get_zone_config(Dev, &zone_config); + + inter_measurement_period_ms = VL53L1DevDataGet(Dev, + LLData.inter_measurement_period_ms); + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_get_timeouts_us(Dev, &PhaseCalTimeoutUs, + &MmTimeoutUs, &TimingBudget); + + if (Status == VL53L1_ERROR_NONE) + Status = SetPresetMode(Dev, + PresetMode, + DistanceMode, + inter_measurement_period_ms); + + if (Status == VL53L1_ERROR_NONE) { + VL53L1DevDataSet(Dev, CurrentParameters.DistanceMode, + DistanceMode); + } + + if (Status == VL53L1_ERROR_NONE) { + Status = VL53L1_set_timeouts_us(Dev, PhaseCalTimeoutUs, + MmTimeoutUs, TimingBudget); + + if (Status == VL53L1_ERROR_NONE) + VL53L1DevDataSet(Dev, LLData.range_config_timeout_us, + TimingBudget); + } + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_set_zone_config(Dev, &zone_config); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetDistanceMode(VL53L1_DEV Dev, + VL53L1_DistanceModes *pDistanceMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pDistanceMode = VL53L1DevDataGet(Dev, CurrentParameters.DistanceMode); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetOutputMode(VL53L1_DEV Dev, + VL53L1_OutputModes OutputMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if ((OutputMode != VL53L1_OUTPUTMODE_NEAREST) && + (OutputMode != VL53L1_OUTPUTMODE_STRONGEST)) + Status = VL53L1_ERROR_MODE_NOT_SUPPORTED; + else + VL53L1DevDataSet(Dev, CurrentParameters.OutputMode, OutputMode); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetOutputMode(VL53L1_DEV Dev, + VL53L1_OutputModes *pOutputMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pOutputMode = VL53L1DevDataGet(Dev, CurrentParameters.OutputMode); + + LOG_FUNCTION_END(Status); + return Status; +} + + + +VL53L1_Error VL53L1_SetMeasurementTimingBudgetMicroSeconds(VL53L1_DEV Dev, + uint32_t MeasurementTimingBudgetMicroSeconds) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t Mm1Enabled = 0; + uint8_t Mm2Enabled = 0; + uint32_t TimingGuard; + uint32_t divisor; + uint32_t TimingBudget = 0; + uint32_t MmTimeoutUs = 0; + VL53L1_PresetModes PresetMode; + uint32_t PhaseCalTimeoutUs = 0; + uint32_t vhv; + int32_t vhv_loops; + uint32_t FDAMaxTimingBudgetUs = FDA_MAX_TIMING_BUDGET_US; + + LOG_FUNCTION_START(""); + + + if (MeasurementTimingBudgetMicroSeconds > 10000000) + Status = VL53L1_ERROR_INVALID_PARAMS; + + if (Status == VL53L1_ERROR_NONE) { + Status = VL53L1_GetSequenceStepEnable(Dev, + VL53L1_SEQUENCESTEP_MM1, &Mm1Enabled); + } + + if (Status == VL53L1_ERROR_NONE) { + Status = VL53L1_GetSequenceStepEnable(Dev, + VL53L1_SEQUENCESTEP_MM2, &Mm2Enabled); + } + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_get_timeouts_us(Dev, + &PhaseCalTimeoutUs, + &MmTimeoutUs, + &TimingBudget); + + if (Status == VL53L1_ERROR_NONE) { + PresetMode = VL53L1DevDataGet(Dev, + CurrentParameters.PresetMode); + + TimingGuard = 0; + divisor = 1; + switch (PresetMode) { + case VL53L1_PRESETMODE_LITE_RANGING: + if ((Mm1Enabled == 1) || (Mm2Enabled == 1)) + TimingGuard = 5000; + else + TimingGuard = 1000; + break; + + case VL53L1_PRESETMODE_AUTONOMOUS: + FDAMaxTimingBudgetUs *= 2; + if ((Mm1Enabled == 1) || (Mm2Enabled == 1)) + TimingGuard = 26600; + else + TimingGuard = 21600; + divisor = 2; + break; + + case VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS: + FDAMaxTimingBudgetUs *= 2; + vhv = LOWPOWER_AUTO_VHV_LOOP_DURATION_US; + VL53L1_get_tuning_parm(Dev, + VL53L1_TUNINGPARM_LOWPOWERAUTO_VHV_LOOP_BOUND, + &vhv_loops); + if (vhv_loops > 0) { + vhv += vhv_loops * + LOWPOWER_AUTO_VHV_LOOP_DURATION_US; + } + TimingGuard = LOWPOWER_AUTO_OVERHEAD_BEFORE_A_RANGING + + LOWPOWER_AUTO_OVERHEAD_BETWEEN_A_B_RANGING + + vhv; + divisor = 2; + break; + + case VL53L1_PRESETMODE_RANGING: + case VL53L1_PRESETMODE_MULTIZONES_SCANNING: + case VL53L1_PRESETMODE_PROXY_RANGING_MODE: + TimingGuard = 1700; + divisor = 6; + break; + + case VL53L1_PRESETMODE_OLT: + TimingGuard = MmTimeoutUs + 5000; + break; + default: + + Status = VL53L1_ERROR_MODE_NOT_SUPPORTED; + } + + if (MeasurementTimingBudgetMicroSeconds <= TimingGuard) + Status = VL53L1_ERROR_INVALID_PARAMS; + else { + TimingBudget = (MeasurementTimingBudgetMicroSeconds + - TimingGuard); + } + + if (Status == VL53L1_ERROR_NONE) { + if (TimingBudget > FDAMaxTimingBudgetUs) + Status = VL53L1_ERROR_INVALID_PARAMS; + else { + TimingBudget /= divisor; + Status = VL53L1_set_timeouts_us( + Dev, + PhaseCalTimeoutUs, + MmTimeoutUs, + TimingBudget); + } + + if (Status == VL53L1_ERROR_NONE) + VL53L1DevDataSet(Dev, + LLData.range_config_timeout_us, + TimingBudget); + } + } + if (Status == VL53L1_ERROR_NONE) { + VL53L1DevDataSet(Dev, + CurrentParameters.MeasurementTimingBudgetMicroSeconds, + MeasurementTimingBudgetMicroSeconds); + } + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_GetMeasurementTimingBudgetMicroSeconds(VL53L1_DEV Dev, + uint32_t *pMeasurementTimingBudgetMicroSeconds) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t Mm1Enabled = 0; + uint8_t Mm2Enabled = 0; + uint32_t MmTimeoutUs = 0; + uint32_t RangeTimeoutUs = 0; + uint32_t MeasTimingBdg = 0; + uint32_t PhaseCalTimeoutUs = 0; + VL53L1_PresetModes PresetMode; + uint32_t TimingGuard; + uint32_t vhv; + int32_t vhv_loops; + + LOG_FUNCTION_START(""); + + *pMeasurementTimingBudgetMicroSeconds = 0; + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_GetSequenceStepEnable(Dev, + VL53L1_SEQUENCESTEP_MM1, &Mm1Enabled); + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_GetSequenceStepEnable(Dev, + VL53L1_SEQUENCESTEP_MM2, &Mm2Enabled); + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_get_timeouts_us(Dev, + &PhaseCalTimeoutUs, + &MmTimeoutUs, + &RangeTimeoutUs); + + if (Status == VL53L1_ERROR_NONE) { + PresetMode = VL53L1DevDataGet(Dev, + CurrentParameters.PresetMode); + + switch (PresetMode) { + case VL53L1_PRESETMODE_LITE_RANGING: + if ((Mm1Enabled == 1) || (Mm2Enabled == 1)) + MeasTimingBdg = RangeTimeoutUs + 5000; + else + MeasTimingBdg = RangeTimeoutUs + 1000; + + break; + + case VL53L1_PRESETMODE_AUTONOMOUS: + if ((Mm1Enabled == 1) || (Mm2Enabled == 1)) + MeasTimingBdg = 2 * RangeTimeoutUs + 26600; + else + MeasTimingBdg = 2 * RangeTimeoutUs + 21600; + + break; + + case VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS: + vhv = LOWPOWER_AUTO_VHV_LOOP_DURATION_US; + VL53L1_get_tuning_parm(Dev, + VL53L1_TUNINGPARM_LOWPOWERAUTO_VHV_LOOP_BOUND, + &vhv_loops); + if (vhv_loops > 0) { + vhv += vhv_loops * + LOWPOWER_AUTO_VHV_LOOP_DURATION_US; + } + TimingGuard = LOWPOWER_AUTO_OVERHEAD_BEFORE_A_RANGING + + LOWPOWER_AUTO_OVERHEAD_BETWEEN_A_B_RANGING + + vhv; + MeasTimingBdg = 2 * RangeTimeoutUs + TimingGuard; + break; + + case VL53L1_PRESETMODE_RANGING: + case VL53L1_PRESETMODE_MULTIZONES_SCANNING: + case VL53L1_PRESETMODE_PROXY_RANGING_MODE: + MeasTimingBdg = (6 * RangeTimeoutUs) + 1700; + break; + + case VL53L1_PRESETMODE_OLT: + MeasTimingBdg = RangeTimeoutUs + MmTimeoutUs + 5000; + break; + default: + + Status = VL53L1_ERROR_MODE_NOT_SUPPORTED; + } + } + if (Status == VL53L1_ERROR_NONE) + *pMeasurementTimingBudgetMicroSeconds = MeasTimingBdg; + + LOG_FUNCTION_END(Status); + return Status; +} + + + +VL53L1_Error VL53L1_SetInterMeasurementPeriodMilliSeconds(VL53L1_DEV Dev, + uint32_t InterMeasurementPeriodMilliSeconds) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint32_t adjustedIMP; + + LOG_FUNCTION_START(""); + + + adjustedIMP = InterMeasurementPeriodMilliSeconds; + adjustedIMP += (adjustedIMP * 64) / 1000; + + Status = VL53L1_set_inter_measurement_period_ms(Dev, + adjustedIMP); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetInterMeasurementPeriodMilliSeconds(VL53L1_DEV Dev, + uint32_t *pInterMeasurementPeriodMilliSeconds) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint32_t adjustedIMP; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_inter_measurement_period_ms(Dev, &adjustedIMP); + + adjustedIMP -= (adjustedIMP * 64) / 1000; + *pInterMeasurementPeriodMilliSeconds = adjustedIMP; + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetDmaxReflectance(VL53L1_DEV Dev, + FixPoint1616_t DmaxReflectance) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_dmax_reflectance_array_t dmax_reflectances; + + LOG_FUNCTION_START(""); + + if (DmaxReflectance > 100*65536) + Status = VL53L1_ERROR_INVALID_PARAMS; + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_get_dmax_reflectance_values(Dev, + &dmax_reflectances); + + if (Status == VL53L1_ERROR_NONE) { + dmax_reflectances.target_reflectance_for_dmax[ + DMAX_REFLECTANCE_IDX] = + VL53L1_FIXPOINT1616TOFIXPOINT72(DmaxReflectance); + Status = VL53L1_set_dmax_reflectance_values(Dev, + &dmax_reflectances); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetDmaxReflectance(VL53L1_DEV Dev, + FixPoint1616_t *pDmaxReflectance) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_dmax_reflectance_array_t dmax_reflectances; + uint16_t r; + + LOG_FUNCTION_START(""); + Status = VL53L1_get_dmax_reflectance_values(Dev, &dmax_reflectances); + if (Status == VL53L1_ERROR_NONE) { + r = dmax_reflectances.target_reflectance_for_dmax[ + DMAX_REFLECTANCE_IDX]; + *pDmaxReflectance = VL53L1_FIXPOINT72TOFIXPOINT1616(r); + } + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_SetDmaxMode(VL53L1_DEV Dev, + VL53L1_DeviceDmaxModes DmaxMode) +{ + + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_DeviceDmaxMode dmax_mode; + + LOG_FUNCTION_START(""); + + switch (DmaxMode) { + case VL53L1_DMAXMODE_FMT_CAL_DATA: + dmax_mode = VL53L1_DEVICEDMAXMODE__FMT_CAL_DATA; + break; + case VL53L1_DMAXMODE_CUSTCAL_DATA: + dmax_mode = VL53L1_DEVICEDMAXMODE__CUST_CAL_DATA; + break; + case VL53L1_DMAXMODE_PER_ZONE_CAL_DATA: + dmax_mode = VL53L1_DEVICEDMAXMODE__PER_ZONE_CAL_DATA; + break; + default: + dmax_mode = VL53L1_DEVICEDMAXMODE__CUST_CAL_DATA; + Status = VL53L1_ERROR_INVALID_PARAMS; + break; + } + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_set_dmax_mode(Dev, dmax_mode); + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_GetDmaxMode(VL53L1_DEV Dev, + VL53L1_DeviceDmaxModes *pDmaxMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_DeviceDmaxMode dmax_mode; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_dmax_mode(Dev, &dmax_mode); + if (Status == VL53L1_ERROR_NONE) { + switch (dmax_mode) { + case VL53L1_DEVICEDMAXMODE__FMT_CAL_DATA: + *pDmaxMode = VL53L1_DMAXMODE_FMT_CAL_DATA; + break; + case VL53L1_DEVICEDMAXMODE__CUST_CAL_DATA: + *pDmaxMode = VL53L1_DMAXMODE_CUSTCAL_DATA; + break; + case VL53L1_DEVICEDMAXMODE__PER_ZONE_CAL_DATA: + *pDmaxMode = VL53L1_DMAXMODE_PER_ZONE_CAL_DATA; + break; + default: + *pDmaxMode = VL53L1_DEVICEDMAXMODE__CUST_CAL_DATA; + Status = VL53L1_ERROR_NOT_IMPLEMENTED; + break; + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + + + + + + +VL53L1_Error VL53L1_GetNumberOfLimitCheck(uint16_t *pNumberOfLimitCheck) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + *pNumberOfLimitCheck = VL53L1_CHECKENABLE_NUMBER_OF_CHECKS; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetLimitCheckInfo(uint16_t LimitCheckId, + char *pLimitCheckString) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_limit_check_info(LimitCheckId, + pLimitCheckString); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetLimitCheckStatus(VL53L1_DEV Dev, uint16_t LimitCheckId, + uint8_t *pLimitCheckStatus) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t Temp8; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L1_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L1_ERROR_INVALID_PARAMS; + } else { + VL53L1_GETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + LimitCheckId, Temp8); + *pLimitCheckStatus = Temp8; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +static VL53L1_Error SetLimitValue(VL53L1_DEV Dev, uint16_t LimitCheckId, + FixPoint1616_t value) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint16_t tmpuint16; + + LOG_FUNCTION_START(""); + + switch (LimitCheckId) { + case VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE: + tmpuint16 = VL53L1_FIXPOINT1616TOFIXPOINT142(value); + VL53L1_set_lite_sigma_threshold(Dev, tmpuint16); + break; + case VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: + tmpuint16 = VL53L1_FIXPOINT1616TOFIXPOINT97(value); + VL53L1_set_lite_min_count_rate(Dev, tmpuint16); + break; + default: + Status = VL53L1_ERROR_INVALID_PARAMS; + } + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_SetLimitCheckEnable(VL53L1_DEV Dev, uint16_t LimitCheckId, + uint8_t LimitCheckEnable) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + FixPoint1616_t TempFix1616 = 0; + + LOG_FUNCTION_START(""); + + + if (LimitCheckId >= VL53L1_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L1_ERROR_INVALID_PARAMS; + } else { + + if (LimitCheckEnable == 0) + TempFix1616 = 0; + else + VL53L1_GETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + LimitCheckId, TempFix1616); + + Status = SetLimitValue(Dev, LimitCheckId, TempFix1616); + } + + if (Status == VL53L1_ERROR_NONE) + VL53L1_SETARRAYPARAMETERFIELD(Dev, + LimitChecksEnable, + LimitCheckId, + ((LimitCheckEnable == 0) ? 0 : 1)); + + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetLimitCheckEnable(VL53L1_DEV Dev, uint16_t LimitCheckId, + uint8_t *pLimitCheckEnable) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t Temp8; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L1_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L1_ERROR_INVALID_PARAMS; + *pLimitCheckEnable = 0; + } else { + VL53L1_GETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + LimitCheckId, Temp8); + *pLimitCheckEnable = Temp8; + } + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetLimitCheckValue(VL53L1_DEV Dev, uint16_t LimitCheckId, + FixPoint1616_t LimitCheckValue) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t LimitChecksEnable; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L1_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L1_ERROR_INVALID_PARAMS; + } else { + + VL53L1_GETARRAYPARAMETERFIELD(Dev, LimitChecksEnable, + LimitCheckId, + LimitChecksEnable); + + if (LimitChecksEnable == 0) { + + VL53L1_SETARRAYPARAMETERFIELD(Dev, LimitChecksValue, + LimitCheckId, LimitCheckValue); + } else { + + Status = SetLimitValue(Dev, LimitCheckId, + LimitCheckValue); + + if (Status == VL53L1_ERROR_NONE) { + VL53L1_SETARRAYPARAMETERFIELD(Dev, + LimitChecksValue, + LimitCheckId, LimitCheckValue); + } + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetLimitCheckValue(VL53L1_DEV Dev, uint16_t LimitCheckId, + FixPoint1616_t *pLimitCheckValue) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint16_t MinCountRate; + FixPoint1616_t TempFix1616 = 0; + uint16_t SigmaThresh; + + LOG_FUNCTION_START(""); + + switch (LimitCheckId) { + case VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE: + Status = VL53L1_get_lite_sigma_threshold(Dev, &SigmaThresh); + TempFix1616 = VL53L1_FIXPOINT142TOFIXPOINT1616(SigmaThresh); + break; + case VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: + Status = VL53L1_get_lite_min_count_rate(Dev, &MinCountRate); + TempFix1616 = VL53L1_FIXPOINT97TOFIXPOINT1616(MinCountRate); + break; + default: + Status = VL53L1_ERROR_INVALID_PARAMS; + } + + if (Status == VL53L1_ERROR_NONE) { + + if (TempFix1616 == 0) { + + VL53L1_GETARRAYPARAMETERFIELD(Dev, + LimitChecksValue, LimitCheckId, + TempFix1616); + *pLimitCheckValue = TempFix1616; + VL53L1_SETARRAYPARAMETERFIELD(Dev, + LimitChecksEnable, LimitCheckId, 0); + } else { + *pLimitCheckValue = TempFix1616; + VL53L1_SETARRAYPARAMETERFIELD(Dev, + LimitChecksValue, LimitCheckId, + TempFix1616); + VL53L1_SETARRAYPARAMETERFIELD(Dev, + LimitChecksEnable, LimitCheckId, 1); + } + } + LOG_FUNCTION_END(Status); + return Status; + +} + +VL53L1_Error VL53L1_GetLimitCheckCurrent(VL53L1_DEV Dev, uint16_t LimitCheckId, + FixPoint1616_t *pLimitCheckCurrent) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + FixPoint1616_t TempFix1616 = 0; + + LOG_FUNCTION_START(""); + + if (LimitCheckId >= VL53L1_CHECKENABLE_NUMBER_OF_CHECKS) { + Status = VL53L1_ERROR_INVALID_PARAMS; + } else { + VL53L1_GETARRAYPARAMETERFIELD(Dev, LimitChecksCurrent, + LimitCheckId, TempFix1616); + *pLimitCheckCurrent = TempFix1616; + } + + LOG_FUNCTION_END(Status); + return Status; + +} + + + + + + + + +VL53L1_Error VL53L1_GetMaxNumberOfROI(VL53L1_DEV Dev, + uint8_t *pMaxNumberOfROI) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_PresetModes PresetMode; + + LOG_FUNCTION_START(""); + + PresetMode = VL53L1DevDataGet(Dev, CurrentParameters.PresetMode); + + + if (PresetMode == VL53L1_PRESETMODE_MULTIZONES_SCANNING) + *pMaxNumberOfROI = VL53L1_MAX_USER_ZONES; + else + *pMaxNumberOfROI = 1; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetROI(VL53L1_DEV Dev, + VL53L1_RoiConfig_t *pRoiConfig) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_PresetModes PresetMode; + uint8_t MaxNumberOfROI = 1; + VL53L1_zone_config_t zone_cfg; + VL53L1_UserRoi_t CurrROI; + uint8_t i; + uint8_t x_centre; + uint8_t y_centre; + uint8_t width, height; + + LOG_FUNCTION_START(""); + + + PresetMode = VL53L1DevDataGet(Dev, CurrentParameters.PresetMode); + + + if (PresetMode == VL53L1_PRESETMODE_MULTIZONES_SCANNING) + MaxNumberOfROI = VL53L1_MAX_USER_ZONES; + + if ((pRoiConfig->NumberOfRoi > MaxNumberOfROI) || + (pRoiConfig->NumberOfRoi < 1)) + Status = VL53L1_ERROR_INVALID_PARAMS; + + if (Status == VL53L1_ERROR_NONE) { + + + zone_cfg.max_zones = MaxNumberOfROI; + zone_cfg.active_zones = pRoiConfig->NumberOfRoi - 1; + + for (i = 0; i < pRoiConfig->NumberOfRoi; i++) { + CurrROI = pRoiConfig->UserRois[i]; + + Status = CheckValidRectRoi(CurrROI); + if (Status != VL53L1_ERROR_NONE) + break; + + x_centre = (CurrROI.BotRightX + CurrROI.TopLeftX + 1) + / 2; + y_centre = (CurrROI.TopLeftY + CurrROI.BotRightY + 1) + / 2; + width = (CurrROI.BotRightX - CurrROI.TopLeftX); + height = (CurrROI.TopLeftY - CurrROI.BotRightY); + if ((width < 3) || (height < 3)) { + Status = VL53L1_ERROR_INVALID_PARAMS; + break; + } + zone_cfg.user_zones[i].x_centre = x_centre; + zone_cfg.user_zones[i].y_centre = y_centre; + zone_cfg.user_zones[i].width = width; + zone_cfg.user_zones[i].height = height; + } + } + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_set_zone_config(Dev, &zone_cfg); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetROI(VL53L1_DEV Dev, + VL53L1_RoiConfig_t *pRoiConfig) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_zone_config_t zone_cfg; + uint8_t i; + uint8_t TopLeftX; + uint8_t TopLeftY; + uint8_t BotRightX; + uint8_t BotRightY; + + LOG_FUNCTION_START(""); + + VL53L1_get_zone_config(Dev, &zone_cfg); + + pRoiConfig->NumberOfRoi = zone_cfg.active_zones + 1; + + for (i = 0; i < pRoiConfig->NumberOfRoi; i++) { + TopLeftX = (2 * zone_cfg.user_zones[i].x_centre - + zone_cfg.user_zones[i].width) >> 1; + TopLeftY = (2 * zone_cfg.user_zones[i].y_centre + + zone_cfg.user_zones[i].height) >> 1; + BotRightX = (2 * zone_cfg.user_zones[i].x_centre + + zone_cfg.user_zones[i].width) >> 1; + BotRightY = (2 * zone_cfg.user_zones[i].y_centre - + zone_cfg.user_zones[i].height) >> 1; + pRoiConfig->UserRois[i].TopLeftX = TopLeftX; + pRoiConfig->UserRois[i].TopLeftY = TopLeftY; + pRoiConfig->UserRois[i].BotRightX = BotRightX; + pRoiConfig->UserRois[i].BotRightY = BotRightY; + } + + LOG_FUNCTION_END(Status); + return Status; +} + + + + + + + +VL53L1_Error VL53L1_GetNumberOfSequenceSteps(VL53L1_DEV Dev, + uint8_t *pNumberOfSequenceSteps) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + SUPPRESS_UNUSED_WARNING(Dev); + + LOG_FUNCTION_START(""); + + *pNumberOfSequenceSteps = VL53L1_SEQUENCESTEP_NUMBER_OF_ITEMS; + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetSequenceStepsInfo(VL53L1_SequenceStepId SequenceStepId, + char *pSequenceStepsString) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_sequence_steps_info( + SequenceStepId, + pSequenceStepsString); + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L1_Error VL53L1_SetSequenceStepEnable(VL53L1_DEV Dev, + VL53L1_SequenceStepId SequenceStepId, uint8_t SequenceStepEnabled) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint32_t MeasurementTimingBudgetMicroSeconds; + + LOG_FUNCTION_START(""); + + + + Status = VL53L1_set_sequence_config_bit(Dev, + (VL53L1_DeviceSequenceConfig)SequenceStepId, + SequenceStepEnabled); + + + if (Status == VL53L1_ERROR_NONE) { + + + MeasurementTimingBudgetMicroSeconds = VL53L1DevDataGet(Dev, + CurrentParameters.MeasurementTimingBudgetMicroSeconds); + + VL53L1_SetMeasurementTimingBudgetMicroSeconds(Dev, + MeasurementTimingBudgetMicroSeconds); + } + + LOG_FUNCTION_END(Status); + + return Status; +} + + +VL53L1_Error VL53L1_GetSequenceStepEnable(VL53L1_DEV Dev, + VL53L1_SequenceStepId SequenceStepId, uint8_t *pSequenceStepEnabled) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_sequence_config_bit(Dev, + (VL53L1_DeviceSequenceConfig)SequenceStepId, + pSequenceStepEnabled); + + LOG_FUNCTION_END(Status); + return Status; +} + + + + + + + + + + +VL53L1_Error VL53L1_StartMeasurement(VL53L1_DEV Dev) +{ +#define TIMED_MODE_TIMING_GUARD_MILLISECONDS 4 + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t DeviceMeasurementMode; + VL53L1_State CurrPalState; + VL53L1_Error lStatus; + uint32_t MTBus, IMPms; + uint8_t i; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + + LOG_FUNCTION_START(""); + + DeviceMeasurementMode = VL53L1DevDataGet(Dev, LLData.measurement_mode); + if (DeviceMeasurementMode != VL53L1_DEVICEMEASUREMENTMODE_TIMED) + VL53L1_load_patch(Dev); + for (i = 0; i < VL53L1_MAX_RANGE_RESULTS; i++) { + pdev->PreviousRangeMilliMeter[i] = 0; + pdev->PreviousRangeStatus[i] = 255; + pdev->PreviousExtendedRange[i] = 0; + } + pdev->PreviousStreamCount = 0; + pdev->PreviousRangeActiveResults = 0; + CurrPalState = VL53L1DevDataGet(Dev, PalState); + switch (CurrPalState) { + case VL53L1_STATE_IDLE: + Status = VL53L1_ERROR_NONE; + break; + case VL53L1_STATE_POWERDOWN: + case VL53L1_STATE_WAIT_STATICINIT: + case VL53L1_STATE_STANDBY: + case VL53L1_STATE_RUNNING: + case VL53L1_STATE_RESET: + case VL53L1_STATE_UNKNOWN: + case VL53L1_STATE_ERROR: + Status = VL53L1_ERROR_INVALID_COMMAND; + break; + default: + Status = VL53L1_ERROR_UNDEFINED; + } + + + if ((Status == VL53L1_ERROR_NONE) && + (DeviceMeasurementMode == VL53L1_DEVICEMEASUREMENTMODE_TIMED)) { + lStatus = VL53L1_GetMeasurementTimingBudgetMicroSeconds(Dev, + &MTBus); + + MTBus /= 1000; + lStatus = VL53L1_GetInterMeasurementPeriodMilliSeconds(Dev, + &IMPms); + + SUPPRESS_UNUSED_WARNING(lStatus); + if (IMPms < MTBus + TIMED_MODE_TIMING_GUARD_MILLISECONDS) + Status = VL53L1_ERROR_INVALID_PARAMS; + } + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_init_and_start_range( + Dev, + DeviceMeasurementMode, + VL53L1_DEVICECONFIGLEVEL_FULL); + + + if (Status == VL53L1_ERROR_NONE) + VL53L1DevDataSet(Dev, PalState, VL53L1_STATE_RUNNING); + + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_StopMeasurement(VL53L1_DEV Dev) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t DeviceMeasurementMode; + + LOG_FUNCTION_START(""); + + Status = VL53L1_stop_range(Dev); + DeviceMeasurementMode = VL53L1DevDataGet(Dev, LLData.measurement_mode); + if (DeviceMeasurementMode != VL53L1_DEVICEMEASUREMENTMODE_TIMED) + VL53L1_unload_patch(Dev); + + if (Status == VL53L1_ERROR_NONE) + VL53L1DevDataSet(Dev, PalState, VL53L1_STATE_IDLE); + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_ClearInterruptAndStartMeasurement(VL53L1_DEV Dev) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t DeviceMeasurementMode; + + LOG_FUNCTION_START(""); + + DeviceMeasurementMode = VL53L1DevDataGet(Dev, LLData.measurement_mode); + + Status = VL53L1_clear_interrupt_and_enable_next_range(Dev, + DeviceMeasurementMode); + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_GetMeasurementDataReady(VL53L1_DEV Dev, + uint8_t *pMeasurementDataReady) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_is_new_data_ready(Dev, pMeasurementDataReady); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_WaitMeasurementDataReady(VL53L1_DEV Dev) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + Status = VL53L1_poll_for_range_completion(Dev, + VL53L1_RANGE_COMPLETION_POLLING_TIMEOUT_MS); + + LOG_FUNCTION_END(Status); + return Status; +} + +static void GenNewPresetMode(int16_t RefRange, + VL53L1_DistanceModes InternalDistanceMode, + VL53L1_DistanceModes *pNewDistanceMode) +{ + uint16_t HRLI = 600; + uint16_t HRLH = 700; + uint16_t MRLI = 1400; + uint16_t MRLH = 1500; + + switch (InternalDistanceMode) { + case VL53L1_DISTANCEMODE_SHORT: + + if (RefRange > MRLH) + *pNewDistanceMode = VL53L1_DISTANCEMODE_LONG; + else if (RefRange > HRLH) + *pNewDistanceMode = VL53L1_DISTANCEMODE_MEDIUM; + break; + case VL53L1_DISTANCEMODE_MEDIUM: + + if (RefRange > MRLH) + *pNewDistanceMode = VL53L1_DISTANCEMODE_LONG; + else if (RefRange < HRLI) + *pNewDistanceMode = VL53L1_DISTANCEMODE_SHORT; + break; + default: + + if (RefRange < HRLI) + *pNewDistanceMode = VL53L1_DISTANCEMODE_SHORT; + else if (RefRange < MRLI) + *pNewDistanceMode = VL53L1_DISTANCEMODE_MEDIUM; + break; + } +} + +static void CheckAndChangeDistanceMode(VL53L1_DEV Dev, + VL53L1_TargetRangeData_t *pRangeData, + int16_t Ambient100DmaxMm, + VL53L1_DistanceModes *pNewDistanceMode +) +{ + VL53L1_DistanceModes DistanceMode; + uint8_t RangeStatus = pRangeData->RangeStatus; + uint8_t DmaxValid; + int32_t MinAmbient = BDTable[VL53L1_TUNING_MIN_AMBIENT_DMAX_VALID]; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + int32_t tmpint32; + + + switch (RangeStatus) { + case VL53L1_RANGESTATUS_RANGE_VALID_NO_WRAP_CHECK_FAIL: + case VL53L1_RANGESTATUS_WRAP_TARGET_FAIL: + case VL53L1_RANGESTATUS_RANGE_VALID_MERGED_PULSE: + case VL53L1_RANGESTATUS_TARGET_PRESENT_LACK_OF_SIGNAL: + case VL53L1_RANGESTATUS_SYNCRONISATION_INT: + case VL53L1_RANGESTATUS_NONE: + return; + default: + + break; + } + + DmaxValid = 1; + tmpint32 = pdev->hist_data.VL53L1_p_004; + if ((tmpint32 < MinAmbient) || (Ambient100DmaxMm == 0)) + DmaxValid = 0; + + DistanceMode = VL53L1DevDataGet(Dev, + CurrentParameters.DistanceMode); + + *pNewDistanceMode = DistanceMode; + + if (RangeStatus == VL53L1_RANGESTATUS_RANGE_VALID) + GenNewPresetMode(pRangeData->RangeMilliMeter, + DistanceMode, pNewDistanceMode); + else { + if (DmaxValid) + GenNewPresetMode(Ambient100DmaxMm, + DistanceMode, pNewDistanceMode); + else + *pNewDistanceMode = VL53L1_DISTANCEMODE_LONG; + } +} + +static uint8_t ComputeRQL(uint8_t active_results, + uint8_t FilteredRangeStatus, + VL53L1_range_data_t *presults_data) +{ + int16_t T_Wide = 150; + int16_t SRL = 300; + uint16_t SRAS = 30; + FixPoint1616_t RAS; + FixPoint1616_t SRQL; + FixPoint1616_t GI = 7713587; + FixPoint1616_t GGm = 3198157; + FixPoint1616_t LRAP = 6554; + FixPoint1616_t partial; + uint8_t finalvalue; + uint8_t returnvalue; + + if (active_results == 0) + returnvalue = 0; + else if (((presults_data->max_range_mm - + presults_data->min_range_mm) >= T_Wide) || + (FilteredRangeStatus == VL53L1_DEVICEERROR_PHASECONSISTENCY)) + returnvalue = 50; + else { + if (presults_data->median_range_mm < SRL) + RAS = SRAS * 65536; + else + RAS = LRAP * presults_data->median_range_mm; + + + if (RAS != 0) { + partial = (GGm * presults_data->VL53L1_p_005); + partial = partial + (RAS >> 1); + partial = partial / RAS; + partial = partial * 65536; + if (partial <= GI) + SRQL = GI - partial; + else + SRQL = 50 * 65536; + } else + SRQL = 100 * 65536; + + finalvalue = (uint8_t)(SRQL >> 16); + returnvalue = MAX(50, MIN(100, finalvalue)); + } + + return returnvalue; +} + + +static uint8_t ConvertStatusLite(uint8_t FilteredRangeStatus) +{ + uint8_t RangeStatus; + + switch (FilteredRangeStatus) { + case VL53L1_DEVICEERROR_GPHSTREAMCOUNT0READY: + RangeStatus = VL53L1_RANGESTATUS_SYNCRONISATION_INT; + break; + case VL53L1_DEVICEERROR_RANGECOMPLETE_NO_WRAP_CHECK: + RangeStatus = VL53L1_RANGESTATUS_RANGE_VALID_NO_WRAP_CHECK_FAIL; + break; + case VL53L1_DEVICEERROR_RANGEPHASECHECK: + RangeStatus = VL53L1_RANGESTATUS_OUTOFBOUNDS_FAIL; + break; + case VL53L1_DEVICEERROR_MSRCNOTARGET: + RangeStatus = VL53L1_RANGESTATUS_SIGNAL_FAIL; + break; + case VL53L1_DEVICEERROR_SIGMATHRESHOLDCHECK: + RangeStatus = VL53L1_RANGESTATUS_SIGMA_FAIL; + break; + case VL53L1_DEVICEERROR_PHASECONSISTENCY: + RangeStatus = VL53L1_RANGESTATUS_WRAP_TARGET_FAIL; + break; + case VL53L1_DEVICEERROR_RANGEIGNORETHRESHOLD: + RangeStatus = VL53L1_RANGESTATUS_XTALK_SIGNAL_FAIL; + break; + case VL53L1_DEVICEERROR_MINCLIP: + RangeStatus = VL53L1_RANGESTATUS_RANGE_VALID_MIN_RANGE_CLIPPED; + break; + case VL53L1_DEVICEERROR_RANGECOMPLETE: + RangeStatus = VL53L1_RANGESTATUS_RANGE_VALID; + break; + default: + RangeStatus = VL53L1_RANGESTATUS_NONE; + } + + return RangeStatus; +} + + +static uint8_t ConvertStatusHisto(uint8_t FilteredRangeStatus) +{ + uint8_t RangeStatus; + + switch (FilteredRangeStatus) { + case VL53L1_DEVICEERROR_RANGEPHASECHECK: + RangeStatus = VL53L1_RANGESTATUS_OUTOFBOUNDS_FAIL; + break; + case VL53L1_DEVICEERROR_SIGMATHRESHOLDCHECK: + RangeStatus = VL53L1_RANGESTATUS_SIGMA_FAIL; + break; + case VL53L1_DEVICEERROR_RANGECOMPLETE_NO_WRAP_CHECK: + RangeStatus = VL53L1_RANGESTATUS_RANGE_VALID_NO_WRAP_CHECK_FAIL; + break; + case VL53L1_DEVICEERROR_PHASECONSISTENCY: + RangeStatus = VL53L1_RANGESTATUS_WRAP_TARGET_FAIL; + break; + case VL53L1_DEVICEERROR_PREV_RANGE_NO_TARGETS: + RangeStatus = VL53L1_RANGESTATUS_TARGET_PRESENT_LACK_OF_SIGNAL; + break; + case VL53L1_DEVICEERROR_EVENTCONSISTENCY: + RangeStatus = VL53L1_RANGESTATUS_WRAP_TARGET_FAIL; + break; + case VL53L1_DEVICEERROR_RANGECOMPLETE_MERGED_PULSE: + RangeStatus = VL53L1_RANGESTATUS_RANGE_VALID_MERGED_PULSE; + break; + case VL53L1_DEVICEERROR_RANGECOMPLETE: + RangeStatus = VL53L1_RANGESTATUS_RANGE_VALID; + break; + default: + RangeStatus = VL53L1_RANGESTATUS_NONE; + } + + return RangeStatus; +} + +static VL53L1_Error SetSimpleData(VL53L1_DEV Dev, + uint8_t active_results, uint8_t device_status, + VL53L1_range_data_t *presults_data, + VL53L1_RangingMeasurementData_t *pRangeData) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t FilteredRangeStatus; + uint8_t SigmaLimitflag; + uint8_t SignalLimitflag; + uint8_t Temp8Enable; + uint8_t Temp8; + FixPoint1616_t AmbientRate; + FixPoint1616_t SignalRate; + FixPoint1616_t TempFix1616; + FixPoint1616_t LimitCheckValue; + VL53L1_PresetModes PresetMode; + int16_t Range; + + pRangeData->TimeStamp = presults_data->time_stamp; + + FilteredRangeStatus = presults_data->range_status & 0x1F; + + pRangeData->RangeQualityLevel = ComputeRQL(active_results, + FilteredRangeStatus, + presults_data); + + SignalRate = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->peak_signal_count_rate_mcps); + pRangeData->SignalRateRtnMegaCps + = SignalRate; + + AmbientRate = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->ambient_count_rate_mcps); + pRangeData->AmbientRateRtnMegaCps = AmbientRate; + + pRangeData->EffectiveSpadRtnCount = + presults_data->VL53L1_p_006; + + TempFix1616 = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->VL53L1_p_005); + + pRangeData->SigmaMilliMeter = TempFix1616; + + pRangeData->RangeMilliMeter = presults_data->median_range_mm; + + pRangeData->RangeFractionalPart = 0; + + + switch (device_status) { + case VL53L1_DEVICEERROR_MULTCLIPFAIL: + case VL53L1_DEVICEERROR_VCSELWATCHDOGTESTFAILURE: + case VL53L1_DEVICEERROR_VCSELCONTINUITYTESTFAILURE: + case VL53L1_DEVICEERROR_NOVHVVALUEFOUND: + pRangeData->RangeStatus = VL53L1_RANGESTATUS_HARDWARE_FAIL; + break; + case VL53L1_DEVICEERROR_USERROICLIP: + pRangeData->RangeStatus = VL53L1_RANGESTATUS_MIN_RANGE_FAIL; + break; + default: + pRangeData->RangeStatus = VL53L1_RANGESTATUS_RANGE_VALID; + } + + + if (pRangeData->RangeStatus == VL53L1_RANGESTATUS_RANGE_VALID) { + PresetMode = VL53L1DevDataGet(Dev, + CurrentParameters.PresetMode); + if ((PresetMode == VL53L1_PRESETMODE_MULTIZONES_SCANNING) || + (PresetMode == VL53L1_PRESETMODE_RANGING) || + (PresetMode == VL53L1_PRESETMODE_PROXY_RANGING_MODE)) + pRangeData->RangeStatus = + ConvertStatusHisto(FilteredRangeStatus); + else + pRangeData->RangeStatus = + ConvertStatusLite(FilteredRangeStatus); + } + + + TempFix1616 = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->VL53L1_p_005); + VL53L1_SETARRAYPARAMETERFIELD(Dev, + LimitChecksCurrent, VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, + TempFix1616); + + TempFix1616 = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->peak_signal_count_rate_mcps); + VL53L1_SETARRAYPARAMETERFIELD(Dev, + LimitChecksCurrent, VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + TempFix1616); + + + + VL53L1_GetLimitCheckValue(Dev, + VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, + &LimitCheckValue); + + SigmaLimitflag = (FilteredRangeStatus == + VL53L1_DEVICEERROR_SIGMATHRESHOLDCHECK) + ? 1 : 0; + + VL53L1_GetLimitCheckEnable(Dev, + VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, + &Temp8Enable); + + Temp8 = ((Temp8Enable == 1) && (SigmaLimitflag == 1)) ? 1 : 0; + VL53L1_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, Temp8); + + + VL53L1_GetLimitCheckValue(Dev, + VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + &LimitCheckValue); + + SignalLimitflag = (FilteredRangeStatus == + VL53L1_DEVICEERROR_MSRCNOTARGET) + ? 1 : 0; + + VL53L1_GetLimitCheckEnable(Dev, + VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + &Temp8Enable); + + Temp8 = ((Temp8Enable == 1) && (SignalLimitflag == 1)) ? 1 : 0; + VL53L1_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, Temp8); + + Range = pRangeData->RangeMilliMeter; + if ((pRangeData->RangeStatus == VL53L1_RANGESTATUS_RANGE_VALID) && + (Range < 0)) { + if (Range < BDTable[VL53L1_TUNING_PROXY_MIN]) + pRangeData->RangeStatus = + VL53L1_RANGESTATUS_RANGE_INVALID; + else + pRangeData->RangeMilliMeter = 0; + } + + return Status; +} + +static VL53L1_Error SetTargetData(VL53L1_DEV Dev, + uint8_t active_results, uint8_t streamcount, uint8_t iteration, + uint8_t device_status, VL53L1_range_data_t *presults_data, + VL53L1_TargetRangeData_t *pRangeData) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_tuning_parm_storage_t *tp = + &(pdev->tuning_parms); + uint8_t sequency; + uint8_t FilteredRangeStatus; + uint8_t SigmaLimitflag; + uint8_t SignalLimitflag; + uint8_t Temp8Enable; + uint8_t Temp8; + FixPoint1616_t AmbientRate; + FixPoint1616_t SignalRate; + FixPoint1616_t TempFix1616; + FixPoint1616_t LimitCheckValue; + VL53L1_PresetModes PresetMode; + int16_t Range, RangeDiff, RangeMillimeterInit; + int32_t ExtendedRangeEnabled = 0; + uint8_t uwr_status; + int16_t AddOffset; + int32_t TuningMinValidSignal; + FixPoint1616_t MinValidSignal; + + FilteredRangeStatus = presults_data->range_status & 0x1F; + + pRangeData->RangeQualityLevel = ComputeRQL(active_results, + FilteredRangeStatus, + presults_data); + + SignalRate = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->peak_signal_count_rate_mcps); + pRangeData->SignalRateRtnMegaCps + = SignalRate; + + AmbientRate = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->ambient_count_rate_mcps); + pRangeData->AmbientRateRtnMegaCps = AmbientRate; + + TempFix1616 = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->VL53L1_p_005); + + pRangeData->SigmaMilliMeter = TempFix1616; + + pRangeData->RangeMilliMeter = presults_data->median_range_mm; + pRangeData->RangeMaxMilliMeter = presults_data->max_range_mm; + pRangeData->RangeMinMilliMeter = presults_data->min_range_mm; + + pRangeData->RangeFractionalPart = 0; + + + switch (device_status) { + case VL53L1_DEVICEERROR_MULTCLIPFAIL: + case VL53L1_DEVICEERROR_VCSELWATCHDOGTESTFAILURE: + case VL53L1_DEVICEERROR_VCSELCONTINUITYTESTFAILURE: + case VL53L1_DEVICEERROR_NOVHVVALUEFOUND: + pRangeData->RangeStatus = VL53L1_RANGESTATUS_HARDWARE_FAIL; + break; + case VL53L1_DEVICEERROR_USERROICLIP: + pRangeData->RangeStatus = VL53L1_RANGESTATUS_MIN_RANGE_FAIL; + break; + default: + pRangeData->RangeStatus = VL53L1_RANGESTATUS_RANGE_VALID; + } + + + if ((pRangeData->RangeStatus == VL53L1_RANGESTATUS_RANGE_VALID) && + (active_results == 0)) { + pRangeData->RangeStatus = VL53L1_RANGESTATUS_NONE; + pRangeData->SignalRateRtnMegaCps = 0; + pRangeData->SigmaMilliMeter = 0; + pRangeData->RangeMilliMeter = 8191; + pRangeData->RangeMaxMilliMeter = 8191; + pRangeData->RangeMinMilliMeter = 8191; + } + + + if (pRangeData->RangeStatus == VL53L1_RANGESTATUS_RANGE_VALID) { + PresetMode = VL53L1DevDataGet(Dev, + CurrentParameters.PresetMode); + if ((PresetMode == VL53L1_PRESETMODE_MULTIZONES_SCANNING) || + (PresetMode == VL53L1_PRESETMODE_RANGING) || + (PresetMode == VL53L1_PRESETMODE_PROXY_RANGING_MODE)) + pRangeData->RangeStatus = + ConvertStatusHisto(FilteredRangeStatus); + else + pRangeData->RangeStatus = + ConvertStatusLite(FilteredRangeStatus); + } + + + TempFix1616 = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->VL53L1_p_005); + VL53L1_SETARRAYPARAMETERFIELD(Dev, + LimitChecksCurrent, VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, + TempFix1616); + + TempFix1616 = VL53L1_FIXPOINT97TOFIXPOINT1616( + presults_data->peak_signal_count_rate_mcps); + VL53L1_SETARRAYPARAMETERFIELD(Dev, + LimitChecksCurrent, VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + TempFix1616); + + + + VL53L1_GetLimitCheckValue(Dev, + VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, + &LimitCheckValue); + + SigmaLimitflag = (FilteredRangeStatus == + VL53L1_DEVICEERROR_SIGMATHRESHOLDCHECK) + ? 1 : 0; + + VL53L1_GetLimitCheckEnable(Dev, + VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, + &Temp8Enable); + + Temp8 = ((Temp8Enable == 1) && (SigmaLimitflag == 1)) ? 1 : 0; + VL53L1_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, Temp8); + + + VL53L1_GetLimitCheckValue(Dev, + VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + &LimitCheckValue); + + SignalLimitflag = (FilteredRangeStatus == + VL53L1_DEVICEERROR_MSRCNOTARGET) + ? 1 : 0; + + VL53L1_GetLimitCheckEnable(Dev, + VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, + &Temp8Enable); + + Temp8 = ((Temp8Enable == 1) && (SignalLimitflag == 1)) ? 1 : 0; + VL53L1_SETARRAYPARAMETERFIELD(Dev, LimitChecksStatus, + VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE, Temp8); + + Range = pRangeData->RangeMilliMeter; + if ((pRangeData->RangeStatus == VL53L1_RANGESTATUS_RANGE_VALID) && + (Range < 0)) { + if (Range < BDTable[VL53L1_TUNING_PROXY_MIN]) + pRangeData->RangeStatus = + VL53L1_RANGESTATUS_RANGE_INVALID; + else + pRangeData->RangeMilliMeter = 0; + } + + + VL53L1_get_tuning_parm(Dev, + VL53L1_TUNINGPARM_MIN_SIGNAL_SECONDARY_TARGETS, + &TuningMinValidSignal); + MinValidSignal = (FixPoint1616_t)TuningMinValidSignal; + if ((iteration > 0) && + (pRangeData->RangeStatus == VL53L1_RANGESTATUS_RANGE_VALID) && + (pRangeData->SignalRateRtnMegaCps < MinValidSignal)) + pRangeData->RangeStatus = + VL53L1_RANGESTATUS_TARGET_PRESENT_LACK_OF_SIGNAL; + + + + + VL53L1_get_tuning_parm(Dev, VL53L1_TUNINGPARM_UWR_ENABLE, + &ExtendedRangeEnabled); + + sequency = streamcount % 2; + uwr_status = 0; + RangeMillimeterInit = pRangeData->RangeMilliMeter; + AddOffset = 0; + + pRangeData->ExtendedRange = 0; + + if ((active_results != 1) || + (pdev->PreviousRangeActiveResults != 1)) + ExtendedRangeEnabled = 0; + + if (ExtendedRangeEnabled && + (pRangeData->RangeStatus == + VL53L1_RANGESTATUS_WRAP_TARGET_FAIL || + pRangeData->RangeStatus == + VL53L1_RANGESTATUS_OUTOFBOUNDS_FAIL) + && (pdev->PreviousRangeStatus[iteration] == + VL53L1_RANGESTATUS_WRAP_TARGET_FAIL || + pdev->PreviousRangeStatus[iteration] == + VL53L1_RANGESTATUS_OUTOFBOUNDS_FAIL || + (pdev->PreviousRangeStatus[iteration] == + VL53L1_RANGESTATUS_RANGE_VALID && + pdev->PreviousExtendedRange[iteration] == 1))) + { + if (((pdev->PreviousStreamCount) == + (pdev->hist_data.result__stream_count - 1 )) + || ((pdev->PreviousStreamCount) == + (pdev->hist_data.result__stream_count + 127))) + { + RangeDiff = pRangeData->RangeMilliMeter - + pdev->PreviousRangeMilliMeter[iteration]; + + uwr_status = 1; + switch (pdev->preset_mode) { + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE: + + uwr_status = 0; + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE: + if (RangeDiff > tp->tp_uwr_med_z_1_min && + RangeDiff < tp->tp_uwr_med_z_1_max && + sequency == 1) { + AddOffset = + tp->tp_uwr_med_corr_z_1_rangeb; + } + else + if (RangeDiff < -tp->tp_uwr_med_z_1_min && + RangeDiff > -tp->tp_uwr_med_z_1_max && + sequency == 0) { + AddOffset = + tp->tp_uwr_med_corr_z_1_rangea; + } + else + if (RangeDiff > tp->tp_uwr_med_z_2_min && + RangeDiff < tp->tp_uwr_med_z_2_max && + sequency == 0) { + AddOffset = + tp->tp_uwr_med_corr_z_2_rangea; + } + else + if (RangeDiff < -tp->tp_uwr_med_z_2_min && + RangeDiff > -tp->tp_uwr_med_z_2_max && + sequency == 1) { + AddOffset = + tp->tp_uwr_med_corr_z_2_rangeb; + } + else + if (RangeDiff > tp->tp_uwr_med_z_3_min && + RangeDiff < tp->tp_uwr_med_z_3_max && + sequency == 1) { + AddOffset = + tp->tp_uwr_med_corr_z_3_rangeb; + } + else + if (RangeDiff < -tp->tp_uwr_med_z_3_min && + RangeDiff > -tp->tp_uwr_med_z_3_max && + sequency == 0) { + AddOffset = + tp->tp_uwr_med_corr_z_3_rangea; + } + else + if (RangeDiff > tp->tp_uwr_med_z_4_min && + RangeDiff < tp->tp_uwr_med_z_4_max && + sequency == 0) { + AddOffset = + tp->tp_uwr_med_corr_z_4_rangea; + } + else + if (RangeDiff < -tp->tp_uwr_med_z_4_min && + RangeDiff > -tp->tp_uwr_med_z_4_max && + sequency == 1) { + AddOffset = + tp->tp_uwr_med_corr_z_4_rangeb; + } + else + if (RangeDiff < tp->tp_uwr_med_z_5_max && + RangeDiff > tp->tp_uwr_med_z_5_min) { + AddOffset = + tp->tp_uwr_med_corr_z_5_rangea; + } + else + if (RangeDiff > tp->tp_uwr_med_z_6_min && + RangeDiff < tp->tp_uwr_med_z_6_max && + sequency == 1) { + AddOffset = + tp->tp_uwr_med_corr_z_6_rangeb; + } + else + if (RangeDiff < -tp->tp_uwr_med_z_6_min && + RangeDiff > -tp->tp_uwr_med_z_6_max && + sequency == 0) { + AddOffset = + tp->tp_uwr_med_corr_z_6_rangea; + } + else + uwr_status = 0; + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE: + if (RangeDiff > tp->tp_uwr_lng_z_1_min && + RangeDiff < tp->tp_uwr_lng_z_1_max && + sequency == 0) { + AddOffset = + tp->tp_uwr_lng_corr_z_1_rangea; + } + else + if (RangeDiff < -tp->tp_uwr_lng_z_1_min && + RangeDiff > -tp->tp_uwr_lng_z_1_max && + sequency == 1) { + AddOffset = + tp->tp_uwr_lng_corr_z_1_rangeb; + } + else + if (RangeDiff > tp->tp_uwr_lng_z_2_min && + RangeDiff < tp->tp_uwr_lng_z_2_max && + sequency == 1) { + AddOffset = + tp->tp_uwr_lng_corr_z_2_rangeb; + } + else + if (RangeDiff < -tp->tp_uwr_lng_z_2_min && + RangeDiff > -tp->tp_uwr_lng_z_2_max && + sequency == 0) { + AddOffset = + tp->tp_uwr_lng_corr_z_2_rangea; + } + else + if (RangeDiff < tp->tp_uwr_lng_z_3_max && + RangeDiff > tp->tp_uwr_lng_z_3_min) { + AddOffset = + tp->tp_uwr_lng_corr_z_3_rangea; + } + else + if (RangeDiff > tp->tp_uwr_lng_z_4_min && + RangeDiff < tp->tp_uwr_lng_z_4_max && + sequency == 1) { + AddOffset = + tp->tp_uwr_lng_corr_z_4_rangeb; + } + else + if (RangeDiff < -tp->tp_uwr_lng_z_4_min && + RangeDiff > -tp->tp_uwr_lng_z_4_max && + sequency == 0) { + AddOffset = + tp->tp_uwr_lng_corr_z_4_rangea; + } + else + uwr_status = 0; + break; + + default: + uwr_status = 0; + break; + } + } + + if (uwr_status) { + pRangeData->RangeMilliMeter += AddOffset; + pRangeData->RangeMinMilliMeter += AddOffset; + pRangeData->RangeMaxMilliMeter += AddOffset; + pRangeData->ExtendedRange = 1; + pRangeData->RangeStatus = 0; + } + + } + + pdev->PreviousRangeMilliMeter[iteration] = RangeMillimeterInit; + pdev->PreviousRangeStatus[iteration] = pRangeData->RangeStatus; + pdev->PreviousExtendedRange[iteration] = pRangeData->ExtendedRange; + pdev->PreviousRangeActiveResults = active_results; + + return Status; +} + +static uint8_t GetOutputDataIndex(VL53L1_DEV Dev, + VL53L1_range_results_t *presults) +{ + uint8_t i; + uint8_t index = 0; + VL53L1_OutputModes OutputMode; + + OutputMode = VL53L1DevDataGet(Dev, CurrentParameters.OutputMode); + + + if (OutputMode == VL53L1_OUTPUTMODE_NEAREST) + return 0; + + + for (i = 1; i < presults->active_results; i++) { + if (presults->VL53L1_p_002[i].peak_signal_count_rate_mcps > + presults->VL53L1_p_002[index].peak_signal_count_rate_mcps) + index = i; + } + + return index; +} + +VL53L1_Error VL53L1_GetRangingMeasurementData(VL53L1_DEV Dev, + VL53L1_RangingMeasurementData_t *pRangingMeasurementData) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_range_results_t *presults = + (VL53L1_range_results_t *) pdev->wArea1; + VL53L1_range_data_t *presults_data; + VL53L1_PresetModes PresetMode; + uint8_t index = 0; + + LOG_FUNCTION_START(""); + + + PresetMode = VL53L1DevDataGet(Dev, CurrentParameters.PresetMode); + + if (PresetMode == VL53L1_PRESETMODE_MULTIZONES_SCANNING) { + Status = VL53L1_ERROR_MODE_NOT_SUPPORTED; + LOG_FUNCTION_END(Status); + return Status; + } + + + memset(pRangingMeasurementData, 0xFF, + sizeof(VL53L1_RangingMeasurementData_t)); + + + Status = VL53L1_get_device_results( + Dev, + VL53L1_DEVICERESULTSLEVEL_FULL, + presults); + + if (Status == VL53L1_ERROR_NONE) { + pRangingMeasurementData->StreamCount = presults->stream_count; + + + index = GetOutputDataIndex(Dev, presults); + presults_data = &(presults->VL53L1_p_002[index]); + Status = SetSimpleData(Dev, presults->active_results, + presults->device_status, + presults_data, + pRangingMeasurementData); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +static VL53L1_Error SetMeasurementData(VL53L1_DEV Dev, + VL53L1_range_results_t *presults, + VL53L1_MultiRangingData_t *pMultiRangingData) +{ + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + uint8_t i; + uint8_t iteration; + VL53L1_TargetRangeData_t *pRangeData; + VL53L1_range_data_t *presults_data; + int16_t dmax_min; + VL53L1_Error Status = VL53L1_ERROR_NONE; + uint8_t Furthest_idx = 0; + int16_t Furthest_range = 0; + uint8_t ActiveResults, amb_idx; + + pMultiRangingData->NumberOfObjectsFound = presults->active_results; + pMultiRangingData->RoiNumber = presults->zone_id; + pMultiRangingData->HasXtalkValueChanged = + presults->smudge_corrector_data.new_xtalk_applied_flag; + dmax_min = MIN(presults->wrap_dmax_mm, + presults->VL53L1_p_007[DMAX_REFLECTANCE_IDX]); + pMultiRangingData->DmaxMilliMeter = dmax_min; + + + pMultiRangingData->TimeStamp = 0; + + pMultiRangingData->StreamCount = presults->stream_count; + + pMultiRangingData->RecommendedDistanceMode = + VL53L1DevDataGet(Dev, CurrentParameters.DistanceMode); + ActiveResults = presults->active_results; + if (ActiveResults < 1) + + iteration = 1; + else + iteration = ActiveResults; + for (i = 0; i < iteration; i++) { + pRangeData = &(pMultiRangingData->RangeData[i]); + + presults_data = &(presults->VL53L1_p_002[i]); + if (Status == VL53L1_ERROR_NONE) + Status = SetTargetData(Dev, ActiveResults, + pMultiRangingData->StreamCount, + i, + presults->device_status, + presults_data, + pRangeData); + + pMultiRangingData->EffectiveSpadRtnCount = + presults_data->VL53L1_p_006; + + if ((pRangeData->RangeStatus == VL53L1_RANGESTATUS_RANGE_VALID) + && (pRangeData->RangeMilliMeter > Furthest_range)) { + Furthest_range = pRangeData->RangeMilliMeter; + Furthest_idx = i; + } + } + pdev->PreviousStreamCount = pdev->hist_data.result__stream_count; + for (i = iteration; i < VL53L1_MAX_RANGE_RESULTS; i++) { + pdev->PreviousRangeMilliMeter[i] = 0; + pdev->PreviousRangeStatus[i] = 255; + pdev->PreviousExtendedRange[i] = 0; + } + + if ((Status == VL53L1_ERROR_NONE) && (ActiveResults > 0)) { + pRangeData = &(pMultiRangingData->RangeData[Furthest_idx]); + amb_idx = VL53L1_MAX_AMBIENT_DMAX_VALUES-1; + CheckAndChangeDistanceMode(Dev, pRangeData, + presults->VL53L1_p_007[amb_idx], + &pMultiRangingData->RecommendedDistanceMode); + } + + return Status; +} + +VL53L1_Error VL53L1_GetMultiRangingData(VL53L1_DEV Dev, + VL53L1_MultiRangingData_t *pMultiRangingData) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_range_results_t *presults = + (VL53L1_range_results_t *) pdev->wArea1; + + LOG_FUNCTION_START(""); + + + memset(pMultiRangingData, 0xFF, + sizeof(VL53L1_MultiRangingData_t)); + + + Status = VL53L1_get_device_results( + Dev, + VL53L1_DEVICERESULTSLEVEL_FULL, + presults); + + + if (Status == VL53L1_ERROR_NONE) { + switch (presults->rd_device_state) { + case VL53L1_DEVICESTATE_RANGING_GATHER_DATA: + pMultiRangingData->RoiStatus = + VL53L1_ROISTATUS_VALID_NOT_LAST; + break; + case VL53L1_DEVICESTATE_RANGING_OUTPUT_DATA: + pMultiRangingData->RoiStatus = + VL53L1_ROISTATUS_VALID_LAST; + break; + default: + pMultiRangingData->RoiStatus = + VL53L1_ROISTATUS_NOT_VALID; + } + + Status = SetMeasurementData(Dev, + presults, + pMultiRangingData); + + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetAdditionalData(VL53L1_DEV Dev, + VL53L1_AdditionalData_t *pAdditionalData) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_additional_data(Dev, pAdditionalData); + + LOG_FUNCTION_END(Status); + return Status; +} + + + + + + +VL53L1_Error VL53L1_SetTuningParameter(VL53L1_DEV Dev, + uint16_t TuningParameterId, int32_t TuningParameterValue) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (TuningParameterId == + VL53L1_TUNINGPARM_DYNXTALK_NODETECT_XTALK_OFFSET_KCPS) + return VL53L1_ERROR_INVALID_PARAMS; + + if (TuningParameterId >= 32768) + Status = VL53L1_set_tuning_parm(Dev, + TuningParameterId, + TuningParameterValue); + else { + if (TuningParameterId < VL53L1_TUNING_MAX_TUNABLE_KEY) + BDTable[TuningParameterId] = TuningParameterValue; + else + Status = VL53L1_ERROR_INVALID_PARAMS; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetTuningParameter(VL53L1_DEV Dev, + uint16_t TuningParameterId, int32_t *pTuningParameterValue) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (TuningParameterId >= 32768) + Status = VL53L1_get_tuning_parm(Dev, + TuningParameterId, + pTuningParameterValue); + else { + if (TuningParameterId < VL53L1_TUNING_MAX_TUNABLE_KEY) + *pTuningParameterValue = BDTable[TuningParameterId]; + else + Status = VL53L1_ERROR_INVALID_PARAMS; + } + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_PerformRefSpadManagement(VL53L1_DEV Dev) +{ +#ifdef VL53L1_NOCALIB + VL53L1_Error Status = VL53L1_ERROR_NOT_SUPPORTED; + + SUPPRESS_UNUSED_WARNING(Dev); + + LOG_FUNCTION_START(""); +#else + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_Error RawStatus; + uint8_t dcrbuffer[24]; + uint8_t *commbuf; + uint8_t numloc[2] = {5, 3}; + VL53L1_LLDriverData_t *pdev; + VL53L1_customer_nvm_managed_t *pc; + VL53L1_PresetModes PresetMode; + + LOG_FUNCTION_START(""); + + pdev = VL53L1DevStructGetLLDriverHandle(Dev); + pc = &pdev->customer; + + if (Status == VL53L1_ERROR_NONE) { + PresetMode = VL53L1DevDataGet(Dev, + CurrentParameters.PresetMode); + Status = VL53L1_run_ref_spad_char(Dev, &RawStatus); + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_SetPresetMode(Dev, PresetMode); + } + + if (Status == VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_HIGH) { + + Status = VL53L1_read_nvm_raw_data(Dev, + (uint8_t)(0xA0 >> 2), + (uint8_t)(24 >> 2), + dcrbuffer); + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_WriteMulti(Dev, + VL53L1_REF_SPAD_MAN__NUM_REQUESTED_REF_SPADS, + numloc, 2); + + if (Status == VL53L1_ERROR_NONE) { + pc->ref_spad_man__num_requested_ref_spads = numloc[0]; + pc->ref_spad_man__ref_location = numloc[1]; + } + + commbuf = &dcrbuffer[16]; + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_WriteMulti(Dev, + VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_0, + commbuf, 6); + + if (Status == VL53L1_ERROR_NONE) { + pc->global_config__spad_enables_ref_0 = commbuf[0]; + pc->global_config__spad_enables_ref_1 = commbuf[1]; + pc->global_config__spad_enables_ref_2 = commbuf[2]; + pc->global_config__spad_enables_ref_3 = commbuf[3]; + pc->global_config__spad_enables_ref_4 = commbuf[4]; + pc->global_config__spad_enables_ref_5 = commbuf[5]; + } + + } + +#endif + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SmudgeCorrectionEnable(VL53L1_DEV Dev, + VL53L1_SmudgeCorrectionModes Mode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_Error s1 = VL53L1_ERROR_NONE; + VL53L1_Error s2 = VL53L1_ERROR_NONE; + VL53L1_Error s3 = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + switch (Mode) { + case VL53L1_SMUDGE_CORRECTION_NONE: + s1 = VL53L1_dynamic_xtalk_correction_disable(Dev); + s2 = VL53L1_dynamic_xtalk_correction_apply_disable(Dev); + s3 = VL53L1_dynamic_xtalk_correction_single_apply_disable(Dev); + break; + case VL53L1_SMUDGE_CORRECTION_CONTINUOUS: + s1 = VL53L1_dynamic_xtalk_correction_enable(Dev); + s2 = VL53L1_dynamic_xtalk_correction_apply_enable(Dev); + s3 = VL53L1_dynamic_xtalk_correction_single_apply_disable(Dev); + break; + case VL53L1_SMUDGE_CORRECTION_SINGLE: + s1 = VL53L1_dynamic_xtalk_correction_enable(Dev); + s2 = VL53L1_dynamic_xtalk_correction_apply_enable(Dev); + s3 = VL53L1_dynamic_xtalk_correction_single_apply_enable(Dev); + break; + case VL53L1_SMUDGE_CORRECTION_DEBUG: + s1 = VL53L1_dynamic_xtalk_correction_enable(Dev); + s2 = VL53L1_dynamic_xtalk_correction_apply_disable(Dev); + s3 = VL53L1_dynamic_xtalk_correction_single_apply_disable(Dev); + break; + default: + Status = VL53L1_ERROR_INVALID_PARAMS; + break; + } + + if (Status == VL53L1_ERROR_NONE) { + Status = s1; + if (Status == VL53L1_ERROR_NONE) + Status = s2; + if (Status == VL53L1_ERROR_NONE) + Status = s3; + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetXTalkCompensationEnable(VL53L1_DEV Dev, + uint8_t XTalkCompensationEnable) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (XTalkCompensationEnable == 0) + Status = VL53L1_disable_xtalk_compensation(Dev); + else + Status = VL53L1_enable_xtalk_compensation(Dev); + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_GetXTalkCompensationEnable(VL53L1_DEV Dev, + uint8_t *pXTalkCompensationEnable) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + VL53L1_get_xtalk_compensation_enable( + Dev, + pXTalkCompensationEnable); + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_PerformXTalkCalibration(VL53L1_DEV Dev, + uint8_t CalibrationOption) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_Error UStatus; + int16_t CalDistanceMm; + VL53L1_xtalk_calibration_results_t xtalk; + + VL53L1_CalibrationData_t caldata; + VL53L1_LLDriverData_t *pLLData; + int i; + uint32_t *pPlaneOffsetKcps; + uint32_t Margin = + BDTable[VL53L1_TUNING_XTALK_FULL_ROI_BIN_SUM_MARGIN]; + uint32_t DefaultOffset = + BDTable[VL53L1_TUNING_XTALK_FULL_ROI_DEFAULT_OFFSET]; + uint32_t *pLLDataPlaneOffsetKcps; + uint32_t sum = 0; + uint8_t binok = 0; + int32_t merge; + + LOG_FUNCTION_START(""); + + pPlaneOffsetKcps = + &caldata.customer.algo__crosstalk_compensation_plane_offset_kcps; + pLLData = VL53L1DevStructGetLLDriverHandle(Dev); + pLLDataPlaneOffsetKcps = + &pLLData->xtalk_cal.algo__crosstalk_compensation_plane_offset_kcps; + VL53L1_get_tuning_parm(Dev, VL53L1_TUNINGPARM_HIST_MERGE, &merge); + + VL53L1_set_tuning_parm(Dev, VL53L1_TUNINGPARM_HIST_MERGE, 0); + switch (CalibrationOption) { + case VL53L1_XTALKCALIBRATIONMODE_NO_TARGET: + Status = VL53L1_run_xtalk_extraction(Dev, &UStatus); + + if (Status == VL53L1_ERROR_XTALK_EXTRACTION_NO_SAMPLE_FAIL) + VL53L1_xtalk_cal_data_init(Dev); + break; + case VL53L1_XTALKCALIBRATIONMODE_SINGLE_TARGET: + Status = SingleTargetXTalkCalibration(Dev); + break; + case VL53L1_XTALKCALIBRATIONMODE_FULL_ROI: + CalDistanceMm = (int16_t) + BDTable[VL53L1_TUNING_XTALK_FULL_ROI_TARGET_DISTANCE_MM]; + + VL53L1_set_tuning_parm(Dev, VL53L1_TUNINGPARM_HIST_MERGE, + merge); + Status = VL53L1_run_hist_xtalk_extraction(Dev, CalDistanceMm, + &UStatus); + + VL53L1_GetCalibrationData(Dev, &caldata); + for (i = 0; i < VL53L1_XTALK_HISTO_BINS; i++) { + sum += caldata.xtalkhisto.xtalk_shape.bin_data[i]; + if (caldata.xtalkhisto.xtalk_shape.bin_data[i] > 0) + binok++; + } + if ((UStatus == + VL53L1_ERROR_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL) || + (sum > (1024 + Margin)) || (sum < (1024 - Margin)) || + (binok < 3)) { + *pPlaneOffsetKcps = DefaultOffset; + *pLLDataPlaneOffsetKcps = DefaultOffset; + caldata.xtalkhisto.xtalk_shape.bin_data[0] = 307; + caldata.xtalkhisto.xtalk_shape.bin_data[1] = 410; + caldata.xtalkhisto.xtalk_shape.bin_data[2] = 410; + caldata.xtalkhisto.xtalk_shape.bin_data[3] = 307; + for (i = 4; i < VL53L1_XTALK_HISTO_BINS; i++) + caldata.xtalkhisto.xtalk_shape.bin_data[i] = 0; + for (i = 0; i < VL53L1_BIN_REC_SIZE; i++) + caldata.algo__xtalk_cpo_HistoMerge_kcps[i] = + DefaultOffset + DefaultOffset * i; + VL53L1_SetCalibrationData(Dev, &caldata); + } + + break; + default: + Status = VL53L1_ERROR_INVALID_PARAMS; + } + VL53L1_set_tuning_parm(Dev, VL53L1_TUNINGPARM_HIST_MERGE, merge); + + if (Status == VL53L1_ERROR_NONE) { + Status = VL53L1_get_current_xtalk_settings(Dev, &xtalk); + Status = VL53L1_set_tuning_parm(Dev, + VL53L1_TUNINGPARM_DYNXTALK_NODETECT_XTALK_OFFSET_KCPS, + xtalk.algo__crosstalk_compensation_plane_offset_kcps); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetOffsetCalibrationMode(VL53L1_DEV Dev, + VL53L1_OffsetCalibrationModes OffsetCalibrationMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_OffsetCalibrationMode offset_cal_mode; + + LOG_FUNCTION_START(""); + + offset_cal_mode = + VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD; + + if (OffsetCalibrationMode == VL53L1_OFFSETCALIBRATIONMODE_STANDARD) { + offset_cal_mode = + VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD; + } else if (OffsetCalibrationMode == + VL53L1_OFFSETCALIBRATIONMODE_PRERANGE_ONLY) { + offset_cal_mode = + VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD_PRE_RANGE_ONLY; + } else if (OffsetCalibrationMode == + VL53L1_OFFSETCALIBRATIONMODE_MULTI_ZONE) { + offset_cal_mode = + VL53L1_OFFSETCALIBRATIONMODE__PER_ZONE; + } else { + Status = VL53L1_ERROR_INVALID_PARAMS; + } + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_set_offset_calibration_mode(Dev, + offset_cal_mode); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetOffsetCorrectionMode(VL53L1_DEV Dev, + VL53L1_OffsetCorrectionModes OffsetCorrectionMode) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_OffsetCorrectionMode offset_cor_mode; + + LOG_FUNCTION_START(""); + + offset_cor_mode = + VL53L1_OFFSETCORRECTIONMODE__MM1_MM2_OFFSETS; + if (OffsetCorrectionMode == VL53L1_OFFSETCORRECTIONMODE_STANDARD) { + offset_cor_mode = + VL53L1_OFFSETCORRECTIONMODE__MM1_MM2_OFFSETS; + } else if (OffsetCorrectionMode == + VL53L1_OFFSETCORRECTIONMODE_PERZONE) { + offset_cor_mode = + VL53L1_OFFSETCORRECTIONMODE__PER_ZONE_OFFSETS; + } else if (OffsetCorrectionMode == + VL53L1_OFFSETCORRECTIONMODE_PERVCSEL) { + offset_cor_mode = + VL53L1_OFFSETCORRECTIONMODE__PER_VCSEL_OFFSETS; + } else { + Status = VL53L1_ERROR_INVALID_PARAMS; + } + + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_set_offset_correction_mode(Dev, + offset_cor_mode); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_PerformOffsetCalibration(VL53L1_DEV Dev, + int32_t CalDistanceMilliMeter, FixPoint1616_t CalReflectancePercent) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_Error UnfilteredStatus; + VL53L1_OffsetCalibrationMode offset_cal_mode; + uint16_t CalReflectancePercent_int; + + VL53L1_DevicePresetModes device_preset_mode; + VL53L1_DeviceZonePreset zone_preset; + VL53L1_zone_config_t zone_cfg; + int32_t MergeEnabled; + + LOG_FUNCTION_START(""); + + CalReflectancePercent_int = + VL53L1_FIXPOINT1616TOFIXPOINT72(CalReflectancePercent); + + Status = VL53L1_get_offset_calibration_mode(Dev, + &offset_cal_mode); + + if (Status != VL53L1_ERROR_NONE) { + LOG_FUNCTION_END(Status); + return Status; + } + + + if ((offset_cal_mode == + VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD) || + (offset_cal_mode == + VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD_PRE_RANGE_ONLY + )) { + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_run_offset_calibration( + Dev, + (int16_t)CalDistanceMilliMeter, + CalReflectancePercent_int, + &UnfilteredStatus); + + } else if (offset_cal_mode == + VL53L1_OFFSETCALIBRATIONMODE__PER_ZONE) { + VL53L1_get_tuning_parm(Dev, VL53L1_TUNINGPARM_HIST_MERGE, + &MergeEnabled); + VL53L1_set_tuning_parm(Dev, VL53L1_TUNINGPARM_HIST_MERGE, 0); + device_preset_mode = + VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE_LONG_RANGE; + zone_preset = VL53L1_DEVICEZONEPRESET_CUSTOM; + + Status = VL53L1_get_zone_config(Dev, &zone_cfg); + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_run_zone_calibration( + Dev, + device_preset_mode, + zone_preset, + &zone_cfg, + (int16_t)CalDistanceMilliMeter, + CalReflectancePercent_int, + &UnfilteredStatus); + VL53L1_set_tuning_parm(Dev, VL53L1_TUNINGPARM_HIST_MERGE, + MergeEnabled); + + } else { + Status = VL53L1_ERROR_INVALID_PARAMS; + } + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_PerformOffsetSimpleCalibration(VL53L1_DEV Dev, + int32_t CalDistanceMilliMeter) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + int32_t sum_ranging; + uint8_t offset_meas; + int16_t Max, UnderMax, OverMax, Repeat; + int32_t total_count, inloopcount; + int32_t IncRounding; + int16_t meanDistance_mm; + int16_t offset; + VL53L1_RangingMeasurementData_t RangingMeasurementData; + VL53L1_LLDriverData_t *pdev; + uint8_t goodmeas; + VL53L1_Error SmudgeStatus = VL53L1_ERROR_NONE; + uint8_t smudge_corr_en; + + LOG_FUNCTION_START(""); + + pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + smudge_corr_en = pdev->smudge_correct_config.smudge_corr_enabled; + SmudgeStatus = VL53L1_dynamic_xtalk_correction_disable(Dev); + + pdev->customer.algo__part_to_part_range_offset_mm = 0; + pdev->customer.mm_config__inner_offset_mm = 0; + pdev->customer.mm_config__outer_offset_mm = 0; + memset(&pdev->per_vcsel_cal_data, 0, sizeof(pdev->per_vcsel_cal_data)); + Repeat = BDTable[VL53L1_TUNING_SIMPLE_OFFSET_CALIBRATION_REPEAT]; + Max = BDTable[ + VL53L1_TUNING_MAX_SIMPLE_OFFSET_CALIBRATION_SAMPLE_NUMBER]; + UnderMax = 1 + (Max / 2); + OverMax = Max + (Max / 2); + sum_ranging = 0; + total_count = 0; + + while ((Repeat > 0) && (Status == VL53L1_ERROR_NONE)) { + Status = VL53L1_StartMeasurement(Dev); + + if (Status == VL53L1_ERROR_NONE) { + VL53L1_WaitMeasurementDataReady(Dev); + VL53L1_GetRangingMeasurementData(Dev, + &RangingMeasurementData); + VL53L1_ClearInterruptAndStartMeasurement(Dev); + } + + inloopcount = 0; + offset_meas = 0; + while ((Status == VL53L1_ERROR_NONE) && (inloopcount < Max) && + (offset_meas < OverMax)) { + Status = VL53L1_WaitMeasurementDataReady(Dev); + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_GetRangingMeasurementData(Dev, + &RangingMeasurementData); + goodmeas = (RangingMeasurementData.RangeStatus == + VL53L1_RANGESTATUS_RANGE_VALID); + if ((Status == VL53L1_ERROR_NONE) && goodmeas) { + sum_ranging = sum_ranging + + RangingMeasurementData.RangeMilliMeter; + inloopcount++; + } + Status = VL53L1_ClearInterruptAndStartMeasurement(Dev); + offset_meas++; + } + total_count += inloopcount; + + + if (inloopcount < UnderMax) + Status = VL53L1_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL; + + VL53L1_StopMeasurement(Dev); + + Repeat--; + + } + + if ((SmudgeStatus == VL53L1_ERROR_NONE) && (smudge_corr_en == 1)) + SmudgeStatus = VL53L1_dynamic_xtalk_correction_enable(Dev); + + if ((sum_ranging < 0) || + (sum_ranging > ((int32_t) total_count * 0xffff))) + Status = VL53L1_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH; + + if ((Status == VL53L1_ERROR_NONE) && (total_count > 0)) { + IncRounding = total_count / 2; + meanDistance_mm = (int16_t)((sum_ranging + IncRounding) + / total_count); + offset = (int16_t)CalDistanceMilliMeter - meanDistance_mm; + pdev->customer.algo__part_to_part_range_offset_mm = 0; + pdev->customer.mm_config__inner_offset_mm = offset; + pdev->customer.mm_config__outer_offset_mm = offset; + + Status = VL53L1_set_customer_nvm_managed(Dev, + &(pdev->customer)); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_PerformOffsetZeroDistanceCalibration(VL53L1_DEV Dev) +{ + #define START_OFFSET 50 + VL53L1_Error Status = VL53L1_ERROR_NONE; + int32_t sum_ranging; + uint8_t offset_meas; + int16_t Max, UnderMax, OverMax, Repeat; + int32_t total_count, inloopcount; + int32_t IncRounding; + int16_t meanDistance_mm; + int16_t offset, ZeroDistanceOffset; + VL53L1_RangingMeasurementData_t RangingMeasurementData; + VL53L1_LLDriverData_t *pdev; + uint8_t goodmeas; + VL53L1_Error SmudgeStatus = VL53L1_ERROR_NONE; + uint8_t smudge_corr_en; + + LOG_FUNCTION_START(""); + + pdev = VL53L1DevStructGetLLDriverHandle(Dev); + smudge_corr_en = pdev->smudge_correct_config.smudge_corr_enabled; + SmudgeStatus = VL53L1_dynamic_xtalk_correction_disable(Dev); + pdev->customer.algo__part_to_part_range_offset_mm = 0; + pdev->customer.mm_config__inner_offset_mm = START_OFFSET; + pdev->customer.mm_config__outer_offset_mm = START_OFFSET; + memset(&pdev->per_vcsel_cal_data, 0, sizeof(pdev->per_vcsel_cal_data)); + ZeroDistanceOffset = BDTable[ + VL53L1_TUNING_ZERO_DISTANCE_OFFSET_NON_LINEAR_FACTOR]; + Repeat = BDTable[VL53L1_TUNING_SIMPLE_OFFSET_CALIBRATION_REPEAT]; + Max = BDTable[ + VL53L1_TUNING_MAX_SIMPLE_OFFSET_CALIBRATION_SAMPLE_NUMBER]; + UnderMax = 1 + (Max / 2); + OverMax = Max + (Max / 2); + sum_ranging = 0; + total_count = 0; + + while ((Repeat > 0) && (Status == VL53L1_ERROR_NONE)) { + Status = VL53L1_StartMeasurement(Dev); + if (Status == VL53L1_ERROR_NONE) { + VL53L1_WaitMeasurementDataReady(Dev); + VL53L1_GetRangingMeasurementData(Dev, + &RangingMeasurementData); + VL53L1_ClearInterruptAndStartMeasurement(Dev); + } + inloopcount = 0; + offset_meas = 0; + while ((Status == VL53L1_ERROR_NONE) && (inloopcount < Max) && + (offset_meas < OverMax)) { + Status = VL53L1_WaitMeasurementDataReady(Dev); + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_GetRangingMeasurementData(Dev, + &RangingMeasurementData); + goodmeas = (RangingMeasurementData.RangeStatus == + VL53L1_RANGESTATUS_RANGE_VALID); + if ((Status == VL53L1_ERROR_NONE) && goodmeas) { + sum_ranging = sum_ranging + + RangingMeasurementData.RangeMilliMeter; + inloopcount++; + } + Status = VL53L1_ClearInterruptAndStartMeasurement(Dev); + offset_meas++; + } + total_count += inloopcount; + if (inloopcount < UnderMax) + Status = VL53L1_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL; + VL53L1_StopMeasurement(Dev); + Repeat--; + } + if ((SmudgeStatus == VL53L1_ERROR_NONE) && (smudge_corr_en == 1)) + SmudgeStatus = VL53L1_dynamic_xtalk_correction_enable(Dev); + if ((sum_ranging < 0) || + (sum_ranging > ((int32_t) total_count * 0xffff))) + Status = VL53L1_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH; + + if ((Status == VL53L1_ERROR_NONE) && (total_count > 0)) { + IncRounding = total_count / 2; + meanDistance_mm = (int16_t) + ((sum_ranging + IncRounding) / total_count); + offset = START_OFFSET - meanDistance_mm + ZeroDistanceOffset; + pdev->customer.algo__part_to_part_range_offset_mm = 0; + pdev->customer.mm_config__inner_offset_mm = offset; + pdev->customer.mm_config__outer_offset_mm = offset; + Status = VL53L1_set_customer_nvm_managed(Dev, + &(pdev->customer)); + } + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_SetCalibrationData(VL53L1_DEV Dev, + VL53L1_CalibrationData_t *pCalibrationData) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_CustomerNvmManaged_t *pC; + VL53L1_calibration_data_t cal_data; + uint32_t x, IncomeVersion, CurrentVersion; + uint8_t CalStopsOn_cal_peak_rate_map = 0; + VL53L1_xtalk_calibration_results_t xtalk; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + cal_data.struct_version = pCalibrationData->struct_version - + VL53L1_ADDITIONAL_CALIBRATION_DATA_STRUCT_VERSION; + + IncomeVersion = pCalibrationData->struct_version; + CurrentVersion = VL53L1_LL_CALIBRATION_DATA_STRUCT_VERSION + + VL53L1_ADDITIONAL_CALIBRATION_DATA_STRUCT_VERSION; + + if ((IncomeVersion < CurrentVersion) && + ((IncomeVersion & 0xFFFFFF0F) == + (CurrentVersion & 0xFFFFFF0F))) { + cal_data.struct_version = + VL53L1_LL_CALIBRATION_DATA_STRUCT_VERSION; + CalStopsOn_cal_peak_rate_map = 1; + + pdev->tuning_parms.tp_hist_merge = 0; + } + + + + memcpy( + &(cal_data.fmt_dmax_cal), + &(pCalibrationData->fmt_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + + memcpy( + &(cal_data.cust_dmax_cal), + &(pCalibrationData->cust_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + + memcpy( + &(cal_data.add_off_cal_data), + &(pCalibrationData->add_off_cal_data), + sizeof(VL53L1_additional_offset_cal_data_t)); + + + memcpy( + &(cal_data.optical_centre), + &(pCalibrationData->optical_centre), + sizeof(VL53L1_optical_centre_t)); + + + memcpy( + &(cal_data.xtalkhisto), + &(pCalibrationData->xtalkhisto), + sizeof(VL53L1_xtalk_histogram_data_t)); + + + memcpy( + &(cal_data.gain_cal), + &(pCalibrationData->gain_cal), + sizeof(VL53L1_gain_calibration_data_t)); + + + memcpy( + &(cal_data.cal_peak_rate_map), + &(pCalibrationData->cal_peak_rate_map), + sizeof(VL53L1_cal_peak_rate_map_t)); + + + if (!CalStopsOn_cal_peak_rate_map) + memcpy( + &(cal_data.per_vcsel_cal_data), + &(pCalibrationData->per_vcsel_cal_data), + sizeof(VL53L1_per_vcsel_period_offset_cal_data_t)); + else { + cal_data.per_vcsel_cal_data.short_a_offset_mm = + cal_data.per_vcsel_cal_data.short_b_offset_mm = + cal_data.per_vcsel_cal_data.medium_a_offset_mm = + cal_data.per_vcsel_cal_data.medium_b_offset_mm = + cal_data.per_vcsel_cal_data.long_a_offset_mm = + cal_data.per_vcsel_cal_data.long_b_offset_mm = 0; + } + + pC = &pCalibrationData->customer; + x = pC->algo__crosstalk_compensation_plane_offset_kcps; + cal_data.customer.algo__crosstalk_compensation_plane_offset_kcps = + (uint16_t)(x&0x0000FFFF); + + cal_data.customer.global_config__spad_enables_ref_0 = + pC->global_config__spad_enables_ref_0; + cal_data.customer.global_config__spad_enables_ref_1 = + pC->global_config__spad_enables_ref_1; + cal_data.customer.global_config__spad_enables_ref_2 = + pC->global_config__spad_enables_ref_2; + cal_data.customer.global_config__spad_enables_ref_3 = + pC->global_config__spad_enables_ref_3; + cal_data.customer.global_config__spad_enables_ref_4 = + pC->global_config__spad_enables_ref_4; + cal_data.customer.global_config__spad_enables_ref_5 = + pC->global_config__spad_enables_ref_5; + cal_data.customer.global_config__ref_en_start_select = + pC->global_config__ref_en_start_select; + cal_data.customer.ref_spad_man__num_requested_ref_spads = + pC->ref_spad_man__num_requested_ref_spads; + cal_data.customer.ref_spad_man__ref_location = + pC->ref_spad_man__ref_location; + cal_data.customer.algo__crosstalk_compensation_x_plane_gradient_kcps = + pC->algo__crosstalk_compensation_x_plane_gradient_kcps; + cal_data.customer.algo__crosstalk_compensation_y_plane_gradient_kcps = + pC->algo__crosstalk_compensation_y_plane_gradient_kcps; + cal_data.customer.ref_spad_char__total_rate_target_mcps = + pC->ref_spad_char__total_rate_target_mcps; + cal_data.customer.algo__part_to_part_range_offset_mm = + pC->algo__part_to_part_range_offset_mm; + cal_data.customer.mm_config__inner_offset_mm = + pC->mm_config__inner_offset_mm; + cal_data.customer.mm_config__outer_offset_mm = + pC->mm_config__outer_offset_mm; + + Status = VL53L1_set_part_to_part_data(Dev, &cal_data); + if (Status != VL53L1_ERROR_NONE) + goto ENDFUNC; + + Status = VL53L1_get_current_xtalk_settings(Dev, &xtalk); + + if (Status != VL53L1_ERROR_NONE) + goto ENDFUNC; + + xtalk.algo__crosstalk_compensation_plane_offset_kcps = x; + + Status = VL53L1_set_tuning_parm(Dev, + VL53L1_TUNINGPARM_DYNXTALK_NODETECT_XTALK_OFFSET_KCPS, + x); + + + if (!CalStopsOn_cal_peak_rate_map) + memcpy( + &(xtalk.algo__xtalk_cpo_HistoMerge_kcps[0]), + &(pCalibrationData->algo__xtalk_cpo_HistoMerge_kcps[0]), + sizeof(pCalibrationData->algo__xtalk_cpo_HistoMerge_kcps)); + else + memset( + &(xtalk.algo__xtalk_cpo_HistoMerge_kcps[0]), 0, + sizeof(pCalibrationData->algo__xtalk_cpo_HistoMerge_kcps)); + + Status = VL53L1_set_current_xtalk_settings(Dev, &xtalk); + +ENDFUNC: + LOG_FUNCTION_END(Status); + return Status; + +} + +VL53L1_Error VL53L1_GetCalibrationData(VL53L1_DEV Dev, + VL53L1_CalibrationData_t *pCalibrationData) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_calibration_data_t cal_data; + VL53L1_CustomerNvmManaged_t *pC; + VL53L1_customer_nvm_managed_t *pC2; + VL53L1_xtalk_calibration_results_t xtalk; + uint32_t tmp; + VL53L1_PresetModes PresetMode; + + LOG_FUNCTION_START(""); + + + Status = VL53L1_get_part_to_part_data(Dev, &cal_data); + + pCalibrationData->struct_version = cal_data.struct_version + + VL53L1_ADDITIONAL_CALIBRATION_DATA_STRUCT_VERSION; + + + memcpy( + &(pCalibrationData->fmt_dmax_cal), + &(cal_data.fmt_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + + memcpy( + &(pCalibrationData->cust_dmax_cal), + &(cal_data.cust_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + + memcpy( + &(pCalibrationData->add_off_cal_data), + &(cal_data.add_off_cal_data), + sizeof(VL53L1_additional_offset_cal_data_t)); + + + memcpy( + &(pCalibrationData->optical_centre), + &(cal_data.optical_centre), + sizeof(VL53L1_optical_centre_t)); + + + memcpy( + &(pCalibrationData->xtalkhisto), + &(cal_data.xtalkhisto), + sizeof(VL53L1_xtalk_histogram_data_t)); + + memcpy( + &(pCalibrationData->gain_cal), + &(cal_data.gain_cal), + sizeof(VL53L1_gain_calibration_data_t)); + + + memcpy( + &(pCalibrationData->cal_peak_rate_map), + &(cal_data.cal_peak_rate_map), + sizeof(VL53L1_cal_peak_rate_map_t)); + + + memcpy( + &(pCalibrationData->per_vcsel_cal_data), + &(cal_data.per_vcsel_cal_data), + sizeof(VL53L1_per_vcsel_period_offset_cal_data_t)); + + pC = &pCalibrationData->customer; + pC2 = &cal_data.customer; + pC->global_config__spad_enables_ref_0 = + pC2->global_config__spad_enables_ref_0; + pC->global_config__spad_enables_ref_1 = + pC2->global_config__spad_enables_ref_1; + pC->global_config__spad_enables_ref_2 = + pC2->global_config__spad_enables_ref_2; + pC->global_config__spad_enables_ref_3 = + pC2->global_config__spad_enables_ref_3; + pC->global_config__spad_enables_ref_4 = + pC2->global_config__spad_enables_ref_4; + pC->global_config__spad_enables_ref_5 = + pC2->global_config__spad_enables_ref_5; + pC->global_config__ref_en_start_select = + pC2->global_config__ref_en_start_select; + pC->ref_spad_man__num_requested_ref_spads = + pC2->ref_spad_man__num_requested_ref_spads; + pC->ref_spad_man__ref_location = + pC2->ref_spad_man__ref_location; + pC->algo__crosstalk_compensation_x_plane_gradient_kcps = + pC2->algo__crosstalk_compensation_x_plane_gradient_kcps; + pC->algo__crosstalk_compensation_y_plane_gradient_kcps = + pC2->algo__crosstalk_compensation_y_plane_gradient_kcps; + pC->ref_spad_char__total_rate_target_mcps = + pC2->ref_spad_char__total_rate_target_mcps; + pC->algo__part_to_part_range_offset_mm = + pC2->algo__part_to_part_range_offset_mm; + pC->mm_config__inner_offset_mm = + pC2->mm_config__inner_offset_mm; + pC->mm_config__outer_offset_mm = + pC2->mm_config__outer_offset_mm; + + pC->algo__crosstalk_compensation_plane_offset_kcps = + (uint32_t)( + pC2->algo__crosstalk_compensation_plane_offset_kcps); + + PresetMode = VL53L1DevDataGet(Dev, CurrentParameters.PresetMode); + + if ((PresetMode == VL53L1_PRESETMODE_RANGING) || + (PresetMode == VL53L1_PRESETMODE_MULTIZONES_SCANNING) || + (PresetMode == VL53L1_PRESETMODE_PROXY_RANGING_MODE) + ) { + + Status = VL53L1_get_current_xtalk_settings(Dev, &xtalk); + + if (Status != VL53L1_ERROR_NONE) + goto ENDFUNC; + + tmp = xtalk.algo__crosstalk_compensation_plane_offset_kcps; + pC->algo__crosstalk_compensation_plane_offset_kcps = tmp; + tmp = xtalk.algo__crosstalk_compensation_x_plane_gradient_kcps; + pC->algo__crosstalk_compensation_x_plane_gradient_kcps = tmp; + tmp = xtalk.algo__crosstalk_compensation_y_plane_gradient_kcps; + pC->algo__crosstalk_compensation_y_plane_gradient_kcps = tmp; + + memcpy(&(pCalibrationData->algo__xtalk_cpo_HistoMerge_kcps[0]), + &(xtalk.algo__xtalk_cpo_HistoMerge_kcps[0]), + sizeof(pCalibrationData->algo__xtalk_cpo_HistoMerge_kcps)); + } +ENDFUNC: + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_SetZoneCalibrationData(VL53L1_DEV Dev, + VL53L1_ZoneCalibrationData_t *pZoneCalibrationData) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_set_zone_calibration_data(Dev, pZoneCalibrationData); + + LOG_FUNCTION_END(Status); + return Status; + +} + +VL53L1_Error VL53L1_GetZoneCalibrationData(VL53L1_DEV Dev, + VL53L1_ZoneCalibrationData_t *pZoneCalibrationData) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_zone_calibration_data(Dev, pZoneCalibrationData); + + LOG_FUNCTION_END(Status); + return Status; +} + +VL53L1_Error VL53L1_GetOpticalCenter(VL53L1_DEV Dev, + FixPoint1616_t *pOpticalCenterX, + FixPoint1616_t *pOpticalCenterY) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_calibration_data_t CalibrationData; + + LOG_FUNCTION_START(""); + + *pOpticalCenterX = 0; + *pOpticalCenterY = 0; + Status = VL53L1_get_part_to_part_data(Dev, &CalibrationData); + if (Status == VL53L1_ERROR_NONE) { + *pOpticalCenterX = VL53L1_FIXPOINT44TOFIXPOINT1616( + CalibrationData.optical_centre.x_centre); + *pOpticalCenterY = VL53L1_FIXPOINT44TOFIXPOINT1616( + CalibrationData.optical_centre.y_centre); + } + + LOG_FUNCTION_END(Status); + return Status; +} + + + + + + +VL53L1_Error VL53L1_SetThresholdConfig(VL53L1_DEV Dev, + VL53L1_DetectionConfig_t *pConfig) +{ +#define BADTHRESBOUNDS(T) \ + (((T.CrossMode == VL53L1_THRESHOLD_OUT_OF_WINDOW) || \ + (T.CrossMode == VL53L1_THRESHOLD_IN_WINDOW)) && (T.Low > T.High)) + + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_GPIO_interrupt_config_t Cfg; + uint16_t g; + FixPoint1616_t gain, high1616, low1616; + VL53L1_LLDriverData_t *pdev; + + LOG_FUNCTION_START(""); + + pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + Status = VL53L1_get_GPIO_interrupt_config(Dev, &Cfg); + if (Status != VL53L1_ERROR_NONE) + return Status; + + if (pConfig->DetectionMode == VL53L1_DETECTION_NORMAL_RUN) { + Cfg.intr_new_measure_ready = 1; + Status = VL53L1_set_GPIO_interrupt_config_struct(Dev, + Cfg); + } else { + if (BADTHRESBOUNDS(pConfig->Distance)) + Status = VL53L1_ERROR_INVALID_PARAMS; + if ((Status == VL53L1_ERROR_NONE) && + (BADTHRESBOUNDS(pConfig->Rate))) + Status = VL53L1_ERROR_INVALID_PARAMS; + if (Status == VL53L1_ERROR_NONE) { + Cfg.intr_new_measure_ready = 0; + Cfg.intr_no_target = pConfig->IntrNoTarget; + + g = pdev->gain_cal.standard_ranging_gain_factor; + if (g != 0) { + + gain = (FixPoint1616_t) ((uint32_t)g << 5); + high1616 = (FixPoint1616_t) ((uint32_t) + pConfig->Distance.High << 16); + low1616 = (FixPoint1616_t) ((uint32_t) + pConfig->Distance.Low << 16); + + high1616 = (high1616 + 32768) / gain; + low1616 = (low1616 + 32768) / gain; + Cfg.threshold_distance_high = (uint16_t) + (high1616 & 0xFFFF); + Cfg.threshold_distance_low = (uint16_t) + (low1616 & 0xFFFF); + } + Cfg.threshold_rate_high = + VL53L1_FIXPOINT1616TOFIXPOINT97( + pConfig->Rate.High); + Cfg.threshold_rate_low = + VL53L1_FIXPOINT1616TOFIXPOINT97( + pConfig->Rate.Low); + + Cfg.intr_mode_distance = ConvertModeToLLD( + &Status, + pConfig->Distance.CrossMode); + if (Status == VL53L1_ERROR_NONE) + Cfg.intr_mode_rate = ConvertModeToLLD( + &Status, + pConfig->Rate.CrossMode); + } + + + if (Status == VL53L1_ERROR_NONE) { + Cfg.intr_combined_mode = 1; + switch (pConfig->DetectionMode) { + case VL53L1_DETECTION_DISTANCE_ONLY: + Cfg.threshold_rate_high = 0; + Cfg.threshold_rate_low = 0; + break; + case VL53L1_DETECTION_RATE_ONLY: + Cfg.threshold_distance_high = 0; + Cfg.threshold_distance_low = 0; + break; + case VL53L1_DETECTION_DISTANCE_OR_RATE: + + break; + case VL53L1_DETECTION_DISTANCE_AND_RATE: + Cfg.intr_combined_mode = 0; + break; + default: + Status = VL53L1_ERROR_INVALID_PARAMS; + } + } + + if (Status == VL53L1_ERROR_NONE) + Status = + VL53L1_set_GPIO_interrupt_config_struct(Dev, Cfg); + + } + + LOG_FUNCTION_END(Status); + return Status; +} + + +VL53L1_Error VL53L1_GetThresholdConfig(VL53L1_DEV Dev, + VL53L1_DetectionConfig_t *pConfig) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + VL53L1_GPIO_interrupt_config_t Cfg; + + LOG_FUNCTION_START(""); + + Status = VL53L1_get_GPIO_interrupt_config(Dev, &Cfg); + + if (Status != VL53L1_ERROR_NONE) { + LOG_FUNCTION_END(Status); + return Status; + } + + pConfig->IntrNoTarget = Cfg.intr_no_target; + pConfig->Distance.High = Cfg.threshold_distance_high; + pConfig->Distance.Low = Cfg.threshold_distance_low; + pConfig->Rate.High = + VL53L1_FIXPOINT97TOFIXPOINT1616( + Cfg.threshold_rate_high); + pConfig->Rate.Low = + VL53L1_FIXPOINT97TOFIXPOINT1616(Cfg.threshold_rate_low); + pConfig->Distance.CrossMode = + ConvertModeFromLLD(&Status, Cfg.intr_mode_distance); + if (Status == VL53L1_ERROR_NONE) + pConfig->Rate.CrossMode = + ConvertModeFromLLD(&Status, Cfg.intr_mode_rate); + + if (Cfg.intr_new_measure_ready == 1) { + pConfig->DetectionMode = VL53L1_DETECTION_NORMAL_RUN; + } else { + + if (Status == VL53L1_ERROR_NONE) { + if (Cfg.intr_combined_mode == 0) + pConfig->DetectionMode = + VL53L1_DETECTION_DISTANCE_AND_RATE; + else { + if ((Cfg.threshold_distance_high == 0) && + (Cfg.threshold_distance_low == 0)) + pConfig->DetectionMode = + VL53L1_DETECTION_RATE_ONLY; + else if ((Cfg.threshold_rate_high == 0) && + (Cfg.threshold_rate_low == 0)) + pConfig->DetectionMode = + VL53L1_DETECTION_DISTANCE_ONLY; + else + pConfig->DetectionMode = + VL53L1_DETECTION_DISTANCE_OR_RATE; + } + } + } + + LOG_FUNCTION_END(Status); + return Status; +} + + + + +VL53L1_Error VL53L1_PerformOffsetPerVcselCalibration(VL53L1_DEV Dev, + int32_t CalDistanceMilliMeter) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + int32_t sum_ranging_range_A, sum_ranging_range_B; + uint8_t offset_meas_range_A, offset_meas_range_B; + int16_t Max, UnderMax, OverMax, Repeat; + int32_t inloopcount; + int32_t IncRounding; + int16_t meanDistance_mm; + VL53L1_RangingMeasurementData_t RangingMeasurementData; + VL53L1_LLDriverData_t *pdev; + uint8_t goodmeas; + VL53L1_PresetModes currentMode; + VL53L1_DistanceModes currentDist; + VL53L1_DistanceModes DistMode[3] = {VL53L1_DISTANCEMODE_SHORT, + VL53L1_DISTANCEMODE_MEDIUM, VL53L1_DISTANCEMODE_LONG}; + int16_t offsetA[3]; + int16_t offsetB[3]; + + VL53L1_Error SmudgeStatus = VL53L1_ERROR_NONE; + uint8_t smudge_corr_en, isc; + + LOG_FUNCTION_START(""); + + pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + smudge_corr_en = pdev->smudge_correct_config.smudge_corr_enabled; + SmudgeStatus = VL53L1_dynamic_xtalk_correction_disable(Dev); + + pdev->customer.algo__part_to_part_range_offset_mm = 0; + pdev->customer.mm_config__inner_offset_mm = 0; + pdev->customer.mm_config__outer_offset_mm = 0; + pdev->customer.mm_config__outer_offset_mm = 0; + memset(&pdev->per_vcsel_cal_data, 0, sizeof(pdev->per_vcsel_cal_data)); + + Repeat = 0; + Max = 2 * BDTable[ + VL53L1_TUNING_MAX_SIMPLE_OFFSET_CALIBRATION_SAMPLE_NUMBER]; + UnderMax = 1 + (Max / 2); + OverMax = Max + (Max / 2); + + Status = VL53L1_GetPresetMode(Dev, ¤tMode); + Status = VL53L1_GetDistanceMode(Dev, ¤tDist); + + while ((Repeat < 3) && (Status == VL53L1_ERROR_NONE)) { + Status = VL53L1_SetDistanceMode(Dev, DistMode[Repeat]); + Status = VL53L1_StartMeasurement(Dev); + + if (Status == VL53L1_ERROR_NONE) { + VL53L1_WaitMeasurementDataReady(Dev); + VL53L1_GetRangingMeasurementData(Dev, + &RangingMeasurementData); + VL53L1_ClearInterruptAndStartMeasurement(Dev); + } + + inloopcount = 0; + offset_meas_range_A = 0; + sum_ranging_range_A = 0; + offset_meas_range_B = 0; + sum_ranging_range_B = 0; + while ((Status == VL53L1_ERROR_NONE) && (inloopcount < Max) && + (inloopcount < OverMax)) { + Status = VL53L1_WaitMeasurementDataReady(Dev); + if (Status == VL53L1_ERROR_NONE) + Status = VL53L1_GetRangingMeasurementData(Dev, + &RangingMeasurementData); + goodmeas = (RangingMeasurementData.RangeStatus == + VL53L1_RANGESTATUS_RANGE_VALID); + isc = pdev->ll_state.cfg_internal_stream_count; + if ((Status == VL53L1_ERROR_NONE) && goodmeas) { + if (isc & 0x01) { + sum_ranging_range_A += + RangingMeasurementData.RangeMilliMeter; + offset_meas_range_A++; + } else { + sum_ranging_range_B += + RangingMeasurementData.RangeMilliMeter; + offset_meas_range_B++; + } + inloopcount = offset_meas_range_A + + offset_meas_range_B; + } + Status = VL53L1_ClearInterruptAndStartMeasurement(Dev); + } + + + if (inloopcount < UnderMax) + Status = VL53L1_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL; + + VL53L1_StopMeasurement(Dev); + + + if ((sum_ranging_range_A < 0) || + (sum_ranging_range_B < 0) || + (sum_ranging_range_A > + ((int32_t) offset_meas_range_A * 0xffff)) || + (sum_ranging_range_B > + ((int32_t) offset_meas_range_B * 0xffff))) { + Status = VL53L1_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH; + } + + if ((Status == VL53L1_ERROR_NONE) && + (offset_meas_range_A > 0)) { + IncRounding = offset_meas_range_A / 2; + meanDistance_mm = (int16_t) + ((sum_ranging_range_A + IncRounding) + / offset_meas_range_A); + offsetA[Repeat] = (int16_t) + CalDistanceMilliMeter - meanDistance_mm; + } + + if ((Status == VL53L1_ERROR_NONE) && + (offset_meas_range_B > 0)) { + IncRounding = offset_meas_range_B / 2; + meanDistance_mm = (int16_t) + ((sum_ranging_range_B + IncRounding) + / offset_meas_range_B); + offsetB[Repeat] = (int16_t) + CalDistanceMilliMeter - meanDistance_mm; + } + Repeat++; + } + + if ((SmudgeStatus == VL53L1_ERROR_NONE) && (smudge_corr_en == 1)) + SmudgeStatus = VL53L1_dynamic_xtalk_correction_enable(Dev); + + if (Status == VL53L1_ERROR_NONE) { + pdev->per_vcsel_cal_data.short_a_offset_mm = offsetA[0]; + pdev->per_vcsel_cal_data.short_b_offset_mm = offsetB[0]; + pdev->per_vcsel_cal_data.medium_a_offset_mm = offsetA[1]; + pdev->per_vcsel_cal_data.medium_b_offset_mm = offsetB[1]; + pdev->per_vcsel_cal_data.long_a_offset_mm = offsetA[2]; + pdev->per_vcsel_cal_data.long_b_offset_mm = offsetB[2]; + } + + VL53L1_SetPresetMode(Dev, currentMode); + VL53L1_SetDistanceMode(Dev, currentDist); + + LOG_FUNCTION_END(Status); + return Status; +} + + diff --git a/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_api_calibration.c b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_api_calibration.c new file mode 100644 index 000000000000..a7527f4ad295 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_api_calibration.c @@ -0,0 +1,2176 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + + +#include "vl53l1_ll_def.h" +#include "vl53l1_ll_device.h" +#include "vl53l1_platform.h" +#include "vl53l1_platform_ipp.h" +#include "vl53l1_register_map.h" +#include "vl53l1_register_funcs.h" +#include "vl53l1_register_settings.h" +#include "vl53l1_hist_map.h" +#include "vl53l1_hist_structs.h" +#include "vl53l1_core.h" +#include "vl53l1_wait.h" +#include "vl53l1_api_preset_modes.h" +#include "vl53l1_silicon_core.h" +#include "vl53l1_api_core.h" +#include "vl53l1_api_calibration.h" + +#ifdef VL53L1_LOG_ENABLE + #include "vl53l1_api_debug.h" +#endif + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_CORE, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_CORE, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_CORE, status, \ + fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_CORE, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_run_ref_spad_char( + VL53L1_DEV Dev, + VL53L1_Error *pcal_status) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t comms_buffer[6]; + + VL53L1_refspadchar_config_t *prefspadchar = &(pdev->refspadchar); + + LOG_FUNCTION_START(""); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_powerforce(Dev); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_ref_spad_char_config( + Dev, + prefspadchar->VL53L1_p_009, + prefspadchar->timeout_us, + prefspadchar->target_count_rate_mcps, + prefspadchar->max_count_rate_limit_mcps, + prefspadchar->min_count_rate_limit_mcps, + pdev->stat_nvm.osc_measured__fast_osc__frequency); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_run_device_test( + Dev, + prefspadchar->device_test_mode); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_ReadMulti( + Dev, + VL53L1_REF_SPAD_CHAR_RESULT__NUM_ACTUAL_REF_SPADS, + comms_buffer, + 2); + + if (status == VL53L1_ERROR_NONE) { + pdev->dbg_results.ref_spad_char_result__num_actual_ref_spads = + comms_buffer[0]; + pdev->dbg_results.ref_spad_char_result__ref_location = + comms_buffer[1]; + } + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WriteMulti( + Dev, + VL53L1_REF_SPAD_MAN__NUM_REQUESTED_REF_SPADS, + comms_buffer, + 2); + + if (status == VL53L1_ERROR_NONE) { + pdev->customer.ref_spad_man__num_requested_ref_spads = + comms_buffer[0]; + pdev->customer.ref_spad_man__ref_location = + comms_buffer[1]; + } + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_ReadMulti( + Dev, + VL53L1_RESULT__SPARE_0_SD1, + comms_buffer, + 6); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WriteMulti( + Dev, + VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_0, + comms_buffer, + 6); + + if (status == VL53L1_ERROR_NONE) { + pdev->customer.global_config__spad_enables_ref_0 = + comms_buffer[0]; + pdev->customer.global_config__spad_enables_ref_1 = + comms_buffer[1]; + pdev->customer.global_config__spad_enables_ref_2 = + comms_buffer[2]; + pdev->customer.global_config__spad_enables_ref_3 = + comms_buffer[3]; + pdev->customer.global_config__spad_enables_ref_4 = + comms_buffer[4]; + pdev->customer.global_config__spad_enables_ref_5 = + comms_buffer[5]; + } + +#ifdef VL53L1_LOG_ENABLE + + if (status == VL53L1_ERROR_NONE) + VL53L1_print_customer_nvm_managed( + &(pdev->customer), + "run_ref_spad_char():pdev->lldata.customer.", + VL53L1_TRACE_MODULE_REF_SPAD_CHAR); +#endif + + if (status == VL53L1_ERROR_NONE) { + + switch (pdev->sys_results.result__range_status) { + + case VL53L1_DEVICEERROR_REFSPADCHARNOTENOUGHDPADS: + status = VL53L1_WARNING_REF_SPAD_CHAR_NOT_ENOUGH_SPADS; + break; + + case VL53L1_DEVICEERROR_REFSPADCHARMORETHANTARGET: + status = VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_HIGH; + break; + + case VL53L1_DEVICEERROR_REFSPADCHARLESSTHANTARGET: + status = VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_LOW; + break; + } + } + + + + *pcal_status = status; + + + + IGNORE_STATUS( + IGNORE_REF_SPAD_CHAR_NOT_ENOUGH_SPADS, + VL53L1_WARNING_REF_SPAD_CHAR_NOT_ENOUGH_SPADS, + status); + + IGNORE_STATUS( + IGNORE_REF_SPAD_CHAR_RATE_TOO_HIGH, + VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_HIGH, + status); + + IGNORE_STATUS( + IGNORE_REF_SPAD_CHAR_RATE_TOO_LOW, + VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_LOW, + status); + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_run_xtalk_extraction( + VL53L1_DEV Dev, + VL53L1_Error *pcal_status) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + + + VL53L1_xtalkextract_config_t *pX = &(pdev->xtalk_extract_cfg); + VL53L1_xtalk_config_t *pC = &(pdev->xtalk_cfg); + VL53L1_xtalk_calibration_results_t *pXC = &(pdev->xtalk_cal); + + uint8_t results_invalid = 0; + + uint8_t i = 0; + uint16_t tmp16 = 0; + + uint8_t measurement_mode = VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + + LOG_FUNCTION_START(""); + + + + + + VL53L1_init_histogram_bin_data_struct( + 0, + (uint16_t)VL53L1_HISTOGRAM_BUFFER_SIZE, + &(pdev->xtalk_results.central_histogram_avg)); + + VL53L1_init_histogram_bin_data_struct( + 0, + (uint16_t)VL53L1_HISTOGRAM_BUFFER_SIZE, + &(pdev->xtalk_results.central_histogram_sum)); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_preset_mode( + Dev, + VL53L1_DEVICEPRESETMODE_HISTOGRAM_XTALK_PLANAR, + + pX->dss_config__target_total_rate_mcps, + pX->phasecal_config_timeout_us, + pX->mm_config_timeout_us, + pX->range_config_timeout_us, + + 100); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_xtalk_compensation(Dev); + + + + pdev->xtalk_results.max_results = VL53L1_MAX_XTALK_RANGE_RESULTS; + pdev->xtalk_results.active_results = pdev->zone_cfg.active_zones+1; + + + + pdev->xtalk_results.central_histogram__window_start = 0xFF; + pdev->xtalk_results.central_histogram__window_end = 0x00; + + pdev->xtalk_results.num_of_samples_status = 0x00; + pdev->xtalk_results.zero_samples_status = 0x00; + pdev->xtalk_results.max_sigma_status = 0x00; + + for (i = 0; i < pdev->xtalk_results.max_results; i++) { + pdev->xtalk_results.VL53L1_p_002[i].no_of_samples = 0; + pdev->xtalk_results.VL53L1_p_002[i].signal_total_events_avg = 0; + pdev->xtalk_results.VL53L1_p_002[i].signal_total_events_sum = 0; + pdev->xtalk_results.VL53L1_p_002[i].rate_per_spad_kcps_sum = 0; + pdev->xtalk_results.VL53L1_p_002[i].rate_per_spad_kcps_avg = 0; + pdev->xtalk_results.VL53L1_p_002[i].sigma_mm_sum = 0; + pdev->xtalk_results.VL53L1_p_002[i].sigma_mm_avg = 0; + + pdev->xtalk_results.VL53L1_p_002[i].median_phase_sum = 0; + pdev->xtalk_results.VL53L1_p_002[i].median_phase_avg = 0; + + } + + + if (status == VL53L1_ERROR_NONE) { + + status = + VL53L1_get_and_avg_xtalk_samples( + Dev, + + pX->num_of_samples, + + measurement_mode, + + pX->algo__crosstalk_extract_max_valid_range_mm, + pX->algo__crosstalk_extract_min_valid_range_mm, + pX->algo__crosstalk_extract_max_valid_rate_kcps, + + 0x0, + 0x4, + &(pdev->xtalk_results), + &(pdev->xtalk_results.central_histogram_sum), + &(pdev->xtalk_results.central_histogram_avg)); + } + + + + + + + + if (status == VL53L1_ERROR_NONE) + if ((pdev->xtalk_results.VL53L1_p_002[4].no_of_samples == 0) || + (pdev->xtalk_results.VL53L1_p_002[4].sigma_mm_avg > + ((uint32_t)pX->algo__crosstalk_extract_max_sigma_mm + << 5))) + results_invalid = 0x01; + + + +#ifdef VL53L1_LOG_ENABLE + if (status == VL53L1_ERROR_NONE) + VL53L1_print_xtalk_range_results( + &(pdev->xtalk_results), + "pdev->xtalk_results", + VL53L1_TRACE_MODULE_CORE); +#endif + + if ((status == VL53L1_ERROR_NONE) && (results_invalid == 0)) { + + status = + VL53L1_ipp_xtalk_calibration_process_data( + Dev, + &(pdev->xtalk_results), + &(pdev->xtalk_shapes), + &(pdev->xtalk_cal)); + + } + if ((status == VL53L1_ERROR_NONE) && (results_invalid == 0)) { + for (i = 0; i < VL53L1_BIN_REC_SIZE; i++) + pXC->algo__xtalk_cpo_HistoMerge_kcps[i] = + pXC->algo__crosstalk_compensation_plane_offset_kcps; + pC->algo__crosstalk_compensation_x_plane_gradient_kcps = + pXC->algo__crosstalk_compensation_x_plane_gradient_kcps; + pC->algo__crosstalk_compensation_y_plane_gradient_kcps = + pXC->algo__crosstalk_compensation_y_plane_gradient_kcps; + pC->algo__crosstalk_compensation_plane_offset_kcps = + pXC->algo__crosstalk_compensation_plane_offset_kcps; + } + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_xtalk_compensation(Dev); + + + + + if (status == VL53L1_ERROR_NONE) { + + for (i = 0; i < pdev->xtalk_results.max_results; i++) { + + if (pdev->xtalk_results.VL53L1_p_002[i].no_of_samples != + + pX->num_of_samples) { + + pdev->xtalk_results.num_of_samples_status = + pdev->xtalk_results.num_of_samples_status | + (1 << i); + } + + if (pdev->xtalk_results.VL53L1_p_002[i].no_of_samples == + 0x00) { + pdev->xtalk_results.zero_samples_status = + pdev->xtalk_results.zero_samples_status | + (1 << i); + } + + + + + tmp16 = pX->algo__crosstalk_extract_max_sigma_mm; + if (pdev->xtalk_results.VL53L1_p_002[i].sigma_mm_avg > + ((uint32_t)tmp16 << 5)) { + pdev->xtalk_results.max_sigma_status = + pdev->xtalk_results.max_sigma_status | + (1 << i); + } + + } + } + + + if (results_invalid > 0) { + + if (pdev->xtalk_results.VL53L1_p_002[4].no_of_samples == 0) { + status = VL53L1_ERROR_XTALK_EXTRACTION_NO_SAMPLE_FAIL; + } else { + + + if (pdev->xtalk_results.VL53L1_p_002[4].sigma_mm_avg > + (((uint32_t)pX->algo__crosstalk_extract_max_sigma_mm) + << 5)) { + status = + VL53L1_ERROR_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL; + } + + } + } else { + + if (pdev->xtalk_results.zero_samples_status != 0x00) { + status = VL53L1_WARNING_XTALK_NO_SAMPLES_FOR_GRADIENT; + } else { + if (pdev->xtalk_results.max_sigma_status != 0x00) { + status = + VL53L1_WARNING_XTALK_SIGMA_LIMIT_FOR_GRADIENT; + } else { + if (pdev->xtalk_results.num_of_samples_status != + 0x00) + status = + VL53L1_WARNING_XTALK_MISSING_SAMPLES; + } + } + } + + + + pdev->xtalk_results.cal_status = status; + *pcal_status = pdev->xtalk_results.cal_status; + + + + IGNORE_STATUS( + IGNORE_XTALK_EXTRACTION_NO_SAMPLE_FAIL, + VL53L1_ERROR_XTALK_EXTRACTION_NO_SAMPLE_FAIL, + status); + + IGNORE_STATUS( + IGNORE_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL, + VL53L1_ERROR_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL, + status); + + IGNORE_STATUS( + IGNORE_XTALK_EXTRACTION_NO_SAMPLE_FOR_GRADIENT_WARN, + VL53L1_WARNING_XTALK_NO_SAMPLES_FOR_GRADIENT, + status); + + IGNORE_STATUS( + IGNORE_XTALK_EXTRACTION_SIGMA_LIMIT_FOR_GRADIENT_WARN, + VL53L1_WARNING_XTALK_SIGMA_LIMIT_FOR_GRADIENT, + status); + + IGNORE_STATUS( + IGNORE_XTALK_EXTRACTION_MISSING_SAMPLES_WARN, + VL53L1_WARNING_XTALK_MISSING_SAMPLES, + status); + +#ifdef VL53L1_LOG_ENABLE + + + + VL53L1_print_customer_nvm_managed( + &(pdev->customer), + "run_xtalk_extraction():pdev->lldata.customer.", + VL53L1_TRACE_MODULE_XTALK_DATA); + + VL53L1_print_xtalk_config( + &(pdev->xtalk_cfg), + "run_xtalk_extraction():pdev->lldata.xtalk_cfg.", + VL53L1_TRACE_MODULE_XTALK_DATA); + + VL53L1_print_xtalk_extract_config( + &(pdev->xtalk_extract_cfg), + "run_xtalk_extraction():pdev->lldata.xtalk_extract_cfg.", + VL53L1_TRACE_MODULE_XTALK_DATA); + + VL53L1_print_histogram_bin_data( + &(pdev->hist_data), + "run_xtalk_extraction():pdev->lldata.hist_data.", + VL53L1_TRACE_MODULE_XTALK_DATA); + + VL53L1_print_xtalk_histogram_data( + &(pdev->xtalk_shapes), + "pdev->lldata.xtalk_shapes.", + VL53L1_TRACE_MODULE_XTALK_DATA); + + VL53L1_print_xtalk_range_results( + &(pdev->xtalk_results), + "run_xtalk_extraction():pdev->lldata.xtalk_results.", + VL53L1_TRACE_MODULE_XTALK_DATA); + +#endif + + LOG_FUNCTION_END(status); + + return status; + +} + + + +VL53L1_Error VL53L1_get_and_avg_xtalk_samples( + VL53L1_DEV Dev, + uint8_t num_of_samples, + uint8_t measurement_mode, + int16_t xtalk_filter_thresh_max_mm, + int16_t xtalk_filter_thresh_min_mm, + uint16_t xtalk_max_valid_rate_kcps, + uint8_t xtalk_result_id, + uint8_t xtalk_histo_id, + VL53L1_xtalk_range_results_t *pXR, + VL53L1_histogram_bin_data_t *psum_histo, + VL53L1_histogram_bin_data_t *pavg_histo) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + +#ifdef VL53L1_LOG_ENABLE + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); +#endif + + VL53L1_range_results_t *prs = + (VL53L1_range_results_t *) pdev->wArea1; + + VL53L1_range_data_t *prange_data; + VL53L1_xtalk_range_data_t *pxtalk_range_data; + + uint8_t i = 0; + uint8_t j = 0; + uint8_t zone_id = 0; + uint8_t final_zone = pdev->zone_cfg.active_zones+1; + uint8_t valid_result; + + uint8_t smudge_corr_en = 0; + + + + + smudge_corr_en = pdev->smudge_correct_config.smudge_corr_enabled; + + status = VL53L1_dynamic_xtalk_correction_disable(Dev); + + + VL53L1_load_patch(Dev); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_init_and_start_range( + Dev, + measurement_mode, + VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS); + + + for (i = 0; i <= (final_zone*num_of_samples); i++) { + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_wait_for_range_completion(Dev); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_get_device_results( + Dev, + VL53L1_DEVICERESULTSLEVEL_FULL, + prs); + + + + if (status == VL53L1_ERROR_NONE && + pdev->ll_state.rd_device_state != + VL53L1_DEVICESTATE_RANGING_WAIT_GPH_SYNC) { + + zone_id = pdev->ll_state.rd_zone_id + xtalk_result_id; + prange_data = &(prs->VL53L1_p_002[0]); + + + if (prs->active_results > 1) { + for (j = 1; + j < prs->active_results; j++) { + if (prs->VL53L1_p_002[j].median_range_mm + < + prange_data->median_range_mm) + prange_data = + &(prs->VL53L1_p_002[j]); + + } + } + + pxtalk_range_data = &(pXR->VL53L1_p_002[zone_id]); + + + + if ((prs->active_results > 0) && + (prange_data->median_range_mm < + xtalk_filter_thresh_max_mm) && + (prange_data->median_range_mm > + xtalk_filter_thresh_min_mm) && + (prange_data->VL53L1_p_012 < + (uint32_t)(xtalk_max_valid_rate_kcps * 16))) + valid_result = 1; + else + valid_result = 0; + + if (valid_result == 1) { + + pxtalk_range_data->no_of_samples++; + + pxtalk_range_data->rate_per_spad_kcps_sum += + prange_data->VL53L1_p_012; + + pxtalk_range_data->signal_total_events_sum += + prange_data->VL53L1_p_013; + + pxtalk_range_data->sigma_mm_sum += + (uint32_t)prange_data->VL53L1_p_005; + + + + pxtalk_range_data->median_phase_sum += + (uint32_t)prange_data->VL53L1_p_014; + + + + + } + + if ((valid_result == 1) && (zone_id >= 4)) { + status = VL53L1_sum_histogram_data( + &(pdev->hist_data), + psum_histo); + + + + if (prange_data->VL53L1_p_015 < + pXR->central_histogram__window_start) + pXR->central_histogram__window_start = + prange_data->VL53L1_p_015; + + + if (prange_data->VL53L1_p_016 > + pXR->central_histogram__window_end) + pXR->central_histogram__window_end = + prange_data->VL53L1_p_016; + + } + + } + + + +#ifdef VL53L1_LOG_ENABLE + if (status == VL53L1_ERROR_NONE) { + VL53L1_print_range_results( + &(pres->range_results), + "pres->range_results.", + VL53L1_TRACE_MODULE_CORE); + } +#endif + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_wait_for_firmware_ready(Dev); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_clear_interrupt_and_enable_next_range( + Dev, + measurement_mode); + + + } + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_stop_range(Dev); + + VL53L1_unload_patch(Dev); + + + + for (i = 0; i < (pdev->zone_cfg.active_zones+1); i++) { + + pxtalk_range_data = &(pXR->VL53L1_p_002[i+xtalk_result_id]); + + if (pxtalk_range_data->no_of_samples > 0) { + pxtalk_range_data->rate_per_spad_kcps_avg = + pxtalk_range_data->rate_per_spad_kcps_sum / + (uint32_t)pxtalk_range_data->no_of_samples; + + pxtalk_range_data->signal_total_events_avg = + pxtalk_range_data->signal_total_events_sum / + (int32_t)pxtalk_range_data->no_of_samples; + + pxtalk_range_data->sigma_mm_avg = + pxtalk_range_data->sigma_mm_sum / + (uint32_t)pxtalk_range_data->no_of_samples; + + + + pxtalk_range_data->median_phase_avg = + pxtalk_range_data->median_phase_sum / + (uint32_t)pxtalk_range_data->no_of_samples; + + + + } else { + pxtalk_range_data->rate_per_spad_kcps_avg = + pxtalk_range_data->rate_per_spad_kcps_sum; + pxtalk_range_data->signal_total_events_avg = + pxtalk_range_data->signal_total_events_sum; + pxtalk_range_data->sigma_mm_avg = + pxtalk_range_data->sigma_mm_sum; + + + + pxtalk_range_data->median_phase_avg = + pxtalk_range_data->median_phase_sum; + + + } + } + + + + memcpy(pavg_histo, &(pdev->hist_data), + sizeof(VL53L1_histogram_bin_data_t)); + + + + if (status == VL53L1_ERROR_NONE) { + + pxtalk_range_data = &(pXR->VL53L1_p_002[xtalk_histo_id]); + + status = VL53L1_avg_histogram_data( + pxtalk_range_data->no_of_samples, + psum_histo, + pavg_histo); + } + + + + + if (status == VL53L1_ERROR_NONE) { + if (smudge_corr_en == 1) + status = VL53L1_dynamic_xtalk_correction_enable(Dev); + } + + + LOG_FUNCTION_END(status); + + return status; + +} + + + +VL53L1_Error VL53L1_run_offset_calibration( + VL53L1_DEV Dev, + int16_t cal_distance_mm, + uint16_t cal_reflectance_pc, + VL53L1_Error *pcal_status) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + VL53L1_DevicePresetModes device_preset_modes[ + VL53L1_MAX_OFFSET_RANGE_RESULTS]; + + VL53L1_range_results_t *prange_results = + (VL53L1_range_results_t *) pdev->wArea1; + + VL53L1_range_data_t *pRData = NULL; + VL53L1_offset_range_data_t *pfs = NULL; + VL53L1_general_config_t *pG = &(pdev->gen_cfg); + VL53L1_additional_offset_cal_data_t *pAO = &(pdev->add_off_cal_data); + + uint8_t i = 0; + uint8_t m = 0; + uint8_t measurement_mode = + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + uint16_t manual_effective_spads = + pG->dss_config__manual_effective_spads_select; + + uint8_t num_of_samples[VL53L1_MAX_OFFSET_RANGE_RESULTS]; + + uint8_t smudge_corr_en = 0; + + LOG_FUNCTION_START(""); + + + + switch (pdev->offset_calibration_mode) { + + case VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__HISTOGRAM: + case VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__HISTOGRAM_PRE_RANGE_ONLY: + device_preset_modes[0] = + VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING; + device_preset_modes[1] = + VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_MM1_CAL; + device_preset_modes[2] = + VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_MM2_CAL; + break; + + default: + device_preset_modes[0] = + VL53L1_DEVICEPRESETMODE_STANDARD_RANGING; + device_preset_modes[1] = + VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_MM1_CAL; + device_preset_modes[2] = + VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_MM2_CAL; + break; + } + + + + num_of_samples[0] = pdev->offsetcal_cfg.pre_num_of_samples; + num_of_samples[1] = pdev->offsetcal_cfg.mm1_num_of_samples; + num_of_samples[2] = pdev->offsetcal_cfg.mm2_num_of_samples; + + + + + switch (pdev->offset_calibration_mode) { + + case VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD_PRE_RANGE_ONLY: + case VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__HISTOGRAM_PRE_RANGE_ONLY: + + pdev->offset_results.active_results = 1; + + break; + + default: + + pdev->customer.mm_config__inner_offset_mm = 0; + pdev->customer.mm_config__outer_offset_mm = 0; + pdev->offset_results.active_results = + VL53L1_MAX_OFFSET_RANGE_RESULTS; + + break; + } + + pdev->customer.algo__part_to_part_range_offset_mm = 0; + + + + pdev->offset_results.max_results = VL53L1_MAX_OFFSET_RANGE_RESULTS; + pdev->offset_results.cal_distance_mm = cal_distance_mm; + pdev->offset_results.cal_reflectance_pc = cal_reflectance_pc; + + for (m = 0; m < VL53L1_MAX_OFFSET_RANGE_RESULTS; m++) { + + pfs = &(pdev->offset_results.VL53L1_p_002[m]); + pfs->preset_mode = 0; + pfs->no_of_samples = 0; + pfs->effective_spads = 0; + pfs->peak_rate_mcps = 0; + pfs->VL53L1_p_005 = 0; + pfs->median_range_mm = 0; + } + + + + + smudge_corr_en = pdev->smudge_correct_config.smudge_corr_enabled; + + status = VL53L1_dynamic_xtalk_correction_disable(Dev); + + + + for (m = 0; m < pdev->offset_results.active_results; m++) { + + pfs = &(pdev->offset_results.VL53L1_p_002[m]); + + pfs->preset_mode = device_preset_modes[m]; + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_preset_mode( + Dev, + device_preset_modes[m], + + pdev->offsetcal_cfg.dss_config__target_total_rate_mcps, + pdev->offsetcal_cfg.phasecal_config_timeout_us, + pdev->offsetcal_cfg.mm_config_timeout_us, + pdev->offsetcal_cfg.range_config_timeout_us, + + 100); + + pG->dss_config__manual_effective_spads_select = + manual_effective_spads; + + + VL53L1_load_patch(Dev); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_init_and_start_range( + Dev, + measurement_mode, + VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS); + + for (i = 0; i <= (num_of_samples[m]+2); i++) { + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_wait_for_range_completion(Dev); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_get_device_results( + Dev, + VL53L1_DEVICERESULTSLEVEL_FULL, + prange_results); + + + + pRData = &(prange_results->VL53L1_p_002[0]); + + if ((prange_results->active_results > 0 && + prange_results->stream_count > 1) && + (pRData->range_status == + VL53L1_DEVICEERROR_RANGECOMPLETE)) { + + pfs->no_of_samples++; + pfs->effective_spads += + (uint32_t)pRData->VL53L1_p_006; + pfs->peak_rate_mcps += + (uint32_t)pRData->peak_signal_count_rate_mcps; + pfs->VL53L1_p_005 += + (uint32_t)pRData->VL53L1_p_005; + pfs->median_range_mm += + (int32_t)pRData->median_range_mm; + + pfs->dss_config__roi_mode_control = + pG->dss_config__roi_mode_control; + pfs->dss_config__manual_effective_spads_select = + pG->dss_config__manual_effective_spads_select; + + } + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_wait_for_firmware_ready(Dev); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_clear_interrupt_and_enable_next_range( + Dev, + measurement_mode); + } + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_stop_range(Dev); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WaitUs(Dev, 1000); + VL53L1_unload_patch(Dev); + + + if (pfs->no_of_samples > 0) { + + pfs->effective_spads += (pfs->no_of_samples/2); + pfs->effective_spads /= pfs->no_of_samples; + + pfs->peak_rate_mcps += (pfs->no_of_samples/2); + pfs->peak_rate_mcps /= pfs->no_of_samples; + + pfs->VL53L1_p_005 += (pfs->no_of_samples/2); + pfs->VL53L1_p_005 /= pfs->no_of_samples; + + pfs->median_range_mm += (pfs->no_of_samples/2); + pfs->median_range_mm /= pfs->no_of_samples; + + pfs->range_mm_offset = (int32_t)cal_distance_mm; + pfs->range_mm_offset -= pfs->median_range_mm; + + + if (pfs->preset_mode == + VL53L1_DEVICEPRESETMODE_STANDARD_RANGING) + manual_effective_spads = + (uint16_t)pfs->effective_spads; + } + } + + + + switch (pdev->offset_calibration_mode) { + + case VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD_PRE_RANGE_ONLY: + case VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__HISTOGRAM_PRE_RANGE_ONLY: + + + pdev->customer.mm_config__inner_offset_mm += + (int16_t)pdev->offset_results.VL53L1_p_002[0].range_mm_offset; + pdev->customer.mm_config__outer_offset_mm += + (int16_t)pdev->offset_results.VL53L1_p_002[0].range_mm_offset; + break; + + default: + + pdev->customer.mm_config__inner_offset_mm = + (int16_t)pdev->offset_results.VL53L1_p_002[1].range_mm_offset; + pdev->customer.mm_config__outer_offset_mm = + (int16_t)pdev->offset_results.VL53L1_p_002[2].range_mm_offset; + pdev->customer.algo__part_to_part_range_offset_mm = 0; + + + + pAO->result__mm_inner_actual_effective_spads = + (uint16_t)pdev->offset_results.VL53L1_p_002[1].effective_spads; + pAO->result__mm_outer_actual_effective_spads = + (uint16_t)pdev->offset_results.VL53L1_p_002[2].effective_spads; + + pAO->result__mm_inner_peak_signal_count_rtn_mcps = + (uint16_t)pdev->offset_results.VL53L1_p_002[1].peak_rate_mcps; + pAO->result__mm_outer_peak_signal_count_rtn_mcps = + (uint16_t)pdev->offset_results.VL53L1_p_002[2].peak_rate_mcps; + + break; + } + + + + pdev->cust_dmax_cal.ref__actual_effective_spads = + (uint16_t)pdev->offset_results.VL53L1_p_002[0].effective_spads; + pdev->cust_dmax_cal.ref__peak_signal_count_rate_mcps = + (uint16_t)pdev->offset_results.VL53L1_p_002[0].peak_rate_mcps; + + + pdev->cust_dmax_cal.ref__distance_mm = cal_distance_mm * 16; + + pdev->cust_dmax_cal.ref_reflectance_pc = cal_reflectance_pc; + pdev->cust_dmax_cal.coverglass_transmission = 0x0100; + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_customer_nvm_managed( + Dev, + &(pdev->customer)); + + + + + if (status == VL53L1_ERROR_NONE) { + if (smudge_corr_en == 1) + status = VL53L1_dynamic_xtalk_correction_enable(Dev); + } + + + + + for (m = 0; m < pdev->offset_results.active_results; m++) { + + pfs = &(pdev->offset_results.VL53L1_p_002[m]); + + if (status == VL53L1_ERROR_NONE) { + + pdev->offset_results.cal_report = m; + + if (pfs->no_of_samples < num_of_samples[m]) + status = + VL53L1_WARNING_OFFSET_CAL_MISSING_SAMPLES; + + + if (m == 0 && pfs->VL53L1_p_005 > + ((uint32_t)VL53L1_OFFSET_CAL_MAX_SIGMA_MM << 5)) + status = + VL53L1_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH; + + if (pfs->peak_rate_mcps > + VL53L1_OFFSET_CAL_MAX_PRE_PEAK_RATE_MCPS) + status = + VL53L1_WARNING_OFFSET_CAL_RATE_TOO_HIGH; + + if (pfs->dss_config__manual_effective_spads_select < + VL53L1_OFFSET_CAL_MIN_EFFECTIVE_SPADS) + status = + VL53L1_WARNING_OFFSET_CAL_SPAD_COUNT_TOO_LOW; + + if (pfs->dss_config__manual_effective_spads_select == 0) + status = + VL53L1_ERROR_OFFSET_CAL_NO_SPADS_ENABLED_FAIL; + + if (pfs->no_of_samples == 0) + status = VL53L1_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL; + } + } + + + + pdev->offset_results.cal_status = status; + *pcal_status = pdev->offset_results.cal_status; + + + + IGNORE_STATUS( + IGNORE_OFFSET_CAL_MISSING_SAMPLES, + VL53L1_WARNING_OFFSET_CAL_MISSING_SAMPLES, + status); + + IGNORE_STATUS( + IGNORE_OFFSET_CAL_SIGMA_TOO_HIGH, + VL53L1_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH, + status); + + IGNORE_STATUS( + IGNORE_OFFSET_CAL_RATE_TOO_HIGH, + VL53L1_WARNING_OFFSET_CAL_RATE_TOO_HIGH, + status); + + IGNORE_STATUS( + IGNORE_OFFSET_CAL_SPAD_COUNT_TOO_LOW, + VL53L1_WARNING_OFFSET_CAL_SPAD_COUNT_TOO_LOW, + status); + +#ifdef VL53L1_LOG_ENABLE + + + + VL53L1_print_customer_nvm_managed( + &(pdev->customer), + "run_offset_calibration():pdev->lldata.customer.", + VL53L1_TRACE_MODULE_OFFSET_DATA); + + VL53L1_print_dmax_calibration_data( + &(pdev->fmt_dmax_cal), + "run_offset_calibration():pdev->lldata.fmt_dmax_cal.", + VL53L1_TRACE_MODULE_OFFSET_DATA); + + VL53L1_print_dmax_calibration_data( + &(pdev->cust_dmax_cal), + "run_offset_calibration():pdev->lldata.cust_dmax_cal.", + VL53L1_TRACE_MODULE_OFFSET_DATA); + + VL53L1_print_additional_offset_cal_data( + &(pdev->add_off_cal_data), + "run_offset_calibration():pdev->lldata.add_off_cal_data.", + VL53L1_TRACE_MODULE_OFFSET_DATA); + + VL53L1_print_offset_range_results( + &(pdev->offset_results), + "run_offset_calibration():pdev->lldata.offset_results.", + VL53L1_TRACE_MODULE_OFFSET_DATA); +#endif + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_run_phasecal_average( + VL53L1_DEV Dev, + uint8_t measurement_mode, + uint8_t phasecal_result__vcsel_start, + uint16_t phasecal_num_of_samples, + VL53L1_range_results_t *prange_results, + uint16_t *pphasecal_result__reference_phase, + uint16_t *pzero_distance_phase) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + uint16_t i = 0; + uint16_t m = 0; + uint32_t samples = 0; + + uint32_t period = 0; + uint32_t VL53L1_p_017 = 0; + uint32_t phasecal_result__reference_phase = 0; + uint32_t zero_distance_phase = 0; + + + VL53L1_load_patch(Dev); + + for (m = 0; m < phasecal_num_of_samples; m++) { + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_init_and_start_range( + Dev, + measurement_mode, + VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS); + + for (i = 0; i <= 1; i++) { + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_wait_for_range_completion(Dev); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_get_device_results( + Dev, + VL53L1_DEVICERESULTSLEVEL_FULL, + prange_results); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_wait_for_firmware_ready(Dev); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_clear_interrupt_and_enable_next_range( + Dev, + measurement_mode); + } + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_stop_range(Dev); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WaitUs(Dev, 1000); + + + + if (status == VL53L1_ERROR_NONE) { + + samples++; + + + period = 2048 * + (uint32_t)VL53L1_decode_vcsel_period( + pdev->hist_data.VL53L1_p_009); + + VL53L1_p_017 = period; + VL53L1_p_017 += (uint32_t)( + pdev->hist_data.phasecal_result__reference_phase); + VL53L1_p_017 += + (2048 * + (uint32_t)phasecal_result__vcsel_start); + VL53L1_p_017 -= (2048 * + (uint32_t)pdev->hist_data.cal_config__vcsel_start); + + if (period != 0) { + VL53L1_p_017 = VL53L1_p_017 % period; + } + else { + status = + VL53L1_ERROR_DIVISION_BY_ZERO; + VL53L1_p_017 = 0; + } + + phasecal_result__reference_phase += (uint32_t)( + pdev->hist_data.phasecal_result__reference_phase); + + zero_distance_phase += (uint32_t)VL53L1_p_017; + } + } + VL53L1_unload_patch(Dev); + + + + if (status == VL53L1_ERROR_NONE && samples > 0) { + + phasecal_result__reference_phase += (samples >> 1); + phasecal_result__reference_phase /= samples; + + zero_distance_phase += (samples >> 1); + zero_distance_phase /= samples; + + *pphasecal_result__reference_phase = + (uint16_t)phasecal_result__reference_phase; + *pzero_distance_phase = + (uint16_t)zero_distance_phase; + } + + return status; +} + + +VL53L1_Error VL53L1_run_zone_calibration( + VL53L1_DEV Dev, + VL53L1_DevicePresetModes device_preset_mode, + VL53L1_DeviceZonePreset zone_preset, + VL53L1_zone_config_t *pzone_cfg, + int16_t cal_distance_mm, + uint16_t cal_reflectance_pc, + VL53L1_Error *pcal_status) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + VL53L1_range_results_t *pRR = + (VL53L1_range_results_t *) pdev->wArea1; + VL53L1_range_data_t *prange_data = NULL; + VL53L1_zone_calibration_data_t *pzone_data = NULL; + + uint16_t i = 0; + uint16_t m = 0; + + uint8_t z = 0; + uint8_t measurement_mode = + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + + VL53L1_OffsetCorrectionMode offset_cor_mode = + VL53L1_OFFSETCORRECTIONMODE__NONE; + + LOG_FUNCTION_START(""); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_preset_mode( + Dev, + device_preset_mode, + + pdev->zonecal_cfg.dss_config__target_total_rate_mcps, + pdev->zonecal_cfg.phasecal_config_timeout_us, + pdev->zonecal_cfg.mm_config_timeout_us, + pdev->zonecal_cfg.range_config_timeout_us, + + 100); + + + + if (zone_preset == VL53L1_DEVICEZONEPRESET_CUSTOM) { + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_zone_config( + Dev, + pzone_cfg); + + } else if (zone_preset != VL53L1_DEVICEZONEPRESET_NONE) { + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_zone_preset( + Dev, + zone_preset); + } + + + + pres->zone_cal.preset_mode = device_preset_mode; + pres->zone_cal.zone_preset = zone_preset; + + pres->zone_cal.cal_distance_mm = cal_distance_mm * 16; + pres->zone_cal.cal_reflectance_pc = cal_reflectance_pc; + pres->zone_cal.max_zones = VL53L1_MAX_USER_ZONES; + pres->zone_cal.active_zones = pdev->zone_cfg.active_zones + 1; + + for (i = 0; i < VL53L1_MAX_USER_ZONES; i++) { + pres->zone_cal.VL53L1_p_002[i].no_of_samples = 0; + pres->zone_cal.VL53L1_p_002[i].effective_spads = 0; + pres->zone_cal.VL53L1_p_002[i].peak_rate_mcps = 0; + pres->zone_cal.VL53L1_p_002[i].VL53L1_p_014 = 0; + pres->zone_cal.VL53L1_p_002[i].VL53L1_p_005 = 0; + pres->zone_cal.VL53L1_p_002[i].median_range_mm = 0; + pres->zone_cal.VL53L1_p_002[i].range_mm_offset = 0; + } + + pres->zone_cal.phasecal_result__reference_phase = 0; + pres->zone_cal.zero_distance_phase = 0; + + + + status = + VL53L1_get_offset_correction_mode( + Dev, + &offset_cor_mode); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_offset_correction_mode( + Dev, + VL53L1_OFFSETCORRECTIONMODE__NONE); + + + VL53L1_load_patch(Dev); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_init_and_start_range( + Dev, + measurement_mode, + VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS); + + + + + m = (pdev->zonecal_cfg.zone_num_of_samples + 2) * + (uint16_t)pres->zone_cal.active_zones; + + + for (i = 0; i <= m; i++) { + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_wait_for_range_completion(Dev); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_get_device_results( + Dev, + VL53L1_DEVICERESULTSLEVEL_FULL, + pRR); + + + + prange_data = &(pRR->VL53L1_p_002[0]); + + if (pRR->active_results > 0 && + i > (uint16_t)pres->zone_cal.active_zones) { + + if (prange_data->range_status == + VL53L1_DEVICEERROR_RANGECOMPLETE) { + + pres->zone_cal.phasecal_result__reference_phase + = + pdev->hist_data.phasecal_result__reference_phase + ; + pres->zone_cal.zero_distance_phase = + pdev->hist_data.zero_distance_phase; + + pzone_data = + &(pres->zone_cal.VL53L1_p_002[pRR->zone_id]); + pzone_data->no_of_samples++; + pzone_data->effective_spads += + (uint32_t)prange_data->VL53L1_p_006; + pzone_data->peak_rate_mcps += (uint32_t)( + prange_data->peak_signal_count_rate_mcps); + pzone_data->VL53L1_p_014 += + (uint32_t)prange_data->VL53L1_p_014; + pzone_data->VL53L1_p_005 += + (uint32_t)prange_data->VL53L1_p_005; + pzone_data->median_range_mm += + (int32_t)prange_data->median_range_mm; + + } + } + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_wait_for_firmware_ready(Dev); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_clear_interrupt_and_enable_next_range( + Dev, + measurement_mode); + } + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_stop_range(Dev); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WaitUs(Dev, 1000); + VL53L1_unload_patch(Dev); + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_run_phasecal_average( + Dev, + measurement_mode, + pdev->hist_data.phasecal_result__vcsel_start, + + pdev->zonecal_cfg.phasecal_num_of_samples, + + pRR, + &(pres->zone_cal.phasecal_result__reference_phase), + &(pres->zone_cal.zero_distance_phase)); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_offset_correction_mode( + Dev, + offset_cor_mode); + + + + if (status == VL53L1_ERROR_NONE) { + + for (z = 0; z < pres->zone_cal.active_zones; z++) { + + pzone_data = &(pres->zone_cal.VL53L1_p_002[z]); + + + if (pzone_data->no_of_samples > 0) { + + pzone_data->effective_spads += + (pzone_data->no_of_samples/2); + pzone_data->effective_spads /= + pzone_data->no_of_samples; + + pzone_data->peak_rate_mcps += + (pzone_data->no_of_samples/2); + pzone_data->peak_rate_mcps /= + pzone_data->no_of_samples; + + pzone_data->VL53L1_p_014 += + (pzone_data->no_of_samples/2); + pzone_data->VL53L1_p_014 /= + pzone_data->no_of_samples; + + pzone_data->VL53L1_p_005 += + (pzone_data->no_of_samples/2); + pzone_data->VL53L1_p_005 /= + pzone_data->no_of_samples; + + + + pzone_data->median_range_mm = + VL53L1_range_maths( + pdev->stat_nvm.osc_measured__fast_osc__frequency + , (uint16_t)pzone_data->VL53L1_p_014, + pres->zone_cal.zero_distance_phase, + 2, + 0x0800, + 0); + + pzone_data->range_mm_offset = + ((int32_t)cal_distance_mm) * 4; + pzone_data->range_mm_offset -= + pzone_data->median_range_mm; + + + if (pzone_data->no_of_samples < + pdev->zonecal_cfg.zone_num_of_samples) + status = + VL53L1_WARNING_ZONE_CAL_MISSING_SAMPLES; + + + if (pzone_data->VL53L1_p_005 > + ((uint32_t)VL53L1_ZONE_CAL_MAX_SIGMA_MM + << 5)) + status = + VL53L1_WARNING_ZONE_CAL_SIGMA_TOO_HIGH; + + if (pzone_data->peak_rate_mcps > + VL53L1_ZONE_CAL_MAX_PRE_PEAK_RATE_MCPS) + status = + VL53L1_WARNING_ZONE_CAL_RATE_TOO_HIGH; + + } else { + status = VL53L1_ERROR_ZONE_CAL_NO_SAMPLE_FAIL; + } + } + } + + + + pres->zone_cal.cal_status = status; + *pcal_status = pres->zone_cal.cal_status; + + + + IGNORE_STATUS( + IGNORE_ZONE_CAL_MISSING_SAMPLES, + VL53L1_WARNING_ZONE_CAL_MISSING_SAMPLES, + status); + + IGNORE_STATUS( + IGNORE_ZONE_CAL_SIGMA_TOO_HIGH, + VL53L1_WARNING_ZONE_CAL_SIGMA_TOO_HIGH, + status); + + IGNORE_STATUS( + IGNORE_ZONE_CAL_RATE_TOO_HIGH, + VL53L1_WARNING_ZONE_CAL_RATE_TOO_HIGH, + status); + +#ifdef VL53L1_LOG_ENABLE + + + + VL53L1_print_zone_calibration_results( + &(pres->zone_cal), + "run_zone_calibration():pdev->llresults.zone_cal.", + VL53L1_TRACE_MODULE_OFFSET_DATA); + +#endif + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_run_spad_rate_map( + VL53L1_DEV Dev, + VL53L1_DeviceTestMode device_test_mode, + VL53L1_DeviceSscArray array_select, + uint32_t ssc_config_timeout_us, + VL53L1_spad_rate_data_t *pspad_rate_data) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_powerforce(Dev); + + + + if (status == VL53L1_ERROR_NONE) { + pdev->ssc_cfg.array_select = array_select; + pdev->ssc_cfg.timeout_us = ssc_config_timeout_us; + status = + VL53L1_set_ssc_config( + Dev, + &(pdev->ssc_cfg), + pdev->stat_nvm.osc_measured__fast_osc__frequency); + } + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_run_device_test( + Dev, + device_test_mode); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_get_spad_rate_data( + Dev, + pspad_rate_data); + + if (device_test_mode == VL53L1_DEVICETESTMODE_LCR_VCSEL_ON) + pspad_rate_data->fractional_bits = 7; + else + pspad_rate_data->fractional_bits = 15; + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_powerforce(Dev); + +#ifdef VL53L1_LOG_ENABLE + + + if (status == VL53L1_ERROR_NONE) { + VL53L1_print_spad_rate_data( + pspad_rate_data, + "run_spad_rate_map():", + VL53L1_TRACE_MODULE_SPAD_RATE_MAP); + VL53L1_print_spad_rate_map( + pspad_rate_data, + "run_spad_rate_map():", + VL53L1_TRACE_MODULE_SPAD_RATE_MAP); + } +#endif + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_run_device_test( + VL53L1_DEV Dev, + VL53L1_DeviceTestMode device_test_mode) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t comms_buffer[2]; + uint8_t gpio_hv_mux__ctrl = 0; + + LOG_FUNCTION_START(""); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_RdByte( + Dev, + VL53L1_GPIO_HV_MUX__CTRL, + &gpio_hv_mux__ctrl); + + if (status == VL53L1_ERROR_NONE) + pdev->stat_cfg.gpio_hv_mux__ctrl = gpio_hv_mux__ctrl; + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_start_test( + Dev, + device_test_mode); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_wait_for_test_completion(Dev); + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_ReadMulti( + Dev, + VL53L1_RESULT__RANGE_STATUS, + comms_buffer, + 2); + + if (status == VL53L1_ERROR_NONE) { + pdev->sys_results.result__range_status = comms_buffer[0]; + pdev->sys_results.result__report_status = comms_buffer[1]; + } + + + + pdev->sys_results.result__range_status &= + VL53L1_RANGE_STATUS__RANGE_STATUS_MASK; + + if (status == VL53L1_ERROR_NONE) { + trace_print( + VL53L1_TRACE_LEVEL_INFO, + " Device Test Complete:\n\t%-32s = %3u\n\t%-32s = %3u\n", + "result__range_status", + pdev->sys_results.result__range_status, + "result__report_status", + pdev->sys_results.result__report_status); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_clear_interrupt(Dev); + } + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_start_test( + Dev, + 0x00); + + LOG_FUNCTION_END(status); + + return status; +} + + +void VL53L1_hist_xtalk_extract_data_init( + VL53L1_hist_xtalk_extract_data_t *pxtalk_data) +{ + + + int32_t lb = 0; + + pxtalk_data->sample_count = 0U; + pxtalk_data->pll_period_mm = 0U; + pxtalk_data->peak_duration_us_sum = 0U; + pxtalk_data->effective_spad_count_sum = 0U; + pxtalk_data->zero_distance_phase_sum = 0U; + pxtalk_data->zero_distance_phase_avg = 0U; + pxtalk_data->event_scaler_sum = 0U; + pxtalk_data->event_scaler_avg = 4096U; + pxtalk_data->signal_events_sum = 0; + pxtalk_data->xtalk_rate_kcps_per_spad = 0U; + pxtalk_data->VL53L1_p_015 = 0U; + pxtalk_data->VL53L1_p_016 = 0U; + pxtalk_data->target_start = 0U; + + for (lb = 0; lb < VL53L1_XTALK_HISTO_BINS; lb++) + pxtalk_data->bin_data_sums[lb] = 0; + +} + + +VL53L1_Error VL53L1_hist_xtalk_extract_update( + int16_t target_distance_mm, + uint16_t target_width_oversize, + VL53L1_histogram_bin_data_t *phist_bins, + VL53L1_hist_xtalk_extract_data_t *pxtalk_data) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + status = + VL53L1_hist_xtalk_extract_calc_window( + target_distance_mm, + target_width_oversize, + phist_bins, + pxtalk_data); + + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_hist_xtalk_extract_calc_event_sums( + phist_bins, + pxtalk_data); + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_hist_xtalk_extract_fini( + VL53L1_histogram_bin_data_t *phist_bins, + VL53L1_hist_xtalk_extract_data_t *pxtalk_data, + VL53L1_xtalk_calibration_results_t *pxtalk_cal, + VL53L1_xtalk_histogram_shape_t *pxtalk_shape) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_xtalk_calibration_results_t *pX = pxtalk_cal; + + LOG_FUNCTION_START(""); + + if (pxtalk_data->sample_count > 0) { + + + pxtalk_data->event_scaler_avg = pxtalk_data->event_scaler_sum; + pxtalk_data->event_scaler_avg += + (pxtalk_data->sample_count >> 1); + pxtalk_data->event_scaler_avg /= pxtalk_data->sample_count; + + + + status = + VL53L1_hist_xtalk_extract_calc_rate_per_spad( + pxtalk_data); + + + + if (status == VL53L1_ERROR_NONE) { + + + pxtalk_data->zero_distance_phase_avg = + pxtalk_data->zero_distance_phase_sum; + pxtalk_data->zero_distance_phase_avg += + (pxtalk_data->sample_count >> 1); + pxtalk_data->zero_distance_phase_avg /= + pxtalk_data->sample_count; + + + status = + VL53L1_hist_xtalk_extract_calc_shape( + pxtalk_data, + pxtalk_shape); + + + + + pxtalk_shape->phasecal_result__vcsel_start = + phist_bins->phasecal_result__vcsel_start; + pxtalk_shape->cal_config__vcsel_start = + phist_bins->cal_config__vcsel_start; + pxtalk_shape->vcsel_width = + phist_bins->vcsel_width; + pxtalk_shape->VL53L1_p_019 = + phist_bins->VL53L1_p_019; + } + + + if (status == VL53L1_ERROR_NONE) { + + + pX->algo__crosstalk_compensation_plane_offset_kcps = + pxtalk_data->xtalk_rate_kcps_per_spad; + pX->algo__crosstalk_compensation_x_plane_gradient_kcps + = 0U; + pX->algo__crosstalk_compensation_y_plane_gradient_kcps + = 0U; + + } + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_run_hist_xtalk_extraction( + VL53L1_DEV Dev, + int16_t cal_distance_mm, + VL53L1_Error *pcal_status) +{ + + + #define OVERSIZE 4 + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_xtalkextract_config_t *pX = &(pdev->xtalk_extract_cfg); + VL53L1_xtalk_config_t *pC = &(pdev->xtalk_cfg); + VL53L1_xtalk_calibration_results_t *pXC = &(pdev->xtalk_cal); + + + + uint8_t smudge_corr_en = 0; + uint8_t i = 0; + int8_t k = 0; + uint8_t nbloops; + int32_t initMergeSize = 0; + int32_t MergeEnabled = 0; + uint32_t deltaXtalk; + uint32_t stepXtalk; + uint32_t XtalkMin; + uint32_t XtalkMax; + uint8_t measurement_mode = VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + int8_t MaxId; + uint8_t histo_merge_nb; + uint8_t wait_for_accumulation; + VL53L1_range_results_t *prange_results = + (VL53L1_range_results_t *) pdev->wArea1; + uint8_t Very1stRange = 0; + + LOG_FUNCTION_START(""); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_preset_mode( + Dev, + VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE, + pX->dss_config__target_total_rate_mcps, + pX->phasecal_config_timeout_us, + pX->mm_config_timeout_us, + pX->range_config_timeout_us, + 100); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_xtalk_compensation(Dev); + + + + smudge_corr_en = pdev->smudge_correct_config.smudge_corr_enabled; + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_dynamic_xtalk_correction_disable(Dev); + + + VL53L1_load_patch(Dev); + + VL53L1_get_tuning_parm(Dev, VL53L1_TUNINGPARM_HIST_MERGE_MAX_SIZE, + &initMergeSize); + VL53L1_get_tuning_parm(Dev, VL53L1_TUNINGPARM_HIST_MERGE, + &MergeEnabled); + memset(&pdev->xtalk_cal, 0, sizeof(pdev->xtalk_cal)); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_and_start_range( + Dev, measurement_mode, + VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS); + + MaxId = pdev->tuning_parms.tp_hist_merge_max_size - 1; + nbloops = (MergeEnabled == 0 ? 1 : 2); + for (k = 0; k < nbloops; k++) { + + VL53L1_hist_xtalk_extract_data_init( + &(pdev->xtalk_extract)); + VL53L1_set_tuning_parm(Dev, + VL53L1_TUNINGPARM_HIST_MERGE_MAX_SIZE, + k * MaxId + 1); + + for (i = 0; i <= pX->num_of_samples; i++) { + if (status == VL53L1_ERROR_NONE) + status = VL53L1_wait_for_range_completion(Dev); + if (status == VL53L1_ERROR_NONE) + status = VL53L1_get_device_results(Dev, + VL53L1_DEVICERESULTSLEVEL_FULL, + prange_results); + Very1stRange = + (pdev->ll_state.rd_device_state == + VL53L1_DEVICESTATE_RANGING_WAIT_GPH_SYNC); + + VL53L1_compute_histo_merge_nb(Dev, &histo_merge_nb); + wait_for_accumulation = ((k != 0) && + (MergeEnabled) && + (status == VL53L1_ERROR_NONE) && + (histo_merge_nb < + pdev->tuning_parms.tp_hist_merge_max_size)); + if (wait_for_accumulation) + i = 0; + else { + if ((status == VL53L1_ERROR_NONE) && + (!Very1stRange)) { + status = + VL53L1_hist_xtalk_extract_update( + cal_distance_mm, + OVERSIZE, + &(pdev->hist_data), + &(pdev->xtalk_extract)); + } + } + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_wait_for_firmware_ready(Dev); + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_clear_interrupt_and_enable_next_range( + Dev, measurement_mode); + } + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_hist_xtalk_extract_fini( + &(pdev->hist_data), + &(pdev->xtalk_extract), + &(pdev->xtalk_cal), + &(pdev->xtalk_shapes.xtalk_shape)); + if (status == VL53L1_ERROR_NONE) { + pXC->algo__xtalk_cpo_HistoMerge_kcps[k * MaxId] = + pXC->algo__crosstalk_compensation_plane_offset_kcps; + } + } + + + VL53L1_stop_range(Dev); + + VL53L1_set_tuning_parm(Dev, VL53L1_TUNINGPARM_HIST_MERGE_MAX_SIZE, + initMergeSize); + VL53L1_unload_patch(Dev); + + if (status != VL53L1_ERROR_NONE) + status = VL53L1_ERROR_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL; + else if ((MergeEnabled == 1) && (MaxId > 0)) { + XtalkMin = pXC->algo__xtalk_cpo_HistoMerge_kcps[0]; + XtalkMax = pXC->algo__xtalk_cpo_HistoMerge_kcps[MaxId]; + pXC->algo__crosstalk_compensation_plane_offset_kcps = + XtalkMin; + if (XtalkMax >= XtalkMin) { + deltaXtalk = XtalkMax - XtalkMin; + stepXtalk = deltaXtalk / MaxId; + for (k = 1; k < MaxId; k++) + pXC->algo__xtalk_cpo_HistoMerge_kcps[k] = + XtalkMin + stepXtalk * k; + } else + status = + VL53L1_ERROR_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL; + } + + if (status == VL53L1_ERROR_NONE) { + pC->algo__crosstalk_compensation_x_plane_gradient_kcps = + pXC->algo__crosstalk_compensation_x_plane_gradient_kcps; + pC->algo__crosstalk_compensation_y_plane_gradient_kcps = + pXC->algo__crosstalk_compensation_y_plane_gradient_kcps; + pC->algo__crosstalk_compensation_plane_offset_kcps = + pXC->algo__crosstalk_compensation_plane_offset_kcps; + } + + + pdev->xtalk_results.cal_status = status; + *pcal_status = pdev->xtalk_results.cal_status; + + + status = VL53L1_enable_xtalk_compensation(Dev); + if (smudge_corr_en == 1) + status = VL53L1_dynamic_xtalk_correction_enable(Dev); + +#ifdef VL53L1_LOG_ENABLE + + + + VL53L1_print_customer_nvm_managed( + &(pdev->customer), + "run_xtalk_extraction():pdev->lldata.customer.", + VL53L1_TRACE_MODULE_XTALK_DATA); + + VL53L1_print_xtalk_config( + &(pdev->xtalk_cfg), + "run_xtalk_extraction():pdev->lldata.xtalk_cfg.", + VL53L1_TRACE_MODULE_XTALK_DATA); + + VL53L1_print_xtalk_histogram_data( + &(pdev->xtalk_shapes), + "pdev->lldata.xtalk_shapes.", + VL53L1_TRACE_MODULE_XTALK_DATA); + +#endif + + LOG_FUNCTION_END(status); + + return status; +} + diff --git a/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_api_core.c b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_api_core.c new file mode 100644 index 000000000000..c5c9f7db3fbe --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_api_core.c @@ -0,0 +1,7295 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + + +#include "vl53l1_ll_def.h" +#include "vl53l1_ll_device.h" +#include "vl53l1_platform.h" +#include "vl53l1_platform_ipp.h" +#include "vl53l1_register_map.h" +#include "vl53l1_register_settings.h" +#include "vl53l1_register_funcs.h" +#include "vl53l1_hist_map.h" +#include "vl53l1_hist_structs.h" +#include "vl53l1_nvm_map.h" +#include "vl53l1_nvm_structs.h" +#include "vl53l1_nvm.h" +#include "vl53l1_core.h" +#include "vl53l1_wait.h" +#include "vl53l1_zone_presets.h" +#include "vl53l1_api_preset_modes.h" +#include "vl53l1_silicon_core.h" +#include "vl53l1_api_core.h" +#include "vl53l1_tuning_parm_defaults.h" + +#ifdef VL53L1_LOG_ENABLE +#include "vl53l1_api_debug.h" +#endif + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_CORE, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_CORE, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_CORE, status, \ + fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_CORE, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + +#define VL53L1_MAX_I2C_XFER_SIZE 256 + +static VL53L1_Error select_offset_per_vcsel(VL53L1_LLDriverData_t *pdev, + int16_t *poffset) { + VL53L1_Error status = VL53L1_ERROR_NONE; + int16_t tA, tB; + uint8_t isc; + + switch (pdev->preset_mode) { + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE: + tA = pdev->per_vcsel_cal_data.short_a_offset_mm; + tB = pdev->per_vcsel_cal_data.short_b_offset_mm; + break; + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE: + tA = pdev->per_vcsel_cal_data.medium_a_offset_mm; + tB = pdev->per_vcsel_cal_data.medium_b_offset_mm; + break; + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE: + tA = pdev->per_vcsel_cal_data.long_a_offset_mm; + tB = pdev->per_vcsel_cal_data.long_b_offset_mm; + break; + default: + tA = pdev->per_vcsel_cal_data.long_a_offset_mm; + tB = pdev->per_vcsel_cal_data.long_b_offset_mm; + status = VL53L1_ERROR_INVALID_PARAMS; + *poffset = 0; + break; + } + + isc = pdev->ll_state.cfg_internal_stream_count; + if (status == VL53L1_ERROR_NONE) + *poffset = (isc & 0x01) ? tA : tB; + + return status; +} + +static void vl53l1_diff_histo_stddev(VL53L1_LLDriverData_t *pdev, + VL53L1_histogram_bin_data_t *pdata, uint8_t timing, uint8_t HighIndex, + uint8_t prev_pos, int32_t *pdiff_histo_stddev) { + uint16_t bin = 0; + int32_t total_rate_pre = 0; + int32_t total_rate_cur = 0; + int32_t PrevBin, CurrBin; + + total_rate_pre = 0; + total_rate_cur = 0; + + + for (bin = timing * 4; bin < HighIndex; bin++) { + total_rate_pre += + pdev->multi_bins_rec[prev_pos][timing][bin]; + total_rate_cur += pdata->bin_data[bin]; + } + + if ((total_rate_pre != 0) && (total_rate_cur != 0)) + for (bin = timing * 4; bin < HighIndex; bin++) { + PrevBin = pdev->multi_bins_rec[prev_pos][timing][bin]; + PrevBin = (PrevBin * 1000) / total_rate_pre; + CurrBin = pdata->bin_data[bin] * 1000 / total_rate_cur; + *pdiff_histo_stddev += (PrevBin - CurrBin) * + (PrevBin - CurrBin); + } +} + +static void vl53l1_histo_merge(VL53L1_DEV Dev, + VL53L1_histogram_bin_data_t *pdata) { + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + uint16_t bin = 0; + uint8_t i = 0; + int32_t TuningBinRecSize = 0; + uint8_t recom_been_reset = 0; + uint8_t timing = 0; + int32_t rmt = 0; + int32_t diff_histo_stddev = 0; + uint8_t HighIndex, prev_pos; + uint8_t BuffSize = VL53L1_HISTOGRAM_BUFFER_SIZE; + uint8_t pos; + + VL53L1_get_tuning_parm(Dev, VL53L1_TUNINGPARM_HIST_MERGE_MAX_SIZE, + &TuningBinRecSize); + + VL53L1_get_tuning_parm(Dev, VL53L1_TUNINGPARM_RESET_MERGE_THRESHOLD, + &rmt); + + + if (pdev->pos_before_next_recom == 0) { + + timing = 1 - pdata->result__stream_count % 2; + + diff_histo_stddev = 0; + HighIndex = BuffSize - timing * 4; + if (pdev->bin_rec_pos > 0) + prev_pos = pdev->bin_rec_pos - 1; + else + prev_pos = (TuningBinRecSize - 1); + + if (pdev->multi_bins_rec[prev_pos][timing][4] > 0) + vl53l1_diff_histo_stddev(pdev, pdata, + timing, HighIndex, prev_pos, + &diff_histo_stddev); + + if (diff_histo_stddev >= rmt) { + memset(pdev->multi_bins_rec, 0, + sizeof(pdev->multi_bins_rec)); + pdev->bin_rec_pos = 0; + + recom_been_reset = 1; + + if (timing == 0) + pdev->pos_before_next_recom = + VL53L1_FRAME_WAIT_EVENT; + else + pdev->pos_before_next_recom = + VL53L1_FRAME_WAIT_EVENT + 1; + } else { + + pos = pdev->bin_rec_pos; + for (i = 0; i < BuffSize; i++) + pdev->multi_bins_rec[pos][timing][i] = + pdata->bin_data[i]; + } + + if (pdev->bin_rec_pos == (TuningBinRecSize - 1) && timing == 1) + pdev->bin_rec_pos = 0; + else if (timing == 1) + pdev->bin_rec_pos++; + + if (!((recom_been_reset == 1) && (timing == 0)) && + (pdev->pos_before_next_recom == 0)) { + + for (bin = 0; bin < BuffSize; bin++) + pdata->bin_data[bin] = 0; + + for (bin = 0; bin < BuffSize; bin++) + for (i = 0; i < TuningBinRecSize; i++) + pdata->bin_data[bin] += + (pdev->multi_bins_rec[i][timing][bin]); + } + } else { + + pdev->pos_before_next_recom--; + if (pdev->pos_before_next_recom == 255) + pdev->pos_before_next_recom = 0; + } +} + +VL53L1_Error VL53L1_load_patch( + VL53L1_DEV Dev) { + VL53L1_Error status = VL53L1_ERROR_NONE; + int32_t patch_tuning = 0; + uint8_t comms_buffer[256]; + uint32_t patch_power; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte(Dev, + VL53L1_FIRMWARE__ENABLE, 0x00); + + if (status == VL53L1_ERROR_NONE) + VL53L1_enable_powerforce(Dev); + + VL53L1_get_tuning_parm(Dev, VL53L1_TUNINGPARM_PHASECAL_PATCH_POWER, + &patch_tuning); + + switch (patch_tuning) { + case 0: + patch_power = 0x00; + break; + case 1: + patch_power = 0x10; + break; + case 2: + patch_power = 0x20; + break; + case 3: + patch_power = 0x40; + break; + default: + patch_power = 0x00; + } + + if (status == VL53L1_ERROR_NONE) { + + comms_buffer[0] = 0x29; + comms_buffer[1] = 0xC9; + comms_buffer[2] = 0x0E; + comms_buffer[3] = 0x40; + comms_buffer[4] = 0x28; + comms_buffer[5] = patch_power; + + status = VL53L1_WriteMulti(Dev, + VL53L1_PATCH__OFFSET_0, comms_buffer, 6); + } + + if (status == VL53L1_ERROR_NONE) { + comms_buffer[0] = 0x03; + comms_buffer[1] = 0x6D; + comms_buffer[2] = 0x03; + comms_buffer[3] = 0x6F; + comms_buffer[4] = 0x07; + comms_buffer[5] = 0x29; + status = VL53L1_WriteMulti(Dev, + VL53L1_PATCH__ADDRESS_0, comms_buffer, 6); + } + + if (status == VL53L1_ERROR_NONE) { + comms_buffer[0] = 0x00; + comms_buffer[1] = 0x07; + status = VL53L1_WriteMulti(Dev, + VL53L1_PATCH__JMP_ENABLES, comms_buffer, 2); + } + + if (status == VL53L1_ERROR_NONE) { + comms_buffer[0] = 0x00; + comms_buffer[1] = 0x07; + status = VL53L1_WriteMulti(Dev, + VL53L1_PATCH__DATA_ENABLES, comms_buffer, 2); + } + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte(Dev, + VL53L1_PATCH__CTRL, 0x01); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte(Dev, + VL53L1_FIRMWARE__ENABLE, 0x01); + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_unload_patch( + VL53L1_DEV Dev) { + VL53L1_Error status = VL53L1_ERROR_NONE; + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte(Dev, + VL53L1_FIRMWARE__ENABLE, 0x00); + + if (status == VL53L1_ERROR_NONE) + VL53L1_disable_powerforce(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte(Dev, + VL53L1_PATCH__CTRL, 0x00); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte(Dev, + VL53L1_FIRMWARE__ENABLE, 0x01); + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_get_version( + VL53L1_DEV Dev, + VL53L1_ll_version_t *pdata) +{ + + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + VL53L1_init_version(Dev); + + memcpy(pdata, &(pdev->version), sizeof(VL53L1_ll_version_t)); + + return VL53L1_ERROR_NONE; +} + + +VL53L1_Error VL53L1_get_device_firmware_version( + VL53L1_DEV Dev, + uint16_t *pfw_version) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_RdWord( + Dev, + VL53L1_MCU_GENERAL_PURPOSE__GP_0, + pfw_version); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_data_init( + VL53L1_DEV Dev, + uint8_t read_p2p_data) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + + + VL53L1_zone_objects_t *pobjects; + + uint8_t i = 0; + + LOG_FUNCTION_START(""); + + VL53L1_init_ll_driver_state( + Dev, + VL53L1_DEVICESTATE_UNKNOWN); + + pres->range_results.max_results = VL53L1_MAX_RANGE_RESULTS; + pres->range_results.active_results = 0; + pres->zone_results.max_zones = VL53L1_MAX_USER_ZONES; + pres->zone_results.active_zones = 0; + + for (i = 0; i < VL53L1_MAX_USER_ZONES; i++) { + pobjects = &(pres->zone_results.VL53L1_p_002[i]); + pobjects->xmonitor.VL53L1_p_020 = 0; + pobjects->xmonitor.VL53L1_p_021 = 0; + pobjects->xmonitor.VL53L1_p_014 = 0; + pobjects->xmonitor.range_status = + VL53L1_DEVICEERROR_NOUPDATE; + } + + + + pres->zone_hists.max_zones = VL53L1_MAX_USER_ZONES; + pres->zone_hists.active_zones = 0; + + + + pres->zone_cal.max_zones = VL53L1_MAX_USER_ZONES; + pres->zone_cal.active_zones = 0; + for (i = 0; i < VL53L1_MAX_USER_ZONES; i++) { + pres->zone_cal.VL53L1_p_002[i].no_of_samples = 0; + pres->zone_cal.VL53L1_p_002[i].effective_spads = 0; + pres->zone_cal.VL53L1_p_002[i].peak_rate_mcps = 0; + pres->zone_cal.VL53L1_p_002[i].median_range_mm = 0; + pres->zone_cal.VL53L1_p_002[i].range_mm_offset = 0; + } + + pdev->wait_method = VL53L1_WAIT_METHOD_BLOCKING; + pdev->preset_mode = VL53L1_DEVICEPRESETMODE_STANDARD_RANGING; + pdev->zone_preset = VL53L1_DEVICEZONEPRESET_NONE; + pdev->measurement_mode = VL53L1_DEVICEMEASUREMENTMODE_STOP; + + pdev->offset_calibration_mode = + VL53L1_OFFSETCALIBRATIONMODE__MM1_MM2__STANDARD; + pdev->offset_correction_mode = + VL53L1_OFFSETCORRECTIONMODE__MM1_MM2_OFFSETS; + pdev->dmax_mode = + VL53L1_DEVICEDMAXMODE__FMT_CAL_DATA; + + pdev->phasecal_config_timeout_us = 1000; + pdev->mm_config_timeout_us = 2000; + pdev->range_config_timeout_us = 13000; + pdev->inter_measurement_period_ms = 100; + pdev->dss_config__target_total_rate_mcps = 0x0A00; + pdev->debug_mode = 0x00; + + pdev->offset_results.max_results = VL53L1_MAX_OFFSET_RANGE_RESULTS; + pdev->offset_results.active_results = 0; + + + + pdev->gain_cal.standard_ranging_gain_factor = + VL53L1_TUNINGPARM_LITE_RANGING_GAIN_FACTOR_DEFAULT; + pdev->gain_cal.histogram_ranging_gain_factor = + VL53L1_TUNINGPARM_HIST_GAIN_FACTOR_DEFAULT; + + + VL53L1_init_version(Dev); + + + memset(pdev->multi_bins_rec, 0, sizeof(pdev->multi_bins_rec)); + pdev->bin_rec_pos = 0; + pdev->pos_before_next_recom = 0; + + + + if (read_p2p_data > 0 && status == VL53L1_ERROR_NONE) + status = VL53L1_read_p2p_data(Dev); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_refspadchar_config_struct( + &(pdev->refspadchar)); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_ssc_config_struct( + &(pdev->ssc_cfg)); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_xtalk_config_struct( + &(pdev->customer), + &(pdev->xtalk_cfg)); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_xtalk_extract_config_struct( + &(pdev->xtalk_extract_cfg)); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_offset_cal_config_struct( + &(pdev->offsetcal_cfg)); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_zone_cal_config_struct( + &(pdev->zonecal_cfg)); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_hist_post_process_config_struct( + pdev->xtalk_cfg.global_crosstalk_compensation_enable, + &(pdev->histpostprocess)); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_hist_gen3_dmax_config_struct( + &(pdev->dmax_cfg)); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_tuning_parm_storage_struct( + &(pdev->tuning_parms)); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_set_preset_mode( + Dev, + pdev->preset_mode, + pdev->dss_config__target_total_rate_mcps, + pdev->phasecal_config_timeout_us, + pdev->mm_config_timeout_us, + pdev->range_config_timeout_us, + pdev->inter_measurement_period_ms); + + + VL53L1_init_histogram_bin_data_struct( + 0, + VL53L1_HISTOGRAM_BUFFER_SIZE, + &(pdev->hist_data)); + + VL53L1_init_histogram_bin_data_struct( + 0, + VL53L1_HISTOGRAM_BUFFER_SIZE, + &(pdev->hist_xtalk)); + + + VL53L1_init_xtalk_bin_data_struct( + 0, + VL53L1_XTALK_HISTO_BINS, + &(pdev->xtalk_shapes.xtalk_shape)); + + + + VL53L1_xtalk_cal_data_init( + Dev + ); + + + + VL53L1_dynamic_xtalk_correction_data_init( + Dev + ); + + + + VL53L1_low_power_auto_data_init( + Dev + ); + +#ifdef VL53L1_LOG_ENABLE + + + + VL53L1_print_static_nvm_managed( + &(pdev->stat_nvm), + "data_init():pdev->lldata.stat_nvm.", + VL53L1_TRACE_MODULE_DATA_INIT); + + VL53L1_print_customer_nvm_managed( + &(pdev->customer), + "data_init():pdev->lldata.customer.", + VL53L1_TRACE_MODULE_DATA_INIT); + + VL53L1_print_nvm_copy_data( + &(pdev->nvm_copy_data), + "data_init():pdev->lldata.nvm_copy_data.", + VL53L1_TRACE_MODULE_DATA_INIT); + + VL53L1_print_dmax_calibration_data( + &(pdev->fmt_dmax_cal), + "data_init():pdev->lldata.fmt_dmax_cal.", + VL53L1_TRACE_MODULE_DATA_INIT); + + VL53L1_print_dmax_calibration_data( + &(pdev->cust_dmax_cal), + "data_init():pdev->lldata.cust_dmax_cal.", + VL53L1_TRACE_MODULE_DATA_INIT); + + VL53L1_print_additional_offset_cal_data( + &(pdev->add_off_cal_data), + "data_init():pdev->lldata.add_off_cal_data.", + VL53L1_TRACE_MODULE_DATA_INIT); + + VL53L1_print_user_zone( + &(pdev->mm_roi), + "data_init():pdev->lldata.mm_roi.", + VL53L1_TRACE_MODULE_DATA_INIT); + + VL53L1_print_optical_centre( + &(pdev->optical_centre), + "data_init():pdev->lldata.optical_centre.", + VL53L1_TRACE_MODULE_DATA_INIT); + + VL53L1_print_cal_peak_rate_map( + &(pdev->cal_peak_rate_map), + "data_init():pdev->lldata.cal_peak_rate_map.", + VL53L1_TRACE_MODULE_DATA_INIT); + +#endif + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_read_p2p_data( + VL53L1_DEV Dev) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_hist_post_process_config_t *pHP = &(pdev->histpostprocess); + VL53L1_customer_nvm_managed_t *pN = &(pdev->customer); + VL53L1_additional_offset_cal_data_t *pCD = &(pdev->add_off_cal_data); + + VL53L1_decoded_nvm_fmt_range_data_t fmt_rrd; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_get_static_nvm_managed( + Dev, + &(pdev->stat_nvm)); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_get_customer_nvm_managed( + Dev, + &(pdev->customer)); + + if (status == VL53L1_ERROR_NONE) { + + status = VL53L1_get_nvm_copy_data( + Dev, + &(pdev->nvm_copy_data)); + + + if (status == VL53L1_ERROR_NONE) + VL53L1_copy_rtn_good_spads_to_buffer( + &(pdev->nvm_copy_data), + &(pdev->rtn_good_spads[0])); + } + + + + if (status == VL53L1_ERROR_NONE) { + pHP->algo__crosstalk_compensation_plane_offset_kcps = + pN->algo__crosstalk_compensation_plane_offset_kcps; + pHP->algo__crosstalk_compensation_x_plane_gradient_kcps = + pN->algo__crosstalk_compensation_x_plane_gradient_kcps; + pHP->algo__crosstalk_compensation_y_plane_gradient_kcps = + pN->algo__crosstalk_compensation_y_plane_gradient_kcps; + } + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_read_nvm_optical_centre( + Dev, + &(pdev->optical_centre)); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_read_nvm_cal_peak_rate_map( + Dev, + &(pdev->cal_peak_rate_map)); + + + + if (status == VL53L1_ERROR_NONE) { + + status = + VL53L1_read_nvm_additional_offset_cal_data( + Dev, + &(pdev->add_off_cal_data)); + + + + if (pCD->result__mm_inner_peak_signal_count_rtn_mcps == 0 && + pCD->result__mm_outer_peak_signal_count_rtn_mcps == 0) { + + pCD->result__mm_inner_peak_signal_count_rtn_mcps + = 0x0080; + pCD->result__mm_outer_peak_signal_count_rtn_mcps + = 0x0180; + + + + VL53L1_calc_mm_effective_spads( + pdev->nvm_copy_data.roi_config__mode_roi_centre_spad, + pdev->nvm_copy_data.roi_config__mode_roi_xy_size, + 0xC7, + 0xFF, + &(pdev->rtn_good_spads[0]), + VL53L1_RTN_SPAD_APERTURE_TRANSMISSION, + &(pCD->result__mm_inner_actual_effective_spads), + &(pCD->result__mm_outer_actual_effective_spads)); + } + } + + + if (status == VL53L1_ERROR_NONE) { + + status = + VL53L1_read_nvm_fmt_range_results_data( + Dev, + VL53L1_NVM__FMT__RANGE_RESULTS__140MM_DARK, + &fmt_rrd); + + if (status == VL53L1_ERROR_NONE) { + pdev->fmt_dmax_cal.ref__actual_effective_spads = + fmt_rrd.result__actual_effective_rtn_spads; + pdev->fmt_dmax_cal.ref__peak_signal_count_rate_mcps = + fmt_rrd.result__peak_signal_count_rate_rtn_mcps; + pdev->fmt_dmax_cal.ref__distance_mm = + fmt_rrd.measured_distance_mm; + + + if (pdev->cal_peak_rate_map.cal_reflectance_pc != 0) { + pdev->fmt_dmax_cal.ref_reflectance_pc = + pdev->cal_peak_rate_map.cal_reflectance_pc; + } else { + pdev->fmt_dmax_cal.ref_reflectance_pc = 0x0014; + } + + + pdev->fmt_dmax_cal.coverglass_transmission = 0x0100; + } + } + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_RdWord( + Dev, + VL53L1_RESULT__OSC_CALIBRATE_VAL, + &(pdev->dbg_results.result__osc_calibrate_val)); + + + + if (pdev->stat_nvm.osc_measured__fast_osc__frequency < 0x1000) { + trace_print( + VL53L1_TRACE_LEVEL_WARNING, + "\nInvalid %s value (0x%04X) - forcing to 0x%04X\n\n", + "pdev->stat_nvm.osc_measured__fast_osc__frequency", + pdev->stat_nvm.osc_measured__fast_osc__frequency, + 0xBCCC); + pdev->stat_nvm.osc_measured__fast_osc__frequency = 0xBCCC; + } + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_get_mode_mitigation_roi( + Dev, + &(pdev->mm_roi)); + + + + if (pdev->optical_centre.x_centre == 0 && + pdev->optical_centre.y_centre == 0) { + pdev->optical_centre.x_centre = + pdev->mm_roi.x_centre << 4; + pdev->optical_centre.y_centre = + pdev->mm_roi.y_centre << 4; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_software_reset( + VL53L1_DEV Dev) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_SOFT_RESET, + 0x00); + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WaitUs( + Dev, + VL53L1_SOFTWARE_RESET_DURATION_US); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_SOFT_RESET, + 0x01); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_wait_for_boot_completion(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_part_to_part_data( + VL53L1_DEV Dev, + VL53L1_calibration_data_t *pcal_data) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_xtalk_config_t *pC = &(pdev->xtalk_cfg); + VL53L1_hist_post_process_config_t *pHP = &(pdev->histpostprocess); + VL53L1_customer_nvm_managed_t *pN = &(pdev->customer); + + uint32_t tempu32; + + LOG_FUNCTION_START(""); + + if (pcal_data->struct_version != + VL53L1_LL_CALIBRATION_DATA_STRUCT_VERSION) { + status = VL53L1_ERROR_INVALID_PARAMS; + } + + if (status == VL53L1_ERROR_NONE) { + + + memcpy( + &(pdev->customer), + &(pcal_data->customer), + sizeof(VL53L1_customer_nvm_managed_t)); + + + memcpy( + &(pdev->add_off_cal_data), + &(pcal_data->add_off_cal_data), + sizeof(VL53L1_additional_offset_cal_data_t)); + + + memcpy( + &(pdev->fmt_dmax_cal), + &(pcal_data->fmt_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + + memcpy( + &(pdev->cust_dmax_cal), + &(pcal_data->cust_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + + memcpy( + &(pdev->xtalk_shapes), + &(pcal_data->xtalkhisto), + sizeof(VL53L1_xtalk_histogram_data_t)); + + + memcpy( + &(pdev->gain_cal), + &(pcal_data->gain_cal), + sizeof(VL53L1_gain_calibration_data_t)); + + + memcpy( + &(pdev->cal_peak_rate_map), + &(pcal_data->cal_peak_rate_map), + sizeof(VL53L1_cal_peak_rate_map_t)); + + + memcpy( + &(pdev->per_vcsel_cal_data), + &(pcal_data->per_vcsel_cal_data), + sizeof(VL53L1_per_vcsel_period_offset_cal_data_t)); + + + + pC->algo__crosstalk_compensation_plane_offset_kcps = + pN->algo__crosstalk_compensation_plane_offset_kcps; + pC->algo__crosstalk_compensation_x_plane_gradient_kcps = + pN->algo__crosstalk_compensation_x_plane_gradient_kcps; + pC->algo__crosstalk_compensation_y_plane_gradient_kcps = + pN->algo__crosstalk_compensation_y_plane_gradient_kcps; + + pHP->algo__crosstalk_compensation_plane_offset_kcps = + VL53L1_calc_crosstalk_plane_offset_with_margin( + pC->algo__crosstalk_compensation_plane_offset_kcps, + pC->histogram_mode_crosstalk_margin_kcps); + + pHP->algo__crosstalk_compensation_x_plane_gradient_kcps = + pC->algo__crosstalk_compensation_x_plane_gradient_kcps; + pHP->algo__crosstalk_compensation_y_plane_gradient_kcps = + pC->algo__crosstalk_compensation_y_plane_gradient_kcps; + + + + if (pC->global_crosstalk_compensation_enable == 0x00) { + pN->algo__crosstalk_compensation_plane_offset_kcps = + 0x00; + pN->algo__crosstalk_compensation_x_plane_gradient_kcps = + 0x00; + pN->algo__crosstalk_compensation_y_plane_gradient_kcps = + 0x00; + } else { + tempu32 = + VL53L1_calc_crosstalk_plane_offset_with_margin( + pC->algo__crosstalk_compensation_plane_offset_kcps, + pC->lite_mode_crosstalk_margin_kcps); + + + if (tempu32 > 0xFFFF) + tempu32 = 0xFFFF; + + pN->algo__crosstalk_compensation_plane_offset_kcps = + (uint16_t)tempu32; + } + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_part_to_part_data( + VL53L1_DEV Dev, + VL53L1_calibration_data_t *pcal_data) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_xtalk_config_t *pC = &(pdev->xtalk_cfg); + VL53L1_customer_nvm_managed_t *pCN = &(pcal_data->customer); + + LOG_FUNCTION_START(""); + + pcal_data->struct_version = + VL53L1_LL_CALIBRATION_DATA_STRUCT_VERSION; + + + memcpy( + &(pcal_data->customer), + &(pdev->customer), + sizeof(VL53L1_customer_nvm_managed_t)); + + + + + if (pC->algo__crosstalk_compensation_plane_offset_kcps > 0xFFFF) { + pCN->algo__crosstalk_compensation_plane_offset_kcps = + 0xFFFF; + } else { + pCN->algo__crosstalk_compensation_plane_offset_kcps = + (uint16_t)pC->algo__crosstalk_compensation_plane_offset_kcps; + } + pCN->algo__crosstalk_compensation_x_plane_gradient_kcps = + pC->algo__crosstalk_compensation_x_plane_gradient_kcps; + pCN->algo__crosstalk_compensation_y_plane_gradient_kcps = + pC->algo__crosstalk_compensation_y_plane_gradient_kcps; + + + memcpy( + &(pcal_data->fmt_dmax_cal), + &(pdev->fmt_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + + memcpy( + &(pcal_data->cust_dmax_cal), + &(pdev->cust_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + + memcpy( + &(pcal_data->add_off_cal_data), + &(pdev->add_off_cal_data), + sizeof(VL53L1_additional_offset_cal_data_t)); + + + memcpy( + &(pcal_data->optical_centre), + &(pdev->optical_centre), + sizeof(VL53L1_optical_centre_t)); + + + memcpy( + &(pcal_data->xtalkhisto), + &(pdev->xtalk_shapes), + sizeof(VL53L1_xtalk_histogram_data_t)); + + + memcpy( + &(pcal_data->gain_cal), + &(pdev->gain_cal), + sizeof(VL53L1_gain_calibration_data_t)); + + + memcpy( + &(pcal_data->cal_peak_rate_map), + &(pdev->cal_peak_rate_map), + sizeof(VL53L1_cal_peak_rate_map_t)); + + + memcpy( + &(pcal_data->per_vcsel_cal_data), + &(pdev->per_vcsel_cal_data), + sizeof(VL53L1_per_vcsel_period_offset_cal_data_t)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_inter_measurement_period_ms( + VL53L1_DEV Dev, + uint32_t inter_measurement_period_ms) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + if (pdev->dbg_results.result__osc_calibrate_val == 0) + status = VL53L1_ERROR_DIVISION_BY_ZERO; + + if (status == VL53L1_ERROR_NONE) { + pdev->inter_measurement_period_ms = inter_measurement_period_ms; + pdev->tim_cfg.system__intermeasurement_period = + inter_measurement_period_ms * + (uint32_t)pdev->dbg_results.result__osc_calibrate_val; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_inter_measurement_period_ms( + VL53L1_DEV Dev, + uint32_t *pinter_measurement_period_ms) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + if (pdev->dbg_results.result__osc_calibrate_val == 0) + status = VL53L1_ERROR_DIVISION_BY_ZERO; + + if (status == VL53L1_ERROR_NONE) + *pinter_measurement_period_ms = + pdev->tim_cfg.system__intermeasurement_period / + (uint32_t)pdev->dbg_results.result__osc_calibrate_val; + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_timeouts_us( + VL53L1_DEV Dev, + uint32_t phasecal_config_timeout_us, + uint32_t mm_config_timeout_us, + uint32_t range_config_timeout_us) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + if (pdev->stat_nvm.osc_measured__fast_osc__frequency == 0) + status = VL53L1_ERROR_DIVISION_BY_ZERO; + + if (status == VL53L1_ERROR_NONE) { + + pdev->phasecal_config_timeout_us = phasecal_config_timeout_us; + pdev->mm_config_timeout_us = mm_config_timeout_us; + pdev->range_config_timeout_us = range_config_timeout_us; + + status = + VL53L1_calc_timeout_register_values( + phasecal_config_timeout_us, + mm_config_timeout_us, + range_config_timeout_us, + pdev->stat_nvm.osc_measured__fast_osc__frequency, + &(pdev->gen_cfg), + &(pdev->tim_cfg)); + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_timeouts_us( + VL53L1_DEV Dev, + uint32_t *pphasecal_config_timeout_us, + uint32_t *pmm_config_timeout_us, + uint32_t *prange_config_timeout_us) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + uint32_t macro_period_us = 0; + uint16_t timeout_encoded = 0; + + LOG_FUNCTION_START(""); + + if (pdev->stat_nvm.osc_measured__fast_osc__frequency == 0) + status = VL53L1_ERROR_DIVISION_BY_ZERO; + + if (status == VL53L1_ERROR_NONE) { + + + macro_period_us = + VL53L1_calc_macro_period_us( + pdev->stat_nvm.osc_measured__fast_osc__frequency, + pdev->tim_cfg.range_config__vcsel_period_a); + + + + *pphasecal_config_timeout_us = + VL53L1_calc_timeout_us( + (uint32_t)pdev->gen_cfg.phasecal_config__timeout_macrop, + macro_period_us); + + + + timeout_encoded = + (uint16_t)pdev->tim_cfg.mm_config__timeout_macrop_a_hi; + timeout_encoded = (timeout_encoded << 8) + + (uint16_t)pdev->tim_cfg.mm_config__timeout_macrop_a_lo; + + *pmm_config_timeout_us = + VL53L1_calc_decoded_timeout_us( + timeout_encoded, + macro_period_us); + + + + timeout_encoded = + (uint16_t)pdev->tim_cfg.range_config__timeout_macrop_a_hi; + timeout_encoded = (timeout_encoded << 8) + + (uint16_t)pdev->tim_cfg.range_config__timeout_macrop_a_lo; + + *prange_config_timeout_us = + VL53L1_calc_decoded_timeout_us( + timeout_encoded, + macro_period_us); + + pdev->phasecal_config_timeout_us = *pphasecal_config_timeout_us; + pdev->mm_config_timeout_us = *pmm_config_timeout_us; + pdev->range_config_timeout_us = *prange_config_timeout_us; + + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_calibration_repeat_period( + VL53L1_DEV Dev, + uint16_t cal_config__repeat_period) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + pdev->gen_cfg.cal_config__repeat_rate = cal_config__repeat_period; + + return status; + +} + + +VL53L1_Error VL53L1_get_calibration_repeat_period( + VL53L1_DEV Dev, + uint16_t *pcal_config__repeat_period) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + *pcal_config__repeat_period = pdev->gen_cfg.cal_config__repeat_rate; + + return status; + +} + + +VL53L1_Error VL53L1_set_sequence_config_bit( + VL53L1_DEV Dev, + VL53L1_DeviceSequenceConfig bit_id, + uint8_t value) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t bit_mask = 0x01; + uint8_t clr_mask = 0xFF - bit_mask; + uint8_t bit_value = value & bit_mask; + + if (bit_id <= VL53L1_DEVICESEQUENCECONFIG_RANGE) { + + if (bit_id > 0) { + bit_mask = 0x01 << bit_id; + bit_value = bit_value << bit_id; + clr_mask = 0xFF - bit_mask; + } + + pdev->dyn_cfg.system__sequence_config = + (pdev->dyn_cfg.system__sequence_config & clr_mask) | + bit_value; + + } else { + status = VL53L1_ERROR_INVALID_PARAMS; + } + + return status; + +} + + +VL53L1_Error VL53L1_get_sequence_config_bit( + VL53L1_DEV Dev, + VL53L1_DeviceSequenceConfig bit_id, + uint8_t *pvalue) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t bit_mask = 0x01; + + if (bit_id <= VL53L1_DEVICESEQUENCECONFIG_RANGE) { + + if (bit_id > 0) + bit_mask = 0x01 << bit_id; + + *pvalue = + pdev->dyn_cfg.system__sequence_config & bit_mask; + + if (bit_id > 0) + *pvalue = *pvalue >> bit_id; + + } else { + status = VL53L1_ERROR_INVALID_PARAMS; + } + + return status; +} + + +VL53L1_Error VL53L1_set_interrupt_polarity( + VL53L1_DEV Dev, + VL53L1_DeviceInterruptPolarity interrupt_polarity) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + pdev->stat_cfg.gpio_hv_mux__ctrl = + (pdev->stat_cfg.gpio_hv_mux__ctrl & + VL53L1_DEVICEINTERRUPTPOLARITY_CLEAR_MASK) | + (interrupt_polarity & + VL53L1_DEVICEINTERRUPTPOLARITY_BIT_MASK); + + return status; + +} + + +VL53L1_Error VL53L1_set_refspadchar_config_struct( + VL53L1_DEV Dev, + VL53L1_refspadchar_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->refspadchar.device_test_mode = pdata->device_test_mode; + pdev->refspadchar.VL53L1_p_009 = pdata->VL53L1_p_009; + pdev->refspadchar.timeout_us = pdata->timeout_us; + pdev->refspadchar.target_count_rate_mcps = + pdata->target_count_rate_mcps; + pdev->refspadchar.min_count_rate_limit_mcps = + pdata->min_count_rate_limit_mcps; + pdev->refspadchar.max_count_rate_limit_mcps = + pdata->max_count_rate_limit_mcps; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_get_refspadchar_config_struct( + VL53L1_DEV Dev, + VL53L1_refspadchar_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdata->device_test_mode = pdev->refspadchar.device_test_mode; + pdata->VL53L1_p_009 = pdev->refspadchar.VL53L1_p_009; + pdata->timeout_us = pdev->refspadchar.timeout_us; + pdata->target_count_rate_mcps = + pdev->refspadchar.target_count_rate_mcps; + pdata->min_count_rate_limit_mcps = + pdev->refspadchar.min_count_rate_limit_mcps; + pdata->max_count_rate_limit_mcps = + pdev->refspadchar.max_count_rate_limit_mcps; + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_set_range_ignore_threshold( + VL53L1_DEV Dev, + uint8_t range_ignore_thresh_mult, + uint16_t range_ignore_threshold_mcps) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + pdev->xtalk_cfg.crosstalk_range_ignore_threshold_rate_mcps = + range_ignore_threshold_mcps; + + pdev->xtalk_cfg.crosstalk_range_ignore_threshold_mult = + range_ignore_thresh_mult; + + return status; + +} + +VL53L1_Error VL53L1_get_range_ignore_threshold( + VL53L1_DEV Dev, + uint8_t *prange_ignore_thresh_mult, + uint16_t *prange_ignore_threshold_mcps_internal, + uint16_t *prange_ignore_threshold_mcps_current) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + *prange_ignore_thresh_mult = + pdev->xtalk_cfg.crosstalk_range_ignore_threshold_mult; + + *prange_ignore_threshold_mcps_current = + pdev->stat_cfg.algo__range_ignore_threshold_mcps; + + *prange_ignore_threshold_mcps_internal = + pdev->xtalk_cfg.crosstalk_range_ignore_threshold_rate_mcps; + + return status; + +} + + + +VL53L1_Error VL53L1_get_interrupt_polarity( + VL53L1_DEV Dev, + VL53L1_DeviceInterruptPolarity *pinterrupt_polarity) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + *pinterrupt_polarity = + pdev->stat_cfg.gpio_hv_mux__ctrl & + VL53L1_DEVICEINTERRUPTPOLARITY_BIT_MASK; + + return status; + +} + + +VL53L1_Error VL53L1_set_user_zone( + VL53L1_DEV Dev, + VL53L1_user_zone_t *puser_zone) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + VL53L1_encode_row_col( + puser_zone->y_centre, + puser_zone->x_centre, + &(pdev->dyn_cfg.roi_config__user_roi_centre_spad)); + + + VL53L1_encode_zone_size( + puser_zone->width, + puser_zone->height, + &(pdev->dyn_cfg.roi_config__user_roi_requested_global_xy_size)); + + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_user_zone( + VL53L1_DEV Dev, + VL53L1_user_zone_t *puser_zone) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + VL53L1_decode_row_col( + pdev->dyn_cfg.roi_config__user_roi_centre_spad, + &(puser_zone->y_centre), + &(puser_zone->x_centre)); + + + VL53L1_decode_zone_size( + pdev->dyn_cfg.roi_config__user_roi_requested_global_xy_size, + &(puser_zone->width), + &(puser_zone->height)); + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_get_mode_mitigation_roi( + VL53L1_DEV Dev, + VL53L1_user_zone_t *pmm_roi) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t x = 0; + uint8_t y = 0; + uint8_t xy_size = 0; + + LOG_FUNCTION_START(""); + + + VL53L1_decode_row_col( + pdev->nvm_copy_data.roi_config__mode_roi_centre_spad, + &y, + &x); + + pmm_roi->x_centre = x; + pmm_roi->y_centre = y; + + + xy_size = pdev->nvm_copy_data.roi_config__mode_roi_xy_size; + + pmm_roi->height = xy_size >> 4; + pmm_roi->width = xy_size & 0x0F; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_zone_config( + VL53L1_DEV Dev, + VL53L1_zone_config_t *pzone_cfg) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + memcpy(&(pdev->zone_cfg.user_zones), &(pzone_cfg->user_zones), + sizeof(pdev->zone_cfg.user_zones)); + + + pdev->zone_cfg.max_zones = pzone_cfg->max_zones; + pdev->zone_cfg.active_zones = pzone_cfg->active_zones; + + status = VL53L1_init_zone_config_histogram_bins(&pdev->zone_cfg); + + + + if (pzone_cfg->active_zones == 0) + pdev->gen_cfg.global_config__stream_divider = 0; + else if (pzone_cfg->active_zones < VL53L1_MAX_USER_ZONES) + pdev->gen_cfg.global_config__stream_divider = + pzone_cfg->active_zones + 1; + else + pdev->gen_cfg.global_config__stream_divider = + VL53L1_MAX_USER_ZONES + 1; + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_get_zone_config( + VL53L1_DEV Dev, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + memcpy(pzone_cfg, &(pdev->zone_cfg), sizeof(VL53L1_zone_config_t)); + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_get_preset_mode_timing_cfg( + VL53L1_DEV Dev, + VL53L1_DevicePresetModes device_preset_mode, + uint16_t *pdss_config__target_total_rate_mcps, + uint32_t *pphasecal_config_timeout_us, + uint32_t *pmm_config_timeout_us, + uint32_t *prange_config_timeout_us) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + switch (device_preset_mode) { + + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING: + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_SHORT_RANGE: + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_LONG_RANGE: + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_MM1_CAL: + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_MM2_CAL: + case VL53L1_DEVICEPRESETMODE_OLT: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_lite_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_lite_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_lite_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_lite_us; + break; + + case VL53L1_DEVICEPRESETMODE_TIMED_RANGING: + case VL53L1_DEVICEPRESETMODE_TIMED_RANGING_SHORT_RANGE: + case VL53L1_DEVICEPRESETMODE_TIMED_RANGING_LONG_RANGE: + case VL53L1_DEVICEPRESETMODE_SINGLESHOT_RANGING: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_timed_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_timed_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_timed_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_timed_us; + break; + + case VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_SHORT_RANGE: + case VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_MEDIUM_RANGE: + case VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_LONG_RANGE: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_timed_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_timed_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_lpa_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_lpa_us; + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_WITH_MM1: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_WITH_MM2: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_MM1_CAL: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_MM2_CAL: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_REF_ARRAY: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE_MM1: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE_MM2: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_CHARACTERISATION: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_histo_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_hist_long_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_histo_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_histo_us; + + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_histo_mz_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_mz_med_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_mz_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_mz_us; + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE_SHORT_RANGE: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_histo_mz_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_mz_short_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_mz_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_mz_us; + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE_LONG_RANGE: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_histo_mz_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_mz_long_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_mz_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_mz_us; + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_SHORT_TIMING: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_histo_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_hist_short_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_histo_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_histo_us; + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE_MM1: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE_MM2: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_histo_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_hist_med_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_histo_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_histo_us; + break; + + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE_MM1: + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE_MM2: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_histo_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_hist_short_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_histo_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_histo_us; + break; + + case VL53L1_DEVICEPRESETMODE_SPECIAL_HISTOGRAM_SHORT_RANGE: + *pdss_config__target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_very_short_mcps; + *pphasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_hist_short_us; + *pmm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_histo_us; + *prange_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_histo_us; + break; + + default: + status = VL53L1_ERROR_INVALID_PARAMS; + break; + + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_preset_mode( + VL53L1_DEV Dev, + VL53L1_DevicePresetModes device_preset_mode, + uint16_t dss_config__target_total_rate_mcps, + uint32_t phasecal_config_timeout_us, + uint32_t mm_config_timeout_us, + uint32_t range_config_timeout_us, + uint32_t inter_measurement_period_ms) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + VL53L1_hist_post_process_config_t *phistpostprocess = + &(pdev->histpostprocess); + + VL53L1_static_config_t *pstatic = &(pdev->stat_cfg); + VL53L1_histogram_config_t *phistogram = &(pdev->hist_cfg); + VL53L1_general_config_t *pgeneral = &(pdev->gen_cfg); + VL53L1_timing_config_t *ptiming = &(pdev->tim_cfg); + VL53L1_dynamic_config_t *pdynamic = &(pdev->dyn_cfg); + VL53L1_system_control_t *psystem = &(pdev->sys_ctrl); + VL53L1_zone_config_t *pzone_cfg = &(pdev->zone_cfg); + VL53L1_tuning_parm_storage_t *ptuning_parms = &(pdev->tuning_parms); + VL53L1_low_power_auto_data_t *plpadata = + &(pdev->low_power_auto_data); + + LOG_FUNCTION_START(""); + + + pdev->preset_mode = device_preset_mode; + pdev->mm_config_timeout_us = mm_config_timeout_us; + pdev->range_config_timeout_us = range_config_timeout_us; + pdev->inter_measurement_period_ms = inter_measurement_period_ms; + + + + VL53L1_init_ll_driver_state( + Dev, + VL53L1_DEVICESTATE_SW_STANDBY); + + + + switch (device_preset_mode) { + + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING: + status = VL53L1_preset_mode_standard_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_SHORT_RANGE: + status = VL53L1_preset_mode_standard_ranging_short_range( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_LONG_RANGE: + status = VL53L1_preset_mode_standard_ranging_long_range( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_MM1_CAL: + status = VL53L1_preset_mode_standard_ranging_mm1_cal( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_STANDARD_RANGING_MM2_CAL: + status = VL53L1_preset_mode_standard_ranging_mm2_cal( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_TIMED_RANGING: + status = VL53L1_preset_mode_timed_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_TIMED_RANGING_SHORT_RANGE: + status = VL53L1_preset_mode_timed_ranging_short_range( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_TIMED_RANGING_LONG_RANGE: + status = VL53L1_preset_mode_timed_ranging_long_range( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING: + status = VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_WITH_MM1: + status = VL53L1_preset_mode_histogram_ranging_with_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_WITH_MM2: + status = VL53L1_preset_mode_histogram_ranging_with_mm2( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_MM1_CAL: + status = VL53L1_preset_mode_histogram_ranging_mm1_cal( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_MM2_CAL: + status = VL53L1_preset_mode_histogram_ranging_mm2_cal( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE: + status = VL53L1_preset_mode_histogram_multizone( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE_SHORT_RANGE: + status = VL53L1_preset_mode_histogram_multizone_short_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MULTIZONE_LONG_RANGE: + status = VL53L1_preset_mode_histogram_multizone_long_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_REF_ARRAY: + status = VL53L1_preset_mode_histogram_ranging_ref( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_RANGING_SHORT_TIMING: + status = VL53L1_preset_mode_histogram_ranging_short_timing( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE: + status = VL53L1_preset_mode_histogram_long_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE_MM1: + status = VL53L1_preset_mode_histogram_long_range_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_LONG_RANGE_MM2: + status = VL53L1_preset_mode_histogram_long_range_mm2( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE: + status = VL53L1_preset_mode_histogram_medium_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE_MM1: + status = VL53L1_preset_mode_histogram_medium_range_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_MEDIUM_RANGE_MM2: + status = VL53L1_preset_mode_histogram_medium_range_mm2( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE: + status = VL53L1_preset_mode_histogram_short_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE_MM1: + status = VL53L1_preset_mode_histogram_short_range_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_SHORT_RANGE_MM2: + status = VL53L1_preset_mode_histogram_short_range_mm2( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_CHARACTERISATION: + status = VL53L1_preset_mode_histogram_characterisation( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_XTALK_PLANAR: + status = VL53L1_preset_mode_histogram_xtalk_planar( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_XTALK_MM1: + status = VL53L1_preset_mode_histogram_xtalk_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_HISTOGRAM_XTALK_MM2: + status = VL53L1_preset_mode_histogram_xtalk_mm2( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_OLT: + status = VL53L1_preset_mode_olt( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_SINGLESHOT_RANGING: + status = VL53L1_preset_mode_singleshot_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + case VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_SHORT_RANGE: + status = VL53L1_preset_mode_low_power_auto_short_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg, + plpadata); + break; + + case VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_MEDIUM_RANGE: + status = VL53L1_preset_mode_low_power_auto_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg, + plpadata); + break; + + case VL53L1_DEVICEPRESETMODE_LOWPOWERAUTO_LONG_RANGE: + status = VL53L1_preset_mode_low_power_auto_long_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg, + plpadata); + break; + + + case VL53L1_DEVICEPRESETMODE_SPECIAL_HISTOGRAM_SHORT_RANGE: + status = VL53L1_preset_mode_special_histogram_short_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + break; + + default: + status = VL53L1_ERROR_INVALID_PARAMS; + break; + + } + + + + if (status == VL53L1_ERROR_NONE) { + + pstatic->dss_config__target_total_rate_mcps = + dss_config__target_total_rate_mcps; + pdev->dss_config__target_total_rate_mcps = + dss_config__target_total_rate_mcps; + + } + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_timeouts_us( + Dev, + phasecal_config_timeout_us, + mm_config_timeout_us, + range_config_timeout_us); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_inter_measurement_period_ms( + Dev, + inter_measurement_period_ms); + + + + V53L1_init_zone_results_structure( + pdev->zone_cfg.active_zones+1, + &(pres->zone_results)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_zone_preset( + VL53L1_DEV Dev, + VL53L1_DeviceZonePreset zone_preset) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + VL53L1_general_config_t *pgeneral = &(pdev->gen_cfg); + VL53L1_zone_config_t *pzone_cfg = &(pdev->zone_cfg); + + LOG_FUNCTION_START(""); + + + pdev->zone_preset = zone_preset; + + + + switch (zone_preset) { + + case VL53L1_DEVICEZONEPRESET_XTALK_PLANAR: + status = + VL53L1_zone_preset_xtalk_planar( + pgeneral, + pzone_cfg); + break; + + case VL53L1_DEVICEZONEPRESET_1X1_SIZE_16X16: + status = + VL53L1_init_zone_config_structure( + 8, 1, 1, + 8, 1, 1, + 15, 15, + pzone_cfg); + break; + + case VL53L1_DEVICEZONEPRESET_1X2_SIZE_16X8: + status = + VL53L1_init_zone_config_structure( + 8, 1, 1, + 4, 8, 2, + 15, 7, + pzone_cfg); + break; + + case VL53L1_DEVICEZONEPRESET_2X1_SIZE_8X16: + status = + VL53L1_init_zone_config_structure( + 4, 8, 2, + 8, 1, 1, + 7, 15, + pzone_cfg); + break; + + case VL53L1_DEVICEZONEPRESET_2X2_SIZE_8X8: + status = + VL53L1_init_zone_config_structure( + 4, 8, 2, + 4, 8, 2, + 7, 7, + pzone_cfg); + break; + + case VL53L1_DEVICEZONEPRESET_3X3_SIZE_5X5: + status = + VL53L1_init_zone_config_structure( + 2, 5, 3, + 2, 5, 3, + 4, 4, + pzone_cfg); + break; + + case VL53L1_DEVICEZONEPRESET_4X4_SIZE_4X4: + status = + VL53L1_init_zone_config_structure( + 2, 4, 4, + 2, 4, 4, + 3, 3, + pzone_cfg); + break; + + case VL53L1_DEVICEZONEPRESET_5X5_SIZE_4X4: + status = + VL53L1_init_zone_config_structure( + 2, 3, 5, + 2, 3, 5, + 3, 3, + pzone_cfg); + break; + + case VL53L1_DEVICEZONEPRESET_11X11_SIZE_5X5: + status = + VL53L1_init_zone_config_structure( + 3, 1, 11, + 3, 1, 11, + 4, 4, + pzone_cfg); + break; + + case VL53L1_DEVICEZONEPRESET_13X13_SIZE_4X4: + status = + VL53L1_init_zone_config_structure( + 2, 1, 13, + 2, 1, 13, + 3, 3, + pzone_cfg); + + break; + + case VL53L1_DEVICEZONEPRESET_1X1_SIZE_4X4_POS_8X8: + status = + VL53L1_init_zone_config_structure( + 8, 1, 1, + 8, 1, 1, + 3, 3, + pzone_cfg); + break; + + } + + + + if (pzone_cfg->active_zones == 0) + pdev->gen_cfg.global_config__stream_divider = 0; + else if (pzone_cfg->active_zones < VL53L1_MAX_USER_ZONES) + pdev->gen_cfg.global_config__stream_divider = + pzone_cfg->active_zones + 1; + else + pdev->gen_cfg.global_config__stream_divider = + VL53L1_MAX_USER_ZONES + 1; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_enable_xtalk_compensation( + VL53L1_DEV Dev) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint32_t tempu32; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_xtalk_config_t *pC = &(pdev->xtalk_cfg); + VL53L1_hist_post_process_config_t *pHP = &(pdev->histpostprocess); + VL53L1_customer_nvm_managed_t *pN = &(pdev->customer); + + LOG_FUNCTION_START(""); + + + tempu32 = VL53L1_calc_crosstalk_plane_offset_with_margin( + pC->algo__crosstalk_compensation_plane_offset_kcps, + pC->lite_mode_crosstalk_margin_kcps); + if (tempu32 > 0xFFFF) + tempu32 = 0xFFFF; + + pN->algo__crosstalk_compensation_plane_offset_kcps = + (uint16_t)tempu32; + + pN->algo__crosstalk_compensation_x_plane_gradient_kcps = + pC->algo__crosstalk_compensation_x_plane_gradient_kcps; + + pN->algo__crosstalk_compensation_y_plane_gradient_kcps = + pC->algo__crosstalk_compensation_y_plane_gradient_kcps; + + + pHP->algo__crosstalk_compensation_plane_offset_kcps = + VL53L1_calc_crosstalk_plane_offset_with_margin( + pC->algo__crosstalk_compensation_plane_offset_kcps, + pC->histogram_mode_crosstalk_margin_kcps); + + pHP->algo__crosstalk_compensation_x_plane_gradient_kcps + = pC->algo__crosstalk_compensation_x_plane_gradient_kcps; + pHP->algo__crosstalk_compensation_y_plane_gradient_kcps + = pC->algo__crosstalk_compensation_y_plane_gradient_kcps; + + + + pC->global_crosstalk_compensation_enable = 0x01; + + pHP->algo__crosstalk_compensation_enable = + pC->global_crosstalk_compensation_enable; + + + + + if (status == VL53L1_ERROR_NONE) { + pC->crosstalk_range_ignore_threshold_rate_mcps = + VL53L1_calc_range_ignore_threshold( + pC->algo__crosstalk_compensation_plane_offset_kcps, + pC->algo__crosstalk_compensation_x_plane_gradient_kcps, + pC->algo__crosstalk_compensation_y_plane_gradient_kcps, + pC->crosstalk_range_ignore_threshold_mult); +} + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_set_customer_nvm_managed( + Dev, + &(pdev->customer)); + + LOG_FUNCTION_END(status); + + return status; + +} + +void VL53L1_get_xtalk_compensation_enable( + VL53L1_DEV Dev, + uint8_t *pcrosstalk_compensation_enable) +{ + + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + *pcrosstalk_compensation_enable = + pdev->xtalk_cfg.global_crosstalk_compensation_enable; + +} + + +VL53L1_Error VL53L1_get_lite_xtalk_margin_kcps( + VL53L1_DEV Dev, + int16_t *pxtalk_margin) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *pxtalk_margin = pdev->xtalk_cfg.lite_mode_crosstalk_margin_kcps; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_lite_xtalk_margin_kcps( + VL53L1_DEV Dev, + int16_t xtalk_margin) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->xtalk_cfg.lite_mode_crosstalk_margin_kcps = xtalk_margin; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_histogram_xtalk_margin_kcps( + VL53L1_DEV Dev, + int16_t *pxtalk_margin) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *pxtalk_margin = pdev->xtalk_cfg.histogram_mode_crosstalk_margin_kcps; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_histogram_xtalk_margin_kcps( + VL53L1_DEV Dev, + int16_t xtalk_margin) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->xtalk_cfg.histogram_mode_crosstalk_margin_kcps = xtalk_margin; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_restore_xtalk_nvm_default( + VL53L1_DEV Dev) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_xtalk_config_t *pC = &(pdev->xtalk_cfg); + + LOG_FUNCTION_START(""); + + pC->algo__crosstalk_compensation_plane_offset_kcps = + pC->nvm_default__crosstalk_compensation_plane_offset_kcps; + pC->algo__crosstalk_compensation_x_plane_gradient_kcps = + pC->nvm_default__crosstalk_compensation_x_plane_gradient_kcps; + pC->algo__crosstalk_compensation_y_plane_gradient_kcps = + pC->nvm_default__crosstalk_compensation_y_plane_gradient_kcps; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_disable_xtalk_compensation( + VL53L1_DEV Dev) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_hist_post_process_config_t *pHP = &(pdev->histpostprocess); + VL53L1_customer_nvm_managed_t *pN = &(pdev->customer); + + LOG_FUNCTION_START(""); + + + pN->algo__crosstalk_compensation_plane_offset_kcps = + 0x00; + + pN->algo__crosstalk_compensation_x_plane_gradient_kcps = + 0x00; + + pN->algo__crosstalk_compensation_y_plane_gradient_kcps = + 0x00; + + + + pdev->xtalk_cfg.global_crosstalk_compensation_enable = 0x00; + + pHP->algo__crosstalk_compensation_enable = + pdev->xtalk_cfg.global_crosstalk_compensation_enable; + + + + if (status == VL53L1_ERROR_NONE) { + pdev->xtalk_cfg.crosstalk_range_ignore_threshold_rate_mcps = + 0x0000; + } + + + + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_set_customer_nvm_managed( + Dev, + &(pdev->customer)); + } + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_get_histogram_phase_consistency( + VL53L1_DEV Dev, + uint8_t *pphase_consistency) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_hist_post_process_config_t *pHP = &(pdev->histpostprocess); + + LOG_FUNCTION_START(""); + + *pphase_consistency = + pHP->algo__consistency_check__phase_tolerance; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_histogram_phase_consistency( + VL53L1_DEV Dev, + uint8_t phase_consistency) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->histpostprocess.algo__consistency_check__phase_tolerance = + phase_consistency; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_get_histogram_event_consistency( + VL53L1_DEV Dev, + uint8_t *pevent_consistency) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *pevent_consistency = + pdev->histpostprocess.algo__consistency_check__event_sigma; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_histogram_event_consistency( + VL53L1_DEV Dev, + uint8_t event_consistency) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->histpostprocess.algo__consistency_check__event_sigma = + event_consistency; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_get_histogram_ambient_threshold_sigma( + VL53L1_DEV Dev, + uint8_t *pamb_thresh_sigma) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *pamb_thresh_sigma = + pdev->histpostprocess.ambient_thresh_sigma1; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_histogram_ambient_threshold_sigma( + VL53L1_DEV Dev, + uint8_t amb_thresh_sigma) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->histpostprocess.ambient_thresh_sigma1 = + amb_thresh_sigma; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_get_lite_sigma_threshold( + VL53L1_DEV Dev, + uint16_t *plite_sigma) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *plite_sigma = + pdev->tim_cfg.range_config__sigma_thresh; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_lite_sigma_threshold( + VL53L1_DEV Dev, + uint16_t lite_sigma) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->tim_cfg.range_config__sigma_thresh = lite_sigma; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_get_lite_min_count_rate( + VL53L1_DEV Dev, + uint16_t *plite_mincountrate) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *plite_mincountrate = + pdev->tim_cfg.range_config__min_count_rate_rtn_limit_mcps; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_lite_min_count_rate( + VL53L1_DEV Dev, + uint16_t lite_mincountrate) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->tim_cfg.range_config__min_count_rate_rtn_limit_mcps = + lite_mincountrate; + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_get_xtalk_detect_config( + VL53L1_DEV Dev, + int16_t *pmax_valid_range_mm, + int16_t *pmin_valid_range_mm, + uint16_t *pmax_valid_rate_kcps, + uint16_t *pmax_sigma_mm) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *pmax_valid_range_mm = + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_range_mm; + *pmin_valid_range_mm = + pdev->xtalk_cfg.algo__crosstalk_detect_min_valid_range_mm; + *pmax_valid_rate_kcps = + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_rate_kcps; + *pmax_sigma_mm = + pdev->xtalk_cfg.algo__crosstalk_detect_max_sigma_mm; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_xtalk_detect_config( + VL53L1_DEV Dev, + int16_t max_valid_range_mm, + int16_t min_valid_range_mm, + uint16_t max_valid_rate_kcps, + uint16_t max_sigma_mm) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_range_mm = + max_valid_range_mm; + pdev->xtalk_cfg.algo__crosstalk_detect_min_valid_range_mm = + min_valid_range_mm; + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_rate_kcps = + max_valid_rate_kcps; + pdev->xtalk_cfg.algo__crosstalk_detect_max_sigma_mm = + max_sigma_mm; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_get_target_order_mode( + VL53L1_DEV Dev, + VL53L1_HistTargetOrder *phist_target_order) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *phist_target_order = + pdev->histpostprocess.hist_target_order; + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_target_order_mode( + VL53L1_DEV Dev, + VL53L1_HistTargetOrder hist_target_order) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->histpostprocess.hist_target_order = hist_target_order; + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_get_dmax_reflectance_values( + VL53L1_DEV Dev, + VL53L1_dmax_reflectance_array_t *pdmax_reflectances) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i = 0; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + for (i = 0; i < VL53L1_MAX_AMBIENT_DMAX_VALUES; i++) { + pdmax_reflectances->target_reflectance_for_dmax[i] = + pdev->dmax_cfg.target_reflectance_for_dmax_calc[i]; + } + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_set_dmax_reflectance_values( + VL53L1_DEV Dev, + VL53L1_dmax_reflectance_array_t *pdmax_reflectances) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i = 0; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + for (i = 0; i < VL53L1_MAX_AMBIENT_DMAX_VALUES; i++) { + pdev->dmax_cfg.target_reflectance_for_dmax_calc[i] = + pdmax_reflectances->target_reflectance_for_dmax[i]; + } + + LOG_FUNCTION_END(status); + + return status; + +} + +VL53L1_Error VL53L1_get_vhv_loopbound( + VL53L1_DEV Dev, + uint8_t *pvhv_loopbound) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *pvhv_loopbound = + pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound / 4; + + LOG_FUNCTION_END(status); + + return status; + +} + + + +VL53L1_Error VL53L1_set_vhv_loopbound( + VL53L1_DEV Dev, + uint8_t vhv_loopbound) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound = + (pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound & 0x03) + + (vhv_loopbound * 4); + + LOG_FUNCTION_END(status); + + return status; + +} + + + +VL53L1_Error VL53L1_get_vhv_config( + VL53L1_DEV Dev, + uint8_t *pvhv_init_en, + uint8_t *pvhv_init_value) +{ + + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *pvhv_init_en = (pdev->stat_nvm.vhv_config__init & 0x80) >> 7; + *pvhv_init_value = + (pdev->stat_nvm.vhv_config__init & 0x7F); + + LOG_FUNCTION_END(status); + + return status; + +} + + + +VL53L1_Error VL53L1_set_vhv_config( + VL53L1_DEV Dev, + uint8_t vhv_init_en, + uint8_t vhv_init_value) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->stat_nvm.vhv_config__init = + ((vhv_init_en & 0x01) << 7) + + (vhv_init_value & 0x7F); + + LOG_FUNCTION_END(status); + + return status; + +} + + + +VL53L1_Error VL53L1_init_and_start_range( + VL53L1_DEV Dev, + uint8_t measurement_mode, + VL53L1_DeviceConfigLevel device_config_level) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + uint8_t buffer[VL53L1_MAX_I2C_XFER_SIZE]; + + VL53L1_static_nvm_managed_t *pstatic_nvm = &(pdev->stat_nvm); + VL53L1_customer_nvm_managed_t *pcustomer_nvm = &(pdev->customer); + VL53L1_static_config_t *pstatic = &(pdev->stat_cfg); + VL53L1_general_config_t *pgeneral = &(pdev->gen_cfg); + VL53L1_timing_config_t *ptiming = &(pdev->tim_cfg); + VL53L1_dynamic_config_t *pdynamic = &(pdev->dyn_cfg); + VL53L1_system_control_t *psystem = &(pdev->sys_ctrl); + + VL53L1_ll_driver_state_t *pstate = &(pdev->ll_state); + VL53L1_customer_nvm_managed_t *pN = &(pdev->customer); + + uint8_t *pbuffer = &buffer[0]; + uint16_t i = 0; + uint16_t i2c_index = 0; + uint16_t i2c_buffer_offset_bytes = 0; + uint16_t i2c_buffer_size_bytes = 0; + + LOG_FUNCTION_START(""); + + + pdev->measurement_mode = measurement_mode; + + + + psystem->system__mode_start = + (psystem->system__mode_start & + VL53L1_DEVICEMEASUREMENTMODE_STOP_MASK) | + measurement_mode; + + + + status = + VL53L1_set_user_zone( + Dev, + &(pdev->zone_cfg.user_zones[pdev->ll_state.cfg_zone_id])); + + + if (pdev->zone_cfg.active_zones > 0) { + status = + VL53L1_set_zone_dss_config( + Dev, + &(pres->zone_dyn_cfgs.VL53L1_p_002[pdev->ll_state.cfg_zone_id]) + ); + } + + + + + if (((pdev->sys_ctrl.system__mode_start & + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM) == 0x00) && + (pdev->xtalk_cfg.global_crosstalk_compensation_enable + == 0x01)) { + pdev->stat_cfg.algo__range_ignore_threshold_mcps = + pdev->xtalk_cfg.crosstalk_range_ignore_threshold_rate_mcps; + } + + + + + + if (pdev->low_power_auto_data.low_power_auto_range_count == 0xFF) + pdev->low_power_auto_data.low_power_auto_range_count = 0x0; + + + if ((pdev->low_power_auto_data.is_low_power_auto_mode == 1) && + (pdev->low_power_auto_data.low_power_auto_range_count == 0)) { + + pdev->low_power_auto_data.saved_interrupt_config = + pdev->gen_cfg.system__interrupt_config_gpio; + + pdev->gen_cfg.system__interrupt_config_gpio = 1 << 5; + + if ((pdev->dyn_cfg.system__sequence_config & ( + VL53L1_SEQUENCE_MM1_EN | VL53L1_SEQUENCE_MM2_EN)) == + 0x0) { + pN->algo__part_to_part_range_offset_mm = + (pN->mm_config__outer_offset_mm << 2); + } else { + pN->algo__part_to_part_range_offset_mm = 0x0; + } + + + if (device_config_level < + VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS) { + device_config_level = + VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS; + } + } + + if ((pdev->low_power_auto_data.is_low_power_auto_mode == 1) && + (pdev->low_power_auto_data.low_power_auto_range_count == 1)) { + + pdev->gen_cfg.system__interrupt_config_gpio = + pdev->low_power_auto_data.saved_interrupt_config; + + + device_config_level = VL53L1_DEVICECONFIGLEVEL_FULL; + } + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_save_cfg_data(Dev); + + + + switch (device_config_level) { + case VL53L1_DEVICECONFIGLEVEL_FULL: + i2c_index = VL53L1_STATIC_NVM_MANAGED_I2C_INDEX; + break; + case VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS: + i2c_index = VL53L1_CUSTOMER_NVM_MANAGED_I2C_INDEX; + break; + case VL53L1_DEVICECONFIGLEVEL_STATIC_ONWARDS: + i2c_index = VL53L1_STATIC_CONFIG_I2C_INDEX; + break; + case VL53L1_DEVICECONFIGLEVEL_GENERAL_ONWARDS: + i2c_index = VL53L1_GENERAL_CONFIG_I2C_INDEX; + break; + case VL53L1_DEVICECONFIGLEVEL_TIMING_ONWARDS: + i2c_index = VL53L1_TIMING_CONFIG_I2C_INDEX; + break; + case VL53L1_DEVICECONFIGLEVEL_DYNAMIC_ONWARDS: + i2c_index = VL53L1_DYNAMIC_CONFIG_I2C_INDEX; + break; + default: + i2c_index = VL53L1_SYSTEM_CONTROL_I2C_INDEX; + break; + } + + + + i2c_buffer_size_bytes = + (VL53L1_SYSTEM_CONTROL_I2C_INDEX + + VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES) - + i2c_index; + + + + pbuffer = &buffer[0]; + for (i = 0; i < i2c_buffer_size_bytes; i++) + *pbuffer++ = 0; + + + + if (device_config_level >= VL53L1_DEVICECONFIGLEVEL_FULL && + status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = + VL53L1_STATIC_NVM_MANAGED_I2C_INDEX - i2c_index; + + status = + VL53L1_i2c_encode_static_nvm_managed( + pstatic_nvm, + VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes]); + } + + if (device_config_level >= VL53L1_DEVICECONFIGLEVEL_CUSTOMER_ONWARDS && + status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = + VL53L1_CUSTOMER_NVM_MANAGED_I2C_INDEX - i2c_index; + + status = + VL53L1_i2c_encode_customer_nvm_managed( + pcustomer_nvm, + VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes]); + } + + if (device_config_level >= VL53L1_DEVICECONFIGLEVEL_STATIC_ONWARDS && + status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = + VL53L1_STATIC_CONFIG_I2C_INDEX - i2c_index; + + status = + VL53L1_i2c_encode_static_config( + pstatic, + VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes]); + } + + if (device_config_level >= VL53L1_DEVICECONFIGLEVEL_GENERAL_ONWARDS && + status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = + VL53L1_GENERAL_CONFIG_I2C_INDEX - i2c_index; + + status = + VL53L1_i2c_encode_general_config( + pgeneral, + VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes]); + } + + if (device_config_level >= VL53L1_DEVICECONFIGLEVEL_TIMING_ONWARDS && + status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = + VL53L1_TIMING_CONFIG_I2C_INDEX - i2c_index; + + status = + VL53L1_i2c_encode_timing_config( + ptiming, + VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes]); + } + + if (device_config_level >= VL53L1_DEVICECONFIGLEVEL_DYNAMIC_ONWARDS && + status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = + VL53L1_DYNAMIC_CONFIG_I2C_INDEX - i2c_index; + + + if ((psystem->system__mode_start & + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK) == + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK) { + pdynamic->system__grouped_parameter_hold_0 = + pstate->cfg_gph_id | 0x01; + pdynamic->system__grouped_parameter_hold_1 = + pstate->cfg_gph_id | 0x01; + pdynamic->system__grouped_parameter_hold = + pstate->cfg_gph_id; + } + status = + VL53L1_i2c_encode_dynamic_config( + pdynamic, + VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes]); + } + + if (status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = + VL53L1_SYSTEM_CONTROL_I2C_INDEX - i2c_index; + + status = + VL53L1_i2c_encode_system_control( + psystem, + VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes]); + } + + + + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_WriteMulti( + Dev, + i2c_index, + buffer, + (uint32_t)i2c_buffer_size_bytes); + } + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_update_ll_driver_rd_state(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_update_ll_driver_cfg_state(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_stop_range( + VL53L1_DEV Dev) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + + + pdev->sys_ctrl.system__mode_start = + (pdev->sys_ctrl.system__mode_start & + VL53L1_DEVICEMEASUREMENTMODE_STOP_MASK) | + VL53L1_DEVICEMEASUREMENTMODE_ABORT; + + status = VL53L1_set_system_control( + Dev, + &pdev->sys_ctrl); + + + pdev->sys_ctrl.system__mode_start = + (pdev->sys_ctrl.system__mode_start & + VL53L1_DEVICEMEASUREMENTMODE_STOP_MASK); + + + VL53L1_init_ll_driver_state( + Dev, + VL53L1_DEVICESTATE_SW_STANDBY); + + + V53L1_init_zone_results_structure( + pdev->zone_cfg.active_zones+1, + &(pres->zone_results)); + + + V53L1_init_zone_dss_configs(Dev); + + + if (pdev->low_power_auto_data.is_low_power_auto_mode == 1) + VL53L1_low_power_auto_data_stop_range(Dev); + + return status; +} + + +VL53L1_Error VL53L1_get_measurement_results( + VL53L1_DEV Dev, + VL53L1_DeviceResultsLevel device_results_level) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t buffer[VL53L1_MAX_I2C_XFER_SIZE]; + + VL53L1_system_results_t *psystem_results = &(pdev->sys_results); + VL53L1_core_results_t *pcore_results = &(pdev->core_results); + VL53L1_debug_results_t *pdebug_results = &(pdev->dbg_results); + + uint16_t i2c_index = VL53L1_SYSTEM_RESULTS_I2C_INDEX; + uint16_t i2c_buffer_offset_bytes = 0; + uint16_t i2c_buffer_size_bytes = 0; + + LOG_FUNCTION_START(""); + + + + switch (device_results_level) { + case VL53L1_DEVICERESULTSLEVEL_FULL: + i2c_buffer_size_bytes = + (VL53L1_DEBUG_RESULTS_I2C_INDEX + + VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES) - + i2c_index; + break; + case VL53L1_DEVICERESULTSLEVEL_UPTO_CORE: + i2c_buffer_size_bytes = + (VL53L1_CORE_RESULTS_I2C_INDEX + + VL53L1_CORE_RESULTS_I2C_SIZE_BYTES) - + i2c_index; + break; + default: + i2c_buffer_size_bytes = + VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES; + break; + } + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_ReadMulti( + Dev, + i2c_index, + buffer, + (uint32_t)i2c_buffer_size_bytes); + + + + if (device_results_level >= VL53L1_DEVICERESULTSLEVEL_FULL && + status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = + VL53L1_DEBUG_RESULTS_I2C_INDEX - i2c_index; + + status = + VL53L1_i2c_decode_debug_results( + VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes], + pdebug_results); + } + + if (device_results_level >= VL53L1_DEVICERESULTSLEVEL_UPTO_CORE && + status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = + VL53L1_CORE_RESULTS_I2C_INDEX - i2c_index; + + status = + VL53L1_i2c_decode_core_results( + VL53L1_CORE_RESULTS_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes], + pcore_results); + } + + if (status == VL53L1_ERROR_NONE) { + + i2c_buffer_offset_bytes = 0; + status = + VL53L1_i2c_decode_system_results( + VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES, + &buffer[i2c_buffer_offset_bytes], + psystem_results); + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_device_results( + VL53L1_DEV Dev, + VL53L1_DeviceResultsLevel device_results_level, + VL53L1_range_results_t *prange_results) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + VL53L1_range_results_t *presults = + &(pres->range_results); + VL53L1_zone_objects_t *pobjects = + &(pres->zone_results.VL53L1_p_002[0]); + VL53L1_ll_driver_state_t *pstate = + &(pdev->ll_state); + VL53L1_zone_config_t *pzone_cfg = + &(pdev->zone_cfg); + VL53L1_zone_hist_info_t *phist_info = + &(pres->zone_hists.VL53L1_p_002[0]); + + VL53L1_dmax_calibration_data_t dmax_cal; + VL53L1_dmax_calibration_data_t *pdmax_cal = &dmax_cal; + VL53L1_hist_post_process_config_t *pHP = &(pdev->histpostprocess); + VL53L1_xtalk_config_t *pC = &(pdev->xtalk_cfg); + VL53L1_low_power_auto_data_t *pL = &(pdev->low_power_auto_data); + VL53L1_histogram_bin_data_t *pHD = &(pdev->hist_data); + VL53L1_customer_nvm_managed_t *pN = &(pdev->customer); + VL53L1_zone_histograms_t *pZH = &(pres->zone_hists); + VL53L1_xtalk_calibration_results_t *pXCR = &(pdev->xtalk_cal); + uint8_t tmp8; + uint8_t zid; + uint8_t i; + uint8_t histo_merge_nb, idx; + uint8_t merge_enabled; + VL53L1_range_data_t *pdata; + + LOG_FUNCTION_START(""); + + merge_enabled = (pdev->tuning_parms.tp_hist_merge == 1) && + (VL53L1DevDataGet(Dev, CurrentParameters.PresetMode) == + VL53L1_PRESETMODE_RANGING); + + if ((pdev->sys_ctrl.system__mode_start & + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM) + == VL53L1_DEVICESCHEDULERMODE_HISTOGRAM) { + + + + status = VL53L1_get_histogram_bin_data( + Dev, + &(pdev->hist_data)); + + + + + if (status == VL53L1_ERROR_NONE && + pHD->number_of_ambient_bins == 0) { + zid = pdev->ll_state.rd_zone_id; + status = VL53L1_hist_copy_and_scale_ambient_info( + &(pZH->VL53L1_p_002[zid]), + &(pdev->hist_data)); + } + + + if (status != VL53L1_ERROR_NONE) + goto UPDATE_DYNAMIC_CONFIG; + + VL53L1_compute_histo_merge_nb(Dev, &histo_merge_nb); + if (histo_merge_nb == 0) + histo_merge_nb = 1; + idx = histo_merge_nb - 1; + if (merge_enabled) + pC->algo__crosstalk_compensation_plane_offset_kcps = + pXCR->algo__xtalk_cpo_HistoMerge_kcps[idx]; + + pHP->gain_factor = + pdev->gain_cal.histogram_ranging_gain_factor; + + pHP->algo__crosstalk_compensation_plane_offset_kcps = + VL53L1_calc_crosstalk_plane_offset_with_margin( + pC->algo__crosstalk_compensation_plane_offset_kcps, + pC->histogram_mode_crosstalk_margin_kcps); + + pHP->algo__crosstalk_compensation_x_plane_gradient_kcps = + pC->algo__crosstalk_compensation_x_plane_gradient_kcps; + pHP->algo__crosstalk_compensation_y_plane_gradient_kcps = + pC->algo__crosstalk_compensation_y_plane_gradient_kcps; + + pdev->dmax_cfg.ambient_thresh_sigma = + pHP->ambient_thresh_sigma1; + pdev->dmax_cfg.min_ambient_thresh_events = + pHP->min_ambient_thresh_events; + pdev->dmax_cfg.signal_total_events_limit = + pHP->signal_total_events_limit; + pdev->dmax_cfg.dss_config__target_total_rate_mcps = + pdev->stat_cfg.dss_config__target_total_rate_mcps; + pdev->dmax_cfg.dss_config__aperture_attenuation = + pdev->gen_cfg.dss_config__aperture_attenuation; + + pHP->algo__crosstalk_detect_max_valid_range_mm = + pC->algo__crosstalk_detect_max_valid_range_mm; + pHP->algo__crosstalk_detect_min_valid_range_mm = + pC->algo__crosstalk_detect_min_valid_range_mm; + pHP->algo__crosstalk_detect_max_valid_rate_kcps = + pC->algo__crosstalk_detect_max_valid_rate_kcps; + pHP->algo__crosstalk_detect_max_sigma_mm = + pC->algo__crosstalk_detect_max_sigma_mm; + + + + VL53L1_copy_rtn_good_spads_to_buffer( + &(pdev->nvm_copy_data), + &(pdev->rtn_good_spads[0])); + + + + switch (pdev->offset_correction_mode) { + + case VL53L1_OFFSETCORRECTIONMODE__MM1_MM2_OFFSETS: + tmp8 = pdev->gen_cfg.dss_config__aperture_attenuation; + + VL53L1_hist_combine_mm1_mm2_offsets( + pN->mm_config__inner_offset_mm, + pN->mm_config__outer_offset_mm, + pdev->nvm_copy_data.roi_config__mode_roi_centre_spad, + pdev->nvm_copy_data.roi_config__mode_roi_xy_size, + pHD->roi_config__user_roi_centre_spad, + pHD->roi_config__user_roi_requested_global_xy_size, + &(pdev->add_off_cal_data), + &(pdev->rtn_good_spads[0]), + (uint16_t)tmp8, + &(pHP->range_offset_mm)); + break; + case VL53L1_OFFSETCORRECTIONMODE__PER_ZONE_OFFSETS: + zid = pdev->ll_state.rd_zone_id; + pHP->range_offset_mm = (int16_t)( + pres->zone_cal.VL53L1_p_002[zid].range_mm_offset); + break; + case VL53L1_OFFSETCORRECTIONMODE__PER_VCSEL_OFFSETS: + select_offset_per_vcsel( + pdev, + &(pHP->range_offset_mm)); + pHP->range_offset_mm *= 4; + break; + default: + pHP->range_offset_mm = 0; + break; + + } + + + + if (status != VL53L1_ERROR_NONE) + goto UPDATE_DYNAMIC_CONFIG; + + + VL53L1_calc_max_effective_spads( + pHD->roi_config__user_roi_centre_spad, + pHD->roi_config__user_roi_requested_global_xy_size, + &(pdev->rtn_good_spads[0]), + (uint16_t)pdev->gen_cfg.dss_config__aperture_attenuation, + &(pdev->dmax_cfg.max_effective_spads)); + + status = + VL53L1_get_dmax_calibration_data( + Dev, + pdev->dmax_mode, + pdev->ll_state.rd_zone_id, + pdmax_cal); + + + + if (status != VL53L1_ERROR_NONE) + goto UPDATE_DYNAMIC_CONFIG; + + status = VL53L1_ipp_hist_process_data( + Dev, + pdmax_cal, + &(pdev->dmax_cfg), + &(pdev->histpostprocess), + &(pdev->hist_data), + &(pdev->xtalk_shapes), + pdev->wArea1, + pdev->wArea2, + &histo_merge_nb, + presults); + + if ((merge_enabled) && (histo_merge_nb > 1)) + for (i = 0; i < VL53L1_MAX_RANGE_RESULTS; i++) { + pdata = &(presults->VL53L1_p_002[i]); + pdata->VL53L1_p_020 /= histo_merge_nb; + pdata->VL53L1_p_021 /= histo_merge_nb; + pdata->VL53L1_p_013 /= histo_merge_nb; + pdata->peak_signal_count_rate_mcps /= histo_merge_nb; + pdata->avg_signal_count_rate_mcps /= histo_merge_nb; + pdata->ambient_count_rate_mcps /= histo_merge_nb; + pdata->VL53L1_p_012 /= histo_merge_nb; + } + + + if (status != VL53L1_ERROR_NONE) + goto UPDATE_DYNAMIC_CONFIG; + + status = VL53L1_hist_wrap_dmax( + &(pdev->histpostprocess), + &(pdev->hist_data), + &(presults->wrap_dmax_mm)); + + + if (status != VL53L1_ERROR_NONE) + goto UPDATE_DYNAMIC_CONFIG; + + zid = pdev->ll_state.rd_zone_id; + status = VL53L1_hist_phase_consistency_check( + Dev, + &(pZH->VL53L1_p_002[zid]), + &(pres->zone_results.VL53L1_p_002[zid]), + presults); + + + if (status != VL53L1_ERROR_NONE) + goto UPDATE_DYNAMIC_CONFIG; + + zid = pdev->ll_state.rd_zone_id; + status = VL53L1_hist_xmonitor_consistency_check( + Dev, + &(pZH->VL53L1_p_002[zid]), + &(pres->zone_results.VL53L1_p_002[zid]), + &(presults->xmonitor)); + + + if (status != VL53L1_ERROR_NONE) + goto UPDATE_DYNAMIC_CONFIG; + + + zid = pdev->ll_state.rd_zone_id; + pZH->max_zones = VL53L1_MAX_USER_ZONES; + pZH->active_zones = + pdev->zone_cfg.active_zones+1; + pHD->zone_id = zid; + + if (zid < + pres->zone_results.max_zones) { + + phist_info = + &(pZH->VL53L1_p_002[zid]); + + phist_info->rd_device_state = + pHD->rd_device_state; + + phist_info->number_of_ambient_bins = + pHD->number_of_ambient_bins; + + phist_info->result__dss_actual_effective_spads = + pHD->result__dss_actual_effective_spads; + + phist_info->VL53L1_p_009 = + pHD->VL53L1_p_009; + + phist_info->total_periods_elapsed = + pHD->total_periods_elapsed; + + phist_info->ambient_events_sum = + pHD->ambient_events_sum; + } + + + + if (status != VL53L1_ERROR_NONE) + goto UPDATE_DYNAMIC_CONFIG; + + VL53L1_hist_copy_results_to_sys_and_core( + &(pdev->hist_data), + presults, + &(pdev->sys_results), + &(pdev->core_results)); + + +UPDATE_DYNAMIC_CONFIG: + if (pzone_cfg->active_zones > 0) { + if (pstate->rd_device_state != + VL53L1_DEVICESTATE_RANGING_WAIT_GPH_SYNC) { + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_dynamic_zone_update( + Dev, presults); + } + } + + + for (i = 0; i < VL53L1_MAX_USER_ZONES; i++) { + pzone_cfg->bin_config[i] = + ((pdev->ll_state.cfg_internal_stream_count) + & 0x01) ? + VL53L1_ZONECONFIG_BINCONFIG__HIGHAMB : + VL53L1_ZONECONFIG_BINCONFIG__LOWAMB; + } + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_multizone_hist_bins_update(Dev); + + } + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_dynamic_xtalk_correction_corrector(Dev); + +#ifdef VL53L1_LOG_ENABLE + if (status == VL53L1_ERROR_NONE) + VL53L1_print_histogram_bin_data( + &(pdev->hist_data), + "get_device_results():pdev->lldata.hist_data.", + VL53L1_TRACE_MODULE_HISTOGRAM_DATA); +#endif + + if (merge_enabled) + pC->algo__crosstalk_compensation_plane_offset_kcps = + pXCR->algo__xtalk_cpo_HistoMerge_kcps[0]; + } else { + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_get_measurement_results( + Dev, + device_results_level); + + if (status == VL53L1_ERROR_NONE) + VL53L1_copy_sys_and_core_results_to_range_results( + (int32_t)pdev->gain_cal.standard_ranging_gain_factor, + &(pdev->sys_results), + &(pdev->core_results), + presults); + + + + if (pL->is_low_power_auto_mode == 1) { + + if ((status == VL53L1_ERROR_NONE) && + (pL->low_power_auto_range_count == 0)) { + + status = + VL53L1_low_power_auto_setup_manual_calibration( + Dev); + pL->low_power_auto_range_count = 1; + } else if ((status == VL53L1_ERROR_NONE) && + (pL->low_power_auto_range_count == 1)) { + pL->low_power_auto_range_count = 2; + } + } + + } + + + presults->cfg_device_state = pdev->ll_state.cfg_device_state; + presults->rd_device_state = pdev->ll_state.rd_device_state; + presults->zone_id = pdev->ll_state.rd_zone_id; + + if (status == VL53L1_ERROR_NONE) { + + + pres->zone_results.max_zones = VL53L1_MAX_USER_ZONES; + pres->zone_results.active_zones = pdev->zone_cfg.active_zones+1; + zid = pdev->ll_state.rd_zone_id; + + if (zid < pres->zone_results.max_zones) { + + pobjects = + &(pres->zone_results.VL53L1_p_002[zid]); + + pobjects->cfg_device_state = + presults->cfg_device_state; + pobjects->rd_device_state = presults->rd_device_state; + pobjects->zone_id = presults->zone_id; + pobjects->stream_count = presults->stream_count; + + + + pobjects->xmonitor.VL53L1_p_020 = + presults->xmonitor.VL53L1_p_020; + pobjects->xmonitor.VL53L1_p_021 = + presults->xmonitor.VL53L1_p_021; + pobjects->xmonitor.VL53L1_p_014 = + presults->xmonitor.VL53L1_p_014; + pobjects->xmonitor.range_status = + presults->xmonitor.range_status; + + pobjects->max_objects = presults->max_results; + pobjects->active_objects = presults->active_results; + + for (i = 0; i < presults->active_results; i++) { + pobjects->VL53L1_p_002[i].VL53L1_p_020 = + presults->VL53L1_p_002[i].VL53L1_p_020; + pobjects->VL53L1_p_002[i].VL53L1_p_021 = + presults->VL53L1_p_002[i].VL53L1_p_021; + pobjects->VL53L1_p_002[i].VL53L1_p_014 = + presults->VL53L1_p_002[i].VL53L1_p_014; + pobjects->VL53L1_p_002[i].range_status = + presults->VL53L1_p_002[i].range_status; + } + + + } + } + + + + memcpy( + prange_results, + presults, + sizeof(VL53L1_range_results_t)); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_check_ll_driver_rd_state(Dev); + +#ifdef VL53L1_LOG_ENABLE + if (status == VL53L1_ERROR_NONE) + VL53L1_print_range_results( + presults, + "get_device_results():pdev->llresults.range_results.", + VL53L1_TRACE_MODULE_RANGE_RESULTS_DATA); +#endif + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_clear_interrupt_and_enable_next_range( + VL53L1_DEV Dev, + uint8_t measurement_mode) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_init_and_start_range( + Dev, + measurement_mode, + VL53L1_DEVICECONFIGLEVEL_GENERAL_ONWARDS); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_histogram_bin_data( + VL53L1_DEV Dev, + VL53L1_histogram_bin_data_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + VL53L1_zone_private_dyn_cfg_t *pzone_dyn_cfg; + + VL53L1_static_nvm_managed_t *pstat_nvm = &(pdev->stat_nvm); + VL53L1_static_config_t *pstat_cfg = &(pdev->stat_cfg); + VL53L1_general_config_t *pgen_cfg = &(pdev->gen_cfg); + VL53L1_timing_config_t *ptim_cfg = &(pdev->tim_cfg); + VL53L1_range_results_t *presults = &(pres->range_results); + + uint8_t buffer[VL53L1_MAX_I2C_XFER_SIZE]; + uint8_t *pbuffer = &buffer[0]; + uint8_t bin_23_0 = 0x00; + uint16_t bin = 0; + uint16_t i2c_buffer_offset_bytes = 0; + uint16_t encoded_timeout = 0; + uint32_t pll_period_us = 0; + uint32_t periods_elapsed_tmp = 0; + uint8_t i = 0; + int32_t hist_merge = 0; + + LOG_FUNCTION_START(""); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_HISTOGRAM_BIN_DATA_I2C_INDEX, + pbuffer, + VL53L1_HISTOGRAM_BIN_DATA_I2C_SIZE_BYTES); + + + + pdata->result__interrupt_status = *(pbuffer + 0); + pdata->result__range_status = *(pbuffer + 1); + pdata->result__report_status = *(pbuffer + 2); + pdata->result__stream_count = *(pbuffer + 3); + pdata->result__dss_actual_effective_spads = + VL53L1_i2c_decode_uint16_t(2, pbuffer + 4); + + + + i2c_buffer_offset_bytes = + VL53L1_PHASECAL_RESULT__REFERENCE_PHASE - + VL53L1_HISTOGRAM_BIN_DATA_I2C_INDEX; + + pbuffer = &buffer[i2c_buffer_offset_bytes]; + + pdata->phasecal_result__reference_phase = + VL53L1_i2c_decode_uint16_t(2, pbuffer); + + i2c_buffer_offset_bytes = + VL53L1_PHASECAL_RESULT__VCSEL_START - + VL53L1_HISTOGRAM_BIN_DATA_I2C_INDEX; + + pdata->phasecal_result__vcsel_start = buffer[i2c_buffer_offset_bytes]; + + + + pdev->dbg_results.phasecal_result__reference_phase = + pdata->phasecal_result__reference_phase; + pdev->dbg_results.phasecal_result__vcsel_start = + pdata->phasecal_result__vcsel_start; + + + + i2c_buffer_offset_bytes = + VL53L1_RESULT__HISTOGRAM_BIN_23_0_MSB - + VL53L1_HISTOGRAM_BIN_DATA_I2C_INDEX; + + bin_23_0 = buffer[i2c_buffer_offset_bytes] << 2; + + i2c_buffer_offset_bytes = + VL53L1_RESULT__HISTOGRAM_BIN_23_0_LSB - + VL53L1_HISTOGRAM_BIN_DATA_I2C_INDEX; + + bin_23_0 += buffer[i2c_buffer_offset_bytes]; + + i2c_buffer_offset_bytes = + VL53L1_RESULT__HISTOGRAM_BIN_23_0 - + VL53L1_HISTOGRAM_BIN_DATA_I2C_INDEX; + + buffer[i2c_buffer_offset_bytes] = bin_23_0; + + + + i2c_buffer_offset_bytes = + VL53L1_RESULT__HISTOGRAM_BIN_0_2 - + VL53L1_HISTOGRAM_BIN_DATA_I2C_INDEX; + + pbuffer = &buffer[i2c_buffer_offset_bytes]; + for (bin = 0; bin < VL53L1_HISTOGRAM_BUFFER_SIZE; bin++) { + pdata->bin_data[bin] = + (int32_t)VL53L1_i2c_decode_uint32_t(3, pbuffer); + pbuffer += 3; + } + + VL53L1_get_tuning_parm(Dev, VL53L1_TUNINGPARM_HIST_MERGE, + &hist_merge); + + if (pdata->result__stream_count == 0) { + + memset(pdev->multi_bins_rec, 0, sizeof(pdev->multi_bins_rec)); + pdev->bin_rec_pos = 0; + pdev->pos_before_next_recom = 0; + } + + if (hist_merge == 1) + vl53l1_histo_merge(Dev, pdata); + + + pdata->zone_id = pdev->ll_state.rd_zone_id; + pdata->VL53L1_p_022 = 0; + pdata->VL53L1_p_023 = VL53L1_HISTOGRAM_BUFFER_SIZE; + pdata->VL53L1_p_024 = VL53L1_HISTOGRAM_BUFFER_SIZE; + + pdata->cal_config__vcsel_start = pgen_cfg->cal_config__vcsel_start; + + + + pdata->vcsel_width = + ((uint16_t)pgen_cfg->global_config__vcsel_width) << 4; + pdata->vcsel_width += + (uint16_t)pstat_cfg->ana_config__vcsel_pulse_width_offset; + + + pdata->VL53L1_p_019 = + pstat_nvm->osc_measured__fast_osc__frequency; + + + + VL53L1_hist_get_bin_sequence_config(Dev, pdata); + + + + if (pdev->ll_state.rd_timing_status == 0) { + + encoded_timeout = + (ptim_cfg->range_config__timeout_macrop_a_hi << 8) + + ptim_cfg->range_config__timeout_macrop_a_lo; + pdata->VL53L1_p_009 = ptim_cfg->range_config__vcsel_period_a; + } else { + + encoded_timeout = + (ptim_cfg->range_config__timeout_macrop_b_hi << 8) + + ptim_cfg->range_config__timeout_macrop_b_lo; + pdata->VL53L1_p_009 = ptim_cfg->range_config__vcsel_period_b; + } + + + + pdata->number_of_ambient_bins = 0; + + for (i = 0; i < 6; i++) { + if ((pdata->bin_seq[i] & 0x07) == 0x07) + pdata->number_of_ambient_bins = + pdata->number_of_ambient_bins + 0x04; + } + + pdata->total_periods_elapsed = + VL53L1_decode_timeout(encoded_timeout); + + + + + pll_period_us = + VL53L1_calc_pll_period_us(pdata->VL53L1_p_019); + + + + periods_elapsed_tmp = pdata->total_periods_elapsed + 1; + + + + pdata->peak_duration_us = + VL53L1_duration_maths( + pll_period_us, + (uint32_t)pdata->vcsel_width, + VL53L1_RANGING_WINDOW_VCSEL_PERIODS, + periods_elapsed_tmp); + + pdata->woi_duration_us = 0; + + + + VL53L1_hist_calc_zero_distance_phase(pdata); + + + + VL53L1_hist_estimate_ambient_from_ambient_bins(pdata); + + + + pdata->cfg_device_state = pdev->ll_state.cfg_device_state; + pdata->rd_device_state = pdev->ll_state.rd_device_state; + + + + pzone_dyn_cfg = &(pres->zone_dyn_cfgs.VL53L1_p_002[pdata->zone_id]); + + pdata->roi_config__user_roi_centre_spad = + pzone_dyn_cfg->roi_config__user_roi_centre_spad; + pdata->roi_config__user_roi_requested_global_xy_size = + pzone_dyn_cfg->roi_config__user_roi_requested_global_xy_size; + + + + presults->device_status = VL53L1_DEVICEERROR_NOUPDATE; + + + + switch (pdata->result__range_status & + VL53L1_RANGE_STATUS__RANGE_STATUS_MASK) { + + case VL53L1_DEVICEERROR_VCSELCONTINUITYTESTFAILURE: + case VL53L1_DEVICEERROR_VCSELWATCHDOGTESTFAILURE: + case VL53L1_DEVICEERROR_NOVHVVALUEFOUND: + case VL53L1_DEVICEERROR_USERROICLIP: + case VL53L1_DEVICEERROR_MULTCLIPFAIL: + + presults->device_status = (pdata->result__range_status & + VL53L1_RANGE_STATUS__RANGE_STATUS_MASK); + + status = VL53L1_ERROR_RANGE_ERROR; + + break; + + } + + LOG_FUNCTION_END(status); + + return status; +} + + +void VL53L1_copy_sys_and_core_results_to_range_results( + int32_t gain_factor, + VL53L1_system_results_t *psys, + VL53L1_core_results_t *pcore, + VL53L1_range_results_t *presults) +{ + uint8_t i = 0; + + VL53L1_range_data_t *pdata; + int32_t range_mm = 0; + uint32_t tmpu32 = 0; + uint16_t rpscr_crosstalk_corrected_mcps_sd0; + uint16_t rmmo_effective_spads_sd0; + uint16_t rmmi_effective_spads_sd0; + + LOG_FUNCTION_START(""); + + + + presults->zone_id = 0; + presults->stream_count = psys->result__stream_count; + presults->wrap_dmax_mm = 0; + presults->max_results = VL53L1_MAX_RANGE_RESULTS; + presults->active_results = 1; + rpscr_crosstalk_corrected_mcps_sd0 = + psys->result__peak_signal_count_rate_crosstalk_corrected_mcps_sd0; + rmmo_effective_spads_sd0 = + psys->result__mm_outer_actual_effective_spads_sd0; + rmmi_effective_spads_sd0 = + psys->result__mm_inner_actual_effective_spads_sd0; + + + for (i = 0; i < VL53L1_MAX_AMBIENT_DMAX_VALUES; i++) + presults->VL53L1_p_007[i] = 0; + + pdata = &(presults->VL53L1_p_002[0]); + + for (i = 0; i < 2; i++) { + + pdata->range_id = i; + pdata->time_stamp = 0; + + if ((psys->result__stream_count == 0) && + ((psys->result__range_status & + VL53L1_RANGE_STATUS__RANGE_STATUS_MASK) == + VL53L1_DEVICEERROR_RANGECOMPLETE)) { + pdata->range_status = + VL53L1_DEVICEERROR_RANGECOMPLETE_NO_WRAP_CHECK; + } else { + pdata->range_status = + psys->result__range_status & + VL53L1_RANGE_STATUS__RANGE_STATUS_MASK; + } + + pdata->VL53L1_p_015 = 0; + pdata->VL53L1_p_022 = 0; + pdata->VL53L1_p_025 = 0; + pdata->VL53L1_p_026 = 0; + pdata->VL53L1_p_016 = 0; + pdata->VL53L1_p_027 = 0; + + switch (i) { + + case 0: + if (psys->result__report_status == + VL53L1_DEVICEREPORTSTATUS_MM1) + pdata->VL53L1_p_006 = + rmmi_effective_spads_sd0; + else if (psys->result__report_status == + VL53L1_DEVICEREPORTSTATUS_MM2) + pdata->VL53L1_p_006 = + rmmo_effective_spads_sd0; + else + pdata->VL53L1_p_006 = + psys->result__dss_actual_effective_spads_sd0; + + pdata->peak_signal_count_rate_mcps = + rpscr_crosstalk_corrected_mcps_sd0; + pdata->avg_signal_count_rate_mcps = + psys->result__avg_signal_count_rate_mcps_sd0; + pdata->ambient_count_rate_mcps = + psys->result__ambient_count_rate_mcps_sd0; + + + + + tmpu32 = ((uint32_t)psys->result__sigma_sd0 << 5); + if (tmpu32 > 0xFFFF) + tmpu32 = 0xFFFF; + + pdata->VL53L1_p_005 = (uint16_t)tmpu32; + + + + pdata->VL53L1_p_014 = + psys->result__phase_sd0; + + range_mm = (int32_t)( + psys->result__final_crosstalk_corrected_range_mm_sd0); + + + range_mm *= gain_factor; + range_mm += 0x0400; + range_mm /= 0x0800; + + pdata->median_range_mm = (int16_t)range_mm; + + pdata->VL53L1_p_021 = + pcore->result_core__ranging_total_events_sd0; + pdata->VL53L1_p_013 = + pcore->result_core__signal_total_events_sd0; + pdata->total_periods_elapsed = + pcore->result_core__total_periods_elapsed_sd0; + pdata->VL53L1_p_020 = + pcore->result_core__ambient_window_events_sd0; + + break; + case 1: + + pdata->VL53L1_p_006 = + psys->result__dss_actual_effective_spads_sd1; + pdata->peak_signal_count_rate_mcps = + psys->result__peak_signal_count_rate_mcps_sd1; + pdata->avg_signal_count_rate_mcps = + 0xFFFF; + pdata->ambient_count_rate_mcps = + psys->result__ambient_count_rate_mcps_sd1; + + + + + tmpu32 = ((uint32_t)psys->result__sigma_sd1 << 5); + if (tmpu32 > 0xFFFF) + tmpu32 = 0xFFFF; + + pdata->VL53L1_p_005 = (uint16_t)tmpu32; + + + + pdata->VL53L1_p_014 = + psys->result__phase_sd1; + + range_mm = (int32_t)( + psys->result__final_crosstalk_corrected_range_mm_sd1); + + + range_mm *= gain_factor; + range_mm += 0x0400; + range_mm /= 0x0800; + + pdata->median_range_mm = (int16_t)range_mm; + + pdata->VL53L1_p_021 = + pcore->result_core__ranging_total_events_sd1; + pdata->VL53L1_p_013 = + pcore->result_core__signal_total_events_sd1; + pdata->total_periods_elapsed = + pcore->result_core__total_periods_elapsed_sd1; + pdata->VL53L1_p_020 = + pcore->result_core__ambient_window_events_sd1; + + break; + } + + + pdata->VL53L1_p_028 = pdata->VL53L1_p_014; + pdata->VL53L1_p_029 = pdata->VL53L1_p_014; + pdata->min_range_mm = pdata->median_range_mm; + pdata->max_range_mm = pdata->median_range_mm; + + pdata++; + } + + + + presults->device_status = VL53L1_DEVICEERROR_NOUPDATE; + + + + switch (psys->result__range_status & + VL53L1_RANGE_STATUS__RANGE_STATUS_MASK) { + + case VL53L1_DEVICEERROR_VCSELCONTINUITYTESTFAILURE: + case VL53L1_DEVICEERROR_VCSELWATCHDOGTESTFAILURE: + case VL53L1_DEVICEERROR_NOVHVVALUEFOUND: + case VL53L1_DEVICEERROR_USERROICLIP: + case VL53L1_DEVICEERROR_MULTCLIPFAIL: + + presults->device_status = (psys->result__range_status & + VL53L1_RANGE_STATUS__RANGE_STATUS_MASK); + + presults->VL53L1_p_002[0].range_status = + VL53L1_DEVICEERROR_NOUPDATE; + break; + + } + + LOG_FUNCTION_END(0); +} + + +VL53L1_Error VL53L1_set_zone_dss_config( + VL53L1_DEV Dev, + VL53L1_zone_private_dyn_cfg_t *pzone_dyn_cfg) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_ll_driver_state_t *pstate = &(pdev->ll_state); + + LOG_FUNCTION_START(""); + + if (pstate->cfg_device_state == + VL53L1_DEVICESTATE_RANGING_DSS_MANUAL) { + pdev->gen_cfg.dss_config__roi_mode_control = + VL53L1_DSS_CONTROL__MODE_EFFSPADS; + pdev->gen_cfg.dss_config__manual_effective_spads_select = + pzone_dyn_cfg->dss_requested_effective_spad_count; + } else { + pdev->gen_cfg.dss_config__roi_mode_control = + VL53L1_DSS_CONTROL__MODE_TARGET_RATE; + } + + LOG_FUNCTION_END(status); + return status; +} + + +VL53L1_Error VL53L1_calc_ambient_dmax( + VL53L1_DEV Dev, + uint16_t target_reflectance, + int16_t *pambient_dmax_mm) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + VL53L1_dmax_calibration_data_t dmax_cal; + VL53L1_dmax_calibration_data_t *pdmax_cal = &dmax_cal; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_get_dmax_calibration_data( + Dev, + pdev->debug_mode, + pdev->ll_state.rd_zone_id, + pdmax_cal); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_ipp_hist_ambient_dmax( + Dev, + target_reflectance, + &(pdev->fmt_dmax_cal), + &(pdev->dmax_cfg), + &(pdev->hist_data), + pambient_dmax_mm); + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_set_GPIO_interrupt_config( + VL53L1_DEV Dev, + VL53L1_GPIO_Interrupt_Mode intr_mode_distance, + VL53L1_GPIO_Interrupt_Mode intr_mode_rate, + uint8_t intr_new_measure_ready, + uint8_t intr_no_target, + uint8_t intr_combined_mode, + uint16_t thresh_distance_high, + uint16_t thresh_distance_low, + uint16_t thresh_rate_high, + uint16_t thresh_rate_low + ) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_GPIO_interrupt_config_t *pintconf = + &(pdev->gpio_interrupt_config); + + LOG_FUNCTION_START(""); + + + pintconf->intr_mode_distance = intr_mode_distance; + pintconf->intr_mode_rate = intr_mode_rate; + pintconf->intr_new_measure_ready = intr_new_measure_ready; + pintconf->intr_no_target = intr_no_target; + pintconf->intr_combined_mode = intr_combined_mode; + pintconf->threshold_distance_high = thresh_distance_high; + pintconf->threshold_distance_low = thresh_distance_low; + pintconf->threshold_rate_high = thresh_rate_high; + pintconf->threshold_rate_low = thresh_rate_low; + + + pdev->gen_cfg.system__interrupt_config_gpio = + VL53L1_encode_GPIO_interrupt_config(pintconf); + + + + status = VL53L1_set_GPIO_thresholds_from_struct( + Dev, + pintconf); + + LOG_FUNCTION_END(status); + return status; +} + + + +VL53L1_Error VL53L1_set_GPIO_interrupt_config_struct( + VL53L1_DEV Dev, + VL53L1_GPIO_interrupt_config_t intconf) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_GPIO_interrupt_config_t *pintconf = + &(pdev->gpio_interrupt_config); + + LOG_FUNCTION_START(""); + + + memcpy(pintconf, &(intconf), sizeof(VL53L1_GPIO_interrupt_config_t)); + + + pdev->gen_cfg.system__interrupt_config_gpio = + VL53L1_encode_GPIO_interrupt_config(pintconf); + + + status = VL53L1_set_GPIO_thresholds_from_struct( + Dev, + pintconf); + + LOG_FUNCTION_END(status); + return status; +} + + + +VL53L1_Error VL53L1_get_GPIO_interrupt_config( + VL53L1_DEV Dev, + VL53L1_GPIO_interrupt_config_t *pintconf) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + pdev->gpio_interrupt_config = VL53L1_decode_GPIO_interrupt_config( + pdev->gen_cfg.system__interrupt_config_gpio); + + + pdev->gpio_interrupt_config.threshold_distance_high = + pdev->dyn_cfg.system__thresh_high; + pdev->gpio_interrupt_config.threshold_distance_low = + pdev->dyn_cfg.system__thresh_low; + + pdev->gpio_interrupt_config.threshold_rate_high = + pdev->gen_cfg.system__thresh_rate_high; + pdev->gpio_interrupt_config.threshold_rate_low = + pdev->gen_cfg.system__thresh_rate_low; + + if (pintconf == &(pdev->gpio_interrupt_config)) { + + } else { + + + memcpy(pintconf, &(pdev->gpio_interrupt_config), + sizeof(VL53L1_GPIO_interrupt_config_t)); + } + + LOG_FUNCTION_END(status); + return status; +} + + +VL53L1_Error VL53L1_set_dmax_mode( + VL53L1_DEV Dev, + VL53L1_DeviceDmaxMode dmax_mode) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->dmax_mode = dmax_mode; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_dmax_mode( + VL53L1_DEV Dev, + VL53L1_DeviceDmaxMode *pdmax_mode) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *pdmax_mode = pdev->dmax_mode; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_dmax_calibration_data( + VL53L1_DEV Dev, + VL53L1_DeviceDmaxMode dmax_mode, + uint8_t zone_id, + VL53L1_dmax_calibration_data_t *pdmax_cal) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + LOG_FUNCTION_START(""); + + switch (dmax_mode) { + + case VL53L1_DEVICEDMAXMODE__PER_ZONE_CAL_DATA: + pdmax_cal->ref__actual_effective_spads = + (uint16_t)pres->zone_cal.VL53L1_p_002[zone_id].effective_spads; + pdmax_cal->ref__peak_signal_count_rate_mcps = + (uint16_t)pres->zone_cal.VL53L1_p_002[zone_id].peak_rate_mcps; + pdmax_cal->ref__distance_mm = + pres->zone_cal.cal_distance_mm; + pdmax_cal->ref_reflectance_pc = + pres->zone_cal.cal_reflectance_pc; + pdmax_cal->coverglass_transmission = 0x0100; + break; + + case VL53L1_DEVICEDMAXMODE__CUST_CAL_DATA: + memcpy( + pdmax_cal, + &(pdev->cust_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + break; + + case VL53L1_DEVICEDMAXMODE__FMT_CAL_DATA: + memcpy( + pdmax_cal, + &(pdev->fmt_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + break; + + default: + status = VL53L1_ERROR_INVALID_PARAMS; + break; + + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_hist_dmax_config( + VL53L1_DEV Dev, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + memcpy( + &(pdev->dmax_cfg), + pdmax_cfg, + sizeof(VL53L1_hist_gen3_dmax_config_t)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_hist_dmax_config( + VL53L1_DEV Dev, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + memcpy( + pdmax_cfg, + &(pdev->dmax_cfg), + sizeof(VL53L1_hist_gen3_dmax_config_t)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_offset_calibration_mode( + VL53L1_DEV Dev, + VL53L1_OffsetCalibrationMode offset_cal_mode) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->offset_calibration_mode = offset_cal_mode; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_offset_calibration_mode( + VL53L1_DEV Dev, + VL53L1_OffsetCalibrationMode *poffset_cal_mode) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *poffset_cal_mode = pdev->offset_calibration_mode; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_offset_correction_mode( + VL53L1_DEV Dev, + VL53L1_OffsetCorrectionMode offset_cor_mode) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->offset_correction_mode = offset_cor_mode; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_offset_correction_mode( + VL53L1_DEV Dev, + VL53L1_OffsetCorrectionMode *poffset_cor_mode) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + *poffset_cor_mode = pdev->offset_correction_mode; + + LOG_FUNCTION_END(status); + + return status; +} + + + + +VL53L1_Error VL53L1_set_zone_calibration_data( + VL53L1_DEV Dev, + VL53L1_zone_calibration_results_t *pzone_cal) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverResults_t *pres = VL53L1DevStructGetLLResultsHandle(Dev); + + LOG_FUNCTION_START(""); + + if (pzone_cal->struct_version != + VL53L1_LL_ZONE_CALIBRATION_DATA_STRUCT_VERSION) + status = VL53L1_ERROR_INVALID_PARAMS; + + + if (status == VL53L1_ERROR_NONE) + + memcpy( + &(pres->zone_cal), + pzone_cal, + sizeof(VL53L1_zone_calibration_results_t)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_zone_calibration_data( + VL53L1_DEV Dev, + VL53L1_zone_calibration_results_t *pzone_cal) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverResults_t *pres = VL53L1DevStructGetLLResultsHandle(Dev); + + LOG_FUNCTION_START(""); + + + memcpy( + pzone_cal, + &(pres->zone_cal), + sizeof(VL53L1_zone_calibration_results_t)); + + pzone_cal->struct_version = + VL53L1_LL_ZONE_CALIBRATION_DATA_STRUCT_VERSION; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_tuning_debug_data( + VL53L1_DEV Dev, + VL53L1_tuning_parameters_t *ptun_data) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_hist_post_process_config_t *pHP = &(pdev->histpostprocess); + VL53L1_xtalkextract_config_t *pXC = &(pdev->xtalk_extract_cfg); + + LOG_FUNCTION_START(""); + + ptun_data->vl53l1_tuningparm_version = + pdev->tuning_parms.tp_tuning_parm_version; + + ptun_data->vl53l1_tuningparm_key_table_version = + pdev->tuning_parms.tp_tuning_parm_key_table_version; + + + ptun_data->vl53l1_tuningparm_lld_version = + pdev->tuning_parms.tp_tuning_parm_lld_version; + + ptun_data->vl53l1_tuningparm_hist_algo_select = + pHP->hist_algo_select; + + ptun_data->vl53l1_tuningparm_hist_target_order = + pHP->hist_target_order; + + ptun_data->vl53l1_tuningparm_hist_filter_woi_0 = + pHP->filter_woi0; + + ptun_data->vl53l1_tuningparm_hist_filter_woi_1 = + pHP->filter_woi1; + + ptun_data->vl53l1_tuningparm_hist_amb_est_method = + pHP->hist_amb_est_method; + + ptun_data->vl53l1_tuningparm_hist_amb_thresh_sigma_0 = + pHP->ambient_thresh_sigma0; + + ptun_data->vl53l1_tuningparm_hist_amb_thresh_sigma_1 = + pHP->ambient_thresh_sigma1; + + ptun_data->vl53l1_tuningparm_hist_min_amb_thresh_events = + pHP->min_ambient_thresh_events; + + ptun_data->vl53l1_tuningparm_hist_amb_events_scaler = + pHP->ambient_thresh_events_scaler; + + ptun_data->vl53l1_tuningparm_hist_noise_threshold = + pHP->noise_threshold; + + ptun_data->vl53l1_tuningparm_hist_signal_total_events_limit = + pHP->signal_total_events_limit; + + ptun_data->vl53l1_tuningparm_hist_sigma_est_ref_mm = + pHP->sigma_estimator__sigma_ref_mm; + + ptun_data->vl53l1_tuningparm_hist_sigma_thresh_mm = + pHP->sigma_thresh; + + ptun_data->vl53l1_tuningparm_hist_gain_factor = + pdev->gain_cal.histogram_ranging_gain_factor; + + ptun_data->vl53l1_tuningparm_consistency_hist_phase_tolerance = + pHP->algo__consistency_check__phase_tolerance; + + ptun_data->vl53l1_tuningparm_consistency_hist_min_max_tolerance_mm = + pHP->algo__consistency_check__min_max_tolerance; + + ptun_data->vl53l1_tuningparm_consistency_hist_event_sigma = + pHP->algo__consistency_check__event_sigma; + + ptun_data->vl53l1_tuningparm_consistency_hist_event_sigma_min_spad_limit + = pHP->algo__consistency_check__event_min_spad_count; + + ptun_data->vl53l1_tuningparm_initial_phase_rtn_histo_long_range = + pdev->tuning_parms.tp_init_phase_rtn_hist_long; + + ptun_data->vl53l1_tuningparm_initial_phase_rtn_histo_med_range = + pdev->tuning_parms.tp_init_phase_rtn_hist_med; + + ptun_data->vl53l1_tuningparm_initial_phase_rtn_histo_short_range = + pdev->tuning_parms.tp_init_phase_rtn_hist_short; + + ptun_data->vl53l1_tuningparm_initial_phase_ref_histo_long_range = + pdev->tuning_parms.tp_init_phase_ref_hist_long; + + ptun_data->vl53l1_tuningparm_initial_phase_ref_histo_med_range = + pdev->tuning_parms.tp_init_phase_ref_hist_med; + + ptun_data->vl53l1_tuningparm_initial_phase_ref_histo_short_range = + pdev->tuning_parms.tp_init_phase_ref_hist_short; + + ptun_data->vl53l1_tuningparm_xtalk_detect_min_valid_range_mm = + pdev->xtalk_cfg.algo__crosstalk_detect_min_valid_range_mm; + + ptun_data->vl53l1_tuningparm_xtalk_detect_max_valid_range_mm = + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_range_mm; + + ptun_data->vl53l1_tuningparm_xtalk_detect_max_sigma_mm = + pdev->xtalk_cfg.algo__crosstalk_detect_max_sigma_mm; + + ptun_data->vl53l1_tuningparm_xtalk_detect_min_max_tolerance = + pHP->algo__crosstalk_detect_min_max_tolerance; + + ptun_data->vl53l1_tuningparm_xtalk_detect_max_valid_rate_kcps = + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_rate_kcps; + + ptun_data->vl53l1_tuningparm_xtalk_detect_event_sigma = + pHP->algo__crosstalk_detect_event_sigma; + + ptun_data->vl53l1_tuningparm_hist_xtalk_margin_kcps = + pdev->xtalk_cfg.histogram_mode_crosstalk_margin_kcps; + + ptun_data->vl53l1_tuningparm_consistency_lite_phase_tolerance = + pdev->tuning_parms.tp_consistency_lite_phase_tolerance; + + ptun_data->vl53l1_tuningparm_phasecal_target = + pdev->tuning_parms.tp_phasecal_target; + + ptun_data->vl53l1_tuningparm_lite_cal_repeat_rate = + pdev->tuning_parms.tp_cal_repeat_rate; + + ptun_data->vl53l1_tuningparm_lite_ranging_gain_factor = + pdev->gain_cal.standard_ranging_gain_factor; + + ptun_data->vl53l1_tuningparm_lite_min_clip_mm = + pdev->tuning_parms.tp_lite_min_clip; + + ptun_data->vl53l1_tuningparm_lite_long_sigma_thresh_mm = + pdev->tuning_parms.tp_lite_long_sigma_thresh_mm; + + ptun_data->vl53l1_tuningparm_lite_med_sigma_thresh_mm = + pdev->tuning_parms.tp_lite_med_sigma_thresh_mm; + + ptun_data->vl53l1_tuningparm_lite_short_sigma_thresh_mm = + pdev->tuning_parms.tp_lite_short_sigma_thresh_mm; + + ptun_data->vl53l1_tuningparm_lite_long_min_count_rate_rtn_mcps = + pdev->tuning_parms.tp_lite_long_min_count_rate_rtn_mcps; + + ptun_data->vl53l1_tuningparm_lite_med_min_count_rate_rtn_mcps = + pdev->tuning_parms.tp_lite_med_min_count_rate_rtn_mcps; + + ptun_data->vl53l1_tuningparm_lite_short_min_count_rate_rtn_mcps = + pdev->tuning_parms.tp_lite_short_min_count_rate_rtn_mcps; + + ptun_data->vl53l1_tuningparm_lite_sigma_est_pulse_width = + pdev->tuning_parms.tp_lite_sigma_est_pulse_width_ns; + + ptun_data->vl53l1_tuningparm_lite_sigma_est_amb_width_ns = + pdev->tuning_parms.tp_lite_sigma_est_amb_width_ns; + + ptun_data->vl53l1_tuningparm_lite_sigma_ref_mm = + pdev->tuning_parms.tp_lite_sigma_ref_mm; + + ptun_data->vl53l1_tuningparm_lite_rit_mult = + pdev->xtalk_cfg.crosstalk_range_ignore_threshold_mult; + + ptun_data->vl53l1_tuningparm_lite_seed_config = + pdev->tuning_parms.tp_lite_seed_cfg; + + ptun_data->vl53l1_tuningparm_lite_quantifier = + pdev->tuning_parms.tp_lite_quantifier; + + ptun_data->vl53l1_tuningparm_lite_first_order_select = + pdev->tuning_parms.tp_lite_first_order_select; + + ptun_data->vl53l1_tuningparm_lite_xtalk_margin_kcps = + pdev->xtalk_cfg.lite_mode_crosstalk_margin_kcps; + + ptun_data->vl53l1_tuningparm_initial_phase_rtn_lite_long_range = + pdev->tuning_parms.tp_init_phase_rtn_lite_long; + + ptun_data->vl53l1_tuningparm_initial_phase_rtn_lite_med_range = + pdev->tuning_parms.tp_init_phase_rtn_lite_med; + + ptun_data->vl53l1_tuningparm_initial_phase_rtn_lite_short_range = + pdev->tuning_parms.tp_init_phase_rtn_lite_short; + + ptun_data->vl53l1_tuningparm_initial_phase_ref_lite_long_range = + pdev->tuning_parms.tp_init_phase_ref_lite_long; + + ptun_data->vl53l1_tuningparm_initial_phase_ref_lite_med_range = + pdev->tuning_parms.tp_init_phase_ref_lite_med; + + ptun_data->vl53l1_tuningparm_initial_phase_ref_lite_short_range = + pdev->tuning_parms.tp_init_phase_ref_lite_short; + + ptun_data->vl53l1_tuningparm_timed_seed_config = + pdev->tuning_parms.tp_timed_seed_cfg; + + ptun_data->vl53l1_tuningparm_dmax_cfg_signal_thresh_sigma = + pdev->dmax_cfg.signal_thresh_sigma; + + ptun_data->vl53l1_tuningparm_dmax_cfg_reflectance_array_0 = + pdev->dmax_cfg.target_reflectance_for_dmax_calc[0]; + + ptun_data->vl53l1_tuningparm_dmax_cfg_reflectance_array_1 = + pdev->dmax_cfg.target_reflectance_for_dmax_calc[1]; + + ptun_data->vl53l1_tuningparm_dmax_cfg_reflectance_array_2 = + pdev->dmax_cfg.target_reflectance_for_dmax_calc[2]; + + ptun_data->vl53l1_tuningparm_dmax_cfg_reflectance_array_3 = + pdev->dmax_cfg.target_reflectance_for_dmax_calc[3]; + + ptun_data->vl53l1_tuningparm_dmax_cfg_reflectance_array_4 = + pdev->dmax_cfg.target_reflectance_for_dmax_calc[4]; + + ptun_data->vl53l1_tuningparm_vhv_loopbound = + pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound; + + ptun_data->vl53l1_tuningparm_refspadchar_device_test_mode = + pdev->refspadchar.device_test_mode; + + ptun_data->vl53l1_tuningparm_refspadchar_vcsel_period = + pdev->refspadchar.VL53L1_p_009; + + ptun_data->vl53l1_tuningparm_refspadchar_phasecal_timeout_us = + pdev->refspadchar.timeout_us; + + ptun_data->vl53l1_tuningparm_refspadchar_target_count_rate_mcps = + pdev->refspadchar.target_count_rate_mcps; + + ptun_data->vl53l1_tuningparm_refspadchar_min_countrate_limit_mcps = + pdev->refspadchar.min_count_rate_limit_mcps; + + ptun_data->vl53l1_tuningparm_refspadchar_max_countrate_limit_mcps = + pdev->refspadchar.max_count_rate_limit_mcps; + + ptun_data->vl53l1_tuningparm_xtalk_extract_num_of_samples = + pXC->num_of_samples; + + ptun_data->vl53l1_tuningparm_xtalk_extract_min_filter_thresh_mm = + pXC->algo__crosstalk_extract_min_valid_range_mm; + + ptun_data->vl53l1_tuningparm_xtalk_extract_max_filter_thresh_mm = + pXC->algo__crosstalk_extract_max_valid_range_mm; + + ptun_data->vl53l1_tuningparm_xtalk_extract_dss_rate_mcps = + pXC->dss_config__target_total_rate_mcps; + + ptun_data->vl53l1_tuningparm_xtalk_extract_phasecal_timeout_us = + pXC->phasecal_config_timeout_us; + + ptun_data->vl53l1_tuningparm_xtalk_extract_max_valid_rate_kcps = + pXC->algo__crosstalk_extract_max_valid_rate_kcps; + + ptun_data->vl53l1_tuningparm_xtalk_extract_sigma_threshold_mm = + pXC->algo__crosstalk_extract_max_sigma_mm; + + ptun_data->vl53l1_tuningparm_xtalk_extract_dss_timeout_us = + pXC->mm_config_timeout_us; + + ptun_data->vl53l1_tuningparm_xtalk_extract_bin_timeout_us = + pXC->range_config_timeout_us; + + ptun_data->vl53l1_tuningparm_offset_cal_dss_rate_mcps = + pdev->offsetcal_cfg.dss_config__target_total_rate_mcps; + + ptun_data->vl53l1_tuningparm_offset_cal_phasecal_timeout_us = + pdev->offsetcal_cfg.phasecal_config_timeout_us; + + ptun_data->vl53l1_tuningparm_offset_cal_mm_timeout_us = + pdev->offsetcal_cfg.mm_config_timeout_us; + + ptun_data->vl53l1_tuningparm_offset_cal_range_timeout_us = + pdev->offsetcal_cfg.range_config_timeout_us; + + ptun_data->vl53l1_tuningparm_offset_cal_pre_samples = + pdev->offsetcal_cfg.pre_num_of_samples; + + ptun_data->vl53l1_tuningparm_offset_cal_mm1_samples = + pdev->offsetcal_cfg.mm1_num_of_samples; + + ptun_data->vl53l1_tuningparm_offset_cal_mm2_samples = + pdev->offsetcal_cfg.mm2_num_of_samples; + + ptun_data->vl53l1_tuningparm_zone_cal_dss_rate_mcps = + pdev->zonecal_cfg.dss_config__target_total_rate_mcps; + + ptun_data->vl53l1_tuningparm_zone_cal_phasecal_timeout_us = + pdev->zonecal_cfg.phasecal_config_timeout_us; + + ptun_data->vl53l1_tuningparm_zone_cal_dss_timeout_us = + pdev->zonecal_cfg.mm_config_timeout_us; + + ptun_data->vl53l1_tuningparm_zone_cal_phasecal_num_samples = + pdev->zonecal_cfg.phasecal_num_of_samples; + + ptun_data->vl53l1_tuningparm_zone_cal_range_timeout_us = + pdev->zonecal_cfg.range_config_timeout_us; + + ptun_data->vl53l1_tuningparm_zone_cal_zone_num_samples = + pdev->zonecal_cfg.zone_num_of_samples; + + ptun_data->vl53l1_tuningparm_spadmap_vcsel_period = + pdev->ssc_cfg.VL53L1_p_009; + + ptun_data->vl53l1_tuningparm_spadmap_vcsel_start = + pdev->ssc_cfg.vcsel_start; + + ptun_data->vl53l1_tuningparm_spadmap_rate_limit_mcps = + pdev->ssc_cfg.rate_limit_mcps; + + ptun_data->vl53l1_tuningparm_lite_dss_config_target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_lite_mcps; + + ptun_data->vl53l1_tuningparm_ranging_dss_config_target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_histo_mcps; + + ptun_data->vl53l1_tuningparm_mz_dss_config_target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_histo_mz_mcps; + + ptun_data->vl53l1_tuningparm_timed_dss_config_target_total_rate_mcps = + pdev->tuning_parms.tp_dss_target_timed_mcps; + + ptun_data->vl53l1_tuningparm_lite_phasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_lite_us; + + ptun_data->vl53l1_tuningparm_ranging_long_phasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_hist_long_us; + + ptun_data->vl53l1_tuningparm_ranging_med_phasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_hist_med_us; + + ptun_data->vl53l1_tuningparm_ranging_short_phasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_hist_short_us; + + ptun_data->vl53l1_tuningparm_mz_long_phasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_mz_long_us; + + ptun_data->vl53l1_tuningparm_mz_med_phasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_mz_med_us; + + ptun_data->vl53l1_tuningparm_mz_short_phasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_mz_short_us; + + ptun_data->vl53l1_tuningparm_timed_phasecal_config_timeout_us = + pdev->tuning_parms.tp_phasecal_timeout_timed_us; + + ptun_data->vl53l1_tuningparm_lite_mm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_lite_us; + + ptun_data->vl53l1_tuningparm_ranging_mm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_histo_us; + + ptun_data->vl53l1_tuningparm_mz_mm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_mz_us; + + ptun_data->vl53l1_tuningparm_timed_mm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_timed_us; + + ptun_data->vl53l1_tuningparm_lite_range_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_lite_us; + + ptun_data->vl53l1_tuningparm_ranging_range_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_histo_us; + + ptun_data->vl53l1_tuningparm_mz_range_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_mz_us; + + ptun_data->vl53l1_tuningparm_timed_range_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_timed_us; + + ptun_data->vl53l1_tuningparm_dynxtalk_smudge_margin = + pdev->smudge_correct_config.smudge_margin; + + ptun_data->vl53l1_tuningparm_dynxtalk_noise_margin = + pdev->smudge_correct_config.noise_margin; + + ptun_data->vl53l1_tuningparm_dynxtalk_xtalk_offset_limit = + pdev->smudge_correct_config.user_xtalk_offset_limit; + + ptun_data->vl53l1_tuningparm_dynxtalk_xtalk_offset_limit_hi = + pdev->smudge_correct_config.user_xtalk_offset_limit_hi; + + ptun_data->vl53l1_tuningparm_dynxtalk_sample_limit = + pdev->smudge_correct_config.sample_limit; + + ptun_data->vl53l1_tuningparm_dynxtalk_single_xtalk_delta = + pdev->smudge_correct_config.single_xtalk_delta; + + ptun_data->vl53l1_tuningparm_dynxtalk_averaged_xtalk_delta = + pdev->smudge_correct_config.averaged_xtalk_delta; + + ptun_data->vl53l1_tuningparm_dynxtalk_clip_limit = + pdev->smudge_correct_config.smudge_corr_clip_limit; + + ptun_data->vl53l1_tuningparm_dynxtalk_scaler_calc_method = + pdev->smudge_correct_config.scaler_calc_method; + + ptun_data->vl53l1_tuningparm_dynxtalk_xgradient_scaler = + pdev->smudge_correct_config.x_gradient_scaler; + + ptun_data->vl53l1_tuningparm_dynxtalk_ygradient_scaler = + pdev->smudge_correct_config.y_gradient_scaler; + + ptun_data->vl53l1_tuningparm_dynxtalk_user_scaler_set = + pdev->smudge_correct_config.user_scaler_set; + + ptun_data->vl53l1_tuningparm_dynxtalk_smudge_cor_single_apply = + pdev->smudge_correct_config.smudge_corr_single_apply; + + ptun_data->vl53l1_tuningparm_dynxtalk_xtalk_amb_threshold = + pdev->smudge_correct_config.smudge_corr_ambient_threshold; + + ptun_data->vl53l1_tuningparm_dynxtalk_nodetect_amb_threshold_kcps = + pdev->smudge_correct_config.nodetect_ambient_threshold; + + ptun_data->vl53l1_tuningparm_dynxtalk_nodetect_sample_limit = + pdev->smudge_correct_config.nodetect_sample_limit; + + ptun_data->vl53l1_tuningparm_dynxtalk_nodetect_xtalk_offset_kcps = + pdev->smudge_correct_config.nodetect_xtalk_offset; + + ptun_data->vl53l1_tuningparm_dynxtalk_nodetect_min_range_mm = + pdev->smudge_correct_config.nodetect_min_range_mm; + + ptun_data->vl53l1_tuningparm_lowpowerauto_vhv_loop_bound = + pdev->low_power_auto_data.vhv_loop_bound; + + ptun_data->vl53l1_tuningparm_lowpowerauto_mm_config_timeout_us = + pdev->tuning_parms.tp_mm_timeout_lpa_us; + + ptun_data->vl53l1_tuningparm_lowpowerauto_range_config_timeout_us = + pdev->tuning_parms.tp_range_timeout_lpa_us; + + ptun_data->vl53l1_tuningparm_very_short_dss_rate_mcps = + pdev->tuning_parms.tp_dss_target_very_short_mcps; + + ptun_data->vl53l1_tuningparm_phasecal_patch_power = + pdev->tuning_parms.tp_phasecal_patch_power; + + LOG_FUNCTION_END(status); + + return status; +} + + + + + +VL53L1_Error VL53L1_get_tuning_parm( + VL53L1_DEV Dev, + VL53L1_TuningParms tuning_parm_key, + int32_t *ptuning_parm_value) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_hist_post_process_config_t *pHP = &(pdev->histpostprocess); + VL53L1_xtalkextract_config_t *pXC = &(pdev->xtalk_extract_cfg); + + LOG_FUNCTION_START(""); + + switch (tuning_parm_key) { + + case VL53L1_TUNINGPARM_VERSION: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_tuning_parm_version; + break; + case VL53L1_TUNINGPARM_KEY_TABLE_VERSION: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_tuning_parm_key_table_version; + break; + case VL53L1_TUNINGPARM_LLD_VERSION: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_tuning_parm_lld_version; + break; + case VL53L1_TUNINGPARM_HIST_ALGO_SELECT: + *ptuning_parm_value = + (int32_t)pHP->hist_algo_select; + break; + case VL53L1_TUNINGPARM_HIST_TARGET_ORDER: + *ptuning_parm_value = + (int32_t)pHP->hist_target_order; + break; + case VL53L1_TUNINGPARM_HIST_FILTER_WOI_0: + *ptuning_parm_value = + (int32_t)pHP->filter_woi0; + break; + case VL53L1_TUNINGPARM_HIST_FILTER_WOI_1: + *ptuning_parm_value = + (int32_t)pHP->filter_woi1; + break; + case VL53L1_TUNINGPARM_HIST_AMB_EST_METHOD: + *ptuning_parm_value = + (int32_t)pHP->hist_amb_est_method; + break; + case VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_0: + *ptuning_parm_value = + (int32_t)pHP->ambient_thresh_sigma0; + break; + case VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_1: + *ptuning_parm_value = + (int32_t)pHP->ambient_thresh_sigma1; + break; + case VL53L1_TUNINGPARM_HIST_MIN_AMB_THRESH_EVENTS: + *ptuning_parm_value = + (int32_t)pHP->min_ambient_thresh_events; + break; + case VL53L1_TUNINGPARM_HIST_AMB_EVENTS_SCALER: + *ptuning_parm_value = + (int32_t)pHP->ambient_thresh_events_scaler; + break; + case VL53L1_TUNINGPARM_HIST_NOISE_THRESHOLD: + *ptuning_parm_value = + (int32_t)pHP->noise_threshold; + break; + case VL53L1_TUNINGPARM_HIST_SIGNAL_TOTAL_EVENTS_LIMIT: + *ptuning_parm_value = + (int32_t)pHP->signal_total_events_limit; + break; + case VL53L1_TUNINGPARM_HIST_SIGMA_EST_REF_MM: + *ptuning_parm_value = + (int32_t)pHP->sigma_estimator__sigma_ref_mm; + break; + case VL53L1_TUNINGPARM_HIST_SIGMA_THRESH_MM: + *ptuning_parm_value = + (int32_t)pHP->sigma_thresh; + break; + case VL53L1_TUNINGPARM_HIST_GAIN_FACTOR: + *ptuning_parm_value = + (int32_t)pdev->gain_cal.histogram_ranging_gain_factor; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_HIST_PHASE_TOLERANCE: + *ptuning_parm_value = + (int32_t)pHP->algo__consistency_check__phase_tolerance; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_HIST_MIN_MAX_TOLERANCE_MM: + *ptuning_parm_value = + (int32_t)pHP->algo__consistency_check__min_max_tolerance; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA: + *ptuning_parm_value = + (int32_t)pHP->algo__consistency_check__event_sigma; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA_MIN_SPAD_LIMIT: + *ptuning_parm_value = + (int32_t)pHP->algo__consistency_check__event_min_spad_count; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_LONG_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_rtn_hist_long; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_MED_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_rtn_hist_med; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_SHORT_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_rtn_hist_short; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_LONG_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_ref_hist_long; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_MED_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_ref_hist_med; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_SHORT_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_ref_hist_short; + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MIN_VALID_RANGE_MM: + *ptuning_parm_value = (int32_t)( + pdev->xtalk_cfg.algo__crosstalk_detect_min_valid_range_mm); + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RANGE_MM: + *ptuning_parm_value = (int32_t)( + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_range_mm); + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MAX_SIGMA_MM: + *ptuning_parm_value = + (int32_t)pdev->xtalk_cfg.algo__crosstalk_detect_max_sigma_mm; + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MIN_MAX_TOLERANCE: + *ptuning_parm_value = + (int32_t)pHP->algo__crosstalk_detect_min_max_tolerance; + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RATE_KCPS: + *ptuning_parm_value = (int32_t)( + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_rate_kcps); + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_EVENT_SIGMA: + *ptuning_parm_value = + (int32_t)pHP->algo__crosstalk_detect_event_sigma; + break; + case VL53L1_TUNINGPARM_HIST_XTALK_MARGIN_KCPS: + *ptuning_parm_value = + (int32_t)pdev->xtalk_cfg.histogram_mode_crosstalk_margin_kcps; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_LITE_PHASE_TOLERANCE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_consistency_lite_phase_tolerance; + break; + case VL53L1_TUNINGPARM_PHASECAL_TARGET: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_phasecal_target; + break; + case VL53L1_TUNINGPARM_LITE_CAL_REPEAT_RATE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_cal_repeat_rate; + break; + case VL53L1_TUNINGPARM_LITE_RANGING_GAIN_FACTOR: + *ptuning_parm_value = + (int32_t)pdev->gain_cal.standard_ranging_gain_factor; + break; + case VL53L1_TUNINGPARM_LITE_MIN_CLIP_MM: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_min_clip; + break; + case VL53L1_TUNINGPARM_LITE_LONG_SIGMA_THRESH_MM: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_long_sigma_thresh_mm; + break; + case VL53L1_TUNINGPARM_LITE_MED_SIGMA_THRESH_MM: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_med_sigma_thresh_mm; + break; + case VL53L1_TUNINGPARM_LITE_SHORT_SIGMA_THRESH_MM: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_short_sigma_thresh_mm; + break; + case VL53L1_TUNINGPARM_LITE_LONG_MIN_COUNT_RATE_RTN_MCPS: + *ptuning_parm_value = (int32_t)( + pdev->tuning_parms.tp_lite_long_min_count_rate_rtn_mcps); + break; + case VL53L1_TUNINGPARM_LITE_MED_MIN_COUNT_RATE_RTN_MCPS: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_med_min_count_rate_rtn_mcps; + break; + case VL53L1_TUNINGPARM_LITE_SHORT_MIN_COUNT_RATE_RTN_MCPS: + *ptuning_parm_value = (int32_t)( + pdev->tuning_parms.tp_lite_short_min_count_rate_rtn_mcps); + break; + case VL53L1_TUNINGPARM_LITE_SIGMA_EST_PULSE_WIDTH: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_sigma_est_pulse_width_ns; + break; + case VL53L1_TUNINGPARM_LITE_SIGMA_EST_AMB_WIDTH_NS: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_sigma_est_amb_width_ns; + break; + case VL53L1_TUNINGPARM_LITE_SIGMA_REF_MM: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_sigma_ref_mm; + break; + case VL53L1_TUNINGPARM_LITE_RIT_MULT: + *ptuning_parm_value = + (int32_t)pdev->xtalk_cfg.crosstalk_range_ignore_threshold_mult; + break; + case VL53L1_TUNINGPARM_LITE_SEED_CONFIG: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_seed_cfg; + break; + case VL53L1_TUNINGPARM_LITE_QUANTIFIER: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_quantifier; + break; + case VL53L1_TUNINGPARM_LITE_FIRST_ORDER_SELECT: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_lite_first_order_select; + break; + case VL53L1_TUNINGPARM_LITE_XTALK_MARGIN_KCPS: + *ptuning_parm_value = + (int32_t)pdev->xtalk_cfg.lite_mode_crosstalk_margin_kcps; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_LONG_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_rtn_lite_long; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_MED_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_rtn_lite_med; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_SHORT_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_rtn_lite_short; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_LONG_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_ref_lite_long; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_MED_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_ref_lite_med; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_SHORT_RANGE: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_init_phase_ref_lite_short; + break; + case VL53L1_TUNINGPARM_TIMED_SEED_CONFIG: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_timed_seed_cfg; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_SIGNAL_THRESH_SIGMA: + *ptuning_parm_value = + (int32_t)pdev->dmax_cfg.signal_thresh_sigma; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_0: + *ptuning_parm_value = + (int32_t)pdev->dmax_cfg.target_reflectance_for_dmax_calc[0]; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_1: + *ptuning_parm_value = + (int32_t)pdev->dmax_cfg.target_reflectance_for_dmax_calc[1]; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_2: + *ptuning_parm_value = + (int32_t)pdev->dmax_cfg.target_reflectance_for_dmax_calc[2]; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_3: + *ptuning_parm_value = + (int32_t)pdev->dmax_cfg.target_reflectance_for_dmax_calc[3]; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_4: + *ptuning_parm_value = + (int32_t)pdev->dmax_cfg.target_reflectance_for_dmax_calc[4]; + break; + case VL53L1_TUNINGPARM_VHV_LOOPBOUND: + *ptuning_parm_value = + (int32_t)pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_DEVICE_TEST_MODE: + *ptuning_parm_value = + (int32_t)pdev->refspadchar.device_test_mode; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_VCSEL_PERIOD: + *ptuning_parm_value = + (int32_t)pdev->refspadchar.VL53L1_p_009; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_PHASECAL_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->refspadchar.timeout_us; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_TARGET_COUNT_RATE_MCPS: + *ptuning_parm_value = + (int32_t)pdev->refspadchar.target_count_rate_mcps; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_MIN_COUNTRATE_LIMIT_MCPS: + *ptuning_parm_value = + (int32_t)pdev->refspadchar.min_count_rate_limit_mcps; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_MAX_COUNTRATE_LIMIT_MCPS: + *ptuning_parm_value = + (int32_t)pdev->refspadchar.max_count_rate_limit_mcps; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_NUM_OF_SAMPLES: + *ptuning_parm_value = + (int32_t)pXC->num_of_samples; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_MIN_FILTER_THRESH_MM: + *ptuning_parm_value = + (int32_t)pXC->algo__crosstalk_extract_min_valid_range_mm; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_FILTER_THRESH_MM: + *ptuning_parm_value = + (int32_t)pXC->algo__crosstalk_extract_max_valid_range_mm; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_RATE_MCPS: + *ptuning_parm_value = + (int32_t)pXC->dss_config__target_total_rate_mcps; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_PHASECAL_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pXC->phasecal_config_timeout_us; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_VALID_RATE_KCPS: + *ptuning_parm_value = + (int32_t)pXC->algo__crosstalk_extract_max_valid_rate_kcps; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_SIGMA_THRESHOLD_MM: + *ptuning_parm_value = + (int32_t)pXC->algo__crosstalk_extract_max_sigma_mm; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pXC->mm_config_timeout_us; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_BIN_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pXC->range_config_timeout_us; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_DSS_RATE_MCPS: + *ptuning_parm_value = + (int32_t)pdev->offsetcal_cfg.dss_config__target_total_rate_mcps; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_PHASECAL_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->offsetcal_cfg.phasecal_config_timeout_us; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_MM_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->offsetcal_cfg.mm_config_timeout_us; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_RANGE_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->offsetcal_cfg.range_config_timeout_us; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_PRE_SAMPLES: + *ptuning_parm_value = + (int32_t)pdev->offsetcal_cfg.pre_num_of_samples; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_MM1_SAMPLES: + *ptuning_parm_value = + (int32_t)pdev->offsetcal_cfg.mm1_num_of_samples; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_MM2_SAMPLES: + *ptuning_parm_value = + (int32_t)pdev->offsetcal_cfg.mm2_num_of_samples; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_DSS_RATE_MCPS: + *ptuning_parm_value = + (int32_t)pdev->zonecal_cfg.dss_config__target_total_rate_mcps; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->zonecal_cfg.phasecal_config_timeout_us; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_DSS_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->zonecal_cfg.mm_config_timeout_us; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_NUM_SAMPLES: + *ptuning_parm_value = + (int32_t)pdev->zonecal_cfg.phasecal_num_of_samples; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_RANGE_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->zonecal_cfg.range_config_timeout_us; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_ZONE_NUM_SAMPLES: + *ptuning_parm_value = + (int32_t)pdev->zonecal_cfg.zone_num_of_samples; + break; + case VL53L1_TUNINGPARM_SPADMAP_VCSEL_PERIOD: + *ptuning_parm_value = + (int32_t)pdev->ssc_cfg.VL53L1_p_009; + break; + case VL53L1_TUNINGPARM_SPADMAP_VCSEL_START: + *ptuning_parm_value = + (int32_t)pdev->ssc_cfg.vcsel_start; + break; + case VL53L1_TUNINGPARM_SPADMAP_RATE_LIMIT_MCPS: + *ptuning_parm_value = + (int32_t)pdev->ssc_cfg.rate_limit_mcps; + break; + case VL53L1_TUNINGPARM_LITE_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_dss_target_lite_mcps; + break; + case VL53L1_TUNINGPARM_RANGING_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_dss_target_histo_mcps; + break; + case VL53L1_TUNINGPARM_MZ_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_dss_target_histo_mz_mcps; + break; + case VL53L1_TUNINGPARM_TIMED_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_dss_target_timed_mcps; + break; + case VL53L1_TUNINGPARM_LITE_PHASECAL_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_phasecal_timeout_lite_us; + break; + case VL53L1_TUNINGPARM_RANGING_LONG_PHASECAL_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_phasecal_timeout_hist_long_us; + break; + case VL53L1_TUNINGPARM_RANGING_MED_PHASECAL_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_phasecal_timeout_hist_med_us; + break; + case VL53L1_TUNINGPARM_RANGING_SHORT_PHASECAL_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_phasecal_timeout_hist_short_us; + break; + case VL53L1_TUNINGPARM_MZ_LONG_PHASECAL_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_phasecal_timeout_mz_long_us; + break; + case VL53L1_TUNINGPARM_MZ_MED_PHASECAL_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_phasecal_timeout_mz_med_us; + break; + case VL53L1_TUNINGPARM_MZ_SHORT_PHASECAL_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_phasecal_timeout_mz_short_us; + break; + case VL53L1_TUNINGPARM_TIMED_PHASECAL_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_phasecal_timeout_timed_us; + break; + case VL53L1_TUNINGPARM_LITE_MM_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_mm_timeout_lite_us; + break; + case VL53L1_TUNINGPARM_RANGING_MM_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_mm_timeout_histo_us; + break; + case VL53L1_TUNINGPARM_MZ_MM_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_mm_timeout_mz_us; + break; + case VL53L1_TUNINGPARM_TIMED_MM_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_mm_timeout_timed_us; + break; + case VL53L1_TUNINGPARM_LITE_RANGE_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_range_timeout_lite_us; + break; + case VL53L1_TUNINGPARM_RANGING_RANGE_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_range_timeout_histo_us; + break; + case VL53L1_TUNINGPARM_MZ_RANGE_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_range_timeout_mz_us; + break; + case VL53L1_TUNINGPARM_TIMED_RANGE_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_range_timeout_timed_us; + break; + case VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_MARGIN: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.smudge_margin; + break; + case VL53L1_TUNINGPARM_DYNXTALK_NOISE_MARGIN: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.noise_margin; + break; + case VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.user_xtalk_offset_limit; + break; + case VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT_HI: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.user_xtalk_offset_limit_hi; + break; + case VL53L1_TUNINGPARM_DYNXTALK_SAMPLE_LIMIT: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.sample_limit; + break; + case VL53L1_TUNINGPARM_DYNXTALK_SINGLE_XTALK_DELTA: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.single_xtalk_delta; + break; + case VL53L1_TUNINGPARM_DYNXTALK_AVERAGED_XTALK_DELTA: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.averaged_xtalk_delta; + break; + case VL53L1_TUNINGPARM_DYNXTALK_CLIP_LIMIT: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.smudge_corr_clip_limit; + break; + case VL53L1_TUNINGPARM_DYNXTALK_SCALER_CALC_METHOD: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.scaler_calc_method; + break; + case VL53L1_TUNINGPARM_DYNXTALK_XGRADIENT_SCALER: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.x_gradient_scaler; + break; + case VL53L1_TUNINGPARM_DYNXTALK_YGRADIENT_SCALER: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.y_gradient_scaler; + break; + case VL53L1_TUNINGPARM_DYNXTALK_USER_SCALER_SET: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.user_scaler_set; + break; + case VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_COR_SINGLE_APPLY: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.smudge_corr_single_apply; + break; + case VL53L1_TUNINGPARM_DYNXTALK_XTALK_AMB_THRESHOLD: + *ptuning_parm_value = (int32_t)( + pdev->smudge_correct_config.smudge_corr_ambient_threshold); + break; + case VL53L1_TUNINGPARM_DYNXTALK_NODETECT_AMB_THRESHOLD_KCPS: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.nodetect_ambient_threshold; + break; + case VL53L1_TUNINGPARM_DYNXTALK_NODETECT_SAMPLE_LIMIT: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.nodetect_sample_limit; + break; + case VL53L1_TUNINGPARM_DYNXTALK_NODETECT_XTALK_OFFSET_KCPS: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.nodetect_xtalk_offset; + break; + case VL53L1_TUNINGPARM_DYNXTALK_NODETECT_MIN_RANGE_MM: + *ptuning_parm_value = + (int32_t)pdev->smudge_correct_config.nodetect_min_range_mm; + break; + case VL53L1_TUNINGPARM_LOWPOWERAUTO_VHV_LOOP_BOUND: + *ptuning_parm_value = + (int32_t)pdev->low_power_auto_data.vhv_loop_bound; + break; + case VL53L1_TUNINGPARM_LOWPOWERAUTO_MM_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_mm_timeout_lpa_us; + break; + case VL53L1_TUNINGPARM_LOWPOWERAUTO_RANGE_CONFIG_TIMEOUT_US: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_range_timeout_lpa_us; + break; + case VL53L1_TUNINGPARM_VERY_SHORT_DSS_RATE_MCPS: + *ptuning_parm_value = + (int32_t)pdev->tuning_parms.tp_dss_target_very_short_mcps; + break; + case VL53L1_TUNINGPARM_PHASECAL_PATCH_POWER: + *ptuning_parm_value = + (int32_t) pdev->tuning_parms.tp_phasecal_patch_power; + break; + case VL53L1_TUNINGPARM_HIST_MERGE: + *ptuning_parm_value = + (int32_t) pdev->tuning_parms.tp_hist_merge; + break; + case VL53L1_TUNINGPARM_RESET_MERGE_THRESHOLD: + *ptuning_parm_value = + (int32_t) pdev->tuning_parms.tp_reset_merge_threshold; + break; + case VL53L1_TUNINGPARM_HIST_MERGE_MAX_SIZE: + *ptuning_parm_value = + (int32_t) pdev->tuning_parms.tp_hist_merge_max_size; + break; + case VL53L1_TUNINGPARM_DYNXTALK_MAX_SMUDGE_FACTOR: + *ptuning_parm_value = + pdev->smudge_correct_config.max_smudge_factor; + break; + + case VL53L1_TUNINGPARM_UWR_ENABLE: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_enable; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_1_MIN: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_z_1_min; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_1_MAX: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_z_1_max; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_2_MIN: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_z_2_min; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_2_MAX: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_z_2_max; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_3_MIN: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_z_3_min; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_3_MAX: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_z_3_max; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_4_MIN: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_z_4_min; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_4_MAX: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_z_4_max; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_5_MIN: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_z_5_min; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_5_MAX: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_z_5_max; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_6_MIN: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_z_6_min; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_6_MAX: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_z_6_max; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_1_RANGEA: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_corr_z_1_rangea; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_1_RANGEB: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_corr_z_1_rangeb; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_2_RANGEA: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_corr_z_2_rangea; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_2_RANGEB: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_corr_z_2_rangeb; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_3_RANGEA: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_corr_z_3_rangea; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_3_RANGEB: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_corr_z_3_rangeb; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_4_RANGEA: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_corr_z_4_rangea; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_4_RANGEB: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_corr_z_4_rangeb; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_5_RANGEA: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_corr_z_5_rangea; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_5_RANGEB: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_corr_z_5_rangeb; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_6_RANGEA: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_corr_z_6_rangea; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_6_RANGEB: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_med_corr_z_6_rangeb; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_1_MIN: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_z_1_min; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_1_MAX: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_z_1_max; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_2_MIN: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_z_2_min; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_2_MAX: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_z_2_max; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_3_MIN: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_z_3_min; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_3_MAX: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_z_3_max; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_4_MIN: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_z_4_min; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_4_MAX: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_z_4_max; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_5_MIN: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_z_5_min; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_5_MAX: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_z_5_max; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_1_RANGEA: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_corr_z_1_rangea; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_1_RANGEB: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_corr_z_1_rangeb; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_2_RANGEA: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_corr_z_2_rangea; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_2_RANGEB: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_corr_z_2_rangeb; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_3_RANGEA: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_corr_z_3_rangea; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_3_RANGEB: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_corr_z_3_rangeb; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_4_RANGEA: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_corr_z_4_rangea; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_4_RANGEB: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_corr_z_4_rangeb; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_5_RANGEA: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_corr_z_5_rangea; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_5_RANGEB: + *ptuning_parm_value = + pdev->tuning_parms.tp_uwr_lng_corr_z_5_rangeb; + break; + case VL53L1_TUNINGPARM_MIN_SIGNAL_SECONDARY_TARGETS: + *ptuning_parm_value = + pdev->tuning_parms.tp_min_signal_secondary_targets; + break; + + default: + *ptuning_parm_value = 0x7FFFFFFF; + status = VL53L1_ERROR_INVALID_PARAMS; + break; + + } + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_set_tuning_parm( + VL53L1_DEV Dev, + VL53L1_TuningParms tuning_parm_key, + int32_t tuning_parm_value) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_hist_post_process_config_t *pHP = &(pdev->histpostprocess); + VL53L1_xtalkextract_config_t *pXC = &(pdev->xtalk_extract_cfg); + + LOG_FUNCTION_START(""); + + switch (tuning_parm_key) { + + case VL53L1_TUNINGPARM_VERSION: + pdev->tuning_parms.tp_tuning_parm_version = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_KEY_TABLE_VERSION: + pdev->tuning_parms.tp_tuning_parm_key_table_version = + (uint16_t)tuning_parm_value; + + + + if ((uint16_t)tuning_parm_value + != VL53L1_TUNINGPARM_KEY_TABLE_VERSION_DEFAULT) + status = VL53L1_ERROR_TUNING_PARM_KEY_MISMATCH; + + break; + case VL53L1_TUNINGPARM_LLD_VERSION: + pdev->tuning_parms.tp_tuning_parm_lld_version = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_ALGO_SELECT: + pHP->hist_algo_select = + (VL53L1_HistAlgoSelect)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_TARGET_ORDER: + pHP->hist_target_order = + (VL53L1_HistTargetOrder)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_FILTER_WOI_0: + pHP->filter_woi0 = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_FILTER_WOI_1: + pHP->filter_woi1 = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_AMB_EST_METHOD: + pHP->hist_amb_est_method = + (VL53L1_HistAmbEstMethod)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_0: + pHP->ambient_thresh_sigma0 = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_1: + pHP->ambient_thresh_sigma1 = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_MIN_AMB_THRESH_EVENTS: + pHP->min_ambient_thresh_events = + (int32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_AMB_EVENTS_SCALER: + pHP->ambient_thresh_events_scaler = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_NOISE_THRESHOLD: + pHP->noise_threshold = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_SIGNAL_TOTAL_EVENTS_LIMIT: + pHP->signal_total_events_limit = + (int32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_SIGMA_EST_REF_MM: + pHP->sigma_estimator__sigma_ref_mm = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_SIGMA_THRESH_MM: + pHP->sigma_thresh = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_GAIN_FACTOR: + pdev->gain_cal.histogram_ranging_gain_factor = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_HIST_PHASE_TOLERANCE: + pHP->algo__consistency_check__phase_tolerance = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_HIST_MIN_MAX_TOLERANCE_MM: + pHP->algo__consistency_check__min_max_tolerance = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA: + pHP->algo__consistency_check__event_sigma = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA_MIN_SPAD_LIMIT: + pHP->algo__consistency_check__event_min_spad_count = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_LONG_RANGE: + pdev->tuning_parms.tp_init_phase_rtn_hist_long = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_MED_RANGE: + pdev->tuning_parms.tp_init_phase_rtn_hist_med = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_SHORT_RANGE: + pdev->tuning_parms.tp_init_phase_rtn_hist_short = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_LONG_RANGE: + pdev->tuning_parms.tp_init_phase_ref_hist_long = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_MED_RANGE: + pdev->tuning_parms.tp_init_phase_ref_hist_med = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_SHORT_RANGE: + pdev->tuning_parms.tp_init_phase_ref_hist_short = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MIN_VALID_RANGE_MM: + pdev->xtalk_cfg.algo__crosstalk_detect_min_valid_range_mm = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RANGE_MM: + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_range_mm = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MAX_SIGMA_MM: + pdev->xtalk_cfg.algo__crosstalk_detect_max_sigma_mm = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MIN_MAX_TOLERANCE: + pHP->algo__crosstalk_detect_min_max_tolerance = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RATE_KCPS: + pdev->xtalk_cfg.algo__crosstalk_detect_max_valid_rate_kcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_DETECT_EVENT_SIGMA: + pHP->algo__crosstalk_detect_event_sigma = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_XTALK_MARGIN_KCPS: + pdev->xtalk_cfg.histogram_mode_crosstalk_margin_kcps = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_CONSISTENCY_LITE_PHASE_TOLERANCE: + pdev->tuning_parms.tp_consistency_lite_phase_tolerance = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_PHASECAL_TARGET: + pdev->tuning_parms.tp_phasecal_target = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_CAL_REPEAT_RATE: + pdev->tuning_parms.tp_cal_repeat_rate = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_RANGING_GAIN_FACTOR: + pdev->gain_cal.standard_ranging_gain_factor = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_MIN_CLIP_MM: + pdev->tuning_parms.tp_lite_min_clip = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_LONG_SIGMA_THRESH_MM: + pdev->tuning_parms.tp_lite_long_sigma_thresh_mm = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_MED_SIGMA_THRESH_MM: + pdev->tuning_parms.tp_lite_med_sigma_thresh_mm = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_SHORT_SIGMA_THRESH_MM: + pdev->tuning_parms.tp_lite_short_sigma_thresh_mm = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_LONG_MIN_COUNT_RATE_RTN_MCPS: + pdev->tuning_parms.tp_lite_long_min_count_rate_rtn_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_MED_MIN_COUNT_RATE_RTN_MCPS: + pdev->tuning_parms.tp_lite_med_min_count_rate_rtn_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_SHORT_MIN_COUNT_RATE_RTN_MCPS: + pdev->tuning_parms.tp_lite_short_min_count_rate_rtn_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_SIGMA_EST_PULSE_WIDTH: + pdev->tuning_parms.tp_lite_sigma_est_pulse_width_ns = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_SIGMA_EST_AMB_WIDTH_NS: + pdev->tuning_parms.tp_lite_sigma_est_amb_width_ns = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_SIGMA_REF_MM: + pdev->tuning_parms.tp_lite_sigma_ref_mm = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_RIT_MULT: + pdev->xtalk_cfg.crosstalk_range_ignore_threshold_mult = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_SEED_CONFIG: + pdev->tuning_parms.tp_lite_seed_cfg = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_QUANTIFIER: + pdev->tuning_parms.tp_lite_quantifier = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_FIRST_ORDER_SELECT: + pdev->tuning_parms.tp_lite_first_order_select = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_XTALK_MARGIN_KCPS: + pdev->xtalk_cfg.lite_mode_crosstalk_margin_kcps = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_LONG_RANGE: + pdev->tuning_parms.tp_init_phase_rtn_lite_long = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_MED_RANGE: + pdev->tuning_parms.tp_init_phase_rtn_lite_med = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_SHORT_RANGE: + pdev->tuning_parms.tp_init_phase_rtn_lite_short = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_LONG_RANGE: + pdev->tuning_parms.tp_init_phase_ref_lite_long = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_MED_RANGE: + pdev->tuning_parms.tp_init_phase_ref_lite_med = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_SHORT_RANGE: + pdev->tuning_parms.tp_init_phase_ref_lite_short = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_TIMED_SEED_CONFIG: + pdev->tuning_parms.tp_timed_seed_cfg = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_SIGNAL_THRESH_SIGMA: + pdev->dmax_cfg.signal_thresh_sigma = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_0: + pdev->dmax_cfg.target_reflectance_for_dmax_calc[0] = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_1: + pdev->dmax_cfg.target_reflectance_for_dmax_calc[1] = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_2: + pdev->dmax_cfg.target_reflectance_for_dmax_calc[2] = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_3: + pdev->dmax_cfg.target_reflectance_for_dmax_calc[3] = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_4: + pdev->dmax_cfg.target_reflectance_for_dmax_calc[4] = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_VHV_LOOPBOUND: + pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_DEVICE_TEST_MODE: + pdev->refspadchar.device_test_mode = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_VCSEL_PERIOD: + pdev->refspadchar.VL53L1_p_009 = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_PHASECAL_TIMEOUT_US: + pdev->refspadchar.timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_TARGET_COUNT_RATE_MCPS: + pdev->refspadchar.target_count_rate_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_MIN_COUNTRATE_LIMIT_MCPS: + pdev->refspadchar.min_count_rate_limit_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_REFSPADCHAR_MAX_COUNTRATE_LIMIT_MCPS: + pdev->refspadchar.max_count_rate_limit_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_NUM_OF_SAMPLES: + pXC->num_of_samples = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_MIN_FILTER_THRESH_MM: + pXC->algo__crosstalk_extract_min_valid_range_mm = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_FILTER_THRESH_MM: + pXC->algo__crosstalk_extract_max_valid_range_mm = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_RATE_MCPS: + pXC->dss_config__target_total_rate_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_PHASECAL_TIMEOUT_US: + pXC->phasecal_config_timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_VALID_RATE_KCPS: + pXC->algo__crosstalk_extract_max_valid_rate_kcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_SIGMA_THRESHOLD_MM: + pXC->algo__crosstalk_extract_max_sigma_mm = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_TIMEOUT_US: + pXC->mm_config_timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_XTALK_EXTRACT_BIN_TIMEOUT_US: + pXC->range_config_timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_DSS_RATE_MCPS: + pdev->offsetcal_cfg.dss_config__target_total_rate_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_PHASECAL_TIMEOUT_US: + pdev->offsetcal_cfg.phasecal_config_timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_MM_TIMEOUT_US: + pdev->offsetcal_cfg.mm_config_timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_RANGE_TIMEOUT_US: + pdev->offsetcal_cfg.range_config_timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_PRE_SAMPLES: + pdev->offsetcal_cfg.pre_num_of_samples = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_MM1_SAMPLES: + pdev->offsetcal_cfg.mm1_num_of_samples = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_OFFSET_CAL_MM2_SAMPLES: + pdev->offsetcal_cfg.mm2_num_of_samples = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_DSS_RATE_MCPS: + pdev->zonecal_cfg.dss_config__target_total_rate_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_TIMEOUT_US: + pdev->zonecal_cfg.phasecal_config_timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_DSS_TIMEOUT_US: + pdev->zonecal_cfg.mm_config_timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_NUM_SAMPLES: + pdev->zonecal_cfg.phasecal_num_of_samples = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_RANGE_TIMEOUT_US: + pdev->zonecal_cfg.range_config_timeout_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_ZONE_CAL_ZONE_NUM_SAMPLES: + pdev->zonecal_cfg.zone_num_of_samples = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_SPADMAP_VCSEL_PERIOD: + pdev->ssc_cfg.VL53L1_p_009 = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_SPADMAP_VCSEL_START: + pdev->ssc_cfg.vcsel_start = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_SPADMAP_RATE_LIMIT_MCPS: + pdev->ssc_cfg.rate_limit_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS: + pdev->tuning_parms.tp_dss_target_lite_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_RANGING_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS: + pdev->tuning_parms.tp_dss_target_histo_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_MZ_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS: + pdev->tuning_parms.tp_dss_target_histo_mz_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_TIMED_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS: + pdev->tuning_parms.tp_dss_target_timed_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_PHASECAL_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_phasecal_timeout_lite_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_RANGING_LONG_PHASECAL_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_phasecal_timeout_hist_long_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_RANGING_MED_PHASECAL_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_phasecal_timeout_hist_med_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_RANGING_SHORT_PHASECAL_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_phasecal_timeout_hist_short_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_MZ_LONG_PHASECAL_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_phasecal_timeout_mz_long_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_MZ_MED_PHASECAL_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_phasecal_timeout_mz_med_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_MZ_SHORT_PHASECAL_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_phasecal_timeout_mz_short_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_TIMED_PHASECAL_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_phasecal_timeout_timed_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_MM_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_mm_timeout_lite_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_RANGING_MM_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_mm_timeout_histo_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_MZ_MM_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_mm_timeout_mz_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_TIMED_MM_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_mm_timeout_timed_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LITE_RANGE_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_range_timeout_lite_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_RANGING_RANGE_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_range_timeout_histo_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_MZ_RANGE_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_range_timeout_mz_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_TIMED_RANGE_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_range_timeout_timed_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_MARGIN: + pdev->smudge_correct_config.smudge_margin = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_NOISE_MARGIN: + pdev->smudge_correct_config.noise_margin = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT: + pdev->smudge_correct_config.user_xtalk_offset_limit = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT_HI: + pdev->smudge_correct_config.user_xtalk_offset_limit_hi = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_SAMPLE_LIMIT: + pdev->smudge_correct_config.sample_limit = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_SINGLE_XTALK_DELTA: + pdev->smudge_correct_config.single_xtalk_delta = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_AVERAGED_XTALK_DELTA: + pdev->smudge_correct_config.averaged_xtalk_delta = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_CLIP_LIMIT: + pdev->smudge_correct_config.smudge_corr_clip_limit = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_SCALER_CALC_METHOD: + pdev->smudge_correct_config.scaler_calc_method = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_XGRADIENT_SCALER: + pdev->smudge_correct_config.x_gradient_scaler = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_YGRADIENT_SCALER: + pdev->smudge_correct_config.y_gradient_scaler = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_USER_SCALER_SET: + pdev->smudge_correct_config.user_scaler_set = + (uint8_t)tuning_parm_value; + break; + + case VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_COR_SINGLE_APPLY: + pdev->smudge_correct_config.smudge_corr_single_apply = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_XTALK_AMB_THRESHOLD: + pdev->smudge_correct_config.smudge_corr_ambient_threshold = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_NODETECT_AMB_THRESHOLD_KCPS: + pdev->smudge_correct_config.nodetect_ambient_threshold = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_NODETECT_SAMPLE_LIMIT: + pdev->smudge_correct_config.nodetect_sample_limit = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_NODETECT_XTALK_OFFSET_KCPS: + pdev->smudge_correct_config.nodetect_xtalk_offset = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_NODETECT_MIN_RANGE_MM: + pdev->smudge_correct_config.nodetect_min_range_mm = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LOWPOWERAUTO_VHV_LOOP_BOUND: + pdev->low_power_auto_data.vhv_loop_bound = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LOWPOWERAUTO_MM_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_mm_timeout_lpa_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_LOWPOWERAUTO_RANGE_CONFIG_TIMEOUT_US: + pdev->tuning_parms.tp_range_timeout_lpa_us = + (uint32_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_VERY_SHORT_DSS_RATE_MCPS: + pdev->tuning_parms.tp_dss_target_very_short_mcps = + (uint16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_PHASECAL_PATCH_POWER: + pdev->tuning_parms.tp_phasecal_patch_power = + (uint16_t) tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_MERGE: + pdev->tuning_parms.tp_hist_merge = + (uint16_t) tuning_parm_value; + break; + case VL53L1_TUNINGPARM_RESET_MERGE_THRESHOLD: + pdev->tuning_parms.tp_reset_merge_threshold = + (uint16_t) tuning_parm_value; + break; + case VL53L1_TUNINGPARM_HIST_MERGE_MAX_SIZE: + pdev->tuning_parms.tp_hist_merge_max_size = + (uint16_t) tuning_parm_value; + break; + case VL53L1_TUNINGPARM_DYNXTALK_MAX_SMUDGE_FACTOR: + pdev->smudge_correct_config.max_smudge_factor = + (uint32_t)tuning_parm_value; + break; + + case VL53L1_TUNINGPARM_UWR_ENABLE: + pdev->tuning_parms.tp_uwr_enable = + (uint8_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_1_MIN: + pdev->tuning_parms.tp_uwr_med_z_1_min = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_1_MAX: + pdev->tuning_parms.tp_uwr_med_z_1_max = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_2_MIN: + pdev->tuning_parms.tp_uwr_med_z_2_min = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_2_MAX: + pdev->tuning_parms.tp_uwr_med_z_2_max = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_3_MIN: + pdev->tuning_parms.tp_uwr_med_z_3_min = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_3_MAX: + pdev->tuning_parms.tp_uwr_med_z_3_max = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_4_MIN: + pdev->tuning_parms.tp_uwr_med_z_4_min = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_4_MAX: + pdev->tuning_parms.tp_uwr_med_z_4_max = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_5_MIN: + pdev->tuning_parms.tp_uwr_med_z_5_min = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_5_MAX: + pdev->tuning_parms.tp_uwr_med_z_5_max = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_6_MIN: + pdev->tuning_parms.tp_uwr_med_z_6_min = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_6_MAX: + pdev->tuning_parms.tp_uwr_med_z_6_max = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_1_RANGEA: + pdev->tuning_parms.tp_uwr_med_corr_z_1_rangea = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_1_RANGEB: + pdev->tuning_parms.tp_uwr_med_corr_z_1_rangeb = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_2_RANGEA: + pdev->tuning_parms.tp_uwr_med_corr_z_2_rangea = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_2_RANGEB: + pdev->tuning_parms.tp_uwr_med_corr_z_2_rangeb = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_3_RANGEA: + pdev->tuning_parms.tp_uwr_med_corr_z_3_rangea = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_3_RANGEB: + pdev->tuning_parms.tp_uwr_med_corr_z_3_rangeb = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_4_RANGEA: + pdev->tuning_parms.tp_uwr_med_corr_z_4_rangea = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_4_RANGEB: + pdev->tuning_parms.tp_uwr_med_corr_z_4_rangeb = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_5_RANGEA: + pdev->tuning_parms.tp_uwr_med_corr_z_5_rangea = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_5_RANGEB: + pdev->tuning_parms.tp_uwr_med_corr_z_5_rangeb = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_6_RANGEA: + pdev->tuning_parms.tp_uwr_med_corr_z_6_rangea = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_6_RANGEB: + pdev->tuning_parms.tp_uwr_med_corr_z_6_rangeb = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_1_MIN: + pdev->tuning_parms.tp_uwr_lng_z_1_min = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_1_MAX: + pdev->tuning_parms.tp_uwr_lng_z_1_max = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_2_MIN: + pdev->tuning_parms.tp_uwr_lng_z_2_min = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_2_MAX: + pdev->tuning_parms.tp_uwr_lng_z_2_max = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_3_MIN: + pdev->tuning_parms.tp_uwr_lng_z_3_min = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_3_MAX: + pdev->tuning_parms.tp_uwr_lng_z_3_max = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_4_MIN: + pdev->tuning_parms.tp_uwr_lng_z_4_min = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_4_MAX: + pdev->tuning_parms.tp_uwr_lng_z_4_max = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_5_MIN: + pdev->tuning_parms.tp_uwr_lng_z_5_min = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_ZONE_5_MAX: + pdev->tuning_parms.tp_uwr_lng_z_5_max = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_1_RANGEA: + pdev->tuning_parms.tp_uwr_lng_corr_z_1_rangea = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_1_RANGEB: + pdev->tuning_parms.tp_uwr_lng_corr_z_1_rangeb = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_2_RANGEA: + pdev->tuning_parms.tp_uwr_lng_corr_z_2_rangea = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_2_RANGEB: + pdev->tuning_parms.tp_uwr_lng_corr_z_2_rangeb = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_3_RANGEA: + pdev->tuning_parms.tp_uwr_lng_corr_z_3_rangea = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_3_RANGEB: + pdev->tuning_parms.tp_uwr_lng_corr_z_3_rangeb = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_4_RANGEA: + pdev->tuning_parms.tp_uwr_lng_corr_z_4_rangea = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_4_RANGEB: + pdev->tuning_parms.tp_uwr_lng_corr_z_4_rangeb = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_5_RANGEA: + pdev->tuning_parms.tp_uwr_lng_corr_z_5_rangea = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_5_RANGEB: + pdev->tuning_parms.tp_uwr_lng_corr_z_5_rangeb = + (int16_t)tuning_parm_value; + break; + case VL53L1_TUNINGPARM_MIN_SIGNAL_SECONDARY_TARGETS: + pdev->tuning_parms.tp_min_signal_secondary_targets = + (uint32_t)tuning_parm_value; + break; + + default: + status = VL53L1_ERROR_INVALID_PARAMS; + break; + + } + + LOG_FUNCTION_END(status); + + return status; +} + + + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_enable( + VL53L1_DEV Dev + ) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->smudge_correct_config.smudge_corr_enabled = 1; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_dynamic_xtalk_correction_disable( + VL53L1_DEV Dev + ) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->smudge_correct_config.smudge_corr_enabled = 0; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_dynamic_xtalk_correction_apply_enable( + VL53L1_DEV Dev + ) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->smudge_correct_config.smudge_corr_apply_enabled = 1; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_dynamic_xtalk_correction_apply_disable( + VL53L1_DEV Dev + ) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->smudge_correct_config.smudge_corr_apply_enabled = 0; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_dynamic_xtalk_correction_single_apply_enable( + VL53L1_DEV Dev + ) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->smudge_correct_config.smudge_corr_single_apply = 1; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_dynamic_xtalk_correction_single_apply_disable( + VL53L1_DEV Dev + ) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->smudge_correct_config.smudge_corr_single_apply = 0; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_set_scalers( + VL53L1_DEV Dev, + int16_t x_scaler_in, + int16_t y_scaler_in, + uint8_t user_scaler_set_in + ) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->smudge_correct_config.x_gradient_scaler = x_scaler_in; + pdev->smudge_correct_config.y_gradient_scaler = y_scaler_in; + pdev->smudge_correct_config.user_scaler_set = user_scaler_set_in; + + LOG_FUNCTION_END(status); + + return status; +} + + + + + + +VL53L1_Error VL53L1_get_current_xtalk_settings( + VL53L1_DEV Dev, + VL53L1_xtalk_calibration_results_t *pxtalk + ) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t i; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pxtalk->algo__crosstalk_compensation_plane_offset_kcps = + pdev->xtalk_cfg.algo__crosstalk_compensation_plane_offset_kcps; + pxtalk->algo__crosstalk_compensation_x_plane_gradient_kcps = + pdev->xtalk_cfg.algo__crosstalk_compensation_x_plane_gradient_kcps; + pxtalk->algo__crosstalk_compensation_y_plane_gradient_kcps = + pdev->xtalk_cfg.algo__crosstalk_compensation_y_plane_gradient_kcps; + for (i = 0; i < VL53L1_BIN_REC_SIZE; i++) + pxtalk->algo__xtalk_cpo_HistoMerge_kcps[i] = + pdev->xtalk_cal.algo__xtalk_cpo_HistoMerge_kcps[i]; + + LOG_FUNCTION_END(status); + + return status; + +} + + + + + +VL53L1_Error VL53L1_set_current_xtalk_settings( + VL53L1_DEV Dev, + VL53L1_xtalk_calibration_results_t *pxtalk + ) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t i; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->xtalk_cfg.algo__crosstalk_compensation_plane_offset_kcps = + pxtalk->algo__crosstalk_compensation_plane_offset_kcps; + pdev->xtalk_cfg.algo__crosstalk_compensation_x_plane_gradient_kcps = + pxtalk->algo__crosstalk_compensation_x_plane_gradient_kcps; + pdev->xtalk_cfg.algo__crosstalk_compensation_y_plane_gradient_kcps = + pxtalk->algo__crosstalk_compensation_y_plane_gradient_kcps; + for (i = 0; i < VL53L1_BIN_REC_SIZE; i++) + pdev->xtalk_cal.algo__xtalk_cpo_HistoMerge_kcps[i] = + pxtalk->algo__xtalk_cpo_HistoMerge_kcps[i]; + + LOG_FUNCTION_END(status); + + return status; + +} + + + diff --git a/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_api_debug.c b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_api_debug.c new file mode 100644 index 000000000000..c84618bb94e5 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_api_debug.c @@ -0,0 +1,3399 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#include "vl53l1_ll_def.h" +#include "vl53l1_ll_device.h" +#include "vl53l1_register_structs.h" +#include "vl53l1_hist_structs.h" +#include "vl53l1_nvm_structs.h" +#include "vl53l1_nvm.h" +#include "vl53l1_core.h" +#include "vl53l1_api_debug.h" + +#ifdef VL53L1_LOG_ENABLE +#include "vl53l1_nvm_debug.h" +#endif + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_CORE, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_CORE, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_CORE, status, \ + fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(trace_flags, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_decode_calibration_data_buffer( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_calibration_data_t *pdata) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (sizeof(VL53L1_calibration_data_t) > buf_size) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + memcpy(pdata, pbuffer, sizeof(VL53L1_calibration_data_t)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_nvm_debug_data( + VL53L1_DEV Dev, + VL53L1_decoded_nvm_data_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + status = VL53L1_read_nvm(Dev, 0, pdata); + +#ifdef VL53L1_LOG_ENABLE + if (status == VL53L1_ERROR_NONE) + VL53L1_print_decoded_nvm_data( + pdata, + "get_nvm_debug_data():pnvm_info.", + VL53L1_TRACE_MODULE_NVM_DATA); +#endif + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_histogram_debug_data( + VL53L1_DEV Dev, + VL53L1_histogram_bin_data_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + memcpy( + pdata, + &(pdev->hist_data), + sizeof(VL53L1_histogram_bin_data_t)); + + LOG_FUNCTION_END(status); + + return status; +} + + + + +VL53L1_Error VL53L1_get_additional_data( + VL53L1_DEV Dev, + VL53L1_additional_data_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + pdata->preset_mode = pdev->preset_mode; + pdata->zone_preset = pdev->zone_preset; + pdata->measurement_mode = pdev->measurement_mode; + pdata->offset_calibration_mode = pdev->offset_calibration_mode; + pdata->offset_correction_mode = pdev->offset_correction_mode; + pdata->dmax_mode = pdev->dmax_mode; + + pdata->phasecal_config_timeout_us = pdev->phasecal_config_timeout_us; + pdata->mm_config_timeout_us = pdev->mm_config_timeout_us; + pdata->range_config_timeout_us = pdev->range_config_timeout_us; + pdata->inter_measurement_period_ms = pdev->inter_measurement_period_ms; + pdata->dss_config__target_total_rate_mcps = + pdev->dss_config__target_total_rate_mcps; + + + + status = + VL53L1_get_histogram_debug_data( + Dev, + &(pdata->VL53L1_p_010)); + + LOG_FUNCTION_END(status); + + return status; +} + + + + +VL53L1_Error VL53L1_get_xtalk_debug_data( + VL53L1_DEV Dev, + VL53L1_xtalk_debug_data_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + memcpy( + &(pdata->customer), + &(pdev->customer), + sizeof(VL53L1_customer_nvm_managed_t)); + + memcpy( + &(pdata->xtalk_cfg), + &(pdev->xtalk_cfg), + sizeof(VL53L1_xtalk_config_t)); + + memcpy( + &(pdata->hist_data), + &(pdev->hist_data), + sizeof(VL53L1_histogram_bin_data_t)); + + memcpy( + &(pdata->xtalk_shapes), + &(pdev->xtalk_shapes), + sizeof(VL53L1_xtalk_histogram_data_t)); + + memcpy( + &(pdata->xtalk_results), + &(pdev->xtalk_results), + sizeof(VL53L1_xtalk_range_results_t)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_offset_debug_data( + VL53L1_DEV Dev, + VL53L1_offset_debug_data_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + memcpy( + &(pdata->customer), + &(pdev->customer), + sizeof(VL53L1_customer_nvm_managed_t)); + + memcpy( + &(pdata->fmt_dmax_cal), + &(pdev->fmt_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + memcpy( + &(pdata->cust_dmax_cal), + &(pdev->cust_dmax_cal), + sizeof(VL53L1_dmax_calibration_data_t)); + + memcpy( + &(pdata->add_off_cal_data), + &(pdev->add_off_cal_data), + sizeof(VL53L1_additional_offset_cal_data_t)); + + memcpy( + &(pdata->offset_results), + &(pdev->offset_results), + sizeof(VL53L1_offset_range_results_t)); + + LOG_FUNCTION_END(status); + + return status; +} + +#ifdef VL53L1_LOG_ENABLE + +void VL53L1_signed_fixed_point_sprintf( + int32_t signed_fp_value, + uint8_t frac_bits, + uint16_t buf_size, + char *pbuffer) +{ + + + uint32_t fp_value = 0; + uint32_t unity_fp_value = 0; + uint32_t sign_bit = 0; + uint32_t int_part = 0; + uint32_t frac_part = 0; + uint32_t dec_points = 0; + uint32_t dec_scaler = 0; + uint32_t dec_part = 0; + + uint64_t tmp_long_int = 0; + + char fmt[VL53L1_MAX_STRING_LENGTH]; + + SUPPRESS_UNUSED_WARNING(buf_size); + + + + sign_bit = signed_fp_value >> 31; + + if (sign_bit > 0) { + fp_value = 0x80000000 - + (0x7FFFFFFF & (uint32_t)signed_fp_value); + } else + fp_value = (uint32_t)signed_fp_value; + + int_part = fp_value >> frac_bits; + unity_fp_value = 0x01 << frac_bits; + frac_part = fp_value & (unity_fp_value-1); + + + dec_points = 2; + dec_scaler = 100; + + while (dec_scaler < unity_fp_value) { + dec_points++; + dec_scaler *= 10; + } + + + if (sign_bit > 0) + sprintf(fmt, "-%%u.%%0%uu", dec_points); + else + sprintf(fmt, "%%u.%%0%uu", dec_points); + + + tmp_long_int = (uint64_t)frac_part * (uint64_t)dec_scaler; + tmp_long_int += (uint64_t)unity_fp_value/2; + + tmp_long_int = do_division_u(tmp_long_int, (uint64_t)unity_fp_value); + + dec_part = (uint32_t)tmp_long_int; + + + sprintf( + pbuffer, + fmt, + int_part, + dec_part); +} + + +void VL53L1_print_static_nvm_managed( + VL53L1_static_nvm_managed_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = 0x%02X\n", + pprefix, + "i2c_slave__device_address", + pdata->i2c_slave__device_address); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ana_config__vhv_ref_sel_vddpix", + pdata->ana_config__vhv_ref_sel_vddpix); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ana_config__vhv_ref_sel_vquench", + pdata->ana_config__vhv_ref_sel_vquench); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ana_config__reg_avdd1v2_sel", + pdata->ana_config__reg_avdd1v2_sel); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ana_config__fast_osc__trim", + pdata->ana_config__fast_osc__trim); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->osc_measured__fast_osc__frequency, + 12, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "osc_measured__fast_osc__frequency", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "vhv_config__timeout_macrop_loop_bound", + pdata->vhv_config__timeout_macrop_loop_bound); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "vhv_config__count_thresh", + pdata->vhv_config__count_thresh); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "vhv_config__offset", + pdata->vhv_config__offset); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "vhv_config__init", + pdata->vhv_config__init); +} + + +void VL53L1_print_customer_nvm_managed( + VL53L1_customer_nvm_managed_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + int16_t tmpi16; + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_ref_0", + pdata->global_config__spad_enables_ref_0); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_ref_1", + pdata->global_config__spad_enables_ref_1); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_ref_2", + pdata->global_config__spad_enables_ref_2); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_ref_3", + pdata->global_config__spad_enables_ref_3); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_ref_4", + pdata->global_config__spad_enables_ref_4); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_ref_5", + pdata->global_config__spad_enables_ref_5); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__ref_en_start_select", + pdata->global_config__ref_en_start_select); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ref_spad_man__num_requested_ref_spads", + pdata->ref_spad_man__num_requested_ref_spads); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ref_spad_man__ref_location", + pdata->ref_spad_man__ref_location); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->algo__crosstalk_compensation_plane_offset_kcps, + 9, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_compensation_plane_offset_kcps", + fp_text); + + tmpi16 = pdata->algo__crosstalk_compensation_x_plane_gradient_kcps; + VL53L1_signed_fixed_point_sprintf( + (int32_t)tmpi16, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_compensation_x_plane_gradient_kcps", + fp_text); + + tmpi16 = pdata->algo__crosstalk_compensation_y_plane_gradient_kcps; + VL53L1_signed_fixed_point_sprintf( + (int32_t)tmpi16, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_compensation_y_plane_gradient_kcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->ref_spad_char__total_rate_target_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "ref_spad_char__total_rate_target_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->algo__part_to_part_range_offset_mm, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__part_to_part_range_offset_mm", + fp_text); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "mm_config__inner_offset_mm", + pdata->mm_config__inner_offset_mm); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "mm_config__outer_offset_mm", + pdata->mm_config__outer_offset_mm); +} + + +void VL53L1_print_nvm_copy_data( + VL53L1_nvm_copy_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "identification__model_id", + pdata->identification__model_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "identification__module_type", + pdata->identification__module_type); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "identification__revision_id", + pdata->identification__revision_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "identification__module_id", + pdata->identification__module_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ana_config__fast_osc__trim_max", + pdata->ana_config__fast_osc__trim_max); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ana_config__fast_osc__freq_set", + pdata->ana_config__fast_osc__freq_set); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ana_config__vcsel_trim", + pdata->ana_config__vcsel_trim); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ana_config__vcsel_selion", + pdata->ana_config__vcsel_selion); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ana_config__vcsel_selion_max", + pdata->ana_config__vcsel_selion_max); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "protected_laser_safety__lock_bit", + pdata->protected_laser_safety__lock_bit); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "laser_safety__key", + pdata->laser_safety__key); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "laser_safety__key_ro", + pdata->laser_safety__key_ro); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "laser_safety__clip", + pdata->laser_safety__clip); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "laser_safety__mult", + pdata->laser_safety__mult); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_0", + pdata->global_config__spad_enables_rtn_0); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_1", + pdata->global_config__spad_enables_rtn_1); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_2", + pdata->global_config__spad_enables_rtn_2); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_3", + pdata->global_config__spad_enables_rtn_3); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_4", + pdata->global_config__spad_enables_rtn_4); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_5", + pdata->global_config__spad_enables_rtn_5); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_6", + pdata->global_config__spad_enables_rtn_6); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_7", + pdata->global_config__spad_enables_rtn_7); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_8", + pdata->global_config__spad_enables_rtn_8); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_9", + pdata->global_config__spad_enables_rtn_9); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_10", + pdata->global_config__spad_enables_rtn_10); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_11", + pdata->global_config__spad_enables_rtn_11); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_12", + pdata->global_config__spad_enables_rtn_12); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_13", + pdata->global_config__spad_enables_rtn_13); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_14", + pdata->global_config__spad_enables_rtn_14); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_15", + pdata->global_config__spad_enables_rtn_15); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_16", + pdata->global_config__spad_enables_rtn_16); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_17", + pdata->global_config__spad_enables_rtn_17); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_18", + pdata->global_config__spad_enables_rtn_18); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_19", + pdata->global_config__spad_enables_rtn_19); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_20", + pdata->global_config__spad_enables_rtn_20); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_21", + pdata->global_config__spad_enables_rtn_21); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_22", + pdata->global_config__spad_enables_rtn_22); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_23", + pdata->global_config__spad_enables_rtn_23); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_24", + pdata->global_config__spad_enables_rtn_24); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_25", + pdata->global_config__spad_enables_rtn_25); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_26", + pdata->global_config__spad_enables_rtn_26); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_27", + pdata->global_config__spad_enables_rtn_27); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_28", + pdata->global_config__spad_enables_rtn_28); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_29", + pdata->global_config__spad_enables_rtn_29); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_30", + pdata->global_config__spad_enables_rtn_30); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_config__spad_enables_rtn_31", + pdata->global_config__spad_enables_rtn_31); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "roi_config__mode_roi_centre_spad", + pdata->roi_config__mode_roi_centre_spad); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = 0x%02X\n", + pprefix, + "roi_config__mode_roi_xy_size", + pdata->roi_config__mode_roi_xy_size); +} + + +void VL53L1_print_histogram_bin_data( + VL53L1_histogram_bin_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + uint8_t i = 0; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cfg_device_state", + pdata->cfg_device_state); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "rd_device_state", + pdata->rd_device_state); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_022", + pdata->VL53L1_p_022); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_023", + pdata->VL53L1_p_023); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_024", + pdata->VL53L1_p_024); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "number_of_ambient_bins", + pdata->number_of_ambient_bins); + + for (i = 0; i < VL53L1_MAX_BIN_SEQUENCE_LENGTH; i++) { + sprintf(ppre_text, "%sbin_seq[%u]", pprefix, i); + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->bin_seq[i]); + } + + for (i = 0; i < VL53L1_MAX_BIN_SEQUENCE_LENGTH; i++) { + sprintf(ppre_text, "%sbin_rep[%u]", pprefix, i); + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->bin_rep[i]); + } + + for (i = 0; i < pdata->VL53L1_p_024; i++) { + sprintf(ppre_text, "%sbin_data[%u]", pprefix, i); + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %d\n", + ppre_text, + pdata->bin_data[i]); + } + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "result__interrupt_status", + pdata->result__interrupt_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "result__range_status", + pdata->result__range_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "result__report_status", + pdata->result__report_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "result__stream_count", + pdata->result__stream_count); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__dss_actual_effective_spads, + 8, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "result__dss_actual_effective_spads", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->phasecal_result__reference_phase, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "phasecal_result__reference_phase", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "phasecal_result__vcsel_start", + pdata->phasecal_result__vcsel_start); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cal_config__vcsel_start", + pdata->cal_config__vcsel_start); + + VL53L1_signed_fixed_point_sprintf( + (uint32_t)pdata->vcsel_width, + 4, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "vcsel_width", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_009", + pdata->VL53L1_p_009); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_019, + 12, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_019", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "total_periods_elapsed", + pdata->total_periods_elapsed); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "peak_duration_us", + pdata->peak_duration_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "woi_duration_us", + pdata->woi_duration_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "min_bin_value", + pdata->min_bin_value); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "max_bin_value", + pdata->max_bin_value); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->zero_distance_phase, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "zero_distance_phase", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "number_of_ambient_samples", + pdata->number_of_ambient_samples); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "ambient_events_sum", + pdata->ambient_events_sum); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "VL53L1_p_004", + pdata->VL53L1_p_004); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = 0x%02X\n", + pprefix, + "roi_config__user_roi_centre_spad", + pdata->roi_config__user_roi_centre_spad); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = 0x%02X\n", + pprefix, + "roi_config__user_roi_requested_global_xy_size", + pdata->roi_config__user_roi_requested_global_xy_size); +} + + +void VL53L1_print_xtalk_histogram_shape_data( + VL53L1_xtalk_histogram_shape_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + uint8_t i = 0; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_022", + pdata->VL53L1_p_022); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_023", + pdata->VL53L1_p_023); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_024", + pdata->VL53L1_p_024); + + for (i = 0; i < pdata->VL53L1_p_024; i++) { + + sprintf(ppre_text, "%sbin_data[%u]", pprefix, i); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->bin_data[i], + 10, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %s\n", + ppre_text, + fp_text); + } + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->phasecal_result__reference_phase, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "phasecal_result__reference_phase", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "phasecal_result__vcsel_start", + pdata->phasecal_result__vcsel_start); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cal_config__vcsel_start", + pdata->cal_config__vcsel_start); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->vcsel_width, + 4, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "vcsel_width", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_019, + 12, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_019", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->zero_distance_phase, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "zero_distance_phase", + fp_text); +} + + +void VL53L1_print_xtalk_histogram_data( + VL53L1_xtalk_histogram_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + + sprintf(ppre_text, "%sxtalk_shape.", pprefix); + VL53L1_print_xtalk_histogram_shape_data( + &(pdata->xtalk_shape), + ppre_text, trace_flags); + + + sprintf(ppre_text, "%sxtalk_hist_removed.", pprefix); + VL53L1_print_histogram_bin_data( + &(pdata->xtalk_hist_removed), + ppre_text, trace_flags); +} + + +void VL53L1_print_range_data( + VL53L1_range_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "range_id", + pdata->range_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "time_stamp", + pdata->time_stamp); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_015", + pdata->VL53L1_p_015); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_022", + pdata->VL53L1_p_022); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_025", + pdata->VL53L1_p_025); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_026", + pdata->VL53L1_p_026); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_016", + pdata->VL53L1_p_016); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_027", + pdata->VL53L1_p_027); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->width, + 4, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "width", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_030", + pdata->VL53L1_p_030); + + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->fast_osc_frequency, + 12, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "fast_osc_frequency", + fp_text); + + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->zero_distance_phase, + 11, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "zero_distance_phase", + fp_text); + + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_006, + 8, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "actual_effective_spad", + fp_text); + + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "total_periods_elapsed", + pdata->total_periods_elapsed); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "peak_duration_us", + pdata->peak_duration_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "woi_duration_us", + pdata->woi_duration_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "VL53L1_p_020", + pdata->VL53L1_p_020); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "VL53L1_p_021", + pdata->VL53L1_p_021); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "VL53L1_p_013", + pdata->VL53L1_p_013); + + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->peak_signal_count_rate_mcps, + 7, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "peak_signal_count_rate_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->avg_signal_count_rate_mcps, + 7, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "avg_signal_count_rate_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->ambient_count_rate_mcps, + 7, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "ambient_count_rate_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->total_rate_per_spad_mcps, + 13, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "total_rate_per_spad_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_012, + 11, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_012", + fp_text); + + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_005, + 2, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_005", + fp_text); + + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_028, + 11, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_028", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_014, + 11, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_014", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_029, + 11, VL53L1_MAX_STRING_LENGTH, fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_029", + fp_text); + + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "min_range_mm", + pdata->min_range_mm); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "median_range_mm", + pdata->median_range_mm); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "max_range_mm", + pdata->max_range_mm); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "range_status", + pdata->range_status); +} + + +void VL53L1_print_range_results( + VL53L1_range_results_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + uint8_t i = 0; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cfg_device_state", + pdata->cfg_device_state); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "rd_device_state", + pdata->rd_device_state); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "zone_id", + pdata->zone_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "stream_count", + pdata->stream_count); + + for (i = 0; i < VL53L1_MAX_AMBIENT_DMAX_VALUES; i++) { + sprintf( + ppre_text, + "%sambient_dmax_mm[%u]", + pprefix, i); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->VL53L1_p_007[i]); + } + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "device_status", + pdata->device_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "wrap_dmax_mm", + pdata->wrap_dmax_mm); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "max_results", + pdata->max_results); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "active_results", + pdata->active_results); + + for (i = 0; i < pdata->active_results; i++) { + sprintf(ppre_text, "%sdata[%u].", pprefix, i); + VL53L1_print_range_data( + &pdata->VL53L1_p_002[i], + ppre_text, trace_flags); + } + + sprintf(ppre_text, "%sxmonitor.", pprefix); + VL53L1_print_range_data( + &pdata->xmonitor, + ppre_text, trace_flags); +} + + +void VL53L1_print_offset_range_results( + VL53L1_offset_range_results_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + uint8_t i = 0; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cal_distance_mm", + pdata->cal_distance_mm); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->cal_reflectance_pc, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "cal_reflectance_pc", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cal_status", + pdata->cal_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cal_report", + pdata->cal_report); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "max_results", + pdata->max_results); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "active_results", + pdata->active_results); + + for (i = 0; i < pdata->active_results; i++) { + sprintf(ppre_text, "%sdata[%u].", pprefix, i); + VL53L1_print_offset_range_data( + &(pdata->VL53L1_p_002[i]), + ppre_text, trace_flags); + } +} + + +void VL53L1_print_offset_range_data( + VL53L1_offset_range_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "preset_mode", + pdata->preset_mode); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "dss_config__roi_mode_control", + pdata->dss_config__roi_mode_control); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->dss_config__manual_effective_spads_select, + 8, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "dss_config__manual_effective_spads_select", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "no_of_samples", + pdata->no_of_samples); + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->effective_spads, + 8, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "effective_spads", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->peak_rate_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "peak_rate_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_005, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_005", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "median_range_mm", + pdata->median_range_mm); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "range_mm_offset", + pdata->range_mm_offset); +} + + +void VL53L1_print_cal_peak_rate_map( + VL53L1_cal_peak_rate_map_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + uint8_t i = 0; + uint8_t x = 0; + uint8_t y = 0; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->cal_distance_mm, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "cal_distance_mm", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->cal_reflectance_pc, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "cal_reflectance_pc", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "max_samples", + pdata->max_samples); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "width", + pdata->width); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "height", + pdata->height); + + i = 0; + for (y = 0; y < pdata->height; y++) { + for (x = 0; x < pdata->width; x++) { + + sprintf(ppre_text, "%speak_rate_mcps[%u]", pprefix, i); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->peak_rate_mcps[i], + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %s\n", + ppre_text, + fp_text); + + i++; + } + } +} + +void VL53L1_print_additional_data( + VL53L1_additional_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "preset_mode", + pdata->preset_mode); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "zone_preset", + pdata->zone_preset); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "measurement_mode", + pdata->measurement_mode); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "offset_calibration_mode", + pdata->offset_calibration_mode); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "offset_correction_mode", + pdata->offset_correction_mode); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "dmax_mode", + pdata->dmax_mode); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "phasecal_config_timeout_us", + pdata->phasecal_config_timeout_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "mm_config_timeout_us", + pdata->mm_config_timeout_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "range_config_timeout_us", + pdata->range_config_timeout_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "inter_measurement_period_ms", + pdata->inter_measurement_period_ms); + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->dss_config__target_total_rate_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "dss_config__target_total_rate_mcps", + fp_text); + + sprintf(ppre_text, "%s VL53L1_p_010.", pprefix); + VL53L1_print_histogram_bin_data( + &pdata->VL53L1_p_010, + ppre_text, trace_flags); + + +} + + +void VL53L1_print_additional_offset_cal_data( + VL53L1_additional_offset_cal_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__mm_inner_actual_effective_spads, + 8, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "result__mm_inner_actual_effective_spads", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__mm_outer_actual_effective_spads, + 8, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "result__mm_outer_actual_effective_spads", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__mm_inner_peak_signal_count_rtn_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "result__mm_inner_peak_signal_count_rtn_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__mm_outer_peak_signal_count_rtn_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "result__mm_outer_peak_signal_count_rtn_mcps", + fp_text); +} + + +void VL53L1_print_gain_calibration_data( + VL53L1_gain_calibration_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->standard_ranging_gain_factor, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "standard_ranging_gain_factor", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->histogram_ranging_gain_factor, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "histogram_ranging_gain_factor", + fp_text); +} + + +void VL53L1_print_zone_calibration_data( + VL53L1_zone_calibration_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "no_of_samples", + pdata->no_of_samples); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->effective_spads, + 8, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "effective_spads", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->peak_rate_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "peak_rate_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_014, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_014", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->VL53L1_p_005, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "VL53L1_p_005", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->median_range_mm, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "median_range_mm", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->range_mm_offset, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "range_mm_offset", + fp_text); +} + + +void VL53L1_print_zone_calibration_results( + VL53L1_zone_calibration_results_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + uint8_t i = 0; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "preset_mode", + pdata->preset_mode); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "zone_preset", + pdata->zone_preset); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cal_distance_mm", + pdata->cal_distance_mm); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->cal_reflectance_pc, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "cal_reflectance_pc", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->phasecal_result__reference_phase, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "phasecal_result__reference_phase", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->zero_distance_phase, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "zero_distance_phase", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cal_status", + pdata->cal_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "max_zones", + pdata->max_zones); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "active_zones", + pdata->active_zones); + + for (i = 0; i < pdata->active_zones; i++) { + sprintf(ppre_text, "%sdata[%u].", pprefix, i); + VL53L1_print_zone_calibration_data( + &(pdata->VL53L1_p_002[i]), + ppre_text, trace_flags); + } +} + +void VL53L1_print_xtalk_range_results( + VL53L1_xtalk_range_results_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + uint8_t i = 0; + + VL53L1_histogram_bin_data_t *pbin_data; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "cal_status", + pdata->cal_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "num_of_samples_status", + pdata->num_of_samples_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "zero_samples_status", + pdata->zero_samples_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "max_sigma_status", + pdata->max_sigma_status); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "max_results", + pdata->max_results); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "active_results", + pdata->active_results); + + for (i = 0; i < pdata->active_results; i++) { + sprintf(ppre_text, "%sdata[%u].", pprefix, i); + VL53L1_print_xtalk_range_data( + &(pdata->VL53L1_p_002[i]), + ppre_text, trace_flags); + } + + sprintf(ppre_text, "%scentral_histogram_sum.", pprefix); + VL53L1_print_histogram_bin_data( + &pdata->central_histogram_sum, + ppre_text, trace_flags); + + sprintf(ppre_text, "%scentral_histogram_avg.", pprefix); + VL53L1_print_histogram_bin_data( + &pdata->central_histogram_avg, + ppre_text, trace_flags); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_015", + pdata->central_histogram__window_start); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "VL53L1_p_016", + pdata->central_histogram__window_end); + + pbin_data = &(pdata->histogram_avg_1[0]); + + for (i = 0; i < 5; i++) { + sprintf(ppre_text, "%shistogram_avg_1[%u].", pprefix, i); + VL53L1_print_histogram_bin_data( + pbin_data, + ppre_text, trace_flags); + pbin_data++; + } + + pbin_data = &(pdata->histogram_avg_2[0]); + + for (i = 0; i < 5; i++) { + sprintf(ppre_text, "%shistogram_avg_2[%u].", pprefix, i); + VL53L1_print_histogram_bin_data( + pbin_data, + ppre_text, trace_flags); + pbin_data++; + } + + pbin_data = &(pdata->xtalk_avg[0]); + + for (i = 0; i < 5; i++) { + sprintf(ppre_text, "%sxtalk_avg[%u].", pprefix, i); + VL53L1_print_histogram_bin_data( + pbin_data, + ppre_text, trace_flags); + pbin_data++; + } +} + + +void VL53L1_print_xtalk_range_data( + VL53L1_xtalk_range_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "no_of_samples", + pdata->no_of_samples); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "signal_total_events_sum", + pdata->signal_total_events_sum); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "signal_total_events_avg", + pdata->signal_total_events_avg); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->rate_per_spad_kcps_sum, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "rate_per_spad_kcps_sum", + fp_text); + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->rate_per_spad_kcps_avg, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "rate_per_spad_kcps_avg", + fp_text); +} + + +void VL53L1_print_xtalk_calibration_results( + VL53L1_xtalk_calibration_results_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + int16_t tmpi16; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->algo__crosstalk_compensation_plane_offset_kcps, + 9, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_compensation_plane_offset_kcps", + fp_text); + + tmpi16 = pdata->algo__crosstalk_compensation_x_plane_gradient_kcps; + VL53L1_signed_fixed_point_sprintf( + (int32_t)tmpi16, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_compensation_x_plane_gradient_kcps", + fp_text); + + tmpi16 = pdata->algo__crosstalk_compensation_y_plane_gradient_kcps; + VL53L1_signed_fixed_point_sprintf( + (int32_t)tmpi16, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_compensation_y_plane_gradient_kcps", + fp_text); +} + + +void VL53L1_print_xtalk_config( + VL53L1_xtalk_config_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + int16_t tmpi16; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->algo__crosstalk_compensation_plane_offset_kcps, + 9, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_compensation_plane_offset_kcps", + fp_text); + + tmpi16 = pdata->algo__crosstalk_compensation_x_plane_gradient_kcps; + VL53L1_signed_fixed_point_sprintf( + (int32_t)tmpi16, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_compensation_x_plane_gradient_kcps", + fp_text); + + tmpi16 = pdata->algo__crosstalk_compensation_y_plane_gradient_kcps; + VL53L1_signed_fixed_point_sprintf( + (int32_t)tmpi16, + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_compensation_y_plane_gradient_kcps", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "global_crosstalk_compensation_enable", + pdata->global_crosstalk_compensation_enable); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->histogram_mode_crosstalk_margin_kcps, + 9, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "histogram_mode_crosstalk_margin_kcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->lite_mode_crosstalk_margin_kcps, + 9, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "lite_mode_crosstalk_margin_kcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->crosstalk_range_ignore_threshold_mult, + 5, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "crosstalk_range_ignore_threshold_mult", + fp_text); + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->crosstalk_range_ignore_threshold_rate_mcps, + 13, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "crosstalk_range_ignore_threshold_rate_mcps", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "algo__crosstalk_detect_max_valid_range_mm", + pdata->algo__crosstalk_detect_max_valid_range_mm); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "algo__crosstalk_detect_min_valid_range_mm", + pdata->algo__crosstalk_detect_min_valid_range_mm); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->algo__crosstalk_detect_max_valid_rate_kcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_detect_max_valid_rate_kcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->algo__crosstalk_detect_max_sigma_mm, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_detect_max_sigma_mm", + fp_text); + +} + + +void VL53L1_print_xtalk_extract_config( + VL53L1_xtalkextract_config_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->dss_config__target_total_rate_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "dss_config__target_total_rate_mcps", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "mm_config_timeout_us", + pdata->mm_config_timeout_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "range_config_timeout_us", + pdata->range_config_timeout_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "num_of_samples", + pdata->num_of_samples); + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "algo__crosstalk_extract_max_valid_range_mm", + pdata->algo__crosstalk_extract_max_valid_range_mm); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "algo__crosstalk_extract_min_valid_range_mm", + pdata->algo__crosstalk_extract_min_valid_range_mm); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->algo__crosstalk_extract_max_valid_rate_kcps, + 9, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_extract_max_valid_rate_kcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->algo__crosstalk_extract_max_sigma_mm, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "algo__crosstalk_extract_max_sigma_mm", + fp_text); + +} + + +void VL53L1_print_zone_cal_config( + VL53L1_zonecal_config_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->dss_config__target_total_rate_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "dss_config__target_total_rate_mcps", + fp_text); + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "mm_config_timeout_us", + pdata->mm_config_timeout_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "range_config_timeout_us", + pdata->range_config_timeout_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "phasecal_config_timeout_us", + pdata->phasecal_config_timeout_us); + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "phasecal_num_of_samples", + pdata->phasecal_num_of_samples); + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "zone_num_of_samples", + pdata->zone_num_of_samples); + +} + +void VL53L1_print_offset_cal_config( + VL53L1_offsetcal_config_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->dss_config__target_total_rate_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "dss_config__target_total_rate_mcps", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "phasecal_config_timeout_us", + pdata->phasecal_config_timeout_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "range_config_timeout_us", + pdata->range_config_timeout_us); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "pre_num_of_samples", + pdata->pre_num_of_samples); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "mm1_num_of_samples", + pdata->mm1_num_of_samples); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "mm2_num_of_samples", + pdata->mm2_num_of_samples); + + +} + + +void VL53L1_print_dmax_calibration_data( + VL53L1_dmax_calibration_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->ref__actual_effective_spads, + 8, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "ref__actual_effective_spads", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->ref__peak_signal_count_rate_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "ref__peak_signal_count_rate_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->ref__distance_mm, + 4, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "ref__distance_mm", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->ref_reflectance_pc, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "ref_reflectance_pc", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->coverglass_transmission, + 8, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "coverglass_transmission", + fp_text); +} + + +void VL53L1_print_calibration_data( + VL53L1_calibration_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = 0x%08X\n", + pprefix, + "struct_version", + pdata->struct_version); + + sprintf(ppre_text, "%scustomer.", pprefix); + VL53L1_print_customer_nvm_managed( + &(pdata->customer), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sfmt_dmax_cal.", pprefix); + VL53L1_print_dmax_calibration_data( + &(pdata->fmt_dmax_cal), + ppre_text, trace_flags); + + sprintf(ppre_text, "%scust_dmax_cal.", pprefix); + VL53L1_print_dmax_calibration_data( + &(pdata->cust_dmax_cal), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sadd_off_cal_data.", pprefix); + VL53L1_print_additional_offset_cal_data( + &(pdata->add_off_cal_data), + ppre_text, trace_flags); + + sprintf(ppre_text, "%soptical_centre.", pprefix); + VL53L1_print_optical_centre( + &(pdata->optical_centre), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sxtalkhisto.", pprefix); + VL53L1_print_xtalk_histogram_data( + &(pdata->xtalkhisto), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sgain_cal.", pprefix); + VL53L1_print_gain_calibration_data( + &(pdata->gain_cal), + ppre_text, trace_flags); + + sprintf(ppre_text, "%scal_peak_rate_map.", pprefix); + VL53L1_print_cal_peak_rate_map( + &(pdata->cal_peak_rate_map), + ppre_text, trace_flags); +} + + +void VL53L1_print_xtalk_debug_data( + VL53L1_xtalk_debug_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + sprintf(ppre_text, "%scustomer.", pprefix); + VL53L1_print_customer_nvm_managed( + &(pdata->customer), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sxtalk_cfg.", pprefix); + VL53L1_print_xtalk_config( + &(pdata->xtalk_cfg), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sxtalk_extract_cfg.", pprefix); + VL53L1_print_xtalk_extract_config( + &(pdata->xtalk_extract_cfg), + ppre_text, trace_flags); + + sprintf(ppre_text, "%shist_data.", pprefix); + VL53L1_print_histogram_bin_data( + &(pdata->hist_data), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sxtalk_shapes.", pprefix); + VL53L1_print_xtalk_histogram_data( + &(pdata->xtalk_shapes), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sgain_cal.", pprefix); + VL53L1_print_xtalk_range_results( + &(pdata->xtalk_results), + ppre_text, trace_flags); +} + + +void VL53L1_print_offset_debug_data( + VL53L1_offset_debug_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + sprintf(ppre_text, "%scustomer.", pprefix); + VL53L1_print_customer_nvm_managed( + &(pdata->customer), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sfmt_dmax_cal.", pprefix); + VL53L1_print_dmax_calibration_data( + &(pdata->fmt_dmax_cal), + ppre_text, trace_flags); + + sprintf(ppre_text, "%scust_dmax_cal.", pprefix); + VL53L1_print_dmax_calibration_data( + &(pdata->cust_dmax_cal), + ppre_text, trace_flags); + + sprintf(ppre_text, "%sadd_off_cal_data.", pprefix); + VL53L1_print_additional_offset_cal_data( + &(pdata->add_off_cal_data), + ppre_text, trace_flags); + + sprintf(ppre_text, "%soffset_results.", pprefix); + VL53L1_print_offset_range_results( + &(pdata->offset_results), + ppre_text, trace_flags); +} + + +void VL53L1_print_zone_config( + VL53L1_zone_config_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + uint8_t i = 0; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "max_zones", + pdata->max_zones); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "active_zones", + pdata->active_zones); + + for (i = 0; i < pdata->active_zones; i++) { + sprintf(ppre_text, "%suser_zones[%u].", pprefix, i); + VL53L1_print_user_zone( + &pdata->user_zones[i], + ppre_text, + trace_flags); + } +} + + +void VL53L1_print_optical_centre( + VL53L1_optical_centre_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->x_centre, + 4, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "x_centre", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->y_centre, + 4, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "y_centre", + fp_text); +} + + +void VL53L1_print_user_zone( + VL53L1_user_zone_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "x_centre", + pdata->x_centre); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "y_centre", + pdata->y_centre); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "width", + pdata->width); + + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "height", + pdata->height); +} + + +void VL53L1_print_spad_rate_data( + VL53L1_spad_rate_data_t *pspad_rates, + char *pprefix, + uint32_t trace_flags) +{ + + + + uint16_t spad_no = 0; + uint8_t row = 0; + uint8_t col = 0; + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%8s,%4s,%4s, %s\n", + pprefix, + "spad_no", + "row", + "col", + "peak_rate_mcps"); + + for (spad_no = 0; spad_no < pspad_rates->no_of_values; spad_no++) { + + + VL53L1_decode_row_col( + (uint8_t)spad_no, + &row, + &col); + + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pspad_rates->rate_data[spad_no], + pspad_rates->fractional_bits, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%8u,%4u,%4u, %s\n", + pprefix, + spad_no, + row, + col, + fp_text); + } +} + + +void VL53L1_print_spad_rate_map( + VL53L1_spad_rate_data_t *pspad_rates, + char *pprefix, + uint32_t trace_flags) +{ + + + + uint8_t spad_no = 0; + uint8_t row = 0; + uint8_t col = 0; + + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%4s", + pprefix, + " "); + + for (col = 0; col < VL53L1_SPAD_ARRAY_WIDTH; col++) + trace_print( + VL53L1_TRACE_LEVEL_INFO, + ",%8u", + col); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "\n"); + + + + for (row = 0; row < VL53L1_SPAD_ARRAY_HEIGHT; row++) { + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%4u", + pprefix, + row); + + for (col = 0; col < VL53L1_SPAD_ARRAY_HEIGHT; col++) { + + + + VL53L1_encode_row_col( + row, + col, + &spad_no); + + + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pspad_rates->rate_data[spad_no], + pspad_rates->fractional_bits, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + ",%8s", + fp_text); + } + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "\n"); + } +} + + +#endif + + diff --git a/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_api_preset_modes.c b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_api_preset_modes.c new file mode 100644 index 000000000000..e8317338a28b --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_api_preset_modes.c @@ -0,0 +1,3681 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#include "vl53l1_ll_def.h" +#include "vl53l1_platform_log.h" +#include "vl53l1_register_structs.h" +#include "vl53l1_register_settings.h" +#include "vl53l1_hist_structs.h" +#include "vl53l1_zone_presets.h" +#include "vl53l1_core.h" +#include "vl53l1_api_preset_modes.h" +#include "vl53l1_tuning_parm_defaults.h" + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_API,\ + status, fmt, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_init_refspadchar_config_struct( + VL53L1_refspadchar_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + pdata->device_test_mode = + VL53L1_TUNINGPARM_REFSPADCHAR_DEVICE_TEST_MODE_DEFAULT; + pdata->VL53L1_p_009 = + VL53L1_TUNINGPARM_REFSPADCHAR_VCSEL_PERIOD_DEFAULT; + pdata->timeout_us = + VL53L1_TUNINGPARM_REFSPADCHAR_PHASECAL_TIMEOUT_US_DEFAULT; + pdata->target_count_rate_mcps = + VL53L1_TUNINGPARM_REFSPADCHAR_TARGET_COUNT_RATE_MCPS_DEFAULT; + pdata->min_count_rate_limit_mcps = + VL53L1_TUNINGPARM_REFSPADCHAR_MIN_COUNTRATE_LIMIT_MCPS_DEFAULT; + pdata->max_count_rate_limit_mcps = + VL53L1_TUNINGPARM_REFSPADCHAR_MAX_COUNTRATE_LIMIT_MCPS_DEFAULT; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_init_ssc_config_struct( + VL53L1_ssc_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + pdata->array_select = VL53L1_DEVICESSCARRAY_RTN; + + + pdata->VL53L1_p_009 = + VL53L1_TUNINGPARM_SPADMAP_VCSEL_PERIOD_DEFAULT; + + + pdata->vcsel_start = + VL53L1_TUNINGPARM_SPADMAP_VCSEL_START_DEFAULT; + + + pdata->vcsel_width = 0x02; + + + pdata->timeout_us = 36000; + + + pdata->rate_limit_mcps = + VL53L1_TUNINGPARM_SPADMAP_RATE_LIMIT_MCPS_DEFAULT; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_init_xtalk_config_struct( + VL53L1_customer_nvm_managed_t *pnvm, + VL53L1_xtalk_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + + pdata->algo__crosstalk_compensation_plane_offset_kcps = + pnvm->algo__crosstalk_compensation_plane_offset_kcps; + pdata->algo__crosstalk_compensation_x_plane_gradient_kcps = + pnvm->algo__crosstalk_compensation_x_plane_gradient_kcps; + pdata->algo__crosstalk_compensation_y_plane_gradient_kcps = + pnvm->algo__crosstalk_compensation_y_plane_gradient_kcps; + + + + pdata->nvm_default__crosstalk_compensation_plane_offset_kcps = + (uint32_t)pnvm->algo__crosstalk_compensation_plane_offset_kcps; + pdata->nvm_default__crosstalk_compensation_x_plane_gradient_kcps = + pnvm->algo__crosstalk_compensation_x_plane_gradient_kcps; + pdata->nvm_default__crosstalk_compensation_y_plane_gradient_kcps = + pnvm->algo__crosstalk_compensation_y_plane_gradient_kcps; + + pdata->histogram_mode_crosstalk_margin_kcps = + VL53L1_TUNINGPARM_HIST_XTALK_MARGIN_KCPS_DEFAULT; + pdata->lite_mode_crosstalk_margin_kcps = + VL53L1_TUNINGPARM_LITE_XTALK_MARGIN_KCPS_DEFAULT; + + + + pdata->crosstalk_range_ignore_threshold_mult = + VL53L1_TUNINGPARM_LITE_RIT_MULT_DEFAULT; + + if ((pdata->algo__crosstalk_compensation_plane_offset_kcps == 0x00) + && (pdata->algo__crosstalk_compensation_x_plane_gradient_kcps + == 0x00) + && (pdata->algo__crosstalk_compensation_y_plane_gradient_kcps + == 0x00)) + pdata->global_crosstalk_compensation_enable = 0x00; + else + pdata->global_crosstalk_compensation_enable = 0x01; + + + if ((status == VL53L1_ERROR_NONE) && + (pdata->global_crosstalk_compensation_enable == 0x01)) { + pdata->crosstalk_range_ignore_threshold_rate_mcps = + VL53L1_calc_range_ignore_threshold( + pdata->algo__crosstalk_compensation_plane_offset_kcps, + pdata->algo__crosstalk_compensation_x_plane_gradient_kcps, + pdata->algo__crosstalk_compensation_y_plane_gradient_kcps, + pdata->crosstalk_range_ignore_threshold_mult); + } else { + pdata->crosstalk_range_ignore_threshold_rate_mcps = 0; + } + + + + + pdata->algo__crosstalk_detect_min_valid_range_mm = + VL53L1_TUNINGPARM_XTALK_DETECT_MIN_VALID_RANGE_MM_DEFAULT; + pdata->algo__crosstalk_detect_max_valid_range_mm = + VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RANGE_MM_DEFAULT; + pdata->algo__crosstalk_detect_max_valid_rate_kcps = + VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RATE_KCPS_DEFAULT; + pdata->algo__crosstalk_detect_max_sigma_mm = + VL53L1_TUNINGPARM_XTALK_DETECT_MAX_SIGMA_MM_DEFAULT; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_init_xtalk_extract_config_struct( + VL53L1_xtalkextract_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + pdata->dss_config__target_total_rate_mcps = + VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_RATE_MCPS_DEFAULT; + + pdata->mm_config_timeout_us = + VL53L1_TUNINGPARM_XTALK_EXTRACT_DSS_TIMEOUT_US_DEFAULT; + + pdata->num_of_samples = + VL53L1_TUNINGPARM_XTALK_EXTRACT_NUM_OF_SAMPLES_DEFAULT; + + pdata->phasecal_config_timeout_us = + VL53L1_TUNINGPARM_XTALK_EXTRACT_PHASECAL_TIMEOUT_US_DEFAULT; + + pdata->range_config_timeout_us = + VL53L1_TUNINGPARM_XTALK_EXTRACT_BIN_TIMEOUT_US_DEFAULT; + + + + + pdata->algo__crosstalk_extract_min_valid_range_mm = + VL53L1_TUNINGPARM_XTALK_EXTRACT_MIN_FILTER_THRESH_MM_DEFAULT; + pdata->algo__crosstalk_extract_max_valid_range_mm = + VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_FILTER_THRESH_MM_DEFAULT; + pdata->algo__crosstalk_extract_max_valid_rate_kcps = + VL53L1_TUNINGPARM_XTALK_EXTRACT_MAX_VALID_RATE_KCPS_DEFAULT; + pdata->algo__crosstalk_extract_max_sigma_mm = + VL53L1_TUNINGPARM_XTALK_EXTRACT_SIGMA_THRESHOLD_MM_DEFAULT; + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_init_offset_cal_config_struct( + VL53L1_offsetcal_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + pdata->dss_config__target_total_rate_mcps = + VL53L1_TUNINGPARM_OFFSET_CAL_DSS_RATE_MCPS_DEFAULT; + + pdata->phasecal_config_timeout_us = + VL53L1_TUNINGPARM_OFFSET_CAL_PHASECAL_TIMEOUT_US_DEFAULT; + + pdata->range_config_timeout_us = + VL53L1_TUNINGPARM_OFFSET_CAL_RANGE_TIMEOUT_US_DEFAULT; + + pdata->mm_config_timeout_us = + VL53L1_TUNINGPARM_OFFSET_CAL_MM_TIMEOUT_US_DEFAULT; + + + + + pdata->pre_num_of_samples = + VL53L1_TUNINGPARM_OFFSET_CAL_PRE_SAMPLES_DEFAULT; + pdata->mm1_num_of_samples = + VL53L1_TUNINGPARM_OFFSET_CAL_MM1_SAMPLES_DEFAULT; + pdata->mm2_num_of_samples = + VL53L1_TUNINGPARM_OFFSET_CAL_MM2_SAMPLES_DEFAULT; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_init_zone_cal_config_struct( + VL53L1_zonecal_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + pdata->dss_config__target_total_rate_mcps = + VL53L1_TUNINGPARM_ZONE_CAL_DSS_RATE_MCPS_DEFAULT; + + pdata->phasecal_config_timeout_us = + VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_TIMEOUT_US_DEFAULT; + + pdata->range_config_timeout_us = + VL53L1_TUNINGPARM_ZONE_CAL_RANGE_TIMEOUT_US_DEFAULT; + + pdata->mm_config_timeout_us = + VL53L1_TUNINGPARM_ZONE_CAL_DSS_TIMEOUT_US_DEFAULT; + + + + + pdata->phasecal_num_of_samples = + VL53L1_TUNINGPARM_ZONE_CAL_PHASECAL_NUM_SAMPLES_DEFAULT; + pdata->zone_num_of_samples = + VL53L1_TUNINGPARM_ZONE_CAL_ZONE_NUM_SAMPLES_DEFAULT; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_init_hist_post_process_config_struct( + uint8_t xtalk_compensation_enable, + VL53L1_hist_post_process_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + pdata->hist_algo_select = + VL53L1_TUNINGPARM_HIST_ALGO_SELECT_DEFAULT; + + + + pdata->hist_target_order = + VL53L1_TUNINGPARM_HIST_TARGET_ORDER_DEFAULT; + + + + pdata->filter_woi0 = + VL53L1_TUNINGPARM_HIST_FILTER_WOI_0_DEFAULT; + pdata->filter_woi1 = + VL53L1_TUNINGPARM_HIST_FILTER_WOI_1_DEFAULT; + + + pdata->hist_amb_est_method = + VL53L1_TUNINGPARM_HIST_AMB_EST_METHOD_DEFAULT; + + pdata->ambient_thresh_sigma0 = + VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_0_DEFAULT; + pdata->ambient_thresh_sigma1 = + VL53L1_TUNINGPARM_HIST_AMB_THRESH_SIGMA_1_DEFAULT; + + + pdata->ambient_thresh_events_scaler = + VL53L1_TUNINGPARM_HIST_AMB_EVENTS_SCALER_DEFAULT; + + + pdata->min_ambient_thresh_events = + VL53L1_TUNINGPARM_HIST_MIN_AMB_THRESH_EVENTS_DEFAULT; + + pdata->noise_threshold = + VL53L1_TUNINGPARM_HIST_NOISE_THRESHOLD_DEFAULT; + + pdata->signal_total_events_limit = + VL53L1_TUNINGPARM_HIST_SIGNAL_TOTAL_EVENTS_LIMIT_DEFAULT; + pdata->sigma_estimator__sigma_ref_mm = + VL53L1_TUNINGPARM_HIST_SIGMA_EST_REF_MM_DEFAULT; + + + pdata->sigma_thresh = + VL53L1_TUNINGPARM_HIST_SIGMA_THRESH_MM_DEFAULT; + + pdata->range_offset_mm = 0; + + pdata->gain_factor = + VL53L1_TUNINGPARM_HIST_GAIN_FACTOR_DEFAULT; + + + + pdata->valid_phase_low = 0x08; + pdata->valid_phase_high = 0x88; + + + + pdata->algo__consistency_check__phase_tolerance = + VL53L1_TUNINGPARM_CONSISTENCY_HIST_PHASE_TOLERANCE_DEFAULT; + + + + pdata->algo__consistency_check__event_sigma = + VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA_DEFAULT; + + + pdata->algo__consistency_check__event_min_spad_count = + VL53L1_TUNINGPARM_CONSISTENCY_HIST_EVENT_SIGMA_MIN_SPAD_LIMIT_DEFAULT; + + + + pdata->algo__consistency_check__min_max_tolerance = + VL53L1_TUNINGPARM_CONSISTENCY_HIST_MIN_MAX_TOLERANCE_MM_DEFAULT; + + + pdata->algo__crosstalk_compensation_enable = xtalk_compensation_enable; + + + pdata->algo__crosstalk_detect_min_valid_range_mm = + VL53L1_TUNINGPARM_XTALK_DETECT_MIN_VALID_RANGE_MM_DEFAULT; + pdata->algo__crosstalk_detect_max_valid_range_mm = + VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RANGE_MM_DEFAULT; + pdata->algo__crosstalk_detect_max_valid_rate_kcps = + VL53L1_TUNINGPARM_XTALK_DETECT_MAX_VALID_RATE_KCPS_DEFAULT; + pdata->algo__crosstalk_detect_max_sigma_mm = + VL53L1_TUNINGPARM_XTALK_DETECT_MAX_SIGMA_MM_DEFAULT; + + + + + + pdata->algo__crosstalk_detect_event_sigma = + VL53L1_TUNINGPARM_XTALK_DETECT_EVENT_SIGMA_DEFAULT; + + + + pdata->algo__crosstalk_detect_min_max_tolerance = + VL53L1_TUNINGPARM_XTALK_DETECT_MIN_MAX_TOLERANCE_DEFAULT; + + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_init_dmax_calibration_data_struct( + VL53L1_dmax_calibration_data_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + pdata->ref__actual_effective_spads = 0x5F2D; + + pdata->ref__peak_signal_count_rate_mcps = 0x0844; + + pdata->ref__distance_mm = 0x08A5; + + + pdata->ref_reflectance_pc = 0x0014; + + + pdata->coverglass_transmission = 0x0100; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_init_tuning_parm_storage_struct( + VL53L1_tuning_parm_storage_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + pdata->tp_tuning_parm_version = + VL53L1_TUNINGPARM_VERSION_DEFAULT; + pdata->tp_tuning_parm_key_table_version = + VL53L1_TUNINGPARM_KEY_TABLE_VERSION_DEFAULT; + pdata->tp_tuning_parm_lld_version = + VL53L1_TUNINGPARM_LLD_VERSION_DEFAULT; + pdata->tp_init_phase_rtn_lite_long = + VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_LONG_RANGE_DEFAULT; + pdata->tp_init_phase_rtn_lite_med = + VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_MED_RANGE_DEFAULT; + pdata->tp_init_phase_rtn_lite_short = + VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_LITE_SHORT_RANGE_DEFAULT; + pdata->tp_init_phase_ref_lite_long = + VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_LONG_RANGE_DEFAULT; + pdata->tp_init_phase_ref_lite_med = + VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_MED_RANGE_DEFAULT; + pdata->tp_init_phase_ref_lite_short = + VL53L1_TUNINGPARM_INITIAL_PHASE_REF_LITE_SHORT_RANGE_DEFAULT; + pdata->tp_init_phase_rtn_hist_long = + VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_LONG_RANGE_DEFAULT; + pdata->tp_init_phase_rtn_hist_med = + VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_MED_RANGE_DEFAULT; + pdata->tp_init_phase_rtn_hist_short = + VL53L1_TUNINGPARM_INITIAL_PHASE_RTN_HISTO_SHORT_RANGE_DEFAULT; + pdata->tp_init_phase_ref_hist_long = + VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_LONG_RANGE_DEFAULT; + pdata->tp_init_phase_ref_hist_med = + VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_MED_RANGE_DEFAULT; + pdata->tp_init_phase_ref_hist_short = + VL53L1_TUNINGPARM_INITIAL_PHASE_REF_HISTO_SHORT_RANGE_DEFAULT; + pdata->tp_consistency_lite_phase_tolerance = + VL53L1_TUNINGPARM_CONSISTENCY_LITE_PHASE_TOLERANCE_DEFAULT; + pdata->tp_phasecal_target = + VL53L1_TUNINGPARM_PHASECAL_TARGET_DEFAULT; + pdata->tp_cal_repeat_rate = + VL53L1_TUNINGPARM_LITE_CAL_REPEAT_RATE_DEFAULT; + pdata->tp_lite_min_clip = + VL53L1_TUNINGPARM_LITE_MIN_CLIP_MM_DEFAULT; + pdata->tp_lite_long_sigma_thresh_mm = + VL53L1_TUNINGPARM_LITE_LONG_SIGMA_THRESH_MM_DEFAULT; + pdata->tp_lite_med_sigma_thresh_mm = + VL53L1_TUNINGPARM_LITE_MED_SIGMA_THRESH_MM_DEFAULT; + pdata->tp_lite_short_sigma_thresh_mm = + VL53L1_TUNINGPARM_LITE_SHORT_SIGMA_THRESH_MM_DEFAULT; + pdata->tp_lite_long_min_count_rate_rtn_mcps = + VL53L1_TUNINGPARM_LITE_LONG_MIN_COUNT_RATE_RTN_MCPS_DEFAULT; + pdata->tp_lite_med_min_count_rate_rtn_mcps = + VL53L1_TUNINGPARM_LITE_MED_MIN_COUNT_RATE_RTN_MCPS_DEFAULT; + pdata->tp_lite_short_min_count_rate_rtn_mcps = + VL53L1_TUNINGPARM_LITE_SHORT_MIN_COUNT_RATE_RTN_MCPS_DEFAULT; + pdata->tp_lite_sigma_est_pulse_width_ns = + VL53L1_TUNINGPARM_LITE_SIGMA_EST_PULSE_WIDTH_DEFAULT; + pdata->tp_lite_sigma_est_amb_width_ns = + VL53L1_TUNINGPARM_LITE_SIGMA_EST_AMB_WIDTH_NS_DEFAULT; + pdata->tp_lite_sigma_ref_mm = + VL53L1_TUNINGPARM_LITE_SIGMA_REF_MM_DEFAULT; + pdata->tp_lite_seed_cfg = + VL53L1_TUNINGPARM_LITE_SEED_CONFIG_DEFAULT; + pdata->tp_timed_seed_cfg = + VL53L1_TUNINGPARM_TIMED_SEED_CONFIG_DEFAULT; + pdata->tp_lite_quantifier = + VL53L1_TUNINGPARM_LITE_QUANTIFIER_DEFAULT; + pdata->tp_lite_first_order_select = + VL53L1_TUNINGPARM_LITE_FIRST_ORDER_SELECT_DEFAULT; + + + + + pdata->tp_dss_target_lite_mcps = + VL53L1_TUNINGPARM_LITE_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS_DEFAULT; + pdata->tp_dss_target_histo_mcps = + VL53L1_TUNINGPARM_RANGING_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS_DEFAULT; + pdata->tp_dss_target_histo_mz_mcps = + VL53L1_TUNINGPARM_MZ_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS_DEFAULT; + pdata->tp_dss_target_timed_mcps = + VL53L1_TUNINGPARM_TIMED_DSS_CONFIG_TARGET_TOTAL_RATE_MCPS_DEFAULT; + pdata->tp_phasecal_timeout_lite_us = + VL53L1_TUNINGPARM_LITE_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_phasecal_timeout_hist_long_us = + VL53L1_TUNINGPARM_RANGING_LONG_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_phasecal_timeout_hist_med_us = + VL53L1_TUNINGPARM_RANGING_MED_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_phasecal_timeout_hist_short_us = + VL53L1_TUNINGPARM_RANGING_SHORT_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_phasecal_timeout_mz_long_us = + VL53L1_TUNINGPARM_MZ_LONG_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_phasecal_timeout_mz_med_us = + VL53L1_TUNINGPARM_MZ_MED_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_phasecal_timeout_mz_short_us = + VL53L1_TUNINGPARM_MZ_SHORT_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_phasecal_timeout_timed_us = + VL53L1_TUNINGPARM_TIMED_PHASECAL_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_mm_timeout_lite_us = + VL53L1_TUNINGPARM_LITE_MM_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_mm_timeout_histo_us = + VL53L1_TUNINGPARM_RANGING_MM_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_mm_timeout_mz_us = + VL53L1_TUNINGPARM_MZ_MM_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_mm_timeout_timed_us = + VL53L1_TUNINGPARM_TIMED_MM_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_range_timeout_lite_us = + VL53L1_TUNINGPARM_LITE_RANGE_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_range_timeout_histo_us = + VL53L1_TUNINGPARM_RANGING_RANGE_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_range_timeout_mz_us = + VL53L1_TUNINGPARM_MZ_RANGE_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_range_timeout_timed_us = + VL53L1_TUNINGPARM_TIMED_RANGE_CONFIG_TIMEOUT_US_DEFAULT; + + + + pdata->tp_mm_timeout_lpa_us = + VL53L1_TUNINGPARM_LOWPOWERAUTO_MM_CONFIG_TIMEOUT_US_DEFAULT; + pdata->tp_range_timeout_lpa_us = + VL53L1_TUNINGPARM_LOWPOWERAUTO_RANGE_CONFIG_TIMEOUT_US_DEFAULT; + + pdata->tp_dss_target_very_short_mcps = + VL53L1_TUNINGPARM_VERY_SHORT_DSS_RATE_MCPS_DEFAULT; + + pdata->tp_phasecal_patch_power = + VL53L1_TUNINGPARM_PHASECAL_PATCH_POWER_DEFAULT; + + pdata->tp_hist_merge = + VL53L1_TUNINGPARM_HIST_MERGE_DEFAULT; + + pdata->tp_reset_merge_threshold = + VL53L1_TUNINGPARM_RESET_MERGE_THRESHOLD_DEFAULT; + + pdata->tp_hist_merge_max_size = + VL53L1_TUNINGPARM_HIST_MERGE_MAX_SIZE_DEFAULT; + + pdata->tp_uwr_enable = + VL53L1_TUNINGPARM_UWR_ENABLE_DEFAULT; + pdata->tp_uwr_med_z_1_min = + VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_1_MIN_DEFAULT; + pdata->tp_uwr_med_z_1_max = + VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_1_MAX_DEFAULT; + pdata->tp_uwr_med_z_2_min = + VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_2_MIN_DEFAULT; + pdata->tp_uwr_med_z_2_max = + VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_2_MAX_DEFAULT; + pdata->tp_uwr_med_z_3_min = + VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_3_MIN_DEFAULT; + pdata->tp_uwr_med_z_3_max = + VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_3_MAX_DEFAULT; + pdata->tp_uwr_med_z_4_min = + VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_4_MIN_DEFAULT; + pdata->tp_uwr_med_z_4_max = + VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_4_MAX_DEFAULT; + pdata->tp_uwr_med_z_5_min = + VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_5_MIN_DEFAULT; + pdata->tp_uwr_med_z_5_max = + VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_5_MAX_DEFAULT; + pdata->tp_uwr_med_z_6_min = + VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_6_MIN_DEFAULT; + pdata->tp_uwr_med_z_6_max = + VL53L1_TUNINGPARM_UWR_MEDIUM_ZONE_6_MAX_DEFAULT; + pdata->tp_uwr_med_corr_z_1_rangea = + VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_1_RANGEA_DEFAULT; + pdata->tp_uwr_med_corr_z_1_rangeb = + VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_1_RANGEB_DEFAULT; + pdata->tp_uwr_med_corr_z_2_rangea = + VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_2_RANGEA_DEFAULT; + pdata->tp_uwr_med_corr_z_2_rangeb = + VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_2_RANGEB_DEFAULT; + pdata->tp_uwr_med_corr_z_3_rangea = + VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_3_RANGEA_DEFAULT; + pdata->tp_uwr_med_corr_z_3_rangeb = + VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_3_RANGEB_DEFAULT; + pdata->tp_uwr_med_corr_z_4_rangea = + VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_4_RANGEA_DEFAULT; + pdata->tp_uwr_med_corr_z_4_rangeb = + VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_4_RANGEB_DEFAULT; + pdata->tp_uwr_med_corr_z_5_rangea = + VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_5_RANGEA_DEFAULT; + pdata->tp_uwr_med_corr_z_5_rangeb = + VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_5_RANGEB_DEFAULT; + pdata->tp_uwr_med_corr_z_6_rangea = + VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_6_RANGEA_DEFAULT; + pdata->tp_uwr_med_corr_z_6_rangeb = + VL53L1_TUNINGPARM_UWR_MEDIUM_CORRECTION_ZONE_6_RANGEB_DEFAULT; + pdata->tp_uwr_lng_z_1_min = + VL53L1_TUNINGPARM_UWR_LONG_ZONE_1_MIN_DEFAULT; + pdata->tp_uwr_lng_z_1_max = + VL53L1_TUNINGPARM_UWR_LONG_ZONE_1_MAX_DEFAULT; + pdata->tp_uwr_lng_z_2_min = + VL53L1_TUNINGPARM_UWR_LONG_ZONE_2_MIN_DEFAULT; + pdata->tp_uwr_lng_z_2_max = + VL53L1_TUNINGPARM_UWR_LONG_ZONE_2_MAX_DEFAULT; + pdata->tp_uwr_lng_z_3_min = + VL53L1_TUNINGPARM_UWR_LONG_ZONE_3_MIN_DEFAULT; + pdata->tp_uwr_lng_z_3_max = + VL53L1_TUNINGPARM_UWR_LONG_ZONE_3_MAX_DEFAULT; + pdata->tp_uwr_lng_z_4_min = + VL53L1_TUNINGPARM_UWR_LONG_ZONE_4_MIN_DEFAULT; + pdata->tp_uwr_lng_z_4_max = + VL53L1_TUNINGPARM_UWR_LONG_ZONE_4_MAX_DEFAULT; + pdata->tp_uwr_lng_z_5_min = + VL53L1_TUNINGPARM_UWR_LONG_ZONE_5_MIN_DEFAULT; + pdata->tp_uwr_lng_z_5_max = + VL53L1_TUNINGPARM_UWR_LONG_ZONE_5_MAX_DEFAULT; + pdata->tp_uwr_lng_corr_z_1_rangea = + VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_1_RANGEA_DEFAULT; + pdata->tp_uwr_lng_corr_z_1_rangeb = + VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_1_RANGEB_DEFAULT; + pdata->tp_uwr_lng_corr_z_2_rangea = + VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_2_RANGEA_DEFAULT; + pdata->tp_uwr_lng_corr_z_2_rangeb = + VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_2_RANGEB_DEFAULT; + pdata->tp_uwr_lng_corr_z_3_rangea = + VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_3_RANGEA_DEFAULT; + pdata->tp_uwr_lng_corr_z_3_rangeb = + VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_3_RANGEB_DEFAULT; + pdata->tp_uwr_lng_corr_z_4_rangea = + VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_4_RANGEA_DEFAULT; + pdata->tp_uwr_lng_corr_z_4_rangeb = + VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_4_RANGEB_DEFAULT; + pdata->tp_uwr_lng_corr_z_5_rangea = + VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_5_RANGEA_DEFAULT; + pdata->tp_uwr_lng_corr_z_5_rangeb = + VL53L1_TUNINGPARM_UWR_LONG_CORRECTION_ZONE_5_RANGEB_DEFAULT; + pdata->tp_min_signal_secondary_targets = + VL53L1_TUNINGPARM_MIN_SIGNAL_SECONDARY_TARGETS_DEFAULT; + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_init_hist_gen3_dmax_config_struct( + VL53L1_hist_gen3_dmax_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + pdata->dss_config__target_total_rate_mcps = 0x1400; + pdata->dss_config__aperture_attenuation = 0x38; + + pdata->signal_thresh_sigma = + VL53L1_TUNINGPARM_DMAX_CFG_SIGNAL_THRESH_SIGMA_DEFAULT; + pdata->ambient_thresh_sigma = 0x70; + pdata->min_ambient_thresh_events = 16; + pdata->signal_total_events_limit = 100; + pdata->max_effective_spads = 0xFFFF; + + + + pdata->target_reflectance_for_dmax_calc[0] = + VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_0_DEFAULT; + pdata->target_reflectance_for_dmax_calc[1] = + VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_1_DEFAULT; + pdata->target_reflectance_for_dmax_calc[2] = + VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_2_DEFAULT; + pdata->target_reflectance_for_dmax_calc[3] = + VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_3_DEFAULT; + pdata->target_reflectance_for_dmax_calc[4] = + VL53L1_TUNINGPARM_DMAX_CFG_REFLECTANCE_ARRAY_4_DEFAULT; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_standard_ranging( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + pstatic->dss_config__target_total_rate_mcps = 0x0A00; + pstatic->debug__ctrl = 0x00; + pstatic->test_mode__ctrl = 0x00; + pstatic->clk_gating__ctrl = 0x00; + pstatic->nvm_bist__ctrl = 0x00; + pstatic->nvm_bist__num_nvm_words = 0x00; + pstatic->nvm_bist__start_address = 0x00; + pstatic->host_if__status = 0x00; + pstatic->pad_i2c_hv__config = 0x00; + pstatic->pad_i2c_hv__extsup_config = 0x00; + + + pstatic->gpio_hv_pad__ctrl = 0x00; + + + pstatic->gpio_hv_mux__ctrl = + VL53L1_DEVICEINTERRUPTPOLARITY_ACTIVE_LOW | + VL53L1_DEVICEGPIOMODE_OUTPUT_RANGE_AND_ERROR_INTERRUPTS; + + pstatic->gpio__tio_hv_status = 0x02; + pstatic->gpio__fio_hv_status = 0x00; + pstatic->ana_config__spad_sel_pswidth = 0x02; + pstatic->ana_config__vcsel_pulse_width_offset = 0x08; + pstatic->ana_config__fast_osc__config_ctrl = 0x00; + + pstatic->sigma_estimator__effective_pulse_width_ns = + ptuning_parms->tp_lite_sigma_est_pulse_width_ns; + pstatic->sigma_estimator__effective_ambient_width_ns = + ptuning_parms->tp_lite_sigma_est_amb_width_ns; + pstatic->sigma_estimator__sigma_ref_mm = + ptuning_parms->tp_lite_sigma_ref_mm; + + pstatic->algo__crosstalk_compensation_valid_height_mm = 0x01; + pstatic->spare_host_config__static_config_spare_0 = 0x00; + pstatic->spare_host_config__static_config_spare_1 = 0x00; + + pstatic->algo__range_ignore_threshold_mcps = 0x0000; + + + pstatic->algo__range_ignore_valid_height_mm = 0xff; + pstatic->algo__range_min_clip = + ptuning_parms->tp_lite_min_clip; + + pstatic->algo__consistency_check__tolerance = + ptuning_parms->tp_consistency_lite_phase_tolerance; + pstatic->spare_host_config__static_config_spare_2 = 0x00; + pstatic->sd_config__reset_stages_msb = 0x00; + pstatic->sd_config__reset_stages_lsb = 0x00; + + pgeneral->gph_config__stream_count_update_value = 0x00; + pgeneral->global_config__stream_divider = 0x00; + pgeneral->system__interrupt_config_gpio = + VL53L1_INTERRUPT_CONFIG_NEW_SAMPLE_READY; + pgeneral->cal_config__vcsel_start = 0x0B; + + + pgeneral->cal_config__repeat_rate = + ptuning_parms->tp_cal_repeat_rate; + pgeneral->global_config__vcsel_width = 0x02; + + pgeneral->phasecal_config__timeout_macrop = 0x0D; + + pgeneral->phasecal_config__target = + ptuning_parms->tp_phasecal_target; + pgeneral->phasecal_config__override = 0x00; + pgeneral->dss_config__roi_mode_control = + VL53L1_DEVICEDSSMODE__TARGET_RATE; + + pgeneral->system__thresh_rate_high = 0x0000; + pgeneral->system__thresh_rate_low = 0x0000; + + pgeneral->dss_config__manual_effective_spads_select = 0x8C00; + pgeneral->dss_config__manual_block_select = 0x00; + + + pgeneral->dss_config__aperture_attenuation = 0x38; + pgeneral->dss_config__max_spads_limit = 0xFF; + pgeneral->dss_config__min_spads_limit = 0x01; + + + + + ptiming->mm_config__timeout_macrop_a_hi = 0x00; + ptiming->mm_config__timeout_macrop_a_lo = 0x1a; + ptiming->mm_config__timeout_macrop_b_hi = 0x00; + ptiming->mm_config__timeout_macrop_b_lo = 0x20; + + ptiming->range_config__timeout_macrop_a_hi = 0x01; + ptiming->range_config__timeout_macrop_a_lo = 0xCC; + + ptiming->range_config__vcsel_period_a = 0x0B; + + ptiming->range_config__timeout_macrop_b_hi = 0x01; + ptiming->range_config__timeout_macrop_b_lo = 0xF5; + + ptiming->range_config__vcsel_period_b = 0x09; + + ptiming->range_config__sigma_thresh = + ptuning_parms->tp_lite_med_sigma_thresh_mm; + + ptiming->range_config__min_count_rate_rtn_limit_mcps = + ptuning_parms->tp_lite_med_min_count_rate_rtn_mcps; + + + ptiming->range_config__valid_phase_low = 0x08; + ptiming->range_config__valid_phase_high = 0x78; + ptiming->system__intermeasurement_period = 0x00000000; + ptiming->system__fractional_enable = 0x00; + + + + phistogram->histogram_config__low_amb_even_bin_0_1 = 0x07; + phistogram->histogram_config__low_amb_even_bin_2_3 = 0x21; + phistogram->histogram_config__low_amb_even_bin_4_5 = 0x43; + + phistogram->histogram_config__low_amb_odd_bin_0_1 = 0x10; + phistogram->histogram_config__low_amb_odd_bin_2_3 = 0x32; + phistogram->histogram_config__low_amb_odd_bin_4_5 = 0x54; + + phistogram->histogram_config__mid_amb_even_bin_0_1 = 0x07; + phistogram->histogram_config__mid_amb_even_bin_2_3 = 0x21; + phistogram->histogram_config__mid_amb_even_bin_4_5 = 0x43; + + phistogram->histogram_config__mid_amb_odd_bin_0_1 = 0x10; + phistogram->histogram_config__mid_amb_odd_bin_2 = 0x02; + phistogram->histogram_config__mid_amb_odd_bin_3_4 = 0x43; + phistogram->histogram_config__mid_amb_odd_bin_5 = 0x05; + + phistogram->histogram_config__user_bin_offset = 0x00; + + phistogram->histogram_config__high_amb_even_bin_0_1 = 0x07; + phistogram->histogram_config__high_amb_even_bin_2_3 = 0x21; + phistogram->histogram_config__high_amb_even_bin_4_5 = 0x43; + + phistogram->histogram_config__high_amb_odd_bin_0_1 = 0x10; + phistogram->histogram_config__high_amb_odd_bin_2_3 = 0x32; + phistogram->histogram_config__high_amb_odd_bin_4_5 = 0x54; + + phistogram->histogram_config__amb_thresh_low = 0xFFFF; + phistogram->histogram_config__amb_thresh_high = 0xFFFF; + + phistogram->histogram_config__spad_array_selection = 0x00; + + + pzone_cfg->max_zones = VL53L1_MAX_USER_ZONES; + pzone_cfg->active_zones = 0x00; + pzone_cfg->user_zones[0].height = 0x0f; + pzone_cfg->user_zones[0].width = 0x0f; + pzone_cfg->user_zones[0].x_centre = 0x08; + pzone_cfg->user_zones[0].y_centre = 0x08; + + + + pdynamic->system__grouped_parameter_hold_0 = 0x01; + + pdynamic->system__thresh_high = 0x0000; + pdynamic->system__thresh_low = 0x0000; + pdynamic->system__enable_xtalk_per_quadrant = 0x00; + pdynamic->system__seed_config = + ptuning_parms->tp_lite_seed_cfg; + + + pdynamic->sd_config__woi_sd0 = 0x0B; + + pdynamic->sd_config__woi_sd1 = 0x09; + + pdynamic->sd_config__initial_phase_sd0 = + ptuning_parms->tp_init_phase_rtn_lite_med; + pdynamic->sd_config__initial_phase_sd1 = + ptuning_parms->tp_init_phase_ref_lite_med; + + pdynamic->system__grouped_parameter_hold_1 = 0x01; + + + + pdynamic->sd_config__first_order_select = + ptuning_parms->tp_lite_first_order_select; + pdynamic->sd_config__quantifier = + ptuning_parms->tp_lite_quantifier; + + + pdynamic->roi_config__user_roi_centre_spad = 0xC7; + + pdynamic->roi_config__user_roi_requested_global_xy_size = 0xFF; + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM2_EN | + VL53L1_SEQUENCE_RANGE_EN; + + pdynamic->system__grouped_parameter_hold = 0x02; + + + + + psystem->system__stream_count_ctrl = 0x00; + psystem->firmware__enable = 0x01; + psystem->system__interrupt_clear = + VL53L1_CLEAR_RANGE_INT; + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_STREAMING | + VL53L1_DEVICEREADOUTMODE_SINGLE_SD | + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_standard_ranging_short_range( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = VL53L1_preset_mode_standard_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + ptiming->range_config__vcsel_period_a = 0x07; + ptiming->range_config__vcsel_period_b = 0x05; + ptiming->range_config__sigma_thresh = + ptuning_parms->tp_lite_short_sigma_thresh_mm; + ptiming->range_config__min_count_rate_rtn_limit_mcps = + ptuning_parms->tp_lite_short_min_count_rate_rtn_mcps; + ptiming->range_config__valid_phase_low = 0x08; + ptiming->range_config__valid_phase_high = 0x38; + + + + pdynamic->sd_config__woi_sd0 = 0x07; + pdynamic->sd_config__woi_sd1 = 0x05; + pdynamic->sd_config__initial_phase_sd0 = + ptuning_parms->tp_init_phase_rtn_lite_short; + pdynamic->sd_config__initial_phase_sd1 = + ptuning_parms->tp_init_phase_ref_lite_short; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_standard_ranging_long_range( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = VL53L1_preset_mode_standard_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + ptiming->range_config__vcsel_period_a = 0x0F; + ptiming->range_config__vcsel_period_b = 0x0D; + ptiming->range_config__sigma_thresh = + ptuning_parms->tp_lite_long_sigma_thresh_mm; + ptiming->range_config__min_count_rate_rtn_limit_mcps = + ptuning_parms->tp_lite_long_min_count_rate_rtn_mcps; + ptiming->range_config__valid_phase_low = 0x08; + ptiming->range_config__valid_phase_high = 0xB8; + + + + pdynamic->sd_config__woi_sd0 = 0x0F; + pdynamic->sd_config__woi_sd1 = 0x0D; + pdynamic->sd_config__initial_phase_sd0 = + ptuning_parms->tp_init_phase_rtn_lite_long; + pdynamic->sd_config__initial_phase_sd1 = + ptuning_parms->tp_init_phase_ref_lite_long; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_standard_ranging_mm1_cal( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = VL53L1_preset_mode_standard_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + pgeneral->dss_config__roi_mode_control = + VL53L1_DEVICEDSSMODE__REQUESTED_EFFFECTIVE_SPADS; + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM1_EN; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_standard_ranging_mm2_cal( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = VL53L1_preset_mode_standard_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + pgeneral->dss_config__roi_mode_control = + VL53L1_DEVICEDSSMODE__REQUESTED_EFFFECTIVE_SPADS; + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM2_EN; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_timed_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = VL53L1_preset_mode_standard_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + + pdynamic->system__grouped_parameter_hold = 0x00; + + + ptiming->range_config__timeout_macrop_a_hi = 0x00; + ptiming->range_config__timeout_macrop_a_lo = 0xB1; + + ptiming->range_config__timeout_macrop_b_hi = 0x00; + ptiming->range_config__timeout_macrop_b_lo = 0xD4; + + + + ptiming->system__intermeasurement_period = 0x00000600; + pdynamic->system__seed_config = + ptuning_parms->tp_timed_seed_cfg; + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_PSEUDO_SOLO | + VL53L1_DEVICEREADOUTMODE_SINGLE_SD | + VL53L1_DEVICEMEASUREMENTMODE_TIMED; + } + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_preset_mode_timed_ranging_short_range( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = VL53L1_preset_mode_standard_ranging_short_range( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + + pdynamic->system__grouped_parameter_hold = 0x00; + + + + + + ptiming->range_config__timeout_macrop_a_hi = 0x01; + ptiming->range_config__timeout_macrop_a_lo = 0x84; + + ptiming->range_config__timeout_macrop_b_hi = 0x01; + ptiming->range_config__timeout_macrop_b_lo = 0xB1; + + ptiming->system__intermeasurement_period = 0x00000600; + pdynamic->system__seed_config = + ptuning_parms->tp_timed_seed_cfg; + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_PSEUDO_SOLO | + VL53L1_DEVICEREADOUTMODE_SINGLE_SD | + VL53L1_DEVICEMEASUREMENTMODE_TIMED; + } + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_preset_mode_timed_ranging_long_range( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = VL53L1_preset_mode_standard_ranging_long_range( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + + pdynamic->system__grouped_parameter_hold = 0x00; + + + + + + ptiming->range_config__timeout_macrop_a_hi = 0x00; + ptiming->range_config__timeout_macrop_a_lo = 0x97; + + ptiming->range_config__timeout_macrop_b_hi = 0x00; + ptiming->range_config__timeout_macrop_b_lo = 0xB1; + + ptiming->system__intermeasurement_period = 0x00000600; + pdynamic->system__seed_config = + ptuning_parms->tp_timed_seed_cfg; + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_PSEUDO_SOLO | + VL53L1_DEVICEREADOUTMODE_SINGLE_SD | + VL53L1_DEVICEMEASUREMENTMODE_TIMED; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_low_power_auto_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg, + VL53L1_low_power_auto_data_t *plpadata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = VL53L1_preset_mode_timed_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_config_low_power_auto_mode( + pgeneral, + pdynamic, + plpadata + ); + } + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_preset_mode_low_power_auto_short_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg, + VL53L1_low_power_auto_data_t *plpadata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = VL53L1_preset_mode_timed_ranging_short_range( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_config_low_power_auto_mode( + pgeneral, + pdynamic, + plpadata + ); + } + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_preset_mode_low_power_auto_long_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg, + VL53L1_low_power_auto_data_t *plpadata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = VL53L1_preset_mode_timed_ranging_long_range( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_config_low_power_auto_mode( + pgeneral, + pdynamic, + plpadata + ); + } + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_preset_mode_singleshot_ranging( + + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = VL53L1_preset_mode_standard_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + + pdynamic->system__grouped_parameter_hold = 0x00; + + + + + ptiming->range_config__timeout_macrop_a_hi = 0x00; + ptiming->range_config__timeout_macrop_a_lo = 0xB1; + + ptiming->range_config__timeout_macrop_b_hi = 0x00; + ptiming->range_config__timeout_macrop_b_lo = 0xD4; + + pdynamic->system__seed_config = + ptuning_parms->tp_timed_seed_cfg; + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_PSEUDO_SOLO | + VL53L1_DEVICEREADOUTMODE_SINGLE_SD | + VL53L1_DEVICEMEASUREMENTMODE_SINGLESHOT; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_standard_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + pstatic->dss_config__target_total_rate_mcps = 0x1400; + + + + VL53L1_init_histogram_config_structure( + 7, 0, 1, 2, 3, 4, + 0, 1, 2, 3, 4, 5, + phistogram); + + + VL53L1_init_histogram_multizone_config_structure( + 7, 0, 1, 2, 3, 4, + 0, 1, 2, 3, 4, 5, + &(pzone_cfg->multizone_hist_cfg)); + + + + + ptiming->range_config__vcsel_period_a = 0x09; + ptiming->range_config__vcsel_period_b = 0x0B; + pdynamic->sd_config__woi_sd0 = 0x09; + pdynamic->sd_config__woi_sd1 = 0x0B; + + + + + ptiming->mm_config__timeout_macrop_a_hi = 0x00; + ptiming->mm_config__timeout_macrop_a_lo = 0x20; + ptiming->mm_config__timeout_macrop_b_hi = 0x00; + ptiming->mm_config__timeout_macrop_b_lo = 0x1A; + + + ptiming->range_config__timeout_macrop_a_hi = 0x00; + ptiming->range_config__timeout_macrop_a_lo = 0x28; + + + ptiming->range_config__timeout_macrop_b_hi = 0x00; + ptiming->range_config__timeout_macrop_b_lo = 0x21; + + + pgeneral->phasecal_config__timeout_macrop = 0xF5; + + + + phistpostprocess->valid_phase_low = 0x08; + phistpostprocess->valid_phase_high = 0x88; + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + + + VL53L1_SEQUENCE_RANGE_EN; + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM | + VL53L1_DEVICEREADOUTMODE_DUAL_SD | + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_with_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + VL53L1_init_histogram_config_structure( + 7, 0, 1, 2, 3, 4, + 8+0, 8+1, 8+2, 3, 4, 5, + phistogram); + + + VL53L1_init_histogram_multizone_config_structure( + 7, 0, 1, 2, 3, 4, + 8+0, 8+1, 8+2, 3, 4, 5, + &(pzone_cfg->multizone_hist_cfg)); + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM1_EN | + VL53L1_SEQUENCE_RANGE_EN; + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM | + VL53L1_DEVICEREADOUTMODE_DUAL_SD | + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_with_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_ranging_with_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM2_EN | + VL53L1_SEQUENCE_RANGE_EN; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_mm1_cal( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + VL53L1_init_histogram_config_structure( + 7, 8+0, 8+1, 8+2, 8+3, 8+4, + 8+0, 8+1, 8+2, 8+3, 8+4, 8+5, + phistogram); + + + VL53L1_init_histogram_multizone_config_structure( + 7, 8+0, 8+1, 8+2, 8+3, 8+4, + 8+0, 8+1, 8+2, 8+3, 8+4, 8+5, + &(pzone_cfg->multizone_hist_cfg)); + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + pgeneral->dss_config__roi_mode_control = + VL53L1_DEVICEDSSMODE__REQUESTED_EFFFECTIVE_SPADS; + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM1_EN | + VL53L1_SEQUENCE_RANGE_EN; + + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_mm2_cal( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_ranging_mm1_cal( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + if (status == VL53L1_ERROR_NONE) { + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM2_EN | + VL53L1_SEQUENCE_RANGE_EN; + + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_short_timing( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + pstatic->dss_config__target_total_rate_mcps = 0x1400; + + + + VL53L1_init_histogram_config_structure( + 7, 0, 1, 2, 3, 4, + 7, 0, 1, 2, 3, 4, + phistogram); + + + VL53L1_init_histogram_multizone_config_structure( + 7, 0, 1, 2, 3, 4, + 7, 0, 1, 2, 3, 4, + &(pzone_cfg->multizone_hist_cfg)); + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + ptiming->range_config__vcsel_period_a = 0x04; + ptiming->range_config__vcsel_period_b = 0x03; + ptiming->mm_config__timeout_macrop_a_hi = 0x00; + ptiming->mm_config__timeout_macrop_a_lo = 0x42; + ptiming->mm_config__timeout_macrop_b_hi = 0x00; + ptiming->mm_config__timeout_macrop_b_lo = 0x42; + ptiming->range_config__timeout_macrop_a_hi = 0x00; + ptiming->range_config__timeout_macrop_a_lo = 0x52; + ptiming->range_config__timeout_macrop_b_hi = 0x00; + ptiming->range_config__timeout_macrop_b_lo = 0x66; + + pgeneral->cal_config__vcsel_start = 0x04; + + + + pgeneral->phasecal_config__timeout_macrop = 0xa4; + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + + + VL53L1_SEQUENCE_RANGE_EN; + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM | + VL53L1_DEVICEREADOUTMODE_DUAL_SD | + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_long_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + VL53L1_init_histogram_config_structure( + 7, 0, 1, 2, 3, 4, + 0, 1, 2, 3, 4, 5, + phistogram); + + + VL53L1_init_histogram_multizone_config_structure( + 7, 0, 1, 2, 3, 4, + 0, 1, 2, 3, 4, 5, + &(pzone_cfg->multizone_hist_cfg)); + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + ptiming->range_config__vcsel_period_a = 0x09; + ptiming->range_config__vcsel_period_b = 0x0b; + + + + ptiming->mm_config__timeout_macrop_a_hi = 0x00; + ptiming->mm_config__timeout_macrop_a_lo = 0x21; + ptiming->mm_config__timeout_macrop_b_hi = 0x00; + ptiming->mm_config__timeout_macrop_b_lo = 0x1b; + + + + ptiming->range_config__timeout_macrop_a_hi = 0x00; + ptiming->range_config__timeout_macrop_a_lo = 0x29; + ptiming->range_config__timeout_macrop_b_hi = 0x00; + ptiming->range_config__timeout_macrop_b_lo = 0x22; + + + + pgeneral->cal_config__vcsel_start = 0x09; + + + + pgeneral->phasecal_config__timeout_macrop = 0xF5; + + + + pdynamic->sd_config__woi_sd0 = 0x09; + pdynamic->sd_config__woi_sd1 = 0x0B; + pdynamic->sd_config__initial_phase_sd0 = + ptuning_parms->tp_init_phase_rtn_hist_long; + pdynamic->sd_config__initial_phase_sd1 = + ptuning_parms->tp_init_phase_ref_hist_long; + + + + phistpostprocess->valid_phase_low = 0x08; + phistpostprocess->valid_phase_high = 0x88; + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_RANGE_EN; + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM | + VL53L1_DEVICEREADOUTMODE_DUAL_SD | + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_long_range_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_long_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + VL53L1_init_histogram_config_structure( + 7, 0, 1, 2, 3, 4, + 8+0, 8+1, 8+2, 3, 4, 5, + phistogram); + + + VL53L1_init_histogram_multizone_config_structure( + 7, 0, 1, 2, 3, 4, + 8+0, 8+1, 8+2, 3, 4, 5, + &(pzone_cfg->multizone_hist_cfg)); + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM1_EN | + VL53L1_SEQUENCE_RANGE_EN; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_long_range_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_long_range_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM2_EN | + VL53L1_SEQUENCE_RANGE_EN; + } + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_preset_mode_histogram_medium_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + VL53L1_init_histogram_config_structure( + 7, 0, 1, 1, 2, 2, + 0, 1, 2, 1, 2, 3, + phistogram); + + + VL53L1_init_histogram_multizone_config_structure( + 7, 0, 1, 1, 2, 2, + 0, 1, 2, 1, 2, 3, + &(pzone_cfg->multizone_hist_cfg)); + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + ptiming->range_config__vcsel_period_a = 0x05; + ptiming->range_config__vcsel_period_b = 0x07; + + + + ptiming->mm_config__timeout_macrop_a_hi = 0x00; + ptiming->mm_config__timeout_macrop_a_lo = 0x36; + ptiming->mm_config__timeout_macrop_b_hi = 0x00; + ptiming->mm_config__timeout_macrop_b_lo = 0x28; + + + + ptiming->range_config__timeout_macrop_a_hi = 0x00; + ptiming->range_config__timeout_macrop_a_lo = 0x44; + ptiming->range_config__timeout_macrop_b_hi = 0x00; + ptiming->range_config__timeout_macrop_b_lo = 0x33; + + + + pgeneral->cal_config__vcsel_start = 0x05; + + + + pgeneral->phasecal_config__timeout_macrop = 0xF5; + + + + pdynamic->sd_config__woi_sd0 = 0x05; + pdynamic->sd_config__woi_sd1 = 0x07; + pdynamic->sd_config__initial_phase_sd0 = + ptuning_parms->tp_init_phase_rtn_hist_med; + pdynamic->sd_config__initial_phase_sd1 = + ptuning_parms->tp_init_phase_ref_hist_med; + + + + phistpostprocess->valid_phase_low = 0x08; + phistpostprocess->valid_phase_high = 0x48; + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_RANGE_EN; + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM | + VL53L1_DEVICEREADOUTMODE_DUAL_SD | + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_medium_range_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_medium_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + VL53L1_init_histogram_config_structure( + 7, 0, 1, 1, 2, 2, + 8+0, 8+1, 8+2, 1, 2, 3, + phistogram); + + + VL53L1_init_histogram_multizone_config_structure( + 7, 0, 1, 1, 2, 2, + 8+0, 8+1, 8+2, 1, 2, 3, + &(pzone_cfg->multizone_hist_cfg)); + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM1_EN | + VL53L1_SEQUENCE_RANGE_EN; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_medium_range_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_medium_range_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM2_EN | + VL53L1_SEQUENCE_RANGE_EN; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_short_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + VL53L1_init_histogram_config_structure( + 7, 7, 0, 1, 1, 1, + 0, 1, 1, 1, 2, 2, + phistogram); + + + VL53L1_init_histogram_multizone_config_structure( + 7, 7, 0, 1, 1, 1, + 0, 1, 1, 1, 2, 2, + &(pzone_cfg->multizone_hist_cfg)); + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + ptiming->range_config__vcsel_period_a = 0x03; + ptiming->range_config__vcsel_period_b = 0x05; + + + + ptiming->mm_config__timeout_macrop_a_hi = 0x00; + ptiming->mm_config__timeout_macrop_a_lo = 0x52; + ptiming->mm_config__timeout_macrop_b_hi = 0x00; + ptiming->mm_config__timeout_macrop_b_lo = 0x37; + + + + ptiming->range_config__timeout_macrop_a_hi = 0x00; + ptiming->range_config__timeout_macrop_a_lo = 0x66; + ptiming->range_config__timeout_macrop_b_hi = 0x00; + ptiming->range_config__timeout_macrop_b_lo = 0x44; + + + + pgeneral->cal_config__vcsel_start = 0x03; + + + + pgeneral->phasecal_config__timeout_macrop = 0xF5; + + + + pdynamic->sd_config__woi_sd0 = 0x03; + pdynamic->sd_config__woi_sd1 = 0x05; + pdynamic->sd_config__initial_phase_sd0 = + ptuning_parms->tp_init_phase_rtn_hist_short; + pdynamic->sd_config__initial_phase_sd1 = + ptuning_parms->tp_init_phase_ref_hist_short; + + + phistpostprocess->valid_phase_low = 0x08; + phistpostprocess->valid_phase_high = 0x28; + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM1_EN | + + VL53L1_SEQUENCE_RANGE_EN; + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM | + VL53L1_DEVICEREADOUTMODE_DUAL_SD | + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + } + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_preset_mode_special_histogram_short_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_short_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + VL53L1_init_histogram_config_structure( + 7, 7, 0, 0, 1, 1, + 0, 0, 0, 1, 1, 1, + phistogram); + + + VL53L1_init_histogram_multizone_config_structure( + 7, 7, 0, 0, 1, 1, + 0, 0, 0, 1, 1, 1, + &(pzone_cfg->multizone_hist_cfg)); + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + ptiming->range_config__vcsel_period_a = 0x02; + ptiming->range_config__vcsel_period_b = 0x03; + + + + pgeneral->cal_config__vcsel_start = 0x00; + + + + pgeneral->phasecal_config__target = 0x31; + + + + pdynamic->sd_config__woi_sd0 = 0x02; + pdynamic->sd_config__woi_sd1 = 0x03; + pdynamic->sd_config__initial_phase_sd0 = + ptuning_parms->tp_init_phase_rtn_hist_short; + pdynamic->sd_config__initial_phase_sd1 = + ptuning_parms->tp_init_phase_ref_hist_short; + + + + phistpostprocess->valid_phase_low = 0x10; + phistpostprocess->valid_phase_high = 0x18; + + } + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_preset_mode_histogram_short_range_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_short_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + VL53L1_init_histogram_config_structure( + 7, 7, 0, 1, 1, 1, + 8+0, 8+1, 1, 1, 2, 2, + phistogram); + + + VL53L1_init_histogram_multizone_config_structure( + 7, 7, 0, 1, 1, 1, + 8+0, 8+1, 1, 1, 2, 2, + &(pzone_cfg->multizone_hist_cfg)); + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM1_EN | + VL53L1_SEQUENCE_RANGE_EN; + + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_short_range_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_short_range_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM2_EN | + VL53L1_SEQUENCE_RANGE_EN; + } + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_preset_mode_histogram_characterisation( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + pstatic->debug__ctrl = 0x01; + psystem->power_management__go1_power_force = 0x01; + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_RANGE_EN; + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM | + VL53L1_DEVICEREADOUTMODE_SPLIT_MANUAL | + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_xtalk_planar( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_multizone_long_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + status = + VL53L1_zone_preset_xtalk_planar( + pgeneral, + pzone_cfg); + + + + ptiming->range_config__vcsel_period_a = 0x09; + ptiming->range_config__vcsel_period_b = 0x09; + + + + VL53L1_init_histogram_config_structure( + 7, 0, 1, 2, 3, 4, + 7, 0, 1, 2, 3, 4, + phistogram); + + + + VL53L1_init_histogram_multizone_config_structure( + 7, 0, 1, 2, 3, 4, + 7, 0, 1, 2, 3, 4, + &(pzone_cfg->multizone_hist_cfg)); + + + + + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_set_histogram_multizone_initial_bin_config( + pzone_cfg, + phistogram, + &(pzone_cfg->multizone_hist_cfg)); + } + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + } + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_preset_mode_histogram_xtalk_mm1( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + + if (status == VL53L1_ERROR_NONE) { + + + + + + VL53L1_init_histogram_config_structure( + 8+7, 8+0, 8+1, 8+2, 8+3, 8+4, + 8+7, 8+0, 8+1, 8+2, 8+3, 8+4, + phistogram); + + + VL53L1_init_histogram_multizone_config_structure( + 8+7, 8+0, 8+1, 8+2, 8+3, 8+4, + 8+7, 8+0, 8+1, 8+2, 8+3, 8+4, + &(pzone_cfg->multizone_hist_cfg)); + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + + + + ptiming->range_config__vcsel_period_a = 0x09; + ptiming->range_config__vcsel_period_b = 0x09; + + + + ptiming->mm_config__timeout_macrop_a_hi = 0x00; + ptiming->mm_config__timeout_macrop_a_lo = 0x21; + ptiming->mm_config__timeout_macrop_b_hi = 0x00; + ptiming->mm_config__timeout_macrop_b_lo = 0x21; + + + + ptiming->range_config__timeout_macrop_a_hi = 0x00; + ptiming->range_config__timeout_macrop_a_lo = 0x29; + ptiming->range_config__timeout_macrop_b_hi = 0x00; + ptiming->range_config__timeout_macrop_b_lo = 0x29; + + + + pgeneral->cal_config__vcsel_start = 0x09; + + + + pgeneral->phasecal_config__timeout_macrop = 0xF5; + + + + pdynamic->sd_config__woi_sd0 = 0x09; + pdynamic->sd_config__woi_sd1 = 0x09; + pdynamic->sd_config__initial_phase_sd0 = 0x09; + pdynamic->sd_config__initial_phase_sd1 = 0x06; + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM1_EN | + VL53L1_SEQUENCE_RANGE_EN; + + + + + psystem->system__mode_start = + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM | + VL53L1_DEVICEREADOUTMODE_DUAL_SD | + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_xtalk_mm2( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_xtalk_mm1( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + VL53L1_SEQUENCE_MM2_EN | + VL53L1_SEQUENCE_RANGE_EN; + + + + LOG_FUNCTION_END(status); + + return status; +} + + + + +VL53L1_Error VL53L1_preset_mode_histogram_multizone( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_medium_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + status = + VL53L1_init_zone_config_structure( + 4, 8, 2, + 4, 8, 2, + 7, 7, + pzone_cfg); + + pgeneral->global_config__stream_divider = + pzone_cfg->active_zones + 1; + + + + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_set_histogram_multizone_initial_bin_config( + pzone_cfg, + phistogram, + &(pzone_cfg->multizone_hist_cfg)); + } + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + } + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_preset_mode_histogram_multizone_short_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_short_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + status = + VL53L1_init_zone_config_structure( + 4, 8, 2, + 4, 8, 2, + 7, 7, + pzone_cfg); + + pgeneral->global_config__stream_divider = + pzone_cfg->active_zones + 1; + + + + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_set_histogram_multizone_initial_bin_config( + pzone_cfg, + phistogram, + &(pzone_cfg->multizone_hist_cfg) + ); + } + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_preset_mode_histogram_multizone_long_range( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_long_range( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + status = + VL53L1_init_zone_config_structure( + 4, 8, 2, + 4, 8, 2, + 7, 7, + pzone_cfg); + + pgeneral->global_config__stream_divider = + pzone_cfg->active_zones + 1; + + + + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_set_histogram_multizone_initial_bin_config( + pzone_cfg, + phistogram, + &(pzone_cfg->multizone_hist_cfg)); + } + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + } + + LOG_FUNCTION_END(status); + + return status; +} + + + + +VL53L1_Error VL53L1_preset_mode_olt( + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = VL53L1_preset_mode_standard_ranging( + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) + + psystem->system__stream_count_ctrl = 0x01; + + LOG_FUNCTION_END(status); + + return status; +} + + +void VL53L1_copy_hist_cfg_to_static_cfg( + VL53L1_histogram_config_t *phistogram, + VL53L1_static_config_t *pstatic, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic) +{ + + + LOG_FUNCTION_START(""); + + SUPPRESS_UNUSED_WARNING(pgeneral); + + pstatic->sigma_estimator__effective_pulse_width_ns = + phistogram->histogram_config__high_amb_even_bin_0_1; + pstatic->sigma_estimator__effective_ambient_width_ns = + phistogram->histogram_config__high_amb_even_bin_2_3; + pstatic->sigma_estimator__sigma_ref_mm = + phistogram->histogram_config__high_amb_even_bin_4_5; + + pstatic->algo__crosstalk_compensation_valid_height_mm = + phistogram->histogram_config__high_amb_odd_bin_0_1; + + pstatic->spare_host_config__static_config_spare_0 = + phistogram->histogram_config__high_amb_odd_bin_2_3; + pstatic->spare_host_config__static_config_spare_1 = + phistogram->histogram_config__high_amb_odd_bin_4_5; + + pstatic->algo__range_ignore_threshold_mcps = + (((uint16_t)phistogram->histogram_config__mid_amb_even_bin_0_1) + << 8) + + (uint16_t)phistogram->histogram_config__mid_amb_even_bin_2_3; + + pstatic->algo__range_ignore_valid_height_mm = + phistogram->histogram_config__mid_amb_even_bin_4_5; + pstatic->algo__range_min_clip = + phistogram->histogram_config__mid_amb_odd_bin_0_1; + pstatic->algo__consistency_check__tolerance = + phistogram->histogram_config__mid_amb_odd_bin_2; + + pstatic->spare_host_config__static_config_spare_2 = + phistogram->histogram_config__mid_amb_odd_bin_3_4; + pstatic->sd_config__reset_stages_msb = + phistogram->histogram_config__mid_amb_odd_bin_5; + + pstatic->sd_config__reset_stages_lsb = + phistogram->histogram_config__user_bin_offset; + + ptiming->range_config__sigma_thresh = + (((uint16_t)phistogram->histogram_config__low_amb_even_bin_0_1) + << 8) + + (uint16_t)phistogram->histogram_config__low_amb_even_bin_2_3; + + ptiming->range_config__min_count_rate_rtn_limit_mcps = + (((uint16_t)phistogram->histogram_config__low_amb_even_bin_4_5) + << 8) + + (uint16_t)phistogram->histogram_config__low_amb_odd_bin_0_1; + + ptiming->range_config__valid_phase_low = + phistogram->histogram_config__low_amb_odd_bin_2_3; + ptiming->range_config__valid_phase_high = + phistogram->histogram_config__low_amb_odd_bin_4_5; + + pdynamic->system__thresh_high = + phistogram->histogram_config__amb_thresh_low; + + pdynamic->system__thresh_low = + phistogram->histogram_config__amb_thresh_high; + + pdynamic->system__enable_xtalk_per_quadrant = + phistogram->histogram_config__spad_array_selection; + + LOG_FUNCTION_END(0); + +} + +void VL53L1_copy_hist_bins_to_static_cfg( + VL53L1_histogram_config_t *phistogram, + VL53L1_static_config_t *pstatic, + VL53L1_timing_config_t *ptiming) +{ + + + LOG_FUNCTION_START(""); + + pstatic->sigma_estimator__effective_pulse_width_ns = + phistogram->histogram_config__high_amb_even_bin_0_1; + pstatic->sigma_estimator__effective_ambient_width_ns = + phistogram->histogram_config__high_amb_even_bin_2_3; + pstatic->sigma_estimator__sigma_ref_mm = + phistogram->histogram_config__high_amb_even_bin_4_5; + + pstatic->algo__crosstalk_compensation_valid_height_mm = + phistogram->histogram_config__high_amb_odd_bin_0_1; + + pstatic->spare_host_config__static_config_spare_0 = + phistogram->histogram_config__high_amb_odd_bin_2_3; + pstatic->spare_host_config__static_config_spare_1 = + phistogram->histogram_config__high_amb_odd_bin_4_5; + + pstatic->algo__range_ignore_threshold_mcps = + (((uint16_t)phistogram->histogram_config__mid_amb_even_bin_0_1) + << 8) + + (uint16_t)phistogram->histogram_config__mid_amb_even_bin_2_3; + + pstatic->algo__range_ignore_valid_height_mm = + phistogram->histogram_config__mid_amb_even_bin_4_5; + pstatic->algo__range_min_clip = + phistogram->histogram_config__mid_amb_odd_bin_0_1; + pstatic->algo__consistency_check__tolerance = + phistogram->histogram_config__mid_amb_odd_bin_2; + + pstatic->spare_host_config__static_config_spare_2 = + phistogram->histogram_config__mid_amb_odd_bin_3_4; + pstatic->sd_config__reset_stages_msb = + phistogram->histogram_config__mid_amb_odd_bin_5; + + ptiming->range_config__sigma_thresh = + (((uint16_t)phistogram->histogram_config__low_amb_even_bin_0_1) + << 8) + + (uint16_t)phistogram->histogram_config__low_amb_even_bin_2_3; + + ptiming->range_config__min_count_rate_rtn_limit_mcps = + (((uint16_t)phistogram->histogram_config__low_amb_even_bin_4_5) + << 8) + + (uint16_t)phistogram->histogram_config__low_amb_odd_bin_0_1; + + ptiming->range_config__valid_phase_low = + phistogram->histogram_config__low_amb_odd_bin_2_3; + ptiming->range_config__valid_phase_high = + phistogram->histogram_config__low_amb_odd_bin_4_5; + + LOG_FUNCTION_END(0); + +} + + +VL53L1_Error VL53L1_preset_mode_histogram_ranging_ref( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_static_config_t *pstatic, + VL53L1_histogram_config_t *phistogram, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_system_control_t *psystem, + VL53L1_tuning_parm_storage_t *ptuning_parms, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_preset_mode_histogram_ranging( + phistpostprocess, + pstatic, + phistogram, + pgeneral, + ptiming, + pdynamic, + psystem, + ptuning_parms, + pzone_cfg); + + + + if (status == VL53L1_ERROR_NONE) { + + + + phistogram->histogram_config__spad_array_selection = 0x01; + + + + VL53L1_copy_hist_cfg_to_static_cfg( + phistogram, + pstatic, + pgeneral, + ptiming, + pdynamic); + } + + LOG_FUNCTION_END(status); + + return status; +} + + + + + diff --git a/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_api_strings.c b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_api_strings.c new file mode 100644 index 000000000000..1e3b3be12368 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_api_strings.c @@ -0,0 +1,221 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + + + +#include "vl53l1_api_core.h" +#include "vl53l1_api_strings.h" +#include "vl53l1_error_codes.h" +#include "vl53l1_error_strings.h" + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_API, status, fmt, \ + ##__VA_ARGS__) + + +VL53L1_Error VL53L1_get_range_status_string( + uint8_t RangeStatus, + char *pRangeStatusString) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + +#ifdef VL53L1_USE_EMPTY_STRING + SUPPRESS_UNUSED_WARNING(RangeStatus); + VL53L1_COPYSTRING(pRangeStatusString, ""); +#else + switch (RangeStatus) { + case 0: + VL53L1_COPYSTRING(pRangeStatusString, + VL53L1_STRING_RANGESTATUS_RANGEVALID); + break; + case 1: + VL53L1_COPYSTRING(pRangeStatusString, + VL53L1_STRING_RANGESTATUS_SIGMA); + break; + case 2: + VL53L1_COPYSTRING(pRangeStatusString, + VL53L1_STRING_RANGESTATUS_SIGNAL); + break; + case 3: + VL53L1_COPYSTRING(pRangeStatusString, + VL53L1_STRING_RANGESTATUS_MINRANGE); + break; + case 4: + VL53L1_COPYSTRING(pRangeStatusString, + VL53L1_STRING_RANGESTATUS_PHASE); + break; + case 5: + VL53L1_COPYSTRING(pRangeStatusString, + VL53L1_STRING_RANGESTATUS_HW); + break; + + default: + VL53L1_COPYSTRING(pRangeStatusString, + VL53L1_STRING_RANGESTATUS_NONE); + } +#endif + + LOG_FUNCTION_END(status); + return status; +} + + +VL53L1_Error VL53L1_get_pal_state_string( + VL53L1_State PalStateCode, + char *pPalStateString) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + +#ifdef VL53L1_USE_EMPTY_STRING + SUPPRESS_UNUSED_WARNING(PalStateCode); + VL53L1_COPYSTRING(pPalStateString, ""); +#else + switch (PalStateCode) { + case VL53L1_STATE_POWERDOWN: + VL53L1_COPYSTRING(pPalStateString, + VL53L1_STRING_STATE_POWERDOWN); + break; + case VL53L1_STATE_WAIT_STATICINIT: + VL53L1_COPYSTRING(pPalStateString, + VL53L1_STRING_STATE_WAIT_STATICINIT); + break; + case VL53L1_STATE_STANDBY: + VL53L1_COPYSTRING(pPalStateString, + VL53L1_STRING_STATE_STANDBY); + break; + case VL53L1_STATE_IDLE: + VL53L1_COPYSTRING(pPalStateString, + VL53L1_STRING_STATE_IDLE); + break; + case VL53L1_STATE_RUNNING: + VL53L1_COPYSTRING(pPalStateString, + VL53L1_STRING_STATE_RUNNING); + break; + case VL53L1_STATE_RESET: + VL53L1_COPYSTRING(pPalStateString, + VL53L1_STRING_STATE_RESET); + break; + case VL53L1_STATE_UNKNOWN: + VL53L1_COPYSTRING(pPalStateString, + VL53L1_STRING_STATE_UNKNOWN); + break; + case VL53L1_STATE_ERROR: + VL53L1_COPYSTRING(pPalStateString, + VL53L1_STRING_STATE_ERROR); + break; + + default: + VL53L1_COPYSTRING(pPalStateString, + VL53L1_STRING_STATE_UNKNOWN); + } +#endif + + LOG_FUNCTION_END(status); + return status; +} + +VL53L1_Error VL53L1_get_sequence_steps_info( + VL53L1_SequenceStepId SequenceStepId, + char *pSequenceStepsString) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + +#ifdef VL53L1_USE_EMPTY_STRING + SUPPRESS_UNUSED_WARNING(SequenceStepId); + VL53L1_COPYSTRING(pSequenceStepsString, ""); +#else + switch (SequenceStepId) { + case VL53L1_SEQUENCESTEP_VHV: + VL53L1_COPYSTRING(pSequenceStepsString, + VL53L1_STRING_SEQUENCESTEP_VHV); + break; + case VL53L1_SEQUENCESTEP_PHASECAL: + VL53L1_COPYSTRING(pSequenceStepsString, + VL53L1_STRING_SEQUENCESTEP_PHASECAL); + break; + case VL53L1_SEQUENCESTEP_REFPHASE: + VL53L1_COPYSTRING(pSequenceStepsString, + VL53L1_STRING_SEQUENCESTEP_DSS1); + break; + case VL53L1_SEQUENCESTEP_DSS1: + VL53L1_COPYSTRING(pSequenceStepsString, + VL53L1_STRING_SEQUENCESTEP_DSS1); + break; + case VL53L1_SEQUENCESTEP_DSS2: + VL53L1_COPYSTRING(pSequenceStepsString, + VL53L1_STRING_SEQUENCESTEP_DSS2); + break; + case VL53L1_SEQUENCESTEP_MM1: + VL53L1_COPYSTRING(pSequenceStepsString, + VL53L1_STRING_SEQUENCESTEP_MM1); + break; + case VL53L1_SEQUENCESTEP_MM2: + VL53L1_COPYSTRING(pSequenceStepsString, + VL53L1_STRING_SEQUENCESTEP_MM2); + break; + case VL53L1_SEQUENCESTEP_RANGE: + VL53L1_COPYSTRING(pSequenceStepsString, + VL53L1_STRING_SEQUENCESTEP_RANGE); + break; + default: + Status = VL53L1_ERROR_INVALID_PARAMS; + } +#endif + + LOG_FUNCTION_END(Status); + + return Status; +} + +VL53L1_Error VL53L1_get_limit_check_info(uint16_t LimitCheckId, + char *pLimitCheckString) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + +#ifdef VL53L1_USE_EMPTY_STRING + SUPPRESS_UNUSED_WARNING(LimitCheckId); + VL53L1_COPYSTRING(pLimitCheckString, ""); +#else + switch (LimitCheckId) { + case VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE: + VL53L1_COPYSTRING(pLimitCheckString, + VL53L1_STRING_CHECKENABLE_SIGMA_FINAL_RANGE); + break; + case VL53L1_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE: + VL53L1_COPYSTRING(pLimitCheckString, + VL53L1_STRING_CHECKENABLE_SIGNAL_RATE_FINAL_RANGE); + break; + default: + VL53L1_COPYSTRING(pLimitCheckString, + VL53L1_STRING_UNKNOW_ERROR_CODE); + } +#endif + + LOG_FUNCTION_END(Status); + return Status; +} + + diff --git a/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_core.c b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_core.c new file mode 100644 index 000000000000..44fe2646228f --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_core.c @@ -0,0 +1,4838 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#include "vl53l1_ll_def.h" +#include "vl53l1_ll_device.h" +#include "vl53l1_platform.h" +#include "vl53l1_register_map.h" +#include "vl53l1_register_funcs.h" +#include "vl53l1_register_settings.h" +#include "vl53l1_hist_structs.h" +#include "vl53l1_api_preset_modes.h" +#include "vl53l1_core.h" +#include "vl53l1_tuning_parm_defaults.h" + + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_CORE, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_CORE, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_CORE, \ + status, fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_CORE, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + + +void VL53L1_init_version( + VL53L1_DEV Dev) +{ + + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + pdev->version.ll_major = VL53L1_LL_API_IMPLEMENTATION_VER_MAJOR; + pdev->version.ll_minor = VL53L1_LL_API_IMPLEMENTATION_VER_MINOR; + pdev->version.ll_build = VL53L1_LL_API_IMPLEMENTATION_VER_SUB; + pdev->version.ll_revision = VL53L1_LL_API_IMPLEMENTATION_VER_REVISION; +} + + +void VL53L1_init_ll_driver_state( + VL53L1_DEV Dev, + VL53L1_DeviceState device_state) +{ + + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_ll_driver_state_t *pstate = &(pdev->ll_state); + + pstate->cfg_device_state = device_state; + pstate->cfg_stream_count = 0; + pstate->cfg_gph_id = VL53L1_GROUPEDPARAMETERHOLD_ID_MASK; + pstate->cfg_timing_status = 0; + pstate->cfg_zone_id = 0; + + pstate->rd_device_state = device_state; + pstate->rd_stream_count = 0; + pstate->rd_gph_id = VL53L1_GROUPEDPARAMETERHOLD_ID_MASK; + pstate->rd_timing_status = 0; + pstate->rd_zone_id = 0; + +} + + +VL53L1_Error VL53L1_update_ll_driver_rd_state( + VL53L1_DEV Dev) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_ll_driver_state_t *pstate = &(pdev->ll_state); + + + + LOG_FUNCTION_START(""); + + + + if ((pdev->sys_ctrl.system__mode_start & + VL53L1_DEVICEMEASUREMENTMODE_MODE_MASK) == 0x00) { + + pstate->rd_device_state = VL53L1_DEVICESTATE_SW_STANDBY; + pstate->rd_stream_count = 0; + pstate->rd_internal_stream_count = 0; + pstate->rd_internal_stream_count_val = 0; + pstate->rd_gph_id = VL53L1_GROUPEDPARAMETERHOLD_ID_MASK; + pstate->rd_timing_status = 0; + pstate->rd_zone_id = 0; + + } else { + + + + if (pstate->rd_stream_count == 0xFF) + pstate->rd_stream_count = 0x80; + else + pstate->rd_stream_count++; + + + status = VL53L1_update_internal_stream_counters(Dev, + pstate->rd_stream_count, + &(pstate->rd_internal_stream_count), + &(pstate->rd_internal_stream_count_val)); + + + + pstate->rd_gph_id ^= VL53L1_GROUPEDPARAMETERHOLD_ID_MASK; + + + + switch (pstate->rd_device_state) { + + case VL53L1_DEVICESTATE_SW_STANDBY: + + if ((pdev->dyn_cfg.system__grouped_parameter_hold & + VL53L1_GROUPEDPARAMETERHOLD_ID_MASK) > 0) { + pstate->rd_device_state = + VL53L1_DEVICESTATE_RANGING_WAIT_GPH_SYNC; + } else { + if (pstate->rd_zone_id >= + pdev->zone_cfg.active_zones) + pstate->rd_device_state = + VL53L1_DEVICESTATE_RANGING_OUTPUT_DATA; + else + pstate->rd_device_state = + VL53L1_DEVICESTATE_RANGING_GATHER_DATA; + } + + pstate->rd_stream_count = 0; + pstate->rd_internal_stream_count = 0; + pstate->rd_internal_stream_count_val = 0; + pstate->rd_timing_status = 0; + pstate->rd_zone_id = 0; + + break; + + case VL53L1_DEVICESTATE_RANGING_WAIT_GPH_SYNC: + pstate->rd_stream_count = 0; + pstate->rd_internal_stream_count = 0; + pstate->rd_internal_stream_count_val = 0; + pstate->rd_zone_id = 0; + if (pstate->rd_zone_id >= + pdev->zone_cfg.active_zones) + pstate->rd_device_state = + VL53L1_DEVICESTATE_RANGING_OUTPUT_DATA; + else + pstate->rd_device_state = + VL53L1_DEVICESTATE_RANGING_GATHER_DATA; + + break; + + case VL53L1_DEVICESTATE_RANGING_GATHER_DATA: + pstate->rd_zone_id++; + if (pstate->rd_zone_id >= + pdev->zone_cfg.active_zones) + pstate->rd_device_state = + VL53L1_DEVICESTATE_RANGING_OUTPUT_DATA; + else + pstate->rd_device_state = + VL53L1_DEVICESTATE_RANGING_GATHER_DATA; + + break; + + case VL53L1_DEVICESTATE_RANGING_OUTPUT_DATA: + pstate->rd_zone_id = 0; + pstate->rd_timing_status ^= 0x01; + + if (pstate->rd_zone_id >= + pdev->zone_cfg.active_zones) + pstate->rd_device_state = + VL53L1_DEVICESTATE_RANGING_OUTPUT_DATA; + else + pstate->rd_device_state = + VL53L1_DEVICESTATE_RANGING_GATHER_DATA; + break; + + default: + pstate->rd_device_state = + VL53L1_DEVICESTATE_SW_STANDBY; + pstate->rd_stream_count = 0; + pstate->rd_internal_stream_count = 0; + pstate->rd_internal_stream_count_val = 0; + pstate->rd_gph_id = VL53L1_GROUPEDPARAMETERHOLD_ID_MASK; + pstate->rd_timing_status = 0; + pstate->rd_zone_id = 0; + break; + } + } + + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_check_ll_driver_rd_state( + VL53L1_DEV Dev) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + VL53L1_ll_driver_state_t *pstate = &(pdev->ll_state); + VL53L1_system_results_t *psys_results = &(pdev->sys_results); + VL53L1_histogram_bin_data_t *phist_data = &(pdev->hist_data); + VL53L1_zone_private_dyn_cfgs_t *pZ = &(pres->zone_dyn_cfgs); + + uint8_t device_range_status = 0; + uint8_t device_stream_count = 0; + uint8_t device_gph_id = 0; + uint8_t histogram_mode = 0; + uint8_t expected_stream_count = 0; + uint8_t expected_gph_id = 0; + + LOG_FUNCTION_START(""); + + + + device_range_status = + psys_results->result__range_status & + VL53L1_RANGE_STATUS__RANGE_STATUS_MASK; + + device_stream_count = psys_results->result__stream_count; + + + + histogram_mode = + (pdev->sys_ctrl.system__mode_start & + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM) == + VL53L1_DEVICESCHEDULERMODE_HISTOGRAM; + + + device_gph_id = (psys_results->result__interrupt_status & + VL53L1_INTERRUPT_STATUS__GPH_ID_INT_STATUS_MASK) >> 4; + + if (histogram_mode) + device_gph_id = (phist_data->result__interrupt_status & + VL53L1_INTERRUPT_STATUS__GPH_ID_INT_STATUS_MASK) >> 4; + + + + if (!((pdev->sys_ctrl.system__mode_start & + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK) == + VL53L1_DEVICEMEASUREMENTMODE_BACKTOBACK)) + goto ENDFUNC; + + + + if (pstate->rd_device_state == + VL53L1_DEVICESTATE_RANGING_WAIT_GPH_SYNC) { + + if (histogram_mode == 0) { + if (device_range_status != + VL53L1_DEVICEERROR_GPHSTREAMCOUNT0READY) + status = + VL53L1_ERROR_GPH_SYNC_CHECK_FAIL; + + } + } else { + if (pstate->rd_stream_count != device_stream_count) + status = VL53L1_ERROR_STREAM_COUNT_CHECK_FAIL; + + + if (pstate->rd_gph_id != device_gph_id) + status = VL53L1_ERROR_GPH_ID_CHECK_FAIL; + + + + + expected_stream_count = + pZ->VL53L1_p_002[pstate->rd_zone_id].expected_stream_count; + expected_gph_id = + pZ->VL53L1_p_002[pstate->rd_zone_id].expected_gph_id; + + + + if (expected_stream_count != device_stream_count) { + + + if (!((pdev->zone_cfg.active_zones == 0) && + (device_stream_count == 255))) + status = + VL53L1_ERROR_ZONE_STREAM_COUNT_CHECK_FAIL; + + + } + + + + if (expected_gph_id != device_gph_id) + status = VL53L1_ERROR_ZONE_GPH_ID_CHECK_FAIL; + + } + + + +ENDFUNC: + LOG_FUNCTION_END(status); + return status; +} + + +VL53L1_Error VL53L1_update_ll_driver_cfg_state( + VL53L1_DEV Dev) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + VL53L1_ll_driver_state_t *pstate = &(pdev->ll_state); + VL53L1_zone_private_dyn_cfgs_t *pZ = &(pres->zone_dyn_cfgs); + + uint8_t prev_cfg_zone_id; + uint8_t prev_cfg_gph_id; + uint8_t prev_cfg_stream_count; + + LOG_FUNCTION_START(""); + + + + + + if ((pdev->sys_ctrl.system__mode_start & + VL53L1_DEVICEMEASUREMENTMODE_MODE_MASK) == 0x00) { + + pstate->cfg_device_state = VL53L1_DEVICESTATE_SW_STANDBY; + pstate->cfg_stream_count = 0; + pstate->cfg_internal_stream_count = 0; + pstate->cfg_internal_stream_count_val = 0; + pstate->cfg_gph_id = VL53L1_GROUPEDPARAMETERHOLD_ID_MASK; + pstate->cfg_timing_status = 0; + pstate->cfg_zone_id = 0; + prev_cfg_zone_id = 0; + prev_cfg_gph_id = 0; + prev_cfg_stream_count = 0; + + } else { + + prev_cfg_gph_id = pstate->cfg_gph_id; + prev_cfg_zone_id = pstate->cfg_zone_id; + prev_cfg_stream_count = pstate->cfg_stream_count; + + + + if (pstate->cfg_stream_count == 0xFF) + pstate->cfg_stream_count = 0x80; + else + pstate->cfg_stream_count++; + + + status = VL53L1_update_internal_stream_counters( + Dev, + pstate->cfg_stream_count, + &(pstate->cfg_internal_stream_count), + &(pstate->cfg_internal_stream_count_val)); + + + + pstate->cfg_gph_id ^= VL53L1_GROUPEDPARAMETERHOLD_ID_MASK; + + + + switch (pstate->cfg_device_state) { + + case VL53L1_DEVICESTATE_SW_STANDBY: + pstate->cfg_zone_id = 1; + if (pstate->cfg_zone_id > + pdev->zone_cfg.active_zones) { + pstate->cfg_zone_id = 0; + pstate->cfg_timing_status ^= 0x01; + } + pstate->cfg_stream_count = 1; + + if (pdev->gen_cfg.global_config__stream_divider == 0) { + pstate->cfg_internal_stream_count = 1; + pstate->cfg_internal_stream_count_val = 0; + } else { + pstate->cfg_internal_stream_count = 0; + pstate->cfg_internal_stream_count_val = 1; + } + pstate->cfg_device_state = + VL53L1_DEVICESTATE_RANGING_DSS_AUTO; + break; + + case VL53L1_DEVICESTATE_RANGING_DSS_AUTO: + pstate->cfg_zone_id++; + if (pstate->cfg_zone_id > + pdev->zone_cfg.active_zones) { + + pstate->cfg_zone_id = 0; + pstate->cfg_timing_status ^= 0x01; + + + + + if (pdev->zone_cfg.active_zones > 0) { + pstate->cfg_device_state = + VL53L1_DEVICESTATE_RANGING_DSS_MANUAL; + } + } + break; + + case VL53L1_DEVICESTATE_RANGING_DSS_MANUAL: + pstate->cfg_zone_id++; + if (pstate->cfg_zone_id > + pdev->zone_cfg.active_zones) { + pstate->cfg_zone_id = 0; + pstate->cfg_timing_status ^= 0x01; + } + break; + + default: + pstate->cfg_device_state = + VL53L1_DEVICESTATE_SW_STANDBY; + pstate->cfg_stream_count = 0; + pstate->cfg_internal_stream_count = 0; + pstate->cfg_internal_stream_count_val = 0; + pstate->cfg_gph_id = + VL53L1_GROUPEDPARAMETERHOLD_ID_MASK; + pstate->cfg_timing_status = 0; + pstate->cfg_zone_id = 0; + break; + } + } + + + if (pdev->zone_cfg.active_zones == 0) { + + pZ->VL53L1_p_002[prev_cfg_zone_id].expected_stream_count + = prev_cfg_stream_count - 1; + + pZ->VL53L1_p_002[pstate->rd_zone_id].expected_gph_id = + prev_cfg_gph_id ^ VL53L1_GROUPEDPARAMETERHOLD_ID_MASK; + } else { + pZ->VL53L1_p_002[prev_cfg_zone_id].expected_stream_count + = prev_cfg_stream_count; + pZ->VL53L1_p_002[prev_cfg_zone_id].expected_gph_id = + prev_cfg_gph_id; + } + + + + LOG_FUNCTION_END(status); + + return status; +} + + +void VL53L1_copy_rtn_good_spads_to_buffer( + VL53L1_nvm_copy_data_t *pdata, + uint8_t *pbuffer) +{ + + + *(pbuffer + 0) = pdata->global_config__spad_enables_rtn_0; + *(pbuffer + 1) = pdata->global_config__spad_enables_rtn_1; + *(pbuffer + 2) = pdata->global_config__spad_enables_rtn_2; + *(pbuffer + 3) = pdata->global_config__spad_enables_rtn_3; + *(pbuffer + 4) = pdata->global_config__spad_enables_rtn_4; + *(pbuffer + 5) = pdata->global_config__spad_enables_rtn_5; + *(pbuffer + 6) = pdata->global_config__spad_enables_rtn_6; + *(pbuffer + 7) = pdata->global_config__spad_enables_rtn_7; + *(pbuffer + 8) = pdata->global_config__spad_enables_rtn_8; + *(pbuffer + 9) = pdata->global_config__spad_enables_rtn_9; + *(pbuffer + 10) = pdata->global_config__spad_enables_rtn_10; + *(pbuffer + 11) = pdata->global_config__spad_enables_rtn_11; + *(pbuffer + 12) = pdata->global_config__spad_enables_rtn_12; + *(pbuffer + 13) = pdata->global_config__spad_enables_rtn_13; + *(pbuffer + 14) = pdata->global_config__spad_enables_rtn_14; + *(pbuffer + 15) = pdata->global_config__spad_enables_rtn_15; + *(pbuffer + 16) = pdata->global_config__spad_enables_rtn_16; + *(pbuffer + 17) = pdata->global_config__spad_enables_rtn_17; + *(pbuffer + 18) = pdata->global_config__spad_enables_rtn_18; + *(pbuffer + 19) = pdata->global_config__spad_enables_rtn_19; + *(pbuffer + 20) = pdata->global_config__spad_enables_rtn_20; + *(pbuffer + 21) = pdata->global_config__spad_enables_rtn_21; + *(pbuffer + 22) = pdata->global_config__spad_enables_rtn_22; + *(pbuffer + 23) = pdata->global_config__spad_enables_rtn_23; + *(pbuffer + 24) = pdata->global_config__spad_enables_rtn_24; + *(pbuffer + 25) = pdata->global_config__spad_enables_rtn_25; + *(pbuffer + 26) = pdata->global_config__spad_enables_rtn_26; + *(pbuffer + 27) = pdata->global_config__spad_enables_rtn_27; + *(pbuffer + 28) = pdata->global_config__spad_enables_rtn_28; + *(pbuffer + 29) = pdata->global_config__spad_enables_rtn_29; + *(pbuffer + 30) = pdata->global_config__spad_enables_rtn_30; + *(pbuffer + 31) = pdata->global_config__spad_enables_rtn_31; +} + + +void VL53L1_init_system_results( + VL53L1_system_results_t *pdata) +{ + + + pdata->result__interrupt_status = 0xFF; + pdata->result__range_status = 0xFF; + pdata->result__report_status = 0xFF; + pdata->result__stream_count = 0xFF; + + pdata->result__dss_actual_effective_spads_sd0 = 0xFFFF; + pdata->result__peak_signal_count_rate_mcps_sd0 = 0xFFFF; + pdata->result__ambient_count_rate_mcps_sd0 = 0xFFFF; + pdata->result__sigma_sd0 = 0xFFFF; + pdata->result__phase_sd0 = 0xFFFF; + pdata->result__final_crosstalk_corrected_range_mm_sd0 = 0xFFFF; + pdata->result__peak_signal_count_rate_crosstalk_corrected_mcps_sd0 = + 0xFFFF; + pdata->result__mm_inner_actual_effective_spads_sd0 = 0xFFFF; + pdata->result__mm_outer_actual_effective_spads_sd0 = 0xFFFF; + pdata->result__avg_signal_count_rate_mcps_sd0 = 0xFFFF; + + pdata->result__dss_actual_effective_spads_sd1 = 0xFFFF; + pdata->result__peak_signal_count_rate_mcps_sd1 = 0xFFFF; + pdata->result__ambient_count_rate_mcps_sd1 = 0xFFFF; + pdata->result__sigma_sd1 = 0xFFFF; + pdata->result__phase_sd1 = 0xFFFF; + pdata->result__final_crosstalk_corrected_range_mm_sd1 = 0xFFFF; + pdata->result__spare_0_sd1 = 0xFFFF; + pdata->result__spare_1_sd1 = 0xFFFF; + pdata->result__spare_2_sd1 = 0xFFFF; + pdata->result__spare_3_sd1 = 0xFF; + +} + + +void V53L1_init_zone_results_structure( + uint8_t active_zones, + VL53L1_zone_results_t *pdata) +{ + + + + uint8_t z = 0; + VL53L1_zone_objects_t *pobjects; + + pdata->max_zones = VL53L1_MAX_USER_ZONES; + pdata->active_zones = active_zones; + + for (z = 0; z < pdata->max_zones; z++) { + pobjects = &(pdata->VL53L1_p_002[z]); + pobjects->cfg_device_state = VL53L1_DEVICESTATE_SW_STANDBY; + pobjects->rd_device_state = VL53L1_DEVICESTATE_SW_STANDBY; + pobjects->max_objects = VL53L1_MAX_RANGE_RESULTS; + pobjects->active_objects = 0; + } +} + +void V53L1_init_zone_dss_configs( + VL53L1_DEV Dev) +{ + + + + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + uint8_t z = 0; + uint8_t max_zones = VL53L1_MAX_USER_ZONES; + VL53L1_zone_private_dyn_cfgs_t *pdata = &(pres->zone_dyn_cfgs); + + for (z = 0; z < max_zones; z++) { + pdata->VL53L1_p_002[z].dss_mode = + VL53L1_DSS_CONTROL__MODE_TARGET_RATE; + pdata->VL53L1_p_002[z].dss_requested_effective_spad_count = 0; + } +} + + +void VL53L1_init_histogram_config_structure( + uint8_t even_bin0, + uint8_t even_bin1, + uint8_t even_bin2, + uint8_t even_bin3, + uint8_t even_bin4, + uint8_t even_bin5, + uint8_t odd_bin0, + uint8_t odd_bin1, + uint8_t odd_bin2, + uint8_t odd_bin3, + uint8_t odd_bin4, + uint8_t odd_bin5, + VL53L1_histogram_config_t *pdata) +{ + + + pdata->histogram_config__low_amb_even_bin_0_1 = + (even_bin1 << 4) + even_bin0; + pdata->histogram_config__low_amb_even_bin_2_3 = + (even_bin3 << 4) + even_bin2; + pdata->histogram_config__low_amb_even_bin_4_5 = + (even_bin5 << 4) + even_bin4; + + pdata->histogram_config__low_amb_odd_bin_0_1 = + (odd_bin1 << 4) + odd_bin0; + pdata->histogram_config__low_amb_odd_bin_2_3 = + (odd_bin3 << 4) + odd_bin2; + pdata->histogram_config__low_amb_odd_bin_4_5 = + (odd_bin5 << 4) + odd_bin4; + + pdata->histogram_config__mid_amb_even_bin_0_1 = + pdata->histogram_config__low_amb_even_bin_0_1; + pdata->histogram_config__mid_amb_even_bin_2_3 = + pdata->histogram_config__low_amb_even_bin_2_3; + pdata->histogram_config__mid_amb_even_bin_4_5 = + pdata->histogram_config__low_amb_even_bin_4_5; + + pdata->histogram_config__mid_amb_odd_bin_0_1 = + pdata->histogram_config__low_amb_odd_bin_0_1; + pdata->histogram_config__mid_amb_odd_bin_2 = odd_bin2; + pdata->histogram_config__mid_amb_odd_bin_3_4 = + (odd_bin4 << 4) + odd_bin3; + pdata->histogram_config__mid_amb_odd_bin_5 = odd_bin5; + + pdata->histogram_config__user_bin_offset = 0x00; + + pdata->histogram_config__high_amb_even_bin_0_1 = + pdata->histogram_config__low_amb_even_bin_0_1; + pdata->histogram_config__high_amb_even_bin_2_3 = + pdata->histogram_config__low_amb_even_bin_2_3; + pdata->histogram_config__high_amb_even_bin_4_5 = + pdata->histogram_config__low_amb_even_bin_4_5; + + pdata->histogram_config__high_amb_odd_bin_0_1 = + pdata->histogram_config__low_amb_odd_bin_0_1; + pdata->histogram_config__high_amb_odd_bin_2_3 = + pdata->histogram_config__low_amb_odd_bin_2_3; + pdata->histogram_config__high_amb_odd_bin_4_5 = + pdata->histogram_config__low_amb_odd_bin_4_5; + + + + pdata->histogram_config__amb_thresh_low = 0xFFFF; + pdata->histogram_config__amb_thresh_high = 0xFFFF; + + + + pdata->histogram_config__spad_array_selection = 0x00; + +} + +void VL53L1_init_histogram_multizone_config_structure( + uint8_t even_bin0, + uint8_t even_bin1, + uint8_t even_bin2, + uint8_t even_bin3, + uint8_t even_bin4, + uint8_t even_bin5, + uint8_t odd_bin0, + uint8_t odd_bin1, + uint8_t odd_bin2, + uint8_t odd_bin3, + uint8_t odd_bin4, + uint8_t odd_bin5, + VL53L1_histogram_config_t *pdata) +{ + + + pdata->histogram_config__low_amb_even_bin_0_1 = + (even_bin1 << 4) + even_bin0; + pdata->histogram_config__low_amb_even_bin_2_3 = + (even_bin3 << 4) + even_bin2; + pdata->histogram_config__low_amb_even_bin_4_5 = + (even_bin5 << 4) + even_bin4; + + pdata->histogram_config__low_amb_odd_bin_0_1 = + pdata->histogram_config__low_amb_even_bin_0_1; + pdata->histogram_config__low_amb_odd_bin_2_3 + = pdata->histogram_config__low_amb_even_bin_2_3; + pdata->histogram_config__low_amb_odd_bin_4_5 + = pdata->histogram_config__low_amb_even_bin_4_5; + + pdata->histogram_config__mid_amb_even_bin_0_1 = + pdata->histogram_config__low_amb_even_bin_0_1; + pdata->histogram_config__mid_amb_even_bin_2_3 + = pdata->histogram_config__low_amb_even_bin_2_3; + pdata->histogram_config__mid_amb_even_bin_4_5 + = pdata->histogram_config__low_amb_even_bin_4_5; + + pdata->histogram_config__mid_amb_odd_bin_0_1 + = pdata->histogram_config__low_amb_odd_bin_0_1; + pdata->histogram_config__mid_amb_odd_bin_2 = odd_bin2; + pdata->histogram_config__mid_amb_odd_bin_3_4 = + (odd_bin4 << 4) + odd_bin3; + pdata->histogram_config__mid_amb_odd_bin_5 = odd_bin5; + + pdata->histogram_config__user_bin_offset = 0x00; + + pdata->histogram_config__high_amb_even_bin_0_1 = + (odd_bin1 << 4) + odd_bin0; + pdata->histogram_config__high_amb_even_bin_2_3 = + (odd_bin3 << 4) + odd_bin2; + pdata->histogram_config__high_amb_even_bin_4_5 = + (odd_bin5 << 4) + odd_bin4; + + pdata->histogram_config__high_amb_odd_bin_0_1 + = pdata->histogram_config__high_amb_even_bin_0_1; + pdata->histogram_config__high_amb_odd_bin_2_3 + = pdata->histogram_config__high_amb_even_bin_2_3; + pdata->histogram_config__high_amb_odd_bin_4_5 + = pdata->histogram_config__high_amb_even_bin_4_5; + + + + pdata->histogram_config__amb_thresh_low = 0xFFFF; + pdata->histogram_config__amb_thresh_high = 0xFFFF; + + + + pdata->histogram_config__spad_array_selection = 0x00; +} + + +void VL53L1_init_xtalk_bin_data_struct( + uint32_t bin_value, + uint16_t VL53L1_p_024, + VL53L1_xtalk_histogram_shape_t *pdata) +{ + + + + uint16_t i = 0; + + pdata->zone_id = 0; + pdata->time_stamp = 0; + + pdata->VL53L1_p_022 = 0; + pdata->VL53L1_p_023 = VL53L1_XTALK_HISTO_BINS; + pdata->VL53L1_p_024 = (uint8_t)VL53L1_p_024; + + pdata->phasecal_result__reference_phase = 0; + pdata->phasecal_result__vcsel_start = 0; + pdata->cal_config__vcsel_start = 0; + + pdata->vcsel_width = 0; + pdata->VL53L1_p_019 = 0; + + pdata->zero_distance_phase = 0; + + for (i = 0; i < VL53L1_XTALK_HISTO_BINS; i++) { + if (i < VL53L1_p_024) + pdata->bin_data[i] = bin_value; + else + pdata->bin_data[i] = 0; + } +} + + +void VL53L1_i2c_encode_uint16_t( + uint16_t ip_value, + uint16_t count, + uint8_t *pbuffer) +{ + + + uint16_t i = 0; + uint16_t VL53L1_p_002 = 0; + + VL53L1_p_002 = ip_value; + + for (i = 0; i < count; i++) { + pbuffer[count-i-1] = (uint8_t)(VL53L1_p_002 & 0x00FF); + VL53L1_p_002 = VL53L1_p_002 >> 8; + } +} + +uint16_t VL53L1_i2c_decode_uint16_t( + uint16_t count, + uint8_t *pbuffer) +{ + + + uint16_t value = 0x00; + + while (count-- > 0) + value = (value << 8) | (uint16_t)*pbuffer++; + + return value; +} + + +void VL53L1_i2c_encode_int16_t( + int16_t ip_value, + uint16_t count, + uint8_t *pbuffer) +{ + + + uint16_t i = 0; + int16_t VL53L1_p_002 = 0; + + VL53L1_p_002 = ip_value; + + for (i = 0; i < count; i++) { + pbuffer[count-i-1] = (uint8_t)(VL53L1_p_002 & 0x00FF); + VL53L1_p_002 = VL53L1_p_002 >> 8; + } +} + +int16_t VL53L1_i2c_decode_int16_t( + uint16_t count, + uint8_t *pbuffer) +{ + + + int16_t value = 0x00; + + + if (*pbuffer >= 0x80) + value = 0xFFFF; + + while (count-- > 0) + value = (value << 8) | (int16_t)*pbuffer++; + + return value; +} + +void VL53L1_i2c_encode_uint32_t( + uint32_t ip_value, + uint16_t count, + uint8_t *pbuffer) +{ + + + uint16_t i = 0; + uint32_t VL53L1_p_002 = 0; + + VL53L1_p_002 = ip_value; + + for (i = 0; i < count; i++) { + pbuffer[count-i-1] = (uint8_t)(VL53L1_p_002 & 0x00FF); + VL53L1_p_002 = VL53L1_p_002 >> 8; + } +} + +uint32_t VL53L1_i2c_decode_uint32_t( + uint16_t count, + uint8_t *pbuffer) +{ + + + uint32_t value = 0x00; + + while (count-- > 0) + value = (value << 8) | (uint32_t)*pbuffer++; + + return value; +} + + +uint32_t VL53L1_i2c_decode_with_mask( + uint16_t count, + uint8_t *pbuffer, + uint32_t bit_mask, + uint32_t down_shift, + uint32_t offset) +{ + + + uint32_t value = 0x00; + + + while (count-- > 0) + value = (value << 8) | (uint32_t)*pbuffer++; + + + value = value & bit_mask; + if (down_shift > 0) + value = value >> down_shift; + + + value = value + offset; + + return value; +} + + +void VL53L1_i2c_encode_int32_t( + int32_t ip_value, + uint16_t count, + uint8_t *pbuffer) +{ + + + uint16_t i = 0; + int32_t VL53L1_p_002 = 0; + + VL53L1_p_002 = ip_value; + + for (i = 0; i < count; i++) { + pbuffer[count-i-1] = (uint8_t)(VL53L1_p_002 & 0x00FF); + VL53L1_p_002 = VL53L1_p_002 >> 8; + } +} + +int32_t VL53L1_i2c_decode_int32_t( + uint16_t count, + uint8_t *pbuffer) +{ + + + int32_t value = 0x00; + + + if (*pbuffer >= 0x80) + value = 0xFFFFFFFF; + + while (count-- > 0) + value = (value << 8) | (int32_t)*pbuffer++; + + return value; +} + + +VL53L1_Error VL53L1_start_test( + VL53L1_DEV Dev, + uint8_t test_mode__ctrl) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_WrByte( + Dev, + VL53L1_TEST_MODE__CTRL, + test_mode__ctrl); + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_firmware_enable_register( + VL53L1_DEV Dev, + uint8_t value) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + pdev->sys_ctrl.firmware__enable = value; + + status = VL53L1_WrByte( + Dev, + VL53L1_FIRMWARE__ENABLE, + pdev->sys_ctrl.firmware__enable); + + return status; +} + +VL53L1_Error VL53L1_enable_firmware( + VL53L1_DEV Dev) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + status = VL53L1_set_firmware_enable_register(Dev, 0x01); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_disable_firmware( + VL53L1_DEV Dev) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + status = VL53L1_set_firmware_enable_register(Dev, 0x00); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_powerforce_register( + VL53L1_DEV Dev, + uint8_t value) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + pdev->sys_ctrl.power_management__go1_power_force = value; + + status = VL53L1_WrByte( + Dev, + VL53L1_POWER_MANAGEMENT__GO1_POWER_FORCE, + pdev->sys_ctrl.power_management__go1_power_force); + + return status; +} + + +VL53L1_Error VL53L1_enable_powerforce( + VL53L1_DEV Dev) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + status = VL53L1_set_powerforce_register(Dev, 0x01); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_disable_powerforce( + VL53L1_DEV Dev) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + status = VL53L1_set_powerforce_register(Dev, 0x00); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_clear_interrupt( + VL53L1_DEV Dev) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->sys_ctrl.system__interrupt_clear = VL53L1_CLEAR_RANGE_INT; + + status = VL53L1_WrByte( + Dev, + VL53L1_SYSTEM__INTERRUPT_CLEAR, + pdev->sys_ctrl.system__interrupt_clear); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_force_shadow_stream_count_to_zero( + VL53L1_DEV Dev) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_WrByte( + Dev, + VL53L1_SHADOW_RESULT__STREAM_COUNT, + 0x00); + } + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + return status; +} + + +uint32_t VL53L1_calc_macro_period_us( + uint16_t fast_osc_frequency, + uint8_t VL53L1_p_009) +{ + + + uint32_t pll_period_us = 0; + uint8_t VL53L1_p_031 = 0; + uint32_t macro_period_us = 0; + + LOG_FUNCTION_START(""); + + + + pll_period_us = VL53L1_calc_pll_period_us(fast_osc_frequency); + + + + VL53L1_p_031 = VL53L1_decode_vcsel_period(VL53L1_p_009); + + + + macro_period_us = + (uint32_t)VL53L1_MACRO_PERIOD_VCSEL_PERIODS * + pll_period_us; + macro_period_us = macro_period_us >> 6; + + macro_period_us = macro_period_us * (uint32_t)VL53L1_p_031; + macro_period_us = macro_period_us >> 6; + + + + LOG_FUNCTION_END(0); + + return macro_period_us; +} + + +uint16_t VL53L1_calc_range_ignore_threshold( + uint32_t central_rate, + int16_t x_gradient, + int16_t y_gradient, + uint8_t rate_mult) +{ + + + int32_t range_ignore_thresh_int = 0; + uint16_t range_ignore_thresh_kcps = 0; + int32_t central_rate_int = 0; + int16_t x_gradient_int = 0; + int16_t y_gradient_int = 0; + + LOG_FUNCTION_START(""); + + + + central_rate_int = ((int32_t)central_rate * (1 << 4)) / (1000); + + if (x_gradient < 0) + x_gradient_int = x_gradient * -1; + + if (y_gradient < 0) + y_gradient_int = y_gradient * -1; + + + + + + range_ignore_thresh_int = (8 * x_gradient_int * 4) + + (8 * y_gradient_int * 4); + + + + range_ignore_thresh_int = range_ignore_thresh_int / 1000; + + + + range_ignore_thresh_int = range_ignore_thresh_int + central_rate_int; + + + + range_ignore_thresh_int = (int32_t)rate_mult * range_ignore_thresh_int; + + range_ignore_thresh_int = (range_ignore_thresh_int + (1<<4)) / (1<<5); + + + + if (range_ignore_thresh_int > 0xFFFF) + range_ignore_thresh_kcps = 0xFFFF; + else + range_ignore_thresh_kcps = (uint16_t)range_ignore_thresh_int; + + + + LOG_FUNCTION_END(0); + + return range_ignore_thresh_kcps; +} + + +uint32_t VL53L1_calc_timeout_mclks( + uint32_t timeout_us, + uint32_t macro_period_us) +{ + + + uint32_t timeout_mclks = 0; + + LOG_FUNCTION_START(""); + + if (macro_period_us == 0) + timeout_mclks = 0; + else + timeout_mclks = + ((timeout_us << 12) + (macro_period_us>>1)) / + macro_period_us; + + LOG_FUNCTION_END(0); + + return timeout_mclks; +} + + +uint16_t VL53L1_calc_encoded_timeout( + uint32_t timeout_us, + uint32_t macro_period_us) +{ + + + uint32_t timeout_mclks = 0; + uint16_t timeout_encoded = 0; + + LOG_FUNCTION_START(""); + + timeout_mclks = + VL53L1_calc_timeout_mclks(timeout_us, macro_period_us); + + timeout_encoded = + VL53L1_encode_timeout(timeout_mclks); + + + + LOG_FUNCTION_END(0); + + return timeout_encoded; +} + + +uint32_t VL53L1_calc_timeout_us( + uint32_t timeout_mclks, + uint32_t macro_period_us) +{ + + + uint32_t timeout_us = 0; + uint64_t tmp = 0; + + LOG_FUNCTION_START(""); + + tmp = (uint64_t)timeout_mclks * (uint64_t)macro_period_us; + tmp += 0x00800; + tmp = tmp >> 12; + + timeout_us = (uint32_t)tmp; + + + + LOG_FUNCTION_END(0); + + return timeout_us; +} + +uint32_t VL53L1_calc_crosstalk_plane_offset_with_margin( + uint32_t plane_offset_kcps, + int16_t margin_offset_kcps) +{ + uint32_t plane_offset_with_margin = 0; + int32_t plane_offset_kcps_temp = 0; + + LOG_FUNCTION_START(""); + + plane_offset_kcps_temp = + (int32_t)plane_offset_kcps + + (int32_t)margin_offset_kcps; + + if (plane_offset_kcps_temp < 0) + plane_offset_kcps_temp = 0; + else + if (plane_offset_kcps_temp > 0x3FFFF) + plane_offset_kcps_temp = 0x3FFFF; + + plane_offset_with_margin = (uint32_t) plane_offset_kcps_temp; + + LOG_FUNCTION_END(0); + + return plane_offset_with_margin; + +} + +uint32_t VL53L1_calc_decoded_timeout_us( + uint16_t timeout_encoded, + uint32_t macro_period_us) +{ + + + uint32_t timeout_mclks = 0; + uint32_t timeout_us = 0; + + LOG_FUNCTION_START(""); + + timeout_mclks = + VL53L1_decode_timeout(timeout_encoded); + + timeout_us = + VL53L1_calc_timeout_us(timeout_mclks, macro_period_us); + + LOG_FUNCTION_END(0); + + return timeout_us; +} + + +uint16_t VL53L1_encode_timeout(uint32_t timeout_mclks) +{ + + + uint16_t encoded_timeout = 0; + uint32_t ls_byte = 0; + uint16_t ms_byte = 0; + + if (timeout_mclks > 0) { + ls_byte = timeout_mclks - 1; + + while ((ls_byte & 0xFFFFFF00) > 0) { + ls_byte = ls_byte >> 1; + ms_byte++; + } + + encoded_timeout = (ms_byte << 8) + + (uint16_t) (ls_byte & 0x000000FF); + } + + return encoded_timeout; +} + + +uint32_t VL53L1_decode_timeout(uint16_t encoded_timeout) +{ + + + uint32_t timeout_macro_clks = 0; + + timeout_macro_clks = ((uint32_t) (encoded_timeout & 0x00FF) + << (uint32_t) ((encoded_timeout & 0xFF00) >> 8)) + 1; + + return timeout_macro_clks; +} + + +VL53L1_Error VL53L1_calc_timeout_register_values( + uint32_t phasecal_config_timeout_us, + uint32_t mm_config_timeout_us, + uint32_t range_config_timeout_us, + uint16_t fast_osc_frequency, + VL53L1_general_config_t *pgeneral, + VL53L1_timing_config_t *ptiming) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint32_t macro_period_us = 0; + uint32_t timeout_mclks = 0; + uint16_t timeout_encoded = 0; + + LOG_FUNCTION_START(""); + + if (fast_osc_frequency == 0) { + status = VL53L1_ERROR_DIVISION_BY_ZERO; + } else { + + macro_period_us = + VL53L1_calc_macro_period_us( + fast_osc_frequency, + ptiming->range_config__vcsel_period_a); + + + timeout_mclks = + VL53L1_calc_timeout_mclks( + phasecal_config_timeout_us, + macro_period_us); + + + if (timeout_mclks > 0xFF) + timeout_mclks = 0xFF; + + pgeneral->phasecal_config__timeout_macrop = + (uint8_t)timeout_mclks; + + + timeout_encoded = + VL53L1_calc_encoded_timeout( + mm_config_timeout_us, + macro_period_us); + + ptiming->mm_config__timeout_macrop_a_hi = + (uint8_t)((timeout_encoded & 0xFF00) >> 8); + ptiming->mm_config__timeout_macrop_a_lo = + (uint8_t) (timeout_encoded & 0x00FF); + + + timeout_encoded = + VL53L1_calc_encoded_timeout( + range_config_timeout_us, + macro_period_us); + + ptiming->range_config__timeout_macrop_a_hi = + (uint8_t)((timeout_encoded & 0xFF00) >> 8); + ptiming->range_config__timeout_macrop_a_lo = + (uint8_t) (timeout_encoded & 0x00FF); + + + macro_period_us = + VL53L1_calc_macro_period_us( + fast_osc_frequency, + ptiming->range_config__vcsel_period_b); + + + timeout_encoded = + VL53L1_calc_encoded_timeout( + mm_config_timeout_us, + macro_period_us); + + ptiming->mm_config__timeout_macrop_b_hi = + (uint8_t)((timeout_encoded & 0xFF00) >> 8); + ptiming->mm_config__timeout_macrop_b_lo = + (uint8_t) (timeout_encoded & 0x00FF); + + + timeout_encoded = VL53L1_calc_encoded_timeout( + range_config_timeout_us, + macro_period_us); + + ptiming->range_config__timeout_macrop_b_hi = + (uint8_t)((timeout_encoded & 0xFF00) >> 8); + ptiming->range_config__timeout_macrop_b_lo = + (uint8_t) (timeout_encoded & 0x00FF); + } + + LOG_FUNCTION_END(0); + + return status; + +} + + +uint8_t VL53L1_encode_vcsel_period(uint8_t VL53L1_p_031) +{ + + + uint8_t vcsel_period_reg = 0; + + vcsel_period_reg = (VL53L1_p_031 >> 1) - 1; + + return vcsel_period_reg; +} + + +uint32_t VL53L1_decode_unsigned_integer( + uint8_t *pbuffer, + uint8_t no_of_bytes) +{ + + + uint8_t i = 0; + uint32_t decoded_value = 0; + + for (i = 0; i < no_of_bytes; i++) + decoded_value = (decoded_value << 8) + (uint32_t)pbuffer[i]; + + return decoded_value; +} + + +void VL53L1_encode_unsigned_integer( + uint32_t ip_value, + uint8_t no_of_bytes, + uint8_t *pbuffer) +{ + + + uint8_t i = 0; + uint32_t VL53L1_p_002 = 0; + + VL53L1_p_002 = ip_value; + for (i = 0; i < no_of_bytes; i++) { + pbuffer[no_of_bytes-i-1] = VL53L1_p_002 & 0x00FF; + VL53L1_p_002 = VL53L1_p_002 >> 8; + } +} + + +VL53L1_Error VL53L1_hist_copy_and_scale_ambient_info( + VL53L1_zone_hist_info_t *pidata, + VL53L1_histogram_bin_data_t *podata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + int64_t evts = 0; + int64_t tmpi = 0; + int64_t tmpo = 0; + + LOG_FUNCTION_START(""); + + + if (pidata->result__dss_actual_effective_spads == 0) { + status = VL53L1_ERROR_DIVISION_BY_ZERO; + } else { + if (pidata->number_of_ambient_bins > 0 && + podata->number_of_ambient_bins == 0) { + + + + tmpo = 1 + (int64_t)podata->total_periods_elapsed; + tmpo *= + (int64_t)podata->result__dss_actual_effective_spads; + + tmpi = 1 + (int64_t)pidata->total_periods_elapsed; + tmpi *= + (int64_t)pidata->result__dss_actual_effective_spads; + + evts = tmpo * + (int64_t)pidata->ambient_events_sum; + evts += (tmpi/2); + + + if (tmpi != 0) + evts = do_division_s(evts, tmpi); + + podata->ambient_events_sum = (int32_t)evts; + + + + podata->VL53L1_p_004 = + podata->ambient_events_sum; + podata->VL53L1_p_004 += + ((int32_t)pidata->number_of_ambient_bins / 2); + podata->VL53L1_p_004 /= + (int32_t)pidata->number_of_ambient_bins; + } + } + + LOG_FUNCTION_END(0); + + return status; +} + + +void VL53L1_hist_get_bin_sequence_config( + VL53L1_DEV Dev, + VL53L1_histogram_bin_data_t *pdata) +{ + + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + int32_t amb_thresh_low = 0; + int32_t amb_thresh_high = 0; + + uint8_t i = 0; + + LOG_FUNCTION_START(""); + + + + amb_thresh_low = 1024 * + (int32_t)pdev->hist_cfg.histogram_config__amb_thresh_low; + amb_thresh_high = 1024 * + (int32_t)pdev->hist_cfg.histogram_config__amb_thresh_high; + + + + if ((pdev->ll_state.rd_stream_count & 0x01) == 0) { + + pdata->bin_seq[5] = + pdev->hist_cfg.histogram_config__mid_amb_even_bin_4_5 >> 4; + pdata->bin_seq[4] = + pdev->hist_cfg.histogram_config__mid_amb_even_bin_4_5 & 0x0F; + pdata->bin_seq[3] = + pdev->hist_cfg.histogram_config__mid_amb_even_bin_2_3 >> 4; + pdata->bin_seq[2] = + pdev->hist_cfg.histogram_config__mid_amb_even_bin_2_3 & 0x0F; + pdata->bin_seq[1] = + pdev->hist_cfg.histogram_config__mid_amb_even_bin_0_1 >> 4; + pdata->bin_seq[0] = + pdev->hist_cfg.histogram_config__mid_amb_even_bin_0_1 & 0x0F; + + if (pdata->ambient_events_sum > amb_thresh_high) { + pdata->bin_seq[5] = + pdev->hist_cfg.histogram_config__high_amb_even_bin_4_5 + >> 4; + pdata->bin_seq[4] = + pdev->hist_cfg.histogram_config__high_amb_even_bin_4_5 + & 0x0F; + pdata->bin_seq[3] = + pdev->hist_cfg.histogram_config__high_amb_even_bin_2_3 + >> 4; + pdata->bin_seq[2] = + pdev->hist_cfg.histogram_config__high_amb_even_bin_2_3 + & 0x0F; + pdata->bin_seq[1] = + pdev->hist_cfg.histogram_config__high_amb_even_bin_0_1 + >> 4; + pdata->bin_seq[0] = + pdev->hist_cfg.histogram_config__high_amb_even_bin_0_1 + & 0x0F; + } + + if (pdata->ambient_events_sum < amb_thresh_low) { + pdata->bin_seq[5] = + pdev->hist_cfg.histogram_config__low_amb_even_bin_4_5 + >> 4; + pdata->bin_seq[4] = + pdev->hist_cfg.histogram_config__low_amb_even_bin_4_5 + & 0x0F; + pdata->bin_seq[3] = + pdev->hist_cfg.histogram_config__low_amb_even_bin_2_3 + >> 4; + pdata->bin_seq[2] = + pdev->hist_cfg.histogram_config__low_amb_even_bin_2_3 + & 0x0F; + pdata->bin_seq[1] = + pdev->hist_cfg.histogram_config__low_amb_even_bin_0_1 + >> 4; + pdata->bin_seq[0] = + pdev->hist_cfg.histogram_config__low_amb_even_bin_0_1 + & 0x0F; + } + + } else { + pdata->bin_seq[5] = + pdev->hist_cfg.histogram_config__mid_amb_odd_bin_5 + & 0x0F; + pdata->bin_seq[4] = + pdev->hist_cfg.histogram_config__mid_amb_odd_bin_3_4 + & 0x0F; + pdata->bin_seq[3] = + pdev->hist_cfg.histogram_config__mid_amb_odd_bin_3_4 + >> 4; + pdata->bin_seq[2] = + pdev->hist_cfg.histogram_config__mid_amb_odd_bin_2 & + 0x0F; + pdata->bin_seq[1] = + pdev->hist_cfg.histogram_config__mid_amb_odd_bin_0_1 + >> 4; + pdata->bin_seq[0] = + pdev->hist_cfg.histogram_config__mid_amb_odd_bin_0_1 + & 0x0F; + + if (pdata->ambient_events_sum > amb_thresh_high) { + pdata->bin_seq[5] = + pdev->hist_cfg.histogram_config__high_amb_odd_bin_4_5 + >> 4; + pdata->bin_seq[4] = + pdev->hist_cfg.histogram_config__high_amb_odd_bin_4_5 + & 0x0F; + pdata->bin_seq[3] = + pdev->hist_cfg.histogram_config__high_amb_odd_bin_2_3 + >> 4; + pdata->bin_seq[2] = + pdev->hist_cfg.histogram_config__high_amb_odd_bin_2_3 + & 0x0F; + pdata->bin_seq[1] = + pdev->hist_cfg.histogram_config__high_amb_odd_bin_0_1 + >> 4; + pdata->bin_seq[0] = + pdev->hist_cfg.histogram_config__high_amb_odd_bin_0_1 + & 0x0F; + } + + if (pdata->ambient_events_sum < amb_thresh_low) { + pdata->bin_seq[5] = + pdev->hist_cfg.histogram_config__low_amb_odd_bin_4_5 + >> 4; + pdata->bin_seq[4] = + pdev->hist_cfg.histogram_config__low_amb_odd_bin_4_5 + & 0x0F; + pdata->bin_seq[3] = + pdev->hist_cfg.histogram_config__low_amb_odd_bin_2_3 + >> 4; + pdata->bin_seq[2] = + pdev->hist_cfg.histogram_config__low_amb_odd_bin_2_3 + & 0x0F; + pdata->bin_seq[1] = + pdev->hist_cfg.histogram_config__low_amb_odd_bin_0_1 + >> 4; + pdata->bin_seq[0] = + pdev->hist_cfg.histogram_config__low_amb_odd_bin_0_1 + & 0x0F; + } + } + + + + for (i = 0; i < VL53L1_MAX_BIN_SEQUENCE_LENGTH; i++) + pdata->bin_rep[i] = 1; + + LOG_FUNCTION_END(0); + +} + + +VL53L1_Error VL53L1_hist_phase_consistency_check( + VL53L1_DEV Dev, + VL53L1_zone_hist_info_t *phist_prev, + VL53L1_zone_objects_t *prange_prev, + VL53L1_range_results_t *prange_curr) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t lc = 0; + uint8_t p = 0; + + uint16_t phase_delta = 0; + uint16_t phase_tolerance = 0; + + int32_t events_delta = 0; + int32_t events_tolerance = 0; + + + uint8_t event_sigma; + uint16_t event_min_spad_count; + uint16_t min_max_tolerance; + uint8_t pht; + + VL53L1_DeviceError range_status = 0; + + LOG_FUNCTION_START(""); + + event_sigma = + pdev->histpostprocess.algo__consistency_check__event_sigma; + event_min_spad_count = + pdev->histpostprocess.algo__consistency_check__event_min_spad_count; + min_max_tolerance = + pdev->histpostprocess.algo__consistency_check__min_max_tolerance; + + + pht = pdev->histpostprocess.algo__consistency_check__phase_tolerance; + phase_tolerance = (uint16_t)pht; + phase_tolerance = phase_tolerance << 8; + + + + if (prange_prev->rd_device_state != + VL53L1_DEVICESTATE_RANGING_GATHER_DATA && + prange_prev->rd_device_state != + VL53L1_DEVICESTATE_RANGING_OUTPUT_DATA) + return status; + + + + if (phase_tolerance == 0) + return status; + + for (lc = 0; lc < prange_curr->active_results; lc++) { + + if (!((prange_curr->VL53L1_p_002[lc].range_status == + VL53L1_DEVICEERROR_RANGECOMPLETE) || + (prange_curr->VL53L1_p_002[lc].range_status == + VL53L1_DEVICEERROR_RANGECOMPLETE_NO_WRAP_CHECK))) + continue; + + + + + + + if (prange_prev->active_objects == 0) + prange_curr->VL53L1_p_002[lc].range_status = + VL53L1_DEVICEERROR_PREV_RANGE_NO_TARGETS; + else + prange_curr->VL53L1_p_002[lc].range_status = + VL53L1_DEVICEERROR_PHASECONSISTENCY; + + + + + + for (p = 0; p < prange_prev->active_objects; p++) { + + if (prange_curr->VL53L1_p_002[lc].VL53L1_p_014 > + prange_prev->VL53L1_p_002[p].VL53L1_p_014) { + phase_delta = + prange_curr->VL53L1_p_002[lc].VL53L1_p_014 - + prange_prev->VL53L1_p_002[p].VL53L1_p_014; + } else { + phase_delta = + prange_prev->VL53L1_p_002[p].VL53L1_p_014 - + prange_curr->VL53L1_p_002[lc].VL53L1_p_014; + } + + if (phase_delta < phase_tolerance) { + + + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_hist_events_consistency_check( + event_sigma, + event_min_spad_count, + phist_prev, + &(prange_prev->VL53L1_p_002[p]), + &(prange_curr->VL53L1_p_002[lc]), + &events_tolerance, + &events_delta, + &range_status); + + + + + if (status == VL53L1_ERROR_NONE && + range_status == + VL53L1_DEVICEERROR_RANGECOMPLETE) + status = + VL53L1_hist_merged_pulse_check( + min_max_tolerance, + &(prange_curr->VL53L1_p_002[lc]), + &range_status); + + prange_curr->VL53L1_p_002[lc].range_status = + range_status; + } + } + + } + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_hist_events_consistency_check( + uint8_t event_sigma, + uint16_t min_effective_spad_count, + VL53L1_zone_hist_info_t *phist_prev, + VL53L1_object_data_t *prange_prev, + VL53L1_range_data_t *prange_curr, + int32_t *pevents_tolerance, + int32_t *pevents_delta, + VL53L1_DeviceError *prange_status) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + int64_t tmpp = 0; + int64_t tmpc = 0; + int64_t events_scaler = 0; + int64_t events_scaler_sq = 0; + int64_t c_signal_events = 0; + int64_t c_sig_noise_sq = 0; + int64_t c_amb_noise_sq = 0; + int64_t p_amb_noise_sq = 0; + + int32_t p_signal_events = 0; + uint32_t noise_sq_sum = 0; + + + + if (event_sigma == 0) { + *prange_status = VL53L1_DEVICEERROR_RANGECOMPLETE; + return status; + } + + + + tmpp = 1 + (int64_t)phist_prev->total_periods_elapsed; + tmpp *= (int64_t)phist_prev->result__dss_actual_effective_spads; + + + + tmpc = 1 + (int64_t)prange_curr->total_periods_elapsed; + tmpc *= (int64_t)prange_curr->VL53L1_p_006; + + + + events_scaler = tmpp * 4096; + events_scaler += (tmpc/2); + if (tmpc != 0) + events_scaler = do_division_s(events_scaler, tmpc); + + events_scaler_sq = events_scaler * events_scaler; + events_scaler_sq += 2048; + events_scaler_sq /= 4096; + + + + c_signal_events = (int64_t)prange_curr->VL53L1_p_021; + c_signal_events -= (int64_t)prange_curr->VL53L1_p_020; + c_signal_events *= (int64_t)events_scaler; + c_signal_events += 2048; + c_signal_events /= 4096; + + c_sig_noise_sq = (int64_t)events_scaler_sq; + c_sig_noise_sq *= (int64_t)prange_curr->VL53L1_p_021; + c_sig_noise_sq += 2048; + c_sig_noise_sq /= 4096; + + c_amb_noise_sq = (int64_t)events_scaler_sq; + c_amb_noise_sq *= (int64_t)prange_curr->VL53L1_p_020; + c_amb_noise_sq += 2048; + c_amb_noise_sq /= 4096; + + + c_amb_noise_sq += 2; + c_amb_noise_sq /= 4; + + + + p_amb_noise_sq = + (int64_t)prange_prev->VL53L1_p_020; + + + p_amb_noise_sq += 2; + p_amb_noise_sq /= 4; + + noise_sq_sum = + (uint32_t)prange_prev->VL53L1_p_021 + + (uint32_t)c_sig_noise_sq + + (uint32_t)p_amb_noise_sq + + (uint32_t)c_amb_noise_sq; + + *pevents_tolerance = + (int32_t)VL53L1_isqrt(noise_sq_sum * 16); + + *pevents_tolerance *= (int32_t)event_sigma; + *pevents_tolerance += 32; + *pevents_tolerance /= 64; + + p_signal_events = (int32_t)prange_prev->VL53L1_p_021; + p_signal_events -= (int32_t)prange_prev->VL53L1_p_020; + + if ((int32_t)c_signal_events > p_signal_events) + *pevents_delta = + (int32_t)c_signal_events - p_signal_events; + else + *pevents_delta = + p_signal_events - (int32_t)c_signal_events; + + if (*pevents_delta > *pevents_tolerance && + prange_curr->VL53L1_p_006 > min_effective_spad_count) + *prange_status = VL53L1_DEVICEERROR_EVENTCONSISTENCY; + else + *prange_status = VL53L1_DEVICEERROR_RANGECOMPLETE; + + + + + + return status; +} + + + + +VL53L1_Error VL53L1_hist_merged_pulse_check( + int16_t min_max_tolerance_mm, + VL53L1_range_data_t *pdata, + VL53L1_DeviceError *prange_status) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + int16_t delta_mm = 0; + + if (pdata->max_range_mm > pdata->min_range_mm) + delta_mm = + pdata->max_range_mm - pdata->min_range_mm; + else + delta_mm = + pdata->min_range_mm - pdata->max_range_mm; + + if (min_max_tolerance_mm > 0 && + delta_mm > min_max_tolerance_mm) + *prange_status = VL53L1_DEVICEERROR_RANGECOMPLETE_MERGED_PULSE; + else + *prange_status = VL53L1_DEVICEERROR_RANGECOMPLETE; + + return status; +} + + + + +VL53L1_Error VL53L1_hist_xmonitor_consistency_check( + VL53L1_DEV Dev, + VL53L1_zone_hist_info_t *phist_prev, + VL53L1_zone_objects_t *prange_prev, + VL53L1_range_data_t *prange_curr) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + int32_t events_delta = 0; + int32_t events_tolerance = 0; + uint8_t event_sigma; + uint16_t min_spad_count; + + event_sigma = pdev->histpostprocess.algo__crosstalk_detect_event_sigma; + min_spad_count = + pdev->histpostprocess.algo__consistency_check__event_min_spad_count; + + if (prange_curr->range_status == VL53L1_DEVICEERROR_RANGECOMPLETE || + prange_curr->range_status == + VL53L1_DEVICEERROR_RANGECOMPLETE_NO_WRAP_CHECK || + prange_curr->range_status == + VL53L1_DEVICEERROR_EVENTCONSISTENCY) { + + if (prange_prev->xmonitor.range_status == + VL53L1_DEVICEERROR_RANGECOMPLETE || + prange_prev->xmonitor.range_status == + VL53L1_DEVICEERROR_RANGECOMPLETE_NO_WRAP_CHECK || + prange_prev->xmonitor.range_status == + VL53L1_DEVICEERROR_EVENTCONSISTENCY) { + + prange_curr->range_status = + VL53L1_DEVICEERROR_RANGECOMPLETE; + + status = + VL53L1_hist_events_consistency_check( + event_sigma, + min_spad_count, + phist_prev, + &(prange_prev->xmonitor), + prange_curr, + &events_tolerance, + &events_delta, + &(prange_curr->range_status)); + + } + } + + return status; +} + + + + +VL53L1_Error VL53L1_hist_wrap_dmax( + VL53L1_hist_post_process_config_t *phistpostprocess, + VL53L1_histogram_bin_data_t *pcurrent, + int16_t *pwrap_dmax_mm) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint32_t pll_period_mm = 0; + uint32_t wrap_dmax_phase = 0; + uint32_t range_mm = 0; + + LOG_FUNCTION_START(""); + + *pwrap_dmax_mm = 0; + + + if (pcurrent->VL53L1_p_019 != 0) { + + + + pll_period_mm = + VL53L1_calc_pll_period_mm( + pcurrent->VL53L1_p_019); + + + + wrap_dmax_phase = + (uint32_t)phistpostprocess->valid_phase_high << 8; + + + + range_mm = wrap_dmax_phase * pll_period_mm; + range_mm = (range_mm + (1<<14)) >> 15; + + *pwrap_dmax_mm = (int16_t)range_mm; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +void VL53L1_hist_combine_mm1_mm2_offsets( + int16_t mm1_offset_mm, + int16_t mm2_offset_mm, + uint8_t encoded_mm_roi_centre, + uint8_t encoded_mm_roi_size, + uint8_t encoded_zone_centre, + uint8_t encoded_zone_size, + VL53L1_additional_offset_cal_data_t *pcal_data, + uint8_t *pgood_spads, + uint16_t aperture_attenuation, + int16_t *prange_offset_mm) +{ + + + + uint16_t max_mm_inner_effective_spads = 0; + uint16_t max_mm_outer_effective_spads = 0; + uint16_t mm_inner_effective_spads = 0; + uint16_t mm_outer_effective_spads = 0; + + uint32_t scaled_mm1_peak_rate_mcps = 0; + uint32_t scaled_mm2_peak_rate_mcps = 0; + + int32_t tmp0 = 0; + int32_t tmp1 = 0; + + + + VL53L1_calc_mm_effective_spads( + encoded_mm_roi_centre, + encoded_mm_roi_size, + 0xC7, + 0xFF, + pgood_spads, + aperture_attenuation, + &max_mm_inner_effective_spads, + &max_mm_outer_effective_spads); + + if ((max_mm_inner_effective_spads == 0) || + (max_mm_outer_effective_spads == 0)) + goto FAIL; + + + + VL53L1_calc_mm_effective_spads( + encoded_mm_roi_centre, + encoded_mm_roi_size, + encoded_zone_centre, + encoded_zone_size, + pgood_spads, + aperture_attenuation, + &mm_inner_effective_spads, + &mm_outer_effective_spads); + + + + scaled_mm1_peak_rate_mcps = + (uint32_t)pcal_data->result__mm_inner_peak_signal_count_rtn_mcps; + scaled_mm1_peak_rate_mcps *= (uint32_t)mm_inner_effective_spads; + scaled_mm1_peak_rate_mcps /= (uint32_t)max_mm_inner_effective_spads; + + scaled_mm2_peak_rate_mcps = + (uint32_t)pcal_data->result__mm_outer_peak_signal_count_rtn_mcps; + scaled_mm2_peak_rate_mcps *= (uint32_t)mm_outer_effective_spads; + scaled_mm2_peak_rate_mcps /= (uint32_t)max_mm_outer_effective_spads; + + + + tmp0 = ((int32_t)mm1_offset_mm * (int32_t)scaled_mm1_peak_rate_mcps); + tmp0 += ((int32_t)mm2_offset_mm * (int32_t)scaled_mm2_peak_rate_mcps); + + tmp1 = (int32_t)scaled_mm1_peak_rate_mcps + + (int32_t)scaled_mm2_peak_rate_mcps; + + + + if (tmp1 != 0) + tmp0 = (tmp0 * 4) / tmp1; +FAIL: + *prange_offset_mm = (int16_t)tmp0; + +} + + +VL53L1_Error VL53L1_hist_xtalk_extract_calc_window( + int16_t target_distance_mm, + uint16_t target_width_oversize, + VL53L1_histogram_bin_data_t *phist_bins, + VL53L1_hist_xtalk_extract_data_t *pxtalk_data) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + pxtalk_data->pll_period_mm = + VL53L1_calc_pll_period_mm(phist_bins->VL53L1_p_019); + + + pxtalk_data->xtalk_width_phase = + (int32_t)phist_bins->vcsel_width * 128; + pxtalk_data->target_width_phase = + pxtalk_data->xtalk_width_phase + + (int32_t)target_width_oversize * 128; + + + + pxtalk_data->xtalk_start_phase = + (int32_t)phist_bins->zero_distance_phase - + (pxtalk_data->xtalk_width_phase / 2); + pxtalk_data->xtalk_end_phase = + (int32_t)pxtalk_data->xtalk_start_phase + + pxtalk_data->xtalk_width_phase; + + if (pxtalk_data->xtalk_start_phase < 0) + pxtalk_data->xtalk_start_phase = 0; + + + + + pxtalk_data->VL53L1_p_015 = + (uint8_t)(pxtalk_data->xtalk_start_phase / 2048); + + + pxtalk_data->VL53L1_p_016 = + (uint8_t)((pxtalk_data->xtalk_end_phase + 2047) / 2048); + + + + pxtalk_data->target_start_phase = + (int32_t)target_distance_mm * 2048 * 16; + pxtalk_data->target_start_phase += + ((int32_t)pxtalk_data->pll_period_mm / 2); + pxtalk_data->target_start_phase /= (int32_t)pxtalk_data->pll_period_mm; + pxtalk_data->target_start_phase += + (int32_t)phist_bins->zero_distance_phase; + + + + pxtalk_data->target_start_phase -= + (pxtalk_data->target_width_phase / 2); + pxtalk_data->target_end_phase = + (int32_t)pxtalk_data->target_start_phase + + pxtalk_data->target_width_phase; + + if (pxtalk_data->target_start_phase < 0) + pxtalk_data->target_start_phase = 0; + + + pxtalk_data->target_start = + (uint8_t)(pxtalk_data->target_start_phase / 2048); + + + if (pxtalk_data->VL53L1_p_016 > (pxtalk_data->target_start-1)) + pxtalk_data->VL53L1_p_016 = pxtalk_data->target_start-1; + + + pxtalk_data->effective_width = + (2048 * ((int32_t)pxtalk_data->VL53L1_p_016+1)); + pxtalk_data->effective_width -= pxtalk_data->xtalk_start_phase; + + + if (pxtalk_data->effective_width > pxtalk_data->xtalk_width_phase) + pxtalk_data->effective_width = pxtalk_data->xtalk_width_phase; + + if (pxtalk_data->effective_width < 1) + pxtalk_data->effective_width = 1; + + + pxtalk_data->event_scaler = pxtalk_data->xtalk_width_phase * 1000; + pxtalk_data->event_scaler += (pxtalk_data->effective_width / 2); + pxtalk_data->event_scaler /= pxtalk_data->effective_width; + + + if (pxtalk_data->event_scaler < 1000) + pxtalk_data->event_scaler = 1000; + + if (pxtalk_data->event_scaler > 4000) + pxtalk_data->event_scaler = 4000; + + + pxtalk_data->event_scaler_sum += pxtalk_data->event_scaler; + + + pxtalk_data->peak_duration_us_sum += + (uint32_t)phist_bins->peak_duration_us; + + + pxtalk_data->effective_spad_count_sum += + (uint32_t)phist_bins->result__dss_actual_effective_spads; + + + pxtalk_data->zero_distance_phase_sum += + (uint32_t)phist_bins->zero_distance_phase; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_hist_xtalk_extract_calc_event_sums( + VL53L1_histogram_bin_data_t *phist_bins, + VL53L1_hist_xtalk_extract_data_t *pxtalk_data) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t lb = 0; + uint8_t i = 0; + + LOG_FUNCTION_START(""); + + + + for (lb = pxtalk_data->VL53L1_p_015; + lb <= pxtalk_data->VL53L1_p_016; + lb++) { + + + i = (lb + phist_bins->number_of_ambient_bins + + phist_bins->VL53L1_p_024) % + phist_bins->VL53L1_p_024; + + + pxtalk_data->signal_events_sum += phist_bins->bin_data[i]; + pxtalk_data->signal_events_sum -= + phist_bins->VL53L1_p_004; + } + + + + for (lb = 0; lb < VL53L1_XTALK_HISTO_BINS && + lb < phist_bins->VL53L1_p_024; lb++) { + + + i = (lb + phist_bins->number_of_ambient_bins + + phist_bins->VL53L1_p_024) % + phist_bins->VL53L1_p_024; + + + pxtalk_data->bin_data_sums[lb] += phist_bins->bin_data[i]; + pxtalk_data->bin_data_sums[lb] -= + phist_bins->VL53L1_p_004; + } + + pxtalk_data->sample_count += 1; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_hist_xtalk_extract_calc_rate_per_spad( + VL53L1_hist_xtalk_extract_data_t *pxtalk_data) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint64_t tmp64_0 = 0; + uint64_t tmp64_1 = 0; + uint64_t xtalk_per_spad = 0; + + LOG_FUNCTION_START(""); + + + + + + + + + tmp64_1 = + (uint64_t)pxtalk_data->effective_spad_count_sum * + (uint64_t)pxtalk_data->peak_duration_us_sum; + + if (pxtalk_data->signal_events_sum < 0) { + pxtalk_data->signal_events_sum = 0; + + tmp64_0 = + ((uint64_t)pxtalk_data->sample_count * + (uint64_t)pxtalk_data->event_scaler_avg * 256U) << 9U; + if (tmp64_0 > 0) { + pxtalk_data->signal_events_sum = (int32_t) + do_division_u((50U * tmp64_1), tmp64_0); + } + } + tmp64_0 = + ((uint64_t)pxtalk_data->signal_events_sum * + (uint64_t)pxtalk_data->sample_count * + (uint64_t)pxtalk_data->event_scaler_avg * 256U) << 9U; + + + + if (tmp64_1 > 0U) { + + tmp64_0 = tmp64_0 + (tmp64_1 >> 1U); + xtalk_per_spad = do_division_u(tmp64_0, tmp64_1); + } else { + xtalk_per_spad = (uint64_t)tmp64_0; + } + + pxtalk_data->xtalk_rate_kcps_per_spad = (uint32_t)xtalk_per_spad; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_hist_xtalk_extract_calc_shape( + VL53L1_hist_xtalk_extract_data_t *pxtalk_data, + VL53L1_xtalk_histogram_shape_t *pxtalk_shape) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + int32_t lb = 0; + uint64_t total_events = 0U; + uint64_t tmp64_0 = 0U; + int32_t remaining_area = 1024; + + LOG_FUNCTION_START(""); + + + + pxtalk_shape->VL53L1_p_022 = 0; + pxtalk_shape->VL53L1_p_023 = VL53L1_XTALK_HISTO_BINS; + pxtalk_shape->VL53L1_p_024 = VL53L1_XTALK_HISTO_BINS; + + pxtalk_shape->zero_distance_phase = + (uint16_t)pxtalk_data->zero_distance_phase_avg; + pxtalk_shape->phasecal_result__reference_phase = + (uint16_t)pxtalk_data->zero_distance_phase_avg + (3*2048); + + + + if (pxtalk_data->signal_events_sum > 0) + total_events = + (uint64_t)pxtalk_data->signal_events_sum * + (uint64_t)pxtalk_data->event_scaler_avg; + else + total_events = 1; + if (total_events == 0) + total_events = 1; + + + remaining_area = 1024; + pxtalk_data->max_shape_value = 0; + + for (lb = 0; lb < VL53L1_XTALK_HISTO_BINS; lb++) { + + if ((lb < (int32_t)pxtalk_data->VL53L1_p_015 || + lb > (int32_t)pxtalk_data->VL53L1_p_016) || + pxtalk_data->bin_data_sums[lb] < 0) { + + + if (remaining_area > 0 && remaining_area < 1024) { + if (remaining_area > + pxtalk_data->max_shape_value) { + pxtalk_shape->bin_data[lb] = + (uint32_t)pxtalk_data->max_shape_value; + remaining_area -= + pxtalk_data->max_shape_value; + } else { + pxtalk_shape->bin_data[lb] = + (uint32_t)remaining_area; + remaining_area = 0; + } + } else { + pxtalk_shape->bin_data[lb] = 0; + } + + } else { + + tmp64_0 = + (uint64_t)pxtalk_data->bin_data_sums[lb] + * 1024U * 1000U; + tmp64_0 += (total_events >> 1); + tmp64_0 = do_division_u(tmp64_0, total_events); + if (tmp64_0 > 0xFFFFU) + tmp64_0 = 0xFFFFU; + + pxtalk_shape->bin_data[lb] = (uint32_t)tmp64_0; + + + if ((int32_t)pxtalk_shape->bin_data[lb] > + pxtalk_data->max_shape_value) + pxtalk_data->max_shape_value = + (int32_t)pxtalk_shape->bin_data[lb]; + + remaining_area -= (int32_t)pxtalk_shape->bin_data[lb]; + } + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_hist_xtalk_shape_model( + uint16_t events_per_bin, + uint16_t pulse_centre, + uint16_t pulse_width, + VL53L1_xtalk_histogram_shape_t *pxtalk_shape) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint32_t phase_start = 0; + uint32_t phase_stop = 0; + uint32_t phase_bin = 0; + + uint32_t bin_start = 0; + uint32_t bin_stop = 0; + + uint32_t lb = 0; + uint16_t VL53L1_p_008 = 0; + + LOG_FUNCTION_START(""); + + + + pxtalk_shape->VL53L1_p_022 = 0; + pxtalk_shape->VL53L1_p_023 = VL53L1_XTALK_HISTO_BINS; + pxtalk_shape->VL53L1_p_024 = VL53L1_XTALK_HISTO_BINS; + + pxtalk_shape->zero_distance_phase = pulse_centre; + pxtalk_shape->phasecal_result__reference_phase = + pulse_centre + (3*2048); + + + if (pulse_centre > (pulse_width >> 1)) + phase_start = (uint32_t)pulse_centre - + ((uint32_t)pulse_width >> 1); + else + phase_start = 0; + + phase_stop = (uint32_t)pulse_centre + + ((uint32_t)pulse_width >> 1); + + + bin_start = (phase_start / 2048); + bin_stop = (phase_stop / 2048); + + for (lb = 0; lb < VL53L1_XTALK_HISTO_BINS; lb++) { + VL53L1_p_008 = 0; + + + if (lb == bin_start && lb == bin_stop) { + VL53L1_p_008 = + VL53L1_hist_xtalk_shape_model_interp( + events_per_bin, + phase_stop - phase_start); + + } else if (lb > bin_start && lb < bin_stop) { + + + VL53L1_p_008 = events_per_bin; + + } else if (lb == bin_start) { + + + phase_bin = (lb + 1) * 2048; + VL53L1_p_008 = + VL53L1_hist_xtalk_shape_model_interp( + events_per_bin, + (phase_bin - phase_start)); + + } else if (lb == bin_stop) { + + + phase_bin = lb * 2048; + VL53L1_p_008 = + VL53L1_hist_xtalk_shape_model_interp( + events_per_bin, + (phase_stop - phase_bin)); + } + + pxtalk_shape->bin_data[lb] = VL53L1_p_008; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +uint16_t VL53L1_hist_xtalk_shape_model_interp( + uint16_t events_per_bin, + uint32_t phase_delta) +{ + + + uint32_t VL53L1_p_008 = 0; + + LOG_FUNCTION_START(""); + + + VL53L1_p_008 = (uint32_t)events_per_bin * phase_delta; + VL53L1_p_008 += 1024; + VL53L1_p_008 /= 2048; + + + if (VL53L1_p_008 > 0xFFFFU) + VL53L1_p_008 = 0xFFFFU; + + LOG_FUNCTION_END(0); + + return (uint16_t)VL53L1_p_008; +} + + +void VL53L1_spad_number_to_byte_bit_index( + uint8_t spad_number, + uint8_t *pbyte_index, + uint8_t *pbit_index, + uint8_t *pbit_mask) +{ + + + + *pbyte_index = spad_number >> 3; + *pbit_index = spad_number & 0x07; + *pbit_mask = 0x01 << *pbit_index; + +} + + +void VL53L1_encode_row_col( + uint8_t row, + uint8_t col, + uint8_t *pspad_number) +{ + + + if (row > 7) + *pspad_number = 128 + (col << 3) + (15-row); + else + *pspad_number = ((15-col) << 3) + row; + +} + + +void VL53L1_decode_zone_size( + uint8_t encoded_xy_size, + uint8_t *pwidth, + uint8_t *pheight) +{ + + + + *pheight = encoded_xy_size >> 4; + *pwidth = encoded_xy_size & 0x0F; + +} + + +void VL53L1_encode_zone_size( + uint8_t width, + uint8_t height, + uint8_t *pencoded_xy_size) +{ + + + *pencoded_xy_size = (height << 4) + width; + +} + + +void VL53L1_decode_zone_limits( + uint8_t encoded_xy_centre, + uint8_t encoded_xy_size, + int16_t *px_ll, + int16_t *py_ll, + int16_t *px_ur, + int16_t *py_ur) +{ + + + + uint8_t x_centre = 0; + uint8_t y_centre = 0; + uint8_t width = 0; + uint8_t height = 0; + + + + VL53L1_decode_row_col( + encoded_xy_centre, + &y_centre, + &x_centre); + + VL53L1_decode_zone_size( + encoded_xy_size, + &width, + &height); + + + + *px_ll = (int16_t)x_centre - ((int16_t)width + 1) / 2; + if (*px_ll < 0) + *px_ll = 0; + + *px_ur = *px_ll + (int16_t)width; + if (*px_ur > (VL53L1_SPAD_ARRAY_WIDTH-1)) + *px_ur = VL53L1_SPAD_ARRAY_WIDTH-1; + + *py_ll = (int16_t)y_centre - ((int16_t)height + 1) / 2; + if (*py_ll < 0) + *py_ll = 0; + + *py_ur = *py_ll + (int16_t)height; + if (*py_ur > (VL53L1_SPAD_ARRAY_HEIGHT-1)) + *py_ur = VL53L1_SPAD_ARRAY_HEIGHT-1; +} + + +uint8_t VL53L1_is_aperture_location( + uint8_t row, + uint8_t col) +{ + + + uint8_t is_aperture = 0; + uint8_t mod_row = row % 4; + uint8_t mod_col = col % 4; + + if (mod_row == 0 && mod_col == 2) + is_aperture = 1; + + if (mod_row == 2 && mod_col == 0) + is_aperture = 1; + + return is_aperture; +} + + +void VL53L1_calc_max_effective_spads( + uint8_t encoded_zone_centre, + uint8_t encoded_zone_size, + uint8_t *pgood_spads, + uint16_t aperture_attenuation, + uint16_t *pmax_effective_spads) +{ + + + + int16_t x = 0; + int16_t y = 0; + + int16_t zone_x_ll = 0; + int16_t zone_y_ll = 0; + int16_t zone_x_ur = 0; + int16_t zone_y_ur = 0; + + uint8_t spad_number = 0; + uint8_t byte_index = 0; + uint8_t bit_index = 0; + uint8_t bit_mask = 0; + + uint8_t is_aperture = 0; + + + + VL53L1_decode_zone_limits( + encoded_zone_centre, + encoded_zone_size, + &zone_x_ll, + &zone_y_ll, + &zone_x_ur, + &zone_y_ur); + + + + *pmax_effective_spads = 0; + + for (y = zone_y_ll; y <= zone_y_ur; y++) { + for (x = zone_x_ll; x <= zone_x_ur; x++) { + + + + VL53L1_encode_row_col( + (uint8_t)y, + (uint8_t)x, + &spad_number); + + + + VL53L1_spad_number_to_byte_bit_index( + spad_number, + &byte_index, + &bit_index, + &bit_mask); + + + + if ((pgood_spads[byte_index] & bit_mask) > 0) { + + + is_aperture = VL53L1_is_aperture_location( + (uint8_t)y, + (uint8_t)x); + + if (is_aperture > 0) + *pmax_effective_spads += + aperture_attenuation; + else + *pmax_effective_spads += 0x0100; + + } + } + } +} + + +void VL53L1_calc_mm_effective_spads( + uint8_t encoded_mm_roi_centre, + uint8_t encoded_mm_roi_size, + uint8_t encoded_zone_centre, + uint8_t encoded_zone_size, + uint8_t *pgood_spads, + uint16_t aperture_attenuation, + uint16_t *pmm_inner_effective_spads, + uint16_t *pmm_outer_effective_spads) +{ + + + + int16_t x = 0; + int16_t y = 0; + + int16_t mm_x_ll = 0; + int16_t mm_y_ll = 0; + int16_t mm_x_ur = 0; + int16_t mm_y_ur = 0; + + int16_t zone_x_ll = 0; + int16_t zone_y_ll = 0; + int16_t zone_x_ur = 0; + int16_t zone_y_ur = 0; + + uint8_t spad_number = 0; + uint8_t byte_index = 0; + uint8_t bit_index = 0; + uint8_t bit_mask = 0; + + uint8_t is_aperture = 0; + uint16_t spad_attenuation = 0; + + + + VL53L1_decode_zone_limits( + encoded_mm_roi_centre, + encoded_mm_roi_size, + &mm_x_ll, + &mm_y_ll, + &mm_x_ur, + &mm_y_ur); + + VL53L1_decode_zone_limits( + encoded_zone_centre, + encoded_zone_size, + &zone_x_ll, + &zone_y_ll, + &zone_x_ur, + &zone_y_ur); + + + + *pmm_inner_effective_spads = 0; + *pmm_outer_effective_spads = 0; + + for (y = zone_y_ll; y <= zone_y_ur; y++) { + for (x = zone_x_ll; x <= zone_x_ur; x++) { + + + + VL53L1_encode_row_col( + (uint8_t)y, + (uint8_t)x, + &spad_number); + + + + VL53L1_spad_number_to_byte_bit_index( + spad_number, + &byte_index, + &bit_index, + &bit_mask); + + + + if ((pgood_spads[byte_index] & bit_mask) > 0) { + + + is_aperture = VL53L1_is_aperture_location( + (uint8_t)y, + (uint8_t)x); + + if (is_aperture > 0) + spad_attenuation = aperture_attenuation; + else + spad_attenuation = 0x0100; + + + + if (x >= mm_x_ll && x <= mm_x_ur && + y >= mm_y_ll && y <= mm_y_ur) + *pmm_inner_effective_spads += + spad_attenuation; + else + *pmm_outer_effective_spads += + spad_attenuation; + } + } + } +} + + +void VL53L1_hist_copy_results_to_sys_and_core( + VL53L1_histogram_bin_data_t *pbins, + VL53L1_range_results_t *phist, + VL53L1_system_results_t *psys, + VL53L1_core_results_t *pcore) +{ + + + uint8_t i = 0; + + VL53L1_range_data_t *pdata; + + LOG_FUNCTION_START(""); + + + + VL53L1_init_system_results(psys); + + + + psys->result__interrupt_status = pbins->result__interrupt_status; + psys->result__range_status = phist->active_results; + psys->result__report_status = pbins->result__report_status; + psys->result__stream_count = pbins->result__stream_count; + + pdata = &(phist->VL53L1_p_002[0]); + + for (i = 0; i < phist->active_results; i++) { + + switch (i) { + case 0: + psys->result__dss_actual_effective_spads_sd0 = + pdata->VL53L1_p_006; + psys->result__peak_signal_count_rate_mcps_sd0 = + pdata->peak_signal_count_rate_mcps; + psys->result__avg_signal_count_rate_mcps_sd0 = + pdata->avg_signal_count_rate_mcps; + psys->result__ambient_count_rate_mcps_sd0 = + pdata->ambient_count_rate_mcps; + + psys->result__sigma_sd0 = pdata->VL53L1_p_005; + psys->result__phase_sd0 = pdata->VL53L1_p_014; + + psys->result__final_crosstalk_corrected_range_mm_sd0 = + (uint16_t)pdata->median_range_mm; + + psys->result__phase_sd1 = pdata->zero_distance_phase; + + pcore->result_core__ranging_total_events_sd0 = + pdata->VL53L1_p_021; + pcore->result_core__signal_total_events_sd0 = + pdata->VL53L1_p_013; + pcore->result_core__total_periods_elapsed_sd0 = + pdata->total_periods_elapsed; + pcore->result_core__ambient_window_events_sd0 = + pdata->VL53L1_p_020; + + break; + case 1: + psys->result__dss_actual_effective_spads_sd1 = + pdata->VL53L1_p_006; + psys->result__peak_signal_count_rate_mcps_sd1 = + pdata->peak_signal_count_rate_mcps; + psys->result__ambient_count_rate_mcps_sd1 = + pdata->ambient_count_rate_mcps; + + psys->result__sigma_sd1 = pdata->VL53L1_p_005; + psys->result__phase_sd1 = pdata->VL53L1_p_014; + + psys->result__final_crosstalk_corrected_range_mm_sd1 = + (uint16_t)pdata->median_range_mm; + + pcore->result_core__ranging_total_events_sd1 = + pdata->VL53L1_p_021; + pcore->result_core__signal_total_events_sd1 = + pdata->VL53L1_p_013; + pcore->result_core__total_periods_elapsed_sd1 = + pdata->total_periods_elapsed; + pcore->result_core__ambient_window_events_sd1 = + pdata->VL53L1_p_020; + break; + } + + pdata++; + } + + LOG_FUNCTION_END(0); + +} + + +VL53L1_Error VL53L1_sum_histogram_data( + VL53L1_histogram_bin_data_t *phist_input, + VL53L1_histogram_bin_data_t *phist_output) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i = 0; + uint8_t smallest_bin_num = 0; + + LOG_FUNCTION_START(""); + + + + if (status == VL53L1_ERROR_NONE) { + if (phist_output->VL53L1_p_024 >= + phist_input->VL53L1_p_024) + smallest_bin_num = phist_input->VL53L1_p_024; + else + smallest_bin_num = phist_output->VL53L1_p_024; + } + + + + + + if (status == VL53L1_ERROR_NONE) + for (i = 0; i < smallest_bin_num; i++) + + phist_output->bin_data[i] += phist_input->bin_data[i]; + + if (status == VL53L1_ERROR_NONE) + phist_output->VL53L1_p_004 += + phist_input->VL53L1_p_004; + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_avg_histogram_data( + uint8_t no_of_samples, + VL53L1_histogram_bin_data_t *phist_sum, + VL53L1_histogram_bin_data_t *phist_avg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i = 0; + + LOG_FUNCTION_START(""); + + + + + + if (status == VL53L1_ERROR_NONE) { + for (i = 0; i < phist_sum->VL53L1_p_024; i++) { + + + + if (no_of_samples > 0) + phist_avg->bin_data[i] = + phist_sum->bin_data[i] / + (int32_t)no_of_samples; + else + phist_avg->bin_data[i] = phist_sum->bin_data[i]; + } + } + + if (status == VL53L1_ERROR_NONE) { + if (no_of_samples > 0) + phist_avg->VL53L1_p_004 = + phist_sum->VL53L1_p_004 / + (int32_t)no_of_samples; + else + phist_avg->VL53L1_p_004 = + phist_sum->VL53L1_p_004; + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_save_cfg_data( + VL53L1_DEV Dev) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + + VL53L1_zone_private_dyn_cfg_t *pzone_dyn_cfg; + VL53L1_dynamic_config_t *pdynamic = &(pdev->dyn_cfg); + + LOG_FUNCTION_START(""); + + pzone_dyn_cfg = + &(pres->zone_dyn_cfgs.VL53L1_p_002[pdev->ll_state.cfg_zone_id]); + + pzone_dyn_cfg->expected_stream_count = + pdev->ll_state.cfg_stream_count; + + pzone_dyn_cfg->expected_gph_id = + pdev->ll_state.cfg_gph_id; + + pzone_dyn_cfg->roi_config__user_roi_centre_spad = + pdynamic->roi_config__user_roi_centre_spad; + + pzone_dyn_cfg->roi_config__user_roi_requested_global_xy_size = + pdynamic->roi_config__user_roi_requested_global_xy_size; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_dynamic_zone_update( + VL53L1_DEV Dev, + VL53L1_range_results_t *presults) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = + VL53L1DevStructGetLLResultsHandle(Dev); + VL53L1_zone_private_dyn_cfgs_t *pZ = &(pres->zone_dyn_cfgs); + + uint8_t zone_id = pdev->ll_state.rd_zone_id; + uint8_t i; + uint16_t max_total_rate_per_spads; + uint16_t target_rate = + pdev->stat_cfg.dss_config__target_total_rate_mcps; + uint32_t temp = 0xFFFF; +#ifdef VL53L1_LOG_ENABLE + uint16_t eff_spad_cnt = + pZ->VL53L1_p_002[zone_id].dss_requested_effective_spad_count; +#endif + + LOG_FUNCTION_START(""); + + pZ->VL53L1_p_002[zone_id].dss_requested_effective_spad_count = 0; + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + " DYNZONEUPDATE: peak signal count rate mcps:"); + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + "%u actual effective spads: %u\n", + presults->VL53L1_p_002[0].peak_signal_count_rate_mcps, + presults->VL53L1_p_002[0].VL53L1_p_006); + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + " DYNZONEUPDATE: active results: %u\n", + presults->active_results); + + max_total_rate_per_spads = + presults->VL53L1_p_002[0].total_rate_per_spad_mcps; + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + " DYNZONEUPDATE: max total rate per spad at start: %u\n", + max_total_rate_per_spads); + + for (i = 1; i < presults->active_results; i++) { + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + " DYNZONEUPDATE: zone total rate per spad: zone_id: %u,", + i); + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + "total rate per spad: %u\n", + presults->VL53L1_p_002[i].total_rate_per_spad_mcps); + + if (presults->VL53L1_p_002[i].total_rate_per_spad_mcps > + max_total_rate_per_spads) + max_total_rate_per_spads = + presults->VL53L1_p_002[i].total_rate_per_spad_mcps; + + } + + if (max_total_rate_per_spads == 0) { + + temp = 0xFFFF; + } else { + + temp = target_rate << 14; + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + " DYNZONEUPDATE: 1: temp: %u\n", + temp); + + + temp = temp / max_total_rate_per_spads; + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + " DYNZONEUPDATE: 2: temp: %u\n", + temp); + + + if (temp > 0xFFFF) + temp = 0xFFFF; + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + " DYNZONEUPDATE: 3: temp: %u\n", + temp); + } + + pZ->VL53L1_p_002[zone_id].dss_requested_effective_spad_count = + (uint16_t)temp; + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + " DYNZONEUPDATE: zone_id: %u, target_rate: %u,", + zone_id, + target_rate); + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + "max_total_rate_per_spads: %u, requested_spads: %u\n", + max_total_rate_per_spads, + eff_spad_cnt); + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_multizone_hist_bins_update( + VL53L1_DEV Dev) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_ll_driver_state_t *pstate = &(pdev->ll_state); + VL53L1_zone_config_t *pzone_cfg = &(pdev->zone_cfg); + VL53L1_histogram_config_t *phist_cfg = &(pdev->hist_cfg); + VL53L1_histogram_config_t *pmulti_hist = + &(pzone_cfg->multizone_hist_cfg); + + uint8_t next_range_is_odd_timing = (pstate->cfg_stream_count) % 2; + + LOG_FUNCTION_START(""); + + + if (pzone_cfg->bin_config[pdev->ll_state.cfg_zone_id] == + VL53L1_ZONECONFIG_BINCONFIG__LOWAMB) { + if (!next_range_is_odd_timing) { + trace_print (VL53L1_TRACE_LEVEL_DEBUG, + " HISTBINCONFIGUPDATE: Setting LOWAMB EVEN timing\n"); + phist_cfg->histogram_config__low_amb_even_bin_0_1 = + pmulti_hist->histogram_config__low_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_even_bin_2_3 = + pmulti_hist->histogram_config__low_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_even_bin_4_5 = + pmulti_hist->histogram_config__low_amb_even_bin_4_5; + } + + if (next_range_is_odd_timing) { + trace_print (VL53L1_TRACE_LEVEL_DEBUG, + " HISTBINCONFIGUPDATE: Setting LOWAMB ODD timing\n"); + phist_cfg->histogram_config__low_amb_odd_bin_0_1 = + pmulti_hist->histogram_config__low_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_odd_bin_2_3 = + pmulti_hist->histogram_config__low_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_odd_bin_4_5 = + pmulti_hist->histogram_config__low_amb_even_bin_4_5; + } + } else if (pzone_cfg->bin_config[pdev->ll_state.cfg_zone_id] == + VL53L1_ZONECONFIG_BINCONFIG__MIDAMB) { + trace_print (VL53L1_TRACE_LEVEL_DEBUG, + " HISTBINCONFIGUPDATE: Setting MIDAMB timing\n"); + if (!next_range_is_odd_timing) { + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + " HISTBINCONFIGUPDATE: Setting MIDAMB EVEN timing\n"); + phist_cfg->histogram_config__low_amb_even_bin_0_1 = + pmulti_hist->histogram_config__mid_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_even_bin_2_3 = + pmulti_hist->histogram_config__mid_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_even_bin_4_5 = + pmulti_hist->histogram_config__mid_amb_even_bin_4_5; + } + + if (next_range_is_odd_timing) { + trace_print (VL53L1_TRACE_LEVEL_DEBUG, + " HISTBINCONFIGUPDATE: Setting MIDAMB ODD timing\n"); + phist_cfg->histogram_config__low_amb_odd_bin_0_1 = + pmulti_hist->histogram_config__mid_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_odd_bin_2_3 = + pmulti_hist->histogram_config__mid_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_odd_bin_4_5 = + pmulti_hist->histogram_config__mid_amb_even_bin_4_5; + } + } else if (pzone_cfg->bin_config[pdev->ll_state.cfg_zone_id] == + VL53L1_ZONECONFIG_BINCONFIG__HIGHAMB) { + if (!next_range_is_odd_timing) { + trace_print (VL53L1_TRACE_LEVEL_DEBUG, + " HISTBINCONFIGUPDATE: Setting HIGHAMB EVEN timing\n" + ); + phist_cfg->histogram_config__low_amb_even_bin_0_1 = + pmulti_hist->histogram_config__high_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_even_bin_2_3 = + pmulti_hist->histogram_config__high_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_even_bin_4_5 = + pmulti_hist->histogram_config__high_amb_even_bin_4_5; + } + + if (next_range_is_odd_timing) { + trace_print (VL53L1_TRACE_LEVEL_DEBUG, + " HISTBINCONFIGUPDATE: Setting HIGHAMB ODD timing\n"); + phist_cfg->histogram_config__low_amb_odd_bin_0_1 = + pmulti_hist->histogram_config__high_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_odd_bin_2_3 = + pmulti_hist->histogram_config__high_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_odd_bin_4_5 = + pmulti_hist->histogram_config__high_amb_even_bin_4_5; + } + } + + + + if (status == VL53L1_ERROR_NONE) { + VL53L1_copy_hist_bins_to_static_cfg( + phist_cfg, + &(pdev->stat_cfg), + &(pdev->tim_cfg)); + } + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_update_internal_stream_counters( + VL53L1_DEV Dev, + uint8_t external_stream_count, + uint8_t *pinternal_stream_count, + uint8_t *pinternal_stream_count_val) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t stream_divider; + + VL53L1_LLDriverData_t *pdev = + VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + stream_divider = pdev->gen_cfg.global_config__stream_divider; + + if (stream_divider == 0) { + + + *pinternal_stream_count = external_stream_count; + + } else if (*pinternal_stream_count_val == (stream_divider-1)) { + + + if (*pinternal_stream_count == 0xFF) + *pinternal_stream_count = 0x80; + else + *pinternal_stream_count = *pinternal_stream_count + 1; + + + *pinternal_stream_count_val = 0; + + } else { + + + *pinternal_stream_count_val = *pinternal_stream_count_val + 1; + } + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + "UPDINTSTREAMCOUNT internal_steam_count: %d,", + *pinternal_stream_count); + + trace_print(VL53L1_TRACE_LEVEL_DEBUG, + "internal_stream_count_val: %d, divider: %d\n", + *pinternal_stream_count_val, + stream_divider); + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_set_histogram_multizone_initial_bin_config( + VL53L1_zone_config_t *pzone_cfg, + VL53L1_histogram_config_t *phist_cfg, + VL53L1_histogram_config_t *pmulti_hist) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + if (pzone_cfg->bin_config[0] == + VL53L1_ZONECONFIG_BINCONFIG__LOWAMB) { + phist_cfg->histogram_config__low_amb_even_bin_0_1 = + pmulti_hist->histogram_config__low_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_even_bin_2_3 = + pmulti_hist->histogram_config__low_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_even_bin_4_5 = + pmulti_hist->histogram_config__low_amb_even_bin_4_5; + + phist_cfg->histogram_config__low_amb_odd_bin_0_1 = + pmulti_hist->histogram_config__low_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_odd_bin_2_3 = + pmulti_hist->histogram_config__low_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_odd_bin_4_5 = + pmulti_hist->histogram_config__low_amb_even_bin_4_5; + } else if (pzone_cfg->bin_config[0] == + VL53L1_ZONECONFIG_BINCONFIG__MIDAMB) { + phist_cfg->histogram_config__low_amb_even_bin_0_1 = + pmulti_hist->histogram_config__mid_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_even_bin_2_3 = + pmulti_hist->histogram_config__mid_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_even_bin_4_5 = + pmulti_hist->histogram_config__mid_amb_even_bin_4_5; + + phist_cfg->histogram_config__low_amb_odd_bin_0_1 = + pmulti_hist->histogram_config__mid_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_odd_bin_2_3 = + pmulti_hist->histogram_config__mid_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_odd_bin_4_5 = + pmulti_hist->histogram_config__mid_amb_even_bin_4_5; + } else if (pzone_cfg->bin_config[0] == + VL53L1_ZONECONFIG_BINCONFIG__HIGHAMB) { + phist_cfg->histogram_config__low_amb_even_bin_0_1 = + pmulti_hist->histogram_config__high_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_even_bin_2_3 = + pmulti_hist->histogram_config__high_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_even_bin_4_5 = + pmulti_hist->histogram_config__high_amb_even_bin_4_5; + phist_cfg->histogram_config__low_amb_odd_bin_0_1 = + pmulti_hist->histogram_config__high_amb_even_bin_0_1; + phist_cfg->histogram_config__low_amb_odd_bin_2_3 = + pmulti_hist->histogram_config__high_amb_even_bin_2_3; + phist_cfg->histogram_config__low_amb_odd_bin_4_5 = + pmulti_hist->histogram_config__high_amb_even_bin_4_5; + } + + LOG_FUNCTION_END(status); + return status; +} + + + +uint8_t VL53L1_encode_GPIO_interrupt_config( + VL53L1_GPIO_interrupt_config_t *pintconf) +{ + uint8_t system__interrupt_config; + + system__interrupt_config = pintconf->intr_mode_distance; + system__interrupt_config |= ((pintconf->intr_mode_rate) << 2); + system__interrupt_config |= ((pintconf->intr_new_measure_ready) << 5); + system__interrupt_config |= ((pintconf->intr_no_target) << 6); + system__interrupt_config |= ((pintconf->intr_combined_mode) << 7); + + return system__interrupt_config; +} + + + +VL53L1_GPIO_interrupt_config_t VL53L1_decode_GPIO_interrupt_config( + uint8_t system__interrupt_config) +{ + VL53L1_GPIO_interrupt_config_t intconf; + + intconf.intr_mode_distance = system__interrupt_config & 0x03; + intconf.intr_mode_rate = (system__interrupt_config >> 2) & 0x03; + intconf.intr_new_measure_ready = (system__interrupt_config >> 5) & 0x01; + intconf.intr_no_target = (system__interrupt_config >> 6) & 0x01; + intconf.intr_combined_mode = (system__interrupt_config >> 7) & 0x01; + + + intconf.threshold_rate_low = 0; + intconf.threshold_rate_high = 0; + intconf.threshold_distance_low = 0; + intconf.threshold_distance_high = 0; + + return intconf; +} + + + +VL53L1_Error VL53L1_set_GPIO_distance_threshold( + VL53L1_DEV Dev, + uint16_t threshold_high, + uint16_t threshold_low) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->dyn_cfg.system__thresh_high = threshold_high; + pdev->dyn_cfg.system__thresh_low = threshold_low; + + LOG_FUNCTION_END(status); + return status; +} + + + +VL53L1_Error VL53L1_set_GPIO_rate_threshold( + VL53L1_DEV Dev, + uint16_t threshold_high, + uint16_t threshold_low) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->gen_cfg.system__thresh_rate_high = threshold_high; + pdev->gen_cfg.system__thresh_rate_low = threshold_low; + + LOG_FUNCTION_END(status); + return status; +} + + + +VL53L1_Error VL53L1_set_GPIO_thresholds_from_struct( + VL53L1_DEV Dev, + VL53L1_GPIO_interrupt_config_t *pintconf) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + status = VL53L1_set_GPIO_distance_threshold( + Dev, + pintconf->threshold_distance_high, + pintconf->threshold_distance_low); + + if (status == VL53L1_ERROR_NONE) { + status = + VL53L1_set_GPIO_rate_threshold( + Dev, + pintconf->threshold_rate_high, + pintconf->threshold_rate_low); + } + + LOG_FUNCTION_END(status); + return status; +} + + +VL53L1_Error VL53L1_set_ref_spad_char_config( + VL53L1_DEV Dev, + uint8_t vcsel_period_a, + uint32_t phasecal_timeout_us, + uint16_t total_rate_target_mcps, + uint16_t max_count_rate_rtn_limit_mcps, + uint16_t min_count_rate_rtn_limit_mcps, + uint16_t fast_osc_frequency) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t buffer[2]; + + uint32_t macro_period_us = 0; + uint32_t timeout_mclks = 0; + + LOG_FUNCTION_START(""); + + + macro_period_us = + VL53L1_calc_macro_period_us( + fast_osc_frequency, + vcsel_period_a); + if (macro_period_us == 0) + macro_period_us = 1; + + + + timeout_mclks = phasecal_timeout_us << 12; + timeout_mclks = timeout_mclks + (macro_period_us>>1); + timeout_mclks = timeout_mclks / macro_period_us; + + if (timeout_mclks > 0xFF) + pdev->gen_cfg.phasecal_config__timeout_macrop = 0xFF; + else + pdev->gen_cfg.phasecal_config__timeout_macrop = + (uint8_t)timeout_mclks; + + pdev->tim_cfg.range_config__vcsel_period_a = vcsel_period_a; + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WrByte( + Dev, + VL53L1_PHASECAL_CONFIG__TIMEOUT_MACROP, + pdev->gen_cfg.phasecal_config__timeout_macrop); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WrByte( + Dev, + VL53L1_RANGE_CONFIG__VCSEL_PERIOD_A, + pdev->tim_cfg.range_config__vcsel_period_a); + + + + buffer[0] = pdev->tim_cfg.range_config__vcsel_period_a; + buffer[1] = pdev->tim_cfg.range_config__vcsel_period_a; + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WriteMulti( + Dev, + VL53L1_SD_CONFIG__WOI_SD0, + buffer, + 2); + + + + pdev->customer.ref_spad_char__total_rate_target_mcps = + total_rate_target_mcps; + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WrWord( + Dev, + VL53L1_REF_SPAD_CHAR__TOTAL_RATE_TARGET_MCPS, + total_rate_target_mcps); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WrWord( + Dev, + VL53L1_RANGE_CONFIG__SIGMA_THRESH, + max_count_rate_rtn_limit_mcps); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WrWord( + Dev, + VL53L1_RANGE_CONFIG__MIN_COUNT_RATE_RTN_LIMIT_MCPS, + min_count_rate_rtn_limit_mcps); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_ssc_config( + VL53L1_DEV Dev, + VL53L1_ssc_config_t *pssc_cfg, + uint16_t fast_osc_frequency) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t buffer[5]; + + uint32_t macro_period_us = 0; + uint16_t timeout_encoded = 0; + + LOG_FUNCTION_START(""); + + + macro_period_us = + VL53L1_calc_macro_period_us( + fast_osc_frequency, + pssc_cfg->VL53L1_p_009); + + + timeout_encoded = + VL53L1_calc_encoded_timeout( + pssc_cfg->timeout_us, + macro_period_us); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WrByte( + Dev, + VL53L1_CAL_CONFIG__VCSEL_START, + pssc_cfg->vcsel_start); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WrByte( + Dev, + VL53L1_GLOBAL_CONFIG__VCSEL_WIDTH, + pssc_cfg->vcsel_width); + + + + buffer[0] = (uint8_t)((timeout_encoded & 0x0000FF00) >> 8); + buffer[1] = (uint8_t) (timeout_encoded & 0x000000FF); + buffer[2] = pssc_cfg->VL53L1_p_009; + buffer[3] = (uint8_t)((pssc_cfg->rate_limit_mcps & 0x0000FF00) >> 8); + buffer[4] = (uint8_t) (pssc_cfg->rate_limit_mcps & 0x000000FF); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WriteMulti( + Dev, + VL53L1_RANGE_CONFIG__TIMEOUT_MACROP_B_HI, + buffer, + 5); + + + + buffer[0] = pssc_cfg->VL53L1_p_009; + buffer[1] = pssc_cfg->VL53L1_p_009; + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WriteMulti( + Dev, + VL53L1_SD_CONFIG__WOI_SD0, + buffer, + 2); + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WrByte( + Dev, + VL53L1_NVM_BIST__CTRL, + pssc_cfg->array_select); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_spad_rate_data( + VL53L1_DEV Dev, + VL53L1_spad_rate_data_t *pspad_rates) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + int i = 0; + + uint8_t VL53L1_p_002[512]; + uint8_t *pdata = &VL53L1_p_002[0]; + + LOG_FUNCTION_START(""); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_ReadMulti( + Dev, + VL53L1_PRIVATE__PATCH_BASE_ADDR_RSLV, + pdata, + 512); + + + pdata = &VL53L1_p_002[0]; + for (i = 0; i < VL53L1_NO_OF_SPAD_ENABLES; i++) { + pspad_rates->rate_data[i] = + (uint16_t)VL53L1_decode_unsigned_integer(pdata, 2); + pdata += 2; + } + + + + pspad_rates->VL53L1_p_023 = VL53L1_NO_OF_SPAD_ENABLES; + pspad_rates->no_of_values = VL53L1_NO_OF_SPAD_ENABLES; + pspad_rates->fractional_bits = 15; + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_dynamic_xtalk_correction_calc_required_samples( + VL53L1_DEV Dev + ) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = VL53L1DevStructGetLLResultsHandle(Dev); + VL53L1_smudge_corrector_config_t *pconfig = + &(pdev->smudge_correct_config); + VL53L1_smudge_corrector_internals_t *pint = + &(pdev->smudge_corrector_internals); + + VL53L1_range_results_t *presults = &(pres->range_results); + VL53L1_range_data_t *pxmonitor = &(presults->xmonitor); + + uint32_t peak_duration_us = pxmonitor->peak_duration_us; + + uint64_t temp64a; + uint64_t temp64z; + + LOG_FUNCTION_START(""); + + if (peak_duration_us == 0) + peak_duration_us = 1000; + + temp64a = pxmonitor->VL53L1_p_021 + + pxmonitor->VL53L1_p_020; + temp64a = do_division_u((temp64a * 1000), peak_duration_us); + temp64a = do_division_u((temp64a * 1000), peak_duration_us); + + temp64z = pconfig->noise_margin * pxmonitor->VL53L1_p_006; + if (temp64z == 0) + temp64z = 1; + temp64a = temp64a * 1000 * 256; + temp64a = do_division_u(temp64a, temp64z); + temp64a = temp64a * 1000 * 256; + temp64a = do_division_u(temp64a, temp64z); + + pint->required_samples = (uint32_t)temp64a; + + + if (pint->required_samples < 2) + pint->required_samples = 2; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_dynamic_xtalk_correction_calc_new_xtalk( + VL53L1_DEV Dev, + uint32_t xtalk_offset_out, + VL53L1_smudge_corrector_config_t *pconfig, + VL53L1_smudge_corrector_data_t *pout, + uint8_t add_smudge, + uint8_t soft_update + ) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + int16_t x_gradient_scaler; + int16_t y_gradient_scaler; + uint32_t orig_xtalk_offset; + int16_t orig_x_gradient; + int16_t orig_y_gradient; + uint8_t histo_merge_nb; + uint8_t i; + int32_t itemp32; + uint32_t SmudgeFactor; + VL53L1_xtalk_config_t *pX = &(pdev->xtalk_cfg); + VL53L1_xtalk_calibration_results_t *pC = &(pdev->xtalk_cal); + uint32_t *pcpo; + uint32_t max, nXtalk, cXtalk; + uint8_t merge_enabled; + uint32_t incXtalk, cval; + + + LOG_FUNCTION_START(""); + + merge_enabled = (pdev->tuning_parms.tp_hist_merge == 1) && + (VL53L1DevDataGet(Dev, CurrentParameters.PresetMode) == + VL53L1_PRESETMODE_RANGING); + + + if (add_smudge == 1) { + pout->algo__crosstalk_compensation_plane_offset_kcps = + (uint32_t)xtalk_offset_out + + (uint32_t)pconfig->smudge_margin; + } else { + pout->algo__crosstalk_compensation_plane_offset_kcps = + (uint32_t)xtalk_offset_out; + } + + + orig_xtalk_offset = + pX->nvm_default__crosstalk_compensation_plane_offset_kcps; + + orig_x_gradient = + pX->nvm_default__crosstalk_compensation_x_plane_gradient_kcps; + + orig_y_gradient = + pX->nvm_default__crosstalk_compensation_y_plane_gradient_kcps; + + if (((pconfig->user_scaler_set == 0) || + (pconfig->scaler_calc_method == 1)) && + (pC->algo__crosstalk_compensation_plane_offset_kcps != 0)) { + + VL53L1_compute_histo_merge_nb(Dev, &histo_merge_nb); + if (histo_merge_nb == 0) + histo_merge_nb = 1; + if (!merge_enabled) + orig_xtalk_offset = + pC->algo__crosstalk_compensation_plane_offset_kcps; + else + orig_xtalk_offset = + pC->algo__xtalk_cpo_HistoMerge_kcps[histo_merge_nb-1]; + + orig_x_gradient = + pC->algo__crosstalk_compensation_x_plane_gradient_kcps; + + orig_y_gradient = + pC->algo__crosstalk_compensation_y_plane_gradient_kcps; + } + + + if ((pconfig->user_scaler_set == 0) && (orig_x_gradient == 0)) + pout->gradient_zero_flag |= 0x01; + + if ((pconfig->user_scaler_set == 0) && (orig_y_gradient == 0)) + pout->gradient_zero_flag |= 0x02; + + + + if (orig_xtalk_offset == 0) + orig_xtalk_offset = 1; + + + + if (pconfig->user_scaler_set == 1) { + x_gradient_scaler = pconfig->x_gradient_scaler; + y_gradient_scaler = pconfig->y_gradient_scaler; + } else { + + x_gradient_scaler = (int16_t)do_division_s( + (((int32_t)orig_x_gradient) << 6), + orig_xtalk_offset); + pconfig->x_gradient_scaler = x_gradient_scaler; + y_gradient_scaler = (int16_t)do_division_s( + (((int32_t)orig_y_gradient) << 6), + orig_xtalk_offset); + pconfig->y_gradient_scaler = y_gradient_scaler; + } + + + + if (pconfig->scaler_calc_method == 0) { + + + itemp32 = (int32_t)( + pout->algo__crosstalk_compensation_plane_offset_kcps * + x_gradient_scaler); + itemp32 = itemp32 >> 6; + if (itemp32 > 0xFFFF) + itemp32 = 0xFFFF; + + pout->algo__crosstalk_compensation_x_plane_gradient_kcps = + (int16_t)itemp32; + + itemp32 = (int32_t)( + pout->algo__crosstalk_compensation_plane_offset_kcps * + y_gradient_scaler); + itemp32 = itemp32 >> 6; + if (itemp32 > 0xFFFF) + itemp32 = 0xFFFF; + + pout->algo__crosstalk_compensation_y_plane_gradient_kcps = + (int16_t)itemp32; + } else if (pconfig->scaler_calc_method == 1) { + + + itemp32 = (int32_t)(orig_xtalk_offset - + pout->algo__crosstalk_compensation_plane_offset_kcps); + itemp32 = (int32_t)(do_division_s(itemp32, 16)); + itemp32 = itemp32 << 2; + itemp32 = itemp32 + (int32_t)(orig_x_gradient); + if (itemp32 > 0xFFFF) + itemp32 = 0xFFFF; + + pout->algo__crosstalk_compensation_x_plane_gradient_kcps = + (int16_t)itemp32; + + itemp32 = (int32_t)(orig_xtalk_offset - + pout->algo__crosstalk_compensation_plane_offset_kcps); + itemp32 = (int32_t)(do_division_s(itemp32, 80)); + itemp32 = itemp32 << 2; + itemp32 = itemp32 + (int32_t)(orig_y_gradient); + if (itemp32 > 0xFFFF) + itemp32 = 0xFFFF; + + pout->algo__crosstalk_compensation_y_plane_gradient_kcps = + (int16_t)itemp32; + } + + + if (pconfig->smudge_corr_apply_enabled == 1 && + (soft_update != 1)) { + pout->new_xtalk_applied_flag = 1; + nXtalk = pout->algo__crosstalk_compensation_plane_offset_kcps; + + VL53L1_compute_histo_merge_nb(Dev, &histo_merge_nb); + max = pdev->tuning_parms.tp_hist_merge_max_size; + pcpo = &(pC->algo__xtalk_cpo_HistoMerge_kcps[0]); + if ((histo_merge_nb > 0) && merge_enabled && (nXtalk != 0)) { + cXtalk = + pX->algo__crosstalk_compensation_plane_offset_kcps; + SmudgeFactor = cXtalk * 1000 / nXtalk; + if ((max == 0)|| + (SmudgeFactor >= pconfig->max_smudge_factor)) + pout->new_xtalk_applied_flag = 0; + else { + incXtalk = nXtalk / max; + cval = 0; + for (i = 0; i < max-1; i++) { + cval += incXtalk; + *pcpo = cval; + pcpo++; + } + *pcpo = nXtalk; + } + } + if (pout->new_xtalk_applied_flag) { + + pX->algo__crosstalk_compensation_plane_offset_kcps = + pout->algo__crosstalk_compensation_plane_offset_kcps; + pX->algo__crosstalk_compensation_x_plane_gradient_kcps = + pout->algo__crosstalk_compensation_x_plane_gradient_kcps; + pX->algo__crosstalk_compensation_y_plane_gradient_kcps = + pout->algo__crosstalk_compensation_y_plane_gradient_kcps; + + if (pconfig->smudge_corr_single_apply == 1) { + + pconfig->smudge_corr_apply_enabled = 0; + pconfig->smudge_corr_single_apply = 0; + } + } + } + + + if (soft_update != 1) + pout->smudge_corr_valid = 1; + + LOG_FUNCTION_END(status); + + return status; +} + +#define CONT_CONTINUE 0 +#define CONT_NEXT_LOOP 1 +#define CONT_RESET 2 +VL53L1_Error VL53L1_dynamic_xtalk_correction_corrector( + VL53L1_DEV Dev + ) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = VL53L1DevStructGetLLResultsHandle(Dev); + VL53L1_smudge_corrector_config_t *pconfig = + &(pdev->smudge_correct_config); + VL53L1_smudge_corrector_internals_t *pint = + &(pdev->smudge_corrector_internals); + VL53L1_smudge_corrector_data_t *pout = + &(pres->range_results.smudge_corrector_data); + VL53L1_range_results_t *pR = &(pres->range_results); + VL53L1_xtalk_config_t *pX = &(pdev->xtalk_cfg); + + uint8_t run_smudge_detection = 0; + uint8_t merging_complete = 0; + uint8_t run_nodetect = 0; + uint8_t ambient_check = 0; + int32_t itemp32 = 0; + uint64_t utemp64 = 0; + uint8_t continue_processing = CONT_CONTINUE; + uint32_t xtalk_offset_out = 0; + uint32_t xtalk_offset_in = 0; + uint32_t current_xtalk = 0; + uint32_t smudge_margin_adjusted = 0; + uint8_t i = 0; + uint8_t nodetect_index = 0; + uint16_t amr; + uint32_t cco; + uint8_t histo_merge_nb; + uint8_t merge_enabled; + + + LOG_FUNCTION_START(""); + + merge_enabled = (pdev->tuning_parms.tp_hist_merge == 1) && + (VL53L1DevDataGet(Dev, CurrentParameters.PresetMode) == + VL53L1_PRESETMODE_RANGING); + + VL53L1_compute_histo_merge_nb(Dev, &histo_merge_nb); + if ((histo_merge_nb == 0) || (!merge_enabled)) + histo_merge_nb = 1; + + + VL53L1_dynamic_xtalk_correction_output_init(pres); + + + ambient_check = (pconfig->smudge_corr_ambient_threshold == 0) || + ((pconfig->smudge_corr_ambient_threshold * histo_merge_nb) > + ((uint32_t)pR->xmonitor.ambient_count_rate_mcps)); + + + merging_complete = ((!merge_enabled) || + (histo_merge_nb == pdev->tuning_parms.tp_hist_merge_max_size)); + + run_smudge_detection = + (pconfig->smudge_corr_enabled == 1) && + ambient_check && + (pR->xmonitor.range_status + == VL53L1_DEVICEERROR_RANGECOMPLETE) && + merging_complete; + + + if ((pR->xmonitor.range_status + != VL53L1_DEVICEERROR_RANGECOMPLETE) && + (pconfig->smudge_corr_enabled == 1)) { + + run_nodetect = 2; + for (i = 0; i < pR->active_results; i++) { + if (pR->VL53L1_p_002[i].range_status == + VL53L1_DEVICEERROR_RANGECOMPLETE) { + if (pR->VL53L1_p_002[i].median_range_mm + <= + pconfig->nodetect_min_range_mm) { + run_nodetect = 0; + } else { + if (run_nodetect == 2) { + run_nodetect = 1; + nodetect_index = i; + } + } + } + } + + if (run_nodetect == 2) + + run_nodetect = 0; + + amr = + pR->VL53L1_p_002[nodetect_index].ambient_count_rate_mcps; + + if (run_nodetect == 1) { + + + + + utemp64 = 1000 * ((uint64_t)amr); + + + utemp64 = utemp64 << 9; + + + if (utemp64 < pconfig->nodetect_ambient_threshold) + run_nodetect = 1; + else + run_nodetect = 0; + + } + } + + + if (run_smudge_detection) { + + pint->nodetect_counter = 0; + + + VL53L1_dynamic_xtalk_correction_calc_required_samples(Dev); + + + xtalk_offset_in = + pR->xmonitor.VL53L1_p_012; + + + cco = pX->algo__crosstalk_compensation_plane_offset_kcps; + current_xtalk = ((uint32_t)cco) << 2; + + + smudge_margin_adjusted = + ((uint32_t)(pconfig->smudge_margin)) << 2; + + + itemp32 = xtalk_offset_in - current_xtalk + + smudge_margin_adjusted; + + if (itemp32 < 0) + itemp32 = itemp32 * (-1); + + + if (itemp32 > ((int32_t)pconfig->single_xtalk_delta)) { + if ((int32_t)xtalk_offset_in > + ((int32_t)current_xtalk - + (int32_t)smudge_margin_adjusted)) { + pout->single_xtalk_delta_flag = 1; + } else { + pout->single_xtalk_delta_flag = 2; + } + } + + + pint->current_samples = pint->current_samples + 1; + + + if (pint->current_samples > pconfig->sample_limit) { + pout->sample_limit_exceeded_flag = 1; + continue_processing = CONT_RESET; + } else { + pint->accumulator = pint->accumulator + + xtalk_offset_in; + } + + if (pint->current_samples < pint->required_samples) + continue_processing = CONT_NEXT_LOOP; + + + xtalk_offset_out = + (uint32_t)(do_division_u(pint->accumulator, + pint->current_samples)); + + + itemp32 = xtalk_offset_out - current_xtalk + + smudge_margin_adjusted; + + if (itemp32 < 0) + itemp32 = itemp32 * (-1); + + if (continue_processing == CONT_CONTINUE && + (itemp32 >= ((int32_t)(pconfig->averaged_xtalk_delta))) + ) { + if ((int32_t)xtalk_offset_out > + ((int32_t)current_xtalk - + (int32_t)smudge_margin_adjusted)) + pout->averaged_xtalk_delta_flag = 1; + else + pout->averaged_xtalk_delta_flag = 2; + } + + if (continue_processing == CONT_CONTINUE && + (itemp32 < ((int32_t)(pconfig->averaged_xtalk_delta))) + ) + + continue_processing = CONT_RESET; + + + + pout->smudge_corr_clipped = 0; + if ((continue_processing == CONT_CONTINUE) && + (pconfig->smudge_corr_clip_limit != 0)) { + if (xtalk_offset_out > + (pconfig->smudge_corr_clip_limit * histo_merge_nb)) { + pout->smudge_corr_clipped = 1; + continue_processing = CONT_RESET; + } + } + + + + if (pconfig->user_xtalk_offset_limit_hi && + (xtalk_offset_out > + pconfig->user_xtalk_offset_limit)) + xtalk_offset_out = + pconfig->user_xtalk_offset_limit; + + + + if ((pconfig->user_xtalk_offset_limit_hi == 0) && + (xtalk_offset_out < + pconfig->user_xtalk_offset_limit)) + xtalk_offset_out = + pconfig->user_xtalk_offset_limit; + + + + xtalk_offset_out = xtalk_offset_out >> 2; + if (xtalk_offset_out > 0x3FFFF) + xtalk_offset_out = 0x3FFFF; + + + if (continue_processing == CONT_CONTINUE) { + + VL53L1_dynamic_xtalk_correction_calc_new_xtalk( + Dev, + xtalk_offset_out, + pconfig, + pout, + 1, + 0 + ); + + + continue_processing = CONT_RESET; + } else { + + VL53L1_dynamic_xtalk_correction_calc_new_xtalk( + Dev, + xtalk_offset_out, + pconfig, + pout, + 1, + 1 + ); + } + + + if (continue_processing == CONT_RESET) { + pint->accumulator = 0; + pint->current_samples = 0; + pint->nodetect_counter = 0; + } + + } + + continue_processing = CONT_CONTINUE; + if (run_nodetect == 1) { + + pint->nodetect_counter += 1; + + + if (pint->nodetect_counter < pconfig->nodetect_sample_limit) + continue_processing = CONT_NEXT_LOOP; + + + xtalk_offset_out = (uint32_t)(pconfig->nodetect_xtalk_offset); + + if (continue_processing == CONT_CONTINUE) { + + VL53L1_dynamic_xtalk_correction_calc_new_xtalk( + Dev, + xtalk_offset_out, + pconfig, + pout, + 0, + 0 + ); + + + pout->smudge_corr_valid = 2; + + + continue_processing = CONT_RESET; + } else { + + VL53L1_dynamic_xtalk_correction_calc_new_xtalk( + Dev, + xtalk_offset_out, + pconfig, + pout, + 0, + 1 + ); + } + + + if (continue_processing == CONT_RESET) { + pint->accumulator = 0; + pint->current_samples = 0; + pint->nodetect_counter = 0; + } + } + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_dynamic_xtalk_correction_data_init( + VL53L1_DEV Dev + ) +{ + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_LLDriverResults_t *pres = VL53L1DevStructGetLLResultsHandle(Dev); + + LOG_FUNCTION_START(""); + + + + pdev->smudge_correct_config.smudge_corr_enabled = 1; + pdev->smudge_correct_config.smudge_corr_apply_enabled = 1; + pdev->smudge_correct_config.smudge_corr_single_apply = + VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_COR_SINGLE_APPLY_DEFAULT; + + pdev->smudge_correct_config.smudge_margin = + VL53L1_TUNINGPARM_DYNXTALK_SMUDGE_MARGIN_DEFAULT; + pdev->smudge_correct_config.noise_margin = + VL53L1_TUNINGPARM_DYNXTALK_NOISE_MARGIN_DEFAULT; + pdev->smudge_correct_config.user_xtalk_offset_limit = + VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT_DEFAULT; + pdev->smudge_correct_config.user_xtalk_offset_limit_hi = + VL53L1_TUNINGPARM_DYNXTALK_XTALK_OFFSET_LIMIT_HI_DEFAULT; + pdev->smudge_correct_config.sample_limit = + VL53L1_TUNINGPARM_DYNXTALK_SAMPLE_LIMIT_DEFAULT; + pdev->smudge_correct_config.single_xtalk_delta = + VL53L1_TUNINGPARM_DYNXTALK_SINGLE_XTALK_DELTA_DEFAULT; + pdev->smudge_correct_config.averaged_xtalk_delta = + VL53L1_TUNINGPARM_DYNXTALK_AVERAGED_XTALK_DELTA_DEFAULT; + pdev->smudge_correct_config.smudge_corr_clip_limit = + VL53L1_TUNINGPARM_DYNXTALK_CLIP_LIMIT_DEFAULT; + pdev->smudge_correct_config.smudge_corr_ambient_threshold = + VL53L1_TUNINGPARM_DYNXTALK_XTALK_AMB_THRESHOLD_DEFAULT; + pdev->smudge_correct_config.scaler_calc_method = + 0; + pdev->smudge_correct_config.x_gradient_scaler = + VL53L1_TUNINGPARM_DYNXTALK_XGRADIENT_SCALER_DEFAULT; + pdev->smudge_correct_config.y_gradient_scaler = + VL53L1_TUNINGPARM_DYNXTALK_YGRADIENT_SCALER_DEFAULT; + pdev->smudge_correct_config.user_scaler_set = + VL53L1_TUNINGPARM_DYNXTALK_USER_SCALER_SET_DEFAULT; + pdev->smudge_correct_config.nodetect_ambient_threshold = + VL53L1_TUNINGPARM_DYNXTALK_NODETECT_AMB_THRESHOLD_KCPS_DEFAULT; + pdev->smudge_correct_config.nodetect_sample_limit = + VL53L1_TUNINGPARM_DYNXTALK_NODETECT_SAMPLE_LIMIT_DEFAULT; + pdev->smudge_correct_config.nodetect_xtalk_offset = + VL53L1_TUNINGPARM_DYNXTALK_NODETECT_XTALK_OFFSET_KCPS_DEFAULT; + pdev->smudge_correct_config.nodetect_min_range_mm = + VL53L1_TUNINGPARM_DYNXTALK_NODETECT_MIN_RANGE_MM_DEFAULT; + pdev->smudge_correct_config.max_smudge_factor = + VL53L1_TUNINGPARM_DYNXTALK_MAX_SMUDGE_FACTOR_DEFAULT; + + + pdev->smudge_corrector_internals.current_samples = 0; + pdev->smudge_corrector_internals.required_samples = 0; + pdev->smudge_corrector_internals.accumulator = 0; + pdev->smudge_corrector_internals.nodetect_counter = 0; + + + VL53L1_dynamic_xtalk_correction_output_init(pres); + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_dynamic_xtalk_correction_output_init( + VL53L1_LLDriverResults_t *pres + ) +{ + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_smudge_corrector_data_t *pdata; + + LOG_FUNCTION_START(""); + + + pdata = &(pres->range_results.smudge_corrector_data); + + pdata->smudge_corr_valid = 0; + pdata->smudge_corr_clipped = 0; + pdata->single_xtalk_delta_flag = 0; + pdata->averaged_xtalk_delta_flag = 0; + pdata->sample_limit_exceeded_flag = 0; + pdata->gradient_zero_flag = 0; + pdata->new_xtalk_applied_flag = 0; + + pdata->algo__crosstalk_compensation_plane_offset_kcps = 0; + pdata->algo__crosstalk_compensation_x_plane_gradient_kcps = 0; + pdata->algo__crosstalk_compensation_y_plane_gradient_kcps = 0; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_xtalk_cal_data_init( + VL53L1_DEV Dev + ) +{ + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + pdev->xtalk_cal.algo__crosstalk_compensation_plane_offset_kcps = 0; + pdev->xtalk_cal.algo__crosstalk_compensation_x_plane_gradient_kcps = 0; + pdev->xtalk_cal.algo__crosstalk_compensation_y_plane_gradient_kcps = 0; + memset(&pdev->xtalk_cal.algo__xtalk_cpo_HistoMerge_kcps[0], 0, + sizeof(pdev->xtalk_cal.algo__xtalk_cpo_HistoMerge_kcps)); + + LOG_FUNCTION_END(status); + + return status; +} + + + + + + +VL53L1_Error VL53L1_low_power_auto_data_init( + VL53L1_DEV Dev + ) +{ + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + pdev->low_power_auto_data.vhv_loop_bound = + VL53L1_TUNINGPARM_LOWPOWERAUTO_VHV_LOOP_BOUND_DEFAULT; + pdev->low_power_auto_data.is_low_power_auto_mode = 0; + pdev->low_power_auto_data.low_power_auto_range_count = 0; + pdev->low_power_auto_data.saved_interrupt_config = 0; + pdev->low_power_auto_data.saved_vhv_init = 0; + pdev->low_power_auto_data.saved_vhv_timeout = 0; + pdev->low_power_auto_data.first_run_phasecal_result = 0; + pdev->low_power_auto_data.dss__total_rate_per_spad_mcps = 0; + pdev->low_power_auto_data.dss__required_spads = 0; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_low_power_auto_data_stop_range( + VL53L1_DEV Dev + ) +{ + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + + + pdev->low_power_auto_data.low_power_auto_range_count = 0xFF; + + pdev->low_power_auto_data.first_run_phasecal_result = 0; + pdev->low_power_auto_data.dss__total_rate_per_spad_mcps = 0; + pdev->low_power_auto_data.dss__required_spads = 0; + + + if (pdev->low_power_auto_data.saved_vhv_init != 0) + pdev->stat_nvm.vhv_config__init = + pdev->low_power_auto_data.saved_vhv_init; + if (pdev->low_power_auto_data.saved_vhv_timeout != 0) + pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound = + pdev->low_power_auto_data.saved_vhv_timeout; + + + pdev->gen_cfg.phasecal_config__override = 0x00; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_config_low_power_auto_mode( + VL53L1_general_config_t *pgeneral, + VL53L1_dynamic_config_t *pdynamic, + VL53L1_low_power_auto_data_t *plpadata + ) +{ + + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + SUPPRESS_UNUSED_WARNING(pgeneral); + + LOG_FUNCTION_START(""); + + + plpadata->is_low_power_auto_mode = 1; + + + plpadata->low_power_auto_range_count = 0; + + + pdynamic->system__sequence_config = + VL53L1_SEQUENCE_VHV_EN | + VL53L1_SEQUENCE_PHASECAL_EN | + VL53L1_SEQUENCE_DSS1_EN | + VL53L1_SEQUENCE_DSS2_EN | + + + VL53L1_SEQUENCE_RANGE_EN; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_low_power_auto_setup_manual_calibration( + VL53L1_DEV Dev) +{ + + + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + pdev->low_power_auto_data.saved_vhv_init = + pdev->stat_nvm.vhv_config__init; + pdev->low_power_auto_data.saved_vhv_timeout = + pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound; + + + pdev->stat_nvm.vhv_config__init &= 0x7F; + + pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound = + (pdev->stat_nvm.vhv_config__timeout_macrop_loop_bound & 0x03) + + (pdev->low_power_auto_data.vhv_loop_bound << 2); + + pdev->gen_cfg.phasecal_config__override = 0x01; + pdev->low_power_auto_data.first_run_phasecal_result = + pdev->dbg_results.phasecal_result__vcsel_start; + pdev->gen_cfg.cal_config__vcsel_start = + pdev->low_power_auto_data.first_run_phasecal_result; + + LOG_FUNCTION_END(status); + + return status; +} + +VL53L1_Error VL53L1_low_power_auto_update_DSS( + VL53L1_DEV Dev) +{ + + + + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + VL53L1_system_results_t *pS = &(pdev->sys_results); + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint32_t utemp32a; + + LOG_FUNCTION_START(""); + + + + + utemp32a = + pS->result__peak_signal_count_rate_crosstalk_corrected_mcps_sd0 + + pS->result__ambient_count_rate_mcps_sd0; + + + if (utemp32a > 0xFFFF) + utemp32a = 0xFFFF; + + + + utemp32a = utemp32a << 16; + + + if (pdev->sys_results.result__dss_actual_effective_spads_sd0 == 0) + status = VL53L1_ERROR_DIVISION_BY_ZERO; + else { + + utemp32a = utemp32a / + pdev->sys_results.result__dss_actual_effective_spads_sd0; + + pdev->low_power_auto_data.dss__total_rate_per_spad_mcps = + utemp32a; + + + utemp32a = pdev->stat_cfg.dss_config__target_total_rate_mcps << + 16; + + + if (pdev->low_power_auto_data.dss__total_rate_per_spad_mcps + == 0) + status = VL53L1_ERROR_DIVISION_BY_ZERO; + else { + + utemp32a = utemp32a / + pdev->low_power_auto_data.dss__total_rate_per_spad_mcps; + + + if (utemp32a > 0xFFFF) + utemp32a = 0xFFFF; + + + pdev->low_power_auto_data.dss__required_spads = + (uint16_t)utemp32a; + + + pdev->gen_cfg.dss_config__manual_effective_spads_select + = pdev->low_power_auto_data.dss__required_spads; + pdev->gen_cfg.dss_config__roi_mode_control = + VL53L1_DEVICEDSSMODE__REQUESTED_EFFFECTIVE_SPADS; + } + + } + + if (status == VL53L1_ERROR_DIVISION_BY_ZERO) { + + + + pdev->low_power_auto_data.dss__required_spads = 0x8000; + + + pdev->gen_cfg.dss_config__manual_effective_spads_select = + pdev->low_power_auto_data.dss__required_spads; + pdev->gen_cfg.dss_config__roi_mode_control = + VL53L1_DEVICEDSSMODE__REQUESTED_EFFFECTIVE_SPADS; + + + status = VL53L1_ERROR_NONE; + } + + LOG_FUNCTION_END(status); + + return status; +} + + + + +VL53L1_Error VL53L1_compute_histo_merge_nb( + VL53L1_DEV Dev, uint8_t *histo_merge_nb) +{ + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t i, timing; + uint8_t sum = 0; + + timing = (pdev->hist_data.bin_seq[0] == 7 ? 1 : 0); + for (i = 0; i < VL53L1_BIN_REC_SIZE; i++) + if (pdev->multi_bins_rec[i][timing][7] > 0) + sum++; + *histo_merge_nb = sum; + + return status; +} + diff --git a/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_core_support.c b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_core_support.c new file mode 100644 index 000000000000..e50661913400 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_core_support.c @@ -0,0 +1,640 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#include "vl53l1_ll_def.h" +#include "vl53l1_ll_device.h" +#include "vl53l1_platform_log.h" +#include "vl53l1_core_support.h" +#include "vl53l1_platform_user_data.h" +#include "vl53l1_platform_user_defines.h" + + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_CORE, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_CORE, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_CORE, \ + status, fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_CORE, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + + +uint32_t VL53L1_calc_pll_period_us( + uint16_t fast_osc_frequency) +{ + + + uint32_t pll_period_us = 0; + + LOG_FUNCTION_START(""); + + if (fast_osc_frequency > 0) + pll_period_us = (0x01 << 30) / fast_osc_frequency; + + + + LOG_FUNCTION_END(0); + + return pll_period_us; +} + + +uint32_t VL53L1_duration_maths( + uint32_t pll_period_us, + uint32_t vcsel_parm_pclks, + uint32_t window_vclks, + uint32_t elapsed_mclks) +{ + + + uint64_t tmp_long_int = 0; + uint32_t duration_us = 0; + + + duration_us = window_vclks * pll_period_us; + + + duration_us = duration_us >> 12; + + + tmp_long_int = (uint64_t)duration_us; + + + duration_us = elapsed_mclks * vcsel_parm_pclks; + + + duration_us = duration_us >> 4; + + + tmp_long_int = tmp_long_int * (uint64_t)duration_us; + + + tmp_long_int = tmp_long_int >> 12; + + + if (tmp_long_int > 0xFFFFFFFF) + tmp_long_int = 0xFFFFFFFF; + + duration_us = (uint32_t)tmp_long_int; + + return duration_us; +} + + +uint32_t VL53L1_events_per_spad_maths( + int32_t VL53L1_p_013, + uint16_t num_spads, + uint32_t duration) +{ + uint64_t total_hist_counts = 0; + uint64_t xtalk_per_spad = 0; + uint32_t rate_per_spad_kcps = 0; + + + + + + + uint64_t dividend = ((uint64_t)VL53L1_p_013 + * 1000 * 256); + + if (num_spads != 0) + total_hist_counts = do_division_u( + dividend, (uint64_t)num_spads); + + + + if (duration > 0) { + + + uint64_t dividend = (((uint64_t)(total_hist_counts << 11)) + + ((uint64_t)duration / 2)); + + xtalk_per_spad = do_division_u(dividend, (uint64_t)duration); + } else { + xtalk_per_spad = (uint64_t)(total_hist_counts << 11); + } + + rate_per_spad_kcps = (uint32_t)xtalk_per_spad; + + return rate_per_spad_kcps; +} + + +uint32_t VL53L1_isqrt(uint32_t num) +{ + + + + uint32_t res = 0; + uint32_t bit = 1 << 30; + + + while (bit > num) + bit >>= 2; + + while (bit != 0) { + if (num >= res + bit) { + num -= res + bit; + res = (res >> 1) + bit; + } else { + res >>= 1; + } + bit >>= 2; + } + + return res; +} + + +void VL53L1_hist_calc_zero_distance_phase( + VL53L1_histogram_bin_data_t *pdata) +{ + + + uint32_t period = 0; + uint32_t VL53L1_p_017 = 0; + + LOG_FUNCTION_START(""); + + period = 2048 * + (uint32_t)VL53L1_decode_vcsel_period(pdata->VL53L1_p_009); + + VL53L1_p_017 = period; + VL53L1_p_017 += (uint32_t)pdata->phasecal_result__reference_phase; + VL53L1_p_017 += (2048 * (uint32_t)pdata->phasecal_result__vcsel_start); + VL53L1_p_017 -= (2048 * (uint32_t)pdata->cal_config__vcsel_start); + + if (period != 0) + VL53L1_p_017 = VL53L1_p_017 % period; + else + VL53L1_p_017 = 0; + + pdata->zero_distance_phase = (uint16_t)VL53L1_p_017; + + LOG_FUNCTION_END(0); +} + + +void VL53L1_hist_estimate_ambient_from_thresholded_bins( + int32_t ambient_threshold_sigma, + VL53L1_histogram_bin_data_t *pdata) +{ + + + uint8_t bin = 0; + int32_t VL53L1_p_032 = 0; + + LOG_FUNCTION_START(""); + + + + VL53L1_hist_find_min_max_bin_values(pdata); + + + + VL53L1_p_032 = + (int32_t)VL53L1_isqrt((uint32_t)pdata->min_bin_value); + VL53L1_p_032 *= ambient_threshold_sigma; + VL53L1_p_032 += 0x07; + VL53L1_p_032 = VL53L1_p_032 >> 4; + VL53L1_p_032 += pdata->min_bin_value; + + + + pdata->number_of_ambient_samples = 0; + pdata->ambient_events_sum = 0; + + for (bin = 0; bin < pdata->VL53L1_p_024; bin++) + if (pdata->bin_data[bin] < VL53L1_p_032) { + pdata->ambient_events_sum += pdata->bin_data[bin]; + pdata->number_of_ambient_samples++; + } + + + + if (pdata->number_of_ambient_samples > 0) { + pdata->VL53L1_p_004 = + pdata->ambient_events_sum; + pdata->VL53L1_p_004 += + ((int32_t)pdata->number_of_ambient_samples/2); + pdata->VL53L1_p_004 /= + (int32_t)pdata->number_of_ambient_samples; + } + + LOG_FUNCTION_END(0); +} + + +void VL53L1_hist_remove_ambient_bins( + VL53L1_histogram_bin_data_t *pdata) +{ + + + + uint8_t bin = 0; + uint8_t lc = 0; + uint8_t i = 0; + + + + if ((pdata->bin_seq[0] & 0x07) == 0x07) { + + i = 0; + for (lc = 0; lc < VL53L1_MAX_BIN_SEQUENCE_LENGTH; lc++) { + if ((pdata->bin_seq[lc] & 0x07) != 0x07) { + pdata->bin_seq[i] = pdata->bin_seq[lc]; + pdata->bin_rep[i] = pdata->bin_rep[lc]; + i++; + } + } + + + + for (lc = i; lc < VL53L1_MAX_BIN_SEQUENCE_LENGTH; lc++) { + pdata->bin_seq[lc] = VL53L1_MAX_BIN_SEQUENCE_CODE + 1; + pdata->bin_rep[lc] = 0; + } + } + + if (pdata->number_of_ambient_bins > 0) { + + + for (bin = pdata->number_of_ambient_bins; + bin < pdata->VL53L1_p_023; bin++) { + pdata->bin_data[bin-pdata->number_of_ambient_bins] = + pdata->bin_data[bin]; + } + + + pdata->VL53L1_p_024 = + pdata->VL53L1_p_024 - + pdata->number_of_ambient_bins; + pdata->number_of_ambient_bins = 0; + } +} + + +uint32_t VL53L1_calc_pll_period_mm( + uint16_t fast_osc_frequency) +{ + + + uint32_t pll_period_us = 0; + uint32_t pll_period_mm = 0; + + LOG_FUNCTION_START(""); + + + + pll_period_us = VL53L1_calc_pll_period_us(fast_osc_frequency); + + + + + pll_period_mm = + VL53L1_SPEED_OF_LIGHT_IN_AIR_DIV_8 * + (pll_period_us >> 2); + + + pll_period_mm = (pll_period_mm + (0x01<<15)) >> 16; + + LOG_FUNCTION_END(0); + + return pll_period_mm; +} + + +uint16_t VL53L1_rate_maths( + int32_t VL53L1_p_008, + uint32_t time_us) +{ + + + uint32_t tmp_int = 0; + uint32_t frac_bits = 7; + uint16_t rate_mcps = 0; + + + + if (VL53L1_p_008 > VL53L1_SPAD_TOTAL_COUNT_MAX) + tmp_int = VL53L1_SPAD_TOTAL_COUNT_MAX; + else if (VL53L1_p_008 > 0) + tmp_int = (uint32_t)VL53L1_p_008; + + + + + if (VL53L1_p_008 > VL53L1_SPAD_TOTAL_COUNT_RES_THRES) + frac_bits = 3; + else + frac_bits = 7; + + + if (time_us > 0) + tmp_int = ((tmp_int << frac_bits) + (time_us / 2)) / time_us; + + + if (VL53L1_p_008 > VL53L1_SPAD_TOTAL_COUNT_RES_THRES) + tmp_int = tmp_int << 4; + + + + if (tmp_int > 0xFFFF) + tmp_int = 0xFFFF; + + rate_mcps = (uint16_t)tmp_int; + + return rate_mcps; +} + + +uint16_t VL53L1_rate_per_spad_maths( + uint32_t frac_bits, + uint32_t peak_count_rate, + uint16_t num_spads, + uint32_t max_output_value) +{ + + uint32_t tmp_int = 0; + + + uint16_t rate_per_spad = 0; + + + + + + if (num_spads > 0) { + tmp_int = (peak_count_rate << 8) << frac_bits; + tmp_int = (tmp_int + + ((uint32_t)num_spads / 2)) / + (uint32_t)num_spads; + } else { + tmp_int = ((peak_count_rate) << frac_bits); + } + + + + if (tmp_int > max_output_value) + tmp_int = max_output_value; + + rate_per_spad = (uint16_t)tmp_int; + + return rate_per_spad; +} + + +int32_t VL53L1_range_maths( + uint16_t fast_osc_frequency, + uint16_t VL53L1_p_017, + uint16_t zero_distance_phase, + uint8_t fractional_bits, + int32_t gain_factor, + int32_t range_offset_mm) +{ + + + uint32_t pll_period_us = 0; + int64_t tmp_long_int = 0; + int32_t range_mm = 0; + int32_t range_mm_10 = 0; + + + + pll_period_us = VL53L1_calc_pll_period_us(fast_osc_frequency); + + + + tmp_long_int = (int64_t)VL53L1_p_017 - (int64_t)zero_distance_phase; + + + + tmp_long_int = tmp_long_int * (int64_t)pll_period_us; + + + + tmp_long_int = tmp_long_int / (0x01 << 9); + + + + tmp_long_int = tmp_long_int * VL53L1_SPEED_OF_LIGHT_IN_AIR_DIV_8; + + + + tmp_long_int = tmp_long_int / (0x01 << 22); + + + range_mm = (int32_t)tmp_long_int + range_offset_mm; + + + range_mm *= gain_factor; + range_mm += 0x0400; + range_mm /= 0x0800; + + + if (fractional_bits == 0) { + range_mm_10 = range_mm * 10; + range_mm_10 = range_mm_10 / (0x01 << 2); + if ((range_mm_10 % 10) < 5) + range_mm = (int16_t)(range_mm_10 / 10); + else + range_mm = (int16_t)(range_mm_10 / 10 + 1); + } else if (fractional_bits == 1) + range_mm = range_mm / (0x01 << 1); + + return range_mm; +} + + +uint8_t VL53L1_decode_vcsel_period(uint8_t vcsel_period_reg) +{ + + + uint8_t VL53L1_p_031 = 0; + + VL53L1_p_031 = (vcsel_period_reg + 1) << 1; + + return VL53L1_p_031; +} + + +void VL53L1_copy_xtalk_bin_data_to_histogram_data_struct( + VL53L1_xtalk_histogram_shape_t *pxtalk, + VL53L1_histogram_bin_data_t *phist) +{ + + + phist->cal_config__vcsel_start = + pxtalk->cal_config__vcsel_start; + phist->VL53L1_p_019 = + pxtalk->VL53L1_p_019; + phist->VL53L1_p_022 = + pxtalk->VL53L1_p_022; + + phist->phasecal_result__reference_phase = + pxtalk->phasecal_result__reference_phase; + phist->phasecal_result__vcsel_start = + pxtalk->phasecal_result__vcsel_start; + + phist->vcsel_width = + pxtalk->vcsel_width; + phist->zero_distance_phase = + pxtalk->zero_distance_phase; + + phist->zone_id = pxtalk->zone_id; + phist->VL53L1_p_023 = pxtalk->VL53L1_p_023; + phist->time_stamp = pxtalk->time_stamp; +} + + +void VL53L1_init_histogram_bin_data_struct( + int32_t bin_value, + uint16_t VL53L1_p_024, + VL53L1_histogram_bin_data_t *pdata) +{ + + + + uint16_t i = 0; + + pdata->cfg_device_state = VL53L1_DEVICESTATE_SW_STANDBY; + pdata->rd_device_state = VL53L1_DEVICESTATE_SW_STANDBY; + + pdata->zone_id = 0; + pdata->time_stamp = 0; + + pdata->VL53L1_p_022 = 0; + pdata->VL53L1_p_023 = VL53L1_HISTOGRAM_BUFFER_SIZE; + pdata->VL53L1_p_024 = (uint8_t)VL53L1_p_024; + pdata->number_of_ambient_bins = 0; + + pdata->result__interrupt_status = 0; + pdata->result__range_status = 0; + pdata->result__report_status = 0; + pdata->result__stream_count = 0; + + pdata->result__dss_actual_effective_spads = 0; + pdata->phasecal_result__reference_phase = 0; + pdata->phasecal_result__vcsel_start = 0; + pdata->cal_config__vcsel_start = 0; + + pdata->vcsel_width = 0; + pdata->VL53L1_p_009 = 0; + pdata->VL53L1_p_019 = 0; + pdata->total_periods_elapsed = 0; + + pdata->min_bin_value = 0; + pdata->max_bin_value = 0; + + pdata->zero_distance_phase = 0; + pdata->number_of_ambient_samples = 0; + pdata->ambient_events_sum = 0; + pdata->VL53L1_p_004 = 0; + + for (i = 0; i < VL53L1_MAX_BIN_SEQUENCE_LENGTH; i++) + pdata->bin_seq[i] = (uint8_t)i; + + for (i = 0; i < VL53L1_MAX_BIN_SEQUENCE_LENGTH; i++) + pdata->bin_rep[i] = 1; + + + for (i = 0; i < VL53L1_HISTOGRAM_BUFFER_SIZE; i++) + if (i < VL53L1_p_024) + pdata->bin_data[i] = bin_value; + else + pdata->bin_data[i] = 0; + + +} + + +void VL53L1_decode_row_col( + uint8_t spad_number, + uint8_t *prow, + uint8_t *pcol) +{ + + + + if (spad_number > 127) { + *prow = 8 + ((255-spad_number) & 0x07); + *pcol = (spad_number-128) >> 3; + } else { + *prow = spad_number & 0x07; + *pcol = (127-spad_number) >> 3; + } +} + + +void VL53L1_hist_find_min_max_bin_values( + VL53L1_histogram_bin_data_t *pdata) +{ + + + uint8_t bin = 0; + + LOG_FUNCTION_START(""); + + for (bin = 0; bin < pdata->VL53L1_p_024; bin++) { + + if (bin == 0 || pdata->min_bin_value >= pdata->bin_data[bin]) + pdata->min_bin_value = pdata->bin_data[bin]; + + if (bin == 0 || pdata->max_bin_value <= pdata->bin_data[bin]) + pdata->max_bin_value = pdata->bin_data[bin]; + + } + + LOG_FUNCTION_END(0); + +} + + +void VL53L1_hist_estimate_ambient_from_ambient_bins( + VL53L1_histogram_bin_data_t *pdata) +{ + + + uint8_t bin = 0; + + LOG_FUNCTION_START(""); + + if (pdata->number_of_ambient_bins > 0) { + + pdata->number_of_ambient_samples = + pdata->number_of_ambient_bins; + + + + pdata->ambient_events_sum = 0; + for (bin = 0; bin < pdata->number_of_ambient_bins; bin++) + pdata->ambient_events_sum += pdata->bin_data[bin]; + + pdata->VL53L1_p_004 = pdata->ambient_events_sum; + pdata->VL53L1_p_004 += + ((int32_t)pdata->number_of_ambient_bins / 2); + pdata->VL53L1_p_004 /= + (int32_t)pdata->number_of_ambient_bins; + + } + + LOG_FUNCTION_END(0); +} + + diff --git a/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_error_strings.c b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_error_strings.c new file mode 100644 index 000000000000..723e549266d6 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_error_strings.c @@ -0,0 +1,248 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#include "vl53l1_error_codes.h" +#include "vl53l1_error_strings.h" +#include "vl53l1_platform_log.h" +#include "vl53l1_ll_def.h" + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_API, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_API, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_API, \ + status, fmt, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_get_pal_error_string( + VL53L1_Error PalErrorCode, + char *pPalErrorString) +{ + VL53L1_Error Status = VL53L1_ERROR_NONE; + +#ifdef VL53L1_USE_EMPTY_STRING + SUPPRESS_UNUSED_WARNING(PalErrorCode); +#endif + + LOG_FUNCTION_START(""); + +#ifdef VL53L1_USE_EMPTY_STRING + VL53L1_COPYSTRING(pPalErrorString, ""); +#else + + switch (PalErrorCode) { + case VL53L1_ERROR_NONE: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_NONE); + break; + case VL53L1_ERROR_CALIBRATION_WARNING: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_CALIBRATION_WARNING); + break; + case VL53L1_ERROR_MIN_CLIPPED: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_MIN_CLIPPED); + break; + case VL53L1_ERROR_UNDEFINED: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_UNDEFINED); + break; + case VL53L1_ERROR_INVALID_PARAMS: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_INVALID_PARAMS); + break; + case VL53L1_ERROR_NOT_SUPPORTED: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_NOT_SUPPORTED); + break; + case VL53L1_ERROR_RANGE_ERROR: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_RANGE_ERROR); + break; + case VL53L1_ERROR_TIME_OUT: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_TIME_OUT); + break; + case VL53L1_ERROR_MODE_NOT_SUPPORTED: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_MODE_NOT_SUPPORTED); + break; + case VL53L1_ERROR_BUFFER_TOO_SMALL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_BUFFER_TOO_SMALL); + break; + case VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_COMMS_BUFFER_TOO_SMALL); + break; + case VL53L1_ERROR_GPIO_NOT_EXISTING: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_GPIO_NOT_EXISTING); + break; + case VL53L1_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_GPIO_FUNCTIONALITY_NOT_SUPPORTED); + break; + case VL53L1_ERROR_CONTROL_INTERFACE: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_CONTROL_INTERFACE); + break; + case VL53L1_ERROR_INVALID_COMMAND: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_INVALID_COMMAND); + break; + case VL53L1_ERROR_DIVISION_BY_ZERO: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_DIVISION_BY_ZERO); + break; + case VL53L1_ERROR_REF_SPAD_INIT: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_REF_SPAD_INIT); + break; + case VL53L1_ERROR_GPH_SYNC_CHECK_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_GPH_SYNC_CHECK_FAIL); + break; + case VL53L1_ERROR_STREAM_COUNT_CHECK_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_STREAM_COUNT_CHECK_FAIL); + break; + case VL53L1_ERROR_GPH_ID_CHECK_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_GPH_ID_CHECK_FAIL); + break; + case VL53L1_ERROR_ZONE_STREAM_COUNT_CHECK_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_ZONE_STREAM_COUNT_CHECK_FAIL); + break; + case VL53L1_ERROR_ZONE_GPH_ID_CHECK_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_ZONE_GPH_ID_CHECK_FAIL); + break; + + case VL53L1_ERROR_XTALK_EXTRACTION_NO_SAMPLE_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_XTALK_EXTRACTION_NO_SAMPLES_FAIL); + break; + case VL53L1_ERROR_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_XTALK_EXTRACTION_SIGMA_LIMIT_FAIL); + break; + + case VL53L1_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_OFFSET_CAL_NO_SAMPLE_FAIL); + break; + case VL53L1_ERROR_OFFSET_CAL_NO_SPADS_ENABLED_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_OFFSET_CAL_NO_SPADS_ENABLED_FAIL); + break; + case VL53L1_ERROR_ZONE_CAL_NO_SAMPLE_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_ZONE_CAL_NO_SAMPLE_FAIL); + break; + + case VL53L1_WARNING_OFFSET_CAL_MISSING_SAMPLES: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_OFFSET_CAL_MISSING_SAMPLES); + break; + case VL53L1_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_OFFSET_CAL_SIGMA_TOO_HIGH); + break; + case VL53L1_WARNING_OFFSET_CAL_RATE_TOO_HIGH: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_OFFSET_CAL_RATE_TOO_HIGH); + break; + case VL53L1_WARNING_OFFSET_CAL_SPAD_COUNT_TOO_LOW: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_OFFSET_CAL_SPAD_COUNT_TOO_LOW); + break; + + case VL53L1_WARNING_ZONE_CAL_MISSING_SAMPLES: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_ZONE_CAL_MISSING_SAMPLES); + break; + case VL53L1_WARNING_ZONE_CAL_SIGMA_TOO_HIGH: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_ZONE_CAL_SIGMA_TOO_HIGH); + break; + case VL53L1_WARNING_ZONE_CAL_RATE_TOO_HIGH: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_ZONE_CAL_RATE_TOO_HIGH); + break; + + case VL53L1_WARNING_REF_SPAD_CHAR_NOT_ENOUGH_SPADS: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_REF_SPAD_CHAR_NOT_ENOUGH_SPADS); + break; + case VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_HIGH: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_REF_SPAD_CHAR_RATE_TOO_HIGH); + break; + case VL53L1_WARNING_REF_SPAD_CHAR_RATE_TOO_LOW: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_REF_SPAD_CHAR_RATE_TOO_LOW); + break; + + case VL53L1_WARNING_XTALK_MISSING_SAMPLES: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_XTALK_MISSING_SAMPLES); + break; + case VL53L1_WARNING_XTALK_NO_SAMPLES_FOR_GRADIENT: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_XTALK_NO_SAMPLES_FOR_GRADIENT); + break; + case VL53L1_WARNING_XTALK_SIGMA_LIMIT_FOR_GRADIENT: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_WARNING_XTALK_SIGMA_LIMIT_FOR_GRADIENT); + break; + + case VL53L1_ERROR_DEVICE_FIRMWARE_TOO_OLD: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_DEVICE_FIRMWARE_TOO_OLD); + break; + case VL53L1_ERROR_DEVICE_FIRMWARE_TOO_NEW: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_DEVICE_FIRMWARE_TOO_NEW); + break; + case VL53L1_ERROR_UNIT_TEST_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_UNIT_TEST_FAIL); + break; + case VL53L1_ERROR_FILE_READ_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_FILE_READ_FAIL); + break; + case VL53L1_ERROR_FILE_WRITE_FAIL: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_FILE_WRITE_FAIL); + break; + case VL53L1_ERROR_NOT_IMPLEMENTED: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_ERROR_NOT_IMPLEMENTED); + break; + default: + VL53L1_COPYSTRING(pPalErrorString, + VL53L1_STRING_UNKNOW_ERROR_CODE); + } + +#endif + + LOG_FUNCTION_END(Status); + + return Status; +} + diff --git a/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_hist_char.c b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_hist_char.c new file mode 100644 index 000000000000..208868bbf9ba --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_hist_char.c @@ -0,0 +1,162 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + + +#include +#include + + + +#include "vl53l1_core.h" +#include "vl53l1_register_settings.h" +#include "vl53l1_hist_char.h" + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_HISTOGRAM, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_HISTOGRAM, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_HISTOGRAM,\ + status, fmt, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_set_calib_config( + VL53L1_DEV Dev, + uint8_t vcsel_delay__a0, + uint8_t calib_1, + uint8_t calib_2, + uint8_t calib_3, + uint8_t calib_2__a0, + uint8_t spad_readout) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[3]; + + LOG_FUNCTION_START(""); + + + + status = VL53L1_enable_powerforce(Dev); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + + + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__VCSEL_DELAY__A0, + vcsel_delay__a0); + } + + + + if (status == VL53L1_ERROR_NONE) { + + + comms_buffer[0] = calib_1; + comms_buffer[1] = calib_2; + comms_buffer[2] = calib_3; + + status = VL53L1_WriteMulti( + Dev, + VL53L1_RANGING_CORE__CALIB_1, + comms_buffer, + 3); + } + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__CALIB_2__A0, + calib_2__a0); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__SPAD_READOUT, + spad_readout); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + + +VL53L1_Error VL53L1_set_hist_calib_pulse_delay( + VL53L1_DEV Dev, + uint8_t calib_delay) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + status = + VL53L1_set_calib_config( + Dev, + 0x01, + calib_delay, + 0x04, + 0x08, + 0x14, + VL53L1_RANGING_CORE__SPAD_READOUT__CALIB_PULSES); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_disable_calib_pulse_delay( + VL53L1_DEV Dev) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + status = + VL53L1_set_calib_config( + Dev, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + VL53L1_RANGING_CORE__SPAD_READOUT__STANDARD); + + LOG_FUNCTION_END(status); + + return status; +} + + diff --git a/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_nvm.c b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_nvm.c new file mode 100644 index 000000000000..c3c8f6e2830a --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_nvm.c @@ -0,0 +1,1540 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + + +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + + + +#include "vl53l1_ll_def.h" +#include "vl53l1_platform.h" +#include "vl53l1_platform_log.h" +#include "vl53l1_register_map.h" +#include "vl53l1_core.h" +#include "vl53l1_nvm_structs.h" +#include "vl53l1_nvm_map.h" +#include "vl53l1_nvm.h" + + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_NVM, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_NVM, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_NVM,\ + status, fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(VL53L1_TRACE_MODULE_NVM, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_nvm_enable( + VL53L1_DEV Dev, + uint16_t nvm_ctrl_pulse_width, + int32_t nvm_power_up_delay_us) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_powerforce(Dev); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WaitUs( + Dev, + VL53L1_ENABLE_POWERFORCE_SETTLING_TIME_US); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__NVM_CTRL__PDN, + 0x01); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__CLK_CTRL1, + 0x05); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WaitUs( + Dev, + nvm_power_up_delay_us); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__NVM_CTRL__MODE, + 0x01); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrWord( + Dev, + VL53L1_RANGING_CORE__NVM_CTRL__PULSE_WIDTH_MSB, + nvm_ctrl_pulse_width); + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_nvm_read( + VL53L1_DEV Dev, + uint8_t start_address, + uint8_t count, + uint8_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t nvm_addr = 0; + + LOG_FUNCTION_START(""); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%-12s = 0x%02X (%3u)\n", + "nvm_addr", nvm_addr, nvm_addr); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%-12s = 0x%02X (%3u)\n", + "count", count, count); + + for (nvm_addr = start_address; + nvm_addr < (start_address+count) ; nvm_addr++) { + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__NVM_CTRL__ADDR, + nvm_addr); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__NVM_CTRL__READN, + 0x00); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WaitUs( + Dev, + VL53L1_NVM_READ_TRIGGER_DELAY_US); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__NVM_CTRL__READN, + 0x01); + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_RANGING_CORE__NVM_CTRL__DATAOUT_MMM, + pdata, + 4); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "NVM address : 0x%02X = 0x%02X%02X%02X%02X\n", + nvm_addr, *pdata, *(pdata+1), *(pdata+2), *(pdata+3)); + + + + pdata = pdata + 4; + + + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_nvm_disable( + VL53L1_DEV Dev) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__NVM_CTRL__READN, + 0x01); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WrByte( + Dev, + VL53L1_RANGING_CORE__NVM_CTRL__PDN, + 0x00); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_powerforce(Dev); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_nvm_format_decode( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_decoded_nvm_data_t *pdata) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i = 0; + uint8_t *ptmp = NULL; + int pptmp[VL53L1_NVM_MAX_FMT_RANGE_DATA]; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_NVM_SIZE_IN_BYTES) + return VL53L1_ERROR_BUFFER_TOO_SMALL; + + pdata->nvm__identification_model_id = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__IDENTIFICATION__MODEL_ID, + 0x000000FF, + 0, + 0); + pdata->nvm__identification_module_type = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__IDENTIFICATION__MODULE_TYPE, + 0x000000FF, + 0, + 0); + pdata->nvm__identification_revision_id = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__IDENTIFICATION__REVISION_ID, + 0x0000000F, + 0, + 0); + pdata->nvm__identification_module_id = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__IDENTIFICATION__MODULE_ID, + 0x0000FFFF, + 0, + 0); + pdata->nvm__i2c_valid = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__I2C_VALID, + 0x000000FF, + 0, + 0); + pdata->nvm__i2c_device_address_ews = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__I2C_SLAVE__DEVICE_ADDRESS, + 0x000000FF, + 0, + 0); + pdata->nvm__ews__fast_osc_frequency = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__EWS__OSC_MEASURED__FAST_OSC_FREQUENCY, + 0x0000FFFF, + 0, + 0); + pdata->nvm__ews__fast_osc_trim_max = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__FAST_OSC_TRIM_MAX, + 0x0000007F, + 0, + 0); + pdata->nvm__ews__fast_osc_freq_set = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__FAST_OSC_FREQ_SET, + 0x00000007, + 0, + 0); + pdata->nvm__ews__slow_osc_calibration = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__EWS__SLOW_OSC_CALIBRATION, + 0x000003FF, + 0, + 0); + pdata->nvm__fmt__fast_osc_frequency = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__FMT__OSC_MEASURED__FAST_OSC_FREQUENCY, + 0x0000FFFF, + 0, + 0); + pdata->nvm__fmt__fast_osc_trim_max = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FAST_OSC_TRIM_MAX, + 0x0000007F, + 0, + 0); + pdata->nvm__fmt__fast_osc_freq_set = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FAST_OSC_FREQ_SET, + 0x00000007, + 0, + 0); + pdata->nvm__fmt__slow_osc_calibration = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__FMT__SLOW_OSC_CALIBRATION, + 0x000003FF, + 0, + 0); + pdata->nvm__vhv_config_unlock = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__VHV_CONFIG_UNLOCK, + 0x000000FF, + 0, + 0); + pdata->nvm__ref_selvddpix = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__REF_SELVDDPIX, + 0x0000000F, + 0, + 0); + pdata->nvm__ref_selvquench = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__REF_SELVQUENCH, + 0x00000078, + 3, + 0); + pdata->nvm__regavdd1v2_sel = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__REGAVDD1V2_SEL_REGDVDD1V2_SEL, + 0x0000000C, + 2, + 0); + pdata->nvm__regdvdd1v2_sel = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__REGAVDD1V2_SEL_REGDVDD1V2_SEL, + 0x00000003, + 0, + 0); + pdata->nvm__vhv_timeout__macrop = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + + VL53L1_NVM__VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND, + 0x00000003, + 0, + 0); + pdata->nvm__vhv_loop_bound = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + + VL53L1_NVM__VHV_CONFIG__TIMEOUT_MACROP_LOOP_BOUND, + 0x000000FC, + 2, + 0); + pdata->nvm__vhv_count_threshold = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__VHV_CONFIG__COUNT_THRESH, + 0x000000FF, + 0, + 0); + pdata->nvm__vhv_offset = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__VHV_CONFIG__OFFSET, + 0x0000003F, + 0, + 0); + pdata->nvm__vhv_init_enable = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__VHV_CONFIG__INIT, + 0x00000080, + 7, + 0); + pdata->nvm__vhv_init_value = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__VHV_CONFIG__INIT, + 0x0000003F, + 0, + 0); + pdata->nvm__laser_safety_vcsel_trim_ll = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__VCSEL_TRIM_LL, + 0x00000007, + 0, + 0); + pdata->nvm__laser_safety_vcsel_selion_ll = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__VCSEL_SELION_LL, + 0x0000003F, + 0, + 0); + pdata->nvm__laser_safety_vcsel_selion_max_ll = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__VCSEL_SELION_MAX_LL, + 0x0000003F, + 0, + 0); + pdata->nvm__laser_safety_mult_ll = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__MULT_LL, + 0x0000003F, + 0, + 0); + pdata->nvm__laser_safety_clip_ll = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__CLIP_LL, + 0x0000003F, + 0, + 0); + pdata->nvm__laser_safety_vcsel_trim_ld = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__VCSEL_TRIM_LD, + 0x00000007, + 0, + 0); + pdata->nvm__laser_safety_vcsel_selion_ld = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__VCSEL_SELION_LD, + 0x0000003F, + 0, + 0); + pdata->nvm__laser_safety_vcsel_selion_max_ld = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__VCSEL_SELION_MAX_LD, + 0x0000003F, + 0, + 0); + pdata->nvm__laser_safety_mult_ld = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__MULT_LD, + 0x0000003F, + 0, + 0); + pdata->nvm__laser_safety_clip_ld = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY__CLIP_LD, + 0x0000003F, + 0, + 0); + pdata->nvm__laser_safety_lock_byte = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY_LOCK_BYTE, + 0x000000FF, + 0, + 0); + pdata->nvm__laser_safety_unlock_byte = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__LASER_SAFETY_UNLOCK_BYTE, + 0x000000FF, + 0, + 0); + + + + ptmp = pbuffer + VL53L1_NVM__EWS__SPAD_ENABLES_RTN_0_; + for (i = 0 ; i < VL53L1_RTN_SPAD_BUFFER_SIZE ; i++) + pdata->nvm__ews__spad_enables_rtn[i] = *ptmp++; + + ptmp = pbuffer + VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC1_0_; + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) + pdata->nvm__ews__spad_enables_ref__loc1[i] = *ptmp++; + + ptmp = pbuffer + VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC2_0_; + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) + pdata->nvm__ews__spad_enables_ref__loc2[i] = *ptmp++; + + ptmp = pbuffer + VL53L1_NVM__EWS__SPAD_ENABLES_REF__LOC3_0_; + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) + pdata->nvm__ews__spad_enables_ref__loc3[i] = *ptmp++; + + + + ptmp = pbuffer + VL53L1_NVM__FMT__SPAD_ENABLES_RTN_0_; + for (i = 0 ; i < VL53L1_RTN_SPAD_BUFFER_SIZE ; i++) + pdata->nvm__fmt__spad_enables_rtn[i] = *ptmp++; + + ptmp = pbuffer + VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC1_0_; + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) + pdata->nvm__fmt__spad_enables_ref__loc1[i] = *ptmp++; + + ptmp = pbuffer + VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC2_0_; + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) + pdata->nvm__fmt__spad_enables_ref__loc2[i] = *ptmp++; + + ptmp = pbuffer + VL53L1_NVM__FMT__SPAD_ENABLES_REF__LOC3_0_; + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) + pdata->nvm__fmt__spad_enables_ref__loc3[i] = *ptmp++; + + + pdata->nvm__fmt__roi_config__mode_roi_centre_spad = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + + VL53L1_NVM__FMT__ROI_CONFIG__MODE_ROI_CENTRE_SPAD, + 0x000000FF, + 0, + 0); + pdata->nvm__fmt__roi_config__mode_roi_x_size = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + + VL53L1_NVM__FMT__ROI_CONFIG__MODE_ROI_XY_SIZE, + 0x000000F0, + 4, + 0); + pdata->nvm__fmt__roi_config__mode_roi_y_size = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__ROI_CONFIG__MODE_ROI_XY_SIZE, + 0x0000000F, + 0, + 0); + pdata->nvm__fmt__ref_spad_apply__num_requested_ref_spad = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + + VL53L1_NVM__FMT__REF_SPAD_APPLY__NUM_REQUESTED_REF_SPAD, + 0x000000FF, + 0, + 0); + pdata->nvm__fmt__ref_spad_man__ref_location = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__REF_SPAD_MAN__REF_LOCATION, + 0x00000003, + 0, + 0); + pdata->nvm__fmt__mm_config__inner_offset_mm = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__FMT__MM_CONFIG__INNER_OFFSET_MM, + 0x0000FFFF, + 0, + 0); + pdata->nvm__fmt__mm_config__outer_offset_mm = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__FMT__MM_CONFIG__OUTER_OFFSET_MM, + 0x0000FFFF, + 0, + 0); + pdata->nvm__fmt__algo_part_to_part_range_offset_mm = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__FMT__ALGO__PART_TO_PART_RANGE_OFFSET_MM, + 0x00000FFF, + 0, + 0); + pdata->nvm__fmt__algo__crosstalk_compensation_plane_offset_kcps = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__FMT__ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS, + 0x0000FFFF, + 0, + 0); + pdata->nvm__fmt__algo__crosstalk_compensation_x_plane_gradient_kcps = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__FMT__ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS, + 0x0000FFFF, + 0, + 0); + pdata->nvm__fmt__algo__crosstalk_compensation_y_plane_gradient_kcps = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__FMT__ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS, + 0x0000FFFF, + 0, + 0); + pdata->nvm__fmt__spare__host_config__nvm_config_spare_0 = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + + VL53L1_NVM__FMT__SPARE_HOST_CONFIG__NVM_CONFIG_SPARE_0, + 0x000000FF, + 0, + 0); + pdata->nvm__fmt__spare__host_config__nvm_config_spare_1 = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + + VL53L1_NVM__FMT__SPARE_HOST_CONFIG__NVM_CONFIG_SPARE_1, + 0x000000FF, + 0, + 0); + pdata->nvm__customer_space_programmed = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__CUSTOMER_NVM_SPACE_PROGRAMMED, + 0x000000FF, + 0, + 0); + pdata->nvm__cust__i2c_device_address = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__CUST__I2C_SLAVE__DEVICE_ADDRESS, + 0x000000FF, + 0, + 0); + pdata->nvm__cust__ref_spad_apply__num_requested_ref_spad = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + + VL53L1_NVM__CUST__REF_SPAD_APPLY__NUM_REQUESTED_REF_SPAD, + 0x000000FF, + 0, + 0); + pdata->nvm__cust__ref_spad_man__ref_location = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__CUST__REF_SPAD_MAN__REF_LOCATION, + 0x00000003, + 0, + 0); + pdata->nvm__cust__mm_config__inner_offset_mm = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__CUST__MM_CONFIG__INNER_OFFSET_MM, + 0x0000FFFF, + 0, + 0); + pdata->nvm__cust__mm_config__outer_offset_mm = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__CUST__MM_CONFIG__OUTER_OFFSET_MM, + 0x0000FFFF, + 0, + 0); + pdata->nvm__cust__algo_part_to_part_range_offset_mm = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__CUST__ALGO__PART_TO_PART_RANGE_OFFSET_MM, + 0x00000FFF, + 0, + 0); + pdata->nvm__cust__algo__crosstalk_compensation_plane_offset_kcps = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__CUST__ALGO__CROSSTALK_COMPENSATION_PLANE_OFFSET_KCPS, + 0x0000FFFF, + 0, + 0); + pdata->nvm__cust__algo__crosstalk_compensation_x_plane_gradient_kcps = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__CUST__ALGO__CROSSTALK_COMPENSATION_X_PLANE_GRADIENT_KCPS, + 0x0000FFFF, + 0, + 0); + pdata->nvm__cust__algo__crosstalk_compensation_y_plane_gradient_kcps = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + + VL53L1_NVM__CUST__ALGO__CROSSTALK_COMPENSATION_Y_PLANE_GRADIENT_KCPS, + 0x0000FFFF, + 0, + 0); + pdata->nvm__cust__spare__host_config__nvm_config_spare_0 = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__CUST__SPARE_HOST_CONFIG__NVM_CONFIG_SPARE_0, + 0x000000FF, + 0, + 0); + pdata->nvm__cust__spare__host_config__nvm_config_spare_1 = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + + VL53L1_NVM__CUST__SPARE_HOST_CONFIG__NVM_CONFIG_SPARE_1, + 0x000000FF, + 0, + 0); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_nvm_decode_optical_centre( + buf_size, + pbuffer + VL53L1_NVM__FMT__OPTICAL_CENTRE_DATA_INDEX, + &(pdata->fmt_optical_centre)); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_nvm_decode_cal_peak_rate_map( + buf_size, + pbuffer + VL53L1_NVM__FMT__CAL_PEAK_RATE_MAP_DATA_INDEX, + &(pdata->fmt_peak_rate_map)); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_nvm_decode_additional_offset_cal_data( + buf_size, + pbuffer + + VL53L1_NVM__FMT__ADDITIONAL_OFFSET_CAL_DATA_INDEX, + &(pdata->fmt_add_offset_data)); + + + + pptmp[0] = VL53L1_NVM__FMT__RANGE_RESULTS__140MM_MM_PRE_RANGE; + pptmp[1] = VL53L1_NVM__FMT__RANGE_RESULTS__140MM_DARK; + pptmp[2] = VL53L1_NVM__FMT__RANGE_RESULTS__400MM_DARK; + pptmp[3] = VL53L1_NVM__FMT__RANGE_RESULTS__400MM_AMBIENT; + + for (i = 0 ; i < VL53L1_NVM_MAX_FMT_RANGE_DATA ; i++) { + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_nvm_decode_fmt_range_results_data( + buf_size, + pbuffer + pptmp[i], + &(pdata->fmt_range_data[i])); + } + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_nvm_decode_fmt_info( + buf_size, + pbuffer, + &(pdata->fmt_info)); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_nvm_decode_ews_info( + buf_size, + pbuffer, + &(pdata->ews_info)); + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_nvm_decode_optical_centre( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_optical_centre_t *pdata) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint16_t tmp = 0; + + if (buf_size < VL53L1_NVM__FMT__OPTICAL_CENTRE_DATA_SIZE) + return VL53L1_ERROR_BUFFER_TOO_SMALL; + + + tmp = 0x0100; + tmp -= (uint16_t)*(pbuffer + 2); + if (tmp > 0x0FF) + tmp = 0; + + pdata->x_centre = (uint8_t)tmp; + pdata->y_centre = *(pbuffer + 3); + + return status; +} + + +VL53L1_Error VL53L1_nvm_decode_cal_peak_rate_map( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_cal_peak_rate_map_t *pdata) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t *ptmp = NULL; + uint8_t i = 0; + + if (buf_size < VL53L1_NVM__FMT__CAL_PEAK_RATE_MAP_DATA_SIZE) + return VL53L1_ERROR_BUFFER_TOO_SMALL; + + pdata->cal_distance_mm = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer); + + pdata->cal_reflectance_pc = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 2); + pdata->cal_reflectance_pc = + pdata->cal_reflectance_pc >> 6; + + pdata->max_samples = VL53L1_NVM_PEAK_RATE_MAP_SAMPLES; + pdata->width = VL53L1_NVM_PEAK_RATE_MAP_WIDTH; + pdata->height = VL53L1_NVM_PEAK_RATE_MAP_HEIGHT; + + ptmp = pbuffer + 4; + for (i = 0 ; i < VL53L1_NVM_PEAK_RATE_MAP_SAMPLES ; i++) { + pdata->peak_rate_mcps[i] = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, ptmp); + ptmp += 2; + } + + return status; +} + + +VL53L1_Error VL53L1_nvm_decode_additional_offset_cal_data( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_additional_offset_cal_data_t *pdata) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + if (buf_size < VL53L1_NVM__FMT__ADDITIONAL_OFFSET_CAL_DATA_SIZE) + return VL53L1_ERROR_BUFFER_TOO_SMALL; + + pdata->result__mm_inner_actual_effective_spads = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer); + + pdata->result__mm_outer_actual_effective_spads = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 2); + + pdata->result__mm_inner_peak_signal_count_rtn_mcps = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 4); + + pdata->result__mm_outer_peak_signal_count_rtn_mcps = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 6); + + return status; +} + + +VL53L1_Error VL53L1_nvm_decode_fmt_range_results_data( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_decoded_nvm_fmt_range_data_t *pdata) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + if (buf_size < VL53L1_NVM__FMT__RANGE_RESULTS__SIZE_BYTES) + return VL53L1_ERROR_BUFFER_TOO_SMALL; + + pdata->result__actual_effective_rtn_spads = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer); + + pdata->ref_spad_array__num_requested_ref_spads = + *(pbuffer+2); + + pdata->ref_spad_array__ref_location = + *(pbuffer+3); + + pdata->result__peak_signal_count_rate_rtn_mcps = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 4); + + pdata->result__ambient_count_rate_rtn_mcps = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 6); + + pdata->result__peak_signal_count_rate_ref_mcps = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 8); + + pdata->result__ambient_count_rate_ref_mcps = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 10); + + pdata->measured_distance_mm = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 12); + + pdata->measured_distance_stdev_mm = + (uint16_t)VL53L1_i2c_decode_uint16_t(2, pbuffer + 14); + + return status; +} + + +VL53L1_Error VL53L1_nvm_decode_fmt_info( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_decoded_nvm_fmt_info_t *pdata) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + if (buf_size < VL53L1_NVM_SIZE_IN_BYTES) + return VL53L1_ERROR_BUFFER_TOO_SMALL; + + pdata->nvm__fmt__fgc[0] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_0, + 0x000000FE, + 1, + 0); + pdata->nvm__fmt__fgc[1] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_1, + 0x000001FC, + 2, + 0); + pdata->nvm__fmt__fgc[2] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_2 - 1, + 0x000003F8, + 3, + 0); + pdata->nvm__fmt__fgc[3] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_3 - 1, + 0x000007F0, + 4, + 0); + pdata->nvm__fmt__fgc[4] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_4 - 1, + 0x00000FE0, + 5, + 0); + pdata->nvm__fmt__fgc[5] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_5 - 1, + 0x00001FC0, + 6, + 0); + pdata->nvm__fmt__fgc[6] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_6 - 1, + 0x00003F80, + 7, + 0); + pdata->nvm__fmt__fgc[7] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_6, + 0x0000007F, + 0, + 0); + pdata->nvm__fmt__fgc[8] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_7, + 0x000000FE, + 1, + 0); + pdata->nvm__fmt__fgc[9] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_8, + 0x000001FC, + 2, + 0); + pdata->nvm__fmt__fgc[10] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_9 - 1, + 0x000003F8, + 3, + 0); + pdata->nvm__fmt__fgc[11] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_10 - 1, + 0x000007F0, + 4, + 0); + pdata->nvm__fmt__fgc[12] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_11 - 1, + 0x00000FE0, + 5, + 0); + pdata->nvm__fmt__fgc[13] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_12 - 1, + 0x00001FC0, + 6, + 0); + pdata->nvm__fmt__fgc[14] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_13 - 1, + 0x00003F80, + 7, + 0); + pdata->nvm__fmt__fgc[15] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_13, + 0x0000007F, + 0, + 0); + pdata->nvm__fmt__fgc[16] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_14, + 0x000000FE, + 1, + 0); + pdata->nvm__fmt__fgc[17] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__FGC__BYTE_15, + 0x000001FC, + 2, + 0); + pdata->nvm__fmt__fgc[18] = 0x00; + + pdata->nvm__fmt__test_program_major = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__TEST_PROGRAM_MAJOR_MINOR, + 0x000000E0, + 5, + 0); + pdata->nvm__fmt__test_program_minor = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__TEST_PROGRAM_MAJOR_MINOR, + 0x0000001F, + 0, + 0); + pdata->nvm__fmt__map_major = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__MAP_MAJOR_MINOR, + 0x000000E0, + 5, + 0); + pdata->nvm__fmt__map_minor = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__MAP_MAJOR_MINOR, + 0x0000001F, + 0, + 0); + pdata->nvm__fmt__year = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__YEAR_MONTH, + 0x000000F0, + 4, + 0); + pdata->nvm__fmt__month = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__YEAR_MONTH, + 0x0000000F, + 0, + 0); + pdata->nvm__fmt__day = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__DAY_MODULE_DATE_PHASE, + 0x000000F8, + 3, + 0); + pdata->nvm__fmt__module_date_phase = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__DAY_MODULE_DATE_PHASE, + 0x00000007, + 0, + 0); + pdata->nvm__fmt__time = + (uint16_t)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__FMT__TIME, + 0x0000FFFF, + 0, + 0); + pdata->nvm__fmt__tester_id = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__TESTER_ID, + 0x000000FF, + 0, + 0); + pdata->nvm__fmt__site_id = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__FMT__SITE_ID, + 0x000000FF, + 0, + 0); + + return status; +} + + +VL53L1_Error VL53L1_nvm_decode_ews_info( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_decoded_nvm_ews_info_t *pdata) +{ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + if (buf_size < VL53L1_NVM_SIZE_IN_BYTES) + return VL53L1_ERROR_BUFFER_TOO_SMALL; + + pdata->nvm__ews__test_program_major = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__TEST_PROGRAM_MAJOR_MINOR, + 0x000000E0, + 5, + 0); + pdata->nvm__ews__test_program_minor = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__TEST_PROGRAM_MAJOR_MINOR, + 0x0000001F, + 0, + 0); + pdata->nvm__ews__probe_card_major = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__PROBE_CARD_MAJOR_MINOR, + 0x000000F0, + 4, + 0); + pdata->nvm__ews__probe_card_minor = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__PROBE_CARD_MAJOR_MINOR, + 0x0000000F, + 0, + 0); + pdata->nvm__ews__tester_id = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__TESTER_ID, + 0x000000FF, + 0, + 0); + pdata->nvm__ews__lot[0] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__LOT__BYTE_0, + 0x000000FC, + 2, + 32); + pdata->nvm__ews__lot[1] = + (char)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__EWS__LOT__BYTE_1 - 1, + 0x000003F0, + 4, + 32); + pdata->nvm__ews__lot[2] = + (char)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__EWS__LOT__BYTE_2 - 1, + 0x00000FC0, + 6, + 32); + pdata->nvm__ews__lot[3] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__LOT__BYTE_2, + 0x0000003F, + 0, + 32); + pdata->nvm__ews__lot[4] = + (char)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__LOT__BYTE_3, + 0x000000FC, + 2, + 32); + pdata->nvm__ews__lot[5] = + (char)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__EWS__LOT__BYTE_4 - 1, + 0x000003F0, + 4, + 32); + pdata->nvm__ews__lot[6] = + (char)VL53L1_i2c_decode_with_mask( + 2, + pbuffer + VL53L1_NVM__EWS__LOT__BYTE_5 - 1, + 0x00000FC0, + 6, + 32); + + pdata->nvm__ews__lot[7] = 0x00; + + pdata->nvm__ews__wafer = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__WAFER, + 0x0000001F, + 0, + 0); + pdata->nvm__ews__xcoord = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__XCOORD, + 0x000000FF, + 0, + 0); + pdata->nvm__ews__ycoord = + (uint8_t)VL53L1_i2c_decode_with_mask( + 1, + pbuffer + VL53L1_NVM__EWS__YCOORD, + 0x000000FF, + 0, + 0); + + return status; + +} + + +void VL53L1_nvm_format_encode( + VL53L1_decoded_nvm_data_t *pnvm_info, + uint8_t *pnvm_data) +{ + SUPPRESS_UNUSED_WARNING(pnvm_info); + SUPPRESS_UNUSED_WARNING(pnvm_data); +} + + +VL53L1_Error VL53L1_read_nvm_raw_data( + VL53L1_DEV Dev, + uint8_t start_address, + uint8_t count, + uint8_t *pnvm_raw_data) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_nvm_enable( + Dev, + 0x0004, + VL53L1_NVM_POWER_UP_DELAY_US); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_nvm_read( + Dev, + start_address, + count, + pnvm_raw_data); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_nvm_disable(Dev); + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_read_nvm( + VL53L1_DEV Dev, + uint8_t nvm_format, + VL53L1_decoded_nvm_data_t *pnvm_info) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t nvm_data[VL53L1_NVM_SIZE_IN_BYTES]; + + LOG_FUNCTION_START(""); + + SUPPRESS_UNUSED_WARNING(nvm_format); + + + + status = VL53L1_read_nvm_raw_data( + Dev, + 0, + VL53L1_NVM_SIZE_IN_BYTES >> 2, + nvm_data); + + + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_nvm_format_decode( + VL53L1_NVM_SIZE_IN_BYTES, + nvm_data, + pnvm_info); + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_read_nvm_optical_centre( + VL53L1_DEV Dev, + VL53L1_optical_centre_t *pcentre) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t nvm_data[VL53L1_NVM__FMT__OPTICAL_CENTRE_DATA_SIZE]; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_read_nvm_raw_data( + Dev, + (uint8_t)(VL53L1_NVM__FMT__OPTICAL_CENTRE_DATA_INDEX + >> 2), + (uint8_t)(VL53L1_NVM__FMT__OPTICAL_CENTRE_DATA_SIZE + >> 2), + nvm_data); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_nvm_decode_optical_centre( + VL53L1_NVM__FMT__OPTICAL_CENTRE_DATA_SIZE, + nvm_data, + pcentre); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_read_nvm_cal_peak_rate_map( + VL53L1_DEV Dev, + VL53L1_cal_peak_rate_map_t *pcal_data) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t nvm_data[VL53L1_NVM__FMT__CAL_PEAK_RATE_MAP_DATA_SIZE]; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_read_nvm_raw_data( + Dev, + (uint8_t)(VL53L1_NVM__FMT__CAL_PEAK_RATE_MAP_DATA_INDEX + >> 2), + (uint8_t)(VL53L1_NVM__FMT__CAL_PEAK_RATE_MAP_DATA_SIZE + >> 2), + nvm_data); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_nvm_decode_cal_peak_rate_map( + VL53L1_NVM__FMT__CAL_PEAK_RATE_MAP_DATA_SIZE, + nvm_data, + pcal_data); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_read_nvm_additional_offset_cal_data( + VL53L1_DEV Dev, + VL53L1_additional_offset_cal_data_t *pcal_data) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t nvm_data[VL53L1_NVM__FMT__ADDITIONAL_OFFSET_CAL_DATA_SIZE]; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_read_nvm_raw_data( + Dev, + (uint8_t)( + VL53L1_NVM__FMT__ADDITIONAL_OFFSET_CAL_DATA_INDEX >> 2), + (uint8_t)( + VL53L1_NVM__FMT__ADDITIONAL_OFFSET_CAL_DATA_SIZE >> 2), + nvm_data); + + + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_nvm_decode_additional_offset_cal_data( + VL53L1_NVM__FMT__ADDITIONAL_OFFSET_CAL_DATA_SIZE, + nvm_data, + pcal_data); + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_read_nvm_fmt_range_results_data( + VL53L1_DEV Dev, + uint16_t range_results_select, + VL53L1_decoded_nvm_fmt_range_data_t *prange_data) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t nvm_data[VL53L1_NVM__FMT__RANGE_RESULTS__SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + + + status = VL53L1_read_nvm_raw_data( + Dev, + (uint8_t)(range_results_select >> 2), + (uint8_t)(VL53L1_NVM__FMT__RANGE_RESULTS__SIZE_BYTES >> 2), + nvm_data); + + + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_nvm_decode_fmt_range_results_data( + VL53L1_NVM__FMT__RANGE_RESULTS__SIZE_BYTES, + nvm_data, + prange_data); + + LOG_FUNCTION_END(status); + + return status; + +} + + diff --git a/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_nvm_debug.c b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_nvm_debug.c new file mode 100644 index 000000000000..0c2e01fd8fed --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_nvm_debug.c @@ -0,0 +1,1032 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#include "vl53l1_ll_def.h" +#include "vl53l1_platform.h" +#include "vl53l1_platform_log.h" +#include "vl53l1_register_map.h" +#include "vl53l1_api_debug.h" +#include "vl53l1_nvm_structs.h" +#include "vl53l1_nvm_debug.h" + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_NVM, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_NVM, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_NVM,\ + status, fmt, ##__VA_ARGS__) + +#define trace_print(level, ...) \ + _LOG_TRACE_PRINT(trace_flags, \ + level, VL53L1_TRACE_FUNCTION_NONE, ##__VA_ARGS__) + +#ifdef VL53L1_LOG_ENABLE + +void VL53L1_print_nvm_raw_data( + uint8_t *pnvm_raw_data, + uint32_t trace_flags) +{ + + + int i = 0; + + LOG_FUNCTION_START(""); + + for (i = 0 ; i < VL53L1_NVM_SIZE_IN_BYTES ; i++) { + if (i % 4 == 0) + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "\n NVM Addr 0x%02X : 0x", + i/4); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%02X", + *pnvm_raw_data++); + } + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "\n"); + + LOG_FUNCTION_END(0); +} + + +void VL53L1_print_decoded_nvm_data( + VL53L1_decoded_nvm_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + char fp_text[VL53L1_MAX_STRING_LENGTH]; + char pre_text[VL53L1_MAX_STRING_LENGTH]; + char *ppre_text = &(pre_text[0]); + + uint8_t i = 0; + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__identification_model_id", + pdata->nvm__identification_model_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__identification_module_type", + pdata->nvm__identification_module_type); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__identification_revision_id", + pdata->nvm__identification_revision_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__identification_module_id", + pdata->nvm__identification_module_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__i2c_valid", + pdata->nvm__i2c_valid); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__i2c_device_address_ews", + pdata->nvm__i2c_device_address_ews); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->nvm__ews__fast_osc_frequency, + 12, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__ews__fast_osc_frequency", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__fast_osc_trim_max", + pdata->nvm__ews__fast_osc_trim_max); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__fast_osc_freq_set", + pdata->nvm__ews__fast_osc_freq_set); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__slow_osc_calibration", + pdata->nvm__ews__slow_osc_calibration); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->nvm__fmt__fast_osc_frequency, + 12, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__fmt__fast_osc_frequency", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__fast_osc_trim_max", + pdata->nvm__fmt__fast_osc_trim_max); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__fast_osc_freq_set", + pdata->nvm__fmt__fast_osc_freq_set); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__slow_osc_calibration", + pdata->nvm__fmt__slow_osc_calibration); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__vhv_config_unlock", + pdata->nvm__vhv_config_unlock); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ref_selvddpix", + pdata->nvm__ref_selvddpix); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ref_selvquench", + pdata->nvm__ref_selvquench); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__regavdd1v2_sel", + pdata->nvm__regavdd1v2_sel); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__regdvdd1v2_sel", + pdata->nvm__regdvdd1v2_sel); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__vhv_timeout__macrop", + pdata->nvm__vhv_timeout__macrop); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__vhv_loop_bound", + pdata->nvm__vhv_loop_bound); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__vhv_count_threshold", + pdata->nvm__vhv_count_threshold); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__vhv_offset", + pdata->nvm__vhv_offset); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__vhv_init_enable", + pdata->nvm__vhv_init_enable); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__vhv_init_value", + pdata->nvm__vhv_init_value); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_vcsel_trim_ll", + pdata->nvm__laser_safety_vcsel_trim_ll); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_vcsel_selion_ll", + pdata->nvm__laser_safety_vcsel_selion_ll); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_vcsel_selion_max_ll", + pdata->nvm__laser_safety_vcsel_selion_max_ll); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_mult_ll", + pdata->nvm__laser_safety_mult_ll); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_clip_ll", + pdata->nvm__laser_safety_clip_ll); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_vcsel_trim_ld", + pdata->nvm__laser_safety_vcsel_trim_ld); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_vcsel_selion_ld", + pdata->nvm__laser_safety_vcsel_selion_ld); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_vcsel_selion_max_ld", + pdata->nvm__laser_safety_vcsel_selion_max_ld); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_mult_ld", + pdata->nvm__laser_safety_mult_ld); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_clip_ld", + pdata->nvm__laser_safety_clip_ld); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_lock_byte", + pdata->nvm__laser_safety_lock_byte); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__laser_safety_unlock_byte", + pdata->nvm__laser_safety_unlock_byte); + + + + for (i = 0 ; i < VL53L1_RTN_SPAD_BUFFER_SIZE ; i++) { + sprintf( + ppre_text, + "%snvm__ews__spad_enables_rtn[%u]", + pprefix, i); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->nvm__ews__spad_enables_rtn[i]); + } + + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) { + sprintf( + ppre_text, + "%snvm__ews__spad_enables_ref__loc1[%u]", + pprefix, i); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->nvm__ews__spad_enables_ref__loc1[i]); + } + + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) { + sprintf( + ppre_text, + "%snvm__ews__spad_enables_ref__loc2[%u]", + pprefix, i); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->nvm__ews__spad_enables_ref__loc2[i]); + } + + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) { + sprintf( + ppre_text, + "%snvm__ews__spad_enables_ref__loc3[%u]", + pprefix, i); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->nvm__ews__spad_enables_ref__loc3[i]); + } + + + + + for (i = 0 ; i < VL53L1_RTN_SPAD_BUFFER_SIZE ; i++) { + sprintf( + ppre_text, + "%snvm__fmt__spad_enables_rtn[%u]", + pprefix, i); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->nvm__fmt__spad_enables_rtn[i]); + } + + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) { + sprintf( + ppre_text, + "%snvm__fmt__spad_enables_ref__loc1[%u]", + pprefix, i); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->nvm__fmt__spad_enables_ref__loc1[i]); + } + + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) { + sprintf( + ppre_text, + "%snvm__fmt__spad_enables_ref__loc2[%u]", + pprefix, i); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->nvm__fmt__spad_enables_ref__loc2[i]); + } + + for (i = 0 ; i < VL53L1_REF_SPAD_BUFFER_SIZE ; i++) { + sprintf( + ppre_text, + "%snvm__fmt__spad_enables_ref__loc3[%u]", + pprefix, i); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s = %u\n", + ppre_text, + pdata->nvm__fmt__spad_enables_ref__loc3[i]); + } + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__roi_config__mode_roi_centre_spad", + pdata->nvm__fmt__roi_config__mode_roi_centre_spad); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__roi_config__mode_roi_x_size", + pdata->nvm__fmt__roi_config__mode_roi_x_size); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__roi_config__mode_roi_y_size", + pdata->nvm__fmt__roi_config__mode_roi_y_size); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__ref_spad_apply__num_requested_ref_spad", + pdata->nvm__fmt__ref_spad_apply__num_requested_ref_spad); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__ref_spad_man__ref_location", + pdata->nvm__fmt__ref_spad_man__ref_location); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "nvm__fmt__mm_config__inner_offset_mm", + pdata->nvm__fmt__mm_config__inner_offset_mm); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "nvm__fmt__mm_config__outer_offset_mm", + pdata->nvm__fmt__mm_config__outer_offset_mm); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->nvm__fmt__algo_part_to_part_range_offset_mm, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__fmt__algo_part_to_part_range_offset_mm", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)( + pdata->nvm__fmt__algo__crosstalk_compensation_plane_offset_kcps), + 9, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__fmt__algo__crosstalk_compensation_plane_offset_kcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)( + pdata->nvm__fmt__algo__crosstalk_compensation_x_plane_gradient_kcps), + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__fmt__algo__crosstalk_compensation_x_plane_gradient_kcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)( + pdata->nvm__fmt__algo__crosstalk_compensation_y_plane_gradient_kcps), + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__fmt__algo__crosstalk_compensation_y_plane_gradient_kcps", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__spare__host_config__nvm_config_spare_0", + pdata->nvm__fmt__spare__host_config__nvm_config_spare_0); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__spare__host_config__nvm_config_spare_1", + pdata->nvm__fmt__spare__host_config__nvm_config_spare_1); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__customer_space_programmed", + pdata->nvm__customer_space_programmed); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__cust__i2c_device_address", + pdata->nvm__cust__i2c_device_address); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__cust__ref_spad_apply__num_requested_ref_spad", + pdata->nvm__cust__ref_spad_apply__num_requested_ref_spad); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__cust__ref_spad_man__ref_location", + pdata->nvm__cust__ref_spad_man__ref_location); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "nvm__cust__mm_config__inner_offset_mm", + pdata->nvm__cust__mm_config__inner_offset_mm); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %d\n", + pprefix, + "nvm__cust__mm_config__outer_offset_mm", + pdata->nvm__cust__mm_config__outer_offset_mm); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->nvm__cust__algo_part_to_part_range_offset_mm, + 2, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__cust__algo_part_to_part_range_offset_mm", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (uint32_t)( + pdata->nvm__cust__algo__crosstalk_compensation_plane_offset_kcps), + 9, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__cust__algo__crosstalk_compensation_plane_offset_kcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)( + pdata->nvm__cust__algo__crosstalk_compensation_x_plane_gradient_kcps), + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__cust__algo__crosstalk_compensation_x_plane_gradient_kcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)( + pdata->nvm__cust__algo__crosstalk_compensation_y_plane_gradient_kcps), + 11, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "nvm__cust__algo__crosstalk_compensation_y_plane_gradient_kcps", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__cust__spare__host_config__nvm_config_spare_0", + pdata->nvm__cust__spare__host_config__nvm_config_spare_0); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__cust__spare__host_config__nvm_config_spare_1", + pdata->nvm__cust__spare__host_config__nvm_config_spare_1); + + + + sprintf( + ppre_text, + "%sfmt_optical_centre.", pprefix); + + VL53L1_print_optical_centre( + &(pdata->fmt_optical_centre), + ppre_text, + VL53L1_TRACE_MODULE_NVM_DATA); + + + + sprintf( + ppre_text, + "%sfmt_peak_rate_map.", pprefix); + + VL53L1_print_cal_peak_rate_map( + &(pdata->fmt_peak_rate_map), + ppre_text, + VL53L1_TRACE_MODULE_NVM_DATA); + + + + sprintf( + ppre_text, + "%sfmt_add_offset_data.", + pprefix); + + VL53L1_print_additional_offset_cal_data( + &(pdata->fmt_add_offset_data), + ppre_text, + VL53L1_TRACE_MODULE_NVM_DATA); + + + + for (i = 0 ; i < VL53L1_NVM_MAX_FMT_RANGE_DATA ; i++) { + sprintf( + ppre_text, + "%sfmt_range_data[%u].", + pprefix, i); + + VL53L1_print_decoded_nvm_fmt_range_data( + &(pdata->fmt_range_data[i]), + ppre_text, + trace_flags); + } + + sprintf( + ppre_text, + "%sfmt_info.", + pprefix); + + VL53L1_print_decoded_nvm_fmt_info( + &(pdata->fmt_info), + ppre_text, + trace_flags); + + sprintf( + ppre_text, + "%sews_info.", + pprefix); + + VL53L1_print_decoded_nvm_ews_info( + &(pdata->ews_info), + ppre_text, + trace_flags); +} + + +void VL53L1_print_decoded_nvm_fmt_range_data( + VL53L1_decoded_nvm_fmt_range_data_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + char fp_text[VL53L1_MAX_STRING_LENGTH]; + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__actual_effective_rtn_spads, + 8, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "result__actual_effective_rtn_spads", + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ref_spad_array__num_requested_ref_spads", + pdata->ref_spad_array__num_requested_ref_spads); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "ref_spad_array__ref_location", + pdata->ref_spad_array__ref_location); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__peak_signal_count_rate_rtn_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "result__peak_signal_count_rate_rtn_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__ambient_count_rate_rtn_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "result__ambient_count_rate_rtn_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__peak_signal_count_rate_ref_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "result__peak_signal_count_rate_ref_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->result__ambient_count_rate_ref_mcps, + 7, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "result__ambient_count_rate_ref_mcps", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (int32_t)pdata->measured_distance_mm, + 4, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "measured_distance_mm", + fp_text); + + VL53L1_signed_fixed_point_sprintf( + (uint32_t)pdata->measured_distance_stdev_mm, + 4, + VL53L1_MAX_STRING_LENGTH, + fp_text); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %s\n", + pprefix, + "measured_distance_stdev_mm", + fp_text); +} + + +void VL53L1_print_decoded_nvm_fmt_info( + VL53L1_decoded_nvm_fmt_info_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = \"%s\"\n", + pprefix, + "nvm__fmt__fgc", + pdata->nvm__fmt__fgc); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__test_program_major", + pdata->nvm__fmt__test_program_major); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__test_program_minor", + pdata->nvm__fmt__test_program_minor); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__map_major", + pdata->nvm__fmt__map_major); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__map_minor", + pdata->nvm__fmt__map_minor); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__year", + pdata->nvm__fmt__year); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__month", + pdata->nvm__fmt__month); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__day", + pdata->nvm__fmt__day); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__module_date_phase", + pdata->nvm__fmt__module_date_phase); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__time", + pdata->nvm__fmt__time); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__tester_id", + pdata->nvm__fmt__tester_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__fmt__site_id", + pdata->nvm__fmt__site_id); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__test_program_major", + pdata->nvm__ews__test_program_major); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__test_program_minor", + pdata->nvm__ews__test_program_minor); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__probe_card_major", + pdata->nvm__ews__probe_card_major); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__probe_card_minor", + pdata->nvm__ews__probe_card_minor); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__tester_id", + pdata->nvm__ews__tester_id); +} + + +void VL53L1_print_decoded_nvm_ews_info( + VL53L1_decoded_nvm_ews_info_t *pdata, + char *pprefix, + uint32_t trace_flags) +{ + trace_print(VL53L1_TRACE_LEVEL_INFO, + "%s%s = \"%s\"\n", + pprefix, + "nvm__ews__lot", + pdata->nvm__ews__lot); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__wafer", + pdata->nvm__ews__wafer); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__xcoord", + pdata->nvm__ews__xcoord); + + trace_print( + VL53L1_TRACE_LEVEL_INFO, + "%s%s = %u\n", + pprefix, + "nvm__ews__ycoord", + pdata->nvm__ews__ycoord); +} + +#endif + + diff --git a/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_register_funcs.c b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_register_funcs.c new file mode 100644 index 000000000000..0e3ae3b7de26 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_register_funcs.c @@ -0,0 +1,4131 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#include "vl53l1_ll_def.h" +#include "vl53l1_platform.h" +#include "vl53l1_platform_log.h" +#include "vl53l1_core.h" +#include "vl53l1_register_map.h" +#include "vl53l1_register_structs.h" +#include "vl53l1_register_funcs.h" + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_REGISTERS, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_REGISTERS, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_REGISTERS,\ + status, fmt, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_i2c_encode_static_nvm_managed( + VL53L1_static_nvm_managed_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->i2c_slave__device_address & 0x7F; + *(pbuffer + 1) = + pdata->ana_config__vhv_ref_sel_vddpix & 0xF; + *(pbuffer + 2) = + pdata->ana_config__vhv_ref_sel_vquench & 0x7F; + *(pbuffer + 3) = + pdata->ana_config__reg_avdd1v2_sel & 0x3; + *(pbuffer + 4) = + pdata->ana_config__fast_osc__trim & 0x7F; + VL53L1_i2c_encode_uint16_t( + pdata->osc_measured__fast_osc__frequency, + 2, + pbuffer + 5); + *(pbuffer + 7) = + pdata->vhv_config__timeout_macrop_loop_bound; + *(pbuffer + 8) = + pdata->vhv_config__count_thresh; + *(pbuffer + 9) = + pdata->vhv_config__offset & 0x3F; + *(pbuffer + 10) = + pdata->vhv_config__init; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_static_nvm_managed( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_static_nvm_managed_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->i2c_slave__device_address = + (*(pbuffer + 0)) & 0x7F; + pdata->ana_config__vhv_ref_sel_vddpix = + (*(pbuffer + 1)) & 0xF; + pdata->ana_config__vhv_ref_sel_vquench = + (*(pbuffer + 2)) & 0x7F; + pdata->ana_config__reg_avdd1v2_sel = + (*(pbuffer + 3)) & 0x3; + pdata->ana_config__fast_osc__trim = + (*(pbuffer + 4)) & 0x7F; + pdata->osc_measured__fast_osc__frequency = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 5)); + pdata->vhv_config__timeout_macrop_loop_bound = + (*(pbuffer + 7)); + pdata->vhv_config__count_thresh = + (*(pbuffer + 8)); + pdata->vhv_config__offset = + (*(pbuffer + 9)) & 0x3F; + pdata->vhv_config__init = + (*(pbuffer + 10)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_static_nvm_managed( + VL53L1_DEV Dev, + VL53L1_static_nvm_managed_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_static_nvm_managed( + pdata, + VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_I2C_SLAVE__DEVICE_ADDRESS, + comms_buffer, + VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_static_nvm_managed( + VL53L1_DEV Dev, + VL53L1_static_nvm_managed_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_I2C_SLAVE__DEVICE_ADDRESS, + comms_buffer, + VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_static_nvm_managed( + VL53L1_STATIC_NVM_MANAGED_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_customer_nvm_managed( + VL53L1_customer_nvm_managed_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->global_config__spad_enables_ref_0; + *(pbuffer + 1) = + pdata->global_config__spad_enables_ref_1; + *(pbuffer + 2) = + pdata->global_config__spad_enables_ref_2; + *(pbuffer + 3) = + pdata->global_config__spad_enables_ref_3; + *(pbuffer + 4) = + pdata->global_config__spad_enables_ref_4; + *(pbuffer + 5) = + pdata->global_config__spad_enables_ref_5 & 0xF; + *(pbuffer + 6) = + pdata->global_config__ref_en_start_select; + *(pbuffer + 7) = + pdata->ref_spad_man__num_requested_ref_spads & 0x3F; + *(pbuffer + 8) = + pdata->ref_spad_man__ref_location & 0x3; + VL53L1_i2c_encode_uint16_t( + pdata->algo__crosstalk_compensation_plane_offset_kcps, + 2, + pbuffer + 9); + VL53L1_i2c_encode_int16_t( + pdata->algo__crosstalk_compensation_x_plane_gradient_kcps, + 2, + pbuffer + 11); + VL53L1_i2c_encode_int16_t( + pdata->algo__crosstalk_compensation_y_plane_gradient_kcps, + 2, + pbuffer + 13); + VL53L1_i2c_encode_uint16_t( + pdata->ref_spad_char__total_rate_target_mcps, + 2, + pbuffer + 15); + VL53L1_i2c_encode_int16_t( + pdata->algo__part_to_part_range_offset_mm & 0x1FFF, + 2, + pbuffer + 17); + VL53L1_i2c_encode_int16_t( + pdata->mm_config__inner_offset_mm, + 2, + pbuffer + 19); + VL53L1_i2c_encode_int16_t( + pdata->mm_config__outer_offset_mm, + 2, + pbuffer + 21); + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_customer_nvm_managed( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_customer_nvm_managed_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->global_config__spad_enables_ref_0 = + (*(pbuffer + 0)); + pdata->global_config__spad_enables_ref_1 = + (*(pbuffer + 1)); + pdata->global_config__spad_enables_ref_2 = + (*(pbuffer + 2)); + pdata->global_config__spad_enables_ref_3 = + (*(pbuffer + 3)); + pdata->global_config__spad_enables_ref_4 = + (*(pbuffer + 4)); + pdata->global_config__spad_enables_ref_5 = + (*(pbuffer + 5)) & 0xF; + pdata->global_config__ref_en_start_select = + (*(pbuffer + 6)); + pdata->ref_spad_man__num_requested_ref_spads = + (*(pbuffer + 7)) & 0x3F; + pdata->ref_spad_man__ref_location = + (*(pbuffer + 8)) & 0x3; + pdata->algo__crosstalk_compensation_plane_offset_kcps = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 9)); + pdata->algo__crosstalk_compensation_x_plane_gradient_kcps = + (VL53L1_i2c_decode_int16_t(2, pbuffer + 11)); + pdata->algo__crosstalk_compensation_y_plane_gradient_kcps = + (VL53L1_i2c_decode_int16_t(2, pbuffer + 13)); + pdata->ref_spad_char__total_rate_target_mcps = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 15)); + pdata->algo__part_to_part_range_offset_mm = + (VL53L1_i2c_decode_int16_t(2, pbuffer + 17)) & 0x1FFF; + pdata->mm_config__inner_offset_mm = + (VL53L1_i2c_decode_int16_t(2, pbuffer + 19)); + pdata->mm_config__outer_offset_mm = + (VL53L1_i2c_decode_int16_t(2, pbuffer + 21)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_customer_nvm_managed( + VL53L1_DEV Dev, + VL53L1_customer_nvm_managed_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_customer_nvm_managed( + pdata, + VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_0, + comms_buffer, + VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_customer_nvm_managed( + VL53L1_DEV Dev, + VL53L1_customer_nvm_managed_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_GLOBAL_CONFIG__SPAD_ENABLES_REF_0, + comms_buffer, + VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_customer_nvm_managed( + VL53L1_CUSTOMER_NVM_MANAGED_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_static_config( + VL53L1_static_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + VL53L1_i2c_encode_uint16_t( + pdata->dss_config__target_total_rate_mcps, + 2, + pbuffer + 0); + *(pbuffer + 2) = + pdata->debug__ctrl & 0x1; + *(pbuffer + 3) = + pdata->test_mode__ctrl & 0xF; + *(pbuffer + 4) = + pdata->clk_gating__ctrl & 0xF; + *(pbuffer + 5) = + pdata->nvm_bist__ctrl & 0x1F; + *(pbuffer + 6) = + pdata->nvm_bist__num_nvm_words & 0x7F; + *(pbuffer + 7) = + pdata->nvm_bist__start_address & 0x7F; + *(pbuffer + 8) = + pdata->host_if__status & 0x1; + *(pbuffer + 9) = + pdata->pad_i2c_hv__config; + *(pbuffer + 10) = + pdata->pad_i2c_hv__extsup_config & 0x1; + *(pbuffer + 11) = + pdata->gpio_hv_pad__ctrl & 0x3; + *(pbuffer + 12) = + pdata->gpio_hv_mux__ctrl & 0x1F; + *(pbuffer + 13) = + pdata->gpio__tio_hv_status & 0x3; + *(pbuffer + 14) = + pdata->gpio__fio_hv_status & 0x3; + *(pbuffer + 15) = + pdata->ana_config__spad_sel_pswidth & 0x7; + *(pbuffer + 16) = + pdata->ana_config__vcsel_pulse_width_offset & 0x1F; + *(pbuffer + 17) = + pdata->ana_config__fast_osc__config_ctrl & 0x1; + *(pbuffer + 18) = + pdata->sigma_estimator__effective_pulse_width_ns; + *(pbuffer + 19) = + pdata->sigma_estimator__effective_ambient_width_ns; + *(pbuffer + 20) = + pdata->sigma_estimator__sigma_ref_mm; + *(pbuffer + 21) = + pdata->algo__crosstalk_compensation_valid_height_mm; + *(pbuffer + 22) = + pdata->spare_host_config__static_config_spare_0; + *(pbuffer + 23) = + pdata->spare_host_config__static_config_spare_1; + VL53L1_i2c_encode_uint16_t( + pdata->algo__range_ignore_threshold_mcps, + 2, + pbuffer + 24); + *(pbuffer + 26) = + pdata->algo__range_ignore_valid_height_mm; + *(pbuffer + 27) = + pdata->algo__range_min_clip; + *(pbuffer + 28) = + pdata->algo__consistency_check__tolerance & 0xF; + *(pbuffer + 29) = + pdata->spare_host_config__static_config_spare_2; + *(pbuffer + 30) = + pdata->sd_config__reset_stages_msb & 0xF; + *(pbuffer + 31) = + pdata->sd_config__reset_stages_lsb; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_static_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_static_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->dss_config__target_total_rate_mcps = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 0)); + pdata->debug__ctrl = + (*(pbuffer + 2)) & 0x1; + pdata->test_mode__ctrl = + (*(pbuffer + 3)) & 0xF; + pdata->clk_gating__ctrl = + (*(pbuffer + 4)) & 0xF; + pdata->nvm_bist__ctrl = + (*(pbuffer + 5)) & 0x1F; + pdata->nvm_bist__num_nvm_words = + (*(pbuffer + 6)) & 0x7F; + pdata->nvm_bist__start_address = + (*(pbuffer + 7)) & 0x7F; + pdata->host_if__status = + (*(pbuffer + 8)) & 0x1; + pdata->pad_i2c_hv__config = + (*(pbuffer + 9)); + pdata->pad_i2c_hv__extsup_config = + (*(pbuffer + 10)) & 0x1; + pdata->gpio_hv_pad__ctrl = + (*(pbuffer + 11)) & 0x3; + pdata->gpio_hv_mux__ctrl = + (*(pbuffer + 12)) & 0x1F; + pdata->gpio__tio_hv_status = + (*(pbuffer + 13)) & 0x3; + pdata->gpio__fio_hv_status = + (*(pbuffer + 14)) & 0x3; + pdata->ana_config__spad_sel_pswidth = + (*(pbuffer + 15)) & 0x7; + pdata->ana_config__vcsel_pulse_width_offset = + (*(pbuffer + 16)) & 0x1F; + pdata->ana_config__fast_osc__config_ctrl = + (*(pbuffer + 17)) & 0x1; + pdata->sigma_estimator__effective_pulse_width_ns = + (*(pbuffer + 18)); + pdata->sigma_estimator__effective_ambient_width_ns = + (*(pbuffer + 19)); + pdata->sigma_estimator__sigma_ref_mm = + (*(pbuffer + 20)); + pdata->algo__crosstalk_compensation_valid_height_mm = + (*(pbuffer + 21)); + pdata->spare_host_config__static_config_spare_0 = + (*(pbuffer + 22)); + pdata->spare_host_config__static_config_spare_1 = + (*(pbuffer + 23)); + pdata->algo__range_ignore_threshold_mcps = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 24)); + pdata->algo__range_ignore_valid_height_mm = + (*(pbuffer + 26)); + pdata->algo__range_min_clip = + (*(pbuffer + 27)); + pdata->algo__consistency_check__tolerance = + (*(pbuffer + 28)) & 0xF; + pdata->spare_host_config__static_config_spare_2 = + (*(pbuffer + 29)); + pdata->sd_config__reset_stages_msb = + (*(pbuffer + 30)) & 0xF; + pdata->sd_config__reset_stages_lsb = + (*(pbuffer + 31)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_static_config( + VL53L1_DEV Dev, + VL53L1_static_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_static_config( + pdata, + VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_DSS_CONFIG__TARGET_TOTAL_RATE_MCPS, + comms_buffer, + VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_static_config( + VL53L1_DEV Dev, + VL53L1_static_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_DSS_CONFIG__TARGET_TOTAL_RATE_MCPS, + comms_buffer, + VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_static_config( + VL53L1_STATIC_CONFIG_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_general_config( + VL53L1_general_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->gph_config__stream_count_update_value; + *(pbuffer + 1) = + pdata->global_config__stream_divider; + *(pbuffer + 2) = + pdata->system__interrupt_config_gpio; + *(pbuffer + 3) = + pdata->cal_config__vcsel_start & 0x7F; + VL53L1_i2c_encode_uint16_t( + pdata->cal_config__repeat_rate & 0xFFF, + 2, + pbuffer + 4); + *(pbuffer + 6) = + pdata->global_config__vcsel_width & 0x7F; + *(pbuffer + 7) = + pdata->phasecal_config__timeout_macrop; + *(pbuffer + 8) = + pdata->phasecal_config__target; + *(pbuffer + 9) = + pdata->phasecal_config__override & 0x1; + *(pbuffer + 11) = + pdata->dss_config__roi_mode_control & 0x7; + VL53L1_i2c_encode_uint16_t( + pdata->system__thresh_rate_high, + 2, + pbuffer + 12); + VL53L1_i2c_encode_uint16_t( + pdata->system__thresh_rate_low, + 2, + pbuffer + 14); + VL53L1_i2c_encode_uint16_t( + pdata->dss_config__manual_effective_spads_select, + 2, + pbuffer + 16); + *(pbuffer + 18) = + pdata->dss_config__manual_block_select; + *(pbuffer + 19) = + pdata->dss_config__aperture_attenuation; + *(pbuffer + 20) = + pdata->dss_config__max_spads_limit; + *(pbuffer + 21) = + pdata->dss_config__min_spads_limit; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_general_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_general_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->gph_config__stream_count_update_value = + (*(pbuffer + 0)); + pdata->global_config__stream_divider = + (*(pbuffer + 1)); + pdata->system__interrupt_config_gpio = + (*(pbuffer + 2)); + pdata->cal_config__vcsel_start = + (*(pbuffer + 3)) & 0x7F; + pdata->cal_config__repeat_rate = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 4)) & 0xFFF; + pdata->global_config__vcsel_width = + (*(pbuffer + 6)) & 0x7F; + pdata->phasecal_config__timeout_macrop = + (*(pbuffer + 7)); + pdata->phasecal_config__target = + (*(pbuffer + 8)); + pdata->phasecal_config__override = + (*(pbuffer + 9)) & 0x1; + pdata->dss_config__roi_mode_control = + (*(pbuffer + 11)) & 0x7; + pdata->system__thresh_rate_high = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 12)); + pdata->system__thresh_rate_low = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 14)); + pdata->dss_config__manual_effective_spads_select = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 16)); + pdata->dss_config__manual_block_select = + (*(pbuffer + 18)); + pdata->dss_config__aperture_attenuation = + (*(pbuffer + 19)); + pdata->dss_config__max_spads_limit = + (*(pbuffer + 20)); + pdata->dss_config__min_spads_limit = + (*(pbuffer + 21)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_general_config( + VL53L1_DEV Dev, + VL53L1_general_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_general_config( + pdata, + VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_GPH_CONFIG__STREAM_COUNT_UPDATE_VALUE, + comms_buffer, + VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_general_config( + VL53L1_DEV Dev, + VL53L1_general_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_GPH_CONFIG__STREAM_COUNT_UPDATE_VALUE, + comms_buffer, + VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_general_config( + VL53L1_GENERAL_CONFIG_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_timing_config( + VL53L1_timing_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->mm_config__timeout_macrop_a_hi & 0xF; + *(pbuffer + 1) = + pdata->mm_config__timeout_macrop_a_lo; + *(pbuffer + 2) = + pdata->mm_config__timeout_macrop_b_hi & 0xF; + *(pbuffer + 3) = + pdata->mm_config__timeout_macrop_b_lo; + *(pbuffer + 4) = + pdata->range_config__timeout_macrop_a_hi & 0xF; + *(pbuffer + 5) = + pdata->range_config__timeout_macrop_a_lo; + *(pbuffer + 6) = + pdata->range_config__vcsel_period_a & 0x3F; + *(pbuffer + 7) = + pdata->range_config__timeout_macrop_b_hi & 0xF; + *(pbuffer + 8) = + pdata->range_config__timeout_macrop_b_lo; + *(pbuffer + 9) = + pdata->range_config__vcsel_period_b & 0x3F; + VL53L1_i2c_encode_uint16_t( + pdata->range_config__sigma_thresh, + 2, + pbuffer + 10); + VL53L1_i2c_encode_uint16_t( + pdata->range_config__min_count_rate_rtn_limit_mcps, + 2, + pbuffer + 12); + *(pbuffer + 14) = + pdata->range_config__valid_phase_low; + *(pbuffer + 15) = + pdata->range_config__valid_phase_high; + VL53L1_i2c_encode_uint32_t( + pdata->system__intermeasurement_period, + 4, + pbuffer + 18); + *(pbuffer + 22) = + pdata->system__fractional_enable & 0x1; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_timing_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_timing_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->mm_config__timeout_macrop_a_hi = + (*(pbuffer + 0)) & 0xF; + pdata->mm_config__timeout_macrop_a_lo = + (*(pbuffer + 1)); + pdata->mm_config__timeout_macrop_b_hi = + (*(pbuffer + 2)) & 0xF; + pdata->mm_config__timeout_macrop_b_lo = + (*(pbuffer + 3)); + pdata->range_config__timeout_macrop_a_hi = + (*(pbuffer + 4)) & 0xF; + pdata->range_config__timeout_macrop_a_lo = + (*(pbuffer + 5)); + pdata->range_config__vcsel_period_a = + (*(pbuffer + 6)) & 0x3F; + pdata->range_config__timeout_macrop_b_hi = + (*(pbuffer + 7)) & 0xF; + pdata->range_config__timeout_macrop_b_lo = + (*(pbuffer + 8)); + pdata->range_config__vcsel_period_b = + (*(pbuffer + 9)) & 0x3F; + pdata->range_config__sigma_thresh = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 10)); + pdata->range_config__min_count_rate_rtn_limit_mcps = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 12)); + pdata->range_config__valid_phase_low = + (*(pbuffer + 14)); + pdata->range_config__valid_phase_high = + (*(pbuffer + 15)); + pdata->system__intermeasurement_period = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 18)); + pdata->system__fractional_enable = + (*(pbuffer + 22)) & 0x1; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_timing_config( + VL53L1_DEV Dev, + VL53L1_timing_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_timing_config( + pdata, + VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_MM_CONFIG__TIMEOUT_MACROP_A_HI, + comms_buffer, + VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_timing_config( + VL53L1_DEV Dev, + VL53L1_timing_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_MM_CONFIG__TIMEOUT_MACROP_A_HI, + comms_buffer, + VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_timing_config( + VL53L1_TIMING_CONFIG_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_dynamic_config( + VL53L1_dynamic_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->system__grouped_parameter_hold_0 & 0x3; + VL53L1_i2c_encode_uint16_t( + pdata->system__thresh_high, + 2, + pbuffer + 1); + VL53L1_i2c_encode_uint16_t( + pdata->system__thresh_low, + 2, + pbuffer + 3); + *(pbuffer + 5) = + pdata->system__enable_xtalk_per_quadrant & 0x1; + *(pbuffer + 6) = + pdata->system__seed_config & 0x7; + *(pbuffer + 7) = + pdata->sd_config__woi_sd0; + *(pbuffer + 8) = + pdata->sd_config__woi_sd1; + *(pbuffer + 9) = + pdata->sd_config__initial_phase_sd0 & 0x7F; + *(pbuffer + 10) = + pdata->sd_config__initial_phase_sd1 & 0x7F; + *(pbuffer + 11) = + pdata->system__grouped_parameter_hold_1 & 0x3; + *(pbuffer + 12) = + pdata->sd_config__first_order_select & 0x3; + *(pbuffer + 13) = + pdata->sd_config__quantifier & 0xF; + *(pbuffer + 14) = + pdata->roi_config__user_roi_centre_spad; + *(pbuffer + 15) = + pdata->roi_config__user_roi_requested_global_xy_size; + *(pbuffer + 16) = + pdata->system__sequence_config; + *(pbuffer + 17) = + pdata->system__grouped_parameter_hold & 0x3; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_dynamic_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_dynamic_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->system__grouped_parameter_hold_0 = + (*(pbuffer + 0)) & 0x3; + pdata->system__thresh_high = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 1)); + pdata->system__thresh_low = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 3)); + pdata->system__enable_xtalk_per_quadrant = + (*(pbuffer + 5)) & 0x1; + pdata->system__seed_config = + (*(pbuffer + 6)) & 0x7; + pdata->sd_config__woi_sd0 = + (*(pbuffer + 7)); + pdata->sd_config__woi_sd1 = + (*(pbuffer + 8)); + pdata->sd_config__initial_phase_sd0 = + (*(pbuffer + 9)) & 0x7F; + pdata->sd_config__initial_phase_sd1 = + (*(pbuffer + 10)) & 0x7F; + pdata->system__grouped_parameter_hold_1 = + (*(pbuffer + 11)) & 0x3; + pdata->sd_config__first_order_select = + (*(pbuffer + 12)) & 0x3; + pdata->sd_config__quantifier = + (*(pbuffer + 13)) & 0xF; + pdata->roi_config__user_roi_centre_spad = + (*(pbuffer + 14)); + pdata->roi_config__user_roi_requested_global_xy_size = + (*(pbuffer + 15)); + pdata->system__sequence_config = + (*(pbuffer + 16)); + pdata->system__grouped_parameter_hold = + (*(pbuffer + 17)) & 0x3; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_dynamic_config( + VL53L1_DEV Dev, + VL53L1_dynamic_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_dynamic_config( + pdata, + VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_SYSTEM__GROUPED_PARAMETER_HOLD_0, + comms_buffer, + VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_dynamic_config( + VL53L1_DEV Dev, + VL53L1_dynamic_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_SYSTEM__GROUPED_PARAMETER_HOLD_0, + comms_buffer, + VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_dynamic_config( + VL53L1_DYNAMIC_CONFIG_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_system_control( + VL53L1_system_control_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->power_management__go1_power_force & 0x1; + *(pbuffer + 1) = + pdata->system__stream_count_ctrl & 0x1; + *(pbuffer + 2) = + pdata->firmware__enable & 0x1; + *(pbuffer + 3) = + pdata->system__interrupt_clear & 0x3; + *(pbuffer + 4) = + pdata->system__mode_start; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_system_control( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_system_control_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->power_management__go1_power_force = + (*(pbuffer + 0)) & 0x1; + pdata->system__stream_count_ctrl = + (*(pbuffer + 1)) & 0x1; + pdata->firmware__enable = + (*(pbuffer + 2)) & 0x1; + pdata->system__interrupt_clear = + (*(pbuffer + 3)) & 0x3; + pdata->system__mode_start = + (*(pbuffer + 4)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_system_control( + VL53L1_DEV Dev, + VL53L1_system_control_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_system_control( + pdata, + VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_POWER_MANAGEMENT__GO1_POWER_FORCE, + comms_buffer, + VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_system_control( + VL53L1_DEV Dev, + VL53L1_system_control_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_POWER_MANAGEMENT__GO1_POWER_FORCE, + comms_buffer, + VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_system_control( + VL53L1_SYSTEM_CONTROL_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_system_results( + VL53L1_system_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->result__interrupt_status & 0x3F; + *(pbuffer + 1) = + pdata->result__range_status; + *(pbuffer + 2) = + pdata->result__report_status & 0xF; + *(pbuffer + 3) = + pdata->result__stream_count; + VL53L1_i2c_encode_uint16_t( + pdata->result__dss_actual_effective_spads_sd0, + 2, + pbuffer + 4); + VL53L1_i2c_encode_uint16_t( + pdata->result__peak_signal_count_rate_mcps_sd0, + 2, + pbuffer + 6); + VL53L1_i2c_encode_uint16_t( + pdata->result__ambient_count_rate_mcps_sd0, + 2, + pbuffer + 8); + VL53L1_i2c_encode_uint16_t( + pdata->result__sigma_sd0, + 2, + pbuffer + 10); + VL53L1_i2c_encode_uint16_t( + pdata->result__phase_sd0, + 2, + pbuffer + 12); + VL53L1_i2c_encode_uint16_t( + pdata->result__final_crosstalk_corrected_range_mm_sd0, + 2, + pbuffer + 14); + VL53L1_i2c_encode_uint16_t( + pdata->result__peak_signal_count_rate_crosstalk_corrected_mcps_sd0, + 2, + pbuffer + 16); + VL53L1_i2c_encode_uint16_t( + pdata->result__mm_inner_actual_effective_spads_sd0, + 2, + pbuffer + 18); + VL53L1_i2c_encode_uint16_t( + pdata->result__mm_outer_actual_effective_spads_sd0, + 2, + pbuffer + 20); + VL53L1_i2c_encode_uint16_t( + pdata->result__avg_signal_count_rate_mcps_sd0, + 2, + pbuffer + 22); + VL53L1_i2c_encode_uint16_t( + pdata->result__dss_actual_effective_spads_sd1, + 2, + pbuffer + 24); + VL53L1_i2c_encode_uint16_t( + pdata->result__peak_signal_count_rate_mcps_sd1, + 2, + pbuffer + 26); + VL53L1_i2c_encode_uint16_t( + pdata->result__ambient_count_rate_mcps_sd1, + 2, + pbuffer + 28); + VL53L1_i2c_encode_uint16_t( + pdata->result__sigma_sd1, + 2, + pbuffer + 30); + VL53L1_i2c_encode_uint16_t( + pdata->result__phase_sd1, + 2, + pbuffer + 32); + VL53L1_i2c_encode_uint16_t( + pdata->result__final_crosstalk_corrected_range_mm_sd1, + 2, + pbuffer + 34); + VL53L1_i2c_encode_uint16_t( + pdata->result__spare_0_sd1, + 2, + pbuffer + 36); + VL53L1_i2c_encode_uint16_t( + pdata->result__spare_1_sd1, + 2, + pbuffer + 38); + VL53L1_i2c_encode_uint16_t( + pdata->result__spare_2_sd1, + 2, + pbuffer + 40); + *(pbuffer + 42) = + pdata->result__spare_3_sd1; + *(pbuffer + 43) = + pdata->result__thresh_info; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_system_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_system_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->result__interrupt_status = + (*(pbuffer + 0)) & 0x3F; + pdata->result__range_status = + (*(pbuffer + 1)); + pdata->result__report_status = + (*(pbuffer + 2)) & 0xF; + pdata->result__stream_count = + (*(pbuffer + 3)); + pdata->result__dss_actual_effective_spads_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 4)); + pdata->result__peak_signal_count_rate_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 6)); + pdata->result__ambient_count_rate_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 8)); + pdata->result__sigma_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 10)); + pdata->result__phase_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 12)); + pdata->result__final_crosstalk_corrected_range_mm_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 14)); + pdata->result__peak_signal_count_rate_crosstalk_corrected_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 16)); + pdata->result__mm_inner_actual_effective_spads_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 18)); + pdata->result__mm_outer_actual_effective_spads_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 20)); + pdata->result__avg_signal_count_rate_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 22)); + pdata->result__dss_actual_effective_spads_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 24)); + pdata->result__peak_signal_count_rate_mcps_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 26)); + pdata->result__ambient_count_rate_mcps_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 28)); + pdata->result__sigma_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 30)); + pdata->result__phase_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 32)); + pdata->result__final_crosstalk_corrected_range_mm_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 34)); + pdata->result__spare_0_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 36)); + pdata->result__spare_1_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 38)); + pdata->result__spare_2_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 40)); + pdata->result__spare_3_sd1 = + (*(pbuffer + 42)); + pdata->result__thresh_info = + (*(pbuffer + 43)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_system_results( + VL53L1_DEV Dev, + VL53L1_system_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_system_results( + pdata, + VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_RESULT__INTERRUPT_STATUS, + comms_buffer, + VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_system_results( + VL53L1_DEV Dev, + VL53L1_system_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_RESULT__INTERRUPT_STATUS, + comms_buffer, + VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_system_results( + VL53L1_SYSTEM_RESULTS_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_core_results( + VL53L1_core_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_CORE_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + VL53L1_i2c_encode_uint32_t( + pdata->result_core__ambient_window_events_sd0, + 4, + pbuffer + 0); + VL53L1_i2c_encode_uint32_t( + pdata->result_core__ranging_total_events_sd0, + 4, + pbuffer + 4); + VL53L1_i2c_encode_int32_t( + pdata->result_core__signal_total_events_sd0, + 4, + pbuffer + 8); + VL53L1_i2c_encode_uint32_t( + pdata->result_core__total_periods_elapsed_sd0, + 4, + pbuffer + 12); + VL53L1_i2c_encode_uint32_t( + pdata->result_core__ambient_window_events_sd1, + 4, + pbuffer + 16); + VL53L1_i2c_encode_uint32_t( + pdata->result_core__ranging_total_events_sd1, + 4, + pbuffer + 20); + VL53L1_i2c_encode_int32_t( + pdata->result_core__signal_total_events_sd1, + 4, + pbuffer + 24); + VL53L1_i2c_encode_uint32_t( + pdata->result_core__total_periods_elapsed_sd1, + 4, + pbuffer + 28); + *(pbuffer + 32) = + pdata->result_core__spare_0; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_core_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_core_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_CORE_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->result_core__ambient_window_events_sd0 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 0)); + pdata->result_core__ranging_total_events_sd0 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 4)); + pdata->result_core__signal_total_events_sd0 = + (VL53L1_i2c_decode_int32_t(4, pbuffer + 8)); + pdata->result_core__total_periods_elapsed_sd0 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 12)); + pdata->result_core__ambient_window_events_sd1 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 16)); + pdata->result_core__ranging_total_events_sd1 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 20)); + pdata->result_core__signal_total_events_sd1 = + (VL53L1_i2c_decode_int32_t(4, pbuffer + 24)); + pdata->result_core__total_periods_elapsed_sd1 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 28)); + pdata->result_core__spare_0 = + (*(pbuffer + 32)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_core_results( + VL53L1_DEV Dev, + VL53L1_core_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_CORE_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_core_results( + pdata, + VL53L1_CORE_RESULTS_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0, + comms_buffer, + VL53L1_CORE_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_core_results( + VL53L1_DEV Dev, + VL53L1_core_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_CORE_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0, + comms_buffer, + VL53L1_CORE_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_core_results( + VL53L1_CORE_RESULTS_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_debug_results( + VL53L1_debug_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + VL53L1_i2c_encode_uint16_t( + pdata->phasecal_result__reference_phase, + 2, + pbuffer + 0); + *(pbuffer + 2) = + pdata->phasecal_result__vcsel_start & 0x7F; + *(pbuffer + 3) = + pdata->ref_spad_char_result__num_actual_ref_spads & 0x3F; + *(pbuffer + 4) = + pdata->ref_spad_char_result__ref_location & 0x3; + *(pbuffer + 5) = + pdata->vhv_result__coldboot_status & 0x1; + *(pbuffer + 6) = + pdata->vhv_result__search_result & 0x3F; + *(pbuffer + 7) = + pdata->vhv_result__latest_setting & 0x3F; + VL53L1_i2c_encode_uint16_t( + pdata->result__osc_calibrate_val & 0x3FF, + 2, + pbuffer + 8); + *(pbuffer + 10) = + pdata->ana_config__powerdown_go1 & 0x3; + *(pbuffer + 11) = + pdata->ana_config__ref_bg_ctrl & 0x3; + *(pbuffer + 12) = + pdata->ana_config__regdvdd1v2_ctrl & 0xF; + *(pbuffer + 13) = + pdata->ana_config__osc_slow_ctrl & 0x7; + *(pbuffer + 14) = + pdata->test_mode__status & 0x1; + *(pbuffer + 15) = + pdata->firmware__system_status & 0x3; + *(pbuffer + 16) = + pdata->firmware__mode_status; + *(pbuffer + 17) = + pdata->firmware__secondary_mode_status; + VL53L1_i2c_encode_uint16_t( + pdata->firmware__cal_repeat_rate_counter & 0xFFF, + 2, + pbuffer + 18); + VL53L1_i2c_encode_uint16_t( + pdata->gph__system__thresh_high, + 2, + pbuffer + 22); + VL53L1_i2c_encode_uint16_t( + pdata->gph__system__thresh_low, + 2, + pbuffer + 24); + *(pbuffer + 26) = + pdata->gph__system__enable_xtalk_per_quadrant & 0x1; + *(pbuffer + 27) = + pdata->gph__spare_0 & 0x7; + *(pbuffer + 28) = + pdata->gph__sd_config__woi_sd0; + *(pbuffer + 29) = + pdata->gph__sd_config__woi_sd1; + *(pbuffer + 30) = + pdata->gph__sd_config__initial_phase_sd0 & 0x7F; + *(pbuffer + 31) = + pdata->gph__sd_config__initial_phase_sd1 & 0x7F; + *(pbuffer + 32) = + pdata->gph__sd_config__first_order_select & 0x3; + *(pbuffer + 33) = + pdata->gph__sd_config__quantifier & 0xF; + *(pbuffer + 34) = + pdata->gph__roi_config__user_roi_centre_spad; + *(pbuffer + 35) = + pdata->gph__roi_config__user_roi_requested_global_xy_size; + *(pbuffer + 36) = + pdata->gph__system__sequence_config; + *(pbuffer + 37) = + pdata->gph__gph_id & 0x1; + *(pbuffer + 38) = + pdata->system__interrupt_set & 0x3; + *(pbuffer + 39) = + pdata->interrupt_manager__enables & 0x1F; + *(pbuffer + 40) = + pdata->interrupt_manager__clear & 0x1F; + *(pbuffer + 41) = + pdata->interrupt_manager__status & 0x1F; + *(pbuffer + 42) = + pdata->mcu_to_host_bank__wr_access_en & 0x1; + *(pbuffer + 43) = + pdata->power_management__go1_reset_status & 0x1; + *(pbuffer + 44) = + pdata->pad_startup_mode__value_ro & 0x3; + *(pbuffer + 45) = + pdata->pad_startup_mode__value_ctrl & 0x3F; + VL53L1_i2c_encode_uint32_t( + pdata->pll_period_us & 0x3FFFF, + 4, + pbuffer + 46); + VL53L1_i2c_encode_uint32_t( + pdata->interrupt_scheduler__data_out, + 4, + pbuffer + 50); + *(pbuffer + 54) = + pdata->nvm_bist__complete & 0x1; + *(pbuffer + 55) = + pdata->nvm_bist__status & 0x1; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_debug_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_debug_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->phasecal_result__reference_phase = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 0)); + pdata->phasecal_result__vcsel_start = + (*(pbuffer + 2)) & 0x7F; + pdata->ref_spad_char_result__num_actual_ref_spads = + (*(pbuffer + 3)) & 0x3F; + pdata->ref_spad_char_result__ref_location = + (*(pbuffer + 4)) & 0x3; + pdata->vhv_result__coldboot_status = + (*(pbuffer + 5)) & 0x1; + pdata->vhv_result__search_result = + (*(pbuffer + 6)) & 0x3F; + pdata->vhv_result__latest_setting = + (*(pbuffer + 7)) & 0x3F; + pdata->result__osc_calibrate_val = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 8)) & 0x3FF; + pdata->ana_config__powerdown_go1 = + (*(pbuffer + 10)) & 0x3; + pdata->ana_config__ref_bg_ctrl = + (*(pbuffer + 11)) & 0x3; + pdata->ana_config__regdvdd1v2_ctrl = + (*(pbuffer + 12)) & 0xF; + pdata->ana_config__osc_slow_ctrl = + (*(pbuffer + 13)) & 0x7; + pdata->test_mode__status = + (*(pbuffer + 14)) & 0x1; + pdata->firmware__system_status = + (*(pbuffer + 15)) & 0x3; + pdata->firmware__mode_status = + (*(pbuffer + 16)); + pdata->firmware__secondary_mode_status = + (*(pbuffer + 17)); + pdata->firmware__cal_repeat_rate_counter = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 18)) & 0xFFF; + pdata->gph__system__thresh_high = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 22)); + pdata->gph__system__thresh_low = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 24)); + pdata->gph__system__enable_xtalk_per_quadrant = + (*(pbuffer + 26)) & 0x1; + pdata->gph__spare_0 = + (*(pbuffer + 27)) & 0x7; + pdata->gph__sd_config__woi_sd0 = + (*(pbuffer + 28)); + pdata->gph__sd_config__woi_sd1 = + (*(pbuffer + 29)); + pdata->gph__sd_config__initial_phase_sd0 = + (*(pbuffer + 30)) & 0x7F; + pdata->gph__sd_config__initial_phase_sd1 = + (*(pbuffer + 31)) & 0x7F; + pdata->gph__sd_config__first_order_select = + (*(pbuffer + 32)) & 0x3; + pdata->gph__sd_config__quantifier = + (*(pbuffer + 33)) & 0xF; + pdata->gph__roi_config__user_roi_centre_spad = + (*(pbuffer + 34)); + pdata->gph__roi_config__user_roi_requested_global_xy_size = + (*(pbuffer + 35)); + pdata->gph__system__sequence_config = + (*(pbuffer + 36)); + pdata->gph__gph_id = + (*(pbuffer + 37)) & 0x1; + pdata->system__interrupt_set = + (*(pbuffer + 38)) & 0x3; + pdata->interrupt_manager__enables = + (*(pbuffer + 39)) & 0x1F; + pdata->interrupt_manager__clear = + (*(pbuffer + 40)) & 0x1F; + pdata->interrupt_manager__status = + (*(pbuffer + 41)) & 0x1F; + pdata->mcu_to_host_bank__wr_access_en = + (*(pbuffer + 42)) & 0x1; + pdata->power_management__go1_reset_status = + (*(pbuffer + 43)) & 0x1; + pdata->pad_startup_mode__value_ro = + (*(pbuffer + 44)) & 0x3; + pdata->pad_startup_mode__value_ctrl = + (*(pbuffer + 45)) & 0x3F; + pdata->pll_period_us = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 46)) & 0x3FFFF; + pdata->interrupt_scheduler__data_out = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 50)); + pdata->nvm_bist__complete = + (*(pbuffer + 54)) & 0x1; + pdata->nvm_bist__status = + (*(pbuffer + 55)) & 0x1; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_debug_results( + VL53L1_DEV Dev, + VL53L1_debug_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_debug_results( + pdata, + VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_PHASECAL_RESULT__REFERENCE_PHASE, + comms_buffer, + VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_debug_results( + VL53L1_DEV Dev, + VL53L1_debug_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_PHASECAL_RESULT__REFERENCE_PHASE, + comms_buffer, + VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_debug_results( + VL53L1_DEBUG_RESULTS_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_nvm_copy_data( + VL53L1_nvm_copy_data_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_NVM_COPY_DATA_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->identification__model_id; + *(pbuffer + 1) = + pdata->identification__module_type; + *(pbuffer + 2) = + pdata->identification__revision_id; + VL53L1_i2c_encode_uint16_t( + pdata->identification__module_id, + 2, + pbuffer + 3); + *(pbuffer + 5) = + pdata->ana_config__fast_osc__trim_max & 0x7F; + *(pbuffer + 6) = + pdata->ana_config__fast_osc__freq_set & 0x7; + *(pbuffer + 7) = + pdata->ana_config__vcsel_trim & 0x7; + *(pbuffer + 8) = + pdata->ana_config__vcsel_selion & 0x3F; + *(pbuffer + 9) = + pdata->ana_config__vcsel_selion_max & 0x3F; + *(pbuffer + 10) = + pdata->protected_laser_safety__lock_bit & 0x1; + *(pbuffer + 11) = + pdata->laser_safety__key & 0x7F; + *(pbuffer + 12) = + pdata->laser_safety__key_ro & 0x1; + *(pbuffer + 13) = + pdata->laser_safety__clip & 0x3F; + *(pbuffer + 14) = + pdata->laser_safety__mult & 0x3F; + *(pbuffer + 15) = + pdata->global_config__spad_enables_rtn_0; + *(pbuffer + 16) = + pdata->global_config__spad_enables_rtn_1; + *(pbuffer + 17) = + pdata->global_config__spad_enables_rtn_2; + *(pbuffer + 18) = + pdata->global_config__spad_enables_rtn_3; + *(pbuffer + 19) = + pdata->global_config__spad_enables_rtn_4; + *(pbuffer + 20) = + pdata->global_config__spad_enables_rtn_5; + *(pbuffer + 21) = + pdata->global_config__spad_enables_rtn_6; + *(pbuffer + 22) = + pdata->global_config__spad_enables_rtn_7; + *(pbuffer + 23) = + pdata->global_config__spad_enables_rtn_8; + *(pbuffer + 24) = + pdata->global_config__spad_enables_rtn_9; + *(pbuffer + 25) = + pdata->global_config__spad_enables_rtn_10; + *(pbuffer + 26) = + pdata->global_config__spad_enables_rtn_11; + *(pbuffer + 27) = + pdata->global_config__spad_enables_rtn_12; + *(pbuffer + 28) = + pdata->global_config__spad_enables_rtn_13; + *(pbuffer + 29) = + pdata->global_config__spad_enables_rtn_14; + *(pbuffer + 30) = + pdata->global_config__spad_enables_rtn_15; + *(pbuffer + 31) = + pdata->global_config__spad_enables_rtn_16; + *(pbuffer + 32) = + pdata->global_config__spad_enables_rtn_17; + *(pbuffer + 33) = + pdata->global_config__spad_enables_rtn_18; + *(pbuffer + 34) = + pdata->global_config__spad_enables_rtn_19; + *(pbuffer + 35) = + pdata->global_config__spad_enables_rtn_20; + *(pbuffer + 36) = + pdata->global_config__spad_enables_rtn_21; + *(pbuffer + 37) = + pdata->global_config__spad_enables_rtn_22; + *(pbuffer + 38) = + pdata->global_config__spad_enables_rtn_23; + *(pbuffer + 39) = + pdata->global_config__spad_enables_rtn_24; + *(pbuffer + 40) = + pdata->global_config__spad_enables_rtn_25; + *(pbuffer + 41) = + pdata->global_config__spad_enables_rtn_26; + *(pbuffer + 42) = + pdata->global_config__spad_enables_rtn_27; + *(pbuffer + 43) = + pdata->global_config__spad_enables_rtn_28; + *(pbuffer + 44) = + pdata->global_config__spad_enables_rtn_29; + *(pbuffer + 45) = + pdata->global_config__spad_enables_rtn_30; + *(pbuffer + 46) = + pdata->global_config__spad_enables_rtn_31; + *(pbuffer + 47) = + pdata->roi_config__mode_roi_centre_spad; + *(pbuffer + 48) = + pdata->roi_config__mode_roi_xy_size; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_nvm_copy_data( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_nvm_copy_data_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_NVM_COPY_DATA_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->identification__model_id = + (*(pbuffer + 0)); + pdata->identification__module_type = + (*(pbuffer + 1)); + pdata->identification__revision_id = + (*(pbuffer + 2)); + pdata->identification__module_id = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 3)); + pdata->ana_config__fast_osc__trim_max = + (*(pbuffer + 5)) & 0x7F; + pdata->ana_config__fast_osc__freq_set = + (*(pbuffer + 6)) & 0x7; + pdata->ana_config__vcsel_trim = + (*(pbuffer + 7)) & 0x7; + pdata->ana_config__vcsel_selion = + (*(pbuffer + 8)) & 0x3F; + pdata->ana_config__vcsel_selion_max = + (*(pbuffer + 9)) & 0x3F; + pdata->protected_laser_safety__lock_bit = + (*(pbuffer + 10)) & 0x1; + pdata->laser_safety__key = + (*(pbuffer + 11)) & 0x7F; + pdata->laser_safety__key_ro = + (*(pbuffer + 12)) & 0x1; + pdata->laser_safety__clip = + (*(pbuffer + 13)) & 0x3F; + pdata->laser_safety__mult = + (*(pbuffer + 14)) & 0x3F; + pdata->global_config__spad_enables_rtn_0 = + (*(pbuffer + 15)); + pdata->global_config__spad_enables_rtn_1 = + (*(pbuffer + 16)); + pdata->global_config__spad_enables_rtn_2 = + (*(pbuffer + 17)); + pdata->global_config__spad_enables_rtn_3 = + (*(pbuffer + 18)); + pdata->global_config__spad_enables_rtn_4 = + (*(pbuffer + 19)); + pdata->global_config__spad_enables_rtn_5 = + (*(pbuffer + 20)); + pdata->global_config__spad_enables_rtn_6 = + (*(pbuffer + 21)); + pdata->global_config__spad_enables_rtn_7 = + (*(pbuffer + 22)); + pdata->global_config__spad_enables_rtn_8 = + (*(pbuffer + 23)); + pdata->global_config__spad_enables_rtn_9 = + (*(pbuffer + 24)); + pdata->global_config__spad_enables_rtn_10 = + (*(pbuffer + 25)); + pdata->global_config__spad_enables_rtn_11 = + (*(pbuffer + 26)); + pdata->global_config__spad_enables_rtn_12 = + (*(pbuffer + 27)); + pdata->global_config__spad_enables_rtn_13 = + (*(pbuffer + 28)); + pdata->global_config__spad_enables_rtn_14 = + (*(pbuffer + 29)); + pdata->global_config__spad_enables_rtn_15 = + (*(pbuffer + 30)); + pdata->global_config__spad_enables_rtn_16 = + (*(pbuffer + 31)); + pdata->global_config__spad_enables_rtn_17 = + (*(pbuffer + 32)); + pdata->global_config__spad_enables_rtn_18 = + (*(pbuffer + 33)); + pdata->global_config__spad_enables_rtn_19 = + (*(pbuffer + 34)); + pdata->global_config__spad_enables_rtn_20 = + (*(pbuffer + 35)); + pdata->global_config__spad_enables_rtn_21 = + (*(pbuffer + 36)); + pdata->global_config__spad_enables_rtn_22 = + (*(pbuffer + 37)); + pdata->global_config__spad_enables_rtn_23 = + (*(pbuffer + 38)); + pdata->global_config__spad_enables_rtn_24 = + (*(pbuffer + 39)); + pdata->global_config__spad_enables_rtn_25 = + (*(pbuffer + 40)); + pdata->global_config__spad_enables_rtn_26 = + (*(pbuffer + 41)); + pdata->global_config__spad_enables_rtn_27 = + (*(pbuffer + 42)); + pdata->global_config__spad_enables_rtn_28 = + (*(pbuffer + 43)); + pdata->global_config__spad_enables_rtn_29 = + (*(pbuffer + 44)); + pdata->global_config__spad_enables_rtn_30 = + (*(pbuffer + 45)); + pdata->global_config__spad_enables_rtn_31 = + (*(pbuffer + 46)); + pdata->roi_config__mode_roi_centre_spad = + (*(pbuffer + 47)); + pdata->roi_config__mode_roi_xy_size = + (*(pbuffer + 48)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_nvm_copy_data( + VL53L1_DEV Dev, + VL53L1_nvm_copy_data_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_NVM_COPY_DATA_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_nvm_copy_data( + pdata, + VL53L1_NVM_COPY_DATA_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_IDENTIFICATION__MODEL_ID, + comms_buffer, + VL53L1_NVM_COPY_DATA_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_nvm_copy_data( + VL53L1_DEV Dev, + VL53L1_nvm_copy_data_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_NVM_COPY_DATA_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_IDENTIFICATION__MODEL_ID, + comms_buffer, + VL53L1_NVM_COPY_DATA_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_nvm_copy_data( + VL53L1_NVM_COPY_DATA_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_prev_shadow_system_results( + VL53L1_prev_shadow_system_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->prev_shadow_result__interrupt_status & 0x3F; + *(pbuffer + 1) = + pdata->prev_shadow_result__range_status; + *(pbuffer + 2) = + pdata->prev_shadow_result__report_status & 0xF; + *(pbuffer + 3) = + pdata->prev_shadow_result__stream_count; + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__dss_actual_effective_spads_sd0, + 2, + pbuffer + 4); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__peak_signal_count_rate_mcps_sd0, + 2, + pbuffer + 6); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__ambient_count_rate_mcps_sd0, + 2, + pbuffer + 8); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__sigma_sd0, + 2, + pbuffer + 10); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__phase_sd0, + 2, + pbuffer + 12); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__final_crosstalk_corrected_range_mm_sd0, + 2, + pbuffer + 14); + VL53L1_i2c_encode_uint16_t( + pdata->psr__peak_signal_count_rate_crosstalk_corrected_mcps_sd0, + 2, + pbuffer + 16); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__mm_inner_actual_effective_spads_sd0, + 2, + pbuffer + 18); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__mm_outer_actual_effective_spads_sd0, + 2, + pbuffer + 20); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__avg_signal_count_rate_mcps_sd0, + 2, + pbuffer + 22); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__dss_actual_effective_spads_sd1, + 2, + pbuffer + 24); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__peak_signal_count_rate_mcps_sd1, + 2, + pbuffer + 26); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__ambient_count_rate_mcps_sd1, + 2, + pbuffer + 28); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__sigma_sd1, + 2, + pbuffer + 30); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__phase_sd1, + 2, + pbuffer + 32); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__final_crosstalk_corrected_range_mm_sd1, + 2, + pbuffer + 34); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__spare_0_sd1, + 2, + pbuffer + 36); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__spare_1_sd1, + 2, + pbuffer + 38); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__spare_2_sd1, + 2, + pbuffer + 40); + VL53L1_i2c_encode_uint16_t( + pdata->prev_shadow_result__spare_3_sd1, + 2, + pbuffer + 42); + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_prev_shadow_system_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_prev_shadow_system_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->prev_shadow_result__interrupt_status = + (*(pbuffer + 0)) & 0x3F; + pdata->prev_shadow_result__range_status = + (*(pbuffer + 1)); + pdata->prev_shadow_result__report_status = + (*(pbuffer + 2)) & 0xF; + pdata->prev_shadow_result__stream_count = + (*(pbuffer + 3)); + pdata->prev_shadow_result__dss_actual_effective_spads_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 4)); + pdata->prev_shadow_result__peak_signal_count_rate_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 6)); + pdata->prev_shadow_result__ambient_count_rate_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 8)); + pdata->prev_shadow_result__sigma_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 10)); + pdata->prev_shadow_result__phase_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 12)); + pdata->prev_shadow_result__final_crosstalk_corrected_range_mm_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 14)); + pdata->psr__peak_signal_count_rate_crosstalk_corrected_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 16)); + pdata->prev_shadow_result__mm_inner_actual_effective_spads_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 18)); + pdata->prev_shadow_result__mm_outer_actual_effective_spads_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 20)); + pdata->prev_shadow_result__avg_signal_count_rate_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 22)); + pdata->prev_shadow_result__dss_actual_effective_spads_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 24)); + pdata->prev_shadow_result__peak_signal_count_rate_mcps_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 26)); + pdata->prev_shadow_result__ambient_count_rate_mcps_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 28)); + pdata->prev_shadow_result__sigma_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 30)); + pdata->prev_shadow_result__phase_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 32)); + pdata->prev_shadow_result__final_crosstalk_corrected_range_mm_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 34)); + pdata->prev_shadow_result__spare_0_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 36)); + pdata->prev_shadow_result__spare_1_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 38)); + pdata->prev_shadow_result__spare_2_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 40)); + pdata->prev_shadow_result__spare_3_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 42)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_prev_shadow_system_results( + VL53L1_DEV Dev, + VL53L1_prev_shadow_system_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_prev_shadow_system_results( + pdata, + VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_PREV_SHADOW_RESULT__INTERRUPT_STATUS, + comms_buffer, + VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_prev_shadow_system_results( + VL53L1_DEV Dev, + VL53L1_prev_shadow_system_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_PREV_SHADOW_RESULT__INTERRUPT_STATUS, + comms_buffer, + VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_prev_shadow_system_results( + VL53L1_PREV_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_prev_shadow_core_results( + VL53L1_prev_shadow_core_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + VL53L1_i2c_encode_uint32_t( + pdata->prev_shadow_result_core__ambient_window_events_sd0, + 4, + pbuffer + 0); + VL53L1_i2c_encode_uint32_t( + pdata->prev_shadow_result_core__ranging_total_events_sd0, + 4, + pbuffer + 4); + VL53L1_i2c_encode_int32_t( + pdata->prev_shadow_result_core__signal_total_events_sd0, + 4, + pbuffer + 8); + VL53L1_i2c_encode_uint32_t( + pdata->prev_shadow_result_core__total_periods_elapsed_sd0, + 4, + pbuffer + 12); + VL53L1_i2c_encode_uint32_t( + pdata->prev_shadow_result_core__ambient_window_events_sd1, + 4, + pbuffer + 16); + VL53L1_i2c_encode_uint32_t( + pdata->prev_shadow_result_core__ranging_total_events_sd1, + 4, + pbuffer + 20); + VL53L1_i2c_encode_int32_t( + pdata->prev_shadow_result_core__signal_total_events_sd1, + 4, + pbuffer + 24); + VL53L1_i2c_encode_uint32_t( + pdata->prev_shadow_result_core__total_periods_elapsed_sd1, + 4, + pbuffer + 28); + *(pbuffer + 32) = + pdata->prev_shadow_result_core__spare_0; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_prev_shadow_core_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_prev_shadow_core_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->prev_shadow_result_core__ambient_window_events_sd0 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 0)); + pdata->prev_shadow_result_core__ranging_total_events_sd0 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 4)); + pdata->prev_shadow_result_core__signal_total_events_sd0 = + (VL53L1_i2c_decode_int32_t(4, pbuffer + 8)); + pdata->prev_shadow_result_core__total_periods_elapsed_sd0 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 12)); + pdata->prev_shadow_result_core__ambient_window_events_sd1 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 16)); + pdata->prev_shadow_result_core__ranging_total_events_sd1 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 20)); + pdata->prev_shadow_result_core__signal_total_events_sd1 = + (VL53L1_i2c_decode_int32_t(4, pbuffer + 24)); + pdata->prev_shadow_result_core__total_periods_elapsed_sd1 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 28)); + pdata->prev_shadow_result_core__spare_0 = + (*(pbuffer + 32)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_prev_shadow_core_results( + VL53L1_DEV Dev, + VL53L1_prev_shadow_core_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_prev_shadow_core_results( + pdata, + VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0, + comms_buffer, + VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_prev_shadow_core_results( + VL53L1_DEV Dev, + VL53L1_prev_shadow_core_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_PREV_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0, + comms_buffer, + VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_prev_shadow_core_results( + VL53L1_PREV_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_patch_debug( + VL53L1_patch_debug_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_PATCH_DEBUG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->result__debug_status; + *(pbuffer + 1) = + pdata->result__debug_stage; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_patch_debug( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_patch_debug_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_PATCH_DEBUG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->result__debug_status = + (*(pbuffer + 0)); + pdata->result__debug_stage = + (*(pbuffer + 1)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_patch_debug( + VL53L1_DEV Dev, + VL53L1_patch_debug_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_PATCH_DEBUG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_patch_debug( + pdata, + VL53L1_PATCH_DEBUG_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_RESULT__DEBUG_STATUS, + comms_buffer, + VL53L1_PATCH_DEBUG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_patch_debug( + VL53L1_DEV Dev, + VL53L1_patch_debug_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_PATCH_DEBUG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_RESULT__DEBUG_STATUS, + comms_buffer, + VL53L1_PATCH_DEBUG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_patch_debug( + VL53L1_PATCH_DEBUG_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_gph_general_config( + VL53L1_gph_general_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_GPH_GENERAL_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + VL53L1_i2c_encode_uint16_t( + pdata->gph__system__thresh_rate_high, + 2, + pbuffer + 0); + VL53L1_i2c_encode_uint16_t( + pdata->gph__system__thresh_rate_low, + 2, + pbuffer + 2); + *(pbuffer + 4) = + pdata->gph__system__interrupt_config_gpio; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_gph_general_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_gph_general_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_GPH_GENERAL_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->gph__system__thresh_rate_high = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 0)); + pdata->gph__system__thresh_rate_low = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 2)); + pdata->gph__system__interrupt_config_gpio = + (*(pbuffer + 4)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_gph_general_config( + VL53L1_DEV Dev, + VL53L1_gph_general_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_GPH_GENERAL_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_gph_general_config( + pdata, + VL53L1_GPH_GENERAL_CONFIG_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_GPH__SYSTEM__THRESH_RATE_HIGH, + comms_buffer, + VL53L1_GPH_GENERAL_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_gph_general_config( + VL53L1_DEV Dev, + VL53L1_gph_general_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_GPH_GENERAL_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_GPH__SYSTEM__THRESH_RATE_HIGH, + comms_buffer, + VL53L1_GPH_GENERAL_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_gph_general_config( + VL53L1_GPH_GENERAL_CONFIG_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_gph_static_config( + VL53L1_gph_static_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_GPH_STATIC_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->gph__dss_config__roi_mode_control & 0x7; + VL53L1_i2c_encode_uint16_t( + pdata->gph__dss_config__manual_effective_spads_select, + 2, + pbuffer + 1); + *(pbuffer + 3) = + pdata->gph__dss_config__manual_block_select; + *(pbuffer + 4) = + pdata->gph__dss_config__max_spads_limit; + *(pbuffer + 5) = + pdata->gph__dss_config__min_spads_limit; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_gph_static_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_gph_static_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_GPH_STATIC_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->gph__dss_config__roi_mode_control = + (*(pbuffer + 0)) & 0x7; + pdata->gph__dss_config__manual_effective_spads_select = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 1)); + pdata->gph__dss_config__manual_block_select = + (*(pbuffer + 3)); + pdata->gph__dss_config__max_spads_limit = + (*(pbuffer + 4)); + pdata->gph__dss_config__min_spads_limit = + (*(pbuffer + 5)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_gph_static_config( + VL53L1_DEV Dev, + VL53L1_gph_static_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_GPH_STATIC_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_gph_static_config( + pdata, + VL53L1_GPH_STATIC_CONFIG_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_GPH__DSS_CONFIG__ROI_MODE_CONTROL, + comms_buffer, + VL53L1_GPH_STATIC_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_gph_static_config( + VL53L1_DEV Dev, + VL53L1_gph_static_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_GPH_STATIC_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_GPH__DSS_CONFIG__ROI_MODE_CONTROL, + comms_buffer, + VL53L1_GPH_STATIC_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_gph_static_config( + VL53L1_GPH_STATIC_CONFIG_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_gph_timing_config( + VL53L1_gph_timing_config_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_GPH_TIMING_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->gph__mm_config__timeout_macrop_a_hi & 0xF; + *(pbuffer + 1) = + pdata->gph__mm_config__timeout_macrop_a_lo; + *(pbuffer + 2) = + pdata->gph__mm_config__timeout_macrop_b_hi & 0xF; + *(pbuffer + 3) = + pdata->gph__mm_config__timeout_macrop_b_lo; + *(pbuffer + 4) = + pdata->gph__range_config__timeout_macrop_a_hi & 0xF; + *(pbuffer + 5) = + pdata->gph__range_config__timeout_macrop_a_lo; + *(pbuffer + 6) = + pdata->gph__range_config__vcsel_period_a & 0x3F; + *(pbuffer + 7) = + pdata->gph__range_config__vcsel_period_b & 0x3F; + *(pbuffer + 8) = + pdata->gph__range_config__timeout_macrop_b_hi & 0xF; + *(pbuffer + 9) = + pdata->gph__range_config__timeout_macrop_b_lo; + VL53L1_i2c_encode_uint16_t( + pdata->gph__range_config__sigma_thresh, + 2, + pbuffer + 10); + VL53L1_i2c_encode_uint16_t( + pdata->gph__range_config__min_count_rate_rtn_limit_mcps, + 2, + pbuffer + 12); + *(pbuffer + 14) = + pdata->gph__range_config__valid_phase_low; + *(pbuffer + 15) = + pdata->gph__range_config__valid_phase_high; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_gph_timing_config( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_gph_timing_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_GPH_TIMING_CONFIG_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->gph__mm_config__timeout_macrop_a_hi = + (*(pbuffer + 0)) & 0xF; + pdata->gph__mm_config__timeout_macrop_a_lo = + (*(pbuffer + 1)); + pdata->gph__mm_config__timeout_macrop_b_hi = + (*(pbuffer + 2)) & 0xF; + pdata->gph__mm_config__timeout_macrop_b_lo = + (*(pbuffer + 3)); + pdata->gph__range_config__timeout_macrop_a_hi = + (*(pbuffer + 4)) & 0xF; + pdata->gph__range_config__timeout_macrop_a_lo = + (*(pbuffer + 5)); + pdata->gph__range_config__vcsel_period_a = + (*(pbuffer + 6)) & 0x3F; + pdata->gph__range_config__vcsel_period_b = + (*(pbuffer + 7)) & 0x3F; + pdata->gph__range_config__timeout_macrop_b_hi = + (*(pbuffer + 8)) & 0xF; + pdata->gph__range_config__timeout_macrop_b_lo = + (*(pbuffer + 9)); + pdata->gph__range_config__sigma_thresh = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 10)); + pdata->gph__range_config__min_count_rate_rtn_limit_mcps = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 12)); + pdata->gph__range_config__valid_phase_low = + (*(pbuffer + 14)); + pdata->gph__range_config__valid_phase_high = + (*(pbuffer + 15)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_gph_timing_config( + VL53L1_DEV Dev, + VL53L1_gph_timing_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_GPH_TIMING_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_gph_timing_config( + pdata, + VL53L1_GPH_TIMING_CONFIG_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_GPH__MM_CONFIG__TIMEOUT_MACROP_A_HI, + comms_buffer, + VL53L1_GPH_TIMING_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_gph_timing_config( + VL53L1_DEV Dev, + VL53L1_gph_timing_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_GPH_TIMING_CONFIG_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_GPH__MM_CONFIG__TIMEOUT_MACROP_A_HI, + comms_buffer, + VL53L1_GPH_TIMING_CONFIG_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_gph_timing_config( + VL53L1_GPH_TIMING_CONFIG_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_fw_internal( + VL53L1_fw_internal_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_FW_INTERNAL_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->firmware__internal_stream_count_div; + *(pbuffer + 1) = + pdata->firmware__internal_stream_counter_val; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_fw_internal( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_fw_internal_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_FW_INTERNAL_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->firmware__internal_stream_count_div = + (*(pbuffer + 0)); + pdata->firmware__internal_stream_counter_val = + (*(pbuffer + 1)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_fw_internal( + VL53L1_DEV Dev, + VL53L1_fw_internal_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_FW_INTERNAL_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_fw_internal( + pdata, + VL53L1_FW_INTERNAL_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_FIRMWARE__INTERNAL_STREAM_COUNT_DIV, + comms_buffer, + VL53L1_FW_INTERNAL_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_fw_internal( + VL53L1_DEV Dev, + VL53L1_fw_internal_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_FW_INTERNAL_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_FIRMWARE__INTERNAL_STREAM_COUNT_DIV, + comms_buffer, + VL53L1_FW_INTERNAL_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_fw_internal( + VL53L1_FW_INTERNAL_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_patch_results( + VL53L1_patch_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_PATCH_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->dss_calc__roi_ctrl & 0x3; + *(pbuffer + 1) = + pdata->dss_calc__spare_1; + *(pbuffer + 2) = + pdata->dss_calc__spare_2; + *(pbuffer + 3) = + pdata->dss_calc__spare_3; + *(pbuffer + 4) = + pdata->dss_calc__spare_4; + *(pbuffer + 5) = + pdata->dss_calc__spare_5; + *(pbuffer + 6) = + pdata->dss_calc__spare_6; + *(pbuffer + 7) = + pdata->dss_calc__spare_7; + *(pbuffer + 8) = + pdata->dss_calc__user_roi_spad_en_0; + *(pbuffer + 9) = + pdata->dss_calc__user_roi_spad_en_1; + *(pbuffer + 10) = + pdata->dss_calc__user_roi_spad_en_2; + *(pbuffer + 11) = + pdata->dss_calc__user_roi_spad_en_3; + *(pbuffer + 12) = + pdata->dss_calc__user_roi_spad_en_4; + *(pbuffer + 13) = + pdata->dss_calc__user_roi_spad_en_5; + *(pbuffer + 14) = + pdata->dss_calc__user_roi_spad_en_6; + *(pbuffer + 15) = + pdata->dss_calc__user_roi_spad_en_7; + *(pbuffer + 16) = + pdata->dss_calc__user_roi_spad_en_8; + *(pbuffer + 17) = + pdata->dss_calc__user_roi_spad_en_9; + *(pbuffer + 18) = + pdata->dss_calc__user_roi_spad_en_10; + *(pbuffer + 19) = + pdata->dss_calc__user_roi_spad_en_11; + *(pbuffer + 20) = + pdata->dss_calc__user_roi_spad_en_12; + *(pbuffer + 21) = + pdata->dss_calc__user_roi_spad_en_13; + *(pbuffer + 22) = + pdata->dss_calc__user_roi_spad_en_14; + *(pbuffer + 23) = + pdata->dss_calc__user_roi_spad_en_15; + *(pbuffer + 24) = + pdata->dss_calc__user_roi_spad_en_16; + *(pbuffer + 25) = + pdata->dss_calc__user_roi_spad_en_17; + *(pbuffer + 26) = + pdata->dss_calc__user_roi_spad_en_18; + *(pbuffer + 27) = + pdata->dss_calc__user_roi_spad_en_19; + *(pbuffer + 28) = + pdata->dss_calc__user_roi_spad_en_20; + *(pbuffer + 29) = + pdata->dss_calc__user_roi_spad_en_21; + *(pbuffer + 30) = + pdata->dss_calc__user_roi_spad_en_22; + *(pbuffer + 31) = + pdata->dss_calc__user_roi_spad_en_23; + *(pbuffer + 32) = + pdata->dss_calc__user_roi_spad_en_24; + *(pbuffer + 33) = + pdata->dss_calc__user_roi_spad_en_25; + *(pbuffer + 34) = + pdata->dss_calc__user_roi_spad_en_26; + *(pbuffer + 35) = + pdata->dss_calc__user_roi_spad_en_27; + *(pbuffer + 36) = + pdata->dss_calc__user_roi_spad_en_28; + *(pbuffer + 37) = + pdata->dss_calc__user_roi_spad_en_29; + *(pbuffer + 38) = + pdata->dss_calc__user_roi_spad_en_30; + *(pbuffer + 39) = + pdata->dss_calc__user_roi_spad_en_31; + *(pbuffer + 40) = + pdata->dss_calc__user_roi_0; + *(pbuffer + 41) = + pdata->dss_calc__user_roi_1; + *(pbuffer + 42) = + pdata->dss_calc__mode_roi_0; + *(pbuffer + 43) = + pdata->dss_calc__mode_roi_1; + *(pbuffer + 44) = + pdata->sigma_estimator_calc__spare_0; + VL53L1_i2c_encode_uint16_t( + pdata->vhv_result__peak_signal_rate_mcps, + 2, + pbuffer + 46); + VL53L1_i2c_encode_uint32_t( + pdata->vhv_result__signal_total_events_ref, + 4, + pbuffer + 48); + VL53L1_i2c_encode_uint16_t( + pdata->phasecal_result__phase_output_ref, + 2, + pbuffer + 52); + VL53L1_i2c_encode_uint16_t( + pdata->dss_result__total_rate_per_spad, + 2, + pbuffer + 54); + *(pbuffer + 56) = + pdata->dss_result__enabled_blocks; + VL53L1_i2c_encode_uint16_t( + pdata->dss_result__num_requested_spads, + 2, + pbuffer + 58); + VL53L1_i2c_encode_uint16_t( + pdata->mm_result__inner_intersection_rate, + 2, + pbuffer + 62); + VL53L1_i2c_encode_uint16_t( + pdata->mm_result__outer_complement_rate, + 2, + pbuffer + 64); + VL53L1_i2c_encode_uint16_t( + pdata->mm_result__total_offset, + 2, + pbuffer + 66); + VL53L1_i2c_encode_uint32_t( + pdata->xtalk_calc__xtalk_for_enabled_spads & 0xFFFFFF, + 4, + pbuffer + 68); + VL53L1_i2c_encode_uint32_t( + pdata->xtalk_result__avg_xtalk_user_roi_kcps & 0xFFFFFF, + 4, + pbuffer + 72); + VL53L1_i2c_encode_uint32_t( + pdata->xtalk_result__avg_xtalk_mm_inner_roi_kcps & 0xFFFFFF, + 4, + pbuffer + 76); + VL53L1_i2c_encode_uint32_t( + pdata->xtalk_result__avg_xtalk_mm_outer_roi_kcps & 0xFFFFFF, + 4, + pbuffer + 80); + VL53L1_i2c_encode_uint32_t( + pdata->range_result__accum_phase, + 4, + pbuffer + 84); + VL53L1_i2c_encode_uint16_t( + pdata->range_result__offset_corrected_range, + 2, + pbuffer + 88); + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_patch_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_patch_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_PATCH_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->dss_calc__roi_ctrl = + (*(pbuffer + 0)) & 0x3; + pdata->dss_calc__spare_1 = + (*(pbuffer + 1)); + pdata->dss_calc__spare_2 = + (*(pbuffer + 2)); + pdata->dss_calc__spare_3 = + (*(pbuffer + 3)); + pdata->dss_calc__spare_4 = + (*(pbuffer + 4)); + pdata->dss_calc__spare_5 = + (*(pbuffer + 5)); + pdata->dss_calc__spare_6 = + (*(pbuffer + 6)); + pdata->dss_calc__spare_7 = + (*(pbuffer + 7)); + pdata->dss_calc__user_roi_spad_en_0 = + (*(pbuffer + 8)); + pdata->dss_calc__user_roi_spad_en_1 = + (*(pbuffer + 9)); + pdata->dss_calc__user_roi_spad_en_2 = + (*(pbuffer + 10)); + pdata->dss_calc__user_roi_spad_en_3 = + (*(pbuffer + 11)); + pdata->dss_calc__user_roi_spad_en_4 = + (*(pbuffer + 12)); + pdata->dss_calc__user_roi_spad_en_5 = + (*(pbuffer + 13)); + pdata->dss_calc__user_roi_spad_en_6 = + (*(pbuffer + 14)); + pdata->dss_calc__user_roi_spad_en_7 = + (*(pbuffer + 15)); + pdata->dss_calc__user_roi_spad_en_8 = + (*(pbuffer + 16)); + pdata->dss_calc__user_roi_spad_en_9 = + (*(pbuffer + 17)); + pdata->dss_calc__user_roi_spad_en_10 = + (*(pbuffer + 18)); + pdata->dss_calc__user_roi_spad_en_11 = + (*(pbuffer + 19)); + pdata->dss_calc__user_roi_spad_en_12 = + (*(pbuffer + 20)); + pdata->dss_calc__user_roi_spad_en_13 = + (*(pbuffer + 21)); + pdata->dss_calc__user_roi_spad_en_14 = + (*(pbuffer + 22)); + pdata->dss_calc__user_roi_spad_en_15 = + (*(pbuffer + 23)); + pdata->dss_calc__user_roi_spad_en_16 = + (*(pbuffer + 24)); + pdata->dss_calc__user_roi_spad_en_17 = + (*(pbuffer + 25)); + pdata->dss_calc__user_roi_spad_en_18 = + (*(pbuffer + 26)); + pdata->dss_calc__user_roi_spad_en_19 = + (*(pbuffer + 27)); + pdata->dss_calc__user_roi_spad_en_20 = + (*(pbuffer + 28)); + pdata->dss_calc__user_roi_spad_en_21 = + (*(pbuffer + 29)); + pdata->dss_calc__user_roi_spad_en_22 = + (*(pbuffer + 30)); + pdata->dss_calc__user_roi_spad_en_23 = + (*(pbuffer + 31)); + pdata->dss_calc__user_roi_spad_en_24 = + (*(pbuffer + 32)); + pdata->dss_calc__user_roi_spad_en_25 = + (*(pbuffer + 33)); + pdata->dss_calc__user_roi_spad_en_26 = + (*(pbuffer + 34)); + pdata->dss_calc__user_roi_spad_en_27 = + (*(pbuffer + 35)); + pdata->dss_calc__user_roi_spad_en_28 = + (*(pbuffer + 36)); + pdata->dss_calc__user_roi_spad_en_29 = + (*(pbuffer + 37)); + pdata->dss_calc__user_roi_spad_en_30 = + (*(pbuffer + 38)); + pdata->dss_calc__user_roi_spad_en_31 = + (*(pbuffer + 39)); + pdata->dss_calc__user_roi_0 = + (*(pbuffer + 40)); + pdata->dss_calc__user_roi_1 = + (*(pbuffer + 41)); + pdata->dss_calc__mode_roi_0 = + (*(pbuffer + 42)); + pdata->dss_calc__mode_roi_1 = + (*(pbuffer + 43)); + pdata->sigma_estimator_calc__spare_0 = + (*(pbuffer + 44)); + pdata->vhv_result__peak_signal_rate_mcps = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 46)); + pdata->vhv_result__signal_total_events_ref = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 48)); + pdata->phasecal_result__phase_output_ref = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 52)); + pdata->dss_result__total_rate_per_spad = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 54)); + pdata->dss_result__enabled_blocks = + (*(pbuffer + 56)); + pdata->dss_result__num_requested_spads = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 58)); + pdata->mm_result__inner_intersection_rate = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 62)); + pdata->mm_result__outer_complement_rate = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 64)); + pdata->mm_result__total_offset = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 66)); + pdata->xtalk_calc__xtalk_for_enabled_spads = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 68)) & 0xFFFFFF; + pdata->xtalk_result__avg_xtalk_user_roi_kcps = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 72)) & 0xFFFFFF; + pdata->xtalk_result__avg_xtalk_mm_inner_roi_kcps = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 76)) & 0xFFFFFF; + pdata->xtalk_result__avg_xtalk_mm_outer_roi_kcps = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 80)) & 0xFFFFFF; + pdata->range_result__accum_phase = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 84)); + pdata->range_result__offset_corrected_range = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 88)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_patch_results( + VL53L1_DEV Dev, + VL53L1_patch_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_PATCH_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_patch_results( + pdata, + VL53L1_PATCH_RESULTS_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_DSS_CALC__ROI_CTRL, + comms_buffer, + VL53L1_PATCH_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_patch_results( + VL53L1_DEV Dev, + VL53L1_patch_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_PATCH_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_DSS_CALC__ROI_CTRL, + comms_buffer, + VL53L1_PATCH_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_patch_results( + VL53L1_PATCH_RESULTS_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_shadow_system_results( + VL53L1_shadow_system_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + *(pbuffer + 0) = + pdata->shadow_phasecal_result__vcsel_start; + *(pbuffer + 2) = + pdata->shadow_result__interrupt_status & 0x3F; + *(pbuffer + 3) = + pdata->shadow_result__range_status; + *(pbuffer + 4) = + pdata->shadow_result__report_status & 0xF; + *(pbuffer + 5) = + pdata->shadow_result__stream_count; + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__dss_actual_effective_spads_sd0, + 2, + pbuffer + 6); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__peak_signal_count_rate_mcps_sd0, + 2, + pbuffer + 8); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__ambient_count_rate_mcps_sd0, + 2, + pbuffer + 10); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__sigma_sd0, + 2, + pbuffer + 12); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__phase_sd0, + 2, + pbuffer + 14); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__final_crosstalk_corrected_range_mm_sd0, + 2, + pbuffer + 16); + VL53L1_i2c_encode_uint16_t( + pdata->shr__peak_signal_count_rate_crosstalk_corrected_mcps_sd0, + 2, + pbuffer + 18); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__mm_inner_actual_effective_spads_sd0, + 2, + pbuffer + 20); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__mm_outer_actual_effective_spads_sd0, + 2, + pbuffer + 22); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__avg_signal_count_rate_mcps_sd0, + 2, + pbuffer + 24); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__dss_actual_effective_spads_sd1, + 2, + pbuffer + 26); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__peak_signal_count_rate_mcps_sd1, + 2, + pbuffer + 28); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__ambient_count_rate_mcps_sd1, + 2, + pbuffer + 30); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__sigma_sd1, + 2, + pbuffer + 32); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__phase_sd1, + 2, + pbuffer + 34); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__final_crosstalk_corrected_range_mm_sd1, + 2, + pbuffer + 36); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__spare_0_sd1, + 2, + pbuffer + 38); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__spare_1_sd1, + 2, + pbuffer + 40); + VL53L1_i2c_encode_uint16_t( + pdata->shadow_result__spare_2_sd1, + 2, + pbuffer + 42); + *(pbuffer + 44) = + pdata->shadow_result__spare_3_sd1; + *(pbuffer + 45) = + pdata->shadow_result__thresh_info; + *(pbuffer + 80) = + pdata->shadow_phasecal_result__reference_phase_hi; + *(pbuffer + 81) = + pdata->shadow_phasecal_result__reference_phase_lo; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_shadow_system_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_shadow_system_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->shadow_phasecal_result__vcsel_start = + (*(pbuffer + 0)); + pdata->shadow_result__interrupt_status = + (*(pbuffer + 2)) & 0x3F; + pdata->shadow_result__range_status = + (*(pbuffer + 3)); + pdata->shadow_result__report_status = + (*(pbuffer + 4)) & 0xF; + pdata->shadow_result__stream_count = + (*(pbuffer + 5)); + pdata->shadow_result__dss_actual_effective_spads_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 6)); + pdata->shadow_result__peak_signal_count_rate_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 8)); + pdata->shadow_result__ambient_count_rate_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 10)); + pdata->shadow_result__sigma_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 12)); + pdata->shadow_result__phase_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 14)); + pdata->shadow_result__final_crosstalk_corrected_range_mm_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 16)); + pdata->shr__peak_signal_count_rate_crosstalk_corrected_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 18)); + pdata->shadow_result__mm_inner_actual_effective_spads_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 20)); + pdata->shadow_result__mm_outer_actual_effective_spads_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 22)); + pdata->shadow_result__avg_signal_count_rate_mcps_sd0 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 24)); + pdata->shadow_result__dss_actual_effective_spads_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 26)); + pdata->shadow_result__peak_signal_count_rate_mcps_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 28)); + pdata->shadow_result__ambient_count_rate_mcps_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 30)); + pdata->shadow_result__sigma_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 32)); + pdata->shadow_result__phase_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 34)); + pdata->shadow_result__final_crosstalk_corrected_range_mm_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 36)); + pdata->shadow_result__spare_0_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 38)); + pdata->shadow_result__spare_1_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 40)); + pdata->shadow_result__spare_2_sd1 = + (VL53L1_i2c_decode_uint16_t(2, pbuffer + 42)); + pdata->shadow_result__spare_3_sd1 = + (*(pbuffer + 44)); + pdata->shadow_result__thresh_info = + (*(pbuffer + 45)); + pdata->shadow_phasecal_result__reference_phase_hi = + (*(pbuffer + 80)); + pdata->shadow_phasecal_result__reference_phase_lo = + (*(pbuffer + 81)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_shadow_system_results( + VL53L1_DEV Dev, + VL53L1_shadow_system_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_shadow_system_results( + pdata, + VL53L1_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_SHADOW_PHASECAL_RESULT__VCSEL_START, + comms_buffer, + VL53L1_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_shadow_system_results( + VL53L1_DEV Dev, + VL53L1_shadow_system_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_SHADOW_PHASECAL_RESULT__VCSEL_START, + comms_buffer, + VL53L1_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_shadow_system_results( + VL53L1_SHADOW_SYSTEM_RESULTS_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_i2c_encode_shadow_core_results( + VL53L1_shadow_core_results_t *pdata, + uint16_t buf_size, + uint8_t *pbuffer) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + VL53L1_i2c_encode_uint32_t( + pdata->shadow_result_core__ambient_window_events_sd0, + 4, + pbuffer + 0); + VL53L1_i2c_encode_uint32_t( + pdata->shadow_result_core__ranging_total_events_sd0, + 4, + pbuffer + 4); + VL53L1_i2c_encode_int32_t( + pdata->shadow_result_core__signal_total_events_sd0, + 4, + pbuffer + 8); + VL53L1_i2c_encode_uint32_t( + pdata->shadow_result_core__total_periods_elapsed_sd0, + 4, + pbuffer + 12); + VL53L1_i2c_encode_uint32_t( + pdata->shadow_result_core__ambient_window_events_sd1, + 4, + pbuffer + 16); + VL53L1_i2c_encode_uint32_t( + pdata->shadow_result_core__ranging_total_events_sd1, + 4, + pbuffer + 20); + VL53L1_i2c_encode_int32_t( + pdata->shadow_result_core__signal_total_events_sd1, + 4, + pbuffer + 24); + VL53L1_i2c_encode_uint32_t( + pdata->shadow_result_core__total_periods_elapsed_sd1, + 4, + pbuffer + 28); + *(pbuffer + 32) = + pdata->shadow_result_core__spare_0; + LOG_FUNCTION_END(status); + + + return status; +} + + +VL53L1_Error VL53L1_i2c_decode_shadow_core_results( + uint16_t buf_size, + uint8_t *pbuffer, + VL53L1_shadow_core_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + if (buf_size < VL53L1_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES) + return VL53L1_ERROR_COMMS_BUFFER_TOO_SMALL; + + pdata->shadow_result_core__ambient_window_events_sd0 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 0)); + pdata->shadow_result_core__ranging_total_events_sd0 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 4)); + pdata->shadow_result_core__signal_total_events_sd0 = + (VL53L1_i2c_decode_int32_t(4, pbuffer + 8)); + pdata->shadow_result_core__total_periods_elapsed_sd0 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 12)); + pdata->shadow_result_core__ambient_window_events_sd1 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 16)); + pdata->shadow_result_core__ranging_total_events_sd1 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 20)); + pdata->shadow_result_core__signal_total_events_sd1 = + (VL53L1_i2c_decode_int32_t(4, pbuffer + 24)); + pdata->shadow_result_core__total_periods_elapsed_sd1 = + (VL53L1_i2c_decode_uint32_t(4, pbuffer + 28)); + pdata->shadow_result_core__spare_0 = + (*(pbuffer + 32)); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_set_shadow_core_results( + VL53L1_DEV Dev, + VL53L1_shadow_core_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_encode_shadow_core_results( + pdata, + VL53L1_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES, + comms_buffer); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_WriteMulti( + Dev, + VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0, + comms_buffer, + VL53L1_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_get_shadow_core_results( + VL53L1_DEV Dev, + VL53L1_shadow_core_results_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t comms_buffer[VL53L1_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES]; + + LOG_FUNCTION_START(""); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_disable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_ReadMulti( + Dev, + VL53L1_SHADOW_RESULT_CORE__AMBIENT_WINDOW_EVENTS_SD0, + comms_buffer, + VL53L1_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_enable_firmware(Dev); + + if (status == VL53L1_ERROR_NONE) + status = VL53L1_i2c_decode_shadow_core_results( + VL53L1_SHADOW_CORE_RESULTS_I2C_SIZE_BYTES, + comms_buffer, + pdata); + + LOG_FUNCTION_END(status); + + return status; +} + + diff --git a/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_silicon_core.c b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_silicon_core.c new file mode 100644 index 000000000000..de0ca1999f1e --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_silicon_core.c @@ -0,0 +1,94 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + + +#include "vl53l1_ll_def.h" +#include "vl53l1_platform.h" +#include "vl53l1_register_map.h" +#include "vl53l1_core.h" +#include "vl53l1_silicon_core.h" + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_CORE, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_CORE, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_CORE,\ + status, fmt, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_is_firmware_ready_silicon( + VL53L1_DEV Dev, + uint8_t *pready) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t comms_buffer[5]; + + LOG_FUNCTION_START(""); + + + + status = VL53L1_ReadMulti( + Dev, + VL53L1_INTERRUPT_MANAGER__ENABLES, + comms_buffer, + 5); + + if (status != VL53L1_ERROR_NONE) + goto ENDFUNC; + + pdev->dbg_results.interrupt_manager__enables = + comms_buffer[0]; + pdev->dbg_results.interrupt_manager__clear = + comms_buffer[1]; + pdev->dbg_results.interrupt_manager__status = + comms_buffer[2]; + pdev->dbg_results.mcu_to_host_bank__wr_access_en = + comms_buffer[3]; + pdev->dbg_results.power_management__go1_reset_status = + comms_buffer[4]; + + if ((pdev->sys_ctrl.power_management__go1_power_force & 0x01) + == 0x01) { + + if (((pdev->dbg_results.interrupt_manager__enables & + 0x1F) == 0x1F) && + ((pdev->dbg_results.interrupt_manager__clear + & 0x1F) == 0x1F)) + *pready = 0x01; + else + *pready = 0x00; + + } else { + + + if ((pdev->dbg_results.power_management__go1_reset_status + & 0x01) == 0x00) + *pready = 0x01; + else + *pready = 0x00; + } + + +ENDFUNC: + LOG_FUNCTION_END(status); + + return status; +} + diff --git a/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_wait.c b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_wait.c new file mode 100644 index 000000000000..003022c3ddfd --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_wait.c @@ -0,0 +1,466 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + + +#include "vl53l1_ll_def.h" +#include "vl53l1_ll_device.h" +#include "vl53l1_platform.h" +#include "vl53l1_core.h" +#include "vl53l1_silicon_core.h" +#include "vl53l1_wait.h" +#include "vl53l1_register_settings.h" + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_CORE, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_CORE, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_CORE, status, \ + fmt, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_wait_for_boot_completion( + VL53L1_DEV Dev) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t fw_ready = 0; + + LOG_FUNCTION_START(""); + + if (pdev->wait_method == VL53L1_WAIT_METHOD_BLOCKING) { + + + + status = + VL53L1_poll_for_boot_completion( + Dev, + VL53L1_BOOT_COMPLETION_POLLING_TIMEOUT_MS); + + } else { + + + + fw_ready = 0; + while (fw_ready == 0x00 && status == VL53L1_ERROR_NONE) { + status = VL53L1_is_boot_complete( + Dev, + &fw_ready); + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_WaitMs( + Dev, + VL53L1_POLLING_DELAY_MS); + } + } + } + + LOG_FUNCTION_END(status); + + return status; + +} + + +VL53L1_Error VL53L1_wait_for_firmware_ready( + VL53L1_DEV Dev) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t fw_ready = 0; + uint8_t mode_start = 0; + + LOG_FUNCTION_START(""); + + + mode_start = + pdev->sys_ctrl.system__mode_start & + VL53L1_DEVICEMEASUREMENTMODE_MODE_MASK; + + + + if ((mode_start == VL53L1_DEVICEMEASUREMENTMODE_TIMED) || + (mode_start == VL53L1_DEVICEMEASUREMENTMODE_SINGLESHOT)) { + + if (pdev->wait_method == VL53L1_WAIT_METHOD_BLOCKING) { + + + + status = + VL53L1_poll_for_firmware_ready( + Dev, + VL53L1_RANGE_COMPLETION_POLLING_TIMEOUT_MS); + + } else { + + + + fw_ready = 0; + while (fw_ready == 0x00 && status == + VL53L1_ERROR_NONE) { + status = VL53L1_is_firmware_ready( + Dev, + &fw_ready); + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_WaitMs( + Dev, + VL53L1_POLLING_DELAY_MS); + } + } + } + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_wait_for_range_completion( + VL53L1_DEV Dev) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t data_ready = 0; + + LOG_FUNCTION_START(""); + + if (pdev->wait_method == VL53L1_WAIT_METHOD_BLOCKING) { + + + + status = + VL53L1_poll_for_range_completion( + Dev, + VL53L1_RANGE_COMPLETION_POLLING_TIMEOUT_MS); + + } else { + + + + data_ready = 0; + while (data_ready == 0x00 && status == VL53L1_ERROR_NONE) { + status = VL53L1_is_new_data_ready( + Dev, + &data_ready); + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_WaitMs( + Dev, + VL53L1_POLLING_DELAY_MS); + } + } + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_wait_for_test_completion( + VL53L1_DEV Dev) +{ + + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t data_ready = 0; + + LOG_FUNCTION_START(""); + + if (pdev->wait_method == VL53L1_WAIT_METHOD_BLOCKING) { + + + + status = + VL53L1_poll_for_range_completion( + Dev, + VL53L1_TEST_COMPLETION_POLLING_TIMEOUT_MS); + + } else { + + + + data_ready = 0; + while (data_ready == 0x00 && status == VL53L1_ERROR_NONE) { + status = VL53L1_is_new_data_ready( + Dev, + &data_ready); + + if (status == VL53L1_ERROR_NONE) { + status = VL53L1_WaitMs( + Dev, + VL53L1_POLLING_DELAY_MS); + } + } + } + + LOG_FUNCTION_END(status); + + return status; +} + + + + +VL53L1_Error VL53L1_is_boot_complete( + VL53L1_DEV Dev, + uint8_t *pready) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t firmware__system_status = 0; + + LOG_FUNCTION_START(""); + + + + status = + VL53L1_RdByte( + Dev, + VL53L1_FIRMWARE__SYSTEM_STATUS, + &firmware__system_status); + + + + if ((firmware__system_status & 0x01) == 0x01) { + *pready = 0x01; + VL53L1_init_ll_driver_state( + Dev, + VL53L1_DEVICESTATE_SW_STANDBY); + } else { + *pready = 0x00; + VL53L1_init_ll_driver_state( + Dev, + VL53L1_DEVICESTATE_FW_COLDBOOT); + } + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_is_firmware_ready( + VL53L1_DEV Dev, + uint8_t *pready) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + LOG_FUNCTION_START(""); + + status = VL53L1_is_firmware_ready_silicon( + Dev, + pready); + + pdev->fw_ready = *pready; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_is_new_data_ready( + VL53L1_DEV Dev, + uint8_t *pready) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t gpio__mux_active_high_hv = 0; + uint8_t gpio__tio_hv_status = 0; + uint8_t interrupt_ready = 0; + + LOG_FUNCTION_START(""); + + gpio__mux_active_high_hv = + pdev->stat_cfg.gpio_hv_mux__ctrl & + VL53L1_DEVICEINTERRUPTLEVEL_ACTIVE_MASK; + + if (gpio__mux_active_high_hv == VL53L1_DEVICEINTERRUPTLEVEL_ACTIVE_HIGH) + interrupt_ready = 0x01; + else + interrupt_ready = 0x00; + + + + status = VL53L1_RdByte( + Dev, + VL53L1_GPIO__TIO_HV_STATUS, + &gpio__tio_hv_status); + + + + if ((gpio__tio_hv_status & 0x01) == interrupt_ready) + *pready = 0x01; + else + *pready = 0x00; + + LOG_FUNCTION_END(status); + + return status; +} + + + + +VL53L1_Error VL53L1_poll_for_boot_completion( + VL53L1_DEV Dev, + uint32_t timeout_ms) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + + status = VL53L1_WaitUs( + Dev, + VL53L1_FIRMWARE_BOOT_TIME_US); + + if (status == VL53L1_ERROR_NONE) + status = + VL53L1_WaitValueMaskEx( + Dev, + timeout_ms, + VL53L1_FIRMWARE__SYSTEM_STATUS, + 0x01, + 0x01, + VL53L1_POLLING_DELAY_MS); + + if (status == VL53L1_ERROR_NONE) + VL53L1_init_ll_driver_state(Dev, VL53L1_DEVICESTATE_SW_STANDBY); + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_poll_for_firmware_ready( + VL53L1_DEV Dev, + uint32_t timeout_ms) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint32_t start_time_ms = 0; + uint32_t current_time_ms = 0; + int32_t poll_delay_ms = VL53L1_POLLING_DELAY_MS; + uint8_t fw_ready = 0; + + + + VL53L1_GetTickCount(Dev, &start_time_ms); + pdev->fw_ready_poll_duration_ms = 0; + + + + while ((status == VL53L1_ERROR_NONE) && + (pdev->fw_ready_poll_duration_ms < timeout_ms) && + (fw_ready == 0)) { + + status = VL53L1_is_firmware_ready( + Dev, + &fw_ready); + + if (status == VL53L1_ERROR_NONE && + fw_ready == 0 && + poll_delay_ms > 0) { + status = VL53L1_WaitMs( + Dev, + poll_delay_ms); + } + + + VL53L1_GetTickCount(Dev, ¤t_time_ms); + pdev->fw_ready_poll_duration_ms = + current_time_ms - start_time_ms; + } + + if (fw_ready == 0 && status == VL53L1_ERROR_NONE) + status = VL53L1_ERROR_TIME_OUT; + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_poll_for_range_completion( + VL53L1_DEV Dev, + uint32_t timeout_ms) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + VL53L1_LLDriverData_t *pdev = VL53L1DevStructGetLLDriverHandle(Dev); + + uint8_t gpio__mux_active_high_hv = 0; + uint8_t interrupt_ready = 0; + + LOG_FUNCTION_START(""); + + gpio__mux_active_high_hv = + pdev->stat_cfg.gpio_hv_mux__ctrl & + VL53L1_DEVICEINTERRUPTLEVEL_ACTIVE_MASK; + + if (gpio__mux_active_high_hv == VL53L1_DEVICEINTERRUPTLEVEL_ACTIVE_HIGH) + interrupt_ready = 0x01; + else + interrupt_ready = 0x00; + + status = + VL53L1_WaitValueMaskEx( + Dev, + timeout_ms, + VL53L1_GPIO__TIO_HV_STATUS, + interrupt_ready, + 0x01, + VL53L1_POLLING_DELAY_MS); + + LOG_FUNCTION_END(status); + + return status; +} + + diff --git a/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_zone_presets.c b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_zone_presets.c new file mode 100644 index 000000000000..a3e5b74b1eaa --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/src/vl53l1_zone_presets.c @@ -0,0 +1,152 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + + +#include "vl53l1_ll_def.h" +#include "vl53l1_ll_device.h" +#include "vl53l1_platform_log.h" +#include "vl53l1_zone_presets.h" + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_CORE, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_CORE, status, ##__VA_ARGS__) +#define LOG_FUNCTION_END_FMT(status, fmt, ...) \ + _LOG_FUNCTION_END_FMT(VL53L1_TRACE_MODULE_CORE,\ + status, fmt, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_init_zone_config_structure( + uint8_t x_off, + uint8_t x_inc, + uint8_t x_zones, + uint8_t y_off, + uint8_t y_inc, + uint8_t y_zones, + uint8_t width, + uint8_t height, + VL53L1_zone_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t x = 0; + uint8_t y = 0; + uint16_t i = 0; + + LOG_FUNCTION_START(""); + + pdata->max_zones = VL53L1_MAX_USER_ZONES; + + i = 0; + + for (x = 0 ; x < x_zones ; x++) { + for (y = 0 ; y < y_zones ; y++) { + + if (i < VL53L1_MAX_USER_ZONES) { + + pdata->active_zones = (uint8_t)i; + pdata->user_zones[i].height = height; + pdata->user_zones[i].width = width; + pdata->user_zones[i].x_centre = + x_off + (x * x_inc); + pdata->user_zones[i].y_centre = + y_off + (y * y_inc); + } + + i++; + } + } + + status = VL53L1_init_zone_config_histogram_bins(pdata); + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_zone_preset_xtalk_planar( + VL53L1_general_config_t *pgeneral, + VL53L1_zone_config_t *pzone_cfg) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + LOG_FUNCTION_START(""); + + + pgeneral->global_config__stream_divider = 0x05; + + + pzone_cfg->active_zones = 0x04; + + pzone_cfg->user_zones[0].height = 15; + pzone_cfg->user_zones[0].width = 7; + pzone_cfg->user_zones[0].x_centre = 4; + pzone_cfg->user_zones[0].y_centre = 8; + + pzone_cfg->user_zones[1].height = 15; + pzone_cfg->user_zones[1].width = 7; + pzone_cfg->user_zones[1].x_centre = 12; + pzone_cfg->user_zones[1].y_centre = 8; + + pzone_cfg->user_zones[2].height = 7; + pzone_cfg->user_zones[2].width = 15; + pzone_cfg->user_zones[2].x_centre = 8; + pzone_cfg->user_zones[2].y_centre = 4; + + pzone_cfg->user_zones[3].height = 7; + pzone_cfg->user_zones[3].width = 15; + pzone_cfg->user_zones[3].x_centre = 8; + pzone_cfg->user_zones[3].y_centre = 12; + + + + pzone_cfg->user_zones[4].height = 15; + pzone_cfg->user_zones[4].width = 15; + pzone_cfg->user_zones[4].x_centre = 8; + pzone_cfg->user_zones[4].y_centre = 8; + + status = VL53L1_init_zone_config_histogram_bins(pzone_cfg); + + + LOG_FUNCTION_END(status); + + return status; +} + + +VL53L1_Error VL53L1_init_zone_config_histogram_bins( + VL53L1_zone_config_t *pdata) +{ + + + VL53L1_Error status = VL53L1_ERROR_NONE; + + uint8_t i; + + LOG_FUNCTION_START(""); + + for (i = 0; i < pdata->max_zones; i++) + pdata->bin_config[i] = VL53L1_ZONECONFIG_BINCONFIG__LOWAMB; + + LOG_FUNCTION_END(status); + + return status; +} + diff --git a/drivers/input/misc/vl53L1_14_1_2/stmvl53l1-i2c.h b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1-i2c.h new file mode 100644 index 000000000000..38e2a4a17fae --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1-i2c.h @@ -0,0 +1,89 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +/** @file stmvl53l1-i2c.h + * Linux kernel i2c/cci wrapper for ST VL53L1 sensor i2c interface + **/ + +#ifndef STMVL53L1_I2C_H +#define STMVL53L1_I2C_H +#include +#include "stmvl53l1.h" + +struct i2c_data { + struct i2c_client *client; + /** back link to driver for interrupt and clean-up */ + struct stmvl53l1_data *vl53l1_data; + + /* reference counter */ + struct kref ref; + + /*!< if null no regulator use for power ctrl */ + struct regulator *vdd; + + /*!< power enable gpio number + * + * if -1 no gpio if vdd not avl pwr is not controllable + */ + int pwren_gpio; + + /*!< xsdn reset (low active) gpio number to device + * + * -1 mean none assume no "resetable" + */ + int xsdn_gpio; + + /*!< intr gpio number to device + * + * intr is active/low negative edge by default + * + * -1 mean none assume use polling + * @warning if the dev tree and intr gpio is require please adapt code + */ + int intr_gpio; + + /*!< device boot i2c register address + * + * boot_reg is the value of device i2c address after it is bring out + * of reset. + */ + int boot_reg; + + /*!< is set if above irq gpio got acquired */ + struct i2d_data_flags_t { + unsigned pwr_owned:1; /*!< set if pwren gpio is owned*/ + unsigned xsdn_owned:1; /*!< set if sxdn gpio is owned*/ + unsigned intr_owned:1; /*!< set if intr gpio is owned*/ + unsigned intr_started:1; /*!< set if irq is hanlde */ + } io_flag; + + /** the irq vectore assigned to gpio + * -1 if no irq hanled + */ + int irq; + + struct msgtctrl_t { + unsigned unhandled_irq_vec:1; + } msg_flag; +}; + +int stmvl53l1_init_i2c(void); +void __exit stmvl53l1_exit_i2c(void *arg); +int stmvl53l1_power_up_i2c(void *arg); +int stmvl53l1_power_down_i2c(void *arg); +int stmvl53l1_reset_release_i2c(void *arg); +int stmvl53l1_reset_hold_i2c(void *arg); +void stmvl53l1_clean_up_i2c(void); +int stmvl53l1_start_intr(void *object, int *poll_mode); +void *stmvl53l1_get(void *arg); +void stmvl53l1_put(void *arg); + +#endif /* STMVL53L1_I2C_H */ diff --git a/drivers/input/misc/vl53L1_14_1_2/stmvl53l1.h b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1.h new file mode 100644 index 000000000000..058089c70a29 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1.h @@ -0,0 +1,292 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +/** + * @file stmvl53l1.h header for vl53l1 sensor driver + */ +#ifndef STMVL53L1_H +#define STMVL53L1_H + +#include +#include +#include +#include +#include + +#include "vl53l1_api.h" + +#ifdef DEBUG +# define IPP_PRINT(...) printk(__VA_ARGS__) +#else +# define IPP_PRINT(...) (void)0 +#endif + +struct st_timeval { + time64_t tv_sec; /* seconds */ + long tv_usec; /* microseconds */ +}; + +void st_gettimeofday(struct st_timeval *tv); + +#include "stmvl53l1_if.h" + +#define STMVL53L1_MAX_CCI_XFER_SZ 256 +#define STMVL53L1_DRV_NAME "stmvl53l1" + +/** + * configure usage of regulator device from device tree info + * to enable/disable sensor power + * see module-i2c or module-cci file + */ +/* define CFG_STMVL53L1_HAVE_REGULATOR */ + +#define DRIVER_VERSION "14.1.2" + +/** @ingroup vl53l1_config + * @{ + */ +/** + * Configure max number of device the driver can support + */ +#define STMVL53L1_CFG_MAX_DEV 2 +/** @} */ /* ingroup vl53l1_config */ + +/** @ingroup vl53l1_mod_dbg + * @{ + */ +#if 0 +#define DEBUG 1 +#endif +#if 0 +#define FORCE_CONSOLE_DEBUG 1 +#endif + +extern int stmvl53l1_enable_debug; + +#ifdef DEBUG +# ifdef FORCE_CONSOLE_DEBUG +#define vl53l1_dbgmsg(str, ...) do { \ + if (stmvl53l1_enable_debug) \ + pr_err("%s: " str, __func__, ##__VA_ARGS__); \ +} while (0) +# else +#define vl53l1_dbgmsg(str, ...) do { \ + if (stmvl53l1_enable_debug) \ + pr_err("%s: " str, __func__, ##__VA_ARGS__); \ +} while (0) +# endif +#else +# define vl53l1_dbgmsg(...) (void)0 +#endif + +/** + * set to 0 1 activate or not debug from work (data interrupt/polling) + */ +#define WORK_DEBUG 0 +#if WORK_DEBUG +# define work_dbg(msg, ...)\ + printk("[D WK53L1] :" msg "\n", ##__VA_ARGS__) +#else +# define work_dbg(...) (void)0 +#endif + +#define vl53l1_info(str, args...) \ + pr_info("%s: " str, __func__, ##args) + +#define vl53l1_errmsg(str, args...) \ + pr_err("%s: " str, __func__, ##args) + +#define vl53l1_wanrmsg(str, args...) \ + pr_warn("%s: " str, __func__, ##args) + +/* turn off poll log if not defined */ +#ifndef STMVL53L1_LOG_POLL_TIMING +# define STMVL53L1_LOG_POLL_TIMING 0 +#endif +/* turn off cci log timing if not defined */ +#ifndef STMVL53L1_LOG_CCI_TIMING +# define STMVL53L1_LOG_CCI_TIMING 0 +#endif + +/**@} */ /* ingroup mod_dbg*/ + +#include +#include +#include + +struct stmvl53l1_waiters { + struct list_head list; + pid_t pid; +}; + +/* + * driver data structs + */ +struct stmvl53l1_data { + int id; /*!< multiple device id 0 based*/ + char name[64]; /*!< misc device name */ + uint8_t product_type; /*!< L1:0xCC L3:0xAA i.e. module_type in datasheet */ + + VL53L1_DevData_t stdev; /*! + +#define WRITE_MULTIPLE_CHUNK_MAX 32 + +#if STMVL53L1_LOG_POLL_TIMING +/** + * helper to elapse time in polling + * @param ptv pointer to start time_val + + */ +# define poll_timing_log(ptv) \ + vl53l1_dbgmsg("poll in %d us\n", tv_elapsed_us(ptv)) +#else +# define poll_timing_log(...) (void)0 +#endif + +#if STMVL53L1_LOG_CCI_TIMING +/** + * compute elapsed time in in micro sec based on do_gettimeofday + * @param tv pointer to start time_val + * @return time elapsed in micro seconde + */ + +/** + * compute elapsed time in in micro sec based on do_gettimeofday + * @param tv pointer to start time_val + * @return time elapsed in micro seconde + */ +static uint32_t tv_elapsed_us(struct st_timeval *tv) +{ + struct st_timeval now; + + st_gettimeofday(&now); + return (now.tv_sec - tv->tv_sec) * 1000000 + (now.tv_usec - + tv->tv_usec); +} + +# define cci_access_var struct st_timeval cci_log_start_tv +# define cci_access_start()\ + st_gettimeofday(&cci_log_start_tv) +# define cci_access_over(fmt, ...) \ + vl53l1_dbgmsg("cci_timing %d us" fmt "\n", \ + tv_elapsed_us(&cci_log_start_tv), ##__VA_ARGS__) +#else +# define cci_access_var +# define cci_access_start(...) (void)0 +# define cci_access_over(...) (void)0 +#endif + +#ifdef STMVL53L1_DEBUG_I2C +# define i2c_debug(fmt, ...) vl53l1_dbgmsg(fmt, ##__VA_ARGS__) +#else +# define i2c_debug(fmt, ...) (void)0 +#endif + +VL53L1_Error VL53L1_GetTickCount(VL53L1_Dev_t *pdev, uint32_t *ptime_ms) +{ + (void)pdev; + (void)ptime_ms; + BUG_ON(1); +} + +/** + * compute elapsed time in in milli sec based on do_gettimeofday + * @param tv pointer to start time_val + * @return time elapsed in milli seconde + */ +static uint32_t tv_elapsed_ms(struct st_timeval *tv) +{ + struct st_timeval now; + + st_gettimeofday(&now); + return (now.tv_sec - tv->tv_sec) * 1000 + + (now.tv_usec - tv->tv_usec) / 1000; +} + +static int cci_write(struct stmvl53l1_data *dev, int index, + uint8_t *data, uint16_t len) +{ + uint8_t buffer[STMVL53L1_MAX_CCI_XFER_SZ + 2]; + struct i2c_msg msg; + struct i2c_data *i2c_client_obj = (struct i2c_data *)dev->client_object; + struct i2c_client *client = (struct i2c_client *)i2c_client_obj->client; + int rc; + + cci_access_var; + if (len > STMVL53L1_MAX_CCI_XFER_SZ || len == 0) { + vl53l1_errmsg("invalid len %d\n", len); + return -1; + } + cci_access_start(); + /* build up little endian index in buffer */ + buffer[0] = (index >> 8) & 0xFF; + buffer[1] = (index >> 0) & 0xFF; + /* copy write data to buffer after index */ + memcpy(buffer + 2, data, len); + /* set i2c msg */ + msg.addr = client->addr; + msg.flags = client->flags; + msg.buf = buffer; + msg.len = len + 2; + + rc = i2c_transfer(client->adapter, &msg, 1); + if (rc != 1) { + vl53l1_errmsg("wr i2c_transfer err:%d, index 0x%x len %d\n", + rc, index, len); + } + cci_access_over("rd status %d long %d ", rc != 1, len); + return rc != 1; +} + +static int cci_read(struct stmvl53l1_data *dev, int index, + uint8_t *data, uint16_t len) +{ + uint8_t buffer[2]; + struct i2c_msg msg[2]; + struct i2c_data *i2c_client_obj = (struct i2c_data *)dev->client_object; + struct i2c_client *client = (struct i2c_client *)i2c_client_obj->client; + int rc; + + cci_access_var; + if (len > STMVL53L1_MAX_CCI_XFER_SZ || len == 0) { + vl53l1_errmsg("invalid len %d\n", len); + return -1; + } + cci_access_start(); + + /* build up little endian index in buffer */ + buffer[0] = (index >> 8) & 0xFF; + buffer[1] = (index >> 0) & 0xFF; + + msg[0].addr = client->addr; + msg[0].flags = client->flags; /* Write */ + msg[0].buf = buffer; + msg[0].len = 2; + /* read part of the i2c transaction */ + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD | client->flags; + msg[1].buf = data; + msg[1].len = len; + + rc = i2c_transfer(client->adapter, msg, 2); + if (rc != 2) { + pr_err("%s: i2c_transfer :%d, @%x index 0x%x len %d\n", + __func__, rc, client->addr, index, len); + + } + cci_access_over(" wr len %d status %d", rc != 2, len); + return rc != 2; +} + +VL53L1_Error VL53L1_WrByte(VL53L1_DEV pdev, uint16_t index, uint8_t data) +{ + struct stmvl53l1_data *dev; + + dev = (struct stmvl53l1_data *)container_of(pdev, + struct stmvl53l1_data, + stdev); + + return cci_write(dev, index, &data, 1); + +} + +VL53L1_Error VL53L1_RdByte(VL53L1_DEV pdev, uint16_t index, uint8_t *pdata) +{ + struct stmvl53l1_data *dev; + + dev = (struct stmvl53l1_data *)container_of(pdev, + struct stmvl53l1_data, + stdev); + + return cci_read(dev, index, pdata, 1) ? + VL53L1_ERROR_CONTROL_INTERFACE : VL53L1_ERROR_NONE; +} + +VL53L1_Error VL53L1_WrWord(VL53L1_DEV pdev, uint16_t index, uint16_t data) +{ + VL53L1_Error status; + uint8_t buffer[2]; + + /* Split 16-bit word into MS and L* stmvl53l1 FlightSense sensor */ + + buffer[0] = (uint8_t) (data >> 8); + buffer[1] = (uint8_t) (data & 0x00FF); + i2c_debug(" @%x d= %x => [ %x , %x ] ", index, data, buffer[0], + buffer[1]); + status = VL53L1_WriteMulti(pdev, index, buffer, 2); + + return status; +} + +VL53L1_Error VL53L1_RdWord(VL53L1_DEV pdev, uint16_t index, uint16_t *pdata) +{ + VL53L1_Error status; + uint8_t buffer[2]; + + status = VL53L1_ReadMulti(pdev, index, buffer, 2); + + *pdata = ((uint16_t) buffer[0] << 8) + (uint16_t) buffer[1]; + + return status; +} + +VL53L1_Error VL53L1_WrDWord(VL53L1_DEV pdev, uint16_t index, uint32_t data) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t buffer[4]; + + /* Split 32-bit word into MS ... LS bytes */ + buffer[0] = (uint8_t) (data >> 24); + buffer[1] = (uint8_t) ((data & 0x00FF0000) >> 16); + buffer[2] = (uint8_t) ((data & 0x0000FF00) >> 8); + buffer[3] = (uint8_t) (data & 0x000000FF); + + status = VL53L1_WriteMulti(pdev, index, buffer, 4); + + return status; +} + +VL53L1_Error VL53L1_RdDWord(VL53L1_DEV pdev, uint16_t index, uint32_t *pdata) +{ + VL53L1_Error status = VL53L1_ERROR_NONE; + uint8_t buffer[4]; + + status = VL53L1_ReadMulti(pdev, index, buffer, 4); + + *pdata = ((uint32_t) buffer[0] << 24) + ((uint32_t) buffer[1] << 16) + + ((uint32_t) buffer[2] << 8) + (uint32_t) buffer[3]; + + return status; +} + +VL53L1_Error VL53L1_WriteMulti(VL53L1_DEV pdev, uint16_t index, + uint8_t *pdata, uint32_t count) +{ + struct stmvl53l1_data *dev; + uint32_t chunk_size = WRITE_MULTIPLE_CHUNK_MAX; + VL53L1_Error status; + uint32_t i; + uint16_t hostaddr = index; + + dev = (struct stmvl53l1_data *)container_of(pdev, + struct stmvl53l1_data, + stdev); + + for (i = 0; i < count; i += chunk_size) { + status = (cci_write(dev, hostaddr, &pdata[i], + min(chunk_size, (count - i))) ? + VL53L1_ERROR_CONTROL_INTERFACE : VL53L1_ERROR_NONE); + if (status != VL53L1_ERROR_NONE) + break; + hostaddr += chunk_size; + } + + return status; +} + +VL53L1_Error VL53L1_ReadMulti(VL53L1_DEV pdev, uint16_t index, + uint8_t *pdata, uint32_t count) +{ + struct stmvl53l1_data *dev; + + dev = (struct stmvl53l1_data *)container_of(pdev, + struct stmvl53l1_data, + stdev); + + return cci_read(dev, index, pdata, count) ? + VL53L1_ERROR_CONTROL_INTERFACE : VL53L1_ERROR_NONE; +} + +static int is_time_over(struct st_timeval *tv, uint32_t msec) +{ + return tv_elapsed_ms(tv) >= msec; +} + +VL53L1_Error VL53L1_WaitValueMaskEx(VL53L1_DEV pdev, + uint32_t timeout_ms, + uint16_t index, + uint8_t value, + uint8_t mask, uint32_t poll_delay_ms) +{ + struct st_timeval start_tv; + struct stmvl53l1_data *dev; + int rc, time_over; + uint8_t rd_val; + + dev = (struct stmvl53l1_data *)container_of(pdev, + struct stmvl53l1_data, + stdev); + + st_gettimeofday(&start_tv); + do { + rc = cci_read(dev, index, &rd_val, 1); + if (rc) + return VL53L1_ERROR_CONTROL_INTERFACE; + if ((rd_val & mask) == value) { + poll_timing_log(&start_tv); + return VL53L1_ERROR_NONE; + } + vl53l1_dbgmsg("poll @%x %x & %d != %x", index, + rd_val, mask, value); + time_over = is_time_over(&start_tv, timeout_ms); + if (!time_over) + msleep(poll_delay_ms); + } while (!time_over); + vl53l1_errmsg("time over %d ms", timeout_ms); + return VL53L1_ERROR_TIME_OUT; +} + +VL53L1_Error VL53L1_WaitUs(VL53L1_DEV pdev, int32_t wait_us) +{ + struct stmvl53l1_data *data; + + data = (struct stmvl53l1_data *)container_of(pdev, + struct stmvl53l1_data, + stdev); + + if (!data->is_delay_allowed) + return VL53L1_ERROR_PLATFORM_SPECIFIC_START; + + /* follow Documentation/timers/timers-howto.txt recommendations */ + if (wait_us < 10) + udelay(wait_us); + else if (wait_us < 20000) + usleep_range(wait_us, wait_us + 1); + else + msleep(wait_us / 1000); + + return VL53L1_ERROR_NONE; +} + +VL53L1_Error VL53L1_WaitMs(VL53L1_DEV pdev, int32_t wait_ms) +{ + return VL53L1_WaitUs(pdev, wait_ms * 1000); +} diff --git a/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_if.h b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_if.h new file mode 100644 index 000000000000..fc7c0e835d1f --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_if.h @@ -0,0 +1,694 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +/** + * @file stmvl53l1_if.h vl53l1 kernel driver user interface + * + * @note to use this header in a user space application it requires + * all st bare/ll driver platform wrapper files (for data struct def) + * this files (types etc ..) shall be same or compliant with bar driver version + * used in the kernel module + */ + +#ifndef STMVL53L1_IF_H +#define STMVL53L1_IF_H + + +#include "vl53l1_def.h" +/** + * @addtogroup vl53l1_ioctl + * @{ + */ + +/** + * misc device name for ioctl device + * + * for mutli instance all device 2nd and next instance are basic name +"1"+"2" + * @li stmvl53l1_ranging + * @li stmvl53l1_ranging1 + * @li stmvl53l1_ranging2 + */ +#define VL53L1_MISC_DEV_NAME "laser" +/** + * register data use for simple/single ranging data @ref VL53L1_IOCTL_GETDATAS + * + * @warning this definition is subject to change ! + */ +#define stmvl531_range_data_t VL53L1_RangingMeasurementData_t + +/** + * parameter name in @ref stmvl53l1_parameter when using + * @ref VL53L1_IOCTL_PARAMETER + */ +enum __stmv53l1_parameter_name_e { + VL53L1_XTALKENABLE_PAR = 2, + /*!< VL53L1_XTALKENABLE_PAR enable/disable crosstalk compensation\n + * valid value : + * @li 0 disable crosstalk compensation + * @li 1 enable crosstalk compensation + * + * @warning mode can only be set while not ranging + */ + + VL53L1_DEVICEMODE_PAR = 6, + /*!< DEVICEMODE_PAR set ranging mode  \n + * valid mode value : + * @li 1 @a VL53L1_PRESETMODE_RANGING default ranging + * @li 2 @a VL53L1_PRESETMODE_MULTIZONES_SCANNING multiple zone + * @li 3 @a VL53L1_PRESETMODE_AUTONOMOUS autonomous mode + * @li 4 @a VL53L1_PRESETMODE_LITE_RANGING low mips ranging mode + * @li 8 @a VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS low power autonomous + * mode + * + * @note VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS and + * VL53L1_PRESETMODE_AUTONOMOUS ranging modes use the polling time and + * the threholds set by @ref VL53L1_IOCTL_AUTONOMOUS_CONFIG + * + * @warning mode can only be set while not ranging + */ + + VL53L1_POLLDELAY_PAR = 10, + /*!< set the polling delay (msec)\n + * + * @note apply only when operates in polling mode as no effect + * otherwise + */ + VL53L1_TIMINGBUDGET_PAR = 11, + /*!< VL53L1_TIMINGBUDGET_PAR + * @ref stmvl53l1_parameter.value field is timing budget + * in micro second + * + * @note Please refer to VL53L1 user manual for minimum timing budget + * according to the selected mode + * + * Mode / timing budget | Min | Typical + * ------------------------------------- | ----- | ------- + * VL53L1_PRESETMODE_RANGING | 8 ms | 16 ms + * VL53L1_PRESETMODE_MULTIZONES_SCANNING | 8 ms | 16 ms + * VL53L1_PRESETMODE_AUTONOMOUS | 41 ms | 76 ms + * VL53L1_PRESETMODE_LITE_RANGING | 17 ms | 66 ms + * VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS | 20 ms | 20 ms + * + */ + + VL53L1_DISTANCEMODE_PAR = 12, + /*!< VL53L1_DISTANCEMODE_PAR + * valid distance mode value : + * @li 1 @a VL53L1_DISTANCEMODE_SHORT + * @li 2 @a VL53L1_DISTANCEMODE_MEDIUM + * @li 3 @a VL53L1_DISTANCEMODE_LONG + * + * @warning distance mode can only be set while not ranging + */ + + VL53L1_OUTPUTMODE_PAR = 13, + /*!< VL53L1_OUTPUTMODE_PAR + * valid output mode value : + * @li 1 @a VL53L1_OUTPUTMODE_NEAREST + * @li 2 @a VL53L1_OUTPUTMODE_STRONGEST + * + * @warning distance mode can only be set while not ranging + */ + + VL53L1_FORCEDEVICEONEN_PAR = 14, + /*!< VL53L1_FORCEDEVICEONEN_PAR + * This parameter will control if device is put under reset when + * stopped. + * valid force device on value : + * @li 0 feature is disable. Device is put under reset when stopped. + * @li 1 feature is enable. Device is not put under reset when stopped. + */ + + VL53L1_LASTERROR_PAR = 15, + /*!< VL53L1_LASTERROR_PAR + * This is a read only parameter. It will return last device internal + * error. It's valid only after an ioctl/sysfs return an -EIO error. + */ + + VL53L1_OFFSETCORRECTIONMODE_PAR = 16, + /*!< VL53L1_OFFSETCORRECTIONMODE_PAR + * This parameter will define which mode to use for the offset + * correction. + * valid force device on value : + * @li 1 @a VL53L1_OFFSETCORRECTIONMODE_STANDARD + * @li 2 @a VL53L1_OFFSETCORRECTIONMODE_PERZONE + * + * @warning offset correction mode can only be set while not ranging + */ + + VL53L1_OPTICALCENTER_PAR = 17, + /*!< VL53L1_OPTICALCENTER_PAR + * This is a read only parameter. It will return optical center issued + * from the nvm set at FTM stage. value will contain X position of + * center. value2 will contain Y position of center. + * Return values have FixPoint1616_t type. + */ + + VL53L1_DMAXREFLECTANCE_PAR = 18, + /*!< VL53L1_DMAXREFLECTANCE_PAR + * This parameter will define target reflectance @ 940nm used to + * calculate the ambient DMAX. Parameter is of type FixPoint1616_t. + * + * @warning dmax reflectance can only be be set while not ranging + */ + + VL53L1_DMAXMODE_PAR = 19, + /*!< VL53L1_DMAXMODE_PAR + * This parameter will select Dmax mode. + * valid Dmax mode value : + * @li 1 @a VL53L1_DMAXMODE_FMT_CAL_DATA + * @li 2 @a VL53L1_DMAXMODE_CUSTCAL_DATA + * @li 3 @a VL53L1_DMAXMODE_PER_ZONE_CAL_DATA + * + * @warning Dmax mode can only be set while not ranging + */ + + VL53L1_TUNING_PAR = 20, + /*!< VL53L1_DMAXMODE_PAR + * This parameter is a write only parameter. It will allow to provide + * low level layer with a configuration parameter. + * value will be use as a key parameter. + * value2 will be use as value parameter. + * + * @warning those configuration parameter settings are only allowed + * before device is start once. + */ + + VL53L1_SMUDGECORRECTIONMODE_PAR = 21, + /*!< VL53L1_SMUDGECORRECTIONMODE_PAR + * This parameter will control if smudge correction is enable and how + * crosstalk values are updated. + * @li 0 @a VL53L1_SMUDGE_CORRECTION_NONE + * @li 1 @a VL53L1_SMUDGE_CORRECTION_CONTINUOUS + * @li 2 @a VL53L1_SMUDGE_CORRECTION_SINGLE + * @li 3 @a VL53L1_SMUDGE_CORRECTION_DEBUG + */ + + VL53L1_ISXTALKVALUECHANGED_PAR = 22, + /*!< VL53L1_ISXTALKCHANGED_PAR + * This is a read only parameter. It will return if Xtalk value has + * been updated while ranging. This parameter is reset each time device + * start to range. + * @li 0 Xtalk values has not been changed. + * @li 1 Xtalk values has been changed. + */ +}; +#define stmv53l1_parameter_name_e enum __stmv53l1_parameter_name_e + +/** + * parameter structure use in @ref VL53L1_IOCTL_PARAMETER + */ +struct stmvl53l1_parameter { + uint32_t is_read; /*!< [in] 1: Get 0: Set*/ + /*!< [in] parameter to set/get + * see @ref stmv53l1_parameter_name_e + */ + stmv53l1_parameter_name_e name; + int32_t value; /*!< [in/out] value to set /get */ + int32_t value2; /*!< [in/out] optional 2nd value */ + int32_t status; /*!< [out] status of the operation */ +}; + + +/** + * roi structure use as @ref VL53L1_IOCTL_ROI arg + * + * see @ref stmvl53l1_roi_full_t for easy to use type variable declaration + * required + */ +struct stmvl53l1_roi_t { + int32_t is_read; + /*!< specify roi transfer direction \n + * @li 0 to get roi + * @li !0 to set roi + */ + /*! roi data and count type use in @ VL53L1_IOCTL_ROI */ + struct roi_cfg_t { + uint8_t NumberOfRoi; + /*!< [in/out] Number of Rois to set/get + * + * on set :\n + * [in] number of roi to set + * @note 0 set can be used to return to device default roi usage + * + * on get :\n + * [in] max number provided\n + * [out] number of ROI copied back to user\n + * @warning 0 will not return any roi datas! + */ + VL53L1_UserRoi_t UserRois[1]; + /*!< roi data array length definition is 1 but + * NumberOfRoi+ FirstRoiToScan in array are required + * and will be effectively copy to/from user space + * + * @sa stmvl53l1_roi_full_t + */ + } roi_cfg /*! [in/out] roi data and count */; +}; + +/** + * full roi struct use in @ref VL53L1_IOCTL_ROI arg + * + * this definition make easier variable declaration with the max roi storage + * capabilities. + * + * @sa stmvl53l1_roi_t for field details + */ +struct stmvl53l1_roi_full_t { + int32_t is_read; + /*!< specify roi transfer direction \n + * @li 0 to get roi + * @li !0 to set roi + */ + VL53L1_RoiConfig_t roi_cfg; + /*!< roi data array of max length but only requested copy to/from user + * space effectively used + * see @a stmvl53l1_roi_t::roi_cfg for details + */ +}; + +/** + * parameter structure use in @ref VL53L1_IOCTL_CALIBRATION_DATA + */ +struct stmvl53l1_ioctl_calibration_data_t { + int32_t is_read; /*!< [in] 1: Get 0: Set*/ + VL53L1_CalibrationData_t data; + /*!< [in/out] data to set /get. Caller + * should consider this structure as an opaque one + */ +}; + +/** + * Opaque structure use to hold content of zone offset calibration result. + */ +#define stmvl531_zone_calibration_data_t \ + struct _stmvl531_zone_calibration_data_t + +struct _stmvl531_zone_calibration_data_t { + uint32_t id; + VL53L1_ZoneCalibrationData_t data; +}; + +/** + * parameter structure use in @ref VL53L1_IOCTL_ZONE_CALIBRATION_DATA + */ +struct stmvl53l1_ioctl_zone_calibration_data_t { + int32_t is_read; /*!< [in] 1: Get 0: Set*/ + stmvl531_zone_calibration_data_t data; + /*!< [in/out] data to set /get. Caller + * should consider this structure as an opaque one + */ +}; + +/** Select reference spad calibration in @ref VL53L1_IOCTL_PERFORM_CALIBRATION. + * + * param1, param2 and param3 not use + */ +#define VL53L1_CALIBRATION_REF_SPAD 0 + +/** Select crosstalk calibration in @ref VL53L1_IOCTL_PERFORM_CALIBRATION. + * + * param1 is calibration method. param2 and param3 not use. + * @li VL53L1_XTALKCALIBRATIONMODE_NO_TARGET + * @li VL53L1_XTALKCALIBRATIONMODE_SINGLE_TARGET + * @li VL53L1_XTALKCALIBRATIONMODE_FULL_ROI + */ +#define VL53L1_CALIBRATION_CROSSTALK 1 + +/** Select offset calibration @ref VL53L1_IOCTL_PERFORM_CALIBRATION. + * param1 is offset calibration mode. Parameter is either: + * - VL53L1_OFFSETCALIBRATIONMODE_STANDARD + * - VL53L1_OFFSETCALIBRATIONMODE_PRERANGE_ONLY + * - VL53L1_OFFSETCALIBRATIONMODE_MULTI_ZONE (deprecated) + * param2 is target distance in mm. + * param3 is target reflectance in percent. Parameter is of type FixPoint1616_t. + * + * Note that VL53L1_OFFSETCALIBRATIONMODE_MULTI_ZONE usage is deprecated. Per + * zone offset calibration should use VL53L1_CALIBRATION_OFFSET_PER_ZONE + * instead. + */ +#define VL53L1_CALIBRATION_OFFSET 2 + +/** Select offset calibration per zone @ref VL53L1_IOCTL_PERFORM_CALIBRATION. + * param1 is offset calibration mode. Parameter is: + * - VL53L1_OFFSETCALIBRATIONMODE_MULTI_ZONE + * param2 is target distance in mm. + * param3 is target reflectance in percent. Parameter is of type FixPoint1616_t. + * + * Note that region of interest should be defined by a prior call to + * VL53L1_IOCTL_ROI before calling VL53L1_IOCTL_PERFORM_CALIBRATION / + * VL53L1_CALIBRATION_OFFSET combinaison. + */ +#define VL53L1_CALIBRATION_OFFSET_PER_ZONE 3 + +/** Select simple offset calibration @ref VL53L1_IOCTL_PERFORM_CALIBRATION. + * param1 is target distance in mm. + * param2 and param3 are not used + */ +#define VL53L1_CALIBRATION_OFFSET_SIMPLE 4 + +/** Select per Vcsel offset calibration @ref VL53L1_IOCTL_PERFORM_CALIBRATION. + * param1 is target distance in mm. + * param2 and param3 are not used + * this calibration is used by the VL53L1_OFFSETCORRECTIONMODE_PERVCSEL mode + */ +#define VL53L1_CALIBRATION_OFFSET_PER_VCSEL 5 + +/** Select no Distance offset calibration @ref VL53L1_IOCTL_PERFORM_CALIBRATION. + * param1, param2 and param3 are not used + */ +#define VL53L1_CALIBRATION_OFFSET_ZERO_DISTANCE 6 + +/** + * parameter structure use in @ref VL53L1_IOCTL_PERFORM_CALIBRATION + */ +struct stmvl53l1_ioctl_perform_calibration_t { + uint32_t calibration_type; + /*!< [in] select which calibration to do : + * @li @ref VL53L1_CALIBRATION_REF_SPAD + * @li @ref VL53L1_CALIBRATION_CROSSTALK + * @li @ref VL53L1_CALIBRATION_OFFSET + * @li @ref VL53L1_CALIBRATION_OFFSET_PER_ZONE + * @li @ref VL53L1_CALIBRATION_OFFSET_SIMPLE + * @li @ref VL53L1_CALIBRATION_OFFSET_PER_VCSEL + * @li @ref VL53L1_CALIBRATION_OFFSET_ZERO_DISTANCE + */ + uint32_t param1; + /*!< [in] first param. Usage depends on calibration_type */ + uint32_t param2; + /*!< [in] second param. Usage depends on calibration_type */ + uint32_t param3; + /*!< [in] third param. Usage depends on calibration_type */ +}; + +/** + * parameter structure use in @ref VL53L1_IOCTL_AUTONOMOUS_CONFIG + */ +struct stmvl53l1_autonomous_config_t { + int32_t is_read; + /*!< [in] 1: Get 0: Set*/ + uint32_t pollingTimeInMs; + /*!< [in/out] interval between two measure in ms */ + VL53L1_DetectionConfig_t config; + /*!< [int/out] autonomous mode configuration structure */ +}; + +/* + * IOCTL definitions + */ + + +/** + * Start ranging (no argument) + * + * @note sysfs and ioctl control are assumed mutual exclusive use + * control from ioctl execute with no consideration of sysfs path. + * + * @return : + * @li 0 on success + * @li -EBUSY if already started + * @li -ENXIO failed to change i2c address change after reset release + * @li -EIO. Read last_error to get device error code + * @li -ENODEV. Device has been removed. + * + * example user land : + @code + int smtvl53l1_start(int fd){error + int rc; + rc= ioctl(fd, VL53L1_IOCTL_START,NULL); + if( rc ){ + if( errno == EBUSY){ + //the device is already started + ioctl_warn("already started"); + return EBUSY; + } + } + if( rc ){ + ioctl_error("%d %s", rc,strerror(errno)); + } + return rc; + } + @endcode + */ + +#define VL53L1_IOCTL_START _IO('p', 0x01) + +/** + * stop ranging (no argument) + + * @note sysfs and ioctl control are assumed mutual exclusive use + * control from ioctl execute action with no consideration of sysfs path. + * + * @return + * @li 0 on success + * @li -EBUSY if it was already + * @li -EIO. Read last_error to get device error code + * @li -ENODEV. Device has been removed. + * + * example user land : + @code + int smtvl53l1_stop(int fd){ + int rc; + rc= ioctl(fd, VL53L1_IOCTL_STOP,NULL); + if( rc ){ + if( errno == EBUSY ){ + ioctl_warn("already stopped"); + return errno; + } + ioctl_error("%d %s", rc,strerror(errno)); + } + return rc; + } + @endcode + */ +#define VL53L1_IOCTL_STOP _IO('p', 0x05) + +/** + * get single ranging data @sa for multi zone/objet + * + * retrieve the last range data available form the device + * + * @param in/out data struct ptr of type @ref stmvl531_range_data_t + * it may come in but is out as of now + * + * @return 0 on success else o, error check errno + * @li -EFAULT fault in cpy to f/m user out range data not copied + * @li -ENODEV. Device has been removed. + * + * @warning this ioctl will not wait for a new range sample acquisition + * it will return what available at time it get called . Hence same data maybe + * returned many time when doing fast polling.\n + * End user must inspect the data structure (time stamp etc )to find about it\n + * Despite it's non "waiting" nature this ioctl may still block/sleep shortly + * to ensure race free usage acquiring mutex and/or locks. + */ +#define VL53L1_IOCTL_GETDATAS \ + _IOWR('p', 0x0b, stmvl531_range_data_t) + +/** + * set or get parameter + * + * @param parameter in/out @ref stmvl53l1_parameter + * @sa stmv53l1_parameter_name_e + * + * for get if ioctl fail do not check for out params it is not valid + * for set theirs not copy back only see ioctl status, errno to get error case + * + * @return 0 on success else o, error check errno + * @li -ENODEV. Device has been removed. + * + * @note a set parameter may not be absorbed straight aways ! + */ +#define VL53L1_IOCTL_PARAMETER \ + _IOWR('p', 0x0d, struct stmvl53l1_parameter) + + +/** + * set/get roi + * + * shall only be use while device is stopped (EBUSY error otherwise) + * setting 0 rois stand for "disable user define roi usage, use device default" + * + * @param roi_cfg [in/out] type @ref stmvl53l1_roi_t and + * @ref stmvl53l1_roi_full_t + * @note when getting roi the returned roi cnt is set to available number + * of roi in driver but at most requested number or available one + * will be set in returned structure + * @warning the coordinate system is not usual image x,y (y down)but traditional + * ecludian x,y (y up) + * + * @warning once defined the user roi is kept alive until unset by user . + * User shall update roi when required (mode change etc ..)\n + * To return to default unset roi by setting none, device will return to default + * at next start + * + * @note roi validity is only checked at start ranging , as such invalid roi set + * can make start to fail + * + * @return 0 on success , see errno for error detail + * @li EBUSY when trying to set roi while ranging + * @li ENODEV never device get started and trying to get more rois than set + * @li other errno code could be ll driver specific + */ +#define VL53L1_IOCTL_ROI\ + _IOWR('p', 0x0e, struct stmvl53l1_roi_t) + +/** + * Get multi object/zone ranging data + * + * this call is non blocking and will return what available internally + * in all case (veen error) + * + * @param [out] multi zone range @ref VL53L1_MultiRangingData_t always update + * but -EFAULT error case + * + * @return 0 on success else o, error check errno + * @li -EFAULT fault in cpy to f/m user out range data not copyed + * @li -ENOEXEC active mode is not mutli-zone + * @li -ENODEV device is not ranging or device has been removed. + * as in that case MZ data may not be fully valid + */ +#define VL53L1_IOCTL_MZ_DATA\ + _IOR('p', 0x0f, VL53L1_MultiRangingData_t) + +/** + * get single ranging data @sa for multi zone/objet + * + * this call is equivalent to VL53L1_IOCTL_GETDATAS but will block until + * new data are available since previous call. + * + * @param in/out data struct ptr of type @ref stmvl531_range_data_t + * it may come in but is out as of now + * + * @return 0 on success else o, error check errno + * @li -EFAULT fault in cpy to f/m user out range data not copied + * @li -ENODEV device is not ranging or device has been removed. + * @li -ERESTARTSYS interrupt while sleeping. + */ +#define VL53L1_IOCTL_GETDATAS_BLOCKING\ + _IOWR('p', 0x10, stmvl531_range_data_t) + +/** + * Get multi object/zone ranging data + * + * this call is equivalent to VL53L1_IOCTL_MZ_DATA but will block until + * new data are available since previous call. + * + * @param [out] multi zone range @ref VL53L1_MultiRangingData_t always update + * but -EFAULT error case + * + * @return 0 on success else o, error check errno + * @li -EFAULT fault in cpy to f/m user out range data not copyed + * @li -ENOEXEC active mode is not mutli-zone + * @li -ENODEV device is not ranging or device has been removed. + * @li -ERESTARTSYS interrupt while sleeping. + * as in that case MZ data may not be fully valid + */ +#define VL53L1_IOCTL_MZ_DATA_BLOCKING\ + _IOR('p', 0x11, VL53L1_MultiRangingData_t) + +/** + * Get / set calibration data + * + * this call allow client to either read calibration data after calibration + * has been performed to store them in the host filesystem or push calibration + * data before ranging at each start-up. + * + * @param [in/out] data struct ptr of type + * @ref stmvl53l1_ioctl_calibration_data_t. Caller should consider it as an + * opaque structure. + * + * use this after either VL53L1_CALIBRATION_REF_SPAD, + * VL53L1_CALIBRATION_CROSSTALK or VL53L1_CALIBRATION_OFFSET. + * + * @return 0 on success else o, error check errno + * @li -EFAULT fault in cpy to f/m user out range data not copied + * @li -EBUSY when trying to set calibration data while ranging + * @li -EIO. Read last_error to get device error code + * @li -ENODEV. Device has been removed. + */ +#define VL53L1_IOCTL_CALIBRATION_DATA\ + _IOWR('p', 0x12, struct stmvl53l1_ioctl_calibration_data_t) + +/** + * Get / set zone calibration data + * + * this call allow client to either read zone calibration data after calibration + * has been performed to store them in the host filesystem or push zone + * calibration data before ranging at each start-up. + * + * use this after VL53L1_CALIBRATION_OFFSET_PER_ZONE calibration. + * + * @param [in/out] data struct ptr of type + * @ref stmvl53l1_ioctl_zone_calibration_data_t. Caller should consider it as an + * opaque structure. + * + * @return 0 on success else o, error check errno + * @li -EFAULT fault in cpy to f/m user out range data not copied + * @li -EBUSY when trying to set calibration data while ranging + * @li -EIO. Read last_error to get device error code + * @li -ENODEV. Device has been removed. + */ +#define VL53L1_IOCTL_ZONE_CALIBRATION_DATA\ + _IOWR('p', 0x12, struct stmvl53l1_ioctl_zone_calibration_data_t) + +/** + * perform calibration squence according to calibration_type + * + * this call is attended to be used during factory calibration. You select + * calibration to issue using calibration_type. + * + * @param [in] data struct ptr of type + * @ref stmvl53l1_ioctl_perform_calibration_t. + * + * @return 0 on success else o, error check errno + * @li -EFAULT fault in cpy to f/m user out range data not copied + * @li -EBUSY when trying to perform calibration data while ranging + * @li -EIO. Read last_error to get device error code + * @li -ENODEV. Device has been removed. + */ +#define VL53L1_IOCTL_PERFORM_CALIBRATION\ + _IOW('p', 0x13, struct stmvl53l1_ioctl_perform_calibration_t) + +/** + * set/get configure autonomous mode parameters + * + * Allow to get or set autonomous configuration when device operates + * in VL53L1_PRESETMODE_AUTONOMOUS or VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS\n + * Change it only when device is stopped otherwise you will receive + * an EBUSY error. + * + * @param stmvl53l1_autonomous_config_t [in/out] + * + * @note autonomous config validity is only checked at start ranging , as such + * invalid autonomous config set can make start to fail. + * + * @return 0 on success , see errno for error detail + * @li -EFAULT failed to copy from/to configuration. + * @li -EBUSY when trying to change configuration while ranging. + * @li -ENODEV. Device has been removed. + * example user land \n + * trig interrupt when a target is withing [0..300mm[ distance + @code + struct stmvl53l1_autonomous_config_t SD; + SD.is_read = 0; + SD.pollingTimeInMs = 100; + SD.config.DetectionMode = 1; + SD.config.Distance.CrossMode = 0; + SD.config.Distance.Low = 300; + rc = ioctl(fd, VL53L1_IOCTL_AUTONOMOUS_CONFIG, &SD); + @endcode + */ +#define VL53L1_IOCTL_AUTONOMOUS_CONFIG\ + _IOWR('p', 0x14, struct stmvl53l1_autonomous_config_t) + +/** @} */ /* ioctl group */ +#endif /* STMVL53L1_IF_H */ diff --git a/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_internal_if.h b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_internal_if.h new file mode 100644 index 000000000000..17900318e9c7 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_internal_if.h @@ -0,0 +1,100 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +#ifndef STMVL53L1_INTERNAL_IF_H +#define STMVL53L1_INTERNAL_IF_H + +#include "vl53l1_def.h" + +/* interface definition move in this file is not supposed to be use by a normal + * client. It's only here for internal testing purpose. + */ + +/* structure and ioctl that allow raw access to vl53l1 register */ +struct stmvl53l1_register { + uint32_t is_read; /*!< type of the access 1: read 0: write*/ + uint32_t index; /*!< register index */ + uint32_t cnt; /*!< register size shall be 1 to n */ + int32_t status; /*!< operation status 0 ok else error */ + + union reg_data_t { + uint8_t b; /*!< single data byte*/ + uint16_t w; /*!< single data word (16 bits)*/ + uint32_t dw; /*!< single data dword (32 bits)*/ + /*!< any size byte array + * @note only effectively used array size is needed and will be + * set/used another possible register definition is + * @ref stmvl53l1_register_flexi + */ + uint8_t bytes[256]; + /*!< data only *@warning device is big endian and + * no endianess adaptation is performed by + * @ref VL53L1_IOCTL_REGISTER + */ + } data; +}; + +struct stmvl53l1_register_flexi { + uint32_t is_read; /*!< [in] type of the access 1: read 0: write*/ + uint32_t index; /*!< [in] register index */ + uint32_t cnt; /*!< [in] register size shall be 1 to n */ + int32_t status; /*!< [out] operation status 0 ok else error */ + uint8_t data[]; /*!< [in/out] flexible array size data */ + /*!< data only *@warning device is big endian and + * no endianess adaptation is performed by @ref VL53L1_IOCTL_REGISTER + */ +}; + +#define VL53L1_IOCTL_REGISTER _IOWR('p', 0x0c, struct stmvl53l1_register) + +struct stmvl53l1_data_with_additional { + VL53L1_MultiRangingData_t data; + VL53L1_AdditionalData_t additional_data; +}; + +/** + * Get multi object/zone ranging data with additional data for debug + * + * this call is non blocking and will return what available internally + * in all case (veen error) + * + * @param [out] multi zone range @ref VL53L1_MultiRangingData_t always update + * but -EFAULT error case + * + * @return 0 on success else o, error check errno + * @li -EFAULT fault in cpy to f/m user out range data not copyed + * @li -ENOEXEC active mode is not mutli-zone + * @li -ENODEV device is not ranging or device has been removed. + * as in that case MZ data may not be fully valid + */ +#define VL53L1_IOCTL_MZ_DATA_ADDITIONAL\ + _IOR('p', 0x15, struct stmvl53l1_data_with_additional) + +/** + * Get multi object/zone ranging data + * + * this call is equivalent to VL53L1_IOCTL_MZ_DATA_ADDITIONAL but will block + * until new data are available since previous call. + * + * @param [out] multi zone range @ref VL53L1_MultiRangingData_t always update + * but -EFAULT error case + * + * @return 0 on success else o, error check errno + * @li -EFAULT fault in cpy to f/m user out range data not copyed + * @li -ENOEXEC active mode is not mutli-zone + * @li -ENODEV device is not ranging or device has been removed. + * @li -ERESTARTSYS interrupt while sleeping. + * as in that case MZ data may not be fully valid + */ +#define VL53L1_IOCTL_MZ_DATA_ADDITIONAL_BLOCKING\ + _IOR('p', 0x16, struct stmvl53l1_data_with_additional) + +#endif diff --git a/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_ipp.h b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_ipp.h new file mode 100644 index 000000000000..d6f21eadca7d --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_ipp.h @@ -0,0 +1,366 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +/** + * @file stmvl53l1_ipp.h + * + * helper to serialize de-serialize data in ipp exchange between user/kernel + * + * @date Sep 2, 2016 + * @author imaging + */ + +#ifndef _STMVL53L1_IPP_H_ +#define _STMVL53L1_IPP_H_ + +#include "vl53l1_types.h" + + +/** @ingroup ipp_dev + * @{ + */ + +/** + * @defgroup ipp_serialize ST IPP serialization helper + * + * to use the dump help you lust define first a few extra "specific" + * + * @li IPP_PRINT(fmt,..) with typical printf/printk interface + */ + /** @{ */ +/** + * generic data type to serialized scalar and offset for pointer + * + * serialized data can be seen as a first array of n ipp_art_t + * where each input are arg value for scaler type + * and an offset with respect to the ipp_arg base array where the data + * + * @note if all ipp argument can fit on 32 bit then ipp_arg_t is best set as + * a 32 bit base type '(i e uint32_t) to save space + * on 64 bit cpu it can remain 64 bit type to get better alignment + */ +typedef uint64_t ipp_arg_t; + +/** + * set to the cpu structure alignment and packing constrain + * + * all serialized argument passed by pointer will be stored with + * this memory align constrain + * + * + * @note if target cpu is ok to access unaligned data or less constrain ie 64 + * bit data align 32 are ok then it can be set to 4 to save space in data + * packing and copy\n + * We may be over constraining in many cases as a struct is only required to + * be aligned on i'st biggest item size + * + * @warning it must be a 2 power of 2 (& operation instead of mudulo used in + * align calculation) + * @warning using 8 byte constrain require that the @a ipp_arg_t is already + * aligned on that constrain + */ +#define IPP_ALIGN_REQ 8 + +/** + * set x to it's nearest aligned offset (not that 0 is fine as a valid offset + * than will not be up aligned to next align chunk ) + */ +#define IPP_ALIGN_OFFSET(x) (((x)+(IPP_ALIGN_REQ-1)) & (~(IPP_ALIGN_REQ-1))) + +/** + * declare variable needed for ipp serialization + */ +#define IPP_SERIALIZE_VAR int ipp_offset +/** + * Put in function start to init the serialization coding + * + * @param ipp_args buffer for data serialization + * @param n_args is the total number of argument scalar and ptr to serialized + * dot not count out only arg that will be returned + * + * it define local var "ipp_offset" used to accumulate all input args that must + * be serialize. It reserve header room in the buffer to place for scalar args + * and offset for args ptr copy + * + * args pass by pointer must be serialized by @ref IPP_SET_ARG_PTR in order they + * are declare\n + * scalar args can be serialized in any order with @ref IPP_SET_ARG + * + * scalar args that can't be cast to basic @ref ipp_arg_t shall be serialized + * Manually or using a macro similar to ptr that can be pass by copy + * + *@note @ref IPP_SERIALIZE_VAR must be use first + * @code + * int ipp_to _ser(int arg0, struct st_t *pdata1){ + * char *local var; + * ipp_arg_t args[256]; + * IPP_SERIALIZE_START(2); + * IPP_SET_ARG(args, 0, arg0); + * IPP_SET_ARG_PTR(args, 1, pdata); + * do_one_ipp(args); + * } + * @endcode + * + */ +#define IPP_SERIALIZE_START(ipp_args, n_args)\ + (ipp_offset = IPP_ALIGN_OFFSET((char *)(&ipp_args[n_args]) - \ + (char *)ipp_args)) + +/** + * @brief Serialize scalar argument + * + * serialized arg number n into ipp_args\n + * can be used in any order + * + * @param ipp_args the args buffer + * @param n 0 base arg number + * @param v the scalar argument variable + * @warning usable only for scalar type that can be copy on a ipp_arg_t + */ +#define IPP_SET_ARG(ipp_args, n, v) memcpy(&ipp_args[n], &v, sizeof(v)) + +/** + * @brief Serialize an arg passed by pointer + * + * @param ipp_args the arg ptr array + * @param n the arg number to serialize + * @param pdata argument it must be a type like struct x_t * int [n] etc ... + * that size is given by size of and and be copied by a single memcpy + */ +#define IPP_SET_ARG_PTR(ipp_args, n, pdata)\ + do {\ + ipp_args[n] = ipp_offset;\ + memcpy(((char *)ipp_args) + ipp_offset, pdata, sizeof(*pdata));\ + ipp_offset = IPP_ALIGN_OFFSET(ipp_offset + sizeof(*pdata));\ + } while (0) + +/** + * serialize out get ptr for pdata + * + * @note it does not cpy data just set ptr and update the offset + * @warning to be use in order + * + * @param ipp_args the arg ptr array + * @param n the arg number to serialize (unused) + * @param pdata init to ptr in ipp_args type is used for offset computation + * @warning to use sequential in order agr are serialized + */ +#define IPP_OUT_ARG_PTR(ipp_args, n, pdata)\ + do {\ + pdata = (void *)(((char *)ipp_args) + ipp_offset);\ + ipp_offset = IPP_ALIGN_OFFSET(ipp_offset + \ + (int)sizeof(*pdata));\ + } while (0) + + +/** + * @brief ipp get payload + * + * @return paylaod at time used \n + * when all done it's overall out payload + * + * require @ref IPP_SERIALIZE_VAR and @ref IPP_SERIALIZE_START used first\n + * best use after all @ref IPP_OUT_ARG_PTR or @ref IPP_SET_ARG_PTR done to get + * full payload + **/ +#define IPP_SERIALIZE_PAYLAOD() (ipp_offset + IPP_WORK_HDR_SIZE) + +/** + * de-serialize and argument that was passed by value + * @param ipp_args the args array + * @param n the 0 base argument number + * @param v argument it must be exact type + * + * + * @code + * f_deserialize(ipp_arg_t args[]) + * { + * // f_ser is like (uint16_t arg0, struct s_arg_t * arg1, int arg2) + * uint16_t arg0; + * void * parg1; + * int arg2; + * + * IPP_GET_ARG( args, 0, arg0) + * IPP_GET_ARG( args, 2, arg2) + * } + * @endcode + */ +#define IPP_GET_ARG(ipp_args, n, v) memcpy(&v, &ipp_args[n], sizeof(v)) + + +/** + * de-serialize an argument passed by pointer + * + * @param ipp_args the serialized argument array + * @param n 0 base argument number + * @param p ptr to arg to be set + * + * @note unlike serializing de-serializing pointer args data can be done in any + * order + * + * @code + * struct some_struct_t *parg2; + * IPP_GET_ARG_PTR(args,2,parg2); + * @endcode + */ +#define IPP_GET_ARG_PTR(ipp_args, n, p) (p = (void *)((char *)ipp_args + \ + ipp_args[n])) + + + + +/** + * debug macro to pint out all serialized args + * + * Implementation shall define IPP_PRINT_FUNC function to use + */ +#define IPP_PRINT_ARGS(ipp_args, n) \ + do {\ + int i;\ + for (i = 0; i < n; i++)\ + IPP_PRINT("arg#%d/%d is %8d 0x%08x\n", i, n,\ + ipp_args[i], ipp_args[i]);\ + IPP_PRINT("used data size %d\n", ipp_offset);\ + } while (0) + + +/** + * processing code type of proccesing + * + * used in @a ipp_work_t::process_no + */ +enum stmvl53l1_ipp_proccesing_e { + stmvl53l1_ipp_ping = 0, + /*!< stmvl53l1_ipp_ping + * @li can be sent by driver to check client is alive + * @li daemon sent it to identify and register himself to the driver + */ + stmvl53l1_ipp_cal_hist = 1, + /*!< stmvl53l1_ipp_cal_hist process cal hist*/ + + stmvl53l1_ipp_xtalk_calibration = 2, + /*!< stmvl53l1_ipp_xtalk_calibration process crosstalk calibration data + */ + + stmvl53l1_ipp_hist_ambient_dmax = 3, + /*!< stmvl53l1_ipp_hist_ambient_dmax process ambient dmac calculation + * from histogram + */ + + stmvl53l1_ipp_generate_dual_reflectance_xtalk_samples = 4, + /*!< stmvl53l1_ipp_generate_dual_reflectance_xtalk_samples process + * Xtalk data from dual reflectance histogram data + */ + + /** keep last*/ + stmvl53l1_ipp_max /*!< stmvl53l1_ipp_max */ +}; + +/** + * status use on @a ipp_work_t::status + */ +enum stmvl53l1_ipp_status_e { + stmvl53l1_ipp_status_ok = 0, /*!< ok work done */ + stmvl53l1_ipp_status_inv_id, /*!< dev id not supported or invalid */ + stmvl53l1_ipp_status_inv_proc, + /*!< process_no asked not supported or not implemented */ + stmvl53l1_ipp_status_inv_payload, + /*!< data payload for asked processing incorrect*/ + + stmvl53l1_ipp_status_proc_code = 0x100, + /*!< the lowest 8 bit is the error code form the processing */ +}; + +/** + * Ipp work (job) struct + * + * containing header with sequenc control information plus serialized data + */ +struct ipp_work_t { + int8_t dev_id; /*!< [in]/[out] device id */ + /*!< Identify the work do be run see @a stmvl53l1_ipp_proccesing_e */ + uint8_t process_no; + /*!< [out] status from daemon */ + int16_t status; + /*!< [in/out] unique xfer id */ + uint32_t xfer_id; + /*!< [in/out] effective data length including header*/ + uint32_t payload; + +/** max IPP data payload (not including header) + * + * we substract size of of item above + * must be lesss than one netlink packet + */ +#define MAX_IPP_DATA ((4096-4*3)/8) + ipp_arg_t data[MAX_IPP_DATA]; /*!< [in][out] */ +}; + +/** + * size of header only message ( no payload) + * require \#include in user land + */ +#define IPP_WORK_HDR_SIZE (offsetof(struct ipp_work_t, data[0])) +/** + * max payload per ipp transfer + */ +#define IPP_WORK_MAX_PAYLOAD sizeof(struct ipp_work_t) + +/** copy ipp header from src to dest + * + * used to prepare return work using incoming work header + * @param dest dest work ptr + * @param src src work ptr + */ +#define IPP_CPY_HEADER(dest, src) memcpy(dest, src, IPP_WORK_HDR_SIZE) + +/** + * dump in human readble way ipp struct + * + * @note require IPP_PRINT macro + * + * @param pw ipp_work struct to dump + * @param max_data max amount of data to be dump + * @param max_dev max number of dev (for check) + */ +static inline void ipp_dump_work(struct ipp_work_t *pw, uint32_t max_data, + int max_dev) +{ + uint32_t data_cnt; + uint32_t i; + uint8_t *pbdata; + + (void)max_dev; /* avoid warning when not used */ + (void)pbdata; /*avoid warning in case no print no use*/ + + IPP_PRINT("dev #%d (%s)\n", pw->dev_id, pw->dev_id < max_dev ? + "ok" : "bad"); + IPP_PRINT("process #%d (%s)\n", pw->process_no, + pw->process_no < stmvl53l1_ipp_max ? "ok" : "bad"); + IPP_PRINT("status %d\n", pw->status); + IPP_PRINT("Xfer id 0x%08X payload %d bytes (%s)\n", pw->xfer_id, + pw->payload, + pw->payload > IPP_WORK_MAX_PAYLOAD ? "invalid" : "ok"); + data_cnt = pw->payload > IPP_WORK_MAX_PAYLOAD ? + IPP_WORK_MAX_PAYLOAD : pw->payload; + data_cnt = data_cnt > max_data ? max_data : data_cnt; + for (i = 0, pbdata = (uint8_t *)pw->data; i < data_cnt; i++) { + if (i%16 == 0) + IPP_PRINT("\n%4X\t", i); + IPP_PRINT("%02X ", pbdata[i]); + } + IPP_PRINT("\n"); +} + +/** @} */ /* ingroup helper */ + +/** @} */ /* ingroup ipp_dev */ +#endif /* _STMVL53L1_IPP_H_ */ diff --git a/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_ipp_nl.c b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_ipp_nl.c new file mode 100644 index 000000000000..e545d341a077 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_ipp_nl.c @@ -0,0 +1,363 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +/** + * @file stmvl53l1_ipp_nl.c vl53l1 ipp proxy over netlink kernel side + */ +#include +#include +#include +#include +#include + +#include "stmvl53l1.h" + +#include "vl53l1_platform_ipp.h" +#include "stmvl53l1_ipp.h" + +#define IPP_DEBUG 1 +#ifndef IPP_DEBUG +# define _ipp_dump_work(...) (void)0 +#else +# define _ipp_dump_work(...) ipp_dump_work(__VA_ARGS__) +#endif + +#define IPP_STATE_PENDING 1 +#define IPP_STATE_COMPLETED 2 +#define IPP_STATE_CANCELED 4 + +#define IPP_TIMEOUT_MS 100 + +/** the single netlink strut use by all instance + * @note is NULL until set + */ +static struct sock *nl_sk; + +static DEFINE_MUTEX(ipp_mutex); + +/** + * current registered daemon pid + * @note default value 0 or later 1 is kind of invalid and will require + * user space to connect before we can send any packet + */ +static int daemon_pid; + +/** + * next xfer_id (shared other all dev) + * no direct us used @@ref get_next_xfer_id (will get lock) + * @note default to 0 what is "reserved" + */ +static int next_xfer_id; + + +#define ipp_err(fmt, ...) pr_err("STMVL53L1 IPP Err in %s %d :" fmt "\n", \ + __func__, __LINE__, ##__VA_ARGS__) + +#define ipp_warn(fmt, ...) pr_warn("STMVL53L1 IPP wng in %s %d : "fmt"\n",\ + __func__, __LINE__, ##__VA_ARGS__) + +#if 0 +# define ipp_dbg(fmt, ...) pr_info("IPP %s %d " fmt "\n",\ + __func__, __LINE__, ##__VA_ARGS__) +#else +# define ipp_dbg(...) (void)0 +#endif + +/** + * get and managed increment of next xfer_id + * @note will get ipp_mutex + * @return the xfer_id to be used + */ +static int get_next_xfer_id(void) +{ + mutex_lock(&ipp_mutex); + next_xfer_id++; + /*0 is reserved skip it*/ + if (next_xfer_id == 0) + next_xfer_id = 1; + mutex_unlock(&ipp_mutex); + + return next_xfer_id; +} + +static int send_client_msg(void *msg_data, int msg_size) +{ + int rc; + struct sk_buff *skb_out; + struct nlmsghdr *nlh; + void *nl_data; + + ipp_dbg("to send %d byte", msg_size); + skb_out = nlmsg_new(msg_size, 0); + if (!skb_out) { + ipp_err("nlmsg_new fail\n"); + return -1; + } + + nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0); + NETLINK_CB(skb_out).dst_group = 0; /* not in mcast group */ + + nl_data = nlmsg_data(nlh); /*get data ptr from header*/ + memcpy(nl_data, msg_data, msg_size); + + /* FIXME do we real need to lock to send a data other nl_sk ? */ + mutex_lock(&ipp_mutex); + rc = nlmsg_unicast(nl_sk, skb_out, daemon_pid); + if (rc < 0) + ipp_err("fail to send data size %d to pid %d\n", + msg_size, daemon_pid); + /* stat can be done here in else case */ + mutex_unlock(&ipp_mutex); + + return rc; +} + +/* + * ipp lock is held ping already handled + */ +int ipp_in_process(struct ipp_work_t *pwork) +{ + struct stmvl53l1_data *data; + + ipp_dbg("enter"); + _ipp_dump_work(pwork, IPP_WORK_MAX_PAYLOAD, STMVL53L1_CFG_MAX_DEV); + + /* work id check already done */ + data = stmvl53l1_dev_table[pwork->dev_id]; + ipp_dbg("to lock "); + /* Release now useless ipp_mutex for below work since we may deadlock + * with irq path. + */ + mutex_unlock(&ipp_mutex); + mutex_lock(&data->work_mutex); + if (data->ipp.buzy == IPP_STATE_PENDING) { + /* if it was already handled ignore it */ + if (data->ipp.waited_xfer_id == pwork->xfer_id) { + /* ok that is what we are expecting back */ + memcpy(&data->ipp.work_out, pwork, pwork->payload); + data->ipp.buzy |= IPP_STATE_COMPLETED; + ipp_dbg("to wake ipp waiter as buzy state %d", + data->ipp.buzy); + wake_up(&data->ipp.waitq); + goto done_lock; + } + } + /* either not waiting any more or not the expected id drop it */ + ipp_err("dev #%d ippp buzy %d xfer id %d rcv id %d droping it", + data->id, data->ipp.buzy, data->ipp.waited_xfer_id, + pwork->xfer_id); +done_lock: + mutex_unlock(&data->work_mutex); + mutex_lock(&ipp_mutex); + + return 0; +} + +int stmvl53l1_ipp_stop(struct stmvl53l1_data *data) +{ + int rc; + + rc = data->ipp.buzy; + ipp_dbg("#%d to stop buzy %d", data->id, data->ipp.buzy); + if (data->ipp.buzy) { + /* set invalid wait id to discard canceled job when back */ + data->ipp.waited_xfer_id = 0; + data->ipp.buzy |= IPP_STATE_CANCELED|IPP_STATE_COMPLETED; + ipp_dbg("#%dto wake up worker", data->id); + /* wake up worker or abort the thread */ + wake_up(&data->ipp.waitq); + } + + return rc; +} + +/* + * ipp and dev lock are held + * release and re-grabbed here + */ +int stmvl53l1_ipp_do(struct stmvl53l1_data *data, + struct ipp_work_t *pin, struct ipp_work_t *pout) +{ + int xfer_id; + int rc; + bool has_timeout; + + ipp_dbg("enter"); + + xfer_id = get_next_xfer_id(); + /* set xfer and device dependent part of the work */ + pin->dev_id = data->id; + pin->xfer_id = xfer_id; + data->ipp.waited_xfer_id = xfer_id; + /* try to do it */ + rc = send_client_msg(pin, pin->payload); + /* shall we retry if fail to send for some time or number of try ? */ + if (rc < 0) { + rc = -1; + ipp_err("fail to send msg %d", rc); + } else if (data->ipp.buzy == 0) { + /* send ok put the ipp on buzy state while locked */ + data->ipp.buzy = IPP_STATE_PENDING; + /* unlock now that state is marked buzy */ + mutex_unlock(&data->work_mutex); + + /* put task to wait for completion */ + ipp_dbg("to wait"); + has_timeout = !wait_event_timeout(data->ipp.waitq, + (data->ipp.buzy != IPP_STATE_PENDING), + msecs_to_jiffies(IPP_TIMEOUT_MS)); + + /* relock the main lock */ + mutex_lock(&data->work_mutex); + + rc = (data->ipp.buzy & IPP_STATE_CANCELED) || has_timeout ? + -1 : 0; + if (rc) { + ipp_dbg("waking up with from canceled/timeout ipp"); + } else { + /* return status from the ipp itself */ + ipp_dbg("ip back with status %d", data->ipp.status); + rc = data->ipp.status; + } + data->ipp.buzy = 0;/* buzy clear but locked so safe */ + } else { + ipp_dbg("buzy still not zero %d", data->ipp.buzy); + rc = -1; + } + + +/* done_lock: */ + return rc; +} + + +static void stmvl53l1_nl_recv_msg(struct sk_buff *skb_in) +{ + int pid_chg = 0; + int pid; + struct nlmsghdr *nlh; + struct ipp_work_t *pwork; + + ipp_dbg("Entering"); + + nlh = (struct nlmsghdr *)skb_in->data; + pid = nlh->nlmsg_pid; /*pid of sending process */ + + pwork = nlmsg_data(nlh); + if (pwork->payload < IPP_WORK_HDR_SIZE || + pwork->payload > IPP_WORK_MAX_PAYLOAD){ + /* invalid header size */ + ipp_err("invalid msg header size %d", pwork->payload); + _ipp_dump_work(pwork, IPP_WORK_MAX_PAYLOAD, + STMVL53L1_CFG_MAX_DEV); + return; + } + + mutex_lock(&ipp_mutex); + + if ((pwork->dev_id >= STMVL53L1_CFG_MAX_DEV) || (pwork->dev_id < 0)) { + ipp_err("invalid dev id on msg %d", pwork->dev_id); + _ipp_dump_work(pwork, IPP_WORK_MAX_PAYLOAD, + STMVL53L1_CFG_MAX_DEV); + goto done_locked; + } + + if (pwork->process_no == stmvl53l1_ipp_ping) { + /* in that case the payload must be exact status size only + * if not it is a badly format message or bad message + */ + if (pwork->payload != IPP_WORK_HDR_SIZE) { + ipp_err("invalid ping msg size %d!=%zu ", + pwork->payload, IPP_WORK_HDR_SIZE); + _ipp_dump_work(pwork, IPP_WORK_MAX_PAYLOAD, + STMVL53L1_CFG_MAX_DEV); + goto done_locked; + } + /* if pid was not set or change resent all ongoing ipp */ + if (pid != daemon_pid) + ipp_warn("pid chg %d => %d\n", daemon_pid, pid); + else + ipp_dbg("got ping fm pid %d\n", daemon_pid); + daemon_pid = pid; + pid_chg = 1; + } else { + ipp_in_process(pwork); + } + done_locked: + mutex_unlock(&ipp_mutex); +} + +int stmvl53l1_ipp_setup(struct stmvl53l1_data *data) +{ + int rc; + + mutex_lock(&ipp_mutex); + + data->ipp.buzy = 0; + init_waitqueue_head(&data->ipp.waitq); + ipp_dbg("now %d dev daemon pid is %d", STMVL53L1_CFG_MAX_DEV, + daemon_pid); + rc = 0; + + mutex_unlock(&ipp_mutex); + + return rc; +} + +void stmvl53l1_ipp_cleanup(struct stmvl53l1_data *data) +{ + /* nothink to do */ +} + +#if !defined(OLD_NETLINK_API) +struct netlink_kernel_cfg cfg = { + .input = stmvl53l1_nl_recv_msg +}; +#endif + +static int netlink_protocol_type = STMVL531_CFG_NETLINK_USER; + +module_param(netlink_protocol_type, int, 0444); +MODULE_PARM_DESC(netlink_protocol_type, + "select netlink protocol type for ipp communications"); + +int stmvl53l1_ipp_init(void) +{ + mutex_init(&ipp_mutex); + daemon_pid = 1; /* pid 1 is safe should not be use for user space */ + +#if defined(OLD_NETLINK_API) + nl_sk = netlink_kernel_create(&init_net, + netlink_protocol_type, + 0, + stmvl53l1_nl_recv_msg, + NULL, + THIS_MODULE); +#else + nl_sk = netlink_kernel_create(&init_net, + netlink_protocol_type, + &cfg); +#endif + + return nl_sk ? 0 : -1; +} + + +void stmvl53l1_ipp_exit(void) +{ + if (nl_sk != NULL) { + vl53l1_dbgmsg("releasing netlink socket"); + netlink_kernel_release(nl_sk); + nl_sk = NULL; + } +} + + diff --git a/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_log.c b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_log.c new file mode 100644 index 000000000000..bfe5419a6734 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_log.c @@ -0,0 +1,59 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +/** + * @file /stmvl53l1_module.c vl53l1_module ST VL53L1 linux kernel module + * + * This is implementation of low level driver trace support + */ +#include +#include + +#include "stmvl53l1.h" + +#ifdef VL53L1_LOG_ENABLE + +static bool trace_function; +static int trace_module; +static int trace_level; + +module_param(trace_function, bool, 0644); +MODULE_PARM_DESC(trace_function, + "allow tracing of low level function entry and exit"); + +module_param(trace_module, int, 0644); +MODULE_PARM_DESC(trace_module, + "control tracing of low level per module"); + +module_param(trace_level, int, 0644); +MODULE_PARM_DESC(trace_level, + "control tracing of low level per level"); + +void log_trace_print(uint32_t module, uint32_t level, uint32_t function, + const char *format, ...) +{ + va_list args; + + if (function && !trace_function) + return; + + if (!(module & trace_module)) + return; + + if (level > trace_level) + return; + + va_start(args, format); + vprintk(format, args); + va_end(args); +} + +#endif diff --git a/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_module-i2c.c b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_module-i2c.c new file mode 100644 index 000000000000..348ff40bf2ff --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_module-i2c.c @@ -0,0 +1,900 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +/** + * @file stmvl53l1_module-i2c.c + * + * implement STM VL53L1 module interface i2c wrapper + control + * using linux native i2c + gpio + reg api + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * power specific includes + */ +#include +#include +#include +#include +#include +#include + +#include "stmvl53l1-i2c.h" +#include "stmvl53l1.h" + +#define STMVL53L1_SLAVE_ADDR (0x52>>1) + +/** @ingroup drv_port + * @{ + */ + +/** + * control specific debug message echo + * + * Set to 0/1 do not remove + * + * most dbg warn err messages goes true main driver macro + * this one permit some specific debug without activating all main dbg + */ +#define MODI2C_DEBUG 0 + +/* + * mutex to handle device i2c address changes. It allow to avoid multiple + * device active with same i2c addresses at the same time. Note that we don't + * support case where boot_reg has the same value as a final i2c address of + * another device. + */ +static DEFINE_MUTEX(dev_addr_change_mutex); + +/** + * i2c client assigned to our driver + * + * this is use for stm test purpose as we fake client create and regstration + * we stores the i2c client for release in clean-up overwise we wan't reload + * the module multiple time + * + * in a normal dev tree prod system this is not required + */ +static struct i2c_client *stm_test_i2c_client; + +/* + * pi3: + * insmod stmvl53l1.ko force_device=1 adapter_nb=1 xsdn_gpio_nb=19 + * intr_gpio_nb=16 pwren_gpio_nb=12 + * + * panda + * insmod stmvl53l1.ko force_device=1 adapter_nb=4 xsdn_gpio_nb=56 + * intr_gpio_nb=59 pwren_gpio_nb=55 + */ + +static int force_device; +static int adapter_nb = -1; +static int xsdn_gpio_nb = -1; +static int pwren_gpio_nb = -1; +static int intr_gpio_nb = -1; +static int i2c_addr_nb = STMVL53L1_SLAVE_ADDR; + +module_param(force_device, int, 0000); +MODULE_PARM_DESC(force_device, "force device insertion at module init"); + +module_param(adapter_nb, int, 0000); +MODULE_PARM_DESC(adapter_nb, "i2c adapter to use"); + +module_param(xsdn_gpio_nb, int, 0000); +MODULE_PARM_DESC(xsdn_gpio_nb, "select gpio numer to use for vl53l1 reset"); + +module_param(pwren_gpio_nb, int, 0000); +MODULE_PARM_DESC(pwren_gpio_nb, "select gpio numer to use for vl53l1 power"); + +module_param(intr_gpio_nb, int, 0000); +MODULE_PARM_DESC(intr_gpio_nb, "select gpio numer to use for vl53l1 interrupt"); + +/** + * warn message + * + * @warning use only in scope where i2c_data ptr is present + **/ +#define modi2c_warn(fmt, ...)\ + dev_WARN(&i2c_data->client->dev, fmt, ##__VA_ARGS__) + +/** + * err message + * + * @warning use only in scope where i2c_data ptr is present + */ +#define modi2c_err(fmt, ...)\ + dev_err(&i2c_data->client->dev, fmt, ##__VA_ARGS__) + + + +#if MODI2C_DEBUG +# define modi2c_dbg(fmt, ...)\ + pr_devel("%s "fmt"\n", __func__, ##__VA_ARGS__) +#else +# define modi2c_dbg(...) (void)0 +#endif + +struct vl53l1_pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; +} g_pinctrl_info; + +static int insert_device(void) +{ + int ret = 0; + struct i2c_adapter *adapter; + struct i2c_board_info info = { + .type = "stmvl53l1", + .addr = STMVL53L1_SLAVE_ADDR, + }; + + memset(&info, 0, sizeof(info)); + strcpy(info.type, "stmvl53l1"); + info.addr = STMVL53L1_SLAVE_ADDR; + adapter = i2c_get_adapter(adapter_nb); + if (!adapter) { + ret = -EINVAL; + goto done; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 5, 0) + stm_test_i2c_client = i2c_new_device(adapter, &info); +#else + stm_test_i2c_client = i2c_new_client_device(adapter, &info); +#endif + if (!stm_test_i2c_client) + ret = -EINVAL; + +done: + return ret; +} + +static int get_xsdn(struct device *dev, struct i2c_data *i2c_data) +{ + int rc = 0; + + i2c_data->io_flag.xsdn_owned = 0; + if (i2c_data->xsdn_gpio == -1) { + vl53l1_errmsg("reset gpio is required"); + rc = -ENODEV; + goto no_gpio; + } + + vl53l1_dbgmsg("request xsdn_gpio %d", i2c_data->xsdn_gpio); + //gpio_free(i2c_data->xsdn_gpio); + rc = gpio_request(i2c_data->xsdn_gpio, "vl53l1_xsdn"); + if (rc) { + vl53l1_errmsg("fail to acquire xsdn %d", rc); + goto request_failed; + } + + rc = gpio_direction_output(i2c_data->xsdn_gpio, 0); + if (rc) { + vl53l1_errmsg("fail to configure xsdn as output %d", rc); + goto direction_failed; + } + i2c_data->io_flag.xsdn_owned = 1; + + return rc; + +direction_failed: + gpio_free(i2c_data->xsdn_gpio); + +request_failed: +no_gpio: + return rc; +} + +static void put_xsdn(struct i2c_data *i2c_data) +{ + if (i2c_data->io_flag.xsdn_owned) { + vl53l1_dbgmsg("release xsdn_gpio %d", i2c_data->xsdn_gpio); + gpio_free(i2c_data->xsdn_gpio); + i2c_data->io_flag.xsdn_owned = 0; + i2c_data->xsdn_gpio = -1; + } + i2c_data->xsdn_gpio = -1; +} + +static int get_pwren(struct device *dev, struct i2c_data *i2c_data) +{ + int rc = 0; + + i2c_data->io_flag.pwr_owned = 0; + if (i2c_data->pwren_gpio == -1) { + vl53l1_wanrmsg("pwren gpio disable"); + goto no_gpio; + } + + vl53l1_dbgmsg("request pwren_gpio %d", i2c_data->pwren_gpio); + rc = gpio_request(i2c_data->pwren_gpio, "vl53l1_pwren"); + if (rc) { + vl53l1_errmsg("fail to acquire pwren %d", rc); + goto request_failed; + } + + rc = gpio_direction_output(i2c_data->pwren_gpio, 0); + if (rc) { + vl53l1_errmsg("fail to configure pwren as output %d", rc); + goto direction_failed; + } + i2c_data->io_flag.pwr_owned = 1; + + return rc; + +direction_failed: + gpio_free(i2c_data->xsdn_gpio); + +request_failed: +no_gpio: + return rc; +} + +static void put_pwren(struct i2c_data *i2c_data) +{ + if (i2c_data->io_flag.pwr_owned) { + vl53l1_dbgmsg("release pwren_gpio %d", i2c_data->pwren_gpio); + gpio_free(i2c_data->pwren_gpio); + i2c_data->io_flag.pwr_owned = 0; + i2c_data->pwren_gpio = -1; + } + i2c_data->pwren_gpio = -1; +} + +static int get_intr(struct device *dev, struct i2c_data *i2c_data) +{ + int rc = 0; + + i2c_data->io_flag.intr_owned = 0; + if (i2c_data->intr_gpio == -1) { + vl53l1_wanrmsg("no interrupt gpio"); + goto no_gpio; + } + + vl53l1_dbgmsg("request intr_gpio %d", i2c_data->intr_gpio); + rc = gpio_request(i2c_data->intr_gpio, "vl53l1_intr"); + if (rc) { + vl53l1_errmsg("fail to acquire intr %d", rc); + goto request_failed; + } + + rc = gpio_direction_input(i2c_data->intr_gpio); + if (rc) { + vl53l1_errmsg("fail to configure intr as input %d", rc); + goto direction_failed; + } + + i2c_data->irq = gpio_to_irq(i2c_data->intr_gpio); + if (i2c_data->irq < 0) { + vl53l1_errmsg("fail to map GPIO: %d to interrupt:%d\n", + i2c_data->intr_gpio, i2c_data->irq); + goto irq_failed; + } + i2c_data->io_flag.intr_owned = 1; + + return rc; + +irq_failed: +direction_failed: + gpio_free(i2c_data->intr_gpio); + +request_failed: +no_gpio: + return rc; +} + +static void put_intr(struct i2c_data *i2c_data) +{ + if (i2c_data->io_flag.intr_owned) { + if (i2c_data->io_flag.intr_started) { + free_irq(i2c_data->irq, i2c_data); + i2c_data->io_flag.intr_started = 0; + } + vl53l1_dbgmsg("release intr_gpio %d", i2c_data->intr_gpio); + gpio_free(i2c_data->intr_gpio); + i2c_data->io_flag.intr_owned = 0; + } + i2c_data->intr_gpio = -1; +} + +/** + * parse dev tree for all platform specific input + */ +static int stmvl53l1_parse_tree(struct device *dev, struct i2c_data *i2c_data) +{ + struct i2c_client *client = (struct i2c_client *) i2c_data->client; + int rc = 0; + //u32 reg; + + /* if force device is in use then gpio nb comes from module param else + * we use devicetree. + */ + i2c_data->vdd = NULL; + i2c_data->pwren_gpio = -1; + i2c_data->xsdn_gpio = -1; + i2c_data->intr_gpio = -1; + i2c_data->boot_reg = STMVL53L1_SLAVE_ADDR; + if (force_device) { + i2c_data->xsdn_gpio = xsdn_gpio_nb; + i2c_data->pwren_gpio = pwren_gpio_nb; + i2c_data->intr_gpio = intr_gpio_nb; + client->addr = i2c_addr_nb; + } else if (dev->of_node) { + /* power : either vdd or pwren_gpio. try reulator first */ + i2c_data->vdd = regulator_get_optional(dev, "vdd-vl53l1"); + if (IS_ERR(i2c_data->vdd) || i2c_data->vdd == NULL) { + i2c_data->vdd = NULL; + /* try gpio */ + i2c_data->pwren_gpio = of_get_named_gpio(dev->of_node, "pwren-gpio", 0); + if (i2c_data->pwren_gpio < 0) { + i2c_data->pwren_gpio = -1; + vl53l1_errmsg(" get i2c_data->pwren_gpio failed\n"); + } + } + + g_pinctrl_info.pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR_OR_NULL(g_pinctrl_info.pinctrl)) { + g_pinctrl_info.gpio_state_active = + pinctrl_lookup_state(g_pinctrl_info.pinctrl, + "laser_default"); + + g_pinctrl_info.gpio_state_suspend = + pinctrl_lookup_state(g_pinctrl_info.pinctrl, + "laser_suspend"); + } else + vl53l1_errmsg("Getting pinctrl handle failed\n"); + + rc = of_property_read_u32_array(dev->of_node, "xsdn-gpio", + &i2c_data->xsdn_gpio, 1); + if (rc) { + vl53l1_wanrmsg("Unable to find xsdn-gpio %d %d", + rc, i2c_data->xsdn_gpio); + if (of_gpio_count(dev->of_node) >= 2) + i2c_data->xsdn_gpio = of_get_gpio(dev->of_node, + 0); + else + i2c_data->xsdn_gpio = -1; + } + rc = of_property_read_u32_array(dev->of_node, "intr-gpio", + &i2c_data->intr_gpio, 1); + if (rc) { + vl53l1_wanrmsg("Unable to find intr-gpio %d %d", + rc, i2c_data->intr_gpio); + if (of_gpio_count(dev->of_node) >= 2) + i2c_data->intr_gpio = of_get_gpio(dev->of_node, + 1); + else + i2c_data->intr_gpio = -1; + } + rc = of_property_read_u32_array(dev->of_node, "boot-reg", + &i2c_data->boot_reg, 1); + if (rc) { + vl53l1_wanrmsg("Unable to find boot-reg %d %d", + rc, i2c_data->boot_reg); + i2c_data->boot_reg = STMVL53L1_SLAVE_ADDR; + } + } + + /* configure gpios */ + rc = get_xsdn(dev, i2c_data); + if (rc) + goto no_xsdn; + rc = get_pwren(dev, i2c_data); + if (rc) + goto no_pwren; + rc = get_intr(dev, i2c_data); + if (rc) + goto no_intr; + + return rc; + +no_intr: + if (i2c_data->vdd) { + regulator_put(i2c_data->vdd); + i2c_data->vdd = NULL; + } + put_pwren(i2c_data); +no_pwren: + put_xsdn(i2c_data); +no_xsdn: + return rc; +} + +static void stmvl53l1_release_gpios(struct i2c_data *i2c_data) +{ + put_xsdn(i2c_data); + if (i2c_data->vdd) { + regulator_put(i2c_data->vdd); + i2c_data->vdd = NULL; + } + put_pwren(i2c_data); + put_intr(i2c_data); +} + +static int stmvl53l1_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc = 0; + struct stmvl53l1_data *vl53l1_data = NULL; + struct i2c_data *i2c_data = NULL; + + vl53l1_errmsg("Enter %s : 0x%02x\n", client->name, client->addr); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { + rc = -EIO; + return rc; + } + + vl53l1_data = kzalloc(sizeof(struct stmvl53l1_data), GFP_KERNEL); + if (!vl53l1_data) { + rc = -ENOMEM; + return rc; + } + if (vl53l1_data) { + vl53l1_data->client_object = + kzalloc(sizeof(struct i2c_data), GFP_KERNEL); + if (!vl53l1_data) + goto done_freemem; + i2c_data = (struct i2c_data *)vl53l1_data->client_object; + } + i2c_data->client = client; + i2c_data->vl53l1_data = vl53l1_data; + i2c_data->irq = -1 ; /* init to no irq */ + + /* parse and configure hardware */ + rc = stmvl53l1_parse_tree(&i2c_data->client->dev, i2c_data); + if (rc) + goto done_freemem; + + /* setup device name */ + /* vl53l1_data->dev_name = dev_name(&client->dev); */ + + /* setup client data */ + i2c_set_clientdata(client, vl53l1_data); + + /* end up by core driver setup */ + rc = stmvl53l1_setup(vl53l1_data); + if (rc) + goto release_gpios; + + vl53l1_data->sysfs_base = i2c_data->client->addr; + rc = stmvl53l1_sysfs_laser(vl53l1_data, true); + if (rc) + goto release_gpios; + vl53l1_dbgmsg("End\n"); + kref_init(&i2c_data->ref); + + return rc; + +release_gpios: + stmvl53l1_release_gpios(i2c_data); + +done_freemem: + /* kfree safe against NULL */ + kfree(vl53l1_data); + kfree(i2c_data); + + return -1; +} + +static int stmvl53l1_remove(struct i2c_client *client) +{ + struct stmvl53l1_data *data = i2c_get_clientdata(client); + struct i2c_data *i2c_data = (struct i2c_data *)data->client_object; + + vl53l1_dbgmsg("Enter\n"); + mutex_lock(&data->work_mutex); + + stmvl53l1_sysfs_laser(data, false); + + /* main driver cleanup */ + stmvl53l1_cleanup(data); + + /* release gpios */ + stmvl53l1_release_gpios(i2c_data); + + mutex_unlock(&data->work_mutex); + + stmvl53l1_put(data->client_object); + + vl53l1_dbgmsg("End\n"); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int stmvl53l1_suspend(struct device *dev) +{ + struct stmvl53l1_data *data = i2c_get_clientdata(to_i2c_client(dev)); + + vl53l1_dbgmsg("Enter\n"); + mutex_lock(&data->work_mutex); + /* Stop ranging */ + stmvl53l1_pm_suspend_stop(data); + + mutex_unlock(&data->work_mutex); + + vl53l1_dbgmsg("End\n"); + + return 0; +} + +static int stmvl53l1_resume(struct device *dev) +{ +#if 0 + struct stmvl53l1_data *data = i2c_get_clientdata(to_i2c_client(dev)); + + vl53l1_dbgmsg("Enter\n"); + + mutex_lock(&data->work_mutex); + + /* do nothing user will restart measurements */ + + mutex_unlock(&data->work_mutex); + + vl53l1_dbgmsg("End\n"); +#else + vl53l1_dbgmsg("Enter\n"); + vl53l1_dbgmsg("End\n"); +#endif + return 0; +} +#endif + + +static SIMPLE_DEV_PM_OPS(stmvl53l1_pm_ops, stmvl53l1_suspend, stmvl53l1_resume); + +static const struct i2c_device_id stmvl53l1_id[] = { + { STMVL53L1_DRV_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, stmvl53l1_id); + +static const struct of_device_id st_stmvl53l1_dt_match[] = { + { .compatible = "st,"STMVL53L1_DRV_NAME, }, + { }, +}; + +static struct i2c_driver stmvl53l1_driver = { + .driver = { + .name = STMVL53L1_DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = st_stmvl53l1_dt_match, + .pm = &stmvl53l1_pm_ops, + }, + .probe = stmvl53l1_probe, + .remove = stmvl53l1_remove, + .id_table = stmvl53l1_id, + +}; + +/** + * give power to device + * + * @param object the i2c layer object + * @return + */ +int stmvl53l1_power_up_i2c(void *object) +{ + int rc = 0; + struct i2c_data *data = (struct i2c_data *) object; + + vl53l1_dbgmsg("Enter\n"); + + /* turn on power */ + if (data->vdd) { + rc = regulator_enable(data->vdd); + if (rc) { + vl53l1_errmsg("fail to turn on regulator"); + return rc; + } + } else if (data->pwren_gpio != -1) { + gpio_set_value(data->pwren_gpio, 1); + vl53l1_info("slow power on"); + } else + vl53l1_wanrmsg("no power control"); + + return rc; +} + +/** + * remove power to device (reset it) + * + * @param i2c_object the i2c layer object + * @return 0 on success + */ +int stmvl53l1_power_down_i2c(void *i2c_object) +{ + struct i2c_data *data = (struct i2c_data *) i2c_object; + int rc = 0; + + vl53l1_dbgmsg("Enter\n"); + + /* turn off power */ + if (data->vdd) { + rc = regulator_disable(data->vdd); + if (rc) + vl53l1_errmsg("reg disable failed. rc=%d\n", + rc); + } else if (data->pwren_gpio != -1) { + gpio_set_value(data->pwren_gpio, 0); + } + vl53l1_dbgmsg("power off"); + + vl53l1_dbgmsg("End\n"); + + return rc; +} + +static int handle_i2c_address_device_change_lock(struct i2c_data *data) +{ + struct i2c_client *client = (struct i2c_client *) data->client; + uint8_t buffer[3]; + struct i2c_msg msg; + int rc = 0; + + vl53l1_dbgmsg("change device i2c address from 0x%02x to 0x%02x", + data->boot_reg, client->addr); + /* no i2c-access must occur before fw boot time */ + usleep_range(VL53L1_FIRMWARE_BOOT_TIME_US, + VL53L1_FIRMWARE_BOOT_TIME_US + 1); + + /* manually send message to update i2c address */ + buffer[0] = (VL53L1_I2C_SLAVE__DEVICE_ADDRESS >> 8) & 0xFF; + buffer[1] = (VL53L1_I2C_SLAVE__DEVICE_ADDRESS >> 0) & 0xFF; + buffer[2] = client->addr; + msg.addr = data->boot_reg; + msg.flags = client->flags; + msg.buf = buffer; + msg.len = 3; + if (i2c_transfer(client->adapter, &msg, 1) != 1) { + rc = -ENXIO; + vl53l1_errmsg("Fail to change i2c address to 0x%02x", + client->addr); + } + + return rc; +} + +/* reset release will also handle device address change. It will avoid state + * where multiple stm53l1 are bring out of reset at the same time with the + * same boot address. + * Note that we don't manage case where boot_reg has the same value as a final + * i2c address of another device. This case is not supported and will lead + * to unpredictable behavior. + */ +static int release_reset(struct i2c_data *data) +{ + struct i2c_client *client = (struct i2c_client *) data->client; + int rc = 0; + bool is_address_change = client->addr != data->boot_reg; + + if (is_address_change) + mutex_lock(&dev_addr_change_mutex); + + gpio_set_value(data->xsdn_gpio, 1); + if (is_address_change) { + rc = handle_i2c_address_device_change_lock(data); + if (rc) + gpio_set_value(data->xsdn_gpio, 0); + } + + if (is_address_change) + mutex_unlock(&dev_addr_change_mutex); + + return rc; +} + +/** + * release device reset + * + * @param i2c_object the i2c layer object + * @return 0 on success + */ +int stmvl53l1_reset_release_i2c(void *i2c_object) +{ + int rc; + struct i2c_data *data = (struct i2c_data *) i2c_object; + + vl53l1_dbgmsg("Enter\n"); + + rc = release_reset(data); + pinctrl_select_state(g_pinctrl_info.pinctrl, + g_pinctrl_info.gpio_state_active); + if (rc) + goto error; + + /* and now wait for device end of boot */ + data->vl53l1_data->is_delay_allowed = true; + rc = VL53L1_WaitDeviceBooted(&data->vl53l1_data->stdev); + data->vl53l1_data->is_delay_allowed = false; + if (rc) { + gpio_set_value(data->xsdn_gpio, 0); + vl53l1_errmsg("boot fail with error %d", rc); + data->vl53l1_data->last_error = rc; + rc = -EIO; + } + +error: + vl53l1_dbgmsg("End\n"); + + return rc; +} + +/** + * put device under reset + * + * @param i2c_object the i2c layer object + * @return 0 on success + */ +int stmvl53l1_reset_hold_i2c(void *i2c_object) +{ + struct i2c_data *data = (struct i2c_data *) i2c_object; + + vl53l1_dbgmsg("Enter\n"); + + gpio_set_value(data->xsdn_gpio, 0); + + pinctrl_select_state(g_pinctrl_info.pinctrl, + g_pinctrl_info.gpio_state_suspend); + + vl53l1_dbgmsg("End\n"); + + return 0; +} + +int stmvl53l1_init_i2c(void) +{ + int ret = 0; + + vl53l1_dbgmsg("Enter\n"); + + /* register as a i2c client device */ + ret = i2c_add_driver(&stmvl53l1_driver); + if (ret) + vl53l1_errmsg("%d erro ret:%d\n", __LINE__, ret); + + if (!ret && force_device) + ret = insert_device(); + + if (ret) + i2c_del_driver(&stmvl53l1_driver); + + vl53l1_dbgmsg("End with rc:%d\n", ret); + + return ret; +} + + +void stmvl53l1_clean_up_i2c(void) +{ + if (stm_test_i2c_client) { + vl53l1_dbgmsg("to unregister i2c client\n"); + i2c_unregister_device(stm_test_i2c_client); + } +} + +static irqreturn_t stmvl53l1_irq_handler_i2c(int vec, void *info) +{ + struct i2c_data *i2c_data = (struct i2c_data *)info; + + if (i2c_data->irq == vec) { + modi2c_dbg("irq"); + stmvl53l1_intr_handler(i2c_data->vl53l1_data); + modi2c_dbg("over"); + } else { + if (!i2c_data->msg_flag.unhandled_irq_vec) { + modi2c_warn("unmatching vec %d != %d\n", + vec, i2c_data->irq); + i2c_data->msg_flag.unhandled_irq_vec = 1; + } + } + + return IRQ_HANDLED; +} + +/** + * enable and start intr handling + * + * @param object our i2c_data specific object + * @param poll_mode [in/out] set to force mode clear to use irq + * @return 0 on success and set ->poll_mode if it faill ranging wan't start + */ +int stmvl53l1_start_intr(void *object, int *poll_mode) +{ + struct i2c_data *i2c_data; + int rc; + + i2c_data = (struct i2c_data *)object; + /* irq and gpio acquire config done in parse_tree */ + if (i2c_data->irq < 0) { + /* the i2c tree as no intr force polling mode */ + *poll_mode = -1; + return 0; + } + /* clear irq warning report enabe it again for this session */ + i2c_data->msg_flag.unhandled_irq_vec = 0; + /* if started do no nothing */ + if (i2c_data->io_flag.intr_started) { + /* nothing to do */ + *poll_mode = 0; + return 0; + } + + vl53l1_dbgmsg("to register_irq:%d\n", i2c_data->irq); + rc = request_threaded_irq(i2c_data->irq, NULL, + stmvl53l1_irq_handler_i2c, + IRQF_TRIGGER_FALLING|IRQF_ONESHOT, + "vl53l1_interrupt", + (void *)i2c_data); + if (rc) { + vl53l1_errmsg("fail to req threaded irq rc=%d\n", rc); + *poll_mode = 0; + } else { + vl53l1_dbgmsg("irq %d now handled\n", i2c_data->irq); + i2c_data->io_flag.intr_started = 1; + *poll_mode = 0; + } + return rc; +} + +void *stmvl53l1_get(void *object) +{ + struct i2c_data *data = (struct i2c_data *) object; + + vl53l1_dbgmsg("Enter\n"); + kref_get(&data->ref); + vl53l1_dbgmsg("End\n"); + + return object; +} + +static void memory_release(struct kref *kref) +{ + struct i2c_data *data = container_of(kref, struct i2c_data, ref); + + vl53l1_dbgmsg("Enter\n"); + kfree(data->vl53l1_data); + kfree(data); + vl53l1_dbgmsg("End\n"); +} + +void stmvl53l1_put(void *object) +{ + struct i2c_data *data = (struct i2c_data *) object; + + vl53l1_dbgmsg("Enter\n"); + kref_put(&data->ref, memory_release); + vl53l1_dbgmsg("End\n"); +} + +void __exit stmvl53l1_exit_i2c(void *i2c_object) +{ + vl53l1_dbgmsg("Enter\n"); + i2c_del_driver(&stmvl53l1_driver); + vl53l1_dbgmsg("End\n"); +} diff --git a/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_module.c b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_module.c new file mode 100644 index 000000000000..6c13ab7e10ae --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_module.c @@ -0,0 +1,4471 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +/** + * @file /stmvl53l1_module.c vl53l1_module ST VL53L1 linux kernel module + * + * main file + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * API includes + */ + +#include "stmvl53l1.h" +#include "stmvl53l1-i2c.h" + +#include "stmvl53l1_if.h" /* our device interface to user space */ +#include "stmvl53l1_internal_if.h" + +/* + * include default tuning file + */ +#include "stmvl53l1_tunings.h" + +/* + * Abnormal stops error code for HAL Layer + */ +#define ABNORMAL_STOP_1 1 +#define ABNORMAL_STOP_2 2 +#define ABNORMAL_STOP_3 3 + +/** @ingroup vl53l1_config + * @{ + */ +/** + * default polling period delay in millisecond + * + * It can be set at run time via @ref vl53l1_ioctl or @ref sysfs_attrib + * + * @note apply only for device operating in polling mode only + */ +#define STMVL53L1_CFG_POLL_DELAY_MS 5 + +/** + * default timing budget in microsecond + * + * Can be change at run time via @ref vl53l1_ioctl or @ref sysfs_attrib + */ +#define STMVL53L1_CFG_TIMING_BUDGET_US 16000 + +/** default preset ranging mode */ +#define STMVL53L1_CFG_DEFAULT_MODE VL53L1_PRESETMODE_RANGING + +/** default distance mode */ +#define STMVL53L1_CFG_DEFAULT_DISTANCE_MODE VL53L1_DISTANCEMODE_LONG + +/** default crosstalk enable */ +#define STMVL53L1_CFG_DEFAULT_CROSSTALK_ENABLE 1 + +/** default crosstalk for autonomous mode */ +#define STMVL53L1_CFG_DEFAULT_CROSSTALK_AUTONOMOUS 1000 + +/** max distance value mm */ +#define STMVL53L1_MAX_DISTANCE 0x1FFF + +/** default output mode */ +#define STMVL53L1_CFG_DEFAULT_OUTPUT_MODE VL53L1_OUTPUTMODE_NEAREST + +#define STMVL53L1_CFG_DEFAULT_OFFSET_CORRECTION_MODE \ + VL53L1_OFFSETCORRECTIONMODE_STANDARD + +/** default Dmax mode */ +#define STMVL53L1_CFG_DEFAULT_DMAX_MODE VL53L1_DMAXMODE_FMT_CAL_DATA + +/** default smudge correction enable value */ +#define STMVL53L1_CFG_DEFAULT_SMUDGE_CORRECTION_MODE \ + VL53L1_SMUDGE_CORRECTION_NONE + +/** @} */ /* ingroup vl53l1_config */ + +/** @ingroup vl53l1_mod_dbg + * @{ + */ + +/** + * activate dump of roi in roi ctrl operation + * + * @note uses @a vl53l1_dbgmsg for output so make sure to enable debug + * to get roi dump + */ +#define STMVL53L1_CFG_ROI_DEBUG 0 + +/** @} */ /* ingroup vl53l1_mod_dbg*/ + +void st_gettimeofday(struct st_timeval *tv) +{ + struct timespec64 now; + + ktime_get_real_ts64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; +} + +/* #define DEBUG_TIME_LOG */ + + +#ifdef DEBUG_TIME_LOG +struct st_timeval start_tv, stop_tv; +#endif + +/* Set default value to 1 to allow to see module insertion debug messages */ +int stmvl53l1_enable_debug = 1; + +#define VL53L1_INPUT_DEVICE_NAME "STM VL53L1 proximity sensor" + +static long stmvl53l1_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); +static int stmvl53l1_open(struct inode *inode, struct file *file); +static int stmvl53l1_release(struct inode *inode, struct file *file); +static int ctrl_start(struct stmvl53l1_data *data); +static int ctrl_stop(struct stmvl53l1_data *data); +#ifdef CONFIG_COMPAT +static long stmvl53l1_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +#endif + +static bool force_device_on_en_default = true; + +module_param(force_device_on_en_default, bool, 0444); +MODULE_PARM_DESC(force_device_on_en_default, + "select whether force_device_on_en is true or false by default"); + +/* boilerplate for integer parameter */ +#define IMPLEMENT_PARAMETER_INTEGER(sysfs_name, info_name)\ +static ssize_t stmvl53l1_show_##sysfs_name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct stmvl53l1_data *data = dev_get_drvdata(dev); \ + int param; \ +\ + mutex_lock(&data->work_mutex); \ + param = data->sysfs_name; \ + mutex_unlock(&data->work_mutex);; \ +\ + return scnprintf(buf, PAGE_SIZE, "%d\n", param); \ +} \ +\ +static ssize_t stmvl53l1_store_##sysfs_name(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct stmvl53l1_data *data = dev_get_drvdata(dev); \ + int rc; \ + int param; \ +\ + mutex_lock(&data->work_mutex); \ +\ + if (kstrtoint(buf, 0, ¶m)) { \ + vl53l1_errmsg("invalid syntax in %s", buf); \ + rc = -EINVAL; \ + } else \ + rc = stmvl53l1_set_##sysfs_name(data, param); \ +\ + mutex_unlock(&data->work_mutex); \ +\ + return rc ? rc : count; \ +} \ +\ +static int ctrl_param_##sysfs_name(struct stmvl53l1_data *data, \ + struct stmvl53l1_parameter *param) \ +{ \ + int rc; \ +\ + if (param->is_read) { \ + param->value = data->sysfs_name; \ + param->status = 0; \ + vl53l1_dbgmsg("get " info_name " %d", param->value); \ + rc = 0; \ + } else { \ + rc = stmvl53l1_set_##sysfs_name(data, param->value); \ + vl53l1_dbgmsg("rc %d req %d now %d", rc, \ + param->value, data->sysfs_name); \ + } \ +\ + return rc; \ +} + +/** + * module interface struct + * interface to platform speficic device handling , concern power/reset ... + */ +struct stmvl53l1_module_fn_t { + int (*init)(void); /*!< init */ + /** + * clean up job + * @param data module specific data ptr + */ + void (*deinit)(void *data); + /** + * give device power + * @param data specific module storage ptr + * @return 0 on sucess + */ + int (*power_up)(void *data); + /** + * power down TOFO also stop intr + */ + int (*power_down)(void *data); + /* + * release reset so device start. + */ + int (*reset_release)(void *data); + /* + * put device under reset. + */ + int (*reset_hold)(void *data); + + /** + * enable interrupt + * + * @param object : interface speficic ptr + * @note "module specfic ptr is data->client_object + * @return 0 on success else error then drievr wan't start ranging! + * if no interrupt or it can't be hooked but still to operated in poll + * mode then return 0 and force data->poll_mode + * might have to clear poll_mode exlplcilty if to operate in real intr + * mode as pool mode + * is the default + */ + int (*start_intr)(void *object, int *poll_mode); + + void (*clean_up)(void); /*!< optional can be void */ + + /* increment reference counter */ + void *(*get)(void *object); + + /* decrement reference counter and deallocate memory when zero */ + void (*put)(void *object); +}; + +/** i2c module interface*/ +static struct stmvl53l1_module_fn_t stmvl53l1_module_func_tbl = { + .init = stmvl53l1_init_i2c, + .deinit = stmvl53l1_exit_i2c, + .power_up = stmvl53l1_power_up_i2c, + .power_down = stmvl53l1_power_down_i2c, + .reset_release = stmvl53l1_reset_release_i2c, + .reset_hold = stmvl53l1_reset_hold_i2c, + .clean_up = stmvl53l1_clean_up_i2c, + .start_intr = stmvl53l1_start_intr, + .get = stmvl53l1_get, + .put = stmvl53l1_put, +}; + + +#ifndef MIN +# define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +/* + * INPUT Subsys interface + */ + +static void stmvl53l1_input_push_data(struct stmvl53l1_data *data); + +/* + * Mutex to handle device id add/removal + */ +static DEFINE_MUTEX(dev_table_mutex); + +/** + * in-used device LUT + * we need this as the message reception from netlink message can't + * associate directly to a device instance that is as we look up id + * to device data structure + */ +struct stmvl53l1_data *stmvl53l1_dev_table[STMVL53L1_CFG_MAX_DEV]; + +/** + * Misc device device operations + */ +static const struct file_operations stmvl53l1_ranging_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = stmvl53l1_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = stmvl53l1_compat_ioctl, +#endif + .open = stmvl53l1_open, + .release = stmvl53l1_release, +}; + +static int store_last_error(struct stmvl53l1_data *data, int rc) +{ + data->last_error = rc; + + return -EIO; +} + +static int allocate_dev_id(void) +{ + int i; + + mutex_lock(&dev_table_mutex); + + for (i = 0; i < STMVL53L1_CFG_MAX_DEV; i++) + if (!stmvl53l1_dev_table[i]) + break; + i = i < STMVL53L1_CFG_MAX_DEV ? i : -1; + + mutex_unlock(&dev_table_mutex); + + return i; +} + +static void deallocate_dev_id(int id) +{ + mutex_lock(&dev_table_mutex); + + stmvl53l1_dev_table[id] = NULL; + + mutex_unlock(&dev_table_mutex); +} + +/* helpers to manage reader list for blockint ioctl */ +/* call them with lock */ +static void empty_and_free_list(struct list_head *head) +{ + struct stmvl53l1_waiters *waiter; + struct stmvl53l1_waiters *tmp; + + list_for_each_entry_safe(waiter, tmp, head, list) { + list_del(&waiter->list); + kfree(waiter); + } +} + +static int add_reader(pid_t pid, struct list_head *head) +{ + struct stmvl53l1_waiters *new_waiter; + + new_waiter = kmalloc(sizeof(struct stmvl53l1_waiters), GFP_KERNEL); + if (!new_waiter) + return -ENOMEM; + new_waiter->pid = pid; + list_add(&new_waiter->list, head); + + return 0; +} + +static bool is_pid_in_list(pid_t pid, struct list_head *head) +{ + struct stmvl53l1_waiters *waiter; + + list_for_each_entry(waiter, head, list) + if (waiter->pid == pid) + return true; + + return false; +} + +static void wake_up_data_waiters(struct stmvl53l1_data *data) +{ + empty_and_free_list(&data->simple_data_reader_list); + empty_and_free_list(&data->mz_data_reader_list); + wake_up(&data->waiter_for_data); +} + +static void stmvl53l1_insert_flush_events_lock(struct stmvl53l1_data *data) +{ + while (data->flush_todo_counter) { + data->flushCount++; + input_report_abs(data->input_dev_ps, ABS_GAS, data->flushCount); + input_sync(data->input_dev_ps); + vl53l1_dbgmsg("Sensor HAL Flush Count = %u\n", + data->flushCount); + data->flush_todo_counter--; + } +} + +static int reset_release(struct stmvl53l1_data *data) +{ + int rc; + + if (!data->reset_state) + return 0; + + vl53l1_dbgmsg("turn on vdd\n"); + rc = stmvl53l1_module_func_tbl.power_up(data->client_object); + if (rc) { + vl53l1_errmsg("%d,error rc %d\n", __LINE__, rc); + return rc; + } + + rc = stmvl53l1_module_func_tbl.reset_release(data->client_object); + if (rc) + vl53l1_errmsg("reset release fail rc=%d\n", rc); + else + data->reset_state = 0; + + return rc; +} + +static int reset_hold(struct stmvl53l1_data *data) +{ + int rc; + + if (data->reset_state) + return 0; + + if (data->force_device_on_en) + return 0; + + rc = stmvl53l1_module_func_tbl.reset_hold(data->client_object); + if (!rc) + data->reset_state = 1; + + vl53l1_dbgmsg("turn off vdd\n"); + rc = stmvl53l1_module_func_tbl.power_down(data->client_object); + if (rc) { + vl53l1_errmsg("%d,error rc %d\n", __LINE__, rc); + return rc; + } + + return rc; +} + +/** + * + * @param pstart_tv time val starting point + * @param pstop_tv time val end point + * @return time dif in usec + */ +long stmvl53l1_tv_dif(struct st_timeval *pstart_tv, struct st_timeval *pstop_tv) +{ + long total_sec, total_usec; + + total_sec = pstop_tv->tv_sec - pstart_tv->tv_sec; + total_usec = (pstop_tv->tv_usec - pstart_tv->tv_usec); + + return total_sec*1000000+total_usec; +} + +#if STMVL53L1_CFG_ROI_DEBUG +static void dump_roi(VL53L1_UserRoi_t *rois, uint32_t n) +{ + uint32_t i; + + vl53l1_dbgmsg("roi dump %d roi:\n", n); + for (i = 0; i < n ; i++) { + vl53l1_dbgmsg("ROI#%02d %2d %2d %2d %2d\n", (int)i, + (int)rois[i].TopLeftX, (int)rois[i].TopLeftY, + (int)rois[i].BotRightX, (int)rois[i].BotRightY); + } +} +#else +# define dump_roi(...) (void)0 +#endif + +static int setup_tunings(struct stmvl53l1_data *data) +{ + int rc = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(tunings); i++) { + rc = VL53L1_SetTuningParameter(&data->stdev, tunings[i][0], + tunings[i][1]); + if (rc) { + rc = store_last_error(data, rc); + break; + } + } + + return rc; +} + +/** + * + * @param data device data + * @return non 0 if current "preset mode" is a multi zone one + */ +static int is_mz_mode(struct stmvl53l1_data *data) +{ + return data->preset_mode == VL53L1_PRESETMODE_RANGING || + data->preset_mode == VL53L1_PRESETMODE_MULTIZONES_SCANNING; +} + +static void kill_mz_data(VL53L1_MultiRangingData_t *pdata) +{ + int i; + + memset(pdata, 0, sizeof(*pdata)); + for (i = 0; i < VL53L1_MAX_RANGE_RESULTS; i++) + pdata->RangeData[i].RangeStatus = VL53L1_RANGESTATUS_NONE; + pdata->RoiStatus = VL53L1_ROISTATUS_NOT_VALID; +} + +static void stmvl53l1_setup_auto_config(struct stmvl53l1_data *data) +{ + /* default config is detect object below 300mm with 1s period */ + data->auto_pollingTimeInMs = 1000; + data->auto_config.DetectionMode = VL53L1_DETECTION_DISTANCE_ONLY; + data->auto_config.IntrNoTarget = 0; + data->auto_config.Distance.CrossMode = VL53L1_THRESHOLD_CROSSED_LOW; + data->auto_config.Distance.High = 1000; + data->auto_config.Distance.Low = 300; + data->auto_config.Rate.CrossMode = VL53L1_THRESHOLD_CROSSED_LOW; + data->auto_config.Rate.High = 0; + data->auto_config.Rate.Low = 0; +} + +static uint32_t stmvl53l1_compute_hash(VL53L1_RoiConfig_t *roi_cfg) +{ + return jhash(roi_cfg->UserRois, + roi_cfg->NumberOfRoi * sizeof(VL53L1_UserRoi_t), + roi_cfg->NumberOfRoi); +} + +static int stmvl53l1_check_calibration_id(struct stmvl53l1_data *data) +{ + uint32_t roi_id; + int rc; + + if (data->offset_correction_mode != VL53L1_OFFSETCORRECTIONMODE_PERZONE) + return 0; + if (data->current_roi_id == 0) + return 0; + + roi_id = stmvl53l1_compute_hash(&data->roi_cfg); + rc = roi_id == data->current_roi_id ? 0 : -EINVAL; + + if (rc) + vl53l1_errmsg( + "Mismatch in zone calibration data 0x%08x != 0x%08x", + roi_id, data->current_roi_id); + + return rc; +} + +/** + * send params to sensor + * + * @warning must be used if only stopped + * @param data device data + * @return 0 on sucess + */ +static int stmvl53l1_sendparams(struct stmvl53l1_data *data) +{ + int rc; + VL53L1_CalibrationData_t cali; + + /* activated stored or last request defined mode */ + rc = VL53L1_SetPresetMode(&data->stdev, data->preset_mode); + if (rc) { + vl53l1_errmsg("VL53L1_SetPresetMode %d fail %d", + data->preset_mode, rc); + rc = store_last_error(data, rc); + goto done; + } + + if (data->preset_mode == VL53L1_PRESETMODE_AUTONOMOUS) { + /* disable xtalk and set short distance mode + * for autonomous mode */ + data->crosstalk_enable = 0; + data->distance_mode = VL53L1_DISTANCEMODE_SHORT; + } else { + /* restore xtalk and distance mode to default + * values for non-autonomous mode */ + data->crosstalk_enable = + STMVL53L1_CFG_DEFAULT_CROSSTALK_ENABLE; + data->distance_mode = + STMVL53L1_CFG_DEFAULT_DISTANCE_MODE; + } + + rc = VL53L1_SetXTalkCompensationEnable(&data->stdev, + data->crosstalk_enable); + if (rc) { + vl53l1_errmsg("VL53L1_SetXTalkCompensationEnable %d fail %d", + data->crosstalk_enable, rc); + rc = store_last_error(data, rc); + goto done; + } + + /* apply distance mode only in lite and standard ranging */ + rc = VL53L1_SetDistanceMode(&data->stdev, data->distance_mode); + if (rc) { + vl53l1_errmsg("VL53L1_SetDistanceMode %d fail %d", + data->distance_mode, rc); + rc = store_last_error(data, rc); + goto done; + } + + /* apply timing budget */ + rc = VL53L1_SetMeasurementTimingBudgetMicroSeconds(&data->stdev, + data->timing_budget); + if (rc) { + vl53l1_errmsg("SetTimingBudget %d fail %d", + data->timing_budget, rc); + rc = store_last_error(data, rc); + goto done; + } + vl53l1_dbgmsg("timing budget @%d\n", data->timing_budget); + + /* apply offset correction mode */ + rc = VL53L1_SetOffsetCorrectionMode(&data->stdev, + data->offset_correction_mode); + if (rc) { + vl53l1_errmsg("offset correction mode %d fail %d", + data->offset_correction_mode, rc); + rc = store_last_error(data, rc); + goto done; + } + vl53l1_dbgmsg("offset correction mode @%d\n", + data->offset_correction_mode); + + /* check zone calibration vs roi */ + rc = stmvl53l1_check_calibration_id(data); + if (rc) + goto done; + + /* apply Dmax reflectance */ + rc = VL53L1_SetDmaxReflectance(&data->stdev, data->dmax_reflectance); + if (rc) { + vl53l1_errmsg("dmax relectance %d fail %d", + data->dmax_reflectance, rc); + rc = store_last_error(data, rc); + goto done; + } + vl53l1_dbgmsg("dmax reflectance @%d\n", data->dmax_reflectance); + + /* apply Dmax mode */ + rc = VL53L1_SetDmaxMode(&data->stdev, data->dmax_mode); + if (rc) { + vl53l1_errmsg("dmax mode %d fail %d", data->dmax_mode, rc); + rc = store_last_error(data, rc); + goto done; + } + vl53l1_dbgmsg("dmax mode @%d\n", data->dmax_mode); + + /* apply smudge correction enable */ + rc = VL53L1_SmudgeCorrectionEnable(&data->stdev, + data->smudge_correction_mode); + if (rc) { + vl53l1_errmsg("smudge correction mode %d fail %d", + data->smudge_correction_mode, rc); + rc = store_last_error(data, rc); + goto done; + } + vl53l1_dbgmsg("smudge correction mode @%d\n", + data->smudge_correction_mode); + + /* apply roi if any set */ + if (data->roi_cfg.NumberOfRoi) { + rc = VL53L1_SetROI(&data->stdev, &data->roi_cfg); + if (rc) { + vl53l1_errmsg("VL53L1_SetROI fail %d\n", rc); + rc = store_last_error(data, rc); + goto done; + } + vl53l1_dbgmsg("#%d custom ROI set status\n", + data->roi_cfg.NumberOfRoi); + } else { + vl53l1_dbgmsg("using default ROI\n"); + } + + /* init the timing */ + st_gettimeofday(&data->start_tv); + data->meas.start_tv = data->start_tv; + /* init the ranging data => kill the previous ranging mz data */ + kill_mz_data(&data->meas.multi_range_data); + /* kill the single ranging data */ + memset(&data->meas.single_range_data, 0, + sizeof(VL53L1_RangingMeasurementData_t)); + + memset(&cali, 0, sizeof(cali)); + if (!data->cali_data) { + rc = VL53L1_GetCalibrationData(&data->stdev, &cali); + if (rc) { + vl53l1_errmsg("VL53L1_GetCalibrationData fail\n"); + rc = store_last_error(data, rc); + goto done; + } + } else { + memcpy(&cali, data->cali_data, sizeof(VL53L1_CalibrationData_t)); + } + + /* set autonomous mode configuration */ + if ((data->preset_mode == VL53L1_PRESETMODE_AUTONOMOUS) || + (data->preset_mode == VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS)) { + if (data->cam_mode == 0) { + rc = VL53L1_SetInterMeasurementPeriodMilliSeconds( + &data->stdev, + data->auto_pollingTimeInMs); + if (rc) { + vl53l1_errmsg("Fail to set auto period %d\n", + rc); + rc = store_last_error(data, rc); + goto done; + } + rc = VL53L1_SetThresholdConfig(&data->stdev, + &data->auto_config); + if (rc) { + vl53l1_errmsg("Fail to set auto config %d\n", + rc); + rc = store_last_error(data, rc); + goto done; + } + } else { + /* set camera autonomous configration */ + rc = VL53L1_SetInterMeasurementPeriodMilliSeconds( + &data->stdev, + data->auto_pollingTimeInMs_cam); + if (rc) { + vl53l1_errmsg("Fail to set auto period %d\n", + rc); + rc = store_last_error(data, rc); + goto done; + } + rc = VL53L1_SetThresholdConfig(&data->stdev, + &data->auto_config_cam); + if (rc) { + vl53l1_errmsg("Fail to set auto config %d\n", + rc); + rc = store_last_error(data, rc); + goto done; + } + } + cali.customer.algo__crosstalk_compensation_plane_offset_kcps + = STMVL53L1_CFG_DEFAULT_CROSSTALK_AUTONOMOUS; + } else { + cali.customer.algo__crosstalk_compensation_plane_offset_kcps + = data->xtalk_offset; + } + + rc = VL53L1_SetCalibrationData(&data->stdev, &cali); + if (rc) { + vl53l1_errmsg("VL53L1_SetCalibrationData fail\n"); + rc = store_last_error(data, rc); + goto done; + } + +done: + + return rc; +} + +/** + * start sensor + * + * @warning must be used if only stopped + * @param data device data + * @return 0 on sucess + */ +static int stmvl53l1_start(struct stmvl53l1_data *data) +{ + int rc; + + data->is_first_irq = true; + data->is_data_valid = false; + data->is_xtalk_value_changed = false; +#ifdef PHIO_TIMEOUT_FIX + data->not_first_frame = false; +#endif + + rc = reset_release(data); + if (rc) + goto done; + + /* full setup when out of reset or power up */ + rc = VL53L1_StaticInit(&data->stdev); + if (rc) { + vl53l1_errmsg("VL53L1_StaticInit @%d fail %d\n", + __LINE__, rc); + rc = store_last_error(data, rc); + goto done; + } + + rc = stmvl53l1_sendparams(data); + if (rc) + goto done; + + /* init the timing */ + st_gettimeofday(&data->start_tv); + data->meas.start_tv = data->start_tv; + /* init the ranging data => kill the previous ranging mz data */ + kill_mz_data(&data->meas.multi_range_data); + /* kill the single ranging data */ + memset(&data->meas.single_range_data, 0, + sizeof(VL53L1_RangingMeasurementData_t)); + + data->allow_hidden_start_stop = false; + /* kick off ranging */ + rc = VL53L1_StartMeasurement(&data->stdev); + if (rc) { + vl53l1_errmsg("VL53L1_StartMeasurement @%d fail %d", + __LINE__, rc); + rc = store_last_error(data, rc); + goto done; + } + + data->meas.cnt = 0; + data->meas.err_cnt = 0; + data->meas.err_tot = 0; + data->meas.poll_cnt = 0; + data->meas.intr = 0; + data->enable_sensor = 1; + if (data->poll_mode) { + /* kick off the periodical polling work */ + schedule_delayed_work(&data->dwork, + msecs_to_jiffies(data->poll_delay_ms)); + } +done: + data->is_first_start_done = true; + + return rc; +} + +/** + * stop sensor + * + * work lock must be held + * @warning to be used if only started! + */ +static int stmvl53l1_stop(struct stmvl53l1_data *data) +{ + int rc; + + rc = VL53L1_StopMeasurement(&data->stdev); + if (rc) { + vl53l1_errmsg("VL53L1_StopMeasurement @%d fail %d", + __LINE__, rc); + rc = store_last_error(data, rc); + } + /* put device under reset */ + /* do we ask explicit intr stop or just use stop */ + reset_hold(data); + + data->enable_sensor = 0; + if (data->poll_mode) { + /* cancel periodical polling work */ + cancel_delayed_work(&data->dwork); + } + + /* wake up all waiters */ + /* they will receive -ENODEV error */ + wake_up_data_waiters(data); + + return rc; +} + +/* + * SysFS support + */ +static ssize_t stmvl53l1_show_enable_ps_sensor(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + + return snprintf(buf, 5, "%d\n", data->enable_sensor); +} + +static ssize_t stmvl53l1_store_enable_ps_sensor(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int rc = 0; + unsigned long val; + + rc = kstrtoul(buf, 10, &val); + if (rc) { + vl53l1_errmsg("enable sensor syntax in %s\n", buf); + return -EINVAL; + } + if (val == 1) { + rc = ctrl_start(data); + } else if (val == 0) { + rc = ctrl_stop(data); + } else { + /* TODO: Remove this workaround after investigation + * see Codex - 479397 for details + */ + vl53l1_dbgmsg("Unclog Input sub-system\n"); + /* Unclog the input device sub-system */ + input_report_abs(data->input_dev_ps, ABS_HAT0X, -1); + input_report_abs(data->input_dev_ps, ABS_HAT0Y, -1); + input_report_abs(data->input_dev_ps, ABS_HAT1X, -1); + input_report_abs(data->input_dev_ps, ABS_HAT1Y, -1); + input_report_abs(data->input_dev_ps, ABS_HAT2X, -1); + input_report_abs(data->input_dev_ps, ABS_HAT2Y, -1); + input_report_abs(data->input_dev_ps, ABS_HAT3X, -1); + input_report_abs(data->input_dev_ps, ABS_HAT3Y, -1); + input_report_abs(data->input_dev_ps, ABS_WHEEL, -1); + input_report_abs(data->input_dev_ps, ABS_BRAKE, -1); + input_report_abs(data->input_dev_ps, ABS_GAS, -1); + input_report_abs(data->input_dev_ps, ABS_TILT_X, -1); + input_report_abs(data->input_dev_ps, ABS_TILT_Y, -1); + input_report_abs(data->input_dev_ps, ABS_TOOL_WIDTH, -1); + input_report_abs(data->input_dev_ps, ABS_DISTANCE, -1); + input_report_abs(data->input_dev_ps, ABS_THROTTLE, -1); + input_report_abs(data->input_dev_ps, ABS_RUDDER, -1); + input_report_abs(data->input_dev_ps, ABS_MISC, -1); + input_report_abs(data->input_dev_ps, ABS_VOLUME, + -1); + input_sync(data->input_dev_ps); + vl53l1_dbgmsg("Unclog the input sub-system\n"); + rc = 0; + } + + vl53l1_dbgmsg("End\n"); + + return rc ? rc : count; +} + + +static ssize_t stmvl53l1_show_product_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int param; + + mutex_lock(&data->work_mutex); + param = data->product_type; + mutex_unlock(&data->work_mutex); + + return scnprintf(buf, PAGE_SIZE, "%d\n", param); +} + +/** + * sysfs attribute "product_type" [rd] + * + * @li read show the device product type + * + * @return 0 on success , EINVAL if fail to start + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(product_type, 0440/*S_IRUGO*/, + stmvl53l1_show_product_type, + NULL); + +/** + * sysfs attribute "enable_ps_sensor" [rd/wr] + * + * @li read show the current enable state + * @li write set the new state value "0" put sensor off "1" put it on + * + * @return 0 on success , EINVAL if fail to start + * + * @warning their's no check and assume exclusive usage of sysfs and ioctl\n + * Sensor will be put on/off disregard of any setup done by the ioctl channel. + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(enable_ps_sensor, 0664/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_enable_ps_sensor, + stmvl53l1_store_enable_ps_sensor); + +static int stmvl53l1_set_poll_delay_ms(struct stmvl53l1_data *data, int delay) +{ + int rc = 0; + + if (delay <= 0) + rc = -EINVAL; + else + data->poll_delay_ms = delay; + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(poll_delay_ms, "poll delay ms") + +/** + * sysfs attribute "poll_delay_ms" [rd/wr] + * + * @li read show the current polling delay in millisecond + * @li write set the new polling delay in millisecond + * + * @note apply only if device is in polling mode\n + * for best performances (minimal delay and cpu load ) set it to the device + * period operating period +1 millis + + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(set_delay_ms, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_poll_delay_ms, + stmvl53l1_store_poll_delay_ms); + +/* Timing Budget */ +static int stmvl53l1_set_timing_budget(struct stmvl53l1_data *data, int timing) +{ + int rc = 0; + + if (timing <= 0) { + vl53l1_errmsg("invalid timing valid %d\n", timing); + rc = -EINVAL; + } else if (data->enable_sensor) { + rc = VL53L1_SetMeasurementTimingBudgetMicroSeconds(&data->stdev, + timing); + if (rc) { + vl53l1_errmsg("SetTimingBudget %d fail %d", timing, rc); + rc = store_last_error(data, rc); + } else + data->timing_budget = timing; + } else + data->timing_budget = timing; + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(timing_budget, "timing budget") + +/** + * sysfs "timing_budget" [rd/wr] + * + * set or get the ranging timing budget in microsecond + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(timing_budget, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_timing_budget, + stmvl53l1_store_timing_budget); + + +static ssize_t stmvl53l1_show_roi(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int i; + int n; + + + mutex_lock(&data->work_mutex); + if (data->roi_cfg.NumberOfRoi == 0) { + /* none define by user */ + /* we could get what stored but may not even be default */ + n = scnprintf(buf, PAGE_SIZE, "device default\n"); + } else { + for (i = 0, n = 0; i < data->roi_cfg.NumberOfRoi; i++) { + n += scnprintf(buf+n, PAGE_SIZE-n, "%d %d %d %d%c", + data->roi_cfg.UserRois[i].TopLeftX, + data->roi_cfg.UserRois[i].TopLeftY, + data->roi_cfg.UserRois[i].BotRightX, + data->roi_cfg.UserRois[i].BotRightY, + i == data->roi_cfg.NumberOfRoi-1 ? + '\n' : ','); + } + } + mutex_unlock(&data->work_mutex); + return n; +} + + + +static ssize_t stmvl53l1_store_roi(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + VL53L1_UserRoi_t rois[VL53L1_MAX_USER_ZONES]; + int rc; + + mutex_lock(&data->work_mutex); + if (data->enable_sensor) { + vl53l1_errmsg(" cant set roi now\n"); + rc = -EBUSY; + } else { + int n, n_roi = 0; + const char *pc = buf; + int tlx, tly, brx, bry; + + while (n_roi < VL53L1_MAX_USER_ZONES && pc != NULL + && *pc != 0 && *pc != '\n') { + n = sscanf(pc, "%d %d %d %d", &tlx, &tly, &brx, &bry); + if (n == 4) { + rois[n_roi].TopLeftX = tlx; + rois[n_roi].TopLeftY = tly; + rois[n_roi].BotRightX = brx; + rois[n_roi].BotRightY = bry; + n_roi++; + } else { + vl53l1_errmsg( +"wrong roi #%d syntax around %s of %s", n_roi, pc, buf); + n_roi = -1; + break; + } + /* find next roi separator */ + pc = strchr(pc, ','); + if (pc) + pc++; + } + /*if any set them */ + if (n_roi >= 0) { + if (n_roi) + memcpy(data->roi_cfg.UserRois, rois, + n_roi*sizeof(rois[0])); + data->roi_cfg.NumberOfRoi = n_roi; + dump_roi(data->roi_cfg.UserRois, + data->roi_cfg.NumberOfRoi); + rc = count; + } else { + rc = -EINVAL; + } + } + mutex_unlock(&data->work_mutex); + vl53l1_dbgmsg("ret %d count %d\n", rc, (int)count); + + return rc; +} + +/** + * sysfs attribute "roi" [rd/wr] + * + * @li read show the current user customized roi setting + * @li write set user custom roi, it can only be done while not ranging. + * + * syntax for set input roi + * @li "[tlx tly brx bry,]\n" repeat n time require will set the n roi + * @li "\n" will reset + * + * @warning roi coordinate is not image x,y(down) but euclidian x,y(up) + * + * @warning roi validity is only check at next range start + * @warning user is responsible to set appropriate number an roi before each + * mode change + * @note roi can be return to default by setting none "" + * + *@code + * >#to set 2x roi + * >echo "0 15 15 0, 0 8 8 0" > /sys/class/input6/roi + * >echo $? + * 0 + * >cat /sys/class/input6/roi + * "0 15 15 0,0 8 8 0" + * #to cancel user define roi" + * >echo "" > /sys/class/input1/roi + * >echo $? + * 0 + * >echo "1" > /sys/class/input6/enable_ps_senspor + * #try to set roi while ranging + * >echo "0 15 15 0, 0 8 8 0" > /sys/class/input6/roi + * [58451.912109] stmvl53l1_store_roi: cant set roi now + * >echo $? + * 1 + *@endcode + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(roi, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_roi, + stmvl53l1_store_roi); + + +static int stmvl53l1_set_preset_mode(struct stmvl53l1_data *data, int mode) +{ + int rc = 0; + + if (data->enable_sensor) { + vl53l1_errmsg("can't change mode while ranging\n"); + rc = -EBUSY; + } else { + switch (mode) { + case VL53L1_PRESETMODE_RANGING: + case VL53L1_PRESETMODE_MULTIZONES_SCANNING: + case VL53L1_PRESETMODE_LITE_RANGING: + case VL53L1_PRESETMODE_AUTONOMOUS: + case VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS: + data->preset_mode = mode; + break; + default: + vl53l1_errmsg("invalid mode %d\n", mode); + rc = -EINVAL; + break; + } + } + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(preset_mode, "preset mode") + +/** + * sysfs attribute "mode " [rd/wr] + * + * set the mode value can only be used while: not ranging + * @li 1 @a VL53L1_PRESETMODE_RANGING default ranging + * @li 2 @a VL53L1_PRESETMODE_MULTIZONES_SCANNING multiple zone + * @li 3 @a VL53L1_PRESETMODE_AUTONOMOUS autonomous mode + * @li 4 @a VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS low Power autonomous mode + * @li 5 @a VL53L1_PRESETMODE_LITE_RANGING low mips ranging mode + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(mode, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_preset_mode, + stmvl53l1_store_preset_mode); + +static int stmvl53l1_set_distance_mode(struct stmvl53l1_data *data, + int distance_mode) +{ + int rc = 0; + + if (data->enable_sensor) { + vl53l1_errmsg("can't change distance mode while ranging\n"); + rc = -EBUSY; + } else { + switch (distance_mode) { + case VL53L1_DISTANCEMODE_SHORT: + case VL53L1_DISTANCEMODE_MEDIUM: + case VL53L1_DISTANCEMODE_LONG: + data->distance_mode = distance_mode; + break; + default: + vl53l1_errmsg("invalid distance mode %d\n", + distance_mode); + rc = -EINVAL; + break; + } + } + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(distance_mode, "distance mode") + +/** + * sysfs attribute " distance mode" [rd/wr] + * + * set the distance mode value can only be used while: not ranging + * @li 1 @a VL53L1_DISTANCEMODE_SHORT + * @li 2 @a VL53L1_DISTANCEMODE_MEDIUM + * @li 3 @a VL53L1_DISTANCEMODE_LONG + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(distance_mode, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_distance_mode, + stmvl53l1_store_distance_mode); + +static int stmvl53l1_set_crosstalk_enable(struct stmvl53l1_data *data, + int crosstalk_enable) +{ + int rc = 0; + + if (data->enable_sensor) { + vl53l1_errmsg("can't change crosstalk enable while ranging\n"); + rc = -EBUSY; + } else if (crosstalk_enable == 0 || crosstalk_enable == 1) { + data->crosstalk_enable = crosstalk_enable; + } else { + vl53l1_errmsg("invalid crosstalk enable %d\n", + crosstalk_enable); + rc = -EINVAL; + } + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(crosstalk_enable, "crosstalk enable") + +/** + * sysfs attribute " crosstalk enable" [rd/wr] + * + * control if crosstalk compensation is eanble or not + * @li 0 disable crosstalk compensation + * @li 1 enable crosstalk compensation + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(crosstalk_enable, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_crosstalk_enable, + stmvl53l1_store_crosstalk_enable); + +static int stmvl53l1_set_output_mode(struct stmvl53l1_data *data, + int output_mode) +{ + int rc = 0; + + if (data->enable_sensor) { + vl53l1_errmsg("can't change output mode while ranging\n"); + rc = -EBUSY; + } else { + switch (output_mode) { + case VL53L1_OUTPUTMODE_NEAREST: + case VL53L1_OUTPUTMODE_STRONGEST: + data->output_mode = output_mode; + break; + default: + vl53l1_errmsg("invalid output mode %d\n", output_mode); + rc = -EINVAL; + break; + } + } + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(output_mode, "output mode") + +/** + * sysfs attribute " output mode" [rd/wr] + * + * set the output mode value can only be used while: not ranging + * @li 1 @a VL53L1_OUTPUTMODE_NEAREST + * @li 2 @a VL53L1_OUTPUTMODE_STRONGEST + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(output_mode, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_output_mode, + stmvl53l1_store_output_mode); + + +static int stmvl53l1_set_force_device_on_en(struct stmvl53l1_data *data, + int force_device_on_en) +{ + int rc; + + if (force_device_on_en != 0 && force_device_on_en != 1) { + vl53l1_errmsg("invalid force_device_on_en mode %d\n", + force_device_on_en); + return -EINVAL; + } + + data->force_device_on_en = force_device_on_en; + + /* don't update reset if sensor is enable */ + if (data->enable_sensor) + return 0; + + /* ok update reset according force_device_on_en value */ + if (force_device_on_en) + rc = reset_release(data); + else + rc = reset_hold(data); + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(force_device_on_en, "force device on enable") + +/** + * sysfs attribute " force_device_on_enable" [rd/wr] + * + * Control if device is put under reset when stopped. + * @li 0 feature is disable. Device is put under reset when stopped. + * @li 1 feature is enable. Device is not put under reset when stopped. + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(force_device_on_enable, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_force_device_on_en, + stmvl53l1_store_force_device_on_en); + +static int stmvl53l1_set_offset_correction_mode(struct stmvl53l1_data *data, + int offset_correction_mode) +{ + int rc = 0; + + if (data->enable_sensor) { + vl53l1_errmsg( + "can't change offset correction mode while ranging\n"); + rc = -EBUSY; + } else { + switch (offset_correction_mode) { + case VL53L1_OFFSETCORRECTIONMODE_STANDARD: + case VL53L1_OFFSETCORRECTIONMODE_PERZONE: + case VL53L1_OFFSETCORRECTIONMODE_PERVCSEL: + data->offset_correction_mode = offset_correction_mode; + break; + default: + vl53l1_errmsg("invalid offset correction mode %d\n", + offset_correction_mode); + rc = -EINVAL; + break; + } + } + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(offset_correction_mode, "offset correction mode") + +/** + * sysfs attribute " offset_correction_mode" [rd/wr] + * + * Control which offset correction is apply on result. can only be used + * while: not ranging + * @li 1 @a VL53L1_OFFSETCORRECTIONMODE_STANDARD + * @li 2 @a VL53L1_OFFSETCORRECTIONMODE_PERZONE + * @li 3 @a VL53L1_OFFSETCORRECTIONMODE_PERVCSEL + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(offset_correction_mode, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_offset_correction_mode, + stmvl53l1_store_offset_correction_mode); + +static ssize_t stmvl53l1_do_flush(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + + mutex_lock(&data->work_mutex); + + data->flush_todo_counter++; + if (data->enable_sensor == 0) + stmvl53l1_insert_flush_events_lock(data); + + mutex_unlock(&data->work_mutex); + + return count; +} + +static DEVICE_ATTR(do_flush, 0660/*S_IWUGO | S_IRUGO*/, + NULL, + stmvl53l1_do_flush); + +static ssize_t stmvl53l1_show_enable_debug(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", stmvl53l1_enable_debug); +} + +static ssize_t stmvl53l1_store_enable_debug(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int enable_debug; + int rc = 0; + + if (kstrtoint(buf, 0, &enable_debug)) { + vl53l1_errmsg("invalid syntax in %s", buf); + rc = -EINVAL; + } else + stmvl53l1_enable_debug = enable_debug; + + return rc ? rc : count; +} + +/** + * sysfs attribute " debug enable" [rd/wr] + * + * dynamic control of vl53l1_dbgmsg messages. Note that in any case your code + * must be enable with DEBUG in stmvl53l1.h at compile time. + * @li 0 disable vl53l1_dbgmsg messages + * @li 1 enable vl53l1_dbgmsg messages + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(enable_debug, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_enable_debug, + stmvl53l1_store_enable_debug); + +static ssize_t display_FixPoint1616(char *buf, size_t size, FixPoint1616_t fix) +{ + uint32_t msb = fix >> 16; + uint32_t lsb = fix & 0xffff; + + lsb = (lsb * 1000000ULL + 32768) / 65536; + + return scnprintf(buf, size, "%d.%06d", msb, (uint32_t) lsb); +} + +static ssize_t stmvl53l1_show_autonomous_config(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + ssize_t res = 0; + + res += scnprintf(&buf[res], PAGE_SIZE, "%d %d %d %d %d %d %d ", + data->auto_pollingTimeInMs, + data->auto_config.DetectionMode, + data->auto_config.IntrNoTarget, + data->auto_config.Distance.CrossMode, + data->auto_config.Distance.High, + data->auto_config.Distance.Low, + data->auto_config.Rate.CrossMode); + + res += display_FixPoint1616(&buf[res], PAGE_SIZE - res, + data->auto_config.Rate.High); + + res += scnprintf(&buf[res], PAGE_SIZE - res, " "); + + res += display_FixPoint1616(&buf[res], PAGE_SIZE - res, + data->auto_config.Rate.Low); + + res += scnprintf(&buf[res], PAGE_SIZE - res, "\n"); + + return res; +} + +static const char *parse_integer(const char *buf, int *res) +{ + int rc; + + while (*buf == ' ') + buf++; + rc = sscanf(buf, "%d ", res); + if (!rc) + return NULL; + + return strchr(buf, ' '); +} + +static bool is_float_format(const char *buf, bool is_last) +{ + char *dot = strchr(buf, '.'); + char *space_or_eos = strchr(buf, is_last ? '\0' : ' '); + + if (!space_or_eos) + return !!dot; + if (!dot) + return false; + + return dot < space_or_eos ? true : false; +} + +static int parse_FixPoint16x16_lsb(const char *lsb_char) +{ + int lsb = 0; + int digit_nb = 0; + + /* parse at most 6 digits */ + lsb_char++; + while (isdigit(*lsb_char) && digit_nb < 6) { + lsb = lsb * 10 + (*lsb_char - '0'); + lsb_char++; + digit_nb++; + } + while (digit_nb++ < 6) + lsb = lsb * 10; + + return div64_s64(lsb * 65536ULL + 500000, 1000000); +} + +/* parse next fix point value and return a pointer to next blank or newline + * character according to is_last parameter. + * parse string must have digit for integer part (something like '.125' will + * return an error) or an error will be return. Only the first 6 digit of the + * decimal part will be parsed. + */ +static const char *parse_FixPoint16x16(const char *buf, FixPoint1616_t *res, + bool is_last) +{ + bool is_float; + int msb; + int lsb = 0; + int rc; + + while (*buf == ' ') + buf++; + is_float = is_float_format(buf, is_last); + + /* scan msb */ + rc = sscanf(buf, "%d ", &msb); + if (!rc) + return NULL; + /* then lsb if present */ + if (is_float) + lsb = parse_FixPoint16x16_lsb(strchr(buf, '.')); + *res = (msb << 16) + lsb; + + return strchr(buf, is_last ? '\0' : ' '); +} + +static ssize_t stmvl53l1_store_autonomous_config(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int pollingTimeInMs, DetectionMode, IntrNoTarget; + int d_CrossMode, d_High, d_Low; + int r_CrossMode; + FixPoint1616_t r_High, r_Low; + const char *buf_ori = buf; + int rc; + + mutex_lock(&data->work_mutex); + + if (data->enable_sensor) + goto busy; + + buf = parse_integer(buf, &pollingTimeInMs); + if (!buf) + goto invalid; + buf = parse_integer(buf, &DetectionMode); + if (!buf) + goto invalid; + buf = parse_integer(buf, &IntrNoTarget); + if (!buf) + goto invalid; + buf = parse_integer(buf, &d_CrossMode); + if (!buf) + goto invalid; + buf = parse_integer(buf, &d_High); + if (!buf) + goto invalid; + buf = parse_integer(buf, &d_Low); + if (!buf) + goto invalid; + buf = parse_integer(buf, &r_CrossMode); + if (!buf) + goto invalid; + buf = parse_FixPoint16x16(buf, &r_High, false); + if (!buf) + goto invalid; + buf = parse_FixPoint16x16(buf, &r_Low, true); + if (!buf) + goto invalid; + + data->auto_pollingTimeInMs = pollingTimeInMs; + data->auto_config.DetectionMode = DetectionMode; + data->auto_config.IntrNoTarget = IntrNoTarget; + data->auto_config.Distance.CrossMode = d_CrossMode; + data->auto_config.Distance.High = d_High; + data->auto_config.Distance.Low = d_Low; + data->auto_config.Rate.CrossMode = r_CrossMode; + data->auto_config.Rate.High = r_High; + data->auto_config.Rate.Low = r_Low; + + mutex_unlock(&data->work_mutex); + + return count; + +busy: + vl53l1_errmsg("can't change config while ranging"); + rc = -EBUSY; + goto error; + +invalid: + vl53l1_errmsg("invalid syntax in %s", buf_ori); + rc = -EINVAL; + goto error; + +error: + mutex_unlock(&data->work_mutex); + + return rc; +} + +/** + * sysfs attribute " autonomous_config" [rd/wr] + * + * Will set/get autonomous configuration using sysfs. + * + * format is the following : + * + * + * + *@code + * > echo "1000 1 0 0 1000 300 0 2.2 1.001" > autonomous_config + *@endcode + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(autonomous_config, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_autonomous_config, + stmvl53l1_store_autonomous_config); + +static ssize_t stmvl53l1_show_last_error_config(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", data->last_error); +} + +/** + * sysfs attribute " last_error" [rd] + * + * Will get last internal error using sysfs. + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(last_error, 0440/*S_IRUGO*/, + stmvl53l1_show_last_error_config, + NULL); + +static ssize_t stmvl53l1_show_optical_center_config(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + ssize_t res = 0; + + res += display_FixPoint1616(&buf[res], PAGE_SIZE - res, + data->optical_offset_x); + res += scnprintf(&buf[res], PAGE_SIZE - res, " "); + res += display_FixPoint1616(&buf[res], PAGE_SIZE - res, + data->optical_offset_y); + + res += scnprintf(&buf[res], PAGE_SIZE - res, "\n"); + + return res; +} + +/** + * sysfs attribute " optical_center" [rd] + * + * Will get optical_center using sysfs. + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(optical_center, 0440/*S_IRUGO*/, + stmvl53l1_show_optical_center_config, + NULL); + +static int stmvl53l1_set_dmax_reflectance(struct stmvl53l1_data *data, + int dmax_reflectance) +{ + int rc = 0; + + if (data->enable_sensor) { + vl53l1_errmsg( + "can't change dmax reflectance while ranging\n"); + rc = -EBUSY; + } else + data->dmax_reflectance = dmax_reflectance; + + return rc; +} + +static ssize_t stmvl53l1_show_dmax_reflectance(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + ssize_t res = 0; + + res += display_FixPoint1616(&buf[res], PAGE_SIZE - res, + data->dmax_reflectance); + + res += scnprintf(&buf[res], PAGE_SIZE - res, "\n"); + + return res; +} + +static ssize_t stmvl53l1_store_dmax_reflectance(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + FixPoint1616_t dmax_reflectance; + const char *buf_ori = buf; + int rc; + + mutex_lock(&data->work_mutex); + + buf = parse_FixPoint16x16(buf, &dmax_reflectance, true); + if (!buf) + goto invalid; + rc = stmvl53l1_set_dmax_reflectance(data, dmax_reflectance); + if (rc) + goto error; + + mutex_unlock(&data->work_mutex); + + return count; + +invalid: + vl53l1_errmsg("invalid syntax in %s", buf_ori); + rc = -EINVAL; + goto error; + +error: + mutex_unlock(&data->work_mutex); + + return rc; +} + +/** + * sysfs attribute "dmax_reflectance" [rd/wr] + * + * target reflectance use for calculate the ambient DMAX. can only be used + * while: not ranging + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(dmax_reflectance, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_dmax_reflectance, + stmvl53l1_store_dmax_reflectance); + +static int stmvl53l1_set_dmax_mode(struct stmvl53l1_data *data, + int dmax_mode) +{ + int rc = 0; + + if (data->enable_sensor) { + vl53l1_errmsg("can't change dmax mode while ranging\n"); + rc = -EBUSY; + } else { + switch (dmax_mode) { + case VL53L1_DMAXMODE_FMT_CAL_DATA: + case VL53L1_DMAXMODE_CUSTCAL_DATA: + case VL53L1_DMAXMODE_PER_ZONE_CAL_DATA: + data->dmax_mode = dmax_mode; + break; + default: + vl53l1_errmsg("invalid dmax mode %d\n", dmax_mode); + rc = -EINVAL; + break; + } + } + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(dmax_mode, "dmax mode") + +/** + * sysfs attribute " dmax mode" [rd/wr] + * + * set the dmax mode value can only be used while: not ranging + * @li 1 @a VL53L1_DMAXMODE_FMT_CAL_DATA + * @li 2 @a VL53L1_DMAXMODE_CUSTCAL_DATA + * @li 3 @a VL53L1_DMAXMODE_PER_ZONE_CAL_DATA + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(dmax_mode, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_dmax_mode, + stmvl53l1_store_dmax_mode); + +static int stmvl53l1_set_tuning(struct stmvl53l1_data *data, int key, + int value) +{ + int rc; + + if (data->enable_sensor) { + vl53l1_errmsg("can't change tuning params while ranging\n"); + return -EBUSY; + } + + if (data->is_calibrating) { + vl53l1_errmsg("can't change tuning params while calibrating\n"); + return -EBUSY; + } + + if (key & ~0xffff) + return -EINVAL; + + vl53l1_dbgmsg("trying to set %d with key %d", value, key); + + rc = VL53L1_SetTuningParameter(&data->stdev, key, value); + if (rc) + rc = store_last_error(data, rc); + + return rc; +} + +static ssize_t stmvl53l1_store_tuning(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int key; + int value; + int n; + int rc; + + mutex_lock(&data->work_mutex); + + n = sscanf(buf, "%d %d", &key, &value); + if (n != 2) { + rc = -EINVAL; + goto error; + } + rc = stmvl53l1_set_tuning(data, key, value); + if (rc) + goto error; + + mutex_unlock(&data->work_mutex); + + return count; + +error: + mutex_unlock(&data->work_mutex); + + return rc; +} + +/** + * sysfs attribute "tuning" [wr] + * + * write a tuning parameter. Two integer parameters are given. First one + * is a key that specify which tuning parameter is update. Other one is the + * value which is write. + * + * writing a tuning parameter is only allowed before the first start. + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(tuning, 0220/*S_IWUGO */, + NULL, + stmvl53l1_store_tuning); + +static int stmvl53l1_display_tuning_key(struct stmvl53l1_data *data, char *buf, + int *pos, int key) +{ + int rc = 0; + int value = 0; + int sz; + + rc = VL53L1_GetTuningParameter(&data->stdev, key, &value); + if (rc) + return 0; + + sz = snprintf(&buf[*pos], PAGE_SIZE - *pos, "%d %d\n", key, value); + if (sz >= PAGE_SIZE - *pos) + return -ENOSPC; /* FIXME : another better error ? */ + + *pos += sz; + + return 0; +} + +static ssize_t stmvl53l1_show_tuning_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const int max_tuning_key = 65535; + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int rc; + int i; + int pos = 0; + + mutex_lock(&data->work_mutex); + + for (i = 0; i < max_tuning_key; ++i) { + rc = stmvl53l1_display_tuning_key(data, buf, &pos, i); + if (rc) + break; + } + + mutex_unlock(&data->work_mutex); + + return rc ? rc : pos; +} + +/** + * sysfs attribute "tuning_status" [rd] + * + * write a tuning parameter. Two integer parameters are given. First one + * is a key that specify which tuning parameter is update. Other one is the + * value which is write. + * + * writing a tuning parameter is only allowed before the first start. + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(tuning_status, 0440/*S_IRUGO */, + stmvl53l1_show_tuning_status, + NULL); + +static int stmvl53l1_set_smudge_correction_mode(struct stmvl53l1_data *data, + int smudge_correction_mode) +{ + int rc = 0; + + if (data->enable_sensor) { + vl53l1_errmsg("can't change smudge corr mode while ranging\n"); + rc = -EBUSY; + } else { + switch (smudge_correction_mode) { + case VL53L1_SMUDGE_CORRECTION_NONE: + case VL53L1_SMUDGE_CORRECTION_CONTINUOUS: + case VL53L1_SMUDGE_CORRECTION_SINGLE: + case VL53L1_SMUDGE_CORRECTION_DEBUG: + data->smudge_correction_mode = smudge_correction_mode; + break; + default: + vl53l1_errmsg("invalid smudge correction mode %d\n", + smudge_correction_mode); + rc = -EINVAL; + break; + } + } + + return rc; +} + +IMPLEMENT_PARAMETER_INTEGER(smudge_correction_mode, "smudge correction mode") + +/** + * sysfs attribute " smudge_correction_mode" [rd/wr] + * + * This parameter will control if smudge correction is enable and how crosstalk + * values are updated. + * @li 0 @a VL53L1_SMUDGE_CORRECTION_NONE + * @li 1 @a VL53L1_SMUDGE_CORRECTION_CONTINUOUS + * @li 2 @a VL53L1_SMUDGE_CORRECTION_SINGLE + * @li 3 @a VL53L1_SMUDGE_CORRECTION_DEBUG + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(smudge_correction_mode, 0660/*S_IWUGO | S_IRUGO*/, + stmvl53l1_show_smudge_correction_mode, + stmvl53l1_store_smudge_correction_mode); + +static ssize_t stmvl53l1_show_is_xtalk_value_changed_config(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int param; + + mutex_lock(&data->work_mutex); + param = data->is_xtalk_value_changed; + mutex_unlock(&data->work_mutex); + + return scnprintf(buf, PAGE_SIZE, "%d\n", param); +} + +/** + * sysfs attribute " optical_center" [rd] + * + * Will get optical_center using sysfs. + * + * @ingroup sysfs_attrib + */ +static DEVICE_ATTR(is_xtalk_value_changed, 0440/*S_IRUGO*/, + stmvl53l1_show_is_xtalk_value_changed_config, + NULL); + +static struct attribute *stmvl53l1_attributes[] = { + &dev_attr_enable_ps_sensor.attr, + &dev_attr_set_delay_ms.attr, + &dev_attr_timing_budget.attr, + &dev_attr_roi.attr, + &dev_attr_mode.attr, + &dev_attr_do_flush.attr, + &dev_attr_distance_mode.attr, + &dev_attr_crosstalk_enable.attr, + &dev_attr_enable_debug.attr, + &dev_attr_output_mode.attr, + &dev_attr_force_device_on_enable.attr, + &dev_attr_autonomous_config.attr, + &dev_attr_last_error.attr, + &dev_attr_offset_correction_mode.attr, + &dev_attr_optical_center.attr, + &dev_attr_dmax_reflectance.attr, + &dev_attr_dmax_mode.attr, + &dev_attr_tuning.attr, + &dev_attr_tuning_status.attr, + &dev_attr_smudge_correction_mode.attr, + &dev_attr_is_xtalk_value_changed.attr, + &dev_attr_product_type.attr, + NULL +}; + +static const struct attribute_group stmvl53l1_attr_group = { + .attrs = stmvl53l1_attributes, +}; + +static ssize_t stmvl53l1_calib_data_read(struct file *filp, + struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct stmvl53l1_data *data = dev_get_drvdata(dev); + VL53L1_CalibrationData_t calib; + int rc; + void *src = (void *) &calib; + + mutex_lock(&data->work_mutex); + + vl53l1_dbgmsg("off = %llu / count = %lu", off, count); + + /* sanity check */ + if (off < 0 || off > sizeof(VL53L1_CalibrationData_t)) + goto invalid; + + /* got current calibration data */ + memset(&calib, 0, sizeof(calib)); + rc = VL53L1_GetCalibrationData(&data->stdev, &calib); + if (rc) { + vl53l1_errmsg("VL53L1_GetCalibrationData fail %d", rc); + rc = store_last_error(data, rc); + goto error; + } + + /* copy to buffer */ + if (off + count > sizeof(VL53L1_CalibrationData_t)) + count = sizeof(VL53L1_CalibrationData_t) - off; + memcpy(buf, src + off, count); + + mutex_unlock(&data->work_mutex); + + return count; + +invalid: + vl53l1_errmsg("invalid syntax"); + rc = -EINVAL; + goto error; + +error: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static ssize_t stmvl53l1_calib_data_write(struct file *filp, + struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int rc; + + mutex_lock(&data->work_mutex); + + vl53l1_dbgmsg("off = %llu / count = %lu", off, count); + + if (data->enable_sensor) { + rc = -EBUSY; + vl53l1_errmsg("can't set calib data while ranging\n"); + goto error; + } + + /* we only support one time write */ + if (off != 0 || count != sizeof(VL53L1_CalibrationData_t)) + goto invalid; + + rc = VL53L1_SetCalibrationData(&data->stdev, + (VL53L1_CalibrationData_t *) buf); + if (rc) { + vl53l1_errmsg("VL53L1_SetCalibrationData fail %d", rc); + rc = store_last_error(data, rc); + goto error; + } + + mutex_unlock(&data->work_mutex); + + return count; + +invalid: + vl53l1_errmsg("invalid syntax"); + rc = -EINVAL; + goto error; + +error: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static struct bin_attribute stmvl53l1_calib_data_attr = { + .attr = { + .name = "calibration_data", + .mode = 0660/*S_IWUGO | S_IRUGO*/, + }, + .size = sizeof(VL53L1_CalibrationData_t), + .read = stmvl53l1_calib_data_read, + .write = stmvl53l1_calib_data_write, +}; + +static ssize_t stmvl53l1_zone_calib_data_read(struct file *filp, + struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int rc; + void *src = (void *) &data->calib.data; + + mutex_lock(&data->work_mutex); + + vl53l1_dbgmsg("off = %llu / count = %lu", off, count); + + /* sanity check */ + if (off < 0 || off > sizeof(stmvl531_zone_calibration_data_t)) + goto invalid; + + /* got current zone calibration data */ + rc = VL53L1_GetZoneCalibrationData(&data->stdev, + &data->calib.data.data); + if (rc) { + vl53l1_errmsg("VL53L1_GetZoneCalibrationData fail %d", rc); + rc = store_last_error(data, rc); + goto error; + } + data->calib.data.id = data->current_roi_id; + + /* copy to buffer */ + if (off + count > sizeof(stmvl531_zone_calibration_data_t)) + count = sizeof(stmvl531_zone_calibration_data_t) - off; + memcpy(buf, src + off, count); + + mutex_unlock(&data->work_mutex); + + return count; + +invalid: + vl53l1_errmsg("invalid syntax"); + rc = -EINVAL; + goto error; + +error: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static ssize_t stmvl53l1_zone_calib_data_write(struct file *filp, + struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct stmvl53l1_data *data = dev_get_drvdata(dev); + int rc; + void *dst = &data->calib.data; + + mutex_lock(&data->work_mutex); + + vl53l1_dbgmsg("off = %llu / count = %lu", off, count); + + /* implementation if quite fragile. We suppose successive access. We + * trigger set on last byte write if amount is exact. + */ + if (off < 0 || off > sizeof(stmvl531_zone_calibration_data_t)) + goto invalid; + if (off + count > sizeof(stmvl531_zone_calibration_data_t)) + goto invalid; + + memcpy(dst + off, buf, count); + if (off + count == sizeof(stmvl531_zone_calibration_data_t)) { + vl53l1_dbgmsg("trigger zone calib setting"); + rc = VL53L1_SetZoneCalibrationData(&data->stdev, + &data->calib.data.data); + if (rc) { + vl53l1_errmsg("VL53L1_SetZoneCalibrationData fail %d", + rc); + rc = store_last_error(data, rc); + goto error; + } + data->current_roi_id = data->calib.data.id; + } + + mutex_unlock(&data->work_mutex); + + return count; + +invalid: + vl53l1_errmsg("invalid syntax"); + rc = -EINVAL; + goto error; + +error: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static struct bin_attribute stmvl53l1_zone_calib_data_attr = { + .attr = { + .name = "zone_calibration_data", + .mode = 0660/*S_IWUGO | S_IRUGO*/, + }, + .size = sizeof(stmvl531_zone_calibration_data_t), + .read = stmvl53l1_zone_calib_data_read, + .write = stmvl53l1_zone_calib_data_write, +}; + +static int ctrl_reg_access(struct stmvl53l1_data *data, void *p) +{ + struct stmvl53l1_register reg; + size_t total_byte; + int rc; + + if (data->is_device_remove) + return -ENODEV; + + total_byte = offsetof(struct stmvl53l1_register, data.b); + if (copy_from_user(®, p, total_byte)) { + vl53l1_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + + if (reg.cnt > STMVL53L1_MAX_CCI_XFER_SZ) { + vl53l1_errmsg("reg len %d > size limit\n", reg.cnt); + return -EINVAL; + } + + total_byte = offsetof(struct stmvl53l1_register, data.bytes[reg.cnt]); + /* for write get the effective data part of the structure */ + if (!reg.is_read) { + if (copy_from_user(®, p, total_byte)) { + vl53l1_errmsg(" data cpy fail\n"); + return -EFAULT; + } + } + + /* put back to user only needed amount of data */ + if (!reg.is_read) { + rc = VL53L1_WriteMulti(&data->stdev, (uint16_t)reg.index, + reg.data.bytes, reg.cnt); + reg.status = rc; + /* for write only write back status no data */ + total_byte = offsetof(struct stmvl53l1_register, data.b); + vl53l1_dbgmsg("wr %x %d bytes statu %d\n", + reg.index, reg.cnt, rc); + if (rc) + rc = store_last_error(data, rc); + } else { + rc = VL53L1_ReadMulti(&data->stdev, (uint16_t)reg.index, + reg.data.bytes, reg.cnt); + reg.status = rc; + vl53l1_dbgmsg("rd %x %d bytes status %d\n", + reg.index, reg.cnt, rc); + /* if fail do not copy back data only status */ + if (rc) { + total_byte = offsetof(struct stmvl53l1_register, + data.b); + rc = store_last_error(data, rc); + } + /* else the total byte is already the full pay-load with data */ + } + + if (copy_to_user(p, ®, total_byte)) { + vl53l1_errmsg("%d, fail\n", __LINE__); + return -EFAULT; + } + return rc; +} + + +/* + * + */ +static int ctrl_start(struct stmvl53l1_data *data) +{ + int rc; + + mutex_lock(&data->work_mutex); + + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + + vl53l1_dbgmsg(" state = %d\n", data->enable_sensor); + + /* turn on tof sensor only if it's not already started */ + if (data->enable_sensor == 0 && !data->is_calibrating) { + /* to start */ + rc = stmvl53l1_start(data); + } else{ + rc = -EBUSY; + } + vl53l1_dbgmsg(" final state = %d\n", data->enable_sensor); + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +/** + * no lock version of ctrl_stop (mutex shall be held) + * + * @warning exist only for use in device exit to ensure "locked and started" + * may also beuse in some erro handling when mutex is already locked + * @return 0 on success and was running >0 if already off <0 on error + */ +static int _ctrl_stop(struct stmvl53l1_data *data) +{ + int rc; + + vl53l1_dbgmsg("enter state = %d\n", data->enable_sensor); + /* be sure waiters are woken */ + data->is_data_valid = true; + /* turn on tof sensor only if it's not enabled by other client */ + if (data->enable_sensor == 1) { + /* to stop */ + rc = stmvl53l1_stop(data); + } else { + vl53l1_dbgmsg("already off did nothing\n"); + rc = 0; + } + stmvl53l1_insert_flush_events_lock(data); + vl53l1_dbgmsg(" final state = %d\n", data->enable_sensor); + + return rc; +} + +/** + * get work lock and stop sensor + * + * see @ref _ctrl_stop + * + * @param data device + * @return 0 on success EBUSY if arleady off + */ +static int ctrl_stop(struct stmvl53l1_data *data) +{ + int rc; + + mutex_lock(&data->work_mutex); + + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + if (data->enable_sensor) + rc = _ctrl_stop(data); + else + rc = -EBUSY; + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static int ctrl_getdata(struct stmvl53l1_data *data, void __user *p) +{ + int rc; + + mutex_lock(&data->work_mutex); + + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + rc = copy_to_user(p, + &data->meas.single_range_data, + sizeof(stmvl531_range_data_t)); + if (rc) { + vl53l1_dbgmsg("copy to user fail %d", rc); + rc = -EFAULT; + } + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static bool is_new_data_for_me(struct stmvl53l1_data *data, pid_t pid, + struct list_head *head) +{ + return data->is_data_valid && !is_pid_in_list(pid, head); +} + +static bool sleep_for_data_condition(struct stmvl53l1_data *data, pid_t pid, + struct list_head *head) +{ + bool res; + + mutex_lock(&data->work_mutex); + res = is_new_data_for_me(data, pid, head); + mutex_unlock(&data->work_mutex); + + return res; +} + +static int sleep_for_data(struct stmvl53l1_data *data, pid_t pid, + struct list_head *head) +{ + int rc; + + mutex_unlock(&data->work_mutex); + rc = wait_event_killable(data->waiter_for_data, + sleep_for_data_condition(data, pid, head)); + mutex_lock(&data->work_mutex); + + return data->enable_sensor ? rc : -ENODEV; +} + +static int sleep_for_data_timeout(struct stmvl53l1_data *data, pid_t pid, + struct list_head *head) +{ + int rc; + + mutex_unlock(&data->work_mutex); +#ifdef PHIO_TIMEOUT_FIX + if(!data->not_first_frame) + rc = wait_event_interruptible_timeout(data->waiter_for_data, + sleep_for_data_condition(data, pid, head), + usecs_to_jiffies(4 * data->timing_budget)); + else + rc = wait_event_interruptible_timeout(data->waiter_for_data, + sleep_for_data_condition(data, pid, head), + usecs_to_jiffies(2 * data->timing_budget)); +#else + rc = wait_event_interruptible_timeout(data->waiter_for_data, + sleep_for_data_condition(data, pid, head), + usecs_to_jiffies(2 * data->timing_budget)); +#endif + if (rc == 0) //condition evaluated to false after timeout elapsed + rc = -EAGAIN; + else + rc = 0; + mutex_lock(&data->work_mutex); + + return data->enable_sensor ? rc : -ENODEV; +} + +static int ctrl_getdata_blocking(struct stmvl53l1_data *data, void __user *p) +{ + int rc = 0; + pid_t pid = current->pid; + + mutex_lock(&data->work_mutex); + /* If device not ranging then exit on error */ + if (data->is_device_remove || !data->enable_sensor) { + rc = -ENODEV; + goto done; + } + /* sleep if data already read */ + if (!is_new_data_for_me(data, pid, &data->simple_data_reader_list)) + rc = sleep_for_data(data, pid, &data->simple_data_reader_list); + if (rc) + goto done; + + /* unless we got interrupted we return data to user and note read */ + rc = copy_to_user(p, &data->meas.single_range_data, + sizeof(stmvl531_range_data_t)); + if (rc) + goto done; + rc = add_reader(pid, &data->simple_data_reader_list); + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static int ctrl_mz_data_common(struct stmvl53l1_data *data, void __user *p, + bool is_additional) +{ + struct stmvl53l1_data_with_additional __user *d = p; + int rc; + + mutex_lock(&data->work_mutex); + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + rc = copy_to_user(&d->data, &data->meas.multi_range_data, + sizeof(VL53L1_MultiRangingData_t)); + if (rc) { + vl53l1_dbgmsg("copy to user fail %d", rc); + rc = -EFAULT; + goto done; + } + if (is_additional) { + rc = copy_to_user(&d->additional_data, + &data->meas.additional_data, + sizeof(VL53L1_AdditionalData_t)); + if (rc) { + vl53l1_dbgmsg("copy to user fail %d", rc); + rc = -EFAULT; + goto done; + } + } + if (!data->enable_sensor) + rc = -ENODEV; + else if (!is_mz_mode(data)) + rc = -ENOEXEC; + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static int ctrl_mz_data_blocking_common(struct stmvl53l1_data *data, + void __user *p, bool is_additional) +{ + int rc = 0; + int rc0; + struct stmvl53l1_data_with_additional __user *d = p; + pid_t pid = current->pid; + + mutex_lock(&data->work_mutex); + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + /* mode 2 or 3 */ + if (!is_mz_mode(data)) { + rc = -ENOEXEC; + goto done; + } + /* If device not ranging then exit on error */ + if (!data->enable_sensor) { + rc = -ENODEV; + goto done; + } + /* sleep if data already read */ + if (!is_new_data_for_me(data, pid, &data->mz_data_reader_list)) + rc = sleep_for_data_timeout(data, pid, &data->mz_data_reader_list); + if (rc) { + kill_mz_data(&data->meas.multi_range_data); + rc0 = copy_to_user(&d->data, &data->meas.multi_range_data, + sizeof(VL53L1_MultiRangingData_t)); + goto done; + } +#ifdef PHIO_TIMEOUT_FIX + if (data->meas.multi_range_data.StreamCount == 0) + data->not_first_frame = true; +#endif + /* unless we got interrupted we return data to user and note read */ + rc = copy_to_user(&d->data, &data->meas.multi_range_data, + sizeof(VL53L1_MultiRangingData_t)); + if (rc) + goto done; + if (is_additional) { + rc = copy_to_user(&d->additional_data, + &data->meas.additional_data, + sizeof(VL53L1_AdditionalData_t)); + if (rc) + goto done; + } + rc = add_reader(pid, &data->mz_data_reader_list); + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +/** + * Get multi zone data + * @param data + * @param p [out] user ptr to @ref VL53L1_MultiRangingData_t structure\n + * is always set but on EFAULT error + * + * @return + * @li 0 on success + * @li ENODEV if not ranging + * @li ENOEXEC not in multi zone mode + * @li EFAULT copy to user error + */ +static int ctrl_mz_data(struct stmvl53l1_data *data, void __user *p) +{ + return ctrl_mz_data_common(data, p, false); +} + +static int ctrl_mz_data_blocking(struct stmvl53l1_data *data, void __user *p) +{ + return ctrl_mz_data_blocking_common(data, p, false); +} + +/** + * Get multi zone data with histogram debug data + * @param data + * @param p [out] user ptr to @ref struct stmvl53l1_data_with_additional + * structure is always set but on EFAULT error + * + * @return + * @li 0 on success + * @li ENODEV if not ranging + * @li ENOEXEC not in multi zone mode + * @li EFAULT copy to user error + */ +static int ctrl_mz_data_additional(struct stmvl53l1_data *data, void __user *p) +{ + return ctrl_mz_data_common(data, p, true); +} + +static int ctrl_mz_data_blocking_additional(struct stmvl53l1_data *data, + void __user *p) +{ + return ctrl_mz_data_blocking_common(data, p, true); +} + +static int ctrl_param_last_error(struct stmvl53l1_data *data, + struct stmvl53l1_parameter *param) +{ + int rc; + + if (param->is_read) { + param->value = data->last_error; + param->status = 0; + vl53l1_dbgmsg("get last error %d", param->value); + rc = 0; + } else { + rc = -EINVAL; + } + + return rc; +} + +static int ctrl_param_optical_center(struct stmvl53l1_data *data, + struct stmvl53l1_parameter *param) +{ + if (!param->is_read) + return -EINVAL; + + param->value = data->optical_offset_x; + param->value2 = data->optical_offset_y; + + return 0; +} + +static int ctrl_param_dmax_reflectance(struct stmvl53l1_data *data, + struct stmvl53l1_parameter *param) +{ + int rc; + + if (param->is_read) { + param->value = data->dmax_reflectance; + param->status = 0; + vl53l1_dbgmsg("get dmax reflectance %d", param->value); + rc = 0; + } else { + rc = stmvl53l1_set_dmax_reflectance(data, param->value); + vl53l1_dbgmsg("rc %d req %d now %d", rc, + param->value, data->dmax_reflectance); + } + + return rc; +} + +static int ctrl_param_tuning(struct stmvl53l1_data *data, + struct stmvl53l1_parameter *param) +{ + if (param->is_read) + return -EINVAL; + + return stmvl53l1_set_tuning(data, param->value, param->value2); +} + +static int ctrl_param_is_xtalk_value_changed(struct stmvl53l1_data *data, + struct stmvl53l1_parameter *param) +{ + if (!param->is_read) + return -EINVAL; + + param->value = data->is_xtalk_value_changed; + + return 0; +} + +/** + * handle ioctl set param mode + * + * @param data + * @param p + * @return 0 on success + */ +static int ctrl_params(struct stmvl53l1_data *data, void __user *p) +{ + int rc, rc2; + struct stmvl53l1_parameter param; + + mutex_lock(&data->work_mutex); + + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + rc = copy_from_user(¶m, p, sizeof(param)); + param.status = 0; + if (rc) { + rc = -EFAULT; + goto done; /* no need for status in user struct */ + } + switch (param.name) { + case VL53L1_POLLDELAY_PAR: + rc = ctrl_param_poll_delay_ms(data, ¶m); + break; + case VL53L1_TIMINGBUDGET_PAR: + rc = ctrl_param_timing_budget(data, ¶m); + break; + case VL53L1_DEVICEMODE_PAR: + rc = ctrl_param_preset_mode(data, ¶m); + break; + case VL53L1_DISTANCEMODE_PAR: + rc = ctrl_param_distance_mode(data, ¶m); + break; + case VL53L1_XTALKENABLE_PAR: + rc = ctrl_param_crosstalk_enable(data, ¶m); + break; + case VL53L1_OUTPUTMODE_PAR: + rc = ctrl_param_output_mode(data, ¶m); + break; + case VL53L1_FORCEDEVICEONEN_PAR: + rc = ctrl_param_force_device_on_en(data, ¶m); + break; + case VL53L1_LASTERROR_PAR: + rc = ctrl_param_last_error(data, ¶m); + break; + case VL53L1_OFFSETCORRECTIONMODE_PAR: + rc = ctrl_param_offset_correction_mode(data, ¶m); + break; + case VL53L1_OPTICALCENTER_PAR: + rc = ctrl_param_optical_center(data, ¶m); + break; + case VL53L1_DMAXREFLECTANCE_PAR: + rc = ctrl_param_dmax_reflectance(data, ¶m); + break; + case VL53L1_DMAXMODE_PAR: + rc = ctrl_param_dmax_mode(data, ¶m); + break; + case VL53L1_TUNING_PAR: + rc = ctrl_param_tuning(data, ¶m); + break; + case VL53L1_SMUDGECORRECTIONMODE_PAR: + rc = ctrl_param_smudge_correction_mode(data, ¶m); + break; + case VL53L1_ISXTALKVALUECHANGED_PAR: + rc = ctrl_param_is_xtalk_value_changed(data, ¶m); + break; + default: + vl53l1_errmsg("unknown or unsupported %d\n", param.name); + rc = -EINVAL; + } + /* copy back (status at least ) to user */ + if (param.is_read && rc == 0) { + rc2 = copy_to_user(p, ¶m, sizeof(param)); + if (rc2) { + rc = -EFAULT; /* kill prev status if that fail */ + vl53l1_errmsg("copy to user fail %d\n", rc); + } + } +done: + mutex_unlock(&data->work_mutex); + return rc; +} + +/** + * implement set/get roi ioctl + * @param data device + * @param p user space ioctl arg ptr + * @return 0 on success <0 errno code + * @li -EINVAL invalid number of roi + * @li -EBUSY when trying to set roi while ranging + * @li -EFAULT if cpy to/fm user fail for requested number of roi + */ +static int ctrl_roi(struct stmvl53l1_data *data, void __user *p) +{ + int rc; + int roi_cnt; + struct stmvl53l1_roi_full_t rois; + + mutex_lock(&data->work_mutex); + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + /* copy fixed part of args at first */ + rc = copy_from_user(&rois, p, + offsetof(struct stmvl53l1_roi_full_t, + roi_cfg.UserRois[0])); + if (rc) { + rc = -EFAULT; + goto done; + } + /* check no of roi limit is ok */ + roi_cnt = rois.roi_cfg.NumberOfRoi; + if (roi_cnt > VL53L1_MAX_USER_ZONES) { + vl53l1_errmsg("invalid roi spec cnt=%d > %d", + rois.roi_cfg.NumberOfRoi, + VL53L1_MAX_USER_ZONES); + rc = -EINVAL; + goto done; + } + + if (rois.is_read) { + int cpy_size; + + roi_cnt = MIN(rois.roi_cfg.NumberOfRoi, + data->roi_cfg.NumberOfRoi); + cpy_size = offsetof(VL53L1_RoiConfig_t, UserRois[roi_cnt]); + /* copy from local to user only effective part requested */ + rc = copy_to_user(&((struct stmvl53l1_roi_full_t *)p)->roi_cfg, + &data->roi_cfg, cpy_size); + vl53l1_dbgmsg("return %d of %d\n", roi_cnt, + data->roi_cfg.NumberOfRoi); + } else { + /* SET check cnt roi is ok */ + if (data->enable_sensor) { + rc = -EBUSY; + vl53l1_errmsg("can't set roi while ranging\n"); + goto done; + } + /* get full data that required from user */ + rc = copy_from_user(&rois, p, + offsetof(struct stmvl53l1_roi_full_t, + roi_cfg.UserRois[roi_cnt])); + if (rc) { + vl53l1_errmsg("get %d roi fm user fail", roi_cnt); + rc = -EFAULT; + goto done; + } + dump_roi(data->roi_cfg.UserRois, data->roi_cfg.NumberOfRoi); + /* we may ask ll driver to check but check is mode dependent + * and so we could get erroneous error back + */ + memcpy(&data->roi_cfg, &rois.roi_cfg, sizeof(data->roi_cfg)); + } +done: + mutex_unlock(&data->work_mutex); + return rc; +} + +static int ctrl_autonomous_config(struct stmvl53l1_data *data, void __user *p) +{ + int rc = 0; + struct stmvl53l1_autonomous_config_t full; + + mutex_lock(&data->work_mutex); + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + /* first copy all data */ + rc = copy_from_user(&full, p, sizeof(full)); + if (rc) { + rc = -EFAULT; + goto done; + } + + if (full.is_read) { + full.pollingTimeInMs = data->auto_pollingTimeInMs; + full.config = data->auto_config; + rc = copy_to_user(p, &full, sizeof(full)); + if (rc) + rc = -EFAULT; + } else { + if (data->enable_sensor) { + rc = -EBUSY; + vl53l1_errmsg("can't change config while ranging\n"); + goto done; + } + data->auto_pollingTimeInMs = full.pollingTimeInMs; + data->auto_config = full.config; + } + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static int ctrl_calibration_data(struct stmvl53l1_data *data, void __user *p) +{ + int rc; + struct stmvl53l1_ioctl_calibration_data_t calib; + int data_offset = offsetof(struct stmvl53l1_ioctl_calibration_data_t, + data); + + mutex_lock(&data->work_mutex); + + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + rc = copy_from_user(&calib, p, data_offset); + if (rc) { + vl53l1_errmsg("fail to detect read or write %d", rc); + rc = -EFAULT; + goto done; + } + + if (calib.is_read) { + memset(&calib.data, 0, sizeof(calib.data)); + rc = VL53L1_GetCalibrationData(&data->stdev, &calib.data); + if (rc) { + vl53l1_errmsg("VL53L1_GetCalibrationData fail %d", rc); + rc = store_last_error(data, rc); + goto done; + } + rc = copy_to_user(p + data_offset, &calib.data, + sizeof(calib.data)); + } else { + if (data->enable_sensor) { + rc = -EBUSY; + vl53l1_errmsg("can't set calib data while ranging\n"); + goto done; + } + rc = copy_from_user(&calib.data, p + data_offset, + sizeof(calib.data)); + if (rc) { + vl53l1_errmsg("fail to copy calib data"); + rc = -EFAULT; + goto done; + } + rc = VL53L1_SetCalibrationData(&data->stdev, &calib.data); + if (rc) { + vl53l1_errmsg("VL53L1_SetCalibrationData fail %d", rc); + rc = store_last_error(data, rc); + } + } + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static int ctrl_zone_calibration_data(struct stmvl53l1_data *data, + void __user *p) +{ + int rc; + struct stmvl53l1_ioctl_zone_calibration_data_t *calib; + int data_offset = + offsetof(struct stmvl53l1_ioctl_zone_calibration_data_t, data); + + mutex_lock(&data->work_mutex); + + calib = &data->calib; + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + rc = copy_from_user(calib, p, data_offset); + if (rc) { + vl53l1_errmsg("fail to detect read or write %d", rc); + rc = -EFAULT; + goto done; + } + + if (calib->is_read) { + memset(&calib->data, 0, sizeof(calib->data)); + rc = VL53L1_GetZoneCalibrationData(&data->stdev, + &calib->data.data); + if (rc) { + vl53l1_errmsg("VL53L1_GetZoneCalibrationData fail %d", + rc); + rc = store_last_error(data, rc); + goto done; + } + calib->data.id = data->current_roi_id; + rc = copy_to_user(p + data_offset, &calib->data, + sizeof(calib->data)); + } else { + if (data->enable_sensor) { + rc = -EBUSY; + vl53l1_errmsg("can't set calib data while ranging\n"); + goto done; + } + rc = copy_from_user(&calib->data, p + data_offset, + sizeof(calib->data)); + if (rc) { + vl53l1_errmsg("fail to copy calib data"); + rc = -EFAULT; + goto done; + } + rc = VL53L1_SetZoneCalibrationData(&data->stdev, + &calib->data.data); + if (rc) { + vl53l1_errmsg("VL53L1_SetZoneCalibrationData fail %d", + rc); + rc = store_last_error(data, rc); + goto done; + } + data->current_roi_id = calib->data.id; + } + +done: + mutex_unlock(&data->work_mutex); + + return rc; +} + +static int ctrl_perform_calibration_ref_spad_lock(struct stmvl53l1_data *data, + struct stmvl53l1_ioctl_perform_calibration_t *calib) +{ + int rc = VL53L1_PerformRefSpadManagement(&data->stdev); + + if (rc) { + vl53l1_errmsg("VL53L1_PerformRefSpadManagement fail => %d", rc); + rc = store_last_error(data, rc); + } + + return rc; +} + +static int ctrl_perform_calibration_crosstalk_lock(struct stmvl53l1_data *data, + struct stmvl53l1_ioctl_perform_calibration_t *calib) +{ + int rc = 0; + + /* Set the preset mode passed on param2 */ + data->preset_mode = (VL53L1_PresetModes)(calib->param2); + + rc = stmvl53l1_sendparams(data); + if (rc) + goto done; + + rc = VL53L1_PerformXTalkCalibration(&data->stdev, calib->param1); + if (rc) { + vl53l1_errmsg("VL53L1_PerformXTalkCalibration fail => %d", rc); + rc = store_last_error(data, rc); + } + +done: + return rc; +} + +static int ctrl_perform_zone_calibration_offset_lock( + struct stmvl53l1_data *data, + struct stmvl53l1_ioctl_perform_calibration_t *calib) +{ + int rc; + + /* first sanity check */ + if (calib->param1 != VL53L1_OFFSETCALIBRATIONMODE_MULTI_ZONE) { + vl53l1_errmsg("invalid param1"); + rc = -EINVAL; + goto done; + } + + /* setup offset calibration mode */ + rc = VL53L1_SetOffsetCalibrationMode(&data->stdev, calib->param1); + if (rc) { + vl53l1_errmsg("VL53L1_SetOffsetCalibrationMode fail => %d", rc); + rc = store_last_error(data, rc); + goto done; + } + + /* set mode to multi zone to allow roi settings */ + rc = VL53L1_SetPresetMode(&data->stdev, + VL53L1_PRESETMODE_MULTIZONES_SCANNING); + if (rc) { + vl53l1_errmsg("VL53L1_SetPresetMode fail => %d", rc); + rc = store_last_error(data, rc); + goto done; + } + + /* setup roi */ + rc = VL53L1_SetROI(&data->stdev, &data->roi_cfg); + if (rc) { + vl53l1_errmsg("VL53L1_SetROI fail => %d", rc); + rc = store_last_error(data, rc); + goto done; + } + + /* finally perform calibration */ + /* allow delay add after stop in VL53L1_run__calibration */ + data->is_delay_allowed = 1; + rc = VL53L1_PerformOffsetCalibration(&data->stdev, calib->param2, + calib->param3); + data->is_delay_allowed = 0; + if (rc) { + vl53l1_errmsg("VL53L1_PerformOffsetCalibration fail => %d", rc); + rc = store_last_error(data, rc); + } + + /* save roi hash for later use */ + data->current_roi_id = stmvl53l1_compute_hash(&data->roi_cfg); + +done: + return rc; +} + +static int ctrl_perform_calibration_offset_lock(struct stmvl53l1_data *data, + struct stmvl53l1_ioctl_perform_calibration_t *calib) +{ + int rc; + + /* for legacy purpose we still support mz */ + if (calib->param1 == VL53L1_OFFSETCALIBRATIONMODE_MULTI_ZONE) + return ctrl_perform_zone_calibration_offset_lock(data, calib); + + /* setup offset calibration mode */ + rc = VL53L1_SetOffsetCalibrationMode(&data->stdev, calib->param1); + if (rc) { + vl53l1_errmsg("VL53L1_SetOffsetCalibrationMode fail => %d", rc); + rc = store_last_error(data, rc); + goto done; + } + + /* finally perform calibration */ + /* allow delay add after stop in VL53L1_run__calibration */ + data->is_delay_allowed = 1; + rc = VL53L1_PerformOffsetCalibration(&data->stdev, calib->param2, + calib->param3); + data->is_delay_allowed = 0; + if (rc) { + vl53l1_errmsg("VL53L1_PerformOffsetCalibration fail => %d", rc); + rc = store_last_error(data, rc); + } + +done: + return rc; +} + +static int ctrl_perform_simple_calibration_offset_lock( + struct stmvl53l1_data *data, + struct stmvl53l1_ioctl_perform_calibration_t *calib) +{ + int rc; + + /* finally perform calibration */ + /* allow delay add after stop in VL53L1_run__calibration */ + data->is_delay_allowed = 1; + + rc = stmvl53l1_sendparams(data); + if (rc) + goto done; + + rc = VL53L1_PerformOffsetSimpleCalibration(&data->stdev, calib->param1); + data->is_delay_allowed = 0; + if (rc) { + vl53l1_errmsg( + "VL53L1_PerformOffsetSimpleCalibration fail => %d", rc); + rc = store_last_error(data, rc); + } + +done: + return rc; +} + +static int ctrl_perform_per_vcsel_calibration_offset_lock( + struct stmvl53l1_data *data, + struct stmvl53l1_ioctl_perform_calibration_t *calib) +{ + int rc; + + /* finally perform calibration */ + /* allow delay add after stop in VL53L1_run__calibration */ + data->is_delay_allowed = 1; + + rc = stmvl53l1_sendparams(data); + if (rc) + goto done; + + rc = VL53L1_PerformOffsetPerVcselCalibration(&data->stdev, + calib->param1); + data->is_delay_allowed = 0; + if (rc) { + vl53l1_errmsg( + "VL53L1_PerformOffsetPerVcselCalibration fail => %d", rc); + rc = store_last_error(data, rc); + } + +done: + return rc; +} + +static int ctrl_perform_zero_distance_calibration_offset_lock( + struct stmvl53l1_data *data, + struct stmvl53l1_ioctl_perform_calibration_t *calib) +{ + int rc; + + /* finally perform calibration */ + /* allow delay add after stop in VL53L1_run__calibration */ + data->is_delay_allowed = 1; + + rc = stmvl53l1_sendparams(data); + if (rc) + goto done; + + rc = VL53L1_PerformOffsetZeroDistanceCalibration(&data->stdev); + data->is_delay_allowed = 0; + if (rc) { + vl53l1_errmsg( + "VL53L1_PerformOffsetZeroDistanceCalibration fail => %d", rc); + rc = store_last_error(data, rc); + } + +done: + return rc; +} + +static int ctrl_perform_calibration(struct stmvl53l1_data *data, void __user *p) +{ + int rc; + struct stmvl53l1_ioctl_perform_calibration_t calib; + + mutex_lock(&data->work_mutex); + + if (data->is_device_remove) { + rc = -ENODEV; + goto done; + } + data->is_calibrating = true; + rc = copy_from_user(&calib, p, sizeof(calib)); + if (rc) { + rc = -EFAULT; + goto done; + } + if (data->enable_sensor) { + rc = -EBUSY; + vl53l1_errmsg("can't perform calibration while ranging\n"); + goto done; + } + + rc = reset_release(data); + if (rc) + goto done; + + rc = VL53L1_StaticInit(&data->stdev); + if (rc) { + vl53l1_errmsg("VL53L1_StaticInit fail => %d", rc); + rc = store_last_error(data, rc); + goto done; + } + + switch (calib.calibration_type) { + case VL53L1_CALIBRATION_REF_SPAD: + rc = ctrl_perform_calibration_ref_spad_lock(data, + &calib); + break; + case VL53L1_CALIBRATION_CROSSTALK: + rc = ctrl_perform_calibration_crosstalk_lock(data, + &calib); + break; + case VL53L1_CALIBRATION_OFFSET: + rc = ctrl_perform_calibration_offset_lock(data, + &calib); + break; + case VL53L1_CALIBRATION_OFFSET_SIMPLE: + rc = ctrl_perform_simple_calibration_offset_lock(data, + &calib); + break; + case VL53L1_CALIBRATION_OFFSET_PER_ZONE: + rc = ctrl_perform_zone_calibration_offset_lock(data, + &calib); + break; + case VL53L1_CALIBRATION_OFFSET_PER_VCSEL: + rc = ctrl_perform_per_vcsel_calibration_offset_lock(data, + &calib); + break; + case VL53L1_CALIBRATION_OFFSET_ZERO_DISTANCE: + rc = ctrl_perform_zero_distance_calibration_offset_lock(data, + &calib); + break; + default: + rc = -EINVAL; + break; + } + + reset_hold(data); + +done: + data->is_calibrating = false; + data->is_first_start_done = true; + mutex_unlock(&data->work_mutex); + + return rc; +} + +static int stmvl53l1_ioctl_handler( + struct stmvl53l1_data *data, + unsigned int cmd, unsigned long arg, + void __user *p) +{ + int rc = 0; + + if (!data) + return -EINVAL; + + switch (cmd) { + + case VL53L1_IOCTL_START: + vl53l1_dbgmsg("VL53L1_IOCTL_START\n"); + rc = ctrl_start(data); + break; + + case VL53L1_IOCTL_STOP: + vl53l1_dbgmsg("VL53L1_IOCTL_STOP\n"); + rc = ctrl_stop(data); + break; + + case VL53L1_IOCTL_GETDATAS: + /* vl53l1_dbgmsg("VL53L1_IOCTL_GETDATAS\n"); */ + rc = ctrl_getdata(data, p); + break; + + case VL53L1_IOCTL_GETDATAS_BLOCKING: + /* vl53l1_dbgmsg("VL53L1_IOCTL_GETDATAS_BLOCKING\n"); */ + rc = ctrl_getdata_blocking(data, p); + break; + + /* Register tool */ + case VL53L1_IOCTL_REGISTER: + vl53l1_dbgmsg("VL53L1_IOCTL_REGISTER\n"); + rc = ctrl_reg_access(data, p); + break; + + case VL53L1_IOCTL_PARAMETER: + vl53l1_dbgmsg("VL53L1_IOCTL_PARAMETER\n"); + rc = ctrl_params(data, p); + break; + + case VL53L1_IOCTL_ROI: + vl53l1_dbgmsg("VL53L1_IOCTL_ROI\n"); + rc = ctrl_roi(data, p); + break; + case VL53L1_IOCTL_MZ_DATA: + /* vl53l1_dbgmsg("VL53L1_IOCTL_MZ_DATA\n"); */ + rc = ctrl_mz_data(data, p); + break; + case VL53L1_IOCTL_MZ_DATA_BLOCKING: + /* vl53l1_dbgmsg("VL53L1_IOCTL_MZ_DATA_BLOCKING\n"); */ + rc = ctrl_mz_data_blocking(data, p); + break; + case VL53L1_IOCTL_CALIBRATION_DATA: + vl53l1_dbgmsg("VL53L1_IOCTL_CALIBRATION_DATA\n"); + rc = ctrl_calibration_data(data, p); + break; + case VL53L1_IOCTL_PERFORM_CALIBRATION: + vl53l1_dbgmsg("VL53L1_IOCTL_PERFORM_CALIBRATION\n"); + rc = ctrl_perform_calibration(data, p); + break; + case VL53L1_IOCTL_AUTONOMOUS_CONFIG: + vl53l1_dbgmsg("VL53L1_IOCTL_AUTONOMOUS_CONFIG\n"); + rc = ctrl_autonomous_config(data, p); + break; + case VL53L1_IOCTL_ZONE_CALIBRATION_DATA: + vl53l1_dbgmsg("VL53L1_IOCTL_ZONE_CALIBRATION_DATA\n"); + rc = ctrl_zone_calibration_data(data, p); + break; + case VL53L1_IOCTL_MZ_DATA_ADDITIONAL: + /* vl53l1_dbgmsg("VL53L1_IOCTL_MZ_DATA_ADDITIONAL\n"); */ + rc = ctrl_mz_data_additional(data, p); + break; + case VL53L1_IOCTL_MZ_DATA_ADDITIONAL_BLOCKING: + /* vl53l1_dbgmsg("VL53L1_IOCTL_MZ_DATA_ADDITIONAL_BLOCKING\n"); + */ + rc = ctrl_mz_data_blocking_additional(data, p); + break; + default: + rc = -EINVAL; + break; + } + + return rc; +} + + + +static int stmvl53l1_open(struct inode *inode, struct file *file) +{ + struct stmvl53l1_data *data = container_of(file->private_data, + struct stmvl53l1_data, miscdev); + + vl53l1_dbgmsg("Start\n"); + stmvl53l1_module_func_tbl.get(data->client_object); + vl53l1_dbgmsg("End\n"); + + return 0; +} + +static int stmvl53l1_release(struct inode *inode, struct file *file) +{ + struct stmvl53l1_data *data = container_of(file->private_data, + struct stmvl53l1_data, miscdev); + + vl53l1_dbgmsg("Start\n"); + stmvl53l1_module_func_tbl.put(data->client_object); + vl53l1_dbgmsg("End\n"); + + return 0; +} + + +/** max number or error per measure too abort */ +#define stvm531_get_max_meas_err(...) 3 +/** max number or error per stream too abort */ +#define stvm531_get_max_stream_err(...) 6 + +static void detect_xtalk_value_change(struct stmvl53l1_data *data, + VL53L1_MultiRangingData_t *meas) +{ + data->is_xtalk_value_changed = meas->HasXtalkValueChanged ? true : + data->is_xtalk_value_changed; +} + +/** + * handle data retrieval and dispatch + * + * work lock must be held + * + * called form work or interrupt thread it must be a blocable context ! + * @param data the device + */ +static void stmvl53l1_on_newdata_event(struct stmvl53l1_data *data) +{ + int rc; + VL53L1_RangingMeasurementData_t *pmsinglerange; + VL53L1_MultiRangingData_t *pmrange; + VL53L1_MultiRangingData_t *tmprange; + VL53L1_TargetRangeData_t RangeData[VL53L1_MAX_RANGE_RESULTS]; + VL53L1_RangingMeasurementData_t singledata; + long ts_msec; + int i; + struct input_dev *input = data->input_dev_ps; + + st_gettimeofday(&data->meas.comp_tv); + ts_msec = stmvl53l1_tv_dif(&data->start_tv, &data->meas.comp_tv)/1000; + + pmrange = &data->meas.multi_range_data; + tmprange = &data->meas.tmp_range_data; + pmsinglerange = &data->meas.single_range_data; + + memcpy(&singledata, pmsinglerange, + sizeof(VL53L1_RangingMeasurementData_t)); + for (i = 0; i < VL53L1_MAX_RANGE_RESULTS; i++) + memcpy(&RangeData[i], &pmrange->RangeData[i], + sizeof(VL53L1_TargetRangeData_t)); + + data->meas.intr++; + + /* use get data method based on active mode */ + switch (data->preset_mode) { + case VL53L1_PRESETMODE_LITE_RANGING: + case VL53L1_PRESETMODE_AUTONOMOUS: + case VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS: + rc = VL53L1_GetRangingMeasurementData(&data->stdev, + pmsinglerange); + break; + case VL53L1_PRESETMODE_RANGING: + case VL53L1_PRESETMODE_MULTIZONES_SCANNING: + rc = VL53L1_GetMultiRangingData(&data->stdev, + &data->meas.tmp_range_data); + if (rc) + vl53l1_errmsg("VL53L1_GetRangingMeasurementData @%d %d", + __LINE__, rc); + + /* be sure we got VL53L1_RANGESTATUS_NONE for object 0 if we got + * invalid roi or no object. So if client read data using + * VL53L1_IOCTL_GETDATAS we got correct status. + */ + if (tmprange->RoiStatus == VL53L1_ROISTATUS_NOT_VALID || + tmprange->NumberOfObjectsFound == 0) + tmprange->RangeData[0].RangeStatus = + VL53L1_RANGESTATUS_NONE; + + memcpy(pmrange, tmprange, sizeof(VL53L1_MultiRangingData_t)); + + /* got histogram debug data in case user want it later on */ + if (!rc) + rc = VL53L1_GetAdditionalData(&data->stdev, + &data->meas.additional_data); + detect_xtalk_value_change(data, pmrange); + break; + default: + /* that must be some bug or data corruption stop now */ + rc = -1; + vl53l1_errmsg("unsorted mode %d=>stop\n", data->preset_mode); + _ctrl_stop(data); + input_report_abs(input, ABS_MISC, ABNORMAL_STOP_2); + input_sync(input); + } + /* check if not stopped yet + * as we may have been unlocked we must re-check + */ + if (data->enable_sensor == 0) { + vl53l1_dbgmsg("at meas #%d we got stopped\n", data->meas.cnt); + return; + } + if (rc) { + vl53l1_errmsg("VL53L1_GetRangingMeasurementData @%d %d", + __LINE__, rc); + data->meas.err_cnt++; + data->meas.err_tot++; + if (data->meas.err_cnt > stvm531_get_max_meas_err(data) || + data->meas.err_tot > stvm531_get_max_stream_err(data)) { + vl53l1_errmsg("on #%d %d err %d tot stop", + data->meas.cnt, data->meas.err_cnt, + data->meas.err_tot); + _ctrl_stop(data); + input_report_abs(input, ABS_MISC, ABNORMAL_STOP_3); + input_sync(input); + } + return; + } + + /* FIXME: remove when implemented by ll or bare driver */ + pmrange->TimeStamp = ts_msec; + pmsinglerange->TimeStamp = ts_msec; + for (i = 1; i < pmrange->NumberOfObjectsFound; i++) + pmrange->TimeStamp = ts_msec; + + data->meas.cnt++; + vl53l1_dbgmsg("#%3d %2d poll ts %5d status=%d obj cnt=%d\n", + data->meas.cnt, + data->meas.poll_cnt, + pmrange->TimeStamp, + pmrange->RangeData[0].RangeStatus, + pmrange->NumberOfObjectsFound); +#if 0 + vl53l1_dbgmsg( +"meas m#%04d i#%04d p#%04d in %d ms data range status %d range %d\n", + (int)data->meas.cnt, + (int)data->meas.intr, + (int)data->meas.poll_cnt, + (int)stmvl53l1_tv_dif(&data->meas.start_tv, + &data->meas.comp_tv)/1000, + (int)data->meas.range_data.RangeStatus, + (int)data->meas.range_data.RangeMilliMeter); +#endif + /* ready that is not always on each new data event */ + + /* mark data as valid from now */ + data->is_data_valid = true; + + /* wake up sleeping client */ + wake_up_data_waiters(data); + + /* push data to input subsys and only and make val for ioctl*/ + stmvl53l1_input_push_data(data); + stmvl53l1_insert_flush_events_lock(data); + + /* roll time now data got used */ + data->meas.start_tv = data->meas.comp_tv; + data->meas.poll_cnt = 0; + data->meas.err_cnt = 0; +} + + +/** + * * handle interrupt/pusdo irq by polling handling + * + * work lock must be held + * + * @param data driver + * @return 0 on success + */ +static int stmvl53l1_intr_process(struct stmvl53l1_data *data) +{ + uint8_t data_rdy; + int rc = 0; + struct st_timeval tv_now; + struct input_dev *input = data->input_dev_ps; + + if (!data->enable_sensor) + goto done; + + data->meas.poll_cnt++; + rc = VL53L1_GetMeasurementDataReady(&data->stdev, &data_rdy); + if (rc) { + vl53l1_errmsg("GetMeasurementDataReady @%d %d, fail\n", + __LINE__, rc); + /* too many successive fail => stop but do not try to do any new + * i/o + */ + goto stop_io; + } + + if (!data_rdy) { + /* FIXME this part to completely skip + * if using interrupt and sure we have + * no false interrupt to handle or no to do any timing check + */ + long poll_us; + + st_gettimeofday(&tv_now); + poll_us = stmvl53l1_tv_dif(&data->meas.start_tv, &tv_now); + if (poll_us > data->timing_budget*4) { + vl53l1_errmsg("we're polling %ld ms too long\n", + poll_us/1000); + /* fixme stop or just warn ? */ + goto stop_io; + } + /* keep trying it could be intr with no processing */ + work_dbg("intr with no data rdy"); + goto done; + } + /* we have data to handle */ + /* first irq after reset has no data so we skip it */ + if (data->is_first_irq) { + data->is_first_irq = false; + + if (data->preset_mode == + VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS) { + /* + * If VL53L1_GetRangingMeasurementData() + * is not called after + * for the first ranging measurement, + * the thresholds do not seem + * to work for ALP mode + */ + VL53L1_RangingMeasurementData_t RangingMeasurementData; + + /* printk("Test Workaround for ALP mode\n"); */ + VL53L1_GetRangingMeasurementData(&data->stdev, + &RangingMeasurementData); + } + + } else + stmvl53l1_on_newdata_event(data); + /* enable_sensor could change on event handling check again */ + if (data->enable_sensor) { + /* clear interrupt and continue ranging */ + work_dbg("intr clr"); + /* In autonomous mode, bare driver will trigger stop/start + * sequence. In that case it wall call platform delay functions. + * So allow delay in VL53L1_ClearInterruptAndStartMeasurement() + * call. + */ + data->is_delay_allowed = data->allow_hidden_start_stop; + rc = VL53L1_ClearInterruptAndStartMeasurement(&data->stdev); + data->is_delay_allowed = 0; + if (rc) { + /* go to stop but stop any new i/o for dbg */ + vl53l1_errmsg("Cltr intr restart fail %d\n", rc); + goto stop_io; + } + } +done: + return rc; +stop_io: + /* too many successive fail take action => stop but do not try to do + * any new i/o + */ + vl53l1_errmsg("GetDatardy fail stop\n"); + _ctrl_stop(data); + input_report_abs(input, ABS_MISC, ABNORMAL_STOP_1); + input_sync(input); + return rc; + +} + +static void stmvl53l1_work_handler(struct work_struct *work) +{ + struct stmvl53l1_data *data; + + data = container_of(work, struct stmvl53l1_data, dwork.work); + work_dbg("enter"); + mutex_lock(&data->work_mutex); + stmvl53l1_intr_process(data); + if (data->poll_mode && data->enable_sensor) { + /* re-sched ourself */ + schedule_delayed_work(&data->dwork, + msecs_to_jiffies(data->poll_delay_ms)); + } + mutex_unlock(&data->work_mutex); +} + +static void stmvl53l1_input_push_data_singleobject(struct stmvl53l1_data *data) +{ + struct input_dev *input = data->input_dev_ps; + VL53L1_RangingMeasurementData_t *meas = &data->meas.single_range_data; + FixPoint1616_t LimitCheckCurrent; + VL53L1_Error st = VL53L1_ERROR_NONE; + + vl53l1_dbgmsg("******* FIXME!!! ************\n"); + vl53l1_dbgmsg("Sensor HAL in Lite ranging mode not yet updated\n"); + vl53l1_dbgmsg("******* FIXME!!! ************\n"); + + /* Do not send the events till this if fixed properly */ + return; + + input_report_abs(input, ABS_DISTANCE, (meas->RangeMilliMeter + 5) / 10); + input_report_abs(input, ABS_HAT0X, meas->TimeStamp / 1000); + input_report_abs(input, ABS_HAT0Y, (meas->TimeStamp % 1000) * 1000); + input_report_abs(input, ABS_HAT1X, meas->RangeMilliMeter); + input_report_abs(input, ABS_HAT1Y, meas->RangeStatus); + input_report_abs(input, ABS_HAT2X, meas->SignalRateRtnMegaCps); + input_report_abs(input, ABS_HAT2Y, meas->AmbientRateRtnMegaCps); + input_report_abs(input, ABS_HAT3X, meas->SigmaMilliMeter); + st = VL53L1_GetLimitCheckCurrent(&data->stdev, + VL53L1_CHECKENABLE_SIGMA_FINAL_RANGE, &LimitCheckCurrent); + if (st == VL53L1_ERROR_NONE) + input_report_abs(input, ABS_WHEEL, LimitCheckCurrent); + input_report_abs(input, ABS_TILT_Y, meas->EffectiveSpadRtnCount); + input_report_abs(input, ABS_TOOL_WIDTH, meas->RangeQualityLevel); + input_report_abs(input, ABS_MISC, 0); + + input_sync(input); +} + +static void stmvl53l1_input_push_data_multiobject(struct stmvl53l1_data *data) +{ + VL53L1_MultiRangingData_t *mmeas = &data->meas.multi_range_data; + int i; + int rc; + VL53L1_TargetRangeData_t *meas_array[4]; + VL53L1_CalibrationData_t calibration_data; + struct st_timeval tv; + struct input_dev *input = data->input_dev_ps; + + st_gettimeofday(&tv); + + for (i = 0; i < 4; i++) + meas_array[i] = &mmeas->RangeData[i]; + + /************************************************************* + * INPUT EVENT CODE L1/L3 Data + ABS_HAT0X Time in Sec(32) + ABS_HAT0Y Time in uSec(32) + ABS_HAT1X Obj0_Distance(16) : Obj0_Sigma(16) + ABS_HAT1Y Obj0_MinRange(16) : Obj0_MaxRange(16) + ABS_HAT2X Obj1_Distance(16) : Obj1_Sigma(16) + ABS_HAT2Y Obj1_ MinRange (16) : Obj1_ MaxRange (16) + ABS_HAT3X Obj0_SignalRate_Spad(32) + ABS_HAT3Y Obj1_SignalRate_Spad(32) + ABS_WHEEL AmbientRate(32) + ABS_BRAKE EffectiveSpadRtnCount(16):RangeStatus_1(8): + Range_status_0(8) + ABS_TILT_X XtalkChange(8) :StreamCount(8) : + NumberofObjects(2) : RoiNumber(4) : + RoiStatus(2) + ABS_TILT_Y DMAX + ABS_TOOL_WIDTH XtalkValue + ABS_DISTANCE + ABS_THROTTLE + ABS_RUDDER + ABS_MISC 0: normal operation, other : i2c I/O failure + ABS_VOLUME + ************************************************************/ + + rc = VL53L1_GetCalibrationData(&data->stdev, &calibration_data); + if (rc) { + /* This should not happen */ + vl53l1_errmsg("%d error:%d\n", __LINE__, rc); + return; + } + + /* ABS_HAT0X - Time in Sec(32) */ + + input_report_abs(input, ABS_HAT0X, tv.tv_sec); + vl53l1_dbgmsg("ABS_HAT0X : %lld, %zu\n", tv.tv_sec, sizeof(tv.tv_sec)); + /* ABS_HAT0Y - Time in uSec(32) */ + /* REVISIT : The following code may cause loss of data due to */ + /* 8 bytes to 32 bits conversion */ + input_report_abs(input, ABS_HAT0Y, tv.tv_usec); + vl53l1_dbgmsg("ABS_HAT0Y : %ld\n", tv.tv_usec); + + /* ABS_WHEEL - AmbientRate(32) */ + input_report_abs(input, ABS_WHEEL, + meas_array[0]->AmbientRateRtnMegaCps); + vl53l1_dbgmsg("ABS_WHEEL : AmbRate = %d\n", + meas_array[0]->AmbientRateRtnMegaCps); + + /* ABS_TILT_X XtalkChange(8) :StreamCount(8) : */ + /* Number of Objects(2) : RoiNumber(4) : RoiStatus(2) */ + input_report_abs(input, ABS_TILT_X, + (mmeas->HasXtalkValueChanged << 16) + | (mmeas->StreamCount << 8) + | ((mmeas->NumberOfObjectsFound & 0x3) << 6) + | ((mmeas->RoiNumber & 0xF) << 2) + | (mmeas->RoiStatus & 0x3)); + vl53l1_dbgmsg("ABS_TILT_X :(%d):(%d):(%d):(%d):(%d)\n\n", + mmeas->HasXtalkValueChanged, + mmeas->StreamCount, + mmeas->NumberOfObjectsFound, + mmeas->RoiNumber, + mmeas->RoiStatus + ); + + + /* ABS_TILT_Y DMAX */ + input_report_abs(input, ABS_TILT_Y, mmeas->DmaxMilliMeter); + vl53l1_dbgmsg("ABS_TILT_Y DMAX = %d\n", mmeas->DmaxMilliMeter); + + /* ABS_TOOL_WIDTH */ + input_report_abs(input, ABS_TOOL_WIDTH, +calibration_data.customer.algo__crosstalk_compensation_plane_offset_kcps); + vl53l1_dbgmsg("ABS_TOOL_WIDTH Xtalk = %d\n", +calibration_data.customer.algo__crosstalk_compensation_plane_offset_kcps); + + /* ABS_BRAKE - EffectiveSpadRtnCount(16):RangeStatus_3(1): */ + /* Range_status_2(0) */ + input_report_abs(input, ABS_BRAKE, + mmeas->EffectiveSpadRtnCount << 16 + | ((meas_array[1]->RangeStatus) << 8) + | meas_array[0]->RangeStatus); + + vl53l1_dbgmsg("ABS_BRAKE : (%d):(%d):(%d)\n", + mmeas->EffectiveSpadRtnCount, + meas_array[1]->RangeStatus, + meas_array[0]->RangeStatus); + + vl53l1_dbgmsg("ABS_BRAKE : 0x%X\n", + (mmeas->EffectiveSpadRtnCount & 0xFFFF) << 16 + | ((meas_array[1]->RangeStatus) << 8) + | meas_array[0]->RangeStatus); + + /* Remaining of data are meaningless in case of no target */ + if (mmeas->NumberOfObjectsFound == 0) { + input_sync(input); + return; + } + + /* ABS_HAT1X - Obj0_Distance(16) : Obj0_Sigma(16) */ + input_report_abs(input, ABS_HAT1X, meas_array[0]->RangeMilliMeter << 16 + | (meas_array[0]->SigmaMilliMeter/65536)); + vl53l1_dbgmsg("ABS_HAT1X : 0x%X(%d:%d)\n", + meas_array[0]->RangeMilliMeter << 16 + | (meas_array[0]->SigmaMilliMeter/65536), + meas_array[0]->RangeMilliMeter, + (meas_array[0]->SigmaMilliMeter/65536)); + + /* ABS_HAT1Y - Obj0_MinRange(16) : Obj0_MaxRange(16) */ + input_report_abs(input, ABS_HAT1Y, + meas_array[0]->RangeMinMilliMeter << 16 + | meas_array[0]->RangeMaxMilliMeter); + + vl53l1_dbgmsg("ABS_HAT1Y : 0x%X(%d:%d)\n", + meas_array[0]->RangeMinMilliMeter << 16 + | meas_array[0]->RangeMaxMilliMeter, + meas_array[0]->RangeMinMilliMeter, + meas_array[0]->RangeMaxMilliMeter); + + if (mmeas->NumberOfObjectsFound > 1) { + /* ABS_HAT2X - Obj1_Distance(16) : Obj1_Sigma(16) */ + input_report_abs(input, ABS_HAT2X, + meas_array[1]->RangeMilliMeter << 16 + | (meas_array[1]->SigmaMilliMeter/65536)); + vl53l1_dbgmsg("ABS_HAT2X : 0x%x(%d:%d)\n", + meas_array[1]->RangeMilliMeter << 16 + | (meas_array[1]->SigmaMilliMeter/65536), + meas_array[1]->RangeMilliMeter, + (meas_array[1]->SigmaMilliMeter/65536)); + + /* ABS_HAT2Y - Obj1_ MinRange (16) : Obj1_ MaxRange (16) */ + input_report_abs(input, ABS_HAT2Y, + meas_array[1]->RangeMinMilliMeter << 16 + | meas_array[1]->RangeMaxMilliMeter); + + vl53l1_dbgmsg("ABS_HAT1Y : 0x%X(%d:%d)\n", + meas_array[1]->RangeMinMilliMeter << 16 + | meas_array[1]->RangeMaxMilliMeter, + meas_array[1]->RangeMinMilliMeter, + meas_array[1]->RangeMaxMilliMeter); + + } + + /* ABS_HAT3X - Obj0_SignalRate_Spad(32) */ + input_report_abs(input, ABS_HAT3X, + meas_array[0]->SignalRateRtnMegaCps); + vl53l1_dbgmsg("ABS_HAT3X : SignalRateRtnMegaCps_0(%d)\n", + meas_array[0]->SignalRateRtnMegaCps); + if (mmeas->NumberOfObjectsFound > 1) { + /* ABS_HAT3Y - Obj1_SignalRate_Spad(32) */ + input_report_abs(input, ABS_HAT3Y, + meas_array[1]->SignalRateRtnMegaCps); + vl53l1_dbgmsg("ABS_HAT3Y : SignalRateRtnMegaCps_1(%d)\n", + meas_array[1]->SignalRateRtnMegaCps); + } + + input_report_abs(input, ABS_MISC, 0); + + input_sync(input); + + +} + +static void stmvl53l1_input_push_data(struct stmvl53l1_data *data) +{ + /* use get data method based on active mode */ + switch (data->preset_mode) { + case VL53L1_PRESETMODE_LITE_RANGING: + case VL53L1_PRESETMODE_AUTONOMOUS: + case VL53L1_PRESETMODE_LOWPOWER_AUTONOMOUS: + stmvl53l1_input_push_data_singleobject(data); + break; + default: + /* VL53L1_PRESETMODE_RANGING: + * VL53L1_PRESETMODE_MULTIZONES_SCANNING: + */ + stmvl53l1_input_push_data_multiobject(data); + } +} + +static int stmvl53l1_input_setup(struct stmvl53l1_data *data) +{ + int rc; + struct input_dev *idev; + /* Register to Input Device */ + idev = input_allocate_device(); + if (idev == NULL) { + rc = -ENOMEM; + vl53l1_errmsg("%d error:%d\n", __LINE__, rc); + goto exit_err; + } + /* setup all event */ + set_bit(EV_ABS, idev->evbit); + + input_set_abs_params(idev, ABS_DISTANCE, 0, 0xff, 0, 0); + + input_set_abs_params(idev, ABS_HAT0X, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_HAT0Y, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_HAT1X, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_HAT1Y, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_HAT2X, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_HAT2Y, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_HAT3X, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_HAT3Y, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_WHEEL, 0, 0xffffffff, 0, 0); + + input_set_abs_params(idev, ABS_TILT_Y, 0, 0xffffffff, 0, 0); + + input_set_abs_params(idev, ABS_BRAKE, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_TILT_X, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_TOOL_WIDTH, 0, 0xffffffff, 0, 0); + + input_set_abs_params(idev, ABS_THROTTLE, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_RUDDER, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_MISC, 0, 0xffffffff, 0, 0); + + input_set_abs_params(idev, ABS_VOLUME, 0, 0xffffffff, 0, 0); + input_set_abs_params(idev, ABS_GAS, 0, 0xffffffff, 0, 0); + + idev->name = "STM VL53L1 proximity sensor"; + rc = input_register_device(idev); + if (rc) { + rc = -ENOMEM; + vl53l1_errmsg("%d error:%d\n", __LINE__, rc); + goto exit_free_dev_ps; + } + /* setup drv data */ + input_set_drvdata(idev, data); + data->input_dev_ps = idev; + return 0; + + +exit_free_dev_ps: + input_free_device(data->input_dev_ps); +exit_err: + return rc; +} + +/** + * handler to be called by interface module on interrupt + * + * managed poll/irq filtering in case poll/irq can be soft forced + * and the module side still fire interrupt + * + * @param data + * @return 0 if all ok else for error + */ +int stmvl53l1_intr_handler(struct stmvl53l1_data *data) +{ + int rc; + + mutex_lock(&data->work_mutex); + + /* handle it only if if we are not stopped */ + if (data->enable_sensor) { + rc = stmvl53l1_intr_process(data); + } else { + /* it's likely race/last unhandled interrupt after + * stop. + * Such dummy irq also occured during offset and crosstalk + * calibration procedures. + */ + vl53l1_dbgmsg("got intr but not on (dummy or calibration)\n"); + rc = 0; + } + + mutex_unlock(&data->work_mutex); + return rc; +} + + +/** + * One time device setup + * + * call by bus (i2c/cci) level probe to finalize non bus related device setup + * + * @param data The device data + * @return 0 on success + */ +int stmvl53l1_setup(struct stmvl53l1_data *data) +{ + int rc = 0; + VL53L1_DeviceInfo_t dev_info; + + vl53l1_dbgmsg("Enter\n"); + + /* acquire an id */ + data->id = allocate_dev_id(); + if (data->id < 0) { + vl53l1_errmsg("too many device already created"); + return -1; + } + vl53l1_dbgmsg("Dev id %d is @%p\n", data->id, data); + stmvl53l1_dev_table[data->id] = data; + + /* init mutex */ + /* mutex_init(&data->update_lock); */ + mutex_init(&data->work_mutex); + + /* init work handler */ + INIT_DELAYED_WORK(&data->dwork, stmvl53l1_work_handler); + data->force_device_on_en = force_device_on_en_default; + data->reset_state = 1; + data->is_calibrating = false; + data->last_error = VL53L1_ERROR_NONE; + data->is_device_remove = false; + + rc = stmvl53l1_module_func_tbl.power_up(data->client_object); + if (rc) { + vl53l1_errmsg("%d,error rc %d\n", __LINE__, rc); + goto exit_func_end; + } + rc = reset_release(data); + if (rc) + goto exit_func_end; + + rc = stmvl53l1_input_setup(data); + if (rc) + goto exit_func_end; + + /* init blocking ioctl stuff */ + INIT_LIST_HEAD(&data->simple_data_reader_list); + INIT_LIST_HEAD(&data->mz_data_reader_list); + init_waitqueue_head(&data->waiter_for_data); + data->is_data_valid = false; + + /* Register sysfs hooks under input dev */ + rc = sysfs_create_group(&data->input_dev_ps->dev.kobj, + &stmvl53l1_attr_group); + if (rc) { + rc = -ENOMEM; + vl53l1_errmsg("%d error:%d\n", __LINE__, rc); + goto exit_unregister_dev_ps; + } + rc = sysfs_create_bin_file(&data->input_dev_ps->dev.kobj, + &stmvl53l1_calib_data_attr); + if (rc) { + rc = -ENOMEM; + vl53l1_errmsg("%d error:%d\n", __LINE__, rc); + goto exit_unregister_dev_ps; + } + rc = sysfs_create_bin_file(&data->input_dev_ps->dev.kobj, + &stmvl53l1_zone_calib_data_attr); + if (rc) { + rc = -ENOMEM; + vl53l1_errmsg("%d error:%d\n", __LINE__, rc); + goto exit_unregister_dev_ps; + } + + data->enable_sensor = 0; + + data->poll_delay_ms = STMVL53L1_CFG_POLL_DELAY_MS; + data->timing_budget = STMVL53L1_CFG_TIMING_BUDGET_US; + data->preset_mode = STMVL53L1_CFG_DEFAULT_MODE; + data->distance_mode = STMVL53L1_CFG_DEFAULT_DISTANCE_MODE; + data->crosstalk_enable = STMVL53L1_CFG_DEFAULT_CROSSTALK_ENABLE; + data->output_mode = STMVL53L1_CFG_DEFAULT_OUTPUT_MODE; + data->offset_correction_mode = + STMVL53L1_CFG_DEFAULT_OFFSET_CORRECTION_MODE; + stmvl53l1_setup_auto_config(data); + data->dmax_mode = STMVL53L1_CFG_DEFAULT_DMAX_MODE; + data->smudge_correction_mode = + STMVL53L1_CFG_DEFAULT_SMUDGE_CORRECTION_MODE; + data->current_roi_id = 0; + data->is_xtalk_value_changed = false; + + data->is_delay_allowed = true; + /* need to be done once */ + rc = VL53L1_DataInit(&data->stdev); + data->is_delay_allowed = false; + if (rc) { + vl53l1_errmsg("VL53L1_DataInit %d\n", rc); + goto exit_unregister_dev_ps; + } + + rc = VL53L1_GetDeviceInfo(&data->stdev, &dev_info); + if (rc) { + vl53l1_errmsg("VL53L1_GetDeviceInfo %d\n", rc); + goto exit_unregister_dev_ps; + } + vl53l1_errmsg("device name %s\ntype %s\n", + dev_info.Name, dev_info.Type); + + data->product_type = dev_info.ProductType; + + /* get managed data here */ + rc = VL53L1_GetDmaxReflectance(&data->stdev, &data->dmax_reflectance); + if (rc) { + vl53l1_errmsg("VL53L1_GetDmaxReflectance %d\n", rc); + goto exit_unregister_dev_ps; + } + rc = VL53L1_GetOpticalCenter(&data->stdev, &data->optical_offset_x, + &data->optical_offset_y); + if (rc) { + vl53l1_errmsg("VL53L1_GetOpticalCenter %d\n", rc); + goto exit_unregister_dev_ps; + } + + /* set tuning from stmvl53l1_tunings.h */ + rc = setup_tunings(data); + if (rc) { + vl53l1_errmsg("setup_tunings %d\n", rc); + goto exit_unregister_dev_ps; + } + + /* Set special parameters for VL53L3 (ticket 513812) */ + if (dev_info.ProductType == 0xAA) { + data->timing_budget = 30000; + data->crosstalk_enable = 1; + data->dmax_mode = VL53L1_DMAXMODE_CUSTCAL_DATA; + data->smudge_correction_mode = + VL53L1_SMUDGE_CORRECTION_CONTINUOUS; + data->dmax_reflectance = (5 << 16); + } + /* End of Set special parameters for VL53L3 (ticket 513812) */ + + /* if working in interrupt ask intr to enable and hook the handler */ + data->poll_mode = 0; + rc = stmvl53l1_module_func_tbl.start_intr(data->client_object, + &data->poll_mode); + if (rc < 0) { + vl53l1_errmsg("can't start no intr\n"); + goto exit_unregister_dev_ps; + } + + data->is_first_irq = true; + data->is_first_start_done = false; + data->is_delay_allowed = false; + + /* to register as a misc device */ + data->miscdev.minor = MISC_DYNAMIC_MINOR; + /* multiple dev name use id in name but 1st */ + if (data->id == 0) + strcpy(data->name, VL53L1_MISC_DEV_NAME); + else + sprintf(data->name, "%s%d", VL53L1_MISC_DEV_NAME, data->id); + + data->miscdev.name = data->name; + data->miscdev.fops = &stmvl53l1_ranging_fops; + vl53l1_errmsg("Misc device registration name:%s\n", data->miscdev.name); + rc = misc_register(&data->miscdev); + if (rc != 0) { + vl53l1_errmsg("misc dev reg fail\n"); + goto exit_unregister_dev_ps; + } + /* bring back device under reset */ + reset_hold(data); + + return 0; + +exit_unregister_dev_ps: + sysfs_remove_bin_file(&data->input_dev_ps->dev.kobj, + &stmvl53l1_zone_calib_data_attr); + sysfs_remove_bin_file(&data->input_dev_ps->dev.kobj, + &stmvl53l1_calib_data_attr); + sysfs_remove_group(&data->input_dev_ps->dev.kobj, + &stmvl53l1_attr_group); + input_unregister_device(data->input_dev_ps); + +exit_func_end: + return rc; +} + +/* Attribute: name (RO) */ +static ssize_t name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, VL53L1_INPUT_DEVICE_NAME); +} + +static struct device_attribute laser_name_attributes = __ATTR_RO(name); + +#define LASERDEV_MINOR_BASE 256 +#define LASERDEV_MINOR_MAX 32 + +int stmvl53l1_sysfs_laser(struct stmvl53l1_data *data, bool create) +{ + int i, error = 0; + static struct class *laser_class; + static struct device *laser_class_dev; + static int minor; + + if (data == NULL) { + vl53l1_errmsg("stmvl53l1 dev data is null!\n"); + return -ENODEV; + } + + if (create) { + dev_t devid; + error = alloc_chrdev_region(&devid, 0, 1, "laser"); + vl53l1_errmsg("MAJOR = %d MINOR = %d\n", MAJOR(devid),MINOR(devid)); + minor = MINOR(devid); + if (minor < 0) + vl53l1_errmsg("alloc_chrdev_region error\n"); + //minor = input_get_new_minor(data->sysfs_base, 1, false); + //if (minor < 0) + // minor = input_get_new_minor(LASERDEV_MINOR_BASE, + // LASERDEV_MINOR_MAX, true); + vl53l1_info("assigned minor %d\n", minor); + + laser_class = class_create(THIS_MODULE, "laser"); + if (IS_ERR(laser_class)) { + error = PTR_ERR(laser_class); + laser_class = NULL; + return error; + } + + laser_class_dev = device_create(laser_class, NULL, + MKDEV(INPUT_MAJOR, minor), + data, STMVL53L1_DRV_NAME); + if (IS_ERR(laser_class_dev)) { + error = PTR_ERR(laser_class_dev); + laser_class_dev = NULL; + return error; + } + + for (i = 0; stmvl53l1_attributes[i] != NULL; ++i) { + error = sysfs_create_file(&laser_class_dev->kobj, + stmvl53l1_attributes[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + + error = device_create_file(laser_class_dev, + &laser_name_attributes); + if (error) + goto device_destroy; + + error = sysfs_create_bin_file(&laser_class_dev->kobj, + &stmvl53l1_calib_data_attr); + if (error) { + device_remove_file(laser_class_dev, + &laser_name_attributes); + goto device_destroy; + } + } else { + if (!laser_class || !laser_class_dev) + return -ENODEV; + + sysfs_remove_bin_file(&laser_class_dev->kobj, + &stmvl53l1_calib_data_attr); + device_remove_file(laser_class_dev, + &laser_name_attributes); + for (i = 0; stmvl53l1_attributes[i] != NULL; ++i) + sysfs_remove_file(&laser_class_dev->kobj, + stmvl53l1_attributes[i]); + + device_unregister(laser_class_dev); + class_unregister(laser_class); + } + + vl53l1_info("laser sysfs %s(0x%x) created\n", + STMVL53L1_DRV_NAME, data->sysfs_base); + return 0; + +device_destroy: + for (--i; i >= 0; --i) + sysfs_remove_file(&laser_class_dev->kobj, + stmvl53l1_attributes[i]); + device_destroy(laser_class, MKDEV(INPUT_MAJOR, minor)); + laser_class_dev = NULL; + class_unregister(laser_class); + vl53l1_errmsg("error creating laser class\n"); + + return -ENODEV; +} + +void stmvl53l1_cleanup(struct stmvl53l1_data *data) +{ + int rc; + + vl53l1_dbgmsg("enter\n"); + rc = _ctrl_stop(data); + if (rc < 0) + vl53l1_errmsg("stop failed %d aborting anyway\n", rc); + + if (data->input_dev_ps) { + vl53l1_dbgmsg("to remove sysfs group\n"); + sysfs_remove_group(&data->input_dev_ps->dev.kobj, + &stmvl53l1_attr_group); + sysfs_remove_bin_file(&data->input_dev_ps->dev.kobj, + &stmvl53l1_calib_data_attr); + sysfs_remove_bin_file(&data->input_dev_ps->dev.kobj, + &stmvl53l1_zone_calib_data_attr); + + vl53l1_dbgmsg("to unregister input dev\n"); + input_unregister_device(data->input_dev_ps); + } + + if (!IS_ERR(data->miscdev.this_device) && + data->miscdev.this_device != NULL) { + vl53l1_dbgmsg("to unregister misc dev\n"); + misc_deregister(&data->miscdev); + } + /* be sure device is put under reset */ + data->force_device_on_en = false; + reset_hold(data); + stmvl53l1_module_func_tbl.power_down(data->client_object); + vl53l1_dbgmsg("done\n"); + deallocate_dev_id(data->id); + data->is_device_remove = true; +} + +#ifdef CONFIG_PM_SLEEP +void stmvl53l1_pm_suspend_stop(struct stmvl53l1_data *data) +{ + int rc; + + vl53l1_dbgmsg("Enter\n"); + + rc = _ctrl_stop(data); + if (rc < 0) + vl53l1_errmsg("stop failed %d aborting anyway\n", rc); + + vl53l1_dbgmsg("done\n"); +} +#endif + +#ifdef CONFIG_COMPAT +static long stmvl53l1_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret; + struct stmvl53l1_data *data = + container_of(file->private_data, + struct stmvl53l1_data, miscdev); + ret = stmvl53l1_ioctl_handler(data, cmd, arg, compat_ptr(arg)); + return ret; +} +#endif + +static long stmvl53l1_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + long ret; + struct stmvl53l1_data *data = + container_of(file->private_data, + struct stmvl53l1_data, miscdev); + ret = stmvl53l1_ioctl_handler(data, cmd, arg, (void __user *)arg); + return ret; +} + +static int __init stmvl53l1_init(void) +{ + int rc = -1; + + vl53l1_dbgmsg("Enter\n"); + /* i2c/cci client specific init function */ + rc = stmvl53l1_module_func_tbl.init(); + vl53l1_dbgmsg("End %d\n", rc); + return rc; +} + +static void __exit stmvl53l1_exit(void) +{ + vl53l1_dbgmsg("Enter\n"); + stmvl53l1_module_func_tbl.deinit(NULL); + if (stmvl53l1_module_func_tbl.clean_up != NULL) + stmvl53l1_module_func_tbl.clean_up(); + vl53l1_dbgmsg("End\n"); +} + + +/* MODULE_DEVICE_TABLE(i2c, stmvl53l1_id); */ +MODULE_AUTHOR("STMicroelectronics Imaging Division"); +MODULE_DESCRIPTION("ST FlightSense Time-of-Flight sensor driver"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(DRIVER_VERSION); + +module_init(stmvl53l1_init); +module_exit(stmvl53l1_exit); diff --git a/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_tunings.h b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_tunings.h new file mode 100644 index 000000000000..e74662460d46 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/stmvl53l1_tunings.h @@ -0,0 +1,22 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +/* + * THIS IS A GENERATED FILE + */ + +#ifndef STMVL53L1_TUNINGS_H +#define STMVL53L1_TUNINGS_H + +static const int tunings[][2] = { +}; + +#endif /* STMVL53L1_TUNINGS_H */ diff --git a/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform.h b/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform.h new file mode 100644 index 000000000000..e36568213149 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform.h @@ -0,0 +1,423 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +#ifndef _VL53L1_PLATFORM_H_ +#define _VL53L1_PLATFORM_H_ + +#include "vl53l1_ll_def.h" +#include "vl53l1_platform_log.h" + +#include "vl53l1_platform_user_data.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @file vl53l1_platform.h + * + * @brief All end user OS/platform/application porting + */ + + + +/** + * @brief Initialise platform comms. + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] comms_type : selects between I2C and SPI + * @param[in] comms_speed_khz : unsigned short containing the I2C speed in kHz + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_CommsInitialise( + VL53L1_Dev_t *pdev, + uint8_t comms_type, + uint16_t comms_speed_khz); + + +/** + * @brief Close platform comms. + * + * @param[in] pdev : pointer to device structure (device handle) + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_CommsClose( + VL53L1_Dev_t *pdev); + + +/** + * @brief Writes the supplied byte buffer to the device + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] index : uint16_t register index value + * @param[in] pdata : pointer to uint8_t (byte) buffer containing the data + * to be written + * @param[in] count : number of bytes in the supplied byte buffer + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_WriteMulti( + VL53L1_Dev_t *pdev, + uint16_t index, + uint8_t *pdata, + uint32_t count); + + +/** + * @brief Reads the requested number of bytes from the device + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] index : uint16_t register index value + * @param[out] pdata : pointer to the uint8_t (byte) buffer to store read + * data + * @param[in] count : number of bytes to read + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_ReadMulti( + VL53L1_Dev_t *pdev, + uint16_t index, + uint8_t *pdata, + uint32_t count); + + +/** + * @brief Writes a single byte to the device + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] index : uint16_t register index value + * @param[in] data : uint8_t data value to write + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_WrByte( + VL53L1_Dev_t *pdev, + uint16_t index, + uint8_t data); + + +/** + * @brief Writes a single word (16-bit unsigned) to the device + * + * Manages the big-endian nature of the device register map + * (first byte written is the MS byte). + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] index : uint16_t register index value + * @param[in] data : uin16_t data value write + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_WrWord( + VL53L1_Dev_t *pdev, + uint16_t index, + uint16_t data); + + +/** + * @brief Writes a single dword (32-bit unsigned) to the device + * + * Manages the big-endian nature of the device register map + * (first byte written is the MS byte). + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] index : uint16_t register index value + * @param[in] data : uint32_t data value to write + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_WrDWord( + VL53L1_Dev_t *pdev, + uint16_t index, + uint32_t data); + + + +/** + * @brief Reads a single byte from the device + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] index : uint16_t register index + * @param[out] pdata : pointer to uint8_t data value + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + * + */ + +VL53L1_Error VL53L1_RdByte( + VL53L1_Dev_t *pdev, + uint16_t index, + uint8_t *pdata); + + +/** + * @brief Reads a single word (16-bit unsigned) from the device + * + * Manages the big-endian nature of the device (first byte read is the MS byte). + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] index : uint16_t register index value + * @param[out] pdata : pointer to uint16_t data value + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_RdWord( + VL53L1_Dev_t *pdev, + uint16_t index, + uint16_t *pdata); + + +/** + * @brief Reads a single dword (32-bit unsigned) from the device + * + * Manages the big-endian nature of the device (first byte read is the MS byte). + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] index : uint16_t register index value + * @param[out] pdata : pointer to uint32_t data value + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_RdDWord( + VL53L1_Dev_t *pdev, + uint16_t index, + uint32_t *pdata); + + + +/** + * @brief Implements a programmable wait in us + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] wait_us : integer wait in micro seconds + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_WaitUs( + VL53L1_Dev_t *pdev, + int32_t wait_us); + + +/** + * @brief Implements a programmable wait in ms + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] wait_ms : integer wait in milliseconds + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_WaitMs( + VL53L1_Dev_t *pdev, + int32_t wait_ms); + + +/** + * @brief Get the frequency of the timer used for ranging results time stamps + * + * @param[out] ptimer_freq_hz : pointer for timer frequency + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GetTimerFrequency(int32_t *ptimer_freq_hz); + +/** + * @brief Get the timer value in units of timer_freq_hz (see + * VL53L1_get_timestamp_frequency()) + * + * @param[out] ptimer_count : pointer for timer count value + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GetTimerValue(int32_t *ptimer_count); + + +/** + * @brief Set the mode of a specified GPIO pin + * + * @param pin - an identifier specifying the pin being modified - defined per + * platform + * + * @param mode - an identifier specifying the requested mode - defined per + * platform + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GpioSetMode(uint8_t pin, uint8_t mode); + + +/** + * @brief Set the value of a specified GPIO pin + * + * @param pin - an identifier specifying the pin being modified - defined per + * platform + * + * @param value - a value to set on the GPIO pin - typically 0 or 1 + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GpioSetValue(uint8_t pin, uint8_t value); + + +/** + * @brief Get the value of a specified GPIO pin + * + * @param pin - an identifier specifying the pin being modified - defined per + * platform + * + * @param pvalue - a value retrieved from the GPIO pin - typically 0 or 1 + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GpioGetValue(uint8_t pin, uint8_t *pvalue); + + +/** + * @brief Sets and clears the XShutdown pin on the Ewok + * + * @param value - the value for xshutdown - 0 = in reset, 1 = operational + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GpioXshutdown(uint8_t value); + + +/** + * @brief Sets and clears the Comms Mode pin (NCS) on the Ewok + * + * @param value - the value for comms select - 0 = I2C, 1 = SPI + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GpioCommsSelect(uint8_t value); + + +/** + * @brief Enables and disables the power to the Ewok module + * + * @param value - the state of the power supply - 0 = power off, 1 = power on + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GpioPowerEnable(uint8_t value); + +/** + * @brief Enables callbacks to the supplied funtion pointer when Ewok interrupts + * ocurr + * + * @param function - a function callback supplies by the caller, for interrupt + * notification + * @param edge_type - falling edge or rising edge interrupt detection + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GpioInterruptEnable(void (*function)(void), + uint8_t edge_type); + + +/** + * @brief Disables the callback on Ewok interrupts + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GpioInterruptDisable(void); + + +/* + * @brief Gets current system tick count in [ms] + * + * @param[in] pdev : pointer to device structure (device handle) + * @return time_ms : current time in [ms] + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_GetTickCount( + VL53L1_Dev_t *pdev, + uint32_t *ptime_ms); + + +/** + * @brief Register "wait for value" polling routine + * + * Port of the V2WReg Script function WaitValueMaskEx() + * + * @param[in] pdev : pointer to device structure (device handle) + * @param[in] timeout_ms : timeout in [ms] + * @param[in] index : uint16_t register index value + * @param[in] value : value to wait for + * @param[in] mask : mask to be applied before comparison with value + * @param[in] poll_delay_ms : polling delay been each read transaction in [ms] + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_WaitValueMaskEx( + VL53L1_Dev_t *pdev, + uint32_t timeout_ms, + uint16_t index, + uint8_t value, + uint8_t mask, + uint32_t poll_delay_ms); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_ipp.c b/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_ipp.c new file mode 100644 index 000000000000..29ebf873c4ac --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_ipp.c @@ -0,0 +1,127 @@ + +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +/** + * @file vl53l1_platform_ipp.c + * + * @brief EwokPlus25 IPP Wrapper Layer + */ + +#include "vl53l1_platform.h" +#include "vl53l1_platform_ipp.h" +#include "vl53l1_ll_def.h" +#include "vl53l1_hist_structs.h" +#include "vl53l1_hist_funcs.h" +#include "vl53l1_xtalk.h" + + +#define LOG_FUNCTION_START(fmt, ...) \ + _LOG_FUNCTION_START(VL53L1_TRACE_MODULE_CORE, fmt, ##__VA_ARGS__) +#define LOG_FUNCTION_END(status, ...) \ + _LOG_FUNCTION_END(VL53L1_TRACE_MODULE_CORE, status, ##__VA_ARGS__) + + +VL53L1_Error VL53L1_ipp_hist_process_data( + VL53L1_DEV Dev, + VL53L1_dmax_calibration_data_t *pdmax_cal, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg, + VL53L1_hist_post_process_config_t *ppost_cfg, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_xtalk_histogram_data_t *pxtalk, + uint8_t *pArea1, + uint8_t *pArea2, + uint8_t *phisto_merge_nb, + VL53L1_range_results_t *presults) +{ + + /* + * IPP wrapper for histogram post processing function + */ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + SUPPRESS_UNUSED_WARNING(Dev); + + status = + VL53L1_hist_process_data( + pdmax_cal, + pdmax_cfg, + ppost_cfg, + pbins, + pxtalk, + pArea1, + pArea2, + presults, + phisto_merge_nb); + + return status; +} + + +VL53L1_Error VL53L1_ipp_hist_ambient_dmax( + VL53L1_DEV Dev, + uint16_t target_reflectance, + VL53L1_dmax_calibration_data_t *pdmax_cal, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg, + VL53L1_histogram_bin_data_t *pbins, + int16_t *pambient_dmax_mm) +{ + + /* + * IPP wrapper for histogram ambient DMAX function + * + * The target reflectance in percent for the DMAX calculation + * is set by target_reflectance input + * + * The fixed point format is 7.2 + */ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + SUPPRESS_UNUSED_WARNING(Dev); + + status = + VL53L1_hist_ambient_dmax( + target_reflectance, + pdmax_cal, + pdmax_cfg, + pbins, + pambient_dmax_mm); + + return status; +} + + +VL53L1_Error VL53L1_ipp_xtalk_calibration_process_data( + VL53L1_DEV Dev, + VL53L1_xtalk_range_results_t *pxtalk_ranges, + VL53L1_xtalk_histogram_data_t *pxtalk_shape, + VL53L1_xtalk_calibration_results_t *pxtalk_cal) +{ + + /* + * IPP wrapper for histogram post processing function + */ + + VL53L1_Error status = VL53L1_ERROR_NONE; + + SUPPRESS_UNUSED_WARNING(Dev); + + status = + VL53L1_xtalk_calibration_process_data( + pxtalk_ranges, + pxtalk_shape, + pxtalk_cal); + + return status; +} + + diff --git a/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_ipp.h b/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_ipp.h new file mode 100644 index 000000000000..7888bd68ae93 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_ipp.h @@ -0,0 +1,170 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +#ifndef _VL53L1_PLATFORM_IPP_H_ +#define _VL53L1_PLATFORM_IPP_H_ + +#include "vl53l1_ll_def.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** + * @file vl53l1_platform_ipp.h + * + * @brief EwokPlus25 IPP Wrapper Functions + */ + +/** + * @brief IPP Wrapper call for histogram post processing + * + * + * @param[in] Dev : Device handle + * @param[in] pdmax_cal : DMAX calibration data + * @param[in] pdmax_cfg : DMAX configuration data + * @param[in] ppost_cfg : VL53L1_hist_post_process_config_t + * @param[in] pbins : Input histogram raw bin data + * @param[in] pxtalk : Cross talk histogram data + * @param[out] presults : Output VL53L1_range_results_t + * structure + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_ipp_hist_process_data( + VL53L1_DEV Dev, + VL53L1_dmax_calibration_data_t *pdmax_cal, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg, + VL53L1_hist_post_process_config_t *ppost_cfg, + VL53L1_histogram_bin_data_t *pbins, + VL53L1_xtalk_histogram_data_t *pxtalk, + uint8_t *pArea1, + uint8_t *pArea2, + uint8_t *phisto_merge_nb, + VL53L1_range_results_t *presults); + + +/** + * @brief IPP Wrapper call for histogram ambient dmax calc + * + * The target reflectance in percent for the DMAX calculation + * is set by target_reflectance input + * + * The fixed point format is 7.2 + * + * @param[in] Dev : Device handle + * @param[in] target_reflectance : target reflectance to report ambient DMAX + * Percentage in 7.2 fixed point format + * @param[in] pdmax_cal : DMAX calibration data + * @param[in] pdmax_cfg : DMAX configuration data + * @param[in] pbins : Input histogram raw bin data + * @param[out] pambient_dmax_mm : Output ambient DMAX distance in [mm] + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_ipp_hist_ambient_dmax( + VL53L1_DEV Dev, + uint16_t target_reflectance, + VL53L1_dmax_calibration_data_t *pdmax_cal, + VL53L1_hist_gen3_dmax_config_t *pdmax_cfg, + VL53L1_histogram_bin_data_t *pbins, + int16_t *pambient_dmax_mm); + + +/** + * @brief IPP Wrapper call for xtalk calibration post processing + * + * @param[in] Dev : Device handle + * @param[in] pxtalk_ranges : Input VL53L1_xtalk_range_results_t + * Must contain 5 ranges, 4 quadrants + 1 + * full FoV + * @param[out] pxtalk_shape : Output normalised Cross talk histogram + * shape + * @param[out] pxtalk_cal : Output VL53L1_xtalk_calibration_results_t + * structure + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_ipp_xtalk_calibration_process_data( + VL53L1_DEV Dev, + VL53L1_xtalk_range_results_t *pxtalk_ranges, + VL53L1_xtalk_histogram_data_t *pxtalk_shape, + VL53L1_xtalk_calibration_results_t *pxtalk_cal); + + +/** + * @brief IPP Wrapper call for applying histogram xtalk correction + * + * @param[in] Dev : Device handle + * @param[in] pcustomer : Pointer to input customer data structure + * @param[in] pdyn_cfg : Pointer to input dynamic parameters + * structure + * @param[in] pxtalk_shape : Pointer to input normalised xtalk + * histogram shape + * @param[in] pip_hist_data : Pointer to input histogram data struct + * @param[out] pop_hist_data : Pointer to output xtalk corrected + * histogram data struct + * @param[out] pxtalk_count_data : Pointer to output xtalk histogram + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_ipp_hist_xtalk_correction( + VL53L1_DEV Dev, + VL53L1_customer_nvm_managed_t *pcustomer, + VL53L1_dynamic_config_t *pdyn_cfg, + VL53L1_xtalk_histogram_data_t *pxtalk_shape, + VL53L1_histogram_bin_data_t *pip_hist_data, + VL53L1_histogram_bin_data_t *pop_hist_data, + VL53L1_histogram_bin_data_t *pxtalk_count_data); + +/** + * @brief IPP Wrapper call for Generating Xtalk data from dual reflectance + * histogram data + * + * @param[in] Dev : Device handle + * @param[in] pxtalk_results : Pointer to xtalk_results structure + * containing dual reflectance + * histogram data + * @param[in] expected_target_distance_mm : User input of true target distance + * @param[in] higher_reflectance : User input detailing which + * histogram data 1 or 2 has the + * highest reflectance. + * @param[out] pxtalk_avg_samples : Pointer to output xtalk histogram + * data + * + * @return VL53L1_ERROR_NONE Success + * @return "Other error code" See ::VL53L1_Error + */ + +VL53L1_Error VL53L1_ipp_generate_dual_reflectance_xtalk_samples( + VL53L1_DEV Dev, + VL53L1_xtalk_range_results_t *pxtalk_results, + uint16_t expected_target_distance_mm, + uint8_t higher_reflectance, + VL53L1_histogram_bin_data_t *pxtalk_avg_samples); + + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_ipp_imports.h b/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_ipp_imports.h new file mode 100644 index 000000000000..c2192510cd74 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_ipp_imports.h @@ -0,0 +1,17 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + + +#ifndef _VL53L1_PLATFORM_IPP_IMPORTS_H_ +#define _VL53L1_PLATFORM_IPP_IMPORTS_H_ + +#endif diff --git a/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_log.h b/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_log.h new file mode 100644 index 000000000000..743aa5655017 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_log.h @@ -0,0 +1,157 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +/** + * @file vl53l1_platform_log.h + * + * @brief EwokPlus25 platform logging function definition + */ + + +#ifndef _VL53L1_PLATFORM_LOG_H_ +#define _VL53L1_PLATFORM_LOG_H_ + +#include + +#ifdef VL53L1_LOG_ENABLE + #include "vl53l1_platform_user_config.h" + + #ifdef _MSC_VER + # define EWOKPLUS_EXPORTS __declspec(dllexport) + #else + # define EWOKPLUS_EXPORTS + #endif + + #include "vl53l1_types.h" + + #ifdef __cplusplus + extern "C" { + #endif + + #include + + /** + * @brief Set the level, output and specific functions for module + * logging. + * + * + * @param filename - full path of output log file, NULL for print to + * stdout + * + * @param modules - Module or None or All to trace + * VL53L1_TRACE_MODULE_NONE + * VL53L1_TRACE_MODULE_API + * VL53L1_TRACE_MODULE_CORE + * VL53L1_TRACE_MODULE_TUNING + * VL53L1_TRACE_MODULE_CHARACTERISATION + * VL53L1_TRACE_MODULE_PLATFORM + * VL53L1_TRACE_MODULE_ALL + * + * @param level - trace level + * VL53L1_TRACE_LEVEL_NONE + * VL53L1_TRACE_LEVEL_ERRORS + * VL53L1_TRACE_LEVEL_WARNING + * VL53L1_TRACE_LEVEL_INFO + * VL53L1_TRACE_LEVEL_DEBUG + * VL53L1_TRACE_LEVEL_ALL + * VL53L1_TRACE_LEVEL_IGNORE + * + * @param functions - function level to trace; + * VL53L1_TRACE_FUNCTION_NONE + * VL53L1_TRACE_FUNCTION_I2C + * VL53L1_TRACE_FUNCTION_ALL + * + * @return status - always VL53L1_ERROR_NONE + * + */ + + #define VL53L1_TRACE_LEVEL_NONE 0x00000000 + #define VL53L1_TRACE_LEVEL_ERRORS 0x00000001 + #define VL53L1_TRACE_LEVEL_WARNING 0x00000002 + #define VL53L1_TRACE_LEVEL_INFO 0x00000004 + #define VL53L1_TRACE_LEVEL_DEBUG 0x00000008 + #define VL53L1_TRACE_LEVEL_ALL 0x00000010 + #define VL53L1_TRACE_LEVEL_IGNORE 0x00000020 + + #define VL53L1_TRACE_FUNCTION_NONE 0x00000000 + #define VL53L1_TRACE_FUNCTION_I2C 0x00000001 + #define VL53L1_TRACE_FUNCTION_ALL 0x7fffffff + + #define VL53L1_TRACE_MODULE_NONE 0x00000000 + #define VL53L1_TRACE_MODULE_API 0x00000001 + #define VL53L1_TRACE_MODULE_CORE 0x00000002 + #define VL53L1_TRACE_MODULE_PROTECTED 0x00000004 + #define VL53L1_TRACE_MODULE_HISTOGRAM 0x00000008 + #define VL53L1_TRACE_MODULE_REGISTERS 0x00000010 + #define VL53L1_TRACE_MODULE_PLATFORM 0x00000020 + #define VL53L1_TRACE_MODULE_NVM 0x00000040 + #define VL53L1_TRACE_MODULE_CALIBRATION_DATA 0x00000080 + #define VL53L1_TRACE_MODULE_NVM_DATA 0x00000100 + #define VL53L1_TRACE_MODULE_HISTOGRAM_DATA 0x00000200 + #define VL53L1_TRACE_MODULE_RANGE_RESULTS_DATA 0x00000400 + #define VL53L1_TRACE_MODULE_XTALK_DATA 0x00000800 + #define VL53L1_TRACE_MODULE_OFFSET_DATA 0x00001000 + #define VL53L1_TRACE_MODULE_DATA_INIT 0x00002000 + #define VL53L1_TRACE_MODULE_REF_SPAD_CHAR 0x00004000 + #define VL53L1_TRACE_MODULE_SPAD_RATE_MAP 0x00008000 + #ifdef PAL_EXTENDED + #define VL53L1_TRACE_MODULE_SPAD 0x01000000 + #define VL53L1_TRACE_MODULE_FMT 0x02000000 + #define VL53L1_TRACE_MODULE_UTILS 0x04000000 + #define VL53L1_TRACE_MODULE_BENCH_FUNCS 0x08000000 + #endif + #define VL53L1_TRACE_MODULE_CUSTOMER_API 0x40000000 + #define VL53L1_TRACE_MODULE_ALL 0x7fffffff + + extern void log_trace_print(uint32_t module, uint32_t level, + uint32_t function, const char *format, ...); + + #define _LOG_TRACE_PRINT_FMT(module, level, function, format, ...) \ + log_trace_print(module, level, function, \ + KERN_INFO " " format, ##__VA_ARGS__) + #define _LOG_TRACE_PRINT(module, level, function, ...) \ + _LOG_TRACE_PRINT_FMT(module, level, function, ##__VA_ARGS__) + #define _LOG_FUNCTION_START(module, fmt, ...) \ + log_trace_print(module, VL53L1_TRACE_LEVEL_NONE, \ + VL53L1_TRACE_FUNCTION_ALL, \ + KERN_INFO " %s "fmt"\n", __func__, ##__VA_ARGS__) + #define _LOG_FUNCTION_END(module, status, ...) \ + log_trace_print(module, VL53L1_TRACE_LEVEL_NONE, \ + VL53L1_TRACE_FUNCTION_ALL, \ + KERN_INFO " %s %d\n", __func__, status) + #define _LOG_FUNCTION_END_FMT(module, status, fmt, ...) \ + log_trace_print(module, VL53L1_TRACE_LEVEL_NONE, \ + VL53L1_TRACE_FUNCTION_ALL, \ + KERN_INFO " %s %d"fmt"\n", __func__, status, \ + ##__VA_ARGS__) + #define _LOG_GET_TRACE_FUNCTIONS() 0 + #define _LOG_SET_TRACE_FUNCTIONS(functions) + + #define _LOG_STRING_BUFFER(x) char x[VL53L1_MAX_STRING_LENGTH] + + #ifdef __cplusplus + } + #endif + +#else /* VL53L1_LOG_ENABLE - no logging */ + #include "vl53l1_platform_user_config.h" + + #define _LOG_TRACE_PRINT(module, level, function, ...) + #define _LOG_FUNCTION_START(module, fmt, ...) + #define _LOG_FUNCTION_END(module, status, ...) + #define _LOG_FUNCTION_END_FMT(module, status, fmt, ...) + #define _LOG_GET_TRACE_FUNCTIONS() 0 + #define _LOG_SET_TRACE_FUNCTIONS(functions) + #define _LOG_STRING_BUFFER(x) + +#endif /* VL53L1_LOG_ENABLE */ + +#endif /* _VL53L1_PLATFORM_LOG_H_ */ diff --git a/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_user_config.h b/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_user_config.h new file mode 100644 index 000000000000..d21a76549ad0 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_user_config.h @@ -0,0 +1,97 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +/** + * @file vl53l1_platform_user_config.h + * + * @brief EwokPlus compile time user modifiable configuration + */ + + +#ifndef _VL53L1_PLATFORM_USER_CONFIG_H_ +#define _VL53L1_PLATFORM_USER_CONFIG_H_ + +#define VL53L1_BYTES_PER_WORD 2 +#define VL53L1_BYTES_PER_DWORD 4 + +/* Define polling delays */ +#define VL53L1_BOOT_COMPLETION_POLLING_TIMEOUT_MS 500 +#define VL53L1_RANGE_COMPLETION_POLLING_TIMEOUT_MS 2000 +#define VL53L1_TEST_COMPLETION_POLLING_TIMEOUT_MS 60000 + +#define VL53L1_POLLING_DELAY_MS 1 + +/* Define LLD TuningParms Page Base Address + * - Part of Patch_AddedTuningParms_11761 + */ +#define VL53L1_TUNINGPARM_PUBLIC_PAGE_BASE_ADDRESS 0x8000 +#define VL53L1_TUNINGPARM_PRIVATE_PAGE_BASE_ADDRESS 0xC000 + +#define VL53L1_GAIN_FACTOR__STANDARD_DEFAULT 0x0800 + /*!< Default standard ranging gain correction factor + * 1.11 format. 1.0 = 0x0800, 0.980 = 0x07D7 + */ +#define VL53L1_GAIN_FACTOR__HISTOGRAM_DEFAULT 0x0800 + /*!< Default histogram ranging gain correction factor + * 1.11 format. 1.0 = 0x0800, 0.975 = 0x07CC + */ + + +#define VL53L1_OFFSET_CAL_MIN_EFFECTIVE_SPADS 0x0500 + /*!< Lower Limit for the MM1 effective SPAD count during offset + * calibration Format 8.8 0x0500 -> 5.0 effective SPADs + */ + +#define VL53L1_OFFSET_CAL_MAX_PRE_PEAK_RATE_MCPS 0x1900 + /*!< Max Limit for the pre range preak rate during offset + * calibration Format 9.7 0x1900 -> 50.0 Mcps. + * If larger then in pile up + */ + +#define VL53L1_OFFSET_CAL_MAX_SIGMA_MM 0x0040 + /*!< Max sigma estimate limit during offset calibration + * Check applies to pre-range, mm1 and mm2 ranges + * Format 14.2 0x0040 -> 16.0mm. + */ + + +#define VL53L1_ZONE_CAL_MAX_PRE_PEAK_RATE_MCPS 0x1900 + /*!< Max Peak Rate Limit for the during zone calibration + * Format 9.7 0x1900 -> 50.0 Mcps. + * If larger then in pile up + */ + +#define VL53L1_ZONE_CAL_MAX_SIGMA_MM 0x0040 + /*!< Max sigma estimate limit during zone calibration + * Format 14.2 0x0040 -> 16.0mm. + */ + + +#define VL53L1_XTALK_EXTRACT_MAX_SIGMA_MM 0x008C + /*!< Max Sigma value allowed for a successful xtalk extraction + * Format 14.2 0x008C -> 35.0 mm. + */ + + +#define VL53L1_MAX_USER_ZONES 169 + /*!< Max number of user Zones - maximal limitation from + * FW stream divide - value of 254 + */ + +#define VL53L1_MAX_RANGE_RESULTS 4 + /*!< Sets the maximum number of targets distances the histogram + * post processing can generate + */ + +#define VL53L1_MAX_STRING_LENGTH 512 + +#endif /* _VL53L1_PLATFORM_USER_CONFIG_H_ */ + diff --git a/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_user_data.h b/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_user_data.h new file mode 100644 index 000000000000..a3529a504d38 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_user_data.h @@ -0,0 +1,43 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + +#ifndef _VL53L1_PLATFORM_USER_DATA_H_ +#define _VL53L1_PLATFORM_USER_DATA_H_ + +#include "vl53l1_ll_def.h" + +#include +#include "vl53l1_def.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +#define VL53L1_Dev_t VL53L1_DevData_t +#define VL53L1_DEV VL53L1_DevData_t * + +#define VL53L1DevDataGet(Dev, field) (Dev->field) +#define VL53L1DevDataSet(Dev, field, data) ((Dev->field) = (data)) + +#define VL53L1DevStructGetLLDriverHandle(Dev) (&VL53L1DevDataGet(Dev, LLData)) +#define VL53L1DevStructGetLLResultsHandle(Dev) (&VL53L1DevDataGet(Dev,\ + llresults)) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_user_defines.h b/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_user_defines.h new file mode 100644 index 000000000000..f9e98beb43d4 --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/vl53l1_platform_user_defines.h @@ -0,0 +1,72 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + + +#ifndef _VL53L1_PLATFORM_USER_DEFINES_H_ +#define _VL53L1_PLATFORM_USER_DEFINES_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef __KERNEL__ +#include +#endif + +/** + * @file vl53l1_platform_user_defines.h + * + * @brief All end user OS/platform/application definitions + */ + + +/** + * @def do_division_u + * @brief customer supplied division operation - 64-bit unsigned + * + * @param dividend unsigned 64-bit numerator + * @param divisor unsigned 64-bit denominator + */ +#ifdef __KERNEL__ +#define do_division_u(dividend, divisor) div64_u64(dividend, divisor) +#else +#define do_division_u(dividend, divisor) (dividend / divisor) +#endif + +/** + * @def do_division_s + * @brief customer supplied division operation - 64-bit signed + * + * @param dividend signed 64-bit numerator + * @param divisor signed 64-bit denominator + */ +#ifdef __KERNEL__ +#define do_division_s(dividend, divisor) div64_s64(dividend, divisor) +#else +#define do_division_s(dividend, divisor) (dividend / divisor) +#endif + +#define WARN_OVERRIDE_STATUS(__X__)\ + trace_print(VL53L1_TRACE_LEVEL_WARNING, #__X__) + + +#define DISABLE_WARNINGS() +#define ENABLE_WARNINGS() + + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/drivers/input/misc/vl53L1_14_1_2/vl53l1_types.h b/drivers/input/misc/vl53L1_14_1_2/vl53l1_types.h new file mode 100644 index 000000000000..3b1c331039eb --- /dev/null +++ b/drivers/input/misc/vl53L1_14_1_2/vl53l1_types.h @@ -0,0 +1,27 @@ + +/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */ +/****************************************************************************** + * Copyright (c) 2020, STMicroelectronics - All Rights Reserved + + This file is part of VL53L1 and is dual licensed, + either GPL-2.0+ + or 'BSD 3-clause "New" or "Revised" License' , at your option. + ****************************************************************************** + */ + +/** + * @file vl53l1_types.h + * @brief VL53L1 types definition + */ + +#ifndef _VL53L1_TYPES_H_ +#define _VL53L1_TYPES_H_ + +#include +/** use where fractional values are expected + * + * Given a floating point value f it's .16 bit point is (int)(f*(1<<16)) + */ +typedef uint32_t FixPoint1616_t; + +#endif /* VL53L1_TYPES_H_ */ diff --git a/drivers/input/misc/vl53L5/Android.mk b/drivers/input/misc/vl53L5/Android.mk new file mode 100644 index 000000000000..3e459982b114 --- /dev/null +++ b/drivers/input/misc/vl53L5/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := stmvl53l5.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/misc/vl53L5/Kbuild b/drivers/input/misc/vl53L5/Kbuild new file mode 100644 index 000000000000..c5206c39a4f1 --- /dev/null +++ b/drivers/input/misc/vl53L5/Kbuild @@ -0,0 +1,10 @@ +# +# Kbuild for the vl53L1 drivers. +# + +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +obj-m += stmvl53l5.o +stmvl53l5-objs := stmvl53l5_module.o stmvl53l5_i2c.o stmvl53l5_spi.o stmvl53l5_load_fw.o + diff --git a/drivers/input/misc/vl53L5/Makefile b/drivers/input/misc/vl53L5/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/misc/vl53L5/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/misc/vl53L5/stmvl53l5_i2c.c b/drivers/input/misc/vl53L5/stmvl53l5_i2c.c new file mode 100644 index 000000000000..8392f64588dc --- /dev/null +++ b/drivers/input/misc/vl53L5/stmvl53l5_i2c.c @@ -0,0 +1,144 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stmvl53l5_i2c.h" + +int32_t stmvl53l5_read_multi(struct i2c_client *client, + uint8_t * i2c_buffer, + uint16_t reg_index, + uint8_t *pdata, + uint32_t count) +{ + int32_t status = 0; + uint32_t position = 0; + uint32_t data_size = 0; + struct i2c_msg message; + + message.addr = 0x29; + + do { + data_size = (count - position) > VL53L5_COMMS_CHUNK_SIZE ? VL53L5_COMMS_CHUNK_SIZE : (count - position); + + i2c_buffer[0] = (reg_index + position) >> 8; + i2c_buffer[1] = (reg_index + position) & 0xFF; + + message.flags = 0; + message.buf = i2c_buffer; + message.len = 2; + + status = i2c_transfer(client->adapter, &message, 1); + if (status != 1) + return -EIO; + + message.flags = 1; + message.buf = pdata + position; + message.len = data_size; + + status = i2c_transfer(client->adapter, &message, 1); + if (status != 1) + return -EIO; + + position += data_size; + + } while (position < count); + + return 0; +} + + +int32_t stmvl53l5_write_multi(struct i2c_client *client, + uint8_t *i2c_buffer, + uint16_t reg_index, + uint8_t *pdata, + uint32_t count) +{ + int32_t status = 0; + uint32_t position = 0; + int32_t data_size = 0; + struct i2c_msg message; + + message.addr = 0x29; + + do { + data_size = (count - position) > (VL53L5_COMMS_CHUNK_SIZE-2) ? (VL53L5_COMMS_CHUNK_SIZE-2) : (count - position); + + memcpy(&i2c_buffer[2], &pdata[position], data_size); + + i2c_buffer[0] = (reg_index + position) >> 8; + i2c_buffer[1] = (reg_index + position) & 0xFF; + + message.flags = 0; + message.len = data_size + 2; + message.buf = i2c_buffer; + + status = i2c_transfer(client->adapter, &message, 1); + if (status != 1) + return -EIO; + + position += data_size; + + } while (position < count); + + return 0; +} + +int32_t stmvl53l5_write_byte( + struct i2c_client *client, uint8_t * i2c_buffer, uint16_t address, uint8_t value) +{ + return stmvl53l5_write_multi(client, i2c_buffer, address, &value, 1); +} + +int32_t stmvl53l5_read_byte( + struct i2c_client *client, uint8_t * i2c_buffer, uint16_t address, uint8_t *p_value) +{ + return stmvl53l5_read_multi(client, i2c_buffer, address, p_value, 1); +} diff --git a/drivers/input/misc/vl53L5/stmvl53l5_i2c.h b/drivers/input/misc/vl53L5/stmvl53l5_i2c.h new file mode 100644 index 000000000000..705584ce29eb --- /dev/null +++ b/drivers/input/misc/vl53L5/stmvl53l5_i2c.h @@ -0,0 +1,63 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + +#ifndef STMVL53L5_I2C_H +#define STMVL53L5_I2C_H + +#define VL53L5_COMMS_CHUNK_SIZE 1024 + +int32_t stmvl53l5_write_byte( + struct i2c_client *client, + uint8_t *i2c_buffer, + uint16_t address, + uint8_t value); + +int32_t stmvl53l5_read_byte( + struct i2c_client *client, + uint8_t *i2c_buffer, + uint16_t address, + uint8_t *p_value); + +int32_t stmvl53l5_read_multi( + struct i2c_client *client, + uint8_t *i2c_buffer, + uint16_t reg_index, + uint8_t *pdata, + uint32_t count); + +int32_t stmvl53l5_write_multi( + struct i2c_client *client, + uint8_t *i2c_buffer, + uint16_t reg_index, + uint8_t *pdata, + uint32_t count); + +#endif diff --git a/drivers/input/misc/vl53L5/stmvl53l5_load_fw.c b/drivers/input/misc/vl53L5/stmvl53l5_load_fw.c new file mode 100644 index 000000000000..e5da11dd1aa7 --- /dev/null +++ b/drivers/input/misc/vl53L5/stmvl53l5_load_fw.c @@ -0,0 +1,634 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stmvl53l5_i2c.h" +#include "stmvl53l5_spi.h" +#include "vl53l5_fw_data.h" +#include "stmvl53l5_load_fw.h" + +#define VL53L5_COMMS_BUFFER_SIZE_BYTES 5052 +#define PAGE_SELECT 0x7FFF +#define XSHUT_CTRL 0x0009 + +const uint8_t _fw_buffer[] = EWOKMZ_STXP70_TCPM_RAM_FULL; + +uint8_t * host_dev_p_fw_buff; +uint32_t host_dev_fw_buff_count; +uint8_t * host_dev_p_comms_buff; +uint32_t host_dev_comms_buff_max_count; + +uint8_t _comms_buffer[VL53L5_COMMS_BUFFER_SIZE_BYTES] = {0}; + +#define VL53L5_ASSIGN_FW_BUFF(fw_buff_ptr, count) \ +do {\ + host_dev_p_fw_buff = (uint8_t *)(fw_buff_ptr);\ + host_dev_fw_buff_count = (count);\ +} while (0) + +#define VL53L5_ASSIGN_COMMS_BUFF(comms_buff_ptr, max_count) \ +do {\ + host_dev_p_comms_buff = (comms_buff_ptr);\ + host_dev_comms_buff_max_count = (max_count);\ +} while (0) + +#define MASK_XSHUT_REGISTER(reg_val, value)\ +do {\ + reg_val &= 0xf8;\ + reg_val |= (value ? 0x04 : 0x02);\ +} while (0) + + +struct dci_ui__dev_info_t dev; + +int32_t wait_mcu_boot(struct i2c_client *client, struct spi_data_t *spi_data, uint8_t i2c_not_spi, uint8_t *raw_data_buffer, uint8_t state) +{ + int32_t status = 0; //STATUS_OK; + // uint32_t start_time_ms = 0; + // uint32_t current_time_ms = 0; + + // status = vl53l5_get_tick_count(p_dev, &start_time_ms); + // if (status < STATUS_OK) + // goto exit; + + VL53L5_GO2_STATUS_0(&dev).bytes = 0; + + do { + + if (i2c_not_spi) + status = stmvl53l5_read_multi(client, raw_data_buffer, GO2_STATUS_0, &VL53L5_GO2_STATUS_0(&dev).bytes, 1); + else + status = stmvl53l5_spi_read(spi_data, GO2_STATUS_0, &VL53L5_GO2_STATUS_0(&dev).bytes, 1); + if (status != 0) //< STATUS_OK) + goto exit; + + if (HW_TRAP(&dev)) { + if (i2c_not_spi) + status = stmvl53l5_read_multi(client, raw_data_buffer, GO2_STATUS_1, &VL53L5_GO2_STATUS_1(&dev).bytes, 1); + else + status = stmvl53l5_spi_read(spi_data, GO2_STATUS_1, &VL53L5_GO2_STATUS_1(&dev).bytes, 1); + if (status != 0) //< STATUS_OK) + goto exit; + + // if (VL53L5_GO2_STATUS_1(&dev).bytes) + // status = VL53L5_ERROR_MCU_ERROR_POWER_STATE; + // else + // status = + // VL53L5_ERROR_FALSE_MCU_ERROR_POWER_STATE; + status = -1; + goto exit; + } + + if (MCU_ERROR(&dev)) { + status = -1; //VL53L5_ERROR_MCU_ERROR_POWER_STATE; + goto exit; + } + + if (((state == 1) && MCU_BOOT_COMPLETE(&dev)) || + ((state == 0) && MCU_BOOT_NOT_COMPLETE(&dev))) + break; + + // status = vl53l5_get_tick_count(p_dev, ¤t_time_ms); + // if (status < STATUS_OK) + // goto exit; + + // CHECK_FOR_TIMEOUT( + // status, p_dev, start_time_ms, current_time_ms, + // VL53L5_BOOT_COMPLETION_POLLING_TIMEOUT_MS); + // if (status < STATUS_OK) { + // status = VL53L5_ERROR_BOOT_COMPLETE_TIMEOUT; + // goto exit; + // } + msleep(10); + + } while (1); + +exit: + if (status != 0) + printk("stmvl53l5: wait_mcu_boot failed !\n"); + + return status; +} + + + +int32_t _check_rom_firmware_boot_cut_1_2(struct i2c_client *client, uint8_t * raw_data_buffer) +{ + int status = 0; + + status = wait_mcu_boot(client, NULL, 1, raw_data_buffer, 1); + if (status != 0) + return -1; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x000E, 0x01); + return status; +} + +int32_t check_rom_firmware_boot(struct i2c_client *client, uint8_t * raw_data_buffer) +{ + int32_t status = 0; + uint8_t page = 0; + uint8_t device_id = 0; + uint8_t revision_id = 0; + + //LOG_FUNCTION_START(""); + //printf("entering check rom fw boot ....\n"); + + status = stmvl53l5_write_multi(client, raw_data_buffer, PAGE_SELECT, &page, 1); + if (status != 0) + goto exit; + + status = stmvl53l5_read_multi(client, raw_data_buffer, 0x00, &device_id, 1); + if (status != 0) + goto exit; + printk ("stmvl53l5: device id = 0x%x\n", device_id); + + status = stmvl53l5_read_multi(client, raw_data_buffer, 0x01, &revision_id, 1); + if (status != 0) + goto exit; + printk ("stmvl53l5: revision id = 0x%x\n", revision_id); + + if ((device_id == 0xF0) && (revision_id == 0x02)) { + status = _check_rom_firmware_boot_cut_1_2(client, raw_data_buffer); + if (status != 0) + goto exit; + } else { + status = -1; + goto exit; + } + +exit: + if (status != 0) + printk("stmvl53l5: check_rom_firmware_boot failed : %d\n", status); + return status; +} + + +static int32_t _enable_host_access_to_go1_async(struct i2c_client *client, uint8_t * raw_data_buffer) +{ + int32_t status = 0; + // uint32_t start_time_ms = 0; + // uint32_t current_time_ms = 0; + uint8_t m_status = 0; + uint8_t revision_id = 0; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x7FFF, 0x00); + if (status != 0) + goto exit; + + status = stmvl53l5_read_byte(client, raw_data_buffer, 0x0001, &revision_id); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x7FFF, 0x02); + if (status != 0) + goto exit; + + if (revision_id != 2) { + status = -1; + goto exit; + } + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x03, 0x0D); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x7FFF, 0x01); + if (status != 0) + goto exit; + + m_status = 0; + // status = vl53l5_get_tick_count(p_dev, &start_time_ms); + if (status != 0) + goto exit; + + while ((m_status & 0x10) == 0) { + status = stmvl53l5_read_byte(client, raw_data_buffer, 0x21, &m_status); + if (status != 0) + goto exit; + + // status = vl53l5_get_tick_count(p_dev, ¤t_time_ms); + // if (status != 0) + // goto exit; + + // CHECK_FOR_TIMEOUT( + // status, p_dev, start_time_ms, current_time_ms, + // VL53L5_BOOT_COMPLETION_POLLING_TIMEOUT_MS); + // if (status != 0) { + // trace_print( + // VL53L5_TRACE_LEVEL_ERRORS, + // "ERROR: timeout waiting for mcu idle m_status %02x\n", + // m_status); + // status = VL53L5_ERROR_MCU_IDLE_TIMEOUT; + // goto exit; + //} + msleep(10); + } + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x7FFF, 0x00); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x0C, 0x01); + if (status != 0) + goto exit; + +exit: + if (status != 0) + printk("stmvl53l5: _enable_host_access_to_go1_async failed: %d\n", status); + return status; +} + +static int32_t _set_to_power_on_status(struct i2c_client *client, uint8_t * raw_data_buffer) +{ + int32_t status = 0; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x7FFF, 0x00); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x101, 0x00); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x102, 0x00); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x4002, 0x01); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x4002, 0x00); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x103, 0x01); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x400F, 0x00); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x21A, 0x43); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x21A, 0x03); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x21A, 0x01); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x21A, 0x00); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x219, 0x00); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x21B, 0x00); + if (status != 0) + goto exit; + +exit: + if (status != 0) + printk("stmvl53l5: _set_to_power_on_status failed : %d\n", status); + return status; +} + + +int32_t _wake_up_mcu(struct i2c_client *client, uint8_t * raw_data_buffer) +{ + int32_t status = 0; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x7FFF, 0x00); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x0C, 0x00); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x7FFF, 0x01); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x20, 0x07); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x20, 0x06); + if (status != 0) + goto exit; + +exit: + if (status != 0) + printk("stmvl53l5: _wake_up_mcu failed : %d\n", status); + return status; +} + +int32_t _wait_for_boot_complete_before_fw_load(struct i2c_client *client, uint8_t * raw_data_buffer) +{ + int32_t status = 0; + + status = _enable_host_access_to_go1_async(client, raw_data_buffer); + if (status != 0) + goto exit; + + status = _set_to_power_on_status(client, raw_data_buffer); + if (status != 0) + goto exit; + + status = _wake_up_mcu(client, raw_data_buffer); + if (status != 0) + goto exit; + +exit: + if (status != 0) + printk("stmvl53l5: _wait_for_boot_complete_before_fw_load failed : %d\n", status); + return status; +} + +int32_t _reset_mcu_and_wait_boot(struct i2c_client *client, uint8_t * raw_data_buffer) +{ + int32_t status = 0; + uint8_t u_start[] = {0, 0, 0x42, 0}; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x7FFF, 0x00); + if (status != 0) + goto exit; + + status = stmvl53l5_write_multi(client, raw_data_buffer, 0x114, u_start, 4); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x0B, 0x00); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x0C, 0x00); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x0B, 0x01); + if (status != 0) + goto exit; + + status = wait_mcu_boot(client, NULL, 1, raw_data_buffer, 1); + if (status != 0) + goto exit; + +exit: + if (status != 0) + printk("stmvl53l5: _reset_mcu_and_wait_boot failed : %d\n", status); + return status; +} + + +int32_t _wait_for_boot_complete_after_fw_load(struct i2c_client *client, uint8_t * raw_data_buffer) +{ + int32_t status = 0; + + status = _enable_host_access_to_go1_async(client, raw_data_buffer); + if (status != 0) + goto exit; + + status = _reset_mcu_and_wait_boot(client, raw_data_buffer); + if (status != 0) + goto exit; + +exit: + if (status != 0) + printk("stmvl53l5: _wait_for_boot_complete_after_fw_load failed : %d\n", status); + return status; +} + + +int32_t _write_page( + struct i2c_client *client, uint8_t * raw_data_buffer, uint32_t page_offset, + uint32_t page_size, uint32_t max_chunk_size, uint32_t *p_write_count) +{ + int32_t status; + uint32_t write_size = 0; + uint32_t remainder_size = 0; + uint8_t *p_write_buff = NULL; + + if ((page_offset + max_chunk_size) < page_size) + write_size = max_chunk_size; + else + write_size = page_size - page_offset; + + if (*p_write_count > host_dev_fw_buff_count) { + + p_write_buff = host_dev_p_comms_buff; + memset(p_write_buff, 0, write_size); + } else { + if ((host_dev_fw_buff_count - *p_write_count) + < write_size) { + + p_write_buff = host_dev_p_comms_buff; + remainder_size = + host_dev_fw_buff_count - *p_write_count; + memcpy(p_write_buff, + host_dev_p_fw_buff + *p_write_count, + remainder_size); + memset(p_write_buff + remainder_size, + 0, + write_size - remainder_size); + } else { + + p_write_buff = + host_dev_p_fw_buff + *p_write_count; + } + } + + status = stmvl53l5_write_multi(client, raw_data_buffer, page_offset, p_write_buff, + write_size); + if (status != 0) { + status = -1; + goto exit; + } + (*p_write_count) += write_size; + +exit: + if (status != 0) + printk("stmvl53l5: _write_page failed : %d\n", status); + return status; +} + + +int32_t _download_fw_to_ram(struct i2c_client *client, uint8_t * raw_data_buffer) +{ + int32_t status = 0; + + uint16_t tdcm_offset = 0; + uint8_t tdcm_page = 9; + uint32_t tdcm_page_size = 0; + uint32_t write_count = 0; + + for (tdcm_page = 9; tdcm_page < 12; tdcm_page++) { + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x7FFF, tdcm_page); + if (status != 0) + goto exit; + + if (tdcm_page == 9) + tdcm_page_size = 0x8000; + if (tdcm_page == 10) + tdcm_page_size = 0x8000; + if (tdcm_page == 11) + tdcm_page_size = 0x5000; + + for (tdcm_offset = 0; tdcm_offset < tdcm_page_size; + tdcm_offset += VL53L5_COMMS_BUFFER_SIZE_BYTES) { + status = _write_page( + client, raw_data_buffer, tdcm_offset, tdcm_page_size, + VL53L5_COMMS_BUFFER_SIZE_BYTES, &write_count); + if (status != 0) + goto exit; + } + } + +exit: + if (status != 0) + printk("stmvl53l5: _download_fw_to_ram failed : %d\n", status); + return status; +} + + +int32_t load_firmware(struct i2c_client *client, uint8_t * raw_data_buffer) +{ + int32_t status = 0; + + status = _wait_for_boot_complete_before_fw_load(client, raw_data_buffer); + if (status != 0) + goto exit; + + status = _download_fw_to_ram(client, raw_data_buffer); + if (status != 0) + goto exit; + + status = _wait_for_boot_complete_after_fw_load(client, raw_data_buffer); + if (status != 0) + goto exit; + + status = stmvl53l5_write_byte(client, raw_data_buffer, 0x7FFF, 0x02); + if (status != 0) + goto exit; + +exit: + printk("stmvl53l5: load_firmware : GO2s0=0x%x GO2s1=0x%x status=%d\n", VL53L5_GO2_STATUS_0(&dev).bytes, VL53L5_GO2_STATUS_1(&dev).bytes, status); + return status; +} + + +int32_t stmvl53l5_load_fw_stm(struct i2c_client *client, uint8_t * raw_data_buffer) +{ + + int status = 0; + + VL53L5_ASSIGN_FW_BUFF(_fw_buffer, sizeof(_fw_buffer)); + VL53L5_ASSIGN_COMMS_BUFF(_comms_buffer, sizeof(_comms_buffer)); + status = check_rom_firmware_boot(client, raw_data_buffer); + status |= load_firmware(client, raw_data_buffer); + printk("stmvl53l5: load_fw : %d\n", status); + + return status; + +} + +int32_t stmvl53l5_move_device_to_low_power(struct i2c_client *client, struct spi_data_t *spi_data, uint8_t i2c_not_spi, uint8_t * raw_data_buffer) +{ + + int status = 0; + uint8_t page = 0; + uint8_t reg_val = 0; + + // put the device in low power idle with comms enabled + if (i2c_not_spi) + status = stmvl53l5_write_multi(client, raw_data_buffer, PAGE_SELECT, &page, 1); + else + status = stmvl53l5_spi_write(spi_data, PAGE_SELECT, &page, 1); + if (status != 0) + return -1; + + reg_val = 0; + if (i2c_not_spi) + status = stmvl53l5_read_multi(client, raw_data_buffer, XSHUT_CTRL, ®_val, 1); + else + status = stmvl53l5_spi_write(spi_data, XSHUT_CTRL, ®_val, 1); + + if (status != 0) + return -1; + + MASK_XSHUT_REGISTER(reg_val, 0); + + if (i2c_not_spi) + status = stmvl53l5_write_multi(client, raw_data_buffer, XSHUT_CTRL, ®_val, 1); + else + status = stmvl53l5_spi_write(spi_data, XSHUT_CTRL, ®_val, 1); + + status = wait_mcu_boot(client, spi_data, i2c_not_spi, raw_data_buffer, 0); + if (status != 0) + return -1; + + printk("stmvl53l5: device set in low power (idle with comms) : %d\n", status); + + return status; + +} + diff --git a/drivers/input/misc/vl53L5/stmvl53l5_load_fw.h b/drivers/input/misc/vl53L5/stmvl53l5_load_fw.h new file mode 100644 index 000000000000..74820cbe28e4 --- /dev/null +++ b/drivers/input/misc/vl53L5/stmvl53l5_load_fw.h @@ -0,0 +1,108 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + +#ifndef STMVL53L5_LOAD_FW_H +#define STMVL53L5_LOAD_FW_H + +union dci_union__go2_status_0_go1_u { + uint8_t bytes; + struct { + + uint8_t mcu__boot_complete_go1 : 1; + + uint8_t mcu__analog_checks_ok_go1 : 1; + + uint8_t mcu__threshold_triggered_g01 : 1; + uint8_t mcu__error_flag_go1 : 1; + uint8_t mcu__ui_range_data_present_go1 : 1; + uint8_t mcu__ui_new_range_data_avail_go1 : 1; + uint8_t mcu__ui_update_blocked_go1 : 1; + + uint8_t mcu__hw_trap_flag_go1 : 1; + }; +}; + +union dci_union__go2_status_1_go1_u { + uint8_t bytes; + struct { + + uint8_t mcu__avdd_reg_ok_go1 : 1; + + uint8_t mcu__pll_lock_ok_go1 : 1; + + uint8_t mcu__ls_watchdog_pass_go1 : 1; + + uint8_t mcu__warning_flag_go1 : 1; + + uint8_t mcu__cp_collapse_flag_go1 : 1; + uint8_t mcu__spare2 : 1; + uint8_t mcu__spare3 : 1; + uint8_t mcu__spare4 : 1; + }; +}; + +struct dci_ui__dev_info_t { + + union dci_union__go2_status_0_go1_u dev_info__go2_status_0; + + union dci_union__go2_status_1_go1_u dev_info__go2_status_1; + + uint8_t dev_info__device_status; + + uint8_t dev_info__ui_stream_count; +}; + +#define GO2_STATUS_0 0x6 +#define GO2_STATUS_1 0x7 + +#define VL53L5_GO2_STATUS_0(p_dev) \ + ((p_dev)->dev_info__go2_status_0) + +#define VL53L5_GO2_STATUS_1(p_dev) \ + ((p_dev)->dev_info__go2_status_1) + +#define HW_TRAP(p_dev)\ + (VL53L5_GO2_STATUS_0(p_dev).mcu__hw_trap_flag_go1 == 1) + +#define MCU_ERROR(p_dev)\ + (VL53L5_GO2_STATUS_0(p_dev).mcu__error_flag_go1 == 1) + +#define MCU_BOOT_COMPLETE(p_dev)\ + (VL53L5_GO2_STATUS_0(p_dev).mcu__boot_complete_go1 == 1) + +#define MCU_BOOT_NOT_COMPLETE(p_dev)\ + (VL53L5_GO2_STATUS_0(p_dev).mcu__boot_complete_go1 == 0) + + +int32_t stmvl53l5_load_fw_stm(struct i2c_client *client, uint8_t * i2c_buffer); +int32_t stmvl53l5_move_device_to_low_power(struct i2c_client *client, struct spi_data_t *spi_data, uint8_t i2c_not_spi, uint8_t * raw_data_buffer); + +#endif diff --git a/drivers/input/misc/vl53L5/stmvl53l5_module.c b/drivers/input/misc/vl53L5/stmvl53l5_module.c new file mode 100644 index 000000000000..1b4a3ae86d57 --- /dev/null +++ b/drivers/input/misc/vl53L5/stmvl53l5_module.c @@ -0,0 +1,615 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "stmvl53l5_i2c.h" +#include "stmvl53l5_spi.h" +#include "stmvl53l5_load_fw.h" + +#define STMVL53L5_DRV_NAME "stmvl53l5" +#define STMVL53L5_SLAVE_ADDR 0x29 + +struct stmvl53l5_comms_struct { + __u16 len; + __u16 reg_index; + __u64 buf; + __u8 write_not_read; +}; + +#define ST_TOF_IOCTL_TRANSFER _IOWR('a',0x1, struct stmvl53l5_comms_struct) + +static struct miscdevice st_tof_miscdev; +static uint8_t * raw_data_buffer = NULL; + +static uint8_t i2c_not_spi = 1; + +static uint8_t i2c_driver_added = 0; +static uint8_t spi_driver_registered = 0; +static uint8_t misc_registered = 0; + +static struct spi_data_t spi_data; + +static struct regulator *vl53l5_avdd = NULL; +static struct regulator *vl53l5_iovdd = NULL; +struct stmvl53l5_pinctrl_info { + struct pinctrl *pinctrl; + struct pinctrl_state *gpio_state_active; + struct pinctrl_state *gpio_state_suspend; +} vl53l5_pinctrl_info = {NULL, NULL, NULL}; + +// ------- i2c --------------------------- +static const struct i2c_device_id stmvl53l5_i2c_id[] = { + {STMVL53L5_DRV_NAME, 0}, + {}, +}; + +// ------- spi --------------------------- +static const struct spi_device_id stmvl53l5_spi_id[] = { + {STMVL53L5_DRV_NAME, 0 }, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, stmvl53l5_i2c_id); + +static const struct of_device_id st_tof_of_match[] = { + { + /* An older compatible */ + .compatible = "st,stmvl53l5", + .data = STMVL53L5_DRV_NAME, + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, st_tof_of_match); // add to the kernel device tree table + +static struct i2c_client *stmvl53l5_i2c_client = NULL; + +static int stmvl53l5_open(struct inode *inode, struct file *file) +{ + pr_debug("stmvl53l5 : %s(%d)\n", __func__, __LINE__); + return 0; +} + +static int stmvl53l5_release(struct inode *inode, struct file *file) +{ + pr_debug("stmvl53l5 : %s(%d)\n", __func__, __LINE__); + return 0; +} + +static int stmvl53l5_ioctl_handler(struct file *file, + unsigned int cmd, unsigned long arg, void __user *p) +{ + struct i2c_msg st_i2c_message; + struct stmvl53l5_comms_struct comms_struct; + int ret = 0; + uint16_t index, transfer_size, chunk_size; + u8 __user *data_ptr = NULL; + pr_debug("stmvl53l5_ioctl : cmd = %u\n", cmd); + + switch (cmd) { + case ST_TOF_IOCTL_TRANSFER: + + ret = copy_from_user(&comms_struct, (struct stmvl53l5_comms_struct *)p, sizeof(comms_struct)); + if (ret) { + pr_err("Error at %s(%d)\n", __func__, __LINE__); + return -EINVAL; + } + data_ptr = (u8 __user *)(comms_struct.buf); + // printk("Transfer. write_not_read = %d, reg_index = 0x%x size = %d\n", comms_struct.write_not_read, comms_struct.reg_index, comms_struct.len); + + if (i2c_not_spi) { + // address and buis the same whatever the transfers to be done ! + st_i2c_message.addr = 0x29; + // st_i2c_message.buf is the same whatever the transfers to be done + st_i2c_message.buf = raw_data_buffer; + } + + // in case of i2c write, it is a single transfer with read index set in the 2 first bytes + // the other case use fully the raw data buffer for raw data transfers + if ((i2c_not_spi) && (comms_struct.write_not_read)) + chunk_size = VL53L5_COMMS_CHUNK_SIZE - 2; + else + chunk_size = VL53L5_COMMS_CHUNK_SIZE; + + // index is the number of bytes already transfered + index = 0; + + do { + // take the max number of bytes that can be transfered + transfer_size = (comms_struct.len - index) > chunk_size ? chunk_size : (comms_struct.len - index); + + // ----- WRITE + if (comms_struct.write_not_read) { + // ---- i2c + if (i2c_not_spi) { + // put red index at the beginning of the buffer + raw_data_buffer[0] = (uint8_t)(((comms_struct.reg_index + index) & 0xFF00) >> 8); + raw_data_buffer[1] = (uint8_t)((comms_struct.reg_index + index) & 0x00FF); + + ret = copy_from_user(&raw_data_buffer[2], data_ptr + index, transfer_size); + if (ret) { + pr_err("Error at %s(%d)\n", __func__, __LINE__); + return -EINVAL; + } + + st_i2c_message.len = transfer_size + 2; + st_i2c_message.flags = 0; + st_i2c_message.buf = raw_data_buffer; + ret = i2c_transfer(stmvl53l5_i2c_client->adapter, &st_i2c_message, 1); + if (ret != 1) { + pr_err("Error %d at %s(%d)\n",ret, __func__, __LINE__); + return -EIO; + } + } + // ---- spi + else { + ret = copy_from_user(raw_data_buffer, data_ptr + index, transfer_size); + if (ret) { + pr_err("stmvl53l5: Error at %s(%d)\n", __func__, __LINE__); + return -EINVAL; + } + + ret = stmvl53l5_spi_write(&spi_data, comms_struct.reg_index + index, raw_data_buffer, transfer_size); + if (ret) { + pr_err("Error %d at %s(%d)\n",ret, __func__, __LINE__); + return -EIO; + } + } + } + // ----- READ + else { + // ---- i2c + if (i2c_not_spi) { + // write reg_index + st_i2c_message.len = 2; + st_i2c_message.flags = 0; + st_i2c_message.buf = raw_data_buffer; + raw_data_buffer[0] = (uint8_t)(((comms_struct.reg_index + index) & 0xFF00) >> 8); + raw_data_buffer[1] = (uint8_t)((comms_struct.reg_index + index) & 0x00FF); + + ret = i2c_transfer(stmvl53l5_i2c_client->adapter, &st_i2c_message, 1); + if (ret != 1) { + pr_err("Error at %s(%d)\n", __func__, __LINE__); + return -EIO; + } + + st_i2c_message.len = transfer_size; + st_i2c_message.flags = 1; + st_i2c_message.buf = raw_data_buffer; + ret = i2c_transfer(stmvl53l5_i2c_client->adapter, &st_i2c_message, 1); + if (ret != 1) { + pr_err("Error at %s(%d)\n", __func__, __LINE__); + return -EIO; + } + } + // ---- spi + else { + ret = stmvl53l5_spi_read(&spi_data, comms_struct.reg_index + index, raw_data_buffer, transfer_size); + if (ret) { + pr_err("stmvl53l5: Error at %s(%d)\n", __func__, __LINE__); + return -EIO; + } + } + + // copy to user buffer the read transfer + ret = copy_to_user(data_ptr + index, raw_data_buffer, transfer_size); + if (ret) { + pr_err("Error at %s(%d)\n", __func__, __LINE__); + return -EINVAL; + } + + } // ----- READ + + index += transfer_size; + + } while (index < comms_struct.len); + break; + + default: + return -EINVAL; + + } + return ret; +} + +#ifdef CONFIG_COMPAT +static long stmvl53l5_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret; + ret = stmvl53l5_ioctl_handler(file, cmd, arg, compat_ptr(arg)); + return ret; +} +#endif + +static long stmvl53l5_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret; + ret = stmvl53l5_ioctl_handler(file, cmd, arg, (void __user *)arg); + return ret; +} + +static const struct file_operations stmvl53l5_ranging_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = stmvl53l5_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = stmvl53l5_compat_ioctl, +#endif + .open = stmvl53l5_open, + .release = stmvl53l5_release, +}; + +static int stmvl53l5_init(struct device *dev) +{ + int ret = 0; + + vl53l5_pinctrl_info.pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR_OR_NULL(vl53l5_pinctrl_info.pinctrl)) { + vl53l5_pinctrl_info.gpio_state_active = + pinctrl_lookup_state(vl53l5_pinctrl_info.pinctrl, "laser_default"); + vl53l5_pinctrl_info.gpio_state_suspend = + pinctrl_lookup_state(vl53l5_pinctrl_info.pinctrl, "laser_suspend"); + pinctrl_select_state(vl53l5_pinctrl_info.pinctrl, vl53l5_pinctrl_info.gpio_state_active); + } else { + dev_err(dev, "stmvl53l5: Error. Getting pinctrl handle failed\n"); + } + + if (vl53l5_avdd || vl53l5_iovdd) { + dev_warn(dev, "stmvl53l5: Power regulator already on\n"); + return 0; + } + + vl53l5_avdd = devm_regulator_get_optional(dev, "avdd-vl53l5"); + if (IS_ERR(vl53l5_avdd) || vl53l5_avdd == NULL) { + dev_err(dev, "stmvl53l5: Error. Could not get avdd-vl53l5 power regulator\n"); + ret = -EINVAL; + goto avdd_fail; + } + + vl53l5_iovdd = devm_regulator_get_optional(dev, "iovdd-vl53l5"); + if (IS_ERR(vl53l5_iovdd) || vl53l5_iovdd == NULL) { + dev_err(dev, "stmvl53l5: Error. Could not get iovdd-vl53l5 power regulator\n"); + ret = -EINVAL; + goto iovdd_fail; + } + + ret = regulator_enable(vl53l5_avdd); + if (ret) { + dev_err(dev, "stmvl53l5: Error. Could not enable avdd-vl53l5 power regulator\n"); + goto avdd_en_fail; + } + ret = regulator_enable(vl53l5_iovdd); + if (ret) { + dev_err(dev, "stmvl53l5: Error. Could not enable iovdd-vl53l5 power regulator\n"); + goto iovdd_en_fail; + } + + return 0; + +iovdd_en_fail: + regulator_disable(vl53l5_avdd); +avdd_en_fail: + devm_regulator_put(vl53l5_iovdd); +iovdd_fail: + devm_regulator_put(vl53l5_avdd); +avdd_fail: + vl53l5_avdd = NULL; + vl53l5_iovdd = NULL; + + return ret; +} + +static int stmvl53l5_deinit(struct device *dev) +{ + int ret = 0; + + if (!vl53l5_avdd || !vl53l5_iovdd) { + dev_err(dev, "stmvl53l5: Error. Power regulator already off\n"); + return 0; + } + + ret = regulator_disable(vl53l5_avdd); + if (ret) { + dev_err(dev, "stmvl53l5: Error. Could not disable avdd-vl53l5 power regulator\n"); + } + ret = regulator_disable(vl53l5_iovdd); + if (ret) { + dev_err(dev, "stmvl53l5: Error. Could not disable iovdd-vl53l5 power regulator\n"); + } + + devm_regulator_put(vl53l5_iovdd); + devm_regulator_put(vl53l5_avdd); + vl53l5_avdd = NULL; + vl53l5_iovdd = NULL; + + if (!IS_ERR_OR_NULL(vl53l5_pinctrl_info.pinctrl)) + pinctrl_select_state(vl53l5_pinctrl_info.pinctrl, vl53l5_pinctrl_info.gpio_state_suspend); + + return ret; +} + +static int stmvl53l5_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + uint8_t page = 0, revision_id = 0, device_id = 0; + stmvl53l5_i2c_client = client; + + printk("stmvl53l5: probing i2c\n"); + + ret = stmvl53l5_init(&client->dev); + if (ret) { + pr_err("stmvl53l5: Error. Power up failed, ret = %d\n", ret); + return ret; + } + + i2c_not_spi = 1; + + raw_data_buffer = kzalloc(VL53L5_COMMS_CHUNK_SIZE, GFP_DMA | GFP_KERNEL); + if (raw_data_buffer == NULL) { + ret = -ENOMEM; + goto exit; + } + + ret = stmvl53l5_write_multi(client, raw_data_buffer, 0x7FFF, &page, 1); + ret |= stmvl53l5_read_multi(client, raw_data_buffer, 0x00, &device_id, 1); + ret |= stmvl53l5_read_multi(client, raw_data_buffer, 0x01, &revision_id, 1); + + if ((device_id != 0xF0) || (revision_id != 0x02)) { + pr_err("stmvl53l5: Error. Could not read device and revision id registers\n"); + ret = ret? ret : -ENXIO; + goto exit; + } + printk("stmvl53l5: device_id : 0x%x. revision_id : 0x%x\n", device_id, revision_id); + + st_tof_miscdev.minor = MISC_DYNAMIC_MINOR; + st_tof_miscdev.name = "stmvl53l5"; + st_tof_miscdev.fops = &stmvl53l5_ranging_fops; + st_tof_miscdev.mode = 0444; + + ret = misc_register(&st_tof_miscdev); + if (ret) { + pr_err("stmvl53l5 : Failed to create misc device, err = %d\n", ret); + goto exit; + } + + misc_registered = 1; + + ret = stmvl53l5_load_fw_stm(client, raw_data_buffer); + if (ret) { + pr_err("stmvl53l5 : Failed in loading the FW into the device, err = %d\n", ret); + } + + ret = stmvl53l5_move_device_to_low_power(client, NULL, 1, raw_data_buffer); + if (ret) { + pr_err("stmvl53l5 : could not move the device to low power = %d\n", ret); + } + + if (ret) + goto exit; + + return 0; + +exit: + if (raw_data_buffer) { + kfree(raw_data_buffer); + raw_data_buffer = NULL; + } + stmvl53l5_deinit(&client->dev); + + return ret; +} + +static int stmvl53l5_i2c_remove(struct i2c_client *client) +{ + + if (raw_data_buffer) + kfree(raw_data_buffer); + + stmvl53l5_deinit(&client->dev); + + return 0; +} + +static struct i2c_driver stmvl53l5_i2c_driver = { + .driver = { + .name = STMVL53L5_DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(st_tof_of_match), // for platform register to pick up the dts info + }, + .probe = stmvl53l5_i2c_probe, + .remove = stmvl53l5_i2c_remove, + .id_table = stmvl53l5_i2c_id, +}; + + +static int stmvl53l5_spi_probe(struct spi_device *spi) +{ + int ret; + uint8_t page = 0, revision_id = 0, device_id = 0; + + printk("stmvl53l5: probing spi\n"); + + ret = stmvl53l5_init(&spi->dev); + if (ret) { + pr_err("stmvl53l5: Error. Power up failed, ret = %d\n", ret); + return ret; + } + + i2c_not_spi = 0; + + spi_data.device = spi; + spi_data.device->mode |= SPI_CPHA; + spi_data.device->mode |= SPI_CPOL; + + ret = stmvl53l5_spi_write(&spi_data, 0x7FFF, &page, 1); + ret |= stmvl53l5_spi_read(&spi_data, 0x00, &device_id, 1); + ret |= stmvl53l5_spi_read(&spi_data, 0x01, &revision_id, 1); + + if ((device_id != 0xF0) || (revision_id != 0x02)) { + pr_err("stmvl53l5: Error. Could not read device and revision id registers\n"); + ret = ret? ret : -ENXIO; + goto exit; + } + printk("stmvl53l5: device_id : 0x%x. revision_id : 0x%x\n", device_id, revision_id); + + raw_data_buffer = kzalloc(VL53L5_COMMS_CHUNK_SIZE, GFP_DMA | GFP_KERNEL); + if (raw_data_buffer == NULL) { + ret = -ENOMEM; + goto exit; + } + + st_tof_miscdev.minor = MISC_DYNAMIC_MINOR; + st_tof_miscdev.name = "stmvl53l5"; + st_tof_miscdev.fops = &stmvl53l5_ranging_fops; + st_tof_miscdev.mode = 0444; + + ret = misc_register(&st_tof_miscdev); + if (ret) { + pr_err("stmvl53l5 : Failed to create misc device, err = %d\n", ret); + ret = -1; + goto exit; + } + + misc_registered = 1; + + return 0; + +exit: + if (raw_data_buffer) { + kfree(raw_data_buffer); + raw_data_buffer = NULL; + } + stmvl53l5_deinit(&spi->dev); + + return ret; +} + +static int stmvl53l5_spi_remove(struct spi_device *device) +{ + + if (raw_data_buffer) + kfree(raw_data_buffer); + + stmvl53l5_deinit(&device->dev); + + return 0; +} + + +static struct spi_driver stmvl53l5_spi_driver = { + .driver = { + .name = STMVL53L5_DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(st_tof_of_match), // for platform register to pick up the dts info + }, + .probe = stmvl53l5_spi_probe, + .remove = stmvl53l5_spi_remove, + .id_table = stmvl53l5_spi_id, +}; + + +static int __init st_tof_module_init(void) +{ + int ret = 0; + + printk("stmvl53l5: module init\n"); + + /* register as a i2c client device */ + ret = i2c_add_driver(&stmvl53l5_i2c_driver); + + if (ret) { + i2c_del_driver(&stmvl53l5_i2c_driver); + printk("stmvl53l5: could not add i2c driver\n"); + return ret; + } + + i2c_driver_added = 1; + + ret = spi_register_driver(&stmvl53l5_spi_driver); + if (ret) { + printk("stmvl53l5: could not register spi driver : %d", ret); + return ret; + } + + spi_driver_registered = 1; + + return ret; +} + +static void __exit st_tof_module_exit(void) +{ + + pr_debug("stmvl53l5 : module exit\n"); + + if (misc_registered) { + misc_deregister(&st_tof_miscdev); + misc_registered = 0; + } + + if (spi_driver_registered) { + spi_unregister_driver(&stmvl53l5_spi_driver); + spi_driver_registered = 0; + } + + if (i2c_driver_added) { + i2c_del_driver(&stmvl53l5_i2c_driver); + i2c_driver_added = 0; + } + +} + +module_init(st_tof_module_init); +module_exit(st_tof_module_exit); +MODULE_LICENSE("GPL"); + + + + + diff --git a/drivers/input/misc/vl53L5/stmvl53l5_spi.c b/drivers/input/misc/vl53L5/stmvl53l5_spi.c new file mode 100644 index 000000000000..0710a562b529 --- /dev/null +++ b/drivers/input/misc/vl53L5/stmvl53l5_spi.c @@ -0,0 +1,88 @@ +/* +* This file is part of VL53L5 Kernel Driver +* +* Copyright (C) 2020, STMicroelectronics - All Rights Reserved +* +* License terms: STMicroelectronics Proprietary in accordance with licensing +* terms at www.st.com/sla0081 +* +* STMicroelectronics confidential +* Reproduction and Communication of this document is strictly prohibited unless +* specifically authorized in writing by STMicroelectronics. +* +*/ + +#include +#include + +#include "stmvl53l5_spi.h" + + +#define SPI_READWRITE_BIT 0x8000 + +#define SPI_WRITE_MASK(x) (x | SPI_READWRITE_BIT) +#define SPI_READ_MASK(x) (x & ~SPI_READWRITE_BIT) + +int stmvl53l5_spi_write(struct spi_data_t *spi_data, int index, uint8_t *data, uint16_t len) +{ + int status = 0; + uint8_t index_bytes[2] = {0}; + struct spi_message m; + struct spi_transfer t[2]; + + spi_message_init(&m); + memset(&t, 0, sizeof(t)); + + index_bytes[0] = ((SPI_WRITE_MASK(index) & 0xff00) >> 8); + index_bytes[1] = (SPI_WRITE_MASK(index) & 0xff); + + t[0].tx_buf = index_bytes; + t[0].len = 2; + + t[1].tx_buf = data; + t[1].len = (unsigned int)len; + + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + status = spi_sync(spi_data->device, &m); + if (status != 0) { + printk("stmvl53l5 : spi_sync failed. %d", status); + goto out; + } + +out: + return status; +} + +int stmvl53l5_spi_read(struct spi_data_t *spi_data, int index, uint8_t *data, uint16_t len) +{ + int status = 0; + uint8_t index_bytes[2] = {0}; + struct spi_message m; + struct spi_transfer t[2]; + + spi_message_init(&m); + memset(&t, 0, sizeof(t)); + + index_bytes[0] = ((SPI_READ_MASK(index) >> 8) & 0xff); + index_bytes[1] = (SPI_READ_MASK(index) & 0xff); + + t[0].tx_buf = index_bytes; + t[0].len = 2; + + t[1].rx_buf = data; + t[1].len = (unsigned int)len; + + spi_message_add_tail(&t[0], &m); + spi_message_add_tail(&t[1], &m); + + status = spi_sync(spi_data->device, &m); + if (status != 0) { + printk("stmvl53l5 : spi_sync failed. %d", status); + goto out; + } + +out: + return status; +} diff --git a/drivers/input/misc/vl53L5/stmvl53l5_spi.h b/drivers/input/misc/vl53L5/stmvl53l5_spi.h new file mode 100644 index 000000000000..82b5e91c79aa --- /dev/null +++ b/drivers/input/misc/vl53L5/stmvl53l5_spi.h @@ -0,0 +1,42 @@ +/************************************************************************** + * Copyright (c) 2016, STMicroelectronics - All Rights Reserved + + License terms: BSD 3-clause "New" or "Revised" License. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. 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. + + 3. Neither the name of the copyright holder 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 HOLDER 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. + ****************************************************************************/ + +#ifndef STMVL53L5_SPI_H +#define STMVL53L5_SPI_H + +struct spi_data_t { + struct spi_device *device; +}; + +int stmvl53l5_spi_write(struct spi_data_t *spi_data, int index, uint8_t *data, uint16_t len); +int stmvl53l5_spi_read(struct spi_data_t *spi_data, int index, uint8_t *data, uint16_t len); + +#endif diff --git a/drivers/input/misc/vl53L5/vl53l5_fw_data.h b/drivers/input/misc/vl53L5/vl53l5_fw_data.h new file mode 100644 index 000000000000..fccb3deb6064 --- /dev/null +++ b/drivers/input/misc/vl53L5/vl53l5_fw_data.h @@ -0,0 +1,43027 @@ + +#ifndef __VL53L5_FW_DATA_H__ +#define __VL53L5_FW_DATA_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#define EWOKMZ_STXP70_TCPM_RAM_FULL { \ + 0xe0, 0x00, 0x03, 0x08, \ + 0xe0, 0x00, 0x0a, 0xc8, \ + 0xe0, 0x00, 0x05, 0x08, \ + 0xe0, 0x64, 0x33, 0xa8, \ + 0xe0, 0x00, 0x0a, 0x88, \ + 0xe0, 0x00, 0x0a, 0x88, \ + 0xe0, 0x00, 0x0a, 0x88, \ + 0xe0, 0x00, 0x0a, 0x88, \ + 0xe0, 0x68, 0x15, 0x08, \ + 0xe0, 0x68, 0x18, 0xe8, \ + 0xe0, 0x68, 0x1d, 0x48, \ + 0xe0, 0x68, 0x22, 0xe8, \ + 0xe0, 0x68, 0x2b, 0x48, \ + 0xe0, 0x68, 0x3a, 0x28, \ + 0xe0, 0x68, 0x3e, 0x88, \ + 0xe0, 0x6c, 0x02, 0x68, \ + 0xe0, 0x6c, 0x09, 0xc8, \ + 0xe0, 0x6c, 0x0d, 0x68, \ + 0xe0, 0x00, 0x09, 0xe8, \ + 0xe0, 0x00, 0x09, 0xc8, \ + 0xe0, 0x00, 0x09, 0xa8, \ + 0xe0, 0x00, 0x09, 0x88, \ + 0xe0, 0x00, 0x09, 0x68, \ + 0xe0, 0x00, 0x09, 0x48, \ + 0xf8, 0x02, 0x00, 0x06, \ + 0xc1, 0xc3, 0x81, 0x01, \ + 0x1e, 0x06, 0xa1, 0x0b, \ + 0x1a, 0x06, 0x81, 0x00, \ + 0x99, 0x23, 0xe1, 0x00, \ + 0x97, 0xb3, 0x97, 0xe3, \ + 0x06, 0x2c, 0x97, 0x73, \ + 0x06, 0x34, 0xe2, 0xc0, \ + 0x06, 0x2c, 0xfc, 0x80, \ + 0xc1, 0x83, 0xe3, 0x40, \ + 0xe2, 0xc0, 0x06, 0x1c, \ + 0xfc, 0x98, 0x06, 0x34, \ + 0xfc, 0x9c, 0x06, 0x34, \ + 0xe3, 0x40, 0x06, 0x1c, \ + 0x41, 0x38, 0xc1, 0x83, \ + 0xc1, 0x43, 0xe8, 0x04, \ + 0xc1, 0xc3, 0x81, 0x05, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0xf4, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xff, 0x84, 0x07, 0xfc, \ + 0x9a, 0x14, 0xd8, 0x04, \ + 0xe4, 0x30, 0x04, 0x60, \ + 0xfc, 0x12, 0x4c, 0x06, \ + 0xe0, 0xd0, 0x4c, 0x4a, \ + 0xc1, 0xc3, 0x82, 0x15, \ + 0xc1, 0xc3, 0xc1, 0xc3, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0x81, 0x93, 0x81, 0x83, \ + 0xe2, 0xc0, 0x0c, 0x2c, \ + 0x1c, 0x8c, 0xd1, 0x67, \ + 0xe3, 0x6b, 0xe4, 0x04, \ + 0xe5, 0x6b, 0x8e, 0x05, \ + 0x80, 0x64, 0x2f, 0x28, \ + 0x30, 0x38, 0xe7, 0x6b, \ + 0xe9, 0x6b, 0x80, 0x64, \ + 0xeb, 0x6b, 0x8a, 0xc5, \ + 0x80, 0x64, 0x31, 0x28, \ + 0x34, 0xb8, 0xed, 0x6b, \ + 0xf1, 0x6b, 0x80, 0x64, \ + 0x80, 0x64, 0x32, 0x08, \ + 0x33, 0x18, 0xf3, 0x6b, \ + 0xf5, 0x6b, 0x80, 0x64, \ + 0xf7, 0x6b, 0x8a, 0xc5, \ + 0xf9, 0x6b, 0x8c, 0x05, \ + 0xfb, 0x6b, 0x8c, 0x45, \ + 0x0c, 0x0a, 0x8a, 0x45, \ + 0x8a, 0x65, 0xe4, 0xb4, \ + 0xe4, 0xb8, 0x0c, 0x0a, \ + 0x34, 0x98, 0x84, 0x45, \ + 0x40, 0x06, 0xe0, 0x64, \ + 0x40, 0x4a, 0xfc, 0x12, \ + 0x80, 0xd5, 0xe0, 0xd0, \ + 0xc1, 0xc3, 0xc1, 0xc3, \ + 0x1f, 0x0c, 0xc1, 0xc3, \ + 0x0f, 0xfc, 0xf0, 0x04, \ + 0xbc, 0xf4, 0xf7, 0x84, \ + 0xc1, 0x53, 0xfd, 0x64, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xc1, 0xc3, 0xc1, 0x43, \ + 0xc1, 0xc3, 0xc1, 0x53, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0x61, 0x7c, 0x0a, 0x14, \ + 0xf9, 0x84, 0x48, 0x02, \ + 0x70, 0x94, 0x4e, 0x02, \ + 0xe1, 0x7c, 0x18, 0x34, \ + 0x72, 0x94, 0x50, 0x12, \ + 0x52, 0x22, 0xa1, 0x4b, \ + 0x81, 0x41, 0x70, 0xb0, \ + 0x72, 0xb0, 0x4c, 0x32, \ + 0x80, 0x00, 0x00, 0x06, \ + 0x02, 0x06, 0x22, 0xc5, \ + 0xa6, 0x2f, 0x80, 0x00, \ + 0xa2, 0x25, 0xa1, 0x2b, \ + 0xd8, 0x6f, 0x19, 0x6b, \ + 0x44, 0x82, 0x52, 0xcf, \ + 0x00, 0xc4, 0xf9, 0x14, \ + 0x12, 0x24, 0x16, 0x80, \ + 0x00, 0x00, 0xe0, 0xfc, \ + 0x00, 0x64, 0x84, 0x80, \ + 0x8b, 0x8b, 0x56, 0x80, \ + 0x81, 0x81, 0x4e, 0x5f, \ + 0x96, 0x80, 0x0e, 0x54, \ + 0x84, 0x80, 0x0e, 0x70, \ + 0xd6, 0x80, 0x0e, 0x24, \ + 0x0e, 0x03, 0xce, 0x7b, \ + 0x23, 0x0d, 0x81, 0x71, \ + 0xe0, 0x00, 0x00, 0x02, \ + 0x9c, 0x00, 0x50, 0x00, \ + 0x0e, 0xc4, 0xa1, 0x8b, \ + 0x10, 0x8a, 0x4f, 0xa0, \ + 0x0c, 0x80, 0xe4, 0x80, \ + 0x10, 0x9a, 0x0c, 0x80, \ + 0x0c, 0xc4, 0xe5, 0x00, \ + 0x0c, 0x64, 0x88, 0x98, \ + 0x4c, 0x62, 0xc8, 0xa0, \ + 0x81, 0x71, 0x59, 0x9c, \ + 0x48, 0xa0, 0x0e, 0xc4, \ + 0xa0, 0x00, 0x0c, 0x06, \ + 0x61, 0x84, 0x18, 0x24, \ + 0xe1, 0x84, 0x10, 0x54, \ + 0x20, 0x00, 0x0e, 0x06, \ + 0xe0, 0x30, 0x4c, 0x1a, \ + 0x4d, 0x3a, 0xb2, 0x8f, \ + 0xc0, 0x00, 0x24, 0x31, \ + 0xa4, 0x20, 0x4f, 0x0a, \ + 0xed, 0x8c, 0x40, 0x0a, \ + 0x24, 0x31, 0x4d, 0x7a, \ + 0x83, 0xc1, 0x40, 0x00, \ + 0x62, 0x00, 0x10, 0x0c, \ + 0xa4, 0x20, 0x4f, 0x5a, \ + 0x6d, 0x8c, 0x42, 0x1a, \ + 0x18, 0x26, 0xc2, 0x3f, \ + 0x40, 0x00, 0x20, 0x01, \ + 0xe1, 0x84, 0x12, 0x74, \ + 0x78, 0x7c, 0x42, 0x80, \ + 0x80, 0x84, 0x0c, 0x64, \ + 0x98, 0x03, 0x63, 0x9f, \ + 0x79, 0xa4, 0x58, 0x62, \ + 0xec, 0x04, 0x0c, 0x00, \ + 0x64, 0x00, 0x0c, 0x4a, \ + 0x01, 0x01, 0xb1, 0x7f, \ + 0x44, 0x1a, 0x81, 0x11, \ + 0x10, 0x04, 0x60, 0x30, \ + 0xc2, 0x17, 0xe1, 0xfc, \ + 0x24, 0x31, 0x44, 0xfa, \ + 0xb0, 0x1f, 0x40, 0x00, \ + 0x24, 0x1c, 0x4a, 0xda, \ + 0x42, 0x1a, 0xc2, 0x07, \ + 0x10, 0xc4, 0x6d, 0x8c, \ + 0x4a, 0x52, 0xe0, 0xfc, \ + 0x40, 0x00, 0x31, 0x9d, \ + 0x44, 0x22, 0xc3, 0x77, \ + 0x40, 0x00, 0x33, 0xb1, \ + 0x3c, 0x81, 0x00, 0x04, \ + 0x30, 0x7f, 0xc0, 0x00, \ + 0x4d, 0x08, 0xc3, 0xc7, \ + 0x44, 0x42, 0x87, 0xf0, \ + 0xa1, 0x3d, 0x71, 0x80, \ + 0x73, 0x84, 0x46, 0x42, \ + 0xdc, 0x00, 0x40, 0x20, \ + 0xdc, 0x00, 0x42, 0x30, \ + 0xf0, 0x1c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x0c, 0x04, 0x4c, \ + 0xf9, 0x84, 0x48, 0x02, \ + 0x81, 0x41, 0x21, 0x4b, \ + 0x04, 0x06, 0x20, 0x45, \ + 0x4a, 0x22, 0x80, 0x00, \ + 0xa1, 0x5b, 0xf9, 0x8c, \ + 0x1c, 0x00, 0x44, 0x00, \ + 0x9c, 0x00, 0x48, 0x10, \ + 0x21, 0x3b, 0x9e, 0x05, \ + 0x0a, 0x24, 0x81, 0x61, \ + 0x0e, 0x14, 0x16, 0x80, \ + 0x0a, 0x50, 0xe0, 0xfc, \ + 0x0a, 0x34, 0x84, 0x80, \ + 0xa1, 0x1b, 0x56, 0x80, \ + 0x96, 0x80, 0x18, 0x04, \ + 0x84, 0x80, 0x18, 0xc0, \ + 0xd6, 0x80, 0x18, 0x14, \ + 0x18, 0x53, 0xd8, 0xcb, \ + 0x23, 0x5d, 0x81, 0xc1, \ + 0xe0, 0x00, 0x0a, 0x52, \ + 0x9c, 0x00, 0x4c, 0x50, \ + 0x18, 0x24, 0xa1, 0x6b, \ + 0x0c, 0x8a, 0x4f, 0x98, \ + 0x06, 0x60, 0xe4, 0x80, \ + 0x0c, 0x9a, 0x0c, 0x80, \ + 0x06, 0x24, 0xe5, 0x00, \ + 0x06, 0x34, 0x88, 0x8c, \ + 0x46, 0x32, 0xc8, 0x98, \ + 0x81, 0xc1, 0x59, 0xb0, \ + 0x48, 0x98, 0x18, 0x24, \ + 0xa0, 0x00, 0x06, 0x06, \ + 0x61, 0x84, 0x04, 0x14, \ + 0xe1, 0x84, 0x0c, 0x04, \ + 0x20, 0x00, 0x18, 0x06, \ + 0xe0, 0x08, 0x46, 0x1a, \ + 0x47, 0x3a, 0xae, 0x6f, \ + 0xc0, 0x00, 0x24, 0x09, \ + 0xa4, 0x18, 0x59, 0x0a, \ + 0xed, 0x8c, 0x40, 0x0a, \ + 0x24, 0x09, 0x47, 0x7a, \ + 0x83, 0x21, 0x40, 0x00, \ + 0x62, 0x00, 0x0c, 0x0c, \ + 0xa4, 0x18, 0x59, 0x5a, \ + 0x6d, 0x8c, 0x42, 0x1a, \ + 0xe1, 0x84, 0x10, 0xc4, \ + 0x20, 0x01, 0x04, 0x26, \ + 0x4e, 0x60, 0x40, 0x00, \ + 0x0a, 0x23, 0xf8, 0x7c, \ + 0x80, 0x84, 0x06, 0x34, \ + 0x6c, 0x04, 0x0a, 0x20, \ + 0x0a, 0x4a, 0xef, 0x8f, \ + 0xb0, 0x3f, 0x64, 0x00, \ + 0x81, 0x21, 0x2d, 0xcf, \ + 0x60, 0x0c, 0x42, 0x1a, \ + 0xe1, 0xfc, 0x0c, 0x24, \ + 0x42, 0xfa, 0xc2, 0x47, \ + 0x40, 0x00, 0x24, 0x0d, \ + 0x40, 0xda, 0xac, 0x4f, \ + 0xc2, 0x27, 0x24, 0x30, \ + 0x6d, 0x8c, 0x42, 0x1a, \ + 0xe0, 0xfc, 0x0c, 0x34, \ + 0x31, 0xb1, 0x40, 0x02, \ + 0xc3, 0xc7, 0x40, 0x00, \ + 0x33, 0x8d, 0x42, 0x12, \ + 0x04, 0x24, 0x40, 0x00, \ + 0xc0, 0x00, 0x3c, 0x81, \ + 0xc3, 0x37, 0x2c, 0xcf, \ + 0x87, 0xf0, 0x4b, 0x08, \ + 0x88, 0x1b, 0x04, 0x0b, \ + 0xf0, 0x0c, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x4c, \ + 0x80, 0xab, 0x02, 0x8b, \ + 0x79, 0xa0, 0x40, 0xa2, \ + 0x21, 0x0b, 0x86, 0x9b, \ + 0x01, 0x11, 0x84, 0x6b, \ + 0x84, 0xe5, 0x81, 0x71, \ + 0xf9, 0xa4, 0x40, 0x62, \ + 0x84, 0x65, 0xa1, 0x0b, \ + 0x8c, 0x1b, 0x14, 0x0b, \ + 0xe8, 0x00, 0x41, 0x48, \ + 0x82, 0xbb, 0x00, 0x7b, \ + 0x92, 0x1b, 0x14, 0x0b, \ + 0xe8, 0x00, 0x40, 0xe8, \ + 0x90, 0x0b, 0x00, 0x9b, \ + 0x68, 0x00, 0x40, 0xa8, \ + 0x92, 0x03, 0x8c, 0x1b, \ + 0xf0, 0xac, 0x42, 0x02, \ + 0x0c, 0x4c, 0x8e, 0x0b, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe6, 0x86, 0x44, 0x0e, \ + 0xe7, 0x06, 0x46, 0x0e, \ + 0x60, 0xc0, 0x0a, 0x24, \ + 0x08, 0x34, 0xe1, 0x27, \ + 0xe1, 0x37, 0x60, 0xc0, \ + 0x4a, 0x0e, 0x0a, 0x43, \ + 0x42, 0x0e, 0xe6, 0x06, \ + 0x44, 0x32, 0x67, 0x86, \ + 0x40, 0x42, 0xf2, 0x88, \ + 0x5d, 0x93, 0xf0, 0x94, \ + 0xf2, 0x84, 0x42, 0x22, \ + 0xe5, 0x00, 0x04, 0xca, \ + 0xc1, 0x7c, 0x02, 0x14, \ + 0xdc, 0x00, 0x40, 0x10, \ + 0x84, 0x80, 0x04, 0xca, \ + 0xcc, 0x80, 0x04, 0x20, \ + 0xc9, 0x08, 0x00, 0x14, \ + 0xc1, 0x7c, 0x02, 0x14, \ + 0x80, 0x00, 0x04, 0x5a, \ + 0xad, 0x88, 0x06, 0x14, \ + 0x20, 0x01, 0x06, 0x06, \ + 0x00, 0x04, 0xc0, 0x00, \ + 0x02, 0x14, 0x89, 0x88, \ + 0x40, 0x02, 0x89, 0x08, \ + 0xdd, 0x93, 0x99, 0x8c, \ + 0xe5, 0x00, 0x04, 0xca, \ + 0xc0, 0x00, 0x02, 0x06, \ + 0xc0, 0x00, 0x00, 0x06, \ + 0x84, 0x80, 0x04, 0xca, \ + 0xcc, 0x80, 0x04, 0x20, \ + 0xc9, 0x88, 0x00, 0x14, \ + 0xc0, 0x00, 0x02, 0x06, \ + 0x80, 0x00, 0x04, 0x5a, \ + 0xad, 0x88, 0x06, 0x14, \ + 0x20, 0x01, 0x06, 0x06, \ + 0x00, 0x04, 0xc0, 0x00, \ + 0x02, 0x14, 0x89, 0x88, \ + 0x40, 0x02, 0x89, 0x88, \ + 0xdd, 0x93, 0x99, 0x8c, \ + 0x80, 0x55, 0xa1, 0x1b, \ + 0x0a, 0x00, 0x83, 0x11, \ + 0x40, 0x06, 0xe5, 0x50, \ + 0x40, 0x4a, 0xfc, 0x12, \ + 0xd7, 0xf4, 0xe0, 0xd0, \ + 0xc1, 0xc3, 0x82, 0xb5, \ + 0xc1, 0xc3, 0xc1, 0xc3, \ + 0xf4, 0x04, 0x0b, 0x3f, \ + 0xf4, 0x04, 0x0b, 0x2f, \ + 0xf4, 0x04, 0x0b, 0x1f, \ + 0xf4, 0x04, 0x0b, 0x0f, \ + 0x0a, 0x50, 0x81, 0x35, \ + 0x40, 0x5a, 0xec, 0x40, \ + 0x44, 0x06, 0xe0, 0xbc, \ + 0x44, 0x6a, 0xbc, 0x22, \ + 0x40, 0x4a, 0xa0, 0x90, \ + 0xc0, 0x00, 0x20, 0x89, \ + 0xed, 0x88, 0x40, 0x1a, \ + 0x20, 0x01, 0x01, 0x98, \ + 0x0a, 0xff, 0xc0, 0x00, \ + 0xc1, 0xc3, 0xf4, 0x04, \ + 0xc1, 0xc3, 0xc1, 0xc3, \ + 0xf4, 0x04, 0x0b, 0xf5, \ + 0xf4, 0x04, 0x0b, 0xe5, \ + 0xf4, 0x04, 0x0b, 0xd5, \ + 0xf4, 0x04, 0x0b, 0xc5, \ + 0xf4, 0x04, 0x0b, 0xb5, \ + 0xf4, 0x04, 0x0b, 0xa5, \ + 0xf4, 0x04, 0x0b, 0x95, \ + 0xf4, 0x04, 0x0b, 0x85, \ + 0x0a, 0xff, 0x81, 0x55, \ + 0x0a, 0x50, 0xf4, 0x04, \ + 0xd7, 0xe4, 0xec, 0x80, \ + 0xd7, 0xb4, 0xd7, 0xd4, \ + 0xd7, 0x94, 0xd7, 0xa4, \ + 0xd7, 0x74, 0xd7, 0x84, \ + 0x82, 0x0b, 0x94, 0x64, \ + 0xfa, 0x00, 0x5c, 0x08, \ + 0xf8, 0x3c, 0x04, 0x4c, \ + 0xe4, 0x40, 0x04, 0x8a, \ + 0x21, 0x2b, 0x80, 0xa5, \ + 0x82, 0x15, 0x80, 0x3b, \ + 0x46, 0x12, 0x81, 0xf5, \ + 0x06, 0x4a, 0xf9, 0x80, \ + 0x82, 0xb5, 0xf8, 0x0c, \ + 0x04, 0x20, 0x80, 0x3b, \ + 0x04, 0x4a, 0xec, 0x04, \ + 0xc5, 0x48, 0xe4, 0x00, \ + 0x45, 0x88, 0xcd, 0x46, \ + 0x0c, 0x4c, 0x87, 0xfc, \ + 0xdd, 0x93, 0xf0, 0x3c, \ + 0x6c, 0x40, 0x0a, 0x20, \ + 0x49, 0x27, 0xe1, 0xc1, \ + 0x04, 0x20, 0x8a, 0xcf, \ + 0x06, 0xc0, 0x6c, 0x04, \ + 0x04, 0x4a, 0xe4, 0x40, \ + 0x48, 0x12, 0x64, 0x00, \ + 0x00, 0x33, 0xf0, 0x8c, \ + 0x02, 0x75, 0x80, 0x6b, \ + 0x02, 0x85, 0xf0, 0x10, \ + 0x02, 0x95, 0xf8, 0x0c, \ + 0x02, 0xa5, 0xf8, 0x08, \ + 0x4c, 0x75, 0xf8, 0x04, \ + 0x4c, 0x85, 0xf0, 0x10, \ + 0x4c, 0x95, 0xf8, 0x0c, \ + 0x4c, 0xa5, 0xf8, 0x08, \ + 0x44, 0x08, 0xf8, 0x04, \ + 0x44, 0x52, 0x87, 0xf8, \ + 0xe9, 0x2d, 0xf1, 0xb0, \ + 0x0a, 0x20, 0x86, 0x75, \ + 0x18, 0x24, 0x6c, 0x10, \ + 0x02, 0x54, 0xe1, 0x88, \ + 0x18, 0xc0, 0x61, 0x88, \ + 0x02, 0x11, 0xec, 0x04, \ + 0xe4, 0x00, 0x18, 0x4a, \ + 0x60, 0x88, 0x04, 0x14, \ + 0x42, 0x42, 0x86, 0x6b, \ + 0xd1, 0x74, 0xf0, 0x88, \ + 0x59, 0x88, 0xd9, 0x7e, \ + 0x04, 0x33, 0x87, 0xfc, \ + 0xf0, 0x0c, 0x44, 0x50, \ + 0xee, 0x15, 0xa1, 0x2b, \ + 0x21, 0x2b, 0xf7, 0xf5, \ + 0xec, 0x95, 0x88, 0x1b, \ + 0x00, 0x0c, 0xf7, 0xb5, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xf8, 0x04, 0x04, 0x0c, \ + 0x78, 0x0c, 0x00, 0x0a, \ + 0x82, 0x25, 0x80, 0x3b, \ + 0x94, 0x65, 0xa1, 0x2b, \ + 0x04, 0x20, 0x4d, 0x16, \ + 0x06, 0x4a, 0xec, 0x04, \ + 0xfe, 0x25, 0xf8, 0x0c, \ + 0x90, 0x55, 0xe9, 0x2d, \ + 0x7a, 0x80, 0x08, 0x14, \ + 0xe4, 0x40, 0x04, 0x8a, \ + 0xe0, 0xa0, 0x0a, 0x44, \ + 0x08, 0x54, 0xa8, 0x5f, \ + 0x06, 0xf5, 0xe0, 0xc0, \ + 0x0c, 0x24, 0xaa, 0x4f, \ + 0x0a, 0x20, 0x61, 0x90, \ + 0x61, 0xc1, 0xec, 0x40, \ + 0xec, 0x04, 0x0c, 0x60, \ + 0x0c, 0x4a, 0x0a, 0xcf, \ + 0x4e, 0x32, 0xe4, 0x00, \ + 0x86, 0x2b, 0x70, 0xb0, \ + 0xe4, 0x40, 0x06, 0x70, \ + 0xf0, 0x10, 0x44, 0x45, \ + 0xf8, 0x0c, 0x44, 0x45, \ + 0xf8, 0x08, 0x44, 0x45, \ + 0xf8, 0x04, 0x44, 0x45, \ + 0x87, 0xfc, 0x4c, 0x08, \ + 0xf1, 0xb0, 0x44, 0x52, \ + 0x86, 0x55, 0xe9, 0x2d, \ + 0xf9, 0xc1, 0x07, 0x51, \ + 0x4a, 0x52, 0xa4, 0x53, \ + 0x04, 0x53, 0xfa, 0xb0, \ + 0xec, 0x10, 0x04, 0x20, \ + 0x0c, 0x24, 0x08, 0x51, \ + 0xc5, 0x57, 0xe1, 0x88, \ + 0x6c, 0x04, 0x18, 0x50, \ + 0xe4, 0x04, 0x0a, 0x60, \ + 0x64, 0x00, 0x18, 0x4a, \ + 0xd9, 0x4e, 0x86, 0x6b, \ + 0x87, 0xfc, 0x59, 0xc8, \ + 0x86, 0x29, 0x44, 0x57, \ + 0xa1, 0x2b, 0x8a, 0x33, \ + 0x04, 0x20, 0x82, 0x05, \ + 0x04, 0x4a, 0xec, 0x04, \ + 0xcd, 0x16, 0xe4, 0x00, \ + 0x87, 0xfc, 0x45, 0xc8, \ + 0xf0, 0x04, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x04, 0xfc, 0x80, 0x5b, \ + 0x40, 0x06, 0xe2, 0xc0, \ + 0xd5, 0x6e, 0xfc, 0x12, \ + 0xd5, 0x8e, 0xd5, 0x7e, \ + 0x40, 0x5a, 0xd5, 0x9e, \ + 0x42, 0x06, 0xe0, 0xbc, \ + 0xd5, 0xae, 0xbc, 0x22, \ + 0xd5, 0xde, 0xd5, 0xbe, \ + 0x42, 0x6a, 0xd5, 0xee, \ + 0x40, 0x4a, 0xa0, 0x90, \ + 0xc0, 0x00, 0x20, 0x89, \ + 0xed, 0x88, 0x40, 0x1a, \ + 0x20, 0x01, 0x01, 0x78, \ + 0xc1, 0xc3, 0xc0, 0x00, \ + 0xc1, 0xc3, 0xc1, 0xc3, \ + 0xf0, 0x04, 0x4b, 0x85, \ + 0xf0, 0x04, 0x4b, 0x95, \ + 0xf0, 0x04, 0x4b, 0xa5, \ + 0xf0, 0x04, 0x4b, 0xb5, \ + 0xf0, 0x04, 0x4b, 0xc5, \ + 0xf0, 0x04, 0x4b, 0xd5, \ + 0xf0, 0x04, 0x4b, 0xe5, \ + 0xf0, 0x04, 0x4b, 0xf5, \ + 0xc2, 0x1f, 0x81, 0xa5, \ + 0xd5, 0x1e, 0xd5, 0x1e, \ + 0xd5, 0x1e, 0xd5, 0x1e, \ + 0xd5, 0x1e, 0xd5, 0x1e, \ + 0xd5, 0x1e, 0xd5, 0x1e, \ + 0xe0, 0xd0, 0x40, 0x4a, \ + 0x82, 0xb5, 0xd5, 0x2e, \ + 0xc1, 0xc3, 0xc1, 0xc3, \ + 0x4b, 0x0f, 0xc1, 0xc3, \ + 0x4b, 0x1f, 0xf0, 0x04, \ + 0x4b, 0x2f, 0xf0, 0x04, \ + 0x4b, 0x3f, 0xf0, 0x04, \ + 0x81, 0x65, 0xf0, 0x04, \ + 0xd5, 0x1e, 0xc2, 0x1f, \ + 0xd5, 0x1e, 0xd5, 0x1e, \ + 0x94, 0xfe, 0xd5, 0x1e, \ + 0xdd, 0x93, 0x81, 0x01, \ + 0xe0, 0x04, 0x19, 0x86, \ + 0xa3, 0xcd, 0xc1, 0xc3, \ + 0x18, 0x4a, 0x82, 0xa5, \ + 0x02, 0x46, 0xe0, 0x00, \ + 0x02, 0x15, 0xe1, 0x4c, \ + 0x42, 0x05, 0x80, 0x00, \ + 0x41, 0xfe, 0x80, 0x00, \ + 0xbf, 0xff, 0x1f, 0xfd, \ + 0x02, 0x46, 0xdd, 0x93, \ + 0x84, 0x14, 0xe1, 0x4c, \ + 0x41, 0xfe, 0x84, 0xce, \ + 0xff, 0xff, 0x7f, 0xfd, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x00, 0x06, 0xa1, 0x1b, \ + 0x5c, 0x08, 0xd4, 0x62, \ + 0x00, 0x06, 0xda, 0x00, \ + 0xdd, 0x93, 0xf4, 0x22, \ + 0x1e, 0xf0, 0x02, 0x5b, \ + 0x0b, 0x0a, 0xec, 0x40, \ + 0x01, 0x7e, 0xe4, 0x7c, \ + 0x1e, 0xf0, 0x5f, 0xf8, \ + 0x5c, 0x08, 0xc4, 0x40, \ + 0x42, 0x56, 0xda, 0x00, \ + 0x0a, 0x0a, 0x70, 0x02, \ + 0x82, 0xa5, 0xe0, 0x7c, \ + 0x7c, 0x2e, 0xfe, 0x1e, \ + 0xfe, 0x14, 0xc1, 0x21, \ + 0xbe, 0x1e, 0x84, 0x1f, \ + 0xfc, 0x24, 0xbe, 0x14, \ + 0xa2, 0x2f, 0xbe, 0x29, \ + 0xfe, 0x14, 0xfe, 0x2e, \ + 0xa0, 0xf1, 0x21, 0x3b, \ + 0xdc, 0xb4, 0x02, 0x14, \ + 0x1c, 0x34, 0x02, 0x14, \ + 0xe0, 0x7c, 0x0a, 0x0a, \ + 0x04, 0x14, 0x82, 0x2b, \ + 0xa1, 0x4b, 0x5c, 0x3c, \ + 0x5c, 0x00, 0x42, 0x20, \ + 0x00, 0x56, 0x8a, 0x2b, \ + 0x42, 0x0e, 0x62, 0x06, \ + 0xc0, 0x10, 0x60, 0x00, \ + 0xc1, 0xc3, 0x81, 0xc5, \ + 0xe2, 0xc0, 0x02, 0x1c, \ + 0xbe, 0x09, 0x41, 0x21, \ + 0xa0, 0x1f, 0x84, 0x1f, \ + 0xe3, 0x40, 0x02, 0x1c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xe0, 0x7c, 0x04, 0x0a, \ + 0xc0, 0x88, 0x04, 0x24, \ + 0xd0, 0x88, 0x42, 0x12, \ + 0x00, 0x01, 0x02, 0x4e, \ + 0xa2, 0x03, 0x80, 0x10, \ + 0xe1, 0x04, 0x04, 0x04, \ + 0xf0, 0x80, 0x00, 0x24, \ + 0x67, 0xfd, 0x01, 0x0a, \ + 0x01, 0x8e, 0x4f, 0xff, \ + 0x5c, 0x08, 0xff, 0xf8, \ + 0x00, 0x24, 0xda, 0x00, \ + 0xc8, 0x27, 0x60, 0xa0, \ + 0x60, 0x01, 0x06, 0x06, \ + 0x44, 0x20, 0x47, 0xff, \ + 0xc0, 0x3f, 0x73, 0xc1, \ + 0x20, 0x2f, 0x86, 0x0f, \ + 0x60, 0x01, 0x00, 0x86, \ + 0xa0, 0x2f, 0xf8, 0x00, \ + 0xff, 0x21, 0x04, 0x2e, \ + 0xfc, 0x12, 0x40, 0x06, \ + 0x81, 0x01, 0x04, 0x0d, \ + 0xda, 0x00, 0x5c, 0x08, \ + 0xfc, 0x1a, 0x44, 0x06, \ + 0x82, 0x35, 0xa1, 0x2d, \ + 0x7c, 0x0a, 0x44, 0x06, \ + 0x60, 0x00, 0x06, 0x06, \ + 0x87, 0x2d, 0xc0, 0x20, \ + 0xda, 0x00, 0x5c, 0x08, \ + 0xf3, 0x72, 0x00, 0x16, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x04, 0x04, 0x7c, \ + 0x74, 0x31, 0x47, 0xce, \ + 0x08, 0x8e, 0xc0, 0x10, \ + 0xc0, 0x10, 0x74, 0x35, \ + 0x88, 0x25, 0x89, 0x3d, \ + 0xcd, 0x14, 0xcd, 0x04, \ + 0x05, 0x0a, 0xcd, 0x24, \ + 0x4a, 0x00, 0xe0, 0x80, \ + 0x84, 0x25, 0xf0, 0x0c, \ + 0xe4, 0x00, 0x0a, 0x3a, \ + 0x60, 0x01, 0x00, 0x98, \ + 0x04, 0x20, 0x80, 0x00, \ + 0xc1, 0x16, 0xec, 0x04, \ + 0xf8, 0x0c, 0x00, 0x4a, \ + 0x04, 0x20, 0xfe, 0x65, \ + 0xe9, 0x2d, 0xec, 0x10, \ + 0xfe, 0x85, 0xc1, 0x1e, \ + 0xe0, 0x04, 0x04, 0xca, \ + 0x80, 0x00, 0x45, 0x48, \ + 0x45, 0xc8, 0xc1, 0x16, \ + 0xfb, 0xf5, 0x87, 0xfc, \ + 0xe1, 0x4c, 0x00, 0x46, \ + 0x61, 0x01, 0x03, 0x86, \ + 0x80, 0x1e, 0xc0, 0x00, \ + 0x81, 0x11, 0x9b, 0x01, \ + 0x81, 0x31, 0x81, 0x21, \ + 0xef, 0xfc, 0x73, 0x28, \ + 0x8c, 0x45, 0xa1, 0x0b, \ + 0x74, 0x31, 0x46, 0x4e, \ + 0x49, 0xce, 0xc0, 0x10, \ + 0xc0, 0x10, 0x74, 0x31, \ + 0x8a, 0x45, 0x89, 0x3d, \ + 0xcd, 0x14, 0xcd, 0x04, \ + 0x05, 0x0a, 0xcd, 0x24, \ + 0x4a, 0x00, 0xe0, 0x80, \ + 0x86, 0x25, 0xf0, 0x0c, \ + 0xf0, 0x0c, 0x4c, 0x10, \ + 0xe4, 0x18, 0x4a, 0x5a, \ + 0xe4, 0x00, 0x0a, 0x3a, \ + 0xa0, 0x00, 0x01, 0x28, \ + 0x60, 0x01, 0x00, 0xa8, \ + 0xc5, 0x50, 0x80, 0x00, \ + 0xec, 0x04, 0x04, 0x20, \ + 0x00, 0x4a, 0xc1, 0x56, \ + 0xfe, 0x45, 0xf8, 0x0c, \ + 0x04, 0x20, 0xc5, 0x54, \ + 0xe9, 0x2d, 0xec, 0x10, \ + 0xfe, 0x65, 0xc1, 0x5e, \ + 0xe0, 0x04, 0x04, 0xca, \ + 0x80, 0x00, 0x45, 0x88, \ + 0xc1, 0x56, 0xc5, 0x50, \ + 0x87, 0xfc, 0x45, 0x88, \ + 0x07, 0x06, 0xfb, 0x65, \ + 0x08, 0xf0, 0xe1, 0x48, \ + 0x8c, 0x4e, 0xe4, 0x30, \ + 0xe1, 0x54, 0x0c, 0xc6, \ + 0xe1, 0x54, 0x0e, 0xc6, \ + 0x82, 0x45, 0x8f, 0x6d, \ + 0x02, 0x3a, 0xd9, 0x14, \ + 0x00, 0x68, 0xe4, 0x00, \ + 0x80, 0x00, 0x60, 0x01, \ + 0xfc, 0x38, 0x42, 0x08, \ + 0x81, 0x61, 0xff, 0x65, \ + 0x00, 0xc6, 0xe1, 0x6b, \ + 0x4c, 0x08, 0xe1, 0x54, \ + 0x8b, 0x04, 0xdc, 0x38, \ + 0xfe, 0x24, 0x89, 0x14, \ + 0xe8, 0x6c, 0x4d, 0xa8, \ + 0x00, 0x0c, 0x81, 0x05, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x86, 0x4b, 0x88, 0x8b, \ + 0x80, 0x6b, 0x84, 0x7b, \ + 0xfc, 0x9e, 0x81, 0x91, \ + 0xe5, 0x06, 0x40, 0x5a, \ + 0xe5, 0x86, 0x42, 0x5a, \ + 0x88, 0x2b, 0x81, 0x31, \ + 0xef, 0xfc, 0x4f, 0xc8, \ + 0x67, 0xd1, 0x00, 0x00, \ + 0x42, 0x12, 0xc0, 0x01, \ + 0x44, 0x86, 0xf2, 0xa4, \ + 0x81, 0x31, 0xe0, 0x3c, \ + 0xef, 0xfc, 0x46, 0x48, \ + 0x64, 0x01, 0x00, 0x00, \ + 0x42, 0x12, 0xc0, 0x04, \ + 0x14, 0x14, 0xf2, 0xa4, \ + 0xd7, 0x07, 0xe0, 0xd4, \ + 0xff, 0x01, 0xa0, 0xaf, \ + 0x00, 0x14, 0x81, 0xab, \ + 0x80, 0x55, 0xe1, 0xac, \ + 0xa1, 0x0b, 0xff, 0xa1, \ + 0x99, 0xb4, 0x9b, 0x94, \ + 0x97, 0x04, 0x95, 0x14, \ + 0xff, 0xa1, 0x80, 0x45, \ + 0xa4, 0x24, 0x80, 0x04, \ + 0x0a, 0xf0, 0xc4, 0x32, \ + 0x90, 0x1b, 0xe4, 0x20, \ + 0x44, 0x38, 0x8e, 0x4b, \ + 0x80, 0x7b, 0xe8, 0x00, \ + 0xae, 0x24, 0xfc, 0x04, \ + 0x12, 0x49, 0xec, 0x34, \ + 0xa0, 0x1b, 0xe0, 0x18, \ + 0xfb, 0x90, 0x40, 0x82, \ + 0x92, 0x0b, 0xbe, 0x0e, \ + 0x74, 0x18, 0x8c, 0x4b, \ + 0xa1, 0x0b, 0xe8, 0x2c, \ + 0x80, 0x7b, 0x80, 0x45, \ + 0xbe, 0x04, 0xbc, 0x7e, \ + 0x8a, 0x05, 0xa1, 0x0b, \ + 0xe0, 0x1c, 0x12, 0x89, \ + 0x0e, 0x06, 0x9d, 0x94, \ + 0xb1, 0x7f, 0xe0, 0x08, \ + 0x81, 0xb1, 0x90, 0x81, \ + 0x42, 0xaa, 0xd9, 0x0a, \ + 0x44, 0xaa, 0xe5, 0x02, \ + 0x40, 0x12, 0xe5, 0x82, \ + 0x81, 0x11, 0xf0, 0x9c, \ + 0xf2, 0x84, 0x42, 0x22, \ + 0x50, 0x18, 0x90, 0x2b, \ + 0x45, 0xfe, 0xef, 0xfc, \ + 0xc0, 0x3f, 0x7f, 0xfd, \ + 0x80, 0x95, 0xc5, 0x0d, \ + 0x7f, 0xfd, 0x41, 0xfe, \ + 0xa1, 0x1b, 0xc0, 0x3f, \ + 0x41, 0xfe, 0x80, 0x85, \ + 0xc0, 0x3f, 0x7f, 0xfd, \ + 0xbe, 0x14, 0x82, 0xb1, \ + 0xe5, 0x0e, 0xc3, 0xbd, \ + 0xbc, 0x04, 0xf8, 0x55, \ + 0x0c, 0x0c, 0x98, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xff, 0xc1, 0xa1, 0x1b, \ + 0xd6, 0x17, 0x80, 0xa5, \ + 0xe1, 0x06, 0x44, 0x2a, \ + 0xa1, 0x4b, 0x81, 0xc1, \ + 0xff, 0x11, 0x82, 0x85, \ + 0xe0, 0x37, 0x82, 0x3d, \ + 0xe1, 0x04, 0x02, 0x44, \ + 0xa2, 0x1b, 0x80, 0x55, \ + 0x42, 0x1a, 0x86, 0x13, \ + 0x81, 0x35, 0xe0, 0x92, \ + 0xff, 0xc1, 0x81, 0x11, \ + 0x82, 0x23, 0xa0, 0x23, \ + 0x98, 0x0b, 0x94, 0x2e, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x82, 0x2b, 0x81, 0x01, \ + 0x44, 0x05, 0x86, 0x0e, \ + 0x44, 0x05, 0xf0, 0x80, \ + 0x44, 0x05, 0xf8, 0x74, \ + 0x44, 0x05, 0xf8, 0x78, \ + 0x44, 0x05, 0xf8, 0x6c, \ + 0x44, 0x05, 0xf8, 0x70, \ + 0x44, 0x05, 0xf8, 0x68, \ + 0x44, 0x05, 0xf8, 0x64, \ + 0x44, 0x05, 0xf8, 0x60, \ + 0x44, 0x05, 0xf8, 0x5c, \ + 0x44, 0x05, 0xf8, 0x58, \ + 0x91, 0x31, 0xf8, 0x54, \ + 0xe4, 0xc0, 0x08, 0x10, \ + 0xec, 0x04, 0x06, 0x30, \ + 0x92, 0x0e, 0xa1, 0x3b, \ + 0xf0, 0x08, 0x48, 0x05, \ + 0xf8, 0x20, 0x44, 0x03, \ + 0xfc, 0xf5, 0xc9, 0x0c, \ + 0xe0, 0x04, 0x00, 0x06, \ + 0xe6, 0x40, 0x04, 0x10, \ + 0x66, 0x41, 0x06, 0x10, \ + 0x08, 0x10, 0xc0, 0x04, \ + 0xc0, 0x03, 0x66, 0x41, \ + 0x00, 0x00, 0x81, 0x51, \ + 0x8a, 0x5e, 0xec, 0x04, \ + 0x44, 0x55, 0xa1, 0x0b, \ + 0x48, 0x55, 0xf0, 0x08, \ + 0xd1, 0x5e, 0xf9, 0x00, \ + 0xfc, 0xd5, 0xcd, 0x56, \ + 0xe0, 0x0c, 0x41, 0xf6, \ + 0xe4, 0xd0, 0x42, 0x05, \ + 0x42, 0x05, 0x81, 0x01, \ + 0x05, 0x06, 0xe4, 0xd4, \ + 0x06, 0x10, 0xe0, 0x08, \ + 0xc0, 0x04, 0x67, 0x61, \ + 0x65, 0x61, 0x02, 0x10, \ + 0x04, 0x20, 0xc0, 0x09, \ + 0xa1, 0x2b, 0xec, 0x04, \ + 0x46, 0x05, 0x8e, 0x0e, \ + 0xc5, 0x0c, 0xf0, 0x08, \ + 0xdd, 0x93, 0xfe, 0x35, \ + 0x6f, 0xd1, 0x00, 0x19, \ + 0xa1, 0x1b, 0xc0, 0x05, \ + 0x00, 0x09, 0x82, 0x45, \ + 0xc0, 0x06, 0x60, 0x2d, \ + 0xf3, 0xf0, 0x40, 0x00, \ + 0x00, 0x0c, 0xa9, 0x0b, \ + 0xdd, 0x93, 0xe2, 0x00, \ + 0xdd, 0x93, 0x81, 0x01, \ + 0xf0, 0x80, 0x44, 0x12, \ + 0xec, 0x70, 0x04, 0x29, \ + 0x84, 0x05, 0xa1, 0x2b, \ + 0xe0, 0x3c, 0x02, 0x29, \ + 0xe6, 0x82, 0x40, 0x2a, \ + 0x02, 0x09, 0x80, 0x13, \ + 0xc0, 0x05, 0x6b, 0x71, \ + 0xec, 0x14, 0x00, 0x00, \ + 0xe4, 0x08, 0x00, 0xca, \ + 0xe2, 0x00, 0x00, 0x0c, \ + 0x81, 0x01, 0xdd, 0x93, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x0c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0xe0, 0x3c, 0x02, 0x39, \ + 0xe0, 0x88, 0x08, 0x04, \ + 0xe6, 0x40, 0x04, 0x20, \ + 0xf0, 0x90, 0x4a, 0x12, \ + 0x0a, 0x55, 0x84, 0x43, \ + 0x08, 0xc5, 0xe1, 0x1c, \ + 0x8a, 0xc3, 0xe2, 0x00, \ + 0xe6, 0x82, 0x46, 0x3a, \ + 0xe2, 0x00, 0x48, 0xc5, \ + 0xe0, 0x88, 0x0a, 0x34, \ + 0x67, 0x71, 0x02, 0x10, \ + 0x02, 0x55, 0xc0, 0x03, \ + 0x18, 0x04, 0xfc, 0x14, \ + 0x4c, 0x22, 0xe0, 0x8c, \ + 0x04, 0xc5, 0xf0, 0xb0, \ + 0x9a, 0x74, 0xfc, 0x30, \ + 0x8a, 0xc3, 0x81, 0x81, \ + 0x4a, 0x72, 0xc2, 0x37, \ + 0x86, 0x13, 0xf2, 0xa0, \ + 0x6c, 0x01, 0x02, 0x13, \ + 0x08, 0x35, 0xc0, 0x01, \ + 0x9a, 0x5e, 0xe3, 0x00, \ + 0x82, 0x33, 0x98, 0xce, \ + 0xe3, 0x00, 0x48, 0x35, \ + 0x04, 0x09, 0x80, 0x23, \ + 0xc0, 0x01, 0x60, 0x01, \ + 0x44, 0x01, 0x82, 0x01, \ + 0xc0, 0x01, 0x60, 0x01, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf0, 0x0c, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x88, 0x6b, 0x8a, 0x7b, \ + 0xf9, 0x8c, 0x48, 0x22, \ + 0x84, 0x65, 0xa1, 0x4b, \ + 0xe1, 0x84, 0x08, 0x24, \ + 0xe0, 0xfc, 0x0a, 0x34, \ + 0x0a, 0x34, 0xaa, 0x4f, \ + 0x88, 0x03, 0xe1, 0x84, \ + 0xf2, 0x84, 0x42, 0x52, \ + 0xef, 0xf8, 0x71, 0x68, \ + 0x80, 0xe5, 0x8f, 0x1b, \ + 0xe4, 0x1c, 0x43, 0x4a, \ + 0x81, 0x75, 0x80, 0xf5, \ + 0x81, 0xb5, 0xff, 0x01, \ + 0xe4, 0x18, 0x41, 0x4a, \ + 0x80, 0x6b, 0x80, 0x45, \ + 0x80, 0x45, 0x89, 0x04, \ + 0x82, 0x7e, 0x82, 0x7b, \ + 0x81, 0x01, 0x80, 0x6e, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x04, \ + 0x82, 0x65, 0xa1, 0x1b, \ + 0xe1, 0x84, 0x08, 0x14, \ + 0x40, 0x4a, 0x80, 0x43, \ + 0x40, 0x22, 0xe0, 0x06, \ + 0x8c, 0x0e, 0xfb, 0x80, \ + 0xdd, 0x93, 0x81, 0x01, \ + 0xdd, 0x93, 0xff, 0x01, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x84, 0x35, 0xa3, 0x1d, \ + 0x8a, 0x0d, 0xff, 0x51, \ + 0xe1, 0x84, 0x0a, 0x14, \ + 0xaa, 0x5b, 0x80, 0x55, \ + 0x40, 0x5a, 0x80, 0x53, \ + 0x40, 0x22, 0xe0, 0x86, \ + 0x40, 0x32, 0xfa, 0x00, \ + 0x90, 0x0e, 0xfb, 0x00, \ + 0xdd, 0x93, 0x81, 0x01, \ + 0xdd, 0x93, 0xff, 0x01, \ + 0xf8, 0x00, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x67, 0xfd, 0x00, 0x25, \ + 0x84, 0x34, 0xc0, 0x01, \ + 0x81, 0x51, 0x86, 0x44, \ + 0x00, 0x21, 0x84, 0x33, \ + 0xc0, 0x05, 0x6f, 0xc9, \ + 0xe0, 0x18, 0x02, 0xc5, \ + 0xf2, 0x94, 0x48, 0x42, \ + 0x6f, 0xcd, 0x00, 0x01, \ + 0x02, 0x55, 0xc0, 0x05, \ + 0x02, 0x6b, 0xe0, 0x1c, \ + 0x86, 0x4e, 0xe0, 0x50, \ + 0x84, 0x3e, 0x84, 0xc3, \ + 0xe0, 0x18, 0x42, 0xc5, \ + 0x82, 0x61, 0x80, 0x53, \ + 0xe0, 0x1c, 0x42, 0x55, \ + 0xe0, 0x50, 0x42, 0x63, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf0, 0x00, \ + 0xe0, 0x3c, 0x02, 0x39, \ + 0xe6, 0x82, 0x46, 0x3a, \ + 0x82, 0x33, 0xc4, 0x37, \ + 0xe7, 0xdc, 0x06, 0x35, \ + 0xca, 0x54, 0xc8, 0x44, \ + 0x86, 0x43, 0x81, 0xc1, \ + 0xf2, 0xb0, 0x46, 0x52, \ + 0xc8, 0x4e, 0xca, 0x3e, \ + 0x02, 0x01, 0x80, 0x13, \ + 0x04, 0x35, 0xea, 0x70, \ + 0x80, 0x33, 0xe0, 0x20, \ + 0xe0, 0x20, 0x44, 0x35, \ + 0xeb, 0x70, 0x02, 0x01, \ + 0xe0, 0x24, 0x04, 0x15, \ + 0xe0, 0x54, 0x04, 0x3b, \ + 0x82, 0x31, 0x80, 0x13, \ + 0xe0, 0x24, 0x44, 0x15, \ + 0xe0, 0x54, 0x44, 0x33, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe0, 0x3c, 0x02, 0x39, \ + 0xe6, 0x82, 0x40, 0x3a, \ + 0x80, 0x13, 0xc4, 0x07, \ + 0xe7, 0xdc, 0x02, 0x05, \ + 0xe0, 0x10, 0x04, 0x15, \ + 0xe0, 0x14, 0x04, 0x35, \ + 0xe0, 0x58, 0x04, 0x4b, \ + 0x81, 0x01, 0x80, 0x13, \ + 0xf2, 0x80, 0x40, 0x32, \ + 0xe0, 0x14, 0x44, 0x05, \ + 0x44, 0x15, 0x82, 0x41, \ + 0x44, 0x43, 0xe0, 0x10, \ + 0xdd, 0x93, 0xe0, 0x58, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x00, 0x29, 0x84, 0x5b, \ + 0x0a, 0x39, 0xe0, 0x58, \ + 0xc0, 0x01, 0x63, 0x41, \ + 0x02, 0x39, 0xc7, 0x2d, \ + 0xc0, 0x01, 0x60, 0x51, \ + 0x63, 0x49, 0x4a, 0x21, \ + 0x80, 0x85, 0xc0, 0x01, \ + 0x63, 0x41, 0x4a, 0x21, \ + 0x0a, 0x49, 0xc0, 0x01, \ + 0xc0, 0x01, 0x63, 0x45, \ + 0x80, 0x85, 0xc8, 0x2d, \ + 0x63, 0x45, 0x4a, 0x21, \ + 0xc2, 0x37, 0xc0, 0x01, \ + 0x02, 0xc3, 0x86, 0x13, \ + 0x02, 0x13, 0xe0, 0x30, \ + 0x00, 0x35, 0xe0, 0xc0, \ + 0x08, 0x00, 0xe0, 0x20, \ + 0x0a, 0x50, 0xe4, 0xa0, \ + 0xc0, 0x04, 0x67, 0x61, \ + 0x05, 0xd8, 0x98, 0x0b, \ + 0x00, 0x0c, 0xe0, 0x0c, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xd0, 0x1e, 0xf0, \ + 0xad, 0x04, 0xfe, 0x1e, \ + 0xa9, 0x74, 0xbe, 0x0e, \ + 0xfc, 0x0e, 0xab, 0x04, \ + 0x04, 0x09, 0x81, 0x11, \ + 0x97, 0x1e, 0xe0, 0x38, \ + 0x93, 0x1e, 0x95, 0x1e, \ + 0x10, 0x06, 0x91, 0x1e, \ + 0x8d, 0x7e, 0xe0, 0x04, \ + 0x8a, 0xcb, 0x89, 0x5e, \ + 0x10, 0x80, 0x88, 0x6b, \ + 0xa1, 0x8b, 0xec, 0x04, \ + 0xf1, 0x1c, 0xd9, 0x1e, \ + 0xfe, 0x55, 0xdd, 0x1c, \ + 0x83, 0x61, 0x81, 0xc1, \ + 0x84, 0xce, 0xfc, 0x14, \ + 0xc6, 0xce, 0x86, 0x6e, \ + 0x02, 0x06, 0xc4, 0xce, \ + 0xa1, 0x0b, 0xe0, 0x08, \ + 0x94, 0x1e, 0xbe, 0x54, \ + 0xd6, 0xce, 0x96, 0x6e, \ + 0x98, 0x25, 0xd4, 0xce, \ + 0x8b, 0x0e, 0xc2, 0x07, \ + 0x66, 0x41, 0x16, 0x30, \ + 0x0c, 0x30, 0xc0, 0x04, \ + 0x14, 0x20, 0xe6, 0x40, \ + 0xc0, 0x03, 0x65, 0x71, \ + 0x66, 0x41, 0x10, 0x30, \ + 0x81, 0x71, 0xc0, 0x03, \ + 0xa1, 0x9b, 0xac, 0x98, \ + 0x8f, 0x4e, 0x8e, 0x65, \ + 0x00, 0x09, 0xfe, 0x04, \ + 0xc1, 0x9d, 0xe0, 0x10, \ + 0xbe, 0x14, 0x82, 0x65, \ + 0xe0, 0x08, 0x00, 0x46, \ + 0x83, 0x01, 0x84, 0x0e, \ + 0xa8, 0x08, 0x86, 0x0e, \ + 0x81, 0x01, 0xc6, 0x0e, \ + 0x9a, 0x14, 0xc4, 0x0e, \ + 0x04, 0xf0, 0x98, 0x04, \ + 0xbc, 0x2e, 0xe4, 0xa0, \ + 0x49, 0xfe, 0x81, 0x31, \ + 0xcf, 0xff, 0x7f, 0xfd, \ + 0x92, 0x2b, 0x81, 0x51, \ + 0xef, 0xfc, 0x6b, 0xe8, \ + 0x8f, 0x14, 0x95, 0x04, \ + 0x10, 0x05, 0x84, 0x0e, \ + 0x06, 0xf0, 0xf9, 0x00, \ + 0x45, 0xf6, 0xe4, 0x90, \ + 0x92, 0x1b, 0xef, 0xfc, \ + 0xef, 0xfc, 0x6d, 0xe8, \ + 0x89, 0x14, 0x93, 0x04, \ + 0x84, 0x0c, 0x8e, 0x13, \ + 0x08, 0xf0, 0xa0, 0x04, \ + 0x04, 0x0e, 0xe4, 0x80, \ + 0x47, 0xf6, 0xf8, 0x00, \ + 0x92, 0x1b, 0xe7, 0xfc, \ + 0xef, 0xfc, 0x6e, 0x08, \ + 0x91, 0x04, 0x8f, 0x44, \ + 0xfc, 0x14, 0x81, 0xa5, \ + 0x84, 0x2e, 0x83, 0x21, \ + 0xa8, 0x08, 0x86, 0x2e, \ + 0x81, 0x01, 0xc6, 0x0e, \ + 0x90, 0x2e, 0xc4, 0x0e, \ + 0x8e, 0x13, 0x8d, 0x14, \ + 0x84, 0x71, 0x84, 0x0c, \ + 0x8b, 0x04, 0x82, 0xb1, \ + 0x82, 0xa1, 0x8f, 0x0b, \ + 0x90, 0x61, 0x88, 0x41, \ + 0xea, 0xf5, 0x88, 0x81, \ + 0x0c, 0x0c, 0xe8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0x82, 0x7b, 0x84, 0x6b, \ + 0xfe, 0x0e, 0x81, 0x01, \ + 0x0e, 0x2b, 0xfc, 0x0e, \ + 0x98, 0x0e, 0xe0, 0x50, \ + 0x0e, 0x3b, 0x9a, 0x0e, \ + 0x0e, 0x4b, 0xe0, 0x54, \ + 0xd8, 0x0e, 0xe0, 0x58, \ + 0xe0, 0x18, 0x4c, 0x23, \ + 0x9c, 0x04, 0x9e, 0x14, \ + 0xe0, 0x1c, 0x4c, 0x33, \ + 0xe0, 0x20, 0x4c, 0x43, \ + 0xe4, 0x20, 0x10, 0xf0, \ + 0x81, 0x31, 0xbc, 0x8e, \ + 0x7f, 0xfd, 0x49, 0xfe, \ + 0x81, 0x51, 0xc0, 0x01, \ + 0xef, 0xfc, 0x66, 0x28, \ + 0x80, 0x75, 0xa1, 0x0b, \ + 0x98, 0x0e, 0xfc, 0x04, \ + 0xdc, 0x04, 0xde, 0x14, \ + 0xe0, 0x54, 0x0e, 0x2b, \ + 0x81, 0x31, 0xbc, 0x8e, \ + 0x7f, 0xfd, 0x49, 0xfe, \ + 0x81, 0x51, 0xc0, 0x01, \ + 0xef, 0xfc, 0x65, 0x28, \ + 0x80, 0x75, 0xa1, 0x0b, \ + 0x9a, 0x0e, 0xfc, 0x04, \ + 0xe0, 0x14, 0x0e, 0x15, \ + 0xe0, 0x10, 0x0e, 0x05, \ + 0xe0, 0x58, 0x0e, 0x2b, \ + 0x81, 0x31, 0xbc, 0x8e, \ + 0x7f, 0xfd, 0x49, 0xfe, \ + 0x81, 0x51, 0xc0, 0x01, \ + 0xef, 0xfc, 0x64, 0x08, \ + 0x80, 0x75, 0xa1, 0x0b, \ + 0xd8, 0x0e, 0xfc, 0x04, \ + 0x0c, 0x0c, 0xa0, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x0c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x82, 0x7b, 0x84, 0x6b, \ + 0xbc, 0x2e, 0x81, 0x21, \ + 0xe0, 0x18, 0x0e, 0x05, \ + 0xe0, 0x50, 0x0e, 0x1b, \ + 0x9a, 0x26, 0x98, 0x26, \ + 0xda, 0x26, 0xd8, 0x26, \ + 0x04, 0x0e, 0x9e, 0x4b, \ + 0x47, 0xf6, 0xff, 0xf8, \ + 0x65, 0xb8, 0xe0, 0x04, \ + 0xa1, 0x0b, 0xef, 0xfc, \ + 0xbc, 0x04, 0x80, 0x75, \ + 0x0e, 0x05, 0x98, 0x06, \ + 0x0e, 0x1b, 0xe0, 0x1c, \ + 0x9e, 0x4b, 0xe0, 0x50, \ + 0xff, 0xf8, 0x04, 0x0e, \ + 0xe0, 0x04, 0x47, 0xf6, \ + 0xef, 0xfc, 0x64, 0xc8, \ + 0x80, 0x75, 0xa1, 0x0b, \ + 0x9a, 0x06, 0xbc, 0x04, \ + 0xe0, 0x20, 0x0e, 0x05, \ + 0xe0, 0x54, 0x0e, 0x1b, \ + 0x04, 0x0e, 0x9e, 0x4b, \ + 0x47, 0xf6, 0xff, 0xf8, \ + 0x63, 0xd8, 0xe0, 0x04, \ + 0xa1, 0x0b, 0xef, 0xfc, \ + 0xbc, 0x04, 0x80, 0x75, \ + 0x0e, 0x05, 0xd8, 0x06, \ + 0x0e, 0x1b, 0xe0, 0x24, \ + 0x9e, 0x4b, 0xe0, 0x54, \ + 0xff, 0xf8, 0x04, 0x0e, \ + 0xe0, 0x04, 0x47, 0xf6, \ + 0xef, 0xfc, 0x62, 0xe8, \ + 0x80, 0x75, 0xa1, 0x0b, \ + 0xda, 0x06, 0xbc, 0x04, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x04, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x82, 0x7b, 0x8a, 0x6b, \ + 0xe0, 0x38, 0x04, 0x89, \ + 0x81, 0x01, 0xa1, 0x8b, \ + 0x88, 0x25, 0xbe, 0x0e, \ + 0x66, 0x41, 0x12, 0x30, \ + 0x14, 0x40, 0xc0, 0x04, \ + 0xa4, 0x08, 0xe4, 0x30, \ + 0x84, 0x45, 0xa1, 0x0b, \ + 0x9c, 0x22, 0x98, 0x02, \ + 0xa0, 0x23, 0xa8, 0x1a, \ + 0x00, 0x24, 0xbe, 0x2e, \ + 0x08, 0xf0, 0xe0, 0xb0, \ + 0x04, 0x0e, 0xe4, 0x10, \ + 0x47, 0xf6, 0xf8, 0x00, \ + 0x60, 0x38, 0xe7, 0xfc, \ + 0xbe, 0x04, 0xef, 0xfc, \ + 0x10, 0x80, 0x98, 0x0c, \ + 0xa1, 0x8b, 0xec, 0x04, \ + 0x84, 0x61, 0x82, 0x91, \ + 0xf8, 0xd5, 0x84, 0xa1, \ + 0x0c, 0x0c, 0x90, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0xc8, 0xc8, 0x89, 0x04, \ + 0xca, 0x78, 0x84, 0x6a, \ + 0xe0, 0x10, 0x04, 0x89, \ + 0xd6, 0x58, 0x86, 0x1a, \ + 0x88, 0x68, 0x80, 0x6c, \ + 0xe0, 0x14, 0x04, 0x99, \ + 0x82, 0x1c, 0x8a, 0x28, \ + 0xe0, 0x10, 0x40, 0x51, \ + 0xe0, 0x14, 0x40, 0x61, \ + 0x40, 0x21, 0xc2, 0xc7, \ + 0x90, 0xc3, 0xe0, 0x18, \ + 0xf3, 0xf8, 0x42, 0xc0, \ + 0x92, 0x73, 0xc2, 0x77, \ + 0xe0, 0x38, 0x06, 0x29, \ + 0xec, 0x04, 0x02, 0x10, \ + 0xf3, 0xf8, 0x4a, 0x70, \ + 0xe0, 0x1c, 0x40, 0x11, \ + 0xec, 0x04, 0x02, 0x50, \ + 0xe0, 0x20, 0x40, 0x11, \ + 0xc2, 0x87, 0xa1, 0x2b, \ + 0x40, 0x81, 0xc2, 0x97, \ + 0x40, 0x91, 0xe0, 0x24, \ + 0x84, 0xe5, 0xe0, 0x28, \ + 0xe0, 0x38, 0x06, 0x29, \ + 0x66, 0x41, 0x06, 0x40, \ + 0x43, 0xf6, 0xc0, 0x04, \ + 0x81, 0x41, 0xe0, 0x0c, \ + 0x06, 0xc9, 0x81, 0x51, \ + 0xa1, 0xcb, 0xfc, 0x10, \ + 0x83, 0x51, 0x80, 0x45, \ + 0x42, 0xc2, 0x80, 0x65, \ + 0x82, 0x41, 0xfb, 0x84, \ + 0xfc, 0xd5, 0xc5, 0x4d, \ + 0x80, 0x55, 0xa1, 0x5b, \ + 0x40, 0x11, 0x81, 0x11, \ + 0x0c, 0x0c, 0xe0, 0x2c, \ + 0xdd, 0x93, 0xf0, 0x1c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x88, 0x6b, 0x89, 0x5e, \ + 0x84, 0xab, 0x86, 0x7b, \ + 0x99, 0x84, 0x82, 0x9b, \ + 0xe4, 0x90, 0x08, 0x80, \ + 0x64, 0x91, 0x0a, 0x80, \ + 0x16, 0x80, 0xc0, 0x01, \ + 0xc0, 0x01, 0x66, 0x91, \ + 0x64, 0xd1, 0x00, 0x80, \ + 0x02, 0x80, 0xc0, 0x02, \ + 0xc0, 0x02, 0x64, 0x91, \ + 0xbe, 0x1e, 0xfc, 0x0e, \ + 0x92, 0x1b, 0xbc, 0xbe, \ + 0x8c, 0x3b, 0x8e, 0x2b, \ + 0xef, 0xfc, 0x63, 0x68, \ + 0xe2, 0x24, 0x10, 0x05, \ + 0x86, 0x95, 0xa1, 0x0b, \ + 0xe4, 0x30, 0x04, 0x80, \ + 0x6c, 0x18, 0x8c, 0x1b, \ + 0xfe, 0xae, 0xef, 0xfc, \ + 0xe4, 0x80, 0x14, 0x80, \ + 0x94, 0x2b, 0x8c, 0x1b, \ + 0xef, 0xfc, 0x70, 0xa8, \ + 0x8e, 0x2b, 0x92, 0x1b, \ + 0x89, 0x44, 0x8c, 0x3b, \ + 0x74, 0xf8, 0x96, 0x5b, \ + 0xbc, 0x8e, 0xef, 0xfc, \ + 0xfe, 0x24, 0x92, 0x1b, \ + 0x8c, 0x4b, 0x8e, 0x3b, \ + 0x77, 0x78, 0x94, 0x5b, \ + 0xa8, 0xf1, 0xef, 0xfc, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf0, 0x80, 0x46, 0x22, \ + 0xec, 0x70, 0x06, 0x39, \ + 0x86, 0xe5, 0xa1, 0x3b, \ + 0xe0, 0x3c, 0x04, 0x39, \ + 0xe6, 0x82, 0x40, 0x3a, \ + 0xf0, 0x80, 0x46, 0x22, \ + 0x6b, 0x71, 0x06, 0x39, \ + 0x06, 0x30, 0xc0, 0x05, \ + 0xe2, 0x3d, 0xec, 0x14, \ + 0xc2, 0x07, 0x84, 0x55, \ + 0x04, 0x03, 0x80, 0x23, \ + 0xc0, 0x02, 0x63, 0xb9, \ + 0xe0, 0x14, 0x02, 0x23, \ + 0xe0, 0x18, 0x02, 0x13, \ + 0x41, 0x1a, 0x85, 0x0d, \ + 0x40, 0x0a, 0xe0, 0x04, \ + 0x00, 0x0c, 0xe1, 0x84, \ + 0xdd, 0x93, 0xe2, 0x00, \ + 0xdd, 0x93, 0x81, 0x01, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xed, 0x70, 0x1e, 0xf0, \ + 0xbd, 0xc4, 0x8d, 0x1e, \ + 0xe0, 0x80, 0x1e, 0x05, \ + 0xbf, 0x04, 0x9b, 0x0e, \ + 0xa1, 0x3e, 0x91, 0x0e, \ + 0xe0, 0x38, 0x06, 0x09, \ + 0x81, 0x01, 0x8b, 0x0e, \ + 0xa9, 0x0e, 0xab, 0x0e, \ + 0x02, 0x06, 0xa7, 0x0e, \ + 0xfe, 0xce, 0xe0, 0x04, \ + 0x8a, 0x3b, 0xa3, 0x5e, \ + 0xec, 0x04, 0x02, 0x10, \ + 0xcd, 0x0e, 0xa1, 0x1b, \ + 0xfe, 0x75, 0xf1, 0x0c, \ + 0x87, 0xc1, 0x81, 0x31, \ + 0xe0, 0x40, 0x04, 0x69, \ + 0x84, 0x3e, 0x91, 0x14, \ + 0xc6, 0x3e, 0x86, 0xce, \ + 0xe0, 0x08, 0x00, 0x06, \ + 0x94, 0x0e, 0x9b, 0x54, \ + 0xec, 0x04, 0x00, 0x60, \ + 0x01, 0x0a, 0xc4, 0x3e, \ + 0x96, 0xce, 0xf4, 0x08, \ + 0xd4, 0x3e, 0xd6, 0x3e, \ + 0x0f, 0x18, 0x8b, 0x01, \ + 0x0c, 0x0a, 0xc0, 0x00, \ + 0x83, 0x01, 0xf0, 0x04, \ + 0x8c, 0x5b, 0x89, 0x0e, \ + 0x85, 0x01, 0x80, 0x65, \ + 0x8b, 0x04, 0x89, 0x0e, \ + 0xe0, 0x88, 0x18, 0x04, \ + 0x66, 0x41, 0x00, 0x40, \ + 0x93, 0x0e, 0xc0, 0x02, \ + 0xe6, 0x40, 0x00, 0x40, \ + 0x00, 0x40, 0xfc, 0x0e, \ + 0xc0, 0x04, 0x66, 0x41, \ + 0xff, 0x01, 0xbe, 0x0e, \ + 0x81, 0x01, 0x9d, 0x0e, \ + 0x81, 0x01, 0xa5, 0x0e, \ + 0x81, 0x01, 0x9f, 0x0e, \ + 0x99, 0x2e, 0x8f, 0x0e, \ + 0x95, 0xce, 0x97, 0x5e, \ + 0xa1, 0x0b, 0x8b, 0x04, \ + 0x81, 0x61, 0xac, 0x05, \ + 0xe0, 0x34, 0x13, 0xc6, \ + 0xbe, 0x74, 0xfc, 0x84, \ + 0x9c, 0xa8, 0xfe, 0xb4, \ + 0x90, 0xc5, 0xa1, 0xab, \ + 0xe0, 0x44, 0x04, 0x09, \ + 0xfa, 0x80, 0x00, 0x04, \ + 0xe4, 0x00, 0x54, 0x9a, \ + 0x20, 0x01, 0x03, 0xf8, \ + 0xa5, 0x04, 0xc0, 0x00, \ + 0xa4, 0x95, 0xa1, 0x0b, \ + 0x00, 0x09, 0x8d, 0x04, \ + 0xc1, 0xad, 0xe0, 0x10, \ + 0x9b, 0x14, 0x82, 0xa5, \ + 0xe0, 0x08, 0x00, 0x46, \ + 0x87, 0x01, 0x84, 0x0e, \ + 0xa1, 0x04, 0x86, 0x0e, \ + 0xfc, 0x24, 0x00, 0x09, \ + 0x81, 0x01, 0xc6, 0x0e, \ + 0xa2, 0x14, 0xc4, 0x0e, \ + 0x04, 0xf0, 0xa0, 0x04, \ + 0xbc, 0x2e, 0xe5, 0x40, \ + 0x49, 0xfe, 0x81, 0x31, \ + 0xcf, 0xff, 0x7f, 0xfd, \ + 0x94, 0x2b, 0x81, 0x51, \ + 0xef, 0xfc, 0x47, 0x68, \ + 0xa3, 0x04, 0xa9, 0x14, \ + 0x80, 0x1e, 0x8c, 0x03, \ + 0x00, 0x05, 0x93, 0x04, \ + 0x9d, 0x24, 0xfc, 0x18, \ + 0xfb, 0x88, 0x44, 0x12, \ + 0x9f, 0x14, 0x9d, 0x2e, \ + 0x9f, 0x1e, 0x82, 0x11, \ + 0xe5, 0x30, 0x06, 0xf0, \ + 0xef, 0xfc, 0x45, 0xf6, \ + 0x48, 0xd8, 0x94, 0x1b, \ + 0xa7, 0x04, 0xef, 0xfc, \ + 0x99, 0x24, 0xac, 0x0c, \ + 0x95, 0xc4, 0x97, 0x54, \ + 0x0a, 0x1a, 0x8b, 0xb5, \ + 0x01, 0x68, 0xf0, 0x04, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0x83, 0x01, 0x91, 0x14, \ + 0x87, 0x01, 0x84, 0x0e, \ + 0xa1, 0x34, 0x86, 0x0e, \ + 0xfc, 0x24, 0x06, 0x09, \ + 0x81, 0x41, 0xc6, 0x0e, \ + 0xa3, 0x04, 0xc4, 0x4e, \ + 0x80, 0x4e, 0x8c, 0x03, \ + 0xf0, 0x98, 0x40, 0x32, \ + 0xe1, 0x1c, 0x00, 0x05, \ + 0xa5, 0x04, 0x89, 0x25, \ + 0xe0, 0x04, 0x00, 0x1a, \ + 0x20, 0x01, 0x03, 0xf8, \ + 0x04, 0x09, 0xc0, 0x00, \ + 0x9b, 0x34, 0xe0, 0x40, \ + 0x8e, 0x1e, 0x87, 0x11, \ + 0x02, 0x19, 0xa1, 0x14, \ + 0x00, 0x1a, 0xfc, 0x24, \ + 0xce, 0x1e, 0xe0, 0x08, \ + 0xcc, 0x0e, 0x81, 0x01, \ + 0x20, 0x01, 0x01, 0xc8, \ + 0x9f, 0x04, 0xc0, 0x00, \ + 0xe0, 0x00, 0x00, 0x1a, \ + 0xe0, 0x08, 0x00, 0x56, \ + 0x01, 0x68, 0x9d, 0x14, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0x93, 0x04, 0x86, 0x25, \ + 0xfc, 0x18, 0x00, 0x05, \ + 0xe5, 0x30, 0x06, 0xf0, \ + 0xef, 0xfc, 0x45, 0xf6, \ + 0x86, 0xab, 0x94, 0x1b, \ + 0xef, 0xfc, 0x44, 0x48, \ + 0x97, 0x54, 0x95, 0xc4, \ + 0x81, 0xf5, 0x99, 0x24, \ + 0x00, 0x66, 0x88, 0x14, \ + 0xa3, 0x34, 0xe0, 0x08, \ + 0x8c, 0x1e, 0x8c, 0x33, \ + 0x84, 0x0e, 0x9b, 0x14, \ + 0xa1, 0x04, 0xfa, 0x35, \ + 0x14, 0x00, 0x8c, 0x03, \ + 0xc0, 0x01, 0x64, 0x71, \ + 0x8f, 0x14, 0xa8, 0x04, \ + 0x8f, 0x1e, 0x82, 0x11, \ + 0x88, 0x61, 0xac, 0x0c, \ + 0x84, 0xb1, 0x8d, 0xcb, \ + 0x82, 0x91, 0x82, 0x71, \ + 0xd6, 0x15, 0x90, 0x81, \ + 0x82, 0x11, 0xa5, 0x14, \ + 0xa5, 0x1e, 0x89, 0x04, \ + 0xd2, 0x35, 0xc1, 0x1d, \ + 0xe0, 0x48, 0x04, 0x09, \ + 0xc0, 0x1d, 0x8f, 0x14, \ + 0x8d, 0x01, 0x82, 0x25, \ + 0x84, 0x0e, 0x91, 0x14, \ + 0x86, 0x0e, 0x87, 0x01, \ + 0xc6, 0x0e, 0x81, 0x01, \ + 0x9f, 0x04, 0xc4, 0x0e, \ + 0x82, 0xd5, 0xa1, 0x0b, \ + 0xe0, 0x40, 0x04, 0x09, \ + 0xf0, 0x08, 0x00, 0x0a, \ + 0x82, 0x15, 0x8f, 0x01, \ + 0x84, 0x0e, 0x91, 0x14, \ + 0x86, 0x0e, 0x87, 0x01, \ + 0xc6, 0x0e, 0x81, 0x01, \ + 0x1e, 0xf0, 0xc4, 0x0e, \ + 0x0c, 0x0c, 0xe5, 0x70, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x88, 0x7b, 0x8a, 0x6b, \ + 0x82, 0x9b, 0x86, 0x8b, \ + 0x99, 0xb4, 0x80, 0xab, \ + 0xe4, 0x90, 0x0a, 0xb0, \ + 0x64, 0x91, 0x00, 0xb0, \ + 0x02, 0xb0, 0xc0, 0x01, \ + 0xc0, 0x01, 0x66, 0xd1, \ + 0x66, 0x91, 0x06, 0xb0, \ + 0xfc, 0x1e, 0xc0, 0x01, \ + 0xbc, 0x0e, 0xbe, 0x3e, \ + 0x8e, 0x3b, 0x92, 0x1b, \ + 0x6b, 0x58, 0x8c, 0x4b, \ + 0x16, 0x05, 0xef, 0xfc, \ + 0xa1, 0x0b, 0xe1, 0xa4, \ + 0x04, 0xb0, 0x86, 0x15, \ + 0x94, 0x0b, 0xe4, 0x30, \ + 0x52, 0x78, 0x8c, 0x1b, \ + 0x89, 0x8e, 0xef, 0xfc, \ + 0xe4, 0x80, 0x10, 0xb0, \ + 0x8c, 0x1b, 0x94, 0x0b, \ + 0x56, 0xf8, 0x90, 0x2b, \ + 0xbc, 0xbe, 0xef, 0xfc, \ + 0x92, 0x1b, 0x94, 0x0b, \ + 0x8e, 0x3b, 0x89, 0x24, \ + 0x90, 0x5b, 0x8c, 0x4b, \ + 0xef, 0xfc, 0x5e, 0x28, \ + 0x0c, 0x0c, 0xa8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0x06, 0x3b, 0x8e, 0x04, \ + 0x04, 0x29, 0xe0, 0x1c, \ + 0xc0, 0x01, 0x63, 0x49, \ + 0xe0, 0x3c, 0x02, 0x19, \ + 0x94, 0x0e, 0xd2, 0x48, \ + 0x4a, 0x21, 0xd4, 0x3c, \ + 0x4a, 0x11, 0xe0, 0x18, \ + 0x4a, 0x41, 0xe0, 0x1c, \ + 0xdd, 0x93, 0xe0, 0x20, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x86, 0x7b, 0x88, 0x6b, \ + 0x82, 0x9b, 0x84, 0x8b, \ + 0xe0, 0x3c, 0x12, 0x09, \ + 0x63, 0x49, 0x10, 0x19, \ + 0x04, 0x80, 0xc0, 0x01, \ + 0xc0, 0x04, 0x67, 0x61, \ + 0x52, 0x78, 0x8c, 0x3b, \ + 0x06, 0x70, 0xe8, 0x0c, \ + 0x08, 0x70, 0xe4, 0x30, \ + 0x92, 0x1b, 0xe4, 0x80, \ + 0x8c, 0x5b, 0x90, 0x2b, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xf9, 0xf5, 0xf2, 0x1c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x88, 0x7b, 0x89, 0x5e, \ + 0x8b, 0x1e, 0x84, 0x8b, \ + 0x94, 0x85, 0xa1, 0x8b, \ + 0xbe, 0x0e, 0x9f, 0x04, \ + 0xfe, 0x0e, 0x9d, 0x04, \ + 0xe0, 0x10, 0x06, 0x0b, \ + 0x81, 0x61, 0xfc, 0x0e, \ + 0xfa, 0x80, 0x14, 0x64, \ + 0xe6, 0x40, 0x12, 0x70, \ + 0xe0, 0x8c, 0x00, 0xa4, \ + 0xf0, 0x80, 0x42, 0x92, \ + 0xfc, 0x00, 0x12, 0x05, \ + 0xa0, 0x01, 0x86, 0x14, \ + 0xe0, 0x88, 0x16, 0xa4, \ + 0x44, 0x12, 0x81, 0x21, \ + 0x96, 0x93, 0xf2, 0x88, \ + 0xe2, 0x00, 0x12, 0x15, \ + 0xcb, 0x07, 0xf6, 0x27, \ + 0x06, 0xf0, 0xa4, 0x0f, \ + 0x8b, 0x24, 0xe4, 0x60, \ + 0xe8, 0x28, 0x66, 0x48, \ + 0x88, 0xf5, 0xa1, 0x0b, \ + 0x99, 0x21, 0x8d, 0x04, \ + 0x6e, 0x58, 0xfc, 0x14, \ + 0x89, 0x14, 0xe8, 0x28, \ + 0xac, 0x0e, 0x82, 0xb3, \ + 0xf0, 0xa8, 0x40, 0x72, \ + 0x62, 0x41, 0x00, 0x09, \ + 0xa1, 0x0b, 0xc0, 0x01, \ + 0x02, 0x90, 0x84, 0x25, \ + 0xc0, 0x02, 0x64, 0x01, \ + 0x82, 0x61, 0x84, 0x14, \ + 0xe0, 0x02, 0x40, 0x1a, \ + 0xfa, 0x80, 0x02, 0x64, \ + 0xd1, 0x1d, 0xc2, 0xa7, \ + 0x82, 0xa3, 0xfe, 0x14, \ + 0xf0, 0xb5, 0xa8, 0x0c, \ + 0x00, 0xa4, 0x81, 0xf5, \ + 0xfe, 0x14, 0xe0, 0x84, \ + 0x81, 0x11, 0x82, 0x03, \ + 0xff, 0x01, 0x80, 0x1c, \ + 0xbe, 0x24, 0x87, 0x11, \ + 0x88, 0x0e, 0x8a, 0x1e, \ + 0xca, 0xae, 0x81, 0x01, \ + 0xb8, 0xf1, 0xc8, 0x0e, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x06, 0x49, 0x88, 0x6b, \ + 0x06, 0x59, 0xe0, 0x2c, \ + 0x8a, 0x04, 0xe0, 0x30, \ + 0xe0, 0x4c, 0x02, 0x19, \ + 0x83, 0x21, 0x8c, 0x34, \ + 0xab, 0x2f, 0xa9, 0x2f, \ + 0x5f, 0x58, 0xc8, 0x17, \ + 0x98, 0x0e, 0xe8, 0x28, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xe0, 0x58, 0x02, 0x59, \ + 0x83, 0x01, 0xa3, 0x5b, \ + 0xa5, 0x5b, 0x82, 0x05, \ + 0xa7, 0x5b, 0x82, 0x25, \ + 0x81, 0x01, 0x8c, 0x55, \ + 0x81, 0x55, 0x89, 0x51, \ + 0x81, 0x01, 0x83, 0x51, \ + 0x85, 0x51, 0x81, 0x25, \ + 0x0c, 0x34, 0x8d, 0xc4, \ + 0xd6, 0x47, 0xe0, 0x88, \ + 0xe0, 0x08, 0x10, 0xa6, \ + 0xfa, 0x80, 0x12, 0x04, \ + 0x54, 0x22, 0x8c, 0x93, \ + 0x14, 0xa9, 0xf0, 0xa4, \ + 0xc0, 0x05, 0x6b, 0x71, \ + 0x84, 0xd5, 0xab, 0xab, \ + 0xe0, 0x88, 0x14, 0x94, \ + 0x14, 0xa5, 0x84, 0xa3, \ + 0x04, 0xb5, 0xe7, 0xdc, \ + 0xc0, 0x01, 0x67, 0xfd, \ + 0x16, 0xa4, 0xb6, 0xa3, \ + 0x86, 0x74, 0xe1, 0x7c, \ + 0x96, 0xa3, 0x88, 0xbf, \ + 0x80, 0xc5, 0x8f, 0xad, \ + 0xb2, 0x7e, 0x87, 0x71, \ + 0xf2, 0x3e, 0xb0, 0x8e, \ + 0x82, 0x01, 0xf0, 0x9e, \ + 0xfa, 0x80, 0x0e, 0x04, \ + 0xf6, 0xb5, 0xce, 0x5d, \ + 0xf0, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x84, 0x7b, 0x88, 0x6b, \ + 0x0e, 0x99, 0x82, 0xab, \ + 0x0e, 0x89, 0xe0, 0x58, \ + 0x91, 0x04, 0xe0, 0x90, \ + 0x67, 0xfd, 0x06, 0xb5, \ + 0xbc, 0x0e, 0xc0, 0x01, \ + 0x90, 0x3b, 0x86, 0x2b, \ + 0x7a, 0xd8, 0x92, 0x4b, \ + 0x14, 0x09, 0xef, 0xfc, \ + 0xa1, 0x0b, 0xe0, 0x4c, \ + 0x0e, 0x19, 0x8a, 0x25, \ + 0x02, 0x10, 0xe0, 0x94, \ + 0x04, 0xb0, 0xec, 0x14, \ + 0xc0, 0x07, 0x67, 0xfd, \ + 0xf4, 0x0c, 0x02, 0x8a, \ + 0x42, 0x22, 0xd7, 0x27, \ + 0xab, 0x21, 0xf0, 0xa4, \ + 0xa9, 0x21, 0x80, 0x55, \ + 0xfc, 0x08, 0x14, 0x21, \ + 0x06, 0x14, 0x84, 0x13, \ + 0x81, 0x21, 0xfb, 0x80, \ + 0x46, 0x3a, 0x81, 0x11, \ + 0x08, 0x34, 0xe1, 0x26, \ + 0x8e, 0x43, 0xe0, 0x88, \ + 0xe0, 0x28, 0x08, 0x45, \ + 0x0a, 0x24, 0x82, 0x21, \ + 0xc1, 0x5d, 0xfa, 0x80, \ + 0x82, 0x31, 0xca, 0x47, \ + 0x06, 0x34, 0x88, 0x13, \ + 0xfc, 0x15, 0xfa, 0x80, \ + 0x81, 0x11, 0x81, 0x25, \ + 0xe0, 0x20, 0x0e, 0x25, \ + 0xe6, 0x82, 0x40, 0x2a, \ + 0xa1, 0x1d, 0xa0, 0x13, \ + 0x00, 0x84, 0x84, 0x55, \ + 0x8c, 0x03, 0xe0, 0x8c, \ + 0xe0, 0x90, 0x00, 0x25, \ + 0xe0, 0x94, 0x00, 0x35, \ + 0xe1, 0x7c, 0x08, 0x14, \ + 0x42, 0x32, 0x82, 0x23, \ + 0x40, 0x15, 0xf2, 0x90, \ + 0x40, 0x25, 0xe0, 0x94, \ + 0x83, 0xe5, 0xe0, 0x90, \ + 0xe0, 0x8c, 0x08, 0x84, \ + 0x08, 0x05, 0x8c, 0x43, \ + 0xa2, 0x2b, 0xe0, 0x94, \ + 0xe1, 0x7c, 0x02, 0x24, \ + 0x08, 0x35, 0x83, 0x0b, \ + 0x82, 0x25, 0xe0, 0x90, \ + 0x08, 0x40, 0xc2, 0x0d, \ + 0x82, 0x35, 0xe6, 0x40, \ + 0x92, 0x0e, 0x81, 0x01, \ + 0x81, 0xa5, 0x90, 0x0e, \ + 0x08, 0x40, 0xc4, 0x3d, \ + 0xfe, 0x25, 0xe6, 0x40, \ + 0x40, 0x02, 0xa4, 0x33, \ + 0x92, 0x0e, 0xf3, 0x84, \ + 0x00, 0x84, 0x90, 0x3e, \ + 0x02, 0x60, 0xe0, 0x88, \ + 0x82, 0x03, 0xe6, 0x40, \ + 0xe0, 0x18, 0x0e, 0x25, \ + 0xe2, 0x00, 0x00, 0x35, \ + 0x40, 0x35, 0x84, 0x33, \ + 0x90, 0x13, 0xe2, 0x00, \ + 0x60, 0x01, 0x02, 0x09, \ + 0x82, 0x01, 0xc0, 0x01, \ + 0x60, 0x01, 0x42, 0x01, \ + 0x88, 0xf1, 0xc0, 0x01, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x89, 0x4e, 0x8b, 0x5e, \ + 0x80, 0x9b, 0x82, 0x6b, \ + 0x9f, 0x84, 0xa1, 0xb4, \ + 0xe4, 0x60, 0x08, 0xf0, \ + 0x70, 0xb8, 0x9d, 0xa4, \ + 0x8d, 0x14, 0xef, 0xfc, \ + 0xe0, 0x38, 0x14, 0x29, \ + 0xe4, 0x90, 0x0a, 0xb0, \ + 0x64, 0x91, 0x00, 0xb0, \ + 0x0e, 0xb0, 0xc0, 0x01, \ + 0xc0, 0x01, 0x66, 0x91, \ + 0xbc, 0x0e, 0xbe, 0x7e, \ + 0x90, 0x4b, 0x8c, 0x3b, \ + 0xef, 0xfc, 0x69, 0x68, \ + 0xe1, 0xa8, 0x16, 0x05, \ + 0x80, 0x95, 0xa7, 0x0b, \ + 0xa1, 0x0b, 0x9c, 0x04, \ + 0x04, 0xb0, 0x84, 0xf5, \ + 0x92, 0x0b, 0xe4, 0x30, \ + 0x76, 0x58, 0x90, 0x1b, \ + 0x0c, 0xb0, 0xef, 0xf8, \ + 0x92, 0x0b, 0xe4, 0x80, \ + 0x8c, 0x2b, 0x90, 0x1b, \ + 0xef, 0xf8, 0x7a, 0xe8, \ + 0x92, 0x0b, 0xbc, 0xbe, \ + 0x8b, 0x24, 0x89, 0x14, \ + 0x90, 0x4b, 0x94, 0x3b, \ + 0x42, 0x18, 0x8c, 0x5b, \ + 0xb8, 0xf1, 0xef, 0xfc, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x86, 0x7b, 0x88, 0x9b, \ + 0x02, 0x22, 0x93, 0xb4, \ + 0xa1, 0x1b, 0xee, 0x40, \ + 0xac, 0x5e, 0x81, 0x21, \ + 0x90, 0xc5, 0xbc, 0x2e, \ + 0x81, 0xa1, 0x91, 0x84, \ + 0x82, 0x21, 0xc1, 0x34, \ + 0x54, 0x32, 0xc3, 0x2d, \ + 0xfe, 0x75, 0xfa, 0x28, \ + 0x8e, 0x85, 0xa0, 0xad, \ + 0x94, 0x0b, 0x9e, 0x3b, \ + 0x92, 0x2b, 0x8a, 0x1b, \ + 0xe8, 0x28, 0x4e, 0x48, \ + 0xa1, 0x6b, 0x80, 0x6b, \ + 0x94, 0x0b, 0x8e, 0x15, \ + 0x90, 0x2b, 0x92, 0x1b, \ + 0xe8, 0x28, 0x51, 0x48, \ + 0xa1, 0x6b, 0x80, 0x6b, \ + 0xbc, 0x94, 0x8c, 0x15, \ + 0x8c, 0x45, 0xa1, 0x9b, \ + 0x9c, 0x0a, 0xa0, 0x24, \ + 0xc1, 0x2d, 0xc7, 0x27, \ + 0x81, 0x61, 0x9e, 0x1a, \ + 0xc2, 0x2d, 0x80, 0x75, \ + 0x82, 0x03, 0x88, 0x85, \ + 0x04, 0x06, 0x81, 0x11, \ + 0x81, 0x31, 0xff, 0x40, \ + 0xef, 0xf4, 0x54, 0xe8, \ + 0xe0, 0xfc, 0x04, 0x14, \ + 0xa4, 0x0f, 0xc3, 0x07, \ + 0x81, 0x31, 0xc3, 0x17, \ + 0x4b, 0x78, 0x92, 0x2b, \ + 0x04, 0x04, 0xef, 0xf4, \ + 0xff, 0x31, 0xe0, 0xa0, \ + 0xf1, 0x07, 0x87, 0x2b, \ + 0xa2, 0x0f, 0xd0, 0x17, \ + 0xff, 0x21, 0x80, 0x55, \ + 0x80, 0x45, 0xa1, 0x0b, \ + 0xac, 0x2e, 0xff, 0x21, \ + 0x41, 0xfe, 0x81, 0x65, \ + 0xc0, 0x3f, 0x7f, 0xfd, \ + 0x81, 0x61, 0xac, 0x0e, \ + 0x88, 0xf1, 0x8c, 0x0b, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x81, 0x01, 0xdd, 0x93, \ + 0xff, 0x61, 0xac, 0x0e, \ + 0x00, 0x0c, 0xff, 0x85, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xf0, 0x1e, 0xf0, \ + 0x8d, 0x2e, 0xfc, 0x5e, \ + 0xad, 0x14, 0x82, 0x2b, \ + 0x02, 0x15, 0xbe, 0x1e, \ + 0x89, 0x1e, 0xe0, 0x14, \ + 0xe4, 0x90, 0x08, 0x00, \ + 0xe4, 0x10, 0x0c, 0x00, \ + 0xe4, 0x80, 0x10, 0x00, \ + 0x81, 0x91, 0x81, 0x71, \ + 0xfe, 0x0e, 0x81, 0x01, \ + 0x8b, 0x0e, 0x81, 0x01, \ + 0x81, 0x51, 0x81, 0x11, \ + 0x9b, 0x2e, 0x93, 0x5e, \ + 0x40, 0x90, 0x9d, 0x3e, \ + 0x04, 0x04, 0xf0, 0x1c, \ + 0xc4, 0x07, 0xe0, 0x84, \ + 0xa4, 0x53, 0x8d, 0x54, \ + 0x58, 0x42, 0x99, 0x4e, \ + 0x97, 0x6e, 0xf1, 0x80, \ + 0xf1, 0x80, 0x44, 0x62, \ + 0xf8, 0x24, 0x18, 0x45, \ + 0x40, 0x82, 0x95, 0x8e, \ + 0x88, 0x34, 0xf1, 0x80, \ + 0xb0, 0x04, 0x80, 0x24, \ + 0x54, 0x52, 0xa1, 0x4b, \ + 0x0a, 0xbb, 0xf0, 0x9c, \ + 0xaa, 0xca, 0xfc, 0x1c, \ + 0xe2, 0x00, 0x0c, 0x0c, \ + 0xe0, 0x20, 0x14, 0x5b, \ + 0x14, 0x1b, 0x82, 0x63, \ + 0xa1, 0x3b, 0xe0, 0x24, \ + 0xf0, 0xac, 0x54, 0xc2, \ + 0xe2, 0x00, 0x10, 0x0c, \ + 0x90, 0x63, 0x8a, 0xa3, \ + 0xa1, 0x2b, 0x82, 0xa3, \ + 0xe0, 0x00, 0x14, 0x1a, \ + 0xe2, 0x00, 0x10, 0x0c, \ + 0x90, 0x63, 0xa1, 0x0b, \ + 0x0c, 0x0c, 0x8f, 0x6e, \ + 0x91, 0x6e, 0xe2, 0x00, \ + 0xa0, 0x00, 0x03, 0xf8, \ + 0xe5, 0x12, 0x4c, 0xba, \ + 0xe5, 0x92, 0x48, 0xba, \ + 0xe1, 0x84, 0x10, 0xa4, \ + 0x50, 0xca, 0x90, 0x63, \ + 0x46, 0xca, 0xe5, 0x0e, \ + 0x81, 0xc1, 0xe5, 0x8e, \ + 0xf2, 0xb0, 0x48, 0x42, \ + 0xe5, 0x0a, 0x58, 0x5a, \ + 0x44, 0x5a, 0x90, 0x63, \ + 0x46, 0x42, 0xe5, 0x8a, \ + 0x48, 0x1a, 0xf2, 0x8c, \ + 0x98, 0x63, 0xe5, 0x02, \ + 0xe5, 0x82, 0x42, 0x1a, \ + 0xf2, 0x88, 0x44, 0x32, \ + 0xf0, 0x90, 0x40, 0x62, \ + 0xf2, 0x84, 0x42, 0x22, \ + 0x94, 0x2b, 0x81, 0x31, \ + 0xef, 0xf4, 0x42, 0x08, \ + 0x64, 0x01, 0x14, 0x8a, \ + 0x86, 0x15, 0xc1, 0x00, \ + 0xa1, 0x1b, 0x8b, 0x14, \ + 0xef, 0xfc, 0x55, 0xf6, \ + 0x9b, 0x24, 0x9d, 0x34, \ + 0x97, 0x64, 0x99, 0x44, \ + 0x93, 0x54, 0x95, 0x84, \ + 0x89, 0x14, 0x84, 0x75, \ + 0x89, 0x11, 0xa3, 0x1b, \ + 0x95, 0x11, 0x80, 0x45, \ + 0x83, 0x11, 0x89, 0x1e, \ + 0xfe, 0x5e, 0x8b, 0x1e, \ + 0x81, 0x01, 0x81, 0x95, \ + 0x9d, 0x34, 0x81, 0xa1, \ + 0x99, 0x44, 0x9b, 0x24, \ + 0x95, 0x84, 0x97, 0x64, \ + 0x8f, 0x14, 0x93, 0x54, \ + 0x98, 0x13, 0x91, 0xc4, \ + 0xcd, 0xac, 0xc9, 0x0e, \ + 0x82, 0x51, 0x90, 0x71, \ + 0xe2, 0x00, 0x0e, 0x0a, \ + 0xa0, 0x41, 0x84, 0x91, \ + 0xa0, 0x81, 0xa0, 0x61, \ + 0x00, 0x14, 0xdc, 0x95, \ + 0x00, 0x0a, 0xfc, 0x98, \ + 0x82, 0xc5, 0xe1, 0x00, \ + 0xa1, 0x0b, 0x8b, 0x04, \ + 0x89, 0x04, 0x82, 0x75, \ + 0x8d, 0x01, 0xa3, 0x0b, \ + 0x97, 0x01, 0x80, 0x45, \ + 0x87, 0x01, 0x89, 0x0e, \ + 0xe0, 0x08, 0x02, 0x86, \ + 0x8b, 0x04, 0x81, 0x75, \ + 0x84, 0x15, 0xa3, 0x0b, \ + 0x02, 0x96, 0x89, 0x01, \ + 0xfc, 0x24, 0xe0, 0x08, \ + 0xfe, 0x44, 0xbe, 0x34, \ + 0xca, 0x4e, 0x88, 0x1e, \ + 0xe0, 0x10, 0x46, 0x05, \ + 0x46, 0x05, 0x89, 0x04, \ + 0x46, 0x45, 0xe0, 0x14, \ + 0xf8, 0xf1, 0xe0, 0x1c, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0xed, 0xa0, 0x1e, 0xf0, \ + 0xfe, 0x2e, 0xbc, 0x5e, \ + 0x1e, 0x15, 0x89, 0x1e, \ + 0xbe, 0x1e, 0xe0, 0x88, \ + 0xe4, 0x90, 0x08, 0x00, \ + 0xe4, 0x10, 0x18, 0x00, \ + 0xe4, 0x80, 0x0c, 0x00, \ + 0x81, 0x81, 0x81, 0x21, \ + 0xfc, 0x0e, 0x81, 0x01, \ + 0xa1, 0x0e, 0x81, 0x01, \ + 0x81, 0x01, 0x81, 0x11, \ + 0x81, 0xa1, 0x9f, 0x0e, \ + 0xab, 0x2e, 0xb3, 0x3e, \ + 0x00, 0x24, 0x8e, 0x29, \ + 0xb1, 0x4e, 0xe0, 0x88, \ + 0xf1, 0x80, 0x4a, 0x42, \ + 0x46, 0xc2, 0xaf, 0xce, \ + 0xad, 0x6e, 0xf1, 0x80, \ + 0xf1, 0x80, 0x48, 0x62, \ + 0xf8, 0x24, 0x0a, 0x05, \ + 0x90, 0x44, 0x8c, 0x34, \ + 0x58, 0x32, 0x94, 0x54, \ + 0xa8, 0xcf, 0xf9, 0x80, \ + 0x0c, 0x06, 0xaa, 0xcf, \ + 0xd0, 0x00, 0x60, 0x01, \ + 0xe4, 0x18, 0x58, 0x9a, \ + 0x18, 0x0c, 0xa1, 0x0b, \ + 0xa1, 0x3b, 0xe2, 0x00, \ + 0x20, 0x01, 0x00, 0x58, \ + 0x83, 0x61, 0xc0, 0x00, \ + 0xa1, 0x64, 0x9f, 0x6e, \ + 0xe0, 0x00, 0x0c, 0x1a, \ + 0x0c, 0x2a, 0x9f, 0x64, \ + 0x42, 0x1a, 0xe0, 0x04, \ + 0x98, 0x13, 0xe1, 0x88, \ + 0xe2, 0x00, 0x0c, 0x0c, \ + 0x18, 0x24, 0xa1, 0x4b, \ + 0x00, 0x58, 0xe0, 0x84, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0xa1, 0x2e, 0x83, 0x21, \ + 0x0e, 0x0c, 0x82, 0x63, \ + 0xa1, 0x5b, 0xe2, 0x00, \ + 0xa5, 0xce, 0x89, 0x94, \ + 0x45, 0xfe, 0xb8, 0x93, \ + 0xcf, 0xff, 0x7f, 0xfd, \ + 0xfb, 0x88, 0x42, 0x02, \ + 0xfb, 0x88, 0x58, 0x32, \ + 0xfb, 0x88, 0x48, 0x42, \ + 0xfb, 0x88, 0x46, 0x52, \ + 0x20, 0x01, 0x00, 0x48, \ + 0xfc, 0xae, 0xc0, 0x00, \ + 0x40, 0x92, 0xa9, 0xae, \ + 0x12, 0x5b, 0xf0, 0xa0, \ + 0x82, 0x9a, 0xfc, 0x20, \ + 0xa7, 0x6e, 0x8e, 0x63, \ + 0xe0, 0x20, 0x00, 0x6b, \ + 0xe0, 0x24, 0x00, 0x2b, \ + 0xe5, 0x16, 0x40, 0x1a, \ + 0xe5, 0x26, 0x4e, 0xca, \ + 0xe5, 0x96, 0x42, 0x1a, \ + 0xe5, 0xa6, 0x56, 0xca, \ + 0xe5, 0x1a, 0x54, 0x4a, \ + 0xf0, 0x80, 0x4a, 0x72, \ + 0xe5, 0x9a, 0x4c, 0x4a, \ + 0xf2, 0x84, 0x48, 0xb2, \ + 0xe5, 0x0a, 0x58, 0x3a, \ + 0x94, 0x53, 0xa3, 0xae, \ + 0xe5, 0x8a, 0x46, 0x3a, \ + 0xf2, 0x98, 0x44, 0x42, \ + 0xf0, 0xb0, 0x48, 0x52, \ + 0xf2, 0x8c, 0x54, 0x22, \ + 0xf9, 0xa8, 0x44, 0x42, \ + 0xe0, 0x00, 0x04, 0x1a, \ + 0xe2, 0x00, 0x0a, 0x0c, \ + 0xa0, 0x00, 0x06, 0x88, \ + 0xfe, 0x24, 0x9d, 0x5e, \ + 0xa5, 0x34, 0x99, 0x3e, \ + 0x46, 0x22, 0xa6, 0x23, \ + 0x06, 0x53, 0xf0, 0xa0, \ + 0x91, 0x5e, 0xe0, 0x24, \ + 0xe0, 0x20, 0x06, 0x93, \ + 0x04, 0x23, 0x9b, 0x8e, \ + 0x8b, 0x6e, 0xfc, 0x20, \ + 0x06, 0x54, 0x8e, 0x62, \ + 0x93, 0x3e, 0xe1, 0x7c, \ + 0xe1, 0x7c, 0x06, 0x94, \ + 0x92, 0x8b, 0x8d, 0x3e, \ + 0xe1, 0x7c, 0x06, 0x24, \ + 0xe1, 0x7c, 0x12, 0x64, \ + 0x8f, 0xce, 0xa5, 0x4e, \ + 0xef, 0xf0, 0x79, 0xe8, \ + 0x95, 0x1e, 0x97, 0x0e, \ + 0x96, 0x1b, 0x8e, 0x0b, \ + 0x92, 0x3b, 0x8c, 0x2b, \ + 0xef, 0xf0, 0x79, 0x68, \ + 0x82, 0xbb, 0x80, 0x6b, \ + 0x8b, 0x14, 0xa3, 0x04, \ + 0x8d, 0x34, 0x90, 0x2b, \ + 0xef, 0xf0, 0x78, 0xe8, \ + 0x82, 0x9b, 0x80, 0x7b, \ + 0x99, 0x14, 0x8f, 0x04, \ + 0x93, 0x34, 0x91, 0x24, \ + 0xef, 0xf0, 0x78, 0x68, \ + 0x80, 0x73, 0xa5, 0x24, \ + 0xf2, 0x84, 0x40, 0x92, \ + 0x42, 0x02, 0x8c, 0x73, \ + 0x97, 0x04, 0xf2, 0xac, \ + 0x95, 0x34, 0x8e, 0x03, \ + 0xf2, 0x8c, 0x42, 0x12, \ + 0xe1, 0x84, 0x18, 0x24, \ + 0xe0, 0xfc, 0x06, 0xa4, \ + 0xff, 0x31, 0xa6, 0xcf, \ + 0x12, 0xa4, 0x86, 0x1d, \ + 0x81, 0x41, 0xe1, 0x04, \ + 0xf1, 0xb0, 0x4a, 0x42, \ + 0xf3, 0xa4, 0x48, 0x42, \ + 0x80, 0x55, 0x94, 0x3b, \ + 0xad, 0x64, 0x8a, 0xcb, \ + 0xa7, 0x74, 0xa9, 0xa4, \ + 0x88, 0x9b, 0x80, 0x55, \ + 0x42, 0x92, 0x98, 0x03, \ + 0x62, 0xb8, 0xf2, 0x84, \ + 0xb3, 0x34, 0xef, 0xf0, \ + 0xaf, 0xc4, 0xb1, 0x44, \ + 0x9d, 0x54, 0xab, 0x24, \ + 0x81, 0x95, 0x9b, 0x84, \ + 0xb3, 0x34, 0x81, 0x01, \ + 0xaf, 0xc4, 0xb1, 0x44, \ + 0xab, 0x24, 0xad, 0x64, \ + 0xa7, 0x74, 0xa9, 0xa4, \ + 0xf0, 0x94, 0x42, 0x72, \ + 0x90, 0x81, 0xcd, 0x0c, \ + 0x10, 0x0a, 0x82, 0xa1, \ + 0x84, 0x21, 0xe2, 0x00, \ + 0xa0, 0xc1, 0xa0, 0x41, \ + 0x30, 0x98, 0xa0, 0x61, \ + 0x00, 0x14, 0xdf, 0xfc, \ + 0xa1, 0x14, 0xfc, 0x98, \ + 0x00, 0x5a, 0xa1, 0x1b, \ + 0x42, 0x0a, 0xe1, 0x00, \ + 0x87, 0x11, 0xe1, 0x80, \ + 0x8a, 0x25, 0x87, 0x01, \ + 0x8a, 0x55, 0xbe, 0x54, \ + 0xe0, 0x08, 0x04, 0x86, \ + 0x04, 0x76, 0x80, 0x65, \ + 0x0a, 0x35, 0xe0, 0x08, \ + 0x06, 0x1a, 0xe0, 0x10, \ + 0x01, 0x88, 0xe0, 0x00, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0x06, 0x5a, 0xa1, 0x34, \ + 0x40, 0x0a, 0xe0, 0x04, \ + 0x42, 0x0a, 0xe9, 0x9c, \ + 0x8b, 0x31, 0xe1, 0x80, \ + 0x92, 0x3e, 0xbc, 0x44, \ + 0xbc, 0x34, 0x82, 0x65, \ + 0xfc, 0x24, 0x8c, 0x2e, \ + 0x4a, 0x15, 0xce, 0x2e, \ + 0x4a, 0x05, 0xe0, 0x10, \ + 0x4a, 0x25, 0xe0, 0x14, \ + 0x1e, 0xf0, 0xe0, 0x1c, \ + 0x0c, 0x0c, 0xe5, 0xa0, \ + 0xdd, 0x93, 0xf6, 0x7c, \ + 0xbe, 0x54, 0x8d, 0x01, \ + 0x85, 0x11, 0xf6, 0x05, \ + 0xe0, 0x08, 0x04, 0x86, \ + 0xfb, 0x85, 0xf4, 0xf5, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x84, 0x7b, 0x86, 0x6b, \ + 0x45, 0x86, 0x82, 0x8b, \ + 0x8e, 0x0b, 0xe0, 0x20, \ + 0xef, 0xf0, 0x7b, 0xa8, \ + 0x4e, 0x01, 0x89, 0x01, \ + 0x4e, 0x01, 0xe0, 0x14, \ + 0x9f, 0x01, 0xe0, 0x18, \ + 0x4e, 0x01, 0x8f, 0x11, \ + 0x4e, 0x11, 0xe0, 0x1c, \ + 0xa1, 0x01, 0xe0, 0x20, \ + 0xe2, 0x30, 0x10, 0x15, \ + 0xe0, 0x24, 0x4e, 0x01, \ + 0x4e, 0x01, 0x8d, 0x21, \ + 0x4e, 0x25, 0xe0, 0x28, \ + 0x06, 0x14, 0xe2, 0x28, \ + 0x08, 0x14, 0xe1, 0x0c, \ + 0x81, 0x01, 0xe1, 0x10, \ + 0x83, 0x17, 0xa8, 0x33, \ + 0x4e, 0x05, 0xc4, 0x37, \ + 0xa6, 0x13, 0xe2, 0x2c, \ + 0xe2, 0x30, 0x4e, 0x15, \ + 0xe2, 0x38, 0x4e, 0x25, \ + 0xe2, 0x3c, 0x4e, 0x05, \ + 0xe2, 0x40, 0x4e, 0x05, \ + 0x83, 0x21, 0x98, 0x0e, \ + 0xd8, 0x0e, 0x9a, 0x2e, \ + 0x4c, 0x05, 0xda, 0x1e, \ + 0x4c, 0x25, 0xe0, 0x10, \ + 0x4c, 0x05, 0xe0, 0x14, \ + 0x4c, 0x05, 0xe0, 0x18, \ + 0x02, 0x06, 0xe0, 0x1c, \ + 0x04, 0x70, 0xe0, 0x04, \ + 0x06, 0x70, 0xe4, 0x90, \ + 0xc0, 0x01, 0x66, 0x91, \ + 0xec, 0x04, 0x02, 0x10, \ + 0xc9, 0x0e, 0xa1, 0x1b, \ + 0xf9, 0x00, 0x46, 0x03, \ + 0xfe, 0x35, 0xcd, 0x0c, \ + 0xe2, 0x24, 0x10, 0x05, \ + 0x80, 0xc5, 0xa1, 0x0b, \ + 0x85, 0x11, 0x83, 0x01, \ + 0x9a, 0x1e, 0x98, 0x0e, \ + 0x12, 0x70, 0x85, 0x65, \ + 0xc0, 0x02, 0x64, 0xd1, \ + 0xe4, 0x90, 0x14, 0x80, \ + 0xe4, 0x90, 0x02, 0x70, \ + 0x64, 0x91, 0x16, 0x80, \ + 0x06, 0x70, 0xc0, 0x01, \ + 0xc0, 0x01, 0x64, 0x91, \ + 0x94, 0x0b, 0xbc, 0x6e, \ + 0x92, 0x5b, 0x96, 0x2b, \ + 0xef, 0xfc, 0x57, 0x68, \ + 0xa1, 0x0b, 0x98, 0x04, \ + 0x8b, 0x01, 0x84, 0x15, \ + 0xe2, 0x28, 0x4e, 0x05, \ + 0x66, 0x91, 0x04, 0x80, \ + 0x06, 0x70, 0xc0, 0x01, \ + 0xc0, 0x01, 0x66, 0x91, \ + 0x94, 0x0b, 0xbc, 0x6e, \ + 0x92, 0x5b, 0x96, 0x1b, \ + 0xef, 0xfc, 0x63, 0x48, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x84, 0x7b, 0x86, 0x6b, \ + 0x45, 0x86, 0x82, 0x8b, \ + 0x8e, 0x0b, 0xe0, 0x18, \ + 0xef, 0xf0, 0x73, 0x68, \ + 0x4e, 0x01, 0x89, 0x01, \ + 0x9f, 0x11, 0xe0, 0x14, \ + 0xe0, 0x18, 0x4e, 0x01, \ + 0xe0, 0x1c, 0x4e, 0x11, \ + 0x10, 0x05, 0x8f, 0x11, \ + 0x4e, 0x11, 0xe1, 0xb0, \ + 0xa1, 0x21, 0xe0, 0x20, \ + 0xe0, 0x24, 0x4e, 0x21, \ + 0xe0, 0x28, 0x4e, 0x21, \ + 0xe1, 0x0c, 0x04, 0x04, \ + 0xe1, 0x10, 0x06, 0x04, \ + 0xe1, 0xa8, 0x4e, 0x15, \ + 0x06, 0x04, 0xa6, 0x23, \ + 0xc4, 0x27, 0xe1, 0x04, \ + 0xa4, 0x33, 0x81, 0x01, \ + 0xe1, 0xac, 0x4e, 0x05, \ + 0xe1, 0xb0, 0x4e, 0x35, \ + 0xe1, 0xb8, 0x4e, 0x15, \ + 0xe1, 0xbc, 0x4e, 0x05, \ + 0xe1, 0xc0, 0x4e, 0x05, \ + 0x9a, 0x1e, 0x98, 0x0e, \ + 0xda, 0x3e, 0xd8, 0x0e, \ + 0xe0, 0x10, 0x4c, 0x05, \ + 0xe0, 0x14, 0x4c, 0x15, \ + 0xe0, 0x18, 0x4c, 0x05, \ + 0xe0, 0x1c, 0x4c, 0x05, \ + 0xe0, 0x04, 0x02, 0x06, \ + 0xe4, 0x90, 0x04, 0x70, \ + 0x64, 0x91, 0x06, 0x70, \ + 0x02, 0x10, 0xc0, 0x01, \ + 0xa1, 0x1b, 0xec, 0x04, \ + 0xcd, 0x0c, 0xc9, 0x0e, \ + 0x10, 0x05, 0xfe, 0x75, \ + 0xa1, 0x0b, 0xe1, 0xa4, \ + 0x83, 0x01, 0x80, 0xc5, \ + 0x98, 0x0e, 0x91, 0x11, \ + 0x83, 0x15, 0x9a, 0x1e, \ + 0x66, 0xd1, 0x0a, 0x70, \ + 0x00, 0x80, 0xc0, 0x01, \ + 0x02, 0x70, 0xe4, 0x90, \ + 0x04, 0x80, 0xe4, 0x90, \ + 0xc0, 0x01, 0x64, 0x91, \ + 0x64, 0x91, 0x06, 0x70, \ + 0xbc, 0x6e, 0xc0, 0x01, \ + 0xef, 0xfc, 0x4f, 0x88, \ + 0x0c, 0x0c, 0x90, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x0c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xf0, 0x1e, 0xf0, \ + 0x84, 0x7b, 0x88, 0xab, \ + 0x80, 0xbb, 0x82, 0x9b, \ + 0x80, 0x84, 0xb3, 0x04, \ + 0xa1, 0x8b, 0xad, 0x14, \ + 0xd4, 0x04, 0x8f, 0x5e, \ + 0x95, 0x1e, 0x97, 0x0e, \ + 0x93, 0x0e, 0xc6, 0x0a, \ + 0x10, 0xd6, 0x80, 0x75, \ + 0x8e, 0x18, 0xe0, 0x0c, \ + 0xa8, 0x04, 0xcc, 0x28, \ + 0x83, 0x41, 0xaa, 0x34, \ + 0xa3, 0x4f, 0x81, 0x51, \ + 0xe0, 0x18, 0x4e, 0x53, \ + 0xe8, 0x88, 0x04, 0x44, \ + 0x54, 0x38, 0xa1, 0x11, \ + 0x80, 0x6b, 0xe8, 0x24, \ + 0x9e, 0x74, 0x91, 0x7e, \ + 0x86, 0x25, 0xa1, 0x7b, \ + 0x81, 0x11, 0xac, 0x04, \ + 0xe3, 0xe8, 0x04, 0x06, \ + 0x5e, 0x78, 0x81, 0x31, \ + 0x81, 0x31, 0xef, 0xf0, \ + 0x55, 0x58, 0x8e, 0x2b, \ + 0x04, 0x04, 0xef, 0xf0, \ + 0xd0, 0x17, 0xe1, 0xe0, \ + 0xa1, 0x2b, 0xa2, 0x2f, \ + 0xe0, 0xa0, 0x02, 0x04, \ + 0x80, 0xe5, 0xa4, 0x04, \ + 0xe0, 0x00, 0x04, 0x4a, \ + 0x81, 0x75, 0x80, 0xf5, \ + 0x81, 0x55, 0xa4, 0x04, \ + 0xe4, 0x00, 0x43, 0x4a, \ + 0x82, 0x0b, 0x80, 0x45, \ + 0xee, 0x1a, 0x99, 0x8e, \ + 0x40, 0x0a, 0xa6, 0x34, \ + 0x42, 0x3a, 0xe6, 0x86, \ + 0x04, 0x14, 0xe5, 0x82, \ + 0x04, 0x0a, 0xf6, 0x80, \ + 0x40, 0x3a, 0xe0, 0x80, \ + 0x82, 0x75, 0xe5, 0x02, \ + 0xe1, 0xdc, 0x04, 0x04, \ + 0xe0, 0xa4, 0x06, 0x14, \ + 0x04, 0x24, 0xa6, 0x2f, \ + 0x92, 0x21, 0xf6, 0x80, \ + 0xec, 0x9c, 0x04, 0x22, \ + 0xf6, 0x80, 0x06, 0x64, \ + 0xe0, 0x00, 0x50, 0x06, \ + 0x11, 0x0a, 0xa6, 0x83, \ + 0xfd, 0x71, 0xe4, 0x4c, \ + 0xc0, 0x00, 0x12, 0xd8, \ + 0xe0, 0x00, 0x46, 0x96, \ + 0x86, 0x83, 0xa4, 0x33, \ + 0xe4, 0xa8, 0x10, 0x8a, \ + 0x04, 0x80, 0x82, 0x35, \ + 0x5f, 0xb8, 0xec, 0xa4, \ + 0x04, 0x46, 0xef, 0xf0, \ + 0xb0, 0x23, 0xe0, 0x04, \ + 0xb7, 0x21, 0x81, 0x25, \ + 0x81, 0x81, 0x8d, 0x2e, \ + 0x8c, 0x2b, 0x81, 0x31, \ + 0xef, 0xf0, 0x59, 0x88, \ + 0x12, 0x1b, 0x82, 0x3b, \ + 0xa1, 0x1b, 0xe0, 0x14, \ + 0x44, 0x86, 0x94, 0x85, \ + 0x44, 0x1a, 0xe0, 0x3c, \ + 0x08, 0x24, 0xe6, 0x8a, \ + 0xc8, 0x5b, 0xe1, 0x84, \ + 0x83, 0x3b, 0xff, 0x11, \ + 0xe4, 0x14, 0x41, 0x5a, \ + 0xe1, 0x84, 0x40, 0x0a, \ + 0x46, 0x32, 0x80, 0x43, \ + 0xff, 0x01, 0xf2, 0xa0, \ + 0x88, 0x0b, 0x80, 0x45, \ + 0x80, 0x45, 0x99, 0x64, \ + 0x81, 0x31, 0x86, 0x1b, \ + 0xef, 0xf0, 0x4e, 0xc8, \ + 0x8c, 0x2b, 0x81, 0x31, \ + 0xef, 0xf0, 0x57, 0x68, \ + 0xf6, 0x80, 0x04, 0x14, \ + 0xe0, 0x80, 0x04, 0x0a, \ + 0x06, 0x04, 0x80, 0x95, \ + 0x86, 0x23, 0xf6, 0x80, \ + 0xe4, 0x7c, 0x05, 0x0a, \ + 0x4c, 0x06, 0x8a, 0xd5, \ + 0xa4, 0x63, 0xe0, 0x00, \ + 0xcd, 0x2d, 0x8d, 0x24, \ + 0xc0, 0x00, 0x0e, 0x38, \ + 0x83, 0x31, 0xa1, 0x6b, \ + 0x80, 0x65, 0x81, 0x21, \ + 0xef, 0x98, 0x04, 0x34, \ + 0xe4, 0x80, 0x0c, 0x8a, \ + 0xae, 0xc5, 0x97, 0x74, \ + 0xe5, 0x00, 0x0c, 0x9a, \ + 0xa0, 0x00, 0x0c, 0x48, \ + 0x0c, 0x48, 0x81, 0x41, \ + 0x00, 0x48, 0xc0, 0x00, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0x06, 0x24, 0x81, 0x41, \ + 0xc3, 0x47, 0xe0, 0xfc, \ + 0xc3, 0x27, 0xa6, 0x4f, \ + 0x42, 0x22, 0x88, 0x03, \ + 0x8c, 0x2b, 0xf2, 0x84, \ + 0xef, 0xf0, 0x59, 0xc8, \ + 0x8d, 0x84, 0x8b, 0x0e, \ + 0x81, 0xf5, 0xac, 0x83, \ + 0x97, 0xc5, 0xff, 0x71, \ + 0xe0, 0x00, 0x43, 0xf6, \ + 0xa6, 0x13, 0x8d, 0x34, \ + 0xfb, 0x88, 0x42, 0x12, \ + 0xec, 0x80, 0x10, 0x10, \ + 0x8b, 0x0e, 0xb1, 0x0f, \ + 0x97, 0x74, 0x86, 0x83, \ + 0xaf, 0x64, 0x93, 0x04, \ + 0xe0, 0x2c, 0x0c, 0x35, \ + 0xfa, 0x1c, 0x44, 0x02, \ + 0xf6, 0x80, 0x00, 0x24, \ + 0xec, 0x6c, 0x00, 0x02, \ + 0xe1, 0x84, 0x0a, 0x34, \ + 0xe8, 0x80, 0x18, 0x24, \ + 0xc2, 0xcd, 0xca, 0x1b, \ + 0xe4, 0x14, 0x02, 0x00, \ + 0x80, 0x75, 0xff, 0x41, \ + 0xf0, 0x94, 0x48, 0xc2, \ + 0xe0, 0x80, 0x42, 0x0a, \ + 0xe0, 0x0e, 0x48, 0x4a, \ + 0x82, 0x41, 0x80, 0xa5, \ + 0x02, 0x00, 0xc3, 0x47, \ + 0x95, 0x04, 0xe4, 0x10, \ + 0xf0, 0x0a, 0x4a, 0x4a, \ + 0x12, 0x03, 0x80, 0xca, \ + 0xec, 0x4a, 0xe0, 0x10, \ + 0x4a, 0x5a, 0xc3, 0x17, \ + 0x90, 0x11, 0xe6, 0xb2, \ + 0xe4, 0xe0, 0x18, 0xf0, \ + 0xfc, 0x8e, 0xfe, 0xce, \ + 0xbe, 0xce, 0x8b, 0xc4, \ + 0x8e, 0x1b, 0xbc, 0x1e, \ + 0xe8, 0x00, 0x48, 0x28, \ + 0xa1, 0x7b, 0x80, 0x7b, \ + 0xb1, 0x04, 0x9e, 0x35, \ + 0x14, 0x1b, 0x80, 0x0a, \ + 0x99, 0x24, 0xe0, 0x18, \ + 0xe6, 0x82, 0x40, 0x2a, \ + 0xe1, 0x84, 0x06, 0x14, \ + 0xc4, 0x0d, 0xc6, 0x2b, \ + 0x80, 0x75, 0xff, 0x21, \ + 0xf0, 0x8c, 0x44, 0x02, \ + 0xe0, 0x10, 0x12, 0x03, \ + 0xe0, 0x2c, 0x0c, 0x35, \ + 0x18, 0xf0, 0xec, 0x4a, \ + 0x4a, 0x2a, 0xe4, 0xd0, \ + 0xfe, 0xce, 0xe0, 0x06, \ + 0x89, 0x11, 0xfc, 0x8e, \ + 0xbe, 0x6e, 0x8b, 0x64, \ + 0x97, 0x14, 0xbc, 0x1e, \ + 0x45, 0xd8, 0x82, 0x2b, \ + 0x80, 0x7b, 0xe8, 0x00, \ + 0x94, 0x95, 0xa1, 0x7b, \ + 0x9b, 0x14, 0x9d, 0x04, \ + 0xe0, 0x10, 0x12, 0x23, \ + 0xfb, 0x84, 0x40, 0x02, \ + 0xe9, 0xa0, 0x02, 0x64, \ + 0x6f, 0xfd, 0x14, 0x02, \ + 0x40, 0x1a, 0xc0, 0x3f, \ + 0x42, 0x1a, 0xe5, 0x0a, \ + 0x06, 0x24, 0xe5, 0x8a, \ + 0xc5, 0x27, 0xe1, 0x7c, \ + 0xe0, 0xf8, 0x08, 0x34, \ + 0xc5, 0x37, 0xa8, 0x2f, \ + 0x48, 0xa3, 0x91, 0x44, \ + 0x4b, 0xb8, 0xe0, 0x18, \ + 0x80, 0x6b, 0xef, 0xf0, \ + 0x99, 0x14, 0x82, 0x9b, \ + 0xe5, 0x2a, 0x40, 0x1a, \ + 0xe5, 0xaa, 0x42, 0x1a, \ + 0x94, 0x2b, 0x81, 0x31, \ + 0xef, 0xf0, 0x4b, 0x08, \ + 0xc8, 0x24, 0x8f, 0x24, \ + 0x47, 0xfe, 0x8b, 0x27, \ + 0xc0, 0x07, 0x7f, 0xfd, \ + 0xf1, 0x88, 0x44, 0x32, \ + 0xe1, 0x7c, 0x06, 0x24, \ + 0xef, 0xf0, 0x4a, 0x48, \ + 0xe1, 0x98, 0x04, 0x14, \ + 0x84, 0xa5, 0x85, 0x9b, \ + 0x86, 0x15, 0xc4, 0x9d, \ + 0x04, 0x60, 0x83, 0xb5, \ + 0x04, 0x34, 0xec, 0x80, \ + 0x0c, 0x9a, 0xe8, 0x88, \ + 0x34, 0x18, 0xe5, 0x00, \ + 0xc3, 0xff, 0x3f, 0xfd, \ + 0x81, 0x41, 0x81, 0x21, \ + 0x08, 0x34, 0xce, 0xe5, \ + 0x33, 0xf8, 0xe8, 0x98, \ + 0xe7, 0xe5, 0xbf, 0xfc, \ + 0xcd, 0x07, 0xf4, 0x17, \ + 0xc0, 0x6d, 0xa2, 0x0f, \ + 0x81, 0x01, 0x80, 0xa5, \ + 0x42, 0x03, 0x91, 0x14, \ + 0x8e, 0x0b, 0xe0, 0x18, \ + 0x0c, 0x0c, 0xf8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x86, 0x9b, 0x88, 0x8b, \ + 0x80, 0x6b, 0x82, 0x7b, \ + 0xbe, 0x2e, 0x91, 0x34, \ + 0x00, 0xf0, 0xbc, 0x5e, \ + 0x9e, 0x2b, 0xe4, 0x10, \ + 0x5a, 0x18, 0x8b, 0x11, \ + 0xbe, 0x44, 0xe8, 0x00, \ + 0xc6, 0x4d, 0xf7, 0x31, \ + 0x7f, 0xfd, 0x43, 0xfe, \ + 0x45, 0xfe, 0xc7, 0xff, \ + 0xc7, 0xff, 0x7f, 0xfd, \ + 0x88, 0x41, 0x80, 0x95, \ + 0xe1, 0x8c, 0x04, 0x44, \ + 0xc6, 0x4d, 0xbc, 0x44, \ + 0x88, 0x41, 0x80, 0x95, \ + 0xe1, 0x8c, 0x02, 0x44, \ + 0xe6, 0xa2, 0x46, 0x8a, \ + 0xa1, 0x9b, 0x84, 0x13, \ + 0xe0, 0x88, 0x04, 0x14, \ + 0xe0, 0xa0, 0x02, 0x34, \ + 0xbc, 0x1e, 0xbe, 0x2e, \ + 0x80, 0x00, 0x0f, 0x28, \ + 0xe1, 0x84, 0x04, 0x94, \ + 0xc6, 0x1d, 0xc4, 0x3b, \ + 0xff, 0x31, 0xff, 0xa1, \ + 0x46, 0x12, 0x80, 0x75, \ + 0x42, 0x3a, 0xf0, 0x88, \ + 0xbc, 0x1e, 0xe0, 0x26, \ + 0xec, 0x0c, 0x02, 0x00, \ + 0xe4, 0x10, 0x00, 0xf0, \ + 0xa1, 0x31, 0x9e, 0x2b, \ + 0xe8, 0x00, 0x56, 0xe8, \ + 0x95, 0x3b, 0xbe, 0x34, \ + 0x7f, 0xfd, 0x43, 0xfe, \ + 0x45, 0xfe, 0xdf, 0xff, \ + 0xdf, 0xff, 0x7f, 0xfd, \ + 0x82, 0x31, 0x80, 0x85, \ + 0xe1, 0x84, 0x04, 0x34, \ + 0x95, 0x3b, 0xbc, 0x34, \ + 0x82, 0x31, 0x80, 0x85, \ + 0xe1, 0x84, 0x02, 0x34, \ + 0x46, 0x16, 0x82, 0x23, \ + 0x08, 0x24, 0xe0, 0x00, \ + 0xa0, 0x33, 0xf6, 0x80, \ + 0xfb, 0x90, 0x46, 0x32, \ + 0xe0, 0x80, 0x06, 0x0a, \ + 0x80, 0x55, 0xbc, 0x1e, \ + 0x86, 0x03, 0xbf, 0x31, \ + 0xec, 0x04, 0x02, 0x00, \ + 0xe0, 0x80, 0x42, 0x0a, \ + 0xbe, 0x2e, 0xa7, 0x2f, \ + 0xff, 0x01, 0x82, 0xa5, \ + 0x41, 0xfe, 0x81, 0x2b, \ + 0xdf, 0xff, 0x7f, 0xfd, \ + 0x82, 0x21, 0x80, 0x85, \ + 0xe1, 0x84, 0x00, 0x24, \ + 0x02, 0x10, 0xbe, 0x0e, \ + 0xbe, 0x24, 0xec, 0x04, \ + 0x40, 0x9a, 0xd0, 0x97, \ + 0x44, 0x2a, 0xf0, 0x0a, \ + 0xa1, 0x0b, 0xf0, 0x0a, \ + 0x0a, 0x18, 0xbe, 0x2e, \ + 0x04, 0x84, 0x80, 0x00, \ + 0x06, 0x22, 0xf6, 0x80, \ + 0x04, 0x04, 0xec, 0x70, \ + 0x0a, 0x84, 0xe1, 0x84, \ + 0xc4, 0x4b, 0xe8, 0x8c, \ + 0xff, 0x81, 0xc8, 0x5d, \ + 0x80, 0x75, 0xff, 0x41, \ + 0xf0, 0x88, 0x48, 0x52, \ + 0xe0, 0x02, 0x48, 0x4a, \ + 0x00, 0xf0, 0xc3, 0x17, \ + 0x9e, 0x2b, 0xe4, 0x10, \ + 0x51, 0x98, 0xbc, 0x4e, \ + 0x80, 0x1b, 0xe8, 0x00, \ + 0x91, 0x3b, 0xbe, 0x34, \ + 0x7f, 0xfd, 0x41, 0xfe, \ + 0x45, 0xfe, 0xdf, 0xff, \ + 0xdf, 0xff, 0x7f, 0xfd, \ + 0x82, 0x31, 0x80, 0x85, \ + 0xe1, 0x84, 0x04, 0x34, \ + 0x91, 0x3b, 0xbc, 0x34, \ + 0x82, 0x31, 0x80, 0x85, \ + 0xe1, 0x84, 0x00, 0x34, \ + 0x44, 0x0a, 0x84, 0x03, \ + 0x08, 0x24, 0xe5, 0x82, \ + 0xbe, 0x0e, 0xf6, 0x80, \ + 0xe4, 0x7c, 0x09, 0x0a, \ + 0xe5, 0x02, 0x40, 0x0a, \ + 0x10, 0x10, 0xc2, 0x17, \ + 0xbc, 0x2e, 0xec, 0x08, \ + 0x52, 0x06, 0x92, 0x95, \ + 0xa8, 0x93, 0xe0, 0x00, \ + 0x83, 0x31, 0xa1, 0x9b, \ + 0x86, 0xa5, 0x81, 0x11, \ + 0xef, 0xa4, 0x02, 0x34, \ + 0xe4, 0x80, 0x12, 0x8a, \ + 0x12, 0x9a, 0x86, 0x65, \ + 0x01, 0xf8, 0xe5, 0x00, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0x81, 0x41, 0x81, 0x11, \ + 0x00, 0x48, 0x86, 0x95, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0x06, 0x14, 0x81, 0x41, \ + 0xc6, 0x5b, 0xe1, 0x84, \ + 0xc3, 0x47, 0x8b, 0x2b, \ + 0xa8, 0x1f, 0xfe, 0x17, \ + 0x45, 0x4a, 0x84, 0xa5, \ + 0x83, 0x55, 0xe4, 0x14, \ + 0xe4, 0x80, 0x12, 0x8a, \ + 0xa8, 0x1b, 0xf8, 0xf5, \ + 0xe8, 0x84, 0x02, 0x34, \ + 0xe5, 0x00, 0x12, 0x9a, \ + 0xbf, 0xfc, 0x3e, 0x68, \ + 0xf8, 0xc5, 0x81, 0x41, \ + 0xe8, 0xa4, 0x08, 0x34, \ + 0xbf, 0xfc, 0x3e, 0x68, \ + 0xc2, 0x4b, 0xfd, 0x55, \ + 0xe4, 0x10, 0x41, 0x4a, \ + 0x44, 0x32, 0x80, 0x13, \ + 0xff, 0xa1, 0xf2, 0x88, \ + 0x80, 0x45, 0xff, 0x01, \ + 0xff, 0x11, 0x82, 0x0b, \ + 0x84, 0x1b, 0x80, 0x45, \ + 0x7d, 0x78, 0x92, 0x2b, \ + 0x95, 0x0b, 0xef, 0xec, \ + 0xff, 0x01, 0x80, 0x55, \ + 0x80, 0x45, 0xa1, 0x1b, \ + 0xb2, 0x83, 0xff, 0x01, \ + 0xbc, 0x0e, 0xc4, 0x77, \ + 0x00, 0xf0, 0xbe, 0x7e, \ + 0x9e, 0x2b, 0xe4, 0x10, \ + 0x90, 0x3b, 0x8b, 0x11, \ + 0xe8, 0x00, 0x4a, 0x28, \ + 0xbe, 0x14, 0xbc, 0x24, \ + 0xa1, 0x2b, 0xa2, 0x23, \ + 0x82, 0x65, 0xbe, 0x2e, \ + 0x06, 0x8a, 0x95, 0x34, \ + 0x93, 0x14, 0xe4, 0x80, \ + 0x81, 0x51, 0x82, 0x15, \ + 0xe0, 0x00, 0x4e, 0x06, \ + 0x85, 0x45, 0x82, 0x4b, \ + 0x91, 0xb5, 0xff, 0x01, \ + 0xe0, 0x00, 0x48, 0x06, \ + 0xe9, 0x8c, 0x0a, 0x14, \ + 0x0e, 0x54, 0xa6, 0x43, \ + 0x88, 0x73, 0xf6, 0x80, \ + 0x81, 0x41, 0xa1, 0x7b, \ + 0x08, 0x14, 0x90, 0x25, \ + 0x0e, 0x8a, 0xef, 0x9c, \ + 0x8e, 0xe5, 0xe4, 0x80, \ + 0xe5, 0x00, 0x0e, 0x9a, \ + 0x20, 0x01, 0x04, 0x48, \ + 0x81, 0x41, 0xc0, 0x00, \ + 0x90, 0x35, 0x81, 0x51, \ + 0x20, 0x01, 0x04, 0x48, \ + 0x81, 0x51, 0xc0, 0x00, \ + 0xe1, 0x00, 0x0e, 0x0a, \ + 0x80, 0x55, 0x90, 0x25, \ + 0x18, 0x24, 0x81, 0x41, \ + 0xd8, 0x8b, 0xe1, 0x84, \ + 0x83, 0x4b, 0xff, 0x11, \ + 0xe4, 0x20, 0x4b, 0x5a, \ + 0xe1, 0x84, 0x40, 0x0a, \ + 0x8a, 0xc3, 0x81, 0x81, \ + 0xf2, 0xa0, 0x4a, 0x42, \ + 0x80, 0x45, 0xff, 0x41, \ + 0x50, 0x32, 0x98, 0x4b, \ + 0x80, 0x45, 0xf1, 0x80, \ + 0x81, 0x31, 0x8a, 0x1b, \ + 0x69, 0x58, 0x88, 0x0b, \ + 0x04, 0x14, 0xef, 0xec, \ + 0x05, 0x0a, 0xf6, 0x80, \ + 0xbc, 0x1e, 0xe4, 0x7c, \ + 0x88, 0x95, 0x8e, 0x83, \ + 0xe0, 0x00, 0x4e, 0x06, \ + 0x8e, 0x2b, 0xa4, 0x73, \ + 0xef, 0xec, 0x76, 0xe8, \ + 0x86, 0x65, 0xcf, 0x8d, \ + 0x81, 0x81, 0xbc, 0x0e, \ + 0x0e, 0x8a, 0x85, 0x95, \ + 0xf0, 0x75, 0xe4, 0x80, \ + 0xec, 0x80, 0x08, 0x70, \ + 0xe8, 0x90, 0x08, 0x14, \ + 0xe5, 0x00, 0x0e, 0x9a, \ + 0xbf, 0xfc, 0x3c, 0x18, \ + 0xf0, 0x25, 0x81, 0x51, \ + 0xe8, 0x9c, 0x0a, 0x14, \ + 0xbf, 0xfc, 0x3c, 0x18, \ + 0xe1, 0x00, 0x0e, 0x0a, \ + 0x81, 0x51, 0xf0, 0x35, \ + 0xf9, 0x05, 0xf0, 0x05, \ + 0x50, 0x0a, 0xae, 0x83, \ + 0xbc, 0x0e, 0xe0, 0x80, \ + 0xff, 0x11, 0x82, 0xa5, \ + 0x43, 0xfe, 0x83, 0x0b, \ + 0xdf, 0xff, 0x7f, 0xfd, \ + 0x82, 0x01, 0x80, 0x85, \ + 0xe1, 0x84, 0x02, 0x04, \ + 0x10, 0x80, 0xbc, 0x1e, \ + 0xbc, 0x04, 0xec, 0x04, \ + 0xf0, 0x0a, 0x40, 0x0a, \ + 0x85, 0x11, 0xc3, 0x87, \ + 0x44, 0x0a, 0xb1, 0x1f, \ + 0x06, 0x14, 0xe6, 0x9a, \ + 0xc6, 0x0b, 0xe1, 0x84, \ + 0x97, 0x04, 0xc0, 0x2d, \ + 0x02, 0x80, 0xbc, 0x2e, \ + 0xff, 0x41, 0xe4, 0x04, \ + 0x48, 0x22, 0x80, 0x75, \ + 0xe3, 0x4f, 0xf0, 0x8c, \ + 0x81, 0x01, 0x80, 0x4e, \ + 0x0c, 0x0c, 0x90, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0xf8, 0x04, 0x04, 0x0c, \ + 0x88, 0x54, 0x80, 0x44, \ + 0xe0, 0x00, 0x58, 0x06, \ + 0xf6, 0x80, 0x0c, 0x44, \ + 0xf1, 0x84, 0x4e, 0xc2, \ + 0xf6, 0x80, 0x0a, 0x54, \ + 0xfb, 0x98, 0x4c, 0x72, \ + 0x0d, 0x0a, 0xa6, 0xc3, \ + 0x4e, 0xc2, 0xe4, 0x7c, \ + 0x81, 0x51, 0xfb, 0x94, \ + 0x80, 0x75, 0x81, 0xc1, \ + 0xe8, 0x98, 0x18, 0x44, \ + 0xe4, 0x7c, 0x0f, 0x0a, \ + 0x80, 0x75, 0x80, 0xce, \ + 0xaf, 0x5f, 0x88, 0x54, \ + 0xf0, 0x84, 0x58, 0x62, \ + 0xf0, 0x8c, 0x48, 0x72, \ + 0x88, 0x5e, 0xc8, 0xcd, \ + 0xa6, 0xc3, 0x84, 0xc5, \ + 0xf1, 0x9c, 0x42, 0xc2, \ + 0xe4, 0x7c, 0x03, 0x0a, \ + 0x82, 0xb5, 0x81, 0x31, \ + 0x80, 0x24, 0x83, 0x31, \ + 0x0a, 0x34, 0xa3, 0x3f, \ + 0xca, 0x3b, 0xe1, 0x84, \ + 0xff, 0x31, 0xc6, 0x2d, \ + 0x46, 0x22, 0x80, 0x75, \ + 0xe3, 0x3f, 0xf0, 0x94, \ + 0x83, 0x55, 0x80, 0x3e, \ + 0x40, 0x42, 0xa2, 0x43, \ + 0x01, 0x0a, 0xf1, 0x98, \ + 0x81, 0x11, 0xe4, 0x7c, \ + 0x83, 0x11, 0x82, 0x95, \ + 0x06, 0x14, 0xa1, 0x1f, \ + 0xc6, 0x1b, 0xe1, 0x84, \ + 0xff, 0x11, 0xc2, 0x5d, \ + 0x42, 0x52, 0x80, 0x75, \ + 0xe1, 0x1f, 0xf0, 0x8c, \ + 0x98, 0x4b, 0x88, 0x1e, \ + 0x0c, 0x0c, 0x88, 0x0b, \ + 0xdd, 0x93, 0xf0, 0x04, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0x84, 0x13, 0x84, 0x03, \ + 0xe1, 0x8a, 0x40, 0x0a, \ + 0x42, 0x1a, 0x82, 0x11, \ + 0x04, 0x20, 0xe1, 0x8a, \ + 0x18, 0x04, 0xec, 0x04, \ + 0x08, 0xc5, 0xe0, 0x88, \ + 0xca, 0xc7, 0xfc, 0x30, \ + 0xe0, 0x8c, 0x0c, 0x04, \ + 0xf0, 0x98, 0x4e, 0x52, \ + 0xfc, 0x18, 0x0a, 0x65, \ + 0x9e, 0x84, 0xa6, 0xc3, \ + 0xe1, 0x7c, 0x12, 0xc4, \ + 0x18, 0x04, 0x98, 0x63, \ + 0x50, 0x82, 0xe0, 0x84, \ + 0x8a, 0xc3, 0xf2, 0xa4, \ + 0xe9, 0x00, 0x18, 0x9b, \ + 0x9e, 0x8e, 0x85, 0x0d, \ + 0x9c, 0x6e, 0x82, 0x91, \ + 0xe9, 0x00, 0x58, 0x93, \ + 0x81, 0x01, 0x80, 0xb5, \ + 0xf8, 0x15, 0x83, 0x0b, \ + 0x82, 0x01, 0x81, 0x45, \ + 0xf6, 0x95, 0x83, 0x0b, \ + 0xf0, 0x1c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0x61, 0xfc, 0x18, 0x24, \ + 0x04, 0xc3, 0xa2, 0x03, \ + 0x03, 0xc7, 0x86, 0x03, \ + 0x0b, 0xc4, 0x81, 0x81, \ + 0x4c, 0x0a, 0xb8, 0x03, \ + 0x00, 0x34, 0x61, 0x8e, \ + 0x09, 0x04, 0xe0, 0x88, \ + 0xf0, 0x80, 0x4e, 0x42, \ + 0x0c, 0x13, 0x94, 0x8e, \ + 0x42, 0x1a, 0xc4, 0x67, \ + 0x88, 0x63, 0x61, 0x8e, \ + 0xb0, 0x8e, 0x80, 0x8e, \ + 0xc4, 0x17, 0x02, 0x23, \ + 0x61, 0x8e, 0x46, 0x2a, \ + 0xf0, 0x84, 0x44, 0x42, \ + 0x42, 0x42, 0xc4, 0x37, \ + 0x06, 0x70, 0x70, 0x8c, \ + 0x82, 0x7b, 0xec, 0x10, \ + 0x14, 0x94, 0xd9, 0x84, \ + 0x10, 0x93, 0xc6, 0x6d, \ + 0xdc, 0x00, 0x4c, 0x40, \ + 0xdd, 0x84, 0x94, 0x9e, \ + 0xc6, 0x7d, 0x30, 0x94, \ + 0x4e, 0x40, 0x10, 0x93, \ + 0x30, 0x9e, 0xdc, 0x00, \ + 0xfc, 0x35, 0x85, 0x6b, \ + 0xc9, 0xc4, 0x80, 0x54, \ + 0x98, 0x53, 0xc6, 0x2d, \ + 0x44, 0x40, 0x00, 0x5e, \ + 0x83, 0x2b, 0xdc, 0x00, \ + 0x0c, 0x0c, 0xfe, 0x35, \ + 0xdd, 0x93, 0xf0, 0x1c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0x70, 0x80, 0x58, 0x52, \ + 0xf0, 0x94, 0x50, 0x12, \ + 0x82, 0x81, 0x18, 0x23, \ + 0x61, 0x96, 0x4e, 0x2a, \ + 0xe0, 0x88, 0x0c, 0x54, \ + 0x0c, 0x60, 0x0d, 0x24, \ + 0x58, 0xca, 0xec, 0x10, \ + 0x0f, 0x04, 0xe1, 0x96, \ + 0x52, 0x3a, 0x8e, 0x33, \ + 0x02, 0x74, 0x61, 0x96, \ + 0x50, 0x8a, 0xe0, 0x88, \ + 0x46, 0x22, 0x61, 0x96, \ + 0x42, 0x22, 0xf0, 0x84, \ + 0x81, 0x51, 0x70, 0x98, \ + 0x0e, 0xc4, 0x13, 0x7b, \ + 0x02, 0xc5, 0xe0, 0x88, \ + 0x44, 0x97, 0x86, 0xcb, \ + 0x58, 0x22, 0x81, 0x51, \ + 0x86, 0x9b, 0x70, 0xa4, \ + 0xc2, 0x9d, 0xe5, 0xa4, \ + 0x52, 0x20, 0x94, 0x53, \ + 0x99, 0x9b, 0xdc, 0x00, \ + 0x23, 0x4d, 0xfe, 0x55, \ + 0x52, 0x02, 0xc4, 0x87, \ + 0x80, 0x63, 0x70, 0x9c, \ + 0x80, 0x00, 0x12, 0x75, \ + 0x4e, 0x72, 0x02, 0x65, \ + 0xff, 0x71, 0x90, 0x94, \ + 0x60, 0x1c, 0x49, 0x1a, \ + 0x12, 0x75, 0x8a, 0x7b, \ + 0x4e, 0x72, 0xa0, 0x00, \ + 0x65, 0x7e, 0xb1, 0x94, \ + 0xf0, 0xa0, 0x4e, 0x02, \ + 0x64, 0x18, 0x53, 0x1a, \ + 0x50, 0x90, 0x80, 0x8b, \ + 0x50, 0x1a, 0xbc, 0x00, \ + 0x02, 0xa8, 0xe0, 0x1c, \ + 0xff, 0x91, 0xa0, 0x00, \ + 0xcd, 0xb4, 0xf1, 0xa4, \ + 0x4a, 0xa2, 0x8a, 0xa3, \ + 0x10, 0xa5, 0xf1, 0xac, \ + 0x02, 0x45, 0x80, 0x00, \ + 0x90, 0x94, 0x54, 0xa2, \ + 0x60, 0x24, 0x49, 0x1a, \ + 0x10, 0xa5, 0x8a, 0xab, \ + 0x54, 0xa2, 0xa0, 0x00, \ + 0x61, 0xae, 0xb1, 0x94, \ + 0xe4, 0x04, 0x59, 0x1a, \ + 0x3c, 0x01, 0x58, 0x20, \ + 0x47, 0x1a, 0x40, 0x00, \ + 0x46, 0x20, 0xe4, 0x04, \ + 0x40, 0x00, 0x3c, 0x01, \ + 0xe4, 0x18, 0x51, 0x1a, \ + 0x54, 0x80, 0x80, 0xab, \ + 0x54, 0x1a, 0xbc, 0x00, \ + 0x94, 0x8b, 0x60, 0x1c, \ + 0x3f, 0xfd, 0x3d, 0xc8, \ + 0x81, 0x01, 0xc3, 0xff, \ + 0xf0, 0x7c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x00, 0x84, 0x80, 0x8b, \ + 0x90, 0x03, 0xe1, 0xfc, \ + 0x14, 0x04, 0xca, 0x37, \ + 0x94, 0x33, 0xe1, 0x04, \ + 0xe0, 0xa2, 0x40, 0x3a, \ + 0xe1, 0x7c, 0x18, 0x04, \ + 0xe1, 0x7c, 0x06, 0x54, \ + 0x56, 0xc2, 0xaa, 0x03, \ + 0x46, 0x02, 0xf3, 0x8c, \ + 0xa1, 0x3b, 0xf9, 0xac, \ + 0x90, 0x25, 0x97, 0x34, \ + 0xfc, 0x3e, 0xbe, 0x1e, \ + 0xe1, 0xfc, 0x0c, 0x04, \ + 0xe0, 0x84, 0x06, 0xb4, \ + 0xe1, 0x7c, 0x0a, 0x44, \ + 0xe1, 0x7c, 0x18, 0x24, \ + 0x4a, 0x52, 0xa4, 0x43, \ + 0xff, 0x21, 0xf3, 0xb0, \ + 0x84, 0x5d, 0x81, 0x71, \ + 0xe1, 0xc0, 0x12, 0x44, \ + 0xf1, 0x80, 0x44, 0x72, \ + 0x42, 0x72, 0xe0, 0x57, \ + 0x80, 0xcb, 0xf3, 0xac, \ + 0x84, 0xcb, 0x80, 0x55, \ + 0x04, 0x04, 0xac, 0x3f, \ + 0xb2, 0x5f, 0xe0, 0x84, \ + 0x80, 0x55, 0xe0, 0x47, \ + 0x40, 0xc2, 0x82, 0xbb, \ + 0x42, 0xb2, 0xf0, 0x90, \ + 0x95, 0xb4, 0xf2, 0x94, \ + 0xef, 0xec, 0x41, 0x08, \ + 0xb4, 0x83, 0xc2, 0xa7, \ + 0x80, 0x95, 0xa3, 0x8b, \ + 0x64, 0x01, 0x00, 0x00, \ + 0xfc, 0x34, 0xc0, 0x04, \ + 0xd6, 0x17, 0xbe, 0x14, \ + 0xd6, 0xb7, 0x82, 0x03, \ + 0xe1, 0x7c, 0x02, 0x04, \ + 0x80, 0x13, 0x96, 0x1f, \ + 0xe1, 0xae, 0x40, 0x1a, \ + 0xe0, 0x00, 0x00, 0x02, \ + 0x6b, 0xfd, 0x00, 0x02, \ + 0x81, 0x55, 0xc7, 0xff, \ + 0x7f, 0xfd, 0x41, 0xfe, \ + 0xff, 0x71, 0xc0, 0x01, \ + 0x8e, 0x0b, 0x8c, 0x0e, \ + 0x0c, 0x0c, 0x98, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xd0, 0x1e, 0xf0, \ + 0x8f, 0x4e, 0x91, 0x5e, \ + 0x8d, 0x1e, 0x84, 0x9b, \ + 0x93, 0x3e, 0x8b, 0x0e, \ + 0xa1, 0x0b, 0x8c, 0x08, \ + 0x95, 0x0e, 0xaf, 0x04, \ + 0x99, 0x0e, 0x81, 0x01, \ + 0x84, 0x65, 0x97, 0x0e, \ + 0x84, 0x18, 0x93, 0x14, \ + 0x04, 0x20, 0x95, 0x24, \ + 0x81, 0x31, 0xe5, 0x80, \ + 0x44, 0x05, 0x82, 0x31, \ + 0xc3, 0x3d, 0xf8, 0x60, \ + 0xf8, 0x30, 0x44, 0x05, \ + 0x44, 0x05, 0xc9, 0x0e, \ + 0x44, 0x05, 0xe0, 0x2c, \ + 0xfc, 0xb5, 0xe0, 0x5c, \ + 0x82, 0x08, 0x93, 0x04, \ + 0x9a, 0xa5, 0xa1, 0x0b, \ + 0xab, 0xa4, 0xad, 0x74, \ + 0x81, 0x81, 0xa9, 0xb4, \ + 0xe0, 0x84, 0x0a, 0x84, \ + 0x80, 0x53, 0x8f, 0x04, \ + 0xf0, 0x90, 0x0a, 0xc3, \ + 0x80, 0xcd, 0x94, 0x02, \ + 0x02, 0x84, 0x88, 0xb5, \ + 0x95, 0x04, 0xe0, 0x88, \ + 0x06, 0x10, 0x80, 0x13, \ + 0x08, 0x10, 0xe4, 0xc0, \ + 0x94, 0x02, 0xe6, 0x40, \ + 0x0a, 0xc0, 0x89, 0xce, \ + 0x58, 0xb2, 0xec, 0x04, \ + 0x82, 0xc1, 0xf0, 0x94, \ + 0xe1, 0xae, 0x58, 0xca, \ + 0x14, 0x65, 0xc4, 0xc7, \ + 0x8c, 0x24, 0xfc, 0x30, \ + 0x8c, 0x2e, 0x8c, 0x23, \ + 0x84, 0x64, 0xe4, 0x24, \ + 0x84, 0x6e, 0x84, 0x63, \ + 0xfc, 0x30, 0x0e, 0x25, \ + 0x82, 0x51, 0x90, 0xc4, \ + 0x84, 0xc3, 0x81, 0x5d, \ + 0xfa, 0x55, 0x90, 0xce, \ + 0x0c, 0x84, 0x89, 0xc4, \ + 0x95, 0x14, 0xe0, 0x88, \ + 0xfc, 0x18, 0x02, 0x25, \ + 0x0c, 0x15, 0x82, 0x63, \ + 0xa0, 0x21, 0xe0, 0x30, \ + 0xe1, 0x94, 0x08, 0x24, \ + 0x83, 0x31, 0xc8, 0x1d, \ + 0x98, 0x4e, 0xb8, 0x33, \ + 0x44, 0x12, 0x80, 0xa5, \ + 0x4c, 0x25, 0xf1, 0x90, \ + 0x8b, 0x24, 0xe0, 0x60, \ + 0xe0, 0x30, 0x04, 0x29, \ + 0x80, 0x33, 0xa1, 0x2b, \ + 0x81, 0x01, 0x80, 0x95, \ + 0xe0, 0x90, 0x4c, 0x05, \ + 0x0c, 0x25, 0xa6, 0x04, \ + 0x8d, 0x44, 0xe0, 0x90, \ + 0x08, 0xf0, 0xd0, 0x58, \ + 0xfc, 0x4e, 0xe4, 0xb0, \ + 0xe4, 0xc0, 0x08, 0xf0, \ + 0xfa, 0x80, 0x18, 0xb4, \ + 0xfb, 0x00, 0x06, 0x34, \ + 0xbc, 0xce, 0xbe, 0x4e, \ + 0x64, 0x98, 0x91, 0x44, \ + 0xa1, 0x0b, 0xe8, 0x00, \ + 0x97, 0x14, 0x80, 0x95, \ + 0xe0, 0xc0, 0x4c, 0x15, \ + 0x86, 0x18, 0x93, 0x14, \ + 0xc3, 0x8d, 0x82, 0x81, \ + 0x81, 0x25, 0xe6, 0x55, \ + 0xe8, 0xf1, 0x81, 0x01, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xed, 0xd0, 0x1e, 0xf0, \ + 0x95, 0x2e, 0x97, 0x5e, \ + 0xa5, 0x0e, 0x93, 0x1e, \ + 0xe0, 0xac, 0x1e, 0x15, \ + 0x42, 0x03, 0x81, 0x01, \ + 0x42, 0x03, 0xe1, 0x4c, \ + 0x42, 0x03, 0xe1, 0x48, \ + 0x42, 0x03, 0xe1, 0x44, \ + 0x42, 0x03, 0xe1, 0x40, \ + 0x42, 0x03, 0xe1, 0x3c, \ + 0x42, 0x03, 0xe1, 0x38, \ + 0x42, 0x03, 0xe1, 0x34, \ + 0x42, 0x03, 0xe1, 0x30, \ + 0x42, 0x03, 0xe1, 0x2c, \ + 0x42, 0x03, 0xe1, 0x28, \ + 0x42, 0x03, 0xe1, 0x24, \ + 0x8e, 0x28, 0xe1, 0x20, \ + 0xb7, 0x0e, 0xb9, 0x0e, \ + 0xb3, 0x0e, 0xb5, 0x0e, \ + 0xb1, 0x0e, 0xa1, 0x2b, \ + 0xad, 0x0e, 0xaf, 0x0e, \ + 0x0c, 0xf8, 0xab, 0x0e, \ + 0x1e, 0x05, 0x80, 0x00, \ + 0x1e, 0x25, 0xe0, 0xa8, \ + 0x91, 0x2e, 0xe0, 0xa4, \ + 0xe0, 0xa0, 0x1e, 0x55, \ + 0xe0, 0x9c, 0x1e, 0xb5, \ + 0xe0, 0x98, 0x1e, 0x25, \ + 0x1e, 0x25, 0xa1, 0x2e, \ + 0x8f, 0x2e, 0xe0, 0x94, \ + 0xe0, 0x90, 0x1e, 0x25, \ + 0x0c, 0xc0, 0xa5, 0xc4, \ + 0xc0, 0x02, 0x65, 0x01, \ + 0x18, 0xc0, 0x9f, 0x6e, \ + 0xc0, 0x04, 0x66, 0x01, \ + 0x12, 0x10, 0x99, 0xce, \ + 0x14, 0x00, 0xe6, 0x40, \ + 0x0c, 0x00, 0xe4, 0xc0, \ + 0x10, 0x40, 0xe6, 0x40, \ + 0xff, 0x41, 0xe5, 0x80, \ + 0xff, 0xc1, 0x81, 0x71, \ + 0xa3, 0x3e, 0x81, 0x01, \ + 0xa7, 0xbe, 0x9d, 0x5e, \ + 0x8d, 0xc5, 0x9b, 0x2e, \ + 0xf8, 0xc0, 0x10, 0x03, \ + 0xf8, 0x30, 0x10, 0x13, \ + 0xa7, 0xb4, 0x81, 0x21, \ + 0x91, 0x44, 0x96, 0x3b, \ + 0x6d, 0x98, 0x99, 0x54, \ + 0xa1, 0x0b, 0xe8, 0x00, \ + 0x9d, 0x54, 0xa3, 0x34, \ + 0xff, 0x41, 0x9b, 0x24, \ + 0xa0, 0x75, 0xa9, 0xc4, \ + 0x96, 0x03, 0x98, 0x02, \ + 0x56, 0x0a, 0xa0, 0x12, \ + 0x00, 0xf0, 0xe1, 0xae, \ + 0x06, 0xf0, 0xe5, 0x80, \ + 0xa9, 0x3e, 0xe5, 0xb0, \ + 0xe5, 0x90, 0x0a, 0xf0, \ + 0xbc, 0x3e, 0xbe, 0x0e, \ + 0xa7, 0x34, 0x96, 0x0b, \ + 0x5c, 0x38, 0x9f, 0x44, \ + 0xa0, 0x12, 0xef, 0xfc, \ + 0xe5, 0xa0, 0x00, 0xf0, \ + 0xe5, 0xc0, 0x0a, 0xf0, \ + 0xa9, 0x04, 0xbe, 0x0e, \ + 0xa7, 0x24, 0xbc, 0x0e, \ + 0x96, 0x0b, 0xa9, 0xbe, \ + 0x9b, 0x24, 0x84, 0xbb, \ + 0xa5, 0x44, 0x96, 0x3b, \ + 0xef, 0xfc, 0x5b, 0x28, \ + 0x00, 0x09, 0x95, 0x04, \ + 0xa1, 0x0b, 0xe0, 0x30, \ + 0xa0, 0x12, 0x84, 0x05, \ + 0xe5, 0x50, 0x00, 0xf0, \ + 0xe5, 0x60, 0x04, 0xf0, \ + 0xe5, 0x70, 0x0a, 0xf0, \ + 0xbc, 0x2e, 0xbe, 0x0e, \ + 0x9b, 0x24, 0xa9, 0x04, \ + 0x99, 0x44, 0x96, 0x3b, \ + 0xef, 0xfc, 0x59, 0xe8, \ + 0x80, 0x0a, 0x93, 0x04, \ + 0xb9, 0x24, 0xa9, 0x0e, \ + 0xb5, 0x44, 0xb7, 0x34, \ + 0xab, 0x04, 0xb3, 0x54, \ + 0xaf, 0xb4, 0xb1, 0xc4, \ + 0xfe, 0x0e, 0xad, 0x14, \ + 0xa7, 0xb4, 0xfc, 0xbe, \ + 0x97, 0x04, 0x8d, 0x9e, \ + 0x8f, 0x04, 0x8b, 0x0e, \ + 0xbe, 0x1e, 0x89, 0x0e, \ + 0x9b, 0x04, 0xbc, 0xce, \ + 0x62, 0xd8, 0xa9, 0x14, \ + 0x80, 0xcb, 0xe8, 0x14, \ + 0xa3, 0x34, 0x81, 0x01, \ + 0xff, 0x41, 0x9d, 0x54, \ + 0x8c, 0x35, 0x89, 0xcb, \ + 0xa8, 0x14, 0x85, 0xc5, \ + 0x63, 0xfd, 0x02, 0x0a, \ + 0x8a, 0x65, 0xc7, 0xff, \ + 0x88, 0x75, 0xa1, 0x0b, \ + 0x10, 0x03, 0xa9, 0xce, \ + 0x10, 0x13, 0xf8, 0xc0, \ + 0xa1, 0x24, 0xf8, 0x30, \ + 0x8a, 0x4b, 0x96, 0x3b, \ + 0xa5, 0x54, 0x8a, 0xbb, \ + 0xe8, 0x00, 0x66, 0x68, \ + 0x80, 0x85, 0xa1, 0x0b, \ + 0x96, 0x5b, 0xa3, 0x34, \ + 0x10, 0x03, 0x81, 0xf5, \ + 0x10, 0x13, 0xf8, 0xc0, \ + 0x81, 0x21, 0xf8, 0x30, \ + 0x96, 0x4b, 0xa7, 0x34, \ + 0x65, 0x78, 0x9f, 0x54, \ + 0xa1, 0x0b, 0xe8, 0x00, \ + 0xa3, 0x34, 0xdc, 0x45, \ + 0xa7, 0xb4, 0x9d, 0x54, \ + 0xa9, 0xc4, 0xff, 0x41, \ + 0x80, 0xb5, 0x89, 0xcb, \ + 0xef, 0xfc, 0x43, 0xf6, \ + 0xff, 0xc1, 0xa4, 0x1c, \ + 0x82, 0x71, 0x8e, 0x18, \ + 0x84, 0x91, 0xc3, 0x7d, \ + 0x84, 0x61, 0x88, 0xa1, \ + 0xf2, 0x55, 0x84, 0x81, \ + 0xe5, 0xd0, 0x1e, 0xf0, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x86, 0x6b, 0x88, 0xbb, \ + 0x82, 0x8b, 0xbe, 0x2e, \ + 0x14, 0x50, 0x80, 0x9b, \ + 0x81, 0x71, 0xe6, 0xa0, \ + 0x04, 0x86, 0x81, 0x11, \ + 0x94, 0x0b, 0xe0, 0x04, \ + 0xef, 0xec, 0x49, 0x08, \ + 0x9a, 0x08, 0x89, 0x6e, \ + 0x8c, 0x45, 0xa1, 0x0b, \ + 0xbe, 0x64, 0xe0, 0xb1, \ + 0x00, 0x0a, 0xac, 0x04, \ + 0xc7, 0xff, 0x63, 0xfd, \ + 0x16, 0x15, 0x88, 0x85, \ + 0xa0, 0x24, 0xf8, 0x30, \ + 0xe0, 0x42, 0xa4, 0x3a, \ + 0x8c, 0x0b, 0xe4, 0x58, \ + 0xe8, 0x00, 0x42, 0x88, \ + 0xac, 0x14, 0xfe, 0x0e, \ + 0xa4, 0x3a, 0xa0, 0x24, \ + 0xe4, 0x58, 0xe0, 0x42, \ + 0x41, 0xf8, 0x8c, 0x0b, \ + 0xfc, 0x0e, 0xe8, 0x00, \ + 0xe0, 0x30, 0x16, 0x15, \ + 0xa4, 0x3a, 0xa0, 0x24, \ + 0xe4, 0x58, 0xe0, 0x42, \ + 0x41, 0x58, 0x8c, 0x0b, \ + 0xfc, 0x14, 0xe8, 0x00, \ + 0xfe, 0x14, 0xa8, 0x1c, \ + 0xe0, 0x30, 0x54, 0x13, \ + 0xe0, 0x60, 0x54, 0x03, \ + 0x82, 0x08, 0x89, 0x04, \ + 0xc1, 0x7d, 0x82, 0x71, \ + 0x84, 0xa1, 0x88, 0xb1, \ + 0x81, 0x01, 0xf4, 0x55, \ + 0x0c, 0x0c, 0xa8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xf8, 0x04, 0x04, 0x0c, \ + 0x58, 0x1a, 0xa4, 0x13, \ + 0xff, 0x21, 0xe4, 0x82, \ + 0x40, 0x1a, 0x84, 0xcd, \ + 0x02, 0x0e, 0xe4, 0x02, \ + 0x0c, 0x0e, 0xf8, 0x00, \ + 0x80, 0x65, 0xf8, 0x00, \ + 0xe8, 0x00, 0x0c, 0x06, \ + 0xe1, 0x7c, 0x0e, 0xc4, \ + 0x58, 0x72, 0x80, 0x63, \ + 0x00, 0xc4, 0xf2, 0xb0, \ + 0x0e, 0x04, 0xe1, 0x7c, \ + 0x81, 0x01, 0xe1, 0xc0, \ + 0x58, 0xc2, 0x8e, 0x63, \ + 0xe0, 0xc7, 0xf2, 0x80, \ + 0xb8, 0x6f, 0xe1, 0x67, \ + 0x48, 0x6a, 0x88, 0x63, \ + 0x84, 0x4d, 0xe4, 0x8e, \ + 0xe4, 0x0e, 0x44, 0x6a, \ + 0xe0, 0x80, 0x06, 0x06, \ + 0x06, 0x0e, 0x80, 0x75, \ + 0x18, 0x44, 0xff, 0x80, \ + 0x84, 0x33, 0xe1, 0x7c, \ + 0xf2, 0x90, 0x44, 0xc2, \ + 0xe1, 0x7c, 0x08, 0x24, \ + 0x88, 0x33, 0xe9, 0x47, \ + 0xf2, 0x80, 0x44, 0x22, \ + 0xd9, 0x37, 0xe8, 0x27, \ + 0x42, 0x32, 0xa4, 0x3f, \ + 0x02, 0x12, 0xfa, 0x04, \ + 0xc0, 0x7f, 0x6b, 0xfd, \ + 0x80, 0xc5, 0xa1, 0x1d, \ + 0x80, 0xe5, 0xa0, 0x5d, \ + 0xf0, 0x04, 0x0c, 0x0c, \ + 0x82, 0x0b, 0xdd, 0x93, \ + 0xfe, 0x75, 0xa0, 0x5d, \ + 0x0c, 0x0c, 0x82, 0x0b, \ + 0xdd, 0x93, 0xf0, 0x04, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x86, 0x7b, 0x88, 0x6b, \ + 0x10, 0x09, 0x80, 0x8b, \ + 0xa0, 0x34, 0xe0, 0x2c, \ + 0xbe, 0xae, 0x81, 0xa1, \ + 0xa1, 0x4f, 0x83, 0x41, \ + 0xbc, 0xae, 0xc8, 0x27, \ + 0x84, 0x1b, 0x82, 0x0b, \ + 0x62, 0x58, 0x88, 0x2b, \ + 0x80, 0x9b, 0xe8, 0x1c, \ + 0x04, 0xf0, 0xd8, 0xae, \ + 0x9a, 0xae, 0xe4, 0x10, \ + 0x92, 0x1b, 0x8e, 0x0b, \ + 0xe8, 0x1c, 0x67, 0xc8, \ + 0x82, 0xd5, 0xa1, 0x0b, \ + 0xbe, 0x04, 0xa2, 0x14, \ + 0xd8, 0x0e, 0x9e, 0x3b, \ + 0x92, 0x2b, 0x8e, 0x0b, \ + 0xe8, 0x1c, 0x63, 0xa8, \ + 0x80, 0x95, 0xa1, 0x0b, \ + 0x9a, 0x0e, 0xbc, 0x04, \ + 0x90, 0xf1, 0x81, 0x01, \ + 0xf2, 0x3c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x86, 0x9b, 0x88, 0xab, \ + 0x80, 0x7b, 0x84, 0x6b, \ + 0xe0, 0x2c, 0x0e, 0x09, \ + 0xe0, 0x30, 0x0e, 0x29, \ + 0xe0, 0x10, 0x0e, 0x4b, \ + 0x83, 0x51, 0x9c, 0x34, \ + 0xa1, 0x5f, 0x81, 0xb1, \ + 0x04, 0x54, 0xfc, 0xbe, \ + 0xbe, 0xbe, 0xe8, 0x88, \ + 0x88, 0x1b, 0x82, 0x0b, \ + 0xe8, 0x1c, 0x5e, 0xc8, \ + 0x99, 0x01, 0x80, 0x8b, \ + 0xe4, 0xc0, 0x02, 0xa0, \ + 0xec, 0x04, 0x00, 0x00, \ + 0xc5, 0xbe, 0xa1, 0x0b, \ + 0xf8, 0x34, 0x42, 0xb5, \ + 0xe0, 0x2c, 0x42, 0xb5, \ + 0x9a, 0x08, 0xfe, 0x15, \ + 0x8c, 0x65, 0xa1, 0x0b, \ + 0x12, 0x90, 0xe0, 0xa1, \ + 0x81, 0x01, 0xe5, 0x80, \ + 0xa1, 0x0b, 0x81, 0xb1, \ + 0xa4, 0x04, 0x88, 0xb5, \ + 0xe4, 0x20, 0x04, 0xf0, \ + 0x62, 0xf8, 0x90, 0x1b, \ + 0xa1, 0x0b, 0xe8, 0x1c, \ + 0xfc, 0x04, 0x86, 0xb5, \ + 0xa4, 0x04, 0xa8, 0x0e, \ + 0x06, 0xf0, 0x9e, 0x14, \ + 0x90, 0x2b, 0xe4, 0x10, \ + 0xe8, 0x1c, 0x5e, 0xc8, \ + 0x84, 0x55, 0xa1, 0x0b, \ + 0x54, 0x05, 0xbe, 0x04, \ + 0x12, 0x05, 0xf8, 0x30, \ + 0x9e, 0x14, 0xe0, 0x60, \ + 0xe4, 0x10, 0x06, 0xf0, \ + 0x5d, 0xf8, 0x90, 0x2b, \ + 0xa1, 0x0b, 0xe8, 0x1c, \ + 0xbe, 0x04, 0x80, 0xb5, \ + 0xe0, 0x30, 0x54, 0x05, \ + 0x9a, 0x18, 0x81, 0x01, \ + 0xc3, 0xbd, 0x82, 0xb1, \ + 0x88, 0x91, 0x88, 0xa1, \ + 0x81, 0x25, 0xf4, 0xb5, \ + 0x98, 0xf1, 0x81, 0x01, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x84, 0x6b, 0x88, 0x8b, \ + 0x91, 0x14, 0x82, 0x7b, \ + 0x95, 0x94, 0xa1, 0x1b, \ + 0x88, 0xc5, 0x93, 0xa4, \ + 0x46, 0x3a, 0xab, 0x3f, \ + 0x44, 0x1a, 0xe6, 0x8e, \ + 0x42, 0x3a, 0xe6, 0x86, \ + 0x46, 0x3a, 0xe5, 0x02, \ + 0x00, 0x24, 0xe5, 0x82, \ + 0x81, 0xb1, 0xe1, 0x84, \ + 0x42, 0x32, 0x82, 0x03, \ + 0x81, 0x31, 0xf2, 0xac, \ + 0xef, 0xe8, 0x5a, 0x68, \ + 0x85, 0x0b, 0xff, 0x21, \ + 0xa1, 0x1b, 0x86, 0x65, \ + 0xa1, 0x8b, 0x86, 0x95, \ + 0x02, 0x84, 0x80, 0xc5, \ + 0x8c, 0x13, 0xe1, 0x84, \ + 0xe0, 0x22, 0x56, 0x1a, \ + 0x80, 0x63, 0x8e, 0x63, \ + 0x42, 0xba, 0x8c, 0xb3, \ + 0x81, 0x01, 0xf0, 0x0a, \ + 0x43, 0xf6, 0x81, 0x55, \ + 0xff, 0x01, 0xef, 0xfc, \ + 0xa8, 0xbe, 0xff, 0xb1, \ + 0x88, 0xf1, 0xa4, 0x1e, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xff, 0x01, 0xdd, 0x93, \ + 0xf8, 0xc5, 0xa1, 0x1b, \ + 0xa1, 0x8b, 0xff, 0x01, \ + 0xfd, 0x95, 0xf8, 0xb5, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xe6, 0x82, 0x40, 0x1a, \ + 0x64, 0x01, 0x00, 0x00, \ + 0xdf, 0x07, 0xc0, 0x40, \ + 0x6f, 0xfd, 0x00, 0x02, \ + 0x00, 0x04, 0xc0, 0x3f, \ + 0xdd, 0x93, 0xfb, 0x80, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x82, 0x8b, 0x84, 0x9b, \ + 0xa3, 0x8d, 0x80, 0xcb, \ + 0x81, 0xa1, 0x81, 0x61, \ + 0x83, 0x11, 0x84, 0x55, \ + 0x81, 0xb1, 0x92, 0x0b, \ + 0xf0, 0x08, 0x00, 0x25, \ + 0xf8, 0x04, 0x00, 0x45, \ + 0x84, 0xa3, 0x91, 0x1d, \ + 0xf2, 0xac, 0x56, 0x42, \ + 0x02, 0x8a, 0x80, 0xe5, \ + 0x82, 0x11, 0xe6, 0x40, \ + 0x81, 0x25, 0xfc, 0x95, \ + 0x00, 0xa4, 0x81, 0xb1, \ + 0x02, 0xb4, 0xe1, 0x84, \ + 0xa2, 0x0f, 0xe0, 0xfc, \ + 0x00, 0xb4, 0xfe, 0x0e, \ + 0xfc, 0x0e, 0xe1, 0x84, \ + 0x0e, 0x30, 0xbe, 0x3e, \ + 0x89, 0xce, 0xe4, 0x30, \ + 0x00, 0x14, 0x81, 0x65, \ + 0x03, 0x78, 0xe8, 0xb0, \ + 0x87, 0x65, 0xa0, 0x00, \ + 0x82, 0xe5, 0xa1, 0xbb, \ + 0xe0, 0x00, 0x16, 0xca, \ + 0x90, 0x85, 0x81, 0x01, \ + 0x90, 0x45, 0x91, 0x6d, \ + 0xa1, 0x2b, 0xa6, 0x24, \ + 0x82, 0x25, 0xa4, 0x14, \ + 0xe0, 0x00, 0x04, 0xca, \ + 0x87, 0xa5, 0x82, 0x15, \ + 0x81, 0x01, 0xa1, 0xab, \ + 0x87, 0x65, 0xfc, 0x95, \ + 0x8c, 0x85, 0xa1, 0x1b, \ + 0x81, 0x01, 0xa1, 0xcb, \ + 0x00, 0x14, 0x80, 0x65, \ + 0x18, 0x8a, 0xef, 0xb0, \ + 0x82, 0x55, 0xe4, 0x80, \ + 0xec, 0x80, 0x00, 0xc0, \ + 0xe8, 0x80, 0x04, 0x14, \ + 0xe5, 0x00, 0x18, 0x9a, \ + 0xa0, 0x00, 0x00, 0xa8, \ + 0xb9, 0x2f, 0x81, 0x95, \ + 0x18, 0x9a, 0xa0, 0x2f, \ + 0x00, 0x48, 0xe5, 0x00, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0x81, 0x01, 0x81, 0x21, \ + 0x00, 0x48, 0xf2, 0x75, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0xfe, 0x14, 0x81, 0x01, \ + 0xfc, 0x14, 0x82, 0x03, \ + 0xf2, 0x84, 0x42, 0x22, \ + 0x96, 0x3b, 0x94, 0x2b, \ + 0xef, 0xe8, 0x50, 0x68, \ + 0x67, 0xfd, 0x00, 0x8a, \ + 0x80, 0x75, 0xc0, 0xff, \ + 0xef, 0xfc, 0x41, 0xf6, \ + 0x89, 0xc4, 0xa1, 0x1b, \ + 0x41, 0xf6, 0x80, 0x65, \ + 0x82, 0x61, 0xef, 0xfc, \ + 0xe2, 0x40, 0x0c, 0x0a, \ + 0x90, 0x91, 0xdd, 0x0c, \ + 0xbe, 0x04, 0xec, 0x55, \ + 0xe0, 0x18, 0x40, 0x81, \ + 0xe0, 0x1c, 0x40, 0xc1, \ + 0x0c, 0x0c, 0xa8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0xa1, 0x4b, 0x8a, 0x48, \ + 0x02, 0x10, 0x88, 0xa5, \ + 0x08, 0x14, 0xec, 0x04, \ + 0x88, 0x13, 0xe1, 0xfc, \ + 0x80, 0x40, 0x83, 0x17, \ + 0x8a, 0x28, 0x82, 0x00, \ + 0xe4, 0x60, 0x0a, 0x30, \ + 0x81, 0xc1, 0xe0, 0x31, \ + 0xd5, 0x7a, 0x83, 0x61, \ + 0x46, 0x73, 0x88, 0x73, \ + 0xcd, 0x8a, 0xf8, 0x60, \ + 0x12, 0x84, 0x80, 0x83, \ + 0x0e, 0x74, 0xfb, 0x00, \ + 0x4e, 0x62, 0xfb, 0x00, \ + 0x82, 0xc1, 0xf1, 0x9c, \ + 0xc5, 0xcd, 0x92, 0x73, \ + 0xfb, 0x1c, 0x4e, 0x12, \ + 0xe0, 0x2c, 0x46, 0x83, \ + 0xe0, 0x5c, 0x46, 0x73, \ + 0x81, 0x01, 0xfa, 0x75, \ + 0xf0, 0x1c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xf8, 0x3c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x60, 0x88, 0x0c, 0x34, \ + 0x0e, 0xb5, 0xa3, 0x3d, \ + 0xf0, 0x98, 0x58, 0x52, \ + 0xe4, 0x10, 0x0e, 0x50, \ + 0x7a, 0x9c, 0x52, 0xc2, \ + 0x8e, 0x93, 0xca, 0x7b, \ + 0xe1, 0x88, 0x10, 0x94, \ + 0x4e, 0x80, 0x82, 0x81, \ + 0xa1, 0x7b, 0xf0, 0x1c, \ + 0x2e, 0xab, 0x82, 0x65, \ + 0x5d, 0x2e, 0x8a, 0x7b, \ + 0xa1, 0xab, 0x82, 0xa1, \ + 0x12, 0x8a, 0xfe, 0xb5, \ + 0x80, 0xc5, 0xe4, 0x70, \ + 0x12, 0x8a, 0x85, 0x45, \ + 0x8a, 0x7b, 0x64, 0x70, \ + 0x53, 0x8e, 0x88, 0x15, \ + 0x5f, 0xff, 0x7f, 0xfd, \ + 0x92, 0x8f, 0xef, 0xa1, \ + 0x52, 0x92, 0xd0, 0x9b, \ + 0x90, 0x93, 0xfa, 0x28, \ + 0xc7, 0x97, 0xa0, 0x91, \ + 0x83, 0xb3, 0x93, 0x13, \ + 0xe8, 0x00, 0x31, 0x1c, \ + 0xf0, 0x20, 0x4e, 0x25, \ + 0xf8, 0x1c, 0x4e, 0x25, \ + 0xf8, 0x18, 0x4e, 0x25, \ + 0xf8, 0x14, 0x4e, 0x25, \ + 0xf8, 0x10, 0x4e, 0x25, \ + 0xf8, 0x0c, 0x4e, 0x25, \ + 0xf8, 0x08, 0x4e, 0x25, \ + 0xf8, 0x04, 0x4e, 0x25, \ + 0x86, 0x03, 0x06, 0x13, \ + 0x61, 0x8e, 0x42, 0x0a, \ + 0xe4, 0x04, 0x04, 0x10, \ + 0xe1, 0x8e, 0x40, 0x2a, \ + 0x84, 0x65, 0x81, 0x1b, \ + 0x04, 0x14, 0x44, 0x07, \ + 0x42, 0x52, 0xe0, 0x88, \ + 0x88, 0x63, 0x70, 0x88, \ + 0x88, 0x23, 0x08, 0x03, \ + 0xcd, 0x2d, 0xc9, 0x34, \ + 0x44, 0x40, 0xc5, 0x3e, \ + 0xd9, 0x1d, 0x1c, 0x00, \ + 0x1c, 0x00, 0x42, 0x50, \ + 0xfc, 0xf5, 0x81, 0x2b, \ + 0x88, 0xf1, 0x01, 0x01, \ + 0xf0, 0x3c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x8b, 0x4e, 0x8a, 0x6b, \ + 0x82, 0x8b, 0x84, 0x7b, \ + 0xa3, 0xb4, 0x80, 0x9b, \ + 0x00, 0x34, 0xa1, 0xa4, \ + 0x9f, 0x14, 0xe1, 0xfc, \ + 0x86, 0x03, 0x8d, 0x3e, \ + 0x83, 0x07, 0x9d, 0x34, \ + 0xfc, 0x2e, 0xa0, 0x2b, \ + 0x89, 0x1e, 0xae, 0x23, \ + 0xfe, 0x3e, 0xbe, 0x1e, \ + 0x81, 0x41, 0xbc, 0x3e, \ + 0x90, 0x1b, 0x92, 0x0b, \ + 0x6c, 0x38, 0x8e, 0x3b, \ + 0xa1, 0x0b, 0xef, 0xf8, \ + 0xbe, 0xae, 0x86, 0x75, \ + 0xbc, 0x0e, 0xfe, 0x04, \ + 0x92, 0x0b, 0x81, 0x41, \ + 0xfc, 0x24, 0x90, 0x1b, \ + 0x8c, 0x5b, 0x8d, 0x34, \ + 0xef, 0xf8, 0x6b, 0x68, \ + 0x82, 0xd5, 0xa1, 0x0b, \ + 0xfc, 0x04, 0x8d, 0x24, \ + 0xbe, 0xbe, 0x80, 0x23, \ + 0xbc, 0x0e, 0xfe, 0x04, \ + 0x92, 0x0b, 0x81, 0x41, \ + 0x8e, 0x3b, 0x90, 0x1b, \ + 0x6a, 0x78, 0x8c, 0x5b, \ + 0xfe, 0x0e, 0xef, 0xf8, \ + 0x90, 0xcb, 0x90, 0x9d, \ + 0x89, 0x04, 0x8b, 0x74, \ + 0xa7, 0x14, 0x88, 0xf5, \ + 0xa5, 0x24, 0x8d, 0x1e, \ + 0xf0, 0xa4, 0x46, 0x62, \ + 0xe1, 0x9a, 0x46, 0x3a, \ + 0x00, 0x45, 0xc4, 0x37, \ + 0x14, 0x55, 0xfc, 0x0c, \ + 0x80, 0x1b, 0xfc, 0x0c, \ + 0x16, 0xc5, 0x98, 0x0b, \ + 0x94, 0x8b, 0xfc, 0x0c, \ + 0x56, 0x42, 0x96, 0xab, \ + 0x8e, 0x43, 0xf1, 0x9c, \ + 0xa8, 0x53, 0x8a, 0xb3, \ + 0x98, 0x53, 0xb8, 0xb3, \ + 0x82, 0x0b, 0x80, 0xcb, \ + 0xf0, 0x8c, 0x48, 0x22, \ + 0x8d, 0x14, 0x99, 0x9d, \ + 0x82, 0x91, 0x82, 0x33, \ + 0x94, 0xbb, 0x90, 0xbe, \ + 0x8c, 0x5e, 0x90, 0xab, \ + 0xfe, 0x04, 0xf6, 0xd5, \ + 0x0c, 0x0c, 0xb8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xd0, 0x1e, 0xf0, \ + 0x93, 0x4e, 0x8a, 0x6b, \ + 0x8f, 0x2e, 0x91, 0x3e, \ + 0x80, 0xbb, 0x82, 0x8b, \ + 0xb1, 0x44, 0xb3, 0x24, \ + 0xad, 0x94, 0xaf, 0x74, \ + 0xa9, 0x04, 0xab, 0x54, \ + 0x81, 0x01, 0xfe, 0x0e, \ + 0x97, 0x0e, 0x99, 0x0e, \ + 0x9c, 0x0e, 0x95, 0x0e, \ + 0xe0, 0x0c, 0x41, 0xf6, \ + 0x7f, 0xfd, 0x43, 0xfe, \ + 0x8b, 0x4e, 0xc0, 0x01, \ + 0x8d, 0x2e, 0x90, 0x0c, \ + 0xff, 0x41, 0x88, 0x1e, \ + 0x91, 0xa4, 0x89, 0x7e, \ + 0xf0, 0xac, 0x40, 0x62, \ + 0xe1, 0x9a, 0x42, 0x0a, \ + 0x0a, 0x25, 0xc4, 0x17, \ + 0xa1, 0x2b, 0xfc, 0x04, \ + 0xa1, 0x2d, 0x84, 0x85, \ + 0x12, 0x15, 0x84, 0xc5, \ + 0xa1, 0x1d, 0xfc, 0x04, \ + 0x82, 0x01, 0x8e, 0x55, \ + 0xe1, 0x9a, 0x40, 0x0a, \ + 0x0a, 0x15, 0xc4, 0x07, \ + 0xa1, 0x1d, 0xfc, 0x00, \ + 0x12, 0x05, 0x8c, 0x55, \ + 0x88, 0x0d, 0xfc, 0x00, \ + 0x81, 0x95, 0x8a, 0xd5, \ + 0xfc, 0x04, 0x12, 0x05, \ + 0x8a, 0x25, 0xa1, 0x0b, \ + 0xfc, 0x04, 0x12, 0x05, \ + 0x88, 0xa5, 0x88, 0x0d, \ + 0x9c, 0x0e, 0x83, 0x01, \ + 0x80, 0xbc, 0x8b, 0x04, \ + 0xe4, 0xa0, 0x00, 0xf0, \ + 0xe4, 0xb0, 0x02, 0xf0, \ + 0x0a, 0xf0, 0x8a, 0x7b, \ + 0xbe, 0x0e, 0xe4, 0xc0, \ + 0x96, 0x0b, 0xbc, 0x1e, \ + 0x94, 0x2b, 0x8f, 0x14, \ + 0xfe, 0x44, 0x8c, 0x3b, \ + 0xef, 0xf8, 0x5d, 0x88, \ + 0x97, 0x34, 0x99, 0x24, \ + 0x8d, 0x04, 0x95, 0x44, \ + 0xbc, 0x6e, 0xbe, 0x0e, \ + 0x96, 0x1b, 0x94, 0x0b, \ + 0x68, 0xd8, 0x93, 0x54, \ + 0xff, 0x41, 0xef, 0xf8, \ + 0x89, 0x74, 0x8e, 0x5b, \ + 0x80, 0x75, 0x89, 0x0b, \ + 0x9c, 0x0e, 0x81, 0x01, \ + 0x91, 0xbd, 0x82, 0xb1, \ + 0x9c, 0x04, 0x80, 0x85, \ + 0xec, 0x25, 0xa1, 0x0b, \ + 0xe8, 0xf1, 0x81, 0x01, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x80, 0x7b, 0x82, 0x6b, \ + 0xe0, 0x14, 0x0e, 0x09, \ + 0xfb, 0x81, 0xf4, 0x0d, \ + 0x0e, 0x19, 0x9c, 0x55, \ + 0xf0, 0x1d, 0xe0, 0x18, \ + 0x52, 0x1a, 0x9a, 0xd5, \ + 0x13, 0x0a, 0xe6, 0x82, \ + 0x9a, 0x35, 0xe5, 0x00, \ + 0x9c, 0x18, 0xdc, 0x08, \ + 0xd8, 0x06, 0x9e, 0x28, \ + 0x9a, 0x26, 0x8c, 0x0b, \ + 0xf0, 0x30, 0x40, 0x11, \ + 0xe0, 0x14, 0x0e, 0x19, \ + 0x0e, 0x39, 0xde, 0x28, \ + 0x40, 0x11, 0xe0, 0x10, \ + 0x40, 0x31, 0xf8, 0x1c, \ + 0x40, 0x21, 0xf8, 0x20, \ + 0x0e, 0x19, 0xf8, 0x24, \ + 0x0e, 0x29, 0xe0, 0x20, \ + 0x0e, 0x39, 0xe0, 0x18, \ + 0x40, 0x11, 0xe0, 0x1c, \ + 0x40, 0x31, 0xf8, 0x10, \ + 0x40, 0x21, 0xf8, 0x14, \ + 0x0e, 0x19, 0xf8, 0x18, \ + 0x0e, 0x29, 0xe0, 0x2c, \ + 0x0e, 0x39, 0xe0, 0x24, \ + 0x40, 0x11, 0xe0, 0x28, \ + 0x40, 0x31, 0xf8, 0x04, \ + 0x40, 0x21, 0xf8, 0x08, \ + 0x81, 0x81, 0xf8, 0x0c, \ + 0x04, 0x06, 0x81, 0x11, \ + 0x4d, 0x98, 0xe0, 0x08, \ + 0xa1, 0x9b, 0xef, 0xe8, \ + 0x00, 0x70, 0x8c, 0xc5, \ + 0x81, 0xb1, 0xe4, 0x90, \ + 0xc1, 0x24, 0x83, 0x11, \ + 0x56, 0x22, 0xd3, 0x1d, \ + 0x80, 0xa5, 0xfa, 0xac, \ + 0xe5, 0x00, 0x02, 0x8a, \ + 0xfe, 0x15, 0x82, 0x11, \ + 0xe1, 0xd0, 0x00, 0xb4, \ + 0xd8, 0xb7, 0xbc, 0x0e, \ + 0x98, 0x61, 0xc8, 0x71, \ + 0x81, 0x81, 0x83, 0xa1, \ + 0xa1, 0x2b, 0x9c, 0x24, \ + 0x00, 0x24, 0x86, 0xa5, \ + 0x96, 0x03, 0xe1, 0x84, \ + 0x42, 0x12, 0xbc, 0x14, \ + 0x81, 0x31, 0xf2, 0xa0, \ + 0xef, 0xe4, 0x70, 0x88, \ + 0x67, 0xfd, 0x00, 0x8a, \ + 0x80, 0x75, 0xc0, 0xff, \ + 0xef, 0xfc, 0x41, 0xf6, \ + 0x80, 0x65, 0xa1, 0x1b, \ + 0xef, 0xfc, 0x41, 0xf6, \ + 0xd9, 0x0c, 0xd3, 0xad, \ + 0x14, 0x8a, 0x82, 0x05, \ + 0x82, 0xa1, 0xe5, 0x00, \ + 0xf8, 0x75, 0x88, 0x71, \ + 0xff, 0x81, 0x81, 0x25, \ + 0x88, 0xf1, 0x90, 0x0b, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x84, 0x0a, 0x80, 0x6b, \ + 0x4c, 0x05, 0x86, 0x4a, \ + 0xc4, 0x0a, 0xed, 0xa8, \ + 0xed, 0xac, 0x4c, 0x45, \ + 0xe0, 0x20, 0x02, 0x49, \ + 0xe1, 0x00, 0x04, 0xc9, \ + 0xe1, 0x04, 0x04, 0x79, \ + 0xed, 0xb0, 0x4c, 0x05, \ + 0xe1, 0x08, 0x04, 0x09, \ + 0xee, 0x24, 0x4c, 0x45, \ + 0xe1, 0x0c, 0x04, 0x49, \ + 0xed, 0xb4, 0x4c, 0xc5, \ + 0xed, 0xb8, 0x4c, 0x75, \ + 0xed, 0xbc, 0x4c, 0x05, \ + 0xe0, 0x2c, 0x04, 0x05, \ + 0xed, 0xc0, 0x4c, 0x45, \ + 0xe1, 0x10, 0x04, 0x41, \ + 0xe1, 0x14, 0x04, 0xc1, \ + 0xe1, 0x18, 0x04, 0x71, \ + 0xee, 0x28, 0x4c, 0x05, \ + 0xe1, 0x1c, 0x04, 0x01, \ + 0xed, 0xc4, 0x4c, 0x45, \ + 0xe1, 0x20, 0x04, 0x49, \ + 0xed, 0xc8, 0x4c, 0xc5, \ + 0xed, 0xcc, 0x4c, 0x75, \ + 0xed, 0xd0, 0x4c, 0x05, \ + 0xe1, 0x28, 0x04, 0x09, \ + 0xed, 0xd4, 0x4c, 0x45, \ + 0xe1, 0x30, 0x04, 0x49, \ + 0xe1, 0x24, 0x04, 0xc9, \ + 0xe1, 0x2c, 0x04, 0x79, \ + 0xed, 0xdc, 0x4c, 0x05, \ + 0x4c, 0x45, 0x81, 0x01, \ + 0x4c, 0x05, 0xed, 0xe4, \ + 0x4c, 0xc5, 0xed, 0xe8, \ + 0x4c, 0x75, 0xed, 0xd8, \ + 0x88, 0x04, 0xed, 0xe0, \ + 0xc8, 0xc4, 0x8a, 0x44, \ + 0x04, 0x85, 0xca, 0x74, \ + 0x04, 0x95, 0xe0, 0x34, \ + 0x4c, 0x05, 0xe0, 0x38, \ + 0x4c, 0x45, 0xed, 0xec, \ + 0x4c, 0xc5, 0xed, 0xf0, \ + 0x4c, 0x75, 0xed, 0xf4, \ + 0x4c, 0x85, 0xed, 0xf8, \ + 0x4c, 0x95, 0xee, 0x48, \ + 0x04, 0x05, 0xee, 0x4c, \ + 0x04, 0x45, 0xe0, 0x3c, \ + 0x04, 0xc5, 0xe0, 0x10, \ + 0x04, 0x75, 0xe0, 0x14, \ + 0x04, 0x85, 0xe0, 0x18, \ + 0x04, 0x95, 0xe0, 0x1c, \ + 0x4c, 0x05, 0xe0, 0x20, \ + 0x4c, 0x45, 0xee, 0x50, \ + 0x4c, 0xc5, 0xee, 0x6c, \ + 0x4c, 0x75, 0xee, 0x70, \ + 0x4c, 0x85, 0xee, 0x74, \ + 0x4c, 0x95, 0xee, 0x78, \ + 0x8c, 0x04, 0xee, 0x7c, \ + 0xee, 0x80, 0x4c, 0x05, \ + 0x4c, 0x05, 0x8e, 0x04, \ + 0xcc, 0x04, 0xee, 0x84, \ + 0xee, 0x88, 0x4c, 0x05, \ + 0x4c, 0x05, 0xce, 0x04, \ + 0x06, 0x05, 0xee, 0x8c, \ + 0x4c, 0x05, 0xe0, 0x10, \ + 0x06, 0x05, 0xee, 0x90, \ + 0x4c, 0x05, 0xe0, 0x14, \ + 0x06, 0x05, 0xee, 0x94, \ + 0x4c, 0x05, 0xe0, 0x18, \ + 0x06, 0x05, 0xee, 0x98, \ + 0x4c, 0x05, 0xe0, 0x1c, \ + 0x06, 0x05, 0xee, 0x9c, \ + 0x4c, 0x05, 0xe0, 0x20, \ + 0x06, 0x05, 0xee, 0xa0, \ + 0x4c, 0x05, 0xe0, 0x24, \ + 0x06, 0x05, 0xee, 0xa4, \ + 0x4c, 0x05, 0xe0, 0x28, \ + 0x06, 0x05, 0xee, 0xa8, \ + 0x9d, 0x84, 0xe0, 0x2c, \ + 0xee, 0xac, 0x4c, 0x05, \ + 0xe0, 0x30, 0x06, 0x05, \ + 0x04, 0x35, 0xa3, 0x94, \ + 0xa1, 0xa4, 0xe0, 0x24, \ + 0x93, 0xc4, 0xa0, 0x48, \ + 0xee, 0xb0, 0x4c, 0x05, \ + 0x4c, 0x35, 0x95, 0x74, \ + 0x97, 0x34, 0xee, 0xb4, \ + 0xed, 0x80, 0x4c, 0x45, \ + 0x4c, 0xc5, 0x91, 0x04, \ + 0x99, 0x44, 0xed, 0x84, \ + 0xed, 0x88, 0x4c, 0x75, \ + 0x4c, 0x35, 0x9b, 0xc4, \ + 0x9f, 0x34, 0xed, 0x94, \ + 0xe0, 0x30, 0x04, 0x25, \ + 0xed, 0x90, 0x4c, 0x45, \ + 0x66, 0x01, 0x0e, 0x60, \ + 0x4c, 0xc5, 0xc0, 0x0d, \ + 0x4c, 0x55, 0xed, 0x8c, \ + 0xff, 0x41, 0xee, 0x34, \ + 0xee, 0x00, 0x04, 0x22, \ + 0xee, 0x04, 0x4c, 0x45, \ + 0xed, 0xa4, 0x4c, 0x25, \ + 0xed, 0x9c, 0x4c, 0x35, \ + 0xff, 0xf8, 0x04, 0x8e, \ + 0x49, 0xce, 0x83, 0x31, \ + 0x0c, 0xc5, 0xff, 0xf4, \ + 0xa4, 0xc3, 0xed, 0x9c, \ + 0xec, 0x04, 0x04, 0x20, \ + 0xb0, 0x36, 0x89, 0x2b, \ + 0x81, 0x21, 0xfe, 0x35, \ + 0xee, 0x3c, 0x4c, 0x25, \ + 0xee, 0x20, 0x4c, 0x05, \ + 0x67, 0x01, 0x04, 0x60, \ + 0x4c, 0x25, 0xc0, 0x06, \ + 0x04, 0x60, 0xed, 0x98, \ + 0xc0, 0x02, 0x65, 0x01, \ + 0xed, 0xa0, 0x4c, 0x65, \ + 0xee, 0x2c, 0x4c, 0x25, \ + 0x66, 0x01, 0x04, 0x60, \ + 0x00, 0x8a, 0xc0, 0x04, \ + 0x4c, 0x25, 0xe0, 0x80, \ + 0xff, 0x01, 0xee, 0x30, \ + 0xee, 0x14, 0x4c, 0x05, \ + 0x88, 0x15, 0xfb, 0x01, \ + 0xed, 0xf8, 0x0c, 0x35, \ + 0xee, 0x28, 0x0c, 0x45, \ + 0x86, 0x55, 0x87, 0x4d, \ + 0xed, 0xb4, 0x0c, 0x25, \ + 0xe4, 0x08, 0x18, 0x20, \ + 0xe1, 0x7c, 0x16, 0xc4, \ + 0x96, 0xc3, 0xfd, 0xb7, \ + 0xa1, 0xb1, 0x85, 0xc7, \ + 0xa9, 0xbd, 0xb8, 0xb3, \ + 0x0c, 0xc5, 0x82, 0xb5, \ + 0x0c, 0xb5, 0xed, 0xc4, \ + 0x96, 0xcd, 0xed, 0xc8, \ + 0x0c, 0xc5, 0x80, 0xf5, \ + 0x0c, 0xb5, 0xee, 0x78, \ + 0x96, 0xcd, 0xee, 0x7c, \ + 0x88, 0xf1, 0x80, 0xa5, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x0c, 0xc5, 0xdd, 0x93, \ + 0xaa, 0xcd, 0xed, 0xcc, \ + 0xa0, 0x41, 0xfe, 0x35, \ + 0xa6, 0x43, 0x98, 0x33, \ + 0xfc, 0x95, 0xa9, 0x4d, \ + 0xed, 0xd0, 0x0c, 0x35, \ + 0xfc, 0x15, 0xac, 0x3d, \ + 0xe4, 0x04, 0x06, 0x20, \ + 0xe1, 0xfc, 0x08, 0x34, \ + 0xa1, 0x31, 0x86, 0x43, \ + 0x83, 0x47, 0xb8, 0x33, \ + 0xf8, 0xd5, 0x89, 0x3d, \ + 0xed, 0xb8, 0x0c, 0x45, \ + 0xee, 0x24, 0x0c, 0xc5, \ + 0x98, 0x43, 0x86, 0x43, \ + 0xf6, 0xd5, 0x85, 0x4d, \ + 0xed, 0xd8, 0x0c, 0x35, \ + 0x89, 0x01, 0xa2, 0x3d, \ + 0xa1, 0x3b, 0x82, 0x35, \ + 0x84, 0x25, 0x91, 0x21, \ + 0x86, 0x75, 0xa3, 0x3b, \ + 0xe0, 0x0c, 0x47, 0xf6, \ + 0xa5, 0x3b, 0x83, 0x25, \ + 0xa7, 0x3b, 0x82, 0x85, \ + 0x47, 0xf6, 0x84, 0x95, \ + 0x45, 0xf6, 0xe0, 0x0c, \ + 0x41, 0xf6, 0xe0, 0x0c, \ + 0x81, 0x75, 0xe0, 0x0c, \ + 0x81, 0x55, 0xa1, 0x31, \ + 0xe0, 0x0c, 0x47, 0xf6, \ + 0xe0, 0x0c, 0x45, 0xf6, \ + 0xee, 0x54, 0x4c, 0x35, \ + 0xee, 0x58, 0x4c, 0x25, \ + 0xee, 0x5c, 0x4c, 0x05, \ + 0xed, 0xe0, 0x0c, 0x35, \ + 0x89, 0x01, 0xa2, 0x3d, \ + 0xa1, 0x3b, 0x82, 0x35, \ + 0x84, 0x25, 0x91, 0x21, \ + 0x86, 0x75, 0xa3, 0x3b, \ + 0xe0, 0x0c, 0x47, 0xf6, \ + 0xa5, 0x3b, 0x83, 0x25, \ + 0xa7, 0x3b, 0x82, 0x85, \ + 0x47, 0xf6, 0x84, 0x95, \ + 0x45, 0xf6, 0xe0, 0x0c, \ + 0x41, 0xf6, 0xe0, 0x0c, \ + 0x81, 0x75, 0xe0, 0x0c, \ + 0x81, 0x55, 0xa1, 0x31, \ + 0xe0, 0x0c, 0x47, 0xf6, \ + 0xe0, 0x0c, 0x45, 0xf6, \ + 0xee, 0x60, 0x4c, 0x35, \ + 0xee, 0x64, 0x4c, 0x25, \ + 0xee, 0x68, 0x4c, 0x05, \ + 0x4c, 0x55, 0xc6, 0x0a, \ + 0x40, 0x02, 0xee, 0x34, \ + 0x4c, 0x05, 0xfa, 0x14, \ + 0x8e, 0x0b, 0xee, 0x34, \ + 0xe8, 0x0c, 0x59, 0x88, \ + 0x4b, 0x18, 0x8e, 0x0b, \ + 0x8e, 0x0b, 0xe8, 0x0c, \ + 0xe8, 0x04, 0x56, 0xe8, \ + 0x73, 0x78, 0x8e, 0x0b, \ + 0x0c, 0x05, 0xe8, 0x08, \ + 0x83, 0x11, 0xee, 0x38, \ + 0x4c, 0x15, 0xbe, 0x0d, \ + 0x4c, 0x15, 0xee, 0x18, \ + 0x4c, 0x05, 0xee, 0x1c, \ + 0x80, 0xd5, 0xee, 0x3c, \ + 0xee, 0x20, 0x0c, 0x15, \ + 0x4c, 0x15, 0x80, 0x13, \ + 0x0c, 0x05, 0xee, 0x3c, \ + 0x0c, 0x15, 0xee, 0x3c, \ + 0x82, 0x03, 0xee, 0x20, \ + 0xec, 0x04, 0x00, 0x00, \ + 0xee, 0x40, 0x4c, 0x05, \ + 0xee, 0x44, 0x4c, 0x05, \ + 0x82, 0xb1, 0xff, 0xb1, \ + 0xe7, 0x1c, 0x17, 0x0a, \ + 0xc0, 0x00, 0x09, 0xd8, \ + 0xee, 0x18, 0x0c, 0x05, \ + 0x84, 0xd5, 0xa1, 0x0b, \ + 0xee, 0x1c, 0x0c, 0x05, \ + 0x82, 0x25, 0xa7, 0x0b, \ + 0x82, 0x65, 0xa5, 0x0b, \ + 0x90, 0x15, 0xa3, 0x0b, \ + 0x4b, 0x78, 0x8e, 0x0b, \ + 0x81, 0x85, 0xe8, 0x00, \ + 0x6d, 0x38, 0x8e, 0x0b, \ + 0x81, 0x45, 0xe8, 0x00, \ + 0x56, 0x38, 0x8e, 0x0b, \ + 0x0c, 0x05, 0xe8, 0x00, \ + 0xa3, 0x0b, 0xee, 0x18, \ + 0x8e, 0x0b, 0x80, 0x95, \ + 0xfc, 0x38, 0x54, 0x08, \ + 0xee, 0x18, 0x0c, 0x05, \ + 0x8a, 0x95, 0xa5, 0x0b, \ + 0xee, 0x08, 0x0c, 0x05, \ + 0xee, 0x40, 0x0c, 0x15, \ + 0x81, 0x1d, 0x84, 0x01, \ + 0xfa, 0x04, 0x40, 0x02, \ + 0x01, 0x0a, 0x80, 0x95, \ + 0x80, 0xb5, 0xe0, 0x40, \ + 0xe0, 0x44, 0x00, 0x02, \ + 0xee, 0x40, 0x4c, 0x05, \ + 0xee, 0x40, 0x0c, 0x15, \ + 0xee, 0x28, 0x0c, 0x05, \ + 0x80, 0x95, 0x80, 0x1d, \ + 0x4c, 0x05, 0x82, 0x01, \ + 0x0c, 0x05, 0xee, 0x40, \ + 0x0c, 0x15, 0xee, 0x20, \ + 0x87, 0x21, 0xee, 0x3c, \ + 0xe6, 0x8a, 0x40, 0x0a, \ + 0xec, 0x38, 0x00, 0x00, \ + 0x80, 0x65, 0x80, 0x1d, \ + 0xee, 0x3c, 0x4c, 0x05, \ + 0x52, 0x08, 0x8e, 0x0b, \ + 0x0c, 0x05, 0xfc, 0x38, \ + 0xa7, 0x0b, 0xee, 0x18, \ + 0x0c, 0x05, 0x88, 0x55, \ + 0x01, 0x0a, 0xee, 0x40, \ + 0x80, 0x95, 0xe0, 0x40, \ + 0x4c, 0x05, 0xa3, 0x01, \ + 0x0c, 0x05, 0xee, 0x40, \ + 0x0c, 0x15, 0xee, 0x08, \ + 0x84, 0x01, 0xee, 0x40, \ + 0x80, 0x65, 0x81, 0x1d, \ + 0xee, 0x40, 0x4c, 0x05, \ + 0xee, 0x20, 0x0c, 0x05, \ + 0xee, 0x3c, 0x0c, 0x15, \ + 0x40, 0x0a, 0x87, 0x21, \ + 0x00, 0x00, 0xe6, 0x8a, \ + 0x80, 0x1d, 0xec, 0x18, \ + 0x4c, 0x05, 0x80, 0x65, \ + 0x8e, 0x0b, 0xee, 0x3c, \ + 0xe8, 0x08, 0x51, 0xa8, \ + 0xee, 0x18, 0x0c, 0x05, \ + 0xe2, 0x15, 0xaf, 0x0b, \ + 0x68, 0x11, 0x0c, 0x09, \ + 0x82, 0x01, 0xc0, 0x03, \ + 0xf0, 0x00, 0x00, 0x0a, \ + 0x02, 0x04, 0xa2, 0x06, \ + 0x81, 0x01, 0xfa, 0x80, \ + 0x9f, 0xfc, 0x2e, 0x38, \ + 0xed, 0x9c, 0x0c, 0x35, \ + 0xee, 0x20, 0x0c, 0x25, \ + 0x8c, 0x42, 0xb0, 0x31, \ + 0xe0, 0x30, 0x06, 0x53, \ + 0xe1, 0x8a, 0x58, 0x4a, \ + 0xe1, 0x8a, 0x48, 0x5a, \ + 0xe8, 0x10, 0x59, 0x0a, \ + 0x46, 0x43, 0x8c, 0xcc, \ + 0x80, 0x85, 0xe0, 0x30, \ + 0x46, 0x43, 0x84, 0x43, \ + 0x02, 0x10, 0xe0, 0x30, \ + 0xa1, 0x1b, 0xec, 0x04, \ + 0xfa, 0x95, 0x84, 0x31, \ + 0xfd, 0x01, 0xd9, 0x75, \ + 0x00, 0x0c, 0xd9, 0x55, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0xec, 0xa0, 0x1e, 0xf0, \ + 0xa5, 0x64, 0xa1, 0xc4, \ + 0xad, 0x84, 0xa9, 0x74, \ + 0x54, 0x0e, 0xaf, 0x94, \ + 0xc0, 0x10, 0x64, 0x79, \ + 0x15, 0x8e, 0x93, 0xae, \ + 0xc0, 0x10, 0x64, 0x39, \ + 0xab, 0xa4, 0x91, 0xae, \ + 0xa7, 0x94, 0x8f, 0x9e, \ + 0xa3, 0x84, 0x8d, 0x8e, \ + 0x89, 0x7e, 0x8b, 0xae, \ + 0xfc, 0x6e, 0xfe, 0x9e, \ + 0xbc, 0xce, 0xbe, 0x8e, \ + 0xef, 0xfc, 0x58, 0x88, \ + 0x0c, 0x0c, 0xd0, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x0c, 0x05, 0x80, 0x6b, \ + 0xa1, 0x0d, 0xe0, 0x88, \ + 0x0c, 0x15, 0x80, 0xb5, \ + 0x83, 0x0d, 0xe0, 0xbc, \ + 0x8f, 0x01, 0x80, 0xa5, \ + 0xe0, 0x98, 0x4c, 0x05, \ + 0x0c, 0x15, 0x8b, 0xd5, \ + 0x0c, 0x25, 0xe1, 0x3c, \ + 0x82, 0x23, 0xe0, 0xa8, \ + 0x0c, 0x75, 0x85, 0x0b, \ + 0x82, 0x55, 0xe0, 0x18, \ + 0xe0, 0x7c, 0x0c, 0x25, \ + 0x80, 0xd5, 0xa1, 0x2b, \ + 0xe1, 0xd0, 0x0c, 0x29, \ + 0xe0, 0x80, 0x44, 0x0a, \ + 0x0c, 0x19, 0x92, 0xf5, \ + 0x42, 0x0a, 0xe1, 0xd0, \ + 0x80, 0xe5, 0xe0, 0x88, \ + 0x8c, 0x1b, 0x83, 0x21, \ + 0xe8, 0x08, 0x64, 0x28, \ + 0xe0, 0x88, 0x4c, 0x05, \ + 0xe0, 0xc4, 0x0c, 0x05, \ + 0xe0, 0x34, 0x0c, 0x15, \ + 0xe0, 0xa0, 0x0c, 0x25, \ + 0xe0, 0xa8, 0x0c, 0x35, \ + 0x80, 0x13, 0x87, 0x41, \ + 0xe6, 0x92, 0x44, 0x2a, \ + 0xf1, 0x8c, 0x48, 0x12, \ + 0x85, 0x41, 0x85, 0x4d, \ + 0xe0, 0x98, 0x4c, 0x45, \ + 0xe0, 0x9c, 0x4c, 0x45, \ + 0xe0, 0xbc, 0x4c, 0x05, \ + 0x86, 0x23, 0x80, 0x95, \ + 0xec, 0x04, 0x02, 0x20, \ + 0x86, 0xe5, 0x80, 0x1d, \ + 0xf1, 0x8c, 0x44, 0x12, \ + 0x0e, 0x45, 0xc4, 0x27, \ + 0x0c, 0x25, 0xfc, 0x08, \ + 0x0a, 0x10, 0xe0, 0xac, \ + 0xa6, 0x53, 0xec, 0x04, \ + 0xc4, 0x57, 0xc4, 0x47, \ + 0xfc, 0x10, 0x04, 0x35, \ + 0x9c, 0x44, 0x8a, 0x73, \ + 0x04, 0x45, 0xc4, 0x47, \ + 0x86, 0x4d, 0xfc, 0x10, \ + 0xec, 0x04, 0x02, 0x10, \ + 0x4c, 0x15, 0x80, 0x95, \ + 0x88, 0x3b, 0xe0, 0xbc, \ + 0x0e, 0x70, 0x80, 0x1d, \ + 0xfc, 0x55, 0xec, 0x10, \ + 0xe0, 0x88, 0x0c, 0x05, \ + 0x4c, 0x05, 0x84, 0x01, \ + 0x88, 0xf1, 0xe0, 0xc0, \ + 0xf2, 0x1c, 0x0c, 0x0c, \ + 0x0c, 0x25, 0xdd, 0x93, \ + 0x0c, 0x35, 0xe0, 0x78, \ + 0xa1, 0x2d, 0xe0, 0x84, \ + 0xe4, 0x04, 0x04, 0x30, \ + 0xe0, 0x84, 0x4c, 0x25, \ + 0x0c, 0x45, 0x86, 0xd5, \ + 0x0c, 0x35, 0xe0, 0x78, \ + 0x82, 0x43, 0xe0, 0x20, \ + 0xe0, 0x88, 0x0a, 0x14, \ + 0x81, 0x51, 0x8a, 0x73, \ + 0x0e, 0x85, 0x82, 0xcb, \ + 0xdd, 0x94, 0xf8, 0x10, \ + 0xc4, 0x97, 0xc4, 0x87, \ + 0xfc, 0x20, 0x06, 0x85, \ + 0xfc, 0x24, 0x06, 0x95, \ + 0xf1, 0xa0, 0x50, 0x92, \ + 0x80, 0x45, 0x8a, 0x8d, \ + 0x89, 0xcd, 0x98, 0x1b, \ + 0x4a, 0x82, 0x82, 0xc1, \ + 0xfa, 0xd5, 0xfa, 0x14, \ + 0x0c, 0x45, 0x98, 0x34, \ + 0x0c, 0x55, 0xe0, 0x1c, \ + 0x06, 0x30, 0xe0, 0x3c, \ + 0x87, 0x2d, 0xec, 0x04, \ + 0x84, 0x43, 0xc2, 0x27, \ + 0x48, 0x13, 0x82, 0x53, \ + 0x48, 0x53, 0xe0, 0x30, \ + 0xd8, 0xc5, 0xe0, 0x60, \ + 0x4c, 0x15, 0x83, 0x11, \ + 0x82, 0x01, 0xe0, 0x98, \ + 0xe0, 0x9c, 0x4c, 0x15, \ + 0xe0, 0xbc, 0x4c, 0x05, \ + 0x00, 0x0c, 0xf7, 0xd5, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x0c, 0x15, 0x80, 0x6b, \ + 0xa1, 0x1d, 0xe0, 0x8c, \ + 0x0c, 0x05, 0x82, 0x35, \ + 0x80, 0x1d, 0xe0, 0x88, \ + 0x0c, 0x05, 0x80, 0xa5, \ + 0x83, 0x0d, 0xe0, 0xbc, \ + 0x8f, 0x01, 0x82, 0x05, \ + 0xe0, 0x98, 0x4c, 0x05, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x1c, \ + 0xe1, 0x40, 0x0c, 0x25, \ + 0xe0, 0xa8, 0x0c, 0x35, \ + 0xf1, 0x8c, 0x46, 0x22, \ + 0x0c, 0xc5, 0x87, 0x1b, \ + 0x0c, 0x45, 0xe0, 0x18, \ + 0x0c, 0x3b, 0xe0, 0xb4, \ + 0x0c, 0x5b, 0xe0, 0x50, \ + 0x82, 0x75, 0xe0, 0x48, \ + 0xe0, 0x80, 0x0c, 0x75, \ + 0x80, 0xf5, 0xa1, 0x7b, \ + 0xe1, 0xd0, 0x0c, 0x79, \ + 0xe0, 0x80, 0x4e, 0x0a, \ + 0xc0, 0x00, 0x0f, 0x88, \ + 0xe0, 0x70, 0x0c, 0x25, \ + 0xe0, 0x3c, 0x50, 0x86, \ + 0xf1, 0x84, 0x4e, 0x02, \ + 0x90, 0x7d, 0xa4, 0x83, \ + 0x0c, 0x25, 0x96, 0x45, \ + 0xfd, 0x81, 0xe0, 0x94, \ + 0x94, 0xa5, 0x91, 0x2b, \ + 0xe0, 0x34, 0x0c, 0x25, \ + 0xe4, 0x04, 0x10, 0x20, \ + 0xe1, 0xfc, 0x12, 0x84, \ + 0x83, 0x87, 0x92, 0x83, \ + 0x91, 0x7d, 0x84, 0x83, \ + 0x4e, 0x12, 0x92, 0x45, \ + 0x82, 0x71, 0xf0, 0x80, \ + 0xe1, 0xfc, 0x10, 0x74, \ + 0xc2, 0x77, 0x90, 0x73, \ + 0x90, 0x7f, 0xf9, 0x81, \ + 0x18, 0x75, 0x8e, 0xc3, \ + 0x0c, 0x85, 0xf8, 0x0c, \ + 0xc4, 0x77, 0xe0, 0x10, \ + 0xfc, 0x1c, 0x10, 0x85, \ + 0xe4, 0x10, 0x12, 0x80, \ + 0x4a, 0x9a, 0xc7, 0x97, \ + 0xc6, 0x47, 0xe6, 0x96, \ + 0x0a, 0x50, 0xa0, 0x41, \ + 0xc9, 0x57, 0xe5, 0x00, \ + 0x7f, 0xfd, 0x53, 0x8e, \ + 0xcb, 0x47, 0xc3, 0xff, \ + 0x0c, 0x95, 0x92, 0x5f, \ + 0x88, 0x83, 0xe0, 0x20, \ + 0x8a, 0x83, 0xb2, 0xc4, \ + 0xf0, 0x0a, 0x4a, 0x8a, \ + 0xfc, 0x1c, 0x12, 0x75, \ + 0x12, 0xc5, 0xc4, 0xc7, \ + 0x46, 0x5a, 0xfc, 0x30, \ + 0xa8, 0x73, 0xe6, 0x8e, \ + 0xe6, 0x00, 0x06, 0x30, \ + 0xd1, 0x37, 0x98, 0x73, \ + 0x84, 0xa5, 0x86, 0x7d, \ + 0xe0, 0x84, 0x0c, 0x35, \ + 0x82, 0x31, 0x98, 0x44, \ + 0xe0, 0x1c, 0x0c, 0x55, \ + 0xe0, 0x84, 0x4c, 0x35, \ + 0xec, 0x04, 0x08, 0x40, \ + 0xc2, 0x37, 0x89, 0x3d, \ + 0xf1, 0x88, 0x44, 0x02, \ + 0x4a, 0x23, 0x86, 0x53, \ + 0x4a, 0x03, 0xe0, 0x30, \ + 0xde, 0xc5, 0xe0, 0x60, \ + 0xe1, 0x30, 0x0c, 0x05, \ + 0x4c, 0x25, 0xff, 0x21, \ + 0x8c, 0x2b, 0xe0, 0x94, \ + 0xe8, 0x08, 0x77, 0x28, \ + 0x0c, 0x05, 0x80, 0x1b, \ + 0x0c, 0x25, 0xe0, 0x8c, \ + 0x48, 0x02, 0xe0, 0x88, \ + 0x85, 0x4d, 0xf1, 0x84, \ + 0x80, 0x55, 0x80, 0x3b, \ + 0xab, 0x1d, 0x88, 0x3b, \ + 0xe1, 0x38, 0x4c, 0x35, \ + 0x0c, 0x19, 0x80, 0xd5, \ + 0x42, 0x0a, 0xe1, 0xd0, \ + 0x92, 0xb5, 0xe0, 0x90, \ + 0xe0, 0x34, 0x0c, 0x15, \ + 0xf1, 0x88, 0x46, 0x02, \ + 0x86, 0xe5, 0x86, 0x1d, \ + 0xa6, 0x03, 0x83, 0x01, \ + 0x06, 0x04, 0x82, 0x03, \ + 0x86, 0x03, 0xe1, 0xfc, \ + 0x0c, 0x35, 0x83, 0x07, \ + 0xa0, 0x23, 0xe0, 0x84, \ + 0xe0, 0x88, 0x4c, 0x25, \ + 0xe0, 0x1c, 0x0c, 0x05, \ + 0x82, 0x31, 0x84, 0x13, \ + 0xe0, 0x84, 0x4c, 0x35, \ + 0x4c, 0x15, 0xc2, 0x37, \ + 0x86, 0x03, 0xe0, 0x8c, \ + 0xe0, 0x30, 0x40, 0x23, \ + 0x40, 0x13, 0x8f, 0x21, \ + 0x4c, 0x25, 0xe0, 0x60, \ + 0xe9, 0x15, 0xe0, 0x98, \ + 0xe1, 0x38, 0x0c, 0x15, \ + 0xe0, 0x38, 0x0c, 0x35, \ + 0xe0, 0xa4, 0x0c, 0x45, \ + 0xf1, 0x88, 0x44, 0x12, \ + 0x89, 0x2d, 0x86, 0x43, \ + 0x8c, 0x1b, 0x8a, 0x65, \ + 0xe8, 0x08, 0x6d, 0x48, \ + 0xe0, 0x84, 0x0c, 0x15, \ + 0xe0, 0x1c, 0x0c, 0x25, \ + 0x0c, 0x35, 0x82, 0x11, \ + 0x4c, 0x15, 0xe0, 0x88, \ + 0xc2, 0x17, 0xe0, 0x84, \ + 0xe0, 0x8c, 0x4c, 0x05, \ + 0x44, 0x33, 0x82, 0x23, \ + 0x8f, 0x11, 0xe0, 0x30, \ + 0xe0, 0x60, 0x44, 0x03, \ + 0xe0, 0x98, 0x4c, 0x15, \ + 0x88, 0xf1, 0x8c, 0x0b, \ + 0xf2, 0x1c, 0x0c, 0x0c, \ + 0xe0, 0x08, 0x12, 0xa8, \ + 0x4c, 0x15, 0x85, 0x11, \ + 0x4c, 0x15, 0xe0, 0x98, \ + 0x00, 0x00, 0xe0, 0x9c, \ + 0xfd, 0x11, 0xec, 0x10, \ + 0xe0, 0xbc, 0x4c, 0x05, \ + 0xe0, 0x94, 0x4c, 0x15, \ + 0x0c, 0x05, 0xe1, 0xc5, \ + 0x87, 0x21, 0xe0, 0xa0, \ + 0xe0, 0x44, 0x0c, 0x35, \ + 0xe0, 0x98, 0x4c, 0x25, \ + 0xe6, 0x8a, 0x40, 0x0a, \ + 0xe0, 0x9c, 0x4c, 0x25, \ + 0xe0, 0x48, 0x0c, 0x25, \ + 0xf1, 0x8c, 0x46, 0x12, \ + 0xec, 0x18, 0x00, 0x00, \ + 0x80, 0x3d, 0xff, 0x41, \ + 0xe0, 0x94, 0x4c, 0x45, \ + 0x4c, 0x35, 0xa4, 0x13, \ + 0x4c, 0x15, 0xe0, 0xbc, \ + 0x2f, 0x18, 0xe0, 0xc0, \ + 0x4c, 0x05, 0x9f, 0xfc, \ + 0xdd, 0xd5, 0xe0, 0xbc, \ + 0xe0, 0x78, 0x0c, 0x05, \ + 0x88, 0x15, 0xa1, 0x0d, \ + 0xe0, 0x78, 0x0c, 0x35, \ + 0xe0, 0x20, 0x0c, 0x05, \ + 0xf1, 0x8c, 0x46, 0x22, \ + 0xe0, 0x88, 0x08, 0x24, \ + 0x81, 0x41, 0x88, 0xc3, \ + 0xf3, 0x74, 0x84, 0x5b, \ + 0xf8, 0x0c, 0x18, 0x85, \ + 0xc4, 0x87, 0xc4, 0x77, \ + 0xfc, 0x1c, 0x00, 0x75, \ + 0xfc, 0x20, 0x00, 0x85, \ + 0xf1, 0x9c, 0x4e, 0x82, \ + 0x80, 0x45, 0x88, 0x7d, \ + 0x86, 0x5d, 0x8a, 0x2b, \ + 0xec, 0x04, 0x0a, 0x50, \ + 0xfa, 0x10, 0x48, 0x72, \ + 0x0c, 0x05, 0xfa, 0xb5, \ + 0x98, 0x34, 0xe0, 0x84, \ + 0x0c, 0x45, 0x82, 0x01, \ + 0x0c, 0x55, 0xe0, 0x3c, \ + 0x4c, 0x05, 0xe0, 0x1c, \ + 0x06, 0x30, 0xe0, 0x84, \ + 0x87, 0x0d, 0xec, 0x04, \ + 0x46, 0x22, 0xc2, 0x07, \ + 0x80, 0x53, 0xf1, 0x90, \ + 0xe0, 0x30, 0x4a, 0x33, \ + 0xe0, 0x60, 0x4a, 0x23, \ + 0x9f, 0xfc, 0x2b, 0x18, \ + 0x4c, 0x05, 0x85, 0x01, \ + 0x4c, 0x05, 0xe0, 0x98, \ + 0x00, 0x10, 0xe0, 0x9c, \ + 0xf3, 0xa5, 0xec, 0x04, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x0c, 0x15, 0x80, 0x6b, \ + 0xa1, 0x1d, 0xe0, 0x90, \ + 0xe0, 0x18, 0x0c, 0x05, \ + 0x0c, 0x75, 0x87, 0x21, \ + 0x4c, 0x25, 0xe0, 0x84, \ + 0xac, 0x95, 0xe0, 0x98, \ + 0xe0, 0x8c, 0x0c, 0x25, \ + 0xac, 0x05, 0x85, 0x1d, \ + 0xe0, 0x88, 0x04, 0x14, \ + 0xf0, 0x88, 0x46, 0x02, \ + 0xfc, 0x08, 0x00, 0x25, \ + 0xf8, 0x10, 0x06, 0x45, \ + 0xe0, 0x20, 0x0c, 0x55, \ + 0xf8, 0x08, 0x06, 0xc5, \ + 0xf8, 0x18, 0x06, 0x35, \ + 0xc4, 0x47, 0xc4, 0x27, \ + 0xfc, 0x08, 0x0a, 0x25, \ + 0xfc, 0x10, 0x0a, 0x45, \ + 0xc4, 0x37, 0xc4, 0xc7, \ + 0xfc, 0x30, 0x0a, 0xc5, \ + 0xfc, 0x0c, 0x0a, 0x35, \ + 0xa6, 0xc3, 0xa8, 0x23, \ + 0x90, 0x05, 0x84, 0xcd, \ + 0xe0, 0x34, 0x0c, 0x25, \ + 0x06, 0x24, 0x84, 0x21, \ + 0xfd, 0x37, 0xe1, 0x7c, \ + 0x85, 0x27, 0x86, 0x23, \ + 0x04, 0x14, 0xa4, 0x13, \ + 0x4c, 0x15, 0xe0, 0x88, \ + 0x42, 0x02, 0xe0, 0x90, \ + 0x86, 0x34, 0xf0, 0x88, \ + 0xfc, 0x08, 0x00, 0x25, \ + 0xc4, 0x37, 0x9a, 0x14, \ + 0x02, 0x35, 0xc4, 0x27, \ + 0x02, 0x25, 0xfc, 0x0c, \ + 0xa4, 0x33, 0xfc, 0x08, \ + 0x0c, 0x55, 0x89, 0x21, \ + 0x08, 0x50, 0xe0, 0x90, \ + 0xc4, 0x57, 0xec, 0x04, \ + 0xe0, 0x88, 0x18, 0x44, \ + 0xfc, 0x14, 0x00, 0x55, \ + 0xfc, 0x30, 0x00, 0xc5, \ + 0xc4, 0xc7, 0xc4, 0x57, \ + 0xfc, 0x14, 0x02, 0x55, \ + 0xfc, 0x30, 0x02, 0xc5, \ + 0xa3, 0x5d, 0xb8, 0x53, \ + 0x18, 0x30, 0x82, 0xb5, \ + 0x10, 0xc4, 0xe4, 0x08, \ + 0xfd, 0x87, 0xe1, 0x7c, \ + 0x85, 0xc7, 0x90, 0xc3, \ + 0x80, 0x95, 0x99, 0x5d, \ + 0xe0, 0x90, 0x4c, 0x45, \ + 0x04, 0x20, 0x8a, 0x3b, \ + 0xa1, 0x2b, 0xec, 0x04, \ + 0x0c, 0x05, 0xf6, 0xb5, \ + 0x83, 0x21, 0xe0, 0x90, \ + 0x7d, 0x78, 0x8c, 0x1b, \ + 0x0c, 0x45, 0xe8, 0x04, \ + 0x0c, 0x15, 0xe1, 0x38, \ + 0x0c, 0x35, 0xe0, 0x34, \ + 0x0c, 0xc5, 0xe0, 0x94, \ + 0x44, 0x42, 0xe0, 0x88, \ + 0xff, 0x51, 0xf1, 0x84, \ + 0x40, 0x22, 0x8b, 0x3b, \ + 0x40, 0xc2, 0xfb, 0x00, \ + 0x4c, 0x05, 0xfa, 0x00, \ + 0xb0, 0x85, 0xe0, 0x90, \ + 0xe0, 0x84, 0x0c, 0x35, \ + 0x18, 0xe8, 0x8a, 0x3d, \ + 0x0c, 0x25, 0x80, 0x00, \ + 0x10, 0x34, 0xe0, 0x1c, \ + 0x84, 0x83, 0xe0, 0x84, \ + 0xe0, 0x30, 0x10, 0x53, \ + 0xe0, 0xa4, 0x0c, 0xc5, \ + 0xf1, 0xb0, 0x52, 0x52, \ + 0x0f, 0x38, 0x93, 0x0d, \ + 0x82, 0x31, 0x80, 0x00, \ + 0xe0, 0x84, 0x08, 0x34, \ + 0xe0, 0x84, 0x4c, 0x35, \ + 0x44, 0x03, 0x88, 0x23, \ + 0x82, 0x03, 0xe0, 0x30, \ + 0xe0, 0x60, 0x44, 0x03, \ + 0xe0, 0x50, 0x0c, 0x85, \ + 0x00, 0x04, 0x91, 0x11, \ + 0x4c, 0x15, 0xfb, 0x00, \ + 0x8c, 0x1b, 0xe0, 0x50, \ + 0xe8, 0x08, 0x59, 0x88, \ + 0x0c, 0x25, 0x98, 0x14, \ + 0x0c, 0x35, 0xe0, 0x84, \ + 0x02, 0x10, 0xe0, 0x1c, \ + 0x83, 0x2d, 0xec, 0x04, \ + 0x84, 0x33, 0xc2, 0x27, \ + 0xe0, 0x60, 0x46, 0x03, \ + 0xe0, 0x50, 0x4c, 0x85, \ + 0xc0, 0x00, 0x12, 0x58, \ + 0x0c, 0x05, 0xab, 0x95, \ + 0xff, 0x11, 0xe0, 0x94, \ + 0x86, 0xd5, 0x83, 0x0b, \ + 0xe1, 0x38, 0x0c, 0x05, \ + 0xe0, 0x34, 0x0c, 0x15, \ + 0xa2, 0x03, 0x98, 0x24, \ + 0xe0, 0x90, 0x4c, 0x05, \ + 0xe4, 0x04, 0x02, 0x70, \ + 0xe0, 0x1c, 0x0c, 0x35, \ + 0xec, 0x04, 0x04, 0x20, \ + 0x0c, 0x25, 0x85, 0x1d, \ + 0x4c, 0x15, 0xe0, 0x8c, \ + 0xc2, 0x17, 0xe0, 0x84, \ + 0xe0, 0x94, 0x4c, 0x05, \ + 0x46, 0x03, 0x82, 0x33, \ + 0x46, 0x23, 0xe0, 0x30, \ + 0x13, 0x88, 0xe0, 0x60, \ + 0x0c, 0x15, 0x80, 0x00, \ + 0x0c, 0x05, 0xe0, 0x94, \ + 0x0c, 0x25, 0xe0, 0x88, \ + 0x46, 0x12, 0xe0, 0xa4, \ + 0x84, 0x3d, 0xf1, 0x80, \ + 0xe0, 0x90, 0x4c, 0x15, \ + 0x0c, 0x25, 0x86, 0x85, \ + 0x85, 0x31, 0xe0, 0x34, \ + 0xe0, 0x98, 0x4c, 0x35, \ + 0x02, 0x10, 0x80, 0x23, \ + 0x04, 0x20, 0xec, 0x04, \ + 0x4c, 0x35, 0xec, 0x0c, \ + 0x84, 0x1d, 0xe0, 0x9c, \ + 0xe0, 0xbc, 0x4c, 0x15, \ + 0x4c, 0x25, 0xff, 0x11, \ + 0x4c, 0x15, 0xe0, 0xc0, \ + 0x8a, 0x85, 0xe0, 0x94, \ + 0xe0, 0x98, 0x0c, 0x05, \ + 0x0d, 0xf8, 0xa7, 0x0b, \ + 0xa3, 0x35, 0x80, 0x00, \ + 0xe0, 0x84, 0x0c, 0x15, \ + 0x10, 0xc8, 0xa1, 0x1d, \ + 0x0c, 0x25, 0xc0, 0x00, \ + 0x06, 0x14, 0xe0, 0x1c, \ + 0x0c, 0x45, 0xe0, 0x84, \ + 0x84, 0x33, 0xe0, 0x40, \ + 0xe0, 0x30, 0x06, 0x33, \ + 0x48, 0x4a, 0x87, 0x51, \ + 0xa0, 0x33, 0xe0, 0x96, \ + 0x80, 0xe5, 0x88, 0x3d, \ + 0x04, 0x09, 0x82, 0x23, \ + 0x82, 0x01, 0xe1, 0xe0, \ + 0xe1, 0xe0, 0x44, 0x01, \ + 0xe0, 0x84, 0x0c, 0x05, \ + 0xe0, 0x1c, 0x0c, 0x15, \ + 0xe0, 0x88, 0x0c, 0x25, \ + 0x80, 0x13, 0xc2, 0x07, \ + 0xe0, 0x30, 0x42, 0x23, \ + 0x0c, 0x15, 0x9d, 0xb5, \ + 0x0c, 0x25, 0xe0, 0x84, \ + 0x0c, 0x35, 0xe0, 0x3c, \ + 0x08, 0x10, 0xe0, 0x1c, \ + 0x02, 0x44, 0xe4, 0x04, \ + 0x82, 0x33, 0xe0, 0x84, \ + 0xf0, 0x80, 0x42, 0x22, \ + 0xe0, 0x30, 0x46, 0x03, \ + 0xe0, 0x60, 0x46, 0x13, \ + 0xe1, 0xd0, 0x0c, 0x09, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0xe0, 0x8c, 0x4c, 0x15, \ + 0xe0, 0x84, 0x4c, 0x45, \ + 0xc0, 0x00, 0x0c, 0x48, \ + 0x0c, 0x15, 0x99, 0xf5, \ + 0x0c, 0x35, 0xe0, 0xa4, \ + 0x42, 0x22, 0xe0, 0x84, \ + 0x83, 0x0d, 0xf1, 0x84, \ + 0xe4, 0x04, 0x02, 0x30, \ + 0xe0, 0x84, 0x4c, 0x15, \ + 0x98, 0x34, 0x92, 0xc5, \ + 0xe0, 0x1c, 0x0c, 0x45, \ + 0xe0, 0x8c, 0x0c, 0x55, \ + 0xec, 0x04, 0x06, 0x30, \ + 0xc2, 0x17, 0x87, 0x1d, \ + 0x48, 0x23, 0x82, 0x43, \ + 0x48, 0x53, 0xe0, 0x30, \ + 0xac, 0x85, 0xe0, 0x60, \ + 0xe1, 0xd0, 0x0c, 0x19, \ + 0xe0, 0x3c, 0x0c, 0x25, \ + 0xe0, 0x84, 0x42, 0x0a, \ + 0x80, 0x85, 0x84, 0x03, \ + 0x4d, 0x78, 0x8c, 0x1b, \ + 0x0c, 0x15, 0xe8, 0x08, \ + 0x98, 0x24, 0xe0, 0x84, \ + 0xe0, 0x1c, 0x0c, 0x35, \ + 0x0c, 0x45, 0x82, 0x11, \ + 0x0a, 0x14, 0xe0, 0x90, \ + 0x04, 0x20, 0xe0, 0x84, \ + 0x8a, 0x33, 0xec, 0x04, \ + 0x4c, 0x15, 0x85, 0x1d, \ + 0x46, 0x43, 0xe0, 0x84, \ + 0x46, 0x03, 0xe0, 0x30, \ + 0x87, 0x01, 0xe0, 0x60, \ + 0x8f, 0x01, 0x80, 0x55, \ + 0xe0, 0x98, 0x4c, 0x05, \ + 0x93, 0x15, 0x96, 0x95, \ + 0xe0, 0x60, 0x10, 0x83, \ + 0xfb, 0x10, 0x48, 0x82, \ + 0x82, 0xc3, 0xa0, 0x43, \ + 0x8e, 0x05, 0x98, 0x4d, \ + 0x82, 0x31, 0x98, 0x44, \ + 0xe0, 0x84, 0x4c, 0x35, \ + 0xec, 0x04, 0x08, 0x40, \ + 0xc2, 0x37, 0x89, 0x3d, \ + 0x80, 0x13, 0x86, 0x23, \ + 0xe0, 0x30, 0x44, 0x03, \ + 0xe0, 0x60, 0x44, 0x13, \ + 0x8f, 0x65, 0x90, 0x35, \ + 0xe0, 0x1c, 0x0c, 0x35, \ + 0xe0, 0x8c, 0x0c, 0x25, \ + 0x82, 0x33, 0xc2, 0x17, \ + 0xe0, 0x30, 0x46, 0x03, \ + 0xe0, 0x60, 0x46, 0x23, \ + 0xe1, 0xd0, 0x0c, 0x09, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x00, 0x24, 0x82, 0xc5, \ + 0x8c, 0x1b, 0xfb, 0x00, \ + 0xe8, 0x08, 0x49, 0x08, \ + 0xe0, 0x84, 0x0c, 0x15, \ + 0xe0, 0x1c, 0x0c, 0x25, \ + 0x82, 0x23, 0xc2, 0x17, \ + 0xe0, 0x60, 0x44, 0x03, \ + 0x0c, 0x15, 0x98, 0x04, \ + 0x00, 0x00, 0xe0, 0x84, \ + 0x81, 0x1d, 0xec, 0x04, \ + 0x4c, 0x05, 0x87, 0x01, \ + 0xce, 0x75, 0xe0, 0x98, \ + 0x0c, 0x15, 0x89, 0xf5, \ + 0x87, 0x41, 0xe0, 0x40, \ + 0xe0, 0x92, 0x42, 0x1a, \ + 0x82, 0x5d, 0xa0, 0x53, \ + 0x86, 0x23, 0x80, 0xe5, \ + 0xe1, 0xe0, 0x04, 0x09, \ + 0x44, 0x01, 0x82, 0x01, \ + 0x0c, 0x05, 0xe1, 0xe0, \ + 0x0c, 0x15, 0xe0, 0x84, \ + 0x0c, 0x25, 0xe0, 0x1c, \ + 0xc2, 0x07, 0xe0, 0x90, \ + 0x42, 0x23, 0x80, 0x13, \ + 0x87, 0x01, 0xe0, 0x30, \ + 0xe0, 0x98, 0x4c, 0x05, \ + 0xe0, 0x88, 0x0c, 0x05, \ + 0xe0, 0xa4, 0x0c, 0x25, \ + 0xe0, 0x90, 0x0c, 0x15, \ + 0x84, 0x1d, 0x80, 0x23, \ + 0x0c, 0x25, 0x8c, 0x45, \ + 0x80, 0x23, 0xe0, 0x34, \ + 0x96, 0xc5, 0x85, 0x1d, \ + 0xe0, 0x84, 0x0c, 0x15, \ + 0xe0, 0x1c, 0x0c, 0x25, \ + 0xe0, 0x3c, 0x0c, 0x35, \ + 0xe4, 0x04, 0x08, 0x10, \ + 0xe0, 0x84, 0x02, 0x44, \ + 0x42, 0x32, 0x82, 0x23, \ + 0x44, 0x03, 0xf0, 0x80, \ + 0x44, 0x13, 0xe0, 0x30, \ + 0x0c, 0x09, 0xe0, 0x60, \ + 0x40, 0x0a, 0xe1, 0xd0, \ + 0x4c, 0x45, 0xe0, 0x84, \ + 0x82, 0xc5, 0xe0, 0x84, \ + 0xfb, 0x00, 0x00, 0x14, \ + 0x43, 0x78, 0x8c, 0x1b, \ + 0x0c, 0x15, 0xe8, 0x08, \ + 0x0c, 0x25, 0xe0, 0x84, \ + 0xc2, 0x17, 0xe0, 0x1c, \ + 0x44, 0x03, 0x82, 0x23, \ + 0x8f, 0x01, 0xe0, 0x60, \ + 0xe0, 0x98, 0x4c, 0x05, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x1c, \ + 0xe0, 0x84, 0x0c, 0x15, \ + 0x88, 0x15, 0xa1, 0x1d, \ + 0xe0, 0x1c, 0x0c, 0x25, \ + 0xe0, 0x84, 0x06, 0x14, \ + 0xe0, 0x40, 0x0c, 0x45, \ + 0x06, 0x33, 0x84, 0x33, \ + 0x87, 0x51, 0xe0, 0x30, \ + 0xe0, 0x96, 0x48, 0x4a, \ + 0x88, 0x3d, 0xa0, 0x33, \ + 0x82, 0x23, 0x80, 0xe5, \ + 0xe1, 0xe0, 0x04, 0x09, \ + 0x44, 0x01, 0x82, 0x01, \ + 0x0c, 0x05, 0xe1, 0xe0, \ + 0x0c, 0x15, 0xe0, 0x84, \ + 0x0c, 0x25, 0xe0, 0x1c, \ + 0xc2, 0x07, 0xe0, 0x88, \ + 0x42, 0x23, 0x80, 0x13, \ + 0x0c, 0x05, 0xe0, 0x30, \ + 0x82, 0x71, 0xe0, 0x84, \ + 0x8f, 0x01, 0x8f, 0x0b, \ + 0xe0, 0x98, 0x4c, 0x05, \ + 0x8c, 0x0b, 0xf4, 0x55, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0x26, 0x78, 0xf2, 0x1c, \ + 0x0c, 0x05, 0xe0, 0x04, \ + 0x0c, 0x25, 0xe0, 0xf8, \ + 0x87, 0x31, 0xe0, 0xfc, \ + 0xe0, 0x98, 0x4c, 0x35, \ + 0xe0, 0x94, 0x4c, 0x15, \ + 0xf1, 0x80, 0x40, 0x12, \ + 0x4c, 0x05, 0xa4, 0x13, \ + 0x4c, 0x15, 0xe0, 0xbc, \ + 0xf7, 0xb5, 0xe0, 0xc0, \ + 0xf8, 0x0c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x84, 0x44, 0x82, 0x14, \ + 0x00, 0x25, 0x86, 0x54, \ + 0xc4, 0xc4, 0xe0, 0xa0, \ + 0x00, 0x35, 0xc6, 0x14, \ + 0x88, 0x53, 0xe0, 0x20, \ + 0xab, 0x2d, 0x98, 0x53, \ + 0x00, 0x15, 0x82, 0x53, \ + 0x8c, 0x5e, 0xe0, 0x18, \ + 0x08, 0x20, 0x84, 0xf5, \ + 0x0a, 0x30, 0xec, 0x10, \ + 0x81, 0xc1, 0xe4, 0x10, \ + 0x0a, 0x75, 0x82, 0x64, \ + 0x50, 0x62, 0xf8, 0x04, \ + 0x0c, 0x65, 0xf0, 0xb0, \ + 0x10, 0x85, 0xfc, 0x30, \ + 0x08, 0x40, 0xe0, 0x10, \ + 0xac, 0x73, 0xec, 0x04, \ + 0x90, 0x73, 0xa1, 0x4b, \ + 0x88, 0xc1, 0xd5, 0x7e, \ + 0xc4, 0x27, 0xfc, 0x15, \ + 0x06, 0x30, 0x84, 0x33, \ + 0xa1, 0x41, 0xec, 0x40, \ + 0x02, 0xc5, 0x82, 0x54, \ + 0x4c, 0x52, 0xfc, 0x08, \ + 0xcd, 0x74, 0xf0, 0x88, \ + 0x98, 0x64, 0xa8, 0x63, \ + 0x0a, 0x55, 0xc4, 0xc7, \ + 0x08, 0x40, 0xfc, 0x30, \ + 0xac, 0x73, 0xec, 0x10, \ + 0x8a, 0x73, 0xa9, 0x4b, \ + 0x88, 0x11, 0x8c, 0x7e, \ + 0x88, 0xf1, 0xfa, 0xd5, \ + 0xf0, 0x0c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xed, 0x80, 0x1e, 0xf0, \ + 0xe0, 0xbc, 0x00, 0x65, \ + 0xe0, 0xc0, 0x00, 0x15, \ + 0xa7, 0x1e, 0xff, 0x91, \ + 0x40, 0x95, 0x82, 0x6d, \ + 0x81, 0x11, 0xe0, 0x88, \ + 0xe0, 0x7c, 0x40, 0x15, \ + 0xe1, 0x3c, 0x40, 0x95, \ + 0xc0, 0x00, 0x1e, 0x78, \ + 0x99, 0x1e, 0x81, 0x11, \ + 0xe0, 0xb4, 0x00, 0x15, \ + 0xe0, 0x18, 0x00, 0x25, \ + 0xe0, 0xa8, 0x00, 0xc5, \ + 0xe0, 0xd4, 0x00, 0x35, \ + 0x00, 0x35, 0xa3, 0x3e, \ + 0x97, 0x3e, 0xe0, 0xd8, \ + 0xe0, 0xdc, 0x00, 0x35, \ + 0x00, 0x35, 0x89, 0x3e, \ + 0x00, 0x45, 0xe0, 0x6c, \ + 0x8d, 0x4e, 0xe0, 0x28, \ + 0xe6, 0x86, 0x48, 0xca, \ + 0xe0, 0x2c, 0x00, 0x55, \ + 0x4a, 0x86, 0x95, 0x5e, \ + 0xa6, 0x53, 0xe0, 0x3c, \ + 0xa0, 0x41, 0x9d, 0x5e, \ + 0x14, 0x44, 0x8c, 0x7b, \ + 0x06, 0x10, 0xe1, 0x94, \ + 0x83, 0x37, 0xe4, 0x04, \ + 0x06, 0x10, 0x93, 0x3e, \ + 0x85, 0x37, 0xe4, 0x08, \ + 0x06, 0x74, 0xfe, 0x3e, \ + 0x86, 0x23, 0xe0, 0x88, \ + 0xe4, 0x40, 0x06, 0x10, \ + 0xfc, 0x3e, 0x8b, 0x37, \ + 0x87, 0x17, 0x88, 0x11, \ + 0x0c, 0x20, 0xbe, 0x1e, \ + 0x00, 0x15, 0xec, 0x40, \ + 0xa1, 0x1e, 0xe0, 0x24, \ + 0xe0, 0x84, 0x02, 0xc4, \ + 0x8f, 0xce, 0x8b, 0x1e, \ + 0xbc, 0xce, 0x8e, 0xc3, \ + 0x81, 0x51, 0x81, 0x31, \ + 0x81, 0xc1, 0x81, 0x41, \ + 0x8e, 0x8b, 0x81, 0xb1, \ + 0x9b, 0xae, 0x9f, 0x7e, \ + 0x19, 0x0a, 0x91, 0x0e, \ + 0x18, 0xe8, 0xe7, 0x1c, \ + 0xa2, 0x4d, 0xc0, 0x00, \ + 0x42, 0xa2, 0x90, 0xab, \ + 0xa9, 0x1e, 0xf0, 0xb0, \ + 0x90, 0x55, 0xad, 0x9e, \ + 0xe0, 0x10, 0x0c, 0x25, \ + 0xe0, 0xac, 0x00, 0x75, \ + 0x0e, 0x75, 0xc4, 0x27, \ + 0x9b, 0xa4, 0xfc, 0x08, \ + 0xf1, 0xa8, 0x42, 0x72, \ + 0x88, 0xb3, 0x86, 0x1d, \ + 0xa5, 0xbe, 0x8e, 0x05, \ + 0x00, 0x75, 0x82, 0x9b, \ + 0x0e, 0x25, 0xe0, 0xb0, \ + 0x0e, 0x24, 0xfc, 0x08, \ + 0x8f, 0x14, 0xe0, 0x84, \ + 0x8b, 0xb4, 0x82, 0x73, \ + 0xe0, 0x2e, 0x4e, 0x7a, \ + 0x4e, 0x7a, 0xa1, 0xb4, \ + 0x0e, 0x70, 0xe6, 0xae, \ + 0xcf, 0x77, 0xe5, 0x00, \ + 0xe6, 0x86, 0x4e, 0x7a, \ + 0x8e, 0x23, 0x94, 0x23, \ + 0x44, 0x2a, 0x92, 0x7b, \ + 0x8d, 0x14, 0xf0, 0x0a, \ + 0xe6, 0x86, 0x44, 0x2a, \ + 0xe6, 0x00, 0x04, 0x20, \ + 0x84, 0x7d, 0xd1, 0x27, \ + 0xa1, 0x4b, 0x86, 0x05, \ + 0x40, 0x15, 0xa9, 0x14, \ + 0x81, 0xb1, 0xe0, 0x88, \ + 0x82, 0x55, 0x90, 0xab, \ + 0xe1, 0x3c, 0x40, 0x15, \ + 0x98, 0x23, 0xbc, 0x24, \ + 0x42, 0x22, 0xa7, 0x14, \ + 0xa7, 0x1e, 0xfb, 0x04, \ + 0xad, 0x94, 0x83, 0x41, \ + 0x86, 0x7b, 0x81, 0xa5, \ + 0x81, 0xf5, 0x88, 0x1b, \ + 0x81, 0x45, 0x86, 0x7b, \ + 0xa5, 0xb4, 0x86, 0x7b, \ + 0x90, 0xab, 0xad, 0x94, \ + 0x82, 0xbd, 0x9d, 0x14, \ + 0xc0, 0x00, 0x13, 0x98, \ + 0x83, 0x11, 0xa3, 0x4d, \ + 0xc0, 0x00, 0x0d, 0xa8, \ + 0xab, 0x7e, 0xaf, 0x1e, \ + 0xe0, 0x10, 0x0c, 0x35, \ + 0xe0, 0x20, 0x00, 0x25, \ + 0xe0, 0x20, 0x0c, 0x45, \ + 0xe0, 0x88, 0x0e, 0x34, \ + 0xfc, 0x1c, 0x04, 0x35, \ + 0x04, 0x45, 0xc4, 0x47, \ + 0x00, 0x85, 0xfc, 0x10, \ + 0xa3, 0x14, 0xe0, 0x10, \ + 0x88, 0x33, 0x83, 0xcd, \ + 0xfc, 0x1c, 0x10, 0x75, \ + 0x97, 0x14, 0x8e, 0x05, \ + 0x9a, 0xc5, 0x83, 0xcd, \ + 0x83, 0xcd, 0x89, 0x14, \ + 0xda, 0x84, 0xb0, 0x05, \ + 0xc4, 0x87, 0x82, 0x14, \ + 0xfc, 0x20, 0x02, 0x15, \ + 0x44, 0x22, 0xfc, 0x24, \ + 0x02, 0x24, 0xfa, 0x04, \ + 0x48, 0x32, 0xe0, 0x8c, \ + 0xc2, 0x47, 0xf1, 0x84, \ + 0x0d, 0xc8, 0x8a, 0x4d, \ + 0xa5, 0xbe, 0x80, 0x00, \ + 0xb3, 0x01, 0xc0, 0x14, \ + 0xe6, 0x82, 0x46, 0x3a, \ + 0xe4, 0x10, 0x12, 0x70, \ + 0x14, 0x30, 0xc7, 0x97, \ + 0xa1, 0xb4, 0xe4, 0x08, \ + 0xe6, 0xae, 0x52, 0x9a, \ + 0xfc, 0x20, 0x02, 0x35, \ + 0x8e, 0xa3, 0xc5, 0xa7, \ + 0xad, 0x01, 0x90, 0x91, \ + 0xe6, 0x82, 0x42, 0x2a, \ + 0x92, 0xa3, 0xc9, 0x97, \ + 0xe0, 0x98, 0x0e, 0x34, \ + 0x94, 0x73, 0x82, 0xa3, \ + 0x04, 0x06, 0x8f, 0x81, \ + 0x83, 0xa1, 0xe0, 0x04, \ + 0x8d, 0x45, 0x83, 0x91, \ + 0xf8, 0x30, 0x0c, 0x85, \ + 0xf8, 0x20, 0x0c, 0x45, \ + 0xf8, 0x10, 0x0c, 0x95, \ + 0x98, 0xa4, 0xc4, 0x87, \ + 0xfc, 0x20, 0x04, 0x15, \ + 0x04, 0x45, 0xc4, 0x47, \ + 0xc4, 0x97, 0xfc, 0x10, \ + 0xfc, 0x24, 0x04, 0x95, \ + 0x04, 0x25, 0xc4, 0xa7, \ + 0x82, 0x43, 0xfc, 0x28, \ + 0x84, 0x43, 0x92, 0x43, \ + 0x44, 0x12, 0x93, 0x14, \ + 0x08, 0x34, 0xfa, 0x10, \ + 0xa4, 0x43, 0xe0, 0x84, \ + 0x98, 0x25, 0x8a, 0x4d, \ + 0xc2, 0x14, 0xa5, 0xbe, \ + 0xe4, 0x10, 0x12, 0x70, \ + 0xa1, 0xb4, 0xc7, 0x97, \ + 0xe6, 0xae, 0x52, 0x9a, \ + 0xfc, 0x20, 0x02, 0x15, \ + 0x86, 0x73, 0x84, 0x91, \ + 0xc4, 0x77, 0xc5, 0x97, \ + 0x82, 0x93, 0x8e, 0x93, \ + 0x4e, 0x92, 0x90, 0x11, \ + 0x06, 0x14, 0xf0, 0x88, \ + 0x91, 0x81, 0xe1, 0x90, \ + 0xe0, 0x08, 0x04, 0x06, \ + 0x89, 0xa1, 0x87, 0x91, \ + 0x0c, 0x95, 0x85, 0xb5, \ + 0x98, 0x14, 0xf8, 0x10, \ + 0xc4, 0x17, 0xc4, 0x97, \ + 0xfc, 0x24, 0x04, 0x45, \ + 0xfc, 0x04, 0x04, 0x15, \ + 0xfe, 0x24, 0x88, 0x13, \ + 0xfa, 0x04, 0x44, 0x22, \ + 0xf1, 0x88, 0x48, 0x32, \ + 0x8a, 0x4d, 0xc2, 0x47, \ + 0xa5, 0xbe, 0x9a, 0x65, \ + 0xfc, 0x24, 0x10, 0x15, \ + 0xe4, 0x10, 0x10, 0x70, \ + 0xc7, 0x87, 0x86, 0x73, \ + 0xa1, 0xb4, 0x82, 0x73, \ + 0xe6, 0xae, 0x46, 0x8a, \ + 0xc4, 0x77, 0x84, 0x73, \ + 0xc5, 0x37, 0x84, 0x31, \ + 0x86, 0x73, 0x88, 0x11, \ + 0xe1, 0x8c, 0x06, 0x14, \ + 0x04, 0x06, 0x91, 0x81, \ + 0x85, 0xa1, 0xe0, 0x08, \ + 0x42, 0x3a, 0x85, 0x91, \ + 0x94, 0x13, 0xe6, 0xae, \ + 0x82, 0x73, 0xf3, 0x1f, \ + 0xf0, 0x0a, 0x42, 0x7a, \ + 0x42, 0x1a, 0x95, 0x34, \ + 0x84, 0x13, 0xe6, 0x8e, \ + 0x82, 0x4d, 0xf1, 0x1f, \ + 0xaf, 0x04, 0x84, 0x65, \ + 0x91, 0x04, 0xa3, 0x0b, \ + 0x40, 0x55, 0xa9, 0x54, \ + 0x99, 0x14, 0xe0, 0x88, \ + 0x99, 0x1e, 0x82, 0x11, \ + 0xad, 0x54, 0x90, 0x65, \ + 0x81, 0xa5, 0x9f, 0xa4, \ + 0xad, 0x54, 0x8a, 0x4b, \ + 0xab, 0x74, 0x9f, 0xa4, \ + 0x8a, 0x4b, 0x8b, 0x25, \ + 0x9f, 0xa4, 0xad, 0x54, \ + 0xa5, 0xb4, 0x91, 0x04, \ + 0x89, 0xb5, 0xab, 0x74, \ + 0x8a, 0x4b, 0xaf, 0x4e, \ + 0x89, 0x75, 0x92, 0x5b, \ + 0xc4, 0x87, 0x98, 0x84, \ + 0xfc, 0x20, 0x04, 0x15, \ + 0x44, 0x22, 0xbe, 0x24, \ + 0x02, 0x24, 0xfa, 0x04, \ + 0x48, 0x32, 0xe0, 0x84, \ + 0xc2, 0x47, 0xf1, 0x84, \ + 0x86, 0xc5, 0x8a, 0x4d, \ + 0x00, 0x15, 0xa5, 0xbe, \ + 0x12, 0x70, 0xe0, 0x14, \ + 0xc7, 0x97, 0xe4, 0x10, \ + 0x52, 0x9a, 0xa1, 0xb4, \ + 0x02, 0x15, 0xe6, 0xae, \ + 0x90, 0x91, 0xfc, 0x20, \ + 0xc9, 0x97, 0x86, 0x73, \ + 0x82, 0x23, 0x92, 0x73, \ + 0x84, 0x11, 0xc4, 0x27, \ + 0x06, 0x14, 0x84, 0x73, \ + 0x8f, 0x81, 0xe1, 0x88, \ + 0xe0, 0x04, 0x04, 0x06, \ + 0x8a, 0x4b, 0xf1, 0x25, \ + 0xab, 0x74, 0xad, 0x54, \ + 0x8a, 0x4b, 0x83, 0xc5, \ + 0xab, 0x74, 0x92, 0x5b, \ + 0x00, 0x25, 0x83, 0x85, \ + 0x00, 0x15, 0xe0, 0xec, \ + 0x9f, 0xa4, 0xe0, 0xa0, \ + 0xf0, 0xb0, 0x46, 0xa2, \ + 0x87, 0x31, 0x86, 0x23, \ + 0xe6, 0x8e, 0x42, 0x1a, \ + 0xe4, 0x10, 0x06, 0x20, \ + 0xa5, 0xb4, 0x83, 0x3d, \ + 0x80, 0x45, 0xab, 0x74, \ + 0x83, 0x11, 0xa7, 0x2e, \ + 0xe0, 0x7c, 0x40, 0x15, \ + 0xaf, 0x1e, 0x85, 0x11, \ + 0x94, 0x8b, 0x82, 0xc1, \ + 0xf0, 0xb0, 0x42, 0xa2, \ + 0xec, 0x04, 0x02, 0x10, \ + 0x85, 0x1d, 0xa7, 0x24, \ + 0x8a, 0x1b, 0x88, 0x61, \ + 0x8e, 0x3b, 0x82, 0x9b, \ + 0xaf, 0x44, 0x88, 0x5b, \ + 0xdf, 0xfc, 0x27, 0x38, \ + 0x92, 0x1b, 0x81, 0x25, \ + 0x99, 0x24, 0xa1, 0x1d, \ + 0x82, 0x15, 0x82, 0x3b, \ + 0xe0, 0xec, 0x00, 0x15, \ + 0x83, 0x2d, 0x82, 0x21, \ + 0x40, 0x35, 0x80, 0x65, \ + 0x81, 0x11, 0xe0, 0x88, \ + 0xe0, 0x98, 0x40, 0x15, \ + 0xe5, 0x80, 0x1e, 0xf0, \ + 0xf0, 0x7c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xed, 0x80, 0x1e, 0xf0, \ + 0x14, 0x05, 0x80, 0xab, \ + 0x14, 0x15, 0xe0, 0xbc, \ + 0xff, 0x21, 0xe0, 0xc0, \ + 0x83, 0x0d, 0xa9, 0x1e, \ + 0xe0, 0x8c, 0x54, 0x25, \ + 0x54, 0x15, 0x81, 0x11, \ + 0x54, 0x25, 0xe0, 0x80, \ + 0x1d, 0x78, 0xe1, 0x40, \ + 0xff, 0x11, 0xc0, 0x00, \ + 0x81, 0x11, 0x8b, 0x1e, \ + 0x14, 0x15, 0x9d, 0x1e, \ + 0x14, 0x45, 0xe0, 0xb4, \ + 0x14, 0x85, 0xe0, 0xa8, \ + 0x14, 0x25, 0xe0, 0x18, \ + 0xa7, 0x2e, 0xe0, 0xe0, \ + 0xe0, 0xe4, 0x14, 0x25, \ + 0x14, 0x25, 0x9b, 0x2e, \ + 0x14, 0x35, 0xe0, 0x6c, \ + 0x8f, 0x3e, 0xe0, 0xe8, \ + 0xe6, 0x86, 0x46, 0x4a, \ + 0xe0, 0x28, 0x14, 0x55, \ + 0x4a, 0x86, 0x93, 0x5e, \ + 0xa4, 0x53, 0xe0, 0x3c, \ + 0xa0, 0x31, 0x9f, 0x5e, \ + 0xe1, 0x94, 0x16, 0x34, \ + 0xe4, 0x04, 0x04, 0x10, \ + 0x99, 0x2e, 0x83, 0x27, \ + 0xe4, 0x08, 0x04, 0x10, \ + 0x8d, 0x2e, 0x85, 0x27, \ + 0xf1, 0x90, 0x44, 0x02, \ + 0x06, 0x04, 0xc4, 0x27, \ + 0x4e, 0x82, 0xe0, 0x88, \ + 0x86, 0x83, 0xf0, 0x88, \ + 0xe4, 0x40, 0x04, 0x10, \ + 0x8b, 0x27, 0x88, 0x11, \ + 0x87, 0x17, 0x89, 0x2e, \ + 0x14, 0x15, 0xfe, 0x1e, \ + 0x97, 0x1e, 0xe0, 0x2c, \ + 0xe0, 0x24, 0x14, 0x15, \ + 0x02, 0x44, 0xa1, 0x1e, \ + 0x91, 0x1e, 0xe0, 0x84, \ + 0xa8, 0x1b, 0x95, 0x4e, \ + 0x81, 0x21, 0xfc, 0x1e, \ + 0x81, 0x11, 0x81, 0x51, \ + 0x81, 0x31, 0x81, 0xc1, \ + 0xa5, 0xae, 0xaf, 0x3e, \ + 0x19, 0x0a, 0xa3, 0xbe, \ + 0x17, 0xc8, 0xe7, 0x1c, \ + 0xa2, 0x1d, 0xc0, 0x00, \ + 0x9c, 0x34, 0x90, 0x35, \ + 0xe0, 0xac, 0x14, 0x65, \ + 0x0c, 0x65, 0xc4, 0x37, \ + 0x52, 0x62, 0xfc, 0x0c, \ + 0x84, 0x9d, 0xf1, 0xac, \ + 0x82, 0x43, 0xaf, 0x44, \ + 0x8c, 0xe5, 0xaf, 0x4e, \ + 0x14, 0x65, 0xad, 0x5e, \ + 0x0c, 0x35, 0xe0, 0xb0, \ + 0x0c, 0x34, 0xfc, 0x0c, \ + 0x95, 0x44, 0xe0, 0x84, \ + 0x96, 0x5b, 0x88, 0x63, \ + 0x4c, 0x6a, 0x91, 0xb4, \ + 0xa1, 0xb4, 0xe0, 0x2e, \ + 0xe6, 0xae, 0x4c, 0x6a, \ + 0x0c, 0x60, 0x8a, 0xbb, \ + 0xcf, 0x67, 0xe5, 0x00, \ + 0xe6, 0x92, 0x4c, 0x6a, \ + 0x8c, 0x33, 0x96, 0x33, \ + 0x46, 0x3a, 0x92, 0x6b, \ + 0x93, 0x44, 0xf0, 0x0a, \ + 0xe6, 0x92, 0x46, 0x3a, \ + 0xe6, 0x00, 0x06, 0x30, \ + 0x86, 0x6d, 0xd1, 0x37, \ + 0xa1, 0x1b, 0x84, 0xc5, \ + 0xe0, 0x8c, 0x54, 0x05, \ + 0xaf, 0x2e, 0x81, 0x21, \ + 0xfc, 0x14, 0x84, 0x35, \ + 0x54, 0x05, 0x80, 0x13, \ + 0xa9, 0x24, 0xe1, 0x40, \ + 0xfa, 0x08, 0x44, 0x12, \ + 0x83, 0x11, 0xa9, 0x2e, \ + 0x84, 0x6b, 0x81, 0x75, \ + 0x81, 0xe5, 0x82, 0x9b, \ + 0x81, 0x35, 0x84, 0x6b, \ + 0xad, 0x54, 0x84, 0x6b, \ + 0xaf, 0x34, 0x9f, 0x24, \ + 0x12, 0xe8, 0x84, 0x3d, \ + 0xa3, 0x1d, 0xc0, 0x00, \ + 0x0d, 0x88, 0x83, 0x91, \ + 0xad, 0x6e, 0xc0, 0x00, \ + 0xf8, 0x10, 0x10, 0x25, \ + 0xe0, 0x20, 0x14, 0x15, \ + 0xf8, 0x20, 0x10, 0x35, \ + 0x02, 0xb5, 0xc4, 0x27, \ + 0x04, 0x34, 0xfc, 0x08, \ + 0x02, 0x65, 0xe0, 0x88, \ + 0x14, 0x35, 0xfc, 0x08, \ + 0xa7, 0x44, 0xe0, 0x10, \ + 0x8c, 0xb3, 0x89, 0xcd, \ + 0xfc, 0x08, 0x06, 0x25, \ + 0x9b, 0x44, 0x8e, 0x65, \ + 0x9c, 0x65, 0x89, 0xcd, \ + 0xa0, 0x64, 0x8f, 0x34, \ + 0xae, 0xa5, 0x87, 0xcd, \ + 0xc4, 0x67, 0xaa, 0x14, \ + 0xfc, 0x18, 0x02, 0x15, \ + 0x46, 0x32, 0x89, 0x34, \ + 0x02, 0x34, 0xfa, 0x04, \ + 0x42, 0xb2, 0xe0, 0x8c, \ + 0xc2, 0x17, 0xf1, 0x84, \ + 0x0d, 0x88, 0x8a, 0x1d, \ + 0xab, 0x9e, 0x80, 0x00, \ + 0xbe, 0x3e, 0xe8, 0x44, \ + 0x52, 0xba, 0xb3, 0x31, \ + 0x14, 0x20, 0xe6, 0x8e, \ + 0xc7, 0xa7, 0xe4, 0x10, \ + 0xa1, 0x34, 0x84, 0x91, \ + 0xe6, 0x8e, 0x54, 0xaa, \ + 0xfc, 0x18, 0x08, 0xb5, \ + 0xc5, 0x97, 0x86, 0x4b, \ + 0x90, 0xa1, 0x84, 0x93, \ + 0xbe, 0x34, 0xad, 0x21, \ + 0xe6, 0x8a, 0x44, 0x3a, \ + 0x94, 0x93, 0xc9, 0xa7, \ + 0xe0, 0x98, 0x06, 0xb4, \ + 0x44, 0x92, 0x84, 0x93, \ + 0x8f, 0x61, 0xf0, 0x8c, \ + 0xe0, 0x04, 0x12, 0x06, \ + 0x83, 0x31, 0x83, 0xa1, \ + 0xab, 0x9e, 0x8d, 0x75, \ + 0x10, 0x65, 0xa0, 0x34, \ + 0x10, 0x95, 0xe0, 0x10, \ + 0xc4, 0x37, 0xe0, 0x20, \ + 0xe0, 0x30, 0x10, 0xa5, \ + 0xfc, 0x0c, 0x02, 0x45, \ + 0x02, 0x65, 0xc4, 0x67, \ + 0xc4, 0x97, 0xfc, 0x18, \ + 0xfc, 0x24, 0x02, 0x95, \ + 0x02, 0x15, 0xc4, 0xa7, \ + 0x88, 0x63, 0xfc, 0x28, \ + 0x82, 0x63, 0x92, 0x63, \ + 0x4c, 0x12, 0x99, 0x14, \ + 0x02, 0xb4, 0xfa, 0x18, \ + 0xac, 0x13, 0xe0, 0x84, \ + 0x98, 0x45, 0x8a, 0x1d, \ + 0xd2, 0x44, 0xa5, 0x44, \ + 0xe4, 0x10, 0x12, 0x20, \ + 0xa1, 0xa4, 0xc7, 0x97, \ + 0xe6, 0xaa, 0x52, 0x9a, \ + 0xfc, 0x0c, 0x08, 0x35, \ + 0x84, 0x91, 0x94, 0x4b, \ + 0xc5, 0x97, 0x96, 0x23, \ + 0x84, 0x93, 0xc4, 0x27, \ + 0x90, 0x31, 0x86, 0x93, \ + 0xf0, 0x98, 0x44, 0x92, \ + 0xe1, 0x90, 0x16, 0x34, \ + 0x12, 0x06, 0x91, 0x61, \ + 0x87, 0x31, 0xe0, 0x08, \ + 0x85, 0xc5, 0x89, 0xa1, \ + 0xa0, 0x94, 0xab, 0x9e, \ + 0xe0, 0x10, 0x10, 0x45, \ + 0xc4, 0x47, 0xc4, 0x97, \ + 0xfc, 0x24, 0x02, 0x65, \ + 0xfc, 0x10, 0x02, 0x15, \ + 0x8d, 0x44, 0x8c, 0x13, \ + 0xfa, 0x04, 0x4c, 0x42, \ + 0xf1, 0x98, 0x42, 0xb2, \ + 0x8a, 0x1d, 0xc2, 0x17, \ + 0x06, 0x35, 0x98, 0x65, \ + 0x08, 0x20, 0xfc, 0x24, \ + 0x96, 0x23, 0xe4, 0x10, \ + 0x86, 0x23, 0xc7, 0x47, \ + 0x48, 0x4a, 0xa1, 0x94, \ + 0x8c, 0x23, 0xe6, 0xa6, \ + 0x84, 0x41, 0xc4, 0x27, \ + 0x88, 0x31, 0xc5, 0x47, \ + 0x92, 0x4b, 0x88, 0x23, \ + 0xe1, 0x8c, 0x16, 0x34, \ + 0x12, 0x06, 0x91, 0x61, \ + 0x85, 0xa1, 0xe0, 0x08, \ + 0x48, 0xba, 0x85, 0x31, \ + 0x94, 0x43, 0xe6, 0x92, \ + 0x88, 0x23, 0xe7, 0x4f, \ + 0xf0, 0x0a, 0x44, 0x2a, \ + 0x44, 0x2a, 0x97, 0x34, \ + 0x92, 0x23, 0xe6, 0x8e, \ + 0x84, 0x1d, 0xed, 0x2f, \ + 0xab, 0x94, 0x82, 0xa5, \ + 0xa5, 0xa4, 0xa3, 0x9b, \ + 0xe0, 0x8c, 0x54, 0x05, \ + 0x82, 0x21, 0x9d, 0x24, \ + 0x8e, 0x45, 0x9d, 0x2e, \ + 0xad, 0x64, 0xa3, 0xb4, \ + 0x8a, 0x1b, 0x89, 0xb5, \ + 0xa3, 0xb4, 0xa5, 0xa4, \ + 0xab, 0x94, 0xad, 0x64, \ + 0x82, 0x9b, 0x89, 0x55, \ + 0x89, 0x25, 0x8a, 0x1b, \ + 0x02, 0x15, 0xc4, 0x67, \ + 0xfe, 0x34, 0xfc, 0x18, \ + 0xfa, 0x04, 0x46, 0x32, \ + 0xe0, 0x84, 0x02, 0x34, \ + 0xf1, 0x84, 0x42, 0xb2, \ + 0x8a, 0x1d, 0xc2, 0x17, \ + 0xab, 0x9e, 0x86, 0xa5, \ + 0xe0, 0x14, 0x14, 0x45, \ + 0xe4, 0x10, 0x12, 0x20, \ + 0xa1, 0xa4, 0xc7, 0x97, \ + 0xe6, 0xaa, 0x52, 0x9a, \ + 0xfc, 0x18, 0x08, 0x45, \ + 0x96, 0x23, 0x90, 0x91, \ + 0x92, 0x23, 0xc9, 0x97, \ + 0xc4, 0x37, 0x88, 0x33, \ + 0x86, 0x23, 0x84, 0x41, \ + 0xe1, 0x88, 0x16, 0x44, \ + 0x8f, 0x61, 0x94, 0x4b, \ + 0xe0, 0x04, 0x12, 0x06, \ + 0x8a, 0x1b, 0xf1, 0x95, \ + 0x8a, 0x1b, 0xfb, 0x15, \ + 0xa3, 0xb4, 0xad, 0x64, \ + 0x14, 0x25, 0x83, 0x55, \ + 0x46, 0x02, 0xe0, 0xf0, \ + 0xbe, 0x3d, 0xf1, 0x88, \ + 0xa3, 0xb4, 0xa9, 0x24, \ + 0x80, 0x45, 0xad, 0x64, \ + 0x95, 0x44, 0x86, 0x2b, \ + 0x80, 0x45, 0x89, 0x3d, \ + 0x83, 0x31, 0xa9, 0x24, \ + 0xe0, 0x80, 0x54, 0x35, \ + 0xa9, 0x2e, 0x85, 0x91, \ + 0xa9, 0x24, 0x8b, 0x0e, \ + 0x82, 0xc1, 0x84, 0x0d, \ + 0xec, 0x04, 0x00, 0x00, \ + 0xec, 0x10, 0x0e, 0x70, \ + 0xec, 0x10, 0x10, 0x80, \ + 0x82, 0x5b, 0x8c, 0x2b, \ + 0x28, 0x48, 0x92, 0x1b, \ + 0x8b, 0x24, 0xdf, 0xfc, \ + 0x9d, 0x14, 0xa1, 0x2d, \ + 0x14, 0x05, 0x82, 0x15, \ + 0x82, 0x11, 0xe0, 0xf0, \ + 0x80, 0x65, 0x81, 0x1d, \ + 0xe0, 0x8c, 0x54, 0x25, \ + 0x54, 0x05, 0x81, 0x01, \ + 0x1e, 0xf0, 0xe0, 0x98, \ + 0x0c, 0x0c, 0xe5, 0x80, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xec, 0x80, 0x1e, 0xf0, \ + 0xe0, 0xbc, 0x00, 0x25, \ + 0xe0, 0xc0, 0x00, 0x15, \ + 0x83, 0x2d, 0x8f, 0x1e, \ + 0x40, 0x15, 0xff, 0x11, \ + 0x17, 0x78, 0xe0, 0x90, \ + 0x00, 0x15, 0xc0, 0x00, \ + 0x00, 0x35, 0xe0, 0x18, \ + 0x89, 0x3e, 0xe0, 0x30, \ + 0xe0, 0x24, 0x00, 0x35, \ + 0x06, 0x24, 0xfe, 0x3e, \ + 0x00, 0x45, 0xe0, 0x88, \ + 0x8b, 0x4e, 0xe0, 0x64, \ + 0x18, 0x10, 0x86, 0x13, \ + 0x81, 0x11, 0xec, 0x40, \ + 0x81, 0x11, 0xfc, 0x1e, \ + 0x81, 0xb1, 0xbe, 0x1e, \ + 0xe7, 0x1c, 0x17, 0x0a, \ + 0xc0, 0x00, 0x15, 0xe8, \ + 0xe0, 0x10, 0x18, 0x35, \ + 0xe0, 0x20, 0x00, 0x75, \ + 0xa1, 0x1b, 0x8b, 0x14, \ + 0x0e, 0x85, 0xc4, 0x37, \ + 0x96, 0xa5, 0xfc, 0x0c, \ + 0x96, 0x65, 0xf0, 0xbd, \ + 0xb0, 0x14, 0x8d, 0x2e, \ + 0xe0, 0x20, 0x18, 0x45, \ + 0xe0, 0xb4, 0x00, 0x25, \ + 0xc4, 0x47, 0xc4, 0x17, \ + 0xfc, 0x04, 0x0e, 0x55, \ + 0xfc, 0x10, 0x0e, 0x95, \ + 0xe4, 0x10, 0x08, 0x20, \ + 0x4a, 0x42, 0x87, 0x47, \ + 0x90, 0x93, 0xfa, 0x14, \ + 0xe0, 0x84, 0x08, 0x54, \ + 0xe0, 0x10, 0x00, 0x85, \ + 0xf1, 0x90, 0x54, 0x92, \ + 0x10, 0x85, 0xa3, 0xad, \ + 0x0d, 0x68, 0xfc, 0x0c, \ + 0x00, 0x35, 0xc0, 0x00, \ + 0x06, 0x15, 0xe0, 0x14, \ + 0x06, 0x80, 0xfc, 0x04, \ + 0xc7, 0x37, 0xe4, 0x10, \ + 0x46, 0x3a, 0xfe, 0x64, \ + 0xbc, 0x8e, 0xe6, 0x9a, \ + 0xf0, 0xa4, 0x48, 0x82, \ + 0xe4, 0x08, 0x10, 0x10, \ + 0xc5, 0x87, 0x90, 0x31, \ + 0x50, 0x8a, 0xc9, 0x37, \ + 0x86, 0x43, 0xe6, 0x9a, \ + 0x88, 0x81, 0x82, 0x53, \ + 0xc7, 0x87, 0xc4, 0x57, \ + 0x90, 0x43, 0x8a, 0x43, \ + 0xf0, 0x0a, 0x42, 0x4a, \ + 0x42, 0x1a, 0x89, 0x34, \ + 0x02, 0x10, 0xe6, 0x8e, \ + 0xc2, 0xa7, 0xe5, 0x00, \ + 0x82, 0xad, 0xcf, 0x17, \ + 0xfc, 0x14, 0xaa, 0xc5, \ + 0x80, 0xa5, 0x82, 0xad, \ + 0x40, 0x15, 0x8d, 0x14, \ + 0xfc, 0xae, 0xe0, 0x90, \ + 0xa1, 0x1b, 0xbe, 0x14, \ + 0xa8, 0x95, 0xbc, 0x84, \ + 0xe0, 0xf4, 0x00, 0x15, \ + 0x42, 0x32, 0x8d, 0x34, \ + 0x8f, 0x34, 0xf1, 0x84, \ + 0xfa, 0x0c, 0x46, 0x12, \ + 0x83, 0x11, 0x8f, 0x3e, \ + 0x93, 0x85, 0xbe, 0x1e, \ + 0x00, 0xa5, 0xb0, 0x44, \ + 0xc4, 0x47, 0xe0, 0xb4, \ + 0xfc, 0x10, 0x0e, 0x15, \ + 0xe4, 0x10, 0x0a, 0xa0, \ + 0x42, 0x52, 0x87, 0x57, \ + 0x12, 0x84, 0xfa, 0x04, \ + 0x0c, 0x14, 0xe0, 0x84, \ + 0x00, 0x55, 0xe0, 0x84, \ + 0x8d, 0x9e, 0xe0, 0x14, \ + 0xf1, 0x98, 0x4c, 0x92, \ + 0x0a, 0x95, 0xa3, 0x6d, \ + 0x8e, 0x55, 0xfc, 0x0c, \ + 0xfc, 0x10, 0x0a, 0x35, \ + 0xf0, 0xa0, 0x48, 0x92, \ + 0xbc, 0x9e, 0x86, 0x43, \ + 0xe4, 0x08, 0x0a, 0x90, \ + 0xc5, 0x57, 0x84, 0x31, \ + 0xfe, 0x94, 0xc5, 0x37, \ + 0xe6, 0xa6, 0x4a, 0x5a, \ + 0xe6, 0xa6, 0x46, 0x3a, \ + 0x88, 0x51, 0x82, 0x43, \ + 0xc7, 0x57, 0x88, 0x31, \ + 0xc4, 0x47, 0xc7, 0x37, \ + 0x88, 0x33, 0x8a, 0x33, \ + 0xf0, 0x0a, 0x42, 0x3a, \ + 0x42, 0x1a, 0x89, 0x34, \ + 0x02, 0x10, 0xe6, 0x8e, \ + 0xc2, 0x67, 0xe5, 0x00, \ + 0x82, 0x6d, 0xcf, 0x17, \ + 0xfc, 0x14, 0x84, 0xc5, \ + 0x80, 0x85, 0x82, 0x6d, \ + 0xe0, 0x90, 0x40, 0x25, \ + 0xbe, 0x14, 0xfc, 0x6e, \ + 0xbc, 0x94, 0xa1, 0x1b, \ + 0x00, 0x15, 0x82, 0xb5, \ + 0x42, 0x22, 0xe0, 0xf4, \ + 0x8f, 0x34, 0xf1, 0x84, \ + 0xfa, 0x0c, 0x46, 0x12, \ + 0x83, 0x11, 0x8f, 0x3e, \ + 0x81, 0x25, 0xbe, 0x1e, \ + 0xf2, 0x14, 0xbc, 0x94, \ + 0xb0, 0x44, 0x82, 0x34, \ + 0x06, 0x35, 0xc4, 0x17, \ + 0xa0, 0xa1, 0xfc, 0x04, \ + 0x8b, 0xa7, 0xc4, 0x47, \ + 0xfc, 0x10, 0x0e, 0x45, \ + 0xfa, 0x0c, 0x46, 0xa2, \ + 0xe0, 0x88, 0x0a, 0x34, \ + 0xa2, 0x85, 0x89, 0x5d, \ + 0xe0, 0x8c, 0x08, 0x34, \ + 0xa8, 0x63, 0x8d, 0x64, \ + 0xa0, 0xd5, 0xa3, 0x6d, \ + 0x0a, 0x90, 0xc0, 0x44, \ + 0x08, 0x15, 0xe4, 0x08, \ + 0xc5, 0x57, 0xfc, 0x04, \ + 0x48, 0x8a, 0x9b, 0x41, \ + 0xfe, 0x74, 0xe6, 0x92, \ + 0xe6, 0x9e, 0x4a, 0x5a, \ + 0x46, 0x3a, 0xb9, 0x81, \ + 0xc4, 0x97, 0xe6, 0xa2, \ + 0x88, 0x51, 0x88, 0x93, \ + 0x48, 0x1a, 0xc7, 0x57, \ + 0xcc, 0x17, 0xe6, 0x9e, \ + 0x86, 0x93, 0x8a, 0x93, \ + 0x82, 0x93, 0x82, 0x41, \ + 0x88, 0x93, 0xc3, 0x47, \ + 0xf0, 0x0a, 0x42, 0x9a, \ + 0x42, 0x1a, 0x89, 0x34, \ + 0x02, 0x10, 0xe6, 0x8e, \ + 0xc2, 0x67, 0xe5, 0x00, \ + 0x82, 0x6d, 0xcf, 0x17, \ + 0xfc, 0x14, 0x96, 0xe5, \ + 0x92, 0xa5, 0x82, 0x6d, \ + 0xe0, 0x90, 0x40, 0x25, \ + 0x89, 0x95, 0xfc, 0x6e, \ + 0xf2, 0x54, 0xbc, 0x84, \ + 0xb0, 0x34, 0x82, 0x14, \ + 0x02, 0x15, 0xc4, 0x57, \ + 0x0c, 0x20, 0xfc, 0x14, \ + 0xc4, 0x37, 0xe4, 0x40, \ + 0x0e, 0x35, 0x8b, 0x67, \ + 0x42, 0x62, 0xfc, 0x0c, \ + 0x08, 0x14, 0xfa, 0x04, \ + 0x87, 0x4d, 0xe0, 0x88, \ + 0x06, 0x14, 0x90, 0x85, \ + 0x46, 0x92, 0xe0, 0x8c, \ + 0xa3, 0x3d, 0xf1, 0x8c, \ + 0xc0, 0x44, 0x8e, 0xd5, \ + 0x4c, 0x9a, 0xb3, 0x21, \ + 0x08, 0x45, 0xe6, 0x8a, \ + 0x0a, 0x80, 0xfc, 0x14, \ + 0x84, 0x61, 0xe4, 0x10, \ + 0xc5, 0x67, 0xc7, 0x57, \ + 0x4a, 0x5a, 0xfe, 0x24, \ + 0x90, 0x63, 0xe6, 0x8a, \ + 0x42, 0x1a, 0xad, 0x71, \ + 0x90, 0x51, 0xe6, 0x9e, \ + 0x4e, 0x4a, 0xc9, 0x57, \ + 0xcc, 0x47, 0xe6, 0x8a, \ + 0x82, 0x63, 0x8a, 0x63, \ + 0x88, 0x63, 0x82, 0x71, \ + 0x8e, 0x63, 0xc3, 0x77, \ + 0xf0, 0x0a, 0x42, 0x6a, \ + 0x42, 0x1a, 0x89, 0x44, \ + 0x02, 0x10, 0xe6, 0x92, \ + 0xc2, 0x37, 0xe5, 0x00, \ + 0x82, 0x3d, 0xcf, 0x17, \ + 0xfc, 0x14, 0x84, 0xc5, \ + 0x8d, 0x24, 0x82, 0x3d, \ + 0x40, 0x25, 0x80, 0x85, \ + 0xfc, 0x3e, 0xe0, 0x90, \ + 0xa1, 0x1b, 0xbe, 0x14, \ + 0x00, 0x15, 0x82, 0xb5, \ + 0x42, 0x22, 0xe0, 0xf4, \ + 0x8f, 0x34, 0xf1, 0x84, \ + 0xfa, 0x0c, 0x46, 0x12, \ + 0x83, 0x11, 0x8f, 0x3e, \ + 0x81, 0x25, 0xbe, 0x1e, \ + 0x8f, 0x14, 0x8d, 0x24, \ + 0x04, 0x20, 0x82, 0x2d, \ + 0x82, 0xb1, 0xec, 0x04, \ + 0xec, 0x10, 0x18, 0xc0, \ + 0xdf, 0xfc, 0x2a, 0x28, \ + 0x40, 0x15, 0x81, 0x11, \ + 0xc0, 0xf1, 0xe0, 0x98, \ + 0xf0, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xe0, 0xa0, 0x00, 0x15, \ + 0x84, 0x55, 0xa3, 0x1d, \ + 0xe0, 0xac, 0x00, 0x25, \ + 0x81, 0x41, 0x88, 0x34, \ + 0xc9, 0xc4, 0x81, 0x51, \ + 0x80, 0x55, 0x86, 0xcd, \ + 0x82, 0x41, 0x88, 0x5b, \ + 0x46, 0x32, 0x89, 0x1b, \ + 0xfe, 0x15, 0xfb, 0x30, \ + 0xe0, 0xb8, 0x40, 0x55, \ + 0x81, 0x51, 0xdd, 0x93, \ + 0xe0, 0xb8, 0x40, 0x55, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x02, 0x25, 0xa3, 0x2b, \ + 0x80, 0x3b, 0xe0, 0x18, \ + 0x8e, 0x95, 0x80, 0x5b, \ + 0xe0, 0x4c, 0x02, 0x45, \ + 0x80, 0x33, 0xc8, 0x3b, \ + 0x94, 0xd5, 0xa1, 0x3d, \ + 0x80, 0x3b, 0xa3, 0x4d, \ + 0x8c, 0x55, 0x80, 0x5b, \ + 0xe0, 0x88, 0x0e, 0x04, \ + 0xfc, 0x1c, 0x04, 0x35, \ + 0xc4, 0x37, 0x86, 0xc4, \ + 0xfc, 0x0c, 0x18, 0x35, \ + 0x84, 0x73, 0xbe, 0x3e, \ + 0x80, 0x5b, 0x81, 0x81, \ + 0x9c, 0x94, 0x80, 0x3b, \ + 0xf8, 0x04, 0x0e, 0xa5, \ + 0xc4, 0xa7, 0xc4, 0x97, \ + 0xfc, 0x24, 0x18, 0x95, \ + 0xfc, 0x28, 0x18, 0xa5, \ + 0x12, 0x50, 0x93, 0xad, \ + 0x82, 0xc5, 0xec, 0x04, \ + 0x8d, 0xad, 0xbe, 0x64, \ + 0x02, 0x65, 0x82, 0x65, \ + 0x16, 0x64, 0xe0, 0xb4, \ + 0xf7, 0xb7, 0xe1, 0x7c, \ + 0x8b, 0x67, 0x96, 0x63, \ + 0x92, 0xab, 0x8c, 0xad, \ + 0xa1, 0x8b, 0x80, 0x95, \ + 0x82, 0x15, 0x86, 0xab, \ + 0x89, 0x8d, 0x82, 0x81, \ + 0xec, 0x10, 0x0e, 0x70, \ + 0x94, 0x3b, 0x92, 0x5b, \ + 0x08, 0x04, 0xf6, 0xb5, \ + 0x04, 0x45, 0xe0, 0x88, \ + 0xc4, 0x57, 0xfc, 0x10, \ + 0x04, 0x25, 0x86, 0xc4, \ + 0x02, 0x55, 0xfc, 0x14, \ + 0x02, 0x15, 0xe0, 0x20, \ + 0xc4, 0x47, 0xe0, 0xc8, \ + 0xfc, 0x10, 0x18, 0x65, \ + 0x18, 0x25, 0xc4, 0x27, \ + 0x0a, 0x45, 0xfc, 0x08, \ + 0x4a, 0x6a, 0xfc, 0x10, \ + 0x42, 0x2a, 0xe6, 0x86, \ + 0x84, 0x41, 0xe6, 0x86, \ + 0x8a, 0x23, 0x85, 0x47, \ + 0x89, 0x2d, 0x82, 0x43, \ + 0x80, 0x3b, 0x80, 0x45, \ + 0x90, 0xf1, 0x86, 0x0b, \ + 0xf0, 0x7c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xec, 0x80, 0x1e, 0xf0, \ + 0xe0, 0x84, 0x00, 0x15, \ + 0x0d, 0xf8, 0xa1, 0x1d, \ + 0x00, 0x55, 0xc0, 0x00, \ + 0x04, 0x14, 0xe0, 0x1c, \ + 0x8a, 0x23, 0xe0, 0x84, \ + 0xe0, 0x30, 0x04, 0x93, \ + 0x0d, 0x58, 0xa1, 0x9d, \ + 0x00, 0x35, 0xc0, 0x00, \ + 0x04, 0x73, 0xe0, 0xa0, \ + 0x87, 0x41, 0xe0, 0x60, \ + 0xe6, 0x92, 0x56, 0x3a, \ + 0x0c, 0xb8, 0x8e, 0xbd, \ + 0xa1, 0x7d, 0x80, 0x00, \ + 0x92, 0xbd, 0xb2, 0x15, \ + 0xfe, 0x5e, 0xb0, 0xc5, \ + 0xe0, 0xd0, 0x00, 0x85, \ + 0xe0, 0xb4, 0x00, 0x45, \ + 0x86, 0x91, 0x8f, 0x9e, \ + 0xe1, 0xa0, 0x06, 0x84, \ + 0x0a, 0x84, 0x8e, 0x9d, \ + 0x8d, 0x5e, 0xfa, 0x80, \ + 0x14, 0x34, 0x89, 0x3e, \ + 0x00, 0x35, 0xfa, 0x80, \ + 0x8b, 0x3e, 0xe0, 0x18, \ + 0x84, 0x95, 0x81, 0x31, \ + 0xe0, 0x20, 0x00, 0xc5, \ + 0x84, 0x61, 0x8f, 0x64, \ + 0x8b, 0x34, 0xc4, 0x97, \ + 0x81, 0x31, 0x86, 0x93, \ + 0xc4, 0x57, 0xe5, 0x54, \ + 0xfc, 0x14, 0x18, 0x55, \ + 0x84, 0x51, 0x82, 0x61, \ + 0x85, 0x57, 0x8f, 0x6d, \ + 0xfa, 0x0c, 0x46, 0x52, \ + 0x8f, 0x64, 0xfc, 0xd5, \ + 0x8c, 0xc3, 0x8d, 0xc4, \ + 0x4e, 0xc2, 0xa0, 0x1d, \ + 0x8e, 0x9b, 0xf0, 0xa8, \ + 0x04, 0x23, 0x80, 0xe5, \ + 0x04, 0x20, 0xe0, 0x2c, \ + 0x52, 0x22, 0xec, 0x10, \ + 0xa3, 0xcd, 0xfb, 0x1c, \ + 0x92, 0xcd, 0xa2, 0xd5, \ + 0x04, 0xa0, 0xa2, 0x95, \ + 0xfa, 0x2d, 0xec, 0x04, \ + 0x97, 0x7d, 0xa2, 0x15, \ + 0xa0, 0x41, 0xa0, 0xc5, \ + 0xe1, 0x7c, 0x04, 0x44, \ + 0x84, 0x43, 0xf7, 0x27, \ + 0xe1, 0x14, 0x0a, 0x44, \ + 0x9e, 0xa5, 0x87, 0x5d, \ + 0x04, 0x0a, 0x89, 0x24, \ + 0x9e, 0x25, 0xf0, 0x00, \ + 0x44, 0x52, 0xfc, 0x1e, \ + 0xc4, 0x27, 0xf1, 0x8c, \ + 0xe1, 0x84, 0x08, 0xa4, \ + 0x48, 0x2a, 0xa8, 0x23, \ + 0x8d, 0xa4, 0xe0, 0xaa, \ + 0x04, 0xa0, 0x8c, 0xa3, \ + 0xe1, 0x87, 0xe4, 0x04, \ + 0xc4, 0x27, 0xbe, 0x8e, \ + 0x18, 0x34, 0xa8, 0x6b, \ + 0x8b, 0x84, 0xe0, 0x88, \ + 0xb8, 0x63, 0x84, 0x83, \ + 0x81, 0xc1, 0x81, 0xb1, \ + 0x55, 0x1a, 0x82, 0xa1, \ + 0x8a, 0x2b, 0xe0, 0x24, \ + 0x20, 0x01, 0x00, 0x88, \ + 0xa0, 0x24, 0xc0, 0x00, \ + 0xc4, 0x27, 0x82, 0x14, \ + 0xfc, 0x08, 0x02, 0x25, \ + 0x8c, 0x23, 0xc4, 0x27, \ + 0xe0, 0x00, 0x02, 0x22, \ + 0xe8, 0x00, 0x04, 0x22, \ + 0xe0, 0x1c, 0x54, 0x9a, \ + 0xa4, 0xb3, 0x82, 0xc3, \ + 0xa8, 0x63, 0x88, 0x81, \ + 0x3f, 0xfd, 0x3e, 0x88, \ + 0x8f, 0x14, 0xc3, 0xff, \ + 0x84, 0xc1, 0x90, 0x85, \ + 0xe1, 0x88, 0x04, 0xc4, \ + 0x08, 0xb4, 0x84, 0xb1, \ + 0x8d, 0xc4, 0xe1, 0x08, \ + 0xc0, 0x64, 0x82, 0xc3, \ + 0xe4, 0x04, 0x0a, 0xc0, \ + 0xe0, 0x24, 0x00, 0x85, \ + 0x8b, 0x94, 0xc4, 0x57, \ + 0x81, 0x51, 0x8a, 0x93, \ + 0xc4, 0x17, 0xe5, 0x14, \ + 0xfc, 0x04, 0x0c, 0x15, \ + 0xe6, 0x86, 0x54, 0x8a, \ + 0x14, 0xa0, 0x82, 0xc1, \ + 0x8f, 0xcd, 0xe5, 0x00, \ + 0xcf, 0xa7, 0x8a, 0x13, \ + 0xf0, 0xa8, 0x4a, 0x12, \ + 0x89, 0x14, 0xfc, 0x55, \ + 0xf4, 0x20, 0x02, 0x8a, \ + 0x86, 0xf5, 0xfe, 0x64, \ + 0xf1, 0x90, 0x58, 0x22, \ + 0x86, 0x65, 0xa0, 0xcd, \ + 0x00, 0x09, 0x84, 0x43, \ + 0x8a, 0x43, 0xe3, 0x34, \ + 0xf0, 0x0a, 0x42, 0x4a, \ + 0xe6, 0x8e, 0x40, 0x0a, \ + 0x42, 0x1a, 0xbe, 0x24, \ + 0x84, 0x01, 0xe6, 0x8a, \ + 0x02, 0x10, 0x85, 0x07, \ + 0x98, 0x03, 0xe6, 0x00, \ + 0x82, 0x0d, 0x91, 0x17, \ + 0x80, 0xe5, 0xfc, 0x04, \ + 0x00, 0x19, 0x8c, 0x03, \ + 0x82, 0x11, 0xe1, 0xe0, \ + 0xe1, 0xe0, 0x40, 0x11, \ + 0x0c, 0x0c, 0xc0, 0xf1, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfc, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0xe0, 0xa8, 0x00, 0xa5, \ + 0xe0, 0xac, 0x00, 0x75, \ + 0xe0, 0xb0, 0x00, 0x25, \ + 0x00, 0xb5, 0xa3, 0xad, \ + 0x81, 0x41, 0xe0, 0xa0, \ + 0x88, 0x4e, 0x9c, 0x4e, \ + 0x94, 0x1b, 0x84, 0x75, \ + 0x9c, 0x54, 0x82, 0x34, \ + 0xfc, 0x10, 0x06, 0x35, \ + 0x9c, 0x3e, 0x8a, 0x33, \ + 0x88, 0x54, 0xc0, 0x34, \ + 0xfc, 0x10, 0x06, 0x35, \ + 0xec, 0x04, 0x02, 0x10, \ + 0x8a, 0x33, 0xa1, 0x1b, \ + 0x88, 0x41, 0x88, 0x3e, \ + 0x48, 0xb2, 0xfc, 0x15, \ + 0xa3, 0x4d, 0xf1, 0xa8, \ + 0x18, 0x70, 0x82, 0x54, \ + 0x89, 0xae, 0xe4, 0x10, \ + 0x52, 0xb2, 0x8a, 0x15, \ + 0x06, 0x94, 0xf1, 0xa8, \ + 0x8e, 0x6b, 0xe0, 0x88, \ + 0xfc, 0x3e, 0x96, 0x1b, \ + 0xfe, 0x0c, 0x0c, 0xb5, \ + 0x82, 0x11, 0xfe, 0x1e, \ + 0xbe, 0x1e, 0xb4, 0x13, \ + 0xe0, 0x88, 0x02, 0xa4, \ + 0x86, 0x8b, 0x8a, 0x3b, \ + 0x06, 0x35, 0xe1, 0xa4, \ + 0x12, 0x90, 0xfc, 0x04, \ + 0xb4, 0xb3, 0xec, 0x04, \ + 0xe0, 0x00, 0x12, 0x1a, \ + 0xf1, 0xbe, 0x86, 0xb3, \ + 0x3f, 0x58, 0x90, 0x3b, \ + 0xc3, 0xff, 0x3f, 0xfd, \ + 0xc4, 0xc7, 0xbe, 0xc4, \ + 0x82, 0x53, 0xfc, 0x14, \ + 0x89, 0xa4, 0x8e, 0xc3, \ + 0x81, 0x25, 0xfe, 0xb4, \ + 0x02, 0xb0, 0x8e, 0x6b, \ + 0x48, 0x9a, 0xec, 0x04, \ + 0x01, 0x28, 0xe0, 0x04, \ + 0x82, 0x74, 0xa0, 0x00, \ + 0xec, 0x04, 0x10, 0xa0, \ + 0xd5, 0x34, 0xd9, 0x14, \ + 0x10, 0x80, 0xdd, 0x94, \ + 0xa6, 0x13, 0xec, 0x04, \ + 0xe0, 0x00, 0x10, 0x2a, \ + 0xf1, 0x1e, 0x92, 0x13, \ + 0x5f, 0xfd, 0x3f, 0x68, \ + 0xc0, 0x54, 0xc3, 0xff, \ + 0xe4, 0x10, 0x18, 0x20, \ + 0x0e, 0x44, 0x88, 0x35, \ + 0x84, 0x6b, 0xe0, 0x88, \ + 0xfe, 0x1c, 0x0c, 0x85, \ + 0xe4, 0x04, 0x06, 0xb0, \ + 0x02, 0xa4, 0xb4, 0x33, \ + 0x8a, 0xbb, 0xe0, 0x88, \ + 0xe5, 0xa4, 0x96, 0x9b, \ + 0xfc, 0x04, 0x16, 0xb5, \ + 0xec, 0x04, 0x08, 0x40, \ + 0xa1, 0x4b, 0xb4, 0x83, \ + 0xf1, 0x8e, 0x96, 0x83, \ + 0xfc, 0xd5, 0x92, 0xbb, \ + 0x8e, 0x53, 0xc4, 0x37, \ + 0xf0, 0x8c, 0x58, 0x22, \ + 0x00, 0x78, 0x89, 0xa4, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0x84, 0x6b, 0x83, 0x15, \ + 0xa0, 0x00, 0x00, 0xf8, \ + 0x02, 0xa0, 0xc0, 0x04, \ + 0xd9, 0x24, 0xec, 0x04, \ + 0xc1, 0x44, 0xd5, 0x34, \ + 0xec, 0x04, 0x02, 0x10, \ + 0xa1, 0x1b, 0xa6, 0x23, \ + 0xf1, 0x2e, 0x88, 0x23, \ + 0xa8, 0xf1, 0xfc, 0xf5, \ + 0xf4, 0x7c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0xe0, 0xa0, 0x02, 0x35, \ + 0xe0, 0x50, 0x02, 0x25, \ + 0x46, 0x3a, 0x87, 0x41, \ + 0x48, 0x22, 0xe6, 0x92, \ + 0x06, 0x30, 0xf0, 0x80, \ + 0x87, 0x4d, 0xec, 0x04, \ + 0xa3, 0x2d, 0x8a, 0xc5, \ + 0x02, 0x35, 0x8a, 0x95, \ + 0x08, 0x04, 0xe0, 0x18, \ + 0x06, 0x55, 0xe0, 0x88, \ + 0x86, 0x44, 0xfc, 0x10, \ + 0x08, 0x55, 0xc4, 0x57, \ + 0x81, 0xc1, 0xfc, 0x14, \ + 0xe4, 0x04, 0x0c, 0x00, \ + 0xe0, 0x88, 0x0e, 0x04, \ + 0xe0, 0x88, 0x10, 0x64, \ + 0xfc, 0x1c, 0x06, 0x75, \ + 0xfc, 0x20, 0x06, 0x85, \ + 0xc4, 0x87, 0xc4, 0x77, \ + 0xfc, 0x1c, 0x08, 0x95, \ + 0xfc, 0x20, 0x08, 0x75, \ + 0x82, 0x05, 0x93, 0x7d, \ + 0x80, 0xc5, 0x8b, 0x7d, \ + 0xe0, 0xb4, 0x02, 0x85, \ + 0x90, 0x7d, 0xca, 0x77, \ + 0xa1, 0xcb, 0x80, 0x95, \ + 0x80, 0xb5, 0x80, 0x6b, \ + 0x85, 0xcd, 0x82, 0xc1, \ + 0xf8, 0x35, 0x8c, 0x0b, \ + 0xf0, 0x1c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xe0, 0xa0, 0x00, 0x25, \ + 0x84, 0x35, 0xa3, 0x2d, \ + 0xe0, 0x18, 0x00, 0x15, \ + 0xe0, 0x88, 0x06, 0x24, \ + 0xf0, 0x8c, 0x44, 0x12, \ + 0x81, 0x41, 0x84, 0x33, \ + 0xc9, 0x4e, 0xc5, 0x4e, \ + 0x00, 0x55, 0xcd, 0x4e, \ + 0x82, 0x41, 0xe0, 0xa0, \ + 0xfe, 0x35, 0x8b, 0x4d, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xec, 0xa0, 0x1e, 0xf0, \ + 0x81, 0x31, 0xad, 0x0d, \ + 0x04, 0x55, 0xaa, 0xf5, \ + 0x04, 0x45, 0xe1, 0x00, \ + 0x04, 0xa5, 0xe1, 0x04, \ + 0x93, 0x5e, 0xe1, 0x08, \ + 0xf1, 0x94, 0x56, 0x12, \ + 0x4a, 0x12, 0x91, 0x4e, \ + 0x04, 0x95, 0xf1, 0x90, \ + 0x40, 0x12, 0xe1, 0x0c, \ + 0x04, 0x85, 0xf1, 0xa8, \ + 0x04, 0x45, 0xe1, 0x10, \ + 0x58, 0x52, 0xe1, 0x14, \ + 0x4e, 0x12, 0xf9, 0xac, \ + 0x4c, 0xc2, 0xf1, 0xa4, \ + 0x58, 0x12, 0xf9, 0x80, \ + 0x8d, 0x7e, 0xf1, 0xa0, \ + 0x8f, 0x1e, 0xac, 0x7f, \ + 0xf1, 0x90, 0x48, 0x12, \ + 0xb8, 0x7f, 0x8b, 0xce, \ + 0xa1, 0x7d, 0xa8, 0x7f, \ + 0xfc, 0x4e, 0xa2, 0x35, \ + 0xbe, 0x9e, 0x89, 0xae, \ + 0x04, 0x95, 0xfe, 0x8e, \ + 0x02, 0xb4, 0xe0, 0x18, \ + 0xc4, 0x57, 0xe0, 0x88, \ + 0xfc, 0x04, 0x12, 0x15, \ + 0xfc, 0x14, 0x12, 0x75, \ + 0xe0, 0x20, 0x04, 0x55, \ + 0xe1, 0x18, 0x04, 0x85, \ + 0xc4, 0x77, 0xc4, 0x17, \ + 0xfc, 0x04, 0x0a, 0x45, \ + 0xfc, 0x1c, 0x0a, 0x15, \ + 0xe1, 0x1c, 0x04, 0x75, \ + 0xe0, 0xb4, 0x04, 0xa5, \ + 0xe6, 0xa2, 0x50, 0x4a, \ + 0xf1, 0x90, 0x48, 0x12, \ + 0xe6, 0x9e, 0x4e, 0x4a, \ + 0x90, 0x7d, 0xb4, 0x83, \ + 0x0c, 0x04, 0x98, 0xa5, \ + 0x12, 0x35, 0xe0, 0x88, \ + 0xc4, 0x37, 0xfc, 0x18, \ + 0xfc, 0x0c, 0x0a, 0x35, \ + 0xe1, 0x20, 0x04, 0x45, \ + 0xf1, 0x84, 0x42, 0x32, \ + 0xe6, 0x92, 0x42, 0x1a, \ + 0x92, 0x4b, 0x8e, 0x1d, \ + 0x8c, 0x25, 0x93, 0x04, \ + 0xc4, 0xb7, 0x8d, 0xb4, \ + 0xfc, 0x2c, 0x08, 0x05, \ + 0x0a, 0x65, 0xc4, 0x07, \ + 0x04, 0x05, 0xfc, 0x00, \ + 0x46, 0x62, 0xe1, 0x24, \ + 0x46, 0x3a, 0xf1, 0x8c, \ + 0x82, 0x3d, 0xe6, 0x82, \ + 0x88, 0x25, 0x91, 0x04, \ + 0xc4, 0xa7, 0x8b, 0xa4, \ + 0xfc, 0x28, 0x08, 0x05, \ + 0x0a, 0x95, 0xc4, 0x07, \ + 0x04, 0x05, 0xfc, 0x00, \ + 0x42, 0x92, 0xe1, 0x28, \ + 0x42, 0x1a, 0xf1, 0x98, \ + 0x86, 0x1d, 0xe6, 0x82, \ + 0x84, 0x25, 0x89, 0x04, \ + 0xc4, 0x07, 0xfc, 0x04, \ + 0xfc, 0x00, 0x08, 0x05, \ + 0x0a, 0x05, 0xc4, 0x07, \ + 0x04, 0x35, 0xfc, 0x00, \ + 0xb2, 0x03, 0xe1, 0x2c, \ + 0xe6, 0x8e, 0x40, 0x0a, \ + 0xfe, 0x04, 0x82, 0x0d, \ + 0xbe, 0x04, 0x80, 0x55, \ + 0x88, 0x35, 0xa9, 0x0d, \ + 0xe0, 0x74, 0x04, 0x15, \ + 0xe0, 0x94, 0x42, 0x0a, \ + 0x8f, 0xc4, 0x86, 0x85, \ + 0xaf, 0xcd, 0xa0, 0xc3, \ + 0xc4, 0xc7, 0x86, 0x15, \ + 0x18, 0x15, 0x88, 0xc3, \ + 0x18, 0x25, 0xf8, 0x18, \ + 0xf0, 0x34, 0xf8, 0x08, \ + 0x0a, 0x15, 0xc4, 0x17, \ + 0xc4, 0x27, 0xfc, 0x04, \ + 0x0a, 0x25, 0xc4, 0x37, \ + 0x0a, 0x35, 0xfc, 0x08, \ + 0xa4, 0x13, 0xfc, 0x0c, \ + 0x84, 0x1d, 0xa6, 0x23, \ + 0x00, 0x00, 0x80, 0xd5, \ + 0x81, 0x35, 0xec, 0x10, \ + 0x86, 0x0b, 0x80, 0x3b, \ + 0x0c, 0x0c, 0xd0, 0xf1, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xb0, 0x1e, 0xf0, \ + 0x8b, 0x3e, 0x88, 0x9b, \ + 0x89, 0x1e, 0x8d, 0x2e, \ + 0x80, 0x08, 0xfe, 0x0e, \ + 0x04, 0x90, 0x99, 0x11, \ + 0x06, 0x90, 0xe7, 0x00, \ + 0x08, 0x90, 0xe6, 0x40, \ + 0x81, 0x51, 0xe4, 0xc0, \ + 0xf8, 0x30, 0x48, 0x55, \ + 0x48, 0x55, 0xd1, 0x5e, \ + 0x02, 0x10, 0xe0, 0x2c, \ + 0xcd, 0x5c, 0xec, 0x04, \ + 0x46, 0x53, 0xa1, 0x1b, \ + 0xc9, 0x56, 0xe0, 0x2c, \ + 0xe0, 0x2c, 0x44, 0x51, \ + 0x8d, 0x14, 0xfc, 0x55, \ + 0xa1, 0x1b, 0x86, 0x18, \ + 0x02, 0x04, 0xa0, 0x65, \ + 0xfc, 0x1e, 0xe0, 0x88, \ + 0xbe, 0x0e, 0xc2, 0x07, \ + 0x17, 0x06, 0x81, 0x31, \ + 0x4e, 0x06, 0xe0, 0x08, \ + 0x95, 0x9e, 0xe0, 0x04, \ + 0x93, 0x3e, 0x89, 0xa4, \ + 0x50, 0x62, 0x8b, 0x64, \ + 0x10, 0x05, 0xf0, 0x9c, \ + 0xa8, 0x1a, 0xf8, 0x60, \ + 0x74, 0x38, 0x99, 0x21, \ + 0x43, 0xfe, 0xe8, 0x08, \ + 0xcf, 0xff, 0x7f, 0xfd, \ + 0xfb, 0x84, 0x40, 0x02, \ + 0x52, 0x05, 0x8e, 0x93, \ + 0x10, 0x05, 0xf8, 0x60, \ + 0xa8, 0x1a, 0xf8, 0x30, \ + 0x73, 0x58, 0x99, 0x21, \ + 0x52, 0x05, 0xe8, 0x08, \ + 0x8f, 0x7e, 0xf8, 0x30, \ + 0xfc, 0x1c, 0x0c, 0x05, \ + 0x99, 0x21, 0xa8, 0x1a, \ + 0xe8, 0x08, 0x72, 0xc8, \ + 0xfc, 0x2c, 0x0c, 0x3b, \ + 0x45, 0xfe, 0xaa, 0x1a, \ + 0xcf, 0xff, 0x7f, 0xfd, \ + 0xfb, 0x88, 0x40, 0x02, \ + 0x96, 0x63, 0xa4, 0x0e, \ + 0x86, 0x0b, 0x99, 0x21, \ + 0xe8, 0x08, 0x71, 0xe8, \ + 0x95, 0x94, 0x91, 0xbe, \ + 0xa4, 0x0c, 0x96, 0x93, \ + 0xe0, 0x60, 0x0c, 0x03, \ + 0x99, 0x21, 0xaa, 0x1a, \ + 0xe8, 0x08, 0x73, 0x48, \ + 0x0c, 0x03, 0x80, 0x8b, \ + 0xaa, 0x1a, 0xe0, 0x30, \ + 0x72, 0xd8, 0x99, 0x21, \ + 0x80, 0xbb, 0xe8, 0x08, \ + 0xe0, 0x90, 0x0c, 0x03, \ + 0x99, 0x21, 0xaa, 0x1a, \ + 0xe8, 0x08, 0x72, 0x68, \ + 0x42, 0x22, 0xbe, 0x24, \ + 0xb0, 0x23, 0xf1, 0xac, \ + 0x8e, 0x38, 0xfe, 0x34, \ + 0x96, 0x23, 0x80, 0x13, \ + 0x40, 0x2a, 0xfc, 0x44, \ + 0x44, 0x1a, 0xe0, 0x92, \ + 0xa1, 0x3b, 0xe0, 0x92, \ + 0xe0, 0x00, 0x02, 0x02, \ + 0xe0, 0x00, 0x06, 0x22, \ + 0x80, 0x45, 0x96, 0x0b, \ + 0xa1, 0xbd, 0x81, 0x01, \ + 0xeb, 0xfc, 0x04, 0x12, \ + 0xeb, 0xfc, 0x02, 0x32, \ + 0x96, 0x0b, 0x80, 0x55, \ + 0xe0, 0x30, 0x52, 0x03, \ + 0x93, 0x34, 0x95, 0x94, \ + 0xf0, 0x8c, 0x40, 0x92, \ + 0xe3, 0x00, 0x40, 0x21, \ + 0xe3, 0x30, 0x40, 0x11, \ + 0x82, 0x08, 0x8d, 0x04, \ + 0xc1, 0x3d, 0x82, 0x31, \ + 0x84, 0xb1, 0x91, 0xb4, \ + 0x88, 0x71, 0x8f, 0x74, \ + 0x81, 0x01, 0xe2, 0x75, \ + 0x0c, 0x0c, 0xd8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe0, 0x84, 0x00, 0x44, \ + 0xf0, 0x80, 0x58, 0x52, \ + 0xe0, 0x90, 0x18, 0xc3, \ + 0xfc, 0x00, 0x0a, 0x03, \ + 0xff, 0x01, 0xa0, 0xc3, \ + 0x80, 0x75, 0x81, 0xcd, \ + 0x81, 0x25, 0x82, 0xc1, \ + 0xa3, 0x1d, 0xd8, 0xcb, \ + 0x86, 0x15, 0xbc, 0x04, \ + 0xe6, 0x8a, 0x44, 0xca, \ + 0xe5, 0x0e, 0x58, 0x2a, \ + 0xe5, 0x8e, 0x44, 0x2a, \ + 0x18, 0xc0, 0x81, 0x31, \ + 0xc1, 0x00, 0x64, 0x01, \ + 0xf2, 0x8c, 0x44, 0x22, \ + 0xe3, 0xc7, 0xde, 0x27, \ + 0x82, 0xcd, 0xa4, 0xcf, \ + 0x80, 0xf5, 0x93, 0x11, \ + 0x0a, 0x29, 0x88, 0x53, \ + 0xe2, 0x2d, 0xe1, 0xe0, \ + 0x8b, 0x11, 0x80, 0x55, \ + 0x40, 0x11, 0x88, 0x03, \ + 0xdd, 0x93, 0xe3, 0x90, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x18, 0x24, 0xc2, 0x5a, \ + 0x98, 0x33, 0xe0, 0x88, \ + 0x06, 0x35, 0xc6, 0x57, \ + 0x58, 0x12, 0xe0, 0x30, \ + 0xaa, 0xc3, 0xfa, 0x94, \ + 0x80, 0xf5, 0xd9, 0x3d, \ + 0xe0, 0x10, 0x00, 0x0b, \ + 0x82, 0x03, 0xc6, 0x07, \ + 0x80, 0xa5, 0xc0, 0x3d, \ + 0x85, 0x01, 0x84, 0x43, \ + 0xe3, 0x90, 0x48, 0x01, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xf0, 0x1e, 0xf0, \ + 0x95, 0x4e, 0x99, 0x5e, \ + 0x8f, 0x2e, 0x91, 0x3e, \ + 0x80, 0x2b, 0x8d, 0x1e, \ + 0xaf, 0x14, 0xb7, 0x04, \ + 0x40, 0x91, 0x81, 0x91, \ + 0x40, 0x91, 0xe3, 0xbc, \ + 0x40, 0x91, 0xe3, 0xb8, \ + 0x40, 0x91, 0xe3, 0xb4, \ + 0x40, 0x91, 0xe3, 0xb0, \ + 0x40, 0x91, 0xe3, 0xac, \ + 0x40, 0x91, 0xe3, 0xa8, \ + 0x40, 0x91, 0xe3, 0xa4, \ + 0x40, 0x91, 0xe3, 0xa0, \ + 0x40, 0x91, 0xe3, 0x9c, \ + 0x40, 0x91, 0xe3, 0x98, \ + 0x9d, 0x0e, 0xe3, 0x94, \ + 0xe3, 0x90, 0x40, 0x91, \ + 0x86, 0x08, 0x9b, 0x1e, \ + 0x98, 0x85, 0xa1, 0x0b, \ + 0xb3, 0x04, 0xb5, 0x74, \ + 0x8b, 0x1e, 0xb1, 0x14, \ + 0x89, 0x1e, 0xad, 0x14, \ + 0x14, 0x10, 0x9d, 0x14, \ + 0x0c, 0x10, 0xe6, 0x40, \ + 0x93, 0x7e, 0xe5, 0x80, \ + 0x10, 0x00, 0xe0, 0x71, \ + 0x97, 0x2e, 0xe5, 0x80, \ + 0x00, 0x0a, 0x9c, 0x04, \ + 0xc7, 0xff, 0x63, 0xfd, \ + 0x9d, 0xb4, 0x90, 0xc5, \ + 0x8d, 0x01, 0x92, 0xb3, \ + 0xe3, 0x90, 0x56, 0x01, \ + 0xa1, 0x0b, 0x88, 0x0a, \ + 0xa0, 0x14, 0x80, 0xe5, \ + 0x80, 0x85, 0xc1, 0x1d, \ + 0x56, 0x01, 0x91, 0x01, \ + 0x8a, 0x0a, 0xe3, 0x90, \ + 0x82, 0x05, 0xa1, 0x0b, \ + 0xce, 0x07, 0x98, 0x14, \ + 0x80, 0x85, 0xc0, 0x1d, \ + 0x56, 0x01, 0x87, 0x01, \ + 0xc8, 0x0a, 0xe3, 0x90, \ + 0x80, 0xe5, 0xa1, 0x0b, \ + 0xc0, 0x1d, 0xa8, 0x1a, \ + 0x87, 0x01, 0x80, 0x85, \ + 0xe3, 0x90, 0x56, 0x01, \ + 0x95, 0x14, 0x84, 0x0b, \ + 0x93, 0x34, 0x92, 0x2b, \ + 0x78, 0x38, 0x9d, 0x44, \ + 0x97, 0x24, 0xef, 0xfc, \ + 0x00, 0x09, 0x99, 0x04, \ + 0x00, 0x0a, 0xe0, 0x34, \ + 0x84, 0xe5, 0xe3, 0xfc, \ + 0xe3, 0x90, 0x16, 0x09, \ + 0xec, 0x14, 0x00, 0x00, \ + 0xf4, 0x04, 0x01, 0x0a, \ + 0x9d, 0x04, 0x84, 0x15, \ + 0x8b, 0x04, 0xfc, 0x0e, \ + 0x89, 0x04, 0xbe, 0x0e, \ + 0x84, 0x0b, 0xbc, 0x0e, \ + 0x8f, 0x24, 0x8d, 0x14, \ + 0x92, 0x4b, 0x91, 0x34, \ + 0x41, 0x38, 0x99, 0x54, \ + 0x97, 0x24, 0xe8, 0x00, \ + 0x82, 0x08, 0x9b, 0x04, \ + 0xc1, 0x9d, 0x82, 0x91, \ + 0x88, 0x61, 0x84, 0xa1, \ + 0x88, 0x81, 0x88, 0x71, \ + 0x81, 0x01, 0xea, 0xd5, \ + 0x0c, 0x0c, 0xf8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x82, 0xbb, 0x8a, 0xcb, \ + 0x10, 0xa3, 0x80, 0x8b, \ + 0xa3, 0xad, 0xe0, 0x14, \ + 0x9f, 0x54, 0xa1, 0x94, \ + 0x18, 0x09, 0x90, 0x15, \ + 0xa1, 0x0b, 0xe0, 0x30, \ + 0xf0, 0x90, 0x40, 0x92, \ + 0x80, 0x45, 0x95, 0x11, \ + 0x40, 0x11, 0x89, 0x11, \ + 0x18, 0x09, 0xe3, 0x90, \ + 0xa1, 0x0b, 0xe0, 0x30, \ + 0x9d, 0x04, 0x8e, 0x05, \ + 0xe0, 0x84, 0x02, 0x44, \ + 0x02, 0x10, 0x92, 0x13, \ + 0x8d, 0x1e, 0xe6, 0xa0, \ + 0xe5, 0xe0, 0x0c, 0x00, \ + 0x8b, 0xbe, 0x81, 0x71, \ + 0xfe, 0x8e, 0x89, 0x5e, \ + 0xbe, 0xae, 0xfc, 0x9e, \ + 0x80, 0x02, 0x8d, 0x04, \ + 0xa2, 0x03, 0x98, 0x12, \ + 0xf0, 0x80, 0x00, 0x04, \ + 0x84, 0x95, 0x94, 0x0d, \ + 0xe0, 0x20, 0x10, 0x13, \ + 0x96, 0x0b, 0xbc, 0x9e, \ + 0x86, 0xab, 0x84, 0x9b, \ + 0x98, 0xbb, 0x88, 0x8b, \ + 0xef, 0xfc, 0x6e, 0xa8, \ + 0x89, 0x54, 0x96, 0xcb, \ + 0x92, 0x2b, 0x8b, 0xb4, \ + 0x94, 0x3b, 0xfc, 0x94, \ + 0x90, 0x4b, 0xbe, 0xa4, \ + 0x18, 0x09, 0xfe, 0x84, \ + 0x82, 0x71, 0xe0, 0x30, \ + 0x84, 0x61, 0xc1, 0x7d, \ + 0x81, 0x75, 0xf8, 0x15, \ + 0xe0, 0x20, 0x10, 0x13, \ + 0x96, 0x0b, 0xbc, 0x9e, \ + 0xef, 0xfc, 0x6d, 0x48, \ + 0x0c, 0x0c, 0xb8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x80, 0xbb, 0x82, 0x9b, \ + 0xe0, 0x94, 0x00, 0x34, \ + 0xe1, 0x84, 0x02, 0xb4, \ + 0xa5, 0xc4, 0x82, 0x03, \ + 0xe0, 0x2e, 0x40, 0x0a, \ + 0xfb, 0xb0, 0x42, 0x02, \ + 0x81, 0x61, 0xa1, 0x71, \ + 0x40, 0xc2, 0xa2, 0x73, \ + 0x42, 0x62, 0xfa, 0x80, \ + 0x80, 0x73, 0xf3, 0x98, \ + 0x40, 0x12, 0xa9, 0x84, \ + 0x02, 0x04, 0xf2, 0x98, \ + 0xcb, 0x77, 0xe0, 0xec, \ + 0xf9, 0x84, 0x54, 0x72, \ + 0xe1, 0x94, 0x0e, 0x04, \ + 0xfa, 0x90, 0x40, 0x22, \ + 0xf9, 0x9c, 0x42, 0xa2, \ + 0xfb, 0x88, 0x44, 0x42, \ + 0x43, 0xf6, 0xa1, 0x1b, \ + 0xa4, 0x03, 0xef, 0xfc, \ + 0x42, 0x62, 0xa0, 0x1c, \ + 0x8e, 0x65, 0xf3, 0x98, \ + 0x8d, 0xae, 0x89, 0x5e, \ + 0x48, 0xba, 0x9f, 0x24, \ + 0x86, 0x23, 0xe6, 0xae, \ + 0x06, 0x44, 0xca, 0x27, \ + 0x86, 0x23, 0xe1, 0x84, \ + 0xe0, 0x12, 0x44, 0x2a, \ + 0x44, 0x62, 0x84, 0xc3, \ + 0xa0, 0xc1, 0xf2, 0x98, \ + 0xf2, 0x98, 0x56, 0x22, \ + 0xe0, 0xec, 0x14, 0xb4, \ + 0xb4, 0xcf, 0xcb, 0xc7, \ + 0xe1, 0x94, 0x04, 0xb4, \ + 0x7f, 0xfd, 0x57, 0xfe, \ + 0x58, 0xca, 0xc0, 0x3f, \ + 0x04, 0x1a, 0xe4, 0x2c, \ + 0x42, 0x0a, 0xe0, 0x00, \ + 0x80, 0x85, 0xe1, 0x80, \ + 0x7f, 0xfd, 0x59, 0xfe, \ + 0xfc, 0xce, 0xc0, 0x3f, \ + 0x81, 0x21, 0x80, 0x45, \ + 0x80, 0x2b, 0xbe, 0x2e, \ + 0x54, 0x18, 0x82, 0x3b, \ + 0x04, 0x04, 0xef, 0xd4, \ + 0xd0, 0x17, 0xe1, 0xe0, \ + 0x97, 0x1b, 0xa4, 0x1f, \ + 0xff, 0x21, 0xd0, 0x07, \ + 0x80, 0xe5, 0x8d, 0xa4, \ + 0xe4, 0x2c, 0x42, 0xca, \ + 0x81, 0x75, 0x80, 0xf5, \ + 0x9b, 0xb5, 0xff, 0x01, \ + 0xe0, 0x08, 0x40, 0x4a, \ + 0xff, 0x01, 0x80, 0x45, \ + 0x43, 0xfe, 0x80, 0x85, \ + 0xc0, 0x3f, 0x7f, 0xfd, \ + 0x8e, 0x3b, 0x94, 0x2b, \ + 0xef, 0xd4, 0x49, 0x88, \ + 0x8e, 0x3b, 0x94, 0x2b, \ + 0xef, 0xd4, 0x49, 0x48, \ + 0x82, 0x3b, 0x80, 0x2b, \ + 0x7f, 0xfd, 0x57, 0xfe, \ + 0x06, 0x0a, 0xc0, 0x3f, \ + 0x80, 0x85, 0xe1, 0xfc, \ + 0xe5, 0xfc, 0x06, 0xca, \ + 0xff, 0x01, 0x81, 0x45, \ + 0xe0, 0x00, 0x44, 0x4a, \ + 0x8b, 0x9e, 0xfe, 0x7e, \ + 0x80, 0x45, 0x90, 0xab, \ + 0xa3, 0x94, 0xff, 0x21, \ + 0x9d, 0x84, 0xa1, 0x74, \ + 0x47, 0xf6, 0x80, 0x65, \ + 0xfc, 0x04, 0xe0, 0x04, \ + 0x50, 0x58, 0xbe, 0x14, \ + 0x89, 0x24, 0xef, 0xd4, \ + 0x44, 0x62, 0x84, 0x83, \ + 0x8e, 0x83, 0xf2, 0x98, \ + 0xf2, 0x98, 0x44, 0x22, \ + 0x44, 0x22, 0x90, 0x93, \ + 0x52, 0xca, 0xf2, 0x98, \ + 0x04, 0x1a, 0xe4, 0x2c, \ + 0x42, 0x0a, 0xe0, 0x00, \ + 0x80, 0x45, 0xe1, 0x80, \ + 0xfe, 0x74, 0x81, 0x21, \ + 0x53, 0xfe, 0x80, 0x85, \ + 0xc0, 0x3f, 0x7f, 0xfd, \ + 0xe1, 0xe0, 0x06, 0x94, \ + 0xd0, 0x97, 0xd0, 0x27, \ + 0x92, 0x03, 0xa6, 0x2f, \ + 0xf2, 0x88, 0x42, 0x12, \ + 0x7f, 0xfd, 0x45, 0xfe, \ + 0x85, 0x1b, 0xdf, 0xff, \ + 0x80, 0xc5, 0x8d, 0x84, \ + 0xe4, 0x08, 0x42, 0xca, \ + 0x80, 0xf5, 0xa7, 0x94, \ + 0xff, 0x21, 0x81, 0x75, \ + 0xe0, 0x08, 0x40, 0x4a, \ + 0x80, 0x45, 0xa7, 0x94, \ + 0x84, 0x91, 0xff, 0x01, \ + 0x43, 0xfe, 0x80, 0x85, \ + 0xdf, 0xff, 0x7f, 0xfd, \ + 0xc7, 0x97, 0xa1, 0x1b, \ + 0x90, 0x2b, 0x82, 0x45, \ + 0x43, 0xb8, 0x8e, 0x3b, \ + 0x81, 0x31, 0xef, 0xd4, \ + 0x4c, 0x58, 0x92, 0x2b, \ + 0x81, 0x95, 0xef, 0xd4, \ + 0x92, 0x2b, 0x81, 0x31, \ + 0xef, 0xd4, 0x4c, 0x08, \ + 0x8e, 0x3b, 0x90, 0x2b, \ + 0xef, 0xd4, 0x42, 0xe8, \ + 0x7f, 0xfd, 0x45, 0xfe, \ + 0x85, 0x1b, 0xdf, 0xff, \ + 0x80, 0xa5, 0xff, 0x31, \ + 0xe4, 0x08, 0x42, 0xca, \ + 0x81, 0x55, 0x80, 0xb5, \ + 0xe0, 0x0c, 0x40, 0x4a, \ + 0xff, 0x01, 0x80, 0x45, \ + 0xa1, 0x1b, 0x84, 0x35, \ + 0x90, 0x2b, 0x84, 0x85, \ + 0x41, 0x98, 0x8e, 0x3b, \ + 0x04, 0x14, 0xef, 0xd4, \ + 0xc5, 0x07, 0xe0, 0xf8, \ + 0xc5, 0x17, 0xa4, 0x0f, \ + 0x92, 0x2b, 0x81, 0x31, \ + 0xef, 0xd4, 0x49, 0xe8, \ + 0x43, 0xfe, 0x83, 0x35, \ + 0xdf, 0xff, 0x7f, 0xfd, \ + 0xfa, 0xd5, 0xa1, 0x1b, \ + 0x92, 0x2b, 0x81, 0x31, \ + 0xef, 0xd4, 0x49, 0x48, \ + 0x8e, 0x3b, 0x90, 0x2b, \ + 0xef, 0xd4, 0x40, 0x28, \ + 0xe0, 0xf8, 0x04, 0x14, \ + 0xa4, 0x0f, 0xc5, 0x07, \ + 0x47, 0xfe, 0xc5, 0x17, \ + 0xdf, 0xff, 0x7f, 0xfd, \ + 0xff, 0x21, 0x87, 0x1b, \ + 0x80, 0xa5, 0x8b, 0x54, \ + 0xe4, 0x0c, 0x42, 0xca, \ + 0x81, 0x55, 0x80, 0xb5, \ + 0xe0, 0x08, 0x40, 0x4a, \ + 0xff, 0x01, 0x80, 0x45, \ + 0x43, 0xfe, 0x80, 0x85, \ + 0xdf, 0xff, 0x7f, 0xfd, \ + 0xe0, 0xf8, 0x06, 0x14, \ + 0xa0, 0x3f, 0xc5, 0x07, \ + 0x46, 0x4a, 0xc5, 0x17, \ + 0x02, 0x1a, 0xe0, 0x08, \ + 0x42, 0x0a, 0xe0, 0x00, \ + 0x80, 0x45, 0xe1, 0x80, \ + 0x81, 0x01, 0xff, 0x31, \ + 0x81, 0x11, 0x80, 0x45, \ + 0xe5, 0x16, 0x48, 0x5a, \ + 0x48, 0x5a, 0x88, 0x33, \ + 0x85, 0x3b, 0xe5, 0x96, \ + 0xf2, 0x90, 0x42, 0x12, \ + 0xff, 0x31, 0x80, 0x55, \ + 0x80, 0x45, 0xa1, 0x1b, \ + 0x42, 0x3a, 0xff, 0x31, \ + 0xa8, 0x1c, 0xf0, 0x0a, \ + 0x0c, 0x0c, 0xb8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x82, 0xb5, 0xa3, 0x0d, \ + 0xc5, 0x54, 0xc9, 0x44, \ + 0x80, 0x75, 0x8a, 0x4d, \ + 0x81, 0x25, 0x81, 0x41, \ + 0x00, 0x00, 0xaa, 0x43, \ + 0xa1, 0x0b, 0xec, 0x04, \ + 0xfc, 0xb5, 0xcd, 0x4e, \ + 0xdd, 0x93, 0x81, 0x01, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xa1, 0x5b, 0x8a, 0x58, \ + 0x0a, 0x30, 0x98, 0x85, \ + 0x81, 0xc1, 0xe6, 0xa0, \ + 0xe0, 0x24, 0x4c, 0x06, \ + 0x81, 0x81, 0x80, 0x7b, \ + 0x52, 0x42, 0xa5, 0x1b, \ + 0xa4, 0x86, 0xf0, 0xa0, \ + 0x06, 0x95, 0x80, 0xf5, \ + 0x14, 0x06, 0xfc, 0x30, \ + 0xd0, 0x00, 0x60, 0x01, \ + 0x94, 0x92, 0x81, 0x45, \ + 0xf0, 0x00, 0x14, 0x0e, \ + 0xf0, 0xa0, 0x56, 0x32, \ + 0xe3, 0x90, 0x16, 0xb9, \ + 0xe4, 0x24, 0x17, 0x1a, \ + 0x20, 0x01, 0x00, 0x98, \ + 0x4c, 0x5a, 0xc0, 0x00, \ + 0x00, 0x48, 0xe4, 0xac, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0x8a, 0xa8, 0x94, 0x93, \ + 0x50, 0x9a, 0x82, 0x81, \ + 0xdd, 0x9e, 0xe4, 0x28, \ + 0x88, 0xc1, 0x84, 0x51, \ + 0x3f, 0xfd, 0x3d, 0xc8, \ + 0x14, 0x9a, 0xc3, 0xff, \ + 0x03, 0x38, 0xe4, 0x08, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0x02, 0x9a, 0x8a, 0x18, \ + 0x02, 0xd8, 0xe4, 0x08, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0x81, 0x31, 0x81, 0x11, \ + 0xf0, 0x84, 0x4a, 0x42, \ + 0xfc, 0x04, 0x08, 0xc9, \ + 0x0e, 0xc4, 0x96, 0x68, \ + 0x10, 0x64, 0xe0, 0x88, \ + 0x00, 0x75, 0xe0, 0x88, \ + 0x00, 0x85, 0xfc, 0x1c, \ + 0x80, 0xf5, 0xfc, 0x20, \ + 0xe0, 0x20, 0x4e, 0x9a, \ + 0x20, 0x01, 0x00, 0x88, \ + 0x81, 0x85, 0xc0, 0x00, \ + 0xe0, 0x20, 0x4f, 0x1a, \ + 0xa0, 0x00, 0x00, 0x58, \ + 0x96, 0xc6, 0x94, 0x66, \ + 0x8a, 0x58, 0x83, 0x31, \ + 0xe4, 0x08, 0x18, 0x10, \ + 0xe4, 0x14, 0x58, 0x9a, \ + 0x3d, 0xf8, 0x82, 0x11, \ + 0xc3, 0xff, 0x3f, 0xfd, \ + 0xe0, 0x04, 0x06, 0x9a, \ + 0xbf, 0xfc, 0x3d, 0x28, \ + 0x0c, 0x0c, 0x81, 0x01, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x88, 0x9b, 0x8a, 0x6b, \ + 0x84, 0x3b, 0x86, 0x7b, \ + 0x9d, 0x84, 0x82, 0xbb, \ + 0x50, 0x01, 0x81, 0x01, \ + 0x81, 0x11, 0xe0, 0x10, \ + 0xfa, 0x80, 0x02, 0x14, \ + 0x84, 0x06, 0x8e, 0x13, \ + 0xe0, 0x10, 0x10, 0x19, \ + 0x02, 0x8a, 0x82, 0x11, \ + 0x50, 0x11, 0xf4, 0x20, \ + 0xfc, 0xb5, 0xe0, 0x10, \ + 0xa5, 0x0b, 0x8e, 0x08, \ + 0xa3, 0x0b, 0x80, 0xe5, \ + 0xa1, 0x0b, 0x94, 0xa5, \ + 0x81, 0x01, 0x94, 0xb5, \ + 0x81, 0x01, 0x8b, 0x25, \ + 0xe0, 0x14, 0x50, 0x01, \ + 0xe0, 0x18, 0x50, 0x01, \ + 0xe0, 0x30, 0x50, 0x01, \ + 0x14, 0x80, 0x98, 0x06, \ + 0x81, 0x11, 0xe4, 0x40, \ + 0xe0, 0x04, 0x04, 0x06, \ + 0xbe, 0x3e, 0x94, 0x0b, \ + 0xef, 0xd4, 0x4c, 0x28, \ + 0xbe, 0x54, 0x81, 0xc1, \ + 0xe0, 0x18, 0x10, 0x09, \ + 0x00, 0x06, 0xa1, 0x0b, \ + 0x50, 0x01, 0xe0, 0x04, \ + 0x8e, 0x85, 0xe0, 0x10, \ + 0xe9, 0x9c, 0x4e, 0x0a, \ + 0xe0, 0x14, 0x50, 0xc1, \ + 0x8c, 0x45, 0x98, 0xc6, \ + 0x81, 0x11, 0x83, 0x01, \ + 0xe0, 0x10, 0x50, 0x11, \ + 0xe0, 0x14, 0x10, 0x29, \ + 0xfa, 0x80, 0x04, 0x24, \ + 0xe0, 0x8c, 0x06, 0x24, \ + 0x14, 0x39, 0x82, 0x33, \ + 0x0e, 0x49, 0xfc, 0x0c, \ + 0xa3, 0x3f, 0xfc, 0x08, \ + 0xa6, 0x4f, 0x8e, 0x23, \ + 0x10, 0x29, 0x88, 0x46, \ + 0x10, 0x19, 0xe0, 0x14, \ + 0xc6, 0x27, 0xe0, 0x10, \ + 0x14, 0x29, 0x82, 0x23, \ + 0xa3, 0x2b, 0xfc, 0x08, \ + 0x98, 0x06, 0x80, 0x55, \ + 0x02, 0x8a, 0x82, 0x11, \ + 0x50, 0x11, 0xf4, 0x20, \ + 0x02, 0x14, 0xe0, 0x10, \ + 0x10, 0x29, 0xfa, 0x80, \ + 0xf6, 0xf5, 0xe0, 0x14, \ + 0x04, 0x8a, 0x82, 0x21, \ + 0x50, 0x21, 0xf4, 0x20, \ + 0xf4, 0x95, 0xe0, 0x14, \ + 0x83, 0x01, 0x81, 0x35, \ + 0xb8, 0xf1, 0x98, 0x06, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x10, 0x49, 0xdd, 0x93, \ + 0x09, 0x0a, 0xe0, 0x14, \ + 0xf0, 0x35, 0xf4, 0xfc, \ + 0x00, 0x44, 0xd4, 0x18, \ + 0xc3, 0x0d, 0xfa, 0x80, \ + 0x99, 0x11, 0xee, 0x85, \ + 0xe6, 0x86, 0x40, 0x0a, \ + 0xf0, 0x80, 0x42, 0x92, \ + 0xe0, 0x20, 0x02, 0x39, \ + 0x18, 0xf8, 0xa1, 0x3b, \ + 0x02, 0x09, 0x80, 0x00, \ + 0x8d, 0x1e, 0xe0, 0x2c, \ + 0xe0, 0x28, 0x02, 0x19, \ + 0xe0, 0x3c, 0x16, 0x29, \ + 0x89, 0x0e, 0xc3, 0x07, \ + 0x42, 0x10, 0x82, 0x09, \ + 0xfc, 0x0e, 0xf0, 0xfc, \ + 0xe0, 0x30, 0x50, 0x01, \ + 0xe6, 0x86, 0x44, 0x2a, \ + 0xe0, 0x2c, 0x50, 0xc1, \ + 0x50, 0x11, 0xb6, 0x3d, \ + 0x50, 0x21, 0xe0, 0x20, \ + 0x00, 0x06, 0xe0, 0x1c, \ + 0xfc, 0x00, 0x60, 0x01, \ + 0x50, 0x31, 0xa0, 0x0e, \ + 0x8b, 0x1e, 0xe0, 0x10, \ + 0xb2, 0x3d, 0x82, 0x95, \ + 0xb1, 0x3b, 0x84, 0xd5, \ + 0xb3, 0x3b, 0x8c, 0x85, \ + 0x96, 0x13, 0x94, 0xb5, \ + 0xec, 0x70, 0x02, 0x29, \ + 0x9f, 0x45, 0xa0, 0x2e, \ + 0x84, 0x95, 0xba, 0x3d, \ + 0x8a, 0xe5, 0xb9, 0x3b, \ + 0x92, 0x55, 0xbb, 0x3b, \ + 0x96, 0x13, 0xc4, 0x17, \ + 0xe1, 0x1c, 0x02, 0x25, \ + 0x9d, 0x85, 0xa0, 0x2e, \ + 0x8c, 0x85, 0xb5, 0x3b, \ + 0x90, 0x15, 0xb7, 0x3b, \ + 0x02, 0x21, 0x96, 0x13, \ + 0xa0, 0x2e, 0xeb, 0x70, \ + 0xbd, 0x3b, 0x9b, 0xf5, \ + 0xbf, 0x3b, 0x8c, 0x25, \ + 0xfe, 0x4e, 0x8c, 0xf5, \ + 0x02, 0x19, 0x96, 0x13, \ + 0xa1, 0x1b, 0xec, 0x70, \ + 0x80, 0x00, 0x0d, 0x38, \ + 0xfa, 0x80, 0x02, 0x24, \ + 0x96, 0x13, 0xc4, 0x17, \ + 0xe7, 0xdc, 0x02, 0x25, \ + 0x67, 0xfd, 0x16, 0x15, \ + 0xa2, 0x23, 0xc0, 0x01, \ + 0x99, 0x75, 0xa0, 0x2e, \ + 0x96, 0x13, 0xc4, 0x17, \ + 0xe0, 0x1c, 0x02, 0x25, \ + 0x99, 0x25, 0xa0, 0x2e, \ + 0x96, 0x13, 0xfe, 0x4e, \ + 0xec, 0x70, 0x02, 0x19, \ + 0x0b, 0xa8, 0xa1, 0x1b, \ + 0x02, 0x24, 0x80, 0x00, \ + 0x96, 0x13, 0xfa, 0x80, \ + 0x6b, 0x71, 0x02, 0x29, \ + 0xa0, 0x2e, 0xc0, 0x05, \ + 0xc2, 0x17, 0x97, 0x25, \ + 0x02, 0x2b, 0x96, 0x13, \ + 0xa0, 0x2e, 0xe4, 0x38, \ + 0x96, 0x13, 0x95, 0xd5, \ + 0xeb, 0x70, 0x02, 0x21, \ + 0xea, 0x70, 0x02, 0x11, \ + 0xa0, 0x2e, 0xa2, 0x23, \ + 0xfe, 0x4e, 0x95, 0x55, \ + 0x50, 0x01, 0x89, 0x01, \ + 0x96, 0x13, 0xe0, 0x28, \ + 0xe0, 0x2c, 0x10, 0x49, \ + 0xec, 0x70, 0x02, 0x59, \ + 0x8e, 0x85, 0xcb, 0x4d, \ + 0x64, 0x71, 0x08, 0x10, \ + 0x0a, 0x24, 0xc0, 0x03, \ + 0x81, 0xc1, 0xfa, 0x80, \ + 0xe0, 0x24, 0x50, 0xc1, \ + 0xfa, 0x80, 0x02, 0xc4, \ + 0x02, 0x19, 0x92, 0x13, \ + 0xa1, 0x1b, 0xec, 0x00, \ + 0x10, 0x09, 0x88, 0x05, \ + 0x58, 0x02, 0xe0, 0x2c, \ + 0x96, 0xc3, 0xf0, 0x94, \ + 0x6b, 0x71, 0x18, 0xc9, \ + 0x83, 0xcb, 0xc0, 0x05, \ + 0x10, 0x19, 0x82, 0xb5, \ + 0xa9, 0x1b, 0xe0, 0x28, \ + 0x50, 0x01, 0x82, 0x35, \ + 0x89, 0x01, 0xe0, 0x28, \ + 0xe0, 0x2c, 0x50, 0x01, \ + 0x50, 0x01, 0x91, 0x01, \ + 0x10, 0xc9, 0xe0, 0x24, \ + 0x82, 0xc1, 0xe0, 0x24, \ + 0xf4, 0x20, 0x18, 0x8a, \ + 0xe0, 0x24, 0x50, 0xc1, \ + 0x81, 0x45, 0xf6, 0x95, \ + 0x50, 0x01, 0x91, 0x01, \ + 0x10, 0x09, 0xe0, 0x24, \ + 0x82, 0x01, 0xe0, 0x2c, \ + 0xe0, 0x2c, 0x50, 0x01, \ + 0x00, 0x04, 0x90, 0x18, \ + 0xc3, 0x0d, 0xfa, 0x80, \ + 0x10, 0x49, 0xf2, 0x75, \ + 0xa9, 0x4b, 0xe0, 0x28, \ + 0x8d, 0x04, 0x86, 0x35, \ + 0x8b, 0x14, 0x96, 0x01, \ + 0x8b, 0x1e, 0x90, 0x13, \ + 0x02, 0x19, 0x80, 0x28, \ + 0x82, 0x29, 0xe0, 0x40, \ + 0x4f, 0xd8, 0xfc, 0x04, \ + 0x8d, 0x14, 0xe8, 0x00, \ + 0x44, 0x01, 0x8b, 0x24, \ + 0x00, 0x16, 0xe0, 0x40, \ + 0xfc, 0x00, 0x60, 0x01, \ + 0xbe, 0x54, 0xa0, 0x0e, \ + 0xfe, 0x44, 0x81, 0xc1, \ + 0xe6, 0x4d, 0x97, 0x25, \ + 0x81, 0xc1, 0xbe, 0x54, \ + 0xa6, 0x3d, 0x8e, 0xb5, \ + 0xaa, 0x3d, 0x84, 0x25, \ + 0xa9, 0x3b, 0x88, 0x15, \ + 0x80, 0x00, 0x0b, 0x58, \ + 0x8c, 0x95, 0xab, 0x3b, \ + 0xfa, 0x80, 0x00, 0x24, \ + 0x96, 0x43, 0x80, 0x43, \ + 0x67, 0x71, 0x08, 0x29, \ + 0xa0, 0x2e, 0xc0, 0x05, \ + 0xa3, 0x3b, 0x85, 0xc5, \ + 0x80, 0x00, 0x0b, 0x18, \ + 0x0b, 0x88, 0xa5, 0x3b, \ + 0xa7, 0x3b, 0x80, 0x00, \ + 0x00, 0x24, 0x88, 0x75, \ + 0x80, 0x43, 0xfa, 0x80, \ + 0x96, 0x43, 0xc4, 0x47, \ + 0xeb, 0xdc, 0x08, 0x25, \ + 0x83, 0xb5, 0xa0, 0x2e, \ + 0x0b, 0x38, 0xad, 0x3b, \ + 0xaf, 0x3b, 0x80, 0x00, \ + 0x00, 0x24, 0x84, 0xb5, \ + 0x80, 0x43, 0xfa, 0x80, \ + 0x67, 0x71, 0x00, 0xb0, \ + 0x02, 0x44, 0xc0, 0x03, \ + 0x80, 0x43, 0xe0, 0x84, \ + 0x08, 0x19, 0x82, 0x03, \ + 0xc0, 0x04, 0x64, 0x01, \ + 0x6c, 0x01, 0x00, 0x23, \ + 0xc8, 0x17, 0xc0, 0x01, \ + 0xa0, 0x2e, 0x82, 0x23, \ + 0xa0, 0x24, 0x81, 0x25, \ + 0x00, 0x24, 0xfe, 0x44, \ + 0x02, 0x06, 0xfc, 0x00, \ + 0xfc, 0x00, 0x60, 0x01, \ + 0x84, 0x15, 0x83, 0x0b, \ + 0x40, 0x0a, 0x89, 0x04, \ + 0x8d, 0x14, 0xe0, 0x80, \ + 0x00, 0x06, 0x98, 0x85, \ + 0xfc, 0x00, 0x60, 0x01, \ + 0x96, 0xf5, 0x81, 0x2b, \ + 0x90, 0x03, 0x8b, 0x04, \ + 0xe0, 0x40, 0x40, 0xc1, \ + 0x8d, 0x04, 0x8b, 0xa5, \ + 0xe0, 0x24, 0x00, 0x19, \ + 0x82, 0x75, 0xa4, 0x1d, \ + 0x84, 0x65, 0xa1, 0x1b, \ + 0x86, 0x45, 0xa3, 0x1b, \ + 0x8d, 0x14, 0xa5, 0x1b, \ + 0x84, 0x04, 0x92, 0xd5, \ + 0x87, 0x25, 0x80, 0x2d, \ + 0x88, 0x05, 0xa7, 0x1b, \ + 0x8a, 0x65, 0xa9, 0x1b, \ + 0x8d, 0x14, 0xab, 0x1b, \ + 0x84, 0x04, 0x90, 0x95, \ + 0x83, 0xb5, 0x81, 0x2b, \ + 0x84, 0x04, 0x8d, 0x14, \ + 0xfe, 0x4e, 0x81, 0x2d, \ + 0x86, 0x04, 0x8a, 0x95, \ + 0x82, 0x45, 0x80, 0x2d, \ + 0x8d, 0x14, 0x85, 0x85, \ + 0x81, 0x2d, 0x84, 0x04, \ + 0x80, 0x95, 0xfe, 0x4e, \ + 0x80, 0x2d, 0x86, 0x04, \ + 0x8b, 0x04, 0x88, 0x05, \ + 0x00, 0x19, 0x90, 0x03, \ + 0x83, 0x21, 0xe0, 0x40, \ + 0x8d, 0x04, 0x83, 0xf5, \ + 0x80, 0x2d, 0x82, 0x04, \ + 0x90, 0x03, 0x8b, 0x04, \ + 0xe0, 0x40, 0x00, 0x09, \ + 0x82, 0xc5, 0xfe, 0x4e, \ + 0xfa, 0x80, 0x02, 0x04, \ + 0x83, 0x25, 0x83, 0x21, \ + 0x80, 0x04, 0x8d, 0x04, \ + 0x8b, 0x04, 0x81, 0x2b, \ + 0x00, 0x09, 0x90, 0x03, \ + 0xfe, 0x4e, 0xe0, 0x40, \ + 0x02, 0x04, 0xfc, 0x85, \ + 0x81, 0x55, 0xfa, 0x80, \ + 0x90, 0x03, 0x8b, 0x04, \ + 0xe0, 0x40, 0x00, 0x19, \ + 0xfc, 0x04, 0x81, 0x21, \ + 0xe8, 0x00, 0x44, 0x88, \ + 0xfe, 0x44, 0x8b, 0x14, \ + 0xbe, 0x54, 0x81, 0xc1, \ + 0x42, 0x01, 0x90, 0x13, \ + 0x8d, 0x14, 0xe0, 0x40, \ + 0xe0, 0x28, 0x02, 0x09, \ + 0xa1, 0x0b, 0xcf, 0x07, \ + 0x50, 0x01, 0x82, 0x41, \ + 0x50, 0x41, 0xe0, 0x18, \ + 0x25, 0x88, 0xe0, 0x14, \ + 0x21, 0x98, 0x9f, 0xfc, \ + 0x00, 0x24, 0xff, 0xfc, \ + 0x80, 0x43, 0xfa, 0x80, \ + 0x96, 0x43, 0xc2, 0x47, \ + 0x6f, 0xb9, 0x08, 0x2b, \ + 0xa0, 0x2e, 0xc0, 0x01, \ + 0x00, 0x24, 0xef, 0xa5, \ + 0x80, 0x43, 0xfa, 0x80, \ + 0x96, 0x43, 0xc2, 0x47, \ + 0x63, 0xb9, 0x08, 0x23, \ + 0xa0, 0x2e, 0xc0, 0x02, \ + 0x00, 0x24, 0xef, 0x05, \ + 0x80, 0x43, 0xfa, 0x80, \ + 0x96, 0x43, 0xc4, 0x47, \ + 0xe3, 0xdc, 0x08, 0x25, \ + 0xed, 0x75, 0xa0, 0x2e, \ + 0xfa, 0x80, 0x00, 0x24, \ + 0x00, 0xb0, 0x80, 0x43, \ + 0xc0, 0x03, 0x67, 0x71, \ + 0xe0, 0x84, 0x02, 0x44, \ + 0x82, 0x03, 0x80, 0x43, \ + 0x60, 0x01, 0x08, 0x19, \ + 0x00, 0x23, 0xc0, 0x04, \ + 0xc0, 0x01, 0x6c, 0x01, \ + 0xd1, 0xc5, 0xc8, 0x17, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x80, 0xf5, 0xa3, 0x2b, \ + 0x83, 0x01, 0xa1, 0x0b, \ + 0x40, 0x10, 0x80, 0x65, \ + 0xdd, 0x93, 0xf0, 0x04, \ + 0x80, 0x55, 0xa3, 0x0b, \ + 0x82, 0x0b, 0x81, 0x11, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0x81, 0x01, 0xe5, 0x2d, \ + 0x86, 0x04, 0x80, 0xf5, \ + 0x41, 0x4a, 0x84, 0x14, \ + 0x00, 0x0c, 0xe0, 0x04, \ + 0xdd, 0x93, 0xe2, 0x00, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x80, 0xb5, 0xe2, 0x3d, \ + 0x84, 0x0e, 0xfb, 0x01, \ + 0xdd, 0x93, 0x81, 0x01, \ + 0x00, 0x34, 0xa1, 0x4b, \ + 0x48, 0x22, 0xe0, 0x88, \ + 0xbc, 0x04, 0xf0, 0x80, \ + 0xf8, 0x08, 0x08, 0x45, \ + 0x49, 0x1a, 0x82, 0x25, \ + 0x4e, 0x0a, 0xe0, 0x14, \ + 0x00, 0x88, 0xe1, 0x9c, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0x8b, 0x4d, 0x85, 0x25, \ + 0x4e, 0x0a, 0x88, 0x05, \ + 0x06, 0x30, 0xe9, 0x9c, \ + 0x81, 0xc1, 0xec, 0x04, \ + 0xe9, 0x9c, 0x40, 0x0a, \ + 0xc4, 0xc7, 0x81, 0x41, \ + 0xb2, 0xc4, 0x84, 0xc3, \ + 0x59, 0x1a, 0x80, 0xc5, \ + 0x00, 0x88, 0xe0, 0x14, \ + 0x83, 0x45, 0xa0, 0x00, \ + 0xe0, 0x14, 0x58, 0x9a, \ + 0x20, 0x01, 0x01, 0x18, \ + 0x82, 0x41, 0xc0, 0x00, \ + 0xfa, 0x80, 0x18, 0x44, \ + 0xe0, 0x30, 0x47, 0x1a, \ + 0x3f, 0xfd, 0x3e, 0xd8, \ + 0xf9, 0xb5, 0xc3, 0xff, \ + 0xec, 0x08, 0x02, 0x30, \ + 0x83, 0x01, 0x80, 0x16, \ + 0x80, 0x46, 0xdd, 0x93, \ + 0xdd, 0x93, 0x83, 0x01, \ + 0x80, 0x3d, 0xff, 0x01, \ + 0x81, 0x01, 0x80, 0xd5, \ + 0xf1, 0x88, 0x44, 0x02, \ + 0xf3, 0x8c, 0x46, 0x02, \ + 0xf6, 0x80, 0x00, 0x34, \ + 0xe0, 0x80, 0x00, 0x0a, \ + 0x02, 0x24, 0x80, 0x95, \ + 0x82, 0x03, 0xf6, 0x80, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xf6, 0x80, 0x00, 0x34, \ + 0xe0, 0x80, 0x00, 0x0a, \ + 0x02, 0x24, 0x80, 0x95, \ + 0x82, 0x03, 0xf6, 0x80, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x8c, 0x3d, 0xff, 0x61, \ + 0xfb, 0x01, 0x80, 0x75, \ + 0x7d, 0x98, 0x84, 0x0e, \ + 0x44, 0x06, 0xef, 0xfc, \ + 0xa0, 0x23, 0xe0, 0x00, \ + 0x81, 0x11, 0x8d, 0x2d, \ + 0xa1, 0x0d, 0x80, 0xf5, \ + 0x80, 0x95, 0xa3, 0x11, \ + 0x02, 0x24, 0x84, 0x21, \ + 0x00, 0x14, 0xe1, 0x84, \ + 0x0c, 0x0c, 0xfa, 0x80, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0x88, 0x8b, 0x8a, 0xab, \ + 0x84, 0x7b, 0xfc, 0x3e, \ + 0x9b, 0x64, 0x81, 0x91, \ + 0x82, 0xbb, 0x84, 0x9e, \ + 0xe0, 0x10, 0x42, 0x95, \ + 0x90, 0x2b, 0x8e, 0x1b, \ + 0xef, 0xfc, 0x76, 0x88, \ + 0x80, 0x75, 0xa1, 0x0b, \ + 0xec, 0x04, 0x12, 0x80, \ + 0xe0, 0x00, 0x0c, 0x1a, \ + 0x03, 0x48, 0x96, 0x6b, \ + 0x02, 0xf0, 0xa0, 0x00, \ + 0xbc, 0x1e, 0xe4, 0x30, \ + 0x8e, 0x2b, 0x8c, 0x1b, \ + 0x80, 0x4b, 0x90, 0x3b, \ + 0x76, 0x18, 0x94, 0x5b, \ + 0xa1, 0x0b, 0xef, 0xfc, \ + 0x80, 0x00, 0x16, 0x88, \ + 0x00, 0x20, 0x99, 0x28, \ + 0xc4, 0x07, 0xe4, 0x04, \ + 0xfc, 0x00, 0x0e, 0x45, \ + 0x0e, 0x55, 0xc4, 0x27, \ + 0x46, 0x72, 0xfc, 0x08, \ + 0x18, 0x44, 0xf0, 0x80, \ + 0x42, 0x72, 0xe1, 0x7c, \ + 0x0c, 0x54, 0xf0, 0x88, \ + 0x4e, 0x42, 0xe1, 0x7c, \ + 0x50, 0xc2, 0xf1, 0x94, \ + 0x48, 0x72, 0xf3, 0x98, \ + 0xa1, 0x4b, 0xf9, 0xa0, \ + 0x88, 0x65, 0x99, 0x94, \ + 0x60, 0x01, 0x08, 0x06, \ + 0x81, 0x51, 0xe0, 0x00, \ + 0x48, 0x82, 0x8e, 0x43, \ + 0xa1, 0x4b, 0xf2, 0x94, \ + 0x86, 0x95, 0x96, 0x6b, \ + 0x81, 0x11, 0x83, 0xd5, \ + 0x02, 0x80, 0x80, 0x65, \ + 0x02, 0x14, 0xec, 0x04, \ + 0xc4, 0x17, 0xfa, 0x80, \ + 0xfc, 0x04, 0x0e, 0x25, \ + 0x82, 0x05, 0x94, 0x2d, \ + 0xfa, 0x80, 0x02, 0x94, \ + 0x0e, 0x25, 0xc4, 0x17, \ + 0x95, 0x2d, 0xfc, 0x04, \ + 0xfc, 0x04, 0xee, 0xb5, \ + 0xfc, 0x04, 0x00, 0xa5, \ + 0xe0, 0x00, 0x12, 0xa8, \ + 0x83, 0x71, 0x81, 0x81, \ + 0xfb, 0x41, 0x96, 0x6b, \ + 0x8c, 0x34, 0x98, 0x4e, \ + 0x08, 0x25, 0xfc, 0x44, \ + 0x84, 0x14, 0xfc, 0x08, \ + 0xfc, 0x00, 0x08, 0x05, \ + 0x56, 0x3a, 0xb4, 0x33, \ + 0xa2, 0xa3, 0xe4, 0x0a, \ + 0xe4, 0x8a, 0x42, 0x3a, \ + 0xe4, 0x02, 0x44, 0xaa, \ + 0xe4, 0x82, 0x40, 0xaa, \ + 0x84, 0xb3, 0xa1, 0x9b, \ + 0xf2, 0x84, 0x54, 0x02, \ + 0xa1, 0x8d, 0x82, 0x85, \ + 0xff, 0x01, 0x8a, 0x75, \ + 0x8a, 0x15, 0x80, 0xad, \ + 0x56, 0x02, 0x81, 0x01, \ + 0x54, 0x02, 0xf1, 0xac, \ + 0x85, 0xe5, 0xf3, 0xa8, \ + 0xe1, 0x7c, 0x00, 0x84, \ + 0xf0, 0x80, 0x42, 0x72, \ + 0xf2, 0x80, 0x44, 0x82, \ + 0xc0, 0x2f, 0xc0, 0x1f, \ + 0xe0, 0xfc, 0x00, 0x24, \ + 0xa2, 0x0f, 0xc3, 0x17, \ + 0xa1, 0xad, 0x81, 0x41, \ + 0xe1, 0x84, 0x02, 0x24, \ + 0xf1, 0x80, 0x46, 0x42, \ + 0xf3, 0x84, 0x44, 0x42, \ + 0x86, 0x0b, 0x80, 0x45, \ + 0x84, 0x1b, 0x80, 0x45, \ + 0x42, 0x12, 0x96, 0x03, \ + 0x8e, 0x2b, 0xf2, 0xa8, \ + 0x70, 0x98, 0x90, 0x3b, \ + 0x80, 0xab, 0xef, 0xcc, \ + 0xff, 0x01, 0x9d, 0x25, \ + 0x0e, 0x68, 0x80, 0x8d, \ + 0xa1, 0xad, 0xc0, 0x00, \ + 0xc0, 0x00, 0x0e, 0x38, \ + 0x4e, 0x02, 0x81, 0x01, \ + 0x50, 0x02, 0xf1, 0x9c, \ + 0x83, 0x01, 0xf3, 0xa0, \ + 0xff, 0x01, 0xbe, 0x0e, \ + 0x82, 0x55, 0x80, 0x9d, \ + 0x96, 0x2b, 0x8c, 0x1b, \ + 0x72, 0x58, 0x94, 0x3b, \ + 0x80, 0x9b, 0xef, 0xfc, \ + 0x0d, 0x68, 0xa1, 0x9b, \ + 0x04, 0x94, 0x80, 0x00, \ + 0xfc, 0x2e, 0xfa, 0x80, \ + 0x00, 0x20, 0xc2, 0x27, \ + 0xa1, 0x0b, 0xec, 0x04, \ + 0x81, 0x11, 0x83, 0x41, \ + 0x02, 0x44, 0x94, 0x25, \ + 0x00, 0x8a, 0xef, 0x80, \ + 0x92, 0xe5, 0xe4, 0x80, \ + 0xe5, 0x00, 0x00, 0x9a, \ + 0x20, 0x01, 0x05, 0x48, \ + 0x81, 0x11, 0xc0, 0x00, \ + 0x94, 0x35, 0x81, 0x31, \ + 0x20, 0x01, 0x00, 0x48, \ + 0x81, 0x31, 0xc0, 0x00, \ + 0xf0, 0xac, 0x40, 0x32, \ + 0xf2, 0xa8, 0x42, 0x12, \ + 0xef, 0xd0, 0x43, 0xc8, \ + 0x82, 0xab, 0x80, 0xbb, \ + 0x00, 0x00, 0xfc, 0x04, \ + 0xa1, 0x0b, 0xec, 0x04, \ + 0x83, 0x31, 0x81, 0x11, \ + 0x02, 0x34, 0x90, 0x65, \ + 0x00, 0x8a, 0xef, 0x80, \ + 0x90, 0x25, 0xe4, 0x80, \ + 0xe5, 0x00, 0x00, 0x9a, \ + 0x20, 0x01, 0x04, 0x78, \ + 0x81, 0x11, 0xc0, 0x00, \ + 0x90, 0x95, 0x81, 0x21, \ + 0x20, 0x01, 0x00, 0x48, \ + 0x81, 0x21, 0xc0, 0x00, \ + 0xf0, 0x9c, 0x40, 0x22, \ + 0xf2, 0xa0, 0x42, 0x12, \ + 0x41, 0xb8, 0xfc, 0x24, \ + 0x80, 0x7b, 0xef, 0xd0, \ + 0x00, 0x74, 0x82, 0x8b, \ + 0x02, 0x84, 0xe1, 0x84, \ + 0xa2, 0x0f, 0xe0, 0xfc, \ + 0x02, 0x84, 0xa1, 0x8b, \ + 0x80, 0xb3, 0xe1, 0x04, \ + 0xf2, 0xa8, 0x40, 0x12, \ + 0x11, 0x6a, 0x8c, 0x05, \ + 0x55, 0xfe, 0xe0, 0x00, \ + 0xdf, 0xff, 0x7f, 0xfd, \ + 0x4e, 0xca, 0x8c, 0x15, \ + 0x8c, 0x15, 0xe4, 0x28, \ + 0x00, 0x8a, 0x87, 0x25, \ + 0xec, 0x75, 0xe4, 0x80, \ + 0xec, 0x84, 0x02, 0x20, \ + 0xe8, 0x84, 0x02, 0x44, \ + 0xe5, 0x00, 0x00, 0x9a, \ + 0xbf, 0xfc, 0x3b, 0x18, \ + 0xec, 0x25, 0x81, 0x31, \ + 0xe8, 0x80, 0x06, 0x44, \ + 0xbf, 0xfc, 0x3b, 0x18, \ + 0x00, 0x8a, 0xf7, 0x05, \ + 0xf0, 0x35, 0xe4, 0x80, \ + 0x02, 0x10, 0xfc, 0x14, \ + 0x02, 0x34, 0xec, 0x84, \ + 0x00, 0x9a, 0xe8, 0x84, \ + 0x3b, 0xe8, 0xe5, 0x00, \ + 0x81, 0x21, 0xbf, 0xfc, \ + 0x04, 0x34, 0xee, 0xc5, \ + 0x3b, 0xe8, 0xe8, 0x80, \ + 0xf7, 0xd5, 0xbf, 0xfc, \ + 0xe0, 0x00, 0x0e, 0xea, \ + 0x7f, 0xfd, 0x55, 0xfe, \ + 0xf4, 0x45, 0xdf, 0xff, \ + 0xe0, 0x00, 0x10, 0xca, \ + 0x4f, 0xfe, 0x80, 0x85, \ + 0xdf, 0xff, 0x7f, 0xfd, \ + 0x82, 0x05, 0xa1, 0x0b, \ + 0xe0, 0x00, 0x01, 0x4a, \ + 0x7f, 0xfd, 0x41, 0xfe, \ + 0x82, 0x15, 0xdf, 0xff, \ + 0x16, 0xca, 0x81, 0x85, \ + 0x41, 0xfe, 0xe0, 0x00, \ + 0xdf, 0xff, 0x7f, 0xfd, \ + 0x96, 0x0b, 0x80, 0x45, \ + 0xe0, 0x9e, 0x4e, 0x0a, \ + 0xe1, 0x7c, 0x10, 0x74, \ + 0xfa, 0x80, 0x04, 0x94, \ + 0x7f, 0xfd, 0x41, 0xfe, \ + 0x81, 0x11, 0xdf, 0xff, \ + 0xef, 0xcc, 0x7d, 0x28, \ + 0x82, 0x05, 0x91, 0x1b, \ + 0xe0, 0x20, 0x42, 0xda, \ + 0x00, 0x98, 0xbe, 0x24, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0x40, 0xda, 0x81, 0x95, \ + 0xbe, 0x24, 0xe4, 0x1c, \ + 0xa0, 0x00, 0x00, 0x58, \ + 0x14, 0x74, 0xfc, 0x34, \ + 0x00, 0x98, 0xe8, 0x8c, \ + 0x80, 0x00, 0x40, 0x01, \ + 0x91, 0x1d, 0x80, 0x85, \ + 0x81, 0x35, 0x80, 0xe5, \ + 0x80, 0x85, 0xcf, 0x0d, \ + 0x4c, 0x05, 0x83, 0x01, \ + 0xa1, 0x2b, 0xe0, 0x10, \ + 0xb4, 0xab, 0x80, 0x45, \ + 0x80, 0xae, 0x9d, 0x04, \ + 0xa0, 0xf1, 0x98, 0x04, \ + 0xf6, 0x7c, 0x0c, 0x0c, \ + 0x42, 0xa2, 0xdd, 0x93, \ + 0x80, 0x1d, 0xf8, 0x20, \ + 0x81, 0x01, 0x80, 0xe5, \ + 0x81, 0x01, 0xe5, 0x05, \ + 0x81, 0x91, 0xfc, 0x0e, \ + 0x81, 0x11, 0xed, 0xd5, \ + 0xf1, 0xac, 0x56, 0x12, \ + 0xf3, 0xa8, 0x54, 0x12, \ + 0xf1, 0x9c, 0x4e, 0x12, \ + 0xbe, 0x0e, 0x81, 0x01, \ + 0xf3, 0xa0, 0x50, 0x12, \ + 0x00, 0x0c, 0xe3, 0x15, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xed, 0x10, 0x1e, 0xf0, \ + 0x8d, 0x4e, 0x9d, 0x5e, \ + 0x99, 0x1e, 0x84, 0x8b, \ + 0xa2, 0x08, 0x80, 0xab, \ + 0x81, 0x01, 0xa1, 0x0b, \ + 0x98, 0xe5, 0xa1, 0x0e, \ + 0xb3, 0x14, 0xb5, 0x04, \ + 0xb1, 0x14, 0x9b, 0x1e, \ + 0x02, 0xa0, 0x9f, 0x1e, \ + 0xc0, 0x02, 0x65, 0x01, \ + 0x02, 0xa0, 0x93, 0x1e, \ + 0xc0, 0x04, 0x66, 0x01, \ + 0x02, 0xa0, 0x91, 0x1e, \ + 0xc0, 0x06, 0x67, 0x01, \ + 0x02, 0xa0, 0x8f, 0x1e, \ + 0xc0, 0x09, 0x64, 0x01, \ + 0x02, 0xa0, 0x97, 0x1e, \ + 0xc0, 0x0b, 0x65, 0x01, \ + 0x12, 0x00, 0x95, 0x1e, \ + 0x16, 0x00, 0xe4, 0xc0, \ + 0x0c, 0x30, 0xe6, 0x40, \ + 0x81, 0x71, 0xe5, 0x20, \ + 0xa1, 0x0b, 0x81, 0x01, \ + 0x0c, 0x03, 0x8e, 0x35, \ + 0x98, 0x12, 0xf8, 0x90, \ + 0x9f, 0x34, 0x9d, 0x24, \ + 0x94, 0x5b, 0x9b, 0x44, \ + 0xef, 0xe4, 0x5b, 0x88, \ + 0x8a, 0xd5, 0xa1, 0x0b, \ + 0xf8, 0x90, 0x0c, 0x03, \ + 0x0c, 0x23, 0x98, 0x12, \ + 0x99, 0x34, 0xe0, 0x30, \ + 0x95, 0x44, 0x8c, 0x38, \ + 0x97, 0x44, 0x8b, 0x4e, \ + 0x8f, 0x44, 0x89, 0x4e, \ + 0x91, 0x44, 0xfe, 0x4e, \ + 0x93, 0x44, 0xfc, 0x4e, \ + 0xbc, 0xae, 0xbe, 0x4e, \ + 0x9f, 0x54, 0x9d, 0x44, \ + 0xef, 0xe4, 0x60, 0x88, \ + 0x84, 0xd5, 0xa1, 0x0b, \ + 0xf8, 0x90, 0x0c, 0x03, \ + 0x0c, 0x23, 0x98, 0x12, \ + 0x99, 0x34, 0xe0, 0x30, \ + 0x8b, 0x9e, 0x8e, 0x38, \ + 0x08, 0xf0, 0x89, 0xbe, \ + 0xfe, 0x4e, 0xe5, 0x00, \ + 0xfc, 0x4e, 0x95, 0x44, \ + 0xbe, 0x4e, 0x97, 0x44, \ + 0x8d, 0x44, 0xbc, 0xae, \ + 0x65, 0xd8, 0x9f, 0x54, \ + 0xa2, 0x18, 0xef, 0xe4, \ + 0x82, 0x71, 0xe5, 0x24, \ + 0x52, 0x25, 0xc3, 0x7d, \ + 0x52, 0x25, 0xf8, 0x34, \ + 0x84, 0xb1, 0xe0, 0x2c, \ + 0xee, 0x95, 0x84, 0x61, \ + 0xe5, 0x10, 0x1e, 0xf0, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x86, 0xab, 0x88, 0x8b, \ + 0x80, 0x6b, 0x82, 0x9b, \ + 0xec, 0x04, 0x0e, 0x60, \ + 0xbc, 0x9e, 0xbe, 0x2e, \ + 0x81, 0x21, 0x81, 0x01, \ + 0x81, 0x41, 0x89, 0x31, \ + 0x8c, 0x5b, 0x8e, 0x1b, \ + 0xef, 0xe0, 0x4a, 0xa8, \ + 0x84, 0xf5, 0xa1, 0x0b, \ + 0xbc, 0x9e, 0xbe, 0xae, \ + 0x81, 0x21, 0x81, 0x01, \ + 0x81, 0x41, 0x91, 0x31, \ + 0x8c, 0x5b, 0x8e, 0x1b, \ + 0xef, 0xe0, 0x49, 0xe8, \ + 0x82, 0x75, 0xa1, 0x0b, \ + 0xbc, 0x9e, 0xbe, 0x8e, \ + 0x81, 0x21, 0x81, 0x01, \ + 0x81, 0x41, 0xa1, 0x31, \ + 0x8c, 0x5b, 0x8e, 0x1b, \ + 0xef, 0xe0, 0x49, 0x28, \ + 0x0c, 0x0c, 0x90, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0x82, 0x65, 0xa1, 0x1b, \ + 0x88, 0x0d, 0xff, 0x41, \ + 0x08, 0x04, 0x82, 0x45, \ + 0x48, 0x4a, 0xe1, 0xac, \ + 0x48, 0x12, 0xe1, 0x06, \ + 0x81, 0x65, 0xf1, 0x90, \ + 0xdd, 0x93, 0xff, 0x01, \ + 0x97, 0x47, 0xc0, 0x4b, \ + 0x42, 0x4a, 0x82, 0x41, \ + 0x88, 0x16, 0xe1, 0x86, \ + 0x73, 0xfd, 0x40, 0x00, \ + 0x8c, 0x0e, 0xc0, 0x07, \ + 0xdd, 0x93, 0x81, 0x01, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x09, 0x0a, 0x80, 0x4b, \ + 0xc0, 0x08, 0x64, 0x01, \ + 0x80, 0x55, 0xfb, 0x01, \ + 0x88, 0x2a, 0x81, 0x01, \ + 0xa4, 0x13, 0x84, 0x1a, \ + 0xe6, 0x92, 0x42, 0x1a, \ + 0x64, 0x01, 0x02, 0x10, \ + 0xd7, 0x17, 0xc0, 0x04, \ + 0x8c, 0x2c, 0x82, 0x23, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xc4, 0x0d, 0x84, 0x01, \ + 0x80, 0x1b, 0x80, 0x55, \ + 0xdd, 0x93, 0x82, 0x0b, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0xed, 0x40, 0x1e, 0xf0, \ + 0x86, 0x8b, 0x88, 0x7b, \ + 0x82, 0xab, 0x84, 0x4b, \ + 0x0c, 0x99, 0x80, 0x6b, \ + 0x04, 0xf0, 0xe0, 0x18, \ + 0x06, 0xf0, 0xe5, 0x30, \ + 0x12, 0x2a, 0xe5, 0x00, \ + 0x89, 0x4e, 0xe0, 0x00, \ + 0x92, 0x1b, 0x88, 0x0b, \ + 0xef, 0xfc, 0x7b, 0x48, \ + 0xe5, 0x20, 0x04, 0xf0, \ + 0xe4, 0xf0, 0x06, 0xf0, \ + 0x92, 0x1b, 0x94, 0x0b, \ + 0xef, 0xfc, 0x7a, 0xc8, \ + 0xe5, 0x10, 0x04, 0xf0, \ + 0xe4, 0xe0, 0x06, 0xf0, \ + 0x90, 0x0b, 0x8b, 0x8e, \ + 0x7a, 0x38, 0x92, 0x1b, \ + 0x09, 0xf8, 0xef, 0xfc, \ + 0x80, 0x00, 0x40, 0x01, \ + 0x1e, 0x09, 0x94, 0x4b, \ + 0x14, 0x60, 0xe1, 0x10, \ + 0x02, 0x94, 0xe4, 0x30, \ + 0x94, 0x13, 0xe0, 0x84, \ + 0xe1, 0x20, 0x1e, 0x29, \ + 0x1e, 0x39, 0xc2, 0x07, \ + 0x94, 0x03, 0xe1, 0x30, \ + 0xec, 0x08, 0x16, 0x10, \ + 0x00, 0x00, 0x91, 0x0e, \ + 0xc2, 0x27, 0xec, 0x08, \ + 0xc2, 0x37, 0xd5, 0x0d, \ + 0x50, 0xa2, 0x94, 0x23, \ + 0x8f, 0x2e, 0xf0, 0x8c, \ + 0xec, 0x08, 0x02, 0x20, \ + 0x80, 0x55, 0x95, 0xbe, \ + 0xd5, 0x1d, 0x95, 0x0e, \ + 0xec, 0x08, 0x00, 0x80, \ + 0x80, 0x55, 0x93, 0xbe, \ + 0xd5, 0x0d, 0x93, 0x1e, \ + 0x80, 0x55, 0x96, 0x6b, \ + 0x89, 0x04, 0x80, 0x6b, \ + 0x8b, 0x04, 0x81, 0x4b, \ + 0xe0, 0x00, 0x48, 0x1a, \ + 0xfe, 0x0e, 0xa1, 0x04, \ + 0xed, 0x84, 0x40, 0x0a, \ + 0xe0, 0x2c, 0x5e, 0x07, \ + 0xbe, 0x0e, 0x9d, 0x04, \ + 0xbc, 0x0e, 0x9f, 0x04, \ + 0x8d, 0x0e, 0x81, 0x01, \ + 0xe4, 0xd0, 0x06, 0xf0, \ + 0x89, 0x14, 0x8b, 0x04, \ + 0xe0, 0x00, 0x42, 0x2a, \ + 0x8c, 0x1b, 0xfe, 0x04, \ + 0x77, 0x78, 0x90, 0x2b, \ + 0xa1, 0x0b, 0xef, 0xfc, \ + 0x8d, 0x0e, 0x80, 0x45, \ + 0x40, 0x01, 0x00, 0x98, \ + 0x81, 0x01, 0xc0, 0x00, \ + 0x1e, 0x07, 0xad, 0x0c, \ + 0x82, 0xe5, 0xe0, 0x2c, \ + 0x06, 0xf0, 0x83, 0x15, \ + 0xbe, 0x04, 0xe4, 0xb0, \ + 0x91, 0x24, 0x95, 0x14, \ + 0xef, 0xfc, 0x76, 0x48, \ + 0x80, 0x45, 0xa1, 0x0b, \ + 0x1e, 0x07, 0x8d, 0x0e, \ + 0x80, 0x95, 0xe0, 0x2c, \ + 0xb1, 0x0c, 0x81, 0x01, \ + 0x06, 0xf0, 0x81, 0xb5, \ + 0xbc, 0x04, 0xe4, 0xc0, \ + 0x8f, 0x24, 0x93, 0x14, \ + 0xef, 0xfc, 0x75, 0x48, \ + 0x80, 0x45, 0xa1, 0x0b, \ + 0xb5, 0x0a, 0x8d, 0x0e, \ + 0xb1, 0x2a, 0xad, 0x1a, \ + 0xfa, 0x84, 0x40, 0x02, \ + 0xfa, 0x80, 0x40, 0x22, \ + 0x90, 0x0b, 0xdd, 0x0c, \ + 0x96, 0x2b, 0x94, 0x1b, \ + 0xef, 0xfc, 0x75, 0x88, \ + 0x8f, 0x04, 0x80, 0x8b, \ + 0x96, 0x2b, 0x94, 0x1b, \ + 0xef, 0xfc, 0x75, 0x28, \ + 0x91, 0x04, 0x8f, 0x0e, \ + 0x96, 0x2b, 0x94, 0x1b, \ + 0xef, 0xfc, 0x74, 0xc8, \ + 0x8c, 0x0b, 0x91, 0x0e, \ + 0x96, 0x2b, 0x94, 0x1b, \ + 0xef, 0xfc, 0x74, 0x68, \ + 0x93, 0x04, 0x80, 0x6b, \ + 0x96, 0x2b, 0x94, 0x1b, \ + 0xef, 0xfc, 0x74, 0x08, \ + 0x95, 0x04, 0x93, 0x0e, \ + 0x96, 0x2b, 0x94, 0x1b, \ + 0xef, 0xfc, 0x73, 0xa8, \ + 0x12, 0x90, 0x95, 0x0e, \ + 0xa1, 0x9b, 0xec, 0x04, \ + 0x81, 0x35, 0xe8, 0x15, \ + 0x8d, 0x0e, 0x81, 0x01, \ + 0x1e, 0xf0, 0x8d, 0x04, \ + 0x0c, 0x0c, 0xe5, 0x40, \ + 0xdd, 0x93, 0xf6, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xf1, 0x88, 0x44, 0x12, \ + 0xe5, 0xd5, 0x82, 0x33, \ + 0x84, 0x01, 0xc2, 0x07, \ + 0xfa, 0x80, 0x00, 0x04, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x00, 0x00, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0xe8, 0x00, 0x42, 0xc8, \ + 0x00, 0x24, 0x80, 0x2b, \ + 0x02, 0x06, 0xe1, 0x84, \ + 0x81, 0x31, 0xe0, 0x10, \ + 0xef, 0xcc, 0x4d, 0xe8, \ + 0x08, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x80, 0x2b, 0x84, 0x6b, \ + 0x82, 0xe5, 0xa1, 0x2b, \ + 0xe0, 0xc8, 0x00, 0x14, \ + 0x06, 0x24, 0xdd, 0x17, \ + 0x81, 0x71, 0xe1, 0x84, \ + 0x42, 0x12, 0x86, 0x03, \ + 0x81, 0x31, 0xf2, 0x9c, \ + 0xef, 0xcc, 0x4c, 0x48, \ + 0x81, 0x25, 0x98, 0x0e, \ + 0x8e, 0x0b, 0xff, 0x71, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x04, \ + 0xe5, 0x06, 0x44, 0x0a, \ + 0xe5, 0x86, 0x40, 0x0a, \ + 0x04, 0x20, 0x81, 0x11, \ + 0xc2, 0x00, 0x64, 0x01, \ + 0xf2, 0x84, 0x40, 0x02, \ + 0xe5, 0x27, 0xdc, 0x07, \ + 0xdd, 0x93, 0xa4, 0x0f, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x7b, 0x58, 0x82, 0x6b, \ + 0x81, 0x11, 0xef, 0xfc, \ + 0x8c, 0x2b, 0x81, 0x31, \ + 0xef, 0xcc, 0x53, 0x08, \ + 0xe1, 0xa0, 0x04, 0x04, \ + 0xa1, 0x2b, 0xa2, 0x2f, \ + 0xff, 0x01, 0x80, 0x45, \ + 0xfa, 0x80, 0x00, 0x04, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x7a, 0x58, 0x84, 0x6b, \ + 0x02, 0x64, 0xef, 0xfc, \ + 0x82, 0x03, 0xe1, 0x84, \ + 0xe0, 0x1a, 0x40, 0x0a, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0x69, 0x29, 0x43, 0x86, \ + 0x44, 0x1a, 0xc0, 0x01, \ + 0x40, 0x1a, 0xe5, 0x02, \ + 0x81, 0x11, 0xe5, 0x82, \ + 0x64, 0x01, 0x04, 0x20, \ + 0x40, 0x02, 0xc2, 0x00, \ + 0xdc, 0x07, 0xf2, 0x84, \ + 0xa4, 0x0f, 0xe5, 0x27, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0x04, 0x34, 0x83, 0x31, \ + 0xa3, 0x3f, 0xe8, 0x88, \ + 0x42, 0x2a, 0x82, 0x21, \ + 0x80, 0x13, 0xe6, 0x8e, \ + 0xfb, 0x80, 0x00, 0x14, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x84, 0x7b, 0x86, 0x6b, \ + 0xef, 0xfc, 0x77, 0x48, \ + 0x8c, 0x0b, 0x80, 0x8b, \ + 0xef, 0xfc, 0x76, 0x88, \ + 0xe6, 0x9e, 0x40, 0x0a, \ + 0xe5, 0x22, 0x42, 0x0a, \ + 0xe5, 0xa2, 0x40, 0x0a, \ + 0x02, 0x10, 0x81, 0x21, \ + 0xc0, 0x08, 0x64, 0x01, \ + 0xf2, 0x88, 0x40, 0x02, \ + 0xd9, 0x17, 0xe8, 0x07, \ + 0x0c, 0x0c, 0xa2, 0x0f, \ + 0xdd, 0x93, 0xf2, 0x0c, \ + 0x84, 0xc5, 0xa1, 0x0b, \ + 0xec, 0x04, 0x00, 0x00, \ + 0x64, 0x01, 0x00, 0x8a, \ + 0x81, 0x11, 0xc0, 0x01, \ + 0x81, 0x11, 0x82, 0x35, \ + 0x67, 0xfd, 0x01, 0x0a, \ + 0xc3, 0x07, 0xc0, 0x01, \ + 0xfe, 0x75, 0x82, 0x11, \ + 0x00, 0x04, 0xd0, 0x17, \ + 0x80, 0x13, 0xfa, 0x80, \ + 0xfb, 0x80, 0x00, 0x14, \ + 0x81, 0x11, 0xdd, 0x93, \ + 0xfb, 0x80, 0x00, 0x14, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x80, 0x02, 0x04, \ + 0xa1, 0x1f, 0xd1, 0x07, \ + 0xe4, 0x04, 0x00, 0x10, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x00, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0xe5, 0x02, 0x48, 0x2a, \ + 0xe5, 0x82, 0x4a, 0x2a, \ + 0xe5, 0x06, 0x40, 0x3a, \ + 0xe5, 0x86, 0x42, 0x3a, \ + 0x8a, 0x3b, 0x88, 0x2b, \ + 0xef, 0xcc, 0x4a, 0x28, \ + 0x60, 0x01, 0x04, 0x06, \ + 0x81, 0x31, 0xc2, 0x00, \ + 0x42, 0x12, 0x84, 0x03, \ + 0x04, 0x14, 0xf2, 0x8c, \ + 0xf9, 0x07, 0xe0, 0x90, \ + 0xff, 0x21, 0xa4, 0x0f, \ + 0xf9, 0x17, 0x85, 0x0b, \ + 0xff, 0x01, 0x80, 0x55, \ + 0x80, 0x45, 0xa1, 0x1b, \ + 0x88, 0xf1, 0xff, 0x01, \ + 0xf2, 0x00, 0x08, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x80, 0x4b, 0x86, 0x6b, \ + 0xff, 0x31, 0xa1, 0x1b, \ + 0x8a, 0x25, 0x81, 0x01, \ + 0x88, 0xe5, 0xa1, 0x2b, \ + 0x7e, 0x81, 0x00, 0x0e, \ + 0x4a, 0x4a, 0xc0, 0x00, \ + 0x40, 0x4a, 0xe5, 0x02, \ + 0x18, 0x54, 0xe5, 0x82, \ + 0xd6, 0x07, 0xe1, 0xd4, \ + 0xe5, 0x0a, 0x48, 0x1a, \ + 0xe5, 0x8a, 0x46, 0x1a, \ + 0x00, 0x34, 0xa0, 0xcf, \ + 0x02, 0x44, 0xe0, 0xfc, \ + 0xd6, 0x57, 0xe1, 0x84, \ + 0x04, 0x34, 0xa0, 0x1f, \ + 0x40, 0x12, 0xe1, 0x84, \ + 0x42, 0x22, 0xf0, 0x94, \ + 0x88, 0x2b, 0xf2, 0xb0, \ + 0xef, 0xc8, 0x7d, 0x48, \ + 0x85, 0x0b, 0xff, 0x21, \ + 0xff, 0x01, 0x80, 0x55, \ + 0x80, 0x45, 0xa1, 0x1b, \ + 0x81, 0x31, 0xff, 0x01, \ + 0x86, 0x0b, 0x98, 0x0e, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe0, 0x00, 0x00, 0x02, \ + 0x7f, 0xfd, 0x47, 0xfe, \ + 0xa1, 0x1b, 0xc0, 0x3f, \ + 0xfb, 0x0c, 0x40, 0x02, \ + 0x82, 0x45, 0x88, 0x0e, \ + 0x06, 0x14, 0xce, 0x07, \ + 0x86, 0x03, 0xe1, 0x84, \ + 0xe0, 0x06, 0x42, 0x0a, \ + 0x88, 0x1e, 0x81, 0x01, \ + 0xff, 0x01, 0xdd, 0x93, \ + 0x88, 0x1e, 0x81, 0x11, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfe, 0x00, 0x00, 0x0c, \ + 0x88, 0xe5, 0xa1, 0x2b, \ + 0xf0, 0x84, 0x4a, 0x32, \ + 0xf4, 0x80, 0x0a, 0x8a, \ + 0xe0, 0x00, 0x06, 0x02, \ + 0xfa, 0x80, 0x08, 0x54, \ + 0x81, 0x01, 0x81, 0x11, \ + 0x00, 0x34, 0x80, 0x65, \ + 0x0a, 0x9a, 0xe8, 0x90, \ + 0x00, 0x48, 0xf5, 0x00, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0x0a, 0x2a, 0x81, 0x01, \ + 0x81, 0x51, 0xf0, 0x00, \ + 0x40, 0x01, 0x01, 0x38, \ + 0x84, 0x65, 0xc0, 0x00, \ + 0x20, 0x01, 0x00, 0x48, \ + 0x81, 0x51, 0xc0, 0x00, \ + 0xe1, 0x84, 0x06, 0x24, \ + 0x42, 0x52, 0x86, 0x03, \ + 0x81, 0x31, 0xf2, 0x84, \ + 0xef, 0xc8, 0x78, 0x28, \ + 0xf6, 0x00, 0x08, 0x0c, \ + 0x0a, 0x34, 0xdd, 0x93, \ + 0xfa, 0xf5, 0xef, 0x90, \ + 0xec, 0x80, 0x08, 0x40, \ + 0xe8, 0x90, 0x0a, 0x34, \ + 0xbf, 0xfc, 0x3e, 0xd8, \ + 0xc1, 0xc3, 0xfd, 0xc5, \ + 0xfa, 0x00, 0x00, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0xa1, 0x2b, 0x80, 0x3b, \ + 0xe5, 0x06, 0x40, 0x3a, \ + 0xe5, 0x86, 0x42, 0x3a, \ + 0x06, 0x20, 0x82, 0x65, \ + 0x83, 0x41, 0xec, 0x04, \ + 0x81, 0x31, 0xa7, 0x4f, \ + 0x42, 0x12, 0x88, 0x03, \ + 0x44, 0x58, 0xf2, 0x8c, \ + 0xff, 0x21, 0xef, 0xcc, \ + 0x80, 0x55, 0x85, 0x0b, \ + 0xa1, 0x1b, 0xff, 0x01, \ + 0xff, 0x01, 0x80, 0x45, \ + 0x08, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0xa3, 0x2d, 0x80, 0x3b, \ + 0xe4, 0x06, 0x40, 0x3a, \ + 0xe4, 0x86, 0x42, 0x3a, \ + 0x08, 0x20, 0x86, 0x75, \ + 0x83, 0x31, 0xec, 0x04, \ + 0xe8, 0x90, 0x08, 0x34, \ + 0xa1, 0x1d, 0x81, 0xc1, \ + 0xe1, 0x7c, 0x0a, 0x44, \ + 0xf1, 0x90, 0x4c, 0xc2, \ + 0xf3, 0x94, 0x58, 0xc2, \ + 0x8c, 0x4b, 0x80, 0x45, \ + 0x98, 0x5b, 0x80, 0x45, \ + 0x04, 0x34, 0x88, 0x03, \ + 0x42, 0x52, 0xe8, 0x88, \ + 0x06, 0x24, 0xf2, 0x84, \ + 0x68, 0xb8, 0xe1, 0x7c, \ + 0xa1, 0x1b, 0xef, 0xc8, \ + 0x03, 0x4a, 0x82, 0xc5, \ + 0x82, 0xc5, 0xe0, 0x00, \ + 0x85, 0x1b, 0xff, 0x21, \ + 0x60, 0x01, 0x06, 0x06, \ + 0x82, 0xc5, 0xe0, 0x00, \ + 0xe0, 0x08, 0x43, 0x4a, \ + 0x83, 0x05, 0x82, 0xd5, \ + 0xe0, 0x00, 0x00, 0xca, \ + 0x41, 0xfe, 0xfc, 0x95, \ + 0xdf, 0xff, 0x7f, 0xfd, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x41, 0x4a, 0xdd, 0x93, \ + 0x80, 0x85, 0xe4, 0x0c, \ + 0x60, 0x01, 0x00, 0x06, \ + 0x0c, 0x0c, 0xe0, 0x00, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0x86, 0x0e, 0x83, 0x01, \ + 0xe0, 0x14, 0x42, 0x05, \ + 0x46, 0x03, 0x81, 0x01, \ + 0x44, 0x03, 0xe0, 0x28, \ + 0x91, 0x11, 0xe0, 0x88, \ + 0xe6, 0x20, 0x04, 0x20, \ + 0xec, 0x04, 0x02, 0x10, \ + 0x44, 0x05, 0xa1, 0x1b, \ + 0x44, 0x05, 0xf8, 0x3c, \ + 0x8a, 0x0e, 0xf8, 0x40, \ + 0xf0, 0x08, 0x44, 0x05, \ + 0xdd, 0x93, 0xfc, 0xd5, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x88, 0xab, 0x8d, 0x5e, \ + 0x84, 0x7b, 0x89, 0x3e, \ + 0x80, 0x8b, 0x82, 0x9b, \ + 0x9f, 0x64, 0x81, 0xb1, \ + 0xe4, 0xbe, 0xa6, 0xbe, \ + 0xe6, 0xbe, 0xa4, 0xbe, \ + 0xe0, 0x14, 0x52, 0xb5, \ + 0xe0, 0x18, 0x52, 0xb5, \ + 0xe0, 0x10, 0x52, 0xb5, \ + 0xe0, 0x1c, 0x52, 0xb5, \ + 0x7c, 0xf8, 0x8c, 0x3b, \ + 0x4e, 0xb1, 0xef, 0xfc, \ + 0x87, 0x01, 0xe1, 0x18, \ + 0xe1, 0x1c, 0x4e, 0xb1, \ + 0x0e, 0x49, 0xa6, 0x0e, \ + 0x8b, 0xae, 0xe3, 0xc4, \ + 0xe0, 0x8c, 0x14, 0x51, \ + 0xe0, 0x14, 0x52, 0x05, \ + 0xe7, 0xe0, 0x04, 0x70, \ + 0xe4, 0x30, 0x14, 0x60, \ + 0x64, 0xe1, 0x06, 0x70, \ + 0xfc, 0xae, 0xc0, 0x01, \ + 0xbc, 0xbe, 0xbe, 0xbe, \ + 0x96, 0x0b, 0x90, 0xbb, \ + 0x66, 0xb8, 0x92, 0x1b, \ + 0xa1, 0x0b, 0xef, 0xf8, \ + 0x00, 0x06, 0x82, 0x05, \ + 0xa8, 0x0e, 0xe0, 0x08, \ + 0x0c, 0x0c, 0xb8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0x8b, 0x44, 0x89, 0x34, \ + 0x0e, 0x09, 0x8d, 0x84, \ + 0xa5, 0x0b, 0xe0, 0x2c, \ + 0x86, 0xc5, 0x9d, 0xa4, \ + 0x90, 0x7b, 0xa3, 0x0b, \ + 0x88, 0x8b, 0xfc, 0x55, \ + 0xe4, 0x90, 0x0c, 0x80, \ + 0xe0, 0x10, 0x04, 0x06, \ + 0x8c, 0x1b, 0x8e, 0x0b, \ + 0xef, 0xc8, 0x7f, 0x28, \ + 0x64, 0x01, 0x00, 0x70, \ + 0x04, 0x06, 0xc0, 0x01, \ + 0x8c, 0x1b, 0xe0, 0x10, \ + 0xef, 0xc8, 0x7e, 0xa8, \ + 0xe0, 0x1c, 0x04, 0x46, \ + 0x90, 0x1b, 0x94, 0x0b, \ + 0x0c, 0x0c, 0xb8, 0xf1, \ + 0x3e, 0x18, 0xf2, 0x7c, \ + 0x96, 0x0b, 0xff, 0xc8, \ + 0x8e, 0x2b, 0x92, 0x1b, \ + 0x4b, 0xb8, 0x90, 0x5b, \ + 0xa4, 0x04, 0xe8, 0x00, \ + 0xf2, 0xf5, 0xa1, 0x0b, \ + 0x92, 0x1b, 0x96, 0x0b, \ + 0x4f, 0xf8, 0x90, 0x2b, \ + 0xa4, 0x04, 0xe8, 0x00, \ + 0xf0, 0xf5, 0xa1, 0x0b, \ + 0x02, 0x06, 0x83, 0x01, \ + 0x4c, 0x01, 0xe0, 0x08, \ + 0x4c, 0x15, 0xe0, 0x64, \ + 0x96, 0x0b, 0xe0, 0x10, \ + 0x8e, 0x2b, 0x92, 0x1b, \ + 0x90, 0x4b, 0x8c, 0x3b, \ + 0xb8, 0xf1, 0x94, 0x5b, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xe0, 0x04, 0x11, 0xe8, \ + 0xe0, 0x0c, 0x41, 0xf6, \ + 0x42, 0x05, 0x86, 0x0e, \ + 0x04, 0x05, 0xe0, 0x14, \ + 0x04, 0x25, 0xe0, 0x34, \ + 0xc4, 0x0d, 0xe0, 0x3c, \ + 0x40, 0x22, 0x82, 0x95, \ + 0xa1, 0x0b, 0xf1, 0x80, \ + 0x01, 0x0a, 0x82, 0x65, \ + 0x80, 0x95, 0xe7, 0x30, \ + 0x42, 0x05, 0x87, 0x01, \ + 0xdd, 0x93, 0xe0, 0x10, \ + 0xa1, 0x0b, 0xa4, 0x03, \ + 0xff, 0x01, 0xfc, 0xf5, \ + 0xdd, 0x93, 0x84, 0x0e, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0x04, 0x49, 0x88, 0x6b, \ + 0x0a, 0x06, 0xe3, 0xc0, \ + 0xe5, 0x4d, 0xe0, 0x08, \ + 0xfe, 0x5e, 0x85, 0x41, \ + 0x42, 0x45, 0x86, 0x4e, \ + 0x4c, 0x55, 0xe0, 0x14, \ + 0x88, 0x55, 0xe0, 0x10, \ + 0x86, 0x4e, 0x87, 0x41, \ + 0xe0, 0x14, 0x42, 0x45, \ + 0xe3, 0xc4, 0x04, 0x49, \ + 0x18, 0x20, 0xce, 0x50, \ + 0x06, 0x20, 0xe7, 0xe0, \ + 0xc0, 0x01, 0x64, 0xe1, \ + 0xe4, 0x30, 0x04, 0xf0, \ + 0x81, 0x21, 0xfc, 0x2e, \ + 0xbc, 0x2e, 0xbe, 0x2e, \ + 0x5d, 0x78, 0x98, 0x2b, \ + 0xa1, 0x0b, 0xef, 0xf8, \ + 0xda, 0x04, 0x82, 0x75, \ + 0xce, 0x17, 0xfe, 0x14, \ + 0xe1, 0x04, 0x04, 0x04, \ + 0x40, 0x1a, 0x84, 0x13, \ + 0x4c, 0x05, 0xe0, 0x82, \ + 0xa0, 0xf1, 0xe0, 0x10, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x0c, 0x0b, 0x86, 0x6b, \ + 0xa1, 0x0b, 0xe0, 0x28, \ + 0x86, 0x3e, 0x9f, 0x31, \ + 0xe0, 0x14, 0x42, 0x35, \ + 0xa3, 0x0b, 0x84, 0xc5, \ + 0x91, 0x01, 0x84, 0xf5, \ + 0xe5, 0x20, 0x02, 0x20, \ + 0x65, 0xe1, 0x06, 0x20, \ + 0x04, 0x20, 0xc0, 0x01, \ + 0xc0, 0x01, 0x66, 0x61, \ + 0x02, 0x55, 0x83, 0x41, \ + 0x00, 0x00, 0xf0, 0x08, \ + 0xa1, 0x0b, 0xec, 0x04, \ + 0xc9, 0x4c, 0xcd, 0x5e, \ + 0x85, 0x35, 0xfe, 0x35, \ + 0x84, 0x0e, 0xff, 0x01, \ + 0x91, 0x71, 0x85, 0x05, \ + 0x66, 0x61, 0x10, 0x20, \ + 0x12, 0x20, 0xc0, 0x01, \ + 0xc0, 0x01, 0x65, 0xe1, \ + 0xe5, 0x20, 0x14, 0x20, \ + 0xaa, 0x14, 0x83, 0xb1, \ + 0x40, 0x42, 0xa8, 0x44, \ + 0xa1, 0x0b, 0xf9, 0x84, \ + 0x81, 0x01, 0xa0, 0xbc, \ + 0x0c, 0x2b, 0x80, 0xe5, \ + 0x81, 0x31, 0xe0, 0x28, \ + 0x5d, 0xf8, 0x88, 0x0b, \ + 0x0e, 0x70, 0xef, 0xc8, \ + 0xa1, 0x7b, 0xec, 0x04, \ + 0x84, 0x81, 0xe5, 0x0e, \ + 0xfa, 0x95, 0x90, 0xa1, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x90, 0x1e, 0xf0, \ + 0x82, 0x7b, 0x8a, 0x6b, \ + 0x06, 0x05, 0xfe, 0x0e, \ + 0x06, 0x15, 0xe0, 0x3c, \ + 0x04, 0x35, 0xe0, 0x34, \ + 0x04, 0x25, 0xe1, 0x90, \ + 0x8f, 0x0e, 0xe1, 0x88, \ + 0xa0, 0x33, 0x91, 0x1e, \ + 0x8b, 0x3e, 0xa2, 0x23, \ + 0x14, 0x40, 0x8d, 0x2e, \ + 0x81, 0xb1, 0xe4, 0x90, \ + 0xa8, 0x94, 0x87, 0x81, \ + 0x4e, 0x85, 0x9e, 0x8e, \ + 0x00, 0xf0, 0xe0, 0x14, \ + 0xfc, 0x0e, 0xe4, 0x40, \ + 0xbe, 0x0e, 0x83, 0x01, \ + 0xe4, 0x70, 0x04, 0xf0, \ + 0xe4, 0x50, 0x06, 0xf0, \ + 0xbc, 0x0e, 0xff, 0x01, \ + 0xfe, 0x04, 0x85, 0x41, \ + 0x92, 0x5b, 0x8e, 0x1b, \ + 0xef, 0xf8, 0x55, 0x08, \ + 0xa1, 0x0b, 0x9c, 0x04, \ + 0x89, 0x04, 0x86, 0x15, \ + 0xa1, 0x9d, 0x80, 0x93, \ + 0x00, 0x06, 0x82, 0xb5, \ + 0xd0, 0x00, 0x60, 0x01, \ + 0x82, 0x35, 0x81, 0x9d, \ + 0x4e, 0x05, 0x83, 0x01, \ + 0x53, 0xfe, 0xe0, 0x10, \ + 0xcf, 0xff, 0x7f, 0xfd, \ + 0x81, 0x91, 0x81, 0x25, \ + 0x16, 0x8a, 0x82, 0xb1, \ + 0xd9, 0x9e, 0xe5, 0x00, \ + 0xf4, 0x55, 0x88, 0xa1, \ + 0x0c, 0x0c, 0xc8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xa1, 0x21, 0x84, 0x3b, \ + 0x00, 0x30, 0x86, 0x2e, \ + 0xc0, 0x01, 0x64, 0x01, \ + 0xe0, 0x14, 0x42, 0x25, \ + 0xe0, 0x10, 0x04, 0x06, \ + 0x2c, 0x98, 0x86, 0x1b, \ + 0x00, 0x0c, 0xff, 0xc8, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x82, 0x7b, 0x88, 0x6b, \ + 0x8f, 0x01, 0x80, 0x8b, \ + 0x4e, 0x05, 0x9e, 0x0e, \ + 0x89, 0x91, 0xe0, 0x14, \ + 0xe6, 0x50, 0x14, 0x30, \ + 0xe6, 0xa0, 0x16, 0x20, \ + 0xbe, 0x0e, 0x81, 0x01, \ + 0xf8, 0x40, 0x14, 0x25, \ + 0x8a, 0xa5, 0xa1, 0x2b, \ + 0xe0, 0x10, 0x0c, 0x45, \ + 0xe4, 0x40, 0x0a, 0xf0, \ + 0x90, 0x0b, 0x81, 0x31, \ + 0x46, 0x38, 0x8e, 0x1b, \ + 0x9c, 0x04, 0xe8, 0x00, \ + 0x96, 0x55, 0xa1, 0x0b, \ + 0xf8, 0x3c, 0x16, 0x05, \ + 0xc0, 0x4b, 0x8b, 0x14, \ + 0x16, 0x25, 0x89, 0x1b, \ + 0x89, 0x34, 0xf8, 0x40, \ + 0xc8, 0x1d, 0x82, 0x65, \ + 0x84, 0x33, 0x82, 0x95, \ + 0xf2, 0x80, 0x40, 0x12, \ + 0xf8, 0x3c, 0x56, 0x05, \ + 0xf8, 0x40, 0x56, 0x35, \ + 0xc4, 0x4b, 0x81, 0xe5, \ + 0xfc, 0xc5, 0xc8, 0x3d, \ + 0x56, 0x05, 0xff, 0x01, \ + 0x83, 0x11, 0xf8, 0x3c, \ + 0xf8, 0x40, 0x56, 0x05, \ + 0xbe, 0x0e, 0x83, 0x01, \ + 0xe0, 0x10, 0x4e, 0x15, \ + 0xa1, 0x2b, 0xa8, 0x24, \ + 0x0c, 0x45, 0x8a, 0x25, \ + 0x0a, 0xf0, 0xe0, 0x10, \ + 0x81, 0x31, 0xe4, 0x20, \ + 0x8e, 0x1b, 0x90, 0x0b, \ + 0xe8, 0x00, 0x43, 0x48, \ + 0xa1, 0x0b, 0x9c, 0x04, \ + 0xfc, 0x04, 0x8a, 0x75, \ + 0x80, 0x2b, 0xfe, 0x14, \ + 0x5a, 0xf8, 0x82, 0x3b, \ + 0xae, 0x24, 0xef, 0xc8, \ + 0x89, 0x1b, 0xc4, 0x4b, \ + 0x82, 0x25, 0xac, 0x34, \ + 0x82, 0x55, 0xc8, 0x1d, \ + 0x40, 0x22, 0x80, 0x33, \ + 0xae, 0x0e, 0xf2, 0x84, \ + 0x81, 0xa5, 0xac, 0x3e, \ + 0xc8, 0x0d, 0xc6, 0x4b, \ + 0xff, 0x01, 0xfe, 0x05, \ + 0xac, 0x0e, 0xae, 0x0e, \ + 0x4e, 0x05, 0x83, 0x01, \ + 0x82, 0x91, 0xe0, 0x10, \ + 0x88, 0xa1, 0xf1, 0x9d, \ + 0xe8, 0x35, 0x90, 0xb1, \ + 0x00, 0x4a, 0xbe, 0x04, \ + 0x80, 0xc5, 0xfb, 0xfc, \ + 0xe0, 0x28, 0x0c, 0x0b, \ + 0x4c, 0x03, 0x82, 0x01, \ + 0xb8, 0xf1, 0xe0, 0x28, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x88, 0x7b, 0x8a, 0x6b, \ + 0x84, 0x8b, 0x86, 0x9b, \ + 0xff, 0x11, 0x82, 0xbb, \ + 0x91, 0x11, 0x82, 0x7d, \ + 0x56, 0x15, 0xae, 0x1e, \ + 0x82, 0x25, 0xe0, 0x14, \ + 0xe2, 0x00, 0x0e, 0x0a, \ + 0xa1, 0x7b, 0xa4, 0x45, \ + 0xff, 0x01, 0x80, 0xf5, \ + 0x91, 0xf5, 0xac, 0x0e, \ + 0xac, 0x0e, 0xfb, 0x01, \ + 0x96, 0x1b, 0x91, 0xc5, \ + 0x92, 0x3b, 0x90, 0x2b, \ + 0xef, 0xf8, 0x45, 0xa8, \ + 0xac, 0x04, 0x80, 0xab, \ + 0xa0, 0x75, 0xa1, 0x0b, \ + 0x84, 0x95, 0xae, 0xad, \ + 0xec, 0x04, 0x00, 0xa0, \ + 0x81, 0x11, 0xa1, 0x0b, \ + 0x02, 0x84, 0x80, 0x65, \ + 0x00, 0x8a, 0xef, 0x80, \ + 0x84, 0xb5, 0xe4, 0x80, \ + 0xec, 0x84, 0x02, 0xa0, \ + 0xe8, 0x84, 0x02, 0x84, \ + 0xe5, 0x00, 0x00, 0x9a, \ + 0xa0, 0x00, 0x01, 0x58, \ + 0x14, 0x8a, 0x83, 0x45, \ + 0x8e, 0x95, 0xe0, 0xa0, \ + 0x00, 0x74, 0xce, 0x87, \ + 0x80, 0x83, 0xe1, 0x84, \ + 0xe0, 0x1e, 0x50, 0x8a, \ + 0x8d, 0x15, 0x81, 0x91, \ + 0xb2, 0x1f, 0xa1, 0x9f, \ + 0xe5, 0x00, 0x00, 0x9a, \ + 0x20, 0x01, 0x00, 0x48, \ + 0x81, 0x11, 0xc0, 0x00, \ + 0x90, 0x95, 0x81, 0x21, \ + 0x20, 0x01, 0x00, 0x48, \ + 0x81, 0x21, 0xc0, 0x00, \ + 0x60, 0x01, 0x00, 0x06, \ + 0x8e, 0x0f, 0xe0, 0x00, \ + 0xe1, 0x84, 0x08, 0x74, \ + 0xe1, 0x7c, 0x06, 0x74, \ + 0x0a, 0x34, 0xa0, 0x4f, \ + 0x40, 0x22, 0xe1, 0x84, \ + 0x42, 0x12, 0xf0, 0x90, \ + 0x8e, 0x2b, 0xf2, 0x94, \ + 0xef, 0xc8, 0x49, 0x48, \ + 0xb4, 0x23, 0x91, 0x21, \ + 0x81, 0x91, 0xa1, 0x2b, \ + 0x12, 0x04, 0x80, 0x65, \ + 0x04, 0x8a, 0xef, 0x88, \ + 0x8a, 0x35, 0xe4, 0x80, \ + 0xb4, 0x13, 0xd1, 0x11, \ + 0xe8, 0x84, 0x12, 0x04, \ + 0xe5, 0x00, 0x04, 0x9a, \ + 0xa0, 0x00, 0x02, 0x98, \ + 0x00, 0x84, 0x85, 0x85, \ + 0xce, 0x97, 0xe1, 0xe4, \ + 0xce, 0x87, 0xa0, 0x9f, \ + 0x60, 0x01, 0x00, 0x06, \ + 0x8e, 0x0f, 0xe0, 0x00, \ + 0xe1, 0x84, 0x02, 0x74, \ + 0xe1, 0x7c, 0x06, 0x74, \ + 0x04, 0x34, 0xa0, 0x1f, \ + 0x40, 0x12, 0xe1, 0x84, \ + 0x42, 0x22, 0xf0, 0xa0, \ + 0x8e, 0x2b, 0xf2, 0xa4, \ + 0xef, 0xc8, 0x46, 0xa8, \ + 0x82, 0x9b, 0x80, 0x8b, \ + 0x04, 0x84, 0x83, 0x45, \ + 0x3b, 0xe8, 0xe8, 0x80, \ + 0xf7, 0xd5, 0xbf, 0xfc, \ + 0xa2, 0x9f, 0xa5, 0x1f, \ + 0xe5, 0x00, 0x04, 0x9a, \ + 0x20, 0x01, 0x00, 0x48, \ + 0x81, 0x91, 0xc0, 0x00, \ + 0x82, 0x75, 0x81, 0x81, \ + 0x20, 0x01, 0x00, 0x48, \ + 0x81, 0x81, 0xc0, 0x00, \ + 0x9a, 0x9e, 0x98, 0x8e, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe8, 0x88, 0x10, 0x04, \ + 0xbf, 0xfc, 0x3f, 0x78, \ + 0xc1, 0xc3, 0xff, 0x65, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x88, 0xab, 0x8a, 0x6b, \ + 0x84, 0x9b, 0x86, 0x7b, \ + 0x80, 0xbb, 0x82, 0x8b, \ + 0x80, 0xad, 0xff, 0x01, \ + 0xa2, 0x0e, 0x93, 0x01, \ + 0xe0, 0x14, 0x50, 0x05, \ + 0x14, 0x0a, 0x80, 0xe5, \ + 0x80, 0xf5, 0xe2, 0x00, \ + 0x9a, 0x7e, 0x98, 0x9e, \ + 0xfb, 0x01, 0x87, 0x55, \ + 0x87, 0x25, 0xa0, 0x0e, \ + 0x90, 0x1b, 0x96, 0x0b, \ + 0x8e, 0x3b, 0x92, 0x2b, \ + 0xef, 0xf4, 0x7a, 0xa8, \ + 0x02, 0xa4, 0xbc, 0x0e, \ + 0x96, 0x0b, 0xe1, 0x7c, \ + 0x90, 0x1b, 0x82, 0xbb, \ + 0x96, 0x3b, 0x94, 0x2b, \ + 0xef, 0xf4, 0x78, 0xc8, \ + 0xa1, 0x1b, 0xa0, 0x14, \ + 0xbc, 0x14, 0x86, 0xf5, \ + 0x01, 0x0a, 0x82, 0x03, \ + 0x82, 0x15, 0xe0, 0xfc, \ + 0x9a, 0x0e, 0xff, 0x01, \ + 0x98, 0x0e, 0x83, 0x11, \ + 0xe0, 0x10, 0x50, 0x15, \ + 0x94, 0x0b, 0x83, 0x35, \ + 0x92, 0x2b, 0x96, 0x1b, \ + 0x49, 0x78, 0x8e, 0x3b, \ + 0x81, 0x21, 0xef, 0xc8, \ + 0xe5, 0x00, 0x00, 0x00, \ + 0xf2, 0x88, 0x42, 0x12, \ + 0xe0, 0xe4, 0x04, 0x14, \ + 0xa4, 0x0f, 0xcf, 0x07, \ + 0x9a, 0x1e, 0xcf, 0x17, \ + 0x88, 0xf1, 0x98, 0x0e, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xed, 0xd0, 0x1e, 0xf0, \ + 0x82, 0x8b, 0x84, 0x6b, \ + 0xa2, 0x1e, 0x95, 0x11, \ + 0xe0, 0x14, 0x50, 0x15, \ + 0xe0, 0x28, 0x06, 0x1b, \ + 0x84, 0x05, 0xa1, 0x1b, \ + 0xbc, 0x3e, 0xa3, 0x1b, \ + 0xfc, 0x5e, 0xfe, 0x0e, \ + 0x84, 0xd5, 0xbe, 0x4e, \ + 0xe6, 0x20, 0x02, 0x60, \ + 0xe4, 0xc0, 0x00, 0xf0, \ + 0xe0, 0x04, 0x04, 0x06, \ + 0xef, 0xc8, 0x52, 0xa8, \ + 0x81, 0x01, 0x85, 0x95, \ + 0xe0, 0x34, 0x4c, 0x05, \ + 0xe0, 0x30, 0x4c, 0x05, \ + 0xe0, 0x3c, 0x4c, 0x05, \ + 0xe0, 0x38, 0x4c, 0x05, \ + 0xe0, 0x40, 0x4c, 0x05, \ + 0x4e, 0x1a, 0x8f, 0x35, \ + 0x91, 0xa1, 0xe6, 0x86, \ + 0xe6, 0x20, 0x16, 0x60, \ + 0xe4, 0xc0, 0x12, 0xf0, \ + 0xac, 0x04, 0xae, 0x14, \ + 0xf9, 0x84, 0x44, 0x02, \ + 0x80, 0xc5, 0xa1, 0x2b, \ + 0x8e, 0x2b, 0x81, 0x31, \ + 0xef, 0xc4, 0x7b, 0xe8, \ + 0x81, 0x01, 0x81, 0x35, \ + 0x14, 0xa0, 0x81, 0x11, \ + 0xa1, 0xab, 0xec, 0x04, \ + 0x52, 0x05, 0xa6, 0x1e, \ + 0x90, 0xb1, 0xf0, 0x08, \ + 0xbe, 0x14, 0xfa, 0x95, \ + 0xe0, 0x3c, 0x02, 0x05, \ + 0xe0, 0x34, 0x02, 0x15, \ + 0xb3, 0x34, 0xb1, 0x24, \ + 0xab, 0x04, 0x95, 0x0e, \ + 0xa9, 0x14, 0x97, 0x1e, \ + 0x8d, 0x2e, 0x8f, 0x3e, \ + 0x81, 0x71, 0x93, 0x0e, \ + 0x4c, 0x75, 0x91, 0x1e, \ + 0x4c, 0x75, 0xe0, 0x34, \ + 0xfc, 0xa4, 0xe0, 0x30, \ + 0xfe, 0x94, 0xc8, 0xa1, \ + 0x06, 0xf0, 0xa8, 0x24, \ + 0x08, 0xf0, 0xe4, 0xa0, \ + 0x92, 0x0b, 0xe4, 0x60, \ + 0x43, 0xd8, 0x90, 0x1b, \ + 0xa0, 0x24, 0xe8, 0x00, \ + 0x8c, 0x95, 0xa1, 0x2b, \ + 0xe0, 0x34, 0x0c, 0x25, \ + 0x80, 0x85, 0x85, 0x1b, \ + 0x80, 0xd5, 0xc4, 0x1d, \ + 0x0c, 0x25, 0x81, 0x95, \ + 0xc4, 0x0d, 0xe0, 0x30, \ + 0x4c, 0x05, 0x80, 0xa5, \ + 0x4c, 0x15, 0xe0, 0x30, \ + 0x82, 0x71, 0xe0, 0x34, \ + 0xe5, 0x00, 0x0e, 0x8a, \ + 0xf8, 0x35, 0x88, 0xa1, \ + 0xe0, 0x34, 0x0c, 0x35, \ + 0xe0, 0x30, 0x0c, 0x25, \ + 0x90, 0x1b, 0x92, 0x0b, \ + 0xe8, 0x00, 0x4f, 0x68, \ + 0xa0, 0x04, 0x80, 0x2b, \ + 0x84, 0x95, 0xa1, 0x0b, \ + 0x00, 0x45, 0xbc, 0x04, \ + 0x0a, 0xf0, 0xe0, 0x10, \ + 0x81, 0x31, 0xe4, 0x40, \ + 0x90, 0x1b, 0x92, 0x0b, \ + 0xef, 0xfc, 0x72, 0x08, \ + 0xa1, 0x0b, 0xa0, 0x04, \ + 0x89, 0x04, 0x80, 0xf5, \ + 0x4c, 0x05, 0xca, 0x07, \ + 0xbc, 0x14, 0xe0, 0x40, \ + 0x1e, 0xf0, 0x86, 0x0e, \ + 0x0c, 0x0c, 0xe5, 0xd0, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x82, 0x6b, 0x88, 0xab, \ + 0x97, 0x01, 0x80, 0x9b, \ + 0x4c, 0x05, 0x9a, 0x0e, \ + 0x89, 0x3e, 0xe0, 0x14, \ + 0xfe, 0x2e, 0x8c, 0x04, \ + 0x56, 0x0a, 0xa4, 0x03, \ + 0x4e, 0x0a, 0xe4, 0x82, \ + 0x92, 0x0b, 0xe4, 0x02, \ + 0x96, 0x3b, 0x8e, 0x2b, \ + 0xef, 0xf4, 0x6c, 0x28, \ + 0x98, 0x04, 0x80, 0x8b, \ + 0x81, 0x01, 0xa1, 0x0b, \ + 0xac, 0xd5, 0x81, 0x11, \ + 0xe8, 0x24, 0xea, 0x34, \ + 0x8c, 0x1b, 0x92, 0x0b, \ + 0xef, 0xf4, 0x6b, 0x68, \ + 0x98, 0x04, 0x80, 0x2b, \ + 0x81, 0x01, 0xa1, 0x0b, \ + 0xaa, 0x55, 0x81, 0x11, \ + 0x90, 0x23, 0xfc, 0xae, \ + 0xe0, 0xfc, 0x05, 0x0a, \ + 0x83, 0x01, 0x82, 0x15, \ + 0xe0, 0x10, 0x4c, 0x05, \ + 0xbe, 0x0e, 0xff, 0x01, \ + 0x83, 0x45, 0xff, 0xb1, \ + 0xc4, 0x04, 0xfc, 0x14, \ + 0x8e, 0x2b, 0xc6, 0x14, \ + 0x7a, 0x98, 0x96, 0x3b, \ + 0x81, 0x21, 0xef, 0xc4, \ + 0x64, 0x01, 0x00, 0x00, \ + 0x56, 0x12, 0xe0, 0x00, \ + 0x02, 0xb4, 0xf2, 0x88, \ + 0xed, 0x07, 0xe0, 0xa8, \ + 0xbe, 0x0e, 0xa2, 0x0f, \ + 0xfe, 0x14, 0xed, 0xb7, \ + 0x82, 0x04, 0x89, 0x04, \ + 0xf1, 0x80, 0x40, 0x12, \ + 0xe4, 0x82, 0x54, 0x0a, \ + 0xe4, 0x02, 0x4e, 0x0a, \ + 0x8c, 0x1b, 0x92, 0x0b, \ + 0x94, 0x3b, 0x8e, 0x2b, \ + 0xef, 0xf4, 0x68, 0x28, \ + 0x98, 0x04, 0x80, 0x8b, \ + 0x81, 0x01, 0xa1, 0x0b, \ + 0xfc, 0x24, 0x81, 0x11, \ + 0x8a, 0x34, 0x9c, 0xb5, \ + 0x92, 0x0b, 0x88, 0x24, \ + 0x67, 0x58, 0x8c, 0x1b, \ + 0x80, 0x2b, 0xef, 0xf4, \ + 0xa1, 0x0b, 0x98, 0x04, \ + 0x81, 0x11, 0x81, 0x01, \ + 0x90, 0x23, 0x9a, 0x35, \ + 0xe0, 0xfc, 0x05, 0x0a, \ + 0x83, 0x01, 0x80, 0xf5, \ + 0xe0, 0x10, 0x4c, 0x05, \ + 0xff, 0x11, 0xff, 0x01, \ + 0xfc, 0x14, 0x83, 0x35, \ + 0x86, 0x14, 0x84, 0x04, \ + 0x94, 0x3b, 0x8e, 0x2b, \ + 0xef, 0xc4, 0x76, 0xa8, \ + 0x00, 0x00, 0x81, 0x21, \ + 0xe0, 0x00, 0x64, 0x01, \ + 0xf2, 0x88, 0x42, 0x12, \ + 0xe0, 0xa8, 0x04, 0x14, \ + 0xa4, 0x0f, 0xed, 0x07, \ + 0xd6, 0x2b, 0xed, 0x17, \ + 0x80, 0xa5, 0x85, 0x1b, \ + 0xe4, 0x08, 0x43, 0x4a, \ + 0x81, 0xc5, 0x80, 0xe5, \ + 0xc4, 0x2b, 0xbe, 0x24, \ + 0xe4, 0x08, 0x41, 0x4a, \ + 0x83, 0x01, 0x80, 0xf5, \ + 0xe0, 0x10, 0x4c, 0x05, \ + 0x81, 0x11, 0x81, 0x01, \ + 0xbe, 0x24, 0x83, 0x35, \ + 0x42, 0xb2, 0x84, 0x03, \ + 0x04, 0x04, 0xf2, 0x84, \ + 0xc8, 0x17, 0xe1, 0xf0, \ + 0xc8, 0x07, 0xa4, 0x1f, \ + 0x00, 0x00, 0x81, 0x21, \ + 0xc0, 0x08, 0x64, 0x01, \ + 0xff, 0x00, 0x06, 0x0e, \ + 0xf2, 0x88, 0x42, 0x12, \ + 0x89, 0x34, 0x86, 0x0f, \ + 0x8e, 0x34, 0x8c, 0x24, \ + 0xa1, 0x2b, 0xa6, 0x23, \ + 0x04, 0x24, 0x86, 0xe5, \ + 0x46, 0x2a, 0xf0, 0x80, \ + 0x44, 0x2a, 0xe5, 0x0a, \ + 0x81, 0x41, 0xe5, 0x8a, \ + 0xe6, 0x00, 0x06, 0x30, \ + 0xf2, 0x90, 0x48, 0x22, \ + 0xe0, 0xe0, 0x04, 0x44, \ + 0xe1, 0xa0, 0x0a, 0x34, \ + 0xe0, 0xdc, 0x18, 0x44, \ + 0xaa, 0x2f, 0xd3, 0x37, \ + 0x0a, 0x44, 0xb8, 0x3f, \ + 0x86, 0x03, 0xe1, 0xa4, \ + 0xe1, 0xa0, 0x06, 0x44, \ + 0xf2, 0x84, 0x42, 0x52, \ + 0xef, 0xc4, 0x68, 0xa8, \ + 0xff, 0x01, 0x81, 0x55, \ + 0x81, 0x01, 0x98, 0x0e, \ + 0xa8, 0xf1, 0x81, 0x11, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x84, 0x8b, 0x86, 0x7b, \ + 0x99, 0x11, 0x82, 0x6b, \ + 0x4c, 0x15, 0x9a, 0x1e, \ + 0x8c, 0x1b, 0xe0, 0x14, \ + 0xef, 0xf4, 0x5f, 0x48, \ + 0xa1, 0x1b, 0x98, 0x14, \ + 0x86, 0xb5, 0x81, 0x11, \ + 0xe0, 0x00, 0x52, 0x06, \ + 0xf1, 0x80, 0x42, 0x92, \ + 0x80, 0xf5, 0xa1, 0x1d, \ + 0x80, 0xd5, 0xa1, 0x0d, \ + 0x12, 0x14, 0x82, 0x11, \ + 0x81, 0x25, 0xfc, 0x00, \ + 0x90, 0x0b, 0x81, 0x91, \ + 0x92, 0x2b, 0x8e, 0x1b, \ + 0xef, 0xc4, 0x74, 0x28, \ + 0x80, 0xa5, 0xa1, 0x1b, \ + 0x4c, 0x05, 0x83, 0x01, \ + 0xff, 0x01, 0xe0, 0x10, \ + 0xf0, 0x0a, 0x42, 0x0a, \ + 0xb3, 0x1f, 0x83, 0x97, \ + 0x88, 0xf1, 0x82, 0x0b, \ + 0xf2, 0x1c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x88, 0x7b, 0x8d, 0x5e, \ + 0x84, 0x9b, 0x8b, 0x3e, \ + 0x89, 0x0e, 0x82, 0xab, \ + 0x89, 0x01, 0x9f, 0x14, \ + 0xfe, 0x2e, 0x9d, 0x24, \ + 0x54, 0x05, 0xaa, 0x0e, \ + 0x83, 0x01, 0xe0, 0x14, \ + 0x84, 0x06, 0xfc, 0x1e, \ + 0x61, 0x51, 0x51, 0x86, \ + 0x81, 0xb1, 0xc0, 0x00, \ + 0xbe, 0x6e, 0xa0, 0x68, \ + 0xbc, 0x0e, 0xfe, 0x04, \ + 0x94, 0x1b, 0x89, 0x04, \ + 0x8b, 0x34, 0x92, 0x2b, \ + 0x8d, 0x54, 0x8e, 0x4b, \ + 0xe8, 0x00, 0x42, 0xe8, \ + 0xa1, 0x0b, 0xa8, 0x04, \ + 0x40, 0x72, 0x88, 0x75, \ + 0x00, 0x19, 0xf0, 0x98, \ + 0xa1, 0x1b, 0xe0, 0xf0, \ + 0x00, 0x09, 0x84, 0xa5, \ + 0xa1, 0x0b, 0xe0, 0xd0, \ + 0x00, 0x64, 0x84, 0x25, \ + 0x92, 0x03, 0xe0, 0x88, \ + 0xe0, 0xc8, 0x00, 0x15, \ + 0xe0, 0x10, 0x00, 0x05, \ + 0x82, 0x05, 0xc0, 0x1d, \ + 0x81, 0x11, 0x92, 0x63, \ + 0xe3, 0xa0, 0x4c, 0x11, \ + 0x80, 0x16, 0xfc, 0x04, \ + 0x92, 0x63, 0x81, 0x55, \ + 0x4c, 0x01, 0x83, 0x01, \ + 0x82, 0xb1, 0xe3, 0xa0, \ + 0x82, 0x81, 0xe9, 0xbd, \ + 0xb8, 0xf1, 0xf4, 0x15, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xf0, 0x1e, 0xf0, \ + 0x88, 0xcb, 0x8f, 0x5e, \ + 0x82, 0xab, 0x8d, 0x3e, \ + 0x95, 0x2e, 0x8b, 0x0e, \ + 0xe0, 0x28, 0x04, 0x09, \ + 0x8b, 0x11, 0xc4, 0x09, \ + 0xaf, 0x24, 0xa5, 0x0b, \ + 0x54, 0x15, 0xaa, 0x1e, \ + 0x81, 0x91, 0xe0, 0x14, \ + 0x40, 0xc2, 0x9a, 0x75, \ + 0x00, 0xb9, 0xf0, 0x88, \ + 0x00, 0x19, 0xe0, 0xb0, \ + 0x96, 0x13, 0xe0, 0xf0, \ + 0x98, 0x45, 0xd6, 0x1d, \ + 0xad, 0x14, 0xfc, 0x2e, \ + 0x02, 0x00, 0x97, 0x1e, \ + 0x89, 0x1e, 0xe4, 0xb0, \ + 0xe4, 0xf0, 0x02, 0x00, \ + 0x02, 0x00, 0xfe, 0x1e, \ + 0x93, 0x1e, 0xe4, 0x90, \ + 0x91, 0x0e, 0xe8, 0x01, \ + 0x81, 0x91, 0x81, 0x61, \ + 0x80, 0x78, 0x93, 0x04, \ + 0x80, 0x08, 0x91, 0x04, \ + 0xce, 0x0d, 0x8e, 0x03, \ + 0x00, 0xf0, 0x8e, 0x25, \ + 0x08, 0xb4, 0xe4, 0xe0, \ + 0x0a, 0x74, 0xfa, 0x80, \ + 0xbc, 0x0e, 0xfa, 0x80, \ + 0x98, 0x8b, 0x94, 0x1b, \ + 0x97, 0x34, 0x98, 0x2b, \ + 0xe8, 0x00, 0x45, 0x08, \ + 0xa1, 0x0b, 0xa8, 0x04, \ + 0x95, 0x04, 0x92, 0x15, \ + 0xe0, 0x28, 0x00, 0x09, \ + 0x9d, 0x04, 0xa5, 0x0b, \ + 0x80, 0xd5, 0xc4, 0x07, \ + 0x82, 0x03, 0x97, 0x14, \ + 0xe0, 0x24, 0x00, 0x05, \ + 0x8f, 0x14, 0x83, 0x05, \ + 0x00, 0x25, 0x82, 0x03, \ + 0x8d, 0x04, 0xe0, 0x24, \ + 0xe0, 0x10, 0x00, 0x45, \ + 0xe4, 0xc0, 0x0a, 0xf0, \ + 0x8b, 0x04, 0x81, 0x31, \ + 0x55, 0x78, 0x94, 0x1b, \ + 0x99, 0x04, 0xef, 0xfc, \ + 0x93, 0x14, 0x90, 0xcb, \ + 0x91, 0x24, 0x84, 0x18, \ + 0x82, 0x71, 0x88, 0x28, \ + 0x02, 0x74, 0x82, 0x23, \ + 0xc2, 0x2d, 0xfa, 0x80, \ + 0x82, 0x61, 0x80, 0x93, \ + 0x89, 0x04, 0xf2, 0x35, \ + 0xfe, 0x14, 0x80, 0x08, \ + 0x82, 0xb1, 0x84, 0x18, \ + 0xfa, 0x80, 0x04, 0xb4, \ + 0xc4, 0x1d, 0x80, 0x13, \ + 0x0c, 0x8a, 0xee, 0x15, \ + 0xfc, 0x24, 0xec, 0x08, \ + 0x00, 0x64, 0x80, 0xb5, \ + 0x52, 0x9a, 0xfb, 0x80, \ + 0x00, 0x24, 0xe0, 0x02, \ + 0x42, 0xc2, 0xe0, 0x88, \ + 0x02, 0x15, 0xf0, 0x80, \ + 0x42, 0x12, 0xe0, 0x44, \ + 0xb2, 0x13, 0xfa, 0xa4, \ + 0x84, 0x03, 0x95, 0x24, \ + 0xe0, 0xc8, 0x40, 0x15, \ + 0x0c, 0x0c, 0xf8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0x86, 0x0e, 0x8d, 0x01, \ + 0xe0, 0x14, 0x42, 0x05, \ + 0xe0, 0x30, 0x04, 0x09, \ + 0xe0, 0x14, 0x06, 0x39, \ + 0xe0, 0x10, 0x04, 0x29, \ + 0x40, 0x4a, 0xa0, 0x43, \ + 0xa4, 0x53, 0xe6, 0x8e, \ + 0x8a, 0x03, 0xbc, 0x24, \ + 0xe5, 0x04, 0x00, 0x8a, \ + 0x80, 0x75, 0x88, 0x0e, \ + 0x84, 0x0e, 0xfb, 0x01, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x82, 0x7b, 0x86, 0x6b, \ + 0x04, 0x09, 0x80, 0x8b, \ + 0xe2, 0x0d, 0xe3, 0xc0, \ + 0x9e, 0x0e, 0xa3, 0x01, \ + 0xe0, 0x14, 0x4e, 0x05, \ + 0x0c, 0x09, 0x80, 0xb5, \ + 0xa3, 0x0b, 0xe0, 0x64, \ + 0x81, 0x91, 0x88, 0x15, \ + 0x14, 0x40, 0x98, 0x9e, \ + 0xc0, 0x01, 0x64, 0x01, \ + 0xe4, 0x90, 0x16, 0x50, \ + 0x0c, 0x45, 0xa8, 0x24, \ + 0x9e, 0x5b, 0xe0, 0x10, \ + 0x90, 0x0b, 0x81, 0x31, \ + 0x4e, 0xb8, 0x8e, 0x1b, \ + 0x9c, 0x04, 0xef, 0xfc, \ + 0x82, 0xb5, 0xa1, 0x0b, \ + 0xac, 0x0e, 0xbc, 0x04, \ + 0xc1, 0x1d, 0x98, 0x14, \ + 0x98, 0x0e, 0x80, 0x45, \ + 0x12, 0x8a, 0x82, 0x91, \ + 0x88, 0xa1, 0xe5, 0x00, \ + 0xfa, 0x35, 0x88, 0xb1, \ + 0x0c, 0x0c, 0x98, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xe0, 0x24, 0x04, 0x49, \ + 0x9d, 0x41, 0xa1, 0x4b, \ + 0x42, 0x45, 0x86, 0x4e, \ + 0x80, 0xc5, 0xe0, 0x14, \ + 0x44, 0x41, 0x81, 0x41, \ + 0x20, 0x18, 0xe1, 0x18, \ + 0x06, 0x4b, 0xff, 0xf8, \ + 0xa1, 0x4b, 0xe0, 0x28, \ + 0x83, 0x01, 0xfe, 0x05, \ + 0xe1, 0x18, 0x44, 0x01, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x86, 0xbb, 0x8a, 0xab, \ + 0x82, 0x7b, 0x84, 0x6b, \ + 0x9d, 0x84, 0x80, 0x9b, \ + 0xb1, 0x06, 0x81, 0x01, \ + 0xe0, 0x30, 0x50, 0x03, \ + 0x94, 0x3b, 0x92, 0x0b, \ + 0x69, 0xf8, 0x90, 0x4b, \ + 0x9c, 0x04, 0xef, 0xf8, \ + 0x8e, 0x95, 0xa1, 0x0b, \ + 0xe0, 0x20, 0x0c, 0x09, \ + 0xa5, 0x54, 0xe5, 0x0d, \ + 0x82, 0x05, 0xa3, 0x44, \ + 0x8e, 0x1b, 0x92, 0x0b, \ + 0x90, 0x3b, 0x8c, 0x2b, \ + 0xef, 0xfc, 0x79, 0x08, \ + 0x89, 0x4e, 0x85, 0xf5, \ + 0x9f, 0x24, 0x8b, 0x5e, \ + 0xfe, 0x2e, 0x8e, 0x1b, \ + 0xef, 0xf8, 0x66, 0xc8, \ + 0xa1, 0x0b, 0x9c, 0x04, \ + 0x0c, 0x09, 0x88, 0xb5, \ + 0xa3, 0x0b, 0xe1, 0x18, \ + 0xfc, 0x0e, 0xa1, 0x04, \ + 0x92, 0x0b, 0x82, 0x55, \ + 0x8c, 0x2b, 0x8e, 0x1b, \ + 0x5b, 0xd8, 0x90, 0x3b, \ + 0x81, 0x01, 0xef, 0xf8, \ + 0xe1, 0x18, 0x4c, 0x01, \ + 0xe0, 0x88, 0x0c, 0x0b, \ + 0x4c, 0x03, 0x82, 0x01, \ + 0x00, 0xf0, 0xe0, 0x88, \ + 0xbe, 0x0e, 0xe4, 0x60, \ + 0xbc, 0x0e, 0x8b, 0x04, \ + 0x8e, 0x1b, 0x92, 0x0b, \ + 0x90, 0x3b, 0x8c, 0x2b, \ + 0xfc, 0x54, 0x94, 0x4b, \ + 0xef, 0xfc, 0x66, 0xc8, \ + 0xa1, 0x0b, 0x9c, 0x04, \ + 0xb8, 0xf1, 0x80, 0xa5, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xb1, 0x08, 0xdd, 0x93, \ + 0x90, 0x15, 0xa3, 0x0b, \ + 0x50, 0x01, 0x83, 0x01, \ + 0x92, 0x0b, 0xe0, 0x60, \ + 0x8c, 0x2b, 0x8e, 0x1b, \ + 0x90, 0x4b, 0x94, 0x3b, \ + 0xef, 0xf8, 0x72, 0xc8, \ + 0xa1, 0x0b, 0x9c, 0x04, \ + 0x92, 0x0b, 0xfa, 0xb5, \ + 0x8c, 0x2b, 0x8e, 0x1b, \ + 0xfe, 0x44, 0x90, 0x3b, \ + 0x4a, 0x18, 0xfc, 0x54, \ + 0x9c, 0x04, 0xef, 0xfc, \ + 0xf8, 0x55, 0xa1, 0x0b, \ + 0xe0, 0x24, 0x0c, 0x09, \ + 0x9b, 0x01, 0xa5, 0x0b, \ + 0x4e, 0x05, 0x9e, 0x0e, \ + 0x80, 0xf5, 0xe0, 0x14, \ + 0xe0, 0x28, 0x10, 0x0b, \ + 0xc3, 0x0d, 0xd8, 0x1a, \ + 0x83, 0xa5, 0x86, 0x05, \ + 0xe0, 0x40, 0x0c, 0x05, \ + 0x80, 0x85, 0xa1, 0x0b, \ + 0xc2, 0x0d, 0x98, 0x14, \ + 0x10, 0x0b, 0x84, 0x05, \ + 0xd8, 0x1a, 0xe0, 0x28, \ + 0x82, 0xd5, 0xc3, 0x0d, \ + 0x8e, 0x1b, 0x92, 0x0b, \ + 0x90, 0x3b, 0x8c, 0x2b, \ + 0xef, 0xfc, 0x75, 0xa8, \ + 0xa1, 0x0b, 0x9c, 0x04, \ + 0x81, 0x45, 0xee, 0xf5, \ + 0x4c, 0x01, 0x83, 0x01, \ + 0x0c, 0x0b, 0xe1, 0x18, \ + 0xda, 0x12, 0xe0, 0x88, \ + 0x82, 0x55, 0x83, 0x0d, \ + 0x8e, 0x1b, 0x92, 0x0b, \ + 0x90, 0x3b, 0x8c, 0x2b, \ + 0xef, 0xfc, 0x74, 0x88, \ + 0xa1, 0x0b, 0x9c, 0x04, \ + 0x0c, 0x09, 0xea, 0xb5, \ + 0xa3, 0x0b, 0xe1, 0x18, \ + 0x0c, 0x09, 0x80, 0xb5, \ + 0xa1, 0x0b, 0xe1, 0x1c, \ + 0x0c, 0x09, 0x86, 0x45, \ + 0xa3, 0x0b, 0xe1, 0x1c, \ + 0x89, 0x44, 0x8b, 0x54, \ + 0xac, 0x08, 0xda, 0x55, \ + 0xd8, 0xe5, 0xa1, 0x0b, \ + 0x4c, 0x01, 0x81, 0x01, \ + 0x8e, 0x1b, 0xe1, 0x1c, \ + 0x6c, 0x18, 0x88, 0x2b, \ + 0x9c, 0x04, 0xef, 0xf8, \ + 0xe4, 0x55, 0xa1, 0x0b, \ + 0x50, 0x01, 0x83, 0x01, \ + 0x8b, 0x54, 0xe0, 0x64, \ + 0xeb, 0x75, 0x89, 0x44, \ + 0x4c, 0x01, 0x83, 0x01, \ + 0x8e, 0x1b, 0xe1, 0x1c, \ + 0x90, 0x3b, 0x8c, 0x2b, \ + 0xef, 0xf8, 0x61, 0xc8, \ + 0xa1, 0x0b, 0x9c, 0x04, \ + 0x92, 0x0b, 0xe0, 0x35, \ + 0x8c, 0x2b, 0x8e, 0x1b, \ + 0xfc, 0x44, 0xfe, 0x34, \ + 0x65, 0x98, 0x89, 0x54, \ + 0x9c, 0x04, 0xef, 0xf8, \ + 0xdc, 0xd5, 0xa1, 0x0b, \ + 0x00, 0x0c, 0xfb, 0x15, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xed, 0x90, 0x1e, 0xf0, \ + 0xa7, 0x4e, 0x9b, 0x5e, \ + 0x84, 0xbb, 0xad, 0x3e, \ + 0x1e, 0x75, 0xa5, 0x1e, \ + 0x9e, 0x14, 0xe0, 0x84, \ + 0xe0, 0x8c, 0x1e, 0x65, \ + 0xe0, 0x88, 0x1e, 0x95, \ + 0xe0, 0x80, 0x1e, 0x85, \ + 0xe4, 0x60, 0x00, 0x70, \ + 0x0e, 0x05, 0xaf, 0x0e, \ + 0xa3, 0x0e, 0xe0, 0x20, \ + 0xe4, 0x10, 0x14, 0x70, \ + 0x82, 0x0b, 0xa9, 0x1e, \ + 0xef, 0xf4, 0x78, 0x48, \ + 0x0e, 0x05, 0x9d, 0x0e, \ + 0xdc, 0x34, 0xe0, 0x18, \ + 0xe0, 0x58, 0x0e, 0x19, \ + 0x52, 0x05, 0xb1, 0x1e, \ + 0x52, 0x35, 0xeb, 0x54, \ + 0x0e, 0x1b, 0xeb, 0x50, \ + 0x9c, 0x28, 0xe0, 0x20, \ + 0xdc, 0x58, 0x9e, 0x48, \ + 0x52, 0x13, 0x9e, 0x04, \ + 0xc0, 0x01, 0x66, 0xb1, \ + 0x52, 0x21, 0x83, 0xc1, \ + 0xc0, 0x02, 0x6d, 0x69, \ + 0x52, 0x41, 0xa9, 0xcf, \ + 0xc0, 0x02, 0x6d, 0x6d, \ + 0xe8, 0x94, 0x04, 0xc4, \ + 0x6d, 0x71, 0x52, 0x51, \ + 0x7c, 0x18, 0xc0, 0x02, \ + 0x99, 0x0e, 0xef, 0xf4, \ + 0xe0, 0x18, 0x0e, 0x05, \ + 0x46, 0x05, 0x8c, 0x3b, \ + 0x0e, 0x09, 0xf0, 0x14, \ + 0x0e, 0x19, 0xe0, 0x48, \ + 0x95, 0x8e, 0xe0, 0x4c, \ + 0xe0, 0x34, 0x10, 0x29, \ + 0x0e, 0x39, 0x86, 0x5b, \ + 0x0e, 0x49, 0xe0, 0x90, \ + 0x4a, 0x01, 0xe0, 0x94, \ + 0x4a, 0x11, 0xf8, 0x18, \ + 0x05, 0x0a, 0xf8, 0x14, \ + 0x4a, 0x31, 0xe7, 0xf4, \ + 0x4a, 0x41, 0xf8, 0x10, \ + 0x81, 0x01, 0xf8, 0x0c, \ + 0x80, 0x75, 0x81, 0x11, \ + 0xe4, 0x04, 0x02, 0x20, \ + 0x65, 0x41, 0x10, 0x90, \ + 0x4c, 0x11, 0xc0, 0x0b, \ + 0x99, 0x11, 0xe0, 0x48, \ + 0x94, 0x1c, 0xab, 0x5e, \ + 0xe0, 0x58, 0x4c, 0x01, \ + 0xe4, 0x10, 0x00, 0xb0, \ + 0xe4, 0xe0, 0x06, 0xb0, \ + 0xe5, 0x40, 0x08, 0xb0, \ + 0xe7, 0xc0, 0x04, 0xb0, \ + 0xe4, 0x30, 0x02, 0xb0, \ + 0x93, 0x2e, 0xfe, 0x1e, \ + 0x9f, 0x4e, 0xfc, 0x2e, \ + 0xa1, 0x3e, 0xbe, 0x4e, \ + 0xad, 0x14, 0xbc, 0x3e, \ + 0x8e, 0x3b, 0x8c, 0x2b, \ + 0xaf, 0x54, 0x94, 0x4b, \ + 0xef, 0xd0, 0x61, 0xa8, \ + 0x97, 0x8e, 0xa1, 0x0b, \ + 0x8d, 0x11, 0x80, 0xc5, \ + 0xa1, 0xcb, 0xb1, 0xc4, \ + 0x83, 0xb5, 0x84, 0x15, \ + 0xe0, 0x1c, 0x0e, 0x35, \ + 0xa9, 0x14, 0x90, 0x0b, \ + 0x8c, 0x4b, 0xb1, 0x24, \ + 0xef, 0xd8, 0x53, 0x68, \ + 0x9c, 0x65, 0xa1, 0x0b, \ + 0xb1, 0xc4, 0x8f, 0x11, \ + 0x82, 0xa5, 0xa1, 0xcb, \ + 0xe4, 0xa0, 0x04, 0x70, \ + 0x64, 0x01, 0x06, 0x90, \ + 0x98, 0x4b, 0xc0, 0x09, \ + 0x08, 0x40, 0xc9, 0x54, \ + 0xa1, 0x4b, 0xec, 0x04, \ + 0xfe, 0x75, 0xcd, 0x5e, \ + 0x1e, 0xa5, 0xa1, 0x0b, \ + 0x8a, 0x25, 0xe0, 0x90, \ + 0x4c, 0x21, 0x81, 0x21, \ + 0xc0, 0x01, 0x60, 0x1d, \ + 0x60, 0x19, 0x4c, 0x21, \ + 0x4c, 0x21, 0xc0, 0x01, \ + 0xc0, 0x01, 0x60, 0x15, \ + 0x60, 0x11, 0x4c, 0x21, \ + 0x4c, 0x21, 0xc0, 0x01, \ + 0xc0, 0x01, 0x60, 0x0d, \ + 0x60, 0x09, 0x4c, 0x21, \ + 0x4c, 0x21, 0xc0, 0x01, \ + 0xc0, 0x01, 0x60, 0x05, \ + 0x60, 0x01, 0x4c, 0x21, \ + 0x4c, 0x21, 0xc0, 0x01, \ + 0x4c, 0x21, 0xef, 0xfc, \ + 0x4c, 0x21, 0xef, 0xf8, \ + 0x4c, 0x21, 0xef, 0xf4, \ + 0xa8, 0x0e, 0xef, 0xf0, \ + 0x1e, 0xf0, 0xaa, 0x1e, \ + 0x0c, 0x0c, 0xe5, 0x90, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe1, 0x04, 0x16, 0x09, \ + 0x88, 0xc5, 0xa1, 0x0b, \ + 0xe6, 0x80, 0x06, 0xb0, \ + 0x64, 0x01, 0x00, 0x90, \ + 0x02, 0x90, 0xc0, 0x09, \ + 0xc0, 0x02, 0x65, 0x01, \ + 0xe4, 0x60, 0x04, 0x60, \ + 0x66, 0x01, 0x08, 0x90, \ + 0x8f, 0x2e, 0xc0, 0x04, \ + 0x67, 0x01, 0x04, 0x90, \ + 0xab, 0x54, 0xc0, 0x06, \ + 0x8b, 0x2e, 0x8d, 0x5e, \ + 0xfe, 0x1e, 0x89, 0x4e, \ + 0xbe, 0x0e, 0xfc, 0x9e, \ + 0xa5, 0x04, 0xbc, 0xce, \ + 0x9f, 0x24, 0xa1, 0x14, \ + 0xa3, 0x54, 0xa7, 0x44, \ + 0xef, 0xdc, 0x6a, 0x08, \ + 0xa1, 0x0b, 0xb1, 0xc4, \ + 0x97, 0x11, 0x80, 0x65, \ + 0x00, 0xb0, 0xf7, 0x05, \ + 0x10, 0x60, 0xe7, 0x80, \ + 0x98, 0x1b, 0xe4, 0x60, \ + 0x90, 0x3b, 0xab, 0x24, \ + 0xef, 0xd8, 0x63, 0x68, \ + 0x88, 0xe5, 0xa1, 0x0b, \ + 0xf5, 0x35, 0x99, 0x11, \ + 0xe0, 0xd0, 0x16, 0x09, \ + 0x8f, 0x11, 0xa1, 0x0b, \ + 0x16, 0x09, 0x8e, 0x85, \ + 0xa3, 0x0b, 0xe0, 0xd4, \ + 0x8e, 0x15, 0xb1, 0xc4, \ + 0xe4, 0xa0, 0x00, 0xb0, \ + 0x84, 0x34, 0xaf, 0x14, \ + 0xca, 0x54, 0xad, 0x24, \ + 0xe4, 0x30, 0x02, 0x20, \ + 0x89, 0x9e, 0xa8, 0x21, \ + 0xfe, 0x4e, 0x9b, 0x44, \ + 0xbe, 0x2e, 0xfc, 0x0e, \ + 0xa5, 0x04, 0xbc, 0x1e, \ + 0x9d, 0x24, 0x99, 0x14, \ + 0x67, 0xf8, 0x98, 0x4b, \ + 0xb1, 0xc4, 0xef, 0xc4, \ + 0x91, 0x11, 0xa1, 0x0b, \ + 0x83, 0xe5, 0xda, 0xd5, \ + 0x00, 0x59, 0xa7, 0x04, \ + 0x00, 0xb0, 0xe0, 0x2c, \ + 0x04, 0x90, 0xe4, 0xa0, \ + 0xc0, 0x09, 0x64, 0x01, \ + 0xe6, 0x70, 0x02, 0x60, \ + 0xfe, 0x1e, 0xa7, 0x1e, \ + 0xa1, 0x2e, 0xfc, 0x9e, \ + 0xb1, 0x14, 0xbe, 0x2e, \ + 0x9f, 0x0e, 0xbc, 0x1e, \ + 0xaf, 0x24, 0x8e, 0x1b, \ + 0x90, 0x4b, 0xab, 0x34, \ + 0xef, 0xd4, 0x64, 0xe8, \ + 0x8a, 0x45, 0xa1, 0x0b, \ + 0xed, 0x35, 0x9b, 0x11, \ + 0x16, 0x09, 0xb1, 0xc4, \ + 0xa1, 0x0b, 0xe0, 0xd0, \ + 0x0e, 0x09, 0x84, 0x65, \ + 0x04, 0x90, 0xe0, 0x58, \ + 0xc0, 0x02, 0x65, 0x01, \ + 0x66, 0x01, 0x06, 0x90, \ + 0x08, 0x90, 0xc0, 0x04, \ + 0xc0, 0x06, 0x67, 0x01, \ + 0x49, 0xb8, 0x92, 0x1b, \ + 0xb1, 0xc4, 0xef, 0xf4, \ + 0x93, 0x11, 0xa1, 0x0b, \ + 0x16, 0x09, 0xce, 0x55, \ + 0xa1, 0x0b, 0xe0, 0xd0, \ + 0x04, 0x70, 0x86, 0x85, \ + 0x06, 0x90, 0xe4, 0xa0, \ + 0xc0, 0x09, 0x64, 0x01, \ + 0x92, 0x1b, 0x98, 0x0b, \ + 0xef, 0xec, 0x6f, 0x68, \ + 0x95, 0x11, 0xb1, 0xc4, \ + 0xaf, 0x8e, 0xe7, 0x85, \ + 0x64, 0xd1, 0x10, 0x60, \ + 0x97, 0x04, 0xc0, 0x02, \ + 0xab, 0x24, 0xa9, 0x14, \ + 0x90, 0x4b, 0xa7, 0x34, \ + 0xef, 0xd8, 0x48, 0x48, \ + 0x82, 0x05, 0xa1, 0x0b, \ + 0xe5, 0xd5, 0x9f, 0x11, \ + 0xa1, 0xcb, 0x81, 0x01, \ + 0xdf, 0xfc, 0x31, 0xa8, \ + 0x9b, 0x8e, 0xe5, 0x45, \ + 0x00, 0x80, 0xa3, 0x84, \ + 0x02, 0x04, 0xe4, 0x40, \ + 0xf7, 0x17, 0xe1, 0x7c, \ + 0x0a, 0x04, 0x82, 0x03, \ + 0x00, 0x60, 0xe1, 0x14, \ + 0xc0, 0x01, 0x66, 0x31, \ + 0xe7, 0x90, 0x02, 0xb0, \ + 0xfc, 0x0e, 0xa9, 0x0e, \ + 0xbe, 0x7e, 0xa1, 0x74, \ + 0xbc, 0x0e, 0xb1, 0x04, \ + 0xab, 0x24, 0xa5, 0x04, \ + 0x90, 0x4b, 0xaf, 0x34, \ + 0x7d, 0xb8, 0x99, 0x5e, \ + 0xa1, 0x0b, 0xef, 0xf0, \ + 0xa1, 0x11, 0x80, 0x65, \ + 0x16, 0x09, 0xe1, 0x85, \ + 0x8b, 0x9e, 0xe3, 0x94, \ + 0xb8, 0x11, 0xad, 0x14, \ + 0x8f, 0x2e, 0x9b, 0x24, \ + 0x8d, 0x2e, 0xa9, 0x24, \ + 0xb1, 0x24, 0x89, 0x7e, \ + 0x99, 0x24, 0xfe, 0x2e, \ + 0xbe, 0x8e, 0xfc, 0x2e, \ + 0xa5, 0x04, 0xbc, 0x0e, \ + 0xab, 0x34, 0x9f, 0x24, \ + 0x9d, 0x94, 0xaf, 0x44, \ + 0x67, 0x38, 0x92, 0x5b, \ + 0xa1, 0x0b, 0xef, 0xd4, \ + 0xab, 0x11, 0x80, 0x65, \ + 0x00, 0xb0, 0xdd, 0xc5, \ + 0xad, 0x84, 0xe7, 0xa0, \ + 0xe4, 0x50, 0x02, 0x80, \ + 0xab, 0x34, 0x92, 0x2b, \ + 0x9b, 0x74, 0xa9, 0x44, \ + 0x76, 0x78, 0x8e, 0x5b, \ + 0xa1, 0x0b, 0xef, 0xd4, \ + 0xbb, 0x11, 0x80, 0x65, \ + 0x00, 0xb0, 0xdb, 0xc5, \ + 0x02, 0x80, 0xe7, 0xb0, \ + 0x12, 0x60, 0xe4, 0x80, \ + 0xc0, 0x03, 0x64, 0x91, \ + 0x8e, 0x3b, 0xab, 0x24, \ + 0x75, 0xb8, 0x92, 0x4b, \ + 0x81, 0x11, 0xef, 0xe8, \ + 0x60, 0x1d, 0x4c, 0x11, \ + 0x4c, 0x11, 0xc0, 0x01, \ + 0xc0, 0x01, 0x60, 0x19, \ + 0x60, 0x15, 0x4c, 0x11, \ + 0x4c, 0x11, 0xc0, 0x01, \ + 0xc0, 0x01, 0x60, 0x11, \ + 0x60, 0x0d, 0x4c, 0x11, \ + 0x4c, 0x11, 0xc0, 0x01, \ + 0xc0, 0x01, 0x60, 0x09, \ + 0x60, 0x05, 0x4c, 0x11, \ + 0x4c, 0x11, 0xc0, 0x01, \ + 0xc0, 0x01, 0x60, 0x01, \ + 0x4c, 0x11, 0xa1, 0x0b, \ + 0x4c, 0x11, 0xef, 0xfc, \ + 0x4c, 0x11, 0xef, 0xf8, \ + 0x4c, 0x11, 0xef, 0xf4, \ + 0x80, 0x65, 0xef, 0xf0, \ + 0xd9, 0xc5, 0xbd, 0x11, \ + 0xe3, 0xb0, 0x16, 0x19, \ + 0xe1, 0xd0, 0x16, 0x3b, \ + 0xe0, 0x14, 0x10, 0x45, \ + 0x00, 0x50, 0x95, 0x54, \ + 0x8b, 0x9e, 0xe4, 0x40, \ + 0x89, 0x2e, 0xa9, 0x24, \ + 0xfe, 0x2e, 0xa7, 0x24, \ + 0xfc, 0x2e, 0xaf, 0x24, \ + 0xbe, 0x7e, 0xab, 0x74, \ + 0x93, 0x04, 0xbc, 0x0e, \ + 0x41, 0x38, 0x9d, 0x24, \ + 0xa1, 0x0b, 0xef, 0xec, \ + 0x80, 0x85, 0x8e, 0x2b, \ + 0xe0, 0x00, 0x42, 0x06, \ + 0x16, 0x19, 0xd5, 0xf5, \ + 0x08, 0x60, 0xe4, 0x10, \ + 0xc0, 0x04, 0x64, 0x51, \ + 0x92, 0x3b, 0xa5, 0x04, \ + 0xef, 0xec, 0x64, 0xc8, \ + 0xe0, 0x00, 0x42, 0x16, \ + 0x00, 0x0c, 0xd5, 0x35, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x88, 0x9b, 0x89, 0x5e, \ + 0x84, 0xab, 0x86, 0x8b, \ + 0x9f, 0x74, 0x80, 0x6b, \ + 0x8d, 0x0e, 0xa3, 0x04, \ + 0xfc, 0x0e, 0x9d, 0x04, \ + 0xe0, 0x40, 0x14, 0x09, \ + 0x8c, 0x0b, 0x8b, 0x0e, \ + 0x8e, 0x3b, 0xfe, 0x1e, \ + 0xe8, 0x00, 0x4d, 0x88, \ + 0xe0, 0x44, 0x14, 0x09, \ + 0x81, 0xb1, 0xa6, 0x0d, \ + 0xa9, 0x0b, 0x86, 0x05, \ + 0xad, 0x0b, 0x8e, 0xa5, \ + 0xaf, 0x0b, 0x94, 0x65, \ + 0xc0, 0x00, 0x0b, 0xf8, \ + 0x90, 0x0b, 0x8b, 0x84, \ + 0x64, 0xf8, 0x8e, 0x1b, \ + 0xa3, 0x0d, 0xef, 0xc4, \ + 0xc0, 0x00, 0x0b, 0x78, \ + 0x8e, 0x1b, 0x90, 0x0b, \ + 0x8c, 0x2b, 0x89, 0x64, \ + 0xef, 0xc4, 0x65, 0xe8, \ + 0x95, 0xa5, 0x90, 0x0b, \ + 0x96, 0x85, 0xa5, 0x0b, \ + 0xaa, 0x55, 0xa7, 0x0b, \ + 0xe0, 0x78, 0x10, 0x09, \ + 0x02, 0x80, 0xa5, 0x0b, \ + 0x9e, 0x65, 0xe4, 0x40, \ + 0xfb, 0xb1, 0xa3, 0x0b, \ + 0xa8, 0x55, 0x8d, 0x64, \ + 0x92, 0x0b, 0x8b, 0x94, \ + 0x56, 0x18, 0x8e, 0x2b, \ + 0xa3, 0x0d, 0xef, 0xc8, \ + 0x92, 0x0b, 0xa8, 0x95, \ + 0x89, 0x84, 0x8e, 0x1b, \ + 0x64, 0x18, 0x90, 0x2b, \ + 0x92, 0x0b, 0xef, 0xc4, \ + 0x90, 0x2b, 0x8e, 0x1b, \ + 0xef, 0xc4, 0x6f, 0x48, \ + 0x94, 0x1b, 0xfe, 0x04, \ + 0x73, 0x38, 0x90, 0x2b, \ + 0x81, 0xb1, 0xef, 0xc4, \ + 0x8b, 0x84, 0x91, 0x95, \ + 0x8e, 0x1b, 0x90, 0x0b, \ + 0xef, 0xc4, 0x61, 0x88, \ + 0xa0, 0x15, 0xa3, 0x0d, \ + 0x8e, 0x1b, 0x90, 0x0b, \ + 0x8c, 0x2b, 0x89, 0x64, \ + 0xef, 0xc4, 0x62, 0x88, \ + 0x8e, 0x1b, 0x90, 0x0b, \ + 0x6d, 0xb8, 0x8c, 0x2b, \ + 0xfe, 0x04, 0xef, 0xc4, \ + 0x8c, 0x2b, 0x94, 0x1b, \ + 0xef, 0xc4, 0x71, 0xa8, \ + 0x10, 0x09, 0x8d, 0xf5, \ + 0xa5, 0x0b, 0xe0, 0x78, \ + 0xe4, 0x40, 0x02, 0x80, \ + 0xa3, 0x0b, 0x94, 0xa5, \ + 0x8d, 0x64, 0xfb, 0xb1, \ + 0x8b, 0x94, 0x98, 0xf5, \ + 0x8e, 0x2b, 0x92, 0x0b, \ + 0xef, 0xc8, 0x52, 0x68, \ + 0x9a, 0x35, 0xa3, 0x0d, \ + 0x8e, 0x1b, 0x92, 0x0b, \ + 0x90, 0x2b, 0x89, 0x84, \ + 0xef, 0xc4, 0x60, 0x68, \ + 0x8e, 0x1b, 0x92, 0x0b, \ + 0x6e, 0x18, 0x90, 0x2b, \ + 0x81, 0xb1, 0xef, 0xc4, \ + 0x0e, 0x09, 0x8b, 0x35, \ + 0xc0, 0x05, 0x6f, 0xd1, \ + 0x84, 0xe5, 0xa1, 0x0b, \ + 0x0e, 0x15, 0xa1, 0x04, \ + 0xc0, 0x01, 0x67, 0xfd, \ + 0x6f, 0xc9, 0x0e, 0x29, \ + 0x0e, 0x39, 0xc0, 0x05, \ + 0xc0, 0x05, 0x6f, 0xcd, \ + 0x60, 0x09, 0x0e, 0x4b, \ + 0xc2, 0x1e, 0xc0, 0x03, \ + 0xe0, 0x14, 0x52, 0x15, \ + 0xe0, 0x68, 0x52, 0x21, \ + 0xe0, 0x6c, 0x52, 0x31, \ + 0xe0, 0x38, 0x52, 0x43, \ + 0x5b, 0x98, 0x8e, 0x0b, \ + 0xa3, 0x0d, 0xef, 0xc4, \ + 0x8c, 0x55, 0x8d, 0x64, \ + 0x89, 0x14, 0x8e, 0x0b, \ + 0xef, 0xc4, 0x66, 0xa8, \ + 0x85, 0xc5, 0x81, 0xb1, \ + 0x0a, 0x00, 0xfc, 0x04, \ + 0xc0, 0x01, 0x66, 0x91, \ + 0x66, 0xd1, 0x00, 0x00, \ + 0xbc, 0x0e, 0xc0, 0x01, \ + 0xfe, 0x24, 0x8c, 0x0b, \ + 0x89, 0x64, 0x8e, 0x3b, \ + 0x76, 0x58, 0x8c, 0x4b, \ + 0x8b, 0x04, 0xef, 0xc8, \ + 0x8c, 0x2b, 0x8e, 0x1b, \ + 0xef, 0xc4, 0x67, 0xc8, \ + 0xfc, 0x04, 0x83, 0x55, \ + 0x66, 0x91, 0x0a, 0x00, \ + 0x00, 0x00, 0xc0, 0x01, \ + 0xc0, 0x01, 0x66, 0xd1, \ + 0x8c, 0x0b, 0xbc, 0x0e, \ + 0x8e, 0x3b, 0xfe, 0x24, \ + 0x8c, 0x4b, 0x89, 0x64, \ + 0xef, 0xc8, 0x75, 0x08, \ + 0x8e, 0x1b, 0x8b, 0x04, \ + 0x68, 0xf8, 0x8c, 0x2b, \ + 0x81, 0xb1, 0xef, 0xc4, \ + 0x40, 0x26, 0x8d, 0x64, \ + 0x98, 0xbe, 0xe0, 0x00, \ + 0x96, 0x0b, 0x9a, 0x0e, \ + 0x0c, 0x0c, 0xb8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xff, 0x65, 0x81, 0xb1, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x84, 0x6b, 0x86, 0x7b, \ + 0xa0, 0x08, 0x82, 0x8b, \ + 0xe0, 0x28, 0xa2, 0x18, \ + 0xef, 0xf4, 0x49, 0x28, \ + 0xa2, 0x04, 0x80, 0x2b, \ + 0xe0, 0x58, 0x10, 0x19, \ + 0xc8, 0x17, 0xe0, 0x34, \ + 0xef, 0xf4, 0x4d, 0x68, \ + 0xe0, 0x44, 0x0c, 0x19, \ + 0x9c, 0x55, 0xa5, 0x1b, \ + 0x9a, 0x24, 0xe0, 0x14, \ + 0x4e, 0x05, 0x98, 0x34, \ + 0xc0, 0x01, 0x67, 0xdd, \ + 0xe0, 0x18, 0x0c, 0x0b, \ + 0x67, 0xe1, 0x4e, 0x15, \ + 0x0c, 0x19, 0xc0, 0x01, \ + 0x4e, 0x25, 0xe0, 0x38, \ + 0xc0, 0x01, 0x67, 0xe9, \ + 0x67, 0xed, 0x4e, 0x35, \ + 0x4e, 0x03, 0xc0, 0x01, \ + 0xc0, 0x02, 0x6f, 0xe1, \ + 0xe0, 0x54, 0x0c, 0x09, \ + 0x6f, 0xc9, 0x4e, 0x11, \ + 0x0c, 0x19, 0xc0, 0x05, \ + 0x0c, 0x29, 0xe0, 0x3c, \ + 0x0c, 0x39, 0xe0, 0x40, \ + 0xa1, 0x0b, 0xe0, 0x48, \ + 0x4e, 0x11, 0x81, 0x01, \ + 0xc0, 0x05, 0x6f, 0xcd, \ + 0x6f, 0xd1, 0x4e, 0x01, \ + 0x4e, 0x21, 0xc0, 0x05, \ + 0xc0, 0x05, 0x6f, 0xd5, \ + 0x6f, 0xd9, 0x4e, 0x31, \ + 0xb0, 0xa5, 0xc0, 0x05, \ + 0x60, 0x51, 0x0c, 0x09, \ + 0x02, 0x60, 0xc0, 0x01, \ + 0xc0, 0x03, 0x64, 0x91, \ + 0xe0, 0x88, 0x04, 0x04, \ + 0xfc, 0x08, 0x02, 0x35, \ + 0xf0, 0x88, 0x48, 0x12, \ + 0x67, 0xf9, 0x4e, 0x35, \ + 0x06, 0x04, 0xc0, 0x01, \ + 0x08, 0x45, 0xe0, 0x84, \ + 0x82, 0x33, 0xe0, 0x60, \ + 0xe1, 0x20, 0x06, 0x5b, \ + 0x68, 0x01, 0x4e, 0x45, \ + 0x84, 0x63, 0xc0, 0x01, \ + 0xe1, 0xbc, 0x0c, 0x25, \ + 0x60, 0x09, 0x4e, 0x53, \ + 0x06, 0x3b, 0xc0, 0x03, \ + 0x83, 0x41, 0xe1, 0x50, \ + 0x6f, 0xd1, 0x4e, 0x41, \ + 0x4e, 0x25, 0xc0, 0x05, \ + 0xc0, 0x01, 0x67, 0xfd, \ + 0x60, 0x0d, 0x4e, 0x33, \ + 0x80, 0x13, 0xc0, 0x03, \ + 0xe3, 0x00, 0x02, 0x09, \ + 0x60, 0x21, 0x4e, 0x01, \ + 0x02, 0x09, 0xc0, 0x06, \ + 0x4e, 0x01, 0xe3, 0x30, \ + 0xc0, 0x06, 0x60, 0x25, \ + 0xe3, 0x60, 0x02, 0x09, \ + 0x60, 0x29, 0x4e, 0x01, \ + 0x02, 0x09, 0xc0, 0x06, \ + 0x4e, 0x01, 0xe3, 0x90, \ + 0xc0, 0x06, 0x60, 0x2d, \ + 0x0c, 0x19, 0x91, 0x45, \ + 0xe0, 0x24, 0xe0, 0x40, \ + 0x08, 0x70, 0x9a, 0x34, \ + 0x0a, 0x14, 0xe4, 0x70, \ + 0x4e, 0x05, 0xe0, 0x88, \ + 0x88, 0x53, 0xe0, 0x10, \ + 0xe0, 0x14, 0x4e, 0x25, \ + 0x98, 0x04, 0x94, 0x3e, \ + 0xe1, 0x00, 0x4a, 0x05, \ + 0xe0, 0x18, 0x0c, 0x0b, \ + 0xe0, 0x84, 0x06, 0x14, \ + 0xe0, 0x38, 0x0c, 0x59, \ + 0xe0, 0x3c, 0x0e, 0x29, \ + 0x46, 0x03, 0x88, 0x33, \ + 0x82, 0x43, 0xe4, 0x00, \ + 0xe0, 0x54, 0x0c, 0x09, \ + 0xea, 0x00, 0x48, 0x51, \ + 0xe0, 0x3c, 0x0c, 0x39, \ + 0xfb, 0x80, 0x40, 0x22, \ + 0xeb, 0x00, 0x48, 0x31, \ + 0xec, 0x00, 0x48, 0x01, \ + 0xe0, 0x40, 0x0c, 0x39, \ + 0xed, 0x00, 0x48, 0x31, \ + 0xe0, 0x48, 0x0c, 0x39, \ + 0x48, 0x31, 0xa1, 0x0b, \ + 0x94, 0x25, 0xee, 0x00, \ + 0xe6, 0x86, 0x44, 0x2a, \ + 0xe0, 0x84, 0x02, 0x24, \ + 0xf0, 0x84, 0x46, 0x72, \ + 0xe0, 0x88, 0x02, 0x24, \ + 0xf0, 0x84, 0x48, 0x72, \ + 0x64, 0x51, 0x02, 0x60, \ + 0x84, 0x73, 0xc0, 0x04, \ + 0x67, 0x71, 0x04, 0x40, \ + 0x06, 0x30, 0xc0, 0x07, \ + 0xc0, 0x11, 0x67, 0x71, \ + 0x02, 0x59, 0x81, 0x41, \ + 0x18, 0x54, 0xfc, 0x10, \ + 0x10, 0x60, 0xe0, 0x88, \ + 0xc0, 0x03, 0x64, 0x91, \ + 0xfc, 0x30, 0x10, 0x95, \ + 0xf0, 0xb0, 0x54, 0x82, \ + 0x78, 0x01, 0x44, 0x95, \ + 0x12, 0x54, 0xc0, 0x02, \ + 0x14, 0xa5, 0xe0, 0x84, \ + 0x90, 0x93, 0xe0, 0x60, \ + 0xe1, 0x20, 0x12, 0xbb, \ + 0xe4, 0x00, 0x44, 0xa5, \ + 0x18, 0xc5, 0x8c, 0xc3, \ + 0x46, 0xb3, 0xe1, 0xbc, \ + 0xc0, 0x02, 0x78, 0x01, \ + 0xe1, 0x50, 0x12, 0x9b, \ + 0xcd, 0x9c, 0xc9, 0xce, \ + 0x10, 0x59, 0x8a, 0x83, \ + 0x58, 0x72, 0xe3, 0x00, \ + 0x58, 0x51, 0xf0, 0x90, \ + 0xc0, 0x04, 0x6f, 0x71, \ + 0xe3, 0x30, 0x10, 0x59, \ + 0x63, 0x71, 0x58, 0x51, \ + 0x10, 0x59, 0xc0, 0x05, \ + 0x58, 0x51, 0xe3, 0x60, \ + 0xc0, 0x05, 0x67, 0x71, \ + 0xe3, 0x90, 0x10, 0x59, \ + 0xc1, 0x4d, 0x82, 0x41, \ + 0x6b, 0x71, 0x58, 0x51, \ + 0xf0, 0xd5, 0xc0, 0x05, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0xfc, 0x4e, 0xfe, 0x5e, \ + 0x84, 0xab, 0x86, 0x6b, \ + 0x80, 0x7b, 0x8d, 0x1e, \ + 0xab, 0x14, 0xaa, 0x04, \ + 0xa7, 0x84, 0x8b, 0x1e, \ + 0xa1, 0x94, 0xa3, 0xb4, \ + 0xef, 0xf0, 0x76, 0x88, \ + 0x14, 0x19, 0x89, 0x0e, \ + 0x98, 0x0a, 0xe0, 0x94, \ + 0xa5, 0x1b, 0x9a, 0x2a, \ + 0xe0, 0x10, 0x56, 0x03, \ + 0xe0, 0x14, 0x56, 0x23, \ + 0x90, 0x65, 0x83, 0x01, \ + 0x8d, 0x74, 0xbe, 0x7e, \ + 0x14, 0x39, 0x9f, 0x04, \ + 0xc0, 0x28, 0xe0, 0x90, \ + 0x80, 0x95, 0xc5, 0x3d, \ + 0xc1, 0x3d, 0xc2, 0x08, \ + 0x00, 0x34, 0x98, 0x65, \ + 0x44, 0x62, 0xe0, 0x84, \ + 0x04, 0x5b, 0xf0, 0x80, \ + 0x08, 0x34, 0xe3, 0x48, \ + 0x88, 0x63, 0xe0, 0x88, \ + 0xe0, 0xd0, 0x12, 0xc9, \ + 0xe0, 0x24, 0x0c, 0x65, \ + 0xe0, 0x30, 0x56, 0x53, \ + 0xa1, 0xcb, 0x9d, 0x34, \ + 0x81, 0x51, 0xae, 0x6e, \ + 0x56, 0x53, 0xee, 0x5e, \ + 0x84, 0x85, 0xe0, 0x20, \ + 0x8a, 0x43, 0xfc, 0x54, \ + 0x18, 0x59, 0x92, 0xcb, \ + 0x08, 0x45, 0xe0, 0xd8, \ + 0x18, 0xc5, 0xe0, 0x24, \ + 0xa5, 0x5b, 0xe0, 0x28, \ + 0xee, 0xce, 0x88, 0xc3, \ + 0x04, 0x20, 0x80, 0xf5, \ + 0xc0, 0x01, 0x66, 0x91, \ + 0x56, 0x23, 0x88, 0x2a, \ + 0xfe, 0x24, 0xe0, 0x20, \ + 0x04, 0x2b, 0x80, 0x23, \ + 0x56, 0x23, 0xe0, 0x18, \ + 0x80, 0x33, 0xe0, 0x44, \ + 0xe0, 0x18, 0x06, 0x0b, \ + 0xe0, 0x40, 0x56, 0x03, \ + 0xbe, 0x74, 0x85, 0x01, \ + 0xa9, 0x64, 0xfe, 0x0e, \ + 0xfc, 0x0e, 0xa5, 0x04, \ + 0x92, 0x2b, 0x8e, 0x0b, \ + 0x47, 0x58, 0x90, 0x3b, \ + 0x14, 0x19, 0xe8, 0x00, \ + 0x8e, 0x0b, 0xe0, 0x94, \ + 0x8c, 0x3b, 0x96, 0x2b, \ + 0xe8, 0x00, 0x42, 0x28, \ + 0xe0, 0x94, 0x14, 0x19, \ + 0xe0, 0x90, 0x14, 0x29, \ + 0x8d, 0x74, 0x8e, 0x0b, \ + 0xfc, 0x44, 0x8e, 0x3b, \ + 0xe8, 0x00, 0x4b, 0x48, \ + 0x89, 0x04, 0x81, 0x61, \ + 0xe1, 0xf0, 0x10, 0x1b, \ + 0xef, 0xd4, 0x5f, 0xe8, \ + 0xe0, 0x18, 0x4e, 0x03, \ + 0x80, 0x6e, 0x8b, 0x04, \ + 0x82, 0x1e, 0xfe, 0x14, \ + 0xb8, 0xf1, 0x8c, 0x0b, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xfb, 0x61, 0xdd, 0x93, \ + 0xfe, 0x0e, 0x85, 0x01, \ + 0xc1, 0xc3, 0xfd, 0xd5, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x84, 0x3b, 0x86, 0x6b, \ + 0x44, 0x46, 0x82, 0x7b, \ + 0x8c, 0x0b, 0xe0, 0x00, \ + 0x4a, 0xd8, 0x86, 0x1b, \ + 0xa6, 0x7d, 0xef, 0xc0, \ + 0xa9, 0x7b, 0x84, 0x65, \ + 0xad, 0x7b, 0x80, 0xa5, \ + 0xaf, 0x7b, 0x84, 0x65, \ + 0x00, 0x0e, 0x8c, 0x35, \ + 0xc0, 0x07, 0x74, 0x01, \ + 0x81, 0x01, 0x9a, 0x0e, \ + 0xe1, 0x00, 0x02, 0x06, \ + 0xe0, 0x30, 0x4c, 0x03, \ + 0xe0, 0x40, 0x4c, 0x13, \ + 0xa5, 0x7b, 0x85, 0x55, \ + 0xa7, 0x7b, 0x84, 0x45, \ + 0x00, 0x0e, 0x88, 0x35, \ + 0xc0, 0x07, 0x74, 0x01, \ + 0x9a, 0x0e, 0x81, 0x11, \ + 0x4c, 0x13, 0xda, 0x1e, \ + 0x00, 0x06, 0xe0, 0x20, \ + 0x4c, 0x13, 0xe1, 0x00, \ + 0x4c, 0x03, 0xe0, 0x30, \ + 0x83, 0x25, 0xe0, 0x40, \ + 0x74, 0x01, 0x00, 0x0e, \ + 0x9a, 0x0e, 0xc0, 0x07, \ + 0xda, 0x0e, 0x81, 0x01, \ + 0xe0, 0x20, 0x4c, 0x03, \ + 0xe0, 0x30, 0x4c, 0x03, \ + 0x61, 0x01, 0x02, 0x06, \ + 0x4c, 0x03, 0xc4, 0x00, \ + 0x4c, 0x15, 0xe0, 0x38, \ + 0x88, 0xf1, 0xe0, 0x20, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x84, 0x7b, 0x86, 0x6b, \ + 0x04, 0x86, 0x82, 0x8b, \ + 0x8c, 0x0b, 0xe0, 0x10, \ + 0x46, 0x38, 0x8e, 0x1b, \ + 0xad, 0x8b, 0xef, 0xc0, \ + 0xe0, 0x0c, 0x10, 0x1a, \ + 0xed, 0x80, 0x42, 0x0a, \ + 0x81, 0x01, 0x80, 0xb5, \ + 0xe0, 0xd0, 0x4c, 0x01, \ + 0xa5, 0x8b, 0x87, 0x95, \ + 0x81, 0x01, 0x8c, 0xf5, \ + 0xe0, 0xd0, 0x4c, 0x01, \ + 0xe1, 0x18, 0x0e, 0x09, \ + 0xe1, 0x10, 0x0e, 0x19, \ + 0xe1, 0x14, 0x0e, 0x29, \ + 0xe0, 0xe8, 0x4c, 0x01, \ + 0xe0, 0xe4, 0x4c, 0x21, \ + 0xe0, 0xe0, 0x4c, 0x11, \ + 0xe1, 0x24, 0x0e, 0x09, \ + 0xe1, 0x1c, 0x0e, 0x19, \ + 0xe1, 0x20, 0x0e, 0x29, \ + 0xe0, 0xf4, 0x4c, 0x01, \ + 0xe0, 0xf0, 0x4c, 0x21, \ + 0xe0, 0xec, 0x4c, 0x11, \ + 0xe1, 0x30, 0x0e, 0x09, \ + 0xe1, 0x28, 0x0e, 0x19, \ + 0xe1, 0x2c, 0x0e, 0x29, \ + 0xe1, 0x00, 0x4c, 0x01, \ + 0xe0, 0xfc, 0x4c, 0x21, \ + 0xe0, 0xf8, 0x4c, 0x11, \ + 0xe1, 0x3c, 0x0e, 0x09, \ + 0xe1, 0x34, 0x0e, 0x19, \ + 0xe1, 0x38, 0x0e, 0x29, \ + 0xe1, 0x0c, 0x4c, 0x01, \ + 0xe1, 0x08, 0x4c, 0x21, \ + 0xe1, 0x04, 0x4c, 0x11, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0x0c, 0x0c, 0xdd, 0x93, \ + 0xdd, 0x93, 0xf2, 0x0c, \ + 0xf8, 0x0c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x8c, 0x35, 0xa5, 0x1b, \ + 0x6f, 0xd1, 0x06, 0x09, \ + 0x06, 0x19, 0xc0, 0x05, \ + 0xc0, 0x05, 0x6f, 0xd9, \ + 0x6f, 0xc9, 0x06, 0x29, \ + 0x06, 0x59, 0xc0, 0x05, \ + 0xc0, 0x05, 0x6f, 0xcd, \ + 0x48, 0x11, 0xa1, 0x0b, \ + 0x48, 0x21, 0xe0, 0x34, \ + 0x48, 0x51, 0xe0, 0x20, \ + 0x48, 0x01, 0xe0, 0x24, \ + 0x98, 0x45, 0xe0, 0x30, \ + 0x67, 0xf9, 0x06, 0x05, \ + 0x06, 0x1b, 0xc0, 0x01, \ + 0xc0, 0x03, 0x60, 0x09, \ + 0xe0, 0x10, 0x48, 0x05, \ + 0x68, 0x01, 0x06, 0x05, \ + 0x48, 0x13, 0xc0, 0x01, \ + 0x06, 0x1b, 0xe0, 0xe0, \ + 0xc0, 0x03, 0x60, 0x0d, \ + 0x60, 0x2d, 0x06, 0x29, \ + 0x48, 0x05, 0xc0, 0x06, \ + 0x48, 0x13, 0xe0, 0x40, \ + 0x48, 0x21, 0xe1, 0x10, \ + 0x89, 0x85, 0xe2, 0x80, \ + 0xf0, 0x88, 0x40, 0x32, \ + 0xee, 0x70, 0x00, 0x19, \ + 0xe0, 0x34, 0x48, 0x11, \ + 0xea, 0x70, 0x00, 0x19, \ + 0xe0, 0x20, 0x48, 0x11, \ + 0xeb, 0x70, 0x00, 0x19, \ + 0xe0, 0x24, 0x48, 0x11, \ + 0xec, 0x70, 0x00, 0x09, \ + 0xec, 0x30, 0x00, 0x02, \ + 0x06, 0x19, 0xa1, 0x0b, \ + 0x48, 0x01, 0xe0, 0x3c, \ + 0x8c, 0x05, 0xe0, 0x30, \ + 0xe6, 0x8a, 0x42, 0x1a, \ + 0xf0, 0x84, 0x58, 0x32, \ + 0xe0, 0x84, 0x04, 0x14, \ + 0x4a, 0x32, 0xc4, 0x17, \ + 0x82, 0x33, 0xf0, 0x88, \ + 0x67, 0x71, 0x02, 0x30, \ + 0x04, 0x40, 0xc0, 0x0b, \ + 0x06, 0x50, 0xe5, 0x00, \ + 0xc0, 0x11, 0x67, 0x71, \ + 0xe6, 0x20, 0x0a, 0x40, \ + 0x67, 0x71, 0x18, 0xc0, \ + 0x81, 0x61, 0xc0, 0x16, \ + 0x78, 0x01, 0x02, 0x75, \ + 0x06, 0x8b, 0xc0, 0x04, \ + 0xc0, 0x02, 0x78, 0x01, \ + 0xf8, 0x30, 0x44, 0x75, \ + 0xf8, 0x30, 0x4a, 0x83, \ + 0xcd, 0x8a, 0xc5, 0x74, \ + 0xd5, 0x8c, 0xc9, 0x7e, \ + 0xfc, 0x18, 0x18, 0x79, \ + 0xf0, 0x98, 0x50, 0x42, \ + 0xc1, 0x6d, 0x82, 0x61, \ + 0xe2, 0x80, 0x50, 0x71, \ + 0x88, 0xf1, 0xfa, 0x55, \ + 0xf0, 0x0c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x86, 0x7b, 0x88, 0x6b, \ + 0x81, 0x01, 0x80, 0x8b, \ + 0xe0, 0x18, 0x4e, 0x03, \ + 0xe0, 0x38, 0x4e, 0x11, \ + 0xe0, 0x3c, 0x4e, 0x21, \ + 0xe0, 0x10, 0x03, 0xc6, \ + 0xe0, 0x20, 0x05, 0xc6, \ + 0xe0, 0x0c, 0x47, 0xf6, \ + 0x4a, 0x72, 0x81, 0x41, \ + 0x4a, 0x05, 0xf0, 0x84, \ + 0x94, 0x0e, 0xf9, 0x00, \ + 0xf0, 0x88, 0x4a, 0x72, \ + 0xf0, 0x90, 0x58, 0x72, \ + 0x58, 0x01, 0x94, 0x0c, \ + 0x58, 0x41, 0xea, 0x70, \ + 0x82, 0x41, 0xed, 0x70, \ + 0xeb, 0x70, 0x58, 0x01, \ + 0xe1, 0x00, 0x08, 0x0a, \ + 0xec, 0x70, 0x58, 0x01, \ + 0xee, 0x70, 0x58, 0x31, \ + 0x84, 0x21, 0x88, 0x11, \ + 0x00, 0x70, 0xf8, 0xf5, \ + 0xc0, 0x16, 0x67, 0x71, \ + 0x81, 0x11, 0x81, 0x91, \ + 0xe0, 0x10, 0x04, 0x06, \ + 0xef, 0xbc, 0x7c, 0x28, \ + 0x67, 0xe9, 0x4e, 0x95, \ + 0x4e, 0x95, 0xc0, 0x01, \ + 0xc0, 0x01, 0x67, 0xed, \ + 0x67, 0xf1, 0x4e, 0x95, \ + 0x00, 0x06, 0xc0, 0x01, \ + 0x4e, 0x03, 0xef, 0xf0, \ + 0xc0, 0x02, 0x6f, 0xe9, \ + 0xe0, 0x0c, 0x41, 0xf6, \ + 0x6f, 0xd9, 0x4e, 0x01, \ + 0x4e, 0x91, 0xc0, 0x05, \ + 0xc0, 0x06, 0x60, 0x2d, \ + 0x8c, 0x1b, 0x90, 0x0b, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0x21, 0x18, 0xf2, 0x1c, \ + 0x00, 0x0c, 0xff, 0xc0, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x84, 0x6b, 0x86, 0x5b, \ + 0xf0, 0xf0, 0x0c, 0x09, \ + 0xf8, 0xec, 0x0c, 0x29, \ + 0x0c, 0x39, 0x8a, 0x8b, \ + 0x50, 0x01, 0xf8, 0xe4, \ + 0x50, 0x21, 0xf1, 0x10, \ + 0x81, 0x01, 0xf9, 0x0c, \ + 0xf9, 0x08, 0x50, 0x01, \ + 0xf9, 0x04, 0x50, 0x31, \ + 0xe4, 0xc0, 0x14, 0x10, \ + 0xe6, 0x00, 0x16, 0x10, \ + 0xe0, 0x00, 0x52, 0x46, \ + 0xbc, 0x5e, 0x81, 0x71, \ + 0xf0, 0x9c, 0x40, 0x62, \ + 0xf8, 0x20, 0x00, 0x19, \ + 0x8a, 0xa5, 0xa1, 0x1b, \ + 0xfc, 0x1c, 0x0c, 0x19, \ + 0x8a, 0x25, 0xa1, 0x1b, \ + 0xa1, 0x1b, 0xac, 0x1a, \ + 0x00, 0x19, 0x88, 0xc5, \ + 0x44, 0x52, 0xf8, 0x60, \ + 0x44, 0x11, 0xf0, 0x9c, \ + 0x00, 0x19, 0xe0, 0x10, \ + 0x44, 0x11, 0xf8, 0x40, \ + 0x00, 0x09, 0xe0, 0x30, \ + 0x44, 0x01, 0xf8, 0x20, \ + 0x0c, 0x09, 0xe0, 0x50, \ + 0x44, 0x01, 0xfc, 0x1c, \ + 0xac, 0x2a, 0xe0, 0x70, \ + 0xaa, 0x14, 0xa8, 0x04, \ + 0xe1, 0x84, 0x06, 0x24, \ + 0x81, 0x31, 0x86, 0x03, \ + 0xf2, 0x8c, 0x42, 0x12, \ + 0x5a, 0xb8, 0x81, 0x31, \ + 0xbc, 0x54, 0xef, 0xbc, \ + 0xf0, 0xa4, 0x42, 0x52, \ + 0x16, 0x0b, 0x84, 0x0e, \ + 0x81, 0xf5, 0xf8, 0x20, \ + 0xf0, 0x9c, 0x42, 0x52, \ + 0x42, 0x01, 0x81, 0x01, \ + 0x42, 0x01, 0xe0, 0x10, \ + 0x42, 0x01, 0xe0, 0x30, \ + 0x44, 0x52, 0xe0, 0x50, \ + 0x42, 0x01, 0xf0, 0xa4, \ + 0x88, 0x0e, 0xe0, 0x70, \ + 0xb1, 0x7b, 0x82, 0x71, \ + 0x90, 0xa1, 0xe1, 0x0c, \ + 0x88, 0x91, 0x84, 0xb1, \ + 0x88, 0xf1, 0xee, 0x55, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xf8, 0x3c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0xa1, 0x2b, 0x86, 0x28, \ + 0x04, 0x00, 0x8c, 0x45, \ + 0x00, 0x00, 0xe4, 0xc0, \ + 0x81, 0x31, 0xe6, 0x00, \ + 0xe0, 0x04, 0x08, 0x46, \ + 0xe0, 0x04, 0x4b, 0x06, \ + 0x4c, 0x12, 0x81, 0xc1, \ + 0x0c, 0x79, 0xf0, 0xb0, \ + 0xa1, 0x7b, 0xe0, 0xd0, \ + 0x0c, 0x69, 0x86, 0x45, \ + 0xa1, 0x6b, 0xe0, 0xf0, \ + 0x02, 0x65, 0x84, 0xc5, \ + 0x88, 0x74, 0xfc, 0x10, \ + 0x8c, 0x73, 0x8a, 0x84, \ + 0xf2, 0x8c, 0x4c, 0x82, \ + 0xf0, 0x94, 0x50, 0x12, \ + 0xe0, 0x68, 0x10, 0x8b, \ + 0xf8, 0x20, 0x00, 0x9b, \ + 0x8a, 0x6e, 0x80, 0xaa, \ + 0x90, 0x93, 0x88, 0x7e, \ + 0x40, 0x93, 0x82, 0xa1, \ + 0x80, 0xac, 0xf8, 0x20, \ + 0x82, 0xc1, 0x86, 0x68, \ + 0x88, 0x41, 0xcd, 0xcd, \ + 0x84, 0x51, 0x90, 0x21, \ + 0xf6, 0x55, 0x84, 0x01, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf0, 0x3c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x84, 0x7b, 0x88, 0x6b, \ + 0x80, 0xab, 0x82, 0x9b, \ + 0xce, 0x08, 0x8e, 0x18, \ + 0xa3, 0xbf, 0x83, 0xb1, \ + 0xbe, 0x1e, 0xcc, 0x18, \ + 0x49, 0x18, 0x91, 0x11, \ + 0x80, 0x1b, 0xef, 0xf0, \ + 0x9e, 0x34, 0x9c, 0x04, \ + 0x10, 0x14, 0xfc, 0x1e, \ + 0x90, 0x1b, 0xe0, 0x90, \ + 0x50, 0xf8, 0x96, 0x2b, \ + 0x9e, 0x34, 0xef, 0xf0, \ + 0xe5, 0x0e, 0x42, 0x9a, \ + 0xe5, 0x8e, 0x44, 0x9a, \ + 0x02, 0x10, 0x81, 0x41, \ + 0xc0, 0x08, 0x64, 0x01, \ + 0xf2, 0x90, 0x44, 0x22, \ + 0x67, 0xfd, 0x05, 0x0a, \ + 0x98, 0x0e, 0xc0, 0x0f, \ + 0xff, 0x11, 0x80, 0x65, \ + 0xe8, 0x27, 0x81, 0x45, \ + 0xa4, 0x1f, 0xd9, 0x17, \ + 0xbe, 0x24, 0xc3, 0x0d, \ + 0x80, 0x75, 0xa5, 0xbf, \ + 0x98, 0x0e, 0xa2, 0x03, \ + 0x90, 0x1b, 0x9c, 0x04, \ + 0x4e, 0xf8, 0x96, 0x2b, \ + 0x9e, 0x34, 0xef, 0xf0, \ + 0xe5, 0x0e, 0x42, 0xaa, \ + 0xe5, 0x8e, 0x44, 0xaa, \ + 0x02, 0x10, 0x81, 0x41, \ + 0xc0, 0x08, 0x64, 0x01, \ + 0xf2, 0x90, 0x44, 0x22, \ + 0x67, 0xfd, 0x05, 0x0a, \ + 0x9a, 0x0e, 0xc0, 0x0f, \ + 0xff, 0x91, 0x80, 0xa5, \ + 0x82, 0x05, 0xd3, 0x0d, \ + 0xe8, 0x27, 0x81, 0x95, \ + 0x52, 0x12, 0xd9, 0x17, \ + 0xd3, 0x0d, 0xf9, 0x88, \ + 0xb2, 0x03, 0x80, 0x75, \ + 0x9c, 0x04, 0x9a, 0x0e, \ + 0xe0, 0x10, 0x0e, 0x1b, \ + 0x4c, 0xd8, 0x96, 0x2b, \ + 0x80, 0xab, 0xef, 0xf0, \ + 0xe0, 0x10, 0x0e, 0x0b, \ + 0xe5, 0x02, 0x42, 0x9a, \ + 0xe5, 0x82, 0x44, 0x9a, \ + 0xc6, 0x07, 0xfc, 0x04, \ + 0x82, 0x03, 0x81, 0x31, \ + 0x42, 0x22, 0xd8, 0xae, \ + 0x81, 0x31, 0xf2, 0x8c, \ + 0x4d, 0xd8, 0x90, 0x2b, \ + 0xff, 0x21, 0xef, 0xbc, \ + 0x88, 0xe5, 0x85, 0x0b, \ + 0x8a, 0x15, 0xa1, 0x1b, \ + 0x80, 0x75, 0xc1, 0xad, \ + 0xd8, 0xae, 0xa0, 0xa3, \ + 0x98, 0x24, 0x9a, 0x04, \ + 0xe0, 0xb0, 0x02, 0x04, \ + 0xe1, 0xd0, 0x06, 0x04, \ + 0xe1, 0x84, 0x00, 0x24, \ + 0x82, 0x03, 0x81, 0x41, \ + 0xf2, 0x90, 0x42, 0x32, \ + 0x4c, 0x58, 0x81, 0x31, \ + 0x00, 0x8a, 0xef, 0xbc, \ + 0xcf, 0xff, 0x67, 0xfd, \ + 0x41, 0xfe, 0x80, 0x95, \ + 0xc0, 0x03, 0x7f, 0xfd, \ + 0x80, 0x85, 0xa1, 0x1b, \ + 0x7f, 0xfd, 0x41, 0xfe, \ + 0xda, 0x0e, 0xc0, 0x03, \ + 0x0c, 0x0c, 0x98, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xa1, 0x1b, 0xff, 0x01, \ + 0xff, 0x01, 0xf6, 0x45, \ + 0xf6, 0x25, 0xc1, 0xad, \ + 0xc1, 0xc3, 0xfb, 0xa5, \ + 0xfe, 0x3c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x88, 0xab, 0x8a, 0x6b, \ + 0x82, 0x7b, 0x86, 0x8b, \ + 0xde, 0x34, 0x80, 0x9b, \ + 0xbc, 0x0e, 0x81, 0x01, \ + 0x90, 0x1b, 0x84, 0x0b, \ + 0x44, 0x98, 0x94, 0x2b, \ + 0x80, 0x1b, 0xe8, 0x00, \ + 0x4e, 0x0a, 0xfb, 0x01, \ + 0x4e, 0x1a, 0xe9, 0x9c, \ + 0x13, 0x2a, 0xe1, 0x9c, \ + 0x00, 0xc8, 0xe0, 0x0c, \ + 0xc0, 0x00, 0x40, 0x01, \ + 0xec, 0x04, 0x04, 0x90, \ + 0xe4, 0x08, 0x04, 0xaa, \ + 0x40, 0x01, 0x01, 0x58, \ + 0x88, 0x71, 0x80, 0x00, \ + 0x12, 0x2a, 0x83, 0xa5, \ + 0x01, 0x68, 0xe0, 0x10, \ + 0x80, 0x00, 0x40, 0x01, \ + 0xe0, 0x14, 0x12, 0x2a, \ + 0x40, 0x01, 0x00, 0xe8, \ + 0x01, 0xd8, 0x80, 0x00, \ + 0x81, 0x11, 0xa0, 0x00, \ + 0x86, 0x95, 0x91, 0x24, \ + 0x12, 0x2a, 0x83, 0xc5, \ + 0x3f, 0x88, 0xe0, 0x0c, \ + 0xc3, 0xff, 0x5f, 0xfd, \ + 0x82, 0x8b, 0x90, 0x71, \ + 0x94, 0x8b, 0x81, 0x25, \ + 0x9e, 0x3b, 0x9c, 0x24, \ + 0x8c, 0x1b, 0x90, 0x0b, \ + 0xef, 0xf0, 0x46, 0xe8, \ + 0xe0, 0x00, 0x00, 0x1a, \ + 0x4e, 0x0a, 0x81, 0x01, \ + 0x3e, 0x88, 0xe1, 0x9c, \ + 0xc3, 0xff, 0x3f, 0xfd, \ + 0x91, 0x24, 0xbc, 0x14, \ + 0x81, 0x11, 0x80, 0x45, \ + 0x88, 0xf1, 0x88, 0x1e, \ + 0xf6, 0x3c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe5, 0x0e, 0x48, 0x2a, \ + 0xe5, 0x8e, 0x44, 0x2a, \ + 0x08, 0x40, 0x81, 0x31, \ + 0xc0, 0x08, 0x64, 0x01, \ + 0xf2, 0x8c, 0x44, 0x22, \ + 0xe0, 0xd0, 0x0a, 0x24, \ + 0xaa, 0x4f, 0xd9, 0x47, \ + 0xff, 0x41, 0x88, 0x03, \ + 0xd9, 0x27, 0x89, 0x0b, \ + 0xf2, 0x8c, 0x44, 0x22, \ + 0xff, 0x01, 0x80, 0x55, \ + 0x80, 0x45, 0xa1, 0x2b, \ + 0x42, 0x12, 0xff, 0x01, \ + 0x40, 0x12, 0xfa, 0x80, \ + 0xdd, 0x93, 0xf1, 0x80, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xf0, 0x1e, 0xf0, \ + 0x84, 0x9b, 0x86, 0x6b, \ + 0x8b, 0x0e, 0x8d, 0x1e, \ + 0x42, 0x0a, 0xc2, 0x04, \ + 0x04, 0x14, 0xe5, 0x82, \ + 0x46, 0x06, 0xf6, 0x80, \ + 0xa4, 0x33, 0xe0, 0x00, \ + 0xe5, 0x02, 0x40, 0x0a, \ + 0xec, 0x60, 0x14, 0x32, \ + 0x50, 0x38, 0x94, 0x2b, \ + 0xfc, 0x0e, 0xef, 0xbc, \ + 0xa1, 0x0b, 0x98, 0x08, \ + 0x80, 0x00, 0x12, 0x48, \ + 0xb4, 0x03, 0xb1, 0x01, \ + 0x00, 0x00, 0xbe, 0x0e, \ + 0x83, 0x11, 0xec, 0x04, \ + 0xbc, 0x1e, 0xa1, 0x1f, \ + 0x65, 0x21, 0x00, 0x90, \ + 0x89, 0x0e, 0xc0, 0x05, \ + 0x81, 0x91, 0x81, 0x71, \ + 0x9a, 0x28, 0xfe, 0x6e, \ + 0xf0, 0xa4, 0x40, 0x62, \ + 0xe0, 0xd0, 0x00, 0x19, \ + 0xe0, 0xf0, 0x00, 0x59, \ + 0x9d, 0x7e, 0xc5, 0x9d, \ + 0x99, 0x7e, 0x9b, 0x7e, \ + 0x42, 0x5a, 0x97, 0x1e, \ + 0x91, 0x1e, 0xe6, 0x86, \ + 0x80, 0x00, 0x0e, 0xc8, \ + 0xa1, 0x1b, 0x91, 0x14, \ + 0x80, 0x00, 0x0e, 0x88, \ + 0x8f, 0x9e, 0xa1, 0x5b, \ + 0x00, 0x29, 0x8c, 0xc5, \ + 0x00, 0x19, 0xe0, 0xb0, \ + 0x9d, 0x01, 0xe0, 0x90, \ + 0xe6, 0x82, 0x40, 0x2a, \ + 0x82, 0x43, 0x97, 0x44, \ + 0x82, 0x03, 0x93, 0x1e, \ + 0x95, 0x5e, 0x84, 0x53, \ + 0x89, 0x74, 0xc4, 0x07, \ + 0x81, 0xb1, 0x80, 0x73, \ + 0x81, 0x91, 0x81, 0x51, \ + 0x81, 0x81, 0x81, 0xa1, \ + 0xa1, 0x0b, 0x97, 0x04, \ + 0x8e, 0xcb, 0x84, 0x65, \ + 0x81, 0x11, 0x93, 0x34, \ + 0x18, 0x05, 0xf1, 0x64, \ + 0x8c, 0xa3, 0xe2, 0x2c, \ + 0xf2, 0x84, 0x50, 0x82, \ + 0x52, 0x92, 0x80, 0x53, \ + 0x18, 0x05, 0xf2, 0x84, \ + 0x82, 0x31, 0xe4, 0x5c, \ + 0x80, 0xb3, 0xc9, 0x3d, \ + 0x82, 0x21, 0xfc, 0x55, \ + 0xc1, 0x2d, 0x95, 0x04, \ + 0xf8, 0xf5, 0xf0, 0x71, \ + 0xff, 0x01, 0xa1, 0x9b, \ + 0x12, 0x4a, 0x82, 0x45, \ + 0x81, 0x85, 0xe0, 0x00, \ + 0x81, 0x51, 0x81, 0xb1, \ + 0x81, 0x81, 0x81, 0xa1, \ + 0x81, 0x65, 0x8b, 0x94, \ + 0xfe, 0x64, 0x81, 0x5b, \ + 0x81, 0x71, 0x8b, 0x94, \ + 0x8a, 0x0b, 0x80, 0x45, \ + 0x06, 0xf0, 0xa4, 0x24, \ + 0x97, 0x0e, 0xe4, 0xd0, \ + 0x7a, 0xf8, 0x96, 0x1b, \ + 0xa1, 0x0b, 0xef, 0xec, \ + 0x9b, 0x7e, 0x80, 0x65, \ + 0x9b, 0x04, 0x81, 0xb5, \ + 0x60, 0x01, 0x02, 0x06, \ + 0xc3, 0x0d, 0xd0, 0x00, \ + 0x41, 0xfe, 0x80, 0xb5, \ + 0xcf, 0xff, 0x7f, 0xfd, \ + 0xa6, 0x04, 0x9b, 0x0e, \ + 0x97, 0x34, 0xa4, 0x24, \ + 0xe5, 0x02, 0x42, 0x3a, \ + 0xe5, 0x82, 0x46, 0x3a, \ + 0xe1, 0x84, 0x00, 0x24, \ + 0x42, 0x32, 0x82, 0x03, \ + 0x81, 0x31, 0xf2, 0x9c, \ + 0xef, 0xb8, 0x78, 0xc8, \ + 0x80, 0x85, 0x83, 0x8b, \ + 0x80, 0x95, 0xc2, 0x8d, \ + 0xc0, 0xad, 0x85, 0x25, \ + 0x40, 0xa2, 0x88, 0x05, \ + 0xff, 0x21, 0xf1, 0x80, \ + 0x42, 0x82, 0x85, 0x0b, \ + 0x80, 0x55, 0xf3, 0x84, \ + 0xa1, 0x1b, 0xff, 0x01, \ + 0xff, 0x01, 0x80, 0x45, \ + 0x06, 0xf0, 0xe4, 0x24, \ + 0x96, 0x1b, 0xe4, 0xe0, \ + 0xef, 0xec, 0x77, 0xa8, \ + 0x80, 0x65, 0xa1, 0x0b, \ + 0x81, 0xb5, 0x9d, 0x7e, \ + 0x02, 0x06, 0x9d, 0x04, \ + 0xd0, 0x00, 0x60, 0x01, \ + 0x80, 0xb5, 0xc3, 0x0d, \ + 0x7f, 0xfd, 0x41, 0xfe, \ + 0x9d, 0x0e, 0xcf, 0xff, \ + 0x00, 0x09, 0x8d, 0x04, \ + 0xab, 0x0b, 0xe0, 0x40, \ + 0x80, 0xc5, 0x8f, 0x94, \ + 0x00, 0x09, 0x8d, 0x04, \ + 0xab, 0x0b, 0xe0, 0x44, \ + 0x8d, 0x04, 0x92, 0xd5, \ + 0xe0, 0x74, 0x00, 0x09, \ + 0xe4, 0xa4, 0x40, 0x0a, \ + 0xfc, 0x04, 0x92, 0x05, \ + 0x52, 0x1a, 0x97, 0x14, \ + 0x44, 0x1a, 0xe5, 0x02, \ + 0x81, 0x11, 0xe5, 0x82, \ + 0x92, 0x03, 0xbc, 0x04, \ + 0x42, 0x22, 0x97, 0x2e, \ + 0xbe, 0x74, 0xf2, 0x84, \ + 0x42, 0xb8, 0x8e, 0x2b, \ + 0xa1, 0x7b, 0xef, 0xbc, \ + 0x80, 0x9b, 0x80, 0x45, \ + 0x80, 0x45, 0x97, 0x24, \ + 0xff, 0x01, 0x82, 0x2b, \ + 0xe0, 0x00, 0x54, 0x4a, \ + 0xe0, 0x00, 0x10, 0x1a, \ + 0xe1, 0x80, 0x42, 0x0a, \ + 0x80, 0x45, 0x81, 0x71, \ + 0x80, 0x45, 0xff, 0xa1, \ + 0x94, 0x93, 0x81, 0x81, \ + 0x40, 0x22, 0x81, 0x9b, \ + 0x80, 0x55, 0xf2, 0xa0, \ + 0xa1, 0x0b, 0xff, 0x91, \ + 0xff, 0x91, 0x80, 0x45, \ + 0xc0, 0x24, 0x8b, 0x04, \ + 0xf0, 0x0a, 0x40, 0x9a, \ + 0xe4, 0xc0, 0x06, 0xf0, \ + 0x72, 0x78, 0x96, 0x1b, \ + 0xa1, 0x0b, 0xef, 0xec, \ + 0x41, 0xfe, 0x80, 0xe5, \ + 0xcf, 0xff, 0x7f, 0xfd, \ + 0x8f, 0x94, 0x99, 0x0e, \ + 0x99, 0x04, 0x81, 0xc5, \ + 0x60, 0x01, 0x02, 0x06, \ + 0xc3, 0x0d, 0xd0, 0x00, \ + 0x80, 0xb5, 0x8f, 0x94, \ + 0x7f, 0xfd, 0x41, 0xfe, \ + 0x99, 0x0e, 0xcf, 0xff, \ + 0x02, 0x94, 0x8c, 0x0b, \ + 0x00, 0x29, 0xe0, 0x88, \ + 0x9d, 0x34, 0xf1, 0x10, \ + 0x9b, 0x44, 0x80, 0x13, \ + 0x18, 0x94, 0x99, 0x54, \ + 0x82, 0x91, 0xe0, 0x84, \ + 0xc5, 0x9d, 0x84, 0x3e, \ + 0xe0, 0x20, 0x42, 0x45, \ + 0x42, 0x55, 0x98, 0x03, \ + 0x91, 0x14, 0xe0, 0x40, \ + 0xe0, 0xc0, 0x40, 0x13, \ + 0xdf, 0xfc, 0x2e, 0xf8, \ + 0x0c, 0x0c, 0xf8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0xec, 0x60, 0x1e, 0xf0, \ + 0x98, 0x18, 0x82, 0x6b, \ + 0xa1, 0x1b, 0x81, 0x71, \ + 0x89, 0x7e, 0x8b, 0x7e, \ + 0xfc, 0x7e, 0xfe, 0x7e, \ + 0x10, 0x00, 0x92, 0x65, \ + 0x12, 0x60, 0xe4, 0xe0, \ + 0x55, 0xc6, 0xe4, 0x90, \ + 0x9a, 0x08, 0xe0, 0x00, \ + 0xec, 0xf0, 0x02, 0xa0, \ + 0x8b, 0x7e, 0xc1, 0x1d, \ + 0xfe, 0x7e, 0xfc, 0x7e, \ + 0x8a, 0x45, 0x89, 0x7e, \ + 0xf8, 0x80, 0x12, 0x09, \ + 0xf8, 0x40, 0x12, 0x19, \ + 0xf8, 0x60, 0x10, 0x39, \ + 0xf8, 0x20, 0x10, 0x49, \ + 0xe4, 0x20, 0x04, 0xf0, \ + 0xe4, 0x50, 0x0a, 0xf0, \ + 0x9b, 0x21, 0xbc, 0x2e, \ + 0xe8, 0x00, 0x43, 0x28, \ + 0xf8, 0x60, 0x12, 0x09, \ + 0xf8, 0x20, 0x12, 0x19, \ + 0xf8, 0x40, 0x10, 0x39, \ + 0x04, 0xf0, 0xa0, 0x48, \ + 0x0a, 0xf0, 0xe4, 0x40, \ + 0xbc, 0x2e, 0xe4, 0x30, \ + 0x42, 0x38, 0x93, 0x21, \ + 0xfc, 0x04, 0xe8, 0x00, \ + 0x80, 0x85, 0xa1, 0x0b, \ + 0xa1, 0x0b, 0x89, 0x04, \ + 0x8b, 0x7e, 0x80, 0xb5, \ + 0xfc, 0x7e, 0xfe, 0x7e, \ + 0x8b, 0x04, 0x89, 0x7e, \ + 0xfe, 0x04, 0xe5, 0x06, \ + 0xe0, 0x1c, 0x52, 0x01, \ + 0x52, 0x01, 0xfc, 0x04, \ + 0x89, 0x04, 0xe0, 0x3c, \ + 0xe0, 0x5c, 0x52, 0x01, \ + 0x02, 0xa0, 0x98, 0x08, \ + 0xc1, 0x1d, 0xec, 0xec, \ + 0x82, 0x81, 0x82, 0xa1, \ + 0xb0, 0xf1, 0xee, 0xb5, \ + 0xf2, 0x3c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x58, 0x12, 0x80, 0x33, \ + 0x40, 0x32, 0xf0, 0x80, \ + 0x99, 0x0d, 0xf0, 0x90, \ + 0xec, 0x04, 0x00, 0x00, \ + 0x80, 0x75, 0x80, 0x1b, \ + 0xec, 0x04, 0x02, 0xc0, \ + 0xfb, 0x04, 0x44, 0x22, \ + 0xbc, 0x14, 0x84, 0x3d, \ + 0x81, 0x51, 0x94, 0x3e, \ + 0x84, 0x0d, 0x82, 0x15, \ + 0x80, 0xa5, 0x88, 0x5b, \ + 0xa6, 0x03, 0x83, 0x01, \ + 0xf0, 0x88, 0x4a, 0x02, \ + 0xdd, 0x93, 0x84, 0x5e, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x82, 0x7b, 0x84, 0x6b, \ + 0xda, 0x14, 0xd8, 0x04, \ + 0x88, 0x2b, 0x8d, 0x84, \ + 0x8e, 0x4b, 0x8a, 0x3b, \ + 0xef, 0xfc, 0x52, 0x08, \ + 0x81, 0x01, 0x91, 0x91, \ + 0x86, 0xf5, 0xa1, 0x0b, \ + 0xe0, 0x40, 0x0c, 0x09, \ + 0xe0, 0x40, 0x10, 0x35, \ + 0xe0, 0x60, 0x10, 0x45, \ + 0xe0, 0x80, 0x10, 0x55, \ + 0x81, 0x21, 0xbc, 0x8e, \ + 0x5a, 0xf8, 0x8e, 0x1b, \ + 0xa1, 0x0b, 0xef, 0xfc, \ + 0x0c, 0x09, 0x84, 0x15, \ + 0x10, 0x35, 0xe0, 0x44, \ + 0x10, 0x45, 0xe0, 0x40, \ + 0x10, 0x55, 0xe0, 0x60, \ + 0x02, 0x80, 0xe0, 0x80, \ + 0xbc, 0x1e, 0xe4, 0x80, \ + 0x8e, 0x1b, 0x81, 0x21, \ + 0xef, 0xfc, 0x59, 0xe8, \ + 0xec, 0x04, 0x12, 0x90, \ + 0x88, 0x81, 0xa1, 0x9b, \ + 0x88, 0xf1, 0xf6, 0x95, \ + 0xf2, 0x1c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x82, 0x7b, 0x84, 0x6b, \ + 0x91, 0x34, 0x8f, 0x24, \ + 0x9a, 0x14, 0x98, 0x04, \ + 0x8e, 0x4b, 0x93, 0x84, \ + 0xef, 0xfc, 0x4e, 0x48, \ + 0xe0, 0x08, 0x12, 0xc6, \ + 0xa1, 0x0b, 0x81, 0x01, \ + 0x0c, 0x09, 0x88, 0x15, \ + 0x10, 0x35, 0xe0, 0x40, \ + 0x10, 0x45, 0xe4, 0x60, \ + 0x10, 0x55, 0xe6, 0x90, \ + 0xbc, 0x8e, 0xe8, 0xc0, \ + 0x8e, 0x1b, 0x81, 0x21, \ + 0xef, 0xfc, 0x57, 0x28, \ + 0x84, 0x35, 0xa1, 0x0b, \ + 0xe0, 0x44, 0x0c, 0x09, \ + 0xe4, 0x60, 0x10, 0x35, \ + 0xe6, 0x90, 0x10, 0x45, \ + 0xe8, 0xc0, 0x10, 0x55, \ + 0x64, 0xc1, 0x02, 0x80, \ + 0xbc, 0x1e, 0xc0, 0x02, \ + 0x8e, 0x1b, 0x81, 0x21, \ + 0xef, 0xfc, 0x56, 0x08, \ + 0xec, 0x04, 0x12, 0x90, \ + 0x88, 0x81, 0xa1, 0x9b, \ + 0x88, 0xf1, 0xf6, 0x75, \ + 0xf2, 0x1c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x84, 0x8b, 0x8a, 0x6b, \ + 0x80, 0xbb, 0x82, 0x9b, \ + 0x08, 0x70, 0x99, 0x74, \ + 0x0a, 0x70, 0xe4, 0x10, \ + 0x00, 0x70, 0xe4, 0x40, \ + 0x9b, 0xa4, 0xe4, 0x50, \ + 0x77, 0x78, 0xbc, 0x0e, \ + 0xa1, 0x0b, 0xef, 0xfc, \ + 0x0e, 0x19, 0x80, 0xa5, \ + 0xea, 0x16, 0xe3, 0x8c, \ + 0x00, 0x70, 0x83, 0x35, \ + 0x02, 0x70, 0xe7, 0x60, \ + 0x04, 0x70, 0xe7, 0xa0, \ + 0xfe, 0x1e, 0xe7, 0x90, \ + 0xbe, 0x0e, 0xfc, 0x2e, \ + 0x90, 0x2b, 0x92, 0x1b, \ + 0xef, 0xfc, 0x7a, 0x08, \ + 0xe3, 0x8c, 0x0e, 0x19, \ + 0xea, 0x16, 0xa1, 0x0b, \ + 0xa8, 0xf1, 0x80, 0xa5, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x96, 0x0b, 0xdd, 0x93, \ + 0x90, 0x2b, 0x92, 0x1b, \ + 0x8e, 0x4b, 0x8c, 0x3b, \ + 0xa8, 0xf1, 0x94, 0x5b, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0x81, 0x45, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x88, 0x8b, 0x8a, 0x6b, \ + 0x84, 0x9b, 0x86, 0x7b, \ + 0x92, 0x0b, 0x82, 0xab, \ + 0x6c, 0xb8, 0x8c, 0x1b, \ + 0x94, 0x0b, 0xef, 0xfc, \ + 0x90, 0x2b, 0x92, 0x1b, \ + 0x58, 0x18, 0x8c, 0x3b, \ + 0x10, 0x09, 0xef, 0xfc, \ + 0x00, 0x00, 0xe3, 0x58, \ + 0xe2, 0x0d, 0xec, 0x0c, \ + 0x8e, 0x0b, 0x80, 0xb5, \ + 0x42, 0x38, 0x8c, 0x1b, \ + 0x81, 0x01, 0xef, 0xfc, \ + 0xf2, 0x3c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x82, 0x05, 0xa1, 0x2b, \ + 0x04, 0x20, 0x81, 0x31, \ + 0xa1, 0x2b, 0xec, 0x04, \ + 0xc5, 0x3e, 0xc1, 0x3e, \ + 0xdd, 0x93, 0xfe, 0x75, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xf8, 0x3c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x6e, 0x01, 0x04, 0x1b, \ + 0xa1, 0x1b, 0xc0, 0x01, \ + 0x02, 0x20, 0x8a, 0x45, \ + 0xc0, 0x06, 0x64, 0x01, \ + 0x64, 0x01, 0x18, 0x20, \ + 0x81, 0x61, 0xc0, 0x0e, \ + 0xb0, 0x88, 0x84, 0x7a, \ + 0xc4, 0x77, 0xc4, 0x87, \ + 0xfc, 0x20, 0x08, 0xa5, \ + 0xfc, 0x1c, 0x00, 0x95, \ + 0x80, 0x65, 0xd3, 0xad, \ + 0xa0, 0x9e, 0x88, 0x83, \ + 0xe0, 0x30, 0x06, 0x89, \ + 0x82, 0x65, 0xa1, 0x8b, \ + 0x80, 0x73, 0xb0, 0x88, \ + 0xc4, 0x87, 0x9c, 0x74, \ + 0xfc, 0x20, 0x0a, 0x95, \ + 0x8a, 0x83, 0xcf, 0x77, \ + 0xa0, 0x9e, 0x8e, 0x93, \ + 0x6e, 0x01, 0x04, 0x7b, \ + 0x82, 0x61, 0xc0, 0x01, \ + 0x84, 0x11, 0xcf, 0x6d, \ + 0xf6, 0xf5, 0x82, 0xc1, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf0, 0x3c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0x6c, 0x09, 0x02, 0x09, \ + 0xa1, 0x0b, 0xc0, 0x03, \ + 0x04, 0x09, 0x8c, 0x25, \ + 0x04, 0xc9, 0xe0, 0x34, \ + 0x02, 0x69, 0xe0, 0x30, \ + 0xc0, 0x03, 0x6c, 0x09, \ + 0x67, 0x11, 0x02, 0x10, \ + 0x81, 0x71, 0xc0, 0x0f, \ + 0x8c, 0x94, 0x81, 0x81, \ + 0xe5, 0x02, 0x54, 0x9a, \ + 0xe5, 0x82, 0x52, 0x9a, \ + 0xfb, 0xfc, 0x18, 0x0a, \ + 0xd1, 0xa7, 0xf0, 0x97, \ + 0x94, 0x9e, 0xb4, 0x9f, \ + 0x90, 0x84, 0x82, 0x85, \ + 0x56, 0x8a, 0xca, 0xaa, \ + 0x50, 0x8a, 0xe5, 0x2a, \ + 0xee, 0x87, 0xe5, 0xaa, \ + 0xb6, 0x8f, 0xd3, 0xb7, \ + 0x94, 0x9e, 0x90, 0x93, \ + 0x42, 0x95, 0xcd, 0x94, \ + 0xd1, 0x94, 0xf8, 0xc0, \ + 0xf8, 0x80, 0x42, 0x95, \ + 0x82, 0x71, 0xd5, 0x94, \ + 0x42, 0x95, 0xcd, 0x7d, \ + 0xc5, 0x8e, 0xf8, 0x40, \ + 0x0c, 0x0c, 0xf6, 0xb5, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xf8, 0x0c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x6e, 0x01, 0x04, 0x1b, \ + 0xa1, 0x1b, 0xc0, 0x01, \ + 0x02, 0x20, 0x88, 0x05, \ + 0xc0, 0x06, 0x64, 0x01, \ + 0x64, 0x01, 0x08, 0x20, \ + 0x81, 0x51, 0xc0, 0x0e, \ + 0x84, 0x6a, 0x99, 0xc1, \ + 0x10, 0x64, 0x90, 0x78, \ + 0xc4, 0x77, 0xe0, 0x88, \ + 0xfc, 0x20, 0x00, 0x85, \ + 0xfc, 0x1c, 0x06, 0x75, \ + 0x80, 0xa5, 0xcf, 0x8d, \ + 0x4c, 0xc1, 0x80, 0x63, \ + 0xc0, 0x04, 0x6c, 0x01, \ + 0x6e, 0x01, 0x04, 0x6b, \ + 0x82, 0x51, 0xc0, 0x01, \ + 0x84, 0x11, 0xcd, 0x5d, \ + 0xfa, 0x55, 0x82, 0x41, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf0, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xef, 0x10, 0x1e, 0xf0, \ + 0x86, 0x7b, 0x88, 0x6b, \ + 0x80, 0x9b, 0x84, 0x8b, \ + 0x9f, 0x01, 0xbc, 0x9e, \ + 0x50, 0x05, 0xa2, 0x0e, \ + 0x0e, 0x29, 0xe0, 0x14, \ + 0xc0, 0x03, 0x6c, 0x09, \ + 0xe6, 0x10, 0x14, 0xf0, \ + 0xe5, 0x10, 0x16, 0xf0, \ + 0x96, 0x1b, 0x94, 0x0b, \ + 0xef, 0xfc, 0x73, 0xe8, \ + 0xa2, 0x0e, 0xa1, 0x01, \ + 0xe0, 0x14, 0x50, 0x05, \ + 0x8e, 0x2b, 0x92, 0x0b, \ + 0x94, 0x4b, 0x8c, 0x3b, \ + 0x73, 0xf8, 0x96, 0x5b, \ + 0xa3, 0x01, 0xef, 0xfc, \ + 0x12, 0xf0, 0xa2, 0x0e, \ + 0x50, 0x05, 0xe4, 0x10, \ + 0x8e, 0x1b, 0xe0, 0x14, \ + 0x94, 0x3b, 0x8c, 0x2b, \ + 0x92, 0x5b, 0x96, 0x4b, \ + 0xef, 0xfc, 0x76, 0xa8, \ + 0xa2, 0x0e, 0xa5, 0x01, \ + 0xe0, 0x14, 0x50, 0x05, \ + 0x8e, 0x2b, 0xbc, 0x04, \ + 0x79, 0xd8, 0x92, 0x3b, \ + 0x1e, 0xf0, 0xef, 0xfc, \ + 0x0c, 0x0c, 0xe7, 0x10, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x02, 0x09, 0x80, 0x1b, \ + 0xa1, 0x0b, 0xe0, 0x2c, \ + 0x82, 0x45, 0x84, 0x02, \ + 0xe6, 0x8a, 0x40, 0x0a, \ + 0xe0, 0xf8, 0x44, 0x06, \ + 0xe0, 0x8a, 0x40, 0x0a, \ + 0x40, 0x02, 0x86, 0x12, \ + 0xdd, 0x93, 0xfa, 0x04, \ + 0x93, 0x11, 0xa4, 0x3d, \ + 0x44, 0x15, 0x8a, 0x1e, \ + 0x80, 0xf5, 0xe0, 0x14, \ + 0x84, 0x85, 0xa3, 0x3b, \ + 0x86, 0x15, 0xa5, 0x3b, \ + 0x83, 0x15, 0xbe, 0x44, \ + 0x82, 0xe5, 0xa7, 0x3b, \ + 0x84, 0x55, 0xa9, 0x3b, \ + 0x42, 0x02, 0xfc, 0x44, \ + 0x42, 0x41, 0xf0, 0x94, \ + 0xc0, 0x04, 0x60, 0x01, \ + 0x00, 0x00, 0xd1, 0x47, \ + 0xc0, 0x11, 0x64, 0x01, \ + 0xbc, 0x44, 0x81, 0x55, \ + 0x64, 0x01, 0x00, 0x00, \ + 0x8a, 0x03, 0xc0, 0x10, \ + 0xdd, 0x93, 0x80, 0x46, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xd0, 0x1e, 0xf0, \ + 0x86, 0x6b, 0x88, 0x7b, \ + 0x8f, 0x0e, 0x84, 0x8b, \ + 0xa2, 0x0e, 0x87, 0x01, \ + 0xe0, 0x14, 0x50, 0x05, \ + 0x64, 0x01, 0x00, 0x60, \ + 0x81, 0x11, 0xc0, 0x04, \ + 0x82, 0x11, 0xc1, 0x1c, \ + 0x60, 0x01, 0x02, 0x0a, \ + 0xfe, 0x75, 0xc0, 0x01, \ + 0xa2, 0x0e, 0x89, 0x01, \ + 0xe0, 0x14, 0x50, 0x05, \ + 0x64, 0x01, 0x02, 0x60, \ + 0x12, 0x60, 0xc0, 0x04, \ + 0xc0, 0x06, 0x64, 0x01, \ + 0xe0, 0x10, 0x04, 0x06, \ + 0x92, 0x3b, 0x8c, 0x0b, \ + 0xe8, 0x00, 0x54, 0x88, \ + 0xa1, 0x0b, 0xa0, 0x04, \ + 0x8b, 0x7e, 0xae, 0xf5, \ + 0x6e, 0x01, 0x0c, 0x0b, \ + 0xa1, 0x0b, 0xc0, 0x01, \ + 0x99, 0x0e, 0x81, 0x01, \ + 0x97, 0x0e, 0x81, 0xa1, \ + 0xaa, 0xe5, 0x92, 0x2b, \ + 0x64, 0x01, 0x00, 0x60, \ + 0x95, 0x0e, 0xc0, 0x0e, \ + 0x64, 0x01, 0x12, 0x60, \ + 0x81, 0xb1, 0xc0, 0x0c, \ + 0x81, 0x01, 0x83, 0x31, \ + 0x81, 0x01, 0x8d, 0x0e, \ + 0x81, 0xa1, 0xfe, 0x0e, \ + 0x89, 0x95, 0x89, 0x6e, \ + 0xf4, 0x3c, 0x08, 0x8a, \ + 0x91, 0x01, 0x82, 0xd5, \ + 0x50, 0x05, 0xa2, 0x0e, \ + 0x8d, 0x24, 0xe0, 0x14, \ + 0x81, 0x01, 0x82, 0x21, \ + 0x9f, 0x11, 0x91, 0x0e, \ + 0x90, 0x7b, 0x89, 0x64, \ + 0x8d, 0xd5, 0x8b, 0x84, \ + 0x40, 0x62, 0x97, 0x04, \ + 0x00, 0x04, 0xf1, 0x80, \ + 0xfe, 0x14, 0xf0, 0x80, \ + 0x8b, 0x84, 0x90, 0x7b, \ + 0x84, 0xc5, 0x83, 0x0d, \ + 0xe0, 0x28, 0x10, 0x19, \ + 0x9e, 0x0e, 0x8d, 0x01, \ + 0xe4, 0xc0, 0x04, 0xf0, \ + 0xe4, 0xb0, 0x06, 0xf0, \ + 0xe0, 0x14, 0x4e, 0x05, \ + 0x8a, 0x6b, 0x8c, 0x0b, \ + 0xe8, 0x00, 0x48, 0x28, \ + 0x94, 0x1b, 0x8c, 0x5b, \ + 0x82, 0x21, 0x8d, 0x24, \ + 0x91, 0x0e, 0x81, 0x01, \ + 0x10, 0x19, 0x89, 0xd5, \ + 0x8f, 0x01, 0xe0, 0x28, \ + 0x4e, 0x05, 0x9e, 0x0e, \ + 0x04, 0xf0, 0xe0, 0x14, \ + 0x06, 0xf0, 0xe4, 0xc0, \ + 0x82, 0x41, 0xe4, 0xb0, \ + 0x8c, 0x0b, 0x8d, 0x4e, \ + 0x47, 0x78, 0x8a, 0xab, \ + 0x91, 0x0e, 0xe8, 0x00, \ + 0x8c, 0x2b, 0x90, 0x0b, \ + 0xef, 0xfc, 0x74, 0x68, \ + 0x8d, 0x14, 0x94, 0x5b, \ + 0x87, 0x35, 0xfe, 0x0e, \ + 0x0a, 0x8a, 0x88, 0x5a, \ + 0xc0, 0x01, 0x64, 0x05, \ + 0xa2, 0x0e, 0x95, 0x01, \ + 0xe0, 0x14, 0x50, 0x05, \ + 0xfb, 0x01, 0x80, 0xd5, \ + 0x4d, 0xf6, 0xa0, 0x0e, \ + 0x81, 0x85, 0xe7, 0xfc, \ + 0xe0, 0x84, 0x00, 0x54, \ + 0x82, 0x03, 0x8f, 0x14, \ + 0x6c, 0x01, 0x00, 0x63, \ + 0x94, 0x4b, 0xc0, 0x01, \ + 0xfb, 0xfc, 0x06, 0x0a, \ + 0xe6, 0xe5, 0x93, 0x2e, \ + 0x8b, 0x84, 0x90, 0x7b, \ + 0xe0, 0x28, 0x10, 0x19, \ + 0x9e, 0x0e, 0x8b, 0x01, \ + 0xe4, 0xc0, 0x04, 0xf0, \ + 0xe4, 0xb0, 0x06, 0xf0, \ + 0xe0, 0x14, 0x4e, 0x05, \ + 0x8a, 0xab, 0x8c, 0x0b, \ + 0xe8, 0x00, 0x44, 0x48, \ + 0x90, 0x0b, 0x91, 0x0e, \ + 0x71, 0x38, 0x8c, 0x2b, \ + 0x94, 0x5b, 0xef, 0xfc, \ + 0x81, 0x11, 0xfe, 0x0e, \ + 0x89, 0x64, 0x81, 0x21, \ + 0xc1, 0x16, 0x95, 0x04, \ + 0x97, 0x04, 0x95, 0x0e, \ + 0x10, 0x39, 0xe5, 0x2c, \ + 0x52, 0x03, 0xe0, 0x3c, \ + 0xfc, 0x0e, 0xe8, 0x04, \ + 0x00, 0x24, 0x8d, 0x2e, \ + 0x82, 0xab, 0xfb, 0x80, \ + 0xfa, 0x80, 0x02, 0x14, \ + 0xfb, 0x80, 0x08, 0xb4, \ + 0xbc, 0x1e, 0xbe, 0x0e, \ + 0x8e, 0x2b, 0x8f, 0x04, \ + 0xef, 0xfc, 0x70, 0x68, \ + 0x6e, 0x01, 0x0c, 0x0b, \ + 0x82, 0xb1, 0xc0, 0x01, \ + 0x93, 0x24, 0xc1, 0xbd, \ + 0x8e, 0x8b, 0x84, 0x21, \ + 0xea, 0x75, 0x91, 0x34, \ + 0xe4, 0x04, 0x00, 0xa0, \ + 0x6c, 0x09, 0x4c, 0x01, \ + 0xe8, 0xf1, 0xc0, 0x03, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x0a, 0x44, 0x88, 0x44, \ + 0xaa, 0x43, 0xe9, 0x04, \ + 0x88, 0x4e, 0x80, 0x43, \ + 0x8c, 0x4e, 0xc3, 0x4f, \ + 0xdd, 0x93, 0x81, 0x01, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xa3, 0x0f, 0x8c, 0x0e, \ + 0x81, 0x01, 0x88, 0x0e, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0x81, 0x61, 0xa1, 0x2b, \ + 0xa1, 0x4b, 0x8c, 0xe5, \ + 0x81, 0x71, 0x81, 0xc1, \ + 0x81, 0x81, 0x8c, 0xa5, \ + 0x81, 0x61, 0x81, 0xc1, \ + 0xfb, 0x80, 0x0e, 0x64, \ + 0xfb, 0x80, 0x12, 0xc4, \ + 0xc2, 0x97, 0xc2, 0x77, \ + 0xfc, 0x1c, 0x02, 0xbb, \ + 0xfc, 0x24, 0x06, 0x9b, \ + 0xe0, 0x88, 0x0e, 0xb4, \ + 0xe0, 0x88, 0x14, 0x94, \ + 0xfc, 0x1c, 0x00, 0x75, \ + 0xfc, 0x28, 0x00, 0xa5, \ + 0x4f, 0x5a, 0x94, 0x7d, \ + 0x0e, 0x0c, 0xe0, 0x28, \ + 0x8e, 0x63, 0xe2, 0x00, \ + 0xfb, 0x80, 0x0e, 0x64, \ + 0xe2, 0x00, 0x14, 0x1c, \ + 0xa0, 0x00, 0x00, 0x38, \ + 0x16, 0x84, 0x96, 0x9b, \ + 0xc5, 0x7d, 0xfb, 0x80, \ + 0x94, 0xc3, 0xc2, 0xb7, \ + 0x0e, 0x80, 0x8a, 0xb3, \ + 0xac, 0x9c, 0xe4, 0x04, \ + 0x10, 0xc4, 0x82, 0x25, \ + 0xc9, 0x8d, 0xfb, 0x80, \ + 0xf4, 0x75, 0x8e, 0x8b, \ + 0x81, 0xc1, 0x81, 0x35, \ + 0x10, 0x64, 0x81, 0x71, \ + 0xc5, 0x8d, 0xfb, 0x80, \ + 0x40, 0x72, 0x84, 0x85, \ + 0x12, 0x84, 0xf0, 0x88, \ + 0x92, 0x13, 0xe0, 0x84, \ + 0xc5, 0x8a, 0xb0, 0x23, \ + 0xfb, 0x80, 0x12, 0x74, \ + 0xec, 0x04, 0x04, 0x20, \ + 0xa1, 0x2b, 0xc2, 0x97, \ + 0x8a, 0x93, 0x82, 0x71, \ + 0xfc, 0xd5, 0xa4, 0x8c, \ + 0xf1, 0x98, 0x4e, 0x02, \ + 0xfb, 0x80, 0x00, 0xc4, \ + 0x84, 0x05, 0xc9, 0x0d, \ + 0xe0, 0x84, 0x02, 0x04, \ + 0xa0, 0x43, 0x82, 0x33, \ + 0x02, 0x74, 0xcd, 0x0a, \ + 0x08, 0x40, 0xfb, 0x80, \ + 0xc2, 0x17, 0xec, 0x04, \ + 0x82, 0x71, 0xa1, 0x4b, \ + 0x84, 0x0c, 0x8a, 0x13, \ + 0x0c, 0x0c, 0xfc, 0xd5, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x84, 0x8b, 0x9e, 0xbb, \ + 0x0e, 0x84, 0x80, 0x4b, \ + 0x0a, 0x74, 0xe1, 0x84, \ + 0x00, 0x50, 0xe0, 0x84, \ + 0x40, 0x00, 0xe4, 0x1c, \ + 0xc1, 0xff, 0x73, 0xe1, \ + 0xf1, 0x9c, 0x52, 0x82, \ + 0xf1, 0x80, 0x54, 0xf2, \ + 0xfb, 0x80, 0x04, 0x94, \ + 0xe0, 0x84, 0x00, 0x24, \ + 0x8e, 0x01, 0x94, 0xfb, \ + 0x73, 0xe1, 0x40, 0x00, \ + 0x4c, 0xf2, 0xc3, 0xff, \ + 0xe5, 0x8d, 0xf1, 0x80, \ + 0x80, 0x85, 0x8c, 0xfb, \ + 0x8c, 0x0c, 0x84, 0x0a, \ + 0x56, 0x25, 0x87, 0xb5, \ + 0x56, 0x35, 0xe0, 0x10, \ + 0xa1, 0x7b, 0xe0, 0x14, \ + 0x44, 0x80, 0x82, 0xa5, \ + 0xc0, 0xff, 0x73, 0xf9, \ + 0xee, 0x4e, 0x94, 0x0b, \ + 0xae, 0x5e, 0xec, 0x1e, \ + 0xef, 0xb8, 0x43, 0x28, \ + 0xec, 0x14, 0xae, 0x54, \ + 0x12, 0x0a, 0xee, 0x44, \ + 0x82, 0xc5, 0xe8, 0x00, \ + 0x90, 0x03, 0xce, 0x0b, \ + 0xfb, 0x80, 0x00, 0x04, \ + 0x88, 0x8b, 0xc2, 0x07, \ + 0x04, 0x00, 0x8a, 0x13, \ + 0x8c, 0x0b, 0xe4, 0x08, \ + 0xef, 0xb8, 0x42, 0x08, \ + 0x88, 0x0b, 0x90, 0x4b, \ + 0x94, 0x1b, 0x88, 0x8b, \ + 0x94, 0x3b, 0x8e, 0x2b, \ + 0xef, 0xfc, 0x7b, 0x48, \ + 0x8c, 0x1b, 0x90, 0x0b, \ + 0xe0, 0x10, 0x16, 0x95, \ + 0x8c, 0x3b, 0x92, 0x2b, \ + 0xef, 0xfc, 0x7a, 0xc8, \ + 0x94, 0x1b, 0x90, 0x0b, \ + 0x8c, 0x3b, 0x8e, 0x2b, \ + 0x16, 0x55, 0x92, 0x4b, \ + 0x73, 0x78, 0xe0, 0x14, \ + 0x96, 0xfb, 0xef, 0xfc, \ + 0x0c, 0x0c, 0xb8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0x9d, 0x01, 0xa1, 0x3b, \ + 0x42, 0x05, 0x86, 0x0e, \ + 0x82, 0x05, 0xe0, 0x14, \ + 0xe0, 0x0e, 0x40, 0x2a, \ + 0xe1, 0x0e, 0x42, 0x2a, \ + 0x94, 0x16, 0x90, 0x06, \ + 0xff, 0x01, 0xdd, 0x93, \ + 0xdd, 0x93, 0x84, 0x0e, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x88, 0x9b, 0x8a, 0x6b, \ + 0x82, 0x2b, 0x84, 0x8b, \ + 0x99, 0x01, 0x80, 0x7b, \ + 0xe0, 0x3c, 0x10, 0x39, \ + 0x52, 0x05, 0xa6, 0x0e, \ + 0x40, 0x82, 0xe0, 0x14, \ + 0x08, 0xf0, 0xf0, 0x88, \ + 0x9e, 0x5b, 0xe4, 0x10, \ + 0x6b, 0x71, 0x00, 0xa9, \ + 0x92, 0x1b, 0xc0, 0x05, \ + 0xef, 0xfc, 0x7d, 0x88, \ + 0x98, 0x06, 0x81, 0x01, \ + 0xa1, 0x0b, 0xa4, 0x04, \ + 0x89, 0x08, 0x86, 0x95, \ + 0xbc, 0x08, 0x80, 0x83, \ + 0xec, 0x70, 0x10, 0x19, \ + 0x84, 0xa5, 0xc3, 0x0d, \ + 0x02, 0xa4, 0x81, 0x01, \ + 0x04, 0x04, 0xfa, 0x80, \ + 0x8e, 0x23, 0xfa, 0x80, \ + 0xe0, 0x40, 0x04, 0x29, \ + 0x82, 0x25, 0x85, 0x1b, \ + 0xe3, 0xfc, 0x04, 0x0a, \ + 0x82, 0x01, 0x82, 0x05, \ + 0xf4, 0x40, 0x00, 0x8a, \ + 0x81, 0x35, 0xfc, 0x75, \ + 0x98, 0x06, 0x83, 0x01, \ + 0x0c, 0x0c, 0x90, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x86, 0x7b, 0xfe, 0x4e, \ + 0x14, 0x00, 0x84, 0x8b, \ + 0xc0, 0x11, 0x67, 0x71, \ + 0x16, 0x00, 0xfc, 0x0e, \ + 0xc0, 0x03, 0x67, 0x71, \ + 0xbe, 0x7e, 0x81, 0x61, \ + 0x02, 0x64, 0x81, 0x91, \ + 0x0a, 0xf0, 0xfb, 0x80, \ + 0xfe, 0x04, 0xe4, 0x40, \ + 0x90, 0x4b, 0xfc, 0x24, \ + 0xef, 0xfc, 0x7a, 0x88, \ + 0xa1, 0x0b, 0xa1, 0x08, \ + 0xa2, 0x0e, 0x85, 0x01, \ + 0xe0, 0x14, 0x50, 0x05, \ + 0xa8, 0x02, 0x82, 0xc5, \ + 0xac, 0x04, 0x9c, 0x0e, \ + 0x60, 0x01, 0x02, 0x06, \ + 0xc3, 0x0d, 0xd0, 0x00, \ + 0x83, 0x01, 0x80, 0x95, \ + 0xe0, 0x10, 0x50, 0x05, \ + 0x81, 0x55, 0x82, 0x91, \ + 0x7f, 0xfd, 0x41, 0xfe, \ + 0x9c, 0x0e, 0xdf, 0xff, \ + 0x0c, 0x0a, 0x82, 0x61, \ + 0xc0, 0x01, 0x60, 0x01, \ + 0x84, 0xa1, 0x88, 0x71, \ + 0xf6, 0x15, 0x88, 0xb1, \ + 0x40, 0x93, 0xbe, 0x04, \ + 0xc0, 0x01, 0x6e, 0x01, \ + 0x0c, 0x0c, 0xa8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe0, 0x28, 0x0a, 0x19, \ + 0xe4, 0x44, 0x02, 0x8a, \ + 0x8e, 0x1e, 0x81, 0x11, \ + 0xe0, 0x14, 0x46, 0x15, \ + 0x46, 0x15, 0x8c, 0x1e, \ + 0xcc, 0x1e, 0xe0, 0x10, \ + 0xe0, 0x18, 0x46, 0x15, \ + 0x46, 0x15, 0xce, 0x1e, \ + 0x80, 0x95, 0xe0, 0x1c, \ + 0x8c, 0x0e, 0xfb, 0x01, \ + 0x00, 0x29, 0xdd, 0x93, \ + 0xa1, 0x2b, 0xe0, 0x38, \ + 0x8e, 0x0e, 0x83, 0x01, \ + 0xe0, 0x14, 0x46, 0x05, \ + 0xd4, 0x0a, 0x82, 0xc5, \ + 0xfc, 0xc0, 0x02, 0x04, \ + 0xe6, 0x8a, 0x42, 0x1a, \ + 0x42, 0x0a, 0xe2, 0x07, \ + 0x00, 0x20, 0xe0, 0x06, \ + 0x40, 0x1a, 0xec, 0x04, \ + 0x81, 0x45, 0xe6, 0x02, \ + 0x8c, 0x1e, 0xff, 0x11, \ + 0xd6, 0x1c, 0x81, 0x11, \ + 0xe0, 0x10, 0x4a, 0x03, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x88, 0x7b, 0x8a, 0x6b, \ + 0x84, 0x9b, 0x86, 0x8b, \ + 0x0c, 0x09, 0x80, 0xab, \ + 0xa1, 0x0b, 0xe0, 0x38, \ + 0x94, 0x0b, 0x86, 0x15, \ + 0x90, 0x2b, 0x92, 0x1b, \ + 0x8c, 0x4b, 0x8e, 0x3b, \ + 0xef, 0xfc, 0x77, 0x48, \ + 0xa1, 0x0b, 0xa0, 0x04, \ + 0x14, 0xa0, 0x82, 0xd5, \ + 0xc0, 0x03, 0x67, 0x71, \ + 0x92, 0x1b, 0x94, 0x0b, \ + 0x8e, 0x3b, 0x90, 0x2b, \ + 0x54, 0x78, 0x8c, 0x4b, \ + 0xa0, 0x04, 0xef, 0xfc, \ + 0x80, 0x85, 0xa1, 0x0b, \ + 0xf2, 0x3c, 0x0c, 0x0c, \ + 0x94, 0x0b, 0xdd, 0x93, \ + 0x90, 0x2b, 0x92, 0x1b, \ + 0x8c, 0x4b, 0x8e, 0x3b, \ + 0xf2, 0x3c, 0x0c, 0x0c, \ + 0xff, 0xfc, 0x0c, 0x48, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x00, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0xe1, 0x00, 0x1e, 0x06, \ + 0xe1, 0x4c, 0x01, 0x06, \ + 0x6a, 0xf8, 0x83, 0x11, \ + 0xc1, 0xc3, 0xef, 0xb4, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x86, 0xab, 0x88, 0x8b, \ + 0x82, 0x6b, 0x84, 0x7b, \ + 0x81, 0xb1, 0x80, 0x9b, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x41, 0x38, 0x81, 0x11, \ + 0xa1, 0xab, 0xef, 0xb8, \ + 0x92, 0x0b, 0x82, 0x05, \ + 0xe8, 0x00, 0x41, 0xe8, \ + 0xe1, 0x48, 0x00, 0x86, \ + 0x80, 0xbe, 0x80, 0x04, \ + 0x80, 0xb5, 0xa1, 0x8b, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0x82, 0xe5, 0xa1, 0x7b, \ + 0x43, 0xf6, 0x98, 0x04, \ + 0xff, 0xc0, 0x6f, 0xfd, \ + 0x40, 0x00, 0x82, 0x0f, \ + 0xc3, 0x00, 0x7c, 0x01, \ + 0x98, 0x04, 0x98, 0x0e, \ + 0xf0, 0x00, 0x02, 0x0e, \ + 0x98, 0x0e, 0x82, 0x0f, \ + 0xef, 0xfc, 0x7c, 0x68, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0xe0, 0x8c, 0x40, 0x4a, \ + 0xf3, 0x64, 0x42, 0x00, \ + 0x6e, 0x51, 0x4c, 0x11, \ + 0x84, 0xa5, 0xc0, 0x2f, \ + 0x65, 0x71, 0x0c, 0x19, \ + 0x40, 0x00, 0xc0, 0x31, \ + 0x4c, 0x01, 0xf3, 0x60, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0x4c, 0x01, 0x81, 0x01, \ + 0xc0, 0x2f, 0x6e, 0x5d, \ + 0xf3, 0xf8, 0x42, 0x10, \ + 0x65, 0x71, 0x4c, 0x11, \ + 0x4c, 0x01, 0xc0, 0x31, \ + 0xc0, 0x31, 0x65, 0x75, \ + 0x64, 0xe1, 0x0c, 0x09, \ + 0x03, 0x46, 0xc0, 0x31, \ + 0x84, 0x14, 0xe1, 0x38, \ + 0xf3, 0xe4, 0x40, 0x00, \ + 0x4c, 0x01, 0x81, 0x71, \ + 0xc0, 0x31, 0x64, 0xe1, \ + 0x65, 0x79, 0x4c, 0x71, \ + 0x84, 0x76, 0xc0, 0x31, \ + 0xe1, 0x38, 0x01, 0x86, \ + 0x8f, 0x21, 0x80, 0x14, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x51, 0xc6, 0x80, 0x76, \ + 0xa0, 0x04, 0xe1, 0x40, \ + 0x8c, 0x0b, 0x80, 0x76, \ + 0xe8, 0x30, 0x77, 0xe8, \ + 0xe1, 0x38, 0x40, 0x06, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf1, 0xfc, 0x42, 0x10, \ + 0x8c, 0x0b, 0x80, 0x16, \ + 0xe8, 0x4c, 0x69, 0x48, \ + 0xe1, 0x40, 0x40, 0x46, \ + 0x80, 0x76, 0x80, 0x04, \ + 0xe1, 0x40, 0x41, 0x06, \ + 0x80, 0x76, 0x80, 0x04, \ + 0x80, 0x76, 0xa0, 0x04, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x8b, 0x11, 0x80, 0x6b, \ + 0xe8, 0x24, 0x69, 0x48, \ + 0xe1, 0x20, 0x40, 0x46, \ + 0x42, 0x06, 0x80, 0x04, \ + 0x4c, 0x11, 0xe0, 0x00, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x80, 0x16, 0x81, 0x11, \ + 0xe1, 0x38, 0x40, 0x06, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf1, 0xfc, 0x42, 0x10, \ + 0x0c, 0x09, 0x80, 0x16, \ + 0xc0, 0x31, 0x65, 0x71, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x0c, 0x09, 0x82, 0x25, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0xf3, 0xec, 0x40, 0x00, \ + 0x6e, 0x51, 0x4c, 0x01, \ + 0x8c, 0x0b, 0xc0, 0x2f, \ + 0xe8, 0x4c, 0x65, 0xe8, \ + 0xe8, 0x4c, 0x65, 0x48, \ + 0xe1, 0x40, 0x41, 0xc6, \ + 0x85, 0x11, 0x80, 0x04, \ + 0x41, 0x06, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0x40, 0x46, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0x8c, 0x0b, 0x80, 0x16, \ + 0xe8, 0x30, 0x60, 0x88, \ + 0x6e, 0x51, 0x0c, 0x09, \ + 0x40, 0x4a, 0xc0, 0x2f, \ + 0x80, 0x85, 0xe0, 0x88, \ + 0x42, 0xb8, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xe8, 0x00, \ + 0xe8, 0x00, 0x5d, 0x48, \ + 0x4d, 0x58, 0x9b, 0x01, \ + 0x89, 0x01, 0xe8, 0x24, \ + 0xe8, 0x14, 0x57, 0x68, \ + 0x5e, 0x78, 0x8c, 0x0b, \ + 0x40, 0xb6, 0xe8, 0x00, \ + 0x4c, 0xb8, 0xe0, 0x00, \ + 0x00, 0x06, 0xe8, 0x24, \ + 0xd0, 0x00, 0x60, 0x01, \ + 0xe8, 0x14, 0x56, 0xa8, \ + 0x6e, 0x51, 0x0c, 0x09, \ + 0x40, 0x4a, 0xc0, 0x2f, \ + 0x82, 0x65, 0xe0, 0x80, \ + 0x72, 0xfd, 0x43, 0xc6, \ + 0x81, 0x21, 0xc0, 0x20, \ + 0x83, 0x41, 0x83, 0x31, \ + 0x0c, 0x0c, 0x8c, 0x0b, \ + 0xe3, 0x15, 0xf2, 0x00, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0xb0, 0x1e, 0xf0, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x15, 0x61, 0x51, \ + 0x81, 0x01, 0xa9, 0x0b, \ + 0x93, 0x0e, 0x95, 0x0e, \ + 0x8f, 0x0e, 0x91, 0x0e, \ + 0x8b, 0x0e, 0x8d, 0x0e, \ + 0xfe, 0x0e, 0x89, 0x0e, \ + 0x0c, 0x09, 0x90, 0x95, \ + 0xc0, 0x15, 0x61, 0x55, \ + 0x8e, 0xf5, 0xa9, 0x0b, \ + 0xe1, 0x48, 0x00, 0x46, \ + 0x09, 0x76, 0x80, 0x04, \ + 0x02, 0x60, 0xe0, 0x0c, \ + 0xc0, 0xa3, 0x65, 0xd1, \ + 0x66, 0xe1, 0x04, 0x60, \ + 0x06, 0xf0, 0xc0, 0xa5, \ + 0x4c, 0x41, 0xe4, 0x30, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xef, 0xc0, 0x61, 0x88, \ + 0x65, 0x61, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x80, 0x75, 0xe1, 0x9c, \ + 0xfe, 0x0e, 0x83, 0x01, \ + 0xa1, 0x1b, 0xfe, 0x14, \ + 0x0c, 0x39, 0x84, 0x05, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x99, 0x5a, 0x95, 0x4a, \ + 0x00, 0x60, 0x91, 0xca, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xfb, 0x00, 0x04, 0x14, \ + 0x8f, 0x11, 0xbc, 0xce, \ + 0xe8, 0x10, 0x60, 0x28, \ + 0xa1, 0x1b, 0x8f, 0x14, \ + 0x0c, 0x39, 0x84, 0x05, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xa9, 0x5a, 0xa5, 0x4a, \ + 0x00, 0x60, 0xa1, 0xca, \ + 0xc0, 0x56, 0x66, 0xf1, \ + 0xfb, 0x00, 0x04, 0x14, \ + 0x8f, 0x11, 0xbc, 0xce, \ + 0xe8, 0x10, 0x6b, 0x48, \ + 0x6e, 0x6d, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x80, 0xc5, 0xe0, 0x80, \ + 0x81, 0x21, 0x83, 0x11, \ + 0x50, 0x98, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xe8, 0x08, \ + 0xe8, 0x18, 0x40, 0xa8, \ + 0x56, 0x38, 0x8c, 0x0b, \ + 0x81, 0x71, 0xe8, 0x28, \ + 0x66, 0xb9, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x05, \ + 0xc0, 0x05, 0x66, 0xb5, \ + 0x66, 0xb1, 0x4c, 0x75, \ + 0x4c, 0x71, 0xc0, 0x05, \ + 0xc0, 0x16, 0x67, 0x61, \ + 0x67, 0x59, 0x4c, 0x71, \ + 0x51, 0xf6, 0xc0, 0x16, \ + 0x4c, 0x81, 0xe0, 0x0c, \ + 0xc0, 0x16, 0x67, 0x5d, \ + 0x69, 0xd1, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x05, \ + 0xc0, 0x0a, 0x65, 0xb5, \ + 0x65, 0xb1, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x65, 0xad, \ + 0x65, 0xa9, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x65, 0xa5, \ + 0x65, 0xa1, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x65, 0x9d, \ + 0x65, 0x99, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x67, 0xf9, \ + 0x67, 0xf5, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x67, 0xf1, \ + 0x67, 0xed, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x67, 0xe9, \ + 0x67, 0xe5, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x67, 0xe1, \ + 0x67, 0xdd, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x69, 0xbd, \ + 0x69, 0xb9, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x69, 0xb5, \ + 0x69, 0xb1, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x69, 0xad, \ + 0x69, 0xa9, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x69, 0xa5, \ + 0x69, 0xa1, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x6b, 0x81, \ + 0x6b, 0x7d, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x6b, 0x79, \ + 0x6b, 0x75, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x6b, 0x71, \ + 0x6b, 0x6d, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x6b, 0x69, \ + 0x6b, 0x65, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x6d, 0x45, \ + 0x6d, 0x41, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x6d, 0x3d, \ + 0x6d, 0x39, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x6d, 0x35, \ + 0x6d, 0x31, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x6d, 0x2d, \ + 0x6d, 0x29, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0a, \ + 0xc0, 0x0b, 0x6f, 0x15, \ + 0x6f, 0x11, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0b, \ + 0xc0, 0x0b, 0x6f, 0x0d, \ + 0x6f, 0x09, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0b, \ + 0xc0, 0x0b, 0x6f, 0x05, \ + 0x6f, 0x01, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x0b, \ + 0xc0, 0x0b, 0x6e, 0xfd, \ + 0x6e, 0xf9, 0x4c, 0x75, \ + 0x01, 0x66, 0xc0, 0x0b, \ + 0x02, 0x46, 0xe0, 0x0c, \ + 0x4c, 0x01, 0xe1, 0x48, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x02, 0x60, 0x84, 0x04, \ + 0xc0, 0xc7, 0x66, 0x91, \ + 0x67, 0xe1, 0x04, 0x60, \ + 0x06, 0x60, 0xc0, 0xc5, \ + 0xc0, 0xae, 0x65, 0xd1, \ + 0x67, 0xf1, 0x08, 0x60, \ + 0x0a, 0x60, 0xc0, 0xa7, \ + 0xc0, 0xb0, 0x67, 0xe1, \ + 0x66, 0x11, 0x18, 0x60, \ + 0x12, 0x60, 0xc0, 0xab, \ + 0xc0, 0xa3, 0x64, 0xf1, \ + 0xbc, 0xce, 0xbe, 0x9e, \ + 0xef, 0xe8, 0x49, 0x28, \ + 0x67, 0xa5, 0x0c, 0x15, \ + 0xa1, 0x1b, 0xc0, 0x0c, \ + 0x0c, 0x39, 0x84, 0xc5, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x6f, 0x59, 0x0c, 0x4b, \ + 0x0c, 0x5b, 0xc0, 0x18, \ + 0xc0, 0x18, 0x6f, 0x61, \ + 0x6f, 0x51, 0x0c, 0xcb, \ + 0x00, 0x60, 0xc0, 0x18, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xfb, 0x00, 0x04, 0x14, \ + 0x8d, 0x11, 0xbc, 0xce, \ + 0xe8, 0x10, 0x4f, 0xc8, \ + 0x6e, 0x51, 0x0c, 0x09, \ + 0x40, 0x00, 0xc0, 0x2f, \ + 0x0c, 0x19, 0xf3, 0xf0, \ + 0xc0, 0x2f, 0x65, 0xd5, \ + 0x4c, 0x01, 0xc2, 0x09, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0x6e, 0x5d, 0x4c, 0x11, \ + 0x4c, 0x81, 0xc0, 0x2f, \ + 0xc0, 0x30, 0x6e, 0xe5, \ + 0x69, 0xe9, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x05, \ + 0xc0, 0x05, 0x69, 0xe5, \ + 0x69, 0xe1, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x05, \ + 0xc0, 0x07, 0x61, 0xb5, \ + 0x61, 0xb1, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x07, \ + 0xc0, 0x07, 0x61, 0xad, \ + 0x64, 0x71, 0x00, 0x60, \ + 0x81, 0x11, 0xc0, 0x91, \ + 0xe0, 0xd0, 0x44, 0x06, \ + 0xef, 0xb4, 0x4f, 0x08, \ + 0x62, 0x2d, 0x4c, 0x73, \ + 0x4c, 0x71, 0xc0, 0x12, \ + 0xc0, 0x27, 0x68, 0xfd, \ + 0x65, 0x21, 0x00, 0x60, \ + 0x81, 0x11, 0xc0, 0x90, \ + 0xe0, 0x0c, 0x04, 0x06, \ + 0xef, 0xb4, 0x4e, 0x28, \ + 0x60, 0x85, 0x4c, 0x73, \ + 0x00, 0x60, 0xc0, 0x12, \ + 0xc0, 0xc2, 0x64, 0x91, \ + 0x04, 0x06, 0x81, 0x11, \ + 0x4d, 0x78, 0xe0, 0x10, \ + 0x0c, 0x09, 0xef, 0xb4, \ + 0xc0, 0x31, 0x64, 0xe1, \ + 0x65, 0x41, 0x0c, 0x19, \ + 0x40, 0x00, 0xc0, 0x31, \ + 0x42, 0x10, 0xf3, 0xf8, \ + 0x4c, 0x01, 0xf3, 0xf0, \ + 0xc0, 0x31, 0x64, 0xe1, \ + 0x65, 0x41, 0x4c, 0x11, \ + 0x4c, 0x75, 0xc0, 0x31, \ + 0xc0, 0x0a, 0x63, 0x39, \ + 0x63, 0x35, 0x4c, 0x75, \ + 0xd8, 0xf1, 0xc0, 0x0a, \ + 0xf2, 0x1c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x01, 0x06, 0x80, 0x6b, \ + 0x0c, 0x1b, 0xe1, 0x04, \ + 0xc0, 0x17, 0x6f, 0x39, \ + 0x80, 0x1c, 0x80, 0x04, \ + 0x61, 0x58, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xe8, 0x34, \ + 0xe8, 0x3c, 0x76, 0xe8, \ + 0x64, 0xe1, 0x0c, 0x09, \ + 0x40, 0x00, 0xc0, 0x31, \ + 0x4c, 0x01, 0xf3, 0xf8, \ + 0xc0, 0x31, 0x64, 0xe1, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x6e, 0x5d, 0x00, 0x19, \ + 0xb0, 0x1d, 0xc0, 0x2f, \ + 0x04, 0x10, 0x82, 0x75, \ + 0xe7, 0x2d, 0xec, 0x04, \ + 0x04, 0x10, 0x82, 0xb5, \ + 0xe5, 0x2d, 0xec, 0x10, \ + 0x25, 0x38, 0x84, 0x25, \ + 0x04, 0x10, 0xe0, 0x40, \ + 0xe7, 0x2d, 0xec, 0x2c, \ + 0x2c, 0xd8, 0x80, 0xa5, \ + 0x13, 0x38, 0xe0, 0x48, \ + 0xb3, 0x1b, 0xe0, 0x38, \ + 0xb5, 0x1b, 0x80, 0xe5, \ + 0x1b, 0xd8, 0x80, 0xd5, \ + 0xad, 0x1b, 0xe0, 0x50, \ + 0x87, 0x95, 0x80, 0x55, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0x6e, 0x51, 0x00, 0x19, \ + 0x81, 0x21, 0xc0, 0x2f, \ + 0xf3, 0xf0, 0x42, 0x10, \ + 0x6e, 0x5d, 0x40, 0x21, \ + 0x40, 0x11, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x81, 0x01, 0x81, 0x61, \ + 0x6a, 0x78, 0x81, 0x11, \ + 0x42, 0xc6, 0xe8, 0x1c, \ + 0x84, 0x24, 0xe1, 0x44, \ + 0x46, 0x30, 0x88, 0x38, \ + 0x88, 0x36, 0xf3, 0xf4, \ + 0xe1, 0x14, 0x04, 0x46, \ + 0x83, 0x31, 0x88, 0x24, \ + 0x88, 0x66, 0x88, 0x36, \ + 0x87, 0x21, 0x84, 0x14, \ + 0x83, 0x11, 0x84, 0x26, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xe0, 0x1c, 0x29, 0x28, \ + 0xe1, 0x14, 0x00, 0x46, \ + 0x83, 0x11, 0x80, 0x04, \ + 0xc1, 0x83, 0x80, 0x16, \ + 0xdd, 0x93, 0xc7, 0x13, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x2f, 0x6e, 0x5d, \ + 0x82, 0x55, 0xb3, 0x0b, \ + 0xe0, 0x00, 0x40, 0x96, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x8c, 0x0b, 0xc0, 0x2f, \ + 0xe8, 0x44, 0x55, 0x88, \ + 0x40, 0x66, 0x81, 0x95, \ + 0x4c, 0x01, 0xe0, 0x00, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x59, 0x78, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x44, \ + 0xc0, 0x15, 0x61, 0xd1, \ + 0xe5, 0x00, 0x00, 0x8a, \ + 0x0c, 0x39, 0x84, 0x35, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xe0, 0x00, 0x41, 0xf6, \ + 0x61, 0xd1, 0x4c, 0x01, \ + 0x00, 0x60, 0xc0, 0x15, \ + 0xc0, 0x56, 0x66, 0xf1, \ + 0x44, 0xb6, 0x81, 0x11, \ + 0x4f, 0x18, 0xe0, 0x00, \ + 0x0c, 0x09, 0xe8, 0x10, \ + 0xc0, 0x15, 0x61, 0xd1, \ + 0x6e, 0x79, 0x0c, 0x19, \ + 0x42, 0x1a, 0xc0, 0x30, \ + 0x0e, 0x60, 0xe6, 0x82, \ + 0xc0, 0x54, 0x65, 0xc1, \ + 0x6e, 0x71, 0x4c, 0x01, \ + 0x82, 0x11, 0xc0, 0x30, \ + 0x67, 0x31, 0x4c, 0x13, \ + 0x8c, 0x0b, 0xc0, 0x18, \ + 0xe8, 0x38, 0x62, 0x08, \ + 0x6e, 0x5d, 0x0c, 0x09, \ + 0xb3, 0x0b, 0xc0, 0x2f, \ + 0x40, 0x96, 0x98, 0x15, \ + 0x02, 0x46, 0xe0, 0x00, \ + 0x4c, 0x01, 0xe1, 0x48, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x04, 0x60, 0x84, 0x04, \ + 0xc0, 0x54, 0x65, 0x51, \ + 0x67, 0x41, 0x06, 0x60, \ + 0x08, 0x60, 0xc0, 0x59, \ + 0xc0, 0xb2, 0x67, 0xe1, \ + 0x67, 0x21, 0x0a, 0x60, \ + 0x02, 0x60, 0xc0, 0xae, \ + 0xc0, 0xa3, 0x65, 0xd1, \ + 0x8e, 0x1b, 0xbc, 0x1e, \ + 0xef, 0xb8, 0x59, 0xc8, \ + 0x65, 0x61, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x15, 0xe1, 0x74, \ + 0x4c, 0x05, 0x83, 0x01, \ + 0xc0, 0x0a, 0x65, 0x99, \ + 0x65, 0x9d, 0x4c, 0x05, \ + 0x0c, 0x15, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x65, 0xa9, \ + 0xe2, 0x00, 0x02, 0x0a, \ + 0x0c, 0x39, 0x84, 0xc5, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x6b, 0x61, 0x0c, 0x4b, \ + 0x0c, 0x5b, 0xc0, 0x14, \ + 0xc0, 0x14, 0x6b, 0x69, \ + 0x6b, 0x59, 0x0c, 0xcb, \ + 0x00, 0x60, 0xc0, 0x14, \ + 0xc0, 0x56, 0x66, 0xf1, \ + 0xfb, 0x00, 0x04, 0x14, \ + 0x8b, 0x11, 0xbc, 0xce, \ + 0xe8, 0x10, 0x45, 0xc8, \ + 0x65, 0x99, 0x0c, 0x15, \ + 0xa1, 0x1b, 0xc0, 0x0a, \ + 0x80, 0x00, 0x10, 0x88, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x0c, 0x4b, 0xc0, 0x2f, \ + 0xc0, 0x14, 0x6b, 0x41, \ + 0x6b, 0x49, 0x0c, 0x5b, \ + 0x0c, 0xcb, 0xc0, 0x14, \ + 0xc0, 0x14, 0x6b, 0x39, \ + 0x66, 0xc1, 0x00, 0x60, \ + 0x04, 0x14, 0xc0, 0x56, \ + 0xbc, 0xce, 0xfb, 0x00, \ + 0x77, 0xd8, 0x8b, 0x11, \ + 0x9f, 0x15, 0xe8, 0x0c, \ + 0x67, 0xa9, 0x0c, 0x09, \ + 0xa5, 0x0b, 0xc0, 0x2f, \ + 0xe0, 0x00, 0x42, 0x66, \ + 0x6e, 0x59, 0x4c, 0x11, \ + 0x10, 0x60, 0xc0, 0x2f, \ + 0xc0, 0xbd, 0x67, 0x71, \ + 0xa3, 0x0b, 0x8c, 0xe5, \ + 0x00, 0x46, 0x9c, 0x75, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0x65, 0x51, 0x06, 0x60, \ + 0x08, 0x60, 0xc0, 0x54, \ + 0xc0, 0x59, 0x67, 0x41, \ + 0x67, 0xe1, 0x12, 0x60, \ + 0x14, 0x60, 0xc0, 0xb2, \ + 0xc0, 0xa7, 0x67, 0xf1, \ + 0x8e, 0x1b, 0xbc, 0xae, \ + 0x92, 0x5b, 0x90, 0x2b, \ + 0xef, 0xb8, 0x6d, 0x08, \ + 0x65, 0x61, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x35, 0xe1, 0x78, \ + 0x87, 0x11, 0x83, 0x01, \ + 0x69, 0xa1, 0x4c, 0x05, \ + 0x4c, 0x15, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x69, 0xa5, \ + 0x69, 0xa1, 0x0c, 0x05, \ + 0xa1, 0x0b, 0xc0, 0x0a, \ + 0x00, 0x46, 0x94, 0xb5, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0x65, 0x21, 0x08, 0x60, \ + 0x90, 0x1b, 0xc0, 0xad, \ + 0x94, 0x3b, 0x92, 0x2b, \ + 0xef, 0xb8, 0x70, 0x28, \ + 0x12, 0x06, 0x89, 0x95, \ + 0xa4, 0x6e, 0xe1, 0x4c, \ + 0xe1, 0x00, 0x02, 0x06, \ + 0x6b, 0xd8, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x4c, \ + 0xc0, 0x1c, 0x69, 0xe9, \ + 0x0c, 0x29, 0xa4, 0x14, \ + 0xc0, 0x1c, 0x69, 0xe5, \ + 0x62, 0x69, 0x0c, 0x45, \ + 0x0c, 0x55, 0xc0, 0x07, \ + 0xc0, 0x07, 0x62, 0x65, \ + 0x66, 0x41, 0x06, 0x10, \ + 0x42, 0x01, 0xc0, 0x1f, \ + 0xc0, 0x07, 0x6e, 0x71, \ + 0x6e, 0x6d, 0x42, 0x21, \ + 0x42, 0x45, 0xc0, 0x07, \ + 0xc0, 0x01, 0x6f, 0x91, \ + 0x61, 0x31, 0x42, 0x55, \ + 0x00, 0x46, 0xc0, 0x01, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0x64, 0xb1, 0x04, 0x10, \ + 0x0a, 0x60, 0xc0, 0x11, \ + 0xc0, 0x54, 0x65, 0x51, \ + 0x67, 0x41, 0x02, 0x60, \ + 0x08, 0x60, 0xc0, 0x59, \ + 0xc0, 0xa7, 0x67, 0xf1, \ + 0x67, 0xe1, 0x18, 0x60, \ + 0xfc, 0x4e, 0xc0, 0xb2, \ + 0xbc, 0x1e, 0xbe, 0xce, \ + 0x8e, 0x4b, 0x90, 0x1b, \ + 0xef, 0xbc, 0x43, 0x08, \ + 0x00, 0x60, 0x81, 0xb5, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0x04, 0x86, 0x81, 0x11, \ + 0x46, 0x66, 0xe0, 0x0c, \ + 0x71, 0x98, 0xe0, 0x00, \ + 0x0c, 0x05, 0xe8, 0x0c, \ + 0xc0, 0x0a, 0x69, 0xa1, \ + 0x82, 0xf5, 0xa1, 0x0b, \ + 0xe1, 0x48, 0x00, 0x46, \ + 0x02, 0x60, 0x80, 0x04, \ + 0xc0, 0xb2, 0x67, 0xe1, \ + 0x64, 0xf1, 0x04, 0x60, \ + 0x06, 0x60, 0xc0, 0x9e, \ + 0xc0, 0xae, 0x65, 0xd1, \ + 0xef, 0xf0, 0x75, 0xe8, \ + 0x69, 0xb1, 0x0c, 0x15, \ + 0x02, 0x0a, 0xc0, 0x0a, \ + 0x84, 0xc5, 0xe2, 0x00, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x0c, 0x4b, 0xc0, 0x2f, \ + 0xc0, 0x15, 0x63, 0x71, \ + 0x63, 0x79, 0x0c, 0x5b, \ + 0x0c, 0xcb, 0xc0, 0x15, \ + 0xc0, 0x15, 0x63, 0x69, \ + 0x66, 0xf1, 0x00, 0x60, \ + 0x04, 0x14, 0xc0, 0x56, \ + 0xbc, 0xce, 0xfb, 0x00, \ + 0x78, 0x78, 0x8b, 0x11, \ + 0x0c, 0x15, 0xe8, 0x0c, \ + 0xc0, 0x0a, 0x69, 0xa1, \ + 0x84, 0xc5, 0xa1, 0x1b, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x0c, 0x4b, 0xc0, 0x2f, \ + 0xc0, 0x15, 0x63, 0x51, \ + 0x63, 0x59, 0x0c, 0x5b, \ + 0x0c, 0xcb, 0xc0, 0x15, \ + 0xc0, 0x15, 0x63, 0x49, \ + 0x66, 0xc1, 0x00, 0x60, \ + 0x04, 0x14, 0xc0, 0x56, \ + 0xbc, 0xce, 0xfb, 0x00, \ + 0x6a, 0x98, 0x8b, 0x11, \ + 0x00, 0x60, 0xe8, 0x0c, \ + 0xc0, 0xb0, 0x67, 0xe1, \ + 0x64, 0x81, 0x0e, 0x60, \ + 0x04, 0x06, 0xc0, 0xa8, \ + 0x8e, 0x1b, 0xe0, 0x10, \ + 0xef, 0xb0, 0x65, 0xe8, \ + 0x67, 0xe1, 0x00, 0x60, \ + 0x04, 0x06, 0xc0, 0xb1, \ + 0x8e, 0x1b, 0xe0, 0x10, \ + 0xef, 0xb0, 0x65, 0x68, \ + 0x66, 0x11, 0x00, 0x60, \ + 0x02, 0x60, 0xc0, 0xab, \ + 0xc0, 0xa7, 0x67, 0xf1, \ + 0xe0, 0x1c, 0x04, 0x46, \ + 0xef, 0xb0, 0x64, 0xc8, \ + 0x71, 0x98, 0x8c, 0x0b, \ + 0x0c, 0x19, 0xe8, 0x30, \ + 0xc0, 0x30, 0x6e, 0xe5, \ + 0xe3, 0xf0, 0x02, 0x0a, \ + 0xff, 0xf8, 0x00, 0x0e, \ + 0x00, 0x10, 0x80, 0x65, \ + 0x4c, 0x01, 0xe4, 0x04, \ + 0xc0, 0x30, 0x6e, 0xe5, \ + 0x7b, 0xd8, 0x8c, 0x0b, \ + 0xa5, 0x01, 0xe8, 0x00, \ + 0xe8, 0x20, 0x4e, 0x28, \ + 0xe0, 0x04, 0x00, 0x06, \ + 0xe8, 0x10, 0x58, 0x28, \ + 0xa0, 0xf1, 0x8c, 0x0b, \ + 0xf2, 0x3c, 0x0c, 0x0c, \ + 0xff, 0xfc, 0x21, 0x48, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x66, 0x15, 0x00, 0x8a, \ + 0x80, 0xf5, 0xc0, 0x03, \ + 0xe1, 0x14, 0x40, 0xc6, \ + 0x87, 0x11, 0x80, 0x04, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0x65, 0x61, 0x00, 0x8a, \ + 0x80, 0xf5, 0xc0, 0x02, \ + 0xe1, 0x14, 0x40, 0xc6, \ + 0x85, 0x11, 0x80, 0x04, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xe1, 0x14, 0x42, 0xc6, \ + 0x01, 0x4a, 0x84, 0x14, \ + 0xc0, 0x01, 0x64, 0xad, \ + 0xe2, 0x00, 0x00, 0x0c, \ + 0xdd, 0x93, 0x84, 0x06, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x66, 0x61, 0x02, 0x8a, \ + 0x80, 0xd5, 0xc0, 0x03, \ + 0xe1, 0x04, 0x02, 0x06, \ + 0x8d, 0x21, 0x84, 0x14, \ + 0x02, 0x8a, 0x83, 0xc5, \ + 0xc0, 0x03, 0x64, 0x99, \ + 0x02, 0x06, 0x80, 0xd5, \ + 0x84, 0x14, 0xe1, 0x04, \ + 0x83, 0x35, 0x8b, 0x21, \ + 0x66, 0xc9, 0x02, 0x8a, \ + 0x80, 0xd5, 0xc0, 0x02, \ + 0xe1, 0x04, 0x02, 0x06, \ + 0x89, 0x21, 0x84, 0x14, \ + 0x03, 0x0a, 0x81, 0xa5, \ + 0xc0, 0x02, 0x64, 0x95, \ + 0xe1, 0x04, 0x02, 0x06, \ + 0x87, 0x21, 0x84, 0x14, \ + 0x85, 0x21, 0x80, 0x55, \ + 0x02, 0x06, 0x84, 0x26, \ + 0x84, 0x14, 0xe1, 0x04, \ + 0x40, 0x11, 0x84, 0x18, \ + 0xc0, 0x2f, 0x66, 0x89, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0x03, 0x0a, 0xc4, 0x17, \ + 0xc0, 0x0f, 0x66, 0x81, \ + 0x02, 0x8a, 0x86, 0xb5, \ + 0xc0, 0x0f, 0x64, 0xf5, \ + 0x43, 0xc6, 0x80, 0xd5, \ + 0x84, 0x14, 0xe1, 0x00, \ + 0x83, 0x35, 0x81, 0x21, \ + 0x66, 0xcd, 0x02, 0x8a, \ + 0x80, 0xd5, 0xc0, 0x0c, \ + 0xe1, 0x00, 0x43, 0xc6, \ + 0x83, 0x21, 0x84, 0x14, \ + 0x03, 0x0a, 0x81, 0xa5, \ + 0xc0, 0x0a, 0x64, 0xa1, \ + 0xe1, 0x00, 0x43, 0xc6, \ + 0x85, 0x21, 0x84, 0x14, \ + 0x87, 0x21, 0x80, 0x55, \ + 0x43, 0xc6, 0x84, 0x26, \ + 0x84, 0x14, 0xe1, 0x00, \ + 0x40, 0x11, 0x84, 0x18, \ + 0xc0, 0x2f, 0x66, 0x85, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe1, 0x14, 0x42, 0xc6, \ + 0x44, 0x06, 0x84, 0x14, \ + 0x84, 0x18, 0xe1, 0x14, \ + 0x88, 0x16, 0x88, 0x24, \ + 0x40, 0x11, 0x88, 0x18, \ + 0xc0, 0x2f, 0x66, 0x8d, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe1, 0x14, 0x45, 0x06, \ + 0x88, 0x28, 0x88, 0x24, \ + 0x82, 0x85, 0xa1, 0x2b, \ + 0xe1, 0x14, 0x44, 0xc6, \ + 0x88, 0x28, 0x88, 0x24, \ + 0x42, 0x1a, 0x82, 0x21, \ + 0x40, 0x13, 0xe0, 0x0a, \ + 0xc0, 0x17, 0x6f, 0x21, \ + 0x00, 0x15, 0xdd, 0x93, \ + 0xc0, 0x0a, 0x63, 0x6d, \ + 0x82, 0x11, 0xd9, 0x17, \ + 0x6f, 0x21, 0x40, 0x13, \ + 0xdd, 0x93, 0xc0, 0x17, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x70, 0x01, 0x00, 0x06, \ + 0x02, 0x06, 0xc0, 0x20, \ + 0xff, 0xc0, 0x60, 0x01, \ + 0xdd, 0x93, 0x80, 0x1e, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x80, 0x7b, 0x82, 0x6b, \ + 0x81, 0x11, 0x81, 0x01, \ + 0xe8, 0x1c, 0x41, 0x48, \ + 0xe1, 0x44, 0x03, 0x06, \ + 0x02, 0x06, 0x84, 0x24, \ + 0xc0, 0x20, 0x70, 0x01, \ + 0x07, 0x46, 0x84, 0x44, \ + 0x88, 0x28, 0xe1, 0x44, \ + 0x8c, 0x58, 0x8c, 0x34, \ + 0x6e, 0x55, 0x0c, 0xc9, \ + 0x06, 0x06, 0xc0, 0x2f, \ + 0xff, 0xc0, 0x60, 0x01, \ + 0x86, 0x4f, 0xd0, 0x57, \ + 0xa1, 0x7b, 0xa4, 0x4f, \ + 0xaa, 0x4f, 0xe0, 0xc7, \ + 0xf9, 0xb0, 0x44, 0x42, \ + 0xa6, 0x2f, 0x80, 0x45, \ + 0x83, 0x11, 0x84, 0x2e, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0x3f, 0x18, 0xf2, 0x04, \ + 0x00, 0x0c, 0xe0, 0x18, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x31, 0x67, 0x4d, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0xe1, 0x50, 0x41, 0x06, \ + 0x80, 0x8e, 0x81, 0x81, \ + 0x83, 0x01, 0x80, 0xa5, \ + 0x6e, 0xdd, 0x4c, 0x01, \ + 0x41, 0x86, 0xc0, 0x30, \ + 0x80, 0x04, 0xe1, 0x44, \ + 0x43, 0x46, 0x80, 0x94, \ + 0x0e, 0x06, 0xe1, 0x44, \ + 0xff, 0xc0, 0x60, 0x01, \ + 0x92, 0x7f, 0x84, 0x04, \ + 0xfc, 0xc0, 0x04, 0x74, \ + 0x00, 0x94, 0x80, 0x2e, \ + 0x84, 0x14, 0xe1, 0xc0, \ + 0xfa, 0x80, 0x04, 0x04, \ + 0x04, 0x74, 0xa4, 0x2d, \ + 0x84, 0x2e, 0xfc, 0xc4, \ + 0x00, 0x0a, 0x9a, 0xb5, \ + 0xa0, 0xc5, 0xf0, 0x04, \ + 0xf0, 0x08, 0x00, 0x0a, \ + 0xc0, 0x00, 0x0e, 0x38, \ + 0xe0, 0x00, 0x41, 0x76, \ + 0xe8, 0x1c, 0x7c, 0x28, \ + 0xfb, 0x80, 0x00, 0x94, \ + 0x72, 0xfd, 0x43, 0x86, \ + 0x44, 0x12, 0xc0, 0x20, \ + 0x88, 0x44, 0xf1, 0x80, \ + 0xfc, 0x3c, 0x08, 0x0a, \ + 0x72, 0xfd, 0x47, 0xc6, \ + 0x40, 0x32, 0xc0, 0x20, \ + 0x89, 0x81, 0xf1, 0x80, \ + 0x89, 0x81, 0x84, 0xc5, \ + 0x4a, 0x40, 0x80, 0x3b, \ + 0x18, 0x50, 0xf0, 0x3c, \ + 0xf7, 0xcd, 0xec, 0x08, \ + 0x58, 0x40, 0xc9, 0x47, \ + 0xc0, 0x0f, 0x73, 0xfd, \ + 0x83, 0x51, 0x80, 0x55, \ + 0x4a, 0x5a, 0xcd, 0x44, \ + 0x08, 0x4a, 0xe6, 0xb2, \ + 0x8a, 0x83, 0xfc, 0x3c, \ + 0xfa, 0xc5, 0x88, 0x81, \ + 0x44, 0x80, 0x88, 0x34, \ + 0xc0, 0xff, 0x73, 0xf1, \ + 0xfc, 0x3c, 0x06, 0x0a, \ + 0xf1, 0x88, 0x48, 0x12, \ + 0xaa, 0xc5, 0x90, 0x3e, \ + 0xe1, 0x50, 0x43, 0x06, \ + 0xe1, 0x48, 0x05, 0xc6, \ + 0x88, 0x2a, 0x84, 0x14, \ + 0xf0, 0x3c, 0x4a, 0x30, \ + 0xec, 0x08, 0x18, 0x50, \ + 0x18, 0x34, 0xf7, 0xcd, \ + 0x58, 0xc0, 0xe1, 0x90, \ + 0xc0, 0x0f, 0x73, 0xfd, \ + 0x83, 0x51, 0x80, 0x55, \ + 0xe6, 0xb2, 0x58, 0x5a, \ + 0xa1, 0xcb, 0xc5, 0xc7, \ + 0xe4, 0x10, 0x0a, 0x40, \ + 0xe1, 0x37, 0x84, 0x45, \ + 0x46, 0x30, 0x84, 0x33, \ + 0xc1, 0xff, 0x73, 0xf1, \ + 0x82, 0x33, 0x88, 0x41, \ + 0xcd, 0xa4, 0x98, 0x9b, \ + 0xec, 0x04, 0x12, 0x90, \ + 0xd5, 0xae, 0xa1, 0x9b, \ + 0xc4, 0xc7, 0xfe, 0x75, \ + 0xf0, 0xb0, 0x4a, 0x42, \ + 0x06, 0x0a, 0xc1, 0x34, \ + 0x94, 0x3e, 0xfc, 0x3c, \ + 0xf4, 0xf5, 0x8a, 0x4b, \ + 0x00, 0x0a, 0x8f, 0xd5, \ + 0x8a, 0x25, 0xf0, 0x0c, \ + 0xf0, 0x10, 0x00, 0x0a, \ + 0x41, 0xa6, 0x9c, 0xf5, \ + 0x75, 0x78, 0xe0, 0x00, \ + 0x0c, 0x09, 0xe8, 0x1c, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0xe1, 0x44, 0x43, 0x46, \ + 0x40, 0x00, 0x84, 0x14, \ + 0x4c, 0x01, 0xf3, 0xd0, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0x83, 0x31, 0x83, 0x21, \ + 0x8c, 0x0b, 0x83, 0x41, \ + 0xef, 0xf8, 0x5a, 0x28, \ + 0x41, 0x86, 0x8d, 0x15, \ + 0x74, 0x18, 0xe0, 0x00, \ + 0x00, 0x94, 0xe8, 0x1c, \ + 0x47, 0x98, 0xfb, 0x80, \ + 0x0c, 0x05, 0xe8, 0x00, \ + 0xc0, 0x0c, 0x65, 0x65, \ + 0xe1, 0x48, 0x42, 0x06, \ + 0x8b, 0x25, 0x84, 0x0e, \ + 0xe1, 0xe0, 0x10, 0x94, \ + 0xe0, 0x00, 0x41, 0x96, \ + 0xe8, 0x1c, 0x73, 0x08, \ + 0x50, 0x58, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x00, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0xe1, 0x48, 0x42, 0x46, \ + 0x40, 0x00, 0x81, 0x21, \ + 0xd2, 0x09, 0xf3, 0x88, \ + 0xe8, 0x00, 0x12, 0x0a, \ + 0x4c, 0x01, 0x84, 0x2e, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0x67, 0x55, 0x4c, 0x81, \ + 0x00, 0x94, 0xc0, 0x16, \ + 0x80, 0x65, 0xfb, 0x80, \ + 0xe8, 0x00, 0x45, 0x28, \ + 0xe1, 0x44, 0x01, 0xc6, \ + 0x0c, 0x19, 0x80, 0x04, \ + 0xc0, 0x15, 0x6a, 0x51, \ + 0xc4, 0x17, 0x80, 0x28, \ + 0x44, 0x20, 0x88, 0x19, \ + 0xa2, 0x2f, 0xf3, 0xec, \ + 0x0c, 0x09, 0x80, 0x26, \ + 0xc0, 0x2f, 0x65, 0xd5, \ + 0x6e, 0x5d, 0x4c, 0x01, \ + 0x8c, 0x0b, 0xc0, 0x2f, \ + 0xe8, 0x00, 0x50, 0xc8, \ + 0xe1, 0x50, 0x41, 0x06, \ + 0x00, 0x60, 0x80, 0x44, \ + 0xc0, 0x54, 0x65, 0x11, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x99, 0x11, 0x81, 0x01, \ + 0x72, 0xfd, 0x46, 0x46, \ + 0x59, 0x58, 0xc0, 0x20, \ + 0x43, 0x46, 0xe8, 0x00, \ + 0x84, 0x14, 0xe1, 0x44, \ + 0x80, 0x2e, 0x9f, 0x21, \ + 0x7c, 0x51, 0x40, 0x70, \ + 0x84, 0x0e, 0xc3, 0x00, \ + 0xef, 0xf8, 0x53, 0x68, \ + 0x41, 0x46, 0xa9, 0x81, \ + 0x02, 0x84, 0xe1, 0x44, \ + 0x80, 0x04, 0xfb, 0x80, \ + 0x42, 0x70, 0xa2, 0x7f, \ + 0xc3, 0x00, 0x7c, 0x01, \ + 0x0c, 0x09, 0x80, 0x1e, \ + 0xc0, 0x31, 0x67, 0x4d, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x0c, 0x09, 0x80, 0xe5, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0xe0, 0x8c, 0x40, 0x0a, \ + 0x0c, 0x0c, 0x80, 0x95, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0x0c, 0x0c, 0x8c, 0x0b, \ + 0x19, 0xf8, 0xf2, 0x3c, \ + 0xc1, 0xc3, 0xe0, 0x00, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x72, 0xfd, 0x43, 0x86, \ + 0x46, 0x12, 0xc0, 0x20, \ + 0x8c, 0x04, 0xf1, 0x80, \ + 0xfc, 0x3c, 0x00, 0x0a, \ + 0x43, 0x06, 0xa2, 0x65, \ + 0x05, 0xc6, 0xe1, 0x50, \ + 0x84, 0x14, 0xe1, 0x48, \ + 0x88, 0x1a, 0x8d, 0x1e, \ + 0x83, 0x95, 0xbe, 0x1e, \ + 0xa1, 0x1b, 0xc5, 0x17, \ + 0xe1, 0x07, 0x9e, 0x45, \ + 0x84, 0x03, 0xbe, 0x24, \ + 0x73, 0xf1, 0x44, 0x00, \ + 0x00, 0x30, 0xc1, 0xff, \ + 0x8d, 0x34, 0xe4, 0x10, \ + 0x82, 0x3b, 0x86, 0x23, \ + 0x06, 0x30, 0xed, 0x44, \ + 0xa1, 0x3b, 0xec, 0x04, \ + 0xfe, 0x75, 0xc9, 0x4e, \ + 0x56, 0x02, 0xc4, 0x17, \ + 0x8d, 0x55, 0xf0, 0x84, \ + 0xf0, 0x3c, 0x42, 0x00, \ + 0xec, 0x08, 0x04, 0x10, \ + 0x04, 0x04, 0xf7, 0x2d, \ + 0x44, 0x20, 0xe1, 0x90, \ + 0xc0, 0x0f, 0x73, 0xfd, \ + 0x83, 0x11, 0x80, 0x55, \ + 0xe6, 0x8a, 0x42, 0x1a, \ + 0xf8, 0x0c, 0x02, 0x4a, \ + 0xe4, 0x10, 0x16, 0x30, \ + 0x44, 0x00, 0x80, 0xc5, \ + 0xc3, 0x00, 0x70, 0x01, \ + 0xf4, 0x25, 0xa1, 0x2b, \ + 0x44, 0x00, 0xe1, 0x07, \ + 0x8b, 0x2e, 0xf0, 0x0c, \ + 0x02, 0x4a, 0x84, 0x13, \ + 0x14, 0x14, 0xf8, 0x0c, \ + 0x04, 0x0c, 0xe1, 0x88, \ + 0x84, 0xa3, 0xe2, 0x00, \ + 0x8e, 0xe5, 0xa1, 0xab, \ + 0x02, 0x10, 0xfc, 0x3e, \ + 0x04, 0x12, 0xec, 0x04, \ + 0x89, 0x2e, 0xec, 0x0c, \ + 0x84, 0x03, 0xbe, 0x24, \ + 0xfe, 0x1e, 0x86, 0x19, \ + 0xe1, 0x88, 0x12, 0x04, \ + 0xec, 0x04, 0x10, 0xa0, \ + 0xe0, 0x88, 0x0e, 0x94, \ + 0x80, 0x73, 0x8d, 0x04, \ + 0xa1, 0x6b, 0x81, 0x61, \ + 0x8d, 0x8b, 0x82, 0xc5, \ + 0x81, 0x01, 0x84, 0xf5, \ + 0x64, 0xf8, 0xfe, 0x14, \ + 0x9c, 0x14, 0xe8, 0x00, \ + 0x82, 0x0f, 0xac, 0x24, \ + 0xf0, 0x98, 0x42, 0x92, \ + 0x81, 0xf5, 0xa4, 0x0f, \ + 0x89, 0x14, 0x8b, 0x04, \ + 0xe8, 0x00, 0x64, 0x48, \ + 0xac, 0x24, 0x9c, 0x14, \ + 0x40, 0x12, 0x80, 0x1f, \ + 0x8e, 0x1b, 0xf9, 0x88, \ + 0xac, 0x04, 0x81, 0x75, \ + 0xf0, 0x98, 0x42, 0x92, \ + 0x8d, 0x24, 0xc4, 0x17, \ + 0x82, 0x61, 0x84, 0x13, \ + 0x88, 0xb1, 0x8d, 0xab, \ + 0x88, 0x71, 0x84, 0x0e, \ + 0xc4, 0xa7, 0xf6, 0x75, \ + 0x80, 0xa3, 0xfc, 0x04, \ + 0xe4, 0x10, 0x16, 0xa0, \ + 0x00, 0x4a, 0xac, 0x04, \ + 0x96, 0x3b, 0xfc, 0x3c, \ + 0xb8, 0xf1, 0xe6, 0x05, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x01, 0x06, 0x80, 0x6b, \ + 0x80, 0x14, 0xe1, 0x44, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x84, 0x26, 0xf3, 0xbc, \ + 0x84, 0x28, 0x80, 0x14, \ + 0xf3, 0x7c, 0x44, 0x20, \ + 0x80, 0x14, 0x84, 0x26, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x84, 0x26, 0xf2, 0xfc, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xdc, 0x42, 0x10, \ + 0x01, 0x46, 0x80, 0x16, \ + 0x80, 0x14, 0xe1, 0x44, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x84, 0x26, 0xf3, 0xf8, \ + 0x84, 0x28, 0x80, 0x14, \ + 0xf3, 0xec, 0x44, 0x20, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xbc, \ + 0xef, 0xf8, 0x74, 0xa8, \ + 0x8c, 0x1b, 0x83, 0x01, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0xfc, 0x1d, 0x48, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x15, 0x61, 0x51, \ + 0x61, 0x55, 0x0c, 0x19, \ + 0x0c, 0x29, 0xc0, 0x15, \ + 0xc0, 0x15, 0x61, 0xe1, \ + 0xe6, 0x82, 0x42, 0x1a, \ + 0x61, 0xe5, 0x0c, 0x09, \ + 0x4c, 0x11, 0xc0, 0x15, \ + 0xc0, 0x16, 0x67, 0x79, \ + 0x67, 0x7d, 0x4c, 0x21, \ + 0x02, 0x00, 0xc0, 0x16, \ + 0x04, 0x60, 0xe4, 0x30, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0x66, 0x11, 0x06, 0x60, \ + 0x91, 0x41, 0xc0, 0x54, \ + 0x81, 0xc1, 0x81, 0x51, \ + 0xbf, 0x7b, 0x85, 0x05, \ + 0xa1, 0xcb, 0x8e, 0x45, \ + 0x0c, 0x85, 0x82, 0x65, \ + 0xc0, 0x05, 0x64, 0x81, \ + 0x80, 0xd5, 0x91, 0xcb, \ + 0xe4, 0x20, 0x10, 0x40, \ + 0x6b, 0xa1, 0x4c, 0x83, \ + 0x50, 0x70, 0xc0, 0x18, \ + 0xb9, 0x8b, 0xf0, 0x3c, \ + 0x88, 0x41, 0x80, 0x75, \ + 0xa1, 0x8b, 0x85, 0x35, \ + 0x4e, 0x70, 0xc9, 0x77, \ + 0xc0, 0x0f, 0x73, 0xfd, \ + 0x80, 0x65, 0x88, 0x41, \ + 0xe6, 0xa2, 0x4e, 0x7a, \ + 0x83, 0x85, 0x8e, 0x43, \ + 0xe0, 0x00, 0x18, 0x4a, \ + 0xf8, 0x7c, 0x18, 0x1a, \ + 0xe1, 0x84, 0x40, 0x0a, \ + 0x88, 0x31, 0x80, 0x55, \ + 0x81, 0x51, 0x80, 0x55, \ + 0x4e, 0x0a, 0x8c, 0x74, \ + 0xc9, 0x74, 0xe4, 0x94, \ + 0x10, 0x70, 0x82, 0x25, \ + 0xa1, 0x7b, 0xec, 0x34, \ + 0xe4, 0x08, 0x10, 0xda, \ + 0xed, 0x80, 0x42, 0x0a, \ + 0x82, 0xc1, 0xf2, 0x75, \ + 0xe6, 0x00, 0x18, 0x8a, \ + 0xf8, 0xb5, 0x82, 0x51, \ + 0x02, 0x8a, 0x88, 0x13, \ + 0xc0, 0x2c, 0x6c, 0x01, \ + 0x0c, 0x29, 0x82, 0x15, \ + 0xc0, 0x31, 0x67, 0x4d, \ + 0x4c, 0x21, 0xd0, 0x29, \ + 0xc0, 0x31, 0x67, 0x4d, \ + 0x64, 0x7d, 0x0c, 0x25, \ + 0x06, 0x14, 0xc0, 0x05, \ + 0x87, 0x2b, 0xfb, 0x80, \ + 0x91, 0x31, 0x81, 0x21, \ + 0x68, 0x9d, 0x4c, 0x23, \ + 0x4c, 0x33, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x68, 0x89, \ + 0x68, 0x91, 0x4c, 0x03, \ + 0x88, 0x01, 0xc0, 0x0a, \ + 0x68, 0x95, 0x4c, 0x03, \ + 0x00, 0x10, 0xc0, 0x0a, \ + 0x4c, 0x13, 0xe4, 0x0c, \ + 0xc0, 0x0a, 0x68, 0x99, \ + 0x68, 0x8d, 0x4c, 0x03, \ + 0x82, 0x85, 0xc0, 0x0a, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x00, 0x60, 0xc0, 0x2f, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0x45, 0x46, 0x81, 0x11, \ + 0x71, 0xf8, 0xe0, 0x00, \ + 0x8c, 0x0b, 0xe8, 0x08, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0xe0, 0x00, 0x18, 0x68, \ + 0xf8, 0x00, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x01, 0xc6, 0x80, 0x5b, \ + 0x80, 0xca, 0xe1, 0x48, \ + 0xe0, 0x90, 0x00, 0x14, \ + 0xf0, 0x3c, 0x4c, 0x50, \ + 0xfb, 0x80, 0x00, 0x04, \ + 0xb8, 0x23, 0xac, 0x0f, \ + 0xe0, 0xc0, 0x0c, 0x24, \ + 0xa0, 0x6f, 0xa1, 0x5b, \ + 0xc1, 0x6e, 0x86, 0x0b, \ + 0x83, 0x51, 0x80, 0x55, \ + 0xe6, 0x86, 0x42, 0x5a, \ + 0xa1, 0x1b, 0xc5, 0x17, \ + 0x04, 0x24, 0x84, 0x65, \ + 0x98, 0x23, 0xfb, 0x80, \ + 0x73, 0xf1, 0x4a, 0x20, \ + 0x04, 0x10, 0xc1, 0xff, \ + 0x8a, 0x43, 0xe4, 0x04, \ + 0x02, 0x10, 0xd1, 0x54, \ + 0xa1, 0x1b, 0xec, 0x04, \ + 0xfe, 0x75, 0xc1, 0x5e, \ + 0x40, 0x32, 0xc4, 0x27, \ + 0x88, 0xf1, 0xf0, 0x88, \ + 0xf0, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x00, 0x86, 0x80, 0x6b, \ + 0x80, 0x04, 0xe1, 0x34, \ + 0xe1, 0x50, 0x53, 0x06, \ + 0xa4, 0x8e, 0x81, 0x81, \ + 0xe0, 0x04, 0x02, 0x96, \ + 0x6e, 0x59, 0x4c, 0x11, \ + 0x80, 0x16, 0xc0, 0x2f, \ + 0x6e, 0x51, 0x0c, 0x09, \ + 0x40, 0x00, 0xc0, 0x2f, \ + 0x4c, 0x01, 0xf3, 0x7c, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0xe0, 0x00, 0x40, 0x86, \ + 0xe8, 0x1c, 0x50, 0xc8, \ + 0x60, 0x01, 0x00, 0x06, \ + 0x5a, 0xb8, 0xc2, 0x00, \ + 0x81, 0x01, 0xe8, 0x0c, \ + 0x4f, 0xb8, 0x81, 0x11, \ + 0xfc, 0x0e, 0xe8, 0x18, \ + 0xe1, 0x44, 0x01, 0x06, \ + 0x01, 0x46, 0x80, 0x14, \ + 0xbe, 0x1e, 0xe1, 0x44, \ + 0x80, 0x04, 0x84, 0x18, \ + 0x0c, 0x2b, 0x80, 0x08, \ + 0xc0, 0x0a, 0x68, 0x91, \ + 0x67, 0x4d, 0x0c, 0x39, \ + 0xf1, 0x2d, 0xc0, 0x31, \ + 0x82, 0x39, 0xd0, 0x07, \ + 0xa0, 0x1f, 0xbc, 0x0e, \ + 0xa6, 0x1f, 0xf0, 0x37, \ + 0x68, 0x01, 0x4c, 0x15, \ + 0x00, 0x60, 0xc0, 0x02, \ + 0xc0, 0x28, 0x64, 0x21, \ + 0x0c, 0x1b, 0x82, 0x75, \ + 0xc0, 0x0a, 0x68, 0x91, \ + 0x83, 0x21, 0xc5, 0x17, \ + 0x82, 0x21, 0x81, 0x31, \ + 0xc1, 0x3e, 0xc3, 0x2d, \ + 0x03, 0x86, 0xfe, 0xb5, \ + 0x84, 0x2a, 0xe1, 0x48, \ + 0x08, 0x60, 0xc1, 0x2e, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0x66, 0x11, 0x0a, 0x60, \ + 0x03, 0xc6, 0xc0, 0x54, \ + 0xa4, 0x34, 0xe1, 0x48, \ + 0x84, 0x1a, 0x89, 0x3e, \ + 0x81, 0xa1, 0xfe, 0x1e, \ + 0x14, 0x4a, 0x81, 0xb1, \ + 0x14, 0x1a, 0xe0, 0x00, \ + 0x40, 0x0a, 0xf8, 0x7c, \ + 0x80, 0x55, 0xe1, 0x84, \ + 0x80, 0x55, 0x88, 0x51, \ + 0x94, 0x14, 0x81, 0xb1, \ + 0xe4, 0xac, 0x42, 0x0a, \ + 0x90, 0x25, 0xd1, 0x34, \ + 0x80, 0xc5, 0xa1, 0xab, \ + 0x64, 0x81, 0x0c, 0x15, \ + 0x83, 0xab, 0xc0, 0x05, \ + 0x02, 0x30, 0x90, 0x05, \ + 0xe4, 0x1d, 0xec, 0x34, \ + 0xbf, 0x3b, 0x80, 0xb5, \ + 0x8c, 0x75, 0xc1, 0x3e, \ + 0xa1, 0x3b, 0x89, 0x25, \ + 0x4e, 0x30, 0x8c, 0x05, \ + 0xa1, 0x7b, 0xf0, 0x3c, \ + 0x02, 0x34, 0x80, 0x3e, \ + 0x18, 0x34, 0xe1, 0x90, \ + 0x42, 0x10, 0xe1, 0xc0, \ + 0xc0, 0x0f, 0x73, 0xfd, \ + 0xb9, 0x7b, 0x82, 0x05, \ + 0x88, 0x01, 0x80, 0x95, \ + 0x83, 0xf5, 0x98, 0x8b, \ + 0xe6, 0x9e, 0x42, 0x1a, \ + 0xa1, 0x1b, 0xc5, 0x17, \ + 0xe4, 0x10, 0x06, 0x00, \ + 0xfe, 0x74, 0x84, 0xe5, \ + 0x58, 0xc0, 0x8e, 0xc3, \ + 0xc1, 0xff, 0x73, 0xf1, \ + 0x89, 0x74, 0x88, 0x01, \ + 0x82, 0x7b, 0x8e, 0xc3, \ + 0x80, 0x75, 0xa1, 0x8b, \ + 0x8c, 0x9e, 0xb0, 0x94, \ + 0xec, 0x04, 0x0e, 0x70, \ + 0x88, 0x31, 0xa1, 0x7b, \ + 0xfc, 0xf5, 0x88, 0xc1, \ + 0x82, 0x03, 0xc4, 0x17, \ + 0x86, 0x0b, 0x81, 0x25, \ + 0x14, 0x8a, 0x82, 0xa1, \ + 0x82, 0xb1, 0xe6, 0x00, \ + 0x81, 0xb5, 0xea, 0xb5, \ + 0x68, 0x99, 0x0c, 0x0b, \ + 0x40, 0x00, 0xc0, 0x0a, \ + 0xc0, 0xff, 0x73, 0xf1, \ + 0x00, 0x00, 0x8c, 0x03, \ + 0xc0, 0x28, 0x64, 0x01, \ + 0xbe, 0x04, 0x80, 0x2e, \ + 0x0c, 0x19, 0x80, 0x08, \ + 0xc0, 0x16, 0x67, 0x5d, \ + 0xbc, 0x24, 0xf0, 0x17, \ + 0xa2, 0x0f, 0xa4, 0x0f, \ + 0x68, 0x01, 0x4c, 0x05, \ + 0x8c, 0x0b, 0xc0, 0x02, \ + 0xe8, 0x0c, 0x64, 0xe8, \ + 0xfc, 0x04, 0x83, 0x11, \ + 0xe8, 0x18, 0x44, 0xe8, \ + 0xe1, 0x44, 0x01, 0x06, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xe0, 0x90, 0x42, 0x0a, \ + 0x80, 0x18, 0x82, 0x65, \ + 0x42, 0x10, 0x80, 0x28, \ + 0x44, 0x20, 0xf0, 0x80, \ + 0xa2, 0x2f, 0xf3, 0x7c, \ + 0xfd, 0x14, 0x02, 0x24, \ + 0x41, 0xc6, 0x80, 0x16, \ + 0x80, 0x14, 0xe1, 0x44, \ + 0xe1, 0x44, 0x05, 0x46, \ + 0x88, 0x34, 0x86, 0x18, \ + 0xa0, 0x19, 0x8c, 0x48, \ + 0xf3, 0xbc, 0x48, 0x40, \ + 0x8c, 0x46, 0xa2, 0x4f, \ + 0x82, 0x08, 0x80, 0x04, \ + 0x84, 0x28, 0x88, 0x14, \ + 0x44, 0x20, 0x90, 0x09, \ + 0xa0, 0x2f, 0xf3, 0xdc, \ + 0x40, 0x46, 0x84, 0x26, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0x82, 0x45, 0xa1, 0x0b, \ + 0x6e, 0x51, 0x0c, 0x09, \ + 0x00, 0x04, 0xc0, 0x2f, \ + 0x4c, 0x01, 0xfc, 0x94, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0x8c, 0x0b, 0x81, 0x45, \ + 0xe8, 0x00, 0x45, 0xe8, \ + 0x8c, 0x1b, 0x81, 0x01, \ + 0xef, 0xf8, 0x7f, 0x68, \ + 0xef, 0xf8, 0x56, 0x48, \ + 0xe0, 0x00, 0x40, 0x96, \ + 0xe8, 0x1c, 0x41, 0x48, \ + 0x60, 0x01, 0x00, 0x06, \ + 0xa8, 0xf1, 0xc4, 0x00, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xe0, 0x0c, 0x0b, 0x08, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x84, 0x65, 0xa5, 0x0b, \ + 0x86, 0x05, 0xa3, 0x0b, \ + 0x00, 0x0e, 0xa1, 0x0b, \ + 0x88, 0x55, 0xff, 0xf0, \ + 0x88, 0x25, 0xa7, 0x1b, \ + 0x88, 0x25, 0xa5, 0x1b, \ + 0x41, 0xfe, 0xa3, 0x1b, \ + 0xc0, 0x3f, 0x7f, 0xfd, \ + 0x41, 0xf6, 0x86, 0x35, \ + 0xdd, 0x93, 0xef, 0xfc, \ + 0x41, 0xfe, 0xa5, 0x1b, \ + 0x84, 0x45, 0xf0, 0x0c, \ + 0xf0, 0x00, 0x00, 0x0e, \ + 0xa5, 0x1b, 0x81, 0xf5, \ + 0x60, 0x0d, 0x41, 0xf6, \ + 0x80, 0x85, 0xff, 0xc0, \ + 0x6f, 0xfd, 0x41, 0xf6, \ + 0xa7, 0x1b, 0xff, 0xc0, \ + 0x00, 0x06, 0x80, 0xb5, \ + 0xff, 0xc0, 0x60, 0x01, \ + 0xdd, 0x93, 0xdd, 0x93, \ + 0xdd, 0x93, 0x81, 0x01, \ + 0xe0, 0x0c, 0x41, 0xf6, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x68, 0x8d, 0x00, 0x1b, \ + 0x05, 0xc6, 0xc0, 0x0a, \ + 0x06, 0x06, 0xe1, 0x3c, \ + 0xc0, 0x20, 0x70, 0x01, \ + 0xa6, 0x1f, 0x88, 0x24, \ + 0x65, 0xa1, 0x40, 0x15, \ + 0x40, 0x15, 0xc0, 0x0c, \ + 0xc0, 0x0c, 0x65, 0xa5, \ + 0x40, 0x06, 0x88, 0x1e, \ + 0x80, 0x04, 0xe1, 0x3c, \ + 0xdd, 0x93, 0x80, 0x1e, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x41, 0x26, 0x80, 0x6b, \ + 0x7b, 0x98, 0xe0, 0x00, \ + 0x81, 0x01, 0xe8, 0x18, \ + 0x7a, 0xd8, 0x81, 0x11, \ + 0x80, 0x7b, 0xe8, 0x14, \ + 0x68, 0x99, 0x0c, 0x0b, \ + 0x0c, 0x15, 0xc0, 0x0a, \ + 0xc0, 0x02, 0x68, 0x01, \ + 0xf9, 0x0d, 0x88, 0x01, \ + 0x70, 0x01, 0x00, 0x06, \ + 0x80, 0x1e, 0xc0, 0x20, \ + 0x0c, 0x0b, 0x84, 0x75, \ + 0xc0, 0x0a, 0x68, 0x99, \ + 0xe4, 0x10, 0x02, 0x00, \ + 0x64, 0x21, 0x00, 0x60, \ + 0xc5, 0x17, 0xc0, 0x28, \ + 0x06, 0x86, 0x85, 0x21, \ + 0xc0, 0x20, 0x70, 0x01, \ + 0x82, 0x21, 0xc1, 0x44, \ + 0xcd, 0x4e, 0xc3, 0x2d, \ + 0x01, 0x06, 0xfe, 0x95, \ + 0x80, 0x04, 0xe1, 0x44, \ + 0xe0, 0x19, 0x80, 0x18, \ + 0x81, 0x01, 0x80, 0x16, \ + 0x76, 0x78, 0x8c, 0x1b, \ + 0x4d, 0x58, 0xef, 0xf8, \ + 0x8c, 0x0b, 0xef, 0xf8, \ + 0xe8, 0x0c, 0x58, 0xa8, \ + 0xe1, 0x48, 0x01, 0x86, \ + 0x82, 0x11, 0x80, 0x1a, \ + 0x83, 0x11, 0x80, 0x1c, \ + 0x88, 0xf1, 0x8e, 0x0b, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0xe0, 0x14, 0x37, 0x28, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x03, 0x06, 0x82, 0x6b, \ + 0x84, 0x14, 0xe1, 0x44, \ + 0x46, 0x00, 0x84, 0x28, \ + 0x08, 0x34, 0xf0, 0x04, \ + 0x44, 0x20, 0xe0, 0x98, \ + 0xa8, 0x2f, 0xf2, 0xfc, \ + 0xe1, 0x48, 0x48, 0x46, \ + 0x90, 0x0e, 0x84, 0x26, \ + 0x6e, 0x51, 0x0c, 0x09, \ + 0xc8, 0x37, 0xc0, 0x2f, \ + 0xf3, 0xbc, 0x40, 0x00, \ + 0x4c, 0x01, 0xa6, 0x0f, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0xef, 0xf8, 0x4a, 0x88, \ + 0x8c, 0x1b, 0x81, 0x01, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0xf8, 0x33, 0x28, \ + 0xe1, 0x00, 0x40, 0x06, \ + 0x80, 0x08, 0x80, 0x04, \ + 0xdd, 0x93, 0x82, 0x09, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xc7, 0x13, 0xc1, 0x83, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x82, 0x8b, 0x84, 0x7b, \ + 0x81, 0x01, 0x80, 0x6b, \ + 0x67, 0x69, 0x4c, 0x01, \ + 0x04, 0x60, 0xc0, 0x30, \ + 0xc0, 0xc1, 0x67, 0x71, \ + 0x8c, 0x0b, 0x83, 0x11, \ + 0xe8, 0x00, 0x42, 0x28, \ + 0x8c, 0x0b, 0x83, 0x11, \ + 0xe8, 0x00, 0x4d, 0xa8, \ + 0x6f, 0x78, 0x8c, 0x0b, \ + 0x81, 0x11, 0xe8, 0x3c, \ + 0xe0, 0x00, 0x45, 0x76, \ + 0x8c, 0x0b, 0x89, 0x31, \ + 0xe8, 0x3c, 0x75, 0x08, \ + 0x82, 0x75, 0xa3, 0x8b, \ + 0x8e, 0x1b, 0x8c, 0x0b, \ + 0xe8, 0x00, 0x4e, 0xe8, \ + 0x81, 0x21, 0x81, 0x11, \ + 0x0c, 0x0c, 0x8c, 0x0b, \ + 0xa7, 0xf5, 0xf2, 0x0c, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xec, 0xc0, 0x1e, 0xf0, \ + 0x46, 0x86, 0xa3, 0x1b, \ + 0x40, 0x31, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xa5, 0x1b, 0x8a, 0xa5, \ + 0x00, 0x06, 0xaa, 0x95, \ + 0x93, 0x0e, 0xe0, 0x08, \ + 0x97, 0x0e, 0x91, 0x01, \ + 0xe0, 0x04, 0x00, 0x06, \ + 0x89, 0x11, 0x91, 0x0e, \ + 0xe0, 0x00, 0x40, 0x06, \ + 0x85, 0x01, 0x8b, 0x0e, \ + 0xa1, 0x01, 0x95, 0x0e, \ + 0x81, 0x01, 0xfe, 0x0e, \ + 0x83, 0x01, 0x89, 0x0e, \ + 0x83, 0x01, 0xfc, 0x0e, \ + 0x81, 0xa1, 0xbe, 0x0e, \ + 0xbc, 0x0e, 0xa1, 0x01, \ + 0x8d, 0x0e, 0x81, 0x01, \ + 0x8f, 0x0e, 0x85, 0x01, \ + 0x52, 0x06, 0x81, 0x81, \ + 0x81, 0xb1, 0xe0, 0x00, \ + 0x81, 0x51, 0x81, 0x41, \ + 0xe0, 0x04, 0x18, 0x06, \ + 0x81, 0x31, 0x81, 0x61, \ + 0xe0, 0x08, 0x00, 0x06, \ + 0x47, 0xf6, 0x87, 0x95, \ + 0x41, 0xe6, 0xe0, 0x0c, \ + 0x97, 0x0e, 0xe0, 0x0c, \ + 0xe0, 0x0c, 0x4b, 0xa6, \ + 0xe0, 0x0c, 0x03, 0xa6, \ + 0xe0, 0x04, 0x01, 0xa6, \ + 0x01, 0x26, 0x8f, 0x0e, \ + 0x8d, 0x0e, 0xe0, 0x04, \ + 0xe0, 0x04, 0x01, 0x86, \ + 0x15, 0x06, 0x95, 0x0e, \ + 0xa1, 0x01, 0xe0, 0x04, \ + 0x00, 0x06, 0x89, 0x0e, \ + 0xfc, 0x0e, 0xe0, 0x04, \ + 0xe0, 0x04, 0x00, 0x06, \ + 0xa1, 0x01, 0xfe, 0x0e, \ + 0x01, 0x06, 0xbe, 0x0e, \ + 0xbc, 0x0e, 0xe0, 0x04, \ + 0xe0, 0x04, 0x01, 0x86, \ + 0x11, 0x86, 0x8b, 0x0e, \ + 0x13, 0xa6, 0xe0, 0x04, \ + 0x17, 0xa6, 0xe0, 0x04, \ + 0x09, 0xa6, 0xe0, 0x04, \ + 0x01, 0xa6, 0xe0, 0x0c, \ + 0x91, 0x0e, 0xe0, 0x0c, \ + 0xe0, 0x0c, 0x59, 0xa6, \ + 0xe0, 0x0c, 0x4d, 0xe6, \ + 0xe0, 0x0c, 0x41, 0xe6, \ + 0x41, 0xf6, 0x93, 0x0e, \ + 0x81, 0x71, 0xe0, 0x0c, \ + 0xfc, 0x74, 0x88, 0x76, \ + 0x89, 0x74, 0x8a, 0x76, \ + 0xfe, 0x74, 0xc8, 0x76, \ + 0xbe, 0x74, 0xca, 0x76, \ + 0xe0, 0x10, 0x44, 0x71, \ + 0x44, 0x71, 0xbc, 0x74, \ + 0x44, 0xa1, 0xe0, 0x18, \ + 0x44, 0xa1, 0xe0, 0x14, \ + 0x44, 0xa1, 0xe0, 0x1c, \ + 0x95, 0x74, 0xe0, 0x20, \ + 0xe0, 0x24, 0x44, 0x71, \ + 0x44, 0x71, 0x8d, 0x74, \ + 0x8b, 0x74, 0xe0, 0x28, \ + 0xe0, 0x2c, 0x44, 0x71, \ + 0x44, 0x71, 0x8f, 0x74, \ + 0x44, 0x81, 0xe0, 0x30, \ + 0x44, 0x91, 0xe0, 0x34, \ + 0x44, 0xb1, 0xe0, 0x38, \ + 0x44, 0xb1, 0xe0, 0x3c, \ + 0x44, 0x41, 0xe0, 0x40, \ + 0x91, 0x44, 0xe0, 0x48, \ + 0xe0, 0x4c, 0x44, 0x41, \ + 0xe0, 0x44, 0x44, 0x11, \ + 0xe0, 0x50, 0x44, 0x11, \ + 0xe0, 0x58, 0x44, 0xc1, \ + 0xe0, 0x54, 0x44, 0x51, \ + 0xe0, 0x5c, 0x44, 0x51, \ + 0xe0, 0x60, 0x44, 0x51, \ + 0xe0, 0x68, 0x44, 0x61, \ + 0x44, 0x11, 0x93, 0x14, \ + 0x97, 0x14, 0xe0, 0x6c, \ + 0xe0, 0x64, 0x44, 0x11, \ + 0xe0, 0x70, 0x44, 0x11, \ + 0xe0, 0x78, 0x44, 0x01, \ + 0xe0, 0x74, 0x44, 0x31, \ + 0xe0, 0x7c, 0x44, 0x31, \ + 0x0c, 0x0c, 0xe0, 0xf1, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x90, 0x1e, 0xf0, \ + 0xa5, 0x1b, 0x80, 0x6b, \ + 0xa3, 0x1b, 0x82, 0x65, \ + 0x02, 0x60, 0x84, 0xf5, \ + 0xc0, 0xc1, 0x67, 0x71, \ + 0x46, 0xf8, 0x8c, 0x0b, \ + 0x83, 0x01, 0xe8, 0x40, \ + 0x0e, 0xf0, 0x81, 0xc5, \ + 0x8c, 0x0b, 0xe4, 0x10, \ + 0x72, 0xf8, 0x8e, 0x2b, \ + 0x8c, 0x0b, 0xef, 0xfc, \ + 0x46, 0x38, 0x8e, 0x1b, \ + 0x85, 0x01, 0xe8, 0x40, \ + 0x67, 0x69, 0x4c, 0x01, \ + 0xc8, 0xf1, 0xc0, 0x30, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x80, 0x6b, 0x82, 0x7b, \ + 0xe4, 0x10, 0x02, 0xf0, \ + 0x6b, 0xf8, 0x9e, 0x2b, \ + 0x0e, 0x2a, 0xe8, 0x0c, \ + 0x89, 0x88, 0xe0, 0x04, \ + 0x00, 0x98, 0xbc, 0x78, \ + 0xc0, 0x00, 0x40, 0x01, \ + 0x52, 0xd8, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xe8, 0x00, \ + 0xe8, 0x00, 0x55, 0x28, \ + 0x66, 0xa1, 0x12, 0x60, \ + 0x81, 0xa1, 0xc0, 0xbf, \ + 0xa4, 0xac, 0x81, 0xb1, \ + 0xe0, 0x90, 0x00, 0x84, \ + 0xf9, 0x9c, 0x42, 0x02, \ + 0xf0, 0xac, 0x40, 0x62, \ + 0x61, 0x21, 0x40, 0x11, \ + 0x40, 0xa1, 0xc0, 0x30, \ + 0xc0, 0x30, 0x62, 0x61, \ + 0x40, 0x01, 0x00, 0xe8, \ + 0x43, 0xf6, 0xc0, 0x00, \ + 0x40, 0x11, 0xe0, 0x0c, \ + 0xc0, 0x30, 0x64, 0xe1, \ + 0x66, 0x21, 0x40, 0x11, \ + 0x40, 0xa1, 0xc0, 0x30, \ + 0xc0, 0x30, 0x63, 0xa1, \ + 0xfa, 0x80, 0x02, 0xb4, \ + 0xfa, 0x80, 0x04, 0x74, \ + 0x53, 0xb8, 0x8c, 0x0b, \ + 0x02, 0x80, 0xe8, 0x00, \ + 0x89, 0x88, 0xe4, 0x04, \ + 0xfa, 0x80, 0x00, 0x14, \ + 0xe4, 0x24, 0x04, 0x80, \ + 0xe4, 0x00, 0x44, 0xca, \ + 0x00, 0x0c, 0x82, 0xb1, \ + 0x80, 0x45, 0xe2, 0x00, \ + 0x16, 0x0a, 0x82, 0x8b, \ + 0x80, 0x73, 0xe1, 0x40, \ + 0xf2, 0x95, 0x84, 0x91, \ + 0x0c, 0x0c, 0x90, 0xf1, \ + 0xdd, 0x93, 0xf6, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xee, 0x70, 0x1e, 0xf0, \ + 0xa1, 0x1b, 0x84, 0x7b, \ + 0x00, 0x19, 0xfc, 0x0e, \ + 0xc0, 0x30, 0x67, 0x69, \ + 0xa5, 0x1b, 0x8e, 0x05, \ + 0xe0, 0x04, 0x42, 0x66, \ + 0x6e, 0x59, 0x40, 0x11, \ + 0x82, 0x05, 0xc0, 0x2f, \ + 0x77, 0x18, 0x85, 0x11, \ + 0xfc, 0x04, 0xef, 0xfc, \ + 0xe8, 0x04, 0x6e, 0xe8, \ + 0x0e, 0x8a, 0xfc, 0x04, \ + 0x82, 0xb5, 0xe4, 0x40, \ + 0x6e, 0x59, 0x00, 0x39, \ + 0x00, 0x00, 0xc0, 0x2f, \ + 0xc0, 0x56, 0x66, 0xf1, \ + 0x81, 0x11, 0x81, 0x71, \ + 0x44, 0xb8, 0xab, 0x21, \ + 0xfc, 0x04, 0xe8, 0x08, \ + 0x43, 0xc6, 0x81, 0x41, \ + 0xc0, 0x00, 0x61, 0x51, \ + 0xe0, 0x80, 0x48, 0x0a, \ + 0x80, 0x65, 0x8e, 0x2b, \ + 0xfc, 0x04, 0x0e, 0x29, \ + 0xe4, 0x40, 0x06, 0xf0, \ + 0x82, 0x41, 0x88, 0x33, \ + 0xe2, 0x30, 0x08, 0x0a, \ + 0xfc, 0x75, 0x8c, 0x26, \ + 0x6e, 0x18, 0x8e, 0x1b, \ + 0xfc, 0x04, 0xe8, 0x04, \ + 0xe0, 0x00, 0x4d, 0x76, \ + 0x93, 0x75, 0x81, 0x81, \ + 0x42, 0xa6, 0xa3, 0x1b, \ + 0x40, 0x11, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x80, 0xa5, 0x80, 0x6b, \ + 0x8c, 0x0b, 0x83, 0x11, \ + 0xef, 0xfc, 0x73, 0x88, \ + 0xe4, 0x40, 0x00, 0xf0, \ + 0x81, 0x11, 0x81, 0xb1, \ + 0xe0, 0x08, 0x04, 0xc6, \ + 0x73, 0xf8, 0xfe, 0x0e, \ + 0x00, 0x60, 0xef, 0xa8, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0x81, 0x91, 0xbe, 0x0e, \ + 0x8c, 0x0b, 0x81, 0x81, \ + 0xf1, 0xac, 0x4c, 0x02, \ + 0x61, 0x21, 0x0c, 0x19, \ + 0x14, 0x14, 0xc0, 0x30, \ + 0x95, 0x21, 0xe1, 0x90, \ + 0xe6, 0x8a, 0x4e, 0xaa, \ + 0x82, 0x73, 0x9e, 0x19, \ + 0xe6, 0x30, 0x0f, 0x0a, \ + 0x00, 0x15, 0x80, 0xd5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0x82, 0x55, 0xab, 0x1b, \ + 0x6e, 0x59, 0x00, 0x39, \ + 0x81, 0x11, 0xc0, 0x2f, \ + 0xbe, 0x04, 0x8d, 0x21, \ + 0xe8, 0x04, 0x72, 0x48, \ + 0x0c, 0x19, 0xfc, 0x04, \ + 0xc0, 0x30, 0x63, 0xa1, \ + 0xfe, 0x24, 0xa1, 0xbb, \ + 0x9c, 0x16, 0x84, 0x73, \ + 0x04, 0x84, 0x82, 0x45, \ + 0x02, 0x94, 0xfa, 0x80, \ + 0x50, 0xa2, 0xfa, 0x80, \ + 0x52, 0xa2, 0xfb, 0x88, \ + 0x81, 0x35, 0xfa, 0x84, \ + 0x94, 0x9b, 0x94, 0x8b, \ + 0xec, 0x04, 0x16, 0xb0, \ + 0xff, 0xf8, 0x43, 0x0e, \ + 0xf2, 0x75, 0x83, 0xbb, \ + 0xe0, 0x0c, 0x03, 0x16, \ + 0x6e, 0x59, 0x40, 0x11, \ + 0x81, 0xb1, 0xc0, 0x2f, \ + 0xfa, 0x80, 0x02, 0xb4, \ + 0x61, 0xb8, 0x80, 0x7b, \ + 0xa1, 0x0b, 0xe8, 0x00, \ + 0x40, 0x72, 0x8a, 0x85, \ + 0x00, 0x09, 0xf0, 0xac, \ + 0xc0, 0x30, 0x6d, 0x51, \ + 0xe1, 0x90, 0x14, 0x04, \ + 0x4c, 0xaa, 0x95, 0x11, \ + 0x9e, 0x09, 0xe6, 0x86, \ + 0x0d, 0x0a, 0x80, 0x63, \ + 0x8e, 0x0b, 0xe6, 0x30, \ + 0x00, 0x15, 0x80, 0xd5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0x82, 0x55, 0xad, 0x1b, \ + 0x6e, 0x59, 0x00, 0x39, \ + 0x81, 0x11, 0xc0, 0x2f, \ + 0xbe, 0x04, 0x8d, 0x21, \ + 0xe8, 0x04, 0x6e, 0x08, \ + 0xfe, 0x14, 0xfc, 0x04, \ + 0x9f, 0x11, 0x82, 0x63, \ + 0x04, 0x84, 0x98, 0x16, \ + 0x02, 0x94, 0xfa, 0x80, \ + 0x50, 0xa2, 0xfa, 0x80, \ + 0x52, 0xa2, 0xfb, 0x88, \ + 0x81, 0x25, 0xfa, 0x84, \ + 0x82, 0xb1, 0x8e, 0x0b, \ + 0xe0, 0x80, 0x16, 0x0a, \ + 0xc4, 0x87, 0xf2, 0x55, \ + 0x0c, 0x90, 0xc4, 0x97, \ + 0x6b, 0xb8, 0xe4, 0x10, \ + 0xfc, 0x04, 0xe8, 0x00, \ + 0xfa, 0x80, 0x02, 0x84, \ + 0xfa, 0x80, 0x04, 0x64, \ + 0xe4, 0x40, 0x06, 0xf0, \ + 0xe8, 0x3c, 0x70, 0x68, \ + 0xe6, 0x70, 0x1e, 0xf0, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0x40, 0x11, 0x81, 0x11, \ + 0xc0, 0x30, 0x68, 0x1d, \ + 0x68, 0x19, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x30, \ + 0xc0, 0x30, 0x68, 0x15, \ + 0x68, 0x11, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x30, \ + 0xc0, 0x30, 0x68, 0x0d, \ + 0x68, 0x09, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x30, \ + 0xc0, 0x30, 0x68, 0x05, \ + 0x68, 0x01, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x30, \ + 0xc0, 0x30, 0x67, 0xfd, \ + 0x67, 0xf9, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x30, \ + 0xc0, 0x30, 0x67, 0xf5, \ + 0x67, 0xf1, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x30, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x00, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x64, 0x21, 0x00, 0x00, \ + 0x81, 0x11, 0xc0, 0xc2, \ + 0x67, 0xb8, 0xb9, 0x21, \ + 0x88, 0xf1, 0xef, 0xa8, \ + 0xf2, 0x00, 0x08, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x82, 0x8b, 0xfc, 0x2e, \ + 0x0c, 0x90, 0x80, 0x9b, \ + 0xc0, 0xbf, 0x66, 0xa1, \ + 0xf0, 0xa0, 0x54, 0x62, \ + 0xe2, 0x80, 0x14, 0x19, \ + 0xe8, 0x00, 0x55, 0xa8, \ + 0xc2, 0x87, 0x80, 0x7b, \ + 0xe7, 0xc0, 0x00, 0xa0, \ + 0x90, 0x63, 0xbc, 0x0e, \ + 0x12, 0x90, 0xbe, 0x6e, \ + 0xc0, 0xc1, 0x67, 0x75, \ + 0x81, 0x61, 0x81, 0xb1, \ + 0x81, 0x81, 0x81, 0xa1, \ + 0xf8, 0x04, 0x12, 0x09, \ + 0xfc, 0x24, 0xa4, 0x18, \ + 0xe0, 0x80, 0x44, 0x0a, \ + 0xa2, 0x0f, 0xd0, 0x07, \ + 0x00, 0x04, 0x80, 0xa5, \ + 0x62, 0x78, 0xfb, 0x80, \ + 0x8e, 0x0f, 0xe8, 0x00, \ + 0xfb, 0x80, 0x02, 0x64, \ + 0x80, 0xc5, 0x83, 0x0b, \ + 0xb7, 0x1f, 0x83, 0x11, \ + 0x82, 0xa1, 0xa2, 0x8f, \ + 0x82, 0xb1, 0x80, 0x6b, \ + 0xe0, 0x40, 0x16, 0x0a, \ + 0xf8, 0xb5, 0x84, 0x91, \ + 0x80, 0xa6, 0xbc, 0x04, \ + 0x80, 0x8c, 0xbe, 0x04, \ + 0x0c, 0x0c, 0x98, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x00, 0xf6, 0x80, 0x6b, \ + 0x0c, 0x19, 0xe0, 0x0c, \ + 0xc0, 0x2f, 0x6e, 0x81, \ + 0x6e, 0x89, 0x0c, 0x29, \ + 0x0c, 0x39, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x85, \ + 0x6e, 0x8d, 0x0c, 0x49, \ + 0x4c, 0x01, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x0c, 0x09, 0x91, 0x51, \ + 0xc0, 0x2f, 0x6e, 0x79, \ + 0xe6, 0x86, 0x42, 0x2a, \ + 0x6e, 0x7d, 0x0c, 0x79, \ + 0x52, 0x4a, 0xc0, 0x2f, \ + 0x4c, 0x51, 0xe6, 0x8e, \ + 0xc0, 0x27, 0x68, 0xf1, \ + 0xc5, 0x17, 0x91, 0x21, \ + 0x4c, 0x01, 0xc5, 0x97, \ + 0xc0, 0x27, 0x69, 0x01, \ + 0x69, 0x21, 0x4c, 0x71, \ + 0x4c, 0x11, 0xc0, 0x27, \ + 0xc0, 0x27, 0x69, 0x41, \ + 0x69, 0x61, 0x4c, 0x91, \ + 0x4c, 0x01, 0xc0, 0x27, \ + 0xc0, 0x27, 0x69, 0x05, \ + 0x69, 0x25, 0x4c, 0x71, \ + 0x4c, 0x11, 0xc0, 0x27, \ + 0xc0, 0x27, 0x69, 0x45, \ + 0x69, 0x65, 0x4c, 0x91, \ + 0x4c, 0x01, 0xc0, 0x27, \ + 0xc0, 0x27, 0x69, 0x09, \ + 0x69, 0x29, 0x4c, 0x71, \ + 0x4c, 0x11, 0xc0, 0x27, \ + 0xc0, 0x27, 0x69, 0x49, \ + 0x69, 0x69, 0x4c, 0x91, \ + 0x4c, 0x01, 0xc0, 0x27, \ + 0xc0, 0x27, 0x69, 0x0d, \ + 0x69, 0x2d, 0x4c, 0x71, \ + 0x4c, 0x11, 0xc0, 0x27, \ + 0xc0, 0x27, 0x69, 0x4d, \ + 0x69, 0x6d, 0x4c, 0x91, \ + 0x83, 0x01, 0xc0, 0x27, \ + 0x6c, 0x91, 0x4c, 0x01, \ + 0x83, 0xa1, 0xc0, 0x30, \ + 0x4c, 0xb1, 0x81, 0xb1, \ + 0xc0, 0x30, 0x6c, 0xa1, \ + 0x6c, 0xb1, 0x4c, 0x21, \ + 0x4c, 0xb1, 0xc0, 0x30, \ + 0xc0, 0x27, 0x69, 0x11, \ + 0x4c, 0x71, 0x85, 0x81, \ + 0xc0, 0x27, 0x69, 0x31, \ + 0x69, 0x51, 0x4c, 0x81, \ + 0x4c, 0x91, 0xc0, 0x27, \ + 0xc0, 0x27, 0x69, 0x71, \ + 0x81, 0x21, 0x81, 0x11, \ + 0x8e, 0x3b, 0x8c, 0x0b, \ + 0xe8, 0x00, 0x46, 0x08, \ + 0x6c, 0x95, 0x4c, 0xa1, \ + 0x4c, 0xb1, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6c, 0xa5, \ + 0x4c, 0x01, 0x91, 0x01, \ + 0xc0, 0x30, 0x6c, 0xb5, \ + 0x69, 0x15, 0x4c, 0xb1, \ + 0x4c, 0x71, 0xc0, 0x27, \ + 0xc0, 0x27, 0x69, 0x35, \ + 0x69, 0x55, 0x4c, 0x81, \ + 0x4c, 0x91, 0xc0, 0x27, \ + 0xc0, 0x27, 0x69, 0x75, \ + 0x83, 0x21, 0x83, 0x11, \ + 0x8e, 0x3b, 0x8c, 0x0b, \ + 0xe8, 0x00, 0x44, 0x48, \ + 0x6c, 0x99, 0x4c, 0xa1, \ + 0x4c, 0xb1, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6c, 0xa9, \ + 0x91, 0x01, 0x99, 0xa1, \ + 0x6c, 0xb9, 0x4c, 0x01, \ + 0x4c, 0xa1, 0xc0, 0x30, \ + 0xc0, 0x27, 0x69, 0x19, \ + 0x69, 0x39, 0x4c, 0x71, \ + 0x4c, 0x81, 0xc0, 0x27, \ + 0xc0, 0x27, 0x69, 0x59, \ + 0x69, 0x79, 0x4c, 0x91, \ + 0x85, 0x11, 0xc0, 0x27, \ + 0x8c, 0x0b, 0x99, 0x21, \ + 0x42, 0x78, 0x8e, 0x3b, \ + 0x83, 0x01, 0xe8, 0x00, \ + 0x6c, 0x9d, 0x4c, 0x01, \ + 0x4c, 0xb1, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6c, 0xad, \ + 0x4c, 0x01, 0x91, 0x01, \ + 0xc0, 0x30, 0x6c, 0xbd, \ + 0x4c, 0xa1, 0x91, 0xb1, \ + 0xc0, 0x27, 0x69, 0x1d, \ + 0x69, 0x3d, 0x4c, 0x71, \ + 0x4c, 0x81, 0xc0, 0x27, \ + 0xc0, 0x27, 0x69, 0x5d, \ + 0x69, 0x7d, 0x4c, 0x91, \ + 0x87, 0x11, 0xc0, 0x27, \ + 0x8c, 0x0b, 0x9b, 0x21, \ + 0x40, 0x98, 0x8e, 0x3b, \ + 0x4c, 0xb1, 0xe8, 0x00, \ + 0xc0, 0x27, 0x68, 0xf5, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x84, 0x7b, 0x86, 0x6b, \ + 0xe0, 0x8c, 0x04, 0x14, \ + 0x02, 0x10, 0x80, 0x13, \ + 0xc0, 0xc3, 0x64, 0xa1, \ + 0xfe, 0x0e, 0xbe, 0x1e, \ + 0x84, 0x03, 0xfc, 0x2e, \ + 0x81, 0xb1, 0xbc, 0x0e, \ + 0x00, 0x74, 0xfe, 0x94, \ + 0x42, 0x60, 0xe0, 0x90, \ + 0x54, 0x02, 0xf0, 0x3c, \ + 0x02, 0xa4, 0xf9, 0x84, \ + 0xfc, 0x84, 0xfa, 0x80, \ + 0x92, 0x0b, 0x96, 0x83, \ + 0xe8, 0x00, 0x43, 0x28, \ + 0x52, 0x78, 0xa1, 0x11, \ + 0x89, 0x0e, 0xe8, 0x44, \ + 0xfa, 0x80, 0x02, 0x84, \ + 0x44, 0x98, 0x92, 0x0b, \ + 0x9d, 0x01, 0xe8, 0x00, \ + 0xe6, 0x82, 0x40, 0x6a, \ + 0x84, 0x18, 0xbe, 0x14, \ + 0xbc, 0x24, 0x8e, 0x03, \ + 0x44, 0x01, 0x96, 0x23, \ + 0xc0, 0x30, 0x6c, 0xc1, \ + 0x6d, 0x51, 0x44, 0xa1, \ + 0xa3, 0x1b, 0xc0, 0x30, \ + 0xe0, 0x04, 0x02, 0x5a, \ + 0x00, 0x1c, 0x82, 0xb1, \ + 0x16, 0x1a, 0xe2, 0x00, \ + 0x89, 0x14, 0xe0, 0x20, \ + 0x6d, 0xd1, 0x44, 0x11, \ + 0x02, 0x0c, 0xc0, 0x30, \ + 0x80, 0x63, 0xe2, 0x00, \ + 0x3c, 0xc8, 0x82, 0x73, \ + 0xc3, 0xff, 0x3f, 0xfd, \ + 0x0c, 0x0c, 0xa8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xe1, 0x90, 0x04, 0x14, \ + 0x44, 0x2a, 0xa9, 0x31, \ + 0x06, 0x14, 0xe6, 0x8e, \ + 0x84, 0x03, 0xe1, 0x84, \ + 0x86, 0x03, 0x8e, 0x39, \ + 0x64, 0x71, 0x00, 0x00, \ + 0x81, 0x21, 0xc0, 0xa2, \ + 0x42, 0x0a, 0x81, 0x31, \ + 0x00, 0x49, 0xe0, 0x80, \ + 0x80, 0x45, 0xf0, 0x14, \ + 0x9e, 0x49, 0xc9, 0x47, \ + 0x88, 0x21, 0xa5, 0x4f, \ + 0xe0, 0x40, 0x04, 0x0a, \ + 0xfc, 0x95, 0xa8, 0x3f, \ + 0xfb, 0x80, 0x00, 0x34, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xe1, 0x8c, 0x04, 0x14, \ + 0x00, 0x29, 0x84, 0x03, \ + 0xc0, 0x30, 0x6d, 0x41, \ + 0x83, 0x31, 0x8e, 0x19, \ + 0xa6, 0x2f, 0xa3, 0x3f, \ + 0x6d, 0x41, 0x40, 0x21, \ + 0xdd, 0x93, 0xc0, 0x30, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xe4, 0x7c, 0x03, 0x0a, \ + 0x04, 0x14, 0x82, 0xb5, \ + 0x84, 0x03, 0xe1, 0x8c, \ + 0x6d, 0x41, 0x00, 0x09, \ + 0x8e, 0x19, 0xc0, 0x30, \ + 0xe4, 0x84, 0x40, 0x4a, \ + 0xe2, 0x00, 0x00, 0x0c, \ + 0x81, 0x01, 0xdd, 0x93, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe5, 0x7c, 0x03, 0x0a, \ + 0x04, 0x14, 0x82, 0xb5, \ + 0x84, 0x03, 0xe1, 0x8c, \ + 0x67, 0xf1, 0x00, 0x09, \ + 0x8e, 0x19, 0xc0, 0x30, \ + 0xe4, 0x84, 0x40, 0x4a, \ + 0xe2, 0x00, 0x00, 0x0c, \ + 0x81, 0x01, 0xdd, 0x93, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x00, 0x00, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x66, 0xa1, 0x00, 0x00, \ + 0x04, 0x14, 0xc0, 0xbf, \ + 0x80, 0x13, 0xe0, 0x84, \ + 0xfc, 0x08, 0x00, 0x0b, \ + 0xe5, 0x00, 0x02, 0x19, \ + 0x49, 0x18, 0x82, 0x11, \ + 0xd0, 0x07, 0xe8, 0x44, \ + 0x08, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x88, 0x7b, 0x8a, 0x6b, \ + 0x84, 0xab, 0x86, 0x8b, \ + 0x00, 0x19, 0x82, 0x9b, \ + 0xc0, 0x2f, 0x6e, 0x99, \ + 0x02, 0x96, 0xa3, 0x1b, \ + 0x40, 0x11, 0xe0, 0x0c, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x88, 0xe5, 0x81, 0x11, \ + 0x82, 0x45, 0xa1, 0x9b, \ + 0x82, 0x05, 0xa1, 0xab, \ + 0x80, 0xc5, 0xa1, 0x8b, \ + 0x65, 0x61, 0x00, 0x15, \ + 0xa5, 0x1b, 0xc0, 0x0c, \ + 0x00, 0x00, 0x82, 0x55, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xff, 0x21, 0x81, 0x11, \ + 0xe0, 0x0c, 0x06, 0x96, \ + 0xe8, 0x04, 0x48, 0x08, \ + 0xe0, 0x22, 0x40, 0x7a, \ + 0xe0, 0x2a, 0x42, 0x0a, \ + 0xe6, 0xa2, 0x40, 0x0a, \ + 0x40, 0x1a, 0xa0, 0x73, \ + 0x02, 0x74, 0xe6, 0xa2, \ + 0x42, 0x1a, 0xfa, 0x80, \ + 0x40, 0x0a, 0xe0, 0x26, \ + 0x82, 0x03, 0xe0, 0x26, \ + 0xef, 0xfc, 0x02, 0x02, \ + 0x0c, 0x0c, 0x98, 0x16, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x88, 0x9b, 0x8a, 0x6b, \ + 0x84, 0x7b, 0x86, 0xab, \ + 0x00, 0x19, 0x82, 0x8b, \ + 0xc0, 0x2f, 0x6e, 0x99, \ + 0x02, 0x86, 0xa3, 0x1b, \ + 0x40, 0x11, 0xe0, 0x0c, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x88, 0xa5, 0x81, 0x11, \ + 0x82, 0x45, 0xa1, 0x8b, \ + 0x82, 0x05, 0xa1, 0x7b, \ + 0x80, 0xc5, 0xa1, 0xab, \ + 0x65, 0x61, 0x00, 0x15, \ + 0xa7, 0x1b, 0xc0, 0x0c, \ + 0x00, 0x00, 0x82, 0x55, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xff, 0x21, 0x81, 0x11, \ + 0xe0, 0x0c, 0x06, 0x86, \ + 0xe8, 0x04, 0x44, 0x48, \ + 0xe0, 0x2a, 0x40, 0x9a, \ + 0xe6, 0xaa, 0x42, 0x0a, \ + 0x40, 0x0a, 0xa2, 0x93, \ + 0x02, 0x94, 0xe1, 0x1e, \ + 0x42, 0x1a, 0xfa, 0x80, \ + 0x40, 0x0a, 0xe1, 0x22, \ + 0x82, 0x03, 0xe6, 0xa2, \ + 0xef, 0xfc, 0x02, 0x02, \ + 0x0c, 0x0c, 0x98, 0x16, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x9f, 0x21, 0x81, 0x11, \ + 0x08, 0x24, 0x81, 0x31, \ + 0x80, 0x4f, 0xe8, 0x84, \ + 0x0a, 0x44, 0xe3, 0x4f, \ + 0x18, 0x44, 0xe1, 0x8c, \ + 0x82, 0x59, 0xe1, 0x84, \ + 0xb8, 0x5f, 0x84, 0xc9, \ + 0xe0, 0x84, 0x18, 0x44, \ + 0xc6, 0x47, 0x88, 0xc9, \ + 0x90, 0x49, 0xb8, 0x5f, \ + 0xa3, 0x5f, 0xa8, 0x5f, \ + 0x02, 0x0a, 0x88, 0x11, \ + 0xaa, 0x3f, 0xe0, 0x40, \ + 0x00, 0x34, 0xfa, 0x35, \ + 0xdd, 0x93, 0xfb, 0x80, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x81, 0x81, 0x80, 0x6b, \ + 0x8b, 0x8e, 0x8d, 0x8e, \ + 0xfe, 0x8e, 0x89, 0x8e, \ + 0xbe, 0x8e, 0xfc, 0x8e, \ + 0x57, 0x38, 0xbc, 0x8e, \ + 0x83, 0x91, 0xef, 0xfc, \ + 0xf0, 0xa0, 0x56, 0x62, \ + 0xfa, 0x80, 0x0e, 0x84, \ + 0x61, 0x21, 0x16, 0xa9, \ + 0x8c, 0x0b, 0xc0, 0x30, \ + 0x72, 0x38, 0x8e, 0x1b, \ + 0x00, 0x0a, 0xef, 0xfc, \ + 0x86, 0xb5, 0xf0, 0x04, \ + 0x6e, 0xd1, 0x0c, 0x09, \ + 0x16, 0x19, 0xc0, 0x30, \ + 0xc0, 0x30, 0x64, 0xe1, \ + 0xfa, 0x80, 0x02, 0x14, \ + 0xfa, 0x80, 0x00, 0x04, \ + 0x84, 0x35, 0x83, 0x0b, \ + 0xf3, 0xc0, 0x40, 0xa0, \ + 0x9e, 0x1b, 0xc7, 0x07, \ + 0xfc, 0x00, 0x02, 0x2b, \ + 0x06, 0x94, 0x9e, 0xa9, \ + 0x80, 0x13, 0xe8, 0xa8, \ + 0x84, 0x2c, 0xa6, 0x2f, \ + 0x8e, 0x1b, 0x8c, 0x0b, \ + 0xe8, 0x00, 0x42, 0x08, \ + 0x10, 0x0a, 0x82, 0x81, \ + 0xf4, 0x75, 0xe1, 0x40, \ + 0x00, 0xe6, 0x9d, 0x11, \ + 0xc0, 0x20, 0x64, 0x8d, \ + 0xc9, 0x3a, 0x9e, 0x2b, \ + 0xec, 0x04, 0x02, 0x10, \ + 0xc1, 0x3c, 0xa1, 0x1b, \ + 0x9d, 0x11, 0xfe, 0x75, \ + 0xc9, 0x3a, 0x9e, 0x2b, \ + 0xec, 0x04, 0x02, 0x10, \ + 0xc1, 0x3c, 0xa1, 0x1b, \ + 0x8c, 0x0b, 0xfe, 0x75, \ + 0xe8, 0x3c, 0x48, 0x68, \ + 0x0c, 0x0c, 0xb8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0x02, 0x19, 0x80, 0x13, \ + 0xc0, 0x30, 0x61, 0x21, \ + 0xe1, 0x8c, 0x04, 0x14, \ + 0x84, 0x03, 0xbc, 0x29, \ + 0x64, 0x11, 0x00, 0x2b, \ + 0x9e, 0x19, 0xc0, 0x18, \ + 0xa3, 0x3f, 0x83, 0x31, \ + 0x40, 0x23, 0xa6, 0x2f, \ + 0xc0, 0x18, 0x64, 0x11, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xb0, 0x1e, 0xf0, \ + 0x04, 0x76, 0x80, 0xbb, \ + 0x16, 0x09, 0xe0, 0x0c, \ + 0xc0, 0x2f, 0x6e, 0x81, \ + 0x6e, 0x89, 0x16, 0x89, \ + 0x16, 0x19, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x85, \ + 0x6e, 0x8d, 0x16, 0x99, \ + 0x56, 0x21, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x6e, 0x79, 0x16, 0x69, \ + 0x40, 0x8a, 0xc0, 0x2f, \ + 0x16, 0x79, 0xe6, 0x82, \ + 0xc0, 0x2f, 0x6e, 0x7d, \ + 0xe6, 0x86, 0x42, 0x9a, \ + 0x6e, 0x91, 0x16, 0x29, \ + 0x16, 0x39, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x95, \ + 0xc5, 0x17, 0xc5, 0x07, \ + 0xfc, 0x0e, 0x8b, 0x6e, \ + 0x8f, 0x7e, 0x80, 0x63, \ + 0x14, 0x24, 0x82, 0x73, \ + 0xc5, 0x37, 0xe1, 0x88, \ + 0xc5, 0x87, 0x89, 0x3e, \ + 0xe4, 0xa0, 0x02, 0xf0, \ + 0x96, 0x0b, 0xc5, 0x97, \ + 0xe8, 0x08, 0x73, 0x28, \ + 0xa1, 0xab, 0xfe, 0xae, \ + 0xe0, 0x0c, 0x00, 0x76, \ + 0x6e, 0x59, 0x56, 0x01, \ + 0x82, 0x25, 0xc0, 0x2f, \ + 0xa1, 0x0b, 0x89, 0x04, \ + 0x16, 0x05, 0x80, 0xc5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0x82, 0x55, 0xa9, 0x0b, \ + 0x66, 0xc1, 0x00, 0xb0, \ + 0x81, 0x11, 0xc0, 0x56, \ + 0x06, 0x76, 0xff, 0x21, \ + 0x75, 0x18, 0xe0, 0x0c, \ + 0xfe, 0x04, 0xe8, 0x00, \ + 0xe0, 0x02, 0x42, 0x8a, \ + 0x40, 0x9a, 0x89, 0x04, \ + 0xbe, 0x1e, 0xe0, 0x02, \ + 0x40, 0x0a, 0xbc, 0x0e, \ + 0x02, 0x60, 0xe6, 0x86, \ + 0x8d, 0x1e, 0xec, 0x04, \ + 0xec, 0x04, 0x0e, 0x70, \ + 0x67, 0x6d, 0x56, 0x01, \ + 0x81, 0x91, 0xc0, 0x30, \ + 0xe0, 0x0c, 0x55, 0xf6, \ + 0x4c, 0xb2, 0x81, 0x81, \ + 0x0c, 0x09, 0xf0, 0xa4, \ + 0xc0, 0x30, 0x61, 0x21, \ + 0xf0, 0x3c, 0x42, 0x00, \ + 0xc5, 0x1d, 0x8f, 0x24, \ + 0x64, 0xe1, 0x4c, 0xa1, \ + 0x4c, 0xa1, 0xc0, 0x30, \ + 0xc0, 0x30, 0x66, 0x21, \ + 0xce, 0x1d, 0x8a, 0xf5, \ + 0xc9, 0x07, 0x8a, 0xb5, \ + 0xc2, 0x0d, 0x8d, 0x14, \ + 0x8b, 0x14, 0x8a, 0x35, \ + 0x88, 0xd5, 0xc3, 0x0d, \ + 0xfa, 0x80, 0x02, 0x94, \ + 0x43, 0x18, 0x96, 0x0b, \ + 0xfc, 0x04, 0xe8, 0x00, \ + 0xfa, 0x80, 0x06, 0x04, \ + 0xfa, 0x80, 0x08, 0x84, \ + 0xe4, 0x90, 0x0a, 0xf0, \ + 0xfe, 0x14, 0x96, 0x0b, \ + 0x66, 0xd8, 0x89, 0x24, \ + 0x1e, 0x49, 0xef, 0xfc, \ + 0x1e, 0x39, 0xe0, 0x90, \ + 0x0a, 0xf0, 0xe0, 0xa0, \ + 0x4c, 0x41, 0xe4, 0x80, \ + 0xc0, 0x30, 0x66, 0x21, \ + 0xbe, 0x14, 0x96, 0x0b, \ + 0x69, 0xb8, 0xbc, 0x24, \ + 0x1e, 0x09, 0xef, 0xfc, \ + 0x4c, 0x01, 0xe0, 0x80, \ + 0xc0, 0x30, 0x64, 0xe1, \ + 0x82, 0x91, 0x82, 0x81, \ + 0xe1, 0x40, 0x12, 0x0a, \ + 0x96, 0x0b, 0xee, 0xf5, \ + 0xe8, 0x00, 0x41, 0xa8, \ + 0x4b, 0xb8, 0x96, 0x0b, \ + 0xd8, 0xf1, 0xef, 0xfc, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xe1, 0x8c, 0x04, 0x14, \ + 0x00, 0x29, 0x84, 0x03, \ + 0xc0, 0x30, 0x67, 0xf1, \ + 0x83, 0x31, 0x8e, 0x19, \ + 0xa6, 0x2f, 0xa3, 0x3f, \ + 0x67, 0xf1, 0x40, 0x21, \ + 0xdd, 0x93, 0xc0, 0x30, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x2f, 0x6e, 0x99, \ + 0x8a, 0xa5, 0xa3, 0x0b, \ + 0xe4, 0x20, 0x02, 0xf0, \ + 0x6a, 0x38, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x08, \ + 0xc0, 0x31, 0x65, 0x71, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0xeb, 0xd8, 0x00, 0x06, \ + 0x00, 0x26, 0x80, 0x65, \ + 0xfc, 0x14, 0xeb, 0xd8, \ + 0x84, 0xe5, 0xa1, 0x1b, \ + 0xfc, 0x00, 0x0c, 0x0b, \ + 0x0e, 0x04, 0x81, 0x81, \ + 0x81, 0x91, 0xfb, 0x80, \ + 0xfa, 0x80, 0x02, 0x94, \ + 0xe4, 0x10, 0x08, 0xf0, \ + 0x8c, 0x0b, 0x81, 0x21, \ + 0x42, 0x38, 0x8e, 0x3b, \ + 0xfc, 0x04, 0xe8, 0x00, \ + 0x82, 0x91, 0xfc, 0x1a, \ + 0x82, 0x83, 0xc1, 0x9d, \ + 0x81, 0x25, 0xfc, 0x55, \ + 0x4c, 0x83, 0x81, 0x81, \ + 0xc0, 0x18, 0x63, 0xb1, \ + 0x0c, 0x0c, 0x98, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x1c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x82, 0xb5, 0xfc, 0x1d, \ + 0x82, 0x11, 0x83, 0x31, \ + 0xf4, 0x38, 0x03, 0x0a, \ + 0x08, 0x14, 0x82, 0x15, \ + 0x08, 0x34, 0xfa, 0x80, \ + 0x80, 0x4f, 0xe8, 0x90, \ + 0xfc, 0xc5, 0xa1, 0x4b, \ + 0xdd, 0x93, 0x88, 0x16, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xf0, 0x1e, 0xf0, \ + 0x82, 0x9b, 0x84, 0x8b, \ + 0x12, 0x0a, 0x80, 0x5b, \ + 0x81, 0x71, 0xe3, 0xfc, \ + 0x90, 0x7c, 0xbe, 0x4e, \ + 0x80, 0x00, 0x0b, 0x48, \ + 0xfe, 0x3e, 0xc9, 0x37, \ + 0x81, 0xa1, 0x81, 0xb1, \ + 0x4c, 0x52, 0x89, 0x5e, \ + 0x0c, 0x09, 0xf0, 0x9c, \ + 0xc0, 0x30, 0x66, 0x21, \ + 0x88, 0x75, 0x93, 0x0b, \ + 0x82, 0x35, 0xa3, 0x8b, \ + 0x6e, 0xd1, 0x0a, 0x09, \ + 0x0c, 0x19, 0xc0, 0x30, \ + 0xc0, 0x30, 0x64, 0xe1, \ + 0x86, 0x35, 0x83, 0x0b, \ + 0xfa, 0x80, 0x02, 0x74, \ + 0x59, 0x98, 0x8a, 0x0b, \ + 0x00, 0x0a, 0xef, 0xfc, \ + 0x84, 0x15, 0xf0, 0x04, \ + 0x62, 0x61, 0x0c, 0x09, \ + 0x02, 0xa4, 0xc0, 0x30, \ + 0x82, 0xa1, 0xe0, 0x88, \ + 0xe4, 0x60, 0x04, 0xf0, \ + 0x82, 0x23, 0xf0, 0xad, \ + 0x88, 0x7e, 0x80, 0xb3, \ + 0x80, 0x65, 0x89, 0x54, \ + 0x89, 0x54, 0x81, 0x85, \ + 0x0e, 0x8a, 0x82, 0x71, \ + 0xf4, 0x95, 0xe5, 0x40, \ + 0x9e, 0xe5, 0xa1, 0xab, \ + 0x6b, 0x05, 0x0a, 0x0b, \ + 0x0a, 0x1b, 0xc0, 0x17, \ + 0xc0, 0x17, 0x6b, 0x09, \ + 0xfe, 0x24, 0xc9, 0x07, \ + 0xfa, 0x88, 0x40, 0x02, \ + 0x50, 0x12, 0xc9, 0x17, \ + 0x00, 0xb4, 0xfb, 0x80, \ + 0xd1, 0x0d, 0xfb, 0x80, \ + 0xc8, 0xb7, 0x84, 0x25, \ + 0x80, 0xbc, 0xbe, 0x04, \ + 0xe4, 0x60, 0x00, 0xf0, \ + 0xc1, 0x24, 0x9f, 0x11, \ + 0xec, 0x04, 0x14, 0xa0, \ + 0x8a, 0x23, 0xa1, 0xab, \ + 0x63, 0xa1, 0x44, 0x11, \ + 0xfe, 0x15, 0xc0, 0x30, \ + 0x81, 0x01, 0x8b, 0xe5, \ + 0xe0, 0x88, 0x02, 0x04, \ + 0xe4, 0x60, 0x18, 0xf0, \ + 0xfc, 0x04, 0x18, 0x15, \ + 0xe0, 0x84, 0x04, 0x14, \ + 0x04, 0x3b, 0x8a, 0x23, \ + 0xc0, 0x17, 0x6f, 0x51, \ + 0x81, 0x21, 0xa1, 0x3b, \ + 0x46, 0x4a, 0x82, 0xe5, \ + 0x81, 0x21, 0xe0, 0x80, \ + 0x81, 0x21, 0x82, 0x65, \ + 0x73, 0xf9, 0x48, 0x30, \ + 0x46, 0x0a, 0xc0, 0xff, \ + 0x06, 0x44, 0xe0, 0x84, \ + 0x82, 0x21, 0xe1, 0x84, \ + 0x82, 0x01, 0xfe, 0x05, \ + 0x8a, 0x13, 0x95, 0x0b, \ + 0x63, 0xa1, 0x42, 0x21, \ + 0xf6, 0xb5, 0xc0, 0x30, \ + 0xa9, 0x26, 0xfc, 0xce, \ + 0x94, 0xbb, 0x81, 0x11, \ + 0xfb, 0x80, 0x00, 0xb4, \ + 0x8a, 0xc5, 0xd1, 0x0d, \ + 0xa1, 0xab, 0xfe, 0x1e, \ + 0x83, 0x91, 0x88, 0xa5, \ + 0x9c, 0x64, 0xfc, 0x74, \ + 0x66, 0xa1, 0x00, 0x50, \ + 0x02, 0x64, 0xc0, 0xbf, \ + 0x80, 0x63, 0xe0, 0x84, \ + 0xfc, 0x04, 0x00, 0x0b, \ + 0xe5, 0x00, 0x0c, 0x19, \ + 0xe4, 0x50, 0x04, 0xf0, \ + 0xef, 0xfc, 0x74, 0xe8, \ + 0x0c, 0x19, 0xa9, 0x08, \ + 0x83, 0x0b, 0xe5, 0x00, \ + 0x02, 0x60, 0x80, 0xc5, \ + 0xc0, 0x01, 0x65, 0x01, \ + 0x84, 0x06, 0x82, 0xb1, \ + 0xfb, 0x80, 0x00, 0xb4, \ + 0x89, 0x54, 0xd1, 0x0d, \ + 0xd5, 0x9d, 0x80, 0xa5, \ + 0x88, 0x71, 0x82, 0x91, \ + 0xfe, 0x04, 0xf6, 0xf5, \ + 0xf4, 0x3c, 0x00, 0x8a, \ + 0x80, 0x1b, 0x82, 0x01, \ + 0xc8, 0xb7, 0xf4, 0x15, \ + 0x80, 0xbc, 0xbe, 0x04, \ + 0x0c, 0x0c, 0xf8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x03, 0xc6, 0x82, 0x6b, \ + 0x84, 0x14, 0xe1, 0x24, \ + 0x00, 0x19, 0x84, 0x34, \ + 0xc0, 0x1c, 0x69, 0xe5, \ + 0x62, 0x65, 0x00, 0x45, \ + 0x00, 0x09, 0xc0, 0x07, \ + 0xc0, 0x1c, 0x69, 0xe9, \ + 0xa3, 0x2f, 0x83, 0x21, \ + 0xa1, 0x2f, 0x82, 0x31, \ + 0x88, 0x0b, 0xa1, 0x11, \ + 0xef, 0xd4, 0x7d, 0xe8, \ + 0x0c, 0x0c, 0x98, 0x0e, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x86, 0x9b, 0x88, 0x7b, \ + 0x82, 0xbb, 0x84, 0xab, \ + 0x10, 0x09, 0x80, 0x8b, \ + 0xc0, 0x2f, 0x66, 0x1d, \ + 0xe0, 0x88, 0x02, 0x74, \ + 0x90, 0x13, 0xa3, 0x0b, \ + 0x42, 0x25, 0x81, 0x21, \ + 0xc0, 0x0c, 0x62, 0x25, \ + 0xa7, 0x0b, 0x82, 0x25, \ + 0x10, 0x0b, 0x8a, 0x95, \ + 0xc0, 0x17, 0x6b, 0x01, \ + 0x81, 0x61, 0x91, 0x0c, \ + 0x00, 0x10, 0x83, 0xd5, \ + 0xc0, 0xc2, 0x64, 0x91, \ + 0x90, 0x0b, 0xbe, 0x0e, \ + 0xfc, 0x5e, 0x8e, 0x1b, \ + 0xe8, 0x00, 0x42, 0x48, \ + 0x02, 0xf0, 0xc9, 0x07, \ + 0x0c, 0x04, 0xe4, 0x40, \ + 0x0a, 0xf0, 0xfb, 0x80, \ + 0xbc, 0x1e, 0xe4, 0x30, \ + 0x96, 0x1b, 0x90, 0x0b, \ + 0x92, 0x3b, 0x94, 0x2b, \ + 0x43, 0x98, 0x8c, 0x4b, \ + 0xfc, 0x54, 0xe8, 0x00, \ + 0xbe, 0x14, 0xfe, 0x04, \ + 0x91, 0x3a, 0x84, 0x0e, \ + 0x80, 0xe5, 0x87, 0x6b, \ + 0x90, 0x0b, 0x83, 0x21, \ + 0x8a, 0x4b, 0x8e, 0x1b, \ + 0xef, 0xfc, 0x6e, 0x08, \ + 0x0c, 0x0c, 0xa8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x80, 0x8b, 0x82, 0x6b, \ + 0x81, 0x91, 0x81, 0x71, \ + 0xf0, 0xa4, 0x40, 0x82, \ + 0x66, 0x21, 0x00, 0x09, \ + 0x00, 0x0a, 0xc0, 0x30, \ + 0x82, 0x25, 0xe3, 0xfc, \ + 0x80, 0xf5, 0x8d, 0x0b, \ + 0xfa, 0x80, 0x02, 0x94, \ + 0x49, 0x78, 0x90, 0x0b, \ + 0x80, 0x73, 0xef, 0xfc, \ + 0x12, 0x0a, 0x82, 0x91, \ + 0xfa, 0xb5, 0xe1, 0x40, \ + 0x88, 0xf1, 0x8e, 0x0b, \ + 0xf2, 0x1c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x88, 0x8b, 0x8a, 0x7b, \ + 0x84, 0xab, 0x86, 0x9b, \ + 0x80, 0x6b, 0x82, 0xbb, \ + 0xe1, 0x14, 0x41, 0x86, \ + 0x81, 0x11, 0x80, 0x04, \ + 0x80, 0x08, 0xfc, 0x1e, \ + 0x6d, 0x38, 0x91, 0x11, \ + 0x80, 0x2b, 0xef, 0xd4, \ + 0xe0, 0x90, 0x0a, 0x84, \ + 0xe0, 0x0c, 0x01, 0x06, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x06, 0x60, 0xc0, 0x2f, \ + 0xc0, 0xbd, 0x65, 0xf1, \ + 0xe4, 0x20, 0x00, 0xf0, \ + 0xbc, 0x7e, 0xbe, 0x0e, \ + 0x94, 0x1b, 0x96, 0x0b, \ + 0x66, 0xf8, 0x92, 0x4b, \ + 0x0c, 0x15, 0xef, 0xac, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe1, 0x98, 0x02, 0x0a, \ + 0xff, 0x01, 0x80, 0x55, \ + 0x95, 0x74, 0xa1, 0x0b, \ + 0x0c, 0x39, 0x82, 0xe5, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x9c, 0x1c, 0x81, 0x11, \ + 0x66, 0xc1, 0x08, 0x60, \ + 0x04, 0x04, 0xc0, 0x56, \ + 0x87, 0x11, 0xfb, 0x00, \ + 0x50, 0x78, 0x88, 0x0b, \ + 0xfc, 0x04, 0xe8, 0x00, \ + 0x00, 0x02, 0xc9, 0x07, \ + 0xc0, 0xff, 0x6f, 0xfd, \ + 0x98, 0xf1, 0x9c, 0x0c, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe0, 0x04, 0x05, 0x06, \ + 0x66, 0x21, 0x06, 0x00, \ + 0x81, 0x01, 0xc0, 0xc1, \ + 0x08, 0x0a, 0x8c, 0x48, \ + 0x80, 0xe5, 0xe3, 0xfc, \ + 0x80, 0xb5, 0x83, 0x4b, \ + 0x79, 0xc1, 0x06, 0x49, \ + 0x88, 0x03, 0xc0, 0x01, \ + 0xec, 0x04, 0x04, 0x20, \ + 0x82, 0x31, 0xa1, 0x2b, \ + 0xd0, 0x07, 0xfc, 0x55, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x80, 0x8b, 0x82, 0x6b, \ + 0x81, 0xa1, 0x81, 0x71, \ + 0x56, 0x82, 0x81, 0x91, \ + 0x16, 0x09, 0xf0, 0xa4, \ + 0xc0, 0x30, 0x6c, 0xc1, \ + 0x82, 0x95, 0x8d, 0x0b, \ + 0xfa, 0x80, 0x02, 0x94, \ + 0x7e, 0x98, 0x90, 0x0b, \ + 0xa1, 0x0b, 0xef, 0xf8, \ + 0x16, 0x79, 0x80, 0xa5, \ + 0xc0, 0x30, 0x6d, 0xd1, \ + 0x82, 0x91, 0x83, 0xa1, \ + 0xe0, 0x80, 0x12, 0x0a, \ + 0xa1, 0xab, 0xfa, 0x75, \ + 0x13, 0x06, 0x88, 0x15, \ + 0x14, 0x80, 0xe0, 0x04, \ + 0xc0, 0xc0, 0x65, 0x21, \ + 0x66, 0xa1, 0x10, 0x80, \ + 0x9d, 0xb1, 0xc0, 0xbf, \ + 0x42, 0x00, 0xa8, 0x08, \ + 0x42, 0x1a, 0xf0, 0x3c, \ + 0xc9, 0x07, 0xe6, 0xae, \ + 0x8d, 0x1b, 0x80, 0x13, \ + 0xa0, 0x0a, 0x82, 0x15, \ + 0xe2, 0x80, 0x14, 0x19, \ + 0x48, 0xd8, 0x82, 0x11, \ + 0x80, 0x7b, 0xe8, 0x40, \ + 0xec, 0x04, 0x12, 0x90, \ + 0x82, 0xa1, 0xa1, 0x9b, \ + 0xfa, 0x75, 0x84, 0x81, \ + 0xe0, 0xa0, 0x00, 0x74, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x02, 0x06, 0x80, 0x6b, \ + 0x7f, 0x78, 0xe2, 0x80, \ + 0x43, 0xf6, 0xe8, 0x3c, \ + 0x05, 0x86, 0xe0, 0x0c, \ + 0x8c, 0x0b, 0xe0, 0x10, \ + 0xef, 0xa4, 0x48, 0x48, \ + 0x45, 0x76, 0x81, 0x11, \ + 0x8c, 0x0b, 0xe0, 0x00, \ + 0x0c, 0x0c, 0x8c, 0x3b, \ + 0x0a, 0xb8, 0xf2, 0x00, \ + 0xc1, 0xc3, 0xe0, 0x38, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x80, 0x6b, 0x82, 0x7b, \ + 0xe0, 0x04, 0x40, 0x96, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x02, 0x06, 0xc0, 0x2f, \ + 0x8c, 0x0b, 0xe2, 0x80, \ + 0xe8, 0x3c, 0x7d, 0x68, \ + 0xe4, 0x40, 0x0e, 0x8a, \ + 0x0c, 0x39, 0x82, 0x95, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x66, 0xf1, 0x00, 0x60, \ + 0x81, 0x71, 0xc0, 0x56, \ + 0xab, 0x21, 0x81, 0x11, \ + 0xe8, 0x00, 0x53, 0x88, \ + 0xe1, 0x88, 0x00, 0x74, \ + 0x91, 0x11, 0x86, 0x09, \ + 0xe1, 0x1f, 0x83, 0x21, \ + 0xc8, 0x17, 0xa1, 0x2f, \ + 0xf9, 0x88, 0x40, 0x12, \ + 0x81, 0x11, 0x86, 0x79, \ + 0xf0, 0x0c, 0x44, 0x10, \ + 0x80, 0x95, 0x8f, 0x2b, \ + 0xf0, 0x84, 0x44, 0x62, \ + 0x82, 0x11, 0x88, 0x06, \ + 0xe0, 0x40, 0x02, 0x0a, \ + 0x81, 0x11, 0xfc, 0xd5, \ + 0x8c, 0x0b, 0x9f, 0x21, \ + 0x68, 0xf8, 0x8c, 0x3b, \ + 0x0c, 0x09, 0xe8, 0x34, \ + 0xc0, 0x2f, 0x6c, 0xa9, \ + 0x84, 0x15, 0xa3, 0x0b, \ + 0x02, 0x60, 0x91, 0x01, \ + 0xc0, 0x90, 0x67, 0x21, \ + 0xe0, 0x10, 0x04, 0x06, \ + 0xec, 0x04, 0x00, 0x00, \ + 0xc5, 0x2e, 0xa1, 0x0b, \ + 0x88, 0xf1, 0xfe, 0x95, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0x8c, 0x0b, 0xdd, 0x93, \ + 0x88, 0xf1, 0x8c, 0x1b, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0xe0, 0x30, 0x15, 0xc8, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x88, 0x7b, 0x8a, 0x6b, \ + 0x8d, 0x94, 0x80, 0x8b, \ + 0xe8, 0x00, 0x40, 0xc8, \ + 0x50, 0x63, 0xe2, 0x7c, \ + 0x50, 0x93, 0xe0, 0x10, \ + 0x88, 0xf1, 0xe0, 0x14, \ + 0xf2, 0x1c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0x81, 0x65, \ + 0x82, 0x2c, 0x80, 0x1c, \ + 0xdd, 0x93, 0xc0, 0x3c, \ + 0xfa, 0x00, 0x00, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0xe0, 0x00, 0x41, 0xc6, \ + 0xe8, 0x10, 0x64, 0xe8, \ + 0xe8, 0x00, 0x41, 0x48, \ + 0x08, 0x0c, 0x88, 0xf1, \ + 0x85, 0xb5, 0xf2, 0x00, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x00, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0xef, 0xfc, 0x7e, 0x88, \ + 0x08, 0x0c, 0x88, 0xf1, \ + 0xfd, 0x75, 0xf2, 0x00, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0xe1, 0x48, 0x0c, 0x06, \ + 0x03, 0x06, 0x98, 0x04, \ + 0x84, 0x14, 0xe1, 0x44, \ + 0xe0, 0x04, 0x05, 0x06, \ + 0x6e, 0x59, 0x40, 0x21, \ + 0x84, 0x08, 0xc0, 0x2f, \ + 0x84, 0x06, 0xd0, 0x09, \ + 0x81, 0x01, 0x98, 0x14, \ + 0xef, 0xf0, 0x60, 0x28, \ + 0xef, 0xec, 0x77, 0x08, \ + 0x83, 0x11, 0x98, 0x04, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xe0, 0x00, 0x3f, 0x68, \ + 0xfa, 0x00, 0x00, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0xe8, 0x00, 0x70, 0x88, \ + 0x80, 0xc5, 0xa1, 0x0b, \ + 0xe1, 0x48, 0x00, 0x06, \ + 0x41, 0x98, 0x80, 0x04, \ + 0x00, 0x06, 0xe8, 0x00, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0x67, 0x4d, 0x00, 0x19, \ + 0xc2, 0x19, 0xc0, 0x31, \ + 0x67, 0x4d, 0x40, 0x11, \ + 0x43, 0xc6, 0xc0, 0x31, \ + 0xc0, 0x20, 0x72, 0xfd, \ + 0x83, 0x31, 0x81, 0x21, \ + 0x88, 0xf1, 0x83, 0x41, \ + 0xf2, 0x00, 0x08, 0x0c, \ + 0xff, 0xec, 0x05, 0x88, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0xf3, 0xd8, 0x40, 0x00, \ + 0x6e, 0x51, 0x4c, 0x01, \ + 0x81, 0x01, 0xc0, 0x2f, \ + 0x65, 0xe5, 0x4c, 0x01, \ + 0x6c, 0x38, 0xc0, 0x2f, \ + 0x80, 0x7b, 0xe8, 0x00, \ + 0x6b, 0x78, 0x8c, 0x0b, \ + 0xa1, 0x7b, 0xe8, 0x00, \ + 0x8c, 0x0b, 0x82, 0x25, \ + 0xe8, 0x00, 0x50, 0xa8, \ + 0x47, 0x78, 0x8c, 0x0b, \ + 0x5a, 0xd8, 0xe8, 0x00, \ + 0x8c, 0x0b, 0xef, 0xf0, \ + 0xe8, 0x38, 0x5e, 0x68, \ + 0x9f, 0x21, 0x81, 0x11, \ + 0x8c, 0x0b, 0x83, 0x31, \ + 0xe8, 0x34, 0x5f, 0x48, \ + 0x5d, 0xd8, 0x8c, 0x0b, \ + 0x6b, 0x38, 0xe8, 0x0c, \ + 0x6c, 0x18, 0xe8, 0x00, \ + 0x71, 0x38, 0xe8, 0x00, \ + 0x83, 0x01, 0xef, 0xec, \ + 0x59, 0xf8, 0x8c, 0x1b, \ + 0x8c, 0x0b, 0xef, 0xf0, \ + 0xe8, 0x08, 0x5a, 0xc8, \ + 0xe1, 0x48, 0x41, 0x86, \ + 0x8c, 0x0b, 0x80, 0x6e, \ + 0xe8, 0x38, 0x5c, 0x28, \ + 0xe1, 0x4c, 0x02, 0x86, \ + 0x8c, 0x0b, 0x84, 0x0e, \ + 0xe8, 0x38, 0x5c, 0x08, \ + 0xe1, 0x48, 0x43, 0x46, \ + 0x88, 0xf1, 0x84, 0x0e, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x88, 0x7b, 0x8a, 0x6b, \ + 0x8d, 0x94, 0x80, 0x8b, \ + 0xef, 0xfc, 0x74, 0x88, \ + 0x50, 0x63, 0xe2, 0x7c, \ + 0x50, 0x93, 0xe0, 0x10, \ + 0x88, 0xf1, 0xe0, 0x14, \ + 0xf2, 0x1c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0x81, 0x25, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0xe1, 0x48, 0x0c, 0x06, \ + 0x03, 0x46, 0x98, 0x04, \ + 0x84, 0x14, 0xe1, 0x44, \ + 0xe0, 0x04, 0x05, 0x06, \ + 0x6e, 0x59, 0x00, 0x79, \ + 0x40, 0x21, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xd0, 0x09, 0x84, 0x08, \ + 0x98, 0x14, 0x84, 0x06, \ + 0x55, 0x98, 0x81, 0x01, \ + 0x6c, 0x78, 0xef, 0xf0, \ + 0x98, 0x04, 0xef, 0xec, \ + 0x6e, 0x59, 0x40, 0x71, \ + 0x88, 0xf1, 0xc0, 0x2f, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x00, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0xef, 0xfc, 0x71, 0x48, \ + 0x08, 0x0c, 0x88, 0xf1, \ + 0xfb, 0x35, 0xf2, 0x00, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x40, 0x11, 0x81, 0x11, \ + 0xc0, 0x31, 0x60, 0x11, \ + 0x60, 0x11, 0x04, 0x26, \ + 0x40, 0x25, 0xc0, 0xc0, \ + 0xc0, 0x0c, 0x63, 0xa5, \ + 0x60, 0x01, 0x04, 0xae, \ + 0x40, 0x25, 0xc0, 0x00, \ + 0xc0, 0x05, 0x64, 0x01, \ + 0x64, 0x05, 0x40, 0x15, \ + 0x04, 0x0e, 0xc0, 0x05, \ + 0xc0, 0x00, 0x60, 0x01, \ + 0x40, 0x25, 0xa1, 0x31, \ + 0xc0, 0x05, 0x64, 0x09, \ + 0x69, 0xd5, 0x40, 0x31, \ + 0x83, 0x21, 0xc0, 0x1c, \ + 0xe0, 0x08, 0x07, 0x06, \ + 0x69, 0xb5, 0x40, 0x21, \ + 0x40, 0x31, 0xc0, 0x23, \ + 0xc0, 0x1c, 0x69, 0xdd, \ + 0x69, 0xbd, 0x40, 0x31, \ + 0x40, 0x15, 0xc0, 0x23, \ + 0xc0, 0x0a, 0x6d, 0x49, \ + 0x60, 0x01, 0x03, 0x06, \ + 0xaf, 0x31, 0xc2, 0xa4, \ + 0x6d, 0x4d, 0x40, 0x15, \ + 0x40, 0x31, 0xc0, 0x0a, \ + 0xc0, 0x2b, 0x65, 0x41, \ + 0xe0, 0x0c, 0x43, 0xf6, \ + 0x60, 0x01, 0x07, 0x06, \ + 0x40, 0x11, 0xc0, 0x20, \ + 0xc0, 0x31, 0x67, 0x49, \ + 0x61, 0xfd, 0x40, 0x35, \ + 0x03, 0x06, 0xc0, 0x0a, \ + 0x07, 0x86, 0xe0, 0x80, \ + 0xc2, 0x30, 0x60, 0x11, \ + 0x64, 0x01, 0x40, 0x13, \ + 0x40, 0x35, 0xc0, 0x14, \ + 0xc0, 0x0a, 0x62, 0x05, \ + 0xe0, 0x00, 0x43, 0x86, \ + 0xe0, 0x00, 0x46, 0x86, \ + 0x68, 0x21, 0x40, 0x11, \ + 0x40, 0x31, 0xc0, 0x28, \ + 0xc0, 0x28, 0x68, 0x25, \ + 0xe0, 0x20, 0x02, 0x46, \ + 0xe0, 0xa0, 0x06, 0xe6, \ + 0x60, 0x69, 0x40, 0x13, \ + 0x40, 0x33, 0xc0, 0x12, \ + 0xc0, 0x12, 0x62, 0x11, \ + 0xe0, 0x0c, 0x03, 0x06, \ + 0xe0, 0x70, 0x06, 0x06, \ + 0x65, 0xe9, 0x40, 0x11, \ + 0x40, 0x33, 0xc0, 0x31, \ + 0xc0, 0x18, 0x6a, 0xf9, \ + 0xb7, 0x31, 0xbf, 0x11, \ + 0x66, 0x79, 0x40, 0x31, \ + 0x40, 0x31, 0xc0, 0x31, \ + 0xc0, 0x31, 0x66, 0xa9, \ + 0x66, 0xd9, 0x40, 0x31, \ + 0x00, 0x39, 0xc0, 0x31, \ + 0xc0, 0x15, 0x6a, 0x51, \ + 0x65, 0x61, 0x40, 0x21, \ + 0x40, 0x11, 0xc0, 0x31, \ + 0xc0, 0x31, 0x66, 0x49, \ + 0x67, 0x09, 0x40, 0x11, \ + 0x91, 0x11, 0xc0, 0x31, \ + 0x68, 0xf1, 0x40, 0x11, \ + 0xc2, 0x39, 0xc0, 0x27, \ + 0x6e, 0x89, 0x40, 0x21, \ + 0x40, 0x31, 0xc0, 0x30, \ + 0xc0, 0x15, 0x6a, 0x51, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe3, 0xc0, 0x02, 0x06, \ + 0x60, 0xc9, 0x04, 0x06, \ + 0x40, 0x15, 0xc0, 0x14, \ + 0xc0, 0x05, 0x64, 0x5d, \ + 0x65, 0xb9, 0x40, 0x25, \ + 0x03, 0x76, 0xc0, 0x0a, \ + 0xc2, 0xc2, 0x60, 0x81, \ + 0x60, 0x81, 0x06, 0x36, \ + 0x40, 0x15, 0xc8, 0x02, \ + 0xc0, 0x0a, 0x65, 0xbd, \ + 0x65, 0xc1, 0x40, 0x35, \ + 0x40, 0x25, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x69, 0xc1, \ + 0x69, 0xc5, 0x40, 0x15, \ + 0x40, 0x35, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x69, 0xc9, \ + 0x6b, 0x85, 0x40, 0x25, \ + 0x40, 0x15, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x6b, 0x89, \ + 0x6b, 0x8d, 0x40, 0x35, \ + 0x40, 0x25, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x6f, 0x55, \ + 0x6f, 0x59, 0x40, 0x15, \ + 0x40, 0x35, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x6f, 0x5d, \ + 0x6f, 0xe1, 0x40, 0x25, \ + 0x40, 0x15, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x6f, 0xe5, \ + 0xe0, 0x80, 0x02, 0x36, \ + 0x6f, 0xd1, 0x40, 0x13, \ + 0x91, 0x11, 0xc0, 0x15, \ + 0x6f, 0xa9, 0x40, 0x11, \ + 0x44, 0x06, 0xc0, 0x2b, \ + 0x40, 0x21, 0xe0, 0x00, \ + 0xc0, 0x2b, 0x6f, 0xad, \ + 0xe9, 0x08, 0x04, 0x06, \ + 0x6f, 0x91, 0x40, 0x23, \ + 0x04, 0x06, 0xc0, 0x16, \ + 0x40, 0x21, 0xe0, 0x08, \ + 0xc0, 0x2d, 0x6f, 0x29, \ + 0x40, 0x31, 0x83, 0x31, \ + 0xc0, 0x2f, 0x66, 0x21, \ + 0x40, 0x41, 0x85, 0x41, \ + 0xc0, 0x2f, 0x66, 0x41, \ + 0xe0, 0x7c, 0x0b, 0x06, \ + 0x6d, 0xb1, 0x40, 0x55, \ + 0x0b, 0x16, 0xc0, 0x0b, \ + 0x40, 0x53, 0xe0, 0x80, \ + 0xc0, 0x17, 0x6b, 0x69, \ + 0xe0, 0x20, 0x0a, 0x26, \ + 0x66, 0xd9, 0x40, 0x31, \ + 0x40, 0x53, 0xc0, 0x2f, \ + 0xc0, 0x17, 0x6b, 0x99, \ + 0xe3, 0x80, 0x06, 0x06, \ + 0x61, 0x01, 0x0a, 0x06, \ + 0x40, 0x35, 0xff, 0xce, \ + 0xc0, 0x0b, 0x6d, 0xe1, \ + 0x6d, 0xe5, 0x40, 0x55, \ + 0x07, 0x86, 0xc0, 0x0b, \ + 0x99, 0x51, 0xe0, 0x24, \ + 0x6b, 0xd1, 0x40, 0x33, \ + 0x40, 0x51, 0xc0, 0x17, \ + 0xc0, 0x2f, 0x67, 0xad, \ + 0x60, 0x41, 0x06, 0x16, \ + 0x4b, 0xd6, 0xc1, 0x81, \ + 0x40, 0x35, 0xef, 0xec, \ + 0xc0, 0x0b, 0x6d, 0xed, \ + 0x6b, 0xe1, 0x40, 0x53, \ + 0x06, 0x06, 0xc0, 0x17, \ + 0x0a, 0x0e, 0xf9, 0x00, \ + 0x40, 0x35, 0xff, 0xc0, \ + 0xc0, 0x0b, 0x6d, 0xf5, \ + 0x6d, 0xf9, 0x40, 0x55, \ + 0x06, 0x06, 0xc0, 0x0b, \ + 0x0a, 0x16, 0xe0, 0x40, \ + 0x40, 0x35, 0xe0, 0x10, \ + 0xc0, 0x0b, 0x6d, 0xfd, \ + 0x6c, 0x01, 0x40, 0x53, \ + 0x40, 0x41, 0xc0, 0x17, \ + 0xc0, 0x2f, 0x68, 0x09, \ + 0xe0, 0x40, 0x06, 0x56, \ + 0x6e, 0x51, 0x40, 0x33, \ + 0x07, 0xc6, 0xc0, 0x17, \ + 0xc0, 0x32, 0x60, 0x5d, \ + 0x6f, 0x59, 0x40, 0x35, \ + 0x06, 0xd6, 0xc0, 0x0b, \ + 0x40, 0x33, 0xe0, 0xcc, \ + 0xc0, 0x17, 0x6e, 0xb9, \ + 0x6f, 0xfd, 0x47, 0xf6, \ + 0x40, 0x35, 0xc0, 0x41, \ + 0xc0, 0x0b, 0x6f, 0x61, \ + 0xe2, 0x60, 0x06, 0x16, \ + 0x6e, 0xc9, 0x40, 0x33, \ + 0x47, 0xf6, 0xc0, 0x17, \ + 0x40, 0x31, 0xe0, 0x0c, \ + 0xc0, 0x30, 0x6e, 0xe5, \ + 0x40, 0x31, 0x8f, 0x31, \ + 0xc0, 0x30, 0x6f, 0x41, \ + 0xe0, 0x10, 0x06, 0x76, \ + 0x68, 0x0d, 0x40, 0x33, \ + 0x06, 0x16, 0xc0, 0x18, \ + 0x40, 0x33, 0xe0, 0x40, \ + 0xc0, 0x18, 0x68, 0x11, \ + 0x40, 0x31, 0x89, 0x31, \ + 0xc0, 0x31, 0x60, 0x29, \ + 0x70, 0x85, 0x47, 0xce, \ + 0x40, 0x35, 0xc0, 0x86, \ + 0xc0, 0x0c, 0x64, 0x11, \ + 0xe1, 0x50, 0x06, 0x16, \ + 0x68, 0x29, 0x40, 0x33, \ + 0x40, 0x21, 0xc0, 0x18, \ + 0xc0, 0x31, 0x60, 0x5d, \ + 0x60, 0x65, 0x40, 0x11, \ + 0x8b, 0x21, 0xc0, 0x31, \ + 0x65, 0xc5, 0x40, 0x21, \ + 0xb5, 0x21, 0xc0, 0x31, \ + 0x65, 0xc9, 0x40, 0x21, \ + 0x06, 0x16, 0xc0, 0x31, \ + 0x40, 0x33, 0xe0, 0x50, \ + 0xc0, 0x18, 0x6a, 0xe9, \ + 0x65, 0xd9, 0x40, 0x21, \ + 0x06, 0x26, 0xc0, 0x31, \ + 0x08, 0xc6, 0xe0, 0x50, \ + 0x40, 0x33, 0xe0, 0x0c, \ + 0xc0, 0x18, 0x6a, 0xf1, \ + 0x65, 0xe9, 0x40, 0x41, \ + 0x07, 0x8e, 0xc0, 0x31, \ + 0xc1, 0x40, 0x70, 0x61, \ + 0x70, 0x51, 0x08, 0x4e, \ + 0x40, 0x35, 0xc0, 0x3f, \ + 0xc0, 0x0c, 0x65, 0x7d, \ + 0x65, 0x81, 0x40, 0x45, \ + 0x87, 0x31, 0xc0, 0x0c, \ + 0x72, 0xfd, 0x49, 0xb6, \ + 0x40, 0x31, 0xc0, 0x20, \ + 0xc0, 0x31, 0x66, 0x11, \ + 0x65, 0x89, 0x40, 0x45, \ + 0x40, 0x45, 0xc0, 0x0c, \ + 0xc0, 0x0c, 0x65, 0x8d, \ + 0xe0, 0x50, 0x06, 0x56, \ + 0x6b, 0x21, 0x40, 0x33, \ + 0xbd, 0x31, 0xc0, 0x18, \ + 0x66, 0x49, 0x40, 0x31, \ + 0x06, 0x96, 0xc0, 0x31, \ + 0xc0, 0x20, 0x70, 0x01, \ + 0x65, 0x95, 0x40, 0x35, \ + 0x0a, 0x66, 0xc0, 0x0c, \ + 0x40, 0x35, 0xe0, 0x50, \ + 0xc0, 0x0c, 0x65, 0x99, \ + 0x6b, 0x39, 0x40, 0x53, \ + 0x40, 0x21, 0xc0, 0x18, \ + 0xc0, 0x31, 0x66, 0x79, \ + 0x65, 0xa1, 0x40, 0x45, \ + 0x40, 0x45, 0xc0, 0x0c, \ + 0xc0, 0x0c, 0x65, 0xa5, \ + 0xe0, 0x50, 0x06, 0x76, \ + 0x6b, 0x51, 0x40, 0x33, \ + 0x06, 0x06, 0xc0, 0x18, \ + 0xc0, 0x20, 0x60, 0x01, \ + 0x66, 0xa9, 0x40, 0x21, \ + 0x40, 0x35, 0xc0, 0x31, \ + 0xc0, 0x0c, 0x65, 0xad, \ + 0x65, 0xb1, 0x40, 0x35, \ + 0x04, 0x86, 0xc0, 0x0c, \ + 0x40, 0x23, 0xe0, 0x50, \ + 0xc0, 0x18, 0x6b, 0x69, \ + 0x40, 0x21, 0xb9, 0x21, \ + 0xc0, 0x31, 0x66, 0xd9, \ + 0x68, 0x01, 0x06, 0x66, \ + 0x40, 0x35, 0xc0, 0x20, \ + 0xc0, 0x0c, 0x65, 0xb9, \ + 0xe0, 0x50, 0x08, 0x96, \ + 0x65, 0xbd, 0x40, 0x35, \ + 0x40, 0x43, 0xc0, 0x0c, \ + 0xc0, 0x18, 0x6b, 0x81, \ + 0x67, 0x09, 0x40, 0x21, \ + 0x04, 0x76, 0xc0, 0x31, \ + 0x40, 0x23, 0xe0, 0x70, \ + 0xc0, 0x18, 0x6b, 0x99, \ + 0xe1, 0x80, 0x04, 0x06, \ + 0x65, 0xf9, 0x40, 0x25, \ + 0x44, 0x46, 0xc0, 0x0c, \ + 0xc0, 0xfa, 0x60, 0x05, \ + 0x65, 0xfd, 0x40, 0x25, \ + 0x40, 0x11, 0xc0, 0x0c, \ + 0xc0, 0x31, 0x6b, 0xa5, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe1, 0x48, 0x02, 0x06, \ + 0x04, 0x15, 0x84, 0x24, \ + 0xc0, 0x0c, 0x65, 0x69, \ + 0xa1, 0x1b, 0x80, 0x1f, \ + 0x42, 0x06, 0x88, 0xe5, \ + 0x83, 0x31, 0xe1, 0x48, \ + 0x08, 0x86, 0x84, 0x3e, \ + 0x90, 0x44, 0xe1, 0x48, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x65, 0x65, 0x44, 0x35, \ + 0x90, 0x0e, 0xc0, 0x0c, \ + 0xc1, 0x83, 0x84, 0x25, \ + 0x00, 0x06, 0xc3, 0x13, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0xe1, 0x34, 0x05, 0x86, \ + 0x00, 0x00, 0x88, 0x24, \ + 0xc0, 0xc5, 0x65, 0x91, \ + 0x01, 0x46, 0x88, 0x0c, \ + 0x80, 0x04, 0xe1, 0x34, \ + 0x84, 0x04, 0x80, 0x1c, \ + 0xfe, 0xd5, 0xa1, 0x0b, \ + 0xe1, 0x48, 0x00, 0x86, \ + 0x81, 0x11, 0x80, 0x04, \ + 0xdd, 0x93, 0x80, 0x1e, \ + 0xe1, 0x10, 0x43, 0xc6, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x6e, 0x31, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x2f, \ + 0xe1, 0x44, 0x01, 0x86, \ + 0x80, 0x08, 0x80, 0x04, \ + 0xe0, 0x00, 0x00, 0x4a, \ + 0xe2, 0x00, 0x00, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe1, 0x44, 0x01, 0x86, \ + 0x81, 0x11, 0x80, 0x04, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xe1, 0x44, 0x01, 0x06, \ + 0x80, 0x08, 0x80, 0x04, \ + 0x40, 0x02, 0x83, 0x11, \ + 0xdd, 0x93, 0xf8, 0x84, \ + 0xe1, 0x44, 0x01, 0x06, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xc2, 0x19, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xe1, 0x44, 0x01, 0x06, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xf8, 0x42, 0x10, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xe1, 0x44, 0x01, 0x06, \ + 0x80, 0x00, 0x80, 0x04, \ + 0x80, 0xf5, 0xa1, 0x0d, \ + 0xe1, 0x44, 0x01, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xc2, 0x19, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe1, 0x44, 0x01, 0x06, \ + 0x80, 0x00, 0x80, 0x04, \ + 0x82, 0x15, 0xa1, 0x0d, \ + 0xe1, 0x44, 0x01, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xf8, 0x42, 0x10, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xe1, 0x44, 0x01, 0x06, \ + 0x80, 0x00, 0x80, 0x04, \ + 0x80, 0xf5, 0xa1, 0x0d, \ + 0xe1, 0x44, 0x01, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xc4, 0x19, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe1, 0x44, 0x01, 0x06, \ + 0x80, 0x00, 0x80, 0x04, \ + 0x82, 0x15, 0xa1, 0x0d, \ + 0xe1, 0x44, 0x01, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xf4, 0x42, 0x10, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xe1, 0x44, 0x01, 0x06, \ + 0x80, 0x00, 0x80, 0x04, \ + 0x80, 0xf5, 0xa1, 0x0d, \ + 0xe1, 0x44, 0x01, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xc8, 0x19, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe1, 0x44, 0x01, 0x06, \ + 0x80, 0x00, 0x80, 0x04, \ + 0x82, 0x15, 0xa1, 0x0d, \ + 0xe1, 0x44, 0x01, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xec, 0x42, 0x10, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x81, 0x81, 0x80, 0x6b, \ + 0x81, 0x11, 0x81, 0x01, \ + 0xe8, 0x08, 0x68, 0x88, \ + 0x4a, 0x58, 0x80, 0x7b, \ + 0x0c, 0x15, 0xe8, 0x00, \ + 0xc0, 0x0c, 0x65, 0x3d, \ + 0x83, 0x21, 0x83, 0x91, \ + 0x74, 0x98, 0x8c, 0x0b, \ + 0x00, 0x06, 0xe8, 0x34, \ + 0x80, 0x04, 0xe1, 0x34, \ + 0x41, 0xc6, 0x80, 0x96, \ + 0x80, 0x04, 0xe1, 0x30, \ + 0x40, 0x86, 0x80, 0x96, \ + 0x80, 0x04, 0xe1, 0x44, \ + 0x80, 0x96, 0x80, 0x86, \ + 0x00, 0x46, 0x80, 0x86, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0xe0, 0x80, 0x00, 0x15, \ + 0x40, 0x15, 0x82, 0x11, \ + 0x95, 0x01, 0xe0, 0x80, \ + 0xe8, 0x0c, 0x66, 0xc8, \ + 0x8e, 0x0b, 0x83, 0x11, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0x25, 0xd8, 0xf2, 0x1c, \ + 0x00, 0x0c, 0xe0, 0x08, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x02, 0x06, 0x80, 0x6b, \ + 0x77, 0x98, 0xe2, 0x80, \ + 0x00, 0x46, 0xe8, 0x38, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0x67, 0x41, 0x02, 0x60, \ + 0x04, 0x60, 0xc0, 0x59, \ + 0xc0, 0x56, 0x66, 0x51, \ + 0x64, 0xd1, 0x06, 0x60, \ + 0x08, 0x60, 0xc0, 0xa3, \ + 0xc0, 0x56, 0x67, 0x21, \ + 0x67, 0x11, 0x0a, 0x60, \ + 0x18, 0x60, 0xc0, 0xc7, \ + 0xc0, 0xbf, 0x64, 0x21, \ + 0x67, 0xe1, 0x0e, 0x60, \ + 0xfc, 0xce, 0xc0, 0xbe, \ + 0xbc, 0x6e, 0xbe, 0x7e, \ + 0xef, 0xc8, 0x6f, 0xc8, \ + 0x6f, 0x11, 0x0c, 0x09, \ + 0x0c, 0x19, 0xc0, 0x31, \ + 0xc0, 0x31, 0x65, 0x41, \ + 0x42, 0x10, 0x82, 0x09, \ + 0xa0, 0x1f, 0xf3, 0xf8, \ + 0x65, 0x41, 0x4c, 0x11, \ + 0x98, 0xf1, 0xc0, 0x31, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x31, 0x65, 0x51, \ + 0x84, 0x55, 0xe2, 0x0d, \ + 0xe0, 0x80, 0x42, 0x4a, \ + 0xe0, 0x00, 0x00, 0x1a, \ + 0xed, 0x80, 0x42, 0x0a, \ + 0xe9, 0x9c, 0x40, 0x0a, \ + 0x41, 0xb6, 0x82, 0x25, \ + 0x61, 0x58, 0xe0, 0x00, \ + 0x8c, 0x0b, 0xe8, 0x0c, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x0c, 0x0c, 0xef, 0x85, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0x65, 0x41, 0x00, 0x19, \ + 0x04, 0x14, 0xc0, 0x31, \ + 0x84, 0x29, 0xe0, 0x84, \ + 0xf3, 0xf4, 0x42, 0x10, \ + 0x40, 0x21, 0xa2, 0x2f, \ + 0xc0, 0x31, 0x65, 0x41, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0x65, 0x41, 0x00, 0x19, \ + 0x42, 0x0a, 0xc0, 0x31, \ + 0x80, 0x55, 0xe0, 0x84, \ + 0x00, 0x19, 0xdd, 0x93, \ + 0xc0, 0x2f, 0x65, 0xe5, \ + 0x82, 0x15, 0xa7, 0x1b, \ + 0x64, 0xe1, 0x00, 0x19, \ + 0xc8, 0x19, 0xc0, 0x31, \ + 0x64, 0xe1, 0x40, 0x11, \ + 0xeb, 0x15, 0xc0, 0x31, \ + 0xe1, 0x34, 0x00, 0x06, \ + 0x81, 0x11, 0x80, 0x04, \ + 0x40, 0x86, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x44, \ + 0x83, 0x21, 0x80, 0x16, \ + 0x80, 0x16, 0x80, 0x26, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0x6e, 0x91, 0x00, 0x19, \ + 0x00, 0x29, 0xc0, 0x2f, \ + 0xc0, 0x15, 0x61, 0x51, \ + 0xe0, 0x84, 0x06, 0x14, \ + 0xe6, 0x86, 0x42, 0x2a, \ + 0xfa, 0x80, 0x04, 0x34, \ + 0xe0, 0x0a, 0x42, 0x1a, \ + 0xe0, 0x0c, 0x04, 0xd6, \ + 0xf0, 0x00, 0x06, 0x0a, \ + 0x6e, 0x59, 0x40, 0x21, \ + 0x40, 0x31, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x89, \ + 0x6e, 0x81, 0x40, 0x11, \ + 0x80, 0xc5, 0xc0, 0x2f, \ + 0x65, 0x61, 0x00, 0x15, \ + 0xaf, 0x1b, 0xc0, 0x0c, \ + 0x00, 0x00, 0x82, 0x55, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xff, 0x21, 0x81, 0x11, \ + 0xe0, 0x0c, 0x06, 0xd6, \ + 0xff, 0xf8, 0x36, 0xc8, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x6e, 0x95, 0x00, 0x19, \ + 0x00, 0x29, 0xc0, 0x2f, \ + 0xc0, 0x15, 0x61, 0x55, \ + 0xe0, 0x84, 0x06, 0x14, \ + 0xe6, 0x86, 0x42, 0x2a, \ + 0xfa, 0x80, 0x04, 0x34, \ + 0xe0, 0x0a, 0x42, 0x1a, \ + 0xe0, 0x0c, 0x04, 0xe6, \ + 0xf0, 0x00, 0x06, 0x0a, \ + 0x6e, 0x59, 0x40, 0x21, \ + 0x40, 0x31, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x8d, \ + 0x6e, 0x85, 0x40, 0x11, \ + 0x80, 0xc5, 0xc0, 0x2f, \ + 0x65, 0x61, 0x00, 0x15, \ + 0xb1, 0x1b, 0xc0, 0x0c, \ + 0x00, 0x00, 0x82, 0x55, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xff, 0x21, 0x81, 0x11, \ + 0xe0, 0x0c, 0x06, 0xe6, \ + 0xff, 0xf8, 0x34, 0x08, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x15, 0x61, 0x55, \ + 0xec, 0x04, 0x02, 0x00, \ + 0xf4, 0x1c, 0x03, 0x0a, \ + 0x0c, 0x19, 0x84, 0x75, \ + 0xc0, 0x15, 0x61, 0x51, \ + 0xec, 0x04, 0x04, 0x10, \ + 0xf4, 0x24, 0x05, 0x0a, \ + 0x40, 0x0a, 0x82, 0x75, \ + 0x01, 0x0a, 0xe6, 0x86, \ + 0x80, 0xd5, 0xe5, 0x40, \ + 0x65, 0x61, 0x0c, 0x05, \ + 0xb5, 0x0b, 0xc0, 0x0c, \ + 0x0c, 0x39, 0x82, 0x75, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x66, 0xc1, 0x00, 0x60, \ + 0x81, 0x11, 0xc0, 0x56, \ + 0x71, 0x78, 0x93, 0x21, \ + 0x0c, 0x09, 0xef, 0xf8, \ + 0xc0, 0x15, 0x61, 0x51, \ + 0x61, 0x61, 0x0c, 0x19, \ + 0x40, 0x1a, 0xc0, 0x15, \ + 0x01, 0x0a, 0xe6, 0x82, \ + 0x84, 0x35, 0xe4, 0xa0, \ + 0x61, 0x55, 0x0c, 0x09, \ + 0x0c, 0x19, 0xc0, 0x15, \ + 0xc0, 0x15, 0x61, 0x65, \ + 0xe6, 0x82, 0x40, 0x1a, \ + 0xe4, 0x80, 0x01, 0x0a, \ + 0x0c, 0x05, 0x80, 0xd5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0x82, 0x75, 0xb7, 0x0b, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x00, 0x60, 0xc0, 0x2f, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xa3, 0x21, 0x81, 0x11, \ + 0xef, 0xf8, 0x6f, 0x28, \ + 0x61, 0x51, 0x0c, 0x09, \ + 0x0c, 0x19, 0xc0, 0x15, \ + 0xc0, 0x15, 0x61, 0x61, \ + 0x61, 0x59, 0x0c, 0x29, \ + 0x40, 0x1a, 0xc0, 0x15, \ + 0x84, 0x03, 0xe6, 0x82, \ + 0xe4, 0xa0, 0x01, 0x0a, \ + 0x0c, 0x09, 0x84, 0x15, \ + 0xc0, 0x15, 0x61, 0x55, \ + 0x61, 0x65, 0x0c, 0x19, \ + 0x0c, 0x29, 0xc0, 0x15, \ + 0xc0, 0x15, 0x61, 0x5d, \ + 0xe6, 0x82, 0x40, 0x1a, \ + 0x00, 0x8a, 0x84, 0x03, \ + 0x82, 0x75, 0xe4, 0x84, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x00, 0x60, 0xc0, 0x2f, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xa3, 0x21, 0x81, 0x11, \ + 0xef, 0xf8, 0x6c, 0xa8, \ + 0x61, 0x61, 0x0c, 0x09, \ + 0xa9, 0x0b, 0xc0, 0x15, \ + 0x0c, 0x09, 0x80, 0xd5, \ + 0xc0, 0x15, 0x61, 0x65, \ + 0x80, 0xc5, 0xa9, 0x0b, \ + 0x65, 0x61, 0x0c, 0x05, \ + 0xb9, 0x0b, 0xc0, 0x0c, \ + 0x0c, 0x09, 0x86, 0x95, \ + 0xc0, 0x15, 0x61, 0x51, \ + 0xe0, 0x80, 0x40, 0x4a, \ + 0x0c, 0x09, 0x82, 0x85, \ + 0xc0, 0x15, 0x61, 0x55, \ + 0xe0, 0x80, 0x40, 0x4a, \ + 0x0c, 0x05, 0x80, 0xc5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0x82, 0x75, 0xb9, 0x0b, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x00, 0x60, 0xc0, 0x2f, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0x95, 0x21, 0x81, 0x11, \ + 0xef, 0xf8, 0x6a, 0x08, \ + 0x61, 0x59, 0x0c, 0x09, \ + 0x00, 0x4a, 0xc0, 0x15, \ + 0x86, 0x05, 0xf8, 0x0c, \ + 0x61, 0x5d, 0x0c, 0x09, \ + 0x00, 0x4a, 0xc0, 0x15, \ + 0x84, 0x45, 0xf8, 0x0c, \ + 0x61, 0x61, 0x0c, 0x09, \ + 0x00, 0x4a, 0xc0, 0x15, \ + 0x82, 0x85, 0xf8, 0x0c, \ + 0x61, 0x65, 0x0c, 0x09, \ + 0x00, 0x4a, 0xc0, 0x15, \ + 0x80, 0xc5, 0xf8, 0x0c, \ + 0x65, 0x61, 0x0c, 0x05, \ + 0xbb, 0x0b, 0xc0, 0x0c, \ + 0x0c, 0x39, 0x82, 0x75, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x66, 0xc1, 0x00, 0x60, \ + 0x81, 0x11, 0xc0, 0x56, \ + 0x67, 0x98, 0x97, 0x21, \ + 0x0c, 0x09, 0xef, 0xf8, \ + 0xc0, 0x15, 0x61, 0x51, \ + 0x88, 0xf5, 0xa3, 0x0b, \ + 0x61, 0x55, 0x0c, 0x09, \ + 0xa3, 0x0b, 0xc0, 0x15, \ + 0x0c, 0x09, 0x88, 0x55, \ + 0xc0, 0x15, 0x61, 0x61, \ + 0xec, 0x10, 0x00, 0x00, \ + 0xf4, 0x30, 0x01, 0x0a, \ + 0x0c, 0x09, 0x82, 0xd5, \ + 0xc0, 0x15, 0x61, 0x65, \ + 0xec, 0x10, 0x00, 0x00, \ + 0xf4, 0x30, 0x01, 0x0a, \ + 0x0c, 0x05, 0x80, 0xd5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0x8c, 0x15, 0xbd, 0x0b, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x00, 0x60, 0xc0, 0x2f, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0x9b, 0x21, 0x81, 0x11, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0xf8, 0x24, 0xe8, \ + 0x61, 0x61, 0x0c, 0x09, \ + 0xb1, 0x0b, 0xc0, 0x15, \ + 0xa9, 0x0b, 0x82, 0x25, \ + 0x0c, 0x09, 0x84, 0x35, \ + 0xc0, 0x15, 0x61, 0x65, \ + 0x80, 0xe5, 0xa9, 0x0b, \ + 0x0c, 0x09, 0x81, 0xb5, \ + 0xc0, 0x15, 0x61, 0x65, \ + 0x80, 0xd5, 0xb1, 0x0b, \ + 0x65, 0x61, 0x0c, 0x05, \ + 0xbf, 0x0b, 0xc0, 0x0c, \ + 0x0c, 0x39, 0x82, 0xb5, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x66, 0xc1, 0x00, 0x60, \ + 0x81, 0x11, 0xc0, 0x56, \ + 0x0c, 0x0c, 0x99, 0x21, \ + 0x22, 0xb8, 0xf2, 0x00, \ + 0x0c, 0x0c, 0xff, 0xf8, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x6e, 0x91, 0x00, 0x19, \ + 0x00, 0x29, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x95, \ + 0xe1, 0x88, 0x06, 0x14, \ + 0x02, 0x46, 0xc5, 0x27, \ + 0x48, 0x2a, 0xe0, 0x40, \ + 0x40, 0x13, 0xe6, 0x8e, \ + 0xc0, 0x17, 0x6f, 0x45, \ + 0xe0, 0x40, 0x08, 0x0a, \ + 0x40, 0x11, 0x83, 0x11, \ + 0xc0, 0x2f, 0x6e, 0x99, \ + 0x6e, 0x81, 0x40, 0x31, \ + 0x40, 0x21, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x85, \ + 0x6e, 0x79, 0x40, 0x11, \ + 0x80, 0x95, 0xc0, 0x30, \ + 0x6e, 0x81, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x30, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x2f, 0x65, 0xd5, \ + 0xec, 0x2c, 0x00, 0x00, \ + 0x00, 0x8a, 0x81, 0x11, \ + 0x89, 0x16, 0xf4, 0x0c, \ + 0xe0, 0x04, 0x40, 0x06, \ + 0x4c, 0x01, 0xbc, 0x16, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x8c, 0x0b, 0xac, 0x95, \ + 0xef, 0xfc, 0x6a, 0xe8, \ + 0x61, 0x51, 0x0c, 0x09, \ + 0x0c, 0x19, 0xc0, 0x15, \ + 0xc0, 0x15, 0x61, 0x55, \ + 0xe6, 0x82, 0x40, 0x1a, \ + 0xe4, 0x10, 0x02, 0xf0, \ + 0x4c, 0x01, 0x9e, 0x2b, \ + 0xc0, 0x16, 0x67, 0x79, \ + 0x4a, 0xd8, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x00, \ + 0xc0, 0x15, 0x61, 0x59, \ + 0xc5, 0x07, 0x89, 0x18, \ + 0x0c, 0x29, 0x80, 0x13, \ + 0xc0, 0x15, 0x61, 0x5d, \ + 0x0c, 0x09, 0xbc, 0x38, \ + 0xc0, 0x15, 0x61, 0x51, \ + 0x6e, 0x79, 0x4c, 0x11, \ + 0x0c, 0x19, 0xc0, 0x2f, \ + 0xc0, 0x15, 0x61, 0x61, \ + 0xe1, 0x88, 0x08, 0x24, \ + 0x61, 0x65, 0x0c, 0x29, \ + 0x86, 0x43, 0xc0, 0x15, \ + 0x4c, 0x41, 0xa3, 0x0b, \ + 0xc0, 0x2f, 0x6e, 0x7d, \ + 0x6e, 0x91, 0x4c, 0x11, \ + 0x4c, 0x21, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x95, \ + 0x61, 0x55, 0x0c, 0x39, \ + 0x82, 0x15, 0xc0, 0x15, \ + 0xf0, 0x04, 0x06, 0x0a, \ + 0x8c, 0x0b, 0x80, 0xb5, \ + 0xef, 0xfc, 0x78, 0x88, \ + 0x07, 0x0a, 0x8d, 0x65, \ + 0x81, 0x41, 0xf4, 0x0c, \ + 0x6e, 0x99, 0x4c, 0x41, \ + 0x82, 0x55, 0xc0, 0x2f, \ + 0x4c, 0x21, 0xea, 0x0d, \ + 0xc0, 0x2f, 0x6e, 0x8d, \ + 0x6e, 0x85, 0x4c, 0x31, \ + 0x88, 0x85, 0xc0, 0x2f, \ + 0x06, 0x0a, 0x85, 0xa5, \ + 0x84, 0xb5, 0xf0, 0x10, \ + 0x4c, 0x21, 0xe8, 0x0d, \ + 0xc0, 0x2f, 0x6e, 0x8d, \ + 0x6e, 0x85, 0x4c, 0x31, \ + 0x86, 0x05, 0xc0, 0x2f, \ + 0x5f, 0x98, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xef, 0xfc, \ + 0xc0, 0x2f, 0x6e, 0x81, \ + 0x86, 0x55, 0xab, 0x0b, \ + 0x61, 0xd8, 0x8c, 0x0b, \ + 0x83, 0x65, 0xef, 0xfc, \ + 0x61, 0x98, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xef, 0xfc, \ + 0xc0, 0x15, 0x61, 0x51, \ + 0x82, 0x75, 0xe8, 0x0d, \ + 0x6e, 0x91, 0x0c, 0x19, \ + 0x4c, 0x11, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x89, \ + 0x6e, 0x81, 0x4c, 0x01, \ + 0x81, 0x45, 0xc0, 0x2f, \ + 0x5d, 0xb8, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xef, 0xfc, \ + 0xc0, 0x2f, 0x6e, 0x81, \ + 0x6e, 0x85, 0x0c, 0x19, \ + 0x40, 0x1a, 0xc0, 0x2f, \ + 0x00, 0x0a, 0xe6, 0x82, \ + 0x80, 0xb5, 0xe0, 0x40, \ + 0x4c, 0x01, 0x83, 0x01, \ + 0xc0, 0x30, 0x6e, 0x81, \ + 0x6e, 0x89, 0x0c, 0x09, \ + 0x0c, 0x19, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x8d, \ + 0x6e, 0x91, 0x0c, 0x29, \ + 0x0c, 0x39, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x95, \ + 0xe6, 0x82, 0x40, 0x1a, \ + 0xe6, 0x8a, 0x42, 0x3a, \ + 0xe0, 0x06, 0x40, 0x0a, \ + 0xfa, 0x80, 0x02, 0x04, \ + 0x6e, 0x79, 0x4c, 0x01, \ + 0x4c, 0x13, 0xc0, 0x30, \ + 0xc0, 0x18, 0x67, 0x31, \ + 0x43, 0xf8, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xe8, 0x00, \ + 0xef, 0xf4, 0x5a, 0x08, \ + 0x69, 0xd8, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xe8, 0x1c, \ + 0xe8, 0x24, 0x7f, 0x68, \ + 0x83, 0x11, 0xa1, 0x01, \ + 0x69, 0xd1, 0x4c, 0x01, \ + 0x4c, 0x11, 0xc0, 0x1c, \ + 0xc0, 0x23, 0x69, 0xb1, \ + 0x0c, 0x0c, 0x90, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0x6d, 0x91, 0x00, 0x59, \ + 0x0a, 0x8a, 0xc0, 0x28, \ + 0x00, 0x39, 0xe5, 0x40, \ + 0xc0, 0x28, 0x6d, 0x95, \ + 0x81, 0x41, 0x81, 0x01, \ + 0xc5, 0x57, 0x80, 0x95, \ + 0xec, 0x50, 0x08, 0x50, \ + 0xe5, 0x00, 0x06, 0x8a, \ + 0x80, 0x95, 0x84, 0x46, \ + 0x00, 0x30, 0xc5, 0x37, \ + 0x88, 0x06, 0xec, 0x40, \ + 0xa1, 0x3b, 0x84, 0x38, \ + 0x84, 0x31, 0x80, 0xa5, \ + 0x84, 0x36, 0xc5, 0x37, \ + 0x00, 0x0a, 0x88, 0x08, \ + 0x80, 0xc5, 0xfb, 0xfc, \ + 0xfa, 0x80, 0x00, 0x04, \ + 0xc5, 0x07, 0x84, 0x01, \ + 0xdd, 0x93, 0x88, 0x06, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x40, 0x98, 0x80, 0x6b, \ + 0x8c, 0x0b, 0xe8, 0x00, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x00, 0x0c, 0x8d, 0x45, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x2f, 0x6e, 0x91, \ + 0x6e, 0x95, 0x0c, 0x19, \ + 0x40, 0x0a, 0xc0, 0x2f, \ + 0xa1, 0x0b, 0xe5, 0x06, \ + 0xe0, 0x0c, 0x02, 0xb6, \ + 0x6e, 0x59, 0x4c, 0x11, \ + 0x80, 0xe5, 0xc0, 0x2f, \ + 0x65, 0x61, 0x0c, 0x15, \ + 0x02, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x95, 0xe0, 0x40, \ + 0x66, 0xc1, 0x00, 0x60, \ + 0x81, 0x71, 0xc0, 0x56, \ + 0x9f, 0x21, 0x81, 0x11, \ + 0xe0, 0x0c, 0x06, 0xb6, \ + 0xef, 0xf8, 0x4d, 0x68, \ + 0x0c, 0x19, 0x83, 0xb5, \ + 0xc0, 0x2f, 0x6e, 0x81, \ + 0x6e, 0x85, 0x0c, 0x29, \ + 0x0c, 0x39, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x89, \ + 0x6e, 0x8d, 0x0c, 0x49, \ + 0x42, 0x1a, 0xc0, 0x2f, \ + 0x44, 0x3a, 0xe5, 0x0a, \ + 0x42, 0x1a, 0xe5, 0x12, \ + 0x40, 0x1a, 0xe5, 0x0a, \ + 0x00, 0x02, 0xe0, 0x02, \ + 0x00, 0x04, 0xef, 0xfc, \ + 0x0e, 0x02, 0xfa, 0x80, \ + 0x0c, 0x09, 0xed, 0x00, \ + 0xc0, 0x2f, 0x6e, 0x91, \ + 0x6e, 0x95, 0x0c, 0x19, \ + 0xc2, 0x07, 0xc0, 0x2f, \ + 0x67, 0x71, 0x4c, 0x01, \ + 0xc2, 0x17, 0xc0, 0x1c, \ + 0x67, 0x75, 0x4c, 0x11, \ + 0x00, 0x06, 0xc0, 0x1c, \ + 0x4c, 0x01, 0xe0, 0x04, \ + 0xc0, 0x1c, 0x67, 0x79, \ + 0x4c, 0x71, 0x81, 0x01, \ + 0xc0, 0x1c, 0x67, 0x7d, \ + 0x67, 0x81, 0x4c, 0x01, \ + 0x88, 0xf1, 0xc0, 0x1c, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0x81, 0x01, 0x80, 0x6b, \ + 0x67, 0x7d, 0x0c, 0x79, \ + 0xfe, 0x0e, 0xc0, 0x1c, \ + 0x89, 0x06, 0x91, 0x06, \ + 0x6e, 0x91, 0x0c, 0x99, \ + 0x02, 0xf0, 0xc0, 0x2f, \ + 0x0c, 0xa9, 0xe4, 0x30, \ + 0xc0, 0x2f, 0x6e, 0x95, \ + 0x44, 0xb8, 0x8c, 0x0b, \ + 0x02, 0xf0, 0xe8, 0x00, \ + 0x04, 0xf0, 0xe4, 0x20, \ + 0xfe, 0x84, 0xe4, 0x10, \ + 0x74, 0xf8, 0x8c, 0x0b, \ + 0xa1, 0x8b, 0xef, 0xfc, \ + 0xa1, 0x7b, 0x8e, 0x45, \ + 0x91, 0x08, 0x8e, 0x05, \ + 0x6e, 0x79, 0x0c, 0x39, \ + 0x0c, 0x49, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x7d, \ + 0x02, 0x90, 0x89, 0x58, \ + 0x04, 0xa0, 0xec, 0x04, \ + 0xa0, 0x33, 0xec, 0x04, \ + 0x0a, 0x34, 0xaa, 0x43, \ + 0x06, 0x44, 0xe0, 0x8c, \ + 0x08, 0x60, 0xe0, 0x8c, \ + 0xc0, 0x72, 0x64, 0x91, \ + 0x82, 0xc1, 0x81, 0xc1, \ + 0x52, 0x12, 0xd1, 0xcd, \ + 0x54, 0x22, 0xf0, 0x94, \ + 0x48, 0x91, 0xf0, 0x8c, \ + 0x90, 0xa6, 0xf9, 0x00, \ + 0x0c, 0x99, 0x80, 0xe5, \ + 0xc0, 0x2f, 0x6e, 0x91, \ + 0x92, 0x53, 0xc2, 0x97, \ + 0x0c, 0x59, 0x81, 0xd5, \ + 0xc0, 0x2f, 0x6e, 0x95, \ + 0x6e, 0x79, 0x0c, 0xc9, \ + 0xc2, 0x57, 0xc0, 0x2f, \ + 0xa0, 0xc3, 0x8a, 0x33, \ + 0xe0, 0x8c, 0x0a, 0xc4, \ + 0x0e, 0x70, 0x81, 0xc1, \ + 0xa1, 0x7b, 0xec, 0x04, \ + 0xf8, 0x15, 0x82, 0x41, \ + 0x0c, 0x0c, 0xa0, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x00, 0x19, 0x82, 0x4b, \ + 0xc0, 0x2f, 0x6e, 0x81, \ + 0x6e, 0x89, 0x00, 0x29, \ + 0x00, 0x39, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x91, \ + 0xc1, 0xc3, 0x85, 0xe5, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x80, 0x7b, 0x82, 0x6b, \ + 0xfc, 0x0e, 0x81, 0x01, \ + 0xe4, 0x20, 0x02, 0xf0, \ + 0x8e, 0x0b, 0xbe, 0x0e, \ + 0xef, 0xfc, 0x7e, 0x88, \ + 0xe4, 0x10, 0x02, 0xf0, \ + 0x40, 0xf8, 0x8e, 0x0b, \ + 0xfc, 0x04, 0xe8, 0x00, \ + 0x40, 0x1a, 0xbe, 0x14, \ + 0x00, 0x02, 0xe6, 0x82, \ + 0x98, 0x0e, 0xef, 0xfc, \ + 0x0c, 0x0c, 0x98, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x04, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x00, 0x19, 0x82, 0x4b, \ + 0xc0, 0x2f, 0x6e, 0x85, \ + 0x6e, 0x8d, 0x00, 0x29, \ + 0x00, 0x39, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x95, \ + 0xc1, 0xc3, 0x81, 0x25, \ + 0x0a, 0xc6, 0xa1, 0x3b, \ + 0x40, 0x51, 0xe0, 0x0c, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x00, 0x55, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe0, 0x44, 0x0a, 0x0a, \ + 0x00, 0x00, 0x82, 0x55, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0x9f, 0x21, 0x81, 0x11, \ + 0xe0, 0x0c, 0x06, 0xc6, \ + 0xff, 0xf4, 0x3e, 0xc8, \ + 0xe5, 0x0a, 0x40, 0x1a, \ + 0xe0, 0x0e, 0x40, 0x0a, \ + 0xef, 0xfc, 0x00, 0x02, \ + 0xdd, 0x93, 0x90, 0x0e, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0xf4, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x80, 0x03, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x01, 0x46, 0xc1, 0x83, \ + 0x80, 0x04, 0xe1, 0x44, \ + 0x80, 0x16, 0x87, 0x11, \ + 0xe1, 0x44, 0x01, 0x06, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xd0, 0x19, \ + 0xef, 0xe4, 0x75, 0x48, \ + 0x1f, 0x0c, 0x98, 0xf1, \ + 0x0b, 0xfc, 0xf0, 0x04, \ + 0xbc, 0xf4, 0xf6, 0x80, \ + 0xc1, 0x53, 0xfd, 0x64, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xe1, 0x44, 0x01, 0x46, \ + 0x89, 0x11, 0x80, 0x04, \ + 0x00, 0x06, 0x80, 0x16, \ + 0xc0, 0x20, 0x60, 0x11, \ + 0xe1, 0x44, 0x03, 0x06, \ + 0x84, 0x04, 0x80, 0x04, \ + 0xd0, 0x19, 0x80, 0x18, \ + 0x33, 0x98, 0x80, 0x16, \ + 0x00, 0x0c, 0xff, 0xe4, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe1, 0x44, 0x01, 0x46, \ + 0x8b, 0x11, 0x80, 0x04, \ + 0x00, 0x06, 0x80, 0x16, \ + 0xc0, 0x20, 0x60, 0x11, \ + 0xe1, 0x44, 0x03, 0x06, \ + 0x84, 0x04, 0x80, 0x04, \ + 0xd0, 0x19, 0x80, 0x18, \ + 0x32, 0x58, 0x80, 0x16, \ + 0x00, 0x0c, 0xff, 0xe4, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe1, 0x44, 0x01, 0x46, \ + 0x91, 0x11, 0x80, 0x04, \ + 0x00, 0x06, 0x80, 0x16, \ + 0xc0, 0x20, 0x60, 0x11, \ + 0xe1, 0x44, 0x03, 0x06, \ + 0x84, 0x04, 0x80, 0x04, \ + 0xd0, 0x19, 0x80, 0x18, \ + 0x31, 0x18, 0x80, 0x16, \ + 0x00, 0x0c, 0xff, 0xe4, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe1, 0x44, 0x01, 0x46, \ + 0x95, 0x11, 0x80, 0x04, \ + 0x00, 0x06, 0x80, 0x16, \ + 0xc0, 0x20, 0x60, 0x11, \ + 0xe1, 0x44, 0x03, 0x06, \ + 0x84, 0x04, 0x80, 0x04, \ + 0xd0, 0x19, 0x80, 0x18, \ + 0x2f, 0xd8, 0x80, 0x16, \ + 0x00, 0x0c, 0xff, 0xe4, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe1, 0x44, 0x01, 0x46, \ + 0x97, 0x11, 0x80, 0x04, \ + 0x00, 0x06, 0x80, 0x16, \ + 0xc0, 0x20, 0x60, 0x11, \ + 0xe1, 0x44, 0x03, 0x06, \ + 0x84, 0x04, 0x80, 0x04, \ + 0xd0, 0x19, 0x80, 0x18, \ + 0x2e, 0x98, 0x80, 0x16, \ + 0x00, 0x0c, 0xff, 0xe4, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe1, 0x44, 0x01, 0x46, \ + 0x9f, 0x11, 0x80, 0x04, \ + 0x00, 0x06, 0x80, 0x16, \ + 0xc0, 0x20, 0x60, 0x11, \ + 0xe1, 0x44, 0x03, 0x06, \ + 0x84, 0x04, 0x80, 0x04, \ + 0xd0, 0x19, 0x80, 0x18, \ + 0x2d, 0x58, 0x80, 0x16, \ + 0x00, 0x0c, 0xff, 0xe4, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe2, 0xc0, 0x00, 0x2c, \ + 0xe1, 0x44, 0x01, 0x46, \ + 0xa1, 0x11, 0x80, 0x04, \ + 0x01, 0x06, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x44, \ + 0xd0, 0x19, 0x80, 0x18, \ + 0x2c, 0x38, 0x80, 0x16, \ + 0xc1, 0xc3, 0xff, 0xe4, \ + 0xfe, 0x1c, 0x04, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x64, 0xb8, 0x80, 0x6b, \ + 0x80, 0x7b, 0xef, 0xf8, \ + 0x90, 0x25, 0xa1, 0x7b, \ + 0xe1, 0x48, 0x10, 0x46, \ + 0xa0, 0x04, 0x85, 0x91, \ + 0x6e, 0x59, 0x4c, 0x91, \ + 0x81, 0x11, 0xc0, 0x2f, \ + 0x65, 0x69, 0x4c, 0x15, \ + 0x45, 0xc6, 0xc0, 0x0c, \ + 0x88, 0x1e, 0xe1, 0x48, \ + 0x04, 0x46, 0x81, 0x11, \ + 0x70, 0xd8, 0xe0, 0x88, \ + 0xa0, 0x04, 0xef, 0x98, \ + 0xe1, 0x00, 0x03, 0x46, \ + 0x84, 0x2e, 0x83, 0x21, \ + 0xff, 0x11, 0xc2, 0x9e, \ + 0xe0, 0x50, 0x40, 0x15, \ + 0xe0, 0x54, 0x40, 0x15, \ + 0x54, 0xb8, 0x97, 0x01, \ + 0x83, 0x01, 0xe8, 0x08, \ + 0xef, 0xf8, 0x5e, 0xc8, \ + 0xef, 0xec, 0x5f, 0x28, \ + 0x82, 0xa5, 0xa1, 0x0b, \ + 0x4c, 0x01, 0x87, 0x01, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xef, 0xf8, 0x62, 0x68, \ + 0xef, 0xf8, 0x63, 0x48, \ + 0xef, 0xe4, 0x68, 0x68, \ + 0xef, 0xec, 0x5e, 0xc8, \ + 0x49, 0x98, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xe8, 0x00, \ + 0xe8, 0x00, 0x49, 0xe8, \ + 0xe0, 0x00, 0x40, 0x16, \ + 0x6f, 0x21, 0x4c, 0x03, \ + 0x83, 0x25, 0xc0, 0x17, \ + 0xe1, 0x48, 0x41, 0xc6, \ + 0xa3, 0x1b, 0x80, 0x14, \ + 0x4c, 0x11, 0x83, 0x11, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x81, 0x11, 0x80, 0x65, \ + 0x97, 0x01, 0x80, 0x1e, \ + 0xe8, 0x08, 0x51, 0xe8, \ + 0x5b, 0xf8, 0x83, 0x01, \ + 0x65, 0xd8, 0xef, 0xf8, \ + 0x9e, 0xcb, 0xe8, 0x30, \ + 0x5b, 0x18, 0x8c, 0x0b, \ + 0xbe, 0x84, 0xe8, 0x0c, \ + 0x80, 0xe5, 0xa1, 0x8b, \ + 0x65, 0x61, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0xf5, 0xe0, 0x48, \ + 0x75, 0x98, 0x8c, 0x0b, \ + 0x0c, 0x39, 0xe8, 0x08, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x66, 0xc1, 0x00, 0x60, \ + 0x83, 0x11, 0xc0, 0x56, \ + 0x6b, 0xb8, 0x85, 0x21, \ + 0x81, 0x75, 0xef, 0xf4, \ + 0x5b, 0xf8, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xe8, 0x0c, \ + 0xe8, 0x10, 0x62, 0x08, \ + 0xe0, 0x00, 0x0e, 0x2a, \ + 0x5c, 0x78, 0x8c, 0x0b, \ + 0x47, 0x18, 0xef, 0xf8, \ + 0x8c, 0x0b, 0xe8, 0x00, \ + 0x40, 0x01, 0x02, 0xd8, \ + 0x41, 0x78, 0x80, 0x00, \ + 0x8c, 0x0b, 0xef, 0xf8, \ + 0xef, 0xf4, 0x78, 0x48, \ + 0xef, 0xe8, 0x4b, 0xa8, \ + 0x4a, 0x78, 0x8c, 0x0b, \ + 0xa3, 0x8b, 0xe8, 0x00, \ + 0x8c, 0x0b, 0x80, 0x95, \ + 0xe8, 0x0c, 0x55, 0xa8, \ + 0x4e, 0xb8, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xe8, 0x30, \ + 0x8e, 0x2b, 0x8e, 0x1b, \ + 0xef, 0xec, 0x58, 0xe8, \ + 0x9f, 0x21, 0x81, 0x11, \ + 0x8c, 0x0b, 0x83, 0x31, \ + 0xe8, 0x2c, 0x4f, 0x48, \ + 0x4d, 0xd8, 0x8c, 0x0b, \ + 0x5b, 0x38, 0xe8, 0x04, \ + 0x01, 0x46, 0xef, 0xf8, \ + 0x80, 0x04, 0xe1, 0x44, \ + 0x02, 0x14, 0x80, 0x18, \ + 0x80, 0x16, 0xfc, 0x98, \ + 0x4d, 0x18, 0x83, 0x35, \ + 0x8c, 0x0b, 0xe8, 0x30, \ + 0x8e, 0x2b, 0x8e, 0x1b, \ + 0xef, 0xec, 0x57, 0x48, \ + 0x9f, 0x21, 0x81, 0x11, \ + 0x8c, 0x0b, 0x83, 0x31, \ + 0xe8, 0x2c, 0x4d, 0xa8, \ + 0x4c, 0x38, 0x8c, 0x0b, \ + 0x59, 0x98, 0xe8, 0x04, \ + 0x5a, 0x78, 0xef, 0xf8, \ + 0x5f, 0x98, 0xef, 0xf8, \ + 0x83, 0x01, 0xef, 0xe4, \ + 0x48, 0x58, 0x8c, 0x1b, \ + 0x99, 0x01, 0xef, 0xe8, \ + 0xe8, 0x08, 0x4a, 0x68, \ + 0x54, 0x78, 0x85, 0x01, \ + 0x90, 0xf1, 0xef, 0xf8, \ + 0xf6, 0x1c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x04, 0x86, 0x81, 0x11, \ + 0x24, 0xd8, 0xec, 0x7c, \ + 0x00, 0x0c, 0xff, 0x98, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0x60, 0x01, 0x02, 0x06, \ + 0x40, 0x15, 0xc0, 0x7d, \ + 0xc0, 0x0b, 0x6d, 0x8d, \ + 0x45, 0x5e, 0x83, 0x11, \ + 0xcd, 0x26, 0x70, 0x41, \ + 0x60, 0x51, 0x40, 0x11, \ + 0x40, 0x25, 0xc0, 0x31, \ + 0xc0, 0x0c, 0x64, 0x0d, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe1, 0x1c, 0x40, 0x46, \ + 0x81, 0x11, 0x80, 0x04, \ + 0x40, 0x86, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x1c, \ + 0x40, 0xc6, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x1c, \ + 0x41, 0x06, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x1c, \ + 0x41, 0x46, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x1c, \ + 0x41, 0x86, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x1c, \ + 0x41, 0xc6, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x1c, \ + 0x00, 0x06, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x00, 0x46, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x00, 0x86, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x00, 0xc6, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x01, 0x06, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x01, 0x46, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x01, 0x86, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x01, 0xc6, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x40, 0x06, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xe1, 0x44, 0x03, 0xc6, \ + 0x00, 0x09, 0x84, 0x14, \ + 0xc0, 0x15, 0x6a, 0x51, \ + 0xc4, 0x07, 0x84, 0x28, \ + 0x44, 0x20, 0x88, 0x09, \ + 0xa0, 0x2f, 0xf3, 0xec, \ + 0x00, 0x06, 0x84, 0x26, \ + 0x80, 0x04, 0xe1, 0x34, \ + 0x80, 0x16, 0x83, 0x11, \ + 0xe1, 0x30, 0x41, 0xc6, \ + 0x80, 0x16, 0x80, 0x04, \ + 0xe1, 0x44, 0x40, 0x86, \ + 0x81, 0x21, 0x80, 0x04, \ + 0x80, 0x16, 0x80, 0x26, \ + 0x41, 0x86, 0x80, 0x26, \ + 0x80, 0x04, 0xe1, 0x30, \ + 0x40, 0x06, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x44, \ + 0xe0, 0x00, 0x43, 0x06, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0x64, 0x01, 0x00, 0x15, \ + 0x00, 0x25, 0xc0, 0x05, \ + 0xc0, 0x05, 0x64, 0x05, \ + 0x64, 0x09, 0x00, 0x35, \ + 0x80, 0x1e, 0xc0, 0x05, \ + 0xc0, 0x3e, 0x82, 0x2e, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0xf4, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x84, 0x07, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x81, 0x61, 0xc1, 0x83, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x66, 0xb8, 0x81, 0x11, \ + 0x00, 0x46, 0xef, 0x98, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0xe0, 0x58, 0x00, 0x15, \ + 0x40, 0x15, 0x82, 0x11, \ + 0x81, 0x01, 0xe0, 0x58, \ + 0xe8, 0x04, 0x7e, 0x48, \ + 0xe1, 0x48, 0x0e, 0x06, \ + 0xe1, 0x34, 0x41, 0x86, \ + 0x80, 0x04, 0x9c, 0x14, \ + 0x83, 0x31, 0xa1, 0x21, \ + 0x6e, 0x59, 0x42, 0x21, \ + 0x80, 0x36, 0xc0, 0x2f, \ + 0x41, 0xf6, 0x80, 0x66, \ + 0x83, 0x11, 0xe0, 0x3c, \ + 0xef, 0x98, 0x64, 0xe8, \ + 0x70, 0x78, 0x9c, 0x04, \ + 0xa8, 0xf1, 0xe8, 0x10, \ + 0xf0, 0x04, 0x1f, 0x0c, \ + 0xf6, 0x84, 0x0f, 0xfc, \ + 0xfd, 0x64, 0xbc, 0xf4, \ + 0xc1, 0xc3, 0xc1, 0x53, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0xf4, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x84, 0x07, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x81, 0x61, 0xc1, 0x83, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x62, 0xb8, 0x81, 0x11, \ + 0x00, 0x46, 0xef, 0x98, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0xe0, 0x5c, 0x00, 0x15, \ + 0x40, 0x15, 0x82, 0x11, \ + 0x83, 0x71, 0xe0, 0x5c, \ + 0x7a, 0x38, 0x83, 0x01, \ + 0x00, 0x06, 0xe8, 0x04, \ + 0x02, 0x06, 0xe1, 0x48, \ + 0x80, 0x04, 0xe1, 0x38, \ + 0xa3, 0x21, 0x84, 0x14, \ + 0x6e, 0x59, 0x40, 0x21, \ + 0x84, 0x76, 0xc0, 0x2f, \ + 0x43, 0x06, 0x84, 0x66, \ + 0x84, 0x14, 0xe1, 0x00, \ + 0x42, 0x4a, 0x84, 0x18, \ + 0x80, 0x65, 0xe0, 0x80, \ + 0xe8, 0x10, 0x4b, 0x68, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x60, 0x58, 0x83, 0x11, \ + 0xa8, 0xf1, 0xef, 0x98, \ + 0xf0, 0x04, 0x1f, 0x0c, \ + 0xf6, 0x84, 0x0f, 0xfc, \ + 0xfd, 0x64, 0xbc, 0xf4, \ + 0x00, 0x0c, 0xc1, 0x53, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0xf4, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x80, 0x07, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x41, 0xf6, 0xc1, 0x83, \ + 0x81, 0x11, 0xe0, 0x3c, \ + 0xef, 0x98, 0x5e, 0x48, \ + 0xe1, 0x48, 0x00, 0x46, \ + 0x00, 0x15, 0x80, 0x04, \ + 0x82, 0x11, 0xe0, 0x60, \ + 0xe0, 0x60, 0x40, 0x15, \ + 0x75, 0xd8, 0x85, 0x01, \ + 0x00, 0x06, 0xe8, 0x04, \ + 0x80, 0x14, 0xe1, 0x48, \ + 0xe1, 0x38, 0x0c, 0xc6, \ + 0xa5, 0x31, 0x98, 0x24, \ + 0x6e, 0x59, 0x42, 0x31, \ + 0x88, 0x18, 0xc0, 0x2f, \ + 0xe0, 0x80, 0x42, 0x0a, \ + 0x02, 0x86, 0x82, 0xe5, \ + 0x84, 0x24, 0xe1, 0x38, \ + 0xc2, 0x39, 0x88, 0x38, \ + 0x84, 0x14, 0x88, 0x36, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x84, 0x26, 0xf3, 0xf8, \ + 0x7c, 0x18, 0x80, 0x04, \ + 0x98, 0x04, 0xe8, 0x2c, \ + 0x40, 0x0a, 0x80, 0x08, \ + 0x82, 0x85, 0xe0, 0x84, \ + 0xe1, 0x38, 0x00, 0x86, \ + 0x84, 0x28, 0x80, 0x14, \ + 0x84, 0x26, 0xc4, 0x29, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xf4, 0x42, 0x10, \ + 0x41, 0xf6, 0x80, 0x16, \ + 0x83, 0x11, 0xe0, 0x3c, \ + 0xef, 0x98, 0x5a, 0x88, \ + 0x1f, 0x0c, 0x90, 0xf1, \ + 0x0f, 0xfc, 0xf0, 0x04, \ + 0xbc, 0xf4, 0xf6, 0x80, \ + 0xc1, 0x53, 0xfd, 0x64, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0xf4, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x80, 0x07, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x41, 0xf6, 0xc1, 0x83, \ + 0x81, 0x11, 0xe0, 0x3c, \ + 0xef, 0x98, 0x58, 0x88, \ + 0xe1, 0x48, 0x00, 0x46, \ + 0x00, 0x15, 0x80, 0x04, \ + 0x82, 0x11, 0xe0, 0x64, \ + 0xe0, 0x64, 0x40, 0x15, \ + 0x70, 0x18, 0x87, 0x01, \ + 0x00, 0x06, 0xe8, 0x04, \ + 0x80, 0x14, 0xe1, 0x48, \ + 0xe1, 0x38, 0x01, 0xc6, \ + 0xa7, 0x31, 0x80, 0x24, \ + 0x6e, 0x59, 0x42, 0x31, \ + 0x88, 0x18, 0xc0, 0x2f, \ + 0xe0, 0x80, 0x42, 0x0a, \ + 0x03, 0x86, 0x82, 0x85, \ + 0x84, 0x24, 0xe1, 0x38, \ + 0xc2, 0x39, 0x88, 0x38, \ + 0x84, 0x14, 0x88, 0x36, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x84, 0x26, 0xf3, 0xf8, \ + 0x80, 0x08, 0x80, 0x04, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x01, 0x86, 0x82, 0x85, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0xc4, 0x29, 0x84, 0x28, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xf4, \ + 0xe1, 0x38, 0x01, 0xc6, \ + 0x80, 0x08, 0x80, 0x04, \ + 0xe0, 0x88, 0x40, 0x0a, \ + 0x01, 0x86, 0x8a, 0x65, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0xc8, 0x29, 0x84, 0x28, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xec, \ + 0xe1, 0x38, 0x01, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xec, 0x42, 0x10, \ + 0x0c, 0x06, 0x80, 0x16, \ + 0x98, 0x04, 0xe1, 0x48, \ + 0xe8, 0x30, 0x42, 0x28, \ + 0x00, 0x19, 0x98, 0x04, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0xe1, 0x00, 0x45, 0x06, \ + 0xd0, 0x19, 0x88, 0x24, \ + 0x6e, 0x51, 0x40, 0x11, \ + 0x88, 0x18, 0xc0, 0x2f, \ + 0xe0, 0x80, 0x42, 0x4a, \ + 0x7d, 0x18, 0x80, 0x65, \ + 0x4f, 0x78, 0xe8, 0x0c, \ + 0x41, 0xf6, 0xef, 0xe0, \ + 0x83, 0x11, 0xe0, 0x3c, \ + 0xef, 0x98, 0x51, 0xe8, \ + 0x1f, 0x0c, 0x90, 0xf1, \ + 0x0f, 0xfc, 0xf0, 0x04, \ + 0xbc, 0xf4, 0xf6, 0x80, \ + 0xc1, 0x53, 0xfd, 0x64, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0xf4, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x80, 0x07, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x41, 0xf6, 0xc1, 0x83, \ + 0x81, 0x11, 0xe0, 0x3c, \ + 0xef, 0x98, 0x50, 0x08, \ + 0xe1, 0x48, 0x00, 0x46, \ + 0x00, 0x15, 0x80, 0x04, \ + 0x82, 0x11, 0xe0, 0x68, \ + 0xe0, 0x68, 0x40, 0x15, \ + 0x67, 0x98, 0x89, 0x01, \ + 0x00, 0x06, 0xe8, 0x04, \ + 0x80, 0x14, 0xe1, 0x48, \ + 0xe1, 0x38, 0x4c, 0x86, \ + 0xa9, 0x31, 0x98, 0x24, \ + 0x6e, 0x59, 0x42, 0x31, \ + 0x88, 0x18, 0xc0, 0x2f, \ + 0xe0, 0x80, 0x42, 0x0a, \ + 0x42, 0x46, 0x84, 0x05, \ + 0x84, 0x24, 0xe1, 0x38, \ + 0xc2, 0x39, 0x88, 0x38, \ + 0x84, 0x14, 0x88, 0x36, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x84, 0x26, 0xf3, 0xf8, \ + 0x8f, 0x11, 0x80, 0x04, \ + 0xe8, 0x00, 0x7e, 0x08, \ + 0x80, 0x08, 0x98, 0x04, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x40, 0x46, 0x84, 0x45, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0xc4, 0x29, 0x84, 0x28, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xf4, \ + 0xe1, 0x48, 0x00, 0x06, \ + 0x91, 0x11, 0x80, 0x04, \ + 0xe8, 0x00, 0x7c, 0xa8, \ + 0xe1, 0x38, 0x4c, 0x86, \ + 0x80, 0x08, 0x98, 0x04, \ + 0xe0, 0x88, 0x40, 0x0a, \ + 0x40, 0x46, 0x84, 0x45, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0xc8, 0x29, 0x84, 0x28, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xec, \ + 0xe1, 0x48, 0x00, 0x06, \ + 0x93, 0x11, 0x80, 0x04, \ + 0xe8, 0x00, 0x7b, 0x28, \ + 0x80, 0x08, 0x98, 0x04, \ + 0xe0, 0x8c, 0x40, 0x0a, \ + 0x40, 0x46, 0x84, 0x45, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0xd0, 0x29, 0x84, 0x28, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xdc, \ + 0xe1, 0x48, 0x00, 0x06, \ + 0x95, 0x11, 0x80, 0x04, \ + 0xe8, 0x00, 0x79, 0xc8, \ + 0xe1, 0x38, 0x4c, 0x86, \ + 0x80, 0x08, 0x98, 0x04, \ + 0xe0, 0x90, 0x40, 0x0a, \ + 0x40, 0x46, 0x84, 0x45, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0xe0, 0x29, 0x84, 0x28, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xbc, \ + 0xe1, 0x48, 0x00, 0x06, \ + 0x97, 0x11, 0x80, 0x04, \ + 0xe8, 0x00, 0x78, 0x48, \ + 0x80, 0x08, 0x98, 0x04, \ + 0xe0, 0x94, 0x40, 0x0a, \ + 0x40, 0x46, 0x84, 0x65, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0x04, 0x24, 0x84, 0x28, \ + 0x84, 0x26, 0xfc, 0x94, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0x7c, 0x42, 0x10, \ + 0x00, 0x06, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0x76, 0xd8, 0xab, 0x11, \ + 0x4c, 0x86, 0xe8, 0x00, \ + 0x98, 0x04, 0xe1, 0x38, \ + 0x40, 0x0a, 0x80, 0x08, \ + 0x84, 0x65, 0xe0, 0x98, \ + 0xe1, 0x38, 0x40, 0x46, \ + 0x84, 0x28, 0x80, 0x14, \ + 0xfc, 0x98, 0x04, 0x24, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf2, 0xfc, \ + 0xe1, 0x48, 0x00, 0x06, \ + 0x99, 0x11, 0x80, 0x04, \ + 0xe8, 0x00, 0x75, 0x48, \ + 0x80, 0x00, 0x98, 0x04, \ + 0x82, 0x0d, 0xff, 0x11, \ + 0x40, 0x46, 0x84, 0x55, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0x04, 0x24, 0x84, 0x28, \ + 0x84, 0x26, 0xfc, 0x9c, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf1, 0xfc, 0x42, 0x10, \ + 0x00, 0x06, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0xe8, 0x00, 0x74, 0xa8, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x43, 0x18, 0x83, 0x11, \ + 0x90, 0xf1, 0xef, 0x98, \ + 0xf0, 0x04, 0x1f, 0x0c, \ + 0xf6, 0x80, 0x0f, 0xfc, \ + 0xfd, 0x64, 0xbc, 0xf4, \ + 0x00, 0x0c, 0xc1, 0x53, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0xf4, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x84, 0x07, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x81, 0x71, 0xc1, 0x83, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x40, 0xf8, 0x81, 0x11, \ + 0x00, 0x46, 0xef, 0x98, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0xe0, 0x6c, 0x00, 0x15, \ + 0x40, 0x15, 0x82, 0x11, \ + 0x8b, 0x01, 0xe0, 0x6c, \ + 0xe8, 0x04, 0x58, 0x88, \ + 0xe1, 0x48, 0x0c, 0x06, \ + 0xe1, 0x38, 0x43, 0xc6, \ + 0x84, 0x14, 0x98, 0x04, \ + 0x83, 0x31, 0xab, 0x21, \ + 0x6e, 0x59, 0x40, 0x21, \ + 0x84, 0x36, 0xc0, 0x2f, \ + 0x43, 0x06, 0x84, 0x76, \ + 0x84, 0x14, 0xe1, 0x00, \ + 0x42, 0x4a, 0x84, 0x18, \ + 0x80, 0x65, 0xe0, 0x80, \ + 0xe8, 0x0c, 0x69, 0xa8, \ + 0x57, 0xb8, 0x98, 0x04, \ + 0x41, 0xf6, 0xef, 0xe4, \ + 0x83, 0x11, 0xe0, 0x3c, \ + 0xef, 0x94, 0x7e, 0x68, \ + 0x1f, 0x0c, 0xa8, 0xf1, \ + 0x0f, 0xfc, 0xf0, 0x04, \ + 0xbc, 0xf4, 0xf6, 0x84, \ + 0xc1, 0x53, 0xfd, 0x64, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0xf4, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x84, 0x07, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x81, 0x61, 0xc1, 0x83, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x7c, 0x78, 0x81, 0x11, \ + 0x00, 0x46, 0xef, 0x94, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0xe0, 0x70, 0x00, 0x15, \ + 0x40, 0x15, 0x82, 0x11, \ + 0x8d, 0x01, 0xe0, 0x70, \ + 0xe8, 0x04, 0x54, 0x08, \ + 0xe1, 0x48, 0x00, 0x06, \ + 0xad, 0x01, 0x80, 0x14, \ + 0x6e, 0x59, 0x42, 0x01, \ + 0x83, 0x71, 0xc0, 0x2f, \ + 0x5b, 0xd8, 0x83, 0x01, \ + 0x01, 0x46, 0xef, 0xe8, \ + 0x80, 0x04, 0xe1, 0x3c, \ + 0x80, 0x66, 0x80, 0x76, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x7a, 0x78, 0x83, 0x11, \ + 0xa8, 0xf1, 0xef, 0x94, \ + 0xf0, 0x04, 0x1f, 0x0c, \ + 0xf6, 0x84, 0x0f, 0xfc, \ + 0xfd, 0x64, 0xbc, 0xf4, \ + 0xc1, 0xc3, 0xc1, 0x53, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0xf4, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x80, 0x07, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x41, 0xf6, 0xc1, 0x83, \ + 0x81, 0x11, 0xe0, 0x3c, \ + 0xef, 0x94, 0x78, 0x88, \ + 0xe1, 0x48, 0x00, 0x46, \ + 0x00, 0x15, 0x80, 0x04, \ + 0x82, 0x11, 0xe0, 0x74, \ + 0xe0, 0x74, 0x40, 0x15, \ + 0x50, 0x18, 0x8f, 0x01, \ + 0x0c, 0x06, 0xe8, 0x04, \ + 0x98, 0x14, 0xe1, 0x48, \ + 0x42, 0x01, 0xaf, 0x01, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x57, 0xf8, 0x81, 0x01, \ + 0x98, 0x04, 0xef, 0xe8, \ + 0x65, 0x39, 0x00, 0x19, \ + 0xc4, 0x19, 0xc0, 0x31, \ + 0x82, 0x35, 0xa5, 0x1b, \ + 0x64, 0xe1, 0x00, 0x19, \ + 0x42, 0x10, 0xc0, 0x31, \ + 0x40, 0x11, 0xf3, 0xec, \ + 0xc0, 0x31, 0x64, 0xe1, \ + 0x6e, 0x51, 0x00, 0x19, \ + 0x42, 0x0a, 0xc0, 0x2f, \ + 0x86, 0x05, 0xe0, 0x94, \ + 0xe1, 0x00, 0x43, 0x06, \ + 0x84, 0x18, 0x84, 0x14, \ + 0xe0, 0x80, 0x42, 0x4a, \ + 0x60, 0x18, 0x80, 0x65, \ + 0x0c, 0x06, 0xe8, 0x0c, \ + 0x98, 0x04, 0xe1, 0x48, \ + 0xef, 0xe8, 0x51, 0x48, \ + 0x00, 0x19, 0x98, 0x04, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0xf3, 0x7c, 0x42, 0x10, \ + 0x6e, 0x51, 0x40, 0x11, \ + 0x00, 0x06, 0xc0, 0x2f, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0x67, 0x4d, 0x00, 0x19, \ + 0x44, 0xc6, 0xc0, 0x31, \ + 0x88, 0x24, 0xe1, 0x3c, \ + 0x40, 0x11, 0xc8, 0x19, \ + 0xc0, 0x31, 0x67, 0x4d, \ + 0x88, 0x06, 0x83, 0x01, \ + 0x88, 0x06, 0x81, 0x01, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x73, 0x18, 0x83, 0x11, \ + 0x90, 0xf1, 0xef, 0x94, \ + 0xf0, 0x04, 0x1f, 0x0c, \ + 0xf6, 0x80, 0x0f, 0xfc, \ + 0xfd, 0x64, 0xbc, 0xf4, \ + 0x00, 0x0c, 0xc1, 0x53, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0xf4, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x80, 0x07, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x81, 0x61, 0xc1, 0x83, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x70, 0xf8, 0x81, 0x11, \ + 0x00, 0x46, 0xef, 0x94, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0xe0, 0x78, 0x00, 0x15, \ + 0x40, 0x15, 0x82, 0x11, \ + 0x91, 0x01, 0xe0, 0x78, \ + 0xe8, 0x04, 0x48, 0x88, \ + 0xe1, 0x48, 0x00, 0x06, \ + 0xe1, 0x40, 0x02, 0x46, \ + 0x84, 0x14, 0x80, 0x04, \ + 0x83, 0x31, 0xb1, 0x21, \ + 0x6e, 0x59, 0x40, 0x21, \ + 0x84, 0x36, 0xc0, 0x2f, \ + 0x41, 0xf6, 0x84, 0x66, \ + 0x83, 0x11, 0xe0, 0x3c, \ + 0xef, 0x94, 0x6f, 0x28, \ + 0x1f, 0x0c, 0x90, 0xf1, \ + 0x0f, 0xfc, 0xf0, 0x04, \ + 0xbc, 0xf4, 0xf6, 0x80, \ + 0xc1, 0x53, 0xfd, 0x64, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0xf4, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x80, 0x07, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x81, 0x61, 0xc1, 0x83, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x6d, 0x38, 0x81, 0x11, \ + 0x00, 0x46, 0xef, 0x94, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0xe0, 0x7c, 0x00, 0x15, \ + 0x40, 0x15, 0x82, 0x11, \ + 0x93, 0x01, 0xe0, 0x7c, \ + 0xe8, 0x04, 0x44, 0xc8, \ + 0xe1, 0x48, 0x00, 0x06, \ + 0xb3, 0x11, 0x80, 0x04, \ + 0x6e, 0x59, 0x40, 0x11, \ + 0xfe, 0x6e, 0xc0, 0x2f, \ + 0xe1, 0x28, 0x40, 0x06, \ + 0x80, 0x04, 0x80, 0x04, \ + 0x00, 0x46, 0xfe, 0x0e, \ + 0x80, 0x04, 0xe1, 0x34, \ + 0xa2, 0x0d, 0x80, 0x08, \ + 0xe1, 0x4c, 0x02, 0xc6, \ + 0x82, 0x15, 0x84, 0x06, \ + 0x82, 0xa5, 0xa1, 0x0b, \ + 0x84, 0x95, 0xa3, 0x0b, \ + 0xc3, 0x13, 0xc1, 0x83, \ + 0xa5, 0x0b, 0x81, 0xd5, \ + 0xa7, 0x0b, 0x82, 0x25, \ + 0xc1, 0x83, 0x82, 0xb5, \ + 0x81, 0x65, 0xc7, 0x13, \ + 0xc1, 0x13, 0xc1, 0x83, \ + 0xc1, 0x83, 0x81, 0x35, \ + 0x00, 0xc6, 0xc5, 0x13, \ + 0x81, 0x11, 0xe1, 0x4c, \ + 0x01, 0xc6, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0x80, 0x16, 0x83, 0x11, \ + 0x80, 0x16, 0x81, 0x11, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x68, 0xf8, 0x83, 0x11, \ + 0xa0, 0xf1, 0xef, 0x94, \ + 0xf0, 0x04, 0x1f, 0x0c, \ + 0xf6, 0x80, 0x0f, 0xfc, \ + 0xfd, 0x64, 0xbc, 0xf4, \ + 0xc1, 0xc3, 0xc1, 0x53, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x82, 0x25, 0xa1, 0x1b, \ + 0x67, 0xf8, 0x83, 0x11, \ + 0x40, 0x06, 0xef, 0x94, \ + 0x0c, 0x0c, 0xfa, 0x02, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0x02, 0x4c, 0x06, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x67, 0x38, 0x81, 0x11, \ + 0x8c, 0x0b, 0xef, 0x94, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0xec, 0xa0, 0x1e, 0xf0, \ + 0x00, 0x0e, 0x80, 0x6b, \ + 0xc0, 0x10, 0x6d, 0x2d, \ + 0x6d, 0x35, 0x02, 0x0e, \ + 0xbc, 0x0e, 0xc0, 0x10, \ + 0x01, 0x0e, 0xbe, 0x1e, \ + 0xc0, 0x10, 0x6d, 0x3d, \ + 0x6d, 0x49, 0x02, 0x8e, \ + 0xfc, 0x0e, 0xc0, 0x10, \ + 0x01, 0x8e, 0xfe, 0x1e, \ + 0xc0, 0x10, 0x6d, 0x59, \ + 0x6d, 0x75, 0x43, 0x8e, \ + 0x89, 0x0e, 0xc0, 0x10, \ + 0x00, 0x8e, 0x8b, 0x1e, \ + 0xc0, 0x10, 0x6d, 0x81, \ + 0x6d, 0x89, 0x02, 0x8e, \ + 0x8d, 0x0e, 0xc0, 0x10, \ + 0x41, 0x8e, 0x8f, 0x1e, \ + 0xc0, 0x10, 0x6d, 0x95, \ + 0x6d, 0x9d, 0x43, 0x0e, \ + 0x91, 0x0e, 0xc0, 0x10, \ + 0x10, 0x60, 0x93, 0x1e, \ + 0xc0, 0xc5, 0x65, 0xc5, \ + 0x65, 0xc9, 0x12, 0x60, \ + 0x81, 0x71, 0xc0, 0xc5, \ + 0x00, 0x74, 0x9e, 0xab, \ + 0xa8, 0x0d, 0xfc, 0x7c, \ + 0x02, 0x00, 0x82, 0x05, \ + 0xe9, 0x1d, 0xec, 0x18, \ + 0x98, 0x81, 0x84, 0x45, \ + 0x83, 0x65, 0x98, 0x91, \ + 0xec, 0x04, 0x02, 0x00, \ + 0x82, 0x15, 0xe5, 0x1d, \ + 0x82, 0xc5, 0xa7, 0x0b, \ + 0x82, 0xd5, 0xa9, 0x0b, \ + 0x8e, 0x91, 0x84, 0x81, \ + 0x88, 0x81, 0x81, 0xb5, \ + 0x81, 0x85, 0x88, 0x91, \ + 0x80, 0xd5, 0xab, 0x0b, \ + 0x9c, 0x91, 0xa0, 0x81, \ + 0x8c, 0x81, 0x81, 0x35, \ + 0xa4, 0x38, 0x84, 0x91, \ + 0xa0, 0x28, 0xa8, 0x04, \ + 0xe1, 0x84, 0x08, 0x34, \ + 0x82, 0x49, 0x82, 0x39, \ + 0x61, 0x78, 0x8e, 0x1b, \ + 0xa1, 0x0b, 0xef, 0x94, \ + 0xc0, 0x00, 0x10, 0x38, \ + 0xf2, 0x7d, 0x82, 0x71, \ + 0xf4, 0x05, 0x88, 0xa1, \ + 0x63, 0xf8, 0x81, 0x01, \ + 0x41, 0xf6, 0xef, 0x94, \ + 0x83, 0x11, 0xe0, 0x3c, \ + 0xef, 0x94, 0x60, 0x08, \ + 0x65, 0xc9, 0x0c, 0x09, \ + 0x43, 0x46, 0xc0, 0x31, \ + 0x84, 0x14, 0xe1, 0x34, \ + 0x82, 0x09, 0xc7, 0x07, \ + 0x0c, 0x09, 0x84, 0x06, \ + 0xc0, 0x31, 0x65, 0xd9, \ + 0xe1, 0x34, 0x43, 0xc6, \ + 0xc7, 0x07, 0x84, 0x14, \ + 0x84, 0x06, 0x82, 0x09, \ + 0xe1, 0x38, 0x00, 0x46, \ + 0x0c, 0x29, 0x80, 0x14, \ + 0xc0, 0x31, 0x65, 0xe9, \ + 0xc9, 0x27, 0x84, 0x38, \ + 0x46, 0x30, 0x82, 0x29, \ + 0xa4, 0x3f, 0xf3, 0xf8, \ + 0x80, 0x04, 0x84, 0x36, \ + 0x65, 0xe9, 0x0c, 0x19, \ + 0x80, 0x28, 0xc0, 0x31, \ + 0x84, 0x19, 0xc9, 0x17, \ + 0xf3, 0xf4, 0x44, 0x20, \ + 0x80, 0x26, 0xa2, 0x2f, \ + 0xe1, 0x38, 0x01, 0x46, \ + 0x0c, 0x2b, 0x80, 0x14, \ + 0xc0, 0x18, 0x6a, 0xf9, \ + 0xcb, 0x27, 0x84, 0x38, \ + 0x46, 0x30, 0x82, 0x29, \ + 0xa4, 0x3f, 0xf3, 0xf8, \ + 0x80, 0x14, 0x84, 0x36, \ + 0x6a, 0xf9, 0x0c, 0x2b, \ + 0x84, 0x38, 0xc0, 0x18, \ + 0x84, 0x29, 0xcb, 0x27, \ + 0xf3, 0xf4, 0x46, 0x30, \ + 0x84, 0x36, 0xa4, 0x3f, \ + 0x0c, 0x1b, 0x80, 0x04, \ + 0xc0, 0x18, 0x6a, 0xf9, \ + 0xcb, 0x17, 0x80, 0x28, \ + 0x44, 0x20, 0x88, 0x19, \ + 0xa2, 0x2f, 0xf3, 0xec, \ + 0x40, 0x06, 0x80, 0x26, \ + 0x0c, 0x19, 0xe1, 0x38, \ + 0xc0, 0x31, 0x66, 0x09, \ + 0x80, 0x16, 0x80, 0x04, \ + 0xe1, 0x38, 0x40, 0xc6, \ + 0x65, 0x89, 0x0c, 0x15, \ + 0x80, 0x04, 0xc0, 0x0c, \ + 0x41, 0x06, 0x80, 0x1e, \ + 0x0c, 0x15, 0xe1, 0x38, \ + 0xc0, 0x0c, 0x65, 0x8d, \ + 0x80, 0x1e, 0x80, 0x04, \ + 0x66, 0x49, 0x0c, 0x09, \ + 0x43, 0x46, 0xc0, 0x31, \ + 0x84, 0x14, 0xe1, 0x38, \ + 0x82, 0x09, 0xc5, 0x07, \ + 0x0c, 0x09, 0x84, 0x06, \ + 0xc0, 0x31, 0x66, 0x49, \ + 0xe1, 0x38, 0x43, 0x86, \ + 0xc7, 0x07, 0x84, 0x14, \ + 0x84, 0x06, 0x82, 0x09, \ + 0xe1, 0x3c, 0x00, 0x46, \ + 0x65, 0x95, 0x0c, 0x15, \ + 0x80, 0x04, 0xc0, 0x0c, \ + 0x00, 0x86, 0x80, 0x1e, \ + 0x0c, 0x15, 0xe1, 0x3c, \ + 0xc0, 0x0c, 0x65, 0x99, \ + 0x80, 0x1e, 0x80, 0x04, \ + 0x66, 0x79, 0x0c, 0x09, \ + 0x02, 0xc6, 0xc0, 0x31, \ + 0x84, 0x14, 0xe1, 0x3c, \ + 0x82, 0x09, 0xc5, 0x07, \ + 0x0c, 0x09, 0x84, 0x06, \ + 0xc0, 0x31, 0x66, 0x79, \ + 0xe1, 0x3c, 0x03, 0x06, \ + 0xc7, 0x07, 0x84, 0x14, \ + 0x84, 0x06, 0x82, 0x09, \ + 0xe1, 0x3c, 0x01, 0xc6, \ + 0x65, 0xa1, 0x0c, 0x15, \ + 0x80, 0x04, 0xc0, 0x0c, \ + 0x40, 0x06, 0x80, 0x1e, \ + 0x0c, 0x15, 0xe1, 0x3c, \ + 0xc0, 0x0c, 0x65, 0xa5, \ + 0x80, 0x1e, 0x80, 0x04, \ + 0x66, 0xa9, 0x0c, 0x09, \ + 0x42, 0x46, 0xc0, 0x31, \ + 0x84, 0x14, 0xe1, 0x3c, \ + 0x82, 0x09, 0xc5, 0x07, \ + 0x0c, 0x09, 0x84, 0x06, \ + 0xc0, 0x31, 0x66, 0xa9, \ + 0xe1, 0x3c, 0x42, 0x86, \ + 0xc7, 0x07, 0x84, 0x14, \ + 0x84, 0x06, 0x82, 0x09, \ + 0xe1, 0x3c, 0x41, 0x46, \ + 0x65, 0xad, 0x0c, 0x15, \ + 0x80, 0x04, 0xc0, 0x0c, \ + 0x41, 0x86, 0x80, 0x1e, \ + 0x0c, 0x15, 0xe1, 0x3c, \ + 0xc0, 0x0c, 0x65, 0xb1, \ + 0x80, 0x1e, 0x80, 0x04, \ + 0x66, 0xd9, 0x0c, 0x09, \ + 0x43, 0xc6, 0xc0, 0x31, \ + 0x84, 0x14, 0xe1, 0x3c, \ + 0x82, 0x09, 0xc5, 0x07, \ + 0x0c, 0x09, 0x84, 0x06, \ + 0xc0, 0x31, 0x66, 0xd9, \ + 0xe1, 0x40, 0x02, 0x06, \ + 0xc7, 0x07, 0x84, 0x14, \ + 0x84, 0x06, 0x82, 0x09, \ + 0xe1, 0x40, 0x00, 0xc6, \ + 0x65, 0xb9, 0x0c, 0x15, \ + 0x80, 0x04, 0xc0, 0x0c, \ + 0x01, 0x06, 0x80, 0x1e, \ + 0x0c, 0x15, 0xe1, 0x40, \ + 0xc0, 0x0c, 0x65, 0xbd, \ + 0x80, 0x1e, 0x80, 0x04, \ + 0x67, 0x09, 0x0c, 0x09, \ + 0x03, 0x46, 0xc0, 0x31, \ + 0x84, 0x14, 0xe1, 0x40, \ + 0x82, 0x09, 0xc5, 0x07, \ + 0x0c, 0x09, 0x84, 0x06, \ + 0xc0, 0x31, 0x67, 0x09, \ + 0xe1, 0x40, 0x03, 0x86, \ + 0xc7, 0x07, 0x84, 0x14, \ + 0x84, 0x06, 0x82, 0x09, \ + 0x0c, 0x39, 0x81, 0xd5, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x66, 0xc1, 0x00, 0x60, \ + 0x81, 0x11, 0xc0, 0x56, \ + 0x42, 0xd8, 0x83, 0x21, \ + 0x44, 0xb8, 0xef, 0xf0, \ + 0xd0, 0xf1, 0xef, 0xf0, \ + 0xf2, 0x3c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x6e, 0x59, 0x00, 0x39, \ + 0x00, 0x00, 0xc0, 0x2f, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xfb, 0x00, 0x04, 0x14, \ + 0x02, 0xf8, 0x85, 0x11, \ + 0xc1, 0xc3, 0xff, 0xf0, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x31, 0x65, 0x75, \ + 0x65, 0x61, 0x0c, 0x19, \ + 0xc3, 0x0d, 0xc0, 0x31, \ + 0x0c, 0x09, 0x80, 0xe5, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0xe0, 0x80, 0x40, 0x4a, \ + 0x0c, 0x39, 0x8a, 0xe5, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x66, 0xc1, 0x00, 0x60, \ + 0x85, 0x11, 0xc0, 0x56, \ + 0x7f, 0xd8, 0x9b, 0x21, \ + 0x01, 0x46, 0xef, 0xec, \ + 0x80, 0x14, 0xe1, 0x44, \ + 0xe0, 0x29, 0x84, 0x28, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xdc, \ + 0xe1, 0x44, 0x01, 0x06, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xd0, 0x19, \ + 0x8c, 0x1b, 0x81, 0x01, \ + 0xef, 0xe0, 0x61, 0xc8, \ + 0xef, 0xdc, 0x78, 0xa8, \ + 0x8c, 0x0b, 0x83, 0x11, \ + 0xef, 0xf4, 0x41, 0x28, \ + 0xe0, 0x00, 0x41, 0x36, \ + 0xe8, 0x00, 0x63, 0x68, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0x01, 0x58, 0xf2, 0x04, \ + 0x0c, 0x39, 0xff, 0xf0, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x66, 0xf1, 0x00, 0x60, \ + 0x85, 0x11, 0xc0, 0x56, \ + 0x4b, 0x78, 0x9b, 0x21, \ + 0x41, 0xc6, 0xef, 0xf0, \ + 0x80, 0x14, 0xe1, 0x44, \ + 0xd0, 0x29, 0x86, 0x28, \ + 0x80, 0x14, 0x86, 0x26, \ + 0xe0, 0x29, 0x86, 0x28, \ + 0x80, 0x14, 0x86, 0x26, \ + 0xe1, 0x44, 0x05, 0x46, \ + 0x88, 0x34, 0x86, 0x18, \ + 0xc2, 0x17, 0x8c, 0x48, \ + 0x48, 0x40, 0xa0, 0x19, \ + 0xa2, 0x4f, 0xf3, 0xbc, \ + 0x80, 0x04, 0x8c, 0x46, \ + 0x88, 0x14, 0x82, 0x08, \ + 0xc3, 0x07, 0x84, 0x28, \ + 0x44, 0x20, 0x90, 0x09, \ + 0xa0, 0x2f, 0xf3, 0xdc, \ + 0x81, 0x71, 0x84, 0x26, \ + 0x8c, 0x1b, 0x81, 0x01, \ + 0xef, 0xe0, 0x5d, 0xe8, \ + 0xef, 0xdc, 0x74, 0xc8, \ + 0x8c, 0x0b, 0x81, 0x11, \ + 0xef, 0xf0, 0x7d, 0x48, \ + 0x65, 0xd5, 0x0c, 0x09, \ + 0x0c, 0x19, 0xc0, 0x2f, \ + 0xc0, 0x31, 0x65, 0x71, \ + 0x65, 0x75, 0x0c, 0x29, \ + 0xa7, 0x0b, 0xc0, 0x31, \ + 0x82, 0x21, 0xc2, 0x19, \ + 0x65, 0x71, 0x4c, 0x11, \ + 0x4c, 0x21, 0xc0, 0x31, \ + 0xc0, 0x31, 0x65, 0x75, \ + 0x41, 0x46, 0x90, 0x75, \ + 0x5e, 0x58, 0xe0, 0x00, \ + 0x42, 0x46, 0xe8, 0x00, \ + 0x83, 0x21, 0xe0, 0x04, \ + 0x67, 0xb8, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x28, \ + 0xc0, 0x31, 0x65, 0x41, \ + 0x65, 0x51, 0x0c, 0x19, \ + 0x0c, 0x29, 0xc0, 0x31, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0x40, 0x00, 0xa1, 0x1b, \ + 0x4c, 0x01, 0xf3, 0xf0, \ + 0xc0, 0x31, 0x65, 0x41, \ + 0xf3, 0x7c, 0x40, 0x20, \ + 0x65, 0x79, 0x4c, 0x71, \ + 0x4c, 0x01, 0xc0, 0x31, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0x0c, 0x09, 0x82, 0xb5, \ + 0xc0, 0x2f, 0x65, 0xe5, \ + 0x82, 0x15, 0xa7, 0x0b, \ + 0x64, 0xe1, 0x0c, 0x09, \ + 0xc8, 0x09, 0xc0, 0x31, \ + 0x64, 0xe1, 0x4c, 0x01, \ + 0x8c, 0x0b, 0xc0, 0x31, \ + 0xe8, 0x18, 0x46, 0xc8, \ + 0x7f, 0xd8, 0x8c, 0x0b, \ + 0x40, 0x46, 0xe8, 0x0c, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0x80, 0x16, 0x81, 0x11, \ + 0xe1, 0x40, 0x41, 0x06, \ + 0x80, 0x16, 0x80, 0x04, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0x3f, 0x78, 0xf2, 0x04, \ + 0x0c, 0x09, 0xff, 0xd8, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0x4c, 0x01, 0xd0, 0x09, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0xe0, 0x00, 0x41, 0x56, \ + 0xe8, 0x00, 0x59, 0xc8, \ + 0x72, 0xfd, 0x43, 0xc6, \ + 0x81, 0x21, 0xc0, 0x20, \ + 0x83, 0x41, 0x83, 0x31, \ + 0x88, 0xf1, 0x8c, 0x0b, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0xff, 0xd8, 0x3e, 0xc8, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x12, 0x06, 0x80, 0x6b, \ + 0x54, 0xc6, 0xe0, 0x08, \ + 0x17, 0x46, 0xe1, 0x10, \ + 0x50, 0x06, 0xe1, 0x10, \ + 0x81, 0x71, 0xe1, 0x38, \ + 0x6e, 0x59, 0x4c, 0x91, \ + 0x8c, 0x0b, 0xc0, 0x2f, \ + 0xe8, 0x00, 0x43, 0xe8, \ + 0x49, 0xf8, 0x8c, 0x0b, \ + 0xa8, 0x04, 0xe8, 0x00, \ + 0x40, 0x0a, 0x80, 0x08, \ + 0x8a, 0x85, 0xe0, 0x84, \ + 0x8e, 0x1b, 0x8c, 0x0b, \ + 0xe8, 0x00, 0x50, 0x28, \ + 0xe0, 0x58, 0x00, 0x0a, \ + 0x68, 0x98, 0x8a, 0x15, \ + 0xac, 0x04, 0xe8, 0x14, \ + 0x80, 0x16, 0x81, 0x11, \ + 0x83, 0x21, 0x95, 0x11, \ + 0x5f, 0xd8, 0x8c, 0x0b, \ + 0xac, 0x04, 0xe8, 0x28, \ + 0x80, 0x16, 0x83, 0x11, \ + 0x84, 0x18, 0xa8, 0x14, \ + 0xe0, 0x84, 0x42, 0x4a, \ + 0x81, 0x11, 0x80, 0x65, \ + 0xa0, 0x04, 0x80, 0x16, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xdc, \ + 0x80, 0x18, 0xa0, 0x04, \ + 0xf3, 0xbc, 0x42, 0x10, \ + 0x41, 0x46, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x0c, \ + 0xc2, 0x19, 0x80, 0x18, \ + 0x82, 0x71, 0x80, 0x16, \ + 0xf0, 0xb5, 0xeb, 0x7d, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x68, 0x18, 0x80, 0x6b, \ + 0x00, 0x16, 0xef, 0xf0, \ + 0x4c, 0x01, 0xe0, 0x08, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xe0, 0x10, 0x42, 0xc6, \ + 0x8c, 0x0b, 0x81, 0x21, \ + 0xe8, 0x28, 0x5c, 0xa8, \ + 0xe1, 0x40, 0x50, 0x46, \ + 0x80, 0x08, 0xa0, 0x04, \ + 0x54, 0x00, 0xc7, 0x07, \ + 0x0e, 0x60, 0xf0, 0x04, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xe1, 0x24, 0x12, 0x06, \ + 0x67, 0x78, 0x81, 0xb5, \ + 0x0c, 0x39, 0xef, 0xf0, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x83, 0x21, 0x85, 0x11, \ + 0x6d, 0x38, 0x8e, 0x0b, \ + 0xa4, 0x04, 0xef, 0xec, \ + 0x40, 0x0a, 0x80, 0x08, \ + 0x80, 0xe5, 0xe0, 0x80, \ + 0x65, 0x61, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x84, 0x15, 0xe0, 0x4c, \ + 0xfb, 0xfc, 0x14, 0x0a, \ + 0x0c, 0x05, 0xfa, 0x25, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe0, 0x4c, 0x00, 0x0a, \ + 0xa0, 0x04, 0xf8, 0x65, \ + 0xc7, 0x07, 0x80, 0x08, \ + 0xf0, 0x04, 0x54, 0x00, \ + 0x62, 0x78, 0xfd, 0x75, \ + 0x00, 0x06, 0xe8, 0x28, \ + 0x80, 0x04, 0xe1, 0x24, \ + 0x40, 0x4a, 0x80, 0x08, \ + 0x80, 0xe5, 0xe0, 0x84, \ + 0x65, 0x61, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x75, 0xe0, 0x50, \ + 0xef, 0xf0, 0x64, 0x48, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x85, 0x11, 0xc0, 0x2f, \ + 0x8e, 0x0b, 0x85, 0x21, \ + 0xef, 0xec, 0x6a, 0x08, \ + 0xef, 0xdc, 0x62, 0xe8, \ + 0x8c, 0x1b, 0x81, 0x01, \ + 0xf2, 0x3c, 0x0c, 0x0c, \ + 0xff, 0xe0, 0x0b, 0x88, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x61, 0xd8, 0x80, 0x6b, \ + 0x00, 0x26, 0xef, 0xf0, \ + 0x4c, 0x01, 0xe0, 0x08, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xe0, 0x3c, 0x42, 0x86, \ + 0x8c, 0x0b, 0x81, 0x21, \ + 0xe8, 0x28, 0x56, 0x68, \ + 0xe1, 0x40, 0x52, 0x46, \ + 0x51, 0xc6, 0xa4, 0x04, \ + 0x80, 0x08, 0xe1, 0x20, \ + 0x84, 0x18, 0xa0, 0x14, \ + 0xe0, 0x80, 0x42, 0x4a, \ + 0xc7, 0x07, 0x88, 0xa5, \ + 0xf0, 0x04, 0x54, 0x00, \ + 0x66, 0xc1, 0x0e, 0x60, \ + 0x14, 0x0a, 0xc0, 0x56, \ + 0x80, 0xe5, 0xfb, 0xfc, \ + 0x65, 0x61, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x95, 0xe0, 0x54, \ + 0xef, 0xf0, 0x60, 0x68, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x85, 0x11, 0xc0, 0x2f, \ + 0x8e, 0x0b, 0x87, 0x21, \ + 0xef, 0xec, 0x66, 0x28, \ + 0xa4, 0x04, 0x81, 0x65, \ + 0xc7, 0x07, 0x80, 0x08, \ + 0xf0, 0x04, 0x54, 0x00, \ + 0x80, 0x08, 0xa0, 0x04, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x5c, 0x58, 0xf8, 0x65, \ + 0xa0, 0x04, 0xe8, 0x28, \ + 0x40, 0x4a, 0x80, 0x08, \ + 0x80, 0xe5, 0xe0, 0x84, \ + 0x65, 0x61, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x84, 0x75, 0xe0, 0x58, \ + 0xef, 0xf0, 0x5e, 0x48, \ + 0xe1, 0x38, 0x40, 0x06, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xd0, 0x19, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x00, 0x60, 0xc0, 0x2f, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0x89, 0x21, 0x85, 0x11, \ + 0xef, 0xec, 0x63, 0x88, \ + 0xe1, 0x38, 0x40, 0x06, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xd0, 0x19, \ + 0xef, 0xdc, 0x5c, 0x08, \ + 0x8c, 0x1b, 0x81, 0x01, \ + 0xf2, 0x3c, 0x0c, 0x0c, \ + 0xff, 0xe0, 0x04, 0xa8, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x80, 0x6b, 0x82, 0x7b, \ + 0xef, 0xf0, 0x5a, 0xe8, \ + 0xe0, 0x08, 0x00, 0x36, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x42, 0x26, 0xc0, 0x2f, \ + 0x81, 0x21, 0xe0, 0x04, \ + 0x4f, 0x78, 0x8c, 0x0b, \ + 0x54, 0x46, 0xe8, 0x28, \ + 0xa8, 0x04, 0xe1, 0x40, \ + 0xe1, 0x24, 0x12, 0x46, \ + 0xa4, 0x14, 0x80, 0x08, \ + 0x42, 0x4a, 0x84, 0x18, \ + 0x88, 0xa5, 0xe0, 0x80, \ + 0x56, 0x00, 0xc7, 0x07, \ + 0x10, 0x60, 0xf0, 0x04, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xfb, 0xfc, 0x16, 0x0a, \ + 0x0c, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe0, 0x5c, 0x00, 0x0a, \ + 0x59, 0x78, 0x82, 0x95, \ + 0x0c, 0x39, 0xef, 0xf0, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x8b, 0x21, 0x85, 0x11, \ + 0x5f, 0x38, 0x90, 0x0b, \ + 0x81, 0x65, 0xef, 0xec, \ + 0x80, 0x08, 0xa8, 0x04, \ + 0x56, 0x00, 0xc7, 0x07, \ + 0xa4, 0x04, 0xf0, 0x04, \ + 0x40, 0x0a, 0x80, 0x08, \ + 0xf8, 0x65, 0xe0, 0x80, \ + 0xe8, 0x28, 0x55, 0x68, \ + 0x80, 0x08, 0xa4, 0x04, \ + 0xe0, 0x84, 0x40, 0x4a, \ + 0x0c, 0x05, 0x82, 0x05, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe0, 0x60, 0x00, 0x0a, \ + 0x84, 0x35, 0x81, 0x81, \ + 0xad, 0x81, 0xe9, 0x7d, \ + 0x57, 0x18, 0x82, 0xd5, \ + 0x0c, 0x39, 0xef, 0xf0, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x66, 0xc1, 0x00, 0x60, \ + 0x85, 0x11, 0xc0, 0x56, \ + 0x5c, 0xb8, 0x8d, 0x21, \ + 0x81, 0x81, 0xef, 0xec, \ + 0xef, 0xdc, 0x55, 0x88, \ + 0x8c, 0x1b, 0x81, 0x01, \ + 0xef, 0xdc, 0x7e, 0x48, \ + 0x88, 0xf1, 0x90, 0x0b, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x00, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0xe1, 0x00, 0x03, 0x46, \ + 0xa1, 0x1b, 0x84, 0x14, \ + 0x00, 0x8a, 0x94, 0x05, \ + 0x02, 0x46, 0xe4, 0x80, \ + 0x84, 0x14, 0xe1, 0x48, \ + 0x02, 0x15, 0x80, 0xf5, \ + 0x42, 0x0a, 0xe0, 0x54, \ + 0x80, 0xf5, 0xe4, 0x80, \ + 0x02, 0x15, 0x89, 0x45, \ + 0x42, 0x0a, 0xe0, 0x50, \ + 0x8e, 0xe5, 0xe4, 0x80, \ + 0xe1, 0x48, 0x06, 0x46, \ + 0xc8, 0x14, 0x8c, 0x24, \ + 0x42, 0x4a, 0xca, 0x44, \ + 0xc4, 0x17, 0xe6, 0x86, \ + 0x42, 0x05, 0x84, 0x13, \ + 0x48, 0x06, 0xe0, 0x84, \ + 0x8c, 0x04, 0xfc, 0x06, \ + 0xe7, 0x6d, 0xc2, 0x64, \ + 0xe0, 0x88, 0x42, 0x45, \ + 0x02, 0x10, 0x88, 0x35, \ + 0x90, 0x11, 0xe6, 0x10, \ + 0x8c, 0x04, 0xe0, 0x21, \ + 0xe1, 0x50, 0x47, 0x06, \ + 0xe1, 0x48, 0x09, 0xc6, \ + 0x90, 0x4a, 0x8c, 0x34, \ + 0xf9, 0xc1, 0x85, 0x51, \ + 0xa1, 0x6b, 0x88, 0x64, \ + 0xf8, 0x20, 0x04, 0x65, \ + 0x88, 0x63, 0x80, 0xa5, \ + 0x86, 0x63, 0x98, 0x6f, \ + 0x0c, 0x64, 0x81, 0x45, \ + 0x98, 0x6f, 0xfc, 0xdc, \ + 0xc5, 0x6e, 0x98, 0x64, \ + 0x82, 0x51, 0xc2, 0x64, \ + 0x88, 0x21, 0xcd, 0x5d, \ + 0xc0, 0x14, 0xfa, 0xd5, \ + 0x44, 0x1a, 0x82, 0x11, \ + 0x06, 0x06, 0xe6, 0x9a, \ + 0xa4, 0x33, 0xe0, 0x20, \ + 0x80, 0x45, 0xcd, 0x3d, \ + 0xc0, 0x1e, 0x81, 0x11, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf0, 0x00, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0xe1, 0x48, 0x0c, 0x06, \ + 0x03, 0xc6, 0x98, 0x04, \ + 0x84, 0x0c, 0xe1, 0x48, \ + 0xe8, 0x00, 0x51, 0xc8, \ + 0xe1, 0x00, 0x00, 0x46, \ + 0xe1, 0x54, 0x03, 0x06, \ + 0x00, 0x86, 0x80, 0x1e, \ + 0x02, 0x86, 0xe1, 0x00, \ + 0x80, 0x1e, 0xec, 0x7c, \ + 0xe1, 0x00, 0x00, 0xc6, \ + 0xed, 0xd0, 0x03, 0x86, \ + 0xe1, 0x34, 0x44, 0xc6, \ + 0x88, 0x04, 0x80, 0x1e, \ + 0xe1, 0x00, 0x03, 0x06, \ + 0xe0, 0x88, 0x04, 0x46, \ + 0x81, 0x11, 0x84, 0x2e, \ + 0x40, 0x46, 0x80, 0x1e, \ + 0x80, 0x04, 0xe1, 0x34, \ + 0x98, 0x04, 0x80, 0x1e, \ + 0xef, 0xf4, 0x60, 0x88, \ + 0xb5, 0x11, 0x98, 0x04, \ + 0x6e, 0x59, 0x40, 0x11, \ + 0x76, 0x18, 0xc0, 0x2f, \ + 0x98, 0x04, 0xef, 0xf4, \ + 0xe1, 0x48, 0x43, 0x86, \ + 0x77, 0x78, 0x84, 0x0e, \ + 0x98, 0x14, 0xe8, 0x24, \ + 0xe1, 0x4c, 0x04, 0x86, \ + 0x82, 0x0b, 0x88, 0x0e, \ + 0xe8, 0x24, 0x77, 0x48, \ + 0x45, 0x46, 0x98, 0x14, \ + 0x88, 0x0e, 0xe1, 0x48, \ + 0x4e, 0x18, 0x82, 0x0b, \ + 0x81, 0x01, 0xe8, 0x00, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x41, 0xf6, 0x80, 0x6b, \ + 0x81, 0x11, 0xe0, 0x3c, \ + 0xef, 0x90, 0x5c, 0xe8, \ + 0x8c, 0x0b, 0x89, 0x11, \ + 0xe8, 0x00, 0x4c, 0xa8, \ + 0x6e, 0x51, 0x0c, 0x09, \ + 0x40, 0x4a, 0xc0, 0x2f, \ + 0xb7, 0x11, 0xe0, 0x8c, \ + 0x6e, 0x59, 0x4c, 0x11, \ + 0x82, 0xa5, 0xc0, 0x2f, \ + 0x65, 0xd5, 0x0c, 0x19, \ + 0xa7, 0x1b, 0xc0, 0x2f, \ + 0x0c, 0x19, 0x88, 0xf5, \ + 0xc0, 0x31, 0x64, 0xe1, \ + 0x40, 0x0a, 0x82, 0x0f, \ + 0x88, 0x05, 0xe0, 0x80, \ + 0xe0, 0x00, 0x40, 0xc6, \ + 0xef, 0xfc, 0x73, 0x48, \ + 0xe1, 0x00, 0x41, 0x06, \ + 0x80, 0x08, 0x80, 0x04, \ + 0xe0, 0x80, 0x40, 0x4a, \ + 0x8c, 0x0b, 0x80, 0x85, \ + 0xe8, 0x08, 0x45, 0x28, \ + 0x6e, 0x51, 0x0c, 0x09, \ + 0x40, 0x00, 0xc0, 0x2f, \ + 0x4c, 0x01, 0xf3, 0xdc, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x59, 0x98, 0x83, 0x11, \ + 0x8c, 0x0b, 0xef, 0x90, \ + 0xef, 0xd8, 0x5f, 0xe8, \ + 0x40, 0xd6, 0x91, 0xd5, \ + 0x71, 0x58, 0xe0, 0x00, \ + 0x0c, 0x09, 0xef, 0xfc, \ + 0xc0, 0x2f, 0x65, 0xe5, \ + 0x94, 0xf5, 0xa7, 0x0b, \ + 0x65, 0x39, 0x0c, 0x09, \ + 0xc4, 0x09, 0xc0, 0x31, \ + 0x94, 0x35, 0xa5, 0x0b, \ + 0x6e, 0x51, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x2f, \ + 0x92, 0x65, 0xe0, 0x80, \ + 0x6e, 0x5d, 0x0c, 0x09, \ + 0xa7, 0x0b, 0xc0, 0x2f, \ + 0x0c, 0x09, 0x90, 0xd5, \ + 0xc0, 0x31, 0x64, 0xe1, \ + 0xe0, 0x88, 0x40, 0x4a, \ + 0x40, 0xe6, 0x90, 0x05, \ + 0x6f, 0x58, 0xe0, 0x00, \ + 0x41, 0x06, 0xef, 0xfc, \ + 0x80, 0x04, 0xe1, 0x00, \ + 0x40, 0x4a, 0x80, 0x08, \ + 0x80, 0x85, 0xe0, 0x80, \ + 0x41, 0x38, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xe8, 0x08, \ + 0xe8, 0x28, 0x4a, 0x08, \ + 0x64, 0x18, 0x8c, 0x0b, \ + 0x0c, 0x05, 0xe8, 0x14, \ + 0xc0, 0x05, 0x69, 0xe9, \ + 0x65, 0x49, 0x0c, 0x15, \ + 0xc2, 0x0d, 0xc0, 0x0c, \ + 0x0c, 0x09, 0x88, 0x85, \ + 0xc0, 0x31, 0x64, 0xe1, \ + 0x4c, 0x01, 0xc4, 0x09, \ + 0xc0, 0x31, 0x64, 0xe1, \ + 0x67, 0x78, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x14, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0x4c, 0x01, 0xd0, 0x09, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0xe1, 0x00, 0x41, 0x06, \ + 0x80, 0x08, 0x80, 0x04, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x8c, 0x0b, 0x80, 0x85, \ + 0xe8, 0x08, 0x54, 0x68, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x53, 0x98, 0x83, 0x11, \ + 0x8c, 0x0b, 0xef, 0x90, \ + 0xe8, 0x08, 0x5f, 0x28, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x53, 0x18, 0x83, 0x11, \ + 0x0c, 0x09, 0xef, 0x90, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0xe0, 0x8c, 0x40, 0x4a, \ + 0x40, 0xf6, 0x88, 0x65, \ + 0x6a, 0xb8, 0xe0, 0x00, \ + 0x0c, 0x09, 0xef, 0xfc, \ + 0xc0, 0x2f, 0x6e, 0x31, \ + 0x80, 0xc5, 0xa1, 0x0b, \ + 0x4c, 0x01, 0xb5, 0x01, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x41, 0x06, 0x83, 0x35, \ + 0x80, 0x04, 0xe1, 0x00, \ + 0x40, 0x0a, 0x80, 0x08, \ + 0x80, 0x85, 0xe0, 0x80, \ + 0x51, 0xb8, 0x8c, 0x0b, \ + 0xb7, 0x01, 0xe8, 0x08, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x8d, 0x11, 0xc0, 0x2f, \ + 0x40, 0xb8, 0x8c, 0x0b, \ + 0xc1, 0x83, 0xe8, 0x00, \ + 0x41, 0xf6, 0xc3, 0x13, \ + 0x83, 0x11, 0xe0, 0x3c, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0x90, 0x10, 0x28, \ + 0x6e, 0x55, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x2f, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x01, 0x06, 0x80, 0x6b, \ + 0x48, 0xd8, 0xe1, 0x4c, \ + 0x40, 0x86, 0xef, 0x90, \ + 0xc0, 0x00, 0x61, 0x49, \ + 0xa3, 0x1b, 0x80, 0x18, \ + 0x81, 0x01, 0x82, 0x95, \ + 0xef, 0x90, 0x52, 0x88, \ + 0x4c, 0x01, 0xb5, 0x01, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x71, 0x38, 0x8c, 0x0b, \ + 0xff, 0xd5, 0xef, 0xfc, \ + 0x80, 0x16, 0x83, 0x11, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x00, 0x86, 0x80, 0x6b, \ + 0x80, 0x04, 0xe1, 0x14, \ + 0xc4, 0x19, 0x80, 0x18, \ + 0x00, 0x86, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x28, \ + 0x80, 0x76, 0x83, 0x71, \ + 0xe1, 0x24, 0x41, 0x86, \ + 0x80, 0x76, 0x80, 0x04, \ + 0x83, 0x21, 0x85, 0x11, \ + 0x6e, 0x58, 0x8c, 0x0b, \ + 0x41, 0xc6, 0xe8, 0x24, \ + 0x80, 0x04, 0xe1, 0x24, \ + 0xbd, 0x11, 0x80, 0x76, \ + 0x8c, 0x0b, 0x83, 0x21, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0x2d, 0x98, 0xf2, 0x04, \ + 0x00, 0x0c, 0xe0, 0x24, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x82, 0x7b, 0x86, 0x6b, \ + 0x01, 0x06, 0x80, 0x8b, \ + 0x80, 0x04, 0xe1, 0x28, \ + 0xbc, 0x2e, 0x8e, 0x23, \ + 0x83, 0x11, 0xcf, 0x2d, \ + 0x9c, 0x95, 0x80, 0x16, \ + 0x66, 0xc1, 0x12, 0x80, \ + 0x15, 0xc6, 0xc0, 0x56, \ + 0x00, 0x06, 0xe1, 0x28, \ + 0x80, 0x04, 0xe1, 0x28, \ + 0x01, 0x46, 0x80, 0x76, \ + 0x80, 0x04, 0xe1, 0x28, \ + 0x80, 0x16, 0x83, 0x11, \ + 0xe0, 0x10, 0x02, 0x06, \ + 0x90, 0x0b, 0x81, 0x21, \ + 0xe8, 0x24, 0x6b, 0x28, \ + 0xe1, 0x40, 0x40, 0x46, \ + 0x80, 0xb8, 0x80, 0x04, \ + 0x81, 0xc5, 0xc7, 0xb7, \ + 0x45, 0xd8, 0x90, 0x0b, \ + 0x10, 0x39, 0xe8, 0x00, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x87, 0x21, 0x83, 0x11, \ + 0x7c, 0x18, 0x92, 0x0b, \ + 0xa8, 0x04, 0xef, 0xe8, \ + 0xa1, 0x0b, 0x80, 0x08, \ + 0x10, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe0, 0x64, 0x00, 0x0a, \ + 0x56, 0x0a, 0x82, 0x75, \ + 0xfa, 0x25, 0xe0, 0x80, \ + 0x65, 0x61, 0x10, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0xf8, 0x65, 0xe0, 0x64, \ + 0x40, 0x06, 0xfb, 0xc5, \ + 0x80, 0x04, 0xe1, 0x28, \ + 0x98, 0x0e, 0x80, 0x04, \ + 0xe1, 0x28, 0x01, 0x46, \ + 0x81, 0x11, 0x80, 0x04, \ + 0x02, 0x06, 0x80, 0x16, \ + 0x81, 0x21, 0xe0, 0x10, \ + 0x67, 0xd8, 0x90, 0x0b, \ + 0x40, 0x46, 0xe8, 0x24, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0xc7, 0xb7, 0x80, 0xb8, \ + 0x90, 0x0b, 0x81, 0xc5, \ + 0xe8, 0x00, 0x42, 0x88, \ + 0x6e, 0x59, 0x10, 0x39, \ + 0x83, 0x11, 0xc0, 0x2f, \ + 0x92, 0x0b, 0x89, 0x21, \ + 0xef, 0xe8, 0x78, 0xc8, \ + 0x80, 0x08, 0xa8, 0x04, \ + 0x80, 0xe5, 0xa3, 0x0b, \ + 0x65, 0x61, 0x10, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x75, 0xe0, 0x68, \ + 0xe0, 0x80, 0x56, 0x0a, \ + 0x10, 0x05, 0xfa, 0x25, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe0, 0x68, 0x00, 0x0a, \ + 0xfb, 0xc5, 0xf8, 0x65, \ + 0xbc, 0x04, 0x82, 0x71, \ + 0x88, 0x61, 0xc0, 0x7d, \ + 0x88, 0xf1, 0xe4, 0x65, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0xe1, 0x24, 0x43, 0xc6, \ + 0x81, 0x61, 0x84, 0x14, \ + 0x83, 0x11, 0x84, 0x66, \ + 0x64, 0x38, 0x83, 0x21, \ + 0x41, 0x86, 0xe8, 0x24, \ + 0x80, 0x04, 0xe1, 0x24, \ + 0x00, 0x86, 0x80, 0x66, \ + 0x80, 0x04, 0xe1, 0x14, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xf4, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xf1, 0x17, 0xc0, 0x14, \ + 0x40, 0x15, 0xbe, 0x19, \ + 0xc0, 0x05, 0x66, 0x9d, \ + 0xe0, 0x18, 0x00, 0x15, \ + 0xe0, 0x50, 0x00, 0x25, \ + 0x40, 0x25, 0xe1, 0x27, \ + 0xc0, 0x0a, 0x63, 0x69, \ + 0xe0, 0x50, 0x00, 0x25, \ + 0x42, 0x10, 0xe0, 0x17, \ + 0xc3, 0x00, 0x70, 0x01, \ + 0xfb, 0x80, 0x04, 0x24, \ + 0x40, 0x25, 0xa2, 0x2f, \ + 0xc0, 0x0a, 0x63, 0x6d, \ + 0xe0, 0x54, 0x00, 0x15, \ + 0x42, 0x10, 0xed, 0x17, \ + 0x40, 0x11, 0xf3, 0xf0, \ + 0xc0, 0x28, 0x6d, 0xc1, \ + 0xe0, 0x54, 0x00, 0x15, \ + 0x42, 0x10, 0xdd, 0x17, \ + 0x40, 0x11, 0xf3, 0xf0, \ + 0xc0, 0x28, 0x6d, 0xc5, \ + 0xe5, 0x70, 0x02, 0x00, \ + 0x02, 0x35, 0x81, 0x21, \ + 0xf1, 0x37, 0xfc, 0x08, \ + 0xf0, 0x88, 0x48, 0x12, \ + 0x48, 0x31, 0xc6, 0x3b, \ + 0xc0, 0x28, 0x67, 0x01, \ + 0xfc, 0x08, 0x02, 0x35, \ + 0xc6, 0x3b, 0xe1, 0x37, \ + 0x67, 0x05, 0x48, 0x31, \ + 0x02, 0x35, 0xc0, 0x28, \ + 0xd1, 0x37, 0xfc, 0x08, \ + 0x48, 0x31, 0xc6, 0x3b, \ + 0xc0, 0x28, 0x67, 0x09, \ + 0xfc, 0x08, 0x02, 0x35, \ + 0x04, 0x8a, 0x88, 0x21, \ + 0xc0, 0x01, 0x64, 0x61, \ + 0x48, 0x31, 0xc6, 0x3b, \ + 0xc0, 0x28, 0x67, 0x0d, \ + 0x00, 0x55, 0xf6, 0xf5, \ + 0x02, 0x00, 0xe1, 0x74, \ + 0xc0, 0x01, 0x65, 0xd1, \ + 0x04, 0x00, 0xf1, 0x57, \ + 0xc0, 0x01, 0x65, 0xe1, \ + 0x64, 0x3d, 0x06, 0x00, \ + 0x81, 0x41, 0xc0, 0xa2, \ + 0x46, 0x51, 0xca, 0x5b, \ + 0x84, 0x54, 0xf8, 0x0c, \ + 0xca, 0x5b, 0xe1, 0x57, \ + 0xf8, 0x08, 0x46, 0x51, \ + 0xd1, 0x57, 0x84, 0x54, \ + 0x46, 0x51, 0xca, 0x5b, \ + 0xc5, 0x54, 0xf8, 0x04, \ + 0x46, 0x51, 0xca, 0x5b, \ + 0x88, 0x41, 0xf0, 0x10, \ + 0x08, 0x8a, 0xc9, 0x54, \ + 0xf1, 0x57, 0xe4, 0x40, \ + 0x40, 0x51, 0xfa, 0x55, \ + 0xc0, 0x28, 0x6d, 0x81, \ + 0xe1, 0x84, 0x00, 0x15, \ + 0x40, 0x11, 0xe1, 0x17, \ + 0xc0, 0x28, 0x6d, 0x85, \ + 0xe1, 0x88, 0x00, 0x15, \ + 0x40, 0x11, 0xf1, 0x17, \ + 0xc0, 0x28, 0x6d, 0x91, \ + 0xe1, 0x88, 0x00, 0x15, \ + 0x40, 0x11, 0xe1, 0x17, \ + 0xc0, 0x28, 0x6d, 0x95, \ + 0xe1, 0x8c, 0x00, 0x15, \ + 0x40, 0x11, 0xff, 0x17, \ + 0xc0, 0x28, 0x6d, 0x71, \ + 0xe1, 0x8c, 0x00, 0x15, \ + 0x42, 0x10, 0xf1, 0x17, \ + 0x40, 0x11, 0xf0, 0xfc, \ + 0xc0, 0x28, 0x6d, 0x61, \ + 0xe1, 0x8c, 0x00, 0x15, \ + 0xe1, 0x8c, 0x00, 0x25, \ + 0xc7, 0x27, 0xe9, 0x17, \ + 0xa0, 0x29, 0x9e, 0x19, \ + 0x40, 0x21, 0xa2, 0x2f, \ + 0xc0, 0x28, 0x6d, 0x6d, \ + 0xe1, 0x8c, 0x00, 0x15, \ + 0xe1, 0x8c, 0x00, 0x25, \ + 0xc5, 0x27, 0xe1, 0x17, \ + 0xa0, 0x29, 0x9e, 0x19, \ + 0x40, 0x21, 0xa2, 0x2f, \ + 0xc0, 0x28, 0x6d, 0x69, \ + 0xe1, 0x8c, 0x00, 0x15, \ + 0xe1, 0x8c, 0x00, 0x25, \ + 0xbe, 0x19, 0xd1, 0x17, \ + 0xf0, 0x80, 0x44, 0x20, \ + 0x40, 0x21, 0xa2, 0x2f, \ + 0xc0, 0x28, 0x6d, 0x65, \ + 0xe1, 0x90, 0x00, 0x15, \ + 0x6f, 0x79, 0x40, 0x15, \ + 0xb5, 0x11, 0xc0, 0x0b, \ + 0x66, 0x61, 0x04, 0x00, \ + 0x06, 0x06, 0xc0, 0xa4, \ + 0xff, 0xc0, 0x60, 0x01, \ + 0x60, 0x01, 0x08, 0x06, \ + 0x00, 0x55, 0xee, 0xc0, \ + 0x86, 0x5f, 0xe3, 0xbc, \ + 0x82, 0x55, 0x89, 0x5b, \ + 0x79, 0x05, 0x04, 0x55, \ + 0xe1, 0x57, 0xc0, 0x51, \ + 0x04, 0x55, 0x88, 0x5c, \ + 0xc0, 0x51, 0x79, 0x05, \ + 0xa1, 0x51, 0x81, 0x35, \ + 0x02, 0x10, 0x88, 0x5c, \ + 0xa1, 0x1b, 0xec, 0x04, \ + 0x88, 0x21, 0x8a, 0x5c, \ + 0x00, 0x15, 0xfa, 0x75, \ + 0xe1, 0x17, 0xe1, 0xfc, \ + 0x66, 0xe9, 0x40, 0x13, \ + 0x00, 0x15, 0xc0, 0x14, \ + 0x40, 0x13, 0xe1, 0xfc, \ + 0xc0, 0x14, 0x66, 0xed, \ + 0xe2, 0x00, 0x00, 0x15, \ + 0x40, 0x11, 0xe1, 0x17, \ + 0xc0, 0x28, 0x6d, 0xe1, \ + 0xe2, 0x00, 0x00, 0x15, \ + 0x40, 0x11, 0xd1, 0x17, \ + 0xc0, 0x28, 0x6d, 0xe5, \ + 0xe2, 0x00, 0x00, 0x15, \ + 0x6d, 0xe9, 0x40, 0x11, \ + 0x00, 0x15, 0xc0, 0x28, \ + 0xf1, 0x17, 0xe2, 0x04, \ + 0x6d, 0xed, 0x40, 0x11, \ + 0x00, 0x15, 0xc0, 0x28, \ + 0xe1, 0x17, 0xe2, 0x04, \ + 0x6d, 0xf1, 0x40, 0x11, \ + 0x00, 0x15, 0xc0, 0x28, \ + 0xd1, 0x17, 0xe2, 0x04, \ + 0x6d, 0xf5, 0x40, 0x11, \ + 0x00, 0x15, 0xc0, 0x28, \ + 0x40, 0x11, 0xe2, 0x04, \ + 0xc0, 0x28, 0x6d, 0xf9, \ + 0xe2, 0x08, 0x00, 0x15, \ + 0x40, 0x11, 0xf1, 0x17, \ + 0xc0, 0x28, 0x6d, 0xfd, \ + 0x64, 0x31, 0x02, 0x00, \ + 0x04, 0x06, 0xc0, 0x02, \ + 0xc5, 0x34, 0xe0, 0x04, \ + 0xec, 0x04, 0x04, 0x20, \ + 0x42, 0x35, 0xa1, 0x2b, \ + 0xc0, 0x0a, 0x61, 0x89, \ + 0x02, 0x00, 0xfe, 0x35, \ + 0xc0, 0x03, 0x64, 0x31, \ + 0x84, 0x34, 0x81, 0x21, \ + 0x42, 0x33, 0xe1, 0x37, \ + 0xc0, 0x14, 0x64, 0x19, \ + 0x84, 0x21, 0xc5, 0x34, \ + 0xe5, 0x00, 0x04, 0x8a, \ + 0x64, 0x15, 0x42, 0x33, \ + 0xfc, 0x95, 0xc0, 0x14, \ + 0x04, 0x00, 0x8d, 0x11, \ + 0xc0, 0xa5, 0x64, 0x01, \ + 0x60, 0x01, 0x06, 0x06, \ + 0x08, 0x06, 0xff, 0xc0, \ + 0xee, 0xc0, 0x60, 0x01, \ + 0xe3, 0xbc, 0x00, 0x55, \ + 0x89, 0x5b, 0x86, 0x5f, \ + 0x04, 0x55, 0x82, 0x55, \ + 0xc0, 0x50, 0x79, 0x75, \ + 0x88, 0x5c, 0xe1, 0x57, \ + 0x79, 0x75, 0x04, 0x55, \ + 0x81, 0x35, 0xc0, 0x50, \ + 0x88, 0x5c, 0xa1, 0x51, \ + 0xec, 0x04, 0x02, 0x10, \ + 0x8a, 0x5c, 0xa1, 0x1b, \ + 0xfa, 0x75, 0x88, 0x21, \ + 0xe3, 0xa4, 0x00, 0x15, \ + 0x73, 0xfd, 0x42, 0x10, \ + 0x40, 0x15, 0xc7, 0xff, \ + 0xc0, 0x0a, 0x63, 0x81, \ + 0xe3, 0xa8, 0x00, 0x15, \ + 0x73, 0xfd, 0x42, 0x10, \ + 0x40, 0x15, 0xc7, 0xff, \ + 0xc0, 0x0a, 0x63, 0x85, \ + 0xe3, 0xac, 0x00, 0x15, \ + 0x73, 0xfd, 0x42, 0x10, \ + 0x40, 0x15, 0xc7, 0xff, \ + 0xc0, 0x0a, 0x63, 0x89, \ + 0xe3, 0xb0, 0x00, 0x15, \ + 0x67, 0x19, 0x40, 0x13, \ + 0x00, 0x15, 0xc0, 0x14, \ + 0xe1, 0x17, 0xe3, 0xb4, \ + 0x67, 0x1d, 0x40, 0x13, \ + 0x00, 0x15, 0xc0, 0x14, \ + 0x40, 0x13, 0xe3, 0xb4, \ + 0xc0, 0x14, 0x67, 0x21, \ + 0xe3, 0xb8, 0x00, 0x15, \ + 0x40, 0x11, 0xf1, 0x17, \ + 0xc0, 0x28, 0x6e, 0x51, \ + 0xe3, 0xb8, 0x00, 0x15, \ + 0x40, 0x11, 0xe1, 0x17, \ + 0xc0, 0x28, 0x6e, 0x55, \ + 0xe3, 0xb8, 0x00, 0x15, \ + 0x40, 0x11, 0xd1, 0x17, \ + 0xc0, 0x28, 0x6e, 0x59, \ + 0xe3, 0xb8, 0x00, 0x15, \ + 0x6e, 0x5d, 0x40, 0x11, \ + 0x02, 0x00, 0xc0, 0x28, \ + 0xc0, 0xbf, 0x65, 0xf1, \ + 0x02, 0x35, 0x89, 0x21, \ + 0xc0, 0x5d, 0x79, 0xb1, \ + 0xec, 0x04, 0x04, 0x20, \ + 0xc5, 0x3e, 0xa1, 0x2b, \ + 0x00, 0x00, 0xfe, 0x35, \ + 0xc0, 0x54, 0x64, 0x81, \ + 0x00, 0x25, 0x93, 0x11, \ + 0xc0, 0x28, 0x78, 0x45, \ + 0xec, 0x04, 0x02, 0x10, \ + 0xc1, 0x2e, 0xa1, 0x1b, \ + 0xdd, 0x93, 0xfe, 0x35, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xf8, 0x00, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0xef, 0x4c, 0x02, 0x29, \ + 0x80, 0xb5, 0xa1, 0x2b, \ + 0xef, 0x3c, 0x02, 0x29, \ + 0x94, 0x05, 0xa1, 0x2b, \ + 0xf9, 0x41, 0x81, 0x31, \ + 0x81, 0x21, 0x87, 0x51, \ + 0xf8, 0x10, 0x58, 0x32, \ + 0x4c, 0x32, 0x82, 0xc3, \ + 0x18, 0xc9, 0xf8, 0x94, \ + 0xd8, 0x2f, 0xfc, 0x18, \ + 0x05, 0x0a, 0x91, 0xc1, \ + 0xc2, 0x27, 0xf3, 0xfc, \ + 0xbb, 0x29, 0x80, 0x55, \ + 0xec, 0x04, 0x18, 0xc0, \ + 0xfe, 0x15, 0xa1, 0xcb, \ + 0x06, 0x0a, 0x82, 0x31, \ + 0xfa, 0x75, 0xe1, 0x40, \ + 0xfb, 0xfc, 0x04, 0x0a, \ + 0x05, 0x9e, 0x80, 0x75, \ + 0x46, 0x86, 0xff, 0xf8, \ + 0x8c, 0x34, 0xe1, 0x34, \ + 0xfa, 0x80, 0x04, 0x24, \ + 0x02, 0x39, 0x8c, 0x2e, \ + 0x87, 0x2b, 0xef, 0x4c, \ + 0x46, 0xc6, 0x82, 0x15, \ + 0x8c, 0x34, 0xe1, 0x30, \ + 0x06, 0x0a, 0x8c, 0x34, \ + 0x88, 0x15, 0xe0, 0x6c, \ + 0xef, 0x3c, 0x02, 0x19, \ + 0x82, 0x15, 0x83, 0x2b, \ + 0xe1, 0x30, 0x42, 0xc6, \ + 0x84, 0x14, 0x84, 0x14, \ + 0xe0, 0x6c, 0x02, 0x0a, \ + 0x00, 0x39, 0x84, 0xb5, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xe1, 0x48, 0x42, 0xc6, \ + 0x84, 0x26, 0x8d, 0x21, \ + 0x60, 0x01, 0x40, 0x21, \ + 0x00, 0x00, 0xc0, 0x31, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0x8d, 0x21, 0x83, 0x11, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0x14, 0x78, 0xf0, 0x00, \ + 0x88, 0xf1, 0xff, 0xe8, \ + 0xf0, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x02, 0x06, 0x80, 0x6b, \ + 0x49, 0xf8, 0xe2, 0x80, \ + 0x8c, 0x0b, 0xe8, 0x28, \ + 0xef, 0xfc, 0x51, 0xc8, \ + 0x45, 0xf6, 0x81, 0x11, \ + 0x8c, 0x0b, 0xe0, 0x0c, \ + 0x53, 0xd8, 0x8c, 0x3b, \ + 0x8c, 0x0b, 0xef, 0xfc, \ + 0xef, 0xfc, 0x5c, 0x28, \ + 0x8c, 0x1b, 0x8c, 0x0b, \ + 0xef, 0xfc, 0x78, 0xa8, \ + 0x0c, 0x0c, 0x8c, 0x0b, \ + 0x1d, 0x58, 0xf2, 0x00, \ + 0x00, 0x0c, 0xff, 0xfc, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x98, 0x6b, 0x80, 0x8b, \ + 0x9a, 0x0e, 0x81, 0x01, \ + 0x90, 0x0b, 0x98, 0x0e, \ + 0xef, 0xfc, 0x4f, 0xc8, \ + 0x66, 0x01, 0x0e, 0x80, \ + 0x43, 0x46, 0xc0, 0xbf, \ + 0x81, 0x21, 0xe0, 0x0c, \ + 0x8e, 0x3b, 0x90, 0x0b, \ + 0xef, 0xfc, 0x51, 0xa8, \ + 0xa1, 0x0b, 0x98, 0x04, \ + 0x0c, 0x0c, 0x80, 0x85, \ + 0xdd, 0x93, 0xf2, 0x0c, \ + 0x43, 0xfe, 0x9c, 0x04, \ + 0xc0, 0x3f, 0x7f, 0xfd, \ + 0xe4, 0x04, 0x41, 0x4a, \ + 0xe2, 0x00, 0x00, 0x0c, \ + 0x0c, 0x0c, 0x9a, 0x0e, \ + 0xdd, 0x93, 0xf2, 0x0c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x85, 0x11, 0x80, 0x6b, \ + 0x8c, 0x3b, 0x81, 0x21, \ + 0xef, 0xfc, 0x4f, 0xc8, \ + 0xe4, 0x10, 0x06, 0x60, \ + 0x81, 0x21, 0x8d, 0x11, \ + 0x4f, 0x58, 0x8c, 0x0b, \ + 0x06, 0x60, 0xef, 0xfc, \ + 0xa9, 0x11, 0xe4, 0x20, \ + 0x8c, 0x0b, 0x81, 0x21, \ + 0xef, 0xfc, 0x4e, 0xe8, \ + 0x57, 0x38, 0x8c, 0x0b, \ + 0xd8, 0x08, 0xef, 0xfc, \ + 0xe1, 0x04, 0x43, 0x06, \ + 0x9f, 0x09, 0x84, 0x14, \ + 0x04, 0x00, 0x84, 0x78, \ + 0x84, 0x26, 0xe4, 0x04, \ + 0xe1, 0x10, 0x43, 0x46, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x80, 0xf5, 0x83, 0x0b, \ + 0x65, 0x61, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x75, 0xe0, 0x70, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x00, 0x60, 0xc0, 0x2f, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xa7, 0x21, 0x85, 0x11, \ + 0xef, 0xe8, 0x4c, 0x08, \ + 0xe1, 0x04, 0x41, 0x06, \ + 0x80, 0x76, 0x80, 0x04, \ + 0xd8, 0x14, 0x9a, 0x04, \ + 0x40, 0x00, 0xe0, 0x07, \ + 0xc3, 0x00, 0x70, 0x01, \ + 0xfb, 0x80, 0x04, 0x14, \ + 0xe1, 0x17, 0xa4, 0x0f, \ + 0x63, 0x69, 0x4c, 0x15, \ + 0x4c, 0x05, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x63, 0x6d, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x04, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x15, 0x61, 0x51, \ + 0x40, 0x06, 0xa9, 0x0b, \ + 0x4c, 0x01, 0xe0, 0x08, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x0c, 0x09, 0x96, 0x95, \ + 0xc0, 0x15, 0x61, 0x55, \ + 0x94, 0xf5, 0xa9, 0x0b, \ + 0x6d, 0x55, 0x0c, 0x09, \ + 0x0c, 0x19, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6d, 0x59, \ + 0x6d, 0x51, 0x0c, 0x29, \ + 0x0c, 0x39, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6d, 0x65, \ + 0x6d, 0x5d, 0x0c, 0x49, \ + 0x0c, 0x59, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6d, 0x61, \ + 0x6b, 0x29, 0x4c, 0x11, \ + 0x4c, 0x01, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6b, 0x25, \ + 0x6b, 0x21, 0x4c, 0x21, \ + 0x4c, 0x31, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6b, 0x35, \ + 0x6b, 0x31, 0x4c, 0x51, \ + 0x4c, 0x41, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6b, 0x2d, \ + 0x6d, 0x6d, 0x0c, 0x09, \ + 0x0c, 0x19, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6d, 0x71, \ + 0x6d, 0x69, 0x0c, 0x29, \ + 0x0c, 0x39, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6d, 0x7d, \ + 0x6d, 0x75, 0x0c, 0x49, \ + 0x0c, 0x59, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6d, 0x79, \ + 0x6b, 0x41, 0x4c, 0x11, \ + 0x4c, 0x01, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6b, 0x3d, \ + 0x6b, 0x39, 0x4c, 0x21, \ + 0x4c, 0x31, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6b, 0x4d, \ + 0x6b, 0x49, 0x4c, 0x51, \ + 0x4c, 0x41, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6b, 0x45, \ + 0x67, 0x51, 0x00, 0x60, \ + 0x02, 0x60, 0xc0, 0xae, \ + 0xc0, 0xaf, 0x65, 0x81, \ + 0xe0, 0x08, 0x04, 0x06, \ + 0xef, 0x88, 0x7d, 0xa8, \ + 0x66, 0xe1, 0x00, 0x60, \ + 0x8b, 0x65, 0xc0, 0xa5, \ + 0x6f, 0x85, 0x0c, 0x09, \ + 0x0c, 0x19, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6f, 0x89, \ + 0x6f, 0x81, 0x0c, 0x29, \ + 0x0c, 0x39, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6f, 0x95, \ + 0x6f, 0x8d, 0x0c, 0x49, \ + 0x0c, 0x59, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6f, 0x91, \ + 0x6b, 0x29, 0x4c, 0x11, \ + 0x4c, 0x01, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6b, 0x25, \ + 0x6b, 0x21, 0x4c, 0x21, \ + 0x4c, 0x31, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6b, 0x35, \ + 0x6b, 0x31, 0x4c, 0x51, \ + 0x4c, 0x41, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6b, 0x2d, \ + 0x6f, 0x9d, 0x0c, 0x09, \ + 0x0c, 0x19, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6f, 0xa1, \ + 0x6f, 0x99, 0x0c, 0x29, \ + 0x0c, 0x39, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6f, 0xad, \ + 0x6f, 0xa5, 0x0c, 0x49, \ + 0x0c, 0x59, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6f, 0xa9, \ + 0x6b, 0x41, 0x4c, 0x11, \ + 0x4c, 0x01, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6b, 0x3d, \ + 0x6b, 0x39, 0x4c, 0x21, \ + 0x4c, 0x31, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6b, 0x4d, \ + 0x6b, 0x49, 0x4c, 0x51, \ + 0x4c, 0x41, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x6b, 0x45, \ + 0x67, 0x51, 0x00, 0x60, \ + 0x02, 0x60, 0xc0, 0xae, \ + 0xc0, 0xaf, 0x67, 0xb1, \ + 0xe0, 0x08, 0x04, 0x06, \ + 0xef, 0x88, 0x78, 0x48, \ + 0x65, 0xd1, 0x00, 0x60, \ + 0x44, 0x16, 0xc0, 0xa3, \ + 0x02, 0x60, 0xe0, 0x08, \ + 0xc0, 0xb0, 0x65, 0xb1, \ + 0x6e, 0x59, 0x4c, 0x21, \ + 0x6b, 0xb8, 0xc0, 0x2f, \ + 0x0c, 0x15, 0xef, 0xa0, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe0, 0xdc, 0x02, 0x0a, \ + 0xff, 0x01, 0x80, 0x55, \ + 0x82, 0xe5, 0xa1, 0x0b, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x08, 0x60, 0xc0, 0x2f, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xfb, 0x00, 0x04, 0x04, \ + 0x88, 0x0b, 0x89, 0x11, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0xe4, 0x3c, 0x88, \ + 0xe1, 0x48, 0x00, 0x46, \ + 0x0c, 0x19, 0x80, 0x04, \ + 0xc0, 0x16, 0x67, 0x79, \ + 0x67, 0x7d, 0x0c, 0x29, \ + 0x0b, 0x46, 0xc0, 0x16, \ + 0x06, 0x60, 0xe0, 0x0c, \ + 0xc0, 0x59, 0x67, 0x41, \ + 0x67, 0xe1, 0x08, 0x60, \ + 0x4c, 0x51, 0xc0, 0xb2, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0xc8, 0x3b, 0x08, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x0e, 0x06, 0x80, 0x6b, \ + 0x9c, 0x6e, 0xe1, 0x4c, \ + 0xe1, 0x00, 0x02, 0x06, \ + 0xe8, 0x24, 0x70, 0x68, \ + 0x8c, 0x0b, 0x9c, 0x14, \ + 0xe8, 0x00, 0x40, 0xa8, \ + 0x8c, 0x0b, 0x9c, 0x14, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0x87, 0xb5, 0xf2, 0x04, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x80, 0x7b, 0x82, 0x6b, \ + 0xe0, 0x08, 0x40, 0x26, \ + 0x6e, 0x59, 0x4e, 0x01, \ + 0x00, 0x60, 0xc0, 0x2f, \ + 0xc0, 0x11, 0x64, 0xb1, \ + 0x65, 0xc1, 0x02, 0x70, \ + 0x89, 0x21, 0xc0, 0x8e, \ + 0x4c, 0x18, 0x81, 0x31, \ + 0x00, 0x60, 0xe8, 0x14, \ + 0xc0, 0x11, 0x64, 0xc1, \ + 0x65, 0x71, 0x02, 0x70, \ + 0xa9, 0x21, 0xc0, 0x8e, \ + 0x4b, 0x78, 0x81, 0x31, \ + 0x00, 0x60, 0xe8, 0x14, \ + 0xc0, 0x11, 0x65, 0x11, \ + 0x65, 0xd1, 0x02, 0x70, \ + 0xa1, 0x21, 0xc0, 0x8e, \ + 0x4a, 0xd8, 0x81, 0x31, \ + 0x00, 0x60, 0xe8, 0x14, \ + 0xc0, 0x11, 0x65, 0x51, \ + 0x66, 0x11, 0x02, 0x70, \ + 0x45, 0x06, 0xc0, 0x8e, \ + 0x89, 0x31, 0xe0, 0x18, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0x09, 0xf8, 0xf2, 0x04, \ + 0xc1, 0xc3, 0xe0, 0x14, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xf0, 0x1e, 0xf0, \ + 0x80, 0x6b, 0x82, 0x7b, \ + 0x61, 0x51, 0x0c, 0x09, \ + 0xa9, 0x0b, 0xc0, 0x15, \ + 0x0c, 0x09, 0x82, 0xb5, \ + 0xc0, 0x15, 0x61, 0x55, \ + 0x82, 0x15, 0xa9, 0x0b, \ + 0x66, 0xe1, 0x10, 0x60, \ + 0x06, 0x60, 0xc0, 0xa5, \ + 0xc0, 0xa9, 0x67, 0x01, \ + 0x10, 0x60, 0x81, 0x75, \ + 0xc0, 0xa3, 0x65, 0xd1, \ + 0x66, 0x11, 0x06, 0x60, \ + 0x0c, 0x15, 0xc0, 0xab, \ + 0xc0, 0x0b, 0x6e, 0xf9, \ + 0x94, 0xd5, 0xa1, 0x1b, \ + 0x67, 0xe1, 0x14, 0x60, \ + 0x40, 0x46, 0xc0, 0xbe, \ + 0x4c, 0x01, 0xe0, 0x08, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xe1, 0x48, 0x00, 0x46, \ + 0x9b, 0x0e, 0x80, 0x04, \ + 0x67, 0x41, 0x02, 0x60, \ + 0x04, 0x70, 0xc0, 0x59, \ + 0xc0, 0x11, 0x64, 0xb1, \ + 0x67, 0x21, 0x00, 0x60, \ + 0x9d, 0x0e, 0xc0, 0xae, \ + 0x65, 0xb1, 0x00, 0x60, \ + 0x99, 0x0e, 0xc0, 0xb0, \ + 0x67, 0x71, 0x18, 0x60, \ + 0x08, 0x60, 0xc0, 0x71, \ + 0xc0, 0xbd, 0x67, 0x31, \ + 0x67, 0x51, 0x12, 0x60, \ + 0x0a, 0x70, 0xc0, 0xbe, \ + 0xc0, 0x13, 0x66, 0x51, \ + 0x64, 0x01, 0x16, 0x70, \ + 0x00, 0x70, 0xc0, 0x10, \ + 0xc0, 0x11, 0x64, 0x21, \ + 0x8f, 0xae, 0x97, 0xae, \ + 0x86, 0x9b, 0x92, 0xab, \ + 0x8d, 0x0e, 0x95, 0x0e, \ + 0x93, 0x5e, 0x8b, 0xbe, \ + 0xfe, 0xae, 0x89, 0x5e, \ + 0xbe, 0xce, 0xfc, 0x4e, \ + 0xbc, 0x0e, 0x99, 0x04, \ + 0x99, 0x1e, 0x9b, 0x04, \ + 0x90, 0x3b, 0x9b, 0x2e, \ + 0x9d, 0x54, 0x92, 0x4b, \ + 0xef, 0xc8, 0x54, 0xa8, \ + 0x65, 0x61, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x55, 0xe1, 0x7c, \ + 0x85, 0x01, 0xfb, 0x11, \ + 0x6e, 0xf9, 0x4c, 0x15, \ + 0x4c, 0x05, 0xc0, 0x0b, \ + 0xc0, 0x0b, 0x6e, 0xfd, \ + 0x97, 0x84, 0x81, 0x55, \ + 0xa1, 0x1b, 0xa0, 0x14, \ + 0x0c, 0x39, 0x86, 0x45, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x6e, 0x01, 0x0c, 0x4b, \ + 0x0c, 0x5b, 0xc0, 0x17, \ + 0xc0, 0x17, 0x6e, 0x09, \ + 0x6d, 0xf9, 0x0c, 0xcb, \ + 0x00, 0x60, 0xc0, 0x17, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xfb, 0x00, 0x04, 0x14, \ + 0x89, 0x11, 0xbc, 0xce, \ + 0xef, 0xe4, 0x6a, 0xa8, \ + 0x0c, 0x0c, 0xf8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe0, 0x08, 0x40, 0x56, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x00, 0x46, 0xc0, 0x2f, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0x65, 0x21, 0x0a, 0x60, \ + 0x02, 0x70, 0xc0, 0xad, \ + 0xc0, 0x14, 0x65, 0x01, \ + 0x66, 0x81, 0x04, 0x70, \ + 0x89, 0x8e, 0xc0, 0x1f, \ + 0xfe, 0x2e, 0x9d, 0x2e, \ + 0x9b, 0x14, 0xfc, 0x1e, \ + 0x93, 0x14, 0xbe, 0x1e, \ + 0x8e, 0x1b, 0xbc, 0x1e, \ + 0x95, 0x34, 0x96, 0x2b, \ + 0x4d, 0x78, 0x92, 0x4b, \ + 0x0c, 0x05, 0xef, 0xc4, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe1, 0x80, 0x00, 0x0a, \ + 0xff, 0x11, 0x80, 0xb5, \ + 0xe0, 0x00, 0x40, 0x16, \ + 0x9d, 0x34, 0xf7, 0x45, \ + 0xa1, 0x1b, 0xa0, 0x14, \ + 0x40, 0x66, 0xee, 0x75, \ + 0x02, 0x46, 0xe0, 0x08, \ + 0x4c, 0x01, 0xe1, 0x48, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x0a, 0x60, 0x84, 0x04, \ + 0xc0, 0xb2, 0x67, 0xe1, \ + 0x67, 0xf1, 0x02, 0x60, \ + 0x04, 0x60, 0xc0, 0xa7, \ + 0xc0, 0xc5, 0x67, 0x51, \ + 0xfc, 0x2e, 0xfe, 0x8e, \ + 0xbe, 0x2e, 0x99, 0x24, \ + 0x9b, 0x14, 0xbc, 0x1e, \ + 0x96, 0x3b, 0x86, 0x2b, \ + 0x6d, 0x98, 0x94, 0x4b, \ + 0x0c, 0x15, 0xef, 0xc4, \ + 0xc0, 0x0b, 0x6e, 0xf9, \ + 0xe6, 0x55, 0xa1, 0x1b, \ + 0x69, 0xa1, 0x0c, 0x15, \ + 0xa1, 0x1b, 0xc0, 0x0a, \ + 0x0c, 0x39, 0xea, 0x45, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x63, 0x51, 0x0c, 0x4b, \ + 0x0c, 0x5b, 0xc0, 0x15, \ + 0xc0, 0x15, 0x63, 0x59, \ + 0x63, 0x49, 0x0c, 0xcb, \ + 0x00, 0x60, 0xc0, 0x15, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xfb, 0x00, 0x04, 0x14, \ + 0x8b, 0x11, 0xbc, 0xce, \ + 0xc1, 0xc3, 0xf3, 0x45, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x0e, 0x06, 0x80, 0x6b, \ + 0x9c, 0x6e, 0xe1, 0x4c, \ + 0xe1, 0x00, 0x02, 0x06, \ + 0xe8, 0x24, 0x5b, 0xe8, \ + 0x64, 0xf1, 0x10, 0x60, \ + 0x81, 0x91, 0xc0, 0x73, \ + 0x17, 0x06, 0x83, 0xa1, \ + 0x0c, 0x1b, 0xe1, 0x34, \ + 0xc0, 0x17, 0x6f, 0x39, \ + 0xe8, 0xa4, 0x00, 0xa4, \ + 0xa1, 0x1b, 0x80, 0x1f, \ + 0xac, 0x14, 0x8a, 0x25, \ + 0xa1, 0x2b, 0x84, 0x28, \ + 0x84, 0x18, 0x80, 0xa5, \ + 0xa1, 0x0b, 0x82, 0x0f, \ + 0xa0, 0x08, 0x88, 0x25, \ + 0xe3, 0xfc, 0x00, 0x0a, \ + 0x80, 0xe5, 0x9c, 0x14, \ + 0xfa, 0x80, 0x04, 0x94, \ + 0x42, 0x18, 0x8c, 0x0b, \ + 0x81, 0x45, 0xe8, 0x00, \ + 0x6a, 0x18, 0x8c, 0x0b, \ + 0xa0, 0x18, 0xef, 0xfc, \ + 0xe3, 0xfc, 0x02, 0x0a, \ + 0x8c, 0x0b, 0x80, 0xa5, \ + 0xef, 0xe4, 0x53, 0x68, \ + 0x0c, 0x05, 0x81, 0x45, \ + 0xc0, 0x08, 0x6e, 0x75, \ + 0x4c, 0x05, 0x9c, 0x14, \ + 0xc0, 0x0b, 0x6e, 0xd5, \ + 0x6c, 0x78, 0x8c, 0x0b, \ + 0x82, 0x91, 0xef, 0xfc, \ + 0xe0, 0x40, 0x12, 0x0a, \ + 0xf2, 0x95, 0x82, 0x81, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x82, 0x7b, 0x84, 0x6b, \ + 0x40, 0x36, 0x80, 0x8b, \ + 0x50, 0x01, 0xe0, 0x08, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x64, 0xb1, 0x00, 0x70, \ + 0x02, 0x80, 0xc0, 0x11, \ + 0xc0, 0x72, 0x65, 0xe1, \ + 0x81, 0x31, 0x89, 0x21, \ + 0xe8, 0x10, 0x74, 0x68, \ + 0x64, 0xc1, 0x00, 0x70, \ + 0x02, 0x80, 0xc0, 0x11, \ + 0xc0, 0x72, 0x65, 0x91, \ + 0x81, 0x31, 0xa9, 0x21, \ + 0xe8, 0x10, 0x73, 0xc8, \ + 0xe0, 0x88, 0x00, 0x64, \ + 0x65, 0xf1, 0x02, 0x80, \ + 0x02, 0x25, 0xc0, 0x72, \ + 0x82, 0x03, 0xfc, 0x00, \ + 0x61, 0x45, 0x4e, 0x25, \ + 0x00, 0x25, 0xc0, 0x01, \ + 0x4e, 0x25, 0xe0, 0x40, \ + 0xc0, 0x01, 0x61, 0x49, \ + 0xe0, 0x80, 0x00, 0x05, \ + 0x61, 0x4d, 0x4e, 0x05, \ + 0x8c, 0x13, 0xc0, 0x01, \ + 0xe3, 0x00, 0x02, 0x09, \ + 0x65, 0x41, 0x4e, 0x01, \ + 0x02, 0x09, 0xc0, 0x04, \ + 0x43, 0x06, 0xe3, 0x40, \ + 0x42, 0x6a, 0xe0, 0x18, \ + 0x4e, 0x01, 0xe6, 0x86, \ + 0xc0, 0x04, 0x65, 0x45, \ + 0x00, 0x70, 0x82, 0x83, \ + 0xc0, 0x11, 0x65, 0x51, \ + 0x65, 0x71, 0x02, 0x80, \ + 0x45, 0x06, 0xc0, 0x73, \ + 0x89, 0x31, 0xe0, 0x18, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0xe0, 0x10, 0x30, 0xa8, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x00, 0x46, 0x80, 0x6b, \ + 0x42, 0xc6, 0xe1, 0x48, \ + 0x80, 0x24, 0xe1, 0x50, \ + 0x00, 0x36, 0x84, 0x6e, \ + 0x14, 0x60, 0xe0, 0x0c, \ + 0xc0, 0x59, 0x67, 0x41, \ + 0x65, 0x51, 0x12, 0x60, \ + 0x10, 0x60, 0xc0, 0x54, \ + 0xc0, 0xbe, 0x67, 0xe1, \ + 0x65, 0x61, 0x0e, 0x60, \ + 0x4c, 0x01, 0xc0, 0xbf, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x92, 0x1b, 0x94, 0x0b, \ + 0x8c, 0x4b, 0x90, 0x3b, \ + 0x5d, 0x78, 0x8e, 0x5b, \ + 0x0c, 0x05, 0xef, 0xd0, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe1, 0x84, 0x00, 0x0a, \ + 0xff, 0x01, 0x80, 0x75, \ + 0xa0, 0x14, 0xa0, 0x0e, \ + 0x84, 0xc5, 0xa1, 0x1b, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x0c, 0x4b, 0xc0, 0x2f, \ + 0xc0, 0x17, 0x6e, 0x01, \ + 0x6e, 0x09, 0x0c, 0x5b, \ + 0x0c, 0xcb, 0xc0, 0x17, \ + 0xc0, 0x17, 0x6d, 0xf9, \ + 0x66, 0xc1, 0x00, 0x60, \ + 0x04, 0x14, 0xc0, 0x56, \ + 0xbc, 0xce, 0xfb, 0x00, \ + 0x55, 0xf8, 0x89, 0x11, \ + 0x00, 0x46, 0xef, 0xe4, \ + 0x42, 0xc6, 0xe1, 0x48, \ + 0x80, 0x24, 0xe1, 0x50, \ + 0x00, 0x46, 0x84, 0x44, \ + 0x4c, 0x01, 0xe0, 0x0c, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x92, 0x1b, 0x94, 0x0b, \ + 0x8e, 0x5b, 0x90, 0x3b, \ + 0xef, 0xd0, 0x5d, 0xa8, \ + 0x65, 0x61, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x80, 0x95, 0xe1, 0x88, \ + 0xa0, 0x1e, 0xff, 0x11, \ + 0xa0, 0x14, 0x81, 0x45, \ + 0x84, 0xc5, 0xa1, 0x1b, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x0c, 0x4b, 0xc0, 0x2f, \ + 0xc0, 0x17, 0x6e, 0x01, \ + 0x6e, 0x09, 0x0c, 0x5b, \ + 0x0c, 0xcb, 0xc0, 0x17, \ + 0xc0, 0x17, 0x6d, 0xf9, \ + 0x66, 0xc1, 0x00, 0x60, \ + 0x04, 0x14, 0xc0, 0x56, \ + 0xbc, 0xce, 0xfb, 0x00, \ + 0x52, 0xd8, 0x89, 0x11, \ + 0x0c, 0x15, 0xef, 0xe4, \ + 0xc0, 0x0b, 0x6f, 0x09, \ + 0x84, 0xc5, 0xa1, 0x1b, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x0c, 0x4b, 0xc0, 0x2f, \ + 0xc0, 0x17, 0x6e, 0x21, \ + 0x6e, 0x29, 0x0c, 0x5b, \ + 0x0c, 0xcb, 0xc0, 0x17, \ + 0xc0, 0x17, 0x6e, 0x19, \ + 0x66, 0xf1, 0x00, 0x60, \ + 0x04, 0x14, 0xc0, 0x56, \ + 0xbc, 0xce, 0xfb, 0x00, \ + 0x5d, 0x78, 0x89, 0x11, \ + 0x00, 0x46, 0xef, 0xe4, \ + 0x42, 0xc6, 0xe1, 0x48, \ + 0x80, 0x24, 0xe1, 0x50, \ + 0x00, 0x56, 0x84, 0x44, \ + 0x4c, 0x01, 0xe0, 0x0c, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x92, 0x1b, 0x94, 0x0b, \ + 0x8e, 0x5b, 0x90, 0x3b, \ + 0xef, 0xd0, 0x55, 0x68, \ + 0x6e, 0xf9, 0x0c, 0x15, \ + 0xa1, 0x1b, 0xc0, 0x0b, \ + 0x0c, 0x39, 0x84, 0xc5, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x6e, 0x01, 0x0c, 0x4b, \ + 0x0c, 0x5b, 0xc0, 0x17, \ + 0xc0, 0x17, 0x6e, 0x09, \ + 0x6d, 0xf9, 0x0c, 0xcb, \ + 0x00, 0x60, 0xc0, 0x17, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xfb, 0x00, 0x04, 0x14, \ + 0x89, 0x11, 0xbc, 0xce, \ + 0xef, 0xe4, 0x4e, 0x88, \ + 0x0c, 0x0c, 0x90, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x00, 0x06, 0x80, 0x6b, \ + 0x80, 0x6e, 0xe1, 0x4c, \ + 0x67, 0x71, 0x10, 0x60, \ + 0x81, 0x71, 0xc0, 0x99, \ + 0x6e, 0x69, 0x0c, 0x09, \ + 0x40, 0x4a, 0xc0, 0x30, \ + 0x00, 0x06, 0xe0, 0x80, \ + 0x80, 0xa5, 0xe0, 0x10, \ + 0x8e, 0x1b, 0x8c, 0x0b, \ + 0xef, 0xe4, 0x41, 0x48, \ + 0x0e, 0x0a, 0x82, 0x71, \ + 0xe1, 0x0e, 0xe2, 0x30, \ + 0xbb, 0x01, 0xfc, 0x15, \ + 0xef, 0xf4, 0x73, 0x08, \ + 0x60, 0x01, 0x00, 0x0e, \ + 0x7c, 0xf8, 0xc0, 0x00, \ + 0x00, 0x06, 0xef, 0xe4, \ + 0x80, 0x14, 0xe1, 0x4c, \ + 0x40, 0xd8, 0x8c, 0x0b, \ + 0xbd, 0x01, 0xe8, 0x00, \ + 0xef, 0xf4, 0x72, 0x28, \ + 0x60, 0x01, 0x00, 0x06, \ + 0x0c, 0x0c, 0xc0, 0x01, \ + 0x3b, 0xf8, 0xf2, 0x0c, \ + 0xc1, 0xc3, 0xff, 0xe4, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x00, 0x16, 0x80, 0x6b, \ + 0x4c, 0x01, 0xe0, 0x0c, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xe1, 0x48, 0x00, 0x46, \ + 0x04, 0x60, 0x80, 0x04, \ + 0xc0, 0xbf, 0x64, 0x61, \ + 0x67, 0xf1, 0x06, 0x60, \ + 0x08, 0x60, 0xc0, 0xa7, \ + 0xc0, 0xae, 0x65, 0xd1, \ + 0x67, 0xe1, 0x0a, 0x60, \ + 0x18, 0x60, 0xc0, 0xb2, \ + 0xc0, 0x90, 0x64, 0xd1, \ + 0x64, 0xf1, 0x0e, 0x60, \ + 0xbe, 0x7e, 0xc0, 0x9e, \ + 0x4f, 0xf8, 0xbc, 0xce, \ + 0x0c, 0x15, 0xef, 0xcc, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe1, 0x8c, 0x02, 0x0a, \ + 0xfb, 0x01, 0x80, 0x55, \ + 0x84, 0x05, 0xa1, 0x0b, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x08, 0x60, 0xc0, 0x2f, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xfb, 0x00, 0x04, 0x04, \ + 0x88, 0x0b, 0x89, 0x11, \ + 0x0c, 0x0c, 0x98, 0xf1, \ + 0x09, 0xb8, 0xf2, 0x04, \ + 0x98, 0xf1, 0xff, 0xe4, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x81, 0x01, 0x80, 0x7b, \ + 0x6c, 0x98, 0x81, 0x11, \ + 0xfc, 0x0e, 0xef, 0xf0, \ + 0x63, 0x6d, 0x0e, 0x05, \ + 0x0e, 0x15, 0xc0, 0x0a, \ + 0xc0, 0x0b, 0x6d, 0x8d, \ + 0xe0, 0x04, 0x47, 0xc6, \ + 0x66, 0x71, 0x04, 0x70, \ + 0x4e, 0x31, 0xc0, 0xbd, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xef, 0xb8, 0x5f, 0x08, \ + 0x0e, 0x05, 0x80, 0x6b, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe0, 0xb8, 0x00, 0x0a, \ + 0xff, 0x61, 0x80, 0x55, \ + 0x82, 0x85, 0xa1, 0x6b, \ + 0x6e, 0x59, 0x0e, 0x39, \ + 0x00, 0x70, 0xc0, 0x2f, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xfb, 0x00, 0x04, 0x64, \ + 0x46, 0x78, 0x81, 0x11, \ + 0xbe, 0x6e, 0xef, 0xe4, \ + 0xe1, 0x04, 0x02, 0x46, \ + 0x6d, 0x9d, 0x0e, 0x05, \ + 0x84, 0x14, 0xc0, 0x0b, \ + 0x0e, 0x15, 0x84, 0x0e, \ + 0xc0, 0x0a, 0x63, 0x6d, \ + 0xef, 0xb8, 0x5e, 0xa8, \ + 0x64, 0x01, 0x12, 0x00, \ + 0xe1, 0x97, 0xc0, 0x80, \ + 0x5c, 0x38, 0x92, 0x0b, \ + 0x8e, 0x0b, 0xef, 0xd4, \ + 0x64, 0xb8, 0x92, 0x1b, \ + 0x8e, 0x0b, 0xef, 0xd4, \ + 0x5d, 0xb8, 0x92, 0x1b, \ + 0x8e, 0x0b, 0xef, 0xd4, \ + 0x60, 0x78, 0x92, 0x1b, \ + 0x8e, 0x0b, 0xef, 0xd4, \ + 0xef, 0xd4, 0x63, 0x08, \ + 0xe1, 0x04, 0x00, 0x86, \ + 0x68, 0x19, 0x0e, 0x1b, \ + 0x80, 0x04, 0xc0, 0x18, \ + 0x00, 0xc6, 0x80, 0x1c, \ + 0x0e, 0x1b, 0xe1, 0x04, \ + 0xc0, 0x18, 0x68, 0x1d, \ + 0x80, 0x1c, 0x80, 0x04, \ + 0xe1, 0x00, 0x40, 0xc6, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xc2, 0x19, \ + 0xe1, 0x00, 0x41, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xd0, 0x19, \ + 0x83, 0x21, 0xa9, 0x11, \ + 0x70, 0x58, 0x8e, 0x0b, \ + 0x01, 0x06, 0xe8, 0x1c, \ + 0x0e, 0x19, 0xe1, 0x10, \ + 0xc0, 0x31, 0x60, 0x51, \ + 0x80, 0x16, 0x80, 0x04, \ + 0xe1, 0x00, 0x41, 0x06, \ + 0x89, 0x21, 0x80, 0x14, \ + 0x80, 0x14, 0x84, 0x26, \ + 0x84, 0x26, 0x8f, 0x21, \ + 0x02, 0x76, 0x80, 0x04, \ + 0x80, 0x16, 0xe0, 0x04, \ + 0xe0, 0x3c, 0x42, 0x86, \ + 0x8e, 0x0b, 0x81, 0x21, \ + 0xe8, 0x1c, 0x6e, 0xc8, \ + 0xe1, 0x40, 0x4c, 0x46, \ + 0x56, 0x06, 0x98, 0x04, \ + 0x80, 0x08, 0xe1, 0x00, \ + 0x84, 0x18, 0xac, 0x14, \ + 0xe0, 0x90, 0x42, 0x4a, \ + 0xc7, 0x07, 0x88, 0xa5, \ + 0xf0, 0x04, 0x50, 0x00, \ + 0x66, 0xc1, 0x14, 0x70, \ + 0x10, 0x0a, 0xc0, 0x56, \ + 0x80, 0xe5, 0xfb, 0xfc, \ + 0x65, 0x61, 0x0e, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x95, 0xe0, 0xbc, \ + 0xe8, 0x1c, 0x76, 0x08, \ + 0x6e, 0x59, 0x0e, 0x39, \ + 0x83, 0x11, 0xc0, 0x2f, \ + 0x94, 0x0b, 0x83, 0x21, \ + 0xef, 0xe0, 0x7e, 0x88, \ + 0x98, 0x04, 0x81, 0x65, \ + 0xc7, 0x07, 0x80, 0x08, \ + 0xf0, 0x04, 0x50, 0x00, \ + 0x80, 0x08, 0xac, 0x04, \ + 0xe0, 0x90, 0x40, 0x0a, \ + 0x41, 0x06, 0xf8, 0x65, \ + 0x80, 0x04, 0xe1, 0x14, \ + 0x80, 0x66, 0x83, 0x61, \ + 0xe1, 0x14, 0x40, 0xc6, \ + 0x80, 0x08, 0x80, 0x04, \ + 0x40, 0x9a, 0x82, 0x01, \ + 0x4e, 0x03, 0xe0, 0x02, \ + 0xc0, 0x17, 0x6f, 0x21, \ + 0xef, 0xe4, 0x73, 0x88, \ + 0xe1, 0x14, 0x40, 0x46, \ + 0x80, 0x66, 0x80, 0x04, \ + 0xe8, 0x1c, 0x73, 0x68, \ + 0xef, 0xd0, 0x75, 0x48, \ + 0xfc, 0x04, 0x83, 0x11, \ + 0xef, 0xf0, 0x5f, 0xc8, \ + 0x98, 0xf1, 0xbe, 0x04, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x0c, 0x01, 0x80, 0x6b, \ + 0xc0, 0x2f, 0x66, 0xb5, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0xe0, 0x04, 0x43, 0x26, \ + 0x6e, 0x59, 0x4c, 0x11, \ + 0x82, 0x55, 0xc0, 0x2f, \ + 0x65, 0x79, 0x0c, 0x19, \ + 0xa3, 0x1b, 0xc0, 0x31, \ + 0x0c, 0x09, 0x82, 0x35, \ + 0xc0, 0x27, 0x6b, 0xd9, \ + 0x0c, 0x09, 0x87, 0x65, \ + 0xc0, 0x2f, 0x66, 0xa9, \ + 0x00, 0x8a, 0x87, 0x25, \ + 0x80, 0xf5, 0xf7, 0x00, \ + 0x6b, 0xdd, 0x0c, 0x19, \ + 0xa1, 0x1b, 0xc0, 0x27, \ + 0xff, 0x05, 0x84, 0x35, \ + 0x40, 0x4a, 0xff, 0x11, \ + 0x82, 0x45, 0xe0, 0x98, \ + 0x82, 0xf5, 0x82, 0x0d, \ + 0x6e, 0xe5, 0x0c, 0x19, \ + 0x02, 0x0a, 0xc0, 0x30, \ + 0x80, 0x95, 0xfb, 0xfc, \ + 0x82, 0x0d, 0x81, 0x85, \ + 0x0c, 0x19, 0x80, 0xd5, \ + 0xc0, 0x27, 0x6b, 0xdd, \ + 0x84, 0x65, 0xa1, 0x1b, \ + 0xe0, 0x94, 0x40, 0x0a, \ + 0xeb, 0xd8, 0x40, 0xc6, \ + 0x41, 0x06, 0x80, 0x65, \ + 0x42, 0xa6, 0xea, 0x34, \ + 0x80, 0x65, 0xeb, 0xd8, \ + 0xea, 0x34, 0x43, 0x16, \ + 0xfc, 0x04, 0x0c, 0x19, \ + 0xfc, 0x00, 0x0c, 0x09, \ + 0x81, 0x35, 0x82, 0x03, \ + 0xe0, 0x00, 0x40, 0x46, \ + 0xe1, 0x00, 0x42, 0x86, \ + 0x84, 0x06, 0x84, 0x14, \ + 0xa1, 0x0b, 0x84, 0x08, \ + 0x84, 0x08, 0x80, 0xa5, \ + 0xe5, 0x04, 0x00, 0x8a, \ + 0x40, 0x46, 0x80, 0x95, \ + 0x84, 0x06, 0xe0, 0x00, \ + 0xe1, 0x00, 0x4e, 0x46, \ + 0x80, 0x18, 0x9c, 0x04, \ + 0xf3, 0xdc, 0x42, 0x10, \ + 0x9c, 0x04, 0x80, 0x16, \ + 0xc2, 0x19, 0x80, 0x18, \ + 0x87, 0x11, 0x80, 0x16, \ + 0x8c, 0x0b, 0x83, 0x21, \ + 0xe8, 0x1c, 0x62, 0xe8, \ + 0x80, 0x18, 0x9c, 0x04, \ + 0x80, 0x16, 0xc4, 0x19, \ + 0x83, 0x21, 0x99, 0x11, \ + 0x62, 0x58, 0x8c, 0x0b, \ + 0x9c, 0x04, 0xe8, 0x1c, \ + 0xc8, 0x19, 0x80, 0x18, \ + 0x88, 0xf1, 0x80, 0x16, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x81, 0x81, 0x80, 0x6b, \ + 0x81, 0x11, 0x81, 0x01, \ + 0xef, 0xf0, 0x56, 0xe8, \ + 0x41, 0x06, 0x80, 0x7b, \ + 0x80, 0x04, 0xe1, 0x14, \ + 0xe0, 0x04, 0x43, 0xd6, \ + 0x6e, 0x59, 0x4c, 0x11, \ + 0x80, 0x86, 0xc0, 0x2f, \ + 0x63, 0x6d, 0x0c, 0x05, \ + 0x50, 0x06, 0xc0, 0x0a, \ + 0xd9, 0x07, 0xe1, 0x38, \ + 0x82, 0x01, 0xa0, 0x14, \ + 0x6f, 0x21, 0x4c, 0x03, \ + 0x84, 0x08, 0xc0, 0x17, \ + 0xf3, 0x7c, 0x40, 0x00, \ + 0x41, 0x06, 0x84, 0x06, \ + 0x80, 0x14, 0xe1, 0x00, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x84, 0x26, 0xf3, 0xec, \ + 0x84, 0x28, 0x80, 0x14, \ + 0xf2, 0xfc, 0x44, 0x20, \ + 0x80, 0x14, 0x84, 0x26, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x84, 0x26, 0xf3, 0xf4, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xf8, 0x42, 0x10, \ + 0x40, 0xc6, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x00, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xf8, \ + 0xe1, 0x00, 0x41, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xdc, 0x42, 0x10, \ + 0x66, 0xf8, 0x80, 0x16, \ + 0xa0, 0x04, 0xef, 0xe4, \ + 0x02, 0x14, 0x80, 0x18, \ + 0x80, 0x16, 0xfc, 0x94, \ + 0xef, 0xd0, 0x67, 0xc8, \ + 0x8e, 0x0b, 0x83, 0x11, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0xff, 0xf0, 0x12, 0x28, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x40, 0xc6, 0x80, 0x6b, \ + 0x80, 0x04, 0xe1, 0x04, \ + 0xc2, 0x19, 0x80, 0x18, \ + 0x42, 0x46, 0x80, 0x16, \ + 0x81, 0x21, 0xe0, 0x04, \ + 0x5b, 0x78, 0x8c, 0x0b, \ + 0x50, 0x46, 0xe8, 0x1c, \ + 0xa0, 0x04, 0xe1, 0x40, \ + 0xe1, 0x00, 0x52, 0x06, \ + 0xa4, 0x14, 0x80, 0x08, \ + 0x42, 0x4a, 0x84, 0x18, \ + 0x88, 0xa5, 0xe0, 0x98, \ + 0x54, 0x00, 0xc7, 0x07, \ + 0x0e, 0x60, 0xf0, 0x04, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xfb, 0xfc, 0x14, 0x0a, \ + 0x0c, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe0, 0xc0, 0x00, 0x0a, \ + 0x62, 0xb8, 0x82, 0x95, \ + 0x0c, 0x39, 0xe8, 0x1c, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xa9, 0x21, 0x85, 0x11, \ + 0x6b, 0x38, 0x8e, 0x0b, \ + 0x81, 0x65, 0xef, 0xe0, \ + 0x80, 0x08, 0xa0, 0x04, \ + 0x54, 0x00, 0xc7, 0x07, \ + 0xa4, 0x04, 0xf0, 0x04, \ + 0x40, 0x0a, 0x80, 0x08, \ + 0xf8, 0x65, 0xe0, 0x98, \ + 0xe8, 0x1c, 0x61, 0x68, \ + 0xef, 0xe4, 0x5f, 0x08, \ + 0xef, 0xd0, 0x63, 0x28, \ + 0x8c, 0x1b, 0x81, 0x01, \ + 0xf2, 0x3c, 0x0c, 0x0c, \ + 0xff, 0xd4, 0x0b, 0xc8, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x40, 0xc6, 0x80, 0x6b, \ + 0x80, 0x04, 0xe1, 0x04, \ + 0x80, 0x16, 0x81, 0x11, \ + 0xef, 0xe4, 0x5e, 0xc8, \ + 0xef, 0xd0, 0x61, 0xe8, \ + 0x8c, 0x1b, 0x81, 0x01, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0xd4, 0x0a, 0x88, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x81, 0x71, 0x80, 0x6b, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x73, 0xb8, 0x81, 0x11, \ + 0x41, 0x66, 0xef, 0x84, \ + 0x4b, 0xb8, 0xe0, 0x00, \ + 0x00, 0xc6, 0xef, 0xf4, \ + 0x80, 0x04, 0xe1, 0x44, \ + 0x5b, 0x58, 0x80, 0x76, \ + 0x5e, 0xb8, 0xef, 0xe4, \ + 0x60, 0x98, 0xef, 0xe4, \ + 0x5c, 0x78, 0xef, 0xe4, \ + 0x4c, 0x71, 0xef, 0xe4, \ + 0xc0, 0x2f, 0x6e, 0x55, \ + 0x8c, 0x1b, 0x81, 0x01, \ + 0xef, 0xd4, 0x48, 0x48, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x71, 0xf8, 0x83, 0x11, \ + 0x88, 0xf1, 0xef, 0x84, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0xff, 0xd0, 0x20, 0x68, \ + 0xe1, 0x2c, 0x40, 0x46, \ + 0x42, 0x86, 0x80, 0x04, \ + 0x80, 0x04, 0xe1, 0x2c, \ + 0x44, 0xc6, 0x84, 0x14, \ + 0x84, 0x14, 0xe1, 0x2c, \ + 0x47, 0x06, 0x88, 0x24, \ + 0x88, 0x24, 0xe1, 0x2c, \ + 0x49, 0x46, 0x8c, 0x34, \ + 0x8c, 0x34, 0xe1, 0x2c, \ + 0x4b, 0x86, 0x90, 0x44, \ + 0x90, 0x44, 0xe1, 0x2c, \ + 0x59, 0xc6, 0x94, 0x54, \ + 0x94, 0x54, 0xe1, 0x2c, \ + 0x80, 0x13, 0xb0, 0xc4, \ + 0xe1, 0x30, 0x00, 0x06, \ + 0x80, 0x04, 0xb0, 0xc4, \ + 0x86, 0x13, 0x84, 0x13, \ + 0x80, 0x04, 0x88, 0x13, \ + 0x98, 0x13, 0x8a, 0x13, \ + 0xdd, 0x93, 0x82, 0x03, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x40, 0xc6, 0x80, 0x6b, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x63, 0x69, 0x0c, 0x15, \ + 0x04, 0xc6, 0xc0, 0x0a, \ + 0x80, 0x08, 0xe1, 0x24, \ + 0x88, 0x34, 0x88, 0x24, \ + 0x44, 0x00, 0xc5, 0x07, \ + 0x8c, 0x0b, 0xf0, 0x0c, \ + 0xe8, 0x20, 0x54, 0x68, \ + 0x6e, 0xe5, 0x0c, 0x19, \ + 0x4c, 0x05, 0xc0, 0x30, \ + 0xc0, 0x05, 0x69, 0xd1, \ + 0x4c, 0x01, 0x81, 0x01, \ + 0xc0, 0x16, 0x67, 0x59, \ + 0x67, 0x5d, 0x4c, 0x11, \ + 0x0c, 0x0c, 0xc0, 0x16, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe1, 0x14, 0x43, 0x86, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x69, 0xcd, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x23, \ + 0xe1, 0x14, 0x43, 0x86, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x69, 0xed, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x1c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x01, 0xc6, 0x80, 0x6b, \ + 0x80, 0x04, 0xe1, 0x24, \ + 0x43, 0x86, 0x80, 0x04, \ + 0x84, 0x14, 0xe1, 0x14, \ + 0x4c, 0x05, 0x82, 0x01, \ + 0xc0, 0x08, 0x6e, 0x61, \ + 0x91, 0x11, 0x84, 0x08, \ + 0xef, 0xb4, 0x78, 0xc8, \ + 0x69, 0xb9, 0x4c, 0x01, \ + 0x0c, 0x0c, 0xc0, 0x23, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x01, 0xc6, 0x80, 0x6b, \ + 0x80, 0x04, 0xe1, 0x24, \ + 0x43, 0x86, 0x80, 0x04, \ + 0x84, 0x14, 0xe1, 0x14, \ + 0x4c, 0x05, 0x82, 0x01, \ + 0xc0, 0x07, 0x62, 0x69, \ + 0x91, 0x11, 0x84, 0x08, \ + 0xef, 0xb4, 0x77, 0x48, \ + 0x6e, 0xe9, 0x0c, 0x19, \ + 0x4c, 0x01, 0xc0, 0x30, \ + 0xc0, 0x1c, 0x69, 0xd9, \ + 0x69, 0xcd, 0x4c, 0x11, \ + 0x0c, 0x0c, 0xc0, 0x1c, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x41, 0x46, 0x80, 0x6b, \ + 0x80, 0x04, 0xe1, 0x30, \ + 0x0c, 0x19, 0x80, 0x04, \ + 0xc0, 0x23, 0x69, 0xc9, \ + 0x69, 0xb9, 0x0c, 0x29, \ + 0x4c, 0x05, 0xc0, 0x23, \ + 0xc0, 0x08, 0x6e, 0x79, \ + 0x45, 0x38, 0x8b, 0x31, \ + 0x43, 0xf6, 0xef, 0xb8, \ + 0x4c, 0x05, 0xe0, 0x0c, \ + 0xc0, 0x08, 0x6e, 0x7d, \ + 0x6a, 0x01, 0x4c, 0x11, \ + 0x0c, 0x0c, 0xc0, 0x23, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x72, 0xd8, 0x80, 0x6b, \ + 0x8c, 0x0b, 0xe8, 0x20, \ + 0xe8, 0x00, 0x41, 0xe8, \ + 0x43, 0xf8, 0x8c, 0x0b, \ + 0xa1, 0x71, 0xe8, 0x00, \ + 0x67, 0xf1, 0x10, 0x60, \ + 0x10, 0x05, 0xc0, 0x72, \ + 0x0c, 0x19, 0xf8, 0x40, \ + 0xc0, 0x1c, 0x69, 0xe9, \ + 0x69, 0xd9, 0x0c, 0x29, \ + 0x8b, 0x31, 0xc0, 0x1c, \ + 0xef, 0xb8, 0x42, 0xc8, \ + 0xec, 0x04, 0x0e, 0x70, \ + 0xe1, 0x0e, 0xa1, 0x7b, \ + 0x0c, 0x0c, 0xfc, 0x35, \ + 0xdd, 0x93, 0xf2, 0x0c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x02, 0x06, 0x80, 0x6b, \ + 0x4d, 0xd8, 0xe0, 0x04, \ + 0xa1, 0x71, 0xe8, 0x20, \ + 0x64, 0xf1, 0x10, 0x60, \ + 0x8c, 0x9b, 0xc0, 0x73, \ + 0x8c, 0x0b, 0xe1, 0x18, \ + 0xef, 0xe0, 0x40, 0x88, \ + 0xec, 0x04, 0x0e, 0x70, \ + 0xe5, 0x0e, 0xa1, 0x7b, \ + 0x00, 0x60, 0xfe, 0x15, \ + 0xc0, 0x72, 0x65, 0xf1, \ + 0xe0, 0x04, 0x04, 0x06, \ + 0x8c, 0x1b, 0x81, 0x31, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0x29, 0xf8, 0xf2, 0x1c, \ + 0xc1, 0xc3, 0xe0, 0x0c, \ + 0x66, 0xf1, 0x00, 0x00, \ + 0x02, 0x06, 0xc0, 0x72, \ + 0xc0, 0x20, 0x63, 0x41, \ + 0xe0, 0x04, 0x04, 0x06, \ + 0x29, 0x38, 0x81, 0x31, \ + 0xc1, 0xc3, 0xe0, 0x0c, \ + 0x66, 0x11, 0x00, 0x00, \ + 0x02, 0x06, 0xc0, 0x8e, \ + 0xc0, 0x20, 0x63, 0x1d, \ + 0xe0, 0x24, 0x04, 0x06, \ + 0x28, 0x78, 0x87, 0x31, \ + 0xc1, 0xc3, 0xe0, 0x0c, \ + 0x65, 0x71, 0x00, 0x00, \ + 0x02, 0x06, 0xc0, 0x73, \ + 0xc0, 0x20, 0x61, 0x01, \ + 0xe2, 0x40, 0x04, 0x06, \ + 0x27, 0xb8, 0x87, 0x31, \ + 0xc1, 0xc3, 0xe0, 0x0c, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x6d, 0x81, 0x1e, 0xf0, \ + 0x80, 0x6b, 0xc0, 0x04, \ + 0xe1, 0x30, 0x00, 0xc6, \ + 0x0c, 0x29, 0x80, 0x04, \ + 0xc0, 0x24, 0x60, 0xdd, \ + 0x64, 0x2d, 0x0c, 0x39, \ + 0x02, 0x46, 0xc0, 0x24, \ + 0x85, 0x41, 0xe1, 0x30, \ + 0x4c, 0x41, 0x84, 0x14, \ + 0xc0, 0x24, 0x60, 0xd9, \ + 0x82, 0x31, 0x82, 0x21, \ + 0x60, 0xdd, 0x4c, 0x21, \ + 0x4c, 0x31, 0xc0, 0x24, \ + 0xc0, 0x24, 0x64, 0x2d, \ + 0x06, 0xf0, 0x81, 0x21, \ + 0xc0, 0x02, 0x64, 0xc1, \ + 0x9d, 0x51, 0x9e, 0x4b, \ + 0x81, 0x71, 0x95, 0xc1, \ + 0xfa, 0x80, 0x10, 0x24, \ + 0xe1, 0x16, 0x50, 0x8a, \ + 0xe6, 0xb2, 0x52, 0x8a, \ + 0x12, 0x94, 0x8e, 0x93, \ + 0xc4, 0x97, 0xfa, 0x80, \ + 0xfc, 0x24, 0x00, 0xa5, \ + 0x02, 0x95, 0xcd, 0xae, \ + 0x82, 0x21, 0xfc, 0x24, \ + 0x04, 0x1a, 0xbb, 0x8b, \ + 0x10, 0x0c, 0xe2, 0x30, \ + 0xd1, 0x9e, 0xe2, 0x00, \ + 0x3e, 0x98, 0x90, 0x73, \ + 0xc3, 0xff, 0x3f, 0xfd, \ + 0x65, 0xf1, 0x00, 0x60, \ + 0x02, 0xf0, 0xc0, 0x95, \ + 0xc0, 0x02, 0x64, 0xc1, \ + 0x45, 0x06, 0x81, 0x71, \ + 0x81, 0x31, 0xe0, 0x20, \ + 0xe8, 0x0c, 0x62, 0xe8, \ + 0x66, 0xb1, 0x00, 0x60, \ + 0x9e, 0x1b, 0xc0, 0x97, \ + 0xe0, 0x20, 0x45, 0x06, \ + 0x62, 0x58, 0x81, 0x31, \ + 0x40, 0x46, 0xe8, 0x0c, \ + 0x03, 0xc6, 0xe1, 0x2c, \ + 0x80, 0x04, 0xe1, 0x2c, \ + 0x04, 0x60, 0x84, 0x14, \ + 0xc0, 0x90, 0x66, 0xa1, \ + 0x48, 0x70, 0x81, 0x31, \ + 0x0a, 0x44, 0xf0, 0x0c, \ + 0x86, 0x53, 0xe0, 0x84, \ + 0xfa, 0x80, 0x0a, 0x54, \ + 0x00, 0xc5, 0xc4, 0x57, \ + 0x44, 0xc5, 0xfc, 0x14, \ + 0x02, 0x55, 0xf8, 0x20, \ + 0x82, 0x71, 0xfc, 0x14, \ + 0x0e, 0x1a, 0xa7, 0x4b, \ + 0x08, 0x0c, 0xe0, 0x20, \ + 0xc9, 0x5e, 0xe2, 0x00, \ + 0x3e, 0xa8, 0x88, 0x33, \ + 0xc3, 0xff, 0x3f, 0xfd, \ + 0x65, 0x81, 0x1e, 0xf0, \ + 0x0c, 0x0c, 0xc0, 0x04, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xe1, 0x14, 0x43, 0x86, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x61, 0x1d, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x24, \ + 0xe1, 0x14, 0x43, 0x86, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x64, 0x6d, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x24, \ + 0x63, 0xf5, 0x00, 0x15, \ + 0x40, 0x15, 0xc0, 0x0c, \ + 0xc0, 0x09, 0x60, 0x3d, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x63, 0xf5, 0x00, 0x15, \ + 0x40, 0x15, 0xc0, 0x0c, \ + 0xc0, 0x09, 0x61, 0x11, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x81, 0x01, 0x80, 0x7b, \ + 0x6c, 0xf8, 0x81, 0x11, \ + 0x80, 0x6b, 0xef, 0xec, \ + 0xe1, 0x0c, 0x40, 0x86, \ + 0x43, 0x16, 0x80, 0x04, \ + 0x4e, 0x11, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x80, 0x86, 0x83, 0x81, \ + 0xe1, 0x04, 0x41, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xc2, 0x19, \ + 0xe1, 0x04, 0x41, 0x86, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xc2, 0x19, \ + 0x59, 0x58, 0x8e, 0x0b, \ + 0x8e, 0x0b, 0xef, 0xfc, \ + 0xef, 0xfc, 0x4b, 0xe8, \ + 0x44, 0x78, 0x8e, 0x0b, \ + 0x00, 0x86, 0xe8, 0x00, \ + 0x80, 0x04, 0xe1, 0x14, \ + 0xc2, 0x19, 0x80, 0x18, \ + 0x01, 0x86, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x04, \ + 0x01, 0xc6, 0x80, 0x86, \ + 0x80, 0x04, 0xe1, 0x14, \ + 0x40, 0x46, 0x80, 0x86, \ + 0x80, 0x04, 0xe1, 0x14, \ + 0x41, 0x86, 0x80, 0x86, \ + 0x0e, 0x19, 0xe1, 0x08, \ + 0xc0, 0x31, 0x60, 0x1d, \ + 0x80, 0x16, 0x80, 0x04, \ + 0xe1, 0x08, 0x41, 0x46, \ + 0x60, 0x25, 0x0e, 0x19, \ + 0x80, 0x04, 0xc0, 0x31, \ + 0x01, 0x06, 0x80, 0x16, \ + 0x0e, 0x19, 0xe1, 0x08, \ + 0xc0, 0x31, 0x60, 0x21, \ + 0x80, 0x16, 0x80, 0x04, \ + 0xe1, 0x08, 0x00, 0xc6, \ + 0x60, 0x29, 0x0e, 0x19, \ + 0x80, 0x04, 0xc0, 0x31, \ + 0x00, 0x86, 0x80, 0x16, \ + 0x0e, 0x19, 0xe1, 0x44, \ + 0xc0, 0x31, 0x60, 0x19, \ + 0x80, 0x16, 0x80, 0x04, \ + 0xe0, 0x04, 0x41, 0x76, \ + 0x6e, 0x59, 0x4e, 0x01, \ + 0x41, 0xf6, 0xc0, 0x2f, \ + 0x81, 0x11, 0xef, 0xfc, \ + 0xe8, 0x00, 0x47, 0x88, \ + 0x8c, 0x0b, 0x83, 0x11, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0xff, 0xec, 0x26, 0xa8, \ + 0xe1, 0x0c, 0x43, 0xc6, \ + 0x60, 0x41, 0x00, 0x29, \ + 0x84, 0x14, 0xc0, 0x31, \ + 0x02, 0x06, 0x84, 0x26, \ + 0x00, 0x29, 0xe1, 0x10, \ + 0xc0, 0x31, 0x60, 0x45, \ + 0x84, 0x26, 0x84, 0x14, \ + 0xe1, 0x10, 0x02, 0xc6, \ + 0x60, 0x49, 0x00, 0x29, \ + 0x84, 0x14, 0xc0, 0x31, \ + 0x02, 0x86, 0x84, 0x26, \ + 0x00, 0x29, 0xe1, 0x10, \ + 0xc0, 0x31, 0x60, 0x11, \ + 0x84, 0x26, 0x84, 0x14, \ + 0x6d, 0xed, 0x00, 0x29, \ + 0x02, 0x46, 0xc0, 0x2f, \ + 0x84, 0x14, 0xe1, 0x10, \ + 0xf0, 0xfc, 0x44, 0x20, \ + 0x00, 0x29, 0x84, 0x26, \ + 0xc0, 0x31, 0x60, 0x69, \ + 0x82, 0x25, 0xa1, 0x2b, \ + 0x6d, 0xed, 0x00, 0x39, \ + 0x46, 0x30, 0xc0, 0x2f, \ + 0xc4, 0x3d, 0xf0, 0xfc, \ + 0x84, 0x26, 0x80, 0x45, \ + 0xe1, 0x10, 0x03, 0xc6, \ + 0x60, 0x61, 0x00, 0x29, \ + 0x84, 0x14, 0xc0, 0x31, \ + 0x42, 0x46, 0x84, 0x26, \ + 0x00, 0x29, 0xe1, 0x10, \ + 0xc0, 0x31, 0x60, 0x65, \ + 0x84, 0x26, 0x84, 0x14, \ + 0xe1, 0x10, 0x42, 0x06, \ + 0x60, 0x15, 0x00, 0x29, \ + 0x84, 0x14, 0xc0, 0x31, \ + 0x42, 0x86, 0x84, 0x26, \ + 0x00, 0x29, 0xe1, 0x10, \ + 0xc0, 0x31, 0x60, 0x4d, \ + 0x84, 0x26, 0x84, 0x14, \ + 0x60, 0x6d, 0x00, 0x19, \ + 0x45, 0x86, 0xc0, 0x31, \ + 0x88, 0x24, 0xe1, 0x0c, \ + 0xe0, 0x00, 0x02, 0x4a, \ + 0xe2, 0x00, 0x06, 0x0c, \ + 0x05, 0x86, 0x88, 0x36, \ + 0x88, 0x24, 0xe1, 0x10, \ + 0x02, 0x0c, 0xa1, 0x1b, \ + 0x88, 0x16, 0xe2, 0x00, \ + 0xe1, 0x10, 0x05, 0x46, \ + 0x88, 0x16, 0x88, 0x24, \ + 0xe1, 0x10, 0x45, 0x06, \ + 0x88, 0x16, 0x88, 0x24, \ + 0xe1, 0x0c, 0x42, 0xc6, \ + 0x60, 0x5d, 0x00, 0x09, \ + 0x84, 0x14, 0xc0, 0x31, \ + 0xdd, 0x93, 0x84, 0x06, \ + 0xe1, 0x18, 0x44, 0x46, \ + 0x88, 0x16, 0x88, 0x24, \ + 0xe1, 0x18, 0x44, 0x86, \ + 0x88, 0x16, 0x88, 0x24, \ + 0xe1, 0x18, 0x44, 0xc6, \ + 0x88, 0x16, 0x88, 0x24, \ + 0xe1, 0x18, 0x45, 0x06, \ + 0x88, 0x16, 0x88, 0x24, \ + 0xe1, 0x18, 0x45, 0x46, \ + 0x88, 0x16, 0x88, 0x24, \ + 0xe1, 0x18, 0x45, 0x86, \ + 0x88, 0x16, 0x88, 0x24, \ + 0xe1, 0x18, 0x45, 0xc6, \ + 0x88, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x04, 0x06, \ + 0x88, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x04, 0x46, \ + 0x88, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x04, 0x86, \ + 0x88, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x04, 0xc6, \ + 0x88, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x05, 0x06, \ + 0x88, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x05, 0x46, \ + 0x88, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x05, 0x86, \ + 0x88, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x05, 0xc6, \ + 0x88, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x44, 0x06, \ + 0x88, 0x16, 0x88, 0x24, \ + 0xe1, 0x14, 0x03, 0x06, \ + 0x84, 0x0c, 0x84, 0x14, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x81, 0x81, 0x80, 0x7b, \ + 0x81, 0x11, 0x81, 0x01, \ + 0xef, 0xec, 0x5a, 0xa8, \ + 0x4a, 0xf8, 0x80, 0x6b, \ + 0x52, 0x06, 0xe8, 0x00, \ + 0xa4, 0x04, 0xe1, 0x38, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xbc, \ + 0xe8, 0x04, 0x6c, 0x68, \ + 0x95, 0x11, 0x83, 0xa1, \ + 0x8e, 0x0b, 0x83, 0x21, \ + 0xe8, 0x18, 0x63, 0xc8, \ + 0xe1, 0x10, 0x01, 0x46, \ + 0x80, 0x86, 0x80, 0x04, \ + 0xa4, 0x04, 0x80, 0xa6, \ + 0xe0, 0x19, 0x80, 0x18, \ + 0x8e, 0x0b, 0x80, 0x16, \ + 0xe8, 0x00, 0x43, 0xc8, \ + 0xe8, 0x00, 0x45, 0xa8, \ + 0xe8, 0x00, 0x46, 0xc8, \ + 0xe1, 0x0c, 0x41, 0x46, \ + 0x80, 0x86, 0x80, 0x04, \ + 0xe1, 0x04, 0x01, 0x86, \ + 0x80, 0x86, 0x80, 0x04, \ + 0x6e, 0x51, 0x0e, 0x09, \ + 0x40, 0x4a, 0xc0, 0x2f, \ + 0x80, 0x85, 0xe0, 0x80, \ + 0x4a, 0x38, 0x8e, 0x0b, \ + 0x41, 0x46, 0xef, 0xfc, \ + 0x80, 0x04, 0xe1, 0x04, \ + 0x41, 0x86, 0x80, 0x86, \ + 0x80, 0x04, 0xe1, 0x04, \ + 0x00, 0xc6, 0x80, 0x86, \ + 0x80, 0x04, 0xe1, 0x08, \ + 0x41, 0x46, 0x80, 0x86, \ + 0x80, 0x04, 0xe1, 0x08, \ + 0x01, 0x86, 0x80, 0x86, \ + 0x80, 0x04, 0xe1, 0x10, \ + 0x40, 0x46, 0x80, 0x86, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0x41, 0x06, 0x80, 0x86, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0x83, 0x11, 0x80, 0x86, \ + 0x0c, 0x0c, 0x8c, 0x0b, \ + 0x15, 0x58, 0xf2, 0x3c, \ + 0x00, 0x0c, 0xff, 0xec, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0xe1, 0x00, 0x4c, 0x46, \ + 0x45, 0x36, 0x98, 0x14, \ + 0x40, 0x21, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x84, 0x26, 0xf3, 0xec, \ + 0x84, 0x28, 0x98, 0x14, \ + 0xf3, 0xf8, 0x44, 0x20, \ + 0x43, 0x46, 0x84, 0x26, \ + 0x83, 0x21, 0xe0, 0x1c, \ + 0xe8, 0x18, 0x5d, 0xe8, \ + 0x80, 0x18, 0x98, 0x04, \ + 0xf3, 0xf4, 0x42, 0x10, \ + 0x0c, 0x0c, 0x80, 0x16, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe1, 0x28, 0x40, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xf8, 0x42, 0x10, \ + 0x00, 0x86, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x18, \ + 0x80, 0x16, 0x81, 0x11, \ + 0xe1, 0x14, 0x00, 0x86, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf2, 0xfc, 0x42, 0x10, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xe1, 0x14, 0x00, 0xc6, \ + 0x81, 0x11, 0x80, 0x04, \ + 0x01, 0x06, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x14, \ + 0x01, 0x86, 0x80, 0x1c, \ + 0x80, 0x04, 0xe1, 0x14, \ + 0x01, 0xc6, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x14, \ + 0x40, 0x46, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x14, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xe1, 0x20, 0x43, 0x46, \ + 0x04, 0x76, 0x84, 0x14, \ + 0x83, 0x31, 0xe0, 0x08, \ + 0x6e, 0x59, 0x40, 0x21, \ + 0x84, 0x36, 0xc0, 0x2f, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xe1, 0x20, 0x41, 0x46, \ + 0x81, 0x11, 0x80, 0x04, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x40, 0xd8, 0x80, 0x6b, \ + 0x85, 0x11, 0xe8, 0x00, \ + 0x41, 0xd8, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xe8, 0x18, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xfd, 0x05, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x00, 0x00, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0xe0, 0x08, 0x02, 0xc6, \ + 0x6e, 0x59, 0x40, 0x11, \ + 0x44, 0xf8, 0xc0, 0x2f, \ + 0x88, 0xf1, 0xe8, 0x00, \ + 0xf2, 0x00, 0x08, 0x0c, \ + 0xc1, 0xc3, 0xfb, 0xa5, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x82, 0x9b, 0x84, 0x3b, \ + 0x81, 0x61, 0x80, 0x7b, \ + 0x86, 0x1b, 0x81, 0x21, \ + 0xe8, 0x18, 0x56, 0xa8, \ + 0x66, 0xc1, 0x10, 0x70, \ + 0x14, 0xc6, 0xc0, 0x56, \ + 0x56, 0x46, 0xe1, 0x2c, \ + 0x01, 0x86, 0xe1, 0x40, \ + 0x80, 0x04, 0xe1, 0x24, \ + 0xa8, 0x14, 0x80, 0x08, \ + 0x84, 0x18, 0x84, 0x28, \ + 0xc5, 0x07, 0xac, 0x34, \ + 0x82, 0x09, 0x8c, 0x38, \ + 0x46, 0x0a, 0xac, 0x0f, \ + 0xc7, 0x27, 0xe0, 0x8c, \ + 0xc4, 0x17, 0x84, 0x29, \ + 0x88, 0x19, 0xa4, 0x0f, \ + 0xf9, 0x84, 0x4c, 0x02, \ + 0x0e, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe0, 0xcc, 0x00, 0x0a, \ + 0x0e, 0x39, 0x82, 0x35, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x91, 0x21, 0x81, 0x11, \ + 0x65, 0xf8, 0x90, 0x0b, \ + 0x92, 0x6f, 0xef, 0xdc, \ + 0xf6, 0x55, 0x93, 0x6b, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0x1c, 0x78, 0xf2, 0x7c, \ + 0xc1, 0xc3, 0xe0, 0x18, \ + 0x02, 0x86, 0x82, 0x2b, \ + 0x40, 0x11, 0xe0, 0x08, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xf7, 0x95, 0x8f, 0x11, \ + 0xe1, 0x14, 0x01, 0xc6, \ + 0x81, 0x21, 0x80, 0x14, \ + 0x02, 0x86, 0x84, 0x26, \ + 0x84, 0x14, 0xe1, 0x2c, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x80, 0x16, 0x83, 0x11, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x80, 0x7b, 0x82, 0x6b, \ + 0xe0, 0x08, 0x00, 0x46, \ + 0x6e, 0x59, 0x4e, 0x01, \ + 0x8e, 0x0b, 0xc0, 0x2f, \ + 0xef, 0xfc, 0x77, 0xc8, \ + 0x8c, 0x1b, 0x8e, 0x0b, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xfb, 0x55, 0xf2, 0x04, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0x00, 0x06, 0x80, 0x6b, \ + 0x4c, 0x01, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x46, 0x18, 0x9f, 0x01, \ + 0xa1, 0x01, 0xef, 0xf0, \ + 0xef, 0xe0, 0x50, 0x28, \ + 0xe1, 0x38, 0x00, 0x86, \ + 0x84, 0x28, 0x80, 0x14, \ + 0x84, 0x26, 0xc2, 0x29, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xf8, 0x42, 0x10, \ + 0x00, 0x46, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x38, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xf8, \ + 0xe1, 0x34, 0x00, 0x86, \ + 0x02, 0x16, 0x80, 0x04, \ + 0x4c, 0x11, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x8c, 0x0b, 0x80, 0x16, \ + 0xe8, 0x1c, 0x46, 0x08, \ + 0x6f, 0x05, 0x0c, 0x09, \ + 0x40, 0x4a, 0xc0, 0x30, \ + 0x82, 0x45, 0xe0, 0x84, \ + 0xe0, 0x88, 0x40, 0x0a, \ + 0x0c, 0x09, 0x82, 0x85, \ + 0xc0, 0x30, 0x6f, 0x11, \ + 0xe0, 0x88, 0x40, 0x0a, \ + 0x01, 0xc6, 0x80, 0xc5, \ + 0x80, 0x04, 0xe1, 0x34, \ + 0x80, 0x16, 0x89, 0x11, \ + 0x6f, 0x05, 0x0c, 0x09, \ + 0x00, 0x4a, 0xc0, 0x30, \ + 0x80, 0xb5, 0xf8, 0x1c, \ + 0xe0, 0x94, 0x40, 0x4a, \ + 0x85, 0x35, 0x82, 0x75, \ + 0xe0, 0x94, 0x40, 0x4a, \ + 0x0c, 0x09, 0x88, 0x05, \ + 0xc0, 0x30, 0x6f, 0x01, \ + 0xfb, 0xfc, 0x00, 0x4a, \ + 0xa1, 0x01, 0x86, 0x55, \ + 0xef, 0xf0, 0x41, 0x68, \ + 0xe0, 0x04, 0x00, 0x26, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x02, 0x86, 0xc0, 0x2f, \ + 0x84, 0x14, 0xe1, 0x34, \ + 0x4e, 0xc6, 0x84, 0x06, \ + 0x9c, 0x04, 0xe1, 0x0c, \ + 0x80, 0x16, 0xa3, 0x11, \ + 0x5f, 0xd8, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x00, \ + 0xc0, 0x31, 0x60, 0x5d, \ + 0x84, 0x06, 0x9c, 0x14, \ + 0xe1, 0x00, 0x40, 0x46, \ + 0x84, 0x28, 0x80, 0x14, \ + 0xf3, 0xec, 0x44, 0x20, \ + 0x42, 0x06, 0x84, 0x26, \ + 0x84, 0x14, 0xe1, 0x38, \ + 0x04, 0x24, 0x84, 0x28, \ + 0x84, 0x26, 0xfc, 0x9c, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xc8, 0x19, \ + 0x6f, 0x05, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x86, 0x45, 0xe0, 0x80, \ + 0xe0, 0x04, 0x00, 0x36, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x02, 0x86, 0xc0, 0x2f, \ + 0x84, 0x14, 0xe1, 0x34, \ + 0x01, 0xc6, 0x84, 0x06, \ + 0x80, 0x04, 0xe1, 0x34, \ + 0x80, 0x16, 0x83, 0x11, \ + 0x58, 0xb8, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x1c, \ + 0xc0, 0x31, 0x67, 0x49, \ + 0x80, 0x95, 0xa1, 0x0b, \ + 0x5f, 0x38, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x00, \ + 0xc0, 0x30, 0x6f, 0x05, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x0c, 0x09, 0x92, 0x85, \ + 0xc0, 0x30, 0x6f, 0x11, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x00, 0x46, 0x90, 0xc5, \ + 0x4c, 0x01, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xe1, 0x34, 0x0e, 0x86, \ + 0x84, 0x06, 0x9c, 0x14, \ + 0xe1, 0x34, 0x01, 0xc6, \ + 0x85, 0x11, 0x80, 0x04, \ + 0x8c, 0x0b, 0x80, 0x16, \ + 0xe8, 0x00, 0x69, 0x68, \ + 0x75, 0xf8, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xef, 0xf8, \ + 0xef, 0xfc, 0x41, 0x88, \ + 0x76, 0x98, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xef, 0xf8, \ + 0xef, 0xf8, 0x79, 0xe8, \ + 0x7a, 0x38, 0xad, 0x01, \ + 0x00, 0x06, 0xef, 0xec, \ + 0x44, 0x38, 0xe0, 0x40, \ + 0x01, 0x36, 0xef, 0xe0, \ + 0x4c, 0x01, 0xe0, 0x08, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x5a, 0x78, 0x8c, 0x0b, \ + 0xaf, 0x01, 0xef, 0xf4, \ + 0xef, 0xec, 0x79, 0x48, \ + 0xe0, 0x80, 0x00, 0x06, \ + 0xef, 0xe0, 0x43, 0x48, \ + 0x67, 0x49, 0x0c, 0x09, \ + 0xa3, 0x0b, 0xc0, 0x31, \ + 0x8c, 0x0b, 0x80, 0x95, \ + 0xe8, 0x00, 0x5a, 0xc8, \ + 0xe0, 0x04, 0x00, 0x56, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x9c, 0x14, 0xc0, 0x2f, \ + 0x8c, 0x0b, 0x84, 0x06, \ + 0xe8, 0x00, 0x69, 0xa8, \ + 0x6f, 0x01, 0x0c, 0x09, \ + 0xa1, 0x0b, 0xc0, 0x30, \ + 0x00, 0x66, 0x98, 0x45, \ + 0x4c, 0x01, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xe1, 0x34, 0x02, 0x86, \ + 0x84, 0x06, 0x84, 0x14, \ + 0x65, 0x21, 0x00, 0x60, \ + 0x00, 0x19, 0xc0, 0x73, \ + 0x0c, 0x29, 0xf0, 0x08, \ + 0xc0, 0x1c, 0x6d, 0x25, \ + 0x80, 0x08, 0x82, 0x38, \ + 0x64, 0xf1, 0x0e, 0x60, \ + 0xd0, 0x27, 0xc0, 0x73, \ + 0xd0, 0x37, 0xa2, 0x2f, \ + 0x00, 0x60, 0xa0, 0x3f, \ + 0xc0, 0x73, 0x65, 0x11, \ + 0xf0, 0x08, 0x00, 0x19, \ + 0x6d, 0x15, 0x0c, 0x49, \ + 0xe0, 0x37, 0xc0, 0x1c, \ + 0xa4, 0x3f, 0x82, 0x58, \ + 0xfe, 0x3e, 0x80, 0x08, \ + 0xa2, 0x4f, 0xd0, 0x47, \ + 0xa0, 0x5f, 0xd0, 0x57, \ + 0x65, 0x01, 0x00, 0x60, \ + 0x00, 0x19, 0xc0, 0x73, \ + 0x0c, 0x29, 0xf0, 0x08, \ + 0xc0, 0x1c, 0x6d, 0x05, \ + 0x82, 0x38, 0xe0, 0x57, \ + 0x80, 0x08, 0xa8, 0x5f, \ + 0xd0, 0x27, 0xfc, 0x5e, \ + 0xd0, 0x37, 0x8e, 0x4b, \ + 0xf0, 0x08, 0x08, 0x59, \ + 0x92, 0x08, 0xa0, 0x3f, \ + 0x6c, 0xf5, 0x0c, 0xc9, \ + 0x90, 0x48, 0xc0, 0x1c, \ + 0xe0, 0x37, 0xa2, 0x2f, \ + 0xd0, 0x07, 0xa4, 0x3f, \ + 0xa8, 0x0f, 0xd0, 0xc7, \ + 0xe0, 0x07, 0xaa, 0xcf, \ + 0xb8, 0x0f, 0xbe, 0x3e, \ + 0x8c, 0x0b, 0xbc, 0x0e, \ + 0xe8, 0x1c, 0x67, 0x08, \ + 0x65, 0xd8, 0x8c, 0x0b, \ + 0x9e, 0x1b, 0xe8, 0x00, \ + 0x8e, 0x0b, 0xa1, 0x21, \ + 0xef, 0x80, 0x47, 0x48, \ + 0x67, 0x49, 0x0c, 0x09, \ + 0xa5, 0x0b, 0xc0, 0x31, \ + 0x8c, 0x0b, 0x80, 0x95, \ + 0xe8, 0x00, 0x53, 0xc8, \ + 0xe1, 0x38, 0x00, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xc2, 0x19, \ + 0xe1, 0x38, 0x00, 0x86, \ + 0x84, 0x28, 0x80, 0x14, \ + 0x84, 0x26, 0xc2, 0x29, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xf8, 0x42, 0x10, \ + 0x0c, 0x09, 0x80, 0x16, \ + 0xc0, 0x30, 0x6f, 0x05, \ + 0xe0, 0x88, 0x40, 0x0a, \ + 0x0c, 0x09, 0x8a, 0xa5, \ + 0xc0, 0x30, 0x6f, 0x11, \ + 0xe0, 0x88, 0x40, 0x0a, \ + 0x00, 0x76, 0x88, 0xe5, \ + 0x4c, 0x01, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xe1, 0x34, 0x02, 0x86, \ + 0x84, 0x06, 0x84, 0x14, \ + 0xe1, 0x34, 0x01, 0xc6, \ + 0x87, 0x11, 0x80, 0x04, \ + 0x8c, 0x0b, 0x80, 0x16, \ + 0xe8, 0x00, 0x66, 0xe8, \ + 0x67, 0x49, 0x0c, 0x09, \ + 0xa7, 0x0b, 0xc0, 0x31, \ + 0x8c, 0x0b, 0x80, 0x95, \ + 0xe8, 0x00, 0x50, 0x68, \ + 0x6d, 0xf8, 0xb7, 0x01, \ + 0x00, 0x06, 0xef, 0xec, \ + 0x77, 0xf8, 0xe8, 0x00, \ + 0x8c, 0x0b, 0xef, 0xdc, \ + 0xef, 0xfc, 0x5e, 0x08, \ + 0x73, 0x98, 0x8c, 0x0b, \ + 0xb9, 0x01, 0xe8, 0x14, \ + 0xef, 0xec, 0x6d, 0x28, \ + 0xf0, 0x00, 0x00, 0x06, \ + 0xef, 0xdc, 0x77, 0x28, \ + 0x6f, 0x05, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x82, 0xa5, 0xe0, 0x8c, \ + 0xe0, 0x04, 0x00, 0x86, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x02, 0x86, 0xc0, 0x2f, \ + 0x84, 0x14, 0xe1, 0x34, \ + 0x8c, 0x0b, 0x84, 0x06, \ + 0xe8, 0x00, 0x64, 0xc8, \ + 0x6f, 0x05, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x80, 0x85, 0xe0, 0x90, \ + 0x45, 0x38, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xef, 0xe0, \ + 0xc0, 0x31, 0x67, 0x49, \ + 0x80, 0x95, 0xa9, 0x0b, \ + 0x4c, 0xf8, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x00, \ + 0xc0, 0x30, 0x6f, 0x05, \ + 0xe0, 0x90, 0x40, 0x0a, \ + 0x8c, 0x0b, 0x80, 0x85, \ + 0xef, 0xd0, 0x57, 0xa8, \ + 0x6f, 0x05, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x92, 0x45, 0xe0, 0x88, \ + 0x6f, 0x11, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x90, 0x85, 0xe0, 0x88, \ + 0xe0, 0x04, 0x00, 0xa6, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x02, 0x86, 0xc0, 0x2f, \ + 0x84, 0x14, 0xe1, 0x34, \ + 0x8c, 0x0b, 0x84, 0x06, \ + 0xef, 0xf8, 0x7a, 0x68, \ + 0x79, 0xb8, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xef, 0xf8, \ + 0xef, 0xf8, 0x78, 0x88, \ + 0x78, 0xd8, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xef, 0xf8, \ + 0xe8, 0x00, 0x63, 0xa8, \ + 0x6e, 0xf8, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x00, \ + 0xc0, 0x30, 0x6e, 0x69, \ + 0xe0, 0x80, 0x40, 0x4a, \ + 0x8c, 0x0b, 0x88, 0x65, \ + 0xef, 0xf8, 0x62, 0xa8, \ + 0x6e, 0x78, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xef, 0xf8, \ + 0xef, 0xf8, 0x64, 0x48, \ + 0x68, 0x18, 0x8c, 0x0b, \ + 0x40, 0x16, 0xef, 0xf8, \ + 0x66, 0x58, 0xe0, 0x00, \ + 0x00, 0x06, 0xef, 0xec, \ + 0xc0, 0x08, 0x60, 0x01, \ + 0xef, 0xdc, 0x70, 0x48, \ + 0x8c, 0x0b, 0x83, 0x11, \ + 0xe8, 0x00, 0x71, 0x48, \ + 0xe0, 0x00, 0x40, 0x26, \ + 0xef, 0xec, 0x65, 0x88, \ + 0x60, 0x01, 0x00, 0x06, \ + 0x6f, 0x78, 0xc0, 0x10, \ + 0x00, 0x86, 0xef, 0xdc, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0xc2, 0x29, 0x84, 0x28, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xf8, \ + 0xe1, 0x38, 0x00, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xf8, 0x42, 0x10, \ + 0x01, 0x36, 0x80, 0x16, \ + 0x4c, 0x01, 0xe0, 0x0c, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x5c, 0x58, 0x8c, 0x0b, \ + 0x00, 0xb6, 0xef, 0xf8, \ + 0x4c, 0x01, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xe1, 0x34, 0x02, 0x86, \ + 0x84, 0x06, 0x84, 0x14, \ + 0xef, 0xfc, 0x52, 0xe8, \ + 0xe1, 0x38, 0x4e, 0x06, \ + 0x80, 0x18, 0x9c, 0x04, \ + 0xf3, 0xbc, 0x42, 0x10, \ + 0x74, 0x58, 0x80, 0x16, \ + 0x83, 0x81, 0xe8, 0x00, \ + 0x83, 0x21, 0x95, 0x11, \ + 0x6b, 0xb8, 0x8c, 0x0b, \ + 0x01, 0x46, 0xe8, 0x14, \ + 0x80, 0x04, 0xe1, 0x10, \ + 0x80, 0x16, 0x81, 0x11, \ + 0x9c, 0x04, 0x80, 0x86, \ + 0xe0, 0x19, 0x80, 0x18, \ + 0x9c, 0x04, 0x80, 0x16, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf1, 0xfc, \ + 0x0c, 0x0c, 0xa0, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x0c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0xe1, 0x14, 0x0c, 0x86, \ + 0x84, 0x28, 0x98, 0x14, \ + 0x84, 0x26, 0xe0, 0x29, \ + 0xe1, 0x38, 0x4e, 0x06, \ + 0x84, 0x28, 0x9c, 0x14, \ + 0xf3, 0xdc, 0x44, 0x20, \ + 0x9c, 0x14, 0x84, 0x26, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x84, 0x26, 0xf3, 0xbc, \ + 0xe1, 0x0c, 0x43, 0x46, \ + 0x84, 0x28, 0x84, 0x14, \ + 0x84, 0x26, 0xc2, 0x29, \ + 0xef, 0xec, 0x46, 0x08, \ + 0x80, 0x18, 0x9c, 0x04, \ + 0x80, 0x16, 0xe0, 0x19, \ + 0x80, 0x18, 0x98, 0x04, \ + 0xf3, 0xbc, 0x42, 0x10, \ + 0x88, 0xf1, 0x80, 0x16, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x0a, 0x09, 0x80, 0x5b, \ + 0xc0, 0x31, 0x67, 0x4d, \ + 0xe0, 0x8c, 0x40, 0x4a, \ + 0xe1, 0x50, 0x43, 0x06, \ + 0x84, 0x2e, 0x81, 0x21, \ + 0x0a, 0x15, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe0, 0xd0, 0x02, 0x0a, \ + 0x0a, 0x39, 0x84, 0x95, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xf3, 0xf4, 0x40, 0x00, \ + 0x67, 0x4d, 0x4a, 0x01, \ + 0x00, 0x50, 0xc0, 0x31, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0x44, 0x96, 0x81, 0x11, \ + 0x98, 0xf1, 0xe0, 0x00, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xff, 0xd8, 0x37, 0x28, \ + 0x64, 0x81, 0x0a, 0x05, \ + 0x0e, 0x00, 0xc0, 0x05, \ + 0x0f, 0x0a, 0xec, 0x04, \ + 0xa2, 0xb5, 0xe5, 0xfc, \ + 0x6b, 0xa1, 0x0a, 0x0b, \ + 0x40, 0x00, 0xc0, 0x18, \ + 0xc0, 0xff, 0x73, 0xf1, \ + 0x16, 0x00, 0x8a, 0x03, \ + 0xc0, 0x28, 0x64, 0x01, \ + 0xf0, 0x7c, 0x50, 0x70, \ + 0xe1, 0x8c, 0x00, 0x74, \ + 0x7f, 0xfd, 0x43, 0xce, \ + 0x82, 0x0f, 0xc7, 0xff, \ + 0xe0, 0x88, 0x02, 0x74, \ + 0x8a, 0x13, 0x8a, 0x03, \ + 0x66, 0x11, 0x08, 0x00, \ + 0x18, 0x10, 0xc0, 0x54, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0xe1, 0x48, 0x0d, 0xc6, \ + 0x40, 0x1a, 0x85, 0x25, \ + 0x06, 0x04, 0xe6, 0x82, \ + 0xa1, 0x3b, 0xe1, 0x88, \ + 0xe4, 0x10, 0x00, 0xb0, \ + 0x98, 0x1a, 0x86, 0x05, \ + 0xe1, 0x50, 0x45, 0x06, \ + 0x82, 0xa3, 0x88, 0x24, \ + 0x73, 0xf1, 0x42, 0xa0, \ + 0x88, 0xb1, 0xc1, 0xff, \ + 0x86, 0x9b, 0x84, 0x13, \ + 0xc5, 0x34, 0x86, 0x2b, \ + 0xec, 0x04, 0x04, 0x20, \ + 0xc1, 0x3e, 0xa1, 0x2b, \ + 0x92, 0x3b, 0xfe, 0x75, \ + 0xe0, 0x88, 0x00, 0x34, \ + 0x89, 0x95, 0x96, 0x03, \ + 0x89, 0x75, 0x81, 0x31, \ + 0xe0, 0x00, 0x0e, 0x4a, \ + 0xf8, 0x7c, 0x0e, 0x1a, \ + 0xe1, 0x84, 0x40, 0x0a, \ + 0x88, 0x41, 0x80, 0x55, \ + 0x81, 0x81, 0x80, 0x55, \ + 0xf1, 0x94, 0x98, 0x0a, \ + 0xf1, 0x80, 0x40, 0x52, \ + 0x64, 0x71, 0x00, 0x00, \ + 0x14, 0x94, 0xc0, 0xc4, \ + 0x00, 0x04, 0xe1, 0xc0, \ + 0x81, 0xab, 0xfb, 0x80, \ + 0x8a, 0x0b, 0x84, 0x55, \ + 0xbc, 0xae, 0xfc, 0x4e, \ + 0xbe, 0xbe, 0x8a, 0xab, \ + 0x86, 0x6b, 0x98, 0xbb, \ + 0xe8, 0x10, 0x7a, 0x68, \ + 0x96, 0xcb, 0x8c, 0x3b, \ + 0x94, 0x5b, 0xbe, 0xb4, \ + 0xfc, 0x44, 0xbc, 0xa4, \ + 0xe1, 0x48, 0x0d, 0xc6, \ + 0x40, 0x0a, 0x90, 0x04, \ + 0x86, 0x45, 0xe4, 0xa0, \ + 0xec, 0x34, 0x00, 0x90, \ + 0x80, 0xd5, 0xe4, 0x0d, \ + 0xed, 0x9e, 0xbf, 0x9b, \ + 0x84, 0x75, 0x96, 0x0b, \ + 0xa1, 0x9b, 0x83, 0x85, \ + 0x40, 0x90, 0x82, 0xe5, \ + 0xb9, 0x0b, 0xf0, 0x3c, \ + 0xea, 0x05, 0xac, 0x9e, \ + 0xc9, 0x97, 0xa1, 0x0b, \ + 0x73, 0xfd, 0x42, 0x90, \ + 0xe6, 0xd5, 0xc0, 0x0f, \ + 0xe1, 0x88, 0x06, 0x14, \ + 0x96, 0x0b, 0xf3, 0xf5, \ + 0x0e, 0x8a, 0x82, 0x71, \ + 0x82, 0x81, 0xe6, 0x00, \ + 0xec, 0xb5, 0x80, 0xbb, \ + 0x0c, 0x0c, 0x98, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x30, 0x6f, 0x11, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x01, 0xc6, 0x8a, 0x65, \ + 0x80, 0x04, 0xe1, 0x04, \ + 0x80, 0x16, 0x81, 0x11, \ + 0x63, 0xed, 0x0c, 0x15, \ + 0x85, 0x31, 0xc0, 0x0c, \ + 0x8c, 0x0b, 0x83, 0x41, \ + 0x59, 0xf8, 0x82, 0x2b, \ + 0xab, 0x01, 0xe8, 0x18, \ + 0xef, 0xec, 0x50, 0x48, \ + 0xe0, 0x20, 0x00, 0x06, \ + 0xef, 0xdc, 0x5a, 0x48, \ + 0x40, 0x58, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xef, 0xfc, \ + 0xe8, 0x14, 0x54, 0x28, \ + 0x79, 0x41, 0x02, 0x06, \ + 0x8c, 0x0b, 0xdd, 0xcd, \ + 0xef, 0xfc, 0x45, 0xc8, \ + 0x52, 0x98, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x14, \ + 0xc0, 0x30, 0x6e, 0xe9, \ + 0x69, 0xad, 0x4c, 0x01, \ + 0x0c, 0x0c, 0xc0, 0x23, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x6e, 0x81, 0x00, 0x09, \ + 0x02, 0x00, 0xc0, 0x30, \ + 0xe5, 0x1d, 0xec, 0x04, \ + 0x01, 0xc6, 0x82, 0xa5, \ + 0x80, 0x04, 0xe1, 0x04, \ + 0x80, 0x16, 0x83, 0x11, \ + 0xe1, 0x0c, 0x00, 0x06, \ + 0x02, 0x06, 0x80, 0x04, \ + 0x80, 0x1e, 0xe8, 0x00, \ + 0xa1, 0x0b, 0x81, 0x95, \ + 0x01, 0xc6, 0x80, 0xf5, \ + 0x80, 0x04, 0xe1, 0x04, \ + 0x80, 0x16, 0x81, 0x11, \ + 0xdd, 0x93, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x30, 0x6f, 0x29, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x81, 0x01, 0x90, 0x25, \ + 0x6f, 0x31, 0x4c, 0x01, \ + 0x8c, 0x0b, 0xc0, 0x30, \ + 0xe8, 0x00, 0x66, 0x28, \ + 0xb1, 0x01, 0x87, 0x15, \ + 0xef, 0xec, 0x4b, 0x68, \ + 0xe1, 0x00, 0x00, 0x06, \ + 0xef, 0xdc, 0x55, 0x68, \ + 0x79, 0x41, 0x02, 0x06, \ + 0x8c, 0x0b, 0xdd, 0xcd, \ + 0xef, 0xfc, 0x42, 0xc8, \ + 0x4a, 0x98, 0xb3, 0x01, \ + 0x00, 0x06, 0xef, 0xec, \ + 0x54, 0x98, 0xe2, 0x00, \ + 0x81, 0x11, 0xef, 0xdc, \ + 0x55, 0x98, 0x8c, 0x0b, \ + 0x0c, 0x1b, 0xe8, 0x00, \ + 0xc0, 0x18, 0x67, 0x35, \ + 0x6e, 0xf1, 0x0c, 0x29, \ + 0x82, 0x19, 0xc0, 0x30, \ + 0x6b, 0x78, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xef, 0xd0, \ + 0xc0, 0x30, 0x6f, 0x31, \ + 0x4c, 0x01, 0x82, 0x01, \ + 0xc0, 0x30, 0x6f, 0x31, \ + 0x48, 0xd8, 0xb5, 0x01, \ + 0x00, 0x06, 0xef, 0xec, \ + 0x52, 0xd8, 0xe4, 0x00, \ + 0x0c, 0x09, 0xef, 0xdc, \ + 0xc0, 0x30, 0x6f, 0x31, \ + 0x6f, 0x01, 0x0c, 0x19, \ + 0xc3, 0x0d, 0xc0, 0x30, \ + 0x0c, 0x0c, 0xf2, 0x35, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0x63, 0xf1, 0x00, 0x15, \ + 0x00, 0x25, 0xc0, 0x0c, \ + 0xc0, 0x0c, 0x63, 0xf5, \ + 0x81, 0x41, 0x85, 0x31, \ + 0xe0, 0x18, 0x10, 0xc8, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x30, 0x6f, 0x41, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x8c, 0x0b, 0x82, 0x85, \ + 0xe8, 0x00, 0x61, 0xa8, \ + 0x67, 0x49, 0x0c, 0x09, \ + 0xb1, 0x0b, 0xc0, 0x31, \ + 0x8c, 0x0b, 0x80, 0x95, \ + 0xef, 0xfc, 0x68, 0x28, \ + 0x6f, 0x41, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x82, 0xa5, 0xe0, 0x88, \ + 0x62, 0x58, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x00, \ + 0xc0, 0x31, 0x67, 0x49, \ + 0x80, 0xb5, 0xb3, 0x0b, \ + 0x0c, 0x0c, 0x8c, 0x0b, \ + 0xcd, 0xf5, 0xf2, 0x00, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0xec, 0xa0, 0x1e, 0xf0, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x31, 0x67, 0x31, \ + 0xe0, 0x80, 0x40, 0x4a, \ + 0x93, 0x0e, 0x81, 0x01, \ + 0x8f, 0x0e, 0x91, 0x0e, \ + 0x8b, 0x0e, 0x8d, 0x0e, \ + 0xfe, 0x0e, 0x89, 0x0e, \ + 0x80, 0xe5, 0xfc, 0x0e, \ + 0x67, 0x35, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x31, \ + 0x88, 0x25, 0xe0, 0x80, \ + 0x04, 0x06, 0x8d, 0x11, \ + 0xdd, 0xcd, 0x79, 0x41, \ + 0x74, 0xd8, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xef, 0xf8, \ + 0xef, 0xf8, 0x4a, 0x68, \ + 0x6f, 0x41, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x84, 0x25, 0xe0, 0x8c, \ + 0x6e, 0xe5, 0x0c, 0x19, \ + 0x00, 0x86, 0xc0, 0x30, \ + 0x80, 0x04, 0xe1, 0x34, \ + 0xe0, 0x08, 0x45, 0xa6, \ + 0x6e, 0x59, 0x4c, 0x21, \ + 0x9e, 0x19, 0xc0, 0x2f, \ + 0x8c, 0x0b, 0x80, 0x26, \ + 0xe8, 0x00, 0x54, 0x08, \ + 0x40, 0x98, 0xbf, 0x01, \ + 0x00, 0x06, 0xef, 0xec, \ + 0xc0, 0x02, 0x60, 0x01, \ + 0xef, 0xdc, 0x4a, 0x88, \ + 0x6f, 0x41, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x86, 0x65, 0xe0, 0x90, \ + 0x65, 0xd9, 0x0c, 0x09, \ + 0xa3, 0x0b, 0xc0, 0x2f, \ + 0xe0, 0x0c, 0x03, 0x56, \ + 0x6e, 0x59, 0x4c, 0x11, \ + 0x84, 0x25, 0xc0, 0x2f, \ + 0x80, 0xb5, 0xa5, 0x0b, \ + 0x54, 0xb8, 0x8c, 0x0b, \ + 0x81, 0xb5, 0xe8, 0x00, \ + 0x66, 0xc1, 0x00, 0x60, \ + 0x81, 0x11, 0xc0, 0x56, \ + 0xe0, 0x00, 0x45, 0x36, \ + 0xe0, 0x0c, 0x07, 0x56, \ + 0xef, 0xd8, 0x5a, 0x08, \ + 0xe1, 0x48, 0x00, 0x46, \ + 0x09, 0x86, 0x80, 0x04, \ + 0x02, 0x60, 0xe0, 0x0c, \ + 0xc0, 0xab, 0x66, 0x11, \ + 0x67, 0x01, 0x04, 0x60, \ + 0x06, 0xf0, 0xc0, 0xa9, \ + 0x4c, 0x41, 0xe4, 0x20, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xef, 0x88, 0x5e, 0x68, \ + 0x65, 0x61, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x80, 0x75, 0xe1, 0xa0, \ + 0xfc, 0x0e, 0x83, 0x01, \ + 0xa1, 0x1b, 0xfc, 0x14, \ + 0x0c, 0x39, 0x84, 0x05, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x95, 0x5a, 0x91, 0x4a, \ + 0x00, 0x60, 0x8d, 0xca, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xfb, 0x00, 0x04, 0x14, \ + 0x8f, 0x11, 0xbc, 0xce, \ + 0xef, 0xd8, 0x54, 0xc8, \ + 0xa1, 0x1b, 0x8d, 0x14, \ + 0x0c, 0x39, 0x84, 0x05, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xa5, 0x5a, 0xa1, 0x4a, \ + 0x00, 0x60, 0x9d, 0xca, \ + 0xc0, 0x56, 0x66, 0xf1, \ + 0xfb, 0x00, 0x04, 0x14, \ + 0x8f, 0x11, 0xbc, 0xce, \ + 0xef, 0xd8, 0x5f, 0xe8, \ + 0xe0, 0x00, 0x40, 0x06, \ + 0xef, 0xe8, 0x7a, 0x28, \ + 0x60, 0x01, 0x00, 0x06, \ + 0x44, 0x18, 0xc0, 0x04, \ + 0x8f, 0x11, 0xef, 0xdc, \ + 0x79, 0x41, 0x04, 0x06, \ + 0x8c, 0x0b, 0xdd, 0xcd, \ + 0xef, 0xf8, 0x6b, 0xe8, \ + 0xe0, 0x00, 0x41, 0xd6, \ + 0xef, 0xe8, 0x79, 0x28, \ + 0x0c, 0x0c, 0xd0, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x81, 0x01, 0x80, 0x6b, \ + 0x77, 0xd8, 0x81, 0x11, \ + 0x80, 0x7b, 0xef, 0xe4, \ + 0xe1, 0x44, 0x01, 0x06, \ + 0x80, 0x08, 0x80, 0x04, \ + 0xe0, 0x90, 0x40, 0x0a, \ + 0x0c, 0x09, 0x8a, 0x45, \ + 0xc0, 0x31, 0x67, 0x4d, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x0c, 0x09, 0x88, 0x85, \ + 0xc0, 0x30, 0x6f, 0x05, \ + 0xe0, 0x90, 0x40, 0x4a, \ + 0x0c, 0x09, 0x80, 0xc5, \ + 0xc0, 0x30, 0x6e, 0xe5, \ + 0x86, 0x35, 0xa1, 0x0b, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x5e, 0x38, 0x83, 0x11, \ + 0x40, 0x86, 0xef, 0x7c, \ + 0x43, 0xf6, 0xe1, 0x34, \ + 0x80, 0x24, 0xe0, 0x0c, \ + 0xc1, 0x83, 0x88, 0x1e, \ + 0x0c, 0x29, 0xc3, 0x13, \ + 0xc0, 0x31, 0x67, 0x4d, \ + 0xe0, 0x88, 0x44, 0x0a, \ + 0x40, 0x20, 0xfc, 0xe5, \ + 0x4c, 0x01, 0xf3, 0xec, \ + 0xc0, 0x31, 0x67, 0x4d, \ + 0x8e, 0x0b, 0x83, 0x11, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0x34, 0x58, 0xf2, 0x04, \ + 0x00, 0x0c, 0xff, 0xe4, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0x80, 0x6b, 0xbc, 0x1e, \ + 0x66, 0x1d, 0x0c, 0x09, \ + 0x81, 0xb1, 0xc0, 0x2f, \ + 0xe0, 0x04, 0x00, 0x2a, \ + 0x41, 0xb6, 0xfe, 0xbe, \ + 0x89, 0xbc, 0xe0, 0x08, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x00, 0x88, 0xc0, 0x2f, \ + 0xc0, 0x00, 0x40, 0x01, \ + 0xe4, 0x30, 0x02, 0xf0, \ + 0x71, 0xf8, 0x8c, 0x0b, \ + 0x02, 0x06, 0xef, 0xd4, \ + 0x8c, 0x0b, 0xe0, 0x48, \ + 0xe8, 0x18, 0x44, 0xa8, \ + 0x64, 0x01, 0x00, 0x60, \ + 0xbe, 0x0e, 0xc0, 0x09, \ + 0x61, 0x01, 0x12, 0x06, \ + 0x0f, 0xc6, 0xc0, 0x20, \ + 0x81, 0x81, 0xe7, 0x34, \ + 0x6f, 0x39, 0x0c, 0x0b, \ + 0x40, 0x0a, 0xc0, 0x17, \ + 0x8a, 0x05, 0xe4, 0xa0, \ + 0xf0, 0xa0, 0x40, 0x62, \ + 0x6c, 0xf1, 0x00, 0xa9, \ + 0x14, 0x0a, 0xc0, 0x1c, \ + 0x88, 0x05, 0xe3, 0xfc, \ + 0x40, 0x01, 0x01, 0x48, \ + 0xbc, 0x04, 0xc0, 0x00, \ + 0x82, 0x05, 0xa1, 0x0b, \ + 0xf0, 0x9c, 0x42, 0x62, \ + 0xe0, 0x18, 0x45, 0x06, \ + 0x8c, 0x0b, 0x89, 0x31, \ + 0x04, 0x06, 0x81, 0x65, \ + 0x83, 0x31, 0xe0, 0x24, \ + 0x92, 0x1b, 0x8c, 0x0b, \ + 0xe8, 0x04, 0x5f, 0xa8, \ + 0x0a, 0xf0, 0xfe, 0x34, \ + 0x8c, 0x0b, 0xe4, 0x20, \ + 0xbe, 0x24, 0x8c, 0x1b, \ + 0x70, 0x18, 0x94, 0x4b, \ + 0x89, 0x0a, 0xef, 0xd4, \ + 0x82, 0x81, 0x80, 0xb3, \ + 0xe0, 0x40, 0x10, 0x0a, \ + 0x65, 0x01, 0x12, 0x90, \ + 0x0e, 0x70, 0xc0, 0x02, \ + 0xc0, 0x01, 0x66, 0xc1, \ + 0x4c, 0xb3, 0xf2, 0x55, \ + 0xc0, 0x18, 0x63, 0xb1, \ + 0x0c, 0x0c, 0xa0, 0xf1, \ + 0xdd, 0x93, 0xf6, 0x7c, \ + 0xfa, 0x00, 0x00, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0xe0, 0x00, 0x40, 0x36, \ + 0xef, 0xe8, 0x6d, 0xa8, \ + 0xe1, 0x0c, 0x41, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xf8, 0x42, 0x10, \ + 0x88, 0xf1, 0x80, 0x16, \ + 0xf2, 0x00, 0x08, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x80, 0x6b, 0x82, 0x7b, \ + 0xef, 0xf0, 0x77, 0xa8, \ + 0x6e, 0x69, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x80, 0xa5, 0xe0, 0x80, \ + 0x8e, 0x1b, 0x8c, 0x0b, \ + 0xe8, 0x10, 0x66, 0x88, \ + 0x6f, 0x18, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x10, \ + 0xc0, 0x30, 0x6e, 0xe9, \ + 0x64, 0x5d, 0x4c, 0x01, \ + 0x4c, 0x01, 0xc0, 0x24, \ + 0xc0, 0x24, 0x61, 0x0d, \ + 0x68, 0xfd, 0x4c, 0x01, \ + 0x88, 0xf1, 0xc0, 0x27, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x00, 0x46, 0x80, 0x6b, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0x66, 0x91, 0x02, 0x60, \ + 0x04, 0x60, 0xc0, 0xc7, \ + 0xc0, 0xc5, 0x67, 0xe1, \ + 0x66, 0x81, 0x06, 0x60, \ + 0x0a, 0x60, 0xc0, 0xc7, \ + 0xc0, 0x9e, 0x64, 0xf1, \ + 0x64, 0xf1, 0x08, 0x60, \ + 0x18, 0x60, 0xc0, 0xa3, \ + 0xc0, 0xae, 0x65, 0xd1, \ + 0x67, 0xf1, 0x0e, 0x60, \ + 0x10, 0x60, 0xc0, 0xa7, \ + 0xc0, 0xab, 0x66, 0x11, \ + 0x67, 0xe1, 0x12, 0x60, \ + 0x89, 0x8e, 0xc0, 0xb0, \ + 0xfc, 0x7e, 0xfe, 0x9e, \ + 0xbc, 0x4e, 0xbe, 0xce, \ + 0x57, 0xf8, 0x8c, 0x4b, \ + 0x0c, 0x15, 0xef, 0xb4, \ + 0xc0, 0x0c, 0x67, 0xa5, \ + 0x84, 0xc5, 0xa1, 0x1b, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x0c, 0x4b, 0xc0, 0x2f, \ + 0xc0, 0x18, 0x6f, 0x59, \ + 0x6f, 0x61, 0x0c, 0x5b, \ + 0x0c, 0xcb, 0xc0, 0x18, \ + 0xc0, 0x18, 0x6f, 0x51, \ + 0x66, 0xc1, 0x00, 0x60, \ + 0x04, 0x14, 0xc0, 0x56, \ + 0xbc, 0xce, 0xfb, 0x00, \ + 0x7f, 0x58, 0x8d, 0x11, \ + 0xa8, 0xf1, 0xef, 0xd4, \ + 0xf2, 0x1c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0x63, 0xf9, 0x00, 0x15, \ + 0x04, 0x10, 0xc0, 0x0c, \ + 0x85, 0x31, 0xec, 0x28, \ + 0x2e, 0x98, 0x83, 0x41, \ + 0x00, 0x0c, 0xe0, 0x14, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x40, 0x46, 0x80, 0x6b, \ + 0x64, 0x78, 0xe0, 0x00, \ + 0x00, 0x06, 0xef, 0xe8, \ + 0xc0, 0x20, 0x60, 0x01, \ + 0xef, 0xd8, 0x6e, 0x68, \ + 0x59, 0x78, 0x8c, 0x0b, \ + 0x40, 0x56, 0xef, 0xf0, \ + 0x63, 0xb8, 0xe0, 0x00, \ + 0x00, 0x06, 0xef, 0xe8, \ + 0xc0, 0x40, 0x60, 0x01, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0xd8, 0x2d, 0x88, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x30, 0x6f, 0x05, \ + 0xe0, 0x90, 0x40, 0x0a, \ + 0x0c, 0x0c, 0x80, 0x95, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe0, 0x00, 0x40, 0x66, \ + 0xef, 0xe8, 0x62, 0x28, \ + 0x60, 0x01, 0x00, 0x06, \ + 0x6c, 0x18, 0xc0, 0x80, \ + 0x8c, 0x0b, 0xef, 0xd8, \ + 0xef, 0xf0, 0x60, 0xe8, \ + 0xe0, 0x00, 0x40, 0x76, \ + 0xef, 0xe8, 0x61, 0x68, \ + 0x60, 0x01, 0x00, 0x06, \ + 0x0c, 0x0c, 0xc1, 0x00, \ + 0x2b, 0x38, 0xf2, 0x00, \ + 0xc1, 0xc3, 0xff, 0xd8, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x30, 0x6e, 0xf5, \ + 0x6e, 0xd9, 0x0c, 0x29, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x0c, 0x3b, 0xe0, 0x80, \ + 0xc0, 0x18, 0x67, 0x31, \ + 0x6e, 0xd5, 0x0c, 0x79, \ + 0x0c, 0x89, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6e, 0x71, \ + 0x6f, 0x05, 0x0c, 0x19, \ + 0x80, 0x85, 0xc0, 0x30, \ + 0xfc, 0x0c, 0x02, 0x14, \ + 0xd0, 0x19, 0x81, 0x25, \ + 0xe0, 0x90, 0x40, 0x0a, \ + 0xec, 0x3c, 0x08, 0x96, \ + 0x08, 0x86, 0x80, 0x65, \ + 0x0c, 0x59, 0xec, 0x3c, \ + 0xc0, 0x30, 0x6e, 0x6d, \ + 0xfc, 0x10, 0x0c, 0xc9, \ + 0xe0, 0x94, 0x4a, 0x0a, \ + 0xfc, 0x10, 0x48, 0x10, \ + 0x6f, 0x01, 0x4c, 0xc1, \ + 0x4c, 0x41, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6f, 0x05, \ + 0x06, 0x34, 0x80, 0xf5, \ + 0x87, 0x2b, 0xfa, 0x80, \ + 0xe8, 0x19, 0x82, 0x15, \ + 0xc2, 0x37, 0x81, 0x85, \ + 0xfa, 0x80, 0x06, 0x34, \ + 0xfe, 0x45, 0x85, 0x3b, \ + 0xfc, 0x10, 0x02, 0x44, \ + 0xe0, 0x8c, 0x40, 0x4a, \ + 0x6f, 0x05, 0x4c, 0x11, \ + 0x82, 0x05, 0xc0, 0x30, \ + 0x44, 0xd8, 0x8c, 0x0b, \ + 0x10, 0x0a, 0xe8, 0x04, \ + 0x82, 0x55, 0xff, 0xfc, \ + 0x40, 0x10, 0x83, 0xb5, \ + 0x4c, 0x01, 0xf3, 0xf0, \ + 0xc0, 0x30, 0x6f, 0x05, \ + 0xff, 0xfc, 0x10, 0x0a, \ + 0x00, 0x84, 0x84, 0x65, \ + 0x02, 0x74, 0xfa, 0x80, \ + 0x81, 0x1b, 0xfa, 0x80, \ + 0x0c, 0x09, 0x82, 0xb5, \ + 0xc0, 0x30, 0x6f, 0x05, \ + 0x40, 0x00, 0x81, 0x11, \ + 0x4c, 0x11, 0xf3, 0xe0, \ + 0xc0, 0x30, 0x6f, 0x01, \ + 0x6f, 0x05, 0x4c, 0x01, \ + 0x0c, 0x0c, 0xc0, 0x30, \ + 0xdd, 0x93, 0xf2, 0x0c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x40, 0xd8, 0x80, 0x6b, \ + 0x8c, 0x0b, 0xe8, 0x00, \ + 0xe8, 0x08, 0x7e, 0x68, \ + 0x56, 0xb8, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xe8, 0x00, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0x89, 0x65, \ + 0x6e, 0x5d, 0x00, 0x19, \ + 0xa3, 0x1b, 0xc0, 0x2f, \ + 0xe0, 0x08, 0x45, 0xc6, \ + 0x6e, 0x59, 0x40, 0x21, \ + 0x82, 0x65, 0xc0, 0x2f, \ + 0x82, 0xe5, 0xa7, 0x1b, \ + 0x8a, 0x95, 0xa5, 0x1b, \ + 0xe0, 0x00, 0x42, 0x36, \ + 0x6e, 0x59, 0x40, 0x11, \ + 0x85, 0x65, 0xc0, 0x2f, \ + 0xe0, 0x00, 0x42, 0x26, \ + 0x6e, 0x59, 0x40, 0x11, \ + 0x85, 0x05, 0xc0, 0x2f, \ + 0x65, 0x39, 0x00, 0x19, \ + 0x02, 0x10, 0xc0, 0x31, \ + 0x03, 0x0a, 0xec, 0x04, \ + 0x42, 0x46, 0xf4, 0x04, \ + 0x40, 0x11, 0xe0, 0x00, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x00, 0x19, 0x82, 0x35, \ + 0xc0, 0x31, 0x64, 0xe1, \ + 0xf3, 0xec, 0x42, 0x10, \ + 0x64, 0xe1, 0x40, 0x11, \ + 0x00, 0x19, 0xc0, 0x31, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0xe0, 0x88, 0x42, 0x0a, \ + 0x83, 0x11, 0x80, 0xa5, \ + 0x65, 0x79, 0x40, 0x11, \ + 0x00, 0x19, 0xc0, 0x31, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0x40, 0x11, 0xc4, 0x19, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x81, 0x01, 0x80, 0x7b, \ + 0x53, 0xd8, 0x81, 0x11, \ + 0x80, 0x6b, 0xef, 0xe4, \ + 0x6e, 0x5d, 0x0e, 0x09, \ + 0xa7, 0x0b, 0xc0, 0x2f, \ + 0xe0, 0x08, 0x43, 0xd6, \ + 0x6e, 0x59, 0x4e, 0x11, \ + 0x84, 0x65, 0xc0, 0x2f, \ + 0xa2, 0x95, 0xa5, 0x0b, \ + 0x6e, 0x51, 0x0e, 0x09, \ + 0x40, 0x00, 0xc0, 0x2f, \ + 0x81, 0x11, 0xf3, 0xf0, \ + 0x6e, 0x51, 0x4e, 0x01, \ + 0x4e, 0x11, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x5d, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x04, \ + 0x6e, 0x51, 0x0e, 0x09, \ + 0x40, 0x00, 0xc0, 0x2f, \ + 0x4e, 0x01, 0xf3, 0xf4, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0x6d, 0x58, 0x8e, 0x0b, \ + 0x8e, 0x0b, 0xe8, 0x10, \ + 0xe8, 0x00, 0x47, 0x68, \ + 0x65, 0xe5, 0x0e, 0x09, \ + 0xa7, 0x0b, 0xc0, 0x2f, \ + 0xa5, 0x0b, 0x82, 0x65, \ + 0xa3, 0x0b, 0x86, 0x05, \ + 0x0e, 0x09, 0x86, 0xb5, \ + 0xc0, 0x31, 0x64, 0xe1, \ + 0xf3, 0xf4, 0x40, 0x00, \ + 0x0e, 0x09, 0x83, 0x45, \ + 0xc0, 0x31, 0x64, 0xe1, \ + 0xe0, 0x88, 0x40, 0x4a, \ + 0x0e, 0x19, 0x80, 0xe5, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0xe0, 0x94, 0x42, 0x0a, \ + 0x40, 0x00, 0x80, 0xe5, \ + 0x81, 0x55, 0xf3, 0xf4, \ + 0x64, 0xe1, 0x0e, 0x09, \ + 0xc4, 0x09, 0xc0, 0x31, \ + 0x64, 0xe1, 0x4e, 0x01, \ + 0x0e, 0x09, 0xc0, 0x31, \ + 0xc0, 0x31, 0x64, 0xe1, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x40, 0x0a, 0x84, 0x55, \ + 0x84, 0x95, 0xe0, 0x84, \ + 0xe0, 0x00, 0x41, 0x16, \ + 0xef, 0xe8, 0x4e, 0x88, \ + 0x6e, 0x31, 0x0e, 0x09, \ + 0xa1, 0x0b, 0xc0, 0x2f, \ + 0xb5, 0x01, 0x86, 0xc5, \ + 0x6e, 0x59, 0x4e, 0x01, \ + 0x85, 0xb5, 0xc0, 0x2f, \ + 0xe0, 0x00, 0x41, 0x06, \ + 0xef, 0xe8, 0x4d, 0xa8, \ + 0x8e, 0x0b, 0x85, 0xd5, \ + 0xe8, 0x00, 0x47, 0xa8, \ + 0x6e, 0x51, 0x0e, 0x09, \ + 0xd0, 0x09, 0xc0, 0x2f, \ + 0x6e, 0x51, 0x4e, 0x01, \ + 0x41, 0xf6, 0xc0, 0x2f, \ + 0x83, 0x11, 0xe0, 0x3c, \ + 0xef, 0x78, 0x74, 0x68, \ + 0x7f, 0xf8, 0x8e, 0x0b, \ + 0x83, 0x65, 0xef, 0xf0, \ + 0xe1, 0x00, 0x41, 0x06, \ + 0x80, 0x08, 0x80, 0x04, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x8e, 0x0b, 0x80, 0x85, \ + 0xef, 0xf0, 0x74, 0x08, \ + 0x4e, 0x01, 0xb7, 0x01, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x8e, 0x0b, 0x8d, 0x11, \ + 0xef, 0xe8, 0x63, 0x08, \ + 0x47, 0x18, 0x8e, 0x0b, \ + 0x81, 0x01, 0xe8, 0x00, \ + 0x65, 0x79, 0x4e, 0x01, \ + 0x83, 0x11, 0xc0, 0x31, \ + 0x88, 0xf1, 0x8c, 0x0b, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0xff, 0xe4, 0x09, 0xe8, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x40, 0xc6, 0x80, 0x6b, \ + 0x43, 0xe6, 0xe1, 0x20, \ + 0x80, 0x04, 0xe0, 0x08, \ + 0x6e, 0x59, 0x4c, 0x11, \ + 0x0c, 0x15, 0xc0, 0x2f, \ + 0xc0, 0x0a, 0x63, 0x69, \ + 0xe1, 0x24, 0x04, 0xc6, \ + 0x88, 0x24, 0x80, 0x08, \ + 0xc5, 0x07, 0x88, 0x34, \ + 0xf0, 0x0c, 0x44, 0x00, \ + 0x57, 0x18, 0x8c, 0x0b, \ + 0x42, 0x86, 0xe8, 0x14, \ + 0x0c, 0x25, 0xe0, 0x3c, \ + 0xc0, 0x05, 0x64, 0x5d, \ + 0xe6, 0x86, 0x46, 0x0a, \ + 0x65, 0x45, 0x0c, 0x45, \ + 0x02, 0x0e, 0xc0, 0x0c, \ + 0xc3, 0xd0, 0x64, 0x01, \ + 0xe0, 0x0a, 0x42, 0x1a, \ + 0x44, 0x32, 0xc9, 0x37, \ + 0xc5, 0x1d, 0xf0, 0x90, \ + 0x65, 0x35, 0x4c, 0x05, \ + 0x82, 0x55, 0xc0, 0x0c, \ + 0x65, 0x41, 0x0c, 0x05, \ + 0xa4, 0x13, 0xc0, 0x0c, \ + 0x4c, 0x15, 0xc1, 0x1d, \ + 0xc0, 0x05, 0x69, 0xe9, \ + 0x0c, 0x09, 0x82, 0x85, \ + 0xc0, 0x31, 0x64, 0xe1, \ + 0x81, 0x11, 0xc2, 0x09, \ + 0x64, 0xe1, 0x4c, 0x01, \ + 0x4c, 0x15, 0xc0, 0x31, \ + 0xc0, 0x05, 0x69, 0xe9, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x63, 0x69, 0x00, 0x15, \ + 0x04, 0x06, 0xc0, 0x0a, \ + 0xc0, 0xfa, 0x60, 0x01, \ + 0xe0, 0x06, 0x42, 0x2a, \ + 0x69, 0xe9, 0x00, 0x05, \ + 0xd0, 0x07, 0xc0, 0x05, \ + 0xe1, 0x84, 0x04, 0x14, \ + 0xc0, 0x1d, 0x84, 0x03, \ + 0x89, 0x01, 0x80, 0x65, \ + 0x40, 0x0a, 0x81, 0x35, \ + 0x43, 0x06, 0xe0, 0x06, \ + 0x84, 0x14, 0xe1, 0x44, \ + 0xdd, 0x93, 0x84, 0x0e, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x69, 0xe9, 0x00, 0x15, \ + 0x02, 0x8a, 0xc0, 0x05, \ + 0x45, 0xf6, 0xe5, 0x94, \ + 0x40, 0x21, 0xe0, 0x08, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x81, 0x21, 0x80, 0x95, \ + 0xe0, 0x10, 0x12, 0x08, \ + 0xe0, 0x04, 0x42, 0x46, \ + 0x11, 0xb8, 0x81, 0x21, \ + 0xc1, 0xc3, 0xe0, 0x10, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x9d, 0x01, 0x80, 0x6b, \ + 0xef, 0xe8, 0x42, 0x68, \ + 0x4c, 0x78, 0x91, 0x01, \ + 0x0c, 0x05, 0xef, 0xd8, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe0, 0xd4, 0x00, 0x0a, \ + 0x40, 0xc6, 0x82, 0x35, \ + 0x8d, 0x21, 0xe1, 0x48, \ + 0x4c, 0x21, 0x80, 0x26, \ + 0xc0, 0x31, 0x60, 0x01, \ + 0x40, 0xc6, 0x81, 0x65, \ + 0x80, 0x28, 0xe1, 0x48, \ + 0x82, 0x45, 0xa1, 0x2b, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x00, 0x60, 0xc0, 0x2f, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0x5c, 0x38, 0x83, 0x11, \ + 0x0c, 0x09, 0xef, 0xd4, \ + 0xc0, 0x31, 0x67, 0x4d, \ + 0x40, 0x0a, 0xc9, 0x07, \ + 0x82, 0x09, 0xe0, 0x80, \ + 0x4c, 0x03, 0x80, 0x85, \ + 0xc0, 0x18, 0x67, 0x31, \ + 0x51, 0x56, 0x81, 0x71, \ + 0x53, 0x76, 0xe0, 0x00, \ + 0x55, 0x86, 0xe0, 0x00, \ + 0x57, 0x96, 0xe0, 0x00, \ + 0xa1, 0x7b, 0xe0, 0x00, \ + 0xe0, 0x00, 0x41, 0x06, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x82, 0x45, 0xc0, 0x2f, \ + 0xe0, 0x00, 0x41, 0x26, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x8c, 0x0b, 0xc0, 0x2f, \ + 0xe8, 0x00, 0x50, 0x48, \ + 0x41, 0x16, 0x81, 0xa5, \ + 0x4c, 0x01, 0xe0, 0x00, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x48, 0x38, 0x8c, 0x0b, \ + 0x83, 0x71, 0xe8, 0x00, \ + 0xe0, 0x00, 0x41, 0x66, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x8c, 0x0b, 0xc0, 0x2f, \ + 0xe8, 0x00, 0x63, 0xa8, \ + 0xe0, 0x00, 0x41, 0x36, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x8c, 0x0b, 0xc0, 0x2f, \ + 0xe8, 0x10, 0x78, 0x28, \ + 0xe0, 0x00, 0x41, 0x46, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x8c, 0x0b, 0xc0, 0x2f, \ + 0xe8, 0x14, 0x6c, 0xe8, \ + 0x6e, 0xf1, 0x0c, 0x09, \ + 0x0c, 0x1b, 0xc0, 0x30, \ + 0xc0, 0x18, 0x67, 0x35, \ + 0xec, 0x04, 0x04, 0x00, \ + 0x82, 0x19, 0x9e, 0x29, \ + 0x6e, 0x59, 0x4c, 0x81, \ + 0x8c, 0x0b, 0xc0, 0x2f, \ + 0xef, 0xcc, 0x5d, 0x28, \ + 0x6e, 0x59, 0x4c, 0x91, \ + 0x8c, 0x0b, 0xc0, 0x2f, \ + 0xef, 0xfc, 0x5a, 0x08, \ + 0x6e, 0xf1, 0x0c, 0x09, \ + 0xa3, 0x0b, 0xc0, 0x30, \ + 0x84, 0x95, 0x81, 0x01, \ + 0x6e, 0x79, 0x0c, 0x09, \ + 0xa3, 0x0b, 0xc0, 0x30, \ + 0x82, 0xc5, 0x83, 0x01, \ + 0x6e, 0x6d, 0x0c, 0x19, \ + 0x42, 0x4a, 0xc0, 0x30, \ + 0x82, 0x05, 0xe0, 0x8c, \ + 0x6e, 0xf5, 0x0c, 0x09, \ + 0xc7, 0x07, 0xc0, 0x30, \ + 0x40, 0x02, 0x83, 0x11, \ + 0x4c, 0x01, 0xf8, 0x84, \ + 0xc0, 0x31, 0x6e, 0x81, \ + 0x6e, 0x59, 0x4c, 0xa1, \ + 0x8c, 0x0b, 0xc0, 0x2f, \ + 0xef, 0xf4, 0x71, 0xe8, \ + 0x6e, 0x71, 0x0c, 0x09, \ + 0x00, 0x0a, 0xc0, 0x30, \ + 0x4c, 0xb1, 0xe3, 0xfc, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x0c, 0x19, 0xe2, 0xe5, \ + 0xc0, 0x30, 0x6e, 0xd5, \ + 0xe2, 0x55, 0x81, 0x1b, \ + 0x6e, 0x89, 0x0c, 0x09, \ + 0x0c, 0x19, 0xc0, 0x30, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0x41, 0xa6, 0xa1, 0x0b, \ + 0xc8, 0x19, 0xe0, 0x00, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x4c, 0x11, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0x8c, 0x0b, 0x80, 0x85, \ + 0xef, 0xf4, 0x5b, 0x08, \ + 0xe0, 0x00, 0x40, 0xa6, \ + 0xef, 0xe4, 0x76, 0x48, \ + 0x60, 0x01, 0x00, 0x06, \ + 0x88, 0xf1, 0xc8, 0x00, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xff, 0xd8, 0x00, 0x08, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x0b, 0x80, 0x6b, \ + 0xc0, 0x18, 0x67, 0x35, \ + 0xe0, 0x90, 0x40, 0x0a, \ + 0xe0, 0x08, 0x03, 0x06, \ + 0x6e, 0x59, 0x4c, 0x11, \ + 0x80, 0xa5, 0xc0, 0x2f, \ + 0x4c, 0x15, 0x81, 0x11, \ + 0xc0, 0x0c, 0x63, 0xb1, \ + 0xe0, 0x88, 0x40, 0x0a, \ + 0xf0, 0x00, 0x02, 0x0e, \ + 0x63, 0xb5, 0x4c, 0x15, \ + 0x92, 0xd5, 0xc0, 0x0c, \ + 0xe0, 0x8c, 0x40, 0x0a, \ + 0x40, 0x0a, 0x94, 0x75, \ + 0x80, 0xa5, 0xe0, 0x94, \ + 0x4c, 0x13, 0x81, 0x11, \ + 0xc0, 0x18, 0x67, 0x75, \ + 0x6e, 0xf5, 0x0c, 0x19, \ + 0x0c, 0x29, 0xc0, 0x30, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0x86, 0x1f, 0xe1, 0x31, \ + 0xe0, 0x88, 0x44, 0x4a, \ + 0x46, 0x10, 0x83, 0x21, \ + 0x4c, 0x21, 0xfc, 0x04, \ + 0xc0, 0x30, 0x6e, 0xf1, \ + 0x6e, 0xf5, 0x4c, 0x31, \ + 0x80, 0xa5, 0xc0, 0x30, \ + 0x4c, 0x11, 0xe2, 0x19, \ + 0xc0, 0x30, 0x6e, 0xf5, \ + 0xe0, 0x9c, 0x40, 0x0a, \ + 0x0c, 0x19, 0x84, 0x65, \ + 0xc0, 0x28, 0x6d, 0x61, \ + 0x6d, 0x65, 0x0c, 0x29, \ + 0x0c, 0x39, 0xc0, 0x28, \ + 0xc0, 0x28, 0x6d, 0x69, \ + 0x6d, 0x6d, 0x0c, 0x49, \ + 0x0c, 0x59, 0xc0, 0x28, \ + 0xc0, 0x28, 0x6d, 0x71, \ + 0x7c, 0x58, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x00, \ + 0xc0, 0x30, 0x6e, 0x6d, \ + 0xe0, 0x90, 0x40, 0x0a, \ + 0x8c, 0x0b, 0x80, 0x85, \ + 0xe8, 0x10, 0x5e, 0xc8, \ + 0x66, 0x61, 0x0c, 0x09, \ + 0x42, 0x46, 0xc0, 0x2f, \ + 0x84, 0x14, 0xe1, 0x04, \ + 0xec, 0x04, 0x00, 0x00, \ + 0x84, 0x06, 0x8e, 0x09, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x43, 0xf6, 0xdd, 0x93, \ + 0x4c, 0x11, 0xe0, 0x0c, \ + 0xc0, 0x30, 0x6e, 0xe1, \ + 0xe0, 0x8c, 0x40, 0x0a, \ + 0x81, 0x11, 0xea, 0xe5, \ + 0x6e, 0xe5, 0x4c, 0x11, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0xea, 0x75, 0xe0, 0x94, \ + 0xc1, 0xc3, 0xf5, 0xe5, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x6e, 0xf5, 0x00, 0x69, \ + 0x03, 0x16, 0xc0, 0x30, \ + 0x40, 0x11, 0xe0, 0x08, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xfc, 0x00, 0x0e, 0x64, \ + 0x67, 0x35, 0x00, 0x4b, \ + 0x40, 0x71, 0xc0, 0x18, \ + 0xc0, 0x30, 0x6e, 0xf5, \ + 0x6e, 0x75, 0x00, 0xc9, \ + 0x00, 0x9b, 0xc0, 0x30, \ + 0xc0, 0x18, 0x67, 0x31, \ + 0x6e, 0x79, 0x00, 0x39, \ + 0x00, 0xa5, 0xc0, 0x30, \ + 0xc0, 0x0c, 0x63, 0x95, \ + 0xe0, 0xb4, 0x48, 0x0a, \ + 0x6e, 0xe1, 0x00, 0x59, \ + 0x00, 0x29, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6e, 0xf1, \ + 0x6e, 0xdd, 0x00, 0xb9, \ + 0x00, 0x89, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6e, 0xd9, \ + 0x02, 0xb4, 0x88, 0x55, \ + 0x0e, 0xc4, 0xfa, 0x80, \ + 0x8f, 0x1b, 0xfa, 0x80, \ + 0x83, 0x71, 0x83, 0x11, \ + 0x0e, 0xb0, 0x80, 0x65, \ + 0x16, 0x0a, 0xe4, 0x04, \ + 0x80, 0x55, 0xf3, 0xfc, \ + 0x10, 0x0a, 0x83, 0x71, \ + 0x40, 0x71, 0xe3, 0xfc, \ + 0xc0, 0x30, 0x6e, 0xdd, \ + 0x93, 0x8b, 0x80, 0xc5, \ + 0x80, 0x65, 0x83, 0x11, \ + 0xe4, 0x04, 0x02, 0x80, \ + 0x8e, 0x6f, 0xf1, 0x71, \ + 0x6e, 0xd9, 0x40, 0x11, \ + 0x4e, 0x60, 0xc0, 0x30, \ + 0x87, 0x85, 0xfc, 0x08, \ + 0x94, 0x2b, 0xbe, 0x2e, \ + 0xe0, 0x84, 0x02, 0xc4, \ + 0xfa, 0x80, 0x14, 0xb4, \ + 0xfa, 0x80, 0x02, 0x14, \ + 0x83, 0x11, 0x83, 0xab, \ + 0x80, 0x65, 0x83, 0xa1, \ + 0xe4, 0x04, 0x14, 0xb0, \ + 0xf3, 0xfc, 0x16, 0x0a, \ + 0xe1, 0xac, 0x16, 0x44, \ + 0x83, 0xa1, 0x80, 0x55, \ + 0xe3, 0xfc, 0x10, 0x0a, \ + 0x6e, 0xdd, 0x40, 0xa1, \ + 0x82, 0x25, 0xc0, 0x30, \ + 0x02, 0x94, 0xc2, 0x97, \ + 0x83, 0x8b, 0xfa, 0x80, \ + 0x80, 0x65, 0x83, 0x11, \ + 0xe4, 0x04, 0x02, 0x80, \ + 0xe0, 0x80, 0x56, 0x0a, \ + 0x6e, 0xd9, 0x40, 0x11, \ + 0x80, 0x85, 0xc0, 0x30, \ + 0xf4, 0x30, 0x55, 0x0a, \ + 0x56, 0x4a, 0x80, 0xf5, \ + 0x80, 0xc5, 0xe0, 0x80, \ + 0xe0, 0x80, 0x54, 0x4a, \ + 0xcc, 0x79, 0x80, 0x65, \ + 0x4e, 0x60, 0x81, 0x35, \ + 0x84, 0xab, 0xf3, 0xe0, \ + 0x4e, 0x0a, 0xbe, 0x24, \ + 0x40, 0x71, 0xe0, 0x84, \ + 0xc0, 0x30, 0x6e, 0xf5, \ + 0x02, 0x54, 0x84, 0x25, \ + 0x18, 0xc4, 0xfa, 0x80, \ + 0x99, 0x1b, 0xfa, 0x80, \ + 0x80, 0x65, 0x83, 0x11, \ + 0xe4, 0x04, 0x02, 0x50, \ + 0xf3, 0xfc, 0x0a, 0x0a, \ + 0x83, 0x11, 0x80, 0x55, \ + 0x6e, 0xe1, 0x40, 0x11, \ + 0x00, 0x59, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6e, 0xe5, \ + 0xe3, 0xfc, 0x0a, 0x0a, \ + 0xff, 0xf8, 0x02, 0x0e, \ + 0x02, 0x50, 0x80, 0x65, \ + 0x48, 0x0a, 0xe4, 0x04, \ + 0x40, 0x11, 0xe0, 0xac, \ + 0xc0, 0x30, 0x6e, 0xe5, \ + 0x02, 0x74, 0x86, 0xf5, \ + 0x42, 0x4a, 0xfd, 0x0c, \ + 0x40, 0x11, 0xe0, 0x8c, \ + 0xc0, 0x30, 0x6e, 0xf5, \ + 0x06, 0x0a, 0x8e, 0x65, \ + 0x80, 0xe5, 0xfb, 0xfc, \ + 0xfa, 0x80, 0x02, 0x34, \ + 0xfa, 0x80, 0x06, 0x24, \ + 0x84, 0xf5, 0x83, 0x3b, \ + 0x6e, 0xd5, 0x00, 0x29, \ + 0x04, 0x0a, 0xc0, 0x30, \ + 0x83, 0x11, 0xe3, 0xf0, \ + 0x6e, 0xf1, 0x40, 0x11, \ + 0x02, 0x0e, 0xc0, 0x30, \ + 0x88, 0x35, 0xff, 0xf8, \ + 0x06, 0x0a, 0x85, 0x25, \ + 0x82, 0x85, 0xfb, 0xfc, \ + 0xfa, 0x80, 0x02, 0x34, \ + 0xfa, 0x80, 0x06, 0x24, \ + 0x80, 0xc5, 0x83, 0x3b, \ + 0x40, 0x21, 0x82, 0x21, \ + 0xc0, 0x30, 0x6e, 0xf1, \ + 0x00, 0x29, 0x83, 0x75, \ + 0xc0, 0x30, 0x6e, 0xd5, \ + 0x04, 0x0a, 0x83, 0x11, \ + 0x40, 0x11, 0xe3, 0xf0, \ + 0xc0, 0x30, 0x6e, 0xf1, \ + 0xfd, 0x0c, 0x02, 0x74, \ + 0x6e, 0xf5, 0x40, 0x11, \ + 0x02, 0x0e, 0xc0, 0x30, \ + 0x80, 0x65, 0xff, 0xf8, \ + 0xe4, 0x04, 0x02, 0x20, \ + 0x6e, 0xd5, 0x40, 0x11, \ + 0x00, 0x19, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6e, 0xd5, \ + 0x00, 0x19, 0xa1, 0x1b, \ + 0xc0, 0x30, 0x6e, 0xf5, \ + 0x42, 0x10, 0x86, 0xe5, \ + 0x00, 0x29, 0xf3, 0xbc, \ + 0xc0, 0x31, 0x65, 0x71, \ + 0x6e, 0xf5, 0x40, 0x11, \ + 0x43, 0xc6, 0xc0, 0x30, \ + 0x84, 0x34, 0xe1, 0x44, \ + 0xf3, 0xf8, 0x44, 0x20, \ + 0x40, 0x21, 0x81, 0x41, \ + 0xc0, 0x31, 0x65, 0x71, \ + 0x65, 0x75, 0x40, 0x41, \ + 0x8e, 0x28, 0xc0, 0x31, \ + 0xf3, 0xdc, 0x44, 0x20, \ + 0x84, 0x14, 0x8e, 0x26, \ + 0x44, 0x20, 0x86, 0x28, \ + 0x86, 0x26, 0xf3, 0xbc, \ + 0x42, 0x4a, 0x83, 0x45, \ + 0x82, 0x25, 0xe0, 0x8c, \ + 0x6e, 0x51, 0x00, 0x29, \ + 0x44, 0x4a, 0xc0, 0x2f, \ + 0x80, 0x65, 0xe0, 0x88, \ + 0x81, 0x65, 0xe0, 0x19, \ + 0x6e, 0xf5, 0x00, 0x19, \ + 0x42, 0x10, 0xc0, 0x30, \ + 0x40, 0x11, 0xf3, 0xbc, \ + 0xc0, 0x30, 0x6e, 0xf5, \ + 0x6e, 0xf5, 0x00, 0x19, \ + 0x42, 0x4a, 0xc0, 0x30, \ + 0x84, 0xc5, 0xe0, 0x8c, \ + 0x6e, 0xf1, 0x00, 0x19, \ + 0xa3, 0x1b, 0xc0, 0x30, \ + 0x00, 0x15, 0x84, 0x35, \ + 0xc0, 0x0c, 0x63, 0x95, \ + 0x82, 0x85, 0xa1, 0x1b, \ + 0x63, 0xb1, 0x00, 0x25, \ + 0x95, 0x2b, 0xc0, 0x0c, \ + 0x80, 0x65, 0x83, 0x11, \ + 0xe4, 0x04, 0x02, 0x20, \ + 0x63, 0xb1, 0x40, 0x15, \ + 0x90, 0xf1, 0xc0, 0x0c, \ + 0xf0, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x5c, 0xd8, 0x80, 0x6b, \ + 0x0c, 0x01, 0xe8, 0x0c, \ + 0xc0, 0x30, 0x6e, 0xe9, \ + 0xe1, 0xb4, 0x01, 0x0a, \ + 0x0c, 0x05, 0x80, 0xf5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe0, 0xe0, 0x00, 0x0a, \ + 0x0c, 0x39, 0x82, 0xb5, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x66, 0xc1, 0x00, 0x60, \ + 0x85, 0x11, 0xc0, 0x56, \ + 0x0c, 0x0c, 0xa5, 0x21, \ + 0x33, 0x98, 0xf2, 0x00, \ + 0x0c, 0x0c, 0xff, 0xd0, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x30, 0x6e, 0xe5, \ + 0x6f, 0x05, 0x0c, 0x19, \ + 0xf9, 0x21, 0xc0, 0x30, \ + 0x40, 0x12, 0xa1, 0x0b, \ + 0x4c, 0x01, 0xf8, 0x08, \ + 0xc0, 0x30, 0x6f, 0x05, \ + 0x0c, 0x29, 0x80, 0xf5, \ + 0xc0, 0x30, 0x6e, 0x6d, \ + 0xe0, 0x84, 0x44, 0x0a, \ + 0x0c, 0x15, 0x92, 0xb5, \ + 0xc0, 0x0c, 0x63, 0xb1, \ + 0x84, 0x85, 0xa1, 0x1b, \ + 0x63, 0x95, 0x0c, 0x25, \ + 0x85, 0x1b, 0xc0, 0x0c, \ + 0x0c, 0x19, 0x82, 0xf5, \ + 0xc0, 0x30, 0x6e, 0xf1, \ + 0x82, 0x55, 0xa3, 0x1b, \ + 0x67, 0x35, 0x0c, 0x1b, \ + 0x42, 0x0a, 0xc0, 0x18, \ + 0x02, 0x14, 0xe0, 0xb8, \ + 0x8a, 0x95, 0xfb, 0x00, \ + 0x0c, 0x09, 0x85, 0xf5, \ + 0xc0, 0x30, 0x6e, 0x6d, \ + 0xe0, 0x88, 0x40, 0x0a, \ + 0x0c, 0x09, 0x8a, 0x85, \ + 0xc0, 0x30, 0x6e, 0x85, \ + 0x88, 0xe5, 0xa1, 0x0b, \ + 0x57, 0x58, 0x8c, 0x0b, \ + 0x0c, 0x11, 0xe8, 0x0c, \ + 0xc0, 0x30, 0x6e, 0xed, \ + 0x6e, 0x85, 0x0c, 0x29, \ + 0x0c, 0x01, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6e, 0xe9, \ + 0xf1, 0x88, 0x46, 0x12, \ + 0x80, 0x95, 0x80, 0x3d, \ + 0x81, 0x2d, 0x82, 0x23, \ + 0x0c, 0x13, 0x84, 0x85, \ + 0xc0, 0x18, 0x67, 0x35, \ + 0xe0, 0xb8, 0x42, 0x0a, \ + 0x6e, 0xed, 0x4c, 0x01, \ + 0x82, 0x05, 0xc0, 0x30, \ + 0x6f, 0x05, 0x0c, 0x09, \ + 0xc2, 0x09, 0xc0, 0x30, \ + 0x6f, 0x05, 0x4c, 0x01, \ + 0xff, 0x01, 0xc0, 0x30, \ + 0x82, 0x65, 0x80, 0x1d, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc6, 0x19, 0xdd, 0x93, \ + 0x6f, 0x05, 0x4c, 0x11, \ + 0x0c, 0x0c, 0xc0, 0x30, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0x6f, 0x05, 0x0c, 0x09, \ + 0xc4, 0x09, 0xc0, 0x30, \ + 0x6f, 0x05, 0x4c, 0x01, \ + 0x0c, 0x0c, 0xc0, 0x30, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0x82, 0x05, 0xa2, 0x3d, \ + 0x82, 0x65, 0xa5, 0x3b, \ + 0x82, 0x65, 0xa7, 0x3b, \ + 0x82, 0xb5, 0xa9, 0x3b, \ + 0xa1, 0x3b, 0x8b, 0x85, \ + 0xa3, 0x3b, 0x82, 0x05, \ + 0x81, 0xb5, 0x82, 0x15, \ + 0x83, 0x95, 0x81, 0x31, \ + 0x83, 0x75, 0x83, 0x31, \ + 0xff, 0x74, 0x24, 0x68, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xa1, 0x2b, 0xc5, 0x27, \ + 0xc5, 0x34, 0x82, 0x45, \ + 0xec, 0x04, 0x04, 0x20, \ + 0x46, 0x30, 0xa1, 0x2b, \ + 0xdf, 0xff, 0x7b, 0xfd, \ + 0xfe, 0x15, 0xc1, 0x3e, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0xa1, 0x2b, 0xc5, 0x27, \ + 0x90, 0x01, 0x8c, 0x25, \ + 0x4b, 0xfe, 0x81, 0x41, \ + 0xc0, 0x3f, 0x7f, 0xfd, \ + 0x84, 0x84, 0xa3, 0x3b, \ + 0xc4, 0xc4, 0x86, 0x74, \ + 0x82, 0xb5, 0xc6, 0x64, \ + 0x7b, 0xfd, 0x50, 0x80, \ + 0x4e, 0x70, 0xdf, 0xff, \ + 0xdf, 0xff, 0x7b, 0xfd, \ + 0x7b, 0xfd, 0x58, 0xc0, \ + 0x4c, 0x60, 0xdf, 0xff, \ + 0xdf, 0xff, 0x7b, 0xfd, \ + 0xe0, 0xe0, 0x12, 0x74, \ + 0xb0, 0x9f, 0x8a, 0x8f, \ + 0xf8, 0x08, 0x40, 0x95, \ + 0x10, 0xc4, 0xd1, 0x77, \ + 0x0e, 0x74, 0xe0, 0xc0, \ + 0xae, 0x8f, 0xfb, 0x80, \ + 0xe1, 0xc7, 0xd0, 0x67, \ + 0x18, 0xc4, 0x88, 0x41, \ + 0xc5, 0x4d, 0xfa, 0x80, \ + 0xf8, 0x04, 0x40, 0x85, \ + 0x40, 0x65, 0xb8, 0x6f, \ + 0xa0, 0x11, 0xf0, 0x0c, \ + 0x0c, 0x0c, 0xf4, 0xd5, \ + 0xdd, 0x93, 0xf0, 0x1c, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0xa1, 0x2b, 0xc5, 0x27, \ + 0x90, 0x01, 0x8a, 0x45, \ + 0x49, 0xfe, 0x81, 0x31, \ + 0xc0, 0x3f, 0x7f, 0xfd, \ + 0x7f, 0xf1, 0x0a, 0x0e, \ + 0x18, 0x0e, 0xc0, 0x3f, \ + 0xc0, 0x3f, 0x70, 0x01, \ + 0xf0, 0x0c, 0x02, 0x65, \ + 0xf8, 0x08, 0x02, 0x75, \ + 0xf8, 0x04, 0x02, 0x85, \ + 0xf8, 0x10, 0x52, 0x62, \ + 0xf8, 0x08, 0x40, 0x95, \ + 0xe0, 0xa0, 0x12, 0x74, \ + 0xf1, 0x67, 0x8a, 0x9f, \ + 0x0c, 0x84, 0xac, 0x9f, \ + 0x40, 0x95, 0xe0, 0xc0, \ + 0x98, 0x6f, 0xf8, 0x04, \ + 0x86, 0x31, 0xe1, 0x77, \ + 0xc5, 0x3d, 0xae, 0x6f, \ + 0xf0, 0x10, 0x40, 0x65, \ + 0x40, 0x85, 0xd1, 0x87, \ + 0xf8, 0x75, 0xf8, 0x0c, \ + 0xf0, 0x1c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0xe0, 0x18, 0x00, 0x29, \ + 0x00, 0x39, 0xc5, 0x27, \ + 0x81, 0x41, 0xe0, 0x10, \ + 0xbe, 0x4e, 0xa3, 0x2b, \ + 0xfe, 0x4e, 0xfc, 0x4e, \ + 0xe6, 0x8a, 0x46, 0x3a, \ + 0xa7, 0x2b, 0x82, 0x05, \ + 0xa5, 0x2b, 0x82, 0x05, \ + 0x83, 0xc1, 0x82, 0x95, \ + 0x81, 0x65, 0x81, 0x51, \ + 0x81, 0x35, 0x81, 0x51, \ + 0x85, 0x51, 0x83, 0x41, \ + 0xbe, 0x5e, 0x81, 0xc1, \ + 0xfe, 0x4e, 0xfc, 0xce, \ + 0xfb, 0xfc, 0x06, 0x0a, \ + 0xc0, 0x08, 0x84, 0x45, \ + 0x81, 0x01, 0x80, 0x13, \ + 0xfa, 0x80, 0x08, 0x04, \ + 0xe1, 0x0a, 0x48, 0x4a, \ + 0x0a, 0xf0, 0xc4, 0x47, \ + 0x0a, 0x45, 0xe4, 0x10, \ + 0x82, 0x01, 0xfc, 0x10, \ + 0xf4, 0x0c, 0x40, 0x8a, \ + 0xfc, 0x75, 0xc5, 0x46, \ + 0xdd, 0x93, 0xa0, 0xf1, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0xe0, 0x1c, 0x00, 0x29, \ + 0x00, 0x39, 0x81, 0x41, \ + 0xbe, 0x4e, 0xe0, 0x14, \ + 0xfc, 0x4e, 0xc5, 0x27, \ + 0xfe, 0x4e, 0xa3, 0x2b, \ + 0xe6, 0x8a, 0x46, 0x3a, \ + 0x81, 0x51, 0x81, 0xc1, \ + 0xa7, 0x2b, 0x82, 0x45, \ + 0xa5, 0x2b, 0x80, 0xc5, \ + 0x85, 0xc1, 0x82, 0x35, \ + 0x81, 0x35, 0x81, 0x51, \ + 0x85, 0xc1, 0x83, 0x51, \ + 0xfc, 0x4e, 0xbe, 0xce, \ + 0x06, 0x0a, 0xfe, 0x5e, \ + 0x84, 0x45, 0xfb, 0xfc, \ + 0x80, 0x13, 0xc2, 0x08, \ + 0x08, 0x04, 0x81, 0x01, \ + 0x48, 0x4a, 0xfa, 0x80, \ + 0xc4, 0x47, 0xe1, 0x0a, \ + 0xe4, 0x10, 0x0a, 0xf0, \ + 0xfc, 0x10, 0x0a, 0x45, \ + 0x40, 0x8a, 0x82, 0x01, \ + 0xc5, 0x46, 0xf4, 0x0c, \ + 0xa0, 0xf1, 0xfc, 0x75, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x82, 0x65, 0xa1, 0x1b, \ + 0x04, 0x59, 0x81, 0x41, \ + 0xa1, 0x5b, 0xfc, 0x00, \ + 0xcd, 0x06, 0x80, 0x75, \ + 0xc3, 0x4d, 0x82, 0x41, \ + 0xfe, 0x15, 0x82, 0x01, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0x82, 0x65, 0xa1, 0x1b, \ + 0x04, 0x59, 0x81, 0x41, \ + 0xa1, 0x5b, 0xfc, 0x00, \ + 0xcd, 0x06, 0x80, 0x75, \ + 0xc3, 0x4d, 0x82, 0x41, \ + 0xfe, 0x15, 0x82, 0x01, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0xe1, 0x0c, 0x06, 0x46, \ + 0x06, 0x86, 0x8c, 0xc4, \ + 0x09, 0x86, 0xe1, 0x0c, \ + 0x8c, 0x64, 0xe1, 0x0c, \ + 0x07, 0x06, 0x90, 0x54, \ + 0x4e, 0x06, 0xe1, 0x0c, \ + 0x8c, 0x44, 0xe1, 0x0c, \ + 0x8b, 0x71, 0x9c, 0x34, \ + 0xe4, 0x60, 0x10, 0x00, \ + 0xf1, 0x96, 0xa0, 0x98, \ + 0x0e, 0x70, 0xe1, 0x98, \ + 0xa1, 0x7b, 0xec, 0x04, \ + 0xfe, 0x35, 0xd9, 0x96, \ + 0x0c, 0x00, 0x89, 0xc1, \ + 0xd9, 0x78, 0xe4, 0x74, \ + 0xec, 0x04, 0x18, 0xc0, \ + 0xd5, 0x76, 0xa1, 0xcb, \ + 0x9d, 0x51, 0xfe, 0x75, \ + 0xf1, 0x68, 0x80, 0xcb, \ + 0xec, 0x04, 0x0a, 0x50, \ + 0xd1, 0x66, 0xa1, 0x5b, \ + 0x95, 0x41, 0xfe, 0x75, \ + 0xc1, 0x58, 0x9c, 0x01, \ + 0xec, 0x04, 0x08, 0x40, \ + 0xcd, 0x56, 0xa1, 0x4b, \ + 0xa1, 0x1b, 0xfe, 0x75, \ + 0x00, 0x20, 0x88, 0x25, \ + 0xff, 0x31, 0xec, 0x04, \ + 0xe8, 0x80, 0x00, 0x34, \ + 0xc0, 0x0b, 0xa5, 0x3f, \ + 0x81, 0x41, 0xc6, 0x3b, \ + 0xe1, 0x00, 0x0a, 0x06, \ + 0x81, 0xc1, 0x81, 0x61, \ + 0x80, 0xf5, 0xe9, 0x2d, \ + 0xe8, 0x90, 0x0e, 0x04, \ + 0x0e, 0x54, 0x8e, 0xc3, \ + 0x81, 0x35, 0xe8, 0x98, \ + 0xe8, 0x90, 0x0e, 0x34, \ + 0x8d, 0x1b, 0x82, 0x61, \ + 0x86, 0x41, 0x8e, 0xc3, \ + 0x00, 0x0e, 0xfc, 0x55, \ + 0xa0, 0xcf, 0xf8, 0x00, \ + 0x18, 0x06, 0x81, 0x35, \ + 0x01, 0x06, 0xe8, 0x00, \ + 0x80, 0x04, 0xe1, 0x04, \ + 0x0c, 0x0c, 0x80, 0xcc, \ + 0xdd, 0x93, 0xf0, 0x1c, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x42, 0x16, 0x80, 0x6b, \ + 0x4a, 0x38, 0xe0, 0x00, \ + 0x0c, 0x09, 0xe8, 0x10, \ + 0xc0, 0x2f, 0x6e, 0x85, \ + 0xe1, 0x0c, 0x02, 0xc6, \ + 0xa9, 0x0b, 0x84, 0x14, \ + 0x66, 0x71, 0x0e, 0x60, \ + 0x00, 0x0c, 0xc0, 0xbf, \ + 0x84, 0x06, 0xe2, 0x00, \ + 0x8c, 0x1b, 0x8e, 0x0b, \ + 0xef, 0xfc, 0x70, 0x28, \ + 0xe4, 0x38, 0x10, 0x60, \ + 0x90, 0x1b, 0x8e, 0x0b, \ + 0xef, 0xfc, 0x73, 0x08, \ + 0x6e, 0x79, 0x0c, 0x09, \ + 0x0c, 0x19, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x81, \ + 0xe4, 0x60, 0x06, 0x60, \ + 0x76, 0x98, 0x8c, 0x2b, \ + 0x0c, 0x09, 0xef, 0xfc, \ + 0xc0, 0x2f, 0x6e, 0x7d, \ + 0x6e, 0x85, 0x0c, 0x19, \ + 0x06, 0x60, 0xc0, 0x2f, \ + 0x90, 0x2b, 0xe4, 0x74, \ + 0xef, 0xfc, 0x74, 0xe8, \ + 0x6e, 0x81, 0x0c, 0x19, \ + 0x0c, 0x29, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x85, \ + 0x76, 0x58, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xef, 0xfc, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0xe0, 0x10, 0x28, 0x88, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0xed, 0x20, 0x1e, 0xf0, \ + 0x88, 0x9b, 0x8a, 0x7b, \ + 0x82, 0xbb, 0x84, 0xab, \ + 0x5e, 0xa1, 0x80, 0x6b, \ + 0x91, 0x3e, 0xe1, 0x10, \ + 0xe1, 0x00, 0x5e, 0x31, \ + 0xe0, 0xf0, 0x5e, 0x91, \ + 0xe0, 0x04, 0x40, 0x16, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x81, 0x01, 0xc0, 0x2f, \ + 0x9b, 0x0e, 0x9d, 0x0e, \ + 0x5e, 0x01, 0x99, 0x0e, \ + 0x5e, 0x01, 0xe0, 0xb0, \ + 0x5e, 0x01, 0xe0, 0xa0, \ + 0x02, 0x06, 0xe0, 0x90, \ + 0x8c, 0x0b, 0xe2, 0x80, \ + 0xe8, 0x10, 0x44, 0x28, \ + 0x9f, 0x21, 0x81, 0x11, \ + 0x8c, 0x3b, 0x8c, 0x0b, \ + 0xe8, 0x04, 0x72, 0x08, \ + 0xe0, 0x90, 0x00, 0xb4, \ + 0xf1, 0xc0, 0x50, 0x00, \ + 0x00, 0xb4, 0x89, 0xbe, \ + 0x8f, 0x11, 0xe1, 0x8c, \ + 0xf1, 0x80, 0x40, 0x12, \ + 0x0e, 0x6a, 0x8f, 0x7e, \ + 0x80, 0x83, 0xe0, 0x04, \ + 0x00, 0x98, 0x90, 0x2b, \ + 0x80, 0x00, 0x40, 0x01, \ + 0xe4, 0x20, 0x04, 0x80, \ + 0x5e, 0x01, 0x91, 0x01, \ + 0x9f, 0x11, 0xe0, 0x90, \ + 0x91, 0xb4, 0xfc, 0x1e, \ + 0xb2, 0xbf, 0xb4, 0xbf, \ + 0x84, 0x95, 0xa1, 0xbb, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0xbd, 0x01, 0xc0, 0x2f, \ + 0x6d, 0x75, 0x4c, 0x01, \ + 0x00, 0x60, 0xc0, 0x28, \ + 0xc0, 0x56, 0x66, 0xf1, \ + 0x84, 0x7b, 0x81, 0x11, \ + 0x57, 0xf8, 0xbd, 0x21, \ + 0x8e, 0x2b, 0xef, 0xd0, \ + 0x5e, 0x01, 0x9b, 0x01, \ + 0x1e, 0x79, 0xe1, 0x00, \ + 0x94, 0x93, 0xe1, 0x00, \ + 0xe8, 0x7d, 0x92, 0x73, \ + 0x8d, 0x8e, 0x9e, 0x89, \ + 0xe0, 0x3c, 0x5e, 0x27, \ + 0x92, 0x95, 0x8b, 0x2e, \ + 0x14, 0x60, 0x91, 0x6e, \ + 0xc0, 0x56, 0x66, 0xf1, \ + 0xe1, 0x10, 0x1e, 0x69, \ + 0xe1, 0x00, 0x1e, 0x89, \ + 0xe0, 0xf0, 0x1e, 0xb9, \ + 0x00, 0x39, 0x91, 0x04, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x40, 0x11, 0xbd, 0x11, \ + 0xc0, 0x28, 0x6d, 0x75, \ + 0x81, 0x11, 0x81, 0x91, \ + 0x94, 0x0b, 0xbd, 0x21, \ + 0xef, 0xd0, 0x55, 0x88, \ + 0xf0, 0x00, 0x0c, 0x1a, \ + 0xf0, 0x00, 0x0c, 0x4a, \ + 0x00, 0x48, 0x81, 0x01, \ + 0x00, 0x60, 0xa0, 0x00, \ + 0x02, 0x0c, 0xe4, 0x04, \ + 0x10, 0x1a, 0xe2, 0x00, \ + 0x82, 0x73, 0xf0, 0x00, \ + 0xf0, 0x00, 0x10, 0x4a, \ + 0x00, 0x48, 0x81, 0x11, \ + 0x02, 0x80, 0xa0, 0x00, \ + 0x16, 0x1a, 0xe4, 0x04, \ + 0x04, 0x0c, 0xf0, 0x00, \ + 0x84, 0x73, 0xe2, 0x00, \ + 0xf0, 0x00, 0x16, 0x4a, \ + 0xa0, 0x00, 0x00, 0x48, \ + 0xe4, 0x04, 0x12, 0xb0, \ + 0xe2, 0x00, 0x04, 0x0c, \ + 0xeb, 0x7d, 0x84, 0x73, \ + 0x82, 0x8b, 0x92, 0xbb, \ + 0xf2, 0x75, 0x80, 0x6b, \ + 0xe1, 0x10, 0x5e, 0x01, \ + 0xe1, 0x00, 0x5e, 0x11, \ + 0xe0, 0xf0, 0x5e, 0x91, \ + 0x8f, 0x14, 0x91, 0x64, \ + 0x8b, 0x24, 0x8d, 0x84, \ + 0x1e, 0x09, 0x81, 0x45, \ + 0x8f, 0x14, 0xe1, 0x10, \ + 0xe0, 0x04, 0x02, 0x2a, \ + 0xe4, 0x20, 0x16, 0x80, \ + 0x00, 0x48, 0x91, 0xbe, \ + 0x80, 0x00, 0x40, 0x01, \ + 0x00, 0x0a, 0x90, 0xbb, \ + 0x54, 0x22, 0xfb, 0xfc, \ + 0xbe, 0xa9, 0xf1, 0xac, \ + 0x56, 0x0a, 0x88, 0x25, \ + 0x84, 0x55, 0xe0, 0x80, \ + 0x84, 0x45, 0xa1, 0xab, \ + 0xe4, 0x40, 0x00, 0x20, \ + 0xf3, 0xfc, 0x01, 0x0a, \ + 0xe0, 0xb0, 0x5e, 0x01, \ + 0x84, 0x55, 0x96, 0x0b, \ + 0xe4, 0x3c, 0x02, 0xb0, \ + 0xec, 0x04, 0x00, 0xb0, \ + 0xe0, 0xb0, 0x5e, 0x11, \ + 0xa1, 0xab, 0x81, 0xb5, \ + 0x5e, 0x21, 0x80, 0x85, \ + 0x81, 0x55, 0xe0, 0xb0, \ + 0xe4, 0x40, 0x00, 0x20, \ + 0xe0, 0xb0, 0x5e, 0x01, \ + 0x5e, 0x01, 0x96, 0x0b, \ + 0x00, 0xf0, 0xe0, 0xa0, \ + 0xbe, 0x0e, 0xe4, 0xe0, \ + 0xe4, 0xb0, 0x10, 0xf0, \ + 0xe5, 0x10, 0x04, 0xf0, \ + 0xe4, 0xa0, 0x08, 0xf0, \ + 0xe4, 0x90, 0x0a, 0xf0, \ + 0x81, 0x11, 0xbc, 0x8e, \ + 0x8c, 0x3b, 0x8c, 0x0b, \ + 0xe8, 0x00, 0x4b, 0xa8, \ + 0xe1, 0x00, 0x1e, 0x09, \ + 0xe0, 0xa0, 0x1e, 0x19, \ + 0xfc, 0x04, 0xa1, 0x0b, \ + 0xfb, 0x80, 0x4e, 0x12, \ + 0x56, 0x0a, 0x84, 0xa5, \ + 0x82, 0x55, 0xe0, 0x80, \ + 0x14, 0x0a, 0x8b, 0x04, \ + 0x80, 0x45, 0xe0, 0x40, \ + 0x5e, 0x01, 0xa0, 0x01, \ + 0x96, 0x0b, 0xe0, 0xb0, \ + 0x02, 0xb0, 0x81, 0x75, \ + 0x00, 0xb0, 0xe4, 0x3c, \ + 0x5e, 0x11, 0xec, 0x04, \ + 0x5e, 0x01, 0xe0, 0xb0, \ + 0x00, 0xf0, 0xe0, 0xa0, \ + 0xbe, 0x0e, 0xe4, 0xd0, \ + 0xe5, 0x00, 0x04, 0xf0, \ + 0xe4, 0xa0, 0x08, 0xf0, \ + 0xe4, 0x90, 0x0a, 0xf0, \ + 0x83, 0x11, 0xbc, 0x8e, \ + 0x8c, 0x3b, 0x8c, 0x0b, \ + 0xe8, 0x00, 0x48, 0xe8, \ + 0xe0, 0xf0, 0x1e, 0x09, \ + 0xe0, 0xa0, 0x1e, 0x19, \ + 0x52, 0x12, 0xa1, 0x0b, \ + 0x86, 0x65, 0xfb, 0x9c, \ + 0xe0, 0x80, 0x56, 0x4a, \ + 0x16, 0xb0, 0x80, 0xc5, \ + 0x5e, 0xb1, 0xec, 0x04, \ + 0x83, 0x15, 0xe0, 0xb0, \ + 0xa1, 0xab, 0x8b, 0x04, \ + 0xa0, 0x01, 0x82, 0x85, \ + 0xf3, 0xfc, 0x01, 0x0a, \ + 0xe0, 0xb0, 0x5e, 0x01, \ + 0x16, 0xb0, 0x82, 0x15, \ + 0x5e, 0xb1, 0xec, 0x08, \ + 0x81, 0x35, 0xe0, 0xb0, \ + 0xe0, 0xb0, 0x5e, 0x01, \ + 0xe0, 0xa0, 0x5e, 0xb1, \ + 0xe4, 0xc0, 0x00, 0xf0, \ + 0xbc, 0x8e, 0xbe, 0x0e, \ + 0xe4, 0xf0, 0x04, 0xf0, \ + 0xe4, 0xa0, 0x08, 0xf0, \ + 0xe4, 0x90, 0x0a, 0xf0, \ + 0x85, 0x11, 0x85, 0x71, \ + 0x8c, 0x3b, 0x8c, 0x0b, \ + 0xe8, 0x00, 0x45, 0xc8, \ + 0xe0, 0xa0, 0x1e, 0x09, \ + 0xa1, 0x8b, 0x8f, 0x84, \ + 0xfb, 0xa4, 0x42, 0x02, \ + 0x80, 0x45, 0x8d, 0x24, \ + 0x8c, 0x0b, 0x91, 0x24, \ + 0x5e, 0xb8, 0x8c, 0x3b, \ + 0x8c, 0x0b, 0xe8, 0x04, \ + 0x4c, 0xf8, 0x8c, 0x1b, \ + 0x01, 0x46, 0xe8, 0x00, \ + 0x80, 0x14, 0xe1, 0x08, \ + 0x85, 0x21, 0x81, 0x01, \ + 0x40, 0x01, 0x00, 0x48, \ + 0x81, 0x21, 0x80, 0x00, \ + 0x03, 0x86, 0x84, 0x26, \ + 0x84, 0x14, 0xe1, 0x08, \ + 0x03, 0xc6, 0x84, 0x06, \ + 0x84, 0x14, 0xe1, 0x08, \ + 0xe0, 0x3c, 0x1e, 0x07, \ + 0xe2, 0x00, 0x04, 0x0c, \ + 0x42, 0x06, 0x84, 0x26, \ + 0x84, 0x14, 0xe1, 0x08, \ + 0x42, 0x46, 0x84, 0x06, \ + 0x84, 0x14, 0xe1, 0x08, \ + 0xe2, 0x00, 0x04, 0x2c, \ + 0x42, 0x86, 0x84, 0x26, \ + 0x84, 0x14, 0xe1, 0x08, \ + 0x84, 0x26, 0x83, 0x21, \ + 0xe1, 0x08, 0x42, 0xc6, \ + 0x84, 0x76, 0x84, 0x14, \ + 0xe1, 0x08, 0x43, 0x06, \ + 0x84, 0x06, 0x84, 0x14, \ + 0x9b, 0x24, 0x9d, 0x14, \ + 0x89, 0x04, 0x99, 0x34, \ + 0x6d, 0x61, 0x4c, 0x01, \ + 0x4c, 0x11, 0xc0, 0x28, \ + 0xc0, 0x28, 0x6d, 0x65, \ + 0x6d, 0x69, 0x4c, 0x21, \ + 0x4c, 0x31, 0xc0, 0x28, \ + 0xc0, 0x28, 0x6d, 0x6d, \ + 0x50, 0xb8, 0x90, 0x0b, \ + 0x4c, 0x05, 0xe8, 0x00, \ + 0xc0, 0x08, 0x6e, 0x75, \ + 0xe5, 0x20, 0x1e, 0xf0, \ + 0xf6, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x88, 0x7b, 0x8a, 0x6b, \ + 0x84, 0x9b, 0xbe, 0x3e, \ + 0x80, 0xbb, 0x82, 0xab, \ + 0xa1, 0x0b, 0xa4, 0x08, \ + 0x97, 0x04, 0x9c, 0x65, \ + 0x95, 0x84, 0xbc, 0x0e, \ + 0x66, 0xc1, 0x00, 0xb0, \ + 0xfc, 0x0e, 0xc0, 0x56, \ + 0x98, 0x18, 0x9c, 0x08, \ + 0x80, 0xf5, 0xc3, 0x0d, \ + 0x65, 0x61, 0x16, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0xb5, 0xe0, 0xe4, \ + 0x6e, 0x59, 0x16, 0x39, \ + 0xbb, 0x01, 0xc0, 0x2f, \ + 0x6d, 0x75, 0x56, 0x01, \ + 0x81, 0x11, 0xc0, 0x28, \ + 0xfc, 0x04, 0xbb, 0x21, \ + 0xef, 0xcc, 0x72, 0xc8, \ + 0xa0, 0x18, 0x9c, 0x08, \ + 0xf0, 0x80, 0x44, 0xb2, \ + 0x04, 0x39, 0xa0, 0x13, \ + 0xc0, 0x28, 0x68, 0x31, \ + 0xe1, 0x90, 0x04, 0x14, \ + 0xfa, 0x80, 0x02, 0x24, \ + 0x02, 0x44, 0x83, 0x41, \ + 0x46, 0x30, 0xe8, 0x84, \ + 0x82, 0x3f, 0xfb, 0xfc, \ + 0x86, 0x35, 0xa1, 0x3b, \ + 0xf4, 0x10, 0x04, 0x8a, \ + 0x97, 0x11, 0x82, 0x15, \ + 0x02, 0x14, 0xa4, 0x13, \ + 0x83, 0x21, 0xfa, 0x80, \ + 0xe8, 0x84, 0x02, 0x24, \ + 0x06, 0x29, 0xbe, 0x34, \ + 0x86, 0x03, 0xfc, 0x00, \ + 0x80, 0x26, 0xa2, 0x2f, \ + 0x00, 0x00, 0xa4, 0x08, \ + 0xa4, 0x06, 0xec, 0x04, \ + 0x84, 0x04, 0xbc, 0x14, \ + 0x84, 0x0e, 0x82, 0x01, \ + 0x00, 0xa0, 0xa0, 0x18, \ + 0xe5, 0x0d, 0xec, 0x04, \ + 0xe4, 0x80, 0x04, 0x10, \ + 0xfa, 0x00, 0x00, 0x24, \ + 0x82, 0x65, 0xa0, 0x26, \ + 0x84, 0x0d, 0xff, 0x21, \ + 0x00, 0x10, 0x86, 0x55, \ + 0xa0, 0x06, 0xed, 0x88, \ + 0x00, 0x00, 0x9c, 0x08, \ + 0x83, 0x25, 0xec, 0x08, \ + 0x84, 0x35, 0xa1, 0xab, \ + 0x82, 0x0d, 0xff, 0x11, \ + 0x40, 0x0a, 0x82, 0xd5, \ + 0x43, 0x16, 0xe0, 0x80, \ + 0x80, 0x65, 0xe0, 0x04, \ + 0xff, 0xf8, 0x03, 0x1e, \ + 0xa0, 0x06, 0xa2, 0x03, \ + 0x00, 0x00, 0x9c, 0x08, \ + 0x9c, 0x06, 0xec, 0x04, \ + 0xa1, 0x0b, 0xa4, 0x08, \ + 0x98, 0xf1, 0xe4, 0xd5, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0x06, 0x00, 0x91, 0x21, \ + 0xc0, 0x90, 0x67, 0x21, \ + 0x04, 0x20, 0x81, 0x41, \ + 0xa1, 0x2b, 0xec, 0x04, \ + 0xfe, 0x95, 0xcd, 0x4e, \ + 0x81, 0x51, 0x81, 0x21, \ + 0x81, 0x41, 0x81, 0x31, \ + 0x84, 0x48, 0xbe, 0x4e, \ + 0xe1, 0x84, 0x18, 0x44, \ + 0xe1, 0x94, 0x0c, 0x44, \ + 0xe1, 0x9c, 0x0e, 0x44, \ + 0xae, 0x6f, 0x84, 0x69, \ + 0xe1, 0x8c, 0x0e, 0x44, \ + 0x88, 0x79, 0x90, 0xc9, \ + 0x04, 0x8a, 0xb8, 0x6f, \ + 0xae, 0x6f, 0xf4, 0x08, \ + 0xc8, 0x67, 0x9e, 0x49, \ + 0x80, 0x75, 0xa9, 0xc1, \ + 0xe0, 0x00, 0x58, 0x16, \ + 0x8b, 0x61, 0xac, 0x4f, \ + 0x9f, 0x61, 0x80, 0x55, \ + 0x0e, 0x44, 0xfe, 0x1e, \ + 0x10, 0x20, 0xfa, 0x80, \ + 0x81, 0x91, 0xe4, 0x10, \ + 0x84, 0xab, 0xfc, 0x2e, \ + 0xe4, 0xa4, 0x4e, 0x0a, \ + 0x06, 0x0a, 0x86, 0x85, \ + 0x06, 0x5a, 0xf0, 0x00, \ + 0x08, 0x1c, 0xf0, 0x00, \ + 0x98, 0xbb, 0xe2, 0x00, \ + 0x8c, 0xbb, 0x80, 0x45, \ + 0xfa, 0x80, 0x02, 0x54, \ + 0xfa, 0x80, 0x04, 0xa4, \ + 0xc4, 0x27, 0x89, 0x1b, \ + 0xf0, 0x88, 0x48, 0x02, \ + 0x16, 0x06, 0x80, 0x75, \ + 0x08, 0x15, 0xe0, 0x10, \ + 0xc0, 0x09, 0x60, 0xc9, \ + 0x48, 0x15, 0x96, 0x13, \ + 0xc0, 0x09, 0x60, 0xc9, \ + 0xe0, 0x0c, 0x12, 0x1a, \ + 0xfb, 0xfc, 0x0a, 0x0a, \ + 0x20, 0x01, 0x00, 0x48, \ + 0x90, 0xab, 0xc0, 0x00, \ + 0x12, 0x1a, 0x82, 0x91, \ + 0x0a, 0x0c, 0xe0, 0x20, \ + 0x3d, 0x58, 0xe2, 0x00, \ + 0xc3, 0xff, 0x3f, 0xfd, \ + 0x08, 0x1a, 0xbe, 0x44, \ + 0x82, 0x41, 0xfc, 0x0c, \ + 0xe2, 0x00, 0x02, 0x1c, \ + 0xf4, 0x40, 0x08, 0x9a, \ + 0xe2, 0x00, 0x0a, 0x0c, \ + 0x82, 0x23, 0xfc, 0x24, \ + 0xfd, 0x00, 0x06, 0x34, \ + 0x82, 0x11, 0xfe, 0x14, \ + 0x3f, 0xfd, 0x39, 0xe8, \ + 0xa0, 0xf1, 0xc3, 0xff, \ + 0xf0, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xa9, 0x01, 0xa1, 0x0b, \ + 0x40, 0x16, 0x80, 0x65, \ + 0x8b, 0x41, 0xe0, 0x00, \ + 0x9f, 0x41, 0x80, 0x45, \ + 0xe6, 0x8a, 0x40, 0x0a, \ + 0x44, 0x4a, 0xd0, 0x17, \ + 0x82, 0x03, 0xe6, 0x8e, \ + 0xdd, 0x93, 0x84, 0x03, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x40, 0x56, 0x80, 0x6b, \ + 0x4c, 0x01, 0xe0, 0x00, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xbc, 0x7e, 0x81, 0x71, \ + 0x72, 0xb8, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x00, \ + 0xc0, 0x2f, 0x67, 0x19, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x6e, 0x89, 0x4c, 0x71, \ + 0x82, 0x25, 0xc0, 0x30, \ + 0x65, 0xf1, 0x00, 0x60, \ + 0x81, 0x11, 0xc0, 0xa0, \ + 0xe0, 0x18, 0x04, 0x06, \ + 0xef, 0x70, 0x61, 0xe8, \ + 0x6e, 0x5d, 0x0c, 0x09, \ + 0xab, 0x0b, 0xc0, 0x2f, \ + 0x0c, 0x09, 0x86, 0x55, \ + 0xc0, 0x30, 0x6f, 0x11, \ + 0xf3, 0xf0, 0x40, 0x00, \ + 0x4c, 0x01, 0xc2, 0x09, \ + 0xc0, 0x30, 0x6f, 0x11, \ + 0x42, 0xd8, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xef, 0xf8, \ + 0xc0, 0x30, 0x6f, 0x11, \ + 0xf3, 0xf0, 0x40, 0x00, \ + 0x83, 0x11, 0xc4, 0x09, \ + 0x6f, 0x11, 0x4c, 0x01, \ + 0x4c, 0x11, 0xc0, 0x30, \ + 0xc0, 0x31, 0x65, 0x79, \ + 0x6d, 0x75, 0x0c, 0x09, \ + 0xa1, 0x0b, 0xc0, 0x28, \ + 0x8a, 0xa5, 0x81, 0x91, \ + 0x68, 0xb8, 0x8c, 0x0b, \ + 0x83, 0x01, 0xef, 0xec, \ + 0x6e, 0x89, 0x4c, 0x01, \ + 0x4c, 0x91, 0xc0, 0x30, \ + 0xc0, 0x28, 0x61, 0xe1, \ + 0x71, 0x18, 0x8c, 0x0b, \ + 0x81, 0x01, 0xef, 0xc0, \ + 0x65, 0x79, 0x4c, 0x01, \ + 0xa3, 0x01, 0xc0, 0x31, \ + 0xef, 0xe0, 0x43, 0x28, \ + 0xe0, 0x00, 0x40, 0x06, \ + 0xef, 0xd0, 0x4d, 0x28, \ + 0x6d, 0x75, 0x0c, 0x29, \ + 0xa1, 0x2b, 0xc0, 0x28, \ + 0x00, 0x60, 0x82, 0x25, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0x46, 0x56, 0x81, 0x11, \ + 0x5d, 0xd8, 0xe0, 0x00, \ + 0x8c, 0x0b, 0xef, 0xcc, \ + 0xef, 0xbc, 0x55, 0xa8, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0x81, 0xb1, 0x81, 0x91, \ + 0x6d, 0x61, 0x0c, 0x29, \ + 0x10, 0xb0, 0xc0, 0x28, \ + 0x02, 0x84, 0xe4, 0x04, \ + 0x8c, 0x0b, 0xfa, 0x80, \ + 0xe8, 0x00, 0x49, 0xa8, \ + 0xeb, 0x7d, 0x80, 0x7b, \ + 0x0c, 0x05, 0xa4, 0x15, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe1, 0x68, 0x00, 0x0a, \ + 0x0c, 0x09, 0xa2, 0x45, \ + 0xc0, 0x28, 0x6d, 0x75, \ + 0x82, 0x85, 0xa1, 0x0b, \ + 0xf4, 0x0c, 0x11, 0x0a, \ + 0x0c, 0x09, 0xee, 0x15, \ + 0xc0, 0x28, 0x6d, 0x75, \ + 0xfb, 0xfc, 0x00, 0x0a, \ + 0xf6, 0xe5, 0x90, 0xbb, \ + 0x8b, 0xa1, 0xf7, 0x05, \ + 0xf0, 0x00, 0x16, 0x0a, \ + 0x96, 0x1b, 0x94, 0x0b, \ + 0x82, 0xc5, 0x96, 0x2b, \ + 0xf0, 0x04, 0x16, 0x0a, \ + 0x16, 0x0a, 0x82, 0x05, \ + 0x84, 0x35, 0xf0, 0x08, \ + 0x81, 0x11, 0x81, 0x01, \ + 0x81, 0x45, 0x94, 0x2b, \ + 0x94, 0x1b, 0x81, 0x01, \ + 0x4c, 0x01, 0x81, 0x21, \ + 0xc0, 0x28, 0x6d, 0x65, \ + 0x6d, 0x69, 0x4c, 0x11, \ + 0x4c, 0x21, 0xc0, 0x28, \ + 0xc0, 0x28, 0x6d, 0x6d, \ + 0x7a, 0x98, 0x8c, 0x0b, \ + 0x9e, 0x1b, 0xef, 0xf4, \ + 0x4b, 0x18, 0x8c, 0x0b, \ + 0xbc, 0x04, 0xe8, 0x00, \ + 0x67, 0x19, 0x0c, 0x19, \ + 0x44, 0x86, 0xc0, 0x2f, \ + 0x40, 0x0a, 0xe0, 0x3c, \ + 0x42, 0x0a, 0xe0, 0x0a, \ + 0xcf, 0x07, 0xe0, 0x80, \ + 0x82, 0xe5, 0xbc, 0x0e, \ + 0xfa, 0x80, 0x02, 0x94, \ + 0x65, 0xf1, 0x04, 0x60, \ + 0x06, 0x14, 0xc0, 0xa0, \ + 0x84, 0x33, 0xe0, 0x88, \ + 0x82, 0x23, 0x8c, 0x0e, \ + 0xe4, 0x00, 0x44, 0xa1, \ + 0xe5, 0x00, 0x44, 0x81, \ + 0x6b, 0x89, 0x0c, 0x1b, \ + 0xc3, 0x0d, 0xc0, 0x17, \ + 0x84, 0x25, 0x82, 0x91, \ + 0xfa, 0x80, 0x02, 0xa4, \ + 0x88, 0x45, 0xcf, 0x1d, \ + 0x00, 0xa4, 0x82, 0xa1, \ + 0xce, 0x0d, 0xfa, 0x80, \ + 0x0c, 0x09, 0xea, 0x15, \ + 0xc0, 0x28, 0x6d, 0x75, \ + 0xfb, 0xfc, 0x00, 0x0a, \ + 0xf5, 0x15, 0xea, 0xc5, \ + 0xf0, 0x14, 0x14, 0x0a, \ + 0x10, 0x0a, 0xd6, 0x15, \ + 0xe6, 0x75, 0xf0, 0x0c, \ + 0x6b, 0x85, 0x0c, 0x1b, \ + 0xc2, 0x0d, 0xc0, 0x17, \ + 0x0c, 0x1b, 0x80, 0xc5, \ + 0xc0, 0x17, 0x6b, 0x81, \ + 0xd2, 0x75, 0xc3, 0x0d, \ + 0x4c, 0x01, 0xb7, 0x01, \ + 0xc0, 0x28, 0x6d, 0x75, \ + 0x0c, 0x1b, 0xe9, 0x65, \ + 0xc0, 0x17, 0x6b, 0x85, \ + 0x80, 0xc5, 0xc2, 0x0d, \ + 0x6b, 0x81, 0x0c, 0x1b, \ + 0xc3, 0x0d, 0xc0, 0x17, \ + 0xb9, 0x01, 0xce, 0x95, \ + 0x6d, 0x75, 0x4c, 0x01, \ + 0xe7, 0x75, 0xc0, 0x28, \ + 0x4c, 0x01, 0xb5, 0x01, \ + 0xc0, 0x28, 0x6d, 0x75, \ + 0xc1, 0xc3, 0xe7, 0x25, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x6d, 0x71, 0x00, 0x39, \ + 0xa1, 0x3b, 0xc0, 0x28, \ + 0xbc, 0x6c, 0x81, 0x61, \ + 0x80, 0x45, 0x81, 0x31, \ + 0xa7, 0x1b, 0x91, 0x31, \ + 0xe1, 0x8c, 0x08, 0x24, \ + 0x48, 0x52, 0x8f, 0x51, \ + 0x85, 0x51, 0xf1, 0x90, \ + 0xa5, 0x1b, 0x82, 0x85, \ + 0xe0, 0x08, 0x58, 0xa6, \ + 0xa3, 0x1b, 0x82, 0xa5, \ + 0x03, 0x56, 0x84, 0x55, \ + 0xbc, 0x16, 0xe0, 0x04, \ + 0x81, 0x45, 0x83, 0x51, \ + 0xe0, 0x04, 0x19, 0x56, \ + 0x83, 0x11, 0x83, 0x61, \ + 0x81, 0x11, 0x81, 0x25, \ + 0xae, 0x1f, 0x9e, 0x7b, \ + 0x84, 0xc6, 0x8c, 0x7b, \ + 0x81, 0x25, 0x8a, 0x6b, \ + 0x10, 0x44, 0x81, 0x71, \ + 0xd0, 0x7d, 0xfa, 0x80, \ + 0x86, 0xb5, 0x81, 0x91, \ + 0xff, 0xa1, 0x8e, 0x29, \ + 0x56, 0x02, 0xa5, 0xaf, \ + 0x81, 0x91, 0xf0, 0x8c, \ + 0x40, 0xb2, 0x8f, 0x8b, \ + 0x00, 0x09, 0xf0, 0x9c, \ + 0xc0, 0x28, 0x68, 0x31, \ + 0x80, 0x45, 0x94, 0x1b, \ + 0x44, 0x70, 0xff, 0x11, \ + 0x9e, 0x3b, 0xf0, 0x04, \ + 0x8c, 0x28, 0xa4, 0x3f, \ + 0x84, 0x0f, 0x82, 0x0f, \ + 0xe8, 0x00, 0x45, 0x48, \ + 0x90, 0x7d, 0x8c, 0x73, \ + 0xfa, 0x65, 0x80, 0x93, \ + 0xfa, 0x80, 0x00, 0x94, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x80, 0x6b, 0x82, 0x7b, \ + 0x6d, 0x9d, 0x0c, 0x05, \ + 0x0c, 0x15, 0xc0, 0x0b, \ + 0xc0, 0x0a, 0x63, 0x6d, \ + 0x68, 0x98, 0x91, 0x21, \ + 0x02, 0xc6, 0xef, 0xa0, \ + 0x84, 0x14, 0xe1, 0x18, \ + 0x66, 0x61, 0x0c, 0x29, \ + 0x84, 0x48, 0xc0, 0x2f, \ + 0xc9, 0x17, 0x84, 0x18, \ + 0x63, 0xed, 0x0c, 0x35, \ + 0x8e, 0x19, 0xc0, 0x0c, \ + 0x9e, 0x49, 0x83, 0x51, \ + 0x02, 0x24, 0xa3, 0x5f, \ + 0x04, 0x54, 0xe0, 0x90, \ + 0x6e, 0x38, 0xe8, 0x90, \ + 0x80, 0x8b, 0xef, 0xa0, \ + 0xef, 0xe8, 0x66, 0x08, \ + 0xe0, 0x10, 0x02, 0x06, \ + 0x8e, 0x3b, 0x90, 0x2b, \ + 0xef, 0xa0, 0x70, 0x28, \ + 0x65, 0x61, 0x0c, 0x15, \ + 0x02, 0x0a, 0xc0, 0x0c, \ + 0x80, 0x55, 0xe0, 0xe8, \ + 0xa1, 0x0b, 0xff, 0x01, \ + 0x0c, 0x39, 0x82, 0xe5, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x66, 0xc1, 0x08, 0x60, \ + 0x04, 0x04, 0xc0, 0x56, \ + 0x81, 0x11, 0xfb, 0x00, \ + 0x0c, 0x0c, 0x88, 0x0b, \ + 0x09, 0xb8, 0xf2, 0x0c, \ + 0x0c, 0x0c, 0xff, 0xcc, \ + 0xdd, 0x93, 0xf2, 0x0c, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x81, 0x01, 0x80, 0x1b, \ + 0x80, 0xe5, 0xa1, 0x1b, \ + 0xf0, 0x04, 0x44, 0x10, \ + 0xa1, 0x1b, 0xc3, 0x17, \ + 0xfe, 0x75, 0x84, 0x03, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x67, 0x35, 0x00, 0x1b, \ + 0x04, 0x06, 0xc0, 0x18, \ + 0x42, 0x10, 0xe0, 0x0c, \ + 0xc0, 0xc1, 0x70, 0x05, \ + 0x6e, 0x59, 0x40, 0x21, \ + 0x42, 0x10, 0xc0, 0x2f, \ + 0xc0, 0x1e, 0x7f, 0xf9, \ + 0x67, 0x35, 0x40, 0x13, \ + 0x00, 0x19, 0xc0, 0x18, \ + 0xc0, 0x2f, 0x65, 0xd1, \ + 0x00, 0x39, 0x83, 0x21, \ + 0xc0, 0x2f, 0x66, 0x19, \ + 0x6e, 0x75, 0x40, 0x21, \ + 0x85, 0x21, 0xc0, 0x30, \ + 0x6e, 0x7d, 0x40, 0x11, \ + 0x40, 0x21, 0xc0, 0x30, \ + 0xc0, 0x23, 0x6a, 0x05, \ + 0x6f, 0x21, 0x40, 0x21, \ + 0x40, 0x31, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6f, 0x25, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe0, 0x10, 0x02, 0x29, \ + 0x67, 0x35, 0x00, 0x3b, \ + 0x82, 0x29, 0xc0, 0x18, \ + 0x06, 0x34, 0xdc, 0x27, \ + 0xa6, 0x2f, 0xfc, 0x38, \ + 0x40, 0x23, 0x84, 0x34, \ + 0xc0, 0x18, 0x67, 0x35, \ + 0xe0, 0x14, 0x02, 0x49, \ + 0x44, 0x20, 0xde, 0x47, \ + 0xc0, 0x7f, 0x73, 0xfd, \ + 0x40, 0x23, 0xa8, 0x2f, \ + 0xc0, 0x18, 0x67, 0x35, \ + 0xe0, 0x1c, 0x02, 0x49, \ + 0x04, 0x24, 0x82, 0x49, \ + 0xa8, 0x2f, 0xfc, 0x00, \ + 0x67, 0x35, 0x40, 0x23, \ + 0x02, 0x49, 0xc0, 0x18, \ + 0x82, 0x49, 0xe0, 0x28, \ + 0x04, 0x24, 0xda, 0x47, \ + 0xa4, 0x4f, 0xfc, 0x34, \ + 0x67, 0x35, 0x40, 0x43, \ + 0x02, 0x29, 0xc0, 0x18, \ + 0xc6, 0x27, 0xe0, 0x2c, \ + 0x08, 0x44, 0x90, 0x29, \ + 0xa4, 0x4f, 0xfc, 0x0c, \ + 0x67, 0x35, 0x40, 0x43, \ + 0x02, 0x29, 0xc0, 0x18, \ + 0xc8, 0x27, 0xe0, 0x30, \ + 0x08, 0x44, 0xa0, 0x29, \ + 0xa4, 0x4f, 0xfc, 0x10, \ + 0x67, 0x35, 0x40, 0x43, \ + 0x02, 0x29, 0xc0, 0x18, \ + 0xca, 0x27, 0xe0, 0x34, \ + 0xf0, 0x80, 0x44, 0x20, \ + 0x73, 0x7d, 0x48, 0x40, \ + 0xa4, 0x4f, 0xc0, 0xff, \ + 0x65, 0xd5, 0x00, 0x29, \ + 0x40, 0x43, 0xc0, 0x2f, \ + 0xc0, 0x18, 0x67, 0x35, \ + 0xe0, 0x18, 0x02, 0x49, \ + 0x40, 0x35, 0xa7, 0x2b, \ + 0xc0, 0x0c, 0x63, 0x95, \ + 0x6e, 0x71, 0x40, 0x41, \ + 0x80, 0xf5, 0xc0, 0x30, \ + 0x6e, 0x51, 0x00, 0x29, \ + 0x44, 0x4a, 0xc0, 0x2f, \ + 0x80, 0xc5, 0xe0, 0x88, \ + 0xe0, 0x24, 0x02, 0x29, \ + 0x67, 0x31, 0x40, 0x23, \ + 0x02, 0x29, 0xc0, 0x18, \ + 0x00, 0x39, 0xe0, 0x40, \ + 0xc0, 0x30, 0x6f, 0x11, \ + 0x06, 0x34, 0x82, 0x29, \ + 0xa4, 0x3f, 0xfc, 0x00, \ + 0xe0, 0x20, 0x02, 0x29, \ + 0x6f, 0x11, 0x40, 0x31, \ + 0x02, 0x49, 0xc0, 0x30, \ + 0xc2, 0x47, 0xe0, 0x44, \ + 0x06, 0x34, 0x84, 0x49, \ + 0xa6, 0x4f, 0xfc, 0x04, \ + 0x6f, 0x11, 0x40, 0x41, \ + 0x02, 0x39, 0xc0, 0x30, \ + 0x00, 0x59, 0xe0, 0x48, \ + 0xc0, 0x30, 0x6f, 0x29, \ + 0x4a, 0x50, 0x82, 0x39, \ + 0xa6, 0x5f, 0xf3, 0xf8, \ + 0x6f, 0x29, 0x40, 0x51, \ + 0x02, 0x39, 0xc0, 0x30, \ + 0xc4, 0x37, 0xe0, 0x4c, \ + 0x48, 0x40, 0x88, 0x39, \ + 0xa8, 0x3f, 0xf3, 0xec, \ + 0x6f, 0x11, 0x40, 0x31, \ + 0x02, 0x39, 0xc0, 0x30, \ + 0x00, 0x49, 0xe0, 0x50, \ + 0xc0, 0x30, 0x6f, 0x05, \ + 0x46, 0x30, 0xca, 0x37, \ + 0x48, 0x40, 0xf0, 0x80, \ + 0xa6, 0x4f, 0xf3, 0x7c, \ + 0x6f, 0x05, 0x40, 0x41, \ + 0x02, 0x39, 0xc0, 0x30, \ + 0x00, 0x49, 0xe0, 0x54, \ + 0xc0, 0x30, 0x6f, 0x41, \ + 0x08, 0x44, 0x82, 0x39, \ + 0xa6, 0x4f, 0xfc, 0x00, \ + 0x6f, 0x41, 0x40, 0x41, \ + 0x02, 0x39, 0xc0, 0x30, \ + 0xc2, 0x37, 0xe0, 0x58, \ + 0x08, 0x44, 0x84, 0x39, \ + 0xa8, 0x3f, 0xfc, 0x04, \ + 0x6f, 0x41, 0x40, 0x31, \ + 0x02, 0x49, 0xc0, 0x30, \ + 0xc4, 0x47, 0xe0, 0x5c, \ + 0x06, 0x34, 0x88, 0x49, \ + 0xa8, 0x3f, 0xfc, 0x08, \ + 0x6f, 0x41, 0x40, 0x31, \ + 0x02, 0x49, 0xc0, 0x30, \ + 0xc6, 0x47, 0xe0, 0x60, \ + 0x06, 0x34, 0x90, 0x49, \ + 0xa8, 0x3f, 0xfc, 0x0c, \ + 0x6f, 0x41, 0x40, 0x31, \ + 0x02, 0x49, 0xc0, 0x30, \ + 0xc8, 0x47, 0xe0, 0x64, \ + 0x46, 0x30, 0xa0, 0x49, \ + 0xa8, 0x3f, 0xf3, 0xbc, \ + 0x6e, 0x85, 0x40, 0x21, \ + 0x40, 0x31, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6f, 0x41, \ + 0x06, 0x00, 0x9f, 0x21, \ + 0xc0, 0x73, 0x65, 0x31, \ + 0xe0, 0x38, 0x02, 0x49, \ + 0xec, 0x04, 0x04, 0x20, \ + 0xcd, 0x46, 0xa1, 0x2b, \ + 0x02, 0x29, 0xfe, 0x55, \ + 0x40, 0x21, 0xe0, 0x3c, \ + 0xc0, 0x1c, 0x6d, 0x6d, \ + 0xe0, 0x38, 0x02, 0x19, \ + 0x64, 0x29, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x24, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0xec, 0x80, 0x1e, 0xf0, \ + 0x0c, 0x05, 0x80, 0x6b, \ + 0xc0, 0x0b, 0x6d, 0xb9, \ + 0x66, 0xf5, 0x0c, 0x19, \ + 0xbe, 0x0e, 0xc0, 0x2f, \ + 0xf0, 0x04, 0x40, 0x10, \ + 0x0c, 0x09, 0x91, 0x06, \ + 0xc0, 0x2f, 0x6e, 0x5d, \ + 0x0c, 0x29, 0xc3, 0x17, \ + 0xc0, 0x30, 0x6e, 0x79, \ + 0x0c, 0x39, 0x82, 0x19, \ + 0xc0, 0x2f, 0x66, 0xf1, \ + 0xa3, 0x0b, 0x93, 0x16, \ + 0x81, 0x11, 0x9b, 0x26, \ + 0x97, 0x16, 0x99, 0x36, \ + 0xa7, 0x0b, 0x82, 0x25, \ + 0xa5, 0x0b, 0x82, 0x25, \ + 0x83, 0x01, 0x84, 0x55, \ + 0x85, 0x11, 0x9d, 0x06, \ + 0x9d, 0x16, 0x83, 0x35, \ + 0x0c, 0x09, 0x81, 0xe5, \ + 0xc0, 0x2f, 0x6e, 0x51, \ + 0x9d, 0x16, 0x83, 0x11, \ + 0xc0, 0x0b, 0xc5, 0x07, \ + 0xa2, 0x0f, 0xfd, 0x11, \ + 0x81, 0x65, 0x85, 0x11, \ + 0x9d, 0x06, 0x81, 0x01, \ + 0xe0, 0x0c, 0x43, 0xf6, \ + 0x9f, 0x06, 0x83, 0x01, \ + 0x0c, 0x29, 0xa1, 0x06, \ + 0xc0, 0x2f, 0x66, 0xb5, \ + 0x00, 0x16, 0xa3, 0x06, \ + 0x95, 0x16, 0xe0, 0x50, \ + 0x82, 0x29, 0x93, 0x0c, \ + 0xa9, 0x26, 0x83, 0x01, \ + 0x02, 0x16, 0xab, 0x06, \ + 0x0c, 0x29, 0xe0, 0x10, \ + 0xc0, 0x2f, 0x68, 0x01, \ + 0x02, 0x06, 0x97, 0x1c, \ + 0xc0, 0x40, 0x70, 0x11, \ + 0xb9, 0x06, 0x8d, 0x1e, \ + 0x8c, 0x0b, 0xbb, 0x26, \ + 0xef, 0xfc, 0x69, 0x28, \ + 0xe4, 0x10, 0x02, 0xf0, \ + 0x6b, 0x98, 0x8c, 0x0b, \ + 0xc0, 0xf1, 0xef, 0xfc, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0xec, 0x80, 0x1e, 0xf0, \ + 0x81, 0x01, 0x80, 0x6b, \ + 0xbe, 0x0e, 0x83, 0x11, \ + 0x97, 0x06, 0x95, 0x16, \ + 0x6e, 0x79, 0x4c, 0x11, \ + 0x02, 0x06, 0xc0, 0x30, \ + 0xc0, 0x40, 0x64, 0x01, \ + 0xe0, 0x10, 0x04, 0x06, \ + 0x8b, 0x2e, 0xfe, 0x1e, \ + 0x8d, 0x0e, 0x89, 0x0c, \ + 0x00, 0x16, 0x9d, 0x0c, \ + 0xc1, 0x40, 0x70, 0x11, \ + 0x8c, 0x0b, 0x89, 0x0e, \ + 0xef, 0xfc, 0x66, 0x88, \ + 0xe4, 0x10, 0x02, 0xf0, \ + 0x68, 0xf8, 0x8c, 0x0b, \ + 0xc0, 0xf1, 0xef, 0xfc, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0xec, 0x80, 0x1e, 0xf0, \ + 0x0c, 0x05, 0x80, 0x6b, \ + 0xc0, 0x0b, 0x6d, 0xb9, \ + 0x61, 0xd1, 0x0c, 0x19, \ + 0xbe, 0x0e, 0xc0, 0x15, \ + 0x66, 0xf1, 0x0c, 0x09, \ + 0x0c, 0x29, 0xc0, 0x2f, \ + 0xc0, 0x30, 0x6e, 0x79, \ + 0x81, 0x11, 0x95, 0x16, \ + 0x0c, 0x39, 0x97, 0x16, \ + 0xc0, 0x2f, 0x66, 0xb5, \ + 0x9b, 0x26, 0x99, 0x06, \ + 0xe0, 0x70, 0x00, 0x46, \ + 0x66, 0xf5, 0x0c, 0x29, \ + 0x93, 0x0c, 0xc0, 0x2f, \ + 0xa9, 0x36, 0x82, 0x39, \ + 0xab, 0x06, 0x83, 0x01, \ + 0xf0, 0x04, 0x46, 0x20, \ + 0xc3, 0x27, 0x91, 0x36, \ + 0x06, 0x16, 0x82, 0x29, \ + 0x93, 0x26, 0xe0, 0x10, \ + 0xb1, 0x16, 0x97, 0x3c, \ + 0x68, 0x01, 0x0c, 0x29, \ + 0xb3, 0x06, 0xc0, 0x2f, \ + 0xb7, 0x06, 0xb5, 0x06, \ + 0xbb, 0x26, 0xb9, 0x06, \ + 0x9f, 0x06, 0x9d, 0x16, \ + 0xa3, 0x06, 0xa1, 0x06, \ + 0x62, 0x18, 0x8c, 0x0b, \ + 0x02, 0xf0, 0xef, 0xfc, \ + 0x8c, 0x0b, 0xe4, 0x10, \ + 0xef, 0xfc, 0x64, 0x88, \ + 0x0c, 0x0c, 0xc0, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0xec, 0x80, 0x1e, 0xf0, \ + 0x0c, 0x05, 0x80, 0x6b, \ + 0xc0, 0x0b, 0x6d, 0xb9, \ + 0x61, 0xd1, 0x0c, 0x19, \ + 0xbe, 0x0e, 0xc0, 0x15, \ + 0x66, 0xf1, 0x0c, 0x09, \ + 0x81, 0x21, 0xc0, 0x2f, \ + 0x6e, 0x79, 0x0c, 0x39, \ + 0x95, 0x16, 0xc0, 0x30, \ + 0x99, 0x06, 0x97, 0x26, \ + 0x66, 0xb5, 0x0c, 0x09, \ + 0x9b, 0x36, 0xc0, 0x2f, \ + 0xe0, 0x60, 0x02, 0x36, \ + 0x0c, 0x19, 0x93, 0x1c, \ + 0xc0, 0x2f, 0x66, 0xf5, \ + 0x83, 0x31, 0x82, 0x09, \ + 0xab, 0x36, 0xa9, 0x06, \ + 0xf0, 0x04, 0x40, 0x10, \ + 0x91, 0x06, 0xc3, 0x17, \ + 0x93, 0x16, 0x82, 0x19, \ + 0xe0, 0x10, 0x00, 0x16, \ + 0xb1, 0x26, 0x97, 0x0c, \ + 0xb5, 0x36, 0xb3, 0x36, \ + 0xb9, 0x36, 0xb7, 0x36, \ + 0x9d, 0x26, 0xbb, 0x26, \ + 0xa1, 0x36, 0x9f, 0x36, \ + 0x8c, 0x0b, 0xa3, 0x36, \ + 0xef, 0xfc, 0x5d, 0xc8, \ + 0xe4, 0x10, 0x02, 0xf0, \ + 0x60, 0x38, 0x8c, 0x0b, \ + 0xc0, 0xf1, 0xef, 0xfc, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0xec, 0x80, 0x1e, 0xf0, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x2f, 0x66, 0xb5, \ + 0xe1, 0x8c, 0x02, 0x04, \ + 0x81, 0x31, 0x83, 0x21, \ + 0xf8, 0x88, 0x42, 0x12, \ + 0x08, 0x06, 0xbe, 0x3e, \ + 0xc0, 0x40, 0x60, 0x11, \ + 0xfe, 0x4e, 0x82, 0x09, \ + 0xab, 0x36, 0xa9, 0x06, \ + 0xf0, 0x00, 0x00, 0x06, \ + 0x97, 0x3c, 0xfc, 0x0e, \ + 0x00, 0x06, 0xb1, 0x16, \ + 0xb3, 0x26, 0xe0, 0x10, \ + 0x9d, 0x3c, 0x9b, 0x0c, \ + 0x70, 0x11, 0x00, 0x16, \ + 0x89, 0x0e, 0xc1, 0x40, \ + 0x5a, 0xb8, 0x8c, 0x0b, \ + 0x02, 0xf0, 0xef, 0xfc, \ + 0x8c, 0x0b, 0xe4, 0x10, \ + 0xef, 0xfc, 0x5d, 0x28, \ + 0x0c, 0x0c, 0xc0, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0xec, 0x80, 0x1e, 0xf0, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x2f, 0x6e, 0x5d, \ + 0xe0, 0x34, 0x00, 0x4a, \ + 0xe2, 0x00, 0x00, 0x0c, \ + 0x04, 0x06, 0x81, 0x11, \ + 0xc0, 0x40, 0x61, 0x01, \ + 0xfe, 0x2e, 0xbe, 0x1e, \ + 0x60, 0x01, 0x04, 0x06, \ + 0x06, 0x06, 0xc0, 0x40, \ + 0xc0, 0x40, 0x70, 0x01, \ + 0xfc, 0x3e, 0x8b, 0x2e, \ + 0xb3, 0x16, 0xb1, 0x06, \ + 0x83, 0x01, 0x9b, 0x1c, \ + 0x00, 0x16, 0x9d, 0x0c, \ + 0xc1, 0x40, 0x70, 0x11, \ + 0x8c, 0x0b, 0x89, 0x0e, \ + 0xef, 0xfc, 0x57, 0xc8, \ + 0xe4, 0x10, 0x02, 0xf0, \ + 0x5a, 0x38, 0x8c, 0x0b, \ + 0xc0, 0xf1, 0xef, 0xfc, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x02, 0x06, 0x80, 0x6b, \ + 0x55, 0x78, 0xe2, 0x80, \ + 0x40, 0x46, 0xe8, 0x08, \ + 0x4c, 0x01, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x64, 0x71, 0x0e, 0x60, \ + 0x81, 0x81, 0xc0, 0xa2, \ + 0xff, 0xec, 0x52, 0x8e, \ + 0xf1, 0xa0, 0x40, 0x72, \ + 0x54, 0x62, 0x80, 0x08, \ + 0x41, 0x18, 0xf1, 0xa0, \ + 0x10, 0x80, 0xe8, 0x00, \ + 0x93, 0x8b, 0xec, 0x04, \ + 0xfc, 0xb5, 0xa8, 0x06, \ + 0x45, 0x76, 0x81, 0x11, \ + 0x8c, 0x0b, 0xe0, 0x00, \ + 0x0c, 0x0c, 0x8c, 0x3b, \ + 0x1f, 0xb8, 0xf2, 0x3c, \ + 0xc1, 0xc3, 0xe0, 0x00, \ + 0xe1, 0x8c, 0x02, 0x04, \ + 0xe1, 0x84, 0x04, 0x04, \ + 0x44, 0x20, 0xa0, 0x19, \ + 0x06, 0x04, 0xf0, 0x80, \ + 0x48, 0x00, 0xe0, 0x84, \ + 0x46, 0x30, 0xf0, 0x3c, \ + 0xa8, 0x2f, 0xf1, 0x00, \ + 0xa2, 0x2f, 0xc6, 0x07, \ + 0xf2, 0x00, 0x40, 0x00, \ + 0xa4, 0x0f, 0xa6, 0x2f, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x82, 0x7b, 0x84, 0x6b, \ + 0x86, 0x1b, 0x80, 0x8b, \ + 0xe8, 0x00, 0x40, 0xa8, \ + 0x90, 0x0b, 0x85, 0x31, \ + 0x8c, 0x2b, 0x8e, 0x1b, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0x83, 0x25, \ + 0xe0, 0x04, 0x44, 0x26, \ + 0x6e, 0x59, 0x40, 0x21, \ + 0x40, 0x06, 0xc0, 0x2f, \ + 0xc0, 0x20, 0x64, 0x0d, \ + 0x64, 0x0d, 0x45, 0x06, \ + 0xc5, 0x38, 0xc0, 0x20, \ + 0x85, 0x0b, 0xc1, 0x36, \ + 0xdd, 0x93, 0xfe, 0xb5, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x84, 0x7b, 0x86, 0x9b, \ + 0x80, 0x6b, 0x82, 0x8b, \ + 0xe0, 0x04, 0x40, 0x36, \ + 0x6e, 0x59, 0x4c, 0x01, \ + 0x8c, 0x0b, 0xc0, 0x2f, \ + 0xe8, 0x00, 0x5f, 0xe8, \ + 0xe1, 0x28, 0x41, 0xc6, \ + 0x81, 0x11, 0x80, 0x04, \ + 0x40, 0xc6, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x30, \ + 0x00, 0x0a, 0x80, 0x04, \ + 0x91, 0xa1, 0xe0, 0xec, \ + 0x0c, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe0, 0xf0, 0x00, 0x0a, \ + 0x8c, 0x0b, 0x90, 0xb5, \ + 0xe8, 0x00, 0x60, 0x08, \ + 0xe1, 0x44, 0x01, 0x06, \ + 0x80, 0x08, 0x80, 0x04, \ + 0xe0, 0x8c, 0x40, 0x0a, \ + 0x66, 0xc1, 0x00, 0x60, \ + 0x83, 0x11, 0xc0, 0x56, \ + 0x8a, 0xb5, 0x8f, 0x21, \ + 0xe0, 0x04, 0x46, 0x36, \ + 0xef, 0xc8, 0x56, 0x48, \ + 0xe1, 0x2c, 0x00, 0x46, \ + 0x80, 0x08, 0x80, 0x04, \ + 0x82, 0xd5, 0xa1, 0x0b, \ + 0xe1, 0x30, 0x40, 0xc6, \ + 0x80, 0x04, 0x80, 0x04, \ + 0xe0, 0xf4, 0x00, 0x0a, \ + 0x0c, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe0, 0xf8, 0x00, 0x0a, \ + 0x8c, 0x0b, 0x92, 0x35, \ + 0xe8, 0x00, 0x5d, 0xa8, \ + 0xe1, 0x44, 0x01, 0x06, \ + 0x80, 0x08, 0x80, 0x04, \ + 0xe0, 0x8c, 0x40, 0x4a, \ + 0x0c, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe0, 0xf4, 0x00, 0x0a, \ + 0x00, 0x60, 0x8c, 0x15, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0x91, 0x21, 0x83, 0x11, \ + 0xe0, 0x04, 0x46, 0x36, \ + 0xef, 0xc8, 0x52, 0x48, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0x13, 0xf8, 0xf2, 0x7c, \ + 0xa4, 0x9d, 0xff, 0xc8, \ + 0xa8, 0x9d, 0x9e, 0xe5, \ + 0xc0, 0x00, 0x0e, 0x88, \ + 0x0f, 0xc8, 0xa7, 0x9b, \ + 0xa9, 0x9b, 0x80, 0x00, \ + 0xec, 0x55, 0x92, 0xab, \ + 0xe1, 0x28, 0x41, 0x06, \ + 0x81, 0x11, 0x80, 0x04, \ + 0x41, 0x46, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x28, \ + 0xe0, 0x00, 0x43, 0x76, \ + 0x41, 0xc6, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x28, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x02, 0x14, 0xf2, 0x7c, \ + 0x80, 0x16, 0xfc, 0x94, \ + 0xf5, 0xf5, 0x89, 0xa1, \ + 0x66, 0xc1, 0x00, 0x60, \ + 0x83, 0x11, 0xc0, 0x56, \ + 0x46, 0x36, 0x91, 0x21, \ + 0x50, 0x98, 0xe0, 0x04, \ + 0x41, 0xc6, 0xef, 0xc8, \ + 0x80, 0x04, 0xe1, 0x28, \ + 0x80, 0x18, 0xe4, 0xad, \ + 0xc2, 0x19, 0x9a, 0x35, \ + 0x42, 0x86, 0x80, 0x16, \ + 0x81, 0x21, 0xe0, 0x3c, \ + 0x7d, 0xb8, 0x8c, 0x0b, \ + 0x40, 0x46, 0xe8, 0x00, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0xe1, 0x2c, 0x12, 0x46, \ + 0xa4, 0x14, 0x80, 0x08, \ + 0x42, 0x4a, 0x84, 0x18, \ + 0xa8, 0x85, 0xe0, 0x80, \ + 0x56, 0x00, 0xc7, 0x07, \ + 0x0e, 0x60, 0xf0, 0x04, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xe1, 0x44, 0x15, 0x06, \ + 0xe1, 0x30, 0x50, 0xc6, \ + 0xfb, 0xfc, 0x16, 0x0a, \ + 0xa0, 0x04, 0x82, 0x85, \ + 0x00, 0x0a, 0x80, 0x04, \ + 0x80, 0xe5, 0xe0, 0xfc, \ + 0x65, 0x61, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x86, 0xd5, 0xe1, 0x00, \ + 0x56, 0x18, 0x8c, 0x0b, \ + 0xa8, 0x04, 0xe8, 0x00, \ + 0x40, 0x4a, 0x80, 0x08, \ + 0x0b, 0x48, 0xe0, 0x8c, \ + 0x0c, 0x05, 0x80, 0x00, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe0, 0xfc, 0x00, 0x0a, \ + 0x80, 0x00, 0x0a, 0xd8, \ + 0x93, 0x21, 0x83, 0x11, \ + 0xe0, 0x04, 0x46, 0x36, \ + 0x4c, 0x18, 0x8e, 0x0b, \ + 0xa4, 0x04, 0xef, 0xc8, \ + 0x40, 0x0a, 0x80, 0x08, \ + 0xf4, 0xe5, 0xe0, 0x80, \ + 0x40, 0x46, 0x8d, 0xf5, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0xc7, 0x07, 0x80, 0x08, \ + 0xf0, 0x04, 0x56, 0x00, \ + 0xa1, 0x9b, 0xff, 0x35, \ + 0xa3, 0x9b, 0xa6, 0x05, \ + 0x80, 0x00, 0x0a, 0x88, \ + 0x92, 0xab, 0xa5, 0x9b, \ + 0x40, 0x86, 0xcc, 0xb5, \ + 0x80, 0x04, 0xe1, 0x28, \ + 0x40, 0xc6, 0x80, 0x86, \ + 0x80, 0x04, 0xe1, 0x28, \ + 0x41, 0xc6, 0x80, 0x76, \ + 0x80, 0x04, 0xe1, 0x28, \ + 0xcc, 0x19, 0x80, 0x18, \ + 0x85, 0xa1, 0x80, 0x16, \ + 0xe0, 0x19, 0xe7, 0x85, \ + 0x42, 0x86, 0x80, 0x16, \ + 0x81, 0x21, 0xe0, 0x3c, \ + 0x77, 0x38, 0x8c, 0x0b, \ + 0x40, 0x46, 0xe8, 0x00, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0xe1, 0x2c, 0x12, 0x46, \ + 0xa4, 0x14, 0x80, 0x08, \ + 0x42, 0x4a, 0x84, 0x18, \ + 0x8e, 0x85, 0xe0, 0x90, \ + 0x56, 0x00, 0xc7, 0x07, \ + 0x0e, 0x60, 0xf0, 0x04, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xe1, 0x44, 0x15, 0x06, \ + 0xe1, 0x30, 0x50, 0xc6, \ + 0x40, 0x46, 0x81, 0x95, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0xc7, 0x07, 0x80, 0x08, \ + 0xf0, 0x04, 0x56, 0x00, \ + 0x16, 0x0a, 0x85, 0x45, \ + 0x82, 0x85, 0xfb, 0xfc, \ + 0x80, 0x04, 0xa0, 0x04, \ + 0xe1, 0x04, 0x00, 0x0a, \ + 0x0c, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe1, 0x08, 0x00, 0x0a, \ + 0x8c, 0x0b, 0xfa, 0x75, \ + 0xe8, 0x00, 0x4f, 0x08, \ + 0x80, 0x08, 0xa8, 0x04, \ + 0xe0, 0x8c, 0x40, 0x4a, \ + 0x0c, 0x05, 0x9a, 0x85, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe1, 0x04, 0x00, 0x0a, \ + 0x83, 0x11, 0x98, 0xc5, \ + 0x46, 0x36, 0x95, 0x21, \ + 0x8e, 0x0b, 0xe0, 0x04, \ + 0xef, 0xc8, 0x45, 0x28, \ + 0x80, 0x08, 0xa4, 0x04, \ + 0xe0, 0x90, 0x40, 0x0a, \ + 0x8c, 0x0b, 0xf6, 0x25, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0x9b, 0x35, 0xf2, 0x7c, \ + 0x96, 0x25, 0xab, 0x9b, \ + 0x92, 0xab, 0xad, 0x9b, \ + 0xdf, 0xfc, 0x2c, 0xd8, \ + 0xe1, 0x28, 0x41, 0x06, \ + 0x80, 0x86, 0x80, 0x04, \ + 0xe1, 0x28, 0x41, 0x46, \ + 0x80, 0x76, 0x80, 0x04, \ + 0xe1, 0x28, 0x41, 0xc6, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xfd, 0x80, 0x42, 0x10, \ + 0x8d, 0xa1, 0x80, 0x16, \ + 0x41, 0x06, 0xd9, 0xe5, \ + 0x80, 0x04, 0xe1, 0x28, \ + 0x80, 0x16, 0x81, 0x11, \ + 0xe1, 0x28, 0x41, 0x46, \ + 0x43, 0x76, 0x80, 0x04, \ + 0x80, 0x16, 0xe0, 0x00, \ + 0xe1, 0x28, 0x41, 0xc6, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf2, 0x7c, 0x42, 0x10, \ + 0x87, 0xa1, 0x80, 0x16, \ + 0x83, 0x11, 0xd7, 0xa5, \ + 0x85, 0x95, 0x93, 0x21, \ + 0xe1, 0x28, 0x40, 0x86, \ + 0x81, 0xa1, 0x80, 0x04, \ + 0x40, 0xc6, 0x80, 0xa6, \ + 0x80, 0x04, 0xe1, 0x28, \ + 0x80, 0x16, 0x9f, 0x11, \ + 0xe1, 0x28, 0x41, 0xc6, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xe4, 0x42, 0x10, \ + 0xd5, 0x55, 0x80, 0x16, \ + 0xe1, 0x28, 0x40, 0x86, \ + 0x81, 0x11, 0x80, 0x04, \ + 0x40, 0xc6, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x28, \ + 0x80, 0x16, 0x9f, 0x11, \ + 0xe1, 0x28, 0x41, 0xc6, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xe4, 0x42, 0x10, \ + 0x80, 0x16, 0xc4, 0x19, \ + 0xd3, 0x15, 0x83, 0xa1, \ + 0x95, 0x21, 0x83, 0x11, \ + 0xe0, 0x04, 0x46, 0x36, \ + 0xd7, 0x55, 0x8e, 0x0b, \ + 0xe1, 0x28, 0x41, 0x06, \ + 0x80, 0x86, 0x80, 0x04, \ + 0xe1, 0x28, 0x41, 0x46, \ + 0x80, 0x76, 0x80, 0x04, \ + 0xe1, 0x28, 0x41, 0xc6, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xfd, 0x80, 0x42, 0x10, \ + 0x8b, 0xa1, 0x80, 0x16, \ + 0x00, 0x0c, 0xcf, 0xa5, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x82, 0x7b, 0x84, 0x6b, \ + 0x86, 0x1b, 0x80, 0x8b, \ + 0xe8, 0x00, 0x40, 0xa8, \ + 0x90, 0x0b, 0x8d, 0x31, \ + 0x8c, 0x2b, 0x8e, 0x1b, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0xff, 0xfc, 0x23, 0x28, \ + 0xe0, 0x04, 0x44, 0x56, \ + 0x6e, 0x59, 0x40, 0x21, \ + 0x00, 0x06, 0xc0, 0x2f, \ + 0xc0, 0x20, 0x64, 0x11, \ + 0x64, 0x21, 0x05, 0x86, \ + 0xc5, 0x38, 0xc0, 0x20, \ + 0x85, 0x0b, 0xc1, 0x36, \ + 0xdd, 0x93, 0xfe, 0xb5, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x82, 0x7b, 0x84, 0x6b, \ + 0x86, 0x0b, 0x80, 0x8b, \ + 0xe8, 0x00, 0x40, 0xa8, \ + 0x90, 0x0b, 0x8b, 0x31, \ + 0x8c, 0x2b, 0x8e, 0x1b, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0xff, 0xfc, 0x21, 0x28, \ + 0x64, 0x31, 0x43, 0x06, \ + 0x45, 0xc6, 0xc0, 0x20, \ + 0xc0, 0x20, 0x64, 0x39, \ + 0xc5, 0x36, 0xc1, 0x38, \ + 0xfe, 0xb5, 0x85, 0x1b, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xe0, 0x04, 0x44, 0x76, \ + 0x6e, 0x59, 0x40, 0x21, \ + 0x41, 0xc6, 0xc0, 0x2f, \ + 0xc0, 0x20, 0x64, 0x39, \ + 0x64, 0x3d, 0x05, 0xc6, \ + 0xc5, 0x38, 0xc0, 0x20, \ + 0x85, 0x0b, 0xc1, 0x36, \ + 0xdd, 0x93, 0xfe, 0xb5, \ + 0xe1, 0x14, 0x02, 0x86, \ + 0x45, 0x86, 0x84, 0x14, \ + 0x40, 0x21, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xe0, 0x29, 0x84, 0x28, \ + 0x03, 0x86, 0x84, 0x26, \ + 0x84, 0x14, 0xe1, 0x14, \ + 0xe0, 0x04, 0x45, 0x96, \ + 0x40, 0x21, 0x83, 0x31, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xdd, 0x93, 0x84, 0x36, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x6e, 0x18, 0x80, 0x6b, \ + 0x41, 0xc6, 0xe8, 0x00, \ + 0x80, 0x04, 0xe1, 0x28, \ + 0x80, 0x16, 0x81, 0x11, \ + 0x0c, 0x0c, 0x8c, 0x0b, \ + 0x83, 0xf5, 0xf2, 0x00, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x81, 0x11, 0x80, 0x6b, \ + 0x87, 0x31, 0x81, 0x21, \ + 0xef, 0xfc, 0x5c, 0x68, \ + 0xe0, 0x10, 0x0f, 0x86, \ + 0x64, 0x71, 0x0c, 0x60, \ + 0x11, 0x86, 0xc0, 0xc4, \ + 0xc0, 0x20, 0x64, 0x21, \ + 0x58, 0x38, 0xe1, 0x08, \ + 0x0e, 0x70, 0xef, 0xfc, \ + 0xa1, 0x7b, 0xec, 0x04, \ + 0xfe, 0x35, 0xd9, 0x06, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xe1, 0x14, 0x02, 0x86, \ + 0x04, 0xf6, 0x84, 0x14, \ + 0x40, 0x21, 0xe0, 0x08, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x40, 0x00, 0x84, 0x08, \ + 0x84, 0x06, 0xf3, 0xbc, \ + 0xe1, 0x14, 0x01, 0x86, \ + 0x81, 0x11, 0x80, 0x04, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x01, 0xc6, 0x80, 0x6b, \ + 0x80, 0x04, 0xe1, 0x14, \ + 0xe0, 0x0c, 0x03, 0x26, \ + 0x6e, 0x59, 0x4c, 0x11, \ + 0x83, 0x11, 0xc0, 0x2f, \ + 0x01, 0x46, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x30, \ + 0x80, 0x16, 0x81, 0x11, \ + 0xe1, 0x30, 0x01, 0x86, \ + 0x88, 0x38, 0x80, 0x24, \ + 0xf3, 0xc0, 0x46, 0x30, \ + 0x80, 0x04, 0x88, 0x36, \ + 0x9e, 0x29, 0x80, 0x28, \ + 0xff, 0x40, 0x44, 0x20, \ + 0x40, 0x06, 0x80, 0x26, \ + 0x80, 0x04, 0xe1, 0x30, \ + 0x40, 0x46, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x30, \ + 0xa1, 0x0b, 0x80, 0x08, \ + 0x0c, 0x05, 0x80, 0xf5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe1, 0x0c, 0x00, 0x0a, \ + 0x0c, 0x39, 0x82, 0x75, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x66, 0xc1, 0x00, 0x60, \ + 0x83, 0x11, 0xc0, 0x56, \ + 0x6f, 0xd8, 0x91, 0x21, \ + 0x01, 0x46, 0xef, 0xc4, \ + 0x80, 0x04, 0xe1, 0x30, \ + 0x80, 0x16, 0x83, 0x11, \ + 0xe0, 0x3c, 0x42, 0x86, \ + 0x8c, 0x0b, 0x81, 0x21, \ + 0xe8, 0x00, 0x5d, 0x28, \ + 0xe1, 0x40, 0x50, 0x46, \ + 0x80, 0x08, 0xa0, 0x04, \ + 0x54, 0x00, 0xc7, 0x07, \ + 0x0e, 0x60, 0xf0, 0x04, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xe1, 0x30, 0x52, 0x46, \ + 0x0c, 0x39, 0x81, 0x95, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x95, 0x21, 0x83, 0x11, \ + 0x6d, 0xd8, 0x8e, 0x0b, \ + 0xa4, 0x04, 0xef, 0xc4, \ + 0xa1, 0x0b, 0x80, 0x08, \ + 0x0c, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe1, 0x10, 0x00, 0x0a, \ + 0x14, 0x0a, 0x84, 0x15, \ + 0xfa, 0x85, 0xfb, 0xfc, \ + 0x65, 0x61, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0xf8, 0xc5, 0xe1, 0x10, \ + 0x80, 0x08, 0xa0, 0x04, \ + 0x54, 0x00, 0xc7, 0x07, \ + 0xfd, 0x85, 0xf0, 0x04, \ + 0xe8, 0x00, 0x63, 0x28, \ + 0xe1, 0x30, 0x01, 0x46, \ + 0x81, 0x11, 0x80, 0x04, \ + 0x01, 0xc6, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x14, \ + 0x0c, 0x0c, 0x80, 0x16, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0x84, 0x65, 0xa5, 0x1b, \ + 0x82, 0x45, 0xa3, 0x1b, \ + 0x86, 0xb5, 0xa1, 0x1b, \ + 0xe1, 0x2c, 0x03, 0x46, \ + 0x65, 0xc5, 0x00, 0x25, \ + 0x84, 0x14, 0xc0, 0x0c, \ + 0x03, 0x86, 0x84, 0x2e, \ + 0x00, 0x25, 0xe1, 0x2c, \ + 0xc0, 0x0c, 0x65, 0xc9, \ + 0x84, 0x2e, 0x84, 0x14, \ + 0xe1, 0x2c, 0x03, 0x46, \ + 0x65, 0xc5, 0x00, 0x25, \ + 0x84, 0x14, 0xc0, 0x0c, \ + 0x03, 0x86, 0x84, 0x2e, \ + 0x00, 0x05, 0xe1, 0x2c, \ + 0xc0, 0x0c, 0x65, 0xc9, \ + 0x84, 0x0e, 0x84, 0x14, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x04, 0x04, 0x0c, \ + 0xe1, 0x14, 0x05, 0xc6, \ + 0xa5, 0x1b, 0x88, 0x24, \ + 0x88, 0x36, 0x81, 0x31, \ + 0xa3, 0x1b, 0x86, 0xe5, \ + 0xa1, 0x1b, 0x90, 0x05, \ + 0x02, 0x86, 0x9a, 0x55, \ + 0x84, 0x14, 0xe1, 0x2c, \ + 0x67, 0x31, 0x00, 0x29, \ + 0x84, 0x38, 0xc0, 0x31, \ + 0x46, 0x30, 0x82, 0x29, \ + 0xa4, 0x3f, 0xf3, 0xf8, \ + 0x00, 0x39, 0x84, 0x36, \ + 0xc0, 0x31, 0x67, 0x31, \ + 0x67, 0x31, 0x00, 0x00, \ + 0xc2, 0x37, 0xc0, 0xc5, \ + 0xe0, 0x0c, 0x43, 0x76, \ + 0x49, 0xb6, 0x87, 0x21, \ + 0x89, 0x51, 0xe0, 0x0c, \ + 0x02, 0x86, 0x87, 0xf5, \ + 0x84, 0x24, 0xe1, 0x2c, \ + 0x67, 0x31, 0x00, 0x39, \ + 0x88, 0x48, 0xc0, 0x31, \ + 0x48, 0x40, 0x82, 0x39, \ + 0xa6, 0x4f, 0xf3, 0xf8, \ + 0x84, 0x24, 0x88, 0x46, \ + 0x67, 0x31, 0x00, 0x39, \ + 0x88, 0x48, 0xc0, 0x31, \ + 0x88, 0x39, 0xc2, 0x37, \ + 0xf3, 0xec, 0x48, 0x40, \ + 0x88, 0x46, 0xa6, 0x4f, \ + 0x00, 0x39, 0x84, 0x24, \ + 0xc0, 0x31, 0x67, 0x31, \ + 0xc2, 0x37, 0x88, 0x48, \ + 0x48, 0x40, 0x90, 0x39, \ + 0xa6, 0x4f, 0xf3, 0xdc, \ + 0x81, 0x35, 0x88, 0x46, \ + 0xe1, 0x2c, 0x02, 0x86, \ + 0x00, 0x29, 0x84, 0x14, \ + 0xc0, 0x31, 0x67, 0x35, \ + 0xc8, 0x27, 0x84, 0x38, \ + 0x46, 0x30, 0xa0, 0x29, \ + 0xa4, 0x3f, 0xf3, 0xbc, \ + 0x00, 0x39, 0x84, 0x36, \ + 0xc0, 0x31, 0x67, 0x35, \ + 0x67, 0x35, 0x00, 0x00, \ + 0xca, 0x37, 0xc0, 0xc5, \ + 0xe0, 0x04, 0x43, 0xf6, \ + 0x49, 0xf6, 0x8f, 0x21, \ + 0x0a, 0x06, 0xe0, 0x08, \ + 0x18, 0x86, 0xe0, 0x04, \ + 0xb0, 0x64, 0xe1, 0x2c, \ + 0x8a, 0x3f, 0x98, 0x78, \ + 0xa6, 0x7f, 0x88, 0x7f, \ + 0xb0, 0x34, 0x98, 0x76, \ + 0x8c, 0x48, 0x80, 0x08, \ + 0x82, 0x09, 0xc5, 0x07, \ + 0x82, 0x4f, 0xa5, 0x0f, \ + 0x8c, 0x46, 0xa0, 0x4f, \ + 0xe1, 0x14, 0x01, 0xc6, \ + 0x83, 0x11, 0x80, 0x04, \ + 0x0c, 0x0c, 0x80, 0x16, \ + 0xdd, 0x93, 0xf0, 0x04, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xf8, 0x0c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0xe1, 0x88, 0x04, 0x14, \ + 0x06, 0x86, 0x86, 0x19, \ + 0x08, 0x24, 0xe1, 0x4c, \ + 0x8c, 0x54, 0xe0, 0x90, \ + 0x06, 0x00, 0xa2, 0x4f, \ + 0xc0, 0x90, 0x65, 0x21, \ + 0x8a, 0x43, 0xc4, 0x47, \ + 0x89, 0xc1, 0x81, 0x51, \ + 0x86, 0x7b, 0x88, 0x6b, \ + 0x18, 0xc0, 0xdd, 0x84, \ + 0xa1, 0xcb, 0xec, 0x04, \ + 0xf0, 0x10, 0x4c, 0x85, \ + 0x82, 0x51, 0xfe, 0x55, \ + 0xa0, 0x31, 0xa5, 0x5b, \ + 0x64, 0x01, 0x08, 0x40, \ + 0xfc, 0x15, 0xc0, 0x01, \ + 0xe0, 0x00, 0x47, 0x86, \ + 0xe5, 0x0e, 0x46, 0x2a, \ + 0x64, 0x71, 0x00, 0x00, \ + 0x45, 0x46, 0xc0, 0x91, \ + 0x88, 0x24, 0xe1, 0x48, \ + 0x81, 0x31, 0x86, 0x13, \ + 0xe0, 0x0c, 0x48, 0x06, \ + 0xe5, 0x12, 0x4a, 0x3a, \ + 0x18, 0x54, 0x82, 0x53, \ + 0x9d, 0x51, 0xe0, 0x88, \ + 0x80, 0x6b, 0x84, 0xc3, \ + 0x0a, 0x50, 0xd9, 0x74, \ + 0xa1, 0x5b, 0xec, 0x04, \ + 0xf0, 0x10, 0x58, 0x75, \ + 0x82, 0x31, 0xfe, 0x55, \ + 0xf0, 0x01, 0xb5, 0x3b, \ + 0x88, 0xf1, 0xfa, 0xd5, \ + 0xf0, 0x0c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x79, 0x98, 0x80, 0x6b, \ + 0xa1, 0x01, 0xef, 0xf8, \ + 0x6e, 0x79, 0x4c, 0x01, \ + 0x8c, 0x0b, 0xc0, 0x30, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0xec, 0x3d, 0x08, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0x65, 0x41, 0x00, 0x00, \ + 0xdd, 0x93, 0xc0, 0x28, \ + 0x66, 0x11, 0x00, 0x00, \ + 0xdd, 0x93, 0xc0, 0x2b, \ + 0xe1, 0x08, 0x02, 0x86, \ + 0x9d, 0x21, 0x84, 0x14, \ + 0x02, 0x06, 0x84, 0x26, \ + 0x84, 0x24, 0xe1, 0x08, \ + 0x88, 0x16, 0x83, 0x11, \ + 0xe1, 0x14, 0x04, 0x86, \ + 0x88, 0x38, 0x88, 0x24, \ + 0x88, 0x36, 0xc8, 0x39, \ + 0x6f, 0x81, 0x00, 0x25, \ + 0x06, 0x06, 0xc0, 0x0b, \ + 0xc0, 0x40, 0x60, 0x01, \ + 0xa1, 0x21, 0xc7, 0x2d, \ + 0x00, 0x29, 0x80, 0x95, \ + 0xc0, 0x28, 0x6d, 0x85, \ + 0xe1, 0x24, 0x40, 0xc6, \ + 0x80, 0x26, 0x80, 0x04, \ + 0xe1, 0x24, 0x40, 0x86, \ + 0x80, 0x16, 0x80, 0x04, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe1, 0x24, 0x43, 0x06, \ + 0x84, 0x38, 0x84, 0x14, \ + 0x85, 0x3b, 0x84, 0x28, \ + 0x02, 0x20, 0xfe, 0xb5, \ + 0x40, 0x11, 0xed, 0x5c, \ + 0xc0, 0x30, 0x6e, 0xe9, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x41, 0x56, 0x80, 0x6b, \ + 0x4c, 0x01, 0xe0, 0x08, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x7e, 0x78, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xef, 0xfc, \ + 0xc0, 0x30, 0x6e, 0xe9, \ + 0x61, 0x41, 0x4c, 0x01, \ + 0x4c, 0x01, 0xc0, 0x15, \ + 0xc0, 0x23, 0x69, 0xa9, \ + 0x61, 0x09, 0x4c, 0x01, \ + 0x0c, 0x0c, 0xc0, 0x24, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x41, 0x66, 0x80, 0x6b, \ + 0x4c, 0x01, 0xe0, 0x08, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x7c, 0xb8, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xef, 0xfc, \ + 0xc0, 0x30, 0x6e, 0xe9, \ + 0x61, 0x41, 0x4c, 0x01, \ + 0x4c, 0x01, 0xc0, 0x15, \ + 0xc0, 0x1c, 0x69, 0xc9, \ + 0x64, 0x59, 0x4c, 0x01, \ + 0x0c, 0x0c, 0xc0, 0x24, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x41, 0x96, 0x80, 0x6b, \ + 0x4c, 0x01, 0xe0, 0x08, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x7a, 0xf8, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xef, 0xfc, \ + 0xc0, 0x30, 0x6e, 0xe9, \ + 0x61, 0x45, 0x4c, 0x01, \ + 0x4c, 0x01, 0xc0, 0x15, \ + 0xc0, 0x16, 0x67, 0x61, \ + 0x69, 0xcd, 0x4c, 0x01, \ + 0x0c, 0x0c, 0xc0, 0x1c, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x00, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x6f, 0x21, 0x00, 0x0b, \ + 0x46, 0x86, 0xc0, 0x17, \ + 0x8c, 0x34, 0xe1, 0x40, \ + 0xe6, 0x86, 0x40, 0x0a, \ + 0x40, 0x46, 0x8c, 0x0e, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0x87, 0x11, 0xa1, 0x2b, \ + 0x80, 0xe5, 0x80, 0x16, \ + 0xe8, 0x00, 0x40, 0xc8, \ + 0x08, 0x0c, 0x88, 0xf1, \ + 0x8f, 0x75, 0xf2, 0x00, \ + 0x08, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xe1, 0x40, 0x40, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xe0, 0x8c, 0x42, 0x4a, \ + 0xdd, 0x93, 0xfe, 0xa5, \ + 0xfa, 0x00, 0x00, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x6f, 0x21, 0x00, 0x0b, \ + 0x47, 0x46, 0xc0, 0x17, \ + 0x8c, 0x34, 0xe1, 0x40, \ + 0xe6, 0x86, 0x40, 0x0a, \ + 0x41, 0x06, 0x8c, 0x0e, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0x87, 0x11, 0xa1, 0x2b, \ + 0x80, 0xe5, 0x80, 0x16, \ + 0xe8, 0x00, 0x40, 0xc8, \ + 0x08, 0x0c, 0x88, 0xf1, \ + 0x8b, 0x75, 0xf2, 0x00, \ + 0x08, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xe1, 0x40, 0x41, 0x06, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xe0, 0x8c, 0x42, 0x4a, \ + 0xdd, 0x93, 0xfe, 0xa5, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0xe1, 0x38, 0x07, 0x46, \ + 0x90, 0x58, 0x8c, 0x44, \ + 0xf3, 0xec, 0x4a, 0x50, \ + 0x49, 0xc6, 0x90, 0x56, \ + 0x90, 0x54, 0xe1, 0x40, \ + 0x94, 0xc6, 0x81, 0xc1, \ + 0x85, 0x61, 0x90, 0x54, \ + 0x90, 0x54, 0x94, 0x66, \ + 0x00, 0x0b, 0x94, 0xc6, \ + 0xc0, 0x17, 0x6f, 0x21, \ + 0xe1, 0x44, 0x0a, 0x06, \ + 0x40, 0x0a, 0x94, 0x54, \ + 0x94, 0x0e, 0xe6, 0x86, \ + 0x87, 0x11, 0x90, 0x04, \ + 0x01, 0x86, 0x80, 0x16, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0xc8, 0x49, 0x84, 0x48, \ + 0x80, 0x04, 0x84, 0x46, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xec, \ + 0x80, 0x18, 0x8c, 0x04, \ + 0xc8, 0x19, 0xa1, 0x2b, \ + 0x80, 0xc5, 0x80, 0x16, \ + 0xe8, 0x00, 0x40, 0x88, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x0c, 0x0c, 0x83, 0xc5, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe1, 0x40, 0x41, 0xc6, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xe0, 0x8c, 0x42, 0x4a, \ + 0xdd, 0x93, 0xfe, 0xa5, \ + 0xe1, 0x40, 0x40, 0x46, \ + 0x81, 0x11, 0x80, 0x04, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xe1, 0x40, 0x41, 0x06, \ + 0x81, 0x11, 0x80, 0x04, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xe1, 0x40, 0x41, 0xc6, \ + 0x81, 0x11, 0x80, 0x04, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xe1, 0x20, 0x40, 0xc6, \ + 0x84, 0x28, 0x80, 0x14, \ + 0xf3, 0xf8, 0x44, 0x20, \ + 0x02, 0x86, 0x84, 0x26, \ + 0x84, 0x14, 0xe1, 0x14, \ + 0x04, 0x24, 0x84, 0x28, \ + 0x84, 0x26, 0xfc, 0x94, \ + 0x84, 0x28, 0x80, 0x14, \ + 0xf3, 0xf4, 0x44, 0x20, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xcc, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe1, 0x20, 0x40, 0xc6, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xc2, 0x19, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x40, 0xc6, 0x80, 0x6b, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0xe0, 0x0c, 0x02, 0xa6, \ + 0x6e, 0x59, 0x4c, 0x11, \ + 0x80, 0x18, 0xc0, 0x2f, \ + 0xf3, 0xf8, 0x42, 0x10, \ + 0x42, 0x46, 0x80, 0x16, \ + 0x81, 0x21, 0xe0, 0x04, \ + 0x71, 0xf8, 0x8c, 0x0b, \ + 0x50, 0x46, 0xef, 0xfc, \ + 0xa0, 0x04, 0xe1, 0x40, \ + 0xc7, 0x07, 0x80, 0x08, \ + 0xf0, 0x04, 0x54, 0x00, \ + 0x66, 0xc1, 0x0e, 0x60, \ + 0x13, 0x06, 0xc0, 0x56, \ + 0x16, 0xc6, 0xe1, 0x44, \ + 0xa1, 0xab, 0xe1, 0x24, \ + 0x0c, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe1, 0x90, 0x00, 0x0a, \ + 0xa4, 0x04, 0x82, 0xf5, \ + 0x40, 0x0a, 0x80, 0x08, \ + 0x0c, 0x39, 0xe0, 0x8c, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x9b, 0x21, 0x83, 0x11, \ + 0x8e, 0x0b, 0x84, 0x55, \ + 0xef, 0xc4, 0x41, 0xc8, \ + 0xa0, 0x04, 0x81, 0x65, \ + 0xc7, 0x07, 0x80, 0x08, \ + 0xf0, 0x04, 0x54, 0x00, \ + 0x80, 0x04, 0xac, 0x04, \ + 0xf8, 0x55, 0xa1, 0x0b, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0x7f, 0x78, 0x8e, 0x0b, \ + 0x88, 0xf1, 0xef, 0xc0, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xff, 0xc4, 0x01, 0x28, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x00, 0x26, 0x80, 0x7b, \ + 0x4e, 0x01, 0xe0, 0x0c, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x81, 0x01, 0x81, 0x61, \ + 0x63, 0x38, 0x81, 0x11, \ + 0xfc, 0x0e, 0xef, 0xd0, \ + 0xe1, 0x20, 0x51, 0x46, \ + 0x80, 0x66, 0xa0, 0x04, \ + 0xe1, 0x24, 0x12, 0xc6, \ + 0x80, 0xa4, 0xa4, 0x04, \ + 0x43, 0xa6, 0x83, 0xb1, \ + 0x83, 0x21, 0xe0, 0x0c, \ + 0x6c, 0x78, 0x8e, 0x0b, \ + 0x01, 0x06, 0xef, 0xfc, \ + 0x80, 0x14, 0xe1, 0x14, \ + 0x42, 0xc6, 0x84, 0x6c, \ + 0x84, 0x14, 0xe1, 0x20, \ + 0xc4, 0x29, 0x84, 0x28, \ + 0x02, 0xc6, 0x84, 0x26, \ + 0x84, 0x14, 0xe1, 0x18, \ + 0xbe, 0x2e, 0x84, 0x28, \ + 0x42, 0x46, 0x84, 0x66, \ + 0x84, 0x14, 0xe1, 0x20, \ + 0x80, 0x04, 0x84, 0xb6, \ + 0x02, 0x86, 0x80, 0xbc, \ + 0x83, 0x21, 0xe0, 0x0c, \ + 0x6a, 0xb8, 0x8e, 0x0b, \ + 0xa0, 0x04, 0xef, 0xfc, \ + 0x42, 0x46, 0x80, 0xb6, \ + 0x81, 0x21, 0xe0, 0x04, \ + 0x6a, 0x38, 0x8e, 0x0b, \ + 0x4c, 0x46, 0xef, 0xfc, \ + 0x98, 0x04, 0xe1, 0x40, \ + 0xc7, 0x07, 0x80, 0x08, \ + 0xf0, 0x04, 0x56, 0x00, \ + 0x66, 0xc1, 0x10, 0x70, \ + 0xa1, 0xbb, 0xc0, 0x56, \ + 0x0e, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe1, 0x94, 0x00, 0x0a, \ + 0x0e, 0x39, 0x82, 0x55, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x9b, 0x21, 0x83, 0x11, \ + 0x7a, 0x98, 0x90, 0x0b, \ + 0x81, 0x65, 0xef, 0xc0, \ + 0x80, 0x08, 0x98, 0x04, \ + 0x56, 0x00, 0xc7, 0x07, \ + 0xa4, 0x04, 0xf0, 0x04, \ + 0x81, 0xab, 0x80, 0x04, \ + 0x41, 0x46, 0xf8, 0xe5, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x80, 0x16, 0x81, 0x11, \ + 0xe1, 0x18, 0x00, 0xc6, \ + 0xbe, 0x24, 0x80, 0x04, \ + 0x40, 0x46, 0x80, 0x26, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x40, 0xc6, 0x80, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xf4, \ + 0xfc, 0x04, 0x83, 0x11, \ + 0x0c, 0x0c, 0x98, 0xf1, \ + 0x1b, 0xf8, 0xf2, 0x7c, \ + 0xc1, 0xc3, 0xff, 0xd0, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x86, 0x7b, 0x88, 0x6b, \ + 0x82, 0x9b, 0x84, 0x8b, \ + 0xe0, 0x04, 0x43, 0xe6, \ + 0x6e, 0x59, 0x40, 0x11, \ + 0x41, 0xf8, 0xc0, 0x2f, \ + 0x43, 0x86, 0xe8, 0x00, \ + 0x84, 0x14, 0xe1, 0x14, \ + 0x43, 0xc6, 0x84, 0x96, \ + 0x84, 0x14, 0xe1, 0x14, \ + 0x02, 0x06, 0x84, 0x86, \ + 0x84, 0x14, 0xe1, 0x18, \ + 0x02, 0x86, 0x84, 0x76, \ + 0x84, 0x14, 0xe1, 0x10, \ + 0xc2, 0x67, 0x84, 0x28, \ + 0x44, 0x20, 0x84, 0x69, \ + 0xac, 0x2f, 0xf3, 0xf4, \ + 0x88, 0xf1, 0x84, 0x26, \ + 0xf2, 0x1c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0x85, 0x05, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe1, 0x14, 0x00, 0x86, \ + 0x84, 0x28, 0x80, 0x14, \ + 0xe1, 0x14, 0x07, 0x06, \ + 0x8c, 0x34, 0x84, 0x48, \ + 0x8c, 0xca, 0x81, 0x51, \ + 0x84, 0x38, 0x8c, 0x5c, \ + 0xf3, 0xf8, 0x46, 0x30, \ + 0x80, 0x14, 0x84, 0x36, \ + 0x84, 0x38, 0xcb, 0x27, \ + 0x82, 0x49, 0x84, 0x29, \ + 0xe0, 0xc7, 0xa8, 0x2f, \ + 0xf9, 0x88, 0x40, 0xc2, \ + 0xf2, 0xfc, 0x44, 0x30, \ + 0xdd, 0x93, 0x84, 0x26, \ + 0xe1, 0x14, 0x03, 0x06, \ + 0x04, 0x04, 0x84, 0x14, \ + 0x84, 0x2c, 0xe1, 0xc0, \ + 0xe1, 0x14, 0x02, 0x86, \ + 0x88, 0x38, 0x84, 0x24, \ + 0xf0, 0x04, 0x48, 0x00, \ + 0xf3, 0xf8, 0x46, 0x30, \ + 0x88, 0x36, 0xa8, 0x3f, \ + 0x84, 0x28, 0x84, 0x14, \ + 0x40, 0x00, 0xca, 0x07, \ + 0x44, 0x20, 0xf1, 0x00, \ + 0xa0, 0x2f, 0xf2, 0xfc, \ + 0xdd, 0x93, 0x84, 0x26, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x84, 0x7b, 0x86, 0x6b, \ + 0x80, 0x9b, 0x82, 0x8b, \ + 0xe0, 0x04, 0x41, 0xf6, \ + 0x6e, 0x59, 0x52, 0x01, \ + 0x7b, 0xb8, 0xc0, 0x2f, \ + 0x03, 0xc6, 0xef, 0xfc, \ + 0x12, 0x29, 0xe1, 0x18, \ + 0xc0, 0x31, 0x60, 0x55, \ + 0x84, 0x26, 0x84, 0x14, \ + 0xe1, 0x18, 0x02, 0xc6, \ + 0x88, 0x38, 0x84, 0x24, \ + 0x48, 0x80, 0xc8, 0x87, \ + 0x46, 0x30, 0xf1, 0xc0, \ + 0xa8, 0x3f, 0xf2, 0x3c, \ + 0x84, 0x14, 0x88, 0x36, \ + 0x9e, 0x79, 0x84, 0x28, \ + 0xf3, 0xc0, 0x44, 0x20, \ + 0x84, 0x26, 0xae, 0x2f, \ + 0xe1, 0x18, 0x42, 0x06, \ + 0x84, 0x66, 0x84, 0x14, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xf7, 0x55, 0xf2, 0x1c, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x80, 0x7b, 0x82, 0x6b, \ + 0x66, 0x4d, 0x0e, 0x19, \ + 0x0e, 0x29, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x66, 0x49, \ + 0x66, 0x45, 0x0e, 0x39, \ + 0x7c, 0x18, 0xc0, 0x2f, \ + 0x8e, 0x0b, 0xef, 0xfc, \ + 0x68, 0x58, 0x8c, 0x1b, \ + 0x8e, 0x0b, 0xe8, 0x00, \ + 0x88, 0xf1, 0x8c, 0x1b, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0xe0, 0x00, 0x29, 0xa8, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x41, 0xa6, 0x80, 0x6b, \ + 0x4c, 0x01, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0xe0, 0x04, 0x43, 0xa6, \ + 0x7d, 0x78, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xef, 0xfc, \ + 0xc0, 0x2f, 0x66, 0x45, \ + 0x66, 0x49, 0x0c, 0x19, \ + 0x0c, 0x29, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x66, 0x4d, \ + 0xef, 0x98, 0x49, 0x48, \ + 0x00, 0x46, 0x80, 0x7b, \ + 0x80, 0x04, 0xe1, 0x04, \ + 0x0c, 0x95, 0x80, 0x84, \ + 0xc0, 0x0a, 0x63, 0x6d, \ + 0x66, 0x51, 0x0c, 0x39, \ + 0x90, 0x0b, 0xc0, 0x2f, \ + 0x8e, 0x2b, 0x92, 0x1b, \ + 0xef, 0x98, 0x49, 0x08, \ + 0x66, 0x59, 0x0c, 0x39, \ + 0x4c, 0x05, 0xc0, 0x2f, \ + 0xc0, 0x0c, 0x63, 0xdd, \ + 0x92, 0x1b, 0x90, 0x0b, \ + 0x48, 0x58, 0x8e, 0x2b, \ + 0x0c, 0x39, 0xef, 0x98, \ + 0xc0, 0x2f, 0x66, 0x55, \ + 0x63, 0xd5, 0x4c, 0x05, \ + 0x90, 0x0b, 0xc0, 0x0c, \ + 0x8e, 0x2b, 0x92, 0x1b, \ + 0xef, 0x98, 0x47, 0xa8, \ + 0x66, 0x5d, 0x0c, 0x39, \ + 0x4c, 0x05, 0xc0, 0x2f, \ + 0xc0, 0x0c, 0x63, 0xe1, \ + 0x92, 0x1b, 0x90, 0x0b, \ + 0x46, 0xf8, 0x8e, 0x2b, \ + 0x0c, 0x15, 0xef, 0x98, \ + 0xc0, 0x0b, 0x6d, 0x9d, \ + 0x63, 0xd9, 0x4c, 0x05, \ + 0x91, 0x21, 0xc0, 0x0c, \ + 0x92, 0x1b, 0x82, 0x0b, \ + 0xef, 0x98, 0x43, 0x48, \ + 0xe1, 0x18, 0x42, 0x06, \ + 0x4c, 0x05, 0x84, 0x14, \ + 0xc0, 0x07, 0x62, 0x65, \ + 0x6e, 0x5d, 0x4c, 0x05, \ + 0x4c, 0x05, 0xc0, 0x08, \ + 0xc0, 0x09, 0x60, 0x39, \ + 0x61, 0x0d, 0x4c, 0x05, \ + 0x84, 0x08, 0xc0, 0x09, \ + 0x69, 0xe1, 0x4c, 0x01, \ + 0x84, 0x08, 0xc0, 0x1c, \ + 0x69, 0xc1, 0x4c, 0x01, \ + 0x84, 0x08, 0xc0, 0x23, \ + 0x61, 0x11, 0x4c, 0x01, \ + 0x84, 0x08, 0xc0, 0x24, \ + 0xe1, 0x18, 0x02, 0xc6, \ + 0x4c, 0x01, 0x84, 0x14, \ + 0xc0, 0x24, 0x64, 0x61, \ + 0x9e, 0x09, 0x84, 0x08, \ + 0x69, 0xe5, 0x4c, 0x01, \ + 0x84, 0x08, 0xc0, 0x1c, \ + 0x4c, 0x01, 0x9e, 0x09, \ + 0xc0, 0x23, 0x69, 0xc5, \ + 0x9e, 0x09, 0x84, 0x08, \ + 0x61, 0x15, 0x4c, 0x01, \ + 0x84, 0x08, 0xc0, 0x24, \ + 0x4c, 0x01, 0x9e, 0x09, \ + 0xc0, 0x24, 0x64, 0x65, \ + 0xc9, 0x07, 0x84, 0x08, \ + 0x4c, 0x01, 0x8e, 0x09, \ + 0xc0, 0x1c, 0x69, 0xe9, \ + 0xc9, 0x07, 0x84, 0x08, \ + 0x4c, 0x01, 0x8e, 0x09, \ + 0xc0, 0x23, 0x69, 0xc9, \ + 0xc9, 0x07, 0x84, 0x08, \ + 0x4c, 0x01, 0x8e, 0x09, \ + 0xc0, 0x24, 0x61, 0x19, \ + 0xc9, 0x07, 0x84, 0x08, \ + 0x4c, 0x01, 0x8e, 0x09, \ + 0xc0, 0x24, 0x64, 0x69, \ + 0x88, 0xf1, 0x8c, 0x0b, \ + 0xf2, 0x1c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0x81, 0x45, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0x64, 0x5d, 0x00, 0x15, \ + 0x04, 0x14, 0xc0, 0x05, \ + 0x06, 0x0e, 0xe1, 0x84, \ + 0xc3, 0xd0, 0x64, 0x01, \ + 0x00, 0x39, 0x86, 0x23, \ + 0xc0, 0x30, 0x6e, 0x79, \ + 0xe0, 0x06, 0x42, 0x2a, \ + 0xe1, 0x84, 0x04, 0x34, \ + 0x42, 0x2a, 0x82, 0x23, \ + 0x00, 0x2b, 0xe0, 0x0e, \ + 0xc0, 0x18, 0x6a, 0x99, \ + 0xfa, 0x88, 0x42, 0x12, \ + 0x40, 0x15, 0xa4, 0x13, \ + 0xc0, 0x0c, 0x63, 0xe5, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x2f, 0x65, 0xd1, \ + 0x66, 0x61, 0x0c, 0x99, \ + 0x02, 0x00, 0xc0, 0x2f, \ + 0xe5, 0x1d, 0xec, 0x04, \ + 0x66, 0xd1, 0x0c, 0x89, \ + 0xc7, 0x97, 0xc0, 0x2f, \ + 0x80, 0x95, 0x81, 0x11, \ + 0x80, 0xb5, 0xa7, 0x0b, \ + 0x4c, 0x11, 0x83, 0x11, \ + 0xc0, 0x30, 0x6f, 0xf1, \ + 0x6f, 0xf1, 0x0c, 0x09, \ + 0xa3, 0x0b, 0xc0, 0x30, \ + 0x0c, 0x09, 0x90, 0x93, \ + 0xc0, 0x30, 0x6e, 0xf5, \ + 0x40, 0x4a, 0x82, 0x15, \ + 0x82, 0x85, 0xe0, 0x8c, \ + 0x66, 0x59, 0x00, 0x60, \ + 0x81, 0xf5, 0xc0, 0xbd, \ + 0xe0, 0x8c, 0x40, 0x4a, \ + 0x00, 0x60, 0x82, 0x25, \ + 0xc0, 0xbd, 0x66, 0x51, \ + 0x00, 0x60, 0x81, 0x85, \ + 0xc0, 0xbd, 0x66, 0x5d, \ + 0x00, 0x60, 0x81, 0x45, \ + 0xc0, 0xbd, 0x66, 0x55, \ + 0x8e, 0x0b, 0x80, 0x78, \ + 0xef, 0x94, 0x72, 0x28, \ + 0xfa, 0x80, 0x02, 0x94, \ + 0xe1, 0x02, 0x46, 0x1a, \ + 0xfa, 0x80, 0x04, 0x84, \ + 0x8c, 0x0b, 0x81, 0x41, \ + 0x63, 0xf8, 0x8e, 0x1b, \ + 0x0c, 0x09, 0xef, 0xfc, \ + 0xc0, 0x2f, 0x66, 0x61, \ + 0x6e, 0x59, 0x0c, 0x19, \ + 0xc8, 0x07, 0xc0, 0x2f, \ + 0x64, 0xe1, 0x4c, 0x03, \ + 0x4c, 0x03, 0xc0, 0x0e, \ + 0xc0, 0x11, 0x6c, 0xd1, \ + 0x60, 0x81, 0x4c, 0x03, \ + 0x4c, 0x03, 0xc0, 0x12, \ + 0xc0, 0x12, 0x62, 0x29, \ + 0x53, 0x98, 0x8c, 0x0b, \ + 0x0c, 0x19, 0xe8, 0x00, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x88, 0xf1, 0x8c, 0x0b, \ + 0xf2, 0x1c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xa3, 0x45, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xf8, 0x04, 0x04, 0x0c, \ + 0x6f, 0xf1, 0x00, 0x19, \ + 0xa1, 0x1b, 0xc0, 0x30, \ + 0x6e, 0xf5, 0x00, 0x19, \ + 0x82, 0x05, 0xc0, 0x30, \ + 0xe0, 0x8c, 0x42, 0x4a, \ + 0x04, 0x00, 0x82, 0x85, \ + 0xc0, 0xc3, 0x67, 0x51, \ + 0x42, 0x4a, 0x81, 0xf5, \ + 0x82, 0x25, 0xe0, 0x8c, \ + 0x67, 0x71, 0x04, 0x00, \ + 0x81, 0x85, 0xc0, 0xc3, \ + 0x67, 0x61, 0x04, 0x00, \ + 0x81, 0x45, 0xc0, 0xc3, \ + 0x67, 0x81, 0x04, 0x00, \ + 0x00, 0x69, 0xc0, 0xc3, \ + 0xc0, 0x2f, 0x65, 0xdd, \ + 0xa3, 0x6b, 0x88, 0x24, \ + 0x6b, 0x51, 0x00, 0x5b, \ + 0x00, 0x45, 0xc0, 0x17, \ + 0xc0, 0x0b, 0x6d, 0xb1, \ + 0x64, 0x69, 0x00, 0xc5, \ + 0x00, 0x35, 0xc0, 0x05, \ + 0xc0, 0x05, 0x64, 0x6d, \ + 0xa5, 0x6b, 0x80, 0xe5, \ + 0x00, 0x65, 0x82, 0x35, \ + 0xc0, 0x05, 0x64, 0x61, \ + 0x00, 0x65, 0x83, 0x65, \ + 0xc0, 0x05, 0x64, 0x65, \ + 0xa7, 0x6b, 0x83, 0x25, \ + 0x63, 0xe5, 0x00, 0x75, \ + 0x80, 0x75, 0xc0, 0x0c, \ + 0x81, 0xb5, 0x8e, 0x6b, \ + 0x64, 0x61, 0x00, 0x65, \ + 0xcc, 0x7d, 0xc0, 0x05, \ + 0x00, 0x65, 0x80, 0xd5, \ + 0xc0, 0x05, 0x64, 0x65, \ + 0xfa, 0x9c, 0x4c, 0x62, \ + 0xe0, 0x8c, 0x42, 0x4a, \ + 0x02, 0x64, 0xd8, 0xc7, \ + 0x82, 0xa5, 0xe0, 0xb0, \ + 0xd8, 0x47, 0xd8, 0x57, \ + 0xe0, 0x0a, 0x4a, 0x5a, \ + 0xe0, 0x0a, 0x48, 0x4a, \ + 0x63, 0xe9, 0x40, 0x55, \ + 0x40, 0x45, 0xc0, 0x0c, \ + 0xc0, 0x0c, 0x63, 0xed, \ + 0xe0, 0x0a, 0x48, 0x1a, \ + 0xd8, 0x37, 0xd8, 0x1d, \ + 0x63, 0xf1, 0x40, 0x45, \ + 0x81, 0x41, 0xc0, 0x0c, \ + 0xb8, 0x13, 0x80, 0x85, \ + 0xe0, 0x0a, 0x48, 0x1a, \ + 0xe0, 0x0a, 0x42, 0x3a, \ + 0x63, 0xf5, 0x40, 0x45, \ + 0x40, 0x15, 0xc0, 0x0c, \ + 0xc0, 0x0c, 0x63, 0xf9, \ + 0xf0, 0x04, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x86, 0x7b, 0x88, 0xab, \ + 0x80, 0x8b, 0x84, 0x9b, \ + 0xe0, 0x08, 0x00, 0xb6, \ + 0x6e, 0x59, 0x50, 0x01, \ + 0x82, 0x0b, 0xc0, 0x2f, \ + 0xef, 0x94, 0x71, 0xc8, \ + 0x73, 0x58, 0x80, 0x6b, \ + 0xa1, 0x0b, 0xef, 0x94, \ + 0x80, 0x65, 0x81, 0x11, \ + 0xec, 0x04, 0x02, 0x00, \ + 0x82, 0x65, 0xa3, 0xab, \ + 0x82, 0x35, 0xd3, 0x1d, \ + 0x84, 0x45, 0xa3, 0x7b, \ + 0x82, 0xb5, 0xa1, 0x7b, \ + 0x65, 0xc5, 0x50, 0x95, \ + 0x83, 0x65, 0xc0, 0x0c, \ + 0x84, 0x25, 0xa3, 0x7b, \ + 0x82, 0x95, 0xa1, 0x7b, \ + 0x65, 0xc5, 0x50, 0x15, \ + 0x81, 0xe5, 0xc0, 0x0c, \ + 0x65, 0xc5, 0x50, 0x95, \ + 0x50, 0x95, 0xc0, 0x0c, \ + 0xc0, 0x0c, 0x65, 0xc9, \ + 0x50, 0x15, 0x81, 0x75, \ + 0xc0, 0x0c, 0x65, 0xc5, \ + 0x65, 0xc9, 0x50, 0x15, \ + 0x90, 0x0b, 0xc0, 0x0c, \ + 0x63, 0x78, 0x8e, 0x1b, \ + 0x0e, 0x64, 0xef, 0xf8, \ + 0x59, 0x38, 0xe1, 0xa0, \ + 0x03, 0x06, 0xef, 0xfc, \ + 0x84, 0x14, 0xe1, 0x18, \ + 0x03, 0x46, 0x84, 0x76, \ + 0x84, 0x14, 0xe1, 0x18, \ + 0x0c, 0x0c, 0x84, 0x66, \ + 0x1a, 0x38, 0xf2, 0x3c, \ + 0xc1, 0xc3, 0xff, 0xfc, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0xc2, 0x27, 0x86, 0x6b, \ + 0x04, 0x24, 0x84, 0x21, \ + 0x06, 0x24, 0xfa, 0x80, \ + 0x82, 0x33, 0xe1, 0x84, \ + 0x4e, 0x3a, 0xc5, 0x3d, \ + 0x80, 0xf5, 0xe0, 0x0a, \ + 0x65, 0x61, 0x00, 0x15, \ + 0x02, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x75, 0xe1, 0x4c, \ + 0x6e, 0x59, 0x00, 0x39, \ + 0x00, 0x00, 0xc0, 0x2f, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xff, 0x21, 0x81, 0x11, \ + 0xef, 0xc0, 0x4b, 0xa8, \ + 0x60, 0x01, 0x00, 0x06, \ + 0x40, 0x0a, 0xfd, 0x09, \ + 0x42, 0x0a, 0xe0, 0x1e, \ + 0x44, 0x0a, 0xe5, 0x1a, \ + 0x81, 0x31, 0xe5, 0x9a, \ + 0x64, 0x81, 0x00, 0x10, \ + 0x42, 0x22, 0xc7, 0xa1, \ + 0x04, 0x0e, 0xf2, 0x8c, \ + 0xc0, 0x03, 0x74, 0x25, \ + 0x6e, 0xf8, 0x81, 0x31, \ + 0x88, 0xf1, 0xef, 0x60, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x6c, 0x01, 0x0c, 0x12, \ + 0x81, 0x11, 0xc0, 0x28, \ + 0x48, 0xd8, 0x8c, 0x2b, \ + 0x8c, 0x0b, 0xef, 0x64, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0x43, 0x86, 0x82, 0x3b, \ + 0x84, 0x14, 0xe1, 0x14, \ + 0xe1, 0x24, 0x05, 0x06, \ + 0x88, 0x24, 0x84, 0x18, \ + 0x85, 0x1b, 0x88, 0x28, \ + 0x00, 0x15, 0x80, 0xf5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe1, 0x50, 0x02, 0x0a, \ + 0x00, 0x00, 0x82, 0x15, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0x9d, 0x21, 0x85, 0x11, \ + 0xff, 0xc0, 0x07, 0x68, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0x02, 0x06, 0x82, 0x3b, \ + 0x84, 0x14, 0xe1, 0x18, \ + 0xe1, 0x24, 0x05, 0x46, \ + 0x88, 0x24, 0x84, 0x18, \ + 0x85, 0x1b, 0x88, 0x28, \ + 0x00, 0x15, 0x80, 0xf5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe1, 0x54, 0x02, 0x0a, \ + 0x00, 0x00, 0x82, 0x15, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0x9f, 0x21, 0x85, 0x11, \ + 0xff, 0xc0, 0x05, 0xa8, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0x42, 0x46, 0x82, 0x3b, \ + 0x84, 0x14, 0xe1, 0x24, \ + 0xe1, 0x18, 0x04, 0xc6, \ + 0x88, 0x24, 0x84, 0x18, \ + 0xc9, 0x27, 0x88, 0x28, \ + 0x85, 0x1b, 0x8e, 0x29, \ + 0x00, 0x15, 0x80, 0xf5, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0xe1, 0x58, 0x02, 0x0a, \ + 0x00, 0x00, 0x82, 0x15, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0xa1, 0x21, 0x85, 0x11, \ + 0xff, 0xc0, 0x03, 0xc8, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x42, 0x06, 0x82, 0x3b, \ + 0x84, 0x14, 0xe1, 0x24, \ + 0xe1, 0x18, 0x04, 0xc6, \ + 0x88, 0x24, 0x84, 0x18, \ + 0x9e, 0x29, 0x88, 0x28, \ + 0x80, 0xf5, 0x85, 0x1b, \ + 0x65, 0x61, 0x00, 0x15, \ + 0x02, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x15, 0xe1, 0x5c, \ + 0x66, 0xc1, 0x00, 0x00, \ + 0x85, 0x11, 0xc0, 0x56, \ + 0x02, 0x18, 0xa3, 0x21, \ + 0xdd, 0x93, 0xff, 0xc0, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xec, 0x80, 0x04, 0x12, \ + 0x82, 0x85, 0xa1, 0x2b, \ + 0x81, 0x11, 0x81, 0x31, \ + 0xc5, 0x3d, 0x82, 0x31, \ + 0xf0, 0x04, 0x48, 0x00, \ + 0xc3, 0x07, 0x88, 0x13, \ + 0x82, 0x0b, 0xfe, 0x55, \ + 0x81, 0x11, 0xdd, 0x93, \ + 0xdd, 0x93, 0x82, 0x0b, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x2f, 0x66, 0xb5, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x0c, 0x09, 0x82, 0x55, \ + 0xc0, 0x30, 0x6f, 0x11, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x0c, 0x0c, 0x82, 0xd5, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe1, 0x00, 0x40, 0x86, \ + 0x66, 0xa9, 0x0c, 0x19, \ + 0x80, 0x04, 0xc0, 0x2f, \ + 0x0c, 0x0c, 0x80, 0x16, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0x63, 0xe9, 0x0c, 0x15, \ + 0x85, 0x31, 0xc0, 0x0c, \ + 0x8c, 0x0b, 0x83, 0x41, \ + 0x6c, 0x18, 0x82, 0x2b, \ + 0x8c, 0x0b, 0xef, 0xfc, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x00, 0x0c, 0x81, 0x45, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x40, 0x06, 0x80, 0x6b, \ + 0x80, 0x04, 0xe1, 0x38, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf1, 0xfc, \ + 0x66, 0xb5, 0x0c, 0x09, \ + 0x40, 0x4a, 0xc0, 0x2f, \ + 0x00, 0x04, 0xe0, 0x98, \ + 0xff, 0x11, 0xfa, 0x00, \ + 0x82, 0x0d, 0x82, 0x45, \ + 0x0c, 0x19, 0x82, 0xf5, \ + 0xc0, 0x30, 0x6e, 0xe5, \ + 0xfb, 0xfc, 0x02, 0x0a, \ + 0x81, 0x85, 0x80, 0x95, \ + 0x80, 0xd5, 0x82, 0x0d, \ + 0x6b, 0xdd, 0x0c, 0x19, \ + 0xa1, 0x1b, 0xc0, 0x27, \ + 0x40, 0x0a, 0x82, 0x85, \ + 0x80, 0xa5, 0xe0, 0x94, \ + 0x65, 0xc5, 0x02, 0x60, \ + 0x81, 0x85, 0xc0, 0xa3, \ + 0x66, 0xa9, 0x02, 0x60, \ + 0x81, 0x45, 0xc0, 0xbd, \ + 0x67, 0xd5, 0x02, 0x60, \ + 0x84, 0x18, 0xc0, 0x9e, \ + 0x66, 0xad, 0x0c, 0x39, \ + 0x44, 0x32, 0xc0, 0x2f, \ + 0x05, 0x0a, 0xf0, 0x84, \ + 0xa6, 0x13, 0xe4, 0xfc, \ + 0xe0, 0x00, 0x4f, 0xf6, \ + 0x0e, 0x24, 0x80, 0x75, \ + 0x40, 0x0a, 0xfa, 0x80, \ + 0x10, 0x12, 0xe0, 0x90, \ + 0x82, 0x25, 0xe0, 0x00, \ + 0x67, 0xe1, 0x00, 0x60, \ + 0x81, 0x11, 0xc0, 0x9e, \ + 0xe0, 0x18, 0x04, 0x06, \ + 0xef, 0x60, 0x78, 0x88, \ + 0xfa, 0x80, 0x14, 0x84, \ + 0xbc, 0x8e, 0xd5, 0x7d, \ + 0x81, 0x01, 0x80, 0x65, \ + 0x56, 0x86, 0x89, 0x95, \ + 0x52, 0x06, 0xe1, 0x00, \ + 0xac, 0x04, 0xe1, 0x00, \ + 0xfa, 0x80, 0x14, 0x84, \ + 0x42, 0x06, 0x80, 0x86, \ + 0x83, 0x21, 0xe0, 0x08, \ + 0x65, 0xb8, 0x8c, 0x0b, \ + 0xa7, 0x01, 0xef, 0xf8, \ + 0xef, 0xd0, 0x5b, 0xc8, \ + 0xe0, 0x08, 0x00, 0x06, \ + 0xef, 0xc0, 0x65, 0xc8, \ + 0xe0, 0x3c, 0x42, 0x86, \ + 0x53, 0x38, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xef, 0xe0, \ + 0xef, 0xf8, 0x5e, 0x88, \ + 0x6e, 0xe9, 0x0c, 0x09, \ + 0x42, 0x62, 0xc0, 0x30, \ + 0x42, 0x01, 0xf0, 0xa8, \ + 0xc0, 0x28, 0x60, 0xe1, \ + 0x80, 0x08, 0xa4, 0x04, \ + 0xef, 0xdc, 0x50, 0x88, \ + 0x66, 0xb5, 0x0c, 0x19, \ + 0x42, 0x0a, 0xc0, 0x2f, \ + 0x82, 0x65, 0xe0, 0x90, \ + 0xe0, 0x88, 0x02, 0xa4, \ + 0x67, 0xe1, 0x04, 0x60, \ + 0x84, 0x13, 0xc0, 0x9e, \ + 0x84, 0x0e, 0x94, 0x23, \ + 0xe4, 0x00, 0x44, 0x81, \ + 0x6d, 0xa5, 0x0c, 0x15, \ + 0xc2, 0x0d, 0xc0, 0x0b, \ + 0x82, 0x81, 0x80, 0xf5, \ + 0xfa, 0x80, 0x14, 0x84, \ + 0xf0, 0x45, 0xd5, 0x7d, \ + 0x81, 0x11, 0x81, 0x55, \ + 0x6b, 0xdd, 0x4c, 0x11, \ + 0x0c, 0x19, 0xc0, 0x27, \ + 0xc0, 0x2f, 0x66, 0xb5, \ + 0xe0, 0x90, 0x42, 0x0a, \ + 0xbc, 0x24, 0x80, 0xe5, \ + 0xf1, 0x88, 0x44, 0x82, \ + 0x6b, 0xd1, 0x4c, 0x21, \ + 0x82, 0x71, 0xc0, 0x27, \ + 0x80, 0xe5, 0x95, 0x7b, \ + 0x65, 0x61, 0x0c, 0x25, \ + 0x04, 0x0a, 0xc0, 0x0c, \ + 0x8a, 0xd5, 0xe1, 0x60, \ + 0xe1, 0x38, 0x40, 0x06, \ + 0x83, 0x11, 0x80, 0x04, \ + 0x6b, 0xdd, 0x4c, 0x11, \ + 0x80, 0x18, 0xc0, 0x27, \ + 0xfc, 0x9c, 0x02, 0x14, \ + 0x40, 0x86, 0x80, 0x16, \ + 0x0c, 0x19, 0xe1, 0x00, \ + 0xc0, 0x27, 0x6b, 0xd5, \ + 0x80, 0x16, 0x80, 0x04, \ + 0x6e, 0x59, 0x0c, 0x39, \ + 0x00, 0x60, 0xc0, 0x2f, \ + 0xc0, 0x56, 0x66, 0xc1, \ + 0x8f, 0x21, 0x81, 0x11, \ + 0xef, 0xbc, 0x71, 0x08, \ + 0xe1, 0x38, 0x40, 0x06, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xfc, 0x9c, 0x02, 0x14, \ + 0xa9, 0x01, 0x80, 0x16, \ + 0xef, 0xd0, 0x54, 0xa8, \ + 0xe0, 0x10, 0x00, 0x06, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0x1e, 0x78, 0xf2, 0x7c, \ + 0x42, 0x0a, 0xff, 0xc0, \ + 0x4c, 0x81, 0xe0, 0x94, \ + 0xc0, 0x27, 0x6b, 0xd5, \ + 0xeb, 0xd8, 0x42, 0xc6, \ + 0x43, 0x06, 0x80, 0x65, \ + 0x0c, 0x19, 0xea, 0x34, \ + 0x82, 0xa3, 0xfc, 0x04, \ + 0xe4, 0xfc, 0x15, 0x0a, \ + 0x51, 0xf6, 0x80, 0x85, \ + 0x81, 0x25, 0xe0, 0x00, \ + 0x42, 0x86, 0x82, 0x83, \ + 0x84, 0x14, 0xe1, 0x00, \ + 0x0c, 0x19, 0x84, 0x86, \ + 0xc0, 0x30, 0x6e, 0x6d, \ + 0xe0, 0x88, 0x42, 0x0a, \ + 0x6b, 0xd9, 0x4c, 0x81, \ + 0x4c, 0x05, 0xc0, 0x27, \ + 0xc0, 0x09, 0x6e, 0xf1, \ + 0x8c, 0x0b, 0xf2, 0x85, \ + 0xef, 0xf8, 0x55, 0x48, \ + 0x6e, 0xe9, 0x0c, 0x09, \ + 0x4c, 0x01, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6e, 0xed, \ + 0x00, 0x0c, 0xf9, 0x25, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x40, 0xa6, 0x80, 0x6b, \ + 0x4c, 0x01, 0xe0, 0x00, \ + 0xc0, 0x2f, 0x6e, 0x59, \ + 0x46, 0xb8, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xef, 0xf4, \ + 0xef, 0xe8, 0x4d, 0x88, \ + 0x6f, 0x05, 0x0c, 0x09, \ + 0x40, 0x00, 0xc0, 0x30, \ + 0x4c, 0x01, 0xf3, 0x7c, \ + 0xc0, 0x30, 0x6f, 0x05, \ + 0x0c, 0x0c, 0x8c, 0x0b, \ + 0x23, 0x38, 0xf2, 0x00, \ + 0xc1, 0xc3, 0xff, 0xac, \ + 0x67, 0x35, 0x00, 0x1b, \ + 0x42, 0x4a, 0xc0, 0x18, \ + 0x8a, 0x65, 0xe0, 0x80, \ + 0x6e, 0x91, 0x00, 0x29, \ + 0x00, 0x39, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x95, \ + 0x6e, 0x89, 0x00, 0x49, \ + 0x00, 0x59, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x8d, \ + 0xc5, 0x37, 0xc5, 0x27, \ + 0xc5, 0x57, 0xc5, 0x47, \ + 0xe6, 0x8a, 0x44, 0x3a, \ + 0xe6, 0x92, 0x46, 0x5a, \ + 0xfa, 0x80, 0x04, 0x24, \ + 0xfa, 0x80, 0x06, 0x34, \ + 0x84, 0x25, 0x87, 0x2b, \ + 0xe0, 0x84, 0x42, 0x0a, \ + 0x6e, 0xf1, 0x00, 0x19, \ + 0x80, 0x95, 0xc0, 0x30, \ + 0xec, 0x04, 0x02, 0x10, \ + 0x80, 0x13, 0x81, 0x55, \ + 0x6e, 0x8d, 0x02, 0x19, \ + 0x40, 0x11, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6e, 0xd1, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xe1, 0x04, 0x03, 0x06, \ + 0x84, 0x1a, 0x84, 0x14, \ + 0x6f, 0x39, 0x40, 0x13, \ + 0xdd, 0x93, 0xc0, 0x17, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xec, 0x80, 0x1e, 0xf0, \ + 0x6e, 0x81, 0x00, 0x29, \ + 0x00, 0x19, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6e, 0x89, \ + 0xe6, 0x8a, 0x44, 0x1a, \ + 0x00, 0x39, 0x81, 0x41, \ + 0xc0, 0x2f, 0x6e, 0x91, \ + 0x87, 0x41, 0xa1, 0x46, \ + 0xa3, 0x46, 0x8d, 0x51, \ + 0x93, 0x41, 0xa5, 0x56, \ + 0xe0, 0x30, 0x18, 0x06, \ + 0x83, 0xc1, 0xbc, 0xcc, \ + 0xa9, 0xc6, 0xa7, 0x46, \ + 0x89, 0x51, 0xfc, 0x56, \ + 0x99, 0x41, 0xfe, 0x46, \ + 0xb9, 0x46, 0xab, 0x56, \ + 0x8f, 0x41, 0x89, 0x46, \ + 0x95, 0xc1, 0x8b, 0xc6, \ + 0xaf, 0xc6, 0xad, 0x46, \ + 0x85, 0x51, 0x8d, 0x56, \ + 0x8f, 0x46, 0xb1, 0x56, \ + 0x91, 0xc6, 0x9b, 0x41, \ + 0xbb, 0x46, 0x8b, 0xc1, \ + 0x91, 0x41, 0x93, 0x46, \ + 0xb5, 0x46, 0xb3, 0xc6, \ + 0x97, 0x51, 0x95, 0x56, \ + 0x9d, 0x61, 0x97, 0xc6, \ + 0x00, 0xc9, 0x99, 0x46, \ + 0xc0, 0x2f, 0x6e, 0x85, \ + 0xbd, 0x66, 0xb7, 0x56, \ + 0x9f, 0x51, 0x9b, 0x56, \ + 0xa1, 0xcb, 0x9d, 0x66, \ + 0x6e, 0x8d, 0x00, 0x49, \ + 0xbf, 0x56, 0xc0, 0x2f, \ + 0x00, 0x59, 0x9f, 0x56, \ + 0xc0, 0x2f, 0x6e, 0x95, \ + 0xa9, 0xcb, 0x96, 0x25, \ + 0x81, 0xc1, 0x8c, 0x15, \ + 0x00, 0x89, 0x81, 0x61, \ + 0xc0, 0x2f, 0x6e, 0x81, \ + 0xe0, 0x88, 0x0e, 0xc4, \ + 0xfa, 0x80, 0x12, 0x74, \ + 0xd2, 0x8d, 0x8e, 0x83, \ + 0x8e, 0x8b, 0x84, 0xc5, \ + 0xfa, 0x80, 0x12, 0x84, \ + 0xe4, 0x40, 0x14, 0xf0, \ + 0xfc, 0x24, 0x14, 0x99, \ + 0x52, 0x61, 0x80, 0x93, \ + 0xc0, 0x1c, 0x6c, 0xf1, \ + 0x6e, 0x81, 0x00, 0x99, \ + 0x82, 0x81, 0xc0, 0x2f, \ + 0xfa, 0x80, 0x14, 0x84, \ + 0xd4, 0x9d, 0x8e, 0x93, \ + 0xfa, 0xb5, 0x82, 0x61, \ + 0x6e, 0x85, 0x00, 0x79, \ + 0x82, 0xc1, 0xc0, 0x2f, \ + 0xf6, 0x95, 0xcf, 0xcd, \ + 0x6e, 0x81, 0x00, 0xc9, \ + 0xa3, 0xcb, 0xc0, 0x30, \ + 0x8a, 0x85, 0x9f, 0xc1, \ + 0x85, 0xa5, 0xfd, 0xc1, \ + 0x8b, 0x61, 0x81, 0xc1, \ + 0x00, 0x99, 0x81, 0x71, \ + 0xc0, 0x2f, 0x6e, 0x81, \ + 0xe6, 0x9a, 0x50, 0xca, \ + 0xfa, 0x80, 0x14, 0x84, \ + 0xd4, 0x9d, 0x90, 0x93, \ + 0x90, 0x9b, 0x84, 0xa5, \ + 0xfa, 0x80, 0x14, 0x94, \ + 0x16, 0xa9, 0x9e, 0xbb, \ + 0x80, 0xa3, 0xfc, 0x28, \ + 0x6c, 0xf1, 0x54, 0x71, \ + 0x00, 0xa9, 0xc0, 0x1c, \ + 0xc0, 0x2f, 0x6e, 0x81, \ + 0x16, 0x94, 0x82, 0x91, \ + 0x90, 0xa3, 0xfa, 0x80, \ + 0x82, 0x71, 0xd6, 0xad, \ + 0x00, 0x89, 0xfa, 0xd5, \ + 0xc0, 0x2f, 0x6e, 0x85, \ + 0xd1, 0xcd, 0x82, 0xc1, \ + 0x59, 0xe6, 0xf6, 0xb5, \ + 0x40, 0xc1, 0xe0, 0x0c, \ + 0xc0, 0x1c, 0x6d, 0x2d, \ + 0xe0, 0x0e, 0x44, 0x2a, \ + 0xe0, 0x0e, 0x42, 0x1a, \ + 0xe0, 0x16, 0x46, 0x4a, \ + 0x64, 0xf1, 0x08, 0x00, \ + 0x81, 0x51, 0xc0, 0x73, \ + 0x6f, 0x39, 0x00, 0xcb, \ + 0x58, 0x0a, 0xc0, 0x17, \ + 0x88, 0x45, 0xe4, 0x94, \ + 0x6e, 0x79, 0x00, 0xc9, \ + 0xe5, 0xcd, 0xc0, 0x30, \ + 0x08, 0xc9, 0x88, 0x55, \ + 0x00, 0x69, 0xfc, 0x14, \ + 0xc0, 0x2f, 0x6e, 0x81, \ + 0x6e, 0xd1, 0x00, 0x79, \ + 0x50, 0xca, 0xc0, 0x30, \ + 0x52, 0x7a, 0xe0, 0x1a, \ + 0x4c, 0x8a, 0xe0, 0x06, \ + 0x50, 0x8a, 0xe6, 0x9a, \ + 0xac, 0xc3, 0xe6, 0x8e, \ + 0xe6, 0x86, 0x4c, 0x9a, \ + 0x58, 0xca, 0x90, 0x93, \ + 0xac, 0x73, 0xe6, 0x86, \ + 0xe6, 0x8a, 0x4c, 0x9a, \ + 0x8c, 0xc3, 0x8e, 0xc3, \ + 0x59, 0xe6, 0x81, 0x35, \ + 0x4c, 0x42, 0xe0, 0x0c, \ + 0x98, 0xc6, 0xf0, 0x94, \ + 0x0a, 0x0a, 0x82, 0x51, \ + 0xf4, 0x35, 0xe0, 0x40, \ + 0x0c, 0x0c, 0xc0, 0xf1, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0x00, 0x00, 0x10, 0x14, \ + 0x00, 0x43, 0x43, 0x48, \ + 0x00, 0x00, 0x04, 0x84, \ + 0x00, 0x00, 0x15, 0x34, \ + 0x00, 0x43, 0x47, 0xcc, \ + 0x00, 0x00, 0x00, 0x18, \ + 0x00, 0x00, 0x14, 0x98, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x9c, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x00, \ + 0x00, 0x80, 0x02, 0x11, \ + 0x00, 0x80, 0x02, 0x12, \ + 0x00, 0x80, 0x02, 0x19, \ + 0x00, 0x80, 0x02, 0x1a, \ + 0x00, 0x80, 0x02, 0x1b, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x1d, \ + 0x00, 0x80, 0x02, 0x1e, \ + 0x00, 0x80, 0x02, 0x20, \ + 0x00, 0x80, 0x02, 0x24, \ + 0x00, 0x80, 0x02, 0x26, \ + 0x00, 0x80, 0x02, 0x2c, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x2f, \ + 0x00, 0x80, 0x02, 0x46, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x4a, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x4c, \ + 0x00, 0x80, 0x02, 0x4d, \ + 0x00, 0x80, 0x02, 0x4e, \ + 0x00, 0x80, 0x02, 0x51, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x53, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x57, \ + 0x00, 0x80, 0x02, 0x5a, \ + 0x00, 0x80, 0x02, 0x5f, \ + 0x00, 0x80, 0x02, 0x60, \ + 0x00, 0x80, 0x02, 0x61, \ + 0x00, 0x80, 0x02, 0x62, \ + 0x00, 0x80, 0x02, 0x63, \ + 0x00, 0x80, 0x02, 0x64, \ + 0x00, 0x80, 0x02, 0x65, \ + 0x00, 0x80, 0x02, 0x66, \ + 0x00, 0x80, 0x02, 0x67, \ + 0x00, 0x80, 0x02, 0x68, \ + 0x00, 0x80, 0x02, 0x73, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x78, \ + 0x00, 0x80, 0x02, 0x7c, \ + 0x00, 0x80, 0x02, 0x81, \ + 0x00, 0x80, 0x02, 0x86, \ + 0x00, 0x80, 0x02, 0x87, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x95, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x99, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0xa3, \ + 0x00, 0x80, 0x02, 0xa8, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0xaa, \ + 0x00, 0x80, 0x02, 0xab, \ + 0x00, 0x80, 0x02, 0xac, \ + 0x00, 0x80, 0x02, 0xad, \ + 0x00, 0x80, 0x02, 0xae, \ + 0x00, 0x80, 0x02, 0xb1, \ + 0x00, 0x80, 0x02, 0xb6, \ + 0x00, 0x80, 0x02, 0xb7, \ + 0x00, 0x80, 0x02, 0xb8, \ + 0x00, 0x80, 0x02, 0xb9, \ + 0x00, 0x80, 0x02, 0xba, \ + 0x00, 0x80, 0x02, 0xbb, \ + 0x00, 0x80, 0x02, 0xbc, \ + 0x00, 0x80, 0x02, 0xbd, \ + 0x00, 0x80, 0x02, 0xbe, \ + 0x00, 0x80, 0x02, 0xbf, \ + 0x00, 0x80, 0x02, 0xc2, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x02, \ + 0x00, 0x80, 0x40, 0x03, \ + 0x00, 0x80, 0x40, 0x04, \ + 0x00, 0x80, 0x40, 0x06, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x09, \ + 0x00, 0x80, 0x40, 0x0a, \ + 0x00, 0x80, 0x40, 0x0b, \ + 0x00, 0x80, 0x40, 0x0c, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x0e, \ + 0x00, 0x80, 0x40, 0x0f, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x13, \ + 0x00, 0x80, 0x40, 0x14, \ + 0x00, 0x80, 0x40, 0x15, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x19, \ + 0x00, 0x80, 0x40, 0x1e, \ + 0x00, 0x80, 0x40, 0x1f, \ + 0x00, 0x80, 0x40, 0x20, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x24, \ + 0x00, 0x80, 0x40, 0x25, \ + 0x00, 0x80, 0x40, 0x3c, \ + 0x00, 0x80, 0x40, 0x3d, \ + 0x00, 0x80, 0x40, 0x3e, \ + 0x00, 0x80, 0x40, 0x3f, \ + 0x00, 0x80, 0x40, 0x40, \ + 0x00, 0x80, 0x40, 0x41, \ + 0x00, 0x80, 0x40, 0x42, \ + 0x00, 0x80, 0x40, 0x43, \ + 0x00, 0x80, 0x40, 0x44, \ + 0x00, 0x80, 0x40, 0x45, \ + 0x00, 0x80, 0x40, 0x46, \ + 0x00, 0x80, 0x40, 0x47, \ + 0x00, 0x80, 0x40, 0x48, \ + 0x00, 0x80, 0x40, 0x49, \ + 0x00, 0x80, 0x40, 0x4a, \ + 0x00, 0x80, 0x40, 0x4b, \ + 0x00, 0x80, 0x40, 0x4c, \ + 0x00, 0x80, 0x40, 0x4d, \ + 0x00, 0x80, 0x40, 0x4e, \ + 0x00, 0x80, 0x40, 0x4f, \ + 0x00, 0x80, 0x40, 0x50, \ + 0x00, 0x80, 0x40, 0x51, \ + 0x00, 0x80, 0x40, 0x52, \ + 0x00, 0x80, 0x40, 0x53, \ + 0x00, 0x80, 0x40, 0x54, \ + 0x00, 0x80, 0x40, 0x55, \ + 0x00, 0x80, 0x40, 0x56, \ + 0x00, 0x80, 0x40, 0x57, \ + 0x00, 0x80, 0x40, 0x58, \ + 0x00, 0x80, 0x40, 0x59, \ + 0x00, 0x80, 0x40, 0x5a, \ + 0x00, 0x80, 0x40, 0x5b, \ + 0x00, 0x80, 0x40, 0x5c, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x5e, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x60, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x69, \ + 0x00, 0x80, 0x40, 0x6a, \ + 0x00, 0x80, 0x40, 0x6b, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x74, \ + 0x00, 0x80, 0x40, 0x78, \ + 0x00, 0x80, 0x40, 0x79, \ + 0x00, 0x80, 0x40, 0x7a, \ + 0x00, 0x80, 0x40, 0x7c, \ + 0x00, 0x80, 0x40, 0x84, \ + 0x00, 0x80, 0x40, 0x85, \ + 0x00, 0x80, 0x40, 0x88, \ + 0x00, 0x80, 0x40, 0x89, \ + 0x00, 0x80, 0x40, 0x8e, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x90, \ + 0x00, 0x80, 0x40, 0x91, \ + 0x00, 0x80, 0x40, 0x99, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x9f, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0xa1, \ + 0x00, 0x80, 0x40, 0xa2, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0xa8, \ + 0x00, 0x80, 0x40, 0xb0, \ + 0x00, 0x80, 0x40, 0xb6, \ + 0x00, 0x80, 0x40, 0xd0, \ + 0x00, 0x80, 0x40, 0xd1, \ + 0x00, 0x80, 0x40, 0xd4, \ + 0x00, 0x80, 0x40, 0xd5, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0xdd, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0xdf, \ + 0x00, 0x80, 0x43, 0xdc, \ + 0x00, 0x80, 0x43, 0xdd, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x43, 0xe4, \ + 0x00, 0x80, 0x44, 0x04, \ + 0x00, 0x80, 0x44, 0x28, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x44, 0x48, \ + 0x00, 0x80, 0x44, 0x4c, \ + 0x00, 0x80, 0x44, 0x50, \ + 0x00, 0x80, 0x44, 0x54, \ + 0x00, 0x80, 0x44, 0x58, \ + 0x00, 0x80, 0x44, 0x5c, \ + 0x00, 0x80, 0x44, 0x60, \ + 0x00, 0x80, 0x44, 0x64, \ + 0x00, 0x80, 0x44, 0x68, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x46, 0x98, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x48, 0xc8, \ + 0x00, 0x80, 0x48, 0xc9, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x48, 0xcb, \ + 0x00, 0x80, 0x48, 0xcc, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x49, 0x1c, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x34, 0x3c, \ + 0x00, 0x80, 0x80, 0x00, \ + 0x00, 0x80, 0x80, 0x01, \ + 0x00, 0x80, 0x80, 0x02, \ + 0x00, 0x80, 0x80, 0x06, \ + 0x00, 0x80, 0x80, 0x07, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x09, \ + 0x00, 0x80, 0x80, 0x0c, \ + 0x00, 0x80, 0x80, 0x0e, \ + 0x00, 0x80, 0x80, 0x10, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x14, \ + 0x00, 0x80, 0x80, 0x18, \ + 0x00, 0x80, 0x80, 0x1c, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x2d, \ + 0x00, 0x80, 0x80, 0x2e, \ + 0x00, 0x80, 0x80, 0x31, \ + 0x00, 0x80, 0x80, 0x32, \ + 0x00, 0x80, 0x80, 0x35, \ + 0x00, 0x80, 0x80, 0x36, \ + 0x00, 0x80, 0x80, 0x37, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x39, \ + 0x00, 0x80, 0x80, 0x3a, \ + 0x00, 0x80, 0x80, 0x3b, \ + 0x00, 0x80, 0x80, 0x3c, \ + 0x00, 0x80, 0x80, 0x3d, \ + 0x00, 0x80, 0x80, 0x3e, \ + 0x00, 0x80, 0x80, 0x40, \ + 0x00, 0x80, 0x80, 0x44, \ + 0x00, 0x80, 0x80, 0x48, \ + 0x00, 0x80, 0x80, 0x49, \ + 0x00, 0x80, 0x80, 0x4a, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x4c, \ + 0x00, 0x80, 0x80, 0x50, \ + 0x00, 0x80, 0x80, 0x54, \ + 0x00, 0x80, 0x80, 0x55, \ + 0x00, 0x80, 0x80, 0x56, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x58, \ + 0x00, 0x80, 0x80, 0x5c, \ + 0x00, 0x80, 0x80, 0x60, \ + 0x00, 0x80, 0x80, 0x61, \ + 0x00, 0x80, 0x80, 0x62, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x64, \ + 0x00, 0x80, 0x80, 0x68, \ + 0x00, 0x80, 0x80, 0x6c, \ + 0x00, 0x80, 0x80, 0x6d, \ + 0x00, 0x80, 0x80, 0x6e, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x70, \ + 0x00, 0x80, 0x80, 0x74, \ + 0x00, 0x80, 0x80, 0x78, \ + 0x00, 0x80, 0x80, 0x79, \ + 0x00, 0x80, 0x80, 0x7a, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x7c, \ + 0x00, 0x80, 0x80, 0x80, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x88, \ + 0x00, 0x80, 0x80, 0x8c, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x94, \ + 0x00, 0x80, 0x80, 0x98, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x00, 0x82, \ + 0x00, 0x80, 0x00, 0x83, \ + 0x00, 0x80, 0x01, 0x01, \ + 0x00, 0x80, 0x01, 0x02, \ + 0x00, 0x80, 0x01, 0x03, \ + 0x00, 0x80, 0x01, 0x04, \ + 0x00, 0x80, 0x01, 0x05, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x01, 0x07, \ + 0x00, 0x80, 0x01, 0x0a, \ + 0x00, 0x80, 0x01, 0x0c, \ + 0x00, 0x81, 0x2f, 0xfc, \ + 0x00, 0x81, 0x2f, 0xf8, \ + 0x00, 0x00, 0x15, 0x28, \ + 0x00, 0x00, 0x15, 0x50, \ + 0x00, 0x00, 0xdd, 0x18, \ + 0x00, 0x81, 0x00, 0x04, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x14, 0xb0, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x03, 0x02, 0x01, 0x00, \ + 0x0f, 0x0e, 0x0d, 0x0c, \ + 0x0b, 0x0a, 0x09, 0x08, \ + 0x07, 0x06, 0x05, 0x04, \ + 0x03, 0x02, 0x01, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00 \ +} + +#define EWOKMZ_STXP70_TCPM_RAM_FULL_MPXPIPE2_4 { \ + 0xe0, 0x00, 0x03, 0x08, \ + 0xe0, 0x00, 0x0a, 0xc8, \ + 0xe0, 0x00, 0x05, 0x08, \ + 0xe0, 0x6c, 0x0a, 0xe8, \ + 0xe0, 0x00, 0x0a, 0x88, \ + 0xe0, 0x00, 0x0a, 0x88, \ + 0xe0, 0x00, 0x0a, 0x88, \ + 0xe0, 0x00, 0x0a, 0x88, \ + 0xe0, 0x6c, 0x29, 0x08, \ + 0xe0, 0x6c, 0x2b, 0x88, \ + 0xe0, 0x6c, 0x2f, 0x28, \ + 0xe0, 0x6c, 0x33, 0xc8, \ + 0xe0, 0x6c, 0x3b, 0xa8, \ + 0xe0, 0x70, 0x08, 0x48, \ + 0xe0, 0x70, 0x0c, 0x28, \ + 0xe0, 0x70, 0x0e, 0xa8, \ + 0xe0, 0x70, 0x14, 0xe8, \ + 0xe0, 0x70, 0x16, 0xe8, \ + 0xe0, 0x00, 0x09, 0xe8, \ + 0xe0, 0x00, 0x09, 0xc8, \ + 0xe0, 0x00, 0x09, 0xa8, \ + 0xe0, 0x00, 0x09, 0x88, \ + 0xe0, 0x00, 0x09, 0x68, \ + 0xe0, 0x00, 0x09, 0x48, \ + 0xf8, 0x02, 0x00, 0x06, \ + 0xc1, 0xc3, 0x81, 0x01, \ + 0x1e, 0x06, 0xa1, 0x0b, \ + 0x1a, 0x06, 0x81, 0x00, \ + 0x99, 0x23, 0xe1, 0x00, \ + 0x97, 0xb3, 0x97, 0xe3, \ + 0x06, 0x2c, 0x97, 0x73, \ + 0x06, 0x34, 0xe2, 0xc0, \ + 0x06, 0x2c, 0xfc, 0x80, \ + 0xc1, 0x83, 0xe3, 0x40, \ + 0xe2, 0xc0, 0x06, 0x1c, \ + 0xfc, 0x98, 0x06, 0x34, \ + 0xfc, 0x9c, 0x06, 0x34, \ + 0xe3, 0x40, 0x06, 0x1c, \ + 0x69, 0x38, 0xc1, 0x83, \ + 0xc1, 0x43, 0xe8, 0x00, \ + 0xc1, 0xc3, 0x81, 0x05, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0xf4, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xff, 0x84, 0x07, 0xfc, \ + 0x9a, 0x14, 0xd8, 0x04, \ + 0xe4, 0x30, 0x04, 0x60, \ + 0xfc, 0x12, 0x4c, 0x06, \ + 0xe0, 0xd0, 0x4c, 0x4a, \ + 0xc1, 0xc3, 0x82, 0x15, \ + 0xc1, 0xc3, 0xc1, 0xc3, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0x81, 0x93, 0x81, 0x83, \ + 0xe2, 0xc0, 0x0c, 0x2c, \ + 0x1c, 0x8c, 0xd1, 0x67, \ + 0xe3, 0x6b, 0xe4, 0x04, \ + 0xe5, 0x6b, 0x8e, 0x05, \ + 0x80, 0x6c, 0x06, 0x08, \ + 0x07, 0x38, 0xe7, 0x6b, \ + 0xe9, 0x6b, 0x80, 0x6c, \ + 0xeb, 0x6b, 0x8a, 0xc5, \ + 0x80, 0x6c, 0x08, 0x48, \ + 0x0c, 0x38, 0xed, 0x6b, \ + 0xf1, 0x6b, 0x80, 0x6c, \ + 0x80, 0x6c, 0x09, 0x48, \ + 0x0a, 0x78, 0xf3, 0x6b, \ + 0xf5, 0x6b, 0x80, 0x6c, \ + 0xf7, 0x6b, 0x8a, 0xc5, \ + 0xf9, 0x6b, 0x8c, 0x05, \ + 0xfb, 0x6b, 0x8c, 0x45, \ + 0x0c, 0x0a, 0x8a, 0x45, \ + 0x8a, 0x65, 0xe4, 0xb4, \ + 0xe4, 0xb8, 0x0c, 0x0a, \ + 0x0c, 0x38, 0x84, 0x45, \ + 0x40, 0x06, 0xe0, 0x6c, \ + 0x40, 0x4a, 0xfc, 0x12, \ + 0x80, 0xd5, 0xe0, 0xd0, \ + 0xc1, 0xc3, 0xc1, 0xc3, \ + 0x1f, 0x0c, 0xc1, 0xc3, \ + 0x0f, 0xfc, 0xf0, 0x04, \ + 0xbc, 0xf4, 0xf7, 0x84, \ + 0xc1, 0x53, 0xfd, 0x64, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xe0, 0x80, 0x00, 0x0c, \ + 0xc1, 0xc3, 0xc1, 0x43, \ + 0xc1, 0xc3, 0xc1, 0x53, \ + 0xfa, 0xfc, 0x04, 0x0c, \ + 0x60, 0x00, 0x06, 0xda, \ + 0x0a, 0x20, 0xa1, 0x0b, \ + 0x18, 0x30, 0x64, 0x00, \ + 0x0e, 0x24, 0xe4, 0x00, \ + 0x02, 0x0a, 0x37, 0x80, \ + 0x0c, 0x06, 0x80, 0x00, \ + 0x08, 0x34, 0x20, 0x00, \ + 0x0a, 0x70, 0xb7, 0x80, \ + 0x58, 0x42, 0xa4, 0x04, \ + 0x81, 0x61, 0x32, 0x98, \ + 0x21, 0x2b, 0xaa, 0x45, \ + 0x06, 0x0a, 0x8c, 0xbb, \ + 0x0c, 0x10, 0x00, 0x00, \ + 0x04, 0x00, 0xe4, 0x00, \ + 0x18, 0x1a, 0x64, 0x00, \ + 0xa8, 0x25, 0xe0, 0x00, \ + 0x08, 0x04, 0xa1, 0x1d, \ + 0x0e, 0x14, 0x57, 0x80, \ + 0x10, 0x06, 0xd7, 0x80, \ + 0x04, 0x40, 0x40, 0x00, \ + 0x08, 0x54, 0xc4, 0x04, \ + 0x4c, 0x72, 0x76, 0x80, \ + 0x10, 0xc4, 0xd2, 0xa0, \ + 0x14, 0x40, 0x76, 0x80, \ + 0x0e, 0x64, 0xa4, 0x80, \ + 0x12, 0x24, 0xf6, 0x80, \ + 0xa1, 0x6b, 0x76, 0x80, \ + 0x3c, 0x01, 0x54, 0x80, \ + 0x8c, 0x8b, 0x40, 0x00, \ + 0x04, 0x80, 0x08, 0x90, \ + 0xdc, 0x00, 0x48, 0x70, \ + 0x84, 0x9b, 0x28, 0xa3, \ + 0xec, 0x04, 0x0e, 0xa0, \ + 0x9e, 0xe5, 0xa0, 0x7d, \ + 0x96, 0x4b, 0x21, 0x7b, \ + 0x0e, 0x8a, 0x86, 0x45, \ + 0x0e, 0x9a, 0xe5, 0x00, \ + 0x00, 0x06, 0x44, 0x80, \ + 0x16, 0xa0, 0x80, 0x00, \ + 0x08, 0x54, 0x2c, 0x84, \ + 0xc0, 0x00, 0x28, 0x9c, \ + 0x20, 0x01, 0x0e, 0x4a, \ + 0x00, 0x54, 0x40, 0x00, \ + 0x00, 0xc4, 0xa8, 0xac, \ + 0xc0, 0x00, 0x28, 0x9d, \ + 0x0f, 0x9c, 0x14, 0x54, \ + 0x40, 0x02, 0x88, 0x5b, \ + 0x80, 0xcb, 0x99, 0xa8, \ + 0x61, 0x84, 0x00, 0x84, \ + 0xe1, 0x84, 0x08, 0x94, \ + 0x12, 0x84, 0x01, 0xcb, \ + 0x32, 0x4f, 0xe0, 0xfc, \ + 0xe4, 0x04, 0x1c, 0x70, \ + 0x4b, 0x0a, 0x01, 0xa1, \ + 0x58, 0xda, 0x84, 0x10, \ + 0x94, 0xbb, 0x64, 0x00, \ + 0x6d, 0x84, 0x40, 0x1a, \ + 0x1c, 0x70, 0xc6, 0x1f, \ + 0x00, 0xc4, 0x24, 0x08, \ + 0x0e, 0x54, 0xa0, 0x84, \ + 0x08, 0x54, 0x21, 0xfc, \ + 0x40, 0x02, 0xa0, 0x84, \ + 0xa1, 0xeb, 0x39, 0x9c, \ + 0x3c, 0x00, 0x58, 0x00, \ + 0xbc, 0x00, 0x4a, 0x40, \ + 0x1d, 0x5a, 0x08, 0xc5, \ + 0x0e, 0xe0, 0xe0, 0x04, \ + 0x0d, 0xcb, 0xec, 0x04, \ + 0xe1, 0xfc, 0x00, 0xa4, \ + 0x4b, 0x0a, 0x42, 0xb7, \ + 0x58, 0xfa, 0x84, 0x08, \ + 0x08, 0xc4, 0x64, 0x18, \ + 0x10, 0xa4, 0xe0, 0xfc, \ + 0x40, 0x0a, 0x60, 0x84, \ + 0x20, 0xbf, 0xed, 0x8c, \ + 0x00, 0x54, 0x90, 0xab, \ + 0x14, 0x84, 0x61, 0x84, \ + 0x28, 0x0f, 0x9c, 0x80, \ + 0x91, 0x94, 0x48, 0x22, \ + 0x13, 0xb0, 0x4a, 0x62, \ + 0x4c, 0x50, 0xc3, 0xc7, \ + 0x80, 0x5b, 0x1c, 0x00, \ + 0x27, 0xf0, 0x4e, 0xd8, \ + 0x9c, 0x00, 0x44, 0x40, \ + 0x96, 0x0b, 0x21, 0x1d, \ + 0x57, 0x80, 0x04, 0xa4, \ + 0xd7, 0x80, 0x02, 0xb4, \ + 0x40, 0x00, 0x06, 0x06, \ + 0xc4, 0x04, 0x04, 0x20, \ + 0x52, 0x8c, 0x42, 0x12, \ + 0x40, 0x10, 0x94, 0x6b, \ + 0x4c, 0x20, 0x5c, 0x00, \ + 0x80, 0x1b, 0xdc, 0x00, \ + 0x0c, 0x0c, 0x8c, 0x0b, \ + 0xdd, 0x93, 0xf2, 0xfc, \ + 0x8c, 0x1b, 0x7f, 0xc5, \ + 0x80, 0x6b, 0x7f, 0xa5, \ + 0x96, 0x7b, 0x73, 0xe5, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x81, 0xc1, 0x21, 0x0b, \ + 0x00, 0x00, 0x02, 0x0a, \ + 0xa0, 0x85, 0x98, 0x6b, \ + 0x76, 0x80, 0x0a, 0x14, \ + 0x08, 0x24, 0xa1, 0x2b, \ + 0x06, 0x0a, 0x76, 0x80, \ + 0x0c, 0x04, 0x80, 0x00, \ + 0x98, 0x8b, 0x76, 0x80, \ + 0x06, 0x1a, 0x1e, 0xc5, \ + 0x18, 0x34, 0xe0, 0x00, \ + 0xa1, 0x1b, 0x76, 0x80, \ + 0xa4, 0x80, 0x0e, 0x40, \ + 0x3c, 0x01, 0x4e, 0xc0, \ + 0x08, 0x60, 0xc0, 0x00, \ + 0x48, 0x50, 0x04, 0x80, \ + 0xa8, 0x73, 0xdc, 0x00, \ + 0xec, 0x04, 0x0a, 0x70, \ + 0x9a, 0xc5, 0xa0, 0x5d, \ + 0x90, 0xcb, 0x21, 0x5b, \ + 0x0a, 0x8a, 0x86, 0x45, \ + 0x0a, 0x9a, 0xe5, 0x00, \ + 0x08, 0x06, 0x44, 0x80, \ + 0x10, 0x70, 0x80, 0x00, \ + 0x0a, 0x4a, 0x2c, 0x84, \ + 0xc0, 0x00, 0x20, 0x00, \ + 0x28, 0x95, 0x08, 0x34, \ + 0x0c, 0x24, 0x40, 0x00, \ + 0x08, 0x24, 0x8f, 0x94, \ + 0x18, 0x24, 0x28, 0xa0, \ + 0xc0, 0x00, 0x28, 0x94, \ + 0x19, 0x98, 0x48, 0x42, \ + 0x88, 0x3b, 0x98, 0x2b, \ + 0x61, 0x84, 0x08, 0x14, \ + 0xe0, 0xfc, 0x0c, 0x14, \ + 0x61, 0x84, 0x18, 0x04, \ + 0x2c, 0xcf, 0x89, 0x3b, \ + 0xe4, 0x10, 0x46, 0xda, \ + 0x04, 0x30, 0x45, 0x0a, \ + 0xe4, 0x04, 0x12, 0x50, \ + 0x6d, 0x84, 0x40, 0x1a, \ + 0x12, 0x50, 0x81, 0xc1, \ + 0x0c, 0x24, 0x24, 0x08, \ + 0x08, 0x34, 0xa1, 0xfc, \ + 0x0a, 0x24, 0x20, 0x84, \ + 0x48, 0x42, 0xa0, 0x84, \ + 0xa1, 0x9b, 0x39, 0x98, \ + 0x3c, 0x00, 0x46, 0x40, \ + 0xbc, 0x00, 0x44, 0x50, \ + 0x13, 0x5a, 0x18, 0x6b, \ + 0x88, 0xc5, 0xe0, 0x04, \ + 0xec, 0x04, 0x0e, 0x90, \ + 0x46, 0xfa, 0x03, 0x3b, \ + 0x45, 0x0a, 0xe4, 0x04, \ + 0x0a, 0x24, 0x04, 0x00, \ + 0x10, 0x34, 0xe1, 0x84, \ + 0xc2, 0x67, 0x60, 0xfc, \ + 0x60, 0x84, 0x1c, 0xc4, \ + 0xed, 0x8c, 0x40, 0x0a, \ + 0x61, 0x84, 0x08, 0x34, \ + 0x10, 0xc4, 0xb0, 0x5f, \ + 0x9c, 0xcb, 0x61, 0xfc, \ + 0x50, 0x02, 0x30, 0x6f, \ + 0x44, 0x12, 0x91, 0x88, \ + 0x88, 0x3b, 0x13, 0x8c, \ + 0x1c, 0x00, 0x42, 0x20, \ + 0x40, 0x80, 0x8a, 0x2b, \ + 0x18, 0xe4, 0x1c, 0x00, \ + 0x4f, 0xd8, 0x9c, 0x80, \ + 0x18, 0x0b, 0xa7, 0xec, \ + 0x0c, 0x0c, 0x8c, 0x1b, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0x82, 0x6b, 0x00, 0xcb, \ + 0x75, 0xf5, 0xff, 0x95, \ + 0xc1, 0xc3, 0x90, 0x5b, \ + 0xfa, 0xfc, 0x04, 0x0c, \ + 0x80, 0x8b, 0x21, 0x0b, \ + 0x00, 0x00, 0x02, 0x0a, \ + 0x04, 0x6b, 0x82, 0x9b, \ + 0x80, 0xe5, 0x86, 0x7b, \ + 0x06, 0x0a, 0xa1, 0x2b, \ + 0x40, 0x0a, 0x80, 0x00, \ + 0x80, 0xe5, 0xe5, 0x9c, \ + 0x84, 0x1b, 0x81, 0x21, \ + 0x0c, 0x0c, 0x84, 0x0b, \ + 0xdd, 0x93, 0xf2, 0xfc, \ + 0x68, 0x00, 0x41, 0x88, \ + 0x00, 0xbb, 0x84, 0x1b, \ + 0x02, 0xab, 0x90, 0x0b, \ + 0x41, 0x18, 0x8e, 0x1b, \ + 0x00, 0x7b, 0xe8, 0x00, \ + 0x40, 0xd8, 0x92, 0x0b, \ + 0x8c, 0x1b, 0x68, 0x00, \ + 0x44, 0xb2, 0x81, 0x31, \ + 0x42, 0xa2, 0xf0, 0x8c, \ + 0x86, 0x23, 0xf2, 0x9c, \ + 0x42, 0x12, 0x7d, 0x85, \ + 0xc1, 0xc3, 0xf2, 0x80, \ + 0xe6, 0x86, 0x4a, 0x0e, \ + 0xe7, 0x06, 0x58, 0x0e, \ + 0x66, 0x06, 0x46, 0x0e, \ + 0xe1, 0xc0, 0x08, 0x54, \ + 0x67, 0x86, 0x44, 0x0e, \ + 0xe0, 0xc0, 0x00, 0xc4, \ + 0x02, 0xc4, 0x60, 0x57, \ + 0x8a, 0x03, 0xe1, 0xc0, \ + 0xf2, 0x90, 0x42, 0x12, \ + 0x5d, 0x93, 0x86, 0x03, \ + 0xf2, 0x88, 0x42, 0x12, \ + 0xe5, 0x00, 0x04, 0xca, \ + 0xc1, 0x7c, 0x02, 0x14, \ + 0xdc, 0x00, 0x40, 0x10, \ + 0x84, 0x80, 0x04, 0xca, \ + 0xcc, 0x80, 0x04, 0x20, \ + 0xc9, 0x08, 0x00, 0x14, \ + 0xc1, 0x7c, 0x02, 0x14, \ + 0x80, 0x00, 0x04, 0x5a, \ + 0xad, 0x88, 0x06, 0x14, \ + 0x20, 0x01, 0x06, 0x06, \ + 0x00, 0x04, 0xc0, 0x00, \ + 0x02, 0x14, 0x89, 0x88, \ + 0x40, 0x02, 0x89, 0x08, \ + 0xdd, 0x93, 0x99, 0x8c, \ + 0xe5, 0x00, 0x04, 0xca, \ + 0xc0, 0x00, 0x02, 0x06, \ + 0xc0, 0x00, 0x00, 0x06, \ + 0x84, 0x80, 0x04, 0xca, \ + 0xcc, 0x80, 0x04, 0x20, \ + 0xc9, 0x88, 0x00, 0x14, \ + 0xc0, 0x00, 0x02, 0x06, \ + 0x80, 0x00, 0x04, 0x5a, \ + 0xad, 0x88, 0x06, 0x14, \ + 0x20, 0x01, 0x06, 0x06, \ + 0x00, 0x04, 0xc0, 0x00, \ + 0x02, 0x14, 0x89, 0x88, \ + 0x40, 0x02, 0x89, 0x88, \ + 0xdd, 0x93, 0x99, 0x8c, \ + 0x81, 0x41, 0x61, 0x0b, \ + 0x06, 0x14, 0x8a, 0x25, \ + 0xe1, 0x1b, 0x76, 0x80, \ + 0x76, 0x80, 0x04, 0x04, \ + 0x08, 0x85, 0x81, 0x41, \ + 0x06, 0x30, 0xa4, 0x33, \ + 0x04, 0x04, 0x6c, 0x04, \ + 0xa0, 0x3d, 0xe1, 0x84, \ + 0xa1, 0x3b, 0x86, 0xe5, \ + 0xc8, 0x8c, 0x02, 0x14, \ + 0x64, 0x04, 0x18, 0x30, \ + 0xe4, 0x08, 0x43, 0x1a, \ + 0x24, 0x08, 0x18, 0x30, \ + 0xa0, 0x84, 0x02, 0x14, \ + 0x0a, 0xc0, 0x21, 0xcb, \ + 0x19, 0x5a, 0xec, 0x04, \ + 0x82, 0x45, 0xe0, 0x04, \ + 0xc2, 0x47, 0x40, 0x1d, \ + 0x1c, 0x80, 0x08, 0x44, \ + 0x91, 0x84, 0x40, 0x02, \ + 0x27, 0xfc, 0x4a, 0x98, \ + 0x5d, 0x93, 0xc3, 0x17, \ + 0x7f, 0xe5, 0x88, 0x0b, \ + 0x7d, 0x55, 0x80, 0x4b, \ + 0xc1, 0xc3, 0x88, 0x3b, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x81, 0x11, 0x00, 0x6b, \ + 0xe8, 0x04, 0x50, 0x48, \ + 0xe1, 0x98, 0x41, 0x86, \ + 0x00, 0x15, 0x80, 0x04, \ + 0xa1, 0x1b, 0xe0, 0x3c, \ + 0xdc, 0x38, 0x42, 0x08, \ + 0x68, 0x04, 0x57, 0xc8, \ + 0xc1, 0xc3, 0x8c, 0x0b, \ + 0xfa, 0x04, 0x04, 0x7c, \ + 0x74, 0xb1, 0x47, 0x4e, \ + 0x08, 0x0e, 0xc0, 0x10, \ + 0xc0, 0x10, 0x74, 0xb5, \ + 0x88, 0x25, 0x89, 0x3d, \ + 0xcd, 0x14, 0xcd, 0x04, \ + 0x05, 0x0a, 0xcd, 0x24, \ + 0x4a, 0x00, 0xe0, 0x80, \ + 0x84, 0x25, 0xf0, 0x0c, \ + 0xe4, 0x00, 0x0a, 0x3a, \ + 0x60, 0x01, 0x00, 0x98, \ + 0x04, 0x20, 0x80, 0x00, \ + 0xc1, 0x16, 0xec, 0x04, \ + 0xf8, 0x0c, 0x00, 0x4a, \ + 0x04, 0x20, 0xfe, 0x65, \ + 0xe9, 0x2d, 0xec, 0x10, \ + 0xfe, 0x85, 0xc1, 0x1e, \ + 0xe0, 0x04, 0x04, 0xca, \ + 0x80, 0x00, 0x45, 0x48, \ + 0x45, 0xc8, 0xc1, 0x16, \ + 0xfb, 0xf5, 0x87, 0xfc, \ + 0xe1, 0x94, 0x00, 0x86, \ + 0x61, 0x91, 0x03, 0x06, \ + 0x80, 0x1e, 0xc0, 0x00, \ + 0x81, 0x11, 0x9b, 0x01, \ + 0x81, 0x31, 0x81, 0x21, \ + 0xe8, 0x04, 0x4a, 0x88, \ + 0x8c, 0x45, 0xa1, 0x0b, \ + 0x74, 0xb1, 0x07, 0xce, \ + 0x49, 0x4e, 0xc0, 0x10, \ + 0xc0, 0x10, 0x74, 0xb1, \ + 0x8a, 0x45, 0x89, 0x3d, \ + 0xcd, 0x14, 0xcd, 0x04, \ + 0x05, 0x0a, 0xcd, 0x24, \ + 0x4a, 0x00, 0xe0, 0x80, \ + 0x86, 0x25, 0xf0, 0x0c, \ + 0xf0, 0x0c, 0x4c, 0x10, \ + 0xe4, 0x18, 0x4a, 0x5a, \ + 0xe4, 0x00, 0x0a, 0x3a, \ + 0xa0, 0x00, 0x01, 0x28, \ + 0x60, 0x01, 0x00, 0xa8, \ + 0xc5, 0x50, 0x80, 0x00, \ + 0xec, 0x04, 0x04, 0x20, \ + 0x00, 0x4a, 0xc1, 0x56, \ + 0xfe, 0x45, 0xf8, 0x0c, \ + 0x04, 0x20, 0xc5, 0x54, \ + 0xe9, 0x2d, 0xec, 0x10, \ + 0xfe, 0x65, 0xc1, 0x5e, \ + 0xe0, 0x04, 0x04, 0xca, \ + 0x80, 0x00, 0x45, 0x88, \ + 0xc1, 0x56, 0xc5, 0x50, \ + 0x87, 0xfc, 0x45, 0x88, \ + 0x07, 0x86, 0xfb, 0x65, \ + 0x08, 0xf0, 0xe1, 0x90, \ + 0x8c, 0x4e, 0xe4, 0x30, \ + 0xe1, 0x9c, 0x0d, 0x46, \ + 0xe1, 0x9c, 0x0f, 0x46, \ + 0x82, 0x45, 0x8f, 0x6d, \ + 0x02, 0x3a, 0xd9, 0x14, \ + 0x00, 0x68, 0xe4, 0x00, \ + 0x80, 0x00, 0x60, 0x01, \ + 0xfc, 0x38, 0x42, 0x08, \ + 0x81, 0x61, 0xff, 0x65, \ + 0x01, 0x46, 0xe1, 0x6b, \ + 0x4c, 0x08, 0xe1, 0x9c, \ + 0x8b, 0x04, 0xdc, 0x38, \ + 0xfe, 0x24, 0x89, 0x14, \ + 0xe8, 0x70, 0x68, 0xc8, \ + 0xe1, 0x9c, 0x0d, 0x46, \ + 0xe1, 0x9c, 0x0f, 0x46, \ + 0x82, 0x45, 0x8f, 0x6d, \ + 0x02, 0x3a, 0xd9, 0x14, \ + 0x00, 0x68, 0xe4, 0x00, \ + 0x80, 0x00, 0x60, 0x01, \ + 0xfc, 0x38, 0x42, 0x08, \ + 0x75, 0x78, 0xff, 0x65, \ + 0x0c, 0x7c, 0xef, 0xfc, \ + 0xdd, 0x93, 0xf2, 0x04, \ + 0x80, 0x55, 0xa1, 0x1b, \ + 0x0a, 0x00, 0x83, 0x11, \ + 0x40, 0x06, 0xe5, 0x50, \ + 0x40, 0x4a, 0xfc, 0x12, \ + 0xd7, 0xf4, 0xe0, 0xd0, \ + 0xc1, 0xc3, 0x82, 0xb5, \ + 0xc1, 0xc3, 0xc1, 0xc3, \ + 0xf4, 0x04, 0x0b, 0x3f, \ + 0xf4, 0x04, 0x0b, 0x2f, \ + 0xf4, 0x04, 0x0b, 0x1f, \ + 0xf4, 0x04, 0x0b, 0x0f, \ + 0x0a, 0x50, 0x81, 0x35, \ + 0x40, 0x5a, 0xec, 0x40, \ + 0x44, 0x06, 0xe0, 0xbc, \ + 0x44, 0x6a, 0xbc, 0x22, \ + 0x40, 0x4a, 0xa0, 0x90, \ + 0xc0, 0x00, 0x20, 0x89, \ + 0xed, 0x88, 0x40, 0x1a, \ + 0x20, 0x01, 0x01, 0x98, \ + 0x0a, 0xff, 0xc0, 0x00, \ + 0xc1, 0xc3, 0xf4, 0x04, \ + 0xc1, 0xc3, 0xc1, 0xc3, \ + 0xf4, 0x04, 0x0b, 0xf5, \ + 0xf4, 0x04, 0x0b, 0xe5, \ + 0xf4, 0x04, 0x0b, 0xd5, \ + 0xf4, 0x04, 0x0b, 0xc5, \ + 0xf4, 0x04, 0x0b, 0xb5, \ + 0xf4, 0x04, 0x0b, 0xa5, \ + 0xf4, 0x04, 0x0b, 0x95, \ + 0xf4, 0x04, 0x0b, 0x85, \ + 0x0a, 0xff, 0x81, 0x55, \ + 0x0a, 0x50, 0xf4, 0x04, \ + 0xd7, 0xe4, 0xec, 0x80, \ + 0xd7, 0xb4, 0xd7, 0xd4, \ + 0xd7, 0x94, 0xd7, 0xa4, \ + 0xd7, 0x74, 0xd7, 0x84, \ + 0x82, 0x0b, 0x94, 0x64, \ + 0xfa, 0x00, 0x5c, 0x08, \ + 0xf8, 0x3c, 0x04, 0x4c, \ + 0xe4, 0x40, 0x04, 0x8a, \ + 0x21, 0x2b, 0x80, 0xa5, \ + 0x82, 0x15, 0x80, 0x3b, \ + 0x46, 0x12, 0x81, 0xf5, \ + 0x06, 0x4a, 0xf9, 0x80, \ + 0x82, 0xb5, 0xf8, 0x0c, \ + 0x04, 0x20, 0x80, 0x3b, \ + 0x04, 0x4a, 0xec, 0x04, \ + 0xc5, 0x48, 0xe4, 0x00, \ + 0x45, 0x88, 0xcd, 0x46, \ + 0x0c, 0x4c, 0x87, 0xfc, \ + 0xdd, 0x93, 0xf0, 0x3c, \ + 0x6c, 0x40, 0x0a, 0x20, \ + 0x49, 0x27, 0xe1, 0xc1, \ + 0x04, 0x20, 0x8a, 0xcf, \ + 0x06, 0xc0, 0x6c, 0x04, \ + 0x04, 0x4a, 0xe4, 0x40, \ + 0x48, 0x12, 0x64, 0x00, \ + 0x00, 0x33, 0xf0, 0x8c, \ + 0x02, 0x75, 0x80, 0x6b, \ + 0x02, 0x85, 0xf0, 0x10, \ + 0x02, 0x95, 0xf8, 0x0c, \ + 0x02, 0xa5, 0xf8, 0x08, \ + 0x4c, 0x75, 0xf8, 0x04, \ + 0x4c, 0x85, 0xf0, 0x10, \ + 0x4c, 0x95, 0xf8, 0x0c, \ + 0x4c, 0xa5, 0xf8, 0x08, \ + 0x44, 0x08, 0xf8, 0x04, \ + 0x44, 0x52, 0x87, 0xf8, \ + 0xe9, 0x2d, 0xf1, 0xb0, \ + 0x0a, 0x20, 0x86, 0x95, \ + 0xc5, 0x27, 0x6c, 0x10, \ + 0x61, 0x88, 0x0e, 0x54, \ + 0xec, 0x04, 0x0c, 0x20, \ + 0x64, 0x04, 0x18, 0x70, \ + 0xe4, 0x00, 0x0c, 0x4a, \ + 0x04, 0x74, 0x44, 0xc7, \ + 0x42, 0x42, 0xe0, 0x88, \ + 0x86, 0x7b, 0x70, 0xb0, \ + 0xdd, 0x8e, 0xd1, 0x84, \ + 0x87, 0xfc, 0x4d, 0x88, \ + 0x71, 0x88, 0x44, 0x52, \ + 0xa1, 0x2b, 0x98, 0x33, \ + 0xf7, 0xe5, 0xec, 0xf5, \ + 0x88, 0x1b, 0x21, 0x2b, \ + 0xf7, 0xa5, 0xec, 0x75, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xf8, 0x04, 0x04, 0x0c, \ + 0x78, 0x0c, 0x00, 0x0a, \ + 0x82, 0x25, 0x80, 0x3b, \ + 0x94, 0xa5, 0xa1, 0x2b, \ + 0x04, 0x20, 0x4d, 0x16, \ + 0x06, 0x4a, 0xec, 0x04, \ + 0xfe, 0x25, 0xf8, 0x0c, \ + 0x90, 0x95, 0xe9, 0x2d, \ + 0x7a, 0x80, 0x08, 0x14, \ + 0xe4, 0x40, 0x04, 0x8a, \ + 0xe0, 0xa0, 0x0a, 0x44, \ + 0x08, 0x54, 0xa8, 0x5f, \ + 0x06, 0xf5, 0xe0, 0xc0, \ + 0x0c, 0x24, 0xaa, 0x4f, \ + 0x0a, 0x20, 0x61, 0x90, \ + 0x61, 0xc1, 0xec, 0x40, \ + 0xec, 0x04, 0x0c, 0x60, \ + 0x0c, 0x4a, 0x0a, 0xcf, \ + 0x4e, 0x32, 0xe4, 0x00, \ + 0x86, 0x2b, 0x70, 0xb0, \ + 0xe4, 0x40, 0x06, 0x70, \ + 0xf0, 0x10, 0x44, 0x45, \ + 0xf8, 0x0c, 0x44, 0x45, \ + 0xf8, 0x08, 0x44, 0x45, \ + 0xf8, 0x04, 0x44, 0x45, \ + 0x87, 0xfc, 0x4c, 0x08, \ + 0xf1, 0xb0, 0x44, 0x52, \ + 0x86, 0x95, 0xe9, 0x2d, \ + 0xf9, 0xc1, 0x07, 0x51, \ + 0x4a, 0x52, 0xa4, 0x53, \ + 0x04, 0x53, 0xfa, 0xb0, \ + 0xec, 0x10, 0x04, 0x20, \ + 0x0e, 0x24, 0x08, 0x51, \ + 0x45, 0x57, 0xe1, 0x88, \ + 0xe4, 0x04, 0x18, 0x70, \ + 0x6c, 0x04, 0x0c, 0x50, \ + 0xe0, 0x88, 0x0a, 0x74, \ + 0x64, 0x00, 0x0c, 0x4a, \ + 0xdd, 0x4e, 0x86, 0x7b, \ + 0x87, 0xfc, 0x4d, 0xc8, \ + 0xaa, 0x23, 0x44, 0xc7, \ + 0xa1, 0x2b, 0x98, 0x33, \ + 0x04, 0x20, 0x82, 0x05, \ + 0x04, 0x4a, 0xec, 0x04, \ + 0xcd, 0x16, 0xe4, 0x00, \ + 0x87, 0xfc, 0x45, 0xc8, \ + 0xf0, 0x04, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0x04, 0x4a, 0x00, 0x7b, \ + 0x40, 0x72, 0xe0, 0x10, \ + 0x1e, 0xf0, 0x79, 0x88, \ + 0x00, 0x0c, 0xed, 0x20, \ + 0x00, 0x1a, 0x62, 0x00, \ + 0x11, 0x0e, 0xf8, 0x0c, \ + 0x1b, 0x0e, 0x82, 0x8b, \ + 0x20, 0x01, 0x00, 0x26, \ + 0x5e, 0x05, 0xc0, 0x00, \ + 0x40, 0x00, 0x20, 0x35, \ + 0x23, 0x3e, 0xef, 0x8d, \ + 0x5e, 0x17, 0xa4, 0x0b, \ + 0x9d, 0x2e, 0xe0, 0xfc, \ + 0x23, 0x88, 0xa1, 0x0e, \ + 0x00, 0x24, 0xc0, 0x00, \ + 0xc4, 0x1b, 0x61, 0x88, \ + 0xc0, 0x3b, 0x7d, 0x41, \ + 0x7a, 0x10, 0x42, 0x12, \ + 0xfa, 0x10, 0x46, 0x32, \ + 0x84, 0x13, 0x00, 0x33, \ + 0x82, 0x11, 0x3e, 0x1e, \ + 0x82, 0x31, 0x7c, 0x3e, \ + 0x00, 0x20, 0x19, 0x1e, \ + 0x97, 0x3e, 0xec, 0x04, \ + 0x00, 0x84, 0xfe, 0x0e, \ + 0xaf, 0x8b, 0x61, 0x84, \ + 0x54, 0x0a, 0x15, 0x8e, \ + 0x2e, 0xa5, 0xe6, 0x8a, \ + 0x00, 0x80, 0x8e, 0xa3, \ + 0x10, 0x8a, 0x6c, 0x04, \ + 0x40, 0x0a, 0xe4, 0xa4, \ + 0x56, 0x72, 0xe6, 0x8a, \ + 0x40, 0x70, 0x70, 0x80, \ + 0xa2, 0xb5, 0xdc, 0x00, \ + 0x00, 0x84, 0x0f, 0xbe, \ + 0x23, 0xb4, 0xe1, 0x8c, \ + 0xe6, 0x8a, 0x4c, 0x0a, \ + 0x60, 0x84, 0x00, 0x64, \ + 0xf0, 0x98, 0x50, 0x72, \ + 0x8e, 0x03, 0x0d, 0x0e, \ + 0x8e, 0x0b, 0x0b, 0x0e, \ + 0x56, 0x08, 0x90, 0x1b, \ + 0x13, 0x8e, 0xfc, 0x38, \ + 0x0b, 0x84, 0x80, 0x9b, \ + 0x90, 0x1b, 0x90, 0x0b, \ + 0xfc, 0x38, 0x56, 0x08, \ + 0x82, 0x9d, 0xff, 0x11, \ + 0xa0, 0x0d, 0x82, 0x85, \ + 0x0e, 0x0b, 0x84, 0xd5, \ + 0xa3, 0x24, 0x90, 0x1b, \ + 0xfc, 0x38, 0x44, 0x08, \ + 0x8e, 0x0b, 0x21, 0x0d, \ + 0x81, 0xc5, 0x82, 0x85, \ + 0x82, 0x75, 0xa1, 0x0d, \ + 0x90, 0x1b, 0x0e, 0x0b, \ + 0x44, 0x08, 0xa3, 0x24, \ + 0x21, 0x0d, 0xfc, 0x38, \ + 0x80, 0x45, 0x8e, 0x0b, \ + 0x93, 0x0e, 0x90, 0x0b, \ + 0x50, 0xa2, 0x2c, 0x0b, \ + 0x23, 0x64, 0xf1, 0x98, \ + 0xf0, 0x98, 0x56, 0xa2, \ + 0x90, 0x0b, 0x0b, 0x0e, \ + 0x4c, 0x08, 0x94, 0x1b, \ + 0x00, 0x9b, 0xfc, 0x38, \ + 0x09, 0xbe, 0x94, 0x0b, \ + 0x4c, 0x08, 0x96, 0x1b, \ + 0xff, 0x11, 0xfc, 0x38, \ + 0x82, 0xa5, 0x82, 0x9d, \ + 0x84, 0xd5, 0xa0, 0x0d, \ + 0x90, 0x0b, 0x09, 0x64, \ + 0x8c, 0x1b, 0xa3, 0x24, \ + 0xfc, 0x38, 0x44, 0x08, \ + 0x01, 0xd5, 0xa1, 0x0d, \ + 0x9c, 0x00, 0x50, 0x60, \ + 0x82, 0x55, 0xa1, 0x0d, \ + 0x90, 0x0b, 0x09, 0xa4, \ + 0x94, 0x1b, 0xa3, 0x24, \ + 0xfc, 0x38, 0x44, 0x08, \ + 0x80, 0x55, 0xa1, 0x0d, \ + 0x8f, 0xb4, 0x90, 0xab, \ + 0x8d, 0x14, 0x8b, 0x94, \ + 0x96, 0x93, 0xa3, 0x64, \ + 0x71, 0x84, 0x40, 0xb2, \ + 0x8d, 0x0e, 0x92, 0x1b, \ + 0xfc, 0x38, 0x4c, 0x08, \ + 0x92, 0x0b, 0x00, 0x8b, \ + 0x4c, 0x08, 0x96, 0x1b, \ + 0xff, 0x11, 0xfc, 0x38, \ + 0x82, 0xc5, 0x82, 0x8d, \ + 0xa0, 0x0d, 0x15, 0x84, \ + 0x0d, 0x64, 0x86, 0x15, \ + 0xa3, 0x24, 0x96, 0x1b, \ + 0x44, 0x08, 0x8c, 0x0b, \ + 0xa1, 0x0d, 0xfc, 0x38, \ + 0x4c, 0xb0, 0x01, 0xe5, \ + 0x15, 0x84, 0x9c, 0x00, \ + 0x82, 0x75, 0xa1, 0x0d, \ + 0x96, 0x1b, 0x0d, 0x64, \ + 0x8c, 0x0b, 0xa3, 0x24, \ + 0xfc, 0x38, 0x44, 0x08, \ + 0x80, 0x95, 0xa1, 0x0d, \ + 0x8c, 0xbb, 0x01, 0x35, \ + 0x93, 0x04, 0x92, 0xbb, \ + 0x94, 0x1b, 0x23, 0x64, \ + 0x4c, 0x08, 0x93, 0x0e, \ + 0x00, 0x9b, 0xfc, 0x38, \ + 0x96, 0x1b, 0x94, 0x0b, \ + 0xfc, 0x38, 0x4c, 0x08, \ + 0x82, 0x9d, 0xff, 0x11, \ + 0xa0, 0x0d, 0x82, 0x85, \ + 0x13, 0xa4, 0x84, 0xd5, \ + 0xa3, 0x24, 0x96, 0x1b, \ + 0x44, 0x08, 0x94, 0x0b, \ + 0xa1, 0x0d, 0xfc, 0x38, \ + 0x81, 0xd5, 0x82, 0xa5, \ + 0x82, 0x75, 0xa1, 0x0d, \ + 0x96, 0x1b, 0x13, 0x64, \ + 0x8c, 0x0b, 0xa3, 0x24, \ + 0xfc, 0x38, 0x44, 0x08, \ + 0x56, 0x60, 0xa1, 0x0d, \ + 0x96, 0xab, 0x9c, 0x00, \ + 0x00, 0x2a, 0x9b, 0x04, \ + 0x01, 0x18, 0xe0, 0x00, \ + 0x80, 0x00, 0x40, 0x01, \ + 0xe0, 0xfc, 0x1e, 0x07, \ + 0x17, 0x14, 0x84, 0x35, \ + 0x02, 0x4a, 0x8e, 0x0b, \ + 0x80, 0x24, 0xe4, 0x00, \ + 0xc1, 0x3e, 0xa8, 0x34, \ + 0x43, 0x08, 0xe9, 0x2e, \ + 0x83, 0x05, 0x87, 0xfc, \ + 0xa8, 0x14, 0x9c, 0x04, \ + 0xa8, 0x0e, 0x9c, 0x1e, \ + 0x19, 0x14, 0x81, 0xb5, \ + 0x02, 0x4a, 0x8e, 0x0b, \ + 0x80, 0x28, 0xe4, 0x00, \ + 0xc1, 0x36, 0xa8, 0x38, \ + 0x43, 0x08, 0xe9, 0x26, \ + 0x1d, 0x14, 0x87, 0xfc, \ + 0xec, 0x04, 0x00, 0x80, \ + 0x50, 0x0a, 0x81, 0x61, \ + 0x40, 0x72, 0x66, 0x86, \ + 0x1b, 0x0e, 0xf0, 0x84, \ + 0x0e, 0x83, 0x80, 0xab, \ + 0x90, 0xbb, 0x80, 0x9b, \ + 0x8c, 0xd5, 0xd6, 0xad, \ + 0x8e, 0x1b, 0x14, 0x0b, \ + 0x44, 0x08, 0xa3, 0x24, \ + 0xa0, 0x0d, 0xfc, 0x38, \ + 0xa1, 0x0b, 0x8a, 0xf5, \ + 0x9d, 0x04, 0x92, 0x05, \ + 0xa1, 0x0b, 0x8d, 0xb5, \ + 0xc0, 0x40, 0x1e, 0x05, \ + 0x01, 0x28, 0x88, 0xd5, \ + 0x80, 0x00, 0x40, 0x01, \ + 0xe0, 0xfc, 0x1e, 0x07, \ + 0x17, 0x24, 0x84, 0x55, \ + 0x96, 0x1b, 0x90, 0x0b, \ + 0xe4, 0x00, 0x04, 0x4a, \ + 0x80, 0x44, 0x84, 0x34, \ + 0xc1, 0x3e, 0xc5, 0x4e, \ + 0x87, 0xfc, 0x45, 0x08, \ + 0xac, 0x04, 0x83, 0x15, \ + 0xac, 0x1e, 0xa0, 0x14, \ + 0x81, 0xc5, 0xa0, 0x0e, \ + 0x96, 0x0b, 0x19, 0x24, \ + 0x04, 0x4a, 0x90, 0x1b, \ + 0x80, 0x38, 0xe4, 0x00, \ + 0xc1, 0x46, 0x84, 0x48, \ + 0x45, 0x08, 0xc5, 0x36, \ + 0x21, 0x04, 0x87, 0xfc, \ + 0x80, 0x83, 0x83, 0x61, \ + 0xd6, 0xad, 0x80, 0xb3, \ + 0x16, 0x0b, 0x94, 0xf5, \ + 0xa3, 0x24, 0x8e, 0x1b, \ + 0xfc, 0x38, 0x44, 0x08, \ + 0xf2, 0xc5, 0xa1, 0x0d, \ + 0x40, 0x01, 0x03, 0x98, \ + 0x1e, 0x07, 0x80, 0x00, \ + 0x8e, 0x35, 0xe0, 0xfc, \ + 0x96, 0x0b, 0x17, 0x24, \ + 0x04, 0x4a, 0x94, 0x1b, \ + 0x84, 0x34, 0xe4, 0x00, \ + 0xc5, 0x4e, 0x80, 0x44, \ + 0x45, 0x08, 0xc1, 0x3e, \ + 0x87, 0x85, 0x87, 0xfc, \ + 0x40, 0x01, 0x01, 0x28, \ + 0x1e, 0x07, 0x80, 0x00, \ + 0x84, 0x55, 0xe0, 0xfc, \ + 0x94, 0x0b, 0x17, 0x24, \ + 0x04, 0x4a, 0x92, 0x1b, \ + 0x84, 0x34, 0xe4, 0x00, \ + 0xc5, 0x4e, 0x80, 0x44, \ + 0x45, 0x08, 0xc1, 0x3e, \ + 0x83, 0x15, 0x87, 0xfc, \ + 0xa8, 0x14, 0xa4, 0x04, \ + 0xa8, 0x0e, 0xa4, 0x1e, \ + 0x19, 0x24, 0x81, 0xc5, \ + 0x94, 0x1b, 0x92, 0x0b, \ + 0xe4, 0x00, 0x04, 0x4a, \ + 0x84, 0x48, 0x80, 0x38, \ + 0xc5, 0x36, 0xc1, 0x46, \ + 0x87, 0xfc, 0x45, 0x08, \ + 0x83, 0x61, 0x1d, 0x04, \ + 0x71, 0x95, 0x80, 0x93, \ + 0xa8, 0x04, 0x80, 0xa3, \ + 0xa8, 0x1e, 0xac, 0x14, \ + 0x81, 0xc5, 0xac, 0x0e, \ + 0x94, 0x0b, 0x19, 0x24, \ + 0x04, 0x4a, 0x96, 0x1b, \ + 0x80, 0x38, 0xe4, 0x00, \ + 0xc1, 0x46, 0x84, 0x48, \ + 0x45, 0x08, 0xc5, 0x36, \ + 0x1d, 0x04, 0x87, 0xfc, \ + 0x21, 0x04, 0x83, 0x61, \ + 0x6f, 0x35, 0x80, 0xa3, \ + 0x1d, 0x14, 0x80, 0xb3, \ + 0x95, 0x04, 0xa1, 0x6b, \ + 0xe6, 0x86, 0x40, 0x0a, \ + 0x00, 0x00, 0x0e, 0x88, \ + 0xf0, 0x80, 0x58, 0x72, \ + 0x71, 0x9c, 0x42, 0x92, \ + 0xf1, 0xa4, 0x40, 0xa2, \ + 0x92, 0x2b, 0x01, 0x1d, \ + 0x44, 0xa0, 0x0e, 0x3b, \ + 0x46, 0x90, 0x9c, 0x00, \ + 0x48, 0x22, 0x9c, 0x00, \ + 0xa1, 0x4b, 0xf1, 0x8c, \ + 0x1e, 0x07, 0x8a, 0x25, \ + 0x42, 0xa2, 0x60, 0xfc, \ + 0x84, 0x55, 0xf1, 0x90, \ + 0xfd, 0x31, 0x45, 0x47, \ + 0x44, 0x22, 0xc8, 0x2b, \ + 0x8e, 0x3b, 0x7a, 0x0c, \ + 0x82, 0x21, 0x88, 0x23, \ + 0xe4, 0x00, 0x04, 0x4a, \ + 0x84, 0x54, 0x8c, 0x44, \ + 0xc5, 0x4e, 0xcd, 0x5e, \ + 0x87, 0xfc, 0x45, 0x08, \ + 0x08, 0x30, 0x83, 0x35, \ + 0xfd, 0x51, 0x6c, 0x04, \ + 0x82, 0x21, 0x24, 0x43, \ + 0xfa, 0x14, 0x48, 0x42, \ + 0x26, 0x23, 0x88, 0x23, \ + 0x04, 0x4a, 0x8e, 0x3b, \ + 0x8c, 0x48, 0xe4, 0x00, \ + 0xcd, 0x56, 0x84, 0x58, \ + 0x45, 0x08, 0xc5, 0x46, \ + 0x1d, 0x24, 0x87, 0xfc, \ + 0xf1, 0xa0, 0x42, 0xc2, \ + 0xf1, 0xac, 0x52, 0x82, \ + 0x44, 0x92, 0xa4, 0x13, \ + 0xa1, 0x2b, 0xfb, 0x84, \ + 0x1e, 0x07, 0x8c, 0x45, \ + 0x84, 0xb5, 0xe0, 0xfc, \ + 0x61, 0x88, 0x02, 0x24, \ + 0x42, 0x3b, 0xfd, 0x41, \ + 0xf1, 0x88, 0x44, 0xc2, \ + 0xfa, 0x10, 0x46, 0x32, \ + 0x02, 0x30, 0x82, 0x33, \ + 0x02, 0x4a, 0xe4, 0x04, \ + 0xa8, 0x34, 0xe4, 0x00, \ + 0xe9, 0x4e, 0x88, 0x44, \ + 0x43, 0x08, 0xc9, 0x3e, \ + 0x83, 0xb5, 0x87, 0xfc, \ + 0x06, 0xb0, 0x7e, 0x14, \ + 0x30, 0x33, 0xec, 0x04, \ + 0x90, 0x13, 0x82, 0x21, \ + 0x42, 0x12, 0xb8, 0x13, \ + 0xfd, 0x31, 0x7a, 0x8c, \ + 0xfa, 0x0c, 0x46, 0x12, \ + 0xf1, 0x84, 0x42, 0x32, \ + 0xe4, 0x00, 0x02, 0x4a, \ + 0x46, 0xc2, 0x28, 0x58, \ + 0x0e, 0x48, 0xf1, 0x88, \ + 0xec, 0x04, 0x04, 0x20, \ + 0x8e, 0x56, 0xe9, 0x46, \ + 0x87, 0xfc, 0x42, 0x08, \ + 0x98, 0x6b, 0x1d, 0x14, \ + 0x82, 0x45, 0xc2, 0x0d, \ + 0xef, 0xf8, 0x7e, 0x08, \ + 0x8e, 0x0b, 0x00, 0x1b, \ + 0xa3, 0x34, 0x9d, 0x24, \ + 0xef, 0xfc, 0x5c, 0x68, \ + 0xc2, 0x9d, 0x9d, 0x14, \ + 0x7d, 0x58, 0xa8, 0x05, \ + 0x92, 0x0b, 0x6f, 0xf8, \ + 0xf1, 0xa4, 0x4e, 0x62, \ + 0x80, 0x8b, 0x1d, 0x24, \ + 0xf9, 0x88, 0x40, 0x72, \ + 0x00, 0x0a, 0x11, 0x04, \ + 0x5e, 0x07, 0xf8, 0x0c, \ + 0x1b, 0x0e, 0xe0, 0xfc, \ + 0xc0, 0x00, 0x00, 0x26, \ + 0x40, 0x34, 0x5e, 0x05, \ + 0x1e, 0x08, 0xef, 0x8d, \ + 0x40, 0x8a, 0x9f, 0xfc, \ + 0x84, 0x0d, 0xe6, 0x8a, \ + 0x0e, 0x03, 0xa0, 0xc5, \ + 0xf0, 0x88, 0x50, 0x72, \ + 0x00, 0x24, 0x19, 0x0e, \ + 0x44, 0x1b, 0xe1, 0x88, \ + 0x40, 0x3b, 0xfd, 0x21, \ + 0xfa, 0x08, 0x42, 0x12, \ + 0x44, 0x32, 0x1d, 0x34, \ + 0x80, 0x23, 0xfa, 0x08, \ + 0xe4, 0x04, 0x16, 0x20, \ + 0x0c, 0x10, 0x86, 0x13, \ + 0xce, 0x8d, 0xe4, 0x04, \ + 0x90, 0x9b, 0x8a, 0xc5, \ + 0x92, 0xab, 0x21, 0x94, \ + 0x94, 0x1b, 0x23, 0x24, \ + 0x92, 0x0b, 0x94, 0x93, \ + 0xfc, 0x38, 0x44, 0x08, \ + 0x88, 0x75, 0xa3, 0x0d, \ + 0xa1, 0x0b, 0x9b, 0x04, \ + 0x1e, 0x07, 0x82, 0xe5, \ + 0x84, 0x35, 0xe0, 0xfc, \ + 0x64, 0x00, 0x16, 0x4a, \ + 0x96, 0x1b, 0x92, 0x0b, \ + 0x80, 0x34, 0xa8, 0x24, \ + 0xc1, 0x2e, 0xe9, 0x3e, \ + 0x87, 0xfc, 0x43, 0x08, \ + 0xa8, 0x04, 0x83, 0x05, \ + 0xa8, 0x1e, 0xa4, 0x14, \ + 0x81, 0xb5, 0xa4, 0x0e, \ + 0x64, 0x00, 0x0c, 0x4a, \ + 0x8c, 0x1b, 0x92, 0x0b, \ + 0x80, 0x38, 0xa8, 0x28, \ + 0xc1, 0x26, 0xe9, 0x36, \ + 0x87, 0xfc, 0x43, 0x08, \ + 0xf4, 0xb5, 0xce, 0x9d, \ + 0x19, 0x04, 0x9d, 0x04, \ + 0xc1, 0x8d, 0x80, 0x83, \ + 0x87, 0xd5, 0xf2, 0xb5, \ + 0x82, 0x0d, 0x1b, 0xb4, \ + 0x8e, 0x25, 0x99, 0xce, \ + 0xfc, 0x84, 0xbe, 0x64, \ + 0x82, 0x81, 0x82, 0x61, \ + 0x8a, 0xc5, 0xce, 0xbd, \ + 0x21, 0x94, 0x96, 0x9b, \ + 0x23, 0x24, 0x92, 0xab, \ + 0x94, 0x93, 0x94, 0x1b, \ + 0x44, 0x08, 0x92, 0x0b, \ + 0xa3, 0x0d, 0xfc, 0x38, \ + 0x01, 0x18, 0x88, 0x75, \ + 0x80, 0x00, 0x40, 0x01, \ + 0xe0, 0xfc, 0x1e, 0x07, \ + 0x10, 0x4a, 0x84, 0x35, \ + 0x92, 0x0b, 0x64, 0x00, \ + 0xa8, 0x24, 0x90, 0x1b, \ + 0xe9, 0x3e, 0x80, 0x34, \ + 0x43, 0x08, 0xc1, 0x2e, \ + 0x83, 0x05, 0x87, 0xfc, \ + 0xa4, 0x14, 0xa8, 0x04, \ + 0xa4, 0x0e, 0xa8, 0x1e, \ + 0x0c, 0x4a, 0x81, 0xb5, \ + 0x92, 0x0b, 0x64, 0x00, \ + 0xa8, 0x28, 0x8c, 0x1b, \ + 0xe9, 0x36, 0x80, 0x38, \ + 0x43, 0x08, 0xc1, 0x26, \ + 0xce, 0x9d, 0x87, 0xfc, \ + 0x9d, 0x04, 0xf4, 0xb5, \ + 0x80, 0xb3, 0x19, 0x04, \ + 0xf2, 0xb5, 0xc1, 0xbd, \ + 0xe5, 0x20, 0x1e, 0xf0, \ + 0xf6, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x04, 0xfc, 0x80, 0x5b, \ + 0x40, 0x06, 0xe2, 0xc0, \ + 0xd5, 0x6e, 0xfc, 0x12, \ + 0xd5, 0x8e, 0xd5, 0x7e, \ + 0x40, 0x5a, 0xd5, 0x9e, \ + 0x42, 0x06, 0xe0, 0xbc, \ + 0xd5, 0xae, 0xbc, 0x22, \ + 0xd5, 0xde, 0xd5, 0xbe, \ + 0x42, 0x6a, 0xd5, 0xee, \ + 0x40, 0x4a, 0xa0, 0x90, \ + 0xc0, 0x00, 0x20, 0x89, \ + 0xed, 0x88, 0x40, 0x1a, \ + 0x20, 0x01, 0x01, 0x78, \ + 0xc1, 0xc3, 0xc0, 0x00, \ + 0xc1, 0xc3, 0xc1, 0xc3, \ + 0xf0, 0x04, 0x4b, 0x85, \ + 0xf0, 0x04, 0x4b, 0x95, \ + 0xf0, 0x04, 0x4b, 0xa5, \ + 0xf0, 0x04, 0x4b, 0xb5, \ + 0xf0, 0x04, 0x4b, 0xc5, \ + 0xf0, 0x04, 0x4b, 0xd5, \ + 0xf0, 0x04, 0x4b, 0xe5, \ + 0xf0, 0x04, 0x4b, 0xf5, \ + 0xc2, 0x1f, 0x81, 0xa5, \ + 0xd5, 0x1e, 0xd5, 0x1e, \ + 0xd5, 0x1e, 0xd5, 0x1e, \ + 0xd5, 0x1e, 0xd5, 0x1e, \ + 0xd5, 0x1e, 0xd5, 0x1e, \ + 0xe0, 0xd0, 0x40, 0x4a, \ + 0x82, 0xb5, 0xd5, 0x2e, \ + 0xc1, 0xc3, 0xc1, 0xc3, \ + 0x4b, 0x0f, 0xc1, 0xc3, \ + 0x4b, 0x1f, 0xf0, 0x04, \ + 0x4b, 0x2f, 0xf0, 0x04, \ + 0x4b, 0x3f, 0xf0, 0x04, \ + 0x81, 0x65, 0xf0, 0x04, \ + 0xd5, 0x1e, 0xc2, 0x1f, \ + 0xd5, 0x1e, 0xd5, 0x1e, \ + 0x94, 0xfe, 0xd5, 0x1e, \ + 0xdd, 0x93, 0x81, 0x01, \ + 0xe0, 0x04, 0x19, 0x86, \ + 0xa3, 0xcd, 0xc1, 0xc3, \ + 0x18, 0x4a, 0x82, 0xa5, \ + 0x02, 0x86, 0xe0, 0x00, \ + 0x02, 0x15, 0xe1, 0x94, \ + 0x42, 0x05, 0x80, 0x00, \ + 0x41, 0xfe, 0x80, 0x00, \ + 0xbf, 0xff, 0x1f, 0xfd, \ + 0x02, 0x86, 0xdd, 0x93, \ + 0x84, 0x14, 0xe1, 0x94, \ + 0x41, 0xfe, 0x84, 0xce, \ + 0xff, 0xff, 0x7f, 0xfd, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0x6c, 0x40, 0x1e, 0xf0, \ + 0x7c, 0x0e, 0x82, 0x6b, \ + 0xe1, 0x98, 0x41, 0x86, \ + 0x02, 0x05, 0x80, 0x14, \ + 0xa1, 0x0b, 0xe1, 0x48, \ + 0x02, 0x10, 0x9a, 0xe5, \ + 0xc0, 0x01, 0x65, 0x21, \ + 0xbe, 0x14, 0xbe, 0x1e, \ + 0x80, 0x8b, 0xfe, 0x1e, \ + 0xa3, 0x0d, 0xa2, 0x04, \ + 0x40, 0x1b, 0x92, 0x55, \ + 0x14, 0x00, 0xfd, 0x21, \ + 0x4e, 0x12, 0x64, 0x04, \ + 0x44, 0x07, 0xfa, 0x08, \ + 0x10, 0x03, 0x94, 0x73, \ + 0xe4, 0x00, 0x0e, 0x6a, \ + 0xe6, 0x10, 0x16, 0x00, \ + 0xa1, 0x6b, 0x14, 0x0b, \ + 0x14, 0x00, 0x00, 0xe5, \ + 0x16, 0x15, 0xec, 0x04, \ + 0x8d, 0x1b, 0xe0, 0x80, \ + 0x22, 0x14, 0x8a, 0xf5, \ + 0xec, 0x08, 0x00, 0x00, \ + 0xf8, 0x80, 0x16, 0x25, \ + 0x50, 0x05, 0x83, 0xab, \ + 0x02, 0x06, 0x00, 0x04, \ + 0x56, 0x15, 0xc0, 0x00, \ + 0xa1, 0x2b, 0x58, 0x80, \ + 0x10, 0x15, 0x88, 0x25, \ + 0x83, 0x31, 0x61, 0x88, \ + 0x00, 0x34, 0x22, 0x94, \ + 0x80, 0x1f, 0xe8, 0x80, \ + 0x82, 0x65, 0xa1, 0x1b, \ + 0xe1, 0x8c, 0x10, 0x15, \ + 0x2c, 0x14, 0x80, 0x1f, \ + 0x82, 0x05, 0xa1, 0x1b, \ + 0x44, 0x08, 0x82, 0x0b, \ + 0x81, 0x75, 0xfc, 0x38, \ + 0xfc, 0x38, 0x44, 0x08, \ + 0xfc, 0x04, 0x81, 0x45, \ + 0xfc, 0x38, 0x44, 0x08, \ + 0x81, 0x9b, 0xa2, 0x04, \ + 0xfe, 0x04, 0x88, 0x35, \ + 0x91, 0x0b, 0x80, 0x04, \ + 0x4e, 0x68, 0x86, 0xb5, \ + 0x3f, 0xff, 0x47, 0xe5, \ + 0xec, 0x10, 0x16, 0xb0, \ + 0xa1, 0x0b, 0x81, 0x01, \ + 0xa2, 0x04, 0x86, 0x25, \ + 0xa1, 0x0b, 0x20, 0x04, \ + 0xa1, 0x0b, 0x82, 0xb5, \ + 0xfe, 0x74, 0x84, 0x65, \ + 0x90, 0x0b, 0x1c, 0x0e, \ + 0x68, 0x01, 0x40, 0x08, \ + 0x9c, 0x84, 0xc0, 0x00, \ + 0xe8, 0x75, 0xa1, 0x8b, \ + 0x7e, 0x8e, 0x81, 0x95, \ + 0xe6, 0xd5, 0xa1, 0x0b, \ + 0xbe, 0x04, 0x81, 0x55, \ + 0xa1, 0x0b, 0x80, 0x04, \ + 0xa0, 0xf1, 0xe4, 0xf5, \ + 0xf6, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x00, 0x00, 0x4c, \ + 0x83, 0x01, 0x00, 0x1b, \ + 0xef, 0xfc, 0x76, 0x08, \ + 0x81, 0x05, 0xc1, 0x43, \ + 0xfa, 0x80, 0x02, 0x14, \ + 0x00, 0x06, 0xe1, 0x1b, \ + 0x00, 0x06, 0x94, 0x22, \ + 0xdd, 0x93, 0xd4, 0x62, \ + 0xf8, 0x00, 0x04, 0x0c, \ + 0x6c, 0x50, 0x1e, 0xf0, \ + 0xe4, 0x7c, 0x03, 0x4a, \ + 0x82, 0x6b, 0x3c, 0x5e, \ + 0xfc, 0x1e, 0xbe, 0x5e, \ + 0x42, 0x66, 0x8e, 0x05, \ + 0x0c, 0x4a, 0xf0, 0x02, \ + 0x06, 0x34, 0x64, 0x7c, \ + 0x5e, 0x15, 0xfa, 0x80, \ + 0x4a, 0x0e, 0x00, 0x04, \ + 0x5e, 0x25, 0x9f, 0xfc, \ + 0x06, 0x1a, 0x00, 0x08, \ + 0x1e, 0x25, 0xe4, 0x00, \ + 0x44, 0x22, 0x80, 0x04, \ + 0x5e, 0x25, 0x98, 0x14, \ + 0x1e, 0x25, 0x80, 0x00, \ + 0x1e, 0x55, 0x80, 0x00, \ + 0x4a, 0x50, 0x80, 0x08, \ + 0x44, 0x22, 0x90, 0x7c, \ + 0x5e, 0x25, 0x99, 0x94, \ + 0x1e, 0x25, 0x80, 0x04, \ + 0x42, 0x20, 0x80, 0x04, \ + 0x18, 0x14, 0x9c, 0x00, \ + 0x40, 0x00, 0x3c, 0xb5, \ + 0xbc, 0x34, 0x18, 0x14, \ + 0x02, 0x44, 0x00, 0xd5, \ + 0xe1, 0x1b, 0xfa, 0x80, \ + 0xdc, 0x3c, 0x18, 0xc4, \ + 0xe2, 0x32, 0x00, 0x66, \ + 0x42, 0x0e, 0x28, 0xf1, \ + 0xc0, 0x10, 0x60, 0x01, \ + 0x0c, 0x0c, 0x8c, 0x2b, \ + 0x87, 0x75, 0xf0, 0x00, \ + 0x01, 0x7e, 0x28, 0xf1, \ + 0x0c, 0x0c, 0xff, 0xf8, \ + 0xdd, 0x93, 0xf0, 0x00, \ + 0x6c, 0x40, 0x1e, 0xf0, \ + 0xe4, 0x7c, 0x01, 0x4a, \ + 0xbe, 0x2e, 0xbc, 0x2e, \ + 0x86, 0x85, 0xfc, 0x2e, \ + 0xe4, 0x7c, 0x00, 0x0a, \ + 0x44, 0x06, 0x84, 0x45, \ + 0x3e, 0x2e, 0xf0, 0x02, \ + 0xfc, 0x1e, 0xc1, 0x21, \ + 0x84, 0x1f, 0xbe, 0x14, \ + 0xbc, 0x14, 0xbc, 0x1e, \ + 0xbe, 0x29, 0xfc, 0x24, \ + 0xbe, 0x1e, 0xa4, 0x1f, \ + 0x00, 0x06, 0xbe, 0x14, \ + 0x81, 0x55, 0xe2, 0x06, \ + 0xf8, 0x7e, 0x40, 0x06, \ + 0xf0, 0xfe, 0x00, 0x06, \ + 0x5d, 0x93, 0x81, 0x01, \ + 0x7f, 0xe5, 0xa0, 0xf1, \ + 0xff, 0xf8, 0x01, 0xae, \ + 0xe2, 0xc0, 0x04, 0x1c, \ + 0x04, 0x1f, 0xc1, 0x11, \ + 0xf0, 0x7c, 0x44, 0x00, \ + 0x02, 0x1c, 0xa4, 0x1f, \ + 0xdd, 0x93, 0xe3, 0x40, \ + 0xe4, 0x7c, 0x04, 0x0a, \ + 0x40, 0x88, 0x04, 0x24, \ + 0x00, 0x00, 0x06, 0x0e, \ + 0x08, 0x30, 0x80, 0x10, \ + 0x48, 0x12, 0x04, 0x10, \ + 0x58, 0x02, 0xd0, 0x88, \ + 0x02, 0x06, 0x71, 0x90, \ + 0xff, 0xfc, 0x60, 0x00, \ + 0x00, 0xc4, 0x83, 0xc7, \ + 0x02, 0x0f, 0xf0, 0x80, \ + 0x70, 0x01, 0x42, 0xc0, \ + 0x61, 0x0b, 0xdf, 0xfc, \ + 0x73, 0xfd, 0x40, 0xc0, \ + 0x48, 0x07, 0xc0, 0x03, \ + 0x00, 0xa5, 0xd0, 0x17, \ + 0x5d, 0x93, 0xa2, 0x0f, \ + 0xff, 0xf8, 0x01, 0x8e, \ + 0x60, 0x01, 0x02, 0x86, \ + 0xa2, 0x0f, 0xf8, 0x00, \ + 0x40, 0x06, 0x90, 0x0e, \ + 0xa1, 0x0d, 0xfc, 0x12, \ + 0x40, 0x06, 0x82, 0xc5, \ + 0xa1, 0x0d, 0xfc, 0x1a, \ + 0x40, 0x06, 0x82, 0x15, \ + 0x02, 0x06, 0xfc, 0x0a, \ + 0xc0, 0x20, 0x60, 0x01, \ + 0x80, 0x75, 0x83, 0x0d, \ + 0xf3, 0x72, 0x00, 0x46, \ + 0x81, 0x01, 0x5d, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x86, 0x4b, 0x08, 0x8b, \ + 0x81, 0x71, 0x3e, 0x2e, \ + 0x40, 0x5a, 0x00, 0x6b, \ + 0x7c, 0x7e, 0xe5, 0x06, \ + 0x42, 0x5a, 0x81, 0x31, \ + 0x88, 0x2b, 0x65, 0x86, \ + 0xef, 0xf8, 0x4f, 0x68, \ + 0x67, 0xd1, 0x00, 0x00, \ + 0x44, 0x86, 0x40, 0x01, \ + 0x42, 0x12, 0xe0, 0x3c, \ + 0x81, 0x31, 0x72, 0x9c, \ + 0xef, 0xf8, 0x45, 0x68, \ + 0x00, 0x00, 0x17, 0xa4, \ + 0xc0, 0x04, 0x64, 0x01, \ + 0x72, 0x9c, 0x42, 0x12, \ + 0x19, 0x74, 0xd7, 0x07, \ + 0xe0, 0xd4, 0x16, 0x14, \ + 0xff, 0x01, 0x20, 0xbf, \ + 0x00, 0x14, 0x01, 0xbb, \ + 0x57, 0xfe, 0xe1, 0xac, \ + 0xa1, 0x0b, 0x1f, 0xfc, \ + 0x60, 0x38, 0x14, 0x09, \ + 0xdf, 0xfc, 0x57, 0xfe, \ + 0x81, 0x11, 0x1b, 0x94, \ + 0x0a, 0xf0, 0x1c, 0x24, \ + 0x15, 0x04, 0xe4, 0x20, \ + 0xbe, 0x44, 0xa1, 0x0b, \ + 0x80, 0x08, 0x00, 0x1b, \ + 0x06, 0x14, 0x80, 0x04, \ + 0x90, 0x1b, 0x7b, 0x00, \ + 0xe8, 0x00, 0x43, 0xc8, \ + 0xfc, 0x04, 0xbc, 0x0e, \ + 0xe0, 0x18, 0x0e, 0x49, \ + 0x68, 0x34, 0xaa, 0x24, \ + 0x50, 0x82, 0xa0, 0x1b, \ + 0x8e, 0x0b, 0x7b, 0x90, \ + 0x68, 0x2c, 0x5c, 0x68, \ + 0x3e, 0x0e, 0x8c, 0x4b, \ + 0x85, 0x05, 0x81, 0xa1, \ + 0x0e, 0x29, 0xd9, 0x0a, \ + 0x42, 0xba, 0xe0, 0x1c, \ + 0x48, 0xba, 0xe5, 0x02, \ + 0x06, 0x20, 0x65, 0x82, \ + 0x83, 0x01, 0xe4, 0x1c, \ + 0x02, 0x03, 0xa7, 0x0f, \ + 0x42, 0x42, 0x81, 0x11, \ + 0x90, 0x21, 0x72, 0x84, \ + 0xef, 0xf8, 0x50, 0x48, \ + 0x7f, 0xfd, 0x45, 0xfe, \ + 0x82, 0xa1, 0x40, 0x3f, \ + 0x41, 0xfe, 0xc5, 0x0d, \ + 0x00, 0x3f, 0x1f, 0xfd, \ + 0x41, 0xfe, 0xa1, 0x1b, \ + 0xc0, 0x3f, 0x1f, 0xfd, \ + 0x91, 0xad, 0xe5, 0x0e, \ + 0xbe, 0x04, 0xf8, 0x15, \ + 0x1e, 0x05, 0xa1, 0x0b, \ + 0x98, 0xf1, 0x00, 0x00, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xff, 0xc1, 0x21, 0x1b, \ + 0x9f, 0xfc, 0x43, 0xfe, \ + 0xc0, 0xac, 0x02, 0x14, \ + 0x41, 0x06, 0x44, 0x2a, \ + 0xc0, 0x00, 0x02, 0x06, \ + 0x06, 0x06, 0xa1, 0x4b, \ + 0x43, 0xfe, 0x00, 0x00, \ + 0x82, 0x45, 0x9f, 0xfc, \ + 0x18, 0x44, 0x18, 0x3d, \ + 0x60, 0x37, 0xe1, 0x04, \ + 0x91, 0x80, 0x18, 0xc4, \ + 0x46, 0xca, 0x86, 0xc3, \ + 0x20, 0x23, 0xe0, 0x92, \ + 0x86, 0x23, 0x82, 0x0b, \ + 0xdd, 0x93, 0x94, 0x2e, \ + 0xfa, 0x04, 0x04, 0x4c, \ + 0x81, 0x11, 0x02, 0x6b, \ + 0x60, 0x00, 0x45, 0x06, \ + 0x66, 0xd8, 0x8c, 0x0b, \ + 0x81, 0x71, 0x6f, 0xf8, \ + 0x66, 0x00, 0x00, 0x60, \ + 0xe4, 0xc0, 0x02, 0x60, \ + 0x91, 0x21, 0x01, 0xa5, \ + 0x04, 0x20, 0x06, 0x7e, \ + 0x40, 0x73, 0xec, 0x04, \ + 0x42, 0x75, 0xf8, 0x20, \ + 0xc1, 0x7c, 0xf0, 0x08, \ + 0xfc, 0xf5, 0xa1, 0x2b, \ + 0x66, 0x41, 0x00, 0x60, \ + 0x02, 0x60, 0x40, 0x04, \ + 0x04, 0x60, 0xe6, 0x40, \ + 0x40, 0x03, 0x66, 0x41, \ + 0xe0, 0x04, 0x06, 0x06, \ + 0x06, 0x7e, 0x81, 0xa5, \ + 0xec, 0x04, 0x06, 0x30, \ + 0xf9, 0x00, 0x44, 0x75, \ + 0xf0, 0x08, 0x42, 0x75, \ + 0xc1, 0x76, 0xc9, 0x7e, \ + 0xfc, 0xd5, 0xa1, 0x3b, \ + 0x60, 0x0c, 0x41, 0xf6, \ + 0x4c, 0x01, 0x81, 0x11, \ + 0x40, 0x01, 0x63, 0x41, \ + 0x67, 0x45, 0x00, 0x60, \ + 0x63, 0xb8, 0xc0, 0x04, \ + 0x8f, 0x21, 0x6f, 0xf8, \ + 0x65, 0x61, 0x00, 0x60, \ + 0x02, 0x60, 0x40, 0x09, \ + 0xc0, 0x04, 0x67, 0x61, \ + 0x05, 0x06, 0x01, 0x95, \ + 0x06, 0x7e, 0xe0, 0x08, \ + 0xec, 0x04, 0x04, 0x20, \ + 0xf0, 0x08, 0x42, 0x75, \ + 0xa1, 0x2b, 0xc1, 0x7c, \ + 0x0c, 0x4c, 0xfe, 0x35, \ + 0xdd, 0x93, 0xf2, 0x04, \ + 0x6f, 0xd1, 0x00, 0x19, \ + 0xa1, 0x1b, 0xc0, 0x05, \ + 0x00, 0x09, 0x82, 0x45, \ + 0xc0, 0x06, 0x60, 0x2d, \ + 0xf3, 0xf0, 0x40, 0x00, \ + 0x00, 0x0c, 0xa9, 0x0b, \ + 0xdd, 0x93, 0xe2, 0x00, \ + 0x81, 0x01, 0x5d, 0x93, \ + 0xf0, 0x80, 0x44, 0x12, \ + 0xec, 0x70, 0x04, 0x29, \ + 0x84, 0x05, 0xa1, 0x2b, \ + 0xe0, 0x3c, 0x02, 0x29, \ + 0xe6, 0x82, 0x40, 0x2a, \ + 0x02, 0x09, 0x80, 0x13, \ + 0xc0, 0x05, 0x6b, 0x71, \ + 0xec, 0x14, 0x00, 0x00, \ + 0xe4, 0x08, 0x00, 0xca, \ + 0xe2, 0x00, 0x00, 0x0c, \ + 0x5d, 0x93, 0xdd, 0x93, \ + 0xc1, 0xc3, 0x81, 0x01, \ + 0xf8, 0x0c, 0x04, 0x4c, \ + 0x60, 0x88, 0x08, 0x04, \ + 0xe6, 0x40, 0x04, 0x20, \ + 0x60, 0x3c, 0x02, 0x39, \ + 0xf0, 0x90, 0x4a, 0x12, \ + 0x61, 0x1c, 0x0a, 0x55, \ + 0x08, 0xc5, 0x84, 0x43, \ + 0x02, 0x10, 0x62, 0x00, \ + 0xc0, 0x03, 0x67, 0x70, \ + 0x66, 0x82, 0x46, 0x3a, \ + 0x0a, 0xc3, 0x81, 0x81, \ + 0xe0, 0x88, 0x0a, 0x34, \ + 0x62, 0x00, 0x48, 0xc5, \ + 0xe0, 0x8c, 0x18, 0x04, \ + 0x7c, 0x14, 0x02, 0x55, \ + 0xf0, 0xb0, 0x4c, 0x22, \ + 0x7c, 0x30, 0x04, 0xc5, \ + 0x9a, 0x74, 0xc2, 0x37, \ + 0x4a, 0x72, 0x8a, 0xc3, \ + 0x86, 0x13, 0x72, 0xa0, \ + 0x80, 0x23, 0x1a, 0x5e, \ + 0x02, 0x13, 0x98, 0xce, \ + 0xc0, 0x01, 0x6c, 0x01, \ + 0xe3, 0x00, 0x08, 0x35, \ + 0x48, 0x35, 0x82, 0x33, \ + 0x04, 0x09, 0xe3, 0x00, \ + 0xc0, 0x01, 0x60, 0x01, \ + 0x44, 0x01, 0x82, 0x01, \ + 0xc0, 0x01, 0x60, 0x01, \ + 0xf0, 0x0c, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfe, 0x04, 0x04, 0x0c, \ + 0x48, 0x22, 0x08, 0x6b, \ + 0x21, 0x4b, 0xf9, 0x8c, \ + 0x08, 0x45, 0x8a, 0x7b, \ + 0x08, 0x24, 0xff, 0x41, \ + 0x0a, 0x34, 0x61, 0x84, \ + 0x2a, 0x4f, 0xe0, 0xfc, \ + 0xe1, 0x84, 0x0a, 0x34, \ + 0x70, 0xf8, 0x88, 0x03, \ + 0x42, 0x52, 0x6f, 0xf4, \ + 0x43, 0x4a, 0xf2, 0x84, \ + 0x81, 0x41, 0x64, 0x1c, \ + 0xe0, 0x1c, 0x42, 0x1a, \ + 0xe4, 0x18, 0x41, 0x6a, \ + 0xe5, 0x80, 0x42, 0x0a, \ + 0xe1, 0x88, 0x42, 0x1a, \ + 0xed, 0x80, 0x42, 0x0a, \ + 0x4c, 0x00, 0x09, 0x04, \ + 0x4e, 0x10, 0xdc, 0x00, \ + 0x82, 0x7e, 0xdc, 0x00, \ + 0x88, 0x0b, 0x80, 0x6e, \ + 0xf6, 0x04, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xff, 0x41, 0x21, 0x1b, \ + 0x08, 0x14, 0x82, 0x45, \ + 0x80, 0x43, 0xe1, 0x84, \ + 0x60, 0x06, 0x40, 0x4a, \ + 0x40, 0x22, 0x81, 0x41, \ + 0x8c, 0x0e, 0xfb, 0x80, \ + 0x88, 0x0b, 0x5d, 0x93, \ + 0xf8, 0x00, 0x04, 0x4c, \ + 0x67, 0xfd, 0x00, 0x25, \ + 0x81, 0x51, 0x40, 0x01, \ + 0x86, 0x44, 0x84, 0x34, \ + 0xe0, 0x18, 0x02, 0xc5, \ + 0x6f, 0xc9, 0x00, 0x21, \ + 0x84, 0x33, 0x40, 0x05, \ + 0x60, 0x50, 0x02, 0x6b, \ + 0xf2, 0x94, 0x48, 0x42, \ + 0x6f, 0xcd, 0x00, 0x01, \ + 0x02, 0x55, 0xc0, 0x05, \ + 0x84, 0xc3, 0x60, 0x1c, \ + 0x82, 0x61, 0x06, 0x4e, \ + 0x42, 0xc5, 0x84, 0x3e, \ + 0x80, 0x53, 0x60, 0x18, \ + 0xe0, 0x1c, 0x42, 0x55, \ + 0xe0, 0x50, 0x42, 0x63, \ + 0xf0, 0x00, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x60, 0x3c, 0x02, 0x39, \ + 0xc8, 0x44, 0x81, 0xc1, \ + 0x46, 0x3a, 0xca, 0x54, \ + 0xc4, 0x37, 0xe6, 0x82, \ + 0x06, 0x35, 0x82, 0x33, \ + 0x86, 0x43, 0xe7, 0xdc, \ + 0x72, 0xb0, 0x46, 0x52, \ + 0xca, 0x3e, 0x80, 0x13, \ + 0x02, 0x01, 0xc8, 0x4e, \ + 0x04, 0x35, 0xea, 0x70, \ + 0x80, 0x33, 0xe0, 0x20, \ + 0xe0, 0x20, 0x44, 0x35, \ + 0xe0, 0x54, 0x04, 0x3b, \ + 0xeb, 0x70, 0x02, 0x01, \ + 0xe0, 0x24, 0x04, 0x15, \ + 0x44, 0x33, 0x82, 0x31, \ + 0x80, 0x13, 0xe0, 0x54, \ + 0xe0, 0x24, 0x44, 0x15, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xe0, 0x3c, 0x02, 0x39, \ + 0xe0, 0x58, 0x04, 0x4b, \ + 0xe6, 0x82, 0x40, 0x3a, \ + 0xe0, 0x14, 0x04, 0x35, \ + 0x80, 0x13, 0xc4, 0x07, \ + 0xe7, 0xdc, 0x02, 0x05, \ + 0xe0, 0x10, 0x04, 0x15, \ + 0x81, 0x01, 0x00, 0x13, \ + 0x72, 0x80, 0x40, 0x32, \ + 0x44, 0x05, 0x82, 0x41, \ + 0x44, 0x15, 0xe0, 0x14, \ + 0x44, 0x43, 0xe0, 0x10, \ + 0xdd, 0x93, 0xe0, 0x58, \ + 0x60, 0x58, 0x00, 0x29, \ + 0x0a, 0x39, 0x84, 0x5b, \ + 0xc0, 0x01, 0x63, 0x41, \ + 0x63, 0x45, 0x0a, 0x49, \ + 0x02, 0x39, 0xc0, 0x01, \ + 0x40, 0x01, 0x60, 0x51, \ + 0x4a, 0x21, 0xc7, 0x2d, \ + 0x40, 0x01, 0x03, 0x41, \ + 0x4a, 0x21, 0xc8, 0x2d, \ + 0x40, 0x01, 0x63, 0x49, \ + 0xe4, 0xa0, 0x08, 0x00, \ + 0x03, 0x45, 0x4a, 0x21, \ + 0xc2, 0x37, 0x40, 0x01, \ + 0x60, 0x20, 0x00, 0x35, \ + 0x02, 0xc3, 0x86, 0x13, \ + 0x0a, 0x50, 0x60, 0x30, \ + 0xc0, 0x04, 0x67, 0x60, \ + 0xe0, 0xc0, 0x02, 0x13, \ + 0x1b, 0x98, 0x98, 0x0b, \ + 0xc1, 0xc3, 0xe0, 0x08, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x90, 0x1e, 0xf0, \ + 0x8a, 0x8b, 0x25, 0x04, \ + 0x81, 0x71, 0x21, 0x64, \ + 0x88, 0x3b, 0x09, 0x3e, \ + 0x23, 0x04, 0xbe, 0x0e, \ + 0x7e, 0x1e, 0x8c, 0xcb, \ + 0xe0, 0x04, 0x02, 0x06, \ + 0x04, 0x09, 0xfc, 0x0e, \ + 0x0d, 0x4e, 0xe0, 0x38, \ + 0x81, 0x65, 0x90, 0x4b, \ + 0x02, 0x10, 0x51, 0x7c, \ + 0xcd, 0x7e, 0xec, 0x04, \ + 0xa1, 0x1b, 0xf1, 0x7c, \ + 0x7c, 0x14, 0xfe, 0x55, \ + 0x03, 0xc1, 0x81, 0x41, \ + 0x13, 0x06, 0xa0, 0x0b, \ + 0x81, 0xa1, 0x60, 0x08, \ + 0x81, 0x71, 0x04, 0x4e, \ + 0xc6, 0x4e, 0x86, 0xce, \ + 0x02, 0x06, 0x44, 0x4e, \ + 0xbe, 0x34, 0xe0, 0x08, \ + 0x8e, 0xce, 0x8c, 0x1e, \ + 0xcc, 0x4e, 0xce, 0x4e, \ + 0x00, 0x20, 0x0f, 0x0e, \ + 0xc0, 0x03, 0x65, 0x71, \ + 0xff, 0x01, 0x0b, 0x0e, \ + 0x89, 0x34, 0xbc, 0x0e, \ + 0x40, 0x32, 0x8d, 0xa5, \ + 0x00, 0xb9, 0xf1, 0x9c, \ + 0xc0, 0x01, 0x62, 0x41, \ + 0x92, 0xe5, 0xa1, 0xbb, \ + 0x91, 0x6e, 0xfe, 0x04, \ + 0xe0, 0x10, 0x00, 0x09, \ + 0x82, 0xa5, 0xc1, 0xbd, \ + 0x00, 0x46, 0x3e, 0x14, \ + 0x04, 0x0e, 0xe0, 0x08, \ + 0x86, 0x0e, 0x83, 0x01, \ + 0xae, 0x03, 0x8b, 0x04, \ + 0x46, 0x0e, 0x80, 0x08, \ + 0xc4, 0x0e, 0x81, 0x01, \ + 0x7c, 0x24, 0x06, 0x15, \ + 0xf0, 0xa4, 0x40, 0x32, \ + 0x0c, 0xb4, 0x02, 0x24, \ + 0x81, 0x31, 0xe1, 0x84, \ + 0x70, 0x98, 0x40, 0x12, \ + 0x42, 0x22, 0x81, 0x11, \ + 0x96, 0x2b, 0x72, 0x84, \ + 0xef, 0xf4, 0x5f, 0x68, \ + 0x7f, 0xfd, 0x45, 0xfe, \ + 0xc5, 0x0d, 0xcf, 0xff, \ + 0x1f, 0xfd, 0x41, 0xfe, \ + 0xa1, 0x1b, 0x0f, 0xff, \ + 0x41, 0xfe, 0x0d, 0x14, \ + 0xcf, 0xff, 0x1f, 0xfd, \ + 0x84, 0x0e, 0x94, 0x13, \ + 0x40, 0x32, 0x89, 0x34, \ + 0x00, 0x25, 0xf0, 0xa8, \ + 0x00, 0x15, 0xe2, 0x90, \ + 0x8c, 0x23, 0xe3, 0x90, \ + 0x60, 0x2e, 0x40, 0x2a, \ + 0x04, 0x1d, 0xff, 0x21, \ + 0xf8, 0x00, 0x04, 0x0e, \ + 0x91, 0x80, 0x0c, 0x64, \ + 0x00, 0x02, 0x02, 0x63, \ + 0xc0, 0xff, 0x6f, 0xfd, \ + 0xe0, 0xae, 0x42, 0x6a, \ + 0x91, 0x64, 0xa0, 0x0c, \ + 0xfa, 0x08, 0x42, 0x12, \ + 0x02, 0x12, 0x03, 0x25, \ + 0xc0, 0x7f, 0x6b, 0xfd, \ + 0x83, 0x21, 0x7c, 0x14, \ + 0x86, 0x2e, 0x84, 0x2e, \ + 0xae, 0x03, 0x8b, 0x04, \ + 0x46, 0x0e, 0x80, 0x08, \ + 0x44, 0x0e, 0x81, 0x01, \ + 0x8d, 0x04, 0x81, 0x11, \ + 0x80, 0x2e, 0x94, 0x03, \ + 0x84, 0x81, 0x59, 0x1c, \ + 0x6c, 0x04, 0x0e, 0x70, \ + 0x90, 0x91, 0x88, 0xa1, \ + 0x8f, 0x0b, 0x8f, 0x04, \ + 0xfc, 0x04, 0xe4, 0xb5, \ + 0xa1, 0x0b, 0x80, 0x04, \ + 0x80, 0x00, 0x00, 0x06, \ + 0x80, 0x00, 0x5e, 0x05, \ + 0xc8, 0xf1, 0x3c, 0x04, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x84, 0x6b, 0x02, 0x7b, \ + 0x60, 0x50, 0x0e, 0x2b, \ + 0x0e, 0xab, 0x81, 0x81, \ + 0x0e, 0x9b, 0xe0, 0x54, \ + 0x18, 0x8e, 0xe0, 0x58, \ + 0x9a, 0x8e, 0xa1, 0x2b, \ + 0x4c, 0x23, 0xd8, 0x8e, \ + 0x4c, 0xa3, 0xe0, 0x18, \ + 0x4c, 0x93, 0xe0, 0x1c, \ + 0x84, 0xc5, 0xe0, 0x20, \ + 0x06, 0x24, 0x1c, 0x04, \ + 0x9e, 0x14, 0xe1, 0x84, \ + 0x81, 0x31, 0x06, 0x03, \ + 0x6f, 0xf4, 0x58, 0x88, \ + 0xf2, 0xa0, 0x42, 0x12, \ + 0x67, 0xfd, 0x00, 0x8a, \ + 0x41, 0xfe, 0xc7, 0xff, \ + 0x00, 0x01, 0x1f, 0xfd, \ + 0x41, 0xfe, 0xa1, 0x1b, \ + 0xc0, 0x01, 0x1f, 0xfd, \ + 0x14, 0x0a, 0x98, 0x0e, \ + 0x04, 0xa4, 0x68, 0x00, \ + 0x84, 0xc5, 0xfb, 0x80, \ + 0x06, 0x24, 0x5c, 0x04, \ + 0xde, 0x14, 0xe1, 0x84, \ + 0x81, 0x31, 0x06, 0x03, \ + 0x6f, 0xf4, 0x56, 0xe8, \ + 0xf2, 0xa0, 0x42, 0x12, \ + 0x67, 0xfd, 0x00, 0x8a, \ + 0x41, 0xfe, 0xc7, 0xff, \ + 0x00, 0x01, 0x1f, 0xfd, \ + 0x41, 0xfe, 0xa1, 0x1b, \ + 0xc0, 0x01, 0x1f, 0xfd, \ + 0x12, 0x0a, 0x9a, 0x0e, \ + 0x04, 0x94, 0x68, 0x00, \ + 0x86, 0x05, 0xfb, 0x80, \ + 0x60, 0x10, 0x0e, 0x05, \ + 0xe1, 0x84, 0x06, 0x24, \ + 0xe0, 0x14, 0x0e, 0x15, \ + 0x81, 0x31, 0x06, 0x03, \ + 0x6f, 0xf4, 0x55, 0x28, \ + 0xf2, 0xa0, 0x42, 0x12, \ + 0x67, 0xfd, 0x00, 0x8a, \ + 0x41, 0xfe, 0xc7, 0xff, \ + 0x00, 0x01, 0x1f, 0xfd, \ + 0x41, 0xfe, 0xa1, 0x1b, \ + 0xc0, 0x01, 0x1f, 0xfd, \ + 0x0c, 0x0c, 0xd8, 0x0e, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0xf8, 0x04, 0x04, 0x0c, \ + 0x84, 0x3b, 0x01, 0x01, \ + 0x46, 0x01, 0x8a, 0x06, \ + 0x8e, 0x06, 0xf0, 0x08, \ + 0x02, 0x0b, 0x8c, 0x06, \ + 0xa1, 0x0b, 0xe0, 0x50, \ + 0x02, 0xc5, 0x88, 0x45, \ + 0x06, 0x04, 0x60, 0x18, \ + 0x7f, 0x51, 0xe1, 0x84, \ + 0x86, 0x6b, 0xa6, 0x4b, \ + 0x4c, 0x40, 0x8a, 0xcd, \ + 0x18, 0x63, 0x9c, 0x00, \ + 0xff, 0xf8, 0x18, 0x0e, \ + 0xe0, 0x82, 0x4e, 0x6a, \ + 0xe0, 0x1c, 0x02, 0x65, \ + 0x4a, 0x72, 0x0a, 0x6d, \ + 0x46, 0x40, 0xfa, 0x30, \ + 0x0a, 0x52, 0x1c, 0x00, \ + 0x08, 0x56, 0xe9, 0xfc, \ + 0x40, 0x3a, 0x8c, 0x33, \ + 0x40, 0x02, 0xe0, 0x82, \ + 0x00, 0x02, 0xfa, 0x30, \ + 0x8a, 0x06, 0xe9, 0xfc, \ + 0xe0, 0x54, 0x02, 0x0b, \ + 0x88, 0x45, 0xa1, 0x0b, \ + 0x60, 0x20, 0x02, 0xc5, \ + 0xe1, 0x84, 0x06, 0x04, \ + 0xa6, 0x4b, 0x7f, 0x51, \ + 0x8a, 0xcd, 0x86, 0x6b, \ + 0x9c, 0x00, 0x4c, 0x40, \ + 0x60, 0x24, 0x02, 0xc5, \ + 0x4c, 0x6a, 0x98, 0x63, \ + 0x02, 0x0e, 0x60, 0x82, \ + 0x8a, 0xcd, 0xff, 0xf8, \ + 0x1c, 0x00, 0x46, 0x40, \ + 0xfa, 0x04, 0x4c, 0x62, \ + 0x0a, 0x62, 0x18, 0x33, \ + 0x40, 0x3a, 0xe9, 0xfc, \ + 0xc8, 0x56, 0xe0, 0x82, \ + 0xfa, 0x04, 0x40, 0x02, \ + 0xe9, 0xfc, 0x00, 0x02, \ + 0x0c, 0x0c, 0xca, 0x06, \ + 0xdd, 0x93, 0xf0, 0x04, \ + 0xf8, 0x0c, 0x04, 0x4c, \ + 0x60, 0x38, 0x04, 0x09, \ + 0x66, 0x40, 0x04, 0x30, \ + 0x18, 0x41, 0xc0, 0x04, \ + 0x05, 0x15, 0xff, 0x31, \ + 0xf8, 0x00, 0x18, 0x0e, \ + 0xa1, 0x6b, 0x88, 0x68, \ + 0x90, 0x6a, 0x84, 0xe5, \ + 0x84, 0x82, 0x94, 0x72, \ + 0x04, 0x25, 0xa1, 0x6b, \ + 0xf1, 0x9c, 0x4e, 0x82, \ + 0x10, 0x64, 0x06, 0x7d, \ + 0x58, 0x77, 0xe1, 0x84, \ + 0x91, 0x80, 0x10, 0x84, \ + 0x4c, 0x8a, 0x8e, 0x83, \ + 0x4c, 0x62, 0xe0, 0x9a, \ + 0x0e, 0x62, 0xfa, 0x30, \ + 0xc0, 0x7f, 0x6b, 0xfd, \ + 0x00, 0x00, 0x94, 0x7c, \ + 0x84, 0x41, 0x6c, 0x04, \ + 0x82, 0x21, 0x04, 0x51, \ + 0xf8, 0x35, 0xa1, 0x0b, \ + 0xf0, 0x0c, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0x89, 0x04, 0x84, 0x7a, \ + 0x04, 0x89, 0x86, 0x1a, \ + 0xd6, 0x58, 0xe0, 0x10, \ + 0x80, 0x7c, 0x88, 0x98, \ + 0xc8, 0xc8, 0x8a, 0x78, \ + 0x04, 0x29, 0xca, 0x68, \ + 0x02, 0x1c, 0xe0, 0x14, \ + 0xe1, 0x84, 0x02, 0x84, \ + 0x60, 0x10, 0x40, 0x51, \ + 0x0a, 0x24, 0x98, 0x13, \ + 0xc2, 0x17, 0x61, 0x84, \ + 0x02, 0x10, 0x0c, 0x53, \ + 0x40, 0x11, 0xec, 0x04, \ + 0xc2, 0x57, 0x60, 0x1c, \ + 0x6c, 0x04, 0x02, 0x50, \ + 0x40, 0x11, 0xc2, 0x87, \ + 0xc2, 0x27, 0x60, 0x20, \ + 0x60, 0x28, 0x40, 0x21, \ + 0x66, 0x40, 0x04, 0x40, \ + 0x06, 0x19, 0xc0, 0x04, \ + 0x81, 0x31, 0x60, 0x38, \ + 0xe0, 0x14, 0x40, 0x91, \ + 0xe0, 0x18, 0x40, 0x71, \ + 0xe0, 0x24, 0x40, 0x81, \ + 0x49, 0x48, 0x81, 0x95, \ + 0xec, 0x04, 0x0a, 0x30, \ + 0xec, 0x04, 0x02, 0x10, \ + 0x46, 0x40, 0xc9, 0x5d, \ + 0xa1, 0x1b, 0x9c, 0x00, \ + 0x40, 0x31, 0xfc, 0xf5, \ + 0x81, 0x01, 0x60, 0x2c, \ + 0xf0, 0x1c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x88, 0x6b, 0x19, 0x84, \ + 0x82, 0x9b, 0x06, 0x7b, \ + 0x84, 0xab, 0x09, 0x5e, \ + 0x64, 0xd1, 0x00, 0x80, \ + 0x16, 0x80, 0x40, 0x02, \ + 0xc0, 0x01, 0x66, 0x91, \ + 0x02, 0x80, 0x7c, 0x0e, \ + 0xc0, 0x02, 0x64, 0x91, \ + 0x08, 0x80, 0x3e, 0x1e, \ + 0x3c, 0xbe, 0xe4, 0x90, \ + 0x64, 0x91, 0x0a, 0x80, \ + 0x12, 0x1b, 0xc0, 0x01, \ + 0x60, 0x38, 0x8e, 0x2b, \ + 0x8c, 0x3b, 0x6f, 0xfc, \ + 0x86, 0xb5, 0xa1, 0x0b, \ + 0x64, 0x30, 0x04, 0x80, \ + 0x6a, 0xd8, 0x8c, 0x1b, \ + 0x7e, 0xae, 0xef, 0xfc, \ + 0xe4, 0x80, 0x14, 0x80, \ + 0x94, 0x2b, 0x0c, 0x1b, \ + 0xef, 0xfc, 0x70, 0xa8, \ + 0x8e, 0x2b, 0x12, 0x1b, \ + 0x8c, 0x3b, 0x09, 0x44, \ + 0x6f, 0xfc, 0x75, 0xa8, \ + 0x3c, 0x8e, 0x96, 0x5b, \ + 0x7e, 0x24, 0x92, 0x1b, \ + 0x0c, 0x4b, 0x8e, 0x3b, \ + 0x78, 0x18, 0x94, 0x5b, \ + 0x81, 0x01, 0xef, 0xfc, \ + 0x0c, 0x0c, 0xa8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xf0, 0x80, 0x46, 0x22, \ + 0xec, 0x70, 0x06, 0x39, \ + 0x86, 0xa5, 0xa1, 0x3b, \ + 0xe0, 0x3c, 0x04, 0x39, \ + 0xe6, 0x82, 0x40, 0x3a, \ + 0xf0, 0x80, 0x46, 0x22, \ + 0x6b, 0x71, 0x06, 0x39, \ + 0x06, 0x30, 0xc0, 0x05, \ + 0xe2, 0x3d, 0xec, 0x14, \ + 0xc2, 0x07, 0x84, 0x15, \ + 0x04, 0x03, 0x80, 0x23, \ + 0xc0, 0x02, 0x63, 0xb9, \ + 0xc6, 0x12, 0xc4, 0x22, \ + 0x41, 0x1a, 0x85, 0x0d, \ + 0x40, 0x0a, 0xe0, 0x04, \ + 0x00, 0x0c, 0xe1, 0x84, \ + 0xdd, 0x93, 0xe2, 0x00, \ + 0x81, 0x01, 0x5d, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xed, 0x50, 0x1e, 0xf0, \ + 0x81, 0x81, 0x3d, 0x04, \ + 0x09, 0x1e, 0xb9, 0xc4, \ + 0xe0, 0x04, 0x02, 0x06, \ + 0xbb, 0x04, 0x93, 0x0e, \ + 0xa7, 0x8e, 0x9b, 0x3e, \ + 0x06, 0x09, 0x8d, 0x0e, \ + 0x98, 0x3b, 0x60, 0x38, \ + 0xa3, 0x8e, 0xa5, 0x8e, \ + 0xfc, 0xce, 0x9d, 0x5e, \ + 0x55, 0x8e, 0x81, 0x55, \ + 0xec, 0x04, 0x02, 0x10, \ + 0xa1, 0x1b, 0xcd, 0x8c, \ + 0x0d, 0x14, 0xfe, 0x75, \ + 0x04, 0x69, 0x87, 0xc1, \ + 0x84, 0x8e, 0xe0, 0x24, \ + 0x06, 0x60, 0x06, 0xce, \ + 0x46, 0x8e, 0xec, 0x04, \ + 0xf4, 0x08, 0x07, 0x0a, \ + 0x02, 0x06, 0x44, 0x8e, \ + 0x93, 0x54, 0xe0, 0x08, \ + 0x8b, 0x11, 0x14, 0x1e, \ + 0xd6, 0x8e, 0x96, 0xce, \ + 0x0f, 0x28, 0xd4, 0x8e, \ + 0x20, 0x0b, 0xc0, 0x00, \ + 0x21, 0x0e, 0x83, 0x11, \ + 0x15, 0x0e, 0xff, 0x01, \ + 0x7e, 0x1e, 0x81, 0x01, \ + 0xf0, 0x04, 0x0c, 0x0a, \ + 0x02, 0x26, 0x0c, 0xcb, \ + 0x0b, 0x0e, 0xc0, 0x00, \ + 0x5e, 0x15, 0x81, 0x01, \ + 0x81, 0x31, 0x40, 0x0c, \ + 0x91, 0x2e, 0x97, 0x0e, \ + 0x8f, 0xce, 0x99, 0x4e, \ + 0x7c, 0x74, 0x99, 0x35, \ + 0xe0, 0x08, 0x15, 0x06, \ + 0x81, 0x91, 0x01, 0x61, \ + 0x97, 0x85, 0x9f, 0x3e, \ + 0xf1, 0xa4, 0x40, 0x42, \ + 0x62, 0x41, 0x00, 0xb9, \ + 0xa1, 0xbb, 0xc0, 0x01, \ + 0x04, 0x09, 0x92, 0x45, \ + 0x00, 0x04, 0xe0, 0x28, \ + 0x56, 0x9a, 0xfa, 0x80, \ + 0x04, 0x38, 0xe4, 0x00, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0xa8, 0x35, 0xa1, 0x3b, \ + 0x00, 0x09, 0x89, 0x04, \ + 0xc1, 0xbd, 0xe0, 0x10, \ + 0x00, 0x46, 0x82, 0x85, \ + 0x14, 0x0e, 0xe0, 0x08, \ + 0x96, 0x0e, 0x87, 0x01, \ + 0xb2, 0x03, 0x9b, 0x04, \ + 0xed, 0x70, 0x00, 0x09, \ + 0xd6, 0x0e, 0xd4, 0x8e, \ + 0x70, 0xa8, 0x42, 0x42, \ + 0xe5, 0x20, 0x04, 0xf0, \ + 0x7c, 0x28, 0x08, 0x05, \ + 0x06, 0x14, 0x81, 0x31, \ + 0x7f, 0xfd, 0x49, 0xfe, \ + 0x3c, 0x2e, 0xcf, 0xff, \ + 0x47, 0x18, 0x81, 0x51, \ + 0x96, 0x2b, 0x6f, 0xfc, \ + 0x06, 0xf0, 0x1d, 0x04, \ + 0xa5, 0x14, 0xe5, 0x10, \ + 0x80, 0x1e, 0x8c, 0x03, \ + 0x99, 0x04, 0x95, 0x24, \ + 0x44, 0x12, 0x17, 0x14, \ + 0x8c, 0x03, 0xfb, 0x88, \ + 0xe2, 0x90, 0x00, 0x05, \ + 0x82, 0x11, 0x15, 0x2e, \ + 0x45, 0xf6, 0x17, 0x1e, \ + 0x48, 0x98, 0xef, 0xfc, \ + 0x96, 0x1b, 0x6f, 0xfc, \ + 0x93, 0x54, 0xa3, 0x04, \ + 0x9c, 0x0c, 0x99, 0x44, \ + 0x8f, 0xc4, 0x91, 0x24, \ + 0x8d, 0x35, 0x9f, 0x34, \ + 0xf0, 0x04, 0x18, 0x1a, \ + 0x20, 0x01, 0x01, 0x98, \ + 0x0d, 0x14, 0xc0, 0x00, \ + 0x04, 0x0e, 0x83, 0x01, \ + 0x86, 0x0e, 0x87, 0x01, \ + 0x40, 0x32, 0x9b, 0x34, \ + 0x00, 0x09, 0xf1, 0xa4, \ + 0xc4, 0x8e, 0xed, 0x70, \ + 0x9d, 0x04, 0xc6, 0x0e, \ + 0x00, 0x8e, 0x8c, 0x03, \ + 0xf0, 0x98, 0x40, 0x32, \ + 0xe1, 0x1c, 0x00, 0x05, \ + 0x9c, 0x0c, 0x9f, 0x34, \ + 0x06, 0x1a, 0x89, 0x85, \ + 0x04, 0x58, 0xe0, 0x04, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0x60, 0x24, 0x04, 0x09, \ + 0x96, 0x1e, 0x87, 0x11, \ + 0x00, 0x1a, 0x9b, 0x14, \ + 0x00, 0x66, 0xe0, 0x08, \ + 0xc0, 0x00, 0x20, 0x09, \ + 0x02, 0x19, 0xb2, 0x13, \ + 0xd4, 0x8e, 0xed, 0x70, \ + 0x04, 0x15, 0xd6, 0x1e, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0x20, 0x01, 0x00, 0xd8, \ + 0x97, 0x04, 0xc0, 0x00, \ + 0x00, 0x1a, 0x95, 0x14, \ + 0x00, 0x56, 0x60, 0x00, \ + 0x00, 0x48, 0xe0, 0x08, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0x9d, 0x34, 0x81, 0x55, \ + 0x8c, 0x1e, 0x8c, 0x33, \ + 0x1e, 0x05, 0x94, 0x0e, \ + 0x40, 0x02, 0x80, 0x34, \ + 0x04, 0x85, 0x90, 0x98, \ + 0x04, 0x71, 0x10, 0x00, \ + 0x40, 0x42, 0x80, 0x01, \ + 0x10, 0xf0, 0x70, 0x98, \ + 0x00, 0x05, 0xe5, 0x10, \ + 0x45, 0xf6, 0x62, 0x90, \ + 0x16, 0x1b, 0xef, 0xfc, \ + 0x42, 0x98, 0x90, 0x3b, \ + 0x8f, 0xc4, 0xef, 0xfc, \ + 0x99, 0x44, 0x93, 0x54, \ + 0x8b, 0x14, 0x91, 0x24, \ + 0x81, 0x81, 0x20, 0x04, \ + 0x82, 0x11, 0x9f, 0x34, \ + 0x9c, 0x0c, 0x8b, 0x1e, \ + 0x6c, 0x04, 0x12, 0x90, \ + 0x04, 0x71, 0x88, 0x61, \ + 0xa1, 0x04, 0x90, 0xa1, \ + 0xd0, 0xf5, 0x93, 0x0b, \ + 0xfe, 0x04, 0x82, 0x31, \ + 0xce, 0x95, 0x81, 0x3b, \ + 0xe0, 0x2c, 0x04, 0x09, \ + 0xc0, 0x1d, 0x8b, 0x14, \ + 0x0d, 0x14, 0x82, 0x05, \ + 0x04, 0x0e, 0x8d, 0x01, \ + 0x86, 0x0e, 0x87, 0x01, \ + 0xc4, 0x8e, 0xc6, 0x8e, \ + 0xa1, 0x0b, 0x97, 0x04, \ + 0x04, 0x09, 0x84, 0xb5, \ + 0x8f, 0x11, 0x60, 0x24, \ + 0xf0, 0x08, 0x00, 0x0a, \ + 0x8d, 0x04, 0x82, 0xf5, \ + 0x87, 0x11, 0x00, 0x1e, \ + 0xc2, 0x8e, 0x82, 0x1e, \ + 0x10, 0x86, 0xc0, 0x8e, \ + 0x10, 0x0b, 0xe0, 0x0c, \ + 0xe5, 0x50, 0x1e, 0xf0, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x8d, 0x04, 0xdd, 0x93, \ + 0xa1, 0x0b, 0x80, 0x04, \ + 0xff, 0x65, 0xfc, 0xb5, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x8a, 0x6b, 0x19, 0xb4, \ + 0x86, 0x8b, 0x08, 0x7b, \ + 0x80, 0xab, 0x02, 0x9b, \ + 0x66, 0xd1, 0x02, 0xb0, \ + 0x00, 0xb0, 0x40, 0x01, \ + 0xc0, 0x01, 0x64, 0x91, \ + 0x06, 0xb0, 0x7c, 0x1e, \ + 0xc0, 0x01, 0x66, 0x91, \ + 0x0a, 0xb0, 0x3e, 0x3e, \ + 0x3c, 0x0e, 0xe4, 0x90, \ + 0x0e, 0x3b, 0x92, 0x1b, \ + 0x6a, 0xf8, 0x8c, 0x4b, \ + 0xa1, 0x0b, 0xef, 0xfc, \ + 0xa8, 0xf1, 0x80, 0xa5, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x04, 0xb0, 0xdd, 0x93, \ + 0x94, 0x0b, 0x64, 0x30, \ + 0x6f, 0xfc, 0x51, 0x08, \ + 0x09, 0x8e, 0x8c, 0x1b, \ + 0xe4, 0x80, 0x10, 0xb0, \ + 0x8c, 0x1b, 0x14, 0x0b, \ + 0x6f, 0xfc, 0x56, 0xc8, \ + 0x3c, 0xbe, 0x90, 0x2b, \ + 0x09, 0x24, 0x94, 0x0b, \ + 0x0e, 0x3b, 0x92, 0x1b, \ + 0x5e, 0x98, 0x8c, 0x4b, \ + 0x90, 0x5b, 0x6f, 0xfc, \ + 0x0c, 0x0c, 0xa8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x1c, 0x04, 0x4c, \ + 0x82, 0x9b, 0x04, 0x8b, \ + 0x86, 0x7b, 0x08, 0x6b, \ + 0x63, 0x49, 0x10, 0x19, \ + 0x04, 0x80, 0x40, 0x01, \ + 0xc0, 0x04, 0x67, 0x61, \ + 0x60, 0x20, 0x12, 0x09, \ + 0x70, 0xf8, 0x8c, 0x3b, \ + 0x0e, 0x05, 0xe8, 0x08, \ + 0x0e, 0x1b, 0xe0, 0x10, \ + 0x10, 0x29, 0xe0, 0x34, \ + 0xc0, 0x01, 0x63, 0x49, \ + 0xe0, 0x20, 0x12, 0x39, \ + 0xe0, 0x8c, 0x0e, 0x49, \ + 0x81, 0x01, 0x18, 0x0e, \ + 0x4c, 0x21, 0xd8, 0x1c, \ + 0x4c, 0x31, 0xe0, 0x18, \ + 0x4c, 0x41, 0xe0, 0x1c, \ + 0x0c, 0x4c, 0xe0, 0x20, \ + 0xdd, 0x93, 0xf2, 0x1c, \ + 0xfa, 0x7c, 0x04, 0x4c, \ + 0x81, 0xb1, 0x13, 0xa4, \ + 0x6e, 0x40, 0x02, 0x22, \ + 0x11, 0x74, 0x88, 0x8b, \ + 0x28, 0x5e, 0x86, 0x6b, \ + 0x0f, 0xbe, 0x81, 0x91, \ + 0x81, 0x55, 0x81, 0x21, \ + 0x82, 0x21, 0x41, 0x34, \ + 0xfa, 0x24, 0x52, 0x32, \ + 0xfe, 0x75, 0xc3, 0x2d, \ + 0x41, 0xfe, 0xa0, 0x9d, \ + 0x80, 0x3f, 0x1f, 0xfd, \ + 0x80, 0x00, 0x54, 0x05, \ + 0x06, 0xf0, 0x90, 0x05, \ + 0x92, 0x0b, 0x64, 0x70, \ + 0x90, 0x2b, 0x0a, 0x1b, \ + 0xe8, 0x28, 0x4e, 0x88, \ + 0x8e, 0x15, 0xa1, 0x0b, \ + 0x90, 0x1b, 0x12, 0x0b, \ + 0x68, 0x28, 0x51, 0x88, \ + 0xa1, 0x0b, 0x8e, 0x2b, \ + 0x8f, 0x84, 0x8c, 0x35, \ + 0x54, 0xb5, 0xa1, 0x8b, \ + 0x41, 0xfe, 0x00, 0x00, \ + 0x8a, 0x45, 0x9f, 0xfc, \ + 0x98, 0x1a, 0x9c, 0x04, \ + 0xc7, 0x07, 0x9a, 0x2a, \ + 0x80, 0x95, 0xc3, 0x0d, \ + 0x81, 0x01, 0x44, 0x0d, \ + 0x40, 0x12, 0x88, 0x25, \ + 0x04, 0x06, 0x70, 0x88, \ + 0x42, 0xb2, 0xff, 0x40, \ + 0x81, 0x31, 0x72, 0xac, \ + 0xef, 0xf0, 0x6c, 0x88, \ + 0x60, 0xfc, 0x04, 0x14, \ + 0x24, 0x0f, 0xc3, 0x07, \ + 0x01, 0x31, 0xc3, 0x17, \ + 0x62, 0x98, 0x90, 0x2b, \ + 0x04, 0x04, 0xef, 0xf0, \ + 0xff, 0x31, 0x60, 0xa0, \ + 0xd0, 0x17, 0x71, 0x07, \ + 0xa2, 0x0f, 0x07, 0x2b, \ + 0x1f, 0xfc, 0x45, 0xfe, \ + 0x45, 0xfe, 0xa1, 0x0b, \ + 0xa8, 0x2e, 0xdf, 0xfc, \ + 0x0c, 0x4c, 0x81, 0x01, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x90, 0x1e, 0xf0, \ + 0x16, 0x00, 0x04, 0x9b, \ + 0x0e, 0x00, 0xe4, 0x80, \ + 0x08, 0x00, 0x64, 0x90, \ + 0x01, 0x01, 0xe4, 0x10, \ + 0x11, 0x55, 0x81, 0x51, \ + 0x0b, 0x8e, 0x81, 0x81, \ + 0xe1, 0x84, 0x10, 0x04, \ + 0x86, 0x89, 0x0f, 0x0e, \ + 0x00, 0x84, 0x0d, 0x5e, \ + 0x4c, 0xb2, 0xe0, 0x8c, \ + 0x58, 0x72, 0x71, 0x80, \ + 0x11, 0x4e, 0xf1, 0x80, \ + 0xf1, 0x80, 0x40, 0x42, \ + 0x78, 0x24, 0x18, 0x55, \ + 0x80, 0x44, 0x86, 0x2b, \ + 0x30, 0x34, 0x98, 0xa4, \ + 0x7f, 0xfd, 0x59, 0xfe, \ + 0x40, 0x42, 0xcf, 0xff, \ + 0xb4, 0x0f, 0xf9, 0x94, \ + 0x58, 0x0d, 0xa6, 0x0f, \ + 0x9a, 0xd5, 0xfb, 0x01, \ + 0x02, 0x84, 0x7e, 0x1e, \ + 0x3e, 0x7e, 0xe0, 0x88, \ + 0x7c, 0xbe, 0xa2, 0x1b, \ + 0xf0, 0x84, 0x58, 0x92, \ + 0x12, 0x1b, 0x89, 0x9e, \ + 0xb2, 0x6a, 0xfc, 0x04, \ + 0xe0, 0x20, 0x18, 0xbb, \ + 0xe0, 0x24, 0x18, 0xcb, \ + 0xf0, 0x84, 0x50, 0x62, \ + 0x52, 0x82, 0x96, 0x83, \ + 0x13, 0x0a, 0xf0, 0xb0, \ + 0xc0, 0xff, 0x67, 0xfd, \ + 0x3c, 0x2e, 0x94, 0xb5, \ + 0x0b, 0x24, 0xa1, 0x5b, \ + 0xe0, 0x00, 0x14, 0x1a, \ + 0x62, 0x00, 0x00, 0x0c, \ + 0x10, 0x1c, 0xa1, 0x4b, \ + 0x12, 0x1a, 0x62, 0x00, \ + 0x04, 0x0c, 0xe0, 0x00, \ + 0x84, 0x03, 0x62, 0x00, \ + 0x84, 0x03, 0x21, 0x3b, \ + 0x62, 0x00, 0x0e, 0x0c, \ + 0x02, 0x68, 0x80, 0x83, \ + 0x81, 0x01, 0x20, 0x00, \ + 0x65, 0x16, 0x40, 0x1a, \ + 0x42, 0x1a, 0x81, 0x21, \ + 0x0a, 0x94, 0x65, 0x96, \ + 0x0a, 0x03, 0xe1, 0x84, \ + 0xe5, 0x12, 0x4a, 0x6a, \ + 0x72, 0x88, 0x42, 0x12, \ + 0xe5, 0x92, 0x48, 0x6a, \ + 0x4c, 0xba, 0x0a, 0x03, \ + 0x42, 0x12, 0xe5, 0x2a, \ + 0x4a, 0xba, 0x72, 0x90, \ + 0x0c, 0x03, 0xe5, 0xaa, \ + 0xe5, 0x0e, 0x48, 0xca, \ + 0x72, 0x94, 0x42, 0x12, \ + 0xe5, 0x8e, 0x44, 0xca, \ + 0x81, 0x31, 0x08, 0x03, \ + 0x72, 0x88, 0x42, 0x12, \ + 0x59, 0x78, 0x92, 0x2b, \ + 0x7e, 0x14, 0xef, 0xf0, \ + 0xc5, 0x0e, 0x8e, 0x83, \ + 0xcd, 0x9c, 0xbc, 0x34, \ + 0x8f, 0x04, 0x8d, 0x54, \ + 0x09, 0x94, 0xfc, 0xb4, \ + 0x3e, 0x74, 0x82, 0x51, \ + 0x11, 0x44, 0x84, 0x01, \ + 0x90, 0x91, 0xa0, 0xb1, \ + 0xa0, 0x41, 0xa0, 0x71, \ + 0xde, 0x85, 0xbe, 0x5d, \ + 0xfc, 0x98, 0x00, 0x84, \ + 0x61, 0x00, 0x00, 0x0a, \ + 0x41, 0xde, 0x81, 0x01, \ + 0xc8, 0xf1, 0xdf, 0xfc, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xed, 0x30, 0x1e, 0xf0, \ + 0x04, 0x10, 0x3e, 0x2e, \ + 0x09, 0x2e, 0xe4, 0x48, \ + 0xe4, 0x08, 0x04, 0x10, \ + 0xa0, 0x11, 0x7e, 0x2e, \ + 0x0c, 0x00, 0x7c, 0x1e, \ + 0x0e, 0x00, 0xe4, 0x80, \ + 0x10, 0x00, 0x64, 0x90, \ + 0x01, 0xb1, 0xe4, 0x10, \ + 0x01, 0x21, 0x81, 0x01, \ + 0x99, 0x25, 0x81, 0x11, \ + 0x04, 0x04, 0x21, 0x2e, \ + 0x23, 0x0e, 0xe1, 0x84, \ + 0x00, 0x24, 0x86, 0x29, \ + 0x53, 0xfe, 0x60, 0x8c, \ + 0xcf, 0xff, 0x7f, 0xfc, \ + 0x71, 0x80, 0x48, 0x62, \ + 0xf1, 0x80, 0x58, 0x72, \ + 0x78, 0x24, 0x18, 0xa5, \ + 0xf1, 0x80, 0x40, 0x82, \ + 0x90, 0x44, 0x80, 0x54, \ + 0x58, 0x52, 0xb0, 0x04, \ + 0xa8, 0xcf, 0xf9, 0xa8, \ + 0xd2, 0xcd, 0xa0, 0xcf, \ + 0x1f, 0x3e, 0xaa, 0xf5, \ + 0x18, 0x0c, 0xa1, 0xab, \ + 0xc4, 0x27, 0x62, 0x00, \ + 0xa1, 0x5b, 0x09, 0x34, \ + 0x82, 0xc3, 0x19, 0x8e, \ + 0xa4, 0x1b, 0x1d, 0x6e, \ + 0x62, 0x00, 0x0c, 0x0c, \ + 0x7e, 0x84, 0xa1, 0x4b, \ + 0x0c, 0x0c, 0x8c, 0xc3, \ + 0x44, 0xb2, 0x62, 0x00, \ + 0x1b, 0x7e, 0xf0, 0x84, \ + 0xf0, 0x88, 0x4e, 0x32, \ + 0x7c, 0x08, 0x10, 0x8b, \ + 0x0e, 0x7b, 0x8c, 0xc3, \ + 0x00, 0x1a, 0x78, 0x24, \ + 0x97, 0xce, 0xe0, 0x00, \ + 0x52, 0x5a, 0xfc, 0xc4, \ + 0x18, 0xcb, 0xe5, 0x22, \ + 0xa5, 0xbe, 0xfc, 0x08, \ + 0xfc, 0x08, 0x06, 0xbb, \ + 0xe5, 0x1e, 0x44, 0xaa, \ + 0xe5, 0x9e, 0x46, 0xaa, \ + 0x65, 0xa2, 0x54, 0x5a, \ + 0xf0, 0x88, 0x4a, 0x92, \ + 0xe5, 0x32, 0x4c, 0x4a, \ + 0x65, 0xb2, 0x4e, 0x4a, \ + 0xf2, 0x8c, 0x48, 0xa2, \ + 0x58, 0x0a, 0x0c, 0x53, \ + 0x50, 0x0a, 0xe5, 0x2e, \ + 0x48, 0x42, 0x65, 0xae, \ + 0x25, 0xb4, 0xf2, 0x9c, \ + 0x15, 0x8e, 0x98, 0x53, \ + 0xf2, 0xa0, 0x50, 0x42, \ + 0x79, 0xa0, 0x48, 0x52, \ + 0x08, 0x1c, 0x81, 0x01, \ + 0xa1, 0x4b, 0x62, 0x00, \ + 0x93, 0x4e, 0x94, 0x05, \ + 0x82, 0x43, 0xbe, 0x44, \ + 0x7c, 0x2c, 0x08, 0x03, \ + 0x91, 0x5e, 0x88, 0xb3, \ + 0x58, 0x98, 0x8b, 0xce, \ + 0x02, 0x04, 0x6f, 0xf0, \ + 0x0f, 0x0e, 0xe1, 0x7c, \ + 0x0d, 0x1e, 0x92, 0x2b, \ + 0xae, 0x02, 0x94, 0x3b, \ + 0x6f, 0xf0, 0x58, 0x08, \ + 0xe1, 0x7c, 0x02, 0x04, \ + 0x60, 0x20, 0x16, 0x03, \ + 0x02, 0xab, 0x80, 0x9b, \ + 0x8e, 0x3b, 0x8c, 0x2b, \ + 0x6f, 0xf0, 0x57, 0x68, \ + 0xe1, 0x7c, 0x02, 0x04, \ + 0x60, 0x24, 0x16, 0x03, \ + 0x0b, 0x24, 0x80, 0x6b, \ + 0x95, 0x34, 0x82, 0x7b, \ + 0x6f, 0xf0, 0x56, 0xc8, \ + 0xe1, 0x7c, 0x02, 0x04, \ + 0x80, 0x63, 0x11, 0x24, \ + 0x40, 0x72, 0x0d, 0x34, \ + 0x12, 0x63, 0xf2, 0x84, \ + 0x0f, 0x04, 0x81, 0x41, \ + 0xf2, 0xa8, 0x42, 0x02, \ + 0xe1, 0x84, 0x18, 0x24, \ + 0x0c, 0x84, 0x0c, 0x03, \ + 0x42, 0x12, 0xe1, 0x04, \ + 0x06, 0x84, 0x72, 0x8c, \ + 0x26, 0xcf, 0xe0, 0xfc, \ + 0x4a, 0x42, 0xff, 0x31, \ + 0x86, 0x1d, 0x71, 0xb0, \ + 0x73, 0x98, 0x48, 0x42, \ + 0x9c, 0x00, 0x58, 0x50, \ + 0x1c, 0x00, 0x4c, 0x40, \ + 0x42, 0x62, 0x98, 0x03, \ + 0x90, 0x3b, 0x72, 0x84, \ + 0xef, 0xec, 0x7e, 0xa8, \ + 0x93, 0x44, 0xa5, 0xb4, \ + 0x90, 0xb1, 0x1f, 0x34, \ + 0xcd, 0x0c, 0x97, 0x14, \ + 0x88, 0x13, 0x21, 0x24, \ + 0x9d, 0x64, 0xa3, 0x04, \ + 0x82, 0x21, 0x1b, 0x74, \ + 0x84, 0x01, 0x19, 0x84, \ + 0xa0, 0x71, 0xa0, 0x61, \ + 0xbe, 0x2d, 0xa0, 0x81, \ + 0x00, 0x14, 0xce, 0xc5, \ + 0x00, 0x0a, 0xfc, 0x98, \ + 0x81, 0x01, 0x61, 0x00, \ + 0xfb, 0x01, 0x80, 0x45, \ + 0xe5, 0x30, 0x1e, 0xf0, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x9c, 0x04, 0x0c, \ + 0x82, 0x7b, 0x04, 0x6b, \ + 0x60, 0x24, 0x04, 0x46, \ + 0x6c, 0x58, 0x8c, 0x0b, \ + 0x0e, 0x25, 0xef, 0xf0, \ + 0x89, 0x01, 0x62, 0x40, \ + 0x60, 0x14, 0x4c, 0x01, \ + 0x4c, 0x01, 0xa1, 0x11, \ + 0x9f, 0x01, 0x60, 0x18, \ + 0x60, 0x1c, 0x4c, 0x01, \ + 0x4c, 0x01, 0x8f, 0x01, \ + 0x08, 0x24, 0x60, 0x20, \ + 0x0e, 0x05, 0xe1, 0x0c, \ + 0x0a, 0x24, 0x62, 0x30, \ + 0x4c, 0x11, 0xe1, 0x10, \ + 0xaa, 0x43, 0x60, 0x24, \ + 0x60, 0x28, 0x4c, 0x11, \ + 0x02, 0x04, 0x83, 0x27, \ + 0x06, 0x04, 0x61, 0x0c, \ + 0x26, 0x13, 0xe1, 0x10, \ + 0x03, 0x07, 0xc4, 0x47, \ + 0x22, 0x03, 0xc4, 0x17, \ + 0x4c, 0x05, 0xa8, 0x23, \ + 0x00, 0x60, 0x62, 0x30, \ + 0xc0, 0x01, 0x66, 0x90, \ + 0x62, 0x40, 0x4c, 0x25, \ + 0xe4, 0x90, 0x02, 0x60, \ + 0x60, 0x04, 0x04, 0x06, \ + 0x81, 0x75, 0x81, 0x31, \ + 0x79, 0x00, 0x40, 0x33, \ + 0xec, 0x04, 0x04, 0x20, \ + 0xc1, 0x3c, 0xc5, 0x3e, \ + 0xfe, 0x35, 0xa1, 0x2b, \ + 0xe2, 0x24, 0x0e, 0x05, \ + 0x00, 0x86, 0x21, 0x0b, \ + 0x84, 0x35, 0xe0, 0x0c, \ + 0x64, 0x90, 0x10, 0x70, \ + 0x64, 0x90, 0x12, 0x70, \ + 0x02, 0x60, 0xc0, 0x01, \ + 0x06, 0x60, 0x64, 0x90, \ + 0xc0, 0x01, 0x64, 0x90, \ + 0x92, 0x2b, 0x10, 0x0b, \ + 0xef, 0xfc, 0x62, 0x48, \ + 0x80, 0x85, 0xa1, 0x0b, \ + 0xf2, 0x9c, 0x0c, 0x0c, \ + 0x04, 0x70, 0xdd, 0x93, \ + 0x40, 0x01, 0x66, 0x91, \ + 0x66, 0x91, 0x06, 0x60, \ + 0x10, 0x0b, 0xc0, 0x01, \ + 0x0c, 0x0c, 0x92, 0x1b, \ + 0xd7, 0x35, 0xf2, 0x9c, \ + 0xfa, 0x84, 0x04, 0x0c, \ + 0x82, 0x7b, 0x04, 0x6b, \ + 0x60, 0x1c, 0x04, 0x46, \ + 0x65, 0xf8, 0x8c, 0x0b, \ + 0x0e, 0x25, 0xef, 0xf0, \ + 0x89, 0x01, 0x61, 0xc0, \ + 0x60, 0x14, 0x4c, 0x01, \ + 0x4c, 0x01, 0xa1, 0x11, \ + 0x9f, 0x01, 0x60, 0x18, \ + 0x60, 0x1c, 0x4c, 0x01, \ + 0x4c, 0x01, 0x8f, 0x01, \ + 0x08, 0x24, 0x60, 0x20, \ + 0x0e, 0x05, 0xe1, 0x0c, \ + 0x0a, 0x24, 0x61, 0xb0, \ + 0x4c, 0x11, 0xe1, 0x10, \ + 0xaa, 0x43, 0x60, 0x24, \ + 0x60, 0x28, 0x4c, 0x11, \ + 0x02, 0x04, 0x83, 0x27, \ + 0x06, 0x04, 0x61, 0x0c, \ + 0x26, 0x13, 0xe1, 0x10, \ + 0x03, 0x07, 0xc4, 0x47, \ + 0x22, 0x03, 0xc4, 0x17, \ + 0x4c, 0x05, 0xa8, 0x23, \ + 0x00, 0x60, 0x61, 0xb0, \ + 0xc0, 0x01, 0x64, 0x90, \ + 0x61, 0xc0, 0x4c, 0x25, \ + 0xe4, 0x90, 0x02, 0x60, \ + 0x60, 0x04, 0x04, 0x06, \ + 0x81, 0x55, 0x81, 0x31, \ + 0x04, 0x20, 0x45, 0x3e, \ + 0xc1, 0x3c, 0xec, 0x04, \ + 0xfe, 0x75, 0xa1, 0x2b, \ + 0xe1, 0xa4, 0x0e, 0x05, \ + 0x80, 0xc5, 0xa1, 0x0b, \ + 0xe0, 0x0c, 0x00, 0x86, \ + 0xf2, 0x84, 0x0c, 0x0c, \ + 0x00, 0x70, 0xdd, 0x93, \ + 0x02, 0x60, 0x64, 0x90, \ + 0x04, 0x70, 0xe4, 0x90, \ + 0x40, 0x01, 0x64, 0x91, \ + 0x64, 0x91, 0x06, 0x60, \ + 0x0c, 0x0c, 0xc0, 0x01, \ + 0x1b, 0xd8, 0xf2, 0x84, \ + 0xc1, 0xc3, 0xff, 0xfc, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0xec, 0xe0, 0x1e, 0xf0, \ + 0x84, 0x7b, 0x17, 0x0e, \ + 0x82, 0x9b, 0x33, 0x04, \ + 0x83, 0x81, 0x2d, 0x24, \ + 0x81, 0x41, 0x08, 0xab, \ + 0x8b, 0x5e, 0x80, 0x04, \ + 0x11, 0x2e, 0xd4, 0x14, \ + 0x00, 0xd6, 0xa1, 0x0b, \ + 0x93, 0x1e, 0x80, 0x0c, \ + 0x95, 0x0e, 0xca, 0x1a, \ + 0x8e, 0x18, 0x8f, 0x1e, \ + 0xa8, 0x04, 0xcc, 0x28, \ + 0x02, 0x84, 0x2a, 0x34, \ + 0x4e, 0x43, 0xe8, 0x84, \ + 0x04, 0x14, 0x60, 0x18, \ + 0x61, 0xf8, 0xe8, 0x88, \ + 0xa1, 0x11, 0x68, 0x24, \ + 0x80, 0x6b, 0x0d, 0x7e, \ + 0xa4, 0xb4, 0x9e, 0x74, \ + 0x88, 0x05, 0xa1, 0x7b, \ + 0x81, 0x11, 0x17, 0x04, \ + 0x63, 0xe8, 0x04, 0x06, \ + 0x80, 0x04, 0x81, 0x31, \ + 0xef, 0xf0, 0x43, 0x68, \ + 0x8e, 0x2b, 0x01, 0x31, \ + 0xef, 0xec, 0x79, 0xc8, \ + 0x61, 0xe0, 0x04, 0x04, \ + 0x24, 0x1f, 0xd0, 0x17, \ + 0x02, 0x4a, 0xd0, 0x07, \ + 0x02, 0x1a, 0xe0, 0x00, \ + 0x41, 0x6a, 0xe0, 0x00, \ + 0x42, 0x0a, 0xe4, 0x2c, \ + 0x42, 0x1a, 0xe5, 0x80, \ + 0x42, 0x0a, 0xe1, 0x88, \ + 0x56, 0x00, 0xed, 0x80, \ + 0x17, 0x04, 0xdc, 0x00, \ + 0xa6, 0x34, 0xfd, 0x71, \ + 0x40, 0x0a, 0xc2, 0x0a, \ + 0x56, 0x06, 0x66, 0xae, \ + 0x42, 0x3a, 0xe0, 0x00, \ + 0x40, 0x3a, 0xe5, 0x82, \ + 0x04, 0x14, 0xe5, 0x02, \ + 0x06, 0x64, 0xf6, 0x80, \ + 0x04, 0x0a, 0x76, 0x80, \ + 0x04, 0x04, 0xe0, 0x80, \ + 0xa6, 0xb3, 0x16, 0x80, \ + 0xe4, 0x4c, 0x17, 0x0a, \ + 0xc0, 0x00, 0x12, 0xe8, \ + 0xe0, 0x00, 0x46, 0x96, \ + 0x86, 0xb3, 0xa4, 0x33, \ + 0xe4, 0xa8, 0x16, 0x8a, \ + 0x17, 0xb6, 0x02, 0x75, \ + 0x45, 0xb8, 0xc0, 0x00, \ + 0x04, 0xb0, 0x6f, 0xf0, \ + 0x04, 0x46, 0xec, 0xa4, \ + 0x56, 0x22, 0xe0, 0x04, \ + 0x01, 0x31, 0xf1, 0xac, \ + 0x7f, 0x18, 0x8c, 0x2b, \ + 0x12, 0x2b, 0xef, 0xec, \ + 0xff, 0x51, 0x60, 0x14, \ + 0x11, 0x38, 0xa1, 0x2b, \ + 0x4f, 0xfe, 0x00, 0x00, \ + 0x46, 0x86, 0x9f, 0xfc, \ + 0x8b, 0x1b, 0x60, 0x3c, \ + 0xe6, 0x8e, 0x44, 0x2a, \ + 0x08, 0x24, 0x95, 0x64, \ + 0x48, 0x3b, 0xe1, 0x84, \ + 0x41, 0x5a, 0x80, 0x43, \ + 0x81, 0x01, 0x64, 0x0c, \ + 0x72, 0x80, 0x46, 0x12, \ + 0xe1, 0x84, 0x40, 0x0a, \ + 0xff, 0x11, 0x7f, 0x01, \ + 0x5c, 0x00, 0x42, 0x30, \ + 0xdc, 0x00, 0x40, 0x40, \ + 0x6f, 0xec, 0x73, 0xa8, \ + 0x01, 0x31, 0x81, 0x31, \ + 0x7c, 0xb8, 0x8c, 0x2b, \ + 0x04, 0x14, 0xef, 0xec, \ + 0x4c, 0x06, 0x76, 0x80, \ + 0x04, 0x0a, 0xe0, 0x00, \ + 0x04, 0x04, 0xe0, 0x80, \ + 0x04, 0x20, 0x96, 0x80, \ + 0x05, 0x0a, 0x84, 0x80, \ + 0x8a, 0xf5, 0xe4, 0x7c, \ + 0xcd, 0xbd, 0xa4, 0x63, \ + 0x7f, 0x21, 0xb8, 0x55, \ + 0xe4, 0x7c, 0x0d, 0x0a, \ + 0x04, 0x60, 0x09, 0x2e, \ + 0x10, 0x06, 0xec, 0x80, \ + 0x44, 0x62, 0x00, 0x00, \ + 0x0c, 0xca, 0xfb, 0x88, \ + 0x06, 0x22, 0x64, 0x80, \ + 0x08, 0x0c, 0xec, 0x7c, \ + 0x18, 0x34, 0x62, 0x00, \ + 0x0a, 0x44, 0xec, 0x00, \ + 0xa6, 0x23, 0x6f, 0x8c, \ + 0x4a, 0xc2, 0x27, 0x8f, \ + 0x2a, 0x8f, 0xf8, 0x94, \ + 0xec, 0x04, 0x04, 0x22, \ + 0xa7, 0x4f, 0x25, 0x8f, \ + 0x60, 0xfc, 0x04, 0x84, \ + 0x24, 0x4f, 0xc3, 0x47, \ + 0x08, 0x03, 0xc3, 0x87, \ + 0x7f, 0x58, 0x8c, 0x2b, \ + 0x42, 0x82, 0x6f, 0xec, \ + 0x00, 0x7b, 0xf2, 0x84, \ + 0x81, 0xe5, 0xac, 0xb3, \ + 0x09, 0x1e, 0xff, 0x11, \ + 0xe0, 0x04, 0x02, 0x06, \ + 0x42, 0x12, 0xb6, 0x13, \ + 0x96, 0x2b, 0x7b, 0x88, \ + 0xec, 0x80, 0x16, 0x10, \ + 0x68, 0xac, 0x0e, 0x04, \ + 0x2f, 0x64, 0x84, 0xb3, \ + 0x93, 0x84, 0xff, 0x41, \ + 0x0c, 0x35, 0x8f, 0x04, \ + 0x44, 0x02, 0xe0, 0x2c, \ + 0x00, 0x24, 0xfa, 0x20, \ + 0x0a, 0x34, 0x76, 0x80, \ + 0x00, 0x02, 0xe1, 0x84, \ + 0xca, 0x1b, 0x6c, 0x6c, \ + 0xe8, 0x80, 0x18, 0x24, \ + 0x02, 0x00, 0x42, 0xcd, \ + 0x48, 0xc2, 0xe4, 0x14, \ + 0x42, 0x0a, 0x10, 0x94, \ + 0x48, 0x4a, 0xe0, 0x80, \ + 0x02, 0x00, 0x60, 0x0e, \ + 0x11, 0x04, 0xc4, 0x10, \ + 0x90, 0x11, 0xc3, 0x17, \ + 0xc4, 0x04, 0x08, 0x40, \ + 0x08, 0x44, 0x00, 0xca, \ + 0x4a, 0x4a, 0xc1, 0x84, \ + 0x97, 0x44, 0xf0, 0x0a, \ + 0xe0, 0x10, 0x12, 0x03, \ + 0x66, 0x96, 0x4a, 0xca, \ + 0xe4, 0xd0, 0x18, 0xf0, \ + 0xfe, 0xce, 0xd0, 0x4a, \ + 0xbe, 0x7e, 0xfc, 0xbe, \ + 0x90, 0x1b, 0x3c, 0x1e, \ + 0xe8, 0x00, 0x47, 0xc8, \ + 0x80, 0x7b, 0x0e, 0x1b, \ + 0x9c, 0x35, 0xa1, 0x7b, \ + 0x82, 0xcb, 0x31, 0x04, \ + 0xe0, 0x18, 0x14, 0x1b, \ + 0x80, 0x0a, 0x95, 0x24, \ + 0x66, 0x8a, 0x40, 0x0a, \ + 0xe1, 0x84, 0x04, 0x14, \ + 0xc6, 0x0d, 0xc4, 0x3b, \ + 0x90, 0x80, 0x44, 0x22, \ + 0x80, 0x10, 0x5e, 0x25, \ + 0x97, 0x24, 0x89, 0x54, \ + 0x60, 0x2c, 0x0c, 0x35, \ + 0x4a, 0x5a, 0x98, 0x6b, \ + 0x89, 0x11, 0x60, 0x06, \ + 0x04, 0xf0, 0x48, 0x4a, \ + 0x12, 0x03, 0xe4, 0xc0, \ + 0x7e, 0x2e, 0xe0, 0x10, \ + 0xfc, 0xbe, 0x90, 0x2b, \ + 0x3c, 0x1e, 0xbe, 0x6e, \ + 0x45, 0x38, 0x90, 0x1b, \ + 0x80, 0x7b, 0xe8, 0x00, \ + 0x92, 0x35, 0xa1, 0x7b, \ + 0xe0, 0x10, 0x12, 0x23, \ + 0x99, 0x14, 0x9b, 0x04, \ + 0xe1, 0x7c, 0x06, 0x24, \ + 0x60, 0xf8, 0x08, 0x34, \ + 0x40, 0x02, 0xc5, 0x37, \ + 0x02, 0x64, 0x7b, 0x84, \ + 0x0c, 0x02, 0xe9, 0xac, \ + 0x40, 0x3f, 0x6f, 0xfd, \ + 0xe5, 0x0a, 0x40, 0x1a, \ + 0x65, 0x8a, 0x42, 0x1a, \ + 0x0d, 0x44, 0xc5, 0x27, \ + 0x48, 0x63, 0xa8, 0x2f, \ + 0x70, 0x78, 0xe0, 0x18, \ + 0x15, 0x14, 0xef, 0xec, \ + 0x00, 0x9b, 0x82, 0xab, \ + 0x8c, 0x2b, 0x81, 0x31, \ + 0xe5, 0x1a, 0x40, 0x1a, \ + 0xe5, 0x9a, 0x42, 0x1a, \ + 0xef, 0xec, 0x6f, 0xc8, \ + 0x47, 0xfe, 0x0b, 0x24, \ + 0xc0, 0x07, 0x7f, 0xfd, \ + 0x8b, 0x27, 0xc8, 0x24, \ + 0xf1, 0x88, 0x44, 0x32, \ + 0x6f, 0xec, 0x6f, 0x28, \ + 0xe1, 0x7c, 0x06, 0x24, \ + 0x60, 0xe8, 0x04, 0x14, \ + 0x4d, 0x17, 0xcd, 0x07, \ + 0xc2, 0xad, 0xa4, 0x0f, \ + 0xe0, 0x04, 0x54, 0x1a, \ + 0xe4, 0x00, 0x53, 0x2a, \ + 0xe5, 0x80, 0x42, 0x0a, \ + 0xe1, 0x88, 0x42, 0x1a, \ + 0xed, 0x80, 0x42, 0x0a, \ + 0x40, 0x18, 0x1e, 0x05, \ + 0xc0, 0x00, 0x02, 0x06, \ + 0xc0, 0x18, 0x40, 0x13, \ + 0xf0, 0xf1, 0x0e, 0x0b, \ + 0xf6, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0x82, 0x8b, 0x06, 0xab, \ + 0x80, 0x6b, 0x7e, 0x2e, \ + 0x88, 0x9b, 0x19, 0x34, \ + 0x00, 0xf0, 0x7c, 0x5e, \ + 0x04, 0xf0, 0xe4, 0x30, \ + 0x8b, 0x11, 0x64, 0x20, \ + 0xe8, 0x00, 0x54, 0xe8, \ + 0xf7, 0x31, 0x7e, 0x44, \ + 0x7f, 0xfd, 0x45, 0xfe, \ + 0x43, 0xfe, 0x47, 0xff, \ + 0xc7, 0xff, 0x7f, 0xfd, \ + 0xc6, 0x4d, 0xff, 0x71, \ + 0x84, 0x10, 0x08, 0x40, \ + 0x04, 0x44, 0x7c, 0x44, \ + 0xc6, 0x4d, 0x81, 0x8c, \ + 0x84, 0x10, 0x08, 0x40, \ + 0x01, 0x8c, 0x02, 0x44, \ + 0x04, 0x13, 0xa1, 0xab, \ + 0xe6, 0xa6, 0x44, 0x9a, \ + 0x7e, 0x1e, 0xc4, 0x17, \ + 0xe0, 0xa0, 0x02, 0x24, \ + 0x12, 0x98, 0xfc, 0x1e, \ + 0x06, 0xa4, 0x80, 0x00, \ + 0xc6, 0x2b, 0xe1, 0x84, \ + 0xff, 0x21, 0x44, 0x1d, \ + 0x10, 0x8c, 0x44, 0x12, \ + 0x42, 0x2a, 0xa1, 0x31, \ + 0x04, 0xf0, 0x60, 0x2a, \ + 0x7c, 0x1e, 0xe4, 0x20, \ + 0xec, 0x0c, 0x02, 0x00, \ + 0x68, 0x00, 0x51, 0xe8, \ + 0xe4, 0x30, 0x00, 0xf0, \ + 0x43, 0xfe, 0x7e, 0x24, \ + 0xdf, 0xff, 0x7f, 0xfd, \ + 0x8f, 0x2b, 0xfc, 0x34, \ + 0xc4, 0x04, 0x04, 0x20, \ + 0x41, 0x84, 0x02, 0x24, \ + 0x45, 0xfe, 0x8f, 0x3b, \ + 0x5f, 0xff, 0x7f, 0xfd, \ + 0xc4, 0x04, 0x06, 0x30, \ + 0xc1, 0x84, 0x04, 0x34, \ + 0x70, 0x84, 0x46, 0x22, \ + 0xe0, 0x00, 0x42, 0x16, \ + 0x76, 0x80, 0x08, 0x34, \ + 0x7c, 0x2e, 0xa0, 0x13, \ + 0xfb, 0x90, 0x48, 0x12, \ + 0x02, 0x00, 0x88, 0x03, \ + 0x00, 0x34, 0x6c, 0x04, \ + 0x7e, 0x0e, 0xe8, 0x90, \ + 0xe0, 0x80, 0x42, 0x0a, \ + 0x0f, 0x0b, 0x82, 0xa5, \ + 0x7f, 0xfd, 0x45, 0xfe, \ + 0x00, 0x00, 0xdf, 0xff, \ + 0x02, 0x10, 0x44, 0x04, \ + 0x04, 0x04, 0xec, 0x04, \ + 0x7e, 0x2e, 0xc1, 0x84, \ + 0x40, 0x0a, 0x84, 0x0b, \ + 0xd0, 0xa7, 0x70, 0x0a, \ + 0xf0, 0x0a, 0x44, 0xaa, \ + 0xa1, 0x2b, 0xfe, 0x0e, \ + 0x00, 0x94, 0xb6, 0x85, \ + 0x08, 0x24, 0x76, 0x80, \ + 0x06, 0x02, 0xe1, 0x84, \ + 0xc8, 0x0b, 0x6c, 0x70, \ + 0xc3, 0x17, 0x27, 0x9f, \ + 0xff, 0x01, 0x40, 0x9d, \ + 0x90, 0x90, 0x40, 0x92, \ + 0x60, 0x0a, 0x48, 0x0a, \ + 0xe4, 0x30, 0x00, 0xf0, \ + 0xe4, 0x20, 0x04, 0xf0, \ + 0x4c, 0xd8, 0xfc, 0x4e, \ + 0x7e, 0x24, 0xe8, 0x00, \ + 0x7c, 0x34, 0x80, 0x1b, \ + 0x7f, 0xfd, 0x41, 0xfe, \ + 0x1d, 0xb4, 0xdf, 0xff, \ + 0x0f, 0x2b, 0xc2, 0x17, \ + 0xec, 0x08, 0x12, 0x10, \ + 0xc4, 0x04, 0x04, 0x20, \ + 0x41, 0x84, 0x00, 0x24, \ + 0x45, 0xfe, 0x8f, 0x3b, \ + 0x5f, 0xff, 0x7f, 0xfd, \ + 0xc4, 0x04, 0x06, 0x30, \ + 0xc1, 0x84, 0x04, 0x34, \ + 0x44, 0x0a, 0x84, 0x03, \ + 0xfe, 0x0e, 0xe5, 0x82, \ + 0xf6, 0x80, 0x06, 0x24, \ + 0x64, 0x7c, 0x07, 0x0a, \ + 0xe5, 0x02, 0x40, 0x0a, \ + 0x90, 0x95, 0xfc, 0x2e, \ + 0x60, 0x00, 0x54, 0x06, \ + 0x26, 0xa3, 0x83, 0x11, \ + 0x15, 0x0a, 0xa6, 0x3b, \ + 0x46, 0xa2, 0x64, 0x7c, \ + 0x02, 0x06, 0xfb, 0x8c, \ + 0x14, 0xca, 0x00, 0x00, \ + 0x0a, 0x0c, 0xe4, 0x80, \ + 0x08, 0x32, 0x62, 0x00, \ + 0x3e, 0x8e, 0xec, 0x7c, \ + 0x18, 0x54, 0x8c, 0x8b, \ + 0x0c, 0x44, 0x6f, 0x90, \ + 0x28, 0x33, 0xec, 0x00, \ + 0xf8, 0xb0, 0x58, 0x62, \ + 0x06, 0x32, 0x29, 0x1f, \ + 0x38, 0x1f, 0xec, 0x04, \ + 0x27, 0x1f, 0xa9, 0x5f, \ + 0x06, 0x14, 0xc3, 0x57, \ + 0xfe, 0x17, 0x61, 0x84, \ + 0xc6, 0x1b, 0x22, 0x5f, \ + 0x45, 0x4a, 0x4a, 0x4b, \ + 0x44, 0x1a, 0xe4, 0x04, \ + 0x42, 0x52, 0x60, 0x04, \ + 0x41, 0x6a, 0xf0, 0x80, \ + 0xff, 0x01, 0x64, 0x10, \ + 0x65, 0x80, 0x42, 0x0a, \ + 0xf2, 0x88, 0x44, 0x32, \ + 0x61, 0x88, 0x42, 0x1a, \ + 0x42, 0x0a, 0x90, 0x6b, \ + 0x40, 0x10, 0xed, 0x80, \ + 0xff, 0x11, 0x5c, 0x00, \ + 0x5c, 0x00, 0x42, 0x20, \ + 0x63, 0xd8, 0x94, 0x2b, \ + 0x3e, 0x84, 0xef, 0xec, \ + 0x7c, 0x0e, 0xb4, 0x93, \ + 0x7e, 0x8e, 0xc4, 0x87, \ + 0xe4, 0x30, 0x00, 0xf0, \ + 0x64, 0x20, 0x04, 0xf0, \ + 0x45, 0xd8, 0x8b, 0x11, \ + 0x92, 0x3b, 0x68, 0x00, \ + 0x04, 0x00, 0x7c, 0x14, \ + 0x7e, 0x34, 0xe4, 0x28, \ + 0xa6, 0x13, 0xd6, 0x2d, \ + 0x84, 0x25, 0xfe, 0x1e, \ + 0x83, 0x31, 0x36, 0x23, \ + 0xfb, 0x88, 0x44, 0x02, \ + 0xa4, 0x03, 0x25, 0x3f, \ + 0xe1, 0x84, 0x08, 0x34, \ + 0x46, 0x1d, 0xc8, 0x3b, \ + 0x46, 0x42, 0xff, 0x31, \ + 0x02, 0x34, 0x90, 0x84, \ + 0xfe, 0x1e, 0xe9, 0x88, \ + 0x8e, 0x65, 0xa1, 0x1b, \ + 0x08, 0x14, 0x1b, 0x24, \ + 0x48, 0x3b, 0xe1, 0x84, \ + 0x40, 0x06, 0xa0, 0xb3, \ + 0x46, 0x2d, 0xe0, 0x00, \ + 0x46, 0x42, 0xff, 0x31, \ + 0xb6, 0x03, 0x10, 0x88, \ + 0xe0, 0x06, 0x42, 0x3a, \ + 0xf6, 0x80, 0x04, 0x14, \ + 0xfb, 0x88, 0x44, 0x02, \ + 0x70, 0xac, 0x40, 0x22, \ + 0x7c, 0x1e, 0xa5, 0x1f, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x0f, 0x1b, 0x82, 0xa5, \ + 0x7f, 0xfd, 0x45, 0xfe, \ + 0x02, 0x10, 0xdf, 0xff, \ + 0x00, 0x00, 0x44, 0x04, \ + 0x04, 0x14, 0xec, 0x04, \ + 0x7c, 0x2e, 0xc1, 0x84, \ + 0x42, 0x1a, 0x84, 0x1b, \ + 0x08, 0x04, 0x70, 0x0a, \ + 0x85, 0x01, 0xe1, 0x84, \ + 0x44, 0x1a, 0xa9, 0x0f, \ + 0x06, 0x04, 0x66, 0x9a, \ + 0x46, 0x0b, 0xe1, 0x84, \ + 0xe4, 0x04, 0x02, 0x40, \ + 0xc0, 0x2d, 0x1f, 0x04, \ + 0x4e, 0x22, 0x7c, 0x2e, \ + 0xe3, 0x7f, 0x90, 0x8c, \ + 0x81, 0x71, 0x00, 0x7e, \ + 0xa0, 0xf1, 0x0e, 0x0b, \ + 0xf6, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x04, 0x04, 0x0c, \ + 0x4c, 0x06, 0x00, 0x44, \ + 0x08, 0x54, 0xe0, 0x00, \ + 0xf1, 0x84, 0x4e, 0x62, \ + 0x18, 0x44, 0xa6, 0x63, \ + 0x0a, 0x54, 0xf6, 0x80, \ + 0x58, 0x72, 0x76, 0x80, \ + 0x39, 0x4f, 0xfb, 0xb0, \ + 0xfb, 0x94, 0x4e, 0x62, \ + 0x4a, 0xc2, 0x00, 0x4e, \ + 0x08, 0x64, 0xf0, 0x84, \ + 0xf0, 0x8c, 0x48, 0x72, \ + 0xaf, 0x6f, 0xc8, 0x5d, \ + 0x82, 0xa5, 0x88, 0x6e, \ + 0x83, 0x21, 0x26, 0x53, \ + 0x42, 0x52, 0x00, 0x34, \ + 0xa3, 0x2f, 0xf1, 0x9c, \ + 0xc4, 0x5b, 0xc3, 0x27, \ + 0x64, 0x14, 0x47, 0x4a, \ + 0x81, 0xe5, 0x84, 0x33, \ + 0x83, 0x01, 0x22, 0x43, \ + 0x71, 0xb0, 0x42, 0x42, \ + 0xa3, 0x0f, 0x8a, 0x4b, \ + 0xc0, 0x3b, 0xc3, 0x07, \ + 0x64, 0x0c, 0x4d, 0x4a, \ + 0xf0, 0x98, 0x46, 0x02, \ + 0x47, 0xfe, 0x84, 0x0b, \ + 0xe3, 0x3f, 0x9f, 0xfc, \ + 0x88, 0x0b, 0x00, 0x3e, \ + 0xf0, 0x04, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0x84, 0x03, 0x04, 0x13, \ + 0x61, 0x8a, 0x40, 0x0a, \ + 0x42, 0x1a, 0x82, 0x11, \ + 0x04, 0x20, 0x61, 0x8a, \ + 0x18, 0x04, 0xec, 0x04, \ + 0x0c, 0x04, 0x60, 0x88, \ + 0x08, 0xc5, 0xe0, 0x8c, \ + 0x4e, 0x52, 0x7c, 0x30, \ + 0x0a, 0x65, 0xf0, 0x98, \ + 0x85, 0x0d, 0x7c, 0x18, \ + 0xca, 0xc7, 0x9e, 0x84, \ + 0x12, 0xc4, 0xa6, 0xc3, \ + 0x8c, 0xc3, 0x61, 0x7c, \ + 0xf2, 0xa0, 0x4c, 0x92, \ + 0x0c, 0x04, 0x1e, 0x6e, \ + 0x1c, 0xce, 0xe0, 0x84, \ + 0x0c, 0xcb, 0x8a, 0x63, \ + 0x00, 0x06, 0x69, 0x00, \ + 0x00, 0x00, 0x80, 0x00, \ + 0x83, 0x0b, 0xc4, 0x04, \ + 0x4c, 0xc3, 0x82, 0xc1, \ + 0xf6, 0xd5, 0xe9, 0x00, \ + 0xf0, 0x1c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0x61, 0xfc, 0x18, 0x24, \ + 0x04, 0xc3, 0xa2, 0x03, \ + 0x03, 0xc7, 0x86, 0x03, \ + 0x0b, 0xc4, 0x81, 0x81, \ + 0x4c, 0x0a, 0xb8, 0x03, \ + 0x00, 0x34, 0x61, 0x8e, \ + 0x09, 0x04, 0xe0, 0x88, \ + 0xf0, 0x80, 0x4e, 0x42, \ + 0x0c, 0x13, 0x94, 0x8e, \ + 0x42, 0x1a, 0xc4, 0x67, \ + 0x88, 0x63, 0x61, 0x8e, \ + 0xb0, 0x8e, 0x80, 0x8e, \ + 0xc4, 0x17, 0x02, 0x23, \ + 0x61, 0x8e, 0x46, 0x2a, \ + 0xf0, 0x84, 0x44, 0x42, \ + 0x42, 0x42, 0xc4, 0x37, \ + 0x06, 0x70, 0x70, 0x8c, \ + 0x82, 0x7b, 0xec, 0x10, \ + 0x14, 0x94, 0xd9, 0x84, \ + 0x10, 0x93, 0xc6, 0x6d, \ + 0xdc, 0x00, 0x4c, 0x40, \ + 0xdd, 0x84, 0x94, 0x9e, \ + 0xc6, 0x7d, 0x30, 0x94, \ + 0x4e, 0x40, 0x10, 0x93, \ + 0x30, 0x9e, 0xdc, 0x00, \ + 0xfc, 0x35, 0x85, 0x6b, \ + 0xc9, 0xc4, 0x80, 0x54, \ + 0x98, 0x53, 0xc6, 0x2d, \ + 0x44, 0x40, 0x00, 0x5e, \ + 0x83, 0x2b, 0xdc, 0x00, \ + 0x0c, 0x0c, 0xfe, 0x35, \ + 0xdd, 0x93, 0xf0, 0x1c, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0x4c, 0x42, 0x08, 0x13, \ + 0x4c, 0x6a, 0xf0, 0x80, \ + 0x82, 0x11, 0x61, 0x92, \ + 0x61, 0x92, 0x42, 0x1a, \ + 0xe0, 0x88, 0x18, 0x44, \ + 0xd6, 0x07, 0x7e, 0x3e, \ + 0xc4, 0x67, 0x19, 0x34, \ + 0x4f, 0xfe, 0x44, 0x17, \ + 0xc0, 0x01, 0x7f, 0xfd, \ + 0x52, 0x52, 0x0a, 0xc3, \ + 0x3e, 0x3e, 0xf0, 0x84, \ + 0xf0, 0x98, 0x54, 0x52, \ + 0x06, 0xc0, 0x0c, 0x7e, \ + 0x7c, 0x5e, 0xec, 0x10, \ + 0xfc, 0xa8, 0x16, 0x04, \ + 0x81, 0xc1, 0x01, 0x41, \ + 0x81, 0x11, 0x01, 0x81, \ + 0xe9, 0x64, 0x83, 0xf5, \ + 0xc6, 0xad, 0x7e, 0x04, \ + 0x1e, 0xa5, 0xca, 0x67, \ + 0x4e, 0x62, 0x40, 0x08, \ + 0x21, 0x7d, 0xf1, 0x80, \ + 0x0c, 0x06, 0x8e, 0x6b, \ + 0xa0, 0x2d, 0x40, 0x00, \ + 0x9c, 0x00, 0x4c, 0x70, \ + 0x61, 0x7c, 0x0e, 0x64, \ + 0x40, 0x6a, 0x8c, 0x43, \ + 0x58, 0x72, 0x64, 0x2e, \ + 0x4c, 0x6a, 0xf2, 0xb0, \ + 0x80, 0x83, 0xe4, 0xae, \ + 0x72, 0x84, 0x42, 0x62, \ + 0x64, 0x00, 0x16, 0xb0, \ + 0x93, 0xab, 0xc0, 0x08, \ + 0x18, 0xca, 0xf8, 0x35, \ + 0x18, 0x1a, 0xe0, 0x00, \ + 0x08, 0x2a, 0xe0, 0x00, \ + 0x42, 0x0a, 0xe0, 0x00, \ + 0x42, 0x1a, 0xe5, 0x80, \ + 0x42, 0x0a, 0xe1, 0x88, \ + 0x08, 0x45, 0xed, 0x80, \ + 0x9f, 0xfc, 0x41, 0xfe, \ + 0x61, 0x84, 0x00, 0x44, \ + 0xe0, 0xfc, 0x04, 0xc4, \ + 0x04, 0xc4, 0x24, 0x0f, \ + 0x10, 0x03, 0xe1, 0x84, \ + 0x42, 0x12, 0x98, 0x3b, \ + 0x88, 0x2b, 0x72, 0x88, \ + 0xef, 0xe8, 0x70, 0x48, \ + 0xe0, 0x00, 0x03, 0x4a, \ + 0x02, 0x1a, 0x3e, 0x14, \ + 0x00, 0x6a, 0xe0, 0x00, \ + 0x42, 0x0a, 0xe0, 0x00, \ + 0x42, 0x1a, 0xe5, 0x80, \ + 0x42, 0x0a, 0xe1, 0x88, \ + 0x00, 0x06, 0xed, 0x80, \ + 0x04, 0x0e, 0xc0, 0x00, \ + 0xa0, 0xf1, 0x81, 0x01, \ + 0xf6, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0x4c, 0x52, 0x0d, 0x04, \ + 0x0c, 0x23, 0xf0, 0x80, \ + 0xf0, 0x94, 0x4e, 0x12, \ + 0x61, 0x96, 0x58, 0x2a, \ + 0xec, 0x04, 0x10, 0x50, \ + 0xc4, 0x87, 0x02, 0x71, \ + 0x18, 0x33, 0x8f, 0x24, \ + 0x46, 0x3a, 0xc4, 0xc7, \ + 0x42, 0x02, 0x61, 0x96, \ + 0x4c, 0x6a, 0xf0, 0xb0, \ + 0x82, 0x9b, 0x61, 0x96, \ + 0x61, 0x96, 0x4e, 0x7a, \ + 0xc4, 0x37, 0x81, 0x51, \ + 0x70, 0x8c, 0x58, 0x02, \ + 0xf0, 0xa0, 0x46, 0x02, \ + 0xe5, 0xa4, 0x81, 0x65, \ + 0x94, 0x53, 0xc6, 0x9d, \ + 0xdc, 0x00, 0x52, 0x00, \ + 0xfe, 0x55, 0x99, 0x9b, \ + 0xa3, 0x4d, 0x44, 0x67, \ + 0xc4, 0x77, 0x04, 0x63, \ + 0x80, 0x00, 0x0c, 0x95, \ + 0x52, 0x92, 0x02, 0x65, \ + 0xff, 0x91, 0x90, 0x94, \ + 0x60, 0x24, 0x49, 0x1a, \ + 0x0c, 0x95, 0x8a, 0x9b, \ + 0x52, 0x92, 0xa0, 0x00, \ + 0x18, 0x9e, 0xb1, 0x94, \ + 0x04, 0x83, 0x84, 0x73, \ + 0x83, 0xd5, 0xff, 0x91, \ + 0xc5, 0xb4, 0xf1, 0xa4, \ + 0x4a, 0xa2, 0x8a, 0xa3, \ + 0x0c, 0xa5, 0xf1, 0xac, \ + 0x02, 0x45, 0x80, 0x00, \ + 0x90, 0x94, 0x54, 0xa2, \ + 0x60, 0x24, 0x49, 0x1a, \ + 0x0c, 0xa5, 0x8a, 0xab, \ + 0x54, 0xa2, 0xa0, 0x00, \ + 0x18, 0xae, 0xb1, 0x94, \ + 0xe4, 0x0c, 0x59, 0x1a, \ + 0x3c, 0x01, 0x58, 0x00, \ + 0x43, 0x1a, 0x40, 0x00, \ + 0x42, 0x00, 0xe4, 0x0c, \ + 0xc0, 0x00, 0x3c, 0x01, \ + 0x64, 0x10, 0x14, 0x60, \ + 0x55, 0x1a, 0x84, 0x6b, \ + 0x4c, 0xa0, 0xe4, 0x20, \ + 0x4c, 0x1a, 0xbc, 0x00, \ + 0x3d, 0xb8, 0xe0, 0x1c, \ + 0xc3, 0xff, 0x3f, 0xfd, \ + 0x0c, 0x0c, 0x81, 0x01, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0xca, 0x37, 0x00, 0x8b, \ + 0xe1, 0xfc, 0x14, 0x84, \ + 0x00, 0xa4, 0x90, 0xa3, \ + 0x80, 0x33, 0xe1, 0x04, \ + 0x60, 0xa2, 0x40, 0x3a, \ + 0xe1, 0x7c, 0x06, 0x54, \ + 0x61, 0x7c, 0x18, 0x04, \ + 0x58, 0xc2, 0xaa, 0x03, \ + 0xff, 0x51, 0x73, 0x8c, \ + 0xf9, 0xb0, 0x46, 0x02, \ + 0xa1, 0x3b, 0x17, 0x34, \ + 0x1f, 0xfd, 0x41, 0xfe, \ + 0x4f, 0xfe, 0x00, 0x01, \ + 0x8e, 0xe5, 0x9f, 0xfc, \ + 0x02, 0x44, 0x3e, 0x1e, \ + 0x0c, 0x24, 0xe1, 0x7c, \ + 0xa4, 0x43, 0x61, 0x7c, \ + 0x73, 0x98, 0x56, 0x12, \ + 0x0a, 0xbd, 0x81, 0x71, \ + 0xf1, 0x80, 0x52, 0x72, \ + 0x80, 0x5b, 0x7c, 0x3e, \ + 0x61, 0xc0, 0x0c, 0x44, \ + 0x06, 0x04, 0xe0, 0xb7, \ + 0x04, 0xc4, 0x61, 0xfc, \ + 0x42, 0x72, 0xe0, 0x84, \ + 0x4a, 0x90, 0x73, 0xb0, \ + 0x60, 0x47, 0x9c, 0x00, \ + 0x04, 0x04, 0xa4, 0x3f, \ + 0xac, 0xbf, 0x60, 0x84, \ + 0x1c, 0x00, 0x58, 0x10, \ + 0xf0, 0x90, 0x40, 0x52, \ + 0x6f, 0xe8, 0x64, 0x08, \ + 0xf2, 0xac, 0x42, 0xc2, \ + 0x02, 0xa4, 0x95, 0xb4, \ + 0xd6, 0xb7, 0x7c, 0x00, \ + 0xa2, 0x83, 0x3e, 0x14, \ + 0xa3, 0x8b, 0x7c, 0x34, \ + 0x04, 0x01, 0x00, 0x00, \ + 0xd6, 0x17, 0x80, 0x04, \ + 0x02, 0x04, 0x82, 0x03, \ + 0x96, 0x1f, 0xe1, 0x7c, \ + 0x40, 0x1a, 0x80, 0x13, \ + 0x00, 0x02, 0xe1, 0xae, \ + 0x00, 0x02, 0xe0, 0x00, \ + 0xc7, 0xff, 0x6b, 0xfd, \ + 0x8e, 0x0b, 0x0c, 0x0e, \ + 0x0c, 0x0c, 0x98, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0xa4, 0x13, 0x1b, 0x74, \ + 0xa4, 0x03, 0x09, 0x3e, \ + 0x8a, 0x9b, 0x1f, 0x64, \ + 0x46, 0x72, 0x1d, 0x84, \ + 0x56, 0x3a, 0xf0, 0x88, \ + 0x88, 0x2b, 0x61, 0x9e, \ + 0x7c, 0x4e, 0x99, 0xa4, \ + 0x16, 0x13, 0x8e, 0x4b, \ + 0x7e, 0x1e, 0x96, 0x03, \ + 0x21, 0xb4, 0xa0, 0x13, \ + 0x3c, 0x6e, 0x93, 0x1d, \ + 0xc4, 0x04, 0x12, 0x10, \ + 0x70, 0xa4, 0x42, 0x02, \ + 0x02, 0x10, 0x94, 0x3b, \ + 0x90, 0x5b, 0x6c, 0x04, \ + 0xef, 0xfc, 0x69, 0x68, \ + 0x86, 0x15, 0xa1, 0x0b, \ + 0x94, 0x3b, 0x7e, 0x14, \ + 0x8e, 0x4b, 0x3c, 0xbe, \ + 0x90, 0x5b, 0x7c, 0x24, \ + 0xe4, 0x04, 0x00, 0x10, \ + 0x6f, 0xfc, 0x68, 0xa8, \ + 0xa1, 0x0b, 0xb2, 0x03, \ + 0x18, 0x04, 0x82, 0x75, \ + 0x40, 0x0a, 0xd6, 0x77, \ + 0x98, 0x0e, 0xe1, 0x1e, \ + 0x40, 0x0a, 0xac, 0x04, \ + 0x2c, 0x0e, 0xe1, 0x1e, \ + 0x98, 0x24, 0x81, 0x01, \ + 0xc2, 0x2d, 0xac, 0x14, \ + 0x40, 0x00, 0x4c, 0x15, \ + 0xdc, 0x00, 0x42, 0x20, \ + 0xc0, 0x00, 0x56, 0x25, \ + 0xc0, 0x00, 0x0c, 0x35, \ + 0x44, 0x30, 0x09, 0x34, \ + 0xa8, 0xf1, 0xdc, 0x00, \ + 0x4c, 0x35, 0xc6, 0x2d, \ + 0x16, 0x15, 0xc0, 0x00, \ + 0xc7, 0x1d, 0xc0, 0x00, \ + 0xc0, 0x00, 0x56, 0x35, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xf0, 0x1e, 0xf0, \ + 0x84, 0x9b, 0x11, 0x1e, \ + 0x93, 0x4e, 0x95, 0x5e, \ + 0x97, 0x3e, 0x8f, 0x0e, \ + 0xb3, 0x14, 0x8c, 0x08, \ + 0x19, 0x1e, 0xa1, 0x0b, \ + 0x9d, 0x1e, 0x81, 0x11, \ + 0x84, 0x45, 0x9b, 0x1e, \ + 0xe0, 0x04, 0x00, 0x02, \ + 0x85, 0xb3, 0x81, 0x13, \ + 0x8b, 0xf3, 0x99, 0x04, \ + 0xe5, 0x80, 0x00, 0x00, \ + 0xf8, 0x60, 0x40, 0x15, \ + 0xf8, 0x30, 0x40, 0x15, \ + 0x40, 0x15, 0xc1, 0x1e, \ + 0x40, 0x15, 0xe0, 0x2c, \ + 0x97, 0x04, 0xe0, 0x5c, \ + 0xa1, 0x0b, 0x82, 0x08, \ + 0x31, 0xb4, 0xa2, 0x25, \ + 0xaf, 0x84, 0x81, 0x71, \ + 0x13, 0x24, 0xad, 0xa4, \ + 0xe0, 0x84, 0x00, 0x74, \ + 0x7c, 0x00, 0x04, 0x43, \ + 0xf0, 0x80, 0x42, 0x22, \ + 0xe0, 0x90, 0x02, 0x23, \ + 0x82, 0x45, 0x84, 0x4d, \ + 0x06, 0x74, 0x19, 0x04, \ + 0x80, 0x33, 0xe0, 0x88, \ + 0x02, 0x55, 0x86, 0x1b, \ + 0x84, 0x04, 0xf0, 0x30, \ + 0x19, 0x04, 0x85, 0xf5, \ + 0xe0, 0x88, 0x06, 0x74, \ + 0x44, 0x42, 0x09, 0x2e, \ + 0x0d, 0x7e, 0xfa, 0x08, \ + 0x00, 0x33, 0x82, 0x21, \ + 0x06, 0x1b, 0xa8, 0x23, \ + 0xe6, 0x40, 0x0c, 0x30, \ + 0xf0, 0x30, 0x02, 0x55, \ + 0x02, 0xc5, 0x84, 0x04, \ + 0x05, 0x13, 0xe0, 0x60, \ + 0xec, 0x04, 0x04, 0x40, \ + 0x8b, 0x4e, 0x85, 0x33, \ + 0xe8, 0x00, 0x31, 0x3c, \ + 0x70, 0x88, 0x4e, 0xa2, \ + 0x82, 0x71, 0x82, 0x21, \ + 0xe1, 0xaa, 0x4e, 0x7a, \ + 0x10, 0x45, 0xc4, 0x77, \ + 0x88, 0x03, 0xfc, 0x1c, \ + 0xe4, 0x44, 0x84, 0x0e, \ + 0x8c, 0x5e, 0x88, 0x53, \ + 0xfc, 0x1c, 0x16, 0x45, \ + 0x98, 0xce, 0x88, 0xc3, \ + 0x8b, 0x44, 0x8d, 0x74, \ + 0x20, 0x51, 0x89, 0x24, \ + 0xe0, 0x88, 0x0c, 0x74, \ + 0x08, 0x44, 0x4b, 0x57, \ + 0x0c, 0x5e, 0xfb, 0x80, \ + 0x1e, 0x55, 0xca, 0x0d, \ + 0x40, 0x02, 0x40, 0x30, \ + 0x06, 0x74, 0xd1, 0x94, \ + 0x82, 0x21, 0x40, 0x88, \ + 0xfa, 0x80, 0x18, 0xa4, \ + 0xd0, 0x94, 0x46, 0x32, \ + 0x40, 0x60, 0x46, 0x05, \ + 0xf1, 0x90, 0x46, 0x22, \ + 0x06, 0x34, 0x0f, 0x04, \ + 0x00, 0x09, 0xfb, 0x00, \ + 0x19, 0x04, 0xe0, 0x30, \ + 0x80, 0x63, 0xa1, 0x0b, \ + 0xe6, 0x40, 0x00, 0x60, \ + 0xc0, 0x00, 0x00, 0x25, \ + 0x80, 0x00, 0x04, 0x06, \ + 0x00, 0x00, 0x40, 0x25, \ + 0x80, 0x00, 0x04, 0x06, \ + 0xa6, 0x04, 0x91, 0x44, \ + 0x50, 0x58, 0x84, 0x14, \ + 0xe4, 0xd0, 0x08, 0xf0, \ + 0x08, 0xf0, 0x7c, 0x4e, \ + 0xbe, 0x4e, 0xe4, 0xe0, \ + 0x95, 0x44, 0xbc, 0xce, \ + 0xe8, 0x00, 0x62, 0x28, \ + 0x82, 0x71, 0x21, 0x0b, \ + 0x80, 0x34, 0x1e, 0x15, \ + 0x80, 0xc0, 0x4c, 0x15, \ + 0x86, 0x18, 0x97, 0x14, \ + 0xde, 0xd5, 0x83, 0x7d, \ + 0x02, 0x0b, 0x80, 0x1b, \ + 0x0c, 0x0c, 0xf8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xed, 0xf0, 0x1e, 0xf0, \ + 0x1e, 0x05, 0xa9, 0x0e, \ + 0x97, 0x2e, 0xe0, 0xac, \ + 0xe0, 0xb4, 0x1e, 0x25, \ + 0x1e, 0x05, 0x9d, 0x0e, \ + 0x19, 0x5e, 0xe0, 0xa0, \ + 0xe6, 0x40, 0x18, 0x20, \ + 0x99, 0x51, 0x15, 0x1e, \ + 0x1e, 0x05, 0xa3, 0x0e, \ + 0x1e, 0x15, 0xe0, 0x9c, \ + 0x1e, 0x65, 0xe0, 0xb0, \ + 0x13, 0x0e, 0xe0, 0xa8, \ + 0x1e, 0xb5, 0x81, 0x01, \ + 0x1e, 0x75, 0xe0, 0xa4, \ + 0xbd, 0x0e, 0xe0, 0x98, \ + 0xb9, 0x0e, 0xbb, 0x0e, \ + 0xb5, 0x0e, 0xb7, 0x0e, \ + 0xb1, 0x0e, 0xb3, 0x0e, \ + 0x81, 0x45, 0xaf, 0x0e, \ + 0x0a, 0x50, 0x71, 0x0c, \ + 0xa1, 0x5b, 0xec, 0x04, \ + 0x29, 0x54, 0xfe, 0x95, \ + 0xe6, 0x40, 0x14, 0x20, \ + 0x12, 0x10, 0x1b, 0x7e, \ + 0x25, 0x6e, 0xe4, 0xc0, \ + 0xe5, 0x80, 0x0c, 0x40, \ + 0x65, 0x01, 0x18, 0x50, \ + 0x0a, 0x50, 0x40, 0x02, \ + 0xc0, 0x04, 0x66, 0x01, \ + 0x10, 0x10, 0x21, 0xce, \ + 0x1f, 0x5e, 0xe6, 0x40, \ + 0x7f, 0x41, 0xff, 0x21, \ + 0xa7, 0x3e, 0x81, 0x71, \ + 0x93, 0xb5, 0xab, 0xbe, \ + 0x02, 0x0a, 0xa4, 0x14, \ + 0xc7, 0xff, 0x63, 0xfd, \ + 0xa1, 0x0b, 0x8a, 0x05, \ + 0x25, 0xb4, 0x86, 0xf5, \ + 0xad, 0x4e, 0x96, 0x3b, \ + 0xf8, 0xc0, 0x0c, 0x03, \ + 0x78, 0x30, 0x0c, 0x13, \ + 0xa3, 0x24, 0x96, 0x4b, \ + 0x6b, 0x78, 0xa9, 0x54, \ + 0xa1, 0x0b, 0xe8, 0x00, \ + 0x0c, 0x03, 0x82, 0xb5, \ + 0x81, 0x21, 0x78, 0xc0, \ + 0x78, 0x30, 0x0c, 0x13, \ + 0xab, 0x34, 0x96, 0x4b, \ + 0x6a, 0xb8, 0xa1, 0x54, \ + 0xa1, 0x0b, 0xe8, 0x00, \ + 0x27, 0x34, 0x84, 0x45, \ + 0xab, 0xb4, 0xff, 0x21, \ + 0x85, 0x4b, 0xad, 0x44, \ + 0x0f, 0xfc, 0x43, 0xf6, \ + 0x9f, 0xfc, 0x49, 0xfe, \ + 0x80, 0x00, 0x54, 0x13, \ + 0x88, 0x91, 0x02, 0x71, \ + 0x84, 0x61, 0x04, 0xa1, \ + 0x84, 0x81, 0x0d, 0xa5, \ + 0x81, 0x21, 0x2b, 0xb4, \ + 0xf8, 0xc0, 0x0c, 0x03, \ + 0xf8, 0x30, 0x0c, 0x13, \ + 0x96, 0x3b, 0x1d, 0x44, \ + 0x68, 0xd8, 0x9f, 0x54, \ + 0x27, 0x34, 0xe8, 0x00, \ + 0x2d, 0x44, 0xa1, 0x0b, \ + 0xf8, 0x95, 0xff, 0x21, \ + 0x04, 0xf0, 0x20, 0x02, \ + 0x18, 0x12, 0xe5, 0xa0, \ + 0xe5, 0xd0, 0x06, 0xf0, \ + 0x0a, 0xf0, 0x3e, 0x2e, \ + 0x11, 0x3e, 0xe5, 0xb0, \ + 0x40, 0x0a, 0x96, 0x03, \ + 0x9b, 0xb4, 0xe1, 0xae, \ + 0xad, 0x0e, 0xbc, 0x3e, \ + 0x96, 0x2b, 0x2b, 0x34, \ + 0x4c, 0xd8, 0xa1, 0x44, \ + 0x18, 0x12, 0xef, 0xfc, \ + 0xe5, 0xc0, 0x00, 0xf0, \ + 0x0a, 0xf0, 0x3e, 0x0e, \ + 0x11, 0x04, 0xe5, 0xe0, \ + 0xab, 0x34, 0x96, 0x2b, \ + 0xbc, 0x0e, 0xa9, 0x44, \ + 0x86, 0xbb, 0x2d, 0x04, \ + 0xef, 0xfc, 0x4b, 0xe8, \ + 0x00, 0x09, 0x97, 0x04, \ + 0xa1, 0x0b, 0xe0, 0x30, \ + 0x00, 0xf0, 0x84, 0x05, \ + 0x04, 0xf0, 0x65, 0x70, \ + 0x18, 0x12, 0xe5, 0x80, \ + 0xe5, 0x90, 0x0a, 0xf0, \ + 0x96, 0x3b, 0x3e, 0x0e, \ + 0xad, 0x04, 0xbc, 0x2e, \ + 0x9f, 0x44, 0x9b, 0x24, \ + 0xef, 0xfc, 0x4a, 0xa8, \ + 0x95, 0x14, 0xbd, 0x04, \ + 0xad, 0x0e, 0x99, 0x24, \ + 0xb3, 0xb4, 0xbb, 0x04, \ + 0x91, 0x0e, 0xb5, 0x34, \ + 0x8d, 0xae, 0x84, 0x1a, \ + 0x93, 0x24, 0x8b, 0x2e, \ + 0xb1, 0xc4, 0xaf, 0x04, \ + 0xfe, 0x0e, 0x89, 0x2e, \ + 0xab, 0xb4, 0xfc, 0xbe, \ + 0xbc, 0x3e, 0xbe, 0xce, \ + 0xb7, 0x54, 0xb9, 0x44, \ + 0xad, 0x24, 0x9b, 0x04, \ + 0x52, 0x18, 0x91, 0x34, \ + 0x00, 0x4b, 0xe8, 0x14, \ + 0x27, 0x34, 0x81, 0x01, \ + 0xf1, 0xc5, 0xff, 0x21, \ + 0x83, 0x7d, 0x8e, 0x18, \ + 0x1e, 0xf0, 0xd8, 0x95, \ + 0x0c, 0x0c, 0xe5, 0xf0, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x82, 0x8b, 0x3e, 0x2e, \ + 0x00, 0x50, 0x00, 0x9b, \ + 0x19, 0x11, 0xe7, 0x60, \ + 0x81, 0x85, 0x81, 0x21, \ + 0x78, 0x60, 0x40, 0x23, \ + 0xec, 0x04, 0x02, 0x10, \ + 0xf8, 0x30, 0x40, 0x23, \ + 0xa1, 0x1b, 0xc1, 0x2c, \ + 0x0c, 0x50, 0xfe, 0x15, \ + 0x0e, 0x40, 0x67, 0x60, \ + 0x7c, 0x3e, 0xe4, 0xc0, \ + 0xbe, 0xb4, 0x81, 0xa1, \ + 0x9c, 0x04, 0x85, 0xd5, \ + 0x63, 0xfd, 0x00, 0x0a, \ + 0x88, 0xa5, 0xc7, 0xff, \ + 0x78, 0x30, 0x0e, 0x15, \ + 0xa0, 0x24, 0x96, 0x0b, \ + 0xe0, 0x42, 0xa4, 0x3a, \ + 0x42, 0x98, 0xe4, 0x58, \ + 0x09, 0x0e, 0xe8, 0x00, \ + 0x9c, 0x14, 0x96, 0x0b, \ + 0xa4, 0x3a, 0xa0, 0x24, \ + 0xe4, 0x58, 0xe0, 0x42, \ + 0xe8, 0x00, 0x42, 0x08, \ + 0x96, 0x0b, 0x7e, 0x0e, \ + 0xe0, 0x30, 0x0e, 0x15, \ + 0xa4, 0x3a, 0xa0, 0x24, \ + 0xe4, 0x58, 0xe0, 0x42, \ + 0xe8, 0x00, 0x41, 0x68, \ + 0xfc, 0x34, 0xfe, 0x14, \ + 0xf8, 0x60, 0x4c, 0x13, \ + 0x98, 0x0c, 0x89, 0x14, \ + 0xf8, 0x30, 0x4c, 0x13, \ + 0x84, 0x61, 0x02, 0xa1, \ + 0x8e, 0x08, 0x88, 0x71, \ + 0xf4, 0x55, 0x81, 0xad, \ + 0xa8, 0xf1, 0x01, 0x01, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x18, 0x0e, 0x24, 0x13, \ + 0x44, 0x1a, 0xf0, 0x00, \ + 0x40, 0x1a, 0xe4, 0x82, \ + 0x02, 0x24, 0xe4, 0x02, \ + 0x82, 0xcf, 0xe1, 0x7c, \ + 0x81, 0x01, 0x00, 0xc3, \ + 0xf2, 0x84, 0x42, 0x22, \ + 0x64, 0x01, 0x04, 0xc0, \ + 0x42, 0x12, 0xc0, 0x80, \ + 0x18, 0x14, 0xf2, 0x80, \ + 0xe1, 0xc7, 0xe1, 0x7c, \ + 0x42, 0x12, 0x98, 0x23, \ + 0xe1, 0x27, 0x72, 0x80, \ + 0xa2, 0x2f, 0xe0, 0x17, \ + 0x08, 0x0e, 0x08, 0x23, \ + 0x42, 0x2a, 0xff, 0x00, \ + 0x44, 0x2a, 0xe4, 0x8e, \ + 0x06, 0x14, 0xe4, 0x0e, \ + 0x86, 0x4f, 0xe1, 0x7c, \ + 0x42, 0x12, 0x88, 0x23, \ + 0x04, 0x20, 0xf2, 0x8c, \ + 0xc0, 0x08, 0x64, 0x01, \ + 0xf2, 0x80, 0x42, 0x12, \ + 0xe1, 0x7c, 0x06, 0x14, \ + 0x86, 0x23, 0xe9, 0x37, \ + 0x72, 0x80, 0x42, 0x12, \ + 0xe8, 0x17, 0xd9, 0x27, \ + 0x02, 0x0e, 0x22, 0x2f, \ + 0x42, 0x22, 0xf8, 0x00, \ + 0x02, 0x12, 0xfa, 0x04, \ + 0xc0, 0x7f, 0x6b, 0xfd, \ + 0x40, 0x10, 0xa1, 0x1d, \ + 0xa0, 0x5d, 0x1c, 0x00, \ + 0x40, 0x10, 0x5d, 0x93, \ + 0xc1, 0xc3, 0x9c, 0x00, \ + 0xfa, 0x3c, 0x04, 0xcc, \ + 0x88, 0x6b, 0x00, 0x8b, \ + 0x60, 0x2c, 0x10, 0x09, \ + 0x01, 0xa1, 0x83, 0x41, \ + 0x20, 0x34, 0xc8, 0x27, \ + 0x21, 0x4f, 0x86, 0x7b, \ + 0x0f, 0xae, 0x82, 0x0b, \ + 0x0d, 0xae, 0x84, 0x1b, \ + 0x6b, 0x58, 0x88, 0x2b, \ + 0x58, 0xae, 0xe8, 0x1c, \ + 0x1a, 0xae, 0x80, 0x9b, \ + 0xe4, 0x70, 0x04, 0xf0, \ + 0x92, 0x1b, 0x0e, 0x0b, \ + 0xe8, 0x1c, 0x70, 0x68, \ + 0x84, 0x35, 0xa1, 0x0b, \ + 0x06, 0xf0, 0x0f, 0x04, \ + 0x22, 0x14, 0xe4, 0x60, \ + 0x58, 0x0e, 0x92, 0x2b, \ + 0x6c, 0x58, 0x8e, 0x0b, \ + 0xa1, 0x0b, 0xe8, 0x1c, \ + 0x80, 0x18, 0x1e, 0x05, \ + 0x00, 0x04, 0x4c, 0x05, \ + 0x80, 0x00, 0x00, 0x06, \ + 0xf2, 0x3c, 0x0c, 0xcc, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x88, 0x9b, 0x00, 0x7b, \ + 0x60, 0x2c, 0x0e, 0x09, \ + 0x0e, 0x29, 0x84, 0x6b, \ + 0x83, 0x51, 0x60, 0x30, \ + 0x60, 0x10, 0x0e, 0x4b, \ + 0x21, 0x5f, 0x81, 0xb1, \ + 0x1c, 0x34, 0x86, 0xab, \ + 0x7c, 0xbe, 0x82, 0x0b, \ + 0xe8, 0x88, 0x04, 0x54, \ + 0x88, 0x1b, 0x3e, 0xbe, \ + 0xe8, 0x1c, 0x67, 0xc8, \ + 0x00, 0x90, 0x00, 0x8b, \ + 0x01, 0x95, 0xe4, 0xc0, \ + 0x41, 0xbe, 0x99, 0x11, \ + 0xec, 0x04, 0x02, 0x10, \ + 0xf8, 0x34, 0x40, 0xb5, \ + 0xe0, 0x2c, 0x40, 0xb5, \ + 0xfe, 0x15, 0xa1, 0x1b, \ + 0x65, 0x80, 0x14, 0xa0, \ + 0x05, 0xc5, 0xe0, 0x91, \ + 0xa1, 0x0b, 0x81, 0x01, \ + 0x28, 0x04, 0x88, 0xd5, \ + 0xe4, 0x20, 0x04, 0xf0, \ + 0x68, 0x1c, 0x6b, 0xc8, \ + 0xa1, 0x0b, 0x90, 0x1b, \ + 0x7c, 0x04, 0x86, 0xd5, \ + 0xe4, 0x10, 0x06, 0xf0, \ + 0xa4, 0x0e, 0x90, 0x2b, \ + 0x9e, 0x14, 0xa8, 0x04, \ + 0xe8, 0x1c, 0x67, 0xa8, \ + 0x84, 0x75, 0xa1, 0x0b, \ + 0x06, 0xf0, 0x3e, 0x04, \ + 0x90, 0x2b, 0xe4, 0x10, \ + 0xf8, 0x30, 0x52, 0x05, \ + 0xe0, 0x60, 0x14, 0x05, \ + 0x66, 0xd8, 0x9e, 0x14, \ + 0xa1, 0x0b, 0xe8, 0x1c, \ + 0x80, 0x04, 0x1e, 0x05, \ + 0x00, 0x30, 0x52, 0x05, \ + 0x80, 0x00, 0x00, 0x06, \ + 0x88, 0xa1, 0x02, 0xb1, \ + 0x9a, 0x18, 0x88, 0x91, \ + 0xf4, 0x95, 0x83, 0xbd, \ + 0x0c, 0x0c, 0x98, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x4c, \ + 0x84, 0x7b, 0x11, 0x24, \ + 0x88, 0x9b, 0x15, 0xa4, \ + 0xff, 0x81, 0x13, 0xb4, \ + 0x41, 0xf6, 0xa1, 0x2b, \ + 0x4d, 0xfe, 0x0f, 0xfc, \ + 0x8a, 0x05, 0x9f, 0xfc, \ + 0x44, 0x2a, 0x2b, 0x3f, \ + 0x46, 0x3a, 0xe6, 0x8a, \ + 0x81, 0x61, 0x66, 0x8e, \ + 0x42, 0x3a, 0x8f, 0x1e, \ + 0x46, 0x3a, 0xe5, 0x02, \ + 0x00, 0x24, 0x65, 0x82, \ + 0x82, 0x03, 0xe1, 0x84, \ + 0x72, 0x98, 0x42, 0x32, \ + 0x7a, 0x78, 0x81, 0x31, \ + 0x0f, 0x24, 0xef, 0xe4, \ + 0x41, 0xfe, 0x91, 0x0b, \ + 0xa1, 0x1b, 0x1f, 0xfc, \ + 0x5f, 0xfc, 0x41, 0xfe, \ + 0x02, 0x94, 0xa1, 0x9b, \ + 0x81, 0x81, 0x41, 0x84, \ + 0x50, 0x9c, 0x42, 0x12, \ + 0x50, 0x1a, 0x84, 0x73, \ + 0x80, 0x73, 0x40, 0x26, \ + 0x40, 0x8a, 0x8e, 0x83, \ + 0xac, 0x8e, 0xf0, 0x0a, \ + 0x8c, 0x0b, 0x28, 0x0e, \ + 0xf2, 0x7c, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xe6, 0x82, 0x40, 0x1a, \ + 0x64, 0x01, 0x00, 0x00, \ + 0xdf, 0x07, 0xc0, 0x40, \ + 0x6f, 0xfd, 0x00, 0x02, \ + 0x5d, 0x93, 0xc0, 0x3f, \ + 0xfb, 0x80, 0x00, 0x04, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0xec, 0x60, 0x1e, 0xf0, \ + 0x82, 0x7b, 0x04, 0x9b, \ + 0x81, 0x61, 0x00, 0x5b, \ + 0x81, 0x11, 0x12, 0x0b, \ + 0x81, 0xb1, 0x01, 0xa1, \ + 0x03, 0x0a, 0x81, 0xc5, \ + 0x82, 0x75, 0xe2, 0x3c, \ + 0xf0, 0x08, 0x00, 0x25, \ + 0xf8, 0x04, 0x00, 0x45, \ + 0x56, 0x42, 0x84, 0xa3, \ + 0x82, 0x11, 0x72, 0xac, \ + 0xfc, 0x95, 0x8f, 0x1d, \ + 0x60, 0x00, 0x17, 0x4a, \ + 0xe1, 0x84, 0x00, 0xa4, \ + 0x60, 0x00, 0x16, 0x1a, \ + 0xe0, 0xfc, 0x02, 0xb4, \ + 0x60, 0x00, 0x14, 0x6a, \ + 0x7e, 0x0e, 0xa2, 0x0f, \ + 0xe5, 0x80, 0x42, 0x0a, \ + 0x61, 0x88, 0x42, 0x1a, \ + 0xe1, 0x84, 0x00, 0xb4, \ + 0x42, 0x2a, 0x7c, 0x0e, \ + 0x3e, 0x3e, 0xed, 0x80, \ + 0xe4, 0x30, 0x10, 0x30, \ + 0x5e, 0x27, 0x89, 0x5e, \ + 0x89, 0xa5, 0xe0, 0x5c, \ + 0x60, 0x1c, 0x4c, 0xca, \ + 0x44, 0x0a, 0x81, 0x01, \ + 0x40, 0x0a, 0xe1, 0x80, \ + 0x8e, 0xe5, 0xe9, 0x9c, \ + 0x81, 0x01, 0x26, 0x24, \ + 0x04, 0xca, 0xa4, 0x14, \ + 0x04, 0x1a, 0xe0, 0x00, \ + 0x02, 0x2a, 0xe0, 0x00, \ + 0x42, 0x0a, 0xe0, 0x00, \ + 0x42, 0x1a, 0xe5, 0x80, \ + 0x1e, 0x27, 0xe1, 0x88, \ + 0x42, 0x0a, 0xe0, 0x5c, \ + 0x8a, 0xa5, 0xed, 0x80, \ + 0x64, 0x7c, 0x0b, 0x0a, \ + 0x00, 0x50, 0x82, 0x3b, \ + 0x46, 0x20, 0x6c, 0x80, \ + 0x44, 0x52, 0x9c, 0x00, \ + 0x02, 0x06, 0x7b, 0x80, \ + 0x00, 0x22, 0xc0, 0x00, \ + 0x20, 0x23, 0xec, 0x7c, \ + 0xef, 0x80, 0x08, 0x14, \ + 0x6c, 0x00, 0x0a, 0x04, \ + 0x7e, 0x04, 0xa1, 0x3f, \ + 0x48, 0x52, 0xa1, 0x1f, \ + 0x04, 0x22, 0x78, 0x90, \ + 0xa8, 0x3f, 0xec, 0x04, \ + 0x82, 0x03, 0x7c, 0x14, \ + 0x94, 0x2b, 0x25, 0x3f, \ + 0x72, 0x84, 0x42, 0x32, \ + 0x70, 0xd8, 0x96, 0x3b, \ + 0x09, 0x54, 0xef, 0xe4, \ + 0x67, 0xfd, 0x00, 0x8a, \ + 0x41, 0xf6, 0xc0, 0xff, \ + 0xa1, 0x1b, 0x0f, 0xfc, \ + 0xcf, 0xfc, 0x41, 0xf6, \ + 0x82, 0x61, 0x61, 0x0c, \ + 0x0c, 0x0a, 0x90, 0x91, \ + 0xec, 0xb5, 0xe2, 0x40, \ + 0xb0, 0xf1, 0x3e, 0x04, \ + 0xe0, 0x18, 0x40, 0x71, \ + 0xe0, 0x1c, 0x40, 0x51, \ + 0xf6, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x00, 0x04, 0x4c, \ + 0x02, 0x10, 0x0a, 0x28, \ + 0x08, 0x14, 0xec, 0x04, \ + 0xe0, 0x31, 0x61, 0xfc, \ + 0x83, 0x41, 0x08, 0x13, \ + 0x83, 0x17, 0x03, 0xa5, \ + 0x78, 0x30, 0x06, 0x5b, \ + 0xec, 0x04, 0x04, 0x20, \ + 0x82, 0x60, 0x80, 0xc0, \ + 0x46, 0xc3, 0x8a, 0xc3, \ + 0xcd, 0x5a, 0xf8, 0x60, \ + 0x0a, 0xc4, 0x0a, 0x63, \ + 0x18, 0x64, 0xfb, 0x00, \ + 0x4a, 0x42, 0x7b, 0x00, \ + 0x46, 0x63, 0xf1, 0x94, \ + 0x98, 0x53, 0x60, 0x2c, \ + 0xfb, 0x14, 0x4a, 0x12, \ + 0xe0, 0x5c, 0x46, 0x53, \ + 0xf8, 0xf5, 0xa1, 0x2b, \ + 0x0c, 0x4c, 0x81, 0x01, \ + 0xdd, 0x93, 0xf0, 0x00, \ + 0xf8, 0x04, 0x04, 0x0c, \ + 0x60, 0x88, 0x0c, 0x34, \ + 0x01, 0x45, 0x8a, 0x7b, \ + 0xf0, 0x98, 0x58, 0x52, \ + 0xd9, 0x7d, 0xdd, 0x2e, \ + 0x06, 0x13, 0xfe, 0xd5, \ + 0x44, 0x0a, 0x86, 0x03, \ + 0x82, 0x11, 0x61, 0x8e, \ + 0x61, 0x8e, 0x46, 0x1a, \ + 0xf0, 0x98, 0x40, 0x42, \ + 0x0c, 0x34, 0xc4, 0x27, \ + 0x42, 0x42, 0x60, 0x88, \ + 0x46, 0x52, 0xf0, 0x88, \ + 0x44, 0x42, 0x70, 0x88, \ + 0x81, 0x95, 0xf0, 0x98, \ + 0xc1, 0x1d, 0xc5, 0x64, \ + 0x42, 0x40, 0xcd, 0x6e, \ + 0xd9, 0x3d, 0x1c, 0x00, \ + 0x9c, 0x00, 0x46, 0x50, \ + 0xfc, 0xf5, 0x85, 0x1b, \ + 0x0c, 0x0c, 0x81, 0x01, \ + 0xdd, 0x93, 0xf0, 0x04, \ + 0xe1, 0x00, 0x00, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x00, 0x34, 0x00, 0x8b, \ + 0x06, 0x03, 0xe1, 0xfc, \ + 0x1f, 0x14, 0x82, 0xbb, \ + 0x0d, 0x3e, 0x83, 0x07, \ + 0x1d, 0x34, 0x8a, 0x6b, \ + 0x23, 0x74, 0x84, 0x5b, \ + 0x21, 0xa4, 0xa0, 0x2b, \ + 0x3e, 0x1e, 0x82, 0x9b, \ + 0x0b, 0x4e, 0x90, 0x0b, \ + 0x7e, 0x2e, 0x81, 0x41, \ + 0x09, 0x3e, 0xaa, 0x23, \ + 0x3c, 0x3e, 0x96, 0x1b, \ + 0x7c, 0x5e, 0x8a, 0x3b, \ + 0x6c, 0x98, 0x8c, 0x5b, \ + 0xa1, 0x0b, 0xef, 0xf8, \ + 0x09, 0x04, 0x86, 0x75, \ + 0x3e, 0xae, 0x81, 0x41, \ + 0x7e, 0x24, 0x96, 0x1b, \ + 0x3c, 0x0e, 0x8c, 0x5b, \ + 0x8d, 0x34, 0x90, 0x0b, \ + 0xef, 0xf8, 0x6b, 0xc8, \ + 0x82, 0xd5, 0xa1, 0x0b, \ + 0x81, 0x41, 0x0d, 0x24, \ + 0x96, 0x1b, 0x7e, 0x04, \ + 0x8c, 0x5b, 0x3e, 0x7e, \ + 0x09, 0x04, 0xfc, 0x34, \ + 0x3c, 0x0e, 0x80, 0x23, \ + 0x6a, 0xd8, 0x90, 0x0b, \ + 0x16, 0x1b, 0xef, 0xf8, \ + 0x0b, 0x64, 0x96, 0x8d, \ + 0x09, 0x0e, 0x8c, 0xbb, \ + 0x0a, 0x55, 0x94, 0x4b, \ + 0x25, 0x94, 0x92, 0x0b, \ + 0xfa, 0x20, 0x42, 0x12, \ + 0xb0, 0x13, 0x82, 0x11, \ + 0xa7, 0x14, 0x83, 0x13, \ + 0x83, 0xb3, 0x8d, 0x1e, \ + 0xe8, 0x00, 0x31, 0xec, \ + 0x70, 0xa0, 0x46, 0xb2, \ + 0x46, 0x3a, 0x80, 0x1b, \ + 0x82, 0x81, 0x61, 0xae, \ + 0x00, 0xa5, 0xc4, 0x37, \ + 0x88, 0x0b, 0x7c, 0x0c, \ + 0x7c, 0x0c, 0x08, 0x55, \ + 0xf0, 0x8c, 0x44, 0x92, \ + 0xfc, 0x0c, 0x0e, 0xc5, \ + 0x71, 0x98, 0x48, 0xa2, \ + 0x0a, 0x43, 0x8c, 0xa3, \ + 0x38, 0x43, 0xb4, 0x53, \ + 0x08, 0x4e, 0x98, 0x53, \ + 0x0d, 0x14, 0x80, 0x4b, \ + 0x82, 0x33, 0x82, 0x0b, \ + 0x09, 0x04, 0x8c, 0x5e, \ + 0x0c, 0x0c, 0xb8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xd0, 0x1e, 0xf0, \ + 0x80, 0xbb, 0x29, 0x04, \ + 0x82, 0x8b, 0x2f, 0x74, \ + 0x43, 0xfe, 0x13, 0x4e, \ + 0xc0, 0x01, 0x7f, 0xfd, \ + 0x8a, 0x6b, 0x0f, 0x2e, \ + 0xb1, 0x44, 0xb3, 0x24, \ + 0x81, 0x01, 0x7e, 0x0e, \ + 0x9c, 0x0e, 0x91, 0x3e, \ + 0x97, 0x0e, 0x99, 0x0e, \ + 0x41, 0xf6, 0x15, 0x0e, \ + 0x8b, 0x4e, 0xe0, 0x0c, \ + 0xff, 0x41, 0x10, 0x0c, \ + 0x88, 0x1e, 0x8d, 0x2e, \ + 0xab, 0x54, 0xad, 0x94, \ + 0x89, 0x7e, 0x91, 0xa4, \ + 0xf0, 0xac, 0x40, 0x62, \ + 0xe1, 0x9a, 0x42, 0x0a, \ + 0x0a, 0x25, 0xc4, 0x17, \ + 0xa1, 0x2b, 0xfc, 0x04, \ + 0xa1, 0x2d, 0x84, 0x85, \ + 0x12, 0x15, 0x84, 0xc5, \ + 0xa1, 0x1d, 0xfc, 0x04, \ + 0x82, 0x01, 0x8e, 0x55, \ + 0xe1, 0x9a, 0x40, 0x0a, \ + 0x0a, 0x15, 0xc4, 0x07, \ + 0xa1, 0x1d, 0xfc, 0x00, \ + 0x12, 0x05, 0x8c, 0x55, \ + 0x88, 0x0d, 0xfc, 0x00, \ + 0x81, 0x95, 0x8a, 0xd5, \ + 0xfc, 0x04, 0x12, 0x05, \ + 0x8a, 0x25, 0xa1, 0x0b, \ + 0xfc, 0x04, 0x12, 0x05, \ + 0x88, 0xa5, 0x88, 0x0d, \ + 0x02, 0xf0, 0x03, 0x01, \ + 0x1c, 0x0e, 0xe4, 0xb0, \ + 0x0b, 0x04, 0x8a, 0x7b, \ + 0xe4, 0xc0, 0x0a, 0xf0, \ + 0x8c, 0x3b, 0x14, 0x2b, \ + 0x00, 0xf0, 0x00, 0xbc, \ + 0x3e, 0x0e, 0xe4, 0xa0, \ + 0xbc, 0x1e, 0x96, 0x0b, \ + 0xfe, 0x44, 0x8f, 0x14, \ + 0xef, 0xf8, 0x56, 0x28, \ + 0x96, 0x1b, 0x0d, 0x04, \ + 0x97, 0x34, 0x99, 0x24, \ + 0x94, 0x0b, 0x3e, 0x0e, \ + 0xbc, 0x6e, 0x95, 0x44, \ + 0x67, 0xd8, 0x93, 0x54, \ + 0x7f, 0x41, 0xef, 0xf8, \ + 0x09, 0x74, 0x8e, 0x5b, \ + 0x80, 0x75, 0x89, 0x0b, \ + 0x9c, 0x0e, 0x81, 0x01, \ + 0x91, 0xbd, 0x82, 0xb1, \ + 0x9c, 0x04, 0x80, 0x85, \ + 0xec, 0x25, 0xa1, 0x0b, \ + 0xe8, 0xf1, 0x01, 0x01, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x4c, \ + 0x82, 0x7b, 0x00, 0x6b, \ + 0x60, 0x14, 0x0c, 0x19, \ + 0xf4, 0x1d, 0xfb, 0x01, \ + 0x0c, 0x29, 0x94, 0x55, \ + 0xf0, 0x2d, 0xe0, 0x18, \ + 0x50, 0x2a, 0x92, 0xd5, \ + 0x11, 0x0a, 0xe6, 0x86, \ + 0x92, 0x35, 0xe5, 0x00, \ + 0x8e, 0x0b, 0x19, 0x21, \ + 0x6f, 0xe4, 0x7d, 0xa8, \ + 0x00, 0x70, 0x8c, 0x1b, \ + 0x02, 0x06, 0x64, 0x30, \ + 0x01, 0x55, 0xe0, 0x04, \ + 0x41, 0x9c, 0x81, 0x91, \ + 0xec, 0x04, 0x02, 0x10, \ + 0xfe, 0x95, 0xa1, 0x1b, \ + 0x64, 0x90, 0x00, 0x60, \ + 0x01, 0x95, 0x81, 0xb1, \ + 0x02, 0x8a, 0x81, 0x11, \ + 0x80, 0xe5, 0xe5, 0x00, \ + 0x82, 0x11, 0x41, 0x24, \ + 0xfa, 0xac, 0x56, 0x22, \ + 0xfe, 0x15, 0xd1, 0x1d, \ + 0x61, 0xd0, 0x00, 0xb4, \ + 0x0f, 0x0e, 0xd8, 0xb7, \ + 0x48, 0x61, 0x98, 0x71, \ + 0x03, 0xc5, 0x81, 0xa1, \ + 0x18, 0x24, 0x81, 0x01, \ + 0xa1, 0x2b, 0xff, 0x01, \ + 0x0f, 0x14, 0x84, 0x85, \ + 0xe1, 0x84, 0x00, 0x24, \ + 0x81, 0x31, 0x16, 0x03, \ + 0x6f, 0xe4, 0x56, 0xe8, \ + 0xf2, 0xa4, 0x42, 0x12, \ + 0x67, 0xfd, 0x00, 0x8a, \ + 0x41, 0xf6, 0xc0, 0xff, \ + 0xa1, 0x1b, 0x0f, 0xfc, \ + 0xcf, 0xfc, 0x41, 0xf6, \ + 0x81, 0x01, 0x1c, 0x0c, \ + 0x84, 0x71, 0x02, 0xa1, \ + 0xd1, 0xad, 0x88, 0x61, \ + 0x15, 0x0a, 0x80, 0xc5, \ + 0x80, 0x75, 0xe4, 0xfc, \ + 0xf8, 0x05, 0xa1, 0x0b, \ + 0xf2, 0x7c, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x80, 0x6b, 0x06, 0x4a, \ + 0x04, 0xc9, 0x84, 0x0a, \ + 0x4c, 0x45, 0xe1, 0x00, \ + 0x02, 0x49, 0xed, 0xac, \ + 0x4c, 0x05, 0xe0, 0x20, \ + 0xc4, 0x0a, 0xed, 0xa8, \ + 0xee, 0x24, 0x4c, 0x45, \ + 0xe1, 0x0c, 0x04, 0x49, \ + 0xe1, 0x04, 0x04, 0x79, \ + 0xed, 0xb0, 0x4c, 0x05, \ + 0xed, 0xc0, 0x4c, 0x45, \ + 0xe1, 0x10, 0x04, 0x41, \ + 0xe1, 0x08, 0x04, 0x09, \ + 0xed, 0xb4, 0x4c, 0xc5, \ + 0xed, 0xb8, 0x4c, 0x75, \ + 0xe1, 0x14, 0x04, 0xc1, \ + 0xe1, 0x18, 0x04, 0x71, \ + 0xed, 0xc4, 0x4c, 0x45, \ + 0xe1, 0x20, 0x04, 0x49, \ + 0xed, 0xbc, 0x4c, 0x05, \ + 0xe0, 0x2c, 0x04, 0x05, \ + 0xed, 0xc8, 0x4c, 0xc5, \ + 0xed, 0xcc, 0x4c, 0x75, \ + 0xed, 0xd4, 0x4c, 0x45, \ + 0xe1, 0x30, 0x04, 0x49, \ + 0xe1, 0x24, 0x04, 0xc9, \ + 0xe1, 0x2c, 0x04, 0x79, \ + 0xee, 0x28, 0x4c, 0x05, \ + 0xe1, 0x1c, 0x04, 0x01, \ + 0xed, 0xe4, 0x4c, 0x45, \ + 0xed, 0xd8, 0x4c, 0xc5, \ + 0xed, 0xe0, 0x4c, 0x75, \ + 0x8a, 0xc4, 0x88, 0x44, \ + 0xca, 0x84, 0xc8, 0x74, \ + 0xe0, 0x34, 0x04, 0x95, \ + 0xe0, 0x38, 0x04, 0xa5, \ + 0xed, 0xd0, 0x4c, 0x05, \ + 0xe1, 0x28, 0x04, 0x09, \ + 0xed, 0xec, 0x4c, 0x45, \ + 0xed, 0xf0, 0x4c, 0xc5, \ + 0xed, 0xf4, 0x4c, 0x75, \ + 0xed, 0xf8, 0x4c, 0x85, \ + 0xee, 0x48, 0x4c, 0x95, \ + 0xee, 0x4c, 0x4c, 0xa5, \ + 0xe0, 0x3c, 0x04, 0x45, \ + 0xe0, 0x10, 0x04, 0xc5, \ + 0xe0, 0x14, 0x04, 0x75, \ + 0xe0, 0x18, 0x04, 0x85, \ + 0xe0, 0x1c, 0x04, 0x95, \ + 0xe0, 0x20, 0x04, 0xa5, \ + 0x6d, 0xdc, 0x4c, 0x05, \ + 0x4c, 0xc5, 0x81, 0x01, \ + 0x4c, 0x75, 0xee, 0x6c, \ + 0x4c, 0x85, 0xee, 0x70, \ + 0x4c, 0xa5, 0xee, 0x74, \ + 0x4c, 0x05, 0xee, 0x7c, \ + 0x4c, 0x45, 0xed, 0xe8, \ + 0x4c, 0x95, 0xee, 0x50, \ + 0x8c, 0x44, 0xee, 0x78, \ + 0x04, 0xc5, 0xa1, 0x74, \ + 0x4c, 0x45, 0xe0, 0x24, \ + 0x8e, 0x44, 0xee, 0x80, \ + 0xa7, 0xa4, 0xa3, 0xb4, \ + 0xee, 0x84, 0x4c, 0x45, \ + 0xa5, 0x84, 0xcc, 0x44, \ + 0xee, 0x88, 0x4c, 0x45, \ + 0x4c, 0x45, 0xce, 0x44, \ + 0x06, 0x45, 0xee, 0x8c, \ + 0x4c, 0x45, 0xe0, 0x10, \ + 0x06, 0x45, 0xee, 0x90, \ + 0x4c, 0x45, 0xe0, 0x14, \ + 0x06, 0x45, 0xee, 0x94, \ + 0x4c, 0x45, 0xe0, 0x18, \ + 0x06, 0x45, 0xee, 0x98, \ + 0x4c, 0x45, 0xe0, 0x1c, \ + 0x06, 0x45, 0xee, 0x9c, \ + 0x4c, 0x45, 0xe0, 0x20, \ + 0x06, 0x45, 0xee, 0xa0, \ + 0x4c, 0x45, 0xe0, 0x24, \ + 0x06, 0x45, 0xee, 0xa4, \ + 0x4c, 0x45, 0xe0, 0x28, \ + 0x06, 0x45, 0xee, 0xa8, \ + 0x4c, 0x45, 0xe0, 0x2c, \ + 0x06, 0x45, 0xee, 0xac, \ + 0xfc, 0x7e, 0xe0, 0x30, \ + 0x4c, 0x45, 0x9c, 0x78, \ + 0x97, 0x44, 0xee, 0xb0, \ + 0xee, 0xb4, 0x4c, 0xc5, \ + 0x4c, 0x45, 0x99, 0xc4, \ + 0x9d, 0x44, 0xed, 0x84, \ + 0x60, 0x30, 0x04, 0x95, \ + 0x4c, 0x75, 0xff, 0x21, \ + 0x9b, 0x74, 0xed, 0x80, \ + 0xed, 0x88, 0x4c, 0xc5, \ + 0x95, 0x34, 0x9f, 0xc4, \ + 0x6d, 0x90, 0x4c, 0x45, \ + 0xee, 0x00, 0x08, 0x92, \ + 0x6d, 0x94, 0x4c, 0x75, \ + 0x66, 0x00, 0x0e, 0x60, \ + 0x4c, 0xc5, 0xc0, 0x0d, \ + 0x59, 0xce, 0x6d, 0x8c, \ + 0x4c, 0x45, 0xff, 0xf4, \ + 0x08, 0x8e, 0x6d, 0xa4, \ + 0x4c, 0xb5, 0xff, 0xf8, \ + 0x83, 0xb1, 0x6d, 0x9c, \ + 0xee, 0x34, 0x4c, 0x55, \ + 0xee, 0x04, 0x4c, 0x25, \ + 0x0c, 0x95, 0x99, 0x4b, \ + 0x52, 0x92, 0xcd, 0x9c, \ + 0x08, 0x40, 0x51, 0x90, \ + 0x52, 0xb1, 0xcc, 0x04, \ + 0xfc, 0xf5, 0xc0, 0x00, \ + 0x6e, 0x3c, 0x4c, 0x05, \ + 0x67, 0x00, 0x00, 0x60, \ + 0x4c, 0x05, 0xc0, 0x06, \ + 0x00, 0x60, 0x6d, 0x98, \ + 0xc0, 0x02, 0x65, 0x00, \ + 0x6e, 0x20, 0x4c, 0x35, \ + 0xe0, 0x80, 0x06, 0x8a, \ + 0x6e, 0x2c, 0x4c, 0x05, \ + 0x66, 0x00, 0x06, 0x60, \ + 0x4c, 0x65, 0xc0, 0x04, \ + 0xfb, 0x01, 0x6d, 0xa0, \ + 0xee, 0x30, 0x4c, 0x35, \ + 0xee, 0x14, 0x4c, 0x25, \ + 0x0c, 0x45, 0x88, 0x75, \ + 0x0c, 0xc5, 0xed, 0xf8, \ + 0x89, 0xcd, 0xee, 0x28, \ + 0x0c, 0x25, 0x86, 0xb5, \ + 0x06, 0x20, 0xed, 0xb4, \ + 0x12, 0x34, 0xe4, 0x08, \ + 0xfd, 0x97, 0xe1, 0x7c, \ + 0x12, 0x34, 0x92, 0x33, \ + 0xa1, 0x31, 0x61, 0x08, \ + 0xf1, 0xa4, 0x52, 0x32, \ + 0x82, 0xd5, 0xa9, 0x9d, \ + 0x0c, 0x95, 0xbe, 0xae, \ + 0x0c, 0xa5, 0xed, 0xc4, \ + 0x94, 0x9d, 0xed, 0xc8, \ + 0x0c, 0x95, 0x80, 0xf5, \ + 0x0c, 0xa5, 0xee, 0x78, \ + 0x94, 0x9d, 0xee, 0x7c, \ + 0x98, 0xf1, 0x80, 0xa5, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x0c, 0x95, 0xdd, 0x93, \ + 0xaa, 0x9d, 0xed, 0xcc, \ + 0x20, 0xc1, 0xfe, 0x35, \ + 0xa8, 0xc3, 0x92, 0x43, \ + 0xfc, 0x95, 0xa9, 0xcd, \ + 0xed, 0xd0, 0x0c, 0x45, \ + 0xfc, 0x15, 0xac, 0x4d, \ + 0x64, 0x04, 0x08, 0x20, \ + 0x18, 0x44, 0xb2, 0x33, \ + 0x98, 0x43, 0xe1, 0xfc, \ + 0x89, 0x3d, 0x83, 0x47, \ + 0x0c, 0x45, 0xf8, 0xf5, \ + 0x0c, 0xc5, 0xed, 0xb8, \ + 0x86, 0x43, 0xee, 0x24, \ + 0x85, 0x4d, 0x98, 0x43, \ + 0x0c, 0x45, 0xf6, 0xf5, \ + 0x89, 0x21, 0x6d, 0xd8, \ + 0xa2, 0x4d, 0x91, 0x31, \ + 0x3e, 0x94, 0x84, 0x15, \ + 0x10, 0xab, 0xa1, 0x4b, \ + 0x80, 0x00, 0x09, 0x06, \ + 0x18, 0x86, 0x06, 0x65, \ + 0x23, 0x4b, 0x80, 0x00, \ + 0xe0, 0x0c, 0x49, 0xf6, \ + 0x89, 0x01, 0x11, 0xc1, \ + 0x83, 0x95, 0x84, 0x85, \ + 0xa5, 0x4b, 0x3e, 0x94, \ + 0x49, 0xf6, 0x10, 0xab, \ + 0x02, 0x85, 0x80, 0x0c, \ + 0x80, 0x0c, 0x59, 0xf6, \ + 0x82, 0xf5, 0xa7, 0x4b, \ + 0x60, 0x0c, 0x49, 0xf6, \ + 0xe0, 0x0c, 0x59, 0xf6, \ + 0x41, 0xf6, 0x01, 0x45, \ + 0x89, 0x01, 0xe0, 0x0c, \ + 0xee, 0x54, 0x4c, 0x45, \ + 0xee, 0x58, 0x4c, 0xc5, \ + 0xee, 0x5c, 0x4c, 0x05, \ + 0xed, 0xe0, 0x0c, 0x45, \ + 0x82, 0x55, 0xa2, 0x4d, \ + 0x04, 0xc5, 0xa1, 0x4b, \ + 0x80, 0x00, 0x01, 0x06, \ + 0x41, 0xf6, 0x23, 0x4b, \ + 0x84, 0x05, 0xe0, 0x0c, \ + 0xa5, 0x4b, 0x83, 0x55, \ + 0x00, 0x0c, 0x47, 0xf6, \ + 0x80, 0x0c, 0x41, 0xf6, \ + 0x27, 0x4b, 0x82, 0x25, \ + 0xe0, 0x0c, 0x41, 0xf6, \ + 0x47, 0xf6, 0x82, 0x75, \ + 0x45, 0xf6, 0x60, 0x0c, \ + 0x4c, 0x05, 0xe0, 0x0c, \ + 0x4c, 0x35, 0xee, 0x60, \ + 0x4c, 0x25, 0xee, 0x64, \ + 0xc6, 0x0a, 0xee, 0x68, \ + 0xee, 0x34, 0x4c, 0x55, \ + 0xfa, 0x14, 0x40, 0x02, \ + 0x6e, 0x34, 0x4c, 0x05, \ + 0x54, 0x78, 0x8e, 0x0b, \ + 0x47, 0x18, 0xe8, 0x0c, \ + 0x8e, 0x0b, 0x68, 0x0c, \ + 0x68, 0x04, 0x53, 0x28, \ + 0x71, 0x98, 0x8e, 0x0b, \ + 0x8e, 0x0b, 0x68, 0x08, \ + 0x6e, 0x38, 0x0c, 0x05, \ + 0x0c, 0x15, 0x87, 0x81, \ + 0x4c, 0xb5, 0xee, 0x20, \ + 0x4c, 0x05, 0xee, 0x18, \ + 0xbe, 0x0d, 0x6e, 0x3c, \ + 0x6e, 0x1c, 0x4c, 0xb5, \ + 0x90, 0x84, 0x40, 0x02, \ + 0x0e, 0x3c, 0x4c, 0x05, \ + 0x00, 0x00, 0x82, 0x03, \ + 0x81, 0x11, 0x6c, 0x04, \ + 0xee, 0x40, 0x4c, 0x05, \ + 0x6e, 0x44, 0x4c, 0x05, \ + 0xbe, 0x1e, 0x81, 0x01, \ + 0xbe, 0x04, 0x8f, 0x45, \ + 0xe3, 0x1c, 0x01, 0x0a, \ + 0x41, 0xee, 0x56, 0xf5, \ + 0xa1, 0xbb, 0xdf, 0xfc, \ + 0x0c, 0x05, 0x84, 0xd5, \ + 0xa7, 0x0b, 0xee, 0x1c, \ + 0xa5, 0x0b, 0x82, 0x25, \ + 0xa3, 0x0b, 0x82, 0x65, \ + 0x4a, 0xd8, 0x82, 0x95, \ + 0x8e, 0x0b, 0x68, 0x00, \ + 0x6b, 0x58, 0x81, 0x85, \ + 0x8e, 0x0b, 0x68, 0x00, \ + 0x54, 0xf8, 0x81, 0x45, \ + 0x8e, 0x0b, 0x68, 0x00, \ + 0xee, 0x18, 0x0c, 0xb5, \ + 0x80, 0xd5, 0xa3, 0xbb, \ + 0x54, 0x08, 0x8e, 0x0b, \ + 0x0c, 0xb5, 0xfc, 0x38, \ + 0xa5, 0xbb, 0xee, 0x18, \ + 0x0c, 0x15, 0x8a, 0x15, \ + 0x0c, 0x05, 0xee, 0x08, \ + 0x84, 0x11, 0xee, 0x40, \ + 0x4c, 0x15, 0x83, 0x0d, \ + 0x40, 0x10, 0x4e, 0x40, \ + 0x0c, 0x15, 0xdc, 0x00, \ + 0x01, 0x0a, 0x6e, 0x28, \ + 0x01, 0x16, 0xe0, 0x40, \ + 0x4c, 0x05, 0x80, 0x00, \ + 0x0c, 0x05, 0x8e, 0x40, \ + 0x82, 0x0d, 0x6e, 0x20, \ + 0x84, 0x04, 0x02, 0x10, \ + 0x8e, 0x40, 0x4c, 0x15, \ + 0xe6, 0xa2, 0x40, 0x0a, \ + 0xee, 0x3c, 0x0c, 0x15, \ + 0xec, 0x38, 0x00, 0x00, \ + 0x4c, 0x05, 0x80, 0x1d, \ + 0x8e, 0x0b, 0x4e, 0x3c, \ + 0xfc, 0x38, 0x52, 0x08, \ + 0xee, 0x18, 0x0c, 0xb5, \ + 0x86, 0xf5, 0xa7, 0xbb, \ + 0xee, 0x40, 0x0c, 0x05, \ + 0xee, 0x08, 0x0c, 0x15, \ + 0xe0, 0x40, 0x01, 0x0a, \ + 0x00, 0x00, 0x01, 0x16, \ + 0x4c, 0x05, 0x84, 0x11, \ + 0x83, 0x0d, 0x0e, 0x40, \ + 0xee, 0x20, 0x0c, 0x05, \ + 0xce, 0x40, 0x4c, 0x15, \ + 0xee, 0x3c, 0x0c, 0x15, \ + 0xe6, 0xa2, 0x40, 0x0a, \ + 0xec, 0x18, 0x00, 0x00, \ + 0x4c, 0x05, 0x80, 0x1d, \ + 0x8e, 0x0b, 0x4e, 0x3c, \ + 0xe8, 0x08, 0x4f, 0xa8, \ + 0xee, 0x18, 0x0c, 0xb5, \ + 0xaf, 0xbb, 0x3e, 0x04, \ + 0xbe, 0x0e, 0x82, 0x01, \ + 0xe2, 0x00, 0x00, 0x0c, \ + 0xe2, 0x85, 0xa1, 0x0b, \ + 0xee, 0x04, 0x0c, 0x05, \ + 0x82, 0x01, 0xfc, 0x14, \ + 0x00, 0x04, 0x06, 0x06, \ + 0x03, 0xa5, 0xfa, 0x80, \ + 0x0c, 0x25, 0x81, 0x11, \ + 0x00, 0x00, 0x6d, 0x9c, \ + 0x0c, 0x45, 0xec, 0x04, \ + 0x02, 0x23, 0xee, 0x20, \ + 0x04, 0x33, 0x84, 0x11, \ + 0x04, 0x53, 0xe0, 0x30, \ + 0x58, 0x3a, 0xe0, 0x60, \ + 0x46, 0x5a, 0xe1, 0x92, \ + 0x44, 0xc3, 0xe1, 0x92, \ + 0x46, 0x8a, 0xe0, 0x30, \ + 0x46, 0x32, 0xe8, 0x30, \ + 0x44, 0x33, 0xd0, 0x90, \ + 0xa1, 0x0b, 0xe0, 0x60, \ + 0x59, 0xa5, 0xf8, 0xf5, \ + 0xc1, 0xc3, 0x81, 0x01, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0xec, 0xa0, 0x1e, 0xf0, \ + 0x14, 0x0e, 0x2d, 0x84, \ + 0xc0, 0x10, 0x64, 0xad, \ + 0x13, 0xae, 0xaf, 0x94, \ + 0x64, 0x69, 0x15, 0xce, \ + 0x91, 0xae, 0xc0, 0x10, \ + 0x8d, 0x8e, 0x8f, 0x9e, \ + 0xa1, 0xc4, 0xab, 0xa4, \ + 0xa9, 0x74, 0xa5, 0x64, \ + 0xa3, 0x84, 0xa7, 0x94, \ + 0x89, 0x7e, 0x8b, 0xae, \ + 0xfc, 0x6e, 0xfe, 0x9e, \ + 0xbc, 0xce, 0xbe, 0x8e, \ + 0xef, 0xfc, 0x58, 0xa8, \ + 0x0c, 0x0c, 0xd0, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0xfa, 0x1c, 0x04, 0x4c, \ + 0x0c, 0x05, 0x80, 0x6b, \ + 0xa1, 0x0d, 0xe0, 0x88, \ + 0x0c, 0x15, 0x80, 0xb5, \ + 0x83, 0x0d, 0xe0, 0xbc, \ + 0x8f, 0x01, 0x80, 0xa5, \ + 0xe0, 0x98, 0x4c, 0x05, \ + 0x0c, 0x15, 0x8b, 0xc5, \ + 0x0c, 0x25, 0xe1, 0x3c, \ + 0x0c, 0x75, 0xe0, 0xa8, \ + 0x46, 0x22, 0xe0, 0x18, \ + 0x87, 0x0b, 0xf0, 0x84, \ + 0x0c, 0x35, 0x82, 0x55, \ + 0xa1, 0x3b, 0xe0, 0x7c, \ + 0x0c, 0x35, 0x80, 0xd5, \ + 0x46, 0x0a, 0xe0, 0x74, \ + 0x92, 0x95, 0xe0, 0x80, \ + 0xe1, 0xd0, 0x0c, 0x19, \ + 0xe0, 0x88, 0x42, 0x0a, \ + 0x03, 0x21, 0x82, 0x25, \ + 0x62, 0x98, 0x8c, 0x1b, \ + 0x0c, 0x25, 0xe8, 0x08, \ + 0x4c, 0x05, 0xe0, 0xa8, \ + 0x0c, 0x15, 0xe0, 0x88, \ + 0x87, 0x51, 0x60, 0xc4, \ + 0xe0, 0x34, 0x0c, 0x35, \ + 0xe0, 0xa0, 0x0c, 0x45, \ + 0xe0, 0xbc, 0x4c, 0x15, \ + 0x48, 0x4a, 0x82, 0x33, \ + 0x4a, 0x32, 0x66, 0x96, \ + 0x09, 0x5d, 0xf1, 0x88, \ + 0x4c, 0x55, 0x85, 0x51, \ + 0x48, 0x42, 0x60, 0x98, \ + 0x4c, 0x55, 0x90, 0x88, \ + 0x06, 0x40, 0x60, 0x9c, \ + 0x48, 0x32, 0x8c, 0x04, \ + 0xc4, 0x2b, 0x71, 0x88, \ + 0x18, 0x24, 0x44, 0x47, \ + 0x0e, 0x55, 0xe0, 0x88, \ + 0x98, 0x73, 0x7c, 0x10, \ + 0xe0, 0xac, 0x0c, 0x45, \ + 0x08, 0x25, 0xc4, 0x57, \ + 0x0a, 0x34, 0xfc, 0x14, \ + 0x8e, 0x53, 0xe0, 0x88, \ + 0x00, 0x00, 0x82, 0x3d, \ + 0x4c, 0x05, 0x84, 0x08, \ + 0x82, 0xa5, 0x80, 0xc0, \ + 0x06, 0x30, 0x57, 0xc4, \ + 0xc4, 0xc7, 0xec, 0x04, \ + 0xfc, 0x30, 0x08, 0xc5, \ + 0xfc, 0x75, 0x84, 0xcd, \ + 0x60, 0xbc, 0x4c, 0x35, \ + 0xfd, 0xc5, 0x98, 0x2b, \ + 0xf2, 0x1c, 0x0c, 0x4c, \ + 0x0c, 0x25, 0xdd, 0x93, \ + 0x06, 0x14, 0x60, 0x84, \ + 0x0c, 0x45, 0xe0, 0x88, \ + 0x86, 0x73, 0x60, 0x78, \ + 0x82, 0x3b, 0x01, 0x51, \ + 0x4c, 0x25, 0x82, 0x21, \ + 0x82, 0x43, 0x60, 0x84, \ + 0x0e, 0xc5, 0x83, 0x45, \ + 0xdd, 0x84, 0xf8, 0x10, \ + 0xe0, 0x20, 0x0c, 0x95, \ + 0xc4, 0x87, 0xc4, 0xc7, \ + 0xfc, 0x30, 0x12, 0xc5, \ + 0xfc, 0x20, 0x12, 0x85, \ + 0xf1, 0xb0, 0x58, 0x82, \ + 0x4a, 0xc2, 0x0a, 0xcd, \ + 0x46, 0x10, 0xfa, 0x14, \ + 0x82, 0x11, 0x5c, 0x00, \ + 0xfa, 0x85, 0x88, 0x1d, \ + 0x0c, 0x55, 0x98, 0x14, \ + 0x0c, 0x45, 0xe0, 0x3c, \ + 0x02, 0x10, 0xe0, 0x1c, \ + 0x03, 0x2d, 0xec, 0x04, \ + 0x04, 0x43, 0xc2, 0x27, \ + 0x48, 0x33, 0x86, 0x53, \ + 0x48, 0x53, 0xe0, 0x30, \ + 0xda, 0x85, 0xe0, 0x60, \ + 0x82, 0x01, 0x03, 0x11, \ + 0xe0, 0x98, 0x4c, 0x15, \ + 0xe0, 0x9c, 0x4c, 0x15, \ + 0xe0, 0xbc, 0x4c, 0x05, \ + 0xc1, 0xc3, 0xf9, 0x25, \ + 0xfa, 0x9c, 0x04, 0x0c, \ + 0x0c, 0x15, 0x80, 0x6b, \ + 0xa1, 0x1d, 0xe0, 0x8c, \ + 0x0c, 0x05, 0x82, 0x35, \ + 0x80, 0x1d, 0xe0, 0x88, \ + 0x0c, 0x05, 0x80, 0xa5, \ + 0x83, 0x0d, 0xe0, 0xbc, \ + 0x8f, 0x01, 0x80, 0xe5, \ + 0xe0, 0x98, 0x4c, 0x05, \ + 0xf2, 0x9c, 0x0c, 0x0c, \ + 0x0c, 0x55, 0xdd, 0x93, \ + 0x0c, 0x25, 0xe1, 0x40, \ + 0x0c, 0xc5, 0xe0, 0xa8, \ + 0x0c, 0x35, 0xe0, 0x18, \ + 0x0c, 0x4b, 0xe0, 0xb4, \ + 0x44, 0x52, 0x60, 0x48, \ + 0x0c, 0x2b, 0xf1, 0x88, \ + 0x85, 0x1b, 0x60, 0x50, \ + 0x0c, 0x75, 0x82, 0x75, \ + 0xa1, 0x7b, 0xe0, 0x80, \ + 0x0c, 0x75, 0x80, 0xf5, \ + 0x4e, 0x0a, 0xe0, 0x74, \ + 0x0f, 0x58, 0xe0, 0x80, \ + 0x0c, 0x55, 0xc0, 0x00, \ + 0x50, 0x86, 0x60, 0x70, \ + 0x4e, 0x02, 0xe0, 0x3c, \ + 0xaa, 0x83, 0xf1, 0x84, \ + 0x96, 0x45, 0x90, 0x7d, \ + 0x60, 0x94, 0x0c, 0x55, \ + 0x91, 0x5b, 0xfd, 0x81, \ + 0x0c, 0x55, 0x94, 0xa5, \ + 0x10, 0x50, 0xe0, 0x34, \ + 0x12, 0x84, 0xe4, 0x04, \ + 0x92, 0x83, 0xe1, 0xfc, \ + 0x8a, 0x83, 0x83, 0x87, \ + 0x92, 0x45, 0x91, 0x7d, \ + 0x70, 0x84, 0x4e, 0x02, \ + 0x02, 0x71, 0xc6, 0x37, \ + 0x10, 0x74, 0xa0, 0x31, \ + 0xcb, 0x37, 0x61, 0xfc, \ + 0xf9, 0x81, 0x10, 0x73, \ + 0x0c, 0x85, 0xc2, 0x77, \ + 0x90, 0x7f, 0x60, 0x10, \ + 0x18, 0x75, 0x8e, 0xc3, \ + 0xb2, 0xc4, 0xf8, 0x0c, \ + 0x10, 0x85, 0xc4, 0x77, \ + 0xc4, 0xc7, 0x7c, 0x1c, \ + 0x64, 0x10, 0x12, 0x80, \ + 0xc7, 0x97, 0x86, 0x83, \ + 0x66, 0x92, 0x48, 0x9a, \ + 0x7f, 0xfc, 0x53, 0x8e, \ + 0x08, 0x40, 0xc3, 0xff, \ + 0xc9, 0x47, 0xe5, 0x00, \ + 0x60, 0x20, 0x0c, 0x95, \ + 0x88, 0x83, 0x92, 0x4f, \ + 0xf0, 0x0a, 0x48, 0x8a, \ + 0xfc, 0x1c, 0x12, 0x75, \ + 0xfc, 0x30, 0x12, 0xc5, \ + 0xe6, 0x8a, 0x44, 0x4a, \ + 0x04, 0x20, 0xa6, 0x73, \ + 0x98, 0x73, 0x66, 0x00, \ + 0x84, 0x7d, 0xd1, 0x27, \ + 0x0c, 0x25, 0x84, 0xa5, \ + 0x98, 0x34, 0xe0, 0x84, \ + 0xe0, 0x1c, 0x0c, 0x45, \ + 0x4c, 0x25, 0x82, 0x21, \ + 0x06, 0x30, 0x60, 0x84, \ + 0x07, 0x2d, 0xec, 0x04, \ + 0x04, 0x43, 0xc2, 0x27, \ + 0xf1, 0x94, 0x46, 0x02, \ + 0xe0, 0x30, 0x48, 0x33, \ + 0xe0, 0x60, 0x48, 0x03, \ + 0x0c, 0x05, 0xde, 0xe5, \ + 0xff, 0x71, 0x61, 0x30, \ + 0x60, 0x94, 0x4c, 0x75, \ + 0x73, 0xb8, 0x8c, 0x2b, \ + 0x0c, 0x05, 0xe8, 0x08, \ + 0x80, 0x1b, 0x60, 0x8c, \ + 0xe0, 0x88, 0x0c, 0x35, \ + 0x71, 0x84, 0x48, 0x02, \ + 0x4c, 0x45, 0x80, 0x2b, \ + 0x87, 0x4d, 0x61, 0x38, \ + 0x1c, 0x00, 0x44, 0x40, \ + 0x4c, 0x25, 0xab, 0x1d, \ + 0x82, 0xd5, 0xe1, 0x38, \ + 0xe1, 0xd0, 0x0c, 0x19, \ + 0xe0, 0x90, 0x42, 0x0a, \ + 0x40, 0x00, 0x02, 0x26, \ + 0xcc, 0x10, 0x00, 0x00, \ + 0xc0, 0x98, 0x4c, 0x15, \ + 0xc0, 0x9c, 0x4c, 0x15, \ + 0x0c, 0x15, 0x92, 0x55, \ + 0x48, 0x02, 0x60, 0x34, \ + 0x88, 0x1d, 0xf1, 0x8c, \ + 0x00, 0x10, 0x86, 0xe5, \ + 0xa8, 0x03, 0xe4, 0x04, \ + 0xe1, 0xfc, 0x04, 0x04, \ + 0x60, 0x84, 0x0c, 0x25, \ + 0x83, 0x07, 0x84, 0x03, \ + 0x60, 0x1c, 0x0c, 0x05, \ + 0x02, 0x21, 0xa0, 0x33, \ + 0x4c, 0x25, 0x86, 0x13, \ + 0xc2, 0x27, 0x60, 0x84, \ + 0x8f, 0x21, 0x04, 0x03, \ + 0xe0, 0x88, 0x4c, 0x35, \ + 0xe0, 0x8c, 0x4c, 0x15, \ + 0xe0, 0x30, 0x40, 0x33, \ + 0xe0, 0x60, 0x40, 0x13, \ + 0xe0, 0x98, 0x4c, 0x25, \ + 0x0c, 0x15, 0xe7, 0x85, \ + 0x46, 0x22, 0x60, 0x38, \ + 0x0c, 0x45, 0xf1, 0x8c, \ + 0x82, 0x43, 0xe0, 0xa4, \ + 0x88, 0x65, 0x89, 0x3d, \ + 0x68, 0x08, 0x69, 0x88, \ + 0x0c, 0x15, 0x8c, 0x1b, \ + 0x0c, 0x25, 0xe0, 0x84, \ + 0x0c, 0x35, 0xe0, 0x1c, \ + 0x4c, 0x05, 0xe0, 0x88, \ + 0x82, 0x11, 0x60, 0x8c, \ + 0x60, 0x84, 0x4c, 0x15, \ + 0x02, 0x23, 0xc2, 0x17, \ + 0x44, 0x03, 0x8f, 0x11, \ + 0x8c, 0x0b, 0x60, 0x60, \ + 0xe0, 0x30, 0x44, 0x33, \ + 0xe0, 0x98, 0x4c, 0x15, \ + 0xf2, 0x9c, 0x0c, 0x0c, \ + 0xe0, 0x08, 0x10, 0xa8, \ + 0x60, 0xbc, 0x4c, 0x05, \ + 0x4c, 0x15, 0xfd, 0x11, \ + 0xe1, 0xd5, 0xe0, 0x94, \ + 0x60, 0xa0, 0x0c, 0x05, \ + 0x0c, 0x35, 0x87, 0x11, \ + 0x0c, 0x45, 0xe0, 0x44, \ + 0x40, 0x0a, 0xe0, 0x48, \ + 0x4c, 0x15, 0xe6, 0x86, \ + 0x46, 0x22, 0x60, 0x98, \ + 0x00, 0x00, 0xf1, 0x8c, \ + 0xa8, 0x23, 0x6c, 0x18, \ + 0x60, 0xbc, 0x4c, 0x35, \ + 0x4c, 0x15, 0x80, 0x3d, \ + 0x4c, 0x75, 0xe0, 0x9c, \ + 0x4c, 0x25, 0xe0, 0x94, \ + 0x4c, 0x05, 0xe0, 0xc0, \ + 0xdf, 0x15, 0xc0, 0xbc, \ + 0x60, 0x78, 0x0c, 0x05, \ + 0x44, 0x52, 0x81, 0x31, \ + 0x00, 0x54, 0x71, 0x80, \ + 0x00, 0xc3, 0xe0, 0x88, \ + 0x83, 0x55, 0x8a, 0x0b, \ + 0x18, 0x75, 0xf3, 0x44, \ + 0x0c, 0x85, 0xf8, 0x0c, \ + 0xc4, 0x47, 0xe0, 0x20, \ + 0x10, 0x45, 0xc4, 0x77, \ + 0x10, 0x75, 0xfc, 0x10, \ + 0x48, 0x72, 0xfc, 0x1c, \ + 0x06, 0x4d, 0xf1, 0x90, \ + 0xfa, 0x0c, 0x46, 0x42, \ + 0x5c, 0x00, 0x40, 0x50, \ + 0xec, 0x04, 0x0a, 0x50, \ + 0xfa, 0x65, 0x85, 0x5d, \ + 0xe0, 0x84, 0x0c, 0x25, \ + 0x0c, 0x45, 0x98, 0x34, \ + 0x0c, 0x55, 0xe0, 0x3c, \ + 0x82, 0x21, 0x60, 0x1c, \ + 0x60, 0x84, 0x4c, 0x25, \ + 0xec, 0x04, 0x06, 0x30, \ + 0xc2, 0x27, 0x07, 0x2d, \ + 0x46, 0x02, 0x04, 0x53, \ + 0x4a, 0x33, 0xf1, 0x90, \ + 0x4a, 0x03, 0xe0, 0x30, \ + 0x2b, 0x88, 0xe0, 0x60, \ + 0x85, 0x01, 0x9f, 0xfc, \ + 0xe0, 0x98, 0x4c, 0x05, \ + 0x60, 0x9c, 0x4c, 0x05, \ + 0xec, 0x04, 0x00, 0x10, \ + 0xc1, 0xc3, 0xf5, 0x05, \ + 0xfa, 0xfc, 0x04, 0x0c, \ + 0x87, 0x81, 0x00, 0x6b, \ + 0xe0, 0x90, 0x0c, 0x05, \ + 0xe0, 0x18, 0x0c, 0x15, \ + 0xe0, 0x84, 0x0c, 0x75, \ + 0x60, 0x98, 0x4c, 0x85, \ + 0xac, 0xd5, 0xa1, 0x0d, \ + 0xe0, 0x8c, 0x0c, 0x25, \ + 0xac, 0x45, 0x85, 0x0d, \ + 0x60, 0x20, 0x0c, 0x55, \ + 0xe0, 0x88, 0x04, 0x04, \ + 0x7c, 0x08, 0x02, 0x25, \ + 0xf0, 0x88, 0x46, 0x12, \ + 0xf8, 0x10, 0x06, 0x45, \ + 0xf8, 0x08, 0x06, 0xc5, \ + 0x78, 0x18, 0x06, 0x35, \ + 0x0a, 0x25, 0xc4, 0x27, \ + 0xc4, 0x47, 0x7c, 0x08, \ + 0x7c, 0x10, 0x0a, 0x45, \ + 0x0a, 0xc5, 0xc4, 0xc7, \ + 0xc4, 0x37, 0x7c, 0x30, \ + 0xfc, 0x0c, 0x0a, 0x35, \ + 0xa6, 0xc3, 0xa8, 0x23, \ + 0x90, 0x65, 0x84, 0xcd, \ + 0xe0, 0x34, 0x0c, 0x25, \ + 0x06, 0x24, 0x84, 0x21, \ + 0xfd, 0x37, 0xe1, 0x7c, \ + 0x85, 0x27, 0x86, 0x23, \ + 0x4c, 0x05, 0xa4, 0x03, \ + 0x04, 0x04, 0x60, 0x90, \ + 0x02, 0x45, 0xe0, 0x88, \ + 0x46, 0x12, 0x7c, 0x08, \ + 0x8e, 0x34, 0xf0, 0x88, \ + 0xc4, 0x47, 0x9a, 0x24, \ + 0x04, 0x35, 0xc4, 0x37, \ + 0x04, 0x45, 0xfc, 0x0c, \ + 0x48, 0x32, 0xfc, 0x10, \ + 0x89, 0x31, 0x71, 0x90, \ + 0x0a, 0x00, 0x85, 0x45, \ + 0x18, 0x04, 0x6c, 0x04, \ + 0x02, 0xc5, 0xe0, 0x88, \ + 0x12, 0x54, 0x7c, 0x30, \ + 0x02, 0x95, 0xe0, 0x88, \ + 0xc4, 0xc7, 0xfc, 0x24, \ + 0x7c, 0x30, 0x04, 0xc5, \ + 0x04, 0x95, 0xc4, 0x97, \ + 0xb2, 0xc3, 0xfc, 0x24, \ + 0x82, 0xf5, 0xa3, 0xcd, \ + 0xe4, 0x08, 0x12, 0x40, \ + 0xe1, 0x7c, 0x14, 0x94, \ + 0x94, 0x93, 0xfd, 0xa7, \ + 0x93, 0xcd, 0x85, 0x97, \ + 0x00, 0x90, 0x4c, 0x55, \ + 0x9c, 0x00, 0x40, 0x50, \ + 0x9c, 0x00, 0x48, 0xc0, \ + 0xec, 0x04, 0x06, 0x30, \ + 0xf6, 0x95, 0xa1, 0x3b, \ + 0x8c, 0x1b, 0x03, 0x21, \ + 0xe8, 0x04, 0x7d, 0x28, \ + 0x61, 0x38, 0x0c, 0x45, \ + 0x0c, 0x35, 0xff, 0xc1, \ + 0x12, 0x60, 0x60, 0x34, \ + 0x0c, 0x15, 0xe6, 0x20, \ + 0x0c, 0x25, 0xe0, 0x94, \ + 0x4a, 0x42, 0xe0, 0x88, \ + 0x40, 0x52, 0xf1, 0x8c, \ + 0x99, 0x1b, 0x7b, 0x00, \ + 0xfa, 0x00, 0x44, 0x22, \ + 0xe0, 0x90, 0x4c, 0x25, \ + 0x0c, 0x15, 0xb0, 0x45, \ + 0x98, 0x1d, 0xe0, 0x84, \ + 0x0c, 0x05, 0xae, 0x05, \ + 0x14, 0x14, 0x60, 0x1c, \ + 0x0c, 0xc5, 0xe0, 0x84, \ + 0x80, 0xa3, 0xe0, 0xa4, \ + 0xe0, 0x30, 0x14, 0x53, \ + 0xf1, 0xb0, 0x56, 0x52, \ + 0x0f, 0x38, 0x97, 0x2d, \ + 0x02, 0x11, 0x80, 0x00, \ + 0x4c, 0x15, 0x84, 0x33, \ + 0x08, 0x14, 0x60, 0x84, \ + 0x08, 0x03, 0xe0, 0x84, \ + 0x0c, 0xa5, 0x91, 0x11, \ + 0x4c, 0x15, 0xe0, 0x50, \ + 0x8c, 0x1b, 0x60, 0x50, \ + 0xe0, 0x30, 0x40, 0x23, \ + 0x60, 0x60, 0x40, 0x33, \ + 0xfb, 0x00, 0x00, 0x34, \ + 0xe8, 0x08, 0x56, 0xa8, \ + 0x0c, 0x15, 0x98, 0x24, \ + 0x0c, 0x35, 0xe0, 0x84, \ + 0x4c, 0xa5, 0xe0, 0x1c, \ + 0x04, 0x20, 0x60, 0x50, \ + 0x05, 0x1d, 0xec, 0x04, \ + 0xe0, 0x84, 0x04, 0x14, \ + 0x46, 0x03, 0x84, 0x33, \ + 0xa0, 0xa5, 0xe0, 0x60, \ + 0x0c, 0x25, 0xa5, 0x35, \ + 0xff, 0x11, 0x60, 0x94, \ + 0x86, 0xb5, 0x83, 0x2b, \ + 0x61, 0x38, 0x0c, 0x25, \ + 0x0c, 0x05, 0x82, 0x71, \ + 0x0a, 0x74, 0x60, 0x34, \ + 0x98, 0x44, 0xe0, 0x84, \ + 0xe0, 0x1c, 0x0c, 0x35, \ + 0x60, 0x8c, 0x0c, 0x05, \ + 0x4c, 0x25, 0xa0, 0x23, \ + 0x08, 0x40, 0x60, 0x90, \ + 0x0a, 0x33, 0xec, 0x04, \ + 0x4c, 0x25, 0x89, 0x7d, \ + 0x4c, 0x75, 0xe0, 0x94, \ + 0x46, 0x23, 0xe0, 0x84, \ + 0x46, 0x03, 0xe0, 0x30, \ + 0x98, 0x65, 0xe0, 0x60, \ + 0xe0, 0x88, 0x0c, 0x05, \ + 0xe0, 0xa4, 0x0c, 0x35, \ + 0xe0, 0x90, 0x4c, 0x25, \ + 0xf1, 0x80, 0x48, 0x22, \ + 0x8e, 0x25, 0x86, 0x4d, \ + 0x60, 0x34, 0x0c, 0x35, \ + 0xec, 0x04, 0x04, 0x20, \ + 0x60, 0xbc, 0x4c, 0x25, \ + 0x4c, 0x45, 0x85, 0x41, \ + 0x4c, 0x45, 0xe0, 0x98, \ + 0x80, 0x33, 0x60, 0x9c, \ + 0x60, 0x94, 0x4c, 0x15, \ + 0xec, 0x0c, 0x06, 0x30, \ + 0x60, 0xc0, 0x4c, 0x35, \ + 0x92, 0x15, 0x86, 0x2d, \ + 0x60, 0x3c, 0x0c, 0x15, \ + 0x0c, 0x25, 0x82, 0x71, \ + 0x06, 0x74, 0x60, 0x1c, \ + 0x4c, 0x75, 0xe0, 0x84, \ + 0x80, 0x13, 0xe0, 0x84, \ + 0x60, 0x8c, 0x4c, 0x15, \ + 0x44, 0x03, 0x86, 0x23, \ + 0x44, 0x13, 0xe0, 0x30, \ + 0x0c, 0x09, 0xe0, 0x60, \ + 0x40, 0x0a, 0xe1, 0xd0, \ + 0x8a, 0xe5, 0xe0, 0x84, \ + 0x7b, 0x00, 0x00, 0x14, \ + 0x4f, 0xf8, 0x8c, 0x1b, \ + 0x0c, 0x15, 0xe8, 0x08, \ + 0x0c, 0x25, 0xe0, 0x84, \ + 0xc2, 0x17, 0xe0, 0x1c, \ + 0x44, 0x03, 0x82, 0x23, \ + 0x85, 0x15, 0xe0, 0x60, \ + 0x86, 0xf5, 0xa1, 0x7d, \ + 0x60, 0x1c, 0x0c, 0x15, \ + 0xe0, 0x84, 0x04, 0x74, \ + 0xe0, 0x40, 0x0c, 0x35, \ + 0x04, 0x23, 0x82, 0x23, \ + 0x46, 0x3a, 0xe0, 0x30, \ + 0xa0, 0x23, 0xe0, 0xa2, \ + 0x82, 0xa5, 0x86, 0x2d, \ + 0x02, 0x09, 0x8e, 0x13, \ + 0x82, 0x01, 0xe1, 0xe0, \ + 0xe1, 0xe0, 0x42, 0x01, \ + 0xe0, 0x88, 0x0c, 0x05, \ + 0xe0, 0x1c, 0x0c, 0x15, \ + 0xe0, 0x84, 0x0c, 0x75, \ + 0x8e, 0x13, 0xc2, 0x77, \ + 0xe0, 0x30, 0x42, 0x03, \ + 0x4c, 0x05, 0x8f, 0x01, \ + 0x0c, 0x0c, 0xe0, 0x98, \ + 0xdd, 0x93, 0xf2, 0xfc, \ + 0xe0, 0xa4, 0x0c, 0x05, \ + 0xe0, 0x84, 0x0c, 0x15, \ + 0xf1, 0x80, 0x40, 0x52, \ + 0x81, 0x2d, 0x02, 0x11, \ + 0xe0, 0x84, 0x4c, 0x15, \ + 0x98, 0x44, 0x94, 0x25, \ + 0xe0, 0x1c, 0x0c, 0x35, \ + 0xe0, 0x8c, 0x0c, 0x05, \ + 0xec, 0x04, 0x18, 0x40, \ + 0x18, 0x14, 0x19, 0x1d, \ + 0x86, 0xc3, 0xe0, 0x84, \ + 0xe0, 0x30, 0x58, 0x53, \ + 0xe0, 0x60, 0x58, 0x03, \ + 0x0c, 0x09, 0xf8, 0x05, \ + 0x0c, 0x55, 0xe1, 0xd0, \ + 0x40, 0x0a, 0xe0, 0x3c, \ + 0x02, 0xa5, 0xe0, 0x84, \ + 0xf0, 0x88, 0x40, 0x52, \ + 0x68, 0x08, 0x4a, 0xa8, \ + 0x0c, 0x15, 0x8c, 0x1b, \ + 0x0c, 0x25, 0xe0, 0x84, \ + 0x0c, 0x35, 0xe0, 0x90, \ + 0x98, 0x44, 0xe0, 0x1c, \ + 0x08, 0x40, 0x02, 0x11, \ + 0x0a, 0x14, 0xec, 0x04, \ + 0x89, 0x1d, 0x60, 0x84, \ + 0x60, 0x84, 0x4c, 0x15, \ + 0x46, 0x03, 0x8a, 0x33, \ + 0x87, 0x01, 0x60, 0x60, \ + 0x60, 0x30, 0x46, 0x23, \ + 0x80, 0x00, 0x00, 0x76, \ + 0xe0, 0x98, 0x4c, 0x05, \ + 0x8b, 0xa5, 0xee, 0xa5, \ + 0x60, 0x60, 0x14, 0xa3, \ + 0x48, 0xa2, 0x86, 0xc3, \ + 0xa4, 0x43, 0xfb, 0x10, \ + 0x8e, 0x05, 0x98, 0x4d, \ + 0x82, 0x11, 0x18, 0x44, \ + 0x60, 0x84, 0x4c, 0x15, \ + 0x08, 0x40, 0x84, 0x33, \ + 0x09, 0x1d, 0xec, 0x04, \ + 0xe0, 0x84, 0x08, 0x14, \ + 0x40, 0x23, 0x88, 0x03, \ + 0x40, 0x33, 0xe0, 0x30, \ + 0xe8, 0x25, 0xe0, 0x60, \ + 0x0c, 0x35, 0x87, 0xf5, \ + 0x08, 0x14, 0x60, 0x1c, \ + 0x0c, 0x05, 0xe0, 0x84, \ + 0x88, 0x33, 0xe0, 0x8c, \ + 0xe0, 0x30, 0x46, 0x23, \ + 0xe0, 0x60, 0x46, 0x03, \ + 0xe1, 0xd0, 0x0c, 0x29, \ + 0xe0, 0x84, 0x44, 0x0a, \ + 0x00, 0x04, 0x82, 0xe5, \ + 0x8c, 0x1b, 0x7b, 0x00, \ + 0xe8, 0x08, 0x46, 0x08, \ + 0xe0, 0x84, 0x0c, 0x15, \ + 0xe0, 0x1c, 0x0c, 0x25, \ + 0xe0, 0x84, 0x06, 0x14, \ + 0x44, 0x03, 0x86, 0x23, \ + 0x98, 0x04, 0xe0, 0x60, \ + 0xe0, 0x98, 0x4c, 0x85, \ + 0xec, 0x04, 0x00, 0x00, \ + 0xde, 0x65, 0x81, 0x1d, \ + 0x0c, 0x35, 0x83, 0xb5, \ + 0xa4, 0x53, 0x60, 0x40, \ + 0xe0, 0xa2, 0x46, 0x3a, \ + 0x82, 0xa5, 0x86, 0x5d, \ + 0x00, 0x19, 0x82, 0x03, \ + 0x82, 0x11, 0xe1, 0xe0, \ + 0xe1, 0xe0, 0x40, 0x11, \ + 0xe0, 0x90, 0x0c, 0x25, \ + 0xe0, 0x1c, 0x0c, 0x05, \ + 0xe0, 0x84, 0x0c, 0x15, \ + 0xe0, 0x84, 0x06, 0x14, \ + 0x40, 0x23, 0x86, 0x03, \ + 0x4c, 0x85, 0xe0, 0x30, \ + 0xa4, 0x04, 0xe0, 0x98, \ + 0xe0, 0xa4, 0x0c, 0x35, \ + 0xe0, 0x90, 0x0c, 0x25, \ + 0x86, 0x2d, 0x80, 0x33, \ + 0x0c, 0x35, 0x86, 0xc5, \ + 0x80, 0x33, 0xe0, 0x34, \ + 0x90, 0xa5, 0x87, 0x2d, \ + 0x60, 0x1c, 0x0c, 0x25, \ + 0xe4, 0x04, 0x08, 0x10, \ + 0x60, 0x3c, 0x0c, 0x35, \ + 0xe0, 0x84, 0x02, 0x44, \ + 0xe0, 0x84, 0x4c, 0x45, \ + 0x44, 0x03, 0x82, 0x23, \ + 0x42, 0x32, 0x60, 0x30, \ + 0x44, 0x13, 0xf0, 0x80, \ + 0x0c, 0x09, 0xe0, 0x60, \ + 0x40, 0x0a, 0xe1, 0xd0, \ + 0x30, 0xf8, 0xe0, 0x84, \ + 0xe7, 0xb5, 0xdf, 0xfc, \ + 0x86, 0xf5, 0xa1, 0x1d, \ + 0x60, 0x1c, 0x0c, 0x25, \ + 0xe0, 0x84, 0x06, 0x14, \ + 0xe0, 0x40, 0x0c, 0x45, \ + 0x06, 0x33, 0x84, 0x33, \ + 0x48, 0x4a, 0xe0, 0x30, \ + 0xa0, 0x33, 0xe0, 0xa2, \ + 0x82, 0x85, 0x88, 0x3d, \ + 0x04, 0x09, 0x82, 0x23, \ + 0x82, 0x01, 0xe1, 0xe0, \ + 0xe1, 0xe0, 0x44, 0x01, \ + 0x0c, 0x25, 0xa4, 0x04, \ + 0x0c, 0x15, 0xe0, 0x1c, \ + 0x06, 0x14, 0xe0, 0x84, \ + 0x86, 0x23, 0xe0, 0x84, \ + 0xe0, 0x30, 0x44, 0x03, \ + 0x8f, 0x01, 0x02, 0x71, \ + 0x60, 0x98, 0x4c, 0x05, \ + 0x31, 0x88, 0x8f, 0x1b, \ + 0x8c, 0x0b, 0xdf, 0xfc, \ + 0xf2, 0xfc, 0x0c, 0x0c, \ + 0xe0, 0x04, 0x27, 0x08, \ + 0xe0, 0xf8, 0x0c, 0x05, \ + 0xe0, 0xfc, 0x0c, 0x15, \ + 0xe0, 0x94, 0x4c, 0x25, \ + 0x60, 0x98, 0x4c, 0x85, \ + 0xf1, 0x80, 0x40, 0x22, \ + 0x60, 0xbc, 0x4c, 0x05, \ + 0x4c, 0x25, 0xa2, 0x23, \ + 0xe1, 0x25, 0xe0, 0xc0, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0x81, 0x61, 0x02, 0x24, \ + 0x60, 0x20, 0x00, 0x15, \ + 0x00, 0x45, 0x81, 0x71, \ + 0x88, 0x34, 0xe0, 0xa0, \ + 0xc8, 0xc4, 0x8a, 0x54, \ + 0x00, 0x35, 0xca, 0x24, \ + 0x86, 0x53, 0x60, 0x18, \ + 0x58, 0x52, 0x98, 0x53, \ + 0x82, 0x5b, 0x70, 0x88, \ + 0x04, 0x40, 0x55, 0xce, \ + 0x81, 0xd5, 0xec, 0x10, \ + 0x82, 0x71, 0x02, 0x84, \ + 0x7c, 0x18, 0x10, 0x85, \ + 0xf0, 0x98, 0x52, 0x82, \ + 0x60, 0x10, 0x12, 0x95, \ + 0xb0, 0xc3, 0x88, 0x61, \ + 0xd5, 0xce, 0x92, 0xc3, \ + 0xfc, 0x75, 0x85, 0x7d, \ + 0x60, 0x88, 0x18, 0x44, \ + 0xec, 0x04, 0x08, 0x40, \ + 0x70, 0xb0, 0x4a, 0x12, \ + 0x0a, 0x50, 0x98, 0x33, \ + 0x18, 0xc0, 0x6c, 0x30, \ + 0x81, 0xf5, 0xec, 0x40, \ + 0x82, 0x21, 0x4d, 0x74, \ + 0x02, 0x85, 0x82, 0x64, \ + 0xc4, 0x77, 0xfc, 0x30, \ + 0x7c, 0x30, 0x0c, 0x95, \ + 0x0c, 0x65, 0x88, 0xc1, \ + 0xb2, 0x83, 0xfc, 0x1c, \ + 0xd5, 0x8e, 0x8c, 0x83, \ + 0xfc, 0x35, 0x89, 0x2d, \ + 0xf0, 0x1c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xed, 0x80, 0x1e, 0xf0, \ + 0x60, 0xbc, 0x00, 0x75, \ + 0x00, 0x35, 0xff, 0x61, \ + 0x81, 0x11, 0x60, 0xc0, \ + 0x60, 0x18, 0x00, 0x25, \ + 0x40, 0x15, 0x81, 0x81, \ + 0x40, 0x65, 0xe0, 0x7c, \ + 0x00, 0x15, 0xe0, 0x88, \ + 0x00, 0xc5, 0xe0, 0xb4, \ + 0x40, 0x65, 0xe0, 0xa8, \ + 0x25, 0x3e, 0xe1, 0x3c, \ + 0xe0, 0x88, 0x06, 0x74, \ + 0x60, 0xd4, 0x00, 0x35, \ + 0x00, 0x45, 0x86, 0x23, \ + 0x16, 0x20, 0x60, 0x6c, \ + 0x4a, 0xca, 0xec, 0x40, \ + 0x81, 0x21, 0x66, 0x86, \ + 0x46, 0x86, 0x21, 0x3e, \ + 0x28, 0x33, 0xe0, 0x3c, \ + 0x1b, 0x3e, 0xa0, 0x51, \ + 0xe4, 0x40, 0x06, 0x10, \ + 0x08, 0x10, 0x0b, 0x37, \ + 0x7c, 0x3e, 0xe4, 0x10, \ + 0xe4, 0x08, 0x06, 0x10, \ + 0xcb, 0x57, 0x02, 0x11, \ + 0x85, 0x37, 0x07, 0x47, \ + 0x83, 0x17, 0x19, 0x5e, \ + 0x81, 0x51, 0x3e, 0x4e, \ + 0x81, 0x41, 0x09, 0x3e, \ + 0x00, 0x15, 0x95, 0x1e, \ + 0x93, 0x1e, 0xe0, 0xd8, \ + 0xe0, 0xdc, 0x00, 0x15, \ + 0x00, 0x15, 0xfe, 0x1e, \ + 0x8d, 0x1e, 0xe0, 0x28, \ + 0xe0, 0x2c, 0x00, 0x15, \ + 0x00, 0x15, 0x91, 0x1e, \ + 0x8f, 0xce, 0xe0, 0x24, \ + 0x1d, 0x1e, 0xa3, 0x7e, \ + 0xe0, 0x84, 0x02, 0xc4, \ + 0x81, 0x11, 0x0b, 0x1e, \ + 0x8e, 0xc3, 0x2b, 0x1e, \ + 0x81, 0x11, 0x3c, 0xce, \ + 0x1a, 0x38, 0x97, 0x1e, \ + 0x0a, 0xa0, 0xe0, 0x00, \ + 0x0b, 0x0a, 0xe4, 0x04, \ + 0x1a, 0x48, 0xe7, 0x20, \ + 0x16, 0x15, 0xc0, 0x00, \ + 0xa2, 0x4d, 0x60, 0x10, \ + 0x5e, 0x45, 0xad, 0x6e, \ + 0x5e, 0x25, 0xc0, 0x5c, \ + 0x98, 0xf5, 0xc0, 0x4c, \ + 0xc4, 0x17, 0x27, 0xbe, \ + 0x60, 0xac, 0x00, 0x35, \ + 0x19, 0x74, 0x8c, 0xbb, \ + 0x06, 0xc5, 0x8e, 0x6b, \ + 0xab, 0x34, 0xfc, 0x04, \ + 0x04, 0xcd, 0xae, 0xc3, \ + 0x2b, 0x3e, 0x88, 0x33, \ + 0x9c, 0x00, 0x58, 0x20, \ + 0x4e, 0x60, 0x0e, 0x05, \ + 0x29, 0x8e, 0x9c, 0x00, \ + 0xaf, 0x4e, 0x98, 0x8b, \ + 0xe0, 0xb0, 0x00, 0x35, \ + 0x8b, 0xc4, 0x8f, 0x44, \ + 0xfc, 0x04, 0x06, 0x15, \ + 0x60, 0x84, 0x06, 0x14, \ + 0x88, 0x33, 0x8e, 0x13, \ + 0xe0, 0x32, 0x46, 0x3a, \ + 0x46, 0x3a, 0x9d, 0xc4, \ + 0x90, 0xcb, 0x66, 0xb2, \ + 0xe5, 0x00, 0x06, 0x30, \ + 0x46, 0x3a, 0xcf, 0x37, \ + 0x0d, 0x34, 0xe6, 0x92, \ + 0x42, 0x1a, 0x86, 0x13, \ + 0x42, 0x1a, 0xf0, 0x0a, \ + 0x02, 0x10, 0xe6, 0x8e, \ + 0xd1, 0x17, 0xe6, 0x00, \ + 0x1e, 0x45, 0x82, 0xcd, \ + 0x58, 0x20, 0x00, 0x5c, \ + 0x1e, 0x85, 0x9c, 0x00, \ + 0x1e, 0x75, 0x80, 0x50, \ + 0x82, 0x65, 0x80, 0x44, \ + 0x96, 0x6b, 0x2f, 0x44, \ + 0xe0, 0x88, 0x40, 0x95, \ + 0xa1, 0x4b, 0xa9, 0x84, \ + 0x81, 0x11, 0x80, 0xc5, \ + 0x81, 0xe5, 0xab, 0x1e, \ + 0x96, 0x6b, 0x01, 0xe5, \ + 0x83, 0x41, 0x3c, 0x14, \ + 0xe1, 0x3c, 0x40, 0x95, \ + 0x94, 0x13, 0xa5, 0x24, \ + 0x7b, 0x08, 0x44, 0x12, \ + 0xa5, 0x2e, 0x81, 0x11, \ + 0xa3, 0x74, 0xab, 0x1e, \ + 0xab, 0x24, 0x9b, 0x14, \ + 0x82, 0x2d, 0xa7, 0xb4, \ + 0xc0, 0x00, 0x13, 0xf8, \ + 0x12, 0x78, 0xa3, 0x4d, \ + 0x4c, 0x80, 0x40, 0x00, \ + 0x27, 0xce, 0xdc, 0x00, \ + 0x16, 0x15, 0x83, 0x21, \ + 0xaf, 0x2e, 0xe0, 0x10, \ + 0x60, 0x20, 0x16, 0x25, \ + 0xe0, 0x88, 0x08, 0x14, \ + 0xe0, 0x20, 0x00, 0x35, \ + 0xe0, 0x10, 0x00, 0x15, \ + 0xc4, 0x27, 0x21, 0xc4, \ + 0xfc, 0x10, 0x06, 0x65, \ + 0xfc, 0x08, 0x06, 0x25, \ + 0x7c, 0x10, 0x02, 0x45, \ + 0x10, 0x05, 0x99, 0xad, \ + 0x93, 0xc4, 0x8c, 0x23, \ + 0x9e, 0x65, 0x99, 0xad, \ + 0x83, 0xad, 0xfe, 0x14, \ + 0xee, 0x34, 0xaa, 0x85, \ + 0xfc, 0xc4, 0x82, 0x14, \ + 0x02, 0x15, 0xc4, 0x37, \ + 0x42, 0xc2, 0xfc, 0x0c, \ + 0x18, 0x14, 0xfa, 0x04, \ + 0x4c, 0x22, 0xe0, 0x8c, \ + 0xc2, 0x67, 0xf1, 0xb0, \ + 0x1e, 0x95, 0x90, 0x6d, \ + 0x4c, 0x80, 0x00, 0x58, \ + 0x0f, 0xb8, 0x9c, 0x00, \ + 0x29, 0x8e, 0x80, 0x00, \ + 0xe4, 0x10, 0x0e, 0x40, \ + 0xc7, 0x77, 0x40, 0xc4, \ + 0xb3, 0x11, 0x1f, 0x1e, \ + 0xe6, 0x86, 0x44, 0x2a, \ + 0x18, 0x25, 0x9d, 0x14, \ + 0x10, 0x20, 0x7c, 0x0c, \ + 0x1f, 0x34, 0xe4, 0x08, \ + 0x4e, 0x7a, 0xc5, 0x87, \ + 0x82, 0xcb, 0x66, 0x86, \ + 0xad, 0x11, 0x08, 0x83, \ + 0x48, 0x3a, 0x10, 0x71, \ + 0x49, 0x77, 0xe6, 0x86, \ + 0xe0, 0x98, 0x02, 0x24, \ + 0x06, 0x06, 0x0e, 0x83, \ + 0x02, 0x83, 0xe0, 0x04, \ + 0x10, 0x43, 0x83, 0x11, \ + 0x13, 0xe5, 0x8f, 0x81, \ + 0x1f, 0x5e, 0x83, 0x71, \ + 0x16, 0x15, 0x80, 0x5b, \ + 0x16, 0x65, 0xf8, 0x30, \ + 0x16, 0x75, 0xf8, 0x20, \ + 0x29, 0x8e, 0xf8, 0x10, \ + 0x2c, 0x84, 0xc4, 0x17, \ + 0x06, 0xc5, 0xc4, 0x67, \ + 0xc4, 0x77, 0x7c, 0x04, \ + 0xfc, 0x18, 0x06, 0x65, \ + 0x7c, 0x1c, 0x06, 0x75, \ + 0x06, 0x35, 0xc4, 0x87, \ + 0x29, 0x04, 0xfc, 0x20, \ + 0x8e, 0x63, 0x98, 0x63, \ + 0x86, 0x63, 0x15, 0x34, \ + 0x7a, 0x18, 0x46, 0x32, \ + 0xe0, 0x84, 0x0c, 0x24, \ + 0x80, 0x6d, 0xa6, 0x63, \ + 0x1d, 0x84, 0x92, 0x45, \ + 0xe4, 0x10, 0x0e, 0x40, \ + 0xc7, 0x77, 0x0a, 0x0b, \ + 0x84, 0x43, 0x42, 0xc4, \ + 0x66, 0xa2, 0x4e, 0x7a, \ + 0x9f, 0x54, 0xc4, 0x47, \ + 0x7c, 0x04, 0x18, 0x15, \ + 0x45, 0x77, 0x84, 0x71, \ + 0x08, 0x73, 0x90, 0xcb, \ + 0x02, 0x73, 0x91, 0x81, \ + 0x48, 0x72, 0x90, 0x11, \ + 0x04, 0x14, 0x70, 0x8c, \ + 0x06, 0x06, 0xe1, 0x90, \ + 0x89, 0x11, 0x60, 0x08, \ + 0x87, 0x71, 0x0b, 0xf5, \ + 0xf8, 0x10, 0x16, 0x75, \ + 0xc4, 0x77, 0xac, 0xc4, \ + 0x7c, 0x1c, 0x06, 0x65, \ + 0x06, 0x35, 0xc4, 0xc7, \ + 0x89, 0xc4, 0xfc, 0x30, \ + 0x46, 0xc2, 0x8c, 0x33, \ + 0x4c, 0x22, 0xfa, 0x0c, \ + 0xc2, 0x67, 0xf1, 0x8c, \ + 0x9e, 0xe5, 0x90, 0x6d, \ + 0x18, 0x40, 0x29, 0x8e, \ + 0x02, 0x15, 0xe4, 0x10, \ + 0xc7, 0xc7, 0x7c, 0x1c, \ + 0x84, 0x43, 0x1d, 0x74, \ + 0x02, 0x43, 0x91, 0x81, \ + 0x44, 0xca, 0x88, 0x11, \ + 0x86, 0x43, 0x66, 0x9e, \ + 0x8e, 0xcb, 0x44, 0x47, \ + 0x06, 0x06, 0x04, 0x21, \ + 0x45, 0x27, 0xe0, 0x08, \ + 0x04, 0x43, 0x85, 0x71, \ + 0xe1, 0x8c, 0x04, 0x14, \ + 0x85, 0x11, 0x07, 0x35, \ + 0x8a, 0x0b, 0x00, 0x6b, \ + 0xa3, 0x74, 0xad, 0x94, \ + 0xa7, 0xc4, 0xaf, 0x44, \ + 0x8d, 0x55, 0x9f, 0x54, \ + 0xc4, 0x77, 0xac, 0x74, \ + 0xfc, 0x1c, 0x06, 0x15, \ + 0x42, 0x32, 0xbe, 0x34, \ + 0x06, 0x14, 0xfa, 0x04, \ + 0x4c, 0x22, 0xe0, 0x84, \ + 0xc2, 0x67, 0xf1, 0x8c, \ + 0x92, 0xe5, 0x90, 0x6d, \ + 0x18, 0x40, 0x29, 0x8e, \ + 0x00, 0x35, 0xe4, 0x10, \ + 0xc7, 0xc7, 0x60, 0x14, \ + 0x84, 0x43, 0x1d, 0x84, \ + 0x7c, 0x1c, 0x06, 0x35, \ + 0x58, 0xca, 0x87, 0x71, \ + 0x10, 0xc1, 0xe6, 0xa2, \ + 0x49, 0xc7, 0x86, 0x13, \ + 0x18, 0x43, 0x84, 0x31, \ + 0x10, 0xcb, 0xc4, 0x17, \ + 0x04, 0x34, 0x82, 0x43, \ + 0x8f, 0x81, 0x61, 0x88, \ + 0x60, 0x04, 0x06, 0x06, \ + 0x44, 0x2a, 0x89, 0x11, \ + 0x82, 0x23, 0xe6, 0xb2, \ + 0x11, 0x24, 0xef, 0x2f, \ + 0x42, 0x4a, 0x84, 0x43, \ + 0x42, 0x1a, 0xf0, 0x0a, \ + 0x86, 0x13, 0xe6, 0x8a, \ + 0x82, 0x6d, 0xf1, 0x1f, \ + 0x80, 0x50, 0x1e, 0x65, \ + 0x80, 0x58, 0x1e, 0x95, \ + 0x80, 0x44, 0x1e, 0x75, \ + 0xaf, 0x44, 0x88, 0xc5, \ + 0xe0, 0x88, 0x40, 0x95, \ + 0x23, 0x74, 0x97, 0x14, \ + 0xa7, 0xc4, 0xa3, 0x4b, \ + 0x97, 0x1e, 0x82, 0x11, \ + 0x00, 0x15, 0x84, 0x95, \ + 0x8e, 0xa3, 0x60, 0xec, \ + 0x60, 0xa0, 0x00, 0x25, \ + 0x85, 0x41, 0x87, 0x31, \ + 0x44, 0x2a, 0x94, 0x13, \ + 0x06, 0x10, 0x66, 0x8e, \ + 0x85, 0x3d, 0xe4, 0x10, \ + 0x40, 0x48, 0x5e, 0x15, \ + 0x40, 0x15, 0x83, 0x11, \ + 0x81, 0xb5, 0xe0, 0x7c, \ + 0x81, 0x95, 0xad, 0x94, \ + 0x90, 0x6b, 0x2d, 0x94, \ + 0xa7, 0xc4, 0xaf, 0x44, \ + 0x81, 0x35, 0xa3, 0x74, \ + 0xa7, 0xc4, 0xaf, 0x44, \ + 0x88, 0xb1, 0x0c, 0x8b, \ + 0x92, 0x6b, 0x18, 0x2b, \ + 0x8a, 0xab, 0x25, 0x14, \ + 0xf0, 0xa8, 0x52, 0x72, \ + 0x25, 0xa8, 0x82, 0x9d, \ + 0xa1, 0x6d, 0x9f, 0xfc, \ + 0x97, 0x24, 0x82, 0x15, \ + 0xe0, 0xec, 0x00, 0x15, \ + 0x83, 0x2d, 0x82, 0x21, \ + 0xc0, 0x88, 0x40, 0x65, \ + 0x1e, 0xf0, 0x01, 0x11, \ + 0x40, 0x15, 0xe5, 0x80, \ + 0x0c, 0x0c, 0xe0, 0x98, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xed, 0x80, 0x1e, 0xf0, \ + 0xff, 0x31, 0x01, 0x21, \ + 0x60, 0x80, 0x40, 0x25, \ + 0xe0, 0x3c, 0x4a, 0x86, \ + 0x60, 0x6c, 0x00, 0x25, \ + 0x00, 0x15, 0x81, 0xb1, \ + 0x81, 0x61, 0x60, 0xb4, \ + 0x60, 0x8c, 0x40, 0x35, \ + 0x40, 0x35, 0x81, 0x91, \ + 0xa4, 0x53, 0x61, 0x40, \ + 0x60, 0xc0, 0x00, 0x35, \ + 0xe4, 0x40, 0x04, 0x10, \ + 0x60, 0xa8, 0x00, 0xc5, \ + 0x00, 0x45, 0x8b, 0x27, \ + 0xa9, 0x3e, 0xe0, 0xbc, \ + 0xe0, 0x18, 0x00, 0xa5, \ + 0x04, 0x10, 0x09, 0x2e, \ + 0x07, 0x27, 0xe4, 0x10, \ + 0xe6, 0x86, 0x46, 0xca, \ + 0x44, 0x42, 0x7e, 0x2e, \ + 0x44, 0x27, 0xf1, 0xb0, \ + 0x50, 0xa2, 0xa0, 0x31, \ + 0x04, 0x10, 0x70, 0x88, \ + 0x02, 0x11, 0xe4, 0x08, \ + 0x05, 0x27, 0xcb, 0x37, \ + 0x1f, 0x3e, 0x83, 0x17, \ + 0xe0, 0x88, 0x06, 0x44, \ + 0x86, 0xa3, 0x21, 0x5e, \ + 0x81, 0x31, 0x0f, 0x2e, \ + 0x00, 0x15, 0x9b, 0x1e, \ + 0xa5, 0x1e, 0xe0, 0xe0, \ + 0xe0, 0xe4, 0x00, 0x15, \ + 0x00, 0x15, 0x99, 0x1e, \ + 0x8d, 0x1e, 0xe0, 0xe8, \ + 0xe0, 0x28, 0x00, 0x15, \ + 0x00, 0x15, 0x93, 0x1e, \ + 0x97, 0x1e, 0xe0, 0x2c, \ + 0xe0, 0x24, 0x00, 0x15, \ + 0x23, 0x1e, 0x95, 0xce, \ + 0xe0, 0x84, 0x02, 0xc4, \ + 0xb8, 0x1b, 0x11, 0x1e, \ + 0xff, 0x11, 0x7c, 0x1e, \ + 0x81, 0x11, 0x0b, 0x1e, \ + 0x81, 0xc1, 0x1d, 0x1e, \ + 0x0d, 0x0a, 0xb1, 0xa5, \ + 0x18, 0xb8, 0xe3, 0x1c, \ + 0xa2, 0xcd, 0xc0, 0x00, \ + 0x5c, 0x00, 0x4e, 0x30, \ + 0xdc, 0x00, 0x46, 0xc0, \ + 0x2d, 0xae, 0x96, 0x55, \ + 0x20, 0x24, 0x8c, 0xab, \ + 0x00, 0x15, 0x88, 0x6b, \ + 0x98, 0xb3, 0x60, 0xac, \ + 0xc4, 0x27, 0x9f, 0x44, \ + 0xfc, 0x08, 0x02, 0x15, \ + 0xf1, 0x90, 0x4e, 0x12, \ + 0x0c, 0x05, 0x86, 0x7d, \ + 0x9c, 0x00, 0x4e, 0x30, \ + 0x8e, 0x9b, 0x2f, 0x9e, \ + 0xe0, 0xb0, 0x00, 0x55, \ + 0x91, 0x74, 0x95, 0x14, \ + 0xfc, 0x08, 0x0a, 0x25, \ + 0x60, 0x84, 0x0a, 0x24, \ + 0x82, 0x53, 0x88, 0x23, \ + 0xe0, 0x1e, 0x4a, 0x5a, \ + 0x4a, 0x5a, 0xa3, 0x74, \ + 0x92, 0x7b, 0x66, 0x9e, \ + 0xe5, 0x00, 0x0a, 0x50, \ + 0x4a, 0x5a, 0xcf, 0x57, \ + 0x93, 0x14, 0xe6, 0x86, \ + 0x44, 0x2a, 0x8a, 0x23, \ + 0x44, 0x2a, 0xf0, 0x0a, \ + 0x04, 0x20, 0xe6, 0x86, \ + 0xd1, 0x27, 0xe6, 0x00, \ + 0x1e, 0x95, 0x84, 0x7d, \ + 0x4e, 0x30, 0x00, 0x5c, \ + 0x82, 0x05, 0x9c, 0x00, \ + 0xa1, 0xcb, 0x0c, 0x4b, \ + 0xe0, 0x8c, 0x40, 0x45, \ + 0x01, 0xe5, 0x80, 0xa5, \ + 0x01, 0xd5, 0x81, 0xb1, \ + 0x7c, 0x24, 0x8c, 0x4b, \ + 0x40, 0x45, 0x81, 0xb1, \ + 0x83, 0xc1, 0x61, 0x40, \ + 0x88, 0x23, 0xa9, 0x14, \ + 0xfa, 0x04, 0x42, 0x22, \ + 0xaf, 0x94, 0xa9, 0x1e, \ + 0x94, 0x6b, 0x21, 0x14, \ + 0x82, 0xbd, 0xad, 0xa4, \ + 0xc0, 0x00, 0x13, 0x48, \ + 0x83, 0x31, 0x23, 0xcd, \ + 0x5c, 0x00, 0x46, 0xc0, \ + 0xdc, 0x00, 0x4a, 0x90, \ + 0xc0, 0x00, 0x11, 0xb8, \ + 0xad, 0xbe, 0xab, 0x7e, \ + 0xf8, 0x10, 0x14, 0x25, \ + 0xe0, 0x20, 0x00, 0x55, \ + 0xf8, 0x20, 0x14, 0xc5, \ + 0xc4, 0x27, 0x25, 0x14, \ + 0xfc, 0x08, 0x0a, 0xb5, \ + 0x60, 0x10, 0x00, 0x25, \ + 0x0a, 0x75, 0xc4, 0xc7, \ + 0x83, 0x6d, 0x7c, 0x30, \ + 0xfc, 0x30, 0x04, 0xc5, \ + 0x8e, 0xb3, 0x0e, 0x65, \ + 0x83, 0x6d, 0x99, 0x14, \ + 0x8d, 0x14, 0x9c, 0x25, \ + 0x83, 0x6d, 0xa8, 0x74, \ + 0x02, 0x14, 0xa6, 0x85, \ + 0x89, 0x24, 0xc4, 0x77, \ + 0xfc, 0x1c, 0x02, 0x15, \ + 0xfa, 0x04, 0x44, 0x22, \ + 0xe0, 0x8c, 0x02, 0x24, \ + 0xf1, 0x84, 0x4a, 0xb2, \ + 0x92, 0x5d, 0xc2, 0x57, \ + 0x80, 0x00, 0x0f, 0xa8, \ + 0x12, 0xc0, 0x2f, 0x9e, \ + 0x27, 0x8e, 0xe4, 0x10, \ + 0xc0, 0x14, 0xc7, 0x97, \ + 0xb3, 0x11, 0x3e, 0x1e, \ + 0xe6, 0x86, 0x50, 0xba, \ + 0x3e, 0xb4, 0xa3, 0x14, \ + 0xc5, 0x87, 0x84, 0x81, \ + 0x66, 0x86, 0x52, 0x9a, \ + 0x16, 0xb5, 0x98, 0x83, \ + 0xad, 0xc1, 0x7c, 0x1c, \ + 0x58, 0x2a, 0x10, 0x91, \ + 0x49, 0x97, 0xe6, 0xb2, \ + 0xe0, 0x04, 0x0e, 0x06, \ + 0x04, 0xb4, 0x12, 0x83, \ + 0x04, 0x83, 0xe0, 0x98, \ + 0x10, 0xc3, 0x83, 0x91, \ + 0x13, 0x45, 0x8f, 0x81, \ + 0xa7, 0x8e, 0x83, 0x21, \ + 0x14, 0x75, 0xa8, 0x24, \ + 0x14, 0x85, 0xe0, 0x10, \ + 0x2f, 0x9e, 0xe0, 0x20, \ + 0x14, 0x95, 0xc4, 0x27, \ + 0xc4, 0x77, 0x60, 0x30, \ + 0x7c, 0x08, 0x0a, 0x15, \ + 0x0a, 0x75, 0xc4, 0x87, \ + 0x0a, 0x85, 0xfc, 0x1c, \ + 0xc4, 0x97, 0x7c, 0x20, \ + 0xfc, 0x24, 0x0a, 0x55, \ + 0x82, 0x73, 0x1b, 0x14, \ + 0x90, 0x73, 0x2f, 0x94, \ + 0x0a, 0xb4, 0x0a, 0x73, \ + 0x4e, 0x12, 0xe0, 0x84, \ + 0xae, 0x53, 0xfa, 0x1c, \ + 0x90, 0xe5, 0x92, 0x5d, \ + 0x10, 0xc0, 0x23, 0x94, \ + 0x42, 0x14, 0xe4, 0x10, \ + 0x96, 0xc3, 0xc7, 0x87, \ + 0x66, 0xa6, 0x50, 0x8a, \ + 0x02, 0x15, 0xc4, 0xc7, \ + 0x84, 0x81, 0xfc, 0x08, \ + 0x98, 0x83, 0xc5, 0x87, \ + 0x90, 0x11, 0x02, 0x83, \ + 0x70, 0x9c, 0x58, 0x82, \ + 0xe1, 0x90, 0x16, 0x14, \ + 0x91, 0x81, 0x12, 0x1b, \ + 0x0e, 0x06, 0x0b, 0x95, \ + 0xa7, 0x8e, 0xe0, 0x08, \ + 0x14, 0x15, 0xa8, 0x84, \ + 0xc4, 0x87, 0xe0, 0x10, \ + 0x7c, 0x20, 0x0a, 0x75, \ + 0x0a, 0x15, 0xc4, 0x17, \ + 0x8f, 0x54, 0xfc, 0x04, \ + 0x4e, 0x52, 0x8e, 0x13, \ + 0x4a, 0xb2, 0xfa, 0x04, \ + 0xc2, 0x57, 0xf1, 0x9c, \ + 0x86, 0x65, 0x92, 0x5d, \ + 0x85, 0x91, 0x2f, 0x9e, \ + 0x7c, 0x20, 0x04, 0x15, \ + 0xe4, 0x10, 0x04, 0xc0, \ + 0xc7, 0x27, 0x23, 0x84, \ + 0x02, 0xc3, 0x96, 0xc3, \ + 0x44, 0x2a, 0x88, 0x11, \ + 0x8e, 0xc3, 0x66, 0xa2, \ + 0x16, 0x14, 0x44, 0xc7, \ + 0x04, 0x21, 0xe1, 0x8c, \ + 0x45, 0x27, 0x90, 0x1b, \ + 0x04, 0xc3, 0x91, 0x81, \ + 0xe0, 0x08, 0x0e, 0x06, \ + 0x85, 0x21, 0x05, 0xd5, \ + 0x92, 0x5b, 0x0d, 0x05, \ + 0xc4, 0x77, 0x7e, 0x24, \ + 0xfc, 0x1c, 0x0a, 0x15, \ + 0xfa, 0x04, 0x44, 0x22, \ + 0xe0, 0x84, 0x02, 0x24, \ + 0xf1, 0x84, 0x4a, 0xb2, \ + 0x92, 0x5d, 0xc2, 0x57, \ + 0xaf, 0x9e, 0x98, 0x05, \ + 0x10, 0xc0, 0x27, 0x8e, \ + 0x00, 0x15, 0xe4, 0x10, \ + 0xc7, 0x87, 0x60, 0x14, \ + 0x96, 0xc3, 0x23, 0x94, \ + 0x7c, 0x1c, 0x02, 0x15, \ + 0xe0, 0x04, 0x0e, 0x06, \ + 0xe6, 0xa6, 0x50, 0x8a, \ + 0x82, 0x23, 0x10, 0x81, \ + 0x84, 0x11, 0x49, 0x87, \ + 0xc4, 0x27, 0x10, 0xc3, \ + 0x61, 0x88, 0x16, 0x14, \ + 0x12, 0x1b, 0x84, 0xc3, \ + 0x07, 0x21, 0x8f, 0x81, \ + 0x42, 0xba, 0x89, 0x91, \ + 0x92, 0x13, 0xe6, 0x86, \ + 0xe5, 0x1f, 0x17, 0x24, \ + 0x42, 0xca, 0x82, 0xc3, \ + 0x42, 0x1a, 0xf0, 0x0a, \ + 0x8e, 0x13, 0xe6, 0x8a, \ + 0x82, 0x5d, 0xf1, 0x1f, \ + 0x80, 0x5c, 0x1e, 0x55, \ + 0x40, 0x45, 0x88, 0xc5, \ + 0xa3, 0x3b, 0x60, 0x8c, \ + 0x88, 0x2b, 0x1d, 0x14, \ + 0x48, 0x20, 0x27, 0x84, \ + 0xad, 0xb4, 0xdc, 0x00, \ + 0x82, 0x11, 0x2b, 0x74, \ + 0x86, 0x95, 0x9d, 0x1e, \ + 0x60, 0xf0, 0x00, 0x15, \ + 0x98, 0x4b, 0x84, 0xcb, \ + 0x71, 0x84, 0x46, 0x22, \ + 0x02, 0x30, 0xff, 0x21, \ + 0x29, 0x24, 0xec, 0x40, \ + 0x95, 0x14, 0x84, 0x1d, \ + 0x5c, 0x00, 0x44, 0x30, \ + 0x1e, 0x25, 0x83, 0x3d, \ + 0x83, 0x11, 0x40, 0x50, \ + 0x60, 0x80, 0x40, 0x15, \ + 0x8b, 0x4e, 0x85, 0x31, \ + 0x81, 0x45, 0xa9, 0x2e, \ + 0xad, 0xb4, 0xa7, 0x84, \ + 0x06, 0xcb, 0xab, 0x74, \ + 0x08, 0x40, 0x82, 0x61, \ + 0x10, 0x80, 0x6c, 0x04, \ + 0x14, 0xa0, 0xec, 0x10, \ + 0x8a, 0x9b, 0x6c, 0x10, \ + 0x8e, 0x3b, 0x01, 0x45, \ + 0x92, 0x5b, 0x7f, 0x35, \ + 0x83, 0x4d, 0xa9, 0x14, \ + 0x9f, 0xfc, 0x27, 0x58, \ + 0xa1, 0x3d, 0x8b, 0x34, \ + 0x9d, 0x24, 0x82, 0x15, \ + 0xe0, 0xf0, 0x00, 0x15, \ + 0x83, 0x2d, 0x82, 0x21, \ + 0xc0, 0x8c, 0x40, 0x35, \ + 0x1e, 0xf0, 0x01, 0x11, \ + 0x40, 0x15, 0xe5, 0x80, \ + 0x0c, 0x0c, 0xe0, 0x98, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xec, 0xa0, 0x1e, 0xf0, \ + 0x60, 0xbc, 0x00, 0x85, \ + 0x40, 0x15, 0xff, 0x11, \ + 0x00, 0x15, 0xe0, 0x90, \ + 0x91, 0x1e, 0xe0, 0xc0, \ + 0xe0, 0x30, 0x00, 0x15, \ + 0xe0, 0x18, 0x00, 0xb5, \ + 0x00, 0x15, 0x8d, 0x1e, \ + 0x8b, 0x1e, 0xe0, 0x24, \ + 0xe0, 0x64, 0x00, 0x15, \ + 0x89, 0xbe, 0xfe, 0x8e, \ + 0x81, 0x11, 0x0f, 0x1e, \ + 0x81, 0x11, 0x13, 0x1e, \ + 0x81, 0x11, 0x3e, 0x1e, \ + 0xad, 0xb5, 0xfc, 0x1e, \ + 0xe3, 0x1c, 0x05, 0x0a, \ + 0xc0, 0x00, 0x16, 0xe8, \ + 0x60, 0x20, 0x00, 0x75, \ + 0xe0, 0x88, 0x06, 0xa4, \ + 0x7c, 0x0c, 0x16, 0x85, \ + 0x8f, 0x14, 0x82, 0x21, \ + 0x0e, 0x95, 0xc4, 0x87, \ + 0xa1, 0x1b, 0x7c, 0x20, \ + 0x96, 0xc5, 0x93, 0x2e, \ + 0xb4, 0x13, 0xfe, 0x14, \ + 0x96, 0x45, 0xb0, 0x1d, \ + 0x60, 0xb4, 0x00, 0x15, \ + 0x06, 0x55, 0x96, 0x33, \ + 0x06, 0x25, 0xfa, 0x10, \ + 0xc4, 0x57, 0xe0, 0x20, \ + 0x7c, 0x14, 0x0e, 0x45, \ + 0x0e, 0xc5, 0xc4, 0x27, \ + 0x04, 0x10, 0x7c, 0x08, \ + 0x87, 0x27, 0xe4, 0x10, \ + 0xfa, 0x10, 0x44, 0x22, \ + 0x60, 0x10, 0x00, 0x95, \ + 0x08, 0x24, 0x92, 0xc3, \ + 0x4c, 0xc2, 0xe0, 0x84, \ + 0x12, 0x85, 0xf1, 0x90, \ + 0xa3, 0x6d, 0x7c, 0x20, \ + 0xa4, 0xb5, 0xbc, 0x8e, \ + 0x60, 0x14, 0x00, 0x45, \ + 0x8b, 0x94, 0xc2, 0x67, \ + 0x7c, 0x14, 0x08, 0x45, \ + 0xe4, 0x10, 0x0a, 0x80, \ + 0x98, 0x83, 0x47, 0x57, \ + 0xe6, 0xa6, 0x4a, 0x5a, \ + 0x64, 0x08, 0x16, 0x40, \ + 0x45, 0xb7, 0x88, 0x23, \ + 0x56, 0xba, 0x90, 0x51, \ + 0xc9, 0x57, 0x66, 0xa6, \ + 0xc4, 0x27, 0x0a, 0x83, \ + 0x84, 0x83, 0x08, 0xb1, \ + 0xc7, 0xb7, 0x0d, 0x44, \ + 0x44, 0x8a, 0x96, 0x83, \ + 0x44, 0x2a, 0xf0, 0x0a, \ + 0x04, 0x20, 0xe6, 0x92, \ + 0xcf, 0x27, 0xe5, 0x00, \ + 0x1e, 0xb5, 0x84, 0x6d, \ + 0x9a, 0xe5, 0x80, 0x10, \ + 0x84, 0x6d, 0xfc, 0x24, \ + 0xc0, 0x90, 0x40, 0xa5, \ + 0x89, 0xb4, 0xbe, 0x24, \ + 0xc0, 0x08, 0x5e, 0x65, \ + 0x98, 0xb5, 0xa1, 0x2b, \ + 0xe0, 0xf4, 0x00, 0x25, \ + 0x44, 0xa2, 0x91, 0x44, \ + 0x48, 0x22, 0xf1, 0x88, \ + 0x83, 0x21, 0x7a, 0x10, \ + 0xbe, 0x2e, 0x91, 0x4e, \ + 0x00, 0x65, 0x8b, 0xa5, \ + 0x42, 0xb2, 0x60, 0xb4, \ + 0x02, 0x45, 0xf0, 0x8c, \ + 0x00, 0xb5, 0xfa, 0x10, \ + 0x06, 0x60, 0xe0, 0x14, \ + 0x44, 0x47, 0xe4, 0x10, \ + 0x0e, 0x25, 0x87, 0x37, \ + 0x16, 0xc5, 0xfc, 0x10, \ + 0x4a, 0x32, 0xfc, 0x20, \ + 0x04, 0x94, 0x7a, 0x08, \ + 0x06, 0x54, 0xe0, 0x84, \ + 0x46, 0x22, 0xe0, 0x84, \ + 0xa3, 0x3d, 0xf1, 0x8c, \ + 0xc0, 0x10, 0x1e, 0xb5, \ + 0x16, 0x45, 0xa2, 0x55, \ + 0x50, 0xc2, 0x7c, 0x10, \ + 0x3c, 0xce, 0xf0, 0xa4, \ + 0xe4, 0x08, 0x16, 0xc0, \ + 0xc5, 0xb7, 0x0b, 0xc4, \ + 0x84, 0x41, 0x08, 0x83, \ + 0x8a, 0x83, 0x45, 0x47, \ + 0x66, 0xb2, 0x56, 0xba, \ + 0x48, 0x4a, 0xc4, 0x87, \ + 0xc2, 0x37, 0x66, 0xb2, \ + 0x88, 0xb1, 0x0d, 0x54, \ + 0xc7, 0xb7, 0x08, 0x41, \ + 0x96, 0x43, 0xc7, 0x47, \ + 0x48, 0x4a, 0x90, 0x43, \ + 0x48, 0x4a, 0xf0, 0x0a, \ + 0x08, 0x40, 0xe6, 0x96, \ + 0xcf, 0x47, 0xe5, 0x00, \ + 0x1e, 0xb5, 0x88, 0x3d, \ + 0x1e, 0xc5, 0x80, 0x10, \ + 0x98, 0x65, 0x80, 0x00, \ + 0x89, 0xb4, 0xfc, 0x44, \ + 0x40, 0xa5, 0x88, 0x3d, \ + 0x5e, 0x35, 0xc0, 0x90, \ + 0xbe, 0x34, 0xc0, 0x08, \ + 0xa1, 0x3b, 0xbc, 0xc4, \ + 0x00, 0x35, 0x96, 0x15, \ + 0x91, 0x44, 0xe0, 0xf4, \ + 0xf1, 0x8c, 0x46, 0xa2, \ + 0x7a, 0x10, 0x48, 0x32, \ + 0x91, 0x4e, 0x83, 0x31, \ + 0x89, 0xd5, 0xbe, 0x3e, \ + 0x10, 0xa0, 0x0c, 0x34, \ + 0x04, 0x84, 0xec, 0x04, \ + 0xa0, 0x11, 0x60, 0x88, \ + 0x7c, 0x08, 0x16, 0x65, \ + 0x02, 0x24, 0x8b, 0x17, \ + 0x0e, 0x45, 0xc4, 0x37, \ + 0xc4, 0x67, 0xfc, 0x0c, \ + 0xfc, 0x18, 0x04, 0x25, \ + 0xfa, 0x08, 0x46, 0x12, \ + 0xe0, 0x88, 0x02, 0x34, \ + 0xa2, 0xa5, 0x89, 0x1d, \ + 0xe0, 0x8c, 0x02, 0x34, \ + 0xf1, 0x84, 0x42, 0xc2, \ + 0xa0, 0xf5, 0xa3, 0x1d, \ + 0xb3, 0x41, 0x40, 0x24, \ + 0x66, 0x92, 0x48, 0xca, \ + 0x8b, 0xc4, 0xc2, 0x17, \ + 0x7c, 0x18, 0x04, 0x25, \ + 0x3c, 0x64, 0x84, 0x41, \ + 0x0a, 0x60, 0xc5, 0x47, \ + 0x8c, 0x43, 0x64, 0x10, \ + 0xad, 0x61, 0x47, 0x57, \ + 0xe6, 0xb2, 0x4a, 0x5a, \ + 0x66, 0xb2, 0x58, 0x2a, \ + 0x10, 0x51, 0xcc, 0x27, \ + 0xe6, 0x9a, 0x46, 0x3a, \ + 0x82, 0xc1, 0x49, 0x57, \ + 0xc3, 0xc7, 0x0a, 0x43, \ + 0x86, 0x43, 0x0d, 0x34, \ + 0x98, 0x43, 0x84, 0x43, \ + 0xf0, 0x0a, 0x44, 0x4a, \ + 0xe6, 0x8e, 0x44, 0x2a, \ + 0xe5, 0x00, 0x04, 0x20, \ + 0x84, 0x1d, 0xcf, 0x27, \ + 0xfc, 0x24, 0x96, 0xc5, \ + 0x40, 0xa5, 0x84, 0x1d, \ + 0x5e, 0x15, 0xc0, 0x90, \ + 0x89, 0xb5, 0xc0, 0x08, \ + 0x10, 0xa0, 0x02, 0x44, \ + 0x04, 0x54, 0xec, 0x04, \ + 0xe0, 0x88, 0x06, 0x84, \ + 0x7c, 0x0c, 0x16, 0x35, \ + 0x8b, 0x67, 0xa0, 0x61, \ + 0x02, 0x34, 0xc4, 0x57, \ + 0x08, 0x35, 0xe0, 0x88, \ + 0x0e, 0x45, 0xfc, 0x04, \ + 0x4c, 0x62, 0xfc, 0x14, \ + 0x06, 0x64, 0xfa, 0x0c, \ + 0x89, 0x3d, 0xe0, 0x88, \ + 0x06, 0x64, 0x90, 0x05, \ + 0x46, 0x22, 0xe0, 0x8c, \ + 0xa3, 0x3d, 0xf1, 0x8c, \ + 0x40, 0x24, 0x8e, 0x55, \ + 0xe4, 0x08, 0x08, 0xc0, \ + 0xc5, 0x47, 0x0b, 0x74, \ + 0xc4, 0xc7, 0x39, 0x51, \ + 0x7c, 0x04, 0x04, 0x15, \ + 0x48, 0x4a, 0x9b, 0x21, \ + 0xc2, 0x37, 0x66, 0x9e, \ + 0xe6, 0x8a, 0x44, 0x9a, \ + 0x4a, 0x6a, 0x08, 0x41, \ + 0x04, 0xc3, 0xe6, 0x96, \ + 0xe6, 0x9e, 0x44, 0x1a, \ + 0xcc, 0x17, 0x47, 0x47, \ + 0x82, 0x21, 0x08, 0xc3, \ + 0xc3, 0x27, 0x0a, 0xc3, \ + 0x0d, 0x24, 0x82, 0xc3, \ + 0x42, 0xca, 0x84, 0xc3, \ + 0x42, 0x1a, 0xf0, 0x0a, \ + 0x02, 0x10, 0xe6, 0x8a, \ + 0xcf, 0x17, 0xe5, 0x00, \ + 0x84, 0x65, 0x82, 0x3d, \ + 0x82, 0x3d, 0xfc, 0x14, \ + 0xc0, 0x90, 0x40, 0xa5, \ + 0xc0, 0x08, 0x5e, 0x35, \ + 0xa1, 0x1b, 0xbe, 0x14, \ + 0x00, 0x15, 0x82, 0x55, \ + 0x11, 0x14, 0xe0, 0xf4, \ + 0x42, 0xa2, 0xa2, 0xa3, \ + 0x11, 0x1e, 0xfa, 0x04, \ + 0xbe, 0x1e, 0x83, 0x11, \ + 0x90, 0xab, 0x11, 0x14, \ + 0x83, 0xad, 0x93, 0x24, \ + 0x9f, 0xfc, 0x29, 0x28, \ + 0xd0, 0xf1, 0x01, 0x11, \ + 0xe0, 0x98, 0x40, 0x15, \ + 0xf0, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x60, 0xac, 0x00, 0x15, \ + 0x00, 0x25, 0x81, 0x41, \ + 0x81, 0x51, 0x60, 0xa0, \ + 0x81, 0x85, 0x84, 0x34, \ + 0x06, 0xcd, 0xc5, 0xc4, \ + 0xfb, 0x30, 0x46, 0x32, \ + 0x1c, 0x00, 0x48, 0x50, \ + 0x85, 0x5d, 0x82, 0x51, \ + 0x40, 0x45, 0xfe, 0x15, \ + 0xdd, 0x93, 0xe0, 0xb8, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0x60, 0x18, 0x02, 0x65, \ + 0x8e, 0x35, 0xa3, 0x2b, \ + 0xe0, 0x4c, 0x02, 0x55, \ + 0x94, 0x65, 0x81, 0x5d, \ + 0x08, 0x04, 0x06, 0xc4, \ + 0x0c, 0x25, 0xe0, 0x88, \ + 0x88, 0x63, 0x7c, 0x10, \ + 0x6c, 0x04, 0x0e, 0x00, \ + 0x80, 0x4b, 0x83, 0x81, \ + 0xe0, 0x88, 0x06, 0x24, \ + 0xfc, 0x0c, 0x18, 0x35, \ + 0x86, 0xab, 0x03, 0xf5, \ + 0xf8, 0x04, 0x0c, 0x95, \ + 0x18, 0x95, 0xc4, 0x97, \ + 0x95, 0x9d, 0xfc, 0x24, \ + 0x87, 0x9d, 0x82, 0xa5, \ + 0x02, 0xa5, 0x82, 0x65, \ + 0x16, 0xa4, 0xe0, 0xb4, \ + 0xf7, 0xb7, 0xe1, 0x7c, \ + 0x8e, 0xbb, 0x16, 0xa3, \ + 0x94, 0x9d, 0x8b, 0xa7, \ + 0xa3, 0x8b, 0x80, 0x95, \ + 0x88, 0xbb, 0x82, 0x75, \ + 0x0c, 0x60, 0x02, 0x81, \ + 0x0e, 0x70, 0xec, 0x10, \ + 0x92, 0xab, 0x6c, 0x04, \ + 0x8a, 0x8d, 0x96, 0x4b, \ + 0x98, 0x54, 0xf8, 0x45, \ + 0x18, 0x55, 0xc4, 0x57, \ + 0x81, 0xc5, 0xfc, 0x14, \ + 0x04, 0x04, 0x06, 0x34, \ + 0x0c, 0x25, 0xe0, 0x88, \ + 0x08, 0x24, 0xfc, 0x08, \ + 0x06, 0x55, 0xe0, 0x88, \ + 0x80, 0x4b, 0x7c, 0x10, \ + 0x02, 0xc5, 0x8a, 0x3b, \ + 0xc4, 0x27, 0x60, 0x20, \ + 0xe0, 0xc8, 0x02, 0x15, \ + 0xfc, 0x08, 0x18, 0x25, \ + 0xe6, 0x86, 0x46, 0x3a, \ + 0xe6, 0x86, 0x42, 0x5a, \ + 0x86, 0x53, 0x04, 0x21, \ + 0x82, 0x23, 0x85, 0x27, \ + 0x48, 0x00, 0x85, 0x5d, \ + 0x88, 0x0b, 0xdc, 0x00, \ + 0xf0, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xec, 0x80, 0x1e, 0xf0, \ + 0xe0, 0x84, 0x00, 0x15, \ + 0xa1, 0x1d, 0x0d, 0x1e, \ + 0xc0, 0x00, 0x0d, 0x28, \ + 0x00, 0x35, 0x8d, 0xb4, \ + 0xc2, 0xb7, 0xe0, 0x1c, \ + 0x16, 0x83, 0x86, 0xb3, \ + 0xa1, 0x8d, 0xe0, 0x30, \ + 0x00, 0x15, 0xb2, 0x15, \ + 0x87, 0x21, 0x60, 0xa0, \ + 0xe0, 0x60, 0x16, 0xa3, \ + 0xe6, 0x8a, 0x52, 0x1a, \ + 0xae, 0xe5, 0x93, 0xad, \ + 0xae, 0xb5, 0xa1, 0xad, \ + 0xae, 0x65, 0x93, 0x8d, \ + 0x00, 0x15, 0x89, 0x3e, \ + 0x00, 0x45, 0xe0, 0xb4, \ + 0xa0, 0x11, 0xe0, 0xd0, \ + 0x61, 0x7c, 0x04, 0x14, \ + 0xe1, 0xa0, 0x06, 0x44, \ + 0x04, 0x13, 0xf7, 0x27, \ + 0xe4, 0x0c, 0x04, 0x80, \ + 0x18, 0x24, 0x0b, 0x17, \ + 0x0f, 0x1e, 0xe0, 0x88, \ + 0xfa, 0x80, 0x02, 0x44, \ + 0x60, 0x18, 0x00, 0x55, \ + 0x7e, 0x3e, 0xe1, 0x47, \ + 0xfa, 0x80, 0x06, 0x34, \ + 0x81, 0x41, 0x7c, 0x4e, \ + 0x8a, 0xc3, 0x0b, 0x5e, \ + 0x71, 0x64, 0x81, 0xc5, \ + 0x00, 0x75, 0x82, 0x21, \ + 0xc4, 0x67, 0xe0, 0x20, \ + 0xfc, 0x18, 0x0e, 0x65, \ + 0x85, 0x67, 0x84, 0x61, \ + 0xfa, 0x10, 0x48, 0x62, \ + 0xfc, 0x85, 0x94, 0x2d, \ + 0x90, 0x13, 0x0d, 0x24, \ + 0xf0, 0x8c, 0x50, 0x12, \ + 0xa0, 0x2d, 0x90, 0xab, \ + 0xc0, 0x2c, 0x16, 0x23, \ + 0xcc, 0x10, 0x04, 0x20, \ + 0x5b, 0x20, 0x54, 0x22, \ + 0xa0, 0x35, 0xa3, 0x1d, \ + 0x9e, 0xf5, 0x94, 0x1d, \ + 0x6c, 0x04, 0x04, 0x30, \ + 0xe0, 0x24, 0x50, 0x9a, \ + 0x05, 0x4a, 0x0f, 0x24, \ + 0x40, 0x0a, 0xe4, 0x34, \ + 0x44, 0x9a, 0xed, 0x84, \ + 0x42, 0x0a, 0xe0, 0x10, \ + 0x9c, 0x25, 0xed, 0x80, \ + 0x18, 0x34, 0x0f, 0x24, \ + 0x0b, 0x74, 0xe1, 0x84, \ + 0x0a, 0x14, 0x82, 0x11, \ + 0x82, 0x9b, 0x60, 0x88, \ + 0xa8, 0x23, 0x3e, 0x5e, \ + 0x8a, 0x73, 0x44, 0x27, \ + 0x81, 0xc1, 0x38, 0x23, \ + 0x60, 0x8e, 0x46, 0x2a, \ + 0xe0, 0x88, 0x04, 0x44, \ + 0x24, 0xb3, 0xa6, 0xbb, \ + 0x83, 0x65, 0x81, 0x21, \ + 0x94, 0x9d, 0x0f, 0x64, \ + 0x00, 0x04, 0x00, 0x55, \ + 0x0e, 0x65, 0x82, 0x91, \ + 0x88, 0x71, 0x00, 0x00, \ + 0x80, 0x88, 0x0c, 0x64, \ + 0x9c, 0x18, 0x0a, 0x65, \ + 0x16, 0x63, 0xc4, 0x67, \ + 0x0a, 0x62, 0xa6, 0xb3, \ + 0x0a, 0xc3, 0xe0, 0x00, \ + 0xe8, 0x00, 0x0a, 0x62, \ + 0x90, 0x9d, 0xaa, 0x23, \ + 0x0b, 0x34, 0xfa, 0x45, \ + 0x3e, 0x54, 0x84, 0xc1, \ + 0x09, 0x74, 0x84, 0x21, \ + 0x03, 0x25, 0x81, 0x91, \ + 0x4d, 0x54, 0x8a, 0x33, \ + 0xc0, 0x64, 0x82, 0x11, \ + 0x0c, 0x55, 0xc4, 0x57, \ + 0x00, 0x65, 0xfc, 0x14, \ + 0x4c, 0x6a, 0xe0, 0x24, \ + 0x92, 0x53, 0x66, 0x96, \ + 0xe5, 0x00, 0x0c, 0x60, \ + 0x52, 0x52, 0xcf, 0x67, \ + 0x90, 0x1d, 0xf0, 0x98, \ + 0xfe, 0x14, 0xfa, 0xe5, \ + 0xf4, 0x20, 0x02, 0x8a, \ + 0x05, 0xc7, 0x88, 0xd5, \ + 0x42, 0xc2, 0x85, 0x27, \ + 0xa0, 0x1d, 0xf1, 0x88, \ + 0x00, 0x05, 0x88, 0x05, \ + 0x98, 0x23, 0x60, 0xcc, \ + 0x92, 0x23, 0x7c, 0x34, \ + 0xf0, 0x0a, 0x44, 0x2a, \ + 0x00, 0x04, 0xd1, 0x07, \ + 0x40, 0x0a, 0xfa, 0x80, \ + 0x44, 0x2a, 0xe6, 0x92, \ + 0x84, 0x01, 0xe6, 0x8e, \ + 0x04, 0x20, 0x05, 0x07, \ + 0x02, 0x03, 0xe6, 0x00, \ + 0x84, 0x0d, 0x91, 0x27, \ + 0xc0, 0x18, 0x1e, 0x05, \ + 0xd0, 0x9c, 0x40, 0x02, \ + 0xc1, 0xe0, 0x00, 0x19, \ + 0xc4, 0x04, 0x02, 0x10, \ + 0xc1, 0xe0, 0x40, 0x11, \ + 0x0c, 0x0c, 0xc0, 0xf1, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0x60, 0xa8, 0x00, 0x15, \ + 0x00, 0x75, 0x81, 0x41, \ + 0x00, 0x25, 0xe0, 0xac, \ + 0x00, 0xa5, 0xe0, 0xb0, \ + 0xa3, 0x1d, 0x60, 0xa0, \ + 0x88, 0x4e, 0x9c, 0x4e, \ + 0x83, 0x13, 0x84, 0x35, \ + 0x8d, 0xf3, 0x83, 0x33, \ + 0x9c, 0xc4, 0x82, 0x54, \ + 0xfc, 0x10, 0x0a, 0x55, \ + 0x9c, 0x5e, 0x98, 0x53, \ + 0x88, 0xc4, 0xc0, 0x54, \ + 0x7c, 0x10, 0x0a, 0x55, \ + 0x98, 0x53, 0x88, 0x41, \ + 0x02, 0x54, 0x88, 0x5e, \ + 0xf1, 0x84, 0x48, 0xa2, \ + 0x18, 0x70, 0x23, 0x4d, \ + 0x4c, 0x70, 0xe4, 0x10, \ + 0x08, 0x55, 0xdc, 0x00, \ + 0xdc, 0x00, 0x50, 0x50, \ + 0x60, 0x88, 0x06, 0x44, \ + 0x7c, 0x3e, 0x8e, 0x6b, \ + 0xe0, 0x88, 0x16, 0x14, \ + 0xfe, 0x0c, 0x0c, 0x95, \ + 0x82, 0xa1, 0x7e, 0xae, \ + 0xa2, 0xa3, 0x09, 0x13, \ + 0x8a, 0xab, 0x3e, 0xae, \ + 0x89, 0x73, 0x83, 0x33, \ + 0x7c, 0x2c, 0x14, 0xa5, \ + 0xcd, 0x84, 0x94, 0x3b, \ + 0x14, 0x93, 0xb0, 0x93, \ + 0xf1, 0x9e, 0x86, 0xab, \ + 0xbe, 0xc4, 0xfc, 0x84, \ + 0x02, 0x54, 0xfe, 0xa4, \ + 0xc4, 0xc7, 0x8a, 0x83, \ + 0x0e, 0xa0, 0x8e, 0xc3, \ + 0x48, 0x9a, 0xec, 0x04, \ + 0x00, 0xd8, 0xe0, 0x1c, \ + 0x0e, 0x10, 0xa0, 0x00, \ + 0x8f, 0x13, 0xec, 0x04, \ + 0x87, 0x73, 0x83, 0x33, \ + 0xe1, 0x74, 0xd9, 0x34, \ + 0xae, 0x33, 0xd5, 0x94, \ + 0xf1, 0x3e, 0x92, 0x33, \ + 0x18, 0x20, 0x40, 0x54, \ + 0x88, 0x75, 0xe4, 0x10, \ + 0x60, 0x88, 0x0e, 0x44, \ + 0x0c, 0x85, 0x84, 0x6b, \ + 0x06, 0xa0, 0x7e, 0x1c, \ + 0x09, 0x13, 0xe4, 0x04, \ + 0xf1, 0x84, 0x48, 0x32, \ + 0x06, 0x14, 0x07, 0x33, \ + 0x30, 0xac, 0xe0, 0x88, \ + 0x8a, 0x9b, 0x68, 0x00, \ + 0x7c, 0x0c, 0x12, 0x95, \ + 0xe9, 0xb4, 0x92, 0xab, \ + 0x12, 0x83, 0xb6, 0x83, \ + 0xf1, 0x8e, 0x94, 0x9b, \ + 0x46, 0x52, 0x40, 0x54, \ + 0xc4, 0x47, 0xf0, 0x9c, \ + 0x20, 0x01, 0x00, 0xa8, \ + 0x58, 0x22, 0x40, 0x00, \ + 0x83, 0x05, 0xf0, 0x90, \ + 0x8a, 0x3b, 0x04, 0x6b, \ + 0xa0, 0x00, 0x00, 0xd8, \ + 0xec, 0x04, 0x00, 0x10, \ + 0x83, 0x33, 0x81, 0x13, \ + 0xd9, 0x04, 0x87, 0x73, \ + 0xd5, 0x24, 0xcd, 0x14, \ + 0x84, 0x03, 0xa2, 0x03, \ + 0xa0, 0xf1, 0xf1, 0x0e, \ + 0xf0, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0x60, 0xa0, 0x02, 0x35, \ + 0x02, 0x25, 0x87, 0x41, \ + 0x46, 0x3a, 0xe0, 0x50, \ + 0x48, 0x22, 0xe6, 0x92, \ + 0x06, 0x30, 0xf0, 0x80, \ + 0x87, 0x4d, 0xec, 0x04, \ + 0x02, 0x35, 0x8a, 0xa5, \ + 0x08, 0x04, 0x60, 0x18, \ + 0x83, 0xc1, 0xe0, 0x88, \ + 0xfc, 0x10, 0x06, 0x55, \ + 0xc4, 0x57, 0x86, 0x44, \ + 0xfc, 0x14, 0x08, 0x55, \ + 0x0c, 0x00, 0x83, 0xf5, \ + 0x0e, 0x04, 0x64, 0x04, \ + 0x06, 0x75, 0xe0, 0x88, \ + 0x10, 0x64, 0x7c, 0x1c, \ + 0x06, 0x85, 0xe0, 0x88, \ + 0xc4, 0x77, 0xfc, 0x20, \ + 0x7c, 0x1c, 0x08, 0x95, \ + 0x08, 0x75, 0xc4, 0x87, \ + 0x93, 0x7d, 0xfc, 0x20, \ + 0x8b, 0x7d, 0x82, 0x05, \ + 0x02, 0x85, 0x80, 0xc5, \ + 0xca, 0x77, 0x60, 0xb4, \ + 0x80, 0x95, 0x90, 0x7d, \ + 0x80, 0x6b, 0x23, 0xcb, \ + 0x02, 0xc1, 0x80, 0xb5, \ + 0x84, 0xcd, 0x8c, 0x0b, \ + 0x0c, 0x0c, 0xf8, 0x25, \ + 0xdd, 0x93, 0xf0, 0x1c, \ + 0x60, 0xa0, 0x00, 0x55, \ + 0x00, 0x45, 0x81, 0x31, \ + 0x02, 0x54, 0xe0, 0x18, \ + 0x04, 0x54, 0x60, 0x88, \ + 0x81, 0xc5, 0xe0, 0x8c, \ + 0x4a, 0x42, 0x08, 0xcb, \ + 0x71, 0x3e, 0xf0, 0x84, \ + 0x94, 0x3e, 0x84, 0x43, \ + 0x82, 0x31, 0x10, 0x3e, \ + 0x60, 0xa0, 0x00, 0x55, \ + 0x8b, 0x3d, 0x98, 0x4b, \ + 0xdd, 0x93, 0xfc, 0x95, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xec, 0xa0, 0x1e, 0xf0, \ + 0x81, 0x31, 0x2d, 0x0d, \ + 0x04, 0x55, 0xac, 0x35, \ + 0x04, 0x45, 0xe1, 0x00, \ + 0x04, 0xa5, 0xe1, 0x04, \ + 0x13, 0x5e, 0xe1, 0x08, \ + 0xf1, 0x94, 0x56, 0x12, \ + 0x4a, 0x12, 0x11, 0x4e, \ + 0x04, 0x95, 0xf1, 0x90, \ + 0x40, 0x12, 0x61, 0x0c, \ + 0x04, 0x85, 0xf1, 0xa8, \ + 0x58, 0x52, 0x61, 0x10, \ + 0x04, 0x45, 0xf9, 0xac, \ + 0x4c, 0xc2, 0x61, 0x14, \ + 0x0f, 0x1e, 0xf9, 0x80, \ + 0xf1, 0xa4, 0x4e, 0x12, \ + 0x58, 0x12, 0x0d, 0x7e, \ + 0x2c, 0x7f, 0xf1, 0xa0, \ + 0xf1, 0x90, 0x48, 0x12, \ + 0xb8, 0x7f, 0x0b, 0xce, \ + 0xa1, 0x7d, 0xa8, 0x7f, \ + 0x7c, 0x4e, 0xa2, 0x75, \ + 0xe0, 0x88, 0x02, 0xb4, \ + 0xc4, 0x57, 0x09, 0xae, \ + 0xfe, 0x8e, 0xbe, 0x9e, \ + 0xe0, 0x18, 0x04, 0x95, \ + 0xe1, 0x18, 0x04, 0x85, \ + 0xe0, 0xb4, 0x04, 0xa5, \ + 0xfc, 0x14, 0x12, 0x75, \ + 0xfc, 0x04, 0x12, 0x15, \ + 0xe0, 0x20, 0x04, 0x55, \ + 0xc4, 0x17, 0xc4, 0x77, \ + 0xfc, 0x04, 0x0a, 0x45, \ + 0xfc, 0x1c, 0x0a, 0x15, \ + 0xe1, 0x1c, 0x04, 0x75, \ + 0xe6, 0xa2, 0x50, 0x4a, \ + 0xf1, 0x90, 0x48, 0x12, \ + 0x66, 0x9e, 0x4e, 0x4a, \ + 0x90, 0x7d, 0xb4, 0x83, \ + 0x04, 0x45, 0x98, 0xe5, \ + 0x0c, 0x04, 0x61, 0x20, \ + 0x12, 0x35, 0xe0, 0x88, \ + 0x93, 0x04, 0xfc, 0x18, \ + 0x0a, 0x35, 0xc4, 0x37, \ + 0x42, 0x32, 0xfc, 0x0c, \ + 0x42, 0x1a, 0xf1, 0x84, \ + 0x92, 0x4b, 0x66, 0x92, \ + 0x8c, 0x25, 0x8e, 0x1d, \ + 0xc4, 0xb7, 0x8d, 0xb4, \ + 0xfc, 0x2c, 0x08, 0x05, \ + 0x0a, 0x65, 0xc4, 0x07, \ + 0x04, 0x05, 0xfc, 0x00, \ + 0x46, 0x62, 0xe1, 0x24, \ + 0x46, 0x3a, 0xf1, 0x8c, \ + 0x91, 0x04, 0xe6, 0x82, \ + 0x88, 0x25, 0x82, 0x3d, \ + 0xc4, 0xa7, 0x8b, 0xa4, \ + 0xfc, 0x28, 0x08, 0x05, \ + 0x0a, 0x95, 0xc4, 0x07, \ + 0x04, 0x05, 0xfc, 0x00, \ + 0x42, 0x92, 0xe1, 0x28, \ + 0x42, 0x1a, 0xf1, 0x98, \ + 0x89, 0x04, 0xe6, 0x82, \ + 0x84, 0x25, 0x86, 0x1d, \ + 0x04, 0x35, 0xfc, 0x04, \ + 0xc4, 0x07, 0xe1, 0x2c, \ + 0xfc, 0x00, 0x08, 0x05, \ + 0x0a, 0x05, 0xc4, 0x07, \ + 0xb2, 0x03, 0xfc, 0x00, \ + 0xe6, 0x8e, 0x40, 0x0a, \ + 0x82, 0x0d, 0x7e, 0x04, \ + 0x80, 0x04, 0x1e, 0x05, \ + 0x88, 0x75, 0xa9, 0x0d, \ + 0xe0, 0x74, 0x04, 0x15, \ + 0xe0, 0x94, 0x42, 0x0a, \ + 0x8f, 0xc4, 0x86, 0xc5, \ + 0xaf, 0xcd, 0xa0, 0xc3, \ + 0xc4, 0xc7, 0x86, 0x55, \ + 0x18, 0x15, 0x88, 0xc3, \ + 0x18, 0x25, 0xf8, 0x18, \ + 0xf0, 0x34, 0xf8, 0x08, \ + 0x0a, 0x15, 0xc4, 0x17, \ + 0xc4, 0x27, 0x7c, 0x04, \ + 0x7c, 0x08, 0x0a, 0x25, \ + 0x0a, 0x35, 0xc4, 0x37, \ + 0xa4, 0x13, 0xfc, 0x0c, \ + 0x84, 0x1d, 0xa6, 0x23, \ + 0x8c, 0x10, 0x00, 0x00, \ + 0x0c, 0x0c, 0xd0, 0xf1, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0x06, 0x0b, 0x80, 0x3b, \ + 0x0c, 0x0c, 0xd0, 0xf1, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x90, 0x1e, 0xf0, \ + 0x82, 0x7b, 0x0b, 0x2e, \ + 0x02, 0x40, 0x09, 0x0e, \ + 0x00, 0x08, 0xe4, 0xc0, \ + 0xe7, 0x00, 0x04, 0x40, \ + 0x66, 0x40, 0x0a, 0x40, \ + 0x01, 0xf5, 0x99, 0xc1, \ + 0x42, 0x65, 0x81, 0x61, \ + 0x18, 0xc0, 0x78, 0x30, \ + 0xc5, 0x6e, 0xec, 0x04, \ + 0x42, 0x65, 0xd5, 0x6c, \ + 0x4a, 0x63, 0xe0, 0x2c, \ + 0xc9, 0x66, 0xe0, 0x2c, \ + 0xe0, 0x2c, 0x44, 0x61, \ + 0xfc, 0x55, 0xa1, 0xcb, \ + 0x60, 0x88, 0x02, 0x04, \ + 0x7c, 0x0e, 0xc2, 0x07, \ + 0xe7, 0x00, 0x00, 0x40, \ + 0x12, 0x30, 0x7e, 0x1e, \ + 0x3e, 0x0e, 0xe4, 0xc0, \ + 0xe4, 0xc0, 0x14, 0x40, \ + 0x66, 0x40, 0x0c, 0x30, \ + 0xe6, 0x40, 0x16, 0x40, \ + 0x81, 0x81, 0x0b, 0xd5, \ + 0x78, 0x30, 0x12, 0x05, \ + 0x9c, 0x1a, 0x99, 0x21, \ + 0xe8, 0x0c, 0x47, 0xa8, \ + 0x78, 0x30, 0x54, 0x05, \ + 0xe5, 0x04, 0x99, 0x21, \ + 0x47, 0x38, 0x9c, 0x1a, \ + 0x69, 0x0e, 0xe8, 0x0c, \ + 0x91, 0x9e, 0x99, 0x21, \ + 0xe0, 0x2c, 0x12, 0x05, \ + 0x48, 0x98, 0x9c, 0x1a, \ + 0x59, 0x3a, 0xe8, 0x0c, \ + 0x9e, 0x1a, 0x99, 0x21, \ + 0x54, 0x05, 0x8f, 0xae, \ + 0x86, 0x0b, 0x60, 0x2c, \ + 0xe8, 0x0c, 0x48, 0x08, \ + 0x99, 0x21, 0x0d, 0xbe, \ + 0x0c, 0x03, 0xed, 0x0c, \ + 0x9e, 0x1a, 0xe0, 0x5c, \ + 0xe8, 0x0c, 0x47, 0x88, \ + 0x60, 0x2c, 0x0c, 0x03, \ + 0x1e, 0x1a, 0x80, 0xab, \ + 0x47, 0x18, 0x99, 0x21, \ + 0x0c, 0x03, 0xe8, 0x0c, \ + 0x80, 0x9b, 0x60, 0x8c, \ + 0x99, 0x21, 0x1e, 0x1a, \ + 0xe8, 0x0c, 0x46, 0xa8, \ + 0x89, 0x34, 0xfc, 0x24, \ + 0x42, 0x22, 0xfe, 0x44, \ + 0xb4, 0x23, 0x71, 0xa4, \ + 0x92, 0x23, 0x00, 0x13, \ + 0x40, 0x2a, 0x8e, 0x38, \ + 0x44, 0x1a, 0xe0, 0x92, \ + 0x0d, 0x44, 0xe0, 0x92, \ + 0x02, 0x02, 0xa1, 0x3b, \ + 0x92, 0x0b, 0x60, 0x00, \ + 0x60, 0x00, 0x06, 0x22, \ + 0xeb, 0xfc, 0x04, 0x12, \ + 0x02, 0x32, 0x3e, 0x34, \ + 0x00, 0x06, 0xeb, 0xfc, \ + 0xa1, 0x9d, 0x40, 0x00, \ + 0x9c, 0x00, 0x40, 0x90, \ + 0x60, 0x30, 0x48, 0x03, \ + 0x0c, 0x26, 0x90, 0x33, \ + 0x46, 0x11, 0x82, 0x81, \ + 0x91, 0x94, 0xe0, 0x30, \ + 0x8b, 0x04, 0x8f, 0xa4, \ + 0x81, 0x8d, 0x82, 0x08, \ + 0x01, 0x01, 0xe8, 0x55, \ + 0x0c, 0x0c, 0xc8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe0, 0x84, 0x00, 0x44, \ + 0x7c, 0x00, 0x0a, 0x03, \ + 0xf0, 0x80, 0x58, 0x52, \ + 0xe0, 0x90, 0x18, 0xc3, \ + 0xfd, 0x01, 0x20, 0xc3, \ + 0x80, 0xcd, 0x3c, 0x04, \ + 0x97, 0x80, 0x18, 0xc4, \ + 0x44, 0x04, 0x18, 0xc0, \ + 0x86, 0x15, 0xa3, 0x1d, \ + 0xe6, 0x8a, 0x44, 0xca, \ + 0xe5, 0x0e, 0x58, 0x2a, \ + 0x65, 0x8e, 0x44, 0x2a, \ + 0x18, 0xc0, 0x81, 0x31, \ + 0xc1, 0x00, 0x64, 0x01, \ + 0x72, 0x8c, 0x44, 0x22, \ + 0xde, 0x27, 0xe3, 0xc7, \ + 0x02, 0xcd, 0xa4, 0xcf, \ + 0x80, 0xf5, 0x93, 0x11, \ + 0x0a, 0x29, 0x88, 0x53, \ + 0xe2, 0x2d, 0xe1, 0xe0, \ + 0x8b, 0x11, 0x80, 0x55, \ + 0x40, 0x11, 0x88, 0x03, \ + 0xdd, 0x93, 0xe3, 0x90, \ + 0x18, 0x24, 0x42, 0x5a, \ + 0x98, 0x33, 0xe0, 0x88, \ + 0xe0, 0x30, 0x06, 0x35, \ + 0xc3, 0x5d, 0xc6, 0x57, \ + 0x80, 0x00, 0x0a, 0x06, \ + 0xd1, 0x94, 0x4a, 0x12, \ + 0x82, 0x15, 0xcb, 0x3d, \ + 0xe0, 0x10, 0x00, 0x0b, \ + 0x82, 0x03, 0xc6, 0x07, \ + 0x5c, 0x08, 0xc0, 0x3d, \ + 0x04, 0x43, 0x9a, 0x00, \ + 0x48, 0x01, 0x85, 0x01, \ + 0xdd, 0x93, 0xe3, 0x90, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xd0, 0x1e, 0xf0, \ + 0x81, 0xc1, 0x0b, 0x1e, \ + 0x91, 0x4e, 0xad, 0x14, \ + 0x89, 0x1e, 0xb3, 0x44, \ + 0x13, 0x5e, 0xab, 0x14, \ + 0x8f, 0x3e, 0x80, 0x5b, \ + 0xa9, 0x14, 0x97, 0x1e, \ + 0x99, 0x21, 0x0d, 0x2e, \ + 0x7e, 0x1e, 0xb1, 0x34, \ + 0xe7, 0x90, 0x02, 0x40, \ + 0x81, 0x45, 0xaf, 0x04, \ + 0x04, 0x20, 0x45, 0xc6, \ + 0xa1, 0x2b, 0xec, 0x04, \ + 0x04, 0x00, 0xfe, 0x95, \ + 0x4f, 0x06, 0x65, 0x80, \ + 0x19, 0x06, 0xe0, 0x00, \ + 0x81, 0xa1, 0x60, 0x08, \ + 0x06, 0x05, 0x8d, 0x05, \ + 0x00, 0x0a, 0xfc, 0x1c, \ + 0xc7, 0xff, 0x63, 0xfd, \ + 0x4c, 0x42, 0x94, 0xc5, \ + 0x8d, 0x01, 0x70, 0xa8, \ + 0xe3, 0x90, 0x4c, 0x01, \ + 0xa1, 0x0b, 0x94, 0x0a, \ + 0x88, 0x14, 0x80, 0xe5, \ + 0x00, 0x86, 0xc1, 0x1d, \ + 0x4c, 0x01, 0xc0, 0x00, \ + 0x96, 0x0a, 0xc3, 0x90, \ + 0x82, 0x65, 0xa1, 0x0b, \ + 0x70, 0x9c, 0x42, 0x42, \ + 0x02, 0x15, 0xce, 0x07, \ + 0xc0, 0x1d, 0xe0, 0x30, \ + 0xc0, 0x00, 0x00, 0x36, \ + 0xc3, 0x90, 0x4c, 0x01, \ + 0xd4, 0x0a, 0x99, 0x2e, \ + 0x82, 0x05, 0xa1, 0x0b, \ + 0xfc, 0x30, 0x08, 0x1b, \ + 0x00, 0x36, 0xc0, 0x1d, \ + 0x4c, 0x01, 0xc0, 0x00, \ + 0x15, 0x6e, 0xc3, 0x90, \ + 0x11, 0x14, 0x8a, 0x0b, \ + 0x08, 0xbb, 0x94, 0x2b, \ + 0x06, 0x9b, 0x8a, 0x6b, \ + 0x78, 0xd8, 0x98, 0x8b, \ + 0x13, 0x04, 0xef, 0xfc, \ + 0x19, 0x24, 0x90, 0xcb, \ + 0x0c, 0x5b, 0x92, 0x3b, \ + 0x00, 0x09, 0x96, 0x4b, \ + 0x00, 0x0a, 0xe0, 0x34, \ + 0x86, 0x65, 0xe3, 0xfc, \ + 0x00, 0x09, 0x95, 0x04, \ + 0x00, 0x00, 0xe3, 0x90, \ + 0x01, 0x0a, 0xec, 0x14, \ + 0x84, 0x75, 0xf4, 0x04, \ + 0x7c, 0x4e, 0x89, 0x04, \ + 0x8b, 0x14, 0x94, 0x4b, \ + 0xfe, 0x04, 0xbe, 0x0e, \ + 0x8f, 0x34, 0x8d, 0x24, \ + 0x8a, 0x0b, 0x3c, 0x0e, \ + 0x41, 0x58, 0x93, 0x54, \ + 0x19, 0x24, 0xe8, 0x00, \ + 0x12, 0x3b, 0x90, 0xcb, \ + 0x96, 0x4b, 0x8c, 0x5b, \ + 0x88, 0x21, 0x02, 0xa1, \ + 0x84, 0xc1, 0x08, 0x71, \ + 0x82, 0x08, 0x97, 0x04, \ + 0xe6, 0xd5, 0x81, 0xad, \ + 0xe8, 0xf1, 0x01, 0x01, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x8a, 0x9b, 0x00, 0xab, \ + 0x60, 0x14, 0x14, 0x03, \ + 0xa1, 0xb4, 0x82, 0x8b, \ + 0x0d, 0x0e, 0x9f, 0x54, \ + 0x8e, 0xf5, 0xa3, 0x0d, \ + 0x60, 0x30, 0x12, 0x09, \ + 0xe0, 0x84, 0x0c, 0x44, \ + 0x42, 0xb2, 0x15, 0xc1, \ + 0x16, 0x63, 0xf0, 0x90, \ + 0x1d, 0x04, 0x81, 0x71, \ + 0x18, 0x46, 0xa1, 0x0b, \ + 0x42, 0xc1, 0xc0, 0x00, \ + 0x18, 0x60, 0x63, 0x90, \ + 0x0b, 0x8e, 0xe6, 0xa0, \ + 0xe5, 0xe0, 0x0c, 0x00, \ + 0xfe, 0xae, 0x89, 0x5e, \ + 0xbe, 0x3e, 0xfc, 0xbe, \ + 0xb0, 0x02, 0x83, 0xe5, \ + 0x0d, 0x14, 0x98, 0x12, \ + 0x00, 0x04, 0xa2, 0x03, \ + 0x82, 0x0d, 0xf0, 0x80, \ + 0x14, 0x13, 0x84, 0x95, \ + 0x90, 0x0b, 0x60, 0x20, \ + 0x92, 0xbb, 0x3c, 0xbe, \ + 0x84, 0x9b, 0x08, 0x8b, \ + 0x6f, 0xfc, 0x6f, 0x48, \ + 0x14, 0xcb, 0x98, 0xab, \ + 0x16, 0x9b, 0x92, 0x2b, \ + 0xfc, 0xb4, 0x90, 0x4b, \ + 0xbe, 0x34, 0x89, 0x54, \ + 0x8b, 0x84, 0xfe, 0xa4, \ + 0x84, 0x61, 0x02, 0x71, \ + 0xe0, 0x30, 0x12, 0x09, \ + 0xf8, 0x15, 0x81, 0x7d, \ + 0x0c, 0x0c, 0xb8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0x60, 0x20, 0x14, 0x13, \ + 0xbc, 0xbe, 0x90, 0x0b, \ + 0xef, 0xfc, 0x6d, 0xa8, \ + 0x0c, 0x0c, 0xb8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0xec, 0x60, 0x1e, 0xf0, \ + 0x82, 0x9b, 0x00, 0xbb, \ + 0x60, 0x94, 0x00, 0x34, \ + 0xe1, 0x84, 0x02, 0xb4, \ + 0x82, 0x03, 0x25, 0xc4, \ + 0x60, 0x2e, 0x40, 0x0a, \ + 0x29, 0x84, 0xa1, 0x71, \ + 0x42, 0x02, 0x81, 0x61, \ + 0x40, 0xc2, 0x7b, 0xb0, \ + 0xa2, 0x73, 0xfa, 0x80, \ + 0xf3, 0x98, 0x42, 0x62, \ + 0x40, 0x12, 0x80, 0x73, \ + 0xcb, 0x77, 0x72, 0x98, \ + 0xe0, 0xec, 0x02, 0x04, \ + 0x79, 0x84, 0x54, 0x72, \ + 0xe1, 0x94, 0x0e, 0x04, \ + 0x7a, 0x90, 0x40, 0x22, \ + 0xfb, 0x88, 0x42, 0x42, \ + 0x79, 0x9c, 0x44, 0xa2, \ + 0x21, 0x2b, 0xa2, 0x03, \ + 0xef, 0xfc, 0x45, 0xf6, \ + 0x42, 0x62, 0x0b, 0xae, \ + 0x20, 0x2c, 0xf3, 0x98, \ + 0x10, 0x28, 0xff, 0xa1, \ + 0x4d, 0xfe, 0x00, 0x00, \ + 0x9f, 0x24, 0x9f, 0xfc, \ + 0xe6, 0xae, 0x48, 0xba, \ + 0x06, 0x23, 0x89, 0x7e, \ + 0xe1, 0x84, 0x06, 0x44, \ + 0xca, 0x27, 0x3e, 0x5e, \ + 0x82, 0x3b, 0x06, 0x23, \ + 0xe0, 0x12, 0x44, 0x2a, \ + 0x44, 0x62, 0x84, 0xc3, \ + 0xa0, 0xc1, 0xf2, 0x98, \ + 0x72, 0x98, 0x4e, 0x22, \ + 0x16, 0x74, 0xcb, 0xc7, \ + 0xcb, 0x77, 0x60, 0xec, \ + 0x45, 0xfe, 0x38, 0xbf, \ + 0xc0, 0x3f, 0x7f, 0xfd, \ + 0x64, 0x08, 0x56, 0xca, \ + 0x0e, 0x1a, 0x80, 0x2b, \ + 0x42, 0x0a, 0xe0, 0x00, \ + 0x57, 0xfe, 0xe1, 0x80, \ + 0x40, 0x3f, 0x1f, 0xfd, \ + 0xc0, 0x00, 0x0e, 0x06, \ + 0xef, 0xd4, 0x41, 0x68, \ + 0x61, 0xe0, 0x04, 0x04, \ + 0x24, 0x1f, 0xd0, 0x17, \ + 0x7f, 0xfd, 0x45, 0xfe, \ + 0x7c, 0x9e, 0xc0, 0x3f, \ + 0x7e, 0x8e, 0xd0, 0x07, \ + 0xe4, 0x08, 0x42, 0xca, \ + 0x42, 0x1a, 0x0b, 0x94, \ + 0x09, 0x84, 0xe0, 0x08, \ + 0xe0, 0x28, 0x40, 0x6a, \ + 0xe5, 0x80, 0x42, 0x0a, \ + 0x61, 0x88, 0x42, 0x1a, \ + 0x42, 0x0a, 0x92, 0x2b, \ + 0x90, 0x3b, 0x6d, 0x80, \ + 0x5f, 0xfc, 0x41, 0xfe, \ + 0x1f, 0xfc, 0x43, 0xfe, \ + 0x75, 0xf8, 0xc0, 0x3f, \ + 0x12, 0x2b, 0xef, 0xd0, \ + 0x75, 0xb8, 0x90, 0x3b, \ + 0x02, 0x3b, 0xef, 0xd0, \ + 0x23, 0xa4, 0x80, 0x2b, \ + 0xe0, 0x28, 0x44, 0x6a, \ + 0x06, 0xca, 0x21, 0x84, \ + 0x1d, 0x94, 0xe5, 0xfc, \ + 0xe1, 0xfc, 0x06, 0x1a, \ + 0x65, 0x80, 0x42, 0x0a, \ + 0x42, 0x1a, 0x96, 0x0b, \ + 0x8e, 0x1b, 0x61, 0x88, \ + 0xed, 0x80, 0x42, 0x0a, \ + 0x5f, 0xfc, 0x45, 0xfe, \ + 0xc0, 0x04, 0x47, 0xf6, \ + 0xef, 0xd0, 0x7d, 0x88, \ + 0x47, 0xfe, 0x3e, 0x24, \ + 0xc0, 0x3f, 0x7f, 0xfd, \ + 0x89, 0xb4, 0xa7, 0x74, \ + 0x44, 0x62, 0x84, 0x93, \ + 0x7e, 0x84, 0xf2, 0x98, \ + 0x44, 0x22, 0x90, 0x93, \ + 0x7c, 0x94, 0xf2, 0x98, \ + 0x44, 0x22, 0x92, 0xa3, \ + 0x54, 0xca, 0x72, 0x98, \ + 0x04, 0x1a, 0xe4, 0x0c, \ + 0x42, 0x0a, 0xe0, 0x00, \ + 0x04, 0x06, 0xe1, 0x80, \ + 0x55, 0xfe, 0x40, 0x00, \ + 0xc0, 0x3f, 0x1f, 0xfc, \ + 0x61, 0xe0, 0x06, 0xa4, \ + 0x50, 0xa7, 0xd0, 0x27, \ + 0x14, 0x03, 0xa6, 0x2f, \ + 0x42, 0x12, 0xff, 0xa1, \ + 0x45, 0xfe, 0x72, 0x88, \ + 0xdf, 0xff, 0x7f, 0xfc, \ + 0x64, 0x08, 0x42, 0xca, \ + 0x42, 0x1a, 0x84, 0x71, \ + 0xc7, 0x77, 0x60, 0x08, \ + 0xe0, 0x28, 0x40, 0x6a, \ + 0xe5, 0x80, 0x42, 0x0a, \ + 0xe1, 0x88, 0x42, 0x1a, \ + 0xed, 0x80, 0x42, 0x0a, \ + 0x1f, 0xfd, 0x43, 0xfe, \ + 0x41, 0xfe, 0x5f, 0xff, \ + 0xa1, 0x1b, 0xdf, 0xfc, \ + 0x0b, 0x24, 0x82, 0x45, \ + 0x70, 0x18, 0x96, 0x3b, \ + 0x01, 0x31, 0xef, 0xd0, \ + 0x79, 0x38, 0x8e, 0x2b, \ + 0x81, 0x95, 0xef, 0xd0, \ + 0x8e, 0x2b, 0x01, 0x31, \ + 0xef, 0xd0, 0x78, 0xe8, \ + 0x96, 0x3b, 0x0b, 0x24, \ + 0xef, 0xd0, 0x6f, 0x48, \ + 0x43, 0xfe, 0xa1, 0x1d, \ + 0x5f, 0xff, 0x1f, 0xfd, \ + 0xdf, 0xfc, 0x41, 0xfe, \ + 0x82, 0xe5, 0xa1, 0x1b, \ + 0x96, 0x3b, 0x0b, 0x24, \ + 0xef, 0xd0, 0x6e, 0x88, \ + 0x60, 0xf8, 0x04, 0x14, \ + 0x24, 0x0f, 0xc5, 0x07, \ + 0x01, 0x31, 0xc5, 0x17, \ + 0x77, 0x58, 0x8e, 0x2b, \ + 0x81, 0xe5, 0xef, 0xd0, \ + 0x8e, 0x2b, 0x01, 0x31, \ + 0xef, 0xd0, 0x77, 0x08, \ + 0x96, 0x3b, 0x0b, 0x24, \ + 0xef, 0xd0, 0x6d, 0x68, \ + 0x60, 0xf8, 0x04, 0x14, \ + 0x24, 0x0f, 0xc5, 0x07, \ + 0x21, 0x1d, 0xc5, 0x17, \ + 0x7f, 0xfd, 0x45, 0xfe, \ + 0x04, 0x14, 0xc7, 0xff, \ + 0xff, 0x31, 0x01, 0x88, \ + 0x01, 0x88, 0x00, 0x04, \ + 0x80, 0xf8, 0x02, 0x14, \ + 0x19, 0x84, 0x46, 0x02, \ + 0x46, 0x5a, 0xa1, 0x2b, \ + 0x40, 0x9a, 0x60, 0x28, \ + 0x40, 0x0a, 0xe5, 0x26, \ + 0x47, 0xfe, 0xe1, 0x84, \ + 0x04, 0x06, 0x5f, 0xfc, \ + 0x00, 0x33, 0xc0, 0x00, \ + 0xe5, 0xa6, 0x40, 0x9a, \ + 0x40, 0x22, 0x95, 0x3b, \ + 0x47, 0xfe, 0x72, 0x80, \ + 0xa1, 0x0b, 0x9f, 0xfc, \ + 0xdf, 0xfc, 0x47, 0xfe, \ + 0xf0, 0x0a, 0x40, 0x3a, \ + 0x0c, 0x0b, 0xa0, 0x0c, \ + 0x0c, 0x0c, 0xb0, 0xf1, \ + 0xdd, 0x93, 0xf6, 0x7c, \ + 0x81, 0x41, 0x01, 0xb5, \ + 0x82, 0x41, 0x49, 0x54, \ + 0x98, 0x5d, 0xc5, 0xc4, \ + 0x80, 0x00, 0x0a, 0x06, \ + 0xd1, 0xb0, 0x4a, 0x52, \ + 0x81, 0x4d, 0xcd, 0x5e, \ + 0x5d, 0x93, 0xfc, 0xd5, \ + 0xc1, 0xc3, 0x81, 0x01, \ + 0xf8, 0x7c, 0x04, 0xcc, \ + 0x67, 0x90, 0x0a, 0x30, \ + 0xe6, 0xa0, 0x18, 0x30, \ + 0x81, 0x61, 0x0f, 0x4e, \ + 0x48, 0x06, 0x08, 0x8b, \ + 0x03, 0xb5, 0xe0, 0x24, \ + 0x20, 0x66, 0x80, 0x9b, \ + 0x18, 0xa3, 0xa5, 0x1b, \ + 0x16, 0x06, 0x40, 0x00, \ + 0xff, 0x00, 0x00, 0x00, \ + 0x16, 0x06, 0x14, 0x78, \ + 0x81, 0x00, 0x00, 0x01, \ + 0x00, 0x00, 0x06, 0xa5, \ + 0x80, 0xb5, 0xf2, 0x7d, \ + 0xe4, 0x9c, 0x48, 0x4a, \ + 0x90, 0xac, 0x54, 0xa2, \ + 0x82, 0x61, 0x65, 0xae, \ + 0x82, 0x51, 0x08, 0x31, \ + 0x82, 0x81, 0x04, 0xc1, \ + 0x95, 0x6d, 0x8a, 0xa8, \ + 0xe5, 0xad, 0xf8, 0xb5, \ + 0x8f, 0x44, 0x88, 0xd5, \ + 0x03, 0x31, 0x82, 0x41, \ + 0x03, 0xb5, 0x88, 0x5b, \ + 0x0a, 0x69, 0x81, 0xc1, \ + 0xa5, 0x1b, 0x78, 0x04, \ + 0x10, 0x64, 0x94, 0x78, \ + 0x00, 0x85, 0xe0, 0x88, \ + 0x12, 0x74, 0x7c, 0x20, \ + 0x00, 0x95, 0xe0, 0x88, \ + 0x80, 0x95, 0xfc, 0x24, \ + 0x80, 0x95, 0x93, 0x8d, \ + 0x92, 0x8d, 0x81, 0x75, \ + 0x4a, 0x71, 0x80, 0xa5, \ + 0x83, 0xc1, 0x78, 0x04, \ + 0x0a, 0xa8, 0x94, 0x66, \ + 0x82, 0x51, 0x82, 0x31, \ + 0xfa, 0x80, 0x0c, 0xa4, \ + 0xf8, 0x95, 0x8d, 0x3d, \ + 0xf6, 0xc5, 0xa3, 0xcd, \ + 0x0c, 0xcc, 0x81, 0x01, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xec, 0x80, 0x1e, 0xf0, \ + 0x84, 0xab, 0x0f, 0x1e, \ + 0x81, 0xc1, 0x1d, 0x04, \ + 0x81, 0x11, 0x01, 0x95, \ + 0xfa, 0x80, 0x02, 0x14, \ + 0x84, 0xc6, 0x86, 0x13, \ + 0xe0, 0x10, 0x00, 0x19, \ + 0x40, 0x11, 0x82, 0x11, \ + 0x03, 0x0a, 0x60, 0x10, \ + 0xfc, 0xa5, 0xf4, 0x1c, \ + 0xa5, 0x1b, 0xaa, 0x18, \ + 0xa3, 0x1b, 0x82, 0xa5, \ + 0x80, 0x00, 0x00, 0x16, \ + 0x80, 0x00, 0x4a, 0x01, \ + 0x80, 0x00, 0x28, 0x08, \ + 0x4a, 0xc1, 0xa1, 0x1b, \ + 0x27, 0xb8, 0x80, 0x00, \ + 0x40, 0xc1, 0xe0, 0x00, \ + 0x81, 0x11, 0x60, 0x14, \ + 0xe0, 0x18, 0x40, 0xc1, \ + 0xe0, 0x30, 0x40, 0xc1, \ + 0x40, 0x11, 0x94, 0xc6, \ + 0x02, 0x8a, 0x60, 0x10, \ + 0x04, 0x14, 0xf5, 0x00, \ + 0x02, 0x10, 0x5a, 0x80, \ + 0x44, 0x22, 0xc4, 0x04, \ + 0x44, 0xc1, 0xd0, 0x80, \ + 0xfc, 0x95, 0xc0, 0x40, \ + 0x12, 0x06, 0x19, 0x71, \ + 0xfc, 0x00, 0x60, 0x01, \ + 0x81, 0x21, 0x01, 0x11, \ + 0xe0, 0x00, 0x22, 0xa8, \ + 0xf4, 0xfc, 0x03, 0x0a, \ + 0xc0, 0x00, 0x22, 0xa8, \ + 0x02, 0x14, 0x68, 0x28, \ + 0xc5, 0x1d, 0xfa, 0x80, \ + 0x80, 0x00, 0x22, 0x48, \ + 0xe6, 0x9e, 0x42, 0x1a, \ + 0xf0, 0x84, 0x44, 0x42, \ + 0xe0, 0x20, 0x04, 0x89, \ + 0x1c, 0x28, 0xa1, 0x8b, \ + 0x0f, 0x64, 0x80, 0x00, \ + 0x04, 0x19, 0xb1, 0x8b, \ + 0xfe, 0x2e, 0xe0, 0x2c, \ + 0xe0, 0x28, 0x04, 0x29, \ + 0x60, 0x3c, 0x0c, 0x69, \ + 0x7c, 0x1e, 0xc3, 0x17, \ + 0x40, 0x11, 0x82, 0x19, \ + 0x44, 0x20, 0x60, 0x30, \ + 0x4e, 0x6a, 0xf0, 0xfc, \ + 0x40, 0xc1, 0xe6, 0x8a, \ + 0x89, 0x2e, 0xe0, 0x2c, \ + 0xe0, 0x20, 0x40, 0x21, \ + 0xe0, 0x1c, 0x40, 0x71, \ + 0x40, 0x81, 0x80, 0x9e, \ + 0x0c, 0x18, 0xe0, 0x10, \ + 0xbf, 0x8b, 0x80, 0x00, \ + 0xb5, 0x8b, 0x84, 0x65, \ + 0xb7, 0x8b, 0x8a, 0x05, \ + 0xb9, 0x8b, 0x8a, 0xa5, \ + 0xbb, 0x8b, 0x8c, 0x25, \ + 0xbd, 0x8b, 0x90, 0x85, \ + 0xb3, 0x8b, 0x92, 0x25, \ + 0x8f, 0x14, 0x94, 0x15, \ + 0x84, 0x13, 0x89, 0x24, \ + 0xec, 0x70, 0x02, 0x69, \ + 0x0f, 0x14, 0x97, 0x25, \ + 0x60, 0x01, 0x0c, 0x06, \ + 0x89, 0x24, 0xfc, 0x00, \ + 0x02, 0x19, 0x84, 0x13, \ + 0xa1, 0x1b, 0xec, 0x70, \ + 0x80, 0x00, 0x0a, 0xe8, \ + 0x02, 0x74, 0x0f, 0x24, \ + 0xc4, 0x17, 0xfa, 0x80, \ + 0x02, 0x65, 0x84, 0x13, \ + 0x04, 0x15, 0xe7, 0xdc, \ + 0xc0, 0x01, 0x67, 0xfd, \ + 0xa2, 0x63, 0x05, 0x05, \ + 0x8f, 0x24, 0x89, 0x14, \ + 0x84, 0x13, 0xc2, 0x17, \ + 0xe4, 0x38, 0x02, 0x6b, \ + 0x8f, 0x14, 0x93, 0x45, \ + 0x84, 0x13, 0x89, 0x24, \ + 0xeb, 0x70, 0x02, 0x61, \ + 0x0f, 0x14, 0x91, 0xe5, \ + 0x60, 0x01, 0x0c, 0x06, \ + 0x89, 0x24, 0xfc, 0x00, \ + 0x02, 0x19, 0x84, 0x13, \ + 0xa1, 0x1b, 0xec, 0x70, \ + 0x0f, 0x24, 0xa2, 0x45, \ + 0xfa, 0x80, 0x02, 0x74, \ + 0x02, 0x69, 0x84, 0x13, \ + 0xc0, 0x05, 0x6b, 0x71, \ + 0x12, 0x06, 0x0f, 0xc5, \ + 0xfc, 0x00, 0x60, 0x01, \ + 0x8f, 0x24, 0x89, 0x14, \ + 0x84, 0x13, 0xc4, 0x17, \ + 0xe1, 0x1c, 0x02, 0x65, \ + 0x8f, 0x14, 0x8f, 0x25, \ + 0x84, 0x13, 0x89, 0x24, \ + 0xeb, 0x70, 0x02, 0x61, \ + 0xea, 0x70, 0x02, 0x11, \ + 0xa2, 0x63, 0x0d, 0xa5, \ + 0x89, 0x91, 0x7e, 0x14, \ + 0x81, 0x61, 0x3e, 0xae, \ + 0x3c, 0x1e, 0x96, 0x11, \ + 0x40, 0x11, 0x89, 0x11, \ + 0x8f, 0x14, 0xe0, 0x28, \ + 0x84, 0x13, 0x89, 0x24, \ + 0x64, 0x71, 0x02, 0x10, \ + 0x0d, 0x1e, 0xc0, 0x03, \ + 0xfa, 0x80, 0x02, 0x74, \ + 0x85, 0xf5, 0x8b, 0x1e, \ + 0xfa, 0x80, 0x14, 0x14, \ + 0x14, 0xb9, 0x88, 0xa3, \ + 0x91, 0xa1, 0x6c, 0x00, \ + 0x86, 0xc5, 0xa1, 0xbb, \ + 0x0e, 0x64, 0x0b, 0x24, \ + 0x0f, 0x24, 0xfa, 0x80, \ + 0x84, 0x73, 0x84, 0x73, \ + 0x6b, 0x71, 0x0e, 0x79, \ + 0x97, 0x7b, 0xc0, 0x05, \ + 0x12, 0x0a, 0x82, 0xb5, \ + 0x82, 0x55, 0xf0, 0x10, \ + 0x60, 0x28, 0x40, 0x61, \ + 0x40, 0x11, 0x89, 0x11, \ + 0x89, 0x11, 0x60, 0x2c, \ + 0xe0, 0x24, 0x40, 0xa1, \ + 0x02, 0xab, 0x81, 0x45, \ + 0x92, 0x6b, 0x8c, 0x1b, \ + 0x82, 0xa1, 0x0c, 0x9b, \ + 0x94, 0x1b, 0x82, 0x6b, \ + 0x60, 0x24, 0x40, 0x11, \ + 0xf4, 0x1c, 0x03, 0x0a, \ + 0x82, 0x61, 0xf4, 0xc5, \ + 0xe0, 0x2c, 0x40, 0x61, \ + 0x14, 0x64, 0x0d, 0x14, \ + 0x84, 0x18, 0xfa, 0x80, \ + 0x7c, 0x55, 0xc3, 0xad, \ + 0xc0, 0x00, 0x14, 0x06, \ + 0x84, 0x18, 0xbc, 0x14, \ + 0xe0, 0x80, 0x42, 0x0a, \ + 0xc0, 0x00, 0x09, 0x98, \ + 0x42, 0x4a, 0x3e, 0xa4, \ + 0x0a, 0xd8, 0xe0, 0x80, \ + 0x12, 0x0a, 0x80, 0x00, \ + 0x0a, 0x98, 0xf0, 0x10, \ + 0x89, 0x24, 0xc0, 0x00, \ + 0x80, 0x23, 0xfc, 0x14, \ + 0x60, 0x80, 0x42, 0x4a, \ + 0x04, 0x19, 0x81, 0x11, \ + 0x93, 0x45, 0xc0, 0x40, \ + 0x8f, 0x24, 0x89, 0x14, \ + 0x84, 0x13, 0xc4, 0x17, \ + 0xe0, 0x1c, 0x02, 0x65, \ + 0x02, 0x64, 0x7e, 0x24, \ + 0x00, 0x6e, 0xfc, 0x00, \ + 0x84, 0x55, 0x93, 0x1b, \ + 0x60, 0x01, 0x12, 0x06, \ + 0x99, 0x71, 0x7c, 0x00, \ + 0x0d, 0x28, 0x93, 0x6b, \ + 0xfc, 0x14, 0xc0, 0x00, \ + 0xe0, 0x80, 0x42, 0x0a, \ + 0xc0, 0x20, 0x00, 0x19, \ + 0xd0, 0x80, 0x42, 0x12, \ + 0xc0, 0x40, 0x42, 0xc1, \ + 0x04, 0x19, 0x99, 0x75, \ + 0xab, 0x1b, 0xe0, 0x24, \ + 0xa3, 0x1b, 0x86, 0xa5, \ + 0xa5, 0x1b, 0x8a, 0x45, \ + 0x27, 0x1b, 0x8c, 0xe5, \ + 0x8e, 0xc5, 0x99, 0x71, \ + 0x90, 0xe5, 0xa9, 0x1b, \ + 0x0b, 0x88, 0xa1, 0x1b, \ + 0x88, 0x14, 0xc0, 0x00, \ + 0x80, 0xb5, 0x83, 0x6d, \ + 0x82, 0x6d, 0x8a, 0x14, \ + 0x80, 0x00, 0x09, 0xa8, \ + 0x89, 0x24, 0xfc, 0x14, \ + 0x60, 0x80, 0x42, 0x4a, \ + 0x0a, 0x68, 0x81, 0x11, \ + 0x80, 0x23, 0x40, 0x00, \ + 0x88, 0x14, 0x95, 0x55, \ + 0x7c, 0x64, 0x89, 0x24, \ + 0x80, 0x23, 0x83, 0x6b, \ + 0x60, 0x40, 0x04, 0x29, \ + 0xe4, 0x40, 0x02, 0x20, \ + 0x4c, 0x4a, 0x9e, 0xb5, \ + 0x99, 0x71, 0x60, 0x80, \ + 0x87, 0x35, 0x8c, 0x65, \ + 0x83, 0x6d, 0x88, 0x14, \ + 0x8a, 0x14, 0x80, 0x95, \ + 0xa0, 0xc5, 0x82, 0x6d, \ + 0x99, 0x71, 0x7c, 0x14, \ + 0x42, 0x0a, 0x89, 0x24, \ + 0x83, 0x11, 0x60, 0x80, \ + 0x80, 0x23, 0x1e, 0x75, \ + 0x08, 0x14, 0x91, 0x75, \ + 0x89, 0x24, 0x99, 0x71, \ + 0x80, 0x23, 0x82, 0x6d, \ + 0x60, 0x40, 0x04, 0x29, \ + 0xe4, 0x40, 0x02, 0x20, \ + 0x8a, 0x14, 0x83, 0x45, \ + 0x7c, 0x64, 0x89, 0x24, \ + 0x80, 0x23, 0x82, 0x6d, \ + 0x60, 0x40, 0x04, 0x29, \ + 0xe4, 0x40, 0x02, 0x20, \ + 0x83, 0x15, 0x82, 0x85, \ + 0x89, 0x24, 0x88, 0x14, \ + 0x80, 0x23, 0x83, 0x6b, \ + 0x60, 0x40, 0x04, 0x29, \ + 0xe4, 0x40, 0x02, 0x20, \ + 0x80, 0xe5, 0xfc, 0x64, \ + 0xe0, 0x80, 0x4c, 0x4a, \ + 0x81, 0x21, 0x80, 0x55, \ + 0x84, 0x6b, 0x09, 0xc5, \ + 0x60, 0x80, 0x4c, 0x0a, \ + 0x90, 0xb5, 0x83, 0x61, \ + 0x3e, 0xa4, 0x89, 0x65, \ + 0xf0, 0x10, 0x12, 0x0a, \ + 0x89, 0x24, 0x84, 0xd5, \ + 0x80, 0x23, 0xfc, 0x14, \ + 0x60, 0x80, 0x42, 0x0a, \ + 0x04, 0x19, 0x83, 0x11, \ + 0x42, 0x10, 0xc0, 0x40, \ + 0x20, 0x21, 0xd0, 0x04, \ + 0x08, 0x16, 0x99, 0x71, \ + 0x60, 0x01, 0x02, 0x16, \ + 0x00, 0x1e, 0xfc, 0x00, \ + 0x60, 0x01, 0x12, 0x06, \ + 0x89, 0x65, 0xfc, 0x00, \ + 0x74, 0x0c, 0x13, 0x0a, \ + 0x60, 0x00, 0x0c, 0x06, \ + 0xda, 0x35, 0xfc, 0x00, \ + 0x02, 0x94, 0x2f, 0x8b, \ + 0x92, 0x45, 0xfa, 0x80, \ + 0x12, 0x06, 0x25, 0x8b, \ + 0xfc, 0x00, 0x60, 0x01, \ + 0xa7, 0x8b, 0x96, 0x05, \ + 0xa9, 0x8b, 0x96, 0xc5, \ + 0xab, 0x8b, 0x98, 0x85, \ + 0xad, 0x8b, 0x9a, 0x65, \ + 0x23, 0x8b, 0x9c, 0x25, \ + 0x60, 0x01, 0x0c, 0x06, \ + 0xd4, 0x75, 0xfc, 0x00, \ + 0x0f, 0x24, 0x8b, 0x24, \ + 0xc2, 0x17, 0x84, 0x13, \ + 0x02, 0x63, 0x84, 0x13, \ + 0xc0, 0x02, 0x63, 0xb9, \ + 0x4c, 0x0a, 0xd9, 0x85, \ + 0x83, 0x61, 0x60, 0x80, \ + 0x99, 0x71, 0x00, 0x85, \ + 0xf0, 0x04, 0x4c, 0x20, \ + 0x83, 0x85, 0x84, 0x66, \ + 0x89, 0x24, 0xfc, 0x14, \ + 0x60, 0x80, 0x42, 0x0a, \ + 0x04, 0x05, 0x83, 0x11, \ + 0x04, 0x19, 0x80, 0x23, \ + 0x01, 0xc5, 0xe0, 0x40, \ + 0x7c, 0x14, 0x82, 0x19, \ + 0x89, 0x24, 0x99, 0x71, \ + 0x60, 0x80, 0x42, 0x4a, \ + 0x00, 0x85, 0x81, 0x11, \ + 0x04, 0x19, 0x80, 0x23, \ + 0xa0, 0x21, 0xe0, 0x40, \ + 0x00, 0x19, 0x88, 0x16, \ + 0x44, 0x1a, 0xe0, 0x14, \ + 0x82, 0x11, 0x66, 0x9e, \ + 0xe0, 0x14, 0x40, 0x11, \ + 0x04, 0x29, 0x88, 0x23, \ + 0xcf, 0x27, 0xe0, 0x28, \ + 0xe0, 0x18, 0x40, 0x21, \ + 0x8b, 0x24, 0x89, 0x85, \ + 0x84, 0x13, 0x0f, 0x24, \ + 0xe0, 0x84, 0x0c, 0x14, \ + 0x67, 0x71, 0x04, 0x20, \ + 0x04, 0x13, 0xc0, 0x03, \ + 0x02, 0x19, 0x8c, 0x23, \ + 0xc0, 0x04, 0x64, 0x01, \ + 0x6c, 0x01, 0x04, 0x63, \ + 0xc8, 0x17, 0xc0, 0x01, \ + 0x82, 0x63, 0x51, 0x85, \ + 0x0f, 0x24, 0x8b, 0x24, \ + 0xc4, 0x17, 0x84, 0x13, \ + 0x02, 0x65, 0x84, 0x13, \ + 0xdf, 0xb5, 0xe3, 0xdc, \ + 0x0f, 0x24, 0x8b, 0x24, \ + 0xc4, 0x17, 0x84, 0x13, \ + 0x02, 0x65, 0x84, 0x13, \ + 0xdf, 0x35, 0xeb, 0xdc, \ + 0x0f, 0x24, 0x8b, 0x24, \ + 0xc2, 0x17, 0x84, 0x13, \ + 0x02, 0x6b, 0x84, 0x13, \ + 0xc0, 0x01, 0x6f, 0xb9, \ + 0x8b, 0x24, 0xdd, 0xa5, \ + 0x84, 0x13, 0x0f, 0x24, \ + 0x02, 0x69, 0x84, 0x13, \ + 0xc0, 0x05, 0x67, 0x71, \ + 0x8b, 0x24, 0xdd, 0x25, \ + 0x84, 0x13, 0x0f, 0x24, \ + 0xe0, 0x84, 0x0c, 0x14, \ + 0x67, 0x71, 0x04, 0x20, \ + 0x04, 0x13, 0xc0, 0x03, \ + 0x02, 0x19, 0x8c, 0x23, \ + 0xc0, 0x04, 0x60, 0x01, \ + 0x6c, 0x01, 0x04, 0x63, \ + 0x26, 0x78, 0xc0, 0x01, \ + 0xc8, 0x17, 0x7f, 0xfc, \ + 0xfb, 0xfc, 0x04, 0x4a, \ + 0xdf, 0xfc, 0x1d, 0x68, \ + 0x60, 0x14, 0x40, 0xc1, \ + 0x94, 0xc6, 0x83, 0x11, \ + 0x08, 0xc4, 0x85, 0x75, \ + 0x04, 0x24, 0x7a, 0x80, \ + 0x18, 0x44, 0xfa, 0x80, \ + 0x0c, 0x00, 0x60, 0x8c, \ + 0x06, 0x79, 0xe4, 0x40, \ + 0x84, 0xc3, 0x7c, 0x10, \ + 0x7c, 0x30, 0x0c, 0xc9, \ + 0xa5, 0xcf, 0x86, 0x43, \ + 0x90, 0x76, 0xb8, 0x7f, \ + 0xe0, 0x14, 0x00, 0xc9, \ + 0xe0, 0x10, 0x00, 0x29, \ + 0xe0, 0x8c, 0x08, 0xc4, \ + 0x82, 0x21, 0x04, 0x43, \ + 0xfc, 0x10, 0x0c, 0x49, \ + 0x4a, 0x11, 0xa3, 0x4b, \ + 0x40, 0x21, 0x80, 0x00, \ + 0x05, 0x0a, 0x60, 0x10, \ + 0xf6, 0xc5, 0xf4, 0x1c, \ + 0x40, 0xc1, 0x82, 0xc1, \ + 0x19, 0x0a, 0xe0, 0x14, \ + 0x7c, 0xc5, 0xf4, 0x1c, \ + 0x80, 0x00, 0x04, 0x06, \ + 0x0c, 0x0c, 0xc0, 0xf1, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xf8, 0x0c, 0x04, 0x4c, \ + 0x41, 0xde, 0xe5, 0x3d, \ + 0x42, 0x05, 0xdf, 0xfc, \ + 0x00, 0x06, 0x40, 0x00, \ + 0x88, 0x35, 0xc0, 0x00, \ + 0x18, 0x30, 0x09, 0x14, \ + 0x01, 0x01, 0xec, 0x04, \ + 0x83, 0x45, 0x81, 0x31, \ + 0xa1, 0x4b, 0x44, 0x67, \ + 0xfc, 0x18, 0x04, 0x75, \ + 0x8a, 0x7d, 0x82, 0x05, \ + 0x50, 0x22, 0x80, 0xd5, \ + 0xa2, 0x84, 0xf0, 0x98, \ + 0x82, 0xb5, 0x8a, 0x8d, \ + 0x80, 0xb5, 0x8b, 0x7d, \ + 0x9a, 0x64, 0x84, 0x63, \ + 0x80, 0xf5, 0x8b, 0x6d, \ + 0x0c, 0x34, 0x82, 0x31, \ + 0x99, 0x6d, 0xfa, 0x80, \ + 0x81, 0x35, 0xfa, 0x55, \ + 0x83, 0x01, 0x04, 0x36, \ + 0xf0, 0x0c, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x00, 0x04, 0x4c, \ + 0x79, 0x8c, 0x40, 0x52, \ + 0x18, 0x34, 0xff, 0x21, \ + 0x84, 0x0d, 0x76, 0x80, \ + 0x60, 0x00, 0x48, 0x06, \ + 0x9f, 0xfc, 0x41, 0xde, \ + 0x00, 0x00, 0x42, 0x05, \ + 0xf1, 0xb0, 0x4c, 0x42, \ + 0x76, 0x80, 0x06, 0x54, \ + 0x01, 0x01, 0x85, 0x6d, \ + 0x80, 0xd5, 0x81, 0x11, \ + 0x42, 0x16, 0xa1, 0xcd, \ + 0x02, 0x60, 0xc0, 0x00, \ + 0xa6, 0x43, 0x84, 0x04, \ + 0x80, 0xd5, 0x85, 0x4d, \ + 0x40, 0x16, 0xa1, 0x3d, \ + 0x00, 0x40, 0xc0, 0x00, \ + 0x80, 0x1d, 0x84, 0x04, \ + 0xc4, 0x04, 0x02, 0x10, \ + 0xc1, 0x84, 0x00, 0x14, \ + 0xfa, 0x80, 0x00, 0x04, \ + 0xf0, 0x00, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0x81, 0x71, 0x02, 0x6b, \ + 0x8a, 0x9b, 0x65, 0x4d, \ + 0x84, 0xbb, 0x06, 0xab, \ + 0x81, 0x01, 0x18, 0x7e, \ + 0xe0, 0x10, 0x4c, 0x75, \ + 0xae, 0x04, 0x80, 0xf5, \ + 0x41, 0x4a, 0xac, 0x14, \ + 0x00, 0x0c, 0xe0, 0x04, \ + 0x02, 0x40, 0xe2, 0x00, \ + 0xa1, 0x0b, 0x6c, 0x04, \ + 0xfa, 0x80, 0x02, 0x14, \ + 0x04, 0x06, 0x82, 0x2b, \ + 0xc4, 0x27, 0x80, 0x00, \ + 0xfc, 0x08, 0x16, 0x35, \ + 0xe0, 0x24, 0x47, 0x1a, \ + 0xbc, 0x08, 0x14, 0x05, \ + 0xa0, 0x00, 0x11, 0x68, \ + 0xc0, 0x00, 0x02, 0x06, \ + 0x16, 0x25, 0xc4, 0x17, \ + 0x93, 0x2d, 0xfc, 0x04, \ + 0x9c, 0x04, 0x14, 0x05, \ + 0x80, 0x00, 0x10, 0xc8, \ + 0x64, 0x30, 0x02, 0xf0, \ + 0x3c, 0x1e, 0x88, 0x3b, \ + 0x16, 0x2b, 0x8c, 0x1b, \ + 0x76, 0xd8, 0x80, 0x4b, \ + 0x92, 0x5b, 0x6f, 0xfc, \ + 0x10, 0x18, 0xa1, 0x0b, \ + 0x99, 0x28, 0x80, 0x00, \ + 0x64, 0x04, 0x02, 0x20, \ + 0x16, 0x45, 0xc4, 0x27, \ + 0xc4, 0x17, 0x7c, 0x08, \ + 0xfc, 0x04, 0x16, 0x35, \ + 0xe1, 0x7c, 0x0a, 0x44, \ + 0x61, 0x7c, 0x00, 0x34, \ + 0xf1, 0x90, 0x50, 0x32, \ + 0x73, 0x94, 0x58, 0x02, \ + 0x40, 0x82, 0x81, 0x51, \ + 0x19, 0x04, 0xf9, 0xb0, \ + 0x7c, 0x0e, 0xa1, 0x0b, \ + 0x82, 0x65, 0x83, 0x01, \ + 0x60, 0x01, 0x0a, 0x06, \ + 0x90, 0x0b, 0x60, 0x00, \ + 0x4a, 0xc2, 0x90, 0x53, \ + 0x21, 0x5b, 0xf2, 0x9c, \ + 0x82, 0x45, 0x98, 0x5b, \ + 0x46, 0xb2, 0x7b, 0x41, \ + 0x18, 0x4e, 0xf0, 0x84, \ + 0x0c, 0x34, 0x84, 0xb3, \ + 0x2c, 0x44, 0x80, 0x8b, \ + 0x14, 0x25, 0x8a, 0xcb, \ + 0xb2, 0x33, 0x7c, 0x08, \ + 0x7c, 0x04, 0x14, 0x15, \ + 0xfc, 0x04, 0xa8, 0x93, \ + 0xe4, 0x0a, 0x54, 0x3a, \ + 0xe4, 0x8a, 0x44, 0x3a, \ + 0x64, 0x06, 0x46, 0x9a, \ + 0x42, 0x9a, 0xa1, 0x0b, \ + 0x94, 0x33, 0xe4, 0x86, \ + 0x52, 0x12, 0x02, 0xe5, \ + 0x98, 0xbb, 0xf2, 0x88, \ + 0x8a, 0x35, 0xa1, 0xbd, \ + 0x82, 0x9d, 0xff, 0x11, \ + 0x46, 0x72, 0x88, 0xd5, \ + 0x52, 0x72, 0xf1, 0x8c, \ + 0x83, 0x71, 0x73, 0xa4, \ + 0x00, 0xc4, 0x87, 0x65, \ + 0xa1, 0x9d, 0x61, 0x7c, \ + 0x70, 0x80, 0x42, 0x82, \ + 0x48, 0xc2, 0x90, 0x2b, \ + 0xc0, 0x1f, 0x72, 0x80, \ + 0xc3, 0x17, 0x40, 0x4f, \ + 0xe0, 0xfc, 0x00, 0x44, \ + 0x02, 0x44, 0x22, 0x0f, \ + 0x4a, 0x72, 0xe1, 0x04, \ + 0x48, 0x72, 0xf1, 0x80, \ + 0x40, 0x50, 0x73, 0x84, \ + 0x06, 0x03, 0xdc, 0x00, \ + 0xdc, 0x00, 0x42, 0x40, \ + 0x72, 0xa4, 0x42, 0x12, \ + 0x5d, 0x78, 0x98, 0x3b, \ + 0x93, 0x35, 0xef, 0xcc, \ + 0x82, 0xbd, 0xff, 0x11, \ + 0xa1, 0x9d, 0x82, 0x35, \ + 0x91, 0xa0, 0x50, 0x72, \ + 0x13, 0xac, 0x56, 0x72, \ + 0x80, 0x00, 0x0e, 0x16, \ + 0x44, 0x92, 0x82, 0x85, \ + 0x82, 0x2d, 0xf8, 0x2c, \ + 0x91, 0x8c, 0x46, 0x72, \ + 0x93, 0xa4, 0x52, 0x72, \ + 0x91, 0xa0, 0x50, 0x72, \ + 0x93, 0xac, 0x56, 0x72, \ + 0x82, 0x0d, 0xff, 0x11, \ + 0x0c, 0x1b, 0x82, 0x55, \ + 0x06, 0xab, 0x86, 0x2b, \ + 0x10, 0x4b, 0x92, 0x3b, \ + 0x70, 0xb8, 0x96, 0x5b, \ + 0x94, 0x3b, 0xef, 0xfc, \ + 0x16, 0x04, 0x16, 0xab, \ + 0x04, 0xb4, 0xfa, 0x80, \ + 0x83, 0x11, 0x60, 0x84, \ + 0xec, 0x04, 0x00, 0x20, \ + 0xe8, 0x80, 0x00, 0x14, \ + 0x61, 0x7c, 0x02, 0x04, \ + 0x74, 0x58, 0x86, 0x03, \ + 0x42, 0x12, 0x6f, 0xcc, \ + 0x7c, 0x1e, 0xf2, 0xa4, \ + 0x00, 0xb0, 0x80, 0x9b, \ + 0x83, 0x11, 0x6c, 0x04, \ + 0x68, 0x80, 0x00, 0x14, \ + 0x02, 0x04, 0x96, 0x2b, \ + 0x90, 0x03, 0x61, 0x7c, \ + 0x6f, 0xcc, 0x73, 0x68, \ + 0xf2, 0xa8, 0x42, 0x12, \ + 0x06, 0x14, 0x00, 0x2b, \ + 0x08, 0x24, 0xe0, 0xfc, \ + 0x41, 0xfe, 0x61, 0x84, \ + 0xdf, 0xff, 0x7f, 0xfc, \ + 0xa6, 0x4f, 0x7c, 0x34, \ + 0x88, 0x93, 0x21, 0x1b, \ + 0x60, 0x00, 0x02, 0xda, \ + 0x44, 0xea, 0x83, 0x17, \ + 0x46, 0x32, 0x64, 0x00, \ + 0x40, 0x1a, 0xf2, 0x84, \ + 0x84, 0x1b, 0x65, 0x84, \ + 0x60, 0x1c, 0x5e, 0x07, \ + 0xe1, 0x88, 0x40, 0x2a, \ + 0x60, 0x18, 0x5e, 0x17, \ + 0xed, 0x84, 0x44, 0x2a, \ + 0x5f, 0xfd, 0x43, 0xfe, \ + 0x07, 0x6a, 0x5f, 0xff, \ + 0x06, 0x1a, 0xe0, 0x00, \ + 0x47, 0xfe, 0x60, 0x00, \ + 0xdf, 0xff, 0x7f, 0xfc, \ + 0xe0, 0x00, 0x12, 0xca, \ + 0xe5, 0x88, 0x42, 0x2a, \ + 0xe1, 0x80, 0x42, 0x0a, \ + 0xed, 0x88, 0x40, 0x0a, \ + 0xdc, 0x00, 0x46, 0x90, \ + 0x60, 0x86, 0x42, 0x3a, \ + 0xe9, 0xac, 0x06, 0x04, \ + 0xe0, 0x0c, 0x43, 0x2a, \ + 0x40, 0x01, 0x01, 0x28, \ + 0x00, 0x14, 0x40, 0x00, \ + 0x80, 0x00, 0x48, 0xad, \ + 0x60, 0x1c, 0x1e, 0x17, \ + 0x45, 0x4a, 0xff, 0x11, \ + 0x42, 0x0a, 0xe0, 0x04, \ + 0x1e, 0x17, 0xe1, 0x80, \ + 0x40, 0x0a, 0xe0, 0x18, \ + 0x80, 0x85, 0xed, 0x84, \ + 0x4c, 0x15, 0x83, 0x11, \ + 0xa1, 0x7b, 0xe0, 0x10, \ + 0xd1, 0x80, 0x00, 0x04, \ + 0x84, 0x0e, 0x9b, 0x14, \ + 0xa0, 0xf1, 0x18, 0x04, \ + 0xf6, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0xe0, 0x18, 0x00, 0x79, \ + 0xe0, 0x28, 0x00, 0x39, \ + 0xe0, 0x20, 0x00, 0xc9, \ + 0x08, 0x70, 0x06, 0x68, \ + 0x4a, 0x3a, 0xec, 0x04, \ + 0x00, 0x49, 0xe6, 0x92, \ + 0x00, 0x89, 0xe0, 0x14, \ + 0x52, 0x52, 0x60, 0x1c, \ + 0x00, 0x59, 0xf0, 0xb0, \ + 0x81, 0x01, 0x60, 0x24, \ + 0xd2, 0x6d, 0x04, 0x18, \ + 0xfb, 0x01, 0x08, 0x0e, \ + 0x12, 0x40, 0x8e, 0x55, \ + 0x52, 0x5a, 0xec, 0x04, \ + 0x90, 0x93, 0xe6, 0xa6, \ + 0x8c, 0x75, 0xd2, 0x1d, \ + 0x8c, 0x35, 0xd9, 0x6d, \ + 0x8a, 0xf5, 0xd1, 0x1d, \ + 0x8a, 0xa5, 0xa1, 0x3b, \ + 0x8a, 0x65, 0xa1, 0x5b, \ + 0xf9, 0xa0, 0x52, 0xc2, \ + 0xf0, 0x00, 0x12, 0x8a, \ + 0xf0, 0x7d, 0x88, 0xd5, \ + 0xf4, 0x4d, 0x88, 0x95, \ + 0x12, 0x40, 0x88, 0x55, \ + 0x13, 0x0a, 0xec, 0x08, \ + 0x86, 0xb5, 0xf4, 0xf4, \ + 0x86, 0x75, 0xe5, 0x7d, \ + 0xf9, 0x94, 0x4e, 0x32, \ + 0xac, 0x7f, 0xa2, 0x7f, \ + 0xf0, 0x00, 0x0e, 0x8a, \ + 0xb0, 0x13, 0x84, 0x95, \ + 0xe1, 0x16, 0x4e, 0x1a, \ + 0x82, 0xf5, 0xa1, 0x7b, \ + 0x58, 0x6a, 0xb8, 0x63, \ + 0xa1, 0xcb, 0xe1, 0x0e, \ + 0x40, 0x6a, 0x82, 0x55, \ + 0x42, 0x1a, 0xe0, 0x0e, \ + 0x40, 0x0a, 0xe0, 0x16, \ + 0x82, 0x03, 0xe6, 0x92, \ + 0x81, 0x01, 0x08, 0x0e, \ + 0xf0, 0x1c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xed, 0x50, 0x1e, 0xf0, \ + 0x80, 0xab, 0x3d, 0x04, \ + 0x81, 0x91, 0x25, 0x5e, \ + 0x84, 0x8b, 0x15, 0x4e, \ + 0x16, 0x30, 0x13, 0x0e, \ + 0x3b, 0x04, 0xe5, 0x20, \ + 0xe0, 0x08, 0x0d, 0x06, \ + 0x81, 0x71, 0x21, 0x1e, \ + 0xa3, 0x0e, 0xa9, 0x9e, \ + 0x27, 0x0e, 0xb9, 0x04, \ + 0x65, 0x01, 0x00, 0xa0, \ + 0x1b, 0x0e, 0xc0, 0x02, \ + 0x66, 0x01, 0x00, 0xa0, \ + 0x19, 0x0e, 0xc0, 0x04, \ + 0x67, 0x01, 0x00, 0xa0, \ + 0x17, 0x0e, 0xc0, 0x06, \ + 0x64, 0x01, 0x00, 0xa0, \ + 0x1f, 0x0e, 0xc0, 0x09, \ + 0x65, 0x01, 0x00, 0xa0, \ + 0x1d, 0x0e, 0xc0, 0x0b, \ + 0x8d, 0x85, 0x81, 0x01, \ + 0x98, 0x55, 0xa1, 0x0b, \ + 0x78, 0x90, 0x16, 0x03, \ + 0xac, 0x12, 0x94, 0x5b, \ + 0xa7, 0x34, 0xa5, 0x24, \ + 0x6a, 0xb8, 0xa3, 0x44, \ + 0xa1, 0x0b, 0xef, 0xe4, \ + 0xa1, 0x34, 0x94, 0xf5, \ + 0x16, 0x03, 0x9d, 0x44, \ + 0xac, 0x12, 0xf8, 0x90, \ + 0xe0, 0x30, 0x16, 0x23, \ + 0x8b, 0x4e, 0x8c, 0x38, \ + 0xa7, 0x54, 0x9f, 0x44, \ + 0x97, 0x44, 0x89, 0x4e, \ + 0x99, 0x44, 0xfe, 0x4e, \ + 0x9b, 0x44, 0xfc, 0x4e, \ + 0xbc, 0xae, 0xbe, 0x4e, \ + 0x6c, 0x18, 0xa5, 0x44, \ + 0xa1, 0x0b, 0xef, 0xe4, \ + 0x93, 0x44, 0x8e, 0xf5, \ + 0x16, 0x03, 0xa1, 0x34, \ + 0x2c, 0x12, 0xf8, 0x90, \ + 0xf0, 0xa4, 0x4a, 0x42, \ + 0x60, 0x30, 0x16, 0x23, \ + 0x8e, 0x38, 0x8c, 0x43, \ + 0xe0, 0x51, 0x0f, 0x5e, \ + 0x09, 0x4e, 0x8b, 0x5e, \ + 0xe5, 0x40, 0x08, 0xf0, \ + 0x9d, 0x44, 0xfe, 0x4e, \ + 0xa7, 0x54, 0x91, 0x5e, \ + 0x9f, 0x44, 0xfc, 0x4e, \ + 0xbc, 0xae, 0xbe, 0x4e, \ + 0x71, 0x58, 0x95, 0x44, \ + 0xa1, 0x0b, 0xef, 0xe4, \ + 0xa9, 0x04, 0x88, 0x35, \ + 0x11, 0x04, 0x8f, 0xc4, \ + 0x00, 0x34, 0xa3, 0x0b, \ + 0xc0, 0x00, 0x00, 0x06, \ + 0xc0, 0x00, 0x58, 0x35, \ + 0xc0, 0x60, 0x58, 0x35, \ + 0xa1, 0x44, 0x84, 0xb5, \ + 0x16, 0x03, 0x93, 0x24, \ + 0x50, 0x58, 0xf8, 0x90, \ + 0xe5, 0x80, 0x08, 0xc0, \ + 0x04, 0x23, 0xac, 0x12, \ + 0x89, 0x4e, 0xfc, 0x18, \ + 0xfe, 0xce, 0xa7, 0x44, \ + 0xbe, 0x4e, 0xfc, 0xae, \ + 0x3c, 0x4e, 0x95, 0x44, \ + 0x63, 0x78, 0x83, 0x41, \ + 0x02, 0x71, 0xef, 0xe0, \ + 0x04, 0xb1, 0x88, 0x91, \ + 0xa2, 0x18, 0x84, 0x61, \ + 0xe4, 0xf5, 0x83, 0x7d, \ + 0xe5, 0x50, 0x1e, 0xf0, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0xec, 0x60, 0x1e, 0xf0, \ + 0x84, 0xab, 0x06, 0xb8, \ + 0x60, 0x10, 0x00, 0x23, \ + 0x02, 0x14, 0x99, 0x41, \ + 0x00, 0x0b, 0x81, 0x51, \ + 0x3e, 0x0e, 0xe0, 0x14, \ + 0xe7, 0x60, 0x00, 0x30, \ + 0x41, 0x56, 0x81, 0x45, \ + 0xec, 0x04, 0x08, 0x40, \ + 0xfe, 0x95, 0xa1, 0x4b, \ + 0x6c, 0x04, 0x00, 0x10, \ + 0x7f, 0xfc, 0x49, 0xee, \ + 0x3e, 0x04, 0xcf, 0xff, \ + 0xe4, 0x10, 0x41, 0x4a, \ + 0xe0, 0x04, 0x04, 0xda, \ + 0xed, 0x80, 0x42, 0x0a, \ + 0xec, 0x04, 0x00, 0x00, \ + 0x6f, 0xf9, 0x01, 0x5a, \ + 0xfb, 0x01, 0x40, 0x0f, \ + 0xed, 0x80, 0x42, 0x0a, \ + 0xe4, 0x30, 0x17, 0x5a, \ + 0xed, 0x80, 0x42, 0x0a, \ + 0xc0, 0x00, 0x00, 0x06, \ + 0x13, 0xc8, 0xbc, 0x0e, \ + 0x40, 0x2e, 0x80, 0x00, \ + 0x12, 0x30, 0x60, 0x0a, \ + 0x10, 0x30, 0xe7, 0x60, \ + 0x44, 0x1a, 0xe6, 0xa0, \ + 0x48, 0x1a, 0xe5, 0x02, \ + 0x09, 0x2e, 0xe5, 0x82, \ + 0xe1, 0x84, 0x02, 0x24, \ + 0x00, 0x44, 0x7e, 0x4e, \ + 0x20, 0x1f, 0xe0, 0xfc, \ + 0xfc, 0x1e, 0xc3, 0x47, \ + 0xa5, 0x35, 0x8b, 0x4e, \ + 0x81, 0x01, 0x20, 0x12, \ + 0xa0, 0x1d, 0xa8, 0x34, \ + 0xc6, 0x86, 0x40, 0x1a, \ + 0xe5, 0x82, 0x42, 0x3a, \ + 0xe5, 0x02, 0x40, 0x3a, \ + 0xf6, 0x80, 0x04, 0x14, \ + 0x6c, 0x80, 0x06, 0x20, \ + 0xe4, 0x7c, 0x05, 0x0a, \ + 0x48, 0x22, 0x00, 0x5b, \ + 0x06, 0x42, 0xfb, 0x8c, \ + 0x4a, 0x10, 0x6c, 0x7c, \ + 0x18, 0x54, 0x9c, 0x00, \ + 0x81, 0x51, 0x68, 0x8c, \ + 0x1c, 0x00, 0x4a, 0x00, \ + 0xec, 0x00, 0x0c, 0x34, \ + 0x6f, 0x8c, 0x0e, 0x54, \ + 0x4c, 0x62, 0xa6, 0x43, \ + 0x08, 0x42, 0x78, 0x9c, \ + 0x0b, 0x64, 0xec, 0x04, \ + 0x08, 0xc4, 0xac, 0xcf, \ + 0xa7, 0x5f, 0x68, 0x90, \ + 0xca, 0xcb, 0x48, 0x3b, \ + 0xe4, 0x0c, 0x4d, 0x4a, \ + 0x60, 0x0c, 0x4c, 0x1a, \ + 0x7c, 0x64, 0x8c, 0x3b, \ + 0xe5, 0x80, 0x42, 0x0a, \ + 0xe4, 0x30, 0x4d, 0x6a, \ + 0xe1, 0x88, 0x42, 0x1a, \ + 0xed, 0x80, 0x42, 0x0a, \ + 0x50, 0x98, 0x40, 0x52, \ + 0xdc, 0x00, 0x4e, 0x20, \ + 0x42, 0x42, 0x08, 0xf5, \ + 0xa1, 0x2b, 0xd2, 0x8c, \ + 0x0e, 0x06, 0x08, 0x65, \ + 0x0e, 0x20, 0x80, 0x00, \ + 0x80, 0x3b, 0x6c, 0x04, \ + 0xe4, 0x7c, 0x0f, 0x0a, \ + 0x1c, 0x00, 0x46, 0x10, \ + 0xec, 0x84, 0x02, 0x20, \ + 0x7b, 0x84, 0x44, 0x72, \ + 0xc0, 0x00, 0x00, 0x06, \ + 0xec, 0x7c, 0x02, 0x22, \ + 0x6f, 0x84, 0x08, 0x04, \ + 0xec, 0x00, 0x0a, 0x14, \ + 0xa3, 0x0f, 0x22, 0x23, \ + 0x48, 0x52, 0x23, 0x3f, \ + 0x0b, 0x14, 0xf8, 0x90, \ + 0x04, 0x22, 0xa8, 0x3f, \ + 0x8c, 0x03, 0x6c, 0x04, \ + 0x42, 0x32, 0xa5, 0x3f, \ + 0x89, 0x24, 0xf2, 0x84, \ + 0x45, 0xd8, 0xfe, 0x34, \ + 0x00, 0x2b, 0xef, 0xcc, \ + 0xe4, 0x2c, 0x00, 0x70, \ + 0x6c, 0x54, 0x08, 0x70, \ + 0xe4, 0x7c, 0x01, 0x0a, \ + 0x48, 0x02, 0x03, 0x31, \ + 0x06, 0x06, 0xfb, 0x90, \ + 0x00, 0xca, 0x00, 0x00, \ + 0x00, 0x0c, 0xe4, 0x80, \ + 0x0a, 0x42, 0x62, 0x00, \ + 0x18, 0x04, 0xec, 0x7c, \ + 0x0c, 0x54, 0x6f, 0x94, \ + 0x2a, 0x43, 0xec, 0x00, \ + 0x58, 0x62, 0xab, 0x3f, \ + 0x08, 0x42, 0x78, 0xb0, \ + 0x38, 0x3f, 0xec, 0x04, \ + 0x29, 0x3f, 0xab, 0x0f, \ + 0xe4, 0x00, 0x44, 0xaa, \ + 0x41, 0xf6, 0x47, 0x1d, \ + 0x42, 0x1a, 0xe0, 0x0c, \ + 0x42, 0x0a, 0xe0, 0x0c, \ + 0x42, 0x1a, 0xe5, 0x80, \ + 0x42, 0x0a, 0xe1, 0x88, \ + 0x9e, 0xc5, 0xed, 0x80, \ + 0x8e, 0xf5, 0xa1, 0x1b, \ + 0x00, 0x24, 0xbe, 0x44, \ + 0x02, 0x44, 0xf6, 0x80, \ + 0x80, 0x13, 0xf6, 0x80, \ + 0xe4, 0x80, 0x02, 0x8a, \ + 0x0f, 0x0a, 0x90, 0x75, \ + 0x83, 0x41, 0x64, 0x74, \ + 0x3e, 0x04, 0x9c, 0x15, \ + 0xae, 0x13, 0xbd, 0x11, \ + 0x66, 0x82, 0x40, 0x2a, \ + 0xe8, 0x84, 0x04, 0x44, \ + 0x64, 0x08, 0x02, 0x70, \ + 0xec, 0x04, 0x04, 0x20, \ + 0x46, 0x02, 0xa3, 0x2f, \ + 0x85, 0x3b, 0xf8, 0x08, \ + 0x06, 0x10, 0x98, 0xd5, \ + 0x03, 0x0a, 0x6c, 0x80, \ + 0x01, 0x21, 0xe4, 0x7c, \ + 0xfb, 0x8c, 0x46, 0x12, \ + 0x40, 0x00, 0x04, 0x16, \ + 0xe4, 0x80, 0x02, 0xca, \ + 0x62, 0x00, 0x08, 0x0c, \ + 0xec, 0x7c, 0x02, 0x32, \ + 0x6f, 0x84, 0x0a, 0x44, \ + 0xec, 0x00, 0x18, 0x14, \ + 0xa3, 0x2f, 0x22, 0x33, \ + 0x78, 0x94, 0x4a, 0xc2, \ + 0x2a, 0x2f, 0xa3, 0x4f, \ + 0xec, 0x04, 0x06, 0x32, \ + 0x88, 0x03, 0x27, 0x2f, \ + 0x07, 0x55, 0x81, 0x11, \ + 0xf2, 0x84, 0x42, 0x22, \ + 0x81, 0x31, 0x04, 0x0b, \ + 0x48, 0x18, 0xbe, 0x24, \ + 0x06, 0x70, 0xef, 0xcc, \ + 0x81, 0x21, 0x64, 0x08, \ + 0xe4, 0x7c, 0x07, 0x0a, \ + 0x04, 0x16, 0x01, 0xe5, \ + 0x06, 0x70, 0xc0, 0x00, \ + 0x40, 0x2a, 0x64, 0x08, \ + 0x42, 0x2a, 0xe5, 0x12, \ + 0x07, 0x0a, 0x65, 0x92, \ + 0x83, 0x21, 0xe4, 0x7c, \ + 0x80, 0x00, 0x04, 0x06, \ + 0x6c, 0x78, 0x08, 0x70, \ + 0xe4, 0x80, 0x06, 0xca, \ + 0x62, 0x00, 0x0a, 0x0c, \ + 0xfb, 0x90, 0x48, 0x32, \ + 0xec, 0x7c, 0x06, 0x42, \ + 0x6f, 0x8c, 0x18, 0x54, \ + 0xec, 0x00, 0x0c, 0x34, \ + 0xa7, 0x2f, 0x26, 0x43, \ + 0x78, 0xb0, 0x58, 0x62, \ + 0x38, 0x2f, 0xa7, 0x5f, \ + 0xec, 0x04, 0x08, 0x42, \ + 0x8a, 0x03, 0x29, 0x2f, \ + 0xf2, 0x88, 0x42, 0x12, \ + 0x6f, 0xcc, 0x4b, 0x28, \ + 0xe4, 0x0c, 0x04, 0x70, \ + 0xe7, 0xfc, 0x00, 0x8a, \ + 0x00, 0x0c, 0x41, 0xf6, \ + 0x41, 0xf6, 0xa1, 0x1b, \ + 0xa4, 0x06, 0xc0, 0x0c, \ + 0x6c, 0x04, 0x16, 0xb0, \ + 0x08, 0xa1, 0x82, 0x91, \ + 0x81, 0x95, 0x84, 0x81, \ + 0x68, 0x84, 0x02, 0x44, \ + 0x82, 0x03, 0x86, 0x71, \ + 0x7f, 0x45, 0xef, 0x0f, \ + 0xef, 0xfc, 0x00, 0x02, \ + 0x2d, 0xd8, 0xa1, 0xbb, \ + 0x3c, 0x04, 0xdf, 0xfc, \ + 0x0c, 0x0c, 0xb0, 0xf1, \ + 0xdd, 0x93, 0xf6, 0x7c, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x82, 0x9b, 0x00, 0x6b, \ + 0x0e, 0x60, 0x3e, 0x2e, \ + 0x08, 0x8b, 0xec, 0x04, \ + 0x3c, 0x9e, 0x86, 0xab, \ + 0x01, 0x21, 0x81, 0x01, \ + 0x01, 0x41, 0x89, 0x31, \ + 0x7d, 0x78, 0x8e, 0x1b, \ + 0x8c, 0x5b, 0x6f, 0xdc, \ + 0x82, 0xb5, 0xa1, 0x0b, \ + 0x81, 0x01, 0x3e, 0xae, \ + 0x81, 0x21, 0x3c, 0x9e, \ + 0x81, 0x41, 0x11, 0x31, \ + 0x8c, 0x5b, 0x0e, 0x1b, \ + 0xef, 0xdc, 0x7c, 0xa8, \ + 0x80, 0xa5, 0xa1, 0x0b, \ + 0x0c, 0x0c, 0x90, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0x81, 0x01, 0x3e, 0x8e, \ + 0x81, 0x21, 0x3c, 0x9e, \ + 0x81, 0x41, 0x21, 0x31, \ + 0x8c, 0x5b, 0x0e, 0x1b, \ + 0xef, 0xdc, 0x7b, 0xa8, \ + 0x0c, 0x0c, 0x90, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0xff, 0x41, 0x21, 0x1b, \ + 0x88, 0x0d, 0x84, 0xc5, \ + 0xc1, 0xac, 0x08, 0x04, \ + 0xc1, 0x06, 0x48, 0x4a, \ + 0xd1, 0x90, 0x48, 0x12, \ + 0x17, 0x80, 0x08, 0x04, \ + 0x73, 0xfc, 0x40, 0x00, \ + 0x08, 0x44, 0xc0, 0x07, \ + 0x08, 0x40, 0x81, 0x2c, \ + 0x42, 0x4a, 0x84, 0x04, \ + 0x81, 0x41, 0x61, 0x86, \ + 0x8c, 0x0e, 0x88, 0x16, \ + 0x88, 0x0b, 0x5d, 0x93, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0xec, 0xe0, 0x1e, 0xf0, \ + 0x88, 0x6b, 0x00, 0xbb, \ + 0x60, 0x18, 0x16, 0x89, \ + 0x06, 0x7b, 0x84, 0x4b, \ + 0x04, 0xf0, 0x82, 0xab, \ + 0x06, 0xf0, 0x64, 0xd0, \ + 0x09, 0x4e, 0xe4, 0xa0, \ + 0x7d, 0x58, 0x88, 0x0b, \ + 0x90, 0x1b, 0x6f, 0xfc, \ + 0x64, 0xc0, 0x04, 0xf0, \ + 0xe4, 0x90, 0x06, 0xf0, \ + 0x90, 0x1b, 0x14, 0x0b, \ + 0xef, 0xfc, 0x7c, 0xc8, \ + 0x64, 0xb0, 0x04, 0xf0, \ + 0xe4, 0x80, 0x06, 0xf0, \ + 0x8e, 0x0b, 0x0b, 0x7e, \ + 0x6f, 0xfc, 0x7c, 0x48, \ + 0x1e, 0x09, 0x90, 0x1b, \ + 0x98, 0xb1, 0x60, 0xd0, \ + 0x60, 0xc0, 0x1e, 0x39, \ + 0xe0, 0x84, 0x04, 0x84, \ + 0x60, 0xb0, 0x1e, 0x59, \ + 0x42, 0x07, 0x96, 0x23, \ + 0xec, 0x08, 0x04, 0x20, \ + 0x70, 0x80, 0x42, 0xb2, \ + 0x00, 0x10, 0xc2, 0x37, \ + 0x4e, 0xb2, 0x6c, 0x08, \ + 0x57, 0x0d, 0xf0, 0x8c, \ + 0x42, 0x57, 0x84, 0x9b, \ + 0x9c, 0x00, 0x52, 0x00, \ + 0x6c, 0x08, 0x00, 0x70, \ + 0xf0, 0x94, 0x46, 0xb2, \ + 0x84, 0x5b, 0x57, 0x0d, \ + 0x1c, 0x00, 0x4a, 0x00, \ + 0xec, 0x08, 0x00, 0x30, \ + 0x84, 0xcb, 0x57, 0x0d, \ + 0x58, 0x00, 0x15, 0x04, \ + 0xfc, 0x0e, 0x9c, 0x00, \ + 0x0b, 0x04, 0x89, 0x04, \ + 0x13, 0x04, 0x81, 0xab, \ + 0xe0, 0x00, 0x54, 0x1a, \ + 0xed, 0x84, 0x40, 0x0a, \ + 0x91, 0x04, 0xbe, 0x0e, \ + 0x81, 0x01, 0x3c, 0x0e, \ + 0x91, 0x95, 0xfe, 0x0e, \ + 0x8a, 0xab, 0x0f, 0x8e, \ + 0x8e, 0x5b, 0x04, 0x8a, \ + 0xa4, 0x0a, 0x8d, 0x9e, \ + 0x89, 0x94, 0xfc, 0x74, \ + 0x40, 0x0a, 0xb0, 0x03, \ + 0x0f, 0x2a, 0x66, 0x9e, \ + 0xc0, 0x08, 0x64, 0x00, \ + 0x52, 0x1a, 0x8b, 0x74, \ + 0x0e, 0x00, 0x60, 0x1c, \ + 0xc0, 0x04, 0x64, 0x00, \ + 0x5f, 0xfd, 0x41, 0xde, \ + 0xd7, 0x77, 0x7f, 0xff, \ + 0x40, 0x0d, 0x5e, 0x05, \ + 0x8c, 0x9b, 0x40, 0x00, \ + 0x20, 0x00, 0x01, 0x88, \ + 0xa0, 0x00, 0x0c, 0x06, \ + 0xb0, 0x0a, 0x8c, 0x6a, \ + 0xac, 0x03, 0xbc, 0x44, \ + 0x66, 0x92, 0x40, 0x0a, \ + 0x64, 0x00, 0x09, 0x1a, \ + 0x49, 0xde, 0xc0, 0x08, \ + 0xff, 0xff, 0x3f, 0xfd, \ + 0x20, 0x0d, 0x5e, 0x45, \ + 0x00, 0x00, 0x40, 0x00, \ + 0xc0, 0x04, 0x64, 0x01, \ + 0x80, 0x63, 0xd7, 0x07, \ + 0x06, 0x25, 0x8e, 0x83, \ + 0x80, 0x00, 0x0e, 0x06, \ + 0x82, 0x4b, 0x14, 0x7a, \ + 0xbe, 0x14, 0xa8, 0x0a, \ + 0x40, 0x0a, 0xae, 0x03, \ + 0x03, 0x1a, 0x66, 0x86, \ + 0xc0, 0x08, 0x64, 0x00, \ + 0x3f, 0xfd, 0x43, 0xde, \ + 0x5e, 0x15, 0xff, 0xff, \ + 0x40, 0x00, 0x20, 0x0d, \ + 0x64, 0x01, 0x00, 0x00, \ + 0x57, 0x07, 0xc0, 0x04, \ + 0x80, 0x73, 0x88, 0x1b, \ + 0x7b, 0x80, 0x00, 0x64, \ + 0xfb, 0x80, 0x0c, 0x84, \ + 0x7a, 0x80, 0x4c, 0x62, \ + 0xe4, 0x08, 0x00, 0x10, \ + 0x7b, 0x80, 0x02, 0x74, \ + 0xe4, 0x08, 0x41, 0x1a, \ + 0x7a, 0x98, 0x42, 0x12, \ + 0x59, 0x1c, 0x92, 0x6b, \ + 0x42, 0x00, 0x96, 0x1b, \ + 0x00, 0x50, 0x3c, 0x00, \ + 0x41, 0x1a, 0xe4, 0x08, \ + 0x96, 0x7b, 0x64, 0x08, \ + 0x4e, 0x00, 0x0d, 0x44, \ + 0x00, 0x30, 0xbc, 0x00, \ + 0x96, 0x3b, 0x64, 0x08, \ + 0x64, 0x08, 0x41, 0x1a, \ + 0x46, 0x00, 0x96, 0x9b, \ + 0x00, 0x40, 0x3c, 0x00, \ + 0x0f, 0x84, 0xe4, 0x08, \ + 0xe4, 0x08, 0x41, 0x1a, \ + 0x3c, 0x00, 0x52, 0x00, \ + 0xe4, 0x08, 0x00, 0xa0, \ + 0x64, 0x08, 0x41, 0x1a, \ + 0x4a, 0x00, 0x96, 0x5b, \ + 0x00, 0xc0, 0x3c, 0x00, \ + 0x41, 0x1a, 0xe4, 0x08, \ + 0x96, 0xcb, 0x64, 0x08, \ + 0x3c, 0x00, 0x58, 0x00, \ + 0xec, 0x04, 0x10, 0x80, \ + 0xe0, 0x00, 0x10, 0x1a, \ + 0x3f, 0xfd, 0x37, 0x68, \ + 0x7e, 0x04, 0xc3, 0xff, \ + 0x0c, 0x0c, 0xf0, 0xf1, \ + 0xdd, 0x93, 0xf6, 0x7c, \ + 0x71, 0x88, 0x44, 0x12, \ + 0xe1, 0xf5, 0x82, 0x33, \ + 0x84, 0x01, 0xc2, 0x07, \ + 0x00, 0x04, 0x5d, 0x93, \ + 0xc1, 0xc3, 0xfa, 0x80, \ + 0xfa, 0x00, 0x00, 0x4c, \ + 0xe5, 0x06, 0x44, 0x0a, \ + 0x65, 0x86, 0x40, 0x0a, \ + 0x04, 0x20, 0x81, 0x11, \ + 0xc2, 0x00, 0x64, 0x01, \ + 0x72, 0x84, 0x40, 0x02, \ + 0xe1, 0xcc, 0x08, 0x24, \ + 0x60, 0xb8, 0x02, 0x04, \ + 0x06, 0x24, 0xda, 0x07, \ + 0xa0, 0x4f, 0x61, 0xc8, \ + 0x79, 0x84, 0x44, 0x32, \ + 0xfc, 0x7c, 0x00, 0x44, \ + 0x60, 0x10, 0x02, 0x06, \ + 0x63, 0x98, 0x81, 0x31, \ + 0x08, 0x4c, 0xef, 0xc8, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0x04, 0x04, 0x4c, \ + 0x80, 0x2b, 0x04, 0x6b, \ + 0xff, 0x71, 0x21, 0x2b, \ + 0x00, 0x14, 0x82, 0xc5, \ + 0x06, 0x24, 0x60, 0xc8, \ + 0x06, 0x03, 0xe1, 0x84, \ + 0x01, 0x71, 0xdd, 0x17, \ + 0x62, 0x58, 0x81, 0x31, \ + 0x42, 0x12, 0x6f, 0xc8, \ + 0x98, 0x0e, 0xf2, 0x9c, \ + 0x0c, 0x4c, 0x8e, 0x0b, \ + 0xdd, 0x93, 0xf2, 0x04, \ + 0xe5, 0x06, 0x44, 0x0a, \ + 0x65, 0x86, 0x40, 0x0a, \ + 0x04, 0x20, 0x81, 0x11, \ + 0xc2, 0x00, 0x64, 0x01, \ + 0x72, 0x84, 0x40, 0x02, \ + 0xdc, 0x07, 0xe5, 0x27, \ + 0xa4, 0x0f, 0x5d, 0x93, \ + 0xfa, 0x00, 0x00, 0x4c, \ + 0x82, 0x2b, 0x42, 0x07, \ + 0x81, 0x11, 0x04, 0x01, \ + 0x7a, 0x80, 0x00, 0x04, \ + 0x69, 0xb8, 0x81, 0x31, \ + 0x04, 0x04, 0xef, 0xc8, \ + 0xa2, 0x2f, 0xe1, 0xa0, \ + 0x41, 0xfe, 0xa1, 0x2b, \ + 0x00, 0x04, 0xdf, 0xfc, \ + 0x08, 0x4c, 0xfa, 0x80, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x6f, 0xfc, 0x7a, 0x28, \ + 0x02, 0x64, 0x84, 0x6b, \ + 0x82, 0x03, 0xe1, 0x84, \ + 0xe0, 0x1a, 0x40, 0x0a, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x69, 0x29, 0x43, 0x86, \ + 0x44, 0x1a, 0xc0, 0x01, \ + 0x40, 0x1a, 0xe5, 0x02, \ + 0x81, 0x11, 0x65, 0x82, \ + 0x64, 0x01, 0x04, 0x20, \ + 0x40, 0x02, 0xc2, 0x00, \ + 0xe5, 0x27, 0x72, 0x84, \ + 0x5d, 0x93, 0xdc, 0x07, \ + 0xc1, 0xc3, 0xa4, 0x0f, \ + 0x04, 0x34, 0x83, 0x31, \ + 0xa3, 0x3f, 0x68, 0x88, \ + 0x42, 0x2a, 0x82, 0x21, \ + 0x80, 0x13, 0xe6, 0x8e, \ + 0x00, 0x14, 0x5d, 0x93, \ + 0xc1, 0xc3, 0xfb, 0x80, \ + 0xfa, 0x04, 0x04, 0x4c, \ + 0x84, 0x7b, 0x06, 0x6b, \ + 0xef, 0xfc, 0x77, 0x48, \ + 0x84, 0x61, 0xc2, 0x67, \ + 0xfa, 0x80, 0x02, 0x64, \ + 0xe6, 0x9e, 0x42, 0x1a, \ + 0xe5, 0x02, 0x44, 0x1a, \ + 0x65, 0x82, 0x40, 0x1a, \ + 0x04, 0x20, 0x81, 0x11, \ + 0xc0, 0x08, 0x64, 0x01, \ + 0x72, 0x84, 0x40, 0x02, \ + 0xe8, 0x07, 0xd9, 0x27, \ + 0x0c, 0x4c, 0xa4, 0x0f, \ + 0xdd, 0x93, 0xf2, 0x04, \ + 0x81, 0x11, 0x21, 0x0b, \ + 0x00, 0x00, 0x84, 0x65, \ + 0x04, 0x0e, 0x6c, 0x04, \ + 0x46, 0x02, 0xff, 0xf0, \ + 0xa1, 0x3b, 0xf8, 0x08, \ + 0x41, 0x84, 0x00, 0x04, \ + 0xc4, 0x04, 0x02, 0x10, \ + 0x02, 0x14, 0xfe, 0x35, \ + 0x00, 0x04, 0x7b, 0x80, \ + 0xd0, 0x17, 0xfa, 0x80, \ + 0x5d, 0x93, 0xa0, 0x1f, \ + 0xfb, 0x80, 0x00, 0x14, \ + 0x7a, 0x80, 0x02, 0x04, \ + 0xa1, 0x1f, 0xd1, 0x07, \ + 0x00, 0x10, 0x5d, 0x93, \ + 0xc1, 0xc3, 0xe4, 0x04, \ + 0xfa, 0x00, 0x00, 0x4c, \ + 0xe5, 0x02, 0x48, 0x2a, \ + 0xe5, 0x82, 0x4a, 0x2a, \ + 0x65, 0x06, 0x40, 0x3a, \ + 0x42, 0x3a, 0x88, 0x2b, \ + 0x8a, 0x3b, 0x65, 0x86, \ + 0xef, 0xc8, 0x61, 0xa8, \ + 0x60, 0x01, 0x04, 0x06, \ + 0x81, 0x31, 0x42, 0x00, \ + 0x42, 0x12, 0x84, 0x03, \ + 0xf9, 0x07, 0x72, 0x8c, \ + 0x60, 0x90, 0x04, 0x14, \ + 0x24, 0x0f, 0xf9, 0x17, \ + 0x85, 0x0b, 0xff, 0x21, \ + 0x1f, 0xfc, 0x41, 0xfe, \ + 0x41, 0xfe, 0xa1, 0x1b, \ + 0x08, 0x4c, 0xdf, 0xfc, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x86, 0x6b, 0x21, 0x1b, \ + 0x81, 0x71, 0x7f, 0x81, \ + 0xa1, 0x2b, 0x8a, 0x05, \ + 0x06, 0x0e, 0x88, 0xc5, \ + 0x40, 0x00, 0x7e, 0x81, \ + 0xe5, 0x0a, 0x48, 0x1a, \ + 0xe5, 0x0e, 0x4a, 0x0a, \ + 0xe5, 0x8e, 0x40, 0x0a, \ + 0x65, 0x8a, 0x46, 0x1a, \ + 0xe1, 0xd4, 0x18, 0x54, \ + 0x02, 0x44, 0x56, 0x07, \ + 0x20, 0xcf, 0xe1, 0x84, \ + 0xe0, 0xfc, 0x00, 0x34, \ + 0xa0, 0x1f, 0x56, 0x57, \ + 0x61, 0x84, 0x04, 0x34, \ + 0xf0, 0x94, 0x40, 0x12, \ + 0x72, 0xb0, 0x42, 0x22, \ + 0x54, 0x78, 0x88, 0x2b, \ + 0x91, 0x0b, 0xef, 0xc8, \ + 0x1f, 0xfc, 0x41, 0xfe, \ + 0x01, 0x55, 0xa1, 0x1b, \ + 0xdf, 0xfc, 0x41, 0xfe, \ + 0xff, 0x71, 0x01, 0x01, \ + 0x8e, 0x0b, 0x18, 0x0e, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x7f, 0xfd, 0x47, 0xfe, \ + 0x86, 0x0d, 0xc0, 0x3f, \ + 0x00, 0x00, 0x06, 0x02, \ + 0x08, 0x3e, 0xa1, 0x1b, \ + 0x80, 0x00, 0x02, 0x06, \ + 0x40, 0x9c, 0x06, 0x34, \ + 0xc1, 0x84, 0x08, 0x14, \ + 0x50, 0x90, 0x46, 0x32, \ + 0x42, 0x3a, 0x81, 0x01, \ + 0x41, 0xfe, 0x40, 0x06, \ + 0x88, 0x1e, 0x9f, 0xfc, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x8a, 0x25, 0xa1, 0x2b, \ + 0x0a, 0x02, 0x06, 0x13, \ + 0x06, 0x14, 0xe0, 0x00, \ + 0x03, 0x0a, 0x7a, 0x80, \ + 0x08, 0x30, 0xf4, 0x7c, \ + 0x81, 0x11, 0x6c, 0x80, \ + 0x7b, 0x90, 0x46, 0x32, \ + 0x00, 0x32, 0x81, 0x41, \ + 0x48, 0x50, 0x6c, 0x7c, \ + 0x0a, 0x06, 0x9c, 0x00, \ + 0x18, 0x44, 0x00, 0x00, \ + 0x0c, 0x04, 0xef, 0x80, \ + 0xa0, 0x33, 0x6c, 0x00, \ + 0x78, 0xb0, 0x58, 0x62, \ + 0x21, 0x4f, 0xa1, 0x5f, \ + 0xe1, 0x84, 0x00, 0x24, \ + 0x06, 0x32, 0x38, 0x5f, \ + 0x27, 0x5f, 0xec, 0x04, \ + 0x42, 0x52, 0x88, 0x03, \ + 0x81, 0x31, 0x72, 0x84, \ + 0xef, 0xc8, 0x4f, 0x48, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x00, 0x4c, \ + 0xa1, 0x2b, 0x00, 0x3b, \ + 0xe5, 0x06, 0x40, 0x3a, \ + 0xe5, 0x86, 0x42, 0x3a, \ + 0x06, 0x20, 0x82, 0x65, \ + 0x83, 0x41, 0x6c, 0x04, \ + 0x81, 0x31, 0x27, 0x4f, \ + 0x5d, 0x58, 0x88, 0x03, \ + 0x42, 0x12, 0x6f, 0xc8, \ + 0xff, 0x21, 0xf2, 0x8c, \ + 0x41, 0xfe, 0x85, 0x0b, \ + 0xa1, 0x1b, 0x1f, 0xfc, \ + 0xdf, 0xfc, 0x41, 0xfe, \ + 0xf2, 0x00, 0x08, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfe, 0x00, 0x04, 0x4c, \ + 0xa3, 0x2d, 0x00, 0x3b, \ + 0xe4, 0x06, 0x40, 0x3a, \ + 0xe4, 0x86, 0x42, 0x3a, \ + 0x08, 0x20, 0x86, 0x75, \ + 0x83, 0x31, 0x6c, 0x04, \ + 0x68, 0x90, 0x08, 0x34, \ + 0x21, 0x1d, 0x81, 0xc1, \ + 0xe1, 0x7c, 0x0a, 0x44, \ + 0x71, 0x90, 0x4c, 0xc2, \ + 0xe8, 0x88, 0x04, 0x34, \ + 0x73, 0x94, 0x58, 0xc2, \ + 0xdc, 0x00, 0x48, 0x60, \ + 0x5c, 0x00, 0x4a, 0xc0, \ + 0x42, 0x52, 0x88, 0x03, \ + 0x06, 0x24, 0x72, 0x84, \ + 0x7e, 0x98, 0xe1, 0x7c, \ + 0x03, 0x4a, 0xef, 0xc4, \ + 0x02, 0x1a, 0xe0, 0x00, \ + 0x00, 0xea, 0xe0, 0x00, \ + 0x42, 0x0a, 0xe0, 0x00, \ + 0x42, 0x1a, 0xe5, 0x80, \ + 0x42, 0x0a, 0xe1, 0x88, \ + 0x80, 0xf5, 0xed, 0x80, \ + 0x7f, 0xfd, 0x41, 0xfe, \ + 0x0c, 0x4c, 0xdf, 0xff, \ + 0xdd, 0x93, 0xf6, 0x00, \ + 0x06, 0x06, 0x7f, 0x21, \ + 0xe0, 0x00, 0x60, 0x01, \ + 0xe0, 0x08, 0x43, 0x4a, \ + 0xe0, 0x08, 0x42, 0x1a, \ + 0xe4, 0x0c, 0x41, 0x6a, \ + 0xe5, 0x80, 0x42, 0x0a, \ + 0xe1, 0x88, 0x42, 0x1a, \ + 0xed, 0x80, 0x42, 0x0a, \ + 0x00, 0x01, 0x00, 0x06, \ + 0x0c, 0x4c, 0xe0, 0x00, \ + 0xdd, 0x93, 0xf6, 0x00, \ + 0x86, 0x0e, 0x83, 0x01, \ + 0x60, 0x14, 0x42, 0x05, \ + 0x46, 0x03, 0x81, 0x01, \ + 0x02, 0x20, 0x60, 0x28, \ + 0x44, 0x03, 0xe6, 0x20, \ + 0x91, 0x21, 0x60, 0x88, \ + 0x5c, 0x08, 0xa1, 0x2b, \ + 0x42, 0x05, 0x9a, 0x00, \ + 0x04, 0x20, 0x78, 0x3c, \ + 0x42, 0x05, 0xec, 0x04, \ + 0x86, 0x0e, 0xf8, 0x40, \ + 0xf0, 0x08, 0x42, 0x05, \ + 0xdd, 0x93, 0xff, 0x45, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x82, 0x6b, 0x1d, 0x74, \ + 0x80, 0xab, 0x04, 0x8b, \ + 0x86, 0x9b, 0x0d, 0x5e, \ + 0x81, 0x11, 0x0b, 0x4e, \ + 0x60, 0x00, 0x44, 0x06, \ + 0x6f, 0x98, 0x8c, 0x0b, \ + 0x81, 0xb1, 0x6f, 0xc8, \ + 0x60, 0x28, 0x4e, 0xb3, \ + 0x1a, 0x0e, 0x83, 0x01, \ + 0x4c, 0x05, 0x91, 0x11, \ + 0x00, 0x80, 0x60, 0x14, \ + 0x50, 0xb3, 0xe6, 0x20, \ + 0x81, 0xa5, 0xe0, 0x88, \ + 0x78, 0x3c, 0x40, 0xb5, \ + 0xec, 0x04, 0x02, 0x10, \ + 0xf8, 0x40, 0x40, 0xb5, \ + 0x40, 0xb5, 0x82, 0xbe, \ + 0xa1, 0x1b, 0xf0, 0x08, \ + 0x10, 0x09, 0xfc, 0xd5, \ + 0x50, 0xb1, 0xe0, 0x2c, \ + 0x50, 0xb1, 0xe1, 0x18, \ + 0xa5, 0x0b, 0xe1, 0x1c, \ + 0x09, 0xae, 0x86, 0xd5, \ + 0xe4, 0x90, 0x14, 0x90, \ + 0x8e, 0x9b, 0x7e, 0x9e, \ + 0x04, 0x06, 0x0b, 0x74, \ + 0x94, 0x1b, 0xe0, 0x10, \ + 0x6f, 0xc8, 0x66, 0xe8, \ + 0x00, 0x70, 0x8e, 0x0b, \ + 0x40, 0x01, 0x64, 0x01, \ + 0x7e, 0x94, 0x92, 0x7b, \ + 0x09, 0xa4, 0x94, 0x1b, \ + 0xe0, 0x10, 0x04, 0x06, \ + 0xef, 0xc8, 0x66, 0x28, \ + 0x04, 0x46, 0x0d, 0x04, \ + 0x65, 0xd8, 0xe0, 0x1c, \ + 0x92, 0x1b, 0x6f, 0xc8, \ + 0x98, 0x71, 0x07, 0x01, \ + 0x60, 0x8c, 0x12, 0x51, \ + 0xe7, 0xe0, 0x04, 0x80, \ + 0x63, 0xc4, 0x10, 0x49, \ + 0x64, 0xe0, 0x06, 0x80, \ + 0x1a, 0x0e, 0xc0, 0x01, \ + 0x4c, 0x05, 0x8c, 0x1b, \ + 0x94, 0x0b, 0x60, 0x14, \ + 0xbc, 0xbe, 0xbe, 0x7e, \ + 0xef, 0xf8, 0x4c, 0x88, \ + 0xb8, 0xf1, 0x21, 0x0b, \ + 0xc0, 0x08, 0x00, 0x06, \ + 0xc0, 0x00, 0x4e, 0x05, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xe0, 0x0c, 0x41, 0xf6, \ + 0x42, 0x05, 0x86, 0x0e, \ + 0x04, 0x05, 0xe0, 0x14, \ + 0x04, 0x25, 0xe0, 0x34, \ + 0xc4, 0x0d, 0xe0, 0x3c, \ + 0x91, 0x80, 0x40, 0x22, \ + 0xd1, 0x88, 0x40, 0x02, \ + 0x02, 0x25, 0xa1, 0x0b, \ + 0x80, 0x00, 0x00, 0x16, \ + 0xe7, 0x30, 0x01, 0x0a, \ + 0xda, 0x00, 0x5c, 0x08, \ + 0x87, 0x01, 0x20, 0x11, \ + 0xdd, 0x93, 0x84, 0x0e, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x63, 0xc0, 0x04, 0x49, \ + 0x0a, 0x06, 0x88, 0x6b, \ + 0xfc, 0x5e, 0xe0, 0x08, \ + 0x85, 0x41, 0x65, 0x4d, \ + 0x42, 0x45, 0x86, 0x4e, \ + 0x4c, 0x55, 0xe0, 0x14, \ + 0x88, 0x35, 0xe0, 0x10, \ + 0x87, 0xc1, 0x4e, 0x50, \ + 0x06, 0x20, 0x06, 0xce, \ + 0xc0, 0x01, 0x64, 0xe1, \ + 0x60, 0x14, 0x42, 0xc5, \ + 0xe7, 0xe0, 0x18, 0x20, \ + 0x63, 0xc4, 0x04, 0x49, \ + 0xe4, 0x20, 0x04, 0xf0, \ + 0x81, 0x71, 0x3e, 0x2e, \ + 0x98, 0x2b, 0x3c, 0x7e, \ + 0xef, 0xf8, 0x47, 0xe8, \ + 0x82, 0x75, 0xa1, 0x0b, \ + 0xda, 0x04, 0xfc, 0x14, \ + 0x04, 0x04, 0xce, 0x17, \ + 0x84, 0x13, 0xe1, 0x04, \ + 0xe0, 0x82, 0x40, 0x1a, \ + 0xe0, 0x10, 0x4c, 0x05, \ + 0x0c, 0x0c, 0x98, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x04, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xed, 0x30, 0x1e, 0xf0, \ + 0x82, 0x7b, 0x0b, 0x0e, \ + 0x8a, 0x6b, 0x35, 0x04, \ + 0x60, 0x28, 0x00, 0xbb, \ + 0x9e, 0x0e, 0x9f, 0x01, \ + 0xe0, 0x14, 0x4e, 0x05, \ + 0x00, 0x26, 0xa1, 0xbb, \ + 0x4e, 0x05, 0x80, 0x00, \ + 0x96, 0x45, 0x80, 0x00, \ + 0xa3, 0xbb, 0x7e, 0x3e, \ + 0x12, 0x20, 0x09, 0x4e, \ + 0x10, 0x86, 0xc5, 0x20, \ + 0x14, 0xf0, 0x40, 0x00, \ + 0x86, 0xf5, 0xc4, 0xb0, \ + 0x65, 0x20, 0x00, 0x20, \ + 0x01, 0x85, 0x91, 0x11, \ + 0xe4, 0xb0, 0x04, 0xf0, \ + 0x70, 0x08, 0x00, 0x35, \ + 0xec, 0x04, 0x02, 0x10, \ + 0xa1, 0x1b, 0xc9, 0x3e, \ + 0x83, 0x35, 0xfe, 0x55, \ + 0xa4, 0x04, 0xa6, 0x14, \ + 0xf9, 0x84, 0x44, 0x02, \ + 0x81, 0x21, 0x21, 0x2b, \ + 0x01, 0x31, 0x80, 0xc5, \ + 0x77, 0x98, 0x96, 0x2b, \ + 0x80, 0x2b, 0xef, 0xc4, \ + 0x10, 0x80, 0x69, 0x2e, \ + 0x90, 0x91, 0xec, 0x04, \ + 0xfa, 0xf5, 0xa1, 0x8b, \ + 0x16, 0x06, 0x7e, 0x24, \ + 0x23, 0x14, 0xe0, 0x04, \ + 0x9f, 0x34, 0x87, 0x81, \ + 0xe0, 0x3c, 0x04, 0x05, \ + 0xe0, 0x34, 0x04, 0x25, \ + 0x13, 0x0e, 0x89, 0xa4, \ + 0x15, 0x2e, 0xa0, 0x13, \ + 0x0f, 0x1e, 0xa4, 0x33, \ + 0x91, 0x3e, 0xc8, 0xa1, \ + 0x69, 0x94, 0x83, 0xb5, \ + 0xe4, 0x60, 0x00, 0xf0, \ + 0x04, 0xf0, 0x1e, 0x8e, \ + 0x4e, 0x85, 0xe4, 0x90, \ + 0x06, 0xf0, 0x60, 0x14, \ + 0x3e, 0x0e, 0xe4, 0x70, \ + 0x3c, 0x0e, 0xff, 0x01, \ + 0x0b, 0x04, 0x85, 0x41, \ + 0x40, 0xb8, 0x8e, 0x1b, \ + 0x92, 0x5b, 0x6f, 0xf8, \ + 0x16, 0xb0, 0x0d, 0x04, \ + 0x92, 0x03, 0xec, 0x04, \ + 0xe0, 0x00, 0x00, 0x02, \ + 0xa1, 0xbb, 0xd9, 0x0e, \ + 0x1e, 0xf0, 0xf8, 0xb5, \ + 0x0c, 0x0c, 0xe5, 0x30, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0xec, 0x60, 0x1e, 0xf0, \ + 0x8f, 0x01, 0x02, 0x7b, \ + 0x88, 0x6b, 0x1e, 0x0e, \ + 0x60, 0x14, 0x4e, 0x05, \ + 0x10, 0x20, 0x81, 0x01, \ + 0x12, 0x30, 0x66, 0xa0, \ + 0x3e, 0x0e, 0xe6, 0x50, \ + 0x0d, 0x75, 0x89, 0xa1, \ + 0x12, 0x25, 0xff, 0xb1, \ + 0xa1, 0x2b, 0xf8, 0x40, \ + 0x0c, 0x45, 0x8a, 0xc5, \ + 0x0a, 0xf0, 0x60, 0x10, \ + 0x01, 0x31, 0xe4, 0x40, \ + 0x46, 0xb8, 0x8e, 0x1b, \ + 0x10, 0x05, 0xe8, 0x00, \ + 0x10, 0x15, 0xf8, 0x3c, \ + 0x8b, 0x24, 0xf8, 0x40, \ + 0xc0, 0x4b, 0x09, 0x34, \ + 0xc8, 0x2d, 0xc2, 0x5b, \ + 0xe0, 0x10, 0x44, 0x1a, \ + 0xe4, 0x14, 0x47, 0x2a, \ + 0xe5, 0x80, 0x42, 0x0a, \ + 0xe1, 0x88, 0x42, 0x1a, \ + 0xed, 0x80, 0x42, 0x0a, \ + 0x90, 0x84, 0x46, 0x32, \ + 0x92, 0x80, 0x40, 0x22, \ + 0x98, 0x3c, 0x50, 0x05, \ + 0x98, 0x40, 0x50, 0x35, \ + 0x50, 0xb5, 0x82, 0x45, \ + 0x83, 0x01, 0x78, 0x3c, \ + 0x60, 0x10, 0x4e, 0x05, \ + 0x50, 0xb5, 0x83, 0x01, \ + 0xbe, 0x0e, 0xf8, 0x40, \ + 0xa1, 0x2b, 0xa4, 0x24, \ + 0x0c, 0x45, 0x8a, 0xc5, \ + 0x0a, 0xf0, 0x60, 0x10, \ + 0x01, 0x31, 0xe4, 0x20, \ + 0x43, 0xb8, 0x8e, 0x1b, \ + 0xfc, 0x04, 0xe8, 0x00, \ + 0x80, 0x2b, 0xfe, 0x14, \ + 0x6f, 0xc4, 0x78, 0x08, \ + 0xa2, 0x24, 0x82, 0x3b, \ + 0xc4, 0x4b, 0xa0, 0x34, \ + 0xc8, 0x1d, 0x46, 0x5b, \ + 0xe0, 0x10, 0x42, 0x1a, \ + 0xe4, 0x14, 0x41, 0x2a, \ + 0xe5, 0x80, 0x42, 0x0a, \ + 0xe1, 0x88, 0x42, 0x1a, \ + 0xed, 0x80, 0x42, 0x0a, \ + 0x90, 0x80, 0x46, 0x32, \ + 0x92, 0x84, 0x40, 0x22, \ + 0x00, 0x04, 0x50, 0x05, \ + 0xc0, 0x00, 0x00, 0x16, \ + 0x80, 0x00, 0x50, 0x35, \ + 0xc0, 0x04, 0x50, 0xb5, \ + 0xc0, 0x00, 0x50, 0xb5, \ + 0xc0, 0x10, 0x4e, 0x05, \ + 0x6c, 0x04, 0x14, 0xa0, \ + 0x88, 0x91, 0x90, 0x81, \ + 0xe6, 0x55, 0xa1, 0xab, \ + 0x00, 0x4a, 0xbe, 0x04, \ + 0x0c, 0x0b, 0xfb, 0xfc, \ + 0x00, 0x00, 0xc0, 0x28, \ + 0x4c, 0x03, 0xc4, 0x04, \ + 0xb0, 0xf1, 0x40, 0x28, \ + 0xf6, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x1c, 0x04, 0x4c, \ + 0x91, 0x81, 0x7f, 0x01, \ + 0x8a, 0x6b, 0x00, 0x4d, \ + 0x00, 0x46, 0x06, 0x8e, \ + 0x42, 0x85, 0x80, 0x00, \ + 0x42, 0x05, 0xe0, 0x14, \ + 0xa0, 0xe5, 0x80, 0x00, \ + 0xe2, 0x00, 0x08, 0x0a, \ + 0xa1, 0x4b, 0xa0, 0x45, \ + 0x80, 0x00, 0x00, 0x56, \ + 0x80, 0x00, 0x42, 0x05, \ + 0x0e, 0x34, 0x9e, 0xc5, \ + 0xae, 0x7d, 0xf6, 0x80, \ + 0x00, 0x70, 0x94, 0x15, \ + 0x02, 0x70, 0x6c, 0x04, \ + 0x01, 0x0a, 0xec, 0x84, \ + 0x84, 0x5b, 0x64, 0x7c, \ + 0x7b, 0x84, 0x42, 0x02, \ + 0x9c, 0x00, 0x4a, 0x30, \ + 0x6c, 0x7c, 0x00, 0x12, \ + 0x06, 0x54, 0x81, 0x91, \ + 0x81, 0x51, 0x68, 0x80, \ + 0x1c, 0x00, 0x4a, 0x20, \ + 0xec, 0x00, 0x18, 0x04, \ + 0x6f, 0x80, 0x04, 0x54, \ + 0x44, 0xc2, 0xa0, 0x13, \ + 0xa1, 0x5f, 0x78, 0x88, \ + 0x60, 0x01, 0x00, 0x06, \ + 0xa4, 0x3f, 0x60, 0x00, \ + 0x6c, 0x04, 0x02, 0x12, \ + 0x04, 0x44, 0x88, 0x0f, \ + 0x02, 0x34, 0x61, 0x84, \ + 0x20, 0x2f, 0xe8, 0x84, \ + 0xe1, 0x7c, 0x06, 0x44, \ + 0x70, 0x88, 0x40, 0x52, \ + 0xe1, 0x84, 0x18, 0x34, \ + 0x72, 0xb0, 0x42, 0x12, \ + 0x66, 0x98, 0x88, 0x2b, \ + 0x2e, 0x83, 0xef, 0xc4, \ + 0x11, 0x0a, 0x80, 0x3b, \ + 0x46, 0x10, 0xe4, 0x7c, \ + 0xd1, 0x11, 0x1c, 0x00, \ + 0x52, 0x00, 0x2e, 0x13, \ + 0x44, 0x82, 0x9c, 0x00, \ + 0x02, 0x22, 0xfb, 0x84, \ + 0x00, 0x94, 0xec, 0x7c, \ + 0x08, 0x14, 0x6f, 0x84, \ + 0x23, 0x3f, 0xec, 0x00, \ + 0xf8, 0x80, 0x40, 0x42, \ + 0xa0, 0x3f, 0x22, 0x23, \ + 0x6c, 0x04, 0x00, 0x22, \ + 0xe8, 0x84, 0x04, 0x94, \ + 0xa1, 0x3f, 0x05, 0xa5, \ + 0xf6, 0x80, 0x00, 0x24, \ + 0x00, 0x8a, 0x8e, 0x03, \ + 0x82, 0x35, 0xe0, 0xa0, \ + 0x00, 0x44, 0x4e, 0x27, \ + 0x00, 0x23, 0xe1, 0x84, \ + 0x44, 0x2a, 0x81, 0x31, \ + 0x83, 0xb5, 0xe0, 0x12, \ + 0x61, 0xe4, 0x00, 0x24, \ + 0x42, 0x32, 0xce, 0x37, \ + 0x00, 0x06, 0x79, 0x80, \ + 0xe0, 0x00, 0x60, 0x00, \ + 0x0a, 0x44, 0x08, 0x0f, \ + 0x06, 0x44, 0xe1, 0x84, \ + 0xce, 0x27, 0x61, 0x7c, \ + 0x18, 0x34, 0x20, 0x5f, \ + 0x40, 0x52, 0xe1, 0x84, \ + 0x88, 0x2b, 0x70, 0x88, \ + 0x6f, 0xc4, 0x62, 0x88, \ + 0xf2, 0x84, 0x42, 0xc2, \ + 0x82, 0x3b, 0x00, 0x2b, \ + 0x9a, 0x3e, 0x98, 0x2e, \ + 0xf2, 0x1c, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x8a, 0x6b, 0x7f, 0x01, \ + 0x93, 0x51, 0x00, 0x4d, \ + 0x00, 0x36, 0x06, 0x5e, \ + 0x42, 0x55, 0x80, 0x00, \ + 0x42, 0x05, 0xe0, 0x14, \ + 0x8a, 0x65, 0x80, 0x00, \ + 0xe2, 0x00, 0x08, 0x0a, \ + 0x80, 0x00, 0x4c, 0x25, \ + 0x80, 0x04, 0x4c, 0x35, \ + 0x0a, 0x34, 0x88, 0x85, \ + 0x18, 0x24, 0xf6, 0x80, \ + 0x18, 0x44, 0xf6, 0x80, \ + 0x98, 0x53, 0x76, 0x80, \ + 0xc0, 0x51, 0x98, 0x53, \ + 0xe0, 0xfc, 0x0b, 0x0a, \ + 0x1a, 0x0e, 0x80, 0xf5, \ + 0x98, 0x0e, 0x83, 0x21, \ + 0xe0, 0x10, 0x42, 0x25, \ + 0x02, 0x44, 0x83, 0x25, \ + 0x88, 0x0b, 0x61, 0x7c, \ + 0xef, 0xc4, 0x68, 0x88, \ + 0x65, 0x00, 0x00, 0x00, \ + 0x42, 0x12, 0x81, 0x21, \ + 0xcf, 0x07, 0x72, 0x88, \ + 0x60, 0xe4, 0x04, 0x14, \ + 0x1a, 0x1e, 0xcf, 0x17, \ + 0x98, 0x0e, 0xa4, 0x0f, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0xed, 0xc0, 0x1e, 0xf0, \ + 0x60, 0x28, 0x06, 0x0b, \ + 0x15, 0x11, 0x82, 0x8b, \ + 0xa2, 0x1e, 0x84, 0x7b, \ + 0x60, 0x14, 0x50, 0x15, \ + 0x86, 0x05, 0xa1, 0x0b, \ + 0xa3, 0x0b, 0x3e, 0x4e, \ + 0xfe, 0x3e, 0xfc, 0x5e, \ + 0x00, 0x70, 0x86, 0x55, \ + 0x91, 0x11, 0x66, 0x20, \ + 0x04, 0xf0, 0x01, 0xc5, \ + 0x00, 0x35, 0xe4, 0xc0, \ + 0x02, 0x10, 0x70, 0x08, \ + 0x00, 0x45, 0xec, 0x04, \ + 0x8a, 0x4e, 0xf8, 0x04, \ + 0xf0, 0x08, 0x44, 0x35, \ + 0xfc, 0xd5, 0xa1, 0x1b, \ + 0x00, 0x70, 0x85, 0x65, \ + 0x81, 0x11, 0x64, 0xc0, \ + 0x6f, 0xc8, 0x45, 0x68, \ + 0x8d, 0xf5, 0xa9, 0x21, \ + 0x66, 0x82, 0x56, 0x0a, \ + 0xe6, 0x20, 0x12, 0x70, \ + 0x14, 0xf0, 0x11, 0x61, \ + 0x83, 0x55, 0xe4, 0xc0, \ + 0xa4, 0x04, 0xa6, 0x14, \ + 0xf9, 0x84, 0x44, 0x02, \ + 0x00, 0x06, 0xa1, 0x2b, \ + 0x02, 0x06, 0x00, 0x00, \ + 0x80, 0xa5, 0x80, 0x00, \ + 0x96, 0x2b, 0x01, 0x31, \ + 0xef, 0xc4, 0x5a, 0x08, \ + 0x0c, 0x60, 0x2a, 0x1e, \ + 0x54, 0x05, 0xec, 0x04, \ + 0x90, 0x91, 0x70, 0x08, \ + 0xfa, 0x75, 0xa1, 0x6b, \ + 0x14, 0x06, 0x3e, 0x14, \ + 0xb3, 0x34, 0xe0, 0x04, \ + 0x02, 0x05, 0xb1, 0x24, \ + 0x02, 0x15, 0xe0, 0x3c, \ + 0x0f, 0x3e, 0xe0, 0x34, \ + 0x0d, 0x2e, 0x81, 0x31, \ + 0x4e, 0x25, 0x81, 0x21, \ + 0x4e, 0x25, 0xe0, 0x34, \ + 0xfc, 0x94, 0xe0, 0x30, \ + 0xab, 0x04, 0x95, 0x0e, \ + 0xc8, 0x91, 0x17, 0x1e, \ + 0x93, 0x0e, 0xa9, 0x14, \ + 0x91, 0x1e, 0xfe, 0x64, \ + 0x24, 0x24, 0x85, 0x35, \ + 0xe4, 0xa0, 0x06, 0xf0, \ + 0x64, 0x60, 0x08, 0xf0, \ + 0x43, 0x58, 0x90, 0x1b, \ + 0x0e, 0x35, 0xe8, 0x00, \ + 0x14, 0xa0, 0x60, 0x34, \ + 0x0e, 0x25, 0xec, 0x04, \ + 0x88, 0x91, 0x60, 0x30, \ + 0x42, 0x1a, 0xc6, 0x1d, \ + 0x41, 0x2a, 0xe0, 0x0c, \ + 0x42, 0x0a, 0xe4, 0x08, \ + 0x42, 0x1a, 0xe5, 0x80, \ + 0x42, 0x0a, 0xe1, 0x88, \ + 0x4e, 0x05, 0xed, 0x80, \ + 0x44, 0x00, 0x40, 0x30, \ + 0x4e, 0x15, 0xdc, 0x00, \ + 0x46, 0x10, 0x40, 0x34, \ + 0xa1, 0xab, 0xdc, 0x00, \ + 0x4c, 0x78, 0xf6, 0xb5, \ + 0x90, 0x1b, 0x68, 0x00, \ + 0x60, 0x10, 0x0c, 0x45, \ + 0xe4, 0x40, 0x0a, 0xf0, \ + 0x90, 0x1b, 0x01, 0x31, \ + 0x6f, 0xfc, 0x73, 0x28, \ + 0x89, 0x04, 0x80, 0x2b, \ + 0x4e, 0x05, 0xca, 0x07, \ + 0x9a, 0x0e, 0xe0, 0x40, \ + 0xe5, 0xc0, 0x1e, 0xf0, \ + 0xf6, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0x97, 0x01, 0x02, 0x6b, \ + 0x86, 0x7b, 0x1a, 0x0e, \ + 0x60, 0x14, 0x4c, 0x05, \ + 0x1c, 0x04, 0x84, 0x9b, \ + 0xe2, 0x14, 0x88, 0x8b, \ + 0x44, 0x0a, 0xb2, 0x03, \ + 0x46, 0x0a, 0xe4, 0x02, \ + 0xe0, 0x04, 0xe4, 0x82, \ + 0xf6, 0x80, 0x08, 0x34, \ + 0xf6, 0x80, 0x0a, 0x24, \ + 0x76, 0x80, 0x08, 0x14, \ + 0x08, 0x04, 0x88, 0x53, \ + 0x88, 0x53, 0x76, 0x80, \ + 0x0b, 0x0a, 0x88, 0x53, \ + 0x80, 0xf5, 0xe0, 0xfc, \ + 0xff, 0xa1, 0x03, 0x01, \ + 0x60, 0x10, 0x4c, 0x05, \ + 0x81, 0xe5, 0xff, 0xb1, \ + 0xef, 0xc4, 0x5b, 0x08, \ + 0x64, 0x01, 0x00, 0x00, \ + 0x81, 0x21, 0x60, 0x00, \ + 0x72, 0x88, 0x56, 0x12, \ + 0x14, 0xb4, 0xed, 0x07, \ + 0xed, 0xb7, 0x60, 0xa8, \ + 0x9e, 0x04, 0xa0, 0xaf, \ + 0x20, 0x04, 0xa2, 0x14, \ + 0x44, 0x9a, 0xa0, 0x93, \ + 0x46, 0x9a, 0xe4, 0x26, \ + 0x0a, 0x24, 0xe4, 0xa6, \ + 0x08, 0x34, 0xf6, 0x80, \ + 0x08, 0x14, 0xf6, 0x80, \ + 0x88, 0x53, 0x76, 0x80, \ + 0x76, 0x80, 0x08, 0x04, \ + 0x88, 0x53, 0x88, 0x53, \ + 0xe0, 0xfc, 0x0b, 0x0a, \ + 0x03, 0x01, 0x80, 0xf5, \ + 0x4c, 0x05, 0xff, 0x11, \ + 0xff, 0x01, 0x60, 0x10, \ + 0x58, 0x78, 0x81, 0xe5, \ + 0x00, 0x00, 0xef, 0xc4, \ + 0x60, 0x00, 0x64, 0x01, \ + 0x42, 0x12, 0x81, 0x21, \ + 0xed, 0x07, 0x72, 0x88, \ + 0x60, 0xa8, 0x04, 0x14, \ + 0xa4, 0x0f, 0xed, 0x17, \ + 0xd4, 0x3b, 0x56, 0x2b, \ + 0xe4, 0x08, 0x43, 0x4a, \ + 0xe0, 0x08, 0x42, 0x1a, \ + 0xe4, 0x0c, 0x41, 0x6a, \ + 0xe5, 0x80, 0x42, 0x0a, \ + 0xe1, 0x88, 0x42, 0x1a, \ + 0xed, 0x80, 0x42, 0x0a, \ + 0x03, 0x01, 0x80, 0xf5, \ + 0x4c, 0x05, 0x81, 0x11, \ + 0x81, 0x01, 0x60, 0x10, \ + 0x00, 0xa3, 0x83, 0x35, \ + 0xff, 0x00, 0x06, 0x0e, \ + 0x72, 0x84, 0x40, 0xb2, \ + 0xe1, 0xf0, 0x02, 0xa4, \ + 0xc8, 0xa7, 0x48, 0x07, \ + 0x81, 0x11, 0x22, 0x0f, \ + 0x64, 0x01, 0x04, 0xa0, \ + 0x42, 0x02, 0xc0, 0x08, \ + 0x40, 0x22, 0x72, 0x84, \ + 0x9c, 0x24, 0xf8, 0x0c, \ + 0xa6, 0x23, 0x9e, 0x34, \ + 0x00, 0x66, 0xa1, 0x2b, \ + 0x02, 0x06, 0x00, 0x00, \ + 0x4c, 0x05, 0x80, 0x00, \ + 0x00, 0x06, 0x00, 0x00, \ + 0x86, 0xc5, 0x80, 0x00, \ + 0x70, 0x80, 0x04, 0x24, \ + 0x46, 0x2a, 0x81, 0x41, \ + 0x44, 0x2a, 0xe4, 0x0a, \ + 0x06, 0x30, 0xe4, 0x8a, \ + 0x48, 0x22, 0xe6, 0x00, \ + 0x0a, 0x34, 0x72, 0x90, \ + 0x18, 0x44, 0xe1, 0xa0, \ + 0xd3, 0x37, 0x60, 0xdc, \ + 0x60, 0xe0, 0x04, 0x44, \ + 0x2a, 0x2f, 0xb8, 0x3f, \ + 0x0a, 0x44, 0x86, 0x03, \ + 0x06, 0x44, 0x61, 0xa4, \ + 0x49, 0x78, 0xe1, 0xa0, \ + 0x42, 0x52, 0x6f, 0xc4, \ + 0x0c, 0x0c, 0xf2, 0x84, \ + 0xdd, 0x93, 0xf6, 0x7c, \ + 0xfa, 0x04, 0x04, 0x4c, \ + 0x76, 0x80, 0x02, 0x34, \ + 0x4c, 0x06, 0x82, 0x7b, \ + 0x99, 0x41, 0x60, 0x00, \ + 0x40, 0x62, 0x1e, 0x4e, \ + 0x4e, 0x45, 0xf1, 0x84, \ + 0xa1, 0x0d, 0x60, 0x14, \ + 0x0c, 0x06, 0x02, 0x15, \ + 0xa1, 0x1d, 0xc0, 0x00, \ + 0x84, 0x04, 0x00, 0x00, \ + 0x9c, 0x00, 0x0c, 0x04, \ + 0x86, 0x1b, 0x04, 0x0b, \ + 0x6f, 0xc4, 0x56, 0xe8, \ + 0x21, 0x1b, 0x8c, 0x2b, \ + 0x00, 0x16, 0x83, 0x67, \ + 0x4e, 0x05, 0xc0, 0x00, \ + 0x41, 0xfe, 0x40, 0x10, \ + 0x40, 0x0a, 0xdf, 0xfc, \ + 0xad, 0x0f, 0xf0, 0x0a, \ + 0xf2, 0x04, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x82, 0xab, 0x1d, 0x14, \ + 0x89, 0x81, 0x09, 0x0e, \ + 0x88, 0x7b, 0x1f, 0x04, \ + 0x83, 0x11, 0x7e, 0x1e, \ + 0x84, 0x9b, 0x0d, 0x5e, \ + 0x57, 0xd6, 0x0b, 0x3e, \ + 0xc0, 0x00, 0x61, 0x99, \ + 0x54, 0x85, 0xaa, 0x8e, \ + 0xfc, 0x0e, 0xe0, 0x14, \ + 0x85, 0xe5, 0x80, 0x16, \ + 0x94, 0x1b, 0x2c, 0x68, \ + 0x92, 0x2b, 0x7e, 0x04, \ + 0x8e, 0x4b, 0x0b, 0x34, \ + 0xbc, 0x0e, 0xbe, 0x6e, \ + 0x8d, 0x54, 0x89, 0x04, \ + 0xe8, 0x00, 0x42, 0xa8, \ + 0xf0, 0x98, 0x40, 0x72, \ + 0xe0, 0xf0, 0x00, 0x19, \ + 0x84, 0xa5, 0xa1, 0x1b, \ + 0xe0, 0xd0, 0x00, 0x09, \ + 0x84, 0x25, 0xa1, 0x0b, \ + 0xe0, 0x88, 0x00, 0x64, \ + 0x00, 0x15, 0x92, 0x03, \ + 0x00, 0x05, 0xe0, 0xc8, \ + 0xc0, 0x1d, 0xe0, 0x10, \ + 0x12, 0x63, 0x82, 0x05, \ + 0x4c, 0x11, 0x81, 0x11, \ + 0xfc, 0x04, 0xe3, 0xa0, \ + 0x81, 0x55, 0x80, 0x16, \ + 0x83, 0x01, 0x12, 0x63, \ + 0xe3, 0xa0, 0x4c, 0x01, \ + 0x6c, 0x04, 0x10, 0x80, \ + 0xa1, 0x8b, 0x82, 0xb1, \ + 0xb8, 0xf1, 0xf4, 0x55, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xf0, 0x1e, 0xf0, \ + 0x82, 0xab, 0x0f, 0x5e, \ + 0x91, 0x2e, 0x8d, 0x3e, \ + 0xe0, 0x28, 0x04, 0x09, \ + 0xc4, 0x09, 0xaf, 0x14, \ + 0x8b, 0x01, 0x25, 0x0b, \ + 0x04, 0x06, 0x2a, 0x0e, \ + 0x54, 0x05, 0xc0, 0x00, \ + 0x9c, 0x55, 0xe0, 0x14, \ + 0x52, 0x42, 0x7c, 0x1e, \ + 0x12, 0x09, 0xf0, 0x84, \ + 0x02, 0x90, 0x60, 0xb0, \ + 0x7e, 0x1e, 0xe4, 0xb0, \ + 0xe4, 0xf0, 0x02, 0x90, \ + 0x02, 0x90, 0x09, 0x1e, \ + 0x17, 0x1e, 0xe4, 0x90, \ + 0x13, 0x1e, 0x81, 0x11, \ + 0x2d, 0xb4, 0x81, 0x11, \ + 0x15, 0x1e, 0xe8, 0x91, \ + 0x89, 0x45, 0x80, 0x2b, \ + 0x8b, 0x2e, 0x97, 0x04, \ + 0x07, 0x45, 0x80, 0x08, \ + 0x00, 0xf0, 0x80, 0x7b, \ + 0x94, 0x1b, 0x64, 0xe0, \ + 0x88, 0x6b, 0x3c, 0x0e, \ + 0x96, 0x3b, 0x08, 0x2b, \ + 0x68, 0x00, 0x46, 0x28, \ + 0xa8, 0x04, 0x90, 0x4b, \ + 0x08, 0x75, 0xa1, 0x0b, \ + 0xdc, 0x00, 0x48, 0x60, \ + 0x00, 0x09, 0x91, 0x04, \ + 0x1d, 0x04, 0xe0, 0x28, \ + 0xc4, 0x07, 0xa5, 0x0b, \ + 0x90, 0xac, 0x40, 0x02, \ + 0x80, 0x24, 0x00, 0x05, \ + 0x0f, 0x14, 0x82, 0xe5, \ + 0xe4, 0xc0, 0x0a, 0xf0, \ + 0x02, 0x03, 0x81, 0x31, \ + 0x00, 0x25, 0x94, 0x1b, \ + 0x8d, 0x04, 0xe0, 0x24, \ + 0xe0, 0x10, 0x00, 0x45, \ + 0xef, 0xfc, 0x5a, 0xa8, \ + 0x13, 0x14, 0x99, 0x04, \ + 0x15, 0x04, 0x8c, 0x4b, \ + 0x93, 0x1e, 0x80, 0x13, \ + 0x95, 0x0e, 0x82, 0x01, \ + 0x82, 0x71, 0x17, 0x04, \ + 0x24, 0x18, 0x80, 0x08, \ + 0xfa, 0x80, 0x00, 0x04, \ + 0xfa, 0x80, 0x0a, 0x74, \ + 0xc3, 0x5d, 0x80, 0x13, \ + 0xfe, 0x04, 0xf0, 0xf5, \ + 0x80, 0x08, 0x8b, 0x24, \ + 0x09, 0x14, 0x82, 0x21, \ + 0xfa, 0x80, 0x00, 0x04, \ + 0xfa, 0x80, 0x10, 0x24, \ + 0x80, 0x13, 0x84, 0x18, \ + 0xec, 0xb5, 0xc3, 0x8d, \ + 0x00, 0x8a, 0x95, 0x04, \ + 0x1e, 0x25, 0xec, 0x08, \ + 0x00, 0x04, 0x40, 0x24, \ + 0x1e, 0x15, 0x9b, 0x80, \ + 0x1e, 0x25, 0xc0, 0x08, \ + 0x1e, 0x15, 0x80, 0x24, \ + 0x44, 0x2a, 0x80, 0x08, \ + 0x00, 0x14, 0x80, 0x02, \ + 0x42, 0x42, 0xe0, 0x88, \ + 0x02, 0x15, 0xf0, 0x80, \ + 0xc4, 0x1d, 0xe0, 0x44, \ + 0x80, 0x00, 0x02, 0x06, \ + 0x42, 0x12, 0x11, 0x24, \ + 0x84, 0x03, 0xd1, 0x88, \ + 0x60, 0xc8, 0x40, 0x15, \ + 0x0c, 0x0c, 0xf8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0x60, 0x14, 0x06, 0x39, \ + 0x86, 0x0e, 0x8d, 0x01, \ + 0xe0, 0x14, 0x42, 0x05, \ + 0xe0, 0x30, 0x04, 0x09, \ + 0xe0, 0x10, 0x04, 0x29, \ + 0x40, 0x3a, 0xa0, 0x43, \ + 0xa4, 0x53, 0x66, 0x92, \ + 0x8a, 0x03, 0xbc, 0x24, \ + 0xe5, 0x04, 0x00, 0x8a, \ + 0x42, 0x05, 0x88, 0x0e, \ + 0xdd, 0x93, 0x80, 0x00, \ + 0xfa, 0x3c, 0x04, 0xcc, \ + 0x63, 0xc0, 0x04, 0x09, \ + 0x86, 0x6b, 0x82, 0x7b, \ + 0xa3, 0x01, 0x62, 0x0d, \ + 0x4e, 0x05, 0x9e, 0x0e, \ + 0x80, 0xb5, 0xe0, 0x14, \ + 0xe0, 0x64, 0x0c, 0x09, \ + 0x88, 0x15, 0xa3, 0x0b, \ + 0x10, 0x50, 0x01, 0x01, \ + 0x18, 0x0e, 0xe4, 0x90, \ + 0x64, 0x01, 0x12, 0x40, \ + 0x03, 0x65, 0xc0, 0x01, \ + 0xe0, 0x04, 0x14, 0x06, \ + 0x0a, 0xf0, 0x24, 0x24, \ + 0x0c, 0x45, 0xe4, 0x60, \ + 0x81, 0x31, 0x60, 0x10, \ + 0x6f, 0xfc, 0x53, 0x08, \ + 0x0d, 0x04, 0x8e, 0x1b, \ + 0xec, 0x04, 0x14, 0xa0, \ + 0x20, 0x0e, 0x88, 0x91, \ + 0x98, 0x14, 0x88, 0x81, \ + 0x4c, 0x05, 0xc2, 0x0d, \ + 0xa1, 0xab, 0xc0, 0x00, \ + 0x0c, 0xcc, 0xfa, 0x95, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0xe0, 0x24, 0x04, 0x49, \ + 0x9d, 0x41, 0x21, 0x4b, \ + 0x42, 0x45, 0x86, 0x4e, \ + 0x80, 0xc5, 0xe0, 0x14, \ + 0x44, 0x41, 0x81, 0x41, \ + 0x2a, 0xb8, 0xe1, 0x18, \ + 0x06, 0x4b, 0xff, 0xf8, \ + 0xa1, 0x4b, 0xe0, 0x28, \ + 0x83, 0x01, 0xfe, 0x05, \ + 0xe1, 0x18, 0x44, 0x01, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x80, 0xbb, 0x1d, 0x94, \ + 0x82, 0x7b, 0x23, 0x04, \ + 0x81, 0x11, 0x25, 0x84, \ + 0x8a, 0xab, 0x7e, 0x3e, \ + 0x84, 0x6b, 0x0b, 0x0e, \ + 0x96, 0x0b, 0x31, 0x16, \ + 0x60, 0x30, 0x52, 0x13, \ + 0x14, 0x3b, 0x8e, 0x1b, \ + 0x72, 0xb8, 0x92, 0x4b, \ + 0x0c, 0x09, 0xef, 0xf8, \ + 0xe5, 0x0d, 0xe0, 0x20, \ + 0x1f, 0x24, 0xa6, 0xd5, \ + 0xa1, 0x04, 0x8e, 0x1b, \ + 0x89, 0x0e, 0xfc, 0x2e, \ + 0xef, 0xf8, 0x70, 0x68, \ + 0xe1, 0x18, 0x0c, 0x09, \ + 0x82, 0x55, 0xa3, 0x0b, \ + 0x8e, 0x1b, 0x16, 0x0b, \ + 0x92, 0x3b, 0x0c, 0x2b, \ + 0xef, 0xf8, 0x67, 0x68, \ + 0x4c, 0x01, 0x81, 0x01, \ + 0x0c, 0x0b, 0xe1, 0x18, \ + 0x8e, 0x1b, 0x60, 0x88, \ + 0x92, 0x3b, 0x0c, 0x2b, \ + 0x82, 0x01, 0x94, 0x4b, \ + 0x60, 0x88, 0x4c, 0x03, \ + 0xe4, 0x60, 0x00, 0xf0, \ + 0x96, 0x0b, 0x3e, 0x0e, \ + 0x89, 0x54, 0xbc, 0x8e, \ + 0xef, 0xfc, 0x68, 0x08, \ + 0xa3, 0x0b, 0xb1, 0x08, \ + 0x03, 0x01, 0x8e, 0x55, \ + 0x52, 0x01, 0x8e, 0x1b, \ + 0x96, 0x0b, 0x60, 0x60, \ + 0x94, 0x3b, 0x0c, 0x2b, \ + 0x6f, 0xf8, 0x79, 0xe8, \ + 0x16, 0x0b, 0x92, 0x4b, \ + 0x0c, 0x2b, 0x8e, 0x1b, \ + 0xfc, 0x44, 0x92, 0x3b, \ + 0x4f, 0x18, 0x89, 0x54, \ + 0x0c, 0x09, 0xef, 0xfc, \ + 0x25, 0x0b, 0xe0, 0x24, \ + 0x9e, 0x0e, 0x9b, 0x01, \ + 0xe0, 0x14, 0x4e, 0x05, \ + 0x12, 0x0b, 0x80, 0xf5, \ + 0xd8, 0x1a, 0xe0, 0x28, \ + 0x86, 0x05, 0xc3, 0x0d, \ + 0x0c, 0x05, 0x83, 0xa5, \ + 0xa1, 0x0b, 0xe0, 0x40, \ + 0x98, 0x14, 0x80, 0x85, \ + 0x84, 0x05, 0xc2, 0x0d, \ + 0xe0, 0x28, 0x12, 0x0b, \ + 0xc3, 0x0d, 0xd8, 0x1a, \ + 0x1c, 0x00, 0x40, 0xb0, \ + 0x9c, 0x00, 0x42, 0x70, \ + 0x1c, 0x00, 0x44, 0x60, \ + 0x9c, 0x00, 0x46, 0x90, \ + 0x8f, 0xfc, 0x77, 0x48, \ + 0x83, 0x01, 0x81, 0x45, \ + 0xe1, 0x18, 0x4c, 0x01, \ + 0xe0, 0x88, 0x0c, 0x0b, \ + 0x83, 0x0d, 0xda, 0x12, \ + 0x1c, 0x00, 0x40, 0xb0, \ + 0x9c, 0x00, 0x42, 0x70, \ + 0x1c, 0x00, 0x44, 0x60, \ + 0x9c, 0x00, 0x46, 0x90, \ + 0x8f, 0xfc, 0x76, 0x28, \ + 0xe1, 0x18, 0x0c, 0x09, \ + 0x84, 0x35, 0xa3, 0x0b, \ + 0xe1, 0x1c, 0x0c, 0x09, \ + 0x82, 0xb5, 0xa1, 0x0b, \ + 0x8e, 0x1b, 0x03, 0x01, \ + 0x61, 0x1c, 0x4c, 0x01, \ + 0x3c, 0x9e, 0x96, 0x0b, \ + 0xfc, 0x34, 0x8c, 0x2b, \ + 0x8b, 0x54, 0x89, 0x44, \ + 0xef, 0xf8, 0x6d, 0xa8, \ + 0xe1, 0x1c, 0x0c, 0x09, \ + 0x84, 0xf5, 0xa3, 0x0b, \ + 0x80, 0x08, 0xfe, 0x04, \ + 0x84, 0x65, 0xa1, 0x0b, \ + 0x81, 0x11, 0x21, 0x01, \ + 0x61, 0x1c, 0x4c, 0x11, \ + 0xe0, 0x10, 0x04, 0x06, \ + 0x4e, 0x05, 0x9e, 0x0e, \ + 0x8b, 0x14, 0xe0, 0x14, \ + 0x6f, 0xc4, 0x4b, 0x28, \ + 0x64, 0x00, 0x00, 0x10, \ + 0x83, 0x01, 0xc0, 0x01, \ + 0xe0, 0x64, 0x52, 0x01, \ + 0x8c, 0x2b, 0x0e, 0x1b, \ + 0x92, 0x3b, 0x0b, 0x44, \ + 0x6f, 0xfc, 0x6f, 0xc8, \ + 0xb8, 0xf1, 0x90, 0x5b, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xed, 0x90, 0x1e, 0xf0, \ + 0x60, 0x84, 0x1e, 0x65, \ + 0x99, 0x5e, 0x84, 0xbb, \ + 0x2d, 0x3e, 0xa5, 0x4e, \ + 0xe4, 0x60, 0x00, 0x60, \ + 0x10, 0x60, 0x23, 0x1e, \ + 0x9a, 0x14, 0xe4, 0x10, \ + 0x0c, 0x05, 0xaf, 0x0e, \ + 0x1e, 0x95, 0xe0, 0x20, \ + 0xa9, 0x1e, 0xe0, 0x88, \ + 0x82, 0x0b, 0x21, 0x0e, \ + 0xef, 0xf8, 0x45, 0xe8, \ + 0xe0, 0x8c, 0x1e, 0xa5, \ + 0xe0, 0x80, 0x1e, 0x75, \ + 0x83, 0xc1, 0x1b, 0x0e, \ + 0xe0, 0x18, 0x0c, 0x05, \ + 0xeb, 0x54, 0x52, 0x05, \ + 0xe0, 0x58, 0x0c, 0x09, \ + 0xb1, 0x0e, 0xd8, 0x34, \ + 0x0c, 0x1b, 0x9a, 0x48, \ + 0x98, 0x28, 0xe0, 0x20, \ + 0xa9, 0xcf, 0x58, 0x58, \ + 0xeb, 0x50, 0x52, 0x35, \ + 0x6d, 0x69, 0x52, 0x21, \ + 0x1a, 0x04, 0xc0, 0x02, \ + 0xe8, 0x94, 0x04, 0xc4, \ + 0x66, 0xb1, 0x52, 0x13, \ + 0x52, 0x41, 0xc0, 0x01, \ + 0xc0, 0x02, 0x6d, 0x6d, \ + 0x6d, 0x71, 0x52, 0x51, \ + 0x49, 0x18, 0xc0, 0x02, \ + 0x17, 0x0e, 0xef, 0xf8, \ + 0x0c, 0x05, 0x94, 0x2b, \ + 0x84, 0xcb, 0x60, 0x18, \ + 0x60, 0x4c, 0x0c, 0x59, \ + 0x58, 0x05, 0x81, 0xa1, \ + 0x0c, 0x09, 0xf0, 0x14, \ + 0x93, 0x7e, 0xe0, 0x48, \ + 0x60, 0x34, 0x0e, 0x19, \ + 0x65, 0x40, 0x0e, 0x90, \ + 0x0c, 0x39, 0xc0, 0x0b, \ + 0x0c, 0x49, 0xe0, 0x90, \ + 0x58, 0x01, 0xe0, 0x94, \ + 0x03, 0x0a, 0x78, 0x18, \ + 0x58, 0x31, 0xe7, 0xf4, \ + 0x81, 0x01, 0x78, 0x10, \ + 0x78, 0x0c, 0x58, 0x41, \ + 0x84, 0x04, 0x00, 0x10, \ + 0x60, 0x48, 0x44, 0x01, \ + 0x58, 0x51, 0x99, 0x01, \ + 0x02, 0xb0, 0x78, 0x14, \ + 0x2b, 0xce, 0xe4, 0xa0, \ + 0xe5, 0x00, 0x06, 0xb0, \ + 0x08, 0xb0, 0x30, 0x06, \ + 0x44, 0xa1, 0xe7, 0x80, \ + 0x00, 0xb0, 0x60, 0x54, \ + 0x44, 0xa1, 0xe4, 0x20, \ + 0x7e, 0x0e, 0xe0, 0x58, \ + 0xfc, 0x4e, 0x96, 0x0b, \ + 0x90, 0x4b, 0x11, 0x4e, \ + 0x84, 0x8b, 0x3e, 0x3e, \ + 0x8c, 0x3b, 0x1d, 0x3e, \ + 0xbc, 0x1e, 0x9f, 0x1e, \ + 0xaf, 0x54, 0xad, 0x14, \ + 0xef, 0xd0, 0x60, 0xe8, \ + 0xa1, 0x0b, 0x15, 0x7e, \ + 0x40, 0x00, 0x02, 0x66, \ + 0xdc, 0x00, 0x54, 0x00, \ + 0x4a, 0x80, 0x04, 0x35, \ + 0x0c, 0x35, 0xdc, 0x00, \ + 0x8e, 0x0b, 0x60, 0x1c, \ + 0x90, 0x4b, 0x29, 0x14, \ + 0xa7, 0x8e, 0xb1, 0x24, \ + 0xef, 0xd8, 0x57, 0x68, \ + 0x8f, 0x11, 0x21, 0x0b, \ + 0x80, 0xab, 0x92, 0x05, \ + 0xb1, 0x84, 0xa7, 0x54, \ + 0x64, 0x01, 0x00, 0x90, \ + 0x04, 0x60, 0x40, 0x09, \ + 0x81, 0x55, 0xe4, 0xa0, \ + 0x10, 0x80, 0x49, 0x44, \ + 0xc1, 0x4e, 0xec, 0x04, \ + 0xfe, 0x75, 0xa1, 0x8b, \ + 0x60, 0x90, 0x1e, 0x85, \ + 0xa8, 0xf5, 0xa1, 0xab, \ + 0x16, 0x09, 0x99, 0x8e, \ + 0xa7, 0x5e, 0xe0, 0xc4, \ + 0x1e, 0x75, 0xa1, 0x0b, \ + 0x0a, 0x50, 0x00, 0x54, \ + 0x1e, 0x85, 0x84, 0x60, \ + 0x96, 0xe5, 0x80, 0x60, \ + 0x14, 0x50, 0x2b, 0x74, \ + 0x31, 0x84, 0xe4, 0x60, \ + 0x64, 0x01, 0x00, 0x90, \ + 0x02, 0x90, 0xc0, 0x09, \ + 0x40, 0x02, 0x65, 0x01, \ + 0x66, 0x01, 0x04, 0x90, \ + 0x0f, 0xae, 0xc0, 0x04, \ + 0x67, 0x01, 0x08, 0x90, \ + 0x0d, 0x7e, 0xc0, 0x06, \ + 0xe6, 0x40, 0x06, 0xb0, \ + 0x89, 0x2e, 0x8b, 0x4e, \ + 0xfc, 0x9e, 0xfe, 0x1e, \ + 0xbc, 0x8e, 0xbe, 0x0e, \ + 0x9f, 0x14, 0xa3, 0x04, \ + 0xa5, 0x44, 0x9d, 0x24, \ + 0x69, 0x18, 0xa1, 0x54, \ + 0x14, 0x5b, 0xef, 0xdc, \ + 0xa1, 0xab, 0x80, 0xab, \ + 0x97, 0x11, 0x8e, 0x45, \ + 0x8f, 0x35, 0x99, 0x84, \ + 0xe0, 0x90, 0x16, 0x09, \ + 0xec, 0xe5, 0xa1, 0x0b, \ + 0xe0, 0x94, 0x16, 0x09, \ + 0xa3, 0x0b, 0xb1, 0x84, \ + 0x2f, 0x04, 0x86, 0x55, \ + 0xad, 0x14, 0x90, 0x4b, \ + 0x00, 0x34, 0x99, 0x24, \ + 0xe4, 0x60, 0x00, 0xb0, \ + 0xa0, 0x11, 0x46, 0x54, \ + 0xfc, 0x2e, 0xfe, 0x9e, \ + 0xbc, 0x1e, 0xbe, 0x0e, \ + 0x97, 0x14, 0xa3, 0x04, \ + 0x4f, 0x78, 0x9b, 0x24, \ + 0x21, 0x0b, 0xef, 0xc8, \ + 0x84, 0xd5, 0x91, 0x11, \ + 0xe0, 0x90, 0x16, 0x09, \ + 0x84, 0x65, 0xa1, 0x0b, \ + 0x60, 0x58, 0x0c, 0x09, \ + 0x65, 0x00, 0x04, 0x90, \ + 0x06, 0x90, 0xc0, 0x02, \ + 0x40, 0x04, 0x66, 0x01, \ + 0x67, 0x01, 0x08, 0x90, \ + 0x5b, 0x98, 0xc0, 0x06, \ + 0x92, 0x1b, 0x6f, 0xf4, \ + 0x93, 0x11, 0x21, 0x0b, \ + 0x80, 0xab, 0x92, 0x05, \ + 0xf1, 0x75, 0xa7, 0x54, \ + 0x8a, 0x7b, 0x0e, 0x2b, \ + 0x67, 0x40, 0x00, 0xb0, \ + 0x65, 0x38, 0x90, 0x1b, \ + 0x8e, 0x3b, 0x6f, 0xd8, \ + 0x80, 0xab, 0x27, 0xc4, \ + 0xa1, 0xab, 0x19, 0x84, \ + 0x99, 0x11, 0x0c, 0x15, \ + 0x02, 0xb0, 0x25, 0x04, \ + 0x2b, 0x34, 0xe4, 0x60, \ + 0x64, 0x01, 0x04, 0x90, \ + 0x8e, 0x4b, 0xc0, 0x09, \ + 0x60, 0x2c, 0x00, 0x59, \ + 0xe6, 0x70, 0x00, 0xc0, \ + 0xa5, 0x0e, 0xfe, 0x0e, \ + 0xfc, 0x9e, 0xb1, 0x04, \ + 0x9f, 0x2e, 0xbe, 0x2e, \ + 0x82, 0x0b, 0x3c, 0x0e, \ + 0x8c, 0x1b, 0x1d, 0x1e, \ + 0x98, 0x6b, 0x2f, 0x24, \ + 0xef, 0xd4, 0x69, 0xa8, \ + 0xa1, 0xab, 0x80, 0xab, \ + 0x02, 0xd6, 0x04, 0x55, \ + 0x2f, 0x7e, 0xc0, 0x00, \ + 0x64, 0xd1, 0x0e, 0x60, \ + 0x15, 0x04, 0xc0, 0x02, \ + 0xa9, 0x14, 0x8e, 0x4b, \ + 0xa5, 0x34, 0xab, 0x24, \ + 0xef, 0xd8, 0x4f, 0x08, \ + 0xa1, 0xab, 0x80, 0xab, \ + 0x9f, 0x11, 0x86, 0x85, \ + 0x94, 0x0b, 0x20, 0xae, \ + 0x1e, 0xf0, 0x22, 0x1e, \ + 0x0c, 0x0c, 0xe5, 0x90, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xe0, 0x90, 0x16, 0x09, \ + 0xa1, 0x0b, 0xa7, 0x54, \ + 0x04, 0x60, 0xce, 0xc5, \ + 0x06, 0x90, 0x64, 0xa0, \ + 0xc0, 0x09, 0x64, 0x00, \ + 0x92, 0x1b, 0x10, 0x0b, \ + 0xef, 0xec, 0x65, 0x68, \ + 0x80, 0xab, 0x27, 0x54, \ + 0x95, 0x11, 0x67, 0xe5, \ + 0x97, 0x7e, 0xa1, 0x24, \ + 0x31, 0x84, 0xaf, 0x74, \ + 0xe4, 0x40, 0x00, 0x20, \ + 0x61, 0x7c, 0x02, 0x04, \ + 0x2b, 0x24, 0x84, 0x4b, \ + 0x02, 0x03, 0xf7, 0x17, \ + 0xe7, 0x50, 0x02, 0xb0, \ + 0x61, 0x14, 0x0a, 0x04, \ + 0x66, 0x30, 0x00, 0x60, \ + 0x1f, 0x64, 0xc0, 0x01, \ + 0xfc, 0x0e, 0x8e, 0x3b, \ + 0xbe, 0x6e, 0xa9, 0x0e, \ + 0xa3, 0x04, 0xbc, 0x8e, \ + 0x73, 0x18, 0x95, 0x5e, \ + 0x80, 0xab, 0xef, 0xf0, \ + 0x54, 0x75, 0xa1, 0xab, \ + 0xc0, 0x00, 0x03, 0x06, \ + 0x8e, 0x4b, 0x17, 0x14, \ + 0xe3, 0x54, 0x16, 0x09, \ + 0x8f, 0x1e, 0x95, 0x24, \ + 0xab, 0x34, 0xa9, 0x14, \ + 0x8b, 0x9e, 0x8d, 0x1e, \ + 0xad, 0x94, 0x89, 0x6e, \ + 0xfc, 0x2e, 0xfe, 0x8e, \ + 0x02, 0x90, 0x21, 0x24, \ + 0x9b, 0x64, 0xe4, 0x60, \ + 0x3c, 0x0e, 0xbe, 0x2e, \ + 0xa3, 0x04, 0x8c, 0x5b, \ + 0x6e, 0x58, 0x9d, 0x24, \ + 0x80, 0xab, 0xef, 0xd4, \ + 0x4c, 0xb5, 0xa1, 0xab, \ + 0xc0, 0x00, 0x03, 0x56, \ + 0x00, 0xb0, 0x17, 0x74, \ + 0x02, 0x90, 0xe7, 0x60, \ + 0x8c, 0x2b, 0x64, 0x40, \ + 0x29, 0x44, 0xab, 0x34, \ + 0x7b, 0xd8, 0x8e, 0x5b, \ + 0x19, 0x84, 0xef, 0xd4, \ + 0xa1, 0xab, 0x80, 0xab, \ + 0x03, 0xd6, 0x66, 0x15, \ + 0x27, 0x24, 0xc0, 0x00, \ + 0xe4, 0x70, 0x02, 0x90, \ + 0x00, 0xb0, 0x12, 0x8b, \ + 0x8e, 0x3b, 0xe7, 0x70, \ + 0x12, 0x20, 0x2b, 0x24, \ + 0xc0, 0x03, 0x64, 0x91, \ + 0x6f, 0xe8, 0x6f, 0xa8, \ + 0x80, 0xab, 0x92, 0x4b, \ + 0x31, 0x18, 0xa1, 0xab, \ + 0x03, 0xe6, 0x5f, 0xfc, \ + 0x2b, 0x14, 0xc0, 0x00, \ + 0x0e, 0x2b, 0x90, 0x0b, \ + 0x77, 0xf8, 0x92, 0x3b, \ + 0x80, 0xab, 0xef, 0xf0, \ + 0x30, 0x58, 0xa1, 0xab, \ + 0x03, 0xf6, 0x5f, 0xfc, \ + 0xa9, 0x24, 0xc0, 0x00, \ + 0xe1, 0xb0, 0x16, 0x3b, \ + 0xe0, 0x10, 0x10, 0x45, \ + 0xe3, 0x70, 0x16, 0x19, \ + 0x89, 0x2e, 0x8b, 0x9e, \ + 0x93, 0x54, 0xa5, 0x24, \ + 0x2f, 0x24, 0xfe, 0x2e, \ + 0xe4, 0x40, 0x00, 0x50, \ + 0xab, 0x24, 0xfc, 0x2e, \ + 0x8c, 0x2b, 0x3e, 0x2e, \ + 0x91, 0x04, 0xbc, 0x0e, \ + 0xef, 0xe8, 0x7a, 0xe8, \ + 0xa1, 0xab, 0x80, 0xab, \ + 0x5f, 0xfc, 0x2e, 0x88, \ + 0xc0, 0x00, 0x42, 0x06, \ + 0x92, 0x3b, 0x27, 0x04, \ + 0xe3, 0xd0, 0x16, 0x19, \ + 0x23, 0x04, 0xab, 0x24, \ + 0x64, 0x51, 0x08, 0x00, \ + 0x5c, 0x98, 0xc0, 0x04, \ + 0x00, 0xab, 0xef, 0xec, \ + 0xe0, 0x00, 0x42, 0x16, \ + 0xc1, 0xc3, 0xdb, 0x65, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x82, 0xab, 0x19, 0x14, \ + 0x84, 0x9b, 0x15, 0x74, \ + 0x86, 0x6b, 0x3e, 0x5e, \ + 0x88, 0xbb, 0x7c, 0x1e, \ + 0x60, 0x40, 0x12, 0x89, \ + 0x47, 0xf8, 0x94, 0x1b, \ + 0x8e, 0x3b, 0x68, 0x00, \ + 0xe0, 0x44, 0x12, 0x09, \ + 0x82, 0xe5, 0xa6, 0x0d, \ + 0x86, 0x65, 0xa9, 0x0b, \ + 0x8c, 0x25, 0xad, 0x0b, \ + 0xaf, 0x0b, 0x7c, 0xb4, \ + 0x10, 0x0b, 0x9a, 0x15, \ + 0x4c, 0x18, 0x8e, 0x1b, \ + 0xa3, 0x0d, 0xef, 0xc8, \ + 0x8d, 0x15, 0x8c, 0x25, \ + 0x8e, 0x45, 0xa5, 0x0b, \ + 0xa7, 0x0b, 0x7c, 0xb4, \ + 0x02, 0x60, 0x96, 0x95, \ + 0x90, 0x0b, 0x64, 0x30, \ + 0x6f, 0xc8, 0x7f, 0x68, \ + 0xa3, 0x0d, 0x8e, 0x2b, \ + 0x8b, 0x35, 0x82, 0x25, \ + 0x8e, 0x1b, 0x10, 0x0b, \ + 0xef, 0xc8, 0x4a, 0xc8, \ + 0xa3, 0x0d, 0x7c, 0xb4, \ + 0x3e, 0x64, 0x92, 0x95, \ + 0x8e, 0x1b, 0x90, 0x0b, \ + 0x6f, 0xc8, 0x4b, 0xc8, \ + 0x10, 0x0b, 0x8c, 0x2b, \ + 0x55, 0x78, 0x8e, 0x1b, \ + 0x8c, 0x2b, 0x6f, 0xc8, \ + 0x92, 0x1b, 0x14, 0x0b, \ + 0x6f, 0xc8, 0x59, 0x28, \ + 0x87, 0xb5, 0x8c, 0x2b, \ + 0x64, 0x30, 0x02, 0x60, \ + 0x7d, 0x58, 0x90, 0x0b, \ + 0x8e, 0x2b, 0x6f, 0xc8, \ + 0xa3, 0x0d, 0x7c, 0xb4, \ + 0x3e, 0x64, 0x8c, 0x55, \ + 0x8e, 0x1b, 0x90, 0x0b, \ + 0x6f, 0xc8, 0x4a, 0x28, \ + 0x10, 0x0b, 0x8c, 0x2b, \ + 0x56, 0x38, 0x8e, 0x1b, \ + 0x8c, 0x2b, 0x6f, 0xc8, \ + 0x0e, 0x09, 0x85, 0x65, \ + 0xc0, 0x05, 0x6f, 0xd1, \ + 0x84, 0xe5, 0xa1, 0x0b, \ + 0x67, 0xfd, 0x0e, 0x15, \ + 0x97, 0x04, 0xc0, 0x01, \ + 0x6f, 0xc9, 0x0e, 0x29, \ + 0x0e, 0x39, 0xc0, 0x05, \ + 0xc0, 0x05, 0x6f, 0xcd, \ + 0x60, 0x09, 0x0e, 0x4b, \ + 0xc2, 0x1e, 0xc0, 0x03, \ + 0xe0, 0x10, 0x56, 0x15, \ + 0xe0, 0x58, 0x56, 0x21, \ + 0xe0, 0x5c, 0x56, 0x31, \ + 0xe0, 0x30, 0x56, 0x43, \ + 0x6f, 0xc8, 0x45, 0x68, \ + 0x7c, 0xb4, 0x8e, 0x0b, \ + 0x80, 0xb5, 0xa3, 0x0d, \ + 0x8e, 0x0b, 0x3e, 0x14, \ + 0xef, 0xc8, 0x4f, 0x08, \ + 0x98, 0xf1, 0x01, 0x01, \ + 0x40, 0x26, 0x2c, 0x0e, \ + 0x2e, 0x0e, 0xe0, 0x00, \ + 0x0c, 0x0c, 0x81, 0x01, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x4c, \ + 0x84, 0x6b, 0x02, 0x8b, \ + 0x86, 0x7b, 0x60, 0x28, \ + 0xa0, 0x08, 0xa2, 0x18, \ + 0xef, 0xf4, 0x5e, 0x88, \ + 0x60, 0x58, 0x10, 0x19, \ + 0xa2, 0x04, 0x80, 0x2b, \ + 0x62, 0x98, 0xe0, 0x34, \ + 0xc8, 0x17, 0x6f, 0xf4, \ + 0xe0, 0x44, 0x0c, 0x19, \ + 0x9c, 0x55, 0xa5, 0x1b, \ + 0x4e, 0x05, 0xe0, 0x14, \ + 0xc0, 0x01, 0x67, 0xdd, \ + 0xe0, 0x18, 0x0c, 0x0b, \ + 0x98, 0x34, 0x9a, 0x24, \ + 0x67, 0xe1, 0x4e, 0x15, \ + 0x0c, 0x19, 0xc0, 0x01, \ + 0x4e, 0x03, 0xe0, 0x38, \ + 0xc0, 0x02, 0x6f, 0xe1, \ + 0xe0, 0x54, 0x0c, 0x09, \ + 0x67, 0xe9, 0x4e, 0x25, \ + 0x4e, 0x35, 0xc0, 0x01, \ + 0xc0, 0x01, 0x67, 0xed, \ + 0x6f, 0xc9, 0x4e, 0x11, \ + 0xa1, 0x0b, 0x40, 0x05, \ + 0x60, 0x3c, 0x0c, 0x19, \ + 0x0c, 0x29, 0x81, 0x01, \ + 0x0c, 0x39, 0xe0, 0x40, \ + 0x4e, 0x11, 0xe0, 0x48, \ + 0xc0, 0x05, 0x6f, 0xcd, \ + 0x6f, 0xd1, 0x4e, 0x01, \ + 0x4e, 0x21, 0xc0, 0x05, \ + 0xc0, 0x05, 0x6f, 0xd5, \ + 0x6f, 0xd9, 0x4e, 0x31, \ + 0xb0, 0xa5, 0xc0, 0x05, \ + 0x60, 0x51, 0x0c, 0x09, \ + 0x02, 0x60, 0x40, 0x01, \ + 0xc0, 0x03, 0x64, 0x91, \ + 0xe0, 0x88, 0x04, 0x04, \ + 0x7c, 0x08, 0x02, 0x35, \ + 0xf0, 0x88, 0x48, 0x12, \ + 0x4e, 0x35, 0x84, 0x63, \ + 0x40, 0x01, 0x67, 0xf9, \ + 0xe0, 0x84, 0x06, 0x04, \ + 0x60, 0x60, 0x08, 0x45, \ + 0x06, 0x5b, 0x82, 0x33, \ + 0x80, 0x13, 0x61, 0x20, \ + 0x68, 0x01, 0x4e, 0x45, \ + 0x83, 0x41, 0x40, 0x01, \ + 0x60, 0x09, 0x4e, 0x53, \ + 0x0c, 0x25, 0xc0, 0x03, \ + 0x06, 0x3b, 0xe1, 0xbc, \ + 0x4e, 0x41, 0xe1, 0x50, \ + 0xc0, 0x05, 0x6f, 0xd1, \ + 0x67, 0xfd, 0x4e, 0x25, \ + 0x4e, 0x33, 0xc0, 0x01, \ + 0xc0, 0x03, 0x60, 0x0d, \ + 0xe3, 0x00, 0x02, 0x09, \ + 0x60, 0x21, 0x4e, 0x01, \ + 0x02, 0x09, 0xc0, 0x06, \ + 0x4e, 0x01, 0xe3, 0x30, \ + 0xc0, 0x06, 0x60, 0x25, \ + 0xe3, 0x60, 0x02, 0x09, \ + 0x60, 0x29, 0x4e, 0x01, \ + 0x02, 0x09, 0xc0, 0x06, \ + 0x4e, 0x01, 0xe3, 0x90, \ + 0xc0, 0x06, 0x60, 0x2d, \ + 0x0c, 0x29, 0x91, 0x45, \ + 0x06, 0x70, 0x60, 0x40, \ + 0xe0, 0x14, 0xe4, 0x70, \ + 0xe0, 0x10, 0x4e, 0x05, \ + 0x08, 0x24, 0x1a, 0x04, \ + 0x4e, 0x15, 0xe0, 0x88, \ + 0x86, 0x43, 0x60, 0x14, \ + 0x60, 0x3c, 0x0e, 0x59, \ + 0xe0, 0x84, 0x10, 0x24, \ + 0x8c, 0x0b, 0x10, 0x0e, \ + 0x70, 0x15, 0x00, 0x15, \ + 0x86, 0x83, 0x40, 0x02, \ + 0x60, 0x54, 0x0c, 0xc9, \ + 0x44, 0x5a, 0x84, 0x33, \ + 0x48, 0x15, 0xe6, 0x8a, \ + 0x00, 0x1b, 0xe1, 0x00, \ + 0xc0, 0x04, 0x78, 0x11, \ + 0x78, 0x19, 0x00, 0x49, \ + 0x50, 0x13, 0xc0, 0x08, \ + 0x42, 0x52, 0x64, 0x00, \ + 0x46, 0x41, 0xfb, 0xb0, \ + 0x58, 0x72, 0x6a, 0x00, \ + 0x00, 0x49, 0xf0, 0x88, \ + 0xc0, 0x08, 0x78, 0x15, \ + 0xec, 0x00, 0x46, 0x11, \ + 0xeb, 0x00, 0x46, 0x41, \ + 0x78, 0x11, 0x00, 0x49, \ + 0x46, 0x41, 0xc0, 0x08, \ + 0x08, 0x24, 0x6d, 0x00, \ + 0x00, 0x59, 0xe0, 0x84, \ + 0x40, 0x08, 0x78, 0x09, \ + 0x0e, 0x43, 0xc4, 0x27, \ + 0x04, 0x70, 0x84, 0x73, \ + 0x40, 0x07, 0x67, 0x71, \ + 0x67, 0x71, 0x08, 0x40, \ + 0x46, 0x51, 0xc0, 0x11, \ + 0x06, 0xc0, 0x6e, 0x00, \ + 0xc0, 0x13, 0x67, 0x70, \ + 0x81, 0x51, 0x07, 0x65, \ + 0x7c, 0x14, 0x00, 0xc9, \ + 0x64, 0x90, 0x10, 0x60, \ + 0x0e, 0xc4, 0xc0, 0x03, \ + 0x10, 0x95, 0xe0, 0x88, \ + 0x54, 0x82, 0x7c, 0x1c, \ + 0x8c, 0x73, 0xf0, 0x9c, \ + 0x78, 0x01, 0x44, 0x95, \ + 0x12, 0xc4, 0x40, 0x02, \ + 0x14, 0xa5, 0xe0, 0x84, \ + 0x90, 0x93, 0x60, 0x60, \ + 0x61, 0x20, 0x12, 0xbb, \ + 0x44, 0xa5, 0x98, 0x83, \ + 0x48, 0xb3, 0xe4, 0x00, \ + 0xc0, 0x02, 0x78, 0x01, \ + 0xe1, 0xbc, 0x0e, 0x75, \ + 0xe1, 0x50, 0x12, 0x9b, \ + 0x4e, 0x32, 0x49, 0x7e, \ + 0x51, 0x9c, 0xf0, 0x94, \ + 0x10, 0xc9, 0x82, 0x51, \ + 0x9c, 0xc6, 0xe3, 0x00, \ + 0xe3, 0x30, 0x10, 0xc9, \ + 0xe4, 0x00, 0x4e, 0xc1, \ + 0xe3, 0x60, 0x10, 0xc9, \ + 0xe8, 0x00, 0x4e, 0xc1, \ + 0xe3, 0x90, 0x10, 0xc9, \ + 0xec, 0x00, 0x4e, 0xc1, \ + 0xf2, 0x75, 0x83, 0x5d, \ + 0xf2, 0x7c, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xb0, 0x1e, 0xf0, \ + 0x84, 0xbb, 0x0d, 0x1e, \ + 0x80, 0x7b, 0x33, 0x14, \ + 0x81, 0xa1, 0x7e, 0x5e, \ + 0x88, 0x8b, 0x2e, 0x04, \ + 0x86, 0x6b, 0x0f, 0x1e, \ + 0xab, 0x94, 0xaf, 0x14, \ + 0x91, 0x1e, 0x93, 0xae, \ + 0x89, 0x1e, 0xa9, 0x14, \ + 0xef, 0xf4, 0x4b, 0xc8, \ + 0xe0, 0x94, 0x16, 0x39, \ + 0x9a, 0x2a, 0x98, 0x1a, \ + 0x52, 0x13, 0xa5, 0x3b, \ + 0x52, 0x23, 0xe0, 0x10, \ + 0x8a, 0x75, 0xe0, 0x14, \ + 0x00, 0x0e, 0x0b, 0x0e, \ + 0xc0, 0x07, 0x74, 0x01, \ + 0x26, 0x0e, 0xbe, 0x7e, \ + 0x7c, 0x0e, 0x83, 0x01, \ + 0xa9, 0xac, 0x85, 0x01, \ + 0x02, 0x04, 0x3e, 0xa4, \ + 0x31, 0x64, 0xfa, 0x80, \ + 0x89, 0x24, 0x81, 0x81, \ + 0x94, 0x0b, 0x11, 0x34, \ + 0xe8, 0x00, 0x4d, 0x88, \ + 0x16, 0x19, 0xad, 0x74, \ + 0x94, 0x0b, 0x60, 0x94, \ + 0x8c, 0x3b, 0x12, 0x2b, \ + 0xe8, 0x00, 0x49, 0xe8, \ + 0x94, 0x0b, 0x0d, 0x64, \ + 0x60, 0x90, 0x16, 0x29, \ + 0x16, 0x19, 0x8e, 0x4b, \ + 0x4e, 0xd8, 0xe0, 0x94, \ + 0x8c, 0x3b, 0x68, 0x00, \ + 0x8b, 0x04, 0x91, 0x14, \ + 0x8f, 0x65, 0xfc, 0x94, \ + 0x16, 0x19, 0xa7, 0x34, \ + 0xfe, 0xa4, 0xe0, 0x90, \ + 0xc5, 0x1d, 0xcc, 0x28, \ + 0xce, 0x28, 0x80, 0x95, \ + 0x8e, 0x45, 0xc5, 0x1d, \ + 0x40, 0x32, 0x0b, 0x0e, \ + 0x00, 0x19, 0xf0, 0x84, \ + 0x04, 0xf0, 0x60, 0x20, \ + 0x00, 0x09, 0xe4, 0x90, \ + 0x5e, 0x11, 0xe1, 0x20, \ + 0x02, 0xf0, 0x60, 0xa0, \ + 0x5e, 0x01, 0xe4, 0xa0, \ + 0x8c, 0x0b, 0x60, 0xa4, \ + 0xef, 0xf0, 0x45, 0x68, \ + 0x80, 0xe5, 0xa1, 0x0b, \ + 0x0d, 0x64, 0x87, 0x91, \ + 0x91, 0x14, 0x80, 0x8b, \ + 0x8b, 0x25, 0x8b, 0x04, \ + 0x89, 0x24, 0x93, 0x04, \ + 0x60, 0x84, 0x02, 0x04, \ + 0x04, 0x29, 0xc4, 0x07, \ + 0x8c, 0x13, 0x60, 0x90, \ + 0x63, 0x48, 0x02, 0x0b, \ + 0x0c, 0x15, 0x80, 0x63, \ + 0x94, 0x6b, 0x60, 0x24, \ + 0x52, 0x03, 0xa1, 0x2b, \ + 0x81, 0x01, 0x60, 0x28, \ + 0x86, 0x45, 0xa6, 0x1e, \ + 0x64, 0xa0, 0x02, 0xf0, \ + 0xe4, 0x90, 0x04, 0xf0, \ + 0x6f, 0xf0, 0x43, 0x48, \ + 0xa1, 0x0b, 0x90, 0x0b, \ + 0x7b, 0xe5, 0x82, 0x05, \ + 0x05, 0x91, 0x8b, 0x91, \ + 0x8d, 0x64, 0xfb, 0x81, \ + 0x85, 0xe5, 0x91, 0x14, \ + 0x89, 0x14, 0x93, 0x04, \ + 0x02, 0x15, 0xc4, 0x07, \ + 0x90, 0x03, 0x60, 0x18, \ + 0xe0, 0x24, 0x00, 0x05, \ + 0x3e, 0x7e, 0x82, 0x03, \ + 0xe4, 0xa0, 0x02, 0xf0, \ + 0x04, 0xf0, 0x66, 0x0e, \ + 0x41, 0x98, 0xe4, 0x90, \ + 0x8c, 0x0b, 0x6f, 0xf0, \ + 0x8b, 0x11, 0x21, 0x0b, \ + 0x84, 0x75, 0xfc, 0x1e, \ + 0x8c, 0x1b, 0x13, 0x04, \ + 0x04, 0xf0, 0x25, 0x64, \ + 0xc2, 0x07, 0xe4, 0x90, \ + 0x02, 0xf0, 0x02, 0x03, \ + 0x00, 0x0b, 0xe4, 0xa0, \ + 0x52, 0x03, 0xe0, 0x18, \ + 0x8c, 0x0b, 0x60, 0x3c, \ + 0xef, 0xf0, 0x40, 0x48, \ + 0x84, 0x85, 0xa1, 0x0b, \ + 0x80, 0x8b, 0x0d, 0x64, \ + 0x8b, 0x91, 0x11, 0x14, \ + 0x02, 0x1b, 0x8b, 0x04, \ + 0x66, 0xf8, 0xe1, 0xd0, \ + 0x4c, 0x03, 0xef, 0xd4, \ + 0x0f, 0x04, 0xe0, 0x18, \ + 0x80, 0x8e, 0xd8, 0xf1, \ + 0x90, 0x0b, 0x02, 0x9e, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x93, 0x04, 0xdd, 0x93, \ + 0x80, 0x63, 0xc2, 0x07, \ + 0xe0, 0x18, 0x0c, 0x0b, \ + 0xe0, 0x38, 0x52, 0x03, \ + 0xe0, 0x94, 0x16, 0x09, \ + 0xc1, 0xc3, 0xeb, 0x45, \ + 0xfa, 0x04, 0x04, 0x4c, \ + 0x84, 0x3b, 0x06, 0x6b, \ + 0x44, 0x06, 0x02, 0x7b, \ + 0x0c, 0x0b, 0xe0, 0x00, \ + 0x41, 0x78, 0x86, 0x1b, \ + 0xa6, 0x7d, 0xef, 0xc0, \ + 0xa9, 0x7b, 0x82, 0x25, \ + 0xad, 0x7b, 0x80, 0xa5, \ + 0xaf, 0x7b, 0x82, 0x25, \ + 0x01, 0x85, 0x86, 0x95, \ + 0xa5, 0x7b, 0x81, 0x01, \ + 0xa7, 0x7b, 0x82, 0x85, \ + 0x81, 0x01, 0x84, 0xd5, \ + 0x4c, 0x03, 0xda, 0x0e, \ + 0x02, 0x06, 0x60, 0x28, \ + 0x4c, 0x13, 0xe1, 0x00, \ + 0x81, 0xd5, 0xe0, 0x38, \ + 0xda, 0x0e, 0x81, 0x01, \ + 0xe0, 0x28, 0x4c, 0x03, \ + 0x60, 0x30, 0x4c, 0x03, \ + 0xe1, 0x00, 0x00, 0x06, \ + 0xe0, 0x38, 0x4c, 0x03, \ + 0xe0, 0x3c, 0x4c, 0x03, \ + 0xf2, 0x04, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x84, 0x7b, 0x06, 0x6b, \ + 0x45, 0x86, 0x02, 0x8b, \ + 0x0c, 0x0b, 0xe0, 0x0c, \ + 0x7e, 0x58, 0x8e, 0x1b, \ + 0xad, 0x8b, 0xef, 0xbc, \ + 0xe0, 0x0c, 0x10, 0x1a, \ + 0xed, 0x80, 0x42, 0x0a, \ + 0x80, 0x00, 0x00, 0x06, \ + 0x80, 0x90, 0x4c, 0x01, \ + 0xa5, 0x8b, 0x82, 0xe5, \ + 0x01, 0x21, 0x82, 0xb5, \ + 0xe4, 0xa0, 0x00, 0x60, \ + 0x60, 0x90, 0x4c, 0x21, \ + 0xe4, 0xd0, 0x02, 0x70, \ + 0x0c, 0x0c, 0x99, 0x21, \ + 0x3c, 0xd8, 0xf2, 0x0c, \ + 0x0c, 0x0c, 0xff, 0xbc, \ + 0xdd, 0x93, 0xf2, 0x0c, \ + 0xf8, 0x3c, 0x04, 0x4c, \ + 0x8c, 0x35, 0xa5, 0x1b, \ + 0x6f, 0xd1, 0x06, 0x09, \ + 0x06, 0x19, 0xc0, 0x05, \ + 0xc0, 0x05, 0x6f, 0xd9, \ + 0x6f, 0xc9, 0x06, 0x29, \ + 0x06, 0x59, 0xc0, 0x05, \ + 0x40, 0x05, 0x6f, 0xcd, \ + 0x48, 0x11, 0xa1, 0x0b, \ + 0x48, 0x21, 0xe0, 0x34, \ + 0x48, 0x51, 0xe0, 0x20, \ + 0x48, 0x01, 0xe0, 0x24, \ + 0x9a, 0xc5, 0xe0, 0x30, \ + 0x67, 0xf9, 0x06, 0x05, \ + 0x06, 0x1b, 0xc0, 0x01, \ + 0xc0, 0x03, 0x60, 0x09, \ + 0x60, 0x2d, 0x06, 0x29, \ + 0x48, 0x05, 0xc0, 0x06, \ + 0x48, 0x13, 0xe0, 0x10, \ + 0x06, 0x05, 0xe0, 0xe0, \ + 0xc0, 0x01, 0x68, 0x01, \ + 0x60, 0x0d, 0x06, 0x1b, \ + 0x48, 0x21, 0xc0, 0x03, \ + 0x48, 0x05, 0xe2, 0x80, \ + 0x48, 0x13, 0xe0, 0x40, \ + 0x8b, 0x45, 0xe1, 0x10, \ + 0x60, 0x3c, 0x06, 0x59, \ + 0xf0, 0x88, 0x40, 0x32, \ + 0x6e, 0x70, 0x00, 0x19, \ + 0xe6, 0x80, 0x18, 0x40, \ + 0x81, 0x71, 0x01, 0x61, \ + 0xe0, 0x34, 0x48, 0x11, \ + 0xea, 0x70, 0x00, 0x19, \ + 0xe0, 0x20, 0x48, 0x11, \ + 0xeb, 0x70, 0x00, 0x19, \ + 0xe0, 0x24, 0x48, 0x11, \ + 0xe6, 0x8a, 0x42, 0x5a, \ + 0xec, 0x70, 0x00, 0x09, \ + 0x60, 0x84, 0x04, 0x14, \ + 0xe0, 0x88, 0x0a, 0x14, \ + 0x10, 0x02, 0x86, 0x23, \ + 0x00, 0x10, 0x6c, 0x30, \ + 0xc0, 0x16, 0x67, 0x70, \ + 0x67, 0x71, 0x02, 0x20, \ + 0x04, 0x50, 0x40, 0x0f, \ + 0xc0, 0x0b, 0x67, 0x71, \ + 0x60, 0x30, 0x48, 0x81, \ + 0xe5, 0x00, 0x0a, 0x40, \ + 0x02, 0x9b, 0x85, 0x35, \ + 0x50, 0x32, 0x7c, 0x18, \ + 0x10, 0x85, 0xf0, 0x88, \ + 0x40, 0x04, 0x78, 0x01, \ + 0xf0, 0x98, 0x54, 0xc2, \ + 0x78, 0x60, 0x54, 0x93, \ + 0xf0, 0x98, 0x52, 0x12, \ + 0x78, 0x30, 0x4a, 0x85, \ + 0x06, 0x85, 0x84, 0x61, \ + 0x88, 0x21, 0x7c, 0x08, \ + 0xe4, 0x00, 0x12, 0x9b, \ + 0x50, 0x32, 0x55, 0x8e, \ + 0x54, 0x93, 0xf0, 0x9c, \ + 0x52, 0xc2, 0x78, 0x30, \ + 0x10, 0x89, 0xf0, 0x9c, \ + 0x82, 0x71, 0x7c, 0x00, \ + 0x08, 0x89, 0xa4, 0x86, \ + 0x10, 0x84, 0xe0, 0x30, \ + 0x91, 0x7d, 0xfa, 0x80, \ + 0x0c, 0x4c, 0xf6, 0x75, \ + 0xdd, 0x93, 0xf0, 0x3c, \ + 0xf8, 0x8c, 0x04, 0x0c, \ + 0x60, 0x38, 0x46, 0x11, \ + 0x46, 0x21, 0x81, 0x51, \ + 0x04, 0x30, 0x60, 0x3c, \ + 0xc0, 0x01, 0x64, 0x70, \ + 0x60, 0x18, 0x46, 0x53, \ + 0x64, 0x70, 0x18, 0x30, \ + 0x0c, 0x30, 0xc0, 0x02, \ + 0x40, 0x02, 0x66, 0x71, \ + 0xe0, 0x0c, 0x43, 0xf6, \ + 0x81, 0x71, 0x03, 0x25, \ + 0x79, 0x00, 0x44, 0x55, \ + 0xf0, 0x9c, 0x50, 0x62, \ + 0xf1, 0x5c, 0xc9, 0x5e, \ + 0x63, 0x00, 0x50, 0x71, \ + 0xa0, 0x56, 0x82, 0x71, \ + 0xe1, 0x00, 0x50, 0x51, \ + 0xe2, 0x00, 0x50, 0x51, \ + 0xe4, 0x00, 0x50, 0x11, \ + 0xe1, 0x00, 0x0e, 0x0a, \ + 0x04, 0x30, 0xfa, 0xd5, \ + 0x40, 0x16, 0x67, 0x71, \ + 0xe0, 0x10, 0x18, 0x06, \ + 0x49, 0x56, 0x81, 0x45, \ + 0xec, 0x04, 0x18, 0xc0, \ + 0xfe, 0x95, 0xa1, 0xcb, \ + 0x67, 0xf1, 0x46, 0x55, \ + 0x04, 0x06, 0x40, 0x01, \ + 0x46, 0x55, 0xef, 0xf0, \ + 0xc0, 0x01, 0x67, 0xed, \ + 0x67, 0xe9, 0x46, 0x55, \ + 0x46, 0x11, 0xc0, 0x01, \ + 0x40, 0x05, 0x6f, 0xd9, \ + 0x46, 0x23, 0x88, 0x1b, \ + 0xc0, 0x02, 0x6f, 0xe9, \ + 0x60, 0x2d, 0x46, 0x51, \ + 0x0c, 0x0c, 0xc0, 0x06, \ + 0x0d, 0xf8, 0xf0, 0x8c, \ + 0xc1, 0xc3, 0xff, 0xc4, \ + 0xfa, 0x7c, 0x04, 0x4c, \ + 0x84, 0x3b, 0x06, 0x6b, \ + 0x70, 0x90, 0x06, 0x09, \ + 0x06, 0x29, 0x81, 0x41, \ + 0x12, 0x10, 0x78, 0x8c, \ + 0x58, 0x46, 0xe6, 0x00, \ + 0xe4, 0xc0, 0x14, 0x10, \ + 0x57, 0xce, 0x18, 0x06, \ + 0x1a, 0x26, 0xff, 0xf8, \ + 0xe0, 0x00, 0x4e, 0x46, \ + 0x81, 0x81, 0x5a, 0x46, \ + 0x89, 0xa5, 0x8f, 0x3e, \ + 0xf0, 0xa0, 0x40, 0x32, \ + 0xe0, 0x40, 0x00, 0x19, \ + 0x8a, 0xa5, 0xa1, 0x1b, \ + 0xe0, 0x60, 0x00, 0x19, \ + 0x8a, 0x25, 0xa1, 0x1b, \ + 0xa1, 0x1b, 0xa4, 0x1a, \ + 0x06, 0x19, 0x88, 0xc5, \ + 0x44, 0x62, 0x7c, 0x20, \ + 0x44, 0x11, 0xf0, 0xa0, \ + 0x00, 0x19, 0xe0, 0x10, \ + 0x44, 0x11, 0xe0, 0x20, \ + 0x00, 0x19, 0xe0, 0x30, \ + 0x44, 0x11, 0xe0, 0x40, \ + 0x00, 0x09, 0xe0, 0x50, \ + 0x44, 0x01, 0xe0, 0x60, \ + 0xa4, 0x2a, 0xe0, 0x70, \ + 0xaa, 0x14, 0xa8, 0x04, \ + 0xe1, 0x84, 0x06, 0x24, \ + 0x81, 0x31, 0x06, 0x03, \ + 0x6f, 0xbc, 0x47, 0x48, \ + 0xf2, 0x90, 0x42, 0x12, \ + 0x42, 0x62, 0x0f, 0x34, \ + 0x04, 0x0e, 0xf0, 0x9c, \ + 0x12, 0x0b, 0x81, 0x41, \ + 0x81, 0xf5, 0xf8, 0x20, \ + 0xf0, 0xa0, 0x40, 0x62, \ + 0xe0, 0x10, 0x40, 0x41, \ + 0xe0, 0x30, 0x40, 0x41, \ + 0xe0, 0x50, 0x40, 0x41, \ + 0x60, 0x70, 0x40, 0x41, \ + 0xf0, 0x9c, 0x40, 0x62, \ + 0x81, 0x01, 0x00, 0x4e, \ + 0x71, 0xac, 0x42, 0x62, \ + 0x04, 0x0c, 0x82, 0x81, \ + 0xec, 0x08, 0x16, 0xb0, \ + 0x88, 0x71, 0x04, 0x91, \ + 0xb1, 0x8b, 0x90, 0xa1, \ + 0x81, 0x01, 0xec, 0xd5, \ + 0xf2, 0x7c, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0x06, 0x00, 0x06, 0x28, \ + 0x60, 0x01, 0xe6, 0x00, \ + 0xe6, 0x90, 0x08, 0x10, \ + 0x65, 0x10, 0x0a, 0x10, \ + 0x59, 0xc6, 0xf8, 0x11, \ + 0x81, 0x61, 0x60, 0x00, \ + 0x02, 0x79, 0x83, 0xf5, \ + 0xa1, 0x7b, 0xf8, 0x20, \ + 0x84, 0x78, 0x84, 0xa5, \ + 0x84, 0x45, 0xa1, 0x7b, \ + 0x80, 0x84, 0x94, 0x74, \ + 0x06, 0xab, 0x82, 0x94, \ + 0x10, 0x7a, 0xf8, 0x20, \ + 0x0c, 0xba, 0x8e, 0x83, \ + 0xf2, 0x98, 0x52, 0x92, \ + 0x00, 0x8e, 0x82, 0x9e, \ + 0x46, 0xa3, 0x8e, 0xa3, \ + 0x82, 0xb1, 0x78, 0x20, \ + 0x02, 0xc1, 0x8c, 0xbc, \ + 0x04, 0x41, 0x84, 0x31, \ + 0x08, 0x51, 0x90, 0x01, \ + 0x0e, 0xc0, 0x82, 0x11, \ + 0x85, 0x7d, 0xec, 0xf0, \ + 0x0c, 0x0c, 0xf6, 0xf5, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x84, 0x6b, 0x06, 0x28, \ + 0x83, 0x81, 0x44, 0x38, \ + 0x80, 0x7b, 0x46, 0x08, \ + 0x91, 0x11, 0x25, 0x8f, \ + 0x6f, 0xf0, 0x5f, 0xe8, \ + 0xe8, 0x8c, 0x12, 0x84, \ + 0x14, 0x04, 0x1c, 0x14, \ + 0x1e, 0x34, 0xe0, 0x90, \ + 0x02, 0x0b, 0x90, 0x2b, \ + 0x67, 0x18, 0x94, 0x1b, \ + 0x1c, 0x14, 0xef, 0xf0, \ + 0x98, 0x0e, 0x92, 0x2b, \ + 0x02, 0x0b, 0x9e, 0x34, \ + 0x66, 0x98, 0x94, 0x1b, \ + 0x9c, 0x24, 0xef, 0xf0, \ + 0x0e, 0x1b, 0x9a, 0x0e, \ + 0x1e, 0x34, 0xe0, 0x10, \ + 0x66, 0x18, 0x84, 0x0b, \ + 0x92, 0x2b, 0x6f, 0xf0, \ + 0x0c, 0x0c, 0xd8, 0x0e, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0xfa, 0x1c, 0x04, 0x4c, \ + 0x88, 0x7b, 0x44, 0x48, \ + 0x81, 0x01, 0x00, 0x9b, \ + 0x84, 0x6b, 0x0a, 0x8b, \ + 0x86, 0x0b, 0x0b, 0x0e, \ + 0x90, 0x2b, 0x0e, 0x1b, \ + 0x68, 0x00, 0x43, 0x28, \ + 0x02, 0x90, 0x88, 0x3b, \ + 0xe5, 0x1d, 0xec, 0x04, \ + 0x0c, 0x60, 0x02, 0xb5, \ + 0xa7, 0x9b, 0xc4, 0x10, \ + 0xa9, 0x9b, 0x80, 0xe5, \ + 0x4e, 0x80, 0x00, 0xe5, \ + 0xab, 0x9b, 0x9c, 0x00, \ + 0x10, 0x61, 0x86, 0x55, \ + 0x0d, 0x14, 0x80, 0x7b, \ + 0xe4, 0x50, 0x06, 0xf0, \ + 0x8e, 0x0b, 0x18, 0x24, \ + 0xef, 0xf0, 0x65, 0xa8, \ + 0x00, 0x1a, 0x0b, 0x14, \ + 0x4e, 0x0a, 0xe0, 0x00, \ + 0x81, 0x01, 0x61, 0x9c, \ + 0x02, 0x06, 0x0f, 0x24, \ + 0xc0, 0x00, 0x20, 0x01, \ + 0xc0, 0x00, 0x02, 0x06, \ + 0x0c, 0x4c, 0x88, 0x1e, \ + 0xdd, 0x93, 0xf2, 0x1c, \ + 0x4e, 0x0a, 0x7b, 0x01, \ + 0x4e, 0x1a, 0xe9, 0x9c, \ + 0x81, 0x11, 0x61, 0x9c, \ + 0xc1, 0xc3, 0xff, 0x05, \ + 0x84, 0x03, 0xa7, 0x2f, \ + 0x5c, 0x08, 0xc3, 0x0d, \ + 0x00, 0x06, 0x1a, 0x00, \ + 0x5d, 0x93, 0x80, 0x00, \ + 0xf1, 0x80, 0x40, 0x12, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xed, 0x30, 0x1e, 0xf0, \ + 0x00, 0x20, 0x3e, 0x0e, \ + 0xc0, 0x05, 0x65, 0x21, \ + 0x82, 0x7b, 0x06, 0x6b, \ + 0x00, 0x20, 0x7e, 0x0e, \ + 0xc0, 0x09, 0x66, 0xa1, \ + 0x81, 0x81, 0x1f, 0x2e, \ + 0x97, 0x7e, 0xfc, 0x0e, \ + 0x22, 0xa8, 0x89, 0x6e, \ + 0x1a, 0x08, 0xe0, 0x00, \ + 0x01, 0xa1, 0x81, 0x31, \ + 0x81, 0x91, 0x81, 0x41, \ + 0x21, 0x28, 0x81, 0x8d, \ + 0x40, 0x62, 0x80, 0x00, \ + 0x9d, 0x11, 0x70, 0xa0, \ + 0x60, 0xb0, 0x00, 0x29, \ + 0x00, 0x49, 0x81, 0xb1, \ + 0x81, 0xa1, 0x60, 0x90, \ + 0x60, 0xd0, 0x00, 0x39, \ + 0x00, 0x09, 0x81, 0x61, \ + 0x81, 0x91, 0x60, 0xf0, \ + 0xe6, 0x86, 0x42, 0x2a, \ + 0x88, 0x33, 0x11, 0x4e, \ + 0x88, 0x13, 0x04, 0x03, \ + 0x83, 0x01, 0x13, 0x0e, \ + 0xc4, 0x17, 0x31, 0x0f, \ + 0x84, 0x4b, 0x19, 0x0e, \ + 0x25, 0x3e, 0xfe, 0x04, \ + 0x0d, 0x8e, 0x81, 0x31, \ + 0xe0, 0x00, 0x50, 0x06, \ + 0x58, 0x02, 0x0f, 0x1e, \ + 0x95, 0x2e, 0xf0, 0x84, \ + 0x18, 0x05, 0x91, 0x45, \ + 0xc0, 0x02, 0x78, 0x61, \ + 0x18, 0x55, 0x9c, 0x48, \ + 0xc0, 0x01, 0x78, 0x31, \ + 0x81, 0x01, 0x00, 0x33, \ + 0x72, 0x80, 0x56, 0xb2, \ + 0x8a, 0xa3, 0xab, 0x4b, \ + 0x4c, 0x62, 0x00, 0xc5, \ + 0x9e, 0x08, 0xf2, 0x80, \ + 0x98, 0xd5, 0xab, 0x0b, \ + 0xe0, 0x34, 0x0e, 0x09, \ + 0x82, 0x0f, 0x99, 0x14, \ + 0x98, 0x05, 0xa1, 0x0b, \ + 0x96, 0x1b, 0x1f, 0x04, \ + 0x90, 0x6b, 0x21, 0x6e, \ + 0x86, 0xab, 0x23, 0xae, \ + 0x63, 0x98, 0x00, 0x49, \ + 0x18, 0x75, 0x92, 0x3b, \ + 0xb0, 0x04, 0xe2, 0x30, \ + 0x0a, 0x40, 0xc2, 0x47, \ + 0x09, 0x0a, 0x6c, 0x80, \ + 0x52, 0x42, 0xe4, 0x7c, \ + 0x81, 0x41, 0x7b, 0x94, \ + 0x0a, 0x92, 0x0e, 0x8b, \ + 0x48, 0x70, 0xec, 0x7c, \ + 0x10, 0x06, 0x1c, 0x00, \ + 0x0e, 0x44, 0x80, 0x00, \ + 0xaa, 0x93, 0x6f, 0x94, \ + 0x16, 0x84, 0x2b, 0x4f, \ + 0x10, 0x54, 0xe8, 0x94, \ + 0x88, 0x03, 0x6c, 0x00, \ + 0x4e, 0x82, 0x7f, 0x51, \ + 0x18, 0x55, 0xf8, 0x9c, \ + 0x8b, 0x0b, 0x64, 0x60, \ + 0x08, 0x92, 0x2e, 0xbf, \ + 0x29, 0xbf, 0xec, 0x04, \ + 0x48, 0xb2, 0x81, 0x41, \ + 0x4e, 0x5a, 0x72, 0x90, \ + 0x41, 0xfe, 0xe5, 0x96, \ + 0xa1, 0x4b, 0x1f, 0xfc, \ + 0x5f, 0xfc, 0x41, 0xfe, \ + 0x67, 0xfc, 0x0f, 0x0a, \ + 0x4a, 0x5a, 0xc0, 0xff, \ + 0x0e, 0x74, 0x05, 0x16, \ + 0x08, 0x04, 0x80, 0xbc, \ + 0x51, 0xfe, 0x76, 0x80, \ + 0xdf, 0xff, 0x7f, 0xfc, \ + 0x01, 0xc4, 0x0a, 0x54, \ + 0x4a, 0x52, 0xa9, 0x0f, \ + 0xc0, 0x0b, 0x19, 0x9c, \ + 0x1c, 0x7c, 0x10, 0x54, \ + 0xe0, 0x00, 0x08, 0x5a, \ + 0x64, 0x00, 0x51, 0x4a, \ + 0x42, 0x0a, 0x86, 0x9b, \ + 0x94, 0x3b, 0x61, 0x80, \ + 0x62, 0x00, 0x00, 0x0c, \ + 0x17, 0x74, 0x8c, 0x8b, \ + 0x23, 0xa4, 0xa0, 0x0b, \ + 0x21, 0x64, 0x88, 0x03, \ + 0x50, 0x02, 0x82, 0xbb, \ + 0x02, 0x91, 0xfb, 0xa0, \ + 0x88, 0xc1, 0x82, 0x21, \ + 0x81, 0x2d, 0xa5, 0x04, \ + 0x9b, 0x44, 0xe0, 0x75, \ + 0x95, 0x24, 0x9d, 0xc4, \ + 0xf0, 0xc1, 0x82, 0x41, \ + 0x81, 0x4d, 0x93, 0x04, \ + 0xc0, 0x20, 0x1e, 0x25, \ + 0xc0, 0x34, 0x5e, 0x45, \ + 0xc0, 0x38, 0x5e, 0xc5, \ + 0x9c, 0x08, 0xfc, 0x15, \ + 0xab, 0x0b, 0x0b, 0x0e, \ + 0x17, 0x04, 0x80, 0xe5, \ + 0x81, 0xc1, 0x81, 0x41, \ + 0xab, 0x0b, 0x82, 0x08, \ + 0x97, 0x04, 0xa2, 0xb5, \ + 0x00, 0x09, 0x99, 0x14, \ + 0x82, 0x0f, 0xe0, 0x34, \ + 0x08, 0x06, 0xa1, 0x0b, \ + 0x18, 0x06, 0x00, 0x00, \ + 0xa0, 0x45, 0x80, 0x00, \ + 0x81, 0x41, 0x7c, 0x04, \ + 0x81, 0xc1, 0x0f, 0x14, \ + 0xa1, 0x6e, 0xa3, 0xae, \ + 0x8f, 0x0e, 0x82, 0x03, \ + 0x1f, 0x04, 0x8f, 0x05, \ + 0x1b, 0x5e, 0x90, 0xab, \ + 0x1d, 0x4e, 0x92, 0x7b, \ + 0x00, 0x09, 0x86, 0x1b, \ + 0x96, 0x3b, 0x63, 0x98, \ + 0x78, 0x31, 0x04, 0x85, \ + 0x04, 0x45, 0xc0, 0x01, \ + 0xc0, 0x02, 0x78, 0x61, \ + 0x0a, 0x00, 0xc2, 0x07, \ + 0x01, 0x0a, 0x6c, 0x80, \ + 0x52, 0x02, 0xe4, 0x7c, \ + 0x81, 0x01, 0x7b, 0x94, \ + 0x6c, 0x7c, 0x0a, 0x92, \ + 0x40, 0x80, 0x90, 0xbb, \ + 0x16, 0x06, 0x1c, 0x00, \ + 0x10, 0x04, 0x80, 0x00, \ + 0xaa, 0x93, 0x6f, 0x94, \ + 0x0c, 0x54, 0x2b, 0x0f, \ + 0x2b, 0xbf, 0xec, 0x00, \ + 0x7f, 0x51, 0x80, 0x43, \ + 0xf8, 0xa0, 0x4c, 0x62, \ + 0x8b, 0x4b, 0x08, 0x54, \ + 0x00, 0x92, 0x2c, 0xbf, \ + 0x21, 0xbf, 0xec, 0x04, \ + 0x40, 0xb2, 0x81, 0x61, \ + 0x50, 0x5a, 0x72, 0x98, \ + 0x49, 0xfe, 0xe5, 0x96, \ + 0xa1, 0x0b, 0x1f, 0xfc, \ + 0x5f, 0xfc, 0x49, 0xfe, \ + 0x67, 0xfc, 0x11, 0x0a, \ + 0x7f, 0x01, 0xc0, 0xff, \ + 0x80, 0xc0, 0x10, 0x84, \ + 0x05, 0x16, 0x40, 0x5a, \ + 0x0e, 0x9b, 0x86, 0xbb, \ + 0x00, 0x04, 0x82, 0x3b, \ + 0x40, 0x02, 0x81, 0xc0, \ + 0x94, 0x8b, 0x19, 0xa0, \ + 0x00, 0x16, 0xa1, 0x0b, \ + 0xa1, 0x8b, 0x00, 0x00, \ + 0x48, 0xa0, 0x08, 0x44, \ + 0xc1, 0x84, 0x0a, 0x04, \ + 0x4a, 0x52, 0x02, 0x75, \ + 0x48, 0x6b, 0xd0, 0x90, \ + 0xe1, 0x84, 0x0a, 0x04, \ + 0x81, 0x61, 0x4c, 0x5d, \ + 0xc0, 0x00, 0x0a, 0x06, \ + 0x40, 0x5a, 0x88, 0x53, \ + 0x9d, 0x44, 0xe0, 0x02, \ + 0xa3, 0xa4, 0x9b, 0x54, \ + 0x21, 0x64, 0x80, 0x43, \ + 0xf2, 0x98, 0x58, 0xc2, \ + 0x88, 0x21, 0x02, 0x51, \ + 0x81, 0x5d, 0xa5, 0x04, \ + 0x8f, 0x04, 0xe4, 0xf5, \ + 0xf0, 0x01, 0x95, 0x24, \ + 0x82, 0x21, 0x0f, 0x0e, \ + 0x81, 0x2d, 0x93, 0x04, \ + 0xc0, 0x28, 0x5e, 0x25, \ + 0xc0, 0x1c, 0x1e, 0x25, \ + 0xc0, 0x20, 0x1e, 0x55, \ + 0xe5, 0x9d, 0xfc, 0x15, \ + 0xc0, 0x10, 0x1e, 0x65, \ + 0xc0, 0x2c, 0x1e, 0x75, \ + 0xc0, 0x18, 0x1e, 0x85, \ + 0x16, 0x1b, 0xb0, 0x35, \ + 0x1b, 0x8e, 0x8c, 0xbb, \ + 0xe1, 0x84, 0x0c, 0x94, \ + 0x70, 0x8c, 0x40, 0x62, \ + 0x1d, 0xce, 0x81, 0x81, \ + 0x42, 0x12, 0x88, 0x7b, \ + 0x81, 0x31, 0x72, 0xa0, \ + 0x6f, 0xb8, 0x62, 0x88, \ + 0x25, 0x0e, 0x92, 0x2b, \ + 0xf0, 0xa8, 0x40, 0x62, \ + 0x72, 0xa0, 0x42, 0xb2, \ + 0x61, 0xf8, 0x81, 0x31, \ + 0x92, 0x2b, 0x6f, 0xb8, \ + 0x80, 0xab, 0x0b, 0x04, \ + 0x1e, 0x65, 0xab, 0x0b, \ + 0x48, 0x70, 0x00, 0x10, \ + 0x1e, 0x85, 0x9c, 0x00, \ + 0x82, 0x85, 0x80, 0x18, \ + 0x8e, 0x4b, 0x17, 0x04, \ + 0x8d, 0x84, 0x89, 0x64, \ + 0xab, 0x0b, 0x82, 0x08, \ + 0xc0, 0x2c, 0x1e, 0x75, \ + 0xc0, 0x48, 0x1e, 0x35, \ + 0x97, 0x04, 0xa4, 0x75, \ + 0xa5, 0x34, 0x99, 0x14, \ + 0xe0, 0x34, 0x00, 0x09, \ + 0xa1, 0x0b, 0x82, 0x0f, \ + 0x80, 0x2c, 0x1e, 0x75, \ + 0x1d, 0x14, 0xa2, 0x25, \ + 0xe0, 0x00, 0x44, 0x06, \ + 0x00, 0x14, 0xa3, 0xae, \ + 0xa0, 0x23, 0xf6, 0x80, \ + 0x82, 0x85, 0xa1, 0x2b, \ + 0xff, 0x01, 0x1b, 0x74, \ + 0xc5, 0x7d, 0x81, 0xa1, \ + 0x54, 0x72, 0x88, 0xb5, \ + 0x88, 0x0b, 0x71, 0x88, \ + 0xef, 0xb8, 0x6e, 0x28, \ + 0x14, 0x44, 0x83, 0x15, \ + 0x1b, 0x14, 0xf6, 0x80, \ + 0xec, 0x80, 0x00, 0xa0, \ + 0x7b, 0x80, 0x40, 0xa2, \ + 0xe4, 0x7c, 0x15, 0x0a, \ + 0x40, 0x00, 0x08, 0x06, \ + 0xec, 0x7c, 0x00, 0x02, \ + 0x00, 0x44, 0x02, 0xa3, \ + 0x54, 0x0a, 0xe8, 0x80, \ + 0x82, 0x85, 0xe0, 0x80, \ + 0x14, 0xa0, 0x7f, 0x11, \ + 0x83, 0x0b, 0xec, 0x04, \ + 0xc4, 0x04, 0x00, 0x00, \ + 0xc1, 0x84, 0x00, 0x04, \ + 0x1f, 0xfd, 0x41, 0xfe, \ + 0x42, 0x0a, 0x9f, 0xff, \ + 0x44, 0x86, 0x70, 0x0a, \ + 0x14, 0x8a, 0xe0, 0x3c, \ + 0xc3, 0xa7, 0x64, 0x60, \ + 0xe5, 0x0a, 0x40, 0x1a, \ + 0xe5, 0x8a, 0x42, 0x1a, \ + 0x6b, 0x58, 0x82, 0x55, \ + 0x04, 0xa0, 0x6f, 0xb8, \ + 0x89, 0x64, 0xec, 0x2c, \ + 0x8d, 0x84, 0x97, 0x74, \ + 0x85, 0x35, 0xa3, 0xa4, \ + 0xd7, 0x31, 0x17, 0x21, \ + 0xb4, 0x33, 0x34, 0x23, \ + 0x7b, 0x8c, 0x48, 0x22, \ + 0xe4, 0x7c, 0x05, 0x0a, \ + 0x04, 0x42, 0x00, 0x3b, \ + 0x06, 0x06, 0xec, 0x7c, \ + 0x40, 0x10, 0x40, 0x00, \ + 0x0a, 0x34, 0x9c, 0x00, \ + 0x18, 0x24, 0x6f, 0x88, \ + 0x17, 0x74, 0xec, 0x00, \ + 0x4a, 0xc2, 0xa4, 0x43, \ + 0xa5, 0x0f, 0x78, 0x94, \ + 0xaa, 0x0f, 0x09, 0x64, \ + 0x02, 0x42, 0x0d, 0x84, \ + 0x23, 0xa4, 0xec, 0x04, \ + 0xe8, 0x84, 0x02, 0x04, \ + 0xe8, 0x88, 0x00, 0x34, \ + 0xc8, 0x34, 0xbe, 0x24, \ + 0xe5, 0x0e, 0x44, 0x9a, \ + 0xe5, 0x8e, 0x46, 0x9a, \ + 0xe1, 0x84, 0x0a, 0x24, \ + 0xe0, 0xfc, 0x08, 0x34, \ + 0x08, 0x34, 0x28, 0x5f, \ + 0x8a, 0x03, 0xe1, 0x84, \ + 0x6f, 0xb8, 0x58, 0x68, \ + 0xf2, 0x84, 0x42, 0x42, \ + 0x41, 0xfe, 0x00, 0x4b, \ + 0xcf, 0xff, 0x7f, 0xfd, \ + 0xc1, 0x4d, 0x25, 0x34, \ + 0x1f, 0xfd, 0x49, 0xfe, \ + 0xa1, 0x1b, 0x0f, 0xff, \ + 0x1f, 0xfd, 0x49, 0xfe, \ + 0x00, 0x84, 0xcf, 0xff, \ + 0x02, 0x60, 0x60, 0x88, \ + 0x04, 0x84, 0xe5, 0x10, \ + 0x82, 0x03, 0x60, 0x84, \ + 0x82, 0x81, 0x04, 0x13, \ + 0x40, 0xa5, 0x80, 0x3e, \ + 0x40, 0x45, 0xe0, 0x20, \ + 0x42, 0x93, 0xe0, 0x40, \ + 0x98, 0x08, 0xe0, 0xc0, \ + 0x1d, 0x68, 0x81, 0x8d, \ + 0x1e, 0xf0, 0xdf, 0xfc, \ + 0x0c, 0x0c, 0xe5, 0x30, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0xb0, 0x01, 0x02, 0x6b, \ + 0x81, 0x71, 0x7c, 0x0e, \ + 0x0a, 0x60, 0x0d, 0x7e, \ + 0x0b, 0x7e, 0xe4, 0xf0, \ + 0x89, 0x7e, 0x81, 0xa1, \ + 0xbe, 0x5e, 0xfe, 0x7e, \ + 0x89, 0xd5, 0xfc, 0x94, \ + 0x81, 0x11, 0x1a, 0x08, \ + 0x81, 0x21, 0x0d, 0x7e, \ + 0x81, 0x31, 0x7e, 0x7e, \ + 0x81, 0x01, 0x01, 0xad, \ + 0x8b, 0x7e, 0x89, 0x7e, \ + 0x56, 0x52, 0x8e, 0x05, \ + 0x50, 0x92, 0x70, 0xa8, \ + 0x16, 0x09, 0xf0, 0xa8, \ + 0x04, 0xf0, 0x78, 0xe0, \ + 0x16, 0x19, 0xe4, 0x30, \ + 0x0a, 0xf0, 0x78, 0xa0, \ + 0x10, 0x39, 0xe4, 0x60, \ + 0x10, 0x49, 0xf8, 0x20, \ + 0x3c, 0x2e, 0xe0, 0x20, \ + 0x43, 0x78, 0x9b, 0x21, \ + 0x16, 0x09, 0xe8, 0x00, \ + 0x04, 0xf0, 0x78, 0xc0, \ + 0x16, 0x19, 0xe4, 0x50, \ + 0x0a, 0xf0, 0x78, 0x80, \ + 0x12, 0x39, 0xe4, 0x40, \ + 0x10, 0x49, 0xfc, 0x28, \ + 0x3c, 0x2e, 0xe0, 0x40, \ + 0x42, 0x78, 0x93, 0x21, \ + 0xfe, 0x04, 0xe8, 0x00, \ + 0x82, 0x05, 0xa1, 0x0b, \ + 0xa1, 0x1b, 0x8b, 0x14, \ + 0xc0, 0x18, 0x1e, 0x35, \ + 0xc0, 0x10, 0x1e, 0x25, \ + 0x0d, 0x7e, 0x82, 0x35, \ + 0x09, 0x7e, 0x81, 0x11, \ + 0x7e, 0x7e, 0x81, 0x01, \ + 0x0b, 0x7e, 0x81, 0x21, \ + 0xbe, 0x54, 0x81, 0x31, \ + 0x70, 0xa8, 0x48, 0x52, \ + 0x48, 0x31, 0x82, 0xa1, \ + 0x48, 0x21, 0xf8, 0x60, \ + 0x48, 0x01, 0xf8, 0x40, \ + 0x90, 0x16, 0xf8, 0x20, \ + 0x81, 0xad, 0x98, 0x08, \ + 0xb8, 0xf1, 0xec, 0x55, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x58, 0x12, 0x00, 0x33, \ + 0x14, 0x3e, 0xf0, 0x80, \ + 0xf0, 0x90, 0x40, 0x32, \ + 0x00, 0x00, 0x19, 0x0d, \ + 0x00, 0x1b, 0xec, 0x04, \ + 0x02, 0xc0, 0x81, 0x51, \ + 0x3c, 0x14, 0x8c, 0x04, \ + 0xfb, 0x04, 0x44, 0x22, \ + 0x82, 0x35, 0x84, 0x3d, \ + 0x88, 0x5b, 0x04, 0x0d, \ + 0xc0, 0x00, 0x00, 0x16, \ + 0xd1, 0x8c, 0x40, 0x02, \ + 0xd0, 0x88, 0x4a, 0x02, \ + 0xdd, 0x93, 0x84, 0x5e, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x82, 0x8b, 0x0a, 0x6b, \ + 0x84, 0x7b, 0x11, 0x94, \ + 0x8c, 0x1b, 0x08, 0x0b, \ + 0x6f, 0xfc, 0x4b, 0xa8, \ + 0x01, 0x01, 0x90, 0x2b, \ + 0x85, 0x65, 0x91, 0xa1, \ + 0x88, 0x35, 0xa1, 0x0b, \ + 0x60, 0x80, 0x12, 0x15, \ + 0x12, 0x45, 0x81, 0x31, \ + 0x90, 0x2b, 0x60, 0x40, \ + 0xe0, 0x60, 0x12, 0x55, \ + 0xbe, 0x9e, 0x9c, 0x08, \ + 0x8c, 0x1b, 0x3c, 0x1e, \ + 0xef, 0xfc, 0x4d, 0x28, \ + 0x84, 0x35, 0xa1, 0x0b, \ + 0x60, 0x80, 0x12, 0x15, \ + 0xe4, 0x80, 0x04, 0x90, \ + 0x60, 0x40, 0x12, 0x45, \ + 0x12, 0x55, 0x81, 0x31, \ + 0x9e, 0x08, 0xe0, 0x60, \ + 0x90, 0x2b, 0x3e, 0x2e, \ + 0x8c, 0x1b, 0x3c, 0x1e, \ + 0xef, 0xfc, 0x4c, 0x08, \ + 0x6c, 0x04, 0x14, 0xa0, \ + 0xa1, 0xab, 0x88, 0x91, \ + 0x90, 0xf1, 0xf6, 0x55, \ + 0xf2, 0x3c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x82, 0x7b, 0x15, 0x84, \ + 0x84, 0x6b, 0x17, 0x94, \ + 0x8e, 0x2b, 0x13, 0x04, \ + 0x6f, 0xfc, 0x47, 0xe8, \ + 0x01, 0x01, 0x90, 0x1b, \ + 0xe0, 0x08, 0x14, 0xc6, \ + 0xa1, 0x0b, 0x85, 0x75, \ + 0x12, 0x15, 0x88, 0x55, \ + 0x81, 0x31, 0x68, 0xc0, \ + 0x64, 0x60, 0x12, 0x45, \ + 0x12, 0x55, 0x8e, 0x2b, \ + 0x98, 0x08, 0xe6, 0x90, \ + 0x3c, 0x1e, 0xbe, 0x9e, \ + 0x49, 0x58, 0x90, 0x1b, \ + 0xa1, 0x0b, 0xef, 0xfc, \ + 0x12, 0x15, 0x84, 0x55, \ + 0x04, 0x90, 0x68, 0xc0, \ + 0xc0, 0x02, 0x64, 0xc0, \ + 0x64, 0x60, 0x12, 0x45, \ + 0x12, 0x55, 0x81, 0x31, \ + 0x9a, 0x08, 0xe6, 0x90, \ + 0x8e, 0x2b, 0x3e, 0x2e, \ + 0x90, 0x1b, 0x3c, 0x1e, \ + 0xef, 0xfc, 0x48, 0x28, \ + 0x6c, 0x04, 0x14, 0xa0, \ + 0xa1, 0xab, 0x88, 0x91, \ + 0x90, 0xf1, 0xf6, 0x35, \ + 0xf2, 0x3c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x80, 0xbb, 0x19, 0x74, \ + 0x8a, 0x6b, 0x1b, 0xa4, \ + 0x82, 0x9b, 0x04, 0x8b, \ + 0x64, 0x50, 0x00, 0x70, \ + 0xe4, 0x10, 0x08, 0x70, \ + 0x0a, 0x70, 0x3c, 0x0e, \ + 0x77, 0x58, 0xe4, 0x40, \ + 0xa1, 0x0b, 0xef, 0xfc, \ + 0xc3, 0x8c, 0x0e, 0x19, \ + 0xc0, 0x0c, 0x54, 0x11, \ + 0x02, 0x70, 0x84, 0x75, \ + 0x00, 0x70, 0x67, 0xa0, \ + 0x7e, 0x1e, 0xe7, 0x60, \ + 0xe7, 0x90, 0x04, 0x70, \ + 0x92, 0x1b, 0x7c, 0x2e, \ + 0x90, 0x2b, 0x3e, 0x0e, \ + 0xef, 0xfc, 0x79, 0xe8, \ + 0x63, 0x8c, 0x0e, 0x19, \ + 0xea, 0x16, 0xa1, 0x0b, \ + 0xa8, 0xf1, 0x80, 0xa5, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x16, 0x0b, 0xdd, 0x93, \ + 0x10, 0x2b, 0x92, 0x1b, \ + 0x0e, 0x4b, 0x8c, 0x3b, \ + 0xa8, 0xf1, 0x94, 0x5b, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0x81, 0x25, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x8a, 0x6b, 0x04, 0x9b, \ + 0x88, 0x8b, 0x02, 0xab, \ + 0x92, 0x0b, 0x06, 0x7b, \ + 0x6f, 0xfc, 0x6c, 0x28, \ + 0x14, 0x0b, 0x8c, 0x1b, \ + 0x10, 0x2b, 0x92, 0x1b, \ + 0x47, 0x58, 0x8c, 0x3b, \ + 0x10, 0x09, 0xef, 0xfc, \ + 0x00, 0x00, 0xe3, 0x58, \ + 0xe2, 0x0d, 0xec, 0x0c, \ + 0x1c, 0x00, 0x40, 0x70, \ + 0x9c, 0x00, 0x42, 0x60, \ + 0x8f, 0xf8, 0x7c, 0x28, \ + 0x0c, 0x0c, 0x81, 0x01, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0xf8, 0x3c, 0x04, 0x4c, \ + 0x62, 0x01, 0x04, 0x1b, \ + 0x81, 0xc1, 0x40, 0x01, \ + 0x0c, 0xc4, 0x85, 0x95, \ + 0x0e, 0x64, 0xfb, 0x80, \ + 0x84, 0x63, 0x60, 0x84, \ + 0x60, 0x01, 0x0c, 0x89, \ + 0x84, 0x73, 0x40, 0x02, \ + 0xe8, 0x00, 0x0e, 0x7b, \ + 0x08, 0xa5, 0xc4, 0x87, \ + 0xc4, 0x77, 0x7c, 0x20, \ + 0xfc, 0x1c, 0x00, 0x95, \ + 0x50, 0x82, 0xd3, 0xad, \ + 0x50, 0x95, 0xd0, 0x90, \ + 0x06, 0x89, 0xc0, 0x00, \ + 0xa1, 0x8b, 0xe0, 0x30, \ + 0x0c, 0x60, 0x82, 0xc5, \ + 0x40, 0x08, 0x64, 0x01, \ + 0x98, 0x68, 0x80, 0x73, \ + 0xc4, 0x67, 0x9c, 0x74, \ + 0x7c, 0x18, 0x0a, 0x85, \ + 0x8a, 0x63, 0xcf, 0x77, \ + 0x98, 0x8e, 0x8e, 0x83, \ + 0x58, 0x8a, 0x82, 0xc1, \ + 0xf4, 0xd5, 0xec, 0x04, \ + 0x0c, 0x4c, 0x81, 0x01, \ + 0xdd, 0x93, 0xf0, 0x3c, \ + 0xf8, 0x7c, 0x04, 0xcc, \ + 0x81, 0xc1, 0x0f, 0x5e, \ + 0x81, 0x61, 0x0d, 0x4e, \ + 0x64, 0x09, 0x02, 0x49, \ + 0x87, 0x45, 0xc0, 0x02, \ + 0x60, 0x34, 0x04, 0x99, \ + 0xfa, 0x80, 0x0e, 0xc4, \ + 0x60, 0x30, 0x04, 0xb9, \ + 0x06, 0x85, 0xc4, 0x77, \ + 0xa1, 0xbb, 0xfc, 0x1c, \ + 0xe5, 0xa6, 0x54, 0x8a, \ + 0x65, 0x26, 0x50, 0x8a, \ + 0xf0, 0x9c, 0x52, 0x32, \ + 0xd1, 0x87, 0xf0, 0xa7, \ + 0x56, 0x82, 0x0f, 0x84, \ + 0x8e, 0x83, 0xf9, 0xa8, \ + 0x8d, 0xa4, 0xa0, 0xbe, \ + 0x8e, 0xa3, 0x02, 0xc5, \ + 0xca, 0x0a, 0xa8, 0x64, \ + 0xe5, 0x02, 0x4a, 0x6a, \ + 0xe5, 0x82, 0x40, 0x6a, \ + 0xee, 0x07, 0xd3, 0x57, \ + 0xf9, 0x80, 0x4c, 0x52, \ + 0xa0, 0xbe, 0x8c, 0xb3, \ + 0x82, 0x73, 0x24, 0x04, \ + 0x4e, 0x05, 0x82, 0xc1, \ + 0xa8, 0x04, 0xe9, 0x04, \ + 0xe9, 0x44, 0x4e, 0x05, \ + 0x4e, 0x65, 0xa0, 0x04, \ + 0x4e, 0x05, 0xe9, 0xc4, \ + 0x58, 0x8a, 0xe9, 0x84, \ + 0xf2, 0x75, 0xf4, 0x10, \ + 0x0c, 0xcc, 0x81, 0x01, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xf8, 0x00, 0x04, 0x4c, \ + 0x81, 0x41, 0x19, 0x11, \ + 0x18, 0x54, 0x83, 0x95, \ + 0x4c, 0x22, 0x60, 0x84, \ + 0x04, 0xc3, 0xf0, 0x94, \ + 0x18, 0x5b, 0x82, 0x41, \ + 0x0c, 0xc9, 0xe8, 0x00, \ + 0xc0, 0x02, 0x60, 0x01, \ + 0xe0, 0x88, 0x0c, 0x54, \ + 0x7c, 0x18, 0x00, 0x65, \ + 0x06, 0xc5, 0xc4, 0xc7, \ + 0xd9, 0x6d, 0xfc, 0x30, \ + 0xd0, 0x80, 0x4a, 0x52, \ + 0x0c, 0x01, 0x4a, 0x11, \ + 0x04, 0xcb, 0xc0, 0x04, \ + 0x40, 0x01, 0x62, 0x01, \ + 0xfb, 0x80, 0x0a, 0x44, \ + 0xf8, 0x55, 0xd9, 0x5d, \ + 0x0c, 0x4c, 0x81, 0x01, \ + 0xdd, 0x93, 0xf0, 0x00, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xef, 0x10, 0x1e, 0xf0, \ + 0x86, 0x8b, 0x04, 0x6b, \ + 0x64, 0x09, 0x0c, 0x09, \ + 0x80, 0x7b, 0x40, 0x02, \ + 0x65, 0x10, 0x02, 0xf0, \ + 0xe6, 0x10, 0x04, 0xf0, \ + 0x81, 0x31, 0x01, 0x65, \ + 0x00, 0x00, 0x49, 0x3e, \ + 0xc5, 0x3e, 0xec, 0x04, \ + 0xfe, 0x75, 0xa1, 0x0b, \ + 0x66, 0x10, 0x12, 0xf0, \ + 0xe5, 0x10, 0x14, 0xf0, \ + 0x8c, 0x2b, 0x0e, 0x0b, \ + 0x92, 0x4b, 0x10, 0x3b, \ + 0x6f, 0xfc, 0x74, 0x08, \ + 0x16, 0xf0, 0x94, 0x5b, \ + 0x8c, 0x1b, 0x64, 0x10, \ + 0x92, 0x3b, 0x10, 0x2b, \ + 0x96, 0x5b, 0x14, 0x4b, \ + 0xef, 0xfc, 0x76, 0xc8, \ + 0x8c, 0x2b, 0x0e, 0x0b, \ + 0x6f, 0xfc, 0x7a, 0xc8, \ + 0x01, 0x01, 0x96, 0x3b, \ + 0xe7, 0x10, 0x1e, 0xf0, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xff, 0x01, 0x00, 0x24, \ + 0x83, 0x2d, 0x84, 0x14, \ + 0x00, 0x00, 0x00, 0x06, \ + 0x5d, 0x93, 0x82, 0x2d, \ + 0xc0, 0x00, 0x00, 0x16, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xb0, 0x1e, 0xf0, \ + 0x86, 0x9b, 0x04, 0x7b, \ + 0x02, 0x06, 0x00, 0x6b, \ + 0x09, 0x21, 0xe0, 0x10, \ + 0x69, 0x49, 0x47, 0x8e, \ + 0x61, 0x58, 0xc0, 0x10, \ + 0x8e, 0x0b, 0x6f, 0xb8, \ + 0x64, 0x01, 0x00, 0x60, \ + 0x81, 0xb1, 0x40, 0x11, \ + 0x00, 0x60, 0x3e, 0x0e, \ + 0xc0, 0x10, 0x64, 0x01, \ + 0x81, 0x01, 0x0f, 0x0e, \ + 0x81, 0x01, 0x09, 0x0e, \ + 0x83, 0x51, 0x15, 0xbe, \ + 0x81, 0xa1, 0x13, 0xbe, \ + 0x81, 0x41, 0x7e, 0x6e, \ + 0x8b, 0x7e, 0xfc, 0x0e, \ + 0x0d, 0xae, 0x95, 0xe5, \ + 0xe0, 0x88, 0x00, 0x84, \ + 0x96, 0xab, 0x11, 0x4e, \ + 0x7c, 0x00, 0x0e, 0x05, \ + 0x4e, 0x78, 0x8a, 0xbb, \ + 0x0c, 0x84, 0xe8, 0x00, \ + 0x80, 0xcb, 0x60, 0x84, \ + 0x19, 0x0a, 0x0e, 0x63, \ + 0xc0, 0x01, 0x64, 0x01, \ + 0x68, 0x00, 0x4c, 0xc3, \ + 0x80, 0x84, 0x00, 0xc4, \ + 0x80, 0x0c, 0x1e, 0x15, \ + 0x90, 0x84, 0x40, 0x02, \ + 0x0c, 0x01, 0x00, 0x73, \ + 0x41, 0xde, 0x00, 0x01, \ + 0x5e, 0x05, 0xdf, 0xfc, \ + 0x4f, 0xf6, 0xc0, 0x08, \ + 0x16, 0x0a, 0x47, 0xfc, \ + 0x88, 0x25, 0xfb, 0xfc, \ + 0x60, 0x28, 0x12, 0x19, \ + 0x04, 0xf0, 0x98, 0xab, \ + 0x06, 0xf0, 0x64, 0xa0, \ + 0x49, 0xb8, 0xe4, 0x90, \ + 0x8e, 0x0b, 0x68, 0x00, \ + 0x60, 0x2c, 0x12, 0x09, \ + 0x24, 0x02, 0x80, 0x5b, \ + 0x82, 0x45, 0xa1, 0x0b, \ + 0x60, 0x1e, 0x40, 0x0e, \ + 0xe0, 0xf8, 0x42, 0x06, \ + 0xe0, 0x86, 0x40, 0x0a, \ + 0x40, 0x02, 0xa6, 0x12, \ + 0x09, 0x0e, 0xfa, 0x04, \ + 0x0b, 0x74, 0x81, 0xb1, \ + 0x11, 0x44, 0x94, 0xcb, \ + 0x85, 0x55, 0x81, 0xa1, \ + 0x16, 0x8a, 0x94, 0xbb, \ + 0x82, 0x15, 0xf4, 0x3c, \ + 0x9f, 0xb1, 0x0d, 0xa4, \ + 0x81, 0x51, 0x0b, 0x74, \ + 0x03, 0xb5, 0x91, 0x44, \ + 0x13, 0x04, 0x82, 0xa1, \ + 0x89, 0x14, 0x98, 0xab, \ + 0xf1, 0x80, 0x40, 0x72, \ + 0xf0, 0x80, 0x00, 0x04, \ + 0x8e, 0x45, 0x83, 0x0d, \ + 0x60, 0x28, 0x12, 0x19, \ + 0xe4, 0xa0, 0x04, 0xf0, \ + 0x64, 0x90, 0x06, 0xf0, \ + 0x45, 0xd8, 0x8e, 0x0b, \ + 0x0d, 0x04, 0xe8, 0x00, \ + 0x0b, 0x74, 0x81, 0x51, \ + 0x91, 0x44, 0x94, 0xcb, \ + 0x80, 0xab, 0x82, 0x01, \ + 0x8e, 0x83, 0x13, 0x34, \ + 0x60, 0x01, 0x50, 0xb1, \ + 0x96, 0x2b, 0x40, 0x02, \ + 0xe0, 0x3c, 0x12, 0x09, \ + 0xec, 0x00, 0x4c, 0xa3, \ + 0x64, 0x09, 0x4c, 0x33, \ + 0x0f, 0x14, 0xc0, 0x01, \ + 0x84, 0xc5, 0xa3, 0x0b, \ + 0x82, 0x85, 0xa9, 0x0b, \ + 0x1e, 0x15, 0xa7, 0x0b, \ + 0x44, 0x40, 0x00, 0x1c, \ + 0x82, 0xc5, 0x9c, 0x00, \ + 0x82, 0xd5, 0xa5, 0x0b, \ + 0x94, 0x2b, 0x0f, 0x14, \ + 0x7e, 0x04, 0x81, 0x95, \ + 0xe1, 0xa0, 0x04, 0x34, \ + 0x40, 0x31, 0x98, 0x03, \ + 0xc0, 0x04, 0x60, 0x01, \ + 0x98, 0x13, 0xbe, 0x14, \ + 0x03, 0xd5, 0x84, 0x26, \ + 0x12, 0x19, 0x82, 0x41, \ + 0x04, 0xf0, 0x60, 0x28, \ + 0x06, 0xf0, 0xe4, 0xa0, \ + 0x8e, 0x0b, 0x64, 0x90, \ + 0x68, 0x00, 0x42, 0xe8, \ + 0x12, 0x09, 0x82, 0xb1, \ + 0x80, 0x5b, 0x60, 0x2c, \ + 0xa1, 0x0b, 0x24, 0x02, \ + 0x40, 0x0e, 0x82, 0x45, \ + 0x42, 0x06, 0x60, 0x1e, \ + 0x40, 0x0a, 0xe0, 0xf8, \ + 0xa6, 0x12, 0xe0, 0x86, \ + 0xfa, 0x04, 0x40, 0x02, \ + 0xf3, 0x45, 0x89, 0x0e, \ + 0x62, 0x01, 0x0e, 0x0b, \ + 0x10, 0x44, 0x40, 0x01, \ + 0xc1, 0x8d, 0xfb, 0x80, \ + 0x82, 0xb1, 0xd2, 0xb5, \ + 0x64, 0x09, 0x4e, 0xb1, \ + 0x7c, 0x04, 0xc0, 0x02, \ + 0x0c, 0x0c, 0xd8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0x0a, 0x44, 0x88, 0x44, \ + 0xaa, 0x43, 0xe9, 0x04, \ + 0x81, 0x01, 0x00, 0x43, \ + 0xc3, 0x4f, 0x08, 0x4e, \ + 0xdd, 0x93, 0x8c, 0x4e, \ + 0xa3, 0x0f, 0x0c, 0x0e, \ + 0x81, 0x01, 0x08, 0x0e, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x60, 0x3c, 0x02, 0x09, \ + 0xa1, 0x0b, 0x80, 0x2b, \ + 0x41, 0xfe, 0x06, 0x65, \ + 0x48, 0x2a, 0x9f, 0xfc, \ + 0x40, 0x2a, 0xe0, 0x02, \ + 0x08, 0x44, 0xe1, 0x02, \ + 0x82, 0x43, 0xfa, 0x80, \ + 0xec, 0x70, 0x08, 0x49, \ + 0x81, 0x01, 0x49, 0x0d, \ + 0x84, 0x13, 0x82, 0xa5, \ + 0x6b, 0x71, 0x02, 0x19, \ + 0xf2, 0x1d, 0xc0, 0x05, \ + 0x44, 0x06, 0x80, 0xf5, \ + 0x44, 0x4a, 0xe0, 0x24, \ + 0x83, 0x11, 0x64, 0x84, \ + 0x81, 0x11, 0x80, 0x45, \ + 0xdd, 0x93, 0x8c, 0x16, \ + 0x00, 0x04, 0x5d, 0x93, \ + 0xc1, 0xc3, 0xfa, 0x80, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x84, 0xbb, 0x00, 0x7b, \ + 0x67, 0x71, 0x10, 0x70, \ + 0x12, 0x70, 0x40, 0x11, \ + 0xc0, 0x03, 0x67, 0x71, \ + 0x81, 0xa1, 0x3e, 0xbe, \ + 0x81, 0x01, 0x01, 0x61, \ + 0x00, 0xa4, 0x85, 0x05, \ + 0x06, 0xf0, 0x7b, 0x80, \ + 0x7c, 0x58, 0xe4, 0x20, \ + 0x8e, 0x1b, 0x6f, 0xfc, \ + 0xa1, 0x1b, 0x91, 0x18, \ + 0x1f, 0xfd, 0x43, 0xfe, \ + 0x56, 0x15, 0x9f, 0xff, \ + 0x82, 0xa5, 0x80, 0x00, \ + 0x45, 0xfe, 0x20, 0x1a, \ + 0xcf, 0xff, 0x7f, 0xfd, \ + 0xe0, 0x17, 0x82, 0x61, \ + 0xac, 0x1e, 0xb4, 0x1f, \ + 0xc4, 0x1d, 0xa4, 0x14, \ + 0xdf, 0xfc, 0x41, 0xde, \ + 0x88, 0xb1, 0x02, 0xa1, \ + 0x88, 0x91, 0x04, 0x81, \ + 0x60, 0x01, 0x14, 0x0a, \ + 0xf6, 0xd5, 0xc0, 0x01, \ + 0x98, 0xf1, 0x3e, 0x14, \ + 0x62, 0x01, 0x42, 0x63, \ + 0x0c, 0x0c, 0xc0, 0x01, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0x81, 0x01, 0x5d, 0x93, \ + 0x60, 0x38, 0x00, 0x19, \ + 0xa1, 0x1b, 0x81, 0x21, \ + 0x00, 0x00, 0x02, 0x16, \ + 0x9f, 0xfc, 0x41, 0xfe, \ + 0xd0, 0x2a, 0x86, 0x05, \ + 0xe0, 0x28, 0x08, 0x09, \ + 0x7c, 0xc0, 0x06, 0x24, \ + 0x46, 0x3a, 0xe2, 0x27, \ + 0x02, 0x10, 0x66, 0x86, \ + 0x01, 0x0a, 0xec, 0x04, \ + 0xfb, 0x01, 0x64, 0x40, \ + 0x60, 0x0e, 0x44, 0x2a, \ + 0xf0, 0x00, 0x06, 0x06, \ + 0x80, 0x00, 0x00, 0x06, \ + 0xe6, 0x86, 0x42, 0x2a, \ + 0xf1, 0x84, 0x42, 0x32, \ + 0x48, 0x13, 0xd2, 0x2c, \ + 0xdd, 0x93, 0xe0, 0x10, \ + 0xfa, 0x9c, 0x04, 0x0c, \ + 0x80, 0x9b, 0x08, 0x6b, \ + 0x60, 0x38, 0x0c, 0x09, \ + 0x84, 0x8b, 0x86, 0x7b, \ + 0x81, 0x01, 0x21, 0x0b, \ + 0x12, 0x0b, 0x84, 0x75, \ + 0x79, 0x18, 0x90, 0x1b, \ + 0x8e, 0x2b, 0x6f, 0xfc, \ + 0x82, 0x95, 0xa1, 0x0b, \ + 0x67, 0x71, 0x12, 0x90, \ + 0x90, 0x1b, 0x40, 0x03, \ + 0x8e, 0x2b, 0x12, 0x0b, \ + 0x6f, 0xfc, 0x66, 0xe8, \ + 0xa1, 0x0b, 0x8c, 0x3b, \ + 0x0c, 0x0c, 0x80, 0x85, \ + 0xdd, 0x93, 0xf2, 0x9c, \ + 0x90, 0x1b, 0x12, 0x0b, \ + 0x8c, 0x3b, 0x0e, 0x2b, \ + 0xf2, 0x9c, 0x0c, 0x0c, \ + 0xff, 0xfc, 0x22, 0x08, \ + 0xfa, 0x00, 0x00, 0x4c, \ + 0xe1, 0x00, 0x1e, 0x06, \ + 0x61, 0x94, 0x01, 0x46, \ + 0x76, 0xd8, 0x83, 0x11, \ + 0xc1, 0xc3, 0xef, 0xb4, \ + 0xfa, 0x7c, 0x04, 0x4c, \ + 0x80, 0x9b, 0x02, 0x6b, \ + 0x86, 0xab, 0x08, 0x8b, \ + 0x81, 0xb1, 0x04, 0x7b, \ + 0x60, 0x3c, 0x41, 0xf6, \ + 0x44, 0x38, 0x81, 0x11, \ + 0xa1, 0xab, 0xef, 0xbc, \ + 0x43, 0x78, 0x82, 0x85, \ + 0x92, 0x0b, 0x68, 0x00, \ + 0xe1, 0x4c, 0x01, 0x46, \ + 0x00, 0xbe, 0x80, 0x04, \ + 0xe1, 0x34, 0x41, 0xc6, \ + 0x80, 0xbe, 0x80, 0x04, \ + 0x6d, 0x01, 0x12, 0x09, \ + 0x40, 0x4a, 0xc0, 0x2f, \ + 0x82, 0xe5, 0xe0, 0x80, \ + 0x61, 0x48, 0x40, 0x46, \ + 0x80, 0x04, 0x92, 0x2b, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x00, 0x16, 0xf3, 0x7c, \ + 0x43, 0x58, 0x81, 0x01, \ + 0x81, 0x11, 0x68, 0x04, \ + 0xe8, 0x00, 0x60, 0xc8, \ + 0x80, 0x95, 0xa1, 0x8b, \ + 0xf2, 0x7c, 0x0c, 0x4c, \ + 0xa1, 0x7b, 0xdd, 0x93, \ + 0x18, 0x04, 0x82, 0xe5, \ + 0x6f, 0xfd, 0x43, 0xf6, \ + 0x02, 0x0f, 0xff, 0xc0, \ + 0xf0, 0x00, 0x02, 0x0e, \ + 0x7c, 0x01, 0x40, 0x00, \ + 0x98, 0x0e, 0xc3, 0x00, \ + 0x82, 0x0f, 0x98, 0x04, \ + 0x7b, 0x38, 0x98, 0x0e, \ + 0xc1, 0xc3, 0xef, 0xfc, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x2f, 0x6d, 0x01, \ + 0x60, 0x8c, 0x40, 0x4a, \ + 0xf3, 0x64, 0x42, 0x00, \ + 0x6d, 0x01, 0x4c, 0x11, \ + 0x84, 0xa5, 0xc0, 0x2f, \ + 0x64, 0x21, 0x0c, 0x19, \ + 0x40, 0x00, 0x40, 0x31, \ + 0x4c, 0x01, 0xf3, 0x60, \ + 0x40, 0x2f, 0x6d, 0x01, \ + 0x4c, 0x01, 0x81, 0x01, \ + 0xc0, 0x2f, 0x6d, 0x0d, \ + 0x64, 0x25, 0x4c, 0x01, \ + 0x42, 0x10, 0x40, 0x31, \ + 0x4c, 0x11, 0xf3, 0xf8, \ + 0xc0, 0x31, 0x64, 0x21, \ + 0x63, 0x91, 0x0c, 0x09, \ + 0x42, 0x46, 0x40, 0x31, \ + 0x04, 0x14, 0xe1, 0x38, \ + 0x4c, 0x71, 0x81, 0x71, \ + 0x40, 0x31, 0x64, 0x29, \ + 0x40, 0x00, 0x8f, 0x21, \ + 0x10, 0xc6, 0x73, 0xe4, \ + 0x4c, 0x01, 0xe1, 0x44, \ + 0x40, 0x31, 0x63, 0x91, \ + 0xe1, 0x38, 0x40, 0x86, \ + 0x80, 0x14, 0x84, 0x76, \ + 0x80, 0x04, 0x84, 0x26, \ + 0xa0, 0x04, 0x80, 0x76, \ + 0x8c, 0x0b, 0x00, 0x76, \ + 0xe8, 0x38, 0x4c, 0x48, \ + 0x68, 0x50, 0x69, 0x08, \ + 0x41, 0x46, 0x8c, 0x0b, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0x00, 0x06, 0x00, 0x76, \ + 0x80, 0x04, 0xe1, 0x44, \ + 0xa0, 0x04, 0x80, 0x76, \ + 0x0c, 0x0c, 0x80, 0x76, \ + 0xdd, 0x93, 0xf2, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x8b, 0x11, 0x00, 0x6b, \ + 0xe8, 0x28, 0x68, 0x68, \ + 0xe0, 0x00, 0x40, 0x06, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x8c, 0x0b, 0x40, 0x2f, \ + 0xe8, 0x50, 0x67, 0x48, \ + 0xe8, 0x50, 0x66, 0xa8, \ + 0x68, 0x34, 0x77, 0xe8, \ + 0x0c, 0x09, 0x8c, 0x0b, \ + 0xc0, 0x2f, 0x6d, 0x01, \ + 0xe0, 0x88, 0x40, 0x4a, \ + 0xdc, 0x00, 0x40, 0x60, \ + 0xc8, 0x00, 0x42, 0xe8, \ + 0x68, 0x00, 0x54, 0x68, \ + 0x0c, 0x09, 0x8c, 0x0b, \ + 0xc0, 0x31, 0x64, 0x51, \ + 0xe0, 0x88, 0x40, 0x0a, \ + 0xc0, 0x00, 0x00, 0x46, \ + 0xc8, 0x18, 0x6e, 0xa8, \ + 0x68, 0x00, 0x54, 0xc8, \ + 0x0c, 0x09, 0x8c, 0x0b, \ + 0xc0, 0x31, 0x64, 0x5d, \ + 0xe0, 0x98, 0x40, 0x0a, \ + 0x00, 0x01, 0x00, 0x06, \ + 0x6d, 0xd8, 0xd0, 0x00, \ + 0x0c, 0x09, 0xc8, 0x18, \ + 0xc0, 0x2f, 0x6d, 0x01, \ + 0xe0, 0x80, 0x40, 0x4a, \ + 0x43, 0xc6, 0x82, 0x65, \ + 0x40, 0x20, 0x72, 0xfd, \ + 0x03, 0x31, 0x81, 0x21, \ + 0x8c, 0x0b, 0x83, 0x41, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x0c, 0x0c, 0xe5, 0x65, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x15, 0x61, 0x51, \ + 0x86, 0xd5, 0xa9, 0x0b, \ + 0x61, 0x55, 0x0c, 0x09, \ + 0xa9, 0x0b, 0xc0, 0x15, \ + 0x01, 0x06, 0x86, 0x35, \ + 0x02, 0x60, 0x61, 0x4c, \ + 0xc0, 0xa1, 0x65, 0x60, \ + 0x04, 0x60, 0x00, 0x04, \ + 0xc0, 0xa3, 0x66, 0x71, \ + 0xef, 0xc0, 0x7f, 0x48, \ + 0x0c, 0x39, 0xa1, 0x0b, \ + 0x40, 0x2f, 0x0d, 0x09, \ + 0x06, 0x51, 0x00, 0x60, \ + 0x02, 0x46, 0xc0, 0x54, \ + 0x45, 0xde, 0x40, 0x00, \ + 0x75, 0x38, 0xdf, 0xfc, \ + 0x0c, 0x09, 0xc8, 0x14, \ + 0xc0, 0x30, 0x6d, 0x1d, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x40, 0x00, 0x02, 0x16, \ + 0xc0, 0x00, 0x04, 0x06, \ + 0x48, 0x0c, 0x64, 0x08, \ + 0xdc, 0x00, 0x40, 0x60, \ + 0x68, 0x1c, 0x57, 0x88, \ + 0x46, 0x98, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0x68, 0x30, \ + 0x00, 0x60, 0x01, 0x71, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0x6e, 0xf1, 0x4c, 0x71, \ + 0x81, 0x11, 0x40, 0x15, \ + 0x6e, 0xe9, 0x4c, 0x71, \ + 0xb1, 0x21, 0x40, 0x15, \ + 0x6f, 0x18, 0xbe, 0x0e, \ + 0x41, 0xf6, 0xef, 0xb4, \ + 0x02, 0x60, 0x60, 0x0c, \ + 0xc0, 0xbe, 0x67, 0x10, \ + 0x6e, 0xed, 0x4c, 0x01, \ + 0x00, 0x60, 0x40, 0x15, \ + 0xc0, 0xa3, 0x65, 0xf1, \ + 0x67, 0xb5, 0x4c, 0x75, \ + 0x10, 0x60, 0x40, 0x05, \ + 0xc0, 0xa5, 0x67, 0x01, \ + 0x12, 0x60, 0x7c, 0x1e, \ + 0xc0, 0xa7, 0x66, 0x11, \ + 0x65, 0x21, 0x14, 0x60, \ + 0x16, 0x60, 0x40, 0xa9, \ + 0xc0, 0xab, 0x64, 0x31, \ + 0x44, 0x06, 0x01, 0x11, \ + 0x6d, 0x18, 0xe0, 0x00, \ + 0x01, 0x11, 0xef, 0xb4, \ + 0xe0, 0x00, 0x44, 0x06, \ + 0x6f, 0xb4, 0x6c, 0xc8, \ + 0x01, 0x11, 0x90, 0x0b, \ + 0xe0, 0x00, 0x44, 0x06, \ + 0x6f, 0xb4, 0x6c, 0x68, \ + 0x01, 0x11, 0x92, 0x0b, \ + 0xe0, 0x00, 0x44, 0x06, \ + 0x6f, 0xb4, 0x6c, 0x08, \ + 0x01, 0x11, 0x94, 0x0b, \ + 0xe0, 0x00, 0x44, 0x06, \ + 0x6f, 0xb4, 0x6b, 0xa8, \ + 0x01, 0x11, 0x96, 0x0b, \ + 0xe0, 0x00, 0x44, 0x06, \ + 0x6b, 0x38, 0xfc, 0x04, \ + 0x01, 0x66, 0xef, 0xb4, \ + 0x18, 0x60, 0x60, 0x0c, \ + 0xc0, 0xa1, 0x64, 0x80, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x01, 0x06, 0x40, 0x2f, \ + 0x00, 0x04, 0xe1, 0x4c, \ + 0x66, 0xf1, 0x02, 0x60, \ + 0x04, 0x60, 0xc0, 0xc7, \ + 0x40, 0xc6, 0x65, 0x01, \ + 0x67, 0x81, 0x06, 0x60, \ + 0x08, 0x60, 0xc0, 0xa5, \ + 0x40, 0xae, 0x67, 0x81, \ + 0x65, 0xa1, 0x0a, 0x60, \ + 0xbc, 0xce, 0xc0, 0xa9, \ + 0xef, 0xe8, 0x78, 0xe8, \ + 0x67, 0xbd, 0x0c, 0x05, \ + 0xa1, 0x0b, 0xc0, 0x0c, \ + 0x0d, 0x09, 0x0c, 0x39, \ + 0x04, 0x04, 0x40, 0x2f, \ + 0x1e, 0x05, 0xdb, 0x00, \ + 0x02, 0x66, 0x40, 0x04, \ + 0x6c, 0x78, 0xc0, 0x00, \ + 0x0c, 0x09, 0xc8, 0x14, \ + 0x40, 0x2f, 0x6d, 0x01, \ + 0xe0, 0xd0, 0x44, 0x06, \ + 0x65, 0x65, 0x0c, 0x19, \ + 0x4c, 0x75, 0xc0, 0x2f, \ + 0xc0, 0x05, 0x67, 0xcd, \ + 0x67, 0xc9, 0x4c, 0x75, \ + 0x40, 0x00, 0x40, 0x05, \ + 0x4c, 0x11, 0xf3, 0xf0, \ + 0x40, 0x2f, 0x6d, 0x0d, \ + 0x4c, 0x01, 0xc2, 0x09, \ + 0x40, 0x2f, 0x6d, 0x01, \ + 0xe0, 0x0c, 0x41, 0xf6, \ + 0x6d, 0x95, 0x4c, 0x01, \ + 0x00, 0x60, 0x40, 0x30, \ + 0xc0, 0x8f, 0x64, 0x01, \ + 0x6f, 0x99, 0x4c, 0x75, \ + 0x81, 0x11, 0x40, 0x06, \ + 0x6f, 0x95, 0x4c, 0x75, \ + 0x4c, 0x75, 0xc0, 0x06, \ + 0xc0, 0x05, 0x67, 0xc5, \ + 0x6f, 0x91, 0x4c, 0x75, \ + 0x65, 0x98, 0xc0, 0x06, \ + 0x4c, 0x71, 0xef, 0xb4, \ + 0x40, 0x23, 0x6b, 0xe9, \ + 0x64, 0xb1, 0x00, 0x60, \ + 0x4c, 0x71, 0xc0, 0x8e, \ + 0x40, 0x23, 0x6b, 0xed, \ + 0x4c, 0x71, 0x81, 0x11, \ + 0x40, 0x27, 0x60, 0x8d, \ + 0xe0, 0x0c, 0x04, 0x06, \ + 0xef, 0xb4, 0x64, 0x88, \ + 0x68, 0x99, 0x4c, 0x71, \ + 0x00, 0x60, 0x40, 0x23, \ + 0xc0, 0xc1, 0x67, 0x41, \ + 0x68, 0x9d, 0x4c, 0x71, \ + 0x81, 0x11, 0x40, 0x23, \ + 0x6f, 0xb4, 0x63, 0xc8, \ + 0xe0, 0x10, 0x04, 0x06, \ + 0x63, 0x91, 0x0c, 0x09, \ + 0x98, 0xf1, 0x40, 0x31, \ + 0x63, 0xf1, 0x0c, 0x19, \ + 0x4c, 0x75, 0xc0, 0x31, \ + 0xc0, 0x0a, 0x61, 0x1d, \ + 0x61, 0x19, 0x4c, 0x75, \ + 0x40, 0x00, 0x40, 0x0a, \ + 0x4c, 0x01, 0xf3, 0xf8, \ + 0x40, 0x31, 0x63, 0x91, \ + 0xf3, 0xf0, 0x42, 0x10, \ + 0x63, 0xf1, 0x4c, 0x11, \ + 0x0c, 0x0c, 0xc0, 0x31, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x01, 0x86, 0x00, 0x6b, \ + 0x0c, 0x1b, 0xe1, 0x04, \ + 0xc0, 0x17, 0x6e, 0x91, \ + 0x00, 0x1c, 0x80, 0x04, \ + 0x78, 0xb8, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0xe8, 0x38, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xe0, 0x44, 0x06, 0x88, \ + 0x6d, 0x0d, 0x00, 0x19, \ + 0x04, 0x10, 0xc0, 0x2f, \ + 0xe7, 0x2d, 0xec, 0x04, \ + 0x04, 0x10, 0x84, 0x55, \ + 0xe7, 0x2d, 0xec, 0x2c, \ + 0x04, 0x10, 0x84, 0x15, \ + 0xe5, 0x2d, 0xec, 0x10, \ + 0xad, 0x1b, 0x82, 0xd5, \ + 0xb3, 0x1b, 0x82, 0xc5, \ + 0xb5, 0x1b, 0x82, 0x85, \ + 0xda, 0x00, 0x5c, 0x08, \ + 0xe0, 0x54, 0x21, 0x48, \ + 0xe0, 0x3c, 0x29, 0xa8, \ + 0xe0, 0x4c, 0x3e, 0x08, \ + 0xe0, 0x44, 0x33, 0xe8, \ + 0xdd, 0x93, 0x85, 0xc5, \ + 0x6d, 0x01, 0x00, 0x19, \ + 0x81, 0x21, 0x40, 0x2f, \ + 0x6d, 0x0d, 0x40, 0x21, \ + 0x42, 0x10, 0xc0, 0x2f, \ + 0x40, 0x11, 0xf3, 0xf0, \ + 0xc0, 0x2f, 0x6d, 0x01, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x61, 0x48, 0x41, 0xc6, \ + 0x80, 0x14, 0x83, 0x31, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x04, 0x26, 0xf3, 0xf4, \ + 0xe1, 0x14, 0x03, 0xc6, \ + 0x08, 0x36, 0x84, 0x24, \ + 0x84, 0x14, 0x81, 0x21, \ + 0x80, 0x04, 0x84, 0x26, \ + 0xc4, 0x19, 0x80, 0x18, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0x61, 0x14, 0x01, 0xc6, \ + 0x80, 0x04, 0x83, 0x11, \ + 0xc1, 0x83, 0x80, 0x16, \ + 0xdd, 0x93, 0xc7, 0x13, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x2f, 0x6d, 0x0d, \ + 0x82, 0x55, 0xb3, 0x0b, \ + 0xe0, 0x00, 0x40, 0x96, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x8c, 0x0b, 0x40, 0x2f, \ + 0xe8, 0x48, 0x64, 0x48, \ + 0x40, 0x66, 0x81, 0x95, \ + 0x4c, 0x01, 0xe0, 0x00, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x67, 0xf8, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x48, \ + 0xc0, 0x15, 0x61, 0xd1, \ + 0x6d, 0x29, 0x0c, 0x19, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x82, 0x01, 0xe6, 0x86, \ + 0x67, 0xf5, 0x01, 0x0a, \ + 0x00, 0x04, 0xc0, 0x03, \ + 0x4c, 0x03, 0x9a, 0x80, \ + 0x80, 0x18, 0x06, 0x89, \ + 0x0c, 0x39, 0x84, 0x25, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0xe0, 0x3c, 0x41, 0xd6, \ + 0x66, 0x89, 0x4c, 0x03, \ + 0x00, 0x60, 0x40, 0x18, \ + 0xc0, 0x54, 0x66, 0x81, \ + 0x44, 0xb6, 0x01, 0x11, \ + 0x65, 0x58, 0xe0, 0x00, \ + 0x0c, 0x0b, 0xe8, 0x14, \ + 0x65, 0xc1, 0x12, 0x60, \ + 0x79, 0x58, 0xc0, 0x54, \ + 0x0c, 0x09, 0xe8, 0x3c, \ + 0xc0, 0x2f, 0x6d, 0x0d, \ + 0x98, 0x15, 0xb3, 0x0b, \ + 0x60, 0x00, 0x40, 0x96, \ + 0xe1, 0x4c, 0x03, 0x06, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x04, 0x60, 0x40, 0x2f, \ + 0xc0, 0x54, 0x65, 0x51, \ + 0x02, 0x60, 0x04, 0x04, \ + 0xc0, 0xa1, 0x65, 0x61, \ + 0x06, 0x60, 0x3c, 0x1e, \ + 0xc0, 0x57, 0x66, 0xd1, \ + 0x67, 0x81, 0x08, 0x60, \ + 0x0a, 0x60, 0x40, 0xb2, \ + 0xc0, 0xac, 0x66, 0xb1, \ + 0x6f, 0xbc, 0x64, 0xe8, \ + 0x0c, 0x05, 0x92, 0x1b, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0x63, 0x8d, 0x0c, 0x15, \ + 0x00, 0x0a, 0xc0, 0x0a, \ + 0x00, 0x16, 0xe1, 0x74, \ + 0x4c, 0x05, 0x80, 0x00, \ + 0x00, 0x0a, 0x03, 0x7d, \ + 0xe2, 0x00, 0x02, 0x0a, \ + 0x0c, 0x25, 0x86, 0x05, \ + 0x40, 0x0a, 0x63, 0x95, \ + 0x66, 0x81, 0x00, 0x60, \ + 0x0c, 0x45, 0xc0, 0x54, \ + 0xc0, 0x0a, 0x63, 0x99, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x4c, 0x23, 0xc0, 0x2f, \ + 0x40, 0x0a, 0x69, 0x4d, \ + 0xfb, 0x00, 0x04, 0x14, \ + 0x69, 0x51, 0x4c, 0x43, \ + 0x8b, 0x11, 0x40, 0x0a, \ + 0xe8, 0x14, 0x60, 0x88, \ + 0x63, 0x7d, 0x0c, 0x15, \ + 0xa1, 0x1b, 0xc0, 0x0a, \ + 0x0c, 0x25, 0xac, 0x45, \ + 0x40, 0x0a, 0x63, 0x85, \ + 0x66, 0x51, 0x00, 0x60, \ + 0x0c, 0x45, 0xc0, 0x54, \ + 0xc0, 0x0a, 0x63, 0x89, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x4c, 0x23, 0xc0, 0x2f, \ + 0x40, 0x0a, 0x69, 0x35, \ + 0xfb, 0x00, 0x04, 0x14, \ + 0x69, 0x39, 0x4c, 0x43, \ + 0x8b, 0x11, 0x40, 0x0a, \ + 0xe8, 0x14, 0x55, 0xe8, \ + 0x40, 0x66, 0x93, 0xa5, \ + 0x14, 0x60, 0x60, 0x00, \ + 0xc0, 0xbd, 0x66, 0xe0, \ + 0x67, 0x81, 0x10, 0x60, \ + 0x17, 0x06, 0x40, 0xb2, \ + 0x4c, 0x01, 0xe1, 0x4c, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x67, 0x81, 0x0e, 0x60, \ + 0x2c, 0x04, 0xc0, 0xa5, \ + 0x65, 0x51, 0x06, 0x60, \ + 0x3c, 0x7e, 0xc0, 0x54, \ + 0x66, 0xd1, 0x08, 0x60, \ + 0x12, 0x1b, 0xc0, 0x57, \ + 0x78, 0x98, 0x94, 0x2b, \ + 0x90, 0x5b, 0x6f, 0xbc, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x00, 0x16, 0xe1, 0x78, \ + 0x4c, 0x05, 0x80, 0x00, \ + 0x80, 0x0a, 0x07, 0x85, \ + 0x0c, 0x05, 0x86, 0xe5, \ + 0xc0, 0x0a, 0x67, 0x85, \ + 0x86, 0x55, 0xa1, 0x0b, \ + 0x08, 0x60, 0x2c, 0x04, \ + 0xc0, 0xab, 0x64, 0xb1, \ + 0x90, 0x2b, 0x14, 0x1b, \ + 0x6f, 0xbc, 0x7a, 0xe8, \ + 0x0c, 0x05, 0x8e, 0x3b, \ + 0xc0, 0x0a, 0x67, 0x85, \ + 0x16, 0x05, 0xa1, 0x0b, \ + 0x04, 0x60, 0x00, 0x00, \ + 0x80, 0x9c, 0x04, 0x80, \ + 0x05, 0x61, 0x06, 0x60, \ + 0x42, 0x80, 0x00, 0xac, \ + 0x58, 0x78, 0x9c, 0x00, \ + 0x0c, 0x15, 0x8f, 0xf4, \ + 0xc0, 0x0a, 0x67, 0x95, \ + 0xe2, 0x00, 0x02, 0x0a, \ + 0x0c, 0x25, 0x86, 0x05, \ + 0x40, 0x0a, 0x67, 0x9d, \ + 0x66, 0x81, 0x00, 0x60, \ + 0x0c, 0x45, 0xc0, 0x54, \ + 0xc0, 0x0a, 0x67, 0xa1, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x4c, 0x23, 0xc0, 0x2f, \ + 0x40, 0x0a, 0x69, 0x4d, \ + 0xfb, 0x00, 0x04, 0x14, \ + 0x69, 0x51, 0x4c, 0x43, \ + 0x8b, 0x11, 0x40, 0x0a, \ + 0xe8, 0x14, 0x58, 0x88, \ + 0x67, 0x85, 0x0c, 0x15, \ + 0xa1, 0x1b, 0xc0, 0x0a, \ + 0x0c, 0x25, 0x86, 0x05, \ + 0x40, 0x0a, 0x67, 0x8d, \ + 0x66, 0x51, 0x00, 0x60, \ + 0x0c, 0x45, 0xc0, 0x54, \ + 0xc0, 0x0a, 0x67, 0x91, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x4c, 0x23, 0xc0, 0x2f, \ + 0x40, 0x0a, 0x69, 0x35, \ + 0xfb, 0x00, 0x04, 0x14, \ + 0x69, 0x39, 0x4c, 0x43, \ + 0x8b, 0x11, 0x40, 0x0a, \ + 0xe8, 0x14, 0x4d, 0xe8, \ + 0x64, 0x11, 0x10, 0x60, \ + 0x00, 0x60, 0x40, 0xa6, \ + 0xc0, 0xae, 0x67, 0x81, \ + 0x60, 0x10, 0x04, 0x06, \ + 0x43, 0x38, 0x90, 0x1b, \ + 0x00, 0x60, 0xef, 0xb4, \ + 0x40, 0xaf, 0x67, 0x81, \ + 0xe0, 0x10, 0x04, 0x06, \ + 0x6f, 0xb4, 0x42, 0xc8, \ + 0x00, 0x60, 0x90, 0x1b, \ + 0x40, 0xa9, 0x65, 0xa1, \ + 0x42, 0x58, 0x99, 0x21, \ + 0x8e, 0x1b, 0x6f, 0xb4, \ + 0x68, 0x38, 0x53, 0xc8, \ + 0x0c, 0x19, 0x8c, 0x0b, \ + 0x40, 0x30, 0x6d, 0x95, \ + 0xff, 0xf8, 0x00, 0x0e, \ + 0xe3, 0xf0, 0x02, 0x0a, \ + 0xc4, 0x04, 0x00, 0x10, \ + 0x6d, 0x95, 0x4c, 0x01, \ + 0x8c, 0x0b, 0x40, 0x30, \ + 0xe8, 0x04, 0x54, 0x88, \ + 0x64, 0x51, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x31, \ + 0x00, 0x06, 0xe0, 0x98, \ + 0x7f, 0x98, 0xc0, 0x04, \ + 0x0c, 0x0b, 0xc8, 0x14, \ + 0x0c, 0x0c, 0x88, 0xf1, \ + 0xcf, 0x75, 0xf2, 0x7c, \ + 0x66, 0x15, 0x00, 0x8a, \ + 0x80, 0xf5, 0xc0, 0x03, \ + 0x61, 0x18, 0x00, 0x46, \ + 0x80, 0x04, 0x87, 0x11, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0x65, 0x61, 0x00, 0x8a, \ + 0x80, 0xf5, 0xc0, 0x02, \ + 0x61, 0x18, 0x00, 0x46, \ + 0x80, 0x04, 0x85, 0x11, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0x61, 0x18, 0x02, 0x46, \ + 0x64, 0xac, 0x01, 0x4a, \ + 0x84, 0x14, 0xc0, 0x01, \ + 0xe2, 0x00, 0x00, 0x0c, \ + 0xdd, 0x93, 0x84, 0x06, \ + 0x66, 0x61, 0x02, 0x8a, \ + 0x02, 0x86, 0xc0, 0x03, \ + 0x04, 0x66, 0x01, 0x04, \ + 0x02, 0x15, 0x80, 0x00, \ + 0x86, 0xc5, 0x80, 0x00, \ + 0x64, 0x99, 0x02, 0x8a, \ + 0x02, 0x86, 0xc0, 0x03, \ + 0x04, 0x56, 0x01, 0x04, \ + 0x02, 0x15, 0x80, 0x00, \ + 0x84, 0x85, 0x80, 0x00, \ + 0x66, 0xc9, 0x02, 0x8a, \ + 0x02, 0x86, 0xc0, 0x02, \ + 0x04, 0x46, 0x01, 0x04, \ + 0x02, 0x15, 0x80, 0x00, \ + 0x82, 0x45, 0x80, 0x00, \ + 0x64, 0x95, 0x03, 0x0a, \ + 0x02, 0x86, 0x40, 0x02, \ + 0x04, 0x14, 0xe1, 0x04, \ + 0x04, 0x26, 0x87, 0x21, \ + 0x04, 0x26, 0x80, 0x00, \ + 0xe1, 0x04, 0x02, 0x86, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x66, 0x19, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x2f, \ + 0x03, 0x0a, 0xc4, 0x17, \ + 0xc0, 0x0f, 0x66, 0x81, \ + 0x02, 0x8a, 0x86, 0xf5, \ + 0xc0, 0x0f, 0x64, 0xf5, \ + 0x01, 0x04, 0x02, 0x46, \ + 0x80, 0x00, 0x04, 0x06, \ + 0x80, 0x00, 0x02, 0x15, \ + 0x02, 0x8a, 0x84, 0x85, \ + 0xc0, 0x0c, 0x66, 0xcd, \ + 0x01, 0x04, 0x02, 0x46, \ + 0x80, 0x00, 0x04, 0x16, \ + 0x80, 0x00, 0x02, 0x15, \ + 0x03, 0x0a, 0x82, 0x45, \ + 0x40, 0x0a, 0x64, 0xa1, \ + 0xe1, 0x04, 0x02, 0x46, \ + 0x85, 0x21, 0x04, 0x14, \ + 0x80, 0x00, 0x04, 0x36, \ + 0x02, 0x46, 0x84, 0x26, \ + 0x84, 0x14, 0xe1, 0x04, \ + 0x40, 0x11, 0x84, 0x18, \ + 0xc0, 0x2f, 0x66, 0x15, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x61, 0x18, 0x02, 0x46, \ + 0xe1, 0x14, 0x45, 0x86, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x8c, 0x16, 0x88, 0x34, \ + 0x84, 0x18, 0x88, 0x14, \ + 0x66, 0x1d, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x2f, \ + 0xe1, 0x18, 0x04, 0x86, \ + 0x88, 0x28, 0x88, 0x24, \ + 0x00, 0x15, 0xa1, 0x2b, \ + 0x00, 0x0a, 0x01, 0x51, \ + 0xc1, 0x18, 0x04, 0x46, \ + 0xc0, 0x00, 0x04, 0x25, \ + 0x81, 0xb0, 0x02, 0x14, \ + 0x40, 0x00, 0x04, 0x29, \ + 0x84, 0x04, 0x02, 0x10, \ + 0xc4, 0x04, 0x04, 0x20, \ + 0xc0, 0x0a, 0x42, 0x1a, \ + 0x6e, 0x79, 0x40, 0x13, \ + 0xdd, 0x93, 0xc0, 0x17, \ + 0x70, 0x01, 0x00, 0x06, \ + 0x02, 0x06, 0x40, 0x20, \ + 0xff, 0xc0, 0x60, 0x01, \ + 0xdd, 0x93, 0x80, 0x1e, \ + 0xf8, 0x04, 0x04, 0x0c, \ + 0x61, 0x48, 0x46, 0x06, \ + 0xe1, 0x48, 0x58, 0x46, \ + 0x06, 0x06, 0x0c, 0x44, \ + 0xc0, 0x20, 0x70, 0x01, \ + 0xa1, 0x0b, 0x0c, 0x54, \ + 0xb0, 0x44, 0x90, 0x68, \ + 0x08, 0x06, 0x10, 0xc8, \ + 0xff, 0xc0, 0x60, 0x01, \ + 0x6d, 0x05, 0x04, 0x79, \ + 0x88, 0x5f, 0x40, 0x2f, \ + 0xd0, 0xc7, 0xaa, 0x6f, \ + 0xb8, 0x6f, 0x60, 0x77, \ + 0xf9, 0x9c, 0x40, 0x62, \ + 0x0d, 0x95, 0x04, 0x19, \ + 0x02, 0x14, 0xc0, 0x30, \ + 0x00, 0xb5, 0xc0, 0xe0, \ + 0xd0, 0x84, 0x48, 0x42, \ + 0x80, 0xc5, 0xa1, 0x1b, \ + 0x7f, 0xfd, 0x43, 0xfe, \ + 0x82, 0x0f, 0xc0, 0x3f, \ + 0x8c, 0x0e, 0xa8, 0x0f, \ + 0xf0, 0x04, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xb0, 0x1e, 0xf0, \ + 0x68, 0x6d, 0x00, 0x19, \ + 0x4f, 0x06, 0x40, 0x31, \ + 0x04, 0x46, 0xe1, 0x98, \ + 0x42, 0x0a, 0xe1, 0x4c, \ + 0x81, 0x11, 0x60, 0x80, \ + 0x02, 0x16, 0x1c, 0x1e, \ + 0x40, 0x11, 0xc0, 0x00, \ + 0xc0, 0x30, 0x0d, 0x8d, \ + 0x00, 0x86, 0x7c, 0x0e, \ + 0x80, 0x04, 0xe1, 0x4c, \ + 0x88, 0x04, 0x80, 0x64, \ + 0x61, 0xe0, 0x10, 0x64, \ + 0xe1, 0xc0, 0x06, 0x64, \ + 0x60, 0xe0, 0x08, 0x84, \ + 0xf0, 0x10, 0x06, 0x0a, \ + 0xfc, 0xc0, 0x02, 0x44, \ + 0x08, 0x14, 0x80, 0x1e, \ + 0xfc, 0xc4, 0x04, 0x44, \ + 0x84, 0x2e, 0xbe, 0x4e, \ + 0x7c, 0x04, 0xaa, 0x25, \ + 0xf0, 0x08, 0x06, 0x0a, \ + 0x80, 0x00, 0x0b, 0x88, \ + 0xf0, 0x0c, 0x06, 0x0a, \ + 0x80, 0x00, 0x10, 0xc8, \ + 0xf0, 0x04, 0x06, 0x0a, \ + 0x40, 0x00, 0x17, 0xb8, \ + 0xdc, 0x00, 0x4c, 0x00, \ + 0x7b, 0x80, 0x00, 0x64, \ + 0x72, 0xfc, 0x43, 0x86, \ + 0x52, 0x12, 0xc0, 0x20, \ + 0x40, 0x46, 0x71, 0x80, \ + 0x80, 0x0a, 0xe1, 0x90, \ + 0xfe, 0x0e, 0x9c, 0x84, \ + 0x04, 0x10, 0x91, 0x05, \ + 0x77, 0x2d, 0xec, 0x08, \ + 0xe1, 0x90, 0x04, 0x04, \ + 0x73, 0xfd, 0x44, 0x20, \ + 0x02, 0x16, 0x40, 0x0f, \ + 0x4c, 0x1a, 0x80, 0x00, \ + 0xe1, 0x07, 0x66, 0x8a, \ + 0xf9, 0x80, 0x42, 0x62, \ + 0xf8, 0x0c, 0x02, 0x4a, \ + 0x44, 0x00, 0x96, 0x95, \ + 0x42, 0x62, 0x70, 0x0c, \ + 0x04, 0x63, 0xf0, 0x80, \ + 0xf8, 0x0c, 0x02, 0x4a, \ + 0x06, 0x60, 0x15, 0x2e, \ + 0x04, 0x0c, 0xec, 0x04, \ + 0x02, 0x64, 0x62, 0x00, \ + 0x4c, 0x22, 0xe1, 0x88, \ + 0x02, 0x32, 0x70, 0x84, \ + 0x13, 0x1e, 0xec, 0x0c, \ + 0xec, 0x04, 0x16, 0x60, \ + 0x81, 0x71, 0x7e, 0x14, \ + 0x89, 0x9e, 0x91, 0x3e, \ + 0xc5, 0x07, 0x82, 0x03, \ + 0x14, 0x04, 0x0f, 0x0e, \ + 0x16, 0x03, 0xe0, 0x88, \ + 0x0d, 0x0e, 0x90, 0xa3, \ + 0x90, 0x03, 0xc4, 0x07, \ + 0x87, 0x35, 0x8b, 0x0e, \ + 0x86, 0x45, 0xa1, 0x7b, \ + 0x1e, 0x15, 0x8f, 0xbb, \ + 0x12, 0x05, 0xc0, 0x1c, \ + 0x08, 0xd5, 0xc0, 0x00, \ + 0xd0, 0x9c, 0x42, 0x12, \ + 0x86, 0x09, 0x91, 0x04, \ + 0x06, 0x25, 0xa7, 0x0b, \ + 0x80, 0x00, 0x00, 0x06, \ + 0x04, 0xa5, 0xa5, 0x0b, \ + 0x80, 0x0c, 0x41, 0xf6, \ + 0x41, 0xfe, 0x23, 0x0b, \ + 0xc0, 0x3f, 0x7f, 0xfd, \ + 0x41, 0xf6, 0x01, 0xe5, \ + 0x95, 0x04, 0x8f, 0xfc, \ + 0x53, 0x98, 0x93, 0x14, \ + 0xa8, 0x14, 0xe8, 0x00, \ + 0x80, 0x1f, 0xa4, 0x24, \ + 0x79, 0x88, 0x40, 0x12, \ + 0x81, 0xa5, 0x94, 0x1b, \ + 0xa4, 0x24, 0x8b, 0x14, \ + 0x80, 0x1f, 0x84, 0x14, \ + 0x40, 0x12, 0x0d, 0x14, \ + 0xc4, 0x17, 0xf9, 0x88, \ + 0x08, 0x91, 0x90, 0x13, \ + 0x84, 0x0e, 0x82, 0x71, \ + 0xf2, 0xb5, 0x8f, 0x6b, \ + 0x83, 0x15, 0x89, 0x94, \ + 0xc5, 0x67, 0x7e, 0x14, \ + 0x02, 0x03, 0x92, 0x2b, \ + 0x40, 0x00, 0x8c, 0x1b, \ + 0xc1, 0xff, 0x73, 0xf1, \ + 0x90, 0x03, 0x01, 0x65, \ + 0x02, 0x10, 0x49, 0x34, \ + 0xc1, 0x3e, 0xec, 0x04, \ + 0xfe, 0x75, 0xa1, 0x1b, \ + 0x8c, 0x93, 0xc4, 0x67, \ + 0x42, 0x00, 0xe5, 0x04, \ + 0xbf, 0x1b, 0xf0, 0x3c, \ + 0x7c, 0x64, 0xde, 0xb5, \ + 0xe1, 0x90, 0x42, 0x86, \ + 0x65, 0x11, 0x0c, 0x05, \ + 0x84, 0x0e, 0xc0, 0x0c, \ + 0x7c, 0x64, 0x9b, 0xf5, \ + 0xe1, 0x4c, 0x02, 0x46, \ + 0x83, 0x21, 0x04, 0x14, \ + 0x83, 0x41, 0x03, 0x31, \ + 0x6d, 0x01, 0x0c, 0x09, \ + 0x40, 0x00, 0xc0, 0x2f, \ + 0x4c, 0x01, 0xf3, 0xd0, \ + 0x40, 0x2f, 0x6d, 0x01, \ + 0x68, 0xf8, 0x8c, 0x0b, \ + 0x99, 0xc5, 0xef, 0xf8, \ + 0x7b, 0x80, 0x00, 0x64, \ + 0x72, 0xfc, 0x43, 0x86, \ + 0x44, 0x12, 0xc0, 0x20, \ + 0x47, 0xc6, 0x71, 0x80, \ + 0xc0, 0x20, 0x72, 0xfc, \ + 0x71, 0x80, 0x40, 0x32, \ + 0x03, 0x35, 0x89, 0x81, \ + 0x18, 0x40, 0x84, 0x3b, \ + 0xc9, 0x57, 0x6c, 0x08, \ + 0x4a, 0x50, 0x77, 0xcd, \ + 0xc0, 0x0f, 0x73, 0xfd, \ + 0x00, 0x00, 0x08, 0x16, \ + 0x48, 0x4a, 0x88, 0x31, \ + 0x0a, 0x84, 0x66, 0x96, \ + 0x88, 0x53, 0xfb, 0x80, \ + 0xe4, 0x10, 0x10, 0x50, \ + 0x48, 0x50, 0x8c, 0x54, \ + 0xbf, 0x4b, 0xf0, 0x3c, \ + 0x08, 0x44, 0xfa, 0x75, \ + 0x73, 0xf1, 0x44, 0x80, \ + 0x46, 0x12, 0xc0, 0xff, \ + 0x44, 0x46, 0x71, 0x88, \ + 0x8c, 0x4e, 0xe1, 0x90, \ + 0x88, 0x2a, 0x9c, 0x14, \ + 0x18, 0x50, 0x85, 0x25, \ + 0x88, 0x31, 0x6c, 0x08, \ + 0x18, 0x44, 0x77, 0xcd, \ + 0x58, 0xc0, 0xe1, 0x90, \ + 0x40, 0x0f, 0x73, 0xfd, \ + 0x80, 0x00, 0x0a, 0x16, \ + 0x66, 0xb2, 0x4a, 0x5a, \ + 0x04, 0x43, 0xe1, 0x47, \ + 0x48, 0x40, 0x86, 0xcb, \ + 0x41, 0xff, 0x73, 0xf1, \ + 0x02, 0x43, 0xc5, 0x57, \ + 0x81, 0x55, 0x8a, 0x6b, \ + 0x0c, 0x60, 0x51, 0x74, \ + 0xf1, 0x7e, 0xec, 0x04, \ + 0xfe, 0x75, 0xa1, 0x6b, \ + 0xc4, 0x57, 0x41, 0x44, \ + 0x8c, 0x4e, 0x8a, 0x33, \ + 0xf0, 0x3c, 0x4a, 0x40, \ + 0xf6, 0x95, 0xbf, 0x5b, \ + 0x8f, 0x55, 0xfc, 0x64, \ + 0x68, 0x00, 0x4c, 0x48, \ + 0x12, 0x09, 0x80, 0x9b, \ + 0x40, 0x2f, 0x6d, 0x01, \ + 0xe8, 0x00, 0x0c, 0x0a, \ + 0x6e, 0xe5, 0x52, 0x81, \ + 0x40, 0x00, 0xc0, 0x15, \ + 0xd2, 0x09, 0xf3, 0x88, \ + 0x6d, 0x01, 0x52, 0x01, \ + 0x00, 0x64, 0x40, 0x2f, \ + 0x8e, 0x05, 0xfb, 0x80, \ + 0x72, 0xfd, 0x49, 0x86, \ + 0x44, 0x46, 0x40, 0x20, \ + 0x43, 0xc6, 0xe1, 0x90, \ + 0x40, 0x20, 0x72, 0xfd, \ + 0x1c, 0x14, 0xa0, 0x43, \ + 0xf1, 0x80, 0x40, 0x12, \ + 0x85, 0x55, 0x88, 0x2a, \ + 0x6c, 0x08, 0x0a, 0x40, \ + 0x77, 0x5d, 0x80, 0x6b, \ + 0xe1, 0x90, 0x0a, 0x34, \ + 0x73, 0xfd, 0x4a, 0x50, \ + 0x08, 0x16, 0x40, 0x0f, \ + 0x48, 0x4a, 0x80, 0x00, \ + 0xe1, 0x37, 0x66, 0x96, \ + 0x4a, 0x30, 0x84, 0x33, \ + 0x41, 0xff, 0x73, 0xf1, \ + 0x06, 0x00, 0xc5, 0x47, \ + 0x82, 0x53, 0x64, 0x10, \ + 0x88, 0xcb, 0x01, 0x65, \ + 0x18, 0xc0, 0x59, 0x84, \ + 0xd5, 0x8e, 0xec, 0x04, \ + 0xfe, 0x75, 0xa1, 0xcb, \ + 0xe0, 0x88, 0x0a, 0x44, \ + 0x70, 0x94, 0x48, 0x02, \ + 0xf0, 0x94, 0x40, 0x32, \ + 0x48, 0x30, 0x90, 0x34, \ + 0xbf, 0x4b, 0xf0, 0x3c, \ + 0x7c, 0x64, 0xf6, 0x15, \ + 0xe1, 0x48, 0x40, 0xc6, \ + 0x0c, 0x19, 0x80, 0x04, \ + 0xc0, 0x15, 0x62, 0x21, \ + 0xc4, 0x17, 0x80, 0x28, \ + 0x44, 0x20, 0x08, 0x19, \ + 0xa2, 0x2f, 0xf3, 0xec, \ + 0x0c, 0x09, 0x80, 0x26, \ + 0xc0, 0x2f, 0x65, 0x65, \ + 0x6d, 0x0d, 0x4c, 0x01, \ + 0x8c, 0x0b, 0x40, 0x2f, \ + 0xe8, 0x00, 0x49, 0xe8, \ + 0x00, 0x60, 0x1c, 0x44, \ + 0xc0, 0x54, 0x65, 0x11, \ + 0x7b, 0x80, 0x04, 0x04, \ + 0x19, 0x11, 0x81, 0x01, \ + 0x72, 0xfd, 0x46, 0x46, \ + 0x68, 0x38, 0xc0, 0x20, \ + 0x1f, 0x11, 0xe8, 0x00, \ + 0x80, 0x1e, 0xa9, 0x81, \ + 0x81, 0x81, 0x81, 0x25, \ + 0x02, 0x46, 0x3e, 0x24, \ + 0x04, 0x14, 0xe1, 0x4c, \ + 0xfb, 0x80, 0x00, 0x84, \ + 0x40, 0x00, 0xa4, 0x0f, \ + 0xc3, 0x00, 0x7c, 0x01, \ + 0x0c, 0x09, 0x84, 0x0e, \ + 0xc0, 0x31, 0x68, 0x6d, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x0c, 0x19, 0x80, 0xe5, \ + 0xc0, 0x2f, 0x6d, 0x01, \ + 0xe0, 0x8c, 0x42, 0x0a, \ + 0xd8, 0xf1, 0x80, 0xb5, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0x0c, 0x0b, 0xdd, 0x93, \ + 0x0c, 0x0c, 0xd8, 0xf1, \ + 0x28, 0xd8, 0xf2, 0x7c, \ + 0xc1, 0xc3, 0xe0, 0x00, \ + 0x84, 0x65, 0xa5, 0x0b, \ + 0x84, 0xe5, 0xa3, 0x0b, \ + 0x00, 0x0e, 0x21, 0x0b, \ + 0x88, 0x15, 0xff, 0xf0, \ + 0x86, 0xe5, 0xa7, 0x1b, \ + 0x86, 0xe5, 0xa5, 0x1b, \ + 0x41, 0xfe, 0x23, 0x1b, \ + 0xc0, 0x3f, 0x7f, 0xfd, \ + 0x5d, 0x93, 0x84, 0xf5, \ + 0xef, 0xfc, 0x41, 0xf6, \ + 0x41, 0xfe, 0x25, 0x1b, \ + 0x5d, 0x93, 0xf0, 0x0c, \ + 0xd0, 0x00, 0x00, 0x0e, \ + 0x41, 0xf6, 0x25, 0x1b, \ + 0xff, 0xc0, 0x60, 0x0d, \ + 0x0f, 0xfd, 0x41, 0xf6, \ + 0xa7, 0x1b, 0x7f, 0xc0, \ + 0x1a, 0x00, 0x5c, 0x08, \ + 0x00, 0x00, 0x00, 0x06, \ + 0xdd, 0x93, 0xbf, 0xc0, \ + 0x81, 0x01, 0x5d, 0x93, \ + 0x41, 0xf6, 0x5d, 0x93, \ + 0xc1, 0xc3, 0xe0, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x40, 0x06, 0x00, 0x6b, \ + 0x80, 0x14, 0xe1, 0x48, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x84, 0x26, 0xf3, 0xbc, \ + 0x84, 0x28, 0x80, 0x14, \ + 0xf3, 0x7c, 0x44, 0x20, \ + 0x80, 0x14, 0x84, 0x26, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x84, 0x26, 0xf2, 0xfc, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xdc, 0x42, 0x10, \ + 0x40, 0x46, 0x00, 0x16, \ + 0x80, 0x14, 0xe1, 0x48, \ + 0x6d, 0x01, 0x0c, 0x29, \ + 0x84, 0x38, 0xc0, 0x2f, \ + 0x44, 0x20, 0xca, 0x27, \ + 0x46, 0x30, 0xf0, 0x80, \ + 0xa4, 0x3f, 0xf3, 0x7c, \ + 0x80, 0x14, 0x84, 0x36, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x84, 0x26, 0xf3, 0xf8, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xec, 0x42, 0x10, \ + 0x77, 0x78, 0x80, 0x16, \ + 0x01, 0x01, 0xef, 0xf8, \ + 0x8c, 0x2b, 0x83, 0x11, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0xfc, 0x19, 0x68, \ + 0xf8, 0x04, 0x04, 0x0c, \ + 0x61, 0x51, 0x00, 0x19, \ + 0x00, 0x29, 0xc0, 0x15, \ + 0xc0, 0x15, 0x61, 0x55, \ + 0x62, 0x01, 0x00, 0x39, \ + 0x00, 0x49, 0xc0, 0x15, \ + 0xc0, 0x15, 0x62, 0x11, \ + 0x66, 0x86, 0x4a, 0x2a, \ + 0xe0, 0x00, 0x44, 0x06, \ + 0xec, 0x04, 0x02, 0x30, \ + 0x62, 0x1d, 0x00, 0x19, \ + 0xe2, 0x1d, 0x40, 0x15, \ + 0x6f, 0x09, 0x40, 0x51, \ + 0x40, 0x41, 0xc0, 0x15, \ + 0xc0, 0x15, 0x6f, 0x0d, \ + 0x00, 0xcb, 0xb8, 0x15, \ + 0x40, 0x0a, 0x68, 0xf1, \ + 0x04, 0x46, 0xa5, 0x3b, \ + 0x45, 0x46, 0xe0, 0x04, \ + 0x58, 0x0a, 0xc0, 0x00, \ + 0x0c, 0x54, 0xe0, 0x80, \ + 0x04, 0x20, 0x40, 0x88, \ + 0x4c, 0x60, 0xc4, 0x10, \ + 0x40, 0x03, 0x13, 0xf1, \ + 0xdb, 0x80, 0x04, 0x24, \ + 0x50, 0x98, 0x44, 0x22, \ + 0xe0, 0x84, 0x58, 0x0a, \ + 0x40, 0x88, 0x0c, 0x54, \ + 0xdb, 0x80, 0x04, 0x24, \ + 0x13, 0xf1, 0x4c, 0x60, \ + 0x44, 0x22, 0xc0, 0x03, \ + 0x04, 0x20, 0xd0, 0x98, \ + 0x58, 0x0a, 0x44, 0x10, \ + 0x0c, 0x54, 0xe0, 0x88, \ + 0x04, 0x24, 0x40, 0x84, \ + 0x4c, 0x60, 0xdb, 0x80, \ + 0xc0, 0x01, 0x13, 0xf9, \ + 0xd0, 0x98, 0x44, 0x22, \ + 0x44, 0x10, 0x04, 0x20, \ + 0xe0, 0x8c, 0x58, 0x0a, \ + 0x5a, 0x80, 0x0c, 0x54, \ + 0xdb, 0x80, 0x04, 0x24, \ + 0xd0, 0x98, 0x44, 0x22, \ + 0x44, 0x10, 0x04, 0x20, \ + 0xe0, 0x90, 0x58, 0x0a, \ + 0x5a, 0x80, 0x0c, 0x54, \ + 0xdb, 0x80, 0x04, 0x24, \ + 0xd0, 0x98, 0x44, 0x22, \ + 0x44, 0x10, 0x04, 0x20, \ + 0xe0, 0x94, 0x58, 0x0a, \ + 0x5a, 0x80, 0x0c, 0x54, \ + 0xdb, 0x80, 0x04, 0x24, \ + 0xd0, 0x98, 0x44, 0x22, \ + 0x44, 0x10, 0x04, 0x20, \ + 0xe0, 0x98, 0x58, 0x0a, \ + 0x5a, 0x80, 0x0c, 0x54, \ + 0xdb, 0x80, 0x04, 0x24, \ + 0xd0, 0x98, 0x44, 0x22, \ + 0x44, 0x10, 0x04, 0x20, \ + 0xe0, 0x9c, 0x58, 0x0a, \ + 0x5a, 0x80, 0x18, 0x54, \ + 0xdb, 0x80, 0x04, 0x24, \ + 0x68, 0xf5, 0x00, 0xcb, \ + 0x44, 0x22, 0x40, 0x0a, \ + 0x04, 0x20, 0xd0, 0xb0, \ + 0x58, 0x0a, 0xc4, 0x10, \ + 0x82, 0x85, 0xe0, 0x80, \ + 0x60, 0x88, 0x0c, 0x54, \ + 0xfb, 0x80, 0x04, 0x24, \ + 0x73, 0xf1, 0x4c, 0x60, \ + 0x4c, 0x6a, 0xc0, 0x03, \ + 0x8c, 0x23, 0xe6, 0x92, \ + 0x58, 0x0a, 0x88, 0x21, \ + 0x82, 0x85, 0xe0, 0x88, \ + 0x60, 0x88, 0x0c, 0x54, \ + 0xfb, 0x80, 0x04, 0x24, \ + 0x73, 0xf1, 0x4c, 0x60, \ + 0x4c, 0x6a, 0xc0, 0x03, \ + 0x8c, 0x23, 0xe6, 0x92, \ + 0x58, 0x0a, 0x88, 0x21, \ + 0x82, 0x85, 0xe0, 0x8c, \ + 0x60, 0x88, 0x0c, 0x54, \ + 0xfb, 0x80, 0x04, 0x24, \ + 0x73, 0xf1, 0x4c, 0x60, \ + 0x4c, 0x6a, 0xc0, 0x03, \ + 0x8c, 0x23, 0xe6, 0x92, \ + 0x58, 0x0a, 0x88, 0x21, \ + 0x82, 0x85, 0xe0, 0x90, \ + 0x60, 0x84, 0x0c, 0x54, \ + 0xfb, 0x80, 0x04, 0x24, \ + 0x73, 0xf9, 0x4c, 0x60, \ + 0x4c, 0x6a, 0xc0, 0x01, \ + 0x8c, 0x23, 0xe6, 0x92, \ + 0x58, 0x0a, 0x88, 0x21, \ + 0x82, 0x85, 0xe0, 0x84, \ + 0x60, 0x84, 0x0c, 0x54, \ + 0xfb, 0x80, 0x04, 0x24, \ + 0x73, 0xf9, 0x4c, 0x60, \ + 0x4c, 0x6a, 0xc0, 0x01, \ + 0x8c, 0x23, 0xe6, 0x92, \ + 0x58, 0x0a, 0x88, 0x21, \ + 0x0c, 0x54, 0xe0, 0x94, \ + 0x04, 0x24, 0x5a, 0x80, \ + 0x4c, 0x4a, 0xdb, 0x80, \ + 0x44, 0x22, 0xc6, 0x9a, \ + 0x04, 0x20, 0xd0, 0x98, \ + 0x58, 0x0a, 0x44, 0x10, \ + 0x0c, 0x54, 0xe0, 0x98, \ + 0x04, 0x24, 0x5a, 0x80, \ + 0x4c, 0x4a, 0xdb, 0x80, \ + 0x44, 0x22, 0xc6, 0x9a, \ + 0x04, 0x20, 0xd0, 0x98, \ + 0x58, 0x0a, 0x44, 0x10, \ + 0x0c, 0x54, 0xe0, 0x9c, \ + 0x04, 0x24, 0x5a, 0x80, \ + 0x4c, 0x4a, 0xdb, 0x80, \ + 0x44, 0x22, 0xc6, 0x9a, \ + 0x04, 0x20, 0xd0, 0x98, \ + 0x58, 0x0a, 0x44, 0x10, \ + 0x0a, 0x54, 0xe0, 0xa0, \ + 0x04, 0x24, 0x5a, 0x80, \ + 0x48, 0x4a, 0xdb, 0x80, \ + 0x44, 0x22, 0xc6, 0x96, \ + 0x04, 0x20, 0xd0, 0x90, \ + 0xa5, 0x3b, 0x44, 0x10, \ + 0x9b, 0x80, 0x04, 0x24, \ + 0x84, 0xf0, 0x04, 0x20, \ + 0x62, 0x09, 0x00, 0x39, \ + 0x00, 0x39, 0xc0, 0x15, \ + 0x40, 0x15, 0x62, 0x05, \ + 0x04, 0x24, 0xa3, 0x3b, \ + 0x04, 0x20, 0x9b, 0x80, \ + 0x80, 0x1e, 0x06, 0xc1, \ + 0xec, 0x04, 0x08, 0x30, \ + 0x92, 0xb5, 0xe4, 0x4d, \ + 0xfc, 0x08, 0x48, 0x30, \ + 0x88, 0x35, 0xa7, 0x4b, \ + 0x68, 0xfd, 0x00, 0x4b, \ + 0x0a, 0x46, 0x40, 0x0a, \ + 0x04, 0x24, 0xe0, 0x04, \ + 0x48, 0x4a, 0xfb, 0x80, \ + 0x4a, 0x06, 0xe0, 0x80, \ + 0x48, 0x0a, 0x40, 0x00, \ + 0x8a, 0x23, 0xe0, 0x84, \ + 0x44, 0x90, 0x04, 0x20, \ + 0xe0, 0x88, 0x48, 0x0a, \ + 0x44, 0x90, 0x04, 0x20, \ + 0xe0, 0x8c, 0x48, 0x0a, \ + 0x44, 0x90, 0x04, 0x20, \ + 0xe0, 0x90, 0x48, 0x0a, \ + 0x44, 0x90, 0x04, 0x20, \ + 0xe0, 0x94, 0x48, 0x0a, \ + 0xc4, 0x90, 0x04, 0x20, \ + 0xa7, 0x3b, 0xc2, 0x39, \ + 0x00, 0x3b, 0x88, 0xd5, \ + 0x40, 0x0a, 0x68, 0xfd, \ + 0xe0, 0x30, 0x49, 0xc6, \ + 0xfb, 0x80, 0x04, 0x24, \ + 0xe0, 0x80, 0x46, 0x4a, \ + 0x40, 0x10, 0x08, 0x86, \ + 0xe0, 0x84, 0x46, 0x0a, \ + 0x04, 0x20, 0x88, 0x23, \ + 0x40, 0x02, 0x04, 0xd1, \ + 0xe0, 0x88, 0x46, 0x0a, \ + 0x04, 0xd1, 0x04, 0x20, \ + 0x46, 0x0a, 0x40, 0x02, \ + 0x04, 0x20, 0xe0, 0x8c, \ + 0x40, 0x02, 0x04, 0xd1, \ + 0xe0, 0x90, 0x46, 0x0a, \ + 0x04, 0xd1, 0x04, 0x20, \ + 0x46, 0x0a, 0x40, 0x02, \ + 0x04, 0x20, 0xe0, 0x94, \ + 0xc0, 0x02, 0x04, 0xd1, \ + 0x65, 0x65, 0x00, 0x39, \ + 0x08, 0x30, 0xc0, 0x2f, \ + 0x67, 0x4d, 0xec, 0x2c, \ + 0xe4, 0x30, 0x08, 0x10, \ + 0xdb, 0x80, 0x04, 0x24, \ + 0x04, 0x20, 0x06, 0x75, \ + 0xc0, 0x26, 0x05, 0x71, \ + 0xec, 0x10, 0x0a, 0x30, \ + 0x04, 0x24, 0xe5, 0x5d, \ + 0x04, 0x55, 0xdb, 0x80, \ + 0x06, 0x81, 0x04, 0x20, \ + 0xb5, 0x3b, 0xc0, 0x01, \ + 0x00, 0x39, 0x82, 0xb5, \ + 0x40, 0x2f, 0x66, 0x55, \ + 0xfb, 0x80, 0x04, 0x24, \ + 0xe0, 0x90, 0x46, 0x0a, \ + 0x84, 0x30, 0x04, 0x20, \ + 0x06, 0x61, 0x04, 0x20, \ + 0x00, 0x39, 0xc0, 0x01, \ + 0x40, 0x15, 0x62, 0x19, \ + 0xfb, 0x80, 0x04, 0x24, \ + 0xe6, 0x8a, 0x44, 0x3a, \ + 0x68, 0x6d, 0x00, 0x39, \ + 0x08, 0x23, 0xc0, 0x31, \ + 0x40, 0x43, 0x91, 0x41, \ + 0x40, 0x0a, 0x68, 0x89, \ + 0x40, 0x43, 0x81, 0x41, \ + 0x40, 0x0a, 0x68, 0x9d, \ + 0xe0, 0x84, 0x46, 0x4a, \ + 0x68, 0x99, 0x40, 0x23, \ + 0x80, 0xe5, 0xc0, 0x0a, \ + 0x68, 0x69, 0x00, 0x59, \ + 0x0a, 0x0a, 0xc0, 0x31, \ + 0x8a, 0xe5, 0xe3, 0xfc, \ + 0x6c, 0x20, 0x0a, 0x20, \ + 0x40, 0x53, 0x81, 0xc1, \ + 0x40, 0x18, 0x6c, 0x31, \ + 0x65, 0xe1, 0x0a, 0x00, \ + 0x83, 0x35, 0xc0, 0xc5, \ + 0x0c, 0x74, 0x94, 0x74, \ + 0x4c, 0x60, 0xe1, 0x90, \ + 0xc0, 0x0f, 0x73, 0xfd, \ + 0x82, 0x25, 0xa1, 0x6b, \ + 0x18, 0xc4, 0x1e, 0x79, \ + 0xa1, 0x7b, 0xfb, 0x80, \ + 0xc6, 0x9e, 0x4c, 0x6a, \ + 0x88, 0xc1, 0x8c, 0xc3, \ + 0x82, 0x41, 0x08, 0x51, \ + 0xf4, 0x9c, 0x09, 0x0a, \ + 0x08, 0x24, 0xfa, 0x85, \ + 0x0a, 0xc4, 0x7b, 0x80, \ + 0x8a, 0x43, 0xfb, 0x80, \ + 0x67, 0xfd, 0x09, 0x0a, \ + 0x46, 0x30, 0xc0, 0x2b, \ + 0x44, 0x40, 0x5c, 0x20, \ + 0x40, 0x31, 0x9c, 0x00, \ + 0xc0, 0x31, 0x08, 0x6d, \ + 0x68, 0x91, 0x40, 0x13, \ + 0x06, 0x10, 0x40, 0x0a, \ + 0x40, 0x23, 0xe4, 0x10, \ + 0x40, 0x0a, 0x68, 0x99, \ + 0xfb, 0x80, 0x02, 0x24, \ + 0x68, 0x95, 0x40, 0x33, \ + 0x44, 0xc6, 0x40, 0x0a, \ + 0x08, 0x24, 0xe1, 0x3c, \ + 0x40, 0x13, 0x86, 0x11, \ + 0x40, 0x0a, 0x68, 0x8d, \ + 0x70, 0x01, 0x06, 0x06, \ + 0xa6, 0x1f, 0xc0, 0x20, \ + 0x65, 0x49, 0x40, 0x15, \ + 0x40, 0x15, 0xc0, 0x0c, \ + 0x40, 0x0c, 0x65, 0x4d, \ + 0xe1, 0x3c, 0x41, 0x06, \ + 0x80, 0x04, 0x88, 0x1e, \ + 0x0c, 0x0c, 0x80, 0x1e, \ + 0xdd, 0x93, 0xf0, 0x04, \ + 0xf8, 0x04, 0x04, 0x0c, \ + 0x61, 0x90, 0x4a, 0x46, \ + 0xe0, 0x90, 0x0c, 0x14, \ + 0x4e, 0x00, 0x14, 0xca, \ + 0x0c, 0x64, 0xf0, 0x3c, \ + 0xa1, 0x0b, 0x7b, 0x80, \ + 0x00, 0x16, 0x2e, 0x6f, \ + 0x58, 0x22, 0x80, 0x00, \ + 0x40, 0x1a, 0x71, 0xb0, \ + 0x04, 0xc4, 0xe6, 0x82, \ + 0x02, 0xc4, 0x60, 0xc0, \ + 0x24, 0x6f, 0xfb, 0x80, \ + 0x49, 0x6e, 0x86, 0x2b, \ + 0x94, 0x5a, 0xc5, 0x07, \ + 0x02, 0x00, 0x02, 0x53, \ + 0x4a, 0x50, 0xe4, 0x04, \ + 0xc1, 0xff, 0x73, 0xf1, \ + 0x8a, 0x43, 0x01, 0x65, \ + 0x00, 0x00, 0x51, 0x54, \ + 0xc9, 0x5e, 0xec, 0x04, \ + 0xfe, 0x75, 0xa1, 0x0b, \ + 0x40, 0x32, 0xc4, 0x17, \ + 0x0c, 0x0c, 0xf0, 0x84, \ + 0xdd, 0x93, 0xf0, 0x04, \ + 0xfa, 0xfc, 0x04, 0x0c, \ + 0x40, 0x06, 0x00, 0x6b, \ + 0x00, 0x04, 0xe1, 0x34, \ + 0xe0, 0x04, 0x02, 0x96, \ + 0x61, 0x98, 0x57, 0x06, \ + 0xac, 0x9e, 0x81, 0x91, \ + 0x6d, 0x09, 0x4c, 0x11, \ + 0x80, 0x16, 0xc0, 0x2f, \ + 0x64, 0x5d, 0x0c, 0x09, \ + 0x0c, 0x19, 0xc0, 0x31, \ + 0xc0, 0x2f, 0x6d, 0x01, \ + 0xe0, 0x8c, 0x40, 0x0a, \ + 0xf3, 0x7c, 0x40, 0x10, \ + 0x6d, 0x01, 0x4c, 0x01, \ + 0x00, 0x06, 0x40, 0x2f, \ + 0xc2, 0x00, 0x00, 0x01, \ + 0xc8, 0x10, 0x69, 0xa8, \ + 0x60, 0x3c, 0x41, 0xf6, \ + 0x73, 0xd8, 0x81, 0x11, \ + 0x4e, 0x06, 0xef, 0xb0, \ + 0x42, 0x46, 0x61, 0x48, \ + 0x1c, 0x04, 0xe1, 0x48, \ + 0x64, 0x41, 0x06, 0x60, \ + 0x80, 0x08, 0xc0, 0x28, \ + 0x84, 0x88, 0x84, 0x14, \ + 0x68, 0x6d, 0x0c, 0x19, \ + 0x0c, 0x2b, 0xc0, 0x31, \ + 0xc0, 0x0a, 0x68, 0x91, \ + 0x02, 0x19, 0xd0, 0x87, \ + 0xf0, 0x17, 0xb0, 0x0f, \ + 0x83, 0x11, 0x22, 0x0f, \ + 0x68, 0x01, 0x4c, 0x05, \ + 0x00, 0x24, 0x40, 0x02, \ + 0xc1, 0x1d, 0xe1, 0x88, \ + 0x58, 0x08, 0x46, 0x95, \ + 0xc4, 0x04, 0x02, 0x10, \ + 0x06, 0x30, 0x7e, 0x75, \ + 0x54, 0x06, 0xc4, 0x10, \ + 0x9b, 0x11, 0x61, 0x90, \ + 0x46, 0x15, 0xa8, 0x0a, \ + 0x99, 0x11, 0x78, 0x04, \ + 0x46, 0x05, 0xac, 0x44, \ + 0x00, 0x60, 0x78, 0x08, \ + 0xc0, 0x57, 0x66, 0xd0, \ + 0x7b, 0x80, 0x04, 0x04, \ + 0x77, 0x58, 0x81, 0x01, \ + 0x00, 0x3b, 0xef, 0xfc, \ + 0x65, 0x41, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x54, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x89, 0x11, 0x01, 0x01, \ + 0xef, 0xfc, 0x76, 0xa8, \ + 0x62, 0x01, 0x0c, 0x09, \ + 0x80, 0x3b, 0x40, 0x15, \ + 0xec, 0x04, 0x00, 0x00, \ + 0x1b, 0xe8, 0xe2, 0x0d, \ + 0x2c, 0x44, 0xc0, 0x00, \ + 0x65, 0x51, 0x00, 0x60, \ + 0x04, 0x04, 0xc0, 0x54, \ + 0x81, 0x01, 0x7b, 0x80, \ + 0x6f, 0xfc, 0x75, 0x88, \ + 0x00, 0x3b, 0x91, 0x11, \ + 0x67, 0x01, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x57, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x89, 0x11, 0x01, 0x01, \ + 0xef, 0xfc, 0x74, 0xc8, \ + 0x62, 0x01, 0x0c, 0x09, \ + 0x80, 0x3b, 0x40, 0x15, \ + 0x82, 0x95, 0xa5, 0x0b, \ + 0x00, 0x60, 0x2c, 0x44, \ + 0xc0, 0x57, 0x67, 0x11, \ + 0x7b, 0x80, 0x04, 0x04, \ + 0x73, 0xd8, 0x81, 0x01, \ + 0x99, 0x11, 0x6f, 0xfc, \ + 0x0c, 0x0b, 0x80, 0x3b, \ + 0xc0, 0x0a, 0x68, 0xf1, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x0c, 0x19, 0x84, 0x25, \ + 0x40, 0x15, 0x6f, 0x09, \ + 0x67, 0x41, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x57, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x6f, 0xfc, 0x72, 0xa8, \ + 0x0c, 0x0b, 0x89, 0x01, \ + 0x40, 0x0a, 0x68, 0xf1, \ + 0x40, 0x0a, 0x80, 0x3b, \ + 0x84, 0x25, 0xe0, 0x84, \ + 0x6f, 0x09, 0x0c, 0x19, \ + 0x00, 0x60, 0x40, 0x15, \ + 0xc0, 0x58, 0x67, 0x41, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x71, 0x78, 0xfb, 0x80, \ + 0x89, 0x01, 0x6f, 0xfc, \ + 0x68, 0xf1, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x88, 0x40, 0x0a, \ + 0x0c, 0x19, 0x84, 0x25, \ + 0x40, 0x15, 0x6f, 0x09, \ + 0x67, 0x41, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x59, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x6f, 0xfc, 0x70, 0x48, \ + 0x0c, 0x0b, 0x85, 0x01, \ + 0x40, 0x0a, 0x68, 0xf1, \ + 0x40, 0x0a, 0x80, 0x3b, \ + 0x84, 0x25, 0xe0, 0x8c, \ + 0x6f, 0x09, 0x0c, 0x19, \ + 0x00, 0x60, 0x40, 0x15, \ + 0xc0, 0x5a, 0x65, 0x41, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x6f, 0x18, 0xfb, 0x80, \ + 0x83, 0x01, 0x6f, 0xfc, \ + 0x68, 0xf1, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x90, 0x40, 0x0a, \ + 0x0c, 0x19, 0x84, 0x25, \ + 0x40, 0x15, 0x6f, 0x09, \ + 0x66, 0x41, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x5a, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x6f, 0xfc, 0x6d, 0xe8, \ + 0x0c, 0x0b, 0x83, 0x01, \ + 0x40, 0x0a, 0x68, 0xf1, \ + 0x40, 0x0a, 0x80, 0x3b, \ + 0x84, 0x25, 0xe0, 0x94, \ + 0x6f, 0x09, 0x0c, 0x19, \ + 0x00, 0x60, 0x40, 0x15, \ + 0xc0, 0x5a, 0x67, 0x41, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x6c, 0xb8, 0xfb, 0x80, \ + 0x83, 0x01, 0x6f, 0xfc, \ + 0x68, 0xf1, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x98, 0x40, 0x0a, \ + 0x0c, 0x19, 0x84, 0x25, \ + 0x40, 0x15, 0x6f, 0x09, \ + 0x64, 0x41, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x5b, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x6f, 0xfc, 0x6b, 0x88, \ + 0x0c, 0x0b, 0x83, 0x01, \ + 0x40, 0x0a, 0x68, 0xf1, \ + 0x40, 0x0a, 0x80, 0x3b, \ + 0x82, 0xc5, 0xe0, 0x9c, \ + 0x6f, 0x09, 0x0c, 0x19, \ + 0x00, 0x60, 0x40, 0x15, \ + 0xc0, 0x5b, 0x65, 0x41, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x6a, 0x58, 0xfb, 0x80, \ + 0x83, 0x01, 0x6f, 0xfc, \ + 0x0c, 0x0b, 0x80, 0x3b, \ + 0xc0, 0x0a, 0x68, 0xf5, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x0c, 0x09, 0x84, 0xc5, \ + 0x40, 0x15, 0x6f, 0x0d, \ + 0x66, 0x41, 0x04, 0x60, \ + 0x0c, 0x19, 0xc0, 0x5b, \ + 0x40, 0x15, 0x6f, 0x09, \ + 0xfb, 0x80, 0x04, 0x24, \ + 0x42, 0x1a, 0xac, 0x44, \ + 0x89, 0x01, 0x66, 0x82, \ + 0xef, 0xfc, 0x68, 0xc8, \ + 0x68, 0xf5, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x88, 0x40, 0x0a, \ + 0x0c, 0x09, 0x84, 0xc5, \ + 0x40, 0x15, 0x6f, 0x0d, \ + 0x66, 0x41, 0x04, 0x60, \ + 0x0c, 0x19, 0xc0, 0x5f, \ + 0x40, 0x15, 0x6f, 0x09, \ + 0xfb, 0x80, 0x04, 0x24, \ + 0x42, 0x1a, 0xac, 0x44, \ + 0x89, 0x01, 0x66, 0x82, \ + 0xef, 0xfc, 0x67, 0x48, \ + 0x68, 0xf5, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x8c, 0x40, 0x0a, \ + 0x0c, 0x09, 0x84, 0xc5, \ + 0x40, 0x15, 0x6f, 0x0d, \ + 0x66, 0x41, 0x04, 0x60, \ + 0x0c, 0x19, 0xc0, 0x63, \ + 0x40, 0x15, 0x6f, 0x09, \ + 0xfb, 0x80, 0x04, 0x24, \ + 0x42, 0x1a, 0xac, 0x44, \ + 0x89, 0x01, 0x66, 0x82, \ + 0xef, 0xfc, 0x65, 0xc8, \ + 0x68, 0xf5, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x90, 0x40, 0x0a, \ + 0x0c, 0x09, 0x84, 0xc5, \ + 0x40, 0x15, 0x6f, 0x0d, \ + 0x66, 0x41, 0x04, 0x60, \ + 0x0c, 0x19, 0xc0, 0x67, \ + 0x40, 0x15, 0x6f, 0x09, \ + 0xfb, 0x80, 0x04, 0x24, \ + 0x42, 0x1a, 0xac, 0x44, \ + 0x85, 0x01, 0x66, 0x82, \ + 0xef, 0xfc, 0x64, 0x48, \ + 0x68, 0xf5, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x0c, 0x09, 0x84, 0xc5, \ + 0x40, 0x15, 0x6f, 0x0d, \ + 0x66, 0x41, 0x04, 0x60, \ + 0x0c, 0x19, 0xc0, 0x69, \ + 0x40, 0x15, 0x6f, 0x09, \ + 0xfb, 0x80, 0x04, 0x24, \ + 0x42, 0x1a, 0xac, 0x44, \ + 0x85, 0x01, 0x66, 0x82, \ + 0xef, 0xfc, 0x62, 0xc8, \ + 0x68, 0xf5, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x94, 0x40, 0x0a, \ + 0x0c, 0x09, 0x84, 0xc5, \ + 0x40, 0x15, 0x6f, 0x0d, \ + 0x66, 0x41, 0x04, 0x60, \ + 0x0c, 0x19, 0xc0, 0x6b, \ + 0x40, 0x15, 0x6f, 0x09, \ + 0xfb, 0x80, 0x04, 0x24, \ + 0x42, 0x1a, 0xac, 0x44, \ + 0x83, 0x01, 0x66, 0x82, \ + 0xef, 0xfc, 0x61, 0x48, \ + 0x68, 0xf5, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x98, 0x40, 0x0a, \ + 0x0c, 0x09, 0x84, 0xc5, \ + 0x40, 0x15, 0x6f, 0x0d, \ + 0x66, 0x41, 0x04, 0x60, \ + 0x0c, 0x19, 0xc0, 0x6c, \ + 0x40, 0x15, 0x6f, 0x09, \ + 0xfb, 0x80, 0x04, 0x24, \ + 0x42, 0x1a, 0xac, 0x44, \ + 0x83, 0x01, 0x66, 0x82, \ + 0xef, 0xfc, 0x5f, 0xc8, \ + 0x68, 0xf5, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x9c, 0x40, 0x0a, \ + 0x0c, 0x09, 0x84, 0xc5, \ + 0x40, 0x15, 0x6f, 0x0d, \ + 0x66, 0x41, 0x04, 0x60, \ + 0x0c, 0x19, 0xc0, 0x6d, \ + 0x40, 0x15, 0x6f, 0x09, \ + 0xfb, 0x80, 0x04, 0x24, \ + 0x42, 0x1a, 0xac, 0x44, \ + 0x83, 0x01, 0x66, 0x82, \ + 0xef, 0xfc, 0x5e, 0x48, \ + 0x68, 0xf5, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0xa0, 0x40, 0x0a, \ + 0x0c, 0x09, 0x84, 0x65, \ + 0x40, 0x15, 0x6f, 0x0d, \ + 0x66, 0x41, 0x04, 0x60, \ + 0x0c, 0x19, 0xc0, 0x6e, \ + 0x40, 0x15, 0x6f, 0x09, \ + 0xfb, 0x80, 0x04, 0x24, \ + 0x42, 0x1a, 0xac, 0x44, \ + 0x83, 0x01, 0x66, 0x82, \ + 0xef, 0xfc, 0x5c, 0xc8, \ + 0x0c, 0x09, 0x80, 0x3b, \ + 0xc0, 0x15, 0x62, 0x01, \ + 0x88, 0x55, 0xa5, 0x0b, \ + 0x00, 0x60, 0x2c, 0x44, \ + 0xc0, 0x6f, 0x66, 0x41, \ + 0x7b, 0x80, 0x04, 0x04, \ + 0x5b, 0xd8, 0x81, 0x01, \ + 0x99, 0x11, 0x6f, 0xfc, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x6f, 0x66, 0x71, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x01, 0x01, 0xfb, 0x80, \ + 0x5b, 0x18, 0xa1, 0x11, \ + 0x00, 0x3b, 0xef, 0xfc, \ + 0x66, 0xb1, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x6f, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0xa9, 0x11, 0x01, 0x01, \ + 0xef, 0xfc, 0x5a, 0x68, \ + 0x0c, 0x09, 0x80, 0x3b, \ + 0xc0, 0x15, 0x62, 0x09, \ + 0xb0, 0x15, 0xa3, 0x0b, \ + 0x00, 0x60, 0x2c, 0x44, \ + 0xc0, 0x54, 0x65, 0x51, \ + 0x7b, 0x80, 0x04, 0x04, \ + 0x59, 0x78, 0x81, 0x01, \ + 0x91, 0x11, 0x6f, 0xfc, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0xc6, 0x64, 0x71, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x01, 0x01, 0xfb, 0x80, \ + 0x58, 0xb8, 0x99, 0x11, \ + 0x00, 0x3b, 0xef, 0xfc, \ + 0x67, 0x01, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x6f, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x91, 0x11, 0x01, 0x01, \ + 0xef, 0xfc, 0x58, 0x08, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x6f, 0x67, 0x21, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x03, 0x01, 0xfb, 0x80, \ + 0xe0, 0x04, 0x02, 0x06, \ + 0xef, 0xfc, 0x57, 0x48, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x70, 0x64, 0x21, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x03, 0x01, 0xfb, 0x80, \ + 0xe0, 0x04, 0x02, 0x06, \ + 0xef, 0xfc, 0x56, 0x88, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x70, 0x65, 0x71, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x01, 0x01, 0xfb, 0x80, \ + 0x55, 0xd8, 0x89, 0x11, \ + 0x00, 0x3b, 0xef, 0xfc, \ + 0x65, 0x21, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x70, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0xa9, 0x11, 0x01, 0x01, \ + 0xef, 0xfc, 0x55, 0x28, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x70, 0x65, 0x81, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x09, 0x01, 0xfb, 0x80, \ + 0x54, 0x78, 0xa1, 0x11, \ + 0x00, 0x3b, 0xef, 0xfc, \ + 0x66, 0x81, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x70, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0xa1, 0x11, 0x09, 0x01, \ + 0xef, 0xfc, 0x53, 0xc8, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x70, 0x67, 0x81, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x09, 0x01, 0xfb, 0x80, \ + 0x53, 0x18, 0xa1, 0x11, \ + 0x00, 0x3b, 0xef, 0xfc, \ + 0x64, 0x81, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x71, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0xa1, 0x11, 0x03, 0x01, \ + 0xef, 0xfc, 0x52, 0x68, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x71, 0x64, 0xc1, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x03, 0x01, 0xfb, 0x80, \ + 0x51, 0xb8, 0xa1, 0x11, \ + 0x00, 0x3b, 0xef, 0xfc, \ + 0x65, 0x01, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x71, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x02, 0x06, 0x07, 0x01, \ + 0x50, 0xf8, 0xe0, 0x90, \ + 0x00, 0x3b, 0xef, 0xfc, \ + 0x65, 0x51, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x8c, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x89, 0x11, 0x01, 0x01, \ + 0xef, 0xfc, 0x50, 0x48, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x8c, 0x65, 0x01, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x01, 0x01, 0xfb, 0x80, \ + 0x4f, 0x98, 0xa9, 0x11, \ + 0x00, 0x3b, 0xef, 0xfc, \ + 0x65, 0x61, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x8c, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0xa1, 0x11, 0x01, 0x01, \ + 0xef, 0xfc, 0x4e, 0xe8, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x8c, 0x65, 0xa1, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x07, 0x01, 0xfb, 0x80, \ + 0xe0, 0x08, 0x03, 0x06, \ + 0xef, 0xfc, 0x4e, 0x28, \ + 0x0c, 0x09, 0x80, 0x3b, \ + 0xc0, 0x15, 0x62, 0x05, \ + 0xec, 0x04, 0x02, 0x00, \ + 0x1b, 0x08, 0xe4, 0x1d, \ + 0xc4, 0x09, 0xc0, 0x00, \ + 0xa2, 0x15, 0xa7, 0x0b, \ + 0x00, 0x60, 0x2c, 0x44, \ + 0xc0, 0x8e, 0x64, 0x61, \ + 0x7b, 0x80, 0x04, 0x04, \ + 0x4c, 0xd8, 0x81, 0x01, \ + 0x89, 0x11, 0x6f, 0xfc, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x8e, 0x64, 0x71, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x01, 0x01, 0xfb, 0x80, \ + 0x4c, 0x18, 0x99, 0x11, \ + 0x00, 0x3b, 0xef, 0xfc, \ + 0x64, 0xa1, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x8e, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x89, 0x11, 0x01, 0x01, \ + 0xef, 0xfc, 0x4b, 0x68, \ + 0x68, 0xfd, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x2c, 0x44, 0x82, 0xe5, \ + 0x64, 0xb1, 0x00, 0x60, \ + 0x04, 0x04, 0xc0, 0x8e, \ + 0x89, 0x01, 0x7b, 0x80, \ + 0x6f, 0xfc, 0x4a, 0x68, \ + 0x0c, 0x0b, 0x91, 0x11, \ + 0x40, 0x0a, 0x68, 0xfd, \ + 0x40, 0x0a, 0x80, 0x3b, \ + 0x82, 0xe5, 0xe0, 0x84, \ + 0x00, 0x60, 0x2c, 0x44, \ + 0xc0, 0x8e, 0x65, 0x31, \ + 0x7b, 0x80, 0x04, 0x04, \ + 0x49, 0x58, 0x89, 0x01, \ + 0x91, 0x11, 0x6f, 0xfc, \ + 0x68, 0xfd, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x88, 0x40, 0x0a, \ + 0x2c, 0x44, 0x82, 0xe5, \ + 0x65, 0xb1, 0x00, 0x60, \ + 0x04, 0x04, 0xc0, 0x8e, \ + 0x89, 0x01, 0x7b, 0x80, \ + 0x6f, 0xfc, 0x48, 0x48, \ + 0x0c, 0x0b, 0x91, 0x11, \ + 0x40, 0x0a, 0x68, 0xfd, \ + 0x40, 0x0a, 0x80, 0x3b, \ + 0x82, 0xe5, 0xe0, 0x8c, \ + 0x00, 0x60, 0x2c, 0x44, \ + 0xc0, 0x8e, 0x66, 0x31, \ + 0x7b, 0x80, 0x04, 0x04, \ + 0x47, 0x38, 0x89, 0x01, \ + 0x91, 0x11, 0x6f, 0xfc, \ + 0x68, 0xfd, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x90, 0x40, 0x0a, \ + 0x2c, 0x44, 0x82, 0xe5, \ + 0x66, 0xb1, 0x00, 0x60, \ + 0x04, 0x04, 0xc0, 0x8e, \ + 0x89, 0x01, 0x7b, 0x80, \ + 0x6f, 0xfc, 0x46, 0x28, \ + 0x0c, 0x0b, 0x91, 0x11, \ + 0x40, 0x0a, 0x68, 0xfd, \ + 0x40, 0x0a, 0x80, 0x3b, \ + 0x82, 0x85, 0xe0, 0x94, \ + 0x00, 0x60, 0x2c, 0x44, \ + 0xc0, 0x8e, 0x67, 0x31, \ + 0x7b, 0x80, 0x04, 0x04, \ + 0x45, 0x18, 0x89, 0x01, \ + 0x91, 0x11, 0x6f, 0xfc, \ + 0x0c, 0x09, 0x80, 0x3b, \ + 0xc0, 0x15, 0x62, 0x05, \ + 0x00, 0x0a, 0xc2, 0x09, \ + 0x11, 0xe8, 0xf0, 0x0c, \ + 0x2c, 0x44, 0xc0, 0x00, \ + 0x64, 0x81, 0x00, 0x60, \ + 0x04, 0x04, 0xc0, 0x9c, \ + 0x81, 0x01, 0x7b, 0x80, \ + 0x6f, 0xfc, 0x43, 0xe8, \ + 0x00, 0x3b, 0x89, 0x11, \ + 0x64, 0x91, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x9c, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x91, 0x11, 0x03, 0x01, \ + 0xef, 0xfc, 0x43, 0x28, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x9c, 0x64, 0xb1, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x03, 0x01, 0xfb, 0x80, \ + 0x42, 0x78, 0x91, 0x11, \ + 0x00, 0x3b, 0xef, 0xfc, \ + 0x64, 0xd1, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x9c, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x91, 0x11, 0x03, 0x01, \ + 0xef, 0xfc, 0x41, 0xc8, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x9c, 0x64, 0xf1, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x03, 0x01, 0xfb, 0x80, \ + 0x41, 0x18, 0x91, 0x11, \ + 0x00, 0x3b, 0xef, 0xfc, \ + 0x65, 0x11, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x9c, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x91, 0x11, 0x03, 0x01, \ + 0xef, 0xfc, 0x40, 0x68, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x9c, 0x65, 0x31, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x03, 0x01, 0xfb, 0x80, \ + 0x7f, 0xb8, 0x91, 0x11, \ + 0x00, 0x3b, 0xef, 0xf8, \ + 0x65, 0x51, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x9c, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x91, 0x11, 0x03, 0x01, \ + 0xef, 0xf8, 0x7f, 0x08, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x9c, 0x65, 0x71, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x03, 0x01, 0xfb, 0x80, \ + 0x7e, 0x58, 0x91, 0x11, \ + 0x00, 0x3b, 0xef, 0xf8, \ + 0x65, 0x91, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x9c, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x91, 0x11, 0x09, 0x01, \ + 0xef, 0xf8, 0x7d, 0xa8, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x9c, 0x66, 0x11, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x09, 0x01, 0xfb, 0x80, \ + 0x7c, 0xf8, 0x91, 0x11, \ + 0x00, 0x3b, 0xef, 0xf8, \ + 0x66, 0x91, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x9c, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x91, 0x11, 0x09, 0x01, \ + 0xef, 0xf8, 0x7c, 0x48, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x9c, 0x67, 0x11, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x05, 0x01, 0xfb, 0x80, \ + 0x7b, 0x98, 0x91, 0x11, \ + 0x00, 0x3b, 0xef, 0xf8, \ + 0x67, 0xb1, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x8e, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x89, 0x11, 0x01, 0x01, \ + 0xef, 0xf8, 0x7a, 0xe8, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x8e, 0x67, 0xc1, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x01, 0x01, 0xfb, 0x80, \ + 0x7a, 0x38, 0x99, 0x11, \ + 0x00, 0x3b, 0xef, 0xf8, \ + 0x67, 0xf1, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0x8e, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x89, 0x11, 0x01, 0x01, \ + 0xef, 0xf8, 0x79, 0x88, \ + 0x68, 0xfd, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x2c, 0x44, 0x84, 0x05, \ + 0x64, 0x01, 0x00, 0x60, \ + 0x04, 0x04, 0xc0, 0x8f, \ + 0x89, 0x01, 0x7b, 0x80, \ + 0x6f, 0xf8, 0x78, 0x88, \ + 0xe0, 0x08, 0x02, 0xc6, \ + 0x68, 0xfd, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x2c, 0x44, 0x84, 0x05, \ + 0x64, 0xc1, 0x00, 0x60, \ + 0x04, 0x04, 0xc0, 0x91, \ + 0x89, 0x01, 0x7b, 0x80, \ + 0x6f, 0xf8, 0x77, 0x68, \ + 0xe0, 0x08, 0x02, 0xc6, \ + 0x68, 0xfd, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x88, 0x40, 0x0a, \ + 0x2c, 0x44, 0x84, 0x05, \ + 0x65, 0x81, 0x00, 0x60, \ + 0x04, 0x04, 0xc0, 0x93, \ + 0x89, 0x01, 0x7b, 0x80, \ + 0x6f, 0xf8, 0x76, 0x48, \ + 0xe0, 0x08, 0x02, 0xc6, \ + 0x68, 0xfd, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x8c, 0x40, 0x0a, \ + 0x2c, 0x44, 0x84, 0x05, \ + 0x66, 0x41, 0x00, 0x60, \ + 0x04, 0x04, 0xc0, 0x95, \ + 0x89, 0x01, 0x7b, 0x80, \ + 0x6f, 0xf8, 0x75, 0x28, \ + 0xe0, 0x08, 0x02, 0xc6, \ + 0x68, 0xfd, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x90, 0x40, 0x0a, \ + 0x2c, 0x44, 0x84, 0x05, \ + 0x67, 0x01, 0x00, 0x60, \ + 0x04, 0x04, 0xc0, 0x97, \ + 0x89, 0x01, 0x7b, 0x80, \ + 0x6f, 0xf8, 0x74, 0x08, \ + 0xe0, 0x08, 0x02, 0xc6, \ + 0x68, 0xfd, 0x0c, 0x0b, \ + 0x80, 0x3b, 0x40, 0x0a, \ + 0xe0, 0x94, 0x40, 0x0a, \ + 0x2c, 0x44, 0x82, 0xa5, \ + 0x67, 0xc1, 0x00, 0x60, \ + 0x04, 0x04, 0xc0, 0x99, \ + 0x89, 0x01, 0x7b, 0x80, \ + 0x6f, 0xf8, 0x72, 0xe8, \ + 0xe0, 0x08, 0x02, 0xc6, \ + 0x0c, 0x09, 0x80, 0x3b, \ + 0xc0, 0x2f, 0x65, 0x65, \ + 0xec, 0x2c, 0x02, 0x00, \ + 0x8e, 0xb5, 0xe7, 0x1d, \ + 0xec, 0x10, 0x02, 0x00, \ + 0xa2, 0xe5, 0xe5, 0x1d, \ + 0x00, 0x60, 0x2c, 0x44, \ + 0xc0, 0x9e, 0x65, 0x71, \ + 0x7b, 0x80, 0x04, 0x04, \ + 0x71, 0x78, 0x81, 0x01, \ + 0x89, 0x11, 0x6f, 0xf8, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x9e, 0x65, 0x81, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x09, 0x01, 0xfb, 0x80, \ + 0xe0, 0x04, 0x02, 0x06, \ + 0xef, 0xf8, 0x70, 0xa8, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x9f, 0x65, 0x81, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x03, 0x01, 0xfb, 0x80, \ + 0xe0, 0x04, 0x02, 0x06, \ + 0xef, 0xf8, 0x6f, 0xe8, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x9f, 0x66, 0x81, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x03, 0x01, 0xfb, 0x80, \ + 0xe0, 0x04, 0x02, 0x06, \ + 0xef, 0xf8, 0x6f, 0x28, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0xa1, 0x64, 0xf1, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x01, 0x01, 0xfb, 0x80, \ + 0x91, 0xb5, 0x91, 0x11, \ + 0x00, 0x60, 0x2c, 0x44, \ + 0xc0, 0x9f, 0x67, 0x81, \ + 0x7b, 0x80, 0x04, 0x04, \ + 0x6d, 0xf8, 0x81, 0x01, \ + 0x91, 0x11, 0x6f, 0xf8, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x9f, 0x67, 0xc1, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x03, 0x01, 0xfb, 0x80, \ + 0x6d, 0x38, 0xa1, 0x11, \ + 0x54, 0x46, 0xef, 0xf8, \ + 0x80, 0x3b, 0x61, 0x90, \ + 0x0e, 0xce, 0x28, 0x0a, \ + 0xc0, 0x00, 0x60, 0x01, \ + 0x70, 0x0d, 0x46, 0x75, \ + 0x91, 0x11, 0x40, 0x01, \ + 0x40, 0x62, 0xac, 0x44, \ + 0xe0, 0x07, 0xf1, 0x80, \ + 0x7c, 0x11, 0x40, 0x00, \ + 0x46, 0x05, 0xc0, 0x08, \ + 0x40, 0x01, 0x78, 0x09, \ + 0x46, 0x05, 0x99, 0x01, \ + 0x00, 0x60, 0x78, 0x04, \ + 0xc0, 0x9f, 0x67, 0xa0, \ + 0x7b, 0x80, 0x04, 0x04, \ + 0x6b, 0x38, 0x81, 0x01, \ + 0x00, 0x3b, 0xef, 0xf8, \ + 0x64, 0x01, 0x00, 0x60, \ + 0x2c, 0x44, 0xc0, 0xa0, \ + 0xfb, 0x80, 0x04, 0x04, \ + 0x03, 0x86, 0x03, 0x01, \ + 0x6a, 0x78, 0xe0, 0x10, \ + 0x28, 0x0a, 0xef, 0xf8, \ + 0x46, 0x75, 0x80, 0x3b, \ + 0x40, 0x11, 0x71, 0x09, \ + 0x64, 0x01, 0x02, 0x60, \ + 0x54, 0x06, 0xc0, 0x02, \ + 0x4e, 0x06, 0x61, 0x90, \ + 0xa0, 0x13, 0xe1, 0x48, \ + 0x40, 0x10, 0xe0, 0x17, \ + 0xc0, 0x8c, 0x7c, 0x11, \ + 0x79, 0x05, 0x46, 0x05, \ + 0x87, 0x85, 0xc0, 0x11, \ + 0x8c, 0xd5, 0xb5, 0x0b, \ + 0x00, 0x60, 0x2c, 0x44, \ + 0xc0, 0x9c, 0x67, 0x51, \ + 0x7b, 0x80, 0x04, 0x04, \ + 0x68, 0x78, 0x81, 0x01, \ + 0x91, 0x11, 0x6f, 0xf8, \ + 0x66, 0x55, 0x0c, 0x09, \ + 0x80, 0x3b, 0x40, 0x2f, \ + 0xe0, 0x90, 0x40, 0x0a, \ + 0x2c, 0x44, 0x88, 0xa5, \ + 0x67, 0x71, 0x00, 0x60, \ + 0x04, 0x04, 0xc0, 0x9c, \ + 0x89, 0x01, 0x7b, 0x80, \ + 0x6f, 0xf8, 0x67, 0x68, \ + 0xe0, 0x04, 0x02, 0x06, \ + 0x00, 0x60, 0x00, 0x3b, \ + 0xc0, 0x9d, 0x67, 0x71, \ + 0x04, 0x04, 0x2c, 0x44, \ + 0x03, 0x01, 0xfb, 0x80, \ + 0xe0, 0x04, 0x02, 0x06, \ + 0xef, 0xf8, 0x66, 0x88, \ + 0x80, 0x3b, 0x2c, 0x44, \ + 0x64, 0x71, 0x00, 0x60, \ + 0x02, 0x06, 0x40, 0x9e, \ + 0x04, 0x04, 0xe0, 0x04, \ + 0x83, 0x01, 0x7b, 0x80, \ + 0xef, 0xf8, 0x65, 0xc8, \ + 0x0c, 0x09, 0x80, 0x3b, \ + 0xc0, 0x31, 0x68, 0x6d, \ + 0xe0, 0x84, 0x40, 0x4a, \ + 0x0c, 0x09, 0x80, 0xe5, \ + 0xc0, 0x31, 0x68, 0x69, \ + 0xe3, 0xfc, 0x00, 0x0a, \ + 0x00, 0x60, 0x88, 0x25, \ + 0x40, 0xc5, 0x65, 0xe1, \ + 0xe0, 0x00, 0x42, 0x86, \ + 0x80, 0x44, 0x83, 0x55, \ + 0xe1, 0x90, 0x04, 0x44, \ + 0x73, 0xfd, 0x44, 0x20, \ + 0xa1, 0x2b, 0xc0, 0x0f, \ + 0x9e, 0x49, 0x82, 0x45, \ + 0x44, 0x2a, 0xa1, 0x4b, \ + 0x08, 0x94, 0x46, 0x92, \ + 0x84, 0x43, 0xfb, 0x80, \ + 0xe4, 0x10, 0x12, 0x40, \ + 0x02, 0x10, 0x08, 0x01, \ + 0xa1, 0x1b, 0xec, 0x04, \ + 0x40, 0x90, 0xfa, 0x75, \ + 0xc0, 0xff, 0x73, 0xf1, \ + 0x28, 0x0a, 0x80, 0x33, \ + 0x9c, 0x24, 0x9d, 0x11, \ + 0x9f, 0x11, 0x0c, 0x1e, \ + 0x02, 0x06, 0x0e, 0x1e, \ + 0xff, 0xc0, 0x60, 0x01, \ + 0x88, 0x08, 0xcc, 0x0e, \ + 0x6d, 0x95, 0x0c, 0x29, \ + 0xb0, 0x0f, 0xc0, 0x30, \ + 0xe3, 0xfc, 0x04, 0x0a, \ + 0xe0, 0x00, 0x04, 0x1a, \ + 0xed, 0x80, 0x42, 0x0a, \ + 0xc0, 0xe0, 0x04, 0x24, \ + 0xd0, 0x88, 0x42, 0x12, \ + 0x4c, 0x05, 0xa2, 0x0f, \ + 0x40, 0x02, 0x68, 0x01, \ + 0x61, 0xf8, 0x8c, 0x0b, \ + 0x41, 0xf6, 0xe8, 0x0c, \ + 0x83, 0x11, 0x60, 0x3c, \ + 0xef, 0xac, 0x59, 0xc8, \ + 0x80, 0x18, 0x9c, 0x04, \ + 0xe0, 0x98, 0x42, 0x0a, \ + 0x0d, 0x01, 0x0c, 0x19, \ + 0x02, 0x14, 0xc0, 0x2f, \ + 0x4c, 0x11, 0xdc, 0x94, \ + 0xc0, 0x2f, 0x0d, 0x01, \ + 0x42, 0xd8, 0x80, 0xb5, \ + 0x8c, 0x0b, 0x68, 0x00, \ + 0x80, 0x18, 0x9c, 0x04, \ + 0x42, 0x0a, 0x00, 0x18, \ + 0x00, 0x39, 0xe0, 0x90, \ + 0x05, 0x06, 0x40, 0x00, \ + 0x44, 0x06, 0x80, 0x00, \ + 0x42, 0x10, 0xc0, 0x00, \ + 0x44, 0x12, 0x93, 0xbc, \ + 0x02, 0x34, 0x58, 0x88, \ + 0x24, 0x1f, 0xdc, 0x14, \ + 0x00, 0x16, 0x8c, 0x2b, \ + 0x58, 0x58, 0x83, 0x01, \ + 0x81, 0x11, 0x6f, 0xf4, \ + 0xef, 0xf0, 0x75, 0xc8, \ + 0x64, 0x5d, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x31, \ + 0x80, 0x95, 0xe0, 0x90, \ + 0xf2, 0xfc, 0x0c, 0x0c, \ + 0x00, 0x06, 0xdd, 0x93, \ + 0xc4, 0x00, 0x60, 0x01, \ + 0xf2, 0xfc, 0x0c, 0x0c, \ + 0xe0, 0x0c, 0x0b, 0xa8, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x41, 0xf6, 0x00, 0x6b, \ + 0x55, 0xb8, 0xe0, 0x3c, \ + 0x81, 0x11, 0x6f, 0xac, \ + 0x68, 0x01, 0x0c, 0x05, \ + 0x04, 0x06, 0x40, 0x02, \ + 0xc0, 0x20, 0x70, 0x01, \ + 0x68, 0x99, 0x0c, 0x1b, \ + 0x06, 0x20, 0x40, 0x0a, \ + 0x0c, 0x0e, 0xe4, 0x00, \ + 0x64, 0x21, 0x00, 0x60, \ + 0x08, 0x11, 0xc0, 0x28, \ + 0x45, 0x17, 0x85, 0x31, \ + 0x81, 0x45, 0x90, 0x21, \ + 0x82, 0x31, 0x41, 0x44, \ + 0xc3, 0x3d, 0xc9, 0x4e, \ + 0x72, 0x98, 0xfe, 0x95, \ + 0x5c, 0x98, 0xef, 0xf0, \ + 0x8c, 0x0b, 0x68, 0x0c, \ + 0xe1, 0x90, 0x40, 0x06, \ + 0x82, 0x11, 0x80, 0x1a, \ + 0x41, 0xf6, 0x00, 0x1c, \ + 0x83, 0x11, 0xe0, 0x3c, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0xac, 0x13, 0x08, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x42, 0x06, 0x02, 0x6b, \ + 0x04, 0x14, 0xe1, 0x48, \ + 0x06, 0x04, 0x82, 0x09, \ + 0xc8, 0x07, 0x60, 0x98, \ + 0x44, 0x20, 0x84, 0x28, \ + 0xa6, 0x2f, 0xf2, 0xfc, \ + 0x0c, 0x19, 0x84, 0x26, \ + 0xc0, 0x2f, 0x6d, 0x01, \ + 0xf3, 0xbc, 0x42, 0x10, \ + 0x4c, 0x11, 0xa0, 0x1f, \ + 0xc0, 0x2f, 0x6d, 0x01, \ + 0xef, 0xf0, 0x70, 0x08, \ + 0x81, 0x11, 0x01, 0x01, \ + 0x0c, 0x0c, 0x8c, 0x2b, \ + 0x11, 0xf8, 0xf2, 0x00, \ + 0xc1, 0xc3, 0xff, 0xf4, \ + 0xe1, 0x00, 0x01, 0x86, \ + 0x80, 0x08, 0x80, 0x04, \ + 0x82, 0x09, 0x5d, 0x93, \ + 0xc7, 0x13, 0xc1, 0x83, \ + 0x00, 0x0c, 0xdd, 0x93, \ + 0xc0, 0x00, 0x61, 0x01, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x81, 0x01, 0x00, 0x6b, \ + 0x66, 0x21, 0x12, 0x60, \ + 0x84, 0x7b, 0x40, 0xc1, \ + 0x66, 0x19, 0x4c, 0x01, \ + 0x82, 0x8b, 0x40, 0x30, \ + 0x83, 0x11, 0x03, 0xa1, \ + 0x92, 0x2b, 0x0c, 0x0b, \ + 0xe8, 0x00, 0x42, 0x28, \ + 0x92, 0x1b, 0x0c, 0x0b, \ + 0xe8, 0x40, 0x49, 0x28, \ + 0x66, 0x19, 0x4c, 0xa1, \ + 0x8c, 0x0b, 0x40, 0x30, \ + 0xe8, 0x3c, 0x62, 0x68, \ + 0x45, 0x76, 0x01, 0x11, \ + 0x09, 0x31, 0xe0, 0x00, \ + 0x66, 0xb8, 0x8c, 0x0b, \ + 0xa3, 0x8b, 0xe8, 0x3c, \ + 0x0c, 0x0b, 0x82, 0x75, \ + 0x51, 0x98, 0x8e, 0x1b, \ + 0x01, 0x11, 0xe8, 0x00, \ + 0x8c, 0x0b, 0x81, 0x21, \ + 0xf2, 0x3c, 0x0c, 0x0c, \ + 0x0c, 0x0c, 0xaf, 0x85, \ + 0xdd, 0x93, 0xf2, 0x3c, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xec, 0xc0, 0x1e, 0xf0, \ + 0x60, 0x04, 0x46, 0x86, \ + 0x40, 0x31, 0xa5, 0x1b, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0xe0, 0x04, 0x0e, 0x06, \ + 0xa1, 0x81, 0x0e, 0xa5, \ + 0xaa, 0x35, 0xa3, 0x1b, \ + 0x60, 0x0c, 0x41, 0xe6, \ + 0xe0, 0x0c, 0x59, 0xe6, \ + 0x01, 0xa6, 0x17, 0x0e, \ + 0x11, 0x0e, 0xe0, 0x04, \ + 0xe0, 0x04, 0x01, 0x26, \ + 0x01, 0x86, 0x0f, 0x0e, \ + 0x15, 0x0e, 0xe0, 0x04, \ + 0xe0, 0x04, 0x00, 0x06, \ + 0xa1, 0x01, 0x0b, 0x0e, \ + 0x00, 0x06, 0x09, 0x0e, \ + 0x7e, 0x0e, 0xe0, 0x04, \ + 0x7c, 0x0e, 0xa1, 0x01, \ + 0xe0, 0x04, 0x01, 0x86, \ + 0x01, 0x86, 0x0d, 0x0e, \ + 0x3e, 0x0e, 0xe0, 0x04, \ + 0xe0, 0x04, 0x01, 0xa6, \ + 0x49, 0xf6, 0x3c, 0x0e, \ + 0x4b, 0xa6, 0xe0, 0x0c, \ + 0x03, 0xa6, 0x60, 0x0c, \ + 0x07, 0x06, 0xe0, 0x0c, \ + 0x11, 0x06, 0x60, 0x04, \ + 0x0d, 0xa6, 0xe0, 0x04, \ + 0x13, 0xa6, 0x60, 0x04, \ + 0x0f, 0xa6, 0xe0, 0x0c, \ + 0x55, 0xa6, 0x60, 0x0c, \ + 0x13, 0xce, 0xe0, 0x0c, \ + 0xe0, 0x0c, 0x41, 0xe6, \ + 0x57, 0xf6, 0x05, 0x95, \ + 0x00, 0x06, 0xe0, 0x0c, \ + 0x89, 0x11, 0x60, 0x08, \ + 0x91, 0x01, 0x13, 0x0e, \ + 0x40, 0x06, 0x17, 0x0e, \ + 0x0d, 0x0e, 0xe0, 0x00, \ + 0x15, 0x0e, 0x85, 0x01, \ + 0x0b, 0x0e, 0x83, 0x01, \ + 0x09, 0x0e, 0x81, 0x01, \ + 0x7e, 0x0e, 0xa1, 0x01, \ + 0x7c, 0x0e, 0x83, 0x01, \ + 0x0f, 0x0e, 0x81, 0x01, \ + 0x11, 0x0e, 0x85, 0x01, \ + 0x3e, 0x0e, 0x81, 0x01, \ + 0xe0, 0x00, 0x40, 0x06, \ + 0x81, 0x31, 0x3c, 0x0e, \ + 0x81, 0x91, 0x01, 0x61, \ + 0x14, 0x06, 0x01, 0x51, \ + 0x01, 0x01, 0xe0, 0x04, \ + 0x16, 0x06, 0x81, 0x41, \ + 0x81, 0xc1, 0xe0, 0x08, \ + 0x8b, 0xc4, 0x88, 0xc6, \ + 0x89, 0xc4, 0x8a, 0xc6, \ + 0xfe, 0xc4, 0xc8, 0xc6, \ + 0xfc, 0xc4, 0xca, 0xc6, \ + 0xe0, 0x18, 0x44, 0x81, \ + 0xe0, 0x14, 0x44, 0x31, \ + 0xe0, 0x10, 0x44, 0xc1, \ + 0xe0, 0x1c, 0x44, 0x31, \ + 0xe0, 0x20, 0x44, 0x31, \ + 0x44, 0x31, 0x95, 0x34, \ + 0x8f, 0x34, 0xe0, 0x24, \ + 0xe0, 0x28, 0x44, 0x31, \ + 0x44, 0x31, 0x8d, 0x34, \ + 0x91, 0x34, 0xe0, 0x2c, \ + 0xe0, 0x30, 0x44, 0x31, \ + 0x44, 0x31, 0xbe, 0x34, \ + 0xbc, 0x34, 0xe0, 0x34, \ + 0xe0, 0x3c, 0x44, 0x61, \ + 0xe0, 0x40, 0x44, 0x61, \ + 0xe0, 0x38, 0x44, 0x31, \ + 0xe0, 0x48, 0x44, 0x91, \ + 0xe0, 0x4c, 0x44, 0x71, \ + 0xe0, 0x44, 0x44, 0x11, \ + 0xe0, 0x50, 0x44, 0x11, \ + 0xe0, 0x58, 0x44, 0xa1, \ + 0xe0, 0x54, 0x44, 0x51, \ + 0xe0, 0x5c, 0x44, 0x51, \ + 0xe0, 0x60, 0x44, 0x51, \ + 0xe0, 0x68, 0x44, 0x01, \ + 0x44, 0x01, 0x93, 0x04, \ + 0x97, 0x04, 0xe0, 0x6c, \ + 0xe0, 0x78, 0x44, 0xb1, \ + 0xe0, 0x74, 0x44, 0x41, \ + 0xe0, 0x64, 0x44, 0x01, \ + 0xe0, 0x70, 0x44, 0x01, \ + 0xe0, 0x7c, 0x44, 0x41, \ + 0x0c, 0x0c, 0xe0, 0xf1, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x90, 0x1e, 0xf0, \ + 0x80, 0x6b, 0x25, 0x1b, \ + 0xa3, 0x1b, 0x82, 0x65, \ + 0x02, 0x60, 0x90, 0xb5, \ + 0x40, 0xc1, 0x66, 0x21, \ + 0x7a, 0xb8, 0x8c, 0x0b, \ + 0x07, 0xb5, 0xe8, 0x3c, \ + 0x40, 0x86, 0x83, 0x71, \ + 0x83, 0x11, 0x60, 0x04, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x00, 0x06, 0x40, 0x2f, \ + 0xc4, 0x00, 0x60, 0x11, \ + 0xa1, 0x01, 0x3e, 0x0e, \ + 0x40, 0x06, 0x15, 0x06, \ + 0x09, 0x1c, 0xe0, 0x00, \ + 0xe0, 0x04, 0x02, 0x06, \ + 0x81, 0x21, 0x1f, 0x06, \ + 0x89, 0x01, 0x25, 0x06, \ + 0x85, 0x71, 0x2b, 0x06, \ + 0x91, 0x01, 0x31, 0x06, \ + 0x35, 0x16, 0xaf, 0x16, \ + 0xe0, 0x08, 0x02, 0x06, \ + 0x99, 0x26, 0x97, 0x26, \ + 0x9d, 0x26, 0x9b, 0x76, \ + 0xa3, 0x26, 0xa1, 0x76, \ + 0xa9, 0x26, 0xa7, 0x26, \ + 0xb3, 0x26, 0xad, 0x26, \ + 0xb9, 0x26, 0xb7, 0x26, \ + 0x5e, 0x01, 0xbb, 0x06, \ + 0x8c, 0x0b, 0x60, 0x80, \ + 0xbf, 0x16, 0xbd, 0x26, \ + 0x60, 0x88, 0x5e, 0x11, \ + 0xe4, 0x10, 0x02, 0xf0, \ + 0xe0, 0x84, 0x5e, 0x21, \ + 0xe0, 0x8c, 0x5e, 0x21, \ + 0xe8, 0x3c, 0x77, 0x08, \ + 0x66, 0x19, 0x4c, 0x71, \ + 0xc8, 0xf1, 0xc0, 0x30, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfe, 0x7c, 0x04, 0xcc, \ + 0x80, 0x6b, 0x02, 0x7b, \ + 0x64, 0x90, 0x02, 0xf0, \ + 0xe4, 0x80, 0x04, 0xf0, \ + 0xe8, 0x0c, 0x6c, 0xa8, \ + 0x60, 0x90, 0x1e, 0x89, \ + 0xe0, 0x04, 0x0e, 0x2a, \ + 0xe0, 0x80, 0x1e, 0x79, \ + 0x40, 0x01, 0x01, 0x88, \ + 0x02, 0x60, 0xc0, 0x00, \ + 0x40, 0xc1, 0x66, 0xa1, \ + 0x01, 0x55, 0x99, 0x21, \ + 0x45, 0x06, 0x81, 0x01, \ + 0xec, 0x04, 0x04, 0x20, \ + 0xfe, 0x95, 0xa1, 0x2b, \ + 0x66, 0xd1, 0x02, 0x60, \ + 0x9d, 0x21, 0x40, 0xc1, \ + 0x45, 0x0c, 0x81, 0x45, \ + 0xec, 0x04, 0x04, 0x20, \ + 0xfe, 0x95, 0xa1, 0x2b, \ + 0x65, 0x51, 0x12, 0x60, \ + 0x81, 0xa1, 0x40, 0xbf, \ + 0x81, 0xb1, 0x07, 0x35, \ + 0x00, 0x84, 0x24, 0xac, \ + 0x42, 0x02, 0xe0, 0x90, \ + 0x40, 0x62, 0x79, 0x9c, \ + 0x40, 0x11, 0xf0, 0xac, \ + 0x40, 0x2f, 0x6f, 0xd1, \ + 0x40, 0x0d, 0x43, 0xf6, \ + 0x40, 0xa1, 0x80, 0x00, \ + 0x40, 0x30, 0x61, 0x11, \ + 0xfa, 0x80, 0x04, 0x74, \ + 0x43, 0x91, 0x40, 0x11, \ + 0x40, 0x11, 0x80, 0x30, \ + 0x00, 0x30, 0x44, 0xd1, \ + 0xfa, 0x80, 0x02, 0xb4, \ + 0x42, 0x51, 0x40, 0xa1, \ + 0x8c, 0x0b, 0x00, 0x30, \ + 0xe8, 0x00, 0x51, 0x48, \ + 0x60, 0x90, 0x1e, 0x89, \ + 0xe4, 0x04, 0x00, 0x80, \ + 0x7a, 0x80, 0x02, 0x04, \ + 0x84, 0x91, 0x82, 0xb1, \ + 0xe4, 0x24, 0x04, 0x80, \ + 0xe4, 0x08, 0x43, 0x4a, \ + 0x62, 0x00, 0x02, 0x0c, \ + 0xdc, 0x00, 0x50, 0x00, \ + 0x16, 0x0a, 0x82, 0x73, \ + 0xf2, 0xb5, 0xe1, 0x40, \ + 0xf6, 0x7c, 0x0c, 0xcc, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xee, 0x70, 0x1e, 0xf0, \ + 0xa1, 0x1b, 0x00, 0xbb, \ + 0x66, 0x19, 0x16, 0x09, \ + 0x84, 0x7b, 0x40, 0x30, \ + 0x25, 0x0b, 0x8c, 0xe5, \ + 0xe0, 0x04, 0x40, 0x66, \ + 0x6d, 0x09, 0x56, 0x01, \ + 0x82, 0x05, 0xc0, 0x2f, \ + 0x96, 0x0b, 0x05, 0x11, \ + 0xef, 0xfc, 0x73, 0x48, \ + 0x68, 0x04, 0x6d, 0x28, \ + 0x0e, 0x8a, 0x96, 0x0b, \ + 0x82, 0x95, 0xe4, 0x40, \ + 0x6d, 0x09, 0x16, 0x39, \ + 0x81, 0x71, 0x40, 0x2f, \ + 0x66, 0x81, 0x00, 0xb0, \ + 0x81, 0x11, 0x40, 0x54, \ + 0x68, 0x04, 0x7c, 0xc8, \ + 0x01, 0x81, 0xab, 0x21, \ + 0x61, 0x9d, 0x00, 0x16, \ + 0x01, 0xc5, 0xc0, 0x00, \ + 0x42, 0x0a, 0x81, 0x11, \ + 0x8e, 0x2b, 0x60, 0x80, \ + 0x5c, 0x00, 0x0e, 0x29, \ + 0xe4, 0x40, 0x06, 0xf0, \ + 0x82, 0x11, 0x02, 0x33, \ + 0x02, 0x0a, 0x8c, 0x26, \ + 0xfc, 0x95, 0xe2, 0x30, \ + 0x8e, 0x1b, 0x16, 0x0b, \ + 0xe8, 0x04, 0x6c, 0x48, \ + 0x4d, 0x76, 0x15, 0xe5, \ + 0x23, 0x0b, 0xe0, 0x00, \ + 0xe0, 0x04, 0x40, 0xa6, \ + 0x6d, 0x09, 0x56, 0x01, \ + 0x82, 0x65, 0xc0, 0x2f, \ + 0x66, 0x21, 0x02, 0xb0, \ + 0x96, 0x0b, 0x40, 0xc1, \ + 0xe8, 0x3c, 0x6b, 0x48, \ + 0x56, 0x01, 0x83, 0x01, \ + 0xc0, 0x30, 0x66, 0x19, \ + 0x60, 0x08, 0x00, 0xc6, \ + 0xe4, 0x40, 0x02, 0xf0, \ + 0x81, 0x81, 0x01, 0x55, \ + 0x00, 0x00, 0x45, 0x86, \ + 0xa1, 0x0b, 0xec, 0x04, \ + 0x00, 0xb0, 0xfe, 0x95, \ + 0x40, 0x54, 0x66, 0x51, \ + 0x66, 0x51, 0x12, 0xb0, \ + 0x3e, 0x0e, 0xc0, 0xc0, \ + 0x7e, 0x0e, 0x81, 0x01, \ + 0xe0, 0x04, 0x0f, 0x06, \ + 0x81, 0xa1, 0x07, 0x95, \ + 0x96, 0x6b, 0x7c, 0xae, \ + 0x78, 0x81, 0x12, 0x09, \ + 0x95, 0x11, 0x40, 0x01, \ + 0x61, 0x90, 0x16, 0x04, \ + 0x54, 0xba, 0x9e, 0x09, \ + 0x80, 0xa3, 0xe6, 0x86, \ + 0xe6, 0x30, 0x15, 0x0a, \ + 0x0c, 0x05, 0x80, 0xd5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0x82, 0x35, 0xab, 0x0b, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x81, 0x11, 0x40, 0x2f, \ + 0x8d, 0x21, 0x3e, 0x04, \ + 0xe8, 0x04, 0x6d, 0xe8, \ + 0x02, 0xf0, 0x24, 0x08, \ + 0x0e, 0x0a, 0xe4, 0x40, \ + 0x94, 0x13, 0x61, 0x40, \ + 0x5e, 0xb5, 0x84, 0x06, \ + 0x54, 0xb0, 0x00, 0x0c, \ + 0x82, 0x85, 0x9c, 0x00, \ + 0xfc, 0x14, 0xfe, 0x04, \ + 0xfa, 0x80, 0x00, 0x04, \ + 0x7a, 0x80, 0x02, 0x14, \ + 0xfb, 0x80, 0x40, 0xb2, \ + 0x54, 0xb2, 0x7e, 0x0e, \ + 0x0c, 0xbb, 0xfa, 0x84, \ + 0xec, 0x04, 0x0e, 0x70, \ + 0xa1, 0x7b, 0x82, 0x91, \ + 0x01, 0x16, 0xf2, 0x15, \ + 0x9f, 0x71, 0x60, 0x0c, \ + 0x6d, 0x09, 0x56, 0x01, \ + 0x87, 0x45, 0xc0, 0x2f, \ + 0x7a, 0x80, 0x02, 0x84, \ + 0x5d, 0xd8, 0x96, 0x0b, \ + 0xa1, 0x0b, 0xe8, 0x00, \ + 0x40, 0xb2, 0x8a, 0x85, \ + 0x95, 0x11, 0x70, 0xa0, \ + 0x6c, 0x01, 0x00, 0x09, \ + 0x94, 0x6b, 0x40, 0x30, \ + 0x61, 0x90, 0x12, 0x04, \ + 0x54, 0x9a, 0x9e, 0x09, \ + 0x80, 0xa3, 0xe6, 0x86, \ + 0xe6, 0x30, 0x15, 0x0a, \ + 0x16, 0x05, 0x80, 0xd5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0x82, 0x35, 0xad, 0x0b, \ + 0x6d, 0x09, 0x16, 0x39, \ + 0x81, 0x11, 0x40, 0x2f, \ + 0x8d, 0x21, 0x3e, 0x04, \ + 0xe8, 0x04, 0x69, 0x68, \ + 0x64, 0x40, 0x00, 0xf0, \ + 0xfa, 0x80, 0x02, 0x64, \ + 0x54, 0x92, 0x14, 0x03, \ + 0x80, 0x76, 0xfa, 0x84, \ + 0x00, 0x04, 0xfe, 0x04, \ + 0x40, 0x92, 0xfa, 0x80, \ + 0xfe, 0x0e, 0xfb, 0x80, \ + 0x10, 0x0a, 0x82, 0x81, \ + 0xf2, 0x75, 0xe0, 0x80, \ + 0x10, 0x04, 0xfe, 0x04, \ + 0x00, 0xa4, 0x7a, 0x80, \ + 0x44, 0x07, 0xfa, 0x80, \ + 0x0c, 0x00, 0xc4, 0x87, \ + 0x96, 0x0b, 0x64, 0x10, \ + 0xe8, 0x00, 0x66, 0xc8, \ + 0x7a, 0x80, 0x02, 0x84, \ + 0xfa, 0x80, 0x04, 0x64, \ + 0x64, 0x40, 0x06, 0xf0, \ + 0x5f, 0xf8, 0x96, 0x0b, \ + 0x1e, 0xf0, 0xe8, 0x3c, \ + 0x0c, 0x0c, 0xe6, 0x70, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x82, 0x8b, 0x00, 0x9b, \ + 0x0c, 0x90, 0x7c, 0x2e, \ + 0xc0, 0xbf, 0x65, 0x51, \ + 0xf0, 0xa0, 0x54, 0x62, \ + 0xe2, 0x80, 0x14, 0x19, \ + 0xe8, 0x00, 0x56, 0x28, \ + 0x80, 0x7b, 0x42, 0x87, \ + 0x67, 0xc0, 0x00, 0xa0, \ + 0x3c, 0x6e, 0x90, 0x63, \ + 0x66, 0x25, 0x12, 0x90, \ + 0x3e, 0x0e, 0xc0, 0xc1, \ + 0x01, 0xa1, 0x81, 0xb1, \ + 0x05, 0x55, 0x81, 0x81, \ + 0xfc, 0x24, 0x81, 0x61, \ + 0xf8, 0x04, 0x12, 0x09, \ + 0x44, 0x0a, 0xa4, 0x18, \ + 0xd0, 0x07, 0xe0, 0x80, \ + 0x48, 0x00, 0x61, 0x48, \ + 0x0e, 0x0f, 0xa2, 0x0f, \ + 0xfb, 0x80, 0x02, 0x64, \ + 0x82, 0x45, 0x83, 0x0b, \ + 0x10, 0x84, 0x03, 0x11, \ + 0x14, 0xa4, 0xfa, 0x80, \ + 0xb7, 0x1f, 0x7b, 0x80, \ + 0x82, 0x81, 0x22, 0xaf, \ + 0x3f, 0xbb, 0x80, 0x6b, \ + 0x1e, 0x05, 0x82, 0xb1, \ + 0x84, 0x91, 0x00, 0x04, \ + 0x80, 0x00, 0x40, 0x81, \ + 0x80, 0x00, 0x1e, 0x05, \ + 0x80, 0x00, 0x40, 0xa3, \ + 0xe0, 0x40, 0x16, 0x0a, \ + 0x98, 0xf1, 0xf6, 0x75, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x4c, \ + 0x00, 0xf6, 0x00, 0x6b, \ + 0x0c, 0x19, 0xe0, 0x0c, \ + 0x40, 0x2f, 0x6d, 0x31, \ + 0x0c, 0x29, 0x81, 0xb1, \ + 0x40, 0x2f, 0x6d, 0x39, \ + 0x0c, 0x39, 0x91, 0x51, \ + 0x40, 0x2f, 0x6d, 0x35, \ + 0x0c, 0x49, 0x85, 0x81, \ + 0x40, 0x2f, 0x6d, 0x3d, \ + 0x4c, 0x01, 0x83, 0xa1, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x6d, 0x29, 0x0c, 0x09, \ + 0x0c, 0x79, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6d, 0x2d, \ + 0x66, 0x86, 0x42, 0x2a, \ + 0x52, 0x4a, 0x91, 0x21, \ + 0x4c, 0x01, 0xe6, 0x8e, \ + 0x40, 0x27, 0x60, 0x91, \ + 0x4c, 0x01, 0xc5, 0x17, \ + 0x40, 0x27, 0x60, 0x95, \ + 0x4c, 0x01, 0xc5, 0x97, \ + 0x40, 0x27, 0x60, 0x99, \ + 0x4c, 0x01, 0x8e, 0x3b, \ + 0x40, 0x27, 0x60, 0x9d, \ + 0x4c, 0x01, 0x83, 0x01, \ + 0x40, 0x30, 0x6b, 0x41, \ + 0x4c, 0x21, 0x8c, 0x0b, \ + 0x40, 0x30, 0x6b, 0x61, \ + 0x4c, 0x51, 0x81, 0x21, \ + 0xc0, 0x27, 0x60, 0x81, \ + 0x60, 0xb1, 0x4c, 0x71, \ + 0x4c, 0x11, 0xc0, 0x27, \ + 0xc0, 0x27, 0x60, 0xd1, \ + 0x60, 0xf1, 0x4c, 0x91, \ + 0x4c, 0x71, 0xc0, 0x27, \ + 0xc0, 0x27, 0x60, 0xb5, \ + 0x60, 0xd5, 0x4c, 0x11, \ + 0x4c, 0x91, 0xc0, 0x27, \ + 0xc0, 0x27, 0x60, 0xf5, \ + 0x60, 0xb9, 0x4c, 0x71, \ + 0x4c, 0x11, 0xc0, 0x27, \ + 0xc0, 0x27, 0x60, 0xd9, \ + 0x60, 0xdd, 0x4c, 0x11, \ + 0x81, 0x11, 0x40, 0x27, \ + 0x60, 0xf9, 0x4c, 0x91, \ + 0x4c, 0x71, 0xc0, 0x27, \ + 0xc0, 0x27, 0x60, 0xbd, \ + 0x60, 0xfd, 0x4c, 0x91, \ + 0x4c, 0xb1, 0xc0, 0x27, \ + 0xc0, 0x30, 0x6b, 0x51, \ + 0x60, 0xa1, 0x4c, 0xb1, \ + 0x4c, 0x71, 0xc0, 0x27, \ + 0xc0, 0x27, 0x60, 0xc1, \ + 0x60, 0xe1, 0x4c, 0x81, \ + 0x4c, 0x91, 0xc0, 0x27, \ + 0xc0, 0x27, 0x61, 0x01, \ + 0xe8, 0x00, 0x46, 0x08, \ + 0x6b, 0x45, 0x4c, 0xa1, \ + 0x91, 0x01, 0x40, 0x30, \ + 0x6b, 0x65, 0x4c, 0x01, \ + 0x83, 0x11, 0x40, 0x30, \ + 0x6b, 0x55, 0x4c, 0xb1, \ + 0x83, 0x21, 0x40, 0x30, \ + 0x60, 0xa5, 0x4c, 0xb1, \ + 0x8c, 0x0b, 0x40, 0x27, \ + 0x60, 0xc5, 0x4c, 0x71, \ + 0x8e, 0x3b, 0x40, 0x27, \ + 0x60, 0xe5, 0x4c, 0x81, \ + 0x4c, 0x91, 0xc0, 0x27, \ + 0xc0, 0x27, 0x61, 0x05, \ + 0xe8, 0x00, 0x44, 0x48, \ + 0x6b, 0x49, 0x4c, 0xa1, \ + 0x99, 0xa1, 0x40, 0x30, \ + 0x6b, 0x59, 0x4c, 0xb1, \ + 0x91, 0x01, 0x40, 0x30, \ + 0x6b, 0x69, 0x4c, 0x01, \ + 0x85, 0x11, 0x40, 0x30, \ + 0x60, 0xa9, 0x4c, 0xa1, \ + 0x99, 0x21, 0x40, 0x27, \ + 0x60, 0xc9, 0x4c, 0x71, \ + 0x8c, 0x0b, 0x40, 0x27, \ + 0x60, 0xe9, 0x4c, 0x81, \ + 0x8e, 0x3b, 0x40, 0x27, \ + 0x61, 0x09, 0x4c, 0x91, \ + 0x42, 0x78, 0xc0, 0x27, \ + 0x4c, 0xb1, 0xe8, 0x00, \ + 0x40, 0x30, 0x6b, 0x5d, \ + 0x4c, 0x01, 0x83, 0x01, \ + 0x40, 0x30, 0x6b, 0x4d, \ + 0x4c, 0x01, 0x91, 0x01, \ + 0x40, 0x30, 0x6b, 0x6d, \ + 0x4c, 0xa1, 0x91, 0xb1, \ + 0x40, 0x27, 0x60, 0xad, \ + 0x4c, 0x71, 0x87, 0x11, \ + 0x40, 0x27, 0x60, 0xcd, \ + 0x4c, 0x81, 0x9b, 0x21, \ + 0x40, 0x27, 0x60, 0xed, \ + 0x4c, 0x91, 0x8c, 0x0b, \ + 0x40, 0x27, 0x61, 0x0d, \ + 0x40, 0x98, 0x8e, 0x3b, \ + 0x4c, 0xb1, 0xe8, 0x00, \ + 0xc0, 0x27, 0x60, 0x85, \ + 0xf2, 0x7c, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x86, 0x6b, 0x00, 0x8b, \ + 0x70, 0x84, 0x40, 0x82, \ + 0x7c, 0x1e, 0xc6, 0x17, \ + 0x67, 0x51, 0x00, 0x00, \ + 0x10, 0x13, 0xc0, 0xc2, \ + 0x3e, 0x0e, 0x84, 0x7b, \ + 0xbc, 0x1e, 0x81, 0xa1, \ + 0x7c, 0x94, 0x87, 0xb5, \ + 0xe0, 0x90, 0x00, 0x74, \ + 0xf0, 0x3c, 0x42, 0x60, \ + 0x79, 0x84, 0x56, 0x02, \ + 0x02, 0xb4, 0x90, 0x0b, \ + 0x94, 0x93, 0x7a, 0x80, \ + 0xe8, 0x00, 0x43, 0x68, \ + 0x68, 0x40, 0x7d, 0x68, \ + 0x02, 0x94, 0xa1, 0x11, \ + 0x8e, 0x99, 0x61, 0x8c, \ + 0x83, 0x31, 0x3e, 0x19, \ + 0xb3, 0x3f, 0x10, 0x13, \ + 0x6b, 0xf1, 0x02, 0x29, \ + 0x9d, 0x41, 0x40, 0x30, \ + 0xe6, 0x92, 0x48, 0x6a, \ + 0xa6, 0x2f, 0x3c, 0x34, \ + 0x6b, 0xf1, 0x42, 0x21, \ + 0x8e, 0x43, 0x40, 0x30, \ + 0x82, 0xa1, 0x14, 0x33, \ + 0x6b, 0x71, 0x46, 0x41, \ + 0x46, 0xb1, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6c, 0x01, \ + 0x6c, 0x81, 0x46, 0x01, \ + 0xbe, 0x04, 0xc0, 0x30, \ + 0x00, 0x4a, 0x80, 0x08, \ + 0x00, 0x0c, 0xe0, 0x04, \ + 0x00, 0x1a, 0x62, 0x00, \ + 0x00, 0x1c, 0xe0, 0x04, \ + 0x80, 0x63, 0x62, 0x00, \ + 0xb1, 0xab, 0x80, 0x73, \ + 0x98, 0xf1, 0xf0, 0xb5, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x61, 0x90, 0x04, 0x14, \ + 0x44, 0x2a, 0xa9, 0x31, \ + 0x06, 0x14, 0x66, 0x8e, \ + 0x8e, 0x39, 0xe1, 0x84, \ + 0x81, 0x21, 0x04, 0x03, \ + 0x81, 0x31, 0x06, 0x03, \ + 0x00, 0x00, 0x01, 0xe5, \ + 0xc0, 0xa0, 0x64, 0x01, \ + 0x70, 0x14, 0x00, 0x49, \ + 0xe0, 0x80, 0x42, 0x0a, \ + 0xc1, 0x90, 0x08, 0x44, \ + 0x25, 0x4f, 0x9e, 0x49, \ + 0xa8, 0x3f, 0x88, 0x21, \ + 0xe0, 0x40, 0x04, 0x0a, \ + 0x5d, 0x93, 0xfc, 0x95, \ + 0xfb, 0x80, 0x00, 0x34, \ + 0xe4, 0x7c, 0x03, 0x0a, \ + 0x04, 0x14, 0x82, 0xb5, \ + 0x8e, 0x19, 0x61, 0x8c, \ + 0x00, 0x09, 0x84, 0x03, \ + 0xc0, 0x30, 0x6b, 0xf1, \ + 0xe4, 0x84, 0x40, 0x4a, \ + 0xe2, 0x00, 0x00, 0x0c, \ + 0x5d, 0x93, 0xdd, 0x93, \ + 0xc1, 0xc3, 0x81, 0x01, \ + 0xe5, 0x7c, 0x03, 0x0a, \ + 0x04, 0x14, 0x82, 0xb5, \ + 0x8e, 0x19, 0x61, 0x8c, \ + 0x00, 0x09, 0x84, 0x03, \ + 0xc0, 0x30, 0x66, 0xa1, \ + 0xe4, 0x84, 0x40, 0x4a, \ + 0xe2, 0x00, 0x00, 0x0c, \ + 0x5d, 0x93, 0xdd, 0x93, \ + 0xc1, 0xc3, 0x81, 0x01, \ + 0xfa, 0x00, 0x00, 0x4c, \ + 0x65, 0x51, 0x00, 0x00, \ + 0x04, 0x14, 0x40, 0xbf, \ + 0x00, 0x0b, 0xe0, 0x84, \ + 0x80, 0x13, 0x7c, 0x08, \ + 0xe5, 0x00, 0x02, 0x19, \ + 0x68, 0x40, 0x75, 0x28, \ + 0xd0, 0x07, 0x82, 0x11, \ + 0xf2, 0x00, 0x08, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x6d, 0x49, 0x00, 0x19, \ + 0x82, 0x9b, 0x40, 0x2f, \ + 0x88, 0x7b, 0x0a, 0x6b, \ + 0x84, 0xab, 0x06, 0x8b, \ + 0x02, 0x96, 0x23, 0x1b, \ + 0x40, 0x11, 0xe0, 0x0c, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x88, 0x85, 0x81, 0x11, \ + 0x82, 0x45, 0xa1, 0x9b, \ + 0x82, 0x05, 0xa1, 0xab, \ + 0x80, 0xc5, 0xa1, 0x8b, \ + 0x65, 0x0d, 0x00, 0x15, \ + 0xa5, 0x1b, 0xc0, 0x0c, \ + 0x00, 0x00, 0x82, 0x55, \ + 0x40, 0x54, 0x66, 0x51, \ + 0x7f, 0x21, 0x81, 0x11, \ + 0xe0, 0x0c, 0x06, 0x96, \ + 0xe8, 0x04, 0x47, 0xc8, \ + 0xe0, 0x22, 0x40, 0x7a, \ + 0xe1, 0x22, 0x42, 0x7a, \ + 0xe0, 0x2a, 0x40, 0x0a, \ + 0xe0, 0x26, 0x42, 0x1a, \ + 0xe6, 0xa2, 0x40, 0x0a, \ + 0xe0, 0x26, 0x40, 0x0a, \ + 0x02, 0x02, 0x82, 0x03, \ + 0x98, 0x16, 0xef, 0xfc, \ + 0xf2, 0x3c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x6d, 0x49, 0x00, 0x19, \ + 0x82, 0x9b, 0x40, 0x2f, \ + 0x88, 0x7b, 0x0a, 0x6b, \ + 0x84, 0xab, 0x06, 0x8b, \ + 0x02, 0x86, 0x23, 0x1b, \ + 0x40, 0x11, 0xe0, 0x0c, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x88, 0x45, 0x81, 0x11, \ + 0x82, 0x45, 0xa1, 0x9b, \ + 0x82, 0x05, 0xa1, 0xab, \ + 0x80, 0xc5, 0xa1, 0x8b, \ + 0x65, 0x0d, 0x00, 0x15, \ + 0xa7, 0x1b, 0xc0, 0x0c, \ + 0x00, 0x00, 0x82, 0x55, \ + 0x40, 0x54, 0x66, 0x51, \ + 0x7f, 0x21, 0x81, 0x11, \ + 0xe0, 0x0c, 0x06, 0x86, \ + 0xe8, 0x04, 0x44, 0x28, \ + 0xe0, 0x22, 0x40, 0x7a, \ + 0xe1, 0x22, 0x42, 0x7a, \ + 0xe1, 0x2a, 0x40, 0x0a, \ + 0xe1, 0x26, 0x42, 0x1a, \ + 0xe6, 0xa6, 0x40, 0x0a, \ + 0x02, 0x02, 0x82, 0x03, \ + 0x98, 0x16, 0xef, 0xfc, \ + 0xf2, 0x3c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x9f, 0x21, 0x01, 0x11, \ + 0x81, 0x31, 0x03, 0x95, \ + 0x68, 0x84, 0x08, 0x24, \ + 0xfb, 0x80, 0x06, 0x34, \ + 0xe3, 0x4f, 0x80, 0x4f, \ + 0x61, 0x8c, 0x0a, 0x44, \ + 0xe1, 0x84, 0x18, 0x44, \ + 0x84, 0xc9, 0x02, 0x59, \ + 0x18, 0x44, 0x38, 0x5f, \ + 0x46, 0x47, 0xe0, 0x84, \ + 0x38, 0x5f, 0x88, 0xc9, \ + 0xa8, 0x5f, 0x90, 0x49, \ + 0x88, 0x11, 0x23, 0x5f, \ + 0x02, 0x0a, 0xaa, 0x3f, \ + 0xf8, 0xf5, 0xe0, 0x40, \ + 0x00, 0x34, 0x5d, 0x93, \ + 0xc1, 0xc3, 0xfb, 0x80, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x9d, 0x81, 0x00, 0xab, \ + 0x81, 0x91, 0x1e, 0x0b, \ + 0x9d, 0x11, 0x01, 0x55, \ + 0x02, 0x10, 0x41, 0x9c, \ + 0xa1, 0x1b, 0xec, 0x04, \ + 0x00, 0xa0, 0xfe, 0x95, \ + 0x40, 0xc1, 0x66, 0xd1, \ + 0x81, 0x45, 0x9d, 0x11, \ + 0x02, 0x10, 0x41, 0x9c, \ + 0xa1, 0x1b, 0xec, 0x04, \ + 0x85, 0xc5, 0xfe, 0x95, \ + 0x70, 0xa4, 0x4c, 0xa2, \ + 0xfa, 0x80, 0x0e, 0x94, \ + 0x6f, 0xd1, 0x0c, 0xb9, \ + 0x94, 0x0b, 0x40, 0x2f, \ + 0x6f, 0xfc, 0x72, 0x48, \ + 0x00, 0x0a, 0x8e, 0x1b, \ + 0x86, 0xb5, 0xf0, 0x04, \ + 0x6d, 0x81, 0x14, 0x09, \ + 0x0c, 0x19, 0xc0, 0x30, \ + 0xc0, 0x30, 0x63, 0x91, \ + 0xfa, 0x80, 0x00, 0x04, \ + 0xfa, 0x80, 0x02, 0x14, \ + 0x84, 0x35, 0x83, 0x0b, \ + 0x73, 0xc0, 0x40, 0xb0, \ + 0x47, 0x07, 0x9e, 0x1b, \ + 0x02, 0x2b, 0x9e, 0xb9, \ + 0x83, 0x31, 0x7c, 0x00, \ + 0x80, 0x13, 0x37, 0x3f, \ + 0xa6, 0x2f, 0x94, 0x0b, \ + 0x8e, 0x1b, 0x04, 0x2c, \ + 0xe8, 0x00, 0x42, 0x48, \ + 0x12, 0x0a, 0x82, 0x91, \ + 0xf4, 0x75, 0xe1, 0x40, \ + 0x64, 0x8d, 0x00, 0xe6, \ + 0x9e, 0x1b, 0x40, 0x20, \ + 0x9d, 0x21, 0x01, 0x65, \ + 0x04, 0x20, 0x45, 0x3a, \ + 0xc1, 0x3c, 0xec, 0x04, \ + 0xfe, 0x75, 0xa1, 0x2b, \ + 0x64, 0x8d, 0x40, 0xa6, \ + 0x9e, 0x1b, 0x40, 0x20, \ + 0x45, 0x2a, 0x81, 0x55, \ + 0xec, 0x04, 0x10, 0x80, \ + 0xa1, 0x8b, 0xc1, 0x2c, \ + 0x79, 0x58, 0xfe, 0x75, \ + 0x94, 0x0b, 0x68, 0x38, \ + 0x0c, 0x0c, 0xb8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0x83, 0x31, 0x00, 0x13, \ + 0x6f, 0xd1, 0x02, 0x19, \ + 0x04, 0x14, 0xc0, 0x2f, \ + 0x9e, 0x19, 0x61, 0x8c, \ + 0xa3, 0x3f, 0x3c, 0x29, \ + 0x00, 0x2b, 0x84, 0x03, \ + 0xc0, 0x18, 0x63, 0x69, \ + 0x40, 0x33, 0xa4, 0x3f, \ + 0xc0, 0x18, 0x63, 0x69, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xd0, 0x1e, 0xf0, \ + 0x04, 0x76, 0x00, 0x6b, \ + 0x0c, 0x09, 0xe0, 0x0c, \ + 0xc0, 0x2f, 0x6d, 0x31, \ + 0x6d, 0x39, 0x0c, 0x89, \ + 0x0c, 0x19, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6d, 0x35, \ + 0x6d, 0x3d, 0x0c, 0xa9, \ + 0x0c, 0x39, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6d, 0x45, \ + 0x66, 0x82, 0x40, 0x8a, \ + 0x42, 0xaa, 0xc5, 0x87, \ + 0xc5, 0xa7, 0x66, 0x86, \ + 0x6d, 0x09, 0x4c, 0x21, \ + 0xc5, 0x07, 0x40, 0x2f, \ + 0x6d, 0x41, 0x0c, 0x29, \ + 0xc5, 0x17, 0x40, 0x2f, \ + 0x6d, 0x29, 0x0c, 0x99, \ + 0xc5, 0x37, 0x40, 0x2f, \ + 0x6d, 0x2d, 0x0c, 0x79, \ + 0x7e, 0x0e, 0xc0, 0x2f, \ + 0xe1, 0x88, 0x16, 0x24, \ + 0x80, 0x93, 0x0d, 0x9e, \ + 0x82, 0x73, 0x13, 0x7e, \ + 0x02, 0xf0, 0x0b, 0x3e, \ + 0x75, 0x58, 0xe4, 0xc0, \ + 0x8c, 0x0b, 0x68, 0x08, \ + 0x00, 0x76, 0x21, 0xbb, \ + 0x89, 0xbe, 0xe0, 0x0c, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x82, 0x25, 0xc0, 0x2f, \ + 0xa1, 0x0b, 0x8b, 0x04, \ + 0x0c, 0x05, 0x80, 0xc5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0x82, 0x55, 0xa9, 0x0b, \ + 0x66, 0x51, 0x00, 0x60, \ + 0x81, 0x11, 0x40, 0x54, \ + 0x06, 0x76, 0x7f, 0x21, \ + 0x74, 0x38, 0xe0, 0x0c, \ + 0x09, 0x04, 0xe8, 0x00, \ + 0x67, 0xd1, 0x06, 0x60, \ + 0x59, 0xf6, 0xc0, 0xbf, \ + 0x42, 0x8a, 0xe0, 0x0c, \ + 0x10, 0x60, 0x60, 0x02, \ + 0xc0, 0xc1, 0x64, 0xd0, \ + 0xfc, 0x1e, 0x8b, 0x04, \ + 0x60, 0x02, 0x40, 0xaa, \ + 0xbe, 0x0e, 0x81, 0xa1, \ + 0x66, 0x86, 0x40, 0x0a, \ + 0xec, 0x04, 0x02, 0x90, \ + 0x02, 0x70, 0x0f, 0x1e, \ + 0x11, 0x1e, 0xec, 0x04, \ + 0x4c, 0x01, 0x81, 0x71, \ + 0xc0, 0x30, 0x66, 0x1d, \ + 0x8b, 0x55, 0xbc, 0x3e, \ + 0x7c, 0x28, 0x06, 0x09, \ + 0xf0, 0xa8, 0x52, 0x62, \ + 0x56, 0x82, 0x13, 0x24, \ + 0x52, 0xc1, 0xf0, 0xa8, \ + 0xc0, 0x30, 0x63, 0x91, \ + 0x42, 0x00, 0x2c, 0xc6, \ + 0xc5, 0x1d, 0xf0, 0x3c, \ + 0x91, 0x24, 0x90, 0xb5, \ + 0x90, 0x55, 0xc4, 0x1d, \ + 0xc9, 0x07, 0x0f, 0x14, \ + 0x8e, 0xd5, 0xc2, 0x0d, \ + 0xc3, 0x0d, 0x8d, 0x14, \ + 0x00, 0xa4, 0x8e, 0x75, \ + 0x44, 0xa0, 0x61, 0x8c, \ + 0x3e, 0x09, 0xf0, 0x1c, \ + 0x0c, 0x03, 0x83, 0xc1, \ + 0xe8, 0x88, 0x04, 0xc4, \ + 0x66, 0xa1, 0x00, 0x19, \ + 0x08, 0x74, 0x40, 0x30, \ + 0x7e, 0x34, 0xfa, 0x80, \ + 0xe4, 0xb0, 0x0a, 0xf0, \ + 0x58, 0x12, 0x09, 0x14, \ + 0x0b, 0x24, 0xf9, 0x88, \ + 0xfa, 0x80, 0x06, 0x34, \ + 0x66, 0xa1, 0x40, 0xc1, \ + 0x8c, 0x0b, 0x40, 0x30, \ + 0xfa, 0x80, 0x02, 0x14, \ + 0x6f, 0xfc, 0x64, 0xc8, \ + 0xfa, 0x80, 0x04, 0x24, \ + 0x0a, 0xf0, 0x7c, 0x04, \ + 0x1e, 0x49, 0xe4, 0xa0, \ + 0x1e, 0x39, 0xe0, 0xb0, \ + 0x3e, 0x04, 0xe0, 0xc0, \ + 0xfa, 0x80, 0x02, 0x04, \ + 0x04, 0x04, 0xac, 0x46, \ + 0x8c, 0x0b, 0x7a, 0x80, \ + 0xef, 0xfc, 0x67, 0x48, \ + 0x60, 0xa0, 0x1e, 0x09, \ + 0xe0, 0x0c, 0x59, 0xf6, \ + 0x82, 0x71, 0x3c, 0x34, \ + 0x63, 0x91, 0x52, 0x01, \ + 0x82, 0xa1, 0xc0, 0x30, \ + 0xe1, 0x40, 0x14, 0x0a, \ + 0x40, 0xb8, 0xea, 0x55, \ + 0x8c, 0x0b, 0x68, 0x00, \ + 0x6f, 0xfc, 0x4a, 0xa8, \ + 0xe8, 0xf1, 0x8c, 0x0b, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x2f, 0x6d, 0x49, \ + 0x8a, 0x65, 0xa3, 0x0b, \ + 0x64, 0x20, 0x02, 0xf0, \ + 0x6b, 0x78, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x08, \ + 0x40, 0x31, 0x64, 0x21, \ + 0x81, 0x91, 0x81, 0x81, \ + 0xe0, 0x80, 0x40, 0x4a, \ + 0x05, 0x91, 0x00, 0x60, \ + 0x00, 0x60, 0xc0, 0xbd, \ + 0x80, 0xbd, 0x05, 0x99, \ + 0x03, 0x15, 0x80, 0x0a, \ + 0xfb, 0x80, 0x0e, 0x04, \ + 0x7a, 0x80, 0x02, 0x84, \ + 0xe4, 0x10, 0x08, 0xf0, \ + 0x8c, 0x0b, 0x01, 0x21, \ + 0x68, 0x00, 0x42, 0x48, \ + 0x7c, 0x0a, 0x8e, 0x3b, \ + 0xfb, 0x80, 0x12, 0x94, \ + 0x80, 0x93, 0x82, 0x81, \ + 0xc1, 0x8d, 0xfc, 0x04, \ + 0x4c, 0x93, 0xfc, 0x15, \ + 0xc0, 0x18, 0x63, 0x09, \ + 0x0c, 0x0c, 0x98, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x1c, \ + 0x82, 0xf5, 0xfc, 0x1d, \ + 0x02, 0x14, 0x83, 0x31, \ + 0x82, 0x11, 0xfa, 0x80, \ + 0xf4, 0x38, 0x03, 0x0a, \ + 0x08, 0x14, 0x82, 0x15, \ + 0x08, 0x34, 0xfa, 0x80, \ + 0x80, 0x4f, 0xe8, 0x90, \ + 0xfc, 0x85, 0xa1, 0x4b, \ + 0xdd, 0x93, 0x88, 0x16, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xf8, 0x00, 0x10, 0xfc, \ + 0xec, 0xf0, 0x1e, 0xf0, \ + 0x80, 0x5b, 0x02, 0x9b, \ + 0x63, 0xfc, 0x12, 0x0a, \ + 0xfc, 0x2e, 0x81, 0x01, \ + 0x90, 0x0c, 0xbe, 0x4e, \ + 0x80, 0x00, 0x0b, 0xa8, \ + 0x10, 0x50, 0x49, 0x37, \ + 0xc0, 0xc0, 0x67, 0x91, \ + 0x0e, 0x50, 0x3c, 0x3e, \ + 0xc0, 0xc0, 0x65, 0x11, \ + 0x81, 0x61, 0x01, 0xa1, \ + 0x81, 0xb1, 0x7e, 0x5e, \ + 0x0e, 0x09, 0x85, 0x85, \ + 0x93, 0x0b, 0xe3, 0xc0, \ + 0xfc, 0x04, 0x88, 0x35, \ + 0x80, 0xf5, 0xa3, 0x0b, \ + 0x6d, 0x81, 0x0a, 0x09, \ + 0xa0, 0x18, 0xc0, 0x30, \ + 0x86, 0x15, 0x83, 0x0b, \ + 0x7a, 0x80, 0x02, 0xb4, \ + 0x58, 0x38, 0x8a, 0x0b, \ + 0x00, 0x0a, 0xef, 0xfc, \ + 0x1e, 0x55, 0xf0, 0x04, \ + 0x82, 0xd5, 0xc0, 0x0c, \ + 0x02, 0xa4, 0x1c, 0x08, \ + 0x04, 0xf0, 0xe0, 0x88, \ + 0x82, 0xa1, 0x64, 0x50, \ + 0x82, 0x23, 0x7e, 0x54, \ + 0x80, 0x63, 0x70, 0xad, \ + 0x80, 0x45, 0x88, 0xbe, \ + 0x02, 0xb1, 0x81, 0x95, \ + 0x82, 0x71, 0x82, 0x81, \ + 0xe5, 0x3c, 0x17, 0x0a, \ + 0xa1, 0xab, 0xf4, 0xe5, \ + 0x0a, 0x8b, 0xa0, 0x05, \ + 0xc0, 0x17, 0x6a, 0xcd, \ + 0xc9, 0x87, 0xbc, 0x14, \ + 0xfb, 0x80, 0x00, 0x14, \ + 0x6a, 0xd1, 0x0a, 0x0b, \ + 0xd1, 0x0d, 0x40, 0x17, \ + 0x9c, 0x00, 0x50, 0x10, \ + 0xfb, 0x80, 0x02, 0x84, \ + 0xc0, 0x1d, 0xc9, 0x07, \ + 0xdc, 0x00, 0x50, 0x00, \ + 0xec, 0x18, 0x51, 0x0a, \ + 0x3e, 0x14, 0x84, 0x65, \ + 0xe0, 0x90, 0x00, 0x64, \ + 0x00, 0xf0, 0x04, 0x0c, \ + 0x01, 0x95, 0xe4, 0x50, \ + 0x41, 0x24, 0x9f, 0x11, \ + 0xec, 0x04, 0x14, 0xa0, \ + 0x44, 0x11, 0x8a, 0x23, \ + 0xc0, 0x30, 0x62, 0x51, \ + 0xfe, 0x15, 0xa1, 0xab, \ + 0x81, 0x31, 0x8b, 0x85, \ + 0x00, 0x34, 0x85, 0x05, \ + 0x02, 0xf0, 0x60, 0x88, \ + 0x02, 0x15, 0xe4, 0x50, \ + 0x00, 0x14, 0xfc, 0x00, \ + 0x8a, 0x03, 0xe0, 0x84, \ + 0x6e, 0xa9, 0x00, 0x2b, \ + 0x81, 0x01, 0x40, 0x17, \ + 0x82, 0x85, 0xa1, 0x2b, \ + 0x44, 0x4a, 0x81, 0x01, \ + 0x44, 0x20, 0xe0, 0x80, \ + 0x40, 0xff, 0x13, 0xf9, \ + 0xc4, 0x04, 0x00, 0x00, \ + 0x04, 0x24, 0x7e, 0x35, \ + 0x0a, 0x13, 0xc1, 0x84, \ + 0x42, 0x01, 0x82, 0x31, \ + 0xc0, 0x30, 0x62, 0x51, \ + 0xf8, 0x15, 0x95, 0x3b, \ + 0x94, 0x7b, 0x21, 0x06, \ + 0x83, 0xa3, 0xa1, 0x43, \ + 0xe8, 0x00, 0x12, 0xcc, \ + 0xec, 0x20, 0x4e, 0x8a, \ + 0x12, 0xf0, 0x8a, 0x25, \ + 0xff, 0xb1, 0x64, 0x50, \ + 0xd5, 0xbd, 0x82, 0xb1, \ + 0x24, 0x64, 0x88, 0x45, \ + 0x65, 0x51, 0x00, 0x50, \ + 0x04, 0xf0, 0xc0, 0xbf, \ + 0x02, 0x64, 0xe4, 0x40, \ + 0x80, 0x63, 0x60, 0x84, \ + 0xfc, 0x04, 0x00, 0x0b, \ + 0xe5, 0x00, 0x0c, 0x19, \ + 0xef, 0xfc, 0x73, 0xe8, \ + 0x88, 0x91, 0x21, 0x08, \ + 0xe5, 0x00, 0x0c, 0x19, \ + 0x02, 0x60, 0x83, 0x0b, \ + 0x40, 0x01, 0x05, 0x01, \ + 0xdb, 0x80, 0x0e, 0x74, \ + 0x40, 0x00, 0x42, 0x01, \ + 0xc4, 0x04, 0x0e, 0x70, \ + 0x4e, 0x8a, 0x7e, 0x54, \ + 0xf6, 0xb5, 0xec, 0x20, \ + 0x3e, 0x04, 0xc1, 0xc3, \ + 0x80, 0x7c, 0xc8, 0x77, \ + 0x18, 0xfc, 0xf8, 0xf1, \ + 0x0c, 0x0c, 0xf0, 0x00, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x43, 0x46, 0x02, 0x6b, \ + 0x04, 0x14, 0xe1, 0x24, \ + 0x84, 0x34, 0x83, 0x21, \ + 0x61, 0x75, 0x00, 0x19, \ + 0x00, 0x45, 0xc0, 0x1c, \ + 0xc0, 0x07, 0x60, 0x49, \ + 0x61, 0x79, 0x00, 0x09, \ + 0x82, 0x31, 0x40, 0x1c, \ + 0xa1, 0x11, 0x23, 0x2f, \ + 0x88, 0x0b, 0x21, 0x2f, \ + 0xef, 0xd4, 0x52, 0x48, \ + 0x0c, 0x0c, 0x98, 0x0e, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x88, 0x7b, 0x00, 0x8b, \ + 0x65, 0xad, 0x10, 0x09, \ + 0x82, 0x6b, 0x40, 0x2f, \ + 0x60, 0x88, 0x02, 0x74, \ + 0x10, 0x13, 0x81, 0xa1, \ + 0x23, 0x0b, 0x86, 0x9b, \ + 0x42, 0xa5, 0x84, 0xbb, \ + 0xc0, 0x0c, 0x61, 0xd1, \ + 0xa7, 0x0b, 0x82, 0x05, \ + 0x10, 0x0b, 0x8c, 0x15, \ + 0xc0, 0x17, 0x6a, 0xc9, \ + 0x83, 0xe5, 0x91, 0x0c, \ + 0x67, 0x41, 0x00, 0x10, \ + 0x8e, 0x1b, 0x40, 0xc1, \ + 0x90, 0x0b, 0x3e, 0x0e, \ + 0x42, 0x78, 0xfc, 0x5e, \ + 0x49, 0x07, 0xe8, 0x00, \ + 0xe4, 0x40, 0x02, 0xf0, \ + 0x14, 0x04, 0x3c, 0x1e, \ + 0x0a, 0xf0, 0xfb, 0x80, \ + 0x90, 0x0b, 0x64, 0x30, \ + 0x96, 0x2b, 0x0c, 0x1b, \ + 0x94, 0x4b, 0x12, 0x3b, \ + 0xe8, 0x00, 0x43, 0x88, \ + 0xbe, 0x14, 0xfe, 0x04, \ + 0x84, 0x0e, 0xfc, 0x54, \ + 0x06, 0x04, 0x91, 0x0a, \ + 0x95, 0x3b, 0xfb, 0x80, \ + 0x40, 0x00, 0x04, 0x16, \ + 0xdc, 0x00, 0x40, 0x80, \ + 0x5c, 0x00, 0x42, 0x70, \ + 0xdc, 0x00, 0x48, 0x50, \ + 0xcf, 0xfc, 0x6d, 0x28, \ + 0x0c, 0x0c, 0xa8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x1c, 0x04, 0x4c, \ + 0x80, 0x7b, 0x02, 0x6b, \ + 0x81, 0x81, 0x01, 0x91, \ + 0x40, 0x72, 0x83, 0x25, \ + 0x00, 0x09, 0xf0, 0xa4, \ + 0xc0, 0x30, 0x64, 0xd1, \ + 0xe3, 0xfc, 0x00, 0x0a, \ + 0x8d, 0x0b, 0x82, 0x25, \ + 0x02, 0x94, 0x80, 0xf5, \ + 0x8e, 0x0b, 0x7a, 0x80, \ + 0xef, 0xfc, 0x47, 0xa8, \ + 0x82, 0x91, 0x80, 0x83, \ + 0xe1, 0x40, 0x12, 0x0a, \ + 0x90, 0x0b, 0xfa, 0xb5, \ + 0xf2, 0x1c, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x01, 0x06, 0x00, 0x6b, \ + 0x00, 0x04, 0xe1, 0x18, \ + 0x01, 0x11, 0x82, 0xbb, \ + 0x7c, 0x1e, 0x8a, 0x7b, \ + 0x06, 0x9b, 0x88, 0x8b, \ + 0x00, 0x08, 0x84, 0xab, \ + 0x42, 0x58, 0x91, 0x11, \ + 0x00, 0x2b, 0xef, 0xd4, \ + 0xe0, 0x0c, 0x01, 0x06, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x00, 0xf0, 0x40, 0x2f, \ + 0x3e, 0x0e, 0xe4, 0x20, \ + 0xe0, 0x90, 0x0a, 0x84, \ + 0x06, 0x60, 0x3c, 0x7e, \ + 0xc0, 0xbd, 0x65, 0x81, \ + 0x94, 0x1b, 0x16, 0x0b, \ + 0x6f, 0xa8, 0x7a, 0xc8, \ + 0x0c, 0x15, 0x92, 0x4b, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0x02, 0x0a, 0x95, 0x74, \ + 0x41, 0xfe, 0xe1, 0x98, \ + 0xa1, 0x0b, 0x9f, 0xfc, \ + 0x01, 0x11, 0x82, 0xe5, \ + 0x66, 0x51, 0x08, 0x60, \ + 0x1c, 0x1c, 0xc0, 0x54, \ + 0xfb, 0x00, 0x04, 0x04, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x87, 0x11, 0x40, 0x2f, \ + 0x68, 0x00, 0x4e, 0x68, \ + 0x7c, 0x04, 0x88, 0x0b, \ + 0xc9, 0x07, 0x98, 0xf1, \ + 0x6f, 0xfd, 0x00, 0x02, \ + 0x9c, 0x0c, 0xc0, 0xff, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x65, 0x11, 0x04, 0x00, \ + 0x81, 0x01, 0x40, 0xc0, \ + 0x07, 0x06, 0x03, 0x05, \ + 0x04, 0x49, 0xe0, 0x04, \ + 0x08, 0x0a, 0xe3, 0xc0, \ + 0x80, 0xc5, 0xe3, 0xfc, \ + 0x04, 0x49, 0x83, 0x4b, \ + 0x40, 0x02, 0x80, 0x00, \ + 0x06, 0x30, 0x90, 0x90, \ + 0x82, 0x21, 0x6c, 0x04, \ + 0xfc, 0x55, 0xa1, 0x3b, \ + 0xd0, 0x07, 0x5d, 0x93, \ + 0xfa, 0x7c, 0x04, 0x4c, \ + 0x80, 0x8b, 0x02, 0x6b, \ + 0x81, 0xa1, 0x01, 0x91, \ + 0x81, 0x71, 0x03, 0x55, \ + 0xf0, 0xa4, 0x56, 0x82, \ + 0x6b, 0x71, 0x16, 0x09, \ + 0x8d, 0x0b, 0xc0, 0x30, \ + 0x02, 0x94, 0x82, 0x95, \ + 0x90, 0x0b, 0x7a, 0x80, \ + 0xef, 0xf8, 0x7d, 0x68, \ + 0x16, 0x79, 0xa1, 0x0b, \ + 0x40, 0x30, 0x0c, 0x81, \ + 0xc0, 0x00, 0x14, 0x16, \ + 0x12, 0x0a, 0x82, 0x91, \ + 0xfa, 0x75, 0xe0, 0x80, \ + 0x88, 0x55, 0xa1, 0xab, \ + 0x66, 0x51, 0x12, 0x80, \ + 0x10, 0x80, 0x40, 0xc0, \ + 0xc0, 0xbf, 0x65, 0x51, \ + 0x60, 0x04, 0x15, 0x06, \ + 0x83, 0x65, 0x9d, 0xb1, \ + 0x78, 0x81, 0x12, 0x09, \ + 0x42, 0x00, 0xc0, 0x01, \ + 0xc9, 0x07, 0x70, 0x3c, \ + 0xe6, 0xae, 0x42, 0x1a, \ + 0x8d, 0x1b, 0x80, 0x13, \ + 0xa4, 0x18, 0x80, 0xf5, \ + 0x73, 0x38, 0xa0, 0x0a, \ + 0x82, 0x11, 0x68, 0x3c, \ + 0x14, 0xa0, 0x80, 0x7b, \ + 0x82, 0x91, 0x6c, 0x04, \ + 0xa1, 0xab, 0x84, 0x81, \ + 0x00, 0x74, 0xfa, 0x55, \ + 0x0c, 0x4c, 0xe0, 0xa0, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x02, 0x06, 0x00, 0x6b, \ + 0x6a, 0x78, 0xe2, 0x80, \ + 0x43, 0xf6, 0xe8, 0x3c, \ + 0x05, 0x86, 0x60, 0x0c, \ + 0x42, 0xf8, 0xe0, 0x10, \ + 0x8c, 0x0b, 0x6f, 0xa0, \ + 0x45, 0x76, 0x01, 0x11, \ + 0x0c, 0x0b, 0xe0, 0x00, \ + 0x0c, 0x0c, 0x8c, 0x3b, \ + 0x3e, 0x18, 0xf2, 0x00, \ + 0xc1, 0xc3, 0xe0, 0x34, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x40, 0x96, 0x00, 0x6b, \ + 0x4c, 0x01, 0xe0, 0x04, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x02, 0x06, 0x82, 0x7b, \ + 0x8c, 0x0b, 0x62, 0x80, \ + 0xe8, 0x3c, 0x68, 0x88, \ + 0xe4, 0x40, 0x0e, 0x8a, \ + 0x0c, 0x39, 0x82, 0x95, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x00, 0x60, 0x81, 0x71, \ + 0x40, 0x54, 0x66, 0x81, \ + 0x4d, 0x78, 0x81, 0x11, \ + 0xab, 0x21, 0x68, 0x00, \ + 0x61, 0x88, 0x00, 0x74, \ + 0x06, 0x09, 0x91, 0x81, \ + 0x04, 0x84, 0x83, 0x11, \ + 0xa1, 0x1f, 0x69, 0x80, \ + 0x40, 0x22, 0xc8, 0x27, \ + 0x81, 0x11, 0x79, 0x84, \ + 0x44, 0x12, 0x81, 0xa5, \ + 0x04, 0x4a, 0xf9, 0x1c, \ + 0x44, 0x62, 0xf8, 0x0c, \ + 0x82, 0x11, 0x50, 0x84, \ + 0xc0, 0x00, 0x44, 0x01, \ + 0xe0, 0x40, 0x02, 0x0a, \ + 0x01, 0x11, 0xfc, 0xb5, \ + 0x0c, 0x0b, 0x9f, 0x21, \ + 0x59, 0xb8, 0x8c, 0x3b, \ + 0x0c, 0x09, 0xe8, 0x34, \ + 0xc0, 0x2f, 0x6b, 0x99, \ + 0x82, 0xf5, 0xa3, 0x0b, \ + 0x66, 0xb1, 0x00, 0x60, \ + 0x02, 0x06, 0x40, 0x8e, \ + 0x81, 0x45, 0xe0, 0x10, \ + 0x10, 0x80, 0x41, 0x1e, \ + 0xa1, 0x8b, 0xec, 0x04, \ + 0x0c, 0x0c, 0xfe, 0x95, \ + 0xdd, 0x93, 0xf2, 0x0c, \ + 0x8c, 0x1b, 0x0c, 0x0b, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0xe0, 0x30, 0x0a, 0xc8, \ + 0x82, 0x2c, 0x80, 0x1c, \ + 0xdd, 0x93, 0xc0, 0x3c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x68, 0x00, 0x40, 0xc8, \ + 0x8c, 0x0b, 0x80, 0x6b, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0x85, 0x45, \ + 0x82, 0x2c, 0x80, 0x1c, \ + 0xff, 0x35, 0xc0, 0x3c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x61, 0x4c, 0x0c, 0xc6, \ + 0xe1, 0x48, 0x42, 0x06, \ + 0x05, 0x06, 0x18, 0x04, \ + 0x84, 0x14, 0xe0, 0x04, \ + 0x6d, 0x09, 0x40, 0x21, \ + 0x84, 0x08, 0xc0, 0x2f, \ + 0x04, 0x06, 0xd0, 0x09, \ + 0x18, 0x24, 0x83, 0x01, \ + 0x7f, 0x18, 0x81, 0x11, \ + 0x5c, 0x98, 0xef, 0xe8, \ + 0x18, 0x04, 0xef, 0xe8, \ + 0x0c, 0x0c, 0x83, 0x11, \ + 0x04, 0x58, 0xf2, 0x00, \ + 0xc1, 0xc3, 0xe0, 0x04, \ + 0xfa, 0x80, 0x00, 0x0c, \ + 0xe8, 0x00, 0x76, 0xa8, \ + 0x00, 0xc6, 0xa1, 0x0b, \ + 0x00, 0x05, 0xc1, 0x4c, \ + 0x41, 0x78, 0xc0, 0x00, \ + 0x00, 0xc6, 0xc8, 0x00, \ + 0x81, 0x21, 0x61, 0x4c, \ + 0x83, 0x31, 0x00, 0x04, \ + 0x00, 0x19, 0x83, 0x41, \ + 0xc0, 0x31, 0x68, 0x6d, \ + 0x40, 0x11, 0xc2, 0x19, \ + 0x40, 0x31, 0x68, 0x6d, \ + 0x72, 0xfd, 0x43, 0xc6, \ + 0x08, 0x0c, 0xc0, 0x20, \ + 0x36, 0xb8, 0xf2, 0x80, \ + 0xc1, 0xc3, 0xff, 0xe4, \ + 0xfa, 0x04, 0x04, 0x4c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x2f, 0x6d, 0x01, \ + 0xf3, 0xd8, 0x40, 0x00, \ + 0x6d, 0x01, 0x4c, 0x01, \ + 0x81, 0x01, 0x40, 0x2f, \ + 0x65, 0x75, 0x4c, 0x01, \ + 0x72, 0xd8, 0xc0, 0x2f, \ + 0x00, 0x7b, 0xe8, 0x00, \ + 0x72, 0x18, 0x8c, 0x0b, \ + 0xa1, 0x7b, 0xe8, 0x00, \ + 0x4f, 0x38, 0x82, 0x25, \ + 0x8c, 0x0b, 0x68, 0x00, \ + 0x68, 0x00, 0x45, 0x88, \ + 0x7a, 0x18, 0x8c, 0x0b, \ + 0x56, 0x38, 0xef, 0xe8, \ + 0x8c, 0x0b, 0x68, 0x38, \ + 0x9f, 0x21, 0x01, 0x11, \ + 0x8c, 0x0b, 0x03, 0x31, \ + 0xe8, 0x34, 0x51, 0xc8, \ + 0x68, 0x0c, 0x50, 0x08, \ + 0x71, 0xb8, 0x8c, 0x0b, \ + 0x72, 0x78, 0xe8, 0x00, \ + 0x57, 0x18, 0xe8, 0x00, \ + 0x01, 0x01, 0xef, 0xe8, \ + 0x79, 0x38, 0x83, 0x11, \ + 0x8c, 0x2b, 0x6f, 0xe8, \ + 0x68, 0x08, 0x5a, 0x48, \ + 0x41, 0xc6, 0x8c, 0x0b, \ + 0x00, 0x6e, 0xe1, 0x90, \ + 0x53, 0xd8, 0x8c, 0x0b, \ + 0x02, 0xc6, 0xe8, 0x38, \ + 0x04, 0x0e, 0xe1, 0x94, \ + 0x53, 0xb8, 0x8c, 0x0b, \ + 0x43, 0x86, 0xe8, 0x38, \ + 0x84, 0x0e, 0xe1, 0x90, \ + 0xf2, 0x04, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x84, 0x04, 0x0c, \ + 0x61, 0x4c, 0x0c, 0xc6, \ + 0xe1, 0x48, 0x4a, 0x46, \ + 0x19, 0x06, 0x18, 0x44, \ + 0x94, 0x54, 0xe0, 0x04, \ + 0x81, 0x11, 0x00, 0x1c, \ + 0x6d, 0x09, 0x08, 0x79, \ + 0x48, 0xc1, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0xc0, 0x3c, 0x82, 0x2c, \ + 0xd0, 0x09, 0x94, 0x08, \ + 0x83, 0x01, 0x14, 0x06, \ + 0x76, 0x18, 0x98, 0x24, \ + 0x53, 0x98, 0xef, 0xe8, \ + 0x18, 0x04, 0xef, 0xe8, \ + 0x40, 0x71, 0x81, 0x11, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0xf2, 0x84, 0x0c, 0x0c, \ + 0xe0, 0x00, 0x3b, 0x28, \ + 0x60, 0x08, 0x0b, 0x06, \ + 0x60, 0x10, 0x04, 0x26, \ + 0x40, 0x51, 0xc0, 0xc0, \ + 0x40, 0x1c, 0x61, 0x6d, \ + 0x40, 0x51, 0x81, 0x11, \ + 0x40, 0x23, 0x61, 0x4d, \ + 0x40, 0x51, 0x91, 0x31, \ + 0x40, 0x2a, 0x6c, 0xc9, \ + 0x40, 0x51, 0xaf, 0x51, \ + 0x40, 0x2a, 0x6c, 0xd1, \ + 0xe0, 0x0c, 0x4b, 0xf6, \ + 0x68, 0x69, 0x40, 0x51, \ + 0x0a, 0x06, 0x40, 0x31, \ + 0x40, 0x25, 0xe0, 0x08, \ + 0x40, 0x0c, 0x63, 0x51, \ + 0x40, 0x53, 0x8b, 0x21, \ + 0x40, 0x13, 0x6f, 0xc5, \ + 0xe0, 0x00, 0x4b, 0x86, \ + 0x64, 0x09, 0x40, 0x25, \ + 0xa1, 0x21, 0x40, 0x05, \ + 0x6f, 0xb1, 0x40, 0x51, \ + 0x89, 0x51, 0x40, 0x27, \ + 0x68, 0x61, 0x40, 0x51, \ + 0x9d, 0x51, 0x40, 0x23, \ + 0x61, 0x65, 0x40, 0x21, \ + 0x83, 0x41, 0x40, 0x1c, \ + 0x66, 0x61, 0x40, 0x23, \ + 0x40, 0x23, 0xc0, 0x15, \ + 0xc0, 0x13, 0x6f, 0xc1, \ + 0x6f, 0x91, 0x40, 0x21, \ + 0x05, 0x86, 0x40, 0x27, \ + 0x40, 0x23, 0xe0, 0x10, \ + 0x40, 0x13, 0x6f, 0xd1, \ + 0xe0, 0x8c, 0x04, 0x06, \ + 0x6b, 0xb1, 0x40, 0x51, \ + 0x00, 0x59, 0xc0, 0x23, \ + 0xc0, 0x15, 0x62, 0x21, \ + 0x6e, 0xc1, 0x40, 0x11, \ + 0x40, 0x15, 0xc0, 0x30, \ + 0xc0, 0x05, 0x64, 0x05, \ + 0x6b, 0x2d, 0x40, 0x15, \ + 0x95, 0x11, 0x40, 0x0a, \ + 0x6f, 0xd5, 0x40, 0x23, \ + 0x44, 0x86, 0x40, 0x13, \ + 0x40, 0x11, 0xe0, 0x00, \ + 0x40, 0x2a, 0x6c, 0xcd, \ + 0x40, 0x11, 0xc2, 0x59, \ + 0x40, 0x23, 0x6b, 0xb5, \ + 0xe0, 0x0c, 0x03, 0x06, \ + 0x6f, 0xb5, 0x40, 0x21, \ + 0x85, 0x21, 0x40, 0x27, \ + 0x64, 0x89, 0x40, 0x11, \ + 0x02, 0x06, 0x40, 0x31, \ + 0x40, 0x21, 0xe0, 0x70, \ + 0x40, 0x23, 0x68, 0x65, \ + 0x40, 0x13, 0xbf, 0x21, \ + 0x40, 0x18, 0x6a, 0x49, \ + 0x40, 0x35, 0xb7, 0x11, \ + 0xc0, 0x05, 0x64, 0x01, \ + 0x61, 0x45, 0x40, 0x41, \ + 0x40, 0x31, 0xc0, 0x23, \ + 0xc0, 0x27, 0x6f, 0x95, \ + 0x64, 0x11, 0x40, 0x41, \ + 0x40, 0x21, 0xc0, 0x31, \ + 0xc0, 0x31, 0x64, 0xe9, \ + 0x65, 0x19, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x31, \ + 0xc0, 0x31, 0x65, 0x49, \ + 0x65, 0x79, 0x40, 0x11, \ + 0x40, 0x21, 0xc0, 0x31, \ + 0xc0, 0x31, 0x65, 0xa9, \ + 0x60, 0x81, 0x40, 0x31, \ + 0x40, 0x41, 0xc0, 0x27, \ + 0xc0, 0x30, 0x6d, 0x39, \ + 0x62, 0x21, 0x40, 0x51, \ + 0xdd, 0x93, 0xc0, 0x15, \ + 0xf8, 0x3c, 0x04, 0x4c, \ + 0x81, 0x21, 0x2f, 0x41, \ + 0x6e, 0x81, 0x40, 0x41, \ + 0x87, 0x31, 0x40, 0x28, \ + 0x6e, 0xa1, 0x40, 0x41, \ + 0x97, 0x51, 0x40, 0x29, \ + 0x65, 0xb1, 0x40, 0x41, \ + 0x58, 0x06, 0x40, 0x2a, \ + 0x40, 0x41, 0xe0, 0x00, \ + 0x40, 0x2b, 0x64, 0xf1, \ + 0xe0, 0xc8, 0x0c, 0x06, \ + 0x67, 0x21, 0x40, 0x41, \ + 0x09, 0x06, 0x40, 0x2b, \ + 0x40, 0x41, 0xe0, 0x08, \ + 0x40, 0x2d, 0x6e, 0xc5, \ + 0xe0, 0x7c, 0x09, 0x06, \ + 0x6d, 0x99, 0x40, 0x45, \ + 0xa3, 0x41, 0x40, 0x0b, \ + 0x66, 0x71, 0x40, 0x41, \ + 0x49, 0x86, 0x40, 0x2f, \ + 0x40, 0x43, 0xef, 0xf0, \ + 0x40, 0x17, 0x6b, 0x79, \ + 0xe0, 0x24, 0x09, 0x86, \ + 0x62, 0x05, 0x40, 0x21, \ + 0x02, 0x06, 0x40, 0x15, \ + 0x40, 0x21, 0xe3, 0xc0, \ + 0x40, 0x31, 0x64, 0xad, \ + 0x72, 0xfd, 0x45, 0xb6, \ + 0x40, 0x43, 0xc0, 0x20, \ + 0x40, 0x17, 0x6b, 0x7d, \ + 0x40, 0x25, 0x99, 0x41, \ + 0x40, 0x0c, 0x65, 0x31, \ + 0xe0, 0x0c, 0x12, 0x86, \ + 0x65, 0x35, 0x40, 0x25, \ + 0x55, 0xc6, 0x40, 0x0c, \ + 0x40, 0x25, 0xe0, 0x04, \ + 0x40, 0x0c, 0x65, 0x49, \ + 0xe0, 0x04, 0x0f, 0x06, \ + 0x65, 0x4d, 0x40, 0x25, \ + 0x04, 0x06, 0x40, 0x0c, \ + 0xc0, 0x20, 0x60, 0x01, \ + 0x67, 0x01, 0x40, 0x41, \ + 0x08, 0x06, 0x40, 0x2f, \ + 0x40, 0x51, 0xf9, 0x00, \ + 0x40, 0x28, 0x6e, 0x8d, \ + 0x40, 0x31, 0x89, 0x81, \ + 0xc0, 0x28, 0x6e, 0x91, \ + 0x6e, 0x9d, 0x40, 0xc1, \ + 0x40, 0x51, 0xc0, 0x28, \ + 0xc0, 0x29, 0x6e, 0xad, \ + 0x6e, 0xb1, 0x40, 0x31, \ + 0x40, 0xc1, 0xc0, 0x29, \ + 0xc0, 0x29, 0x6e, 0xbd, \ + 0x65, 0xbd, 0x40, 0x51, \ + 0x40, 0x31, 0xc0, 0x2a, \ + 0xc0, 0x2a, 0x65, 0xc1, \ + 0x65, 0xcd, 0x40, 0xc1, \ + 0x40, 0x51, 0xc0, 0x2a, \ + 0xc0, 0x2b, 0x64, 0xfd, \ + 0x67, 0x2d, 0x40, 0x51, \ + 0x0b, 0xc6, 0x40, 0x2b, \ + 0x40, 0x31, 0xe0, 0x5c, \ + 0xc0, 0x2b, 0x65, 0x01, \ + 0x65, 0x0d, 0x40, 0xc1, \ + 0x40, 0xc1, 0xc0, 0x2b, \ + 0x40, 0x2b, 0x67, 0x3d, \ + 0x40, 0x31, 0x85, 0xc1, \ + 0xc0, 0x2b, 0x67, 0x31, \ + 0x64, 0x99, 0x40, 0x31, \ + 0x40, 0x31, 0xc0, 0x31, \ + 0x40, 0x31, 0x64, 0xb1, \ + 0x40, 0x25, 0xbd, 0x31, \ + 0xc0, 0x0c, 0x65, 0x55, \ + 0x65, 0x59, 0x40, 0x25, \ + 0xb9, 0x21, 0x40, 0x0c, \ + 0x6d, 0xc5, 0x40, 0x45, \ + 0x08, 0x0e, 0x40, 0x0b, \ + 0x40, 0x53, 0xff, 0xc0, \ + 0x40, 0x17, 0x6e, 0x29, \ + 0xe0, 0xcc, 0x0a, 0xd6, \ + 0x64, 0xe9, 0x40, 0x31, \ + 0x06, 0x96, 0x40, 0x31, \ + 0xc0, 0x20, 0x70, 0x01, \ + 0x65, 0xd1, 0x40, 0xc1, \ + 0x40, 0xc1, 0xc0, 0x2f, \ + 0xc0, 0x30, 0x6e, 0xfd, \ + 0x64, 0x81, 0x40, 0xc1, \ + 0x18, 0xc6, 0x40, 0x31, \ + 0x40, 0x21, 0xe0, 0x0c, \ + 0xc0, 0x31, 0x65, 0x79, \ + 0x65, 0xa9, 0x40, 0x21, \ + 0x04, 0x06, 0x40, 0x31, \ + 0x40, 0x15, 0xe1, 0x80, \ + 0x40, 0x05, 0x64, 0x5d, \ + 0x60, 0xc9, 0x02, 0x06, \ + 0x40, 0x45, 0xc0, 0x14, \ + 0x40, 0x0b, 0x6d, 0xc9, \ + 0xe0, 0x40, 0x08, 0x06, \ + 0x6e, 0x2d, 0x40, 0x93, \ + 0x53, 0xf6, 0x40, 0x17, \ + 0x40, 0x53, 0xef, 0xfc, \ + 0x40, 0x17, 0x6e, 0x31, \ + 0xe0, 0x00, 0x4a, 0x66, \ + 0x6e, 0xf1, 0x40, 0xa1, \ + 0xb7, 0xa1, 0x40, 0x30, \ + 0x64, 0x89, 0x40, 0xc1, \ + 0x19, 0x86, 0x40, 0x31, \ + 0x40, 0x25, 0xe0, 0x60, \ + 0x40, 0x0c, 0x66, 0x41, \ + 0xe0, 0x04, 0x44, 0x46, \ + 0x6f, 0x49, 0x40, 0x63, \ + 0x40, 0x63, 0xc0, 0x14, \ + 0xc0, 0x15, 0x62, 0xd1, \ + 0x6a, 0x71, 0x40, 0x63, \ + 0x40, 0x63, 0xc0, 0x15, \ + 0x40, 0x15, 0x6b, 0x89, \ + 0xe0, 0x08, 0x0c, 0x06, \ + 0x65, 0x3d, 0x40, 0x35, \ + 0x40, 0x35, 0xc0, 0x0c, \ + 0x40, 0x0c, 0x65, 0x41, \ + 0x68, 0x01, 0x06, 0x66, \ + 0x40, 0x15, 0xc0, 0x20, \ + 0x40, 0x0a, 0x63, 0x9d, \ + 0x40, 0x45, 0x91, 0x11, \ + 0x40, 0x0b, 0x6d, 0xcd, \ + 0x40, 0x93, 0x8b, 0x41, \ + 0x40, 0x17, 0x6e, 0x39, \ + 0xe0, 0x0c, 0x53, 0xf6, \ + 0x6c, 0x85, 0x40, 0x51, \ + 0x8f, 0x51, 0x40, 0x2f, \ + 0x6e, 0xf9, 0x40, 0xa1, \ + 0xab, 0xa1, 0x40, 0x30, \ + 0x6a, 0x49, 0x40, 0xc3, \ + 0x8d, 0xc1, 0x40, 0x18, \ + 0x6c, 0x89, 0x40, 0x23, \ + 0x44, 0x86, 0x40, 0x18, \ + 0x40, 0x73, 0xe0, 0x3c, \ + 0xc0, 0x14, 0x6f, 0x4d, \ + 0x62, 0xd5, 0x40, 0x73, \ + 0x40, 0x73, 0xc0, 0x15, \ + 0xc0, 0x15, 0x6a, 0x75, \ + 0x6b, 0x8d, 0x40, 0x73, \ + 0x83, 0x71, 0x40, 0x15, \ + 0x6e, 0xc1, 0x40, 0x61, \ + 0x40, 0x61, 0xc0, 0x2d, \ + 0xc0, 0x2d, 0x6e, 0xc9, \ + 0x6f, 0x0d, 0x40, 0x61, \ + 0xb5, 0x61, 0x40, 0x30, \ + 0x65, 0x61, 0x40, 0x35, \ + 0x40, 0x35, 0xc0, 0x0c, \ + 0x40, 0x0c, 0x65, 0x65, \ + 0x40, 0x11, 0x93, 0x31, \ + 0xc0, 0x28, 0x6e, 0x85, \ + 0x6e, 0x89, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x28, \ + 0xc0, 0x28, 0x6e, 0x95, \ + 0x6e, 0x99, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x28, \ + 0xc0, 0x29, 0x6e, 0xa5, \ + 0x6e, 0xa9, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x29, \ + 0xc0, 0x29, 0x6e, 0xb5, \ + 0x6e, 0xb9, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x29, \ + 0xc0, 0x2a, 0x65, 0xb5, \ + 0x65, 0xb9, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x2a, \ + 0xc0, 0x2a, 0x65, 0xc5, \ + 0x65, 0xc9, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x2a, \ + 0xc0, 0x2b, 0x64, 0xf5, \ + 0x64, 0xf9, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x65, 0x05, \ + 0x65, 0x09, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x67, 0x25, \ + 0x67, 0x29, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x2b, \ + 0xc0, 0x2b, 0x67, 0x35, \ + 0x67, 0x39, 0x40, 0x11, \ + 0x40, 0x71, 0xc0, 0x2b, \ + 0xc0, 0x2f, 0x65, 0xb1, \ + 0x66, 0x75, 0x40, 0x11, \ + 0x40, 0x71, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x66, 0x79, \ + 0x67, 0x05, 0x40, 0x71, \ + 0x40, 0x81, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x67, 0x09, \ + 0x67, 0x0d, 0x40, 0x81, \ + 0x40, 0x71, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x67, 0x41, \ + 0x67, 0x45, 0x40, 0x71, \ + 0x40, 0x41, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6b, 0x91, \ + 0x6b, 0x95, 0x40, 0x81, \ + 0x40, 0x81, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6c, 0x79, \ + 0x6c, 0x7d, 0x40, 0x71, \ + 0x40, 0x71, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6c, 0x81, \ + 0x6d, 0x95, 0x40, 0x91, \ + 0x40, 0x51, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6d, 0xf1, \ + 0x6e, 0xc9, 0x40, 0x51, \ + 0x40, 0x71, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6e, 0xcd, \ + 0x6e, 0xd1, 0x40, 0x71, \ + 0x40, 0x81, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6e, 0xd5, \ + 0x6e, 0xd9, 0x40, 0x81, \ + 0x40, 0x11, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6e, 0xf5, \ + 0x6f, 0x01, 0x40, 0x71, \ + 0x40, 0xa1, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6f, 0x05, \ + 0x6f, 0x15, 0x40, 0x11, \ + 0x40, 0x41, 0xc0, 0x30, \ + 0xc0, 0x31, 0x64, 0x65, \ + 0x64, 0x69, 0x40, 0x61, \ + 0x40, 0x71, 0xc0, 0x31, \ + 0xc0, 0x31, 0x64, 0x71, \ + 0x64, 0x75, 0x40, 0x41, \ + 0x40, 0x61, 0xc0, 0x31, \ + 0xc0, 0x31, 0x64, 0x79, \ + 0x64, 0x85, 0x40, 0x41, \ + 0x40, 0x41, 0xc0, 0x31, \ + 0xc0, 0x31, 0x64, 0x9d, \ + 0x64, 0xa1, 0x40, 0x81, \ + 0x40, 0x41, 0xc0, 0x31, \ + 0xc0, 0x31, 0x64, 0xa5, \ + 0x64, 0xa9, 0x40, 0x91, \ + 0x40, 0x41, 0xc0, 0x31, \ + 0xc0, 0x31, 0x64, 0xe1, \ + 0x64, 0xe5, 0x40, 0x41, \ + 0x40, 0xc1, 0xc0, 0x31, \ + 0xc0, 0x31, 0x65, 0x11, \ + 0x65, 0x15, 0x40, 0x41, \ + 0x40, 0x61, 0xc0, 0x31, \ + 0xc0, 0x31, 0x65, 0x19, \ + 0x65, 0x41, 0x40, 0x51, \ + 0x40, 0x41, 0xc0, 0x31, \ + 0xc0, 0x31, 0x65, 0x45, \ + 0x65, 0x49, 0x40, 0x61, \ + 0x40, 0x11, 0xc0, 0x31, \ + 0xc0, 0x31, 0x65, 0x71, \ + 0x65, 0x75, 0x40, 0x41, \ + 0x40, 0x31, 0xc0, 0x31, \ + 0xc0, 0x31, 0x65, 0xa1, \ + 0x65, 0xa5, 0x40, 0x41, \ + 0x40, 0x51, 0xc0, 0x31, \ + 0xc0, 0x31, 0x65, 0xd1, \ + 0x65, 0xd5, 0x40, 0x51, \ + 0x40, 0x23, 0xc0, 0x31, \ + 0xc0, 0x18, 0x6c, 0x8d, \ + 0x6c, 0xc5, 0x40, 0x11, \ + 0x0c, 0x4c, 0xc0, 0x31, \ + 0xdd, 0x93, 0xf0, 0x3c, \ + 0x61, 0x90, 0x42, 0x86, \ + 0x04, 0x4e, 0x83, 0x41, \ + 0xe1, 0x4c, 0x06, 0xc6, \ + 0x05, 0x46, 0x0c, 0x54, \ + 0x08, 0xc4, 0xe1, 0x4c, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x65, 0x11, 0x4a, 0x45, \ + 0xb0, 0x0e, 0xc0, 0x0c, \ + 0xc1, 0x83, 0x82, 0xe5, \ + 0x0c, 0x04, 0xc3, 0x13, \ + 0xe1, 0x34, 0x47, 0x06, \ + 0x00, 0x00, 0x8c, 0x34, \ + 0xc0, 0xc5, 0x64, 0x41, \ + 0x40, 0xc6, 0x0c, 0x0c, \ + 0x80, 0x04, 0xe1, 0x34, \ + 0x84, 0x04, 0x80, 0x1c, \ + 0xfe, 0xd5, 0xa1, 0x0b, \ + 0x81, 0x11, 0x08, 0x04, \ + 0xdd, 0x93, 0x80, 0x1e, \ + 0xe1, 0x14, 0x03, 0x46, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x6c, 0xe1, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x2f, \ + 0xe1, 0x48, 0x40, 0x86, \ + 0x80, 0x08, 0x80, 0x04, \ + 0xe0, 0x00, 0x00, 0x4a, \ + 0xe2, 0x00, 0x00, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x61, 0x48, 0x40, 0x86, \ + 0x80, 0x04, 0x81, 0x11, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0x61, 0x48, 0x40, 0x06, \ + 0x80, 0x04, 0x83, 0x11, \ + 0x5d, 0x93, 0x80, 0x08, \ + 0xf8, 0x84, 0x40, 0x02, \ + 0xe1, 0x48, 0x40, 0x06, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xc2, 0x19, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xe1, 0x48, 0x40, 0x06, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xf8, 0x42, 0x10, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xe1, 0x48, 0x40, 0x06, \ + 0x80, 0x00, 0x80, 0x04, \ + 0x40, 0x46, 0xa1, 0x0d, \ + 0x00, 0x05, 0x81, 0x48, \ + 0x00, 0x19, 0x80, 0x00, \ + 0x42, 0x10, 0x80, 0x00, \ + 0x40, 0x11, 0x9c, 0x04, \ + 0xdd, 0x93, 0x80, 0x00, \ + 0xe1, 0x48, 0x40, 0x06, \ + 0x80, 0x00, 0x80, 0x04, \ + 0x40, 0x46, 0xa1, 0x0d, \ + 0x00, 0x05, 0x81, 0x48, \ + 0x00, 0x19, 0x80, 0x00, \ + 0x42, 0x10, 0x80, 0x00, \ + 0x40, 0x11, 0x93, 0xf8, \ + 0xdd, 0x93, 0x80, 0x00, \ + 0xe1, 0x48, 0x40, 0x06, \ + 0x80, 0x00, 0x80, 0x04, \ + 0x40, 0x46, 0xa1, 0x0d, \ + 0x00, 0x05, 0x81, 0x48, \ + 0x00, 0x19, 0x80, 0x00, \ + 0x42, 0x10, 0x80, 0x00, \ + 0x40, 0x11, 0x9c, 0x08, \ + 0xdd, 0x93, 0x80, 0x00, \ + 0xe1, 0x48, 0x40, 0x06, \ + 0x80, 0x00, 0x80, 0x04, \ + 0x40, 0x46, 0xa1, 0x0d, \ + 0x00, 0x05, 0x81, 0x48, \ + 0x00, 0x19, 0x80, 0x00, \ + 0x42, 0x10, 0x80, 0x00, \ + 0x40, 0x11, 0x93, 0xf4, \ + 0xdd, 0x93, 0x80, 0x00, \ + 0xe1, 0x48, 0x40, 0x06, \ + 0x80, 0x00, 0x80, 0x04, \ + 0x40, 0x46, 0xa1, 0x0d, \ + 0x00, 0x05, 0x81, 0x48, \ + 0x00, 0x19, 0x80, 0x00, \ + 0x42, 0x10, 0x80, 0x00, \ + 0x40, 0x11, 0x9c, 0x10, \ + 0xdd, 0x93, 0x80, 0x00, \ + 0xe1, 0x48, 0x40, 0x06, \ + 0x80, 0x00, 0x80, 0x04, \ + 0x40, 0x46, 0xa1, 0x0d, \ + 0x00, 0x05, 0x81, 0x48, \ + 0x00, 0x19, 0x80, 0x00, \ + 0x42, 0x10, 0x80, 0x00, \ + 0x40, 0x11, 0x93, 0xec, \ + 0xdd, 0x93, 0x80, 0x00, \ + 0xfa, 0x1c, 0x04, 0x4c, \ + 0x61, 0x34, 0x0d, 0x86, \ + 0x18, 0x14, 0x81, 0x71, \ + 0xe1, 0x48, 0x51, 0x86, \ + 0x83, 0x21, 0x03, 0x91, \ + 0xa0, 0x14, 0x84, 0x76, \ + 0xa0, 0x14, 0x84, 0x76, \ + 0xa0, 0x14, 0x84, 0x96, \ + 0x00, 0x15, 0x84, 0x76, \ + 0xc0, 0x0c, 0x64, 0xe9, \ + 0xe8, 0x34, 0x64, 0xa8, \ + 0x00, 0x96, 0x98, 0x04, \ + 0xe1, 0x34, 0x01, 0x46, \ + 0x80, 0x96, 0x80, 0x04, \ + 0x80, 0x76, 0xa0, 0x04, \ + 0x80, 0x96, 0xa0, 0x04, \ + 0x80, 0x76, 0xa0, 0x04, \ + 0xf2, 0x1c, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x02, 0x06, 0x00, 0x6b, \ + 0x5f, 0xf8, 0xe2, 0x80, \ + 0x01, 0x06, 0xe8, 0x38, \ + 0x18, 0x60, 0x61, 0x4c, \ + 0xc0, 0xbe, 0x67, 0x50, \ + 0x0e, 0x60, 0x7c, 0xce, \ + 0xc0, 0xbe, 0x67, 0x11, \ + 0x02, 0x60, 0x3e, 0x7e, \ + 0xc0, 0x57, 0x66, 0xd1, \ + 0x04, 0x60, 0x00, 0x04, \ + 0xc0, 0x54, 0x66, 0x21, \ + 0x64, 0x61, 0x06, 0x60, \ + 0x08, 0x60, 0x40, 0xa1, \ + 0xc0, 0x54, 0x66, 0xb1, \ + 0x0a, 0x60, 0x3c, 0x6e, \ + 0xc0, 0xc7, 0x67, 0x71, \ + 0xef, 0xc4, 0x69, 0x28, \ + 0x6f, 0x71, 0x0c, 0x09, \ + 0x98, 0xf1, 0x40, 0x31, \ + 0x63, 0xf1, 0x0c, 0x19, \ + 0x82, 0x09, 0xc0, 0x31, \ + 0xf3, 0xf8, 0x42, 0x10, \ + 0x4c, 0x11, 0xa0, 0x1f, \ + 0xc0, 0x31, 0x63, 0xf1, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x64, 0x01, 0x00, 0x29, \ + 0xe2, 0x2d, 0xc0, 0x31, \ + 0xda, 0x00, 0x5c, 0x08, \ + 0xe0, 0x80, 0x42, 0x4a, \ + 0xe0, 0x00, 0x04, 0x1a, \ + 0xed, 0x80, 0x42, 0x0a, \ + 0xe9, 0x9c, 0x40, 0x0a, \ + 0xf3, 0x35, 0x80, 0x45, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x63, 0xf1, 0x00, 0x19, \ + 0x04, 0x14, 0xc0, 0x31, \ + 0x42, 0x10, 0x60, 0x84, \ + 0x84, 0x29, 0xf3, 0xf4, \ + 0x40, 0x21, 0xa2, 0x2f, \ + 0xc0, 0x31, 0x63, 0xf1, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x63, 0xf1, 0x00, 0x19, \ + 0x42, 0x0a, 0xc0, 0x31, \ + 0x5c, 0x08, 0xe0, 0x84, \ + 0x00, 0x19, 0x9a, 0x00, \ + 0xc0, 0x2f, 0x65, 0x75, \ + 0x00, 0x19, 0xa7, 0x1b, \ + 0x80, 0x31, 0x03, 0x91, \ + 0x9c, 0x10, 0x42, 0x10, \ + 0x03, 0x91, 0x40, 0x11, \ + 0xed, 0xf5, 0x80, 0x31, \ + 0x6d, 0x41, 0x00, 0x19, \ + 0x00, 0x29, 0xc0, 0x2f, \ + 0xc0, 0x15, 0x61, 0x51, \ + 0xe0, 0x84, 0x06, 0x14, \ + 0x66, 0x86, 0x42, 0x2a, \ + 0xfa, 0x80, 0x04, 0x34, \ + 0x6d, 0x39, 0x40, 0x31, \ + 0x06, 0x0a, 0x40, 0x2f, \ + 0x42, 0x1a, 0xf0, 0x00, \ + 0x04, 0xd6, 0x60, 0x0a, \ + 0x40, 0x21, 0xe0, 0x0c, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x6d, 0x31, 0x40, 0x11, \ + 0x80, 0xc5, 0xc0, 0x2f, \ + 0x65, 0x0d, 0x00, 0x15, \ + 0xaf, 0x1b, 0xc0, 0x0c, \ + 0x00, 0x00, 0x82, 0x55, \ + 0x40, 0x54, 0x66, 0x51, \ + 0x7f, 0x21, 0x81, 0x11, \ + 0xe0, 0x0c, 0x06, 0xd6, \ + 0xff, 0xf8, 0x34, 0x08, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x6d, 0x45, 0x00, 0x19, \ + 0x00, 0x29, 0xc0, 0x2f, \ + 0xc0, 0x15, 0x61, 0x55, \ + 0xe0, 0x84, 0x06, 0x14, \ + 0x66, 0x86, 0x42, 0x2a, \ + 0xfa, 0x80, 0x04, 0x34, \ + 0x6d, 0x3d, 0x40, 0x31, \ + 0x06, 0x0a, 0x40, 0x2f, \ + 0x42, 0x1a, 0xf0, 0x00, \ + 0x04, 0xe6, 0x60, 0x0a, \ + 0x40, 0x21, 0xe0, 0x0c, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x6d, 0x35, 0x40, 0x11, \ + 0x80, 0xc5, 0xc0, 0x2f, \ + 0x65, 0x0d, 0x00, 0x15, \ + 0xb1, 0x1b, 0xc0, 0x0c, \ + 0x00, 0x00, 0x82, 0x55, \ + 0x40, 0x54, 0x66, 0x51, \ + 0x7f, 0x21, 0x81, 0x11, \ + 0xe0, 0x0c, 0x06, 0xe6, \ + 0xff, 0xf8, 0x31, 0x48, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x19, 0x80, 0x6b, \ + 0xc0, 0x15, 0x61, 0x55, \ + 0xec, 0x04, 0x00, 0x10, \ + 0xf4, 0x1c, 0x01, 0x0a, \ + 0x0c, 0x09, 0x84, 0x75, \ + 0xc0, 0x15, 0x61, 0x51, \ + 0xec, 0x04, 0x04, 0x00, \ + 0xf4, 0x24, 0x05, 0x0a, \ + 0x42, 0x1a, 0x82, 0x75, \ + 0x03, 0x0a, 0xe6, 0x82, \ + 0x80, 0xd5, 0xe5, 0x40, \ + 0x65, 0x0d, 0x0c, 0x15, \ + 0xb5, 0x1b, 0xc0, 0x0c, \ + 0x0c, 0x39, 0x82, 0xd5, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x66, 0x51, 0x00, 0x60, \ + 0x01, 0x11, 0xc0, 0x54, \ + 0x6e, 0xb8, 0x93, 0x21, \ + 0x0c, 0x09, 0xef, 0xf8, \ + 0xc0, 0x15, 0x61, 0x51, \ + 0x61, 0x61, 0x0c, 0x19, \ + 0x04, 0x04, 0x40, 0x15, \ + 0x44, 0x1a, 0xfa, 0x80, \ + 0x05, 0x0a, 0xe6, 0x8a, \ + 0x84, 0x35, 0xe4, 0xa0, \ + 0x61, 0x55, 0x0c, 0x29, \ + 0x0c, 0x39, 0xc0, 0x15, \ + 0xc0, 0x15, 0x61, 0x65, \ + 0xe6, 0x8a, 0x44, 0x3a, \ + 0xe4, 0x80, 0x05, 0x0a, \ + 0x0c, 0x25, 0x80, 0xd5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0x84, 0x35, 0xb7, 0x2b, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x00, 0x60, 0x40, 0x2f, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0xa3, 0x21, 0x01, 0x11, \ + 0xef, 0xf8, 0x6c, 0x48, \ + 0x61, 0x51, 0x0c, 0x09, \ + 0x0c, 0x19, 0xc0, 0x15, \ + 0xc0, 0x15, 0x61, 0x61, \ + 0x7a, 0x80, 0x00, 0x04, \ + 0xfa, 0x80, 0x04, 0x14, \ + 0x61, 0x59, 0x0c, 0x39, \ + 0x40, 0x2a, 0xc0, 0x15, \ + 0x86, 0x03, 0xe6, 0x82, \ + 0xe4, 0xa0, 0x01, 0x0a, \ + 0x0c, 0x09, 0x84, 0x15, \ + 0xc0, 0x15, 0x61, 0x55, \ + 0x61, 0x65, 0x0c, 0x29, \ + 0x0c, 0x39, 0xc0, 0x15, \ + 0xc0, 0x15, 0x61, 0x5d, \ + 0xe6, 0x82, 0x40, 0x2a, \ + 0x00, 0x8a, 0x86, 0x03, \ + 0x82, 0xd5, 0xe4, 0x84, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x00, 0x60, 0x40, 0x2f, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0xa3, 0x21, 0x01, 0x11, \ + 0xef, 0xf8, 0x69, 0x88, \ + 0x61, 0x61, 0x0c, 0x19, \ + 0x02, 0x0a, 0xc0, 0x15, \ + 0x80, 0xd5, 0xf0, 0x10, \ + 0x61, 0x65, 0x0c, 0x09, \ + 0xa9, 0x0b, 0xc0, 0x15, \ + 0x0c, 0x05, 0x80, 0xc5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0x86, 0x95, 0xb9, 0x0b, \ + 0x61, 0x51, 0x0c, 0x09, \ + 0x40, 0x4a, 0xc0, 0x15, \ + 0x82, 0x85, 0xe0, 0x80, \ + 0x61, 0x55, 0x0c, 0x09, \ + 0x40, 0x4a, 0xc0, 0x15, \ + 0x80, 0xc5, 0xe0, 0x80, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0xb9, 0x0b, 0xc0, 0x0c, \ + 0x0c, 0x39, 0x82, 0x75, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x66, 0x51, 0x00, 0x60, \ + 0x01, 0x11, 0xc0, 0x54, \ + 0x66, 0xd8, 0x95, 0x21, \ + 0x0c, 0x09, 0xef, 0xf8, \ + 0xc0, 0x15, 0x61, 0x59, \ + 0xf8, 0x0c, 0x00, 0x4a, \ + 0x0c, 0x09, 0x86, 0x05, \ + 0xc0, 0x15, 0x61, 0x5d, \ + 0xf8, 0x0c, 0x00, 0x4a, \ + 0x0c, 0x09, 0x84, 0x45, \ + 0xc0, 0x15, 0x61, 0x61, \ + 0xf8, 0x0c, 0x00, 0x4a, \ + 0x0c, 0x09, 0x82, 0x85, \ + 0xc0, 0x15, 0x61, 0x65, \ + 0xf8, 0x0c, 0x00, 0x4a, \ + 0x0c, 0x05, 0x80, 0xc5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0x82, 0x75, 0xbb, 0x0b, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x00, 0x60, 0x40, 0x2f, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0x97, 0x21, 0x01, 0x11, \ + 0xef, 0xf8, 0x64, 0x68, \ + 0x61, 0x51, 0x0c, 0x09, \ + 0xa3, 0x0b, 0xc0, 0x15, \ + 0x0c, 0x09, 0x88, 0xf5, \ + 0xc0, 0x15, 0x61, 0x55, \ + 0x88, 0x55, 0xa3, 0x0b, \ + 0x61, 0x61, 0x0c, 0x09, \ + 0x00, 0x00, 0xc0, 0x15, \ + 0x01, 0x0a, 0xec, 0x10, \ + 0x82, 0xd5, 0xf4, 0x30, \ + 0x61, 0x65, 0x0c, 0x09, \ + 0x00, 0x00, 0xc0, 0x15, \ + 0x01, 0x0a, 0xec, 0x10, \ + 0x80, 0xd5, 0xf4, 0x30, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0xbd, 0x0b, 0xc0, 0x0c, \ + 0x0c, 0x39, 0x8c, 0x15, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x66, 0x51, 0x00, 0x60, \ + 0x01, 0x11, 0xc0, 0x54, \ + 0x0c, 0x0c, 0x9b, 0x21, \ + 0x21, 0xb8, 0xf2, 0x00, \ + 0x0c, 0x09, 0xff, 0xf8, \ + 0xc0, 0x15, 0x61, 0x61, \ + 0x82, 0x25, 0xb1, 0x0b, \ + 0x84, 0x35, 0xa9, 0x0b, \ + 0x61, 0x65, 0x0c, 0x09, \ + 0xa9, 0x0b, 0xc0, 0x15, \ + 0x81, 0xb5, 0x80, 0xe5, \ + 0x61, 0x65, 0x0c, 0x09, \ + 0xb1, 0x0b, 0xc0, 0x15, \ + 0x0c, 0x05, 0x80, 0xd5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0x82, 0xb5, 0xbf, 0x0b, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x00, 0x60, 0x40, 0x2f, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0x99, 0x21, 0x01, 0x11, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0xf8, 0x1f, 0x88, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x6d, 0x45, 0x00, 0x29, \ + 0x00, 0x19, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6d, 0x41, \ + 0x06, 0x14, 0xc5, 0x27, \ + 0x89, 0x11, 0x61, 0x88, \ + 0xe6, 0x8a, 0x48, 0x3a, \ + 0x6d, 0x39, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x2f, \ + 0x40, 0x2f, 0x6d, 0x3d, \ + 0x40, 0x11, 0x83, 0x11, \ + 0x40, 0x2f, 0x6d, 0x49, \ + 0xe0, 0x40, 0x08, 0x0a, \ + 0x6d, 0x31, 0x40, 0x31, \ + 0x40, 0x21, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6d, 0x35, \ + 0x6d, 0x29, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x30, \ + 0x80, 0x30, 0x0d, 0x31, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x81, 0x71, 0x00, 0x6b, \ + 0x65, 0x65, 0x0c, 0x09, \ + 0x91, 0x76, 0xc0, 0x2f, \ + 0x00, 0x00, 0x89, 0x76, \ + 0x00, 0x8a, 0xec, 0x2c, \ + 0x40, 0x06, 0x74, 0x0c, \ + 0x4c, 0x01, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x6a, 0x78, 0xac, 0xb5, \ + 0x8c, 0x0b, 0x6f, 0xfc, \ + 0x61, 0x51, 0x0c, 0x09, \ + 0x04, 0xf0, 0x40, 0x15, \ + 0x0c, 0x19, 0xe4, 0x10, \ + 0xc0, 0x15, 0x61, 0x55, \ + 0x66, 0x82, 0x40, 0x1a, \ + 0xe4, 0x20, 0x02, 0xf0, \ + 0x6f, 0x09, 0x4c, 0x01, \ + 0x8c, 0x0b, 0x40, 0x15, \ + 0xe8, 0x00, 0x4a, 0xe8, \ + 0x61, 0x59, 0x0c, 0x09, \ + 0x91, 0x18, 0xc0, 0x15, \ + 0x61, 0x5d, 0x0c, 0x29, \ + 0x09, 0x38, 0xc0, 0x15, \ + 0x0c, 0x09, 0xc5, 0x07, \ + 0x40, 0x15, 0x61, 0x51, \ + 0x4c, 0x11, 0x80, 0x13, \ + 0x40, 0x2f, 0x6d, 0x29, \ + 0xe1, 0x88, 0x08, 0x24, \ + 0x61, 0x61, 0x0c, 0x19, \ + 0x86, 0x43, 0x40, 0x15, \ + 0x61, 0x65, 0x0c, 0x29, \ + 0xa3, 0x0b, 0x40, 0x15, \ + 0x61, 0x55, 0x0c, 0x39, \ + 0x4c, 0x41, 0xc0, 0x15, \ + 0xc0, 0x2f, 0x6d, 0x2d, \ + 0x6d, 0x41, 0x4c, 0x11, \ + 0x4c, 0x21, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6d, 0x45, \ + 0x06, 0x0a, 0x82, 0x15, \ + 0x80, 0xb5, 0xf0, 0x04, \ + 0x6f, 0xfc, 0x78, 0x68, \ + 0x8d, 0x65, 0x8c, 0x0b, \ + 0x6d, 0x49, 0x4c, 0x71, \ + 0x07, 0x0a, 0x40, 0x2f, \ + 0x82, 0x55, 0xf4, 0x0c, \ + 0x6d, 0x3d, 0x4c, 0x21, \ + 0xea, 0x0d, 0x40, 0x2f, \ + 0x6d, 0x35, 0x4c, 0x31, \ + 0x88, 0xa5, 0xc0, 0x2f, \ + 0x06, 0x0a, 0x85, 0xb5, \ + 0x84, 0xd5, 0xf0, 0x10, \ + 0x89, 0x31, 0x68, 0x0d, \ + 0x6d, 0x3d, 0x4c, 0x21, \ + 0x4c, 0x31, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6d, 0x35, \ + 0x5f, 0x18, 0x86, 0x05, \ + 0x8c, 0x0b, 0x6f, 0xfc, \ + 0x6d, 0x31, 0x0c, 0x09, \ + 0xab, 0x0b, 0xc0, 0x2f, \ + 0x61, 0x58, 0x86, 0x55, \ + 0x8c, 0x0b, 0x6f, 0xfc, \ + 0x61, 0x18, 0x83, 0x65, \ + 0x8c, 0x0b, 0x6f, 0xfc, \ + 0x61, 0x51, 0x0c, 0x09, \ + 0xe8, 0x0d, 0xc0, 0x15, \ + 0x0c, 0x19, 0x82, 0x75, \ + 0xc0, 0x2f, 0x6d, 0x41, \ + 0x6d, 0x39, 0x4c, 0x11, \ + 0x4c, 0x01, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6d, 0x31, \ + 0x5d, 0x38, 0x81, 0x45, \ + 0x8c, 0x0b, 0x6f, 0xfc, \ + 0x6d, 0x31, 0x0c, 0x09, \ + 0x0c, 0x19, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6d, 0x35, \ + 0x6d, 0x41, 0x0c, 0x29, \ + 0x0c, 0x39, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6d, 0x45, \ + 0xe6, 0x82, 0x40, 0x1a, \ + 0x6d, 0x3d, 0x0c, 0x19, \ + 0x00, 0x0a, 0xc0, 0x2f, \ + 0x00, 0x16, 0xe0, 0x40, \ + 0x4c, 0x01, 0x80, 0x00, \ + 0x80, 0x30, 0x0d, 0x31, \ + 0x6d, 0x39, 0x0c, 0x09, \ + 0x40, 0x1a, 0xc0, 0x2f, \ + 0x42, 0x3a, 0xe6, 0x82, \ + 0x40, 0x0a, 0xe6, 0x8a, \ + 0x4c, 0x01, 0xe0, 0x06, \ + 0x40, 0x30, 0x6d, 0x29, \ + 0xfa, 0x80, 0x02, 0x04, \ + 0x66, 0x89, 0x4c, 0x13, \ + 0x44, 0x78, 0xc0, 0x18, \ + 0x8c, 0x0b, 0x68, 0x00, \ + 0x6f, 0xf4, 0x57, 0xa8, \ + 0x64, 0x18, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0x68, 0x1c, \ + 0x68, 0x24, 0x72, 0x08, \ + 0x21, 0x01, 0x8c, 0x0b, \ + 0x4c, 0x01, 0x83, 0x11, \ + 0x40, 0x1c, 0x61, 0x61, \ + 0x4c, 0x11, 0x98, 0xf1, \ + 0xc0, 0x23, 0x61, 0x41, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x65, 0x21, 0x00, 0x59, \ + 0x81, 0x41, 0x40, 0x28, \ + 0x65, 0x25, 0x00, 0x39, \ + 0x81, 0x01, 0x40, 0x28, \ + 0xe5, 0x40, 0x0a, 0x8a, \ + 0x81, 0x88, 0x0a, 0x54, \ + 0x0c, 0x50, 0x08, 0x50, \ + 0xe5, 0x00, 0x06, 0x8a, \ + 0x06, 0x34, 0x04, 0x46, \ + 0x00, 0x34, 0x81, 0x88, \ + 0x00, 0x00, 0x9a, 0x80, \ + 0x88, 0x06, 0x8c, 0x40, \ + 0xa1, 0x3b, 0x84, 0x38, \ + 0xc4, 0x08, 0x06, 0x30, \ + 0xc1, 0x88, 0x06, 0x34, \ + 0xc0, 0x00, 0x42, 0x31, \ + 0xc0, 0x00, 0x04, 0x09, \ + 0xfb, 0xfc, 0x00, 0x0a, \ + 0xda, 0x80, 0x00, 0x04, \ + 0xc4, 0x08, 0x00, 0x00, \ + 0xc1, 0x88, 0x00, 0x04, \ + 0xc0, 0x00, 0x44, 0x01, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x68, 0x00, 0x40, 0x88, \ + 0x8c, 0x0b, 0x80, 0x6b, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0x8b, 0xc5, \ + 0xfa, 0x04, 0x04, 0x4c, \ + 0x06, 0xb6, 0x00, 0x6b, \ + 0x0c, 0x19, 0xe0, 0x0c, \ + 0xc0, 0x2f, 0x6d, 0x41, \ + 0x6d, 0x45, 0x0c, 0x09, \ + 0x4c, 0x31, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0xe5, 0x02, 0x44, 0x1a, \ + 0x80, 0xe5, 0xa1, 0x2b, \ + 0x65, 0x0d, 0x0c, 0x35, \ + 0x06, 0x0a, 0xc0, 0x0c, \ + 0x84, 0x55, 0xe0, 0x40, \ + 0x66, 0x51, 0x00, 0x60, \ + 0x81, 0x11, 0x40, 0x54, \ + 0x06, 0xb6, 0x1f, 0x21, \ + 0x49, 0xf8, 0xe0, 0x0c, \ + 0x81, 0x71, 0x6f, 0xf8, \ + 0x6d, 0x41, 0x0c, 0x19, \ + 0x0c, 0x09, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6d, 0x45, \ + 0x0c, 0x39, 0x83, 0x75, \ + 0xc0, 0x2f, 0x6d, 0x31, \ + 0x6d, 0x35, 0x0c, 0x49, \ + 0x0c, 0x59, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6d, 0x39, \ + 0x6d, 0x3d, 0x0c, 0xc9, \ + 0x46, 0x3a, 0xc0, 0x2f, \ + 0x48, 0x5a, 0xe5, 0x12, \ + 0x46, 0x3a, 0xe5, 0x32, \ + 0x44, 0x3a, 0xe5, 0x12, \ + 0x0e, 0x22, 0xe0, 0x0a, \ + 0x42, 0x07, 0xef, 0xfc, \ + 0xfa, 0x80, 0x04, 0x74, \ + 0x6f, 0x05, 0x4c, 0x01, \ + 0x00, 0x06, 0x40, 0x1b, \ + 0x04, 0x22, 0xe0, 0x04, \ + 0xc2, 0x17, 0x6d, 0x00, \ + 0x6f, 0x09, 0x4c, 0x01, \ + 0x81, 0x01, 0x40, 0x1b, \ + 0x6f, 0x01, 0x4c, 0x11, \ + 0x4c, 0x21, 0xc0, 0x1b, \ + 0xc0, 0x1b, 0x6f, 0x0d, \ + 0x6f, 0x11, 0x4c, 0x01, \ + 0x0c, 0x4c, 0xc0, 0x1b, \ + 0xdd, 0x93, 0xf2, 0x04, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x81, 0x81, 0x00, 0x6b, \ + 0x6f, 0x0d, 0x0c, 0x79, \ + 0x02, 0xf0, 0x40, 0x1b, \ + 0x0c, 0xa9, 0xe4, 0x20, \ + 0xc0, 0x2f, 0x6d, 0x41, \ + 0x6d, 0x45, 0x0c, 0xb9, \ + 0xfc, 0x8e, 0xc0, 0x2f, \ + 0xbc, 0x86, 0x89, 0x86, \ + 0xe8, 0x00, 0x44, 0x88, \ + 0x02, 0xf0, 0x7c, 0x94, \ + 0x1e, 0x2b, 0xe4, 0x10, \ + 0x75, 0x38, 0x8c, 0x0b, \ + 0xa1, 0x9b, 0xef, 0xfc, \ + 0x0c, 0x39, 0x8e, 0x45, \ + 0x40, 0x2f, 0x6d, 0x2d, \ + 0xec, 0x04, 0x02, 0xa0, \ + 0x04, 0xb0, 0x3c, 0x48, \ + 0x0c, 0x59, 0xec, 0x04, \ + 0xc0, 0x2f, 0x6d, 0x29, \ + 0xa8, 0x33, 0x89, 0x08, \ + 0x60, 0x8c, 0x08, 0x34, \ + 0x64, 0x20, 0x06, 0x60, \ + 0xa0, 0x53, 0xc0, 0x70, \ + 0xc6, 0x57, 0x05, 0x45, \ + 0x18, 0x44, 0x02, 0x81, \ + 0x53, 0x8d, 0xfa, 0x80, \ + 0xf0, 0x94, 0x54, 0x12, \ + 0x79, 0x00, 0x46, 0xa1, \ + 0xf0, 0xb0, 0x56, 0x22, \ + 0x82, 0x25, 0x8c, 0xb6, \ + 0x6d, 0x41, 0x0c, 0xc9, \ + 0x0a, 0x54, 0x40, 0x2f, \ + 0xc2, 0xc7, 0xfa, 0x80, \ + 0x98, 0x53, 0x01, 0xd5, \ + 0x6d, 0x45, 0x0c, 0x49, \ + 0x81, 0x81, 0x40, 0x2f, \ + 0x6d, 0x29, 0x0c, 0x59, \ + 0xc2, 0x47, 0xc0, 0x2f, \ + 0x98, 0x43, 0x20, 0x53, \ + 0x0e, 0x70, 0xc6, 0x57, \ + 0x82, 0x31, 0x6c, 0x04, \ + 0xf6, 0xb5, 0xa1, 0x7b, \ + 0x0c, 0x0c, 0x98, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0x6d, 0x41, 0x00, 0x39, \ + 0x82, 0x4b, 0x40, 0x2f, \ + 0x6d, 0x39, 0x00, 0x29, \ + 0x00, 0x19, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6d, 0x31, \ + 0xc1, 0xc3, 0x85, 0xc5, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x81, 0x01, 0x00, 0x7b, \ + 0x82, 0x6b, 0x7c, 0x0e, \ + 0x02, 0xf0, 0x3e, 0x0e, \ + 0x7e, 0x98, 0xe4, 0x20, \ + 0x8e, 0x0b, 0x6f, 0xfc, \ + 0x64, 0x10, 0x02, 0xf0, \ + 0x40, 0xd8, 0x8e, 0x0b, \ + 0xfc, 0x04, 0xe8, 0x00, \ + 0x98, 0xf1, 0x3e, 0x14, \ + 0xe6, 0x82, 0x40, 0x1a, \ + 0xef, 0xfc, 0x00, 0x02, \ + 0x0c, 0x0c, 0x98, 0x0e, \ + 0xdd, 0x93, 0xf2, 0x04, \ + 0x6d, 0x45, 0x00, 0x39, \ + 0x82, 0x4b, 0x40, 0x2f, \ + 0x6d, 0x3d, 0x00, 0x29, \ + 0x00, 0x19, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6d, 0x35, \ + 0xc1, 0xc3, 0x81, 0x25, \ + 0x0a, 0xc6, 0x21, 0x3b, \ + 0x40, 0x51, 0xe0, 0x0c, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x00, 0x55, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0x44, 0x0a, 0x0a, \ + 0x00, 0x00, 0x82, 0x55, \ + 0x40, 0x54, 0x66, 0x51, \ + 0x1f, 0x21, 0x81, 0x11, \ + 0xe0, 0x0c, 0x06, 0xc6, \ + 0xff, 0xf4, 0x3b, 0xe8, \ + 0xe5, 0x0a, 0x40, 0x1a, \ + 0xe0, 0x0e, 0x40, 0x0a, \ + 0xef, 0xfc, 0x00, 0x02, \ + 0xdd, 0x93, 0x90, 0x0e, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0x74, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x80, 0x03, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x40, 0x46, 0xc1, 0x83, \ + 0x87, 0x11, 0x61, 0x48, \ + 0x00, 0x16, 0x80, 0x04, \ + 0xe1, 0x48, 0x40, 0x06, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xd0, 0x19, \ + 0xef, 0xe0, 0x57, 0xa8, \ + 0x6f, 0x94, 0x5f, 0xc8, \ + 0xc1, 0xc3, 0x81, 0x01, \ + 0xfa, 0x00, 0x00, 0x4c, \ + 0x61, 0x48, 0x40, 0x46, \ + 0x80, 0x04, 0x89, 0x11, \ + 0x00, 0x06, 0x00, 0x16, \ + 0xc0, 0x20, 0x60, 0x11, \ + 0x42, 0x06, 0x00, 0x04, \ + 0x84, 0x04, 0xe1, 0x48, \ + 0xd0, 0x19, 0x80, 0x18, \ + 0x56, 0x38, 0x80, 0x16, \ + 0x5e, 0x58, 0xef, 0xe0, \ + 0x81, 0x01, 0x6f, 0x94, \ + 0xfa, 0x00, 0x00, 0x4c, \ + 0x61, 0x48, 0x40, 0x46, \ + 0x80, 0x04, 0x8b, 0x11, \ + 0x00, 0x06, 0x00, 0x16, \ + 0xc0, 0x20, 0x60, 0x11, \ + 0x42, 0x06, 0x00, 0x04, \ + 0x84, 0x04, 0xe1, 0x48, \ + 0xd0, 0x19, 0x80, 0x18, \ + 0x54, 0xd8, 0x80, 0x16, \ + 0x5c, 0xf8, 0xef, 0xe0, \ + 0x81, 0x01, 0x6f, 0x94, \ + 0xfa, 0x00, 0x00, 0x4c, \ + 0x61, 0x48, 0x40, 0x46, \ + 0x80, 0x04, 0x91, 0x11, \ + 0x00, 0x06, 0x00, 0x16, \ + 0xc0, 0x20, 0x60, 0x11, \ + 0x42, 0x06, 0x00, 0x04, \ + 0x84, 0x04, 0xe1, 0x48, \ + 0xd0, 0x19, 0x80, 0x18, \ + 0x53, 0x78, 0x80, 0x16, \ + 0x5b, 0x98, 0xef, 0xe0, \ + 0x81, 0x01, 0x6f, 0x94, \ + 0xfa, 0x00, 0x00, 0x4c, \ + 0x61, 0x48, 0x40, 0x46, \ + 0x80, 0x04, 0x95, 0x11, \ + 0x00, 0x06, 0x00, 0x16, \ + 0xc0, 0x20, 0x60, 0x11, \ + 0x42, 0x06, 0x00, 0x04, \ + 0x84, 0x04, 0xe1, 0x48, \ + 0xd0, 0x19, 0x80, 0x18, \ + 0x52, 0x18, 0x80, 0x16, \ + 0x5a, 0x38, 0xef, 0xe0, \ + 0x81, 0x01, 0x6f, 0x94, \ + 0xfa, 0x00, 0x00, 0x4c, \ + 0x61, 0x48, 0x40, 0x46, \ + 0x80, 0x04, 0x97, 0x11, \ + 0x00, 0x06, 0x00, 0x16, \ + 0xc0, 0x20, 0x60, 0x11, \ + 0x42, 0x06, 0x00, 0x04, \ + 0x84, 0x04, 0xe1, 0x48, \ + 0xd0, 0x19, 0x80, 0x18, \ + 0x50, 0xb8, 0x80, 0x16, \ + 0x58, 0xd8, 0xef, 0xe0, \ + 0x81, 0x01, 0x6f, 0x94, \ + 0xfa, 0x00, 0x00, 0x4c, \ + 0x61, 0x48, 0x40, 0x46, \ + 0x80, 0x04, 0x9f, 0x11, \ + 0x00, 0x06, 0x00, 0x16, \ + 0xc0, 0x20, 0x60, 0x11, \ + 0x42, 0x06, 0x00, 0x04, \ + 0x84, 0x04, 0xe1, 0x48, \ + 0xd0, 0x19, 0x80, 0x18, \ + 0x4f, 0x58, 0x80, 0x16, \ + 0x57, 0x78, 0xef, 0xe0, \ + 0x81, 0x01, 0x6f, 0x94, \ + 0xfa, 0x00, 0x00, 0x4c, \ + 0xe2, 0xc0, 0x00, 0x2c, \ + 0x61, 0x48, 0x40, 0x46, \ + 0x80, 0x04, 0xa1, 0x11, \ + 0x40, 0x06, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0xd0, 0x19, 0x80, 0x18, \ + 0x4e, 0x18, 0x80, 0x16, \ + 0x56, 0x38, 0xef, 0xe0, \ + 0x81, 0x01, 0x6f, 0x94, \ + 0xfe, 0x1c, 0x04, 0xcc, \ + 0x6f, 0xf8, 0x67, 0x68, \ + 0x00, 0x7b, 0x80, 0x6b, \ + 0xe1, 0x94, 0x00, 0x06, \ + 0xe0, 0x00, 0x0e, 0x2a, \ + 0x40, 0x01, 0x01, 0x08, \ + 0x01, 0x11, 0x80, 0x00, \ + 0x65, 0x09, 0x10, 0x60, \ + 0x00, 0x1e, 0xc0, 0xbf, \ + 0x4c, 0x15, 0x85, 0x01, \ + 0xc0, 0x0c, 0x65, 0x15, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x81, 0xb5, 0xc0, 0x2f, \ + 0x10, 0x60, 0x00, 0x04, \ + 0xc0, 0xbf, 0x65, 0x09, \ + 0x83, 0x01, 0x23, 0x0b, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x80, 0xa5, 0xc0, 0x2f, \ + 0x61, 0x94, 0x00, 0x06, \ + 0x80, 0x1e, 0x81, 0x11, \ + 0x65, 0x15, 0x0c, 0x05, \ + 0x40, 0x0a, 0xc0, 0x0c, \ + 0x00, 0x16, 0xe0, 0x80, \ + 0x61, 0xb8, 0xc0, 0x00, \ + 0x03, 0x08, 0xcf, 0xf8, \ + 0x80, 0x00, 0x40, 0x01, \ + 0xef, 0xec, 0x5b, 0x28, \ + 0x82, 0x65, 0xa1, 0x0b, \ + 0xa0, 0x06, 0x87, 0x01, \ + 0xef, 0xf8, 0x64, 0x88, \ + 0xef, 0xf8, 0x65, 0x48, \ + 0xef, 0xe0, 0x49, 0xe8, \ + 0xef, 0xec, 0x5a, 0xc8, \ + 0x44, 0x06, 0x01, 0x11, \ + 0x67, 0x58, 0xec, 0x7c, \ + 0x8c, 0x0b, 0x6f, 0x94, \ + 0x60, 0x01, 0x00, 0x06, \ + 0x43, 0x56, 0x40, 0x7d, \ + 0x4c, 0x05, 0xe0, 0x40, \ + 0x40, 0x0b, 0x6d, 0x71, \ + 0x4c, 0x01, 0x83, 0x01, \ + 0x40, 0x30, 0x6f, 0x01, \ + 0xe3, 0x48, 0x01, 0xb6, \ + 0x67, 0x71, 0x4c, 0x13, \ + 0x42, 0x16, 0x40, 0x18, \ + 0x4c, 0x03, 0xe0, 0x00, \ + 0xc0, 0x18, 0x67, 0x75, \ + 0x6e, 0x79, 0x4c, 0x13, \ + 0x53, 0x78, 0xc0, 0x17, \ + 0x18, 0xf0, 0xe8, 0x30, \ + 0x8c, 0x0b, 0x64, 0x60, \ + 0xe8, 0x0c, 0x79, 0xc8, \ + 0xa1, 0x9b, 0x8f, 0x94, \ + 0x0c, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0x48, 0x00, 0x0a, \ + 0x61, 0xb8, 0x82, 0xb5, \ + 0x8c, 0x0b, 0x68, 0x08, \ + 0x00, 0x60, 0x20, 0x38, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0x85, 0x21, 0x03, 0x11, \ + 0xef, 0xf4, 0x68, 0x08, \ + 0x46, 0x58, 0x81, 0x75, \ + 0x8c, 0x0b, 0x68, 0x0c, \ + 0x68, 0x10, 0x6a, 0x48, \ + 0x5e, 0xd8, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0x6f, 0xf8, \ + 0xe8, 0x00, 0x44, 0xe8, \ + 0x40, 0x01, 0x02, 0x38, \ + 0x7b, 0xb8, 0x80, 0x00, \ + 0x8c, 0x0b, 0x6f, 0xf4, \ + 0x6f, 0xf4, 0x72, 0x08, \ + 0x66, 0x98, 0x8c, 0x0b, \ + 0x41, 0x06, 0xef, 0xe0, \ + 0x43, 0x06, 0x61, 0x48, \ + 0x00, 0x04, 0xe0, 0x00, \ + 0x00, 0x16, 0xa3, 0x9b, \ + 0xe1, 0x48, 0x40, 0xc6, \ + 0x0c, 0x19, 0x80, 0x04, \ + 0xc0, 0x15, 0x62, 0x21, \ + 0xc4, 0x17, 0x80, 0x28, \ + 0x44, 0x20, 0x88, 0x19, \ + 0xa2, 0x2f, 0xf3, 0xec, \ + 0x40, 0x60, 0x00, 0x26, \ + 0x41, 0xb8, 0x9c, 0x00, \ + 0x41, 0x38, 0x88, 0x0c, \ + 0x8c, 0x0b, 0x68, 0x30, \ + 0x8e, 0x1b, 0x0c, 0x0b, \ + 0x6f, 0xec, 0x54, 0x28, \ + 0x01, 0x11, 0x8e, 0x2b, \ + 0x03, 0x31, 0x9f, 0x21, \ + 0x7c, 0x78, 0x8c, 0x0b, \ + 0x7a, 0xb8, 0xe8, 0x28, \ + 0x8c, 0x0b, 0x68, 0x00, \ + 0xef, 0xf8, 0x5c, 0x68, \ + 0xef, 0xf8, 0x5d, 0x28, \ + 0xef, 0xe0, 0x41, 0xc8, \ + 0x83, 0x11, 0x01, 0x01, \ + 0x6f, 0xe0, 0x63, 0xe8, \ + 0x0c, 0x09, 0x8c, 0x2b, \ + 0xc0, 0x31, 0x64, 0x51, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0xc0, 0x00, 0x00, 0x26, \ + 0xcf, 0xf8, 0x57, 0xc8, \ + 0xf6, 0x1c, 0x0c, 0xcc, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x61, 0x1c, 0x41, 0xc6, \ + 0x80, 0x04, 0x81, 0x11, \ + 0x00, 0x06, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x00, 0x46, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x00, 0x86, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x00, 0xc6, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x01, 0x06, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x01, 0x46, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x01, 0x86, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x01, 0xc6, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x40, 0x06, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x40, 0x46, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x40, 0x86, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x40, 0xc6, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x41, 0x06, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x41, 0x46, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0x41, 0x86, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x20, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0x68, 0x05, 0x00, 0x1b, \ + 0x00, 0x2b, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x68, 0x01, \ + 0x68, 0x15, 0x00, 0x3b, \ + 0x00, 0x4b, 0xc0, 0x0a, \ + 0x40, 0x0a, 0x68, 0x09, \ + 0x00, 0x2b, 0xe0, 0x17, \ + 0x40, 0x0a, 0x68, 0x0d, \ + 0x00, 0x5b, 0xa4, 0x1f, \ + 0x40, 0x0a, 0x68, 0x11, \ + 0x80, 0x1e, 0xe0, 0x37, \ + 0x28, 0x2f, 0xe0, 0x27, \ + 0x82, 0x2e, 0xaa, 0x3f, \ + 0xdd, 0x93, 0xc0, 0x3e, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0x74, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x80, 0x03, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x00, 0xc6, 0xc1, 0x83, \ + 0x02, 0x86, 0x61, 0x4c, \ + 0x00, 0x04, 0xe1, 0x38, \ + 0x04, 0x24, 0xa1, 0x31, \ + 0x40, 0x31, 0x83, 0x41, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x81, 0x21, 0x08, 0x46, \ + 0x84, 0x26, 0x84, 0x14, \ + 0xe8, 0x10, 0x7a, 0x68, \ + 0x1f, 0x0c, 0x98, 0xf1, \ + 0x0b, 0xfc, 0xf0, 0x04, \ + 0xbc, 0xf4, 0xf6, 0x80, \ + 0xc1, 0x53, 0xfd, 0x64, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0x74, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x80, 0x07, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x0c, 0xc6, 0xc1, 0x83, \ + 0x01, 0x06, 0x61, 0x4c, \ + 0x18, 0x14, 0xe1, 0x38, \ + 0x00, 0x24, 0xa3, 0x31, \ + 0x42, 0x31, 0x83, 0x41, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x88, 0x46, 0x81, 0x11, \ + 0x00, 0x16, 0x80, 0x04, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x6f, 0x98, 0x57, 0x48, \ + 0x41, 0x86, 0x81, 0x11, \ + 0x80, 0x04, 0xe1, 0x00, \ + 0x40, 0x4a, 0x80, 0x08, \ + 0x0c, 0x05, 0xe0, 0x80, \ + 0x59, 0xb8, 0xc0, 0x00, \ + 0x41, 0xf6, 0xc8, 0x10, \ + 0x83, 0x11, 0x60, 0x3c, \ + 0xef, 0x98, 0x56, 0x48, \ + 0x1f, 0x0c, 0x90, 0xf1, \ + 0x0f, 0xfc, 0xf0, 0x04, \ + 0xbc, 0xf4, 0xf6, 0x80, \ + 0xc1, 0x53, 0xfd, 0x64, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0x74, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x8c, 0x07, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x0c, 0xc6, 0xc1, 0x83, \ + 0x11, 0xc6, 0x61, 0x4c, \ + 0x18, 0x14, 0xe1, 0x38, \ + 0xa0, 0x04, 0xa5, 0x21, \ + 0x6d, 0x09, 0x02, 0x79, \ + 0x42, 0x21, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x42, 0x0a, 0x80, 0x18, \ + 0x84, 0x05, 0xe0, 0x80, \ + 0xe1, 0x38, 0x01, 0x86, \ + 0x84, 0x28, 0x80, 0x14, \ + 0x84, 0x26, 0xc2, 0x29, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xf8, 0x42, 0x10, \ + 0x98, 0x04, 0x80, 0x16, \ + 0xe8, 0x2c, 0x76, 0x28, \ + 0x80, 0x08, 0xa0, 0x04, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x01, 0x86, 0x82, 0x85, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0xc4, 0x29, 0x84, 0x28, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xf4, \ + 0xa0, 0xf1, 0x18, 0x04, \ + 0x6d, 0x09, 0x40, 0x71, \ + 0x1f, 0x0c, 0xc0, 0x2f, \ + 0x0f, 0xfc, 0xf0, 0x04, \ + 0xbc, 0xf4, 0xf6, 0x8c, \ + 0xc1, 0x53, 0xfd, 0x64, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0x74, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x80, 0x07, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x0c, 0xc6, 0xc1, 0x83, \ + 0x40, 0xc6, 0x61, 0x4c, \ + 0x18, 0x24, 0xe1, 0x38, \ + 0x80, 0x14, 0xa7, 0x31, \ + 0x6d, 0x09, 0x44, 0x31, \ + 0x84, 0x28, 0xc0, 0x2f, \ + 0xe0, 0x80, 0x44, 0x0a, \ + 0x42, 0x86, 0x82, 0xa5, \ + 0x84, 0x24, 0xe1, 0x38, \ + 0xc2, 0x39, 0x88, 0x38, \ + 0x84, 0x14, 0x88, 0x36, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x84, 0x26, 0xf3, 0xf8, \ + 0x84, 0x28, 0x80, 0x14, \ + 0xe0, 0x84, 0x44, 0x0a, \ + 0x42, 0x86, 0x82, 0xa5, \ + 0x84, 0x24, 0xe1, 0x38, \ + 0xc4, 0x39, 0x88, 0x38, \ + 0x84, 0x14, 0x88, 0x36, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x84, 0x26, 0xf3, 0xf4, \ + 0x84, 0x08, 0x80, 0x14, \ + 0xe0, 0x88, 0x40, 0x0a, \ + 0x90, 0xf1, 0x82, 0x35, \ + 0xf0, 0x04, 0x1f, 0x0c, \ + 0xf6, 0x80, 0x0f, 0xfc, \ + 0xfd, 0x64, 0xbc, 0xf4, \ + 0x40, 0x86, 0xc1, 0x53, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0xc8, 0x29, 0x84, 0x28, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x00, 0x16, 0xf3, 0xec, \ + 0xe1, 0x38, 0x40, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xec, 0x42, 0x10, \ + 0x98, 0x04, 0x80, 0x16, \ + 0xe8, 0x2c, 0x77, 0xc8, \ + 0x00, 0x19, 0x98, 0x04, \ + 0xc0, 0x2f, 0x6d, 0x01, \ + 0x40, 0x11, 0xd0, 0x19, \ + 0x40, 0x2f, 0x6d, 0x01, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x6f, 0x98, 0x4a, 0x88, \ + 0x41, 0x86, 0x81, 0x11, \ + 0x80, 0x04, 0xe1, 0x00, \ + 0x40, 0x4a, 0x80, 0x08, \ + 0x0c, 0x05, 0xe0, 0x80, \ + 0x4c, 0xf8, 0xc0, 0x00, \ + 0x41, 0xf6, 0xc8, 0x10, \ + 0x83, 0x11, 0x60, 0x3c, \ + 0xef, 0x98, 0x49, 0x88, \ + 0x1f, 0x0c, 0x90, 0xf1, \ + 0x0f, 0xfc, 0xf0, 0x04, \ + 0xbc, 0xf4, 0xf6, 0x80, \ + 0xc1, 0x53, 0xfd, 0x64, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0x74, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x84, 0x07, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x0c, 0xc6, 0xc1, 0x83, \ + 0x4f, 0x86, 0x61, 0x4c, \ + 0x18, 0x14, 0xe1, 0x38, \ + 0x9c, 0x04, 0xa9, 0x21, \ + 0x6d, 0x09, 0x42, 0x21, \ + 0x80, 0x18, 0xc0, 0x2f, \ + 0xe0, 0x80, 0x42, 0x0a, \ + 0x41, 0x46, 0x84, 0x25, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0xc2, 0x29, 0x84, 0x28, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x00, 0x16, 0xf3, 0xf8, \ + 0x98, 0x04, 0x8f, 0x11, \ + 0xe8, 0x04, 0x42, 0x68, \ + 0x80, 0x18, 0x9c, 0x04, \ + 0xe0, 0x84, 0x42, 0x0a, \ + 0x41, 0x46, 0x84, 0x25, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0xc4, 0x29, 0x84, 0x28, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x00, 0x16, 0xf3, 0xf4, \ + 0x98, 0x04, 0x91, 0x11, \ + 0xe8, 0x04, 0x41, 0x28, \ + 0x80, 0x18, 0x9c, 0x04, \ + 0xe0, 0x88, 0x42, 0x0a, \ + 0x41, 0x46, 0x84, 0x25, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0xc8, 0x29, 0x84, 0x28, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x00, 0x16, 0xf3, 0xec, \ + 0x98, 0x04, 0x93, 0x11, \ + 0xe8, 0x00, 0x7f, 0xe8, \ + 0x80, 0x18, 0x9c, 0x04, \ + 0xe0, 0x8c, 0x42, 0x0a, \ + 0x41, 0x46, 0x84, 0x25, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0xd0, 0x29, 0x84, 0x28, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x00, 0x16, 0xf3, 0xdc, \ + 0x98, 0x04, 0x95, 0x11, \ + 0xe8, 0x00, 0x7e, 0xa8, \ + 0x80, 0x18, 0x9c, 0x04, \ + 0xe0, 0x90, 0x42, 0x0a, \ + 0x41, 0x46, 0x84, 0x25, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0xe0, 0x29, 0x84, 0x28, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x00, 0x16, 0xf3, 0xbc, \ + 0x98, 0x04, 0x97, 0x11, \ + 0xe8, 0x00, 0x7d, 0x68, \ + 0x80, 0x18, 0x9c, 0x04, \ + 0xe0, 0x94, 0x42, 0x0a, \ + 0x41, 0x46, 0x84, 0x45, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0x04, 0x24, 0x84, 0x28, \ + 0x84, 0x26, 0xfc, 0x94, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0x7c, 0x42, 0x10, \ + 0xab, 0x11, 0x00, 0x16, \ + 0x7c, 0x18, 0x98, 0x04, \ + 0x9c, 0x04, 0xe8, 0x00, \ + 0x42, 0x0a, 0x80, 0x18, \ + 0x84, 0x45, 0xe0, 0x98, \ + 0xe1, 0x38, 0x41, 0x46, \ + 0x84, 0x28, 0x80, 0x14, \ + 0xfc, 0x98, 0x04, 0x24, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x00, 0x16, 0xf2, 0xfc, \ + 0x98, 0x04, 0x99, 0x11, \ + 0xe8, 0x00, 0x7a, 0xc8, \ + 0x00, 0x00, 0x9c, 0x04, \ + 0x82, 0x0d, 0xff, 0x11, \ + 0xa8, 0xf1, 0x82, 0x25, \ + 0xf0, 0x04, 0x1f, 0x0c, \ + 0xf6, 0x84, 0x0f, 0xfc, \ + 0xfd, 0x64, 0xbc, 0xf4, \ + 0x41, 0x46, 0xc1, 0x53, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0x04, 0x24, 0x84, 0x28, \ + 0x84, 0x26, 0xfc, 0x9c, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf1, 0xfc, 0x42, 0x10, \ + 0x98, 0x04, 0x80, 0x16, \ + 0xe8, 0x00, 0x79, 0xc8, \ + 0x1f, 0x0c, 0xa8, 0xf1, \ + 0x0f, 0xfc, 0xf0, 0x04, \ + 0xbc, 0xf4, 0xf6, 0x84, \ + 0xc1, 0x53, 0xfd, 0x64, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0x74, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x80, 0x07, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x0c, 0xc6, 0xc1, 0x83, \ + 0x00, 0xc6, 0x61, 0x4c, \ + 0x18, 0x14, 0xe1, 0x3c, \ + 0x00, 0x24, 0xab, 0x31, \ + 0x42, 0x31, 0x83, 0x41, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x88, 0x46, 0x81, 0x11, \ + 0x00, 0x16, 0x80, 0x04, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x6f, 0x94, 0x7a, 0x08, \ + 0x41, 0x86, 0x81, 0x11, \ + 0x80, 0x04, 0xe1, 0x00, \ + 0x40, 0x4a, 0x80, 0x08, \ + 0x0c, 0x05, 0xe0, 0x80, \ + 0x7c, 0x78, 0xc0, 0x00, \ + 0x41, 0xf6, 0xc8, 0x0c, \ + 0x83, 0x11, 0x60, 0x3c, \ + 0xef, 0x94, 0x79, 0x08, \ + 0x7c, 0xd8, 0x98, 0x04, \ + 0x90, 0xf1, 0xef, 0xdc, \ + 0xf0, 0x04, 0x1f, 0x0c, \ + 0xf6, 0x80, 0x0f, 0xfc, \ + 0xfd, 0x64, 0xbc, 0xf4, \ + 0xc1, 0xc3, 0xc1, 0x53, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0x74, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x80, 0x07, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x00, 0xc6, 0xc1, 0x83, \ + 0x83, 0x61, 0x61, 0x4c, \ + 0xad, 0x01, 0x00, 0x14, \ + 0x6d, 0x09, 0x42, 0x01, \ + 0x83, 0x01, 0x40, 0x2f, \ + 0xef, 0xe8, 0x63, 0xa8, \ + 0x61, 0x3c, 0x40, 0x46, \ + 0x00, 0x04, 0x81, 0x11, \ + 0x80, 0x66, 0x90, 0xf1, \ + 0x1f, 0x0c, 0x80, 0x16, \ + 0x0f, 0xfc, 0xf0, 0x04, \ + 0xbc, 0xf4, 0xf6, 0x80, \ + 0xc1, 0x53, 0xfd, 0x64, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0x74, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfe, 0x84, 0x07, 0xfc, \ + 0xf8, 0x04, 0x17, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x81, 0x93, 0x81, 0x83, \ + 0x0e, 0xc6, 0xc1, 0x83, \ + 0xaf, 0x01, 0x61, 0x4c, \ + 0x81, 0x61, 0x1c, 0x14, \ + 0x6d, 0x09, 0x42, 0x01, \ + 0x81, 0x01, 0x40, 0x2f, \ + 0xef, 0xe8, 0x61, 0x08, \ + 0x00, 0x19, 0x9c, 0x04, \ + 0xc0, 0x31, 0x63, 0xe9, \ + 0xa5, 0x1b, 0xc4, 0x19, \ + 0x03, 0x91, 0x00, 0x19, \ + 0x42, 0x10, 0x80, 0x31, \ + 0x40, 0x11, 0x93, 0xec, \ + 0x80, 0x31, 0x03, 0x91, \ + 0x6d, 0x01, 0x00, 0x19, \ + 0x42, 0x0a, 0xc0, 0x2f, \ + 0x88, 0x25, 0xe0, 0x94, \ + 0x60, 0x3c, 0x41, 0xf6, \ + 0x72, 0x58, 0x81, 0x11, \ + 0x41, 0x86, 0xef, 0x94, \ + 0x80, 0x04, 0xe1, 0x00, \ + 0x40, 0x4a, 0x80, 0x08, \ + 0x0e, 0x05, 0xe0, 0x80, \ + 0x74, 0xd8, 0xc0, 0x00, \ + 0x41, 0xf6, 0xc8, 0x0c, \ + 0x83, 0x11, 0x60, 0x3c, \ + 0xef, 0x94, 0x71, 0x68, \ + 0x5b, 0x38, 0x9c, 0x04, \ + 0x9c, 0x04, 0xef, 0xe8, \ + 0x6d, 0x01, 0x00, 0x19, \ + 0x42, 0x10, 0xc0, 0x2f, \ + 0x40, 0x11, 0xf3, 0x7c, \ + 0xc0, 0x2f, 0x6d, 0x01, \ + 0x68, 0x6d, 0x00, 0x19, \ + 0x45, 0xc6, 0x40, 0x31, \ + 0x08, 0x34, 0xe1, 0x3c, \ + 0xa8, 0xf1, 0x83, 0x41, \ + 0x40, 0x11, 0xc8, 0x19, \ + 0xc0, 0x31, 0x68, 0x6d, \ + 0x88, 0x04, 0x8c, 0x46, \ + 0x1f, 0x0c, 0x80, 0x66, \ + 0x0f, 0xfc, 0xf0, 0x04, \ + 0xbc, 0xf4, 0xf6, 0x84, \ + 0xc1, 0x53, 0xfd, 0x64, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0x74, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xf8, 0x00, 0x01, 0xfc, \ + 0xec, 0x10, 0x1e, 0xf0, \ + 0x61, 0x4c, 0x00, 0xc6, \ + 0xe1, 0x40, 0x03, 0x46, \ + 0xb1, 0x31, 0x00, 0x04, \ + 0x83, 0x41, 0x04, 0x24, \ + 0x40, 0x31, 0x88, 0xf1, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x04, 0x04, 0x88, 0x46, \ + 0x80, 0x16, 0x81, 0x11, \ + 0xf0, 0x00, 0x09, 0xfc, \ + 0xfd, 0x64, 0xbc, 0xf4, \ + 0xc1, 0xc3, 0xc1, 0x53, \ + 0xfa, 0x04, 0x5e, 0x65, \ + 0xec, 0x10, 0x0c, 0xf0, \ + 0x74, 0x1c, 0x5e, 0x60, \ + 0xbc, 0x6e, 0x88, 0x61, \ + 0xfc, 0x00, 0x00, 0x7c, \ + 0xec, 0x20, 0x1e, 0xf0, \ + 0x61, 0x4c, 0x00, 0xc6, \ + 0x80, 0x04, 0xb3, 0x11, \ + 0x6d, 0x09, 0x40, 0x11, \ + 0x81, 0x01, 0x40, 0x2f, \ + 0x43, 0x86, 0x3e, 0x0e, \ + 0x84, 0x14, 0xe1, 0x28, \ + 0x3e, 0x1e, 0x84, 0x14, \ + 0xe1, 0x34, 0x03, 0xc6, \ + 0x04, 0x28, 0x84, 0x14, \ + 0xe1, 0x94, 0x03, 0x06, \ + 0xa7, 0x2b, 0x04, 0x26, \ + 0xa3, 0x2b, 0x82, 0x45, \ + 0xa5, 0x2b, 0x82, 0x65, \ + 0xa1, 0x2b, 0x82, 0x85, \ + 0xc1, 0x83, 0x82, 0xb5, \ + 0x81, 0x95, 0xc1, 0x13, \ + 0xc7, 0x13, 0xc1, 0x83, \ + 0xc1, 0x83, 0x81, 0x65, \ + 0x81, 0x35, 0xc3, 0x13, \ + 0xc5, 0x13, 0xc1, 0x83, \ + 0x42, 0xc6, 0x84, 0x06, \ + 0x83, 0x21, 0x61, 0x40, \ + 0x90, 0xf1, 0x04, 0x14, \ + 0x84, 0x06, 0x84, 0x26, \ + 0xf4, 0x00, 0x08, 0x7c, \ + 0xfd, 0x64, 0xbc, 0xf4, \ + 0xc1, 0xc3, 0xc1, 0x53, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x68, 0x00, 0x5e, 0x68, \ + 0xa1, 0x0b, 0x80, 0x6b, \ + 0x0c, 0x39, 0xb2, 0x35, \ + 0x40, 0x31, 0x64, 0x69, \ + 0x6d, 0xd5, 0x00, 0x0e, \ + 0x0c, 0x19, 0xc0, 0x10, \ + 0xc0, 0x31, 0x64, 0x61, \ + 0x64, 0x65, 0x0c, 0x29, \ + 0x08, 0x34, 0xc0, 0x31, \ + 0x82, 0x39, 0x61, 0x84, \ + 0x6f, 0x94, 0x68, 0x08, \ + 0xa1, 0x0b, 0x82, 0x49, \ + 0x0c, 0x39, 0xac, 0xb5, \ + 0x40, 0x31, 0x64, 0x79, \ + 0x6d, 0xd9, 0x01, 0x4e, \ + 0x0c, 0x19, 0xc0, 0x10, \ + 0xc0, 0x31, 0x64, 0x71, \ + 0x64, 0x75, 0x0c, 0x29, \ + 0x08, 0x34, 0xc0, 0x31, \ + 0x82, 0x39, 0x61, 0x84, \ + 0x6f, 0x94, 0x66, 0xc8, \ + 0xa1, 0x0b, 0x82, 0x49, \ + 0x0c, 0x39, 0xa8, 0x35, \ + 0x40, 0x31, 0x64, 0x89, \ + 0x6d, 0xe1, 0x00, 0xce, \ + 0x0c, 0x19, 0xc0, 0x10, \ + 0xc0, 0x31, 0x64, 0x81, \ + 0x64, 0x85, 0x0c, 0x29, \ + 0x08, 0x34, 0xc0, 0x31, \ + 0x82, 0x39, 0x61, 0x84, \ + 0x6f, 0x94, 0x65, 0x88, \ + 0xa1, 0x0b, 0x82, 0x49, \ + 0x0c, 0x3b, 0xa2, 0xb5, \ + 0x40, 0x18, 0x6a, 0x49, \ + 0x6d, 0xe9, 0x40, 0x4e, \ + 0x0c, 0x19, 0xc0, 0x10, \ + 0xc0, 0x31, 0x64, 0x99, \ + 0x64, 0x9d, 0x0c, 0x29, \ + 0x08, 0x34, 0xc0, 0x31, \ + 0x82, 0x39, 0x61, 0x84, \ + 0x6f, 0x94, 0x64, 0x48, \ + 0xa1, 0x0b, 0x82, 0x49, \ + 0x0c, 0x39, 0x9e, 0x35, \ + 0x40, 0x31, 0x64, 0xb1, \ + 0x6d, 0xf9, 0x40, 0x4e, \ + 0x0c, 0x19, 0xc0, 0x10, \ + 0xc0, 0x31, 0x64, 0xa1, \ + 0x64, 0xa5, 0x0c, 0x29, \ + 0x08, 0x34, 0xc0, 0x31, \ + 0x82, 0x39, 0x61, 0x84, \ + 0x6f, 0x94, 0x63, 0x08, \ + 0xa1, 0x0b, 0x82, 0x49, \ + 0x0c, 0x39, 0x98, 0xb5, \ + 0x40, 0x31, 0x64, 0xe9, \ + 0x6e, 0x11, 0x41, 0xce, \ + 0x0c, 0x19, 0xc0, 0x10, \ + 0xc0, 0x31, 0x64, 0xe1, \ + 0x64, 0xe5, 0x0c, 0x29, \ + 0x08, 0x34, 0xc0, 0x31, \ + 0x82, 0x39, 0x61, 0x84, \ + 0x6f, 0x94, 0x61, 0xc8, \ + 0xa1, 0x0b, 0x82, 0x49, \ + 0x0c, 0x39, 0x94, 0x35, \ + 0x40, 0x31, 0x65, 0x19, \ + 0x6e, 0x19, 0x41, 0xce, \ + 0x0c, 0x19, 0xc0, 0x10, \ + 0xc0, 0x31, 0x65, 0x11, \ + 0x65, 0x15, 0x0c, 0x29, \ + 0x08, 0x34, 0xc0, 0x31, \ + 0x82, 0x39, 0x61, 0x84, \ + 0x6f, 0x94, 0x60, 0x88, \ + 0xa1, 0x0b, 0x82, 0x49, \ + 0x0c, 0x39, 0x8e, 0xb5, \ + 0x40, 0x31, 0x65, 0x49, \ + 0x6e, 0x21, 0x01, 0x0e, \ + 0x0c, 0x19, 0xc0, 0x10, \ + 0xc0, 0x31, 0x65, 0x41, \ + 0x65, 0x45, 0x0c, 0x29, \ + 0x08, 0x34, 0xc0, 0x31, \ + 0x82, 0x39, 0x61, 0x84, \ + 0x6f, 0x94, 0x5f, 0x48, \ + 0xa1, 0x0b, 0x82, 0x49, \ + 0x0c, 0x39, 0x8a, 0x35, \ + 0x40, 0x31, 0x65, 0x79, \ + 0x6e, 0x2d, 0x01, 0xce, \ + 0x0c, 0x19, 0xc0, 0x10, \ + 0xc0, 0x31, 0x65, 0x71, \ + 0x65, 0x75, 0x0c, 0x29, \ + 0x08, 0x34, 0xc0, 0x31, \ + 0x82, 0x39, 0x61, 0x84, \ + 0x6f, 0x94, 0x5e, 0x08, \ + 0xa1, 0x0b, 0x82, 0x49, \ + 0x0c, 0x39, 0x84, 0xb5, \ + 0x40, 0x31, 0x65, 0xa9, \ + 0x6e, 0x31, 0x40, 0x0e, \ + 0x0c, 0x19, 0xc0, 0x10, \ + 0xc0, 0x31, 0x65, 0xa1, \ + 0x65, 0xa5, 0x0c, 0x29, \ + 0x08, 0x34, 0xc0, 0x31, \ + 0x82, 0x39, 0x61, 0x84, \ + 0x6f, 0x94, 0x5c, 0xc8, \ + 0xa1, 0x0b, 0x82, 0x49, \ + 0x0c, 0x39, 0x84, 0x25, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x66, 0x51, 0x0c, 0x60, \ + 0x01, 0x11, 0xc0, 0x54, \ + 0x5b, 0x18, 0x83, 0x21, \ + 0x8c, 0x0b, 0x6f, 0xf0, \ + 0x0c, 0x0c, 0x8c, 0x0b, \ + 0x1b, 0xd8, 0xf2, 0x0c, \ + 0x01, 0x01, 0xff, 0xf0, \ + 0x62, 0x38, 0x81, 0x71, \ + 0x03, 0x81, 0xef, 0x94, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0x6f, 0x94, 0x5a, 0x88, \ + 0x0c, 0x09, 0x83, 0x11, \ + 0x40, 0x31, 0x64, 0x69, \ + 0xe1, 0x38, 0x02, 0x46, \ + 0xc7, 0x07, 0x84, 0x14, \ + 0x04, 0x06, 0x82, 0x09, \ + 0xe1, 0x38, 0x02, 0xc6, \ + 0x64, 0x79, 0x0c, 0x09, \ + 0x84, 0x14, 0xc0, 0x31, \ + 0x82, 0x09, 0xc7, 0x07, \ + 0x01, 0x46, 0x04, 0x06, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0x64, 0x89, 0x0c, 0x29, \ + 0x84, 0x38, 0xc0, 0x31, \ + 0x82, 0x29, 0xc9, 0x27, \ + 0xf3, 0xf8, 0x46, 0x30, \ + 0x84, 0x36, 0xa4, 0x3f, \ + 0x0c, 0x19, 0x80, 0x04, \ + 0xc0, 0x31, 0x64, 0x89, \ + 0xc9, 0x17, 0x80, 0x28, \ + 0x44, 0x20, 0x84, 0x19, \ + 0xa2, 0x2f, 0xf3, 0xf4, \ + 0x40, 0x46, 0x00, 0x26, \ + 0x80, 0x14, 0xe1, 0x38, \ + 0x6a, 0x49, 0x0c, 0x2b, \ + 0x84, 0x38, 0xc0, 0x18, \ + 0x82, 0x29, 0xcb, 0x27, \ + 0xf3, 0xf8, 0x46, 0x30, \ + 0x84, 0x36, 0xa4, 0x3f, \ + 0x0c, 0x2b, 0x80, 0x14, \ + 0xc0, 0x18, 0x6a, 0x49, \ + 0xcb, 0x27, 0x84, 0x38, \ + 0x46, 0x30, 0x84, 0x29, \ + 0xa4, 0x3f, 0xf3, 0xf4, \ + 0x80, 0x04, 0x84, 0x36, \ + 0x6a, 0x49, 0x0c, 0x1b, \ + 0x80, 0x28, 0xc0, 0x18, \ + 0x88, 0x19, 0xcb, 0x17, \ + 0xf3, 0xec, 0x44, 0x20, \ + 0x00, 0x26, 0xa2, 0x2f, \ + 0xe1, 0x38, 0x41, 0x06, \ + 0x64, 0xa9, 0x0c, 0x19, \ + 0x80, 0x04, 0xc0, 0x31, \ + 0x41, 0xc6, 0x00, 0x16, \ + 0x0c, 0x15, 0xe1, 0x38, \ + 0xc0, 0x0c, 0x65, 0x31, \ + 0x00, 0x1e, 0x80, 0x04, \ + 0xe1, 0x3c, 0x00, 0x06, \ + 0x65, 0x35, 0x0c, 0x15, \ + 0x80, 0x04, 0xc0, 0x0c, \ + 0x02, 0x46, 0x00, 0x1e, \ + 0x0c, 0x09, 0xe1, 0x3c, \ + 0xc0, 0x31, 0x64, 0xe9, \ + 0xc5, 0x07, 0x84, 0x14, \ + 0x04, 0x06, 0x82, 0x09, \ + 0xe1, 0x3c, 0x02, 0x86, \ + 0x64, 0xe9, 0x0c, 0x09, \ + 0x84, 0x14, 0xc0, 0x31, \ + 0x82, 0x09, 0xc7, 0x07, \ + 0x01, 0x46, 0x04, 0x06, \ + 0x0c, 0x15, 0xe1, 0x3c, \ + 0xc0, 0x0c, 0x65, 0x3d, \ + 0x00, 0x1e, 0x80, 0x04, \ + 0xe1, 0x3c, 0x01, 0x86, \ + 0x65, 0x41, 0x0c, 0x15, \ + 0x80, 0x04, 0xc0, 0x0c, \ + 0x03, 0xc6, 0x00, 0x1e, \ + 0x0c, 0x09, 0xe1, 0x3c, \ + 0xc0, 0x31, 0x65, 0x19, \ + 0xc5, 0x07, 0x84, 0x14, \ + 0x04, 0x06, 0x82, 0x09, \ + 0xe1, 0x3c, 0x42, 0x06, \ + 0x65, 0x19, 0x0c, 0x09, \ + 0x84, 0x14, 0xc0, 0x31, \ + 0x82, 0x09, 0xc7, 0x07, \ + 0x40, 0xc6, 0x04, 0x06, \ + 0x0c, 0x15, 0xe1, 0x3c, \ + 0xc0, 0x0c, 0x65, 0x49, \ + 0x00, 0x1e, 0x80, 0x04, \ + 0xe1, 0x3c, 0x41, 0x06, \ + 0x65, 0x4d, 0x0c, 0x15, \ + 0x80, 0x04, 0xc0, 0x0c, \ + 0x43, 0x46, 0x00, 0x1e, \ + 0x0c, 0x09, 0xe1, 0x3c, \ + 0xc0, 0x31, 0x65, 0x49, \ + 0xc5, 0x07, 0x84, 0x14, \ + 0x04, 0x06, 0x82, 0x09, \ + 0xe1, 0x3c, 0x43, 0x86, \ + 0x65, 0x49, 0x0c, 0x09, \ + 0x84, 0x14, 0xc0, 0x31, \ + 0x82, 0x09, 0xc7, 0x07, \ + 0x00, 0x46, 0x04, 0x06, \ + 0x0c, 0x15, 0xe1, 0x40, \ + 0xc0, 0x0c, 0x65, 0x55, \ + 0x00, 0x1e, 0x80, 0x04, \ + 0xe1, 0x40, 0x00, 0x86, \ + 0x65, 0x59, 0x0c, 0x15, \ + 0x80, 0x04, 0xc0, 0x0c, \ + 0x02, 0xc6, 0x00, 0x1e, \ + 0x0c, 0x09, 0xe1, 0x40, \ + 0xc0, 0x31, 0x65, 0x79, \ + 0xc5, 0x07, 0x84, 0x14, \ + 0x04, 0x06, 0x82, 0x09, \ + 0xe1, 0x40, 0x03, 0x06, \ + 0x65, 0x79, 0x0c, 0x09, \ + 0x84, 0x14, 0xc0, 0x31, \ + 0x82, 0x09, 0xc7, 0x07, \ + 0x01, 0xc6, 0x04, 0x06, \ + 0x0c, 0x15, 0xe1, 0x40, \ + 0xc0, 0x0c, 0x65, 0x61, \ + 0x00, 0x1e, 0x80, 0x04, \ + 0xe1, 0x40, 0x40, 0x06, \ + 0x65, 0x65, 0x0c, 0x15, \ + 0x80, 0x04, 0xc0, 0x0c, \ + 0x42, 0x46, 0x00, 0x1e, \ + 0x0c, 0x09, 0xe1, 0x40, \ + 0xc0, 0x31, 0x65, 0xa9, \ + 0xc5, 0x07, 0x84, 0x14, \ + 0x04, 0x06, 0x82, 0x09, \ + 0xe1, 0x40, 0x42, 0x86, \ + 0x65, 0xa9, 0x0c, 0x09, \ + 0x84, 0x14, 0xc0, 0x31, \ + 0x82, 0x09, 0xc7, 0x07, \ + 0x01, 0x06, 0x04, 0x06, \ + 0x80, 0x04, 0xe1, 0x34, \ + 0x01, 0x86, 0x00, 0x86, \ + 0x80, 0x04, 0xe1, 0x34, \ + 0x0c, 0x0c, 0x80, 0x76, \ + 0xdd, 0x93, 0xf2, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x31, 0x64, 0x61, \ + 0x64, 0x65, 0x0c, 0x19, \ + 0x4e, 0xd8, 0xc0, 0x31, \ + 0xa1, 0x0b, 0xef, 0x94, \ + 0x0c, 0x09, 0x94, 0x35, \ + 0xc0, 0x31, 0x64, 0x71, \ + 0x64, 0x75, 0x0c, 0x19, \ + 0x4e, 0x38, 0xc0, 0x31, \ + 0xa1, 0x0b, 0xef, 0x94, \ + 0x0c, 0x09, 0x90, 0xf5, \ + 0xc0, 0x31, 0x64, 0x81, \ + 0x64, 0x85, 0x0c, 0x19, \ + 0x4d, 0x98, 0xc0, 0x31, \ + 0xa1, 0x0b, 0xef, 0x94, \ + 0x0c, 0x09, 0x8e, 0xb5, \ + 0xc0, 0x31, 0x64, 0x99, \ + 0x64, 0x9d, 0x0c, 0x19, \ + 0x4c, 0xf8, 0xc0, 0x31, \ + 0xa1, 0x0b, 0xef, 0x94, \ + 0x0c, 0x09, 0x8c, 0x75, \ + 0xc0, 0x31, 0x64, 0xa1, \ + 0x64, 0xa5, 0x0c, 0x19, \ + 0x4c, 0x58, 0xc0, 0x31, \ + 0xa1, 0x0b, 0xef, 0x94, \ + 0x0c, 0x09, 0x8a, 0x35, \ + 0xc0, 0x31, 0x64, 0xe1, \ + 0x64, 0xe5, 0x0c, 0x19, \ + 0x4b, 0xb8, 0xc0, 0x31, \ + 0xa1, 0x0b, 0xef, 0x94, \ + 0x0c, 0x09, 0x86, 0xf5, \ + 0xc0, 0x31, 0x65, 0x11, \ + 0x65, 0x15, 0x0c, 0x19, \ + 0x4b, 0x18, 0xc0, 0x31, \ + 0xa1, 0x0b, 0xef, 0x94, \ + 0x0c, 0x09, 0x84, 0xb5, \ + 0xc0, 0x31, 0x65, 0x41, \ + 0x65, 0x45, 0x0c, 0x19, \ + 0x4a, 0x78, 0xc0, 0x31, \ + 0xa1, 0x0b, 0xef, 0x94, \ + 0x0c, 0x09, 0x82, 0x75, \ + 0xc0, 0x31, 0x65, 0x71, \ + 0x65, 0x75, 0x0c, 0x19, \ + 0x49, 0xd8, 0xc0, 0x31, \ + 0xa1, 0x0b, 0xef, 0x94, \ + 0x0c, 0x0c, 0x80, 0x85, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0x65, 0xa1, 0x0c, 0x09, \ + 0x0c, 0x19, 0xc0, 0x31, \ + 0xc0, 0x31, 0x65, 0xa5, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0x94, 0x08, 0xe8, \ + 0x6d, 0x09, 0x00, 0x39, \ + 0x04, 0x14, 0x40, 0x2f, \ + 0x00, 0x00, 0xfb, 0x00, \ + 0x40, 0x54, 0x66, 0x51, \ + 0x03, 0xb8, 0x85, 0x11, \ + 0xc1, 0xc3, 0xff, 0xf0, \ + 0xfa, 0x84, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x31, 0x64, 0x25, \ + 0x64, 0x11, 0x0c, 0x19, \ + 0xc3, 0x0d, 0xc0, 0x31, \ + 0x0c, 0x09, 0x80, 0xe5, \ + 0xc0, 0x2f, 0x6d, 0x01, \ + 0xe0, 0x80, 0x40, 0x4a, \ + 0x0c, 0x39, 0x88, 0x45, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x66, 0x51, 0x0e, 0x60, \ + 0x05, 0x11, 0xc0, 0x54, \ + 0x41, 0x18, 0x9b, 0x21, \ + 0x8e, 0x0b, 0x6f, 0xf0, \ + 0x61, 0x48, 0x40, 0x46, \ + 0x80, 0x04, 0x8c, 0x2b, \ + 0xe0, 0x19, 0x80, 0x18, \ + 0x83, 0x01, 0x00, 0x16, \ + 0x6f, 0xdc, 0x41, 0xc8, \ + 0x5f, 0x38, 0x81, 0x11, \ + 0x03, 0x11, 0xef, 0xd8, \ + 0x47, 0x18, 0x8c, 0x0b, \ + 0x8e, 0x0b, 0xef, 0xf4, \ + 0xf2, 0x84, 0x0c, 0x0c, \ + 0xff, 0xf0, 0x02, 0xa8, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x00, 0x60, 0x40, 0x2f, \ + 0xc0, 0x54, 0x66, 0x81, \ + 0x9b, 0x21, 0x05, 0x11, \ + 0xef, 0xf0, 0x48, 0xc8, \ + 0x6d, 0x01, 0x0c, 0x09, \ + 0x83, 0x31, 0x40, 0x2f, \ + 0x64, 0x21, 0x0c, 0x19, \ + 0x81, 0x41, 0x40, 0x31, \ + 0x64, 0x25, 0x0c, 0x29, \ + 0xd0, 0x09, 0xc0, 0x31, \ + 0x6d, 0x01, 0x4c, 0x01, \ + 0xc2, 0x19, 0x40, 0x2f, \ + 0x64, 0x21, 0x4c, 0x11, \ + 0x82, 0x21, 0x40, 0x31, \ + 0x64, 0x25, 0x4c, 0x21, \ + 0x43, 0xc6, 0x40, 0x31, \ + 0xc0, 0x20, 0x72, 0xfd, \ + 0x8c, 0x0b, 0x01, 0x21, \ + 0xf2, 0x84, 0x0c, 0x0c, \ + 0xff, 0xd4, 0x38, 0x88, \ + 0xfa, 0x7c, 0x04, 0x4c, \ + 0x12, 0x06, 0x00, 0x6b, \ + 0x55, 0xc6, 0xe0, 0x08, \ + 0x56, 0x46, 0x61, 0x10, \ + 0x51, 0x06, 0xe1, 0x10, \ + 0x81, 0x71, 0x61, 0x38, \ + 0x4c, 0x91, 0x87, 0xe5, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x43, 0xf8, 0x8c, 0x0b, \ + 0x49, 0x78, 0xe8, 0x00, \ + 0x8c, 0x0b, 0x68, 0x00, \ + 0x80, 0x08, 0xa8, 0x04, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x0c, 0x0b, 0x8a, 0xe5, \ + 0x4e, 0xd8, 0x8e, 0x1b, \ + 0x00, 0x0a, 0xe8, 0x00, \ + 0x8a, 0x75, 0xe0, 0x58, \ + 0xe8, 0x14, 0x70, 0x28, \ + 0x81, 0x11, 0x2c, 0x04, \ + 0x00, 0x16, 0x83, 0x21, \ + 0x5f, 0xd8, 0x95, 0x11, \ + 0x8c, 0x0b, 0x68, 0x28, \ + 0x83, 0x11, 0x2c, 0x04, \ + 0xa8, 0x04, 0x80, 0x16, \ + 0x40, 0x4a, 0x80, 0x08, \ + 0x16, 0x05, 0xe0, 0x84, \ + 0x02, 0x06, 0x40, 0x00, \ + 0x40, 0x11, 0xc0, 0x00, \ + 0xa0, 0x04, 0xc0, 0x00, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xdc, \ + 0x80, 0x18, 0xa0, 0x04, \ + 0xf3, 0xbc, 0x42, 0x10, \ + 0x00, 0x46, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x10, \ + 0xc2, 0x19, 0x80, 0x18, \ + 0x82, 0x71, 0x80, 0x16, \ + 0xf0, 0x45, 0xe8, 0x7d, \ + 0xf2, 0x7c, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x6f, 0xf0, 0x77, 0x08, \ + 0x00, 0x16, 0x80, 0x6b, \ + 0x42, 0xc6, 0x60, 0x08, \ + 0x4c, 0x01, 0xe0, 0x10, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x5c, 0x98, 0x81, 0x21, \ + 0x8c, 0x0b, 0x68, 0x28, \ + 0x66, 0x51, 0x0e, 0x60, \ + 0x53, 0x46, 0x40, 0x54, \ + 0x11, 0x86, 0xe1, 0x40, \ + 0xa4, 0x04, 0xe1, 0x24, \ + 0x81, 0xb5, 0x80, 0xa8, \ + 0xef, 0xf0, 0x76, 0x88, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x85, 0x11, 0x40, 0x2f, \ + 0x8e, 0x0b, 0x03, 0x21, \ + 0xef, 0xec, 0x76, 0xa8, \ + 0x80, 0x08, 0xa0, 0x04, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x0c, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0x4c, 0x00, 0x0a, \ + 0x54, 0x0a, 0x82, 0x75, \ + 0xfa, 0x25, 0xe0, 0x8c, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0xf8, 0x65, 0xe0, 0x4c, \ + 0x5e, 0xb8, 0xfb, 0xf5, \ + 0xa0, 0x04, 0xe8, 0x28, \ + 0x40, 0x4a, 0x80, 0x08, \ + 0x80, 0xe5, 0xe0, 0x84, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x75, 0xe0, 0x50, \ + 0xef, 0xf0, 0x73, 0xc8, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x85, 0x11, 0x40, 0x2f, \ + 0x8e, 0x0b, 0x05, 0x21, \ + 0xef, 0xec, 0x73, 0xe8, \ + 0xef, 0xd8, 0x51, 0xe8, \ + 0x81, 0x11, 0x01, 0x01, \ + 0x0c, 0x0c, 0x8c, 0x2b, \ + 0x33, 0xd8, 0xf2, 0x3c, \ + 0xc1, 0xc3, 0xff, 0xd8, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x6f, 0xf0, 0x71, 0x68, \ + 0x00, 0x26, 0x80, 0x6b, \ + 0x42, 0x86, 0x60, 0x08, \ + 0x4c, 0x01, 0xe0, 0x3c, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x56, 0xf8, 0x81, 0x21, \ + 0x8c, 0x0b, 0x68, 0x28, \ + 0x66, 0x51, 0x0e, 0x60, \ + 0x53, 0x46, 0x40, 0x54, \ + 0x11, 0x46, 0xe1, 0x40, \ + 0xa4, 0x04, 0xe1, 0x24, \ + 0x81, 0xb5, 0x80, 0xa8, \ + 0xef, 0xf0, 0x70, 0xe8, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x85, 0x11, 0x40, 0x2f, \ + 0x8e, 0x0b, 0x07, 0x21, \ + 0xef, 0xec, 0x71, 0x08, \ + 0x80, 0x08, 0xa0, 0x04, \ + 0xe0, 0x80, 0x40, 0x4a, \ + 0x54, 0x0a, 0x82, 0x65, \ + 0xfa, 0xe5, 0xe0, 0x8c, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0xfa, 0x25, 0xe0, 0x54, \ + 0x59, 0x78, 0xfd, 0x55, \ + 0xa0, 0x04, 0xe8, 0x28, \ + 0x40, 0x4a, 0x80, 0x08, \ + 0x80, 0xe5, 0xe0, 0x84, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x84, 0x35, 0xe0, 0x58, \ + 0xef, 0xf0, 0x6e, 0x88, \ + 0x61, 0x38, 0x41, 0x06, \ + 0x80, 0x04, 0x89, 0x21, \ + 0xd0, 0x19, 0x80, 0x18, \ + 0x85, 0x11, 0x00, 0x16, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x8e, 0x0b, 0x40, 0x2f, \ + 0xef, 0xec, 0x6e, 0x48, \ + 0xe1, 0x38, 0x41, 0x06, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xd0, 0x19, \ + 0xef, 0xd8, 0x4b, 0xe8, \ + 0x81, 0x11, 0x01, 0x01, \ + 0x0c, 0x0c, 0x8c, 0x2b, \ + 0x2d, 0xd8, 0xf2, 0x3c, \ + 0xc1, 0xc3, 0xff, 0xd8, \ + 0xfa, 0x7c, 0x04, 0x4c, \ + 0x80, 0x6b, 0x02, 0x7b, \ + 0xef, 0xf0, 0x6b, 0x48, \ + 0x60, 0x08, 0x00, 0x36, \ + 0xe0, 0x04, 0x42, 0x26, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x81, 0x21, 0x40, 0x2f, \ + 0x68, 0x28, 0x50, 0xe8, \ + 0x10, 0x60, 0x8c, 0x0b, \ + 0x40, 0x54, 0x66, 0x51, \ + 0xe1, 0x40, 0x55, 0x46, \ + 0xe1, 0x24, 0x13, 0xc6, \ + 0x80, 0xb8, 0xa8, 0x04, \ + 0x6a, 0xd8, 0x81, 0xb5, \ + 0x0c, 0x39, 0xef, 0xf0, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x0b, 0x21, 0x85, 0x11, \ + 0x6a, 0xf8, 0x90, 0x0b, \ + 0xa4, 0x04, 0xef, 0xec, \ + 0x40, 0x4a, 0x80, 0x08, \ + 0x82, 0x65, 0xe0, 0x80, \ + 0xe0, 0x8c, 0x56, 0x0a, \ + 0x0c, 0x05, 0xfa, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0x5c, 0x00, 0x0a, \ + 0xfd, 0x55, 0xfa, 0x25, \ + 0xe8, 0x28, 0x53, 0x68, \ + 0x80, 0x08, 0xa4, 0x04, \ + 0xe0, 0x84, 0x40, 0x4a, \ + 0x0c, 0x05, 0x82, 0x05, \ + 0x40, 0x0c, 0x65, 0x0d, \ + 0x00, 0x0a, 0x81, 0x91, \ + 0x82, 0xf5, 0xe0, 0x60, \ + 0xad, 0x91, 0x69, 0x7d, \ + 0x68, 0x38, 0x82, 0x95, \ + 0x0c, 0x39, 0xef, 0xf0, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x0d, 0x21, 0x85, 0x11, \ + 0x68, 0x58, 0x90, 0x0b, \ + 0x81, 0x91, 0xef, 0xec, \ + 0xef, 0xd8, 0x46, 0x48, \ + 0x81, 0x11, 0x01, 0x01, \ + 0x6f, 0xd8, 0x68, 0x68, \ + 0x92, 0x0b, 0x8c, 0x2b, \ + 0xf2, 0x7c, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x04, 0x04, 0x4c, \ + 0x61, 0x4c, 0x01, 0x06, \ + 0x00, 0x64, 0x81, 0x11, \ + 0xe0, 0x60, 0x45, 0xc6, \ + 0x6f, 0x8c, 0x62, 0xe8, \ + 0xe4, 0x10, 0x00, 0x60, \ + 0x61, 0x4c, 0x0e, 0xc6, \ + 0xef, 0xfc, 0x43, 0xf6, \ + 0x03, 0xd6, 0x18, 0x1c, \ + 0x9c, 0x04, 0xed, 0xdc, \ + 0x02, 0x06, 0x1a, 0x1c, \ + 0x4c, 0x15, 0xed, 0x00, \ + 0x42, 0x46, 0x62, 0x40, \ + 0x84, 0x0c, 0xe1, 0x90, \ + 0xe8, 0x00, 0x52, 0x88, \ + 0x61, 0x00, 0x00, 0x46, \ + 0xe1, 0x9c, 0x03, 0x86, \ + 0x02, 0x86, 0x00, 0x1e, \ + 0x44, 0x06, 0xe1, 0x00, \ + 0x06, 0xc6, 0x6c, 0x7c, \ + 0x04, 0x2e, 0xe1, 0x00, \ + 0xee, 0x18, 0x49, 0x86, \ + 0x61, 0x00, 0x03, 0x06, \ + 0xe0, 0x64, 0x04, 0x06, \ + 0x8c, 0x4e, 0x9c, 0x04, \ + 0x74, 0x98, 0x84, 0x2e, \ + 0x1c, 0x04, 0xef, 0xf4, \ + 0x40, 0x11, 0xb5, 0x11, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0xef, 0xf8, 0x45, 0x88, \ + 0x43, 0xc6, 0x1c, 0x04, \ + 0x84, 0x0e, 0xe1, 0x90, \ + 0xe8, 0x24, 0x7f, 0x28, \ + 0x04, 0xc6, 0x1c, 0x14, \ + 0x88, 0x0e, 0xe1, 0x94, \ + 0x68, 0x24, 0x7f, 0x08, \ + 0x1c, 0x14, 0x82, 0x0b, \ + 0xe1, 0x90, 0x45, 0x86, \ + 0x4f, 0x78, 0x88, 0x0e, \ + 0x82, 0x0b, 0x68, 0x00, \ + 0x0c, 0x4c, 0x81, 0x01, \ + 0xdd, 0x93, 0xf2, 0x04, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x41, 0xf6, 0x00, 0x6b, \ + 0x61, 0x58, 0xe0, 0x3c, \ + 0x81, 0x11, 0x6f, 0x90, \ + 0x6d, 0x01, 0x0c, 0x09, \ + 0x89, 0x11, 0x40, 0x2f, \ + 0x6d, 0x05, 0x4c, 0x11, \ + 0xb7, 0x71, 0x40, 0x2f, \ + 0x6d, 0x09, 0x4c, 0x71, \ + 0x40, 0x4a, 0xc0, 0x2f, \ + 0x82, 0xa5, 0xe0, 0x8c, \ + 0x65, 0x65, 0x0c, 0x19, \ + 0xa7, 0x1b, 0xc0, 0x2f, \ + 0x0c, 0x19, 0x8c, 0x35, \ + 0xc0, 0x31, 0x63, 0x91, \ + 0x40, 0x0a, 0x82, 0x0f, \ + 0x8a, 0x45, 0xe0, 0x80, \ + 0x60, 0x3c, 0x41, 0xf6, \ + 0x5f, 0x58, 0x81, 0x11, \ + 0x41, 0x86, 0xef, 0x90, \ + 0x80, 0x04, 0xe1, 0x00, \ + 0x40, 0x4a, 0x80, 0x08, \ + 0x40, 0x60, 0xe0, 0x80, \ + 0x61, 0xd8, 0xdc, 0x00, \ + 0x41, 0xf6, 0xc8, 0x08, \ + 0x83, 0x11, 0x60, 0x3c, \ + 0xef, 0x90, 0x5e, 0x68, \ + 0x6d, 0x01, 0x0c, 0x09, \ + 0x40, 0x00, 0xc0, 0x2f, \ + 0x4c, 0x01, 0xf3, 0xdc, \ + 0x40, 0x2f, 0x6d, 0x01, \ + 0x4a, 0x38, 0x8c, 0x0b, \ + 0x41, 0xf6, 0xe8, 0x28, \ + 0x83, 0x11, 0x60, 0x3c, \ + 0xef, 0x90, 0x5d, 0x68, \ + 0x0c, 0x0c, 0x8c, 0x0b, \ + 0x21, 0x98, 0xf2, 0x0c, \ + 0x41, 0xf6, 0xff, 0xd4, \ + 0x83, 0x11, 0x60, 0x3c, \ + 0xef, 0x90, 0x5c, 0xc8, \ + 0x65, 0x75, 0x0c, 0x09, \ + 0xa7, 0x0b, 0xc0, 0x2f, \ + 0x0c, 0x09, 0x98, 0x35, \ + 0xc0, 0x31, 0x63, 0xe9, \ + 0xa5, 0x0b, 0xc4, 0x09, \ + 0x0c, 0x09, 0x96, 0x75, \ + 0xc0, 0x2f, 0x6d, 0x01, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x0c, 0x09, 0x96, 0x05, \ + 0xc0, 0x2f, 0x6d, 0x0d, \ + 0x94, 0x75, 0xa7, 0x0b, \ + 0x63, 0x91, 0x0c, 0x09, \ + 0x40, 0x4a, 0xc0, 0x31, \ + 0x92, 0xa5, 0xe0, 0x88, \ + 0x60, 0x3c, 0x41, 0xf6, \ + 0x5a, 0xb8, 0x81, 0x11, \ + 0x51, 0x86, 0xef, 0x90, \ + 0xa0, 0x04, 0xe1, 0x00, \ + 0x40, 0x4a, 0x80, 0x08, \ + 0x40, 0x60, 0xe0, 0x80, \ + 0x5d, 0x38, 0xdc, 0x00, \ + 0x41, 0xf6, 0xc8, 0x08, \ + 0x83, 0x11, 0x60, 0x3c, \ + 0xef, 0x90, 0x59, 0xc8, \ + 0x68, 0x28, 0x4b, 0x08, \ + 0x6f, 0xd8, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0x68, 0x14, \ + 0x67, 0xcd, 0x0c, 0x05, \ + 0x0c, 0x15, 0xc0, 0x05, \ + 0xc0, 0x0c, 0x64, 0xf5, \ + 0x8a, 0x05, 0xc2, 0x0d, \ + 0x63, 0x91, 0x0c, 0x09, \ + 0xc4, 0x09, 0xc0, 0x31, \ + 0x63, 0x91, 0x4c, 0x01, \ + 0x8c, 0x0b, 0x40, 0x31, \ + 0xe8, 0x14, 0x73, 0x28, \ + 0x6d, 0x01, 0x0c, 0x09, \ + 0x81, 0x11, 0x40, 0x2f, \ + 0x4c, 0x01, 0xd0, 0x09, \ + 0x40, 0x2f, 0x6d, 0x01, \ + 0xe0, 0x3c, 0x41, 0xf6, \ + 0xef, 0x90, 0x57, 0x88, \ + 0x80, 0x08, 0xa0, 0x04, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0xdc, 0x00, 0x40, 0x60, \ + 0xc8, 0x08, 0x6d, 0x88, \ + 0x60, 0x3c, 0x41, 0xf6, \ + 0x56, 0xb8, 0x83, 0x11, \ + 0x76, 0xf8, 0xef, 0x90, \ + 0x8c, 0x0b, 0x68, 0x08, \ + 0x42, 0xd8, 0x81, 0x45, \ + 0x8c, 0x0b, 0x68, 0x28, \ + 0x6d, 0x01, 0x0c, 0x09, \ + 0x40, 0x4a, 0xc0, 0x2f, \ + 0x82, 0x65, 0xe0, 0x8c, \ + 0x6c, 0xe1, 0x0c, 0x09, \ + 0xa1, 0x0b, 0xc0, 0x2f, \ + 0xb5, 0x01, 0x82, 0x25, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x83, 0xf5, 0xc0, 0x2f, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0x41, 0xf6, 0xdd, 0x93, \ + 0x81, 0x11, 0x60, 0x3c, \ + 0xef, 0x90, 0x54, 0xc8, \ + 0xe1, 0x00, 0x41, 0x86, \ + 0x80, 0x08, 0x80, 0x04, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0xdc, 0x00, 0x40, 0x60, \ + 0xc8, 0x08, 0x6a, 0xa8, \ + 0x60, 0x3c, 0x41, 0xf6, \ + 0x53, 0xd8, 0x83, 0x11, \ + 0x4c, 0x71, 0xef, 0x90, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x4c, 0x01, 0x8d, 0x01, \ + 0xc0, 0x2f, 0x6d, 0x05, \ + 0xc3, 0x13, 0xc1, 0x83, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x6d, 0x05, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x2f, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x01, 0x46, 0x00, 0x6b, \ + 0x43, 0x18, 0xe1, 0x94, \ + 0x40, 0xc6, 0xef, 0x90, \ + 0xc0, 0x00, 0x61, 0x91, \ + 0xa3, 0x1b, 0x80, 0x18, \ + 0x59, 0x38, 0x82, 0x95, \ + 0x81, 0x01, 0x6f, 0x90, \ + 0x4c, 0x01, 0xb5, 0x01, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x6f, 0xfc, 0x6f, 0xc8, \ + 0xff, 0xd5, 0x8c, 0x0b, \ + 0x80, 0x16, 0x83, 0x11, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x84, 0x04, 0x0c, \ + 0x40, 0x06, 0x00, 0x6b, \ + 0x00, 0x04, 0xe1, 0x14, \ + 0x83, 0x21, 0x83, 0x71, \ + 0xc4, 0x19, 0x80, 0x18, \ + 0x40, 0x06, 0x00, 0x16, \ + 0x00, 0x04, 0xe1, 0x28, \ + 0x00, 0x76, 0x85, 0x11, \ + 0xe1, 0x28, 0x01, 0x06, \ + 0x00, 0x76, 0x80, 0x04, \ + 0x74, 0x98, 0x8c, 0x0b, \ + 0x01, 0x46, 0xe8, 0x24, \ + 0xbd, 0x11, 0x61, 0x28, \ + 0x83, 0x21, 0x00, 0x04, \ + 0x8c, 0x0b, 0x00, 0x76, \ + 0xf2, 0x84, 0x0c, 0x0c, \ + 0xe0, 0x24, 0x33, 0xe8, \ + 0xfa, 0x7c, 0x04, 0x4c, \ + 0x40, 0x86, 0x00, 0x8b, \ + 0x00, 0x04, 0xe1, 0x28, \ + 0x03, 0x11, 0x82, 0x7b, \ + 0x06, 0x6b, 0x8e, 0x23, \ + 0x66, 0x51, 0x12, 0x80, \ + 0x00, 0x16, 0xc0, 0x54, \ + 0xe1, 0x28, 0x57, 0x46, \ + 0x8d, 0xc5, 0x8f, 0x2e, \ + 0x61, 0x28, 0x01, 0x86, \ + 0x00, 0x04, 0x83, 0x11, \ + 0x00, 0x76, 0x81, 0x21, \ + 0xe1, 0x28, 0x40, 0xc6, \ + 0x00, 0x16, 0x80, 0x04, \ + 0xe0, 0x10, 0x02, 0x06, \ + 0x68, 0x24, 0x71, 0xe8, \ + 0x41, 0x46, 0x90, 0x0b, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0x81, 0xc5, 0x80, 0xa8, \ + 0x68, 0x00, 0x45, 0xe8, \ + 0x10, 0x39, 0x90, 0x0b, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x07, 0x21, 0x83, 0x11, \ + 0x4c, 0x38, 0x92, 0x0b, \ + 0xac, 0x04, 0xef, 0xec, \ + 0xa1, 0x0b, 0x80, 0x08, \ + 0x10, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0x64, 0x00, 0x0a, \ + 0x54, 0x0a, 0x82, 0x75, \ + 0xfa, 0x25, 0xe0, 0x8c, \ + 0x65, 0x0d, 0x10, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0xf8, 0x65, 0xe0, 0x64, \ + 0x41, 0x86, 0xfb, 0xd5, \ + 0x81, 0x11, 0x61, 0x28, \ + 0x81, 0x21, 0x00, 0x04, \ + 0x18, 0x0e, 0x80, 0x04, \ + 0xe1, 0x28, 0x40, 0xc6, \ + 0x00, 0x16, 0x80, 0x04, \ + 0xe0, 0x10, 0x02, 0x06, \ + 0x68, 0x24, 0x6e, 0xa8, \ + 0x41, 0x46, 0x90, 0x0b, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0x81, 0xc5, 0x80, 0xa8, \ + 0x68, 0x00, 0x42, 0xa8, \ + 0x10, 0x39, 0x90, 0x0b, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x09, 0x21, 0x83, 0x11, \ + 0x48, 0xf8, 0x92, 0x0b, \ + 0xac, 0x04, 0xef, 0xec, \ + 0xa3, 0x0b, 0x80, 0x08, \ + 0x10, 0x05, 0x82, 0x65, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0x68, 0x00, 0x0a, \ + 0x44, 0x10, 0x0c, 0x60, \ + 0xc4, 0x04, 0x0e, 0x70, \ + 0x54, 0x0a, 0x82, 0x75, \ + 0xf8, 0xa5, 0xe0, 0x8c, \ + 0x65, 0x0d, 0x10, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0xf6, 0xe5, 0xe0, 0x68, \ + 0x8f, 0x04, 0xfb, 0x95, \ + 0xe4, 0x65, 0xc0, 0x7d, \ + 0xf2, 0x7c, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x61, 0x28, 0x03, 0x46, \ + 0x04, 0x14, 0x81, 0x61, \ + 0x04, 0x66, 0x83, 0x21, \ + 0x6a, 0xf8, 0x83, 0x11, \ + 0x01, 0x06, 0xe8, 0x24, \ + 0x80, 0x04, 0xe1, 0x28, \ + 0x40, 0x06, 0x00, 0x66, \ + 0x80, 0x04, 0xe1, 0x14, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xf4, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x0c, 0x04, 0x4c, \ + 0x81, 0x31, 0x40, 0x14, \ + 0xbe, 0x19, 0xf1, 0x17, \ + 0x64, 0x91, 0x40, 0x15, \ + 0x00, 0x15, 0xc0, 0x05, \ + 0x00, 0x25, 0xe0, 0x18, \ + 0xe0, 0x17, 0xe0, 0x50, \ + 0x42, 0x10, 0x61, 0x27, \ + 0xc3, 0x00, 0x70, 0x01, \ + 0x61, 0x4d, 0x40, 0x25, \ + 0x00, 0x25, 0xc0, 0x0a, \ + 0x04, 0x24, 0xe0, 0x50, \ + 0x24, 0x1f, 0xfb, 0x80, \ + 0xe5, 0x70, 0x04, 0x00, \ + 0x61, 0x51, 0x40, 0x15, \ + 0x00, 0x15, 0xc0, 0x0a, \ + 0xed, 0x17, 0xe0, 0x54, \ + 0xf3, 0xf0, 0x42, 0x10, \ + 0x65, 0x51, 0x40, 0x11, \ + 0x00, 0x15, 0xc0, 0x28, \ + 0xdd, 0x17, 0xe0, 0x54, \ + 0xf3, 0xf0, 0x42, 0x10, \ + 0x65, 0x55, 0x40, 0x11, \ + 0x81, 0x11, 0x40, 0x28, \ + 0x08, 0x44, 0x83, 0x95, \ + 0xf1, 0x47, 0x88, 0x31, \ + 0x44, 0x41, 0xc8, 0x4b, \ + 0xc0, 0x27, 0x6e, 0x91, \ + 0xe1, 0x47, 0x88, 0x44, \ + 0x44, 0x41, 0xc8, 0x4b, \ + 0xc0, 0x27, 0x6e, 0x95, \ + 0xd1, 0x47, 0x88, 0x44, \ + 0x44, 0x41, 0xc8, 0x4b, \ + 0xc0, 0x27, 0x6e, 0x99, \ + 0xc8, 0x4b, 0xc9, 0x44, \ + 0x6e, 0x8d, 0x44, 0x41, \ + 0x06, 0x8a, 0xc0, 0x27, \ + 0xc0, 0x01, 0x60, 0x61, \ + 0x04, 0x00, 0xf8, 0xb5, \ + 0x40, 0x01, 0x65, 0xd1, \ + 0x83, 0x75, 0x81, 0x31, \ + 0x88, 0x31, 0x48, 0x4b, \ + 0x69, 0xf1, 0x44, 0x41, \ + 0x88, 0x44, 0xc0, 0x27, \ + 0xc8, 0x4b, 0xe1, 0x47, \ + 0x69, 0xf5, 0x44, 0x41, \ + 0x88, 0x44, 0xc0, 0x27, \ + 0xc8, 0x4b, 0xd1, 0x47, \ + 0x69, 0xf9, 0x44, 0x41, \ + 0xc9, 0x44, 0xc0, 0x27, \ + 0x44, 0x41, 0xc8, 0x4b, \ + 0xc0, 0x27, 0x69, 0xed, \ + 0x06, 0x8a, 0x08, 0x44, \ + 0x78, 0xf5, 0xe0, 0x40, \ + 0x40, 0x41, 0xf1, 0x47, \ + 0x40, 0x28, 0x65, 0x11, \ + 0x66, 0x51, 0x08, 0x00, \ + 0x00, 0x25, 0xc0, 0x01, \ + 0x0a, 0x00, 0x61, 0x84, \ + 0xc0, 0xa2, 0x65, 0xf0, \ + 0x81, 0x61, 0x01, 0xc1, \ + 0x40, 0x21, 0xe1, 0x27, \ + 0xc0, 0x28, 0x65, 0x15, \ + 0xe1, 0x88, 0x00, 0x25, \ + 0x40, 0x21, 0xf1, 0x27, \ + 0xc0, 0x28, 0x65, 0x21, \ + 0xe1, 0x88, 0x00, 0x25, \ + 0x40, 0x21, 0xe1, 0x27, \ + 0xc0, 0x28, 0x65, 0x25, \ + 0xe1, 0x8c, 0x00, 0x25, \ + 0x40, 0x21, 0xff, 0x27, \ + 0xc0, 0x28, 0x65, 0x01, \ + 0xe1, 0x8c, 0x00, 0x25, \ + 0x44, 0x20, 0xf1, 0x27, \ + 0x40, 0x21, 0xf0, 0xfc, \ + 0xc0, 0x28, 0x64, 0xf1, \ + 0xe1, 0x8c, 0x00, 0x25, \ + 0xe1, 0x8c, 0x00, 0x35, \ + 0x47, 0x37, 0xe9, 0x27, \ + 0xa0, 0x39, 0x9e, 0x29, \ + 0x40, 0x21, 0xa6, 0x2f, \ + 0xc0, 0x28, 0x64, 0xfd, \ + 0xe1, 0x8c, 0x00, 0x25, \ + 0xe1, 0x8c, 0x00, 0x35, \ + 0x45, 0x37, 0xe1, 0x27, \ + 0xa0, 0x39, 0x9e, 0x29, \ + 0x40, 0x21, 0xa6, 0x2f, \ + 0xc0, 0x28, 0x64, 0xf9, \ + 0xe1, 0x8c, 0x00, 0x25, \ + 0xe1, 0x8c, 0x00, 0x35, \ + 0x3e, 0x29, 0xd1, 0x27, \ + 0xf0, 0x80, 0x46, 0x30, \ + 0x06, 0x06, 0x26, 0x2f, \ + 0xee, 0xc0, 0x60, 0x01, \ + 0x64, 0xf5, 0x40, 0x21, \ + 0x00, 0x25, 0xc0, 0x28, \ + 0x40, 0x25, 0xe1, 0x90, \ + 0x40, 0x0b, 0x6f, 0x25, \ + 0x60, 0x01, 0x04, 0x06, \ + 0x83, 0xa5, 0xff, 0xc0, \ + 0xe3, 0xbc, 0x00, 0x75, \ + 0x87, 0x7b, 0x84, 0x7f, \ + 0x40, 0x00, 0x0f, 0x06, \ + 0xc0, 0x84, 0x10, 0x64, \ + 0xc0, 0x00, 0x4a, 0x73, \ + 0x10, 0x74, 0x80, 0xd5, \ + 0xe1, 0x77, 0x98, 0x8b, \ + 0x90, 0x74, 0x94, 0x7c, \ + 0x88, 0x41, 0x42, 0x87, \ + 0x82, 0x61, 0x00, 0x83, \ + 0x64, 0xfd, 0x50, 0x73, \ + 0x84, 0xc1, 0x40, 0x14, \ + 0x18, 0x0a, 0x88, 0x51, \ + 0xf8, 0xb5, 0xe0, 0xd0, \ + 0x61, 0xfc, 0x00, 0x45, \ + 0xe0, 0x04, 0x0a, 0x06, \ + 0x40, 0x43, 0xe1, 0x47, \ + 0xc0, 0x14, 0x62, 0xb1, \ + 0xe1, 0xfc, 0x00, 0x45, \ + 0x62, 0xb5, 0x40, 0x43, \ + 0x00, 0x45, 0xc0, 0x14, \ + 0xe1, 0x47, 0xe2, 0x00, \ + 0x65, 0x71, 0x40, 0x41, \ + 0x00, 0x45, 0xc0, 0x28, \ + 0xd1, 0x47, 0xe2, 0x00, \ + 0x65, 0x75, 0x40, 0x41, \ + 0x00, 0x45, 0xc0, 0x28, \ + 0x40, 0x41, 0xe2, 0x00, \ + 0xc0, 0x28, 0x65, 0x79, \ + 0xe2, 0x04, 0x00, 0x45, \ + 0x40, 0x41, 0xf1, 0x47, \ + 0xc0, 0x28, 0x65, 0x7d, \ + 0xe2, 0x04, 0x00, 0x45, \ + 0x40, 0x41, 0xe1, 0x47, \ + 0xc0, 0x28, 0x65, 0x81, \ + 0xe2, 0x04, 0x00, 0x45, \ + 0x40, 0x41, 0xd1, 0x47, \ + 0xc0, 0x28, 0x65, 0x85, \ + 0xe2, 0x04, 0x00, 0x45, \ + 0x65, 0x89, 0x40, 0x41, \ + 0x00, 0x45, 0xc0, 0x28, \ + 0xf1, 0x47, 0xe2, 0x08, \ + 0x65, 0x8d, 0x40, 0x41, \ + 0x08, 0x00, 0x40, 0x28, \ + 0xc0, 0x02, 0x64, 0x31, \ + 0x51, 0xc4, 0x81, 0x75, \ + 0xec, 0x04, 0x0a, 0x50, \ + 0x6f, 0x6d, 0x48, 0xc5, \ + 0xa1, 0x5b, 0xc0, 0x09, \ + 0x01, 0xe5, 0xfe, 0x35, \ + 0x64, 0x31, 0x08, 0x00, \ + 0x10, 0x54, 0xc0, 0x03, \ + 0xe1, 0x57, 0x84, 0x11, \ + 0x6f, 0xe1, 0x48, 0x53, \ + 0xd1, 0x54, 0xc0, 0x13, \ + 0x6f, 0xdd, 0x48, 0x53, \ + 0x02, 0x8a, 0xc0, 0x13, \ + 0xfc, 0x95, 0xe5, 0x00, \ + 0x66, 0x31, 0x02, 0x00, \ + 0x08, 0x00, 0x40, 0x03, \ + 0xc0, 0xa2, 0x67, 0x91, \ + 0x59, 0x46, 0x35, 0x51, \ + 0x83, 0xa5, 0xe0, 0x00, \ + 0xe3, 0xbc, 0x00, 0x65, \ + 0x87, 0x6b, 0x84, 0x6f, \ + 0x40, 0x00, 0x0d, 0x06, \ + 0xc0, 0x84, 0x0e, 0x54, \ + 0xc0, 0x00, 0x48, 0x63, \ + 0x04, 0x64, 0x80, 0xd5, \ + 0xe1, 0x67, 0x98, 0x7b, \ + 0x84, 0x64, 0x90, 0x6c, \ + 0x88, 0x11, 0x42, 0x77, \ + 0x82, 0x51, 0x00, 0x73, \ + 0x64, 0xfd, 0x4e, 0x63, \ + 0x84, 0xc1, 0x40, 0x14, \ + 0x18, 0x0a, 0x88, 0x41, \ + 0xf8, 0xb5, 0xe1, 0x00, \ + 0x63, 0xa4, 0x00, 0x15, \ + 0x42, 0x10, 0x89, 0x21, \ + 0xc7, 0xff, 0x73, 0xfd, \ + 0x61, 0x65, 0x40, 0x15, \ + 0x00, 0x15, 0xc0, 0x0a, \ + 0x42, 0x10, 0xe3, 0xa8, \ + 0xc7, 0xff, 0x73, 0xfd, \ + 0x61, 0x69, 0x40, 0x15, \ + 0x00, 0x15, 0xc0, 0x0a, \ + 0x42, 0x10, 0xe3, 0xac, \ + 0xc7, 0xff, 0x73, 0xfd, \ + 0x61, 0x6d, 0x40, 0x15, \ + 0x00, 0x15, 0xc0, 0x0a, \ + 0x40, 0x13, 0xe3, 0xb0, \ + 0xc0, 0x14, 0x62, 0xe1, \ + 0xe3, 0xb4, 0x00, 0x15, \ + 0x40, 0x13, 0xe1, 0x17, \ + 0xc0, 0x14, 0x62, 0xe5, \ + 0xe3, 0xb4, 0x00, 0x15, \ + 0x62, 0xe9, 0x40, 0x13, \ + 0x00, 0x15, 0xc0, 0x14, \ + 0xf1, 0x17, 0xe3, 0xb8, \ + 0x65, 0xe1, 0x40, 0x11, \ + 0x00, 0x15, 0xc0, 0x28, \ + 0xe1, 0x17, 0xe3, 0xb8, \ + 0x65, 0xe5, 0x40, 0x11, \ + 0x00, 0x15, 0xc0, 0x28, \ + 0xd1, 0x17, 0xe3, 0xb8, \ + 0x65, 0xe9, 0x40, 0x11, \ + 0x00, 0x15, 0xc0, 0x28, \ + 0x40, 0x11, 0xe3, 0xb8, \ + 0x40, 0x28, 0x65, 0xed, \ + 0x67, 0x31, 0x02, 0x00, \ + 0x81, 0x75, 0xc0, 0x03, \ + 0x04, 0x20, 0x45, 0x34, \ + 0x42, 0x35, 0xec, 0x04, \ + 0xc0, 0x0b, 0x6b, 0x59, \ + 0xfe, 0x35, 0xa1, 0x2b, \ + 0x67, 0x71, 0x00, 0x00, \ + 0x93, 0x11, 0x40, 0x03, \ + 0x41, 0x24, 0x81, 0x75, \ + 0xec, 0x04, 0x02, 0x10, \ + 0x60, 0x41, 0x40, 0x25, \ + 0xa1, 0x1b, 0xc0, 0x05, \ + 0x0c, 0x4c, 0xfe, 0x35, \ + 0xdd, 0x93, 0xf0, 0x0c, \ + 0xf8, 0x80, 0x04, 0x0c, \ + 0xef, 0x4c, 0x02, 0x29, \ + 0x80, 0xb5, 0xa1, 0x2b, \ + 0xef, 0x3c, 0x02, 0x29, \ + 0x94, 0x65, 0xa1, 0x2b, \ + 0xf9, 0x31, 0x01, 0x21, \ + 0x81, 0x51, 0x07, 0x41, \ + 0x58, 0x52, 0x83, 0x85, \ + 0x4c, 0x52, 0x78, 0x0c, \ + 0x82, 0xc3, 0xf8, 0x90, \ + 0xfc, 0x18, 0x18, 0xc9, \ + 0x91, 0xc1, 0x58, 0x2f, \ + 0x44, 0x0a, 0x81, 0xb5, \ + 0xc2, 0x27, 0x60, 0x9c, \ + 0x73, 0xf9, 0x44, 0x20, \ + 0x18, 0xc0, 0x40, 0x01, \ + 0x44, 0x20, 0xec, 0x04, \ + 0xa1, 0xcb, 0xd8, 0x74, \ + 0x82, 0x51, 0xfc, 0xb5, \ + 0xe1, 0x40, 0x0a, 0x0a, \ + 0x47, 0x86, 0xf8, 0xf5, \ + 0x04, 0x0a, 0x61, 0x34, \ + 0x0c, 0x34, 0xfb, 0xfc, \ + 0x9f, 0xf8, 0x05, 0x9e, \ + 0xfa, 0x80, 0x04, 0x24, \ + 0x02, 0x39, 0x8c, 0x2e, \ + 0x87, 0x2b, 0xef, 0x4c, \ + 0x06, 0x46, 0x82, 0x15, \ + 0x8c, 0x34, 0xe1, 0x34, \ + 0x06, 0x0a, 0x8c, 0x34, \ + 0x86, 0xf5, 0xe0, 0x6c, \ + 0xef, 0x3c, 0x02, 0x19, \ + 0x82, 0x15, 0x83, 0x2b, \ + 0xe1, 0x34, 0x02, 0x46, \ + 0x84, 0x14, 0x84, 0x14, \ + 0xe0, 0x6c, 0x02, 0x0a, \ + 0x0d, 0x21, 0x84, 0x95, \ + 0xe1, 0x90, 0x43, 0x06, \ + 0x83, 0x11, 0x04, 0x26, \ + 0x6e, 0xb1, 0x40, 0x21, \ + 0x8d, 0x21, 0x40, 0x30, \ + 0x6d, 0x09, 0x00, 0x39, \ + 0x00, 0x00, 0x40, 0x2f, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0xf0, 0x80, 0x0c, 0x0c, \ + 0xff, 0xe8, 0x23, 0xc8, \ + 0xf0, 0x80, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x02, 0x06, 0x00, 0x6b, \ + 0x46, 0x38, 0xe2, 0x80, \ + 0x51, 0xb8, 0xe8, 0x28, \ + 0x8c, 0x0b, 0x6f, 0xfc, \ + 0x45, 0xf6, 0x01, 0x11, \ + 0x0c, 0x0b, 0xe0, 0x0c, \ + 0x53, 0x58, 0x8c, 0x3b, \ + 0x5b, 0x78, 0xef, 0xfc, \ + 0x8c, 0x0b, 0x6f, 0xfc, \ + 0x8c, 0x1b, 0x0c, 0x0b, \ + 0xef, 0xfc, 0x78, 0xa8, \ + 0x0c, 0x0c, 0x8c, 0x0b, \ + 0x1c, 0x78, 0xf2, 0x00, \ + 0xc1, 0xc3, 0xff, 0xfc, \ + 0xfa, 0x04, 0x04, 0x4c, \ + 0xb9, 0x11, 0x00, 0x6b, \ + 0x68, 0x28, 0x44, 0x68, \ + 0xe4, 0x10, 0x0e, 0x60, \ + 0x81, 0x21, 0x29, 0x11, \ + 0x8e, 0x3b, 0x0c, 0x0b, \ + 0xef, 0xfc, 0x51, 0xa8, \ + 0x64, 0x20, 0x06, 0x60, \ + 0x09, 0x21, 0x85, 0x11, \ + 0x51, 0x38, 0x8c, 0x0b, \ + 0x59, 0x58, 0xef, 0xfc, \ + 0x8c, 0x0b, 0x6f, 0xfc, \ + 0x4f, 0x86, 0x58, 0x14, \ + 0x9c, 0x04, 0xe1, 0x04, \ + 0x61, 0xc0, 0x04, 0x14, \ + 0x06, 0x14, 0xd1, 0x17, \ + 0x04, 0x24, 0x7a, 0x80, \ + 0x1f, 0x29, 0xfa, 0x80, \ + 0x6c, 0x78, 0xa1, 0x39, \ + 0x89, 0x11, 0x68, 0x00, \ + 0x02, 0x46, 0x1c, 0x04, \ + 0x80, 0x08, 0xe1, 0x14, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x80, 0xf5, 0x83, 0x0b, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x75, 0xe0, 0x70, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x00, 0x60, 0x40, 0x2f, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0xa7, 0x21, 0x05, 0x11, \ + 0xef, 0xe8, 0x5d, 0xa8, \ + 0x0f, 0x86, 0x5a, 0x14, \ + 0x9c, 0x04, 0xe1, 0x44, \ + 0x61, 0xc0, 0x04, 0x14, \ + 0x06, 0x24, 0xf1, 0x17, \ + 0x44, 0x10, 0x7a, 0x80, \ + 0x46, 0x30, 0xf8, 0x7c, \ + 0x8b, 0x11, 0x78, 0x80, \ + 0xe8, 0x00, 0x69, 0xe8, \ + 0x03, 0x46, 0x1c, 0x04, \ + 0x80, 0x08, 0xe1, 0x48, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x80, 0xf5, 0x83, 0x0b, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x75, 0xe0, 0x74, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x00, 0x60, 0x40, 0x2f, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0xa7, 0x21, 0x05, 0x11, \ + 0xef, 0xe8, 0x5b, 0x28, \ + 0x0f, 0xc6, 0x5a, 0x14, \ + 0x9c, 0x04, 0xe1, 0x44, \ + 0x61, 0xa0, 0x04, 0x14, \ + 0xfa, 0x80, 0x02, 0x14, \ + 0x7a, 0x80, 0x04, 0x24, \ + 0xf8, 0x80, 0x46, 0x10, \ + 0x8b, 0x11, 0x3f, 0x29, \ + 0xe8, 0x00, 0x67, 0x68, \ + 0x03, 0x86, 0x1c, 0x04, \ + 0x80, 0x08, 0xe1, 0x48, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x80, 0xf5, 0x83, 0x0b, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x75, 0xe0, 0x78, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x00, 0x60, 0x40, 0x2f, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0xa7, 0x21, 0x05, 0x11, \ + 0xef, 0xe8, 0x58, 0xa8, \ + 0x60, 0x10, 0x0c, 0x45, \ + 0xe1, 0x44, 0x43, 0x06, \ + 0x04, 0x44, 0x84, 0x04, \ + 0x00, 0x38, 0xe1, 0xe0, \ + 0xfb, 0xfc, 0x44, 0x20, \ + 0x80, 0xd5, 0xc4, 0x3d, \ + 0x04, 0x44, 0xe1, 0x47, \ + 0xc5, 0x3d, 0xfa, 0x80, \ + 0x80, 0x26, 0x80, 0x65, \ + 0x00, 0x08, 0x84, 0x04, \ + 0xe1, 0x48, 0x02, 0x06, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x80, 0xf5, 0x83, 0x0b, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x75, 0xe0, 0x7c, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x00, 0x60, 0x40, 0x2f, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0xa7, 0x21, 0x05, 0x11, \ + 0xef, 0xe8, 0x56, 0x08, \ + 0xe0, 0x10, 0x0c, 0x05, \ + 0xe0, 0xb8, 0x40, 0x0a, \ + 0x42, 0xc6, 0x88, 0x25, \ + 0xd5, 0x07, 0x61, 0x44, \ + 0x86, 0x09, 0x04, 0x24, \ + 0x04, 0x04, 0x88, 0x06, \ + 0xe1, 0x48, 0x02, 0x46, \ + 0x84, 0x14, 0x80, 0x08, \ + 0xcb, 0x17, 0x84, 0x18, \ + 0x83, 0x0b, 0x86, 0x19, \ + 0x0c, 0x05, 0x80, 0xf5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0x80, 0x00, 0x0a, \ + 0x0c, 0x39, 0x82, 0x75, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x66, 0x51, 0x00, 0x60, \ + 0x05, 0x11, 0xc0, 0x54, \ + 0x53, 0xb8, 0xa7, 0x21, \ + 0x0c, 0x05, 0xef, 0xe8, \ + 0x40, 0x0a, 0xe0, 0x10, \ + 0x88, 0x25, 0xe0, 0xb4, \ + 0x61, 0x44, 0x42, 0x86, \ + 0x04, 0x24, 0xd1, 0x07, \ + 0x88, 0x06, 0x86, 0x09, \ + 0x02, 0x46, 0x04, 0x04, \ + 0x80, 0x08, 0xe1, 0x48, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x86, 0x19, 0xc7, 0x17, \ + 0x80, 0xf5, 0x83, 0x0b, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x75, 0xe0, 0x84, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x00, 0x60, 0x40, 0x2f, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0xa7, 0x21, 0x05, 0x11, \ + 0xef, 0xe8, 0x51, 0x68, \ + 0xe0, 0x10, 0x0c, 0x05, \ + 0xe0, 0xb0, 0x40, 0x0a, \ + 0x42, 0x46, 0x86, 0xe5, \ + 0x8e, 0x09, 0x61, 0x44, \ + 0x88, 0x06, 0x84, 0x24, \ + 0x02, 0x46, 0x04, 0x04, \ + 0x80, 0x08, 0xe1, 0x48, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x83, 0x0b, 0x8e, 0x19, \ + 0x0c, 0x05, 0x80, 0xf5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0x88, 0x00, 0x0a, \ + 0x0c, 0x39, 0x82, 0x75, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x66, 0x51, 0x00, 0x60, \ + 0x05, 0x11, 0xc0, 0x54, \ + 0x4f, 0x38, 0xa7, 0x21, \ + 0x0c, 0x05, 0xef, 0xe8, \ + 0x40, 0x0a, 0xe0, 0x10, \ + 0x86, 0xe5, 0xe0, 0x94, \ + 0x61, 0x08, 0x03, 0x86, \ + 0x04, 0x24, 0xc7, 0x07, \ + 0x88, 0x06, 0x86, 0x09, \ + 0x02, 0x86, 0x04, 0x04, \ + 0x80, 0x08, 0xe1, 0x14, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x80, 0xf5, 0x83, 0x0b, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x75, 0xe0, 0x8c, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x00, 0x60, 0x40, 0x2f, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0xa7, 0x21, 0x05, 0x11, \ + 0xef, 0xe8, 0x4d, 0x08, \ + 0xe0, 0x14, 0x0c, 0x05, \ + 0xe0, 0xf8, 0x40, 0x0a, \ + 0x43, 0x46, 0x86, 0xe5, \ + 0xf1, 0x07, 0x61, 0x44, \ + 0x9e, 0x09, 0x04, 0x24, \ + 0x04, 0x04, 0x88, 0x06, \ + 0xe1, 0x48, 0x02, 0x86, \ + 0x84, 0x14, 0x80, 0x08, \ + 0x83, 0x0b, 0x84, 0x18, \ + 0x0c, 0x05, 0x80, 0xf5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0x90, 0x00, 0x0a, \ + 0x0c, 0x39, 0x82, 0x75, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x66, 0x51, 0x00, 0x60, \ + 0x05, 0x11, 0xc0, 0x54, \ + 0x4a, 0xd8, 0xa7, 0x21, \ + 0x0c, 0x05, 0xef, 0xe8, \ + 0x40, 0x0a, 0xe0, 0x14, \ + 0x86, 0xe5, 0xe0, 0xf4, \ + 0x61, 0x44, 0x43, 0x86, \ + 0x04, 0x24, 0xe9, 0x07, \ + 0x88, 0x06, 0x9e, 0x09, \ + 0x02, 0xc6, 0x04, 0x04, \ + 0x80, 0x08, 0xe1, 0x48, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x80, 0xf5, 0x83, 0x0b, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x75, 0xe0, 0x94, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x00, 0x60, 0x40, 0x2f, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0xa7, 0x21, 0x05, 0x11, \ + 0xef, 0xe8, 0x48, 0xa8, \ + 0xe0, 0x14, 0x0c, 0x05, \ + 0xe0, 0xf0, 0x40, 0x0a, \ + 0x43, 0xc6, 0x86, 0xe5, \ + 0xe1, 0x07, 0x61, 0x44, \ + 0x9e, 0x09, 0x04, 0x24, \ + 0x04, 0x04, 0x88, 0x06, \ + 0xe1, 0x48, 0x03, 0x06, \ + 0x84, 0x14, 0x80, 0x08, \ + 0x83, 0x0b, 0x84, 0x18, \ + 0x0c, 0x05, 0x80, 0xf5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0x98, 0x00, 0x0a, \ + 0x0c, 0x39, 0x82, 0x75, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x66, 0x51, 0x00, 0x60, \ + 0x05, 0x11, 0xc0, 0x54, \ + 0x46, 0x78, 0xa7, 0x21, \ + 0x0c, 0x05, 0xef, 0xe8, \ + 0x40, 0x0a, 0xe0, 0x14, \ + 0x88, 0xe5, 0xe0, 0xb4, \ + 0x61, 0x00, 0x43, 0x06, \ + 0x04, 0x24, 0xc9, 0x07, \ + 0x88, 0x38, 0x84, 0x09, \ + 0xf3, 0xf4, 0x46, 0x30, \ + 0x88, 0x36, 0xa0, 0x3f, \ + 0x02, 0xc6, 0x04, 0x04, \ + 0x80, 0x08, 0xe1, 0x14, \ + 0xc3, 0x07, 0x84, 0x14, \ + 0xcd, 0x17, 0x84, 0x18, \ + 0x42, 0x4a, 0xc0, 0x1f, \ + 0x80, 0xe5, 0xe0, 0x80, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x75, 0xe0, 0x9c, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x00, 0x60, 0x40, 0x2f, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0xa7, 0x21, 0x05, 0x11, \ + 0xef, 0xe8, 0x43, 0xc8, \ + 0xe0, 0x14, 0x0c, 0x05, \ + 0xe0, 0xb0, 0x40, 0x0a, \ + 0x43, 0x06, 0x88, 0xc5, \ + 0xc9, 0x07, 0x61, 0x00, \ + 0x82, 0x09, 0x04, 0x24, \ + 0x46, 0x30, 0x88, 0x38, \ + 0xa0, 0x3f, 0xf3, 0xf8, \ + 0x04, 0x04, 0x88, 0x36, \ + 0xe1, 0x14, 0x02, 0xc6, \ + 0x84, 0x14, 0x80, 0x08, \ + 0xcb, 0x17, 0x84, 0x18, \ + 0x42, 0x4a, 0xc0, 0x1f, \ + 0x80, 0xe5, 0xe0, 0x80, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x75, 0xe0, 0xa0, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x00, 0x60, 0x40, 0x2f, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0xa7, 0x21, 0x05, 0x11, \ + 0xef, 0xe8, 0x41, 0x28, \ + 0xe0, 0x14, 0x0c, 0x05, \ + 0xe0, 0xac, 0x40, 0x0a, \ + 0x42, 0xc6, 0x88, 0xe5, \ + 0xc5, 0x07, 0x61, 0x00, \ + 0x84, 0x09, 0x04, 0x24, \ + 0x46, 0x30, 0x88, 0x38, \ + 0xa0, 0x3f, 0xf3, 0xf4, \ + 0x04, 0x04, 0x88, 0x36, \ + 0xe1, 0x14, 0x02, 0xc6, \ + 0x84, 0x14, 0x80, 0x08, \ + 0x84, 0x18, 0xc3, 0x07, \ + 0xc0, 0x1f, 0xc7, 0x17, \ + 0xe0, 0x80, 0x42, 0x4a, \ + 0x0c, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0xa4, 0x00, 0x0a, \ + 0x0c, 0x39, 0x82, 0x75, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x66, 0x51, 0x00, 0x60, \ + 0x05, 0x11, 0xc0, 0x54, \ + 0x7e, 0x78, 0xa7, 0x21, \ + 0x0c, 0x05, 0xef, 0xe4, \ + 0x40, 0x0a, 0xe0, 0x14, \ + 0x88, 0xc5, 0xe0, 0xa8, \ + 0x61, 0x00, 0x42, 0xc6, \ + 0x04, 0x24, 0xc5, 0x07, \ + 0x88, 0x38, 0x82, 0x09, \ + 0xf3, 0xf8, 0x46, 0x30, \ + 0x88, 0x36, 0xa0, 0x3f, \ + 0x02, 0xc6, 0x04, 0x04, \ + 0x80, 0x08, 0xe1, 0x14, \ + 0x84, 0x18, 0x84, 0x14, \ + 0xc0, 0x1f, 0xc5, 0x17, \ + 0xe0, 0x80, 0x42, 0x4a, \ + 0x0c, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0xa8, 0x00, 0x0a, \ + 0x0c, 0x39, 0x82, 0x75, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x66, 0x51, 0x00, 0x60, \ + 0x05, 0x11, 0xc0, 0x54, \ + 0x7b, 0xd8, 0xa7, 0x21, \ + 0x0c, 0x05, 0xef, 0xe4, \ + 0x40, 0x0a, 0xe0, 0x14, \ + 0x88, 0x85, 0xe0, 0xa4, \ + 0x61, 0x00, 0x42, 0x86, \ + 0x84, 0x24, 0x84, 0x09, \ + 0x46, 0x30, 0x88, 0x38, \ + 0xa0, 0x3f, 0xf3, 0xf4, \ + 0x04, 0x04, 0x88, 0x36, \ + 0xe1, 0x14, 0x02, 0xc6, \ + 0x84, 0x14, 0x80, 0x08, \ + 0xc0, 0x1f, 0x84, 0x18, \ + 0xe0, 0x84, 0x42, 0x4a, \ + 0x0c, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0xac, 0x00, 0x0a, \ + 0x0c, 0x39, 0x82, 0x75, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x66, 0x51, 0x00, 0x60, \ + 0x05, 0x11, 0xc0, 0x54, \ + 0x79, 0x58, 0xa7, 0x21, \ + 0x0c, 0x05, 0xef, 0xe4, \ + 0x40, 0x0a, 0xe0, 0x14, \ + 0x88, 0x85, 0xe0, 0xa0, \ + 0x61, 0x00, 0x42, 0x86, \ + 0x84, 0x24, 0x82, 0x09, \ + 0x46, 0x30, 0x88, 0x38, \ + 0xa0, 0x3f, 0xf3, 0xf8, \ + 0x04, 0x04, 0x88, 0x36, \ + 0xe1, 0x14, 0x02, 0xc6, \ + 0x84, 0x14, 0x80, 0x08, \ + 0xc0, 0x1f, 0x84, 0x18, \ + 0xe0, 0x80, 0x42, 0x4a, \ + 0x0c, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0xb0, 0x00, 0x0a, \ + 0x0c, 0x39, 0x82, 0x75, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x66, 0x51, 0x00, 0x60, \ + 0x05, 0x11, 0xc0, 0x54, \ + 0x76, 0xd8, 0xa7, 0x21, \ + 0x0c, 0x05, 0xef, 0xe4, \ + 0x40, 0x0a, 0xe0, 0x18, \ + 0x88, 0xc5, 0xe0, 0xf4, \ + 0x61, 0x00, 0x43, 0xc6, \ + 0x04, 0x14, 0xeb, 0x07, \ + 0x46, 0x02, 0x87, 0x31, \ + 0x86, 0x09, 0x78, 0x8c, \ + 0x44, 0x20, 0x84, 0x28, \ + 0xa6, 0x2f, 0xf3, 0xf0, \ + 0x03, 0x06, 0x04, 0x26, \ + 0x84, 0x14, 0xe1, 0x14, \ + 0xcb, 0x17, 0x84, 0x18, \ + 0x81, 0x1b, 0x86, 0x19, \ + 0x0c, 0x05, 0x80, 0xf5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0xb4, 0x00, 0x0a, \ + 0x0c, 0x39, 0x82, 0x75, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x66, 0x51, 0x00, 0x60, \ + 0x05, 0x11, 0xc0, 0x54, \ + 0x74, 0x38, 0xa7, 0x21, \ + 0x0c, 0x05, 0xef, 0xe4, \ + 0x9a, 0x14, 0xe0, 0x18, \ + 0x04, 0x14, 0xe0, 0x07, \ + 0x40, 0x00, 0x7b, 0x80, \ + 0xc3, 0x00, 0x70, 0x00, \ + 0xe1, 0x17, 0x24, 0x0f, \ + 0x61, 0x4d, 0x4c, 0x15, \ + 0x4c, 0x05, 0xc0, 0x0a, \ + 0xc0, 0x0a, 0x61, 0x51, \ + 0xf2, 0x04, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x69, 0x84, 0x08, 0x24, \ + 0xe9, 0x84, 0x0a, 0x34, \ + 0x18, 0x44, 0x00, 0x48, \ + 0xb8, 0x5f, 0xe0, 0x84, \ + 0x04, 0x25, 0xa7, 0x5b, \ + 0xe9, 0x84, 0x02, 0x44, \ + 0x86, 0x25, 0xa3, 0x5b, \ + 0x86, 0xa5, 0xa5, 0x5b, \ + 0x5c, 0x08, 0xa1, 0x5b, \ + 0xc4, 0x4d, 0xda, 0x00, \ + 0xa1, 0x1b, 0x80, 0x65, \ + 0xa1, 0x1b, 0x84, 0x85, \ + 0x81, 0x85, 0x86, 0x35, \ + 0x82, 0xe5, 0xa1, 0x1b, \ + 0x5c, 0x08, 0xa3, 0x1b, \ + 0xc4, 0x4d, 0xda, 0x00, \ + 0xc7, 0x4d, 0x82, 0x55, \ + 0x9a, 0x00, 0x5c, 0x08, \ + 0xdd, 0x93, 0x80, 0x36, \ + 0x82, 0x05, 0xc4, 0x4d, \ + 0x80, 0xd5, 0xa1, 0x1b, \ + 0xdd, 0x93, 0x80, 0x26, \ + 0x80, 0x16, 0x81, 0x11, \ + 0xc7, 0x4d, 0xdd, 0x93, \ + 0xa3, 0x1b, 0x80, 0x65, \ + 0xdd, 0x93, 0xfc, 0x45, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x98, 0x7b, 0x00, 0x6b, \ + 0xef, 0xf8, 0x5e, 0x08, \ + 0x64, 0xb1, 0x06, 0x60, \ + 0x81, 0x81, 0x40, 0xbf, \ + 0x60, 0x0c, 0x43, 0x46, \ + 0x5f, 0x98, 0x81, 0x21, \ + 0x8c, 0x0b, 0x6f, 0xf8, \ + 0x6f, 0x2d, 0x0c, 0x05, \ + 0x43, 0xfe, 0x40, 0x0b, \ + 0xc0, 0x3f, 0x7f, 0xfd, \ + 0x41, 0x4a, 0x9c, 0x8e, \ + 0x00, 0x0c, 0xe4, 0x04, \ + 0x9e, 0x0e, 0xe2, 0x00, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x15, 0x61, 0x51, \ + 0x40, 0x06, 0x29, 0x0b, \ + 0x4c, 0x01, 0xe0, 0x08, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x0c, 0x09, 0x86, 0xb5, \ + 0xc0, 0x15, 0x61, 0x55, \ + 0x86, 0x15, 0xa9, 0x0b, \ + 0x66, 0xb1, 0x00, 0x60, \ + 0x02, 0x60, 0x40, 0xac, \ + 0xc0, 0xad, 0x64, 0xe1, \ + 0x6f, 0x84, 0x62, 0x08, \ + 0x00, 0x60, 0x99, 0x21, \ + 0x40, 0xac, 0x66, 0xe1, \ + 0x65, 0x11, 0x02, 0x60, \ + 0x61, 0x78, 0xc0, 0xad, \ + 0x04, 0x06, 0x6f, 0x84, \ + 0x03, 0xa5, 0xe0, 0x08, \ + 0x66, 0x71, 0x00, 0x60, \ + 0x00, 0x60, 0xc0, 0xa3, \ + 0x40, 0xac, 0x66, 0xb1, \ + 0x67, 0x11, 0x02, 0x60, \ + 0x60, 0x98, 0xc0, 0xad, \ + 0x99, 0x21, 0x6f, 0x84, \ + 0x66, 0xe1, 0x00, 0x60, \ + 0x02, 0x60, 0x40, 0xac, \ + 0xc0, 0xad, 0x67, 0x41, \ + 0x6f, 0x84, 0x60, 0x08, \ + 0xe0, 0x08, 0x04, 0x06, \ + 0x65, 0x61, 0x00, 0x60, \ + 0x44, 0x16, 0xc0, 0xa1, \ + 0x02, 0x60, 0x60, 0x08, \ + 0xc0, 0xae, 0x65, 0x40, \ + 0x6d, 0x09, 0x4c, 0x21, \ + 0x60, 0x38, 0xc0, 0x2f, \ + 0x0c, 0x15, 0xef, 0x9c, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0xdc, 0x02, 0x0a, \ + 0x9f, 0xfc, 0x41, 0xfe, \ + 0x82, 0xe5, 0xa1, 0x0b, \ + 0x66, 0x51, 0x08, 0x60, \ + 0x04, 0x04, 0x40, 0x54, \ + 0x0c, 0x39, 0xfb, 0x00, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x88, 0x0b, 0x89, 0x11, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0xe4, 0x27, 0xa8, \ + 0x61, 0x4c, 0x01, 0x06, \ + 0xe0, 0x0c, 0x0b, 0x46, \ + 0x06, 0x60, 0x00, 0x04, \ + 0xc0, 0x57, 0x66, 0xd1, \ + 0x6f, 0x0d, 0x0c, 0x29, \ + 0x08, 0x60, 0x40, 0x15, \ + 0xc0, 0xb2, 0x67, 0x81, \ + 0x6f, 0x09, 0x0c, 0x19, \ + 0x4c, 0x51, 0xc0, 0x15, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0xc4, 0x28, 0x48, \ + 0xfa, 0x84, 0x04, 0x0c, \ + 0x61, 0x94, 0x0e, 0x46, \ + 0x1c, 0x6e, 0x80, 0x6b, \ + 0xe1, 0x00, 0x02, 0x06, \ + 0xe8, 0x24, 0x48, 0xa8, \ + 0x8c, 0x0b, 0x1c, 0x14, \ + 0xe8, 0x00, 0x40, 0x88, \ + 0x8c, 0x0b, 0x1c, 0x14, \ + 0xf2, 0x84, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0x87, 0x65, \ + 0xfa, 0x84, 0x04, 0x0c, \ + 0x80, 0x7b, 0x02, 0x6b, \ + 0x60, 0x08, 0x40, 0x26, \ + 0x65, 0x50, 0x02, 0x70, \ + 0x4e, 0x01, 0xc0, 0x8c, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x64, 0x61, 0x00, 0x60, \ + 0x09, 0x21, 0xc0, 0x11, \ + 0x6f, 0xb8, 0x81, 0x31, \ + 0x00, 0x60, 0xe8, 0x10, \ + 0x40, 0x11, 0x64, 0x71, \ + 0x65, 0x01, 0x02, 0x70, \ + 0x29, 0x21, 0xc0, 0x8c, \ + 0x6f, 0x18, 0x81, 0x31, \ + 0x00, 0x60, 0xe8, 0x10, \ + 0x40, 0x11, 0x64, 0xc1, \ + 0x65, 0x61, 0x02, 0x70, \ + 0x21, 0x21, 0xc0, 0x8c, \ + 0x6e, 0x78, 0x81, 0x31, \ + 0x00, 0x60, 0xe8, 0x10, \ + 0x40, 0x11, 0x65, 0x01, \ + 0x65, 0xa1, 0x02, 0x70, \ + 0x45, 0x06, 0xc0, 0x8c, \ + 0x89, 0x31, 0x60, 0x18, \ + 0xf2, 0x84, 0x0c, 0x0c, \ + 0xe0, 0x10, 0x2d, 0xa8, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0xf0, 0x1e, 0xf0, \ + 0x82, 0x7b, 0x00, 0x6b, \ + 0x61, 0x51, 0x0c, 0x09, \ + 0xa9, 0x0b, 0xc0, 0x15, \ + 0x0c, 0x09, 0x82, 0x95, \ + 0xc0, 0x15, 0x61, 0x55, \ + 0x0a, 0x60, 0xa9, 0x0b, \ + 0x00, 0xa3, 0x06, 0x71, \ + 0x06, 0x91, 0x06, 0x60, \ + 0x80, 0xe5, 0x80, 0xa7, \ + 0x65, 0x61, 0x0a, 0x60, \ + 0x06, 0x60, 0x40, 0xa1, \ + 0xc0, 0xa9, 0x65, 0xa1, \ + 0x6e, 0xc5, 0x0c, 0x15, \ + 0xa1, 0x1b, 0xc0, 0x0b, \ + 0x10, 0x60, 0x14, 0xd5, \ + 0xc0, 0xbf, 0x05, 0x09, \ + 0x60, 0x08, 0x40, 0x46, \ + 0xe1, 0x4c, 0x03, 0x06, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x08, 0x60, 0x40, 0x2f, \ + 0xc0, 0xbe, 0x67, 0x11, \ + 0x16, 0x70, 0x04, 0x04, \ + 0xc0, 0x10, 0x67, 0xe1, \ + 0x66, 0x91, 0x14, 0x60, \ + 0x12, 0x70, 0x40, 0xbe, \ + 0xc0, 0x10, 0x64, 0x01, \ + 0x02, 0x60, 0x0f, 0x4e, \ + 0xc0, 0x57, 0x66, 0xd1, \ + 0x00, 0x60, 0x19, 0x0e, \ + 0xc0, 0xac, 0x66, 0xb1, \ + 0x00, 0x70, 0x1b, 0x0e, \ + 0xc0, 0x13, 0x66, 0x01, \ + 0x04, 0x70, 0x0d, 0xbe, \ + 0xc0, 0x11, 0x64, 0x61, \ + 0x80, 0xbb, 0x15, 0xbe, \ + 0x10, 0x60, 0x1d, 0x4e, \ + 0xc0, 0x6f, 0x67, 0x01, \ + 0x18, 0x60, 0x0b, 0x9e, \ + 0xc0, 0xbd, 0x66, 0xb1, \ + 0x08, 0x60, 0x09, 0xbe, \ + 0xc0, 0xae, 0x65, 0x41, \ + 0x13, 0xae, 0xfe, 0xae, \ + 0x7c, 0xce, 0x86, 0xab, \ + 0x3e, 0x8e, 0x8a, 0x3b, \ + 0x65, 0x09, 0x10, 0x60, \ + 0x3c, 0x4e, 0xc0, 0xbf, \ + 0x99, 0x04, 0x94, 0x4b, \ + 0x97, 0x9e, 0x9b, 0x54, \ + 0x9b, 0x2e, 0x99, 0x1e, \ + 0xef, 0xc4, 0x41, 0xa8, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x80, 0xb5, 0xe1, 0x7c, \ + 0x9d, 0x04, 0xfb, 0x11, \ + 0x81, 0x55, 0x80, 0x1e, \ + 0x88, 0x14, 0x9d, 0x24, \ + 0x86, 0x25, 0xa1, 0x1b, \ + 0x6e, 0xcd, 0x0c, 0x05, \ + 0x04, 0x14, 0x40, 0x0b, \ + 0x0c, 0x45, 0xfb, 0x00, \ + 0x40, 0x0b, 0x6e, 0xd1, \ + 0xa0, 0x38, 0x89, 0x11, \ + 0x69, 0x35, 0x4c, 0x03, \ + 0x00, 0x60, 0x40, 0x0a, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0x69, 0x39, 0x4c, 0x43, \ + 0xf8, 0xf1, 0x40, 0x0a, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xff, 0xe4, 0x18, 0xc8, \ + 0x60, 0x08, 0x40, 0x56, \ + 0x64, 0xb0, 0x02, 0x70, \ + 0x4c, 0x01, 0xc0, 0x14, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x66, 0x31, 0x12, 0x70, \ + 0x09, 0x2e, 0xc0, 0x1f, \ + 0xe1, 0x4c, 0x01, 0x06, \ + 0x0a, 0x60, 0x7e, 0x9e, \ + 0xc0, 0xab, 0x64, 0xb1, \ + 0x94, 0x4b, 0x7c, 0x1e, \ + 0x80, 0x04, 0x9b, 0x14, \ + 0x3e, 0x1e, 0x95, 0x34, \ + 0x97, 0x74, 0x8e, 0x1b, \ + 0x43, 0x18, 0xbc, 0xbe, \ + 0x8e, 0x2b, 0x6f, 0xc0, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x6e, 0xc5, 0xe1, 0x80, \ + 0x9f, 0xfc, 0x43, 0xfe, \ + 0x01, 0x06, 0x1d, 0x34, \ + 0x8c, 0x14, 0xe1, 0x4c, \ + 0xee, 0xb5, 0xa1, 0x1b, \ + 0x42, 0x66, 0x00, 0x04, \ + 0x4c, 0x11, 0xe0, 0x08, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x64, 0x71, 0x02, 0x60, \ + 0x7c, 0x3e, 0xc0, 0xc6, \ + 0x67, 0x81, 0x0a, 0x60, \ + 0x3e, 0x1e, 0xc0, 0xb2, \ + 0x19, 0x14, 0x92, 0x2b, \ + 0x93, 0x44, 0x8e, 0x3b, \ + 0x9b, 0x14, 0xbc, 0x1e, \ + 0xef, 0xc0, 0x61, 0x68, \ + 0x6e, 0xc5, 0x0c, 0x15, \ + 0xa1, 0x1b, 0xc0, 0x0b, \ + 0xf8, 0xf1, 0xe8, 0x55, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x4c, \ + 0x00, 0x46, 0x00, 0x7b, \ + 0x00, 0x7e, 0xe1, 0x94, \ + 0xe1, 0x00, 0x02, 0x06, \ + 0x68, 0x20, 0x76, 0x88, \ + 0x01, 0x81, 0x8e, 0x0b, \ + 0x03, 0xa1, 0x97, 0x91, \ + 0xe1, 0x34, 0x57, 0x06, \ + 0x0e, 0x1b, 0x89, 0x45, \ + 0x40, 0x17, 0x6e, 0x91, \ + 0xe8, 0xa0, 0x00, 0xa4, \ + 0xa1, 0x1b, 0x80, 0x1f, \ + 0x42, 0x86, 0x8e, 0x45, \ + 0x84, 0x14, 0xe1, 0x34, \ + 0xa1, 0x2b, 0x84, 0x28, \ + 0x84, 0x18, 0x80, 0xa5, \ + 0xa1, 0x1b, 0x80, 0x1f, \ + 0x4c, 0x72, 0x8c, 0x05, \ + 0x0c, 0x09, 0xf0, 0xa0, \ + 0xc0, 0x1c, 0x64, 0x81, \ + 0x63, 0xfc, 0x00, 0x0a, \ + 0xe1, 0x94, 0x00, 0x46, \ + 0x80, 0xe5, 0x80, 0x14, \ + 0x7a, 0x80, 0x04, 0x84, \ + 0x42, 0x98, 0x8e, 0x0b, \ + 0x81, 0x45, 0xe8, 0x00, \ + 0x6f, 0xfc, 0x6b, 0xe8, \ + 0x0c, 0x19, 0x8e, 0x0b, \ + 0xc0, 0x1c, 0x64, 0x81, \ + 0xe3, 0xfc, 0x02, 0x0a, \ + 0x0c, 0x59, 0x0e, 0x05, \ + 0x80, 0x85, 0x80, 0x08, \ + 0x6f, 0xe4, 0x42, 0x68, \ + 0x2c, 0x14, 0x8e, 0x0b, \ + 0xec, 0x04, 0x04, 0x90, \ + 0x6e, 0xa5, 0x4e, 0x05, \ + 0x00, 0x46, 0x40, 0x0b, \ + 0x84, 0x2c, 0xe1, 0x94, \ + 0x8e, 0x0b, 0x00, 0x14, \ + 0xef, 0xfc, 0x6d, 0x88, \ + 0x80, 0x9c, 0xac, 0x04, \ + 0x84, 0x91, 0x02, 0x81, \ + 0xe0, 0xac, 0x12, 0x0a, \ + 0x0c, 0x4c, 0xee, 0x75, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x80, 0x8b, 0x02, 0x7b, \ + 0x60, 0x08, 0x40, 0x36, \ + 0x50, 0x01, 0x84, 0x6b, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x64, 0x61, 0x00, 0x70, \ + 0x02, 0x80, 0xc0, 0x11, \ + 0x40, 0x70, 0x65, 0x71, \ + 0x59, 0x38, 0x89, 0x21, \ + 0x81, 0x31, 0x68, 0x10, \ + 0x64, 0x71, 0x00, 0x70, \ + 0x02, 0x80, 0x40, 0x11, \ + 0xc0, 0x70, 0x65, 0x21, \ + 0x81, 0x31, 0x29, 0x21, \ + 0xe8, 0x10, 0x58, 0x88, \ + 0x60, 0x88, 0x00, 0x64, \ + 0x65, 0x80, 0x02, 0x80, \ + 0x02, 0x25, 0xc0, 0x70, \ + 0x82, 0x03, 0x7c, 0x00, \ + 0x89, 0x31, 0x0c, 0x13, \ + 0x61, 0x31, 0x4e, 0x25, \ + 0x00, 0x25, 0xc0, 0x01, \ + 0x4e, 0x25, 0xe0, 0x40, \ + 0x40, 0x01, 0x61, 0x35, \ + 0xe0, 0x18, 0x45, 0x06, \ + 0xe0, 0x80, 0x00, 0x05, \ + 0x61, 0x39, 0x4e, 0x05, \ + 0x02, 0x09, 0xc0, 0x01, \ + 0x4e, 0x01, 0xe3, 0x00, \ + 0xc0, 0x04, 0x64, 0xf1, \ + 0x63, 0x40, 0x02, 0x09, \ + 0xe0, 0x18, 0x43, 0x06, \ + 0xe6, 0x86, 0x42, 0x6a, \ + 0x64, 0xf5, 0x4e, 0x01, \ + 0x82, 0x83, 0x40, 0x04, \ + 0x65, 0x01, 0x00, 0x70, \ + 0x02, 0x80, 0x40, 0x11, \ + 0xc0, 0x71, 0x65, 0x01, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0xe0, 0x10, 0x15, 0x68, \ + 0xfa, 0xfc, 0x04, 0x0c, \ + 0x17, 0x06, 0x00, 0x6b, \ + 0x54, 0xc6, 0xe1, 0x4c, \ + 0x00, 0x36, 0x61, 0x98, \ + 0x10, 0x60, 0xe0, 0x0c, \ + 0x40, 0x54, 0x65, 0x51, \ + 0x64, 0x51, 0x0e, 0x60, \ + 0x4c, 0x01, 0xc0, 0xbf, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x66, 0xd1, 0x12, 0x60, \ + 0x2c, 0x24, 0xc0, 0x57, \ + 0x28, 0x6e, 0x92, 0x0b, \ + 0x0c, 0x3b, 0x90, 0x1b, \ + 0x7c, 0xd8, 0x8e, 0x4b, \ + 0x0c, 0x15, 0xef, 0xc8, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe1, 0x84, 0x02, 0x0a, \ + 0x9f, 0xfc, 0x41, 0xfe, \ + 0x88, 0xd5, 0xa1, 0x0b, \ + 0x00, 0x46, 0x2c, 0x24, \ + 0x4c, 0x01, 0xe0, 0x0c, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x28, 0x34, 0x92, 0x0b, \ + 0x7d, 0xd8, 0x90, 0x1b, \ + 0x8e, 0x4b, 0x6f, 0xc8, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x80, 0x1b, 0x40, 0x0c, \ + 0x61, 0x88, 0x00, 0x0a, \ + 0x84, 0x45, 0xff, 0x01, \ + 0x82, 0x0b, 0x21, 0x1b, \ + 0x2c, 0x24, 0x82, 0xf5, \ + 0xe0, 0x0c, 0x00, 0x56, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x92, 0x0b, 0x40, 0x2f, \ + 0x90, 0x1b, 0x28, 0x34, \ + 0x6f, 0xc8, 0x7a, 0x08, \ + 0xa1, 0x0b, 0x8e, 0x4b, \ + 0x08, 0x60, 0x82, 0xe5, \ + 0x40, 0x54, 0x66, 0x51, \ + 0xfb, 0x00, 0x04, 0x04, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x89, 0x11, 0x40, 0x2f, \ + 0x0c, 0x0c, 0x88, 0x0b, \ + 0x03, 0xd8, 0xf2, 0xfc, \ + 0x0c, 0x0c, 0xff, 0xe4, \ + 0xdd, 0x93, 0xf2, 0xfc, \ + 0xfa, 0x9c, 0x04, 0x0c, \ + 0x10, 0x46, 0x00, 0x6b, \ + 0x20, 0x6e, 0xe1, 0x94, \ + 0x67, 0x01, 0x12, 0x60, \ + 0x03, 0x15, 0xc0, 0x97, \ + 0x0c, 0x0b, 0x81, 0x71, \ + 0xc0, 0x18, 0x66, 0x8d, \ + 0x60, 0x80, 0x40, 0x4a, \ + 0xe0, 0x10, 0x00, 0x06, \ + 0x5c, 0x00, 0x40, 0x60, \ + 0xdc, 0x00, 0x42, 0x70, \ + 0xcf, 0xe0, 0x76, 0x28, \ + 0x82, 0x71, 0x65, 0x0e, \ + 0xe2, 0x30, 0x0e, 0x0a, \ + 0x20, 0x14, 0xfa, 0xf5, \ + 0x0c, 0x0c, 0x8c, 0x0b, \ + 0x81, 0x15, 0xf2, 0x9c, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x00, 0x16, 0x00, 0x6b, \ + 0x4c, 0x01, 0xe0, 0x0c, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0xe1, 0x4c, 0x01, 0x06, \ + 0x64, 0x81, 0x0e, 0x60, \ + 0x18, 0x60, 0x40, 0x9c, \ + 0xc0, 0x8e, 0x64, 0x61, \ + 0x04, 0x60, 0x3e, 0x7e, \ + 0xc0, 0xbe, 0x67, 0x91, \ + 0x06, 0x60, 0x00, 0x04, \ + 0xc0, 0xa5, 0x67, 0x81, \ + 0x65, 0x61, 0x08, 0x60, \ + 0x0a, 0x60, 0x40, 0xac, \ + 0xc0, 0xb2, 0x67, 0x81, \ + 0x4b, 0x78, 0xbc, 0xce, \ + 0x0c, 0x15, 0xef, 0xc8, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe1, 0x8c, 0x02, 0x0a, \ + 0x9f, 0xfc, 0x41, 0xde, \ + 0x84, 0x05, 0xa1, 0x0b, \ + 0x66, 0x51, 0x08, 0x60, \ + 0x04, 0x04, 0x40, 0x54, \ + 0x0c, 0x39, 0xfb, 0x00, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x08, 0x0b, 0x89, 0x11, \ + 0x0c, 0x0c, 0x98, 0xf1, \ + 0x3d, 0xd8, 0xf2, 0x04, \ + 0x98, 0xf1, 0xff, 0xe0, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x7c, 0x04, 0x4c, \ + 0x47, 0xc6, 0x00, 0x6b, \ + 0x0c, 0x05, 0xe0, 0x04, \ + 0x40, 0x0a, 0x61, 0x51, \ + 0x66, 0x01, 0x04, 0x60, \ + 0x0c, 0x15, 0xc0, 0xbd, \ + 0xc0, 0x0b, 0x6d, 0x71, \ + 0x6d, 0x09, 0x4c, 0x31, \ + 0x6b, 0x38, 0xc0, 0x2f, \ + 0x0c, 0x05, 0xef, 0xb4, \ + 0x40, 0x0c, 0x65, 0x0d, \ + 0x10, 0x60, 0x80, 0x7b, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0xe0, 0xb8, 0x00, 0x0a, \ + 0x9f, 0xfc, 0x4f, 0xfe, \ + 0x0c, 0x39, 0xa1, 0x7b, \ + 0x40, 0x2f, 0x0d, 0x09, \ + 0xdb, 0x00, 0x04, 0x74, \ + 0x40, 0x00, 0x02, 0x06, \ + 0xdc, 0x00, 0x40, 0x80, \ + 0xcf, 0xe0, 0x7a, 0xe8, \ + 0x6d, 0x81, 0x0c, 0x15, \ + 0x00, 0xc6, 0x40, 0x0b, \ + 0x80, 0x04, 0xe1, 0x04, \ + 0x0c, 0x05, 0x80, 0x1e, \ + 0xc0, 0x0b, 0x6d, 0x81, \ + 0x61, 0x51, 0x0c, 0x15, \ + 0x6a, 0x58, 0xc0, 0x0a, \ + 0x12, 0x00, 0xef, 0xb4, \ + 0xc0, 0x80, 0x64, 0x01, \ + 0x6f, 0x78, 0xe1, 0x97, \ + 0x92, 0x0b, 0x6f, 0xcc, \ + 0x92, 0x1b, 0x0c, 0x0b, \ + 0xef, 0xcc, 0x77, 0xa8, \ + 0x92, 0x1b, 0x0c, 0x0b, \ + 0xef, 0xcc, 0x70, 0xc8, \ + 0x92, 0x1b, 0x0c, 0x0b, \ + 0xef, 0xcc, 0x73, 0x88, \ + 0x6f, 0xcc, 0x76, 0x28, \ + 0x0c, 0x1b, 0x8c, 0x0b, \ + 0x40, 0x18, 0x67, 0x71, \ + 0xe1, 0x04, 0x01, 0x06, \ + 0x83, 0x21, 0x00, 0x04, \ + 0x01, 0x46, 0x00, 0x1c, \ + 0x0c, 0x1b, 0xe1, 0x04, \ + 0xc0, 0x18, 0x67, 0x75, \ + 0x00, 0x1c, 0x80, 0x04, \ + 0xe1, 0x00, 0x41, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x00, 0x16, 0xc2, 0x19, \ + 0xe1, 0x00, 0x41, 0xc6, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x00, 0x16, 0xd0, 0x19, \ + 0x5b, 0x58, 0xa9, 0x11, \ + 0x8c, 0x0b, 0x68, 0x1c, \ + 0x6f, 0x01, 0x0c, 0x19, \ + 0x40, 0x06, 0x40, 0x30, \ + 0x00, 0x04, 0xe1, 0x10, \ + 0x00, 0x16, 0x89, 0x21, \ + 0xe1, 0x00, 0x41, 0x86, \ + 0x04, 0x26, 0x80, 0x14, \ + 0x80, 0x14, 0x8f, 0x21, \ + 0x02, 0x76, 0x04, 0x26, \ + 0x00, 0x04, 0xe0, 0x04, \ + 0x00, 0x16, 0x81, 0x21, \ + 0xe0, 0x3c, 0x42, 0x86, \ + 0x68, 0x1c, 0x59, 0xc8, \ + 0x15, 0x86, 0x8c, 0x0b, \ + 0x41, 0x46, 0xe1, 0x00, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0x81, 0xb5, 0x80, 0xb8, \ + 0xe8, 0x1c, 0x5e, 0x08, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x83, 0x11, 0x40, 0x2f, \ + 0x90, 0x0b, 0x03, 0x21, \ + 0xef, 0xe0, 0x74, 0x08, \ + 0x80, 0x08, 0xa8, 0x04, \ + 0xe0, 0x90, 0x40, 0x4a, \ + 0x56, 0x0a, 0x82, 0x65, \ + 0xfa, 0xe5, 0xe0, 0x8c, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0xfa, 0x25, 0xe0, 0xbc, \ + 0x00, 0x86, 0xfd, 0x35, \ + 0x83, 0x81, 0x61, 0x18, \ + 0x00, 0x86, 0x80, 0x04, \ + 0xe1, 0x18, 0x00, 0x46, \ + 0x80, 0x08, 0x80, 0x04, \ + 0x40, 0x9a, 0x82, 0x01, \ + 0x4c, 0x03, 0xe0, 0x02, \ + 0xc0, 0x17, 0x6e, 0x79, \ + 0xef, 0xe4, 0x6e, 0x68, \ + 0xe1, 0x14, 0x41, 0xc6, \ + 0x80, 0x86, 0x80, 0x04, \ + 0xe8, 0x1c, 0x5b, 0x28, \ + 0xef, 0xcc, 0x4f, 0xa8, \ + 0x0c, 0x4c, 0x8e, 0x0b, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x04, 0x04, 0x4c, \ + 0x43, 0x26, 0x00, 0x6b, \ + 0x0c, 0x01, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x66, 0x55, \ + 0x6d, 0x09, 0x4c, 0x11, \ + 0x40, 0x0a, 0xc0, 0x2f, \ + 0x0c, 0x09, 0xe0, 0x84, \ + 0xc0, 0x2f, 0x06, 0x49, \ + 0x0c, 0x19, 0x8c, 0x95, \ + 0xc0, 0x31, 0x64, 0x29, \ + 0x80, 0xb5, 0xa3, 0x1b, \ + 0x63, 0x69, 0x0c, 0x09, \ + 0x85, 0xb5, 0xc0, 0x27, \ + 0xf7, 0x00, 0x00, 0x8a, \ + 0x0c, 0x19, 0x80, 0xd5, \ + 0xc0, 0x27, 0x63, 0x6d, \ + 0xfc, 0xa5, 0xa1, 0x1b, \ + 0xe0, 0x98, 0x40, 0x4a, \ + 0x0c, 0x19, 0x80, 0xc5, \ + 0xc0, 0x30, 0x6d, 0x95, \ + 0x82, 0x65, 0xa1, 0x1b, \ + 0x82, 0x0d, 0xff, 0x11, \ + 0x0c, 0x19, 0x82, 0x15, \ + 0xc0, 0x27, 0x63, 0x6d, \ + 0x04, 0x65, 0xa1, 0x1b, \ + 0x80, 0x00, 0x40, 0x46, \ + 0xe0, 0x94, 0x40, 0x0a, \ + 0x06, 0x49, 0x0c, 0x09, \ + 0x02, 0x60, 0x00, 0x2f, \ + 0x80, 0xbd, 0x06, 0x51, \ + 0x05, 0x51, 0x02, 0x60, \ + 0x84, 0x18, 0xc0, 0xa1, \ + 0x05, 0x55, 0x0c, 0x09, \ + 0x82, 0x03, 0xc0, 0x28, \ + 0xe1, 0x00, 0x42, 0x06, \ + 0x84, 0x06, 0x84, 0x14, \ + 0xa1, 0x0b, 0x84, 0x08, \ + 0x84, 0x08, 0x80, 0xa5, \ + 0xe5, 0x04, 0x00, 0x8a, \ + 0x40, 0x46, 0x80, 0x95, \ + 0x84, 0x06, 0xe0, 0x00, \ + 0x61, 0x00, 0x0f, 0xc6, \ + 0x9c, 0x04, 0x83, 0x21, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xdc, \ + 0x80, 0x18, 0x9c, 0x04, \ + 0x00, 0x16, 0xc2, 0x19, \ + 0x4f, 0xd8, 0x87, 0x11, \ + 0x8c, 0x0b, 0x68, 0x1c, \ + 0x83, 0x21, 0x1c, 0x04, \ + 0xc4, 0x19, 0x80, 0x18, \ + 0x99, 0x11, 0x00, 0x16, \ + 0x68, 0x1c, 0x4f, 0x48, \ + 0x9c, 0x04, 0x8c, 0x0b, \ + 0xc8, 0x19, 0x80, 0x18, \ + 0x0c, 0x4c, 0x80, 0x16, \ + 0xdd, 0x93, 0xf2, 0x04, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x61, 0x18, 0x02, 0x86, \ + 0xe0, 0x04, 0x45, 0xd6, \ + 0x81, 0x31, 0x04, 0x14, \ + 0x6d, 0x09, 0x40, 0x21, \ + 0x4d, 0x06, 0x40, 0x2f, \ + 0x84, 0x36, 0xe1, 0x38, \ + 0x61, 0x51, 0x00, 0x15, \ + 0x98, 0x24, 0xc0, 0x0a, \ + 0x82, 0x11, 0xd9, 0x17, \ + 0x6e, 0x79, 0x40, 0x13, \ + 0x88, 0x08, 0xc0, 0x17, \ + 0xf3, 0x7c, 0x40, 0x00, \ + 0x41, 0x86, 0x08, 0x06, \ + 0x80, 0x14, 0xe1, 0x00, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x84, 0x26, 0xf3, 0xec, \ + 0x84, 0x28, 0x80, 0x14, \ + 0xf2, 0xfc, 0x44, 0x20, \ + 0x80, 0x14, 0x84, 0x26, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x84, 0x26, 0xf3, 0xf4, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xf8, 0x42, 0x10, \ + 0x41, 0x46, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x00, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x00, 0x16, 0xf3, 0xf8, \ + 0xe1, 0x00, 0x41, 0xc6, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xdc, 0x42, 0x10, \ + 0x63, 0x78, 0x80, 0x16, \ + 0x98, 0x04, 0xef, 0xe4, \ + 0x02, 0x14, 0x80, 0x18, \ + 0x80, 0x16, 0xfc, 0x94, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0xcc, 0x03, 0xa8, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x41, 0x46, 0x00, 0x6b, \ + 0x00, 0x04, 0xe1, 0x04, \ + 0x80, 0x18, 0x81, 0x21, \ + 0x00, 0x16, 0xc2, 0x19, \ + 0xe0, 0x04, 0x42, 0x46, \ + 0x68, 0x1c, 0x49, 0x48, \ + 0x0e, 0x60, 0x8c, 0x0b, \ + 0x40, 0x54, 0x66, 0x51, \ + 0xe1, 0x40, 0x51, 0x46, \ + 0xe1, 0x00, 0x13, 0x86, \ + 0x80, 0xa8, 0xa0, 0x04, \ + 0x4d, 0x58, 0x81, 0xb5, \ + 0x0c, 0x39, 0xe8, 0x1c, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x29, 0x21, 0x85, 0x11, \ + 0x63, 0x58, 0x8e, 0x0b, \ + 0xa4, 0x04, 0xef, 0xe0, \ + 0x40, 0x4a, 0x80, 0x08, \ + 0x82, 0x65, 0xe0, 0x98, \ + 0xe0, 0x8c, 0x54, 0x0a, \ + 0x0c, 0x05, 0xfa, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0xc0, 0x00, 0x0a, \ + 0xfd, 0x55, 0xfa, 0x25, \ + 0xe8, 0x1c, 0x4b, 0xc8, \ + 0xef, 0xe4, 0x5c, 0x88, \ + 0xef, 0xcc, 0x40, 0x28, \ + 0x81, 0x11, 0x01, 0x01, \ + 0x0c, 0x0c, 0x8c, 0x2b, \ + 0x22, 0x18, 0xf2, 0x3c, \ + 0xc1, 0xc3, 0xff, 0xcc, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x41, 0x46, 0x00, 0x6b, \ + 0x00, 0x04, 0xe1, 0x04, \ + 0x80, 0x16, 0x81, 0x11, \ + 0xef, 0xe4, 0x5c, 0x48, \ + 0xef, 0xc8, 0x7e, 0xe8, \ + 0x81, 0x11, 0x01, 0x01, \ + 0x0c, 0x0c, 0x8c, 0x2b, \ + 0x20, 0xd8, 0xf2, 0x00, \ + 0xc1, 0xc3, 0xff, 0xcc, \ + 0xfa, 0x84, 0x04, 0x0c, \ + 0x40, 0x46, 0x00, 0x6b, \ + 0x00, 0x04, 0xe1, 0x44, \ + 0x80, 0x76, 0x81, 0x71, \ + 0xef, 0xe4, 0x59, 0x88, \ + 0xef, 0xe4, 0x5c, 0xe8, \ + 0xef, 0xe4, 0x5e, 0xc8, \ + 0xef, 0xe4, 0x5a, 0xa8, \ + 0x6d, 0x05, 0x4c, 0x71, \ + 0x81, 0x01, 0x40, 0x2f, \ + 0x8c, 0x2b, 0x01, 0x11, \ + 0xef, 0xcc, 0x5f, 0x48, \ + 0xf2, 0x84, 0x0c, 0x0c, \ + 0xff, 0xc8, 0x3d, 0xe8, \ + 0x61, 0x2c, 0x41, 0xc6, \ + 0xe1, 0x30, 0x02, 0x06, \ + 0x04, 0x46, 0x00, 0x04, \ + 0x06, 0x86, 0xe1, 0x30, \ + 0x08, 0xc6, 0x61, 0x30, \ + 0x0b, 0x06, 0xe1, 0x30, \ + 0x19, 0x46, 0x61, 0x30, \ + 0x80, 0x04, 0xe1, 0x30, \ + 0x84, 0x14, 0x84, 0x14, \ + 0x00, 0x13, 0x88, 0x24, \ + 0xe1, 0x30, 0x01, 0x86, \ + 0x8c, 0x34, 0x88, 0x24, \ + 0x8c, 0x34, 0x84, 0x13, \ + 0x86, 0x13, 0x90, 0x44, \ + 0x94, 0x54, 0x90, 0x44, \ + 0x94, 0x54, 0x88, 0x13, \ + 0x8a, 0x13, 0xb0, 0xc4, \ + 0x80, 0x04, 0xb0, 0xc4, \ + 0x80, 0x04, 0x98, 0x13, \ + 0x82, 0x03, 0x5d, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x00, 0x46, 0x00, 0x6b, \ + 0x00, 0x04, 0xe1, 0x24, \ + 0xe1, 0x24, 0x44, 0x46, \ + 0x61, 0x4d, 0x0c, 0x15, \ + 0x80, 0x08, 0xc0, 0x0a, \ + 0xc5, 0x07, 0x88, 0x24, \ + 0x44, 0x00, 0x08, 0x34, \ + 0x7a, 0x98, 0xf0, 0x0c, \ + 0x8c, 0x0b, 0x68, 0x1c, \ + 0x6d, 0x95, 0x0c, 0x19, \ + 0x4c, 0x05, 0xc0, 0x30, \ + 0x40, 0x05, 0x67, 0xb5, \ + 0x4c, 0x01, 0x81, 0x01, \ + 0xc0, 0x15, 0x6e, 0xe9, \ + 0x6e, 0xed, 0x4c, 0x11, \ + 0x0c, 0x0c, 0xc0, 0x15, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xe1, 0x18, 0x03, 0x06, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x61, 0x5d, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x23, \ + 0xe1, 0x18, 0x03, 0x06, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x61, 0x7d, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x1c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x41, 0x46, 0x00, 0x6b, \ + 0x00, 0x04, 0xe1, 0x24, \ + 0xe1, 0x18, 0x03, 0x06, \ + 0x84, 0x14, 0x80, 0x04, \ + 0x4c, 0x05, 0x82, 0x01, \ + 0xc0, 0x08, 0x6c, 0x45, \ + 0x91, 0x11, 0x04, 0x08, \ + 0xef, 0xb4, 0x48, 0xe8, \ + 0x61, 0x49, 0x4c, 0x01, \ + 0x0c, 0x0c, 0xc0, 0x23, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x41, 0x46, 0x00, 0x6b, \ + 0x00, 0x04, 0xe1, 0x24, \ + 0xe1, 0x18, 0x03, 0x06, \ + 0x84, 0x14, 0x80, 0x04, \ + 0x4c, 0x05, 0x82, 0x01, \ + 0xc0, 0x07, 0x60, 0x4d, \ + 0x91, 0x11, 0x04, 0x08, \ + 0xef, 0xb4, 0x47, 0x68, \ + 0x6d, 0x99, 0x0c, 0x19, \ + 0x4c, 0x01, 0xc0, 0x30, \ + 0xc0, 0x1c, 0x61, 0x69, \ + 0x61, 0x5d, 0x4c, 0x11, \ + 0x0c, 0x0c, 0xc0, 0x1c, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x00, 0xc6, 0x00, 0x6b, \ + 0x00, 0x04, 0xe1, 0x34, \ + 0x80, 0x04, 0x8b, 0x31, \ + 0x61, 0x49, 0x0c, 0x29, \ + 0x0c, 0x19, 0xc0, 0x23, \ + 0xc0, 0x23, 0x61, 0x59, \ + 0x6c, 0x5d, 0x4c, 0x05, \ + 0x54, 0x78, 0xc0, 0x08, \ + 0x4c, 0x05, 0xef, 0xb4, \ + 0x40, 0x08, 0x6c, 0x61, \ + 0xe0, 0x0c, 0x43, 0xf6, \ + 0x61, 0x91, 0x4c, 0x11, \ + 0x0c, 0x0c, 0xc0, 0x23, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x68, 0x20, 0x56, 0x68, \ + 0x42, 0x78, 0x80, 0x6b, \ + 0x8c, 0x0b, 0x68, 0x00, \ + 0x66, 0x81, 0x00, 0x60, \ + 0x02, 0x06, 0x40, 0x70, \ + 0xc0, 0x20, 0x63, 0x41, \ + 0x60, 0x04, 0x04, 0x06, \ + 0x5e, 0x58, 0x81, 0x31, \ + 0x0e, 0x60, 0xe8, 0x0c, \ + 0x40, 0x70, 0x67, 0x81, \ + 0x81, 0xf5, 0xa1, 0x81, \ + 0x78, 0x40, 0x0e, 0x05, \ + 0x0c, 0x29, 0x8b, 0x31, \ + 0xc0, 0x1c, 0x61, 0x69, \ + 0x61, 0x79, 0x0c, 0x19, \ + 0x51, 0x98, 0xc0, 0x1c, \ + 0x5d, 0x0e, 0xef, 0xb4, \ + 0xec, 0x04, 0x10, 0x80, \ + 0xfc, 0x35, 0xa1, 0x8b, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x9c, 0x04, 0x0c, \ + 0x02, 0x06, 0x00, 0x6b, \ + 0x73, 0x78, 0xe0, 0x04, \ + 0x0e, 0x60, 0xe8, 0x1c, \ + 0x40, 0x71, 0x64, 0x81, \ + 0x01, 0x95, 0xa1, 0x81, \ + 0x5d, 0x18, 0x8c, 0x9b, \ + 0x7b, 0x98, 0x8c, 0x0b, \ + 0x65, 0x0e, 0xef, 0xdc, \ + 0xec, 0x04, 0x10, 0x80, \ + 0xfe, 0x15, 0xa1, 0x8b, \ + 0x65, 0x81, 0x00, 0x60, \ + 0x04, 0x06, 0x40, 0x70, \ + 0x01, 0x31, 0xe0, 0x04, \ + 0x0c, 0x0c, 0x8c, 0x1b, \ + 0x1a, 0xb8, 0xf2, 0x9c, \ + 0xc1, 0xc3, 0xe0, 0x0c, \ + 0x65, 0xa1, 0x00, 0x00, \ + 0x02, 0x06, 0x40, 0x8c, \ + 0xc0, 0x20, 0x63, 0x1d, \ + 0x60, 0x24, 0x04, 0x06, \ + 0x19, 0xf8, 0x87, 0x31, \ + 0xc1, 0xc3, 0xe0, 0x0c, \ + 0x65, 0x01, 0x00, 0x00, \ + 0x02, 0x06, 0x40, 0x71, \ + 0xc0, 0x20, 0x61, 0x01, \ + 0x62, 0x40, 0x04, 0x06, \ + 0x19, 0x38, 0x87, 0x31, \ + 0xc1, 0xc3, 0xe0, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0x6d, 0x91, 0x1e, 0xf0, \ + 0x00, 0x6b, 0xc0, 0x04, \ + 0xe1, 0x30, 0x40, 0x46, \ + 0x68, 0x6d, 0x0c, 0x29, \ + 0x03, 0xc6, 0x40, 0x23, \ + 0x0c, 0x39, 0xe1, 0x30, \ + 0x40, 0x23, 0x6b, 0xbd, \ + 0x4c, 0x41, 0x85, 0x41, \ + 0x40, 0x23, 0x68, 0x69, \ + 0x00, 0x04, 0x81, 0x71, \ + 0x4c, 0x21, 0x82, 0x21, \ + 0x40, 0x23, 0x68, 0x6d, \ + 0x4c, 0x31, 0x82, 0x31, \ + 0x40, 0x23, 0x6b, 0xbd, \ + 0x64, 0xd1, 0x04, 0xf0, \ + 0x04, 0x14, 0xc0, 0x02, \ + 0xe4, 0x10, 0x06, 0xf0, \ + 0x95, 0x51, 0x1d, 0x41, \ + 0x81, 0x81, 0x01, 0xc1, \ + 0x12, 0xc4, 0x83, 0x65, \ + 0x82, 0xc1, 0x7a, 0x80, \ + 0xe1, 0x12, 0x52, 0x9a, \ + 0x66, 0x96, 0x54, 0x9a, \ + 0x12, 0x0c, 0xbb, 0x9b, \ + 0x10, 0xa3, 0xe2, 0x00, \ + 0x14, 0xa4, 0x92, 0x83, \ + 0xc4, 0xa7, 0xfa, 0x80, \ + 0xfc, 0x28, 0x00, 0xb5, \ + 0x02, 0xa5, 0xc9, 0xbe, \ + 0xcd, 0xae, 0xfc, 0x28, \ + 0xe2, 0x30, 0x18, 0x0a, \ + 0x00, 0x60, 0xfa, 0x35, \ + 0x40, 0x93, 0x65, 0x81, \ + 0x64, 0xd1, 0x02, 0xf0, \ + 0x45, 0x06, 0xc0, 0x02, \ + 0x81, 0x31, 0x60, 0x20, \ + 0xe8, 0x0c, 0x54, 0x68, \ + 0x66, 0x41, 0x00, 0x60, \ + 0x02, 0xf0, 0x40, 0x95, \ + 0x45, 0x06, 0xe4, 0x10, \ + 0x81, 0x31, 0x60, 0x20, \ + 0xe8, 0x0c, 0x53, 0xc8, \ + 0x61, 0x2c, 0x41, 0xc6, \ + 0xe1, 0x2c, 0x43, 0x46, \ + 0x04, 0x60, 0x00, 0x04, \ + 0xc0, 0x8e, 0x66, 0x31, \ + 0x81, 0x31, 0x04, 0x14, \ + 0x48, 0x30, 0x83, 0x55, \ + 0x82, 0x31, 0x70, 0x0c, \ + 0x60, 0x84, 0x0a, 0x44, \ + 0x08, 0x0c, 0xa7, 0x4b, \ + 0x8e, 0x53, 0x62, 0x00, \ + 0x7a, 0x80, 0x0a, 0x54, \ + 0xc4, 0x57, 0x88, 0x73, \ + 0xfc, 0x14, 0x00, 0xc5, \ + 0xf8, 0x20, 0x44, 0xc5, \ + 0xfc, 0x14, 0x02, 0x55, \ + 0xb1, 0x3b, 0xc9, 0x5e, \ + 0x1e, 0xf0, 0xfa, 0x75, \ + 0xc0, 0x04, 0x65, 0x91, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xe1, 0x18, 0x03, 0x06, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x68, 0xad, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x23, \ + 0xe1, 0x18, 0x03, 0x06, \ + 0x84, 0x18, 0x84, 0x14, \ + 0x6b, 0xfd, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x23, \ + 0x63, 0xa1, 0x00, 0x15, \ + 0x40, 0x15, 0xc0, 0x0c, \ + 0xc0, 0x08, 0x6e, 0x21, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x63, 0xa1, 0x00, 0x15, \ + 0x40, 0x15, 0xc0, 0x0c, \ + 0xc0, 0x08, 0x6e, 0xf5, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x84, 0x04, 0x0c, \ + 0x41, 0x86, 0x00, 0x6b, \ + 0x00, 0x04, 0xe1, 0x0c, \ + 0xe0, 0x04, 0x43, 0x16, \ + 0x6d, 0x09, 0x4c, 0x11, \ + 0x83, 0x71, 0x40, 0x2f, \ + 0x41, 0xc6, 0x00, 0x76, \ + 0x80, 0x04, 0xe1, 0x04, \ + 0xc2, 0x19, 0x80, 0x18, \ + 0x00, 0x06, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x08, \ + 0xc2, 0x19, 0x80, 0x18, \ + 0x8c, 0x0b, 0x00, 0x16, \ + 0xef, 0xfc, 0x5c, 0x28, \ + 0x6f, 0xfc, 0x50, 0x48, \ + 0x44, 0xd8, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0x68, 0x00, \ + 0x61, 0x14, 0x40, 0x06, \ + 0xe0, 0x04, 0x45, 0x66, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x00, 0x16, 0xc2, 0x19, \ + 0xe1, 0x04, 0x40, 0x06, \ + 0x43, 0x56, 0x00, 0x04, \ + 0x00, 0x76, 0xe0, 0x04, \ + 0xe1, 0x14, 0x41, 0x46, \ + 0x4c, 0x11, 0x80, 0x04, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x41, 0xc6, 0x00, 0x76, \ + 0x80, 0x04, 0xe1, 0x14, \ + 0x00, 0x86, 0x00, 0x76, \ + 0x0c, 0x19, 0xe1, 0x0c, \ + 0xc0, 0x30, 0x6e, 0xcd, \ + 0x4c, 0x21, 0x80, 0x04, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0xe0, 0x04, 0x45, 0x76, \ + 0x00, 0x46, 0x00, 0x16, \ + 0x0c, 0x19, 0xe1, 0x0c, \ + 0xc0, 0x30, 0x6e, 0xd5, \ + 0x00, 0x16, 0x80, 0x04, \ + 0xe1, 0x08, 0x40, 0x06, \ + 0x6e, 0xd1, 0x0c, 0x19, \ + 0x80, 0x04, 0xc0, 0x30, \ + 0x01, 0xc6, 0x00, 0x16, \ + 0x0c, 0x19, 0xe1, 0x08, \ + 0xc0, 0x30, 0x6e, 0xd9, \ + 0x00, 0x16, 0x80, 0x04, \ + 0xe1, 0x44, 0x40, 0x06, \ + 0x6e, 0xc9, 0x0c, 0x19, \ + 0x80, 0x04, 0xc0, 0x30, \ + 0x41, 0xf6, 0x00, 0x16, \ + 0x4c, 0x21, 0xef, 0xfc, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x0c, 0x0c, 0x81, 0x11, \ + 0x8f, 0x35, 0xf2, 0x84, \ + 0x6e, 0xf1, 0x00, 0x29, \ + 0x02, 0xc6, 0x40, 0x30, \ + 0x84, 0x14, 0xe1, 0x10, \ + 0x03, 0x06, 0x04, 0x26, \ + 0x00, 0x29, 0xe1, 0x10, \ + 0xc0, 0x30, 0x6e, 0xf5, \ + 0x04, 0x26, 0x84, 0x14, \ + 0xe1, 0x10, 0x03, 0xc6, \ + 0x6e, 0xf9, 0x00, 0x29, \ + 0x84, 0x14, 0xc0, 0x30, \ + 0x03, 0x86, 0x04, 0x26, \ + 0x00, 0x29, 0xe1, 0x10, \ + 0xc0, 0x30, 0x6e, 0xc1, \ + 0x04, 0x26, 0x84, 0x14, \ + 0xe1, 0x10, 0x03, 0x46, \ + 0x6c, 0x9d, 0x00, 0x29, \ + 0x84, 0x34, 0xc0, 0x2f, \ + 0xf0, 0xfc, 0x44, 0x20, \ + 0x00, 0x29, 0x8c, 0x26, \ + 0xc0, 0x30, 0x6f, 0x19, \ + 0x82, 0x65, 0xa1, 0x2b, \ + 0x6c, 0x9d, 0x00, 0x39, \ + 0x46, 0x30, 0xc0, 0x2f, \ + 0xc7, 0x2d, 0xf0, 0xfc, \ + 0xc0, 0x00, 0x02, 0x15, \ + 0xc0, 0x00, 0x42, 0x21, \ + 0x6f, 0x11, 0x00, 0x29, \ + 0x42, 0xc6, 0x40, 0x30, \ + 0x84, 0x14, 0xe1, 0x10, \ + 0x43, 0x46, 0x04, 0x26, \ + 0x00, 0x29, 0xe1, 0x10, \ + 0xc0, 0x30, 0x6f, 0x15, \ + 0x04, 0x26, 0x84, 0x14, \ + 0xe1, 0x10, 0x43, 0x06, \ + 0x6e, 0xc5, 0x00, 0x29, \ + 0x84, 0x14, 0xc0, 0x30, \ + 0x43, 0x86, 0x04, 0x26, \ + 0x00, 0x29, 0xe1, 0x10, \ + 0xc0, 0x30, 0x6e, 0xfd, \ + 0x04, 0x26, 0x84, 0x14, \ + 0xe1, 0x10, 0x04, 0x86, \ + 0x6f, 0x1d, 0x00, 0x19, \ + 0x88, 0x24, 0xc0, 0x30, \ + 0xe0, 0x00, 0x02, 0x4a, \ + 0x62, 0x00, 0x06, 0x0c, \ + 0x08, 0x36, 0xa1, 0x1b, \ + 0xe1, 0x10, 0x44, 0x86, \ + 0x02, 0x0c, 0x88, 0x24, \ + 0x08, 0x16, 0xe2, 0x00, \ + 0xe1, 0x10, 0x44, 0x46, \ + 0x08, 0x16, 0x88, 0x24, \ + 0xe1, 0x14, 0x04, 0x06, \ + 0x08, 0x16, 0x88, 0x24, \ + 0xe1, 0x0c, 0x43, 0xc6, \ + 0x6f, 0x0d, 0x00, 0x09, \ + 0x84, 0x14, 0xc0, 0x30, \ + 0xdd, 0x93, 0x84, 0x06, \ + 0xe1, 0x18, 0x45, 0xc6, \ + 0x08, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x04, 0x06, \ + 0x08, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x04, 0x46, \ + 0x08, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x04, 0x86, \ + 0x08, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x04, 0xc6, \ + 0x08, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x05, 0x06, \ + 0x08, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x05, 0x46, \ + 0x08, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x05, 0x86, \ + 0x08, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x05, 0xc6, \ + 0x08, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x44, 0x06, \ + 0x08, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x44, 0x46, \ + 0x08, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x44, 0x86, \ + 0x08, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x44, 0xc6, \ + 0x08, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x45, 0x06, \ + 0x08, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x45, 0x46, \ + 0x08, 0x16, 0x88, 0x24, \ + 0xe1, 0x1c, 0x45, 0x86, \ + 0x08, 0x16, 0x88, 0x24, \ + 0xe1, 0x14, 0x42, 0x86, \ + 0x84, 0x0c, 0x84, 0x14, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x1c, 0x04, 0x4c, \ + 0x68, 0x00, 0x49, 0x68, \ + 0x51, 0x06, 0x80, 0x6b, \ + 0xa0, 0x04, 0xe1, 0x38, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xbc, \ + 0xe8, 0x04, 0x64, 0xa8, \ + 0x95, 0x11, 0x03, 0x91, \ + 0x8c, 0x0b, 0x03, 0x21, \ + 0xe8, 0x18, 0x54, 0x68, \ + 0x61, 0x10, 0x40, 0x46, \ + 0x80, 0x14, 0x81, 0x71, \ + 0x80, 0x04, 0x84, 0x76, \ + 0xa0, 0x04, 0x80, 0x96, \ + 0xe0, 0x19, 0x80, 0x18, \ + 0x8c, 0x0b, 0x00, 0x16, \ + 0xe8, 0x00, 0x45, 0x88, \ + 0xe1, 0x28, 0x41, 0xc6, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xf8, 0x42, 0x10, \ + 0x40, 0x06, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x18, \ + 0x40, 0x06, 0x00, 0x76, \ + 0x80, 0x04, 0xe1, 0x14, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x00, 0x16, 0xf2, 0xfc, \ + 0xe1, 0x14, 0x40, 0x46, \ + 0x00, 0x76, 0x80, 0x04, \ + 0xe1, 0x14, 0x40, 0x86, \ + 0x00, 0x7c, 0x80, 0x04, \ + 0xe1, 0x14, 0x41, 0x06, \ + 0x00, 0x76, 0x80, 0x04, \ + 0xe1, 0x14, 0x41, 0x46, \ + 0x00, 0x76, 0x80, 0x04, \ + 0xe1, 0x14, 0x41, 0xc6, \ + 0x00, 0x76, 0x80, 0x04, \ + 0xe1, 0x10, 0x00, 0x46, \ + 0x00, 0x76, 0x80, 0x04, \ + 0xe1, 0x04, 0x40, 0x06, \ + 0x80, 0x76, 0x80, 0x04, \ + 0x6d, 0x01, 0x0c, 0x09, \ + 0x40, 0x4a, 0xc0, 0x2f, \ + 0x40, 0x60, 0xe0, 0x80, \ + 0x4a, 0x18, 0xdc, 0x00, \ + 0x41, 0xc6, 0xcf, 0xfc, \ + 0x80, 0x04, 0xe1, 0x04, \ + 0x00, 0x06, 0x00, 0x76, \ + 0x80, 0x04, 0xe1, 0x08, \ + 0x01, 0xc6, 0x00, 0x76, \ + 0x80, 0x04, 0xe1, 0x08, \ + 0x00, 0x46, 0x00, 0x76, \ + 0x80, 0x04, 0xe1, 0x0c, \ + 0x40, 0x86, 0x00, 0x76, \ + 0x80, 0x04, 0xe1, 0x10, \ + 0x41, 0x46, 0x00, 0x76, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0x00, 0x06, 0x00, 0x76, \ + 0x80, 0x04, 0xe1, 0x44, \ + 0x0c, 0x4c, 0x80, 0x76, \ + 0xdd, 0x93, 0xf2, 0x1c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x61, 0x00, 0x0d, 0xc6, \ + 0xe0, 0x04, 0x45, 0x36, \ + 0x40, 0x21, 0x98, 0x14, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x84, 0x26, 0xf3, 0xec, \ + 0x84, 0x28, 0x98, 0x14, \ + 0xf3, 0xf8, 0x44, 0x20, \ + 0x43, 0x46, 0x04, 0x26, \ + 0x4c, 0xb8, 0xe0, 0x1c, \ + 0x83, 0x21, 0x68, 0x18, \ + 0x80, 0x18, 0x98, 0x04, \ + 0xf3, 0xf4, 0x42, 0x10, \ + 0x0c, 0x0c, 0x80, 0x16, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0x61, 0x24, 0x00, 0xc6, \ + 0x80, 0x04, 0x81, 0x11, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x41, 0x46, 0x00, 0x6b, \ + 0x00, 0x14, 0xe1, 0x14, \ + 0xe0, 0x08, 0x04, 0xc6, \ + 0x6d, 0x09, 0x4c, 0x21, \ + 0x81, 0x21, 0x40, 0x2f, \ + 0x10, 0xc6, 0x03, 0x71, \ + 0x04, 0x26, 0xe1, 0x24, \ + 0xe1, 0x2c, 0x42, 0x06, \ + 0x04, 0x26, 0x84, 0x14, \ + 0x80, 0x04, 0x85, 0x11, \ + 0xa0, 0x04, 0x80, 0x76, \ + 0x8c, 0x0b, 0x00, 0x26, \ + 0xe8, 0x14, 0x6d, 0x28, \ + 0x02, 0x76, 0x20, 0x04, \ + 0x4c, 0x11, 0xe0, 0x08, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x0c, 0x0c, 0x80, 0x76, \ + 0xdd, 0x93, 0xf2, 0x0c, \ + 0xfa, 0xfc, 0x04, 0x0c, \ + 0x82, 0x6b, 0x04, 0x3b, \ + 0x81, 0x91, 0x00, 0x7b, \ + 0x86, 0x1b, 0x01, 0x21, \ + 0xe8, 0x18, 0x48, 0xc8, \ + 0x66, 0x51, 0x00, 0x70, \ + 0xbe, 0x0e, 0xc0, 0x54, \ + 0x61, 0x24, 0x41, 0x06, \ + 0xe1, 0x2c, 0x42, 0x46, \ + 0x47, 0x46, 0x00, 0x04, \ + 0x80, 0x08, 0xe1, 0x40, \ + 0xc5, 0x07, 0x84, 0x14, \ + 0x56, 0x00, 0x04, 0x28, \ + 0x84, 0x18, 0xf0, 0x04, \ + 0xc7, 0x27, 0x8c, 0x34, \ + 0x50, 0x20, 0x44, 0x17, \ + 0x0c, 0x38, 0xf0, 0x08, \ + 0xf0, 0x10, 0x54, 0x10, \ + 0xe0, 0x8c, 0x46, 0x0a, \ + 0x0e, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0xcc, 0x00, 0x0a, \ + 0x0e, 0x39, 0x82, 0x35, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x3e, 0x04, 0x81, 0x11, \ + 0x61, 0x78, 0x91, 0x21, \ + 0xb2, 0xbf, 0xef, 0xdc, \ + 0xb4, 0xbf, 0xb0, 0xbf, \ + 0xf8, 0x18, 0x52, 0xb2, \ + 0xf4, 0x75, 0x93, 0x6b, \ + 0xf2, 0xfc, 0x0c, 0x0c, \ + 0xe0, 0x18, 0x0a, 0x48, \ + 0x02, 0x86, 0x02, 0x2b, \ + 0x40, 0x11, 0xe0, 0x08, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0xf7, 0x95, 0x8f, 0x11, \ + 0xfa, 0x84, 0x04, 0x0c, \ + 0x00, 0x46, 0x00, 0x7b, \ + 0x4e, 0x01, 0xe0, 0x08, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x78, 0x98, 0x82, 0x6b, \ + 0x8e, 0x0b, 0x6f, 0xfc, \ + 0x60, 0x08, 0x00, 0x86, \ + 0x4e, 0x01, 0x8f, 0x11, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x8c, 0x2b, 0x8e, 0x0b, \ + 0xf2, 0x84, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xf5, 0x25, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x31, 0x64, 0x51, \ + 0x60, 0x90, 0x40, 0x0a, \ + 0xe0, 0x04, 0x00, 0x06, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x01, 0x06, 0x40, 0x2f, \ + 0x52, 0x98, 0xc0, 0x00, \ + 0x0f, 0x86, 0xcf, 0xe0, \ + 0x13, 0x46, 0x61, 0x38, \ + 0x1c, 0x04, 0xe1, 0x38, \ + 0xe1, 0x34, 0x50, 0x06, \ + 0xc2, 0x19, 0x80, 0x18, \ + 0x9c, 0x04, 0x80, 0x16, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xf8, \ + 0x80, 0x18, 0xa4, 0x04, \ + 0xf3, 0xf8, 0x42, 0x10, \ + 0x02, 0x16, 0x00, 0x16, \ + 0xa0, 0x04, 0xe0, 0x04, \ + 0x6d, 0x09, 0x4c, 0x11, \ + 0x00, 0x16, 0xc0, 0x2f, \ + 0x6f, 0xd8, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x18, \ + 0xc0, 0x30, 0x6d, 0xb5, \ + 0xe0, 0x84, 0x40, 0x4a, \ + 0x40, 0x0a, 0x82, 0x45, \ + 0x82, 0xe5, 0xe0, 0x88, \ + 0x6d, 0xc1, 0x0c, 0x19, \ + 0x42, 0x0a, 0xc0, 0x30, \ + 0x82, 0x25, 0xe0, 0x88, \ + 0x61, 0x34, 0x41, 0x46, \ + 0x80, 0x04, 0x89, 0x11, \ + 0x0c, 0x09, 0x80, 0x16, \ + 0xc0, 0x30, 0x6d, 0xb5, \ + 0xf8, 0x1c, 0x00, 0x4a, \ + 0x40, 0x4a, 0x80, 0xb5, \ + 0x82, 0x55, 0xe0, 0x94, \ + 0x0c, 0x19, 0x85, 0x05, \ + 0xc0, 0x30, 0x6d, 0xb1, \ + 0x86, 0x65, 0xa1, 0x1b, \ + 0xe0, 0x94, 0x40, 0x0a, \ + 0x20, 0x04, 0x86, 0x15, \ + 0xe0, 0x04, 0x02, 0x26, \ + 0x6d, 0x09, 0x4c, 0x11, \ + 0x55, 0xc6, 0x40, 0x2f, \ + 0x00, 0x16, 0xe1, 0x0c, \ + 0xa8, 0x04, 0xa3, 0x11, \ + 0x8c, 0x0b, 0x00, 0x16, \ + 0xe8, 0x00, 0x5d, 0x88, \ + 0x6f, 0x0d, 0x0c, 0x09, \ + 0xa8, 0x14, 0xc0, 0x30, \ + 0x0c, 0x09, 0x84, 0x06, \ + 0xc0, 0x30, 0x6d, 0xb5, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x20, 0x04, 0x86, 0x05, \ + 0xe0, 0x04, 0x02, 0x36, \ + 0x6d, 0x09, 0x4c, 0x11, \ + 0x00, 0x16, 0xc0, 0x2f, \ + 0xe1, 0x34, 0x41, 0x46, \ + 0x83, 0x11, 0x00, 0x04, \ + 0x8c, 0x0b, 0x00, 0x16, \ + 0xe8, 0x1c, 0x42, 0x68, \ + 0x68, 0x69, 0x0c, 0x09, \ + 0xa1, 0x0b, 0xc0, 0x31, \ + 0x9c, 0x00, 0x40, 0x60, \ + 0x88, 0x00, 0x5d, 0xe8, \ + 0x61, 0x34, 0x55, 0x06, \ + 0xa8, 0x04, 0x87, 0xb1, \ + 0x0c, 0x09, 0x80, 0xbc, \ + 0xc0, 0x30, 0x6d, 0xb5, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x0c, 0x09, 0x9a, 0xa5, \ + 0xc0, 0x30, 0x6d, 0xc1, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x20, 0x04, 0x98, 0xe5, \ + 0xe0, 0x04, 0x02, 0x46, \ + 0x6d, 0x09, 0x4c, 0x11, \ + 0x00, 0x16, 0xc0, 0x2f, \ + 0xe1, 0x34, 0x41, 0x46, \ + 0x85, 0x11, 0x00, 0x04, \ + 0x8c, 0x0b, 0x00, 0x16, \ + 0xe8, 0x00, 0x64, 0x48, \ + 0x6f, 0xf8, 0x7a, 0x88, \ + 0x45, 0x78, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0x6f, 0xfc, \ + 0x6f, 0xf8, 0x7b, 0x28, \ + 0x7e, 0x58, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0x6f, 0xf8, \ + 0x64, 0x55, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x31, \ + 0x00, 0x06, 0xe0, 0x88, \ + 0x47, 0xf8, 0xc0, 0x40, \ + 0x28, 0x04, 0xcf, 0xe0, \ + 0x00, 0x1c, 0x89, 0x11, \ + 0xe0, 0x08, 0x01, 0x36, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x8c, 0x0b, 0x40, 0x2f, \ + 0xef, 0xf4, 0x6c, 0x48, \ + 0x64, 0x55, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x31, \ + 0x00, 0x06, 0xe0, 0x8c, \ + 0x46, 0xb8, 0xc0, 0x80, \ + 0x28, 0x04, 0xcf, 0xe0, \ + 0x80, 0x1c, 0x8b, 0x11, \ + 0x68, 0x69, 0x0c, 0x09, \ + 0xa3, 0x0b, 0xc0, 0x31, \ + 0x9c, 0x00, 0x40, 0x60, \ + 0x88, 0x00, 0x58, 0xa8, \ + 0x02, 0x56, 0x20, 0x04, \ + 0x4c, 0x11, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x0c, 0x09, 0x80, 0x16, \ + 0xc0, 0x30, 0x6d, 0x31, \ + 0xec, 0x04, 0x02, 0x00, \ + 0x82, 0xa5, 0xe5, 0x1d, \ + 0x61, 0x04, 0x40, 0x46, \ + 0x80, 0x04, 0x83, 0x11, \ + 0x01, 0x06, 0x00, 0x16, \ + 0x00, 0x04, 0xe1, 0x0c, \ + 0xe8, 0x00, 0x02, 0x06, \ + 0x81, 0xa5, 0x80, 0x1e, \ + 0x40, 0x46, 0xa1, 0x0b, \ + 0x02, 0x06, 0x01, 0x04, \ + 0x00, 0x05, 0x80, 0x00, \ + 0x40, 0x11, 0x80, 0x00, \ + 0x0c, 0x09, 0x80, 0x00, \ + 0xc0, 0x30, 0x6d, 0xb1, \ + 0x8c, 0x45, 0xa1, 0x0b, \ + 0x02, 0x66, 0x20, 0x04, \ + 0x4c, 0x11, 0xe0, 0x04, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0xa1, 0x21, 0xa1, 0xa1, \ + 0x00, 0x60, 0x00, 0x16, \ + 0xc0, 0x71, 0x64, 0x81, \ + 0x02, 0xf0, 0x01, 0x75, \ + 0x41, 0x38, 0xe4, 0x10, \ + 0xec, 0x04, 0x04, 0x20, \ + 0xa1, 0x2b, 0xc5, 0x36, \ + 0x50, 0x38, 0xfe, 0x75, \ + 0x8c, 0x0b, 0x68, 0x1c, \ + 0x68, 0x00, 0x62, 0x28, \ + 0x00, 0x60, 0x8c, 0x0b, \ + 0x40, 0x71, 0x64, 0x81, \ + 0xe4, 0x10, 0x02, 0xf0, \ + 0x45, 0x28, 0x81, 0x55, \ + 0xec, 0x04, 0x14, 0xa0, \ + 0xa1, 0xab, 0xc1, 0x26, \ + 0x0c, 0x09, 0xfe, 0x75, \ + 0x40, 0x31, 0x68, 0x69, \ + 0xe1, 0x34, 0x55, 0x06, \ + 0x40, 0x60, 0xa5, 0x0b, \ + 0x53, 0x18, 0x9c, 0x00, \ + 0xa4, 0x04, 0x88, 0x00, \ + 0xc2, 0x19, 0x80, 0x18, \ + 0x9c, 0x04, 0x80, 0x16, \ + 0xc2, 0x19, 0x80, 0x18, \ + 0x9c, 0x04, 0x80, 0x16, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xf8, \ + 0x6d, 0xb5, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x8a, 0x85, 0xe0, 0x88, \ + 0x6d, 0xc1, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x88, 0xc5, 0xe0, 0x88, \ + 0x02, 0x76, 0x20, 0x04, \ + 0x4c, 0x11, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x41, 0x46, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x34, \ + 0x8c, 0x0b, 0x00, 0xb6, \ + 0xe8, 0x00, 0x66, 0x08, \ + 0x68, 0x69, 0x0c, 0x09, \ + 0xa7, 0x0b, 0xc0, 0x31, \ + 0x9c, 0x00, 0x40, 0x60, \ + 0x88, 0x00, 0x50, 0x28, \ + 0x64, 0x55, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x31, \ + 0x00, 0x06, 0xe0, 0x9c, \ + 0x7c, 0xf8, 0xc8, 0x00, \ + 0x60, 0xd8, 0xcf, 0xdc, \ + 0x8c, 0x0b, 0x6f, 0xfc, \ + 0x68, 0x14, 0x68, 0xc8, \ + 0x0c, 0x09, 0x8c, 0x0b, \ + 0xc0, 0x31, 0x64, 0x59, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0xd0, 0x00, 0x00, 0x06, \ + 0xcf, 0xdc, 0x7c, 0x08, \ + 0x6d, 0xb5, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x82, 0xc5, 0xe0, 0x8c, \ + 0x02, 0x86, 0x20, 0x04, \ + 0x4c, 0x11, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x8c, 0x0b, 0x00, 0x16, \ + 0xe8, 0x00, 0x63, 0xa8, \ + 0x6d, 0xb5, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x40, 0x60, 0xe0, 0x90, \ + 0x48, 0x38, 0xdc, 0x00, \ + 0x0c, 0x09, 0xcf, 0xe0, \ + 0xc0, 0x31, 0x68, 0x69, \ + 0x40, 0x60, 0xa9, 0x0b, \ + 0x4c, 0x98, 0x9c, 0x00, \ + 0x0c, 0x09, 0x88, 0x00, \ + 0xc0, 0x30, 0x6d, 0xb5, \ + 0xe0, 0x90, 0x40, 0x0a, \ + 0x4d, 0xd8, 0x80, 0xe5, \ + 0x8c, 0x0b, 0x6f, 0xcc, \ + 0x6d, 0xb5, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x92, 0x45, 0xe0, 0x88, \ + 0x6d, 0xc1, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x90, 0x85, 0xe0, 0x88, \ + 0x02, 0xa6, 0x20, 0x04, \ + 0x4c, 0x11, 0xe0, 0x04, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x8c, 0x0b, 0x00, 0x16, \ + 0xef, 0xf8, 0x7f, 0x68, \ + 0x6f, 0xf8, 0x7e, 0xc8, \ + 0x7d, 0x98, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0x6f, 0xf8, \ + 0x6f, 0xf8, 0x7d, 0xe8, \ + 0x62, 0x98, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0x68, 0x00, \ + 0x68, 0x00, 0x6c, 0xc8, \ + 0x0c, 0x0b, 0x8c, 0x0b, \ + 0xc0, 0x18, 0x66, 0x8d, \ + 0xe0, 0x80, 0x40, 0x4a, \ + 0x68, 0x78, 0x88, 0xa5, \ + 0x8c, 0x0b, 0x6f, 0xf8, \ + 0x6f, 0xf8, 0x73, 0xa8, \ + 0x6a, 0x18, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0x6f, 0xf8, \ + 0x6f, 0xf8, 0x6d, 0xa8, \ + 0x0c, 0x09, 0x8c, 0x0b, \ + 0xc0, 0x31, 0x64, 0x59, \ + 0xe0, 0x94, 0x40, 0x0a, \ + 0x00, 0x01, 0x00, 0x06, \ + 0x75, 0x58, 0xc0, 0x08, \ + 0x03, 0x11, 0xcf, 0xdc, \ + 0x6d, 0x98, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x00, \ + 0xc0, 0x31, 0x64, 0x59, \ + 0xe0, 0x98, 0x40, 0x0a, \ + 0x00, 0x01, 0x00, 0x06, \ + 0x74, 0x78, 0xc0, 0x10, \ + 0x9c, 0x04, 0xcf, 0xdc, \ + 0xc2, 0x19, 0x80, 0x18, \ + 0x9c, 0x04, 0x80, 0x16, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xf8, \ + 0x80, 0x18, 0xa4, 0x04, \ + 0xf3, 0xf8, 0x42, 0x10, \ + 0x01, 0x36, 0x00, 0x16, \ + 0x4c, 0x01, 0xe0, 0x0c, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x62, 0x38, 0x8c, 0x0b, \ + 0x20, 0x04, 0xef, 0xf8, \ + 0xe0, 0x04, 0x02, 0xb6, \ + 0x6d, 0x09, 0x4c, 0x11, \ + 0x80, 0x16, 0xc0, 0x2f, \ + 0xef, 0xfc, 0x56, 0x28, \ + 0x61, 0x38, 0x4f, 0x06, \ + 0x1c, 0x04, 0x83, 0x81, \ + 0x80, 0x18, 0x83, 0x21, \ + 0xf3, 0xbc, 0x42, 0x10, \ + 0x00, 0x46, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x10, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x00, 0x16, 0xf3, 0xf8, \ + 0x60, 0xf8, 0x95, 0x11, \ + 0x8c, 0x0b, 0x68, 0x14, \ + 0x61, 0x10, 0x40, 0x46, \ + 0x00, 0x14, 0x81, 0x21, \ + 0x84, 0x26, 0xa8, 0xf1, \ + 0x80, 0x86, 0x80, 0x04, \ + 0x80, 0x18, 0x9c, 0x04, \ + 0x00, 0x16, 0xe0, 0x19, \ + 0xe0, 0x00, 0x42, 0xe6, \ + 0x80, 0x1c, 0xa8, 0x04, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x04, 0x04, 0x4c, \ + 0x61, 0x14, 0x4c, 0x06, \ + 0xe1, 0x38, 0x4f, 0x06, \ + 0x84, 0x28, 0x98, 0x14, \ + 0x84, 0x26, 0xe0, 0x29, \ + 0x84, 0x28, 0x9c, 0x14, \ + 0xf3, 0xdc, 0x44, 0x20, \ + 0x9c, 0x14, 0x84, 0x26, \ + 0x44, 0x20, 0x84, 0x28, \ + 0x04, 0x26, 0xf3, 0xbc, \ + 0xe1, 0x10, 0x02, 0x46, \ + 0x84, 0x28, 0x84, 0x14, \ + 0x84, 0x26, 0xc2, 0x29, \ + 0xef, 0xe8, 0x7b, 0x88, \ + 0x80, 0x18, 0x9c, 0x04, \ + 0x80, 0x16, 0xe0, 0x19, \ + 0x80, 0x18, 0x98, 0x04, \ + 0xf3, 0xbc, 0x42, 0x10, \ + 0x0c, 0x4c, 0x80, 0x16, \ + 0xdd, 0x93, 0xf2, 0x04, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x08, 0x09, 0x80, 0x4b, \ + 0xc0, 0x31, 0x68, 0x6d, \ + 0xe0, 0x8c, 0x40, 0x4a, \ + 0x08, 0x15, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0xd0, 0x02, 0x0a, \ + 0x08, 0x39, 0x84, 0x95, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0xf3, 0xf4, 0x40, 0x00, \ + 0x68, 0x6d, 0x48, 0x01, \ + 0x00, 0x40, 0x40, 0x31, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0x44, 0x96, 0x01, 0x11, \ + 0xa8, 0xf1, 0xe0, 0x00, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xff, 0xd8, 0x36, 0x48, \ + 0x6c, 0x31, 0x08, 0x0b, \ + 0x0e, 0x40, 0x40, 0x18, \ + 0xc0, 0xc5, 0x65, 0xe1, \ + 0x81, 0xc1, 0x3e, 0x4e, \ + 0x73, 0xf1, 0x40, 0x00, \ + 0x88, 0x03, 0xc0, 0xff, \ + 0x64, 0x01, 0x0a, 0x00, \ + 0x00, 0x40, 0x40, 0x28, \ + 0xc0, 0xc3, 0x67, 0x21, \ + 0x00, 0x40, 0x7e, 0x0e, \ + 0xc0, 0x9f, 0x67, 0xc1, \ + 0x89, 0x35, 0xfc, 0x0e, \ + 0x00, 0x94, 0x9c, 0x94, \ + 0x7e, 0x04, 0xe1, 0xc0, \ + 0x73, 0xf1, 0x4c, 0x00, \ + 0x1c, 0xa4, 0xc0, 0xff, \ + 0x9c, 0x84, 0x88, 0x63, \ + 0x82, 0x35, 0x81, 0x6b, \ + 0x8a, 0xbb, 0x08, 0x0b, \ + 0x70, 0x78, 0x89, 0xce, \ + 0x09, 0xc4, 0xe8, 0x10, \ + 0xbe, 0x44, 0x96, 0x5b, \ + 0x56, 0xa0, 0x7c, 0x04, \ + 0xc0, 0xff, 0x73, 0xc1, \ + 0x82, 0x35, 0x81, 0x6b, \ + 0x88, 0x0b, 0x3c, 0x5e, \ + 0x6d, 0xd8, 0x89, 0xce, \ + 0x89, 0xc4, 0xe8, 0x10, \ + 0xbe, 0x44, 0xbc, 0x54, \ + 0x86, 0xa5, 0xa1, 0xbb, \ + 0x70, 0x00, 0x00, 0x0e, \ + 0x00, 0x9f, 0x9e, 0x89, \ + 0x32, 0xbf, 0xc9, 0xa7, \ + 0x30, 0xbf, 0xa1, 0x8b, \ + 0x73, 0xfd, 0x40, 0xa0, \ + 0x55, 0xbe, 0xc0, 0x0f, \ + 0x80, 0x00, 0x10, 0x16, \ + 0x66, 0x82, 0x40, 0x8a, \ + 0xc5, 0x07, 0x8a, 0x2b, \ + 0x80, 0x1b, 0x01, 0x65, \ + 0x02, 0x10, 0x59, 0x34, \ + 0xc9, 0x3e, 0xec, 0x04, \ + 0xfe, 0x75, 0xa1, 0x1b, \ + 0x80, 0x53, 0xc4, 0x07, \ + 0x82, 0xc1, 0x08, 0x71, \ + 0xe0, 0xa0, 0x18, 0x0a, \ + 0xa8, 0xf1, 0xee, 0x95, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x30, 0x6d, 0xc1, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x40, 0x46, 0x8a, 0xa5, \ + 0x81, 0x11, 0x61, 0x04, \ + 0x85, 0x31, 0x00, 0x04, \ + 0x00, 0x16, 0x83, 0x41, \ + 0x0c, 0x15, 0x8c, 0x0b, \ + 0xc0, 0x0c, 0x63, 0x99, \ + 0x68, 0x18, 0x49, 0xc8, \ + 0x0c, 0x09, 0x82, 0x2b, \ + 0xc0, 0x31, 0x64, 0x55, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0xc0, 0x20, 0x00, 0x06, \ + 0xcf, 0xdc, 0x63, 0x28, \ + 0x6f, 0xfc, 0x47, 0x08, \ + 0x4d, 0x38, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0x68, 0x14, \ + 0x79, 0x41, 0x02, 0x06, \ + 0x8c, 0x0b, 0x5d, 0xcd, \ + 0xef, 0xfc, 0x4c, 0xc8, \ + 0x68, 0x14, 0x4b, 0xc8, \ + 0x0c, 0x09, 0x8c, 0x0b, \ + 0xc0, 0x30, 0x6d, 0x99, \ + 0x61, 0x3d, 0x4c, 0x01, \ + 0x0c, 0x0c, 0xc0, 0x23, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0x6d, 0x31, 0x00, 0x09, \ + 0x02, 0x00, 0xc0, 0x30, \ + 0xe5, 0x1d, 0xec, 0x04, \ + 0x40, 0x46, 0x82, 0xa5, \ + 0x83, 0x11, 0x61, 0x04, \ + 0x00, 0x16, 0x80, 0x04, \ + 0xe1, 0x0c, 0x01, 0x06, \ + 0x02, 0x06, 0x00, 0x04, \ + 0x80, 0x1e, 0xe8, 0x00, \ + 0xa1, 0x0b, 0xdd, 0x93, \ + 0xda, 0x00, 0x5c, 0x08, \ + 0x61, 0x04, 0x40, 0x46, \ + 0x80, 0x04, 0x81, 0x11, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xfa, 0x7c, 0x04, 0x4c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x30, 0x6d, 0xd9, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x81, 0x01, 0x9c, 0x05, \ + 0x6d, 0xe1, 0x4c, 0x01, \ + 0x8c, 0x0b, 0x40, 0x30, \ + 0xe8, 0x00, 0x65, 0x48, \ + 0x61, 0x34, 0x4f, 0x06, \ + 0x11, 0x91, 0x8d, 0x81, \ + 0x0b, 0xb5, 0x8f, 0xa1, \ + 0x0c, 0x09, 0x93, 0xb1, \ + 0xc0, 0x31, 0x64, 0x55, \ + 0xe0, 0x90, 0x40, 0x0a, \ + 0xc1, 0x00, 0x00, 0x06, \ + 0xcf, 0xdc, 0x5d, 0xe8, \ + 0x79, 0x41, 0x02, 0x06, \ + 0x8c, 0x0b, 0x5d, 0xcd, \ + 0xef, 0xfc, 0x48, 0x68, \ + 0x64, 0x55, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x31, \ + 0x00, 0x06, 0xe0, 0x94, \ + 0x5c, 0xf8, 0xc2, 0x00, \ + 0x0c, 0x09, 0xcf, 0xdc, \ + 0x40, 0x30, 0x6d, 0xe1, \ + 0xa1, 0x0b, 0x81, 0x11, \ + 0x80, 0x00, 0x0e, 0x05, \ + 0x80, 0x00, 0x40, 0x83, \ + 0x0d, 0xe1, 0x0c, 0x09, \ + 0x00, 0x0a, 0x80, 0x30, \ + 0x0e, 0x05, 0xf0, 0x04, \ + 0x40, 0x93, 0x80, 0x00, \ + 0x8c, 0x0b, 0x00, 0x00, \ + 0xe8, 0x00, 0x54, 0x28, \ + 0x6d, 0xe1, 0x0c, 0x09, \ + 0xa1, 0x0b, 0xc0, 0x30, \ + 0x80, 0x00, 0x0e, 0x05, \ + 0x80, 0x00, 0x40, 0xa3, \ + 0x0d, 0xe1, 0x0c, 0x09, \ + 0x00, 0x0a, 0x80, 0x30, \ + 0x0e, 0x05, 0xf0, 0x04, \ + 0x40, 0xb3, 0x80, 0x00, \ + 0x8c, 0x0b, 0x00, 0x00, \ + 0x66, 0x8d, 0x0c, 0x1b, \ + 0x0c, 0x29, 0xc0, 0x18, \ + 0xc0, 0x30, 0x6d, 0xa1, \ + 0x6f, 0xd0, 0x6e, 0xe8, \ + 0x0c, 0x19, 0x82, 0x19, \ + 0xc0, 0x30, 0x6d, 0xe1, \ + 0x64, 0x55, 0x0c, 0x09, \ + 0x82, 0x11, 0xc0, 0x31, \ + 0x6d, 0xe1, 0x4c, 0x11, \ + 0x40, 0x0a, 0x40, 0x30, \ + 0x80, 0xa5, 0xe0, 0x98, \ + 0x6f, 0xdc, 0x59, 0x08, \ + 0xe4, 0x00, 0x00, 0x06, \ + 0x6d, 0xe1, 0x0c, 0x09, \ + 0x0c, 0x19, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6d, 0xb1, \ + 0xe8, 0x15, 0xc3, 0x0d, \ + 0xf2, 0x7c, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x63, 0x9d, 0x00, 0x15, \ + 0x85, 0x31, 0x40, 0x0c, \ + 0x63, 0xa1, 0x00, 0x25, \ + 0x81, 0x41, 0x40, 0x0c, \ + 0xe0, 0x14, 0x3d, 0x88, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x30, 0x6d, 0xf1, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x5d, 0xd8, 0x82, 0x85, \ + 0x8c, 0x0b, 0x68, 0x00, \ + 0x68, 0x69, 0x0c, 0x09, \ + 0xb1, 0x0b, 0xc0, 0x31, \ + 0x9c, 0x00, 0x40, 0x60, \ + 0x8f, 0xfc, 0x68, 0xe8, \ + 0x6d, 0xf1, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x82, 0xa5, 0xe0, 0x88, \ + 0x68, 0x00, 0x5e, 0xc8, \ + 0x0c, 0x09, 0x8c, 0x0b, \ + 0xc0, 0x31, 0x68, 0x69, \ + 0x80, 0xb5, 0xb3, 0x0b, \ + 0x0c, 0x0c, 0x8c, 0x0b, \ + 0xcf, 0xb5, 0xf2, 0x00, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x31, 0x65, 0xd1, \ + 0xe0, 0x80, 0x40, 0x4a, \ + 0x0c, 0x09, 0x82, 0x45, \ + 0xc0, 0x31, 0x65, 0xd5, \ + 0xe0, 0x80, 0x40, 0x4a, \ + 0x0e, 0x60, 0x10, 0xb5, \ + 0xc0, 0xc5, 0x04, 0x51, \ + 0x04, 0x06, 0x0d, 0x11, \ + 0xdd, 0xcd, 0x79, 0x41, \ + 0x6f, 0xf8, 0x79, 0xc8, \ + 0x51, 0x38, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0x6f, 0xf8, \ + 0x6b, 0x9d, 0x0c, 0x09, \ + 0x0c, 0x89, 0xc0, 0x2f, \ + 0xc0, 0x30, 0x6d, 0x95, \ + 0x1e, 0x89, 0xa3, 0x0b, \ + 0x9c, 0x00, 0x40, 0x60, \ + 0x08, 0x10, 0x71, 0xe8, \ + 0x9a, 0x80, 0x02, 0x84, \ + 0x64, 0x59, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x31, \ + 0x00, 0x0e, 0xe0, 0x84, \ + 0xc0, 0x00, 0x00, 0x01, \ + 0xcf, 0xdc, 0x51, 0xa8, \ + 0x6d, 0xf1, 0x0c, 0x09, \ + 0x0e, 0x60, 0x40, 0x30, \ + 0xc0, 0xc5, 0x64, 0x51, \ + 0xe0, 0x8c, 0x40, 0x0a, \ + 0x40, 0x06, 0x82, 0xe5, \ + 0x45, 0xa6, 0x61, 0x34, \ + 0x00, 0x04, 0xe0, 0x08, \ + 0xfa, 0x80, 0x02, 0x84, \ + 0x6d, 0x09, 0x4c, 0x21, \ + 0x00, 0x26, 0xc0, 0x2f, \ + 0x50, 0x38, 0x8c, 0x0b, \ + 0xdc, 0x08, 0xe8, 0x00, \ + 0xe0, 0x88, 0x40, 0x0a, \ + 0x00, 0x01, 0x00, 0x06, \ + 0x4f, 0xb8, 0xc0, 0x01, \ + 0xdc, 0x08, 0xcf, 0xdc, \ + 0xe0, 0x8c, 0x40, 0x0a, \ + 0x00, 0x01, 0x00, 0x06, \ + 0x4f, 0x38, 0xc0, 0x02, \ + 0x0c, 0x09, 0xcf, 0xdc, \ + 0xc0, 0x30, 0x6d, 0xf1, \ + 0xe0, 0x90, 0x40, 0x0a, \ + 0x0c, 0x09, 0x86, 0x65, \ + 0x40, 0x2f, 0x65, 0x69, \ + 0xe0, 0x0c, 0x03, 0x56, \ + 0x6d, 0x09, 0x4c, 0x11, \ + 0xa3, 0x0b, 0xc0, 0x2f, \ + 0xa5, 0x0b, 0x84, 0x25, \ + 0x50, 0x38, 0x80, 0xb5, \ + 0x8c, 0x0b, 0x68, 0x00, \ + 0x00, 0x60, 0x81, 0xb5, \ + 0x40, 0x54, 0x66, 0x51, \ + 0x45, 0x36, 0x81, 0x11, \ + 0x07, 0x56, 0x60, 0x00, \ + 0x58, 0x38, 0xe0, 0x0c, \ + 0x01, 0x06, 0xef, 0xd8, \ + 0x02, 0x60, 0x61, 0x4c, \ + 0xc0, 0xa9, 0x65, 0xa0, \ + 0x04, 0x60, 0x00, 0x04, \ + 0xc0, 0xa7, 0x66, 0x91, \ + 0xef, 0x84, 0x67, 0x28, \ + 0x82, 0xa5, 0xa1, 0x0b, \ + 0x66, 0x51, 0x08, 0x60, \ + 0x04, 0x04, 0x40, 0x54, \ + 0x0c, 0x39, 0xfb, 0x00, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x56, 0xb8, 0x89, 0x11, \ + 0x88, 0x0b, 0x6f, 0xd8, \ + 0x40, 0x0a, 0xdc, 0x08, \ + 0x00, 0x06, 0xe0, 0x90, \ + 0xc0, 0x04, 0x00, 0x01, \ + 0xcf, 0xdc, 0x4b, 0x28, \ + 0x04, 0x06, 0x0f, 0x11, \ + 0xdd, 0xcd, 0x79, 0x41, \ + 0x0c, 0x0c, 0x8c, 0x0b, \ + 0x30, 0xf8, 0xf2, 0x0c, \ + 0xc1, 0xc3, 0xff, 0xf8, \ + 0xe1, 0x48, 0x42, 0x06, \ + 0x84, 0x18, 0x84, 0x14, \ + 0xe0, 0x90, 0x42, 0x0a, \ + 0x9a, 0x00, 0x5c, 0x08, \ + 0x68, 0x6d, 0x00, 0x19, \ + 0x42, 0x0a, 0xc0, 0x31, \ + 0x5c, 0x08, 0xe0, 0x84, \ + 0x00, 0x19, 0x9a, 0x00, \ + 0xc0, 0x30, 0x6d, 0xb5, \ + 0xe0, 0x90, 0x42, 0x4a, \ + 0x00, 0x19, 0x80, 0xe5, \ + 0xc0, 0x30, 0x6d, 0x95, \ + 0x5c, 0x08, 0xa1, 0x1b, \ + 0x43, 0x86, 0xda, 0x00, \ + 0x45, 0xf6, 0x61, 0x34, \ + 0x84, 0x14, 0xe0, 0x0c, \ + 0x00, 0x39, 0x84, 0x2e, \ + 0xc0, 0x31, 0x68, 0x6d, \ + 0xe0, 0x88, 0x46, 0x0a, \ + 0x42, 0x30, 0xfe, 0x45, \ + 0x40, 0x11, 0xf3, 0xec, \ + 0xc0, 0x31, 0x68, 0x6d, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0x80, 0x6b, 0x3c, 0x1e, \ + 0x65, 0xad, 0x0c, 0x09, \ + 0x81, 0xb1, 0x40, 0x2f, \ + 0x89, 0xbc, 0xfe, 0xbe, \ + 0x60, 0x04, 0x00, 0x2a, \ + 0xe0, 0x08, 0x41, 0xb6, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x02, 0xf0, 0x40, 0x2f, \ + 0x80, 0x00, 0x44, 0x31, \ + 0x4f, 0xd5, 0x76, 0xa8, \ + 0x40, 0x60, 0x0f, 0xff, \ + 0x80, 0x00, 0x5c, 0x01, \ + 0x60, 0x48, 0x02, 0x06, \ + 0x73, 0xb8, 0x8c, 0x0b, \ + 0x00, 0x60, 0xe8, 0x14, \ + 0x40, 0x09, 0x64, 0x01, \ + 0xe7, 0x14, 0x0e, 0x06, \ + 0x12, 0x06, 0x3e, 0x0e, \ + 0xc0, 0x20, 0x61, 0x01, \ + 0x81, 0x81, 0x07, 0xb5, \ + 0x6e, 0x91, 0x0c, 0x0b, \ + 0x40, 0x0a, 0xc0, 0x17, \ + 0x8a, 0xa5, 0xe4, 0xa0, \ + 0xf0, 0xa0, 0x40, 0x62, \ + 0x64, 0x81, 0x00, 0xa9, \ + 0x14, 0x0a, 0xc0, 0x1c, \ + 0x88, 0xa5, 0xe3, 0xfc, \ + 0x40, 0x01, 0x01, 0x78, \ + 0xbc, 0x04, 0xc0, 0x00, \ + 0x04, 0x06, 0xa1, 0x0b, \ + 0x06, 0x16, 0x00, 0x24, \ + 0x40, 0x60, 0x80, 0x00, \ + 0x42, 0x90, 0x1c, 0x00, \ + 0x42, 0x62, 0x9c, 0x00, \ + 0x45, 0x06, 0x50, 0x9c, \ + 0x06, 0x46, 0xc0, 0x18, \ + 0x40, 0x60, 0x40, 0x00, \ + 0x59, 0x98, 0xdc, 0x00, \ + 0x7e, 0x34, 0xe8, 0x04, \ + 0xe4, 0x20, 0x0a, 0xf0, \ + 0x8c, 0x1b, 0x0c, 0x0b, \ + 0x94, 0x4b, 0x3e, 0x24, \ + 0xef, 0xd4, 0x74, 0x48, \ + 0x16, 0xb4, 0x09, 0x0a, \ + 0x80, 0xb3, 0xfb, 0x80, \ + 0x0e, 0x70, 0x02, 0x81, \ + 0xc0, 0x01, 0x66, 0xc1, \ + 0x65, 0x01, 0x12, 0x90, \ + 0x0e, 0x0a, 0xc0, 0x02, \ + 0xc0, 0x8c, 0x61, 0x01, \ + 0x4c, 0xb3, 0xf0, 0x95, \ + 0x40, 0x18, 0x63, 0x09, \ + 0x0c, 0x0c, 0xa0, 0xf1, \ + 0xdd, 0x93, 0xf6, 0x7c, \ + 0xe1, 0x10, 0x00, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xf8, 0x42, 0x10, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xfa, 0x04, 0x04, 0x4c, \ + 0x82, 0x7b, 0x00, 0x6b, \ + 0xef, 0xf4, 0x47, 0x48, \ + 0x66, 0x8d, 0x0c, 0x0b, \ + 0x40, 0x0a, 0xc0, 0x18, \ + 0x40, 0x60, 0xe0, 0x80, \ + 0x42, 0x70, 0x5c, 0x00, \ + 0x59, 0xb8, 0xdc, 0x00, \ + 0x68, 0x78, 0xc8, 0x10, \ + 0x8c, 0x0b, 0x68, 0x10, \ + 0x6d, 0x99, 0x0c, 0x09, \ + 0x4c, 0x01, 0xc0, 0x30, \ + 0xc0, 0x23, 0x6b, 0xed, \ + 0x68, 0x9d, 0x4c, 0x01, \ + 0x4c, 0x01, 0xc0, 0x23, \ + 0xc0, 0x27, 0x60, 0x8d, \ + 0xf2, 0x04, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x1c, 0x04, 0x0c, \ + 0xec, 0x50, 0x1e, 0xf0, \ + 0x01, 0x06, 0x00, 0x6b, \ + 0x10, 0x60, 0xe1, 0x4c, \ + 0x40, 0xa9, 0x65, 0xa1, \ + 0x64, 0x81, 0x08, 0x60, \ + 0x18, 0x60, 0xc0, 0xa1, \ + 0x40, 0xac, 0x65, 0x61, \ + 0x67, 0x81, 0x0e, 0x60, \ + 0x09, 0x8e, 0xc0, 0xa5, \ + 0x67, 0x81, 0x12, 0x60, \ + 0x7e, 0x9e, 0xc0, 0xae, \ + 0x66, 0xf1, 0x02, 0x60, \ + 0x7c, 0x7e, 0xc0, 0xc7, \ + 0x65, 0x01, 0x04, 0x60, \ + 0x3e, 0xce, 0xc0, 0xc6, \ + 0x66, 0xe1, 0x06, 0x60, \ + 0x3c, 0x4e, 0xc0, 0xc7, \ + 0x64, 0x81, 0x0a, 0x60, \ + 0x00, 0x04, 0xc0, 0x9c, \ + 0x66, 0x18, 0x8c, 0x4b, \ + 0x0c, 0x15, 0xef, 0xb0, \ + 0xc0, 0x0c, 0x67, 0xbd, \ + 0x82, 0xe5, 0xa1, 0x1b, \ + 0x66, 0x51, 0x00, 0x60, \ + 0x04, 0x14, 0x40, 0x54, \ + 0x0c, 0x39, 0xfb, 0x00, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0xa8, 0xf1, 0x8d, 0x11, \ + 0xf2, 0x1c, 0x0c, 0x0c, \ + 0xff, 0xd8, 0x05, 0x08, \ + 0x0c, 0x0c, 0xa8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x1c, \ + 0x63, 0xa5, 0x00, 0x15, \ + 0x85, 0x31, 0x40, 0x0c, \ + 0x04, 0x10, 0x83, 0x41, \ + 0x1f, 0x38, 0xec, 0x28, \ + 0xc1, 0xc3, 0xe0, 0x14, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x31, 0x64, 0x59, \ + 0xe0, 0x9c, 0x40, 0x0a, \ + 0x00, 0x01, 0x00, 0x06, \ + 0x78, 0x58, 0xc0, 0x20, \ + 0x6f, 0x58, 0xcf, 0xd8, \ + 0x8c, 0x0b, 0x6f, 0xf0, \ + 0x64, 0x5d, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x31, \ + 0x80, 0x95, 0xe0, 0x80, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x00, 0x06, 0xdd, 0x93, \ + 0xc0, 0x40, 0x60, 0x01, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0xd8, 0x37, 0x28, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x30, 0x6d, 0xb5, \ + 0xe0, 0x90, 0x40, 0x0a, \ + 0x0c, 0x09, 0x84, 0x85, \ + 0xc0, 0x31, 0x64, 0x5d, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x00, 0x01, 0x00, 0x06, \ + 0x75, 0xf8, 0xc0, 0x80, \ + 0x77, 0x98, 0xcf, 0xd8, \ + 0x8c, 0x0b, 0x6f, 0xf0, \ + 0x64, 0x5d, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x31, \ + 0x80, 0x95, 0xe0, 0x88, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x00, 0x06, 0xdd, 0x93, \ + 0xc1, 0x00, 0x60, 0x01, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0xd8, 0x34, 0xc8, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x30, 0x6d, 0xa5, \ + 0x6d, 0xb5, 0x0c, 0x19, \ + 0x0c, 0x2b, 0xc0, 0x30, \ + 0xc0, 0x18, 0x66, 0x89, \ + 0x6d, 0x1d, 0x0c, 0x49, \ + 0x40, 0x4a, 0x40, 0x30, \ + 0x0c, 0x79, 0xe0, 0x80, \ + 0xc0, 0x30, 0x6d, 0x85, \ + 0x6d, 0x21, 0x0c, 0x89, \ + 0x42, 0x10, 0x40, 0x30, \ + 0x02, 0x14, 0xdc, 0x20, \ + 0x40, 0x0a, 0x1c, 0x0c, \ + 0x06, 0x60, 0xe0, 0x90, \ + 0xc0, 0xc3, 0x05, 0xd1, \ + 0x05, 0xd5, 0x06, 0x60, \ + 0x48, 0x0a, 0x00, 0xc3, \ + 0x0c, 0x49, 0xe0, 0x94, \ + 0x40, 0x30, 0x6d, 0x89, \ + 0xc0, 0x84, 0x04, 0x24, \ + 0x04, 0x24, 0x0c, 0x58, \ + 0x46, 0x10, 0xfa, 0x80, \ + 0x4c, 0x31, 0xfc, 0x10, \ + 0x40, 0x30, 0x6d, 0xb5, \ + 0x4c, 0x51, 0x85, 0x4b, \ + 0x40, 0x30, 0x6d, 0xb1, \ + 0xdc, 0x10, 0x02, 0x34, \ + 0x1c, 0x50, 0x42, 0x10, \ + 0xe0, 0x8c, 0x40, 0x4a, \ + 0x6d, 0xb5, 0x4c, 0x11, \ + 0x40, 0x10, 0x40, 0x30, \ + 0x4c, 0x01, 0x93, 0xf0, \ + 0x80, 0x30, 0x0d, 0xb5, \ + 0x41, 0xf8, 0x80, 0x85, \ + 0x8c, 0x0b, 0x68, 0x04, \ + 0xff, 0xfc, 0x10, 0x0a, \ + 0x00, 0x84, 0x84, 0x65, \ + 0x02, 0x74, 0x7a, 0x80, \ + 0x81, 0x1b, 0xfa, 0x80, \ + 0x0d, 0xb5, 0x0c, 0x09, \ + 0x02, 0x06, 0x00, 0x30, \ + 0x4c, 0x11, 0x80, 0x00, \ + 0x80, 0x30, 0x0d, 0xb1, \ + 0x93, 0xe0, 0x40, 0x00, \ + 0x0d, 0xb5, 0x4c, 0x01, \ + 0x0c, 0x0c, 0x80, 0x30, \ + 0xdd, 0x93, 0xf2, 0x0c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x68, 0x00, 0x40, 0xe8, \ + 0x76, 0x38, 0x80, 0x6b, \ + 0x8c, 0x0b, 0x68, 0x08, \ + 0x68, 0x00, 0x56, 0x68, \ + 0x8c, 0x0b, 0x8c, 0x0b, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0x89, 0x05, \ + 0x6d, 0x0d, 0x00, 0x19, \ + 0x45, 0xc6, 0x40, 0x2f, \ + 0x40, 0x21, 0xe0, 0x08, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x42, 0x26, 0xa3, 0x1b, \ + 0x40, 0x11, 0x80, 0x00, \ + 0x80, 0x2f, 0x0d, 0x09, \ + 0xa7, 0x1b, 0x8a, 0x05, \ + 0xa5, 0x1b, 0x82, 0x05, \ + 0x80, 0x00, 0x42, 0x36, \ + 0x0d, 0x09, 0x40, 0x11, \ + 0x83, 0xf5, 0x80, 0x2f, \ + 0x63, 0xe9, 0x00, 0x19, \ + 0x02, 0x10, 0xc0, 0x31, \ + 0x03, 0x0a, 0xec, 0x04, \ + 0x42, 0x46, 0x74, 0x04, \ + 0x40, 0x11, 0xe0, 0x00, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x03, 0x91, 0x00, 0x19, \ + 0x42, 0x10, 0x80, 0x31, \ + 0x40, 0x11, 0x93, 0xec, \ + 0x80, 0x31, 0x03, 0x91, \ + 0x6d, 0x01, 0x00, 0x19, \ + 0x42, 0x0a, 0xc0, 0x2f, \ + 0x02, 0x16, 0xe0, 0x88, \ + 0x40, 0x11, 0xc0, 0x00, \ + 0xc0, 0x31, 0x04, 0x29, \ + 0x6d, 0x01, 0x00, 0x19, \ + 0xc4, 0x19, 0xc0, 0x2f, \ + 0x6d, 0x01, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x2f, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x43, 0xd6, 0x00, 0x6b, \ + 0x0c, 0x09, 0xe0, 0x08, \ + 0xc0, 0x2f, 0x6d, 0x0d, \ + 0x6d, 0x09, 0x4c, 0x11, \ + 0xa7, 0x0b, 0xc0, 0x2f, \ + 0xa5, 0x0b, 0x84, 0x05, \ + 0x0c, 0x09, 0xa6, 0x35, \ + 0x40, 0x2f, 0x6d, 0x01, \ + 0x4c, 0x11, 0x81, 0x11, \ + 0xc0, 0x2f, 0x6d, 0x0d, \ + 0xf3, 0xf0, 0x40, 0x00, \ + 0x6d, 0x01, 0x4c, 0x01, \ + 0x91, 0xc5, 0xc0, 0x2f, \ + 0x6d, 0x01, 0x0c, 0x09, \ + 0x40, 0x00, 0xc0, 0x2f, \ + 0x4c, 0x01, 0xf3, 0xf4, \ + 0x40, 0x2f, 0x6d, 0x01, \ + 0x63, 0x58, 0x8c, 0x0b, \ + 0x48, 0x38, 0xe8, 0x10, \ + 0x8c, 0x0b, 0x68, 0x00, \ + 0x65, 0x75, 0x0c, 0x09, \ + 0xa3, 0x0b, 0xc0, 0x2f, \ + 0x03, 0x91, 0x0c, 0x09, \ + 0x06, 0xa5, 0x80, 0x31, \ + 0x9c, 0x04, 0x00, 0x04, \ + 0x0c, 0x09, 0xa5, 0x0b, \ + 0x80, 0x31, 0x03, 0x91, \ + 0xa7, 0x0b, 0x84, 0xa5, \ + 0x03, 0x91, 0x0c, 0x09, \ + 0x84, 0x95, 0xc0, 0x31, \ + 0x63, 0x91, 0x0c, 0x09, \ + 0x40, 0x4a, 0xc0, 0x31, \ + 0x80, 0xe5, 0xe0, 0x88, \ + 0x6d, 0x01, 0x0c, 0x19, \ + 0x42, 0x0a, 0xc0, 0x2f, \ + 0x80, 0x85, 0xe0, 0x94, \ + 0x40, 0x00, 0x01, 0x45, \ + 0xc4, 0x09, 0xf3, 0xf4, \ + 0x63, 0x91, 0x4c, 0x01, \ + 0x40, 0x4a, 0xc0, 0x31, \ + 0x94, 0x85, 0xe0, 0x80, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x0c, 0x09, 0x82, 0x75, \ + 0xc0, 0x2f, 0x6c, 0xe1, \ + 0x8a, 0x25, 0xa1, 0x0b, \ + 0x4c, 0x01, 0xb5, 0x01, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x0c, 0x05, 0x89, 0x05, \ + 0x40, 0x0a, 0x61, 0x4d, \ + 0x60, 0x01, 0x02, 0x06, \ + 0x40, 0x1a, 0xc0, 0xfa, \ + 0x0c, 0x15, 0xe0, 0x02, \ + 0xc0, 0x05, 0x67, 0xcd, \ + 0xe1, 0x84, 0x04, 0x04, \ + 0x84, 0x13, 0xd0, 0x17, \ + 0x00, 0x46, 0xc2, 0x0d, \ + 0x40, 0x1a, 0xc0, 0x00, \ + 0x02, 0x06, 0x00, 0x02, \ + 0x84, 0x14, 0xe1, 0x4c, \ + 0x0c, 0x09, 0x84, 0x0e, \ + 0xc0, 0x2f, 0x6d, 0x01, \ + 0x4c, 0x01, 0xd0, 0x09, \ + 0x40, 0x2f, 0x6d, 0x01, \ + 0x4c, 0x98, 0x8c, 0x0b, \ + 0x85, 0x05, 0xef, 0xf4, \ + 0x60, 0x3c, 0x41, 0xf6, \ + 0x6b, 0xd8, 0x81, 0x11, \ + 0x41, 0x86, 0xef, 0x78, \ + 0x80, 0x04, 0xe1, 0x00, \ + 0x40, 0x0a, 0x80, 0x08, \ + 0x40, 0x60, 0xe0, 0x80, \ + 0x41, 0xb8, 0xdc, 0x00, \ + 0x41, 0xf6, 0xcf, 0xf4, \ + 0x83, 0x11, 0x60, 0x3c, \ + 0xef, 0x78, 0x6a, 0xe8, \ + 0x8d, 0x11, 0x37, 0x01, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x8c, 0x0b, 0x40, 0x2f, \ + 0xef, 0xe8, 0x57, 0x88, \ + 0x68, 0x00, 0x46, 0xc8, \ + 0x81, 0x01, 0x8c, 0x0b, \ + 0x64, 0x29, 0x4c, 0x01, \ + 0x0c, 0x0c, 0xc0, 0x31, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x00, 0x46, 0x00, 0x6b, \ + 0x00, 0x04, 0xe1, 0x24, \ + 0xe0, 0x08, 0x43, 0xe6, \ + 0x6d, 0x09, 0x4c, 0x11, \ + 0x44, 0x46, 0x40, 0x2f, \ + 0x0c, 0x15, 0xe1, 0x24, \ + 0xc0, 0x0a, 0x61, 0x4d, \ + 0x88, 0x24, 0x80, 0x08, \ + 0x08, 0x34, 0xc5, 0x07, \ + 0xf0, 0x0c, 0x44, 0x00, \ + 0x68, 0x14, 0x48, 0x88, \ + 0x0c, 0x25, 0x8c, 0x0b, \ + 0x40, 0x05, 0x64, 0x5d, \ + 0xe0, 0x3c, 0x42, 0x86, \ + 0x66, 0x86, 0x46, 0x0a, \ + 0x64, 0x00, 0x02, 0x0e, \ + 0x0c, 0x45, 0xc3, 0xd0, \ + 0xc0, 0x0c, 0x64, 0xf1, \ + 0x60, 0x0a, 0x42, 0x1a, \ + 0x4c, 0x05, 0xc9, 0x37, \ + 0xc0, 0x0c, 0x64, 0xe1, \ + 0xf0, 0x90, 0x44, 0x32, \ + 0x82, 0x55, 0xc5, 0x1d, \ + 0x64, 0xed, 0x0c, 0x05, \ + 0xa4, 0x13, 0x40, 0x0c, \ + 0x67, 0xcd, 0x4c, 0x15, \ + 0xc1, 0x1d, 0xc0, 0x05, \ + 0x0c, 0x09, 0x82, 0x85, \ + 0x40, 0x31, 0x63, 0x91, \ + 0x4c, 0x15, 0x81, 0x11, \ + 0xc0, 0x05, 0x67, 0xcd, \ + 0x4c, 0x01, 0xc2, 0x09, \ + 0xc0, 0x31, 0x63, 0x91, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x61, 0x4d, 0x00, 0x15, \ + 0x04, 0x06, 0x40, 0x0a, \ + 0xc0, 0xfa, 0x60, 0x01, \ + 0x67, 0xcd, 0x00, 0x05, \ + 0x42, 0x2a, 0xc0, 0x05, \ + 0xd0, 0x07, 0xe0, 0x06, \ + 0xe1, 0x84, 0x04, 0x14, \ + 0xc0, 0x1d, 0x84, 0x03, \ + 0xc0, 0x00, 0x00, 0x46, \ + 0x00, 0x06, 0x40, 0x0a, \ + 0xe1, 0x4c, 0x02, 0x06, \ + 0x84, 0x0e, 0x84, 0x14, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x61, 0x38, 0x42, 0x46, \ + 0xe0, 0x08, 0x45, 0xf6, \ + 0x40, 0x21, 0x84, 0x14, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0xc8, 0x29, 0x84, 0x28, \ + 0x81, 0x21, 0x04, 0x26, \ + 0x67, 0xcd, 0x00, 0x15, \ + 0xa1, 0x1b, 0xc0, 0x05, \ + 0x80, 0x00, 0x02, 0x16, \ + 0xe0, 0x10, 0x0a, 0xc8, \ + 0xfa, 0xfc, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x31, 0x64, 0x51, \ + 0xe0, 0x8c, 0x40, 0x0a, \ + 0xc0, 0x00, 0x00, 0x86, \ + 0xcf, 0xd8, 0x57, 0x28, \ + 0x61, 0x34, 0x41, 0x06, \ + 0x80, 0x04, 0x83, 0x11, \ + 0x0c, 0x05, 0x80, 0x1c, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0xd4, 0x00, 0x0a, \ + 0x0d, 0x21, 0x82, 0x35, \ + 0xe1, 0x90, 0x41, 0x06, \ + 0x4c, 0x21, 0x80, 0x26, \ + 0xc0, 0x30, 0x6e, 0xb1, \ + 0x41, 0x06, 0x81, 0x65, \ + 0x80, 0x28, 0xe1, 0x90, \ + 0x82, 0x45, 0xa1, 0x2b, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x00, 0x60, 0x40, 0x2f, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0x6f, 0xd4, 0x60, 0x28, \ + 0x0c, 0x09, 0x83, 0x11, \ + 0x40, 0x31, 0x68, 0x6d, \ + 0x57, 0x76, 0x81, 0x91, \ + 0x51, 0x86, 0x60, 0x00, \ + 0x05, 0xa1, 0xe0, 0x00, \ + 0xe0, 0x00, 0x4f, 0x96, \ + 0x40, 0x0a, 0xc9, 0x07, \ + 0x82, 0x09, 0x60, 0x80, \ + 0x06, 0x89, 0x4c, 0x03, \ + 0x81, 0x01, 0x40, 0x18, \ + 0x21, 0x9b, 0x91, 0x25, \ + 0xe0, 0x00, 0x41, 0x06, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x82, 0x45, 0xc0, 0x2f, \ + 0xe0, 0x00, 0x41, 0x26, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x8c, 0x0b, 0x40, 0x2f, \ + 0xe8, 0x00, 0x50, 0xc8, \ + 0x41, 0x16, 0x81, 0xa5, \ + 0x4c, 0x01, 0xe0, 0x00, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x49, 0x18, 0x8c, 0x0b, \ + 0x83, 0x91, 0xe8, 0x00, \ + 0xe0, 0x00, 0x41, 0x66, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x8c, 0x0b, 0x40, 0x2f, \ + 0xe8, 0x00, 0x61, 0x68, \ + 0xe0, 0x00, 0x41, 0x36, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x8c, 0x0b, 0x40, 0x2f, \ + 0xe8, 0x10, 0x6a, 0x88, \ + 0xe0, 0x00, 0x41, 0x46, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x8c, 0x0b, 0x40, 0x2f, \ + 0xe8, 0x14, 0x5b, 0x68, \ + 0x6d, 0xa1, 0x0c, 0x09, \ + 0x0c, 0x1b, 0xc0, 0x30, \ + 0xc0, 0x18, 0x66, 0x8d, \ + 0x6c, 0x04, 0x04, 0x00, \ + 0xe0, 0x00, 0x41, 0x56, \ + 0x6d, 0x09, 0x4c, 0x01, \ + 0x9e, 0x29, 0x40, 0x2f, \ + 0x8c, 0x0b, 0x02, 0x19, \ + 0xef, 0xcc, 0x64, 0x88, \ + 0x6d, 0x09, 0x4c, 0xb1, \ + 0x8c, 0x0b, 0x40, 0x2f, \ + 0xef, 0xfc, 0x5a, 0xa8, \ + 0x6d, 0xa1, 0x0c, 0x09, \ + 0x23, 0x0b, 0xc0, 0x30, \ + 0x84, 0xd5, 0x81, 0x01, \ + 0x6d, 0x29, 0x0c, 0x09, \ + 0x23, 0x0b, 0xc0, 0x30, \ + 0x84, 0x05, 0x83, 0x01, \ + 0x6d, 0x1d, 0x0c, 0x09, \ + 0x40, 0x4a, 0xc0, 0x30, \ + 0x83, 0x01, 0x60, 0x8c, \ + 0x0d, 0xa5, 0x0c, 0x09, \ + 0x02, 0x16, 0x40, 0x30, \ + 0x00, 0x04, 0xc0, 0x00, \ + 0x40, 0x02, 0xc1, 0x8c, \ + 0x4c, 0x01, 0xd8, 0x84, \ + 0x40, 0x31, 0x6e, 0xe1, \ + 0xe1, 0x34, 0x43, 0x06, \ + 0x8c, 0x0b, 0x04, 0x14, \ + 0x6d, 0x09, 0x4c, 0x81, \ + 0x84, 0xac, 0xc0, 0x2f, \ + 0xef, 0xf4, 0x79, 0x28, \ + 0x6d, 0x21, 0x0c, 0x19, \ + 0x81, 0x01, 0x40, 0x30, \ + 0x6d, 0x09, 0x4c, 0x71, \ + 0x02, 0x0a, 0xc0, 0x2f, \ + 0x80, 0xe5, 0xe3, 0xfc, \ + 0x6d, 0x85, 0x0c, 0x09, \ + 0x83, 0x0b, 0xc0, 0x30, \ + 0xe2, 0x00, 0x00, 0x0c, \ + 0xde, 0xc5, 0xa1, 0x0b, \ + 0x6d, 0x39, 0x0c, 0x09, \ + 0x0c, 0x19, 0xc0, 0x30, \ + 0xc0, 0x2f, 0x6d, 0x01, \ + 0x41, 0xa6, 0x21, 0x0b, \ + 0x4c, 0x01, 0xe0, 0x00, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x4c, 0x11, 0xc8, 0x19, \ + 0x40, 0x2f, 0x6d, 0x01, \ + 0xdc, 0x00, 0x40, 0x60, \ + 0xcf, 0xf4, 0x64, 0x88, \ + 0x64, 0x5d, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x31, \ + 0x80, 0x95, 0xe0, 0x94, \ + 0xf2, 0xfc, 0x0c, 0x0c, \ + 0x00, 0x06, 0xdd, 0x93, \ + 0xc8, 0x00, 0x60, 0x01, \ + 0xf2, 0xfc, 0x0c, 0x0c, \ + 0xff, 0xd8, 0x09, 0x68, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x03, 0x06, 0x00, 0x6b, \ + 0x0c, 0x0b, 0xe0, 0x08, \ + 0x40, 0x18, 0x66, 0x8d, \ + 0x4c, 0x11, 0xe1, 0x31, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x6d, 0x01, 0x0c, 0x29, \ + 0x40, 0x0a, 0xc0, 0x2f, \ + 0x02, 0x06, 0xe0, 0x90, \ + 0x4c, 0x15, 0xc0, 0x00, \ + 0x40, 0x0c, 0x03, 0x5d, \ + 0xf0, 0x00, 0x02, 0x0e, \ + 0x63, 0x61, 0x4c, 0x15, \ + 0x40, 0x0a, 0x40, 0x0c, \ + 0x43, 0xf6, 0xe0, 0x88, \ + 0x4c, 0x11, 0xc0, 0x0c, \ + 0x40, 0x30, 0x0d, 0x91, \ + 0xe0, 0x8c, 0x40, 0x0a, \ + 0xc0, 0x00, 0x02, 0x06, \ + 0x0d, 0x95, 0x4c, 0x11, \ + 0x40, 0x0a, 0x40, 0x30, \ + 0x02, 0x06, 0xe0, 0x94, \ + 0x4c, 0x11, 0xc0, 0x00, \ + 0xc0, 0x30, 0x0d, 0x99, \ + 0x0d, 0x9d, 0x4c, 0x11, \ + 0x44, 0x4a, 0x40, 0x30, \ + 0x0c, 0x19, 0xe0, 0x88, \ + 0x40, 0x30, 0x6d, 0xa5, \ + 0x4c, 0x21, 0x83, 0x21, \ + 0xc0, 0x30, 0x6d, 0xa1, \ + 0x46, 0x10, 0x86, 0x1f, \ + 0x42, 0x10, 0x7c, 0x04, \ + 0x4c, 0x31, 0xdc, 0x44, \ + 0xc0, 0x30, 0x6d, 0xa5, \ + 0x0d, 0xa5, 0x4c, 0x11, \ + 0x40, 0x0a, 0x40, 0x30, \ + 0x84, 0xc5, 0xe0, 0x9c, \ + 0x65, 0x01, 0x0c, 0x59, \ + 0x8c, 0x0b, 0x40, 0x28, \ + 0x64, 0xfd, 0x0c, 0x49, \ + 0x0c, 0x39, 0xc0, 0x28, \ + 0xc0, 0x28, 0x64, 0xf9, \ + 0x64, 0xf5, 0x0c, 0x29, \ + 0x0c, 0x19, 0xc0, 0x28, \ + 0xc0, 0x28, 0x64, 0xf1, \ + 0xe8, 0x00, 0x75, 0x88, \ + 0x66, 0x8d, 0x0c, 0x0b, \ + 0x40, 0x0a, 0xc0, 0x18, \ + 0x40, 0x60, 0xe0, 0xb0, \ + 0x4f, 0xf8, 0xdc, 0x00, \ + 0x0c, 0x09, 0xc8, 0x10, \ + 0x40, 0x2f, 0x65, 0xf1, \ + 0xe1, 0x04, 0x42, 0xc6, \ + 0x00, 0x00, 0x84, 0x14, \ + 0x8e, 0x09, 0xec, 0x04, \ + 0x0c, 0x0c, 0x84, 0x06, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xf8, 0x7c, 0x04, 0xcc, \ + 0x6d, 0xa5, 0x00, 0x59, \ + 0x03, 0x16, 0x40, 0x30, \ + 0x00, 0x4b, 0xe0, 0x08, \ + 0xc0, 0x18, 0x66, 0x8d, \ + 0x6d, 0x09, 0x40, 0x11, \ + 0x00, 0x19, 0xc0, 0x2f, \ + 0x40, 0x30, 0x6d, 0x29, \ + 0xfc, 0x00, 0x0e, 0x54, \ + 0x6d, 0x25, 0x00, 0x69, \ + 0x48, 0x0a, 0x40, 0x30, \ + 0x00, 0x9b, 0xe0, 0xb4, \ + 0xc0, 0x18, 0x66, 0x89, \ + 0x63, 0x41, 0x00, 0xa5, \ + 0x00, 0xc9, 0xc0, 0x0c, \ + 0xc0, 0x30, 0x6d, 0x91, \ + 0x6d, 0xa1, 0x00, 0x29, \ + 0x00, 0xb9, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6d, 0x8d, \ + 0x6d, 0x89, 0x00, 0x89, \ + 0x40, 0x71, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6d, 0xa5, \ + 0x86, 0x55, 0x8f, 0x1e, \ + 0x83, 0x31, 0x0d, 0xbb, \ + 0x44, 0x04, 0x06, 0xb0, \ + 0xe3, 0xfc, 0x16, 0x0a, \ + 0x00, 0x00, 0x06, 0x16, \ + 0xe3, 0xfc, 0x10, 0x0a, \ + 0x6d, 0x8d, 0x40, 0x31, \ + 0x83, 0x11, 0x40, 0x30, \ + 0x93, 0x8b, 0x80, 0x85, \ + 0xc4, 0x04, 0x02, 0x80, \ + 0x6d, 0x89, 0x40, 0x11, \ + 0xf1, 0x31, 0x40, 0x30, \ + 0x07, 0x45, 0x86, 0x5f, \ + 0x0d, 0xae, 0xc4, 0x59, \ + 0xe3, 0xfc, 0x16, 0x0a, \ + 0x61, 0xac, 0x14, 0x44, \ + 0x02, 0x45, 0x83, 0x11, \ + 0x06, 0x64, 0x83, 0x31, \ + 0x06, 0x34, 0xe0, 0x84, \ + 0x07, 0xbb, 0xfa, 0x80, \ + 0x06, 0xb0, 0x83, 0x31, \ + 0x40, 0x31, 0xc4, 0x04, \ + 0x40, 0x30, 0x6d, 0x8d, \ + 0xe3, 0xfc, 0x10, 0x0a, \ + 0xc2, 0x97, 0x80, 0xe5, \ + 0xfa, 0x80, 0x12, 0x94, \ + 0x02, 0x80, 0x93, 0x8b, \ + 0x40, 0x11, 0xc4, 0x04, \ + 0x40, 0x30, 0x6d, 0x89, \ + 0xe0, 0x80, 0x54, 0x0a, \ + 0x02, 0x34, 0x80, 0xa5, \ + 0xcc, 0x1d, 0xfa, 0x80, \ + 0x54, 0x4a, 0x80, 0xf5, \ + 0x80, 0xe5, 0xe0, 0x80, \ + 0xe0, 0x80, 0x46, 0x4a, \ + 0x01, 0x55, 0x80, 0x85, \ + 0xfc, 0x18, 0x4a, 0x70, \ + 0xf3, 0xe0, 0x4a, 0x50, \ + 0x40, 0x51, 0x8d, 0xa4, \ + 0x40, 0x30, 0x6d, 0xa5, \ + 0xe0, 0x84, 0x4a, 0x0a, \ + 0x02, 0xc4, 0x82, 0xc5, \ + 0x0d, 0x1b, 0xfa, 0x80, \ + 0x02, 0xc0, 0x83, 0x11, \ + 0x18, 0x0a, 0x44, 0x04, \ + 0x02, 0x16, 0xf3, 0xfc, \ + 0x40, 0x11, 0x80, 0x00, \ + 0xc0, 0x30, 0x6d, 0x91, \ + 0x6d, 0x95, 0x00, 0x39, \ + 0x02, 0x0e, 0x40, 0x30, \ + 0x18, 0x0e, 0xff, 0xf8, \ + 0x06, 0x0a, 0xff, 0xf8, \ + 0x02, 0x30, 0xe3, 0xfc, \ + 0x48, 0x0a, 0x44, 0x04, \ + 0x40, 0x11, 0xe0, 0xac, \ + 0xc0, 0x30, 0x6d, 0x95, \ + 0x91, 0x11, 0x88, 0x35, \ + 0x78, 0x84, 0x42, 0x52, \ + 0xfc, 0x0c, 0x0a, 0x54, \ + 0xa1, 0x1b, 0x22, 0x5f, \ + 0x6d, 0xa5, 0x40, 0x51, \ + 0x8e, 0xb5, 0xc0, 0x30, \ + 0x02, 0x0a, 0x8f, 0x14, \ + 0x80, 0xe5, 0xfb, 0xfc, \ + 0x7a, 0x80, 0x02, 0x14, \ + 0xfa, 0x80, 0x06, 0x24, \ + 0x84, 0xd5, 0x83, 0x3b, \ + 0x6d, 0x85, 0x00, 0x19, \ + 0x83, 0x21, 0x40, 0x30, \ + 0x6d, 0xa1, 0x40, 0x21, \ + 0x02, 0x0a, 0xc0, 0x30, \ + 0x88, 0x95, 0xe3, 0xf0, \ + 0x8f, 0x14, 0x85, 0x55, \ + 0xfb, 0xfc, 0x02, 0x0a, \ + 0x02, 0x14, 0x82, 0x85, \ + 0x06, 0x24, 0x7a, 0x80, \ + 0x83, 0x3b, 0xfa, 0x80, \ + 0x82, 0x21, 0x80, 0xc5, \ + 0x6d, 0xa1, 0x40, 0x21, \ + 0x83, 0x95, 0xc0, 0x30, \ + 0x6d, 0x85, 0x00, 0x19, \ + 0x91, 0x21, 0x40, 0x30, \ + 0x78, 0x88, 0x44, 0x52, \ + 0xfc, 0x0c, 0x0a, 0x54, \ + 0x83, 0x21, 0x24, 0x5f, \ + 0x6d, 0xa1, 0x40, 0x21, \ + 0x02, 0x0a, 0x40, 0x30, \ + 0x40, 0x51, 0xe3, 0xf0, \ + 0xc0, 0x30, 0x6d, 0xa5, \ + 0x18, 0x10, 0x80, 0x65, \ + 0x40, 0xc1, 0xe4, 0x04, \ + 0xc0, 0x30, 0x6d, 0x85, \ + 0x6d, 0x85, 0x00, 0x19, \ + 0xa1, 0x1b, 0xc0, 0x30, \ + 0x00, 0x19, 0x82, 0xe5, \ + 0x40, 0x31, 0x64, 0x21, \ + 0xf3, 0xbc, 0x4a, 0x50, \ + 0x6d, 0xa5, 0x40, 0x51, \ + 0x42, 0x10, 0xc0, 0x30, \ + 0x40, 0x11, 0xf3, 0xf8, \ + 0xc0, 0x31, 0x64, 0x21, \ + 0x4a, 0x4a, 0x83, 0x15, \ + 0x82, 0x25, 0xe0, 0x8c, \ + 0x6d, 0x01, 0x00, 0x19, \ + 0x42, 0x4a, 0xc0, 0x2f, \ + 0x00, 0xb5, 0xe0, 0x88, \ + 0xdc, 0x40, 0x4a, 0x50, \ + 0xf3, 0xbc, 0x4a, 0x50, \ + 0x6d, 0xa5, 0x40, 0x51, \ + 0x04, 0x0a, 0xc0, 0x30, \ + 0x84, 0x15, 0xf0, 0x04, \ + 0xe0, 0x8c, 0x4a, 0x4a, \ + 0xa1, 0xab, 0x82, 0xa5, \ + 0x00, 0x25, 0x82, 0x65, \ + 0x40, 0x0c, 0x63, 0x5d, \ + 0x95, 0x2b, 0x83, 0x11, \ + 0xc4, 0x04, 0x02, 0x20, \ + 0x63, 0x5d, 0x40, 0x15, \ + 0x0c, 0xcc, 0xc0, 0x0c, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x68, 0x0c, 0x59, 0xc8, \ + 0x0c, 0x01, 0x80, 0x6b, \ + 0xc0, 0x30, 0x6d, 0x99, \ + 0xe1, 0xb4, 0x01, 0x0a, \ + 0x0c, 0x05, 0x80, 0xf5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0xe0, 0x00, 0x0a, \ + 0x0c, 0x39, 0x82, 0xb5, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x66, 0x51, 0x00, 0x60, \ + 0x05, 0x11, 0xc0, 0x54, \ + 0x0c, 0x0c, 0xa5, 0x21, \ + 0x39, 0xb8, 0xf2, 0x00, \ + 0x0c, 0x0c, 0xff, 0xd0, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x30, 0x6d, 0x95, \ + 0x80, 0xf5, 0xa1, 0x0b, \ + 0x6d, 0x1d, 0x0c, 0x09, \ + 0x40, 0x4a, 0xc0, 0x30, \ + 0x84, 0x05, 0xe0, 0x84, \ + 0x63, 0x5d, 0x0c, 0x05, \ + 0xa1, 0x0b, 0xc0, 0x0c, \ + 0x0c, 0x15, 0x84, 0x05, \ + 0xc0, 0x0c, 0x63, 0x41, \ + 0x82, 0x75, 0x83, 0x0b, \ + 0x6d, 0xa1, 0x0c, 0x09, \ + 0xa3, 0x0b, 0xc0, 0x30, \ + 0x0c, 0x09, 0x80, 0xd5, \ + 0xc0, 0x30, 0x6d, 0xb5, \ + 0xc6, 0x09, 0x05, 0x55, \ + 0x6d, 0x1d, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x86, 0x25, 0xe0, 0x88, \ + 0x6d, 0x35, 0x0c, 0x09, \ + 0xa1, 0x0b, 0xc0, 0x30, \ + 0x55, 0x18, 0x84, 0x85, \ + 0x8c, 0x0b, 0x68, 0x0c, \ + 0x6d, 0x9d, 0x0c, 0x11, \ + 0x0c, 0x29, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6d, 0x35, \ + 0x6d, 0x99, 0x0c, 0x01, \ + 0x46, 0x12, 0xc0, 0x30, \ + 0x87, 0x0d, 0xf1, 0x88, \ + 0x82, 0x23, 0x82, 0xf5, \ + 0x82, 0x95, 0x84, 0x0d, \ + 0x6d, 0xb5, 0x0c, 0x09, \ + 0x40, 0x00, 0xc0, 0x30, \ + 0x4c, 0x01, 0xf3, 0xf0, \ + 0xc0, 0x30, 0x6d, 0xb5, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x0c, 0x19, 0xdd, 0x93, \ + 0xc0, 0x30, 0x6d, 0xb5, \ + 0x6d, 0x9d, 0x4c, 0x01, \ + 0xc6, 0x19, 0xc0, 0x30, \ + 0x6d, 0xb5, 0x4c, 0x11, \ + 0xff, 0x35, 0xc0, 0x30, \ + 0x82, 0xc5, 0xa9, 0x3b, \ + 0x02, 0xa5, 0xa3, 0x3b, \ + 0x81, 0x88, 0x04, 0x24, \ + 0x84, 0x85, 0xa5, 0x3b, \ + 0x84, 0x85, 0xa7, 0x3b, \ + 0x5c, 0x08, 0xa1, 0x3b, \ + 0x29, 0x38, 0xda, 0x00, \ + 0x89, 0x75, 0xff, 0x70, \ + 0x5c, 0x08, 0xa1, 0x2b, \ + 0x45, 0x34, 0x9a, 0x00, \ + 0xec, 0x04, 0x04, 0x20, \ + 0x7b, 0xfd, 0x46, 0x30, \ + 0xc1, 0x3e, 0xdf, 0xff, \ + 0x81, 0x31, 0xff, 0x65, \ + 0x83, 0x31, 0x81, 0x45, \ + 0xdd, 0x93, 0x81, 0x25, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0x90, 0x01, 0x45, 0x27, \ + 0x4b, 0xfe, 0x01, 0x41, \ + 0xc0, 0x3f, 0x7f, 0xfd, \ + 0x04, 0x84, 0x85, 0x95, \ + 0x06, 0x74, 0xa3, 0x3b, \ + 0xc4, 0xc4, 0x88, 0x41, \ + 0xa0, 0x11, 0x46, 0x64, \ + 0x1b, 0xfd, 0x50, 0x80, \ + 0x4e, 0x70, 0x9f, 0xff, \ + 0x1f, 0xff, 0x1b, 0xfd, \ + 0x12, 0x74, 0x8a, 0x8f, \ + 0x58, 0xc0, 0x60, 0xe0, \ + 0x9f, 0xff, 0x1b, 0xfc, \ + 0x1b, 0xfd, 0x4c, 0x60, \ + 0xb0, 0x9f, 0x1f, 0xff, \ + 0x10, 0xc4, 0x51, 0x77, \ + 0x61, 0xc7, 0xe0, 0xc0, \ + 0xfb, 0x80, 0x0e, 0x74, \ + 0x18, 0xc4, 0x50, 0x67, \ + 0x2e, 0x8f, 0xfa, 0x80, \ + 0x40, 0x95, 0xb8, 0x6f, \ + 0x40, 0x85, 0xf8, 0x08, \ + 0x40, 0x65, 0xf8, 0x04, \ + 0xc5, 0x4d, 0xf0, 0x0c, \ + 0x0c, 0x0c, 0xf4, 0xf5, \ + 0xdd, 0x93, 0xf0, 0x1c, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0x90, 0x01, 0x45, 0x27, \ + 0x49, 0xfe, 0x01, 0x31, \ + 0xc0, 0x3f, 0x7f, 0xfd, \ + 0x7f, 0xf1, 0x0a, 0x0e, \ + 0x18, 0x0e, 0x40, 0x3f, \ + 0xc0, 0x3f, 0x70, 0x01, \ + 0x02, 0x65, 0x83, 0xd5, \ + 0x86, 0x31, 0x70, 0x0c, \ + 0xf8, 0x08, 0x02, 0x75, \ + 0xf8, 0x04, 0x02, 0x85, \ + 0x78, 0x10, 0x52, 0x62, \ + 0x40, 0x95, 0xf1, 0x67, \ + 0x12, 0x74, 0x78, 0x08, \ + 0x0a, 0x9f, 0xe0, 0xa0, \ + 0x2c, 0x9f, 0xe1, 0x77, \ + 0xe0, 0xc0, 0x0c, 0x84, \ + 0x78, 0x04, 0x40, 0x95, \ + 0x2e, 0x6f, 0x98, 0x6f, \ + 0x40, 0x65, 0xd1, 0x87, \ + 0x40, 0x85, 0xf0, 0x10, \ + 0xc5, 0x3d, 0xf8, 0x0c, \ + 0x0c, 0x0c, 0xf8, 0x75, \ + 0xdd, 0x93, 0xf0, 0x1c, \ + 0xf8, 0x00, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x60, 0x18, 0x00, 0x39, \ + 0x00, 0x49, 0x81, 0x21, \ + 0x83, 0x51, 0x60, 0x10, \ + 0x3e, 0x2e, 0xbc, 0x2e, \ + 0x27, 0x3b, 0xc5, 0x37, \ + 0xe6, 0x8e, 0x48, 0x4a, \ + 0x18, 0x26, 0x7c, 0x2e, \ + 0x02, 0xa5, 0x80, 0x00, \ + 0x80, 0x00, 0x0c, 0x06, \ + 0x81, 0xc1, 0x25, 0x3b, \ + 0x81, 0x51, 0x03, 0x61, \ + 0xa3, 0x3b, 0x80, 0xc5, \ + 0x01, 0xc1, 0x80, 0xf5, \ + 0x81, 0x51, 0x81, 0x61, \ + 0xbe, 0x6e, 0xbc, 0xce, \ + 0xc0, 0x08, 0xfc, 0x5e, \ + 0x00, 0x44, 0x00, 0x13, \ + 0x81, 0x95, 0xfa, 0x80, \ + 0x61, 0x0e, 0x48, 0x4a, \ + 0x82, 0x21, 0x9e, 0x5b, \ + 0x0a, 0x45, 0xc4, 0x47, \ + 0xc5, 0x46, 0xfc, 0x10, \ + 0xfa, 0x80, 0x08, 0x24, \ + 0xfc, 0xb5, 0xc1, 0x4d, \ + 0x0c, 0x0c, 0x98, 0xf1, \ + 0xdd, 0x93, 0xf0, 0x00, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0x60, 0x1c, 0x00, 0x39, \ + 0x00, 0x49, 0x81, 0x21, \ + 0x85, 0x51, 0x60, 0x14, \ + 0x7c, 0x2e, 0xbe, 0x2e, \ + 0x27, 0x3b, 0xc5, 0x37, \ + 0xe6, 0x8e, 0x48, 0x4a, \ + 0x18, 0x16, 0x7e, 0x2e, \ + 0x82, 0x05, 0x80, 0x00, \ + 0x81, 0xc1, 0x25, 0x3b, \ + 0xa3, 0x3b, 0x80, 0xa5, \ + 0x01, 0x51, 0x80, 0xd5, \ + 0xbe, 0x5e, 0x81, 0xc1, \ + 0xfe, 0xce, 0xfc, 0x2e, \ + 0x00, 0x13, 0xc2, 0x08, \ + 0xfa, 0x80, 0x00, 0x44, \ + 0x48, 0x4a, 0x81, 0xa5, \ + 0x0a, 0xf0, 0x61, 0x0e, \ + 0x82, 0x21, 0xe4, 0x10, \ + 0x0a, 0x45, 0xc4, 0x47, \ + 0xc5, 0x46, 0xfc, 0x10, \ + 0xfa, 0x80, 0x08, 0x24, \ + 0xfc, 0x95, 0xc1, 0x4d, \ + 0xdd, 0x93, 0xa0, 0xf1, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0x61, 0x0c, 0x07, 0x46, \ + 0xe1, 0x0c, 0x48, 0x86, \ + 0x07, 0x86, 0x0c, 0xc4, \ + 0x0c, 0x64, 0xe1, 0x0c, \ + 0xe1, 0x0c, 0x46, 0x06, \ + 0x4f, 0x06, 0x10, 0x54, \ + 0x0c, 0x44, 0xe1, 0x0c, \ + 0x1c, 0x34, 0x8b, 0x81, \ + 0xe4, 0x60, 0x0e, 0x00, \ + 0x1c, 0x98, 0x81, 0x75, \ + 0xec, 0x04, 0x10, 0x80, \ + 0xdd, 0x98, 0xf1, 0x96, \ + 0xa1, 0x8b, 0xd9, 0x96, \ + 0x18, 0x00, 0xfe, 0x35, \ + 0x89, 0x61, 0x64, 0x74, \ + 0x71, 0x78, 0x81, 0x55, \ + 0xec, 0x04, 0x0c, 0x60, \ + 0xa1, 0x6b, 0xd5, 0x76, \ + 0x1d, 0x51, 0xfe, 0x75, \ + 0x81, 0x55, 0x80, 0xcb, \ + 0x0a, 0x50, 0x71, 0x68, \ + 0xd1, 0x66, 0xec, 0x04, \ + 0xfe, 0x75, 0xa1, 0x5b, \ + 0x95, 0x41, 0x1c, 0x01, \ + 0x41, 0x58, 0x81, 0x55, \ + 0xec, 0x04, 0x08, 0x40, \ + 0xa1, 0x4b, 0xcd, 0x56, \ + 0x00, 0x20, 0xfe, 0x75, \ + 0x83, 0x31, 0x6c, 0x04, \ + 0x68, 0x80, 0x00, 0x34, \ + 0x08, 0x00, 0x81, 0xc1, \ + 0x00, 0x34, 0x6c, 0x04, \ + 0x0a, 0x00, 0xe8, 0x88, \ + 0x81, 0x01, 0x6c, 0x04, \ + 0x81, 0x61, 0x03, 0x45, \ + 0x0e, 0x54, 0xe9, 0x2d, \ + 0x00, 0x04, 0x48, 0xb0, \ + 0x82, 0x55, 0xdb, 0x80, \ + 0xe8, 0xb0, 0x0e, 0x44, \ + 0x00, 0x60, 0x00, 0x73, \ + 0x00, 0x34, 0xe4, 0x30, \ + 0x0e, 0x74, 0x68, 0x80, \ + 0x0e, 0x03, 0xfb, 0x80, \ + 0x86, 0xc1, 0x82, 0x61, \ + 0xfa, 0xb5, 0x8d, 0x1b, \ + 0x61, 0x04, 0x03, 0x86, \ + 0x6b, 0xfc, 0x01, 0x0a, \ + 0x04, 0x14, 0xc0, 0xff, \ + 0xdb, 0x80, 0x00, 0x04, \ + 0x04, 0x01, 0x00, 0x00, \ + 0x84, 0x0c, 0xc0, 0x80, \ + 0xf0, 0x1c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x84, 0x04, 0x0c, \ + 0x42, 0x16, 0x00, 0x6b, \ + 0x41, 0x38, 0xe0, 0x00, \ + 0x0c, 0x09, 0xe8, 0x10, \ + 0x40, 0x2f, 0x6d, 0x35, \ + 0xe1, 0x0c, 0x03, 0xc6, \ + 0x0e, 0x60, 0x04, 0x14, \ + 0xc0, 0xbf, 0x65, 0x21, \ + 0x00, 0x0c, 0xa9, 0x0b, \ + 0x04, 0x06, 0xe2, 0x00, \ + 0x71, 0x78, 0x8e, 0x0b, \ + 0x8c, 0x1b, 0x6f, 0xfc, \ + 0x64, 0x38, 0x02, 0x60, \ + 0x74, 0x98, 0x8e, 0x0b, \ + 0x0c, 0x29, 0xef, 0xfc, \ + 0x40, 0x2f, 0x6d, 0x29, \ + 0xe4, 0x60, 0x02, 0x60, \ + 0x6d, 0x31, 0x0c, 0x39, \ + 0x81, 0x01, 0x40, 0x2f, \ + 0x81, 0x41, 0x01, 0x95, \ + 0xfc, 0x08, 0x0c, 0x59, \ + 0x80, 0x75, 0xa1, 0x5b, \ + 0x82, 0x41, 0x45, 0x26, \ + 0xc7, 0x4d, 0x82, 0x21, \ + 0x0c, 0x19, 0xfe, 0x15, \ + 0x40, 0x2f, 0x6d, 0x2d, \ + 0xe4, 0x74, 0x04, 0x60, \ + 0x6d, 0x35, 0x0c, 0x49, \ + 0x46, 0x62, 0xc0, 0x2f, \ + 0x01, 0x95, 0xf0, 0x84, \ + 0x8c, 0x58, 0x9c, 0x31, \ + 0x80, 0x75, 0xa1, 0x5b, \ + 0x82, 0x01, 0x49, 0x16, \ + 0x82, 0x31, 0x02, 0x11, \ + 0xfe, 0x15, 0xc9, 0x0d, \ + 0x6d, 0x35, 0x0c, 0x29, \ + 0x8c, 0x0b, 0x40, 0x2f, \ + 0x6d, 0x31, 0x0c, 0x19, \ + 0x74, 0x78, 0xc0, 0x2f, \ + 0x8c, 0x0b, 0xef, 0xfc, \ + 0xf2, 0x84, 0x0c, 0x0c, \ + 0xe0, 0x10, 0x1b, 0xe8, \ + 0xfe, 0x7c, 0x04, 0x0c, \ + 0xed, 0x40, 0x1e, 0xf0, \ + 0x40, 0x16, 0x00, 0x6b, \ + 0x08, 0x8b, 0xe0, 0x04, \ + 0x4c, 0x01, 0x86, 0xab, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x01, 0x01, 0x84, 0xbb, \ + 0x5e, 0xb1, 0x82, 0x7b, \ + 0x8a, 0x9b, 0x61, 0x30, \ + 0x61, 0x20, 0x5e, 0xa1, \ + 0xe2, 0x80, 0x02, 0x06, \ + 0xe1, 0x10, 0x5e, 0x81, \ + 0x9f, 0x0e, 0xa1, 0x0e, \ + 0x5e, 0x01, 0x9d, 0x0e, \ + 0x5e, 0x01, 0xe0, 0xd0, \ + 0x5e, 0x01, 0xe0, 0xc0, \ + 0x8c, 0x0b, 0x60, 0xb0, \ + 0xe8, 0x0c, 0x7a, 0x28, \ + 0x9f, 0x21, 0x01, 0x11, \ + 0x8c, 0x3b, 0x0c, 0x0b, \ + 0xe8, 0x04, 0x6d, 0xc8, \ + 0x60, 0x90, 0x00, 0x74, \ + 0xe0, 0x04, 0x12, 0x4a, \ + 0x71, 0xc0, 0x40, 0x00, \ + 0xe1, 0x8c, 0x02, 0x74, \ + 0xce, 0x09, 0x11, 0x9e, \ + 0x71, 0x84, 0x52, 0x02, \ + 0x8f, 0x7e, 0x8f, 0x01, \ + 0x5e, 0x07, 0x93, 0x9e, \ + 0x82, 0x25, 0xe0, 0x6c, \ + 0xfa, 0x80, 0x00, 0x94, \ + 0x13, 0x0e, 0x90, 0x01, \ + 0x5e, 0x01, 0x91, 0x01, \ + 0x9f, 0x01, 0x60, 0xb0, \ + 0x40, 0xa2, 0x0b, 0x0e, \ + 0xb0, 0x0f, 0xf9, 0xac, \ + 0x04, 0xd5, 0xa1, 0x0b, \ + 0x06, 0x81, 0x0e, 0x60, \ + 0x3d, 0x01, 0xc0, 0x54, \ + 0x66, 0x81, 0x0e, 0x60, \ + 0x4c, 0x01, 0xc0, 0x54, \ + 0x40, 0x28, 0x65, 0x05, \ + 0x0c, 0x39, 0x81, 0x11, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x5c, 0x78, 0xbd, 0x21, \ + 0x8e, 0x0b, 0x6f, 0xd0, \ + 0x5e, 0xa1, 0x9b, 0xa1, \ + 0x9e, 0x99, 0xe1, 0x20, \ + 0x52, 0xa2, 0x15, 0x9e, \ + 0x07, 0x15, 0xf0, 0xac, \ + 0x0c, 0x39, 0x90, 0x93, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x4c, 0x01, 0xbd, 0x01, \ + 0x40, 0x28, 0x65, 0x05, \ + 0x3d, 0x21, 0x81, 0x11, \ + 0x5b, 0x18, 0x8e, 0x0b, \ + 0x16, 0x0a, 0xef, 0xd0, \ + 0x81, 0x01, 0x70, 0x00, \ + 0x44, 0x04, 0x00, 0xb0, \ + 0xf0, 0x00, 0x16, 0x4a, \ + 0x62, 0x00, 0x02, 0x0c, \ + 0xf0, 0x00, 0x14, 0x4a, \ + 0x62, 0x00, 0x04, 0x0c, \ + 0x14, 0x1a, 0x82, 0x93, \ + 0x81, 0x11, 0x70, 0x00, \ + 0x24, 0x05, 0x02, 0xa0, \ + 0x84, 0x93, 0x40, 0x00, \ + 0x70, 0x00, 0x10, 0x4a, \ + 0x06, 0x0c, 0x81, 0x21, \ + 0x10, 0x1a, 0x62, 0x00, \ + 0x04, 0x80, 0xf0, 0x00, \ + 0x40, 0x00, 0x24, 0x05, \ + 0x04, 0x8b, 0x86, 0x93, \ + 0x80, 0xbb, 0x82, 0xab, \ + 0xf4, 0x05, 0xe8, 0x9d, \ + 0x16, 0x0a, 0x11, 0x04, \ + 0x5e, 0xb1, 0xfb, 0xfc, \ + 0x5e, 0xa1, 0xe1, 0x30, \ + 0x15, 0x04, 0xe1, 0x20, \ + 0xe0, 0x04, 0x00, 0x1a, \ + 0xe1, 0x10, 0x5e, 0x81, \ + 0x0e, 0x00, 0x13, 0x04, \ + 0xfe, 0x7e, 0xe4, 0x20, \ + 0x20, 0x29, 0x1e, 0x75, \ + 0x04, 0x74, 0xc0, 0x00, \ + 0xa4, 0x03, 0xfa, 0x80, \ + 0x52, 0x00, 0x08, 0x05, \ + 0x13, 0x04, 0xf0, 0x7c, \ + 0xe0, 0x80, 0x44, 0x0a, \ + 0xa1, 0x9b, 0x84, 0x55, \ + 0xa0, 0x01, 0x84, 0x65, \ + 0x60, 0xd0, 0x5e, 0x01, \ + 0xf3, 0xc0, 0x42, 0x00, \ + 0x65, 0xc4, 0x02, 0x8a, \ + 0x02, 0x20, 0x8e, 0x0b, \ + 0x00, 0x20, 0x04, 0x3c, \ + 0x5e, 0x11, 0x8c, 0x04, \ + 0x81, 0x75, 0x80, 0xd0, \ + 0x00, 0x00, 0xa1, 0x9b, \ + 0x5e, 0x01, 0x84, 0x40, \ + 0x8e, 0x0b, 0x60, 0xd0, \ + 0xe0, 0xc0, 0x5e, 0x01, \ + 0x65, 0x00, 0x00, 0xf0, \ + 0xe4, 0xd0, 0x16, 0xf0, \ + 0x08, 0xf0, 0x3e, 0x0e, \ + 0x09, 0x2e, 0xe4, 0xc0, \ + 0xe5, 0x30, 0x04, 0xf0, \ + 0x60, 0x68, 0x5e, 0x17, \ + 0xe4, 0xb0, 0x0a, 0xf0, \ + 0x81, 0x11, 0x3c, 0xbe, \ + 0x8c, 0x3b, 0x0c, 0x0b, \ + 0xe8, 0x00, 0x4c, 0xc8, \ + 0x14, 0x0a, 0x0b, 0x14, \ + 0x1e, 0x09, 0xfb, 0xfc, \ + 0x02, 0x14, 0xe0, 0xc0, \ + 0x04, 0xc5, 0xfa, 0x80, \ + 0xfb, 0x84, 0x54, 0x02, \ + 0xe0, 0x80, 0x4e, 0x0a, \ + 0x44, 0x3c, 0x02, 0x70, \ + 0xcc, 0x04, 0x00, 0x70, \ + 0xc0, 0xd0, 0x5e, 0x11, \ + 0x13, 0x04, 0x82, 0x35, \ + 0xe0, 0x40, 0x12, 0x0a, \ + 0xc4, 0x40, 0x00, 0x00, \ + 0x60, 0xd0, 0x5e, 0x01, \ + 0x5e, 0x01, 0x8e, 0x0b, \ + 0x00, 0xf0, 0xe0, 0xc0, \ + 0x04, 0xf0, 0x64, 0xf0, \ + 0x3e, 0x0e, 0xe5, 0x20, \ + 0xe4, 0xc0, 0x08, 0xf0, \ + 0x0a, 0xf0, 0x3c, 0xbe, \ + 0x03, 0x11, 0xe4, 0xb0, \ + 0x4a, 0x18, 0x8c, 0x0b, \ + 0x8c, 0x3b, 0x68, 0x00, \ + 0x60, 0xc0, 0x1e, 0x09, \ + 0xfb, 0xfc, 0x10, 0x0a, \ + 0x50, 0x02, 0x06, 0xa5, \ + 0x89, 0x24, 0xfb, 0xa8, \ + 0xe0, 0x80, 0x44, 0x4a, \ + 0xcc, 0x04, 0x0e, 0x70, \ + 0xc0, 0xd0, 0x5e, 0x71, \ + 0x13, 0x04, 0x84, 0x35, \ + 0x5e, 0x01, 0xa1, 0x9b, \ + 0x82, 0x85, 0x80, 0xd0, \ + 0x5e, 0x01, 0xa0, 0x01, \ + 0x42, 0x00, 0x60, 0xd0, \ + 0x02, 0x8a, 0xf3, 0xc0, \ + 0x0e, 0x20, 0xe5, 0xc4, \ + 0x5e, 0x71, 0x8c, 0x08, \ + 0x5e, 0x71, 0x80, 0xd0, \ + 0x00, 0xf0, 0xe0, 0xc0, \ + 0x04, 0xf0, 0x64, 0xe0, \ + 0x3e, 0x0e, 0xe5, 0x10, \ + 0xe4, 0xc0, 0x08, 0xf0, \ + 0x0a, 0xf0, 0x3c, 0xbe, \ + 0x05, 0x71, 0xe4, 0xb0, \ + 0x0c, 0x0b, 0x85, 0x11, \ + 0x46, 0xf8, 0x8c, 0x3b, \ + 0x11, 0x14, 0xe8, 0x00, \ + 0x1e, 0x09, 0x8c, 0x3b, \ + 0x02, 0x2a, 0xe0, 0xc0, \ + 0x1e, 0x05, 0xe0, 0x00, \ + 0x40, 0x00, 0x40, 0x0d, \ + 0xfb, 0xa0, 0x42, 0x02, \ + 0x40, 0x29, 0x5e, 0x05, \ + 0x95, 0x04, 0xc0, 0x00, \ + 0x7a, 0x80, 0x04, 0x04, \ + 0x5b, 0x58, 0x8c, 0x0b, \ + 0x0c, 0x0b, 0xe8, 0x04, \ + 0x4d, 0xb8, 0x8c, 0x1b, \ + 0x1e, 0x17, 0xe8, 0x00, \ + 0x40, 0x46, 0x60, 0x68, \ + 0x00, 0x04, 0xe1, 0x08, \ + 0x01, 0x21, 0x85, 0x11, \ + 0x02, 0x06, 0x8b, 0x41, \ + 0x40, 0x00, 0x20, 0x01, \ + 0x40, 0x01, 0x08, 0xf6, \ + 0x00, 0x16, 0xc0, 0x00, \ + 0xe1, 0x08, 0x40, 0x86, \ + 0x00, 0x26, 0x80, 0x04, \ + 0xe1, 0x08, 0x40, 0xc6, \ + 0xe0, 0x6c, 0x1e, 0x07, \ + 0x02, 0x0c, 0x80, 0x04, \ + 0x00, 0x16, 0xe2, 0x00, \ + 0xe1, 0x08, 0x41, 0x06, \ + 0x02, 0x1c, 0x80, 0x04, \ + 0x00, 0x26, 0xe2, 0x00, \ + 0xe1, 0x08, 0x41, 0x46, \ + 0x00, 0x16, 0x80, 0x04, \ + 0xe1, 0x08, 0x41, 0x86, \ + 0x83, 0x11, 0x00, 0x04, \ + 0x41, 0xc6, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x08, \ + 0x00, 0x06, 0x00, 0x76, \ + 0x80, 0x04, 0xe1, 0x0c, \ + 0x8f, 0x34, 0x80, 0x26, \ + 0xa1, 0x04, 0x9d, 0x24, \ + 0x4c, 0x31, 0x9f, 0x14, \ + 0x40, 0x28, 0x64, 0xf1, \ + 0x4c, 0x21, 0xa9, 0x31, \ + 0x40, 0x28, 0x64, 0xfd, \ + 0x40, 0x01, 0x46, 0x16, \ + 0x44, 0x2a, 0xc0, 0x00, \ + 0x4c, 0x01, 0xe6, 0x92, \ + 0x40, 0x28, 0x64, 0xf5, \ + 0x4c, 0x11, 0xd0, 0x07, \ + 0x40, 0x28, 0x64, 0xf9, \ + 0x42, 0x1a, 0x84, 0x03, \ + 0x82, 0x03, 0xe6, 0x8e, \ + 0x6c, 0x59, 0x4c, 0x05, \ + 0x1e, 0xf0, 0x40, 0x08, \ + 0x0c, 0x0c, 0xe5, 0x40, \ + 0xdd, 0x93, 0xf6, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x80, 0xbb, 0x17, 0x04, \ + 0x88, 0x7b, 0x0a, 0x6b, \ + 0x84, 0x9b, 0x3e, 0x3e, \ + 0x00, 0xb0, 0x3c, 0x0e, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0x82, 0xab, 0x15, 0x84, \ + 0x8d, 0xc5, 0xfc, 0x0e, \ + 0x98, 0x18, 0x9c, 0x08, \ + 0x80, 0xf5, 0xc3, 0x0d, \ + 0x65, 0x0d, 0x16, 0x15, \ + 0x02, 0x0a, 0xc0, 0x0c, \ + 0x82, 0xd5, 0xe0, 0xe4, \ + 0x6d, 0x09, 0x16, 0x39, \ + 0xbb, 0x01, 0x40, 0x2f, \ + 0x65, 0x05, 0x56, 0x01, \ + 0x81, 0x11, 0x40, 0x28, \ + 0xbb, 0x21, 0x7c, 0x04, \ + 0xef, 0xcc, 0x7c, 0x28, \ + 0x20, 0x18, 0x9c, 0x08, \ + 0xfa, 0x80, 0x00, 0x04, \ + 0x70, 0x80, 0x44, 0xb2, \ + 0x04, 0x49, 0x83, 0x51, \ + 0xc0, 0x27, 0x6f, 0xc1, \ + 0xf1, 0x80, 0x46, 0x12, \ + 0x04, 0x34, 0xc9, 0x37, \ + 0x48, 0x40, 0x7a, 0x80, \ + 0x04, 0x54, 0xfb, 0xfc, \ + 0x84, 0x4f, 0xe8, 0x88, \ + 0x86, 0x95, 0xa1, 0x4b, \ + 0xf4, 0x10, 0x06, 0x8a, \ + 0x00, 0x00, 0x02, 0xb6, \ + 0x80, 0x00, 0x04, 0x16, \ + 0x42, 0x12, 0x3e, 0x34, \ + 0x02, 0x14, 0x91, 0x8c, \ + 0x04, 0x24, 0x9a, 0x80, \ + 0x06, 0x19, 0x88, 0x84, \ + 0x86, 0x03, 0x7c, 0x00, \ + 0x80, 0x16, 0xa4, 0x1f, \ + 0x00, 0x00, 0xa4, 0x08, \ + 0xa4, 0x06, 0xec, 0x04, \ + 0x84, 0x04, 0xbc, 0x14, \ + 0x84, 0x0e, 0x82, 0x01, \ + 0x00, 0x14, 0xa0, 0x18, \ + 0x04, 0xa0, 0x7a, 0x80, \ + 0x02, 0x00, 0xec, 0x04, \ + 0xe5, 0x2d, 0x64, 0x80, \ + 0x84, 0x55, 0xa0, 0x16, \ + 0x86, 0x55, 0xa1, 0xab, \ + 0xf6, 0x00, 0x02, 0x8a, \ + 0x4a, 0x17, 0x84, 0xf5, \ + 0xed, 0x44, 0x00, 0x00, \ + 0xf0, 0x80, 0x42, 0x10, \ + 0xa0, 0x06, 0xa2, 0x03, \ + 0x00, 0x00, 0x9c, 0x08, \ + 0x9c, 0x06, 0xec, 0x04, \ + 0x03, 0x0a, 0x81, 0xb5, \ + 0x82, 0x15, 0xf3, 0xfc, \ + 0xed, 0x88, 0x00, 0x00, \ + 0x9c, 0x08, 0xa0, 0x06, \ + 0xec, 0x08, 0x00, 0x00, \ + 0xa4, 0x08, 0x9c, 0x06, \ + 0xe4, 0x75, 0xa1, 0x0b, \ + 0x0c, 0x0c, 0x98, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xec, 0x40, 0x1e, 0xf0, \ + 0x66, 0xb1, 0x06, 0x00, \ + 0x91, 0x41, 0x40, 0x8e, \ + 0x81, 0x21, 0x01, 0x55, \ + 0x08, 0x40, 0x4d, 0x2e, \ + 0xa1, 0x4b, 0xec, 0x04, \ + 0x01, 0x31, 0xfe, 0x95, \ + 0x0b, 0xa5, 0x81, 0x51, \ + 0x7c, 0x2e, 0x81, 0x41, \ + 0xfa, 0x80, 0x10, 0x44, \ + 0x08, 0x8a, 0x7e, 0x1e, \ + 0x04, 0x28, 0xf4, 0x08, \ + 0x3e, 0x4e, 0x88, 0x81, \ + 0x88, 0xab, 0x81, 0x91, \ + 0x61, 0x94, 0x0c, 0x24, \ + 0xe1, 0x9c, 0x0e, 0x24, \ + 0x18, 0x24, 0x04, 0x69, \ + 0x2e, 0x6f, 0xe1, 0x84, \ + 0xe1, 0x8c, 0x0e, 0x24, \ + 0x90, 0xc9, 0x08, 0x79, \ + 0x9e, 0x29, 0x2e, 0x6f, \ + 0xa9, 0xc1, 0x38, 0x6f, \ + 0x58, 0x16, 0x48, 0x67, \ + 0x2c, 0x2f, 0x80, 0x00, \ + 0x0c, 0xf6, 0x8b, 0x61, \ + 0x0e, 0x24, 0x00, 0x00, \ + 0x85, 0x75, 0xfa, 0x80, \ + 0xe4, 0xa4, 0x4e, 0x0a, \ + 0x08, 0xa4, 0x86, 0x65, \ + 0x06, 0x5a, 0x7a, 0x80, \ + 0x06, 0x0a, 0xf0, 0x00, \ + 0x98, 0xbb, 0x70, 0x00, \ + 0x62, 0x00, 0x04, 0x1c, \ + 0xfa, 0x80, 0x02, 0x54, \ + 0x56, 0x60, 0x44, 0x47, \ + 0x05, 0x1b, 0xdc, 0x00, \ + 0xf0, 0x90, 0x44, 0x02, \ + 0x6e, 0xad, 0x04, 0x15, \ + 0x16, 0x06, 0x40, 0x08, \ + 0x96, 0x13, 0x80, 0x10, \ + 0x6e, 0xad, 0x44, 0x15, \ + 0x12, 0x1a, 0xc0, 0x08, \ + 0x82, 0x91, 0x60, 0x0c, \ + 0x7b, 0xfc, 0x0a, 0x0a, \ + 0xbc, 0x00, 0x54, 0x80, \ + 0xe2, 0x00, 0x0a, 0x0c, \ + 0xf6, 0x35, 0xb1, 0x9b, \ + 0x83, 0x39, 0x7c, 0x24, \ + 0x04, 0x0a, 0xbe, 0x44, \ + 0x82, 0x21, 0x7c, 0x0c, \ + 0xe2, 0x00, 0x02, 0x0c, \ + 0x82, 0x43, 0x7e, 0x14, \ + 0x05, 0x0a, 0x82, 0x11, \ + 0xe8, 0xc5, 0xf4, 0x3c, \ + 0x0c, 0x0c, 0xa0, 0xf1, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0xfa, 0x7c, 0x04, 0x0c, \ + 0xec, 0x30, 0x1e, 0xf0, \ + 0x40, 0x56, 0x00, 0x6b, \ + 0x4c, 0x01, 0xe0, 0x00, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x7c, 0x7e, 0x81, 0x71, \ + 0x70, 0xf8, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xe8, 0x00, \ + 0xc0, 0x2f, 0x66, 0x99, \ + 0x6d, 0x39, 0x4c, 0x71, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x00, 0x60, 0xe0, 0x80, \ + 0x40, 0x9e, 0x05, 0x81, \ + 0xc0, 0x00, 0x02, 0x06, \ + 0x4f, 0x6c, 0x69, 0x68, \ + 0xc0, 0x18, 0x04, 0x06, \ + 0x6d, 0x0d, 0x0c, 0x09, \ + 0xab, 0x0b, 0xc0, 0x2f, \ + 0x0c, 0x09, 0x86, 0x55, \ + 0xc0, 0x30, 0x6d, 0xc1, \ + 0xf3, 0xf0, 0x40, 0x00, \ + 0x4c, 0x01, 0xc2, 0x09, \ + 0x40, 0x30, 0x6d, 0xc1, \ + 0x49, 0x78, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xef, 0xf8, \ + 0x40, 0x30, 0x6d, 0xc1, \ + 0x4c, 0x11, 0x83, 0x11, \ + 0xc0, 0x31, 0x64, 0x29, \ + 0xf3, 0xf0, 0x40, 0x00, \ + 0x4c, 0x01, 0xc4, 0x09, \ + 0xc0, 0x30, 0x6d, 0xc1, \ + 0x81, 0x81, 0x01, 0x91, \ + 0x0c, 0x05, 0x91, 0xa5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe1, 0x68, 0x00, 0x0a, \ + 0x0b, 0xa1, 0xa6, 0x65, \ + 0x16, 0xa4, 0x81, 0x01, \ + 0xce, 0xbd, 0xfa, 0x80, \ + 0x00, 0x4a, 0x98, 0x35, \ + 0x96, 0xc5, 0xfb, 0xfc, \ + 0x70, 0x00, 0x10, 0x0a, \ + 0x01, 0x11, 0x94, 0x0b, \ + 0x84, 0x05, 0x81, 0x21, \ + 0xf0, 0x04, 0x10, 0x0a, \ + 0x00, 0x00, 0x00, 0x06, \ + 0x9c, 0x00, 0x42, 0xa0, \ + 0x04, 0x06, 0x02, 0x25, \ + 0x10, 0x0a, 0x80, 0x00, \ + 0x82, 0xb5, 0xf0, 0x08, \ + 0x81, 0x11, 0x01, 0x01, \ + 0x4c, 0x01, 0x94, 0x2b, \ + 0xc0, 0x28, 0x64, 0xf5, \ + 0x64, 0xf9, 0x4c, 0x11, \ + 0x4c, 0x21, 0xc0, 0x28, \ + 0xc0, 0x28, 0x64, 0xfd, \ + 0x6f, 0xf8, 0x45, 0x88, \ + 0x02, 0xf0, 0x8c, 0x0b, \ + 0x8c, 0x0b, 0x64, 0x20, \ + 0xe8, 0x00, 0x50, 0x08, \ + 0x44, 0x86, 0x7c, 0x04, \ + 0x0c, 0x19, 0xe0, 0x3c, \ + 0xc0, 0x2f, 0x66, 0x99, \ + 0xe0, 0x0a, 0x40, 0x0a, \ + 0xe0, 0x80, 0x42, 0x0a, \ + 0xfc, 0x0e, 0xcf, 0x07, \ + 0x02, 0x94, 0x84, 0x05, \ + 0x04, 0x60, 0x7a, 0x80, \ + 0xc0, 0x9e, 0x65, 0x80, \ + 0xe0, 0x88, 0x06, 0x14, \ + 0x82, 0x23, 0x04, 0x33, \ + 0x44, 0xa1, 0x8c, 0x0e, \ + 0xbe, 0x14, 0xe4, 0x00, \ + 0xe5, 0x00, 0x44, 0x11, \ + 0x6b, 0x49, 0x0c, 0x1b, \ + 0x82, 0x91, 0x40, 0x17, \ + 0x86, 0x05, 0xc3, 0x0d, \ + 0x0c, 0x09, 0xcf, 0xbd, \ + 0x40, 0x28, 0x05, 0x05, \ + 0xc4, 0x04, 0x14, 0xa0, \ + 0x0c, 0x1b, 0xea, 0x75, \ + 0xc0, 0x17, 0x6b, 0x45, \ + 0x82, 0x05, 0xc1, 0x1d, \ + 0x6b, 0x41, 0x0c, 0x1b, \ + 0x51, 0xf6, 0x40, 0x17, \ + 0xc3, 0x0d, 0xe0, 0x0c, \ + 0x03, 0xa5, 0x88, 0x15, \ + 0xbe, 0x84, 0xb9, 0x01, \ + 0x14, 0x0a, 0x83, 0xc5, \ + 0x51, 0xf6, 0x70, 0x14, \ + 0x84, 0xf5, 0xe0, 0x0c, \ + 0x10, 0x0a, 0xbe, 0x84, \ + 0x84, 0x75, 0xf0, 0x0c, \ + 0x6b, 0x45, 0x0c, 0x1b, \ + 0xc1, 0x1d, 0xc0, 0x17, \ + 0x0c, 0x1b, 0x82, 0x05, \ + 0x40, 0x17, 0x6b, 0x41, \ + 0xe0, 0x0c, 0x51, 0xf6, \ + 0x80, 0xf5, 0xc3, 0x0d, \ + 0x4c, 0x01, 0xb7, 0x01, \ + 0x40, 0x28, 0x65, 0x05, \ + 0xe0, 0x0c, 0x51, 0xf6, \ + 0xf4, 0x0c, 0x11, 0x0a, \ + 0x0c, 0x09, 0x86, 0x35, \ + 0xc0, 0x28, 0x65, 0x05, \ + 0xfb, 0xfc, 0x00, 0x4a, \ + 0x0c, 0x29, 0x84, 0x65, \ + 0x40, 0x28, 0x64, 0xf1, \ + 0xe4, 0x04, 0x00, 0x80, \ + 0x02, 0x04, 0x3e, 0x0e, \ + 0x43, 0x98, 0xfa, 0x80, \ + 0x8c, 0x0b, 0x68, 0x00, \ + 0xeb, 0x7d, 0x80, 0x7b, \ + 0xb5, 0x01, 0xd8, 0x25, \ + 0x65, 0x05, 0x4c, 0x01, \ + 0x6f, 0xf8, 0xc0, 0x28, \ + 0x8c, 0x0b, 0x6f, 0xec, \ + 0x69, 0x71, 0x4c, 0x91, \ + 0x83, 0x01, 0x40, 0x27, \ + 0x6d, 0x39, 0x4c, 0x01, \ + 0x8c, 0x0b, 0x40, 0x30, \ + 0xef, 0xbc, 0x69, 0x88, \ + 0x64, 0x51, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x31, \ + 0x81, 0x01, 0x60, 0x94, \ + 0x64, 0x29, 0x4c, 0x01, \ + 0x40, 0x06, 0x40, 0x31, \ + 0x54, 0x58, 0xc0, 0x00, \ + 0x0c, 0x29, 0xcf, 0xd0, \ + 0xc0, 0x28, 0x65, 0x05, \ + 0x00, 0x60, 0xa1, 0x2b, \ + 0x40, 0x54, 0x06, 0x51, \ + 0xc0, 0x00, 0x02, 0x06, \ + 0x4f, 0xcc, 0x5e, 0xa8, \ + 0xc0, 0x00, 0x46, 0x56, \ + 0x6f, 0xb4, 0x7b, 0xa8, \ + 0x98, 0xf1, 0x8c, 0x0b, \ + 0xf2, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x1c, 0x04, 0xcc, \ + 0x65, 0x01, 0x00, 0x59, \ + 0xa7, 0x1b, 0x40, 0x28, \ + 0x18, 0x24, 0x01, 0x31, \ + 0x15, 0x3c, 0xe1, 0x8c, \ + 0x05, 0x41, 0x8f, 0x61, \ + 0x80, 0x04, 0x0f, 0x56, \ + 0x02, 0x16, 0x04, 0x85, \ + 0x25, 0x1b, 0x80, 0x00, \ + 0xe0, 0x08, 0x4e, 0xa6, \ + 0x00, 0x00, 0x02, 0x06, \ + 0x80, 0x00, 0x10, 0x06, \ + 0x23, 0x1b, 0x82, 0x65, \ + 0x02, 0x95, 0x81, 0x41, \ + 0x03, 0x56, 0x81, 0x11, \ + 0x83, 0x41, 0x60, 0x04, \ + 0x81, 0x11, 0x29, 0x16, \ + 0x12, 0xf0, 0x83, 0x81, \ + 0xb0, 0x9f, 0xe4, 0x50, \ + 0x38, 0x63, 0xa4, 0x76, \ + 0xfb, 0xfc, 0x0a, 0x4a, \ + 0x70, 0x1c, 0x58, 0x20, \ + 0x0a, 0x64, 0x81, 0x21, \ + 0xff, 0x61, 0x7a, 0x80, \ + 0x00, 0x00, 0x04, 0x86, \ + 0xe8, 0xb0, 0x18, 0x64, \ + 0x08, 0x44, 0x05, 0x55, \ + 0x4c, 0x12, 0xfa, 0x80, \ + 0x10, 0xf0, 0x70, 0x88, \ + 0x00, 0x63, 0xe4, 0x50, \ + 0x0c, 0x79, 0x8b, 0x1b, \ + 0x40, 0x27, 0x6f, 0xc1, \ + 0xe1, 0xfc, 0x0c, 0x14, \ + 0x0c, 0x64, 0x82, 0x63, \ + 0x4c, 0x12, 0xfc, 0x00, \ + 0x10, 0x69, 0xf1, 0x98, \ + 0x4e, 0x72, 0x7c, 0x18, \ + 0x0c, 0x7f, 0x98, 0x30, \ + 0xa1, 0x7b, 0x81, 0x61, \ + 0x50, 0x04, 0x50, 0x70, \ + 0xc1, 0x84, 0x0e, 0x74, \ + 0x4c, 0x62, 0x7e, 0x75, \ + 0x06, 0x34, 0xd0, 0xa0, \ + 0x88, 0x13, 0x7a, 0x80, \ + 0x8a, 0x1d, 0x8c, 0x33, \ + 0x00, 0x34, 0xf6, 0xa5, \ + 0x0c, 0xcc, 0xfa, 0x80, \ + 0xdd, 0x93, 0xf0, 0x1c, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x82, 0x7b, 0x00, 0x6b, \ + 0x6d, 0x81, 0x0c, 0x05, \ + 0x91, 0x21, 0x40, 0x0b, \ + 0x61, 0x51, 0x0c, 0x15, \ + 0x49, 0xd8, 0xc0, 0x0a, \ + 0x0c, 0x29, 0xef, 0xa0, \ + 0x40, 0x2f, 0x65, 0xf1, \ + 0xe1, 0x18, 0x42, 0x46, \ + 0x83, 0x51, 0x04, 0x14, \ + 0x84, 0x18, 0x84, 0x48, \ + 0x63, 0x99, 0x0c, 0x35, \ + 0x9e, 0x49, 0xc0, 0x0c, \ + 0x8e, 0x19, 0xc9, 0x17, \ + 0x02, 0x24, 0x23, 0x5f, \ + 0x4f, 0x18, 0xe0, 0x90, \ + 0x04, 0x54, 0x6f, 0xa0, \ + 0x77, 0x58, 0xe8, 0x90, \ + 0x80, 0x8b, 0x6f, 0xe8, \ + 0x60, 0x10, 0x02, 0x06, \ + 0x50, 0xb8, 0x90, 0x2b, \ + 0x8e, 0x3b, 0x6f, 0xa0, \ + 0x65, 0x0d, 0x0c, 0x15, \ + 0x02, 0x0a, 0xc0, 0x0c, \ + 0x41, 0xfe, 0xe0, 0xe8, \ + 0xa1, 0x0b, 0x9f, 0xfc, \ + 0x08, 0x60, 0x82, 0xe5, \ + 0x40, 0x54, 0x66, 0x51, \ + 0xfb, 0x00, 0x04, 0x04, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x81, 0x11, 0x40, 0x2f, \ + 0x0c, 0x0c, 0x88, 0x0b, \ + 0x13, 0xd8, 0xf2, 0x0c, \ + 0x0c, 0x0c, 0xff, 0xcc, \ + 0xdd, 0x93, 0xf2, 0x0c, \ + 0x66, 0x8d, 0x00, 0x1b, \ + 0x04, 0x06, 0x40, 0x18, \ + 0x40, 0x21, 0xe0, 0x0c, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x62, 0x19, 0x00, 0x29, \ + 0x00, 0x39, 0xc0, 0x15, \ + 0x40, 0x2f, 0x65, 0x61, \ + 0x70, 0x05, 0x42, 0x10, \ + 0x42, 0x10, 0xc0, 0xc1, \ + 0xc0, 0x1e, 0x7f, 0xf9, \ + 0x66, 0x8d, 0x40, 0x13, \ + 0x00, 0x19, 0xc0, 0x18, \ + 0xc0, 0x2f, 0x65, 0xa9, \ + 0x6d, 0x25, 0x40, 0x21, \ + 0x85, 0x21, 0x40, 0x30, \ + 0x6d, 0x2d, 0x40, 0x31, \ + 0x40, 0x21, 0xc0, 0x30, \ + 0xc0, 0x23, 0x61, 0x95, \ + 0x6d, 0xd1, 0x40, 0x21, \ + 0x40, 0x11, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6d, 0xd5, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xe0, 0x14, 0x02, 0x29, \ + 0x66, 0x8d, 0x00, 0x3b, \ + 0x82, 0x29, 0xc0, 0x18, \ + 0xfc, 0x00, 0x06, 0x34, \ + 0xa4, 0x3f, 0x04, 0x24, \ + 0x66, 0x8d, 0x40, 0x33, \ + 0x06, 0x34, 0x40, 0x18, \ + 0x02, 0x49, 0xfc, 0x34, \ + 0x40, 0x25, 0xe0, 0x20, \ + 0xc0, 0x0c, 0x63, 0x41, \ + 0xda, 0x47, 0x82, 0x49, \ + 0x40, 0x43, 0xa6, 0x4f, \ + 0x40, 0x18, 0x66, 0x8d, \ + 0xfc, 0x0c, 0x08, 0x44, \ + 0xe0, 0x24, 0x02, 0x39, \ + 0x90, 0x39, 0xc6, 0x37, \ + 0x40, 0x43, 0xa6, 0x4f, \ + 0x40, 0x18, 0x66, 0x8d, \ + 0xfc, 0x10, 0x08, 0x44, \ + 0xe0, 0x28, 0x02, 0x39, \ + 0xa0, 0x39, 0xc8, 0x37, \ + 0x40, 0x43, 0xa6, 0x4f, \ + 0x40, 0x18, 0x66, 0x8d, \ + 0x73, 0x7d, 0x48, 0x40, \ + 0x02, 0x39, 0xc0, 0xff, \ + 0xca, 0x37, 0xe0, 0x2c, \ + 0xf0, 0x80, 0x46, 0x30, \ + 0x65, 0x65, 0x00, 0x39, \ + 0xa6, 0x4f, 0x40, 0x2f, \ + 0x66, 0x8d, 0x40, 0x43, \ + 0x02, 0x49, 0xc0, 0x18, \ + 0xa7, 0x3b, 0xe0, 0x10, \ + 0x6d, 0x21, 0x40, 0x41, \ + 0x80, 0xf5, 0xc0, 0x30, \ + 0x6d, 0x01, 0x00, 0x29, \ + 0x44, 0x4a, 0xc0, 0x2f, \ + 0x80, 0xc5, 0xe0, 0x88, \ + 0xe0, 0x1c, 0x02, 0x29, \ + 0x66, 0x89, 0x40, 0x23, \ + 0x02, 0x29, 0xc0, 0x18, \ + 0x00, 0x39, 0xe0, 0x38, \ + 0xc0, 0x30, 0x6d, 0xc1, \ + 0x6d, 0xd9, 0x00, 0x59, \ + 0x82, 0x29, 0xc0, 0x30, \ + 0xfc, 0x00, 0x06, 0x34, \ + 0x60, 0x18, 0x02, 0x29, \ + 0x40, 0x31, 0xa4, 0x3f, \ + 0x40, 0x30, 0x6d, 0xc1, \ + 0xfc, 0x04, 0x06, 0x34, \ + 0x60, 0x3c, 0x02, 0x49, \ + 0xf3, 0xf8, 0x4a, 0x50, \ + 0x6d, 0x35, 0x40, 0x21, \ + 0x04, 0x00, 0x40, 0x30, \ + 0xc0, 0x71, 0x64, 0xc1, \ + 0x84, 0x49, 0xc2, 0x47, \ + 0x40, 0x41, 0xa6, 0x4f, \ + 0x40, 0x30, 0x6d, 0xc1, \ + 0xf3, 0xec, 0x48, 0x40, \ + 0xe0, 0x40, 0x02, 0x39, \ + 0xa6, 0x5f, 0x82, 0x39, \ + 0x6d, 0xd9, 0x40, 0x51, \ + 0x02, 0x39, 0xc0, 0x30, \ + 0xc4, 0x37, 0xe0, 0x44, \ + 0xa6, 0x4f, 0x88, 0x39, \ + 0x6d, 0xc1, 0x40, 0x41, \ + 0x02, 0x39, 0xc0, 0x30, \ + 0x00, 0x49, 0xe0, 0x48, \ + 0xc0, 0x30, 0x6d, 0xb5, \ + 0x46, 0x30, 0xca, 0x37, \ + 0x48, 0x40, 0x70, 0x80, \ + 0xa6, 0x4f, 0xf3, 0x7c, \ + 0x6d, 0xb5, 0x40, 0x41, \ + 0x02, 0x39, 0xc0, 0x30, \ + 0x00, 0x49, 0xe0, 0x4c, \ + 0xc0, 0x30, 0x6d, 0xf1, \ + 0x08, 0x44, 0x82, 0x39, \ + 0xa6, 0x4f, 0xfc, 0x00, \ + 0x6d, 0xf1, 0x40, 0x41, \ + 0x08, 0x44, 0x40, 0x30, \ + 0x02, 0x39, 0xfc, 0x04, \ + 0xc2, 0x37, 0xe0, 0x50, \ + 0xa8, 0x3f, 0x84, 0x39, \ + 0x6d, 0xf1, 0x40, 0x31, \ + 0x06, 0x34, 0x40, 0x30, \ + 0x02, 0x49, 0xfc, 0x08, \ + 0xc4, 0x47, 0xe0, 0x54, \ + 0xa8, 0x3f, 0x88, 0x49, \ + 0x6d, 0xf1, 0x40, 0x31, \ + 0x06, 0x34, 0x40, 0x30, \ + 0x02, 0x49, 0xfc, 0x0c, \ + 0xc6, 0x47, 0xe0, 0x58, \ + 0xa8, 0x3f, 0x90, 0x49, \ + 0x6d, 0xf1, 0x40, 0x31, \ + 0x46, 0x30, 0x40, 0x30, \ + 0x02, 0x49, 0xf3, 0xbc, \ + 0xc8, 0x47, 0xe0, 0x5c, \ + 0xa8, 0x3f, 0xa0, 0x49, \ + 0x6d, 0xf1, 0x40, 0x31, \ + 0x9f, 0x31, 0x40, 0x30, \ + 0x02, 0x49, 0x81, 0x65, \ + 0x06, 0x30, 0x60, 0x30, \ + 0xc9, 0x46, 0xec, 0x04, \ + 0xfe, 0x55, 0xa1, 0x3b, \ + 0xe0, 0x34, 0x02, 0x29, \ + 0x64, 0xfd, 0x40, 0x21, \ + 0x02, 0x19, 0xc0, 0x1c, \ + 0x40, 0x11, 0xe0, 0x30, \ + 0xc0, 0x23, 0x6b, 0xb9, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x0c, 0x05, 0x80, 0x6b, \ + 0xc0, 0x0b, 0x6d, 0x8d, \ + 0x6d, 0x0d, 0x0c, 0x19, \ + 0x0c, 0x29, 0xc0, 0x2f, \ + 0xc0, 0x30, 0x6d, 0x29, \ + 0x66, 0x59, 0x0c, 0x39, \ + 0x3e, 0x0e, 0xc0, 0x2f, \ + 0x17, 0x26, 0x81, 0x01, \ + 0x95, 0x36, 0xa3, 0x1b, \ + 0x82, 0x25, 0x93, 0x06, \ + 0x82, 0x85, 0xa7, 0x1b, \ + 0x02, 0x16, 0xa5, 0x1b, \ + 0x5e, 0x11, 0x80, 0x00, \ + 0x82, 0xe5, 0x80, 0x30, \ + 0x45, 0xf6, 0x19, 0x06, \ + 0x01, 0xc5, 0xe0, 0x0c, \ + 0x0c, 0x19, 0x83, 0x11, \ + 0x40, 0x2f, 0x6d, 0x01, \ + 0x19, 0x26, 0x83, 0x21, \ + 0xc5, 0x17, 0xfd, 0x21, \ + 0xa4, 0x1f, 0xc2, 0x1b, \ + 0x0c, 0x39, 0x85, 0x21, \ + 0x40, 0x2f, 0x66, 0x55, \ + 0xe4, 0x10, 0x0e, 0xf0, \ + 0x8b, 0x21, 0x11, 0x26, \ + 0x04, 0x70, 0x23, 0x26, \ + 0x1b, 0x16, 0xe4, 0x4c, \ + 0x25, 0x36, 0x82, 0x39, \ + 0x70, 0x11, 0x06, 0x16, \ + 0x44, 0x31, 0xc0, 0x40, \ + 0x0c, 0x39, 0xf0, 0x08, \ + 0xc0, 0x2f, 0x67, 0x41, \ + 0x1f, 0x16, 0x9d, 0x16, \ + 0x2d, 0x06, 0x83, 0x11, \ + 0x8a, 0x16, 0x8c, 0x0b, \ + 0xa1, 0x16, 0x88, 0x16, \ + 0xa9, 0x16, 0xa7, 0x16, \ + 0xb1, 0x16, 0xab, 0x16, \ + 0x6a, 0x98, 0xb7, 0x36, \ + 0x0c, 0x0b, 0xef, 0xfc, \ + 0x6d, 0x18, 0x8e, 0x1b, \ + 0xb8, 0xf1, 0xef, 0xfc, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0xec, 0x60, 0x1e, 0xf0, \ + 0x60, 0x04, 0x04, 0x06, \ + 0x00, 0x6b, 0x83, 0x11, \ + 0x0f, 0x26, 0x81, 0x01, \ + 0x8b, 0x0e, 0x8b, 0x21, \ + 0x89, 0x16, 0xbc, 0x0e, \ + 0x4c, 0x11, 0x8b, 0x06, \ + 0xc0, 0x30, 0x6d, 0x29, \ + 0x99, 0x16, 0x8d, 0x06, \ + 0x9d, 0x06, 0x9b, 0x26, \ + 0x91, 0x06, 0x9f, 0x16, \ + 0x8c, 0x0b, 0x09, 0x0e, \ + 0x95, 0x16, 0x93, 0x16, \ + 0x68, 0x18, 0x97, 0x16, \ + 0x1e, 0x1b, 0xef, 0xfc, \ + 0x6a, 0x98, 0x8c, 0x0b, \ + 0xb0, 0xf1, 0xef, 0xfc, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x0e, 0xf0, 0x00, 0x6b, \ + 0x0c, 0x05, 0xe4, 0x10, \ + 0x40, 0x0b, 0x6d, 0x8d, \ + 0x0c, 0x19, 0x81, 0x21, \ + 0xc0, 0x15, 0x61, 0xd1, \ + 0x6d, 0x29, 0x0c, 0x39, \ + 0xbe, 0x0e, 0xc0, 0x30, \ + 0x66, 0x59, 0x0c, 0x09, \ + 0x11, 0x16, 0xc0, 0x2f, \ + 0x21, 0x16, 0x89, 0x11, \ + 0x95, 0x06, 0x8f, 0x11, \ + 0x66, 0x55, 0x0c, 0x09, \ + 0x17, 0x36, 0xc0, 0x2f, \ + 0x70, 0x11, 0x06, 0x16, \ + 0x23, 0x16, 0xc0, 0x40, \ + 0xe4, 0x4c, 0x02, 0x70, \ + 0x70, 0x08, 0x42, 0x31, \ + 0x0c, 0x39, 0x82, 0x09, \ + 0xc0, 0x2f, 0x67, 0x41, \ + 0x83, 0x01, 0x25, 0x06, \ + 0x84, 0x06, 0x86, 0x06, \ + 0xa7, 0x06, 0x93, 0x26, \ + 0xab, 0x06, 0xa9, 0x06, \ + 0xb1, 0x06, 0xad, 0x26, \ + 0x99, 0x26, 0xb7, 0x36, \ + 0x9d, 0x06, 0x9b, 0x06, \ + 0x8c, 0x0b, 0x1f, 0x06, \ + 0xef, 0xfc, 0x63, 0xc8, \ + 0x8e, 0x1b, 0x0c, 0x0b, \ + 0xef, 0xfc, 0x66, 0x48, \ + 0x0c, 0x0c, 0xb8, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x04, \ + 0xfa, 0x04, 0x04, 0x0c, \ + 0xec, 0x70, 0x1e, 0xf0, \ + 0x8d, 0x31, 0x00, 0x6b, \ + 0x6d, 0x8d, 0x0c, 0x05, \ + 0x0e, 0xf0, 0x40, 0x0b, \ + 0x0c, 0x29, 0xe4, 0x10, \ + 0xc0, 0x2f, 0x66, 0x59, \ + 0x61, 0xd1, 0x0c, 0x19, \ + 0xbe, 0x0e, 0xc0, 0x15, \ + 0x6d, 0x29, 0x0c, 0x09, \ + 0x15, 0x26, 0xc0, 0x30, \ + 0x23, 0x36, 0x87, 0x21, \ + 0x70, 0x11, 0x06, 0x16, \ + 0x97, 0x06, 0xc0, 0x40, \ + 0x66, 0x55, 0x0c, 0x09, \ + 0x11, 0x16, 0xc0, 0x2f, \ + 0x21, 0x26, 0x81, 0x11, \ + 0x13, 0x16, 0x83, 0x21, \ + 0x25, 0x06, 0x82, 0x09, \ + 0xe4, 0x4c, 0x00, 0x70, \ + 0xf0, 0x08, 0x40, 0x31, \ + 0x00, 0x26, 0x82, 0x26, \ + 0xa7, 0x26, 0x8c, 0x0b, \ + 0xab, 0x26, 0xa9, 0x26, \ + 0xb1, 0x26, 0xad, 0x16, \ + 0x99, 0x16, 0xb7, 0x16, \ + 0x9d, 0x26, 0x9b, 0x26, \ + 0x5f, 0xb8, 0x9f, 0x26, \ + 0x0c, 0x0b, 0xef, 0xfc, \ + 0x62, 0x38, 0x8e, 0x1b, \ + 0xb8, 0xf1, 0xef, 0xfc, \ + 0xf2, 0x04, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0xec, 0x60, 0x1e, 0xf0, \ + 0x83, 0x21, 0x00, 0x6b, \ + 0x66, 0x55, 0x0c, 0x09, \ + 0x08, 0x16, 0x40, 0x2f, \ + 0xc0, 0x40, 0x60, 0x01, \ + 0x81, 0x31, 0x3e, 0x4e, \ + 0x08, 0x16, 0x3c, 0x3e, \ + 0x02, 0x04, 0xe0, 0x50, \ + 0x82, 0x09, 0x61, 0x8c, \ + 0x42, 0x12, 0x1d, 0x06, \ + 0x0d, 0x4c, 0xf8, 0x88, \ + 0x9f, 0x36, 0x8c, 0x0b, \ + 0xa3, 0x36, 0xa1, 0x36, \ + 0xa7, 0x26, 0xa5, 0x16, \ + 0xab, 0x26, 0xa9, 0x36, \ + 0xaf, 0x36, 0xad, 0x36, \ + 0x93, 0x26, 0x91, 0x36, \ + 0x97, 0x26, 0x95, 0x26, \ + 0xef, 0xfc, 0x5c, 0xa8, \ + 0x8c, 0x0b, 0x1e, 0x1b, \ + 0xef, 0xfc, 0x5f, 0x28, \ + 0x0c, 0x0c, 0xb0, 0xf1, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0xec, 0x60, 0x1e, 0xf0, \ + 0x04, 0x16, 0x00, 0x6b, \ + 0xc4, 0x00, 0x60, 0x11, \ + 0x6d, 0x0d, 0x0c, 0x09, \ + 0x06, 0x16, 0x40, 0x2f, \ + 0x3e, 0x2e, 0xe0, 0x50, \ + 0x8d, 0x3c, 0x83, 0x21, \ + 0x60, 0x34, 0x00, 0x4a, \ + 0x02, 0x0c, 0x81, 0x01, \ + 0xbc, 0x0e, 0xe2, 0x00, \ + 0x9f, 0x06, 0x9d, 0x06, \ + 0xa3, 0x26, 0xa1, 0x06, \ + 0xa7, 0x06, 0xa5, 0x16, \ + 0xab, 0x06, 0xa9, 0x06, \ + 0xaf, 0x06, 0xad, 0x26, \ + 0x8c, 0x0b, 0x11, 0x06, \ + 0x95, 0x26, 0x93, 0x26, \ + 0x59, 0xb8, 0x97, 0x26, \ + 0x1e, 0x1b, 0xef, 0xfc, \ + 0x5c, 0x38, 0x8c, 0x0b, \ + 0xb0, 0xf1, 0xef, 0xfc, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x02, 0x06, 0x00, 0x6b, \ + 0x4f, 0x58, 0xe2, 0x80, \ + 0x40, 0x46, 0xe8, 0x08, \ + 0x0f, 0x86, 0x60, 0x04, \ + 0x4c, 0x01, 0xe0, 0x10, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x81, 0x95, 0x8c, 0x8b, \ + 0x60, 0x01, 0x10, 0x09, \ + 0x41, 0x18, 0xc0, 0x28, \ + 0x61, 0x06, 0xe8, 0x00, \ + 0xec, 0x04, 0x0e, 0x70, \ + 0xfc, 0xf5, 0xa1, 0x7b, \ + 0x45, 0x76, 0x01, 0x11, \ + 0x0c, 0x0b, 0xe0, 0x00, \ + 0x0c, 0x0c, 0x8c, 0x3b, \ + 0x22, 0x38, 0xf2, 0x0c, \ + 0xc1, 0xc3, 0xe0, 0x00, \ + 0x61, 0x84, 0x04, 0x04, \ + 0xe1, 0x8c, 0x02, 0x04, \ + 0x70, 0x80, 0x44, 0x20, \ + 0xf0, 0x3c, 0x48, 0x00, \ + 0x60, 0x84, 0x06, 0x04, \ + 0x28, 0x2f, 0xa0, 0x19, \ + 0x46, 0x30, 0xc6, 0x07, \ + 0xa2, 0x2f, 0x71, 0x00, \ + 0x72, 0x00, 0x40, 0x00, \ + 0x5d, 0x93, 0xa6, 0x2f, \ + 0xc1, 0xc3, 0xa4, 0x0f, \ + 0x60, 0x04, 0x48, 0x26, \ + 0x64, 0x0c, 0x4b, 0x06, \ + 0x40, 0x41, 0xc0, 0x20, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x64, 0x0d, 0x48, 0x06, \ + 0x81, 0x35, 0xc0, 0x20, \ + 0xd1, 0xc6, 0xcd, 0xc8, \ + 0xfe, 0xb5, 0x8b, 0x4b, \ + 0x81, 0x15, 0x85, 0x31, \ + 0xfa, 0xfc, 0x04, 0x0c, \ + 0x40, 0x06, 0x00, 0x6b, \ + 0x00, 0x04, 0xe1, 0x14, \ + 0xe0, 0x04, 0x49, 0x86, \ + 0x6d, 0x09, 0x4c, 0x41, \ + 0x17, 0x46, 0x40, 0x2f, \ + 0x01, 0xa1, 0xe1, 0x2c, \ + 0x80, 0x48, 0x91, 0x91, \ + 0x00, 0x46, 0xe0, 0x49, \ + 0xe1, 0x14, 0x41, 0x06, \ + 0x49, 0x96, 0x00, 0x04, \ + 0x4c, 0x41, 0xe0, 0x04, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x80, 0x46, 0x83, 0x41, \ + 0x00, 0xa6, 0xac, 0x04, \ + 0xe1, 0x34, 0x00, 0x46, \ + 0x80, 0x04, 0x80, 0x04, \ + 0xe0, 0xec, 0x00, 0x0a, \ + 0x0c, 0x05, 0x82, 0x05, \ + 0x40, 0x0c, 0x65, 0x0d, \ + 0x00, 0x0a, 0x91, 0x91, \ + 0x9c, 0x35, 0xe0, 0xf0, \ + 0xe8, 0x04, 0x4e, 0x88, \ + 0x02, 0xf6, 0x2c, 0x04, \ + 0x0e, 0x60, 0xe0, 0x08, \ + 0x40, 0x54, 0x66, 0x51, \ + 0x46, 0x36, 0x8f, 0x21, \ + 0x00, 0xa6, 0xe0, 0x04, \ + 0xe1, 0x14, 0x40, 0x06, \ + 0x4c, 0x11, 0x80, 0x04, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x00, 0x16, 0xf3, 0xbc, \ + 0xe1, 0x14, 0x41, 0x06, \ + 0x83, 0x11, 0x00, 0x04, \ + 0x40, 0x06, 0x00, 0xa6, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0x40, 0x0a, 0x80, 0x08, \ + 0x90, 0xf5, 0xe0, 0x8c, \ + 0x6f, 0xc8, 0x62, 0xe8, \ + 0x11, 0xc6, 0x8e, 0x0b, \ + 0xa0, 0x04, 0xe1, 0x2c, \ + 0xa1, 0x0b, 0x80, 0x08, \ + 0x00, 0x46, 0x82, 0xd5, \ + 0x80, 0x04, 0xe1, 0x34, \ + 0x00, 0x0a, 0x80, 0x04, \ + 0x80, 0xe5, 0xe0, 0xf4, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x98, 0xf5, 0xe0, 0xf8, \ + 0xe8, 0x04, 0x4b, 0x08, \ + 0x02, 0xf6, 0x2c, 0x04, \ + 0x00, 0xa6, 0xe0, 0x08, \ + 0xe1, 0x14, 0x40, 0x06, \ + 0x4c, 0x11, 0x80, 0x04, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x00, 0x16, 0xf3, 0xbc, \ + 0xe1, 0x14, 0x41, 0x06, \ + 0x00, 0xa6, 0x80, 0x04, \ + 0xe1, 0x48, 0x40, 0x06, \ + 0x80, 0x08, 0x80, 0x04, \ + 0xe0, 0x8c, 0x40, 0x4a, \ + 0x0c, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0xf4, 0x00, 0x0a, \ + 0x0c, 0x60, 0x8e, 0xb5, \ + 0x40, 0x54, 0x66, 0x51, \ + 0x11, 0x21, 0x83, 0x11, \ + 0xe0, 0x04, 0x46, 0x36, \ + 0x6f, 0xc8, 0x5e, 0x28, \ + 0x8c, 0x0b, 0x8c, 0x0b, \ + 0xf2, 0xfc, 0x0c, 0x0c, \ + 0xff, 0xc8, 0x1e, 0xe8, \ + 0x6f, 0xc8, 0x5d, 0xa8, \ + 0x8e, 0x0b, 0x8e, 0x0b, \ + 0xf2, 0xfc, 0x0c, 0x0c, \ + 0xff, 0xc8, 0x1e, 0x68, \ + 0x0f, 0xa8, 0xad, 0x3b, \ + 0xa3, 0x3b, 0x80, 0x00, \ + 0x80, 0x00, 0x10, 0x68, \ + 0x11, 0x48, 0xa5, 0x3b, \ + 0xa7, 0x3b, 0x80, 0x00, \ + 0x80, 0x00, 0x11, 0xf8, \ + 0x12, 0xd8, 0xa9, 0x3b, \ + 0xab, 0x3b, 0x80, 0x00, \ + 0x80, 0x00, 0x13, 0xd8, \ + 0x86, 0x9b, 0x21, 0x3b, \ + 0x00, 0x06, 0xde, 0x95, \ + 0x9f, 0x11, 0x61, 0x2c, \ + 0x81, 0x91, 0x00, 0x04, \ + 0x00, 0x46, 0x00, 0xa6, \ + 0x80, 0x04, 0xe1, 0x2c, \ + 0xac, 0x04, 0x80, 0x16, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xe4, \ + 0x00, 0x60, 0xf3, 0x15, \ + 0x40, 0x54, 0x66, 0x51, \ + 0x11, 0x21, 0x83, 0x11, \ + 0xe0, 0x04, 0x46, 0x36, \ + 0xef, 0xc8, 0x5b, 0x48, \ + 0xe4, 0x9d, 0x2c, 0x04, \ + 0x92, 0xd5, 0x80, 0x18, \ + 0x81, 0x21, 0x42, 0x19, \ + 0x42, 0x86, 0x00, 0x16, \ + 0x7f, 0x38, 0xe0, 0x3c, \ + 0x8c, 0x0b, 0x68, 0x00, \ + 0x66, 0x51, 0x0e, 0x60, \ + 0x41, 0x46, 0xc0, 0x54, \ + 0x80, 0x04, 0xe1, 0x40, \ + 0xa0, 0x04, 0x80, 0x98, \ + 0x40, 0x4a, 0x80, 0x08, \ + 0xa0, 0xe5, 0xe0, 0x80, \ + 0xe0, 0x8c, 0x52, 0x0a, \ + 0x00, 0x46, 0x82, 0xc5, \ + 0x80, 0x04, 0xe1, 0x34, \ + 0x00, 0x0a, 0x80, 0x04, \ + 0x80, 0xe5, 0xe0, 0xfc, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0xfa, 0x15, 0xe1, 0x00, \ + 0xe8, 0x04, 0x42, 0x28, \ + 0x02, 0xf6, 0x2c, 0x04, \ + 0x00, 0xa6, 0xe0, 0x08, \ + 0xe1, 0x14, 0x40, 0x06, \ + 0x4c, 0x11, 0x80, 0x04, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x00, 0x16, 0xf3, 0xbc, \ + 0xe1, 0x14, 0x41, 0x06, \ + 0x00, 0xa6, 0x80, 0x04, \ + 0xe1, 0x48, 0x40, 0x06, \ + 0x80, 0x08, 0x80, 0x04, \ + 0xe0, 0x8c, 0x40, 0x4a, \ + 0x0c, 0x05, 0x9c, 0x65, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe0, 0xfc, 0x00, 0x0a, \ + 0x03, 0x11, 0x9a, 0xa5, \ + 0x46, 0x36, 0x93, 0x21, \ + 0x8e, 0x0b, 0x60, 0x04, \ + 0xef, 0xc8, 0x56, 0x48, \ + 0x60, 0x19, 0xf9, 0x35, \ + 0x00, 0x16, 0x81, 0x21, \ + 0xe0, 0x3c, 0x42, 0x86, \ + 0x68, 0x00, 0x7a, 0x68, \ + 0x0e, 0x60, 0x8c, 0x0b, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0xe1, 0x40, 0x41, 0x46, \ + 0x80, 0x98, 0x80, 0x04, \ + 0x80, 0x08, 0xa0, 0x04, \ + 0xe0, 0x90, 0x40, 0x4a, \ + 0x52, 0x0a, 0x8e, 0x45, \ + 0x82, 0xc5, 0xe0, 0x8c, \ + 0xe1, 0x34, 0x00, 0x46, \ + 0x80, 0x04, 0x80, 0x04, \ + 0xe1, 0x04, 0x00, 0x0a, \ + 0x0c, 0x05, 0x80, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe1, 0x08, 0x00, 0x0a, \ + 0x7d, 0x58, 0xfa, 0x15, \ + 0x2c, 0x04, 0xe8, 0x00, \ + 0xe0, 0x08, 0x02, 0xf6, \ + 0x40, 0x06, 0x00, 0xa6, \ + 0x80, 0x04, 0xe1, 0x14, \ + 0x6d, 0x09, 0x4c, 0x11, \ + 0x80, 0x18, 0xc0, 0x2f, \ + 0xf3, 0xbc, 0x42, 0x10, \ + 0x41, 0x06, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x14, \ + 0x40, 0x06, 0x00, 0xa6, \ + 0x80, 0x04, 0xe1, 0x48, \ + 0x40, 0x4a, 0x80, 0x08, \ + 0x8c, 0x45, 0xe0, 0x8c, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x8a, 0x85, 0xe1, 0x04, \ + 0x95, 0x21, 0x03, 0x11, \ + 0x60, 0x04, 0x46, 0x36, \ + 0x51, 0x78, 0x8e, 0x0b, \ + 0xf9, 0x35, 0xef, 0xc8, \ + 0xe8, 0x00, 0x7a, 0xc8, \ + 0x02, 0xf6, 0x2c, 0x04, \ + 0x00, 0xa6, 0xe0, 0x08, \ + 0xe1, 0x14, 0x40, 0x06, \ + 0x4c, 0x11, 0x80, 0x04, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x00, 0x16, 0xf3, 0xbc, \ + 0xe1, 0x14, 0x41, 0x06, \ + 0x80, 0xa6, 0x80, 0x04, \ + 0xf2, 0xfc, 0x0c, 0x0c, \ + 0x03, 0x11, 0xdd, 0x93, \ + 0x46, 0x36, 0x93, 0x21, \ + 0x8e, 0x0b, 0x60, 0x04, \ + 0xef, 0xc8, 0x4e, 0xa8, \ + 0x0c, 0x0c, 0x8e, 0x0b, \ + 0x0f, 0x78, 0xf2, 0xfc, \ + 0x03, 0x11, 0xff, 0xc8, \ + 0x46, 0x36, 0x95, 0x21, \ + 0x8e, 0x0b, 0x60, 0x04, \ + 0xef, 0xc8, 0x4d, 0xe8, \ + 0x0c, 0x0c, 0x8e, 0x0b, \ + 0x0e, 0xb8, 0xf2, 0xfc, \ + 0x00, 0x86, 0xff, 0xc8, \ + 0x8d, 0x91, 0x61, 0x2c, \ + 0x00, 0x16, 0x80, 0x04, \ + 0xe1, 0x2c, 0x00, 0xc6, \ + 0x80, 0x26, 0x80, 0x04, \ + 0x80, 0x18, 0xac, 0x04, \ + 0xfd, 0x80, 0x42, 0x10, \ + 0xd5, 0xc5, 0x80, 0x16, \ + 0x61, 0x2c, 0x00, 0x06, \ + 0x00, 0x04, 0x9f, 0x11, \ + 0x00, 0xa6, 0x83, 0x91, \ + 0xe1, 0x2c, 0x00, 0x46, \ + 0x80, 0x16, 0x80, 0x04, \ + 0x80, 0x18, 0xac, 0x04, \ + 0xf3, 0xe4, 0x42, 0x10, \ + 0x80, 0x16, 0xc4, 0x19, \ + 0x00, 0x06, 0xd3, 0xb5, \ + 0x85, 0x91, 0x61, 0x2c, \ + 0x00, 0x16, 0x80, 0x04, \ + 0xe1, 0x2c, 0x00, 0x46, \ + 0x80, 0x26, 0x80, 0x04, \ + 0x80, 0x18, 0xac, 0x04, \ + 0x80, 0x16, 0xcc, 0x19, \ + 0x00, 0x86, 0xd1, 0xd5, \ + 0x43, 0x76, 0x61, 0x2c, \ + 0x00, 0x04, 0xe0, 0x00, \ + 0x00, 0xa6, 0x87, 0x91, \ + 0xe1, 0x2c, 0x00, 0xc6, \ + 0x80, 0x16, 0x80, 0x04, \ + 0x80, 0x18, 0xac, 0x04, \ + 0xf2, 0x7c, 0x42, 0x10, \ + 0xcf, 0xc5, 0x80, 0x16, \ + 0x61, 0x2c, 0x00, 0x86, \ + 0xe0, 0x00, 0x43, 0x76, \ + 0x89, 0x91, 0x00, 0x04, \ + 0x00, 0xc6, 0x00, 0xa6, \ + 0x80, 0x04, 0xe1, 0x2c, \ + 0xac, 0x04, 0x80, 0x16, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x02, 0x14, 0xf2, 0x7c, \ + 0x80, 0x16, 0xfc, 0x94, \ + 0x00, 0x86, 0xcd, 0x95, \ + 0x8b, 0x91, 0x61, 0x2c, \ + 0x00, 0x16, 0x80, 0x04, \ + 0xe1, 0x2c, 0x00, 0xc6, \ + 0x80, 0x26, 0x80, 0x04, \ + 0x80, 0x18, 0xac, 0x04, \ + 0xfd, 0x80, 0x42, 0x10, \ + 0xcb, 0xa5, 0x80, 0x16, \ + 0x60, 0x04, 0x48, 0x56, \ + 0x64, 0x20, 0x0b, 0x86, \ + 0x40, 0x41, 0xc0, 0x20, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x64, 0x11, 0x08, 0x06, \ + 0x81, 0x35, 0xc0, 0x20, \ + 0xd1, 0xc6, 0xcd, 0xc8, \ + 0xfe, 0xb5, 0x8b, 0x4b, \ + 0x1f, 0x78, 0x8d, 0x31, \ + 0xc1, 0xc3, 0xff, 0xfc, \ + 0x64, 0x31, 0x49, 0x06, \ + 0x4b, 0xc6, 0x40, 0x20, \ + 0xc0, 0x20, 0x64, 0x39, \ + 0xcd, 0xc8, 0x81, 0x35, \ + 0x8b, 0x4b, 0xd1, 0xc6, \ + 0x8b, 0x31, 0xfe, 0xb5, \ + 0xff, 0xfc, 0x1e, 0x88, \ + 0xe0, 0x04, 0x44, 0x76, \ + 0x6d, 0x09, 0x40, 0x21, \ + 0x41, 0xc6, 0x40, 0x2f, \ + 0xc0, 0x20, 0x64, 0x39, \ + 0x64, 0x3d, 0x05, 0xc6, \ + 0x85, 0x0b, 0xc0, 0x20, \ + 0x9a, 0x00, 0x5c, 0x08, \ + 0xc1, 0x36, 0xc5, 0x38, \ + 0xdd, 0x93, 0xff, 0xb5, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x81, 0x11, 0x00, 0x6b, \ + 0x81, 0x31, 0x01, 0x21, \ + 0xef, 0xfc, 0x5c, 0xe8, \ + 0x67, 0x71, 0x0c, 0x60, \ + 0x4f, 0x06, 0x40, 0x9d, \ + 0xc0, 0x20, 0x64, 0x0d, \ + 0xa1, 0x81, 0x01, 0x85, \ + 0x59, 0xb8, 0xdd, 0x08, \ + 0x59, 0x06, 0xef, 0xfc, \ + 0xec, 0x04, 0x10, 0x80, \ + 0xfe, 0x35, 0xa1, 0x8b, \ + 0xf2, 0x0c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x0c, 0x04, 0x0c, \ + 0x81, 0x11, 0x00, 0x6b, \ + 0x87, 0x31, 0x01, 0x21, \ + 0xef, 0xfc, 0x5b, 0x28, \ + 0x67, 0x21, 0x0c, 0x60, \ + 0x0f, 0x86, 0x40, 0xc3, \ + 0xc0, 0x20, 0x64, 0x21, \ + 0x11, 0x86, 0x01, 0x95, \ + 0xdd, 0x08, 0xe0, 0x10, \ + 0xef, 0xfc, 0x57, 0xe8, \ + 0x10, 0x80, 0x59, 0x06, \ + 0xa1, 0x8b, 0xec, 0x04, \ + 0x0c, 0x0c, 0xfe, 0x35, \ + 0xdd, 0x93, 0xf2, 0x0c, \ + 0xfa, 0x7c, 0x04, 0x4c, \ + 0x41, 0x46, 0x00, 0x6b, \ + 0x00, 0x04, 0xe1, 0x14, \ + 0xe0, 0x0c, 0x03, 0x26, \ + 0x6d, 0x09, 0x4c, 0x11, \ + 0x83, 0x81, 0x40, 0x2f, \ + 0x61, 0x30, 0x54, 0xc6, \ + 0x00, 0x86, 0x81, 0x91, \ + 0xe1, 0x30, 0x57, 0xc6, \ + 0x00, 0x96, 0xa8, 0x04, \ + 0xe1, 0x30, 0x41, 0x06, \ + 0x84, 0x28, 0x80, 0x14, \ + 0xf3, 0xc0, 0x44, 0x20, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x9e, 0x19, 0x80, 0x18, \ + 0xff, 0x40, 0x42, 0x10, \ + 0x41, 0x86, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x30, \ + 0xac, 0x04, 0x80, 0x96, \ + 0xa1, 0x0b, 0x80, 0x08, \ + 0x0c, 0x05, 0x82, 0x55, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe1, 0x0c, 0x00, 0x0a, \ + 0x0e, 0x60, 0x02, 0xf5, \ + 0xc0, 0x54, 0x06, 0x51, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x0e, 0x60, 0x40, 0x2f, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0x91, 0x21, 0x03, 0x11, \ + 0x6f, 0xc4, 0x7d, 0xe8, \ + 0x28, 0x04, 0x8e, 0x0b, \ + 0xe0, 0x3c, 0x42, 0x86, \ + 0x00, 0x86, 0x81, 0x21, \ + 0x61, 0xf8, 0x8c, 0x0b, \ + 0x51, 0x46, 0xe8, 0x00, \ + 0xa0, 0x04, 0xe1, 0x40, \ + 0x81, 0x95, 0x80, 0xa8, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x83, 0x11, 0x40, 0x2f, \ + 0x8e, 0x0b, 0x15, 0x21, \ + 0xef, 0xc4, 0x7c, 0x88, \ + 0x80, 0x08, 0xac, 0x04, \ + 0x82, 0x75, 0xa1, 0x0b, \ + 0xe0, 0x8c, 0x54, 0x0a, \ + 0x0c, 0x05, 0xfc, 0x45, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe1, 0x10, 0x00, 0x0a, \ + 0xfd, 0x85, 0xfa, 0x85, \ + 0xe8, 0x00, 0x65, 0x08, \ + 0xe1, 0x30, 0x40, 0xc6, \ + 0x00, 0x96, 0x80, 0x04, \ + 0xe1, 0x14, 0x41, 0x46, \ + 0x80, 0x96, 0x80, 0x04, \ + 0xf2, 0x7c, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x00, 0x25, 0xa1, 0x1b, \ + 0x00, 0x0c, 0x05, 0x6d, \ + 0x81, 0x2c, 0x42, 0xc6, \ + 0x80, 0x00, 0x02, 0x15, \ + 0x80, 0x00, 0x42, 0x25, \ + 0xa3, 0x1b, 0x80, 0xc5, \ + 0xa5, 0x1b, 0x80, 0x85, \ + 0x83, 0x65, 0x82, 0x25, \ + 0x65, 0x71, 0x00, 0x25, \ + 0x43, 0x06, 0x40, 0x0c, \ + 0x84, 0x14, 0xe1, 0x2c, \ + 0x00, 0x25, 0x84, 0x2e, \ + 0x40, 0x0c, 0x65, 0x6d, \ + 0xe1, 0x2c, 0x42, 0xc6, \ + 0x04, 0x2e, 0x84, 0x14, \ + 0xe1, 0x2c, 0x43, 0x06, \ + 0x65, 0x71, 0x00, 0x05, \ + 0x84, 0x14, 0xc0, 0x0c, \ + 0xdd, 0x93, 0x84, 0x0e, \ + 0xf8, 0x0c, 0x04, 0x4c, \ + 0x61, 0x14, 0x45, 0x46, \ + 0x08, 0x34, 0xa5, 0x1b, \ + 0x8c, 0x46, 0x81, 0x41, \ + 0xa3, 0x1b, 0x88, 0x25, \ + 0x42, 0x06, 0x10, 0x25, \ + 0xa1, 0x1b, 0x81, 0x2c, \ + 0x00, 0x39, 0x9a, 0x35, \ + 0x40, 0x31, 0x65, 0xd1, \ + 0xe1, 0x2c, 0x42, 0x06, \ + 0x4b, 0xb6, 0x04, 0x14, \ + 0x89, 0xc1, 0xe0, 0x0c, \ + 0x84, 0x48, 0x82, 0x39, \ + 0xf3, 0xf8, 0x48, 0x40, \ + 0x87, 0x31, 0x26, 0x4f, \ + 0x43, 0x76, 0x04, 0x46, \ + 0x00, 0x49, 0xe0, 0x0c, \ + 0x40, 0x31, 0x65, 0xd1, \ + 0x65, 0xd1, 0x00, 0x00, \ + 0x07, 0xd5, 0xc0, 0xc5, \ + 0x00, 0x49, 0xc2, 0x47, \ + 0x40, 0x31, 0x65, 0xd1, \ + 0xe1, 0x2c, 0x42, 0x06, \ + 0x82, 0x49, 0x84, 0x34, \ + 0x4a, 0x50, 0x8c, 0x58, \ + 0xa8, 0x5f, 0xf3, 0xf8, \ + 0x84, 0x34, 0x8c, 0x56, \ + 0x65, 0xd1, 0x00, 0x49, \ + 0x8c, 0x58, 0xc0, 0x31, \ + 0x88, 0x49, 0xc2, 0x47, \ + 0xf3, 0xec, 0x4a, 0x50, \ + 0x8c, 0x56, 0xa8, 0x5f, \ + 0x00, 0x49, 0x84, 0x34, \ + 0xc0, 0x31, 0x65, 0xd1, \ + 0xc2, 0x47, 0x8c, 0x58, \ + 0x4a, 0x50, 0x90, 0x49, \ + 0xa8, 0x5f, 0xf3, 0xdc, \ + 0x04, 0x14, 0x8c, 0x56, \ + 0xe0, 0x08, 0x4b, 0xf6, \ + 0x65, 0xd5, 0x00, 0x39, \ + 0x18, 0x06, 0x40, 0x31, \ + 0x84, 0x48, 0xe0, 0x04, \ + 0xa0, 0x39, 0xc8, 0x37, \ + 0xf3, 0xbc, 0x48, 0x40, \ + 0x8f, 0x31, 0x26, 0x4f, \ + 0x43, 0xf6, 0x04, 0x46, \ + 0x00, 0x49, 0xe0, 0x04, \ + 0x40, 0x31, 0x65, 0xd5, \ + 0x65, 0xd5, 0x00, 0x00, \ + 0xca, 0x47, 0xc0, 0xc5, \ + 0x61, 0x2c, 0x4c, 0x06, \ + 0x18, 0x74, 0x98, 0x4f, \ + 0xfa, 0x80, 0x06, 0x34, \ + 0x8a, 0x8f, 0x9c, 0x88, \ + 0x9c, 0x86, 0xa8, 0x8f, \ + 0x80, 0x08, 0x98, 0x44, \ + 0xc5, 0x07, 0x90, 0x58, \ + 0x27, 0x0f, 0x82, 0x09, \ + 0xa0, 0x5f, 0x82, 0x5f, \ + 0x08, 0x04, 0x90, 0x56, \ + 0x80, 0x16, 0x83, 0x11, \ + 0xf0, 0x0c, 0x0c, 0x4c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x1c, 0x04, 0x0c, \ + 0x61, 0x88, 0x06, 0x14, \ + 0xe1, 0x94, 0x04, 0xc6, \ + 0x08, 0x34, 0x06, 0x19, \ + 0x08, 0x54, 0xe0, 0x90, \ + 0x04, 0x44, 0xa2, 0x4f, \ + 0x08, 0x00, 0x60, 0x88, \ + 0xc0, 0x8e, 0x64, 0xb0, \ + 0x04, 0x53, 0x81, 0xc1, \ + 0x81, 0xd5, 0x81, 0x21, \ + 0x0c, 0x60, 0x61, 0x94, \ + 0x4e, 0x95, 0xec, 0x04, \ + 0xa1, 0x6b, 0xf0, 0x10, \ + 0x20, 0x41, 0xfe, 0x55, \ + 0x0a, 0x50, 0x82, 0xc1, \ + 0xc0, 0x01, 0x64, 0x01, \ + 0x0c, 0x46, 0xa5, 0xcb, \ + 0x4e, 0x50, 0x40, 0x00, \ + 0x7c, 0x95, 0xdc, 0x00, \ + 0xdc, 0x00, 0x50, 0x40, \ + 0x61, 0x90, 0x49, 0x86, \ + 0xe0, 0x00, 0x59, 0x86, \ + 0x00, 0x00, 0x10, 0x44, \ + 0xc0, 0x8f, 0x64, 0x01, \ + 0x60, 0x0c, 0x4a, 0x06, \ + 0xe5, 0x32, 0x46, 0x3a, \ + 0x4c, 0x2a, 0x83, 0x25, \ + 0x4e, 0x12, 0x65, 0x16, \ + 0x86, 0x63, 0xf0, 0xb0, \ + 0x7c, 0x30, 0x00, 0x65, \ + 0x44, 0x77, 0x8c, 0x73, \ + 0x88, 0x73, 0x88, 0xc1, \ + 0x18, 0x0a, 0x9c, 0x6e, \ + 0xfc, 0x55, 0xe0, 0xe0, \ + 0x82, 0x21, 0x70, 0x01, \ + 0x7e, 0x55, 0xb5, 0x2b, \ + 0xc0, 0x00, 0x18, 0x06, \ + 0xf0, 0x1c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0x61, 0x88, 0x08, 0x14, \ + 0xf0, 0x0c, 0x44, 0x10, \ + 0x60, 0x08, 0x07, 0x06, \ + 0x58, 0x4a, 0x93, 0x11, \ + 0x0a, 0x44, 0x66, 0x8e, \ + 0x46, 0x2a, 0xe0, 0x90, \ + 0x0e, 0x00, 0x66, 0x86, \ + 0xc0, 0x8e, 0x65, 0xb0, \ + 0x81, 0x61, 0x24, 0x5f, \ + 0x81, 0x31, 0x06, 0xc3, \ + 0x10, 0x70, 0x83, 0x75, \ + 0x89, 0x91, 0x64, 0x40, \ + 0x98, 0xbb, 0x0a, 0xab, \ + 0x5d, 0xbe, 0x81, 0x85, \ + 0xec, 0x04, 0x12, 0x90, \ + 0x60, 0x1c, 0x4e, 0xa5, \ + 0x88, 0xa1, 0xc8, 0xb1, \ + 0xfe, 0x15, 0xa1, 0x9b, \ + 0x18, 0xc0, 0x02, 0x61, \ + 0xc0, 0x02, 0x65, 0x01, \ + 0x65, 0x00, 0x0a, 0x50, \ + 0xa5, 0x6b, 0x90, 0x7b, \ + 0x00, 0x5b, 0xfa, 0x35, \ + 0x40, 0x18, 0x66, 0x8d, \ + 0x65, 0x81, 0x00, 0x00, \ + 0x4a, 0x0a, 0xc0, 0x93, \ + 0x59, 0x86, 0xe0, 0x80, \ + 0x4a, 0x06, 0x40, 0x00, \ + 0x48, 0x4a, 0xc0, 0x0c, \ + 0x86, 0xf5, 0xc5, 0x32, \ + 0x83, 0x21, 0x01, 0x75, \ + 0x92, 0x11, 0x41, 0x1e, \ + 0x62, 0x2c, 0x40, 0x25, \ + 0x04, 0x0a, 0x82, 0x21, \ + 0xfe, 0x35, 0xe2, 0x34, \ + 0x4e, 0x3a, 0x83, 0xa5, \ + 0x50, 0x22, 0x65, 0x16, \ + 0x52, 0x02, 0xf0, 0x98, \ + 0x88, 0x61, 0x70, 0x98, \ + 0x8e, 0x83, 0x88, 0x73, \ + 0xe6, 0x86, 0x4e, 0x8a, \ + 0x52, 0x85, 0xa4, 0x7e, \ + 0x0c, 0x0a, 0xe2, 0x30, \ + 0xfc, 0x15, 0xe0, 0xe0, \ + 0x98, 0x0b, 0x02, 0x31, \ + 0x18, 0x00, 0xb5, 0x3b, \ + 0x0c, 0x06, 0x44, 0xe0, \ + 0xfc, 0xd5, 0xc0, 0x00, \ + 0xf0, 0x7c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x6f, 0xf8, 0x75, 0x88, \ + 0xa1, 0x01, 0x80, 0x6b, \ + 0x6d, 0x29, 0x4c, 0x01, \ + 0x8c, 0x0b, 0x40, 0x30, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xff, 0xf0, 0x01, 0xa8, \ + 0x00, 0x00, 0x5d, 0x93, \ + 0xc0, 0x28, 0x65, 0x41, \ + 0x00, 0x00, 0x5d, 0x93, \ + 0xc0, 0x2b, 0x66, 0x11, \ + 0x61, 0x08, 0x03, 0x06, \ + 0x84, 0x14, 0x9d, 0x21, \ + 0x02, 0x86, 0x04, 0x26, \ + 0x04, 0x24, 0xe1, 0x08, \ + 0x08, 0x16, 0x83, 0x11, \ + 0xe1, 0x14, 0x44, 0x06, \ + 0x88, 0x38, 0x88, 0x24, \ + 0x08, 0x36, 0xc8, 0x39, \ + 0x60, 0x01, 0x06, 0x06, \ + 0x00, 0x25, 0xc0, 0x40, \ + 0xc0, 0x0b, 0x6f, 0x2d, \ + 0xa1, 0x21, 0x47, 0x2d, \ + 0x05, 0x15, 0x00, 0x29, \ + 0x00, 0x46, 0x00, 0x28, \ + 0x80, 0x04, 0xe1, 0x28, \ + 0x00, 0x06, 0x00, 0x26, \ + 0x80, 0x04, 0xe1, 0x28, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xe1, 0x28, 0x02, 0x86, \ + 0x84, 0x28, 0x84, 0x14, \ + 0x87, 0x2b, 0x84, 0x38, \ + 0x02, 0x20, 0xfe, 0xb5, \ + 0x40, 0x11, 0xed, 0x5c, \ + 0xc0, 0x30, 0x6d, 0x99, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x60, 0x08, 0x43, 0x56, \ + 0xe1, 0x28, 0x04, 0x86, \ + 0x6d, 0x09, 0x40, 0x11, \ + 0x88, 0x14, 0xc0, 0x2f, \ + 0x84, 0x38, 0x84, 0x28, \ + 0xfe, 0xb5, 0x87, 0x2b, \ + 0xed, 0x5c, 0x02, 0x20, \ + 0x6d, 0x99, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x30, \ + 0xc0, 0x15, 0x61, 0x41, \ + 0x61, 0x39, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x23, \ + 0xc0, 0x23, 0x68, 0x99, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x60, 0x08, 0x43, 0x66, \ + 0xe1, 0x28, 0x04, 0x86, \ + 0x6d, 0x09, 0x40, 0x11, \ + 0x88, 0x14, 0xc0, 0x2f, \ + 0x84, 0x38, 0x84, 0x28, \ + 0xfe, 0xb5, 0x87, 0x2b, \ + 0xed, 0x5c, 0x02, 0x20, \ + 0x6d, 0x99, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x30, \ + 0xc0, 0x15, 0x61, 0x41, \ + 0x61, 0x59, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x1c, \ + 0xc0, 0x23, 0x6b, 0xe9, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x60, 0x08, 0x43, 0x96, \ + 0xe1, 0x28, 0x04, 0x86, \ + 0x6d, 0x09, 0x40, 0x11, \ + 0x88, 0x14, 0xc0, 0x2f, \ + 0x84, 0x38, 0x84, 0x28, \ + 0xfe, 0xb5, 0x87, 0x2b, \ + 0xed, 0x5c, 0x02, 0x20, \ + 0x6d, 0x99, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x30, \ + 0xc0, 0x15, 0x61, 0x45, \ + 0x6e, 0xf1, 0x40, 0x11, \ + 0x40, 0x11, 0xc0, 0x15, \ + 0xc0, 0x1c, 0x61, 0x5d, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x6e, 0x79, 0x00, 0x0b, \ + 0x47, 0x86, 0x40, 0x17, \ + 0x0c, 0x34, 0xe1, 0x40, \ + 0x87, 0x21, 0xa1, 0x2b, \ + 0xe6, 0x86, 0x40, 0x0a, \ + 0x41, 0x46, 0x0c, 0x0e, \ + 0x80, 0x14, 0xe1, 0x40, \ + 0x5c, 0x08, 0x84, 0x26, \ + 0x80, 0x04, 0x9a, 0x00, \ + 0x42, 0x4a, 0x80, 0x18, \ + 0xfe, 0xa5, 0xe0, 0x8c, \ + 0x80, 0x16, 0x81, 0x11, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x6e, 0x79, 0x00, 0x0b, \ + 0x06, 0x46, 0x40, 0x17, \ + 0x0c, 0x34, 0xe1, 0x44, \ + 0x87, 0x21, 0xa1, 0x2b, \ + 0xe6, 0x86, 0x40, 0x0a, \ + 0x00, 0x06, 0x0c, 0x0e, \ + 0x80, 0x14, 0xe1, 0x44, \ + 0x5c, 0x08, 0x84, 0x26, \ + 0x80, 0x04, 0x9a, 0x00, \ + 0x42, 0x4a, 0x80, 0x18, \ + 0xfe, 0xa5, 0xe0, 0x8c, \ + 0x80, 0x16, 0x81, 0x11, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x6e, 0x79, 0x00, 0x0b, \ + 0x07, 0x06, 0x40, 0x17, \ + 0x0c, 0x34, 0xe1, 0x44, \ + 0x87, 0x21, 0xa1, 0x2b, \ + 0xe6, 0x86, 0x40, 0x0a, \ + 0x00, 0xc6, 0x0c, 0x0e, \ + 0x80, 0x14, 0xe1, 0x44, \ + 0x5c, 0x08, 0x84, 0x26, \ + 0x80, 0x04, 0x9a, 0x00, \ + 0x42, 0x4a, 0x80, 0x18, \ + 0xfe, 0xa5, 0xe0, 0x8c, \ + 0x80, 0x16, 0x81, 0x11, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x61, 0x40, 0x41, 0x46, \ + 0x80, 0x04, 0x81, 0x11, \ + 0xdd, 0x93, 0x80, 0x16, \ + 0xe1, 0x24, 0x00, 0x46, \ + 0x84, 0x28, 0x80, 0x14, \ + 0xf3, 0xf8, 0x44, 0x20, \ + 0x42, 0x06, 0x04, 0x26, \ + 0x84, 0x14, 0xe1, 0x14, \ + 0x04, 0x24, 0x84, 0x28, \ + 0x84, 0x26, 0xfc, 0x94, \ + 0x84, 0x28, 0x80, 0x14, \ + 0xf3, 0xf4, 0x44, 0x20, \ + 0x80, 0x04, 0x84, 0x26, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf3, 0xcc, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xe1, 0x24, 0x00, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x80, 0x16, 0xc2, 0x19, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0xfc, 0x04, 0x0c, \ + 0x00, 0x46, 0x00, 0x6b, \ + 0x00, 0x04, 0xe1, 0x24, \ + 0xe0, 0x0c, 0x02, 0xa6, \ + 0x6d, 0x09, 0x4c, 0x11, \ + 0x81, 0x21, 0x40, 0x2f, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x00, 0x16, 0xf3, 0xf8, \ + 0xe0, 0x04, 0x42, 0x46, \ + 0x6f, 0xfc, 0x77, 0x88, \ + 0x51, 0x46, 0x8c, 0x0b, \ + 0x0e, 0x60, 0x61, 0x40, \ + 0xc0, 0x54, 0x66, 0x50, \ + 0x52, 0x06, 0x20, 0x04, \ + 0x56, 0x46, 0xe1, 0x48, \ + 0x80, 0x08, 0xe1, 0x24, \ + 0x54, 0x00, 0xc7, 0x07, \ + 0xa1, 0xab, 0xf0, 0x04, \ + 0x0c, 0x05, 0x82, 0xe5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe1, 0x90, 0x00, 0x0a, \ + 0xc0, 0x00, 0x10, 0x05, \ + 0xc0, 0x00, 0x00, 0x09, \ + 0xc1, 0x8c, 0x00, 0x04, \ + 0x54, 0x00, 0x04, 0x15, \ + 0x24, 0x04, 0xd0, 0x04, \ + 0x9b, 0x21, 0x83, 0x11, \ + 0x0c, 0x39, 0x80, 0x08, \ + 0xc0, 0x2f, 0x6d, 0x09, \ + 0x60, 0x8c, 0x40, 0x0a, \ + 0x82, 0x55, 0x8e, 0x0b, \ + 0xef, 0xc4, 0x50, 0x28, \ + 0x80, 0x04, 0xac, 0x04, \ + 0xf8, 0x15, 0xa1, 0x0b, \ + 0xf2, 0xfc, 0x0c, 0x0c, \ + 0x4e, 0xb8, 0xdd, 0x93, \ + 0x8e, 0x0b, 0xef, 0xc4, \ + 0xf2, 0xfc, 0x0c, 0x0c, \ + 0xff, 0xc4, 0x0f, 0x88, \ + 0xfa, 0x7c, 0x04, 0x4c, \ + 0x00, 0x46, 0x00, 0x6b, \ + 0x00, 0x04, 0xe1, 0x24, \ + 0xe0, 0x0c, 0x02, 0x26, \ + 0x6d, 0x09, 0x4c, 0x11, \ + 0x52, 0x46, 0x40, 0x2f, \ + 0x44, 0x86, 0xe1, 0x24, \ + 0x80, 0x18, 0xe1, 0x14, \ + 0x00, 0x16, 0xc4, 0x19, \ + 0xe1, 0x18, 0x40, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0x81, 0x11, 0x0f, 0x1e, \ + 0x41, 0xc6, 0x00, 0x16, \ + 0x00, 0x04, 0xe1, 0x20, \ + 0x80, 0x16, 0x83, 0x11, \ + 0x80, 0x84, 0xa4, 0x04, \ + 0x81, 0x21, 0x08, 0x04, \ + 0x00, 0xc6, 0x00, 0x1c, \ + 0x80, 0x04, 0xe1, 0x24, \ + 0x42, 0x46, 0x00, 0x16, \ + 0x71, 0x38, 0xe0, 0x04, \ + 0x8c, 0x0b, 0x6f, 0xfc, \ + 0x61, 0x40, 0x55, 0x46, \ + 0x66, 0x50, 0x0e, 0x60, \ + 0xa8, 0x04, 0xc0, 0x54, \ + 0xc7, 0x07, 0x80, 0x08, \ + 0xf0, 0x04, 0x56, 0x00, \ + 0x82, 0xe5, 0xa1, 0xbb, \ + 0x65, 0x0d, 0x0c, 0x05, \ + 0x00, 0x0a, 0xc0, 0x0c, \ + 0x14, 0x05, 0xe1, 0x94, \ + 0x00, 0x09, 0xc0, 0x00, \ + 0x00, 0x04, 0xc0, 0x00, \ + 0x02, 0x75, 0xc1, 0x8c, \ + 0xd0, 0x04, 0x56, 0x00, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x83, 0x11, 0x40, 0x2f, \ + 0x8e, 0x0b, 0x1b, 0x21, \ + 0xef, 0xc4, 0x4a, 0x68, \ + 0x80, 0x04, 0xa4, 0x04, \ + 0xf8, 0xa5, 0x81, 0x8b, \ + 0x61, 0x24, 0x00, 0xc6, \ + 0x80, 0x04, 0x81, 0x11, \ + 0x40, 0x46, 0x00, 0x16, \ + 0x80, 0x04, 0xe1, 0x18, \ + 0x00, 0x26, 0x8f, 0x24, \ + 0xe1, 0x20, 0x41, 0xc6, \ + 0x00, 0x16, 0x80, 0x04, \ + 0xe1, 0x24, 0x00, 0x46, \ + 0x80, 0x18, 0x80, 0x04, \ + 0xf3, 0xf4, 0x42, 0x10, \ + 0x0c, 0x4c, 0x80, 0x16, \ + 0xdd, 0x93, 0xf2, 0x7c, \ + 0xfa, 0x9c, 0x04, 0x0c, \ + 0x43, 0xe6, 0x02, 0x9b, \ + 0x08, 0x6b, 0xe0, 0x04, \ + 0x40, 0x11, 0x86, 0x7b, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x41, 0xb8, 0x84, 0x8b, \ + 0x03, 0x06, 0xe8, 0x00, \ + 0xc2, 0x67, 0x61, 0x18, \ + 0x84, 0x69, 0x04, 0x14, \ + 0x03, 0x46, 0x04, 0x96, \ + 0x84, 0x14, 0xe1, 0x18, \ + 0x03, 0x86, 0x04, 0x86, \ + 0x84, 0x14, 0xe1, 0x18, \ + 0x03, 0x86, 0x04, 0x76, \ + 0x84, 0x14, 0xe1, 0x10, \ + 0x44, 0x20, 0x84, 0x28, \ + 0xac, 0x2f, 0xf3, 0xf4, \ + 0x0c, 0x0c, 0x84, 0x26, \ + 0x83, 0xf5, 0xf2, 0x9c, \ + 0x61, 0x14, 0x40, 0x06, \ + 0xe1, 0x14, 0x46, 0x86, \ + 0x81, 0x41, 0x00, 0x14, \ + 0x84, 0x18, 0x84, 0x28, \ + 0xcb, 0x27, 0x8c, 0x34, \ + 0x82, 0x19, 0x04, 0x29, \ + 0xa4, 0x1f, 0x0c, 0x5a, \ + 0x80, 0x34, 0x8c, 0x4c, \ + 0x8c, 0x48, 0xe0, 0x57, \ + 0xf3, 0xf8, 0x48, 0x40, \ + 0x00, 0x34, 0x8c, 0x46, \ + 0xf9, 0x84, 0x40, 0x52, \ + 0x42, 0x40, 0x8c, 0x48, \ + 0x8c, 0x16, 0xf2, 0xfc, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x61, 0x14, 0x42, 0x86, \ + 0xe1, 0xc0, 0x04, 0x04, \ + 0x48, 0x00, 0x04, 0x14, \ + 0xca, 0x07, 0xf0, 0x04, \ + 0xf1, 0x00, 0x40, 0x00, \ + 0x42, 0x06, 0x04, 0x2c, \ + 0x84, 0x24, 0xe1, 0x14, \ + 0x46, 0x30, 0x88, 0x38, \ + 0xa8, 0x3f, 0xf3, 0xf8, \ + 0x84, 0x14, 0x88, 0x36, \ + 0x44, 0x20, 0x84, 0x28, \ + 0xa0, 0x2f, 0xf2, 0xfc, \ + 0xdd, 0x93, 0x84, 0x26, \ + 0xfa, 0x9c, 0x04, 0x0c, \ + 0x41, 0xf6, 0x00, 0x9b, \ + 0x06, 0x6b, 0xe0, 0x04, \ + 0x52, 0x01, 0x84, 0x7b, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x7b, 0xd8, 0x82, 0x8b, \ + 0x12, 0x29, 0xef, 0xfc, \ + 0x40, 0x30, 0x6f, 0x05, \ + 0xe1, 0x18, 0x43, 0x46, \ + 0xc8, 0x87, 0x04, 0x14, \ + 0x71, 0xc0, 0x48, 0x80, \ + 0x04, 0x26, 0x9e, 0x79, \ + 0xe1, 0x18, 0x42, 0x46, \ + 0x88, 0x38, 0x84, 0x24, \ + 0xf2, 0x3c, 0x46, 0x30, \ + 0x88, 0x36, 0xa8, 0x3f, \ + 0x84, 0x28, 0x84, 0x14, \ + 0xf3, 0xc0, 0x44, 0x20, \ + 0x04, 0x26, 0xae, 0x2f, \ + 0xe1, 0x18, 0x43, 0x86, \ + 0x84, 0x66, 0x84, 0x14, \ + 0xf2, 0x9c, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xf7, 0xa5, \ + 0xfa, 0x84, 0x04, 0x0c, \ + 0x82, 0x6b, 0x00, 0x7b, \ + 0x65, 0xd5, 0x0e, 0x39, \ + 0x0e, 0x29, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x65, 0xd9, \ + 0x65, 0xdd, 0x0e, 0x19, \ + 0x7c, 0x58, 0xc0, 0x2f, \ + 0x0e, 0x0b, 0xef, 0xfc, \ + 0x65, 0xf8, 0x8c, 0x1b, \ + 0x0e, 0x0b, 0xe8, 0x00, \ + 0x0c, 0x0c, 0x8c, 0x1b, \ + 0x27, 0x58, 0xf2, 0x84, \ + 0xc1, 0xc3, 0xe0, 0x00, \ + 0xfa, 0x9c, 0x04, 0x0c, \ + 0x41, 0xa6, 0x00, 0x6b, \ + 0x4c, 0x01, 0xe0, 0x04, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0xe0, 0x04, 0x43, 0xa6, \ + 0x6f, 0xfc, 0x7d, 0xc8, \ + 0x0c, 0x29, 0x8c, 0x0b, \ + 0xc0, 0x2f, 0x65, 0xdd, \ + 0x65, 0xd9, 0x0c, 0x19, \ + 0x0c, 0x09, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x65, 0xd5, \ + 0xef, 0x94, 0x71, 0x68, \ + 0x00, 0xc6, 0x00, 0x7b, \ + 0x00, 0x04, 0xe1, 0x04, \ + 0x80, 0x84, 0x8e, 0x2b, \ + 0x61, 0x51, 0x0c, 0x95, \ + 0x0c, 0x39, 0xc0, 0x0a, \ + 0xc0, 0x2f, 0x65, 0xe1, \ + 0x71, 0x38, 0x90, 0x0b, \ + 0x92, 0x1b, 0x6f, 0x94, \ + 0x63, 0x89, 0x4c, 0x05, \ + 0x90, 0x0b, 0x40, 0x0c, \ + 0x65, 0xe9, 0x0c, 0x39, \ + 0x92, 0x1b, 0x40, 0x2f, \ + 0x6f, 0x94, 0x70, 0x88, \ + 0x4c, 0x05, 0x8e, 0x2b, \ + 0x40, 0x0c, 0x63, 0x81, \ + 0x0c, 0x39, 0x90, 0x0b, \ + 0x40, 0x2f, 0x65, 0xe5, \ + 0x6f, 0xd8, 0x92, 0x1b, \ + 0x8e, 0x2b, 0x6f, 0x94, \ + 0x63, 0x8d, 0x4c, 0x05, \ + 0x90, 0x0b, 0x40, 0x0c, \ + 0x65, 0xed, 0x0c, 0x39, \ + 0x92, 0x1b, 0x40, 0x2f, \ + 0x6f, 0x94, 0x6f, 0x28, \ + 0x0c, 0x15, 0x8e, 0x2b, \ + 0x40, 0x0b, 0x6d, 0x81, \ + 0x4c, 0x05, 0x91, 0x21, \ + 0xc0, 0x0c, 0x63, 0x85, \ + 0x92, 0x1b, 0x02, 0x0b, \ + 0xef, 0x94, 0x6b, 0xa8, \ + 0x60, 0x49, 0x4c, 0x05, \ + 0x43, 0x86, 0x40, 0x07, \ + 0x84, 0x14, 0xe1, 0x18, \ + 0x6c, 0x41, 0x4c, 0x05, \ + 0x4c, 0x05, 0xc0, 0x08, \ + 0xc0, 0x08, 0x6e, 0x1d, \ + 0x6e, 0xf1, 0x4c, 0x05, \ + 0x84, 0x08, 0xc0, 0x08, \ + 0x61, 0x71, 0x4c, 0x01, \ + 0x84, 0x08, 0xc0, 0x1c, \ + 0x61, 0x51, 0x4c, 0x01, \ + 0x84, 0x08, 0xc0, 0x23, \ + 0x68, 0xa1, 0x4c, 0x01, \ + 0x04, 0x08, 0xc0, 0x23, \ + 0xe1, 0x18, 0x42, 0x46, \ + 0x4c, 0x01, 0x84, 0x14, \ + 0xc0, 0x23, 0x6b, 0xf1, \ + 0x9e, 0x09, 0x84, 0x08, \ + 0x61, 0x75, 0x4c, 0x01, \ + 0x84, 0x08, 0xc0, 0x1c, \ + 0x4c, 0x01, 0x9e, 0x09, \ + 0xc0, 0x23, 0x61, 0x55, \ + 0x9e, 0x09, 0x84, 0x08, \ + 0x68, 0xa5, 0x4c, 0x01, \ + 0x84, 0x08, 0xc0, 0x23, \ + 0x4c, 0x01, 0x9e, 0x09, \ + 0xc0, 0x23, 0x6b, 0xf5, \ + 0xc9, 0x07, 0x84, 0x08, \ + 0x4c, 0x01, 0x8e, 0x09, \ + 0xc0, 0x1c, 0x61, 0x79, \ + 0xc9, 0x07, 0x84, 0x08, \ + 0x4c, 0x01, 0x8e, 0x09, \ + 0xc0, 0x23, 0x61, 0x59, \ + 0xc9, 0x07, 0x84, 0x08, \ + 0x4c, 0x01, 0x8e, 0x09, \ + 0xc0, 0x23, 0x68, 0xa9, \ + 0xc9, 0x07, 0x84, 0x08, \ + 0x4c, 0x01, 0x8e, 0x09, \ + 0x40, 0x23, 0x6b, 0xf9, \ + 0x0c, 0x0c, 0x8c, 0x0b, \ + 0x81, 0x15, 0xf2, 0x9c, \ + 0x64, 0x5d, 0x00, 0x15, \ + 0x06, 0x0e, 0x40, 0x05, \ + 0xc3, 0xd0, 0x64, 0x01, \ + 0xe1, 0x84, 0x04, 0x14, \ + 0x6d, 0x29, 0x00, 0x39, \ + 0x86, 0x23, 0x40, 0x30, \ + 0xe0, 0x06, 0x42, 0x2a, \ + 0xe1, 0x84, 0x04, 0x34, \ + 0x42, 0x2a, 0x82, 0x23, \ + 0x00, 0x2b, 0xe0, 0x0e, \ + 0xc0, 0x18, 0x69, 0xf1, \ + 0x02, 0x06, 0xc4, 0x1d, \ + 0x42, 0x12, 0x80, 0x00, \ + 0x40, 0x15, 0xd1, 0x88, \ + 0xc0, 0x0c, 0x63, 0x91, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0xfa, 0x9c, 0x04, 0x0c, \ + 0x0c, 0x19, 0x80, 0x6b, \ + 0xc0, 0x2f, 0x65, 0x61, \ + 0x65, 0xf1, 0x0c, 0x99, \ + 0x0c, 0x79, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x66, 0x71, \ + 0xec, 0x04, 0x00, 0x10, \ + 0xc7, 0x97, 0x65, 0x0d, \ + 0x81, 0x01, 0x02, 0x15, \ + 0x0c, 0x09, 0xa7, 0x1b, \ + 0xc0, 0x30, 0x0e, 0xa1, \ + 0x83, 0x01, 0x80, 0xb5, \ + 0x6e, 0xa1, 0x4c, 0x01, \ + 0x0c, 0x09, 0xc0, 0x30, \ + 0x40, 0x30, 0x6d, 0xa5, \ + 0xf0, 0x04, 0x00, 0x0a, \ + 0x8e, 0x93, 0x02, 0x75, \ + 0xe0, 0x8c, 0x40, 0x4a, \ + 0x05, 0xed, 0x00, 0x60, \ + 0x01, 0xc5, 0x80, 0xbd, \ + 0x05, 0xe9, 0x00, 0x60, \ + 0x40, 0x4a, 0xc0, 0xbd, \ + 0x00, 0x60, 0xe0, 0x8c, \ + 0x80, 0xbd, 0x05, 0xe5, \ + 0x05, 0xe1, 0x00, 0x60, \ + 0x80, 0x88, 0xc0, 0xbd, \ + 0x6f, 0x94, 0x5b, 0x08, \ + 0x02, 0x94, 0x90, 0x0b, \ + 0x81, 0x41, 0x7a, 0x80, \ + 0x61, 0x02, 0x46, 0x1a, \ + 0x10, 0x1b, 0x8c, 0x0b, \ + 0x65, 0x58, 0x8e, 0x2b, \ + 0x0c, 0x09, 0xef, 0xfc, \ + 0xc0, 0x2f, 0x65, 0xf1, \ + 0x6d, 0x09, 0x0c, 0x19, \ + 0xc8, 0x07, 0xc0, 0x2f, \ + 0x60, 0xa9, 0x4c, 0x03, \ + 0x4c, 0x03, 0xc0, 0x0e, \ + 0xc0, 0x11, 0x68, 0x99, \ + 0x6c, 0x49, 0x4c, 0x03, \ + 0x4c, 0x03, 0xc0, 0x11, \ + 0x40, 0x11, 0x6d, 0xf1, \ + 0x52, 0x18, 0x8c, 0x0b, \ + 0x0c, 0x19, 0xe8, 0x00, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x0c, 0x0c, 0x8c, 0x0b, \ + 0x9f, 0xf5, 0xf2, 0x9c, \ + 0xf8, 0x04, 0x04, 0x0c, \ + 0x6e, 0xa1, 0x00, 0x19, \ + 0x00, 0x19, 0xc0, 0x30, \ + 0x40, 0x30, 0x6d, 0xa5, \ + 0x82, 0x45, 0xa1, 0x1b, \ + 0xe0, 0x8c, 0x42, 0x4a, \ + 0x06, 0x11, 0x04, 0x00, \ + 0x01, 0xc5, 0x80, 0xc3, \ + 0x06, 0x01, 0x04, 0x00, \ + 0x42, 0x4a, 0xc0, 0xc3, \ + 0x04, 0x00, 0xe0, 0x8c, \ + 0x80, 0xc3, 0x06, 0x31, \ + 0x06, 0x21, 0x04, 0x00, \ + 0x00, 0x69, 0xc0, 0xc3, \ + 0xc0, 0x2f, 0x65, 0x6d, \ + 0x00, 0x5b, 0x88, 0x24, \ + 0xc0, 0x17, 0x6b, 0x21, \ + 0x6d, 0x99, 0x00, 0x45, \ + 0xa3, 0x6b, 0x40, 0x0b, \ + 0x64, 0x69, 0x00, 0xc5, \ + 0x00, 0x35, 0xc0, 0x05, \ + 0xc0, 0x05, 0x64, 0x6d, \ + 0x04, 0x65, 0x00, 0x65, \ + 0x84, 0xc5, 0x80, 0x05, \ + 0x00, 0x65, 0xa5, 0x6b, \ + 0x80, 0x05, 0x04, 0x61, \ + 0x00, 0x75, 0x84, 0x25, \ + 0x40, 0x0c, 0x63, 0x91, \ + 0x02, 0x85, 0xa7, 0x6b, \ + 0x9c, 0x00, 0x4c, 0x70, \ + 0x64, 0x61, 0x00, 0x65, \ + 0xcc, 0x7d, 0xc0, 0x05, \ + 0x04, 0x65, 0x00, 0x65, \ + 0x4c, 0x62, 0x80, 0x05, \ + 0x42, 0x4a, 0x9a, 0x9c, \ + 0xd8, 0xc7, 0x60, 0x8c, \ + 0x02, 0x64, 0x02, 0xe5, \ + 0x58, 0x57, 0xe0, 0xb0, \ + 0x4a, 0x5a, 0xd8, 0x47, \ + 0x48, 0x4a, 0xe0, 0x0a, \ + 0x40, 0x55, 0xe0, 0x0a, \ + 0xc0, 0x0c, 0x63, 0x95, \ + 0x63, 0x99, 0x40, 0x45, \ + 0x48, 0x1a, 0xc0, 0x0c, \ + 0xd8, 0x1d, 0x60, 0x0a, \ + 0x42, 0x12, 0x58, 0x37, \ + 0x40, 0x45, 0xd1, 0xb0, \ + 0x40, 0x0c, 0x63, 0x9d, \ + 0x48, 0x1a, 0x81, 0x41, \ + 0x42, 0x3a, 0xc0, 0x0a, \ + 0x40, 0x45, 0xe0, 0x0a, \ + 0xc0, 0x0c, 0x63, 0xa1, \ + 0x63, 0xa5, 0x40, 0x15, \ + 0x0c, 0x0c, 0xc0, 0x0c, \ + 0xdd, 0x93, 0xf0, 0x04, \ + 0xfa, 0x3c, 0x04, 0x0c, \ + 0x00, 0xb6, 0x00, 0x8b, \ + 0x50, 0x01, 0xe0, 0x08, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x06, 0x7b, 0x88, 0xab, \ + 0x5b, 0x78, 0x84, 0x9b, \ + 0x82, 0x0b, 0x6f, 0x94, \ + 0x6f, 0x94, 0x5c, 0xc8, \ + 0x21, 0x0b, 0x80, 0x6b, \ + 0x02, 0x06, 0xff, 0x11, \ + 0xa3, 0xab, 0x00, 0x00, \ + 0x82, 0x03, 0x02, 0xe5, \ + 0x82, 0x95, 0xd3, 0x0d, \ + 0x84, 0xa5, 0xa3, 0x7b, \ + 0x50, 0x95, 0xa1, 0x7b, \ + 0xc0, 0x0c, 0x05, 0x6d, \ + 0x50, 0x95, 0x84, 0x15, \ + 0xc0, 0x0c, 0x65, 0x6d, \ + 0xa3, 0x7b, 0x83, 0x35, \ + 0xa1, 0x7b, 0x82, 0xc5, \ + 0x05, 0x6d, 0x50, 0x05, \ + 0x82, 0x35, 0xc0, 0x0c, \ + 0x65, 0x6d, 0x50, 0x05, \ + 0x81, 0x85, 0xc0, 0x0c, \ + 0x65, 0x71, 0x50, 0x95, \ + 0x81, 0x45, 0xc0, 0x0c, \ + 0x65, 0x71, 0x50, 0x05, \ + 0x10, 0x0b, 0xc0, 0x0c, \ + 0x66, 0xd8, 0x8e, 0x1b, \ + 0x5b, 0x38, 0xef, 0xf8, \ + 0x0e, 0x64, 0x6f, 0xfc, \ + 0x42, 0x86, 0xe1, 0xa0, \ + 0x84, 0x14, 0xe1, 0x18, \ + 0x42, 0xc6, 0x04, 0x76, \ + 0x84, 0x14, 0xe1, 0x18, \ + 0x0c, 0x0c, 0x84, 0x66, \ + 0x1c, 0x38, 0xf2, 0x3c, \ + 0xc1, 0xc3, 0xff, 0xfc, \ + 0xfa, 0x04, 0x04, 0x4c, \ + 0x86, 0x6b, 0x42, 0x27, \ + 0x04, 0x24, 0x84, 0x21, \ + 0x06, 0x24, 0xfa, 0x80, \ + 0x82, 0x33, 0xe1, 0x84, \ + 0x60, 0x0a, 0x4e, 0x3a, \ + 0x80, 0xf5, 0xc6, 0x2d, \ + 0x65, 0x0d, 0x00, 0x15, \ + 0x02, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x75, 0xe1, 0x4c, \ + 0x6d, 0x09, 0x00, 0x39, \ + 0x00, 0x00, 0x40, 0x2f, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0xff, 0x21, 0x01, 0x11, \ + 0xef, 0xc0, 0x5e, 0x88, \ + 0x60, 0x01, 0x00, 0x06, \ + 0x81, 0x31, 0x7d, 0x09, \ + 0xe0, 0x1e, 0x40, 0x0a, \ + 0xe5, 0x1a, 0x42, 0x0a, \ + 0xe5, 0x9a, 0x44, 0x0a, \ + 0x64, 0x81, 0x00, 0x10, \ + 0x42, 0x22, 0xc7, 0xa1, \ + 0x04, 0x0e, 0x72, 0x8c, \ + 0xc0, 0x03, 0x74, 0x24, \ + 0x6f, 0x5c, 0x6f, 0x68, \ + 0x0c, 0x4c, 0x81, 0x31, \ + 0xdd, 0x93, 0xf2, 0x04, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x6c, 0x01, 0x0c, 0x12, \ + 0x81, 0x11, 0x40, 0x28, \ + 0x6f, 0x60, 0x58, 0x88, \ + 0x8c, 0x0b, 0x8c, 0x2b, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x03, 0x06, 0x02, 0x3b, \ + 0x04, 0x14, 0xe1, 0x18, \ + 0xe1, 0x24, 0x44, 0x86, \ + 0x88, 0x24, 0x84, 0x18, \ + 0x85, 0x1b, 0x88, 0x28, \ + 0x00, 0x15, 0x80, 0xf5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe1, 0x50, 0x02, 0x0a, \ + 0x00, 0x00, 0x82, 0x15, \ + 0x40, 0x54, 0x66, 0x51, \ + 0x9d, 0x21, 0x85, 0x11, \ + 0xff, 0xc0, 0x1a, 0xa8, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x03, 0x86, 0x02, 0x3b, \ + 0x04, 0x14, 0xe1, 0x18, \ + 0xe1, 0x24, 0x44, 0xc6, \ + 0x88, 0x24, 0x84, 0x18, \ + 0x85, 0x1b, 0x88, 0x28, \ + 0x00, 0x15, 0x80, 0xf5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe1, 0x54, 0x02, 0x0a, \ + 0x00, 0x00, 0x82, 0x15, \ + 0x40, 0x54, 0x66, 0x51, \ + 0x9f, 0x21, 0x85, 0x11, \ + 0xff, 0xc0, 0x19, 0x08, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x43, 0xc6, 0x02, 0x3b, \ + 0x04, 0x14, 0xe1, 0x24, \ + 0xe1, 0x18, 0x44, 0x46, \ + 0x88, 0x24, 0x84, 0x18, \ + 0xc9, 0x27, 0x88, 0x28, \ + 0x85, 0x1b, 0x8e, 0x29, \ + 0x00, 0x15, 0x80, 0xf5, \ + 0xc0, 0x0c, 0x65, 0x0d, \ + 0xe1, 0x58, 0x02, 0x0a, \ + 0x00, 0x00, 0x82, 0x15, \ + 0x40, 0x54, 0x66, 0x51, \ + 0xa1, 0x21, 0x85, 0x11, \ + 0xff, 0xc0, 0x17, 0x48, \ + 0xc1, 0xc3, 0xdd, 0x93, \ + 0x43, 0x86, 0x02, 0x3b, \ + 0x04, 0x14, 0xe1, 0x24, \ + 0xe1, 0x18, 0x44, 0x46, \ + 0x88, 0x24, 0x84, 0x18, \ + 0x9e, 0x29, 0x88, 0x28, \ + 0x80, 0xf5, 0x85, 0x1b, \ + 0x65, 0x0d, 0x00, 0x15, \ + 0x02, 0x0a, 0xc0, 0x0c, \ + 0x82, 0x15, 0xe1, 0x5c, \ + 0x66, 0x51, 0x00, 0x00, \ + 0x85, 0x11, 0x40, 0x54, \ + 0x15, 0x98, 0xa3, 0x21, \ + 0xdd, 0x93, 0xff, 0xc0, \ + 0x6c, 0x80, 0x04, 0x12, \ + 0x81, 0x31, 0x81, 0x11, \ + 0x48, 0x00, 0xc5, 0x3d, \ + 0x00, 0x04, 0x50, 0x04, \ + 0x42, 0x12, 0xc1, 0x84, \ + 0x06, 0x30, 0x50, 0x90, \ + 0xfc, 0xf5, 0xc4, 0x04, \ + 0x82, 0x0b, 0x5d, 0x93, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x0c, 0x09, 0x80, 0x6b, \ + 0xc0, 0x2f, 0x66, 0x55, \ + 0xe0, 0x84, 0x40, 0x0a, \ + 0x06, 0x49, 0x0c, 0x19, \ + 0x40, 0x06, 0x40, 0x2f, \ + 0x00, 0x05, 0xc1, 0x00, \ + 0x40, 0x11, 0xc0, 0x00, \ + 0x84, 0x95, 0xc0, 0x00, \ + 0x6d, 0xc1, 0x0c, 0x09, \ + 0x40, 0x0a, 0xc0, 0x30, \ + 0x82, 0xc5, 0xe0, 0x80, \ + 0x63, 0x95, 0x0c, 0x15, \ + 0x85, 0x31, 0x40, 0x0c, \ + 0x8c, 0x0b, 0x03, 0x41, \ + 0x6f, 0xfc, 0x6d, 0x88, \ + 0x8c, 0x0b, 0x82, 0x2b, \ + 0xf2, 0x00, 0x0c, 0x0c, \ + 0x0c, 0x0c, 0x81, 0x45, \ + 0xdd, 0x93, 0xf2, 0x00, \ + 0xfa, 0xfc, 0x04, 0x0c, \ + 0x41, 0x06, 0x00, 0x6b, \ + 0x80, 0x04, 0xe1, 0x38, \ + 0x42, 0x10, 0x80, 0x18, \ + 0x80, 0x16, 0xf1, 0xfc, \ + 0x66, 0x55, 0x0c, 0x09, \ + 0x40, 0x4a, 0xc0, 0x2f, \ + 0x00, 0x04, 0x60, 0x98, \ + 0x80, 0xc5, 0xfa, 0x00, \ + 0x6d, 0x95, 0x0c, 0x19, \ + 0xa1, 0x1b, 0xc0, 0x30, \ + 0xff, 0x11, 0x82, 0x85, \ + 0x82, 0x35, 0x82, 0x0d, \ + 0x63, 0x6d, 0x0c, 0x19, \ + 0xa1, 0x1b, 0xc0, 0x27, \ + 0x02, 0x60, 0x02, 0x85, \ + 0x80, 0x9c, 0x07, 0x65, \ + 0xe0, 0x94, 0x40, 0x0a, \ + 0x05, 0x55, 0x02, 0x60, \ + 0x02, 0x60, 0xc0, 0xa1, \ + 0x80, 0xbd, 0x06, 0x49, \ + 0x53, 0xf6, 0x04, 0x18, \ + 0x0c, 0x39, 0xe0, 0x00, \ + 0xc0, 0x2f, 0x66, 0x4d, \ + 0x70, 0x84, 0x44, 0x32, \ + 0x05, 0x0a, 0xa6, 0x13, \ + 0x14, 0x12, 0x64, 0xfc, \ + 0x12, 0x24, 0xe0, 0x00, \ + 0x40, 0x0a, 0x1a, 0x80, \ + 0x00, 0x60, 0xe0, 0x90, \ + 0x40, 0x9c, 0x07, 0x71, \ + 0xc0, 0x00, 0x02, 0x06, \ + 0x4f, 0x60, 0x49, 0xe8, \ + 0xc0, 0x18, 0x04, 0x06, \ + 0x61, 0x00, 0x50, 0x06, \ + 0xe1, 0x00, 0x0f, 0x86, \ + 0x81, 0x01, 0x3e, 0xae, \ + 0x20, 0x04, 0x89, 0x15, \ + 0xe0, 0x08, 0x42, 0x06, \ + 0x00, 0xa6, 0x83, 0x21, \ + 0x71, 0x78, 0x8c, 0x0b, \ + 0x0c, 0x09, 0xef, 0xf8, \ + 0xc0, 0x31, 0x64, 0x51, \ + 0xe0, 0x9c, 0x40, 0x0a, \ + 0xc0, 0x08, 0x00, 0x06, \ + 0xcf, 0xc4, 0x41, 0x48, \ + 0x60, 0x3c, 0x42, 0x86, \ + 0x6b, 0xd8, 0x8c, 0x0b, \ + 0x6a, 0x58, 0xef, 0xe0, \ + 0x8c, 0x0b, 0x6f, 0xf8, \ + 0x6d, 0x99, 0x0c, 0x09, \ + 0x44, 0x62, 0x40, 0x30, \ + 0x9c, 0x14, 0xf0, 0xac, \ + 0x68, 0x71, 0x44, 0x01, \ + 0x84, 0x08, 0xc0, 0x27, \ + 0xef, 0xdc, 0x6c, 0xa8, \ + 0x66, 0x55, 0x0c, 0x19, \ + 0x42, 0x0a, 0xc0, 0x2f, \ + 0x82, 0x65, 0xe0, 0x90, \ + 0x60, 0x88, 0x02, 0xb4, \ + 0x67, 0x70, 0x04, 0x60, \ + 0x04, 0x13, 0xc0, 0x9c, \ + 0x84, 0x0e, 0x96, 0x23, \ + 0xe4, 0x00, 0x44, 0xa1, \ + 0x6d, 0x89, 0x0c, 0x15, \ + 0xc2, 0x0d, 0xc0, 0x0b, \ + 0xc0, 0x00, 0x02, 0x06, \ + 0x03, 0x6d, 0x4c, 0x11, \ + 0x80, 0xd5, 0xc0, 0x27, \ + 0x16, 0xa4, 0x82, 0xa1, \ + 0xd2, 0xbd, 0xfa, 0x80, \ + 0x0c, 0x19, 0xee, 0xa5, \ + 0x40, 0x2f, 0x66, 0x55, \ + 0x42, 0x0a, 0x82, 0x91, \ + 0x1e, 0x25, 0xe0, 0x90, \ + 0x44, 0xb2, 0xc0, 0x04, \ + 0x4c, 0x21, 0xd1, 0x88, \ + 0x40, 0x27, 0x03, 0x61, \ + 0x80, 0xe5, 0x93, 0xbb, \ + 0x65, 0x0d, 0x0c, 0x25, \ + 0x04, 0x0a, 0xc0, 0x0c, \ + 0x8a, 0xf5, 0xe1, 0x60, \ + 0x61, 0x38, 0x4f, 0x06, \ + 0x1c, 0x04, 0x83, 0x11, \ + 0x4c, 0x11, 0x8f, 0x21, \ + 0xc0, 0x27, 0x63, 0x6d, \ + 0x02, 0x14, 0x80, 0x18, \ + 0x80, 0x16, 0xfc, 0x9c, \ + 0x63, 0x65, 0x0c, 0x09, \ + 0xa0, 0x14, 0xc0, 0x27, \ + 0x00, 0x60, 0x04, 0x06, \ + 0xc0, 0x54, 0x66, 0x51, \ + 0x6d, 0x09, 0x0c, 0x39, \ + 0x81, 0x11, 0x40, 0x2f, \ + 0xef, 0xc0, 0x46, 0x28, \ + 0x80, 0x18, 0x9c, 0x04, \ + 0xfc, 0x9c, 0x02, 0x14, \ + 0x0c, 0x09, 0x80, 0x16, \ + 0xc0, 0x31, 0x64, 0x55, \ + 0xe0, 0x80, 0x40, 0x0a, \ + 0x0c, 0x0c, 0x80, 0x95, \ + 0xdd, 0x93, 0xf2, 0xfc, \ + 0xe0, 0x10, 0x00, 0x06, \ + 0xf2, 0xfc, 0x0c, 0x0c, \ + 0xff, 0xc0, 0x39, 0xe8, \ + 0x42, 0x0a, 0x20, 0x24, \ + 0x4c, 0xa1, 0xe0, 0x94, \ + 0x40, 0x27, 0x63, 0x65, \ + 0x05, 0x51, 0x02, 0x60, \ + 0x02, 0x60, 0xc0, 0xa1, \ + 0x00, 0xbd, 0x06, 0x51, \ + 0xe1, 0x38, 0x4f, 0x06, \ + 0x96, 0x13, 0x84, 0x18, \ + 0xec, 0xfc, 0x02, 0x12, \ + 0x0c, 0x29, 0x88, 0x16, \ + 0xc0, 0x30, 0x6d, 0x1d, \ + 0x63, 0x69, 0x4c, 0x11, \ + 0x4c, 0x05, 0xc0, 0x27, \ + 0xc0, 0x09, 0x6c, 0xd5, \ + 0xe0, 0x88, 0x44, 0x0a, \ + 0x61, 0x58, 0xf2, 0xc5, \ + 0x8c, 0x0b, 0x6f, 0xf8, \ + 0x6d, 0x99, 0x0c, 0x09, \ + 0x4c, 0x01, 0xc0, 0x30, \ + 0xc0, 0x30, 0x6d, 0x9d, \ + 0xc1, 0xc3, 0xf9, 0x45, \ + 0xfa, 0x00, 0x04, 0x0c, \ + 0x40, 0xa6, 0x00, 0x6b, \ + 0x4c, 0x01, 0xe0, 0x00, \ + 0x40, 0x2f, 0x6d, 0x09, \ + 0x4f, 0x18, 0x8c, 0x0b, \ + 0x5e, 0xb8, 0xef, 0xf4, \ + 0x8c, 0x0b, 0x6f, 0xe8, \ + 0x6d, 0xb5, 0x0c, 0x09, \ + 0x40, 0x00, 0xc0, 0x30, \ + 0x4c, 0x01, 0xf3, 0x7c, \ + 0x40, 0x30, 0x6d, 0xb5, \ + 0x0c, 0x0c, 0x8c, 0x0b, \ + 0x1d, 0xd8, 0xf2, 0x00, \ + 0xc1, 0xc3, 0xff, 0xa8, \ + 0x66, 0x8d, 0x00, 0x1b, \ + 0x42, 0x4a, 0xc0, 0x18, \ + 0x5c, 0x08, 0xe0, 0x80, \ + 0x00, 0x29, 0x9a, 0x00, \ + 0xc0, 0x2f, 0x6d, 0x41, \ + 0x6d, 0x45, 0x00, 0x39, \ + 0x00, 0x49, 0xc0, 0x2f, \ + 0xc0, 0x2f, 0x6d, 0x39, \ + 0x6d, 0x3d, 0x00, 0x59, \ + 0xc5, 0x27, 0x40, 0x2f, \ + 0x45, 0x47, 0xc5, 0x37, \ + 0xe6, 0x8a, 0x44, 0x3a, \ + 0x46, 0x5a, 0xc5, 0x57, \ + 0xc4, 0x3f, 0xe6, 0x92, \ + 0xfb, 0xfc, 0x06, 0x0a, \ + 0x9a, 0x00, 0x5c, 0x08, \ + 0x6d, 0xa1, 0x00, 0x29, \ + 0x42, 0x0a, 0x40, 0x30, \ + 0x02, 0x20, 0xe0, 0x84, \ + 0x42, 0x12, 0xec, 0x04, \ + 0x02, 0x19, 0xd0, 0x80, \ + 0xc0, 0x30, 0x0d, 0x41, \ + 0x6d, 0x81, 0x40, 0x11, \ + 0xdd, 0x93, 0xc0, 0x30, \ + 0xe1, 0x04, 0x03, 0x86, \ + 0x84, 0x1a, 0x84, 0x14, \ + 0x6e, 0x91, 0x40, 0x13, \ + 0xdd, 0x93, 0xc0, 0x17, \ + 0xf8, 0x7c, 0x04, 0x0c, \ + 0xec, 0xa0, 0x1e, 0xf0, \ + 0x6d, 0x31, 0x00, 0x69, \ + 0x87, 0x91, 0x40, 0x2f, \ + 0x6d, 0x39, 0x00, 0x29, \ + 0x8f, 0x31, 0x40, 0x2f, \ + 0x6d, 0x41, 0x00, 0x49, \ + 0x85, 0x81, 0x40, 0x2f, \ + 0x8d, 0x91, 0x13, 0x96, \ + 0x66, 0x9a, 0x42, 0x2a, \ + 0x15, 0x96, 0x8b, 0x71, \ + 0x42, 0x1a, 0x93, 0x91, \ + 0x89, 0xb1, 0x60, 0x12, \ + 0x99, 0x91, 0x17, 0x96, \ + 0xbe, 0x1e, 0xbd, 0x36, \ + 0x6d, 0x3d, 0x00, 0x19, \ + 0x1f, 0x36, 0xc0, 0x2f, \ + 0x5e, 0x91, 0x9b, 0x31, \ + 0x3c, 0x1e, 0xe0, 0x90, \ + 0x60, 0x31, 0x02, 0x0e, \ + 0x0d, 0x1e, 0xc2, 0x41, \ + 0xe0, 0x40, 0x02, 0x16, \ + 0x6d, 0x35, 0x00, 0xa9, \ + 0x1d, 0x1c, 0xc0, 0x2f, \ + 0x19, 0x96, 0x95, 0x11, \ + 0x00, 0xc9, 0x83, 0x91, \ + 0x40, 0x2f, 0x6d, 0x45, \ + 0xbf, 0x16, 0xa9, 0xab, \ + 0x97, 0x11, 0x21, 0x16, \ + 0x91, 0x91, 0x1b, 0x96, \ + 0xe0, 0x80, 0x5e, 0x81, \ + 0x9f, 0x81, 0x25, 0x86, \ + 0xe0, 0x94, 0x5e, 0x31, \ + 0x9d, 0x31, 0x23, 0x36, \ + 0xe0, 0x8c, 0x5e, 0x11, \ + 0x81, 0x11, 0x2b, 0x16, \ + 0xe0, 0x84, 0x5e, 0x71, \ + 0xe0, 0x88, 0x5e, 0x91, \ + 0x12, 0x06, 0x29, 0x96, \ + 0x5e, 0x31, 0xc0, 0x00, \ + 0x5e, 0x81, 0xe0, 0x98, \ + 0x2f, 0x86, 0xe0, 0x9c, \ + 0xc0, 0x00, 0x10, 0x06, \ + 0x91, 0x16, 0xad, 0x36, \ + 0xa7, 0x76, 0x9d, 0xb6, \ + 0x01, 0x71, 0x92, 0x15, \ + 0x83, 0x95, 0x81, 0x91, \ + 0x64, 0x60, 0x0a, 0xf0, \ + 0x0a, 0x39, 0x82, 0xb1, \ + 0x80, 0x33, 0xfc, 0x0c, \ + 0x64, 0x81, 0x46, 0x71, \ + 0x82, 0x71, 0x40, 0x1c, \ + 0x6d, 0x31, 0x00, 0x69, \ + 0x0a, 0x64, 0xc0, 0x2f, \ + 0x06, 0xb4, 0x7a, 0x80, \ + 0x94, 0x53, 0xfa, 0x80, \ + 0xfa, 0xb5, 0xcb, 0x3d, \ + 0x6d, 0x35, 0x00, 0xb9, \ + 0x82, 0x91, 0x40, 0x2f, \ + 0xf4, 0x2c, 0x52, 0x8a, \ + 0xc0, 0x88, 0x06, 0x94, \ + 0x13, 0xf1, 0x54, 0x30, \ + 0x7a, 0xd5, 0xc0, 0x03, \ + 0xdc, 0x00, 0x56, 0xa0, \ + 0x6d, 0x31, 0x00, 0x39, \ + 0xa3, 0x3b, 0xc0, 0x30, \ + 0xdf, 0xfc, 0x51, 0xee, \ + 0x64, 0xbd, 0x40, 0x81, \ + 0x85, 0x75, 0xc0, 0x1c, \ + 0x64, 0x20, 0x0a, 0xf0, \ + 0x0a, 0x39, 0x82, 0xb1, \ + 0x80, 0x33, 0xfc, 0x0c, \ + 0x64, 0x81, 0x46, 0x81, \ + 0x82, 0x81, 0x40, 0x1c, \ + 0x6d, 0x31, 0x00, 0x69, \ + 0x0a, 0x64, 0xc0, 0x2f, \ + 0x06, 0xb4, 0x7a, 0x80, \ + 0x94, 0x53, 0xfa, 0x80, \ + 0xfa, 0xb5, 0xcb, 0x3d, \ + 0x6d, 0x35, 0x00, 0xa9, \ + 0x82, 0x91, 0x40, 0x2f, \ + 0xf4, 0x28, 0x52, 0x8a, \ + 0xda, 0x80, 0x06, 0x94, \ + 0xc6, 0x9e, 0x54, 0x3a, \ + 0x56, 0xa0, 0x7a, 0xf5, \ + 0x47, 0xe6, 0xdc, 0x00, \ + 0x40, 0x31, 0xe0, 0x0c, \ + 0xc0, 0x1c, 0x64, 0xbd, \ + 0xbc, 0x54, 0xbe, 0x34, \ + 0xe0, 0x12, 0x44, 0x2a, \ + 0xfa, 0x80, 0x06, 0x34, \ + 0x60, 0x32, 0x48, 0x5a, \ + 0x64, 0x80, 0x0a, 0x00, \ + 0x87, 0x25, 0xc0, 0x71, \ + 0x6e, 0x91, 0x00, 0xcb, \ + 0x58, 0x0a, 0xc0, 0x17, \ + 0x59, 0xe6, 0xe4, 0x84, \ + 0x4c, 0x50, 0x00, 0x0c, \ + 0x88, 0xa5, 0x9c, 0x00, \ + 0x6d, 0x29, 0x00, 0xc9, \ + 0xe5, 0xcd, 0xc0, 0x30, \ + 0x94, 0xc8, 0x88, 0x35, \ + 0x6d, 0x31, 0x00, 0x69, \ + 0x00, 0x79, 0xc0, 0x2f, \ + 0xc0, 0x30, 0x6d, 0x81, \ + 0xe0, 0x1a, 0x50, 0xca, \ + 0xe0, 0x0a, 0x52, 0x7a, \ + 0xe1, 0x1a, 0x58, 0xca, \ + 0xe6, 0x92, 0x50, 0x8a, \ + 0xe1, 0x0a, 0x4c, 0x7a, \ + 0x58, 0xca, 0x10, 0x93, \ + 0x0e, 0x94, 0xe6, 0x8a, \ + 0x50, 0x02, 0x7a, 0x80, \ + 0x4e, 0x7a, 0xf0, 0x84, \ + 0x8c, 0xc3, 0x66, 0x8e, \ + 0x64, 0x81, 0x0c, 0x80, \ + 0x8e, 0xc3, 0xc0, 0x71, \ + 0x02, 0x11, 0x98, 0xc6, \ + 0x02, 0x0a, 0x82, 0x51, \ + 0xf2, 0xb5, 0xe0, 0x40, \ + 0x0c, 0x0c, 0xd0, 0xf1, \ + 0xdd, 0x93, 0xf0, 0x7c, \ + 0x00, 0x00, 0x10, 0x14, \ + 0x00, 0x43, 0x4b, 0x40, \ + 0x00, 0x00, 0x09, 0x0c, \ + 0x00, 0x00, 0x19, 0xb4, \ + 0x00, 0x43, 0x54, 0xe0, \ + 0x00, 0x00, 0x00, 0x20, \ + 0x00, 0x00, 0x19, 0x20, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x94, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x00, \ + 0x00, 0x80, 0x02, 0x11, \ + 0x00, 0x80, 0x02, 0x12, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x16, \ + 0x00, 0x80, 0x02, 0x17, \ + 0x00, 0x80, 0x02, 0x18, \ + 0x00, 0x80, 0x02, 0x19, \ + 0x00, 0x80, 0x02, 0x1a, \ + 0x00, 0x80, 0x02, 0x1b, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x1d, \ + 0x00, 0x80, 0x02, 0x1e, \ + 0x00, 0x80, 0x02, 0x20, \ + 0x00, 0x80, 0x02, 0x24, \ + 0x00, 0x80, 0x02, 0x26, \ + 0x00, 0x80, 0x02, 0x2c, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x2f, \ + 0x00, 0x80, 0x02, 0x46, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x4a, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x4c, \ + 0x00, 0x80, 0x02, 0x4d, \ + 0x00, 0x80, 0x02, 0x4e, \ + 0x00, 0x80, 0x02, 0x51, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x53, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x57, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x59, \ + 0x00, 0x80, 0x02, 0x5a, \ + 0x00, 0x80, 0x02, 0x5f, \ + 0x00, 0x80, 0x02, 0x60, \ + 0x00, 0x80, 0x02, 0x61, \ + 0x00, 0x80, 0x02, 0x62, \ + 0x00, 0x80, 0x02, 0x63, \ + 0x00, 0x80, 0x02, 0x64, \ + 0x00, 0x80, 0x02, 0x65, \ + 0x00, 0x80, 0x02, 0x66, \ + 0x00, 0x80, 0x02, 0x67, \ + 0x00, 0x80, 0x02, 0x68, \ + 0x00, 0x80, 0x02, 0x73, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x78, \ + 0x00, 0x80, 0x02, 0x7c, \ + 0x00, 0x80, 0x02, 0x81, \ + 0x00, 0x80, 0x02, 0x86, \ + 0x00, 0x80, 0x02, 0x87, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x95, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0x99, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0xa3, \ + 0x00, 0x80, 0x02, 0xa8, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x02, 0xaa, \ + 0x00, 0x80, 0x02, 0xab, \ + 0x00, 0x80, 0x02, 0xac, \ + 0x00, 0x80, 0x02, 0xad, \ + 0x00, 0x80, 0x02, 0xae, \ + 0x00, 0x80, 0x02, 0xb1, \ + 0x00, 0x80, 0x02, 0xb6, \ + 0x00, 0x80, 0x02, 0xb7, \ + 0x00, 0x80, 0x02, 0xb8, \ + 0x00, 0x80, 0x02, 0xb9, \ + 0x00, 0x80, 0x02, 0xba, \ + 0x00, 0x80, 0x02, 0xbb, \ + 0x00, 0x80, 0x02, 0xbc, \ + 0x00, 0x80, 0x02, 0xbd, \ + 0x00, 0x80, 0x02, 0xbe, \ + 0x00, 0x80, 0x02, 0xbf, \ + 0x00, 0x80, 0x02, 0xc2, \ + 0x00, 0x80, 0x02, 0xc3, \ + 0x00, 0x80, 0x02, 0xc4, \ + 0x00, 0x80, 0x02, 0xc5, \ + 0x00, 0x80, 0x40, 0x00, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x02, \ + 0x00, 0x80, 0x40, 0x03, \ + 0x00, 0x80, 0x40, 0x04, \ + 0x00, 0x80, 0x40, 0x06, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x09, \ + 0x00, 0x80, 0x40, 0x0a, \ + 0x00, 0x80, 0x40, 0x0b, \ + 0x00, 0x80, 0x40, 0x0c, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x0e, \ + 0x00, 0x80, 0x40, 0x0f, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x13, \ + 0x00, 0x80, 0x40, 0x14, \ + 0x00, 0x80, 0x40, 0x15, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x19, \ + 0x00, 0x80, 0x40, 0x1e, \ + 0x00, 0x80, 0x40, 0x1f, \ + 0x00, 0x80, 0x40, 0x20, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x24, \ + 0x00, 0x80, 0x40, 0x25, \ + 0x00, 0x80, 0x40, 0x3c, \ + 0x00, 0x80, 0x40, 0x3d, \ + 0x00, 0x80, 0x40, 0x3e, \ + 0x00, 0x80, 0x40, 0x3f, \ + 0x00, 0x80, 0x40, 0x40, \ + 0x00, 0x80, 0x40, 0x41, \ + 0x00, 0x80, 0x40, 0x42, \ + 0x00, 0x80, 0x40, 0x43, \ + 0x00, 0x80, 0x40, 0x44, \ + 0x00, 0x80, 0x40, 0x45, \ + 0x00, 0x80, 0x40, 0x46, \ + 0x00, 0x80, 0x40, 0x47, \ + 0x00, 0x80, 0x40, 0x48, \ + 0x00, 0x80, 0x40, 0x49, \ + 0x00, 0x80, 0x40, 0x4a, \ + 0x00, 0x80, 0x40, 0x4b, \ + 0x00, 0x80, 0x40, 0x4c, \ + 0x00, 0x80, 0x40, 0x4d, \ + 0x00, 0x80, 0x40, 0x4e, \ + 0x00, 0x80, 0x40, 0x4f, \ + 0x00, 0x80, 0x40, 0x50, \ + 0x00, 0x80, 0x40, 0x51, \ + 0x00, 0x80, 0x40, 0x52, \ + 0x00, 0x80, 0x40, 0x53, \ + 0x00, 0x80, 0x40, 0x54, \ + 0x00, 0x80, 0x40, 0x55, \ + 0x00, 0x80, 0x40, 0x56, \ + 0x00, 0x80, 0x40, 0x57, \ + 0x00, 0x80, 0x40, 0x58, \ + 0x00, 0x80, 0x40, 0x59, \ + 0x00, 0x80, 0x40, 0x5a, \ + 0x00, 0x80, 0x40, 0x5b, \ + 0x00, 0x80, 0x40, 0x5c, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x5e, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x60, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x69, \ + 0x00, 0x80, 0x40, 0x6a, \ + 0x00, 0x80, 0x40, 0x6b, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x74, \ + 0x00, 0x80, 0x40, 0x78, \ + 0x00, 0x80, 0x40, 0x79, \ + 0x00, 0x80, 0x40, 0x7a, \ + 0x00, 0x80, 0x40, 0x7c, \ + 0x00, 0x80, 0x40, 0x84, \ + 0x00, 0x80, 0x40, 0x85, \ + 0x00, 0x80, 0x40, 0x88, \ + 0x00, 0x80, 0x40, 0x89, \ + 0x00, 0x80, 0x40, 0x8e, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x90, \ + 0x00, 0x80, 0x40, 0x91, \ + 0x00, 0x80, 0x40, 0x99, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0x9f, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0xa1, \ + 0x00, 0x80, 0x40, 0xa2, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0xa8, \ + 0x00, 0x80, 0x40, 0xb0, \ + 0x00, 0x80, 0x40, 0xb6, \ + 0x00, 0x80, 0x40, 0xd0, \ + 0x00, 0x80, 0x40, 0xd1, \ + 0x00, 0x80, 0x40, 0xd4, \ + 0x00, 0x80, 0x40, 0xd5, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0xdd, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x40, 0xdf, \ + 0x00, 0x80, 0x43, 0xdc, \ + 0x00, 0x80, 0x43, 0xdd, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x43, 0xe4, \ + 0x00, 0x80, 0x44, 0x04, \ + 0x00, 0x80, 0x44, 0x28, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x44, 0x48, \ + 0x00, 0x80, 0x44, 0x4c, \ + 0x00, 0x80, 0x44, 0x50, \ + 0x00, 0x80, 0x44, 0x54, \ + 0x00, 0x80, 0x44, 0x58, \ + 0x00, 0x80, 0x44, 0x5c, \ + 0x00, 0x80, 0x44, 0x60, \ + 0x00, 0x80, 0x44, 0x64, \ + 0x00, 0x80, 0x44, 0x68, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x46, 0x98, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x48, 0xc8, \ + 0x00, 0x80, 0x48, 0xc9, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x48, 0xcb, \ + 0x00, 0x80, 0x48, 0xcc, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x49, 0x1c, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x34, 0x3c, \ + 0x00, 0x80, 0x80, 0x00, \ + 0x00, 0x80, 0x80, 0x01, \ + 0x00, 0x80, 0x80, 0x02, \ + 0x00, 0x80, 0x80, 0x06, \ + 0x00, 0x80, 0x80, 0x07, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x09, \ + 0x00, 0x80, 0x80, 0x0c, \ + 0x00, 0x80, 0x80, 0x0e, \ + 0x00, 0x80, 0x80, 0x10, \ + 0x00, 0x80, 0x80, 0x18, \ + 0x00, 0x80, 0x80, 0x1c, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x2d, \ + 0x00, 0x80, 0x80, 0x2e, \ + 0x00, 0x80, 0x80, 0x31, \ + 0x00, 0x80, 0x80, 0x32, \ + 0x00, 0x80, 0x80, 0x35, \ + 0x00, 0x80, 0x80, 0x36, \ + 0x00, 0x80, 0x80, 0x37, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x39, \ + 0x00, 0x80, 0x80, 0x3a, \ + 0x00, 0x80, 0x80, 0x3b, \ + 0x00, 0x80, 0x80, 0x3c, \ + 0x00, 0x80, 0x80, 0x3d, \ + 0x00, 0x80, 0x80, 0x3e, \ + 0x00, 0x80, 0x80, 0x40, \ + 0x00, 0x80, 0x80, 0x44, \ + 0x00, 0x80, 0x80, 0x48, \ + 0x00, 0x80, 0x80, 0x49, \ + 0x00, 0x80, 0x80, 0x4a, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x4c, \ + 0x00, 0x80, 0x80, 0x50, \ + 0x00, 0x80, 0x80, 0x54, \ + 0x00, 0x80, 0x80, 0x55, \ + 0x00, 0x80, 0x80, 0x56, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x58, \ + 0x00, 0x80, 0x80, 0x5c, \ + 0x00, 0x80, 0x80, 0x60, \ + 0x00, 0x80, 0x80, 0x61, \ + 0x00, 0x80, 0x80, 0x62, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x64, \ + 0x00, 0x80, 0x80, 0x68, \ + 0x00, 0x80, 0x80, 0x6c, \ + 0x00, 0x80, 0x80, 0x6d, \ + 0x00, 0x80, 0x80, 0x6e, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x70, \ + 0x00, 0x80, 0x80, 0x74, \ + 0x00, 0x80, 0x80, 0x78, \ + 0x00, 0x80, 0x80, 0x79, \ + 0x00, 0x80, 0x80, 0x7a, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x7c, \ + 0x00, 0x80, 0x80, 0x80, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x88, \ + 0x00, 0x80, 0x80, 0x8c, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x80, 0x94, \ + 0x00, 0x80, 0x80, 0x98, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x00, 0x80, \ + 0x00, 0x80, 0x00, 0x81, \ + 0x00, 0x80, 0x00, 0x82, \ + 0x00, 0x80, 0x00, 0x83, \ + 0x00, 0x80, 0x00, 0x84, \ + 0x00, 0x80, 0x00, 0x85, \ + 0x00, 0x80, 0x00, 0x86, \ + 0x00, 0x80, 0x00, 0x87, \ + 0x00, 0x80, 0x00, 0x88, \ + 0x00, 0x80, 0x00, 0x89, \ + 0x00, 0x80, 0x00, 0x8a, \ + 0x00, 0x80, 0x00, 0x8b, \ + 0x00, 0x80, 0x00, 0x8c, \ + 0x00, 0x80, 0x00, 0x8d, \ + 0x00, 0x80, 0x00, 0x8e, \ + 0x00, 0x80, 0x00, 0x8f, \ + 0x00, 0x80, 0x00, 0x90, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x01, 0x01, \ + 0x00, 0x80, 0x01, 0x02, \ + 0x00, 0x80, 0x01, 0x03, \ + 0x00, 0x80, 0x01, 0x04, \ + 0x00, 0x80, 0x01, 0x05, \ + 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x80, 0x01, 0x07, \ + 0x00, 0x80, 0x01, 0x0a, \ + 0x00, 0x80, 0x01, 0x0c, \ + 0x00, 0x81, 0x2f, 0xfc, \ + 0x00, 0x81, 0x2f, 0xf8, \ + 0x00, 0x00, 0x19, 0xd8, \ + 0x00, 0x00, 0xe1, 0xb8 \ +} + +#ifdef __cplusplus +} +#endif + +#endif /* __VL53L5_FW_DATA_H__ */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/Android.mk b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/Android.mk new file mode 100644 index 000000000000..4843c5ba5a54 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/Android.mk @@ -0,0 +1,32 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +CHIPONE_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +CHIPONE_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif +ifeq ($(CHIPONE_ESD),true) + KERNEL_CFLAGS += CONFIG_CHIPONE_ESD=y +endif + +ifneq ($(BOARD_USES_DOUBLE_TAP),) + KERNEL_CFLAGS += CONFIG_INPUT_CHIPONE_0FLASH_MMI_ENABLE_DOUBLE_TAP=y +endif + +ifneq ($(BOARD_USES_PANEL_NOTIFICATIONS),) + KERNEL_CFLAGS += CONFIG_INPUT_CHIPONE_PANEL_NOTIFICATIONS=y +endif + +ifneq ($(MOTO_PANEL_CHECK_TOUCH_STATE),) + KERNEL_CFLAGS += CONFIG_INPUT_CHIPONE_0FLASH_MMI_NOTIFY_TOUCH_STATE=y +endif + +include $(CLEAR_VARS) +ifneq ($(BOARD_USES_DOUBLE_TAP),) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/sensors_class.ko +endif +LOCAL_MODULE := chipone_tddi_fhd_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(CHIPONE_MMI_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/Kbuild b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/Kbuild new file mode 100644 index 000000000000..a688886262b6 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/Kbuild @@ -0,0 +1,40 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/chipone_tddi_fhd_mmi +ifneq ($(filter m y,$(CONFIG_CHIPONE_ESD)),) +EXTRA_CFLAGS += -DCONFIG_CTS_ESD_PROTECTION +endif + +#EXTRA_CFLAGS += -DCONFIG_CTS_I2C_HOST + +ifneq ($(filter m y,$(CONFIG_INPUT_CHIPONE_0FLASH_MMI_ENABLE_DOUBLE_TAP)),) + EXTRA_CFLAGS += -DCHIPONE_SENSOR_EN + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_CHIPONE_0FLASH_MMI_NOTIFY_TOUCH_STATE)),) + EXTRA_CFLAGS += -DCHIPONE_SET_TOUCH_STATE +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_CHIPONE_PANEL_NOTIFICATIONS)),) +EXTRA_CFLAGS += -DCHIPONE_CONFIG_PANEL_NOTIFICATIONS +endif + + +obj-m := chipone_tddi_fhd_mmi.o + +chipone_tddi_fhd_mmi-objs += cts_i2c_driver.o +chipone_tddi_fhd_mmi-objs += cts_core.o +chipone_tddi_fhd_mmi-objs += cts_platform.o +chipone_tddi_fhd_mmi-objs += cts_sfctrlv2.o +chipone_tddi_fhd_mmi-objs += cts_spi_flash.o +chipone_tddi_fhd_mmi-objs += cts_firmware.o +chipone_tddi_fhd_mmi-objs += cts_tool.o +chipone_tddi_fhd_mmi-objs += cts_sysfs.o +chipone_tddi_fhd_mmi-objs += cts_test.o +chipone_tddi_fhd_mmi-objs += cts_charger_detect.o +chipone_tddi_fhd_mmi-objs += cts_hostcomm.o +chipone_tddi_fhd_mmi-objs += cts_tcs.o +chipone_tddi_fhd_mmi-objs += cts_strerror.o + diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/Makefile b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/Makefile new file mode 100644 index 000000000000..5d83a7439bc9 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/Makefile @@ -0,0 +1,23 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + +obj-$(CONFIG_TOUCHSCREEN_CHIPONE) += chipone-tddi.o + +chipone-tddi-objs := cts_i2c_driver.o +chipone-tddi-objs += cts_core.o cts_sfctrlv2.o cts_spi_flash.o cts_firmware.o cts_test.o +chipone-tddi-objs += cts_charger_detect.o +chipone-tddi-objs += cts_hostcomm.o +chipone-tddi-objs += cts_tcs.o +chipone-tddi-objs += cts_platform.o +chipone-tddi-objs += cts_tool.o +chipone-tddi-objs += cts_sysfs.o +chipone-tddi-objs += cts_strerror.o + diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_builtin_firmware.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_builtin_firmware.h new file mode 100644 index 000000000000..5ea7b7bc3166 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_builtin_firmware.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +static u8 icnl9911s_driver_builtin_firmware[] = { +}; + +static u8 icnl9911c_driver_builtin_firmware[] = { +}; + +const static struct cts_firmware cts_driver_builtin_firmwares[] = { + { + .name = "OEM-Project", /* MUST set non-NULL */ + .hwid = CTS_DEV_HWID_ICNL9911S, + .fwid = CTS_DEV_FWID_ICNL9911S, + .data = icnl9911s_driver_builtin_firmware, + .size = ARRAY_SIZE(icnl9911s_driver_builtin_firmware), + }, + { + .name = "OEM-Project", /* MUST set non-NULL */ + .hwid = CTS_DEV_HWID_ICNL9911C, + .fwid = CTS_DEV_FWID_ICNL9911C, + .data = icnl9911c_driver_builtin_firmware, + .size = ARRAY_SIZE(icnl9911c_driver_builtin_firmware), + }, + +}; diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_charger_detect.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_charger_detect.c new file mode 100644 index 000000000000..2baf4aacab26 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_charger_detect.c @@ -0,0 +1,1104 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "Charger" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sysfs.h" + +#ifdef CONFIG_CTS_CHARGER_DETECT + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) +/** + * match_string - matches given string in an array + * @array: array of strings + * @n: number of strings in the array or -1 for NULL terminated arrays + * @string: string to match with + * + * Return: + * index of a @string in the @array if matches, or %-EINVAL otherwise. + */ +int match_string(const char *const *array, size_t n, const char *string) +{ + int index; + const char *item; + + for (index = 0; index < n; index++) { + item = array[index]; + if (!item) + break; + if (!strcmp(item, string)) + return index; + } + + return -EINVAL; +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +#define CFG_CTS_CHARGER_DETECT_PSY_NOTIFY +#endif +#define CFG_CTS_CHARGER_DETECT_PSY_POLL + +#include +#include + +enum cts_charger_detect_type { + CTS_CHGR_DET_TYPE_NONE = 0, + CTS_CHGR_DET_TYPE_PSY_NOTIFY, + CTS_CHGR_DET_TYPE_POLL_PSP, + CTS_CHGR_DET_TYPE_MAX +}; + +/* Over-written setting for DTS */ +/* #define CFG_CTS_DEF_CHGR_DET_ENABLE */ + +/* Default settings */ +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY +#define CFG_CTS_DEF_CHGR_DET_TYPE CTS_CHGR_DET_TYPE_PSY_NOTIFY +#else /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ +#define CFG_CTS_DEF_CHGR_DET_TYPE CTS_CHGR_DET_TYPE_POLL_PSP +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#define CFG_CTS_DEF_CHGR_DET_PSY_NAME "usb" +#define CFG_CTS_DEF_CHGR_DET_PSY_PROP POWER_SUPPLY_PROP_ONLINE +#define CFG_CTS_DEF_CHGR_DET_PSP_POLL_INTERVAL 2000u + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +/* Lower version has no power_supply_get_property() */ +#define POWER_SUPPLY_GET_PROPERTY(psy, psp, val) \ + power_supply_get_property(psy, psp, val) +#else +#define POWER_SUPPLY_GET_PROPERTY(psy, psp, val) \ + psy->get_property(psy, psp, val) +#endif + +struct cts_charger_detect_data { + bool enable; + bool running; + bool state; + + /* Parameter */ + enum cts_charger_detect_type type; + const char *psy_name; + enum power_supply_property psp; + u32 psp_poll_interval; /* Unit is ms */ + + /* Power supply notify */ +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY + bool notifier_registered; + struct notifier_block psy_notifier; +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL + /* Polling power supply property */ + struct delayed_work psp_poll_work; +#endif /* CFG_CTS_CHARGER_DETECT_PSY_POLL */ + + struct work_struct set_charger_state_work; + + /* Sysfs */ +#ifdef CONFIG_CTS_SYSFS + bool sysfs_created; +#endif /* CONFIG_CTS_SYSFS */ + + struct chipone_ts_data *cts_data; + +}; + +#define POWER_SUPPLY_PROP_PREFIX "POWER_SUPPLY_PROP_" +#define POWER_SUPPLY_PROP_TOKEN(property) \ + {.prop = POWER_SUPPLY_PROP_ ##property, \ + .name = POWER_SUPPLY_PROP_PREFIX #property} + +const static struct { + enum power_supply_property prop; + const char *name; +} power_supply_prop_token[] = { + POWER_SUPPLY_PROP_TOKEN(STATUS), + POWER_SUPPLY_PROP_TOKEN(PRESENT), + POWER_SUPPLY_PROP_TOKEN(ONLINE), +}; + +static const char *power_supply_prop_str(enum power_supply_property prop) +{ + int i; + + for (i = 0; i < sizeof(power_supply_prop_token); i++) { + if (prop == power_supply_prop_token[i].prop) + return power_supply_prop_token[i].name; + } + + return "Unknown"; +} + +static enum power_supply_property power_supply_prop_from_name(const char *name) +{ + size_t offset = strlen(POWER_SUPPLY_PROP_PREFIX); + int i; + + for (i = 0; i < ARRAY_SIZE(power_supply_prop_token); i++) { + if (strcasecmp(power_supply_prop_token[i].name + offset, name) + == 0 + || strcasecmp(power_supply_prop_token[i].name, name) == 0) { + return power_supply_prop_token[i].prop; + } + } + + return -ENOENT; +} + +static const char *charger_detect_type_text[] = { + "none", "notify", "poll", +}; + +static const char *charger_detect_type_str(enum cts_charger_detect_type type) +{ + return type < ARRAY_SIZE(charger_detect_type_text) ? + charger_detect_type_text[type] : "Unknown"; +} + +static int parse_charger_detect_dt(struct cts_charger_detect_data *cd_data, + struct device_node *np) +{ + const char *psy_name; + const char *type_str; + const char *psp_str; + int ret; + + cts_info("Parse dt"); + +#ifdef CFG_CTS_DEF_CHGR_DET_ENABLE + cd_data->enable = true; +#else /* CFG_CTS_DEF_CHGR_DET_ENABLE */ + cd_data->enable = + of_property_read_bool(np, "chipone,touch-charger-detect-enable"); +#endif /* CFG_CTS_DEF_CHGR_DET_ENABLE */ + + cd_data->type = CFG_CTS_DEF_CHGR_DET_TYPE; + ret = of_property_read_string(np, + "chipone,touch-charger-detect-type", + &type_str); + if (ret) + cts_warn("Parse detect type failed %d", ret); + else { + int type = match_string(charger_detect_type_text, + ARRAY_SIZE(charger_detect_type_text), + type_str); + if (type < 0) + cts_err("Parse detect type '%s' invalid", type_str); + else + cd_data->type = type; + } + + ret = of_property_read_string(np, + "chipone,touch-charger-detect-psy-name", + &psy_name); + if (ret) { + cts_warn("Parse detect psy name failed %d", ret); + psy_name = CFG_CTS_DEF_CHGR_DET_PSY_NAME; + } else { + if (power_supply_get_by_name(psy_name) == NULL) { + cts_warn("Power supply '%s' not found", psy_name); + psy_name = CFG_CTS_DEF_CHGR_DET_PSY_NAME; + } + } + cd_data->psy_name = kstrdup(psy_name, GFP_KERNEL); + if (cd_data->psy_name == NULL) { + cts_err("Alloc mem for psy name failed"); + return -ENOMEM; + } + + cd_data->psp = CFG_CTS_DEF_CHGR_DET_PSY_PROP; + ret = of_property_read_string(np, + "chipone,touch-charger-detect-psp", + &psp_str); + if (ret) { + cts_warn("Parse detect psp failed %d", ret); + } else { + struct power_supply *psy; + enum power_supply_property psp; + union power_supply_propval val; + + psp = power_supply_prop_from_name(psp_str); + if (psp < 0) { + cts_warn("Parse detect psp: '%s' invalid", psp_str); + } else { + psy = power_supply_get_by_name(cd_data->psy_name); + if (psy != NULL && + POWER_SUPPLY_GET_PROPERTY(psy, psp, &val) >= 0) { + cts_err("Parse detect psp invalid"); + } else { + cd_data->psp = psp; + } + } + } + + cd_data->psp_poll_interval = CFG_CTS_DEF_CHGR_DET_PSP_POLL_INTERVAL; + ret = of_property_read_u32(np, + "chipone,touch-charger-detect-psp-poll-interval", + &cd_data->psp_poll_interval); + if (ret) + cts_warn("Parse detect psp poll interval failed %d", ret); + + return 0; +} + +static int start_charger_detect(struct cts_charger_detect_data *cd_data) +{ + if (!cd_data->enable) { + cts_warn("Start detect while NOT enabled"); + return -EINVAL; + } + + if (cd_data->running) { + cts_warn("Start detect while already RUNNING"); + return 0; + } + + cts_info("Start detect type: %d(%s)", + cd_data->type, charger_detect_type_str(cd_data->type)); + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY + if (cd_data->type == CTS_CHGR_DET_TYPE_PSY_NOTIFY) { + int ret = power_supply_reg_notifier(&cd_data->psy_notifier); + + if (ret) { + cts_err("Register notifier failed: %d", ret); + return ret; + } + cd_data->notifier_registered = true; + + cd_data->running = true; + + return 0; + } +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL + if (cd_data->type == CTS_CHGR_DET_TYPE_POLL_PSP) { + if (!queue_delayed_work(cd_data->cts_data->workqueue, + &cd_data->psp_poll_work, + msecs_to_jiffies + (cd_data->psp_poll_interval))) { + cts_warn + ("Queue detect work while already on the queue"); + } + cd_data->running = true; + + return 0; + } +#endif /* CFG_CTS_CHARGER_DETECT_PSY_POLL */ + + return -ENOTSUPP; +} + +static int stop_charger_detect(struct cts_charger_detect_data *cd_data) +{ + if (!cd_data->running) { + cts_warn("Stop detect while NOT running"); + return 0; + } + + cts_info("Stop detect type: %d(%s)", + cd_data->type, charger_detect_type_str(cd_data->type)); + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY + if (cd_data->type == CTS_CHGR_DET_TYPE_PSY_NOTIFY) { + if (cd_data->notifier_registered) { + power_supply_unreg_notifier(&cd_data->psy_notifier); + cd_data->notifier_registered = false; + } + cd_data->running = false; + + return 0; + } +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL + if (cd_data->type == CTS_CHGR_DET_TYPE_POLL_PSP) { + if (!cancel_delayed_work_sync(&cd_data->psp_poll_work)) + cts_warn("Cancel poll psp work while NOT pending"); + + cd_data->running = false; + + return 0; + } +#endif /* CFG_CTS_CHARGER_DETECT_PSY_POLL */ + + return 0; +} + +static int enable_charger_detect(struct cts_charger_detect_data *cd_data) +{ + cts_info("Enable detect type: %d(%s)", + cd_data->type, charger_detect_type_str(cd_data->type)); + + cd_data->enable = true; + + return 0; +} + +static int disable_charger_detect(struct cts_charger_detect_data *cd_data) +{ + int ret; + + cts_info("Disable detect type: %d(%s)", + cd_data->type, charger_detect_type_str(cd_data->type)); + + ret = stop_charger_detect(cd_data); + if (ret) + cts_err("Disable detect failed %d", ret); + + cd_data->enable = false; + + return 0; +} + +static int get_charger_state(struct cts_charger_detect_data *cd_data) +{ + struct power_supply *psy; + union power_supply_propval propval; + int ret; + + cts_dbg("Get state from psy: '%s' prop: %d(%s)", + cd_data->psy_name, cd_data->psp, + power_supply_prop_str(cd_data->psp)); + + psy = power_supply_get_by_name(cd_data->psy_name); + if (cd_data->psy_name == NULL || psy == NULL) { + cts_err("Get state from psy: '%s' not found", + cd_data->psy_name); + return -EINVAL; + } + + ret = POWER_SUPPLY_GET_PROPERTY(psy, cd_data->psp, &propval); + if (ret < 0) { + cts_err("Get state from psy: '%s' prop: %d(%s) failed", + cd_data->psy_name, cd_data->psp, + power_supply_prop_str(cd_data->psp)); + return -EINVAL; + } + + /* ONLY for bool type */ + cd_data->state = !!propval.intval; + + cts_dbg("State: %s", cd_data->state ? "ATTACHED" : "DETACHED"); + + return 0; +} + +static int switch_charger_detect_type(struct cts_charger_detect_data *cd_data, + enum cts_charger_detect_type type) +{ + bool running; + + cts_info("Switch detect type to %d(%s)", + type, charger_detect_type_str(type)); + + if (type >= CTS_CHGR_DET_TYPE_MAX) { + cts_err("Switch detect type %d invalid", type); + return -EINVAL; + } + + if (cd_data->type == type) { + cts_warn("Switch detect type equal"); + return 0; + } + + running = cd_data->running; + + if (running) { + int ret = stop_charger_detect(cd_data); + + if (ret) { + cts_err("Stop detect failed %d", ret); + return ret; + } + } + + cd_data->type = type; + + if (running) { + int ret = start_charger_detect(cd_data); + + if (ret) { + cts_err("Start detect failed %d", ret); + cd_data->type = CTS_CHGR_DET_TYPE_NONE; + return ret; + } + } + + return 0; +} + +static void set_dev_charger_state_work(struct work_struct *work) +{ + struct cts_charger_detect_data *cd_data; + int ret; + + cd_data = container_of(work, struct cts_charger_detect_data, + set_charger_state_work); + + cts_lock_device(&cd_data->cts_data->cts_dev); + ret = cts_set_dev_charger_attached(&cd_data->cts_data->cts_dev, + cd_data->state); + cts_unlock_device(&cd_data->cts_data->cts_dev); + if (ret) { + cts_err("Set dev charger attached to %s failed %d", + cd_data->state ? "ATTACHED" : "DETATCHED", ret); + /* Set to previous state, try set again in next loop */ + cd_data->state = !cd_data->state; + } +} + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY +static int psy_notify_callback(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct cts_charger_detect_data *cd_data; + struct power_supply *psy; + bool prev_state; + int ret; + + if (nb == NULL || data == NULL) { + cts_err + ("PSY notify callback with notifier %p or data %p = NULL", + nb, data); + return NOTIFY_DONE; + } + + if (action != PSY_EVENT_PROP_CHANGED) { + cts_dbg("Notify event %ld not care", action); + return NOTIFY_DONE; + } + + cd_data = + container_of(nb, struct cts_charger_detect_data, psy_notifier); + + psy = (struct power_supply *)data; + if (strcmp(psy->desc->name, cd_data->psy_name) != 0) { + cts_dbg("Notify from power supply '%s' not care", + psy->desc->name); + return NOTIFY_DONE; + } + + prev_state = cd_data->state; + + ret = get_charger_state(cd_data); + if (ret < 0) { + cts_err("Get state failed %d", ret); + return NOTIFY_DONE; + } + + cts_info("State changed: %s -> %s", + prev_state ? "ATTACHED" : "DETACHED", + cd_data->state ? "ATTACHED" : "DETACHED"); + + if (!queue_work(cd_data->cts_data->workqueue, + &cd_data->set_charger_state_work)) { + cts_warn("Set device charger state work is PENDING"); + } + + return NOTIFY_OK; +} +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL +static void poll_psp_work(struct work_struct *work) +{ + struct cts_charger_detect_data *cd_data; + bool prev_state; + int ret; + + cts_dbg("Poll psp work"); + + cd_data = container_of(to_delayed_work(work), + struct cts_charger_detect_data, psp_poll_work); + + prev_state = cd_data->state; + + ret = get_charger_state(cd_data); + if (ret < 0) { + cts_err("Get state failed %d", ret); + } else { + if (cd_data->state != prev_state) { + cts_info("State changed: %s -> %s", + prev_state ? "ATTACHED" : "DETACHED", + cd_data->state ? "ATTACHED" : "DETACHED"); + + if (!queue_work(cd_data->cts_data->workqueue, + &cd_data->set_charger_state_work)) { + cts_warn + ("Set device charger state work is PENDING"); + } + } + } + + if (!queue_delayed_work(cd_data->cts_data->workqueue, + &cd_data->psp_poll_work, + msecs_to_jiffies(cd_data->psp_poll_interval))) { + cts_warn("Queue detect work while already on the queue"); + } +} +#endif /* CFG_CTS_CHARGER_DETECT_PSY_POLL */ + +static int init_charger_detect(struct cts_charger_detect_data *cd_data) +{ + int ret; + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY + cd_data->psy_notifier.notifier_call = psy_notify_callback; +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL + INIT_DELAYED_WORK(&cd_data->psp_poll_work, poll_psp_work); +#endif /* CFG_CTS_CHARGER_DETECT_PSY_POLL */ + + INIT_WORK(&cd_data->set_charger_state_work, set_dev_charger_state_work); + + ret = get_charger_state(cd_data); + if (ret) + cts_err("Get state failed %d", ret); + + return 0; +} + +/* Sysfs */ +#ifdef CONFIG_CTS_SYSFS +extern int argc; +extern char *argv[]; +extern int parse_arg(const char *buf, size_t count); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) +/* From lib/kstrtox.c */ +/** + * kstrtobool - convert common user inputs into boolean values + * @s: input string + * @res: result + * + * This routine returns 0 iff the first character is one of 'Yy1Nn0', or + * [oO][NnFf] for "on" and "off". Otherwise it will return -EINVAL. Value + * pointed to by res is updated upon finding a match. + */ +int kstrtobool(const char *s, bool *res) +{ + if (!s) + return -EINVAL; + + switch (s[0]) { + case 'y': + case 'Y': + case '1': + *res = true; + return 0; + case 'n': + case 'N': + case '0': + *res = false; + return 0; + case 'o': + case 'O': + switch (s[1]) { + case 'n': + case 'N': + *res = true; + return 0; + case 'f': + case 'F': + *res = false; + return 0; + default: + break; + } + default: + break; + } + + return -EINVAL; +} +#endif + +#define CHARGER_DET_SYSFS_GROUP_NAME "charger-det" + +static ssize_t charger_detect_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + + cts_info("Read sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s'", + attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, + "Charger detect: %s\n", + cd_data->enable ? "ENABLED" : "DISABLED"); +} + +/* Echo 0/n/N/of/Of/Of/OF/1/y/Y/on/oN/On/ON > enable */ +static ssize_t charger_detect_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + bool enable; + int ret; + + cts_info("Write sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num of args"); + return -EINVAL; + } + + ret = kstrtobool(argv[0], &enable); + if (ret) { + cts_err("Invalid param of enable"); + return ret; + } + + if (enable) + ret = enable_charger_detect(cd_data); + else + ret = disable_charger_detect(cd_data); + + if (ret) { + cts_err("%s charger detect failed %d", + enable ? "Enable" : "Disable", ret); + return ret; + } + + return count; +} + +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + charger_detect_enable_show, charger_detect_enable_store); + +static ssize_t charger_detect_running_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + + cts_info("Read sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s'", + attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, + "Charger detect: %sRunning\n", + cd_data->running ? "" : "Not-"); +} + +/* Echo 0/n/N/of/Of/Of/OF/1/y/Y/on/oN/On/ON > runing */ +static ssize_t charger_detect_running_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + bool running; + int ret; + + cts_info("Write sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num of args"); + return -EINVAL; + } + + ret = kstrtobool(argv[0], &running); + if (ret) { + cts_err("Invalid param of running"); + return ret; + } + + if (running) + ret = start_charger_detect(cd_data); + else + ret = stop_charger_detect(cd_data); + + if (ret) { + cts_err("%s charger detect failed %d", + running ? "Start" : "Stop", ret); + return ret; + } + + return count; +} + +static DEVICE_ATTR(running, S_IWUSR | S_IRUGO, + charger_detect_running_show, charger_detect_running_store); + +static ssize_t charger_detect_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + + cts_info("Read sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s'", + attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, + "Charger detect type: %d(%s)\n", + cd_data->type, charger_detect_type_str(cd_data->type)); +} + +/* echo charger_detect_type > type */ +static ssize_t charger_detect_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + int type, ret; + + cts_info("Write sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc != 1) { + cts_err(""); + return -EINVAL; + } + + type = match_string(charger_detect_type_text, + ARRAY_SIZE(charger_detect_type_text), argv[0]); + if (type < 0) { + cts_err("Invalid charger detect type: '%s'", argv[0]); + return -EINVAL; + } + + ret = switch_charger_detect_type(cd_data, type); + if (ret) { + cts_err("Switch charger detect type failed %d", ret); + return ret; + } + + return count; +} + +static DEVICE_ATTR(type, S_IWUSR | S_IRUGO, + charger_detect_type_show, charger_detect_type_store); + +static ssize_t charger_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + int ret; + + cts_info("Read sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s'", + attr->attr.name); + + ret = get_charger_state(cd_data); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Get charge state failed %d\n", ret); + } + + return scnprintf(buf, PAGE_SIZE, + "Charger state: %s\n", + cd_data->state ? "ATTACHED" : "DETACHED"); +} + +static DEVICE_ATTR(state, S_IRUGO, charger_state_show, NULL); + +static ssize_t charger_detect_param_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + struct power_supply *psy; + + cts_info("Read sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s'", + attr->attr.name); + + psy = power_supply_get_by_name(cd_data->psy_name); + + return scnprintf(buf, PAGE_SIZE, + "Power supply name: %s(%sExist)\n" + "Power supply prop: %d(%s)\n" + "Poll interval : %dms\n", + cd_data->psy_name, psy == NULL ? "Non-" : "", + cd_data->psp, power_supply_prop_str(cd_data->psp), + cd_data->psp_poll_interval); +} + +/* echo psy_name psy_property [interval] > param */ +static ssize_t charger_detect_param_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + struct power_supply *psy; + union power_supply_propval propval; + enum power_supply_property psp; + u32 interval; + int ret; + + cts_info("Write sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc < 2 || argc > 3) { + cts_err("Invalid num of args"); + return -EINVAL; + } + + psy = power_supply_get_by_name(argv[0]); + if (psy == NULL) { + cts_err("Power supply '%s' not found", argv[0]); + return -EINVAL; + } + + psp = power_supply_prop_from_name(argv[1]); + if (psp < 0 || POWER_SUPPLY_GET_PROPERTY(psy, psp, &propval) < 0) { + cts_err("Power supply '%s' property '%s' not valid", + argv[0], power_supply_prop_str(psp)); + return -EINVAL; + } + + interval = cd_data->psp_poll_interval; + if (argc > 2) { + ret = kstrtou32(argv[2], 0, &interval); + if (ret) { + cts_err("Arg interval is invalid"); + return -EINVAL; + } + } + + if (cd_data->psy_name) + kfree(cd_data->psy_name); + + cd_data->psy_name = kstrdup(argv[0], GFP_KERNEL); + if (cd_data->psy_name == NULL) { + cts_err("Dup power supply name failed"); + return -ENOMEM; + } + + cd_data->psp = psp; + cd_data->psp_poll_interval = interval; + + return count; +} + +static DEVICE_ATTR(param, S_IWUSR | S_IRUGO, + charger_detect_param_show, charger_detect_param_store); + +static struct attribute *charger_detect_attrs[] = { + &dev_attr_enable.attr, + &dev_attr_running.attr, + &dev_attr_type.attr, + &dev_attr_state.attr, + &dev_attr_param.attr, + NULL +}; + +static const struct attribute_group charger_detect_attr_group = { + .name = CHARGER_DET_SYSFS_GROUP_NAME, + .attrs = charger_detect_attrs, +}; +#endif /* CONFIG_CTS_SYSFS */ + +int cts_charger_detect_init(struct chipone_ts_data *cts_data) +{ + struct cts_charger_detect_data *cd_data; + int ret = 0; + + cts_info("Init detect"); + + if (cts_data == NULL) { + cts_err("Init detect while cts_data = NULL"); + return -EFAULT; + } + + cd_data = kzalloc(sizeof(*cd_data), GFP_KERNEL); + if (cd_data == NULL) { + cts_err("Alloc charger detect data failed"); + return -ENOMEM; + } + +#ifdef CONFIG_CTS_OF + ret = parse_charger_detect_dt(cd_data, cts_data->device->of_node); +#else /* CONFIG_CTS_OF */ +#ifdef CFG_CTS_DEF_CHGR_DET_ENABLE + cd_data->enable = true; +#else /* CFG_CTS_DEF_CHGR_DET_ENABLE */ + cd_data->enable = false; +#endif /* CFG_CTS_DEF_CHGR_DET_ENABLE */ + + cd_data->type = CFG_CTS_DEF_CHGR_DET_TYPE; + cd_data->psp = CFG_CTS_DEF_CHGR_DET_PSY_PROP; + cd_data->psp_poll_interval = CFG_CTS_DEF_CHGR_DET_PSP_POLL_INTERVAL; + cd_data->psy_name = kstrdup(CFG_CTS_DEF_CHGR_DET_PSY_NAME, GFP_KERNEL); + if (cd_data->psy_name == NULL) { + cts_err("Dup power supply name failed"); + ret = -ENOMEM; + } +#endif /* CONFIG_CTS_OF */ + if (ret) { + cts_err("Get detect param failed %d", ret); + goto free_cd_data; + } + + cts_info("Detect: %sABLED", cd_data->enable ? "EN" : "DIS"); + cts_info(" Type : %s", charger_detect_type_str(cd_data->type)); + cts_info(" PSY Name: %s, prop: %d(%s)", cd_data->psy_name, + cd_data->psp, power_supply_prop_str(cd_data->psp)); + cts_info(" Poll Int: %dms", cd_data->psp_poll_interval); + + ret = init_charger_detect(cd_data); + if (ret) { + cts_err("Init detect failed %d", ret); + goto free_psy_name; + } + +#ifdef CONFIG_CTS_SYSFS + cts_info("Create sysfs group '%s'", CHARGER_DET_SYSFS_GROUP_NAME); + ret = sysfs_create_group(&cts_data->device->kobj, + &charger_detect_attr_group); + if (ret) + cts_warn("Create sysfs group failed %d", ret); + else + cd_data->sysfs_created = true; + +#endif /* CONFIG_CTS_SYSFS */ + + cts_data->charger_detect_data = cd_data; + cd_data->cts_data = cts_data; + + return 0; + +free_psy_name: + kfree(cd_data->psy_name); +free_cd_data: + kfree(cd_data); + + return ret; +} + +int cts_charger_detect_deinit(struct chipone_ts_data *cts_data) +{ + struct cts_charger_detect_data *cd_data; + int ret; + + cts_info("Deinit detect"); + + if (cts_data == NULL) { + cts_err("Deinit detect with cts_data = NULL"); + return -EFAULT; + } + + cd_data = cts_data->charger_detect_data; + if (cd_data == NULL) { + cts_warn("Deinit detect with charger_detect_data = NULL"); + return 0; + } + + if (cd_data->running) { + ret = stop_charger_detect(cd_data); + if (ret) + cts_err("Stop detect failed %d", ret); + } + +#ifdef CONFIG_CTS_SYSFS + if (cd_data->sysfs_created) { + cts_info("Remove sysfs group '%s'", + CHARGER_DET_SYSFS_GROUP_NAME); + sysfs_remove_group(&cts_data->device->kobj, + &charger_detect_attr_group); + cd_data->sysfs_created = false; + } +#endif /* CONFIG_CTS_SYSFS */ + + if (cd_data->psy_name) { + cts_info("Kfree power supply name"); + kfree(cd_data->psy_name); + cd_data->psy_name = NULL; + } + + kfree(cd_data); + cts_data->charger_detect_data = NULL; + + return 0; +} + +int cts_is_charger_attached(struct chipone_ts_data *cts_data, bool *attached) +{ + struct cts_charger_detect_data *cd_data; + int ret; + + if (cts_data == NULL) { + cts_err("Get state with cts_data = NULL"); + return -EFAULT; + } + + cd_data = cts_data->charger_detect_data; + if (cd_data == NULL) { + cts_err("Get state with charger_detect_data = NULL"); + return -ENODEV; + } + + cts_info("Get state using type %d(%s) param", + cd_data->type, charger_detect_type_str(cd_data->type)); + + ret = get_charger_state(cd_data); + if (ret) { + cts_err("Get state failed %d", ret); + return ret; + } + + *attached = cd_data->state; + + return 0; +} + +int cts_start_charger_detect(struct chipone_ts_data *cts_data) +{ + struct cts_charger_detect_data *cd_data; + + if (cts_data == NULL) { + cts_err("Start detect with cts_data = NULL"); + return -EFAULT; + } + + cd_data = cts_data->charger_detect_data; + if (cd_data == NULL) { + cts_err("Start detect with charger_detect_data = NULL"); + return -ENODEV; + } + + return start_charger_detect(cd_data); +} + +int cts_stop_charger_detect(struct chipone_ts_data *cts_data) +{ + struct cts_charger_detect_data *cd_data; + + if (cts_data == NULL) { + cts_err("Stop detect with cts_data = NULL"); + return -EFAULT; + } + + cd_data = cts_data->charger_detect_data; + if (cd_data == NULL) { + cts_err("Stop detect with charger_detect_data = NULL"); + return -ENODEV; + } + + return stop_charger_detect(cd_data); +} +#endif /* CONFIG_CTS_CHARGER_DETECT */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_charger_detect.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_charger_detect.h new file mode 100644 index 000000000000..4665561fffc4 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_charger_detect.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_CHARGER_DETECT_H +#define CTS_CHARGER_DETECT_H + +#include "cts_config.h" + +struct chipone_ts_data; + +#ifdef CONFIG_CTS_CHARGER_DETECT +extern int cts_charger_detect_init(struct chipone_ts_data *cts_data); +extern int cts_charger_detect_deinit(struct chipone_ts_data *cts_data); +extern int cts_start_charger_detect(struct chipone_ts_data *cts_data); +extern int cts_stop_charger_detect(struct chipone_ts_data *cts_data); +extern int cts_is_charger_attached(struct chipone_ts_data *cts_data, + bool *attached); +#else /* CONFIG_CTS_CHARGER_DETECT */ +static inline int cts_charger_detect_init(struct chipone_ts_data *cts_data) +{ + return -ENOTSUPP; +} + +static inline int cts_charger_detect_deinit(struct chipone_ts_data *cts_data) +{ + return -ENOTSUPP; +} + +static inline int cts_start_charger_detect(struct chipone_ts_data *cts_data) +{ + return -ENODEV; +} + +static inline int cts_stop_charger_detect(struct chipone_ts_data *cts_data) +{ + return -ENODEV; +} + +static inline int cts_is_charger_attached(struct chipone_ts_data *cts_data, + bool *attached) +{ + return -ENODEV; +} +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#endif /* CTS_CHARGER_DETECT_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_config.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_config.h new file mode 100644 index 000000000000..d1c5898887b8 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_config.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_CONFIG_H +#define CTS_CONFIG_H + +/** Driver version */ +#define CFG_CTS_DRIVER_MAJOR_VERSION 3 +#define CFG_CTS_DRIVER_MINOR_VERSION 0 +#define CFG_CTS_DRIVER_PATCH_VERSION 9 + +#define CFG_CTS_DRIVER_VERSION "v3.0.9" + +/** Whether reset pin is used */ +#define CFG_CTS_HAS_RESET_PIN + +/* #define CONFIG_CTS_I2C_HOST */ +#ifndef CONFIG_CTS_I2C_HOST + +#ifndef CFG_CTS_HAS_RESET_PIN +#define CFG_CTS_HAS_RESET_PIN +#endif + +#define CFG_CTS_SPI_SPEED_KHZ 6000 + +#endif + +//#define CFG_CTS_FORCE_UP +#define CFG_DUMP_INT_DATA +#define CONFIG_GENERIC_HARDIRQS +//#define CFG_CTS_FW_LOG_REDIRECT + +/** Whether force download firmware to chip */ +/* #define CFG_CTS_FIRMWARE_FORCE_UPDATE */ + +/** Use build in firmware or firmware file in fs*/ +/* #define CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ +#define CFG_CTS_FIRMWARE_IN_FS +#ifdef CFG_CTS_FIRMWARE_IN_FS +/* Add FW custom process. */ +/* #define CFG_CTS_FW_UPDATE_SYS */ + +/* Load config fw bin as default. */ +/* #define CFG_CTS_FW_UPDATE_FILE_LOAD */ +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD +#define CFG_CTS_FW_FILE_NAME_MAX_LEN 128 +#define CFG_CTS_FW_FILE_PATH "/vendor/firmware/" +#define CFG_CTS_FW_FILE_NAME_VENDOR "chipone" +#endif /*CFG_CTS_FW_UPDATE_FILE_LOAD */ + +#define CFG_CTS_FIRMWARE_FILENAME_9911 "ICNL9911.bin" +#define CFG_CTS_FIRMWARE_FILENAME_9916 "ICNL9916.bin" +#define CFG_CTS_FIRMWARE_FILENAME_9922 "ICNL9922.bin" + +#define CFG_CTS_FIRMWARE_FILEPATH_9911 "/vendor/firmware/ICNL9911.bin" +#define CFG_CTS_FIRMWARE_FILEPATH_9916 "/vendor/firmware/ICNL9916.bin" +#define CFG_CTS_FIRMWARE_FILEPATH_9922 "/vendor/firmware/ICNL9922.bin" +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + +/* IC type support */ +#define CFG_CTS_CHIP_NAME "ICNL9911S" + +#ifdef CONFIG_PROC_FS + /* Proc FS for backward compatibility for APK tool com.ICN85xx */ +#define CONFIG_CTS_LEGACY_TOOL +#endif /* CONFIG_PROC_FS */ + +#ifdef CONFIG_SYSFS + /* Sys FS for gesture report, debug feature etc. */ +#define CONFIG_CTS_SYSFS +#endif /* CONFIG_SYSFS */ + +#define CFG_CTS_MAX_TOUCH_NUM (10) // 5 + +/* Virtual key support */ +/* #define CONFIG_CTS_VIRTUALKEY */ +#ifdef CONFIG_CTS_VIRTUALKEY +#define CFG_CTS_MAX_VKEY_NUM (4) +#define CFG_CTS_NUM_VKEY (3) +#define CFG_CTS_VKEY_KEYCODES {KEY_BACK, KEY_HOME, KEY_MENU} +#endif /* CONFIG_CTS_VIRTUALKEY */ + +/* Gesture wakeup */ +#define CFG_CTS_GESTURE +#ifdef CFG_CTS_GESTURE +#define GESTURE_UP 0x11 +#define GESTURE_C 0x12 +#define GESTURE_O 0x13 +#define GESTURE_M 0x14 +#define GESTURE_W 0x15 +#define GESTURE_E 0x16 +#define GESTURE_S 0x17 +#define GESTURE_Z 0x1d +#define GESTURE_V 0x1e +#define GESTURE_D_TAP 0x50 +#define GESTURE_DOWN 0x22 +#define GESTURE_LEFT 0x23 +#define GESTURE_RIGHT 0x24 +#define GESTURE_TAP 0x7f + +#define CFG_CTS_NUM_GESTURE (14u) +#define CFG_CTS_GESTURE_REPORT_KEY +#define CFG_CTS_GESTURE_KEYMAP \ +{ \ + { GESTURE_C, KEY_C,}, \ + { GESTURE_W, KEY_W,}, \ + { GESTURE_V, KEY_V,}, \ + { GESTURE_D_TAP, KEY_F1,}, \ + { GESTURE_Z, KEY_Z,}, \ + { GESTURE_M, KEY_M,}, \ + { GESTURE_O, KEY_O,}, \ + { GESTURE_E, KEY_E,}, \ + { GESTURE_S, KEY_S,}, \ + { GESTURE_UP, KEY_UP,}, \ + { GESTURE_DOWN, KEY_DOWN,}, \ + { GESTURE_LEFT, KEY_LEFT,}, \ + { GESTURE_RIGHT, KEY_RIGHT,}, \ + { GESTURE_TAP, KEY_F1,}, \ +} +#define CFG_CTS_GESTURE_REPORT_TRACE 0 +#endif /* CFG_CTS_GESTURE */ + +/* #define CONFIG_CTS_GLOVE */ + +#define CONFIG_CTS_CHARGER_DETECT + +/* ESD protection */ +/* #define CONFIG_CTS_ESD_PROTECTION */ +#ifdef CONFIG_CTS_ESD_PROTECTION +#define CFG_CTS_ESD_PROTECTION_CHECK_PERIOD (2 * HZ) +#define CFG_CTS_ESD_FAILED_CONFIRM_CNT 3 +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +/* Use slot protocol (protocol B), comment it if use protocol A. */ +#define CONFIG_CTS_SLOTPROTOCOL + +#ifdef CONFIG_CTS_LEGACY_TOOL +#define CFG_CTS_TOOL_PROC_FILENAME "cts_tool" +#endif /* CONFIG_CTS_LEGACY_TOOL */ + +#define CFG_CTS_UPDATE_CRCCHECK +/**************************************************************************** + * Platform configurations + ****************************************************************************/ + +#include "cts_plat_qcom_config.h" + +#endif /* CTS_CONFIG_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_core.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_core.c new file mode 100644 index 000000000000..ee4926bc3526 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_core.c @@ -0,0 +1,3368 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "Core" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sfctrl.h" +#include "cts_spi_flash.h" +#include "cts_sysfs.h" +#include "cts_firmware.h" +#include "cts_charger_detect.h" +#include "cts_tcs.h" + +static DEFINE_RT_MUTEX(dev_lock); + +#ifdef CONFIG_CTS_I2C_HOST +static int cts_i2c_writeb(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay) +{ + u8 buff[8]; + + cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x val: 0x%02x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, b); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, buff); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, buff); + else { + cts_err("Writeb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + buff[cts_dev->rtdata.addr_width] = b; + + return cts_plat_i2c_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 1, retry, + delay); +} + +static int cts_i2c_writew(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay) +{ + u8 buff[8]; + + cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x val: 0x%04x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, w); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, buff); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, buff); + else { + cts_err("Writew invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + put_unaligned_le16(w, buff + cts_dev->rtdata.addr_width); + + return cts_plat_i2c_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 2, retry, + delay); +} + +static int cts_i2c_writel(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay) +{ + u8 buff[8]; + + cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x val: 0x%08x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, l); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, buff); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, buff); + else { + cts_err("Writel invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + put_unaligned_le32(l, buff + cts_dev->rtdata.addr_width); + + return cts_plat_i2c_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 4, retry, + delay); +} + +static int cts_i2c_writesb(const struct cts_device *cts_dev, u32 addr, + const u8 *src, size_t len, int retry, int delay) +{ + int ret; + u8 *data; + size_t max_xfer_size; + size_t payload_len; + size_t xfer_len; + + cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x len: %zu", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, len); + + max_xfer_size = cts_plat_get_max_i2c_xfer_size(cts_dev->pdata); + data = cts_plat_get_i2c_xfer_buf(cts_dev->pdata, len); + while (len) { + payload_len = + min((size_t)(max_xfer_size - cts_dev->rtdata.addr_width), + len); + xfer_len = payload_len + cts_dev->rtdata.addr_width; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, data); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, data); + else { + cts_err("Writesb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + memcpy(data + cts_dev->rtdata.addr_width, src, payload_len); + + ret = cts_plat_i2c_write(cts_dev->pdata, + cts_dev->rtdata.slave_addr, data, + xfer_len, retry, delay); + if (ret) { + cts_err("Platform i2c write failed %d", ret); + return ret; + } + + src += payload_len; + len -= payload_len; + addr += payload_len; + } + + return 0; +} + +static int cts_i2c_readb(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay) +{ + u8 addr_buf[4]; + + cts_dbg("Readb from slave_addr: 0x%02x reg: 0x%0*x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + return cts_plat_i2c_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, b, 1, + retry, delay); +} + +static int cts_i2c_readw(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + u8 buff[2]; + + cts_dbg("Readw from slave_addr: 0x%02x reg: 0x%0*x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readw invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_i2c_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, buff, 2, + retry, delay); + if (ret == 0) + *w = get_unaligned_le16(buff); + + return ret; +} + +static int cts_i2c_readl(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + u8 buff[4]; + + cts_dbg("Readl from slave_addr: 0x%02x reg: 0x%0*x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readl invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_i2c_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, buff, 4, + retry, delay); + if (ret == 0) + *l = get_unaligned_le32(buff); + + return ret; +} + +static int cts_i2c_readsb(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + size_t max_xfer_size, xfer_len; + + cts_dbg("Readsb from slave_addr: 0x%02x reg: 0x%0*x len: %zu", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, len); + + max_xfer_size = cts_plat_get_max_i2c_xfer_size(cts_dev->pdata); + while (len) { + xfer_len = min(max_xfer_size, len); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readsb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_i2c_read(cts_dev->pdata, + cts_dev->rtdata.slave_addr, addr_buf, + cts_dev->rtdata.addr_width, dst, + xfer_len, retry, delay); + if (ret) { + cts_err("Platform i2c read failed %d", ret); + return ret; + } + + dst += xfer_len; + len -= xfer_len; + addr += xfer_len; + } + + return 0; +} +#else +static int cts_spi_writeb(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay) +{ + u8 buff[8]; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, buff); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, buff); + else { + cts_err("Writeb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + buff[cts_dev->rtdata.addr_width] = b; + + return cts_plat_spi_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 1, retry, + delay); + return 0; +} + +static int cts_spi_writew(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay) +{ + u8 buff[8]; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, buff); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, buff); + else { + cts_err("Writew invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + put_unaligned_le16(w, buff + cts_dev->rtdata.addr_width); + + return cts_plat_spi_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 2, retry, + delay); + return 0; +} + +static int cts_spi_writel(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay) +{ + u8 buff[8]; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, buff); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, buff); + else { + cts_err("Writel invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + put_unaligned_le32(l, buff + cts_dev->rtdata.addr_width); + + return cts_plat_spi_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 4, retry, + delay); + return 0; +} + +static int cts_spi_writesb(const struct cts_device *cts_dev, u32 addr, + const u8 *src, size_t len, int retry, int delay) +{ + int ret; + u8 *data; + size_t max_xfer_size; + size_t payload_len; + size_t xfer_len; + + max_xfer_size = cts_plat_get_max_spi_xfer_size(cts_dev->pdata); + data = cts_plat_get_spi_xfer_buf(cts_dev->pdata, len); + while (len) { + payload_len = + min((size_t)(max_xfer_size - cts_dev->rtdata.addr_width), + len); + xfer_len = payload_len + cts_dev->rtdata.addr_width; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, data); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, data); + else { + cts_err("Writesb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + memcpy(data + cts_dev->rtdata.addr_width, src, payload_len); + + ret = + cts_plat_spi_write(cts_dev->pdata, + cts_dev->rtdata.slave_addr, data, + xfer_len, retry, delay); + if (ret) { + cts_err("Platform i2c write failed %d", ret); + return ret; + } + + src += payload_len; + len -= payload_len; + addr += payload_len; + } + return 0; +} + +static int cts_spi_readb(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay) +{ + u8 addr_buf[4]; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + return cts_plat_spi_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, b, 1, + retry, delay); +} + +static int cts_spi_readw(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + u8 buff[2]; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readw invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_spi_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, buff, 2, + retry, delay); + if (ret == 0) + *w = get_unaligned_le16(buff); + + return ret; +} + +static int cts_spi_readl(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + u8 buff[4]; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readl invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_spi_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, buff, 4, + retry, delay); + if (ret == 0) + *l = get_unaligned_le32(buff); + + return ret; +} + +static int cts_spi_readsb(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + size_t max_xfer_size, xfer_len; + + max_xfer_size = cts_plat_get_max_spi_xfer_size(cts_dev->pdata); + while (len) { + xfer_len = min(max_xfer_size, len); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readsb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_spi_read(cts_dev->pdata, + cts_dev->rtdata.slave_addr, addr_buf, + cts_dev->rtdata.addr_width, dst, + xfer_len, retry, delay); + if (ret) { + cts_err("Platform i2c read failed %d", ret); + return ret; + } + + dst += xfer_len; + len -= xfer_len; + addr += xfer_len; + } + return 0; +} + +static int cts_spi_readsb_delay_idle(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay, int idle) +{ + int ret; + u8 addr_buf[4]; + size_t max_xfer_size, xfer_len; + + max_xfer_size = cts_plat_get_max_spi_xfer_size(cts_dev->pdata); + while (len) { + xfer_len = min(max_xfer_size, len); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readsb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_spi_read_delay_idle(cts_dev->pdata, + cts_dev->rtdata.slave_addr, + addr_buf, + cts_dev->rtdata.addr_width, + dst, xfer_len, retry, delay, + idle); + if (ret) { + cts_err("Platform i2c read failed %d", ret); + return ret; + } + + dst += xfer_len; + len -= xfer_len; + addr += xfer_len; + } + return 0; +} + +#endif /* CONFIG_CTS_I2C_HOST */ + +int cts_dev_writeb(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_writeb(cts_dev, addr, b, retry, delay); +#else + return cts_spi_writeb(cts_dev, addr, b, retry, delay); +#endif +} + +static inline int cts_dev_writew(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_writew(cts_dev, addr, w, retry, delay); +#else + return cts_spi_writew(cts_dev, addr, w, retry, delay); +#endif +} + +static inline int cts_dev_writel(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_writel(cts_dev, addr, l, retry, delay); +#else + return cts_spi_writel(cts_dev, addr, l, retry, delay); +#endif +} + +static inline int cts_dev_writesb(const struct cts_device *cts_dev, u32 addr, + const u8 *src, size_t len, int retry, + int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_writesb(cts_dev, addr, src, len, retry, delay); +#else + return cts_spi_writesb(cts_dev, addr, src, len, retry, delay); +#endif +} + +int cts_dev_readb(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readb(cts_dev, addr, b, retry, delay); +#else + return cts_spi_readb(cts_dev, addr, b, retry, delay); +#endif +} + +static inline int cts_dev_readw(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readw(cts_dev, addr, w, retry, delay); +#else + return cts_spi_readw(cts_dev, addr, w, retry, delay); +#endif +} + +static inline int cts_dev_readl(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readl(cts_dev, addr, l, retry, delay); +#else + return cts_spi_readl(cts_dev, addr, l, retry, delay); +#endif +} + +static inline int cts_dev_readsb(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readsb(cts_dev, addr, dst, len, retry, delay); +#else + return cts_spi_readsb(cts_dev, addr, dst, len, retry, delay); +#endif +} + +static inline int cts_dev_readsb_delay_idle(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, + int retry, int delay, int idle) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readsb(cts_dev, addr, dst, len, retry, delay); +#else + return cts_spi_readsb_delay_idle(cts_dev, addr, dst, len, retry, delay, + idle); +#endif +} + +#ifdef CFG_CTS_UPDATE_CRCCHECK +int cts_sram_writesb_boot_crc_retry(const struct cts_device *cts_dev, + size_t len, u32 crc, int retry) +{ + int ret = 0, retries; + u32 addr[3]; + + if (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911S + || cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911C) { + addr[0] = 0x015ff0; + addr[1] = 0x08fffc; + addr[2] = 0x08fff8; + } + + if (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9916 + || cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9922) { + addr[0] = 0x01fff0; + addr[1] = 0x01fff8; + addr[2] = 0x01fffc; + } + + retries = 0; + do { + ret = cts_dev_writel(cts_dev, addr[0], 0xCC33555A, 3, 1); + if (ret != 0) { + cts_err("SRAM writesb failed %d", ret); + continue; + } + + ret = cts_dev_writel(cts_dev, addr[1], crc, 3, 1); + if (ret != 0) { + cts_err("SRAM writesb failed %d", ret); + continue; + } + + ret = cts_dev_writel(cts_dev, addr[2], len, 3, 1); + if (ret != 0) { + cts_err("SRAM writesb failed %d", ret); + continue; + } + + break; + } while (retries++ < retry); + + return ret; +} +#endif + +static int cts_write_sram_normal_mode(const struct cts_device *cts_dev, + u32 addr, const void *src, size_t len, + int retry, int delay) +{ + int i, ret; + u8 buff[5]; + + for (i = 0; i < len; i++) { + put_unaligned_le32(addr, buff); + buff[4] = *(u8 *) src; + + addr++; + src++; + + ret = cts_dev_writesb(cts_dev, CTS_DEVICE_FW_REG_DEBUG_INTF, + buff, 5, retry, delay); + if (ret) { + cts_err("Write rDEBUG_INTF len=5B failed %d", ret); + return ret; + } + } + + return 0; +} + +int cts_sram_writeb_retry(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) + return cts_dev_writeb(cts_dev, addr, b, retry, delay); + else + return cts_write_sram_normal_mode(cts_dev, addr, &b, 1, retry, + delay); +} + +int cts_sram_writew_retry(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay) +{ + u8 buff[2]; + + if (cts_dev->rtdata.program_mode) + return cts_dev_writew(cts_dev, addr, w, retry, delay); + else { + put_unaligned_le16(w, buff); + + return cts_write_sram_normal_mode(cts_dev, addr, buff, 2, retry, + delay); + } +} + +int cts_sram_writel_retry(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay) +{ + u8 buff[4]; + + if (cts_dev->rtdata.program_mode) + return cts_dev_writel(cts_dev, addr, l, retry, delay); + else { + put_unaligned_le32(l, buff); + + return cts_write_sram_normal_mode(cts_dev, addr, buff, 4, retry, + delay); + } +} + +int cts_sram_writesb_retry(const struct cts_device *cts_dev, + u32 addr, const void *src, size_t len, int retry, + int delay) +{ + if (cts_dev->rtdata.program_mode) + return cts_dev_writesb(cts_dev, addr, src, len, retry, delay); + else + return cts_write_sram_normal_mode(cts_dev, addr, src, len, + retry, delay); +} + +static int cts_calc_sram_crc(const struct cts_device *cts_dev, + u32 sram_addr, size_t size, u32 *crc) +{ + cts_info("Calc crc from sram 0x%06x size %zu", sram_addr, size); + + return cts_dev->hwdata->sfctrl->ops->calc_sram_crc(cts_dev, + sram_addr, size, + crc); +} + +int cts_sram_writesb_check_crc_retry(const struct cts_device *cts_dev, + u32 addr, const void *src, size_t len, + u32 crc, int retry) +{ + int ret, retries; + u32 fw_crc; + u8 tdata[16]; + u8 magic_num[] = { 0x5A, 0x55, 0x33, 0xCC, 0x00, 0x00, 0x00, 0x00 }; + + retries = 0; + do { + u32 crc_sram; + + retries++; + + ret = cts_sram_writesb(cts_dev, 0, src, len); + if (ret != 0) { + cts_err("SRAM writesb failed %d", ret); + continue; + } + + fw_crc = cts_crc32(src, len); + cts_info("fw crc: 0x%04x", fw_crc); + memcpy(tdata, magic_num, 8); + memcpy(tdata + 8, &fw_crc, 4); + memcpy(tdata + 12, &len, 4); + + ret = + cts_hw_reg_writesb_retry(cts_dev, 0x01FFF0, tdata, + sizeof(tdata), 3, 1); + if (ret != 0) { + cts_err("cts_hw_reg_writesb_retry %d", ret); + continue; + } + + ret = cts_calc_sram_crc(cts_dev, 0, len, &crc_sram); + if (ret != 0) { + cts_err("Get CRC for sram writesb failed %d retries %d", + ret, retries); + continue; + } + + if (crc == crc_sram) + return 0; + + cts_err + ("Check CRC for sram writesb mismatch %x != %x retries %d", + crc, crc_sram, retries); + ret = -EFAULT; + } while (retries < retry); + + return ret; +} + +int cts_read_sram_normal_mode(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay) +{ + int i, ret; + + for (i = 0; i < len; i++) { + ret = cts_dev_writel(cts_dev, + CTS_DEVICE_FW_REG_DEBUG_INTF, addr, retry, + delay); + if (ret) { + cts_err("Write addr to rDEBUG_INTF failed %d", ret); + return ret; + } + + ret = cts_dev_readb(cts_dev, + CTS_DEVICE_FW_REG_DEBUG_INTF + 4, + (u8 *) dst, retry, delay); + if (ret) { + cts_err("Read value from rDEBUG_INTF + 4 failed %d", + ret); + return ret; + } + + addr++; + dst++; + } + + return 0; +} + +int cts_sram_readb_retry(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) + return cts_dev_readb(cts_dev, addr, b, retry, delay); + else + return cts_read_sram_normal_mode(cts_dev, addr, b, 1, retry, + delay); +} + +int cts_sram_readw_retry(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay) +{ + int ret; + u8 buff[2]; + + if (cts_dev->rtdata.program_mode) + return cts_dev_readw(cts_dev, addr, w, retry, delay); + else { + ret = + cts_read_sram_normal_mode(cts_dev, addr, buff, 2, retry, + delay); + if (ret) { + cts_err("SRAM readw in normal mode failed %d", ret); + return ret; + } + + *w = get_unaligned_le16(buff); + return 0; + } +} + +int cts_sram_readl_retry(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay) +{ + int ret; + u8 buff[4]; + + if (cts_dev->rtdata.program_mode) + return cts_dev_readl(cts_dev, addr, l, retry, delay); + else { + ret = + cts_read_sram_normal_mode(cts_dev, addr, buff, 4, retry, + delay); + if (ret) { + cts_err("SRAM readl in normal mode failed %d", ret); + return ret; + } + + *l = get_unaligned_le32(buff); + + return 0; + } +} + +int cts_sram_readsb_retry(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) + return cts_dev_readsb(cts_dev, addr, dst, len, retry, delay); + else + return cts_read_sram_normal_mode(cts_dev, addr, dst, len, retry, + delay); +} + +int cts_fw_reg_writeb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 b, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Writeb to fw reg 0x%04x under program mode", reg_addr); + return -ENODEV; + } + + return cts_dev_writeb(cts_dev, reg_addr, b, retry, delay); +} + +int cts_fw_reg_writew_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 w, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Writew to fw reg 0x%04x under program mode", reg_addr); + return -ENODEV; + } + + return cts_dev_writew(cts_dev, reg_addr, w, retry, delay); +} + +int cts_fw_reg_writel_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 l, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Writel to fw reg 0x%04x under program mode", reg_addr); + return -ENODEV; + } + + return cts_dev_writel(cts_dev, reg_addr, l, retry, delay); +} + +int cts_fw_reg_writesb_retry(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len, + int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Writesb to fw reg 0x%04x under program mode", + reg_addr); + return -ENODEV; + } + + return cts_dev_writesb(cts_dev, reg_addr, src, len, retry, delay); +} + +int cts_fw_reg_readb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readb from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readb(cts_dev, reg_addr, b, retry, delay); +} + +int cts_fw_reg_readw_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readw from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readw(cts_dev, reg_addr, w, retry, delay); +} + +int cts_fw_reg_readl_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readl from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readl(cts_dev, reg_addr, l, retry, delay); +} + +int cts_fw_reg_readsb_retry(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, int retry, + int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readsb from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readsb(cts_dev, reg_addr, dst, len, retry, delay); +} + +int cts_fw_reg_readsb_retry_delay_idle(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, + int retry, int delay, int idle) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readsb from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readsb_delay_idle(cts_dev, reg_addr, dst, len, retry, + delay, idle); +} + +int cts_hw_reg_writeb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 b, int retry, int delay) +{ + return cts_sram_writeb_retry(cts_dev, reg_addr, b, retry, delay); +} + +int cts_hw_reg_writew_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 w, int retry, int delay) +{ + return cts_sram_writew_retry(cts_dev, reg_addr, w, retry, delay); +} + +int cts_hw_reg_writel_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 l, int retry, int delay) +{ + return cts_sram_writel_retry(cts_dev, reg_addr, l, retry, delay); +} + +int cts_hw_reg_writesb_retry(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len, + int retry, int delay) +{ + return cts_sram_writesb_retry(cts_dev, reg_addr, src, len, retry, + delay); +} + +int cts_hw_reg_readb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b, int retry, int delay) +{ + return cts_sram_readb_retry(cts_dev, reg_addr, b, retry, delay); +} + +int cts_hw_reg_readw_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w, int retry, int delay) +{ + return cts_sram_readw_retry(cts_dev, reg_addr, w, retry, delay); +} + +int cts_hw_reg_readl_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l, int retry, int delay) +{ + return cts_sram_readl_retry(cts_dev, reg_addr, l, retry, delay); +} + +int cts_hw_reg_readsb_retry(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, int retry, + int delay) +{ + return cts_sram_readsb_retry(cts_dev, reg_addr, dst, len, retry, delay); +} + +#define CTS_DEV_HW_REG_DDI_REG_CTRL (0x3002Cu) + +static int icnl9911s_set_access_ddi_reg(struct cts_device *cts_dev, bool enable) +{ + int ret; + u8 access_flag; + + cts_info("ICNL9911S %s access ddi reg", enable ? "enable" : "disable"); + + ret = + cts_hw_reg_readb(cts_dev, CTS_DEV_HW_REG_DDI_REG_CTRL, + &access_flag); + if (ret) { + cts_err("Read HW_REG_DDI_REG_CTRL failed %d", ret); + return ret; + } + + access_flag = enable ? (access_flag | 0x01) : (access_flag & (~0x01)); + ret = + cts_hw_reg_writeb(cts_dev, CTS_DEV_HW_REG_DDI_REG_CTRL, + access_flag); + if (ret) { + cts_err("Write HW_REG_DDI_REG_CTRL %02x failed %d", access_flag, + ret); + return ret; + } + + ret = cts_hw_reg_writeb(cts_dev, 0x30074, enable ? 1 : 0); + if (ret) { + cts_err("Write 0x30074 failed %d", ret); + return ret; + } + + ret = cts_hw_reg_writew(cts_dev, 0x3DFF0, enable ? 0x595A : 0x5A5A); + if (ret) { + cts_err("Write password to F0 failed %d", ret); + return ret; + } + ret = cts_hw_reg_writew(cts_dev, 0x3DFF4, enable ? 0xA6A5 : 0x5A5A); + if (ret) { + cts_err("Write password to F1 failed %d", ret); + return ret; + } + + return 0; +} + +static int icnl9916_set_access_ddi_reg(struct cts_device *cts_dev, bool enable) +{ + int ret; + u8 access_flag; + + cts_info("ICNL9916 %s access ddi reg", enable ? "enable" : "disable"); + + ret = + cts_hw_reg_readb(cts_dev, CTS_DEV_HW_REG_DDI_REG_CTRL, + &access_flag); + if (ret) { + cts_err("Read HW_REG_DDI_REG_CTRL failed %d", ret); + return ret; + } + + access_flag = enable ? (access_flag | 0x01) : (access_flag & (~0x01)); + ret = + cts_hw_reg_writeb(cts_dev, CTS_DEV_HW_REG_DDI_REG_CTRL, + access_flag); + if (ret) { + cts_err("Write HW_REG_DDI_REG_CTRL %02x failed %d", access_flag, + ret); + return ret; + } + + ret = cts_hw_reg_writeb(cts_dev, 0x30074, enable ? 1 : 0); + if (ret) { + cts_err("Write 0x30074 failed %d", ret); + return ret; + } + + ret = cts_hw_reg_writew(cts_dev, 0x3DFF0, enable ? 0x595A : 0x5A5A); + if (ret) { + cts_err("Write password to F0 failed %d", ret); + return ret; + } + ret = cts_hw_reg_writew(cts_dev, 0x3DFF4, enable ? 0xA6A5 : 0x5A5A); + if (ret) { + cts_err("Write password to F1 failed %d", ret); + return ret; + } + + return 0; +} + +const static struct cts_sfctrl icnl9911_sfctrl = { + .reg_base = 0x34000, + .xchg_sram_base = (80 - 1) * 1024, + .xchg_sram_size = 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_sfctrl icnl9911s_sfctrl = { + .reg_base = 0x34000, + .xchg_sram_base = (64 - 1) * 1024, + .xchg_sram_size = 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_sfctrl icnl9911c_sfctrl = { + .reg_base = 0x34000, + .xchg_sram_base = (64 - 1) * 1024, + .xchg_sram_size = 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_sfctrl icnl9916_sfctrl = { + .reg_base = 0x34000, + .xchg_sram_base = 96 * 1024, + .xchg_sram_size = 32 * 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_sfctrl icnl9922_sfctrl = { + .reg_base = 0x34000, + .xchg_sram_base = 96 * 1024, + .xchg_sram_size = 32 * 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_device_hwdata cts_device_hwdatas[] = { + { + .name = "ICNL9911", + .hwid = CTS_DEV_HWID_ICNL9911, + .fwid = CTS_DEV_FWID_ICNL9911, + .num_row = 32, + .num_col = 18, + .sram_size = 80 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9911_sfctrl, + }, + { + .name = "ICNL9911S", + .hwid = CTS_DEV_HWID_ICNL9911S, + .fwid = CTS_DEV_FWID_ICNL9911S, + .num_row = 32, + .num_col = 18, + .sram_size = 64 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9911s_sfctrl, + }, + { + .name = "ICNL9911C", + .hwid = CTS_DEV_HWID_ICNL9911C, + .fwid = CTS_DEV_FWID_ICNL9911C, + .num_row = 32, + .num_col = 18, + .sram_size = 64 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9911c_sfctrl, + .enable_access_ddi_reg = icnl9911s_set_access_ddi_reg, + }, + { + .name = "ICNL9916", + .hwid = CTS_DEV_HWID_ICNL9916, + .fwid = CTS_DEV_FWID_ICNL9916, + .num_row = 32, + .num_col = 18, + .sram_size = 96 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9916_sfctrl, + .enable_access_ddi_reg = icnl9916_set_access_ddi_reg, + }, + { + .name = "ICNL9922", + .hwid = CTS_DEV_HWID_ICNL9922, + .fwid = CTS_DEV_FWID_ICNL9922, + .num_row = 36, + .num_col = 18, + .sram_size = 96 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9922_sfctrl, + .enable_access_ddi_reg = icnl9916_set_access_ddi_reg, + } +}; + +static int cts_init_device_hwdata(struct cts_device *cts_dev, + u32 hwid, u16 fwid) +{ + int i; + + cts_info("Init hardware data hwid: %06x fwid: %04x", hwid, fwid); + + for (i = 0; i < ARRAY_SIZE(cts_device_hwdatas); i++) { + if (hwid == cts_device_hwdatas[i].hwid || + fwid == cts_device_hwdatas[i].fwid) { + cts_dev->hwdata = &cts_device_hwdatas[i]; + return 0; + } + } + + return -EINVAL; +} + +void cts_lock_device(const struct cts_device *cts_dev) +{ + cts_dbg("*** Lock ***"); + + rt_mutex_lock(&dev_lock); +} + +void cts_unlock_device(const struct cts_device *cts_dev) +{ + cts_dbg("### Un-Lock ###"); + + rt_mutex_unlock(&dev_lock); +} + +int cts_set_work_mode(const struct cts_device *cts_dev, u8 mode) +{ + cts_info("Set work mode to %u", mode); + + return cts_fw_reg_writeb(cts_dev, CTS_DEVICE_FW_REG_WORK_MODE, mode); +} + +int cts_get_work_mode(const struct cts_device *cts_dev, u8 *mode) +{ + return cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_WORK_MODE, mode); +} + +int cts_get_firmware_version(const struct cts_device *cts_dev, u16 *version) +{ + int ret = cts_fw_reg_readw(cts_dev, CTS_DEVICE_FW_REG_VERSION, version); + + if (ret) + *version = 0; + else + *version = be16_to_cpup(version); + + return ret; +} + +int cts_get_ddi_version(const struct cts_device *cts_dev, u8 *version) +{ + int ret = + cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_DDI_VERSION, version); + + if (ret) + *version = 0; + + return ret; +} + +int cts_get_lib_version(const struct cts_device *cts_dev, u16 *lib_version) +{ + u8 main_version, sub_version; + int ret; + + ret = cts_fw_reg_readb(cts_dev, + CTS_DEVICE_FW_REG_FW_LIB_MAIN_VERSION, + &main_version); + if (ret) { + cts_err("Get fw lib main version failed %d", ret); + return ret; + } + + ret = cts_fw_reg_readb(cts_dev, + CTS_DEVICE_FW_REG_FW_LIB_SUB_VERSION, + &sub_version); + if (ret) { + cts_err("Get fw lib sub version failed %d", ret); + return ret; + } + + *lib_version = (main_version << 8) + sub_version; + return 0; +} + +int cts_get_data_ready_flag(const struct cts_device *cts_dev, u8 *flag) +{ + return cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_DATA_READY, flag); +} + +int cts_clr_data_ready_flag(const struct cts_device *cts_dev) +{ + return cts_fw_reg_writeb(cts_dev, CTS_DEVICE_FW_REG_DATA_READY, 0); +} + +int cts_send_command(const struct cts_device *cts_dev, u8 cmd) +{ + cts_info("Send command 0x%02x", cmd); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Send command %u while chip in program mode", cmd); + return -ENODEV; + } + + return cts_fw_reg_writeb_retry(cts_dev, CTS_DEVICE_FW_REG_CMD, cmd, 3, + 0); +} + +static int cts_get_touchinfo(struct cts_device *cts_dev, + struct cts_device_touch_info *touch_info) +{ + cts_dbg("Get touch info"); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Get touch info in program mode"); + return -ENODEV; + } + + return cts_dev->ops->get_touchinfo(cts_dev, touch_info); +} + +int cts_get_panel_param(const struct cts_device *cts_dev, + void *param, size_t size) +{ + cts_info("Get panel parameter"); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Get panel parameter in program mode"); + return -ENODEV; + } + + return cts_fw_reg_readsb(cts_dev, CTS_DEVICE_FW_REG_PANEL_PARAM, + param, size); +} + +int cts_set_panel_param(const struct cts_device *cts_dev, + const void *param, size_t size) +{ + cts_info("Set panel parameter"); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Set panel parameter in program mode"); + return -ENODEV; + } + return cts_fw_reg_writesb(cts_dev, + CTS_DEVICE_FW_REG_PANEL_PARAM, param, size); +} + +int cts_get_x_resolution(const struct cts_device *cts_dev, u16 *resolution) +{ + return cts_fw_reg_readw(cts_dev, CTS_DEVICE_FW_REG_X_RESOLUTION, + resolution); +} + +int cts_get_y_resolution(const struct cts_device *cts_dev, u16 *resolution) +{ + return cts_fw_reg_readw(cts_dev, CTS_DEVICE_FW_REG_Y_RESOLUTION, + resolution); +} + +int cts_get_num_rows(const struct cts_device *cts_dev, u8 *num_rows) +{ + return cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_NUM_TX, num_rows); +} + +int cts_get_num_cols(const struct cts_device *cts_dev, u8 *num_cols) +{ + return cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_NUM_RX, num_cols); +} + +#ifdef CONFIG_CTS_LEGACY_TOOL +int cts_enable_get_rawdata(const struct cts_device *cts_dev) +{ + cts_info("Enable get raw/diff data"); + return cts_send_command(cts_dev, CTS_CMD_ENABLE_READ_RAWDATA); +} + +int cts_disable_get_rawdata(const struct cts_device *cts_dev) +{ + cts_info("Disable get raw/diff data"); + return cts_send_command(cts_dev, CTS_CMD_DISABLE_READ_RAWDATA); +} + +static void tsdata_flip_x(void *tsdata, u8 fw_rows, u8 fw_cols) +{ + u8 r, c; + u16 *data; + + data = (u16 *) tsdata; + for (r = 0; r < fw_rows; r++) { + for (c = 0; c < fw_cols / 2; c++) { + swap(data[r * fw_cols + c], + data[r * fw_cols + wrap(fw_cols, c)]); + } + } +} + +static void tsdata_flip_y(void *tsdata, u8 fw_rows, u8 fw_cols) +{ + u8 r, c; + u16 *data; + + data = (u16 *) tsdata; + for (r = 0; r < fw_rows / 2; r++) { + for (c = 0; c < fw_cols; c++) { + swap(data[r * fw_cols + c], + data[wrap(fw_rows, r) * fw_cols + c]); + } + } +} + +int cts_polling_data(const struct cts_device *cts_dev, void *buf, size_t size) +{ + if (cts_dev->ops->polling_data) + return cts_dev->ops->polling_data(cts_dev, buf, size); + + return -EIO; +} + +int cts_polling_test_data(const struct cts_device *cts_dev, void *buf, + size_t size) +{ + if (cts_dev->ops->polling_test_data) + return cts_dev->ops->polling_test_data(cts_dev, buf, size); + + return -EIO; +} + +int cts_get_rawdata(const struct cts_device *cts_dev, void *buf) +{ + int i, ret; + u8 ready; + u8 retries = 5; + + cts_info("Get rawdata"); + + /** - Wait data ready flag set */ + for (i = 0; i < 100; i++) { + mdelay(10); + ret = cts_dev->ops->get_data_ready_flag(cts_dev, &ready); + if (ret) { + cts_err("Get data ready flag failed %d", ret); + continue; + } + if (ready) + goto read_rawdata; + } + + cts_err("Wait data ready timeout"); + return -ETIMEDOUT; + + /* Read rawdata */ +read_rawdata: + do { + ret = cts_dev->ops->get_rawdata(cts_dev, buf); + if (ret) + cts_err("Read rawdata failed %d", ret); + else + break; + } while (--retries > 0); + + for (i = 0; i < 5; i++) { + int r = cts_dev->ops->clr_data_ready_flag(cts_dev); + + if (r) + cts_err("Clear data ready flag failed %d", r); + else + break; + } + + if (cts_dev->fwdata.flip_x) + tsdata_flip_x(buf, cts_dev->fwdata.rows, cts_dev->fwdata.cols); + if (cts_dev->fwdata.flip_y) + tsdata_flip_y(buf, cts_dev->fwdata.rows, cts_dev->fwdata.cols); + + return ret; +} + +int cts_get_diffdata(const struct cts_device *cts_dev, void *buf) +{ + int i, j, ret; + u8 ready; + u8 retries = 5; + u8 *cache_buf; + + cts_info("Get diffdata"); + cache_buf = kzalloc((cts_dev->fwdata.rows + 2) * (cts_dev->fwdata.cols + + 2) * 2, GFP_KERNEL); + if (cache_buf == NULL) { + cts_err("Get diffdata: malloc error"); + ret = -ENOMEM; + goto get_diff_exit; + } + /** - Wait data ready flag set */ + for (i = 0; i < 1000; i++) { + mdelay(1); + ret = cts_dev->ops->get_data_ready_flag(cts_dev, &ready); + if (ret) { + cts_err("Get data ready flag failed %d", ret); + goto get_diff_free_buf; + } + if (ready) + break; + } + if (i == 1000) { + ret = -ETIMEDOUT; + goto get_diff_free_buf; + } + do { + ret = cts_dev->ops->get_diffdata(cts_dev, cache_buf); + if (ret) + cts_err("Read diffdata failed %d", ret); + else + break; + } while (--retries > 0); + + if (ret == 0) { + for (i = 0; i < cts_dev->fwdata.rows; i++) { + for (j = 0; j < cts_dev->fwdata.cols; j++) { + ((u8 *) buf)[2 * + (i * cts_dev->fwdata.cols + j)] = + cache_buf[2 * + ((i + 1) * (cts_dev->fwdata.cols + + 2) + j + 1)]; + ((u8 *) buf)[2 * + (i * cts_dev->fwdata.cols + j) + + 1] = + cache_buf[2 * + ((i + 1) * (cts_dev->fwdata.cols + + 2) + j + 1) + 1]; + } + } + } + + for (i = 0; i < 5; i++) { + int r = cts_dev->ops->clr_data_ready_flag(cts_dev); + + if (r) + cts_err("Clear data ready flag failed %d", r); + else + break; + } + + if (cts_dev->fwdata.flip_x) + tsdata_flip_x(buf, cts_dev->fwdata.rows, cts_dev->fwdata.cols); + if (cts_dev->fwdata.flip_y) + tsdata_flip_y(buf, cts_dev->fwdata.rows, cts_dev->fwdata.cols); +get_diff_free_buf: + kfree(cache_buf); +get_diff_exit: + return ret; +} + +int cts_get_basedata(const struct cts_device *cts_dev, void *buf) +{ + int i, j, ret; + u8 ready; + u8 retries = 5; + u8 *cache_buf; + + cts_info("Get basedata"); + cache_buf = kzalloc((cts_dev->fwdata.rows + 2) * (cts_dev->fwdata.cols + + 2) * 2, GFP_KERNEL); + if (cache_buf == NULL) { + cts_err("Get basedata: malloc error"); + ret = -ENOMEM; + goto get_diff_exit; + } + /** - Wait data ready flag set */ + for (i = 0; i < 1000; i++) { + mdelay(1); + ret = cts_dev->ops->get_data_ready_flag(cts_dev, &ready); + if (ret) { + cts_err("Get data ready flag failed %d", ret); + goto get_diff_free_buf; + } + if (ready) + break; + } + if (i == 1000) { + ret = -ETIMEDOUT; + goto get_diff_free_buf; + } + do { + ret = cts_dev->ops->get_basedata(cts_dev, cache_buf); + if (ret) + cts_err("Read basedata failed %d", ret); + else + break; + } while (--retries > 0); + if (ret == 0) { + for (i = 0; i < cts_dev->fwdata.rows; i++) { + for (j = 0; j < cts_dev->fwdata.cols; j++) { + ((u8 *) buf)[2 * + (i * cts_dev->fwdata.cols + j)] = + cache_buf[2 * + ((i + 1) * (cts_dev->fwdata.cols + + 2) + j + 1)]; + ((u8 *) buf)[2 * + (i * cts_dev->fwdata.cols + j) + + 1] = + cache_buf[2 * + ((i + 1) * (cts_dev->fwdata.cols + + 2) + j + 1) + 1]; + } + } + } + + for (i = 0; i < 5; i++) { + int r = cts_dev->ops->clr_data_ready_flag(cts_dev); + + if (r) + cts_err("Clear data ready flag failed %d", r); + else + break; + } + + if (cts_dev->fwdata.flip_x) + tsdata_flip_x(buf, cts_dev->fwdata.rows, cts_dev->fwdata.cols); + if (cts_dev->fwdata.flip_y) + tsdata_flip_y(buf, cts_dev->fwdata.rows, cts_dev->fwdata.cols); +get_diff_free_buf: + kfree(cache_buf); +get_diff_exit: + return ret; +} +#endif /* CONFIG_CTS_LEGACY_TOOL */ + +static int cts_get_dev_boot_mode(const struct cts_device *cts_dev, + u8 *boot_mode) +{ + int ret; + + if (cts_dev->rtdata.program_mode) + ret = cts_hw_reg_readb_retry(cts_dev, + CTS_DEV_HW_REG_CURRENT_MODE, + boot_mode, 5, 10); + else + ret = cts_dev->ops->read_hw_reg(cts_dev, + CTS_DEV_HW_REG_CURRENT_MODE, + boot_mode, 1); + + if (ret) { + cts_err("Read boot mode failed %d", ret); + return ret; + } + + *boot_mode &= CTS_DEV_BOOT_MODE_MASK; + + cts_info("Curr dev boot mode: %u(%s)", *boot_mode, + cts_dev_boot_mode2str(*boot_mode)); + return 0; +} + +static int cts_set_dev_boot_mode(const struct cts_device *cts_dev, u8 boot_mode) +{ + int ret; + + cts_info("Set dev boot mode to %u(%s)", boot_mode, + cts_dev_boot_mode2str(boot_mode)); + + ret = cts_hw_reg_writeb_retry(cts_dev, CTS_DEV_HW_REG_BOOT_MODE, + boot_mode, 5, 5); + if (ret) { + cts_err("Write hw register BOOT_MODE failed %d", ret); + return ret; + } + + cts_info("%s exit", __func__); + + return 0; +} + +static int cts_init_fwdata(struct cts_device *cts_dev) +{ + + struct cts_device_fwdata *fwdata = &cts_dev->fwdata; + int ret; + + cts_info("Init firmware data"); + + if (cts_dev->rtdata.program_mode) { + cts_err("Init firmware data while in program mode"); + return -EINVAL; + } + + memset(fwdata, 0, sizeof(*fwdata)); + + ret = cts_dev->ops->get_fw_ver(cts_dev, &fwdata->version); + if (ret < 0) { + cts_err("get_fw_ver failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_lib_ver(cts_dev, &fwdata->lib_version); + if (ret < 0) { + cts_err("get_lib_ver failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_ddi_ver(cts_dev, &fwdata->ddi_version); + if (ret < 0) { + cts_err("get_ddi_ver failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_res_x(cts_dev, &fwdata->res_x); + if (ret < 0) { + cts_err("get_res_x failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_res_y(cts_dev, &fwdata->res_y); + if (ret < 0) { + cts_err("get_res_y failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_rows(cts_dev, &fwdata->rows); + if (ret < 0) { + cts_err("get_rows failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_cols(cts_dev, &fwdata->cols); + if (ret < 0) { + cts_err("get_cols failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_flip_x(cts_dev, &fwdata->flip_x); + if (ret < 0) { + cts_err("get_flip_x failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_flip_y(cts_dev, &fwdata->flip_y); + if (ret < 0) { + cts_err("get_flip_y failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_swap_axes(cts_dev, &fwdata->swap_axes); + if (ret < 0) { + cts_err("get_swap_axes failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_int_mode(cts_dev, &fwdata->int_mode); + if (ret < 0) { + cts_err("get_int_mode failed"); + return -EINVAL; + } + ret = cts_dev->ops->get_int_keep_time(cts_dev, &fwdata->int_keep_time); + if (ret < 0) { + cts_err("get_int_keep_time failed"); + return -EINVAL; + } + fwdata->rawdata_target = 2000; + + ret = cts_dev->ops->get_esd_method(cts_dev, &fwdata->esd_method); + if (ret < 0) { + cts_err("get_esd_method failed"); + return -EINVAL; + } + + if (cts_dev->ops->get_has_int_data) { + ret = + cts_dev->ops->get_has_int_data(cts_dev, + &fwdata->has_int_data); + if (ret < 0) { + cts_err("get_has_int_data failed: %d", ret); + return -EINVAL; + } + + if (fwdata->has_int_data) { + if (cts_dev->ops->get_int_data_method) { + ret = + cts_dev->ops->get_int_data_method(cts_dev, + &fwdata->int_data_method); + if (ret < 0) { + cts_err + ("get_int_data_method failed: %d", + ret); + return -EINVAL; + } + if (fwdata->int_data_method >= + INT_DATA_METHOD_CNT) { + cts_err("Invalid int data method: %d", + fwdata->int_data_method); + return -EINVAL; + } + + } + + if (cts_dev->ops->get_int_data_types) { + ret = + cts_dev->ops->get_int_data_types(cts_dev, + &fwdata->int_data_types); + if (ret < 0) { + cts_err("get_int_data_types failed: %d", + ret); + return -EINVAL; + } + fwdata->int_data_types &= INT_DATA_TYPE_MASK; + } + + if (cts_dev->ops->calc_int_data_size) { + ret = cts_dev->ops->calc_int_data_size(cts_dev); + if (ret < 0) { + cts_err("calc_int_data_size failed: %d", + ret); + return -EINVAL; + } + } + + if (cts_dev->ops->init_int_data) { + ret = cts_dev->ops->init_int_data(cts_dev); + if (ret < 0) { + cts_err("init_int_data failed: %d", + ret); + return -EINVAL; + } + } + } + } + + cts_err("fwver: %04x", fwdata->version); + cts_err("libver: %04x", fwdata->lib_version); + cts_err("ddi_version: %02x", fwdata->ddi_version); + cts_err("res_x: %d", fwdata->res_x); + cts_err("res_y: %d", fwdata->res_y); + cts_err("rows: %d", fwdata->rows); + cts_err("cols: %d", fwdata->cols); + cts_err("flip_x: %d", fwdata->flip_x); + cts_err("flip_y: %d", fwdata->flip_y); + cts_err("swap_axes: %d", fwdata->swap_axes); + cts_err("int_mode: %d", fwdata->int_mode); + cts_err("int_keep_time: %d", fwdata->int_keep_time); + cts_err("rawdata_target: %d", fwdata->rawdata_target); + cts_err("esd_method: %d", fwdata->esd_method); + + if (cts_dev->ops->get_has_int_data) { + cts_err("has_int_data: %d", fwdata->has_int_data); + cts_err("int_data_method: %d", fwdata->int_data_method); + cts_err("int_data_types: %d", fwdata->int_data_types); + cts_err("int_data_size: %ld", fwdata->int_data_size); + } + + return 0; +} + +#ifdef CFG_CTS_FW_LOG_REDIRECT +void cts_show_fw_log(struct cts_device *cts_dev) +{ + u8 len, max_len; + int ret; + u8 *data; + + max_len = cts_plat_get_max_fw_log_size(cts_dev->pdata); + data = cts_plat_get_fw_log_buf(cts_dev->pdata, max_len); + ret = + cts_fw_reg_readsb(cts_dev, CTS_DEVICE_FW_REG_TOUCH_INFO + 1, &len, + 1); + if (ret) { + cts_err("Get i2c print buf len error"); + return; + } + if (len >= max_len) + len = max_len - 1; + + ret = + cts_fw_reg_readsb(cts_dev, CTS_DEVICE_FW_REG_TOUCH_INFO + 2, data, + len); + if (ret) { + cts_err("Get i2c print buf error"); + return; + } + data[len] = '\0'; + printk("CTS-FW_LOG %s", data); + cts_fw_log_show_finish(cts_dev); +} +#endif + +extern int cts_tcs_clr_data_ready_flag(const struct cts_device *cts_dev); +extern int cts_tcs_clr_gstr_ready_flag(const struct cts_device *cts_dev); +int cts_irq_handler(struct cts_device *cts_dev) +{ + struct cts_device_touch_info *touch_info; + u8 pwrmode = 3; + int ret; + + cts_dbg("Enter IRQ handler"); + + if (cts_dev->rtdata.program_mode) { + cts_err("IRQ triggered in program mode"); + return -EINVAL; + } + + touch_info = &cts_dev->rtdata.touch_info; + ret = cts_get_touchinfo(cts_dev, touch_info); + if (ret) { + cts_err("Get touch info failed %d", ret); + return ret; + } + if (unlikely(cts_dev->rtdata.suspended)) { +#ifdef CFG_CTS_GESTURE + if (cts_dev->rtdata.gesture_wakeup_enabled) { + struct cts_device_gesture_info *gesture_info; + + gesture_info = &cts_dev->rtdata.gesture_info; + memcpy(gesture_info, touch_info, sizeof(struct cts_device_gesture_info)); + + ret = cts_plat_process_gesture_info(cts_dev->pdata, gesture_info); + + if (cts_dev->fwdata.int_data_method != INT_DATA_METHOD_HOST) { + if (ret) + cts_err("Process gesture info failed %d", ret); + /* return ret; */ + if (cts_tcs_clr_gstr_ready_flag(cts_dev)) + cts_err("Clear gesture ready flag failed"); + +/** - Issure another suspend with gesture wakeup command to device + * after get gesture info. + */ + ret = cts_dev->ops->set_pwr_mode(cts_dev, pwrmode); + if (ret) + cts_warn("Reenter suspend with gesture wakeup failed %d", ret); + } else { + if (cts_tcs_clr_data_ready_flag(cts_dev)) + cts_err("Clear data ready flag failed"); + return ret; + } + } else { + cts_warn("IRQ triggered while device suspended " + "without gesture wakeup enable"); + } +#endif /* CFG_CTS_GESTURE */ + } else { + cts_dbg("Touch info: vkey_state %x, num_msg %u", + touch_info->vkey_state, touch_info->num_msg); + + if (cts_dev->fwdata.int_data_method != INT_DATA_METHOD_NONE + && cts_dev->fwdata.int_data_method != INT_DATA_METHOD_DEBUG) { + if (cts_tcs_clr_data_ready_flag(cts_dev)) { + cts_err("Clear data ready flag failed"); + } + } +#ifdef CFG_CTS_PALM_DETECT + if (CFG_CTS_PALM_ID == touch_info->vkey_state) { + cts_report_palm_event(cts_dev->pdata); + cts_plat_release_all_touch(cts_dev->pdata); + return 0; + } +#endif + + ret = cts_plat_process_touch_msg(cts_dev->pdata, + touch_info->msgs, + touch_info->num_msg); + if (ret) { + cts_err("Process touch msg failed %d", ret); + return ret; + } +#ifdef CONFIG_CTS_VIRTUALKEY + ret = cts_plat_process_vkey(cts_dev->pdata, + touch_info->vkey_state); + if (ret) { + cts_err("Process vkey failed %d", ret); + return ret; + } +#endif /* CONFIG_CTS_VIRTUALKEY */ + } + + return 0; +} + +bool cts_is_device_suspended(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.suspended; +} + +int cts_suspend_device(struct cts_device *cts_dev) +{ + int ret; + u8 buf; + + cts_info("Suspend device"); + + if (cts_dev->rtdata.program_mode) { + cts_info("Quit programming mode before suspend"); + ret = cts_enter_normal_mode(cts_dev); + if (ret) { + cts_err("Failed to exit program mode before suspend:%d", + ret); + return ret; + } + } + + cts_info("Set suspend mode:%s", + cts_dev->rtdata.gesture_wakeup_enabled ? "gesture" : "sleep"); + + buf = cts_dev->rtdata.gesture_wakeup_enabled ? 3 : 2; + ret = cts_dev->ops->set_pwr_mode(cts_dev, buf); + if (ret) { + cts_err("Suspend device failed %d", ret); + + return ret; + } + +#ifdef CFG_CTS_HAS_RESET_PIN + /* cts_plat_set_reset(cts_dev->pdata, 0); */ +#endif + + cts_info("Device suspended ..."); + cts_dev->rtdata.suspended = true; + + return 0; +} + +int cts_resume_device(struct cts_device *cts_dev) +{ + int ret = 0; + int retries = 3; + u8 tdata[4] = { 'R', 'S', 'T', '!' }; + + cts_info("Resume device"); + + /* Check whether device is in normal mode */ + while (--retries >= 0) { +#ifdef CFG_CTS_HAS_RESET_PIN + ret = cts_hw_reg_writesb_retry(cts_dev, 0x300F8, tdata, + sizeof(tdata), 3, 1); + if (ret != 0) + cts_err("0x300F8 write 0 failed"); + cts_reset_device(cts_dev); +#endif + cts_set_normal_addr(cts_dev); +#ifdef CONFIG_CTS_I2C_HOST + if (cts_plat_is_i2c_online + (cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR)) +#else + if (cts_plat_is_normal_mode(cts_dev->pdata)) +#endif + { + break; + } + } + + if (retries < 0) { + const struct cts_firmware *firmware = NULL; + + cts_info("Need update firmware when resume"); + +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD + if (cts_dev->config_fw_name[0] != '\0') { + firmware = cts_request_firmware_from_fs(cts_dev, + cts_dev->config_fw_name); + } +#else + firmware = cts_request_firmware(cts_dev, + cts_dev->hwdata->hwid, + cts_dev->hwdata->fwid, 0); +#endif + if (firmware) { + ret = cts_update_firmware(cts_dev, firmware, false); + cts_release_firmware(firmware); + + if (ret) { + cts_err("Update default firmware failed %d", + ret); + goto err_set_program_mode; + } + } else { + cts_err("Request default firmware failed %d, " + "please update manually!!", ret); + + goto err_set_program_mode; + } + } +#ifdef CONFIG_CTS_CHARGER_DETECT + if (cts_is_charger_exist(cts_dev)) { + int r = cts_set_dev_charger_attached(cts_dev, true); + + if (r) + cts_err("Set dev charger attached failed %d", r); + } +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) + cts_enter_glove_mode(cts_dev); +#endif + +#ifdef CFG_CTS_FW_LOG_REDIRECT + if (cts_is_fw_log_redirect(cts_dev)) + cts_enable_fw_log_redirect(cts_dev); +#endif + + cts_dev->rtdata.suspended = false; + return 0; + +err_set_program_mode: + cts_dev->rtdata.program_mode = true; + cts_dev->rtdata.slave_addr = CTS_DEV_PROGRAM_MODE_I2CADDR; + cts_dev->rtdata.addr_width = CTS_DEV_PROGRAM_MODE_ADDR_WIDTH; + + return ret; +} + +bool cts_is_device_program_mode(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.program_mode; +} + +static inline void cts_init_rtdata_with_normal_mode(struct cts_device *cts_dev) +{ + memset(&cts_dev->rtdata, 0, sizeof(cts_dev->rtdata)); + + cts_set_normal_addr(cts_dev); + cts_dev->rtdata.suspended = false; + cts_dev->rtdata.updating = false; + cts_dev->rtdata.testing = false; + cts_dev->rtdata.fw_log_redirect_enabled = false; + cts_dev->rtdata.glove_mode_enabled = false; + cts_dev->rtdata.int_data = NULL; +} + +int cts_enter_program_mode(struct cts_device *cts_dev) +{ + const static u8 magic_num[] = { 0xCC, 0x33, 0x55, 0x5A }; + u8 boot_mode; + int ret; + + cts_info("Enter program mode"); + + if (cts_dev->rtdata.program_mode) + cts_warn("Enter program mode while alredy in"); + /* return 0; */ +#ifdef CONFIG_CTS_I2C_HOST + ret = cts_plat_i2c_write(cts_dev->pdata, + CTS_DEV_PROGRAM_MODE_I2CADDR, magic_num, 4, 5, + 10); + if (ret) { + cts_err("Write magic number to i2c_dev: 0x%02x failed %d", + CTS_DEV_PROGRAM_MODE_I2CADDR, ret); + return ret; + } + + cts_set_program_addr(cts_dev); + /* Write i2c deglitch register */ + ret = cts_hw_reg_writeb_retry(cts_dev, 0x37001, 0x0F, 5, 1); + if (ret) + cts_err("Write i2c deglitch register failed\n"); + +#else + cts_set_program_addr(cts_dev); + ret = cts_plat_spi_write(cts_dev->pdata, 0xcc, &magic_num[1], 3, 5, 10); + if (ret) { + cts_err("Write magic number to i2c_dev: 0x%02x failed %d", + CTS_DEV_PROGRAM_MODE_SPIADDR, ret); + return ret; + } +#endif /* CONFIG_CTS_I2C_HOST */ + cts_dev->rtdata.program_mode = true; + mdelay(5); + ret = cts_get_dev_boot_mode(cts_dev, &boot_mode); + if (ret) { + cts_err("Read BOOT_MODE failed %d", ret); + return ret; + } +#ifdef CONFIG_CTS_I2C_HOST + if ((boot_mode == CTS_DEV_BOOT_MODE_TCH_PRG_9916) || + (boot_mode == CTS_DEV_BOOT_MODE_I2C_PRG_9911C)) +#else + if ((boot_mode == CTS_DEV_BOOT_MODE_TCH_PRG_9916) || + (boot_mode == CTS_DEV_BOOT_MODE_SPI_PRG_9911C)) +#endif + { + return 0; + } + cts_err("BOOT_MODE readback %u != I2C/SPI PROMGRAM mode", boot_mode); + return -EFAULT; +} + +const char *cts_dev_boot_mode2str(u8 boot_mode) +{ + switch (boot_mode) { + case CTS_DEV_BOOT_MODE_IDLE: + return "IDLE-BOOT"; + case CTS_DEV_BOOT_MODE_FLASH: + return "FLASH-BOOT"; + case CTS_DEV_BOOT_MODE_SRAM: + return "SRAM-BOOT"; + /* case CTS_DEV_BOOT_MODE_I2C_PRG_9911C: */ + case CTS_DEV_BOOT_MODE_TCH_PRG_9916: + return "I2C-PRG-BOOT/TCH-PRG-BOOT"; + case CTS_DEV_BOOT_MODE_DDI_PRG: + return "DDI-PRG-BOOT"; + case CTS_DEV_BOOT_MODE_SPI_PRG_9911C: + return "SPI-PROG-BOOT/INVALID-BOOT"; + default: + return "INVALID"; + } +} + +int cts_enter_normal_mode(struct cts_device *cts_dev) +{ + int ret = 0; + u8 boot_mode; + int retries; + u16 fwid = CTS_DEV_FWID_INVALID; + u8 auto_boot = 0; + u8 first_boot = 1; + + cts_info("Enter normal mode"); + + if (!cts_dev->rtdata.program_mode) { + cts_warn("Enter normal mode while already in"); + return 0; + } + + if (cts_dev->rtdata.has_flash) + auto_boot = 1; + +#ifdef CFG_CTS_UPDATE_CRCCHECK + auto_boot = 1; +#endif + for (retries = 5; retries >= 0; retries--) { + if (first_boot == 1 || auto_boot == 0) { + cts_set_program_addr(cts_dev); + ret = + cts_set_dev_boot_mode(cts_dev, + CTS_DEV_BOOT_MODE_SRAM); + if (ret) + cts_err("Set BOOT_MODE to SRAM failed %d," + "try to reset device", ret); + + mdelay(30); + } + first_boot = 0; +#ifdef CONFIG_CTS_I2C_HOST + if (cts_plat_is_i2c_online + (cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR)) + cts_set_normal_addr(cts_dev); +#else + cts_set_normal_addr(cts_dev); +#endif + ret = cts_get_dev_boot_mode(cts_dev, &boot_mode); + if (ret) + cts_err("Get BOOT_MODE failed %d", ret); + if (boot_mode != CTS_DEV_BOOT_MODE_SRAM) + cts_err("Curr boot mode %u(%s) != SRAM_BOOT", + boot_mode, cts_dev_boot_mode2str(boot_mode)); + else + break; + + ret = cts_dev->ops->get_fw_id(cts_dev, &fwid); + if (ret) + cts_err("Get firmware id failed %d, retries %d", ret, + retries); + else { + if (fwid == CTS_DEV_FWID_ICNL9916 + || fwid == CTS_DEV_FWID_ICNL9911 + || fwid == CTS_DEV_FWID_ICNL9911S + || fwid == CTS_DEV_FWID_ICNL9911C + || fwid == CTS_DEV_FWID_ICNL9922) { + cts_info("Get firmware id successful 0x%02x", + fwid); + break; + } + } + cts_reset_device(cts_dev); + } + if (retries >= 0) { + ret = cts_init_fwdata(cts_dev); + if (ret) { + cts_err("Device init firmware data failed %d", ret); + return ret; + } + return 0; + } + cts_set_program_addr(cts_dev); + return ret; +} + +bool cts_is_device_enabled(const struct cts_device *cts_dev) +{ + return cts_dev->enabled; +} + +int cts_start_device(struct cts_device *cts_dev) +{ +#if defined(CONFIG_CTS_ESD_PROTECTION) || defined(CONFIG_CTS_CHARGER_DETECT) + struct chipone_ts_data *cts_data = + container_of(cts_dev, struct chipone_ts_data, cts_dev); +#endif + int ret; + + cts_info("Start device..."); + + if (cts_is_device_enabled(cts_dev)) { + cts_warn("Start device while already started"); + return 0; + } +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_enable_esd_protection(cts_data); +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CONFIG_CTS_CHARGER_DETECT + cts_start_charger_detect(cts_data); +#endif /* CONFIG_CTS_CHARGER_DETECT */ + + ret = cts_plat_enable_irq(cts_dev->pdata); + if (ret < 0) { + cts_err("Enable IRQ failed %d", ret); + return ret; + } + + cts_dev->enabled = true; + + cts_info("Start device successfully"); + + return 0; +} + +int cts_stop_device(struct cts_device *cts_dev) +{ + struct chipone_ts_data *cts_data = + container_of(cts_dev, struct chipone_ts_data, cts_dev); + int ret; + + cts_info("Stop device..."); + + if (!cts_is_device_enabled(cts_dev)) { + cts_warn("Stop device while halted"); + return 0; + } + + if (cts_is_firmware_updating(cts_dev)) { + cts_warn + ("Stop device while firmware updating, please try again"); + return -EAGAIN; + } + + ret = cts_plat_disable_irq(cts_dev->pdata); + if (ret < 0) { + cts_err("Disable IRQ failed %d", ret); + return ret; + } + + cts_dev->enabled = false; + +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_disable_esd_protection(cts_data); +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CONFIG_CTS_CHARGER_DETECT + cts_stop_charger_detect(cts_data); +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifndef CONFIG_GENERIC_HARDIRQS + if (work_pending(&cts_data->pdata->ts_irq_work)) { + cts_warn("IRQ work is pending .... flush it"); + flush_work(&cts_data->pdata->ts_irq_work); + } else + cts_info("None IRQ work is pending"); +#endif + cts_info("Flush workqueue..."); + flush_workqueue(cts_data->workqueue); + + ret = cts_plat_release_all_touch(cts_dev->pdata); + if (ret) { + cts_err("Release all touch failed %d", ret); + return ret; + } +#ifdef CONFIG_CTS_VIRTUALKEY + ret = cts_plat_release_all_vkey(cts_dev->pdata); + if (ret) { + cts_err("Release all vkey failed %d", ret); + return ret; + } +#endif /* CONFIG_CTS_VIRTUALKEY */ + + return 0; +} + +#ifdef CONFIG_CTS_ESD_PROTECTION +int cts_start_device_esdrecover(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Start device..."); + + if (cts_is_device_enabled(cts_dev)) { + cts_warn("Start device while already started"); + return 0; + } + + ret = cts_plat_enable_irq(cts_dev->pdata); + if (ret < 0) { + cts_err("Enable IRQ failed %d", ret); + return ret; + } + + cts_dev->enabled = true; + + cts_info("Start device successfully"); + + return 0; +} + +int cts_stop_device_esdrecover(struct cts_device *cts_dev) +{ + struct chipone_ts_data *cts_data = + container_of(cts_dev, struct chipone_ts_data, cts_dev); + int ret; + + cts_info("Stop device..."); + + if (!cts_is_device_enabled(cts_dev)) { + cts_warn("Stop device while halted"); + return 0; + } + + if (cts_is_firmware_updating(cts_dev)) { + cts_warn + ("Stop device while firmware updating, please try again"); + return -EAGAIN; + } + + ret = cts_plat_disable_irq(cts_dev->pdata); + if (ret < 0) { + cts_err("Disable IRQ failed %d", ret); + return ret; + } + + cts_dev->enabled = false; + + flush_workqueue(cts_data->workqueue); + + ret = cts_plat_release_all_touch(cts_dev->pdata); + if (ret) { + cts_err("Release all touch failed %d", ret); + return ret; + } +#ifdef CONFIG_CTS_VIRTUALKEY + ret = cts_plat_release_all_vkey(cts_dev->pdata); + if (ret) { + cts_err("Release all vkey failed %d", ret); + return ret; + } +#endif /* CONFIG_CTS_VIRTUALKEY */ + + return 0; +} +#endif + +bool cts_is_fwid_valid(u16 fwid) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cts_device_hwdatas); i++) { + if (cts_device_hwdatas[i].fwid == fwid) + return true; + } + + return false; +} + +static bool cts_is_hwid_valid(u32 hwid) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cts_device_hwdatas); i++) { + if (cts_device_hwdatas[i].hwid == hwid) + return true; + } + + return false; +} + +int cts_get_fwid(struct cts_device *cts_dev, u16 *fwid) +{ + int ret; + + cts_info("Get device firmware id"); + + if (cts_dev->hwdata) { + *fwid = cts_dev->hwdata->fwid; + return 0; + } + + if (cts_dev->rtdata.program_mode) { + cts_err("Get device firmware id while in program mode"); + ret = -ENODEV; + goto err_out; + } + + ret = cts_fw_reg_readw_retry(cts_dev, + CTS_DEVICE_FW_REG_CHIP_TYPE, fwid, 5, 1); + if (ret) + goto err_out; + + *fwid = be16_to_cpu(*fwid); + + cts_info("Device firmware id: %04x", *fwid); + + if (!cts_is_fwid_valid(*fwid)) { + cts_warn("Get invalid firmware id %04x", *fwid); + ret = -EINVAL; + goto err_out; + } + + return 0; + +err_out: + *fwid = CTS_DEV_FWID_INVALID; + return ret; +} + +int cts_get_hwid(struct cts_device *cts_dev, u32 *hwid) +{ + int ret; + + cts_info("Get device hardware id"); + + if (cts_dev->hwdata) { + *hwid = cts_dev->hwdata->hwid; + return 0; + } + + cts_info + ("Device hardware data not initialized, try to read from register"); + + if (!cts_dev->rtdata.program_mode) { + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + goto err_out; + } + } + + ret = + cts_hw_reg_readl_retry(cts_dev, CTS_DEV_HW_REG_HARDWARE_ID, hwid, 5, + 0); + if (ret) + goto err_out; + + *hwid = le32_to_cpu(*hwid); + *hwid &= 0XFFFFFFF0; + cts_info("Device hardware id: %04x", *hwid); + + if (!cts_is_hwid_valid(*hwid)) { + cts_warn("Device hardware id %04x invalid", *hwid); + ret = -EINVAL; + goto err_out; + } + + return 0; + +err_out: + *hwid = CTS_DEV_HWID_INVALID; + return ret; +} + +extern struct cts_dev_ops tcs_ops; +extern struct cts_dev_ops hostcomm_ops; + +int cts_probe_device(struct cts_device *cts_dev) +{ + int ret, retries = 0; + u16 fwid = CTS_DEV_FWID_INVALID; + u32 hwid = CTS_DEV_HWID_INVALID; + u16 device_fw_ver = 0; + const struct cts_firmware *firmware; + + cts_info("Probe device"); + + cts_dev->tx = kzalloc(INT_DATA_MAX_SIZ, GFP_KERNEL); + if (cts_dev->tx == NULL) { + cts_err("Alloc tx failed"); + return -ENOMEM; + } + cts_dev->rx = kzalloc(INT_DATA_MAX_SIZ, GFP_KERNEL); + if (cts_dev->rx == NULL) { + cts_err("Alloc rx failed"); + ret = -ENOMEM; + goto alloc_rx_exit; + } + + cts_dev->ops = &tcs_ops; + +read_hwid: +/** - Try to read hardware id, + *it will enter program mode as normal + */ + ret = cts_get_hwid(cts_dev, &hwid); + if (ret || hwid == CTS_DEV_HWID_INVALID) { + retries++; + + cts_err("Get hardware id failed %d retries %d", ret, retries); + + if (retries < 3) { + cts_reset_device(cts_dev); + goto read_hwid; + } else + return -ENODEV; + } + + if (hwid == CTS_DEV_HWID_ICNL9916 || hwid == CTS_DEV_HWID_ICNL9922) + cts_dev->ops = &tcs_ops; + else if ((hwid == CTS_DEV_HWID_ICNL9911C) || + (hwid == CTS_DEV_HWID_ICNL9911S) || + (hwid == CTS_DEV_HWID_ICNL9911)) + cts_dev->ops = &hostcomm_ops; + + //read_fwid: +#ifdef CONFIG_CTS_I2C_HOST + if (!cts_plat_is_i2c_online + (cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR)) + cts_warn("Normal mode i2c addr is offline"); + else +#else + if (!cts_plat_is_normal_mode(cts_dev->pdata)) + cts_warn("Normal mode spi addr is offline"); + else +#endif + { + ret = cts_dev->ops->get_fw_id(cts_dev, &fwid); + if (ret) + cts_err("Get firmware id failed %d, retries %d", ret, + retries); + else { + ret = + cts_fw_reg_readw_retry(cts_dev, + CTS_DEVICE_FW_REG_VERSION, + &device_fw_ver, 5, 0); + if (ret) { + cts_err("Read firmware version failed %d", ret); + device_fw_ver = 0; + } else { + device_fw_ver = be16_to_cpu(device_fw_ver); + cts_info("Device firmware version: %04x", + device_fw_ver); + } + goto init_hwdata; + } + } + +init_hwdata: + ret = cts_init_device_hwdata(cts_dev, hwid, fwid); + if (ret) { + cts_err("Device hwid: %06x fwid: %04x not found", hwid, fwid); + return -ENODEV; + } + +#ifdef CFG_CTS_FIRMWARE_FORCE_UPDATE + cts_warn("Force update firmware"); + firmware = cts_request_firmware(cts_dev, + CTS_DEV_HWID_ANY, CTS_DEV_FWID_ANY, 0); +#else /* CFG_CTS_FIRMWARE_FORCE_UPDATE */ + firmware = cts_request_firmware(cts_dev, hwid, fwid, device_fw_ver); +#endif /* CFG_CTS_FIRMWARE_FORCE_UPDATE */ + + retries = 0; +update_firmware: + if (firmware) { + ++retries; + ret = cts_update_firmware(cts_dev, firmware, false); + if (ret) { + cts_err("Update firmware failed %d retries %d", ret, + retries); + + if (retries < 3) { + cts_reset_device(cts_dev); + goto update_firmware; + } + } + cts_release_firmware(firmware); + } else { + if (fwid != CTS_DEV_FWID_INVALID) { + ret = cts_init_fwdata(cts_dev); + if (ret) + cts_err("Device init firmware data failed %d", + ret); + } + } + return 0; + +alloc_rx_exit: + if (cts_dev->tx) { + kfree(cts_dev->tx); + cts_dev->tx = NULL; + } + return ret; +} + +#ifdef CFG_CTS_GESTURE +void cts_enable_gesture_wakeup(struct cts_device *cts_dev) +{ + cts_info("Enable gesture wakeup"); + cts_dev->rtdata.gesture_wakeup_enabled = true; +} + +void cts_disable_gesture_wakeup(struct cts_device *cts_dev) +{ + cts_info("Disable gesture wakeup"); + cts_dev->rtdata.gesture_wakeup_enabled = false; +} + +bool cts_is_gesture_wakeup_enabled(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.gesture_wakeup_enabled; +} + +int cts_get_gesture_info(const struct cts_device *cts_dev, void *gesture_info) +{ + int ret; + + cts_info("Get gesture info"); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Get gesture info in program mode"); + return -ENODEV; + } + + if (!cts_dev->rtdata.suspended) { + cts_warn("Get gesture info while not suspended"); + return -ENODEV; + } + + if (!cts_dev->rtdata.gesture_wakeup_enabled) { + cts_warn("Get gesture info while gesture wakeup not enabled"); + return -ENODEV; + } + + ret = cts_dev->ops->get_gestureinfo(cts_dev, gesture_info); + if (ret) { + cts_err("Read gesture info header failed %d", ret); + return ret; + } + + return 0; +} +#endif /* CFG_CTS_GESTURE */ + +int cts_set_int_data_types(struct cts_device *cts_dev, u16 types) +{ + int ret = 0; + u16 realtypes = types & INT_DATA_TYPE_MASK; + + cts_info("Set int data types: %#06x, mask to %#06x", types, realtypes); + + if (cts_dev->ops->set_int_data_types) { + ret = cts_dev->ops->set_int_data_types(cts_dev, realtypes); + if (ret) { + cts_err("Set int data type failed: %d", ret); + return -EIO; + } + cts_dev->fwdata.int_data_types = realtypes; + ret = 0; + } + + if (cts_dev->ops->calc_int_data_size) + cts_dev->ops->calc_int_data_size(cts_dev); + + return ret; +} + +int cts_set_int_data_method(struct cts_device *cts_dev, u8 method) +{ + int ret = 0; + + cts_info("Set int data method: %d", method); + + if (method >= INT_DATA_METHOD_CNT) { + cts_err("Invalid int data method"); + return -EINVAL; + } + + if (cts_dev->ops->set_int_data_method) { + ret = cts_dev->ops->set_int_data_method(cts_dev, method); + if (ret) { + cts_err("Set int data method failed: %d", ret); + return -EIO; + } + cts_dev->fwdata.int_data_method = method; + ret = 0; + } + + if (cts_dev->ops->calc_int_data_size) + cts_dev->ops->calc_int_data_size(cts_dev); + + return ret; +} + +#ifdef CONFIG_CTS_ESD_PROTECTION +static void cts_esd_protection_work(struct work_struct *work) +{ + struct chipone_ts_data *cts_data; + int ret; + + cts_info("ESD protection work"); + cts_data = container_of(work, struct chipone_ts_data, esd_work.work); + cts_lock_device(&cts_data->cts_dev); +#ifdef CONFIG_CTS_I2C_HOST + if (!cts_plat_is_i2c_online + (cts_data->pdata, CTS_DEV_NORMAL_MODE_I2CADDR)) +#else + if (!cts_plat_is_normal_mode(cts_data->pdata)) +#endif + { + cts_data->esd_check_fail_cnt++; + /*reset chip next time */ + if ((cts_data->esd_check_fail_cnt % 2) == 0) { + cts_err + ("ESD protection read normal mode failed, reset chip!"); + ret = cts_reset_device(cts_data); + if (ret) + cts_err("ESD protection reset chip failed %d", + ret); + + } + } else + cts_data->esd_check_fail_cnt = 0; + + if (cts_data->esd_check_fail_cnt >= CFG_CTS_ESD_FAILED_CONFIRM_CNT) { + const struct cts_firmware *firmware; + + cts_warn("ESD protection check failed, update firmware!!!"); + cts_stop_device_esdrecover(&cts_data->cts_dev); + firmware = cts_request_firmware(&cts_data->cts_dev, + cts_data->cts_dev.hwdata->hwid, + cts_data->cts_dev.hwdata->fwid, + 0); + if (firmware) { + ret = + cts_update_firmware(&cts_data->cts_dev, firmware, + false); + cts_release_firmware(firmware); + + if (ret) + cts_err("Update default firmware failed %d", + ret); + } else + cts_err("Request default firmware failed %d, " + "please update manually!!", ret); + + cts_start_device_esdrecover(&cts_data->cts_dev); + cts_data->esd_check_fail_cnt = 0; + } + queue_delayed_work(cts_data->esd_workqueue, + &cts_data->esd_work, + CFG_CTS_ESD_PROTECTION_CHECK_PERIOD); + + cts_unlock_device(&cts_data->cts_dev); +} + +void cts_enable_esd_protection(struct chipone_ts_data *cts_data) +{ + if (cts_data->esd_workqueue && !cts_data->esd_enabled) { + cts_info("ESD protection enable"); + + cts_data->esd_enabled = true; + cts_data->esd_check_fail_cnt = 0; + queue_delayed_work(cts_data->esd_workqueue, + &cts_data->esd_work, + CFG_CTS_ESD_PROTECTION_CHECK_PERIOD); + } +} + +void cts_disable_esd_protection(struct chipone_ts_data *cts_data) +{ + if (cts_data->esd_workqueue && cts_data->esd_enabled) { + cts_info("ESD protection disable"); + + cts_data->esd_enabled = false; + cancel_delayed_work(&cts_data->esd_work); + flush_workqueue(cts_data->esd_workqueue); + } +} + +void cts_init_esd_protection(struct chipone_ts_data *cts_data) +{ + cts_info("Init ESD protection"); + + INIT_DELAYED_WORK(&cts_data->esd_work, cts_esd_protection_work); + + cts_data->esd_enabled = false; + cts_data->esd_check_fail_cnt = 0; +} + +void cts_deinit_esd_protection(struct chipone_ts_data *cts_data) +{ + cts_info("De-Init ESD protection"); + + if (cts_data->esd_workqueue && cts_data->esd_enabled) { + cts_data->esd_enabled = false; + cancel_delayed_work(&cts_data->esd_work); + } +} +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CONFIG_CTS_GLOVE +int cts_enter_glove_mode(const struct cts_device *cts_dev) +{ + cts_info("Enter glove mode"); + + ret = cts_fw_reg_writeb(cts_dev, 0x8000 + 149, 1); + if (ret) + cts_err("Enable Glove mode err"); + else + cts_dev->rtdata.glove_mode_enabled = true; + + return ret; +} + +int cts_exit_glove_mode(const struct cts_device *cts_dev) +{ + cts_info("Exit glove mode"); + + ret = cts_fw_reg_writeb(cts_dev, 0x8000 + 149, 0); + if (ret) + cts_err("Exit Glove mode err"); + else + cts_dev->rtdata.glove_mode_enabled = false; + + return ret; +} + +int cts_is_glove_enabled(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.glove_mode_enabled; +} + +#endif /* CONFIG_CTS_GLOVE */ + +#ifdef CONFIG_CTS_CHARGER_DETECT +bool cts_is_charger_exist(struct cts_device *cts_dev) +{ + struct chipone_ts_data *cts_data; + bool attached = false; + int ret; + + cts_data = container_of(cts_dev, struct chipone_ts_data, cts_dev); + + ret = cts_is_charger_attached(cts_data, &attached); + if (ret) + cts_err("Get charger state failed %d", ret); + + cts_dev->rtdata.charger_exist = attached; + + return attached; +} + +int cts_set_dev_charger_attached(struct cts_device *cts_dev, bool attached) +{ + int ret; + + cts_info("Set dev charger %s", attached ? "ATTACHED" : "DETATCHED"); + ret = cts_send_command(cts_dev, + attached ? CTS_CMD_CHARGER_ATTACHED : + CTS_CMD_CHARGER_DETACHED); + if (ret) { + if (ret) + cts_err("Send CMD_CHARGER_%s failed %d", + attached ? "ATTACHED" : "DETACHED", ret); + } + + return ret; +} +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +int cts_enable_fw_log_redirect(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Fw log redirect enable"); + ret = cts_send_command(cts_dev, CTS_CMD_ENABLE_FW_LOG_REDIRECT); + if (ret) + cts_err("Send CTS_CMD_ENABLE_FW_LOG_REDIRECT failed %d", ret); + else + cts_dev->rtdata.fw_log_redirect_enabled = true; + + return 0; +} + +int cts_disable_fw_log_redirect(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Fw log redirect disable"); + ret = cts_send_command(cts_dev, CTS_CMD_DISABLE_FW_LOG_REDIRECT); + if (ret) + cts_err("Send CTS_CMD_DISABLE_FW_LOG_REDIRECT failed %d", ret); + else + cts_dev->rtdata.fw_log_redirect_enabled = false; + + return 0; +} + +bool cts_is_fw_log_redirect(struct cts_device *cts_dev) +{ + return cts_dev->rtdata.fw_log_redirect_enabled; +} + +int cts_fw_log_show_finish(struct cts_device *cts_dev) +{ + int ret; + + ret = cts_send_command(cts_dev, CTS_CMD_FW_LOG_SHOW_FINISH); + if (ret) + cts_err("Send CTS_CMD_FW_LOG_SHOW_FINISH failed %d", ret); + + return ret; +} + +int cts_get_compensate_cap(struct cts_device *cts_dev, u8 *cap) +{ + int i, ret; + + if (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911 && + cts_dev->fwdata.lib_version < 0x0500) { + cts_err("ICNL9911 lib version 0x%04x < v5.0 " + "NOT supported get compensate cap", + cts_dev->fwdata.lib_version); + return -ENOTSUPP; + } + + cts_info("Get compensate cap"); + + ret = cts_dev->ops->enable_get_cneg(cts_dev); + if (ret) { + cts_err("Enable read compensate cap failed %d", ret); + return ret; + } + + mdelay(10); + + for (i = 0; i < 100; i++) { + u8 ready; + + ret = cts_dev->ops->is_cneg_ready(cts_dev, &ready); + if (ret) + cts_err("Read compensate cap ready flag failed %d", + ret); + else { + if (ready) + goto read_compensate_cap; + } + mdelay(5); + } + + cts_err("Wait compensate cap ready timeout"); + return -ETIMEDOUT; + +read_compensate_cap: + ret = cts_dev->ops->get_cneg(cts_dev, cap, + cts_dev->hwdata->num_row * + cts_dev->hwdata->num_col); + if (ret) + cts_err("Read compensate cap failed %d", ret); + /*Fall through to disable read compensate cap */ + + for (i = 0; i < 100; i++) { + int r; + u8 ready; + + /* r = cts_send_command(cts_dev,CTS_CMD_DISABLE_READ_CNEG); */ + r = cts_dev->ops->disable_get_cneg(cts_dev); + if (r) { + cts_err("Send cmd DISABLE_READ_CNEG failed %d", r); + continue; + } + + mdelay(1); + + r = cts_dev->ops->is_cneg_ready(cts_dev, &ready); + if (r) { + cts_err("Read compensate cap ready flag failed %d", r); + continue; + } + if (ready) + continue; + else + return ret; + } + + cts_warn("Compensate cap ready flag cannot clear, try to do reset"); + + /* Try to do hardware reset */ + cts_reset_device(cts_dev); + +#ifdef CONFIG_CTS_CHARGER_DETECT + if (cts_is_charger_exist(cts_dev)) { + int r = cts_set_dev_charger_attached(cts_dev, true); + + if (r) + cts_err("Set dev charger attached failed %d", r); + } +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) { + int r = cts_enter_glove_mode(cts_dev); + + if (r) + cts_err("Enter dev glove mode failed %d", r); + } +#endif /* CONFIG_CTS_GLOVE */ + +#ifdef CFG_CTS_FW_LOG_REDIRECT + if (cts_is_fw_log_redirect(cts_dev)) { + int r = cts_enable_fw_log_redirect(cts_dev); + + if (r) + cts_err("Enable fw log redirect failed %d", r); + } +#endif /* CONFIG_CTS_GLOVE */ + + return ret; +} + +void cts_firmware_upgrade_work(struct work_struct *work) +{ + struct chipone_ts_data *cts_data; + struct cts_device *cts_dev; + const struct cts_firmware *firmware; + int retries; + int ret; + + cts_info("Firmware upgrade work"); + + cts_data = + container_of(work, struct chipone_ts_data, fw_upgrade_work.work); + cts_dev = &cts_data->cts_dev; + + firmware = cts_request_firmware(cts_dev, cts_dev->hwdata->hwid, + CTS_DEV_FWID_ANY, + cts_dev->fwdata.version); + if (firmware == NULL) { + cts_err("Request firmware failed"); + return; + } + + retries = 0; + do { + ret = cts_update_firmware(cts_dev, firmware, false); + if (ret) { + cts_err("Update firmware failed %d retries %d", ret, + retries); + + cts_reset_device(cts_dev); + } else + break; + } while (++retries < 3); + + cts_release_firmware(firmware); + + if (ret == 0) + cts_start_device(cts_dev); +} + +void cts_deinit_rtdata(struct cts_device *cts_dev) +{ + if (cts_dev->ops->deinit_int_data) + cts_dev->ops->deinit_int_data(cts_dev); +} + +int cts_reset_device(struct cts_device *cts_dev) +{ + cts_info("Reset device"); + + if (!cts_dev->ops) + return cts_enter_program_mode(cts_dev); + + if (cts_dev->ops->reset_device) + return cts_dev->ops->reset_device(cts_dev); + + cts_err("BUG! reset_device should NOT be null!"); + return -EIO; +} + +int cts_spi_xtrans(const struct cts_device *cts_dev, u8 *tx, size_t txlen, u8 *rx, size_t rxlen) +{ + if (cts_dev->ops->spi_xtrans) { + return cts_dev->ops->spi_xtrans(cts_dev, tx, txlen, rx, rxlen); + } + + return -1; +} + +static struct file *cts_log_filp; +static int cts_log_to_file_level; +extern int cts_write_file(struct file *filp, const void *data, size_t size); + +static char *cts_log_buffer; +static int cts_log_buf_size; +static int cts_log_buf_wr_size; + +static bool cts_log_redirect; + +extern int cts_mkdir_for_file(const char *filepath, umode_t mode); + +int cts_start_driver_log_redirect(const char *filepath, bool append_to_file, + char *log_buffer, int log_buf_size, int log_level) +{ +#define START_BANNER \ + ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" + + int ret = 0; + + cts_info("Start driver log redirect"); + + cts_log_to_file_level = log_level; + + if (log_buffer && log_buf_size) { + cts_info(" - Start driver log to buffer: %p size: %d level: %d", + log_buffer, log_buf_size, log_level); + cts_log_buffer = log_buffer; + cts_log_buf_size = log_buf_size; + cts_log_buf_wr_size = 0; + } + + if (filepath && filepath[0]) { + cts_info(" - Start driver log to file : '%s' level: %d", + filepath, log_level); + cts_log_filp = filp_open(filepath, + O_WRONLY | O_CREAT | (append_to_file ? O_APPEND : O_TRUNC), + S_IRUGO | S_IWUGO); + if (IS_ERR(cts_log_filp)) { + ret = PTR_ERR(cts_log_filp); + cts_log_filp = NULL; + cts_err("Open file '%s' for driver log failed %d", + filepath, ret); + } else { + cts_write_file(cts_log_filp, START_BANNER, strlen(START_BANNER)); + } + } + + cts_log_redirect = true; + + return ret; +#undef START_BANNER +} + +void cts_stop_driver_log_redirect(void) +{ +#define END_BANNER \ + "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n" + + cts_log_redirect = false; + + cts_info("Stop driver log redirect"); + + if (cts_log_filp) { + int ret; + + cts_info(" - Stop driver log to file"); + + cts_write_file(cts_log_filp, END_BANNER, strlen(END_BANNER)); + ret = filp_close(cts_log_filp, NULL); + if (ret) + cts_err("Close driver log file failed %d", ret); + + cts_log_filp = NULL; + } + + if (cts_log_buffer) { + cts_info(" - Stop driver log to buffer"); + + cts_log_buffer = NULL; + cts_log_buf_size = 0; + cts_log_buf_wr_size = 0; + } + +#undef END_BANNER +} + +int cts_get_driver_log_redirect_size(void) +{ + if (cts_log_redirect && cts_log_buffer && cts_log_buf_wr_size) + return cts_log_buf_wr_size; + else + return 0; +} + +void cts_log(int level, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + if (cts_log_redirect) { + if (cts_log_buffer && + cts_log_buf_wr_size < cts_log_buf_size && + level <= cts_log_to_file_level) + cts_log_buf_wr_size += vscnprintf(cts_log_buffer + cts_log_buf_wr_size, + cts_log_buf_size - cts_log_buf_wr_size, fmt, args); + + if (cts_log_filp != NULL && level <= cts_log_to_file_level) { + char buf[512]; + int count = vscnprintf(buf, sizeof(buf), fmt, args); + + cts_write_file(cts_log_filp, buf, count); + } + } + + if (level < CTS_DRIVER_LOG_DEBUG || cts_show_debug_log) + vprintk(fmt, args); + + va_end(args); +} diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_core.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_core.h new file mode 100644 index 000000000000..90518567f407 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_core.h @@ -0,0 +1,954 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_CORE_H +#define CTS_CORE_H + +#include "cts_config.h" +#include +#include + +#ifdef CHIPONE_SENSOR_EN +#include +#endif + +enum cts_dev_hw_reg { + CTS_DEV_HW_REG_HARDWARE_ID = 0x30000u, + CTS_DEV_HW_REG_CLOCK_GATING = 0x30004u, + CTS_DEV_HW_REG_RESET_CONFIG = 0x30008u, + CTS_DEV_HW_REG_BOOT_MODE = 0x30010u, + CTS_DEV_HW_REG_CURRENT_MODE = 0x30011u, +}; + +enum cts_dev_boot_mode { + CTS_DEV_BOOT_MODE_IDLE = 0, + CTS_DEV_BOOT_MODE_FLASH = 1, + CTS_DEV_BOOT_MODE_I2C_PRG_9911C = 2, + CTS_DEV_BOOT_MODE_TCH_PRG_9916 = 2, + CTS_DEV_BOOT_MODE_SRAM = 3, + CTS_DEV_BOOT_MODE_DDI_PRG = 4, + CTS_DEV_BOOT_MODE_SPI_PRG_9911C = 5, + CTS_DEV_BOOT_MODE_MASK = 7, +}; + +/** I2C addresses(7bits), transfer size and bitrate */ +#define CTS_DEV_NORMAL_MODE_I2CADDR (0x48) +#define CTS_DEV_PROGRAM_MODE_I2CADDR (0x30) +#define CTS_DEV_NORMAL_MODE_ADDR_WIDTH (2) +#define CTS_DEV_PROGRAM_MODE_ADDR_WIDTH (3) +#define CTS_DEV_NORMAL_MODE_SPIADDR (0xF0) +#define CTS_DEV_PROGRAM_MODE_SPIADDR (0x60) + +/** Chipone firmware register addresses under normal mode */ +enum cts_device_fw_reg { + CTS_DEVICE_FW_REG_WORK_MODE = 0x0000, + CTS_DEVICE_FW_REG_SYS_BUSY = 0x0001, + CTS_DEVICE_FW_REG_DATA_READY = 0x0002, + CTS_DEVICE_FW_REG_CMD = 0x0004, + CTS_DEVICE_FW_REG_POWER_MODE = 0x0005, + CTS_DEVICE_FW_REG_FW_LIB_MAIN_VERSION = 0x0009, + CTS_DEVICE_FW_REG_CHIP_TYPE = 0x000A, + CTS_DEVICE_FW_REG_VERSION = 0x000C, + CTS_DEVICE_FW_REG_DDI_VERSION = 0x0010, + CTS_DEVICE_FW_REG_GET_WORK_MODE = 0x003F, + CTS_DEVICE_FW_REG_FW_LIB_SUB_VERSION = 0x0047, + CTS_DEVICE_FW_REG_COMPENSATE_CAP_READY = 0x004E, + + CTS_DEVICE_FW_REG_TOUCH_INFO = 0x1000, + CTS_DEVICE_FW_REG_RAW_DATA = 0x2000, + CTS_DEVICE_FW_REG_DIFF_DATA = 0x3000, + CTS_DEVICE_FW_REG_GESTURE_INFO = 0x7000, + + CTS_DEVICE_FW_REG_PANEL_PARAM = 0x8000, + CTS_DEVICE_FW_REG_NUM_TX = 0x8007, + CTS_DEVICE_FW_REG_NUM_RX = 0x8008, + CTS_DEVICE_FW_REG_INT_KEEP_TIME = 0x8047, /* Unit us */ + CTS_DEVICE_FW_REG_RAWDATA_TARGET = 0x8049, + CTS_DEVICE_FW_REG_X_RESOLUTION = 0x8090, + CTS_DEVICE_FW_REG_Y_RESOLUTION = 0x8092, + CTS_DEVICE_FW_REG_SWAP_AXES = 0x8094, + CTS_DEVICE_FW_REG_GLOVE_MODE = 0x8095, + CTS_DEVICE_FW_REG_TEST_WITH_DISPLAY_ON = 0x80A3, + CTS_DEVICE_FW_REG_INT_MODE = 0x80D8, + CTS_DEVICE_FW_REG_EARJACK_DETECT_SUPP = 0x8113, + CTS_DEVICE_FW_REG_AUTO_COMPENSATE_EN = 0x8114, + CTS_DEVICE_FW_REG_ESD_PROTECTION = 0x8156, /* RW */ + CTS_DEVICE_FW_REG_FLAG_BITS = 0x8158, + + CTS_DEVICE_FW_REG_COMPENSATE_CAP = 0xA000, + CTS_DEVICE_FW_REG_DEBUG_INTF = 0xF000, +}; + +/** Hardware IDs, read from hardware id register */ +enum cts_dev_hwid { + CTS_DEV_HWID_ICNL9911 = 0x990100u, + CTS_DEV_HWID_ICNL9911S = 0x990110u, + CTS_DEV_HWID_ICNL9911C = 0x991110u, + CTS_DEV_HWID_ICNL9916 = 0x990160u, + CTS_DEV_HWID_ICNL9922 = 0x990220u, + + CTS_DEV_HWID_ANY = 0, + CTS_DEV_HWID_INVALID = 0xFFFFFFFFu, +}; + +/* Firmware IDs, read from firmware register @ref CTS_DEV_FW_REG_CHIP_TYPE + * under normal mode + */ +enum cts_dev_fwid { + CTS_DEV_FWID_ICNL9911 = 0x9911u, + CTS_DEV_FWID_ICNL9911S = 0x9964u, + CTS_DEV_FWID_ICNL9911C = 0x9954u, + CTS_DEV_FWID_ICNL9916 = 0x9916u, + CTS_DEV_FWID_ICNL9922 = 0x9922u, + + CTS_DEV_FWID_ANY = 0u, + CTS_DEV_FWID_INVALID = 0xFFFFu +}; + +/** Commands written to firmware register @ref CTS_DEVICE_FW_REG_CMD under normal mode */ +enum cts_firmware_cmd { + CTS_CMD_RESET = 1, + CTS_CMD_SUSPEND = 2, + CTS_CMD_ENTER_WRITE_PARA_TO_FLASH_MODE = 3, + CTS_CMD_WRITE_PARA_TO_FLASH = 4, + CTS_CMD_WRTITE_INT_HIGH = 5, + CTS_CMD_WRTITE_INT_LOW = 6, + CTS_CMD_RELASE_INT_TEST = 7, + CTS_CMD_RECOVERY_TX_VOL = 0x10, + CTS_CMD_DEC_TX_VOL_1 = 0x11, + CTS_CMD_DEC_TX_VOL_2 = 0x12, + CTS_CMD_DEC_TX_VOL_3 = 0x13, + CTS_CMD_DEC_TX_VOL_4 = 0x14, + CTS_CMD_DEC_TX_VOL_5 = 0x15, + CTS_CMD_DEC_TX_VOL_6 = 0x16, + CTS_CMD_ENABLE_READ_RAWDATA = 0x20, + CTS_CMD_DISABLE_READ_RAWDATA = 0x21, + CTS_CMD_SUSPEND_WITH_GESTURE = 0x40, + CTS_CMD_QUIT_GESTURE_MONITOR = 0x41, + CTS_CMD_CHARGER_ATTACHED = 0x55, + CTS_CMD_EARJACK_ATTACHED = 0x57, + CTS_CMD_EARJACK_DETACHED = 0x58, + CTS_CMD_CHARGER_DETACHED = 0x66, + CTS_CMD_ENABLE_FW_LOG_REDIRECT = 0x86, + CTS_CMD_DISABLE_FW_LOG_REDIRECT = 0x87, + CTS_CMD_ENABLE_READ_CNEG = 0x88, + CTS_CMD_DISABLE_READ_CNEG = 0x89, + CTS_CMD_FW_LOG_SHOW_FINISH = 0xE0, + +}; + +#pragma pack(1) +/** Touch message read back from chip */ +struct cts_device_touch_msg { + u8 id; + __le16 x; + __le16 y; + u8 pressure; + u8 event; + +#define CTS_DEVICE_TOUCH_EVENT_NONE (0) +#define CTS_DEVICE_TOUCH_EVENT_DOWN (1) +#define CTS_DEVICE_TOUCH_EVENT_MOVE (2) +#define CTS_DEVICE_TOUCH_EVENT_STAY (3) +#define CTS_DEVICE_TOUCH_EVENT_UP (4) + +}; + +/** Touch information read back from chip */ +struct cts_device_touch_info { + u8 vkey_state; + u8 num_msg; + + struct cts_device_touch_msg msgs[CFG_CTS_MAX_TOUCH_NUM]; +}; + +/** Gesture trace point read back from chip */ +struct cts_device_gesture_point { + __le16 x; + __le16 y; +}; + +/** Gesture information read back from chip */ +/* total size 112 bytes */ +struct cts_device_gesture_info { + u8 gesture_id; +#define CTS_GESTURE_UP (0x11) +#define CTS_GESTURE_C (0x12) +#define CTS_GESTURE_O (0x13) +#define CTS_GESTURE_M (0x14) +#define CTS_GESTURE_W (0x15) +#define CTS_GESTURE_E (0x16) +#define CTS_GESTURE_S (0x17) +#define CTS_GESTURE_B (0x18) +#define CTS_GESTURE_T (0x19) +#define CTS_GESTURE_H (0x1A) +#define CTS_GESTURE_F (0x1B) +#define CTS_GESTURE_X (0x1C) +#define CTS_GESTURE_Z (0x1D) +#define CTS_GESTURE_V (0x1E) +#define CTS_GESTURE_D_TAP (0x50) +#define CTS_GESTURE_TAP (0x7F) + + u8 num_points; + +#define CTS_CHIP_MAX_GESTURE_TRACE_POINT (27u) + struct cts_device_gesture_point points[CTS_CHIP_MAX_GESTURE_TRACE_POINT]; + u8 reserved[2]; + +}; +#pragma pack() + +struct cts_device; + +enum cts_crc_type { + CTS_CRC16 = 1, + CTS_CRC32 = 2, +}; + +/** Chip hardware data, will never change */ +struct cts_device_hwdata { + const char *name; + u32 hwid; + u16 fwid; + u8 num_row; + u8 num_col; + u32 sram_size; + + /* Address width under program mode */ + u8 program_addr_width; + + const struct cts_sfctrl *sfctrl; + int (*enable_access_ddi_reg)(struct cts_device *cts_dev, bool enable); +}; + +enum int_data_type { + INT_DATA_TYPE_NONE = 0, + INT_DATA_TYPE_RAWDATA = BIT(0), + INT_DATA_TYPE_MANUAL_DIFF = BIT(1), + INT_DATA_TYPE_REAL_DIFF = BIT(2), + INT_DATA_TYPE_NOISE_DIFF = BIT(3), + INT_DATA_TYPE_BASEDATA = BIT(4), + INT_DATA_TYPE_CNEGDATA = BIT(5), + INT_DATA_TYPE_MASK = 0x3F, +}; + +enum int_data_method { + INT_DATA_METHOD_NONE = 0, + INT_DATA_METHOD_HOST = 1, + INT_DATA_METHOD_POLLING = 2, + INT_DATA_METHOD_DEBUG = 3, + INT_DATA_METHOD_CNT = 4, +}; + +/** Chip firmware data */ +struct cts_device_fwdata { + u16 version; + u16 res_x; + u16 res_y; + u8 rows; + u8 cols; + bool flip_x; + bool flip_y; + bool swap_axes; + u8 ddi_version; + u8 int_mode; + u8 esd_method; + u16 lib_version; + u16 int_keep_time; + u16 rawdata_target; + bool has_int_data; + u8 int_data_method; + u16 int_data_types; + size_t int_data_size; +}; + +/** Chip runtime data */ +struct cts_device_rtdata { + u8 slave_addr; + int addr_width; + bool program_mode; + bool has_flash; + + bool suspended; + bool updating; + bool testing; + +#ifdef CFG_DUMP_INT_DATA + bool dumping; + u32 dump_cnt; +#endif + + bool gesture_wakeup_enabled; + bool charger_exist; + bool fw_log_redirect_enabled; + bool glove_mode_enabled; + + struct cts_device_touch_info touch_info; + struct cts_device_gesture_info gesture_info; + + u8 *int_data; +}; + +struct cts_dev_ops { + int (*get_fw_ver)(const struct cts_device *cts_dev, u16 *fwver); + int (*get_lib_ver)(const struct cts_device *cts_dev, u16 *libver); + int (*get_ddi_ver)(const struct cts_device *cts_dev, u8 *ddiver); + int (*get_res_x)(const struct cts_device *cts_dev, u16 *res_x); + int (*get_res_y)(const struct cts_device *cts_dev, u16 *res_y); + int (*get_rows)(const struct cts_device *cts_dev, u8 *rows); + int (*get_cols)(const struct cts_device *cts_dev, u8 *cols); + int (*get_flip_x)(const struct cts_device *cts_dev, bool *flip_x); + int (*get_flip_y)(const struct cts_device *cts_dev, bool *flip_y); + int (*get_swap_axes)(const struct cts_device *cts_dev, + bool *swap_axes); + int (*get_int_mode)(const struct cts_device *cts_dev, u8 *int_mode); + int (*get_int_keep_time)(const struct cts_device *cts_dev, + u16 *int_keep_time); + int (*get_esd_method)(const struct cts_device *cts_dev, + u8 *esd_method); + int (*get_gestureinfo)(const struct cts_device *cts_dev, + struct cts_device_gesture_info *gesture_info); + int (*get_touchinfo)(struct cts_device *cts_dev, + struct cts_device_touch_info *touch_info); + int (*get_esd_protection)(const struct cts_device *cts_dev, + u8 *esd_protection); + int (*get_data_ready_flag)(const struct cts_device *cts_dev, + u8 *ready); + int (*clr_data_ready_flag)(const struct cts_device *cts_dev); + int (*enable_get_rawdata)(const struct cts_device *cts_dev); + int (*is_enabled_get_rawdata)(const struct cts_device *cts_dev, + u8 *enabled); + int (*disable_get_rawdata)(const struct cts_device *cts_dev); + int (*enable_get_cneg)(const struct cts_device *cts_dev); + int (*disable_get_cneg)(const struct cts_device *cts_dev); + int (*is_cneg_ready)(const struct cts_device *cts_dev, u8 *ready); + int (*quit_guesture_mode)(const struct cts_device *cts_dev); + int (*get_rawdata)(const struct cts_device *cts_dev, u8 *buf); + int (*get_diffdata)(const struct cts_device *cts_dev, u8 *buf); + int (*get_basedata)(const struct cts_device *cts_dev, u8 *buf); + int (*get_cneg)(const struct cts_device *cts_dev, u8 *buf, + size_t size); + int (*read_hw_reg)(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); + int (*write_hw_reg)(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); + int (*read_ddi_reg)(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size); + int (*write_ddi_reg)(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size); + int (*read_fw_reg)(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); + int (*write_fw_reg)(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); + int (*read_reg)(const struct cts_device *cts_dev, uint16_t cmd, + u8 *rbuf, size_t rlen); + int (*write_reg)(const struct cts_device *cts_dev, uint16_t cmd, + u8 *wbuf, size_t wlen); + int (*get_fw_id)(const struct cts_device *cts_dev, u16 *fwid); + int (*get_workmode)(const struct cts_device *cts_dev, u8 *workmode); + int (*set_workmode)(const struct cts_device *cts_dev, u8 workmode); + int (*set_openshort_mode)(const struct cts_device *cts_dev, u8 mode); + int (*set_tx_vol)(const struct cts_device *cts_dev, u8 txvol); + int (*set_short_test_type)(const struct cts_device *cts_dev, + u8 short_type); + int (*set_openshort_enable)(const struct cts_device *cts_dev, + u8 enable); + int (*is_openshort_enabled)(const struct cts_device *cts_dev, + u8 *enabled); + int (*set_esd_enable)(const struct cts_device *cts_dev, u8 enable); + int (*set_cneg_enable)(const struct cts_device *cts_dev, u8 enable); + int (*set_mnt_enable)(const struct cts_device *cts_dev, u8 enable); + int (*is_display_on)(const struct cts_device *cts_dev, + u8 *display_on); + int (*set_display_on)(const struct cts_device *cts_dev, u8 display_on); + int (*is_cneg_enabled)(const struct cts_device *cts_dev, u8 *enabled); + int (*is_mnt_enabled)(const struct cts_device *cts_dev, u8 *enabled); + int (*set_pwr_mode)(const struct cts_device *cts_dev, u8 pwr_mode); + int (*read_sram_normal_mode)(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay); + int (*init_int_data)(struct cts_device *cts_dev); + int (*deinit_int_data)(struct cts_device *cts_dev); + int (*get_has_int_data)(const struct cts_device *cts_dev, + bool *has_int_data); + int (*get_int_data_method)(const struct cts_device *cts_dev, + u8 *int_data_method); + int (*set_int_data_method)(const struct cts_device *cts_dev, + u8 int_data_method); + int (*get_int_data_types)(const struct cts_device *cts_dev, + u16 *int_data_types); + int (*set_int_data_types)(const struct cts_device *cts_dev, + u16 int_data_types); + int (*calc_int_data_size)(struct cts_device *cts_dev); + int (*polling_data)(const struct cts_device *cts_dev, u8 *buf, + size_t size); + int (*polling_test_data)(const struct cts_device *cts_dev, u8 *buf, + size_t size); + + int (*top_get_rawdata)(struct cts_device *cts_dev, u8 *buf, + size_t size); + int (*top_get_manual_diff)(struct cts_device *cts_dev, u8 *buf, + size_t size); + int (*top_get_real_diff)(struct cts_device *cts_dev, u8 *buf, + size_t size); + int (*top_get_noise_diff)(struct cts_device *cts_dev, u8 *buf, + size_t size); + int (*top_get_basedata)(struct cts_device *cts_dev, u8 *buf, + size_t size); + int (*top_get_cnegdata)(struct cts_device *cts_dev, u8 *buf, + size_t size); + + int (*reset_device)(const struct cts_device *cts_dev); + + int (*set_int_test)(const struct cts_device *cts_dev, u8 enable); + int (*set_int_pin)(const struct cts_device *cts_dev, u8 high); + int (*get_module_id)(const struct cts_device *cts_dev, u32 *modId); + int (*spi_xtrans)(const struct cts_device *cts_dev, u8 *tx, size_t txlen, u8 *rx, size_t rxlen); +}; + +struct cts_device { + struct cts_platform_data *pdata; + + const struct cts_device_hwdata *hwdata; + struct cts_device_fwdata fwdata; + struct cts_device_rtdata rtdata; + const struct cts_flash *flash; + bool enabled; +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD + char config_fw_name[CFG_CTS_FW_FILE_NAME_MAX_LEN + 1]; +#endif /* CFG_CTS_FW_UPDATE_FILE_LOAD */ + + struct cts_dev_ops *ops; + u8 *tx; + u8 *rx; +}; + +struct cts_platform_data; + +#define MAX_PANEL_IDX 2 +enum touch_panel_id { + TOUCH_PANEL_IDX_PRIMARY = 0, + TOUCH_PANEL_MAX_IDX, +}; + +#ifdef CHIPONE_SENSOR_EN +/* display state */ +enum display_state { + SCREEN_UNKNOWN, + SCREEN_OFF, + SCREEN_ON, +}; +struct chipone_sensor_platform_data { + struct input_dev *input_sensor_dev; + struct sensors_classdev ps_cdev; + int sensor_opened; + char sensor_data; /* 0 near, 1 far */ + struct chipone_ts_data *data; +}; +#define REPORT_MAX_COUNT 10000 +#endif + +struct chipone_ts_data { +#ifdef CONFIG_CTS_I2C_HOST + struct i2c_client *i2c_client; +#else + struct spi_device *spi_client; +#endif /* CONFIG_CTS_I2C_HOST */ + struct device *device; + struct cts_device cts_dev; + struct cts_platform_data *pdata; + struct workqueue_struct *workqueue; + struct delayed_work fw_upgrade_work; + struct work_struct ts_resume_work; +#ifdef CONFIG_CTS_CHARGER_DETECT + void *charger_detect_data; +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_ESD_PROTECTION + struct workqueue_struct *esd_workqueue; + struct delayed_work esd_work; + bool esd_enabled; + int esd_check_fail_cnt; +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CONFIG_CTS_LEGACY_TOOL + struct proc_dir_entry *procfs_entry; +#endif /* CONFIG_CTS_LEGACY_TOOL */ + + void *oem_data; + + bool force_reflash; + +#ifdef CHIPONE_SENSOR_EN + bool wakeable; + bool should_enable_gesture; + bool gesture_enabled; + uint32_t report_gesture_key; + enum display_state screen_state; + struct mutex state_mutex; + struct chipone_sensor_platform_data *sensor_pdata; +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock gesture_wakelock; +#else + struct wakeup_source *gesture_wakelock; +#endif +#endif + +}; + +static inline u32 get_unaligned_le24(const void *p) +{ + const u8 *puc = (const u8 *)p; + + return (puc[0] | (puc[1] << 8) | (puc[2] << 16)); +} + +static inline u32 get_unaligned_be24(const void *p) +{ + const u8 *puc = (const u8 *)p; + + return (puc[2] | (puc[1] << 8) | (puc[0] << 16)); +} + +static inline void put_unaligned_be24(u32 v, void *p) +{ + u8 *puc = (u8 *) p; + + puc[0] = (v >> 16) & 0xFF; + puc[1] = (v >> 8) & 0xFF; + puc[2] = (v >> 0) & 0xFF; +} + +#define wrap(max, x) ((max) - 1 - (x)) + +extern void cts_lock_device(const struct cts_device *cts_dev); +extern void cts_unlock_device(const struct cts_device *cts_dev); + +extern int cts_dev_readb(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay); +extern int cts_dev_writeb(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay); + +extern int cts_sram_writeb_retry(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay); +extern int cts_sram_writew_retry(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay); +extern int cts_sram_writel_retry(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay); +extern int cts_sram_writesb_retry(const struct cts_device *cts_dev, + u32 addr, const void *src, size_t len, + int retry, int delay); +extern int cts_sram_writesb_check_crc_retry(const struct cts_device *cts_dev, + u32 addr, const void *src, + size_t len, u32 crc, int retry); + +extern int cts_sram_readb_retry(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay); +extern int cts_sram_readw_retry(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay); +extern int cts_sram_readl_retry(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay); +extern int cts_sram_readsb_retry(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay); + +extern int cts_fw_reg_writeb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 b, int retry, int delay); +extern int cts_fw_reg_writew_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 w, int retry, int delay); +extern int cts_fw_reg_writel_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 l, int retry, int delay); +extern int cts_fw_reg_writesb_retry(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len, + int retry, int delay); + +extern int cts_fw_reg_readb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b, int retry, int delay); +extern int cts_fw_reg_readw_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w, int retry, int delay); +extern int cts_fw_reg_readl_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l, int retry, int delay); +extern int cts_fw_reg_readsb_retry(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, + int retry, int delay); +extern int cts_fw_reg_readsb_retry_delay_idle(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, + size_t len, int retry, int delay, + int idle); + +extern int cts_hw_reg_writeb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 b, int retry, int delay); +extern int cts_hw_reg_writew_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 w, int retry, int delay); +extern int cts_hw_reg_writel_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 l, int retry, int delay); +extern int cts_hw_reg_writesb_retry(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len, + int retry, int delay); + +extern int cts_hw_reg_readb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b, int retry, int delay); +extern int cts_hw_reg_readw_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w, int retry, int delay); +extern int cts_hw_reg_readl_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l, int retry, int delay); +extern int cts_hw_reg_readsb_retry(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, + int retry, int delay); + +static inline int cts_hw_reg_readb(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b) +{ + return cts_hw_reg_readb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_hw_reg_writeb(const struct cts_device *cts_dev, + u32 reg_addr, u8 b) +{ + return cts_hw_reg_writeb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_hw_reg_writew(const struct cts_device *cts_dev, + u32 reg_addr, u16 w) +{ + return cts_hw_reg_writew_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_fw_reg_writeb(const struct cts_device *cts_dev, + u32 reg_addr, u8 b) +{ + return cts_fw_reg_writeb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_fw_reg_writew(const struct cts_device *cts_dev, + u32 reg_addr, u16 w) +{ + return cts_fw_reg_writew_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_fw_reg_writel(const struct cts_device *cts_dev, + u32 reg_addr, u32 l) +{ + return cts_fw_reg_writel_retry(cts_dev, reg_addr, l, 1, 0); +} + +static inline int cts_fw_reg_writesb(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len) +{ + return cts_fw_reg_writesb_retry(cts_dev, reg_addr, src, len, 1, 0); +} + +static inline int cts_fw_reg_readb(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b) +{ + return cts_fw_reg_readb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_fw_reg_readw(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w) +{ + return cts_fw_reg_readw_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_fw_reg_readl(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l) +{ + return cts_fw_reg_readl_retry(cts_dev, reg_addr, l, 1, 0); +} + +static inline int cts_fw_reg_readsb(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len) +{ + return cts_fw_reg_readsb_retry(cts_dev, reg_addr, dst, len, 1, 0); +} + +static inline int cts_fw_reg_readsb_delay_idle(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, + size_t len, int idle) +{ + return cts_fw_reg_readsb_retry_delay_idle(cts_dev, reg_addr, dst, len, + 1, 0, idle); +} + +static inline int cts_hw_reg_writeb_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u8 b) +{ + return cts_hw_reg_writeb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_hw_reg_writew_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u16 w) +{ + return cts_hw_reg_writew_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_hw_reg_writel_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u32 l) +{ + return cts_hw_reg_writel_retry(cts_dev, reg_addr, l, 1, 0); +} + +static inline int cts_hw_reg_writesb(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len) +{ + return cts_hw_reg_writesb_retry(cts_dev, reg_addr, src, len, 1, 0); +} + +static inline int cts_hw_reg_readb_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b) +{ + return cts_hw_reg_readb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_hw_reg_readw_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w) +{ + return cts_hw_reg_readw_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_hw_reg_readl_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l) +{ + return cts_hw_reg_readl_retry(cts_dev, reg_addr, l, 1, 0); +} + +static inline int cts_hw_reg_readsb(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len) +{ + return cts_hw_reg_readsb_retry(cts_dev, reg_addr, dst, len, 1, 0); +} + +static inline int cts_sram_writeb(const struct cts_device *cts_dev, + u32 addr, u8 b) +{ + return cts_sram_writeb_retry(cts_dev, addr, b, 1, 0); +} + +static inline int cts_sram_writew(const struct cts_device *cts_dev, + u32 addr, u16 w) +{ + return cts_sram_writew_retry(cts_dev, addr, w, 1, 0); +} + +static inline int cts_sram_writel(const struct cts_device *cts_dev, + u32 addr, u32 l) +{ + return cts_sram_writel_retry(cts_dev, addr, l, 1, 0); +} + +static inline int cts_sram_writesb(const struct cts_device *cts_dev, u32 addr, + const void *src, size_t len) +{ + return cts_sram_writesb_retry(cts_dev, addr, src, len, 1, 0); +} + +static inline int cts_sram_readb(const struct cts_device *cts_dev, + u32 addr, u8 *b) +{ + return cts_sram_readb_retry(cts_dev, addr, b, 1, 0); +} + +static inline int cts_sram_readw(const struct cts_device *cts_dev, + u32 addr, u16 *w) +{ + return cts_sram_readw_retry(cts_dev, addr, w, 1, 0); +} + +static inline int cts_sram_readl(const struct cts_device *cts_dev, + u32 addr, u32 *l) +{ + return cts_sram_readl_retry(cts_dev, addr, l, 1, 0); +} + +static inline int cts_sram_readsb(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len) +{ + return cts_sram_readsb_retry(cts_dev, addr, dst, len, 1, 0); +} + +#ifdef CONFIG_CTS_I2C_HOST +static inline void cts_set_program_addr(struct cts_device *cts_dev) +{ + cts_dev->rtdata.slave_addr = CTS_DEV_PROGRAM_MODE_I2CADDR; + cts_dev->rtdata.program_mode = true; + cts_dev->rtdata.addr_width = CTS_DEV_PROGRAM_MODE_ADDR_WIDTH; +} + +static inline void cts_set_normal_addr(struct cts_device *cts_dev) +{ + cts_dev->rtdata.slave_addr = CTS_DEV_NORMAL_MODE_I2CADDR; + cts_dev->rtdata.program_mode = false; + cts_dev->rtdata.addr_width = CTS_DEV_NORMAL_MODE_ADDR_WIDTH; +} +#else +static inline void cts_set_program_addr(struct cts_device *cts_dev) +{ + cts_dev->rtdata.slave_addr = CTS_DEV_PROGRAM_MODE_SPIADDR; + cts_dev->rtdata.program_mode = true; + cts_dev->rtdata.addr_width = CTS_DEV_PROGRAM_MODE_ADDR_WIDTH; +} + +static inline void cts_set_normal_addr(struct cts_device *cts_dev) +{ + cts_dev->rtdata.slave_addr = CTS_DEV_NORMAL_MODE_SPIADDR; + cts_dev->rtdata.program_mode = false; + cts_dev->rtdata.addr_width = CTS_DEV_NORMAL_MODE_ADDR_WIDTH; +} +#endif + +extern int cts_irq_handler(struct cts_device *cts_dev); +extern void cts_firmware_upgrade_work(struct work_struct *work); + +extern bool cts_is_device_suspended(const struct cts_device *cts_dev); +extern int cts_suspend_device(struct cts_device *cts_dev); +extern int cts_resume_device(struct cts_device *cts_dev); + +extern bool cts_is_device_program_mode(const struct cts_device *cts_dev); +extern int cts_enter_program_mode(struct cts_device *cts_dev); +extern int cts_enter_normal_mode(struct cts_device *cts_dev); + +extern int cts_probe_device(struct cts_device *cts_dev); +extern int cts_set_work_mode(const struct cts_device *cts_dev, u8 mode); +extern int cts_get_work_mode(const struct cts_device *cts_dev, u8 *mode); +extern int cts_get_firmware_version(const struct cts_device *cts_dev, + u16 *version); +extern int cts_get_ddi_version(const struct cts_device *cts_dev, u8 *version); +extern int cts_get_lib_version(const struct cts_device *cts_dev, + u16 *lib_version); +extern int cts_get_data_ready_flag(const struct cts_device *cts_dev, u8 *flag); +extern int cts_clr_data_ready_flag(const struct cts_device *cts_dev); +extern int cts_send_command(const struct cts_device *cts_dev, u8 cmd); +extern int cts_get_panel_param(const struct cts_device *cts_dev, + void *param, size_t size); +extern int cts_set_panel_param(const struct cts_device *cts_dev, + const void *param, size_t size); +extern int cts_get_x_resolution(const struct cts_device *cts_dev, + u16 *resolution); +extern int cts_get_y_resolution(const struct cts_device *cts_dev, + u16 *resolution); +extern int cts_get_num_rows(const struct cts_device *cts_dev, u8 *num_rows); +extern int cts_get_num_cols(const struct cts_device *cts_dev, u8 *num_cols); +extern int cts_enable_get_rawdata(const struct cts_device *cts_dev); +extern int cts_disable_get_rawdata(const struct cts_device *cts_dev); +extern int cts_polling_data(const struct cts_device *cts_dev, void *buf, + size_t size); +extern int cts_polling_test_data(const struct cts_device *cts_dev, void *buf, + size_t size); +extern int cts_get_rawdata(const struct cts_device *cts_dev, void *buf); +extern int cts_get_diffdata(const struct cts_device *cts_dev, void *buf); +extern int cts_get_basedata(const struct cts_device *cts_dev, void *buf); +extern int cts_get_compensate_cap(struct cts_device *cts_dev, u8 *cap); +extern int cts_get_fwid(struct cts_device *cts_dev, u16 *fwid); +extern int cts_get_hwid(struct cts_device *cts_dev, u32 *hwid); +extern int cts_read_sram_normal_mode(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay); + +#ifdef CFG_CTS_GESTURE +extern void cts_enable_gesture_wakeup(struct cts_device *cts_dev); +extern void cts_disable_gesture_wakeup(struct cts_device *cts_dev); +extern bool cts_is_gesture_wakeup_enabled(const struct cts_device *cts_dev); +extern int cts_get_gesture_info(const struct cts_device *cts_dev, void *gesture_info); +#endif /* CFG_CTS_GESTURE */ + +extern int cts_set_int_data_types(struct cts_device *cts_dev, u16 types); +extern int cts_set_int_data_method(struct cts_device *cts_dev, u8 method); + +#ifdef CONFIG_CTS_ESD_PROTECTION +extern void cts_init_esd_protection(struct chipone_ts_data *cts_data); +extern void cts_enable_esd_protection(struct chipone_ts_data *cts_data); +extern void cts_disable_esd_protection(struct chipone_ts_data *cts_data); +extern void cts_deinit_esd_protection(struct chipone_ts_data *cts_data); +#else /* CONFIG_CTS_ESD_PROTECTION */ +static inline void cts_init_esd_protection(struct chipone_ts_data *cts_data) +{ +} + +static inline void cts_enable_esd_protection(struct chipone_ts_data *cts_data) +{ +} + +static inline void cts_disable_esd_protection(struct chipone_ts_data *cts_data) +{ +} + +static inline void cts_deinit_esd_protection(struct chipone_ts_data *cts_data) +{ +} +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CONFIG_CTS_GLOVE +extern int cts_enter_glove_mode(struct cts_device *cts_dev); +extern int cts_exit_glove_mode(struct cts_device *cts_dev); +int cts_is_glove_enabled(const struct cts_device *cts_dev); +#else +static inline int cts_enter_glove_mode(struct cts_device *cts_dev) +{ + return 0; +} + +static inline int cts_exit_glove_mode(struct cts_device *cts_dev) +{ + return 0; +} + +static inline int cts_is_glove_enabled(const struct cts_device *cts_dev) +{ + return 0; +} +#endif + +#ifdef CONFIG_CTS_CHARGER_DETECT +extern bool cts_is_charger_exist(struct cts_device *cts_dev); +extern int cts_set_dev_charger_attached(struct cts_device *cts_dev, + bool attached); +#else /* CONFIG_CTS_CHARGER_DETECT */ +static inline bool cts_is_charger_exist(struct cts_device *cts_dev) +{ + return false; +} + +static inline int cts_set_dev_charger_attached(struct cts_device *cts_dev, + bool attached) +{ + return 0; +} +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_LEGACY_TOOL +extern int cts_tool_init(struct chipone_ts_data *cts_data); +extern void cts_tool_deinit(struct chipone_ts_data *data); +#else /* CONFIG_CTS_LEGACY_TOOL */ +static inline int cts_tool_init(struct chipone_ts_data *cts_data) +{ + return 0; +} + +static inline void cts_tool_deinit(struct chipone_ts_data *data) +{ +} +#endif /* CONFIG_CTS_LEGACY_TOOL */ + +extern bool cts_is_device_enabled(const struct cts_device *cts_dev); +extern int cts_start_device(struct cts_device *cts_dev); +extern int cts_stop_device(struct cts_device *cts_dev); + +#ifdef CFG_CTS_FW_LOG_REDIRECT +extern int cts_enable_fw_log_redirect(struct cts_device *cts_dev); +extern int cts_disable_fw_log_redirect(struct cts_device *cts_dev); +extern bool cts_is_fw_log_redirect(struct cts_device *cts_dev); +extern int cts_fw_log_show_finish(struct cts_device *cts_dev); +#endif + +#ifdef CFG_CTS_UPDATE_CRCCHECK +extern int cts_sram_writesb_boot_crc_retry(const struct cts_device *cts_dev, + size_t len, u32 crc, int retry); +#endif + +extern const char *cts_dev_boot_mode2str(u8 boot_mode); +extern bool cts_is_fwid_valid(u16 fwid); +extern void cts_deinit_rtdata(struct cts_device *cts_dev); + +extern int cts_reset_device(struct cts_device *cts_dev); +extern int cts_spi_xtrans(const struct cts_device *cts_dev, u8 *tx, size_t txlen, u8 *rx, size_t rxlen); +int touch_set_state(int state, int panel_idx); +int check_touch_state(int *state, int panel_idx); +#endif /* CTS_CORE_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_firmware.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_firmware.c new file mode 100644 index 000000000000..c132cdca6c8d --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_firmware.c @@ -0,0 +1,1162 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "Firmware" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sfctrl.h" +#include "cts_spi_flash.h" +#include "cts_firmware.h" +#include +#include +#include +#include + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE +#include "cts_builtin_firmware.h" +#define NUM_DRIVER_BUILTIN_FIRMWARE ARRAY_SIZE(cts_driver_builtin_firmwares) +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ + +#define CTS_FIRMWARE_MULTI_SECTION_FILE_SIZE (0x20000) +#define CTS_SECTION_ENABLE_FLAG (0x0000C35A) + +enum cts_firmware_section_offset { + CTS_FIRMWARE_SECTION_OFFSET = 0x00000000, + CTS_FIRMWARE_CRC_SECTION_OFFSET = 0x17000, + CTS_DDIPARAM_SECTION_OFFSET = 0x00019000, + CTS_DDIPARAM_CRC_SECTION_OFFSET = 0x1B000, + +}; + +struct cts_firmware_sect_info { + const u8 *firmware_sect; + size_t firmware_sect_size; + const u8 *firmware_crc_sect; + size_t firmware_crc_sect_size; + u32 firmware_sect_crc; + + const u8 *ddiparam_sect; + size_t ddiparam_sect_size; + const u8 *ddiparam_crc_sect; + size_t ddiparam_crc_sect_size; + u32 ddiparam_sect_crc; + +}; + +#define FIRMWARE_SECTION(firmware) \ + ((firmware)->data) +#define FIRMWARE_CRC_SECTION(firmware) \ + ((firmware)->data + CTS_FIRMWARE_CRC_SECTION_OFFSET) +#define DDIPARAM_SECTION(firmware) \ + ((firmware)->data + CTS_DDIPARAM_SECTION_OFFSET) +#define DDIPARAM_CRC_SECTION(firmware) \ + ((firmware)->data + CTS_DDIPARAM_CRC_SECTION_OFFSET) + +#define FIRMWARE_SECTION_CRC(firmware) \ + (get_unaligned_le32(FIRMWARE_CRC_SECTION(firmware))) +#define FIRMWARE_SECTION_SIZE(firmware) \ + (get_unaligned_le32(FIRMWARE_CRC_SECTION(firmware) + 4)) +#define FIRMWARE_SECTION_CRC_ENABLE(firmware) \ + (get_unaligned_le32(FIRMWARE_CRC_SECTION(firmware) + 8)) +#define FIRMWARE_CRC_SECTION_SIZE (12) + +#define DDIPARAM_SECTION_ENABLE(firmware) \ + (get_unaligned_le32(DDIPARAM_CRC_SECTION(firmware))) +#define DDIPARAM_SECTION_CRC_ENABLE(firmware) \ + (get_unaligned_le32(DDIPARAM_CRC_SECTION(firmware) + 4)) +#define DDIPARAM_SECTION_CRC(firmware) \ + (get_unaligned_le32(DDIPARAM_CRC_SECTION(firmware) + 8)) +#define DDIPARAM_SECTION_SIZE(firmware) \ + (get_unaligned_le32(DDIPARAM_CRC_SECTION(firmware) + 12)) +#define DDIPARAM_CRC_SECTION_SIZE (17) + +u16 cts_crc16(const u8 *data, size_t len) +{ + u16 crc = 0; + + const static u16 crc16_table[] = { + 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, + 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, + 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, + 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, + 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, + 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, + 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, + 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, + 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, + 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, + 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, + 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, + 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, + 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, + 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, + 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, + 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202, + }; + + while (len) { + crc = (crc << 8) ^ crc16_table[((crc >> 8) ^ *data) & 0xFF]; + data++; + len--; + } + + return crc; +} + +u32 cts_crc32(const u8 *data, size_t len) +{ + const static u32 crc32_table[] = { + 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, + 0x17C56B6B, + 0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, + 0x2B4BCB61, + 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, 0x4C11DB70, + 0x48D0C6C7, + 0x4593E01E, 0x4152FDA9, 0x5F15ADAC, 0x5BD4B01B, 0x569796C2, + 0x52568B75, + 0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, 0x791D4014, + 0x7DDC5DA3, + 0x709F7B7A, 0x745E66CD, 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, + 0x95609039, + 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58, + 0xBAEA46EF, + 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, + 0xA06C0B5D, + 0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, 0xC7361B4C, + 0xC3F706FB, + 0xCEB42022, 0xCA753D95, 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, + 0xFF79A6F1, + 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, 0x34867077, + 0x30476DC0, + 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5, + 0x2AC12072, + 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, 0x018AEB13, + 0x054BF6A4, + 0x0808D07D, 0x0CC9CDCA, 0x7897AB07, 0x7C56B6B0, 0x71159069, + 0x75D48DDE, + 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02, 0x5E9F46BF, + 0x5A5E5B08, + 0x571D7DD1, 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, + 0x40D816BA, + 0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B, + 0xBB60ADFC, + 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698, 0x832F1041, + 0x87EE0DF6, + 0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A, 0xE0B41DE7, + 0xE4750050, + 0xE9362689, 0xEDF73B3E, 0xF3B06B3B, 0xF771768C, 0xFA325055, + 0xFEF34DE2, + 0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, + 0xD1799B34, + 0xDC3ABDED, 0xD8FBA05A, 0x690CE0EE, 0x6DCDFD59, 0x608EDB80, + 0x644FC637, + 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, 0x4F040D56, + 0x4BC510E1, + 0x46863638, 0x42472B8F, 0x5C007B8A, 0x58C1663D, 0x558240E4, + 0x51435D53, + 0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, 0x36194D42, + 0x32D850F5, + 0x3F9B762C, 0x3B5A6B9B, 0x0315D626, 0x07D4CB91, 0x0A97ED48, + 0x0E56F0FF, + 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, 0xF12F560E, + 0xF5EE4BB9, + 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, + 0xEF68060B, + 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, 0xC423CD6A, + 0xC0E2D0DD, + 0xCDA1F604, 0xC960EBB3, 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, + 0xB07DABA7, + 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, + 0x9FF77D71, + 0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74, + 0x857130C3, + 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, 0x4E8EE645, + 0x4A4FFBF2, + 0x470CDD2B, 0x43CDC09C, 0x7B827D21, 0x7F436096, 0x7200464F, + 0x76C15BF8, + 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24, 0x119B4BE9, + 0x155A565E, + 0x18197087, 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, + 0x0FDC1BEC, + 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D, + 0x2056CD3A, + 0x2D15EBE3, 0x29D4F654, 0xC5A92679, 0xC1683BCE, 0xCC2B1D17, + 0xC8EA00A0, + 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C, 0xE3A1CBC1, + 0xE760D676, + 0xEA23F0AF, 0xEEE2ED18, 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, + 0xFDE69BC4, + 0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, + 0x9E7D9662, + 0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF, + 0xA2F33668, + 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4 + }; + + u32 crc = 0; + + while (len) { + crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ *data) & 0xFF]; + + data++; + len--; + } + + return crc; +} + +static int calc_crc_in_flash(const struct cts_device *cts_dev, + u32 flash_addr, size_t size, u32 *crc) +{ + return cts_dev->hwdata->sfctrl->ops->calc_flash_crc(cts_dev, + flash_addr, size, + crc); +} + +static bool is_multi_section_firmware(const struct cts_firmware *firmware) +{ + return (firmware->size == CTS_FIRMWARE_MULTI_SECTION_FILE_SIZE); +} + +static bool is_single_section_firmware(const struct cts_firmware *firmware) +{ + return !(is_multi_section_firmware(firmware)); +} + +static bool is_firmware_size_valid(const struct cts_firmware *firmware) +{ + return (firmware->size > 0x102 && + firmware->size <= CTS_FIRMWARE_MULTI_SECTION_FILE_SIZE); +} + +static bool is_multi_section_firmware_valid(const struct cts_firmware *firmware) +{ + u32 crc; + + crc = + cts_crc32(FIRMWARE_SECTION(firmware), + FIRMWARE_SECTION_SIZE(firmware)); + if (crc != FIRMWARE_SECTION_CRC(firmware)) { + cts_err("Firmware-section crc mismatch crc-section %08x != %08x" + ", File maybe broken!!!", crc, + FIRMWARE_SECTION_CRC(firmware)); + return false; + } + + if (DDIPARAM_SECTION_ENABLE(firmware) == CTS_SECTION_ENABLE_FLAG) { + crc = cts_crc32(DDIPARAM_SECTION(firmware), + DDIPARAM_SECTION_SIZE(firmware)); + if (crc != DDIPARAM_SECTION_CRC(firmware)) { + cts_err + ("DDIParam-section crc mismatch crc-section %08x != %08x" + ", File maybe broken!!!", crc, + DDIPARAM_SECTION_CRC(firmware)); + return false; + } + } else + cts_info("DDIParam-section is NOT enabled"); + + return true; +} + +static bool is_firmware_valid(const struct cts_firmware *firmware) +{ + if (firmware && firmware->data && is_firmware_size_valid(firmware)) { + if (is_single_section_firmware(firmware) || + is_multi_section_firmware_valid(firmware)) + return true; + } + + return false; +} + +static void parse_single_section_firmware(const struct cts_firmware *firmware, + struct cts_firmware_sect_info *info) +{ + static u8 crc_sect[12]; + + info->firmware_sect = firmware->data; + info->firmware_sect_size = firmware->size; + info->firmware_sect_crc = cts_crc32(firmware->data, firmware->size); + + put_unaligned_le32(info->firmware_sect_crc, crc_sect); + put_unaligned_le32(info->firmware_sect_size, crc_sect + 4); + put_unaligned_le32(~0x0000C35A, crc_sect + 8); /* Enable CRC check */ + info->firmware_crc_sect = crc_sect; + info->firmware_crc_sect_size = sizeof(crc_sect); +} + +static void parse_multi_section_firmware(const struct cts_firmware *firmware, + struct cts_firmware_sect_info *info) +{ + info->firmware_sect = FIRMWARE_SECTION(firmware); + info->firmware_sect_size = FIRMWARE_SECTION_SIZE(firmware); + info->firmware_crc_sect = FIRMWARE_CRC_SECTION(firmware); + info->firmware_crc_sect_size = FIRMWARE_CRC_SECTION_SIZE; + info->firmware_sect_crc = FIRMWARE_SECTION_CRC(firmware); + + if (DDIPARAM_SECTION_ENABLE(firmware) == CTS_SECTION_ENABLE_FLAG) { + info->ddiparam_sect = DDIPARAM_SECTION(firmware); + info->ddiparam_sect_size = DDIPARAM_SECTION_SIZE(firmware); + info->ddiparam_crc_sect = DDIPARAM_CRC_SECTION(firmware); + info->ddiparam_crc_sect_size = DDIPARAM_CRC_SECTION_SIZE; + info->ddiparam_sect_crc = DDIPARAM_SECTION_CRC(firmware); + } +} + +static int parse_firmware(const struct cts_firmware *firmware, + struct cts_firmware_sect_info *info) +{ + memset(info, 0, sizeof(*info)); + + if (is_multi_section_firmware(firmware)) + parse_multi_section_firmware(firmware, info); + else + parse_single_section_firmware(firmware, info); + + cts_info(" Firmware section size: %zu", info->firmware_sect_size); + if (info->ddiparam_crc_sect) + cts_info(" DDIParam section size: %zu", + info->ddiparam_sect_size); + + return 0; +} + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE +#ifdef CONFIG_CTS_SYSFS +int cts_get_num_driver_builtin_firmware(void) +{ + return NUM_DRIVER_BUILTIN_FIRMWARE; +} + +const struct cts_firmware *cts_request_driver_builtin_firmware_by_name(const + char + *name) +{ + const struct cts_firmware *firmware; + int i; + + cts_info("Request driver builtin by name '%s'", name); + + firmware = cts_driver_builtin_firmwares; + for (i = 0; i < NUM_DRIVER_BUILTIN_FIRMWARE; i++, firmware++) { + if (strcmp(firmware->name, name) == 0) { + if (is_firmware_valid(firmware)) { + cts_info("Found driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu ver: %04x", + firmware->name, firmware->hwid, + firmware->fwid, firmware->size, + FIRMWARE_VERSION(firmware)); + return firmware; + } + + cts_warn("Found driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu invalid", + firmware->name, firmware->hwid, firmware->hwid, + firmware->size); + } + } + + return NULL; +} + +const struct cts_firmware *cts_request_driver_builtin_firmware_by_index(u32 + index) +{ + const struct cts_firmware *firmware; + + cts_info("Request driver builtin by index %u", index); + + if (index < NUM_DRIVER_BUILTIN_FIRMWARE) { + firmware = cts_driver_builtin_firmwares + index; + if (is_firmware_valid(firmware)) { + cts_info("Found driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu ver: %04x", + firmware->name, firmware->hwid, firmware->fwid, + firmware->size, FIRMWARE_VERSION(firmware)); + return firmware; + } + cts_warn("Found driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu INVALID", + firmware->name, firmware->hwid, firmware->hwid, + firmware->size); + } else + cts_warn("Request driver builtin by index %u too large >= %zu", + index, NUM_DRIVER_BUILTIN_FIRMWARE); + + return NULL; +} +#endif /* CONFIG_CTS_SYSFS */ + +static const struct cts_firmware *cts_request_newer_driver_builtin_firmware(u32 + hwid, + u16 + fwid, + u16 + device_fw_ver) +{ +#define MATCH_HWID(firmware, hwid) \ + ((hwid) == CTS_DEV_HWID_ANY || (firmware)->hwid == (hwid)) +#define MATCH_FWID(firmware, fwid) \ + ((fwid) == CTS_DEV_FWID_ANY || (firmware)->fwid == (fwid)) + + const struct cts_firmware *firmware = NULL; + int i; + + cts_info + ("Request driver builtin if match hwid: %06x fwid: %04x && ver > %04x", + hwid, fwid, device_fw_ver); + + firmware = cts_driver_builtin_firmwares; + for (i = 0; i < ARRAY_SIZE(cts_driver_builtin_firmwares); + i++, firmware++) { + if (MATCH_HWID(firmware, hwid) && MATCH_FWID(firmware, fwid)) { + if (!is_firmware_valid(firmware)) { + cts_err("Found driver builtin '%s' " + "hwid: %06x fwid: %04x data: %p size: %zu INVALID", + firmware->name, firmware->hwid, + firmware->fwid, firmware->data, + firmware->size); + continue; + } + + cts_info("Found matched driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu ver: %04x", + firmware->name, firmware->hwid, firmware->fwid, + firmware->size, FIRMWARE_VERSION(firmware)); + + /* if(FIRMWARE_VERSION(firmware) > device_fw_ver) { */ + if (1) { + cts_info("Found newer driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu ver: %04x > %04x", + firmware->name, firmware->hwid, + firmware->fwid, firmware->size, + FIRMWARE_VERSION(firmware), + device_fw_ver); + return firmware; + } + } + } + + cts_info("No newer driver builtin found"); + + return NULL; + +#undef MATCH_HWID +#undef MATCH_FWID +} +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ + +#ifdef CFG_CTS_FIRMWARE_IN_FS +bool is_filesystem_mounted(const char *filepath) +{ + struct path root_path; + struct path path; + int ret; + + ret = kern_path("/", LOOKUP_FOLLOW, &root_path); + if (ret) + return false; + + ret = kern_path(filepath, LOOKUP_FOLLOW, &path); + if (ret) + goto err_put_root_path; + + if (path.mnt->mnt_sb == root_path.mnt->mnt_sb) + /* not mounted */ + ret = false; + else + ret = true; + + path_put(&path); +err_put_root_path: + path_put(&root_path); + + return !!ret; +} + +static int cts_request_firmware_full_filepath(struct cts_firmware *firmware, + const char *filepath, + int curr_version) +{ + struct file *file; + u16 version; + u8 buff[2]; + loff_t pos = 0; + int read_size; + int ret = 0; + + file = filp_open(filepath, O_RDONLY, 0); + if (IS_ERR(file)) { + cts_err("Open file '%s' failed %ld", filepath, PTR_ERR(file)); + return PTR_ERR(file); + } + + firmware->size = file_inode(file)->i_size; + if (!is_firmware_size_valid(firmware)) { + cts_info("File '%s' size: %zu invalid", filepath, + firmware->size); + ret = -EINVAL; + goto err_close_file; + } + + pos = FIRMWARE_VERSION_OFFSET; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + read_size = kernel_read(file, buff, 2, &pos); +#else + read_size = kernel_read(file, pos, buff, 2); +#endif + if (read_size < 0) { + cts_err("Read version from offset 0x100 failed"); + ret = -EIO; + goto err_close_file; + } + version = get_unaligned_le16(buff); + + if (version <= curr_version) { + cts_info("File '%s' size: %zu version: %04x <= %04x", + filepath, firmware->size, version, curr_version); + ret = -EINVAL; + goto err_close_file; + } + + cts_info("File '%s' size: %zu version: %04x", + filepath, firmware->size, version); + + firmware->data = (u8 *) vmalloc(firmware->size); + if (firmware->data == NULL) { + cts_err("Request form fs alloc firmware data failed"); + ret = -ENOMEM; + goto err_close_file; + } + + pos = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + read_size = kernel_read(file, firmware->data, firmware->size, &pos); +#else + read_size = kernel_read(file, pos, firmware->data, firmware->size); +#endif + if (read_size < 0 || read_size != firmware->size) { + cts_err("Request from fs read whole file failed %d", read_size); + ret = -EIO; + goto err_free_firmware_data; + } + + { + int r = filp_close(file, NULL); + + if (r) + cts_warn("Close file '%s' failed %d", filepath, r); + } + + return 0; + +err_free_firmware_data: + vfree(firmware->data); + firmware->data = NULL; + +err_close_file:{ + int r = filp_close(file, NULL); + + if (r) + cts_warn("Close file '%s' failed %d", filepath, r); + } + + return ret; +} + +static int cts_wrap_request_firmware(struct cts_firmware *firmware, + const char *name, struct device *device, + int curr_version) +{ + int ret; + + ret = request_firmware(&firmware->fw, name, device); + if (ret) { + cts_err("Could not load firmware from %s: %d", name, ret); + return ret; + } + + /* Map firmware structure to cts_firmware */ + firmware->data = (u8 *) firmware->fw->data; + firmware->size = firmware->fw->size; + + /* Check firmware */ + if (!is_firmware_valid(firmware)) { + cts_err("Request firmware file INVALID, size %zu", + firmware->size); + ret = -EINVAL; + goto err_release_firmware; + } + + /* Check firmware version */ + if (curr_version > 0 && curr_version >= FIRMWARE_VERSION(firmware)) { + cts_info("Request firmware file with version 0x%04x < 0x%04x", + FIRMWARE_VERSION(firmware), curr_version); + ret = -EINVAL; + goto err_release_firmware; + } + + return 0; + +err_release_firmware: + release_firmware(firmware->fw); + + return ret; +} + +const struct cts_firmware *cts_request_newer_firmware_from_fs(const struct + cts_device + *cts_dev, + const char + *filepath, + u16 curr_version) +{ + struct cts_firmware *firmware = NULL; + int ret = 0; + + cts_info("Request from file '%s' if version > %04x", + filepath, curr_version); + + if (filepath == NULL || filepath[0] == '\0') { + cts_err("Request from file path INVALID %p", filepath); + return NULL; + } + + firmware = + (struct cts_firmware *)kzalloc(sizeof(*firmware), GFP_KERNEL); + if (firmware == NULL) { + cts_err("Request from file alloc struct cts_firmware failed"); + return NULL; + } + + if (strchr(filepath, '/') != NULL) { /* Full path */ + cts_info("Filepath is fullpath, direct read it out"); + ret = cts_request_firmware_full_filepath(firmware, + filepath, + curr_version); + } else { /* ONLY file name is givin */ + cts_info("Filepath is only filename, use request_firmware()"); + ret = cts_wrap_request_firmware(firmware, filepath, + &cts_dev->pdata->ts_input_dev->dev, + curr_version); + } + + if (ret) { + cts_err("Request from file '%s' failed %d", filepath, ret); + kfree(firmware); + return NULL; + } else { + return firmware; + } +} + +const struct cts_firmware *cts_request_firmware_from_fs(const struct cts_device + *cts_dev, + const char *filepath) +{ + cts_info("Request from file '%s'", filepath); + + return cts_request_newer_firmware_from_fs(cts_dev, filepath, 0); +} + +int cts_update_firmware_from_file(struct cts_device *cts_dev, + const char *filepath, bool to_flash) +{ + const struct cts_firmware *firmware; + int ret; + + cts_info("Update from file '%s' to %s", filepath, + to_flash ? "flash" : "sram"); + + firmware = cts_request_firmware_from_fs(cts_dev, filepath); + if (firmware == NULL) { + cts_err("Request from file '%s' failed", filepath); + return -EFAULT; + } + + ret = cts_update_firmware(cts_dev, firmware, to_flash); + if (ret) { + cts_err("Update to %s from file failed %d", + to_flash ? "flash" : "sram", ret); + goto err_release_firmware; + } + + cts_info("Update from file success"); + +err_release_firmware: + cts_release_firmware(firmware); + + return ret; +} +#endif /*CFG_CTS_FIRMWARE_IN_FS */ + +const struct cts_firmware *cts_request_firmware(const struct cts_device + *cts_dev, u32 hwid, u16 fwid, + u16 curr_firmware_ver) +{ + const struct cts_firmware *firmware_builtin = NULL; + const struct cts_firmware *firmware_from_file = NULL; + + if (hwid == CTS_DEV_HWID_INVALID) + hwid = CTS_DEV_HWID_ANY; + if (fwid == CTS_DEV_FWID_INVALID) + fwid = CTS_DEV_FWID_ANY; + + cts_info("Request newer if match hwid: %06x fwid: %04x && ver > %04x", + hwid, fwid, curr_firmware_ver); + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE + firmware_builtin = + cts_request_newer_driver_builtin_firmware(hwid, fwid, + curr_firmware_ver); +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ + +#ifdef CFG_CTS_FIRMWARE_IN_FS + /* Check firmware in file system when probe only when build to .ko */ + if (hwid == CTS_DEV_HWID_ICNL9911C) { + if (is_filesystem_mounted(CFG_CTS_FIRMWARE_FILEPATH_9911)) { + firmware_from_file = + cts_request_newer_firmware_from_fs(cts_dev, +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD + cts_dev->config_fw_name[0] + ? cts_dev->config_fw_name : + CFG_CTS_FIRMWARE_FILENAME_9911, +#else /* CFG_CTS_FW_UPDATE_FILE_LOAD */ + CFG_CTS_FIRMWARE_FILENAME_9911, +#endif /* CFG_CTS_FW_UPDATE_FILE_LOAD */ + firmware_builtin + ? + FIRMWARE_VERSION(firmware_builtin) + : + curr_firmware_ver); + } + } else if (hwid == CTS_DEV_HWID_ICNL9916) { + if (is_filesystem_mounted(CFG_CTS_FIRMWARE_FILEPATH_9916)) { + firmware_from_file = + cts_request_newer_firmware_from_fs(cts_dev, +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD + cts_dev->config_fw_name[0] + ? cts_dev->config_fw_name : + CFG_CTS_FIRMWARE_FILENAME_9916, +#else /* CFG_CTS_FW_UPDATE_FILE_LOAD */ + CFG_CTS_FIRMWARE_FILENAME_9916, +#endif /* CFG_CTS_FW_UPDATE_FILE_LOAD */ + firmware_builtin + ? + FIRMWARE_VERSION(firmware_builtin) + : + curr_firmware_ver); + } + } else if (hwid == CTS_DEV_HWID_ICNL9922) { + if (is_filesystem_mounted(CFG_CTS_FIRMWARE_FILEPATH_9922)) { + firmware_from_file = + cts_request_newer_firmware_from_fs(cts_dev, +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD + cts_dev->config_fw_name[0] + ? cts_dev->config_fw_name : + CFG_CTS_FIRMWARE_FILENAME_9922, +#else /* CFG_CTS_FW_UPDATE_FILE_LOAD */ + CFG_CTS_FIRMWARE_FILENAME_9922, +#endif /* CFG_CTS_FW_UPDATE_FILE_LOAD */ + firmware_builtin + ? + FIRMWARE_VERSION(firmware_builtin) + : + curr_firmware_ver); + } + } +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + + return firmware_from_file ? firmware_from_file : firmware_builtin; +} + +void cts_release_firmware(const struct cts_firmware *firmware) +{ + cts_info("Release firmware %p", firmware); + + if (firmware) { + if (firmware->fw) { + /* Use request_firmware() get struct firmware * */ + cts_info("Release firmware from request_firmware"); + release_firmware(firmware->fw); + kfree(firmware); + } else if (firmware->name == NULL) { + /* Direct read from file */ + cts_info("Release firmware from direct-load"); + vfree(firmware->data); + kfree(firmware); + } else + /* Builtin firmware with non-NULL name, no need to free */ + cts_info("Release firmware from driver built-in"); + } +} + +static int validate_flash_data(const struct cts_device *cts_dev, + u32 flash_addr, const u8 *data, size_t size, + u8 *buf, bool calc_crc, u32 crc) +{ + int ret, i; + bool free_data = false; + u32 crc_flash; + + cts_info("Validate flash data from 0x%06x size %zu by %s", + flash_addr, size, calc_crc ? "check-crc" : "direct-readback"); + + if (calc_crc) { + ret = calc_crc_in_flash(cts_dev, flash_addr, size, &crc_flash); + if (ret) { + cts_err + ("Calc data in flash from 0x%06x size %zu crc failed %d, " + "try to validate by direct readback", flash_addr, + size, ret); + /* FALL through by direct compare data read back */ + } else { + if (crc_flash != crc) { + cts_err + ("Crc in flash from 0x%06x size %zu mismatch 0x%08x != 0x%08x", + flash_addr, size, crc_flash, crc); + /* FALL through by direct compare data read back */ + } else { + cts_info("Flash data crc correct"); + return 0; + } + } + } + + if (buf == NULL) { + buf = (u8 *) kmalloc(size, GFP_KERNEL); + if (buf == NULL) { + cts_err("Validate flash data allocate mem failed"); + return -ENOMEM; + } + + free_data = true; + } + + ret = cts_read_flash(cts_dev, flash_addr, buf, size); + if (ret) { + cts_err("Read flash from 0x%06x size %zu failed %d", flash_addr, + size, ret); + goto err_free_buf; + } + + for (i = 0; i < size; i++) { + if (buf[i] != data[i]) { + if (ret == 0) { + cts_err + ("Flash data from 0x%06x size %zu first bytes diff:", + flash_addr, size); + } + + if (ret < 100) { + cts_err(" 0x%06x: %02x %02x", i, buf[i], + data[i]); + } else if (ret == 100) { + cts_err(" ..."); + } + ret++; + } + } + +err_free_buf: + if (free_data) + kfree(buf); + + return ret; +} + +static int cts_program_firmware(const struct cts_device *cts_dev, + const struct cts_firmware_sect_info + *firmware_info) +{ + int ret; + u8 crc_sect_buf[FIRMWARE_CRC_SECTION_SIZE]; + + cts_info("Program firmware size %zu", + firmware_info->firmware_sect_size); + + /* Write firmware to SRAM for easy enter normal mode after update */ + cts_info("Write firmware section to sram"); + ret = cts_sram_writesb_check_crc_retry(cts_dev, 0, + firmware_info->firmware_sect, + firmware_info->firmware_sect_size, + firmware_info->firmware_sect_crc, + 3); + if (ret) { + cts_err("Write firmware section to sram failed %d", ret); + return ret; + } + + ret = cts_program_flash_from_sram(cts_dev, + 4, 4, + firmware_info->firmware_sect_size - + 4); + if (ret) { + cts_err("Program firmware section from sram failed %d", ret); + return ret; + } + + ret = cts_program_flash(cts_dev, + CTS_FIRMWARE_CRC_SECTION_OFFSET, + firmware_info->firmware_crc_sect, + firmware_info->firmware_crc_sect_size); + if (ret) { + cts_err("Program firmware crc section failed %d", ret); + return ret; + } + + ret = validate_flash_data(cts_dev, + CTS_FIRMWARE_CRC_SECTION_OFFSET, + firmware_info->firmware_crc_sect, + firmware_info->firmware_crc_sect_size, + crc_sect_buf, false, 0); + if (ret) { + cts_err("Validate Firmware-CRC section failed %d", ret); + return ret; + } + + ret = cts_program_flash(cts_dev, 0, firmware_info->firmware_sect, 4); + if (ret) { + cts_err("Program firmware section fist 4bytes failed %d", ret); + return ret; + } + + ret = validate_flash_data(cts_dev, + CTS_FIRMWARE_SECTION_OFFSET, + firmware_info->firmware_sect, + firmware_info->firmware_sect_size, NULL, true, + firmware_info->firmware_sect_crc); + if (ret) { + cts_err("Validate firmware section failed %d", ret); + return ret; + } + + return 0; +} + +static int cts_program_ddiparam(const struct cts_device *cts_dev, + const struct cts_firmware_sect_info + *firmware_info) +{ + int ret; + u8 crc_sect_buf[DDIPARAM_CRC_SECTION_SIZE]; + + cts_info("Program DDIParam size: %zu", + firmware_info->ddiparam_sect_size); + + ret = cts_program_flash(cts_dev, CTS_DDIPARAM_SECTION_OFFSET, + firmware_info->ddiparam_sect, + firmware_info->ddiparam_sect_size); + if (ret) { + cts_err("Program DDIParam section failed %d", ret); + return ret; + } + + ret = validate_flash_data(cts_dev, + CTS_DDIPARAM_SECTION_OFFSET, + firmware_info->ddiparam_sect, + firmware_info->ddiparam_sect_size, NULL, true, + firmware_info->ddiparam_sect_crc); + if (ret) { + cts_err("Validate DDIParam section failed %d", ret); + return ret; + } + + ret = cts_program_flash(cts_dev, CTS_DDIPARAM_CRC_SECTION_OFFSET, + firmware_info->ddiparam_crc_sect, + firmware_info->ddiparam_crc_sect_size); + if (ret) { + cts_err("Program DDIParam-CRC section failed %d", ret); + return ret; + } + + ret = validate_flash_data(cts_dev, + CTS_DDIPARAM_CRC_SECTION_OFFSET, + firmware_info->ddiparam_crc_sect, + firmware_info->ddiparam_crc_sect_size, + crc_sect_buf, false, 0); + if (ret) { + cts_err("Validate DDIParam-CRC section failed %d", ret); + return ret; + } + + return 0; +} + +int cts_update_firmware(struct cts_device *cts_dev, + const struct cts_firmware *firmware, bool to_flash) +{ + struct cts_firmware_sect_info firmware_info; + int ret, retries; + + to_flash = false; + cts_info("Update firmware to %s ver: %04x size: %zu", + to_flash ? "flash" : "sram", + FIRMWARE_VERSION(firmware), firmware->size); + + if (parse_firmware(firmware, &firmware_info)) { + cts_err("Parse firmware failed"); + return -EINVAL; + } + + cts_dev->rtdata.updating = true; + + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Device enter program mode failed %d", ret); + goto out; + } + + if (!to_flash || !cts_dev->rtdata.has_flash) { + cts_info("Write firmware section to sram size %zu", + firmware_info.firmware_sect_size); + ret = cts_sram_writesb_check_crc_retry(cts_dev, + 0, + firmware_info.firmware_sect, + firmware_info.firmware_sect_size, + firmware_info.firmware_sect_crc, 3); + if (ret) + cts_err("Write firmware section to sram failed %d", + ret); +#ifdef CFG_CTS_UPDATE_CRCCHECK + if (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911S || + cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911C || + cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9916 || + cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9922) + cts_sram_writesb_boot_crc_retry(cts_dev, + firmware_info.firmware_sect_size, + firmware_info.firmware_sect_crc, + 3); +#endif + goto out; + } + + ret = cts_prepare_flash_operation(cts_dev); + if (ret) + cts_warn("Prepare flash operation failed %d", ret); + /* Go through and try */ + + retries = 0; + do { + retries++; + + ret = cts_erase_flash(cts_dev, CTS_FIRMWARE_CRC_SECTION_OFFSET, + firmware_info.firmware_crc_sect_size); + if (ret) { + cts_err + ("Erase firmware crc section failed %d retries %d", + ret, retries); + continue; + } + + ret = + cts_erase_flash(cts_dev, 0, + firmware_info.firmware_sect_size); + if (ret) { + cts_err("Erase firmware section failed %d retries %d", + ret, retries); + continue; + } + + ret = cts_program_firmware(cts_dev, &firmware_info); + if (ret) { + cts_err + ("Program firmware & crc section failed %d retries %d", + ret, retries); + } + } while (ret && retries < 3); + + if (ret == 0 && firmware_info.ddiparam_sect_size != 0) { + retries = 0; + do { + retries++; + + ret = + cts_erase_flash(cts_dev, + CTS_DDIPARAM_CRC_SECTION_OFFSET, + firmware_info.ddiparam_crc_sect_size); + if (ret) { + cts_err + ("Erase DDIParam crc secction failed %d, retries %d", + ret, retries); + continue; + } + + ret = + cts_erase_flash(cts_dev, + CTS_DDIPARAM_SECTION_OFFSET, + firmware_info.ddiparam_sect_size); + if (ret) { + cts_err + ("Erase DDIParam section failed %d, retries %d", + ret, retries); + continue; + } + + ret = cts_program_ddiparam(cts_dev, &firmware_info); + if (ret) { + cts_err + ("Program DDIParam & crc section failed %d retries %d", + ret, retries); + } + } while (ret && retries < 3); + } + + cts_post_flash_operation(cts_dev); + +out: + cts_dev->rtdata.updating = false; + + if (ret == 0) { + if (firmware_info.firmware_sect_size <= + cts_dev->hwdata->sfctrl->xchg_sram_base) { + ret = cts_enter_normal_mode(cts_dev); + if (ret) { + cts_err("Enter normal mode failed %d", ret); + /* return ret; */ + } + } + } + +#ifdef CONFIG_CTS_CHARGER_DETECT + if (cts_is_charger_exist(cts_dev)) { + int r = cts_set_dev_charger_attached(cts_dev, true); + + if (r) + cts_err("Set dev charger attached failed %d", r); + } +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) + cts_enter_glove_mode(cts_dev); +#endif + +#ifdef CFG_CTS_FW_LOG_REDIRECT + if (cts_is_fw_log_redirect(cts_dev)) + cts_enable_fw_log_redirect(cts_dev); +#endif + + return ret; +} + +bool cts_is_firmware_updating(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.updating; +} diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_firmware.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_firmware.h new file mode 100644 index 000000000000..b8eec9a170a1 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_firmware.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_FIRMWARE_H +#define CTS_FIRMWARE_H + +#include "cts_config.h" +#include +#include + +struct cts_firmware { + const char *name; /* MUST set to non-NULL if driver builtin firmware */ + u32 hwid; + u16 fwid; + + u8 *data; + size_t size; + const struct firmware *fw; +}; + +#define FIRMWARE_VERSION_OFFSET 0x100 +#define FIRMWARE_VERSION(firmware) \ + get_unaligned_le16((firmware)->data + FIRMWARE_VERSION_OFFSET) + +struct cts_device; + +extern const struct cts_firmware *cts_request_firmware(const struct cts_device + *cts_dev, u32 hwid, + u16 fwid, + u16 device_fw_ver); +extern void cts_release_firmware(const struct cts_firmware *firmware); + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE +extern int cts_get_num_driver_builtin_firmware(void); +extern const struct cts_firmware +*cts_request_driver_builtin_firmware_by_name(const char *name); +extern const struct cts_firmware +*cts_request_driver_builtin_firmware_by_index(u32 index); +#else /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ +static inline int cts_get_num_driver_builtin_firmware(void) +{ + return 0; +} + +static inline const struct cts_firmware +*cts_request_driver_builtin_firmware_by_name(const char *name) +{ + return NULL; +} + +static inline const struct cts_firmware +*cts_request_driver_builtin_firmware_by_index(u32 index) +{ + return NULL; +} +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ +extern bool cts_is_firmware_updating(const struct cts_device *cts_dev); + +extern int cts_update_firmware(struct cts_device *cts_dev, + const struct cts_firmware *firmware, + bool to_flash); + +#ifdef CFG_CTS_FIRMWARE_IN_FS +extern const struct cts_firmware *cts_request_firmware_from_fs(const struct + cts_device + *cts_dev, + const char + *filepath); +extern int cts_update_firmware_from_file(struct cts_device *cts_dev, + const char *filepath, bool to_flash); + +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + +extern u16 cts_crc16(const u8 *data, size_t len); +extern u32 cts_crc32(const u8 *data, size_t len); + +#endif /* CTS_FIRMWARE_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_hostcomm.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_hostcomm.c new file mode 100644 index 000000000000..fd361da9921f --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_hostcomm.c @@ -0,0 +1,552 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "cts_config.h" +#include "cts_firmware.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_hostcomm.h" + +extern bool set_short_test_type(const struct cts_device *cts_dev, u8 type); +extern int disable_fw_monitor_mode(const struct cts_device *cts_dev); +extern int disable_fw_auto_compensate(const struct cts_device *cts_dev); +extern int disable_fw_esd_protection(const struct cts_device *cts_dev); +extern int set_fw_work_mode(const struct cts_device *cts_dev, u8 mode); +extern int set_display_state(const struct cts_device *cts_dev, bool active); + +int cts_hostcomm_get_fw_ver(const struct cts_device *cts_dev, u16 *fwver) +{ + return cts_get_firmware_version(cts_dev, fwver); +} + +int cts_hostcomm_get_lib_ver(const struct cts_device *cts_dev, u16 *libver) +{ + return cts_get_lib_version(cts_dev, libver); +} + +int cts_hostcomm_get_ddi_ver(const struct cts_device *cts_dev, u8 *ddiver) +{ + return cts_get_ddi_version(cts_dev, ddiver); +} + +int cts_hostcomm_get_res_x(const struct cts_device *cts_dev, u16 *res_x) +{ + return cts_get_x_resolution(cts_dev, res_x); +} + +int cts_hostcomm_get_res_y(const struct cts_device *cts_dev, u16 *res_y) +{ + return cts_get_y_resolution(cts_dev, res_y); +} + +int cts_hostcomm_get_rows(const struct cts_device *cts_dev, u8 *rows) +{ + return cts_get_num_rows(cts_dev, rows); +} + +int cts_hostcomm_get_cols(const struct cts_device *cts_dev, u8 *cols) +{ + return cts_get_num_cols(cts_dev, cols); +} + +int cts_hostcomm_get_flip_x(const struct cts_device *cts_dev, bool *flip_x) +{ + u8 val; + int ret; + + ret = cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_FLAG_BITS, &val); + if (ret < 0) + return ret; + + *flip_x = !!(val & BIT(2)); + return 0; +} + +int cts_hostcomm_get_flip_y(const struct cts_device *cts_dev, bool *flip_y) +{ + u8 val; + int ret; + + ret = cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_FLAG_BITS, &val); + if (ret < 0) + return ret; + + *flip_y = !!(val & BIT(3)); + return 0; +} + +int cts_hostcomm_get_swap_axes(const struct cts_device *cts_dev, + bool *swap_axes) +{ + u8 val; + int ret; + + ret = cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_SWAP_AXES, &val); + if (ret < 0) + return ret; + + *swap_axes = !!val; + return 0; +} + +int cts_hostcomm_get_int_mode(const struct cts_device *cts_dev, u8 *int_mode) +{ + return cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_INT_MODE, int_mode); +} + +int cts_hostcomm_get_int_keep_time(const struct cts_device *cts_dev, + u16 *int_keep_time) +{ + return cts_fw_reg_readw(cts_dev, CTS_DEVICE_FW_REG_INT_KEEP_TIME, + int_keep_time); +} + +int cts_hostcomm_get_esd_method(const struct cts_device *cts_dev, + u8 *esd_method) +{ + return cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_ESD_PROTECTION, + esd_method); +} + +int cts_hostcomm_get_esd_protection(const struct cts_device *cts_dev, + u8 *esd_protection) +{ + return cts_fw_reg_readb(cts_dev, 0x8000 + 342, esd_protection); +} + +int cts_hostcomm_get_data_ready_flag(const struct cts_device *cts_dev, + u8 *ready) +{ + return cts_get_data_ready_flag(cts_dev, ready); +} + +int cts_hostcomm_clr_data_ready_flag(const struct cts_device *cts_dev) +{ + return cts_clr_data_ready_flag(cts_dev); +} + +int cts_hostcomm_enable_get_rawdata(const struct cts_device *cts_dev) +{ + return cts_enable_get_rawdata(cts_dev); +} + +int cts_hostcomm_disable_get_rawdata(const struct cts_device *cts_dev) +{ + return cts_disable_get_rawdata(cts_dev); +} + +int cts_hostcomm_enable_get_cneg(const struct cts_device *cts_dev) +{ + return cts_send_command(cts_dev, CTS_CMD_ENABLE_READ_CNEG); +} + +int cts_hostcomm_disable_get_cneg(const struct cts_device *cts_dev) +{ + return cts_send_command(cts_dev, CTS_CMD_DISABLE_READ_CNEG); +} + +int cts_hostcomm_is_cneg_ready(const struct cts_device *cts_dev, u8 *ready) +{ + return cts_fw_reg_readb(cts_dev, + CTS_DEVICE_FW_REG_COMPENSATE_CAP_READY, ready); +} + +int cts_hostcomm_quit_guesture_mode(const struct cts_device *cts_dev) +{ + //TODO: + return 0; +} + +int cts_hostcomm_get_rawdata(const struct cts_device *cts_dev, u8 *buf) +{ + return cts_fw_reg_readsb_delay_idle(cts_dev, + CTS_DEVICE_FW_REG_RAW_DATA, + buf, + cts_dev->fwdata.rows * + cts_dev->fwdata.cols * 2, 500); +} + +int cts_hostcomm_get_diffdata(const struct cts_device *cts_dev, u8 *buf) +{ + return cts_fw_reg_readsb_delay_idle(cts_dev, + CTS_DEVICE_FW_REG_DIFF_DATA, + buf, + (cts_dev->fwdata.rows + 2) * + (cts_dev->fwdata.cols + 2) * 2, + 500); +} + +int cts_hostcomm_get_basedata(const struct cts_device *cts_dev, u8 *buf) +{ + //TODO: + return 0; +} + +int cts_hostcomm_get_cneg(const struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + return cts_fw_reg_readsb_delay_idle(cts_dev, + CTS_DEVICE_FW_REG_COMPENSATE_CAP, + buf, + cts_dev->hwdata->num_row * + cts_dev->hwdata->num_col, 500); +} + +int cts_hostcomm_read_hw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + + return cts_hw_reg_readsb_retry(cts_dev, addr, regbuf, size, 3, 100); +} + +int cts_hostcomm_write_hw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + return cts_hw_reg_writesb_retry(cts_dev, addr, regbuf, size, 3, 100); +} + +int cts_hostcomm_read_ddi_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + int ret = -1; + + cts_err("%s: BUG! Should not be here!", __func__); + return ret; +} + +int cts_hostcomm_write_ddi_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + int ret = -1; + + cts_err("%s: BUG! Should not be here!", __func__); + return ret; +} + +int cts_hostcomm_read_reg(const struct cts_device *cts_dev, uint16_t cmd, + u8 *rbuf, size_t rlen) +{ + int ret = -1; + + cts_err("%s: BUG! Should not be here!", __func__); + return ret; +} + +int cts_hostcomm_write_reg(const struct cts_device *cts_dev, uint16_t cmd, + u8 *wbuf, size_t wlen) +{ + int ret = -1; + + cts_err("%s: BUG! Should not be here!", __func__); + return ret; +} + +int cts_hostcomm_read_fw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + return cts_fw_reg_readsb(cts_dev, addr, regbuf, size); +} + +int cts_hostcomm_write_fw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + return cts_fw_reg_writesb(cts_dev, addr, regbuf, size); +} + +int cts_hostcomm_get_touchinfo(struct cts_device *cts_dev, + struct cts_device_touch_info *touch_info) +{ + memset(touch_info, 0, sizeof(*touch_info)); + return cts_fw_reg_readsb(cts_dev, CTS_DEVICE_FW_REG_TOUCH_INFO, + touch_info, sizeof(*touch_info)); +} + +int cts_hostcomm_get_fw_id(const struct cts_device *cts_dev, u16 *fwid) +{ + u16 tmp = 0xffff; + int ret; + + ret = cts_fw_reg_readw(cts_dev, CTS_DEVICE_FW_REG_CHIP_TYPE, &tmp); + tmp = be16_to_cpu(tmp); + *fwid = tmp; + cts_info("%s:%04X", __func__, tmp); + + return ret; +} + +int cts_hostcomm_get_workmode(const struct cts_device *cts_dev, u8 *workmode) +{ + return cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_GET_WORK_MODE, + workmode); +} + +int cts_hostcomm_set_workmode(const struct cts_device *cts_dev, u8 workmode) +{ + return set_fw_work_mode(cts_dev, workmode); +} + +int cts_hostcomm_set_openshort_mode(const struct cts_device *cts_dev, u8 type) +{ + int i, ret; + + cts_info("Set test type %d", type); + + for (i = 0; i < 5; i++) { + u8 type_readback; + + ret = cts_fw_reg_writeb(cts_dev, 0x34, type); + if (ret) { + cts_err("Write test type register to failed %d", ret); + continue; + } + + ret = cts_fw_reg_readb(cts_dev, 0x34, &type_readback); + if (ret) { + cts_err("Read test type register failed %d", ret); + continue; + } + + if (type != type_readback) { + cts_err("Set test type %u != readback %u", type, + type_readback); + ret = -EFAULT; + continue; + } + } + + return ret; +} + +int cts_hostcomm_set_tx_vol(const struct cts_device *cts_dev, u8 txvol) +{ + return cts_send_command(cts_dev, CTS_CMD_RECOVERY_TX_VOL); +} + +int cts_hostcomm_is_enabled_get_rawdata(const struct cts_device *cts_dev, + u8 *enabled) +{ + return cts_fw_reg_readb(cts_dev, 0x12, enabled); +} + +int cts_hostcomm_set_short_test_type(const struct cts_device *cts_dev, + u8 short_type) +{ + return set_short_test_type(cts_dev, short_type); +} + +int cts_hostcomm_is_openshort_enabled(const struct cts_device *cts_dev, + u8 *enabled) +{ + int ret = -1; + + cts_err("BUG! Should not be here!"); + return ret; +} + +int cts_hostcomm_set_openshort_enable(const struct cts_device *cts_dev, + u8 enable) +{ + int ret = -1; + + cts_err("BUG! Should not be here!"); + return ret; +} + +int cts_hostcomm_set_esd_enable(const struct cts_device *cts_dev, u8 enable) +{ + int ret = -1; + + if (!enable) + return disable_fw_esd_protection(cts_dev); + + return ret; +} + +int cts_hostcomm_is_cneg_enabled(const struct cts_device *cts_dev, u8 *enabled) +{ + return cts_fw_reg_readb(cts_dev, 0x8000 + 276, enabled); +} + +int cts_hostcomm_is_mnt_enabled(const struct cts_device *cts_dev, u8 *enabled) +{ + return cts_fw_reg_readb(cts_dev, 0x8000 + 344, enabled); +} + +int cts_hostcomm_set_cneg_enable(const struct cts_device *cts_dev, u8 enable) +{ + int ret = -1; + + if (!enable) + return disable_fw_auto_compensate(cts_dev); + + return ret; +} + +int cts_hostcomm_set_mnt_enable(const struct cts_device *cts_dev, u8 enable) +{ + int ret = -1; + + if (!enable) + return disable_fw_monitor_mode(cts_dev); + + return ret; +} + +int cts_hostcomm_is_display_on(const struct cts_device *cts_dev, + u8 *display_on) +{ + return cts_fw_reg_readb(cts_dev, 0x8000 + 163, display_on); +} + +int cts_hostcomm_set_pwr_mode(const struct cts_device *cts_dev, u8 pwr_mode) +{ + //TODO: + return 0; +} + +int cts_hostcomm_set_display_on(const struct cts_device *cts_dev, u8 display_on) +{ + return set_display_state(cts_dev, false); +} + +int cts_hostcomm_top_get_rawdata(struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + //TODO: moved from sysfs:rawdata_show + //TODO: + return 0; +} + +int cts_hostcomm_top_get_manual_diff(struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + //TODO: + return 0; +} + +int cts_hostcomm_top_get_real_diff(struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + //TODO: + return 0; +} + +int cts_hostcomm_top_get_noise_diff(struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + //TODO: + return 0; +} + +int cts_hostcomm_top_get_basedata(struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + //TODO: + return 0; +} + +int cts_hostcomm_top_get_cnegdata(struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + return cts_get_compensate_cap(cts_dev, buf); +} + +int cts_hostcomm_reset_device(const struct cts_device *cts_dev) +{ + return cts_plat_reset_device(cts_dev->pdata); +} + +int cts_hostcomm_set_int_pin(const struct cts_device *cts_dev, u8 high) +{ + int ret; + + if (high) + ret = cts_send_command(cts_dev, CTS_CMD_WRTITE_INT_HIGH); + else + ret = cts_send_command(cts_dev, CTS_CMD_WRTITE_INT_LOW); + + if (ret) + cts_err("Send command WRTITE_INT_%s failed %d", high ? "HIGH" : "LOW", ret); + + return ret; +} + +struct cts_dev_ops hostcomm_ops = { + .get_fw_ver = cts_hostcomm_get_fw_ver, + .get_lib_ver = cts_hostcomm_get_lib_ver, + .get_ddi_ver = cts_hostcomm_get_ddi_ver, + .get_res_x = cts_hostcomm_get_res_x, + .get_res_y = cts_hostcomm_get_res_y, + .get_rows = cts_hostcomm_get_rows, + .get_cols = cts_hostcomm_get_cols, + .get_flip_x = cts_hostcomm_get_flip_x, + .get_flip_y = cts_hostcomm_get_flip_y, + .get_swap_axes = cts_hostcomm_get_swap_axes, + .get_int_mode = cts_hostcomm_get_int_mode, + .get_int_keep_time = cts_hostcomm_get_int_keep_time, + .get_esd_method = cts_hostcomm_get_esd_method, + .get_touchinfo = cts_hostcomm_get_touchinfo, + .get_esd_protection = cts_hostcomm_get_esd_protection, + .get_data_ready_flag = cts_hostcomm_get_data_ready_flag, + .clr_data_ready_flag = cts_hostcomm_clr_data_ready_flag, + .enable_get_rawdata = cts_hostcomm_enable_get_rawdata, + .is_enabled_get_rawdata = cts_hostcomm_is_enabled_get_rawdata, + .disable_get_rawdata = cts_hostcomm_disable_get_rawdata, + .enable_get_cneg = cts_hostcomm_enable_get_cneg, + .disable_get_cneg = cts_hostcomm_disable_get_cneg, + .is_cneg_ready = cts_hostcomm_is_cneg_ready, + .quit_guesture_mode = cts_hostcomm_quit_guesture_mode, + .get_rawdata = cts_hostcomm_get_rawdata, + .get_diffdata = cts_hostcomm_get_diffdata, + .get_basedata = cts_hostcomm_get_basedata, + .get_cneg = cts_hostcomm_get_cneg, + .read_hw_reg = cts_hostcomm_read_hw_reg, + .write_hw_reg = cts_hostcomm_write_hw_reg, + .read_ddi_reg = cts_hostcomm_read_ddi_reg, + .write_ddi_reg = cts_hostcomm_write_ddi_reg, + .read_fw_reg = cts_hostcomm_read_fw_reg, + .write_fw_reg = cts_hostcomm_write_fw_reg, + .read_reg = cts_hostcomm_read_reg, + .write_reg = cts_hostcomm_write_reg, + .get_fw_id = cts_hostcomm_get_fw_id, + .get_workmode = cts_hostcomm_get_workmode, + .set_workmode = cts_hostcomm_set_workmode, + .set_openshort_mode = cts_hostcomm_set_openshort_mode, + .set_tx_vol = cts_hostcomm_set_tx_vol, + .set_short_test_type = cts_hostcomm_set_short_test_type, + .set_openshort_enable = cts_hostcomm_set_openshort_enable, + .is_openshort_enabled = cts_hostcomm_is_openshort_enabled, + .set_esd_enable = cts_hostcomm_set_esd_enable, + .set_cneg_enable = cts_hostcomm_set_cneg_enable, + .set_mnt_enable = cts_hostcomm_set_mnt_enable, + .is_display_on = cts_hostcomm_is_display_on, + .set_display_on = cts_hostcomm_set_display_on, + .is_cneg_enabled = cts_hostcomm_is_cneg_enabled, + .is_mnt_enabled = cts_hostcomm_is_mnt_enabled, + .set_pwr_mode = cts_hostcomm_set_pwr_mode, + .read_sram_normal_mode = cts_read_sram_normal_mode, + .init_int_data = NULL, //TODO + .deinit_int_data = NULL, //TODO + .get_has_int_data = NULL, + .get_int_data_types = NULL, + .set_int_data_types = NULL, + .get_int_data_method = NULL, + .set_int_data_method = NULL, + .calc_int_data_size = NULL, + .polling_data = NULL, + .polling_test_data = NULL, + + .top_get_rawdata = cts_hostcomm_top_get_rawdata, + .top_get_manual_diff = cts_hostcomm_top_get_manual_diff, + .top_get_real_diff = cts_hostcomm_top_get_real_diff, + .top_get_noise_diff = cts_hostcomm_top_get_noise_diff, + .top_get_basedata = cts_hostcomm_top_get_basedata, + .top_get_cnegdata = cts_hostcomm_top_get_cnegdata, + + .reset_device = cts_hostcomm_reset_device, + + .set_int_test = NULL, + .set_int_pin = cts_hostcomm_set_int_pin, + .get_module_id = NULL, +}; + +//TODO: +//fwdata->rawdata_target; diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_hostcomm.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_hostcomm.h new file mode 100644 index 000000000000..f28968798b6e --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_hostcomm.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_HOSTCOMM_H +#define CTS_HOSTCOMM_H + +#include "cts_config.h" +#include "cts_core.h" +extern struct cts_dev_ops hostcomm_ops; + +#endif /* CTS_HOSTCOMM_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_i2c_driver.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_i2c_driver.c new file mode 100644 index 000000000000..019e050aa5e5 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_i2c_driver.c @@ -0,0 +1,1111 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "I2CDrv" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sysfs.h" +#include "cts_charger_detect.h" + +struct chipone_ts_data *chipone_ts; + +enum touch_state { + TOUCH_DEEP_SLEEP_STATE = 0, + TOUCH_LOW_POWER_STATE, +}; + +static void cts_resume_work_func(struct work_struct *work); +#ifdef CFG_CTS_DRM_NOTIFIER +#include +static struct drm_panel *active_panel; +static int check_dt(struct device_node *np); +#endif +bool cts_show_debug_log; +#ifdef CTS_MTK_GET_PANEL +static char *active_panel_name; +#endif + +module_param_named(debug_log, cts_show_debug_log, bool, 0660); +MODULE_PARM_DESC(debug_log, "Show debug log control"); + +static int cts_suspend(struct chipone_ts_data *cts_data) +{ + int ret; + + cts_info("Suspend"); + + cts_lock_device(&cts_data->cts_dev); + ret = cts_suspend_device(&cts_data->cts_dev); + cts_unlock_device(&cts_data->cts_dev); + + if (ret) + cts_err("Suspend device failed %d", ret); + + ret = cts_stop_device(&cts_data->cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } +#ifdef CFG_CTS_GESTURE + /* Enable IRQ wake if gesture wakeup enabled */ + if (cts_is_gesture_wakeup_enabled(&cts_data->cts_dev)) { + ret = cts_plat_enable_irq_wake(cts_data->pdata); + if (ret) { + cts_err("Enable IRQ wake failed %d", ret); + return ret; + } + ret = cts_plat_enable_irq(cts_data->pdata); + if (ret) { + cts_err("Enable IRQ failed %d", ret); + return ret; + } + } +#endif /* CFG_CTS_GESTURE */ + +/** - To avoid waking up while not sleeping, + *delay 20ms to ensure reliability + */ + msleep(20); + + return 0; +} + +static int cts_resume(struct chipone_ts_data *cts_data) +{ + int ret; + + cts_info("Resume"); + +#ifdef CFG_CTS_GESTURE + if (cts_is_gesture_wakeup_enabled(&cts_data->cts_dev)) { + ret = cts_plat_disable_irq_wake(cts_data->pdata); + if (ret) + cts_warn("Disable IRQ wake failed %d", ret); + ret = cts_plat_disable_irq(cts_data->pdata); + if (ret < 0) + cts_err("Disable IRQ failed %d", ret); + } +#endif /* CFG_CTS_GESTURE */ + + cts_lock_device(&cts_data->cts_dev); + ret = cts_resume_device(&cts_data->cts_dev); + cts_unlock_device(&cts_data->cts_dev); + if (ret) { + cts_warn("Resume device failed %d", ret); + return ret; + } + + ret = cts_start_device(&cts_data->cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + return ret; + } + + return 0; +} + +static void cts_resume_work_func(struct work_struct *work) +{ + struct chipone_ts_data *cts_data = + container_of(work, struct chipone_ts_data, ts_resume_work); + cts_info("%s", __func__); + cts_resume(cts_data); +} + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER +#ifdef CFG_CTS_DRM_NOTIFIER +static int fb_notifier_callback(struct notifier_block *nb, + unsigned long action, void *data) +{ + volatile int blank; + const struct cts_platform_data *pdata = + container_of(nb, struct cts_platform_data, fb_notifier); + struct chipone_ts_data *cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + struct drm_panel_notifier *evdata = data; + + cts_info("FB notifier callback"); + if (!evdata || !cts_data) + return 0; + + blank = *(int *)evdata->data; + cts_info("action=%lu, blank=%d\n", action, blank); + + if (action == DRM_PANEL_EARLY_EVENT_BLANK) { + if (blank == DRM_PANEL_BLANK_POWERDOWN) { + if (chipone_ts->should_enable_gesture) + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); + else + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); + + cts_suspend(cts_data); + } + } else if (evdata->data) { + blank = *(int *)evdata->data; + if (action == DRM_PANEL_EVENT_BLANK) { + if (blank == DRM_PANEL_BLANK_UNBLANK) + /* cts_resume(cts_data); */ + queue_work(cts_data->workqueue, + &cts_data->ts_resume_work); + } + } + + return 0; +} +#else +static int fb_notifier_callback(struct notifier_block *nb, + unsigned long action, void *data) +{ + volatile int blank; + const struct cts_platform_data *pdata = + container_of(nb, struct cts_platform_data, fb_notifier); + struct chipone_ts_data *cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + struct fb_event *evdata = data; + + cts_info("FB notifier callback"); + + if (evdata && evdata->data) { + if (action == FB_EVENT_BLANK) { + blank = *(int *)evdata->data; + if (blank == FB_BLANK_UNBLANK) { + /* cts_resume(cts_data); */ + queue_work(cts_data->workqueue, + &cts_data->ts_resume_work); + return NOTIFY_OK; + } + } else if (action == FB_EARLY_EVENT_BLANK) { + blank = *(int *)evdata->data; + if (blank == FB_BLANK_POWERDOWN) { + cts_suspend(cts_data); + return NOTIFY_OK; + } + } + } + + return NOTIFY_DONE; +} +#endif + +static int cts_init_pm_fb_notifier(struct chipone_ts_data *cts_data) +{ + cts_info("Init FB notifier"); + + cts_data->pdata->fb_notifier.notifier_call = fb_notifier_callback; + +#ifdef CFG_CTS_DRM_NOTIFIER + { + int ret = -ENODEV; + + if (active_panel) { + ret = + drm_panel_notifier_register(active_panel, + &cts_data->pdata->fb_notifier); + if (ret) + cts_err + ("register drm_notifier failed. ret=%d\n", + ret); + } + return ret; + } +#else + return fb_register_client(&cts_data->pdata->fb_notifier); +#endif +} + +static int cts_deinit_pm_fb_notifier(struct chipone_ts_data *cts_data) +{ + cts_info("Deinit FB notifier"); +#ifdef CFG_CTS_DRM_NOTIFIER + { + int ret = 0; + + if (active_panel) { + ret = + drm_panel_notifier_unregister(active_panel, + &cts_data->pdata->fb_notifier); + if (ret) + cts_err + ("Error occurred while unregistering drm_notifier.\n"); + } + return ret; + } +#else + return fb_unregister_client(&cts_data->pdata->fb_notifier); +#endif +} +#endif /* CONFIG_CTS_PM_FB_NOTIFIER */ + +#ifdef CFG_CTS_DRM_NOTIFIER +static int 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)) { + cts_info("check active_panel"); + active_panel = panel; + return 0; + } + } + if (node) + pr_err("%s: %s not actived\n", __func__, node->name); + return -ENODEV; +} + +static int check_default_tp(struct device_node *dt, const char *prop) +{ + const char *active_tp; + const char *compatible; + char *start; + int ret; + + ret = of_property_read_string(dt->parent, prop, &active_tp); + if (ret) { + cts_err(" %s:fail to read %s %d\n", __func__, prop, ret); + return -ENODEV; + } + + ret = of_property_read_string(dt, "compatible", &compatible); + if (ret < 0) { + cts_err(" %s:fail to read %s %d\n", __func__, "compatible", + ret); + return -ENODEV; + } + + start = strnstr(active_tp, compatible, strlen(active_tp)); + if (start == NULL) { + cts_err(" %s:no match compatible, %s, %s\n", + __func__, compatible, active_tp); + ret = -ENODEV; + } + + return ret; +} +#endif + +#ifdef CTS_MTK_GET_PANEL +char panel_name[50] = { 0 }; + +static int cts_get_panel(void) +{ + int ret = -1; + + cts_info("enter"); + if (saved_command_line) { + char *sub; + char key_prefix[] = "mipi_mot_vid_"; + char ic_prefix[] = "icnl"; + + cts_info("saved_command_line is %s", saved_command_line); + sub = strstr(saved_command_line, key_prefix); + if (sub) { + char *d; + int n, len, len_max = 50; + + d = strstr(sub, " "); + if (d) + n = strlen(sub) - strlen(d); + else + n = strlen(sub); + + if (n > len_max) + len = len_max; + else + len = n; + + strncpy(panel_name, sub, len); + active_panel_name = panel_name; + if (strstr(active_panel_name, ic_prefix)) + cts_info("active_panel_name=%s", + active_panel_name); + else { + cts_info("Not chipone panel!"); + return ret; + } + + } else { + cts_info("chipone active panel not found!"); + return ret; + } + } else { + cts_info("saved_command_line null!"); + return ret; + } + + return 0; +} +#endif + +#ifdef CHIPONE_SENSOR_EN +static struct sensors_classdev __maybe_unused sensors_touch_cdev = { + + .name = "dt-gesture", + .vendor = "ilitek", + .version = 1, + .type = SENSOR_TYPE_MOTO_DOUBLE_TAP, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + /* WAKE_UP & SPECIAL_REPORT */ + .flags = 1 | 6, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; + +static int chipone_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + cts_info("Gesture set enable %d!", enable); + mutex_lock(&chipone_ts->state_mutex); + if (enable == 1) { + cts_enable_gesture_wakeup(&chipone_ts->cts_dev); + chipone_ts->should_enable_gesture = true; + } else if (enable == 0) { + cts_disable_gesture_wakeup(&chipone_ts->cts_dev); + chipone_ts->should_enable_gesture = false; + } else { + cts_info("unknown enable symbol\n"); + } + mutex_unlock(&chipone_ts->state_mutex); + return 0; +} + +static int chipone_sensor_init(struct chipone_ts_data *data) +{ + struct chipone_sensor_platform_data *sensor_pdata; + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + cts_err("Failed to allocate device"); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct chipone_sensor_platform_data), + GFP_KERNEL); + if (!sensor_pdata) { + cts_err("Failed to allocate memory"); + goto free_sensor_pdata; + } + data->sensor_pdata = sensor_pdata; + + __set_bit(EV_KEY, sensor_input_dev->evbit); + __set_bit(KEY_F1, sensor_input_dev->keybit); + __set_bit(EV_SYN, sensor_input_dev->evbit); + + sensor_input_dev->name = "double-tap"; + data->sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + cts_err("Unable to register device, err=%d", err); + goto free_sensor_input_dev; + } + + sensor_pdata->ps_cdev = sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = chipone_sensor_set_enable; + sensor_pdata->data = data; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(data->sensor_pdata->input_sensor_dev); +free_sensor_input_dev: + input_free_device(data->sensor_pdata->input_sensor_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, sensor_pdata); + data->sensor_pdata = NULL; +exit: + return 1; +} + +int chipone_sensor_remove(struct chipone_ts_data *data) +{ + sensors_classdev_unregister(&data->sensor_pdata->ps_cdev); + input_unregister_device(data->sensor_pdata->input_sensor_dev); + devm_kfree(&data->sensor_pdata->input_sensor_dev->dev, + data->sensor_pdata); + data->sensor_pdata = NULL; + data->wakeable = false; + data->should_enable_gesture = false; + return 0; +} +#endif + +#ifdef CONFIG_CTS_I2C_HOST +static int cts_driver_probe(struct i2c_client *client, + const struct i2c_device_id *id) +#else +static int cts_driver_probe(struct spi_device *client) +#endif +{ + struct chipone_ts_data *cts_data = NULL; + int ret = 0; +#ifdef CHIPONE_SENSOR_EN + static bool initialized_sensor; +#endif + +#ifdef CTS_MTK_GET_PANEL + ret = cts_get_panel(); + if (ret) { + cts_info("MTK get chipone panel error"); + return ret; + } +#endif + +#ifdef CFG_CTS_DRM_NOTIFIER + { + struct device_node *dp = client->dev.of_node; + + if (check_dt(dp)) { + if (!check_default_tp(dp, "qcom,i2c-touch-active")) + ret = -EPROBE_DEFER; + else + ret = -ENODEV; + + cts_err("%s: %s not actived\n", __func__, dp->name); + return ret; + } + } +#endif + +#ifdef CONFIG_CTS_I2C_HOST + cts_info("Probe i2c client: name='%s' addr=0x%02x flags=0x%02x irq=%d", + client->name, client->addr, client->flags, client->irq); + +#if !defined(CONFIG_MTK_PLATFORM) + if (client->addr != CTS_DEV_NORMAL_MODE_I2CADDR) { + cts_err("Probe i2c addr 0x%02x != driver config addr 0x%02x", + client->addr, CTS_DEV_NORMAL_MODE_I2CADDR); + return -ENODEV; + }; +#endif + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + cts_err("Check functionality failed"); + return -ENODEV; + } +#endif + + cts_data = + (struct chipone_ts_data *)kzalloc(sizeof(*cts_data), GFP_KERNEL); + if (cts_data == NULL) { + cts_err("Allocate chipone_ts_data failed"); + return -ENOMEM; + } + cts_data->pdata = (struct cts_platform_data *) + kzalloc(sizeof(struct cts_platform_data), GFP_KERNEL); + if (cts_data->pdata == NULL) { + cts_err("Allocate cts_platform_data failed"); + ret = -ENOMEM; + goto err_free_cts_data; + } +#ifdef CONFIG_CTS_I2C_HOST + i2c_set_clientdata(client, cts_data); + cts_data->i2c_client = client; + cts_data->device = &client->dev; +#else + spi_set_drvdata(client, cts_data); + cts_data->spi_client = client; + cts_data->device = &client->dev; +#endif + + cts_init_platform_data(cts_data->pdata, client); + + cts_data->cts_dev.pdata = cts_data->pdata; + cts_data->pdata->cts_dev = &cts_data->cts_dev; + + cts_data->workqueue = + create_singlethread_workqueue(CFG_CTS_DEVICE_NAME "-workqueue"); + if (cts_data->workqueue == NULL) { + cts_err("Create workqueue failed"); + ret = -ENOMEM; + goto err_deinit_platform_data; + } +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_data->esd_workqueue = + create_singlethread_workqueue(CFG_CTS_DEVICE_NAME "-esd_workqueue"); + if (cts_data->esd_workqueue == NULL) { + cts_err("Create esd workqueue failed"); + ret = -ENOMEM; + goto err_destroy_workqueue; + } +#endif + chipone_ts = cts_data; + ret = cts_plat_request_resource(cts_data->pdata); + if (ret < 0) { + cts_err("Request resource failed %d", ret); + goto err_destroy_esd_workqueue; + } + + ret = cts_reset_device(&cts_data->cts_dev); + if (ret < 0) { + cts_err("Reset device failed %d", ret); + goto err_free_resource; + } + + ret = cts_probe_device(&cts_data->cts_dev); + if (ret) { + cts_err("Probe device failed %d", ret); + goto err_free_resource; + } + + ret = cts_plat_init_touch_device(cts_data->pdata); + if (ret < 0) { + cts_err("Init touch device failed %d", ret); + goto err_free_resource; + } + + ret = cts_plat_init_vkey_device(cts_data->pdata); + if (ret < 0) { + cts_err("Init vkey device failed %d", ret); + goto err_deinit_touch_device; + } + + ret = cts_plat_init_gesture(cts_data->pdata); + if (ret < 0) { + cts_err("Init gesture failed %d", ret); + goto err_deinit_vkey_device; + } + + cts_init_esd_protection(cts_data); + + ret = cts_tool_init(cts_data); + if (ret < 0) + cts_warn("Init tool node failed %d", ret); + + ret = cts_sysfs_add_device(&client->dev); + if (ret < 0) + cts_warn("Add sysfs entry for device failed %d", ret); + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + ret = cts_init_pm_fb_notifier(cts_data); + if (ret) { + cts_err("Init FB notifier failed %d", ret); + goto err_deinit_sysfs; + } +#endif /* CONFIG_CTS_PM_FB_NOTIFIER */ + + ret = cts_plat_request_irq(cts_data->pdata); + if (ret < 0) { + cts_err("Request IRQ failed %d", ret); + goto err_register_fb; + } + +#ifdef CONFIG_CTS_CHARGE_DETECT + ret = cts_charger_detect_init(cts_data); + if (ret) + cts_err("Init charger detect failed %d", ret); + /* Ignore this error */ +#endif + + /* Init firmware upgrade work and schedule */ + INIT_DELAYED_WORK(&cts_data->fw_upgrade_work, + cts_firmware_upgrade_work); + queue_delayed_work(cts_data->workqueue, &cts_data->fw_upgrade_work, + msecs_to_jiffies(15 * 1000)); + + INIT_WORK(&cts_data->ts_resume_work, cts_resume_work_func); + +#ifdef CHIPONE_SENSOR_EN + mutex_init(&cts_data->state_mutex); + //unknown screen state + cts_data->screen_state = SCREEN_UNKNOWN; + if (!initialized_sensor) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&(cts_data->gesture_wakelock), WAKE_LOCK_SUSPEND, "dt-wake-lock"); +#else + PM_WAKEUP_REGISTER(cts_data->device, cts_data->gesture_wakelock, "dt-wake-lock"); + if (!cts_data->gesture_wakelock) { + cts_err("ILITEK Driver failed to load. wakeup_source_init failed."); + return -ENOMEM; + } +#endif + if (!chipone_sensor_init(cts_data)) + initialized_sensor = true; + } +#endif + + return 0; + +err_register_fb: +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + cts_deinit_pm_fb_notifier(cts_data); +err_deinit_sysfs: +#endif /* CONFIG_CTS_PM_FB_NOTIFIER */ + cts_sysfs_remove_device(&client->dev); +#ifdef CONFIG_CTS_LEGACY_TOOL + cts_tool_deinit(cts_data); +#endif /* CONFIG_CTS_LEGACY_TOOL */ + +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_deinit_esd_protection(cts_data); +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CFG_CTS_GESTURE + cts_plat_deinit_gesture(cts_data->pdata); +#endif /* CFG_CTS_GESTURE */ + +err_deinit_vkey_device: +#ifdef CONFIG_CTS_VIRTUALKEY + cts_plat_deinit_vkey_device(cts_data->pdata); +#endif /* CONFIG_CTS_VIRTUALKEY */ + +err_deinit_touch_device: + cts_plat_deinit_touch_device(cts_data->pdata); + +err_free_resource: + cts_plat_free_resource(cts_data->pdata); +err_destroy_esd_workqueue: +#ifdef CONFIG_CTS_ESD_PROTECTION + destroy_workqueue(cts_data->esd_workqueue); +err_destroy_workqueue: +#endif + destroy_workqueue(cts_data->workqueue); +err_deinit_platform_data: + cts_deinit_platform_data(cts_data->pdata); + kfree(cts_data->pdata); +err_free_cts_data: + kfree(cts_data); + + cts_err("Probe failed %d", ret); + + return ret; +} + +#ifdef CONFIG_CTS_I2C_HOST +static int cts_driver_remove(struct i2c_client *client) +#else +static int cts_driver_remove(struct spi_device *client) +#endif +{ + struct chipone_ts_data *cts_data; + int ret = 0; + + cts_info("Remove"); + +#ifdef CONFIG_CTS_I2C_HOST + cts_data = (struct chipone_ts_data *)i2c_get_clientdata(client); +#else + cts_data = (struct chipone_ts_data *)spi_get_drvdata(client); +#endif + if (cts_data) { + ret = cts_stop_device(&cts_data->cts_dev); + if (ret) + cts_warn("Stop device failed %d", ret); + +#ifdef CONFIG_CTS_CHARGE_DETECT + cts_charger_detect_deinit(cts_data); +#endif + + cts_plat_free_irq(cts_data->pdata); + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + cts_deinit_pm_fb_notifier(cts_data); +#endif /* CONFIG_CTS_PM_FB_NOTIFIER */ + + cts_tool_deinit(cts_data); + + cts_sysfs_remove_device(&client->dev); + + cts_deinit_esd_protection(cts_data); + + cts_plat_deinit_touch_device(cts_data->pdata); + + cts_plat_deinit_vkey_device(cts_data->pdata); + + cts_plat_deinit_gesture(cts_data->pdata); + + cts_plat_free_resource(cts_data->pdata); + + if (cts_data->cts_dev.tx) + kfree(cts_data->cts_dev.tx); + if (cts_data->cts_dev.rx) + kfree(cts_data->cts_dev.rx); + +#ifdef CONFIG_CTS_ESD_PROTECTION + if (cts_data->esd_workqueue) + destroy_workqueue(cts_data->esd_workqueue); +#endif + + if (cts_data->workqueue) + destroy_workqueue(cts_data->workqueue); + + cts_deinit_platform_data(cts_data->pdata); + + cts_deinit_rtdata(&cts_data->cts_dev); + + if (cts_data->pdata) + kfree(cts_data->pdata); + kfree(cts_data); + } else { + cts_warn + ("Chipone i2c driver remove while NULL chipone_ts_data"); + return -EINVAL; + } + + return ret; +} + +#ifdef CONFIG_CTS_PM_LEGACY +static int cts_i2c_driver_suspend(struct device *dev, pm_message_t state) +{ + cts_info("Suspend by legacy power management"); + return cts_suspend(dev_get_drvdata(dev)); +} + +static int cts_i2c_driver_resume(struct device *dev) +{ + cts_info("Resume by legacy power management"); + return cts_resume(dev_get_drvdata(dev)); +} +#endif /* CONFIG_CTS_PM_LEGACY */ + +#ifdef CONFIG_CTS_PM_GENERIC +static int cts_i2c_driver_pm_suspend(struct device *dev) +{ + cts_info("Suspend by bus power management"); + return cts_suspend(dev_get_drvdata(dev)); +} + +static int cts_i2c_driver_pm_resume(struct device *dev) +{ + cts_info("Resume by bus power management"); + return cts_resume(dev_get_drvdata(dev)); +} + +/* bus control the suspend/resume procedure */ +static const struct dev_pm_ops cts_i2c_driver_pm_ops = { + .suspend = cts_i2c_driver_pm_suspend, + .resume = cts_i2c_driver_pm_resume, +}; +#endif /* CONFIG_CTS_PM_GENERIC */ + +#ifdef CONFIG_CTS_SYSFS +static ssize_t reset_pin_show(struct device_driver *driver, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_HAS_RESET_PIN: %c\n", +#ifdef CFG_CTS_HAS_RESET_PIN + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(reset_pin, S_IRUGO, reset_pin_show, NULL); +#else +static DRIVER_ATTR_RO(reset_pin); +#endif + +static ssize_t swap_xy_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_SWAP_XY: %c\n", +#ifdef CFG_CTS_SWAP_XY + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(swap_xy, S_IRUGO, swap_xy_show, NULL); +#else +static DRIVER_ATTR_RO(swap_xy); +#endif + +static ssize_t wrap_x_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_WRAP_X: %c\n", +#ifdef CFG_CTS_WRAP_X + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(wrap_x, S_IRUGO, wrap_x_show, NULL); +#else +static DRIVER_ATTR_RO(wrap_x); +#endif + +static ssize_t wrap_y_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_WRAP_Y: %c\n", +#ifdef CFG_CTS_WRAP_Y + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(wrap_y, S_IRUGO, wrap_y_show, NULL); +#else +static DRIVER_ATTR_RO(wrap_y); +#endif + +static ssize_t force_update_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_HAS_RESET_PIN: %c\n", +#ifdef CFG_CTS_FIRMWARE_FORCE_UPDATE + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(force_update, S_IRUGO, force_update_show, NULL); +#else +static DRIVER_ATTR_RO(force_update); +#endif + +static ssize_t max_touch_num_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_MAX_TOUCH_NUM: %d\n", + CFG_CTS_MAX_TOUCH_NUM); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(max_touch_num, S_IRUGO, max_touch_num_show, NULL); +#else +static DRIVER_ATTR_RO(max_touch_num); +#endif + +static ssize_t vkey_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CONFIG_CTS_VIRTUALKEY: %c\n", +#ifdef CONFIG_CTS_VIRTUALKEY + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(vkey, S_IRUGO, vkey_show, NULL); +#else +static DRIVER_ATTR_RO(vkey); +#endif + +static ssize_t gesture_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_GESTURE: %c\n", +#ifdef CFG_CTS_GESTURE + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(gesture, S_IRUGO, gesture_show, NULL); +#else +static DRIVER_ATTR_RO(gesture); +#endif + +static ssize_t esd_protection_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CONFIG_CTS_ESD_PROTECTION: %c\n", +#ifdef CONFIG_CTS_ESD_PROTECTION + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(esd_protection, S_IRUGO, esd_protection_show, NULL); +#else +static DRIVER_ATTR_RO(esd_protection); +#endif + +static ssize_t slot_protocol_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CONFIG_CTS_SLOTPROTOCOL: %c\n", +#ifdef CONFIG_CTS_SLOTPROTOCOL + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(slot_protocol, S_IRUGO, slot_protocol_show, NULL); +#else +static DRIVER_ATTR_RO(slot_protocol); +#endif + +static ssize_t max_xfer_size_show(struct device_driver *dev, char *buf) +{ +#ifdef CONFIG_CTS_I2C_HOST + return snprintf(buf, PAGE_SIZE, "CFG_CTS_MAX_I2C_XFER_SIZE: %d\n", + CFG_CTS_MAX_I2C_XFER_SIZE); +#else + return snprintf(buf, PAGE_SIZE, "CFG_CTS_MAX_SPI_XFER_SIZE: %d\n", + CFG_CTS_MAX_SPI_XFER_SIZE); +#endif +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(max_xfer_size, S_IRUGO, max_xfer_size_show, NULL); +#else +static DRIVER_ATTR_RO(max_xfer_size); +#endif + +static ssize_t driver_info_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "Driver version: %s\n", + CFG_CTS_DRIVER_VERSION); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(driver_info, S_IRUGO, driver_info_show, NULL); +#else +static DRIVER_ATTR_RO(driver_info); +#endif + +static struct attribute *cts_i2c_driver_config_attrs[] = { + &driver_attr_reset_pin.attr, + &driver_attr_swap_xy.attr, + &driver_attr_wrap_x.attr, + &driver_attr_wrap_y.attr, + &driver_attr_force_update.attr, + &driver_attr_max_touch_num.attr, + &driver_attr_vkey.attr, + &driver_attr_gesture.attr, + &driver_attr_esd_protection.attr, + &driver_attr_slot_protocol.attr, + &driver_attr_max_xfer_size.attr, + &driver_attr_driver_info.attr, + NULL +}; + +static const struct attribute_group cts_i2c_driver_config_group = { + .name = "config", + .attrs = cts_i2c_driver_config_attrs, +}; + +static const struct attribute_group *cts_i2c_driver_config_groups[] = { + &cts_i2c_driver_config_group, + NULL, +}; +#endif /* CONFIG_CTS_SYSFS */ + +#ifdef CONFIG_CTS_OF +static const struct of_device_id cts_i2c_of_match_table[] = { + {.compatible = CFG_CTS_OF_DEVICE_ID_NAME, }, + { }, +}; + +MODULE_DEVICE_TABLE(of, cts_i2c_of_match_table); +#endif /* CONFIG_CTS_OF */ + +#ifdef CONFIG_CTS_I2C_HOST +static const struct i2c_device_id cts_device_id_table[] = { + { CFG_CTS_DEVICE_NAME, 0 }, + { } +}; +#else +static const struct spi_device_id cts_device_id_table[] = { + { CFG_CTS_DEVICE_NAME, 0 }, + { } +}; +#endif + +#ifdef CONFIG_CTS_I2C_HOST +static struct i2c_driver cts_i2c_driver = { +#else +static struct spi_driver cts_spi_driver = { +#endif + .probe = cts_driver_probe, + .remove = cts_driver_remove, + .driver = { + .name = CFG_CTS_DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_CTS_OF + .of_match_table = of_match_ptr(cts_i2c_of_match_table), +#endif /* CONFIG_CTS_OF */ +#ifdef CONFIG_CTS_SYSFS + .groups = cts_i2c_driver_config_groups, +#endif /* CONFIG_CTS_SYSFS */ +#ifdef CONFIG_CTS_PM_LEGACY + .suspend = cts_i2c_driver_suspend, + .resume = cts_i2c_driver_resume, +#endif /* CONFIG_CTS_PM_LEGACY */ +#ifdef CONFIG_CTS_PM_GENERIC + .pm = &cts_i2c_driver_pm_ops, +#endif /* CONFIG_CTS_PM_GENERIC */ + + }, + .id_table = cts_device_id_table, +}; + +static int __init cts_driver_init(void) +{ + cts_info("Init"); + +#ifdef CONFIG_CTS_I2C_HOST + return i2c_add_driver(&cts_i2c_driver); +#else + pr_err("%s spi", __func__); + return spi_register_driver(&cts_spi_driver); +#endif +} + +static void __exit cts_driver_exit(void) +{ + cts_info("Exit"); + +#ifdef CONFIG_CTS_I2C_HOST + i2c_del_driver(&cts_i2c_driver); +#else + spi_unregister_driver(&cts_spi_driver); +#endif +} + +module_init(cts_driver_init); +module_exit(cts_driver_exit); + +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +MODULE_DESCRIPTION("Chipone TDDI touchscreen Driver for QualComm platform"); +MODULE_VERSION(CFG_CTS_DRIVER_VERSION); +MODULE_AUTHOR("Miao Defang "); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_plat_qcom_config.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_plat_qcom_config.h new file mode 100644 index 000000000000..e4e07fe22469 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_plat_qcom_config.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_PLAT_QCOM_CONFIG_H +#define CTS_PLAT_QCOM_CONFIG_H + +#define CONFIG_CTS_PM_FB_NOTIFIER + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER +/* + * #ifdef CONFIG_DRM + * #define CFG_CTS_DRM_NOTIFIER + * #endif + */ +#ifdef CONFIG_DRM +#define CFG_CTS_DRM_NOTIFIER +#endif /*CONFIG_DRM_MSM */ +#else /*CONFIG_CTS_PM_FB_NOTIFIER */ +#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_SUSPEND) + /* #define CONFIG_CTS_PM_GENERIC */ +#endif /* CONFIG_PM_SLEEP */ + +#if !defined(CONFIG_CTS_PM_GENERIC) +#define CONFIG_CTS_PM_LEGACY +#endif /*CONFIG_CTS_PM_GENERIC */ +#endif /*CONFIG_CTS_PM_FB_NOTIFIER */ + +#define CFG_CTS_MAX_I2C_XFER_SIZE (48u) +#define CFG_CTS_MAX_SPI_XFER_SIZE (8192u) + +#define CTS_FW_LOG_REDIRECT_SIGN 0x60 +#define CTS_FW_LOG_BUF_LEN 128 + +/** + * #define CFG_CTS_SWAP_XY + * #define CFG_CTS_WRAP_X + * #define CFG_CTS_WRAP_Y + */ + +#define CFG_CTS_DEVICE_NAME "chipone-tddi" +#define CFG_CTS_DRIVER_NAME "chipone-tddi" + +#if CFG_CTS_MAX_I2C_XFER_SIZE < 8 +#error "I2C transfer size should large than 8" +#endif + +#ifdef CONFIG_OF +#define CONFIG_CTS_OF +#endif +#ifdef CONFIG_CTS_OF +#define CFG_CTS_OF_DEVICE_ID_NAME "chipone-tddi" + +#define CFG_CTS_OF_INT_GPIO_NAME "chipone,irq-gpio" +#define CFG_CTS_OF_RST_GPIO_NAME "chipone,rst-gpio" + +#ifdef CFG_CTS_MANUAL_CS +#define CFG_CTS_OF_CS_GPIO_NAME "chipone,cs-gpio" +#endif + +#define CFG_CTS_OF_X_RESOLUTION_NAME "chipone,x-res" +#define CFG_CTS_OF_Y_RESOLUTION_NAME "chipone,y-res" + +#ifdef CFG_CTS_FW_UPDATE_SYS +#define CFG_CTS_OF_PANEL_SUPPLIER "chipone,panel-supplier" +#endif +#endif /* CONFIG_CTS_OF */ + +#endif /* CTS_PLAT_QCOM_CONFIG_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_platform.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_platform.c new file mode 100644 index 000000000000..d62fc24d983b --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_platform.c @@ -0,0 +1,1269 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "Plat" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_firmware.h" +#include "cts_sysfs.h" + +extern struct chipone_ts_data *chipone_ts; + +#ifdef CFG_CTS_FW_LOG_REDIRECT +size_t cts_plat_get_max_fw_log_size(struct cts_platform_data *pdata) +{ + return CTS_FW_LOG_BUF_LEN; +} + +u8 *cts_plat_get_fw_log_buf(struct cts_platform_data *pdata, size_t size) +{ + return pdata->fw_log_buf; +} +#endif + +size_t cts_plat_get_max_i2c_xfer_size(struct cts_platform_data *pdata) +{ + return CFG_CTS_MAX_I2C_XFER_SIZE; +} + +#ifdef CONFIG_CTS_I2C_HOST +u8 *cts_plat_get_i2c_xfer_buf(struct cts_platform_data *pdata, size_t xfer_size) +{ + return pdata->i2c_fifo_buf; +} + +int cts_plat_i2c_write(struct cts_platform_data *pdata, u8 i2c_addr, + const void *src, size_t len, int retry, int delay) +{ + int ret = 0, retries = 0; + + struct i2c_msg msg = { + .flags = 0, + .addr = i2c_addr, + .buf = (u8 *) src, + .len = len, + }; + + do { + ret = i2c_transfer(pdata->i2c_client->adapter, &msg, 1); + if (ret != 1) { + if (ret >= 0) + ret = -EIO; + + if (delay) + mdelay(delay); + continue; + } else + return 0; + } while (++retries < retry); + + return ret; +} + +int cts_plat_i2c_read(struct cts_platform_data *pdata, u8 i2c_addr, + const u8 *wbuf, size_t wlen, void *rbuf, size_t rlen, + int retry, int delay) +{ + int num_msg, ret = 0, retries = 0; + + struct i2c_msg msgs[2] = { + { + .addr = i2c_addr, + .flags = 0, + .buf = (u8 *) wbuf, + .len = wlen }, + { + .addr = i2c_addr, + .flags = I2C_M_RD, + .buf = (u8 *) rbuf, + .len = rlen } + }; + + if (wbuf == NULL || wlen == 0) + num_msg = 1; + else + num_msg = 2; + + do { + ret = i2c_transfer(pdata->i2c_client->adapter, + msgs + ARRAY_SIZE(msgs) - num_msg, num_msg); + + if (ret != num_msg) { + if (ret >= 0) + ret = -EIO; + + if (delay) + mdelay(delay); + continue; + } else + return 0; + } while (++retries < retry); + + return ret; +} + +int cts_plat_is_i2c_online(struct cts_platform_data *pdata, u8 i2c_addr) +{ + u8 dummy_bytes[2] = { 0x00, 0x00 }; + int ret; + + ret = cts_plat_i2c_write(pdata, i2c_addr, dummy_bytes, + sizeof(dummy_bytes), 5, 2); + if (ret) { + cts_err("!!! I2C addr 0x%02x is offline !!!", i2c_addr); + return false; + } + + cts_dbg("I2C addr 0x%02x is online", i2c_addr); + return true; +} +#else +void dump_spi_common(const char *prefix, u8 *data, size_t datalen) +{ + u8 str[1024]; + int offset = 0; + int i; + + offset += snprintf(str + offset, sizeof(str) - offset, "%s", prefix); + for (i = 0; i < datalen; i++) { + offset += + snprintf(str + offset, sizeof(str) - offset, " %02x", + data[i]); + } + cts_err("%s", str); +} + +#ifdef CFG_CTS_MANUAL_CS +int cts_plat_set_cs(struct cts_platform_data *pdata, int val) +{ + if (val) + gpio_set_value(pdata->cs_gpio, 1); + else + gpio_set_value(pdata->cs_gpio, 0); + + return 0; +} +#endif + +int cts_spi_send_recv(struct cts_platform_data *pdata, size_t len, + u8 *tx_buffer, u8 *rx_buffer) +{ + struct chipone_ts_data *cts_data; + struct spi_message msg; + struct spi_transfer cmd = { + .cs_change = 0, + .delay_usecs = 0, + .speed_hz = pdata->spi_speed * 1000u, + .tx_buf = tx_buffer, + .rx_buf = rx_buffer, + .len = len, + /* .tx_dma = 0, */ + /* .rx_dma = 0, */ + .bits_per_word = 8, + }; + int ret = 0; + + cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(pdata, 0); +#endif + cts_data->spi_client->chip_select = 0; + spi_message_init(&msg); + spi_message_add_tail(&cmd, &msg); + ret = spi_sync(cts_data->spi_client, &msg); + if (ret) + cts_err("spi sync failed %d", ret); + + udelay(100); + +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(pdata, 1); +#endif + return ret; +} + +size_t cts_plat_get_max_spi_xfer_size(struct cts_platform_data *pdata) +{ + return CFG_CTS_MAX_SPI_XFER_SIZE; +} + +u8 *cts_plat_get_spi_xfer_buf(struct cts_platform_data *pdata, size_t xfer_size) +{ + return pdata->spi_cache_buf; +} + +int cts_plat_spi_write(struct cts_platform_data *pdata, u8 dev_addr, + const void *src, size_t len, int retry, int delay) +{ + int ret = 0, retries = 0; + u16 crc; + size_t data_len; + + if (len > CFG_CTS_MAX_SPI_XFER_SIZE) { + cts_err("write too much data:wlen=%zu\n", len); + return -EIO; + } + + if (pdata->cts_dev->rtdata.program_mode) { + pdata->spi_tx_buf[0] = dev_addr; + memcpy(&pdata->spi_tx_buf[1], src, len); + + do { + ret = + cts_spi_send_recv(pdata, len + 1, pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI write failed %d", ret); + if (delay) + mdelay(delay); + } else + return 0; + } while (++retries < retry); + } else { + data_len = len - 2; + pdata->spi_tx_buf[0] = dev_addr; + pdata->spi_tx_buf[1] = *((u8 *) src + 1); + pdata->spi_tx_buf[2] = *((u8 *) src); + put_unaligned_le16(data_len, &pdata->spi_tx_buf[3]); + crc = (u16) cts_crc32(pdata->spi_tx_buf, 5); + put_unaligned_le16(crc, &pdata->spi_tx_buf[5]); + memcpy(&pdata->spi_tx_buf[7], (char *)src + 2, data_len); + crc = (u16) cts_crc32((char *)src + 2, data_len); + put_unaligned_le16(crc, &pdata->spi_tx_buf[7 + data_len]); + do { + ret = + cts_spi_send_recv(pdata, len + 7, pdata->spi_tx_buf, + pdata->spi_rx_buf); + udelay(10 * data_len); + if (ret) { + cts_err("SPI write failed %d", ret); + if (delay) + mdelay(delay); + } else + return 0; + } while (++retries < retry); + } + return ret; +} + +int cts_plat_spi_read(struct cts_platform_data *pdata, u8 dev_addr, + const u8 *wbuf, size_t wlen, void *rbuf, size_t rlen, + int retry, int delay) +{ + int ret = 0, retries = 0; + u16 crc; + + if (wlen > CFG_CTS_MAX_SPI_XFER_SIZE + || rlen > CFG_CTS_MAX_SPI_XFER_SIZE) { + cts_err("write/read too much data:wlen=%zd, rlen=%zd", wlen, + rlen); + return -EIO; + } + + if (pdata->cts_dev->rtdata.program_mode) { + pdata->spi_tx_buf[0] = dev_addr | 0x01; + memcpy(&pdata->spi_tx_buf[1], wbuf, wlen); + do { + ret = cts_spi_send_recv(pdata, rlen + 5, + pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) + mdelay(delay); + continue; + } + memcpy(rbuf, pdata->spi_rx_buf + 5, rlen); + return 0; + } while (++retries < retry); + } else { + do { + if (wlen != 0) { + pdata->spi_tx_buf[0] = dev_addr | 0x01; + pdata->spi_tx_buf[1] = wbuf[1]; + pdata->spi_tx_buf[2] = wbuf[0]; + put_unaligned_le16(rlen, &pdata->spi_tx_buf[3]); + crc = (u16) cts_crc32(pdata->spi_tx_buf, 5); + put_unaligned_le16(crc, &pdata->spi_tx_buf[5]); + ret = cts_spi_send_recv(pdata, 7, + pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) + mdelay(delay); + continue; + } + } + memset(pdata->spi_tx_buf, 0, 7); + pdata->spi_tx_buf[0] = dev_addr | 0x01; + udelay(100); + ret = cts_spi_send_recv(pdata, rlen + 2, + pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) + mdelay(delay); + continue; + } + memcpy(rbuf, pdata->spi_rx_buf, rlen); + crc = (u16) cts_crc32(pdata->spi_rx_buf, rlen); + if (get_unaligned_le16(&pdata->spi_rx_buf[rlen]) != crc) { + cts_err("SPI RX CRC error: rx_crc %04x != %04x", + get_unaligned_le16(&pdata->spi_rx_buf[rlen]), + crc); + continue; + } + return 0; + } while (++retries < retry); + } + if (retries >= retry) + cts_err("SPI read too much retry"); + + return -EIO; +} + +int cts_plat_spi_read_delay_idle(struct cts_platform_data *pdata, u8 dev_addr, + const u8 *wbuf, size_t wlen, void *rbuf, + size_t rlen, int retry, int delay, int idle) +{ + int ret = 0, retries = 0; + u16 crc; + + if (wlen > CFG_CTS_MAX_SPI_XFER_SIZE || + rlen > CFG_CTS_MAX_SPI_XFER_SIZE) { + cts_err("write/read too much data:wlen=%zu, rlen=%zu", wlen, + rlen); + return -E2BIG; + } + + if (pdata->cts_dev->rtdata.program_mode) { + pdata->spi_tx_buf[0] = dev_addr | 0x01; + memcpy(&pdata->spi_tx_buf[1], wbuf, wlen); + do { + ret = cts_spi_send_recv(pdata, rlen + 5, + pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) + mdelay(delay); + continue; + } + memcpy(rbuf, pdata->spi_rx_buf + 5, rlen); + return 0; + } while (++retries < retry); + } else { + do { + if (wlen != 0) { + pdata->spi_tx_buf[0] = dev_addr | 0x01; + pdata->spi_tx_buf[1] = wbuf[1]; + pdata->spi_tx_buf[2] = wbuf[0]; + put_unaligned_le16(rlen, &pdata->spi_tx_buf[3]); + crc = (u16) cts_crc32(pdata->spi_tx_buf, 5); + put_unaligned_le16(crc, &pdata->spi_tx_buf[5]); + ret = + cts_spi_send_recv(pdata, 7, + pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) + mdelay(delay); + continue; + } + } + memset(pdata->spi_tx_buf, 0, 7); + pdata->spi_tx_buf[0] = dev_addr | 0x01; + udelay(idle); + ret = cts_spi_send_recv(pdata, rlen + 2, + pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + if (delay) + mdelay(delay); + continue; + } + memcpy(rbuf, pdata->spi_rx_buf, rlen); + crc = (u16) cts_crc32(pdata->spi_rx_buf, rlen); + if (get_unaligned_le16(&pdata->spi_rx_buf[rlen]) != crc) + continue; + return 0; + } while (++retries < retry); + } + if (retries >= retry) + cts_err("cts_plat_spi_read error"); + + return -EIO; +} + +int cts_plat_is_normal_mode(struct cts_platform_data *pdata) +{ + struct chipone_ts_data *cts_data; + u16 fwid; + int ret; + /* u8 tx_buf[4] = { 0 }; */ + /* u32 addr; */ + + cts_set_normal_addr(pdata->cts_dev); + cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + /* + * addr = CTS_DEVICE_FW_REG_CHIP_TYPE; + * put_unaligned_be16(addr, tx_buf); + * ret = cts_plat_spi_read(pdata, CTS_DEV_NORMAL_MODE_SPIADDR, + * tx_buf, 2, + * &fwid, 2, 3, 10); + * fwid = be16_to_cpu(fwid); + */ + ret = pdata->cts_dev->ops->get_fw_id(pdata->cts_dev, &fwid); + + if (ret || !cts_is_fwid_valid(fwid)) + return false; + + return true; +} +#endif + +static void cts_plat_handle_irq(struct cts_platform_data *pdata) +{ + int ret; + + cts_dbg("Handle IRQ"); + + cts_lock_device(pdata->cts_dev); + ret = cts_irq_handler(pdata->cts_dev); + if (ret) + cts_err("Device handle IRQ failed %d", ret); + cts_unlock_device(pdata->cts_dev); +} + +static irqreturn_t cts_plat_irq_handler(int irq, void *dev_id) +{ + struct cts_platform_data *pdata; +#ifndef CONFIG_GENERIC_HARDIRQS + struct chipone_ts_data *cts_data; +#endif /* CONFIG_GENERIC_HARDIRQS */ + + cts_dbg("IRQ handler"); + + pdata = (struct cts_platform_data *)dev_id; + if (pdata == NULL) { + cts_err("IRQ handler with NULL dev_id"); + return IRQ_NONE; + } +#ifdef CONFIG_GENERIC_HARDIRQS + cts_plat_handle_irq(pdata); +#else /* CONFIG_GENERIC_HARDIRQS */ + cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + + if (queue_work(cts_data->workqueue, &pdata->ts_irq_work)) { + cts_dbg("IRQ queue work"); + cts_plat_disable_irq(pdata); + } else + cts_warn + ("IRQ handler queue work failed as already on the queue"); +#endif /* CONFIG_GENERIC_HARDIRQS */ + + return IRQ_HANDLED; +} + +#ifndef CONFIG_GENERIC_HARDIRQS +static void cts_plat_touch_dev_irq_work(struct work_struct *work) +{ + struct cts_platform_data *pdata = + container_of(work, struct cts_platform_data, ts_irq_work); + + cts_dbg("IRQ work"); + + cts_plat_handle_irq(pdata); + + cts_plat_enable_irq(pdata); +} +#endif /* CONFIG_GENERIC_HARDIRQS */ + +#ifdef CONFIG_CTS_OF +static int cts_plat_parse_dt(struct cts_platform_data *pdata, + struct device_node *dev_node) +{ + int ret = 0; + + cts_info("Parse device tree"); + + pdata->int_gpio = + of_get_named_gpio(dev_node, CFG_CTS_OF_INT_GPIO_NAME, 0); + if (!gpio_is_valid(pdata->int_gpio)) { + cts_err("Parse INT GPIO from dt failed %d", pdata->int_gpio); + pdata->int_gpio = -1; + } + cts_info(" %-12s: %d", "int gpio", pdata->int_gpio); + + pdata->irq = gpio_to_irq(pdata->int_gpio); + if (pdata->irq < 0) { + cts_err("Parse irq failed %d", ret); + return pdata->irq; + } + cts_info(" %-12s: %d", "irq num", pdata->irq); + +#ifdef CFG_CTS_HAS_RESET_PIN + pdata->rst_gpio = + of_get_named_gpio(dev_node, CFG_CTS_OF_RST_GPIO_NAME, 0); + if (!gpio_is_valid(pdata->rst_gpio)) { + cts_err("Parse RST GPIO from dt failed %d", pdata->rst_gpio); + pdata->rst_gpio = -1; + } + cts_info(" %-12s: %d", "rst gpio", pdata->rst_gpio); +#endif /* CFG_CTS_HAS_RESET_PIN */ + +#ifdef CFG_CTS_MANUAL_CS + pdata->cs_gpio = + of_get_named_gpio(dev_node, CFG_CTS_OF_CS_GPIO_NAME, 0); + if (!gpio_is_valid(pdata->cs_gpio)) { + cts_err("Parse CS GPIO from dt failed %d", pdata->cs_gpio); + pdata->cs_gpio = -1; + } + cts_info(" %-12s: %d", "cs gpio", pdata->cs_gpio); +#endif + + ret = of_property_read_u32(dev_node, CFG_CTS_OF_X_RESOLUTION_NAME, + &pdata->res_x); + if (ret) + cts_warn("Parse X resolution from dt failed %d", ret); + + cts_info(" %-12s: %d", "X resolution", pdata->res_x); + + ret = of_property_read_u32(dev_node, CFG_CTS_OF_Y_RESOLUTION_NAME, + &pdata->res_y); + if (ret) + cts_warn("Parse Y resolution from dt failed %d", ret); + + cts_info(" %-12s: %d", "Y resolution", pdata->res_y); + + if (of_property_read_u32 + (dev_node, "chipone,def-build-id", &pdata->build_id)) { + pdata->build_id = 0; + cts_info("chipone,build_id undefined.\n"); + } else + cts_info("chipone,build_id=0x%04X\n", pdata->build_id); + + if (of_property_read_u32 + (dev_node, "chipone,def-config-id", &pdata->config_id)) { + pdata->config_id = 0; + cts_info("chipone,config_id undefined.\n"); + } else + cts_info("chipone,config_id=0x%04X\n", pdata->config_id); + +#ifdef CFG_CTS_FW_UPDATE_SYS + ret = of_property_read_string(dev_node, CFG_CTS_OF_PANEL_SUPPLIER, + &pdata->panel_supplier); + if (ret) { + pdata->panel_supplier = NULL; + cts_warn("read panel supplier failed, ret=%d\n", ret); + } else + cts_info("panel supplier=%s", (char *)pdata->panel_supplier); +#endif + + return 0; +} +#endif /* CONFIG_CTS_OF */ + +#ifdef CFG_CTS_FORCE_UP +static void cts_plat_touch_event_timeout_work(struct work_struct *work) +{ + struct cts_platform_data *pdata = container_of + (work, struct cts_platform_data, touch_event_timeout_work.work); + + cts_warn("Touch event timeout work"); + + cts_plat_release_all_touch(pdata); +} +#endif + +int cts_plat_spi_setup(struct cts_platform_data *pdata) +{ + int ret; + + pdata->spi_client->mode = SPI_MODE_3; + pdata->spi_client->bits_per_word = 8; + if (pdata->spi_client->max_speed_hz > 6000000) + pdata->spi_client->max_speed_hz = 6000000; + + ret = spi_setup(pdata->spi_client); + if (ret) + cts_err("spi_setup err!"); + return 0; +} + +#ifdef CONFIG_CTS_I2C_HOST +int cts_init_platform_data(struct cts_platform_data *pdata, + struct i2c_client *i2c_client) +#else +int cts_init_platform_data(struct cts_platform_data *pdata, + struct spi_device *spi) +#endif +{ + struct input_dev *input_dev; + int ret = 0; + + cts_info("cts_init_platform_data Init"); + +#ifdef CONFIG_CTS_OF + { + struct device *dev; + +#ifdef CONFIG_CTS_I2C_HOST + dev = &i2c_client->dev; +#else + dev = &spi->dev; +#endif /* CONFIG_CTS_I2C_HOST */ + ret = cts_plat_parse_dt(pdata, dev->of_node); + if (ret) { + cts_err("Parse dt failed %d", ret); + return ret; + } + } +#endif /* CONFIG_CTS_OF */ + +#ifdef CONFIG_CTS_I2C_HOST + pdata->i2c_client = i2c_client; + pdata->i2c_client->irq = pdata->irq; +#else + pdata->spi_client = spi; + pdata->spi_client->irq = pdata->irq; +#endif /* CONFIG_CTS_I2C_HOST */ + spin_lock_init(&pdata->irq_lock); + + input_dev = input_allocate_device(); + if (input_dev == NULL) { + cts_err("Failed to allocate input device."); + return -ENOMEM; + } + + /** - Init input device */ + input_dev->name = CFG_CTS_DEVICE_NAME; + input_dev->name = CFG_CTS_DEVICE_NAME; +#ifdef CONFIG_CTS_I2C_HOST + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &pdata->i2c_client->dev; +#else + input_dev->id.bustype = BUS_SPI; + input_dev->dev.parent = &pdata->spi_client->dev; +#endif /* CONFIG_CTS_I2C_HOST */ + input_dev->evbit[0] = BIT_MASK(EV_SYN) | + BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); +#ifdef CFG_CTS_SWAP_XY + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, pdata->res_y, 0, + 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, pdata->res_x, 0, + 0); +#else /* CFG_CTS_SWAP_XY */ + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, pdata->res_x, 0, + 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, pdata->res_y, 0, + 0); +#endif /* CFG_CTS_SWAP_XY */ + + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, + CFG_CTS_MAX_TOUCH_NUM * 2, 0, 0); + + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + +#ifdef CONFIG_CTS_SLOTPROTOCOL + input_mt_init_slots(input_dev, CFG_CTS_MAX_TOUCH_NUM, 0); +#endif /* CONFIG_CTS_SLOTPROTOCOL */ + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + __set_bit(EV_ABS, input_dev->evbit); + input_set_drvdata(input_dev, pdata); + ret = input_register_device(input_dev); + if (ret) { + cts_err("Failed to register input device"); + return ret; + } + + pdata->ts_input_dev = input_dev; + +#if !defined(CONFIG_GENERIC_HARDIRQS) + INIT_WORK(&pdata->ts_irq_work, cts_plat_touch_dev_irq_work); +#endif /* CONFIG_GENERIC_HARDIRQS */ + +#ifdef CONFIG_CTS_VIRTUALKEY + { + u8 vkey_keymap[CFG_CTS_NUM_VKEY] = CFG_CTS_VKEY_KEYCODES; + + memcpy(pdata->vkey_keycodes, vkey_keymap, sizeof(vkey_keymap)); + pdata->vkey_num = CFG_CTS_NUM_VKEY; + } +#endif /* CONFIG_CTS_VIRTUALKEY */ + +#ifdef CFG_CTS_GESTURE + { + u8 gesture_keymap[CFG_CTS_NUM_GESTURE][2] = + CFG_CTS_GESTURE_KEYMAP; + + memcpy(pdata->gesture_keymap, gesture_keymap, + sizeof(gesture_keymap)); + pdata->gesture_num = CFG_CTS_NUM_GESTURE; + } +#endif /* CFG_CTS_GESTURE */ + +#ifdef CFG_CTS_FORCE_UP + INIT_DELAYED_WORK(&pdata->touch_event_timeout_work, + cts_plat_touch_event_timeout_work); +#endif + +#ifndef CONFIG_CTS_I2C_HOST + pdata->spi_speed = CFG_CTS_SPI_SPEED_KHZ; + cts_plat_spi_setup(pdata); +#endif + return 0; +} + +int cts_deinit_platform_data(struct cts_platform_data *pdata) +{ + cts_info("De-Init platform_data"); + input_unregister_device(pdata->ts_input_dev); + return 0; +} + +int cts_plat_request_resource(struct cts_platform_data *pdata) +{ + int ret; + + cts_info("Request resource"); + + ret = gpio_request_one(pdata->int_gpio, GPIOF_IN, + CFG_CTS_DEVICE_NAME "-int"); + if (ret) { + cts_err("Request INT gpio (%d) failed %d", pdata->int_gpio, + ret); + goto err_out; + } +#ifdef CFG_CTS_HAS_RESET_PIN + ret = gpio_request_one(pdata->rst_gpio, GPIOF_OUT_INIT_HIGH, + CFG_CTS_DEVICE_NAME "-rst"); + if (ret) { + cts_err("Request RST gpio (%d) failed %d", pdata->rst_gpio, + ret); + goto err_free_int; + } +#endif /* CFG_CTS_HAS_RESET_PIN */ + +#ifdef CFG_CTS_MANUAL_CS + ret = gpio_request_one(pdata->cs_gpio, GPIOF_OUT_INIT_HIGH, + CFG_CTS_DEVICE_NAME "-cs"); + if (ret) { + cts_err("Request CS gpio (%d) failed %d", pdata->cs_gpio, ret); + goto err_request_cs_gpio; + } +#endif + + return 0; + +#ifdef CFG_CTS_MANUAL_CS +err_request_cs_gpio: +#endif + +#ifdef CONFIG_CTS_REGULATOR +err_free_rst: +#endif /* CONFIG_CTS_REGULATOR */ +#ifdef CFG_CTS_HAS_RESET_PIN + gpio_free(pdata->rst_gpio); +err_free_int: +#endif /* CFG_CTS_HAS_RESET_PIN */ + gpio_free(pdata->int_gpio); +err_out: + return ret; +} + +void cts_plat_free_resource(struct cts_platform_data *pdata) +{ + cts_info("Free resource"); + + if (gpio_is_valid(pdata->int_gpio)) + gpio_free(pdata->int_gpio); + +#ifdef CFG_CTS_HAS_RESET_PIN + if (gpio_is_valid(pdata->rst_gpio)) + gpio_free(pdata->rst_gpio); + +#endif /* CFG_CTS_HAS_RESET_PIN */ +#ifdef CFG_CTS_MANUAL_CS + if (gpio_is_valid(pdata->cs_gpio)) + gpio_free(pdata->cs_gpio); + +#endif +} + +int cts_plat_request_irq(struct cts_platform_data *pdata) +{ + int ret; + + cts_info("Request IRQ"); + +#ifdef CONFIG_GENERIC_HARDIRQS + ret = request_threaded_irq(pdata->irq, NULL, + cts_plat_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + CFG_CTS_DRIVER_NAME, pdata); +#else /* CONFIG_GENERIC_HARDIRQS */ + ret = request_irq(pdata->irq, + cts_plat_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + CFG_CTS_DRIVER_NAME, pdata); +#endif /* CONFIG_GENERIC_HARDIRQS */ + if (ret) { + cts_err("Request IRQ failed %d", ret); + return ret; + } + + cts_plat_disable_irq(pdata); + + return 0; +} + +void cts_plat_free_irq(struct cts_platform_data *pdata) +{ + free_irq(pdata->irq, pdata); +} + +int cts_plat_enable_irq(struct cts_platform_data *pdata) +{ + unsigned long irqflags; + + cts_dbg("Enable IRQ"); + + if (pdata->irq > 0) { + spin_lock_irqsave(&pdata->irq_lock, irqflags); + if (pdata->irq_is_disable) { /* && !cts_is_device_suspended(pdata->chip)) */ + cts_dbg("Real enable IRQ"); + enable_irq(pdata->irq); + pdata->irq_is_disable = false; + } + spin_unlock_irqrestore(&pdata->irq_lock, irqflags); + + return 0; + } + + return -ENODEV; +} + +int cts_plat_disable_irq(struct cts_platform_data *pdata) +{ + unsigned long irqflags; + + cts_dbg("Disable IRQ"); + + if (pdata->irq > 0) { + spin_lock_irqsave(&pdata->irq_lock, irqflags); + if (!pdata->irq_is_disable) { + cts_dbg("Real disable IRQ"); + disable_irq_nosync(pdata->irq); + pdata->irq_is_disable = true; + } + spin_unlock_irqrestore(&pdata->irq_lock, irqflags); + + return 0; + } + + return -ENODEV; +} + +#ifdef CFG_CTS_HAS_RESET_PIN +int cts_plat_reset_device(struct cts_platform_data *pdata) +{ + /* !!!can not be modified */ + /* !!!can not be modified */ + /* !!!can not be modified */ + cts_info("Reset device"); + + gpio_set_value(pdata->rst_gpio, 1); + mdelay(1); + gpio_set_value(pdata->rst_gpio, 0); + mdelay(10); + gpio_set_value(pdata->rst_gpio, 1); + mdelay(40); + + return 0; +} + +int cts_plat_set_reset(struct cts_platform_data *pdata, int val) +{ + cts_info("Set Reset to %s", val ? "HIGH" : "LOW"); + if (val) + gpio_set_value(pdata->rst_gpio, 1); + else + gpio_set_value(pdata->rst_gpio, 0); + + return 0; +} +#endif /* CFG_CTS_HAS_RESET_PIN */ + +int cts_plat_get_int_pin(struct cts_platform_data *pdata) +{ + return gpio_get_value(pdata->int_gpio); +} + +int cts_plat_power_up_device(struct cts_platform_data *pdata) +{ + cts_info("Power up device"); + + return 0; +} + +int cts_plat_power_down_device(struct cts_platform_data *pdata) +{ + cts_info("Power down device"); + + return 0; +} + +int cts_plat_init_touch_device(struct cts_platform_data *pdata) +{ + cts_info("Init touch device"); + + return 0; +} + +void cts_plat_deinit_touch_device(struct cts_platform_data *pdata) +{ + cts_info("De-init touch device"); + +#ifndef CONFIG_GENERIC_HARDIRQS + if (work_pending(&pdata->ts_irq_work)) + cancel_work_sync(&pdata->ts_irq_work); +#endif /* CONFIG_GENERIC_HARDIRQS */ +} + +int cts_plat_process_touch_msg(struct cts_platform_data *pdata, + struct cts_device_touch_msg *msgs, int num) +{ + struct input_dev *input_dev = pdata->ts_input_dev; + int i; + int contact = 0; +#ifdef CONFIG_CTS_SLOTPROTOCOL + static unsigned char finger_last[CFG_CTS_MAX_TOUCH_NUM] = { 0 }; + unsigned char finger_current[CFG_CTS_MAX_TOUCH_NUM] = { 0 }; +#endif + + cts_dbg("Process touch %d msgs", num); + if (num == 0 || num > CFG_CTS_MAX_TOUCH_NUM) + return 0; + + for (i = 0; i < num; i++) { + u16 x, y; + + x = le16_to_cpu(msgs[i].x); + y = le16_to_cpu(msgs[i].y); + +#ifdef CFG_CTS_SWAP_XY + swap(x, y); +#endif /* CFG_CTS_SWAP_XY */ +#ifdef CFG_CTS_WRAP_X + x = wrap(pdata->res_x, x); +#endif /* CFG_CTS_WRAP_X */ +#ifdef CFG_CTS_WRAP_Y + y = wrap(pdata->res_y, y); +#endif /* CFG_CTS_WRAP_Y */ + cts_dbg(" Process touch msg[%d]: id[%u] ev=%u x=%u y=%u p=%u", + i, msgs[i].id, msgs[i].event, x, y, msgs[i].pressure); + if (msgs[i].event == CTS_DEVICE_TOUCH_EVENT_DOWN + || msgs[i].event == CTS_DEVICE_TOUCH_EVENT_MOVE + || msgs[i].event == CTS_DEVICE_TOUCH_EVENT_STAY) { + if (msgs[i].id < CFG_CTS_MAX_TOUCH_NUM) + finger_current[msgs[i].id] = 1; + } +#ifdef CONFIG_CTS_SLOTPROTOCOL + /* input_mt_slot(input_dev, msgs[i].id); */ + switch (msgs[i].event) { + case CTS_DEVICE_TOUCH_EVENT_DOWN: + case CTS_DEVICE_TOUCH_EVENT_MOVE: + case CTS_DEVICE_TOUCH_EVENT_STAY: + contact++; + input_mt_slot(input_dev, msgs[i].id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, + true); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + msgs[i].pressure); + input_report_abs(input_dev, ABS_MT_PRESSURE, + msgs[i].pressure); + break; + + case CTS_DEVICE_TOUCH_EVENT_UP: + /* input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); */ + break; + + default: + cts_warn + ("Process touch msg with unknwon event %u id %u", + msgs[i].event, msgs[i].id); + break; + } +#else /* CONFIG_CTS_SLOTPROTOCOL */ + /** + * If the driver reports one of BTN_TOUCH or ABS_PRESSURE + * in addition to the ABS_MT events, the last SYN_MT_REPORT event + * may be omitted. Otherwise, the last SYN_REPORT will be dropped + * by the input core, resulting in no zero-contact event + * reaching userland. + */ + switch (msgs[i].event) { + case CTS_DEVICE_TOUCH_EVENT_DOWN: + case CTS_DEVICE_TOUCH_EVENT_MOVE: + case CTS_DEVICE_TOUCH_EVENT_STAY: + contact++; + input_report_abs(input_dev, ABS_MT_PRESSURE, + msgs[i].pressure); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + msgs[i].pressure); + input_report_key(input_dev, BTN_TOUCH, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_mt_sync(input_dev); + break; + + case CTS_DEVICE_TOUCH_EVENT_UP: + break; + default: + cts_warn + ("Process touch msg with unknwon event %u id %u", + msgs[i].event, msgs[i].id); + break; + } +#endif /* CONFIG_CTS_SLOTPROTOCOL */ + } + +#ifdef CONFIG_CTS_SLOTPROTOCOL + for (i = 0; i < CFG_CTS_MAX_TOUCH_NUM; i++) { + if (finger_last[i] != 0 && finger_current[i] == 0) { + input_mt_slot(input_dev, i); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, + false); + } + finger_last[i] = finger_current[i]; + } + input_report_key(input_dev, BTN_TOUCH, contact > 0); +#else + if (contact == 0) { + input_report_key(input_dev, BTN_TOUCH, 0); + input_mt_sync(input_dev); + } +#endif + input_sync(input_dev); +#ifdef CFG_CTS_FORCE_UP + if (contact) { + struct chipone_ts_data *cts_data; + + cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, + cts_dev); + + if (delayed_work_pending(&pdata->touch_event_timeout_work)) { + mod_delayed_work(cts_data->workqueue, + &pdata->touch_event_timeout_work, + msecs_to_jiffies(100)); + } else { + queue_delayed_work(cts_data->workqueue, + &pdata->touch_event_timeout_work, + msecs_to_jiffies(100)); + } + } else { + cancel_delayed_work_sync(&pdata->touch_event_timeout_work); + } +#endif + return 0; +} + +int cts_plat_release_all_touch(struct cts_platform_data *pdata) +{ + struct input_dev *input_dev = pdata->ts_input_dev; + +#if defined(CONFIG_CTS_SLOTPROTOCOL) + int id; +#endif /* CONFIG_CTS_SLOTPROTOCOL */ + + cts_info("Release all touch"); + +#ifdef CONFIG_CTS_SLOTPROTOCOL + for (id = 0; id < CFG_CTS_MAX_TOUCH_NUM; id++) { + input_mt_slot(input_dev, id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + } + input_report_key(input_dev, BTN_TOUCH, 0); +#else + input_report_key(input_dev, BTN_TOUCH, 0); + input_mt_sync(input_dev); +#endif /* CONFIG_CTS_SLOTPROTOCOL */ + input_sync(input_dev); + + return 0; +} + +#ifdef CONFIG_CTS_VIRTUALKEY +int cts_plat_init_vkey_device(struct cts_platform_data *pdata) +{ + int i; + + cts_info("Init VKey"); + + pdata->vkey_state = 0; + + for (i = 0; i < pdata->vkey_num; i++) { + input_set_capability(pdata->ts_input_dev, + EV_KEY, pdata->vkey_keycodes[i]); + } + + return 0; +} + +void cts_plat_deinit_vkey_device(struct cts_platform_data *pdata) +{ + cts_info("De-init VKey"); + + pdata->vkey_state = 0; +} + +int cts_plat_process_vkey(struct cts_platform_data *pdata, u8 vkey_state) +{ + u8 event; + int i; + + event = pdata->vkey_state ^ vkey_state; + + cts_dbg("Process vkey state=0x%02x, event=0x%02x", vkey_state, event); + + for (i = 0; i < pdata->vkey_num; i++) { + input_report_key(pdata->ts_input_dev, + pdata->vkey_keycodes[i], + vkey_state & BIT(i) ? 1 : 0); + } + + pdata->vkey_state = vkey_state; + + return 0; +} + +int cts_plat_release_all_vkey(struct cts_platform_data *pdata) +{ + int i; + + cts_info("Release all vkeys"); + + for (i = 0; i < pdata->vkey_num; i++) { + if (pdata->vkey_state & BIT(i)) { + input_report_key(pdata->ts_input_dev, + pdata->vkey_keycodes[i], 0); + } + } + + pdata->vkey_state = 0; + + return 0; +} +#endif /* CONFIG_CTS_VIRTUALKEY */ + +#ifdef CFG_CTS_GESTURE +int cts_plat_enable_irq_wake(struct cts_platform_data *pdata) +{ + int ret; + + cts_info("Enable IRQ wake"); + + if (pdata->irq > 0) { + ret = enable_irq_wake(pdata->irq); + if (ret < 0) { + cts_err("Enable irq wake failed"); + return -EINVAL; + } + pdata->irq_wake_enabled = true; + return 0; + } + + cts_warn("Enable irq wake while irq invalid %d", pdata->irq); + return -ENODEV; +} + +int cts_plat_disable_irq_wake(struct cts_platform_data *pdata) +{ + int ret; + + cts_info("Disable IRQ wake"); + + if (pdata->irq > 0) { + ret = disable_irq_wake(pdata->irq); + if (ret < 0) { + cts_warn("Disable irq wake while already disabled"); + return -EINVAL; + } + pdata->irq_wake_enabled = false; + return 0; + } + + cts_warn("Disable irq wake while irq invalid %d", pdata->irq); + return -ENODEV; +} + +int cts_plat_init_gesture(struct cts_platform_data *pdata) +{ + int i; + + cts_info("Init gesture"); + + /* TODO: If system will issure enable/disable command, comment following line. */ + /* cts_enable_gesture_wakeup(pdata->cts_dev); */ + + for (i = 0; i < pdata->gesture_num; i++) { + input_set_capability(pdata->ts_input_dev, EV_KEY, + pdata->gesture_keymap[i][1]); + } + + return 0; +} + +void cts_plat_deinit_gesture(struct cts_platform_data *pdata) +{ + cts_info("De-init gesture"); +} + +int cts_plat_process_gesture_info(struct cts_platform_data *pdata, + struct cts_device_gesture_info *gesture_info) +{ + int i; + + cts_info("Process gesture, id=0x%02x", gesture_info->gesture_id); + +#if defined(CFG_CTS_GESTURE_REPORT_KEY) + for (i = 0; i < CFG_CTS_NUM_GESTURE; i++) { + if (gesture_info->gesture_id == pdata->gesture_keymap[i][0]) { + cts_info("Report key[%u]", pdata->gesture_keymap[i][1]); +/* input_report_key(pdata->ts_input_dev, + pdata->gesture_keymap[i][1], 1); + input_sync(pdata->ts_input_dev); + + input_report_key(pdata->ts_input_dev, + pdata->gesture_keymap[i][1], 0); + input_sync(pdata->ts_input_dev); +*/ + input_report_key(chipone_ts->sensor_pdata->input_sensor_dev, KEY_F1, 1); + input_sync(chipone_ts->sensor_pdata->input_sensor_dev); + input_report_key(chipone_ts->sensor_pdata->input_sensor_dev, KEY_F1, 0); + input_sync(chipone_ts->sensor_pdata->input_sensor_dev); + return 0; + } + } +#endif /* CFG_CTS_GESTURE_REPORT_KEY */ + + cts_warn("Process unrecognized gesture id=%u", + gesture_info->gesture_id); + + return -EINVAL; +} + +#endif /* CFG_CTS_GESTURE */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_platform.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_platform.h new file mode 100644 index 000000000000..6eeeb2fdd339 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_platform.h @@ -0,0 +1,273 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_PLATFORM_H +#define CTS_PLATFORM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* #include */ +#include + +#ifdef CONFIG_OF +#include +#include +#include +#endif /* CONFIG_OF */ + +#include +#include + +#include +#include + +#include "cts_config.h" +#include "cts_core.h" + +extern bool cts_show_debug_log; + +#ifndef LOG_TAG +#define LOG_TAG "" +#endif /* LOG_TAG */ + +enum cts_driver_log_level { + CTS_DRIVER_LOG_ERROR, + CTS_DRIVER_LOG_WARN, + CTS_DRIVER_LOG_INFO, + CTS_DRIVER_LOG_DEBUG, +}; + +extern int cts_start_driver_log_redirect(const char *filepath, bool append_to_file, + char *log_buffer, int log_buf_size, int log_level); +extern void cts_stop_driver_log_redirect(void); +extern int cts_get_driver_log_redirect_size(void); +extern void cts_log(int level, const char *fmt, ...); + +#define cts_err(fmt, ...) \ + cts_log(CTS_DRIVER_LOG_ERROR, "CTS-" LOG_TAG " " fmt"\n", ##__VA_ARGS__) +#define cts_warn(fmt, ...) \ + cts_log(CTS_DRIVER_LOG_WARN, "CTS-" LOG_TAG " " fmt"\n", ##__VA_ARGS__) +#define cts_info(fmt, ...) \ + cts_log(CTS_DRIVER_LOG_INFO, "CTS-" LOG_TAG " " fmt"\n", ##__VA_ARGS__) +#define cts_dbg(fmt, ...) \ + cts_log(CTS_DRIVER_LOG_DEBUG, "CTS-" LOG_TAG " " fmt"\n", ##__VA_ARGS__) + + +struct cts_device; +struct cts_device_touch_msg; +struct cts_device_gesture_info; + +struct cts_platform_data { + int irq; + int int_gpio; +#ifdef CFG_CTS_HAS_RESET_PIN + int rst_gpio; +#endif /* CFG_CTS_HAS_RESET_PIN */ + +#ifdef CFG_CTS_MANUAL_CS + int cs_gpio; +#endif + + u32 res_x; + u32 res_y; + +#ifdef CONFIG_CTS_VIRTUALKEY + u8 vkey_num; + u8 vkey_state; + u8 vkey_keycodes[CFG_CTS_MAX_VKEY_NUM]; +#endif /* CONFIG_CTS_VIRTUALKEY */ + +#ifdef CFG_CTS_FW_UPDATE_SYS + const char *panel_supplier; +#endif + u32 build_id; + u32 config_id; + + struct cts_device *cts_dev; + + struct input_dev *ts_input_dev; + +#ifndef CONFIG_GENERIC_HARDIRQS + struct work_struct ts_irq_work; +#endif /* CONFIG_GENERIC_HARDIRQS */ + + struct spinlock irq_lock; + bool irq_is_disable; + +#ifdef CFG_CTS_GESTURE + u8 gesture_num; + u8 gesture_keymap[CFG_CTS_NUM_GESTURE][2]; + bool irq_wake_enabled; +#endif /* CFG_CTS_GESTURE */ + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + struct notifier_block fb_notifier; +#endif /* CONFIG_CTS_PM_FB_NOTIFIER */ + +#ifdef CFG_CTS_FORCE_UP + struct delayed_work touch_event_timeout_work; +#endif + +#ifdef CFG_CTS_FW_LOG_REDIRECT + u8 fw_log_buf[CTS_FW_LOG_BUF_LEN]; +#endif + +#ifdef CONFIG_CTS_I2C_HOST + struct i2c_client *i2c_client; + u8 i2c_fifo_buf[CFG_CTS_MAX_I2C_XFER_SIZE]; +#else + struct spi_device *spi_client; + u8 spi_cache_buf[ALIGN(CFG_CTS_MAX_SPI_XFER_SIZE + 10, 4)]; + u8 spi_rx_buf[ALIGN(CFG_CTS_MAX_SPI_XFER_SIZE + 10, 4)]; + u8 spi_tx_buf[ALIGN(CFG_CTS_MAX_SPI_XFER_SIZE + 10, 4)]; + u32 spi_speed; +#endif /* CONFIG_CTS_I2C_HOST */ +}; + +#ifdef CONFIG_CTS_I2C_HOST +extern size_t cts_plat_get_max_i2c_xfer_size(struct cts_platform_data *pdata); +extern u8 *cts_plat_get_i2c_xfer_buf(struct cts_platform_data *pdata, + size_t xfer_size); +extern int cts_plat_i2c_write(struct cts_platform_data *pdata, u8 i2c_addr, + const void *src, size_t len, int retry, + int delay); +extern int cts_plat_i2c_read(struct cts_platform_data *pdata, u8 i2c_addr, + const u8 *wbuf, size_t wlen, void *rbuf, + size_t rlen, int retry, int delay); +extern int cts_plat_is_i2c_online(struct cts_platform_data *pdata, u8 i2c_addr); +#else /* CONFIG_CTS_I2C_HOST */ +extern size_t cts_plat_get_max_spi_xfer_size(struct cts_platform_data *pdata); +extern u8 *cts_plat_get_spi_xfer_buf(struct cts_platform_data *pdata, + size_t xfer_size); +extern int cts_plat_spi_write(struct cts_platform_data *pdata, u8 i2c_addr, + const void *src, size_t len, int retry, + int delay); +extern int cts_plat_spi_read(struct cts_platform_data *pdata, u8 i2c_addr, + const u8 *wbuf, size_t wlen, void *rbuf, + size_t rlen, int retry, int delay); +extern int cts_plat_spi_read_delay_idle(struct cts_platform_data *pdata, + u8 dev_addr, const u8 *wbuf, + size_t wlen, void *rbuf, size_t rlen, + int retry, int delay, int idle); +extern int cts_spi_send_recv(struct cts_platform_data *pdata, size_t len, + u8 *tx_buffer, u8 *rx_buffer); +#endif /* CONFIG_CTS_I2C_HOST */ + +#ifdef CONFIG_CTS_I2C_HOST +extern int cts_init_platform_data(struct cts_platform_data *pdata, + struct i2c_client *i2c_client); +#else +extern int cts_plat_is_normal_mode(struct cts_platform_data *pdata); +extern int cts_init_platform_data(struct cts_platform_data *pdata, + struct spi_device *spi); +#endif + +extern int cts_deinit_platform_data(struct cts_platform_data *pdata); +extern int cts_plat_request_resource(struct cts_platform_data *pdata); +extern void cts_plat_free_resource(struct cts_platform_data *pdata); + +extern int cts_plat_request_irq(struct cts_platform_data *pdata); +extern void cts_plat_free_irq(struct cts_platform_data *pdata); +extern int cts_plat_enable_irq(struct cts_platform_data *pdata); +extern int cts_plat_disable_irq(struct cts_platform_data *pdata); + +#ifdef CFG_CTS_HAS_RESET_PIN +extern int cts_plat_reset_device(struct cts_platform_data *pdata); +#else /* CFG_CTS_HAS_RESET_PIN */ +static inline int cts_plat_reset_device(struct cts_platform_data *pdata) +{ + return 0; +} +#endif /* CFG_CTS_HAS_RESET_PIN */ + +extern int cts_plat_init_touch_device(struct cts_platform_data *pdata); +extern void cts_plat_deinit_touch_device(struct cts_platform_data *pdata); +extern int cts_plat_process_touch_msg(struct cts_platform_data *pdata, + struct cts_device_touch_msg *msgs, + int num); +extern int cts_plat_release_all_touch(struct cts_platform_data *pdata); + +#ifdef CONFIG_CTS_VIRTUALKEY +extern int cts_plat_init_vkey_device(struct cts_platform_data *pdata); +extern void cts_plat_deinit_vkey_device(struct cts_platform_data *pdata); +extern int cts_plat_process_vkey(struct cts_platform_data *pdata, + u8 vkey_state); +extern int cts_plat_release_all_vkey(struct cts_platform_data *pdata); +#else /* CONFIG_CTS_VIRTUALKEY */ +static inline int cts_plat_init_vkey_device(struct cts_platform_data *pdata) +{ + return 0; +} + +static inline void cts_plat_deinit_vkey_device(struct cts_platform_data *pdata) +{ +} + +static inline int cts_plat_process_vkey(struct cts_platform_data *pdata, + u8 vkey_state) +{ + return 0; +} + +static inline int cts_plat_release_all_vkey(struct cts_platform_data *pdata) +{ + return 0; +} +#endif /* CONFIG_CTS_VIRTUALKEY */ + +#ifdef CFG_CTS_GESTURE +extern int cts_plat_enable_irq_wake(struct cts_platform_data *pdata); +extern int cts_plat_disable_irq_wake(struct cts_platform_data *pdata); + +extern int cts_plat_init_gesture(struct cts_platform_data *pdata); +extern void cts_plat_deinit_gesture(struct cts_platform_data *pdata); +extern int cts_plat_process_gesture_info(struct cts_platform_data *pdata, + struct cts_device_gesture_info + *gesture_info); +#else /* CFG_CTS_GESTURE */ +static inline int cts_plat_init_gesture(struct cts_platform_data *pdata) +{ + return 0; +} + +static inline void cts_plat_deinit_gesture(struct cts_platform_data *pdata) +{ +} +#endif /* CFG_CTS_GESTURE */ + +#ifdef CFG_CTS_FW_LOG_REDIRECT +extern size_t cts_plat_get_max_fw_log_size(struct cts_platform_data *pdata); +extern u8 *cts_plat_get_fw_log_buf(struct cts_platform_data *pdata, + size_t size); +#endif + +extern int cts_plat_set_reset(struct cts_platform_data *pdata, int val); +extern int cts_plat_get_int_pin(struct cts_platform_data *pdata); + +#ifdef CFG_CTS_MANUAL_CS +extern int cts_plat_set_cs(struct cts_platform_data *pdata, int val); +#endif +#endif /* CTS_PLATFORM_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sfctrl.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sfctrl.h new file mode 100644 index 000000000000..3e296fe20124 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sfctrl.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_SFCTRL_H +#define CTS_SFCTRL_H + +#include "cts_core.h" +#include "cts_spi_flash.h" + +struct cts_device; + +struct cts_sfctrl_ops { + int (*rdid)(const struct cts_device *cts_dev, u32 *id); + int (*se)(const struct cts_device *cts_dev, u32 sector_addr); + int (*be)(const struct cts_device *cts_dev, u32 sector_addr); + int (*read)(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t len); + int (*read_to_sram)(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t len); + int (*program)(const struct cts_device *cts_dev, + u32 flash_addr, const void *src, size_t len); + int (*program_from_sram)(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t len); + int (*calc_sram_crc)(const struct cts_device *cts_dev, + u32 sram_addr, size_t len, u32 *crc); + int (*calc_flash_crc)(const struct cts_device *cts_dev, + u32 flash_addr, size_t len, u32 *crc); + +}; + +struct cts_sfctrl { + u32 reg_base; + u32 xchg_sram_base; + size_t xchg_sram_size; + const struct cts_sfctrl_ops *ops; +}; + +extern const struct cts_sfctrl_ops cts_sfctrlv2_ops; + +#endif /* CTS_SFCTRL_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sfctrlv2.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sfctrlv2.c new file mode 100644 index 000000000000..019c1ceb42ec --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sfctrlv2.c @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "SFCtrl" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sfctrl.h" + +#define rSFCTRLv2_CMD_SEL (0x0000) +#define rSFCTRLv2_FLASH_ADDR (0x0004) +#define rSFCTRLv2_SRAM_ADDR (0x0008) +#define rSFCTRLv2_DATA_LENGTH (0x000C) +#define rSFCTRLv2_START_DEXC (0x0010) +#define rSFCTRLv2_RELEASE_FLASH (0x0014) +#define rSFCTRLv2_HW_STATE (0x0018) +#define rSFCTRLv2_CRC_RESULT (0x001C) +#define rSFCTRLv2_SRAM_CRC_START (0x0020) +#define rSFCTRLv2_FLASH_CRC_START (0x0022) +#define rSFCTRLv2_SF_BUSY (0x0024) + +/** Constants for register @ref rSFCTRLv2_CMD_SEL */ +enum sfctrlv2_cmd { + SFCTRLv2_CMD_FAST_READ = 0x01u, + SFCTRLv2_CMD_SE = 0x02u, + SFCTRLv2_CMD_BE = 0x03u, + SFCTRLv2_CMD_PP = 0x04u, + SFCTRLv2_CMD_RDSR = 0x05u, + SFCTRLv2_CMD_RDID = 0x06u +}; + +/** SPI flash controller v2 operation flags. */ +enum sfctrlv2_opflags { + SFCTRLv2_OPFLAG_READ = BIT(0), + SFCTRLv2_OPFLAG_SET_FLASH_ADDR = BIT(1), + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG = BIT(2), + SFCTRLv2_OPFLAG_SET_DATA_LENGTH = BIT(3), + SFCTRLv2_OPFLAG_WAIT_WIP_CLR = BIT(4), +}; + +#define SFCTRLv2_CMD_RDID_FLAGS \ + (SFCTRLv2_OPFLAG_READ | \ + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG) + +#define SFCTRLv2_CMD_RDSR_FLAGS \ + (SFCTRLv2_OPFLAG_READ | \ + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG) + +#define SFCTRLv2_CMD_SE_FLAGS \ + (SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \ + SFCTRLv2_OPFLAG_WAIT_WIP_CLR) + +#define SFCTRLv2_CMD_BE_FLAGS \ + (SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \ + SFCTRLv2_OPFLAG_WAIT_WIP_CLR) + +#define SFCTRLv2_CMD_PP_FLAGS \ + (SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \ + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG | \ + SFCTRLv2_OPFLAG_SET_DATA_LENGTH | \ + SFCTRLv2_OPFLAG_WAIT_WIP_CLR) + +#define SFCTRLv2_CMD_FAST_READ_FLAGS \ + (SFCTRLv2_OPFLAG_READ | \ + SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \ + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG | \ + SFCTRLv2_OPFLAG_SET_DATA_LENGTH) + +#define sfctrl_reg_addr(cts_dev, offset) \ + ((cts_dev)->hwdata->sfctrl->reg_base + offset) + +#define DEFINE_SFCTRL_REG_ACCESS_FUNC(access_type, data_type) \ + static inline int sfctrl_reg_ ## access_type(const struct cts_device *cts_dev, \ + u32 reg, data_type data) { \ + return cts_hw_reg_ ## access_type(cts_dev, sfctrl_reg_addr(cts_dev, reg), data); \ + } + +DEFINE_SFCTRL_REG_ACCESS_FUNC(writeb_relaxed, u8) + DEFINE_SFCTRL_REG_ACCESS_FUNC(writew_relaxed, u16) + DEFINE_SFCTRL_REG_ACCESS_FUNC(writel_relaxed, u32) + DEFINE_SFCTRL_REG_ACCESS_FUNC(readb_relaxed, u8 *) + DEFINE_SFCTRL_REG_ACCESS_FUNC(readw_relaxed, u16 *) + DEFINE_SFCTRL_REG_ACCESS_FUNC(readl_relaxed, u32 *) +#define sfctrl_write_reg_check_ret(access_type, reg, val) \ + do { \ + int ret; \ + cts_dbg("Write " #reg " to 0x%x", val); \ + ret = sfctrl_reg_ ## access_type(cts_dev, reg, val); \ + if (ret) { \ + cts_err("Write " #reg " failed %d", ret); \ + return ret; \ + } \ + } while (0) +#define sfctrl_read_reg_check_ret(access_type, reg, val) \ + do { \ + int ret; \ + cts_dbg("Read " #reg ""); \ + ret = sfctrl_reg_ ## access_type(cts_dev, reg, val); \ + if (ret) { \ + cts_err("Read " #reg " failed %d", ret); \ + return ret; \ + } \ + } while (0) +static int wait_sfctrl_xfer_comp(const struct cts_device *cts_dev) +{ + int retries = 0; + u8 status; + + do { + sfctrl_read_reg_check_ret(readb_relaxed, rSFCTRLv2_SF_BUSY, + &status); + if (status == 0) + return 0; + mdelay(20); + } while (status && retries++ < 50); + + return -ETIMEDOUT; +} + +static int sfctrlv2_rdsr(const struct cts_device *cts_dev, u8 *status) +{ +#define RDSR_XCHG_SRAM_ADDR (cts_dev->hwdata->sram_size - 1) + + int ret; + + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_CMD_SEL, + SFCTRLv2_CMD_RDSR); + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_SRAM_ADDR, + RDSR_XCHG_SRAM_ADDR); + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_START_DEXC, 1); + + ret = wait_sfctrl_xfer_comp(cts_dev); + if (ret != 0) { + cts_err("Wait sfctrl ready failed %d", ret); + return ret; + } + + ret = cts_sram_readb(cts_dev, RDSR_XCHG_SRAM_ADDR, status); + if (ret) { + cts_err("Read exchange sram failed %d", ret); + return ret; + } +#undef RDSR_XCHG_SRAM_ADDR + + return 0; +} + +static int wait_flash_wip_clear(const struct cts_device *cts_dev) +{ +#define FLASH_SR_WIP BIT(0) /*!< Write in progress */ + + int ret, retries = 0; + u8 status; + + do { + ret = sfctrlv2_rdsr(cts_dev, &status); + if (ret) { + cts_err("Read flash status register failed %d", ret); + return ret; + } + + if (status & FLASH_SR_WIP) + mdelay(1); + else + return 0; + } while (status & FLASH_SR_WIP && retries++ < 1000); + + return -ETIMEDOUT; +#undef FLASH_SR_WIP +} + +static int sfctrlv2_transfer(const struct cts_device *cts_dev, + u8 cmd, void *data, u32 flash_addr, u32 sram_addr, + size_t size, u32 flags) +{ + int ret; + + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_CMD_SEL, cmd); + + if (flags & SFCTRLv2_OPFLAG_SRAM_DATA_XCHG) { + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_SRAM_ADDR, + sram_addr); + +/** - Write data to exchange SRAM if operation is write and + * data != NULL(data not in SRAM) + */ + if ((!(flags & SFCTRLv2_OPFLAG_READ)) && data) { + ret = cts_sram_writesb(cts_dev, sram_addr, data, size); + if (ret) { + cts_err + ("Write data to exchange sram 0x%06x size %zu failed %d", + sram_addr, size, ret); + return ret; + } + } + } + + if (flags & SFCTRLv2_OPFLAG_SET_FLASH_ADDR) { + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_FLASH_ADDR, + flash_addr); + } + if (flags & SFCTRLv2_OPFLAG_SET_DATA_LENGTH) { + sfctrl_write_reg_check_ret(writel_relaxed, + rSFCTRLv2_DATA_LENGTH, (u32) size); + } + + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_START_DEXC, 1); + + ret = wait_sfctrl_xfer_comp(cts_dev); + if (ret != 0) { + cts_err("Wait sfctrl ready failed %d", ret); + return ret; + } + + ret = wait_flash_wip_clear(cts_dev); + if (flags & SFCTRLv2_OPFLAG_WAIT_WIP_CLR && ret != 0) { + cts_err("Wait WIP clear failed %d", ret); + return ret; + } + + if ((flags & SFCTRLv2_OPFLAG_READ) && data) { + ret = cts_sram_readsb(cts_dev, sram_addr, data, size); + if (ret) { + cts_err + ("Read data from exchange sram 0x%06x size %zu failed %d", + sram_addr, size, ret); + return ret; + } + } + + return 0; +} + +static int sfctrlv2_rdid(const struct cts_device *cts_dev, u32 *id) +{ + int ret; + u8 buf[4]; + + ret = sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_RDID, buf, + 0, cts_dev->hwdata->sfctrl->xchg_sram_base, 3, + SFCTRLv2_CMD_RDID_FLAGS); + *id = ret ? 0 : get_unaligned_be24(buf); + + return ret; +} + +static int sfctrlv2_se(const struct cts_device *cts_dev, u32 sector_addr) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_SE, NULL, + sector_addr, 0, 0, SFCTRLv2_CMD_SE_FLAGS); +} + +static int sfctrlv2_be(const struct cts_device *cts_dev, u32 block_addr) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_BE, NULL, + block_addr, 0, 0, SFCTRLv2_CMD_BE_FLAGS); +} + +static int sfctrlv2_pp(const struct cts_device *cts_dev, + u32 flash_addr, const void *src, size_t size) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_PP, (void *)src, + flash_addr, + cts_dev->hwdata->sfctrl->xchg_sram_base, size, + SFCTRLv2_CMD_PP_FLAGS); +} + +static int sfctrlv2_program_flash_from_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, + size_t len) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_PP, NULL, + flash_addr, sram_addr, len, + SFCTRLv2_CMD_PP_FLAGS); +} + +static int sfctrlv2_read(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t size) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_FAST_READ, dst, + flash_addr, + cts_dev->hwdata->sfctrl->xchg_sram_base, size, + SFCTRLv2_CMD_FAST_READ_FLAGS); +} + +static int sfctrlv2_read_flash_to_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, + size_t size) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_FAST_READ, NULL, + flash_addr, sram_addr, size, + SFCTRLv2_CMD_FAST_READ_FLAGS); +} + +int sfctrlv2_calc_sram_crc(const struct cts_device *cts_dev, + u32 sram_addr, size_t size, u32 *crc) +{ + int ret; + + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_SRAM_ADDR, + sram_addr); + mdelay(1); + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_DATA_LENGTH, + (u32) size); + mdelay(1); + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_SRAM_CRC_START, 1); + mdelay(10); + + ret = wait_sfctrl_xfer_comp(cts_dev); + if (ret != 0) { + cts_err("Wait sfctrl ready failed %d", ret); + return ret; + } + + sfctrl_read_reg_check_ret(readl_relaxed, rSFCTRLv2_CRC_RESULT, crc); + + return 0; +} + +int sfctrlv2_calc_flash_crc(const struct cts_device *cts_dev, + u32 flash_addr, size_t size, u32 *crc) +{ + int ret; + + cts_info("Calc crc from flash 0x%06x size %zu", flash_addr, size); + + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_FLASH_ADDR, + flash_addr); + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_DATA_LENGTH, + (u32) size); + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_FLASH_CRC_START, + 1); + + ret = wait_sfctrl_xfer_comp(cts_dev); + if (ret != 0) { + cts_err("Wait sfctrl ready failed %d", ret); + return ret; + } + + sfctrl_read_reg_check_ret(readl_relaxed, rSFCTRLv2_CRC_RESULT, crc); + + return 0; +} + +const struct cts_sfctrl_ops cts_sfctrlv2_ops = { + .rdid = sfctrlv2_rdid, + .se = sfctrlv2_se, + .be = sfctrlv2_be, + .read = sfctrlv2_read, + .read_to_sram = sfctrlv2_read_flash_to_sram, + .program = sfctrlv2_pp, + .program_from_sram = sfctrlv2_program_flash_from_sram, + .calc_sram_crc = sfctrlv2_calc_sram_crc, + .calc_flash_crc = sfctrlv2_calc_flash_crc, +}; diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_spi_flash.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_spi_flash.c new file mode 100644 index 000000000000..0b291eef2f60 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_spi_flash.c @@ -0,0 +1,532 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "Flash" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sfctrl.h" +#include "cts_spi_flash.h" + +/* NOTE: double check command sets and memory organization when you add + * more flash chips. This current list focusses on newer chips, which + * have been converging on command sets which including JEDEC ID. + */ +static int probe_flash(struct cts_device *cts_dev) +{ + /* for no-flash project */ + return -ENODEV; +} + +/** Make sure sector addr is sector aligned && < flash total size */ +static int erase_sector_retry(const struct cts_device *cts_dev, + u32 sector_addr, int retry) +{ + int ret, retries; + + cts_info(" Erase sector 0x%06x", sector_addr); + + retries = 0; + do { + retries++; + ret = cts_dev->hwdata->sfctrl->ops->se(cts_dev, sector_addr); + if (ret) { + cts_err("Erase sector 0x%06x failed %d retries %d", + sector_addr, ret, retries); + continue; + } + } while (retries < retry); + + return ret; +} + +/** Make sure sector addr is sector aligned && < flash total size */ +static inline int erase_sector(const struct cts_device *cts_dev, + u32 sector_addr) +{ + return erase_sector_retry(cts_dev, sector_addr, + CTS_FLASH_ERASE_DEFAULT_RETRY); +} + +/** Make sure block addr is block aligned && < flash total size */ +static int erase_block_retry(const struct cts_device *cts_dev, + u32 block_addr, int retry) +{ + int ret, retries; + + cts_info(" Erase block 0x%06x", block_addr); + + retries = 0; + do { + retries++; + + ret = cts_dev->hwdata->sfctrl->ops->be(cts_dev, block_addr); + if (ret) { + cts_err("Erase block 0x%06x failed %d retries %d", + block_addr, ret, retries); + continue; + } + } while (retries < retry); + + return ret; +} + +/** Make sure block addr is block aligned && < flash total size */ +static inline int erase_block(const struct cts_device *cts_dev, u32 block_addr) +{ + return erase_block_retry(cts_dev, block_addr, + CTS_FLASH_ERASE_DEFAULT_RETRY); +} + +int cts_prepare_flash_operation(struct cts_device *cts_dev) +{ + int ret; + u8 diva = 0; + bool program_mode = cts_is_device_program_mode(cts_dev); + bool enabled = cts_is_device_enabled(cts_dev); + + cts_info("Prepare for flash operation"); + + if (enabled) { + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } + } + + if (!program_mode) { + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + goto err_start_device; + } + } + + ret = cts_hw_reg_readb_retry(cts_dev, 0x30033, &diva, 5, 0); + if (ret) + cts_warn("Read DIVA failed %d", ret); + else + cts_dbg("Device DIVA = %d", diva); + + if (ret == 0 && diva == 0x0C) + cts_info("DIVA is ready already"); + else { + int retries; + + /* Set HCLK to 10MHz */ + ret = cts_hw_reg_writeb_retry(cts_dev, 0x30033, 0x0C, 5, 0); + if (ret) { + cts_err("Write DIVA failed %d", ret); + goto err_enter_normal_mode; + } + + /* Reset SFCTL */ + ret = cts_hw_reg_writeb_retry(cts_dev, 0x30008, 0xFB, 5, 0); + if (ret) { + cts_err("Reset sfctl failed %d", ret); + goto err_enter_normal_mode; + } + + retries = 0; + do { + u8 state; + + ret = + cts_hw_reg_readb_relaxed(cts_dev, 0x3401B, &state); + if (ret == 0 && (state & 0x40) != 0) + goto init_flash; + + mdelay(2); + } while (++retries < 1000); + + cts_warn("Wait SFCTRL ready failed %d", ret); + } + +init_flash: + if (cts_dev->flash == NULL) { + cts_info("Flash is not initialized, try to probe..."); + ret = probe_flash(cts_dev); + if (ret != 0) { + cts_dev->rtdata.has_flash = false; + cts_warn("Probe flash failed %d", ret); + return 0; + } + } + cts_dev->rtdata.has_flash = true; + return 0; + +err_enter_normal_mode: + if (!program_mode) { + int r = cts_enter_normal_mode(cts_dev); + + if (r) + cts_err("Enter normal mode failed %d", r); + } +err_start_device: + if (enabled) { + int r = cts_start_device(cts_dev); + + if (r) + cts_err("Start device failed %d", r); + } + + return ret; +} + +int cts_post_flash_operation(struct cts_device *cts_dev) +{ + cts_info("Post flash operation"); + + return cts_hw_reg_writeb_retry(cts_dev, 0x30033, 4, 5, 0); +} + +int cts_read_flash_retry(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t size, int retry) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret; + + cts_info("Read from 0x%06x size %zu", flash_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || sfctrl->ops->read == NULL) { + cts_err("Read not supported"); + return -ENOTSUPP; + } + + if (flash_addr > flash->total_size) { + cts_err("Read from 0x%06x > flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Read actually from 0x%06x size %zu", flash_addr, size); + + while (size) { + size_t l; + + l = min(sfctrl->xchg_sram_size, size); + + ret = sfctrl->ops->read(cts_dev, flash_addr, dst, l); + if (ret < 0) { + cts_err("Read from 0x%06x size %zu failed %d", + flash_addr, size, ret); + return ret; + } + + dst += l; + size -= l; + flash_addr += l; + } + + return 0; +} + +int cts_read_flash_to_sram_retry(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t size, + int retry) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret, retries; + + cts_info("Read from 0x%06x to sram 0x%06x size %zu", + flash_addr, sram_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || sfctrl->ops->read_to_sram == NULL) { + cts_err("Read to sram not supported"); + return -ENOTSUPP; + } + + if (flash_addr > flash->total_size) { + cts_err("Read to sram from 0x%06x > flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Read to sram actually from 0x%06x size %zu", flash_addr, + size); + + retries = 0; + do { + retries++; + ret = + sfctrl->ops->read_to_sram(cts_dev, flash_addr, sram_addr, + size); + if (ret) { + cts_err + ("Read from 0x%06x to sram 0x%06x size %zu failed %d retries %d", + flash_addr, sram_addr, size, ret, retries); + continue; + } + } while (retries < retry); + + return ret; +} + +int cts_read_flash_to_sram_check_crc_retry(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, + size_t size, u32 crc, int retry) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret, retries; + + cts_info("Read from 0x%06x to sram 0x%06x size %zu with crc check", + flash_addr, sram_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || + sfctrl->ops->read_to_sram == NULL || + sfctrl->ops->calc_sram_crc == NULL) { + cts_err("Read to sram check crc not supported"); + return -ENOTSUPP; + } + + if (flash_addr > flash->total_size) { + cts_err("Read to sram from 0x%06x > flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Read to sram actually from 0x%06x size %zu with crc check", + flash_addr, size); + + retries = 0; + do { + u32 crc_sram; + + retries++; + + ret = + sfctrl->ops->read_to_sram(cts_dev, flash_addr, sram_addr, + size); + if (ret) { + cts_err + ("Read from 0x%06x to sram 0x%06x size %zu failed %d retries %d", + flash_addr, sram_addr, size, ret, retries); + continue; + } + + ret = + sfctrl->ops->calc_sram_crc(cts_dev, sram_addr, size, + &crc_sram); + if (ret) { + cts_err + ("Get crc for read from 0x%06x to sram 0x%06x size %zu " + "failed %d retries %d", flash_addr, sram_addr, + size, ret, retries); + continue; + } + + if (crc == crc_sram) + return 0; + + cts_err + ("Check crc for read from 0x%06x to sram 0x%06x size %zu " + "mismatch %x != %x, retries %d", flash_addr, sram_addr, + size, crc, crc_sram, retries); + ret = -EFAULT; + } while (retries < retry); + + return ret; +} + +int cts_program_flash(const struct cts_device *cts_dev, + u32 flash_addr, const void *src, size_t size) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret; + + cts_info("Program to 0x%06x size %zu", flash_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || sfctrl->ops->program == NULL) { + cts_err("Program not supported"); + return -ENOTSUPP; + } + + if (flash_addr >= flash->total_size) { + cts_err("Program from 0x%06x >= flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Program actually to 0x%06x size %zu", flash_addr, size); + + while (size) { + size_t l, offset; + + l = min(flash->page_size, size); + offset = flash_addr & (flash->page_size - 1); + + if (offset) + l = min(flash->page_size - offset, l); + + ret = sfctrl->ops->program(cts_dev, flash_addr, src, l); + if (ret) { + cts_err("Program to 0x%06x size %zu failed %d", + flash_addr, l, ret); + return ret; + } + + src += l; + size -= l; + flash_addr += l; + } + + return 0; +} + +int cts_program_flash_from_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t size) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret; + + cts_info("Program to 0x%06x from sram 0x%06x size %zu", + flash_addr, sram_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || sfctrl->ops->program_from_sram == NULL) { + cts_err("Program from sram not supported"); + return -ENOTSUPP; + } + + if (flash_addr >= flash->total_size) { + cts_err("Program from 0x%06x >= flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Program actually to 0x%06x from sram 0x%06x size %zu", + flash_addr, sram_addr, size); + + while (size) { + size_t l, offset; + + l = min(flash->page_size, size); + offset = flash_addr & (flash->page_size - 1); + + if (offset) + l = min(flash->page_size - offset, l); + + ret = + sfctrl->ops->program_from_sram(cts_dev, flash_addr, + sram_addr, l); + if (ret) { + cts_err + ("Program to 0x%06x from sram 0x%06x size %zu failed %d", + flash_addr, sram_addr, l, ret); + return ret; + } + + size -= l; + flash_addr += l; + sram_addr += l; + } + + return 0; +} + +int cts_erase_flash(const struct cts_device *cts_dev, u32 addr, size_t size) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret; + + cts_info("Erase from 0x%06x size %zu", addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || sfctrl->ops == NULL || + sfctrl->ops->se == NULL || sfctrl->ops->be == NULL || + flash == NULL) { + cts_err("Oops"); + return -EINVAL; + } + + /* Addr and size MUST sector aligned */ + addr = rounddown(addr, flash->sector_size); + size = roundup(size, flash->sector_size); + + if (addr > flash->total_size) { + cts_err("Erase from 0x%06x > flash size 0x%06zx", + addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - addr); + + cts_info("Erase actually from 0x%06x size %zu", addr, size); + + if (flash->block_size) { + while (addr != ALIGN(addr, flash->block_size) && + size >= flash->sector_size) { + ret = erase_sector(cts_dev, addr); + if (ret) { + cts_err + ("Erase sector 0x%06x size 0x%04zx failed %d", + addr, flash->sector_size, ret); + return ret; + } + addr += flash->sector_size; + size -= flash->sector_size; + } + + while (size >= flash->block_size) { + ret = erase_block(cts_dev, addr); + if (ret) { + cts_err + ("Erase block 0x%06x size 0x%04zx failed %d", + addr, flash->block_size, ret); + return ret; + } + addr += flash->block_size; + size -= flash->block_size; + } + } + + while (size >= flash->sector_size) { + ret = erase_sector(cts_dev, addr); + if (ret) { + cts_err("Erase sector 0x%06x size 0x%04zx failed %d", + addr, flash->sector_size, ret); + return ret; + } + addr += flash->sector_size; + size -= flash->sector_size; + } + + return 0; +} diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_spi_flash.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_spi_flash.h new file mode 100644 index 000000000000..963abcd2676e --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_spi_flash.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_SPI_FLASH_H +#define CTS_SPI_FLASH_H + +struct cts_flash { + const char *name; + u32 jedec_id; /* Device ID by command 0x9F */ + size_t page_size; /* Page size by command 0x02 */ + size_t sector_size; /* Sector size by command 0x20 */ + +/* Block size by command 0x52, + * if 0 means block erase NOT supported + */ + size_t block_size; + size_t total_size; + +}; + +#define CTS_FLASH_READ_DEFAULT_RETRY (3) +#define CTS_FLASH_ERASE_DEFAULT_RETRY (3) + +struct cts_device; + +extern int cts_prepare_flash_operation(struct cts_device *cts_dev); +extern int cts_post_flash_operation(struct cts_device *cts_dev); + +extern int cts_read_flash_retry(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t size, + int retry); +static inline int cts_read_flash(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t size) +{ + return cts_read_flash_retry(cts_dev, flash_addr, dst, size, + CTS_FLASH_READ_DEFAULT_RETRY); +} + +extern int cts_read_flash_to_sram_retry(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, + size_t size, int retry); +static inline int cts_read_flash_to_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, + size_t size) +{ + return cts_read_flash_to_sram_retry(cts_dev, + flash_addr, sram_addr, size, + CTS_FLASH_READ_DEFAULT_RETRY); +} + +extern int cts_read_flash_to_sram_check_crc_retry(const struct cts_device + *cts_dev, u32 flash_addr, + u32 sram_addr, size_t size, + u32 crc, int retry); +static inline int cts_read_flash_to_sram_check_crc(const struct cts_device + *cts_dev, u32 flash_addr, + u32 sram_addr, size_t size, + u32 crc) +{ + return cts_read_flash_to_sram_check_crc_retry(cts_dev, + flash_addr, sram_addr, + size, crc, + CTS_FLASH_READ_DEFAULT_RETRY); +} + +extern int cts_erase_flash(const struct cts_device *cts_dev, u32 addr, + size_t size); + +extern int cts_program_flash(const struct cts_device *cts_dev, + u32 flash_addr, const void *src, size_t size); +extern int cts_program_flash_from_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, + size_t size); + +#endif /* CTS_SPI_FLASH_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_strerror.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_strerror.c new file mode 100644 index 000000000000..822d6547f15f --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_strerror.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include + +#define ERROR_TEXT_ENTRY(errno, err_desc_str) \ + [(errno)] = #errno ", " err_desc_str + +static const char *const _error_text[] = { + [0] = + "Success", ERROR_TEXT_ENTRY(EPERM, "Operation not permitted"), + ERROR_TEXT_ENTRY(ENOENT, "No such file or directory"), + ERROR_TEXT_ENTRY(ESRCH, "No such process"), + ERROR_TEXT_ENTRY(EINTR, "Interrupted system call"), + ERROR_TEXT_ENTRY(EIO, "Input/output error"), + ERROR_TEXT_ENTRY(ENXIO, "No such device or address"), + ERROR_TEXT_ENTRY(E2BIG, "Argument list too long"), + ERROR_TEXT_ENTRY(ENOEXEC, "Exec format error"), + ERROR_TEXT_ENTRY(EBADF, "Bad file descriptor"), + ERROR_TEXT_ENTRY(ECHILD, "No child processes"), + ERROR_TEXT_ENTRY(EAGAIN, "Resource temporarily unavailable"), + ERROR_TEXT_ENTRY(ENOMEM, "Cannot allocate memory"), + ERROR_TEXT_ENTRY(EACCES, "Permission denied"), + ERROR_TEXT_ENTRY(EFAULT, "Bad address"), ERROR_TEXT_ENTRY(ENOTBLK, + "Block device required"), + ERROR_TEXT_ENTRY(EBUSY, "Device or resource busy"), + ERROR_TEXT_ENTRY(EEXIST, "File exists"), ERROR_TEXT_ENTRY(EXDEV, + "Invalid cross-device link"), + ERROR_TEXT_ENTRY(ENODEV, "No such device"), + ERROR_TEXT_ENTRY(ENOTDIR, "Not a directory"), + ERROR_TEXT_ENTRY(EISDIR, "Is a directory"), + ERROR_TEXT_ENTRY(EINVAL, "Invalid argument"), + ERROR_TEXT_ENTRY(EMFILE, "Too many open files"), + ERROR_TEXT_ENTRY(ENFILE, "Too many open files in system"), + ERROR_TEXT_ENTRY(ENOTTY, "Inappropriate ioctl for device"), + ERROR_TEXT_ENTRY(ETXTBSY, "Text file busy"), + ERROR_TEXT_ENTRY(EFBIG, "File too large"), + ERROR_TEXT_ENTRY(ENOSPC, "No space left on device"), + ERROR_TEXT_ENTRY(ESPIPE, "Illegal seek"), ERROR_TEXT_ENTRY(EROFS, + "Read-only file system"), + ERROR_TEXT_ENTRY(EMLINK, "Too many links"), + ERROR_TEXT_ENTRY(EPIPE, "Broken pipe"), ERROR_TEXT_ENTRY(EDOM, + "Numerical argument out of domain"), + ERROR_TEXT_ENTRY(ERANGE, "Numerical result out of range"), + ERROR_TEXT_ENTRY(EDEADLK, "Resource deadlock avoided"), + ERROR_TEXT_ENTRY(ENAMETOOLONG, "File name too long"), + ERROR_TEXT_ENTRY(ENOLCK, "No locks available"), + ERROR_TEXT_ENTRY(ENOSYS, "Function not implemented"), + ERROR_TEXT_ENTRY(ENOTEMPTY, "Directory not empty"), + ERROR_TEXT_ENTRY(ELOOP, "Too many levels of symbolic links"), + ERROR_TEXT_ENTRY(EWOULDBLOCK, "Operation would block"), + ERROR_TEXT_ENTRY(ENOMSG, "No message of desired type"), + ERROR_TEXT_ENTRY(EIDRM, "Identifier removed"), + ERROR_TEXT_ENTRY(ECHRNG, "Channel number out of range"), + ERROR_TEXT_ENTRY(EL2NSYNC, "Level 2 not synchronized"), + ERROR_TEXT_ENTRY(EL3HLT, "Level 3 halted"), + ERROR_TEXT_ENTRY(EL3RST, "Level 3 reset"), + ERROR_TEXT_ENTRY(ELNRNG, "Link number out of range"), + ERROR_TEXT_ENTRY(EUNATCH, "Protocol driver not attached"), + ERROR_TEXT_ENTRY(ENOCSI, "No CSI structure available"), + ERROR_TEXT_ENTRY(EL2HLT, "Level 2 halted"), + ERROR_TEXT_ENTRY(EBADE, "Invalid exchange"), + ERROR_TEXT_ENTRY(EBADR, "Invalid request descriptor"), + ERROR_TEXT_ENTRY(EXFULL, "Exchange full"), + ERROR_TEXT_ENTRY(ENOANO, "No anode"), ERROR_TEXT_ENTRY(EBADRQC, + "Invalid request code"), + ERROR_TEXT_ENTRY(EBADSLT, "Invalid slot"), + ERROR_TEXT_ENTRY(EDEADLOCK, "File locking deadlock error"), + ERROR_TEXT_ENTRY(EBFONT, "Bad font file format"), + ERROR_TEXT_ENTRY(ENOSTR, "Device not a stream"), + ERROR_TEXT_ENTRY(ENODATA, "No data available"), + ERROR_TEXT_ENTRY(ETIME, "Timer expired"), ERROR_TEXT_ENTRY(ENOSR, + "Out of streams resources"), + ERROR_TEXT_ENTRY(ENONET, "Machine is not on the network"), + ERROR_TEXT_ENTRY(ENOPKG, "Package not installed"), + ERROR_TEXT_ENTRY(EREMOTE, "Object is remote"), + ERROR_TEXT_ENTRY(ENOLINK, "Link has been severed"), + ERROR_TEXT_ENTRY(EADV, "Advertise error"), + ERROR_TEXT_ENTRY(ESRMNT, "Srmount error"), ERROR_TEXT_ENTRY(ECOMM, + "Communication error on send"), + ERROR_TEXT_ENTRY(EPROTO, "Protocol error"), + ERROR_TEXT_ENTRY(EMULTIHOP, "Multihop attempted"), + ERROR_TEXT_ENTRY(EDOTDOT, "RFS specific error"), + ERROR_TEXT_ENTRY(EBADMSG, "Bad message"), + ERROR_TEXT_ENTRY(EOVERFLOW, + "Value too large for defined data type"), + ERROR_TEXT_ENTRY(ENOTUNIQ, "Name not unique on network"), + ERROR_TEXT_ENTRY(EBADFD, "File descriptor in bad state"), + ERROR_TEXT_ENTRY(EREMCHG, "Remote address changed"), + ERROR_TEXT_ENTRY(ELIBACC, + "Can not access a needed shared library"), + ERROR_TEXT_ENTRY(ELIBBAD, "Accessing a corrupted shared library"), + ERROR_TEXT_ENTRY(ELIBSCN, ".lib section in a.out corrupted"), + ERROR_TEXT_ENTRY(ELIBMAX, + "Attempting to link in too many shared libraries"), + ERROR_TEXT_ENTRY(ELIBEXEC, + "Cannot exec a shared library directly"), + ERROR_TEXT_ENTRY(EILSEQ, + "Invalid or incomplete multibyte or wide character"), + ERROR_TEXT_ENTRY(ERESTART, + "Interrupted system call should be restarted"), + ERROR_TEXT_ENTRY(ESTRPIPE, "Streams pipe error"), + ERROR_TEXT_ENTRY(EUSERS, "Too many users"), + ERROR_TEXT_ENTRY(ENOTSOCK, "Socket operation on non-socket"), + ERROR_TEXT_ENTRY(EDESTADDRREQ, "Destination address required"), + ERROR_TEXT_ENTRY(EMSGSIZE, "Message too long"), + ERROR_TEXT_ENTRY(EPROTOTYPE, "Protocol wrong type for socket"), + ERROR_TEXT_ENTRY(ENOPROTOOPT, "Protocol not available"), + ERROR_TEXT_ENTRY(EPROTONOSUPPORT, "Protocol not supported"), + ERROR_TEXT_ENTRY(ESOCKTNOSUPPORT, "Socket type not supported"), + ERROR_TEXT_ENTRY(EOPNOTSUPP, "Operation not supported"), + ERROR_TEXT_ENTRY(EPFNOSUPPORT, "Protocol family not supported"), + ERROR_TEXT_ENTRY(EAFNOSUPPORT, + "Address family not supported by protocol"), + ERROR_TEXT_ENTRY(EADDRINUSE, "Address already in use"), + ERROR_TEXT_ENTRY(EADDRNOTAVAIL, "Cannot assign requested address"), + ERROR_TEXT_ENTRY(ENETDOWN, "Network is down"), + ERROR_TEXT_ENTRY(ENETUNREACH, "Network is unreachable"), + ERROR_TEXT_ENTRY(ENETRESET, "Network dropped connection on reset"), + ERROR_TEXT_ENTRY(ECONNABORTED, "Software caused connection abort"), + ERROR_TEXT_ENTRY(ECONNRESET, "Connection reset by peer"), + ERROR_TEXT_ENTRY(ENOBUFS, "No buffer space available"), + ERROR_TEXT_ENTRY(EISCONN, + "Transport endpoint is already connected"), + ERROR_TEXT_ENTRY(ENOTCONN, "Transport endpoint is not connected"), + ERROR_TEXT_ENTRY(ESHUTDOWN, + "Cannot send after transport endpoint shutdown"), + ERROR_TEXT_ENTRY(ETOOMANYREFS, + "Too many references: cannot splice"), + ERROR_TEXT_ENTRY(ETIMEDOUT, "Connection timed out"), + ERROR_TEXT_ENTRY(ECONNREFUSED, "Connection refused"), + ERROR_TEXT_ENTRY(EHOSTDOWN, "Host is down"), + ERROR_TEXT_ENTRY(EHOSTUNREACH, "No route to host"), + ERROR_TEXT_ENTRY(EALREADY, "Operation already in progress"), + ERROR_TEXT_ENTRY(EINPROGRESS, "Operation now in progress"), + ERROR_TEXT_ENTRY(ESTALE, "Stale file handle"), + ERROR_TEXT_ENTRY(EUCLEAN, "Structure needs cleaning"), + ERROR_TEXT_ENTRY(ENOTNAM, "Not a XENIX named type file"), + ERROR_TEXT_ENTRY(ENAVAIL, "No XENIX semaphores available"), + ERROR_TEXT_ENTRY(EISNAM, "Is a named type file"), + ERROR_TEXT_ENTRY(EREMOTEIO, "Remote I/O error"), + ERROR_TEXT_ENTRY(EDQUOT, "Disk quota exceeded"), + ERROR_TEXT_ENTRY(ENOMEDIUM, "No medium found"), + ERROR_TEXT_ENTRY(EMEDIUMTYPE, "Wrong medium type"), + ERROR_TEXT_ENTRY(ECANCELED, "Operation canceled"), + ERROR_TEXT_ENTRY(ENOKEY, "Required key not available"), + ERROR_TEXT_ENTRY(EKEYEXPIRED, "Key has expired"), + ERROR_TEXT_ENTRY(EKEYREVOKED, "Key has been revoked"), + ERROR_TEXT_ENTRY(EKEYREJECTED, "Key was rejected by service"), + ERROR_TEXT_ENTRY(EOWNERDEAD, "Owner died"), + ERROR_TEXT_ENTRY(ENOTRECOVERABLE, "State not recoverable"), + ERROR_TEXT_ENTRY(ERFKILL, "Operation not possible due to RF-kill"), + ERROR_TEXT_ENTRY(EHWPOISON, "Memory page has hardware error"), + ERROR_TEXT_ENTRY(ERESTARTSYS, "Restart system"), + ERROR_TEXT_ENTRY(ERESTARTNOINTR, "Restart monitor"), + ERROR_TEXT_ENTRY(ERESTARTNOHAND, "Restart if no handler"), + ERROR_TEXT_ENTRY(ENOIOCTLCMD, "No ioctl command"), + ERROR_TEXT_ENTRY(ERESTART_RESTARTBLOCK, + "Restart by calling sys_restart_syscall"), + ERROR_TEXT_ENTRY(EPROBE_DEFER, "Driver requests probe retry"), + ERROR_TEXT_ENTRY(EOPENSTALE, "Open found a stale dentry"), + ERROR_TEXT_ENTRY(EBADHANDLE, "Illegal NFS file handle"), + ERROR_TEXT_ENTRY(ENOTSYNC, "Update synchronization mismatch"), + ERROR_TEXT_ENTRY(EBADCOOKIE, "Cookie is stale"), + ERROR_TEXT_ENTRY(ENOTSUPP, "Operation is not supported"), + ERROR_TEXT_ENTRY(ETOOSMALL, "Buffer or request is too small"), + ERROR_TEXT_ENTRY(ESERVERFAULT, "An untranslatable error occurred"), + ERROR_TEXT_ENTRY(EBADTYPE, "Type not supported by server"), + ERROR_TEXT_ENTRY(EJUKEBOX, + "Request initiated, but will not complete before timeout"), + ERROR_TEXT_ENTRY(EIOCBQUEUED, + "IOCB queued, will get completion event"), +}; + +#define ERROR_TEXT_ARRAY_SIZE (sizeof(_error_text) / sizeof(_error_text[0])) +const char *cts_strerror(int errno) +{ + if (errno > 0) { + return ""; + } else if (-errno < (int) ERROR_TEXT_ARRAY_SIZE) { + const char *s = _error_text[-errno]; + + if (s) + return s; + } + return ""; +} diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_strerror.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_strerror.h new file mode 100644 index 000000000000..3274181229fc --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_strerror.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_STRERROR_H +#define CTS_STRERROR_H + +#define CTS_ERR_FMT_STR "%d(%s)" +#define CTS_ERR_ARG(errno) cts_strerror(errno) +const char *cts_strerror(int errno); + +#endif /* CTS_STRERROR_H */ + diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sysfs.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sysfs.c new file mode 100644 index 000000000000..7010782ab3de --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sysfs.c @@ -0,0 +1,3220 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "Sysfs" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_test.h" +#include "cts_sfctrl.h" +#include "cts_spi_flash.h" +#include "cts_firmware.h" +#include "cts_strerror.h" + +#ifdef CONFIG_CTS_SYSFS + +#define SPLIT_LINE_STR \ + "-----------------------------------------------"\ + "------------------------------------------------\n" +#define ROW_NUM_FORMAT_STR "%2d | " +#define COL_NUM_FORMAT_STR "%4u " +#define DATA_FORMAT_STR "%5d" + +#define DIFFDATA_BUFFER_SIZE(cts_dev) \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * 2) + +#define MAX_ARG_NUM (100) +#define MAX_ARG_LENGTH (1024) +#define HW_STUB_ADDR (0XF000) + +static char cmdline_param[MAX_ARG_LENGTH + 1]; +int argc; +char *argv[MAX_ARG_NUM]; + +static int jitter_test_frame = 10; +static s16 *manualdiff_base; +static int manualdiff_base_updated; + +int parse_arg(const char *buf, size_t count) +{ + char *p; + + memcpy(cmdline_param, buf, min((size_t)MAX_ARG_LENGTH, count)); + cmdline_param[count] = '\0'; + + argc = 0; + p = strim(cmdline_param); + if (p == NULL || p[0] == '\0') + return 0; + + while (p && p[0] != '\0' && argc < MAX_ARG_NUM) + argv[argc++] = strsep(&p, " ,"); + + return argc; +} + +/* echo addr value1 value2 value3 ... valueN > write_reg */ +static ssize_t write_firmware_register_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 addr; + int i, ret; + u8 *data = NULL; + + parse_arg(buf, count); + + cts_info("Write firmware register '%.*s'", (int)count, buf); + + if (argc < 2) { + cts_err("Too few args %d", argc); + return -EFAULT; + } + + ret = kstrtou16(argv[0], 0, &addr); + if (ret) { + cts_err("Invalid address %s", argv[0]); + return -EINVAL; + } + + data = (u8 *) kmalloc(argc - 1, GFP_KERNEL); + if (data == NULL) { + cts_err("Allocate buffer for write data failed\n"); + return -ENOMEM; + } + + for (i = 1; i < argc; i++) { + ret = kstrtou8(argv[i], 0, data + i - 1); + if (ret) { + cts_err("Invalid value %s", argv[i]); + goto free_data; + } + } + + ret = cts_dev->ops->write_reg(cts_dev, addr, data, argc - 1); + if (ret) { + cts_err("Write firmware register addr: 0x%04x size: %d failed", + addr, argc - 1); + goto free_data; + } + +free_data: + kfree(data); + + return (ret < 0 ? ret : count); +} + +#ifdef CFG_CTS_FW_UPDATE_SYS + +static ssize_t cts_panel_supplier_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *data = dev_get_drvdata(dev); + + if (data->pdata && data->pdata->panel_supplier) { + return scnprintf(buf, PAGE_SIZE, "%s\n", + data->pdata->panel_supplier); + } + return 0; +} + +static ssize_t buildid_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "0000-%04x\n", + cts_data->cts_dev.fwdata.version); +} + +static ssize_t forcereflash_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 0) + return -EINVAL; + + cts_data->force_reflash = (input == 0) ? false : true; + + cts_info("%s force_reflash=%d, count=%zu", __func__, + (cts_data->force_reflash ? 1 : 0), count); + return count; +} + +static ssize_t flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + cts_is_firmware_updating(&cts_data->cts_dev) ? 1 : 0); +} + +static bool is_reflash_filename_valid(const struct chipone_ts_data *cts_data, + const char *filename) +{ + char prefix[CFG_CTS_FW_FILE_NAME_MAX_LEN]; + + if (cts_data->pdata->panel_supplier != NULL) { + snprintf(prefix, sizeof(prefix), "%s-%s-%s-", + CFG_CTS_FW_FILE_NAME_VENDOR, + cts_data->pdata->panel_supplier, + cts_data->cts_dev.hwdata->name); + } else { + /* panel supplier not set, just check vendor. */ + snprintf(prefix, sizeof(prefix), "%s", + CFG_CTS_FW_FILE_NAME_VENDOR); + } + + cts_info("%s: prefix=%s", __func__, prefix); + if (strncmp(filename, prefix, strlen(prefix))) { + cts_err("%s: invalid FW file.", __func__); + return false; + } + + return true; +} + +static ssize_t doreflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + int ret; + + cts_info("doreflash FW filename: %s len: %zu", buf, count); + + if (count > CFG_CTS_FW_FILE_NAME_MAX_LEN) { + cts_err("doreflash FW filename is too long %zu > %d", + count, CFG_CTS_FW_FILE_NAME_MAX_LEN); + return -EINVAL; + } + + if (cts_is_device_suspended(&cts_data->cts_dev)) { + cts_err("In suspend state, try again later"); + return -EAGAIN; + } + + if (cts_is_firmware_updating(&cts_data->cts_dev)) { + cts_err("In FW flashing state, try again later"); + return -EAGAIN; + } + + if (!cts_data->force_reflash) { + /* Check filename if force_reflash is false */ + if (!is_reflash_filename_valid(cts_data, buf)) { + cts_err("Invalid firmware filename '%*.s'", + (int)count, buf); + return -EINVAL; + } + } + + strncpy(cts_data->cts_dev.config_fw_name, buf, count); + + /* If use echo xxx > doreflash, 0x0A will append to the string, + * if use echo -n xxx > doreflash, nothing will append. + */ + if (cts_data->cts_dev.config_fw_name[count - 1] == '\n') + cts_data->cts_dev.config_fw_name[count - 1] = '\0'; + else + cts_data->cts_dev.config_fw_name[count] = '\0'; + + cts_stop_device(&cts_data->cts_dev); + + cts_lock_device(&cts_data->cts_dev); + ret = cts_update_firmware_from_file(&cts_data->cts_dev, + cts_data->cts_dev.config_fw_name, + false); + cts_unlock_device(&cts_data->cts_dev); + + if (ret) + cts_err("Update firmware from file '%s' failed %d", + cts_data->cts_dev.config_fw_name, ret); + + cts_start_device(&cts_data->cts_dev); + + cts_data->force_reflash = false; + + cts_info("%s: end", __func__); + + return ret ? ret : count; +} + +static ssize_t cts_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + bool val; + + /* TBD: check if cts is power to ready for flash. + * set "1" if power on ready. + */ + val = cts_is_device_suspended(&cts_data->cts_dev); + return scnprintf(buf, PAGE_SIZE, "%d\n", val == false); +} + +static ssize_t cts_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + /* set chip IC type to productinfo */ + return scnprintf(buf, PAGE_SIZE, "%s\n", + cts_data->cts_dev.hwdata->name); +} + +/* add sys entries for FW update */ +/* static DEVICE_ATTR(drv_irq, S_IRUGO | S_IWUSR, drv_irq_show, drv_irq_store); */ +/* static DEVICE_ATTR(reset, S_IWUSR | S_IWGRP, NULL, reset_store); */ +static DEVICE_ATTR(panel_supplier, 0444, cts_panel_supplier_show, NULL); +static DEVICE_ATTR(buildid, S_IRUGO, buildid_show, NULL); +static DEVICE_ATTR(forcereflash, S_IWUSR | S_IWGRP, NULL, forcereflash_store); +static DEVICE_ATTR(flashprog, S_IRUGO, flashprog_show, NULL); +static DEVICE_ATTR(doreflash, S_IWUSR | S_IWGRP, NULL, doreflash_store); +static DEVICE_ATTR(poweron, S_IRUGO, cts_poweron_show, NULL); +static DEVICE_ATTR(productinfo, S_IRUGO, cts_productinfo_show, NULL); +#endif + +static DEVICE_ATTR(write_reg, S_IWUSR, NULL, write_firmware_register_store); + +static ssize_t read_firmware_register_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ +#define PRINT_ROW_SIZE (16) + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 addr, size, i, remaining; + u8 *data = NULL; + ssize_t count = 0; + int ret; + + cts_info("Read firmware register "); + + if (argc != 2) { + return snprintf(buf, PAGE_SIZE, + "Invalid num args %d\n" + " 1. echo addr size > read_reg\n" + " 2. cat read_reg\n", argc); + } + + ret = kstrtou16(argv[0], 0, &addr); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid address: %s\n", + argv[0]); + } + ret = kstrtou16(argv[1], 0, &size); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid size: %s\n", argv[1]); + + data = (u8 *) kmalloc(size, GFP_KERNEL); + if (data == NULL) { + return snprintf(buf, PAGE_SIZE, + "Allocate buffer for read data failed\n"); + } + + cts_info("Read firmware register from 0x%04x size %u", addr, size); + cts_lock_device(cts_dev); + /* ret = cts_fw_reg_readsb(cts_dev, addr, data, (size_t) size); */ + ret = cts_dev->ops->read_reg(cts_dev, addr, data, (size_t)size); + cts_unlock_device(cts_dev); + if (ret) { + count = snprintf(buf, PAGE_SIZE, + "Read firmware register from 0x%04x size %u failed %d\n", + addr, size, ret); + goto err_free_data; + } + + remaining = size; + for (i = 0; i < size && count <= PAGE_SIZE; i += PRINT_ROW_SIZE) { + size_t linelen = min((size_t)remaining, (size_t)PRINT_ROW_SIZE); + + remaining -= PRINT_ROW_SIZE; + + count += + snprintf(buf + count, PAGE_SIZE - count, "%04x: ", addr); + + /* Lower version kernel return void */ + hex_dump_to_buffer(data + i, linelen, PRINT_ROW_SIZE, 1, + buf + count, PAGE_SIZE - count, true); + count += strlen(buf + count); + + if (count < PAGE_SIZE) { + buf[count++] = '\n'; + addr += PRINT_ROW_SIZE; + } else { + break; + } + } + +err_free_data: + kfree(data); + + return count; +#undef PRINT_ROW_SIZE +} + +/* echo addr size > read_reg */ +static ssize_t read_firmware_register_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + parse_arg(buf, count); + + return (argc == 0 ? 0 : count); +} + +static DEVICE_ATTR(read_reg, S_IWUSR | S_IRUSR, + read_firmware_register_show, read_firmware_register_store); + +static ssize_t read_hw_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#define PRINT_ROW_SIZE (16) + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u32 addr, size, i, remaining; + u8 *data = NULL; + ssize_t count = 0; + int ret; + + cts_info("Read hw register"); + + if (argc != 2) { + return snprintf(buf, PAGE_SIZE, + "Invalid num args %d\n" + " 1. echo addr size > read_hw_reg\n" + " 2. cat read_hw_reg\n", argc); + } + + ret = kstrtou32(argv[0], 0, &addr); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid address: %s\n", + argv[0]); + } + ret = kstrtou32(argv[1], 0, &size); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid size: %s\n", argv[1]); + + data = (u8 *) kmalloc(size, GFP_KERNEL); + if (data == NULL) + return snprintf(buf, PAGE_SIZE, + "Allocate buffer for read data failed\n"); + + cts_info("Read hw register from 0x%04x size %u", addr, size); + cts_lock_device(cts_dev); + + if ((cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911C) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911S) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911)) { + for (i = 0; i < size; i++) { + ret = + cts_fw_reg_writel_retry(cts_dev, HW_STUB_ADDR, + addr + i, 3, 10); + if (ret) { + count = + snprintf(buf, PAGE_SIZE, + "Write hw register error\n"); + goto err_free_data; + } + + ret = + cts_fw_reg_readsb(cts_dev, HW_STUB_ADDR + 4, + data + i, 1); + if (ret) { + count = + snprintf(buf, PAGE_SIZE, + "Read hw register error\n"); + goto err_free_data; + } + } + } + + if ((cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9916) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9922)) { + if (cts_dev->rtdata.program_mode) { + for (i = 0; i < size; i++) { + ret = + cts_dev_readb(cts_dev, addr + i, data + i, + 3, 10); + if (ret) { + count = + snprintf(buf, PAGE_SIZE, + "Write hw register error\n"); + goto err_free_data; + } + } + } else { + ret = + cts_dev->ops->read_hw_reg(cts_dev, addr, data, + size); + if (ret < 0) { + count = + snprintf(buf, PAGE_SIZE, + "Read hw register error\n"); + goto err_free_data; + } + } + } + + remaining = size; + for (i = 0; i < size && count <= PAGE_SIZE; i += PRINT_ROW_SIZE) { + size_t linelen = min((size_t)remaining, (size_t)PRINT_ROW_SIZE); + + remaining -= PRINT_ROW_SIZE; + + count += + snprintf(buf + count, PAGE_SIZE - count, "%04x: ", addr); + + /* Lower version kernel return void */ + hex_dump_to_buffer(data + i, linelen, PRINT_ROW_SIZE, 1, + buf + count, PAGE_SIZE - count, true); + count += strlen(buf + count); + + if (count < PAGE_SIZE) { + buf[count++] = '\n'; + addr += PRINT_ROW_SIZE; + } else { + break; + } + } + +err_free_data: + cts_unlock_device(cts_dev); + kfree(data); + + return count; +#undef PRINT_ROW_SIZE +} + +/* echo addr size > read_reg */ +static ssize_t read_hw_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + parse_arg(buf, count); + + return (argc == 0 ? 0 : count); +} + +static DEVICE_ATTR(read_hw_reg, S_IRUSR | S_IWUSR, read_hw_reg_show, + read_hw_reg_store); + +static ssize_t write_hw_reg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u32 addr; + int i, ret; + u8 *data = NULL; + + parse_arg(buf, count); + + cts_info("Write hw register"); + + if (argc < 2) { + cts_err("Too few args %d", argc); + return -EFAULT; + } + + ret = kstrtou32(argv[0], 0, &addr); + if (ret) { + cts_err("Invalid address %s", argv[0]); + return -EINVAL; + } + + data = (u8 *) kmalloc(argc - 1, GFP_KERNEL); + if (data == NULL) { + cts_err("Allocate buffer for write data failed\n"); + return -ENOMEM; + } + + for (i = 1; i < argc; i++) { + ret = kstrtou8(argv[i], 0, data + i - 1); + if (ret) { + cts_err("Invalid value %s", argv[i]); + goto free_data; + } + } + + cts_lock_device(cts_dev); + + if ((cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911C) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911S) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911)) { + for (i = 0; i < argc - 1; i++) { + ret = + cts_fw_reg_writel_retry(cts_dev, HW_STUB_ADDR, + addr + i, 3, 10); + if (ret) { + cts_err("Write hw register error"); + break; + } + ret = + cts_fw_reg_writeb_retry(cts_dev, HW_STUB_ADDR + 4, + data[i], 3, 10); + if (ret) { + cts_err("Write hw register error"); + break; + } + } + } + + if ((cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9916) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9922)) { + if (cts_dev->rtdata.program_mode) { + for (i = 0; i < argc - 1; i++) { + ret = + cts_dev_writeb(cts_dev, addr + i, data[i], + 3, 10); + if (ret) { + cts_err("Write hw register error"); + break; + } + } + } else { + ret = + cts_dev->ops->write_hw_reg(cts_dev, addr, data, + argc - 1); + if (ret < 0) + cts_err("Write hw register error"); + } + } + + cts_unlock_device(cts_dev); +free_data: + kfree(data); + + return (ret < 0 ? ret : count); +} + +static DEVICE_ATTR(write_hw_reg, S_IWUSR, NULL, write_hw_reg_store); + +static ssize_t curr_firmware_version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Current firmware version: %04x\n", + cts_data->cts_dev.fwdata.version); +} + +static DEVICE_ATTR(curr_version, S_IRUGO, curr_firmware_version_show, NULL); + +static ssize_t curr_ddi_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Current ddi version: %02x\n", + cts_data->cts_dev.fwdata.ddi_version); +} + +static DEVICE_ATTR(curr_ddi_version, S_IRUGO, curr_ddi_version_show, NULL); + +static ssize_t rows_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Num rows: %u\n", + cts_data->cts_dev.fwdata.rows); +} + +static DEVICE_ATTR(rows, S_IRUGO, rows_show, NULL); + +static ssize_t cols_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Num cols: %u\n", + cts_data->cts_dev.fwdata.cols); +} + +static DEVICE_ATTR(cols, S_IRUGO, cols_show, NULL); + +static ssize_t res_x_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "X Resolution: %u\n", + cts_data->cts_dev.fwdata.res_x); +} + +static DEVICE_ATTR(res_x, S_IRUGO, res_x_show, NULL); + +static ssize_t res_y_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Y Resolution: %u\n", + cts_data->cts_dev.fwdata.res_y); +} + +static DEVICE_ATTR(res_y, S_IRUGO, res_y_show, NULL); + +static ssize_t esd_protection_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + u8 esd_protection; + + cts_lock_device(cts_dev); + ret = cts_dev->ops->get_esd_protection(cts_dev, &esd_protection); + cts_unlock_device(cts_dev); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Read firmware ESD protection register failed %d\n", + ret); + + return snprintf(buf, PAGE_SIZE, "ESD protection: %u\n", esd_protection); +} + +static DEVICE_ATTR(esd_protection, S_IRUGO, esd_protection_show, NULL); + +static ssize_t monitor_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + u8 value; + + cts_lock_device(cts_dev); + ret = cts_dev->ops->is_mnt_enabled(cts_dev, &value); + cts_unlock_device(cts_dev); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Read firmware monitor enable register failed %d\n", + ret); + + return snprintf(buf, PAGE_SIZE, "Monitor mode: %s\n", + value & BIT(0) ? "Enable" : "Disable"); +} + +static ssize_t monitor_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + u8 value, enable = 0; + + if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') + enable = 1; + + cts_info("Write firmware monitor mode to '%c', %s", + buf[0], enable ? "Enable" : "Disable"); + + cts_lock_device(cts_dev); + ret = cts_fw_reg_readb(&cts_data->cts_dev, 0x8000 + 344, &value); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("Write firmware monitor enable register failed %d", + ret); + return -EIO; + } + + if ((value & BIT(0)) && enable) + cts_info("Monitor mode already enabled"); + else if ((value & BIT(0)) == 0 && enable == 0) + cts_info("Monitor mode already disabled"); + else { + if (enable) + value |= BIT(0); + else + value &= ~BIT(0); + + cts_lock_device(cts_dev); + ret = + cts_fw_reg_writeb(&cts_data->cts_dev, 0x8000 + 344, value); + cts_unlock_device(cts_dev); + if (ret) { + cts_err + ("Write firmware monitor enable register failed %d", + ret); + return -EIO; + } + } + + return count; +} + +static DEVICE_ATTR(monitor_mode, S_IRUGO, monitor_mode_show, + monitor_mode_store); + +static ssize_t auto_compensate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + u8 value; + + cts_lock_device(cts_dev); + ret = cts_dev->ops->is_cneg_enabled(&cts_data->cts_dev, &value); + cts_unlock_device(cts_dev); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Read auto compensate enable register failed %d\n", + ret); + + return snprintf(buf, PAGE_SIZE, "Auto compensate: %s\n", + value ? "Enable" : "Disable"); +} + +static DEVICE_ATTR(auto_compensate, S_IRUGO, auto_compensate_show, NULL); + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE +static ssize_t driver_builtin_firmware_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int i, count = 0; + + count += snprintf(buf + count, PAGE_SIZE - count, + "Total %d builtin firmware:\n", + cts_get_num_driver_builtin_firmware()); + + for (i = 0; i < cts_get_num_driver_builtin_firmware(); i++) { + const struct cts_firmware *firmware = + cts_request_driver_builtin_firmware_by_index(i); + if (firmware) + count += snprintf(buf + count, PAGE_SIZE - count, + "%-2d: hwid: %04x fwid: %04x ver: %04x size: %6zu desc: %s\n", + i, firmware->hwid, firmware->fwid, + FIRMWARE_VERSION(firmware), + firmware->size, firmware->name); + else + count += snprintf(buf + count, PAGE_SIZE - count, + "%-2d: INVALID\n", i); + } + + return count; +} + +/* echo index/name [flash/sram] > driver_builtin_firmware */ +static ssize_t driver_builtin_firmware_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + const struct cts_firmware *firmware; + bool to_flash = true; + int ret, index = -1; + + parse_arg(buf, count); + + if (argc != 1 && argc != 2) { + cts_err("Invalid num args %d\n" + " echo index/name [flash/sram] > driver_builtin_firmware\n", + argc); + return -EFAULT; + } + + if (isdigit(*argv[0])) + index = simple_strtoul(argv[0], NULL, 0); + + if (argc > 1) { + if (strncasecmp(argv[1], "flash", 5) == 0) + to_flash = true; + else if (strncasecmp(argv[1], "sram", 4) == 0) + to_flash = false; + else { + cts_err + ("Invalid location '%s', must be 'flash' or 'sram'", + argv[1]); + return -EINVAL; + } + } + + cts_info("Update driver builtin firmware '%s' to %s", + argv[1], to_flash ? "flash" : "sram"); + + if (index >= 0 && index < cts_get_num_driver_builtin_firmware()) + firmware = cts_request_driver_builtin_firmware_by_index(index); + else + firmware = cts_request_driver_builtin_firmware_by_name(argv[0]); + + if (firmware) { + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } + + cts_lock_device(cts_dev); + ret = cts_update_firmware(cts_dev, firmware, to_flash); + cts_unlock_device(cts_dev); + + if (ret) { + cts_err("Update firmware failed %d", ret); + goto err_start_device; + } + + ret = cts_start_device(cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + return ret; + } + } else { + cts_err("Firmware '%s' NOT found", argv[0]); + return -ENOENT; + } + + return count; + +err_start_device: + cts_start_device(cts_dev); + + return ret; +} + +static DEVICE_ATTR(driver_builtin_firmware, S_IWUSR | S_IRUGO, + driver_builtin_firmware_show, driver_builtin_firmware_store); +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ + +#ifdef CFG_CTS_FIRMWARE_IN_FS +/* echo filepath [flash/sram] > update_firmware_from_file */ +static ssize_t update_firmware_from_file_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + const struct cts_firmware *firmware; + bool to_flash = true; + int ret; + + parse_arg(buf, count); + + if (argc > 2) { + cts_err("Invalid num args %d\n" + " echo filepath [flash/sram] > update_from_file\n", + argc); + return -EFAULT; + } else if (argc > 1) { + if (strncasecmp(argv[1], "flash", 5) == 0) + to_flash = true; + else if (strncasecmp(argv[1], "sram", 4) == 0) + to_flash = false; + else { + cts_err + ("Invalid location '%s', must be 'flash' or 'sram'", + argv[1]); + return -EINVAL; + } + } + + cts_info("Update firmware from file '%s'", argv[0]); + + firmware = cts_request_firmware_from_fs(cts_dev, argv[0]); + if (firmware == NULL) { + cts_err("Request firmware from file '%s' failed", argv[0]); + return -ENOENT; + } + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto err_release_firmware; + } + + cts_lock_device(cts_dev); + ret = cts_update_firmware(cts_dev, firmware, to_flash); + cts_unlock_device(cts_dev); + + if (ret) { + cts_err("Update firmware failed %d", ret); + goto err_release_firmware; + } + + ret = cts_start_device(cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + goto err_release_firmware; + } + + return count; + +err_release_firmware: + cts_release_firmware(firmware); + + return ret; +} + +static DEVICE_ATTR(update_from_file, S_IWUSR, NULL, + update_firmware_from_file_store); +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + +static ssize_t updating_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Updating: %s\n", + cts_data->cts_dev.rtdata.updating ? "Y" : "N"); +} + +static DEVICE_ATTR(updating, S_IRUGO, updating_show, NULL); + +static struct attribute *cts_dev_firmware_atts[] = { + &dev_attr_curr_version.attr, + &dev_attr_curr_ddi_version.attr, + &dev_attr_rows.attr, + &dev_attr_cols.attr, + &dev_attr_res_x.attr, + &dev_attr_res_y.attr, + &dev_attr_esd_protection.attr, + &dev_attr_monitor_mode.attr, + &dev_attr_auto_compensate.attr, +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE + &dev_attr_driver_builtin_firmware.attr, +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ +#ifdef CFG_CTS_FIRMWARE_IN_FS + &dev_attr_update_from_file.attr, +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + &dev_attr_updating.attr, + NULL +}; + +static const struct attribute_group cts_dev_firmware_attr_group = { + .name = "cts_firmware", + .attrs = cts_dev_firmware_atts, +}; + +static ssize_t flash_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + const struct cts_flash *flash; + + if (cts_dev->flash == NULL) { + bool program_mode; + bool enabled; + int ret; + + program_mode = cts_is_device_program_mode(cts_dev); + enabled = cts_is_device_enabled(cts_dev); + + ret = cts_prepare_flash_operation(cts_dev); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Prepare flash operation failed %d", + ret); + + cts_post_flash_operation(cts_dev); + + if (!program_mode) { + ret = cts_enter_normal_mode(cts_dev); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Enter normal mode failed %d", + ret); + } + + if (enabled) { + ret = cts_start_device(cts_dev); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Start device failed %d", ret); + } + + if (cts_dev->flash == NULL) + return snprintf(buf, PAGE_SIZE, "Flash not found\n"); + } + + flash = cts_dev->flash; + return snprintf(buf, PAGE_SIZE, + "%s:\n" + " JEDEC ID : %06X\n" + " Page size : 0x%zx\n" + " Sector size: 0x%zx\n" + " Block size : 0x%zx\n" + " Total size : 0x%zx\n", + flash->name, flash->jedec_id, flash->page_size, + flash->sector_size, flash->block_size, + flash->total_size); +} + +static DEVICE_ATTR(info, S_IRUGO, flash_info_show, NULL); + +static ssize_t read_flash_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u32 flash_addr, size, i, remaining; + u8 *data = NULL; + ssize_t count = 0; + int ret; + bool program_mode; + bool enabled; + loff_t pos = 0; + + if (argc != 2 && argc != 3) + return snprintf(buf, PAGE_SIZE, "Invalid num args %d\n", argc); + + ret = kstrtou32(argv[0], 0, &flash_addr); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid flash addr: %s\n", + argv[0]); + ret = kstrtou32(argv[1], 0, &size); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid size: %s\n", argv[1]); + + data = (u8 *) kmalloc(size, GFP_KERNEL); + if (data == NULL) + return snprintf(buf, PAGE_SIZE, + "Allocate buffer for read data failed\n"); + + cts_info("Read flash from 0x%06x size %u%s%s", + flash_addr, size, argc == 3 ? " to file " : "", + argc == 3 ? argv[2] : ""); + + cts_lock_device(cts_dev); + program_mode = cts_is_device_program_mode(cts_dev); + enabled = cts_is_device_enabled(cts_dev); + + ret = cts_prepare_flash_operation(cts_dev); + if (ret) { + count += + snprintf(buf, PAGE_SIZE, + "Prepare flash operation failed %d", ret); + goto err_free_data; + } + + ret = cts_read_flash(cts_dev, flash_addr, data, size); + if (ret) { + count = + snprintf(buf, PAGE_SIZE, "Read flash data failed %d\n", + ret); + goto err_post_flash_operation; + } + + if (argc == 3) { + struct file *file; + + cts_info("Write flash data to file '%s'", argv[2]); + + file = filp_open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0666); + if (IS_ERR(file)) { + count += + snprintf(buf, PAGE_SIZE, + "Open file '%s' failed %ld", argv[2], + PTR_ERR(file)); + goto err_post_flash_operation; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(file, data, size, &pos); +#else + ret = kernel_write(file, data, size, pos); +#endif + if (ret != size) + count += + snprintf(buf, PAGE_SIZE, + "Write flash data to file '%s' failed %d", + argv[2], ret); + + ret = filp_close(file, NULL); + if (ret) + count += + snprintf(buf, PAGE_SIZE, + "Close file '%s' failed %d", argv[2], ret); + } else { +#define PRINT_ROW_SIZE (16) + remaining = size; + for (i = 0; i < size && count <= PAGE_SIZE; i += PRINT_ROW_SIZE) { + size_t linelen = + min((size_t)remaining, (size_t)PRINT_ROW_SIZE); + + remaining -= PRINT_ROW_SIZE; + + count += snprintf(buf + count, PAGE_SIZE - count - 1, + "%04x-%04x: ", flash_addr >> 16, + flash_addr & 0xFFFF); + /* Lower version kernel return void */ + hex_dump_to_buffer(data + i, linelen, PRINT_ROW_SIZE, 1, + buf + count, PAGE_SIZE - count - 1, + true); + count += strlen(buf + count); + buf[count++] = '\n'; + flash_addr += linelen; +#undef PRINT_ROW_SIZE + } + } + +err_post_flash_operation: + cts_post_flash_operation(cts_dev); + + if (!program_mode) { + int r = cts_enter_normal_mode(cts_dev); + + if (r) + count += + snprintf(buf, PAGE_SIZE, + "Enter normal mode failed %d", r); + } + + if (enabled) { + int r = cts_start_device(cts_dev); + + if (r) { + cts_unlock_device(cts_dev); + return snprintf(buf, PAGE_SIZE, + "Start device failed %d", r); + } + } +err_free_data: + cts_unlock_device(cts_dev); + kfree(data); + + return (ret < 0 ? ret : count); +} + +/* echo start_addr size [filepath] > read */ +static ssize_t read_flash_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(read, S_IWUSR | S_IRUGO, read_flash_show, read_flash_store); + +/* echo addr size > erase */ +static ssize_t erase_flash_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u32 flash_addr, size; + int ret; + bool program_mode; + bool enabled; + + parse_arg(buf, count); + + if (argc != 2) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + ret = kstrtou32(argv[0], 0, &flash_addr); + if (ret) { + cts_err("Invalid flash addr: %s", argv[0]); + return -EINVAL; + } + ret = kstrtou32(argv[1], 0, &size); + if (ret) { + cts_err("Invalid size: %s", argv[1]); + return -EINVAL; + } + + cts_info("Erase flash from 0x%06x size %u", flash_addr, size); + + cts_lock_device(cts_dev); + program_mode = cts_is_device_program_mode(cts_dev); + enabled = cts_is_device_enabled(cts_dev); + + ret = cts_prepare_flash_operation(cts_dev); + if (ret) { + cts_err("Prepare flash operation failed %d", ret); + cts_unlock_device(cts_dev); + return ret; + } + + ret = cts_erase_flash(cts_dev, flash_addr, size); + if (ret) { + cts_err("Erase flash from 0x%06x size %u failed %d", + flash_addr, size, ret); + goto err_post_flash_operation; + } + +err_post_flash_operation: + cts_post_flash_operation(cts_dev); + + if (!program_mode) { + int r = cts_enter_normal_mode(cts_dev); + + if (r) + cts_err("Enter normal mode failed %d", r); + } + + if (enabled) { + int r = cts_start_device(cts_dev); + + if (r) + cts_err("Start device failed %d", r); + } + cts_unlock_device(cts_dev); + + return (ret < 0 ? ret : count); +} + +static DEVICE_ATTR(erase, S_IWUSR, NULL, erase_flash_store); + +static struct attribute *cts_dev_flash_attrs[] = { + &dev_attr_info.attr, + &dev_attr_read.attr, + &dev_attr_erase.attr, + + NULL +}; + +static const struct attribute_group cts_dev_flash_attr_group = { + .name = "flash", + .attrs = cts_dev_flash_attrs, +}; + +static ssize_t open_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + struct cts_test_param test_param = { + .test_item = CTS_TEST_OPEN, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = + "/sdcard/open-test-data.txt", + .num_invalid_node = 0, + .invalid_nodes = NULL, + }; + int min = 0; + int ret; + ktime_t start_time, end_time, delta_time; + + if (argc != 1) + return scnprintf(buf, PAGE_SIZE, "Invalid num args %d\n", argc); + + ret = kstrtoint(argv[0], 0, &min); + if (ret) + return scnprintf(buf, PAGE_SIZE, "Invalid min thres: %s\n", argv[0]); + + cts_info("Open test, threshold = %u", min); + + test_param.min = &min; + + start_time = ktime_get(); + + ret = cts_test_open(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret > 0) + return scnprintf(buf, PAGE_SIZE, + "Open test has %d nodes FAIL, min: %u, ELAPSED TIME: %lldms\n", + ret, min, ktime_to_ms(delta_time)); + else if (ret < 0) + return scnprintf(buf, PAGE_SIZE, + "Open test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + else + return scnprintf(buf, PAGE_SIZE, + "Open test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + +} + +/* echo threshod > open_test */ +static ssize_t open_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(open_test, S_IWUSR | S_IRUGO, open_test_show, + open_test_store); + +static ssize_t short_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + struct cts_test_param test_param = { + .test_item = CTS_TEST_SHORT, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = + "/sdcard/short-test-data.txt", + .num_invalid_node = 0, + .invalid_nodes = NULL, + }; + int min = 0; + int ret; + ktime_t start_time, end_time, delta_time; + + if (argc != 1) + return scnprintf(buf, PAGE_SIZE, "Invalid num args %d\n", argc); + + ret = kstrtoint(argv[0], 0, &min); + if (ret) + return scnprintf(buf, PAGE_SIZE, "Invalid min thres: %s\n", argv[0]); + + cts_info("Short test, threshold = %u", min); + + test_param.min = &min; + + start_time = ktime_get(); + + ret = cts_test_short(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret > 0) + return scnprintf(buf, PAGE_SIZE, + "Short test has %d nodes FAIL, min: %u, ELAPSED TIME: %lldms\n", + ret, min, ktime_to_ms(delta_time)); + else if (ret < 0) + return scnprintf(buf, PAGE_SIZE, + "Short test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + else + return scnprintf(buf, PAGE_SIZE, + "Short test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); +} + +/* echo threshod > short_test */ +static ssize_t short_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(short_test, S_IWUSR | S_IRUGO, + short_test_show, short_test_store); + +static ssize_t testing_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Testting: %s\n", + cts_data->cts_dev.rtdata.testing ? "Y" : "N"); +} + +static DEVICE_ATTR(testing, S_IRUGO, testing_show, NULL); + +#ifdef CFG_CTS_HAS_RESET_PIN +static ssize_t reset_pin_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef CFG_CTS_HAS_RESET_PIN + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + struct cts_test_param test_param = { + .test_item = CTS_TEST_RESET_PIN, + .flags = 0, + }; + int ret; + ktime_t start_time, end_time, delta_time; + + start_time = ktime_get(); + + ret = cts_test_reset_pin(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret) + return scnprintf(buf, PAGE_SIZE, + "Reset-Pin test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + else + return scnprintf(buf, PAGE_SIZE, + "Reset-Pin test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + +#else /* CFG_CTS_HAS_RESET_PIN */ + return scnprintf(buf, PAGE_SIZE, + "Reset-Pin test NOT supported(CFG_CTS_HAS_RESET_PIN not defined)\n"); +#endif +} +static DEVICE_ATTR(reset_pin_test, S_IRUGO, reset_pin_test_show, NULL); +#endif + +static ssize_t int_pin_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + struct cts_test_param test_param = { + .test_item = CTS_TEST_INT_PIN, + .flags = 0, + }; + int ret; + ktime_t start_time, end_time, delta_time; + + start_time = ktime_get(); + + ret = cts_test_int_pin(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret) + return scnprintf(buf, PAGE_SIZE, + "Int-Pin test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + else + return scnprintf(buf, PAGE_SIZE, + "Int-Pin test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); +} +static DEVICE_ATTR(int_pin_test, S_IRUGO, int_pin_test_show, NULL); + +static ssize_t compensate_cap_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + struct cts_test_param test_param = { + .test_item = CTS_TEST_COMPENSATE_CAP, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = + "/sdcard/comp-cap-test-data.txt", + .num_invalid_node = 0, + .invalid_nodes = NULL, + }; + int min = 0, max = 0; + int ret; + ktime_t start_time, end_time, delta_time; + + if (argc != 2) + return scnprintf(buf, PAGE_SIZE, "Invalid num args\n" + "USAGE:\n" + " 1. echo min max > compensate_cap_test\n" + " 2. cat compensate_cap_test\n"); + + ret = kstrtoint(argv[0], 0, &min); + if (ret) + return scnprintf(buf, PAGE_SIZE, + "Invalid min thres: %s\n", argv[0]); + + ret = kstrtoint(argv[1], 0, &max); + if (ret) + return scnprintf(buf, PAGE_SIZE, + "Invalid max thres: %s\n", argv[1]); + + cts_info("Compensate cap test, min: %u, max: %u", + min, max); + + test_param.min = &min; + test_param.max = &max; + + start_time = ktime_get(); + + ret = cts_test_compensate_cap(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret > 0) + return scnprintf(buf, PAGE_SIZE, + "Compensate cap test has %d nodes FAIL, " + "threshold[%u, %u], ELAPSED TIME: %lldms\n", + ret, min, max, ktime_to_ms(delta_time)); + else if (ret < 0) + return scnprintf(buf, PAGE_SIZE, + "Compensate cap test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + else + return scnprintf(buf, PAGE_SIZE, + "Compensate cap test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); +} + +/* echo threshod > short_test */ +static ssize_t compensate_cap_test_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(compensate_cap_test, S_IWUSR | S_IRUGO, + compensate_cap_test_show, compensate_cap_test_store); + +static ssize_t rawdata_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + struct cts_rawdata_test_priv_param priv_param = { + .frames = 16, + }; + struct cts_test_param test_param = { + .test_item = CTS_TEST_RAWDATA, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = + "/sdcard/rawdata-test-data.txt", + .num_invalid_node = 0, + .invalid_nodes = NULL, + .priv_param = &priv_param, + .priv_param_size = sizeof(priv_param), + }; + + int min, max; + int ret; + ktime_t start_time, end_time, delta_time; + + if (argc < 2 || argc > 3) + return scnprintf(buf, PAGE_SIZE, "Invalid num args\n" + "USAGE:\n" + " 1. echo min max [frames] > rawdata_test\n" + " 2. cat rawdata_test\n"); + + ret = kstrtoint(argv[0], 0, &min); + if (ret) + return scnprintf(buf, PAGE_SIZE, + "Invalid min thres: %s\n", argv[0]); + + ret = kstrtoint(argv[1], 0, &max); + if (ret) + return scnprintf(buf, PAGE_SIZE, + "Invalid max thres: %s\n", argv[1]); + + if (argc > 2) { + ret = kstrtou32(argv[2], 0, &priv_param.frames); + if (ret) + return scnprintf(buf, PAGE_SIZE, + "Invalid frames: %s\n", argv[2]); + } + cts_info("Rawdata test, frames: %u min: %d, max: %d", + priv_param.frames, min, max); + + test_param.min = &min; + test_param.max = &max; + + start_time = ktime_get(); + + ret = cts_test_rawdata(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret > 0) + return scnprintf(buf, PAGE_SIZE, + "Rawdata test has %d nodes FAIL, threshold[%u, %u], " + "ELAPSED TIME: %lldms\n", + ret, min, max, ktime_to_ms(delta_time)); + else if (ret < 0) + return scnprintf(buf, PAGE_SIZE, + "Rawdata test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + else + return scnprintf(buf, PAGE_SIZE, + "Rawdata test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); +} + +/* echo threshod > short_test */ +static ssize_t rawdata_test_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(rawdata_test, S_IWUSR | S_IRUGO, + rawdata_test_show, rawdata_test_store); + +static ssize_t noise_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + struct cts_noise_test_priv_param priv_param = { + .frames = 50, + }; + struct cts_test_param test_param = { + .test_item = CTS_TEST_NOISE, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = + "/sdcard/noise-test-data.txt", + .num_invalid_node = 0, + .invalid_nodes = NULL, + .priv_param = &priv_param, + .priv_param_size = sizeof(priv_param), + }; + + int max; + int ret; + ktime_t start_time, end_time, delta_time; + + if (argc < 1 || argc > 2) + return scnprintf(buf, PAGE_SIZE, "Invalid num args\n" + "USAGE:\n" + " 1. echo threshold [frames] > noise_test\n" + " 2. cat noise_test\n"); + + ret = kstrtoint(argv[0], 0, &max); + if (ret) + return scnprintf(buf, PAGE_SIZE, + "Invalid max thres: %s\n", argv[0]); + + if (argc > 1) { + ret = kstrtou32(argv[1], 0, &priv_param.frames); + if (ret) + return scnprintf(buf, PAGE_SIZE, + "Invalid frames: %s\n", argv[1]); + } + cts_info("Noise test, frames: %u threshold: %d", + priv_param.frames, max); + + test_param.max = &max; + + start_time = ktime_get(); + + ret = cts_test_noise(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret > 0) + return scnprintf(buf, PAGE_SIZE, + "Noise test has %d nodes FAIL, max: %u, ELAPSED TIME: %lldms\n", + ret, max, ktime_to_ms(delta_time)); + else if (ret < 0) + return scnprintf(buf, PAGE_SIZE, + "Noise test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + else + return scnprintf(buf, PAGE_SIZE, + "Noise test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); +} + +static ssize_t noise_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + parse_arg(buf, count); + + return count; +} +static DEVICE_ATTR(noise_test, S_IWUSR | S_IRUGO, + noise_test_show, noise_test_store); + +static struct attribute *cts_dev_test_atts[] = { + &dev_attr_open_test.attr, + &dev_attr_short_test.attr, + &dev_attr_testing.attr, +#ifdef CFG_CTS_HAS_RESET_PIN + &dev_attr_reset_pin_test.attr, +#endif + &dev_attr_int_pin_test.attr, + &dev_attr_compensate_cap_test.attr, + &dev_attr_rawdata_test.attr, + &dev_attr_noise_test.attr, + NULL +}; + +static const struct attribute_group cts_dev_test_attr_group = { + .name = "test", + .attrs = cts_dev_test_atts, +}; + +static ssize_t ic_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "IC Type : %s\n", + cts_data->cts_dev.hwdata->name); +} + +static DEVICE_ATTR(ic_type, S_IRUGO, ic_type_show, NULL); + +static ssize_t program_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Program mode: %s\n", + cts_data->cts_dev.rtdata.program_mode ? "Y" : "N"); +} + +static ssize_t program_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + int ret; + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + if (*argv[0] == '1' || tolower(*argv[0]) == 'y') { + ret = cts_enter_program_mode(&cts_data->cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + return ret; + } + } else if (*argv[0] == '0' || tolower(*argv[0]) == 'n') { + ret = cts_enter_normal_mode(&cts_data->cts_dev); + if (ret) { + cts_err("Exit program mode failed %d", ret); + return ret; + } + } else + cts_err("Invalid args"); + + return count; +} + +static DEVICE_ATTR(program_mode, S_IWUSR | S_IRUGO, + program_mode_show, program_mode_store); + +static ssize_t rawdata_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#define RAWDATA_BUFFER_SIZE(cts_dev) \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * 2) + + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 *rawdata = NULL; + int ret, r, c, count = 0; + u32 max, min, sum, average; + int max_r, max_c, min_r, min_c; + bool data_valid = true; + + cts_info("Show rawdata"); + + rawdata = (u16 *) kmalloc(RAWDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata == NULL) + return snprintf(buf, PAGE_SIZE, + "Allocate memory for rawdata failed\n"); + + cts_lock_device(cts_dev); + ret = + cts_dev->ops->top_get_rawdata(cts_dev, (u8 *) rawdata, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret < 0) { + cts_err("Get rawdata failed"); + goto err_free_rawdata; + } + data_valid = true; + + if (data_valid) { + max = min = rawdata[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + u16 val = + rawdata[r * cts_dev->hwdata->num_col + c]; + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count += snprintf(buf + count, PAGE_SIZE - count, + SPLIT_LINE_STR + "Raw data MIN: [%d][%d]=%u, MAX: [%d][%d]=%u, AVG=%u\n" + SPLIT_LINE_STR + " | ", min_r, min_c, min, max_r, max_c, + max, average); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += snprintf(buf + count, PAGE_SIZE - count, + COL_NUM_FORMAT_STR, c); + + count += snprintf(buf + count, PAGE_SIZE - count, + "\n" SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows && count < PAGE_SIZE; r++) { + count += snprintf(buf + count, PAGE_SIZE - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; + c < cts_dev->fwdata.cols && count < PAGE_SIZE; + c++) + count += + snprintf(buf + count, PAGE_SIZE - count - 1, + DATA_FORMAT_STR, + rawdata[r * + cts_dev->hwdata->num_col + + c]); + + buf[count++] = '\n'; + } + } + +err_free_rawdata: + cts_unlock_device(cts_dev); + kfree(rawdata); + + return data_valid ? count : ret; + +#undef RAWDATA_BUFFER_SIZE +} + +static DEVICE_ATTR(rawdata, S_IRUGO, rawdata_show, NULL); + +static ssize_t diffdata_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + s16 *diffdata = NULL; + int ret, r, c, count = 0; + int max, min, sum, average; + int max_r, max_c, min_r, min_c; + bool data_valid = true; + + cts_info("Show diffdata"); + + diffdata = (s16 *) kmalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (diffdata == NULL) { + cts_err("Allocate memory for diffdata failed"); + return -ENOMEM; + } + + cts_lock_device(cts_dev); + ret = + cts_dev->ops->top_get_real_diff(cts_dev, (u8 *) diffdata, + DIFFDATA_BUFFER_SIZE(cts_dev)); + if (ret < 0) { + cts_err("Get diffdata failed"); + goto err_free_diffdata; + } + data_valid = true; + + if (data_valid) { + max = min = diffdata[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + s16 val = + diffdata[r * cts_dev->hwdata->num_col + c]; + + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count += snprintf(buf + count, PAGE_SIZE - count, + SPLIT_LINE_STR + "Diff data MIN: [%d][%d]=%d, MAX: [%d][%d]=%d, AVG=%d\n" + SPLIT_LINE_STR + " | ", min_r, min_c, min, max_r, max_c, + max, average); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += snprintf(buf + count, PAGE_SIZE - count, + COL_NUM_FORMAT_STR, c); + + count += snprintf(buf + count, PAGE_SIZE - count, + "\n" SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count += snprintf(buf + count, PAGE_SIZE - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += + snprintf(buf + count, PAGE_SIZE - count, + DATA_FORMAT_STR, + diffdata[r * + cts_dev->hwdata->num_col + + c]); + + buf[count++] = '\n'; + } + } + +err_free_diffdata: + cts_unlock_device(cts_dev); + kfree(diffdata); + + return data_valid ? count : ret; +} + +static DEVICE_ATTR(diffdata, S_IRUGO, diffdata_show, NULL); + +static ssize_t manualdiffdata_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + s16 *rawdata = NULL; + int ret, r, c, count = 0; + int max, min, sum, average; + int max_r, max_c, min_r, min_c; + bool data_valid = true; + int frame; + struct file *file = NULL; + int i; + loff_t pos = 0; + + cts_info("Show manualdiff"); + + if (argc != 1 && argc != 2) { + return snprintf(buf, PAGE_SIZE, "Invalid num args\n" + "USAGE:\n" + " 1. echo updatebase > manualdiff\n" + " 2. cat manualdiff\n" + " or\n" + " 1. echo updatebase > manualdiff\n" + " 2. echo frame file > manualdiff\n" + " 3. cat manualdiff\n"); + } + + if (argc == 2) { + ret = kstrtou32(argv[0], 0, &frame); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid frame num\n"); + file = filp_open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0666); + if (IS_ERR(file)) + return snprintf(buf, PAGE_SIZE, "Can't open file:%s", + argv[1]); + } else + frame = 1; + + rawdata = (s16 *) kmalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate memory for rawdata failed"); + filp_close(file, NULL); + return -ENOMEM; + } + + if (!manualdiff_base && (DIFFDATA_BUFFER_SIZE(cts_dev) > 0)) { + manualdiff_base = + (s16 *) kzalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (manualdiff_base == NULL) { + cts_err("Malloc manualdiff_base failed"); + filp_close(file, NULL); + kfree(rawdata); + goto err_free_diffdata; + } + } + cts_lock_device(cts_dev); + + cts_info("frame %d, file:%s", frame, argv[1]); + + if (!manualdiff_base_updated) { + ret = + cts_dev->ops->top_get_rawdata(cts_dev, + (u8 *) manualdiff_base, + DIFFDATA_BUFFER_SIZE + (cts_dev)); + if (ret) + cts_err("Get raw data failed %d", ret); + } + for (i = 0; i < frame; i++) { + ret = + cts_dev->ops->top_get_rawdata(cts_dev, (u8 *) rawdata, + DIFFDATA_BUFFER_SIZE + (cts_dev)); + if (ret) { + cts_err("Get raw data failed %d", ret); + data_valid = false; + } else + data_valid = true; + msleep(5); + + if (data_valid) { + max = -32768; + min = 32767; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + s16 val; + + rawdata[r * cts_dev->hwdata->num_col + + c] -= + manualdiff_base[r * + cts_dev->hwdata->num_col + c]; + val = + rawdata[r * + cts_dev->hwdata->num_col + + c]; + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = + sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + count += + snprintf(buf + count, PAGE_SIZE - count, + SPLIT_LINE_STR + "Manualdiff data MIN: [%d][%d]=%d, MAX: [%d][%d]=%d, AVG=%d\n" + SPLIT_LINE_STR " | ", min_r, min_c, min, + max_r, max_c, max, average); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += + snprintf(buf + count, PAGE_SIZE - count, + COL_NUM_FORMAT_STR, c); + + count += snprintf(buf + count, PAGE_SIZE - count, + "\n" SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count += + snprintf(buf + count, PAGE_SIZE - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += + snprintf(buf + count, + PAGE_SIZE - count, + DATA_FORMAT_STR, + rawdata[r * + cts_dev->hwdata->num_col + c]); + + buf[count++] = '\n'; + } + /*cts_info("manualdiffdata_show:%d, %d", i, frame); */ + if (argc == 2) { + pos = file->f_pos; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(file, buf, count, &pos); +#else + ret = kernel_write(file, buf, count, pos); +#endif + if (ret != count) + cts_err + ("Write data to file '%s' failed %d", + argv[1], ret); + + file->f_pos += count; + count = 0; + } + } + } + if (argc == 2) + filp_close(file, NULL); + +err_free_diffdata: + cts_unlock_device(cts_dev); + kfree(rawdata); + + return data_valid ? count : ret; +} + +static ssize_t manualdiffdata_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + + parse_arg(buf, count); + + cts_lock_device(cts_dev); + if (strncasecmp("updatebase", argv[0], 10) == 0) { + ret = cts_dev->ops->enable_get_rawdata(cts_dev); + if (ret) { + cts_err("Enable read raw data failed %d", ret); + goto err_manual_diff_store; + } + + if (!manualdiff_base && (DIFFDATA_BUFFER_SIZE(cts_dev) > 0)) { + manualdiff_base = + (s16 *) kzalloc(DIFFDATA_BUFFER_SIZE(cts_dev), + GFP_KERNEL); + if (manualdiff_base == NULL) { + cts_err("Malloc manualdiff_base failed"); + goto err_manual_diff_store; + } + } + if (manualdiff_base != NULL) { + ret = cts_get_rawdata(cts_dev, manualdiff_base); + if (ret) + cts_err("Get raw data failed %d", ret); + manualdiff_base_updated = 1; + } + /* cts_info("update base successful"); */ + ret = cts_dev->ops->disable_get_rawdata(cts_dev); + if (ret) + cts_err("Disable read raw data failed %d", ret); + } +err_manual_diff_store: + cts_unlock_device(cts_dev); + return count; +} + +static DEVICE_ATTR(manualdiff, S_IRUSR | S_IWUSR, manualdiffdata_show, + manualdiffdata_store); + +static ssize_t jitter_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + u16 cnt; + int ret; + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + ret = kstrtou16(argv[0], 0, &cnt); + if (ret == 0) { + if (cnt > 2 && cnt < 10000) + jitter_test_frame = cnt; + } + + cts_info("jitter test frame: %d", jitter_test_frame); + return count; +} + +static ssize_t jitter_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + s16 *rawdata = NULL; + s16 *rawdata_min = NULL; + s16 *rawdata_max = NULL; + int ret, r, c, count = 0; + int max, min, sum, average; + int max_r, max_c, min_r, min_c; + bool data_valid = false; + int i; + + cts_info("Show jitter"); + cts_lock_device(cts_dev); + rawdata = (s16 *) kmalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate memory for rawdata failed"); + ret = -ENOMEM; + goto err_jitter_show_exit; + } + rawdata_min = + (s16 *) kmalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata_min == NULL) { + cts_err("Allocate memory for rawdata failed"); + ret = -ENOMEM; + goto err_free_rawdata; + } + rawdata_max = + (s16 *) kmalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata_max == NULL) { + cts_err("Allocate memory for rawdata failed"); + ret = -ENOMEM; + goto err_free_rawdata_min; + } + + for (i = 0; i < DIFFDATA_BUFFER_SIZE(cts_dev) / 2; i++) { + rawdata_min[i] = 32767; + rawdata_max[i] = -32768; + } + + for (i = 0; i < jitter_test_frame; i++) { + ret = + cts_dev->ops->top_get_rawdata(cts_dev, (u8 *) rawdata, + DIFFDATA_BUFFER_SIZE + (cts_dev)); + if (ret) { + cts_err("Get raw data failed %d", ret); + msleep(5); + continue; + } + for (r = 0; r < cts_dev->hwdata->num_row; r++) { + for (c = 0; c < cts_dev->hwdata->num_col; c++) { + int index; + + index = r * cts_dev->hwdata->num_col + c; + if (rawdata_min[index] > rawdata[index]) + rawdata_min[index] = rawdata[index]; + else if (rawdata_max[index] < rawdata[index]) + rawdata_max[index] = rawdata[index]; + } + } + } + data_valid = true; + + if (data_valid) { + max = -32768; + min = 32767; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + s16 val; + int index = r * cts_dev->hwdata->num_col + c; + + val = rawdata_max[index] - rawdata_min[index]; + rawdata[index] = val; + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count += snprintf(buf + count, PAGE_SIZE - count, + SPLIT_LINE_STR + "Jitter data MIN: [%d][%d]=%d, MAX: [%d][%d]=%d, AVG=%d, TOTAL FRAME=%d\n" + SPLIT_LINE_STR + " | ", min_r, min_c, min, max_r, max_c, + max, average, jitter_test_frame); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += snprintf(buf + count, PAGE_SIZE - count, + COL_NUM_FORMAT_STR, c); + + count += snprintf(buf + count, PAGE_SIZE - count, + "\n" SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count += snprintf(buf + count, PAGE_SIZE - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += + snprintf(buf + count, PAGE_SIZE - count, + DATA_FORMAT_STR, + rawdata[r * + cts_dev->hwdata->num_col + + c]); + + buf[count++] = '\n'; + } + } + + //err_free_rawdata_max: + kfree(rawdata_max); +err_free_rawdata_min: + kfree(rawdata_min); +err_free_rawdata: + kfree(rawdata); +err_jitter_show_exit: + cts_unlock_device(cts_dev); + return data_valid ? count : ret; +} + +static DEVICE_ATTR(jitter, S_IRUSR | S_IWUSR, jitter_show, jitter_store); + +static ssize_t compensate_cap_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 *cap = NULL; + int ret; + ssize_t count = 0; + int r, c, min, max, max_r, max_c, min_r, min_c, sum, average; + + cts_info("Read '%s'", attr->attr.name); + + cap = kzalloc(cts_dev->hwdata->num_row * cts_dev->hwdata->num_col, + GFP_KERNEL); + if (cap == NULL) + return scnprintf(buf, PAGE_SIZE, + "Allocate mem for compensate cap failed\n"); + + cts_lock_device(cts_dev); + ret = cts_dev->ops->top_get_cnegdata(cts_dev, cap, + cts_dev->hwdata->num_row * + cts_dev->hwdata->num_col); + cts_unlock_device(cts_dev); + if (ret) { + kfree(cap); + return scnprintf(buf, PAGE_SIZE, + "Get compensate cap failed %d\n", ret); + } + + max = min = cap[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + u16 val = cap[r * cts_dev->hwdata->num_col + c]; + + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count += scnprintf(buf + count, PAGE_SIZE - count, + "--------------------------------------"\ + "--------------------------------------\n" + " Compensatete Cap MIN: [%d][%d]=%u, MAX: [%d][%d]=%u, AVG=%u\n" + "---+----------------------------------"\ + "--------------------------------------\n" + " |", min_r, min_c, min, max_r, max_c, max, + average); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += scnprintf(buf + count, PAGE_SIZE - count, " %3u", c); + + count += scnprintf(buf + count, PAGE_SIZE - count, + "\n" + "---+----------------------------------"\ + "--------------------------------------\n"); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count += scnprintf(buf + count, PAGE_SIZE - count, "%2u |", r); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += scnprintf(buf + count, PAGE_SIZE - count, + " %3u", + cap[r * cts_dev->hwdata->num_col + + c]); + } + buf[count++] = '\n'; + } + count += scnprintf(buf + count, PAGE_SIZE - count, + "---+----------------------------------"\ + "--------------------------------------\n"); + + kfree(cap); + + return count; +} + +static DEVICE_ATTR(compensate_cap, S_IRUGO, compensate_cap_show, NULL); + +#ifdef CFG_CTS_HAS_RESET_PIN +static ssize_t reset_pin_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + cts_info("Read RESET-PIN"); + + return snprintf(buf, PAGE_SIZE, + "Reset pin: %d, status: %d\n", + cts_data->pdata->rst_gpio, + gpio_get_value(cts_data->pdata->rst_gpio)); +} + +static ssize_t reset_pin_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + cts_info("Write RESET-PIN"); + cts_info("Chip staus maybe changed"); + + cts_plat_set_reset(cts_dev->pdata, (buf[0] == '1') ? 1 : 0); + return count; +} + +static DEVICE_ATTR(reset_pin, S_IRUSR | S_IWUSR, reset_pin_show, + reset_pin_store); +#endif + +static ssize_t irq_pin_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + cts_info("Read IRQ-PIN"); + + return snprintf(buf, PAGE_SIZE, + "IRQ pin: %d, status: %d\n", + cts_data->pdata->int_gpio, + gpio_get_value(cts_data->pdata->int_gpio)); +} + +static DEVICE_ATTR(irq_pin, S_IRUGO, irq_pin_show, NULL); + +static ssize_t irq_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct irq_desc *desc; + + cts_info("Read IRQ-INFO"); + + desc = irq_to_desc(cts_data->pdata->irq); + if (desc == NULL) { + return snprintf(buf, PAGE_SIZE, + "IRQ: %d descriptor not found\n", + cts_data->pdata->irq); + } + + return snprintf(buf, PAGE_SIZE, + "IRQ num: %d, depth: %u, " + "count: %u, unhandled: %u, last unhandled eslape: %lu\n", + cts_data->pdata->irq, desc->depth, + desc->irq_count, desc->irqs_unhandled, + desc->last_unhandled); +} + +static DEVICE_ATTR(irq_info, S_IRUGO, irq_info_show, NULL); + +#ifdef CFG_CTS_FW_LOG_REDIRECT +static ssize_t fw_log_redirect_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + return snprintf(buf, PAGE_SIZE, "Fw log redirect is %s\n", + cts_is_fw_log_redirect(cts_dev) ? "enable" : "disable"); +} + +static ssize_t fw_log_redirect_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 enable = 0; + + if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') + enable = 1; + if (enable) + cts_enable_fw_log_redirect(cts_dev); + else + cts_disable_fw_log_redirect(cts_dev); + + return count; +} + +static DEVICE_ATTR(fw_log_redirect, S_IRUSR | S_IWUSR, fw_log_redirect_show, + fw_log_redirect_store); +#endif + +static ssize_t debug_spi_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + return snprintf(buf, PAGE_SIZE, "spi_speed=%d\n", + cts_dev->pdata->spi_speed); +} + +static ssize_t debug_spi_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u16 s = 0; + int ret = 0; + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + ret = kstrtou16(argv[0], 0, &s); + if (ret) { + cts_err("Invalid spi speed: %s", argv[0]); + return -EINVAL; + } + + cts_dev->pdata->spi_speed = s; + + return count; +} + +static DEVICE_ATTR(debug_spi, S_IRUSR | S_IWUSR, debug_spi_show, + debug_spi_store); + +#ifdef CFG_CTS_GESTURE +static ssize_t gesture_en_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + return sprintf(buf, "Gesture wakup is %s\n", + cts_is_gesture_wakeup_enabled(cts_dev) ? "enable" : + "disable"); +} + +static ssize_t gesture_en_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 enable = 0; + + if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') + enable = 1; + + if (enable) + cts_enable_gesture_wakeup(cts_dev); + else + cts_disable_gesture_wakeup(cts_dev); + + return count; +} + +static DEVICE_ATTR(gesture_en, S_IRUSR | S_IWUSR, gesture_en_show, + gesture_en_store); +#endif + +static ssize_t int_data_types_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%#04x\n", + cts_data->cts_dev.fwdata.int_data_types); +} + +static ssize_t int_data_types_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + u16 type = 0; + int ret = 0; + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + ret = kstrtou16(argv[0], 0, &type); + if (ret) { + cts_err("Invalid int data types: %s", argv[0]); + return -EINVAL; + } + + cts_lock_device(&cts_data->cts_dev); + ret = cts_set_int_data_types(&cts_data->cts_dev, type); + cts_unlock_device(&cts_data->cts_dev); + if (ret) + return -EIO; + return count; +} + +static DEVICE_ATTR(int_data_types, S_IWUSR | S_IRUGO, + int_data_types_show, int_data_types_store); + +static ssize_t int_data_method_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + cts_data->cts_dev.fwdata.int_data_method); +} + +static ssize_t int_data_method_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + u8 method = 0; + int ret = 0; + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + ret = kstrtou8(argv[0], 0, &method); + if (ret) { + cts_err("Invalid int data method: %s", argv[0]); + return -EINVAL; + } else if (method >= INT_DATA_METHOD_CNT) { + cts_err("Invalid int data method: %s", argv[0]); + return -EINVAL; + } + + cts_lock_device(&cts_data->cts_dev); + ret = cts_set_int_data_method(&cts_data->cts_dev, method); + cts_unlock_device(&cts_data->cts_dev); + if (ret) + return -EIO; + return count; +} + +static DEVICE_ATTR(int_data_method, S_IWUSR | S_IRUGO, + int_data_method_show, int_data_method_store); + + +#ifdef CFG_DUMP_INT_DATA +/* For dump bin file */ +static int cts_write_intdata(struct file *filp, const void *data, size_t size) +{ + loff_t pos; + ssize_t ret; + + pos = filp->f_pos; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + ret = kernel_write(filp, data, size, &pos); +#else + ret = kernel_write(filp, data, size, pos); +#endif + + if (ret >= 0) { + filp->f_pos += ret; + } + + return ret; +} + +u32 old_cnt; +struct file *cts_int_data_filp = NULL; +char chipone_bin_path[64] = "/sdcard/chipone.bin"; +void cts_dump_int_tsdata(const u8 *data, size_t size) +{ + if (cts_int_data_filp) { + cts_write_intdata(cts_int_data_filp, data, size); + } +} + +static int cts_start_dump_int_data_to_file(const char *filepath, bool append) +{ + int ret; + u8 header[24] = { 0x00 }; + + cts_info("Start dump test data to file '%s'", filepath); + + cts_int_data_filp = filp_open(filepath, + O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC), + S_IRUGO | S_IWUGO); + if (IS_ERR(cts_int_data_filp)) { + ret = PTR_ERR(cts_int_data_filp); + cts_int_data_filp = NULL; + cts_err("Open file '%s' for test data failed %d", + filepath, ret); + return ret; + } + + if (cts_int_data_filp) + cts_dump_int_tsdata(header, sizeof(header)); + + return 0; +} + +static void cts_stop_dump_int_data_to_file(void) +{ + int r; + + cts_info("Stop dump test data to file"); + + if (cts_int_data_filp) { + r = filp_close(cts_int_data_filp, NULL); + if (r) { + cts_err("Close test data file failed %d", r); + } + cts_int_data_filp = NULL; + } else { + cts_warn("Stop dump tsdata to file with filp = NULL"); + } +} + +static void cts_redump_header_data_to_file(struct chipone_ts_data *cts_data, + const char *filepath, bool append) +{ + int ret; + u32 cnt; + u8 header[24] = { + /* Flag */ 'C', 'h', 'i', 'p', 'o', 'n', 'e', + /* Header len */ 0x10, + /* Version */ 0x00, 0x00, 0x01 , 0x00, + /* Data type */ 0x00, 0x00, + /* Rows and Cols */ 32, 18, + /* Frame cnt */ 0x00, 0x00, 0x00, 0x00, + /* Total time */ 0x00, 0x00, 0x00, 0x00 + }; + + header[12] = cts_data->cts_dev.fwdata.int_data_types & 0xFF; + header[13] = (cts_data->cts_dev.fwdata.int_data_types >> 8) & 0xFF; + header[14] = cts_data->cts_dev.fwdata.rows; + header[15] = cts_data->cts_dev.fwdata.cols; + + if (old_cnt == 0) { + cnt = 0xffffffff - cts_data->cts_dev.rtdata.dump_cnt; + } else { + cnt = old_cnt; + } + + header[16] = cnt & 0xff; + header[17] = (cnt >> 8) & 0xff; + header[18] = (cnt >> 16) & 0xff; + header[19] = (cnt >> 24) & 0xff; + + cts_int_data_filp = filp_open(filepath, + O_WRONLY | O_CREAT | O_WRONLY, + S_IRUGO | S_IWUGO); + if (IS_ERR(cts_int_data_filp)) { + ret = PTR_ERR(cts_int_data_filp); + cts_int_data_filp = NULL; + cts_err("Open file '%s' for test data failed %d", + filepath, ret); + return; + } + + if (cts_int_data_filp) + cts_dump_int_tsdata(header, sizeof(header)); + + if (cts_int_data_filp) { + int r = filp_close(cts_int_data_filp, NULL); + if (r) { + cts_err("Close test data file failed %d", r); + } + cts_int_data_filp = NULL; + } else { + cts_warn("Stop dump tsdata to file with filp = NULL"); + } + + return; +} + +static ssize_t dump_int_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "Dumping: %s\n", + cts_data->cts_dev.rtdata.dumping ? "Y" : "N"); +} + +static ssize_t dump_int_data_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + u8 dumping = 0; + int ret = 0; + + parse_arg(buf, count); + + if (argc != 2) { + cts_err("Invalid num args != 2%d", argc); + return -EFAULT; + } + + ret = kstrtou8(argv[0], 0, &dumping); + if (ret) { + cts_err("Invalid int data dumping: %s", argv[0]); + return -EINVAL; + } + if (!!dumping) { + if (cts_int_data_filp) { + cts_err("IS Dumpping ... , please stop dumping then restart."); + return -EINVAL; + } + } + + ret = kstrtou32(argv[1], 0, &old_cnt); + if (ret) { + cts_err("Invalid int data dump cnt: %s", argv[1]); + return -EINVAL; + } + + if (!!dumping) { + cts_data->cts_dev.rtdata.dump_cnt = old_cnt; + cts_start_dump_int_data_to_file(chipone_bin_path, false); + } else { + cts_stop_dump_int_data_to_file(); + cts_redump_header_data_to_file(cts_data, chipone_bin_path, true); + } + cts_data->cts_dev.rtdata.dumping = !!dumping; + + return count; +} + +static DEVICE_ATTR(dump_int_data, S_IWUSR | S_IRUGO, + dump_int_data_show, dump_int_data_store); +#endif + +static struct attribute *cts_dev_misc_atts[] = { + &dev_attr_ic_type.attr, + &dev_attr_program_mode.attr, + &dev_attr_rawdata.attr, + &dev_attr_diffdata.attr, + &dev_attr_manualdiff.attr, + &dev_attr_jitter.attr, +#ifdef CFG_CTS_HAS_RESET_PIN + &dev_attr_reset_pin.attr, +#endif + &dev_attr_irq_pin.attr, + &dev_attr_irq_info.attr, +#ifdef CFG_CTS_FW_LOG_REDIRECT + &dev_attr_fw_log_redirect.attr, +#endif + &dev_attr_compensate_cap.attr, + &dev_attr_read_reg.attr, + &dev_attr_write_reg.attr, + &dev_attr_read_hw_reg.attr, + &dev_attr_write_hw_reg.attr, + &dev_attr_debug_spi.attr, +#ifdef CFG_CTS_GESTURE + &dev_attr_gesture_en.attr, +#endif /* CFG_CTS_GESTURE */ + &dev_attr_int_data_types.attr, + &dev_attr_int_data_method.attr, +#ifdef CFG_DUMP_INT_DATA + &dev_attr_dump_int_data.attr, +#endif + NULL +}; + +static const struct attribute_group cts_dev_misc_attr_group = { + .name = "misc", + .attrs = cts_dev_misc_atts, +}; + +#ifdef CFG_CTS_FW_UPDATE_SYS +static struct attribute *cts_dev_fw_up_atts[] = { + /** + * &dev_attr_drv_irq.attr, + * &dev_attr_reset.attr, + */ + &dev_attr_buildid.attr, + &dev_attr_forcereflash.attr, + &dev_attr_flashprog.attr, + &dev_attr_doreflash.attr, + &dev_attr_poweron.attr, + &dev_attr_productinfo.attr, + &dev_attr_panel_supplier.attr, + NULL +}; + +static const struct attribute_group cts_dev_fw_up_attr_group = { + .attrs = cts_dev_fw_up_atts, +}; +#endif + +static const struct attribute_group *cts_dev_attr_groups[] = { + &cts_dev_firmware_attr_group, +#ifdef CFG_CTS_FW_UPDATE_SYS + &cts_dev_fw_up_attr_group, +#endif + &cts_dev_flash_attr_group, + &cts_dev_test_attr_group, + &cts_dev_misc_attr_group, + NULL +}; + +#include +#include + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *data = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!data) { + cts_err("Read 'path' with chipone_ts_data NULL"); + return (ssize_t) 0; + } +#ifdef CONFIG_CTS_I2C_HOST + path = kobject_get_path(&data->i2c_client->dev.kobj, GFP_KERNEL); +#else + path = kobject_get_path(&data->spi_client->dev.kobj, GFP_KERNEL); +#endif + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "chipone"); +} + +/* Attribute: vendor (RO) */ +static ssize_t ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *ts = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%s%s\n%s%04x\n%s%04x\n", + "Product ID: ", ts->cts_dev.hwdata->name, + "Build ID: ", ts->cts_dev.fwdata.version + ? ts->cts_dev.fwdata.version + : ts->pdata->build_id, + "Config ID: ", ts->cts_dev.fwdata.ddi_version + ? ts->cts_dev.fwdata.ddi_version + : ts->pdata->config_id); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_RO(ic_ver), + __ATTR_NULL +}; + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +/******************************************************* + *Description: + * Chipone touchscreen FW function class. file node + * initial function. + * + * return: + * Executive outcomes. 0---succeed. -1---failed. + *******************************************************/ +static int cts_fw_class_init(void *_data, bool create) +{ + struct chipone_ts_data *data = _data; + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + static struct class *touchscreen_class; + static struct device *ts_class_dev; + dev_t devno; + + cts_info("%s touchscreen class files", create ? "Add" : "Remove"); + + if (create) { + if (data->cts_dev.hwdata->name != NULL) + error = + alloc_chrdev_region(&devno, 0, 1, + data->cts_dev.hwdata->name); + else + error = + alloc_chrdev_region(&devno, 0, 1, + CFG_CTS_CHIP_NAME); + + if (error) { + cts_info("Alloc input devno failed %d", error); + return error; + } + + cts_info("Create class 'touchscreen'"); + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + cts_err("Create class 'touchscreen' failed %ld", + PTR_ERR(touchscreen_class)); + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + + if (data->cts_dev.hwdata->name != NULL) { + ts_class_dev = device_create(touchscreen_class, NULL, + devno, data, "%s", + data->cts_dev.hwdata->name); + cts_info("Create device for IC: %s", + data->cts_dev.hwdata->name); + } else { + ts_class_dev = device_create(touchscreen_class, NULL, + devno, data, "%s", + CFG_CTS_CHIP_NAME); + cts_info("Create device '" CFG_CTS_CHIP_NAME "'"); + } + if (IS_ERR(ts_class_dev)) { + cts_err("Create device '" CFG_CTS_CHIP_NAME + "'failed %ld", PTR_ERR(ts_class_dev)); + error = PTR_ERR(ts_class_dev); + ts_class_dev = NULL; + return error; + } + + cts_info("Create attr files"); + for (i = 0; attrs[i].attr.name != NULL; ++i) { + cts_info(" Create attr file '%s'", attrs[i].attr.name); + error = device_create_file(ts_class_dev, &attrs[i]); + if (error) { + cts_err("Create attr file '%s' failed %d", + attrs[i].attr.name, error); + break; + } + } + + if (error) + goto device_destroy; + else + cts_info("Create /sys/class/touchscreen/ Succeeded"); + } else { + if (!touchscreen_class || !ts_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + cts_info("Remove device file '%s'", attrs[i].attr.name); + device_remove_file(ts_class_dev, &attrs[i]); + } + device_unregister(ts_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, devno); + ts_class_dev = NULL; + class_unregister(touchscreen_class); + cts_err("Creating touchscreen class failed %d", error); + + return -ENODEV; +} + +int cts_sysfs_add_device(struct device *dev) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret, i; + + cts_info("Add device attr groups"); + + /*Low version kernel NOT support sysfs_create_groups() */ + for (i = 0; cts_dev_attr_groups[i]; i++) { + ret = sysfs_create_group(&dev->kobj, cts_dev_attr_groups[i]); + if (ret) { + while (--i >= 0) + sysfs_remove_group(&dev->kobj, + cts_dev_attr_groups[i]); + break; + } + } + + if (ret) { + cts_err("Add device attr failed %d", ret); + return ret; + } + + if (DIFFDATA_BUFFER_SIZE(cts_dev) > 0) { + manualdiff_base = + (s16 *) kzalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (manualdiff_base == NULL) { + cts_err("Malloc manualdiff_base failed"); + return -ENOMEM; + } + } + + ret = sysfs_create_link(NULL, &dev->kobj, "chipone-tddi"); + if (ret) + cts_err("Create sysfs link error:%d", ret); + + ret = cts_fw_class_init(cts_data, true); + if (ret) { + cts_err("Create touchscreen class failed. ret=%d\n", ret); + return ret; + } + + return 0; +} + +void cts_sysfs_remove_device(struct device *dev) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + int i; + + cts_info("Remove device attr groups"); + + if (manualdiff_base != NULL) { + kfree(manualdiff_base); + manualdiff_base = NULL; + } + + sysfs_remove_link(NULL, "chipone-tddi"); + /*Low version kernel NOT support sysfs_remove_groups() */ + for (i = 0; cts_dev_attr_groups[i]; i++) + sysfs_remove_group(&dev->kobj, cts_dev_attr_groups[i]); + + cts_fw_class_init(cts_data, false); +} + +#undef SPLIT_LINE_STR +#undef ROW_NUM_FORMAT_STR +#undef COL_NUM_FORMAT_STR +#undef DATA_FORMAT_STR + +#endif /* CONFIG_CTS_SYSFS */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sysfs.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sysfs.h new file mode 100644 index 000000000000..ea9ec24267be --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_sysfs.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_SYSFS_H +#define CTS_SYSFS_H + +#include "cts_config.h" + +struct device; + +extern u16 speed; + +#ifdef CONFIG_CTS_SYSFS +extern int cts_sysfs_add_device(struct device *dev); +extern void cts_sysfs_remove_device(struct device *dev); +#else /* CONFIG_CTS_SYSFS */ +static inline int cts_sysfs_add_device(struct device *dev) +{ + return -ENOTSUPP; +} + +static inline void cts_sysfs_remove_device(struct device *dev) +{ +} +#endif /* CONFIG_CTS_SYSFS */ + +#endif /* CTS_SYSFS_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tcs.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tcs.c new file mode 100644 index 000000000000..b5b67c594182 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tcs.c @@ -0,0 +1,1738 @@ +// SPDX-License-Identifier: GPL-2.0 +#include + +#define LOG_TAG "TCS" + +#include "cts_config.h" +#include "cts_firmware.h" +#include "cts_platform.h" + +#define _CTS_TCS_C_ +#include "cts_tcs.h" +#undef _CTS_TCS_C_ + +#define TEST_RESULT_BUFFER_SIZE(cts_dev) \ + (cts_dev->fwdata.rows * cts_dev->fwdata.cols * 2) + +#define RAWDATA_BUFFER_SIZE(cts_dev) \ + (cts_dev->fwdata.rows * cts_dev->fwdata.cols * 2) + +#define DIFFDATA_BUFFER_SIZE(cts_dev) \ + ((cts_dev->fwdata.rows + 2) * (cts_dev->fwdata.cols + 2) * 2) + +/* raw touch info without data */ +#define TOUCH_INFO_SIZ (112) +/* tcs reply tail: (errcode + cmd + crc) */ +#define TCS_REPLY_TAIL_SIZ (sizeof(u8) + sizeof(u16) + sizeof(u16)) + +#define INT_DATA_INFO_SIZ (64) +#define INT_DATA_TYPE_LEN_SIZ (4) + +static u8 dump_flag = 1; + +void dump_spi(const char *prefix, u8 *data, size_t datalen) +{ + u8 str[1024]; + int offset = 0; + int i; + + if (!dump_flag) + return; + + offset += snprintf(str + offset, sizeof(str) - offset, "%s", prefix); + for (i = 0; i < datalen; i++) { + offset += + snprintf(str + offset, sizeof(str) - offset, " %02x", + data[i]); + } + cts_err("%s", str); +} + +static int cts_tcs_spi_xtrans(const struct cts_device *cts_dev, u8 *tx, + size_t txlen, u8 *rx, size_t rxlen) +{ + int ret; + struct spi_message msg; + struct spi_transfer xfer[2]; + struct chipone_ts_data *cts_data = container_of(cts_dev, + struct chipone_ts_data, + cts_dev); + u16 crc16_recv, crc16_calc; + + memset(&xfer[0], 0, sizeof(struct spi_transfer)); + xfer[0].cs_change = 0; + xfer[0].delay_usecs = 0; + xfer[0].speed_hz = cts_dev->pdata->spi_speed * 1000u; + xfer[0].tx_buf = tx; + xfer[0].rx_buf = NULL; + xfer[0].len = txlen; + xfer[0].bits_per_word = 8; + + spi_message_init(&msg); + spi_message_add_tail(&xfer[0], &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 0); +#endif + ret = spi_sync(cts_data->spi_client, &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 1); +#endif + if (ret < 0) { + cts_err("spi_sync xfer[0] failed: %d", ret); + return ret; + } + udelay(100); + + memset(&xfer[1], 0, sizeof(struct spi_transfer)); + xfer[1].cs_change = 0; + xfer[1].delay_usecs = 0; + xfer[1].speed_hz = cts_dev->pdata->spi_speed * 1000u; + xfer[1].tx_buf = NULL; + xfer[1].rx_buf = rx; + xfer[1].len = rxlen; + xfer[1].bits_per_word = 8; + + spi_message_init(&msg); + spi_message_add_tail(&xfer[1], &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 0); +#endif + ret = spi_sync(cts_data->spi_client, &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 1); +#endif + if (ret < 0) { + cts_err("spi_sync xfer[1] failed: %d", ret); + return ret; + } + + crc16_recv = (rx[rxlen - 1] << 8) | rx[rxlen - 2]; + crc16_calc = cts_crc16(rx, rxlen - 2); + if (crc16_recv != crc16_calc) { + cts_err("xtrans crc error: recv %04x != %04x calc", crc16_recv, + crc16_calc); + return -EIO; + } + udelay(100); + + return 0; +} + +static int cts_tcs_spi_xtrans_1_cs(const struct cts_device *cts_dev, u8 *tx, + size_t txlen, u8 *rx, size_t rxlen) +{ + int ret; + struct spi_message msg; + struct spi_transfer xfer[1]; + struct chipone_ts_data *cts_data = container_of(cts_dev, + struct chipone_ts_data, + cts_dev); + + memset(&xfer[0], 0, sizeof(struct spi_transfer)); + xfer[0].cs_change = 0; + xfer[0].delay_usecs = 0; + xfer[0].speed_hz = cts_dev->pdata->spi_speed * 1000u; + xfer[0].tx_buf = tx; + xfer[0].rx_buf = rx; + xfer[0].len = txlen > rxlen ? txlen : rxlen; + xfer[0].bits_per_word = 8; + + spi_message_init(&msg); + spi_message_add_tail(&xfer[0], &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 0); +#endif + ret = spi_sync(cts_data->spi_client, &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 1); +#endif + if (ret < 0) { + cts_err("spi_sync xfer[0] failed: %d", ret); + return ret; + } + + return 0; +} + +static int cts_tcs_spi_read_pack(u8 *tx, TcsCmdValue_t *tcv, u16 rdatalen) +{ + tcs_tx_head *txhdr = (tcs_tx_head *) tx; + int packlen = 0; + u16 crc16; + + txhdr->addr = TCS_RD_ADDR; + txhdr->cmd = + (tcv->baseFlag << 15) | + (tcv->isRead << 14) | (tcv->classID << 8) | (tcv->cmdID << 0); + txhdr->datlen = rdatalen; + crc16 = cts_crc16((const u8 *)txhdr, offsetof(tcs_tx_head, crc16)); + txhdr->crc16 = crc16; + packlen += sizeof(tcs_tx_head); + + return packlen; +} + +static int cts_tcs_spi_write_pack(u8 *tx, TcsCmdValue_t *tcv, + u8 *wdata, u16 wdatalen) +{ + tcs_tx_head *txhdr = (tcs_tx_head *) tx; + int packlen = 0; + u16 crc16; + + txhdr->addr = TCS_WR_ADDR; + txhdr->cmd = + (tcv->baseFlag << 15) | + (tcv->isWrite << 13) | (tcv->classID << 8) | (tcv->cmdID << 0); + txhdr->datlen = wdatalen; + crc16 = cts_crc16((const u8 *)txhdr, offsetof(tcs_tx_head, crc16)); + txhdr->crc16 = crc16; + packlen += sizeof(tcs_tx_head); + + if (wdatalen > 0) { + memcpy(tx + sizeof(tcs_tx_head), wdata, wdatalen); + crc16 = cts_crc16(wdata, wdatalen); + *(tx + sizeof(tcs_tx_head) + wdatalen) = ((crc16 >> 0) & 0xFF); + *(tx + sizeof(tcs_tx_head) + wdatalen + 1) = + ((crc16 >> 8) & 0xFF); + packlen += wdatalen + sizeof(crc16); + } + + return packlen; +} + +int cts_tcs_spi_read(const struct cts_device *cts_dev, + enum TcsCmdIndex cmdIdx, u8 *rdata, size_t rdatalen) +{ + // static u8 tx[INT_DATA_MAX_SIZ]; + // static u8 rx[INT_DATA_MAX_SIZ]; + int txlen; + + txlen = + cts_tcs_spi_read_pack(cts_dev->tx, TcsCmdValue + cmdIdx, rdatalen); + /* dump_spi(">> ", tx, txlen); */ + cts_tcs_spi_xtrans(cts_dev, cts_dev->tx, txlen, cts_dev->rx, + rdatalen + sizeof(tcs_rx_tail)); + /* dump_spi("<< ", rx, rdatalen + sizeof(tcs_rx_tail)); */ + /** + * if (rx[rdatalen] != 0) { + * cts_err("spi_sync failed: err=%d", rx[rdatalen]); + * } + */ + memcpy(rdata, cts_dev->rx, rdatalen); + + return 0; +} + +int cts_tcs_spi_read_1_cs(const struct cts_device *cts_dev, + enum TcsCmdIndex cmdIdx, u8 *rdata, size_t rdatalen) +{ + // static u8 tx[INT_DATA_MAX_SIZ]; + // static u8 rx[INT_DATA_MAX_SIZ]; + int txlen; + u16 crc16_recv, crc16_calc; + + txlen = + cts_tcs_spi_read_pack(cts_dev->tx, TcsCmdValue + cmdIdx, rdatalen); + /* dump_spi(">> ", tx, txlen); */ + cts_tcs_spi_xtrans_1_cs(cts_dev, cts_dev->tx, txlen, cts_dev->rx, + rdatalen); + /* dump_spi("<< ", rx, rdatalen + sizeof(tcs_rx_tail)); */ + /** + * if (rx[rdatalen] != 0) { + * cts_err("spi_sync failed: err=%d", rx[rdatalen]); + * } + */ + crc16_recv = + (cts_dev->rx[rdatalen - 1] << 8) | cts_dev->rx[rdatalen - 2]; + crc16_calc = cts_crc16(cts_dev->rx, rdatalen - 2); + if (crc16_recv != crc16_calc) { + cts_err("Get int data crc error: recv %04x != %04x calc", + crc16_recv, crc16_calc); + return -EIO; + } + + memcpy(rdata, cts_dev->rx, rdatalen); + + return 0; +} + +int cts_tcs_spi_write(const struct cts_device *cts_dev, + enum TcsCmdIndex cmdIdx, u8 *wdata, size_t wdatalen) +{ + // static u8 tx[2048]; + // static u8 rx[2048]; + int txlen; + + txlen = + cts_tcs_spi_write_pack(cts_dev->tx, TcsCmdValue + cmdIdx, wdata, + wdatalen); + /* dump_spi(">> ", tx, txlen); */ + cts_tcs_spi_xtrans(cts_dev, cts_dev->tx, txlen, cts_dev->rx, + sizeof(tcs_rx_tail)); + /* dump_spi("<< ", rx, wdatalen + sizeof(tcs_rx_tail)); */ + return cts_dev->rx[0]; +} + +int cts_tcs_get_fw_ver(const struct cts_device *cts_dev, u16 *fwver) +{ + int ret = -1; + u8 buf[4]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_INFO_FW_VER_RO, + buf, sizeof(buf)); + if (!ret) { + *fwver = buf[0] | (buf[1] << 8); + return 0; + } + return ret; +} + +int cts_tcs_get_lib_ver(const struct cts_device *cts_dev, u16 *libver) +{ + int ret = -1; + u8 buf[4]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_INFO_FW_VER_RO, buf, + sizeof(buf)); + if (!ret) { + *libver = buf[2] | (buf[3] << 8); + return 0; + } + return ret; +} + +int cts_tcs_get_fw_id(const struct cts_device *cts_dev, u16 *fwid) +{ + int ret = -1; + u8 buf[4]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_INFO_CHIP_FW_ID_RO, + buf, sizeof(buf)); + if (!ret) { + *fwid = buf[0] | (buf[1] << 8); + return 0; + } + + return ret; +} + +int cts_tcs_get_ddi_ver(const struct cts_device *cts_dev, u8 *ddiver) +{ + int ret = -1; + u8 buf[1]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_SYS_STS_DDI_CODE_VER_RO, + buf, sizeof(buf)); + if (!ret) { + *ddiver = buf[0]; + return 0; + } + return ret; +} + +int cts_tcs_get_res_x(const struct cts_device *cts_dev, u16 *res_x) +{ + int ret = -1; + u8 buf[10]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_INFO_TOUCH_XY_INFO_RO, + buf, sizeof(buf)); + if (!ret) { + *res_x = buf[0] | (buf[1] << 8); + return 0; + } + return ret; +} + +int cts_tcs_get_res_y(const struct cts_device *cts_dev, u16 *res_y) +{ + int ret = -1; + u8 buf[10]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_INFO_TOUCH_XY_INFO_RO, + buf, sizeof(buf)); + if (!ret) { + *res_y = buf[2] | (buf[3] << 8); + return 0; + } + return ret; +} + +int cts_tcs_get_rows(const struct cts_device *cts_dev, u8 *rows) +{ + int ret = -1; + u8 buf[10]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_INFO_TOUCH_XY_INFO_RO, + buf, sizeof(buf)); + if (!ret) { + *rows = buf[5]; + return 0; + } + return ret; +} + +int cts_tcs_get_cols(const struct cts_device *cts_dev, u8 *cols) +{ + int ret = -1; + u8 buf[10]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_INFO_TOUCH_XY_INFO_RO, + buf, sizeof(buf)); + if (!ret) { + *cols = buf[4]; + return 0; + } + return ret; +} + +int cts_tcs_get_flip_x(const struct cts_device *cts_dev, bool *flip_x) +{ + int ret = -1; + u8 buf[1]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_COORD_FLIP_X_EN_RW, + buf, sizeof(buf)); + if (!ret) { + *flip_x = !!buf[0]; + return 0; + } + return ret; +} + +int cts_tcs_get_flip_y(const struct cts_device *cts_dev, bool *flip_y) +{ + int ret = -1; + u8 buf[1]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_COORD_FLIP_Y_EN_RW, + buf, sizeof(buf)); + if (!ret) { + *flip_y = !!buf[0]; + return 0; + } + return ret; +} + +int cts_tcs_get_swap_axes(const struct cts_device *cts_dev, bool *swap_axes) +{ + int ret = -1; + u8 buf[1]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_COORD_SWAP_AXES_EN_RW, + buf, sizeof(buf)); + if (!ret) { + *swap_axes = !!buf[0]; + return 0; + } + return ret; +} + +int cts_tcs_get_int_mode(const struct cts_device *cts_dev, u8 *int_mode) +{ + int ret = -1; + u8 buf[1]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_SYS_STS_INT_MODE_RW, + buf, sizeof(buf)); + if (!ret) { + *int_mode = buf[0]; + return 0; + } + return ret; +} + +int cts_tcs_get_int_keep_time(const struct cts_device *cts_dev, + u16 *int_keep_time) +{ + int ret = -1; + u8 buf[2]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_SYS_STS_INT_KEEP_TIME_RW, + buf, sizeof(buf)); + if (!ret) { + *int_keep_time = (buf[0] | (buf[1] << 8)); + return 0; + } + return ret; + +} + +int cts_tcs_get_esd_method(const struct cts_device *cts_dev, u8 *esd_method) +{ + int ret = -1; + u8 buf[1]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_DDI_ESD_OPTIONS_RW, + buf, sizeof(buf)); + if (!ret) { + *esd_method = buf[0]; + return 0; + } + return ret; +} + +int cts_tcs_get_esd_protection(const struct cts_device *cts_dev, + u8 *esd_protection) +{ + int ret = -1; + u8 buf[4]; + + buf[0] = 0x01; + buf[1] = 0x56; + buf[2] = 0x81; + buf[3] = 0x00; + + ret = cts_tcs_spi_write(cts_dev, + TP_STD_CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, + buf, sizeof(buf)); + if (ret != 0) + return ret; + + ret = cts_tcs_spi_read(cts_dev, + TP_STD_CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, + esd_protection, sizeof(u8)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_get_data_ready_flag(const struct cts_device *cts_dev, u8 *ready) +{ + int ret = -1; + u8 buf[1]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_SYS_STS_DAT_RDY_FLAG_RW, + buf, sizeof(buf)); + if (!ret) { + *ready = buf[0]; + return 0; + } + return ret; +} + +int cts_tcs_clr_gstr_ready_flag(const struct cts_device *cts_dev) +{ + int ret; + u8 ready = 0; + + ret = cts_tcs_spi_write(cts_dev, TP_STD_CMD_GSTR_DAT_RDY_FLAG_GSTR_RW, + &ready, sizeof(ready)); + return ret; +} + +int cts_tcs_clr_data_ready_flag(const struct cts_device *cts_dev) +{ + int ret; + u8 ready = 0; + + ret = cts_tcs_spi_write(cts_dev, TP_STD_CMD_SYS_STS_DAT_RDY_FLAG_RW, + &ready, sizeof(ready)); + return ret; +} + +int cts_tcs_enable_get_rawdata(const struct cts_device *cts_dev) +{ + int ret; + u8 buf[1]; + + buf[0] = 0x01; + + ret = + cts_tcs_spi_write(cts_dev, + TP_STD_CMD_SYS_STS_DAT_TRANS_IN_NORMAL_RW, buf, + sizeof(buf)); + if (ret != 0) + return ret; + + return ret; +} + +int cts_tcs_disable_get_rawdata(const struct cts_device *cts_dev) +{ + int ret; + u8 buf[1]; + + buf[0] = 0x00; + + ret = + cts_tcs_spi_write(cts_dev, + TP_STD_CMD_SYS_STS_DAT_TRANS_IN_NORMAL_RW, buf, + sizeof(buf)); + if (ret != 0) + return ret; + + return ret; +} + +int cts_tcs_enable_get_cneg(const struct cts_device *cts_dev) +{ + int ret; + u8 buf[1]; + + buf[0] = 0x01; + + ret = cts_tcs_spi_write(cts_dev, TP_STD_CMD_SYS_STS_CNEG_RD_EN_RW, + buf, sizeof(buf)); + if (ret != 0) + return ret; + + return ret; +} + +int cts_tcs_disable_get_cneg(const struct cts_device *cts_dev) +{ + int ret; + u8 buf[1]; + + buf[0] = 0x00; + + ret = cts_tcs_spi_write(cts_dev, TP_STD_CMD_SYS_STS_CNEG_RD_EN_RW, + buf, sizeof(buf)); + if (ret != 0) + return ret; + + return ret; +} + +int cts_tcs_is_cneg_ready(const struct cts_device *cts_dev, u8 *ready) +{ + int ret; + u8 buf[1]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_SYS_STS_CNEG_RDY_FLAG_RW, + buf, sizeof(buf)); + if (!ret) { + *ready = buf[0]; + return 0; + } + + return ret; +} + +int cts_tcs_quit_guesture_mode(const struct cts_device *cts_dev) +{ + int ret; + u8 buf[1]; + + buf[0] = 0x00; + + ret = cts_tcs_spi_write(cts_dev, TP_STD_CMD_MNT_FORCE_EXIT_MNT_WO, + buf, sizeof(buf)); + if (ret != 0) + return ret; + + return ret; +} + +int cts_tcs_get_rawdata(const struct cts_device *cts_dev, u8 *buf) +{ + int ret; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_TP_DATA_RAW_RO, buf, + RAWDATA_BUFFER_SIZE(cts_dev)); + + return ret; +} + +int cts_tcs_get_diffdata(const struct cts_device *cts_dev, u8 *buf) +{ + int ret; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_TP_DATA_DIFF_RO, buf, + DIFFDATA_BUFFER_SIZE(cts_dev)); + + return ret; +} + +int cts_tcs_get_basedata(const struct cts_device *cts_dev, u8 *buf) +{ + int ret; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_TP_DATA_BASE_RO, buf, + DIFFDATA_BUFFER_SIZE(cts_dev)); + + return ret; +} + +int cts_tcs_get_cneg(const struct cts_device *cts_dev, u8 *buf, size_t size) +{ + int ret; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_TP_DATA_CNEG_RO, buf, size); + + return ret; + +} + +int cts_tcs_read_hw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + int ret; + u8 buf[4]; + + buf[0] = 1; + buf[1] = ((addr >> 0) & 0xFF); + buf[2] = ((addr >> 8) & 0xFF); + buf[3] = ((addr >> 16) & 0xFF); + + ret = + cts_tcs_spi_write(cts_dev, + TP_STD_CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, buf, + sizeof(buf)); + if (ret != 0) + return ret; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_TP_DATA_READ_START_RO, + regbuf, size); + if (ret != 0) + return ret; + + return 0; +} + +int cts_tcs_write_hw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + int ret; + u8 *buf; + + buf = kmalloc(size + 6, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + buf[0] = ((size >> 0) & 0xFF); + buf[1] = ((size >> 8) & 0xFF); + buf[2] = ((addr >> 0) & 0xFF); + buf[3] = ((addr >> 8) & 0xFF); + buf[4] = ((addr >> 16) & 0xFF); + buf[5] = 0x00; + memcpy(buf + 6, regbuf, size); + + ret = cts_tcs_spi_write(cts_dev, + TP_STD_CMD_TP_DATA_WR_REG_RAM_SEQUENCE_WO, + buf, size + 6); + if (ret != 0) { + kfree(buf); + return ret; + } + + kfree(buf); + + return ret; + +} + +int cts_tcs_read_ddi_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + int ret; + u8 buf[2]; + + buf[0] = 2; + buf[1] = addr; + + ret = + cts_tcs_spi_write(cts_dev, + TP_STD_CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, buf, + sizeof(buf)); + if (ret != 0) + return ret; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_TP_DATA_READ_START_RO, + regbuf, size); + if (ret != 0) + return ret; + + return 0; +} + +int cts_tcs_write_ddi_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + int ret; + u8 *buf; + + buf = kmalloc(size + 6, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + buf[0] = ((size >> 0) & 0xFF); + buf[1] = ((size >> 8) & 0xFF); + buf[2] = addr; + buf[3] = 0; + buf[4] = 0; + buf[5] = 0; + memcpy(buf + 6, regbuf, size); + + ret = cts_tcs_spi_write(cts_dev, + TP_STD_CMD_TP_DATA_WR_DDI_REG_SEQUENCE_WO, + buf, size + 6); + if (ret != 0) { + kfree(buf); + return ret; + } + + kfree(buf); + + return ret; + +} + +static int cts_tcs_reg_read_pack(u8 *tx, uint16_t cmd, u16 rdatalen) +{ + tcs_tx_head *txhdr = (tcs_tx_head *) tx; + int packlen = 0; + u16 crc16; + + txhdr->addr = TCS_RD_ADDR; + txhdr->cmd = cmd; + txhdr->datlen = rdatalen; + crc16 = cts_crc16((const u8 *)txhdr, offsetof(tcs_tx_head, crc16)); + txhdr->crc16 = crc16; + packlen += sizeof(tcs_tx_head); + + return packlen; +} + +int cts_tcs_read_reg(const struct cts_device *cts_dev, uint16_t cmd, + u8 *rbuf, size_t rlen) +{ + static u8 tx[2048]; + static u8 rx[2048]; + + int txlen = cts_tcs_reg_read_pack(tx, cmd, rlen); + + cts_tcs_spi_xtrans(cts_dev, tx, txlen, rx, + rlen + sizeof(tcs_rx_tail)); + + memcpy(rbuf, rx, rlen); + + return 0; +} + +static int cts_tcs_reg_write_pack(u8 *tx, uint16_t cmd, + u8 *wdata, u16 wdatalen) +{ + tcs_tx_head *txhdr = (tcs_tx_head *) tx; + int packlen = 0; + u16 crc16; + + txhdr->addr = TCS_WR_ADDR; + txhdr->cmd = cmd; + txhdr->datlen = wdatalen; + crc16 = cts_crc16((const u8 *)txhdr, offsetof(tcs_tx_head, crc16)); + txhdr->crc16 = crc16; + packlen += sizeof(tcs_tx_head); + + if (wdatalen > 0) { + memcpy(tx + sizeof(tcs_tx_head), wdata, wdatalen); + crc16 = cts_crc16(wdata, wdatalen); + *(tx + sizeof(tcs_tx_head) + wdatalen) = ((crc16 >> 0) & 0xFF); + *(tx + sizeof(tcs_tx_head) + wdatalen + 1) = + ((crc16 >> 8) & 0xFF); + packlen += wdatalen + sizeof(crc16); + } + + return packlen; +} + +int cts_tcs_write_reg(const struct cts_device *cts_dev, uint16_t cmd, + u8 *wbuf, size_t wlen) +{ + static u8 rx[2048]; + static u8 tx[2048]; + int txlen = cts_tcs_reg_write_pack(tx, cmd, wbuf, wlen); + + cts_tcs_spi_xtrans(cts_dev, tx, txlen + wlen, rx, + wlen + sizeof(tcs_rx_tail)); + return 0; +} + +int cts_tcs_read_fw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + int ret; + + ret = + cts_tcs_spi_read(cts_dev, TP_STD_CMD_SYS_STS_READ_RO, regbuf, size); + if (ret != 0) + return ret; + + return 0; +} + +int cts_tcs_write_fw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + int ret; + u8 buf[4]; + + buf[0] = 0x01; + buf[1] = ((addr >> 0) & 0xFF); + buf[2] = ((addr >> 8) & 0xFF); + buf[3] = ((addr >> 16) & 0xFF); + + ret = + cts_tcs_spi_write(cts_dev, + TP_STD_CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, buf, + sizeof(buf)); + if (ret != 0) + return ret; + + ret = + cts_tcs_spi_write(cts_dev, + TP_STD_CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, regbuf, + sizeof(buf)); + if (ret != 0) + return ret; + + return ret; +} + +int cts_tcs_calc_int_data_size(struct cts_device *cts_dev) +{ +#define INT_DATA_TYPE_U8_SIZ \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * sizeof(u8)) +#define INT_DATA_TYPE_U16_SIZ \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * sizeof(u16)) + + int data_size = TOUCH_INFO_SIZ + TCS_REPLY_TAIL_SIZ; + u16 data_types = cts_dev->fwdata.int_data_types; + u8 data_method = cts_dev->fwdata.int_data_method; + + if (data_method == INT_DATA_METHOD_NONE) { + cts_dev->fwdata.int_data_size = data_size; + return 0; + } else if (data_method == INT_DATA_METHOD_DEBUG) { + data_size += INT_DATA_INFO_SIZ; + data_size += (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U16_SIZ); + cts_dev->fwdata.int_data_size = data_size; + return 0; + } + + cts_info("data_method:%d, data_type:%d", data_method, data_types); + if (data_types != INT_DATA_TYPE_NONE) { + data_size += INT_DATA_INFO_SIZ; + if ((data_types & INT_DATA_TYPE_RAWDATA)) { + data_size += + (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U16_SIZ); + } + if ((data_types & INT_DATA_TYPE_MANUAL_DIFF)) { + data_size += + (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U16_SIZ); + } + if ((data_types & INT_DATA_TYPE_REAL_DIFF)) { + data_size += + (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U16_SIZ); + } + if ((data_types & INT_DATA_TYPE_NOISE_DIFF)) { + data_size += + (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U16_SIZ); + } + if ((data_types & INT_DATA_TYPE_BASEDATA)) { + data_size += + (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U16_SIZ); + } + if ((data_types & INT_DATA_TYPE_CNEGDATA)) { + data_size += + (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U8_SIZ); + } + } + + cts_info("data_size: %d", data_size); + cts_dev->fwdata.int_data_size = data_size; + return 0; +} + +static int cts_tcs_polling_data(const struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + int ret = -1; + int retries = 100; + u8 ready = 0; + size_t data_size = cts_dev->fwdata.int_data_size; + + if (!data_size) + data_size = TOUCH_INFO_SIZ + TCS_REPLY_TAIL_SIZ; + + do { + ret = cts_tcs_get_data_ready_flag(cts_dev, &ready); + if (!ret && ready) + break; + mdelay(10); + } while (!ready && --retries); + cts_info("get data rdy, retries left %d", retries); + + if (!ready) { + cts_err("time out wait for data rdy"); + return -EIO; + } + + retries = 3; + do { + ret = + cts_tcs_spi_read_1_cs(cts_dev, + TP_STD_CMD_GET_DATA_BY_POLLING_RO, + cts_dev->rtdata.int_data, data_size); + mdelay(1); + if (cts_tcs_clr_data_ready_flag(cts_dev)) + cts_err("Clear data ready flag failed"); + } while (ret && --retries); + + return ret; +} + +static int cts_tcs_polling_test_data(const struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + int ret = -EIO, retries = 5; + int offset = TOUCH_INFO_SIZ + INT_DATA_INFO_SIZ + INT_DATA_TYPE_LEN_SIZ; + + while (retries--) { + ret = cts_polling_data(cts_dev, buf, size); + if (!ret) { + memcpy(buf, cts_dev->rtdata.int_data + offset, size); + break; + } + } + + return ret; +} + +static int polling_data(struct cts_device *cts_dev, u8 *buf, size_t size, + enum int_data_type type) +{ + u8 old_int_data_method; + u16 old_int_data_types; + int ret = -EIO, retries = 5; + int offset = TOUCH_INFO_SIZ + INT_DATA_INFO_SIZ + INT_DATA_TYPE_LEN_SIZ; + + old_int_data_types = cts_dev->fwdata.int_data_types; + old_int_data_method = cts_dev->fwdata.int_data_method; + + cts_set_int_data_types(cts_dev, type); + cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); + + while (retries--) { + ret = cts_polling_data(cts_dev, buf, size); + if (!ret) { + memcpy(buf, cts_dev->rtdata.int_data + offset, size); + break; + } + } + + cts_set_int_data_types(cts_dev, old_int_data_types); + cts_set_int_data_method(cts_dev, old_int_data_method); + + return ret; +} + +int cts_tcs_top_get_rawdata(struct cts_device *cts_dev, u8 *buf, size_t size) +{ + return polling_data(cts_dev, buf, size, INT_DATA_TYPE_RAWDATA); +} + +int cts_tcs_top_get_manual_diff(struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + return polling_data(cts_dev, buf, size, INT_DATA_TYPE_MANUAL_DIFF); +} + +int cts_tcs_top_get_real_diff(struct cts_device *cts_dev, u8 *buf, size_t size) +{ + return polling_data(cts_dev, buf, size, INT_DATA_TYPE_REAL_DIFF); +} + +int cts_tcs_top_get_noise_diff(struct cts_device *cts_dev, u8 *buf, + size_t size) +{ + return polling_data(cts_dev, buf, size, INT_DATA_TYPE_NOISE_DIFF); +} + +int cts_tcs_top_get_basedata(struct cts_device *cts_dev, u8 *buf, size_t size) +{ + return polling_data(cts_dev, buf, size, INT_DATA_TYPE_BASEDATA); +} + +int cts_tcs_top_get_cnegdata(struct cts_device *cts_dev, u8 *buf, size_t size) +{ + return polling_data(cts_dev, buf, size, INT_DATA_TYPE_CNEGDATA); +} + +static int cts_tcs_tp_reset(const struct cts_device *cts_dev) +{ + int ret; + u8 buf[] = { 0x01, 0xfe }; + + ret = cts_tcs_spi_write(cts_dev, TP_STD_CMD_SYS_STS_RESET_WO, + buf, sizeof(buf)); + if (ret != 0) + return ret; + + return ret; +} + +int cts_tcs_reset_device(const struct cts_device *cts_dev) +{ + bool use_soft_tp_reset; + int ret; + + if (!cts_dev->hwdata) { + use_soft_tp_reset = true; + } else if (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9916) { + use_soft_tp_reset = false; + } else if (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9922) { + use_soft_tp_reset = true; + } else { + use_soft_tp_reset = true; + } + + cts_info("use_soft_tp_reset=%d", use_soft_tp_reset); + if (use_soft_tp_reset) { + /* normal */ + cts_info("tp reset in normal mode"); + ret = cts_tcs_tp_reset(cts_dev); + if (!ret) { + mdelay(40); + return 0; + } + /* program */ + cts_info("tp reset in program mode"); + ret = + cts_hw_reg_writeb(cts_dev, CTS_DEV_HW_REG_RESET_CONFIG, + 0xfd); + if (!ret) { + mdelay(40); + return 0; + } + return ret; + } else + return cts_plat_reset_device(cts_dev->pdata); +} + +int cts_tcs_set_int_test(const struct cts_device *cts_dev, u8 enable) +{ + int ret; + + ret = cts_tcs_spi_write(cts_dev, TP_STD_CMD_SYS_STS_INT_TEST_EN_RW, &enable, sizeof(enable)); + if (!ret) + return 0; + + return -1; +} + +int cts_tcs_set_int_pin(const struct cts_device *cts_dev, u8 high) +{ + int ret; + + ret = cts_tcs_spi_write(cts_dev, TP_STD_CMD_SYS_STS_SET_INT_PIN_RW, &high, sizeof(high)); + if (!ret) + return 0; + + return -1; +} + +int cts_tcs_get_module_id(const struct cts_device *cts_dev, u32 *modId) +{ + int ret; + u8 buf[4]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_INFO_MODULE_ID_RO, + buf, sizeof(buf)); + if (!ret) { + *modId = *(u32 *)buf; + return 0; + } + return -1; +} + +int cts_tcs_get_gestureinfo(const struct cts_device *cts_dev, + struct cts_device_gesture_info *gesture_info) +{ + int ret = 0; + size_t size = sizeof(*gesture_info) + TCS_REPLY_TAIL_SIZ; + + ret = cts_tcs_spi_read_1_cs(cts_dev, TP_STD_CMD_TP_DATA_COORDINATES_RO, + cts_dev->rtdata.int_data, size); + if (cts_tcs_clr_gstr_ready_flag(cts_dev)) { + cts_err("Clear gesture ready flag failed"); + } + if (ret < 0) { + cts_err("Get gesture info failed: ret=%d", ret); + return ret; + } + if (!ret) { + memcpy(gesture_info, cts_dev->rtdata.int_data, sizeof(*gesture_info)); + } + + return ret; +} + +#ifdef CFG_DUMP_INT_DATA +extern void cts_dump_int_tsdata(const u8 *data, size_t size); +#endif +int cts_tcs_get_touchinfo(struct cts_device *cts_dev, + struct cts_device_touch_info *touch_info) +{ + int ret = -1; + size_t size = cts_dev->fwdata.int_data_size; + u8 method = cts_dev->fwdata.int_data_method; + + if (!size) + size = TOUCH_INFO_SIZ; + + memset(touch_info, 0, sizeof(*touch_info)); + + ret = cts_tcs_spi_read_1_cs(cts_dev, TP_STD_CMD_TP_DATA_COORDINATES_RO, + cts_dev->rtdata.int_data, size); + if (ret) { + cts_err("tcs_spi_read_1_cs failed"); + return ret; + } + + memcpy(touch_info, cts_dev->rtdata.int_data, sizeof(*touch_info)); + + if (method == INT_DATA_METHOD_HOST) { +#ifdef CFG_DUMP_INT_DATA + if (cts_dev->rtdata.dumping) { + if ((--cts_dev->rtdata.dump_cnt) == 0) + cts_dev->rtdata.dumping = false; + cts_dump_int_tsdata(cts_dev->rtdata.int_data, size - 5); + } +#endif + udelay(200); + } + + return ret; +} + +int cts_tcs_get_workmode(const struct cts_device *cts_dev, u8 *workmode) +{ + int ret = -1; + u8 buf[1]; + + ret = + cts_tcs_spi_read(cts_dev, TP_STD_CMD_SYS_STS_CURRENT_WORKMODE_RO, + buf, sizeof(buf)); + if (!ret) { + *workmode = buf[0]; + return 0; + } + + return ret; +} + +int cts_tcs_set_workmode(const struct cts_device *cts_dev, u8 workmode) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_SYS_STS_WORK_MODE_RW, + &workmode, sizeof(workmode)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_set_openshort_mode(const struct cts_device *cts_dev, u8 mode) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_OPENSHORT_MODE_SEL_RW, &mode, + sizeof(mode)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_set_tx_vol(const struct cts_device *cts_dev, u8 txvol) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_SYS_STS_VSTIM_LVL_RW, &txvol, + sizeof(txvol)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_is_enabled_get_rawdata(const struct cts_device *cts_dev, + u8 *enabled) +{ + int ret; + u8 buf[1]; + + ret = + cts_tcs_spi_read(cts_dev, TP_STD_CMD_SYS_STS_DAT_TRANS_IN_NORMAL_RW, + buf, sizeof(buf)); + if (!ret) { + *enabled = buf[0]; + return 0; + } + + return ret; +} + +int cts_tcs_set_short_test_type(const struct cts_device *cts_dev, u8 short_type) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_OPENSHORT_SHORT_SEL_RW, + &short_type, sizeof(short_type)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_is_openshort_enabled(const struct cts_device *cts_dev, u8 *enabled) +{ + int ret = -1; + u8 buf[1]; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_OPENSHORT_EN_RW, buf, + sizeof(buf)); + if (!ret) { + *enabled = buf[0]; + return 0; + } + + return ret; +} + +int cts_tcs_set_openshort_enable(const struct cts_device *cts_dev, u8 enable) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_OPENSHORT_EN_RW, &enable, + sizeof(enable)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_set_esd_enable(const struct cts_device *cts_dev, u8 enable) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_DDI_ESD_EN_RW, &enable, + sizeof(enable)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_is_cneg_enabled(const struct cts_device *cts_dev, u8 *enabled) +{ + int ret = -1; + + ret = + cts_tcs_spi_read(cts_dev, TP_STD_CMD_CNEG_EN_RW, enabled, + sizeof(*enabled)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_is_mnt_enabled(const struct cts_device *cts_dev, u8 *enabled) +{ + int ret = -1; + + ret = + cts_tcs_spi_read(cts_dev, TP_STD_CMD_MNT_EN_RW, enabled, + sizeof(*enabled)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_set_cneg_enable(const struct cts_device *cts_dev, u8 enable) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_CNEG_EN_RW, &enable, + sizeof(enable)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_set_mnt_enable(const struct cts_device *cts_dev, u8 enable) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_MNT_EN_RW, &enable, + sizeof(enable)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_is_display_on(const struct cts_device *cts_dev, u8 *display_on) +{ + int ret; + u8 buf[1]; + + ret = + cts_tcs_spi_read(cts_dev, TP_STD_CMD_OPENSHORT_SHORT_DISP_ON_EN_RW, + buf, sizeof(buf)); + if (!ret) { + *display_on = buf[0]; + return 0; + } + + return ret; +} + +int cts_tcs_set_pwr_mode(const struct cts_device *cts_dev, u8 pwr_mode) +{ + int ret; + u8 buf[0]; + + buf[0] = pwr_mode; + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_SYS_STS_PWR_STATE_RW, buf, 1); + return ret; +} + +int cts_tcs_set_display_on(const struct cts_device *cts_dev, u8 display_on) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, TP_STD_CMD_OPENSHORT_SHORT_DISP_ON_EN_RW, + &display_on, sizeof(display_on)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_read_sram_normal_mode(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay) +{ + struct spi_message msg; + struct spi_transfer xfer[2]; + struct chipone_ts_data *cts_data = container_of(cts_dev, + struct chipone_ts_data, + cts_dev); + u16 crc; + int ret; + u8 tx[ALIGN(128, 4)]; + u8 rx[ALIGN(128, 4)]; + u8 tx1[ALIGN(128, 4)]; + + /* Write add */ + tx[0] = CTS_DEV_NORMAL_MODE_SPIADDR; + tx[1] = 0x01; + tx[2] = 0x21; + tx[3] = 0x04; + tx[4] = 0x00; + crc = cts_crc16(tx, 5); + tx[5] = ((crc >> 0) & 0xFF); + tx[6] = ((crc >> 8) & 0xFF); + tx[7] = 0x01; + tx[8] = ((addr >> 0) & 0xFF); + tx[9] = ((addr >> 8) & 0xFF); + tx[10] = ((addr >> 16) & 0xFF); + crc = cts_crc16(&tx[7], 4); + tx[11] = ((crc >> 0) & 0xFF); + tx[12] = ((crc >> 8) & 0xFF); + + memset(&xfer[0], 0, sizeof(struct spi_transfer)); + xfer[0].cs_change = 0, + xfer[0].delay_usecs = 0, + xfer[0].speed_hz = cts_data->pdata->spi_speed * 1000u, + xfer[0].tx_buf = tx, xfer[0].rx_buf = rx, xfer[0].len = 13, + /** + * xfer[0].tx_dma = 0, + * xfer[0].rx_dma = 0, + */ + xfer[0].bits_per_word = 8, spi_message_init(&msg); + spi_message_add_tail(&xfer[0], &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 0); +#endif + ret = spi_sync(cts_data->spi_client, &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 1); +#endif + if (ret) + cts_err("spi sync 1 failed %d", ret); + udelay(100); + + memset(rx, 0, sizeof(rx)); + memset(&xfer[1], 0, sizeof(struct spi_transfer)); + xfer[1].cs_change = 0, + xfer[1].delay_usecs = 0, + xfer[1].speed_hz = cts_data->pdata->spi_speed * 1000u, + xfer[1].tx_buf = tx1, xfer[1].rx_buf = rx, xfer[1].len = 5, + /** + * xfer[0].tx_dma = 0, + * xfer[0].rx_dma = 0, + */ + xfer[1].bits_per_word = 8, spi_message_init(&msg); + spi_message_add_tail(&xfer[1], &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 0); +#endif + ret = spi_sync(cts_data->spi_client, &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 1); +#endif + if (ret) + cts_err("spi sync 2 failed %d", ret); + udelay(100); + + memset(tx, 0, sizeof(tx)); + memset(tx1, 0, sizeof(tx1)); + memset(rx, 0, sizeof(rx)); + /* Read data */ + tx[0] = CTS_DEV_NORMAL_MODE_SPIADDR | 0x01; + tx[1] = 0x02; + tx[2] = 0x41; + tx[3] = ((len >> 0) & 0xFF); + tx[4] = ((len >> 8) & 0xFF); + crc = cts_crc16(tx, 5); + tx[5] = ((crc >> 0) & 0xFF); + tx[6] = ((crc >> 8) & 0xFF); + + memset(&xfer[0], 0, sizeof(struct spi_transfer)); + xfer[0].cs_change = 0, + xfer[0].delay_usecs = 0, + xfer[0].speed_hz = cts_data->pdata->spi_speed * 1000u, + xfer[0].tx_buf = tx, xfer[0].rx_buf = rx, xfer[0].len = 7, + /** + * xfer[0].tx_dma = 0, + * xfer[0].rx_dma = 0, + */ + xfer[0].bits_per_word = 8, spi_message_init(&msg); + spi_message_add_tail(&xfer[0], &msg); + ret = spi_sync(cts_data->spi_client, &msg); + if (ret) + cts_err("spi sync 3 failed %d", ret); + udelay(100); + + memset(rx, 0, sizeof(rx)); + memset(&xfer[1], 0, sizeof(struct spi_transfer)); + xfer[1].cs_change = 0, + xfer[1].delay_usecs = 0, + xfer[1].speed_hz = cts_data->pdata->spi_speed * 1000u, + xfer[1].tx_buf = tx1, xfer[1].rx_buf = rx, xfer[1].len = len + 5, + /** + * xfer[0].tx_dma = 0, + * xfer[0].rx_dma = 0, + */ + xfer[1].bits_per_word = 8, spi_message_init(&msg); + spi_message_add_tail(&xfer[1], &msg); + ret = spi_sync(cts_data->spi_client, &msg); + if (ret) + cts_err("spi sync 4 failed %d", ret); + udelay(100); + + memcpy(dst, rx, len); + + return ret; +} + +int cts_tcs_init_int_data(struct cts_device *cts_dev) +{ + if (!cts_dev->rtdata.int_data) { + cts_dev->rtdata.int_data = + kmalloc(INT_DATA_MAX_SIZ, GFP_KERNEL); + if (!cts_dev->rtdata.int_data) { + cts_err("Malloc for int_data failed"); + return -ENOMEM; + } + return 0; + } + + return 0; +} + +int cts_tcs_deinit_int_data(struct cts_device *cts_dev) +{ + if (cts_dev->rtdata.int_data) { + kfree(cts_dev->rtdata.int_data); + cts_dev->rtdata.int_data = NULL; + } + + return 0; +} + +int cts_tcs_get_has_int_data(const struct cts_device *cts_dev, + bool *has_int_data) +{ + int ret = -1; + u8 buf[1]; + + ret = + cts_tcs_spi_read(cts_dev, + TP_STD_CMD_SYS_STS_DATA_CAPTURE_SUPPORT_RO, buf, + sizeof(buf)); + if (!ret) { + *has_int_data = !!buf[0]; + return 0; + } + return ret; +} + +int cts_tcs_get_int_data_types(const struct cts_device *cts_dev, + u16 *int_data_types) +{ + int ret = -1; + u8 buf[4]; + + ret = + cts_tcs_spi_read(cts_dev, + TP_STD_CMD_SYS_STS_DATA_CAPTURE_FUNC_MAP_RW, buf, + sizeof(buf)); + if (!ret) { + *int_data_types = buf[0] | (buf[1] << 8); + return 0; + } + return ret; +} + +int cts_tcs_set_int_data_types(const struct cts_device *cts_dev, + u16 int_data_types) +{ + int ret = -1; + + ret = + cts_tcs_spi_write(cts_dev, + TP_STD_CMD_SYS_STS_DATA_CAPTURE_FUNC_MAP_RW, + (u8 *) &int_data_types, sizeof(int_data_types)); + if (!ret) + return 0; + + return ret; +} + +int cts_tcs_get_int_data_method(const struct cts_device *cts_dev, + u8 *int_data_method) +{ + int ret = -1; + u8 buf[1]; + + ret = cts_tcs_spi_read(cts_dev, TP_STD_CMD_SYS_STS_DATA_CAPTURE_EN_RW, + buf, sizeof(buf)); + if (!ret) { + *int_data_method = buf[0]; + return 0; + } + return ret; +} + +int cts_tcs_set_int_data_method(const struct cts_device *cts_dev, + u8 int_data_method) +{ + int ret = -1; + + ret = cts_tcs_spi_write(cts_dev, TP_STD_CMD_SYS_STS_DATA_CAPTURE_EN_RW, + &int_data_method, sizeof(int_data_method)); + if (!ret) + return 0; + + return ret; +} + +struct cts_dev_ops tcs_ops = { + .get_fw_ver = cts_tcs_get_fw_ver, + .get_lib_ver = cts_tcs_get_lib_ver, + .get_ddi_ver = cts_tcs_get_ddi_ver, + .get_res_x = cts_tcs_get_res_x, + .get_res_y = cts_tcs_get_res_y, + .get_rows = cts_tcs_get_rows, + .get_cols = cts_tcs_get_cols, + .get_flip_x = cts_tcs_get_flip_x, + .get_flip_y = cts_tcs_get_flip_y, + .get_swap_axes = cts_tcs_get_swap_axes, + .get_int_mode = cts_tcs_get_int_mode, + .get_int_keep_time = cts_tcs_get_int_keep_time, + .get_esd_method = cts_tcs_get_esd_method, + .get_gestureinfo = cts_tcs_get_gestureinfo, + .get_touchinfo = cts_tcs_get_touchinfo, + .get_esd_protection = cts_tcs_get_esd_protection, + .get_data_ready_flag = cts_tcs_get_data_ready_flag, + .clr_data_ready_flag = cts_tcs_clr_data_ready_flag, + .enable_get_rawdata = cts_tcs_enable_get_rawdata, + .is_enabled_get_rawdata = cts_tcs_is_enabled_get_rawdata, + .disable_get_rawdata = cts_tcs_disable_get_rawdata, + .enable_get_cneg = cts_tcs_enable_get_cneg, + .disable_get_cneg = cts_tcs_disable_get_cneg, + .is_cneg_ready = cts_tcs_is_cneg_ready, + .quit_guesture_mode = cts_tcs_quit_guesture_mode, + .get_rawdata = cts_tcs_get_rawdata, + .get_diffdata = cts_tcs_get_diffdata, + .get_basedata = cts_tcs_get_basedata, + .get_cneg = cts_tcs_get_cneg, + .read_hw_reg = cts_tcs_read_hw_reg, + .write_hw_reg = cts_tcs_write_hw_reg, + .read_ddi_reg = cts_tcs_read_ddi_reg, + .write_ddi_reg = cts_tcs_write_ddi_reg, + .read_fw_reg = cts_tcs_read_fw_reg, + .write_fw_reg = cts_tcs_write_fw_reg, + .read_reg = cts_tcs_read_reg, + .write_reg = cts_tcs_write_reg, + .get_fw_id = cts_tcs_get_fw_id, + .get_workmode = cts_tcs_get_workmode, + .set_workmode = cts_tcs_set_workmode, + .set_openshort_mode = cts_tcs_set_openshort_mode, + .set_tx_vol = cts_tcs_set_tx_vol, + .set_short_test_type = cts_tcs_set_short_test_type, + .set_openshort_enable = cts_tcs_set_openshort_enable, + .is_openshort_enabled = cts_tcs_is_openshort_enabled, + .set_esd_enable = cts_tcs_set_esd_enable, + .set_cneg_enable = cts_tcs_set_cneg_enable, + .set_mnt_enable = cts_tcs_set_mnt_enable, + .is_display_on = cts_tcs_is_display_on, + .set_display_on = cts_tcs_set_display_on, + .is_cneg_enabled = cts_tcs_is_cneg_enabled, + .is_mnt_enabled = cts_tcs_is_mnt_enabled, + .set_pwr_mode = cts_tcs_set_pwr_mode, + .read_sram_normal_mode = cts_tcs_read_sram_normal_mode, + .init_int_data = cts_tcs_init_int_data, + .deinit_int_data = cts_tcs_deinit_int_data, + .get_has_int_data = cts_tcs_get_has_int_data, + .get_int_data_types = cts_tcs_get_int_data_types, + .set_int_data_types = cts_tcs_set_int_data_types, + .get_int_data_method = cts_tcs_get_int_data_method, + .set_int_data_method = cts_tcs_set_int_data_method, + .calc_int_data_size = cts_tcs_calc_int_data_size, + .polling_data = cts_tcs_polling_data, + .polling_test_data = cts_tcs_polling_test_data, + + .top_get_rawdata = cts_tcs_top_get_rawdata, + .top_get_manual_diff = cts_tcs_top_get_manual_diff, + .top_get_real_diff = cts_tcs_top_get_real_diff, + .top_get_noise_diff = cts_tcs_top_get_noise_diff, + .top_get_basedata = cts_tcs_top_get_basedata, + .top_get_cnegdata = cts_tcs_top_get_cnegdata, + + .reset_device = cts_tcs_reset_device, + + .set_int_test = cts_tcs_set_int_test, + .set_int_pin = cts_tcs_set_int_pin, + .get_module_id = cts_tcs_get_module_id, +}; diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tcs.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tcs.h new file mode 100644 index 000000000000..739ac654ec61 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tcs.h @@ -0,0 +1,249 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _CTS_TCS_H_ +#define _CTS_TCS_H_ + +#include "cts_config.h" + +#define TCS_RD_ADDR 0xF1 +#define TCS_WR_ADDR 0xF0 + +#define INT_DATA_MAX_SIZ 8192 + +#pragma pack(1) +typedef struct { + u8 addr; + u16 cmd; + u16 datlen; + u16 crc16; +} tcs_tx_head; + +typedef struct { + u8 ecode; + u16 cmd; + u16 crc16; +} tcs_rx_tail; + +typedef struct { + u8 baseFlag; + u8 classID; + u8 cmdID; + u8 isRead; + u8 isWrite; + u8 isData; +} TcsCmdValue_t; +#pragma pack(0) + +int cts_tcs_get_fw_ver(const struct cts_device *cts_dev, u16 *fwver); +int cts_tcs_get_lib_ver(const struct cts_device *cts_dev, u16 *libver); +int cts_tcs_get_ddi_ver(const struct cts_device *cts_dev, u8 *ddiver); +int cts_tcs_get_res_x(const struct cts_device *cts_dev, u16 *res_x); +int cts_tcs_get_res_y(const struct cts_device *cts_dev, u16 *res_y); +int cts_tcs_get_rows(const struct cts_device *cts_dev, u8 *rows); +int cts_tcs_get_cols(const struct cts_device *cts_dev, u8 *cols); +int cts_tcs_get_flip_x(const struct cts_device *cts_dev, bool *flip_x); +int cts_tcs_get_flip_y(const struct cts_device *cts_dev, bool *flip_y); +int cts_tcs_get_swap_axes(const struct cts_device *cts_dev, bool *swap_axes); + +int cts_tcs_get_int_mode(const struct cts_device *cts_dev, u8 *int_mode); +int cts_tcs_get_int_keep_time(const struct cts_device *cts_dev, + u16 *int_keep_time); +int cts_tcs_get_esd_method(const struct cts_device *cts_dev, u8 *esd_method); + +int cts_tcs_get_touchinfo(struct cts_device *cts_dev, + struct cts_device_touch_info *touch_info); +int cts_tcs_get_esd_protection(const struct cts_device *cts_dev, + u8 *esd_protection); + +int cts_tcs_get_data_ready_flag(const struct cts_device *cts_dev, u8 *ready); +int cts_tcs_clr_data_ready_flag(const struct cts_device *cts_dev); +int cts_tcs_clr_gstr_ready_flag(const struct cts_device *cts_dev); +int cts_tcs_enable_get_rawdata(const struct cts_device *cts_dev); +int cts_tcs_is_enabled_get_rawdata(const struct cts_device *cts_dev, + u8 *enabled); +int cts_tcs_disable_get_rawdata(const struct cts_device *cts_dev); +int cts_tcs_enable_get_cneg(const struct cts_device *cts_dev); +int cts_tcs_disable_get_cneg(const struct cts_device *cts_dev); +int cts_tcs_is_cneg_ready(const struct cts_device *cts_dev, u8 *ready); + +int cts_tcs_quit_guesture_mode(const struct cts_device *cts_dev); + +int cts_tcs_get_rawdata(const struct cts_device *cts_dev, u8 *buf); +int cts_tcs_get_diffdata(const struct cts_device *cts_dev, u8 *buf); +int cts_tcs_get_basedata(const struct cts_device *cts_dev, u8 *buf); +int cts_tcs_get_cneg(const struct cts_device *cts_dev, u8 *buf, size_t size); + +int cts_tcs_read_hw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); +int cts_tcs_write_hw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); +int cts_tcs_read_ddi_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size); +int cts_tcs_write_ddi_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size); +int cts_tcs_read_fw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); +int cts_tcs_write_fw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); +int cts_tcs_read_reg(const struct cts_device *cts_dev, uint16_t cmd, + u8 *rbuf, size_t rlen); +int cts_tcs_write_reg(const struct cts_device *cts_dev, uint16_t cmd, + u8 *wbuf, size_t wlen); + +int cts_tcs_get_fw_id(const struct cts_device *cts_dev, u16 *fwid); +int cts_tcs_get_workmode(const struct cts_device *cts_dev, u8 *workmode); +int cts_tcs_set_workmode(const struct cts_device *cts_dev, u8 workmode); +int cts_tcs_set_openshort_mode(const struct cts_device *cts_dev, u8 mode); +int cts_tcs_set_tx_vol(const struct cts_device *cts_dev, u8 txvol); + +int cts_tcs_set_short_test_type(const struct cts_device *cts_dev, + u8 short_type); +int cts_tcs_set_openshort_enable(const struct cts_device *cts_dev, u8 enable); +int cts_tcs_is_openshort_enabled(const struct cts_device *cts_dev, + u8 *enabled); + +int cts_tcs_set_esd_enable(const struct cts_device *cts_dev, u8 enable); +int cts_tcs_set_cneg_enable(const struct cts_device *cts_dev, u8 enable); +int cts_tcs_set_mnt_enable(const struct cts_device *cts_dev, u8 enable); +int cts_tcs_is_display_on(const struct cts_device *cts_dev, u8 *display_on); +int cts_tcs_set_display_on(const struct cts_device *cts_dev, u8 display_on); +int cts_tcs_is_cneg_enabled(const struct cts_device *cts_dev, u8 *enabled); +int cts_tcs_is_mnt_enabled(const struct cts_device *cts_dev, u8 *enabled); +int cts_tcs_set_pwr_mode(const struct cts_device *cts_dev, u8 pwr_mode); + +int cts_tcs_read_sram_normal_mode(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay); + +enum TcsCmdIndex { + TP_STD_CMD_INFO_CHIP_FW_ID_RO, + TP_STD_CMD_INFO_FW_VER_RO, + TP_STD_CMD_INFO_TOUCH_XY_INFO_RO, + TP_STD_CMD_INFO_MODULE_ID_RO, + + TP_STD_CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, + TP_STD_CMD_TP_DATA_READ_START_RO, + TP_STD_CMD_TP_DATA_COORDINATES_RO, + TP_STD_CMD_TP_DATA_RAW_RO, + TP_STD_CMD_TP_DATA_DIFF_RO, + TP_STD_CMD_TP_DATA_BASE_RO, + TP_STD_CMD_TP_DATA_CNEG_RO, + TP_STD_CMD_TP_DATA_WR_REG_RAM_SEQUENCE_WO, + TP_STD_CMD_TP_DATA_WR_REG_RAM_BATCH_WO, + TP_STD_CMD_TP_DATA_WR_DDI_REG_SEQUENCE_WO, + TP_STD_CMD_GET_DATA_BY_POLLING_RO, + + TP_STD_CMD_SYS_STS_READ_RO, + TP_STD_CMD_SYS_STS_WORK_MODE_RW, + TP_STD_CMD_SYS_STS_DAT_RDY_FLAG_RW, + TP_STD_CMD_SYS_STS_PWR_STATE_RW, + TP_STD_CMD_SYS_STS_CHARGER_PLUGIN_RW, + TP_STD_CMD_SYS_STS_DDI_CODE_VER_RO, + TP_STD_CMD_SYS_STS_DAT_TRANS_IN_NORMAL_RW, + TP_STD_CMD_SYS_STS_VSTIM_LVL_RW, + TP_STD_CMD_SYS_STS_CNEG_RDY_FLAG_RW, + TP_STD_CMD_SYS_STS_RESET_WO, + TP_STD_CMD_SYS_STS_INT_TEST_EN_RW, + TP_STD_CMD_SYS_STS_SET_INT_PIN_RW, + TP_STD_CMD_SYS_STS_CNEG_RD_EN_RW, + TP_STD_CMD_SYS_STS_INT_MODE_RW, + TP_STD_CMD_SYS_STS_INT_KEEP_TIME_RW, + TP_STD_CMD_SYS_STS_CURRENT_WORKMODE_RO, + TP_STD_CMD_SYS_STS_DATA_CAPTURE_SUPPORT_RO, + TP_STD_CMD_SYS_STS_DATA_CAPTURE_EN_RW, + TP_STD_CMD_SYS_STS_DATA_CAPTURE_FUNC_MAP_RW, + TP_STD_CMD_SYS_STS_PANEL_DIRECTION_RW, + TP_STD_CMD_SYS_STS_GAME_MODE_RW, + + TP_STD_CMD_GSTR_WAKEUP_EN_RW, + TP_STD_CMD_GSTR_DAT_RDY_FLAG_GSTR_RW, + TP_STD_CMD_GSTR_ENTER_MAP_RW, + + TP_STD_CMD_MNT_EN_RW, + TP_STD_CMD_MNT_FORCE_EXIT_MNT_WO, + + TP_STD_CMD_DDI_ESD_EN_RW, + TP_STD_CMD_DDI_ESD_OPTIONS_RW, + + TP_STD_CMD_CNEG_EN_RW, + + TP_STD_CMD_COORD_FLIP_X_EN_RW, + TP_STD_CMD_COORD_FLIP_Y_EN_RW, + TP_STD_CMD_COORD_SWAP_AXES_EN_RW, + + TP_STD_CMD_OPENSHORT_EN_RW, + TP_STD_CMD_OPENSHORT_MODE_SEL_RW, + TP_STD_CMD_OPENSHORT_SHORT_SEL_RW, + TP_STD_CMD_OPENSHORT_SHORT_DISP_ON_EN_RW, +}; + +#ifdef _CTS_TCS_C_ +static TcsCmdValue_t TcsCmdValue[] = { +/*--------------------------------------------------- + *baseFlage, classID, cmdID, isRead, isWrite, isData + *--------------------------------------------------- + */ + { 0, 0, 3, 1, 0, 0 }, /* TP_STD_CMD_INFO_CHIP_FW_ID_RO */ + { 0, 0, 5, 1, 0, 0 }, /* TP_STD_CMD_INFO_FW_VER_RO */ + { 0, 0, 7, 1, 0, 0 }, /* TP_STD_CMD_INFO_TOUCH_XY_INFO_RO */ + { 0, 0, 17, 1, 0, 0 }, /* TP_STD_CMD_INFO_MODULE_ID_RO */ + + { 0, 1, 1, 1, 1, 0 }, /* TP_STD_CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW */ + { 0, 1, 2, 1, 0, 1 }, /* TP_STD_CMD_TP_DATA_READ_START_RO */ + { 0, 1, 3, 1, 0, 1 }, /* TP_STD_CMD_TP_DATA_COORDINATES_RO */ + { 0, 1, 4, 1, 0, 1 }, /* TP_STD_CMD_TP_DATA_RAW_RO */ + { 0, 1, 5, 1, 0, 1 }, /* TP_STD_CMD_TP_DATA_DIFF_RO */ + { 0, 1, 6, 1, 0, 1 }, /* TP_STD_CMD_TP_DATA_BASE_RO */ + { 0, 1, 10, 1, 0, 1 }, /* TP_STD_CMD_TP_DATA_CNEG_RO */ + { 0, 1, 20, 0, 1, 1 }, /* TP_STD_CMD_TP_DATA_WR_REG_RAM_SEQUENCE_WO */ + { 0, 1, 21, 0, 1, 1 }, /* TP_STD_CMD_TP_DATA_WR_REG_RAM_BATCH_WO */ + { 0, 1, 22, 0, 1, 1 }, /* TP_STD_CMD_TP_DATA_WR_DDI_REG_SEQUENCE_WO */ + { 0, 1, 35, 1, 0, 0 }, /* TP_STD_CMD_GET_DATA_BY_POLLING_RO */ + + { 0, 2, 0, 1, 0, 1 }, /* TP_STD_CMD_SYS_STS_READ_RO *//*CHECK!!*/ + { 0, 2, 1, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_WORK_MODE_RW */ + { 0, 2, 3, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_DAT_RDY_FLAG_RW */ + { 0, 2, 4, 1, 1, 1 }, /* TP_STD_CMD_SYS_STS_PWR_STATE_RW */ + { 0, 2, 5, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_CHARGER_PLUGIN_RW */ + { 0, 2, 6, 1, 0, 0 }, /* TP_STD_CMD_SYS_STS_DDI_CODE_VER_RO */ + { 0, 2, 7, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_DAT_TRANS_IN_NORMAL_RW */ + { 0, 2, 8, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_VSTIM_LVL_RW */ + { 0, 2, 17, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_CNEG_RDY_FLAG_RW */ + { 0, 2, 22, 0, 1, 0 }, /* TP_STD_CMD_SYS_STS_RESET_WO */ + { 0, 2, 23, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_INT_TEST_EN_RW */ + { 0, 2, 24, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_SET_INT_PIN_RW */ + { 0, 2, 25, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_CNEG_RD_EN_RW */ + { 0, 2, 35, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_INT_MODE_RW */ + { 0, 2, 36, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_INT_KEEP_TIME_RW */ + { 0, 2, 51, 1, 0, 0 }, /* TP_STD_CMD_SYS_STS_CURRENT_WORKMODE_RO */ + { 0, 2, 63, 1, 0, 0 }, /* TP_STD_CMD_SYS_STS_DATA_CAPTURE_SUPPORT_RO */ + { 0, 2, 64, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_DATA_CAPTURE_EN_RW */ + { 0, 2, 65, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_DATA_CAPTURE_FUNC_MAP_RW */ + { 0, 2, 66, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_PANEL_DIRECTION_RW */ + { 0, 2, 78, 1, 1, 0 }, /* TP_STD_CMD_SYS_STS_GAME_MODE_RW */ + + { 0, 3, 1, 1, 1, 0 }, /* TP_STD_CMD_GSTR_WAKEUP_EN_RW */ + { 0, 3, 30, 1, 1, 0 }, /* TP_STD_CMD_GSTR_DAT_RDY_FLAG_GSTR_RW */ + { 0, 3, 40, 1, 1, 0 }, /* TP_STD_CMD_GSTR_ENTER_MAP_RW */ + + { 0, 4, 1, 1, 1, 0 }, /* TP_STD_CMD_MNT_EN_RW */ + { 0, 4, 3, 0, 1, 0 }, /* TP_STD_CMD_MNT_FORCE_EXIT_MNT_WO */ + + { 0, 5, 1, 1, 1, 0 }, /* TP_STD_CMD_DDI_ESD_EN_RW */ + { 0, 5, 2, 1, 1, 0 }, /* TP_STD_CMD_DDI_ESD_OPTIONS_RW */ + + { 0, 6, 1, 1, 1, 0 }, /* TP_STD_CMD_CNEG_EN_RW */ + + { 0, 7, 2, 1, 1, 0 }, /* TP_STD_CMD_COORD_FLIP_X_EN_RW */ + { 0, 7, 3, 1, 1, 0 }, /* TP_STD_CMD_COORD_FLIP_Y_EN_RW */ + { 0, 7, 4, 1, 1, 0 }, /* TP_STD_CMD_COORD_SWAP_AXES_EN_RW */ + + { 0, 11, 1, 1, 1, 0 }, /* TP_STD_CMD_OPENSHORT_EN_RW */ + { 0, 11, 2, 1, 1, 0 }, /* TP_STD_CMD_OPENSHORT_MODE_SEL_RW */ + { 0, 11, 3, 1, 1, 0 }, /* TP_STD_CMD_OPENSHORT_SHORT_SEL_RW */ + { 0, 11, 4, 1, 1, 0 }, /* TP_STD_CMD_OPENSHORT_SHORT_DISP_ON_EN_RW */ +}; +#endif /* _CTS_TCS_C_ */ + +extern struct cts_dev_ops tcs_ops; + +#endif /* _CTS_TCS_H_ */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_test.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_test.c new file mode 100644 index 000000000000..f682c1996acb --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_test.c @@ -0,0 +1,2154 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "Test" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_strerror.h" +#include "cts_test.h" + +#ifdef CTS_CONFIG_MKDIR_FOR_CTS_TEST +/* for ksys_mkdir/sys_mkdir */ +#include +#include +#endif /* CTS_CONFIG_MKDIR_FOR_CTS_TEST */ + +const char *cts_test_item_str(int test_item) +{ +#define case_test_item(item) \ + case CTS_TEST_ ## item: return #item "-TEST" + + switch (test_item) { + case_test_item(RESET_PIN); + case_test_item(INT_PIN); + case_test_item(RAWDATA); + case_test_item(NOISE); + case_test_item(OPEN); + case_test_item(SHORT); + case_test_item(COMPENSATE_CAP); + + default: + return "INVALID"; + } +#undef case_test_item +} + +/* ICNL9911/S/C */ +#define CTS_FIRMWARE_WORK_MODE_NORMAL (0x00) +#define CTS_FIRMWARE_WORK_MODE_FACTORY (0x01) +#define CTS_FIRMWARE_WORK_MODE_CONFIG (0x02) +#define CTS_FIRMWARE_WORK_MODE_TEST (0x03) + +/* ICNL9916/9922 */ +#define CTS_FIRMWARE_WORK_MODE_CFG (0x01) +#define CTS_FIRMWARE_WORK_MODE_OPEN_SHORT (0x02) + +#define CTS_TEST_SHORT (0x01) +#define CTS_TEST_OPEN (0x02) + +#define CTS_SHORT_TEST_UNDEFINED (0x00) +#define CTS_SHORT_TEST_BETWEEN_COLS (0x01) +#define CTS_SHORT_TEST_BETWEEN_ROWS (0x02) +#define CTS_SHORT_TEST_BETWEEN_GND (0x03) + +#define TEST_RESULT_BUFFER_SIZE(cts_dev) \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * 2) + +#define RAWDATA_BUFFER_SIZE(cts_dev) \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * 2) + +int disable_fw_esd_protection(struct cts_device *cts_dev) +{ + return cts_fw_reg_writeb(cts_dev, 0x8000 + 342, 1); +} + +int disable_fw_monitor_mode(struct cts_device *cts_dev) +{ + int ret; + u8 value; + + ret = cts_fw_reg_readb(cts_dev, 0x8000 + 344, &value); + if (ret) + return ret; + + if (value & BIT(0)) + return cts_fw_reg_writeb(cts_dev, 0x8000 + 344, + value & (~BIT(0))); + + return 0; +} + +int disable_fw_auto_compensate(struct cts_device *cts_dev) +{ + return cts_fw_reg_writeb(cts_dev, 0x8000 + 276, 1); +} + +int set_fw_work_mode(const struct cts_device *cts_dev, u8 mode) +{ + int ret, retries; + u8 pwr_mode; + + cts_info("Set firmware work mode to %u", mode); + + ret = cts_fw_reg_writeb(cts_dev, CTS_DEVICE_FW_REG_WORK_MODE, mode); + if (ret) { + cts_err("Write firmware work mode register failed %d", ret); + return ret; + } + + ret = cts_fw_reg_readb(cts_dev, 0x05, &pwr_mode); + if (ret) { + cts_err("Read firmware power mode register failed %d", ret); + return ret; + } + + if (pwr_mode == 1) { + ret = cts_send_command(cts_dev, CTS_CMD_QUIT_GESTURE_MONITOR); + if (ret) { + cts_err("Send cmd QUIT_GESTURE_MONITOR failed %d", ret); + return ret; + } + + msleep(50); + } + + retries = 0; + do { + u8 sys_busy, curr_mode; + + msleep(10); + + ret = cts_fw_reg_readb(cts_dev, 0x01, &sys_busy); + if (ret) { + cts_err("Read firmware system busy register failed %d", + ret); + return ret; + } + if (sys_busy) + continue; + + ret = cts_fw_reg_readb(cts_dev, 0x3F, &curr_mode); + if (ret) { + cts_err("Read firmware current work mode failed %d", + ret); + return ret; + } + + if (curr_mode == mode || curr_mode == 0xFF) + break; + } while (retries++ < 1000); + + return (retries >= 1000 ? -ETIMEDOUT : 0); +} + +int set_display_state(const struct cts_device *cts_dev, bool active) +{ + int ret; + u8 access_flag; + + cts_info("Set display state to %s", active ? "ACTIVE" : "SLEEP"); + + ret = cts_hw_reg_readb_relaxed(cts_dev, 0x3002C, &access_flag); + if (ret) { + cts_err("Read display access flag failed %d", ret); + return ret; + } + + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3002C, access_flag | 0x01); + if (ret) { + cts_err("Write display access flag %02x failed %d", access_flag, + ret); + return ret; + } + + if (active) { + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3C044, 0x55); + if (ret) { + cts_err("Write DCS-CMD11 fail"); + return ret; + } + + msleep(100); + + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3C0A4, 0x55); + if (ret) { + cts_err("Write DCS-CMD29 fail"); + return ret; + } + + msleep(100); + } else { + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3C0A0, 0x55); + if (ret) { + cts_err("Write DCS-CMD28 fail"); + return ret; + } + + msleep(100); + + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3C040, 0x55); + if (ret) { + cts_err("Write DCS-CMD10 fail"); + return ret; + } + + msleep(100); + } + + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3002C, access_flag); + if (ret) { + cts_err("Restore display access flag %02x failed %d", + access_flag, ret); + return ret; + } + + return 0; +} + +static int wait_test_complete(struct cts_device *cts_dev, int skip_frames) +{ + int ret, i, j; + + cts_info("Wait test complete skip %d frames", skip_frames); + + if ((cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9916) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9922)) { + return 0; + } + + for (i = 0; i < (skip_frames + 1); i++) { + u8 ready; + + for (j = 0; j < 100; j++) { + mdelay(10); + + ready = 0; + ret = + cts_dev->ops->get_data_ready_flag(cts_dev, &ready); + if (ret) { + cts_err("Get data ready flag failed %d", ret); + return ret; + } + + if (ready) + break; + } + + if (ready == 0) + return -ETIMEDOUT; + + if (i < skip_frames) { + ret = cts_dev->ops->clr_data_ready_flag(cts_dev); + if (ret) { + cts_err("Clr data ready flag failed %d", ret); + return ret; + } + } + } + + return 0; +} + +struct cts_fw_short_test_param { + u8 type; + u32 col_pattern[2]; + u32 row_pattern[2]; +}; + +bool set_short_test_type(struct cts_device *cts_dev, u8 type) +{ + static struct cts_fw_short_test_param param = { + .type = CTS_SHORT_TEST_BETWEEN_COLS, + .col_pattern = { 0, 0 }, + .row_pattern = { 0, 0 } + }; + int i, ret; + + cts_info("Set short test type to %u", type); + + param.type = type; + for (i = 0; i < 5; i++) { + u8 type_readback; + + ret = + cts_fw_reg_writesb(cts_dev, 0x5000, ¶m, sizeof(param)); + if (ret) { + cts_err("Set short test type to %u failed %d", type, + ret); + continue; + } + ret = cts_fw_reg_readb(cts_dev, 0x5000, &type_readback); + if (ret) { + cts_err("Get short test type failed %d", ret); + continue; + } + if (type == type_readback) + return 0; + else { + cts_err("Set test type %u != readback %u", type, + type_readback); + continue; + } + } + + return ret; +} + +int cts_write_file(struct file *filp, const void *data, size_t size) +{ + loff_t pos; + ssize_t ret; + + pos = filp->f_pos; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(filp, data, size, &pos); +#else + ret = kernel_write(filp, data, size, pos); +#endif + + if (ret >= 0) + filp->f_pos += ret; + + return ret; +} + +#ifdef CTS_CONFIG_MKDIR_FOR_CTS_TEST +/* copied from fs/namei.c */ +long do_mkdirat(int dfd, const char __user *pathname, umode_t mode) +{ + struct dentry *dentry; + struct path path; + int error; + unsigned int lookup_flags = LOOKUP_DIRECTORY; + +retry: + dentry = user_path_create(dfd, pathname, &path, lookup_flags); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + if (!IS_POSIXACL(path.dentry->d_inode)) + mode &= ~current_umask(); + error = security_path_mkdir(&path, dentry, mode); + if (!error) + error = vfs_mkdir(path.dentry->d_inode, dentry, mode); + done_path_create(&path, dentry); + if (retry_estale(error, lookup_flags)) { + lookup_flags |= LOOKUP_REVAL; + goto retry; + } + return error; +} + +/* Make directory for filepath + * If filepath = "/A/B/C/D.file", it will make dir /A/B/C recursive + * like userspace mkdir -p + */ +int cts_mkdir_for_file(const char *filepath, umode_t mode) +{ + char *dirname = NULL; + int dirname_len; + char *s; + int ret; + mm_segment_t fs; + + if (filepath == NULL) { + cts_err("Create dir for file with filepath = NULL"); + return -EINVAL; + } + + if (filepath[0] == '\0' || filepath[0] != '/') { + cts_err("Create dir for file with invalid filepath[0]: %c", + filepath[0]); + return -EINVAL; + } + + dirname_len = strrchr(filepath, '/') - filepath; + if (dirname_len == 0) { + cts_warn("Create dir for file '%s' in root dir", filepath); + return 0; + } + + dirname = kstrndup(filepath, dirname_len, GFP_KERNEL); + if (dirname == NULL) { + cts_err("Create dir alloc mem for dirname failed"); + return -ENOMEM; + } + + cts_info("Create dir '%s' for file '%s'", dirname, filepath); + + fs = get_fs(); + set_fs(KERNEL_DS); + + s = dirname + 1; /* Skip leading '/' */ + while (1) { + char c = '\0'; + + /* Bypass leading non-'/'s and then subsequent '/'s */ + while (*s) { + if (*s == '/') { + do { + ++s; + } while (*s == '/'); + c = *s; /* Save current char */ + *s = '\0'; /* and replace it with nul */ + break; + } + ++s; + } + + cts_dbg(" - Create dir '%s'", dirname); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) + ret = ksys_mkdir(dirname, 0777); +#else + ret = sys_mkdir(dirname, 0777); +#endif + if (ret < 0 && ret != -EEXIST) { + cts_info("Create dir '%s' failed %d(%s)", + dirname, ret, cts_strerror(ret)); + /* Remove any inserted nul from the path */ + *s = c; + break; + } + /* Reset ret to 0 if return -EEXIST */ + ret = 0; + + if (c) { + /* Remove any inserted nul from the path */ + *s = c; + } else { + break; + } + } + + set_fs(fs); + + if (dirname) + kfree(dirname); + + return ret; +} +#endif /* CTS_CONFIG_MKDIR_FOR_CTS_TEST */ + +struct file *cts_test_data_filp; +int cts_start_dump_test_data_to_file(const char *filepath, bool append_to_file) +{ + int ret; + +#define START_BANNER \ + ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" + + cts_info("Start dump test data to file '%s'", filepath); + +#ifdef CTS_CONFIG_MKDIR_FOR_CTS_TEST + ret = cts_mkdir_for_file(filepath, 0777); + if (ret) { + cts_err("Create dir for test data file failed %d", ret); + return ret; + } +#endif /* CTS_CONFIG_MKDIR_FOR_CTS_TEST */ + + cts_test_data_filp = filp_open(filepath, + O_WRONLY | O_CREAT | (append_to_file ? + O_APPEND : + O_TRUNC), + S_IRUGO | S_IWUGO); + if (IS_ERR(cts_test_data_filp)) { + ret = PTR_ERR(cts_test_data_filp); + cts_test_data_filp = NULL; + cts_err("Open file '%s' for test data failed %d", + cts_test_data_filp, ret); + return ret; + } + + cts_write_file(cts_test_data_filp, START_BANNER, strlen(START_BANNER)); + + return 0; +#undef START_BANNER +} + +void cts_stop_dump_test_data_to_file(void) +{ +#define END_BANNER \ + "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n" + int r; + + cts_info("Stop dump test data to file"); + + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, + END_BANNER, strlen(END_BANNER)); + r = filp_close(cts_test_data_filp, NULL); + if (r) + cts_err("Close test data file failed %d", r); + cts_test_data_filp = NULL; + } else { + cts_warn("Stop dump tsdata to file with filp = NULL"); + } +#undef END_BANNER +} + +static void cts_dump_tsdata(struct cts_device *cts_dev, + const char *desc, const u16 *data, bool to_console) +{ +#define SPLIT_LINE_STR \ + "--------------------------------------------------------"\ + "--------------------------------------------------------" +#define ROW_NUM_FORMAT_STR "%2d | " +#define COL_NUM_FORMAT_STR "%-5u " +#define DATA_FORMAT_STR "%-5u " + + int r, c; + u32 max, min, sum, average; + int max_r, max_c, min_r, min_c; + char line_buf[128]; + int count = 0; + + max = min = data[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + u16 val = data[r * cts_dev->hwdata->num_col + c]; + + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count = 0; + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + " %s test data MIN: [%u][%u]=%u, MAX: [%u][%u]=%u, AVG=%u", + desc, min_r, min_c, min, max_r, max_c, max, average); + if (to_console) { + cts_info(SPLIT_LINE_STR); + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, + strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, + strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } + + count = 0; + count += + scnprintf(line_buf + count, sizeof(line_buf) - count, " | "); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + COL_NUM_FORMAT_STR, c); + } + if (to_console) { + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, + strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count = 0; + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += + scnprintf(line_buf + count, + sizeof(line_buf) - count, DATA_FORMAT_STR, + data[r * cts_dev->hwdata->num_col + c]); + } + if (to_console) + cts_info("%s", line_buf); + + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + } + } + if (to_console) + cts_info(SPLIT_LINE_STR); + + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, + strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } +#undef SPLIT_LINE_STR +#undef ROW_NUM_FORMAT_STR +#undef COL_NUM_FORMAT_STR +#undef DATA_FORMAT_STR +} + +static bool is_invalid_node(u32 *invalid_nodes, u32 num_invalid_nodes, + u16 row, u16 col) +{ + int i; + + for (i = 0; i < num_invalid_nodes; i++) { + if (MAKE_INVALID_NODE(row, col) == invalid_nodes[i]) + return true; + } + + return false; +} + +/* Return number of failed nodes */ +static int validate_tsdata(struct cts_device *cts_dev, + const char *desc, u16 *data, + u32 *invalid_nodes, u32 num_invalid_nodes, + bool per_node, int *min, int *max) +{ +#define SPLIT_LINE_STR \ + "------------------------------" + + int r, c; + int failed_cnt = 0; + + cts_info + ("%s validate data: %s, num invalid node: %u, thresh[0]=[%d, %d]", + desc, per_node ? "Per-Node" : "Uniform-Threshold", + num_invalid_nodes, min ? min[0] : INT_MIN, max ? max[0] : INT_MAX); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + int offset = r * cts_dev->hwdata->num_col + c; + + if (num_invalid_nodes && + is_invalid_node(invalid_nodes, num_invalid_nodes, r, + c)) { + continue; + } + + if ((min != NULL + && data[offset] < min[per_node ? offset : 0]) + || (max != NULL + && data[offset] > max[per_node ? offset : 0])) { + if (failed_cnt == 0) { + cts_info(SPLIT_LINE_STR); + cts_info("%s failed nodes:", desc); + } + failed_cnt++; + + cts_info(" %3d: [%-2d][%-2d] = %u", + failed_cnt, r, c, data[offset]); + } + } + } + + if (failed_cnt) { + cts_info(SPLIT_LINE_STR); + cts_info("%s test %d node total failed", desc, failed_cnt); + } + + return failed_cnt; + +#undef SPLIT_LINE_STR +} + +static int wait_fw_to_normal_work(struct cts_device *cts_dev) +{ + int i = 0; + int ret; + + cts_info("Wait fw to normal work"); + + do { + u8 work_mode; + + ret = cts_dev->ops->get_workmode(cts_dev, &work_mode); + if (ret) { + cts_err("Get fw curr work mode failed %d", work_mode); + continue; + } else { + if (work_mode == CTS_FIRMWARE_WORK_MODE_NORMAL) + return 0; + } + + mdelay(10); + } while (++i < 100); + + return ret ? ret : -ETIMEDOUT; +} + +static int prepare_test(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Prepare test"); + + cts_reset_device(cts_dev); + + ret = wait_fw_to_normal_work(cts_dev); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + return ret; + } + + ret = cts_dev->ops->set_esd_enable(cts_dev, 0x00); + if (ret) { + cts_err("Disable firmware ESD protection failed %d", ret); + return ret; + } + + ret = cts_dev->ops->set_mnt_enable(cts_dev, 0x00); + if (ret) { + cts_err("Disable firmware monitor mode failed %d", ret); + return ret; + } + + ret = cts_dev->ops->set_cneg_enable(cts_dev, 0x00); + if (ret) { + cts_err("Disable firmware auto compensate failed %d", ret); + return ret; + } + + if ((cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9916) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9922)) + ret = + cts_dev->ops->set_workmode(cts_dev, + CTS_FIRMWARE_WORK_MODE_CFG); + else + ret = + cts_dev->ops->set_workmode(cts_dev, + CTS_FIRMWARE_WORK_MODE_CONFIG); + + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", + ret); + return ret; + } + + cts_dev->rtdata.testing = true; + + return 0; +} + +#define SHORT_COLS_TEST_LOOP 3 +#define SHORT_ROWS_TEST_LOOP 6 +static void post_test(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Post test"); + + cts_reset_device(cts_dev); + + ret = + cts_dev->ops->set_workmode(cts_dev, CTS_FIRMWARE_WORK_MODE_NORMAL); + if (ret) + cts_err("Set firmware work mode to WORK_MODE_NORMAL failed %d", + ret); + + ret = wait_fw_to_normal_work(cts_dev); + if (ret) + cts_err("Wait fw to normal work failed %d", ret); + + cts_dev->rtdata.testing = false; +} + +#ifdef CFG_CTS_HAS_RESET_PIN +int cts_test_reset_pin(struct cts_device *cts_dev, struct cts_test_param *param) +{ + ktime_t start_time, end_time, delta_time; + int ret; + + if (cts_dev == NULL || param == NULL) { + cts_err + ("Reset-pin test with invalid param: cts_dev: %p test param: %p", + cts_dev, param); + return -EINVAL; + } + + cts_info("Reset-Pin test, flags: 0x%08x, " + "drive log file: '%s' buf size: %d", + param->flags, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto show_test_result; + } + + cts_lock_device(cts_dev); + + cts_plat_set_reset(cts_dev->pdata, 0); + mdelay(50); +#ifdef CONFIG_CTS_I2C_HOST + /* Check whether device is in normal mode */ + if (cts_plat_is_i2c_online(cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR)) { +#else + if (cts_plat_is_normal_mode(cts_dev->pdata)) { +#endif /* CONFIG_CTS_I2C_HOST */ + ret = -EIO; + cts_err("Device is alive while reset is low"); + } + cts_plat_set_reset(cts_dev->pdata, 1); + mdelay(50); + + { + int r = wait_fw_to_normal_work(cts_dev); + + if (r) + cts_err("Wait fw to normal work failed %d", r); + } + +#ifdef CONFIG_CTS_I2C_HOST + /* Check whether device is in normal mode */ + if (!cts_plat_is_i2c_online(cts_dev->pdata, + CTS_DEV_NORMAL_MODE_I2CADDR)) { +#else + if (!cts_plat_is_normal_mode(cts_dev->pdata)) { +#endif /* CONFIG_CTS_I2C_HOST */ + ret = -EIO; + cts_err("Device is offline while reset is high"); + } + +#ifdef CONFIG_CTS_CHARGER_DETECT + if (cts_is_charger_exist(cts_dev)) { + int r = cts_set_dev_charger_attached(cts_dev, true); + + if (r) + cts_err("Set dev charger attached failed %d", r); + } +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_EARJACK_DETECT + if (cts_dev->fwdata.supp_headphone_cable_reject && + cts_is_earjack_exist(cts_dev)) { + int r = cts_set_dev_earjack_attached(cts_dev, true); + + if (r) + cts_err("Set dev earjack attached failed %d", r); + } +#endif /* CONFIG_CTS_EARJACK_DETECT */ + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) + cts_enter_glove_mode(cts_dev); +#endif + +#ifdef CFG_CTS_FW_LOG_REDIRECT + if (cts_is_fw_log_redirect(cts_dev)) + cts_enable_fw_log_redirect(cts_dev); +#endif + + cts_unlock_device(cts_dev); + + { + int r = cts_start_device(cts_dev); + + if (r) + cts_err("Start device failed %d", r); + } + + if (!cts_dev->rtdata.program_mode) + cts_set_normal_addr(cts_dev); + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret) + cts_info("Reset-Pin test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + else + cts_info("Reset-Pin test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + + return ret; +} +#endif + +int cts_test_int_pin(struct cts_device *cts_dev, struct cts_test_param *param) +{ + ktime_t start_time, end_time, delta_time; + int ret; + + if (cts_dev == NULL || param == NULL) { + cts_err + ("Int-pin test with invalid param: cts_dev: %p test param: %p", + cts_dev, param); + return -EINVAL; + } + + cts_info("Int-Pin test, flags: 0x%08x, " + "drive log file: '%s' buf size: %d", + param->flags, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto show_test_result; + } + + cts_lock_device(cts_dev); + ret = cts_dev->ops->set_int_test(cts_dev, 1); + if (ret) { + cts_err("Enable Int Test failed"); + goto unlock_device; + } + ret = cts_dev->ops->set_int_pin(cts_dev, 1); + if (ret) { + cts_err("Enable Int Test High failed"); + goto exit_int_test; + } + mdelay(10); + if (cts_plat_get_int_pin(cts_dev->pdata) == 0) { + cts_err("INT pin state != HIGH"); + ret = -EFAULT; + goto exit_int_test; + } + + ret = cts_dev->ops->set_int_pin(cts_dev, 0); + if (ret) { + cts_err("Enable Int Test LOW failed"); + goto exit_int_test; + } + mdelay(10); + if (cts_plat_get_int_pin(cts_dev->pdata) != 0) { + cts_err("INT pin state != LOW"); + ret = -EFAULT; + goto exit_int_test; + } + +exit_int_test: + { + int r = cts_dev->ops->set_int_test(cts_dev, 0); + + if (r) + cts_err("Disable Int Test failed %d", r); + } + mdelay(10); + +unlock_device: + cts_unlock_device(cts_dev); + + { + int r = cts_start_device(cts_dev); + + if (r) + cts_err("Start device failed %d", r); + } + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret) + cts_info("Int-Pin test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + else + cts_info("Int-Pin test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + + return ret; +} + +int cts_test_rawdata(struct cts_device *cts_dev, struct cts_test_param *param) +{ + struct cts_rawdata_test_priv_param *priv_param; + bool driver_validate_data = false; + bool validate_data_per_node = false; + bool stop_test_if_validate_fail = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_file = false; + int num_nodes; + int tsdata_frame_size; + int frame; + int fail_frame = 0; + u16 *rawdata = NULL; + ktime_t start_time, end_time, delta_time; + int i; + int ret; + + if (cts_dev == NULL || param == NULL || + param->priv_param_size != sizeof(*priv_param) || + param->priv_param == NULL) { + cts_err + ("Rawdata test with invalid param: priv param: %p size: %d", + param->priv_param, param->priv_param_size); + return -EINVAL; + } + + priv_param = param->priv_param; + if (priv_param->frames <= 0) { + cts_info("Rawdata test with too little frame %u", + priv_param->frames); + return -EINVAL; + } + + num_nodes = cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; + tsdata_frame_size = 2 * num_nodes; + + driver_validate_data = !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); + validate_data_per_node = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + dump_test_data_to_user = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_console = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); + dump_test_data_to_file = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + stop_test_if_validate_fail = + !!(param->flags & CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED); + + cts_info("Rawdata test, flags: 0x%08x, frames: %d, " + "num invalid node: %u, " + "test data file: '%s' buf size: %d, " + "drive log file: '%s' buf size: %d", + param->flags, priv_param->frames, param->num_invalid_node, + param->test_data_filepath, param->test_data_buf_size, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + if (dump_test_data_to_user) { + rawdata = (u16 *) param->test_data_buf; + } else { + rawdata = (u16 *) kmalloc(tsdata_frame_size, GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate memory for rawdata failed"); + ret = -ENOMEM; + goto show_test_result; + } + } + + /* Stop device to avoid un-wanted interrrupt */ + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto free_mem; + } + + if (dump_test_data_to_file) { + int r = + cts_start_dump_test_data_to_file(param->test_data_filepath, + !!(param->flags & + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); + + if (r) + cts_err("Start dump test data to file failed %d", r); + } + + cts_lock_device(cts_dev); + for (frame = 0; frame < priv_param->frames; frame++) { + bool data_valid = false; + + for (i = 0; i < 3; i++) { + if ((cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9916) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9922)) { + ret = + cts_dev->ops->top_get_rawdata(cts_dev, + (u8 *) + rawdata, + RAWDATA_BUFFER_SIZE + (cts_dev)); + if (ret < 0) { + cts_err("Get raw data failed: %d", ret); + mdelay(30); + } else { + data_valid = true; + break; + } + } + } + + if (!data_valid) { + ret = -EIO; + break; + } + + if (dump_test_data_to_user) + *param->test_data_wr_size += tsdata_frame_size; + + if (dump_test_data_to_console || dump_test_data_to_file) + cts_dump_tsdata(cts_dev, "Rawdata", rawdata, + dump_test_data_to_console); + + if (driver_validate_data) { + ret = validate_tsdata(cts_dev, + "Rawdata", rawdata, + param->invalid_nodes, + param->num_invalid_node, + validate_data_per_node, + param->min, param->max); + if (ret) { + cts_err("Rawdata test failed %d", ret); + fail_frame++; + cts_err("Rawdata test has %d nodes failed", ret); + if (stop_test_if_validate_fail) + break; + } + } + + if (dump_test_data_to_user) + rawdata += num_nodes; + } + + if (dump_test_data_to_file) + cts_stop_dump_test_data_to_file(); + + cts_unlock_device(cts_dev); + { + int r = cts_start_device(cts_dev); + + if (r) + cts_err("Start device failed %d", r); + } + +free_mem: + if (!dump_test_data_to_user && rawdata != NULL) + kfree(rawdata); + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (fail_frame > 0) { + cts_info("Rawdata test has %d frame(s) FAIL, ELAPSED TIME: %lldms", + fail_frame, ktime_to_ms(delta_time)); + } else if (fail_frame == 0) { + cts_info("Rawdata test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + } + + return fail_frame; +} + +int cts_test_noise(struct cts_device *cts_dev, struct cts_test_param *param) +{ + struct cts_noise_test_priv_param *priv_param; + bool driver_validate_data = false; + bool validate_data_per_node = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_file = false; + int num_nodes; + int tsdata_frame_size; + int frame; + u16 *buffer = NULL; + int buf_size = 0; + u16 *curr_rawdata = NULL; + u16 *max_rawdata = NULL; + u16 *min_rawdata = NULL; + u16 *noise = NULL; + bool first_frame = true; + bool data_valid = false; + ktime_t start_time, end_time, delta_time; + int i; + int ret; + + if (cts_dev == NULL || param == NULL || + param->priv_param_size != sizeof(*priv_param) || + param->priv_param == NULL) { + cts_err + ("Noise test with invalid param: priv param: %p size: %d", + param->priv_param, param->priv_param_size); + return -EINVAL; + } + + priv_param = param->priv_param; + if (priv_param->frames < 2) { + cts_err("Noise test with too little frame %u", + priv_param->frames); + return -EINVAL; + } + + num_nodes = cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; + tsdata_frame_size = 2 * num_nodes; + + driver_validate_data = !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); + validate_data_per_node = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + dump_test_data_to_user = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_console = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); + dump_test_data_to_file = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + + cts_info("Noise test, flags: 0x%08x, frames: %d, " + "num invalid node: %u, " + "test data file: '%s' buf size: %d, " + "drive log file: '%s' buf size: %d", + param->flags, priv_param->frames, param->num_invalid_node, + param->test_data_filepath, param->test_data_buf_size, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + buf_size = (driver_validate_data ? 4 : 1) * tsdata_frame_size; + buffer = (u16 *) kmalloc(buf_size, GFP_KERNEL); + if (buffer == NULL) { + cts_err("Alloc mem for touch data failed"); + ret = -ENOMEM; + goto show_test_result; + } + + curr_rawdata = buffer; + if (driver_validate_data) { + max_rawdata = curr_rawdata + 1 * num_nodes; + min_rawdata = curr_rawdata + 2 * num_nodes; + noise = curr_rawdata + 3 * num_nodes; + } + + /* Stop device to avoid un-wanted interrrupt */ + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto free_mem; + } + + if (dump_test_data_to_file) { + int r = + cts_start_dump_test_data_to_file(param->test_data_filepath, + !!(param->flags & + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); + + if (r) + cts_err("Start dump test data to file failed %d", r); + } + + cts_lock_device(cts_dev); + + for (frame = 0; frame < priv_param->frames; frame++) { + for (i = 0; i < 3; i++) { + if ((cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9916) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9922)) { + ret = + cts_dev->ops->top_get_rawdata(cts_dev, + (u8 *) + curr_rawdata, + RAWDATA_BUFFER_SIZE + (cts_dev)); + if (ret < 0) { + cts_err("Get raw data failed: %d", ret); + mdelay(30); + } else { + data_valid = true; + break; + } + } + } + + if (i >= 3) { + cts_err("Read rawdata failed"); + ret = -EIO; + goto disable_get_tsdata; + } + + if (dump_test_data_to_console || dump_test_data_to_file) + cts_dump_tsdata(cts_dev, "Noise-rawdata", curr_rawdata, + dump_test_data_to_console); + + if (dump_test_data_to_user) { + memcpy(param->test_data_buf + frame * tsdata_frame_size, + curr_rawdata, tsdata_frame_size); + *param->test_data_wr_size += tsdata_frame_size; + } + + if (driver_validate_data) { + if (unlikely(first_frame)) { + memcpy(max_rawdata, curr_rawdata, + tsdata_frame_size); + memcpy(min_rawdata, curr_rawdata, + tsdata_frame_size); + first_frame = false; + } else { + for (i = 0; i < num_nodes; i++) { + if (curr_rawdata[i] > max_rawdata[i]) { + max_rawdata[i] = + curr_rawdata[i]; + } else if (curr_rawdata[i] < + min_rawdata[i]) { + min_rawdata[i] = + curr_rawdata[i]; + } + } + } + } + } + + data_valid = true; + +disable_get_tsdata: + if (dump_test_data_to_file) + cts_stop_dump_test_data_to_file(); + + cts_unlock_device(cts_dev); + { + int r = cts_start_device(cts_dev); + + if (r) + cts_err("Start device failed %d", r); + } + + if (driver_validate_data && data_valid) { + for (i = 0; i < num_nodes; i++) + noise[i] = max_rawdata[i] - min_rawdata[i]; + + if (dump_test_data_to_console || dump_test_data_to_file) + cts_dump_tsdata(cts_dev, "Noise", noise, + dump_test_data_to_console); + + ret = validate_tsdata(cts_dev, "Noise test", + noise, param->invalid_nodes, + param->num_invalid_node, + validate_data_per_node, param->min, + param->max); + } + +free_mem: + if (buffer) + kfree(buffer); + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret > 0) + cts_info("Noise test has %d nodes FAIL, ELAPSED TIME: %lldms", + ret, ktime_to_ms(delta_time)); + else if (ret < 0) + cts_info("Noise test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + else + cts_info("Noise test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + + return ret; +} + +/* Return 0 success + * negative value while error occurs + * positive value means how many nodes fail + */ +int cts_test_open(struct cts_device *cts_dev, struct cts_test_param *param) +{ + bool driver_validate_data = false; + bool validate_data_per_node = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_file = false; + int num_nodes; + int tsdata_frame_size; + int ret; + u16 *test_result = NULL; + bool recovery_display_state = false; + u8 need_display_on; + ktime_t start_time, end_time, delta_time; + u8 old_int_data_method = cts_dev->fwdata.int_data_method; + u16 old_int_data_types = cts_dev->fwdata.int_data_types; + + if (cts_dev == NULL || param == NULL) { + cts_err + ("Open test with invalid param: cts_dev: %p test param: %p", + cts_dev, param); + return -EINVAL; + } + + num_nodes = cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; + tsdata_frame_size = 2 * num_nodes; + + driver_validate_data = !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); + validate_data_per_node = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + dump_test_data_to_user = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_console = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); + dump_test_data_to_file = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + + cts_info("Open test, flags: 0x%08x," + "num invalid node: %u, " + "test data file: '%s' buf size: %d, " + "drive log file: '%s' buf size: %d", + param->flags, param->num_invalid_node, + param->test_data_filepath, param->test_data_buf_size, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + if (dump_test_data_to_user) { + test_result = (u16 *) param->test_data_buf; + } else { + test_result = (u16 *) kmalloc(tsdata_frame_size, GFP_KERNEL); + if (test_result == NULL) { + cts_err("Allocate memory for test result faild"); + ret = -ENOMEM; + goto show_test_result; + } + } + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto free_mem; + } + + cts_lock_device(cts_dev); + ret = prepare_test(cts_dev); + if (ret) { + cts_err("Prepare test failed %d", ret); + goto err_free_test_result; + } + + ret = cts_dev->ops->is_display_on(cts_dev, &need_display_on); + if (ret) { + cts_err("Read need display on register failed %d", ret); + goto err_free_test_result; + } + + if (need_display_on == 0) { + ret = cts_dev->ops->set_display_on(cts_dev, 0x00); + if (ret) { + cts_err("Set display state to SLEEP failed %d", ret); + goto err_free_test_result; + } + recovery_display_state = true; + } + + cts_set_int_data_types(cts_dev, INT_DATA_TYPE_RAWDATA); + cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); + + if ((cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911C) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911S) || + (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911)) { + ret = cts_send_command(cts_dev, CTS_CMD_RECOVERY_TX_VOL); + if (ret) { + cts_err("Recovery tx voltage failed %d", ret); + goto err_recovery_display_state; + } + } + + ret = cts_dev->ops->set_openshort_mode(cts_dev, CTS_TEST_OPEN); + if (ret) { + cts_err("Set test type to OPEN_TEST failed %d", ret); + goto err_recovery_display_state; + } + + ret = + cts_dev->ops->set_workmode(cts_dev, + CTS_FIRMWARE_WORK_MODE_OPEN_SHORT); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_TEST failed %d", + ret); + goto err_recovery_display_state; + } + + ret = wait_test_complete(cts_dev, 20); + if (ret) { + cts_err("Wait test complete failed %d", ret); + goto err_recovery_display_state; + } + + ret = + cts_polling_test_data(cts_dev, test_result, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret) { + cts_err("Read test result failed %d", ret); + goto err_recovery_display_state; + } + +err_recovery_display_state: + if (recovery_display_state) { + int r = cts_dev->ops->set_display_on(cts_dev, 0x01); + + if (r) + cts_err("Set display state to ACTIVE failed %d", r); + } + + if (dump_test_data_to_user) + *param->test_data_wr_size += tsdata_frame_size; + + if (dump_test_data_to_file) { + int r = + cts_start_dump_test_data_to_file(param->test_data_filepath, + !!(param->flags & + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); + + if (r) + cts_err("Start dump test data to file failed %d", r); + } + + if (dump_test_data_to_console || dump_test_data_to_file) + cts_dump_tsdata(cts_dev, "Open-circuit", test_result, + dump_test_data_to_console); + + if (dump_test_data_to_file) + cts_stop_dump_test_data_to_file(); + + if (driver_validate_data) + ret = validate_tsdata(cts_dev, "Open-circuit", + test_result, param->invalid_nodes, + param->num_invalid_node, + validate_data_per_node, param->min, + param->max); + +err_free_test_result: + post_test(cts_dev); + + cts_set_int_data_method(cts_dev, old_int_data_method); + cts_set_int_data_types(cts_dev, old_int_data_types); + + cts_unlock_device(cts_dev); + cts_start_device(cts_dev); + +free_mem: + if (!dump_test_data_to_user && test_result) + kfree(test_result); + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret > 0) + cts_info("Open test has %d nodes FAIL, ELAPSED TIME: %lldms", + ret, ktime_to_ms(delta_time)); + else if (ret < 0) + cts_info("Open test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + else + cts_info("Open test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + + return ret; +} + +/* Return 0 success + *negative value while error occurs + *positive value means how many nodes fail + */ +int cts_test_short(struct cts_device *cts_dev, struct cts_test_param *param) +{ + bool driver_validate_data = false; + bool validate_data_per_node = false; + bool stop_if_failed = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_file = false; + int num_nodes; + int tsdata_frame_size; + int loopcnt; + int ret; + u16 *test_result = NULL; + bool recovery_display_state = false; + u8 need_display_on; + ktime_t start_time, end_time, delta_time; + u8 old_int_data_method = cts_dev->fwdata.int_data_method; + u16 old_int_data_types = cts_dev->fwdata.int_data_types; + + if (cts_dev == NULL || param == NULL) { + cts_err + ("Short test with invalid param: cts_dev: %p test param: %p", + cts_dev, param); + return -EINVAL; + } + + num_nodes = cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; + tsdata_frame_size = 2 * num_nodes; + + driver_validate_data = !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); + validate_data_per_node = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + dump_test_data_to_user = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_console = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); + dump_test_data_to_file = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + stop_if_failed = + !!(param->flags & CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED); + + cts_info("Short test, flags: 0x%08x," + "num invalid node: %u, " + "test data file: '%s' buf size: %d, " + "drive log file: '%s' buf size: %d", + param->flags, param->num_invalid_node, + param->test_data_filepath, param->test_data_buf_size, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + if (dump_test_data_to_user) { + test_result = (u16 *) param->test_data_buf; + } else { + test_result = (u16 *) kmalloc(tsdata_frame_size, GFP_KERNEL); + if (test_result == NULL) { + cts_err("Allocate test result buffer failed"); + ret = -ENOMEM; + goto show_test_result; + } + } + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto err_free_test_result; + } + + cts_lock_device(cts_dev); + + ret = prepare_test(cts_dev); + if (ret) { + cts_err("Prepare test failed %d", ret); + goto unlock_device; + } + ret = cts_dev->ops->is_display_on(cts_dev, &need_display_on); + if (ret) { + cts_err("Read need display on register failed %d", ret); + goto err_free_test_result; + } + + if (need_display_on == 0) { + ret = cts_dev->ops->set_display_on(cts_dev, 0x00); + if (ret) { + cts_err("Set display state to SLEEP failed %d", ret); + goto err_free_test_result; + } + recovery_display_state = true; + } + + cts_set_int_data_types(cts_dev, INT_DATA_TYPE_RAWDATA); + cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); + + cts_info("Test short to GND"); + ret = + cts_dev->ops->set_short_test_type(cts_dev, + CTS_SHORT_TEST_BETWEEN_GND); + if (ret) { + cts_err("Set short test type to SHORT_TO_GND failed %d", ret); + goto recovery_display_state; + } + ret = cts_dev->ops->set_openshort_mode(cts_dev, CTS_TEST_SHORT); + if (ret) { + cts_err("Set test type to SHORT failed %d", ret); + goto recovery_display_state; + } + ret = + cts_dev->ops->set_workmode(cts_dev, + CTS_FIRMWARE_WORK_MODE_OPEN_SHORT); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_TEST failed %d", + ret); + goto recovery_display_state; + } + ret = wait_test_complete(cts_dev, 0); + if (ret) { + cts_err("Wait test complete failed %d", ret); + goto recovery_display_state; + } + ret = + cts_polling_test_data(cts_dev, (u8 *) test_result, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret) { + cts_err("Read test result failed %d", ret); + goto recovery_display_state; + } + if (dump_test_data_to_user) + *param->test_data_wr_size += tsdata_frame_size; + + if (dump_test_data_to_file) { + int r = + cts_start_dump_test_data_to_file(param->test_data_filepath, + !!(param->flags & + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); + + if (r) + cts_err("Start dump test data to file failed %d", r); + + } + if (dump_test_data_to_console || dump_test_data_to_file) + cts_dump_tsdata(cts_dev, "GND-short", test_result, + dump_test_data_to_console); + + if (driver_validate_data) { + ret = validate_tsdata(cts_dev, "GND-short", + test_result, param->invalid_nodes, + param->num_invalid_node, + validate_data_per_node, param->min, + param->max); + if (ret) { + cts_err("Short to GND test failed %d", ret); + if (stop_if_failed) + goto stop_dump_test_data_to_file; + } + } + if (dump_test_data_to_user) + test_result += num_nodes; + + /* + * Short between colums + */ + cts_info("Test short between columns"); + ret = + cts_dev->ops->set_short_test_type(cts_dev, + CTS_SHORT_TEST_BETWEEN_COLS); + if (ret) { + cts_err("Set short test type to BETWEEN_COLS failed %d", ret); + goto recovery_display_state; + } + ret = cts_dev->ops->set_openshort_mode(cts_dev, CTS_TEST_SHORT); + if (ret) { + cts_err("Set test type to SHORT failed %d", ret); + goto recovery_display_state; + } + ret = + cts_dev->ops->set_workmode(cts_dev, + CTS_FIRMWARE_WORK_MODE_OPEN_SHORT); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_TEST failed %d", + ret); + goto recovery_display_state; + } + + for (loopcnt = 0; loopcnt < SHORT_COLS_TEST_LOOP; loopcnt++) { + ret = wait_test_complete(cts_dev, 0); + if (ret) { + cts_err("Wait test complete failed %d", ret); + goto recovery_display_state; + } + + ret = + cts_polling_test_data(cts_dev, test_result, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret) { + cts_err("Read test result failed %d", ret); + goto recovery_display_state; + } + + if (dump_test_data_to_user) + *param->test_data_wr_size += tsdata_frame_size; + + if (dump_test_data_to_console || dump_test_data_to_file) + cts_dump_tsdata(cts_dev, "Col-short", test_result, + dump_test_data_to_console); + + if (driver_validate_data) { + ret = validate_tsdata(cts_dev, "Col-short", + test_result, param->invalid_nodes, + param->num_invalid_node, + validate_data_per_node, + param->min, param->max); + if (ret) { + cts_err("Short between columns test failed %d", + ret); + if (stop_if_failed) + goto recovery_display_state; + } + } + if (dump_test_data_to_user) + test_result += num_nodes; + } + + /* + * Short between rows + */ + cts_info("Test short between rows"); + for (loopcnt = 0; loopcnt < SHORT_ROWS_TEST_LOOP; loopcnt++) { + ret = + cts_dev->ops->set_short_test_type(cts_dev, + CTS_SHORT_TEST_BETWEEN_ROWS); + if (ret) { + cts_err("Set short test type to BETWEEN_ROWS failed %d", + ret); + goto recovery_display_state; + } + ret = cts_dev->ops->set_openshort_mode(cts_dev, CTS_TEST_SHORT); + if (ret) { + cts_err("Set test type to SHORT failed %d", ret); + goto recovery_display_state; + } + ret = + cts_dev->ops->set_workmode(cts_dev, + CTS_FIRMWARE_WORK_MODE_OPEN_SHORT); + if (ret) { + cts_err + ("Set firmware work mode to WORK_MODE_TEST failed %d", + ret); + goto recovery_display_state; + } + ret = wait_test_complete(cts_dev, 0); + if (ret) { + cts_err("Wait test complete failed %d", ret); + goto recovery_display_state; + } + ret = + cts_polling_test_data(cts_dev, test_result, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret) { + cts_err("Read test result failed %d", ret); + goto recovery_display_state; + } + if (dump_test_data_to_user) + *param->test_data_wr_size += tsdata_frame_size; + if (dump_test_data_to_console || dump_test_data_to_file) + cts_dump_tsdata(cts_dev, "Row-short", test_result, + dump_test_data_to_console); + + if (driver_validate_data) { + ret = validate_tsdata(cts_dev, "Row-short", + test_result, param->invalid_nodes, + param->num_invalid_node, + validate_data_per_node, + param->min, param->max); + if (ret) { + cts_err("Short between rows test failed %d", + ret); + if (stop_if_failed) + goto recovery_display_state; + + } + } + if (dump_test_data_to_user) + test_result += num_nodes; + } + +recovery_display_state: + if (recovery_display_state) { + int r = set_display_state(cts_dev, true); + + if (r) + cts_err("Set display state to ACTIVE failed %d", r); + + } + +stop_dump_test_data_to_file: + if (dump_test_data_to_file) + cts_stop_dump_test_data_to_file(); + + post_test(cts_dev); + cts_set_int_data_method(cts_dev, old_int_data_method); + cts_set_int_data_types(cts_dev, old_int_data_types); + +unlock_device: + cts_unlock_device(cts_dev); + + cts_start_device(cts_dev); + +err_free_test_result: + if (!dump_test_data_to_user && test_result) + kfree(test_result); + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret > 0) + cts_info("Short test has %d nodes FAIL, ELAPSED TIME: %lldms", + ret, ktime_to_ms(delta_time)); + else if (ret < 0) + cts_info("Short test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + else + cts_info("Short test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + + return ret; +} + +static int validate_comp_cap(struct cts_device *cts_dev, + const char *desc, u8 *cap, + u32 *invalid_nodes, u32 num_invalid_nodes, + bool per_node, int *min, int *max) +{ +#define SPLIT_LINE_STR \ + "------------------------------" + + int r, c; + int failed_cnt = 0; + + cts_info + ("Validate %s data: %s, num invalid node: %u, thresh[0]=[%d, %d]", + desc, per_node ? "Per-Node" : "Uniform-Threshold", + num_invalid_nodes, min ? min[0] : INT_MIN, max ? max[0] : INT_MAX); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + int offset = r * cts_dev->hwdata->num_col + c; + + if (num_invalid_nodes && + is_invalid_node(invalid_nodes, num_invalid_nodes, r, + c)) { + continue; + } + + if ((min != NULL + && cap[offset] < min[per_node ? offset : 0]) + || (max != NULL + && cap[offset] > max[per_node ? offset : 0])) { + if (failed_cnt == 0) { + cts_info(SPLIT_LINE_STR); + cts_info("%s failed nodes:", desc); + } + failed_cnt++; + + cts_info(" %3d: [%-2d][%-2d] = %u", + failed_cnt, r, c, cap[offset]); + } + } + } + + if (failed_cnt) { + cts_info(SPLIT_LINE_STR); + cts_info("%s test %d node total failed", desc, failed_cnt); + } + + return failed_cnt; + +#undef SPLIT_LINE_STR +} + +void cts_dump_comp_cap(struct cts_device *cts_dev, u8 *cap, bool to_console) +{ +#define SPLIT_LINE_STR \ + "-----------------------------------------------------------------------------" +#define ROW_NUM_FORMAT_STR "%2d | " +#define COL_NUM_FORMAT_STR "%3u " +#define DATA_FORMAT_STR "%4d" + + int r, c; + u32 max, min, sum, average; + int max_r, max_c, min_r, min_c; + char line_buf[128]; + int count; + + max = min = cap[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + u16 val = cap[r * cts_dev->hwdata->num_col + c]; + + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count = 0; + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + " Compensate Cap MIN: [%u][%u]=%u, MAX: [%u][%u]=%u, AVG=%u", + min_r, min_c, min, max_r, max_c, max, average); + if (to_console) { + cts_info(SPLIT_LINE_STR); + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, + strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, + strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } + + count = 0; + count += + scnprintf(line_buf + count, sizeof(line_buf) - count, " "); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + COL_NUM_FORMAT_STR, c); + } + if (to_console) { + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, + strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count = 0; + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += scnprintf(line_buf + count, + sizeof(line_buf) - count, + DATA_FORMAT_STR, + cap[r * cts_dev->hwdata->num_col + + c]); + } + if (to_console) + cts_info("%s", line_buf); + + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + } + } + + if (to_console) + cts_info(SPLIT_LINE_STR); + + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, + strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } +#undef SPLIT_LINE_STR +#undef ROW_NUM_FORMAT_STR +#undef COL_NUM_FORMAT_STR +#undef DATA_FORMAT_STR +} + +int cts_test_compensate_cap(struct cts_device *cts_dev, + struct cts_test_param *param) +{ + bool driver_validate_data = false; + bool validate_data_per_node = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_file = false; + int num_nodes; + u8 *cap = NULL; + int ret = 0; + ktime_t start_time, end_time, delta_time; + + if (cts_dev == NULL || param == NULL) { + cts_err + ("Compensate cap test with invalid param: cts_dev: %p test param: %p", + cts_dev, param); + return -EINVAL; + } + + num_nodes = cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; + + driver_validate_data = !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); + if (driver_validate_data) { + validate_data_per_node = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + } + dump_test_data_to_user = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_console = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); + dump_test_data_to_file = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + + cts_info("Compensate cap test, flags: 0x%08x " + "num invalid node: %u, " + "test data file: '%s' buf size: %d, " + "drive log file: '%s' buf size: %d", + param->flags, param->num_invalid_node, + param->test_data_filepath, param->test_data_buf_size, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + if (dump_test_data_to_user) { + cap = (u8 *) param->test_data_buf; + } else { + cap = (u8 *) kzalloc(num_nodes, GFP_KERNEL); + if (cap == NULL) { + cts_err("Alloc mem for compensate cap failed"); + ret = -ENOMEM; + goto show_test_result; + } + } + + /* Stop device to avoid un-wanted interrrupt */ + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto free_mem; + } + + cts_lock_device(cts_dev); + ret = cts_dev->ops->top_get_cnegdata(cts_dev, cap, num_nodes); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("Get compensate cap failed %d", ret); + goto start_device; + } + + if (dump_test_data_to_user) + *param->test_data_wr_size = num_nodes; + + if (dump_test_data_to_file) { + int r = + cts_start_dump_test_data_to_file(param->test_data_filepath, + !!(param->flags & + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); + + if (r) + cts_err("Start dump test data to file failed %d", r); + } + + if (dump_test_data_to_console || dump_test_data_to_file) + cts_dump_comp_cap(cts_dev, cap, dump_test_data_to_console); + + if (dump_test_data_to_file) + cts_stop_dump_test_data_to_file(); + + if (driver_validate_data) + ret = validate_comp_cap(cts_dev, "Compensate-Cap", + cap, param->invalid_nodes, + param->num_invalid_node, + validate_data_per_node, param->min, + param->max); + +start_device: + { + int r = cts_start_device(cts_dev); + + if (r) + cts_err("Start device failed %d", r); + } + +free_mem: + if (!dump_test_data_to_user && cap) + kfree(cap); + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret > 0) + cts_info + ("Compensate-Cap test has %d nodes FAIL, ELAPSED TIME: %lldms", + ret, ktime_to_ms(delta_time)); + else if (ret < 0) + cts_info + ("Compensate-Cap test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + else + cts_info("Compensate-Cap test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + + return ret; +} diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_test.h b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_test.h new file mode 100644 index 000000000000..0b553d08e4bb --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_test.h @@ -0,0 +1,104 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef CTS_TEST_H +#define CTS_TEST_H + +struct cts_device; + +#define CTS_TEST_FLAG_RESET_BEFORE_TEST (1u << 0) +#define CTS_TEST_FLAG_RESET_AFTER_TEST (1u << 1) +#define CTS_TEST_FLAG_DISPLAY_ON (1u << 2) +#define CTS_TEST_FLAG_DISABLE_GAS (1u << 3) +#define CTS_TEST_FLAG_DISABLE_LINESHIFT (1u << 4) + +#define CTS_TEST_FLAG_VALIDATE_DATA (1u << 8) +#define CTS_TEST_FLAG_VALIDATE_PER_NODE (1u << 9) +#define CTS_TEST_FLAG_VALIDATE_MIN (1u << 10) +#define CTS_TEST_FLAG_VALIDATE_MAX (1u << 11) +#define CTS_TEST_FLAG_VALIDATE_SKIP_INVALID_NODE (1u << 12) +#define CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED (1u << 13) + +#define CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE (1u << 16) +#define CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE (1u << 17) +#define CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE (1u << 18) +#define CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND (1u << 19) +#define CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_CSV (1u << 20) + +#define CTS_TEST_FLAG_DRIVER_LOG_TO_USERSPACE (1u << 24) +#define CTS_TEST_FLAG_DRIVER_LOG_TO_FILE (1u << 25) +#define CTS_TEST_FLAG_DRIVER_LOG_TO_FILE_APPEND (1u << 26) + +#define MAKE_INVALID_NODE(r, c) (((c) << 16) | (r)) +#define INVALID_NODE_ROW(node) ((u16)(node)) +#define INVALID_NODE_COL(node) ((u16)((node) >> 16)) + +enum cts_test_item { + CTS_TEST_RESET_PIN = 1, + CTS_TEST_INT_PIN, + CTS_TEST_RAWDATA, + CTS_TEST_NOISE, + CTS_TEST_OPEN, + CTS_TEST_SHORT, + CTS_TEST_COMPENSATE_CAP, +}; + +struct cts_test_param { + int test_item; + + __u32 flags; + + __u32 num_invalid_node; + __u32 *invalid_nodes; + int *min; + int *max; + + int *test_result; + + void *test_data_buf; + int test_data_buf_size; + int *test_data_wr_size; + const char *test_data_filepath; + + int driver_log_level; + char *driver_log_buf; + int driver_log_buf_size; + int *driver_log_wr_size; + const char *driver_log_filepath; + + void *priv_param; + int priv_param_size; +}; + +struct cts_rawdata_test_priv_param { + __u32 frames; + //__u8 work_mode; +}; + +struct cts_noise_test_priv_param { + __u32 frames; + //__u8 work_mode; +}; + +extern const char *cts_test_item_str(int test_item); +extern int cts_write_file(struct file *filp, const void *data, size_t size); +extern int cts_mkdir_for_file(const char *filepath, umode_t mode); + +extern int cts_start_dump_test_data_to_file(const char *filepath, + bool append_to_file); +extern void cts_stop_dump_test_data_to_file(void); + +extern int cts_test_reset_pin(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_int_pin(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_rawdata(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_noise(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_open(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_short(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_compensate_cap(struct cts_device *cts_dev, + struct cts_test_param *param); + +#endif /* CTS_TEST_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tool.c b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tool.c new file mode 100644 index 000000000000..7d9f0d684a9c --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_fhd_mmi/cts_tool.c @@ -0,0 +1,1272 @@ +// SPDX-License-Identifier: GPL-2.0 +#define LOG_TAG "Tool" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_firmware.h" +#include "cts_test.h" + +#ifdef CONFIG_CTS_LEGACY_TOOL + +#define CTS_TOOL_IOCTL_TCS_RW_BUF_MAX_SIZ 1024 +#define CTS_TOOL_IOCTL_TCS_RW_OPFLAG_READ 1 +#define CTS_TOOL_IOCTL_TCS_RW_OPFLAG_WRITE 2 + +#pragma pack(1) +/** Tool command structure */ +struct cts_tool_cmd { + u8 cmd; + u8 flag; + u8 circle; + u8 times; + u8 retry; + u32 data_len; + u8 addr_len; + u8 addr[4]; + u8 tcs[2]; + u8 data[PAGE_SIZE]; + +}; +struct cts_ioctl_tcs_rw_data { + u32 opflags; + u32 txlen; + u32 rxlen; + u8 __user *txbuf; + u8 __user *rxbuf; +}; +#pragma pack() + +#define CTS_TOOL_CMD_HEADER_LENGTH (16) + +enum cts_tool_cmd_code { + CTS_TOOL_CMD_GET_PANEL_PARAM = 0, + CTS_TOOL_CMD_GET_DOWNLOAD_STATUS = 2, + CTS_TOOL_CMD_GET_RAW_DATA = 4, + CTS_TOOL_CMD_GET_DIFF_DATA = 6, + CTS_TOOL_CMD_READ_HOSTCOMM = 12, + CTS_TOOL_CMD_READ_ADC_STATUS = 14, + CTS_TOOL_CMD_READ_GESTURE_INFO = 16, + CTS_TOOL_CMD_READ_HOSTCOMM_MULTIBYTE = 18, + CTS_TOOL_CMD_READ_PROGRAM_MODE_MULTIBYTE = 20, + CTS_TOOL_CMD_READ_ICTYPE = 22, + CTS_TOOL_CMD_I2C_DIRECT_READ = 24, + CTS_TOOL_CMD_GET_DRIVER_INFO = 26, + CTS_TOOL_CMD_TCS_READ_HW_CMD = 28, + CTS_TOOL_CMD_TCS_READ_DDI_CMD = 30, + CTS_TOOL_CMD_TCS_READ_FW_CMD = 32, + CTS_TOOL_CMD_GET_BASE_DATA = 34, + CTS_TOOL_CMD_GET_INT_DATAS = 36, + + CTS_TOOL_CMD_UPDATE_PANEL_PARAM_IN_SRAM = 1, + CTS_TOOL_CMD_DOWNLOAD_FIRMWARE_WITH_FILENAME = 3, + CTS_TOOL_CMD_DOWNLOAD_FIRMWARE = 5, + CTS_TOOL_CMD_WRITE_HOSTCOMM = 11, + CTS_TOOL_CMD_WRITE_HOSTCOMM_MULTIBYTE = 15, + CTS_TOOL_CMD_WRITE_PROGRAM_MODE_MULTIBYTE = 17, + CTS_TOOL_CMD_I2C_DIRECT_WRITE = 19, + CTS_TOOL_CMD_TCS_WRITE_HW_CMD = 21, + CTS_TOOL_CMD_TCS_WRITE_DDI_CMD = 23, + CTS_TOOL_CMD_TCS_WRITE_FW_CMD = 25, + CTS_TOOL_CMD_TCS_ENABLE_GET_RAWDATA_CMD = 27, + CTS_TOOL_CMD_TCS_DISABLE_GET_RAWDATA_CMD = 29, + CTS_TOOL_CMD_SET_INT_DATA_TYPE_AND_METHOD = 31, + +}; + +struct cts_test_ioctl_data { + __u32 ntests; + struct cts_test_param __user *tests; +}; + + +#define CTS_TOOL_IOCTL_GET_DRIVER_VERSION _IOR('C', 0x00, u32 *) +#define CTS_TOOL_IOCTL_GET_DEVICE_TYPE _IOR('C', 0x01, u32 *) +#define CTS_TOOL_IOCTL_GET_FW_VERSION _IOR('C', 0x02, u16 *) +#define CTS_TOOL_IOCTL_GET_RESOLUTION _IOR('C', 0x03, u32 *) /* X in LSW, Y in MSW */ +#define CTS_TOOL_IOCTL_GET_ROW_COL _IOR('C', 0x04, u32 *) /* row in LSW, col in MSW */ +#define CTS_TOOL_IOCTL_GET_MODULE_ID _IOWR('C', 0x05, u32 *) + +#define CTS_TOOL_IOCTL_TEST _IOWR('C', 0x10, struct cts_test_ioctl_data *) +#define CTS_TOOL_IOCTL_RDWR_REG _IOWR('C', 0x20, struct cts_rdwr_reg_ioctl_data *) +#define CTS_TOOL_IOCTL_UPGRADE_FW _IOWR('C', 0x21, struct cts_upgrade_fw_ioctl_data *) + +#define CTS_TOOL_IOCTL_TCS_RW _IOWR('C', 0x30, struct cts_ioctl_tcs_rw_data *) + +#define CTS_DRIVER_VERSION_CODE ((CFG_CTS_DRIVER_MAJOR_VERSION << 16) | \ + (CFG_CTS_DRIVER_MINOR_VERSION << 8) | \ + (CFG_CTS_DRIVER_PATCH_VERSION << 0)) + +static struct cts_tool_cmd cts_tool_cmd; +static char cts_tool_firmware_filepath[PATH_MAX]; +/* If CFG_CTS_MAX_I2C_XFER_SIZE < 58(PC tool length), this is neccessary */ +#ifdef CONFIG_CTS_I2C_HOST +/*static u32 cts_tool_direct_access_addr = 0;*/ +#endif /* CONFIG_CTS_I2C_HOST */ + +static int cts_tool_open(struct inode *inode, struct file *file) +{ + file->private_data = PDE_DATA(inode); + return 0; +} + +static ssize_t cts_tool_read(struct file *file, + char __user *buffer, size_t count, loff_t *ppos) +{ +#define RAWDATA_BUFFER_SIZE(cts_dev) \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * 2) + struct chipone_ts_data *cts_data; + struct cts_tool_cmd *cmd; + struct cts_device *cts_dev; + int ret = 0; + + cts_data = (struct chipone_ts_data *)file->private_data; + if (cts_data == NULL) { + cts_err("Read with private_data = NULL"); + return -EIO; + } + + cmd = &cts_tool_cmd; + cts_dev = &cts_data->cts_dev; + cts_lock_device(cts_dev); + + cts_info("read: flag:%d, addr:0x%06x, tcm:0x%04x, datalen:%d", + cmd->cmd, get_unaligned_le32(cmd->addr), + get_unaligned_le16(cmd->tcs), cmd->data_len); + + switch (cmd->cmd) { + case CTS_TOOL_CMD_TCS_READ_HW_CMD: + cts_dev->ops->read_hw_reg(cts_dev, + get_unaligned_le32(cmd->addr), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_TCS_READ_DDI_CMD: + cts_dev->ops->read_ddi_reg(cts_dev, + get_unaligned_le32(cmd->addr), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_TCS_READ_FW_CMD: + cts_dev->ops->read_reg(cts_dev, get_unaligned_le16(cmd->tcs), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_GET_PANEL_PARAM: + cts_info("Get panel param len: %u", cmd->data_len); + ret = cts_get_panel_param(cts_dev, cmd->data, cmd->data_len); + if (ret) { + cts_err("Get panel param len: %u failed %d", + cmd->data_len, ret); + } + break; + + case CTS_TOOL_CMD_GET_DOWNLOAD_STATUS: + cmd->data[0] = 100; + cts_info("Get update status = %hhu", cmd->data[0]); + break; + + case CTS_TOOL_CMD_GET_RAW_DATA: + case CTS_TOOL_CMD_GET_DIFF_DATA: + case CTS_TOOL_CMD_GET_BASE_DATA: + cts_info("Get %s data row: %u col: %u len: %u", + cmd->cmd == CTS_TOOL_CMD_GET_RAW_DATA ? "raw" : "diff", + cmd->addr[1], cmd->addr[0], cmd->data_len); + + if (cmd->cmd == CTS_TOOL_CMD_GET_RAW_DATA) { + // ret = cts_get_rawdata(cts_dev, cmd->data); + ret = + cts_dev->ops->top_get_rawdata(cts_dev, + (u8 *) cmd->data, + RAWDATA_BUFFER_SIZE + (cts_dev)); + } else if (cmd->cmd == CTS_TOOL_CMD_GET_DIFF_DATA) { + // ret = cts_get_diffdata(cts_dev, cmd->data); + ret = + cts_dev->ops->top_get_manual_diff(cts_dev, + (u8 *) cmd->data, + RAWDATA_BUFFER_SIZE + (cts_dev)); + } else if (cmd->cmd == CTS_TOOL_CMD_GET_BASE_DATA) { + // ret = cts_get_basedata(cts_dev, cmd->data); + ret = + cts_dev->ops->top_get_basedata(cts_dev, + (u8 *) cmd->data, + RAWDATA_BUFFER_SIZE + (cts_dev)); + } + if (ret) { + cts_err("Get %s data failed %d", + cmd->cmd == CTS_TOOL_CMD_GET_RAW_DATA ? "raw" : + cmd->cmd == + CTS_TOOL_CMD_GET_DIFF_DATA ? "diff" : "base", + ret); + } + break; + case CTS_TOOL_CMD_GET_INT_DATAS: + memcpy((u8 *)cmd->data, cts_dev->rtdata.int_data, cts_dev->fwdata.int_data_size); + cmd->data_len = cts_dev->fwdata.int_data_size; + break; + case CTS_TOOL_CMD_READ_HOSTCOMM: + ret = cts_fw_reg_readb(cts_dev, get_unaligned_le16(cmd->addr), + cmd->data); + if (ret) { + cts_err("Read firmware reg addr 0x%04x failed %d", + get_unaligned_le16(cmd->addr), ret); + } else { + cts_dbg("Read firmware reg addr 0x%04x, val=0x%02x", + get_unaligned_le16(cmd->addr), cmd->data[0]); + } + break; + +#ifdef CFG_CTS_GESTURE + case CTS_TOOL_CMD_READ_GESTURE_INFO: + ret = cts_get_gesture_info(cts_dev, cmd->data); + if (ret) + cts_err("Get gesture info failed %d", ret); + break; +#endif /* CFG_CTS_GESTURE */ + + case CTS_TOOL_CMD_READ_HOSTCOMM_MULTIBYTE: + cmd->data_len = min((size_t)cmd->data_len, sizeof(cmd->data)); + ret = cts_fw_reg_readsb(cts_dev, get_unaligned_le16(cmd->addr), + cmd->data, cmd->data_len); + if (ret) { + cts_err + ("Read firmware reg addr 0x%04x len %u failed %d", + get_unaligned_le16(cmd->addr), cmd->data_len, ret); + } else { + cts_dbg("Read firmware reg addr 0x%04x len %u", + get_unaligned_le16(cmd->addr), cmd->data_len); + } + break; + + case CTS_TOOL_CMD_READ_PROGRAM_MODE_MULTIBYTE: + cts_dbg("Read under program mode addr 0x%06x len %u", + (cmd->flag << 16) | get_unaligned_le16(cmd->addr), + cmd->data_len); + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + break; + } + + ret = cts_sram_readsb(&cts_data->cts_dev, + get_unaligned_le16(cmd->addr), cmd->data, + cmd->data_len); + if (ret) { + cts_err("Read under program mode I2C xfer failed %d", + ret); + } + + ret = cts_enter_normal_mode(cts_dev); + if (ret) { + cts_err("Enter normal mode failed %d", ret); + break; + } + break; + + case CTS_TOOL_CMD_READ_ICTYPE: + cts_info("Get IC type"); + if (cts_dev->hwdata) { + switch (cts_dev->hwdata->hwid) { + case CTS_DEV_HWID_ICNL9911: + cmd->data[0] = 0x91; + break; + case CTS_DEV_HWID_ICNL9911S: + cmd->data[0] = 0x91; + break; + case CTS_DEV_HWID_ICNL9911C: + cmd->data[0] = 0x91; + break; + default: + cmd->data[0] = 0x00; + break; + } + } else { + cmd->data[0] = 0x10; + } + break; + +#ifdef CONFIG_CTS_I2C_HOST + case CTS_TOOL_CMD_I2C_DIRECT_READ: + { + u32 addr_width; + char *wr_buff = NULL; + u8 addr_buff[4]; + size_t left_size, max_xfer_size; + u8 *data; + + if (cmd->addr[0] != CTS_DEV_PROGRAM_MODE_I2CADDR) { + cmd->addr[0] = CTS_DEV_NORMAL_MODE_I2CADDR; + addr_width = 2; + } else { + addr_width = + cts_dev->hwdata->program_addr_width; + } + + cts_dbg + ("Direct read from i2c_addr 0x%02x addr 0x%06x size %u", + cmd->addr[0], cts_tool_direct_access_addr, + cmd->data_len); + + left_size = cmd->data_len; + max_xfer_size = + cts_plat_get_max_i2c_xfer_size(cts_dev->pdata); + data = cmd->data; + while (left_size) { + size_t xfer_size = + min(left_size, max_xfer_size); + ret = + cts_plat_i2c_read(cts_data->pdata, + cmd->addr[0], wr_buff, + addr_width, data, + xfer_size, 1, 0); + if (ret) { + cts_err + ("Direct read i2c_addr 0x%02x addr 0x%06x " + "len %zu failed %d", cmd->addr[0], + cts_tool_direct_access_addr, + xfer_size, ret); + break; + } + + left_size -= xfer_size; + if (left_size) { + data += xfer_size; + cts_tool_direct_access_addr += + xfer_size; + if (addr_width == 2) { + put_unaligned_be16 + (cts_tool_direct_access_addr, + addr_buff); + } else if (addr_width == 3) { + put_unaligned_be24 + (cts_tool_direct_access_addr, + addr_buff); + } + wr_buff = addr_buff; + } + } + } + break; +#endif + case CTS_TOOL_CMD_GET_DRIVER_INFO: + break; + + default: + cts_warn("Read unknown command %u", cmd->cmd); + ret = -EINVAL; + break; + } + + cts_unlock_device(cts_dev); + + if (ret == 0) { + if (cmd->cmd == CTS_TOOL_CMD_I2C_DIRECT_READ) { + ret = copy_to_user(buffer + CTS_TOOL_CMD_HEADER_LENGTH, + cmd->data, cmd->data_len); + } else { + ret = copy_to_user(buffer, cmd->data, cmd->data_len); + } + if (ret) { + cts_err("Copy data to user buffer failed %d", ret); + return 0; + } + + return cmd->data_len; + } + + return 0; +} + +static ssize_t cts_tool_write(struct file *file, + const char __user *buffer, size_t count, + loff_t *ppos) +{ + struct chipone_ts_data *cts_data; + struct cts_device *cts_dev; + struct cts_tool_cmd *cmd; + int ret = 0; + + if (count < CTS_TOOL_CMD_HEADER_LENGTH || count > PAGE_SIZE) { + cts_err("Write len %zu invalid", count); + return -EFAULT; + } + + cts_data = (struct chipone_ts_data *)file->private_data; + if (cts_data == NULL) { + cts_err("Write with private_data = NULL"); + return -EIO; + } + + cmd = &cts_tool_cmd; + ret = copy_from_user(cmd, buffer, CTS_TOOL_CMD_HEADER_LENGTH); + cts_info + ("write: flag:%d, addr:0x%06x, tcmd:0x%04x data:0x%02x, datalen:%d", + cmd->cmd, get_unaligned_le32(cmd->addr), + get_unaligned_le16(cmd->tcs), cmd->data[0], cmd->data_len); + + if (ret) { + cts_err("Copy command header from user buffer failed %d", ret); + return -EIO; + } else + ret = CTS_TOOL_CMD_HEADER_LENGTH; + + if (cmd->data_len > PAGE_SIZE) { + cts_err("Write with invalid count %d", cmd->data_len); + return -EIO; + } + + if (cmd->cmd & BIT(0)) { + if (cmd->data_len) { + ret = + copy_from_user(cmd->data, + buffer + CTS_TOOL_CMD_HEADER_LENGTH, + cmd->data_len); + if (ret) { + cts_err + ("Copy command payload from user buffer len %u failed %d", + cmd->data_len, ret); + return -EIO; + } + } + } else { + /* Prepare for read command */ + cts_dbg("Write read command(%d) header, prepare read size: %d", + cmd->cmd, cmd->data_len); + return CTS_TOOL_CMD_HEADER_LENGTH + cmd->data_len; + } + + cts_dev = &cts_data->cts_dev; + cts_lock_device(cts_dev); + + switch (cmd->cmd) { + case CTS_TOOL_CMD_TCS_WRITE_HW_CMD: + cts_dev->ops->write_hw_reg(cts_dev, + get_unaligned_le32(cmd->addr), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_TCS_WRITE_DDI_CMD: + cts_dev->ops->write_ddi_reg(cts_dev, + get_unaligned_le32(cmd->addr), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_TCS_WRITE_FW_CMD: + cts_dev->ops->write_reg(cts_dev, get_unaligned_le16(cmd->tcs), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_TCS_ENABLE_GET_RAWDATA_CMD: + cts_info("Enable touch data"); + ret = cts_dev->ops->enable_get_rawdata(cts_dev); + if (ret) + cts_err("Enable get touch data failed"); + break; + case CTS_TOOL_CMD_TCS_DISABLE_GET_RAWDATA_CMD: + cts_info("Disable touch data"); + ret = cts_dev->ops->disable_get_rawdata(cts_dev); + if (ret) + cts_err("Disable get touch data failed"); + break; + case CTS_TOOL_CMD_SET_INT_DATA_TYPE_AND_METHOD: + cts_set_int_data_types(cts_dev, cmd->data[0]); + cts_set_int_data_method(cts_dev, cmd->data[1]); + break; + case CTS_TOOL_CMD_UPDATE_PANEL_PARAM_IN_SRAM: + cts_info("Write panel param len %u data\n", cmd->data_len); + ret = cts_fw_reg_writesb(cts_dev, CTS_DEVICE_FW_REG_PANEL_PARAM, + cmd->data, cmd->data_len); + if (ret) { + cts_err("Write panel param failed %d", ret); + break; + } + + ret = cts_send_command(cts_dev, CTS_CMD_RESET); + + ret = cts_set_work_mode(cts_dev, 1); + + mdelay(100); + + ret = cts_set_work_mode(cts_dev, 0); + + mdelay(100); + + break; + + case CTS_TOOL_CMD_DOWNLOAD_FIRMWARE_WITH_FILENAME: + cts_info("Write firmware path: '%.*s'", + cmd->data_len, cmd->data); + + memcpy(cts_tool_firmware_filepath, cmd->data, cmd->data_len); + cts_tool_firmware_filepath[cmd->data_len] = '\0'; + break; + + case CTS_TOOL_CMD_DOWNLOAD_FIRMWARE: + cts_info("Start download firmware path: '%s'", + cts_tool_firmware_filepath); + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + break; + } +/** + *TODO: Use async mode such as thread, otherwise, + *host can not get update status. + */ + ret = cts_update_firmware_from_file(cts_dev, + cts_tool_firmware_filepath, + true); + if (ret) + cts_err("Updata firmware failed %d", ret); + + ret = cts_start_device(cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + break; + } + break; + + case CTS_TOOL_CMD_WRITE_HOSTCOMM: + cts_dbg("Write firmware reg addr: 0x%04x val=0x%02x", + get_unaligned_le16(cmd->addr), cmd->data[0]); + + ret = cts_fw_reg_writeb(cts_dev, get_unaligned_le16(cmd->addr), + cmd->data[0]); + if (ret) { + cts_err + ("Write firmware reg addr: 0x%04x val=0x%02x failed %d", + get_unaligned_le16(cmd->addr), cmd->data[0], ret); + } + break; + + case CTS_TOOL_CMD_WRITE_HOSTCOMM_MULTIBYTE: + cts_dbg("Write firmare reg addr: 0x%04x len %u", + get_unaligned_le16(cmd->addr), cmd->data_len); + ret = cts_fw_reg_writesb(cts_dev, get_unaligned_le16(cmd->addr), + cmd->data, cmd->data_len); + if (ret) { + cts_err + ("Write firmare reg addr: 0x%04x len %u failed %d", + get_unaligned_le16(cmd->addr), cmd->data_len, ret); + } + break; + + case CTS_TOOL_CMD_WRITE_PROGRAM_MODE_MULTIBYTE: + cts_dbg("Write to addr 0x%06x size %u under program mode", + (cmd->flag << 16) | (cmd->addr[1] << 8) | cmd->addr[0], + cmd->data_len); + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + break; + } + + ret = cts_sram_writesb(cts_dev, + (cmd->flag << 16) | (cmd->addr[1] << 8) | + cmd->addr[0], cmd->data, cmd->data_len); + if (ret) + cts_err("Write program mode multibyte failed %d", ret); + + ret = cts_enter_normal_mode(cts_dev); + if (ret) { + cts_err("Enter normal mode failed %d", ret); + break; + } + + break; + +#ifdef CONFIG_CTS_I2C_HOST + case CTS_TOOL_CMD_I2C_DIRECT_WRITE: + { + u32 addr_width; + size_t left_payload_size; /* Payload exclude address field */ + size_t max_xfer_size; + char *payload; + + if (cmd->addr[0] != CTS_DEV_PROGRAM_MODE_I2CADDR) { + cmd->addr[0] = CTS_DEV_NORMAL_MODE_I2CADDR; + addr_width = 2; + cts_tool_direct_access_addr = + get_unaligned_be16(cmd->data); + } else { + addr_width = + cts_dev->hwdata->program_addr_width; + cts_tool_direct_access_addr = + get_unaligned_be24(cmd->data); + } + + if (cmd->data_len < addr_width) { + cts_err + ("Direct write too short %d < address width %d", + cmd->data_len, addr_width); + ret = -EINVAL; + break; + } + + cts_dbg + ("Direct write to i2c_addr 0x%02x addr 0x%06x size %u", + cmd->addr[0], cts_tool_direct_access_addr, + cmd->data_len); + + left_payload_size = cmd->data_len - addr_width; + max_xfer_size = + cts_plat_get_max_i2c_xfer_size(cts_dev->pdata); + payload = cmd->data + addr_width; + do { + size_t xfer_payload_size = + min(left_payload_size, + max_xfer_size - addr_width); + size_t xfer_len = + xfer_payload_size + addr_width; + + ret = cts_plat_i2c_write(cts_data->pdata, + cmd->addr[0], + payload - addr_width, + xfer_len, 1, 0); + if (ret) { + cts_err + ("Direct write i2c_addr 0x%02x addr 0x%06x len %zu failed %d", + cmd->addr[0], + cts_tool_direct_access_addr, + xfer_len, ret); + break; + } + + left_payload_size -= xfer_payload_size; + if (left_payload_size) { + payload += xfer_payload_size; + cts_tool_direct_access_addr += + xfer_payload_size; + if (addr_width == 2) { + put_unaligned_be16 + (cts_tool_direct_access_addr, + payload - addr_width); + } else if (addr_width == 3) { + put_unaligned_be24 + (cts_tool_direct_access_addr, + payload - addr_width); + } + } + } while (left_payload_size); + } + break; +#endif + default: + cts_warn("Write unknown command %u", cmd->cmd); + ret = -EINVAL; + break; + } + + cts_unlock_device(cts_dev); + + return ret ? 0 : cmd->data_len + CTS_TOOL_CMD_HEADER_LENGTH; +} + + +static long cts_ioctl_tcs_rw(struct cts_device *cts_dev, + struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret; + struct cts_ioctl_tcs_rw_data rwdata; + u8 *txbuf = NULL; + u8 *rxbuf = NULL; + + if (copy_from_user(&rwdata, + (struct cts_ioctl_tcs_rw_data __user *)arg, + sizeof(rwdata))) { + cts_err("Copy ioctl tcs rw arg to kernel failed"); + return -EFAULT; + } + + if ((rwdata.opflags != CTS_TOOL_IOCTL_TCS_RW_OPFLAG_READ) && + (rwdata.opflags != CTS_TOOL_IOCTL_TCS_RW_OPFLAG_WRITE)) + { + cts_err("ioctl tcs rw with invalid opflags %u", rwdata.opflags); + return -EINVAL; + } + + if (rwdata.txlen > CTS_TOOL_IOCTL_TCS_RW_BUF_MAX_SIZ) { + cts_err("Send too long: %d", rwdata.txlen); + return -EINVAL; + } + + if (rwdata.rxlen > CTS_TOOL_IOCTL_TCS_RW_BUF_MAX_SIZ) { + cts_err("Recv too long: %d", rwdata.rxlen); + return -EINVAL; + } + + if (!rwdata.txbuf || !rwdata.rxbuf) { + cts_err("Txbuf of Rxbuf is NULL!"); + return -EINVAL; + } + + txbuf = memdup_user(rwdata.txbuf, rwdata.txlen); + if (IS_ERR(txbuf)) { + ret = PTR_ERR(txbuf); + cts_err("Memdup txbuf failed %d", ret); + txbuf = NULL; + goto free_buf; + } + + rxbuf = memdup_user(rwdata.rxbuf, rwdata.rxlen); + if (IS_ERR(rxbuf)) { + ret = PTR_ERR(rxbuf); + cts_err("Memdup rxbuf failed %d", ret); + rxbuf = NULL; + goto free_buf; + } + + cts_lock_device(cts_dev); + ret = cts_spi_xtrans(cts_dev, txbuf, rwdata.txlen, rxbuf, rwdata.rxlen); + cts_unlock_device(cts_dev); + if (ret < 0) { + cts_err("Trans failed %d", ret); + goto free_buf; + } + + ret = copy_to_user(rwdata.rxbuf, rxbuf, rwdata.rxlen); + if (ret < 0) { + cts_err("Copy to user failed %d", ret); + goto free_buf; + } + + ret = 0; + +free_buf: + if (txbuf) { + kfree(txbuf); + txbuf = NULL; + } + if (rxbuf) { + kfree(rxbuf); + rxbuf = NULL; + } + + return ret; +} + +static int cts_ioctl_test(struct cts_device *cts_dev, + u32 ntests, struct cts_test_param *tests) +{ + u32 num_nodes = 0; + int i, ret = 0; + + cts_info("ioctl test total %u items", ntests); + + num_nodes = cts_dev->fwdata.rows * cts_dev->fwdata.cols; + + for (i = 0; i < ntests; i++) { + bool validate_data = false; + bool validate_data_per_node = false; + bool validate_min = false; + bool validate_max = false; + bool skip_invalid_node = false; + bool stop_test_if_validate_fail = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_file = false; + bool dump_test_data_to_file_append = false; + bool driver_log_to_user = false; + bool driver_log_to_file = false; + bool driver_log_to_file_append = false; + u32 __user *user_min_threshold = NULL; + u32 __user *user_max_threshold = NULL; + u32 __user *user_invalid_nodes = NULL; + int __user *user_test_result = NULL; + void __user *user_test_data = NULL; + int __user *user_test_data_wr_size = NULL; + const char __user *user_test_data_filepath = NULL; + void __user *user_driver_log_buf = NULL; + int __user *user_driver_log_wr_size = NULL; + const char __user *user_driver_log_filepath = NULL; + void __user *user_priv_param = NULL; + int test_result = 0; + int test_data_wr_size = 0; + int driver_log_wr_size = 0; + + cts_info("ioctl test item %d: %d(%s) flags: %08x priv param size: %d", + i, tests[i].test_item, cts_test_item_str(tests[i].test_item), + tests[i].flags, tests[i].priv_param_size); + /* + * Validate arguement + */ + validate_data = + !!(tests[i].flags & CTS_TEST_FLAG_VALIDATE_DATA); + validate_data_per_node = + !!(tests[i].flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + validate_min = + !!(tests[i].flags & CTS_TEST_FLAG_VALIDATE_MIN); + validate_max = + !!(tests[i].flags & CTS_TEST_FLAG_VALIDATE_MAX); + skip_invalid_node = + !!(tests[i].flags & CTS_TEST_FLAG_VALIDATE_SKIP_INVALID_NODE); + stop_test_if_validate_fail = + !!(tests[i].flags & CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED); + dump_test_data_to_user = + !!(tests[i].flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_file = + !!(tests[i].flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + dump_test_data_to_file_append = + !!(tests[i].flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND); + driver_log_to_user = + !!(tests[i].flags & CTS_TEST_FLAG_DRIVER_LOG_TO_USERSPACE); + driver_log_to_file = + !!(tests[i].flags & CTS_TEST_FLAG_DRIVER_LOG_TO_FILE); + driver_log_to_file_append = + !!(tests[i].flags & CTS_TEST_FLAG_DRIVER_LOG_TO_FILE_APPEND); + + if (tests[i].test_result == NULL) { + cts_err("Result pointer = NULL"); + return -EFAULT; + } + + if (validate_data) { + cts_info(" - Flag: Validate data"); + + if (validate_data_per_node) + cts_info(" - Flag: Validate data per-node"); + + if (validate_min) + cts_info(" - Flag: Validate min threshold"); + + if (validate_max) + cts_info(" - Flag: Validate max threshold"); + + if (stop_test_if_validate_fail) + cts_info(" - Flag: Stop test if validate fail"); + + if (validate_min && tests[i].min == NULL) { + cts_err("Min threshold pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + if (validate_max && tests[i].max == NULL) { + cts_err("Max threshold pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + if (skip_invalid_node) { + cts_info(" - Flag: Skip invalid node"); + + if (tests[i].num_invalid_node == 0 || + tests[i].num_invalid_node >= num_nodes) { + cts_err("Num invalid node %u out of range[0, %u]", + tests[i].num_invalid_node, num_nodes); + ret = -EINVAL; + goto store_result; + } + + if (tests[i].invalid_nodes == NULL) { + cts_err("Invalid nodes pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + } + } + + if (dump_test_data_to_console) + cts_info(" - Flag: Dump test data to console"); + + if (dump_test_data_to_user) { + cts_info(" - Flag: Dump test data to user, size: %d", + tests[i].test_data_buf_size); + + if (tests[i].test_data_buf == NULL) { + cts_err("Test data pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + if (tests[i].test_data_wr_size == NULL) { + cts_err("Test data write size pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + if (tests[i].test_data_buf_size < num_nodes) { + cts_err("Test data size %d too small < %u", + tests[i].test_data_buf_size, num_nodes); + ret = -EINVAL; + goto store_result; + } + } + + if (dump_test_data_to_file) { + cts_info(" - Flag: Dump test data to file%s", + dump_test_data_to_file_append ? "[Append]" : ""); + + if (tests[i].test_data_filepath == NULL) { + cts_err("Test data filepath = NULL"); + ret = -EINVAL; + goto store_result; + } + } + + if (driver_log_to_user) { + cts_info(" - Flag: Dump driver log to user, size: %d", + tests[i].driver_log_buf_size); + + if (tests[i].driver_log_buf == NULL) { + cts_err("Driver log buf pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + if (tests[i].driver_log_wr_size == NULL) { + cts_err("Driver log write size pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + if (tests[i].driver_log_buf_size < 1024) { + cts_err("Driver log buf size %d too small < 1024", + tests[i].test_data_buf_size); + ret = -EINVAL; + goto store_result; + } + } + + if (driver_log_to_file) { + cts_info(" - Flag: Dump driver log to file %s", + driver_log_to_file_append ? "[Append]" : ""); + + if (tests[i].driver_log_filepath == NULL) { + cts_err("Driver log filepath = NULL"); + ret = -EINVAL; + goto store_result; + } + } + + /* + * Dump input parameter from user, + * Aallocate memory for output, + * Replace __user pointer with kernel pointer. + */ + user_test_result = (int __user *)tests[i].test_result; + tests[i].test_result = &test_result; + + if (validate_data) { + int num_threshold = validate_data_per_node ? num_nodes : 1; + int threshold_size = num_threshold * sizeof(tests[i].min[0]); + + if (validate_min) { + user_min_threshold = (int __user *)tests[i].min; + tests[i].min = memdup_user(user_min_threshold, threshold_size); + if (IS_ERR(tests[i].min)) { + ret = PTR_ERR(tests[i].min); + tests[i].min = NULL; + cts_err("Memdup min threshold from user failed %d", ret); + goto store_result; + } + } else { + tests[i].min = NULL; + } + if (validate_max) { + user_max_threshold = (int __user *)tests[i].max; + tests[i].max = memdup_user(user_max_threshold, threshold_size); + if (IS_ERR(tests[i].max)) { + ret = PTR_ERR(tests[i].max); + tests[i].max = NULL; + cts_err("Memdup max threshold from user failed %d", ret); + goto store_result; + } + } else { + tests[i].max = NULL; + } + if (skip_invalid_node) { + user_invalid_nodes = (u32 __user *)tests[i].invalid_nodes; + tests[i].invalid_nodes = memdup_user(user_invalid_nodes, + tests[i].num_invalid_node * sizeof(tests[i].invalid_nodes[0])); + if (IS_ERR(tests[i].invalid_nodes)) { + ret = PTR_ERR(tests[i].invalid_nodes); + tests[i].invalid_nodes = NULL; + cts_err("Memdup invalid node from user failed %d", ret); + goto store_result; + } + } + } + + if (dump_test_data_to_user) { + user_test_data = (void __user *)tests[i].test_data_buf; + tests[i].test_data_buf = vmalloc(tests[i].test_data_buf_size); + if (tests[i].test_data_buf == NULL) { + ret = -ENOMEM; + cts_err("Alloc test data mem failed"); + goto store_result; + } + user_test_data_wr_size = (int __user *)tests[i].test_data_wr_size; + tests[i].test_data_wr_size = &test_data_wr_size; + } + + if (dump_test_data_to_file) { + user_test_data_filepath = (const char __user *)tests[i].test_data_filepath; + tests[i].test_data_filepath = strndup_user(user_test_data_filepath, PATH_MAX); + if (tests[i].test_data_filepath == NULL) { + cts_err("Strdup test data filepath failed"); + goto store_result; + } + } + + if (driver_log_to_user) { + user_driver_log_buf = (void __user *)tests[i].driver_log_buf; + tests[i].driver_log_buf = kmalloc(tests[i].driver_log_buf_size, GFP_KERNEL); + if (tests[i].driver_log_buf == NULL) { + ret = -ENOMEM; + cts_err("Alloc driver log mem failed"); + goto store_result; + } + user_driver_log_wr_size = (int __user *)tests[i].driver_log_wr_size; + tests[i].driver_log_wr_size = &driver_log_wr_size; + } + + if (driver_log_to_file) { + user_driver_log_filepath = (const char __user *)tests[i].driver_log_filepath; + tests[i].driver_log_filepath = strndup_user(user_driver_log_filepath, PATH_MAX); + if (tests[i].driver_log_filepath == NULL) { + cts_err("Strdup driver log filepath failed"); + goto store_result; + } + cts_info("Log driver log to file '%s'", tests[i].driver_log_filepath); + } + + if (driver_log_to_file || driver_log_to_user) { + ret = cts_start_driver_log_redirect( + tests[i].driver_log_filepath, driver_log_to_file_append, + tests[i].driver_log_buf, tests[i].driver_log_buf_size, + tests[i].driver_log_level); + if (ret) { + cts_err("Start driver log redirect failed %d", ret); + goto store_result; + } + } + + if (tests[i].priv_param_size && tests[i].priv_param) { + user_priv_param = (void __user *)tests[i].priv_param; + tests[i].priv_param = memdup_user(user_priv_param, tests[i].priv_param_size); + if (IS_ERR(tests[i].priv_param)) { + ret = PTR_ERR(tests[i].priv_param); + tests[i].priv_param = NULL; + cts_err("Memdup priv param from user failed %d", ret); + goto store_result; + } + } + + switch (tests[i].test_item) { + case CTS_TEST_RESET_PIN: + ret = cts_test_reset_pin(cts_dev, &tests[i]); + break; + case CTS_TEST_INT_PIN: + ret = cts_test_int_pin(cts_dev, &tests[i]); + break; + case CTS_TEST_RAWDATA: + ret = cts_test_rawdata(cts_dev, &tests[i]); + break; + case CTS_TEST_NOISE: + ret = cts_test_noise(cts_dev, &tests[i]); + break; + case CTS_TEST_OPEN: + ret = cts_test_open(cts_dev, &tests[i]); + break; + case CTS_TEST_SHORT: + ret = cts_test_short(cts_dev, &tests[i]); + break; + case CTS_TEST_COMPENSATE_CAP: + ret = cts_test_compensate_cap(cts_dev, &tests[i]); + break; + default: + ret = ENOTSUPP; + cts_err("Un-supported test item"); + break; + } + +/* + * Copy result and test data back to userspace. + */ +store_result: + if (dump_test_data_to_user) { + if (user_test_data != NULL && test_data_wr_size > 0) { + cts_info("Copy test data to user, size: %d", test_data_wr_size); + if (copy_to_user(user_test_data, tests[i].test_data_buf, + test_data_wr_size)) { + cts_err("Copy test data to user failed"); + test_data_wr_size = 0; + } + } + + if (user_test_data_wr_size != NULL) + put_user(test_data_wr_size, user_test_data_wr_size); + } + + if (driver_log_to_user) { + driver_log_wr_size = cts_get_driver_log_redirect_size(); + if (user_driver_log_buf != NULL && driver_log_wr_size > 0) { + cts_info("Copy driver log to user, size: %d", driver_log_wr_size); + if (copy_to_user(user_driver_log_buf, tests[i].driver_log_buf, + driver_log_wr_size)) { + cts_err("Copy driver log to user failed"); + driver_log_wr_size = 0; + } + } + + if (user_driver_log_wr_size != NULL) + put_user(driver_log_wr_size, user_driver_log_wr_size); + } + + if (user_test_result != NULL) + put_user(ret, user_test_result); + else if (tests[i].test_result != NULL) + put_user(ret, tests[i].test_result); + + if (dump_test_data_to_user) { + if (user_test_data != NULL && tests[i].test_data_buf != NULL) + vfree(tests[i].test_data_buf); + } + if (validate_data) { + if (validate_min && user_min_threshold != NULL && tests[i].min != NULL) + kfree(tests[i].min); + if (validate_max && user_max_threshold != NULL && tests[i].max != NULL) + kfree(tests[i].max); + if (skip_invalid_node) { + if (user_invalid_nodes != NULL && tests[i].invalid_nodes != NULL) + kfree(tests[i].invalid_nodes); + } + } + + if (dump_test_data_to_file && + user_test_data_filepath != NULL && + tests[i].test_data_filepath != NULL) { + kfree(tests[i].test_data_filepath); + } + + if (driver_log_to_user) { + if (user_driver_log_buf != NULL && + tests[i].driver_log_buf != NULL) { + kfree(tests[i].driver_log_buf); + } + } + + if (driver_log_to_file) { + if (user_driver_log_filepath != NULL && + tests[i].driver_log_filepath != NULL) { + kfree(tests[i].driver_log_filepath); + } + } + + if (driver_log_to_file || driver_log_to_user) + cts_stop_driver_log_redirect(); + + if (user_priv_param && tests[i].priv_param) + kfree(tests[i].priv_param); + + if (ret && stop_test_if_validate_fail) + break; + } + + kfree(tests); + + return ret; +} + + +static long cts_tool_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct chipone_ts_data *cts_data; + struct cts_device *cts_dev; + unsigned int modid; + int ret; + + cts_info("ioctl, cmd=0x%016x, arg=0x%016lx", cmd, arg); + + cts_data = file->private_data; + if (cts_data == NULL) { + cts_err("IOCTL with private data = NULL"); + return -EFAULT; + } + + cts_dev = &cts_data->cts_dev; + + switch (cmd) { + case CTS_TOOL_IOCTL_GET_DRIVER_VERSION: + return put_user(CTS_DRIVER_VERSION_CODE, + (unsigned int __user *)arg); + case CTS_TOOL_IOCTL_GET_DEVICE_TYPE: + return put_user(cts_dev->hwdata->hwid, + (unsigned int __user *)arg); + case CTS_TOOL_IOCTL_GET_FW_VERSION: + return put_user(cts_dev->fwdata.version, + (unsigned short __user *)arg); + case CTS_TOOL_IOCTL_GET_MODULE_ID: + ret = cts_dev->ops->get_module_id(cts_dev, &modid); + if (ret) { + cts_err("Get module Id failed"); + return -EFAULT; + } + return put_user(modid, + (unsigned int __user *)arg); + + case CTS_TOOL_IOCTL_TEST:{ + struct cts_test_ioctl_data test_arg; + struct cts_test_param *tests_pa; + + if (copy_from_user(&test_arg, + (struct cts_test_ioctl_data __user *)arg, + sizeof(test_arg))) { + cts_err("Copy ioctl test arg to kernel failed"); + return -EFAULT; + } + + if (test_arg.ntests > 8) { + cts_err("ioctl test with too many tests %u", + test_arg.ntests); + return -EINVAL; + } + + tests_pa = memdup_user(test_arg.tests, + test_arg.ntests * sizeof(struct cts_test_param)); + if (IS_ERR(tests_pa)) { + int ret = PTR_ERR(tests_pa); + + cts_err("Memdump test param to kernel failed %d", ret); + return ret; + } + + return cts_ioctl_test(cts_dev, test_arg.ntests, tests_pa); + } + case CTS_TOOL_IOCTL_TCS_RW:{ + return cts_ioctl_tcs_rw(cts_dev, file, cmd, arg); + } + + default: + break; + } + + return -ENOTSUPP; +} + +static struct file_operations cts_tool_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = cts_tool_open, + .read = cts_tool_read, + .write = cts_tool_write, + .unlocked_ioctl = cts_tool_ioctl, +}; + +int cts_tool_init(struct chipone_ts_data *cts_data) +{ + cts_info("Init"); + + cts_data->procfs_entry = proc_create_data(CFG_CTS_TOOL_PROC_FILENAME, + 0660, NULL, &cts_tool_fops, + cts_data); + if (cts_data->procfs_entry == NULL) { + cts_err("Create proc entry failed"); + return -EFAULT; + } + + return 0; +} + +void cts_tool_deinit(struct chipone_ts_data *data) +{ + cts_info("Deinit"); + + if (data->procfs_entry) + remove_proc_entry(CFG_CTS_TOOL_PROC_FILENAME, NULL); +} +#endif /* CONFIG_CTS_LEGACY_TOOL */ diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/Android.mk b/drivers/input/touchscreen/chipone_tddi_mmi/Android.mk new file mode 100644 index 000000000000..19b33f5b1b41 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/Android.mk @@ -0,0 +1,17 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +CHIPONE_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +CHIPONE_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif +ifeq ($(CHIPONE_ESD),true) + KERNEL_CFLAGS += CONFIG_CHIPONE_ESD=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := chipone_tddi_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(CHIPONE_MMI_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/Kbuild b/drivers/input/touchscreen/chipone_tddi_mmi/Kbuild new file mode 100644 index 000000000000..f6b80347f77f --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/Kbuild @@ -0,0 +1,20 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/chipone_tddi_mmi +ifneq ($(filter m y,$(CONFIG_CHIPONE_ESD)),) +EXTRA_CFLAGS += -DCONFIG_CTS_ESD_PROTECTION +endif + +obj-m := chipone_tddi_mmi.o + +chipone_tddi_mmi-objs += cts_i2c_driver.o +chipone_tddi_mmi-objs += cts_core.o +chipone_tddi_mmi-objs += cts_platform.o +chipone_tddi_mmi-objs += cts_sfctrlv2.o +chipone_tddi_mmi-objs += cts_spi_flash.o +chipone_tddi_mmi-objs += cts_firmware.o +chipone_tddi_mmi-objs += cts_tool.o +chipone_tddi_mmi-objs += cts_sysfs.o +chipone_tddi_mmi-objs += cts_test.o +chipone_tddi_mmi-objs += cts_charger_detect.o diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/Makefile b/drivers/input/touchscreen/chipone_tddi_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_builtin_firmware.h b/drivers/input/touchscreen/chipone_tddi_mmi/cts_builtin_firmware.h new file mode 100644 index 000000000000..e5f292e94067 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_builtin_firmware.h @@ -0,0 +1,23 @@ +static u8 icnl9911s_driver_builtin_firmware[] = { +}; + +static u8 icnl9911c_driver_builtin_firmware[] = { +}; + +const static struct cts_firmware cts_driver_builtin_firmwares[] = { + { + .name = "OEM-Project", /* MUST set non-NULL */ + .hwid = CTS_DEV_HWID_ICNL9911S, + .fwid = CTS_DEV_FWID_ICNL9911S, + .data = icnl9911s_driver_builtin_firmware, + .size = ARRAY_SIZE(icnl9911s_driver_builtin_firmware), + }, + { + .name = "OEM-Project", /* MUST set non-NULL */ + .hwid = CTS_DEV_HWID_ICNL9911C, + .fwid = CTS_DEV_FWID_ICNL9911C, + .data = icnl9911c_driver_builtin_firmware, + .size = ARRAY_SIZE(icnl9911c_driver_builtin_firmware), + }, + +}; diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_charger_detect.c b/drivers/input/touchscreen/chipone_tddi_mmi/cts_charger_detect.c new file mode 100755 index 000000000000..187de1e18ca2 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_charger_detect.c @@ -0,0 +1,1084 @@ +#define LOG_TAG "Charger" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sysfs.h" + +#ifdef CONFIG_CTS_CHARGER_DETECT + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0) +/** + * match_string - matches given string in an array + * @array: array of strings + * @n: number of strings in the array or -1 for NULL terminated arrays + * @string: string to match with + * + * Return: + * index of a @string in the @array if matches, or %-EINVAL otherwise. + */ +int match_string(const char * const *array, size_t n, const char *string) +{ + int index; + const char *item; + + for (index = 0; index < n; index++) { + item = array[index]; + if (!item) + break; + if (!strcmp(item, string)) + return index; + } + + return -EINVAL; +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0) +#define CFG_CTS_CHARGER_DETECT_PSY_NOTIFY +#endif +#define CFG_CTS_CHARGER_DETECT_PSY_POLL + +#include +#include + +enum cts_charger_detect_type { + CTS_CHGR_DET_TYPE_NONE = 0, + CTS_CHGR_DET_TYPE_PSY_NOTIFY, + CTS_CHGR_DET_TYPE_POLL_PSP, + CTS_CHGR_DET_TYPE_MAX +}; + +/* Over-written setting for DTS */ +#define CFG_CTS_DEF_CHGR_DET_ENABLE + +/* Default settings */ +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY +#define CFG_CTS_DEF_CHGR_DET_TYPE CTS_CHGR_DET_TYPE_PSY_NOTIFY +#else /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ +#define CFG_CTS_DEF_CHGR_DET_TYPE CTS_CHGR_DET_TYPE_POLL_PSP +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#define CFG_CTS_DEF_CHGR_DET_PSY_NAME "usb" +#define CFG_CTS_DEF_CHGR_DET_PSY_PROP POWER_SUPPLY_PROP_PRESENT +#define CFG_CTS_DEF_CHGR_DET_PSP_POLL_INTERVAL 2000u + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,1,0) +/* Lower version has no power_supply_get_property() */ +#define POWER_SUPPLY_GET_PROPERTY(psy, psp, val) \ + power_supply_get_property(psy, psp, val) +#else +#define POWER_SUPPLY_GET_PROPERTY(psy, psp, val) \ + psy->get_property(psy, psp, val) +#endif + +struct cts_charger_detect_data { + bool enable; + bool running; + bool state; + + /* Parameter */ + enum cts_charger_detect_type type; + const char *psy_name; + enum power_supply_property psp; + u32 psp_poll_interval; /* Unit is ms */ + + /* Power supply notify */ +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY + bool notifier_registered; + struct notifier_block psy_notifier; +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL + /* Polling power supply property */ + struct delayed_work psp_poll_work; +#endif /* CFG_CTS_CHARGER_DETECT_PSY_POLL */ + + struct work_struct set_charger_state_work; + + /* Sysfs */ +#ifdef CONFIG_CTS_SYSFS + bool sysfs_created; +#endif /* CONFIG_CTS_SYSFS */ + + struct chipone_ts_data *cts_data; + +}; + +#define POWER_SUPPLY_PROP_PREFIX "POWER_SUPPLY_PROP_" +#define POWER_SUPPLY_PROP_TOKEN(property) \ + {.prop = POWER_SUPPLY_PROP_ ##property, \ + .name = POWER_SUPPLY_PROP_PREFIX #property} + +const static struct { + enum power_supply_property prop; + const char *name; +} power_supply_prop_token [] = { + POWER_SUPPLY_PROP_TOKEN(STATUS), + POWER_SUPPLY_PROP_TOKEN(PRESENT), + POWER_SUPPLY_PROP_TOKEN(ONLINE), +}; + +static const char *power_supply_prop_str(enum power_supply_property prop) +{ + int i; + + for (i = 0; i < sizeof(power_supply_prop_token); i++) { + if (prop == power_supply_prop_token[i].prop) { + return power_supply_prop_token[i].name; + } + } + + return "Unknown"; +} + +static enum power_supply_property power_supply_prop_from_name(const char *name) +{ + size_t offset = strlen(POWER_SUPPLY_PROP_PREFIX); + int i; + + for (i = 0; i < ARRAY_SIZE(power_supply_prop_token); i++) { + if (strcasecmp(power_supply_prop_token[i].name + offset, name) == 0 || + strcasecmp(power_supply_prop_token[i].name, name) == 0) { + return power_supply_prop_token[i].prop; + } + } + + return -ENOENT; +} + +static const char *charger_detect_type_text[] = { + "none", "notify", "poll", +}; + +static const char *charger_detect_type_str(enum cts_charger_detect_type type) +{ + return type < ARRAY_SIZE(charger_detect_type_text) ? + charger_detect_type_text[type] : "Unknown"; +} + +static int parse_charger_detect_dt(struct cts_charger_detect_data *cd_data, + struct device_node *np) +{ + const char *psy_name; + const char *type_str; + const char *psp_str; + int ret; + + cts_info("Parse dt"); + +#ifdef CFG_CTS_DEF_CHGR_DET_ENABLE + cd_data->enable = true; +#else /* CFG_CTS_DEF_CHGR_DET_ENABLE */ + cd_data->enable = + of_property_read_bool(np, "chipone,touch-charger-detect-enable"); +#endif /* CFG_CTS_DEF_CHGR_DET_ENABLE */ + + cd_data->type = CFG_CTS_DEF_CHGR_DET_TYPE; + ret = of_property_read_string(np, + "chipone,touch-charger-detect-type", &type_str); + if (ret) { + cts_warn("Parse detect type failed %d", ret); + } else { + int type = match_string(charger_detect_type_text, + ARRAY_SIZE(charger_detect_type_text), type_str); + if (type < 0) { + cts_err("Parse detect type '%s' invalid", type_str); + } else { + cd_data->type = type; + } + } + + ret = of_property_read_string(np, + "chipone,touch-charger-detect-psy-name", &psy_name); + if (ret) { + cts_warn("Parse detect psy name failed %d", ret); + psy_name = CFG_CTS_DEF_CHGR_DET_PSY_NAME; + } else { + if (power_supply_get_by_name(psy_name) == NULL) { + cts_warn("Power supply '%s' not found", psy_name); + psy_name = CFG_CTS_DEF_CHGR_DET_PSY_NAME; + } + } + cd_data->psy_name = kstrdup(psy_name, GFP_KERNEL); + if (cd_data->psy_name == NULL) { + cts_err("Alloc mem for psy name failed"); + return -ENOMEM; + } + + cd_data->psp = CFG_CTS_DEF_CHGR_DET_PSY_PROP; + ret = of_property_read_string(np, + "chipone,touch-charger-detect-psp", &psp_str); + if (ret) { + cts_warn("Parse detect psp failed %d", ret); + } else { + struct power_supply *psy; + enum power_supply_property psp; + union power_supply_propval val; + + psp = power_supply_prop_from_name(psp_str); + if (psp < 0) { + cts_warn("Parse detect psp: '%s' invalid", + psp_str); + } else { + psy = power_supply_get_by_name(cd_data->psy_name); + if (psy != NULL && + POWER_SUPPLY_GET_PROPERTY(psy, psp, &val) >=0) { + cts_err("Parse detect psp invalid"); + } else { + cd_data->psp = psp; + } + } + } + + cd_data->psp_poll_interval = CFG_CTS_DEF_CHGR_DET_PSP_POLL_INTERVAL; + ret = of_property_read_u32(np, + "chipone,touch-charger-detect-psp-poll-interval", + &cd_data->psp_poll_interval); + if (ret) { + cts_warn("Parse detect psp poll interval failed %d", ret); + } + + return 0; +} + +static int start_charger_detect(struct cts_charger_detect_data *cd_data) +{ + if (!cd_data->enable) { + cts_warn("Start detect while NOT enabled"); + return -EINVAL; + } + + if (cd_data->running) { + cts_warn("Start detect while already RUNNING"); + return 0; + } + + cts_info("Start detect type: %d(%s)", + cd_data->type, charger_detect_type_str(cd_data->type)); + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY + if(cd_data->type == CTS_CHGR_DET_TYPE_PSY_NOTIFY) { + int ret = power_supply_reg_notifier(&cd_data->psy_notifier); + if (ret) { + cts_err("Register notifier failed: %d", ret); + return ret; + } + cd_data->notifier_registered = true; + + cd_data->running = true; + + return 0; + } +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL + if(cd_data->type == CTS_CHGR_DET_TYPE_POLL_PSP) { + if (!queue_delayed_work(cd_data->cts_data->workqueue, + &cd_data->psp_poll_work, + msecs_to_jiffies(cd_data->psp_poll_interval))) { + cts_warn("Queue detect work while already on the queue"); + } + cd_data->running = true; + + return 0; + } +#endif /* CFG_CTS_CHARGER_DETECT_PSY_POLL */ + + return -ENOTSUPP; +} + +static int stop_charger_detect(struct cts_charger_detect_data *cd_data) +{ + if (!cd_data->running) { + cts_warn("Stop detect while NOT running"); + return 0; + } + + cts_info("Stop detect type: %d(%s)", + cd_data->type, charger_detect_type_str(cd_data->type)); + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY + if(cd_data->type == CTS_CHGR_DET_TYPE_PSY_NOTIFY) { + if (cd_data->notifier_registered) { + power_supply_unreg_notifier(&cd_data->psy_notifier); + cd_data->notifier_registered = false; + } + cd_data->running = false; + + return 0; + } +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL + if(cd_data->type == CTS_CHGR_DET_TYPE_POLL_PSP) { + if (!cancel_delayed_work_sync(&cd_data->psp_poll_work)) { + cts_warn("Cancel poll psp work while NOT pending"); + } + cd_data->running = false; + + return 0; + } +#endif /* CFG_CTS_CHARGER_DETECT_PSY_POLL */ + + return 0; +} + +static int enable_charger_detect(struct cts_charger_detect_data *cd_data) +{ + cts_info("Enable detect type: %d(%s)", + cd_data->type, charger_detect_type_str(cd_data->type)); + + cd_data->enable = true; + + return 0; +} + +static int disable_charger_detect(struct cts_charger_detect_data *cd_data) +{ + int ret; + + cts_info("Disable detect type: %d(%s)", + cd_data->type, charger_detect_type_str(cd_data->type)); + + ret = stop_charger_detect(cd_data); + if (ret) { + cts_err("Disable detect failed %d", ret); + } + + cd_data->enable = false; + + return 0; +} + +static int get_charger_state(struct cts_charger_detect_data *cd_data) +{ + struct power_supply *psy; + union power_supply_propval propval; + int ret; + + cts_dbg("Get state from psy: '%s' prop: %d(%s)", + cd_data->psy_name, cd_data->psp, + power_supply_prop_str(cd_data->psp)); + + if (cd_data->psy_name == NULL || + (psy = power_supply_get_by_name(cd_data->psy_name)) == NULL) { + cts_err("Get state from psy: '%s' not found", + cd_data->psy_name); + return -EINVAL; + } + + ret = POWER_SUPPLY_GET_PROPERTY(psy, cd_data->psp, &propval); + if (ret < 0) { + cts_err("Get state from psy: '%s' prop: %d(%s) failed", + cd_data->psy_name, cd_data->psp, + power_supply_prop_str(cd_data->psp)); + return -EINVAL; + } + + /* ONLY for bool type */ + cd_data->state = !!propval.intval; + + cts_dbg("State: %s", cd_data->state ? "ATTACHED" : "DETACHED"); + + return 0; +} + +static int switch_charger_detect_type(struct cts_charger_detect_data *cd_data, + enum cts_charger_detect_type type) +{ + bool running; + + cts_info("Switch detect type to %d(%s)", + type, charger_detect_type_str(type)); + + if (type >= CTS_CHGR_DET_TYPE_MAX) { + cts_err("Switch detect type %d invalid", type); + return -EINVAL; + } + + if (cd_data->type == type) { + cts_warn("Switch detect type equal"); + return 0; + } + + running = cd_data->running; + + if (running) { + int ret = stop_charger_detect(cd_data); + if (ret) { + cts_err("Stop detect failed %d", ret); + return ret; + } + } + + cd_data->type = type; + + if (running) { + int ret = start_charger_detect(cd_data); + if (ret) { + cts_err("Start detect failed %d", ret); + cd_data->type = CTS_CHGR_DET_TYPE_NONE; + return ret; + } + } + + return 0; +} + +static void set_dev_charger_state_work(struct work_struct *work) +{ + struct cts_charger_detect_data *cd_data; + int ret; + + cd_data = container_of(work, struct cts_charger_detect_data, + set_charger_state_work); + + cts_lock_device(&cd_data->cts_data->cts_dev); + ret = cts_set_dev_charger_attached(&cd_data->cts_data->cts_dev, + cd_data->state); + cts_unlock_device(&cd_data->cts_data->cts_dev); + if (ret) { + cts_err("Set dev charger attached to %s failed %d", + cd_data->state ? "ATTACHED" : "DETATCHED", ret); + /* Set to previous state, try set again in next loop */ + cd_data->state = !cd_data->state; + } +} + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY +static int psy_notify_callback(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct cts_charger_detect_data *cd_data; + struct power_supply *psy; + bool prev_state; + int ret; + + if (nb == NULL || data == NULL) { + cts_err("PSY notify callback with notifier %p or data %p = NULL", + nb, data); + return NOTIFY_DONE; + } + + if (action != PSY_EVENT_PROP_CHANGED) { + cts_dbg("Notify event %ld not care", action); + return NOTIFY_DONE; + } + + cd_data = container_of(nb, struct cts_charger_detect_data, psy_notifier); + + psy = (struct power_supply *)data; + if (strcmp(psy->desc->name, cd_data->psy_name) != 0) { + cts_dbg("Notify from power supply '%s' not care", + psy->desc->name); + return NOTIFY_DONE; + } + + prev_state = cd_data->state; + + ret = get_charger_state(cd_data); + if (ret < 0) { + cts_err("Get state failed %d", ret); + return NOTIFY_DONE; + } + + cts_info("State changed: %s -> %s", + prev_state ? "ATTACHED" : "DETACHED", + cd_data->state ? "ATTACHED" : "DETACHED"); + + if (!queue_work(cd_data->cts_data->workqueue, + &cd_data->set_charger_state_work)) { + cts_warn("Set device charger state work is PENDING"); + } + + return NOTIFY_OK; +} +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL +static void poll_psp_work(struct work_struct *work) +{ + struct cts_charger_detect_data *cd_data; + bool prev_state; + int ret; + + cts_dbg("Poll psp work"); + + cd_data = container_of(to_delayed_work(work), + struct cts_charger_detect_data, psp_poll_work); + + prev_state = cd_data->state; + + ret = get_charger_state(cd_data); + if (ret < 0) { + cts_err("Get state failed %d", ret); + } else { + if (cd_data->state != prev_state) { + cts_info("State changed: %s -> %s", + prev_state ? "ATTACHED" : "DETACHED", + cd_data->state ? "ATTACHED" : "DETACHED"); + + if (!queue_work(cd_data->cts_data->workqueue, + &cd_data->set_charger_state_work)) { + cts_warn("Set device charger state work is PENDING"); + } + } + } + + if (!queue_delayed_work(cd_data->cts_data->workqueue, + &cd_data->psp_poll_work, + msecs_to_jiffies(cd_data->psp_poll_interval))) { + cts_warn("Queue detect work while already on the queue"); + } +} +#endif /* CFG_CTS_CHARGER_DETECT_PSY_POLL */ + +static int init_charger_detect(struct cts_charger_detect_data *cd_data) +{ + int ret; + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY + cd_data->psy_notifier.notifier_call = psy_notify_callback; +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL + INIT_DELAYED_WORK(&cd_data->psp_poll_work, poll_psp_work); +#endif /* CFG_CTS_CHARGER_DETECT_PSY_POLL */ + + INIT_WORK(&cd_data->set_charger_state_work, set_dev_charger_state_work); + + ret = get_charger_state(cd_data); + if (ret) { + cts_err("Get state failed %d", ret); + } + + return 0; +} + +/* Sysfs */ +#ifdef CONFIG_CTS_SYSFS +extern int argc; +extern char *argv[]; +extern int parse_arg(const char *buf, size_t count); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,6,0) +/* From lib/kstrtox.c */ +/** + * kstrtobool - convert common user inputs into boolean values + * @s: input string + * @res: result + * + * This routine returns 0 iff the first character is one of 'Yy1Nn0', or + * [oO][NnFf] for "on" and "off". Otherwise it will return -EINVAL. Value + * pointed to by res is updated upon finding a match. + */ +int kstrtobool(const char *s, bool *res) +{ + if (!s) + return -EINVAL; + + switch (s[0]) { + case 'y': + case 'Y': + case '1': + *res = true; + return 0; + case 'n': + case 'N': + case '0': + *res = false; + return 0; + case 'o': + case 'O': + switch (s[1]) { + case 'n': + case 'N': + *res = true; + return 0; + case 'f': + case 'F': + *res = false; + return 0; + default: + break; + } + default: + break; + } + + return -EINVAL; +} +#endif + +#define CHARGER_DET_SYSFS_GROUP_NAME "charger-det" + +static ssize_t charger_detect_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + + cts_info("Read sysfs '"CHARGER_DET_SYSFS_GROUP_NAME"/%s'", + attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, + "Charger detect: %s\n", + cd_data->enable ? "ENABLED" : "DISABLED"); +} + +/* Echo 0/n/N/of/Of/Of/OF/1/y/Y/on/oN/On/ON > enable */ +static ssize_t charger_detect_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + bool enable; + int ret; + + cts_info("Write sysfs '"CHARGER_DET_SYSFS_GROUP_NAME"/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num of args"); + return -EINVAL; + } + + ret = kstrtobool(argv[0], &enable); + if (ret) { + cts_err("Invalid param of enable"); + return ret; + } + + if (enable) { + ret = enable_charger_detect(cd_data); + } else { + ret = disable_charger_detect(cd_data); + } + if (ret) { + cts_err("%s charger detect failed %d", + enable ? "Enable" : "Disable", ret); + return ret; + } + + return count; +} +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + charger_detect_enable_show, charger_detect_enable_store); + +static ssize_t charger_detect_running_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + + cts_info("Read sysfs '"CHARGER_DET_SYSFS_GROUP_NAME"/%s'", + attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, + "Charger detect: %sRunning\n", + cd_data->running ? "" : "Not-"); +} + +/* Echo 0/n/N/of/Of/Of/OF/1/y/Y/on/oN/On/ON > runing */ +static ssize_t charger_detect_running_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + bool running; + int ret; + + cts_info("Write sysfs '"CHARGER_DET_SYSFS_GROUP_NAME"/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num of args"); + return -EINVAL; + } + + ret = kstrtobool(argv[0], &running); + if (ret) { + cts_err("Invalid param of running"); + return ret; + } + + if (running) { + ret = start_charger_detect(cd_data); + } else { + ret = stop_charger_detect(cd_data); + } + if (ret) { + cts_err("%s charger detect failed %d", + running ? "Start" : "Stop", ret); + return ret; + } + + return count; +} +static DEVICE_ATTR(running, S_IWUSR | S_IRUGO, + charger_detect_running_show, charger_detect_running_store); + +static ssize_t charger_detect_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + + cts_info("Read sysfs '"CHARGER_DET_SYSFS_GROUP_NAME"/%s'", + attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, + "Charger detect type: %d(%s)\n", + cd_data->type, charger_detect_type_str(cd_data->type)); +} + +/* echo charger_detect_type > type */ +static ssize_t charger_detect_type_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + int type, ret; + + cts_info("Write sysfs '"CHARGER_DET_SYSFS_GROUP_NAME"/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc != 1) { + cts_err(""); + return -EINVAL; + } + + type = match_string(charger_detect_type_text, + ARRAY_SIZE(charger_detect_type_text), argv[0]); + if (type < 0) { + cts_err("Invalid charger detect type: '%s'", argv[0]); + return -EINVAL; + } + + ret = switch_charger_detect_type(cd_data, type); + if (ret) { + cts_err("Switch charger detect type failed %d", ret); + return ret; + } + + return count; +} +static DEVICE_ATTR(type, S_IWUSR | S_IRUGO, + charger_detect_type_show, charger_detect_type_store); + +static ssize_t charger_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + int ret; + + cts_info("Read sysfs '"CHARGER_DET_SYSFS_GROUP_NAME"/%s'", + attr->attr.name); + + ret = get_charger_state(cd_data); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Get charge state failed %d\n", ret); + } + + return scnprintf(buf, PAGE_SIZE, + "Charger state: %s\n", + cd_data->state ? "ATTACHED" : "DETACHED"); +} +static DEVICE_ATTR(state, S_IRUGO, charger_state_show, NULL); + +static ssize_t charger_detect_param_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + struct power_supply *psy; + + cts_info("Read sysfs '"CHARGER_DET_SYSFS_GROUP_NAME"/%s'", + attr->attr.name); + + psy = power_supply_get_by_name(cd_data->psy_name); + + return scnprintf(buf, PAGE_SIZE, + "Power supply name: %s(%sExist)\n" + "Power supply prop: %d(%s)\n" + "Poll interval : %dms\n", + cd_data->psy_name, psy == NULL ? "Non-" : "", + cd_data->psp, power_supply_prop_str(cd_data->psp), + cd_data->psp_poll_interval); +} + +/* echo psy_name psy_property [interval] > param */ +static ssize_t charger_detect_param_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + struct power_supply *psy; + union power_supply_propval propval; + enum power_supply_property psp; + u32 interval; + int ret; + + cts_info("Write sysfs '"CHARGER_DET_SYSFS_GROUP_NAME"/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc < 2 || argc > 3) { + cts_err("Invalid num of args"); + return -EINVAL; + } + + if ((psy = power_supply_get_by_name(argv[0])) == NULL) { + cts_err("Power supply '%s' not found", argv[0]); + return -EINVAL; + } + + psp = power_supply_prop_from_name(argv[1]); + if (psp < 0 || + POWER_SUPPLY_GET_PROPERTY(psy, psp, &propval) < 0) { + cts_err("Power supply '%s' property '%s' not valid", + argv[0], power_supply_prop_str(psp)); + return -EINVAL; + } + + interval = cd_data->psp_poll_interval; + if (argc > 2) { + ret = kstrtou32(argv[2], 0, &interval); + if (ret) { + cts_err("Arg interval is invalid"); + return -EINVAL; + } + } + + if (cd_data->psy_name) { + kfree(cd_data->psy_name); + } + cd_data->psy_name = kstrdup(argv[0], GFP_KERNEL); + if (cd_data->psy_name == NULL) { + cts_err("Dup power supply name failed"); + return -ENOMEM; + } + + cd_data->psp = psp; + cd_data->psp_poll_interval = interval; + + return count; +} +static DEVICE_ATTR(param, S_IWUSR | S_IRUGO, + charger_detect_param_show, charger_detect_param_store); + +static struct attribute *charger_detect_attrs[] = { + &dev_attr_enable.attr, + &dev_attr_running.attr, + &dev_attr_type.attr, + &dev_attr_state.attr, + &dev_attr_param.attr, + NULL +}; + +static const struct attribute_group charger_detect_attr_group = { + .name = CHARGER_DET_SYSFS_GROUP_NAME, + .attrs = charger_detect_attrs, +}; +#endif /* CONFIG_CTS_SYSFS */ + +int cts_charger_detect_init(struct chipone_ts_data *cts_data) +{ + struct cts_charger_detect_data *cd_data; + int ret = 0; + + cts_info("Init detect"); + + if (cts_data == NULL) { + cts_err("Init detect while cts_data = NULL"); + return -EFAULT; + } + + cd_data = kzalloc(sizeof(*cd_data), GFP_KERNEL); + if (cd_data == NULL) { + cts_err("Alloc charger detect data failed"); + return -ENOMEM; + } + +#ifdef CONFIG_CTS_OF + ret = parse_charger_detect_dt(cd_data, cts_data->device->of_node); +#else /* CONFIG_CTS_OF */ +#ifdef CFG_CTS_DEF_CHGR_DET_ENABLE + cd_data->enable = true; +#else /* CFG_CTS_DEF_CHGR_DET_ENABLE */ + cd_data->enable = false; +#endif /* CFG_CTS_DEF_CHGR_DET_ENABLE */ + + cd_data->type = CFG_CTS_DEF_CHGR_DET_TYPE; + cd_data->psp = CFG_CTS_DEF_CHGR_DET_PSY_PROP; + cd_data->psp_poll_interval = CFG_CTS_DEF_CHGR_DET_PSP_POLL_INTERVAL; + cd_data->psy_name = kstrdup(CFG_CTS_DEF_CHGR_DET_PSY_NAME, GFP_KERNEL); + if (cd_data->psy_name == NULL) { + cts_err("Dup power supply name failed"); + ret = -ENOMEM; + } +#endif /* CONFIG_CTS_OF */ + if (ret) { + cts_err("Get detect param failed %d", ret); + goto free_cd_data; + } + + cts_info("Detect: %sABLED", cd_data->enable ? "EN" : "DIS"); + cts_info(" Type : %s", charger_detect_type_str(cd_data->type)); + cts_info(" PSY Name: %s, prop: %d(%s)", cd_data->psy_name, + cd_data->psp, power_supply_prop_str(cd_data->psp)); + cts_info(" Poll Int: %dms", cd_data->psp_poll_interval); + + ret = init_charger_detect(cd_data); + if (ret) { + cts_err("Init detect failed %d", ret); + goto free_psy_name; + } + +#ifdef CONFIG_CTS_SYSFS + cts_info("Create sysfs group '%s'", CHARGER_DET_SYSFS_GROUP_NAME); + ret = sysfs_create_group(&cts_data->device->kobj, + &charger_detect_attr_group); + if (ret) { + cts_warn("Create sysfs group failed %d", ret); + } else { + cd_data->sysfs_created = true; + } +#endif /* CONFIG_CTS_SYSFS */ + + cts_data->charger_detect_data = cd_data; + cd_data->cts_data = cts_data; + + return 0; + +free_psy_name: + kfree(cd_data->psy_name); +free_cd_data: + kfree(cd_data); + + return ret; +} + +int cts_charger_detect_deinit(struct chipone_ts_data *cts_data) +{ + struct cts_charger_detect_data *cd_data; + int ret; + + cts_info("Deinit detect"); + + if (cts_data == NULL) { + cts_err("Deinit detect with cts_data = NULL"); + return -EFAULT; + } + + cd_data = cts_data->charger_detect_data; + if (cd_data == NULL) { + cts_warn("Deinit detect with charger_detect_data = NULL"); + return 0; + } + + if (cd_data->running) { + ret = stop_charger_detect(cd_data); + if (ret) { + cts_err("Stop detect failed %d", ret); + } + } + +#ifdef CONFIG_CTS_SYSFS + if (cd_data->sysfs_created) { + cts_info("Remove sysfs group '%s'", CHARGER_DET_SYSFS_GROUP_NAME); + sysfs_remove_group(&cts_data->device->kobj, + &charger_detect_attr_group); + cd_data->sysfs_created = false; + } +#endif /* CONFIG_CTS_SYSFS */ + + if (cd_data->psy_name) { + cts_info("Kfree power supply name"); + kfree(cd_data->psy_name); + cd_data->psy_name = NULL; + } + + kfree(cd_data); + cts_data->charger_detect_data = NULL; + + return 0; +} + +int cts_is_charger_attached(struct chipone_ts_data *cts_data, bool *attached) +{ + struct cts_charger_detect_data *cd_data; + int ret; + + if (cts_data == NULL) { + cts_err("Get state with cts_data = NULL"); + return -EFAULT; + } + + cd_data = cts_data->charger_detect_data; + if (cd_data == NULL) { + cts_err("Get state with charger_detect_data = NULL"); + return -ENODEV; + } + + cts_info("Get state using type %d(%s) param", + cd_data->type, charger_detect_type_str(cd_data->type)); + + ret = get_charger_state(cd_data); + if (ret) { + cts_err("Get state failed %d", ret); + return ret; + } + + *attached = cd_data->state; + + return 0; +} + +int cts_start_charger_detect(struct chipone_ts_data *cts_data) +{ + struct cts_charger_detect_data *cd_data; + + if (cts_data == NULL) { + cts_err("Start detect with cts_data = NULL"); + return -EFAULT; + } + + cd_data = cts_data->charger_detect_data; + if (cd_data == NULL) { + cts_err("Start detect with charger_detect_data = NULL"); + return -ENODEV; + } + + return start_charger_detect(cd_data); +} + +int cts_stop_charger_detect(struct chipone_ts_data *cts_data) +{ + struct cts_charger_detect_data *cd_data; + + if (cts_data == NULL) { + cts_err("Stop detect with cts_data = NULL"); + return -EFAULT; + } + + cd_data = cts_data->charger_detect_data; + if (cd_data == NULL) { + cts_err("Stop detect with charger_detect_data = NULL"); + return -ENODEV; + } + + return stop_charger_detect(cd_data); +} +#endif /* CONFIG_CTS_CHARGER_DETECT */ + diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_charger_detect.h b/drivers/input/touchscreen/chipone_tddi_mmi/cts_charger_detect.h new file mode 100644 index 000000000000..67de64d2fb64 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_charger_detect.h @@ -0,0 +1,40 @@ +#ifndef CTS_CHARGER_DETECT_H +#define CTS_CHARGER_DETECT_H + +#include "cts_config.h" + +struct chipone_ts_data; + +#ifdef CONFIG_CTS_CHARGER_DETECT +extern int cts_charger_detect_init(struct chipone_ts_data *cts_data); +extern int cts_charger_detect_deinit(struct chipone_ts_data *cts_data); +extern int cts_start_charger_detect(struct chipone_ts_data *cts_data); +extern int cts_stop_charger_detect(struct chipone_ts_data *cts_data); +extern int cts_is_charger_attached(struct chipone_ts_data *cts_data, + bool *attached); +#else /* CONFIG_CTS_CHARGER_DETECT */ +static inline int cts_charger_detect_init(struct chipone_ts_data *cts_data) +{ + return -ENOTSUPP; +} +static inline int cts_charger_detect_deinit(struct chipone_ts_data *cts_data) +{ + return -ENOTSUPP; +} +static inline int cts_start_charger_detect(struct chipone_ts_data *cts_data) +{ + return -ENODEV; +} +static inline int cts_stop_charger_detect(struct chipone_ts_data *cts_data) +{ + return -ENODEV; +} +static inline int cts_is_charger_attached(struct chipone_ts_data *cts_data, + bool *attached) +{ + return -ENODEV; +} +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#endif /* CTS_CHARGER_DETECT_H */ + diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_config.h b/drivers/input/touchscreen/chipone_tddi_mmi/cts_config.h new file mode 100755 index 000000000000..8c4fa9e16b10 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_config.h @@ -0,0 +1,133 @@ +#ifndef CTS_CONFIG_H +#define CTS_CONFIG_H + +/** Driver version */ +#define CFG_CTS_DRIVER_MAJOR_VERSION 1 +#define CFG_CTS_DRIVER_MINOR_VERSION 2 +#define CFG_CTS_DRIVER_PATCH_VERSION 8 + +#define CFG_CTS_DRIVER_VERSION "v1.2.8" + +/** Whether reset pin is used */ +#define CFG_CTS_HAS_RESET_PIN + +//#define CONFIG_CTS_I2C_HOST +#ifndef CONFIG_CTS_I2C_HOST + +#ifndef CFG_CTS_HAS_RESET_PIN +#define CFG_CTS_HAS_RESET_PIN +#endif + +#define CFG_CTS_SPI_SPEED_KHZ 6000 + +#endif + +#define CFG_CTS_FORCE_UP +#define CONFIG_GENERIC_HARDIRQS +//#define CFG_CTS_FW_LOG_REDIRECT + +/** Whether force download firmware to chip */ +//#define CFG_CTS_FIRMWARE_FORCE_UPDATE + +/** Use build in firmware or firmware file in fs*/ +//#define CFG_CTS_DRIVER_BUILTIN_FIRMWARE +#define CFG_CTS_FIRMWARE_IN_FS +#ifdef CFG_CTS_FIRMWARE_IN_FS +//#define CFG_CTS_FW_UPDATE_SYS // Add FW custom process. +//#define CFG_CTS_FW_UPDATE_FILE_LOAD // Load config fw bin as default. +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD +#define CFG_CTS_FW_FILE_NAME_MAX_LEN 128 +#define CFG_CTS_FW_FILE_PATH "/vendor/firmware/" +#define CFG_CTS_FW_FILE_NAME_VENDOR "chipone" +#endif /*CFG_CTS_FW_UPDATE_FILE_LOAD*/ + +#define CFG_CTS_FIRMWARE_FILENAME "ICNL9911.bin" +#define CFG_CTS_FIRMWARE_FILEPATH "/vendor/firmware/ICNL9911.bin" +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + +/* IC type support */ +#define CFG_CTS_CHIP_NAME "ICNL9911S" + +#ifdef CONFIG_PROC_FS + /* Proc FS for backward compatibility for APK tool com.ICN85xx */ +#define CONFIG_CTS_LEGACY_TOOL +#endif /* CONFIG_PROC_FS */ + +#ifdef CONFIG_SYSFS + /* Sys FS for gesture report, debug feature etc. */ +#define CONFIG_CTS_SYSFS +#endif /* CONFIG_SYSFS */ + +#define CFG_CTS_MAX_TOUCH_NUM (10) // 5 + +/* Virtual key support */ +//#define CONFIG_CTS_VIRTUALKEY +#ifdef CONFIG_CTS_VIRTUALKEY +#define CFG_CTS_MAX_VKEY_NUM (4) +#define CFG_CTS_NUM_VKEY (3) +#define CFG_CTS_VKEY_KEYCODES {KEY_BACK, KEY_HOME, KEY_MENU} +#endif /* CONFIG_CTS_VIRTUALKEY */ + +/* Gesture wakeup */ +//#define CFG_CTS_GESTURE +#ifdef CFG_CTS_GESTURE +#define GESTURE_UP 0x11 +#define GESTURE_C 0x12 +#define GESTURE_O 0x13 +#define GESTURE_M 0x14 +#define GESTURE_W 0x15 +#define GESTURE_E 0x16 +#define GESTURE_S 0x17 +#define GESTURE_Z 0x1d +#define GESTURE_V 0x1e +#define GESTURE_D_TAP 0x50 +#define GESTURE_DOWN 0x22 +#define GESTURE_LEFT 0x23 +#define GESTURE_RIGHT 0x24 + +#define CFG_CTS_NUM_GESTURE (13u) +#define CFG_CTS_GESTURE_REPORT_KEY +#define CFG_CTS_GESTURE_KEYMAP \ + {{GESTURE_C, KEY_C,}, \ + {GESTURE_W, KEY_W,}, \ + {GESTURE_V, KEY_V,}, \ + {GESTURE_D_TAP, KEY_F1,}, \ + {GESTURE_Z, KEY_Z,}, \ + {GESTURE_M, KEY_M,}, \ + {GESTURE_O, KEY_O,}, \ + {GESTURE_E, KEY_E,}, \ + {GESTURE_S, KEY_S,}, \ + {GESTURE_UP, KEY_UP,}, \ + {GESTURE_DOWN, KEY_DOWN,}, \ + {GESTURE_LEFT, KEY_LEFT,}, \ + {GESTURE_RIGHT, KEY_RIGHT,}, \ + } +#define CFG_CTS_GESTURE_REPORT_TRACE 0 +#endif /* CFG_CTS_GESTURE */ + +//#define CONFIG_CTS_GLOVE + +#define CONFIG_CTS_CHARGER_DETECT + +/* ESD protection */ +//#define CONFIG_CTS_ESD_PROTECTION +#ifdef CONFIG_CTS_ESD_PROTECTION +#define CFG_CTS_ESD_PROTECTION_CHECK_PERIOD (2 * HZ) +#define CFG_CTS_ESD_FAILED_CONFIRM_CNT 3 +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +/* Use slot protocol (protocol B), comment it if use protocol A. */ +#define CONFIG_CTS_SLOTPROTOCOL + +#ifdef CONFIG_CTS_LEGACY_TOOL +#define CFG_CTS_TOOL_PROC_FILENAME "icn85xx_tool" +#endif /* CONFIG_CTS_LEGACY_TOOL */ + +#define CFG_CTS_UPDATE_CRCCHECK +/**************************************************************************** + * Platform configurations + ****************************************************************************/ + +#include "cts_plat_qcom_config.h" + +#endif /* CTS_CONFIG_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_core.c b/drivers/input/touchscreen/chipone_tddi_mmi/cts_core.c new file mode 100755 index 000000000000..689ed4e0ac5b --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_core.c @@ -0,0 +1,2966 @@ +#define LOG_TAG "Core" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sfctrl.h" +#include "cts_spi_flash.h" +#include "cts_sysfs.h" +#include "cts_firmware.h" +#include "cts_charger_detect.h" + +static DEFINE_RT_MUTEX(dev_lock); + +#ifdef CONFIG_CTS_I2C_HOST +static int cts_i2c_writeb(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay) +{ + u8 buff[8]; + + cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x val: 0x%02x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, b); + + if (cts_dev->rtdata.addr_width == 2) { + put_unaligned_be16(addr, buff); + } else if (cts_dev->rtdata.addr_width == 3) { + put_unaligned_be24(addr, buff); + } else { + cts_err("Writeb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + buff[cts_dev->rtdata.addr_width] = b; + + return cts_plat_i2c_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 1, retry, + delay); +} + +static int cts_i2c_writew(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay) +{ + u8 buff[8]; + + cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x val: 0x%04x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, w); + + if (cts_dev->rtdata.addr_width == 2) { + put_unaligned_be16(addr, buff); + } else if (cts_dev->rtdata.addr_width == 3) { + put_unaligned_be24(addr, buff); + } else { + cts_err("Writew invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + put_unaligned_le16(w, buff + cts_dev->rtdata.addr_width); + + return cts_plat_i2c_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 2, retry, + delay); +} + +static int cts_i2c_writel(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay) +{ + u8 buff[8]; + + cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x val: 0x%08x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, l); + + if (cts_dev->rtdata.addr_width == 2) { + put_unaligned_be16(addr, buff); + } else if (cts_dev->rtdata.addr_width == 3) { + put_unaligned_be24(addr, buff); + } else { + cts_err("Writel invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + put_unaligned_le32(l, buff + cts_dev->rtdata.addr_width); + + return cts_plat_i2c_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 4, retry, + delay); +} + +static int cts_i2c_writesb(const struct cts_device *cts_dev, u32 addr, + const u8 *src, size_t len, int retry, int delay) +{ + int ret; + u8 *data; + size_t max_xfer_size; + size_t payload_len; + size_t xfer_len; + + cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x len: %zu", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, len); + + max_xfer_size = cts_plat_get_max_i2c_xfer_size(cts_dev->pdata); + data = cts_plat_get_i2c_xfer_buf(cts_dev->pdata, len); + while (len) { + payload_len = + min((size_t) (max_xfer_size - cts_dev->rtdata.addr_width), + len); + xfer_len = payload_len + cts_dev->rtdata.addr_width; + + if (cts_dev->rtdata.addr_width == 2) { + put_unaligned_be16(addr, data); + } else if (cts_dev->rtdata.addr_width == 3) { + put_unaligned_be24(addr, data); + } else { + cts_err("Writesb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + memcpy(data + cts_dev->rtdata.addr_width, src, payload_len); + + ret = + cts_plat_i2c_write(cts_dev->pdata, + cts_dev->rtdata.slave_addr, data, + xfer_len, retry, delay); + if (ret) { + cts_err("Platform i2c write failed %d", ret); + return ret; + } + + src += payload_len; + len -= payload_len; + addr += payload_len; + } + + return 0; +} + +static int cts_i2c_readb(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay) +{ + u8 addr_buf[4]; + + cts_dbg("Readb from slave_addr: 0x%02x reg: 0x%0*x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr); + + if (cts_dev->rtdata.addr_width == 2) { + put_unaligned_be16(addr, addr_buf); + } else if (cts_dev->rtdata.addr_width == 3) { + put_unaligned_be24(addr, addr_buf); + } else { + cts_err("Readb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + return cts_plat_i2c_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, b, 1, + retry, delay); +} + +static int cts_i2c_readw(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + u8 buff[2]; + + cts_dbg("Readw from slave_addr: 0x%02x reg: 0x%0*x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr); + + if (cts_dev->rtdata.addr_width == 2) { + put_unaligned_be16(addr, addr_buf); + } else if (cts_dev->rtdata.addr_width == 3) { + put_unaligned_be24(addr, addr_buf); + } else { + cts_err("Readw invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_i2c_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, buff, 2, + retry, delay); + if (ret == 0) { + *w = get_unaligned_le16(buff); + } + + return ret; +} + +static int cts_i2c_readl(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + u8 buff[4]; + + cts_dbg("Readl from slave_addr: 0x%02x reg: 0x%0*x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr); + + if (cts_dev->rtdata.addr_width == 2) { + put_unaligned_be16(addr, addr_buf); + } else if (cts_dev->rtdata.addr_width == 3) { + put_unaligned_be24(addr, addr_buf); + } else { + cts_err("Readl invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_i2c_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, buff, 4, + retry, delay); + if (ret == 0) { + *l = get_unaligned_le32(buff); + } + + return ret; +} + +static int cts_i2c_readsb(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + size_t max_xfer_size, xfer_len; + + cts_dbg("Readsb from slave_addr: 0x%02x reg: 0x%0*x len: %zu", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, len); + + max_xfer_size = cts_plat_get_max_i2c_xfer_size(cts_dev->pdata); + while (len) { + xfer_len = min(max_xfer_size, len); + + if (cts_dev->rtdata.addr_width == 2) { + put_unaligned_be16(addr, addr_buf); + } else if (cts_dev->rtdata.addr_width == 3) { + put_unaligned_be24(addr, addr_buf); + } else { + cts_err("Readsb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = + cts_plat_i2c_read(cts_dev->pdata, + cts_dev->rtdata.slave_addr, addr_buf, + cts_dev->rtdata.addr_width, dst, xfer_len, + retry, delay); + if (ret) { + cts_err("Platform i2c read failed %d", ret); + return ret; + } + + dst += xfer_len; + len -= xfer_len; + addr += xfer_len; + } + + return 0; +} +#else +static int cts_spi_writeb(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay) +{ + u8 buff[8]; + +// cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x val: 0x%02x", +// cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, addr, b); + + if (cts_dev->rtdata.addr_width == 2) { + put_unaligned_be16(addr, buff); + } else if (cts_dev->rtdata.addr_width == 3) { + put_unaligned_be24(addr, buff); + } else { + cts_err("Writeb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + buff[cts_dev->rtdata.addr_width] = b; + + return cts_plat_spi_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 1, retry, + delay); + return 0; +} + +static int cts_spi_writew(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay) +{ + u8 buff[8]; + +// cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x val: 0x%04x", +// cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, addr, w); + + if (cts_dev->rtdata.addr_width == 2) { + put_unaligned_be16(addr, buff); + } else if (cts_dev->rtdata.addr_width == 3) { + put_unaligned_be24(addr, buff); + } else { + cts_err("Writew invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + put_unaligned_le16(w, buff + cts_dev->rtdata.addr_width); + + return cts_plat_spi_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 2, retry, + delay); + return 0; +} + +static int cts_spi_writel(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay) +{ + u8 buff[8]; + +// cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x val: 0x%08x", +// cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, addr, l); + + if (cts_dev->rtdata.addr_width == 2) { + put_unaligned_be16(addr, buff); + } else if (cts_dev->rtdata.addr_width == 3) { + put_unaligned_be24(addr, buff); + } else { + cts_err("Writel invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + put_unaligned_le32(l, buff + cts_dev->rtdata.addr_width); + + return cts_plat_spi_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 4, retry, + delay); + return 0; +} + +static int cts_spi_writesb(const struct cts_device *cts_dev, u32 addr, + const u8 *src, size_t len, int retry, int delay) +{ +#if 1 + int ret; + u8 *data; + size_t max_xfer_size; + size_t payload_len; + size_t xfer_len; + +// cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x len: %zu", +// cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, addr, len); + + max_xfer_size = cts_plat_get_max_spi_xfer_size(cts_dev->pdata); + data = cts_plat_get_spi_xfer_buf(cts_dev->pdata, len); + while (len) { + payload_len = + min((size_t) (max_xfer_size - cts_dev->rtdata.addr_width), + len); + xfer_len = payload_len + cts_dev->rtdata.addr_width; + + if (cts_dev->rtdata.addr_width == 2) { + put_unaligned_be16(addr, data); + } else if (cts_dev->rtdata.addr_width == 3) { + put_unaligned_be24(addr, data); + } else { + cts_err("Writesb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + memcpy(data + cts_dev->rtdata.addr_width, src, payload_len); + + ret = + cts_plat_spi_write(cts_dev->pdata, + cts_dev->rtdata.slave_addr, data, + xfer_len, retry, delay); + if (ret) { + cts_err("Platform i2c write failed %d", ret); + return ret; + } + + src += payload_len; + len -= payload_len; + addr += payload_len; + } +#endif + return 0; +} + +static int cts_spi_readb(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay) +{ + u8 addr_buf[4]; + +// cts_dbg("Readb from slave_addr: 0x%02x reg: 0x%0*x", +// cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, addr); + + if (cts_dev->rtdata.addr_width == 2) { + put_unaligned_be16(addr, addr_buf); + } else if (cts_dev->rtdata.addr_width == 3) { + put_unaligned_be24(addr, addr_buf); + } else { + cts_err("Readb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + return cts_plat_spi_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, b, 1, + retry, delay); +} + +static int cts_spi_readw(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + u8 buff[2]; + +// cts_dbg("Readw from slave_addr: 0x%02x reg: 0x%0*x", +// cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, addr); + + if (cts_dev->rtdata.addr_width == 2) { + put_unaligned_be16(addr, addr_buf); + } else if (cts_dev->rtdata.addr_width == 3) { + put_unaligned_be24(addr, addr_buf); + } else { + cts_err("Readw invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_spi_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, buff, 2, + retry, delay); + if (ret == 0) { + *w = get_unaligned_le16(buff); + } + + return ret; +} + +static int cts_spi_readl(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + u8 buff[4]; + +// cts_dbg("Readl from slave_addr: 0x%02x reg: 0x%0*x", +// cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, addr); + + if (cts_dev->rtdata.addr_width == 2) { + put_unaligned_be16(addr, addr_buf); + } else if (cts_dev->rtdata.addr_width == 3) { + put_unaligned_be24(addr, addr_buf); + } else { + cts_err("Readl invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_spi_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, buff, 4, + retry, delay); + if (ret == 0) { + *l = get_unaligned_le32(buff); + } + + return ret; + return 0; +} + +static int cts_spi_readsb(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + size_t max_xfer_size, xfer_len; + +// cts_dbg("Readsb from slave_addr: 0x%02x reg: 0x%0*x len: %zu", +// cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, addr, len); + + max_xfer_size = cts_plat_get_max_spi_xfer_size(cts_dev->pdata); + while (len) { + xfer_len = min(max_xfer_size, len); + + if (cts_dev->rtdata.addr_width == 2) { + put_unaligned_be16(addr, addr_buf); + } else if (cts_dev->rtdata.addr_width == 3) { + put_unaligned_be24(addr, addr_buf); + } else { + cts_err("Readsb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = + cts_plat_spi_read(cts_dev->pdata, + cts_dev->rtdata.slave_addr, addr_buf, + cts_dev->rtdata.addr_width, dst, xfer_len, + retry, delay); + if (ret) { + cts_err("Platform i2c read failed %d", ret); + return ret; + } + + dst += xfer_len; + len -= xfer_len; + addr += xfer_len; + } + return 0; +} + +static int cts_spi_readsb_delay_idle(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay, int idle) +{ + int ret; + u8 addr_buf[4]; + size_t max_xfer_size, xfer_len; + +// cts_dbg("Readsb from slave_addr: 0x%02x reg: 0x%0*x len: %zu", +// cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, addr, len); + + max_xfer_size = cts_plat_get_max_spi_xfer_size(cts_dev->pdata); + while (len) { + xfer_len = min(max_xfer_size, len); + + if (cts_dev->rtdata.addr_width == 2) { + put_unaligned_be16(addr, addr_buf); + } else if (cts_dev->rtdata.addr_width == 3) { + put_unaligned_be24(addr, addr_buf); + } else { + cts_err("Readsb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = + cts_plat_spi_read_delay_idle(cts_dev->pdata, + cts_dev->rtdata.slave_addr, + addr_buf, + cts_dev->rtdata.addr_width, + dst, xfer_len, retry, delay, + idle); + if (ret) { + cts_err("Platform i2c read failed %d", ret); + return ret; + } + + dst += xfer_len; + len -= xfer_len; + addr += xfer_len; + } + return 0; +} + +#endif /* CONFIG_CTS_I2C_HOST */ + +static inline int cts_dev_writeb(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_writeb(cts_dev, addr, b, retry, delay); +#else /* CONFIG_CTS_I2C_HOST */ + return cts_spi_writeb(cts_dev, addr, b, retry, delay); +#endif /* CONFIG_CTS_I2C_HOST */ +} + +static inline int cts_dev_writew(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_writew(cts_dev, addr, w, retry, delay); +#else /* CONFIG_CTS_I2C_HOST */ + return cts_spi_writew(cts_dev, addr, w, retry, delay); +#endif /* CONFIG_CTS_I2C_HOST */ +} + +static inline int cts_dev_writel(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_writel(cts_dev, addr, l, retry, delay); +#else /* CONFIG_CTS_I2C_HOST */ + return cts_spi_writel(cts_dev, addr, l, retry, delay); +#endif /* CONFIG_CTS_I2C_HOST */ +} + +static inline int cts_dev_writesb(const struct cts_device *cts_dev, u32 addr, + const u8 *src, size_t len, int retry, + int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_writesb(cts_dev, addr, src, len, retry, delay); +#else /* CONFIG_CTS_I2C_HOST */ + return cts_spi_writesb(cts_dev, addr, src, len, retry, delay); +#endif /* CONFIG_CTS_I2C_HOST */ +} + +static inline int cts_dev_readb(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readb(cts_dev, addr, b, retry, delay); +#else /* CONFIG_CTS_I2C_HOST */ + return cts_spi_readb(cts_dev, addr, b, retry, delay); +#endif /* CONFIG_CTS_I2C_HOST */ +} + +static inline int cts_dev_readw(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readw(cts_dev, addr, w, retry, delay); +#else /* CONFIG_CTS_I2C_HOST */ + return cts_spi_readw(cts_dev, addr, w, retry, delay);; +#endif /* CONFIG_CTS_I2C_HOST */ +} + +static inline int cts_dev_readl(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readl(cts_dev, addr, l, retry, delay); +#else /* CONFIG_CTS_I2C_HOST */ + return cts_spi_readl(cts_dev, addr, l, retry, delay); +#endif /* CONFIG_CTS_I2C_HOST */ +} + +static inline int cts_dev_readsb(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readsb(cts_dev, addr, dst, len, retry, delay); +#else /* CONFIG_CTS_I2C_HOST */ + return cts_spi_readsb(cts_dev, addr, dst, len, retry, delay); +#endif /* CONFIG_CTS_I2C_HOST */ +} + +static inline int cts_dev_readsb_delay_idle(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, + int retry, int delay, int idle) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readsb(cts_dev, addr, dst, len, retry, delay); +#else /* CONFIG_CTS_I2C_HOST */ + return cts_spi_readsb_delay_idle(cts_dev, addr, dst, len, retry, delay, + idle); +#endif /* CONFIG_CTS_I2C_HOST */ +} + +#ifdef CFG_CTS_UPDATE_CRCCHECK +int cts_sram_writesb_boot_crc_retry(const struct cts_device *cts_dev, + size_t len, u32 crc, int retry) +{ + int ret = 0, retries; + + retries = 0; + do { + ret = cts_dev_writel(cts_dev, 0x015ff0, 0xCC33555A, 3, 1); + if (ret != 0) { + cts_err("SRAM writesb failed %d", ret); + continue; + } + + ret = cts_dev_writel(cts_dev, 0x08fffc, crc, 3, 1); + if (ret != 0) { + cts_err("SRAM writesb failed %d", ret); + continue; + } + + ret = cts_dev_writel(cts_dev, 0x08fff8, len, 3, 1); + if (ret != 0) { + cts_err("SRAM writesb failed %d", ret); + continue; + } + + break; + } while (retries++ < retry); + + return ret; +} +#endif + +static int cts_write_sram_normal_mode(const struct cts_device *cts_dev, + u32 addr, const void *src, size_t len, + int retry, int delay) +{ + int i, ret; + u8 buff[5]; + + for (i = 0; i < len; i++) { + put_unaligned_le32(addr, buff); + buff[4] = *(u8 *) src; + + addr++; + src++; + + ret = cts_dev_writesb(cts_dev, + CTS_DEVICE_FW_REG_DEBUG_INTF, buff, 5, + retry, delay); + if (ret) { + cts_err("Write rDEBUG_INTF len=5B failed %d", ret); + return ret; + } + } + + return 0; +} + +int cts_sram_writeb_retry(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + return cts_dev_writeb(cts_dev, addr, b, retry, delay); + } else { + return cts_write_sram_normal_mode(cts_dev, addr, &b, 1, retry, + delay); + } +} + +int cts_sram_writew_retry(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay) +{ + u8 buff[2]; + + if (cts_dev->rtdata.program_mode) { + return cts_dev_writew(cts_dev, addr, w, retry, delay); + } else { + put_unaligned_le16(w, buff); + + return cts_write_sram_normal_mode(cts_dev, addr, buff, 2, retry, + delay); + } +} + +int cts_sram_writel_retry(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay) +{ + u8 buff[4]; + + if (cts_dev->rtdata.program_mode) { + return cts_dev_writel(cts_dev, addr, l, retry, delay); + } else { + put_unaligned_le32(l, buff); + + return cts_write_sram_normal_mode(cts_dev, addr, buff, 4, retry, + delay); + } +} + +int cts_sram_writesb_retry(const struct cts_device *cts_dev, + u32 addr, const void *src, size_t len, int retry, + int delay) +{ + if (cts_dev->rtdata.program_mode) { + return cts_dev_writesb(cts_dev, addr, src, len, retry, delay); + } else { + return cts_write_sram_normal_mode(cts_dev, addr, src, len, + retry, delay); + } +} + +static int cts_calc_sram_crc(const struct cts_device *cts_dev, + u32 sram_addr, size_t size, u32 *crc) +{ + cts_info("Calc crc from sram 0x%06x size %zu", sram_addr, size); + + return cts_dev->hwdata->sfctrl->ops->calc_sram_crc(cts_dev, + sram_addr, size, + crc); +} + +int cts_sram_writesb_check_crc_retry(const struct cts_device *cts_dev, + u32 addr, const void *src, size_t len, + u32 crc, int retry) +{ + int ret, retries; + + retries = 0; + do { + u32 crc_sram; + + retries++; + + ret = cts_sram_writesb(cts_dev, 0, src, len); + if (ret != 0) { + cts_err("SRAM writesb failed %d", ret); + continue; + } + + ret = cts_calc_sram_crc(cts_dev, 0, len, &crc_sram); + if (ret != 0) { + cts_err("Get CRC for sram writesb failed %d retries %d", + ret, retries); + continue; + } + + if (crc == crc_sram) { + return 0; + } + + cts_err + ("Check CRC for sram writesb mismatch %x != %x retries %d", + crc, crc_sram, retries); + ret = -EFAULT; + } while (retries < retry); + + return ret; +} + +static int cts_read_sram_normal_mode(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay) +{ + int i, ret; + + for (i = 0; i < len; i++) { + ret = cts_dev_writel(cts_dev, + CTS_DEVICE_FW_REG_DEBUG_INTF, addr, retry, + delay); + if (ret) { + cts_err("Write addr to rDEBUG_INTF failed %d", ret); + return ret; + } + + ret = cts_dev_readb(cts_dev, + CTS_DEVICE_FW_REG_DEBUG_INTF + 4, + (u8 *) dst, retry, delay); + if (ret) { + cts_err("Read value from rDEBUG_INTF + 4 failed %d", + ret); + return ret; + } + + addr++; + dst++; + } + + return 0; +} + +int cts_sram_readb_retry(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + return cts_dev_readb(cts_dev, addr, b, retry, delay); + } else { + return cts_read_sram_normal_mode(cts_dev, addr, b, 1, retry, + delay); + } +} + +int cts_sram_readw_retry(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay) +{ + int ret; + u8 buff[2]; + + if (cts_dev->rtdata.program_mode) { + return cts_dev_readw(cts_dev, addr, w, retry, delay); + } else { + ret = + cts_read_sram_normal_mode(cts_dev, addr, buff, 2, retry, + delay); + if (ret) { + cts_err("SRAM readw in normal mode failed %d", ret); + return ret; + } + + *w = get_unaligned_le16(buff); + + return 0; + } +} + +int cts_sram_readl_retry(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay) +{ + int ret; + u8 buff[4]; + + if (cts_dev->rtdata.program_mode) { + return cts_dev_readl(cts_dev, addr, l, retry, delay); + } else { + ret = + cts_read_sram_normal_mode(cts_dev, addr, buff, 4, retry, + delay); + if (ret) { + cts_err("SRAM readl in normal mode failed %d", ret); + return ret; + } + + *l = get_unaligned_le32(buff); + + return 0; + } +} + +int cts_sram_readsb_retry(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + return cts_dev_readsb(cts_dev, addr, dst, len, retry, delay); + } else { + return cts_read_sram_normal_mode(cts_dev, addr, dst, len, retry, + delay); + } +} + +int cts_fw_reg_writeb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 b, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Writeb to fw reg 0x%04x under program mode", reg_addr); + return -ENODEV; + } + + return cts_dev_writeb(cts_dev, reg_addr, b, retry, delay); +} + +int cts_fw_reg_writew_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 w, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Writew to fw reg 0x%04x under program mode", reg_addr); + return -ENODEV; + } + + return cts_dev_writew(cts_dev, reg_addr, w, retry, delay); +} + +int cts_fw_reg_writel_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 l, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Writel to fw reg 0x%04x under program mode", reg_addr); + return -ENODEV; + } + + return cts_dev_writel(cts_dev, reg_addr, l, retry, delay); +} + +int cts_fw_reg_writesb_retry(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len, + int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Writesb to fw reg 0x%04x under program mode", + reg_addr); + return -ENODEV; + } + + return cts_dev_writesb(cts_dev, reg_addr, src, len, retry, delay); +} + +int cts_fw_reg_readb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readb from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readb(cts_dev, reg_addr, b, retry, delay); +} + +int cts_fw_reg_readw_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readw from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readw(cts_dev, reg_addr, w, retry, delay); +} + +int cts_fw_reg_readl_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readl from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readl(cts_dev, reg_addr, l, retry, delay); +} + +int cts_fw_reg_readsb_retry(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, int retry, + int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readsb from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readsb(cts_dev, reg_addr, dst, len, retry, delay); +} + +int cts_fw_reg_readsb_retry_delay_idle(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, + int retry, int delay, int idle) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readsb from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readsb_delay_idle(cts_dev, reg_addr, dst, len, retry, + delay, idle); +} + +int cts_hw_reg_writeb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 b, int retry, int delay) +{ + return cts_sram_writeb_retry(cts_dev, reg_addr, b, retry, delay); +} + +int cts_hw_reg_writew_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 w, int retry, int delay) +{ + return cts_sram_writew_retry(cts_dev, reg_addr, w, retry, delay); +} + +int cts_hw_reg_writel_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 l, int retry, int delay) +{ + return cts_sram_writel_retry(cts_dev, reg_addr, l, retry, delay); +} + +int cts_hw_reg_writesb_retry(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len, + int retry, int delay) +{ + return cts_sram_writesb_retry(cts_dev, reg_addr, src, len, retry, + delay); +} + +int cts_hw_reg_readb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b, int retry, int delay) +{ + return cts_sram_readb_retry(cts_dev, reg_addr, b, retry, delay); +} + +int cts_hw_reg_readw_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w, int retry, int delay) +{ + return cts_sram_readw_retry(cts_dev, reg_addr, w, retry, delay); +} + +int cts_hw_reg_readl_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l, int retry, int delay) +{ + return cts_sram_readl_retry(cts_dev, reg_addr, l, retry, delay); +} + +int cts_hw_reg_readsb_retry(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, int retry, + int delay) +{ + return cts_sram_readsb_retry(cts_dev, reg_addr, dst, len, retry, delay); +} + +#define CTS_DEV_HW_REG_DDI_REG_CTRL (0x3002Cu) + +static int icnl9911s_set_access_ddi_reg(struct cts_device *cts_dev, bool enable) +{ + int ret; + u8 access_flag; + + cts_info("ICNL9911S %s access ddi reg", enable ? "enable" : "disable"); + + ret = cts_hw_reg_readb(cts_dev, CTS_DEV_HW_REG_DDI_REG_CTRL, &access_flag); + if (ret) { + cts_err("Read HW_REG_DDI_REG_CTRL failed %d", ret); + return ret; + } + + access_flag = enable ? (access_flag | 0x01) : (access_flag & (~0x01)); + ret = cts_hw_reg_writeb(cts_dev, CTS_DEV_HW_REG_DDI_REG_CTRL, access_flag); + if (ret) { + cts_err("Write HW_REG_DDI_REG_CTRL %02x failed %d", access_flag, ret); + return ret; + } + + ret = cts_hw_reg_writeb(cts_dev, 0x30074, enable ? 1 : 0); + if (ret) { + cts_err("Write 0x30074 failed %d", ret); + return ret; + } + + ret = cts_hw_reg_writew(cts_dev, 0x3DFF0, enable ? 0x595A : 0x5A5A); + if (ret) { + cts_err("Write password to F0 failed %d", ret); + return ret; + } + ret = cts_hw_reg_writew(cts_dev, 0x3DFF4, enable ? 0xA6A5 : 0x5A5A); + if (ret) { + cts_err("Write password to F1 failed %d", ret); + return ret; + } + + return 0; +} + +const static struct cts_sfctrl icnl9911_sfctrl = { + .reg_base = 0x34000, + .xchg_sram_base = (80 - 1) * 1024, + .xchg_sram_size = 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_sfctrl icnl9911s_sfctrl = { + .reg_base = 0x34000, + .xchg_sram_base = (64 - 1) * 1024, + .xchg_sram_size = 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_sfctrl icnl9911c_sfctrl = { + .reg_base = 0x34000, + .xchg_sram_base = (64 - 1) * 1024, + .xchg_sram_size = 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_device_hwdata cts_device_hwdatas[] = { + { + .name = "ICNL9911", + .hwid = CTS_DEV_HWID_ICNL9911, + .fwid = CTS_DEV_FWID_ICNL9911, + .num_row = 32, + .num_col = 18, + .sram_size = 80 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9911_sfctrl, + }, + { + .name = "ICNL9911S", + .hwid = CTS_DEV_HWID_ICNL9911S, + .fwid = CTS_DEV_FWID_ICNL9911S, + .num_row = 32, + .num_col = 18, + .sram_size = 64 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9911s_sfctrl, + }, + { + .name = "ICNL9911C", + .hwid = CTS_DEV_HWID_ICNL9911C, + .fwid = CTS_DEV_FWID_ICNL9911C, + .num_row = 32, + .num_col = 18, + .sram_size = 64 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9911c_sfctrl, + .enable_access_ddi_reg = icnl9911s_set_access_ddi_reg, + } +}; + +static int cts_init_device_hwdata(struct cts_device *cts_dev, + u32 hwid, u16 fwid) +{ + int i; + + cts_info("Init hardware data hwid: %06x fwid: %04x", hwid, fwid); + + for (i = 0; i < ARRAY_SIZE(cts_device_hwdatas); i++) { + if (hwid == cts_device_hwdatas[i].hwid || + fwid == cts_device_hwdatas[i].fwid) { + cts_dev->hwdata = &cts_device_hwdatas[i]; + return 0; + } + } + + return -EINVAL; +} + +void cts_lock_device(const struct cts_device *cts_dev) +{ + cts_dbg("*** Lock ***"); + + rt_mutex_lock(&dev_lock); +} + +void cts_unlock_device(const struct cts_device *cts_dev) +{ + cts_dbg("### Un-Lock ###"); + + rt_mutex_unlock(&dev_lock); +} + +int cts_set_work_mode(const struct cts_device *cts_dev, u8 mode) +{ + cts_info("Set work mode to %u", mode); + + return cts_fw_reg_writeb(cts_dev, CTS_DEVICE_FW_REG_WORK_MODE, mode); +} + +int cts_get_work_mode(const struct cts_device *cts_dev, u8 *mode) +{ + return cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_WORK_MODE, mode); +} + +int cts_get_firmware_version(const struct cts_device *cts_dev, u16 *version) +{ + int ret = cts_fw_reg_readw(cts_dev, CTS_DEVICE_FW_REG_VERSION, version); + + if (ret) { + *version = 0; + } else { + *version = be16_to_cpup(version); + } + + return ret; +} + +int cts_get_ddi_version(const struct cts_device *cts_dev, u8 *version) +{ + int ret = + cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_DDI_VERSION, version); + + if (ret) { + *version = 0; + } + return ret; +} + +int cts_get_lib_version(const struct cts_device *cts_dev, u16 *lib_version) +{ + u8 main_version, sub_version; + int ret; + + ret = + cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_FW_LIB_MAIN_VERSION, + &main_version); + if (ret) { + cts_err("Get fw lib main version failed %d", ret); + return ret; + } + + ret = + cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_FW_LIB_SUB_VERSION, + &sub_version); + if (ret) { + cts_err("Get fw lib sub version failed %d", ret); + return ret; + } + + *lib_version = (main_version << 8) + sub_version; + return 0; +} + +int cts_get_data_ready_flag(const struct cts_device *cts_dev, u8 *flag) +{ + return cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_DATA_READY, flag); +} + +int cts_clr_data_ready_flag(const struct cts_device *cts_dev) +{ + return cts_fw_reg_writeb(cts_dev, CTS_DEVICE_FW_REG_DATA_READY, 0); +} + +int cts_send_command(const struct cts_device *cts_dev, u8 cmd) +{ + cts_info("Send command 0x%02x", cmd); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Send command %u while chip in program mode", cmd); + return -ENODEV; + } + + return cts_fw_reg_writeb_retry(cts_dev, CTS_DEVICE_FW_REG_CMD, cmd, 3, + 0); +} + +static int cts_get_touchinfo(const struct cts_device *cts_dev, + struct cts_device_touch_info *touch_info) +{ + cts_dbg("Get touch info"); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Get touch info in program mode"); + return -ENODEV; + } + + if (cts_dev->rtdata.suspended) { + cts_warn("Get touch info while is suspended"); + return -ENODEV; + } + + return cts_fw_reg_readsb(cts_dev, CTS_DEVICE_FW_REG_TOUCH_INFO, + touch_info, sizeof(*touch_info)); +} + +int cts_get_panel_param(const struct cts_device *cts_dev, + void *param, size_t size) +{ + cts_info("Get panel parameter"); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Get panel parameter in program mode"); + return -ENODEV; + } + + return cts_fw_reg_readsb(cts_dev, + CTS_DEVICE_FW_REG_PANEL_PARAM, param, size); +} + +int cts_set_panel_param(const struct cts_device *cts_dev, + const void *param, size_t size) +{ + cts_info("Set panel parameter"); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Set panel parameter in program mode"); + return -ENODEV; + } + return cts_fw_reg_writesb(cts_dev, + CTS_DEVICE_FW_REG_PANEL_PARAM, param, size); +} + +int cts_get_x_resolution(const struct cts_device *cts_dev, u16 *resolution) +{ + return cts_fw_reg_readw(cts_dev, CTS_DEVICE_FW_REG_X_RESOLUTION, + resolution); +} + +int cts_get_y_resolution(const struct cts_device *cts_dev, u16 *resolution) +{ + return cts_fw_reg_readw(cts_dev, CTS_DEVICE_FW_REG_Y_RESOLUTION, + resolution); +} + +int cts_get_num_rows(const struct cts_device *cts_dev, u8 *num_rows) +{ + return cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_NUM_TX, num_rows); +} + +int cts_get_num_cols(const struct cts_device *cts_dev, u8 *num_cols) +{ + return cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_NUM_RX, num_cols); +} + +#ifdef CONFIG_CTS_LEGACY_TOOL +int cts_enable_get_rawdata(const struct cts_device *cts_dev) +{ + cts_info("Enable get raw/diff data"); + return cts_send_command(cts_dev, CTS_CMD_ENABLE_READ_RAWDATA); +} + +int cts_disable_get_rawdata(const struct cts_device *cts_dev) +{ + cts_info("Disable get raw/diff data"); + return cts_send_command(cts_dev, CTS_CMD_DISABLE_READ_RAWDATA); +} + +static void tsdata_flip_x(void *tsdata, u8 fw_rows, u8 fw_cols) +{ + u8 r, c; + u16 *data; + + data = (u16 *)tsdata; + for (r = 0; r < fw_rows; r++) { + for (c = 0; c < fw_cols / 2; c++) { + swap(data[r * fw_cols + c], + data[r * fw_cols + wrap(fw_cols, c)]); + } + } +} + +static void tsdata_flip_y(void *tsdata, u8 fw_rows, u8 fw_cols) +{ + u8 r, c; + u16 *data; + + data = (u16 *)tsdata; + for (r = 0; r < fw_rows / 2; r++) { + for (c = 0; c < fw_cols; c++) { + swap(data[r * fw_cols + c], + data[wrap(fw_rows, r) * fw_cols + c]); + } + } +} + +int cts_get_rawdata(const struct cts_device *cts_dev, void *buf) +{ + int i, ret; + u8 ready; + u8 retries = 5; + + cts_info("Get rawdata"); + + /** - Wait data ready flag set */ + for (i = 0; i < 100; i++) { + mdelay(10); + ret = cts_get_data_ready_flag(cts_dev, &ready); + if (ret) { + cts_err("Get data ready flag failed %d", ret); + continue; + } + if (ready) { + goto read_rawdata; + } + } + + cts_err("Wait data ready timeout"); + return -ETIMEDOUT; + + /* Read rawdata */ +read_rawdata: + do { + ret = + cts_fw_reg_readsb_delay_idle(cts_dev, + CTS_DEVICE_FW_REG_RAW_DATA, + buf, + cts_dev->fwdata.rows * + cts_dev->fwdata.cols * 2, 500); + if (ret) { + cts_err("Read rawdata failed %d", ret); + } else { + break; + } + } while (--retries > 0); + + for (i = 0; i < 5; i++) { + int r = cts_clr_data_ready_flag(cts_dev); + if (r) { + cts_err("Clear data ready flag failed %d", r); + } else { + break; + } + } + + if (cts_dev->fwdata.flip_x) { + tsdata_flip_x(buf, cts_dev->fwdata.rows, cts_dev->fwdata.cols); + } + if (cts_dev->fwdata.flip_y) { + tsdata_flip_y(buf, cts_dev->fwdata.rows, cts_dev->fwdata.cols); + } + + return ret; +} + +int cts_get_diffdata(const struct cts_device *cts_dev, void *buf) +{ + int i, j, ret; + u8 ready; + u8 retries = 5; + u8 *cache_buf; + + cts_info("Get diffdata"); + cache_buf = kzalloc((cts_dev->fwdata.rows + 2) * (cts_dev->fwdata.cols + + 2) * 2, GFP_KERNEL); + if (cache_buf == NULL) { + cts_err("Get diffdata: malloc error"); + ret = -ENOMEM; + goto get_diff_exit; + } + /** - Wait data ready flag set */ + for (i = 0; i < 1000; i++) { + mdelay(1); + ret = cts_get_data_ready_flag(cts_dev, &ready); + if (ret) { + cts_err("Get data ready flag failed %d", ret); + goto get_diff_free_buf; + } + if (ready) { + break; + } + } + if (i == 1000) { + ret = -ETIMEDOUT; + goto get_diff_free_buf; + } + do { + ret = + cts_fw_reg_readsb_delay_idle(cts_dev, + CTS_DEVICE_FW_REG_DIFF_DATA, + cache_buf, + (cts_dev->fwdata.rows + + 2) * (cts_dev->fwdata.cols + + 2) * 2, 500); + if (ret) { + cts_err("Read diffdata failed %d", ret); + } else { + break; + } + } while (--retries > 0); + + if (ret == 0) { + for (i = 0; i < cts_dev->fwdata.rows; i++) { + for (j = 0; j < cts_dev->fwdata.cols; j++) { + ((u8 *) buf)[2 * (i * cts_dev->fwdata.cols + j)] = + cache_buf[2 * + ((i + 1) * (cts_dev->fwdata.cols + 2) + + j + 1)]; + ((u8 *) buf)[2 * (i * cts_dev->fwdata.cols + j) + 1] = + cache_buf[2 * + ((i + 1) * (cts_dev->fwdata.cols + 2) + + j + 1) + 1]; + } + } + } + + for (i = 0; i < 5; i++) { + int r = cts_clr_data_ready_flag(cts_dev); + if (r) { + cts_err("Clear data ready flag failed %d", r); + } else { + break; + } + } + + if (cts_dev->fwdata.flip_x) { + tsdata_flip_x(buf, cts_dev->fwdata.rows, cts_dev->fwdata.cols); + } + if (cts_dev->fwdata.flip_y) { + tsdata_flip_y(buf, cts_dev->fwdata.rows, cts_dev->fwdata.cols); + } +get_diff_free_buf: + kfree(cache_buf); +get_diff_exit: + return ret; +} +#endif /* CONFIG_CTS_LEGACY_TOOL */ + +static int cts_get_dev_boot_mode(const struct cts_device *cts_dev, + u8 *boot_mode) +{ + int ret; + + ret = cts_hw_reg_readb_retry(cts_dev, + CTS_DEV_HW_REG_CURRENT_MODE, boot_mode, 5, + 10); + if (ret) { + cts_err("Read boot mode failed %d", ret); + return ret; + } + + *boot_mode &= CTS_DEV_BOOT_MODE_MASK; + + cts_info("Curr dev boot mode: %u(%s)", *boot_mode, + cts_dev_boot_mode2str(*boot_mode)); + return 0; +} + +static int cts_set_dev_boot_mode(const struct cts_device *cts_dev, u8 boot_mode) +{ + int ret; + + cts_info("Set dev boot mode to %u(%s)", boot_mode, + cts_dev_boot_mode2str(boot_mode)); + + ret = cts_hw_reg_writeb_retry(cts_dev, CTS_DEV_HW_REG_BOOT_MODE, + boot_mode, 5, 5); + if (ret) { + cts_err("Write hw register BOOT_MODE failed %d", ret); + return ret; + } + + cts_info("cts_set_dev_boot_mode exit"); + + return 0; +} + +static int cts_init_fwdata(struct cts_device *cts_dev) +{ + struct cts_device_fwdata *fwdata = &cts_dev->fwdata; + u8 val; + int ret; + + cts_info("Init firmware data"); + + if (cts_dev->rtdata.program_mode) { + cts_err("Init firmware data while in program mode"); + return -EINVAL; + } + + ret = cts_get_firmware_version(cts_dev, &fwdata->version); + if (ret) { + cts_err("Read firmware version failed %d", ret); + return ret; + } + cts_info(" %-24s: %04x", "Firmware version", fwdata->version); + + ret = cts_get_lib_version(cts_dev, &fwdata->lib_version); + if (ret) { + cts_err("Read firmware Lib version failed %d", ret); + } + cts_info(" %-24s: v%x.%x", "Fimrware lib verion", + (u8)(fwdata->lib_version >> 8), + (u8)(fwdata->lib_version)); + + ret = cts_get_ddi_version(cts_dev, &fwdata->ddi_version); + if (ret) { + cts_err("Read ddi version failed %d", ret); + return ret; + } + cts_info(" %-24s: %02x", "DDI init code verion", fwdata->ddi_version); + + ret = cts_get_x_resolution(cts_dev, &fwdata->res_x); + if (ret) { + cts_err("Read firmware X resoltion failed %d", ret); + return ret; + } + cts_info(" %-24s: %u", "X resolution", fwdata->res_x); + + ret = cts_get_y_resolution(cts_dev, &fwdata->res_y); + if (ret) { + cts_err("Read firmware Y resolution failed %d", ret); + return ret; + } + cts_info(" %-24s: %u", "Y resolution", fwdata->res_y); + + ret = cts_get_num_rows(cts_dev, &fwdata->rows); + if (ret) { + cts_err("Read firmware num TX failed %d", ret); + return ret; + } + cts_info(" %-24s: %u", "Num rows", fwdata->rows); + + ret = cts_get_num_cols(cts_dev, &fwdata->cols); + if (ret) { + cts_err("Read firmware num RX failed %d", ret); + return ret; + } + cts_info(" %-24s: %u", "Num cols", fwdata->cols); + + ret = cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_FLAG_BITS, &val); + if (ret) { + cts_err("Read FW_REG_FLIP_X/Y failed %d", ret); + return ret; + } + cts_dev->fwdata.flip_x = !!(val & BIT(2)); + cts_dev->fwdata.flip_y = !!(val & BIT(3)); + cts_info(" %-24s: %s", "Flip X", + cts_dev->fwdata.flip_x ? "True" : "Flase"); + cts_info(" %-24s: %s", "Flip Y", + cts_dev->fwdata.flip_y ? "True" : "Flase"); + + ret = cts_fw_reg_readb(cts_dev, CTS_DEVICE_FW_REG_SWAP_AXES, &val); + if (ret) { + cts_err("Read FW_REG_SWAP_AXES failed %d", ret); + return ret; + } + cts_info(" %-24s: %s", "Swap axes", + cts_dev->fwdata.swap_axes ? "True" : "Flase"); + + ret = cts_fw_reg_readb(cts_dev, + CTS_DEVICE_FW_REG_INT_MODE, &fwdata->int_mode); + if (ret) { + cts_err("Read firmware Int mode failed %d", ret); + return ret; + } + cts_info(" %-24s: %s", "Int polarity", + (fwdata->int_mode == 0) ? "LOW" : "HIGH"); + + ret = cts_fw_reg_readw(cts_dev, + CTS_DEVICE_FW_REG_INT_KEEP_TIME, &fwdata->int_keep_time); + if (ret) { + cts_err("Read firmware Int keep time failed %d", ret); + return ret; + } + cts_info(" %-24s: %d", "Int keep time", fwdata->int_keep_time); + + ret = cts_fw_reg_readw(cts_dev, + CTS_DEVICE_FW_REG_RAWDATA_TARGET, &fwdata->rawdata_target); + if (ret) { + cts_err("Read firmware Raw dest value failed %d", ret); + return ret; + } + cts_info(" %-24s: %d", "Raw target value", fwdata->rawdata_target); + + + ret = cts_fw_reg_readb(cts_dev, + CTS_DEVICE_FW_REG_ESD_PROTECTION, &fwdata->esd_method); + if (ret) { + cts_err("Read firmware Esd method failed %d", ret); + return ret; + } + cts_info(" %-24s: %d", "Esd method", fwdata->esd_method); + + return 0; +} + + +#ifdef CFG_CTS_FW_LOG_REDIRECT +void cts_show_fw_log(struct cts_device *cts_dev) +{ + u8 len, max_len; + int ret; + u8 *data; + + max_len = cts_plat_get_max_fw_log_size(cts_dev->pdata); + data = cts_plat_get_fw_log_buf(cts_dev->pdata, max_len); + ret = + cts_fw_reg_readsb(cts_dev, CTS_DEVICE_FW_REG_TOUCH_INFO + 1, &len, + 1); + if (ret) { + cts_err("Get i2c print buf len error"); + return; + } + if (len >= max_len) { + len = max_len - 1; + } + ret = + cts_fw_reg_readsb(cts_dev, CTS_DEVICE_FW_REG_TOUCH_INFO + 2, data, + len); + if (ret) { + cts_err("Get i2c print buf error"); + return; + } + data[len] = '\0'; + printk("CTS-FW_LOG %s", data); + cts_fw_log_show_finish(cts_dev); +} +#endif + +int cts_irq_handler(struct cts_device *cts_dev) +{ + int ret; + + cts_dbg("Enter IRQ handler"); + + if (cts_dev->rtdata.program_mode) { + cts_err("IRQ triggered in program mode"); + return -EINVAL; + } + + if (unlikely(cts_dev->rtdata.suspended)) { +#ifdef CFG_CTS_GESTURE + if (cts_dev->rtdata.gesture_wakeup_enabled) { + struct cts_device_gesture_info gesture_info; + + ret = cts_get_gesture_info(cts_dev, + &gesture_info, + CFG_CTS_GESTURE_REPORT_TRACE); + if (ret) { + cts_warn("Get gesture info failed %d", ret); + //return ret; + } + + /** - Issure another suspend with gesture wakeup command to device + * after get gesture info. + */ + cts_send_command(cts_dev, CTS_CMD_SUSPEND_WITH_GESTURE); + + ret = + cts_plat_process_gesture_info(cts_dev->pdata, + &gesture_info); + if (ret) { + cts_err("Process gesture info failed %d", ret); + return ret; + } + } else { + cts_warn("IRQ triggered while device suspended " + "without gesture wakeup enable"); + } +#endif /* CFG_CTS_GESTURE */ + } else { + struct cts_device_touch_info *touch_info; + + touch_info = &cts_dev->pdata->touch_info; +#ifdef CFG_CTS_FW_LOG_REDIRECT + ret = + cts_fw_reg_readsb(cts_dev, CTS_DEVICE_FW_REG_TOUCH_INFO, + touch_info, 1); + if (ret) { + cts_err("Get vkey_state failed %d", ret); + return ret; + } + + if (touch_info->vkey_state == CTS_FW_LOG_REDIRECT_SIGN) { + if (cts_is_fw_log_redirect(cts_dev)) { + cts_show_fw_log(cts_dev); + } + return 0; + } +#endif + ret = cts_get_touchinfo(cts_dev, touch_info); + if (ret) { + cts_err("Get touch info failed %d", ret); + return ret; + } + + cts_dbg("Touch info: vkey_state %x, num_msg %u", + touch_info->vkey_state, touch_info->num_msg); + + ret = cts_plat_process_touch_msg(cts_dev->pdata, + touch_info->msgs, + touch_info->num_msg); + if (ret) { + cts_err("Process touch msg failed %d", ret); + return ret; + } +#ifdef CONFIG_CTS_VIRTUALKEY + ret = + cts_plat_process_vkey(cts_dev->pdata, + touch_info->vkey_state); + if (ret) { + cts_err("Process vkey failed %d", ret); + return ret; + } +#endif /* CONFIG_CTS_VIRTUALKEY */ + } + + return 0; +} + +bool cts_is_device_suspended(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.suspended; +} + +int cts_suspend_device(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Suspend device"); + + if (cts_dev->rtdata.suspended) { + cts_warn("Suspend device while already suspended"); + return 0; + } + if (cts_dev->rtdata.program_mode) { + cts_info("Quit programming mode before suspend"); + ret = cts_enter_normal_mode(cts_dev); + if (ret) { + cts_err("Failed to exit program mode before suspend:%d", + ret); + return ret; + } + } + ret = cts_send_command(cts_dev, + cts_dev->rtdata.gesture_wakeup_enabled ? + CTS_CMD_SUSPEND_WITH_GESTURE : CTS_CMD_SUSPEND); + + if (ret) { + cts_err("Suspend device failed %d", ret); + + return ret; + } + +#ifdef CFG_CTS_HAS_RESET_PIN + //cts_plat_set_reset(cts_dev->pdata, 0); +#endif + + cts_info("Device suspended ..."); + cts_dev->rtdata.suspended = true; + + return 0; +} + +int cts_resume_device(struct cts_device *cts_dev) +{ + int ret = 0; + int retries = 3; + + cts_info("Resume device"); + + /* Check whether device is in normal mode */ + while (--retries >= 0) { +#ifdef CFG_CTS_HAS_RESET_PIN + cts_plat_reset_device(cts_dev->pdata); +#endif + cts_set_normal_addr(cts_dev); +#ifdef CONFIG_CTS_I2C_HOST + if (cts_plat_is_i2c_online + (cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR)) +#else + if (cts_plat_is_normal_mode(cts_dev->pdata)) +#endif + { + break; + } + } + + if (retries < 0) { + const struct cts_firmware *firmware = NULL; + + cts_info("Need update firmware when resume"); + +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD + if (cts_dev->config_fw_name[0] != '\0') { + firmware = cts_request_firmware_from_fs(cts_dev, + cts_dev->config_fw_name); + } +#else + firmware = cts_request_firmware(cts_dev, + cts_dev->hwdata->hwid, cts_dev->hwdata->fwid, 0); +#endif + if (firmware) { + ret = cts_update_firmware(cts_dev, firmware, false); + cts_release_firmware(firmware); + + if (ret) { + cts_err("Update default firmware failed %d", + ret); + goto err_set_program_mode; + } + } else { + cts_err("Request default firmware failed %d, " + "please update manually!!", ret); + + goto err_set_program_mode; + } + } +#ifdef CONFIG_CTS_CHARGER_DETECT + if (cts_is_charger_exist(cts_dev)) { + int r = cts_set_dev_charger_attached(cts_dev, true); + if (r) { + cts_err("Set dev charger attached failed %d", r); + } + } +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) { + cts_enter_glove_mode(cts_dev); + } +#endif + +#ifdef CFG_CTS_FW_LOG_REDIRECT + if (cts_is_fw_log_redirect(cts_dev)) { + cts_enable_fw_log_redirect(cts_dev); + } +#endif + + cts_dev->rtdata.suspended = false; + return 0; + +err_set_program_mode: + cts_dev->rtdata.program_mode = true; + cts_dev->rtdata.slave_addr = CTS_DEV_PROGRAM_MODE_I2CADDR; + cts_dev->rtdata.addr_width = CTS_DEV_PROGRAM_MODE_ADDR_WIDTH; + + return ret; +} + +bool cts_is_device_program_mode(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.program_mode; +} + +static inline void cts_init_rtdata_with_normal_mode(struct cts_device *cts_dev) +{ + memset(&cts_dev->rtdata, 0, sizeof(cts_dev->rtdata)); + + cts_set_normal_addr(cts_dev); + cts_dev->rtdata.suspended = false; + cts_dev->rtdata.updating = false; + cts_dev->rtdata.testing = false; + cts_dev->rtdata.fw_log_redirect_enabled = false; + cts_dev->rtdata.glove_mode_enabled = false; +} + +int cts_enter_program_mode(struct cts_device *cts_dev) +{ + const static u8 magic_num[] = { 0xCC, 0x33, 0x55, 0x5A }; + u8 boot_mode; + int ret; + + cts_info("Enter program mode"); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Enter program mode while alredy in"); + //return 0; + } +#ifdef CONFIG_CTS_I2C_HOST + ret = cts_plat_i2c_write(cts_dev->pdata, + CTS_DEV_PROGRAM_MODE_I2CADDR, magic_num, 4, 5, + 10); + if (ret) { + cts_err("Write magic number to i2c_dev: 0x%02x failed %d", + CTS_DEV_PROGRAM_MODE_I2CADDR, ret); + return ret; + } + + cts_set_program_addr(cts_dev); + /* Write i2c deglitch register */ + ret = cts_hw_reg_writeb_retry(cts_dev, 0x37001, 0x0F, 5, 1); + if (ret) { + cts_err("Write i2c deglitch register failed\n"); + // FALL through + } +#else + cts_set_program_addr(cts_dev); + cts_plat_reset_device(cts_dev->pdata); + ret = cts_plat_spi_write(cts_dev->pdata, 0xcc, &magic_num[1], 3, 5, 10); + if (ret) { + cts_err("Write magic number to i2c_dev: 0x%02x failed %d", + CTS_DEV_PROGRAM_MODE_SPIADDR, ret); + return ret; + } +#endif /* CONFIG_CTS_I2C_HOST */ + ret = cts_get_dev_boot_mode(cts_dev, &boot_mode); + if (ret) { + cts_err("Read BOOT_MODE failed %d", ret); + return ret; + } +#ifdef CONFIG_CTS_I2C_HOST + if (boot_mode != CTS_DEV_BOOT_MODE_I2C_PROGRAM) +#else + if (boot_mode != CTS_DEV_BOOT_MODE_SPI_PROGRAM) +#endif + { + cts_err("BOOT_MODE readback %u != I2C/SPI PROMGRAM mode", + boot_mode); + return -EFAULT; + } + + return 0; +} + +const char *cts_dev_boot_mode2str(u8 boot_mode) +{ +#define case_boot_mode(mode) \ + case CTS_DEV_BOOT_MODE_ ## mode: return #mode "-BOOT" + + switch (boot_mode) { + case_boot_mode(FLASH); + case_boot_mode(I2C_PROGRAM); + case_boot_mode(SRAM); + case_boot_mode(SPI_PROGRAM); + default: + return "INVALID"; + } + +#undef case_boot_mode +} + +int cts_enter_normal_mode(struct cts_device *cts_dev) +{ + int ret = 0; + u8 boot_mode; + int retries; + u16 fwid = CTS_DEV_FWID_INVALID; + u8 auto_boot = 0; + u8 first_boot = 1; + + cts_info("Enter normal mode"); + + if (!cts_dev->rtdata.program_mode) { + cts_warn("Enter normal mode while already in"); + return 0; + } + + if (cts_dev->rtdata.has_flash) { + auto_boot = 1; + } +#ifdef CFG_CTS_UPDATE_CRCCHECK +/* + if (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911S) { + auto_boot = 1; + } +*/ + auto_boot = 1; +#endif + for (retries = 5; retries >= 0; retries--) { + if (first_boot == 1 || auto_boot == 0) { + cts_set_program_addr(cts_dev); + ret = + cts_set_dev_boot_mode(cts_dev, + CTS_DEV_BOOT_MODE_SRAM); + if (ret) { + cts_err + ("Set BOOT_MODE to SRAM failed %d, try to reset device", + ret); + } + mdelay(30); + } + first_boot = 0; +#ifdef CONFIG_CTS_I2C_HOST + if (cts_plat_is_i2c_online + (cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR)) { + cts_set_normal_addr(cts_dev); + } +#else + cts_set_normal_addr(cts_dev); +#endif + ret = cts_get_dev_boot_mode(cts_dev, &boot_mode); + if (ret) { + cts_err("Get BOOT_MODE failed %d", ret); + } + if (boot_mode != CTS_DEV_BOOT_MODE_SRAM) { + cts_err("Curr boot mode %u(%s) != SRAM_BOOT", + boot_mode, cts_dev_boot_mode2str(boot_mode)); + } else { + break; + } + ret = cts_get_fwid(cts_dev, &fwid); + if (ret) { + cts_err("Get firmware id failed %d, retries %d", ret, + retries); + } else { + if (fwid == CTS_DEV_FWID_ICNL9911 + || fwid == CTS_DEV_FWID_ICNL9911S + || fwid == CTS_DEV_FWID_ICNL9911C) { + cts_info("Get firmware id successful 0x%02x", + fwid); + break; + } + } + cts_plat_reset_device(cts_dev->pdata); + } + if (retries >= 0) { + ret = cts_init_fwdata(cts_dev); + if (ret) { + cts_err("Device init firmware data failed %d", ret); + return ret; + } + return 0; + } + cts_set_program_addr(cts_dev); + return ret; +} + +bool cts_is_device_enabled(const struct cts_device *cts_dev) +{ + return cts_dev->enabled; +} + +int cts_start_device(struct cts_device *cts_dev) +{ +#if defined(CONFIG_CTS_ESD_PROTECTION) || defined(CONFIG_CTS_CHARGER_DETECT) + struct chipone_ts_data *cts_data = + container_of(cts_dev, struct chipone_ts_data, cts_dev); +#endif + int ret; + + cts_info("Start device..."); + + if (cts_is_device_enabled(cts_dev)) { + cts_warn("Start device while already started"); + return 0; + } +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_enable_esd_protection(cts_data); +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CONFIG_CTS_CHARGER_DETECT + cts_start_charger_detect(cts_data); +#endif /* CONFIG_CTS_CHARGER_DETECT */ + + ret = cts_plat_enable_irq(cts_dev->pdata); + if (ret < 0) { + cts_err("Enable IRQ failed %d", ret); + return ret; + } + + cts_dev->enabled = true; + + cts_info("Start device successfully"); + + return 0; +} + +int cts_stop_device(struct cts_device *cts_dev) +{ + struct chipone_ts_data *cts_data = + container_of(cts_dev, struct chipone_ts_data, cts_dev); + int ret; + + cts_info("Stop device..."); + + if (!cts_is_device_enabled(cts_dev)) { + cts_warn("Stop device while halted"); + return 0; + } + + if (cts_is_firmware_updating(cts_dev)) { + cts_warn + ("Stop device while firmware updating, please try again"); + return -EAGAIN; + } + + ret = cts_plat_disable_irq(cts_dev->pdata); + if (ret < 0) { + cts_err("Disable IRQ failed %d", ret); + return ret; + } + + cts_dev->enabled = false; + +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_disable_esd_protection(cts_data); +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CONFIG_CTS_CHARGER_DETECT + cts_stop_charger_detect(cts_data); +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifndef CONFIG_GENERIC_HARDIRQS + if (work_pending(&cts_data->pdata->ts_irq_work)) { + cts_warn("IRQ work is pending .... flush it"); + flush_work(&cts_data->pdata->ts_irq_work); + } else { + cts_info("None IRQ work is pending"); + } +#endif + cts_info("Flush workqueue..."); + flush_workqueue(cts_data->workqueue); + + ret = cts_plat_release_all_touch(cts_dev->pdata); + if (ret) { + cts_err("Release all touch failed %d", ret); + return ret; + } +#ifdef CONFIG_CTS_VIRTUALKEY + ret = cts_plat_release_all_vkey(cts_dev->pdata); + if (ret) { + cts_err("Release all vkey failed %d", ret); + return ret; + } +#endif /* CONFIG_CTS_VIRTUALKEY */ + + return 0; +} + +#ifdef CONFIG_CTS_ESD_PROTECTION +int cts_start_device_esdrecover(struct cts_device *cts_dev) +{ + //struct chipone_ts_data *cts_data = + // container_of(cts_dev, struct chipone_ts_data, cts_dev); + int ret; + + cts_info("Start device..."); + + if (cts_is_device_enabled(cts_dev)) { + cts_warn("Start device while already started"); + return 0; + } + + ret = cts_plat_enable_irq(cts_dev->pdata); + if (ret < 0) { + cts_err("Enable IRQ failed %d", ret); + return ret; + } + + cts_dev->enabled = true; + + cts_info("Start device successfully"); + + return 0; +} + +int cts_stop_device_esdrecover(struct cts_device *cts_dev) +{ + struct chipone_ts_data *cts_data = + container_of(cts_dev, struct chipone_ts_data, cts_dev); + int ret; + + cts_info("Stop device..."); + + if (!cts_is_device_enabled(cts_dev)) { + cts_warn("Stop device while halted"); + return 0; + } + + if (cts_is_firmware_updating(cts_dev)) { + cts_warn + ("Stop device while firmware updating, please try again"); + return -EAGAIN; + } + + ret = cts_plat_disable_irq(cts_dev->pdata); + if (ret < 0) { + cts_err("Disable IRQ failed %d", ret); + return ret; + } + + cts_dev->enabled = false; + + flush_workqueue(cts_data->workqueue); + + ret = cts_plat_release_all_touch(cts_dev->pdata); + if (ret) { + cts_err("Release all touch failed %d", ret); + return ret; + } +#ifdef CONFIG_CTS_VIRTUALKEY + ret = cts_plat_release_all_vkey(cts_dev->pdata); + if (ret) { + cts_err("Release all vkey failed %d", ret); + return ret; + } +#endif /* CONFIG_CTS_VIRTUALKEY */ + + return 0; +} +#endif + +bool cts_is_fwid_valid(u16 fwid) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cts_device_hwdatas); i++) { + if (cts_device_hwdatas[i].fwid == fwid) { + return true; + } + } + + return false; +} + +static bool cts_is_hwid_valid(u32 hwid) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cts_device_hwdatas); i++) { + if (cts_device_hwdatas[i].hwid == hwid) { + return true; + } + } + + return false; +} + +int cts_get_fwid(struct cts_device *cts_dev, u16 *fwid) +{ + int ret; + + cts_info("Get device firmware id"); + + if (cts_dev->hwdata) { + *fwid = cts_dev->hwdata->fwid; + return 0; + } + + if (cts_dev->rtdata.program_mode) { + cts_err("Get device firmware id while in program mode"); + ret = -ENODEV; + goto err_out; + } + + ret = cts_fw_reg_readw_retry(cts_dev, + CTS_DEVICE_FW_REG_CHIP_TYPE, fwid, 5, 1); + if (ret) { + goto err_out; + } + + *fwid = be16_to_cpu(*fwid); + + cts_info("Device firmware id: %04x", *fwid); + + if (!cts_is_fwid_valid(*fwid)) { + cts_warn("Get invalid firmware id %04x", *fwid); + ret = -EINVAL; + goto err_out; + } + + return 0; + +err_out: + *fwid = CTS_DEV_FWID_INVALID; + return ret; +} + +int cts_get_hwid(struct cts_device *cts_dev, u32 *hwid) +{ + int ret; + + cts_info("Get device hardware id"); + + if (cts_dev->hwdata) { + *hwid = cts_dev->hwdata->hwid; + return 0; + } + + cts_info + ("Device hardware data not initialized, try to read from register"); + + if (!cts_dev->rtdata.program_mode) { + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + goto err_out; + } + } + + ret = + cts_hw_reg_readl_retry(cts_dev, CTS_DEV_HW_REG_HARDWARE_ID, hwid, 5, + 0); + if (ret) { + goto err_out; + } + + *hwid = le32_to_cpu(*hwid); + *hwid &= 0XFFFFFFF0; + cts_info("Device hardware id: %04x", *hwid); + + if (!cts_is_hwid_valid(*hwid)) { + cts_warn("Device hardware id %04x invalid", *hwid); + ret = -EINVAL; + goto err_out; + } + + return 0; + +err_out: + *hwid = CTS_DEV_HWID_INVALID; + return ret; +} + +int cts_probe_device(struct cts_device *cts_dev) +{ + int ret, retries = 0; + u16 fwid = CTS_DEV_FWID_INVALID; + u32 hwid = CTS_DEV_HWID_INVALID; + u16 device_fw_ver = 0; + cts_info("Probe device"); + +read_fwid: +#ifdef CONFIG_CTS_I2C_HOST + if (!cts_plat_is_i2c_online + (cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR)) { + cts_warn("Normal mode i2c addr is offline"); + } else +#else + if (!cts_plat_is_normal_mode(cts_dev->pdata)) { + cts_warn("Normal mode spi addr is offline"); + } else +#endif + { + cts_init_rtdata_with_normal_mode(cts_dev); + ret = cts_get_fwid(cts_dev, &fwid); + if (ret) { + cts_err("Get firmware id failed %d, retries %d", ret, + retries); + } else { + ret = cts_fw_reg_readw_retry(cts_dev, + CTS_DEVICE_FW_REG_VERSION, + &device_fw_ver, 5, 0); + if (ret) { + cts_err("Read firmware version failed %d", ret); + device_fw_ver = 0; + // TODO: Handle this error + } else { + device_fw_ver = be16_to_cpu(device_fw_ver); + cts_info("Device firmware version: %04x", + device_fw_ver); + } + goto init_hwdata; + } + } + + /** - Try to read hardware id, + it will enter program mode as normal */ + ret = cts_get_hwid(cts_dev, &hwid); + if (ret || hwid == CTS_DEV_HWID_INVALID) { + retries++; + + cts_err("Get hardware id failed %d retries %d", ret, retries); + + if (retries < 3) { + cts_plat_reset_device(cts_dev->pdata); + goto read_fwid; + } else { + return -ENODEV; + } + } + +init_hwdata: + ret = cts_init_device_hwdata(cts_dev, hwid, fwid); + if (ret) { + cts_err("Device hwid: %06x fwid: %04x not found", hwid, fwid); + return -ENODEV; + } + +#if 0 +#ifdef CFG_CTS_FIRMWARE_FORCE_UPDATE + cts_warn("Force update firmware"); + firmware = cts_request_firmware(cts_dev, + CTS_DEV_HWID_ANY, CTS_DEV_FWID_ANY, 0); +#else /* CFG_CTS_FIRMWARE_FORCE_UPDATE */ + firmware = cts_request_firmware(cts_dev, hwid, fwid, device_fw_ver); +#endif /* CFG_CTS_FIRMWARE_FORCE_UPDATE */ + + retries = 0; +update_firmware: + if (firmware) { + ++retries; + ret = cts_update_firmware(cts_dev, firmware, true); + if (ret) { + cts_err("Update firmware failed %d retries %d", ret, + retries); + + if (retries < 3) { + cts_plat_reset_device(cts_dev->pdata); + goto update_firmware; + } + } + cts_release_firmware(firmware); + } else { + if (fwid != CTS_DEV_FWID_INVALID) { + ret = cts_init_fwdata(cts_dev); + if (ret) { + cts_err("Device init firmware data failed %d", ret); + } + } + } +#endif + return 0; +} + +#ifdef CFG_CTS_GESTURE +void cts_enable_gesture_wakeup(struct cts_device *cts_dev) +{ + cts_info("Enable gesture wakeup"); + cts_dev->rtdata.gesture_wakeup_enabled = true; +} + +void cts_disable_gesture_wakeup(struct cts_device *cts_dev) +{ + cts_info("Disable gesture wakeup"); + cts_dev->rtdata.gesture_wakeup_enabled = false; +} + +bool cts_is_gesture_wakeup_enabled(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.gesture_wakeup_enabled; +} + +int cts_get_gesture_info(const struct cts_device *cts_dev, + void *gesture_info, bool trace_point) +{ + int ret; + + cts_info("Get gesture info"); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Get gesture info in program mode"); + return -ENODEV; + } + + if (!cts_dev->rtdata.suspended) { + cts_warn("Get gesture info while not suspended"); + return -ENODEV; + } + + if (!cts_dev->rtdata.gesture_wakeup_enabled) { + cts_warn("Get gesture info while gesture wakeup not enabled"); + return -ENODEV; + } + + ret = cts_fw_reg_readsb(cts_dev, + CTS_DEVICE_FW_REG_GESTURE_INFO, gesture_info, + 2); + if (ret) { + cts_err("Read gesture info header failed %d", ret); + return ret; + } + + if (trace_point) { + ret = + cts_fw_reg_readsb(cts_dev, + CTS_DEVICE_FW_REG_GESTURE_INFO + 2, + gesture_info + 2, + (((u8 *) gesture_info))[1] * + sizeof(struct cts_device_gesture_point)); + if (ret) { + cts_err("Read gesture trace points failed %d", ret); + return ret; + } + } + + return 0; +} +#endif /* CFG_CTS_GESTURE */ + +#ifdef CONFIG_CTS_ESD_PROTECTION +static void cts_esd_protection_work(struct work_struct *work) +{ + struct chipone_ts_data *cts_data; + int ret; + + cts_info("ESD protection work"); + cts_data = container_of(work, struct chipone_ts_data, esd_work.work); + cts_lock_device(&cts_data->cts_dev); +#ifdef CONFIG_CTS_I2C_HOST + if (!cts_plat_is_i2c_online + (cts_data->pdata, CTS_DEV_NORMAL_MODE_I2CADDR)) +#else + if (!cts_plat_is_normal_mode(cts_data->pdata)) +#endif + { + cts_data->esd_check_fail_cnt++; + /*reset chip next time */ + if ((cts_data->esd_check_fail_cnt % 2) == 0) { + cts_err + ("ESD protection read normal mode failed, reset chip!"); + ret = cts_plat_reset_device(cts_data->pdata); + if (ret) { + cts_err("ESD protection reset chip failed %d", + ret); + } + } + } else { + cts_data->esd_check_fail_cnt = 0; + } + + if (cts_data->esd_check_fail_cnt >= CFG_CTS_ESD_FAILED_CONFIRM_CNT) { + const struct cts_firmware *firmware; + + cts_warn("ESD protection check failed, update firmware!!!"); + cts_stop_device_esdrecover(&cts_data->cts_dev); + firmware = cts_request_firmware(&cts_data->cts_dev, + cts_data->cts_dev.hwdata->hwid, + cts_data->cts_dev.hwdata->fwid, + 0); + if (firmware) { + ret = + cts_update_firmware(&cts_data->cts_dev, firmware, + true); + cts_release_firmware(firmware); + + if (ret) { + cts_err("Update default firmware failed %d", + ret); + } + } else { + cts_err("Request default firmware failed %d, " + "please update manually!!", ret); + } + cts_start_device_esdrecover(&cts_data->cts_dev); + cts_data->esd_check_fail_cnt = 0; + } + queue_delayed_work(cts_data->esd_workqueue, + &cts_data->esd_work, + CFG_CTS_ESD_PROTECTION_CHECK_PERIOD); + + cts_unlock_device(&cts_data->cts_dev); +} + +void cts_enable_esd_protection(struct chipone_ts_data *cts_data) +{ + if (cts_data->esd_workqueue && !cts_data->esd_enabled) { + cts_info("ESD protection enable"); + + cts_data->esd_enabled = true; + cts_data->esd_check_fail_cnt = 0; + queue_delayed_work(cts_data->esd_workqueue, + &cts_data->esd_work, + CFG_CTS_ESD_PROTECTION_CHECK_PERIOD); + } +} + +void cts_disable_esd_protection(struct chipone_ts_data *cts_data) +{ + if (cts_data->esd_workqueue && cts_data->esd_enabled) { + cts_info("ESD protection disable"); + + cts_data->esd_enabled = false; + cancel_delayed_work(&cts_data->esd_work); + flush_workqueue(cts_data->esd_workqueue); + } +} + +void cts_init_esd_protection(struct chipone_ts_data *cts_data) +{ + cts_info("Init ESD protection"); + + INIT_DELAYED_WORK(&cts_data->esd_work, cts_esd_protection_work); + + cts_data->esd_enabled = false; + cts_data->esd_check_fail_cnt = 0; +} + +void cts_deinit_esd_protection(struct chipone_ts_data *cts_data) +{ + cts_info("De-Init ESD protection"); + + if (cts_data->esd_workqueue && cts_data->esd_enabled) { + cts_data->esd_enabled = false; + cancel_delayed_work(&cts_data->esd_work); + } +} +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CONFIG_CTS_GLOVE +int cts_enter_glove_mode(const struct cts_device *cts_dev) +{ + cts_info("Enter glove mode"); + + ret = cts_fw_reg_writeb(cts_dev, 0x8000 + 149, 1); + if (ret) { + cts_err("Enable Glove mode err"); + } else { + cts_dev->rtdata.glove_mode_enabled = true; + } + return ret; +} + +int cts_exit_glove_mode(const struct cts_device *cts_dev) +{ + cts_info("Exit glove mode"); + + ret = cts_fw_reg_writeb(cts_dev, 0x8000 + 149, 0); + if (ret) { + cts_err("Exit Glove mode err"); + } else { + cts_dev->rtdata.glove_mode_enabled = false; + } + return ret; +} + +int cts_is_glove_enabled(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.glove_mode_enabled; +} + +#endif /* CONFIG_CTS_GLOVE */ + +#ifdef CONFIG_CTS_CHARGER_DETECT +bool cts_is_charger_exist(struct cts_device *cts_dev) +{ + struct chipone_ts_data *cts_data; + bool attached = false; + int ret; + + cts_data = container_of(cts_dev, struct chipone_ts_data, cts_dev); + + ret = cts_is_charger_attached(cts_data, &attached); + if (ret) { + cts_err("Get charger state failed %d", ret); + } + + cts_dev->rtdata.charger_exist = attached; + + return attached; +} + +int cts_set_dev_charger_attached(struct cts_device *cts_dev, bool attached) +{ + int ret; + + cts_info("Set dev charger %s", attached ? "ATTACHED" : "DETATCHED"); + ret = cts_send_command(cts_dev, + attached ? CTS_CMD_CHARGER_ATTACHED : CTS_CMD_CHARGER_DETACHED); + if (ret) { + if (ret) { + cts_err("Send CMD_CHARGER_%s failed %d", + attached ? "ATTACHED" : "DETACHED", ret); + } + } + + return ret; +} +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +int cts_enable_fw_log_redirect(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Fw log redirect enable"); + ret = cts_send_command(cts_dev, CTS_CMD_ENABLE_FW_LOG_REDIRECT); + if (ret) { + cts_err("Send CTS_CMD_ENABLE_FW_LOG_REDIRECT failed %d", ret); + } else { + cts_dev->rtdata.fw_log_redirect_enabled = true; + } + return 0; +} + +int cts_disable_fw_log_redirect(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Fw log redirect disable"); + ret = cts_send_command(cts_dev, CTS_CMD_DISABLE_FW_LOG_REDIRECT); + if (ret) { + cts_err("Send CTS_CMD_DISABLE_FW_LOG_REDIRECT failed %d", ret); + } else { + cts_dev->rtdata.fw_log_redirect_enabled = false; + } + return 0; +} + +bool cts_is_fw_log_redirect(struct cts_device *cts_dev) +{ + return cts_dev->rtdata.fw_log_redirect_enabled; +} + +int cts_fw_log_show_finish(struct cts_device *cts_dev) +{ + int ret; + + ret = cts_send_command(cts_dev, CTS_CMD_FW_LOG_SHOW_FINISH); + if (ret) { + cts_err("Send CTS_CMD_FW_LOG_SHOW_FINISH failed %d", ret); + } + + return ret; +} + +int cts_get_compensate_cap(struct cts_device *cts_dev, u8 *cap) +{ + int i, ret; + + if (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911 && + cts_dev->fwdata.lib_version < 0x0500) { + cts_err("ICNL9911 lib version 0x%04x < v5.0 " + "NOT supported get compensate cap", + cts_dev->fwdata.lib_version); + return -ENOTSUPP; + } + + cts_info("Get compensate cap"); + + ret = cts_send_command(cts_dev,CTS_CMD_ENABLE_READ_CNEG); + if (ret) { + cts_err("Enable read compensate cap failed %d",ret); + return ret; + } + + mdelay(10); + + for (i = 0; i < 100; i++) { + u8 ready; + + ret = cts_fw_reg_readb(cts_dev, + CTS_DEVICE_FW_REG_COMPENSATE_CAP_READY, &ready); + if (ret) { + cts_err("Read compensate cap ready flag failed %d", ret); + } else { + if (ready) { + goto read_compensate_cap; + } + } + mdelay(5); + } + + cts_err("Wait compensate cap ready timeout"); + return -ETIMEDOUT; + +read_compensate_cap: + /* Use hardware row & col here */ + ret = cts_fw_reg_readsb_delay_idle(cts_dev, + CTS_DEVICE_FW_REG_COMPENSATE_CAP, cap, + cts_dev->hwdata->num_row * cts_dev->hwdata->num_col, + 500); + if (ret) { + cts_err("Read compensate cap failed %d",ret); + // Fall through to disable read compensate cap + } + + for (i = 0; i < 100; i++) { + int r; + u8 ready; + + r = cts_send_command(cts_dev,CTS_CMD_DISABLE_READ_CNEG); + if (r) { + cts_err("Send cmd DISABLE_READ_CNEG failed %d", r); + continue; + } + + mdelay(5); + r = cts_fw_reg_readb(cts_dev, + CTS_DEVICE_FW_REG_COMPENSATE_CAP_READY, &ready); + if (r) { + cts_err("Read compensate cap ready flag failed %d", r); + continue; + } + if (ready) { + continue; + } else { + return ret; + } + } + + cts_warn("Compensate cap ready flag cannot clear, try to do reset"); + + /* Try to do hardware reset */ + cts_plat_reset_device(cts_dev->pdata); + +#ifdef CONFIG_CTS_CHARGER_DETECT + if (cts_is_charger_exist(cts_dev)) { + int r = cts_set_dev_charger_attached(cts_dev, true); + if (r) { + cts_err("Set dev charger attached failed %d", r); + } + } +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) { + int r = cts_enter_glove_mode(cts_dev); + if (r) { + cts_err("Enter dev glove mode failed %d", r); + } + } +#endif /* CONFIG_CTS_GLOVE */ + +#ifdef CFG_CTS_FW_LOG_REDIRECT + if (cts_is_fw_log_redirect(cts_dev)) { + int r = cts_enable_fw_log_redirect(cts_dev); + if (r) { + cts_err("Enable fw log redirect failed %d", r); + } + } +#endif /* CONFIG_CTS_GLOVE */ + + return ret; +} + +void cts_firmware_upgrade_work(struct work_struct *work) +{ + struct chipone_ts_data *cts_data; + struct cts_device *cts_dev; + const struct cts_firmware *firmware; + int retries; + int ret; + + cts_info("Firmware upgrade work"); + + cts_data = container_of(work, struct chipone_ts_data, + fw_upgrade_work.work); + cts_dev = &cts_data->cts_dev; + + firmware = cts_request_firmware(cts_dev, cts_dev->hwdata->hwid, + CTS_DEV_FWID_ANY, cts_dev->fwdata.version); + if (firmware == NULL) { + cts_err("Request firmware failed"); + return ; + } + + retries = 0; + do { + ret = cts_update_firmware(cts_dev, firmware, true); + if (ret) { + cts_err("Update firmware failed %d retries %d", ret, + retries); + + cts_plat_reset_device(cts_dev->pdata); + } else { + break; + } + } while (++retries < 3); + + cts_release_firmware(firmware); + + if (ret == 0) { + cts_start_device(cts_dev); + } +} + diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_core.h b/drivers/input/touchscreen/chipone_tddi_mmi/cts_core.h new file mode 100755 index 000000000000..eb63383aaa7f --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_core.h @@ -0,0 +1,716 @@ +#ifndef CTS_CORE_H +#define CTS_CORE_H + +#include "cts_config.h" + +enum cts_dev_hw_reg { + CTS_DEV_HW_REG_HARDWARE_ID = 0x30000u, + CTS_DEV_HW_REG_CLOCK_GATING = 0x30004u, + CTS_DEV_HW_REG_RESET_CONFIG = 0x30008u, + CTS_DEV_HW_REG_BOOT_MODE = 0x30010u, + CTS_DEV_HW_REG_CURRENT_MODE = 0x30011u, +}; + +enum cts_dev_boot_mode { + CTS_DEV_BOOT_MODE_FLASH = 1, + CTS_DEV_BOOT_MODE_I2C_PROGRAM = 2, + CTS_DEV_BOOT_MODE_SRAM = 3, + CTS_DEV_BOOT_MODE_SPI_PROGRAM = 5, + CTS_DEV_BOOT_MODE_MASK = 7, +}; + +/** I2C addresses(7bits), transfer size and bitrate */ +#define CTS_DEV_NORMAL_MODE_I2CADDR (0x48) +#define CTS_DEV_PROGRAM_MODE_I2CADDR (0x30) +#define CTS_DEV_NORMAL_MODE_ADDR_WIDTH (2) +#define CTS_DEV_PROGRAM_MODE_ADDR_WIDTH (3) +#define CTS_DEV_NORMAL_MODE_SPIADDR (0xF0) +#define CTS_DEV_PROGRAM_MODE_SPIADDR (0x60) + +/** Chipone firmware register addresses under normal mode */ +enum cts_device_fw_reg { + CTS_DEVICE_FW_REG_WORK_MODE = 0x0000, + CTS_DEVICE_FW_REG_SYS_BUSY = 0x0001, + CTS_DEVICE_FW_REG_DATA_READY = 0x0002, + CTS_DEVICE_FW_REG_CMD = 0x0004, + CTS_DEVICE_FW_REG_POWER_MODE = 0x0005, + CTS_DEVICE_FW_REG_FW_LIB_MAIN_VERSION = 0x0009, + CTS_DEVICE_FW_REG_CHIP_TYPE = 0x000A, + CTS_DEVICE_FW_REG_VERSION = 0x000C, + CTS_DEVICE_FW_REG_DDI_VERSION = 0x0010, + CTS_DEVICE_FW_REG_GET_WORK_MODE = 0x003F, + CTS_DEVICE_FW_REG_FW_LIB_SUB_VERSION = 0x0047, + CTS_DEVICE_FW_REG_COMPENSATE_CAP_READY = 0x004E, + + CTS_DEVICE_FW_REG_TOUCH_INFO = 0x1000, + CTS_DEVICE_FW_REG_RAW_DATA = 0x2000, + CTS_DEVICE_FW_REG_DIFF_DATA = 0x3000, + CTS_DEVICE_FW_REG_GESTURE_INFO = 0x7000, + + CTS_DEVICE_FW_REG_PANEL_PARAM = 0x8000, + CTS_DEVICE_FW_REG_NUM_TX = 0x8007, + CTS_DEVICE_FW_REG_NUM_RX = 0x8008, + CTS_DEVICE_FW_REG_INT_KEEP_TIME = 0x8047, /* Unit us */ + CTS_DEVICE_FW_REG_RAWDATA_TARGET = 0x8049, + CTS_DEVICE_FW_REG_X_RESOLUTION = 0x8090, + CTS_DEVICE_FW_REG_Y_RESOLUTION = 0x8092, + CTS_DEVICE_FW_REG_SWAP_AXES = 0x8094, + CTS_DEVICE_FW_REG_GLOVE_MODE = 0x8095, + CTS_DEVICE_FW_REG_TEST_WITH_DISPLAY_ON = 0x80A3, + CTS_DEVICE_FW_REG_INT_MODE = 0x80D8, + CTS_DEVICE_FW_REG_EARJACK_DETECT_SUPP = 0x8113, + CTS_DEVICE_FW_REG_AUTO_COMPENSATE_EN = 0x8114, + CTS_DEVICE_FW_REG_ESD_PROTECTION = 0x8156, /* RW */ + CTS_DEVICE_FW_REG_FLAG_BITS = 0x8158, + + CTS_DEVICE_FW_REG_COMPENSATE_CAP = 0xA000, + CTS_DEVICE_FW_REG_DEBUG_INTF = 0xF000, +}; + +/** Hardware IDs, read from hardware id register */ +enum cts_dev_hwid { + CTS_DEV_HWID_ICNL9911 = 0x990100u, + CTS_DEV_HWID_ICNL9911S = 0x990110u, + CTS_DEV_HWID_ICNL9911C = 0x991110u, + + CTS_DEV_HWID_ANY = 0, + CTS_DEV_HWID_INVALID = 0xFFFFFFFFu, +}; + +/* Firmware IDs, read from firmware register @ref CTS_DEV_FW_REG_CHIP_TYPE + under normal mode */ +enum cts_dev_fwid { + CTS_DEV_FWID_ICNL9911 = 0x9911u, + CTS_DEV_FWID_ICNL9911S = 0x9964u, + CTS_DEV_FWID_ICNL9911C = 0x9954u, + + CTS_DEV_FWID_ANY = 0u, + CTS_DEV_FWID_INVALID = 0xFFFFu +}; + +/** Commands written to firmware register @ref CTS_DEVICE_FW_REG_CMD under normal mode */ +enum cts_firmware_cmd { + CTS_CMD_RESET = 1, + CTS_CMD_SUSPEND = 2, + CTS_CMD_ENTER_WRITE_PARA_TO_FLASH_MODE = 3, + CTS_CMD_WRITE_PARA_TO_FLASH = 4, + CTS_CMD_WRTITE_INT_HIGH = 5, + CTS_CMD_WRTITE_INT_LOW = 6, + CTS_CMD_RELASE_INT_TEST = 7, + CTS_CMD_RECOVERY_TX_VOL = 0x10, + CTS_CMD_DEC_TX_VOL_1 = 0x11, + CTS_CMD_DEC_TX_VOL_2 = 0x12, + CTS_CMD_DEC_TX_VOL_3 = 0x13, + CTS_CMD_DEC_TX_VOL_4 = 0x14, + CTS_CMD_DEC_TX_VOL_5 = 0x15, + CTS_CMD_DEC_TX_VOL_6 = 0x16, + CTS_CMD_ENABLE_READ_RAWDATA = 0x20, + CTS_CMD_DISABLE_READ_RAWDATA = 0x21, + CTS_CMD_SUSPEND_WITH_GESTURE = 0x40, + CTS_CMD_QUIT_GESTURE_MONITOR = 0x41, + CTS_CMD_CHARGER_ATTACHED = 0x55, + CTS_CMD_EARJACK_ATTACHED = 0x57, + CTS_CMD_EARJACK_DETACHED = 0x58, + CTS_CMD_CHARGER_DETACHED = 0x66, + CTS_CMD_ENABLE_FW_LOG_REDIRECT = 0x86, + CTS_CMD_DISABLE_FW_LOG_REDIRECT = 0x87, + CTS_CMD_ENABLE_READ_CNEG = 0x88, + CTS_CMD_DISABLE_READ_CNEG = 0x89, + CTS_CMD_FW_LOG_SHOW_FINISH = 0xE0, + +}; + +#pragma pack(1) +/** Touch message read back from chip */ +struct cts_device_touch_msg { + u8 id; + __le16 x; + __le16 y; + u8 pressure; + u8 event; + +#define CTS_DEVICE_TOUCH_EVENT_NONE (0) +#define CTS_DEVICE_TOUCH_EVENT_DOWN (1) +#define CTS_DEVICE_TOUCH_EVENT_MOVE (2) +#define CTS_DEVICE_TOUCH_EVENT_STAY (3) +#define CTS_DEVICE_TOUCH_EVENT_UP (4) + +}; + +/** Touch information read back from chip */ +struct cts_device_touch_info { + u8 vkey_state; + u8 num_msg; + + struct cts_device_touch_msg msgs[CFG_CTS_MAX_TOUCH_NUM]; +}; + +/** Gesture trace point read back from chip */ +struct cts_device_gesture_point { + __le16 x; + __le16 y; + u8 pressure; + u8 event; +}; + +/** Gesture information read back from chip */ +struct cts_device_gesture_info { + u8 gesture_id; +#define CTS_GESTURE_UP (0x11) +#define CTS_GESTURE_C (0x12) +#define CTS_GESTURE_O (0x13) +#define CTS_GESTURE_M (0x14) +#define CTS_GESTURE_W (0x15) +#define CTS_GESTURE_E (0x16) +#define CTS_GESTURE_S (0x17) +#define CTS_GESTURE_B (0x18) +#define CTS_GESTURE_T (0x19) +#define CTS_GESTURE_H (0x1A) +#define CTS_GESTURE_F (0x1B) +#define CTS_GESTURE_X (0x1C) +#define CTS_GESTURE_Z (0x1D) +#define CTS_GESTURE_V (0x1E) +#define CTS_GESTURE_D_TAP (0x50) + + u8 num_points; + +#define CTS_CHIP_MAX_GESTURE_TRACE_POINT (64u) + struct cts_device_gesture_point points[CTS_CHIP_MAX_GESTURE_TRACE_POINT]; + +}; +#pragma pack() + + +struct cts_device; + +enum cts_crc_type { + CTS_CRC16 = 1, + CTS_CRC32 = 2, +}; + +/** Chip hardware data, will never change */ +struct cts_device_hwdata { + const char *name; + u32 hwid; + u16 fwid; + u8 num_row; + u8 num_col; + u32 sram_size; + + /* Address width under program mode */ + u8 program_addr_width; + + const struct cts_sfctrl *sfctrl; + int (*enable_access_ddi_reg)(struct cts_device *cts_dev, bool enable); +}; + +/** Chip firmware data */ +struct cts_device_fwdata { + u16 version; + u16 res_x; + u16 res_y; + u8 rows; + u8 cols; + bool flip_x; + bool flip_y; + bool swap_axes; + u8 ddi_version; + u8 int_mode; + u8 esd_method; + u16 lib_version; + u16 int_keep_time; + u16 rawdata_target; +}; + +/** Chip runtime data */ +struct cts_device_rtdata { + u8 slave_addr; + int addr_width; + bool program_mode; + bool has_flash; + + bool suspended; + bool updating; + bool testing; + + bool gesture_wakeup_enabled; + bool charger_exist; + bool fw_log_redirect_enabled; + bool glove_mode_enabled; +}; + +struct cts_device { + struct cts_platform_data *pdata; + + const struct cts_device_hwdata *hwdata; + struct cts_device_fwdata fwdata; + struct cts_device_rtdata rtdata; + const struct cts_flash *flash; + bool enabled; +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD + char config_fw_name[CFG_CTS_FW_FILE_NAME_MAX_LEN + 1]; +#endif /* CFG_CTS_FW_UPDATE_FILE_LOAD */ +}; + +struct cts_platform_data; + +struct chipone_ts_data { +#ifdef CONFIG_CTS_I2C_HOST + struct i2c_client *i2c_client; +#else + struct spi_device *spi_client; +#endif /* CONFIG_CTS_I2C_HOST */ + struct device *device; + struct cts_device cts_dev; + struct cts_platform_data *pdata; + struct workqueue_struct *workqueue; + struct delayed_work fw_upgrade_work; + struct work_struct ts_resume_work; +#ifdef CONFIG_CTS_CHARGER_DETECT + void *charger_detect_data; +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_ESD_PROTECTION + struct workqueue_struct *esd_workqueue; + struct delayed_work esd_work; + bool esd_enabled; + int esd_check_fail_cnt; +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CONFIG_CTS_LEGACY_TOOL + struct proc_dir_entry *procfs_entry; +#endif /* CONFIG_CTS_LEGACY_TOOL */ + + bool force_reflash; + +}; + +static inline u32 get_unaligned_le24(const void *p) +{ + const u8 *puc = (const u8 *)p; + return (puc[0] | (puc[1] << 8) | (puc[2] << 16)); +} + +static inline u32 get_unaligned_be24(const void *p) +{ + const u8 *puc = (const u8 *)p; + return (puc[2] | (puc[1] << 8) | (puc[0] << 16)); +} + +static inline void put_unaligned_be24(u32 v, void *p) +{ + u8 *puc = (u8 *) p; + + puc[0] = (v >> 16) & 0xFF; + puc[1] = (v >> 8) & 0xFF; + puc[2] = (v >> 0) & 0xFF; +} + +#define wrap(max, x) ((max) - 1 - (x)) + +extern void cts_lock_device(const struct cts_device *cts_dev); +extern void cts_unlock_device(const struct cts_device *cts_dev); + +extern int cts_sram_writeb_retry(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay); +extern int cts_sram_writew_retry(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay); +extern int cts_sram_writel_retry(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay); +extern int cts_sram_writesb_retry(const struct cts_device *cts_dev, + u32 addr, const void *src, size_t len, + int retry, int delay); +extern int cts_sram_writesb_check_crc_retry(const struct cts_device *cts_dev, + u32 addr, const void *src, + size_t len, u32 crc, int retry); + +extern int cts_sram_readb_retry(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay); +extern int cts_sram_readw_retry(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay); +extern int cts_sram_readl_retry(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay); +extern int cts_sram_readsb_retry(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, + int delay); + +extern int cts_fw_reg_writeb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 b, int retry, int delay); +extern int cts_fw_reg_writew_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 w, int retry, int delay); +extern int cts_fw_reg_writel_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 l, int retry, int delay); +extern int cts_fw_reg_writesb_retry(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len, + int retry, int delay); + +extern int cts_fw_reg_readb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b, int retry, int delay); +extern int cts_fw_reg_readw_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w, int retry, int delay); +extern int cts_fw_reg_readl_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l, int retry, int delay); +extern int cts_fw_reg_readsb_retry(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, + int retry, int delay); +extern int cts_fw_reg_readsb_retry_delay_idle(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, + size_t len, int retry, int delay, + int idle); + +extern int cts_hw_reg_writeb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 b, int retry, int delay); +extern int cts_hw_reg_writew_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 w, int retry, int delay); +extern int cts_hw_reg_writel_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 l, int retry, int delay); +extern int cts_hw_reg_writesb_retry(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len, + int retry, int delay); + +extern int cts_hw_reg_readb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b, int retry, int delay); +extern int cts_hw_reg_readw_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w, int retry, int delay); +extern int cts_hw_reg_readl_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l, int retry, int delay); +extern int cts_hw_reg_readsb_retry(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, + int retry, int delay); + +static inline int cts_hw_reg_readb(const struct cts_device *cts_dev, u32 reg_addr, u8 *b) +{ + return cts_hw_reg_readb_retry(cts_dev, reg_addr, b, 1, 0); +} +static inline int cts_hw_reg_writeb(const struct cts_device *cts_dev, u32 reg_addr, u8 b) +{ + return cts_hw_reg_writeb_retry(cts_dev, reg_addr, b, 1, 0); +} +static inline int cts_hw_reg_writew(const struct cts_device *cts_dev, u32 reg_addr, u16 w) +{ + return cts_hw_reg_writew_retry(cts_dev, reg_addr, w, 1, 0); +} +static inline int cts_fw_reg_writeb(const struct cts_device *cts_dev, + u32 reg_addr, u8 b) +{ + return cts_fw_reg_writeb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_fw_reg_writew(const struct cts_device *cts_dev, + u32 reg_addr, u16 w) +{ + return cts_fw_reg_writew_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_fw_reg_writel(const struct cts_device *cts_dev, + u32 reg_addr, u32 l) +{ + return cts_fw_reg_writel_retry(cts_dev, reg_addr, l, 1, 0); +} + +static inline int cts_fw_reg_writesb(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len) +{ + return cts_fw_reg_writesb_retry(cts_dev, reg_addr, src, len, 1, 0); +} + +static inline int cts_fw_reg_readb(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b) +{ + return cts_fw_reg_readb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_fw_reg_readw(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w) +{ + return cts_fw_reg_readw_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_fw_reg_readl(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l) +{ + return cts_fw_reg_readl_retry(cts_dev, reg_addr, l, 1, 0); +} + +static inline int cts_fw_reg_readsb(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len) +{ + return cts_fw_reg_readsb_retry(cts_dev, reg_addr, dst, len, 1, 0); +} + +static inline int cts_fw_reg_readsb_delay_idle(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, + size_t len, int idle) +{ + return cts_fw_reg_readsb_retry_delay_idle(cts_dev, reg_addr, dst, len, + 1, 0, idle); +} + +static inline int cts_hw_reg_writeb_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u8 b) +{ + return cts_hw_reg_writeb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_hw_reg_writew_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u16 w) +{ + return cts_hw_reg_writew_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_hw_reg_writel_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u32 l) +{ + return cts_hw_reg_writel_retry(cts_dev, reg_addr, l, 1, 0); +} + +static inline int cts_hw_reg_writesb(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len) +{ + return cts_hw_reg_writesb_retry(cts_dev, reg_addr, src, len, 1, 0); +} + +static inline int cts_hw_reg_readb_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b) +{ + return cts_hw_reg_readb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_hw_reg_readw_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w) +{ + return cts_hw_reg_readw_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_hw_reg_readl_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l) +{ + return cts_hw_reg_readl_retry(cts_dev, reg_addr, l, 1, 0); +} + +static inline int cts_hw_reg_readsb(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len) +{ + return cts_hw_reg_readsb_retry(cts_dev, reg_addr, dst, len, 1, 0); +} + +static inline int cts_sram_writeb(const struct cts_device *cts_dev, u32 addr, + u8 b) +{ + return cts_sram_writeb_retry(cts_dev, addr, b, 1, 0); +} + +static inline int cts_sram_writew(const struct cts_device *cts_dev, u32 addr, + u16 w) +{ + return cts_sram_writew_retry(cts_dev, addr, w, 1, 0); +} + +static inline int cts_sram_writel(const struct cts_device *cts_dev, u32 addr, + u32 l) +{ + return cts_sram_writel_retry(cts_dev, addr, l, 1, 0); +} + +static inline int cts_sram_writesb(const struct cts_device *cts_dev, u32 addr, + const void *src, size_t len) +{ + return cts_sram_writesb_retry(cts_dev, addr, src, len, 1, 0); +} + +static inline int cts_sram_readb(const struct cts_device *cts_dev, u32 addr, + u8 *b) +{ + return cts_sram_readb_retry(cts_dev, addr, b, 1, 0); +} + +static inline int cts_sram_readw(const struct cts_device *cts_dev, u32 addr, + u16 *w) +{ + return cts_sram_readw_retry(cts_dev, addr, w, 1, 0); +} + +static inline int cts_sram_readl(const struct cts_device *cts_dev, u32 addr, + u32 *l) +{ + return cts_sram_readl_retry(cts_dev, addr, l, 1, 0); +} + +static inline int cts_sram_readsb(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len) +{ + return cts_sram_readsb_retry(cts_dev, addr, dst, len, 1, 0); +} + +#ifdef CONFIG_CTS_I2C_HOST +static inline void cts_set_program_addr(struct cts_device *cts_dev) +{ + cts_dev->rtdata.slave_addr = CTS_DEV_PROGRAM_MODE_I2CADDR; + cts_dev->rtdata.program_mode = true; + cts_dev->rtdata.addr_width = CTS_DEV_PROGRAM_MODE_ADDR_WIDTH; +} + +static inline void cts_set_normal_addr(struct cts_device *cts_dev) +{ + cts_dev->rtdata.slave_addr = CTS_DEV_NORMAL_MODE_I2CADDR; + cts_dev->rtdata.program_mode = false; + cts_dev->rtdata.addr_width = CTS_DEV_NORMAL_MODE_ADDR_WIDTH; +} +#else +static inline void cts_set_program_addr(struct cts_device *cts_dev) +{ + cts_dev->rtdata.slave_addr = CTS_DEV_PROGRAM_MODE_SPIADDR; + cts_dev->rtdata.program_mode = true; + cts_dev->rtdata.addr_width = CTS_DEV_PROGRAM_MODE_ADDR_WIDTH; +} + +static inline void cts_set_normal_addr(struct cts_device *cts_dev) +{ + cts_dev->rtdata.slave_addr = CTS_DEV_NORMAL_MODE_SPIADDR; + cts_dev->rtdata.program_mode = false; + cts_dev->rtdata.addr_width = CTS_DEV_NORMAL_MODE_ADDR_WIDTH; +} +#endif + +extern int cts_irq_handler(struct cts_device *cts_dev); +extern void cts_firmware_upgrade_work(struct work_struct *work); + +extern bool cts_is_device_suspended(const struct cts_device *cts_dev); +extern int cts_suspend_device(struct cts_device *cts_dev); +extern int cts_resume_device(struct cts_device *cts_dev); + +extern bool cts_is_device_program_mode(const struct cts_device *cts_dev); +extern int cts_enter_program_mode(struct cts_device *cts_dev); +extern int cts_enter_normal_mode(struct cts_device *cts_dev); + +extern int cts_probe_device(struct cts_device *cts_dev); +extern int cts_set_work_mode(const struct cts_device *cts_dev, u8 mode); +extern int cts_get_work_mode(const struct cts_device *cts_dev, u8 *mode); +extern int cts_get_firmware_version(const struct cts_device *cts_dev, + u16 *version); +extern int cts_get_ddi_version(const struct cts_device *cts_dev, u8 *version); +extern int cts_get_lib_version(const struct cts_device *cts_dev, + u16 *lib_version); +extern int cts_get_data_ready_flag(const struct cts_device *cts_dev, u8 *flag); +extern int cts_clr_data_ready_flag(const struct cts_device *cts_dev); +extern int cts_send_command(const struct cts_device *cts_dev, u8 cmd); +extern int cts_get_panel_param(const struct cts_device *cts_dev, + void *param, size_t size); +extern int cts_set_panel_param(const struct cts_device *cts_dev, + const void *param, size_t size); +extern int cts_get_x_resolution(const struct cts_device *cts_dev, + u16 *resolution); +extern int cts_get_y_resolution(const struct cts_device *cts_dev, + u16 *resolution); +extern int cts_get_num_rows(const struct cts_device *cts_dev, u8 *num_rows); +extern int cts_get_num_cols(const struct cts_device *cts_dev, u8 *num_cols); +extern int cts_enable_get_rawdata(const struct cts_device *cts_dev); +extern int cts_disable_get_rawdata(const struct cts_device *cts_dev); +extern int cts_get_rawdata(const struct cts_device *cts_dev, void *buf); +extern int cts_get_diffdata(const struct cts_device *cts_dev, void *buf); +extern int cts_get_compensate_cap(struct cts_device *cts_dev, u8 *cap); +extern int cts_get_fwid(struct cts_device *cts_dev, u16 *fwid); +extern int cts_get_hwid(struct cts_device *cts_dev, u32 *hwid); + +#ifdef CFG_CTS_GESTURE +extern void cts_enable_gesture_wakeup(struct cts_device *cts_dev); +extern void cts_disable_gesture_wakeup(struct cts_device *cts_dev); +extern bool cts_is_gesture_wakeup_enabled(const struct cts_device *cts_dev); +extern int cts_get_gesture_info(const struct cts_device *cts_dev, + void *gesture_info, bool trace_point); +#endif /* CFG_CTS_GESTURE */ + +#ifdef CONFIG_CTS_ESD_PROTECTION +extern void cts_init_esd_protection(struct chipone_ts_data *cts_data); +extern void cts_enable_esd_protection(struct chipone_ts_data *cts_data); +extern void cts_disable_esd_protection(struct chipone_ts_data *cts_data); +extern void cts_deinit_esd_protection(struct chipone_ts_data *cts_data); +#else /* CONFIG_CTS_ESD_PROTECTION */ +static inline void cts_init_esd_protection(struct chipone_ts_data *cts_data) +{ +} + +static inline void cts_enable_esd_protection(struct chipone_ts_data *cts_data) +{ +} + +static inline void cts_disable_esd_protection(struct chipone_ts_data *cts_data) +{ +} + +static inline void cts_deinit_esd_protection(struct chipone_ts_data *cts_data) +{ +} +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CONFIG_CTS_GLOVE +extern int cts_enter_glove_mode(struct cts_device *cts_dev); +extern int cts_exit_glove_mode(struct cts_device *cts_dev); +int cts_is_glove_enabled(const struct cts_device *cts_dev); +#else +static inline int cts_enter_glove_mode(struct cts_device *cts_dev) +{ + return 0; +} + +static inline int cts_exit_glove_mode(struct cts_device *cts_dev) +{ + return 0; +} + +static inline int cts_is_glove_enabled(const struct cts_device *cts_dev) +{ + return 0; +} +#endif + +#ifdef CONFIG_CTS_CHARGER_DETECT +extern bool cts_is_charger_exist(struct cts_device *cts_dev); +extern int cts_set_dev_charger_attached(struct cts_device *cts_dev, bool attached); +#else /* CONFIG_CTS_CHARGER_DETECT */ +static inline bool cts_is_charger_exist(struct cts_device *cts_dev) +{ + return false; +} + +static inline int cts_set_dev_charger_attached(struct cts_device *cts_dev, bool attached) +{ + return 0; +} +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_LEGACY_TOOL +extern int cts_tool_init(struct chipone_ts_data *cts_data); +extern void cts_tool_deinit(struct chipone_ts_data *data); +#else /* CONFIG_CTS_LEGACY_TOOL */ +static inline int cts_tool_init(struct chipone_ts_data *cts_data) +{ + return 0; +} + +static inline void cts_tool_deinit(struct chipone_ts_data *data) +{ +} +#endif /* CONFIG_CTS_LEGACY_TOOL */ + +extern bool cts_is_device_enabled(const struct cts_device *cts_dev); +extern int cts_start_device(struct cts_device *cts_dev); +extern int cts_stop_device(struct cts_device *cts_dev); + +#ifdef CFG_CTS_FW_LOG_REDIRECT +extern int cts_enable_fw_log_redirect(struct cts_device *cts_dev); +extern int cts_disable_fw_log_redirect(struct cts_device *cts_dev); +extern bool cts_is_fw_log_redirect(struct cts_device *cts_dev); +extern int cts_fw_log_show_finish(struct cts_device *cts_dev); +#endif + +#ifdef CFG_CTS_UPDATE_CRCCHECK +extern int cts_sram_writesb_boot_crc_retry(const struct cts_device *cts_dev, + size_t len, u32 crc, int retry); +#endif + +extern const char *cts_dev_boot_mode2str(u8 boot_mode); +extern bool cts_is_fwid_valid(u16 fwid); + +#endif /* CTS_CORE_H */ + diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_firmware.c b/drivers/input/touchscreen/chipone_tddi_mmi/cts_firmware.c new file mode 100755 index 000000000000..0c08899dfb6a --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_firmware.c @@ -0,0 +1,1099 @@ +#define LOG_TAG "Firmware" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sfctrl.h" +#include "cts_spi_flash.h" +#include "cts_firmware.h" +#include +#include +#include +#include + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE +#include "cts_builtin_firmware.h" +#define NUM_DRIVER_BUILTIN_FIRMWARE ARRAY_SIZE(cts_driver_builtin_firmwares) +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ + +#define CTS_FIRMWARE_MULTI_SECTION_FILE_SIZE (0x20000) +#define CTS_SECTION_ENABLE_FLAG (0x0000C35A) + +enum cts_firmware_section_offset { + CTS_FIRMWARE_SECTION_OFFSET = 0x00000000, + CTS_FIRMWARE_CRC_SECTION_OFFSET = 0x17000, + CTS_DDIPARAM_SECTION_OFFSET = 0x00019000, + CTS_DDIPARAM_CRC_SECTION_OFFSET = 0x1B000, + +}; + +struct cts_firmware_sect_info { + const u8 *firmware_sect; + size_t firmware_sect_size; + const u8 *firmware_crc_sect; + size_t firmware_crc_sect_size; + u32 firmware_sect_crc; + + const u8 *ddiparam_sect; + size_t ddiparam_sect_size; + const u8 *ddiparam_crc_sect; + size_t ddiparam_crc_sect_size; + u32 ddiparam_sect_crc; + +}; + +#define FIRMWARE_SECTION(firmware) \ + ((firmware)->data) +#define FIRMWARE_CRC_SECTION(firmware) \ + ((firmware)->data + CTS_FIRMWARE_CRC_SECTION_OFFSET) +#define DDIPARAM_SECTION(firmware) \ + ((firmware)->data + CTS_DDIPARAM_SECTION_OFFSET) +#define DDIPARAM_CRC_SECTION(firmware) \ + ((firmware)->data + CTS_DDIPARAM_CRC_SECTION_OFFSET) + +#define FIRMWARE_SECTION_CRC(firmware) \ + (get_unaligned_le32(FIRMWARE_CRC_SECTION(firmware))) +#define FIRMWARE_SECTION_SIZE(firmware) \ + (get_unaligned_le32(FIRMWARE_CRC_SECTION(firmware) + 4)) +#define FIRMWARE_SECTION_CRC_ENABLE(firmware) \ + (get_unaligned_le32(FIRMWARE_CRC_SECTION(firmware) + 8)) +#define FIRMWARE_CRC_SECTION_SIZE (12) + +#define DDIPARAM_SECTION_ENABLE(firmware) \ + (get_unaligned_le32(DDIPARAM_CRC_SECTION(firmware))) +#define DDIPARAM_SECTION_CRC_ENABLE(firmware) \ + (get_unaligned_le32(DDIPARAM_CRC_SECTION(firmware) + 4)) +#define DDIPARAM_SECTION_CRC(firmware) \ + (get_unaligned_le32(DDIPARAM_CRC_SECTION(firmware) + 8)) +#define DDIPARAM_SECTION_SIZE(firmware) \ + (get_unaligned_le32(DDIPARAM_CRC_SECTION(firmware) + 12)) +#define DDIPARAM_CRC_SECTION_SIZE (17) + +u32 crc32(const u8 *data, size_t len) +{ + const static u32 crc32_table[] = { + 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, + 0x17C56B6B, + 0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, + 0x2B4BCB61, + 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, 0x4C11DB70, + 0x48D0C6C7, + 0x4593E01E, 0x4152FDA9, 0x5F15ADAC, 0x5BD4B01B, 0x569796C2, + 0x52568B75, + 0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, 0x791D4014, + 0x7DDC5DA3, + 0x709F7B7A, 0x745E66CD, 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, + 0x95609039, + 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58, + 0xBAEA46EF, + 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, + 0xA06C0B5D, + 0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, 0xC7361B4C, + 0xC3F706FB, + 0xCEB42022, 0xCA753D95, 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, + 0xFF79A6F1, + 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, 0x34867077, + 0x30476DC0, + 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5, + 0x2AC12072, + 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, 0x018AEB13, + 0x054BF6A4, + 0x0808D07D, 0x0CC9CDCA, 0x7897AB07, 0x7C56B6B0, 0x71159069, + 0x75D48DDE, + 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02, 0x5E9F46BF, + 0x5A5E5B08, + 0x571D7DD1, 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, + 0x40D816BA, + 0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B, + 0xBB60ADFC, + 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698, 0x832F1041, + 0x87EE0DF6, + 0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A, 0xE0B41DE7, + 0xE4750050, + 0xE9362689, 0xEDF73B3E, 0xF3B06B3B, 0xF771768C, 0xFA325055, + 0xFEF34DE2, + 0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, + 0xD1799B34, + 0xDC3ABDED, 0xD8FBA05A, 0x690CE0EE, 0x6DCDFD59, 0x608EDB80, + 0x644FC637, + 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, 0x4F040D56, + 0x4BC510E1, + 0x46863638, 0x42472B8F, 0x5C007B8A, 0x58C1663D, 0x558240E4, + 0x51435D53, + 0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, 0x36194D42, + 0x32D850F5, + 0x3F9B762C, 0x3B5A6B9B, 0x0315D626, 0x07D4CB91, 0x0A97ED48, + 0x0E56F0FF, + 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, 0xF12F560E, + 0xF5EE4BB9, + 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, + 0xEF68060B, + 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, 0xC423CD6A, + 0xC0E2D0DD, + 0xCDA1F604, 0xC960EBB3, 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, + 0xB07DABA7, + 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, + 0x9FF77D71, + 0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74, + 0x857130C3, + 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, 0x4E8EE645, + 0x4A4FFBF2, + 0x470CDD2B, 0x43CDC09C, 0x7B827D21, 0x7F436096, 0x7200464F, + 0x76C15BF8, + 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24, 0x119B4BE9, + 0x155A565E, + 0x18197087, 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, + 0x0FDC1BEC, + 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D, + 0x2056CD3A, + 0x2D15EBE3, 0x29D4F654, 0xC5A92679, 0xC1683BCE, 0xCC2B1D17, + 0xC8EA00A0, + 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C, 0xE3A1CBC1, + 0xE760D676, + 0xEA23F0AF, 0xEEE2ED18, 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, + 0xFDE69BC4, + 0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, + 0x9E7D9662, + 0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF, + 0xA2F33668, + 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4 + }; + + u32 crc = 0; + while (len) { + crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ *data) & 0xFF]; + + data++; + len--; + } + + return crc; +} + +static int calc_crc_in_flash(const struct cts_device *cts_dev, + u32 flash_addr, size_t size, u32 *crc) +{ + return cts_dev->hwdata->sfctrl->ops->calc_flash_crc(cts_dev, + flash_addr, size, + crc); +} + +static bool is_multi_section_firmware(const struct cts_firmware *firmware) +{ + return (firmware->size == CTS_FIRMWARE_MULTI_SECTION_FILE_SIZE); +} + +static bool is_single_section_firmware(const struct cts_firmware *firmware) +{ + return !(is_multi_section_firmware(firmware)); +} + +static bool is_firmware_size_valid(const struct cts_firmware *firmware) +{ + return (firmware->size > 0x102 && + firmware->size <= CTS_FIRMWARE_MULTI_SECTION_FILE_SIZE); +} + +static bool is_multi_section_firmware_valid(const struct cts_firmware *firmware) +{ + u32 crc; + + crc = + crc32(FIRMWARE_SECTION(firmware), FIRMWARE_SECTION_SIZE(firmware)); + if (crc != FIRMWARE_SECTION_CRC(firmware)) { + cts_err("Firmware-section crc mismatch crc-section %08x != %08x" + ", File maybe broken!!!", + crc, FIRMWARE_SECTION_CRC(firmware)); + return false; + } + + if (DDIPARAM_SECTION_ENABLE(firmware) == CTS_SECTION_ENABLE_FLAG) { + crc = + crc32(DDIPARAM_SECTION(firmware), + DDIPARAM_SECTION_SIZE(firmware)); + if (crc != DDIPARAM_SECTION_CRC(firmware)) { + cts_err + ("DDIParam-section crc mismatch crc-section %08x != %08x" + ", File maybe broken!!!", crc, + DDIPARAM_SECTION_CRC(firmware)); + return false; + } + } else { + cts_info("DDIParam-section is NOT enabled"); + } + + return true; +} + +static bool is_firmware_valid(const struct cts_firmware *firmware) +{ + if (firmware && firmware->data && is_firmware_size_valid(firmware)) { + if (is_single_section_firmware(firmware) || + is_multi_section_firmware_valid(firmware)) { + return true; + } + } + + return false; +} + +static void parse_single_section_firmware(const struct cts_firmware *firmware, + struct cts_firmware_sect_info *info) +{ + static u8 crc_sect[12]; + + info->firmware_sect = firmware->data; + info->firmware_sect_size = firmware->size; + info->firmware_sect_crc = crc32(firmware->data, firmware->size); + + put_unaligned_le32(info->firmware_sect_crc, crc_sect); + put_unaligned_le32(info->firmware_sect_size, crc_sect + 4); + put_unaligned_le32(~0x0000C35A, crc_sect + 8); /* Enable CRC check */ + info->firmware_crc_sect = crc_sect; + info->firmware_crc_sect_size = sizeof(crc_sect); +} + +static void parse_multi_section_firmware(const struct cts_firmware *firmware, + struct cts_firmware_sect_info *info) +{ + info->firmware_sect = FIRMWARE_SECTION(firmware); + info->firmware_sect_size = FIRMWARE_SECTION_SIZE(firmware); + info->firmware_crc_sect = FIRMWARE_CRC_SECTION(firmware); + info->firmware_crc_sect_size = FIRMWARE_CRC_SECTION_SIZE; + info->firmware_sect_crc = FIRMWARE_SECTION_CRC(firmware); + + if (DDIPARAM_SECTION_ENABLE(firmware) == CTS_SECTION_ENABLE_FLAG) { + info->ddiparam_sect = DDIPARAM_SECTION(firmware); + info->ddiparam_sect_size = DDIPARAM_SECTION_SIZE(firmware); + info->ddiparam_crc_sect = DDIPARAM_CRC_SECTION(firmware); + info->ddiparam_crc_sect_size = DDIPARAM_CRC_SECTION_SIZE; + info->ddiparam_sect_crc = DDIPARAM_SECTION_CRC(firmware); + } +} + +static int parse_firmware(const struct cts_firmware *firmware, + struct cts_firmware_sect_info *info) +{ + memset(info, 0, sizeof(*info)); + + if (is_multi_section_firmware(firmware)) { + parse_multi_section_firmware(firmware, info); + } else { + parse_single_section_firmware(firmware, info); + } + + cts_info(" Firmware section size: %zu", info->firmware_sect_size); + if (info->ddiparam_crc_sect) { + cts_info(" DDIParam section size: %zu", + info->ddiparam_sect_size); + } + + return 0; +} + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE +#ifdef CONFIG_CTS_SYSFS +int cts_get_num_driver_builtin_firmware(void) +{ + return NUM_DRIVER_BUILTIN_FIRMWARE; +} + +const struct cts_firmware *cts_request_driver_builtin_firmware_by_name(const + char + *name) +{ + const struct cts_firmware *firmware; + int i; + + cts_info("Request driver builtin by name '%s'", name); + + firmware = cts_driver_builtin_firmwares; + for (i = 0; i < NUM_DRIVER_BUILTIN_FIRMWARE; i++, firmware++) { + if (strcmp(firmware->name, name) == 0) { + if (is_firmware_valid(firmware)) { + cts_info("Found driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu ver: %04x", + firmware->name, firmware->hwid, + firmware->fwid, firmware->size, + FIRMWARE_VERSION(firmware)); + return firmware; + } + + cts_warn("Found driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu invalid", + firmware->name, firmware->hwid, firmware->hwid, + firmware->size); + } + } + + return NULL; +} + +const struct cts_firmware *cts_request_driver_builtin_firmware_by_index(u32 + index) +{ + const struct cts_firmware *firmware; + + cts_info("Request driver builtin by index %u", index); + + if (index < NUM_DRIVER_BUILTIN_FIRMWARE) { + firmware = cts_driver_builtin_firmwares + index; + if (is_firmware_valid(firmware)) { + cts_info("Found driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu ver: %04x", + firmware->name, firmware->hwid, firmware->fwid, + firmware->size, FIRMWARE_VERSION(firmware)); + return firmware; + } + cts_warn("Found driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu INVALID", + firmware->name, firmware->hwid, firmware->hwid, + firmware->size); + } else { + cts_warn("Request driver builtin by index %u too large >= %zu", + index, NUM_DRIVER_BUILTIN_FIRMWARE); + } + + return NULL; +} +#endif /* CONFIG_CTS_SYSFS */ + +static const struct cts_firmware *cts_request_newer_driver_builtin_firmware(u32 + hwid, + u16 + fwid, + u16 + device_fw_ver) +{ +#define MATCH_HWID(firmware, hwid) \ + ((hwid) == CTS_DEV_HWID_ANY || (firmware)->hwid == (hwid)) +#define MATCH_FWID(firmware, fwid) \ + ((fwid) == CTS_DEV_FWID_ANY || (firmware)->fwid == (fwid)) + + const struct cts_firmware *firmware = NULL; + int i; + + cts_info + ("Request driver builtin if match hwid: %06x fwid: %04x && ver > %04x", + hwid, fwid, device_fw_ver); + + firmware = cts_driver_builtin_firmwares; + for (i = 0; i < ARRAY_SIZE(cts_driver_builtin_firmwares); + i++, firmware++) { + if (MATCH_HWID(firmware, hwid) && MATCH_FWID(firmware, fwid)) { + if (!is_firmware_valid(firmware)) { + cts_err("Found driver builtin '%s' " + "hwid: %06x fwid: %04x data: %p size: %zu INVALID", + firmware->name, firmware->hwid, + firmware->fwid, firmware->data, + firmware->size); + continue; + } + + cts_info("Found matched driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu ver: %04x", + firmware->name, firmware->hwid, firmware->fwid, + firmware->size, FIRMWARE_VERSION(firmware)); + +// if(FIRMWARE_VERSION(firmware) > device_fw_ver) { + if(1) { + cts_info("Found newer driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu ver: %04x > %04x", + firmware->name, firmware->hwid, + firmware->fwid, firmware->size, + FIRMWARE_VERSION(firmware), + device_fw_ver); + return firmware; + } + } + } + + cts_info("No newer driver builtin found"); + + return NULL; + +#undef MATCH_HWID +#undef MATCH_FWID +} +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ + +#ifdef CFG_CTS_FIRMWARE_IN_FS +bool is_filesystem_mounted(const char *filepath) +{ + struct path root_path; + struct path path; + int ret; + + ret = kern_path("/", LOOKUP_FOLLOW, &root_path); + if (ret) { + return false; + } + + ret = kern_path(filepath, LOOKUP_FOLLOW, &path); + if (ret) { + goto err_put_root_path; + } + + if (path.mnt->mnt_sb == root_path.mnt->mnt_sb) { + /* not mounted */ + ret = false; + } else { + ret = true; + } + + path_put(&path); +err_put_root_path: + path_put(&root_path); + + return !!ret; +} + +static int cts_request_firmware_full_filepath(struct cts_firmware *firmware, + const char *filepath, + int curr_version) +{ + struct file *file; + u16 version; + u8 buff[2]; + loff_t pos = 0; + int read_size; + int ret = 0; + + file = filp_open(filepath, O_RDONLY, 0); + if (IS_ERR(file)) { + cts_err("Open file '%s' failed %ld", filepath, PTR_ERR(file)); + return PTR_ERR(file); + } + + firmware->size = file_inode(file)->i_size; + if (!is_firmware_size_valid(firmware)) { + cts_info("File '%s' size: %zu invalid", filepath, + firmware->size); + ret = -EINVAL; + goto err_close_file; + } + + pos = FIRMWARE_VERSION_OFFSET; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + read_size = kernel_read(file, buff, 2, &pos); +#else + read_size = kernel_read(file, pos, buff, 2); +#endif + if (read_size < 0) { + cts_err("Read version from offset 0x100 failed"); + ret = -EIO; + goto err_close_file; + } + version = get_unaligned_le16(buff); + + if (version <= curr_version) { + cts_info("File '%s' size: %zu version: %04x <= %04x", + filepath, firmware->size, version, curr_version); + ret = -EINVAL; + goto err_close_file; + } + + cts_info("File '%s' size: %zu version: %04x", + filepath, firmware->size, version); + + firmware->data = (u8 *) vmalloc(firmware->size); + if (firmware->data == NULL) { + cts_err("Request form fs alloc firmware data failed"); + ret = -ENOMEM; + goto err_close_file; + } + + pos = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + read_size = kernel_read(file, firmware->data, firmware->size, &pos); +#else + read_size = kernel_read(file, pos, firmware->data, firmware->size); +#endif + if (read_size < 0 || read_size != firmware->size) { + cts_err("Request from fs read whole file failed %d", read_size); + ret = -EIO; + goto err_free_firmware_data; + } + + { + int r = filp_close(file, NULL); + if (r) { + cts_warn("Close file '%s' failed %d", filepath, r); + } + } + + return 0; + +err_free_firmware_data: + vfree(firmware->data); + firmware->data = NULL; + +err_close_file:{ + int r = filp_close(file, NULL); + if (r) { + cts_warn("Close file '%s' failed %d", filepath, r); + } + } + + return ret; +} + +static int cts_wrap_request_firmware(struct cts_firmware *firmware, + const char *name, struct device *device, + int curr_version) +{ + int ret; + + ret = request_firmware(&firmware->fw, name, device); + if (ret) { + cts_err("Could not load firmware from %s: %d", name, ret); + return ret; + } + + /* Map firmware structure to cts_firmware */ + firmware->data = (u8 *) firmware->fw->data; + firmware->size = firmware->fw->size; + + /* Check firmware */ + if (!is_firmware_valid(firmware)) { + cts_err("Request firmware file INVALID, size %zu", + firmware->size); + ret = -EINVAL; + goto err_release_firmware; + } + + /* Check firmware version */ + if (curr_version > 0 && curr_version >= FIRMWARE_VERSION(firmware)) { + cts_info("Request firmware file with version 0x%04x < 0x%04x", + FIRMWARE_VERSION(firmware), curr_version); + ret = -EINVAL; + goto err_release_firmware; + } + + return 0; + +err_release_firmware: + release_firmware(firmware->fw); + + return ret; +} + +const struct cts_firmware *cts_request_newer_firmware_from_fs(const struct + cts_device + *cts_dev, + const char + *filepath, + u16 curr_version) +{ + struct cts_firmware *firmware = NULL; + int ret = 0; + + cts_info("Request from file '%s' if version > %04x", + filepath, curr_version); + + if (filepath == NULL || filepath[0] == '\0') { + cts_err("Request from file path INVALID %p", filepath); + return NULL; + } + + firmware = + (struct cts_firmware *)kzalloc(sizeof(*firmware), GFP_KERNEL); + if (firmware == NULL) { + cts_err("Request from file alloc struct cts_firmware failed"); + return NULL; + } + + if (strchr(filepath, '/') != NULL) { /* Full path */ + cts_info("Filepath is fullpath, direct read it out"); + ret = cts_request_firmware_full_filepath(firmware, + filepath, + curr_version); + } else { /* ONLY file name is givin */ + cts_info("Filepath is only filename, use request_firmware()"); + ret = cts_wrap_request_firmware(firmware, filepath, + &cts_dev->pdata->ts_input_dev-> + dev, curr_version); + } + + if (ret) { + cts_err("Request from file '%s' failed %d", filepath, ret); + kfree(firmware); + return NULL; + } else { + return firmware; + } +} + +const struct cts_firmware *cts_request_firmware_from_fs(const struct cts_device + *cts_dev, + const char *filepath) +{ + cts_info("Request from file '%s'", filepath); + + return cts_request_newer_firmware_from_fs(cts_dev, filepath, 0); +} + +int cts_update_firmware_from_file(struct cts_device *cts_dev, + const char *filepath, bool to_flash) +{ + const struct cts_firmware *firmware; + int ret; + + cts_info("Update from file '%s' to %s", filepath, + to_flash ? "flash" : "sram"); + + firmware = cts_request_firmware_from_fs(cts_dev, filepath); + if (firmware == NULL) { + cts_err("Request from file '%s' failed", filepath); + return -EFAULT; + } + + ret = cts_update_firmware(cts_dev, firmware, to_flash); + if (ret) { + cts_err("Update to %s from file failed %d", + to_flash ? "flash" : "sram", ret); + goto err_release_firmware; + } + + cts_info("Update from file success"); + +err_release_firmware: + cts_release_firmware(firmware); + + return ret; +} +#endif /*CFG_CTS_FIRMWARE_IN_FS */ + +const struct cts_firmware *cts_request_firmware(const struct cts_device + *cts_dev, u32 hwid, u16 fwid, + u16 curr_firmware_ver) +{ + const struct cts_firmware *firmware_builtin = NULL; + const struct cts_firmware *firmware_from_file = NULL; + + if (hwid == CTS_DEV_HWID_INVALID) { + hwid = CTS_DEV_HWID_ANY; + } + if (fwid == CTS_DEV_FWID_INVALID) { + fwid = CTS_DEV_FWID_ANY; + } + + cts_info("Request newer if match hwid: %06x fwid: %04x && ver > %04x", + hwid, fwid, curr_firmware_ver); + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE + firmware_builtin = + cts_request_newer_driver_builtin_firmware(hwid, fwid, + curr_firmware_ver); +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ + +#ifdef CFG_CTS_FIRMWARE_IN_FS + /* Check firmware in file system when probe only when build to .ko */ + if (is_filesystem_mounted(CFG_CTS_FIRMWARE_FILEPATH)) { + firmware_from_file = cts_request_newer_firmware_from_fs(cts_dev, +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD + cts_dev-> + config_fw_name + [0] ? + cts_dev-> + config_fw_name + : +// CFG_CTS_FIRMWARE_FILEPATH, + CFG_CTS_FIRMWARE_FILENAME, +#else /* CFG_CTS_FW_UPDATE_FILE_LOAD */ +// CFG_CTS_FIRMWARE_FILEPATH, + CFG_CTS_FIRMWARE_FILENAME, +#endif /* CFG_CTS_FW_UPDATE_FILE_LOAD */ + firmware_builtin + ? + FIRMWARE_VERSION + (firmware_builtin) + : + curr_firmware_ver); + } +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + + return firmware_from_file ? firmware_from_file : firmware_builtin; +} + +void cts_release_firmware(const struct cts_firmware *firmware) +{ + cts_info("Release firmware %p", firmware); + + if (firmware) { + if (firmware->fw) { + /* Use request_firmware() get struct firmware * */ + cts_info("Release firmware from request_firmware"); + release_firmware(firmware->fw); + kfree(firmware); + } else if (firmware->name == NULL) { + /* Direct read from file */ + cts_info("Release firmware from direct-load"); + vfree(firmware->data); + kfree(firmware); + } else { + /* Builtin firmware with non-NULL name, no need to free */ + cts_info("Release firmware from driver built-in"); + } + } +} + +static int validate_flash_data(const struct cts_device *cts_dev, + u32 flash_addr, const u8 *data, size_t size, + u8 *buf, bool calc_crc, u32 crc) +{ + int ret, i; + bool free_data = false; + u32 crc_flash; + + cts_info("Validate flash data from 0x%06x size %zu by %s", + flash_addr, size, calc_crc ? "check-crc" : "direct-readback"); + + if (calc_crc) { + ret = calc_crc_in_flash(cts_dev, flash_addr, size, &crc_flash); + if (ret) { + cts_err + ("Calc data in flash from 0x%06x size %zu crc failed %d, " + "try to validate by direct readback", flash_addr, + size, ret); + // FALL through by direct compare data read back + } else { + if (crc_flash != crc) { + cts_err + ("Crc in flash from 0x%06x size %zu mismatch 0x%08x != 0x%08x", + flash_addr, size, crc_flash, crc); + // FALL through by direct compare data read back + } else { + cts_info("Flash data crc correct"); + return 0; + } + } + } + + if (buf == NULL) { + buf = (u8 *) kmalloc(size, GFP_KERNEL); + if (buf == NULL) { + cts_err("Validate flash data allocate mem failed"); + return -ENOMEM; + } + + free_data = true; + } + + ret = cts_read_flash(cts_dev, flash_addr, buf, size); + if (ret) { + cts_err("Read flash from 0x%06x size %zu failed %d", + flash_addr, size, ret); + goto err_free_buf; + } + + for (i = 0; i < size; i++) { + if (buf[i] != data[i]) { + if (ret == 0) { + cts_err + ("Flash data from 0x%06x size %zu first bytes diff:", + flash_addr, size); + } + + if (ret < 100) { + cts_err(" 0x%06x: %02x %02x", i, buf[i], + data[i]); + } else if (ret == 100) { + cts_err(" ..."); + } + ret++; + } + } + +err_free_buf: + if (free_data) { + kfree(buf); + } + + return ret; +} + +static int cts_program_firmware(const struct cts_device *cts_dev, const struct cts_firmware_sect_info + *firmware_info) +{ + int ret; + u8 crc_sect_buf[FIRMWARE_CRC_SECTION_SIZE]; + + cts_info("Program firmware size %zu", + firmware_info->firmware_sect_size); + + /* Write firmware to SRAM for easy enter normal mode after update */ + cts_info("Write firmware section to sram"); + ret = cts_sram_writesb_check_crc_retry(cts_dev, + 0, firmware_info->firmware_sect, + firmware_info-> + firmware_sect_size, + firmware_info->firmware_sect_crc, + 3); + if (ret) { + cts_err("Write firmware section to sram failed %d", ret); + return ret; + } + + ret = cts_program_flash_from_sram(cts_dev, + 4, 4, + firmware_info->firmware_sect_size - + 4); + if (ret) { + cts_err("Program firmware section from sram failed %d", ret); + return ret; + } + + ret = cts_program_flash(cts_dev, + CTS_FIRMWARE_CRC_SECTION_OFFSET, + firmware_info->firmware_crc_sect, + firmware_info->firmware_crc_sect_size); + if (ret) { + cts_err("Program firmware crc section failed %d", ret); + return ret; + } + + ret = validate_flash_data(cts_dev, + CTS_FIRMWARE_CRC_SECTION_OFFSET, + firmware_info->firmware_crc_sect, + firmware_info->firmware_crc_sect_size, + crc_sect_buf, false, 0); + if (ret) { + cts_err("Validate Firmware-CRC section failed %d", ret); + return ret; + } + + ret = cts_program_flash(cts_dev, 0, firmware_info->firmware_sect, 4); + if (ret) { + cts_err("Program firmware section fist 4bytes failed %d", ret); + return ret; + } + + ret = validate_flash_data(cts_dev, + CTS_FIRMWARE_SECTION_OFFSET, + firmware_info->firmware_sect, + firmware_info->firmware_sect_size, NULL, true, + firmware_info->firmware_sect_crc); + if (ret) { + cts_err("Validate firmware section failed %d", ret); + return ret; + } + + return 0; +} + +static int cts_program_ddiparam(const struct cts_device *cts_dev, const struct cts_firmware_sect_info + *firmware_info) +{ + int ret; + u8 crc_sect_buf[DDIPARAM_CRC_SECTION_SIZE]; + + cts_info("Program DDIParam size: %zu", + firmware_info->ddiparam_sect_size); + + ret = cts_program_flash(cts_dev, CTS_DDIPARAM_SECTION_OFFSET, + firmware_info->ddiparam_sect, + firmware_info->ddiparam_sect_size); + if (ret) { + cts_err("Program DDIParam section failed %d", ret); + return ret; + } + + ret = validate_flash_data(cts_dev, + CTS_DDIPARAM_SECTION_OFFSET, + firmware_info->ddiparam_sect, + firmware_info->ddiparam_sect_size, NULL, true, + firmware_info->ddiparam_sect_crc); + if (ret) { + cts_err("Validate DDIParam section failed %d", ret); + return ret; + } + + ret = cts_program_flash(cts_dev, CTS_DDIPARAM_CRC_SECTION_OFFSET, + firmware_info->ddiparam_crc_sect, + firmware_info->ddiparam_crc_sect_size); + if (ret) { + cts_err("Program DDIParam-CRC section failed %d", ret); + return ret; + } + + ret = validate_flash_data(cts_dev, + CTS_DDIPARAM_CRC_SECTION_OFFSET, + firmware_info->ddiparam_crc_sect, + firmware_info->ddiparam_crc_sect_size, + crc_sect_buf, false, 0); + if (ret) { + cts_err("Validate DDIParam-CRC section failed %d", ret); + return ret; + } + + return 0; +} + +int cts_update_firmware(struct cts_device *cts_dev, + const struct cts_firmware *firmware, bool to_flash) +{ + struct cts_firmware_sect_info firmware_info; + int ret, retries; + + cts_info("Update firmware to %s ver: %04x size: %zu", + to_flash ? "flash" : "sram", + FIRMWARE_VERSION(firmware), firmware->size); + + if (parse_firmware(firmware, &firmware_info)) { + cts_err("Parse firmware failed"); + return -EINVAL; + } + + cts_dev->rtdata.updating = true; + + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Device enter program mode failed %d", ret); + goto out; + } + + ret = cts_prepare_flash_operation(cts_dev); + if (ret) { + cts_warn("Prepare flash operation failed %d", ret); + // Go through and try + } + + if (!to_flash || !cts_dev->rtdata.has_flash) { + cts_info("Write firmware section to sram size %zu", + firmware_info.firmware_sect_size); + ret = cts_sram_writesb_check_crc_retry(cts_dev, + 0, + firmware_info. + firmware_sect, + firmware_info. + firmware_sect_size, + firmware_info. + firmware_sect_crc, 3); + if (ret) { + cts_err("Write firmware section to sram failed %d", + ret); + } +#ifdef CFG_CTS_UPDATE_CRCCHECK + if (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911S || + cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9911C) { + cts_sram_writesb_boot_crc_retry(cts_dev, + firmware_info. + firmware_sect_size, + firmware_info. + firmware_sect_crc, 3); + } +#endif + goto out; + } + + retries = 0; + do { + retries++; + + ret = cts_erase_flash(cts_dev, CTS_FIRMWARE_CRC_SECTION_OFFSET, + firmware_info.firmware_crc_sect_size); + if (ret) { + cts_err + ("Erase firmware crc section failed %d retries %d", + ret, retries); + continue; + } + + ret = + cts_erase_flash(cts_dev, 0, + firmware_info.firmware_sect_size); + if (ret) { + cts_err("Erase firmware section failed %d retries %d", + ret, retries); + continue; + } + + ret = cts_program_firmware(cts_dev, &firmware_info); + if (ret) { + cts_err + ("Program firmware & crc section failed %d retries %d", + ret, retries); + } + } while (ret && retries < 3); + + if (ret == 0 && firmware_info.ddiparam_sect_size != 0) { + retries = 0; + do { + retries++; + + ret = + cts_erase_flash(cts_dev, + CTS_DDIPARAM_CRC_SECTION_OFFSET, + firmware_info.ddiparam_crc_sect_size); + if (ret) { + cts_err + ("Erase DDIParam crc secction failed %d, retries %d", + ret, retries); + continue; + } + + ret = + cts_erase_flash(cts_dev, + CTS_DDIPARAM_SECTION_OFFSET, + firmware_info.ddiparam_sect_size); + if (ret) { + cts_err + ("Erase DDIParam section failed %d, retries %d", + ret, retries); + continue; + } + + ret = cts_program_ddiparam(cts_dev, &firmware_info); + if (ret) { + cts_err + ("Program DDIParam & crc section failed %d retries %d", + ret, retries); + } + } while (ret && retries < 3); + } + + cts_post_flash_operation(cts_dev); + +out: + cts_dev->rtdata.updating = false; + + if (ret == 0) { + if (firmware_info.firmware_sect_size <= + cts_dev->hwdata->sfctrl->xchg_sram_base) { + ret = cts_enter_normal_mode(cts_dev); + if (ret) { + cts_err("Enter normal mode failed %d", ret); + //return ret; + } + } + } + +#ifdef CONFIG_CTS_CHARGER_DETECT + if (cts_is_charger_exist(cts_dev)) { + int r = cts_set_dev_charger_attached(cts_dev, true); + if (r) { + cts_err("Set dev charger attached failed %d", r); + } + } +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) { + cts_enter_glove_mode(cts_dev); + } +#endif + +#ifdef CFG_CTS_FW_LOG_REDIRECT + if (cts_is_fw_log_redirect(cts_dev)) { + cts_enable_fw_log_redirect(cts_dev); + } +#endif + + return ret; +} + +bool cts_is_firmware_updating(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.updating; +} diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_firmware.h b/drivers/input/touchscreen/chipone_tddi_mmi/cts_firmware.h new file mode 100644 index 000000000000..2039b9dd8556 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_firmware.h @@ -0,0 +1,68 @@ +#ifndef CTS_FIRMWARE_H +#define CTS_FIRMWARE_H + +#include "cts_config.h" +#include + +struct cts_firmware { + const char *name; /* MUST set to non-NULL if driver builtin firmware */ + u32 hwid; + u16 fwid; + + u8 *data; + size_t size; + const struct firmware *fw; +}; + +#define FIRMWARE_VERSION_OFFSET 0x100 +#define FIRMWARE_VERSION(firmware) \ + get_unaligned_le16((firmware)->data + FIRMWARE_VERSION_OFFSET) + +struct cts_device; + +extern const struct cts_firmware *cts_request_firmware( + const struct cts_device *cts_dev, + u32 hwid, u16 fwid, u16 device_fw_ver); +extern void cts_release_firmware(const struct cts_firmware *firmware); + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE +extern int cts_get_num_driver_builtin_firmware(void); +extern const struct cts_firmware + *cts_request_driver_builtin_firmware_by_name(const char *name); +extern const struct cts_firmware + *cts_request_driver_builtin_firmware_by_index(u32 index); +#else /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ +static inline int cts_get_num_driver_builtin_firmware(void) +{ + return 0; +} + +static inline const struct cts_firmware + *cts_request_driver_builtin_firmware_by_name(const char *name) +{ + return NULL; +} + +static inline const struct cts_firmware + *cts_request_driver_builtin_firmware_by_index(u32 index) +{ + return NULL; +} +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ +extern bool cts_is_firmware_updating(const struct cts_device *cts_dev); + +extern int cts_update_firmware(struct cts_device *cts_dev, + const struct cts_firmware *firmware, + bool to_flash); + +#ifdef CFG_CTS_FIRMWARE_IN_FS +extern const struct cts_firmware *cts_request_firmware_from_fs( + const struct cts_device *cts_dev, const char *filepath); +extern int cts_update_firmware_from_file(struct cts_device *cts_dev, + const char *filepath, bool to_flash); + +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + +extern u32 crc32(const u8 *data, size_t len); + +#endif /* CTS_FIRMWARE_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_i2c_driver.c b/drivers/input/touchscreen/chipone_tddi_mmi/cts_i2c_driver.c new file mode 100755 index 000000000000..73ab41f3f5b2 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_i2c_driver.c @@ -0,0 +1,895 @@ +#define LOG_TAG "I2CDrv" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sysfs.h" +#include "cts_charger_detect.h" + +#ifdef CFG_CTS_DRM_NOTIFIER +#include +static struct drm_panel *active_panel; +static int check_dt(struct device_node *np); +static void cts_resume_work_func(struct work_struct *work); +#endif +bool cts_show_debug_log = false; + +module_param_named(debug_log, cts_show_debug_log, bool, 0660); +MODULE_PARM_DESC(debug_log, "Show debug log control"); + +static int cts_suspend(struct chipone_ts_data *cts_data) +{ + int ret; + + cts_info("Suspend"); + + cts_lock_device(&cts_data->cts_dev); + ret = cts_suspend_device(&cts_data->cts_dev); + cts_unlock_device(&cts_data->cts_dev); + + if (ret) { + cts_err("Suspend device failed %d", ret); + // TODO: + //return ret; + } + + ret = cts_stop_device(&cts_data->cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } +#ifdef CFG_CTS_GESTURE + /* Enable IRQ wake if gesture wakeup enabled */ + if (cts_is_gesture_wakeup_enabled(&cts_data->cts_dev)) { + ret = cts_plat_enable_irq_wake(cts_data->pdata); + if (ret) { + cts_err("Enable IRQ wake failed %d", ret); + return ret; + } + ret = cts_plat_enable_irq(cts_data->pdata); + if (ret) { + cts_err("Enable IRQ failed %d", ret); + return ret; + } + } +#endif /* CFG_CTS_GESTURE */ + + /** - To avoid waking up while not sleeping, + delay 20ms to ensure reliability */ + msleep(20); + + return 0; +} + +static int cts_resume(struct chipone_ts_data *cts_data) +{ + int ret; + + cts_info("Resume"); + +#ifdef CFG_CTS_GESTURE + if (cts_is_gesture_wakeup_enabled(&cts_data->cts_dev)) { + ret = cts_plat_disable_irq_wake(cts_data->pdata); + if (ret) { + cts_warn("Disable IRQ wake failed %d", ret); + //return ret; + } + ret = cts_plat_disable_irq(cts_data->pdata); + if (ret < 0) { + cts_err("Disable IRQ failed %d", ret); + //return ret; + } + } +#endif /* CFG_CTS_GESTURE */ + + cts_lock_device(&cts_data->cts_dev); + ret = cts_resume_device(&cts_data->cts_dev); + cts_unlock_device(&cts_data->cts_dev); + if (ret) { + cts_warn("Resume device failed %d", ret); + return ret; + } + + ret = cts_start_device(&cts_data->cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + return ret; + } + + return 0; +} + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER +#ifdef CFG_CTS_DRM_NOTIFIER +static void cts_resume_work_func(struct work_struct *work) +{ + struct chipone_ts_data *cts_data = + container_of(work, struct chipone_ts_data, ts_resume_work); + cts_info("%s", __func__); + cts_resume(cts_data); +} +static int fb_notifier_callback(struct notifier_block *nb, + unsigned long action, void *data) +{ + volatile int blank; + const struct cts_platform_data *pdata = + container_of(nb, struct cts_platform_data, fb_notifier); + struct chipone_ts_data *cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + struct drm_panel_notifier *evdata = data; + + cts_info("FB notifier callback"); + if (!evdata || !cts_data) + return 0; + + blank = *(int *)evdata->data; + cts_info("action=%lu, blank=%d\n", action, blank); + + if (action == DRM_PANEL_EARLY_EVENT_BLANK) { + if (blank == DRM_PANEL_BLANK_POWERDOWN) { + cts_suspend(cts_data); + } + } else if (evdata->data) { + blank = *(int *)evdata->data; + if (action == DRM_PANEL_EVENT_BLANK) { + if (blank == DRM_PANEL_BLANK_UNBLANK) { +// cts_resume(cts_data); + queue_work(cts_data->workqueue, &cts_data->ts_resume_work); + } + } + } + + return 0; +} +#else +static int fb_notifier_callback(struct notifier_block *nb, + unsigned long action, void *data) +{ + volatile int blank; + const struct cts_platform_data *pdata = + container_of(nb, struct cts_platform_data, fb_notifier); + struct chipone_ts_data *cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + struct fb_event *evdata = data; + + cts_info("FB notifier callback"); + + if (evdata && evdata->data) { + if (action == FB_EVENT_BLANK) { + blank = *(int *)evdata->data; + if (blank == FB_BLANK_UNBLANK) { + cts_resume(cts_data); + return NOTIFY_OK; + } + } else if (action == FB_EARLY_EVENT_BLANK) { + blank = *(int *)evdata->data; + if (blank == FB_BLANK_POWERDOWN) { + cts_suspend(cts_data); + return NOTIFY_OK; + } + } + } + + return NOTIFY_DONE; +} +#endif + +static int cts_init_pm_fb_notifier(struct chipone_ts_data *cts_data) +{ + cts_info("Init FB notifier"); + + cts_data->pdata->fb_notifier.notifier_call = fb_notifier_callback; + +#ifdef CFG_CTS_DRM_NOTIFIER +{ + int ret = -ENODEV; + if (active_panel) { + ret = drm_panel_notifier_register(active_panel, &cts_data->pdata->fb_notifier); + if(ret) { + cts_err("register drm_notifier failed. ret=%d\n", ret); + } + } + return ret; +} +#else + return fb_register_client(&cts_data->pdata->fb_notifier); +#endif +} + +static int cts_deinit_pm_fb_notifier(struct chipone_ts_data *cts_data) +{ + cts_info("Deinit FB notifier"); +#ifdef CFG_CTS_DRM_NOTIFIER +{ + int ret = 0; + if (active_panel) { + ret = drm_panel_notifier_unregister(active_panel, &cts_data->pdata->fb_notifier); + if (ret) + cts_err("Error occurred while unregistering drm_notifier.\n"); + } + return ret; +} +#else + return fb_unregister_client(&cts_data->pdata->fb_notifier); +#endif +} +#endif /* CONFIG_CTS_PM_FB_NOTIFIER */ + +static int 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)) { + cts_info("check active_panel"); + active_panel = panel; + return 0; + } + } + if (node) + pr_err("%s: %s not actived\n", __func__, node->name); + return -ENODEV; +} + +static int check_default_tp(struct device_node *dt, const char *prop) +{ + const char *active_tp; + const char *compatible; + char *start; + int ret; + + ret = of_property_read_string(dt->parent, prop, &active_tp); + if (ret) { + cts_err(" %s:fail to read %s %d\n", __func__, prop, ret); + return -ENODEV; + } + + ret = of_property_read_string(dt, "compatible", &compatible); + if (ret < 0) { + cts_err(" %s:fail to read %s %d\n", __func__, "compatible", ret); + return -ENODEV; + } + + start = strnstr(active_tp, compatible, strlen(active_tp)); + if (start == NULL) { + cts_err(" %s:no match compatible, %s, %s\n", + __func__, compatible, active_tp); + ret = -ENODEV; + } + + return ret; +} + +#ifdef CONFIG_CTS_I2C_HOST +static int cts_driver_probe(struct i2c_client *client, + const struct i2c_device_id *id) +#else +static int cts_driver_probe(struct spi_device *client) +#endif +{ + struct chipone_ts_data *cts_data = NULL; + int ret = 0; + +{ + struct device_node *dp = client->dev.of_node; + if (check_dt(dp)) { + if (!check_default_tp(dp, "qcom,i2c-touch-active")) + ret = -EPROBE_DEFER; + else + ret = -ENODEV; + + cts_err("%s: %s not actived\n", __func__, dp->name); + return ret; + } +} +#ifdef CONFIG_CTS_I2C_HOST + cts_info("Probe i2c client: name='%s' addr=0x%02x flags=0x%02x irq=%d", + client->name, client->addr, client->flags, client->irq); + +#if !defined(CONFIG_MTK_PLATFORM) + if (client->addr != CTS_DEV_NORMAL_MODE_I2CADDR) { + cts_err("Probe i2c addr 0x%02x != driver config addr 0x%02x", + client->addr, CTS_DEV_NORMAL_MODE_I2CADDR); + return -ENODEV; + }; +#endif + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + cts_err("Check functionality failed"); + return -ENODEV; + } +#endif + + cts_data = + (struct chipone_ts_data *)kzalloc(sizeof(*cts_data), GFP_KERNEL); + if (cts_data == NULL) { + cts_err("Allocate chipone_ts_data failed"); + return -ENOMEM; + } + cts_data->pdata = + (struct cts_platform_data *) + kzalloc(sizeof(struct cts_platform_data), GFP_KERNEL); + if (cts_data->pdata == NULL) { + cts_err("Allocate cts_platform_data failed"); + ret = -ENOMEM; + goto err_free_cts_data; + } +#ifdef CONFIG_CTS_I2C_HOST + i2c_set_clientdata(client, cts_data); + cts_data->i2c_client = client; + cts_data->device = &client->dev; +#else + spi_set_drvdata(client, cts_data); + cts_data->spi_client = client; + cts_data->device = &client->dev; +#endif + + cts_init_platform_data(cts_data->pdata, client); + + cts_data->cts_dev.pdata = cts_data->pdata; + cts_data->pdata->cts_dev = &cts_data->cts_dev; + + cts_data->workqueue = + create_singlethread_workqueue(CFG_CTS_DEVICE_NAME "-workqueue"); + if (cts_data->workqueue == NULL) { + cts_err("Create workqueue failed"); + ret = -ENOMEM; + goto err_deinit_platform_data; + } +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_data->esd_workqueue = + create_singlethread_workqueue(CFG_CTS_DEVICE_NAME "-esd_workqueue"); + if (cts_data->esd_workqueue == NULL) { + cts_err("Create esd workqueue failed"); + ret = -ENOMEM; + goto err_destroy_workqueue; + } +#endif + ret = cts_plat_request_resource(cts_data->pdata); + if (ret < 0) { + cts_err("Request resource failed %d", ret); + goto err_destroy_esd_workqueue; + } + + ret = cts_plat_reset_device(cts_data->pdata); + if (ret < 0) { + cts_err("Reset device failed %d", ret); + goto err_free_resource; + } + + ret = cts_probe_device(&cts_data->cts_dev); + if (ret) { + cts_err("Probe device failed %d", ret); + goto err_free_resource; + } + + ret = cts_plat_init_touch_device(cts_data->pdata); + if (ret < 0) { + cts_err("Init touch device failed %d", ret); + goto err_free_resource; + } + + ret = cts_plat_init_vkey_device(cts_data->pdata); + if (ret < 0) { + cts_err("Init vkey device failed %d", ret); + goto err_deinit_touch_device; + } + + ret = cts_plat_init_gesture(cts_data->pdata); + if (ret < 0) { + cts_err("Init gesture failed %d", ret); + goto err_deinit_vkey_device; + } + + cts_init_esd_protection(cts_data); + + ret = cts_tool_init(cts_data); + if (ret < 0) { + cts_warn("Init tool node failed %d", ret); + } + + ret = cts_sysfs_add_device(&client->dev); + if (ret < 0) { + cts_warn("Add sysfs entry for device failed %d", ret); + } + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + ret = cts_init_pm_fb_notifier(cts_data); + if (ret) { + cts_err("Init FB notifier failed %d", ret); + goto err_deinit_sysfs; + } +#endif /* CONFIG_CTS_PM_FB_NOTIFIER */ + + ret = cts_plat_request_irq(cts_data->pdata); + if (ret < 0) { + cts_err("Request IRQ failed %d", ret); + goto err_register_fb; + } + + ret = cts_charger_detect_init(cts_data); + if (ret) { + cts_err("Init charger detect failed %d", ret); + // Ignore this error + } + + /* Init firmware upgrade work and schedule */ + INIT_DELAYED_WORK(&cts_data->fw_upgrade_work, cts_firmware_upgrade_work); + queue_delayed_work(cts_data->workqueue, &cts_data->fw_upgrade_work, msecs_to_jiffies(15*1000)); + + INIT_WORK(&cts_data->ts_resume_work, cts_resume_work_func); +#if 0 + ret = cts_start_device(&cts_data->cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + goto err_free_irq; + } +#endif + return 0; + +//err_free_irq: +// cts_plat_free_irq(cts_data->pdata); + +err_register_fb: +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + cts_deinit_pm_fb_notifier(cts_data); +err_deinit_sysfs: +#endif /* CONFIG_CTS_PM_FB_NOTIFIER */ + cts_sysfs_remove_device(&client->dev); +#ifdef CONFIG_CTS_LEGACY_TOOL + cts_tool_deinit(cts_data); +#endif /* CONFIG_CTS_LEGACY_TOOL */ + +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_deinit_esd_protection(cts_data); +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CFG_CTS_GESTURE + cts_plat_deinit_gesture(cts_data->pdata); +#endif /* CFG_CTS_GESTURE */ + +err_deinit_vkey_device: +#ifdef CONFIG_CTS_VIRTUALKEY + cts_plat_deinit_vkey_device(cts_data->pdata); +#endif /* CONFIG_CTS_VIRTUALKEY */ + +err_deinit_touch_device: + cts_plat_deinit_touch_device(cts_data->pdata); + +err_free_resource: + cts_plat_free_resource(cts_data->pdata); +err_destroy_esd_workqueue: +#ifdef CONFIG_CTS_ESD_PROTECTION + destroy_workqueue(cts_data->esd_workqueue); +err_destroy_workqueue: +#endif + destroy_workqueue(cts_data->workqueue); +err_deinit_platform_data: + cts_deinit_platform_data(cts_data->pdata); +//err_free_pdata: + kfree(cts_data->pdata); +err_free_cts_data: + kfree(cts_data); + + cts_err("Probe failed %d", ret); + + return ret; +} + +#ifdef CONFIG_CTS_I2C_HOST +static int cts_driver_remove(struct i2c_client *client) +#else +static int cts_driver_remove(struct spi_device *client) +#endif +{ + struct chipone_ts_data *cts_data; + int ret = 0; + + cts_info("Remove"); + +#ifdef CONFIG_CTS_I2C_HOST + cts_data = (struct chipone_ts_data *)i2c_get_clientdata(client); +#else + cts_data = (struct chipone_ts_data *)spi_get_drvdata(client); +#endif + if (cts_data) { + ret = cts_stop_device(&cts_data->cts_dev); + if (ret) { + cts_warn("Stop device failed %d", ret); + } + + cts_charger_detect_deinit(cts_data); + + cts_plat_free_irq(cts_data->pdata); + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + cts_deinit_pm_fb_notifier(cts_data); +#endif /* CONFIG_CTS_PM_FB_NOTIFIER */ + + cts_tool_deinit(cts_data); + + cts_sysfs_remove_device(&client->dev); + + cts_deinit_esd_protection(cts_data); + + cts_plat_deinit_touch_device(cts_data->pdata); + + cts_plat_deinit_vkey_device(cts_data->pdata); + + cts_plat_deinit_gesture(cts_data->pdata); + + cts_plat_free_resource(cts_data->pdata); + +#ifdef CONFIG_CTS_ESD_PROTECTION + if (cts_data->esd_workqueue) { + destroy_workqueue(cts_data->esd_workqueue); + } +#endif + + if (cts_data->workqueue) { + destroy_workqueue(cts_data->workqueue); + } + + cts_deinit_platform_data(cts_data->pdata); + + if (cts_data->pdata) { + kfree(cts_data->pdata); + } + kfree(cts_data); + } else { + cts_warn + ("Chipone i2c driver remove while NULL chipone_ts_data"); + return -EINVAL; + } + + return ret; +} + +#ifdef CONFIG_CTS_PM_LEGACY +static int cts_i2c_driver_suspend(struct device *dev, pm_message_t state) +{ + cts_info("Suspend by legacy power management"); + return cts_suspend(dev_get_drvdata(dev)); +} + +static int cts_i2c_driver_resume(struct device *dev) +{ + cts_info("Resume by legacy power management"); + return cts_resume(dev_get_drvdata(dev)); +} +#endif /* CONFIG_CTS_PM_LEGACY */ + +#ifdef CONFIG_CTS_PM_GENERIC +static int cts_i2c_driver_pm_suspend(struct device *dev) +{ + cts_info("Suspend by bus power management"); + return cts_suspend(dev_get_drvdata(dev)); +} + +static int cts_i2c_driver_pm_resume(struct device *dev) +{ + cts_info("Resume by bus power management"); + return cts_resume(dev_get_drvdata(dev)); +} + +/* bus control the suspend/resume procedure */ +static const struct dev_pm_ops cts_i2c_driver_pm_ops = { + .suspend = cts_i2c_driver_pm_suspend, + .resume = cts_i2c_driver_pm_resume, +}; +#endif /* CONFIG_CTS_PM_GENERIC */ + +#ifdef CONFIG_CTS_SYSFS +static ssize_t reset_pin_show(struct device_driver *driver, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_HAS_RESET_PIN: %c\n", +#ifdef CFG_CTS_HAS_RESET_PIN + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) +static DRIVER_ATTR(reset_pin, S_IRUGO, reset_pin_show, NULL); +#else +static DRIVER_ATTR_RO(reset_pin); +#endif + +static ssize_t swap_xy_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_SWAP_XY: %c\n", +#ifdef CFG_CTS_SWAP_XY + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) +static DRIVER_ATTR(swap_xy, S_IRUGO, swap_xy_show, NULL); +#else +static DRIVER_ATTR_RO(swap_xy); +#endif + +static ssize_t wrap_x_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_WRAP_X: %c\n", +#ifdef CFG_CTS_WRAP_X + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) +static DRIVER_ATTR(wrap_x, S_IRUGO, wrap_x_show, NULL); +#else +static DRIVER_ATTR_RO(wrap_x); +#endif + +static ssize_t wrap_y_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_WRAP_Y: %c\n", +#ifdef CFG_CTS_WRAP_Y + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) +static DRIVER_ATTR(wrap_y, S_IRUGO, wrap_y_show, NULL); +#else +static DRIVER_ATTR_RO(wrap_y); +#endif + +static ssize_t force_update_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_HAS_RESET_PIN: %c\n", +#ifdef CFG_CTS_FIRMWARE_FORCE_UPDATE + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) +static DRIVER_ATTR(force_update, S_IRUGO, force_update_show, NULL); +#else +static DRIVER_ATTR_RO(force_update); +#endif + +static ssize_t max_touch_num_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_MAX_TOUCH_NUM: %d\n", + CFG_CTS_MAX_TOUCH_NUM); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) +static DRIVER_ATTR(max_touch_num, S_IRUGO, max_touch_num_show, NULL); +#else +static DRIVER_ATTR_RO(max_touch_num); +#endif + +static ssize_t vkey_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CONFIG_CTS_VIRTUALKEY: %c\n", +#ifdef CONFIG_CTS_VIRTUALKEY + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) +static DRIVER_ATTR(vkey, S_IRUGO, vkey_show, NULL); +#else +static DRIVER_ATTR_RO(vkey); +#endif + +static ssize_t gesture_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_GESTURE: %c\n", +#ifdef CFG_CTS_GESTURE + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) +static DRIVER_ATTR(gesture, S_IRUGO, gesture_show, NULL); +#else +static DRIVER_ATTR_RO(gesture); +#endif + +static ssize_t esd_protection_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CONFIG_CTS_ESD_PROTECTION: %c\n", +#ifdef CONFIG_CTS_ESD_PROTECTION + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) +static DRIVER_ATTR(esd_protection, S_IRUGO, esd_protection_show, NULL); +#else +static DRIVER_ATTR_RO(esd_protection); +#endif + +static ssize_t slot_protocol_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CONFIG_CTS_SLOTPROTOCOL: %c\n", +#ifdef CONFIG_CTS_SLOTPROTOCOL + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) +static DRIVER_ATTR(slot_protocol, S_IRUGO, slot_protocol_show, NULL); +#else +static DRIVER_ATTR_RO(slot_protocol); +#endif + +static ssize_t max_xfer_size_show(struct device_driver *dev, char *buf) +{ +#ifdef CONFIG_CTS_I2C_HOST + return snprintf(buf, PAGE_SIZE, "CFG_CTS_MAX_I2C_XFER_SIZE: %d\n", + CFG_CTS_MAX_I2C_XFER_SIZE); +#else + return snprintf(buf, PAGE_SIZE, "CFG_CTS_MAX_SPI_XFER_SIZE: %d\n", + CFG_CTS_MAX_SPI_XFER_SIZE); +#endif +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) +static DRIVER_ATTR(max_xfer_size, S_IRUGO, max_xfer_size_show, NULL); +#else +static DRIVER_ATTR_RO(max_xfer_size); +#endif + +static ssize_t driver_info_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "Driver version: %s\n", CFG_CTS_DRIVER_VERSION); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) +static DRIVER_ATTR(driver_info, S_IRUGO, driver_info_show, NULL); +#else +static DRIVER_ATTR_RO(driver_info); +#endif + +static struct attribute *cts_i2c_driver_config_attrs[] = { + &driver_attr_reset_pin.attr, + &driver_attr_swap_xy.attr, + &driver_attr_wrap_x.attr, + &driver_attr_wrap_y.attr, + &driver_attr_force_update.attr, + &driver_attr_max_touch_num.attr, + &driver_attr_vkey.attr, + &driver_attr_gesture.attr, + &driver_attr_esd_protection.attr, + &driver_attr_slot_protocol.attr, + &driver_attr_max_xfer_size.attr, + &driver_attr_driver_info.attr, + NULL +}; + +static const struct attribute_group cts_i2c_driver_config_group = { + .name = "config", + .attrs = cts_i2c_driver_config_attrs, +}; + +static const struct attribute_group *cts_i2c_driver_config_groups[] = { + &cts_i2c_driver_config_group, + NULL, +}; +#endif /* CONFIG_CTS_SYSFS */ + +#ifdef CONFIG_CTS_OF +static const struct of_device_id cts_i2c_of_match_table[] = { + {.compatible = CFG_CTS_OF_DEVICE_ID_NAME,}, + {}, +}; + +MODULE_DEVICE_TABLE(of, cts_i2c_of_match_table); +#endif /* CONFIG_CTS_OF */ + +#ifdef CONFIG_CTS_I2C_HOST +static const struct i2c_device_id cts_device_id_table[] = { + {CFG_CTS_DEVICE_NAME, 0}, + {} +}; +#else +static const struct spi_device_id cts_device_id_table[] = { + {CFG_CTS_DEVICE_NAME, 0}, + {} +}; +#endif + +#ifdef CONFIG_CTS_I2C_HOST +static struct i2c_driver cts_i2c_driver = { +#else +static struct spi_driver cts_spi_driver = { +#endif + .probe = cts_driver_probe, + .remove = cts_driver_remove, + .driver = { + .name = CFG_CTS_DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_CTS_OF + .of_match_table = of_match_ptr(cts_i2c_of_match_table), +#endif /* CONFIG_CTS_OF */ +#ifdef CONFIG_CTS_SYSFS + .groups = cts_i2c_driver_config_groups, +#endif /* CONFIG_CTS_SYSFS */ +#ifdef CONFIG_CTS_PM_LEGACY + .suspend = cts_i2c_driver_suspend, + .resume = cts_i2c_driver_resume, +#endif /* CONFIG_CTS_PM_LEGACY */ +#ifdef CONFIG_CTS_PM_GENERIC + .pm = &cts_i2c_driver_pm_ops, +#endif /* CONFIG_CTS_PM_GENERIC */ + + }, + .id_table = cts_device_id_table, +}; + +static int __init cts_driver_init(void) +{ + cts_info("Init"); + +#ifdef CONFIG_CTS_I2C_HOST + return i2c_add_driver(&cts_i2c_driver); +#else + pr_err("cts_driver_init spi"); + return spi_register_driver(&cts_spi_driver); +#endif +} + +static void __exit cts_driver_exit(void) +{ + cts_info("Exit"); + +#ifdef CONFIG_CTS_I2C_HOST + i2c_del_driver(&cts_i2c_driver); +#else + spi_unregister_driver(&cts_spi_driver); +#endif +} + +module_init(cts_driver_init); +//late_initcall(cts_driver_init); +module_exit(cts_driver_exit); + +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +MODULE_DESCRIPTION("Chipone TDDI touchscreen Driver for QualComm platform"); +MODULE_VERSION(CFG_CTS_DRIVER_VERSION); +MODULE_AUTHOR("Miao Defang "); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_plat_qcom_config.h b/drivers/input/touchscreen/chipone_tddi_mmi/cts_plat_qcom_config.h new file mode 100644 index 000000000000..cc0cb49c3052 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_plat_qcom_config.h @@ -0,0 +1,53 @@ +#ifndef CTS_PLAT_QCOM_CONFIG_H +#define CTS_PLAT_QCOM_CONFIG_H + +#define CONFIG_CTS_PM_FB_NOTIFIER + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER +#ifdef CONFIG_DRM +#define CFG_CTS_DRM_NOTIFIER +#endif /*CONFIG_DRM_MSM */ +#else /*CONFIG_CTS_PM_FB_NOTIFIER */ +#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_SUSPEND) + //#define CONFIG_CTS_PM_GENERIC +#endif /* CONFIG_PM_SLEEP */ + +#if !defined(CONFIG_CTS_PM_GENERIC) +#define CONFIG_CTS_PM_LEGACY +#endif /*CONFIG_CTS_PM_GENERIC */ +#endif /*CONFIG_CTS_PM_FB_NOTIFIER */ + +#define CFG_CTS_MAX_I2C_XFER_SIZE (48u) +#define CFG_CTS_MAX_SPI_XFER_SIZE (1400u) + +#define CTS_FW_LOG_REDIRECT_SIGN 0x60 +#define CTS_FW_LOG_BUF_LEN 128 + +//#define CFG_CTS_SWAP_XY +//#define CFG_CTS_WRAP_X +//#define CFG_CTS_WRAP_Y + +#define CFG_CTS_DEVICE_NAME "chipone-tddi" +#define CFG_CTS_DRIVER_NAME "chipone-tddi" + +#if CFG_CTS_MAX_I2C_XFER_SIZE < 8 +#error "I2C transfer size should large than 8" +#endif + +#ifdef CONFIG_OF +#define CONFIG_CTS_OF +#endif +#ifdef CONFIG_CTS_OF +#define CFG_CTS_OF_DEVICE_ID_NAME "chipone-tddi" + +#define CFG_CTS_OF_INT_GPIO_NAME "chipone,irq-gpio" +#define CFG_CTS_OF_RST_GPIO_NAME "chipone,rst-gpio" +#define CFG_CTS_OF_X_RESOLUTION_NAME "chipone,x-res" +#define CFG_CTS_OF_Y_RESOLUTION_NAME "chipone,y-res" + +#ifdef CFG_CTS_FW_UPDATE_SYS +#define CFG_CTS_OF_PANEL_SUPPLIER "chipone,panel-supplier" +#endif +#endif /* CONFIG_CTS_OF */ + +#endif /* CTS_PLAT_QCOM_CONFIG_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_platform.c b/drivers/input/touchscreen/chipone_tddi_mmi/cts_platform.c new file mode 100644 index 000000000000..2717b54576a7 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_platform.c @@ -0,0 +1,1214 @@ +#define LOG_TAG "Plat" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_firmware.h" +#include "cts_sysfs.h" + +#ifdef CFG_CTS_FW_LOG_REDIRECT +size_t cts_plat_get_max_fw_log_size(struct cts_platform_data *pdata) +{ + return CTS_FW_LOG_BUF_LEN; +} + +u8 *cts_plat_get_fw_log_buf(struct cts_platform_data *pdata, size_t size) +{ + return pdata->fw_log_buf; +} +#endif + +size_t cts_plat_get_max_i2c_xfer_size(struct cts_platform_data *pdata) +{ + return CFG_CTS_MAX_I2C_XFER_SIZE; +} + +#ifdef CONFIG_CTS_I2C_HOST +u8 *cts_plat_get_i2c_xfer_buf(struct cts_platform_data *pdata, + size_t xfer_size) +{ + return pdata->i2c_fifo_buf; +} + +int cts_plat_i2c_write(struct cts_platform_data *pdata, u8 i2c_addr, + const void *src, size_t len, int retry, int delay) +{ + int ret = 0, retries = 0; + + struct i2c_msg msg = { + .flags = 0, + .addr = i2c_addr, + .buf = (u8 *) src, + .len = len, + }; + + do { + ret = i2c_transfer(pdata->i2c_client->adapter, &msg, 1); + if (ret != 1) { + if (ret >= 0) { + ret = -EIO; + } + + if (delay) { + mdelay(delay); + } + continue; + } else { + return 0; + } + } while (++retries < retry); + + return ret; +} + +int cts_plat_i2c_read(struct cts_platform_data *pdata, u8 i2c_addr, + const u8 *wbuf, size_t wlen, void *rbuf, size_t rlen, + int retry, int delay) +{ + int num_msg, ret = 0, retries = 0; + + struct i2c_msg msgs[2] = { + { + .addr = i2c_addr, + .flags = 0, + .buf = (u8 *) wbuf, + .len = wlen}, + { + .addr = i2c_addr, + .flags = I2C_M_RD, + .buf = (u8 *) rbuf, + .len = rlen} + }; + + if (wbuf == NULL || wlen == 0) { + num_msg = 1; + } else { + num_msg = 2; + } + + do { + ret = i2c_transfer(pdata->i2c_client->adapter, + msgs + ARRAY_SIZE(msgs) - num_msg, num_msg); + + if (ret != num_msg) { + if (ret >= 0) { + ret = -EIO; + } + + if (delay) { + mdelay(delay); + } + continue; + } else { + return 0; + } + } while (++retries < retry); + + return ret; +} + +int cts_plat_is_i2c_online(struct cts_platform_data *pdata, u8 i2c_addr) +{ + u8 dummy_bytes[2] = { 0x00, 0x00 }; + int ret; + + ret = + cts_plat_i2c_write(pdata, i2c_addr, dummy_bytes, + sizeof(dummy_bytes), 5, 2); + if (ret) { + cts_err("!!! I2C addr 0x%02x is offline !!!", i2c_addr); + return false; + } else { + cts_dbg("I2C addr 0x%02x is online", i2c_addr); + return true; + } +} +#else +int cts_spi_send_recv(struct cts_platform_data *pdata, size_t len, + u8 *tx_buffer, u8 *rx_buffer) +{ + struct chipone_ts_data *cts_data; + struct spi_message msg; + struct spi_transfer cmd = { + .cs_change = 0, + .delay_usecs = 0, + //.speed_hz = pdata->spi_speed * 1000u, + .speed_hz = speed * 1000u, + .tx_buf = tx_buffer, + .rx_buf = rx_buffer, + .len = len, + //.tx_dma = 0, + //.rx_dma = 0, + .bits_per_word = 8, + }; + int ret = 0; + cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + + //cts_info("spi_speed=%d", speed); + +#if 1 +//#ifdef CFG_CTS_MANUAL_CS +// cts_plat_set_cs(pdata, 0); + cts_data->spi_client->chip_select = 0; +#endif + spi_message_init(&msg); + spi_message_add_tail(&cmd, &msg); + ret = spi_sync(cts_data->spi_client, &msg); + if (ret) { + cts_err("spi sync failed %d", ret); + } +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(pdata, 1); +#endif + return ret; +} + +size_t cts_plat_get_max_spi_xfer_size(struct cts_platform_data *pdata) +{ + return CFG_CTS_MAX_SPI_XFER_SIZE; +} + +u8 *cts_plat_get_spi_xfer_buf(struct cts_platform_data *pdata, + size_t xfer_size) +{ + return pdata->spi_cache_buf; +} + +int cts_plat_spi_write(struct cts_platform_data *pdata, u8 dev_addr, + const void *src, size_t len, int retry, int delay) +{ + int ret = 0, retries = 0; + u16 crc; + size_t data_len; + + if (len > CFG_CTS_MAX_SPI_XFER_SIZE) { + cts_err("write too much data:wlen=%zu\n", len); + return -EIO; + } + + if (pdata->cts_dev->rtdata.program_mode) { + pdata->spi_tx_buf[0] = dev_addr; + memcpy(&pdata->spi_tx_buf[1], src, len); + + do { + ret = + cts_spi_send_recv(pdata, len + 1, pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI write failed %d", ret); + if (delay) { + mdelay(delay); + } + } else { + return 0; + } + } while (++retries < retry); + } else { + data_len = len - 2; + pdata->spi_tx_buf[0] = dev_addr; + pdata->spi_tx_buf[1] = *((u8 *) src + 1); + pdata->spi_tx_buf[2] = *((u8 *) src); + put_unaligned_le16(data_len, &pdata->spi_tx_buf[3]); + crc = (u16) crc32(pdata->spi_tx_buf, 5); + put_unaligned_le16(crc, &pdata->spi_tx_buf[5]); + memcpy(&pdata->spi_tx_buf[7], (char *)src + 2, data_len); + crc = (u16) crc32((char *)src + 2, data_len); + put_unaligned_le16(crc, &pdata->spi_tx_buf[7 + data_len]); + do { + ret = + cts_spi_send_recv(pdata, len + 7, pdata->spi_tx_buf, + pdata->spi_rx_buf); + udelay(10 * data_len); + if (ret) { + cts_err("SPI write failed %d", ret); + if (delay) { + mdelay(delay); + } + } else { + return 0; + } + } while (++retries < retry); + } + return ret; +} + +int cts_plat_spi_read(struct cts_platform_data *pdata, u8 dev_addr, + const u8 *wbuf, size_t wlen, void *rbuf, size_t rlen, + int retry, int delay) +{ + int ret = 0, retries = 0; + u16 crc; + + if (wlen > CFG_CTS_MAX_SPI_XFER_SIZE + || rlen > CFG_CTS_MAX_SPI_XFER_SIZE) { + cts_err("write/read too much data:wlen=%zd, rlen=%zd", wlen, + rlen); + return -EIO; + } + + if (pdata->cts_dev->rtdata.program_mode) { + pdata->spi_tx_buf[0] = dev_addr | 0x01; + memcpy(&pdata->spi_tx_buf[1], wbuf, wlen); + do { + ret = + cts_spi_send_recv(pdata, rlen + 5, + pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) { + mdelay(delay); + } + continue; + } + memcpy(rbuf, pdata->spi_rx_buf + 5, rlen); + return 0; + } while (++retries < retry); + } else { + do { + if (wlen != 0) { + pdata->spi_tx_buf[0] = dev_addr | 0x01; + pdata->spi_tx_buf[1] = wbuf[1]; + pdata->spi_tx_buf[2] = wbuf[0]; + put_unaligned_le16(rlen, &pdata->spi_tx_buf[3]); + crc = (u16) crc32(pdata->spi_tx_buf, 5); + put_unaligned_le16(crc, &pdata->spi_tx_buf[5]); + ret = + cts_spi_send_recv(pdata, 7, + pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) { + mdelay(delay); + } + continue; + } + } + memset(pdata->spi_tx_buf, 0, 7); + pdata->spi_tx_buf[0] = dev_addr | 0x01; + udelay(100); + ret = + cts_spi_send_recv(pdata, rlen + 2, + pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) { + mdelay(delay); + } + continue; + } + memcpy(rbuf, pdata->spi_rx_buf, rlen); + crc = (u16) crc32(pdata->spi_rx_buf, rlen); + if (get_unaligned_le16(&pdata->spi_rx_buf[rlen]) != crc) { + cts_err("SPI RX CRC error: rx_crc %04x != %04x", + get_unaligned_le16(&pdata->spi_rx_buf[rlen]), crc); + continue; + } + return 0; + } while (++retries < retry); + } + if (retries >= retry) { + cts_err("SPI read too much retry"); + } + + return -EIO; +} + +int cts_plat_spi_read_delay_idle(struct cts_platform_data *pdata, u8 dev_addr, + const u8 *wbuf, size_t wlen, void *rbuf, + size_t rlen, int retry, int delay, int idle) +{ + int ret = 0, retries = 0; + u16 crc; + + if (wlen > CFG_CTS_MAX_SPI_XFER_SIZE || + rlen > CFG_CTS_MAX_SPI_XFER_SIZE) { + cts_err("write/read too much data:wlen=%zu, rlen=%zu", wlen, + rlen); + return -E2BIG; + } + + if (pdata->cts_dev->rtdata.program_mode) { + pdata->spi_tx_buf[0] = dev_addr | 0x01; + memcpy(&pdata->spi_tx_buf[1], wbuf, wlen); + do { + ret = + cts_spi_send_recv(pdata, rlen + 5, + pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) { + mdelay(delay); + } + continue; + } + memcpy(rbuf, pdata->spi_rx_buf + 5, rlen); + return 0; + } while (++retries < retry); + } else { + do { + if (wlen != 0) { + pdata->spi_tx_buf[0] = dev_addr | 0x01; + pdata->spi_tx_buf[1] = wbuf[1]; + pdata->spi_tx_buf[2] = wbuf[0]; + put_unaligned_le16(rlen, &pdata->spi_tx_buf[3]); + crc = (u16) crc32(pdata->spi_tx_buf, 5); + put_unaligned_le16(crc, &pdata->spi_tx_buf[5]); + ret = + cts_spi_send_recv(pdata, 7, + pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) { + mdelay(delay); + } + continue; + } + } + memset(pdata->spi_tx_buf, 0, 7); + pdata->spi_tx_buf[0] = dev_addr | 0x01; + udelay(idle); + ret = + cts_spi_send_recv(pdata, rlen + 2, + pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + if (delay) { + mdelay(delay); + } + continue; + } + memcpy(rbuf, pdata->spi_rx_buf, rlen); + crc = (u16) crc32(pdata->spi_rx_buf, rlen); + if (get_unaligned_le16(&pdata->spi_rx_buf[rlen]) != crc) { + continue; + } + return 0; + } while (++retries < retry); + } + if (retries >= retry) { + cts_err("cts_plat_spi_read error"); + } + + return -EIO; +} + +int cts_plat_is_normal_mode(struct cts_platform_data *pdata) +{ + struct chipone_ts_data *cts_data; + u8 tx_buf[4] = { 0 }; + u16 fwid; + u32 addr; + int ret; + + cts_set_normal_addr(pdata->cts_dev); + cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + addr = CTS_DEVICE_FW_REG_CHIP_TYPE; + put_unaligned_be16(addr, tx_buf); + ret = + cts_plat_spi_read(pdata, CTS_DEV_NORMAL_MODE_SPIADDR, tx_buf, 2, + &fwid, 2, 3, 10); + fwid = be16_to_cpu(fwid); + if (ret || !cts_is_fwid_valid(fwid)) { + return false; + } + + return true; +} +#endif + +static void cts_plat_handle_irq(struct cts_platform_data *pdata) +{ + int ret; + + cts_dbg("Handle IRQ"); + + cts_lock_device(pdata->cts_dev); + ret = cts_irq_handler(pdata->cts_dev); + if (ret) { + cts_err("Device handle IRQ failed %d", ret); + } + cts_unlock_device(pdata->cts_dev); +} + +static irqreturn_t cts_plat_irq_handler(int irq, void *dev_id) +{ + struct cts_platform_data *pdata; +#ifndef CONFIG_GENERIC_HARDIRQS + struct chipone_ts_data *cts_data; +#endif /* CONFIG_GENERIC_HARDIRQS */ + + cts_dbg("IRQ handler"); + + pdata = (struct cts_platform_data *)dev_id; + if (pdata == NULL) { + cts_err("IRQ handler with NULL dev_id"); + return IRQ_NONE; + } +#ifdef CONFIG_GENERIC_HARDIRQS + cts_plat_handle_irq(pdata); +#else /* CONFIG_GENERIC_HARDIRQS */ + cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + + if (queue_work(cts_data->workqueue, &pdata->ts_irq_work)) { + cts_dbg("IRQ queue work"); + cts_plat_disable_irq(pdata); + } else { + cts_warn("IRQ handler queue work failed as already on the queue"); + } +#endif /* CONFIG_GENERIC_HARDIRQS */ + + return IRQ_HANDLED; +} + +#ifndef CONFIG_GENERIC_HARDIRQS +static void cts_plat_touch_dev_irq_work(struct work_struct *work) +{ + struct cts_platform_data *pdata = + container_of(work, struct cts_platform_data, ts_irq_work); + + cts_dbg("IRQ work"); + + cts_plat_handle_irq(pdata); + + cts_plat_enable_irq(pdata); +} +#endif /* CONFIG_GENERIC_HARDIRQS */ + +#ifdef CONFIG_CTS_OF +static int cts_plat_parse_dt(struct cts_platform_data *pdata, + struct device_node *dev_node) +{ + int ret = 0; + + cts_info("Parse device tree"); + + pdata->int_gpio = + of_get_named_gpio(dev_node, CFG_CTS_OF_INT_GPIO_NAME, 0); + if (!gpio_is_valid(pdata->int_gpio)) { + cts_err("Parse INT GPIO from dt failed %d", pdata->int_gpio); + pdata->int_gpio = -1; + } + cts_info(" %-12s: %d", "int gpio", pdata->int_gpio); + + pdata->irq = gpio_to_irq(pdata->int_gpio); + if (pdata->irq < 0) { + cts_err("Parse irq failed %d", ret); + return pdata->irq; + } + cts_info(" %-12s: %d", "irq num", pdata->irq); + +#ifdef CFG_CTS_HAS_RESET_PIN + pdata->rst_gpio = + of_get_named_gpio(dev_node, CFG_CTS_OF_RST_GPIO_NAME, 0); + if (!gpio_is_valid(pdata->rst_gpio)) { + cts_err("Parse RST GPIO from dt failed %d", pdata->rst_gpio); + pdata->rst_gpio = -1; + } + cts_info(" %-12s: %d", "rst gpio", pdata->rst_gpio); +#endif /* CFG_CTS_HAS_RESET_PIN */ + + ret = of_property_read_u32(dev_node, CFG_CTS_OF_X_RESOLUTION_NAME, + &pdata->res_x); + if (ret) { + cts_warn("Parse X resolution from dt failed %d", ret); + //return ret; + } + cts_info(" %-12s: %d", "X resolution", pdata->res_x); + + ret = of_property_read_u32(dev_node, CFG_CTS_OF_Y_RESOLUTION_NAME, + &pdata->res_y); + if (ret) { + cts_warn("Parse Y resolution from dt failed %d", ret); + //return ret; + } + cts_info(" %-12s: %d", "Y resolution", pdata->res_y); + + if (of_property_read_u32(dev_node, "chipone,def-build-id", &pdata->build_id)) { + pdata->build_id = 0; + cts_info("chipone,build_id undefined.\n"); + } else { + cts_info("chipone,build_id=0x%04X\n", pdata->build_id); + } + + if (of_property_read_u32(dev_node, "chipone,def-config-id", &pdata->config_id)) { + pdata->config_id = 0; + cts_info("chipone,config_id undefined.\n"); + } else { + cts_info("chipone,config_id=0x%04X\n", pdata->config_id); + } + +#ifdef CFG_CTS_FW_UPDATE_SYS + ret = of_property_read_string(dev_node, CFG_CTS_OF_PANEL_SUPPLIER, + &pdata->panel_supplier); + if (ret) { + pdata->panel_supplier = NULL; + cts_warn("read panel supplier failed, ret=%d\n", ret); + } else { + cts_info("panel supplier=%s", (char *)pdata->panel_supplier); + } +#endif + + return 0; +} +#endif /* CONFIG_CTS_OF */ + +#ifdef CFG_CTS_FORCE_UP +static void cts_plat_touch_event_timeout_work(struct work_struct *work) +{ + struct cts_platform_data *pdata = container_of + (work, struct cts_platform_data, touch_event_timeout_work.work); + + cts_warn("Touch event timeout work"); + + cts_plat_release_all_touch(pdata); +} +#endif + +#ifdef CONFIG_CTS_I2C_HOST +int cts_init_platform_data(struct cts_platform_data *pdata, + struct i2c_client *i2c_client) +#else +int cts_init_platform_data(struct cts_platform_data *pdata, + struct spi_device *spi) +#endif +{ + struct input_dev *input_dev; + int ret = 0; + + cts_info("cts_init_platform_data Init"); + +#ifdef CONFIG_CTS_OF + { + struct device *dev; + +#ifdef CONFIG_CTS_I2C_HOST + dev = &i2c_client->dev; +#else + dev = &spi->dev; +#endif /* CONFIG_CTS_I2C_HOST */ + ret = cts_plat_parse_dt(pdata, dev->of_node); + if (ret) { + cts_err("Parse dt failed %d", ret); + return ret; + } + } +#endif /* CONFIG_CTS_OF */ + +#ifdef CONFIG_CTS_I2C_HOST + pdata->i2c_client = i2c_client; + pdata->i2c_client->irq = pdata->irq; +#else + pdata->spi_client = spi; + pdata->spi_client->irq = pdata->irq; +#endif /* CONFIG_CTS_I2C_HOST */ + spin_lock_init(&pdata->irq_lock); + + input_dev = input_allocate_device(); + if (input_dev == NULL) { + cts_err("Failed to allocate input device."); + return -ENOMEM; + } + + /** - Init input device */ + input_dev->name = CFG_CTS_DEVICE_NAME; + input_dev->name = CFG_CTS_DEVICE_NAME; +#ifdef CONFIG_CTS_I2C_HOST + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &pdata->i2c_client->dev; +#else + input_dev->id.bustype = BUS_SPI; + input_dev->dev.parent = &pdata->spi_client->dev; +#endif /* CONFIG_CTS_I2C_HOST */ + input_dev->evbit[0] = BIT_MASK(EV_SYN) | + BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); +#ifdef CFG_CTS_SWAP_XY + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, pdata->res_y, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, pdata->res_x, 0, 0); +#else /* CFG_CTS_SWAP_XY */ + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, pdata->res_x, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, pdata->res_y, 0, 0); +#endif /* CFG_CTS_SWAP_XY */ + + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, + CFG_CTS_MAX_TOUCH_NUM * 2, 0, 0); + + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + +#ifdef CONFIG_CTS_SLOTPROTOCOL + input_mt_init_slots(input_dev, CFG_CTS_MAX_TOUCH_NUM, 0); +#endif /* CONFIG_CTS_SLOTPROTOCOL */ + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + __set_bit(EV_ABS, input_dev->evbit); + input_set_drvdata(input_dev, pdata); + ret = input_register_device(input_dev); + if (ret) { + cts_err("Failed to register input device"); + return ret; + } + + pdata->ts_input_dev = input_dev; + +#if !defined(CONFIG_GENERIC_HARDIRQS) + INIT_WORK(&pdata->ts_irq_work, cts_plat_touch_dev_irq_work); +#endif /* CONFIG_GENERIC_HARDIRQS */ + +#ifdef CONFIG_CTS_VIRTUALKEY + { + u8 vkey_keymap[CFG_CTS_NUM_VKEY] = CFG_CTS_VKEY_KEYCODES; + memcpy(pdata->vkey_keycodes, vkey_keymap, sizeof(vkey_keymap)); + pdata->vkey_num = CFG_CTS_NUM_VKEY; + } +#endif /* CONFIG_CTS_VIRTUALKEY */ + +#ifdef CFG_CTS_GESTURE + { + u8 gesture_keymap[CFG_CTS_NUM_GESTURE][2] = + CFG_CTS_GESTURE_KEYMAP; + memcpy(pdata->gesture_keymap, gesture_keymap, + sizeof(gesture_keymap)); + pdata->gesture_num = CFG_CTS_NUM_GESTURE; + } +#endif /* CFG_CTS_GESTURE */ + +#ifdef CFG_CTS_FORCE_UP + INIT_DELAYED_WORK(&pdata->touch_event_timeout_work, + cts_plat_touch_event_timeout_work); +#endif + +#ifndef CONFIG_CTS_I2C_HOST + pdata->spi_speed = CFG_CTS_SPI_SPEED_KHZ; +#endif + return 0; +} + +int cts_deinit_platform_data(struct cts_platform_data *pdata) +{ + cts_info("De-Init platform_data"); + input_unregister_device(pdata->ts_input_dev); + return 0; +} + +int cts_plat_request_resource(struct cts_platform_data *pdata) +{ + int ret; + + cts_info("Request resource"); + + ret = gpio_request_one(pdata->int_gpio, GPIOF_IN, + CFG_CTS_DEVICE_NAME "-int"); + if (ret) { + cts_err("Request INT gpio (%d) failed %d", pdata->int_gpio, + ret); + goto err_out; + } +#ifdef CFG_CTS_HAS_RESET_PIN + ret = gpio_request_one(pdata->rst_gpio, GPIOF_OUT_INIT_HIGH, + CFG_CTS_DEVICE_NAME "-rst"); + if (ret) { + cts_err("Request RST gpio (%d) failed %d", pdata->rst_gpio, + ret); + goto err_free_int; + } +#endif /* CFG_CTS_HAS_RESET_PIN */ + + return 0; + +#ifdef CONFIG_CTS_REGULATOR +err_free_rst: +#endif /* CONFIG_CTS_REGULATOR */ +#ifdef CFG_CTS_HAS_RESET_PIN + gpio_free(pdata->rst_gpio); +err_free_int: +#endif /* CFG_CTS_HAS_RESET_PIN */ + gpio_free(pdata->int_gpio); +err_out: + return ret; +} + +void cts_plat_free_resource(struct cts_platform_data *pdata) +{ + cts_info("Free resource"); + + if (gpio_is_valid(pdata->int_gpio)) { + gpio_free(pdata->int_gpio); + } +#ifdef CFG_CTS_HAS_RESET_PIN + if (gpio_is_valid(pdata->rst_gpio)) { + gpio_free(pdata->rst_gpio); + } +#endif /* CFG_CTS_HAS_RESET_PIN */ +} + +int cts_plat_request_irq(struct cts_platform_data *pdata) +{ + int ret; + + cts_info("Request IRQ"); + +#ifdef CONFIG_GENERIC_HARDIRQS + ret = request_threaded_irq(pdata->irq, NULL, + cts_plat_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + CFG_CTS_DRIVER_NAME, pdata); +#else /* CONFIG_GENERIC_HARDIRQS */ + ret = request_irq(pdata->irq, + cts_plat_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + CFG_CTS_DRIVER_NAME, pdata); +#endif /* CONFIG_GENERIC_HARDIRQS */ + if (ret) { + cts_err("Request IRQ failed %d", ret); + return ret; + } + + cts_plat_disable_irq(pdata); + + return 0; +} + +void cts_plat_free_irq(struct cts_platform_data *pdata) +{ + free_irq(pdata->irq, pdata); +} + +int cts_plat_enable_irq(struct cts_platform_data *pdata) +{ + unsigned long irqflags; + + cts_dbg("Enable IRQ"); + + if (pdata->irq > 0) { + spin_lock_irqsave(&pdata->irq_lock, irqflags); + if (pdata->irq_is_disable) { /* && !cts_is_device_suspended(pdata->chip)) */ + cts_dbg("Real enable IRQ"); + enable_irq(pdata->irq); + pdata->irq_is_disable = false; + } + spin_unlock_irqrestore(&pdata->irq_lock, irqflags); + + return 0; + } + + return -ENODEV; +} + +int cts_plat_disable_irq(struct cts_platform_data *pdata) +{ + unsigned long irqflags; + + cts_dbg("Disable IRQ"); + + if (pdata->irq > 0) { + spin_lock_irqsave(&pdata->irq_lock, irqflags); + if (!pdata->irq_is_disable) { + cts_dbg("Real disable IRQ"); + disable_irq_nosync(pdata->irq); + pdata->irq_is_disable = true; + } + spin_unlock_irqrestore(&pdata->irq_lock, irqflags); + + return 0; + } + + return -ENODEV; +} + +#ifdef CFG_CTS_HAS_RESET_PIN +int cts_plat_reset_device(struct cts_platform_data *pdata) +{ + /* !!!can not be modified */ + /* !!!can not be modified */ + /* !!!can not be modified */ + cts_info("Reset device"); + + gpio_set_value(pdata->rst_gpio, 1); + mdelay(1); + gpio_set_value(pdata->rst_gpio, 0); + mdelay(10); + gpio_set_value(pdata->rst_gpio, 1); + mdelay(40); + + return 0; +} + +int cts_plat_set_reset(struct cts_platform_data *pdata, int val) +{ + cts_info("Set Reset to %s", val ? "HIGH" : "LOW"); + if (val) { + gpio_set_value(pdata->rst_gpio, 1); + } else { + gpio_set_value(pdata->rst_gpio, 0); + } + return 0; +} +#endif /* CFG_CTS_HAS_RESET_PIN */ + +int cts_plat_get_int_pin(struct cts_platform_data *pdata) +{ + return gpio_get_value(pdata->int_gpio); +} + +int cts_plat_power_up_device(struct cts_platform_data *pdata) +{ + cts_info("Power up device"); + + return 0; +} + +int cts_plat_power_down_device(struct cts_platform_data *pdata) +{ + cts_info("Power down device"); + + return 0; +} + +int cts_plat_init_touch_device(struct cts_platform_data *pdata) +{ + cts_info("Init touch device"); + + return 0; +} + +void cts_plat_deinit_touch_device(struct cts_platform_data *pdata) +{ + cts_info("De-init touch device"); + +#ifndef CONFIG_GENERIC_HARDIRQS + if (work_pending(&pdata->ts_irq_work)) { + cancel_work_sync(&pdata->ts_irq_work); + } +#endif /* CONFIG_GENERIC_HARDIRQS */ +} + +int cts_plat_process_touch_msg(struct cts_platform_data *pdata, + struct cts_device_touch_msg *msgs, int num) +{ + struct input_dev *input_dev = pdata->ts_input_dev; + int i; + int contact = 0; +#ifdef CONFIG_CTS_SLOTPROTOCOL + static unsigned char finger_last[CFG_CTS_MAX_TOUCH_NUM] = { 0 }; + unsigned char finger_current[CFG_CTS_MAX_TOUCH_NUM] = { 0 }; +#endif + + cts_dbg("Process touch %d msgs", num); + if (num == 0 || num > CFG_CTS_MAX_TOUCH_NUM) { + return 0; + } + + for (i = 0; i < num; i++) { + u16 x, y; + + x = le16_to_cpu(msgs[i].x); + y = le16_to_cpu(msgs[i].y); + +#ifdef CFG_CTS_SWAP_XY + swap(x, y); +#endif /* CFG_CTS_SWAP_XY */ +#ifdef CFG_CTS_WRAP_X + x = wrap(pdata->res_x, x); +#endif /* CFG_CTS_WRAP_X */ +#ifdef CFG_CTS_WRAP_Y + y = wrap(pdata->res_y, y); +#endif /* CFG_CTS_WRAP_Y */ + cts_dbg(" Process touch msg[%d]: id[%u] ev=%u x=%u y=%u p=%u", + i, msgs[i].id, msgs[i].event, x, y, msgs[i].pressure); + if (msgs[i].event == CTS_DEVICE_TOUCH_EVENT_DOWN + || msgs[i].event == CTS_DEVICE_TOUCH_EVENT_MOVE + || msgs[i].event == CTS_DEVICE_TOUCH_EVENT_STAY) { + if (msgs[i].id < CFG_CTS_MAX_TOUCH_NUM) { + finger_current[msgs[i].id] = 1; + } + } +#ifdef CONFIG_CTS_SLOTPROTOCOL + //input_mt_slot(input_dev, msgs[i].id); + switch (msgs[i].event) { + case CTS_DEVICE_TOUCH_EVENT_DOWN: + case CTS_DEVICE_TOUCH_EVENT_MOVE: + case CTS_DEVICE_TOUCH_EVENT_STAY: + contact++; + input_mt_slot(input_dev, msgs[i].id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, + true); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + msgs[i].pressure); + input_report_abs(input_dev, ABS_MT_PRESSURE, + msgs[i].pressure); + break; + + case CTS_DEVICE_TOUCH_EVENT_UP: + //input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + break; + + default: + cts_warn + ("Process touch msg with unknwon event %u id %u", + msgs[i].event, msgs[i].id); + break; + } +#else /* CONFIG_CTS_SLOTPROTOCOL */ + /** + * If the driver reports one of BTN_TOUCH or ABS_PRESSURE + * in addition to the ABS_MT events, the last SYN_MT_REPORT event + * may be omitted. Otherwise, the last SYN_REPORT will be dropped + * by the input core, resulting in no zero-contact event + * reaching userland. + */ + switch (msgs[i].event) { + case CTS_DEVICE_TOUCH_EVENT_DOWN: + case CTS_DEVICE_TOUCH_EVENT_MOVE: + case CTS_DEVICE_TOUCH_EVENT_STAY: + contact++; + input_report_abs(input_dev, ABS_MT_PRESSURE, + msgs[i].pressure); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + msgs[i].pressure); + input_report_key(input_dev, BTN_TOUCH, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_mt_sync(input_dev); + break; + + case CTS_DEVICE_TOUCH_EVENT_UP: + break; + default: + cts_warn + ("Process touch msg with unknwon event %u id %u", + msgs[i].event, msgs[i].id); + break; + } +#endif /* CONFIG_CTS_SLOTPROTOCOL */ + } + +#ifdef CONFIG_CTS_SLOTPROTOCOL + for (i = 0; i < CFG_CTS_MAX_TOUCH_NUM; i++) { + if (finger_last[i] != 0 && finger_current[i] == 0) { + input_mt_slot(input_dev, i); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, + false); + } + finger_last[i] = finger_current[i]; + } + input_report_key(input_dev, BTN_TOUCH, contact > 0); +#else + if (contact == 0) { + input_report_key(input_dev, BTN_TOUCH, 0); + input_mt_sync(input_dev); + } +#endif + input_sync(input_dev); +#ifdef CFG_CTS_FORCE_UP + if (contact) { + struct chipone_ts_data *cts_data; + cts_data = container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + + if (delayed_work_pending(&pdata->touch_event_timeout_work)) { + mod_delayed_work(cts_data->workqueue, + &pdata->touch_event_timeout_work, + msecs_to_jiffies(100)); + } else { + queue_delayed_work(cts_data->workqueue, + &pdata->touch_event_timeout_work, + msecs_to_jiffies(100)); + } + } else { + cancel_delayed_work_sync(&pdata->touch_event_timeout_work); + } +#endif + return 0; +} + +int cts_plat_release_all_touch(struct cts_platform_data *pdata) +{ + struct input_dev *input_dev = pdata->ts_input_dev; + +#if defined(CONFIG_CTS_SLOTPROTOCOL) + int id; +#endif /* CONFIG_CTS_SLOTPROTOCOL */ + + cts_info("Release all touch"); + +#ifdef CONFIG_CTS_SLOTPROTOCOL + for (id = 0; id < CFG_CTS_MAX_TOUCH_NUM; id++) { + input_mt_slot(input_dev, id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + } + input_report_key(input_dev, BTN_TOUCH, 0); +#else + input_report_key(input_dev, BTN_TOUCH, 0); + input_mt_sync(input_dev); +#endif /* CONFIG_CTS_SLOTPROTOCOL */ + input_sync(input_dev); + + return 0; +} + +#ifdef CONFIG_CTS_VIRTUALKEY +int cts_plat_init_vkey_device(struct cts_platform_data *pdata) +{ + int i; + + cts_info("Init VKey"); + + pdata->vkey_state = 0; + + for (i = 0; i < pdata->vkey_num; i++) { + input_set_capability(pdata->ts_input_dev, + EV_KEY, pdata->vkey_keycodes[i]); + } + + return 0; +} + +void cts_plat_deinit_vkey_device(struct cts_platform_data *pdata) +{ + cts_info("De-init VKey"); + + pdata->vkey_state = 0; +} + +int cts_plat_process_vkey(struct cts_platform_data *pdata, u8 vkey_state) +{ + u8 event; + int i; + + event = pdata->vkey_state ^ vkey_state; + + cts_dbg("Process vkey state=0x%02x, event=0x%02x", vkey_state, event); + + for (i = 0; i < pdata->vkey_num; i++) { + input_report_key(pdata->ts_input_dev, + pdata->vkey_keycodes[i], + vkey_state & BIT(i) ? 1 : 0); + } + + pdata->vkey_state = vkey_state; + + return 0; +} + +int cts_plat_release_all_vkey(struct cts_platform_data *pdata) +{ + int i; + + cts_info("Release all vkeys"); + + for (i = 0; i < pdata->vkey_num; i++) { + if (pdata->vkey_state & BIT(i)) { + input_report_key(pdata->ts_input_dev, + pdata->vkey_keycodes[i], 0); + } + } + + pdata->vkey_state = 0; + + return 0; +} +#endif /* CONFIG_CTS_VIRTUALKEY */ + +#ifdef CFG_CTS_GESTURE +int cts_plat_enable_irq_wake(struct cts_platform_data *pdata) +{ + cts_info("Enable IRQ wake"); + + if (pdata->irq > 0) { + if (!pdata->irq_wake_enabled) { + pdata->irq_wake_enabled = true; + return enable_irq_wake(pdata->irq); + } + + cts_warn("Enable irq wake while already disabled"); + return -EINVAL; + } + + cts_warn("Enable irq wake while irq invalid %d", pdata->irq); + return -ENODEV; +} + +int cts_plat_disable_irq_wake(struct cts_platform_data *pdata) +{ + cts_info("Disable IRQ wake"); + + if (pdata->irq > 0) { + if (pdata->irq_wake_enabled) { + pdata->irq_wake_enabled = false; + return disable_irq_wake(pdata->irq); + } + + cts_warn("Disable irq wake while already disabled"); + return -EINVAL; + } + + cts_warn("Disable irq wake while irq invalid %d", pdata->irq); + return -ENODEV; +} + +int cts_plat_init_gesture(struct cts_platform_data *pdata) +{ + int i; + + cts_info("Init gesture"); + + // TODO: If system will issure enable/disable command, comment following line. + //cts_enable_gesture_wakeup(pdata->cts_dev); + + for (i = 0; i < pdata->gesture_num; i++) { + input_set_capability(pdata->ts_input_dev, EV_KEY, + pdata->gesture_keymap[i][1]); + } + + return 0; +} + +void cts_plat_deinit_gesture(struct cts_platform_data *pdata) +{ + cts_info("De-init gesture"); +} + +int cts_plat_process_gesture_info(struct cts_platform_data *pdata, + struct cts_device_gesture_info *gesture_info) +{ + int i; + + cts_info("Process gesture, id=0x%02x", gesture_info->gesture_id); + +#if defined(CFG_CTS_GESTURE_REPORT_KEY) + for (i = 0; i < CFG_CTS_NUM_GESTURE; i++) { + if (gesture_info->gesture_id == pdata->gesture_keymap[i][0]) { + cts_info("Report key[%u]", pdata->gesture_keymap[i][1]); + input_report_key(pdata->ts_input_dev, + pdata->gesture_keymap[i][1], 1); + input_sync(pdata->ts_input_dev); + + input_report_key(pdata->ts_input_dev, + pdata->gesture_keymap[i][1], 0); + input_sync(pdata->ts_input_dev); + + return 0; + } + } +#endif /* CFG_CTS_GESTURE_REPORT_KEY */ + + cts_warn("Process unrecognized gesture id=%u", + gesture_info->gesture_id); + + return -EINVAL; +} + +#endif /* CFG_CTS_GESTURE */ diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_platform.h b/drivers/input/touchscreen/chipone_tddi_mmi/cts_platform.h new file mode 100644 index 000000000000..5f4e6bbeef71 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_platform.h @@ -0,0 +1,257 @@ +#ifndef CTS_PLATFORM_H +#define CTS_PLATFORM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include + +#ifdef CONFIG_OF +#include +#include +#include +#endif /* CONFIG_OF */ + +#include +#include + +#include +#include + +#include "cts_config.h" +#include "cts_core.h" + +extern bool cts_show_debug_log; + +#ifndef LOG_TAG +#define LOG_TAG "" +#endif /* LOG_TAG */ + +#define cts_err(fmt, ...) \ + printk("CTS-" LOG_TAG " " fmt"\n", ##__VA_ARGS__) +#define cts_warn(fmt, ...) \ + printk("CTS-" LOG_TAG " " fmt"\n", ##__VA_ARGS__) +#define cts_info(fmt, ...) \ + printk("CTS-" LOG_TAG " " fmt"\n", ##__VA_ARGS__) +#define cts_dbg(fmt, ...) \ + do { \ + if (cts_show_debug_log) \ + printk("CTS-" LOG_TAG " "fmt"\n", ##__VA_ARGS__); \ + } while (0) + +struct cts_device; +struct cts_device_touch_msg; +struct cts_device_gesture_info; + +struct cts_platform_data { + int irq; + int int_gpio; +#ifdef CFG_CTS_HAS_RESET_PIN + int rst_gpio; +#endif /* CFG_CTS_HAS_RESET_PIN */ + + u32 res_x; + u32 res_y; + +#ifdef CONFIG_CTS_VIRTUALKEY + u8 vkey_num; + u8 vkey_state; + u8 vkey_keycodes[CFG_CTS_MAX_VKEY_NUM]; +#endif /* CONFIG_CTS_VIRTUALKEY */ + +#ifdef CFG_CTS_FW_UPDATE_SYS + const char *panel_supplier; +#endif + u32 build_id; + u32 config_id; + + struct cts_device *cts_dev; + + struct input_dev *ts_input_dev; + +#ifndef CONFIG_GENERIC_HARDIRQS + struct work_struct ts_irq_work; +#endif /* CONFIG_GENERIC_HARDIRQS */ + + struct spinlock irq_lock; + bool irq_is_disable; + +#ifdef CFG_CTS_GESTURE + u8 gesture_num; + u8 gesture_keymap[CFG_CTS_NUM_GESTURE][2]; + bool irq_wake_enabled; +#endif /* CFG_CTS_GESTURE */ + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + struct notifier_block fb_notifier; +#endif /* CONFIG_CTS_PM_FB_NOTIFIER */ + +#ifdef CFG_CTS_FORCE_UP + struct delayed_work touch_event_timeout_work; +#endif + +#ifdef CFG_CTS_FW_LOG_REDIRECT + u8 fw_log_buf[CTS_FW_LOG_BUF_LEN]; +#endif + +#ifdef CONFIG_CTS_I2C_HOST + struct i2c_client *i2c_client; + u8 i2c_fifo_buf[CFG_CTS_MAX_I2C_XFER_SIZE]; +#else + struct spi_device *spi_client; + u8 spi_cache_buf[ALIGN(CFG_CTS_MAX_SPI_XFER_SIZE + 10, 4)]; + u8 spi_rx_buf[ALIGN(CFG_CTS_MAX_SPI_XFER_SIZE + 10, 4)]; + u8 spi_tx_buf[ALIGN(CFG_CTS_MAX_SPI_XFER_SIZE + 10, 4)]; + u32 spi_speed; +#endif /* CONFIG_CTS_I2C_HOST */ + + struct cts_device_touch_info touch_info; +}; + +#ifdef CONFIG_CTS_I2C_HOST +extern size_t cts_plat_get_max_i2c_xfer_size(struct cts_platform_data *pdata); +extern u8 *cts_plat_get_i2c_xfer_buf(struct cts_platform_data *pdata, + size_t xfer_size); +extern int cts_plat_i2c_write(struct cts_platform_data *pdata, u8 i2c_addr, + const void *src, size_t len, int retry, + int delay); +extern int cts_plat_i2c_read(struct cts_platform_data *pdata, u8 i2c_addr, + const u8 *wbuf, size_t wlen, void *rbuf, + size_t rlen, int retry, int delay); +extern int cts_plat_is_i2c_online(struct cts_platform_data *pdata, u8 i2c_addr); +#else /* CONFIG_CTS_I2C_HOST */ +extern size_t cts_plat_get_max_spi_xfer_size(struct cts_platform_data *pdata); +extern u8 *cts_plat_get_spi_xfer_buf(struct cts_platform_data *pdata, + size_t xfer_size); +extern int cts_plat_spi_write(struct cts_platform_data *pdata, u8 i2c_addr, + const void *src, size_t len, int retry, + int delay); +extern int cts_plat_spi_read(struct cts_platform_data *pdata, u8 i2c_addr, + const u8 *wbuf, size_t wlen, void *rbuf, + size_t rlen, int retry, int delay); +extern int cts_plat_spi_read_delay_idle(struct cts_platform_data *pdata, + u8 dev_addr, const u8 *wbuf, + size_t wlen, void *rbuf, size_t rlen, + int retry, int delay, int idle); +extern int cts_spi_send_recv(struct cts_platform_data *pdata, size_t len, + u8 *tx_buffer, u8 *rx_buffer); +#endif /* CONFIG_CTS_I2C_HOST */ + +#ifdef CONFIG_CTS_I2C_HOST +extern int cts_init_platform_data(struct cts_platform_data *pdata, + struct i2c_client *i2c_client); +#else +extern int cts_plat_is_normal_mode(struct cts_platform_data *pdata); +extern int cts_init_platform_data(struct cts_platform_data *pdata, + struct spi_device *spi); +#endif + +extern int cts_deinit_platform_data(struct cts_platform_data *pdata); +extern int cts_plat_request_resource(struct cts_platform_data *pdata); +extern void cts_plat_free_resource(struct cts_platform_data *pdata); + +extern int cts_plat_request_irq(struct cts_platform_data *pdata); +extern void cts_plat_free_irq(struct cts_platform_data *pdata); +extern int cts_plat_enable_irq(struct cts_platform_data *pdata); +extern int cts_plat_disable_irq(struct cts_platform_data *pdata); + +#ifdef CFG_CTS_HAS_RESET_PIN +extern int cts_plat_reset_device(struct cts_platform_data *pdata); +#else /* CFG_CTS_HAS_RESET_PIN */ +static inline int cts_plat_reset_device(struct cts_platform_data *pdata) +{ + return 0; +} +#endif /* CFG_CTS_HAS_RESET_PIN */ + +extern int cts_plat_init_touch_device(struct cts_platform_data *pdata); +extern void cts_plat_deinit_touch_device(struct cts_platform_data *pdata); +extern int cts_plat_process_touch_msg(struct cts_platform_data *pdata, + struct cts_device_touch_msg *msgs, + int num); +extern int cts_plat_release_all_touch(struct cts_platform_data *pdata); + +#ifdef CONFIG_CTS_VIRTUALKEY +extern int cts_plat_init_vkey_device(struct cts_platform_data *pdata); +extern void cts_plat_deinit_vkey_device(struct cts_platform_data *pdata); +extern int cts_plat_process_vkey(struct cts_platform_data *pdata, + u8 vkey_state); +extern int cts_plat_release_all_vkey(struct cts_platform_data *pdata); +#else /* CONFIG_CTS_VIRTUALKEY */ +static inline int cts_plat_init_vkey_device(struct cts_platform_data *pdata) +{ + return 0; +} + +static inline void cts_plat_deinit_vkey_device(struct cts_platform_data *pdata) +{ +} + +static inline int cts_plat_process_vkey(struct cts_platform_data *pdata, + u8 vkey_state) +{ + return 0; +} + +static inline int cts_plat_release_all_vkey(struct cts_platform_data *pdata) +{ + return 0; +} +#endif /* CONFIG_CTS_VIRTUALKEY */ + +#ifdef CFG_CTS_GESTURE +extern int cts_plat_enable_irq_wake(struct cts_platform_data *pdata); +extern int cts_plat_disable_irq_wake(struct cts_platform_data *pdata); + +extern int cts_plat_init_gesture(struct cts_platform_data *pdata); +extern void cts_plat_deinit_gesture(struct cts_platform_data *pdata); +extern int cts_plat_process_gesture_info(struct cts_platform_data *pdata, + struct cts_device_gesture_info + *gesture_info); +#else /* CFG_CTS_GESTURE */ +static inline int cts_plat_init_gesture(struct cts_platform_data *pdata) +{ + return 0; +} + +static inline void cts_plat_deinit_gesture(struct cts_platform_data *pdata) +{ +} +#endif /* CFG_CTS_GESTURE */ + +#ifdef CFG_CTS_FW_LOG_REDIRECT +extern size_t cts_plat_get_max_fw_log_size(struct cts_platform_data *pdata); +extern u8 *cts_plat_get_fw_log_buf(struct cts_platform_data *pdata, + size_t size); +#endif + +extern int cts_plat_set_reset(struct cts_platform_data *pdata, int val); +extern int cts_plat_get_int_pin(struct cts_platform_data *pdata); + +#endif /* CTS_PLATFORM_H */ + diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_sfctrl.h b/drivers/input/touchscreen/chipone_tddi_mmi/cts_sfctrl.h new file mode 100644 index 000000000000..81a8c63c5a17 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_sfctrl.h @@ -0,0 +1,38 @@ +#ifndef CTS_SFCTRL_H +#define CTS_SFCTRL_H + +#include "cts_core.h" +#include "cts_spi_flash.h" + +struct cts_device; + +struct cts_sfctrl_ops { + int (*rdid) (const struct cts_device *cts_dev, u32 *id); + int (*se) (const struct cts_device *cts_dev, u32 sector_addr); + int (*be) (const struct cts_device *cts_dev, u32 sector_addr); + int (*read) (const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t len); + int (*read_to_sram) (const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t len); + int (*program) (const struct cts_device *cts_dev, + u32 flash_addr, const void *src, size_t len); + int (*program_from_sram) (const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t len); + int (*calc_sram_crc) (const struct cts_device *cts_dev, + u32 sram_addr, size_t len, u32 *crc); + int (*calc_flash_crc) (const struct cts_device *cts_dev, + u32 flash_addr, size_t len, u32 *crc); + +}; + +struct cts_sfctrl { + u32 reg_base; + u32 xchg_sram_base; + size_t xchg_sram_size; + + const struct cts_sfctrl_ops *ops; +}; + +extern const struct cts_sfctrl_ops cts_sfctrlv2_ops; + +#endif /* CTS_SFCTRL_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_sfctrlv2.c b/drivers/input/touchscreen/chipone_tddi_mmi/cts_sfctrlv2.c new file mode 100644 index 000000000000..3b0f1a06c700 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_sfctrlv2.c @@ -0,0 +1,347 @@ +#define LOG_TAG "SFCtrl" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sfctrl.h" + +#define rSFCTRLv2_CMD_SEL (0x0000) +#define rSFCTRLv2_FLASH_ADDR (0x0004) +#define rSFCTRLv2_SRAM_ADDR (0x0008) +#define rSFCTRLv2_DATA_LENGTH (0x000C) +#define rSFCTRLv2_START_DEXC (0x0010) +#define rSFCTRLv2_RELEASE_FLASH (0x0014) +#define rSFCTRLv2_HW_STATE (0x0018) +#define rSFCTRLv2_CRC_RESULT (0x001C) +#define rSFCTRLv2_SRAM_CRC_START (0x0020) +#define rSFCTRLv2_FLASH_CRC_START (0x0022) +#define rSFCTRLv2_SF_BUSY (0x0024) + +/** Constants for register @ref rSFCTRLv2_CMD_SEL */ +enum sfctrlv2_cmd { + SFCTRLv2_CMD_FAST_READ = 0x01u, + SFCTRLv2_CMD_SE = 0x02u, + SFCTRLv2_CMD_BE = 0x03u, + SFCTRLv2_CMD_PP = 0x04u, + SFCTRLv2_CMD_RDSR = 0x05u, + SFCTRLv2_CMD_RDID = 0x06u +}; + +/** SPI flash controller v2 operation flags. */ +enum sfctrlv2_opflags { + SFCTRLv2_OPFLAG_READ = BIT(0), + SFCTRLv2_OPFLAG_SET_FLASH_ADDR = BIT(1), + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG = BIT(2), + SFCTRLv2_OPFLAG_SET_DATA_LENGTH = BIT(3), + SFCTRLv2_OPFLAG_WAIT_WIP_CLR = BIT(4), + +}; + +#define SFCTRLv2_CMD_RDID_FLAGS \ + (SFCTRLv2_OPFLAG_READ | \ + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG) + +#define SFCTRLv2_CMD_RDSR_FLAGS \ + (SFCTRLv2_OPFLAG_READ | \ + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG) + +#define SFCTRLv2_CMD_SE_FLAGS \ + (SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \ + SFCTRLv2_OPFLAG_WAIT_WIP_CLR) + +#define SFCTRLv2_CMD_BE_FLAGS \ + (SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \ + SFCTRLv2_OPFLAG_WAIT_WIP_CLR) + +#define SFCTRLv2_CMD_PP_FLAGS \ + (SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \ + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG | \ + SFCTRLv2_OPFLAG_SET_DATA_LENGTH | \ + SFCTRLv2_OPFLAG_WAIT_WIP_CLR) + +#define SFCTRLv2_CMD_FAST_READ_FLAGS \ + (SFCTRLv2_OPFLAG_READ | \ + SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \ + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG | \ + SFCTRLv2_OPFLAG_SET_DATA_LENGTH) + +#define sfctrl_reg_addr(cts_dev, offset) \ + ((cts_dev)->hwdata->sfctrl->reg_base + offset) + +#define DEFINE_SFCTRL_REG_ACCESS_FUNC(access_type, data_type) \ + static inline int sfctrl_reg_ ## access_type(const struct cts_device *cts_dev, \ + u32 reg, data_type data) { \ + return cts_hw_reg_ ## access_type(cts_dev, sfctrl_reg_addr(cts_dev, reg), data); \ + } + +DEFINE_SFCTRL_REG_ACCESS_FUNC(writeb_relaxed, u8) + DEFINE_SFCTRL_REG_ACCESS_FUNC(writew_relaxed, u16) + DEFINE_SFCTRL_REG_ACCESS_FUNC(writel_relaxed, u32) + DEFINE_SFCTRL_REG_ACCESS_FUNC(readb_relaxed, u8 *) + DEFINE_SFCTRL_REG_ACCESS_FUNC(readw_relaxed, u16 *) + DEFINE_SFCTRL_REG_ACCESS_FUNC(readl_relaxed, u32 *) +#define sfctrl_write_reg_check_ret(access_type, reg, val) \ + do { \ + int ret; \ + cts_dbg("Write " #reg " to 0x%x", val); \ + ret = sfctrl_reg_ ## access_type(cts_dev, reg, val); \ + if (ret) { \ + cts_err("Write " #reg " failed %d", ret); \ + return ret; \ + } \ + } while (0) +#define sfctrl_read_reg_check_ret(access_type, reg, val) \ + do { \ + int ret; \ + cts_dbg("Read " #reg ""); \ + ret = sfctrl_reg_ ## access_type(cts_dev, reg, val); \ + if (ret) { \ + cts_err("Read " #reg " failed %d", ret); \ + return ret; \ + } \ + } while (0) +static int wait_sfctrl_xfer_comp(const struct cts_device *cts_dev) +{ + int retries = 0; + u8 status; + + do { + sfctrl_read_reg_check_ret(readb_relaxed, rSFCTRLv2_SF_BUSY, &status); + if (status == 0) { + return 0; + } + + mdelay(1); + } while (status && retries++ < 1000); + + return -ETIMEDOUT; +} + +static int sfctrlv2_rdsr(const struct cts_device *cts_dev, u8 *status) +{ +#define RDSR_XCHG_SRAM_ADDR (cts_dev->hwdata->sram_size - 1) + + int ret; + + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_CMD_SEL, + SFCTRLv2_CMD_RDSR); + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_SRAM_ADDR, + RDSR_XCHG_SRAM_ADDR); + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_START_DEXC, 1); + + ret = wait_sfctrl_xfer_comp(cts_dev); + if (ret != 0) { + cts_err("Wait sfctrl ready failed %d", ret); + return ret; + } + + ret = cts_sram_readb(cts_dev, RDSR_XCHG_SRAM_ADDR, status); + if (ret) { + cts_err("Read exchange sram failed %d", ret); + return ret; + } +#undef RDSR_XCHG_SRAM_ADDR + + return 0; +} + +static int wait_flash_wip_clear(const struct cts_device *cts_dev) +{ +#define FLASH_SR_WIP BIT(0) /*!< Write in progress */ + + int ret, retries = 0; + u8 status; + + do { + ret = sfctrlv2_rdsr(cts_dev, &status); + if (ret) { + cts_err("Read flash status register failed %d", ret); + return ret; + } + + if (status & FLASH_SR_WIP) { + mdelay(1); + } else { + return 0; + } + } while (status & FLASH_SR_WIP && retries++ < 1000); + + return -ETIMEDOUT; +#undef FLASH_SR_WIP +} + +static int sfctrlv2_transfer(const struct cts_device *cts_dev, + u8 cmd, void *data, u32 flash_addr, u32 sram_addr, + size_t size, u32 flags) +{ + int ret; + + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_CMD_SEL, cmd); + + if (flags & SFCTRLv2_OPFLAG_SRAM_DATA_XCHG) { + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_SRAM_ADDR, + sram_addr); + + /** - Write data to exchange SRAM if operation is write and + * data != NULL(data not in SRAM) */ + if ((!(flags & SFCTRLv2_OPFLAG_READ)) && data) { + ret = cts_sram_writesb(cts_dev, sram_addr, data, size); + if (ret) { + cts_err + ("Write data to exchange sram 0x%06x size %zu failed %d", + sram_addr, size, ret); + return ret; + } + } + } + + if (flags & SFCTRLv2_OPFLAG_SET_FLASH_ADDR) { + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_FLASH_ADDR, + flash_addr); + } + if (flags & SFCTRLv2_OPFLAG_SET_DATA_LENGTH) { + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_DATA_LENGTH, + (u32) size); + } + + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_START_DEXC, 1); + + ret = wait_sfctrl_xfer_comp(cts_dev); + if (ret != 0) { + cts_err("Wait sfctrl ready failed %d", ret); + return ret; + } + + ret = wait_flash_wip_clear(cts_dev); + if (flags & SFCTRLv2_OPFLAG_WAIT_WIP_CLR && ret != 0) { + cts_err("Wait WIP clear failed %d", ret); + return ret; + } + + if ((flags & SFCTRLv2_OPFLAG_READ) && data) { + ret = cts_sram_readsb(cts_dev, sram_addr, data, size); + if (ret) { + cts_err + ("Read data from exchange sram 0x%06x size %zu failed %d", + sram_addr, size, ret); + return ret; + } + } + + return 0; +} + +static int sfctrlv2_rdid(const struct cts_device *cts_dev, u32 *id) +{ + int ret; + u8 buf[4]; + + ret = sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_RDID, buf, + 0, cts_dev->hwdata->sfctrl->xchg_sram_base, 3, + SFCTRLv2_CMD_RDID_FLAGS); + *id = ret ? 0 : get_unaligned_be24(buf); + + return ret; +} + +static int sfctrlv2_se(const struct cts_device *cts_dev, u32 sector_addr) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_SE, NULL, + sector_addr, 0, 0, SFCTRLv2_CMD_SE_FLAGS); +} + +static int sfctrlv2_be(const struct cts_device *cts_dev, u32 block_addr) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_BE, NULL, + block_addr, 0, 0, SFCTRLv2_CMD_BE_FLAGS); +} + +static int sfctrlv2_pp(const struct cts_device *cts_dev, + u32 flash_addr, const void *src, size_t size) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_PP, (void *)src, + flash_addr, + cts_dev->hwdata->sfctrl->xchg_sram_base, size, + SFCTRLv2_CMD_PP_FLAGS); +} + +static int sfctrlv2_program_flash_from_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, + size_t len) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_PP, NULL, + flash_addr, sram_addr, len, + SFCTRLv2_CMD_PP_FLAGS); +} + +static int sfctrlv2_read(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t size) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_FAST_READ, dst, + flash_addr, + cts_dev->hwdata->sfctrl->xchg_sram_base, size, + SFCTRLv2_CMD_FAST_READ_FLAGS); +} + +static int sfctrlv2_read_flash_to_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, + size_t size) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_FAST_READ, NULL, + flash_addr, sram_addr, size, + SFCTRLv2_CMD_FAST_READ_FLAGS); +} + +int sfctrlv2_calc_sram_crc(const struct cts_device *cts_dev, + u32 sram_addr, size_t size, u32 *crc) +{ + int ret; + + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_SRAM_ADDR, sram_addr); + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_DATA_LENGTH, (u32) size); + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_SRAM_CRC_START, 1); + + ret = wait_sfctrl_xfer_comp(cts_dev); + if (ret != 0) { + cts_err("Wait sfctrl ready failed %d", ret); + return ret; + } + + sfctrl_read_reg_check_ret(readl_relaxed, rSFCTRLv2_CRC_RESULT, crc); + + return 0; +} + +int sfctrlv2_calc_flash_crc(const struct cts_device *cts_dev, + u32 flash_addr, size_t size, u32 *crc) +{ + int ret; + + cts_info("Calc crc from flash 0x%06x size %zu", flash_addr, size); + + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_FLASH_ADDR, flash_addr); + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_DATA_LENGTH, (u32) size); + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_FLASH_CRC_START, 1); + + ret = wait_sfctrl_xfer_comp(cts_dev); + if (ret != 0) { + cts_err("Wait sfctrl ready failed %d", ret); + return ret; + } + + sfctrl_read_reg_check_ret(readl_relaxed, rSFCTRLv2_CRC_RESULT, crc); + + return 0; +} + +const struct cts_sfctrl_ops cts_sfctrlv2_ops = { + .rdid = sfctrlv2_rdid, + .se = sfctrlv2_se, + .be = sfctrlv2_be, + .read = sfctrlv2_read, + .read_to_sram = sfctrlv2_read_flash_to_sram, + .program = sfctrlv2_pp, + .program_from_sram = sfctrlv2_program_flash_from_sram, + .calc_sram_crc = sfctrlv2_calc_sram_crc, + .calc_flash_crc = sfctrlv2_calc_flash_crc, +}; diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_spi_flash.c b/drivers/input/touchscreen/chipone_tddi_mmi/cts_spi_flash.c new file mode 100755 index 000000000000..fc0d732edfcd --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_spi_flash.c @@ -0,0 +1,620 @@ +#define LOG_TAG "Flash" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sfctrl.h" +#include "cts_spi_flash.h" + +/* NOTE: double check command sets and memory organization when you add + * more flash chips. This current list focusses on newer chips, which + * have been converging on command sets which including JEDEC ID. + */ +#if 0 +static const struct cts_flash cts_flashes[] = { + /* Winbond */ + {"W25Q10EW", 0xEF6011, 256, 0x1000, 0x8000, 0x20000}, + {"W25Q20EW", 0xEF6012, 256, 0x1000, 0x8000, 0x40000}, + {"W25Q40EW", 0xEF6013, 256, 0x1000, 0x8000, 0x80000}, + {"W25Q20BW", 0xEF5012, 256, 0x1000, 0x8000, 0x40000}, + {"W25Q40BW", 0xEF5013, 256, 0x1000, 0x8000, 0x80000}, + + /* Giga device */ + {"GD25LQ10B", 0xC86011, 256, 0x1000, 0x8000, 0x20000}, + {"GD25LD20CEIGR", 0xC86012, 256, 0x1000, 0x8000, 0x40000}, + {"GD25LD40CEIGR", 0xC86013, 256, 0x1000, 0x8000, 0x80000}, + + /* Macronix */ + {"MX25U1001E", 0xC22531, 32, 0x1000, 0x10000, 0x20000}, + {"MX25R2035F", 0xC22812, 256, 0x1000, 0x8000, 0x40000}, + + /* Puya-Semi */ + {"P25Q20LUVHIR", 0x856012, 256, 0x1000, 0x8000, 0x40000}, + {"P25Q40LUXHIR", 0x856013, 256, 0x1000, 0x8000, 0x80000}, + {"P25Q11L", 0x854011, 256, 0x1000, 0x8000, 0x20000}, + {"P25Q21L", 0x854012, 256, 0x1000, 0x8000, 0x40000}, + + /* Boya */ + {"BY25D10", 0x684011, 256, 0x1000, 0x8000, 0x20000}, + + /* EON */ + {"EN25S20A", 0x1C3812, 256, 0x1000, 0x8000, 0x40000}, + + /* XTXTECH */ + {"XT25Q01", 0x0B6011, 256, 0x1000, 0, 0x20000}, + {"XT25Q02", 0x0B6012, 256, 0x1000, 0, 0x40000}, + + /* Xinxin-Semi */ + {"XM25QU20B", 0x205012, 256, 0x1000, 0x8000, 0x40000}, + {"XM25QU40B", 0x205013, 256, 0x1000, 0x8000, 0x80000}, +}; +#endif +/* +static const struct cts_flash *find_flash_by_jedec_id(u32 jedec_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cts_flashes); i++) { + if (cts_flashes[i].jedec_id == jedec_id) { + return &cts_flashes[i]; + } + } + + return NULL; +} +*/ +static int probe_flash(struct cts_device *cts_dev) +{ + //for no-flash project + return -ENODEV; + + /* + int ret; + + cts_info("Probe flash"); + + if (cts_dev->hwdata->sfctrl->ops->rdid != NULL) { + u32 id; + + cts_info("Read JEDEC ID"); + + ret = cts_dev->hwdata->sfctrl->ops->rdid(cts_dev, &id); + if (ret) { + cts_err("Read JEDEC ID failed %d", ret); + return ret; + } + + cts_dev->flash = find_flash_by_jedec_id(id); + if (cts_dev->flash == NULL) { + cts_err("Unknown JEDEC ID: %06x", id); + return -ENODEV; + } + + cts_info("Flash type: '%s'", cts_dev->flash->name); + return 0; + } else { + cts_err("Probe flash while rdid == NULL"); + return -ENOTSUPP; + } + */ +} + +/** Make sure sector addr is sector aligned && < flash total size */ +static int erase_sector_retry(const struct cts_device *cts_dev, + u32 sector_addr, int retry) +{ + int ret, retries; + + cts_info(" Erase sector 0x%06x", sector_addr); + + retries = 0; + do { + retries++; + ret = cts_dev->hwdata->sfctrl->ops->se(cts_dev, sector_addr); + if (ret) { + cts_err("Erase sector 0x%06x failed %d retries %d", + sector_addr, ret, retries); + continue; + } + } while (retries < retry); + + return ret; +} + +/** Make sure sector addr is sector aligned && < flash total size */ +static inline int erase_sector(const struct cts_device *cts_dev, + u32 sector_addr) +{ + return erase_sector_retry(cts_dev, sector_addr, + CTS_FLASH_ERASE_DEFAULT_RETRY); +} + +/** Make sure block addr is block aligned && < flash total size */ +static int erase_block_retry(const struct cts_device *cts_dev, + u32 block_addr, int retry) +{ + int ret, retries; + + cts_info(" Erase block 0x%06x", block_addr); + + retries = 0; + do { + retries++; + + ret = cts_dev->hwdata->sfctrl->ops->be(cts_dev, block_addr); + if (ret) { + cts_err("Erase block 0x%06x failed %d retries %d", + block_addr, ret, retries); + continue; + } + } while (retries < retry); + + return ret; +} + +/** Make sure block addr is block aligned && < flash total size */ +static inline int erase_block(const struct cts_device *cts_dev, u32 block_addr) +{ + return erase_block_retry(cts_dev, block_addr, + CTS_FLASH_ERASE_DEFAULT_RETRY); +} + +int cts_prepare_flash_operation(struct cts_device *cts_dev) +{ + int ret; + u8 diva = 0; + bool program_mode = cts_is_device_program_mode(cts_dev); + bool enabled = cts_is_device_enabled(cts_dev); + + cts_info("Prepare for flash operation"); + + if (enabled) { + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } + } + + if (!program_mode) { + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + goto err_start_device; + } + } + + ret = cts_hw_reg_readb_retry(cts_dev, 0x30033, &diva, 5, 0); + if (ret) { + cts_warn("Read DIVA failed %d", ret); + } else { + cts_dbg("Device DIVA = %d", diva); + } + + if (ret == 0 && diva == 0x0C) { + cts_info("DIVA is ready already"); + } else { + int retries; + + /* Set HCLK to 10MHz */ + ret = cts_hw_reg_writeb_retry(cts_dev, 0x30033, 0x0C, 5, 0); + if (ret) { + cts_err("Write DIVA failed %d", ret); + goto err_enter_normal_mode; + } + + /* Reset SFCTL */ + ret = cts_hw_reg_writeb_retry(cts_dev, 0x30008, 0xFB, 5, 0); + if (ret) { + cts_err("Reset sfctl failed %d", ret); + goto err_enter_normal_mode; + } + + retries = 0; + do { + u8 state; + + ret = cts_hw_reg_readb_relaxed(cts_dev, 0x3401B, &state); + if (ret == 0 && (state & 0x40) != 0) { + goto init_flash; + } + + mdelay(2); + } while (++retries < 1000); + + cts_warn("Wait SFCTRL ready failed %d", ret); + // Go through and try + } + +init_flash: + if (cts_dev->flash == NULL) { + cts_info("Flash is not initialized, try to probe..."); + ret = probe_flash(cts_dev); + if (ret != 0) { + cts_dev->rtdata.has_flash = false; + cts_warn("Probe flash failed %d", ret); + //dzy goto err_enter_normal_mode; + return 0; + } + } + cts_dev->rtdata.has_flash = true; + return 0; + +err_enter_normal_mode: + if (!program_mode) { + int r = cts_enter_normal_mode(cts_dev); + if (r) { + cts_err("Enter normal mode failed %d", r); + } + } +err_start_device: + if (enabled) { + int r = cts_start_device(cts_dev); + if (r) { + cts_err("Start device failed %d", r); + } + } + + return ret; +} + +int cts_post_flash_operation(struct cts_device *cts_dev) +{ + cts_info("Post flash operation"); + + return cts_hw_reg_writeb_retry(cts_dev, 0x30033, 4, 5, 0); +} + +int cts_read_flash_retry(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t size, int retry) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret; + + cts_info("Read from 0x%06x size %zu", flash_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || sfctrl->ops->read == NULL) { + cts_err("Read not supported"); + return -ENOTSUPP; + } + + if (flash_addr > flash->total_size) { + cts_err("Read from 0x%06x > flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Read actually from 0x%06x size %zu", flash_addr, size); + + while (size) { + size_t l; + + l = min(sfctrl->xchg_sram_size, size); + + ret = sfctrl->ops->read(cts_dev, flash_addr, dst, l); + if (ret < 0) { + cts_err("Read from 0x%06x size %zu failed %d", + flash_addr, size, ret); + return ret; + } + + dst += l; + size -= l; + flash_addr += l; + } + + return 0; +} + +int cts_read_flash_to_sram_retry(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t size, + int retry) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret, retries; + + cts_info("Read from 0x%06x to sram 0x%06x size %zu", + flash_addr, sram_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || sfctrl->ops->read_to_sram == NULL) { + cts_err("Read to sram not supported"); + return -ENOTSUPP; + } + + if (flash_addr > flash->total_size) { + cts_err("Read to sram from 0x%06x > flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Read to sram actually from 0x%06x size %zu", flash_addr, + size); + + retries = 0; + do { + retries++; + ret = + sfctrl->ops->read_to_sram(cts_dev, flash_addr, sram_addr, + size); + if (ret) { + cts_err + ("Read from 0x%06x to sram 0x%06x size %zu failed %d retries %d", + flash_addr, sram_addr, size, ret, retries); + continue; + } + } while (retries < retry); + + return ret; +} + +int cts_read_flash_to_sram_check_crc_retry(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, + size_t size, u32 crc, int retry) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret, retries; + + cts_info("Read from 0x%06x to sram 0x%06x size %zu with crc check", + flash_addr, sram_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || + sfctrl->ops->read_to_sram == NULL || + sfctrl->ops->calc_sram_crc == NULL) { + cts_err("Read to sram check crc not supported"); + return -ENOTSUPP; + } + + if (flash_addr > flash->total_size) { + cts_err("Read to sram from 0x%06x > flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Read to sram actually from 0x%06x size %zu with crc check", + flash_addr, size); + + retries = 0; + do { + u32 crc_sram; + + retries++; + + ret = + sfctrl->ops->read_to_sram(cts_dev, flash_addr, sram_addr, + size); + if (ret) { + cts_err + ("Read from 0x%06x to sram 0x%06x size %zu failed %d retries %d", + flash_addr, sram_addr, size, ret, retries); + continue; + } + + ret = + sfctrl->ops->calc_sram_crc(cts_dev, sram_addr, size, + &crc_sram); + if (ret) { + cts_err + ("Get crc for read from 0x%06x to sram 0x%06x size %zu " + "failed %d retries %d", flash_addr, sram_addr, + size, ret, retries); + continue; + } + + if (crc == crc_sram) { + return 0; + } + + cts_err + ("Check crc for read from 0x%06x to sram 0x%06x size %zu " + "mismatch %x != %x, retries %d", flash_addr, sram_addr, + size, crc, crc_sram, retries); + ret = -EFAULT; + } while (retries < retry); + + return ret; +} + +int cts_program_flash(const struct cts_device *cts_dev, + u32 flash_addr, const void *src, size_t size) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret; + + cts_info("Program to 0x%06x size %zu", flash_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || sfctrl->ops->program == NULL) { + cts_err("Program not supported"); + return -ENOTSUPP; + } + + if (flash_addr >= flash->total_size) { + cts_err("Program from 0x%06x >= flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Program actually to 0x%06x size %zu", flash_addr, size); + + while (size) { + size_t l, offset; + + l = min(flash->page_size, size); + offset = flash_addr & (flash->page_size - 1); + + if (offset) { + l = min(flash->page_size - offset, l); + } + + ret = sfctrl->ops->program(cts_dev, flash_addr, src, l); + if (ret) { + cts_err("Program to 0x%06x size %zu failed %d", + flash_addr, l, ret); + return ret; + } + + src += l; + size -= l; + flash_addr += l; + } + + return 0; +} + +int cts_program_flash_from_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t size) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret; + + cts_info("Program to 0x%06x from sram 0x%06x size %zu", + flash_addr, sram_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || sfctrl->ops->program_from_sram == NULL) { + cts_err("Program from sram not supported"); + return -ENOTSUPP; + } + + if (flash_addr >= flash->total_size) { + cts_err("Program from 0x%06x >= flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Program actually to 0x%06x from sram 0x%06x size %zu", + flash_addr, sram_addr, size); + + while (size) { + size_t l, offset; + + l = min(flash->page_size, size); + offset = flash_addr & (flash->page_size - 1); + + if (offset) { + l = min(flash->page_size - offset, l); + } + + ret = + sfctrl->ops->program_from_sram(cts_dev, flash_addr, + sram_addr, l); + if (ret) { + cts_err + ("Program to 0x%06x from sram 0x%06x size %zu failed %d", + flash_addr, sram_addr, l, ret); + return ret; + } + + size -= l; + flash_addr += l; + sram_addr += l; + } + + return 0; +} + +int cts_erase_flash(const struct cts_device *cts_dev, u32 addr, size_t size) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret; + + cts_info("Erase from 0x%06x size %zu", addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || sfctrl->ops == NULL || + sfctrl->ops->se == NULL || sfctrl->ops->be == NULL || + flash == NULL) { + cts_err("Oops"); + return -EINVAL; + } + + /* Addr and size MUST sector aligned */ + addr = rounddown(addr, flash->sector_size); + size = roundup(size, flash->sector_size); + + if (addr > flash->total_size) { + cts_err("Erase from 0x%06x > flash size 0x%06zx", + addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - addr); + + cts_info("Erase actually from 0x%06x size %zu", addr, size); + + if (flash->block_size) { + while (addr != ALIGN(addr, flash->block_size) && + size >= flash->sector_size) { + ret = erase_sector(cts_dev, addr); + if (ret) { + cts_err + ("Erase sector 0x%06x size 0x%04zx failed %d", + addr, flash->sector_size, ret); + return ret; + } + addr += flash->sector_size; + size -= flash->sector_size; + } + + while (size >= flash->block_size) { + ret = erase_block(cts_dev, addr); + if (ret) { + cts_err + ("Erase block 0x%06x size 0x%04zx failed %d", + addr, flash->block_size, ret); + return ret; + } + addr += flash->block_size; + size -= flash->block_size; + } + } + + while (size >= flash->sector_size) { + ret = erase_sector(cts_dev, addr); + if (ret) { + cts_err("Erase sector 0x%06x size 0x%04zx failed %d", + addr, flash->sector_size, ret); + return ret; + } + addr += flash->sector_size; + size -= flash->sector_size; + } + + return 0; +} diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_spi_flash.h b/drivers/input/touchscreen/chipone_tddi_mmi/cts_spi_flash.h new file mode 100644 index 000000000000..e610bdcb75ac --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_spi_flash.h @@ -0,0 +1,70 @@ +#ifndef CTS_SPI_FLASH_H +#define CTS_SPI_FLASH_H + +struct cts_flash { + const char *name; + u32 jedec_id; /* Device ID by command 0x9F */ + size_t page_size; /* Page size by command 0x02 */ + size_t sector_size; /* Sector size by command 0x20 */ + size_t block_size; /* Block size by command 0x52, + if 0 means block erase NOT supported */ + size_t total_size; + +}; + +#define CTS_FLASH_READ_DEFAULT_RETRY (3) +#define CTS_FLASH_ERASE_DEFAULT_RETRY (3) + +struct cts_device; + +extern int cts_prepare_flash_operation(struct cts_device *cts_dev); +extern int cts_post_flash_operation(struct cts_device *cts_dev); + +extern int cts_read_flash_retry(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t size, + int retry); +static inline int cts_read_flash(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t size) +{ + return cts_read_flash_retry(cts_dev, + flash_addr, dst, size, + CTS_FLASH_READ_DEFAULT_RETRY); +} + +extern int cts_read_flash_to_sram_retry(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, + size_t size, int retry); +static inline int cts_read_flash_to_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, + size_t size) +{ + return cts_read_flash_to_sram_retry(cts_dev, + flash_addr, sram_addr, size, + CTS_FLASH_READ_DEFAULT_RETRY); +} + +extern int cts_read_flash_to_sram_check_crc_retry(const struct cts_device + *cts_dev, u32 flash_addr, + u32 sram_addr, size_t size, + u32 crc, int retry); +static inline int cts_read_flash_to_sram_check_crc(const struct cts_device + *cts_dev, u32 flash_addr, + u32 sram_addr, size_t size, + u32 crc) +{ + return cts_read_flash_to_sram_check_crc_retry(cts_dev, + flash_addr, sram_addr, + size, crc, + CTS_FLASH_READ_DEFAULT_RETRY); +} + +extern int cts_erase_flash(const struct cts_device *cts_dev, u32 addr, + size_t size); + +extern int cts_program_flash(const struct cts_device *cts_dev, + u32 flash_addr, const void *src, size_t size); +extern int cts_program_flash_from_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, + size_t size); + +#endif /* CTS_SPI_FLASH_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_sysfs.c b/drivers/input/touchscreen/chipone_tddi_mmi/cts_sysfs.c new file mode 100755 index 000000000000..f4c8bd4126b5 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_sysfs.c @@ -0,0 +1,2758 @@ +#define LOG_TAG "Sysfs" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_test.h" +#include "cts_sfctrl.h" +#include "cts_spi_flash.h" +#include "cts_firmware.h" + +#ifdef CONFIG_CTS_SYSFS + +#define SPLIT_LINE_STR \ + "-----------------------------------------------------------------------------------------------\n" +#define ROW_NUM_FORMAT_STR "%2d | " +#define COL_NUM_FORMAT_STR "%4u " +#define DATA_FORMAT_STR "%5d" + +#define DIFFDATA_BUFFER_SIZE(cts_dev) \ + (cts_dev->fwdata.rows * cts_dev->fwdata.cols * 2) + +#define MAX_ARG_NUM (100) +#define MAX_ARG_LENGTH (1024) +#define HW_STUB_ADDR (0XF000) + +static char cmdline_param[MAX_ARG_LENGTH + 1]; +int argc; +char *argv[MAX_ARG_NUM]; + +static int jitter_test_frame = 10; +static s16 *manualdiff_base; + +u16 speed = 9600; + +int parse_arg(const char *buf, size_t count) +{ + char *p; + + memcpy(cmdline_param, buf, min((size_t) MAX_ARG_LENGTH, count)); + cmdline_param[count] = '\0'; + + argc = 0; + p = strim(cmdline_param); + if (p == NULL || p[0] == '\0') { + return 0; + } + + while (p && p[0] != '\0' && argc < MAX_ARG_NUM) { + argv[argc++] = strsep(&p, " ,"); + } + + return argc; +} + +/* echo addr value1 value2 value3 ... valueN > write_reg */ +static ssize_t write_firmware_register_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 addr; + int i, ret; + u8 *data = NULL; + + parse_arg(buf, count); + + cts_info("Write firmware register '%.*s'", (int)count, buf); + + if (argc < 2) { + cts_err("Too few args %d", argc); + return -EFAULT; + } + + ret = kstrtou16(argv[0], 0, &addr); + if (ret) { + cts_err("Invalid address %s", argv[0]); + return -EINVAL; + } + + data = (u8 *) kmalloc(argc - 1, GFP_KERNEL); + if (data == NULL) { + cts_err("Allocate buffer for write data failed\n"); + return -ENOMEM; + } + + for (i = 1; i < argc; i++) { + ret = kstrtou8(argv[i], 0, data + i - 1); + if (ret) { + cts_err("Invalid value %s", argv[i]); + goto free_data; + } + } + + ret = cts_fw_reg_writesb(cts_dev, addr, data, argc - 1); + if (ret) { + cts_err("Write firmware register addr: 0x%04x size: %d failed", + addr, argc - 1); + goto free_data; + } + +free_data: + kfree(data); + + return (ret < 0 ? ret : count); +} + +#ifdef CFG_CTS_FW_UPDATE_SYS +/* +static ssize_t drv_irq_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s\n", + cts_data->irq_disabled? "DISABLED" : "ENABLED"); +} + +static ssize_t drv_irq_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = cts_data->input_dev; + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + //TBD. + cts_lock_device(dev); + if (buf[0] == '1') { + cts_info("drv_irq_store: enable irq"); + cts_plat_enable_irq(); + } else if (buf[0] == '0') { + cts_info("drv_irq_store: disable irq"); + ret = cts_plat_disable_irq(cts_data->cts_dev->pdata); + if (ret < 0) { + cts_err("Disable IRQ failed %d", ret); + return ret; + } + } + cts_unlock_device(dev); + return count; +} + +static ssize_t reset_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + if ('1' != buf[0]) { + pr_err("Invalid argument for reset\n"); + return -EINVAL; + } + + cts_lock_device(dev); + //TBD + //cts_reset_proc(); + cts_unlock_device(dev); + + return count; +} +*/ + +static ssize_t cts_panel_supplier_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *data = dev_get_drvdata(dev); + + if (data->pdata && data->pdata->panel_supplier) { + return scnprintf(buf, PAGE_SIZE, "%s\n", + data->pdata->panel_supplier); + } + return 0; +} + +static ssize_t buildid_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "0000-%04x\n", + cts_data->cts_dev.fwdata.version); +} + +static ssize_t forcereflash_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 0) + return -EINVAL; + + cts_data->force_reflash = (input == 0) ? false : true; + + cts_info("forcereflash_store force_reflash=%d, count=%zu", + cts_data->force_reflash, count); + return count; +} + +static ssize_t flashprog_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + cts_is_firmware_updating(&cts_data->cts_dev) ? 1 : 0); +} + +static bool is_reflash_filename_valid(const struct chipone_ts_data *cts_data, + const char *filename) +{ + char prefix[CFG_CTS_FW_FILE_NAME_MAX_LEN]; + + if(NULL != cts_data->pdata->panel_supplier) { + snprintf(prefix, sizeof(prefix), "%s-%s-%s-", + CFG_CTS_FW_FILE_NAME_VENDOR, + cts_data->pdata->panel_supplier, + cts_data->cts_dev.hwdata->name); + } else { + // panel supplier not set, just check vendor. + snprintf(prefix, sizeof(prefix), "%s", + CFG_CTS_FW_FILE_NAME_VENDOR); + } + + cts_info("is_reflash_filename_valid: prefix=%s", prefix); + if (strncmp(filename, prefix, strlen(prefix))) { + cts_err("is_reflash_filename_valid: invalid FW file."); + return false; + } + + return true; +} + +static ssize_t doreflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + int ret; + + cts_info("doreflash FW filename: %s len: %zu", buf, count); + + if (count > CFG_CTS_FW_FILE_NAME_MAX_LEN) { + cts_err("doreflash FW filename is too long %zu > %d", + count, CFG_CTS_FW_FILE_NAME_MAX_LEN); + return -EINVAL; + } + + if (cts_is_device_suspended(&cts_data->cts_dev)) { + cts_err("In suspend state, try again later"); + return -EAGAIN; + } + + if (cts_is_firmware_updating(&cts_data->cts_dev)) { + cts_err("In FW flashing state, try again later"); + return -EAGAIN; + } + + if (!cts_data->force_reflash) { + /* Check filename if force_reflash is false */ + if (!is_reflash_filename_valid(cts_data, buf)) { + cts_err("Invalid firmware filename '%*.s'", + (int)count, buf); + return -EINVAL; + } + } + + strncpy(cts_data->cts_dev.config_fw_name, buf, count); + + /* If use echo xxx > doreflash, 0x0A will append to the string, + if use echo -n xxx > doreflash, nothing will append. */ + if (cts_data->cts_dev.config_fw_name[count - 1] == '\n') { + cts_data->cts_dev.config_fw_name[count - 1] = '\0'; + } else { + cts_data->cts_dev.config_fw_name[count] = '\0'; + } + + cts_stop_device(&cts_data->cts_dev); + + cts_lock_device(&cts_data->cts_dev); + ret = cts_update_firmware_from_file(&cts_data->cts_dev, + cts_data->cts_dev.config_fw_name, + false); + cts_unlock_device(&cts_data->cts_dev); + + if (ret) { + cts_err("Update firmware from file '%s' failed %d", + cts_data->cts_dev.config_fw_name, ret); + } + + cts_start_device(&cts_data->cts_dev); + + cts_data->force_reflash = false; + + cts_info("doreflash_store: end"); + + return ret ? ret : count; +} + +static ssize_t cts_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + bool val; + + //TBD: check if cts is power to ready for flash. set "1" if power on ready. + val = cts_is_device_suspended(&cts_data->cts_dev); + return scnprintf(buf, PAGE_SIZE, "%d\n", val == false); +} + +static ssize_t cts_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + //set chip IC type to productinfo + return scnprintf(buf, PAGE_SIZE, "%s\n", + cts_data->cts_dev.hwdata->name); +} + +//add sys entries for FW update +//static DEVICE_ATTR(drv_irq, S_IRUGO | S_IWUSR, drv_irq_show, drv_irq_store); +//static DEVICE_ATTR(reset, S_IWUSR | S_IWGRP, NULL, reset_store); +static DEVICE_ATTR(panel_supplier, 0444, cts_panel_supplier_show, NULL); +static DEVICE_ATTR(buildid, S_IRUGO, buildid_show, NULL); +static DEVICE_ATTR(forcereflash, S_IWUSR | S_IWGRP, NULL, forcereflash_store); +static DEVICE_ATTR(flashprog, S_IRUGO, flashprog_show, NULL); +static DEVICE_ATTR(doreflash, S_IWUSR | S_IWGRP, NULL, doreflash_store); +static DEVICE_ATTR(poweron, S_IRUGO, cts_poweron_show, NULL); +static DEVICE_ATTR(productinfo, S_IRUGO, cts_productinfo_show, NULL); +#endif + +static DEVICE_ATTR(write_reg, S_IWUSR, NULL, write_firmware_register_store); + +static ssize_t read_firmware_register_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ +#define PRINT_ROW_SIZE (16) + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 addr, size, i, remaining; + u8 *data = NULL; + ssize_t count = 0; + int ret; + + cts_info("Read firmware register "); + + if (argc != 2) { + return snprintf(buf, PAGE_SIZE, + "Invalid num args %d\n" + " 1. echo addr size > read_reg\n" + " 2. cat read_reg\n", argc); + } + + ret = kstrtou16(argv[0], 0, &addr); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid address: %s\n", + argv[0]); + } + ret = kstrtou16(argv[1], 0, &size); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid size: %s\n", argv[1]); + } + + data = (u8 *) kmalloc(size, GFP_KERNEL); + if (data == NULL) { + return snprintf(buf, PAGE_SIZE, + "Allocate buffer for read data failed\n"); + } + + cts_info("Read firmware register from 0x%04x size %u", addr, size); + cts_lock_device(cts_dev); + ret = cts_fw_reg_readsb(cts_dev, addr, data, (size_t) size); + cts_unlock_device(cts_dev); + if (ret) { + count = snprintf(buf, PAGE_SIZE, + "Read firmware register from 0x%04x size %u failed %d\n", + addr, size, ret); + goto err_free_data; + } + + remaining = size; + for (i = 0; i < size && count <= PAGE_SIZE; i += PRINT_ROW_SIZE) { + size_t linelen = + min((size_t) remaining, (size_t) PRINT_ROW_SIZE); + remaining -= PRINT_ROW_SIZE; + + count += + snprintf(buf + count, PAGE_SIZE - count, "%04x: ", addr); + + /* Lower version kernel return void */ + hex_dump_to_buffer(data + i, linelen, PRINT_ROW_SIZE, 1, + buf + count, PAGE_SIZE - count, true); + count += strlen(buf + count); + + if (count < PAGE_SIZE) { + buf[count++] = '\n'; + addr += PRINT_ROW_SIZE; + } else { + break; + } + } + +err_free_data: + kfree(data); + + return count; +#undef PRINT_ROW_SIZE +} + +/* echo addr size > read_reg */ +static ssize_t read_firmware_register_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + parse_arg(buf, count); + + return (argc == 0 ? 0 : count); +} + +static DEVICE_ATTR(read_reg, S_IWUSR | S_IRUSR, + read_firmware_register_show, read_firmware_register_store); + +static ssize_t read_hw_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#define PRINT_ROW_SIZE (16) + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u32 addr, size, i, remaining; + u8 *data = NULL; + ssize_t count = 0; + int ret; + + cts_info("Read hw register"); + + if (argc != 2) { + return snprintf(buf, PAGE_SIZE, + "Invalid num args %d\n" + " 1. echo addr size > read_hw_reg\n" + " 2. cat read_hw_reg\n", argc); + } + + ret = kstrtou32(argv[0], 0, &addr); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid address: %s\n", + argv[0]); + } + ret = kstrtou32(argv[1], 0, &size); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid size: %s\n", argv[1]); + } + + data = (u8 *) kmalloc(size, GFP_KERNEL); + if (data == NULL) { + return snprintf(buf, PAGE_SIZE, + "Allocate buffer for read data failed\n"); + } + + cts_info("Read hw register from 0x%04x size %u", addr, size); + cts_lock_device(cts_dev); + + for (i = 0; i < size; i++) { + ret = + cts_fw_reg_writel_retry(cts_dev, HW_STUB_ADDR, addr + i, 3, + 10); + if (ret) { + count = + snprintf(buf, PAGE_SIZE, + "Write hw register error\n"); + goto err_free_data; + } + + ret = cts_fw_reg_readsb(cts_dev, HW_STUB_ADDR + 4, data + i, 1); + if (ret) { + count = + snprintf(buf, PAGE_SIZE, + "Read hw register error\n"); + goto err_free_data; + } + } + remaining = size; + for (i = 0; i < size && count <= PAGE_SIZE; i += PRINT_ROW_SIZE) { + size_t linelen = + min((size_t) remaining, (size_t) PRINT_ROW_SIZE); + remaining -= PRINT_ROW_SIZE; + + count += + snprintf(buf + count, PAGE_SIZE - count, "%04x: ", addr); + + /* Lower version kernel return void */ + hex_dump_to_buffer(data + i, linelen, PRINT_ROW_SIZE, 1, + buf + count, PAGE_SIZE - count, true); + count += strlen(buf + count); + + if (count < PAGE_SIZE) { + buf[count++] = '\n'; + addr += PRINT_ROW_SIZE; + } else { + break; + } + } + +err_free_data: + cts_unlock_device(cts_dev); + kfree(data); + + return count; +#undef PRINT_ROW_SIZE +} + +/* echo addr size > read_reg */ +static ssize_t read_hw_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + parse_arg(buf, count); + + return (argc == 0 ? 0 : count); +} + +static DEVICE_ATTR(read_hw_reg, S_IRUSR | S_IWUSR, read_hw_reg_show, + read_hw_reg_store); + +static ssize_t write_hw_reg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u32 addr; + int i, ret; + u8 *data = NULL; + + parse_arg(buf, count); + + cts_info("Write hw register"); + + if (argc < 2) { + cts_err("Too few args %d", argc); + return -EFAULT; + } + + ret = kstrtou32(argv[0], 0, &addr); + if (ret) { + cts_err("Invalid address %s", argv[0]); + return -EINVAL; + } + + data = (u8 *) kmalloc(argc - 1, GFP_KERNEL); + if (data == NULL) { + cts_err("Allocate buffer for write data failed\n"); + return -ENOMEM; + } + + for (i = 1; i < argc; i++) { + ret = kstrtou8(argv[i], 0, data + i - 1); + if (ret) { + cts_err("Invalid value %s", argv[i]); + goto free_data; + } + } + + cts_lock_device(cts_dev); + for (i = 0; i < argc - 1; i++) { + ret = + cts_fw_reg_writel_retry(cts_dev, HW_STUB_ADDR, addr + i, 3, + 10); + if (ret) { + cts_err("Write hw register error"); + break; + } + ret = + cts_fw_reg_writeb_retry(cts_dev, HW_STUB_ADDR + 4, data[i], + 3, 10); + if (ret) { + cts_err("Write hw register error"); + break; + } + } + cts_unlock_device(cts_dev); +free_data: + kfree(data); + + return (ret < 0 ? ret : count); +} + +static DEVICE_ATTR(write_hw_reg, S_IWUSR, NULL, write_hw_reg_store); + +static ssize_t curr_firmware_version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Current firmware version: %04x\n", + cts_data->cts_dev.fwdata.version); +} + +static DEVICE_ATTR(curr_version, S_IRUGO, curr_firmware_version_show, NULL); + +static ssize_t curr_ddi_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Current ddi version: %02x\n", + cts_data->cts_dev.fwdata.ddi_version); +} + +static DEVICE_ATTR(curr_ddi_version, S_IRUGO, curr_ddi_version_show, NULL); + +static ssize_t rows_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Num rows: %u\n", + cts_data->cts_dev.fwdata.rows); +} + +static DEVICE_ATTR(rows, S_IRUGO, rows_show, NULL); + +static ssize_t cols_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Num cols: %u\n", + cts_data->cts_dev.fwdata.cols); +} + +static DEVICE_ATTR(cols, S_IRUGO, cols_show, NULL); + +static ssize_t res_x_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "X Resolution: %u\n", + cts_data->cts_dev.fwdata.res_x); +} + +static DEVICE_ATTR(res_x, S_IRUGO, res_x_show, NULL); + +static ssize_t res_y_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Y Resolution: %u\n", + cts_data->cts_dev.fwdata.res_y); +} + +static DEVICE_ATTR(res_y, S_IRUGO, res_y_show, NULL); + +static ssize_t esd_protection_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + u8 esd_protection; + + cts_lock_device(cts_dev); + ret = + cts_fw_reg_readb(&cts_data->cts_dev, 0x8000 + 342, &esd_protection); + cts_unlock_device(cts_dev); + if (ret) { + return snprintf(buf, PAGE_SIZE, + "Read firmware ESD protection register failed %d\n", + ret); + } + + return snprintf(buf, PAGE_SIZE, "ESD protection: %u\n", esd_protection); +} + +static DEVICE_ATTR(esd_protection, S_IRUGO, esd_protection_show, NULL); + +static ssize_t monitor_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + u8 value; + + cts_lock_device(cts_dev); + ret = cts_fw_reg_readb(&cts_data->cts_dev, 0x8000 + 344, &value); + cts_unlock_device(cts_dev); + if (ret) { + return snprintf(buf, PAGE_SIZE, + "Read firmware monitor enable register failed %d\n", + ret); + } + + return snprintf(buf, PAGE_SIZE, "Monitor mode: %s\n", + value & BIT(0) ? "Enable" : "Disable"); +} + +static ssize_t monitor_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + u8 value, enable = 0; + + if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') { + enable = 1; + } + + cts_info("Write firmware monitor mode to '%c', %s", + buf[0], enable ? "Enable" : "Disable"); + + cts_lock_device(cts_dev); + ret = cts_fw_reg_readb(&cts_data->cts_dev, 0x8000 + 344, &value); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("Write firmware monitor enable register failed %d", + ret); + return -EIO; + } + + if ((value & BIT(0)) && enable) { + cts_info("Monitor mode already enabled"); + } else if ((value & BIT(0)) == 0 && enable == 0) { + cts_info("Monitor mode already disabled"); + } else { + if (enable) { + value |= BIT(0); + } else { + value &= ~BIT(0); + } + + cts_lock_device(cts_dev); + ret = + cts_fw_reg_writeb(&cts_data->cts_dev, 0x8000 + 344, value); + cts_unlock_device(cts_dev); + if (ret) { + cts_err + ("Write firmware monitor enable register failed %d", + ret); + return -EIO; + } + } + + return count; +} + +static DEVICE_ATTR(monitor_mode, S_IRUGO, monitor_mode_show, + monitor_mode_store); + +static ssize_t auto_compensate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + u8 value; + + cts_lock_device(cts_dev); + ret = cts_fw_reg_readb(&cts_data->cts_dev, 0x8000 + 276, &value); + cts_unlock_device(cts_dev); + if (ret) { + return snprintf(buf, PAGE_SIZE, + "Read auto compensate enable register failed %d\n", + ret); + } + + return snprintf(buf, PAGE_SIZE, "Auto compensate: %s\n", + value ? "Enable" : "Disable"); +} + +static DEVICE_ATTR(auto_compensate, S_IRUGO, auto_compensate_show, NULL); + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE +static ssize_t driver_builtin_firmware_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int i, count = 0; + + count += snprintf(buf + count, PAGE_SIZE - count, + "Total %d builtin firmware:\n", + cts_get_num_driver_builtin_firmware()); + + for (i = 0; i < cts_get_num_driver_builtin_firmware(); i++) { + const struct cts_firmware *firmware = + cts_request_driver_builtin_firmware_by_index(i); + if (firmware) { + count += snprintf(buf + count, PAGE_SIZE - count, + "%-2d: hwid: %04x fwid: %04x ver: %04x size: %6zu desc: %s\n", + i, firmware->hwid, firmware->fwid, + FIRMWARE_VERSION(firmware), + firmware->size, firmware->name); + } else { + count += snprintf(buf + count, PAGE_SIZE - count, + "%-2d: INVALID\n", i); + } + } + + return count; +} + +/* echo index/name [flash/sram] > driver_builtin_firmware */ +static ssize_t driver_builtin_firmware_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + const struct cts_firmware *firmware; + bool to_flash = true; + int ret, index = -1; + + parse_arg(buf, count); + + if (argc != 1 && argc != 2) { + cts_err("Invalid num args %d\n" + " echo index/name [flash/sram] > driver_builtin_firmware\n", + argc); + return -EFAULT; + } + + if (isdigit(*argv[0])) { + index = simple_strtoul(argv[0], NULL, 0); + } + + if (argc > 1) { + if (strncasecmp(argv[1], "flash", 5) == 0) { + to_flash = true; + } else if (strncasecmp(argv[1], "sram", 4) == 0) { + to_flash = false; + } else { + cts_err + ("Invalid location '%s', must be 'flash' or 'sram'", + argv[1]); + return -EINVAL; + } + } + + cts_info("Update driver builtin firmware '%s' to %s", + argv[1], to_flash ? "flash" : "sram"); + + if (index >= 0 && index < cts_get_num_driver_builtin_firmware()) { + firmware = cts_request_driver_builtin_firmware_by_index(index); + } else { + firmware = cts_request_driver_builtin_firmware_by_name(argv[0]); + } + + if (firmware) { + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } + + cts_lock_device(cts_dev); + ret = cts_update_firmware(cts_dev, firmware, to_flash); + cts_unlock_device(cts_dev); + + if (ret) { + cts_err("Update firmware failed %d", ret); + goto err_start_device; + } + + ret = cts_start_device(cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + return ret; + } + } else { + cts_err("Firmware '%s' NOT found", argv[0]); + return -ENOENT; + } + + return count; + +err_start_device: + cts_start_device(cts_dev); + + return ret; +} + +static DEVICE_ATTR(driver_builtin_firmware, S_IWUSR | S_IRUGO, + driver_builtin_firmware_show, driver_builtin_firmware_store); +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ + +#ifdef CFG_CTS_FIRMWARE_IN_FS +/* echo filepath [flash/sram] > update_firmware_from_file */ +static ssize_t update_firmware_from_file_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + const struct cts_firmware *firmware; + bool to_flash = true; + int ret; + // int index; + + parse_arg(buf, count); + + if (argc > 2) { + cts_err("Invalid num args %d\n" + " echo filepath [flash/sram] > update_from_file\n", + argc); + return -EFAULT; + } else if (argc > 1) { + if (strncasecmp(argv[1], "flash", 5) == 0) { + to_flash = true; + } else if (strncasecmp(argv[1], "sram", 4) == 0) { + to_flash = false; + } else { + cts_err + ("Invalid location '%s', must be 'flash' or 'sram'", + argv[1]); + return -EINVAL; + } + } + + cts_info("Update firmware from file '%s'", argv[0]); + + firmware = cts_request_firmware_from_fs(cts_dev, argv[0]); + if (firmware == NULL) { + cts_err("Request firmware from file '%s' failed", argv[0]); + return -ENOENT; + } + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto err_release_firmware; + } + + cts_lock_device(cts_dev); + ret = cts_update_firmware(cts_dev, firmware, to_flash); + cts_unlock_device(cts_dev); + + if (ret) { + cts_err("Update firmware failed %d", ret); + goto err_release_firmware; + } + + ret = cts_start_device(cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + goto err_release_firmware; + } + + return count; + +err_release_firmware: + cts_release_firmware(firmware); + + return ret; +} + +static DEVICE_ATTR(update_from_file, S_IWUSR, NULL, + update_firmware_from_file_store); +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + +static ssize_t updating_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Updating: %s\n", + cts_data->cts_dev.rtdata.updating ? "Y" : "N"); +} + +static DEVICE_ATTR(updating, S_IRUGO, updating_show, NULL); + +static struct attribute *cts_dev_firmware_atts[] = { + &dev_attr_curr_version.attr, + &dev_attr_curr_ddi_version.attr, + &dev_attr_rows.attr, + &dev_attr_cols.attr, + &dev_attr_res_x.attr, + &dev_attr_res_y.attr, + &dev_attr_esd_protection.attr, + &dev_attr_monitor_mode.attr, + &dev_attr_auto_compensate.attr, +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE + &dev_attr_driver_builtin_firmware.attr, +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ +#ifdef CFG_CTS_FIRMWARE_IN_FS + &dev_attr_update_from_file.attr, +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + &dev_attr_updating.attr, + NULL +}; + +static const struct attribute_group cts_dev_firmware_attr_group = { + .name = "cts_firmware", + .attrs = cts_dev_firmware_atts, +}; + +static ssize_t flash_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + const struct cts_flash *flash; + + if (cts_dev->flash == NULL) { + bool program_mode; + bool enabled; + int ret; + + program_mode = cts_is_device_program_mode(cts_dev); + enabled = cts_is_device_enabled(cts_dev); + + ret = cts_prepare_flash_operation(cts_dev); + if (ret) { + return snprintf(buf, PAGE_SIZE, + "Prepare flash operation failed %d", + ret); + } + + cts_post_flash_operation(cts_dev); + + if (!program_mode) { + ret = cts_enter_normal_mode(cts_dev); + if (ret) { + return snprintf(buf, PAGE_SIZE, + "Enter normal mode failed %d", + ret); + } + } + + if (enabled) { + ret = cts_start_device(cts_dev); + if (ret) { + return snprintf(buf, PAGE_SIZE, + "Start device failed %d", ret); + } + } + + if (cts_dev->flash == NULL) { + return snprintf(buf, PAGE_SIZE, "Flash not found\n"); + } + } + + flash = cts_dev->flash; + return snprintf(buf, PAGE_SIZE, + "%s:\n" + " JEDEC ID : %06X\n" + " Page size : 0x%zx\n" + " Sector size: 0x%zx\n" + " Block size : 0x%zx\n" + " Total size : 0x%zx\n", + flash->name, flash->jedec_id, flash->page_size, + flash->sector_size, flash->block_size, + flash->total_size); +} + +static DEVICE_ATTR(info, S_IRUGO, flash_info_show, NULL); + +static ssize_t read_flash_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u32 flash_addr, size, i, remaining; + u8 *data = NULL; + ssize_t count = 0; + int ret; + bool program_mode; + bool enabled; + loff_t pos = 0; + + if (argc != 2 && argc != 3) { + return snprintf(buf, PAGE_SIZE, "Invalid num args %d\n", argc); + } + + ret = kstrtou32(argv[0], 0, &flash_addr); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid flash addr: %s\n", + argv[0]); + } + ret = kstrtou32(argv[1], 0, &size); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid size: %s\n", argv[1]); + } + + data = (u8 *) kmalloc(size, GFP_KERNEL); + if (data == NULL) { + return snprintf(buf, PAGE_SIZE, + "Allocate buffer for read data failed\n"); + } + + cts_info("Read flash from 0x%06x size %u%s%s", + flash_addr, size, argc == 3 ? " to file " : "", + argc == 3 ? argv[2] : ""); + + cts_lock_device(cts_dev); + program_mode = cts_is_device_program_mode(cts_dev); + enabled = cts_is_device_enabled(cts_dev); + + ret = cts_prepare_flash_operation(cts_dev); + if (ret) { + count += + snprintf(buf, PAGE_SIZE, + "Prepare flash operation failed %d", ret); + goto err_free_data; + } + + ret = cts_read_flash(cts_dev, flash_addr, data, size); + if (ret) { + count = + snprintf(buf, PAGE_SIZE, "Read flash data failed %d\n", + ret); + goto err_post_flash_operation; + } + + if (argc == 3) { + struct file *file; + + cts_info("Write flash data to file '%s'", argv[2]); + + file = filp_open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0666); + if (IS_ERR(file)) { + count += + snprintf(buf, PAGE_SIZE, + "Open file '%s' failed %ld", argv[2], + PTR_ERR(file)); + goto err_post_flash_operation; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(file, data, size, &pos); +#else + ret = kernel_write(file, data, size, pos); +#endif + if (ret != size) { + count += + snprintf(buf, PAGE_SIZE, + "Write flash data to file '%s' failed %d", + argv[2], ret); + } + + ret = filp_close(file, NULL); + if (ret) { + count += + snprintf(buf, PAGE_SIZE, + "Close file '%s' failed %d", argv[2], ret); + } + } else { +#define PRINT_ROW_SIZE (16) + remaining = size; + for (i = 0; i < size && count <= PAGE_SIZE; i += PRINT_ROW_SIZE) { + size_t linelen = + min((size_t) remaining, (size_t) PRINT_ROW_SIZE); + remaining -= PRINT_ROW_SIZE; + + count += snprintf(buf + count, PAGE_SIZE - count - 1, + "%04x-%04x: ", flash_addr >> 16, + flash_addr & 0xFFFF); + /* Lower version kernel return void */ + hex_dump_to_buffer(data + i, linelen, PRINT_ROW_SIZE, 1, + buf + count, PAGE_SIZE - count - 1, + true); + count += strlen(buf + count); + buf[count++] = '\n'; + flash_addr += linelen; +#undef PRINT_ROW_SIZE + } + } + +err_post_flash_operation: + cts_post_flash_operation(cts_dev); + + if (!program_mode) { + int r = cts_enter_normal_mode(cts_dev); + if (r) { + count += + snprintf(buf, PAGE_SIZE, + "Enter normal mode failed %d", r); + } + } + + if (enabled) { + int r = cts_start_device(cts_dev); + if (r) { + cts_unlock_device(cts_dev); + return snprintf(buf, PAGE_SIZE, + "Start device failed %d", r); + } + } +err_free_data: + cts_unlock_device(cts_dev); + kfree(data); + + return (ret < 0 ? ret : count); +} + +/* echo start_addr size [filepath] > read */ +static ssize_t read_flash_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(read, S_IWUSR | S_IRUGO, read_flash_show, read_flash_store); + +/* echo addr size > erase */ +static ssize_t erase_flash_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u32 flash_addr, size; + int ret; + bool program_mode; + bool enabled; + + parse_arg(buf, count); + + if (argc != 2) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + ret = kstrtou32(argv[0], 0, &flash_addr); + if (ret) { + cts_err("Invalid flash addr: %s", argv[0]); + return -EINVAL; + } + ret = kstrtou32(argv[1], 0, &size); + if (ret) { + cts_err("Invalid size: %s", argv[1]); + return -EINVAL; + } + + cts_info("Erase flash from 0x%06x size %u", flash_addr, size); + + cts_lock_device(cts_dev); + program_mode = cts_is_device_program_mode(cts_dev); + enabled = cts_is_device_enabled(cts_dev); + + ret = cts_prepare_flash_operation(cts_dev); + if (ret) { + cts_err("Prepare flash operation failed %d", ret); + cts_unlock_device(cts_dev); + return ret; + } + + ret = cts_erase_flash(cts_dev, flash_addr, size); + if (ret) { + cts_err("Erase flash from 0x%06x size %u failed %d", + flash_addr, size, ret); + goto err_post_flash_operation; + } + +err_post_flash_operation: + cts_post_flash_operation(cts_dev); + + if (!program_mode) { + int r = cts_enter_normal_mode(cts_dev); + if (r) { + cts_err("Enter normal mode failed %d", r); + } + } + + if (enabled) { + int r = cts_start_device(cts_dev); + if (r) { + cts_err("Start device failed %d", r); + } + } + cts_unlock_device(cts_dev); + + return (ret < 0 ? ret : count); +} + +static DEVICE_ATTR(erase, S_IWUSR, NULL, erase_flash_store); + +static struct attribute *cts_dev_flash_attrs[] = { + &dev_attr_info.attr, + &dev_attr_read.attr, + &dev_attr_erase.attr, + + NULL +}; + +static const struct attribute_group cts_dev_flash_attr_group = { + .name = "flash", + .attrs = cts_dev_flash_attrs, +}; + +static ssize_t open_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 threshold; + int ret; + + if (argc != 1) { + return snprintf(buf, PAGE_SIZE, "Invalid num args %d\n", argc); + } + + ret = kstrtou16(argv[0], 0, &threshold); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid threshold: %s\n", + argv[0]); + } + + cts_info("Open test, threshold = %u", threshold); + + ret = cts_open_test(cts_dev, threshold); + if (ret) { + return snprintf(buf, PAGE_SIZE, + "Open test FAILED %d, threshold = %u\n", ret, + threshold); + } else { + return snprintf(buf, PAGE_SIZE, + "Open test PASSED, threshold = %u\n", + threshold); + } +} + +/* echo threshod > open_test */ +static ssize_t open_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(open_test, S_IWUSR | S_IRUGO, open_test_show, + open_test_store); + +static ssize_t short_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 threshold; + int ret; + + if (argc != 1) { + return snprintf(buf, PAGE_SIZE, "Invalid num args %d\n", argc); + } + + ret = kstrtou16(argv[0], 0, &threshold); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid threshold: %s\n", + argv[0]); + } + + cts_info("Short test, threshold = %u", threshold); + + ret = cts_short_test(cts_dev, threshold); + if (ret) { + return snprintf(buf, PAGE_SIZE, + "Short test FAILED %d, threshold = %u\n", ret, + threshold); + } else { + return snprintf(buf, PAGE_SIZE, + "Short test PASSED, threshold = %u\n", + threshold); + } +} + +/* echo threshod > short_test */ +static ssize_t short_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(short_test, S_IWUSR | S_IRUGO, + short_test_show, short_test_store); + +static ssize_t testing_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Testting: %s\n", + cts_data->cts_dev.rtdata.testing ? "Y" : "N"); +} + +static DEVICE_ATTR(testing, S_IRUGO, testing_show, NULL); + +#ifdef CFG_CTS_HAS_RESET_PIN +static ssize_t reset_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + int count; + + ret = cts_reset_test(cts_dev); + if (ret == 0) { + count = snprintf(buf, PAGE_SIZE, "reset pin test sucessful\n"); + } else { + count = snprintf(buf, PAGE_SIZE, "reset pin test failed\n"); + } + + return count; +} + +static DEVICE_ATTR(reset_test, S_IRUGO, reset_test_show, NULL); +#endif + +static ssize_t compensate_cap_test_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 min_thres, max_thres; + int ret; + + if (argc != 2) { + return snprintf(buf, PAGE_SIZE, "Invalid num args\n" + "USAGE:\n" + " 1. echo min max > compensate_cap_test\n" + " 2. cat compensate_cap_test\n"); + } + + ret = kstrtou8(argv[0], 0, &min_thres); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid min_thres: %s\n", + argv[0]); + } + + ret = kstrtou8(argv[1], 0, &max_thres); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid max_thres: %s\n", + argv[1]); + } + + cts_info("Compensate cap test, min_thres = %u, max_thres = %u", + min_thres, max_thres); + + ret = cts_compensate_cap_test(cts_dev, min_thres, max_thres); + if (ret) { + return snprintf(buf, PAGE_SIZE, + "Compensate cap test FAILED, min_thres = %u, max_thres=%u\n", + min_thres, max_thres); + } else { + return snprintf(buf, PAGE_SIZE, + "Compensate cap test PASSED, min_thres = %u, max_thres=%u\n", + min_thres, max_thres); + } +} + +/* echo threshod > short_test */ +static ssize_t compensate_cap_test_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(compensate_cap_test, S_IWUSR | S_IRUGO, + compensate_cap_test_show, compensate_cap_test_store); + +static ssize_t rawdata_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 min_thres, max_thres; + int ret; + + if (argc != 2) { + return snprintf(buf, PAGE_SIZE, "Invalid num args\n" + "USAGE:\n" + " 1. echo min max > rawdata_test\n" + " 2. cat rawdata_test\n"); + } + + ret = kstrtou16(argv[0], 0, &min_thres); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid min_thres: %s\n", + argv[0]); + } + + ret = kstrtou16(argv[1], 0, &max_thres); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid max_thres: %s\n", + argv[1]); + } + + cts_info("Rawdata test, min_thres = %u, max_thres = %u", min_thres, + max_thres); + + ret = cts_rawdata_test(cts_dev, min_thres, max_thres); + if (ret) { + return snprintf(buf, PAGE_SIZE, + "Rawdata test FAILED, min_thres = %u, max_thres=%u\n", + min_thres, max_thres); + } else { + return snprintf(buf, PAGE_SIZE, + "Rawdata test PASSED, min_thres = %u, max_thres=%u\n", + min_thres, max_thres); + } +} + +/* echo threshod > short_test */ +static ssize_t rawdata_test_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(rawdata_test, S_IWUSR | S_IRUGO, + rawdata_test_show, rawdata_test_store); + +static struct attribute *cts_dev_test_atts[] = { + &dev_attr_open_test.attr, + &dev_attr_short_test.attr, + &dev_attr_testing.attr, +#ifdef CFG_CTS_HAS_RESET_PIN + &dev_attr_reset_test.attr, +#endif + &dev_attr_compensate_cap_test.attr, + &dev_attr_rawdata_test.attr, + NULL +}; + +static const struct attribute_group cts_dev_test_attr_group = { + .name = "test", + .attrs = cts_dev_test_atts, +}; + +static ssize_t ic_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "IC Type : %s\n", + cts_data->cts_dev.hwdata->name); +} + +static DEVICE_ATTR(ic_type, S_IRUGO, ic_type_show, NULL); + +static ssize_t program_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Program mode: %s\n", + cts_data->cts_dev.rtdata.program_mode ? "Y" : "N"); +} + +static ssize_t program_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + int ret; + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + if (*argv[0] == '1' || tolower(*argv[0]) == 'y') { + ret = cts_enter_program_mode(&cts_data->cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + return ret; + } + } else if (*argv[0] == '0' || tolower(*argv[0]) == 'n') { + ret = cts_enter_normal_mode(&cts_data->cts_dev); + if (ret) { + cts_err("Exit program mode failed %d", ret); + return ret; + } + } else { + cts_err("Invalid args"); + } + + return count; +} + +static DEVICE_ATTR(program_mode, S_IWUSR | S_IRUGO, + program_mode_show, program_mode_store); + +static ssize_t rawdata_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#define RAWDATA_BUFFER_SIZE(cts_dev) \ + (cts_dev->fwdata.rows * cts_dev->fwdata.cols * 2) + + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 *rawdata = NULL; + int ret, r, c, count = 0; + u32 max, min, sum, average; + int max_r, max_c, min_r, min_c; + bool data_valid = true; + + cts_info("Show rawdata"); + + rawdata = (u16 *) kmalloc(RAWDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata == NULL) { + return snprintf(buf, PAGE_SIZE, + "Allocate memory for rawdata failed\n"); + } + + cts_lock_device(cts_dev); + ret = cts_enable_get_rawdata(cts_dev); + if (ret) { + count += + snprintf(buf, PAGE_SIZE, "Enable read raw data failed %d\n", + ret); + goto err_free_rawdata; + } + + ret = cts_send_command(cts_dev, CTS_CMD_QUIT_GESTURE_MONITOR); + if (ret) { + count += + snprintf(buf, PAGE_SIZE, + "Send cmd QUIT_GESTURE_MONITOR failed %d\n", ret); + goto err_free_rawdata; + } + msleep(50); + + ret = cts_get_rawdata(cts_dev, rawdata); + if (ret) { + count += + snprintf(buf, PAGE_SIZE, "Get raw data failed %d\n", ret); + data_valid = false; + // Fall through to disable get rawdata + } + ret = cts_disable_get_rawdata(cts_dev); + if (ret) { + count += + snprintf(buf, PAGE_SIZE, + "Disable read raw data failed %d\n", ret); + // Fall through to show rawdata + } + + if (data_valid) { + max = min = rawdata[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + u16 val = rawdata[r * cts_dev->fwdata.cols + c]; + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count += snprintf(buf + count, PAGE_SIZE - count, + SPLIT_LINE_STR + "Raw data MIN: [%d][%d]=%u, MAX: [%d][%d]=%u, AVG=%u\n" + SPLIT_LINE_STR + " | ", min_r, min_c, min, max_r, max_c, + max, average); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += + snprintf(buf + count, PAGE_SIZE - count, + COL_NUM_FORMAT_STR, c); + } + count += + snprintf(buf + count, PAGE_SIZE - count, + "\n" SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows && count < PAGE_SIZE; r++) { + count += + snprintf(buf + count, PAGE_SIZE - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; + c < cts_dev->fwdata.cols && count < PAGE_SIZE; + c++) { + count += + snprintf(buf + count, PAGE_SIZE - count - 1, + DATA_FORMAT_STR, + rawdata[r * cts_dev->fwdata.cols + + c]); + } + buf[count++] = '\n'; + } + } + +err_free_rawdata: + cts_unlock_device(cts_dev); + kfree(rawdata); + + return data_valid ? count : ret; + +#undef RAWDATA_BUFFER_SIZE +} + +static DEVICE_ATTR(rawdata, S_IRUGO, rawdata_show, NULL); + +static ssize_t diffdata_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + s16 *diffdata = NULL; + int ret, r, c, count = 0; + int max, min, sum, average; + int max_r, max_c, min_r, min_c; + bool data_valid = true; + + cts_info("Show diffdata"); + + diffdata = (s16 *) kmalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (diffdata == NULL) { + cts_err("Allocate memory for diffdata failed"); + return -ENOMEM; + } + + cts_lock_device(cts_dev); + ret = cts_enable_get_rawdata(cts_dev); + if (ret) { + cts_err("Enable read diff data failed %d", ret); + goto err_free_diffdata; + } + + ret = cts_send_command(cts_dev, CTS_CMD_QUIT_GESTURE_MONITOR); + if (ret) { + cts_err("Send cmd QUIT_GESTURE_MONITOR failed %d", ret); + goto err_free_diffdata; + } + msleep(50); + + ret = cts_get_diffdata(cts_dev, diffdata); + if (ret) { + cts_err("Get diff data failed %d", ret); + data_valid = false; + // Fall through to disable get diffdata + } + ret = cts_disable_get_rawdata(cts_dev); + if (ret) { + cts_err("Disable read diff data failed %d", ret); + // Fall through to show diffdata + } + + if (data_valid) { + max = min = diffdata[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + s16 val = + diffdata[r * cts_dev->fwdata.cols + c]; + + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count += snprintf(buf + count, PAGE_SIZE - count, + SPLIT_LINE_STR + "Diff data MIN: [%d][%d]=%d, MAX: [%d][%d]=%d, AVG=%d\n" + SPLIT_LINE_STR + " | ", min_r, min_c, min, max_r, max_c, + max, average); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += + snprintf(buf + count, PAGE_SIZE - count, + COL_NUM_FORMAT_STR, c); + } + count += + snprintf(buf + count, PAGE_SIZE - count, + "\n" SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count += + snprintf(buf + count, PAGE_SIZE - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += + snprintf(buf + count, PAGE_SIZE - count, + DATA_FORMAT_STR, + diffdata[r * cts_dev->fwdata.cols + + c]); + } + buf[count++] = '\n'; + } + } + +err_free_diffdata: + cts_unlock_device(cts_dev); + kfree(diffdata); + + return data_valid ? count : ret; +} + +static DEVICE_ATTR(diffdata, S_IRUGO, diffdata_show, NULL); + +static ssize_t manualdiffdata_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + s16 *rawdata = NULL; + int ret, r, c, count = 0; + int max, min, sum, average; + int max_r, max_c, min_r, min_c; + bool data_valid = true; + int frame; + struct file *file = NULL; + int i; + loff_t pos = 0; + + cts_info("Show manualdiff"); + + if (argc != 1 && argc != 2) { + return snprintf(buf, PAGE_SIZE, "Invalid num args\n" + "USAGE:\n" + " 1. echo updatebase > manualdiff\n" + " 2. cat manualdiff\n" + " or\n" + " 1. echo updatebase > manualdiff\n" + " 2. echo frame file > manualdiff\n" + " 3. cat manualdiff\n"); + } + + if (argc == 2) { + ret = kstrtou32(argv[0], 0, &frame); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid frame num\n"); + } + file = filp_open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0666); + if (IS_ERR(file)) { + return snprintf(buf, PAGE_SIZE, "Can't open file:%s", + argv[1]); + } + } else { + frame = 1; + } + + rawdata = (s16 *) kmalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate memory for rawdata failed"); + filp_close(file, NULL); + return -ENOMEM; + } + + cts_lock_device(cts_dev); + ret = cts_enable_get_rawdata(cts_dev); + if (ret) { + cts_err("Enable read raw data failed %d", ret); + goto err_free_diffdata; + } + + ret = cts_send_command(cts_dev, CTS_CMD_QUIT_GESTURE_MONITOR); + if (ret) { + cts_err("Send cmd QUIT_GESTURE_MONITOR failed %d", ret); + goto err_free_diffdata; + } + msleep(50); + + cts_info("frame %d, file:%s", frame, argv[1]); + for (i = 0; i < frame; i++) { + ret = cts_get_rawdata(cts_dev, rawdata); + if (ret) { + cts_err("Get raw data failed %d", ret); + data_valid = false; + } else { + data_valid = true; + } + msleep(50); + + if (data_valid) { + max = -32768; + min = 32767; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + s16 val; + + rawdata[r * cts_dev->fwdata.cols + c] -= + manualdiff_base[r * + cts_dev-> + fwdata.cols + c]; + val = + rawdata[r * cts_dev->fwdata.cols + + c]; + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = + sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + count += + snprintf(buf + count, PAGE_SIZE - count, + SPLIT_LINE_STR + "Manualdiff data MIN: [%d][%d]=%d, MAX: [%d][%d]=%d, AVG=%d\n" + SPLIT_LINE_STR " | ", min_r, min_c, min, + max_r, max_c, max, average); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += + snprintf(buf + count, PAGE_SIZE - count, + COL_NUM_FORMAT_STR, c); + } + count += + snprintf(buf + count, PAGE_SIZE - count, + "\n" SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count += + snprintf(buf + count, PAGE_SIZE - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += + snprintf(buf + count, + PAGE_SIZE - count, + DATA_FORMAT_STR, + rawdata[r * + cts_dev-> + fwdata.cols + c]); + } + buf[count++] = '\n'; + } + //cts_info("manualdiffdata_show:%d, %d", i, frame); + if (argc == 2) { + pos = file->f_pos; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(file, buf, count, &pos); +#else + ret = kernel_write(file, buf, count, pos); +#endif + if (ret != count) { + cts_err + ("Write data to file '%s' failed %d", + argv[1], ret); + } + file->f_pos += count; + count = 0; + } + } + } + if (argc == 2) { + filp_close(file, NULL); + } + + ret = cts_disable_get_rawdata(cts_dev); + if (ret) { + cts_err("Disable read raw data failed %d", ret); + // Fall through to show diffdata + } + +err_free_diffdata: + cts_unlock_device(cts_dev); + kfree(rawdata); + + return data_valid ? count : ret; +} + +static ssize_t manualdiffdata_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + + parse_arg(buf, count); + + cts_lock_device(cts_dev); + if (strncasecmp("updatebase", argv[0], 10) == 0) { + ret = cts_enable_get_rawdata(cts_dev); + if (ret) { + cts_err("Enable read raw data failed %d", ret); + goto err_manual_diff_store; + } + + ret = cts_send_command(cts_dev, CTS_CMD_QUIT_GESTURE_MONITOR); + if (ret) { + cts_err("Send cmd QUIT_GESTURE_MONITOR failed %d", ret); + goto err_manual_diff_store; + } + msleep(50); + + if (manualdiff_base != NULL) { + ret = cts_get_rawdata(cts_dev, manualdiff_base); + if (ret) { + cts_err("Get raw data failed %d", ret); + } + } + //cts_info("update base successful"); + ret = cts_disable_get_rawdata(cts_dev); + if (ret) { + cts_err("Disable read raw data failed %d", ret); + } + } +err_manual_diff_store: + cts_unlock_device(cts_dev); + return count; +} + +static DEVICE_ATTR(manualdiff, S_IRUSR | S_IWUSR, manualdiffdata_show, + manualdiffdata_store); + +static ssize_t jitter_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + u16 cnt; + int ret; + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + ret = kstrtou16(argv[0], 0, &cnt); + if (ret == 0) { + if (cnt > 2 && cnt < 10000) { + jitter_test_frame = cnt; + } + } + + cts_info("jitter test frame: %d", jitter_test_frame); + return count; +} + +static ssize_t jitter_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + s16 *rawdata = NULL; + s16 *rawdata_min = NULL; + s16 *rawdata_max = NULL; + int ret, r, c, count = 0; + int max, min, sum, average; + int max_r, max_c, min_r, min_c; + bool data_valid = false; + int i; + + cts_info("Show jitter"); + cts_lock_device(cts_dev); + rawdata = (s16 *) kmalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate memory for rawdata failed"); + ret = -ENOMEM; + goto err_jitter_show_exit; + } + rawdata_min = + (s16 *) kmalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata_min == NULL) { + cts_err("Allocate memory for rawdata failed"); + ret = -ENOMEM; + goto err_free_rawdata; + } + rawdata_max = + (s16 *) kmalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata_max == NULL) { + cts_err("Allocate memory for rawdata failed"); + ret = -ENOMEM; + goto err_free_rawdata_min; + } + + for (i = 0; i < DIFFDATA_BUFFER_SIZE(cts_dev) / 2; i++) { + rawdata_min[i] = 32767; + rawdata_max[i] = -32768; + } + + ret = cts_enable_get_rawdata(cts_dev); + if (ret) { + cts_err("Enable read raw data failed %d", ret); + goto err_free_rawdata_max; + } + + ret = cts_send_command(cts_dev, CTS_CMD_QUIT_GESTURE_MONITOR); + if (ret) { + cts_err("Send cmd QUIT_GESTURE_MONITOR failed %d", ret); + goto err_free_rawdata_max; + } + msleep(50); + data_valid = true; + for (i = 0; i < jitter_test_frame; i++) { + ret = cts_get_rawdata(cts_dev, rawdata); + if (ret) { + cts_err("Get raw data failed %d", ret); + continue; + } + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + int index; + index = r * cts_dev->fwdata.cols + c; + if (rawdata_min[index] > rawdata[index]) + rawdata_min[index] = rawdata[index]; + else if (rawdata_max[index] < rawdata[index]) + rawdata_max[index] = rawdata[index]; + } + } + msleep(1); + } + ret = cts_disable_get_rawdata(cts_dev); + if (ret) { + cts_err("Disable read raw data failed %d", ret); + } + + if (data_valid) { + max = -32768; + min = 32767; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + s16 val; + int index = r * cts_dev->fwdata.cols + c; + + val = rawdata_max[index] - rawdata_min[index]; + rawdata[index] = val; + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count += snprintf(buf + count, PAGE_SIZE - count, + SPLIT_LINE_STR + "Jitter data MIN: [%d][%d]=%d, MAX: [%d][%d]=%d, AVG=%d, TOTAL FRAME=%d\n" + SPLIT_LINE_STR + " | ", min_r, min_c, min, max_r, max_c, + max, average, jitter_test_frame); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += + snprintf(buf + count, PAGE_SIZE - count, + COL_NUM_FORMAT_STR, c); + } + count += + snprintf(buf + count, PAGE_SIZE - count, + "\n" SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count += + snprintf(buf + count, PAGE_SIZE - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += + snprintf(buf + count, PAGE_SIZE - count, + DATA_FORMAT_STR, + rawdata[r * cts_dev->fwdata.cols + + c]); + } + buf[count++] = '\n'; + } + } + +err_free_rawdata_max: + kfree(rawdata_max); +err_free_rawdata_min: + kfree(rawdata_min); +err_free_rawdata: + kfree(rawdata); +err_jitter_show_exit: + cts_unlock_device(cts_dev); + return data_valid ? count : ret; +} + +static DEVICE_ATTR(jitter, S_IRUSR | S_IWUSR, jitter_show, jitter_store); + +static ssize_t compensate_cap_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 *cap = NULL; + int ret; + ssize_t count = 0; + int r, c, min, max, max_r, max_c, min_r, min_c, sum, average; + + cts_info("Read '%s'", attr->attr.name); + + cap = kzalloc(cts_dev->hwdata->num_row * cts_dev->hwdata->num_col, + GFP_KERNEL); + if (cap == NULL) { + return scnprintf(buf, PAGE_SIZE, + "Allocate mem for compensate cap failed\n"); + } + + cts_lock_device(cts_dev); + ret = cts_get_compensate_cap(cts_dev, cap); + cts_unlock_device(cts_dev); + if (ret) { + kfree(cap); + return scnprintf(buf, PAGE_SIZE, + "Get compensate cap failed %d\n", ret); + } + + max = min = cap[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->hwdata->num_row; r++) { + for (c = 0; c < cts_dev->hwdata->num_col; c++) { + u16 val = cap[r * cts_dev->hwdata->num_col + c]; + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col); + + count += scnprintf(buf + count, PAGE_SIZE - count, + "----------------------------------------------------------------------------\n" + " Compensatete Cap MIN: [%d][%d]=%u, MAX: [%d][%d]=%u, AVG=%u\n" + "---+------------------------------------------------------------------------\n" + " |", min_r, min_c, min, max_r, max_c, max, average); + for (c = 0; c < cts_dev->hwdata->num_col; c++) { + count += scnprintf(buf + count, PAGE_SIZE - count, " %3u", c); + } + count += scnprintf(buf + count, PAGE_SIZE - count, + "\n" + "---+------------------------------------------------------------------------\n"); + + for (r = 0; r < cts_dev->hwdata->num_row; r++) { + count += scnprintf(buf + count, PAGE_SIZE - count, "%2u |", r); + for (c = 0; c < cts_dev->hwdata->num_col; c++) { + count += scnprintf(buf + count, PAGE_SIZE - count, + " %3u", cap[r * cts_dev->hwdata->num_col + c]); + } + buf[count++] = '\n'; + } + count += scnprintf(buf + count, PAGE_SIZE - count, + "---+------------------------------------------------------------------------\n"); + + kfree(cap); + + return count; +} + +static DEVICE_ATTR(compensate_cap, S_IRUGO, compensate_cap_show, NULL); + +#ifdef CFG_CTS_HAS_RESET_PIN +static ssize_t reset_pin_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + cts_info("Read RESET-PIN"); + + return snprintf(buf, PAGE_SIZE, + "Reset pin: %d, status: %d\n", + cts_data->pdata->rst_gpio, + gpio_get_value(cts_data->pdata->rst_gpio)); +} + +static ssize_t reset_pin_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + cts_info("Write RESET-PIN"); + cts_info("Chip staus maybe changed"); + + cts_plat_set_reset(cts_dev->pdata, (buf[0] == '1') ? 1 : 0); + return count; +} + +static DEVICE_ATTR(reset_pin, S_IRUSR | S_IWUSR, reset_pin_show, + reset_pin_store); +#endif + +static ssize_t irq_pin_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + cts_info("Read IRQ-PIN"); + + return snprintf(buf, PAGE_SIZE, + "IRQ pin: %d, status: %d\n", + cts_data->pdata->int_gpio, + gpio_get_value(cts_data->pdata->int_gpio)); +} + +static DEVICE_ATTR(irq_pin, S_IRUGO, irq_pin_show, NULL); + +static ssize_t irq_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct irq_desc *desc; + + cts_info("Read IRQ-INFO"); + + desc = irq_to_desc(cts_data->pdata->irq); + if (desc == NULL) { + return snprintf(buf, PAGE_SIZE, + "IRQ: %d descriptor not found\n", + cts_data->pdata->irq); + } + + return snprintf(buf, PAGE_SIZE, + "IRQ num: %d, depth: %u, " + "count: %u, unhandled: %u, last unhandled eslape: %lu\n", + cts_data->pdata->irq, desc->depth, + desc->irq_count, desc->irqs_unhandled, + desc->last_unhandled); +} + +static DEVICE_ATTR(irq_info, S_IRUGO, irq_info_show, NULL); + +#ifdef CFG_CTS_FW_LOG_REDIRECT +static ssize_t fw_log_redirect_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + return snprintf(buf, PAGE_SIZE, "Fw log redirect is %s\n", + cts_is_fw_log_redirect(cts_dev) ? "enable" : "disable"); +} + +static ssize_t fw_log_redirect_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 enable = 0; + + if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') { + enable = 1; + } + if (enable) { + cts_enable_fw_log_redirect(cts_dev); + } else { + cts_disable_fw_log_redirect(cts_dev); + } + + return count; +} + +static DEVICE_ATTR(fw_log_redirect, S_IRUSR | S_IWUSR, fw_log_redirect_show, + fw_log_redirect_store); +#endif + +static ssize_t debug_spi_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + //struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + //struct cts_device *cts_dev = &cts_data->cts_dev; + + return snprintf(buf, PAGE_SIZE, "spi_speed=%d\n", speed); +} + +static ssize_t debug_spi_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u16 s = 0; + int ret = 0; + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + ret = kstrtou16(argv[0], 0, &s); + if (ret) { + cts_err("Invalid spi speed: %s", argv[0]); + return -EINVAL; + } + + speed = s; + + return count; +} + +static DEVICE_ATTR(debug_spi, S_IRUSR | S_IWUSR, debug_spi_show, + debug_spi_store); + +#ifdef CFG_CTS_GESTURE +static ssize_t gesture_en_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + return sprintf(buf, "Gesture wakup is %s\n", + cts_is_gesture_wakeup_enabled(cts_dev) ? "enable" : + "disable"); +} + +static ssize_t gesture_en_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 enable = 0; + + if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') { + enable = 1; + } + if (enable) { + cts_enable_gesture_wakeup(cts_dev); + } else { + cts_disable_gesture_wakeup(cts_dev); + } + + return count; +} + +static DEVICE_ATTR(gesture_en, S_IRUSR | S_IWUSR, gesture_en_show, + gesture_en_store); +#endif + +static struct attribute *cts_dev_misc_atts[] = { + &dev_attr_ic_type.attr, + &dev_attr_program_mode.attr, + &dev_attr_rawdata.attr, + &dev_attr_diffdata.attr, + &dev_attr_manualdiff.attr, + &dev_attr_jitter.attr, +#ifdef CFG_CTS_HAS_RESET_PIN + &dev_attr_reset_pin.attr, +#endif + &dev_attr_irq_pin.attr, + &dev_attr_irq_info.attr, +#ifdef CFG_CTS_FW_LOG_REDIRECT + &dev_attr_fw_log_redirect.attr, +#endif + &dev_attr_compensate_cap.attr, + &dev_attr_read_reg.attr, + &dev_attr_write_reg.attr, + &dev_attr_read_hw_reg.attr, + &dev_attr_write_hw_reg.attr, + &dev_attr_debug_spi.attr, +#ifdef CFG_CTS_GESTURE + &dev_attr_gesture_en.attr, +#endif /* CFG_CTS_GESTURE */ + NULL +}; + +static const struct attribute_group cts_dev_misc_attr_group = { + .name = "misc", + .attrs = cts_dev_misc_atts, +}; + +#ifdef CFG_CTS_FW_UPDATE_SYS +static struct attribute *cts_dev_fw_up_atts[] = { +// &dev_attr_drv_irq.attr, +// &dev_attr_reset.attr, + &dev_attr_buildid.attr, + &dev_attr_forcereflash.attr, + &dev_attr_flashprog.attr, + &dev_attr_doreflash.attr, + &dev_attr_poweron.attr, + &dev_attr_productinfo.attr, + &dev_attr_panel_supplier.attr, + NULL +}; + +static const struct attribute_group cts_dev_fw_up_attr_group = { + .attrs = cts_dev_fw_up_atts, +}; +#endif + +static const struct attribute_group *cts_dev_attr_groups[] = { + &cts_dev_firmware_attr_group, +#ifdef CFG_CTS_FW_UPDATE_SYS + &cts_dev_fw_up_attr_group, +#endif + &cts_dev_flash_attr_group, + &cts_dev_test_attr_group, + &cts_dev_misc_attr_group, + NULL +}; + +#include +#include + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *data = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!data) { + cts_err("Read 'path' with chipone_ts_data NULL"); + return (ssize_t) 0; + } +#ifdef CONFIG_CTS_I2C_HOST + path = kobject_get_path(&data->i2c_client->dev.kobj, GFP_KERNEL); +#else + path = kobject_get_path(&data->spi_client->dev.kobj, GFP_KERNEL); +#endif + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "chipone"); +} + +/* Attribute: vendor (RO) */ +static ssize_t ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *ts = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%s%s\n%s%04x\n%s%04x\n", + "Product ID: ", ts->cts_dev.hwdata->name, + "Build ID: ", ts->cts_dev.fwdata.version ? ts->cts_dev.fwdata.version : ts->pdata->build_id, + "Config ID: ", ts->cts_dev.fwdata.ddi_version ? ts->cts_dev.fwdata.ddi_version : ts->pdata->config_id); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_RO(ic_ver), + __ATTR_NULL +}; + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +/******************************************************* +Description: + Chipone touchscreen FW function class. file node + initial function. + +return: + Executive outcomes. 0---succeed. -1---failed. +*******************************************************/ +static int cts_fw_class_init(void *_data, bool create) +{ + struct chipone_ts_data *data = _data; + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + static struct class *touchscreen_class; + static struct device *ts_class_dev; + dev_t devno; + + cts_info("%s touchscreen class files", create ? "Add" : "Remove"); + + if (create) { + if(NULL != data->cts_dev.hwdata->name) { + error = alloc_chrdev_region(&devno, 0, 1, data->cts_dev.hwdata->name); + } else { + error = alloc_chrdev_region(&devno, 0, 1, CFG_CTS_CHIP_NAME); + } + if (error) { + cts_info + ("Alloc input devno failed %d", error); + return error; + } + + cts_info("Create class 'touchscreen'"); + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + cts_err("Create class 'touchscreen' failed %ld", + PTR_ERR(touchscreen_class)); + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + + if(NULL != data->cts_dev.hwdata->name) { + ts_class_dev = device_create(touchscreen_class, NULL, + devno, + data, "%s", data->cts_dev.hwdata->name); + cts_info("Create device for IC: %s", data->cts_dev.hwdata->name); + } else { + ts_class_dev = device_create(touchscreen_class, NULL, + devno, + data, "%s", CFG_CTS_CHIP_NAME); + cts_info("Create device '" CFG_CTS_CHIP_NAME "'"); + } + if (IS_ERR(ts_class_dev)) { + cts_err("Create device '" CFG_CTS_CHIP_NAME + "'failed %ld", PTR_ERR(ts_class_dev)); + error = PTR_ERR(ts_class_dev); + ts_class_dev = NULL; + return error; + } + + cts_info("Create attr files"); + for (i = 0; attrs[i].attr.name != NULL; ++i) { + cts_info(" Create attr file '%s'", attrs[i].attr.name); + error = device_create_file(ts_class_dev, &attrs[i]); + if (error) { + cts_err("Create attr file '%s' failed %d", + attrs[i].attr.name, error); + break; + } + } + + if (error) { + goto device_destroy; + } else { + cts_info("Create /sys/class/touchscreen/ Succeeded"); + } + } else { + if (!touchscreen_class || !ts_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + cts_info("Remove device file '%s'", attrs[i].attr.name); + device_remove_file(ts_class_dev, &attrs[i]); + } + device_unregister(ts_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, devno); + ts_class_dev = NULL; + class_unregister(touchscreen_class); + cts_err("Creating touchscreen class failed %d", error); + + return -ENODEV; +} + +int cts_sysfs_add_device(struct device *dev) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret, i; + + cts_info("Add device attr groups"); + + // Low version kernel NOT support sysfs_create_groups() + for (i = 0; cts_dev_attr_groups[i]; i++) { + ret = sysfs_create_group(&dev->kobj, cts_dev_attr_groups[i]); + if (ret) { + while (--i >= 0) { + sysfs_remove_group(&dev->kobj, + cts_dev_attr_groups[i]); + } + break; + } + } + + if (ret) { + cts_err("Add device attr failed %d", ret); + return ret; + } + + manualdiff_base = + (s16 *) kzalloc(DIFFDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (manualdiff_base == NULL) { + cts_err("Malloc manualdiff_base failed"); + return -ENOMEM; + } + + ret = sysfs_create_link(NULL, &dev->kobj, "chipone-tddi"); + if (ret) { + cts_err("Create sysfs link error:%d", ret); + } + + ret = cts_fw_class_init(cts_data, true); + if (ret) { + cts_err("Create touchscreen class failed. ret=%d\n", ret); + return ret; + } + + return 0; +} + +void cts_sysfs_remove_device(struct device *dev) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + int i; + + cts_info("Remove device attr groups"); + + if (manualdiff_base != NULL) { + kfree(manualdiff_base); + manualdiff_base = NULL; + } + + sysfs_remove_link(NULL, "chipone-tddi"); + // Low version kernel NOT support sysfs_remove_groups() + for (i = 0; cts_dev_attr_groups[i]; i++) { + sysfs_remove_group(&dev->kobj, cts_dev_attr_groups[i]); + } + cts_fw_class_init(cts_data, false); +} + +#undef SPLIT_LINE_STR +#undef ROW_NUM_FORMAT_STR +#undef COL_NUM_FORMAT_STR +#undef DATA_FORMAT_STR + +#endif /* CONFIG_CTS_SYSFS */ diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_sysfs.h b/drivers/input/touchscreen/chipone_tddi_mmi/cts_sysfs.h new file mode 100644 index 000000000000..7bf99c347703 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_sysfs.h @@ -0,0 +1,24 @@ +#ifndef CTS_SYSFS_H +#define CTS_SYSFS_H + +#include "cts_config.h" + +struct device; + +extern u16 speed; + +#ifdef CONFIG_CTS_SYSFS +extern int cts_sysfs_add_device(struct device *dev); +extern void cts_sysfs_remove_device(struct device *dev); +#else /* CONFIG_CTS_SYSFS */ +static inline int cts_sysfs_add_device(struct device *dev) +{ + return -ENOTSUPP; +} + +static inline void cts_sysfs_remove_device(struct device *dev) +{ +} +#endif /* CONFIG_CTS_SYSFS */ + +#endif /* CTS_SYSFS_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_test.c b/drivers/input/touchscreen/chipone_tddi_mmi/cts_test.c new file mode 100755 index 000000000000..01cb37b95949 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_test.c @@ -0,0 +1,1415 @@ +#define LOG_TAG "Test" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" + +#define CTS_FIRMWARE_WORK_MODE_NORMAL (0x00) +#define CTS_FIRMWARE_WORK_MODE_FACTORY (0x01) +#define CTS_FIRMWARE_WORK_MODE_CONFIG (0x02) +#define CTS_FIRMWARE_WORK_MODE_TEST (0x03) + +#define CTS_TEST_SHORT (0x01) +#define CTS_TEST_OPEN (0x02) + +#define CTS_SHORT_TEST_UNDEFINED (0x00) +#define CTS_SHORT_TEST_BETWEEN_COLS (0x01) +#define CTS_SHORT_TEST_BETWEEN_ROWS (0x02) +#define CTS_SHORT_TEST_BETWEEN_GND (0x03) + +#define TEST_RESULT_BUFFER_SIZE(cts_dev) \ + (cts_dev->fwdata.rows * cts_dev->fwdata.cols * 2) + +#define RAWDATA_BUFFER_SIZE(cts_dev) \ + (cts_dev->fwdata.rows * cts_dev->fwdata.cols * 2) + +static int disable_fw_esd_protection(struct cts_device *cts_dev) +{ + return cts_fw_reg_writeb(cts_dev, 0x8000 + 342, 1); +} + +static int disable_fw_monitor_mode(struct cts_device *cts_dev) +{ + int ret; + u8 value; + + ret = cts_fw_reg_readb(cts_dev, 0x8000 + 344, &value); + if (ret) { + return ret; + } + + if (value & BIT(0)) { + return cts_fw_reg_writeb(cts_dev, 0x8000 + 344, + value & (~BIT(0))); + } + + return 0; +} + +static int disable_fw_auto_compensate(struct cts_device *cts_dev) +{ + return cts_fw_reg_writeb(cts_dev, 0x8000 + 276, 1); +} + +static int set_fw_work_mode(struct cts_device *cts_dev, u8 mode) +{ + int ret, retries; + u8 pwr_mode; + + cts_info("Set firmware work mode to %u", mode); + + ret = cts_fw_reg_writeb(cts_dev, CTS_DEVICE_FW_REG_WORK_MODE, mode); + if (ret) { + cts_err("Write firmware work mode register failed %d", ret); + return ret; + } + + ret = cts_fw_reg_readb(cts_dev, 0x05, &pwr_mode); + if (ret) { + cts_err("Read firmware power mode register failed %d", ret); + return ret; + } + + if (pwr_mode == 1) { + ret = cts_send_command(cts_dev, CTS_CMD_QUIT_GESTURE_MONITOR); + if (ret) { + cts_err("Send cmd QUIT_GESTURE_MONITOR failed %d", ret); + return ret; + } + + msleep(50); + } + + retries = 0; + do { + u8 sys_busy, curr_mode; + + msleep(10); + + ret = cts_fw_reg_readb(cts_dev, 0x01, &sys_busy); + if (ret) { + cts_err("Read firmware system busy register failed %d", + ret); + return ret; + } + if (sys_busy) + continue; + + ret = cts_fw_reg_readb(cts_dev, 0x3F, &curr_mode); + if (ret) { + cts_err("Read firmware current work mode failed %d", + ret); + return ret; + } + + if (curr_mode == mode || curr_mode == 0xFF) { + break; + } + } while (retries++ < 1000); + + return (retries >= 1000 ? -ETIMEDOUT : 0); +} + +static int set_display_state(struct cts_device *cts_dev, bool active) +{ + int ret; + u8 access_flag; + + cts_info("Set display state to %s", active ? "ACTIVE" : "SLEEP"); + + ret = cts_hw_reg_readb_relaxed(cts_dev, 0x3002C, &access_flag); + if (ret) { + cts_err("Read display access flag failed %d", ret); + return ret; + } + + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3002C, access_flag | 0x01); + if (ret) { + cts_err("Write display access flag %02x failed %d", access_flag, + ret); + return ret; + } + + if (active) { + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3C044, 0x55); + if (ret) { + cts_err("Write DCS-CMD11 fail"); + return ret; + } + + msleep(100); + + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3C0A4, 0x55); + if (ret) { + cts_err("Write DCS-CMD29 fail"); + return ret; + } + + msleep(100); + } else { + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3C0A0, 0x55); + if (ret) { + cts_err("Write DCS-CMD28 fail"); + return ret; + } + + msleep(100); + + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3C040, 0x55); + if (ret) { + cts_err("Write DCS-CMD10 fail"); + return ret; + } + + msleep(100); + } + + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3002C, access_flag); + if (ret) { + cts_err("Restore display access flag %02x failed %d", + access_flag, ret); + return ret; + } + + return 0; +} + +static int wait_test_complete(struct cts_device *cts_dev, int skip_frames) +{ + int ret, i, j; + + cts_info("Wait test complete skip %d frames", skip_frames); + + for (i = 0; i < (skip_frames + 1); i++) { + u8 ready; + + for (j = 0; j < 1000; j++) { + mdelay(1); + + ready = 0; + ret = cts_get_data_ready_flag(cts_dev, &ready); + if (ret) { + cts_err("Get data ready flag failed %d", ret); + return ret; + } + + if (ready) { + break; + } + } + + if (ready == 0) { + return -ETIMEDOUT; + } + if (i < skip_frames) { + ret = cts_clr_data_ready_flag(cts_dev); + if (ret) { + cts_err("Clr data ready flag failed %d", ret); + return ret; + } + } + } + + return 0; +} + +static int get_test_result(struct cts_device *cts_dev, u16 *result) +{ + int ret; + + ret = cts_fw_reg_readsb(cts_dev, CTS_DEVICE_FW_REG_RAW_DATA, result, + TEST_RESULT_BUFFER_SIZE(cts_dev)); + if (ret) { + cts_err("Get test result data failed %d", ret); + return ret; + } + + ret = cts_clr_data_ready_flag(cts_dev); + if (ret) { + cts_err("Clear data ready flag failed %d", ret); + return ret; + } + + return 0; +} + +static int set_fw_test_type(struct cts_device *cts_dev, u8 type) +{ + int i, ret; + + cts_info("Set test type %d", type); + + for (i = 0; i < 5; i++) { + u8 type_readback; + + ret = cts_fw_reg_writeb(cts_dev, 0x34, type); + if (ret) { + cts_err("Write test type register to failed %d", ret); + continue; + } + + ret = cts_fw_reg_readb(cts_dev, 0x34, &type_readback); + if (ret) { + cts_err("Read test type register failed %d", ret); + continue; + } + + if (type != type_readback) { + cts_err("Set test type %u != readback %u", type, + type_readback); + ret = -EFAULT; + continue; + } + } + + return ret; +} + +struct cts_fw_short_test_param { + u8 type; + u32 col_pattern[2]; + u32 row_pattern[2]; +}; + +static bool set_short_test_type(struct cts_device *cts_dev, u8 type) +{ + static struct cts_fw_short_test_param param = { + .type = CTS_SHORT_TEST_BETWEEN_COLS, + .col_pattern = {0, 0}, + .row_pattern = {0, 0} + }; + int i, ret; + + cts_info("Set short test type to %u", type); + + param.type = type; + for (i = 0; i < 5; i++) { + u8 type_readback; + + ret = + cts_fw_reg_writesb(cts_dev, 0x5000, ¶m, sizeof(param)); + if (ret) { + cts_err("Set short test type to %u failed %d", type, + ret); + continue; + } + ret = cts_fw_reg_readb(cts_dev, 0x5000, &type_readback); + if (ret) { + cts_err("Get short test type failed %d", ret); + continue; + } + if (type == type_readback) { + return 0; + } else { + cts_err("Set test type %u != readback %u", type, + type_readback); + continue; + } + } + + return ret; +} + +static void dump_test_data(struct cts_device *cts_dev, + const char *desc, const u16 *data) +{ +#define SPLIT_LINE_STR \ + "----------------------------------------------------------------------------------------------------------------" +#define ROW_NUM_FORMAT_STR "%2d | " +#define COL_NUM_FORMAT_STR "%-5u " +#define DATA_FORMAT_STR "%-5u " + + int r, c; + u32 max, min, sum, average; + int max_r, max_c, min_r, min_c; + char line_buf[128]; + int count = 0; + + max = min = data[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + u16 val = data[r * cts_dev->fwdata.cols + c]; + + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + cts_info(SPLIT_LINE_STR); + count += snprintf(line_buf + count, sizeof(line_buf) - count, + " %s test data MIN: [%u][%u]=%u, MAX: [%u][%u]=%u, AVG=%u", + desc, min_r, min_c, min, max_r, max_c, max, average); + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + + count = 0; + count += snprintf(line_buf + count, sizeof(line_buf) - count, " | "); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += snprintf(line_buf + count, sizeof(line_buf) - count, + COL_NUM_FORMAT_STR, c); + } + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count = 0; + count += snprintf(line_buf + count, sizeof(line_buf) - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += + snprintf(line_buf + count, sizeof(line_buf) - count, + DATA_FORMAT_STR, + data[r * cts_dev->fwdata.cols + c]); + } + cts_info("%s", line_buf); + } + cts_info(SPLIT_LINE_STR); + +#undef SPLIT_LINE_STR +#undef ROW_NUM_FORMAT_STR +#undef COL_NUM_FORMAT_STR +#undef DATA_FORMAT_STR +} + +/* Return number of failed nodes */ +static int validate_test_result(struct cts_device *cts_dev, + const char *desc, u16 *test_result, + u16 min, u16 max) +{ +#define SPLIT_LINE_STR \ + "------------------------------" + + int r, c; + int failed_cnt = 0; + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + if (test_result[r * cts_dev->fwdata.cols + c] < min || + test_result[r * cts_dev->fwdata.cols + c] > max) { + if (failed_cnt == 0) { + cts_info(SPLIT_LINE_STR); + cts_info("%s failed nodes:", desc); + } + failed_cnt++; + + cts_info(" %3d: [%-2d][%-2d] = %u", failed_cnt, + r, c, + test_result[r * cts_dev->fwdata.cols + + c]); + } + } + } + + if (failed_cnt) { + cts_info(SPLIT_LINE_STR); + } + cts_info("%s test %d node total failed", desc, failed_cnt); + + return failed_cnt; + +#undef SPLIT_LINE_STR +} + +static int test_short_to_gnd(struct cts_device *cts_dev, + u8 feature_ver, u16 *test_result, u16 threshold) +{ + int ret; + + cts_info("Test short to GND"); + + ret = set_short_test_type(cts_dev, CTS_SHORT_TEST_UNDEFINED); + if (ret) { + cts_err("Set short test type to UNDEFINED failed %d", ret); + return ret; + } + + ret = set_fw_test_type(cts_dev, CTS_TEST_SHORT); + if (ret) { + cts_err("Set test type to SHORT failed %d", ret); + return ret; + } + + ret = set_fw_work_mode(cts_dev, CTS_FIRMWARE_WORK_MODE_TEST); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_TEST failed %d", + ret); + return ret; + } + + if (feature_ver <= 3) { + u8 val; + ret = cts_hw_reg_readb_relaxed(cts_dev, 0x350E2, &val); + if (ret) { + cts_err("Read 0x350E2 failed %d", ret); + return ret; + } + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x350E2, val & 0xDB); + if (ret) { + cts_err("Write 0x350E2 failed %d", ret); + return ret; + } + } + + ret = set_short_test_type(cts_dev, CTS_SHORT_TEST_BETWEEN_GND); + if (ret) { + cts_err("Set short test type to SHORT_TO_GND failed %d", ret); + return ret; + } + + ret = wait_test_complete(cts_dev, 0); + if (ret) { + cts_err("Wait test complete failed %d", ret); + return ret; + } + + ret = get_test_result(cts_dev, test_result); + if (ret) { + cts_err("Read test result failed %d", ret); + return ret; + } + + dump_test_data(cts_dev, "GND-short", test_result); + + return validate_test_result(cts_dev, "GND-short", test_result, + threshold, USHRT_MAX); +} + +static int test_short_to_gnd_legacy(struct cts_device *cts_dev, + u16 *test_result, u16 threshold) +{ + int ret; + + cts_info("Test short to GND"); + + ret = cts_send_command(cts_dev, CTS_CMD_RECOVERY_TX_VOL); + if (ret) { + cts_err("Send command RECOVERY_TX_VOL failed %d", ret); + return ret; + } + + ret = wait_test_complete(cts_dev, 2); + if (ret) { + cts_err("Wait test complete failed %d", ret); + return ret; + } + + ret = get_test_result(cts_dev, test_result); + if (ret) { + cts_err("Read test result failed %d", ret); + return ret; + } + + dump_test_data(cts_dev, "GND-short", test_result); + + return validate_test_result(cts_dev, "GND-short", test_result, + threshold, USHRT_MAX); +} + +static int test_short_between_cols(struct cts_device *cts_dev, + u16 *test_result, u16 threshold, + bool skip_first_frame) +{ + int ret; + + cts_info("Test short between columns"); + + ret = set_fw_test_type(cts_dev, CTS_TEST_SHORT); + if (ret) { + cts_err("Set test type to SHORT failed %d", ret); + return ret; + } + + ret = set_short_test_type(cts_dev, CTS_SHORT_TEST_BETWEEN_COLS); + if (ret) { + cts_err("Set short test type to BETWEEN_COLS failed %d", ret); + return ret; + } + + ret = set_fw_work_mode(cts_dev, CTS_FIRMWARE_WORK_MODE_TEST); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_TEST failed %d", + ret); + return ret; + } + + if (skip_first_frame) { + cts_info("Skip first frame data"); + + ret = wait_test_complete(cts_dev, 0); + if (ret) { + cts_err("Wait test complete failed %d", ret); + return ret; + } + + ret = get_test_result(cts_dev, test_result); + if (ret) { + cts_err("Read skip test result failed %d", ret); + return ret; + } + + ret = set_short_test_type(cts_dev, CTS_SHORT_TEST_BETWEEN_COLS); + if (ret) { + cts_err("Set short test type to BETWEEN_COLS failed %d", + ret); + return ret; + } + } + + ret = wait_test_complete(cts_dev, 0); + if (ret) { + cts_err("Wait test complete failed %d", ret); + return ret; + } + + ret = get_test_result(cts_dev, test_result); + if (ret) { + cts_err("Read test result failed %d", ret); + return ret; + } + + dump_test_data(cts_dev, "Col-short", test_result); + + return validate_test_result(cts_dev, "Col-short", test_result, + threshold, USHRT_MAX); +} + +static int test_short_between_rows(struct cts_device *cts_dev, + u16 *test_result, u16 threshold) +{ + int ret; + int loopcnt; + + cts_info("Test short between rows"); + + ret = set_short_test_type(cts_dev, CTS_SHORT_TEST_BETWEEN_ROWS); + if (ret) { + cts_err("Set short test type to BETWEEN_ROWS failed %d", ret); + return ret; + } + + loopcnt = cts_dev->hwdata->num_row; + while (loopcnt > 1) { + ret = wait_test_complete(cts_dev, 0); + if (ret) { + cts_err("Wait test complete failed %d", ret); + return ret; + } + + ret = get_test_result(cts_dev, test_result); + if (ret) { + cts_err("Read test result failed %d", ret); + return ret; + } + + dump_test_data(cts_dev, "Row-short", test_result); + + ret = + validate_test_result(cts_dev, "Row-short", test_result, + threshold, USHRT_MAX); + if (ret != 0) { + break; + } + + loopcnt += loopcnt % 2; + loopcnt = loopcnt >> 1; + } + + return ret; +} + +static int wait_fw_to_normal_work(struct cts_device *cts_dev) +{ + int i = 0; + int ret; + + cts_info("Wait fw to normal work"); + + do { + u8 work_mode; + + ret = cts_fw_reg_readb(cts_dev, + CTS_DEVICE_FW_REG_GET_WORK_MODE, + &work_mode); + if (ret) { + cts_err("Get fw curr work mode failed %d", work_mode); + continue; + } else { + if (work_mode == CTS_FIRMWARE_WORK_MODE_NORMAL) { + return 0; + } + } + + mdelay(10); + } while (++i < 100); + + return ret ? ret : -ETIMEDOUT; +} + +static int prepare_test(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Prepare test"); + + cts_plat_reset_device(cts_dev->pdata); + + ret = wait_fw_to_normal_work(cts_dev); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + return ret; + } + + ret = disable_fw_esd_protection(cts_dev); + if (ret) { + cts_err("Disable firmware ESD protection failed %d", ret); + return ret; + } + + ret = disable_fw_monitor_mode(cts_dev); + if (ret) { + cts_err("Disable firmware monitor mode failed %d", ret); + return ret; + } + + ret = disable_fw_auto_compensate(cts_dev); + if (ret) { + cts_err("Disable firmware auto compensate failed %d", ret); + return ret; + } + + ret = set_fw_work_mode(cts_dev, CTS_FIRMWARE_WORK_MODE_CONFIG); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", + ret); + return ret; + } + + cts_dev->rtdata.testing = true; + + return 0; +} + +static void post_test(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Post test"); + + cts_plat_reset_device(cts_dev->pdata); + + ret = set_fw_work_mode(cts_dev, CTS_FIRMWARE_WORK_MODE_NORMAL); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_NORMAL failed %d", + ret); + } + + ret = wait_fw_to_normal_work(cts_dev); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + //return ret; + } + + cts_dev->rtdata.testing = false; +} + +/* Return 0 success + negative value while error occurs + positive value means how many nodes fail */ +int cts_short_test(struct cts_device *cts_dev, u16 threshold) +{ + int ret; + u16 *test_result = NULL; + bool recovery_display_state = false; + u8 need_display_on; + u8 feature_ver; + + cts_info("Start short test, threshold = %u", threshold); + + test_result = + (u16 *) kmalloc(TEST_RESULT_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (test_result == NULL) { + cts_err("Allocate test result buffer failed"); + return -ENOMEM; + } + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } + + cts_lock_device(cts_dev); + + ret = prepare_test(cts_dev); + if (ret) { + cts_err("Prepare test failed %d", ret); + goto err_free_test_result; + } + + ret = cts_sram_readb(cts_dev, 0xE8, &feature_ver); + if (ret) { + cts_err("Read firmware feature version failed %d", ret); + goto err_free_test_result; + } + cts_info("Feature version: %u", feature_ver); + + if (feature_ver > 0) { + ret = + test_short_to_gnd(cts_dev, feature_ver, test_result, + threshold); + } else { + ret = test_short_to_gnd_legacy(cts_dev, test_result, threshold); + } + if (ret) { + cts_err("Short to GND test failed %d", ret); + goto err_free_test_result; + } + + ret = cts_fw_reg_readb(cts_dev, 0x8000 + 163, &need_display_on); + if (ret) { + cts_err("Read need display on register failed %d", ret); + goto err_free_test_result; + } + + if (need_display_on == 0) { + ret = set_display_state(cts_dev, false); + if (ret) { + cts_err("Set display state to SLEEP failed %d", ret); + goto err_free_test_result; + } + recovery_display_state = true; + } + + ret = test_short_between_cols(cts_dev, + test_result, threshold, + need_display_on == 0); + if (ret) { + cts_err("Short between columns test failed %d", ret); + goto err_recovery_display_state; + } + + ret = test_short_between_rows(cts_dev, test_result, threshold); + if (ret) { + cts_err("Short between columns test failed %d", ret); + goto err_recovery_display_state; + } + +err_recovery_display_state: + if (recovery_display_state) { + int r = set_display_state(cts_dev, true); + if (r) { + cts_err("Set display state to ACTIVE failed %d", r); + } + } +err_free_test_result: + kfree(test_result); + post_test(cts_dev); + + cts_unlock_device(cts_dev); + + cts_info("Short test %s", ret ? "FAILED" : "PASSED"); + + cts_start_device(cts_dev); + + return ret; +} + +/* Return 0 success + negative value while error occurs + positive value means how many nodes fail */ +int cts_open_test(struct cts_device *cts_dev, u16 threshold) +{ + int ret; + u16 *test_result = NULL; + bool recovery_display_state = false; + u8 need_display_on; + + cts_info("Start open test, threshold = %u", threshold); + + test_result = + (u16 *) kmalloc(TEST_RESULT_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (test_result == NULL) { + cts_err("Allocate memory for test result faild"); + return -ENOMEM; + } + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } + + cts_lock_device(cts_dev); + ret = prepare_test(cts_dev); + if (ret) { + cts_err("Prepare test failed %d", ret); + goto err_free_test_result; + } + + ret = cts_fw_reg_readb(cts_dev, 0x8000 + 163, &need_display_on); + if (ret) { + cts_err("Read need display on register failed %d", ret); + goto err_free_test_result; + } + + if (need_display_on == 0) { + ret = set_display_state(cts_dev, false); + if (ret) { + cts_err("Set display state to SLEEP failed %d", ret); + goto err_free_test_result; + } + recovery_display_state = true; + } + + ret = cts_send_command(cts_dev, CTS_CMD_RECOVERY_TX_VOL); + if (ret) { + cts_err("Recovery tx voltage failed %d", ret); + goto err_recovery_display_state; + } + + ret = set_fw_test_type(cts_dev, CTS_TEST_OPEN); + if (ret) { + cts_err("Set test type to OPEN_TEST failed %d", ret); + goto err_recovery_display_state; + } + + ret = set_fw_work_mode(cts_dev, CTS_FIRMWARE_WORK_MODE_TEST); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_TEST failed %d", + ret); + goto err_recovery_display_state; + } + + ret = wait_test_complete(cts_dev, 2); + if (ret) { + cts_err("Wait test complete failed %d", ret); + goto err_recovery_display_state; + } + + ret = get_test_result(cts_dev, test_result); + if (ret) { + cts_err("Read test result failed %d", ret); + goto err_recovery_display_state; + } + + dump_test_data(cts_dev, "Open-circuit", test_result); + + ret = + validate_test_result(cts_dev, "Open-circuit", test_result, + threshold, USHRT_MAX); + +err_recovery_display_state: + if (recovery_display_state) { + int r = set_display_state(cts_dev, true); + if (r) { + cts_err("Set display state to ACTIVE failed %d", r); + } + } +err_free_test_result: + kfree(test_result); + post_test(cts_dev); + + cts_unlock_device(cts_dev); + cts_info("Open test %s", ret ? "FAILED" : "PASSED"); + + cts_start_device(cts_dev); + + return ret; +} + +#ifdef CFG_CTS_HAS_RESET_PIN +int cts_reset_test(struct cts_device *cts_dev) +{ + int ret; + int val = 0; + + cts_info("Reset Pin"); + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } + + cts_lock_device(cts_dev); + + cts_plat_set_reset(cts_dev->pdata, 0); + mdelay(50); +#ifdef CONFIG_CTS_I2C_HOST + /* Check whether device is in normal mode */ + if (!cts_plat_is_i2c_online(cts_dev->pdata, + CTS_DEV_NORMAL_MODE_I2CADDR)) { +#else + if (!cts_plat_is_normal_mode(cts_dev->pdata)) { +#endif /* CONFIG_CTS_I2C_HOST */ + val++; + } else { + cts_err("Device is alive while reset is low"); + } + cts_plat_set_reset(cts_dev->pdata, 1); + mdelay(50); + + ret = wait_fw_to_normal_work(cts_dev); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + } +#ifdef CONFIG_CTS_I2C_HOST + /* Check whether device is in normal mode */ + if (cts_plat_is_i2c_online(cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR)) { +#else + if (cts_plat_is_normal_mode(cts_dev->pdata)) { +#endif /* CONFIG_CTS_I2C_HOST */ + val++; + } else { + cts_err("Device is offline while reset is high"); + } +#ifdef CONFIG_CTS_CHARGER_DETECT + if (cts_is_charger_exist(cts_dev)) { + int r = cts_set_dev_charger_attached(cts_dev, true); + if (r) { + cts_err("Set dev charger attached failed %d", r); + } + } +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) { + cts_enter_glove_mode(cts_dev); + } +#endif + +#ifdef CFG_CTS_FW_LOG_REDIRECT + if (cts_is_fw_log_redirect(cts_dev)) { + cts_enable_fw_log_redirect(cts_dev); + } +#endif + + cts_unlock_device(cts_dev); + + ret = cts_start_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + } + + cts_info("Reset-Pin test %s", val == 2 ? "PASS" : "FAIL"); + if (val == 2) { + if (!cts_dev->rtdata.program_mode) { + cts_set_normal_addr(cts_dev); + } + return 0; + } + + return -EFAULT; +} +#endif + +int cts_test_int_pin(struct cts_device *cts_dev) +{ + int ret; + + cts_info("INT-pin"); + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } + + cts_lock_device(cts_dev); + + ret = cts_send_command(cts_dev, CTS_CMD_WRTITE_INT_HIGH); + if (ret) { + cts_err("Send command WRTITE_INT_HIGH failed %d", ret); + goto unlock_device; + } + mdelay(10); + if (cts_plat_get_int_pin(cts_dev->pdata) == 0) { + cts_err("INT pin state != HIGH"); + ret = -EFAULT; + goto unlock_device; + } + + ret = cts_send_command(cts_dev, CTS_CMD_WRTITE_INT_LOW); + if (ret) { + cts_err("Send command WRTITE_INT_LOW failed %d", ret); + goto unlock_device; + } + mdelay(10); + if (cts_plat_get_int_pin(cts_dev->pdata) != 0) { + cts_err("INT pin state != LOW"); + ret = -EFAULT; + goto unlock_device; + } + + ret = cts_send_command(cts_dev, CTS_CMD_RELASE_INT_TEST); + if (ret) { + cts_err("Send command RELASE_INT_TEST failed %d", ret); + ret = 0; // Ignore this error + } + mdelay(10); + +unlock_device: + cts_unlock_device(cts_dev); + + ret = cts_start_device(cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + ret = 0; // Ignore this error + } + + cts_info("Int-Pin test %s", ret == 0 ? "PASS" : "FAIL"); + return ret; +} + +void dump_compensate_cap(struct cts_device *cts_dev, u8 *cap) +{ +#define SPLIT_LINE_STR \ + "-----------------------------------------------------------------------------" +#define ROW_NUM_FORMAT_STR "%2d | " +#define COL_NUM_FORMAT_STR "%3u " +#define DATA_FORMAT_STR "%4d" + + int r, c; + char line_buf[128]; + int count = 0; + + cts_info(SPLIT_LINE_STR); + count += snprintf(line_buf + count, sizeof(line_buf) - count, " "); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += snprintf(line_buf + count, sizeof(line_buf) - count, + COL_NUM_FORMAT_STR, c); + } + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count = 0; + count += snprintf(line_buf + count, sizeof(line_buf) - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += snprintf(line_buf + count, + sizeof(line_buf) - count, + DATA_FORMAT_STR, + cap[r * cts_dev->fwdata.cols + c]); + } + cts_info("%s", line_buf); + } + cts_info(SPLIT_LINE_STR); +#undef SPLIT_LINE_STR +#undef ROW_NUM_FORMAT_STR +#undef COL_NUM_FORMAT_STR +#undef DATA_FORMAT_STR +} + +static int validate_compensate_cap(struct cts_device *cts_dev, + const char *desc, u8 *test_result, + u16 min, u16 max) +{ +#define SPLIT_LINE_STR \ + "------------------------------" + + int r, c; + int failed_cnt = 0; + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + if (test_result[r * cts_dev->fwdata.cols + c] < min || + test_result[r * cts_dev->fwdata.cols + c] > max) { + if (failed_cnt == 0) { + cts_info(SPLIT_LINE_STR); + cts_info("%s failed nodes:", desc); + } + failed_cnt++; + + cts_info(" %3d: [%-2d][%-2d] = %u", failed_cnt, + r, c, + test_result[r * cts_dev->fwdata.cols + + c]); + } + } + } + + if (failed_cnt) { + cts_info("%s", SPLIT_LINE_STR); + } + cts_info("%s test %d node total failed", desc, failed_cnt); + + return failed_cnt; + +#undef SPLIT_LINE_STR +} + +int cts_compensate_cap_test(struct cts_device *cts_dev, u8 min_thres, + u8 max_thres) +{ + u8 *cap = NULL; + int ret = 0; + + cts_info("Compensate cap, threshold [%u, %u]", min_thres, max_thres); + + cap = + kzalloc(cts_dev->hwdata->num_row * cts_dev->hwdata->num_col, + GFP_KERNEL); + if (cap == NULL) { + cts_err("Allocate mem for cap failed %d", ret); + return -ENOMEM; + } + + cts_lock_device(cts_dev); + ret = cts_get_compensate_cap(cts_dev, cap); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("Get compensate cap failed %d", ret); + goto free_cap; + } + + dump_compensate_cap(cts_dev, cap); + ret = + validate_compensate_cap(cts_dev, "Compensate-Cap", cap, + min_thres, max_thres); + +free_cap: + kfree(cap); + + cts_info("Compensate-Cap test %s", ret == 0 ? "PASS" : "FAIL"); + return ret; +} + +int cts_rawdata_test(struct cts_device *cts_dev, u16 min, u16 max) +{ + int ret; + bool data_valid = false; + u16 *rawdata; + int i; + + cts_info("Rawdata, threshold[%u, %u]", min, max); + + rawdata = (u16 *) kmalloc(RAWDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate memory for rawdata failed"); + return -ENOMEM; + } + + cts_lock_device(cts_dev); + + for (i = 0; i < 5; i++) { + int r; + u8 val; + r = cts_enable_get_rawdata(cts_dev); + if (r) { + cts_err("Enable get ts data failed %d", r); + continue; + } + mdelay(1); + r = cts_fw_reg_readb(cts_dev, 0x12, &val); + if (r) { + cts_err("Read enable get ts data failed %d", r); + continue; + } + if (val != 0) { + break; + } + } + + if (i >= 5) { + cts_err("Enable read tsdata failed"); + ret = -EIO; + goto unlock_device; + } + + ret = cts_send_command(cts_dev, CTS_CMD_QUIT_GESTURE_MONITOR); + if (ret) { + cts_err("Send CMD_QUIT_GESTURE_MONITOR failed %d", ret); + } + + msleep(50); + for (i = 0; i < 3; i++) { + ret = cts_get_rawdata(cts_dev, rawdata); + if (ret) { + cts_err("Get rawdata failed %d", ret); + continue; + } else { + data_valid = true; + break; + } + mdelay(30); + } + + if (i >= 3) { + cts_err("Read rawdata failed"); + ret = -EIO; + } + + for (i = 0; i < 5; i++) { + int r = cts_disable_get_rawdata(cts_dev); + if (r) { + cts_err("Disable get rawdata failed %d", r); + continue; + } else { + break; + } + } + +unlock_device: + cts_unlock_device(cts_dev); + + if (data_valid) { + dump_test_data(cts_dev, "Rawdata", rawdata); + ret = validate_test_result(cts_dev, "Rawdata", + rawdata, min, max); + } + kfree(rawdata); + + cts_info("Rawdata test %s", + (data_valid && (ret == 0)) ? "PASS" : "FAIL"); + + return ret; +} + +int cts_noise_test(struct cts_device *cts_dev, u32 frames, u16 max) +{ + u16 *curr_rawdata = NULL; + u16 *max_rawdata = NULL; + u16 *min_rawdata = NULL; + u16 *noise = NULL; + bool first_frame = true; + bool data_valid = false; + u16 i; + int ret; + + cts_info("Noise frames: %u, threshold: %u", frames, max); + + curr_rawdata = + (u16 *) kmalloc(TEST_RESULT_BUFFER_SIZE(cts_dev) * 4, GFP_KERNEL); + if (curr_rawdata == NULL) { + cts_err("Alloc mem for rawdata failed"); + return -ENOMEM; + } + max_rawdata = + curr_rawdata + cts_dev->fwdata.rows * cts_dev->fwdata.cols; + min_rawdata = max_rawdata + cts_dev->fwdata.rows * cts_dev->fwdata.cols; + noise = min_rawdata + cts_dev->fwdata.rows * cts_dev->fwdata.cols; + + cts_lock_device(cts_dev); + + for (i = 0; i < 5; i++) { + int r; + u8 val; + r = cts_enable_get_rawdata(cts_dev); + if (r) { + cts_err("Enable get ts data failed %d", r); + continue; + } + mdelay(1); + r = cts_fw_reg_readb(cts_dev, 0x12, &val); + if (r) { + cts_err("Read enable get ts data failed %d", r); + continue; + } + if (val != 0) { + break; + } + } + + if (i >= 5) { + cts_err("Enable read tsdata failed"); + ret = -EIO; + goto unlock_device; + } + + ret = cts_send_command(cts_dev, CTS_CMD_QUIT_GESTURE_MONITOR); + if (ret) { + cts_err("send quit gesture monitor err"); + // Ignore this error + } + + msleep(50); + + while (frames--) { + for (i = 0; i < 3; i++) { + int r; + r = cts_get_rawdata(cts_dev, curr_rawdata); + if (r) { + cts_err("Get rawdata failed %d", r); + continue; + } else { + break; + } + mdelay(30); + } + + if (i >= 3) { + cts_err("Read rawdata failed"); + ret = -EIO; + goto disable_get_tsdata; + } + + /* Dump raw data */ + dump_test_data(cts_dev, "Noise-rawdata", curr_rawdata); + + if (unlikely(first_frame)) { + memcpy(max_rawdata, curr_rawdata, + TEST_RESULT_BUFFER_SIZE(cts_dev)); + memcpy(min_rawdata, curr_rawdata, + TEST_RESULT_BUFFER_SIZE(cts_dev)); + first_frame = false; + + continue; + } else { + for (i = 0; + i < cts_dev->fwdata.rows * cts_dev->fwdata.cols; + i++) { + if (curr_rawdata[i] > max_rawdata[i]) { + max_rawdata[i] = curr_rawdata[i]; + } else if (curr_rawdata[i] < min_rawdata[i]) { + min_rawdata[i] = curr_rawdata[i]; + } + } + } + } + + data_valid = true; + +disable_get_tsdata: + for (i = 0; i < 5; i++) { + int r = cts_disable_get_rawdata(cts_dev); + if (r) { + cts_err("Disable get rawdata failed %d", r); + continue; + } else { + break; + } + } + +unlock_device: + cts_unlock_device(cts_dev); + + if (data_valid) { + for (i = 0; i < cts_dev->fwdata.rows * cts_dev->fwdata.cols; + i++) { + noise[i] = max_rawdata[i] - min_rawdata[i]; + } + + dump_test_data(cts_dev, "Noise", noise); + + ret = + validate_test_result(cts_dev, "Noise test", noise, 0, max); + } + + kfree(curr_rawdata); + + cts_info("Noise test %s", (data_valid && (ret == 0)) ? "PASS" : "FAIL"); + + return ret; +} diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_test.h b/drivers/input/touchscreen/chipone_tddi_mmi/cts_test.h new file mode 100644 index 000000000000..3f47b5505c2c --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_test.h @@ -0,0 +1,16 @@ +#ifndef CTS_TEST_H +#define CTS_TEST_H + +struct cts_device; + +extern int cts_short_test(struct cts_device *cts_dev, u16 threshold); +extern int cts_open_test(struct cts_device *cts_dev, u16 threshold); +extern int cts_reset_test(struct cts_device *cts_dev); +extern int cts_compensate_cap_test(struct cts_device *cts_dev, u8 min_thres, + u8 max_thres); +extern int cts_rawdata_test(struct cts_device *cts_dev, u16 min_thres, + u16 max_thres); +extern int cts_noise_test(struct cts_device *cts_dev, u32 frames, u16 max); +extern int cts_test_int_pin(struct cts_device *cts_dev); + +#endif /* CTS_TEST_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_mmi/cts_tool.c b/drivers/input/touchscreen/chipone_tddi_mmi/cts_tool.c new file mode 100644 index 000000000000..59a710e48371 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_mmi/cts_tool.c @@ -0,0 +1,752 @@ +#define LOG_TAG "Tool" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_firmware.h" +#include "cts_test.h" + +#ifdef CONFIG_CTS_LEGACY_TOOL + +#pragma pack(1) +/** Tool command structure */ +struct cts_tool_cmd { + u8 cmd; + u8 flag; + u8 circle; + u8 times; + u8 retry; + u32 data_len; + u8 addr_len; + u8 addr[2]; + u8 data[PAGE_SIZE]; + +}; +#pragma pack() + +#define CTS_TOOL_CMD_HEADER_LENGTH (12) + +enum cts_tool_cmd_code { + CTS_TOOL_CMD_GET_PANEL_PARAM = 0, + CTS_TOOL_CMD_GET_DOWNLOAD_STATUS = 2, + CTS_TOOL_CMD_GET_RAW_DATA = 4, + CTS_TOOL_CMD_GET_DIFF_DATA = 6, + CTS_TOOL_CMD_READ_HOSTCOMM = 12, + CTS_TOOL_CMD_READ_ADC_STATUS = 14, + CTS_TOOL_CMD_READ_GESTURE_INFO = 16, + CTS_TOOL_CMD_READ_HOSTCOMM_MULTIBYTE = 18, + CTS_TOOL_CMD_READ_PROGRAM_MODE_MULTIBYTE = 20, + CTS_TOOL_CMD_READ_ICTYPE = 22, + CTS_TOOL_CMD_I2C_DIRECT_READ = 24, + CTS_TOOL_CMD_GET_DRIVER_INFO = 26, + + CTS_TOOL_CMD_UPDATE_PANEL_PARAM_IN_SRAM = 1, + CTS_TOOL_CMD_DOWNLOAD_FIRMWARE_WITH_FILENAME = 3, + CTS_TOOL_CMD_DOWNLOAD_FIRMWARE = 5, + CTS_TOOL_CMD_WRITE_HOSTCOMM = 11, + CTS_TOOL_CMD_WRITE_HOSTCOMM_MULTIBYTE = 15, + CTS_TOOL_CMD_WRITE_PROGRAM_MODE_MULTIBYTE = 17, + CTS_TOOL_CMD_I2C_DIRECT_WRITE = 19, + +}; + +struct cts_test_rawdata_ioctl_data { + int frames; + int min; + int max; +}; + +struct cts_test_noise_ioctl_data { + int frames; + int max; +}; + +struct cts_test_open_ioctl_data { + int min; + int max; +}; + +struct cts_test_short_ioctl_data { + int min; + int max; + bool display_on; + bool disable_gas; +}; + +struct cts_test_comp_cap_ioctl_data { + int min; + int max; +}; + +#define CTS_TOOL_IOCTL_GET_DRIVER_VERSION _IOR('C', 0x00, unsigned int) +#define CTS_TOOL_IOCTL_GET_DEVICE_TYPE _IOR('C', 0x01, unsigned int) +#define CTS_TOOL_IOCTL_GET_FW_VERSION _IOR('C', 0x02, unsigned short) + +#define CTS_TOOL_IOCTL_TEST_RESET_PIN _IO('C', 0x11) +#define CTS_TOOL_IOCTL_TEST_INT_PIN _IO('C', 0x12) +#define CTS_TOOL_IOCTL_TEST_DEVICE_TYPE _IOW('C', 0x13, unsigned int) +#define CTS_TOOL_IOCTL_TEST_FW_VERSION _IOW('C', 0x14, unsigned short) +#define CTS_TOOL_IOCTL_TEST_RAWDATA _IOW('C', 0x15, struct cts_test_rawdata_ioctl_data *) +#define CTS_TOOL_IOCTL_TEST_NOISE _IOW('C', 0x16, struct cts_test_noise_ioctl_data *) +#define CTS_TOOL_IOCTL_TEST_OPEN _IOW('C', 0x17, struct cts_test_open_ioctl_data *) +#define CTS_TOOL_IOCTL_TEST_SHORT _IOW('C', 0x18, struct cts_test_short_ioctl_data *) +#define CTS_TOOL_IOCTL_TEST_COMP_CAP _IOW('C', 0x19, struct cts_test_comp_cap_ioctl_data *) + +#define CTS_DRIVER_VERSION_CODE ((CFG_CTS_DRIVER_MAJOR_VERSION << 16) | \ + (CFG_CTS_DRIVER_MINOR_VERSION << 8) | \ + (CFG_CTS_DRIVER_PATCH_VERSION << 0)) + +static struct cts_tool_cmd cts_tool_cmd; +static char cts_tool_firmware_filepath[PATH_MAX]; +/* If CFG_CTS_MAX_I2C_XFER_SIZE < 58(PC tool length), this is neccessary */ +#ifdef CONFIG_CTS_I2C_HOST +//static u32 cts_tool_direct_access_addr = 0; +#endif /* CONFIG_CTS_I2C_HOST */ + +static int cts_tool_open(struct inode *inode, struct file *file) +{ + file->private_data = PDE_DATA(inode); + return 0; +} + +static ssize_t cts_tool_read(struct file *file, + char __user *buffer, size_t count, loff_t *ppos) +{ + struct chipone_ts_data *cts_data; + struct cts_tool_cmd *cmd; + struct cts_device *cts_dev; + int ret = 0; + + cts_data = (struct chipone_ts_data *)file->private_data; + if (cts_data == NULL) { + cts_err("Read with private_data = NULL"); + return -EIO; + } + + cmd = &cts_tool_cmd; + cts_dev = &cts_data->cts_dev; + cts_lock_device(cts_dev); + + switch (cmd->cmd) { + case CTS_TOOL_CMD_GET_PANEL_PARAM: + cts_info("Get panel param len: %u", cmd->data_len); + ret = cts_get_panel_param(cts_dev, cmd->data, cmd->data_len); + if (ret) { + cts_err("Get panel param len: %u failed %d", + cmd->data_len, ret); + } + break; + + case CTS_TOOL_CMD_GET_DOWNLOAD_STATUS: + cmd->data[0] = 100; + cts_info("Get update status = %hhu", cmd->data[0]); + break; + + case CTS_TOOL_CMD_GET_RAW_DATA: + case CTS_TOOL_CMD_GET_DIFF_DATA: + cts_dbg("Get %s data row: %u col: %u len: %u", + cmd->cmd == CTS_TOOL_CMD_GET_RAW_DATA ? "raw" : "diff", + cmd->addr[1], cmd->addr[0], cmd->data_len); + + ret = cts_enable_get_rawdata(cts_dev); + if (ret) { + cts_err("Enable read raw/diff data failed %d", ret); + break; + } + mdelay(1); + + if (cmd->cmd == CTS_TOOL_CMD_GET_RAW_DATA) { + ret = cts_get_rawdata(cts_dev, cmd->data); + } else if (cmd->cmd == CTS_TOOL_CMD_GET_DIFF_DATA) { + ret = cts_get_diffdata(cts_dev, cmd->data); + } + if (ret) { + cts_err("Get %s data failed %d", + cmd->cmd == + CTS_TOOL_CMD_GET_RAW_DATA ? "raw" : "diff", + ret); + break; + } + + ret = cts_disable_get_rawdata(cts_dev); + if (ret) { + cts_err("Disable read raw/diff data failed %d", ret); + break; + } + + break; + + case CTS_TOOL_CMD_READ_HOSTCOMM: + ret = cts_fw_reg_readb(cts_dev, + get_unaligned_le16(cmd->addr), + cmd->data); + if (ret) { + cts_err("Read firmware reg addr 0x%04x failed %d", + get_unaligned_le16(cmd->addr), ret); + } else { + cts_dbg("Read firmware reg addr 0x%04x, val=0x%02x", + get_unaligned_le16(cmd->addr), cmd->data[0]); + } + break; + +#ifdef CFG_CTS_GESTURE + case CTS_TOOL_CMD_READ_GESTURE_INFO: + ret = cts_get_gesture_info(cts_dev, cmd->data, true); + if (ret) { + cts_err("Get gesture info failed %d", ret); + } + break; +#endif /* CFG_CTS_GESTURE */ + + case CTS_TOOL_CMD_READ_HOSTCOMM_MULTIBYTE: + cmd->data_len = min((size_t) cmd->data_len, sizeof(cmd->data)); + ret = cts_fw_reg_readsb(cts_dev, get_unaligned_le16(cmd->addr), + cmd->data, cmd->data_len); + if (ret) { + cts_err + ("Read firmware reg addr 0x%04x len %u failed %d", + get_unaligned_le16(cmd->addr), cmd->data_len, ret); + } else { + cts_dbg("Read firmware reg addr 0x%04x len %u", + get_unaligned_le16(cmd->addr), cmd->data_len); + } + break; + + case CTS_TOOL_CMD_READ_PROGRAM_MODE_MULTIBYTE: + cts_dbg("Read under program mode addr 0x%06x len %u", + (cmd->flag << 16) | get_unaligned_le16(cmd->addr), + cmd->data_len); + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + break; + } + + ret = cts_sram_readsb(&cts_data->cts_dev, + get_unaligned_le16(cmd->addr), cmd->data, + cmd->data_len); + if (ret) { + cts_err("Read under program mode I2C xfer failed %d", + ret); + //break; + } + + ret = cts_enter_normal_mode(cts_dev); + if (ret) { + cts_err("Enter normal mode failed %d", ret); + break; + } + break; + + case CTS_TOOL_CMD_READ_ICTYPE: + cts_info("Get IC type"); + if (cts_dev->hwdata) { + switch (cts_dev->hwdata->hwid) { + case CTS_DEV_HWID_ICNL9911: + cmd->data[0] = 0x91; + break; + case CTS_DEV_HWID_ICNL9911S: + cmd->data[0] = 0x91; + break; + case CTS_DEV_HWID_ICNL9911C: + cmd->data[0] = 0x91; + break; + default: + cmd->data[0] = 0x00; + break; + } + } else { + cmd->data[0] = 0x10; + } + break; + +#ifdef CONFIG_CTS_I2C_HOST + case CTS_TOOL_CMD_I2C_DIRECT_READ: + { + u32 addr_width; + char *wr_buff = NULL; + u8 addr_buff[4]; + size_t left_size, max_xfer_size; + u8 *data; + + if (cmd->addr[0] != CTS_DEV_PROGRAM_MODE_I2CADDR) { + cmd->addr[0] = CTS_DEV_NORMAL_MODE_I2CADDR; + addr_width = 2; + } else { + addr_width = + cts_dev->hwdata->program_addr_width; + } + + cts_dbg + ("Direct read from i2c_addr 0x%02x addr 0x%06x size %u", + cmd->addr[0], cts_tool_direct_access_addr, + cmd->data_len); + + left_size = cmd->data_len; + max_xfer_size = + cts_plat_get_max_i2c_xfer_size(cts_dev->pdata); + data = cmd->data; + while (left_size) { + size_t xfer_size = + min(left_size, max_xfer_size); + ret = + cts_plat_i2c_read(cts_data->pdata, + cmd->addr[0], wr_buff, + addr_width, data, + xfer_size, 1, 0); + if (ret) { + cts_err + ("Direct read i2c_addr 0x%02x addr 0x%06x len %zu failed %d", + cmd->addr[0], + cts_tool_direct_access_addr, + xfer_size, ret); + break; + } + + left_size -= xfer_size; + if (left_size) { + data += xfer_size; + cts_tool_direct_access_addr += + xfer_size; + if (addr_width == 2) { + put_unaligned_be16 + (cts_tool_direct_access_addr, + addr_buff); + } else if (addr_width == 3) { + put_unaligned_be24 + (cts_tool_direct_access_addr, + addr_buff); + } + wr_buff = addr_buff; + } + } + } + break; +#endif + case CTS_TOOL_CMD_GET_DRIVER_INFO: + break; + + default: + cts_warn("Read unknown command %u", cmd->cmd); + ret = -EINVAL; + break; + } + + cts_unlock_device(cts_dev); + + if (ret == 0) { + if (cmd->cmd == CTS_TOOL_CMD_I2C_DIRECT_READ) { + ret = copy_to_user(buffer + CTS_TOOL_CMD_HEADER_LENGTH, + cmd->data, cmd->data_len); + } else { + ret = copy_to_user(buffer, cmd->data, cmd->data_len); + } + if (ret) { + cts_err("Copy data to user buffer failed %d", ret); + return 0; + } + + return cmd->data_len; + } + + return 0; +} + +static ssize_t cts_tool_write(struct file *file, + const char __user *buffer, size_t count, + loff_t *ppos) +{ + struct chipone_ts_data *cts_data; + struct cts_device *cts_dev; + struct cts_tool_cmd *cmd; + int ret = 0; + + if (count < CTS_TOOL_CMD_HEADER_LENGTH || count > PAGE_SIZE) { + cts_err("Write len %zu invalid", count); + return -EFAULT; + } + + cts_data = (struct chipone_ts_data *)file->private_data; + if (cts_data == NULL) { + cts_err("Write with private_data = NULL"); + return -EIO; + } + + cmd = &cts_tool_cmd; + ret = copy_from_user(cmd, buffer, CTS_TOOL_CMD_HEADER_LENGTH); + if (ret) { + cts_err("Copy command header from user buffer failed %d", ret); + return -EIO; + } else { + ret = CTS_TOOL_CMD_HEADER_LENGTH; + } + + if (cmd->data_len > PAGE_SIZE) { + cts_err("Write with invalid count %d", cmd->data_len); + return -EIO; + } + + if (cmd->cmd & BIT(0)) { + if (cmd->data_len) { + ret = copy_from_user(cmd->data, + buffer + + CTS_TOOL_CMD_HEADER_LENGTH, + cmd->data_len); + if (ret) { + cts_err + ("Copy command payload from user buffer len %u failed %d", + cmd->data_len, ret); + return -EIO; + } + } + } else { + /* Prepare for read command */ + cts_dbg("Write read command(%d) header, prepare read size: %d", + cmd->cmd, cmd->data_len); + return CTS_TOOL_CMD_HEADER_LENGTH + cmd->data_len; + } + + cts_dev = &cts_data->cts_dev; + cts_lock_device(cts_dev); + + switch (cmd->cmd) { + case CTS_TOOL_CMD_UPDATE_PANEL_PARAM_IN_SRAM: + cts_info("Write panel param len %u data\n", cmd->data_len); + ret = cts_fw_reg_writesb(cts_dev, CTS_DEVICE_FW_REG_PANEL_PARAM, + cmd->data, cmd->data_len); + if (ret) { + cts_err("Write panel param failed %d", ret); + break; + } + + ret = cts_send_command(cts_dev, CTS_CMD_RESET); + if (ret) { + + } + + ret = cts_set_work_mode(cts_dev, 1); + if (ret) { + + } + + mdelay(100); + + ret = cts_set_work_mode(cts_dev, 0); + if (ret) { + + } + mdelay(100); + + break; + + case CTS_TOOL_CMD_DOWNLOAD_FIRMWARE_WITH_FILENAME: + cts_info("Write firmware path: '%.*s'", + cmd->data_len, cmd->data); + + memcpy(cts_tool_firmware_filepath, cmd->data, cmd->data_len); + cts_tool_firmware_filepath[cmd->data_len] = '\0'; + break; + + case CTS_TOOL_CMD_DOWNLOAD_FIRMWARE: + cts_info("Start download firmware path: '%s'", + cts_tool_firmware_filepath); + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + break; + } + // TODO: Use async mode such as thread, otherwise, host can not get update status. + ret = + cts_update_firmware_from_file(cts_dev, + cts_tool_firmware_filepath, + true); + if (ret) { + cts_err("Updata firmware failed %d", ret); + //break; + } + + ret = cts_start_device(cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + break; + } + break; + + case CTS_TOOL_CMD_WRITE_HOSTCOMM: + cts_dbg("Write firmware reg addr: 0x%04x val=0x%02x", + get_unaligned_le16(cmd->addr), cmd->data[0]); + + ret = cts_fw_reg_writeb(cts_dev, + get_unaligned_le16(cmd->addr), + cmd->data[0]); + if (ret) { + cts_err + ("Write firmware reg addr: 0x%04x val=0x%02x failed %d", + get_unaligned_le16(cmd->addr), cmd->data[0], ret); + } + break; + + case CTS_TOOL_CMD_WRITE_HOSTCOMM_MULTIBYTE: + cts_dbg("Write firmare reg addr: 0x%04x len %u", + get_unaligned_le16(cmd->addr), cmd->data_len); + ret = cts_fw_reg_writesb(cts_dev, get_unaligned_le16(cmd->addr), + cmd->data, cmd->data_len); + if (ret) { + cts_err + ("Write firmare reg addr: 0x%04x len %u failed %d", + get_unaligned_le16(cmd->addr), cmd->data_len, ret); + } + break; + + case CTS_TOOL_CMD_WRITE_PROGRAM_MODE_MULTIBYTE: + cts_dbg("Write to addr 0x%06x size %u under program mode", + (cmd->flag << 16) | (cmd->addr[1] << 8) | cmd->addr[0], + cmd->data_len); + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + break; + } + + ret = cts_sram_writesb(cts_dev, + (cmd->flag << 16) | (cmd->addr[1] << 8) | + cmd->addr[0], cmd->data, cmd->data_len); + if (ret) { + cts_err("Write program mode multibyte failed %d", ret); + //break; + } + + ret = cts_enter_normal_mode(cts_dev); + if (ret) { + cts_err("Enter normal mode failed %d", ret); + break; + } + + break; + +#ifdef CONFIG_CTS_I2C_HOST + case CTS_TOOL_CMD_I2C_DIRECT_WRITE: + { + u32 addr_width; + size_t left_payload_size; /* Payload exclude address field */ + size_t max_xfer_size; + char *payload; + + if (cmd->addr[0] != CTS_DEV_PROGRAM_MODE_I2CADDR) { + cmd->addr[0] = CTS_DEV_NORMAL_MODE_I2CADDR; + addr_width = 2; + cts_tool_direct_access_addr = + get_unaligned_be16(cmd->data); + } else { + addr_width = + cts_dev->hwdata->program_addr_width; + cts_tool_direct_access_addr = + get_unaligned_be24(cmd->data); + } + + if (cmd->data_len < addr_width) { + cts_err + ("Direct write too short %d < address width %d", + cmd->data_len, addr_width); + ret = -EINVAL; + break; + } + + cts_dbg + ("Direct write to i2c_addr 0x%02x addr 0x%06x size %u", + cmd->addr[0], cts_tool_direct_access_addr, + cmd->data_len); + + left_payload_size = cmd->data_len - addr_width; + max_xfer_size = + cts_plat_get_max_i2c_xfer_size(cts_dev->pdata); + payload = cmd->data + addr_width; + do { + size_t xfer_payload_size = + min(left_payload_size, + max_xfer_size - addr_width); + size_t xfer_len = + xfer_payload_size + addr_width; + + ret = + cts_plat_i2c_write(cts_data->pdata, + cmd->addr[0], + payload - addr_width, + xfer_len, 1, 0); + if (ret) { + cts_err + ("Direct write i2c_addr 0x%02x addr 0x%06x len %zu failed %d", + cmd->addr[0], + cts_tool_direct_access_addr, + xfer_len, ret); + break; + } + + left_payload_size -= xfer_payload_size; + if (left_payload_size) { + payload += xfer_payload_size; + cts_tool_direct_access_addr += + xfer_payload_size; + if (addr_width == 2) { + put_unaligned_be16 + (cts_tool_direct_access_addr, + payload - addr_width); + } else if (addr_width == 3) { + put_unaligned_be24 + (cts_tool_direct_access_addr, + payload - addr_width); + } + } + } while (left_payload_size); + } + break; +#endif + default: + cts_warn("Write unknown command %u", cmd->cmd); + ret = -EINVAL; + break; + } + + cts_unlock_device(cts_dev); + + return ret ? 0 : cmd->data_len + CTS_TOOL_CMD_HEADER_LENGTH; +} + +static long cts_tool_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct chipone_ts_data *cts_data; + struct cts_device *cts_dev; + + cts_info("ioctl, cmd=0x%04x, arg=0x%02lx", cmd, arg); + + cts_data = file->private_data; + if (cts_data == NULL) { + cts_err("IOCTL with private data = NULL"); + return -EFAULT; + } + + cts_dev = &cts_data->cts_dev; + + switch (cmd) { + case CTS_TOOL_IOCTL_GET_DRIVER_VERSION: + return put_user(CTS_DRIVER_VERSION_CODE, + (unsigned int __user *)arg); + case CTS_TOOL_IOCTL_GET_DEVICE_TYPE: + return put_user(cts_dev->hwdata->hwid, + (unsigned int __user *)arg); + case CTS_TOOL_IOCTL_GET_FW_VERSION: + return put_user(cts_dev->fwdata.version, + (unsigned short __user *)arg); + + case CTS_TOOL_IOCTL_TEST_RESET_PIN:{ + return cts_reset_test(cts_dev); + } + case CTS_TOOL_IOCTL_TEST_INT_PIN:{ + return cts_test_int_pin(cts_dev); + } + case CTS_TOOL_IOCTL_TEST_DEVICE_TYPE:{ + if (cts_dev->hwdata->hwid == (u32) arg) { + return 0; + } + return -EINVAL; + } + case CTS_TOOL_IOCTL_TEST_FW_VERSION:{ + if (cts_dev->fwdata.version == (u16) arg) { + return 0; + } + return -EINVAL; + } + case CTS_TOOL_IOCTL_TEST_RAWDATA:{ + struct cts_test_rawdata_ioctl_data param; + + if (copy_from_user(¶m, + (struct cts_test_rawdata_ioctl_data + __user *)arg, sizeof(param))) + return -EFAULT; + + return cts_rawdata_test(cts_dev, param.min, param.max); + } + case CTS_TOOL_IOCTL_TEST_NOISE:{ + struct cts_test_noise_ioctl_data param; + + if (copy_from_user(¶m, + (struct cts_test_noise_ioctl_data + __user *)arg, sizeof(param))) + return -EFAULT; + + return cts_noise_test(cts_dev, param.frames, param.max); + } + case CTS_TOOL_IOCTL_TEST_OPEN:{ + struct cts_test_open_ioctl_data param; + + if (copy_from_user(¶m, + (struct cts_test_open_ioctl_data + __user *)arg, sizeof(param))) + return -EFAULT; + + return cts_open_test(cts_dev, param.max); + } + case CTS_TOOL_IOCTL_TEST_SHORT:{ + struct cts_test_short_ioctl_data param; + + if (copy_from_user(¶m, + (struct cts_test_short_ioctl_data + __user *)arg, sizeof(param))) + return -EFAULT; + + return cts_short_test(cts_dev, param.max); + } + case CTS_TOOL_IOCTL_TEST_COMP_CAP:{ + struct cts_test_comp_cap_ioctl_data param; + + if (copy_from_user(¶m, + (struct cts_test_comp_cap_ioctl_data + __user *)arg, sizeof(param))) + return -EFAULT; + + return cts_compensate_cap_test(cts_dev, param.min, + param.max); + } + + default: + break; + } + + return -ENOTSUPP; +} + +static struct file_operations cts_tool_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = cts_tool_open, + .read = cts_tool_read, + .write = cts_tool_write, + .unlocked_ioctl = cts_tool_ioctl, +}; + +int cts_tool_init(struct chipone_ts_data *cts_data) +{ + cts_info("Init"); + + cts_data->procfs_entry = proc_create_data(CFG_CTS_TOOL_PROC_FILENAME, + 0666, NULL, &cts_tool_fops, + cts_data); + if (cts_data->procfs_entry == NULL) { + cts_err("Create proc entry failed"); + return -EFAULT; + } + + return 0; +} + +void cts_tool_deinit(struct chipone_ts_data *data) +{ + cts_info("Deinit"); + + if (data->procfs_entry) { + remove_proc_entry(CFG_CTS_TOOL_PROC_FILENAME, NULL); + } +} +#endif /* CONFIG_CTS_LEGACY_TOOL */ diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/Android.mk b/drivers/input/touchscreen/chipone_tddi_v2_mmi/Android.mk new file mode 100644 index 000000000000..7bb564cfea0b --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/Android.mk @@ -0,0 +1,74 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +CHIPONE_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +CHIPONE_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +ifeq ($(CHIPONE_ESD),true) + KERNEL_CFLAGS += CONFIG_CHIPONE_ESD=y + KBUILD_OPTIONS += CONFIG_CHIPONE_ESD=y +endif + +ifneq ($(BOARD_USES_DOUBLE_TAP),) + KERNEL_CFLAGS += CONFIG_INPUT_CHIPONE_0FLASH_MMI_ENABLE_DOUBLE_TAP=y + KBUILD_OPTIONS += CONFIG_INPUT_CHIPONE_0FLASH_MMI_ENABLE_DOUBLE_TAP=y +endif + +ifneq ($(BOARD_USES_PANEL_NOTIFICATIONS),) + KERNEL_CFLAGS += CONFIG_INPUT_CHIPONE_PANEL_NOTIFICATIONS=y + KBUILD_OPTIONS += CONFIG_INPUT_CHIPONE_0FLASH_MMI_ENABLE_DOUBLE_TAP=y +endif + +ifneq ($(MOTO_PANEL_CHECK_TOUCH_STATE),) + KERNEL_CFLAGS += CONFIG_INPUT_CHIPONE_0FLASH_MMI_NOTIFY_TOUCH_STATE=y + KBUILD_OPTIONS += CONFIG_INPUT_CHIPONE_0FLASH_MMI_NOTIFY_TOUCH_STATE=y +endif + +ifeq ($(TOUCHSCREEN_LAST_TIME),true) + KERNEL_CFLAGS += CONFIG_GTP_LAST_TIME=y + KBUILD_OPTIONS += CONFIG_GTP_LAST_TIME=y +endif + +ifeq ($(CONFIG_STOWED_MODE_EN),true) + KERNEL_CFLAGS += CONFIG_STOWED_MODE_EN=y +endif + +ifeq ($(BOARD_USES_DOUBLE_TAP_CTRL),true) + KERNEL_CFLAGS += CONFIG_BOARD_USES_DOUBLE_TAP_CTRL=y + KBUILD_OPTIONS += CONFIG_BOARD_USES_DOUBLE_TAP_CTRL=y +endif + +ifneq ($(findstring mmi_info.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KBUILD_OPTIONS += CONFIG_INPUT_TOUCHSCREEN_MMI=y +endif + +ifeq ($(TOUCHSCREEN_PM_BRL_SPI),true) + KERNEL_CFLAGS += TOUCHSCREEN_PM_BRL_SPI=y + KBUILD_OPTIONS += TOUCHSCREEN_PM_BRL_SPI=y +endif + +ifeq ($(CONFIG_INPUT_CHECK_DEVICE_BOOTMODE),true) + KERNEL_CFLAGS += CONFIG_CHIPONE_CHECK_DEVICE_BOOTMODE=y +endif + +include $(CLEAR_VARS) +ifneq ($(BOARD_USES_DOUBLE_TAP),) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/sensors_class.ko +endif +LOCAL_MODULE := chipone_tddi_v2_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(CHIPONE_MMI_MODULE_PATH) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/mmi_info.ko +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KBUILD_OPTIONS += CONFIG_INPUT_TOUCHSCREEN_MMI=y + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +endif +ifneq ($(MOTO_PANEL_CHECK_TOUCH_STATE_V2),) + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/msm_drm.ko +endif +KBUILD_OPTIONS += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/Kbuild b/drivers/input/touchscreen/chipone_tddi_v2_mmi/Kbuild new file mode 100644 index 000000000000..d2defc3198e9 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/Kbuild @@ -0,0 +1,70 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/chipone_tddi_fhd_mmi +ifneq ($(filter m y,$(CONFIG_CHIPONE_ESD)),) +EXTRA_CFLAGS += -DCONFIG_CTS_ESD_PROTECTION +endif + +#EXTRA_CFLAGS += -DCONFIG_CTS_I2C_HOST + +ifneq ($(filter m y,$(CONFIG_INPUT_CHIPONE_0FLASH_MMI_ENABLE_DOUBLE_TAP)),) + EXTRA_CFLAGS += -DCHIPONE_SENSOR_EN + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_CHIPONE_0FLASH_MMI_NOTIFY_TOUCH_STATE)),) + EXTRA_CFLAGS += -DCHIPONE_SET_TOUCH_STATE +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_CHIPONE_PANEL_NOTIFICATIONS)),) +EXTRA_CFLAGS += -DCHIPONE_CONFIG_PANEL_NOTIFICATIONS +endif + +ifneq ($(filter m y,$(CONFIG_GTP_LAST_TIME)),) + EXTRA_CFLAGS += -DCONFIG_GTP_LAST_TIME +endif + +ifneq ($(filter m y, $(CONFIG_BOARD_USES_DOUBLE_TAP_CTRL)),) + EXTRA_CFLAGS += -DCONFIG_BOARD_USES_DOUBLE_TAP_CTRL +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI +endif + +ifeq ($(TARGET_BUILD_VARIANT),user) + EXTRA_CFLAGS += -DCFG_CTS_FOR_GKI +endif + +ifneq ($(filter m y, $(CONFIG_STOWED_MODE_EN)),) + EXTRA_CFLAGS += -DCTS_STOWED_MODE_EN +endif + +ifneq ($(filter m y,$(TOUCHSCREEN_PM_BRL_SPI)),) + EXTRA_CFLAGS += -DTOUCHSCREEN_PM_BRL_SPI +endif + +ifneq ($(filter m y,$(CONFIG_CHIPONE_CHECK_DEVICE_BOOTMODE)),) + EXTRA_CFLAGS += -DCTS_CHECK_DEVICE_BOOTMODE +endif + +#obj-$(CONFIG_TOUCHSCREEN_CHIPONE) += chipone-tddi.o +obj-m += chipone_tddi_v2_mmi.o + +chipone_tddi_v2_mmi-objs := cts_driver.o +chipone_tddi_v2_mmi-objs += cts_core.o +chipone_tddi_v2_mmi-objs += cts_sfctrlv2.o +chipone_tddi_v2_mmi-objs += cts_spi_flash.o +chipone_tddi_v2_mmi-objs += cts_firmware.o +chipone_tddi_v2_mmi-objs += cts_test.o +chipone_tddi_v2_mmi-objs += cts_charger_detect.o +chipone_tddi_v2_mmi-objs += cts_earjack_detect.o +chipone_tddi_v2_mmi-objs += cts_tcs.o +chipone_tddi_v2_mmi-objs += cts_platform.o +chipone_tddi_v2_mmi-objs += cts_tool.o +chipone_tddi_v2_mmi-objs += cts_sysfs.o +chipone_tddi_v2_mmi-objs += cts_strerror.o +chipone_tddi_v2_mmi-objs += cts_oem.o +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/Makefile b/drivers/input/touchscreen/chipone_tddi_v2_mmi/Makefile new file mode 100644 index 000000000000..848165238487 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/Makefile @@ -0,0 +1,37 @@ +ifeq ($(strip $(MODULE_KERNEL_VERSION)), 5.15) +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(M)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(M)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(OUT_DIR)/../../vendor/qcom/opensource/display-drivers/msm/Module.symvers + +else + +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + +KBUILD_EXTRA_SYMBOLS += $(M)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(M)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers + +endif \ No newline at end of file diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_builtin_firmware.h b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_builtin_firmware.h new file mode 100644 index 000000000000..40b4085a2754 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_builtin_firmware.h @@ -0,0 +1,23 @@ +static u8 icnl9916_driver_builtin_firmware[] = { +}; + +static u8 icnl9916c_driver_builtin_firmware[] = { +}; + +const static struct cts_firmware cts_driver_builtin_firmwares[] = { + { + .name = "OEM-Project", /* MUST set non-NULL */ + .hwid = CTS_DEV_HWID_ICNL9916, + .fwid = CTS_DEV_FWID_ICNL9916, + .data = icnl9916_driver_builtin_firmware, + .size = ARRAY_SIZE(icnl9916_driver_builtin_firmware), + }, + { + .name = "OEM-Project", /* MUST set non-NULL */ + .hwid = CTS_DEV_HWID_ICNL9916C, + .fwid = CTS_DEV_FWID_ICNL9916C, + .data = icnl9916c_driver_builtin_firmware, + .size = ARRAY_SIZE(icnl9916c_driver_builtin_firmware), + }, + +}; diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_charger_detect.c b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_charger_detect.c new file mode 100644 index 000000000000..a2f5e6dccf6f --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_charger_detect.c @@ -0,0 +1,1025 @@ +#define LOG_TAG "Charger" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sysfs.h" + +#ifdef CONFIG_CTS_CHARGER_DETECT + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) +/** + * match_string - matches given string in an array + * @array: array of strings + * @n: number of strings in the array or -1 for NULL terminated arrays + * @string: string to match with + * + * Return: + * index of a @string in the @array if matches, or %-EINVAL otherwise. + */ +int match_string(const char *const *array, size_t n, const char *string) +{ + int index; + const char *item; + + for (index = 0; index < n; index++) { + item = array[index]; + if (!item) + break; + if (!strcmp(item, string)) + return index; + } + + return -EINVAL; +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +#define CFG_CTS_CHARGER_DETECT_PSY_NOTIFY +#endif +#define CFG_CTS_CHARGER_DETECT_PSY_POLL + +#include +#include + +enum cts_charger_detect_type { + CTS_CHGR_DET_TYPE_NONE = 0, + CTS_CHGR_DET_TYPE_PSY_NOTIFY, + CTS_CHGR_DET_TYPE_POLL_PSP, + CTS_CHGR_DET_TYPE_MAX +}; + +/* Over-written setting for DTS */ +/* #define CFG_CTS_DEF_CHGR_DET_ENABLE */ + +/* Default settings */ +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY +#define CFG_CTS_DEF_CHGR_DET_TYPE CTS_CHGR_DET_TYPE_PSY_NOTIFY +#else /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ +#define CFG_CTS_DEF_CHGR_DET_TYPE CTS_CHGR_DET_TYPE_POLL_PSP +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#define CFG_CTS_DEF_CHGR_DET_PSY_NAME "usb" +#define CFG_CTS_DEF_CHGR_DET_PSY_PROP POWER_SUPPLY_PROP_PRESENT +#define CFG_CTS_DEF_CHGR_DET_PSP_POLL_INTERVAL 2000u + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) +/* Lower version has no power_supply_get_property() */ +#define POWER_SUPPLY_GET_PROPERTY(psy, psp, val) \ + power_supply_get_property(psy, psp, val) +#else +#define POWER_SUPPLY_GET_PROPERTY(psy, psp, val) \ + psy->get_property(psy, psp, val) +#endif + +struct cts_charger_detect_data { + bool enable; + bool running; + bool state; + + /* Parameter */ + enum cts_charger_detect_type type; + const char *psy_name; + enum power_supply_property psp; + u32 psp_poll_interval; /* Unit is ms */ + + /* Power supply notify */ +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY + bool notifier_registered; + struct notifier_block psy_notifier; +#endif + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL + /* Polling power supply property */ + struct delayed_work psp_poll_work; +#endif + + struct work_struct set_charger_state_work; + + /* Sysfs */ +#ifdef CONFIG_CTS_SYSFS + bool sysfs_created; +#endif + + struct chipone_ts_data *cts_data; + +}; + +#define POWER_SUPPLY_PROP_PREFIX "POWER_SUPPLY_PROP_" +#define POWER_SUPPLY_PROP_TOKEN(property) \ + {.prop = POWER_SUPPLY_PROP_ ##property, \ + .name = POWER_SUPPLY_PROP_PREFIX #property} + +const static struct { + enum power_supply_property prop; + const char *name; +} power_supply_prop_token[] = { + POWER_SUPPLY_PROP_TOKEN(STATUS), + POWER_SUPPLY_PROP_TOKEN(PRESENT), + POWER_SUPPLY_PROP_TOKEN(ONLINE), +}; + +static const char *power_supply_prop_str(enum power_supply_property prop) +{ + int i; + + for (i = 0; i < sizeof(power_supply_prop_token); i++) { + if (prop == power_supply_prop_token[i].prop) + return power_supply_prop_token[i].name; + } + + return "Unknown"; +} + +static enum power_supply_property power_supply_prop_from_name(const char *name) +{ + size_t offset = strlen(POWER_SUPPLY_PROP_PREFIX); + int i; + + for (i = 0; i < ARRAY_SIZE(power_supply_prop_token); i++) { + if (strcasecmp(power_supply_prop_token[i].name + offset, name) == 0 + || strcasecmp(power_supply_prop_token[i].name, name) == 0) { + return power_supply_prop_token[i].prop; + } + } + + return -ENOENT; +} + +static const char *charger_detect_type_text[] = { + "none", "notify", "poll", +}; + +static const char *charger_detect_type_str(enum cts_charger_detect_type type) +{ + return type < ARRAY_SIZE(charger_detect_type_text) ? + charger_detect_type_text[type] : "Unknown"; +} + +static int parse_charger_detect_dt(struct cts_charger_detect_data *cd_data, + struct device_node *np) +{ + const char *psy_name; + const char *type_str; + const char *psp_str; + int ret; + + cts_info("Parse dt"); + +#ifdef CFG_CTS_DEF_CHGR_DET_ENABLE + cd_data->enable = true; +#else /* CFG_CTS_DEF_CHGR_DET_ENABLE */ + cd_data->enable = + of_property_read_bool(np, "chipone,touch-charger-detect-enable"); +#endif /* CFG_CTS_DEF_CHGR_DET_ENABLE */ + + cd_data->type = CFG_CTS_DEF_CHGR_DET_TYPE; + ret = of_property_read_string(np, + "chipone,touch-charger-detect-type", &type_str); + if (ret) + cts_warn("Parse detect type failed %d", ret); + else { + int type = match_string(charger_detect_type_text, + ARRAY_SIZE(charger_detect_type_text), + type_str); + if (type < 0) + cts_err("Parse detect type '%s' invalid", type_str); + else + cd_data->type = type; + } + + ret = of_property_read_string(np, "chipone,touch-charger-detect-psy-name", + &psy_name); + if (ret) { + cts_warn("Parse detect psy name failed %d", ret); + psy_name = CFG_CTS_DEF_CHGR_DET_PSY_NAME; + } else { + if (power_supply_get_by_name(psy_name) == NULL) { + cts_warn("Power supply '%s' not found", psy_name); + psy_name = CFG_CTS_DEF_CHGR_DET_PSY_NAME; + } + } + cd_data->psy_name = kstrdup(psy_name, GFP_KERNEL); + if (cd_data->psy_name == NULL) { + cts_err("Alloc mem for psy name failed"); + return -ENOMEM; + } + + cd_data->psp = CFG_CTS_DEF_CHGR_DET_PSY_PROP; + ret = of_property_read_string(np, "chipone,touch-charger-detect-psp", + &psp_str); + if (ret) { + cts_warn("Parse detect psp failed %d", ret); + } else { + struct power_supply *psy; + enum power_supply_property psp; + union power_supply_propval val; + + psp = power_supply_prop_from_name(psp_str); + if (psp < 0) { + cts_warn("Parse detect psp: '%s' invalid", psp_str); + } else { + psy = power_supply_get_by_name(cd_data->psy_name); + if (psy != NULL && POWER_SUPPLY_GET_PROPERTY(psy, psp, &val) >= 0) { + cts_err("Parse detect psp invalid"); + } else { + cd_data->psp = psp; + } + } + } + + cd_data->psp_poll_interval = CFG_CTS_DEF_CHGR_DET_PSP_POLL_INTERVAL; + ret = of_property_read_u32(np, + "chipone,touch-charger-detect-psp-poll-interval", + &cd_data->psp_poll_interval); + if (ret) + cts_warn("Parse detect psp poll interval failed %d", ret); + + return 0; +} + +static int start_charger_detect(struct cts_charger_detect_data *cd_data) +{ + if (!cd_data->enable) { + cts_warn("Start detect while NOT enabled"); + return -EINVAL; + } + + if (cd_data->running) { + cts_warn("Start detect while already RUNNING"); + return 0; + } + + cts_info("Start detect type: %d(%s)", + cd_data->type, charger_detect_type_str(cd_data->type)); + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY + if (cd_data->type == CTS_CHGR_DET_TYPE_PSY_NOTIFY) { + int ret = power_supply_reg_notifier(&cd_data->psy_notifier); + + if (ret) { + cts_err("Register notifier failed: %d", ret); + return ret; + } + cd_data->notifier_registered = true; + + cd_data->running = true; + + return 0; + } +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL + if (cd_data->type == CTS_CHGR_DET_TYPE_POLL_PSP) { + if (!queue_delayed_work(cd_data->cts_data->workqueue, + &cd_data->psp_poll_work, + msecs_to_jiffies(cd_data->psp_poll_interval))) { + cts_warn("Queue detect work while already on the queue"); + } + cd_data->running = true; + + return 0; + } +#endif /* CFG_CTS_CHARGER_DETECT_PSY_POLL */ + + return -ENOTSUPP; +} + +static int stop_charger_detect(struct cts_charger_detect_data *cd_data) +{ + if (!cd_data->running) { + cts_warn("Stop detect while NOT running"); + return 0; + } + + cts_info("Stop detect type: %d(%s)", cd_data->type, + charger_detect_type_str(cd_data->type)); + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY + if (cd_data->type == CTS_CHGR_DET_TYPE_PSY_NOTIFY) { + if (cd_data->notifier_registered) { + power_supply_unreg_notifier(&cd_data->psy_notifier); + cd_data->notifier_registered = false; + } + cd_data->running = false; + + return 0; + } +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL + if (cd_data->type == CTS_CHGR_DET_TYPE_POLL_PSP) { + if (!cancel_delayed_work_sync(&cd_data->psp_poll_work)) + cts_warn("Cancel poll psp work while NOT pending"); + + cd_data->running = false; + + return 0; + } +#endif /* CFG_CTS_CHARGER_DETECT_PSY_POLL */ + + return 0; +} + +static int enable_charger_detect(struct cts_charger_detect_data *cd_data) +{ + cts_info("Enable detect type: %d(%s)", cd_data->type, + charger_detect_type_str(cd_data->type)); + + cd_data->enable = true; + + return 0; +} + +static int disable_charger_detect(struct cts_charger_detect_data *cd_data) +{ + int ret; + + cts_info("Disable detect type: %d(%s)", cd_data->type, + charger_detect_type_str(cd_data->type)); + + ret = stop_charger_detect(cd_data); + if (ret) + cts_err("Disable detect failed %d", ret); + + cd_data->enable = false; + + return 0; +} + +static int get_charger_state(struct cts_charger_detect_data *cd_data) +{ + struct power_supply *psy; + union power_supply_propval propval; + int ret; + + cts_dbg("Get state from psy: '%s' prop: %d(%s)", + cd_data->psy_name, cd_data->psp, + power_supply_prop_str(cd_data->psp)); + + psy = power_supply_get_by_name(cd_data->psy_name); + if (cd_data->psy_name == NULL || psy == NULL) { + cts_err("Get state from psy: '%s' not found", cd_data->psy_name); + return -EINVAL; + } + + ret = POWER_SUPPLY_GET_PROPERTY(psy, cd_data->psp, &propval); + if (ret < 0) { + cts_err("Get state from psy: '%s' prop: %d(%s) failed", + cd_data->psy_name, cd_data->psp, + power_supply_prop_str(cd_data->psp)); + return -EINVAL; + } + + /* ONLY for bool type */ + cd_data->state = !!propval.intval; + + cts_dbg("State: %s", cd_data->state ? "ATTACHED" : "DETACHED"); + + return 0; +} + +static int switch_charger_detect_type(struct cts_charger_detect_data *cd_data, + enum cts_charger_detect_type type) +{ + bool running; + + cts_info("Switch detect type to %d(%s)", + type, charger_detect_type_str(type)); + + if (type >= CTS_CHGR_DET_TYPE_MAX) { + cts_err("Switch detect type %d invalid", type); + return -EINVAL; + } + + if (cd_data->type == type) { + cts_warn("Switch detect type equal"); + return 0; + } + + running = cd_data->running; + + if (running) { + int ret = stop_charger_detect(cd_data); + if (ret) { + cts_err("Stop detect failed %d", ret); + return ret; + } + } + + cd_data->type = type; + + if (running) { + int ret = start_charger_detect(cd_data); + if (ret) { + cts_err("Start detect failed %d", ret); + cd_data->type = CTS_CHGR_DET_TYPE_NONE; + return ret; + } + } + + return 0; +} + +static void set_dev_charger_state_work(struct work_struct *work) +{ + struct cts_charger_detect_data *cd_data; + int ret; + + cd_data = container_of(work, struct cts_charger_detect_data, + set_charger_state_work); + + cts_lock_device(&cd_data->cts_data->cts_dev); + ret = cts_set_dev_charger_attached(&cd_data->cts_data->cts_dev, + cd_data->state); + cts_unlock_device(&cd_data->cts_data->cts_dev); + if (ret) { + cts_err("Set dev charger attached to %s failed %d", + cd_data->state ? "ATTACHED" : "DETATCHED", ret); + /* Set to previous state, try set again in next loop */ + cd_data->state = !cd_data->state; + } +} + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY +static int psy_notify_callback(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct cts_charger_detect_data *cd_data; + struct power_supply *psy; + bool prev_state; + int ret; + + if (nb == NULL || data == NULL) { + cts_err("PSY notify callback with notifier %p or data %p = NULL", + nb, data); + return NOTIFY_DONE; + } + + if (action != PSY_EVENT_PROP_CHANGED) { + cts_dbg("Notify event %ld not care", action); + return NOTIFY_DONE; + } + + cd_data = container_of(nb, struct cts_charger_detect_data, psy_notifier); + + psy = (struct power_supply *)data; + if (strcmp(psy->desc->name, cd_data->psy_name) != 0) { + cts_dbg("Notify from power supply '%s' not care", psy->desc->name); + return NOTIFY_DONE; + } + + prev_state = cd_data->state; + + ret = get_charger_state(cd_data); + if (ret < 0) { + cts_err("Get state failed %d", ret); + return NOTIFY_DONE; + } + + cts_info("State changed: %s -> %s", prev_state ? "ATTACHED" : "DETACHED", + cd_data->state ? "ATTACHED" : "DETACHED"); + + if (!queue_work(cd_data->cts_data->workqueue, + &cd_data->set_charger_state_work)) { + cts_warn("Set device charger state work is PENDING"); + } + + return NOTIFY_OK; +} +#endif /* CFG_CTS_CHARGER_DETECT_PSY_NOTIFY */ + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL +static void poll_psp_work(struct work_struct *work) +{ + struct cts_charger_detect_data *cd_data; + bool prev_state; + int ret; + + cts_dbg("Poll psp work"); + + cd_data = container_of(to_delayed_work(work), + struct cts_charger_detect_data, psp_poll_work); + + prev_state = cd_data->state; + + ret = get_charger_state(cd_data); + if (ret < 0) { + cts_err("Get state failed %d", ret); + } else { + if (cd_data->state != prev_state) { + cts_info("State changed: %s -> %s", + prev_state ? "ATTACHED" : "DETACHED", + cd_data->state ? "ATTACHED" : "DETACHED"); + + if (!queue_work(cd_data->cts_data->workqueue, + &cd_data->set_charger_state_work)) { + cts_warn("Set device charger state work is PENDING"); + } + } + } + + if (!queue_delayed_work(cd_data->cts_data->workqueue, + &cd_data->psp_poll_work, + msecs_to_jiffies(cd_data->psp_poll_interval))) { + cts_warn("Queue detect work while already on the queue"); + } +} +#endif /* CFG_CTS_CHARGER_DETECT_PSY_POLL */ + +static int init_charger_detect(struct cts_charger_detect_data *cd_data) +{ + int ret; + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_NOTIFY + cd_data->psy_notifier.notifier_call = psy_notify_callback; +#endif + +#ifdef CFG_CTS_CHARGER_DETECT_PSY_POLL + INIT_DELAYED_WORK(&cd_data->psp_poll_work, poll_psp_work); +#endif + + INIT_WORK(&cd_data->set_charger_state_work, set_dev_charger_state_work); + + ret = get_charger_state(cd_data); + if (ret) + cts_err("Get state failed %d", ret); + + return 0; +} + +/* Sysfs */ +#ifdef CONFIG_CTS_SYSFS +extern int argc; +extern char *argv[]; +extern int parse_arg(const char *buf, size_t count); + +#define CHARGER_DET_SYSFS_GROUP_NAME "charger-det" + +static ssize_t charger_detect_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + + cts_info("Read sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s'", + attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, "Charger detect: %s\n", + cd_data->enable ? "ENABLED" : "DISABLED"); +} + +/* Echo 0/n/N/of/Of/Of/OF/1/y/Y/on/oN/On/ON > enable */ +static ssize_t charger_detect_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + bool enable; + int ret; + + cts_info("Write sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num of args"); + return -EINVAL; + } + + ret = kstrtobool(argv[0], &enable); + if (ret) { + cts_err("Invalid param of enable"); + return ret; + } + + if (enable) + ret = enable_charger_detect(cd_data); + else + ret = disable_charger_detect(cd_data); + + if (ret) { + cts_err("%s charger detect failed %d", + enable ? "Enable" : "Disable", ret); + return ret; + } + + return count; +} + +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + charger_detect_enable_show, charger_detect_enable_store); + +static ssize_t charger_detect_running_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + + cts_info("Read sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s'", + attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, "Charger detect: %sRunning\n", + cd_data->running ? "" : "Not-"); +} + +/* Echo 0/n/N/of/Of/Of/OF/1/y/Y/on/oN/On/ON > runing */ +static ssize_t charger_detect_running_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + bool running; + int ret; + + cts_info("Write sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num of args"); + return -EINVAL; + } + + ret = kstrtobool(argv[0], &running); + if (ret) { + cts_err("Invalid param of running"); + return ret; + } + + if (running) + ret = start_charger_detect(cd_data); + else + ret = stop_charger_detect(cd_data); + + if (ret) { + cts_err("%s charger detect failed %d", running ? "Start" : "Stop", ret); + return ret; + } + + return count; +} + +static DEVICE_ATTR(running, S_IWUSR | S_IRUGO, + charger_detect_running_show, charger_detect_running_store); + +static ssize_t charger_detect_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + + cts_info("Read sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s'", + attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, "Charger detect type: %d(%s)\n", + cd_data->type, charger_detect_type_str(cd_data->type)); +} + +/* echo charger_detect_type > type */ +static ssize_t charger_detect_type_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + int type, ret; + + cts_info("Write sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc != 1) { + cts_err(""); + return -EINVAL; + } + + type = match_string(charger_detect_type_text, + ARRAY_SIZE(charger_detect_type_text), argv[0]); + if (type < 0) { + cts_err("Invalid charger detect type: '%s'", argv[0]); + return -EINVAL; + } + + ret = switch_charger_detect_type(cd_data, type); + if (ret) { + cts_err("Switch charger detect type failed %d", ret); + return ret; + } + + return count; +} + +static DEVICE_ATTR(type, S_IWUSR | S_IRUGO, + charger_detect_type_show, charger_detect_type_store); + +static ssize_t charger_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + int ret; + + cts_info("Read sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s'", + attr->attr.name); + + ret = get_charger_state(cd_data); + if (ret) { + return scnprintf(buf, PAGE_SIZE, "Get charge state failed %d\n", ret); + } + + return scnprintf(buf, PAGE_SIZE, "Charger state: %s\n", + cd_data->state ? "ATTACHED" : "DETACHED"); +} + +static DEVICE_ATTR(state, S_IRUGO, charger_state_show, NULL); + +static ssize_t charger_detect_param_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + struct power_supply *psy; + + cts_info("Read sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s'", + attr->attr.name); + + psy = power_supply_get_by_name(cd_data->psy_name); + + return scnprintf(buf, PAGE_SIZE, + "Power supply name: %s(%sExist)\n" + "Power supply prop: %d(%s)\n" + "Poll interval : %dms\n", + cd_data->psy_name, psy == NULL ? "Non-" : "", + cd_data->psp, power_supply_prop_str(cd_data->psp), + cd_data->psp_poll_interval); +} + +/* echo psy_name psy_property [interval] > param */ +static ssize_t charger_detect_param_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_charger_detect_data *cd_data = cts_data->charger_detect_data; + struct power_supply *psy; + union power_supply_propval propval; + enum power_supply_property psp; + u32 interval; + int ret; + + cts_info("Write sysfs '" CHARGER_DET_SYSFS_GROUP_NAME "/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc < 2 || argc > 3) { + cts_err("Invalid num of args"); + return -EINVAL; + } + + psy = power_supply_get_by_name(argv[0]); + if (psy == NULL) { + cts_err("Power supply '%s' not found", argv[0]); + return -EINVAL; + } + + psp = power_supply_prop_from_name(argv[1]); + if (psp < 0 || POWER_SUPPLY_GET_PROPERTY(psy, psp, &propval) < 0) { + cts_err("Power supply '%s' property '%s' not valid", + argv[0], power_supply_prop_str(psp)); + return -EINVAL; + } + + interval = cd_data->psp_poll_interval; + if (argc > 2) { + ret = kstrtou32(argv[2], 0, &interval); + if (ret) { + cts_err("Arg interval is invalid"); + return -EINVAL; + } + } + + if (cd_data->psy_name) + kfree(cd_data->psy_name); + + cd_data->psy_name = kstrdup(argv[0], GFP_KERNEL); + if (cd_data->psy_name == NULL) { + cts_err("Dup power supply name failed"); + return -ENOMEM; + } + + cd_data->psp = psp; + cd_data->psp_poll_interval = interval; + + return count; +} + +static DEVICE_ATTR(param, S_IWUSR | S_IRUGO, + charger_detect_param_show, charger_detect_param_store); + +static struct attribute *charger_detect_attrs[] = { + &dev_attr_enable.attr, + &dev_attr_running.attr, + &dev_attr_type.attr, + &dev_attr_state.attr, + &dev_attr_param.attr, + NULL +}; + +static const struct attribute_group charger_detect_attr_group = { + .name = CHARGER_DET_SYSFS_GROUP_NAME, + .attrs = charger_detect_attrs, +}; +#endif /* CONFIG_CTS_SYSFS */ + +int cts_charger_detect_init(struct chipone_ts_data *cts_data) +{ + struct cts_charger_detect_data *cd_data; + int ret = 0; + + cts_info("Init detect"); + + if (cts_data == NULL) { + cts_err("Init detect while cts_data = NULL"); + return -EFAULT; + } + + cd_data = kzalloc(sizeof(*cd_data), GFP_KERNEL); + if (cd_data == NULL) { + cts_err("Alloc charger detect data failed"); + return -ENOMEM; + } + +#ifdef CONFIG_CTS_OF + ret = parse_charger_detect_dt(cd_data, cts_data->device->of_node); +#else /* CONFIG_CTS_OF */ +#ifdef CFG_CTS_DEF_CHGR_DET_ENABLE + cd_data->enable = true; +#else /* CFG_CTS_DEF_CHGR_DET_ENABLE */ + cd_data->enable = false; +#endif /* CFG_CTS_DEF_CHGR_DET_ENABLE */ + + cd_data->type = CFG_CTS_DEF_CHGR_DET_TYPE; + cd_data->psp = CFG_CTS_DEF_CHGR_DET_PSY_PROP; + cd_data->psp_poll_interval = CFG_CTS_DEF_CHGR_DET_PSP_POLL_INTERVAL; + cd_data->psy_name = kstrdup(CFG_CTS_DEF_CHGR_DET_PSY_NAME, GFP_KERNEL); + if (cd_data->psy_name == NULL) { + cts_err("Dup power supply name failed"); + ret = -ENOMEM; + } +#endif /* CONFIG_CTS_OF */ + if (ret) { + cts_err("Get detect param failed %d", ret); + goto free_cd_data; + } + + cts_info("Detect: %sABLED", cd_data->enable ? "EN" : "DIS"); + cts_info(" Type : %s", charger_detect_type_str(cd_data->type)); + cts_info(" PSY Name: %s, prop: %d(%s)", cd_data->psy_name, + cd_data->psp, power_supply_prop_str(cd_data->psp)); + cts_info(" Poll Int: %dms", cd_data->psp_poll_interval); + + ret = init_charger_detect(cd_data); + if (ret) { + cts_err("Init detect failed %d", ret); + goto free_psy_name; + } + +#ifdef CONFIG_CTS_SYSFS + cts_info("Create sysfs group '%s'", CHARGER_DET_SYSFS_GROUP_NAME); + ret = sysfs_create_group(&cts_data->device->kobj, + &charger_detect_attr_group); + if (ret) + cts_warn("Create sysfs group failed %d", ret); + else + cd_data->sysfs_created = true; + +#endif /* CONFIG_CTS_SYSFS */ + + cts_data->charger_detect_data = cd_data; + cd_data->cts_data = cts_data; + + return 0; + +free_psy_name: + kfree(cd_data->psy_name); +free_cd_data: + kfree(cd_data); + + return ret; +} + +int cts_charger_detect_deinit(struct chipone_ts_data *cts_data) +{ + struct cts_charger_detect_data *cd_data; + int ret; + + cts_info("Deinit detect"); + + if (cts_data == NULL) { + cts_err("Deinit detect with cts_data = NULL"); + return -EFAULT; + } + + cd_data = cts_data->charger_detect_data; + if (cd_data == NULL) { + cts_warn("Deinit detect with charger_detect_data = NULL"); + return 0; + } + + if (cd_data->running) { + ret = stop_charger_detect(cd_data); + if (ret) + cts_err("Stop detect failed %d", ret); + } + +#ifdef CONFIG_CTS_SYSFS + if (cd_data->sysfs_created) { + cts_info("Remove sysfs group '%s'", CHARGER_DET_SYSFS_GROUP_NAME); + sysfs_remove_group(&cts_data->device->kobj, + &charger_detect_attr_group); + cd_data->sysfs_created = false; + } +#endif /* CONFIG_CTS_SYSFS */ + + if (cd_data->psy_name) { + cts_info("Kfree power supply name"); + kfree(cd_data->psy_name); + cd_data->psy_name = NULL; + } + + kfree(cd_data); + cts_data->charger_detect_data = NULL; + + return 0; +} + +int cts_is_charger_attached(struct chipone_ts_data *cts_data, bool *attached) +{ + struct cts_charger_detect_data *cd_data; + int ret; + + if (cts_data == NULL) { + cts_err("Get state with cts_data = NULL"); + return -EFAULT; + } + + cd_data = cts_data->charger_detect_data; + if (cd_data == NULL) { + cts_err("Get state with charger_detect_data = NULL"); + return -ENODEV; + } + + cts_info("Get state using type %d(%s) param", + cd_data->type, charger_detect_type_str(cd_data->type)); + + ret = get_charger_state(cd_data); + if (ret) { + cts_err("Get state failed %d", ret); + return ret; + } + + *attached = cd_data->state; + + return 0; +} + +int cts_start_charger_detect(struct chipone_ts_data *cts_data) +{ + struct cts_charger_detect_data *cd_data; + + if (cts_data == NULL) { + cts_err("Start detect with cts_data = NULL"); + return -EFAULT; + } + + cd_data = cts_data->charger_detect_data; + if (cd_data == NULL) { + cts_err("Start detect with charger_detect_data = NULL"); + return -ENODEV; + } + + return start_charger_detect(cd_data); +} + +int cts_stop_charger_detect(struct chipone_ts_data *cts_data) +{ + struct cts_charger_detect_data *cd_data; + + if (cts_data == NULL) { + cts_err("Stop detect with cts_data = NULL"); + return -EFAULT; + } + + cd_data = cts_data->charger_detect_data; + if (cd_data == NULL) { + cts_err("Stop detect with charger_detect_data = NULL"); + return -ENODEV; + } + + return stop_charger_detect(cd_data); +} +#endif /* CONFIG_CTS_CHARGER_DETECT */ diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_charger_detect.h b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_charger_detect.h new file mode 100644 index 000000000000..34960970a647 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_charger_detect.h @@ -0,0 +1,43 @@ +#ifndef CTS_CHARGER_DETECT_H +#define CTS_CHARGER_DETECT_H + +#include "cts_config.h" + +struct chipone_ts_data; + +#ifdef CONFIG_CTS_CHARGER_DETECT +extern int cts_charger_detect_init(struct chipone_ts_data *cts_data); +extern int cts_charger_detect_deinit(struct chipone_ts_data *cts_data); +extern int cts_start_charger_detect(struct chipone_ts_data *cts_data); +extern int cts_stop_charger_detect(struct chipone_ts_data *cts_data); +extern int cts_is_charger_attached(struct chipone_ts_data *cts_data, + bool *attached); +#else /* CONFIG_CTS_CHARGER_DETECT */ +static inline int cts_charger_detect_init(struct chipone_ts_data *cts_data) +{ + return -ENOTSUPP; +} + +static inline int cts_charger_detect_deinit(struct chipone_ts_data *cts_data) +{ + return -ENOTSUPP; +} + +static inline int cts_start_charger_detect(struct chipone_ts_data *cts_data) +{ + return -ENODEV; +} + +static inline int cts_stop_charger_detect(struct chipone_ts_data *cts_data) +{ + return -ENODEV; +} + +static inline int cts_is_charger_attached(struct chipone_ts_data *cts_data, + bool *attached) +{ + return -ENODEV; +} +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#endif /* CTS_CHARGER_DETECT_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_config.h b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_config.h new file mode 100644 index 000000000000..f54dbd647d9b --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_config.h @@ -0,0 +1,226 @@ +#ifndef CTS_CONFIG_H +#define CTS_CONFIG_H +#include +/** Driver version */ +#define CFG_CTS_DRIVER_MAJOR_VERSION 3 +#define CFG_CTS_DRIVER_MINOR_VERSION 4 +#define CFG_CTS_DRIVER_PATCH_VERSION 3 + +#define CFG_CTS_DRIVER_VERSION "v3.4.3" + +/** Use software reset */ +/* #define CONFIG_CTS_ICTYPE_ICNL9922 */ + +/** 1: hwid register addr 0x70000 + * 2: Use spi drw protocol */ +/* #define CONFIG_CTS_ICTYPE_ICNL9922C */ + +/** Feature: hwid register addr 0x70000 */ +/* #define CONFIG_CTS_ICTYPE_ICNL9951 */ + +/* For Goole Security */ +/* #define CFG_CTS_FOR_GKI */ + +/** Whether reset pin is used */ +#define CFG_CTS_HAS_RESET_PIN + +/* #define CONFIG_CTS_I2C_HOST */ +#ifndef CONFIG_CTS_I2C_HOST +#ifndef CFG_CTS_HAS_RESET_PIN +#define CFG_CTS_HAS_RESET_PIN +#endif + +#define CFG_CTS_SPI_SPEED_KHZ 6000 +#endif /* CONFIG_CTS_I2C_HOST */ + +#define CFG_CTS_FORCE_UP +/* #define CFG_CTS_HEARTBEAT_MECHANISM */ + +/* #define CFG_CTS_PALM_DETECT */ +#ifdef CFG_CTS_PALM_DETECT +#define CFG_CTS_PALM_EVENT 240 +#define CFG_CTS_PALM_ID 0x80 +#endif + +#define CONFIG_GENERIC_HARDIRQS +/* #define CFG_CTS_FW_LOG_REDIRECT */ + +/** Whether force download firmware to chip */ +/* #define CFG_CTS_FIRMWARE_FORCE_UPDATE */ + +/** Use build in firmware or firmware file in fs*/ +#define CFG_CTS_DRIVER_BUILTIN_FIRMWARE +#define CFG_CTS_FIRMWARE_IN_FS +#ifdef CFG_CTS_FIRMWARE_IN_FS +/* Add FW custom process. */ +/* #define CFG_CTS_FW_UPDATE_SYS */ + +/* Load config fw bin as default. */ +/* #define CFG_CTS_FW_UPDATE_FILE_LOAD */ +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD +#define CFG_CTS_FW_FILE_NAME_MAX_LEN 128 +#define CFG_CTS_FW_FILE_PATH "/vendor/firmware/" +#define CFG_CTS_FW_FILE_NAME_VENDOR "chipone" +#endif /*CFG_CTS_FW_UPDATE_FILE_LOAD */ + +#define CFG_CTS_FIRMWARE_FILENAME "chipone_firmware.bin" +#define CFG_CTS_FIRMWARE_FILEPATH "/vendor/firmware/chipone_firmware.bin" +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + +#define CFG_CTS_FACTORY_LIMIT_FILENAME "chipone_limit.bin" + +/* IC type support */ +#define CFG_CTS_CHIP_NAME "CHIPONE-TDDI" + +#ifdef CONFIG_PROC_FS +/* Proc FS for backward compatibility for APK tool com.ICN85xx */ +#define CONFIG_CTS_LEGACY_TOOL +#endif /* CONFIG_PROC_FS */ + +#ifdef CONFIG_SYSFS +/* Sys FS for gesture report, debug feature etc. */ +#define CONFIG_CTS_SYSFS +#endif /* CONFIG_SYSFS */ + +#define CFG_CTS_MAX_TOUCH_NUM (10) + +/* Virtual key support */ +/* #define CONFIG_CTS_VIRTUALKEY */ +#ifdef CONFIG_CTS_VIRTUALKEY +#define CFG_CTS_MAX_VKEY_NUM (4) +#define CFG_CTS_NUM_VKEY (3) +#define CFG_CTS_VKEY_KEYCODES {KEY_BACK, KEY_HOME, KEY_MENU} +#endif /* CONFIG_CTS_VIRTUALKEY */ + +/* Gesture wakeup */ +#define CFG_CTS_GESTURE +#ifdef CFG_CTS_GESTURE +#define GESTURE_UP 0x11 +#define GESTURE_C 0x12 +#define GESTURE_O 0x13 +#define GESTURE_M 0x14 +#define GESTURE_W 0x15 +#define GESTURE_E 0x16 +#define GESTURE_S 0x17 +#define GESTURE_Z 0x1d +#define GESTURE_V 0x1e +#define GESTURE_D_TAP 0x50 +#define GESTURE_DOWN 0x22 +#define GESTURE_LEFT 0x23 +#define GESTURE_RIGHT 0x24 +#define GESTURE_TAP 0x7f + +#define CFG_CTS_NUM_GESTURE (14u) +#define CFG_CTS_GESTURE_REPORT_KEY +#define CFG_CTS_GESTURE_KEYMAP \ +{ \ + { GESTURE_C, KEY_C,}, \ + { GESTURE_W, KEY_W,}, \ + { GESTURE_V, KEY_V,}, \ + { GESTURE_D_TAP, BTN_TRIGGER_HAPPY6,}, \ + { GESTURE_Z, KEY_Z,}, \ + { GESTURE_M, KEY_M,}, \ + { GESTURE_O, KEY_O,}, \ + { GESTURE_E, KEY_E,}, \ + { GESTURE_S, KEY_S,}, \ + { GESTURE_UP, KEY_UP,}, \ + { GESTURE_DOWN, KEY_DOWN,}, \ + { GESTURE_LEFT, KEY_LEFT,}, \ + { GESTURE_RIGHT, KEY_RIGHT,}, \ + { GESTURE_TAP, BTN_TRIGGER_HAPPY3,}, \ +} +#define CFG_CTS_GESTURE_REPORT_TRACE 0 +#endif /* CFG_CTS_GESTURE */ + +/* #define CONFIG_CTS_GLOVE */ +#define CONFIG_CTS_EARJACK_DETECT +#define CONFIG_CTS_CHARGER_DETECT + +/* #define CONFIG_CTS_TP_PROXIMITY */ + +/* ESD protection */ +#define CONFIG_CTS_ESD_PROTECTION +#ifdef CONFIG_CTS_ESD_PROTECTION +#define CFG_CTS_ESD_PROTECTION_CHECK_PERIOD (2 * HZ) +#define CFG_CTS_ESD_FAILED_CONFIRM_CNT 3 +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +/* Use slot protocol (protocol B), comment it if use protocol A. */ +#define CONFIG_CTS_SLOTPROTOCOL + +/* #define CTS_CONFIG_MKDIR_FOR_CTS_TEST */ + +#ifdef CONFIG_CTS_LEGACY_TOOL +#define CFG_CTS_TOOL_PROC_FILENAME "cts_tool" +#endif /* CONFIG_CTS_LEGACY_TOOL */ + +#define CFG_CTS_UPDATE_CRCCHECK + + +/**************************************************************************** + * Platform configurations + ****************************************************************************/ + +#if IS_ENABLED(CONFIG_QCOM_PANEL_EVENT_NOTIFIER) +#define CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS +#endif + +#define CONFIG_CTS_PM_FB_NOTIFIER +#ifdef CONFIG_CTS_PM_FB_NOTIFIER +#ifdef CONFIG_DRM +#define CFG_CTS_DRM_NOTIFIER +#endif /*CONFIG_DRM */ +#else /*CONFIG_CTS_PM_FB_NOTIFIER */ +#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_SUSPEND) + /* #define CONFIG_CTS_PM_GENERIC */ +#endif /* CONFIG_PM_SLEEP */ + +#ifndef CONFIG_CTS_PM_GENERIC +#define CONFIG_CTS_PM_LEGACY +#endif /*CONFIG_CTS_PM_GENERIC */ +#endif /*CONFIG_CTS_PM_FB_NOTIFIER */ + +#define CFG_CTS_MAX_I2C_XFER_SIZE (48u) +#define CFG_CTS_MAX_I2C_READ_SIZE (2048u) +#define CFG_CTS_MAX_SPI_XFER_SIZE (8192u) +#define CFG_CTS_INT_DATA_MAX_SIZE (8192u) + + +#define CTS_FW_LOG_REDIRECT_SIGN 0x60 +#define CTS_FW_LOG_BUF_LEN 128 + +/** + * #define CFG_CTS_SWAP_XY + * #define CFG_CTS_WRAP_X + * #define CFG_CTS_WRAP_Y + */ + +#define CFG_CTS_DEVICE_NAME "chipone-tddi" +#define CFG_CTS_DRIVER_NAME "chipone-tddi" + +#if CFG_CTS_MAX_I2C_XFER_SIZE < 8 +#error "I2C transfer size should large than 8" +#endif + +#ifdef CONFIG_OF +#define CONFIG_CTS_OF +#endif +#ifdef CONFIG_CTS_OF +#define CFG_CTS_OF_DEVICE_ID_NAME "chipone-tddi" + +#define CFG_CTS_OF_INT_GPIO_NAME "chipone,irq-gpio" +#define CFG_CTS_OF_RST_GPIO_NAME "chipone,rst-gpio" + +#ifdef CFG_CTS_MANUAL_CS +#define CFG_CTS_OF_CS_GPIO_NAME "chipone,cs-gpio" +#endif + +#define CFG_CTS_OF_X_RESOLUTION_NAME "chipone,x-res" +#define CFG_CTS_OF_Y_RESOLUTION_NAME "chipone,y-res" + +#ifdef CFG_CTS_FW_UPDATE_SYS +#define CFG_CTS_OF_PANEL_SUPPLIER "chipone,panel-supplier" +#endif +#endif /* CONFIG_CTS_OF */ + +#endif /* CTS_CONFIG_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_core.c b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_core.c new file mode 100644 index 000000000000..1172eb43bd05 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_core.c @@ -0,0 +1,2939 @@ +#define LOG_TAG "Core" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sfctrl.h" +#include "cts_spi_flash.h" +#include "cts_sysfs.h" +#include "cts_firmware.h" +#include "cts_charger_detect.h" +#include "cts_earjack_detect.h" +#include "cts_tcs.h" + +#ifdef CONFIG_CTS_I2C_HOST +static int cts_i2c_writeb(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay) +{ + u8 buff[8]; + + cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x val: 0x%02x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, b); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, buff); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, buff); + else { + cts_err("Writeb invalid address width %u", cts_dev->rtdata.addr_width); + return -EINVAL; + } + buff[cts_dev->rtdata.addr_width] = b; + + return cts_plat_i2c_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 1, retry, delay); +} + +static int cts_i2c_writew(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay) +{ + u8 buff[8]; + + cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x val: 0x%04x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, w); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, buff); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, buff); + else { + cts_err("Writew invalid address width %u", cts_dev->rtdata.addr_width); + return -EINVAL; + } + + put_unaligned_le16(w, buff + cts_dev->rtdata.addr_width); + + return cts_plat_i2c_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 2, retry, delay); +} + +static int cts_i2c_writel(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay) +{ + u8 buff[8]; + + cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x val: 0x%08x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, l); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, buff); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, buff); + else { + cts_err("Writel invalid address width %u", cts_dev->rtdata.addr_width); + return -EINVAL; + } + + put_unaligned_le32(l, buff + cts_dev->rtdata.addr_width); + + return cts_plat_i2c_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 4, retry, delay); +} + +static int cts_i2c_writesb(const struct cts_device *cts_dev, u32 addr, + const u8 *src, size_t len, int retry, int delay) +{ + int ret; + u8 *data; + size_t max_xfer_size; + size_t payload_len; + size_t xfer_len; + + cts_dbg("Write to slave_addr: 0x%02x reg: 0x%0*x len: %zu", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, len); + + max_xfer_size = cts_plat_get_max_i2c_xfer_size(cts_dev->pdata); + data = cts_plat_get_i2c_xfer_buf(cts_dev->pdata, len); + while (len) { + payload_len = + min((size_t)(max_xfer_size - cts_dev->rtdata.addr_width), len); + xfer_len = payload_len + cts_dev->rtdata.addr_width; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, data); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, data); + else { + cts_err("Writesb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + memcpy(data + cts_dev->rtdata.addr_width, src, payload_len); + + ret = cts_plat_i2c_write(cts_dev->pdata, + cts_dev->rtdata.slave_addr, data, + xfer_len, retry, delay); + if (ret) { + cts_err("Platform i2c write failed %d", ret); + return ret; + } + + src += payload_len; + len -= payload_len; + addr += payload_len; + } + + return 0; +} + +static int cts_i2c_readb(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay) +{ + u8 addr_buf[4]; + + cts_dbg("Readb from slave_addr: 0x%02x reg: 0x%0*x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, addr); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readb invalid address width %u", cts_dev->rtdata.addr_width); + return -EINVAL; + } + + return cts_plat_i2c_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, b, 1, retry, delay); +} + +static int cts_i2c_readw(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + u8 buff[2]; + + cts_dbg("Readw from slave_addr: 0x%02x reg: 0x%0*x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, addr); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readw invalid address width %u", cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_i2c_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, buff, 2, retry, delay); + if (ret == 0) + *w = get_unaligned_le16(buff); + + return ret; +} + +static int cts_i2c_readl(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + u8 buff[4]; + + cts_dbg("Readl from slave_addr: 0x%02x reg: 0x%0*x", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, addr); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readl invalid address width %u", cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_i2c_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, buff, 4, retry, delay); + if (ret == 0) + *l = get_unaligned_le32(buff); + + return ret; +} + +static int cts_i2c_readsb(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + size_t max_xfer_size, xfer_len; + + cts_dbg("Readsb from slave_addr: 0x%02x reg: 0x%0*x len: %zu", + cts_dev->rtdata.slave_addr, cts_dev->rtdata.addr_width * 2, + addr, len); + + max_xfer_size = cts_plat_get_max_i2c_xfer_size(cts_dev->pdata); + while (len) { + xfer_len = min(max_xfer_size, len); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readsb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_i2c_read(cts_dev->pdata, + cts_dev->rtdata.slave_addr, addr_buf, + cts_dev->rtdata.addr_width, dst, + xfer_len, retry, delay); + if (ret) { + cts_err("Platform i2c read failed %d", ret); + return ret; + } + + dst += xfer_len; + len -= xfer_len; + addr += xfer_len; + } + + return 0; +} +#else +static int cts_spi_writeb(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay) +{ + u8 buff[8]; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, buff); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, buff); + else { + cts_err("Writeb invalid address width %u", cts_dev->rtdata.addr_width); + return -EINVAL; + } + buff[cts_dev->rtdata.addr_width] = b; + + return cts_plat_spi_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 1, retry, delay); + return 0; +} + +static int cts_spi_writew(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay) +{ + u8 buff[8]; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, buff); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, buff); + else { + cts_err("Writew invalid address width %u", cts_dev->rtdata.addr_width); + return -EINVAL; + } + + put_unaligned_le16(w, buff + cts_dev->rtdata.addr_width); + + return cts_plat_spi_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 2, retry, delay); + return 0; +} + +static int cts_spi_writel(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay) +{ + u8 buff[8]; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, buff); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, buff); + else { + cts_err("Writel invalid address width %u", cts_dev->rtdata.addr_width); + return -EINVAL; + } + + put_unaligned_le32(l, buff + cts_dev->rtdata.addr_width); + + return cts_plat_spi_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + buff, cts_dev->rtdata.addr_width + 4, retry, delay); + return 0; +} + +static int cts_spi_writesb(const struct cts_device *cts_dev, u32 addr, + const u8 *src, size_t len, int retry, int delay) +{ + int ret; + u8 *data; + size_t max_xfer_size; + size_t payload_len; + size_t xfer_len; + + max_xfer_size = cts_plat_get_max_spi_xfer_size(cts_dev->pdata); + data = cts_plat_get_spi_xfer_buf(cts_dev->pdata, len); + while (len) { + payload_len = + min((size_t)(max_xfer_size - cts_dev->rtdata.addr_width), len); + xfer_len = payload_len + cts_dev->rtdata.addr_width; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, data); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, data); + else { + cts_err("Writesb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + memcpy(data + cts_dev->rtdata.addr_width, src, payload_len); + + ret = cts_plat_spi_write(cts_dev->pdata, cts_dev->rtdata.slave_addr, + data, xfer_len, retry, delay); + if (ret) { + cts_err("Platform i2c write failed %d", ret); + return ret; + } + + src += payload_len; + len -= payload_len; + addr += payload_len; + } + return 0; +} + +static int cts_spi_readb(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay) +{ + u8 addr_buf[4]; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readb invalid address width %u", cts_dev->rtdata.addr_width); + return -EINVAL; + } + + return cts_plat_spi_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, b, 1, retry, delay); +} + +static int cts_spi_readw(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + u8 buff[2]; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readw invalid address width %u", cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_spi_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, buff, 2, retry, delay); + if (ret == 0) + *w = get_unaligned_le16(buff); + + return ret; +} + +static int cts_spi_readl(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + u8 buff[4]; + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readl invalid address width %u", cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_spi_read(cts_dev->pdata, cts_dev->rtdata.slave_addr, + addr_buf, cts_dev->rtdata.addr_width, buff, 4, retry, delay); + if (ret == 0) + *l = get_unaligned_le32(buff); + + return ret; +} + +static int cts_spi_readsb(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay) +{ + int ret; + u8 addr_buf[4]; + size_t max_xfer_size, xfer_len; + + max_xfer_size = cts_plat_get_max_spi_xfer_size(cts_dev->pdata); + while (len) { + xfer_len = min(max_xfer_size, len); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readsb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_spi_read(cts_dev->pdata, + cts_dev->rtdata.slave_addr, addr_buf, + cts_dev->rtdata.addr_width, dst, + xfer_len, retry, delay); + if (ret) { + cts_err("Platform i2c read failed %d", ret); + return ret; + } + + dst += xfer_len; + len -= xfer_len; + addr += xfer_len; + } + return 0; +} + +static int cts_spi_readsb_delay_idle(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay, int idle) +{ + int ret; + u8 addr_buf[4]; + size_t max_xfer_size, xfer_len; + + max_xfer_size = cts_plat_get_max_spi_xfer_size(cts_dev->pdata); + while (len) { + xfer_len = min(max_xfer_size, len); + + if (cts_dev->rtdata.addr_width == 2) + put_unaligned_be16(addr, addr_buf); + else if (cts_dev->rtdata.addr_width == 3) + put_unaligned_be24(addr, addr_buf); + else { + cts_err("Readsb invalid address width %u", + cts_dev->rtdata.addr_width); + return -EINVAL; + } + + ret = cts_plat_spi_read_delay_idle(cts_dev->pdata, + cts_dev->rtdata.slave_addr, addr_buf, + cts_dev->rtdata.addr_width, + dst, xfer_len, retry, delay, idle); + if (ret) { + cts_err("Platform i2c read failed %d", ret); + return ret; + } + + dst += xfer_len; + len -= xfer_len; + addr += xfer_len; + } + return 0; +} + +#endif /* CONFIG_CTS_I2C_HOST */ + +int cts_dev_writeb(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_writeb(cts_dev, addr, b, retry, delay); +#else + return cts_spi_writeb(cts_dev, addr, b, retry, delay); +#endif +} + +static inline int cts_dev_writew(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_writew(cts_dev, addr, w, retry, delay); +#else + return cts_spi_writew(cts_dev, addr, w, retry, delay); +#endif +} + +static inline int cts_dev_writel(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_writel(cts_dev, addr, l, retry, delay); +#else + return cts_spi_writel(cts_dev, addr, l, retry, delay); +#endif +} + +static inline int cts_dev_writesb(const struct cts_device *cts_dev, u32 addr, + const u8 *src, size_t len, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_writesb(cts_dev, addr, src, len, retry, delay); +#else + return cts_spi_writesb(cts_dev, addr, src, len, retry, delay); +#endif +} + +int cts_dev_readb(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readb(cts_dev, addr, b, retry, delay); +#else + return cts_spi_readb(cts_dev, addr, b, retry, delay); +#endif +} + +static inline int cts_dev_readw(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readw(cts_dev, addr, w, retry, delay); +#else + return cts_spi_readw(cts_dev, addr, w, retry, delay); +#endif +} + +static inline int cts_dev_readl(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readl(cts_dev, addr, l, retry, delay); +#else + return cts_spi_readl(cts_dev, addr, l, retry, delay); +#endif +} + +static inline int cts_dev_readsb(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readsb(cts_dev, addr, dst, len, retry, delay); +#else + return cts_spi_readsb(cts_dev, addr, dst, len, retry, delay); +#endif +} + +static inline int cts_dev_readsb_delay_idle(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay, int idle) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_i2c_readsb(cts_dev, addr, dst, len, retry, delay); +#else + return cts_spi_readsb_delay_idle(cts_dev, addr, dst, len, retry, delay, + idle); +#endif +} + +#ifdef CFG_CTS_UPDATE_CRCCHECK +int cts_sram_writesb_boot_crc_retry(const struct cts_device *cts_dev, + size_t len, u32 crc, int retry) +{ + int ret = 0, retries; + u32 addr[3]; + + if (cts_dev->hwdata->hwid == CTS_DEV_HWID_ICNL9922C) { + addr[0] = 0x02bff0; + addr[1] = 0x02bff8; + addr[2] = 0x02bffc; + } else { + addr[0] = 0x01fff0; + addr[1] = 0x01fff8; + addr[2] = 0x01fffc; + } + + retries = 0; + do { + ret = cts_dev_writel(cts_dev, addr[0], 0xCC33555A, 3, 1); + if (ret != 0) { + cts_err("SRAM writesb failed %d", ret); + continue; + } + + ret = cts_dev_writel(cts_dev, addr[1], crc, 3, 1); + if (ret != 0) { + cts_err("SRAM writesb failed %d", ret); + continue; + } + + ret = cts_dev_writel(cts_dev, addr[2], len, 3, 1); + if (ret != 0) { + cts_err("SRAM writesb failed %d", ret); + continue; + } + + break; + } while (retries++ < retry); + + return ret; +} +#endif + +static int cts_write_sram_normal_mode(const struct cts_device *cts_dev, + u32 addr, const void *src, size_t len, int retry, int delay) +{ + int i, ret; + u8 buff[5]; + + for (i = 0; i < len; i++) { + put_unaligned_le32(addr, buff); + buff[4] = *(u8 *) src; + + addr++; + src++; + + ret = cts_dev_writesb(cts_dev, CTS_DEVICE_FW_REG_DEBUG_INTF, + buff, 5, retry, delay); + if (ret) { + cts_err("Write rDEBUG_INTF len=5B failed %d", ret); + return ret; + } + } + + return 0; +} + +int cts_sram_writeb_retry(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) + return cts_dev_writeb(cts_dev, addr, b, retry, delay); + else + return cts_write_sram_normal_mode(cts_dev, addr, &b, 1, retry, delay); +} + +int cts_sram_writew_retry(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay) +{ + u8 buff[2]; + + if (cts_dev->rtdata.program_mode) + return cts_dev_writew(cts_dev, addr, w, retry, delay); + else { + put_unaligned_le16(w, buff); + + return cts_write_sram_normal_mode(cts_dev, addr, buff, 2, retry, delay); + } +} + +int cts_sram_writel_retry(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay) +{ + u8 buff[4]; + + if (cts_dev->rtdata.program_mode) + return cts_dev_writel(cts_dev, addr, l, retry, delay); + else { + put_unaligned_le32(l, buff); + + return cts_write_sram_normal_mode(cts_dev, addr, buff, 4, retry, delay); + } +} + +int cts_sram_writesb_retry(const struct cts_device *cts_dev, + u32 addr, const void *src, size_t len, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) + return cts_dev_writesb(cts_dev, addr, src, len, retry, delay); + else + return cts_write_sram_normal_mode(cts_dev, addr, src, len, retry, delay); +} + +static int cts_calc_sram_crc(const struct cts_device *cts_dev, + u32 sram_addr, size_t size, u32 *crc) +{ + cts_info("Calc crc from sram 0x%06x size %zu", sram_addr, size); + + return cts_dev->hwdata->sfctrl->ops->calc_sram_crc(cts_dev, + sram_addr, size, crc); +} + +int cts_sram_writesb_check_crc_retry(const struct cts_device *cts_dev, + u32 addr, const void *src, size_t len, u32 crc, int retry) +{ + int ret, retries; + u32 fw_crc; + u8 tdata[16]; + u8 magic_num[] = { 0x5A, 0x55, 0x33, 0xCC, 0x00, 0x00, 0x00, 0x00 }; + + retries = 0; + do { + u32 crc_sram; + + retries++; + + ret = cts_sram_writesb(cts_dev, 0, src, len); + if (ret != 0) { + cts_err("SRAM writesb failed %d", ret); + continue; + } + + fw_crc = cts_crc32(src, len); + cts_info("fw crc: 0x%04x", fw_crc); + memcpy(tdata, magic_num, 8); + memcpy(tdata + 8, &fw_crc, 4); + memcpy(tdata + 12, &len, 4); + + ret = cts_sram_writesb_boot_crc_retry(cts_dev, len, fw_crc, 3); + if (ret != 0) { + cts_err("cts_hw_reg_writesb_retry %d", ret); + continue; + } + + ret = cts_calc_sram_crc(cts_dev, 0, len, &crc_sram); + if (ret != 0) { + cts_err("Get CRC for sram writesb failed %d retries %d", + ret, retries); + continue; + } + + if (crc == crc_sram) + return 0; + + cts_err("Check CRC for sram writesb mismatch %x != %x retries %d", + crc, crc_sram, retries); + ret = -EFAULT; + } while (retries < retry); + + return ret; +} + +int cts_read_sram_normal_mode(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay) +{ + int i, ret; + + for (i = 0; i < len; i++) { + ret = cts_dev_writel(cts_dev, CTS_DEVICE_FW_REG_DEBUG_INTF, + addr, retry, delay); + if (ret) { + cts_err("Write addr to rDEBUG_INTF failed %d", ret); + return ret; + } + + ret = cts_dev_readb(cts_dev, CTS_DEVICE_FW_REG_DEBUG_INTF + 4, + (u8 *) dst, retry, delay); + if (ret) { + cts_err("Read value from rDEBUG_INTF + 4 failed %d", ret); + return ret; + } + + addr++; + dst++; + } + + return 0; +} + +int cts_sram_readb_retry(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) + return cts_dev_readb(cts_dev, addr, b, retry, delay); + else + return cts_read_sram_normal_mode(cts_dev, addr, b, 1, retry, delay); +} + +int cts_sram_readw_retry(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay) +{ + int ret; + u8 buff[2]; + + if (cts_dev->rtdata.program_mode) + return cts_dev_readw(cts_dev, addr, w, retry, delay); + else { + ret = cts_read_sram_normal_mode(cts_dev, addr, buff, 2, retry, delay); + if (ret) { + cts_err("SRAM readw in normal mode failed %d", ret); + return ret; + } + + *w = get_unaligned_le16(buff); + return 0; + } +} + +int cts_sram_readl_retry(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay) +{ + int ret; + u8 buff[4]; + + if (cts_dev->rtdata.program_mode) + return cts_dev_readl(cts_dev, addr, l, retry, delay); + else { + ret = cts_read_sram_normal_mode(cts_dev, addr, buff, 4, retry, delay); + if (ret) { + cts_err("SRAM readl in normal mode failed %d", ret); + return ret; + } + + *l = get_unaligned_le32(buff); + + return 0; + } +} + +int cts_sram_readsb_retry(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) + return cts_dev_readsb(cts_dev, addr, dst, len, retry, delay); + else + return cts_read_sram_normal_mode(cts_dev, addr, dst, len, retry, delay); +} + +int cts_fw_reg_writeb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 b, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Writeb to fw reg 0x%04x under program mode", reg_addr); + return -ENODEV; + } + + return cts_dev_writeb(cts_dev, reg_addr, b, retry, delay); +} + +int cts_fw_reg_writew_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 w, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Writew to fw reg 0x%04x under program mode", reg_addr); + return -ENODEV; + } + + return cts_dev_writew(cts_dev, reg_addr, w, retry, delay); +} + +int cts_fw_reg_writel_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 l, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Writel to fw reg 0x%04x under program mode", reg_addr); + return -ENODEV; + } + + return cts_dev_writel(cts_dev, reg_addr, l, retry, delay); +} + +int cts_fw_reg_writesb_retry(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Writesb to fw reg 0x%04x under program mode", + reg_addr); + return -ENODEV; + } + + return cts_dev_writesb(cts_dev, reg_addr, src, len, retry, delay); +} + +int cts_fw_reg_readb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readb from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readb(cts_dev, reg_addr, b, retry, delay); +} + +int cts_fw_reg_readw_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readw from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readw(cts_dev, reg_addr, w, retry, delay); +} + +int cts_fw_reg_readl_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readl from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readl(cts_dev, reg_addr, l, retry, delay); +} + +int cts_fw_reg_readsb_retry(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, int retry, int delay) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readsb from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readsb(cts_dev, reg_addr, dst, len, retry, delay); +} + +int cts_fw_reg_readsb_retry_delay_idle(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, int retry, int delay, int idle) +{ + if (cts_dev->rtdata.program_mode) { + cts_err("Readsb from fw reg under program mode"); + return -ENODEV; + } + + return cts_dev_readsb_delay_idle(cts_dev, reg_addr, dst, len, retry, + delay, idle); +} + +int cts_hw_reg_writeb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 b, int retry, int delay) +{ + return cts_sram_writeb_retry(cts_dev, reg_addr, b, retry, delay); +} + +int cts_hw_reg_writew_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 w, int retry, int delay) +{ + return cts_sram_writew_retry(cts_dev, reg_addr, w, retry, delay); +} + +int cts_hw_reg_writel_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 l, int retry, int delay) +{ + return cts_sram_writel_retry(cts_dev, reg_addr, l, retry, delay); +} + +int cts_hw_reg_writesb_retry(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len, int retry, int delay) +{ + return cts_sram_writesb_retry(cts_dev, reg_addr, src, len, retry, delay); +} + +int cts_hw_reg_readb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b, int retry, int delay) +{ + return cts_sram_readb_retry(cts_dev, reg_addr, b, retry, delay); +} + +int cts_hw_reg_readw_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w, int retry, int delay) +{ + return cts_sram_readw_retry(cts_dev, reg_addr, w, retry, delay); +} + +int cts_hw_reg_readl_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l, int retry, int delay) +{ + return cts_sram_readl_retry(cts_dev, reg_addr, l, retry, delay); +} + +int cts_hw_reg_readsb_retry(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, int retry, int delay) +{ + return cts_sram_readsb_retry(cts_dev, reg_addr, dst, len, retry, delay); +} + +static int icnl9916_set_access_ddi_reg(struct cts_device *cts_dev, bool enable) +{ + int ret; + u8 access_flag; + + cts_info("ICNL9916 %s access ddi reg", enable ? "enable" : "disable"); + + ret = cts_hw_reg_readb(cts_dev, CTS_DEV_HW_REG_DDI_REG_CTRL, &access_flag); + if (ret) { + cts_err("Read HW_REG_DDI_REG_CTRL failed %d", ret); + return ret; + } + + access_flag = enable ? (access_flag | 0x01) : (access_flag & (~0x01)); + ret = cts_hw_reg_writeb(cts_dev, CTS_DEV_HW_REG_DDI_REG_CTRL, access_flag); + if (ret) { + cts_err("Write HW_REG_DDI_REG_CTRL %02x failed %d", access_flag, ret); + return ret; + } + + ret = cts_hw_reg_writeb(cts_dev, CTS_DEV_HW_REG_HPPROG, enable ? 1 : 0); + if (ret) { + cts_err("Write CTS_DEV_HW_REG_HPPROG failed %d", ret); + return ret; + } + + ret = cts_hw_reg_writew(cts_dev, 0x3DFF0, enable ? 0x595A : 0x5A5A); + if (ret) { + cts_err("Write password to F0 failed %d", ret); + return ret; + } + ret = cts_hw_reg_writew(cts_dev, 0x3DFF4, enable ? 0xA6A5 : 0x5A5A); + if (ret) { + cts_err("Write password to F1 failed %d", ret); + return ret; + } + + return 0; +} + +const static struct cts_sfctrl icnl9916_sfctrl = { + .reg_base = 0x34000, + .xchg_sram_base = 96 * 1024, + .xchg_sram_size = 32 * 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_sfctrl icnl9916c_sfctrl = { + .reg_base = 0x34000, + .xchg_sram_base = 96 * 1024, + .xchg_sram_size = 32 * 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_sfctrl icnl9922_sfctrl = { + .reg_base = 0x34000, + .xchg_sram_base = 96 * 1024, + .xchg_sram_size = 32 * 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_sfctrl icnl9922c_sfctrl = { + .reg_base = 0x74000, + .xchg_sram_base = 0x24000, + .xchg_sram_size = 16 * 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_sfctrl icnl9951_sfctrl = { + .reg_base = 0x74000, + .xchg_sram_base = 160 * 1024, + .xchg_sram_size = 64 * 1024, /* For non firmware programming */ + .ops = &cts_sfctrlv2_ops +}; + +const static struct cts_device_hwdata cts_device_hwdatas[] = { + { + .name = "ICNL9916", + .hwid = CTS_DEV_HWID_ICNL9916, + .fwid = CTS_DEV_FWID_ICNL9916, + .num_row = 32, + .num_col = 18, + .sram_size = 96 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9916_sfctrl, + .enable_access_ddi_reg = icnl9916_set_access_ddi_reg, + }, + { + .name = "primary", + .hwid = CTS_DEV_HWID_ICNL9916C, + .fwid = CTS_DEV_FWID_ICNL9916C, + .num_row = 32, + .num_col = 18, + .sram_size = 96 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9916c_sfctrl, + .enable_access_ddi_reg = icnl9916_set_access_ddi_reg, + }, + + { + .name = "ICNL9922", + .hwid = CTS_DEV_HWID_ICNL9922, + .fwid = CTS_DEV_FWID_ICNL9922, + .num_row = 36, + .num_col = 18, + .sram_size = 96 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9922_sfctrl, + .enable_access_ddi_reg = icnl9916_set_access_ddi_reg, + }, + + { + .name = "ICNL9922C", + .hwid = CTS_DEV_HWID_ICNL9922C, + .fwid = CTS_DEV_FWID_ICNL9922C, + .num_row = 36, + .num_col = 18, + .sram_size = 96 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9922c_sfctrl, + .enable_access_ddi_reg = icnl9916_set_access_ddi_reg, + }, + + { + .name = "ICNL9951", + .hwid = CTS_DEV_HWID_ICNL9951, + .fwid = CTS_DEV_FWID_ICNL9951, + .num_row = 36, + .num_col = 22, + .sram_size = 128 * 1024, + + .program_addr_width = 3, + + .sfctrl = &icnl9951_sfctrl, + .enable_access_ddi_reg = icnl9916_set_access_ddi_reg, + } +}; + +static int cts_init_device_hwdata(struct cts_device *cts_dev, + u32 hwid, u16 fwid) +{ + int i; + + cts_info("Init hardware data hwid: %06x fwid: %04x", hwid, fwid); + + for (i = 0; i < ARRAY_SIZE(cts_device_hwdatas); i++) { + if (hwid == cts_device_hwdatas[i].hwid || + fwid == cts_device_hwdatas[i].fwid) { + cts_dev->hwdata = &cts_device_hwdatas[i]; + return 0; + } + } + + return -EINVAL; +} + +void cts_lock_device(const struct cts_device *cts_dev) +{ + cts_dbg("*** Lock ***"); + + mutex_lock(&cts_dev->pdata->dev_lock); +} + +void cts_unlock_device(const struct cts_device *cts_dev) +{ + cts_dbg("### Un-Lock ###"); + + mutex_unlock(&cts_dev->pdata->dev_lock); +} + +int cts_send_command(const struct cts_device *cts_dev, u8 cmd) +{ + cts_info("Send command 0x%02x", cmd); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Send command %u while chip in program mode", cmd); + return -ENODEV; + } + + return cts_fw_reg_writeb_retry(cts_dev, CTS_DEVICE_FW_REG_CMD, cmd, 3, 0); +} + +static int cts_get_touchinfo(struct cts_device *cts_dev, + struct cts_device_touch_info *touch_info) +{ + cts_dbg("Get touch info"); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Get touch info in program mode"); + return -ENODEV; + } + + return cts_tcs_get_touchinfo(cts_dev, touch_info); +} + +static int cts_get_dev_boot_mode(const struct cts_device *cts_dev, + u8 *boot_mode) +{ + int ret; + + if (cts_dev->rtdata.program_mode) + ret = cts_hw_reg_readb_retry(cts_dev, CTS_DEV_HW_REG_CURRENT_MODE, + boot_mode, 5, 10); + else + ret = cts_tcs_read_hw_reg(cts_dev, CTS_DEV_HW_REG_CURRENT_MODE, + boot_mode, 1); + + if (ret) { + cts_err("Read boot mode failed %d", ret); + return ret; + } + + *boot_mode &= CTS_DEV_BOOT_MODE_MASK; + + cts_info("Curr dev boot mode: %u(%s)", *boot_mode, + cts_dev_boot_mode2str(*boot_mode)); + return 0; +} + +static int cts_set_dev_boot_mode(const struct cts_device *cts_dev, u8 boot_mode) +{ + int ret; + + cts_info("Set dev boot mode to %u(%s)", boot_mode, + cts_dev_boot_mode2str(boot_mode)); + + ret = cts_hw_reg_writeb_retry(cts_dev, CTS_DEV_HW_REG_BOOT_MODE, + boot_mode, 5, 5); + if (ret) { + cts_err("Write hw register BOOT_MODE failed %d", ret); + return ret; + } + + return 0; +} + +static int cts_init_fwdata(struct cts_device *cts_dev) +{ + + struct cts_device_fwdata *fwdata = &cts_dev->fwdata; + int ret; + + cts_info("Init firmware data"); + + if (cts_dev->rtdata.program_mode) { + cts_err("Init firmware data while in program mode"); + return -EINVAL; + } + + memset(fwdata, 0, sizeof(*fwdata)); + + ret = cts_tcs_get_fw_ver(cts_dev, &fwdata->version); + if (ret < 0) { + cts_err("get_fw_ver failed"); + return -EINVAL; + } + ret = cts_tcs_get_lib_ver(cts_dev, &fwdata->lib_version); + if (ret < 0) { + cts_err("get_lib_ver failed"); + return -EINVAL; + } + ret = cts_tcs_get_ddi_ver(cts_dev, &fwdata->ddi_version); + if (ret < 0) { + cts_err("get_ddi_ver failed"); + return -EINVAL; + } + ret = cts_tcs_get_res_x(cts_dev, &fwdata->res_x); + if (ret < 0) { + cts_err("get_res_x failed"); + return -EINVAL; + } + ret = cts_tcs_get_res_y(cts_dev, &fwdata->res_y); + if (ret < 0) { + cts_err("get_res_y failed"); + return -EINVAL; + } + ret = cts_tcs_get_rows(cts_dev, &fwdata->rows); + if (ret < 0) { + cts_err("get_rows failed"); + return -EINVAL; + } + ret = cts_tcs_get_cols(cts_dev, &fwdata->cols); + if (ret < 0) { + cts_err("get_cols failed"); + return -EINVAL; + } + ret = cts_tcs_get_flip_x(cts_dev, &fwdata->flip_x); + if (ret < 0) { + cts_err("get_flip_x failed"); + return -EINVAL; + } + ret = cts_tcs_get_flip_y(cts_dev, &fwdata->flip_y); + if (ret < 0) { + cts_err("get_flip_y failed"); + return -EINVAL; + } + ret = cts_tcs_get_swap_axes(cts_dev, &fwdata->swap_axes); + if (ret < 0) { + cts_err("get_swap_axes failed"); + return -EINVAL; + } + ret = cts_tcs_get_int_mode(cts_dev, &fwdata->int_mode); + if (ret < 0) { + cts_err("get_int_mode failed"); + return -EINVAL; + } + ret = cts_tcs_get_int_keep_time(cts_dev, &fwdata->int_keep_time); + if (ret < 0) { + cts_err("get_int_keep_time failed"); + return -EINVAL; + } + + ret = cts_tcs_get_rawdata_target(cts_dev, &fwdata->rawdata_target); + if (ret < 0) { + cts_err("get_rawdata_target failed"); + return -EINVAL; + } + + ret = cts_tcs_get_esd_method(cts_dev, &fwdata->esd_method); + if (ret < 0) { + cts_err("get_esd_method failed"); + return -EINVAL; + } + + ret = cts_tcs_get_has_int_data(cts_dev, + &fwdata->has_int_data); + if (ret < 0) { + cts_err("get_has_int_data failed: %d", ret); + return -EINVAL; + } + + if (fwdata->has_int_data) { + ret = cts_tcs_get_int_data_method(cts_dev, + &fwdata->int_data_method); + if (ret < 0) { + cts_err("get_int_data_method failed: %d", ret); + return -EINVAL; + } + if (fwdata->int_data_method >= INT_DATA_METHOD_CNT) { + cts_err("Invalid int data method: %d", + fwdata->int_data_method); + return -EINVAL; + } + + ret = cts_tcs_get_int_data_types(cts_dev, + &fwdata->int_data_types); + if (ret < 0) { + cts_err("get_int_data_types failed: %d", ret); + return -EINVAL; + } + fwdata->int_data_types &= INT_DATA_TYPE_MASK; + + ret = cts_tcs_calc_int_data_size(cts_dev); + if (ret < 0) { + cts_err("calc_int_data_size failed: %d", ret); + return -EINVAL; + } + } + + cts_err("fwver: %04x", fwdata->version); + cts_err("libver: %04x", fwdata->lib_version); + cts_err("ddi_version: %02x", fwdata->ddi_version); + cts_err("res_x: %d", fwdata->res_x); + cts_err("res_y: %d", fwdata->res_y); + cts_err("rows: %d", fwdata->rows); + cts_err("cols: %d", fwdata->cols); + cts_err("flip_x: %d", fwdata->flip_x); + cts_err("flip_y: %d", fwdata->flip_y); + cts_err("swap_axes: %d", fwdata->swap_axes); + cts_err("int_mode: %d", fwdata->int_mode); + cts_err("int_keep_time: %d", fwdata->int_keep_time); + cts_err("rawdata_target: %d", fwdata->rawdata_target); + cts_err("esd_method: %d", fwdata->esd_method); + cts_err("has_int_data: %d", fwdata->has_int_data); + cts_err("int_data_method: %d", fwdata->int_data_method); + cts_err("int_data_types: %d", fwdata->int_data_types); + cts_err("int_data_size: %ld", fwdata->int_data_size); + + return 0; +} + +#ifdef CFG_CTS_FW_LOG_REDIRECT +void cts_show_fw_log(struct cts_device *cts_dev) +{ + u8 len, max_len; + int ret; + u8 *data; + + max_len = cts_plat_get_max_fw_log_size(cts_dev->pdata); + data = cts_plat_get_fw_log_buf(cts_dev->pdata, max_len); + ret = cts_fw_reg_readsb(cts_dev, CTS_DEVICE_FW_REG_TOUCH_INFO + 1, &len, 1); + if (ret) { + cts_err("Get i2c print buf len error"); + return; + } + if (len >= max_len) + len = max_len - 1; + + ret = cts_fw_reg_readsb(cts_dev, CTS_DEVICE_FW_REG_TOUCH_INFO + 2, data, len); + if (ret) { + cts_err("Get i2c print buf error"); + return; + } + data[len] = '\0'; + printk("CTS-FW_LOG %s", data); + cts_fw_log_show_finish(cts_dev); +} +#endif + +#ifdef CONFIG_CTS_TP_PROXIMITY +static bool cts_is_proximity_enable(struct cts_device *cts_dev) +{ + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + return !!status->proximity; +} + +static int cts_enable_proximity_mode(struct cts_device *cts_dev) +{ + return cts_tcs_set_proximity_mode(cts_dev, 1); +} + +static int cts_exit_proximity_mode(struct cts_device *cts_dev) +{ + return cts_tcs_set_proximity_mode(cts_dev, 0); +} + +static void cts_handle_proximity_event(bool status) +{ + cts_info("Handler proximity '%s' event", status ? "Near" : "Far"); +} +#endif + +int cts_irq_handler(struct cts_device *cts_dev) +{ + struct cts_device_touch_info *touch_info; + u8 pwrmode = 3; + int ret; + + cts_dbg("Enter IRQ handler"); + + if (cts_dev->rtdata.program_mode) { + cts_err("IRQ triggered in program mode"); + return -EINVAL; + } + + touch_info = &cts_dev->rtdata.touch_info; + ret = cts_get_touchinfo(cts_dev, touch_info); + if (ret) { + cts_err("Get touch info failed %d", ret); + return ret; + } + + if (unlikely(cts_dev->rtdata.suspended)) { +#ifdef CFG_CTS_GESTURE + if (cts_dev->rtdata.gesture_wakeup_enabled) { + struct cts_device_gesture_info *gesture_info; + + gesture_info = &cts_dev->rtdata.gesture_info; + memcpy(gesture_info, touch_info, sizeof(struct cts_device_gesture_info)); + + ret = cts_plat_process_gesture_info(cts_dev->pdata, gesture_info); + + if (cts_dev->fwdata.int_data_method != INT_DATA_METHOD_HOST) { + if (ret) + cts_err("Process gesture info failed %d", ret); + + ret = cts_tcs_set_pwr_mode(cts_dev, pwrmode); + if (ret) + cts_warn("Reenter suspend with gesture wakeup failed %d", ret); + + if (cts_tcs_clr_gstr_ready_flag(cts_dev)) + cts_err("Clear gesture ready flag failed"); + } + } else { + cts_warn("IRQ triggered while device suspended " + "without gesture wakeup enable"); + } +#endif /* CFG_CTS_GESTURE */ + } else { + cts_dbg("Touch info: vkey_state %x, num_msg %u", + touch_info->vkey_state, touch_info->num_msg); + +#ifdef CFG_CTS_PALM_DETECT + if (CFG_CTS_PALM_ID == touch_info->vkey_state) { + cts_report_palm_event(cts_dev->pdata); + cts_plat_release_all_touch(cts_dev->pdata); + return 0; + } +#endif + +#ifdef CONFIG_CTS_TP_PROXIMITY + if (cts_is_proximity_enable(cts_dev)) { + cts_dbg("proximity status:%d", cts_dev->rtdata.proximity_status); + if (!cts_dev->rtdata.proximity_status + && touch_info->vkey_state == CTS_CMD_PROXIMITY_STATUS) { + cts_dev->rtdata.proximity_num++; + if (cts_dev->rtdata.proximity_num == 4) { + cts_dev->rtdata.proximity_num = 0; + cts_dev->rtdata.proximity_status = true; + cts_handle_proximity_event(true); + return 0; + } + } else if (cts_dev->rtdata.proximity_status + && (touch_info->vkey_state == 0)) { + cts_dev->rtdata.proximity_num = 0; + cts_dev->rtdata.proximity_status = false; + cts_handle_proximity_event(false); + cts_plat_release_all_touch(cts_dev->pdata); + return 0; + } + } +#endif + + ret = cts_plat_process_touch_msg(cts_dev->pdata, touch_info->msgs, + touch_info->num_msg); + if (ret) { + cts_err("Process touch msg failed %d", ret); + return ret; + } +#ifdef CONFIG_CTS_VIRTUALKEY + ret = cts_plat_process_vkey(cts_dev->pdata, touch_info->vkey_state); + if (ret) { + cts_err("Process vkey failed %d", ret); + return ret; + } +#endif + } + + return 0; +} + +bool cts_is_device_suspended(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.suspended; +} + +int cts_suspend_device(struct cts_device *cts_dev) +{ + int ret; + u8 buf; +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + struct chipone_ts_data *cts_data = container_of(cts_dev, + struct chipone_ts_data, cts_dev); +#endif + cts_info("Suspend device"); + +/* Disable this check for sleep/gesture switch */ +/* + if (cts_dev->rtdata.suspended) { + cts_warn("Suspend device while already suspended"); + return 0; + } +*/ + if (cts_dev->rtdata.program_mode) { + cts_info("Quit programming mode before suspend"); + ret = cts_enter_normal_mode(cts_dev); + if (ret) { + cts_err("Failed to exit program mode before suspend:%d", ret); + return ret; + } + } + +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + cts_tcs_set_gesture_en_mask(cts_dev, cts_data->d_tap_flag, cts_data->s_tap_flag); +#endif + + cts_info("Set suspend mode:%s", + cts_dev->rtdata.gesture_wakeup_enabled ? "gesture" : "sleep"); + + buf = cts_dev->rtdata.gesture_wakeup_enabled ? 3 : 2; + ret = cts_tcs_set_pwr_mode(cts_dev, buf); + if (ret) { + cts_err("Suspend device failed %d", ret); + return ret; + } + + cts_info("Device suspended ..."); + cts_dev->rtdata.suspended = true; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + cts_info("rst -------- high"); + cts_plat_set_reset(cts_dev->pdata, 1); + cts_info("rst -------- high++"); +#endif + return 0; +} + +int cts_resume_device(struct cts_device *cts_dev) +{ + u8 data[4] = {'R', 'S', 'T', '!'}; + int retries = 3; + int ret = 0; + + cts_info("Resume device"); + + /* Check whether device is in normal mode */ + while (--retries >= 0) { +#ifdef CFG_CTS_HAS_RESET_PIN + ret = cts_tcs_write_hw_reg(cts_dev, CTS_DEV_HW_REG_SET_RESET, + data, sizeof(data)); + if (ret) { + cts_err("write RST failed"); + } + cts_reset_device(cts_dev); +#endif + cts_set_normal_addr(cts_dev); + + if (cts_plat_is_normal_mode(cts_dev->pdata)) { + break; + } + } + + if (retries < 0) { + const struct cts_firmware *firmware = NULL; + + cts_info("Need update firmware when resume"); + +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD + if (cts_dev->config_fw_name[0] != '\0') { + firmware = cts_request_firmware_from_fs(cts_dev, cts_dev->config_fw_name); + } +#else + firmware = cts_request_firmware(cts_dev, cts_dev->hwdata->hwid, + cts_dev->hwdata->fwid, 0); +#endif + if (firmware) { + ret = cts_update_firmware(cts_dev, firmware, false); + cts_release_firmware(firmware); + + if (ret) { + cts_err("Update default firmware failed %d", ret); + goto err_set_program_mode; + } + } else { + cts_err("Request default firmware failed %d, " + "please update manually!!", ret); + + goto err_set_program_mode; + } + } +#ifdef CONFIG_CTS_CHARGER_DETECT + if (cts_is_charger_exist(cts_dev)) { + int r = cts_set_dev_charger_attached(cts_dev, true); + if (r) + cts_err("Set dev charger attached failed %d", r); + } +#endif + +#ifdef CONFIG_CTS_EARJACK_DETECT + if (cts_is_earjack_exist(cts_dev)) { + int r = cts_set_dev_earjack_attached(cts_dev, true); + if (r) { + cts_err("Set dev earjack attached failed %d", r); + } + } +#endif + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) + cts_enter_glove_mode(cts_dev); +#endif + +#ifdef CFG_CTS_FW_LOG_REDIRECT + if (cts_is_fw_log_redirect(cts_dev)) + cts_enable_fw_log_redirect(cts_dev); +#endif + +#ifdef CONFIG_CTS_TP_PROXIMITY + if (cts_is_proximity_enable(cts_dev)) { + cts_enable_proximity_mode(cts_dev); + cts_info("after update firmware, enable proximity mode."); + } +#endif + + cts_dev->rtdata.suspended = false; + return 0; + +err_set_program_mode: + cts_dev->rtdata.program_mode = true; + cts_dev->rtdata.slave_addr = CTS_DEV_PROGRAM_MODE_I2CADDR; + cts_dev->rtdata.addr_width = CTS_DEV_PROGRAM_MODE_ADDR_WIDTH; + + return ret; +} + +bool cts_is_device_program_mode(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.program_mode; +} + +static inline void cts_init_rtdata_with_normal_mode(struct cts_device *cts_dev) +{ + memset(&cts_dev->rtdata, 0, sizeof(cts_dev->rtdata)); + + cts_set_normal_addr(cts_dev); + cts_dev->rtdata.suspended = false; + cts_dev->rtdata.updating = false; + cts_dev->rtdata.testing = false; + cts_dev->rtdata.fw_log_redirect_enabled = false; + cts_dev->rtdata.glove_mode_enabled = false; + cts_dev->rtdata.esd_count = 0; +#ifdef CONFIG_CTS_TP_PROXIMITY + cts_dev->rtdata.proximity_num = 0; + cts_dev->rtdata.proximity_status = false; +#endif +} + +#ifdef FIX_DEFAULT_ENABLE_DRW +extern int cts_spi_send_recv(struct cts_platform_data *pdata, size_t len, u8 *tx_buffer, u8 *rx_buffer); + +static int cts_disable_drw(struct cts_device *cts_dev) +{ + int ret; + + u8 txbuf[] = { + 0x60, + 0x07, 0x80, 0x34, + 0x00, 0x00, 0x04, + 0xdd, 0xe8, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0xdf, 0xfc, + 0x00, 0x00, 0x00 + }; + u8 rxbuf[] = { + 0x60, + 0x07, 0x80, 0x34, + 0x00, 0x00, 0x04, + 0xdd, 0xe8, + 0x00, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0xdf, 0xfc, + 0x00, 0x00, 0x00 + }; + + ret = cts_spi_send_recv(cts_dev->pdata, sizeof(txbuf), txbuf, rxbuf); + if (ret) + cts_err("disable drw ret=%d", ret); + return ret; +} +#endif + +int cts_enter_program_mode(struct cts_device *cts_dev) +{ + const static u8 magic_num[] = { 0xCC, 0x33, 0x55, 0x5A }; + u8 boot_mode; + int ret; + + cts_info("Enter program mode"); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Enter program mode while alredy in"); + /* return 0; */ + } + +#ifdef CONFIG_CTS_I2C_HOST + ret = cts_plat_i2c_write(cts_dev->pdata, + CTS_DEV_PROGRAM_MODE_I2CADDR, magic_num, 4, 5, 10); + if (ret) { + cts_err("Write magic number to i2c_dev: 0x%02x failed %d", + CTS_DEV_PROGRAM_MODE_I2CADDR, ret); + return ret; + } + + cts_set_program_addr(cts_dev); + /* Write i2c deglitch register */ + ret = cts_hw_reg_writeb_retry(cts_dev, CTS_DEV_HW_REG_I2C_CFG, 0x0F, 5, 1); + if (ret) + cts_err("Write i2c deglitch register failed\n"); +#else + cts_set_program_addr(cts_dev); + ret = cts_spi_send_recv(cts_dev->pdata, sizeof(magic_num), (u8 *)magic_num, NULL); + if (ret) { + cts_err("Write magic number to i2c_dev: 0x%02x failed %d", + CTS_DEV_PROGRAM_MODE_SPIADDR, ret); + return ret; + } +#endif /* CONFIG_CTS_I2C_HOST */ + + cts_dev->rtdata.program_mode = true; + mdelay(5); + +#ifdef FIX_DEFAULT_ENABLE_DRW + cts_disable_drw(cts_dev); +#endif + + ret = cts_get_dev_boot_mode(cts_dev, &boot_mode); + if (ret) { + cts_err("Read BOOT_MODE failed %d", ret); + return ret; + } + /* Note: the following CTS_DEV_BOOT_MODE_TCH_PRG_9916 + is used by both ICNL9916 and ICNL9916C */ +#ifdef CONFIG_CTS_I2C_HOST + if ((boot_mode == CTS_DEV_BOOT_MODE_TCH_PRG_9916) || + (boot_mode == CTS_DEV_BOOT_MODE_I2C_PRG_9911C)) +#else + if ((boot_mode == CTS_DEV_BOOT_MODE_TCH_PRG_9916) || + (boot_mode == CTS_DEV_BOOT_MODE_SPI_PRG_9911C)) +#endif + { + return 0; + } + cts_err("BOOT_MODE readback %u != I2C/SPI PROMGRAM mode", boot_mode); + return -EFAULT; +} + +const char *cts_dev_boot_mode2str(u8 boot_mode) +{ + switch (boot_mode) { + case CTS_DEV_BOOT_MODE_IDLE: + return "IDLE-BOOT"; + case CTS_DEV_BOOT_MODE_FLASH: + return "FLASH-BOOT"; + case CTS_DEV_BOOT_MODE_SRAM: + return "SRAM-BOOT"; + /* case CTS_DEV_BOOT_MODE_I2C_PRG_9911C: */ + case CTS_DEV_BOOT_MODE_TCH_PRG_9916: + return "I2C-PRG-BOOT/TCH-PRG-BOOT"; + case CTS_DEV_BOOT_MODE_DDI_PRG: + return "DDI-PRG-BOOT"; + case CTS_DEV_BOOT_MODE_SPI_PRG_9911C: + return "SPI-PROG-BOOT/INVALID-BOOT"; + default: + return "INVALID"; + } +} + +int cts_enter_normal_mode(struct cts_device *cts_dev) +{ + int ret = 0; + u8 boot_mode; + int retries; + u16 fwid = CTS_DEV_FWID_INVALID; + u8 auto_boot = 0; + u8 first_boot = 1; + + cts_info("Enter normal mode"); + + if (!cts_dev->rtdata.program_mode) { + cts_warn("Enter normal mode while already in"); + return 0; + } + + if (cts_dev->rtdata.has_flash) + auto_boot = 1; + +#ifdef CFG_CTS_UPDATE_CRCCHECK + auto_boot = 1; +#endif + for (retries = 5; retries >= 0; retries--) { + if (first_boot == 1 || auto_boot == 0) { + cts_set_program_addr(cts_dev); + ret = cts_set_dev_boot_mode(cts_dev, CTS_DEV_BOOT_MODE_SRAM); + if (ret) { + cts_err("Set BOOT_MODE to SRAM failed %d," + "try to reset device", ret); + } + + mdelay(30); + } + first_boot = 0; + + cts_set_normal_addr(cts_dev); + + ret = cts_get_dev_boot_mode(cts_dev, &boot_mode); + if (ret) + cts_err("Get BOOT_MODE failed %d", ret); + if (boot_mode != CTS_DEV_BOOT_MODE_SRAM) + cts_err("Curr boot mode %u(%s) != SRAM_BOOT", + boot_mode, cts_dev_boot_mode2str(boot_mode)); + else + break; + + ret = cts_tcs_get_fw_id(cts_dev, &fwid); + if (ret) + cts_err("Get firmware id failed %d, retries %d", ret, retries); + else { + cts_info("Get firmware id: 0x%02x", fwid); + break; + } + cts_reset_device(cts_dev); + } + if (retries >= 0) { + ret = cts_init_fwdata(cts_dev); + if (ret) { + cts_err("Device init firmware data failed %d", ret); + return ret; + } + return 0; + } + cts_set_program_addr(cts_dev); + return ret; +} + +bool cts_is_device_enabled(const struct cts_device *cts_dev) +{ + return cts_dev->enabled; +} + +int cts_start_device(struct cts_device *cts_dev) +{ +#if defined(CONFIG_CTS_ESD_PROTECTION) || \ + defined(CONFIG_CTS_CHARGER_DETECT) || \ + defined(CONFIG_CTS_EARJACK_DETECT) + struct chipone_ts_data *cts_data = + container_of(cts_dev, struct chipone_ts_data, cts_dev); +#endif + int ret; + + cts_info("Start device..."); + + if (cts_is_device_enabled(cts_dev)) { + cts_warn("Start device while already started"); + return 0; + } + +#ifdef CFG_CTS_HEARTBEAT_MECHANISM + cts_enable_heartbeat_mechanism(cts_data); +#endif + +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_enable_esd_protection(cts_data); +#endif + +#ifdef CONFIG_CTS_CHARGER_DETECT + cts_start_charger_detect(cts_data); +#endif + +#ifdef CONFIG_CTS_EARJACK_DETECT + cts_start_earjack_detect(cts_data); +#endif + + ret = cts_plat_enable_irq(cts_dev->pdata); + if (ret < 0) { + cts_err("Enable IRQ failed %d", ret); + return ret; + } + + cts_dev->enabled = true; + + cts_info("Start device successfully"); + + return 0; +} + +int cts_stop_device(struct cts_device *cts_dev) +{ + struct chipone_ts_data *cts_data = + container_of(cts_dev, struct chipone_ts_data, cts_dev); + int ret; + + cts_info("Stop device..."); + + if (!cts_is_device_enabled(cts_dev)) { + cts_warn("Stop device while halted"); + return 0; + } + + if (cts_is_firmware_updating(cts_dev)) { + cts_warn("Stop device while firmware updating, please try again"); + return -EAGAIN; + } + + ret = cts_plat_disable_irq(cts_dev->pdata); + if (ret < 0) { + cts_err("Disable IRQ failed %d", ret); + return ret; + } + + cts_dev->enabled = false; + +#ifdef CFG_CTS_HEARTBEAT_MECHANISM + cts_disable_heartbeat_mechanism(cts_data); +#endif + +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_disable_esd_protection(cts_data); +#endif + +#ifdef CONFIG_CTS_CHARGER_DETECT + cts_stop_charger_detect(cts_data); +#endif + +#ifdef CONFIG_CTS_EARJACK_DETECT + cts_stop_earjack_detect(cts_data); +#endif + +#ifndef CONFIG_GENERIC_HARDIRQS + if (work_pending(&cts_data->pdata->ts_irq_work)) { + cts_warn("IRQ work is pending .... flush it"); + flush_work(&cts_data->pdata->ts_irq_work); + } else + cts_info("None IRQ work is pending"); +#endif + cts_info("Flush workqueue..."); + if (!cts_dev->rtdata.update_work_run) + flush_workqueue(cts_data->workqueue); + + ret = cts_plat_release_all_touch(cts_dev->pdata); + if (ret) { + cts_err("Release all touch failed %d", ret); + return ret; + } +#ifdef CONFIG_CTS_VIRTUALKEY + ret = cts_plat_release_all_vkey(cts_dev->pdata); + if (ret) { + cts_err("Release all vkey failed %d", ret); + return ret; + } +#endif + + return 0; +} + +#ifdef CONFIG_CTS_ESD_PROTECTION +int cts_start_device_esdrecover(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Start device..."); + + if (cts_is_device_enabled(cts_dev)) { + cts_warn("Start device while already started"); + return 0; + } + + ret = cts_plat_enable_irq(cts_dev->pdata); + if (ret < 0) { + cts_err("Enable IRQ failed %d", ret); + return ret; + } + + cts_dev->enabled = true; + + cts_info("Start device successfully"); + + return 0; +} + +int cts_stop_device_esdrecover(struct cts_device *cts_dev) +{ + struct chipone_ts_data *cts_data = + container_of(cts_dev, struct chipone_ts_data, cts_dev); + int ret; + + cts_info("Stop device..."); + + if (!cts_is_device_enabled(cts_dev)) { + cts_warn("Stop device while halted"); + return 0; + } + + if (cts_is_firmware_updating(cts_dev)) { + cts_warn("Stop device while firmware updating, please try again"); + return -EAGAIN; + } + + ret = cts_plat_disable_irq(cts_dev->pdata); + if (ret < 0) { + cts_err("Disable IRQ failed %d", ret); + return ret; + } + + cts_dev->enabled = false; + + flush_workqueue(cts_data->workqueue); + + ret = cts_plat_release_all_touch(cts_dev->pdata); + if (ret) { + cts_err("Release all touch failed %d", ret); + return ret; + } +#ifdef CONFIG_CTS_VIRTUALKEY + ret = cts_plat_release_all_vkey(cts_dev->pdata); + if (ret) { + cts_err("Release all vkey failed %d", ret); + return ret; + } +#endif /* CONFIG_CTS_VIRTUALKEY */ + + return 0; +} +#endif + +bool cts_is_fwid_valid(u16 fwid) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cts_device_hwdatas); i++) { + if (cts_device_hwdatas[i].fwid == fwid) + return true; + } + + return false; +} + +static bool cts_is_hwid_valid(u32 hwid) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cts_device_hwdatas); i++) { + if (cts_device_hwdatas[i].hwid == hwid) + return true; + } + + return false; +} + +int cts_get_hwid(struct cts_device *cts_dev, u32 *hwid) +{ + int ret; + + cts_info("Get device hardware id"); + + if (cts_dev->hwdata) { + *hwid = cts_dev->hwdata->hwid; + return 0; + } + + cts_info("Device hardware data not initialized, try to read from register"); + + if (!cts_dev->rtdata.program_mode) { + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + goto err_out; + } + } + + ret = cts_hw_reg_readl_retry(cts_dev, CTS_DEV_HW_REG_HARDWARE_ID, + hwid, 5, 0); + if (ret) + goto err_out; + + *hwid = le32_to_cpu(*hwid); + *hwid &= 0XFFFFFFF0; + cts_info("Device hardware id: %04x", *hwid); + + if (!cts_is_hwid_valid(*hwid)) { + cts_warn("Device hardware id %04x invalid", *hwid); + ret = -EINVAL; + goto err_out; + } + + return 0; + +err_out: + *hwid = CTS_DEV_HWID_INVALID; + return ret; +} + +int cts_probe_device(struct cts_device *cts_dev) +{ + int ret, retries = 0; + u16 fwid = CTS_DEV_FWID_INVALID; + u32 hwid = CTS_DEV_HWID_INVALID; + + cts_dev->fwdata.version = 0; + + cts_info("Probe device"); + + cts_init_rtdata_with_normal_mode(cts_dev); + if (!cts_plat_is_normal_mode(cts_dev->pdata)) { + cts_warn("Normal mode spi addr is offline"); + } else { + ret = cts_tcs_get_fw_id(cts_dev, &fwid); + if (ret) { + cts_err("Get firmware id failed %d", ret); + } else { + cts_info("Firmware id: 0x%04x", fwid); + ret = cts_tcs_get_fw_ver(cts_dev, &cts_dev->fwdata.version); + if (ret) { + cts_err("Read firmware version failed %d", ret); + cts_dev->fwdata.version = 0; + } else { + cts_info("Firmware version: 0x%04x", cts_dev->fwdata.version); + } + goto init_hwdata; + } + } + +read_hwid: + ret = cts_get_hwid(cts_dev, &hwid); + if (ret || hwid == CTS_DEV_HWID_INVALID) { + retries++; + + cts_err("Get hardware id failed %d retries %d", ret, retries); + + if (retries < 3) { + cts_reset_device(cts_dev); + goto read_hwid; + } else + return -ENODEV; + } + +init_hwdata: + ret = cts_init_device_hwdata(cts_dev, hwid, fwid); + if (ret) { + cts_err("Device hwid: %06x fwid: %04x not found", hwid, fwid); + return -ENODEV; + } + + cts_info("Touch info size:%zu", sizeof(struct cts_device_touch_info)); + + return ret; +} + +#ifdef CFG_CTS_GESTURE +int enter_gesture_pocket_mode(struct cts_device *cts_dev) +{ + int ret = 0; + u8 pockmode = 2; + u16 fwid = CTS_DEV_FWID_INVALID; + + cts_err("enter gesture_pocket_mode start"); + if (cts_dev->rtdata.gesture_wakeup_enabled) { + if (cts_dev->fwdata.int_data_method != INT_DATA_METHOD_HOST) { + ret = cts_tcs_get_fw_id(cts_dev, &fwid); + cts_warn("Get firmware id: 0x%02x", fwid); + if (ret){ + cts_err("enter ready gesture_pocket_mode failed"); + } + ret = cts_tcs_set_pwr_mode(cts_dev, pockmode); + if (ret){ + cts_warn("enter gesture_pocket_mode failed %d", ret); + } + cts_err("enter gesture_pocket_mode success"); + } + } else { + cts_warn("enter Not enabled to pocket mode"); + } + return ret; +} + +int exit_gesture_pocket_mode(struct cts_device *cts_dev) +{ + int ret; + u8 pockmode_exit = 3; + u16 fwid = CTS_DEV_FWID_INVALID; + + cts_err("exit gesture_pocket_mode start"); + ret = cts_tcs_get_fw_id(cts_dev, &fwid); + cts_warn("Get firmware id: 0x%02x", fwid); + ret = cts_tcs_set_pwr_mode(cts_dev, pockmode_exit); + if (ret){ + cts_warn("exit gesture_pocket_mode_exit failed %d", ret); + } + cts_err("exit gesture_pocket_mode success"); + return ret; +} + +void cts_enable_gesture_wakeup(struct cts_device *cts_dev) +{ + cts_info("Enable gesture wakeup"); + cts_dev->rtdata.gesture_wakeup_enabled = true; +} + +void cts_disable_gesture_wakeup(struct cts_device *cts_dev) +{ + cts_info("Disable gesture wakeup"); + cts_dev->rtdata.gesture_wakeup_enabled = false; +} + +bool cts_is_gesture_wakeup_enabled(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.gesture_wakeup_enabled; +} + +int cts_get_gesture_info(struct cts_device *cts_dev, void *gesture_info) +{ + int ret; + + cts_info("Get gesture info"); + + if (cts_dev->rtdata.program_mode) { + cts_warn("Get gesture info in program mode"); + return -ENODEV; + } + + if (!cts_dev->rtdata.suspended) { + cts_warn("Get gesture info while not suspended"); + return -ENODEV; + } + + if (!cts_dev->rtdata.gesture_wakeup_enabled) { + cts_warn("Get gesture info while gesture wakeup not enabled"); + return -ENODEV; + } + + ret = cts_tcs_get_gestureinfo(cts_dev, gesture_info); + if (ret) { + cts_err("Read gesture info header failed %d", ret); + return ret; + } + + return 0; +} +#endif /* CFG_CTS_GESTURE */ + +int cts_set_int_data_types(struct cts_device *cts_dev, u16 types) +{ + int ret; + u16 realtypes = types & INT_DATA_TYPE_MASK; + + cts_info("Set int data types: %#06x, mask to %#06x", types, realtypes); + + ret = cts_tcs_set_int_data_types(cts_dev, realtypes); + if (ret) { + cts_err("Set int data type failed: %d", ret); + return -EIO; + } + cts_dev->fwdata.int_data_types = realtypes; + + cts_tcs_calc_int_data_size(cts_dev); + + return ret; +} + +int cts_set_int_data_method(struct cts_device *cts_dev, u8 method) +{ + int ret = 0; + + cts_info("Set int data method: %d", method); + + if (method >= INT_DATA_METHOD_CNT) { + cts_err("Invalid int data method"); + return -EINVAL; + } + + ret = cts_tcs_set_int_data_method(cts_dev, method); + if (ret) { + cts_err("Set int data method failed: %d", ret); + return -EIO; + } + cts_dev->fwdata.int_data_method = method; + + cts_tcs_calc_int_data_size(cts_dev); + + return ret; +} + +#ifdef CONFIG_CTS_ESD_PROTECTION +static void cts_esd_protection_work(struct work_struct *work) +{ + struct chipone_ts_data *cts_data; + int ret = 0; + + cts_dbg("ESD protection work"); + cts_data = container_of(work, struct chipone_ts_data, esd_work.work); + if (!cts_is_device_enabled(&cts_data->cts_dev)) { + cts_info("stop esd check when suspend"); + return; + } + + cts_lock_device(&cts_data->cts_dev); + if (!cts_plat_is_normal_mode(cts_data->pdata)) { + cts_data->esd_check_fail_cnt++; + /*reset chip next time */ + if ((cts_data->esd_check_fail_cnt % 2) == 0) { + cts_err("ESD protection read normal mode failed, reset chip!"); + ret = cts_reset_device(&cts_data->cts_dev); + if (ret) + cts_err("ESD protection reset chip failed %d", ret); + } + } else { + cts_data->esd_check_fail_cnt = 0; + } + + if (cts_data->esd_check_fail_cnt >= CFG_CTS_ESD_FAILED_CONFIRM_CNT) { + const struct cts_firmware *firmware; + + cts_warn("ESD protection check failed, update firmware!!!"); + cts_stop_device_esdrecover(&cts_data->cts_dev); + firmware = cts_request_firmware(&cts_data->cts_dev, + cts_data->cts_dev.hwdata->hwid, cts_data->cts_dev.hwdata->fwid, 0); + if (firmware) { + ret = cts_update_firmware(&cts_data->cts_dev, firmware, false); + cts_release_firmware(firmware); + + if (ret) + cts_err("Update default firmware failed %d", ret); + } else + cts_err("Request default firmware failed %d, " + "please update manually!!", ret); + + cts_start_device_esdrecover(&cts_data->cts_dev); + cts_data->esd_check_fail_cnt = 0; + } + queue_delayed_work(cts_data->esd_workqueue, &cts_data->esd_work, + CFG_CTS_ESD_PROTECTION_CHECK_PERIOD); + + cts_unlock_device(&cts_data->cts_dev); +} + +void cts_enable_esd_protection(struct chipone_ts_data *cts_data) +{ + if (cts_data->esd_workqueue && !cts_data->esd_enabled) { + cts_info("ESD protection enable"); + + cts_data->esd_enabled = true; + cts_data->esd_check_fail_cnt = 0; + queue_delayed_work(cts_data->esd_workqueue, + &cts_data->esd_work, + CFG_CTS_ESD_PROTECTION_CHECK_PERIOD); + } +} + +void cts_disable_esd_protection(struct chipone_ts_data *cts_data) +{ + if (cts_data->esd_workqueue && cts_data->esd_enabled) { + cts_info("ESD protection disable"); + + cts_data->esd_enabled = false; + cancel_delayed_work(&cts_data->esd_work); + flush_workqueue(cts_data->esd_workqueue); + } +} + +void cts_init_esd_protection(struct chipone_ts_data *cts_data) +{ + cts_info("Init ESD protection"); + + INIT_DELAYED_WORK(&cts_data->esd_work, cts_esd_protection_work); + + cts_data->esd_enabled = false; + cts_data->esd_check_fail_cnt = 0; +} + +void cts_deinit_esd_protection(struct chipone_ts_data *cts_data) +{ + cts_info("De-Init ESD protection"); + + if (cts_data->esd_workqueue && cts_data->esd_enabled) { + cts_data->esd_enabled = false; + cancel_delayed_work(&cts_data->esd_work); + } +} +#endif /* CONFIG_CTS_ESD_PROTECTION */ + + +#ifdef CFG_CTS_HEARTBEAT_MECHANISM +void cts_show_touch_debug_msg(void *debug) +{ + struct cts_touch_debug_msg *msg = (struct cts_touch_debug_msg *)debug; + struct cts_firmware_status *status = (struct cts_firmware_status *) + &msg->firmware_status; + + cts_info("FW_Ver : 0x%02X", msg->fw_version); + cts_info("Reset_Flag : 0x%08X", msg->reset_flag); + + cts_info("Charger : %d", status->charger); + cts_info("Proxmty : %d", status->proximity); + cts_info("Earjack : %d", status->earjack); + cts_info("Knuckle : %d", status->knuckle); + cts_info("Glove : %d", status->glove); + cts_info("Pocket : %d", status->pocket); + cts_info("Game : %d", status->game); + + cts_info("High_Temp : %d", status->high_temperature); + cts_info("Low_Temp : %d", status->low_temperature); + cts_info("Bend : %d", status->bend_mode); + cts_info("Palm : %d", status->palm_status); + cts_info("Noise : %d", status->noise_mode); + cts_info("Base_Trace : %d", status->base_trace); + cts_info("Water : %d", status->water_mode); + cts_info("Ground : %d", status->ground); + + cts_info("Proxmity_Sts: 0x%02x", msg->proximity); + + cts_info("Work_Mode : %d", msg->work_mode); + cts_info("Power_Mode : %d", msg->power_mode); + cts_info("Freq : %d", msg->curr_freq); + cts_info("Esd_0a : %d", msg->esd_0a_status); + cts_info("Fw_Esd : %d", msg->fw_esd_status); + cts_info("Landscape : %d", msg->landscape_mode); + + cts_info("Freq_Noise : %d", msg->curr_freq_noise); + cts_info("Max_Diff : %d", msg->max_diff); + cts_info("Row : %d", msg->max_diff_row); + cts_info("Col : %d", msg->max_diff_col); + cts_info("Max_Neg : -%d", msg->max_neg_diff); + cts_info("Neg_Row : %d", msg->max_neg_diff_row); + cts_info("Neg_Col : %d", msg->max_neg_diff_col); +} + +void cts_heartbeat_mechanism_work(struct work_struct *work) +{ + struct chipone_ts_data *cts_data; + struct cts_device *cts_dev; + struct cts_device_touch_info info; + const struct cts_firmware *firmware; + u8 curr_status; + int ret = 0; + + cts_data = container_of(work, struct chipone_ts_data, heart_work.work); + cts_dev = &cts_data->cts_dev; + + cts_dbg("heartbeat mechanism work"); + + cts_lock_device(cts_dev); + ret = cts_tcs_get_touch_status(cts_dev); + if (ret) { + cts_err("get touch status failed"); + + cts_reset_device(cts_dev); + + if (++cts_dev->rtdata.esd_count < 3) + goto end; + + firmware = cts_request_firmware(cts_dev, cts_dev->hwdata->hwid, + cts_dev->hwdata->fwid, 0); + if (firmware) { + ret = cts_update_firmware(&cts_data->cts_dev, firmware, false); + if (ret) + cts_err("Update default firmware failed %d", ret); + cts_release_firmware(firmware); + } + cts_tcs_reinit_fw_status(cts_dev); + goto end; + } + + memcpy(&info, cts_dev->int_data, sizeof(info)); + + curr_status = info.debug_msg.firmware_status; + + cts_dev->rtdata.esd_count = 0; + + /* 9922C reset_flag:0xFFFFFF; 9922 9916 9916C reset_flag:0xFFFF */ + if ((info.debug_msg.reset_flag & 0xFFFFFF) != 0xFFFFFF + || (info.debug_msg.reset_flag & 0xFFFF) != 0xFFFF) { + cts_err("heartbeat reset flag:0x%08x error", info.debug_msg.reset_flag); + cts_show_touch_debug_msg(&info.debug_msg); + } + + if (curr_status != cts_dev->rtdata.firmware_status) { + cts_err("firmware status error"); + cts_show_touch_debug_msg(&info.debug_msg); + cts_tcs_reinit_fw_status(cts_dev); + } + +end: + queue_delayed_work(cts_data->heart_workqueue, + &cts_data->heart_work, msecs_to_jiffies(2000)); + cts_unlock_device(cts_dev); +} +void cts_enable_heartbeat_mechanism(struct chipone_ts_data *cts_data) +{ + queue_delayed_work(cts_data->heart_workqueue, + &cts_data->heart_work, msecs_to_jiffies(2000)); +} +void cts_disable_heartbeat_mechanism(struct chipone_ts_data *cts_data) +{ + cancel_delayed_work_sync(&cts_data->heart_work); +} +#endif + +#ifdef CONFIG_CTS_GLOVE +int cts_enter_glove_mode(const struct cts_device *cts_dev) +{ + int ret; + + cts_info("Enter glove mode"); + + ret = cts_tcs_set_glove_mode(cts_dev, 1); + if (ret) + cts_err("Enable Glove mode err"); + else + cts_dev->rtdata.glove_mode_enabled = true; + + return ret; +} + +int cts_exit_glove_mode(const struct cts_device *cts_dev) +{ + cts_info("Exit glove mode"); + + ret = cts_tcs_set_glove_mode(cts_dev, 0); + if (ret) + cts_err("Exit Glove mode err"); + else + cts_dev->rtdata.glove_mode_enabled = false; + + return ret; +} + +int cts_is_glove_enabled(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.glove_mode_enabled; +} + +#endif /* CONFIG_CTS_GLOVE */ + +#ifdef CONFIG_CTS_CHARGER_DETECT +bool cts_is_charger_exist(struct cts_device *cts_dev) +{ + struct chipone_ts_data *cts_data; + bool attached = false; + int ret; + + cts_data = container_of(cts_dev, struct chipone_ts_data, cts_dev); + + ret = cts_is_charger_attached(cts_data, &attached); + if (ret) + cts_err("Get charger state failed %d", ret); + + cts_dev->rtdata.charger_exist = attached; + + return attached; +} + +int cts_set_dev_charger_attached(struct cts_device *cts_dev, bool attached) +{ + int ret; + u8 buf; + + cts_info("Set dev charger %s", attached ? "ATTACHED" : "DETATCHED"); + buf = attached ? 1 : 0; + + ret = cts_tcs_set_charger_plug(cts_dev, buf); + if (ret) + cts_err("Set failed %d", ret); + + return ret; +} +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_EARJACK_DETECT +bool cts_is_earjack_exist(struct cts_device *cts_dev) +{ + struct chipone_ts_data *cts_data; + bool attached = false; + int ret; + + cts_data = container_of(cts_dev, struct chipone_ts_data, cts_dev); + + ret = cts_is_earjack_attached(cts_data, &attached); + if (ret) { + cts_err("Get earjack state failed %d", ret); + } + + return attached; +} + +int cts_set_dev_earjack_attached(struct cts_device *cts_dev, bool attached) +{ + int ret; + u8 buf; + + cts_info("Set dev earjack %s", attached ? "ATTACHED" : "DETATCHED"); + buf = attached ? 1 : 0; + ret = cts_tcs_set_earjack_plug(cts_dev, buf); + if (ret) + cts_err("Set failed %d", ret); + + return ret; +} +#endif /* CONFIG_CTS_EARJACK_DETECT */ + +int cts_enable_fw_log_redirect(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Fw log redirect enable"); + ret = cts_send_command(cts_dev, CTS_CMD_ENABLE_FW_LOG_REDIRECT); + if (ret) + cts_err("Send CTS_CMD_ENABLE_FW_LOG_REDIRECT failed %d", ret); + else + cts_dev->rtdata.fw_log_redirect_enabled = true; + + return 0; +} + +int cts_disable_fw_log_redirect(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Fw log redirect disable"); + ret = cts_send_command(cts_dev, CTS_CMD_DISABLE_FW_LOG_REDIRECT); + if (ret) + cts_err("Send CTS_CMD_DISABLE_FW_LOG_REDIRECT failed %d", ret); + else + cts_dev->rtdata.fw_log_redirect_enabled = false; + + return 0; +} + +bool cts_is_fw_log_redirect(struct cts_device *cts_dev) +{ + return cts_dev->rtdata.fw_log_redirect_enabled; +} + +int cts_fw_log_show_finish(struct cts_device *cts_dev) +{ + int ret; + + ret = cts_send_command(cts_dev, CTS_CMD_FW_LOG_SHOW_FINISH); + if (ret) + cts_err("Send CTS_CMD_FW_LOG_SHOW_FINISH failed %d", ret); + + return ret; +} + +void cts_firmware_upgrade_work(struct work_struct *work) +{ + struct chipone_ts_data *cts_data; + struct cts_device *cts_dev; + const struct cts_firmware *firmware; + int retries; + int ret = 0; + + cts_info("Firmware upgrade work"); + + cts_data = container_of(work, struct chipone_ts_data, fw_upgrade_work.work); + cts_dev = &cts_data->cts_dev; + + cts_lock_device(cts_dev); + cts_dev->rtdata.update_work_run = true; + firmware = cts_request_firmware(cts_dev, cts_dev->hwdata->hwid, + CTS_DEV_FWID_ANY, cts_dev->fwdata.version); + if (firmware == NULL) { + cts_warn("Request firmware failed"); + /* With flash, do not update. */ + cts_set_program_addr(cts_dev); + cts_enter_normal_mode(cts_dev); + goto end; + } + + retries = 0; + do { + ret = cts_update_firmware(cts_dev, firmware, false); + if (ret) { + cts_err("Update firmware failed times: %d", retries); + cts_reset_device(cts_dev); + } else + break; + } while (++retries < 3); + + cts_release_firmware(firmware); + +end: + if (ret == 0) + cts_start_device(cts_dev); + + cts_dev->rtdata.update_work_run = false; + cts_unlock_device(cts_dev); +} + +int cts_reset_device(struct cts_device *cts_dev) +{ + cts_info("Reset device"); + + return cts_tcs_reset_device(cts_dev); +} + +static struct file *cts_log_filp; +static int cts_log_to_file_level; +extern int cts_write_file(struct file *filp, const void *data, size_t size); + +static char *cts_log_buffer; +static int cts_log_buf_size; +static int cts_log_buf_wr_size; + +static bool cts_log_redirect; + +extern int cts_mkdir_for_file(const char *filepath, umode_t mode); + +int cts_start_driver_log_redirect(const char *filepath, bool append_to_file, + char *log_buffer, int log_buf_size, int log_level) +{ +#define START_BANNER \ + ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" + + int ret = 0; + + cts_info("Start driver log redirect"); + + cts_log_to_file_level = log_level; + + if (log_buffer && log_buf_size) { + cts_info(" - Start driver log to buffer: %p size: %d level: %d", + log_buffer, log_buf_size, log_level); + cts_log_buffer = log_buffer; + cts_log_buf_size = log_buf_size; + cts_log_buf_wr_size = 0; + } + + if (filepath && filepath[0]) { + cts_info(" - Start driver log to file : '%s' level: %d", + filepath, log_level); +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): filp_open is forbiddon with GKI Version!", __func__); +#else + cts_log_filp = filp_open(filepath, + O_WRONLY | O_CREAT | (append_to_file ? O_APPEND : O_TRUNC), + S_IRUGO | S_IWUGO); + if (IS_ERR(cts_log_filp)) { + ret = PTR_ERR(cts_log_filp); + cts_log_filp = NULL; + cts_err("Open file '%s' for driver log failed %d", + filepath, ret); + } else { + cts_write_file(cts_log_filp, START_BANNER, strlen(START_BANNER)); + } +#endif + } + + cts_log_redirect = true; + + return ret; +#undef START_BANNER +} + +void cts_stop_driver_log_redirect(void) +{ +#define END_BANNER \ + "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n" +#ifndef CFG_CTS_FOR_GKI + int ret; +#endif + + cts_log_redirect = false; + + cts_info("Stop driver log redirect"); + + if (cts_log_filp) { + cts_info(" - Stop driver log to file"); + + cts_write_file(cts_log_filp, END_BANNER, strlen(END_BANNER)); +#ifndef CFG_CTS_FOR_GKI + ret = filp_close(cts_log_filp, NULL); + if (ret) { + cts_err("Close driver log file failed %d", ret); + } +#endif + cts_log_filp = NULL; + } + + if (cts_log_buffer) { + cts_info(" - Stop driver log to buffer"); + + cts_log_buffer = NULL; + cts_log_buf_size = 0; + cts_log_buf_wr_size = 0; + } + +#undef END_BANNER +} + +int cts_get_driver_log_redirect_size(void) +{ + if (cts_log_redirect && cts_log_buffer && cts_log_buf_wr_size) { + return cts_log_buf_wr_size; + } else { + return 0; + } +} + +void cts_log(int level, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + + if (cts_log_redirect) { + if (cts_log_buffer && + cts_log_buf_wr_size < cts_log_buf_size && + level <= cts_log_to_file_level) { + cts_log_buf_wr_size += vscnprintf(cts_log_buffer + cts_log_buf_wr_size, + cts_log_buf_size - cts_log_buf_wr_size, fmt, args); + } + + if (cts_log_filp != NULL && level <= cts_log_to_file_level) { + char buf[512]; + int count = vscnprintf(buf, sizeof(buf), fmt, args); + + cts_write_file(cts_log_filp, buf, count); + } + } + + if (level < CTS_DRIVER_LOG_DEBUG || cts_show_debug_log) { + vprintk(fmt, args); + } + + va_end(args); +} + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0) +/* From lib/kstrtox.c */ +/** + * kstrtobool - convert common user inputs into boolean values + * @s: input string + * @res: result + * + * This routine returns 0 iff the first character is one of 'Yy1Nn0', or + * [oO][NnFf] for "on" and "off". Otherwise it will return -EINVAL. Value + * pointed to by res is updated upon finding a match. + */ +int kstrtobool(const char *s, bool *res) +{ + if (!s) + return -EINVAL; + + switch (s[0]) { + case 'y': + case 'Y': + case '1': + *res = true; + return 0; + case 'n': + case 'N': + case '0': + *res = false; + return 0; + case 'o': + case 'O': + switch (s[1]) { + case 'n': + case 'N': + *res = true; + return 0; + case 'f': + case 'F': + *res = false; + return 0; + default: + break; + } + default: + break; + } + + return -EINVAL; +} +#endif diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_core.h b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_core.h new file mode 100644 index 000000000000..623ee119b326 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_core.h @@ -0,0 +1,938 @@ +#ifndef CTS_CORE_H +#define CTS_CORE_H + +#include "cts_config.h" +#include +#include +#ifdef CHIPONE_SENSOR_EN +#include +#endif + +enum cts_dev_hw_reg { +#if defined(CONFIG_CTS_ICTYPE_ICNL9922C) ||\ + defined(CONFIG_CTS_ICTYPE_ICNL9951) + CTS_DEV_HW_REG_HARDWARE_ID = 0x70000u, + CTS_DEV_HW_REG_CLOCK_GATING = 0x70004u, + CTS_DEV_HW_REG_RESET_CONFIG = 0x70008u, + CTS_DEV_HW_REG_BOOT_MODE = 0x70010u, + CTS_DEV_HW_REG_CURRENT_MODE = 0x70011u, + CTS_DEV_HW_REG_DDI_REG_CTRL = 0x7002Cu, + CTS_DEV_HW_REG_CLK_DIV_CFG = 0x70033u, + CTS_DEV_HW_REG_HPPROG = 0x70074u, + CTS_DEV_HW_REG_SET_RESET = 0x700F8u, + CTS_DEV_HW_REG_HW_STATUS = 0x7401Bu, + CTS_DEV_HW_REG_I2C_CFG = 0x77001u, +#else + CTS_DEV_HW_REG_HARDWARE_ID = 0x30000u, + CTS_DEV_HW_REG_CLOCK_GATING = 0x30004u, + CTS_DEV_HW_REG_RESET_CONFIG = 0x30008u, + CTS_DEV_HW_REG_BOOT_MODE = 0x30010u, + CTS_DEV_HW_REG_CURRENT_MODE = 0x30011u, + CTS_DEV_HW_REG_DDI_REG_CTRL = 0x3002Cu, + CTS_DEV_HW_REG_CLK_DIV_CFG = 0x30033u, + CTS_DEV_HW_REG_HPPROG = 0x30074u, + CTS_DEV_HW_REG_SET_RESET = 0x300F8u, + CTS_DEV_HW_REG_HW_STATUS = 0x3401Bu, + CTS_DEV_HW_REG_I2C_CFG = 0x37001u, +#endif +}; + +enum cts_dev_boot_mode { + CTS_DEV_BOOT_MODE_IDLE = 0, + CTS_DEV_BOOT_MODE_FLASH = 1, + CTS_DEV_BOOT_MODE_I2C_PRG_9911C = 2, + CTS_DEV_BOOT_MODE_TCH_PRG_9916 = 2, + CTS_DEV_BOOT_MODE_SRAM = 3, + CTS_DEV_BOOT_MODE_DDI_PRG = 4, + CTS_DEV_BOOT_MODE_SPI_PRG_9911C = 5, + CTS_DEV_BOOT_MODE_MASK = 7, +}; + +/** I2C addresses(7bits), transfer size and bitrate */ +#define CTS_DEV_NORMAL_MODE_I2CADDR (0x48) +#define CTS_DEV_PROGRAM_MODE_I2CADDR (0x30) +#define CTS_DEV_NORMAL_MODE_ADDR_WIDTH (2) +#define CTS_DEV_PROGRAM_MODE_ADDR_WIDTH (3) +#define CTS_DEV_NORMAL_MODE_SPIADDR (0xF0) +#define CTS_DEV_PROGRAM_MODE_SPIADDR (0x60) + +/** Chipone firmware register addresses under normal mode */ +enum cts_device_fw_reg { + CTS_DEVICE_FW_REG_WORK_MODE = 0x0000, + CTS_DEVICE_FW_REG_SYS_BUSY = 0x0001, + CTS_DEVICE_FW_REG_DATA_READY = 0x0002, + CTS_DEVICE_FW_REG_CMD = 0x0004, + CTS_DEVICE_FW_REG_POWER_MODE = 0x0005, + CTS_DEVICE_FW_REG_FW_LIB_MAIN_VERSION = 0x0009, + CTS_DEVICE_FW_REG_CHIP_TYPE = 0x000A, + CTS_DEVICE_FW_REG_VERSION = 0x000C, + CTS_DEVICE_FW_REG_DDI_VERSION = 0x0010, + CTS_DEVICE_FW_REG_GET_WORK_MODE = 0x003F, + CTS_DEVICE_FW_REG_FW_LIB_SUB_VERSION = 0x0047, + CTS_DEVICE_FW_REG_COMPENSATE_CAP_READY = 0x004E, + + CTS_DEVICE_FW_REG_TOUCH_INFO = 0x1000, + CTS_DEVICE_FW_REG_RAW_DATA = 0x2000, + CTS_DEVICE_FW_REG_DIFF_DATA = 0x3000, + CTS_DEVICE_FW_REG_GESTURE_INFO = 0x7000, + + CTS_DEVICE_FW_REG_PANEL_PARAM = 0x8000, + CTS_DEVICE_FW_REG_NUM_TX = 0x8007, + CTS_DEVICE_FW_REG_NUM_RX = 0x8008, + CTS_DEVICE_FW_REG_INT_KEEP_TIME = 0x8047, + CTS_DEVICE_FW_REG_RAWDATA_TARGET = 0x8049, + CTS_DEVICE_FW_REG_X_RESOLUTION = 0x8090, + CTS_DEVICE_FW_REG_Y_RESOLUTION = 0x8092, + CTS_DEVICE_FW_REG_SWAP_AXES = 0x8094, + CTS_DEVICE_FW_REG_GLOVE_MODE = 0x8095, + CTS_DEVICE_FW_REG_TEST_WITH_DISPLAY_ON = 0x80A3, + CTS_DEVICE_FW_REG_INT_MODE = 0x80D8, + CTS_DEVICE_FW_REG_EARJACK_DETECT_SUPP = 0x8113, + CTS_DEVICE_FW_REG_AUTO_COMPENSATE_EN = 0x8114, + CTS_DEVICE_FW_REG_ESD_PROTECTION = 0x8156, + CTS_DEVICE_FW_REG_FLAG_BITS = 0x8158, + + CTS_DEVICE_FW_REG_COMPENSATE_CAP = 0xA000, + CTS_DEVICE_FW_REG_DEBUG_INTF = 0xF000, +}; + +/** Hardware IDs, read from hardware id register */ +enum cts_dev_hwid { + CTS_DEV_HWID_ICNL9911 = 0x990100u, + CTS_DEV_HWID_ICNL9911S = 0x990110u, + CTS_DEV_HWID_ICNL9911C = 0x991110u, + CTS_DEV_HWID_ICNL9916 = 0x990160u, + CTS_DEV_HWID_ICNL9916C = 0x99C160u, + CTS_DEV_HWID_ICNL9922 = 0x990220u, + CTS_DEV_HWID_ICNL9922C = 0x99C220u, + CTS_DEV_HWID_ICNL9951 = 0x990510u, + + CTS_DEV_HWID_ANY = 0, + CTS_DEV_HWID_INVALID = 0xFFFFFFFFu, +}; + +/* Firmware IDs, read from firmware register @ref CTS_DEV_FW_REG_CHIP_TYPE + * under normal mode + */ +enum cts_dev_fwid { + CTS_DEV_FWID_ICNL9911 = 0x9911u, + CTS_DEV_FWID_ICNL9911S = 0x9964u, + CTS_DEV_FWID_ICNL9911C = 0x9954u, + CTS_DEV_FWID_ICNL9916 = 0x9916u, + CTS_DEV_FWID_ICNL9916C = 0x9959u, + CTS_DEV_FWID_ICNL9922 = 0x9922u, + CTS_DEV_FWID_ICNL9922C = 0x9965u, + CTS_DEV_FWID_ICNL9951 = 0x9951u, + + CTS_DEV_FWID_ANY = 0u, + CTS_DEV_FWID_INVALID = 0xFFFFu +}; + +/** Commands written to firmware register @ref CTS_DEVICE_FW_REG_CMD under normal mode */ +enum cts_firmware_cmd { + CTS_CMD_RESET = 1, + CTS_CMD_SUSPEND = 2, + CTS_CMD_ENTER_WRITE_PARA_TO_FLASH_MODE = 3, + CTS_CMD_WRITE_PARA_TO_FLASH = 4, + CTS_CMD_WRTITE_INT_HIGH = 5, + CTS_CMD_WRTITE_INT_LOW = 6, + CTS_CMD_RELASE_INT_TEST = 7, + CTS_CMD_RECOVERY_TX_VOL = 0x10, + CTS_CMD_DEC_TX_VOL_1 = 0x11, + CTS_CMD_DEC_TX_VOL_2 = 0x12, + CTS_CMD_DEC_TX_VOL_3 = 0x13, + CTS_CMD_DEC_TX_VOL_4 = 0x14, + CTS_CMD_DEC_TX_VOL_5 = 0x15, + CTS_CMD_DEC_TX_VOL_6 = 0x16, + CTS_CMD_ENABLE_READ_RAWDATA = 0x20, + CTS_CMD_DISABLE_READ_RAWDATA = 0x21, + CTS_CMD_SUSPEND_WITH_GESTURE = 0x40, + CTS_CMD_QUIT_GESTURE_MONITOR = 0x41, + CTS_CMD_CHARGER_ATTACHED = 0x55, + CTS_CMD_EARJACK_ATTACHED = 0x57, + CTS_CMD_EARJACK_DETACHED = 0x58, + CTS_CMD_CHARGER_DETACHED = 0x66, + CTS_CMD_ENABLE_FW_LOG_REDIRECT = 0x86, + CTS_CMD_DISABLE_FW_LOG_REDIRECT = 0x87, + CTS_CMD_ENABLE_READ_CNEG = 0x88, + CTS_CMD_DISABLE_READ_CNEG = 0x89, + CTS_CMD_FW_LOG_SHOW_FINISH = 0xE0, + +#ifdef CONFIG_CTS_TP_PROXIMITY + CTS_CMD_PROXIMITY_STATUS = 0x80, + /* For vivo code, make a distinction between palm func and proximity func */ + CTS_CMD_PROXIMITY_NEAR_VIVO = 0x30, + CTS_CMD_PROXIMITY_FAR_VIVO = 0x31, +#endif +}; + +#pragma pack(1) +/** Touch message read back from chip */ +struct cts_device_touch_msg { + u8 id; + __le16 x; + __le16 y; + u8 pressure; + u8 event; + +#define CTS_DEVICE_TOUCH_EVENT_NONE (0) +#define CTS_DEVICE_TOUCH_EVENT_DOWN (1) +#define CTS_DEVICE_TOUCH_EVENT_MOVE (2) +#define CTS_DEVICE_TOUCH_EVENT_STAY (3) +#define CTS_DEVICE_TOUCH_EVENT_UP (4) + +}; + +struct cts_firmware_status { + uint16_t charger:1; + uint16_t proximity:1; + uint16_t earjack:1; + uint16_t knuckle:1; + uint16_t glove:1; + uint16_t pocket:1; + uint16_t game:1; + uint16_t reserved:1; + uint16_t high_temperature:1; + uint16_t low_temperature:1; + uint16_t bend_mode:1; + uint16_t palm_status:1; + uint16_t noise_mode:1; + uint16_t base_trace:1; + uint16_t water_mode:1; + uint16_t ground:1; +}; +/** Touch debug messages read back from chip [40 bytes] */ +struct cts_touch_debug_msg { + u8 protocol_version; + u8 fw_version; + u32 reset_flag; + u16 frame_index; + u16 firmware_status; + + u8 proximity; + u8 work_mode; + u8 power_mode; + u8 curr_freq; + u8 esd_0a_status; + u8 fw_esd_status; + u8 landscape_mode; + u16 curr_freq_noise; + u16 max_diff; + u8 max_diff_row; + u8 max_diff_col; + u16 max_neg_diff; + u8 max_neg_diff_row; + u8 max_neg_diff_col; + u8 data_method; + u8 data_type; + u8 reserved[11]; +}; + +/** Touch information read back from chip */ +struct cts_device_touch_info { + u8 vkey_state; + u8 num_msg; + + struct cts_device_touch_msg msgs[CFG_CTS_MAX_TOUCH_NUM]; + struct cts_touch_debug_msg debug_msg; +}; + +/** Gesture trace point read back from chip */ +struct cts_device_gesture_point { + __le16 x; + __le16 y; +}; + +/** Gesture information read back from chip */ +/* total size 112 bytes */ +struct cts_device_gesture_info { + u8 gesture_id; +#define CTS_GESTURE_UP (0x11) +#define CTS_GESTURE_C (0x12) +#define CTS_GESTURE_O (0x13) +#define CTS_GESTURE_M (0x14) +#define CTS_GESTURE_W (0x15) +#define CTS_GESTURE_E (0x16) +#define CTS_GESTURE_S (0x17) +#define CTS_GESTURE_B (0x18) +#define CTS_GESTURE_T (0x19) +#define CTS_GESTURE_H (0x1A) +#define CTS_GESTURE_F (0x1B) +#define CTS_GESTURE_X (0x1C) +#define CTS_GESTURE_Z (0x1D) +#define CTS_GESTURE_V (0x1E) +#define CTS_GESTURE_D_TAP (0x50) +#define CTS_GESTURE_TAP (0x7F) + + u8 num_points; + +#define CTS_CHIP_MAX_GESTURE_TRACE_POINT (27u) + struct cts_device_gesture_point points[CTS_CHIP_MAX_GESTURE_TRACE_POINT]; + u8 reserved[2]; + +}; + +struct cts_tcs_cmd { + uint16_t cmd_id : 8; + uint16_t class_id : 5; + uint16_t is_write : 1; + uint16_t is_read : 1; + uint16_t base_flag : 1; +}; +#pragma pack() + +struct cts_device; + +enum cts_crc_type { + CTS_CRC16 = 1, + CTS_CRC32 = 2, +}; + +/** Chip hardware data, will never change */ +struct cts_device_hwdata { + const char *name; + u32 hwid; + u16 fwid; + u8 num_row; + u8 num_col; + u32 sram_size; + + /* Address width under program mode */ + u8 program_addr_width; + + const struct cts_sfctrl *sfctrl; + int (*enable_access_ddi_reg)(struct cts_device *cts_dev, bool enable); +}; + +enum int_data_type { + INT_DATA_TYPE_NONE = 0, + INT_DATA_TYPE_RAWDATA = BIT(0), + INT_DATA_TYPE_MANUAL_DIFF = BIT(1), + INT_DATA_TYPE_REAL_DIFF = BIT(2), + INT_DATA_TYPE_NOISE_DIFF = BIT(3), + INT_DATA_TYPE_BASEDATA = BIT(4), + INT_DATA_TYPE_CNEGDATA = BIT(5), + INT_DATA_TYPE_MASK = 0x3F, +}; + +enum int_data_method { + INT_DATA_METHOD_NONE = 0, + INT_DATA_METHOD_HOST = 1, + INT_DATA_METHOD_POLLING = 2, + INT_DATA_METHOD_DEBUG = 3, + INT_DATA_METHOD_CNT = 4, +}; + +/** Chip firmware data */ +struct cts_device_fwdata { + u16 version; + u16 res_x; + u16 res_y; + u8 rows; + u8 cols; + bool flip_x; + bool flip_y; + bool swap_axes; + u8 ddi_version; + u8 int_mode; + u8 esd_method; + u16 lib_version; + u16 int_keep_time; + u16 rawdata_target; + bool has_int_data; + u8 int_data_method; + u16 int_data_types; + size_t int_data_size; +}; + +/** Chip runtime data */ +struct cts_device_rtdata { + u8 slave_addr; + int addr_width; + bool program_mode; + bool has_flash; + + bool suspended; + bool updating; + bool testing; + bool update_work_run; + + bool gesture_wakeup_enabled; + bool charger_exist; + bool fw_log_redirect_enabled; + bool glove_mode_enabled; + + u8 esd_count; + u16 firmware_status; + + struct cts_device_touch_info touch_info; + struct cts_device_gesture_info gesture_info; + +#ifdef CONFIG_CTS_TP_PROXIMITY + bool proximity_status; + int proximity_num; +#endif + + u16 tcscmd[ALIGN(PAGE_SIZE + 10, 4)]; + int tcscmd_len; + u16 curr_cmd; + int curr_len; +}; + +struct cts_device { + struct cts_platform_data *pdata; + + const struct cts_device_hwdata *hwdata; + struct cts_device_fwdata fwdata; + struct cts_device_rtdata rtdata; + const struct cts_flash *flash; + bool enabled; +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD + char config_fw_name[CFG_CTS_FW_FILE_NAME_MAX_LEN + 1]; +#endif + u8 int_data[ALIGN(CFG_CTS_INT_DATA_MAX_SIZE + 10, 4)]; +}; + +struct cts_platform_data; + +#define MAX_PANEL_IDX 2 +enum touch_panel_id { + TOUCH_PANEL_IDX_PRIMARY = 0, + TOUCH_PANEL_MAX_IDX, +}; + +#ifdef CHIPONE_SENSOR_EN +/* display state */ +enum display_state { + SCREEN_UNKNOWN, + SCREEN_OFF, + SCREEN_ON, +}; +struct chipone_sensor_platform_data { + struct input_dev *input_sensor_dev; + struct sensors_classdev ps_cdev; + int sensor_opened; + char sensor_data; /* 0 near, 1 far */ + struct chipone_ts_data *data; +}; +#define REPORT_MAX_COUNT 10000 +#endif + +struct chipone_ts_data { +#ifdef CONFIG_CTS_I2C_HOST + struct i2c_client *i2c_client; +#else + struct spi_device *spi_client; +#endif + struct device *device; + struct cts_device cts_dev; + struct cts_platform_data *pdata; + struct workqueue_struct *workqueue; + struct delayed_work fw_upgrade_work; + struct work_struct ts_resume_work; +#ifdef CONFIG_CTS_CHARGER_DETECT + void *charger_detect_data; +#endif + +#ifdef CONFIG_CTS_EARJACK_DETECT + void *earjack_detect_data; +#endif + +#ifdef CONFIG_CTS_ESD_PROTECTION + struct workqueue_struct *esd_workqueue; + struct delayed_work esd_work; + bool esd_enabled; + int esd_check_fail_cnt; +#endif + +#ifdef CFG_CTS_HEARTBEAT_MECHANISM + struct workqueue_struct *heart_workqueue; + struct delayed_work heart_work; +#endif + +#ifdef CONFIG_CTS_LEGACY_TOOL + struct proc_dir_entry *procfs_entry; +#endif + +#ifdef CHIPONE_SENSOR_EN + bool wakeable; + bool should_enable_gesture; + bool gesture_enabled; + uint32_t report_gesture_key; + enum display_state screen_state; + struct mutex state_mutex; + struct chipone_sensor_platform_data *sensor_pdata; +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock gesture_wakelock; +#else + struct wakeup_source *gesture_wakelock; +#endif +#endif + +#ifdef CONFIG_GTP_LAST_TIME + ktime_t last_event_time; +#endif + +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + unsigned char gesture_mode_type; + bool d_tap_flag; + bool s_tap_flag; +#endif +#ifdef CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS + void* notifier_cookie; +#endif + void *oem_data; + + bool force_reflash; + struct kobject *suspend_kobj; + +#ifdef CTS_STOWED_MODE_EN + atomic_t post_suspended; +#endif + +#ifdef TOUCHSCREEN_PM_BRL_SPI + atomic_t pm_resume; + wait_queue_head_t pm_wq; +#endif +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) +static inline u32 get_unaligned_le24(const void *p) +{ + const u8 *puc = (const u8 *)p; + return (puc[0] | (puc[1] << 8) | (puc[2] << 16)); +} + +static inline u32 get_unaligned_be24(const void *p) +{ + const u8 *puc = (const u8 *)p; + return (puc[2] | (puc[1] << 8) | (puc[0] << 16)); +} + +static inline void put_unaligned_be24(u32 v, void *p) +{ + u8 *puc = (u8 *) p; + + puc[0] = (v >> 16) & 0xFF; + puc[1] = (v >> 8) & 0xFF; + puc[2] = (v >> 0) & 0xFF; +} +#endif + +#define wrap(max, x) ((max) - 1 - (x)) + +extern void cts_lock_device(const struct cts_device *cts_dev); +extern void cts_unlock_device(const struct cts_device *cts_dev); + +extern int cts_dev_readb(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay); +extern int cts_dev_writeb(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay); + +extern int cts_sram_writeb_retry(const struct cts_device *cts_dev, + u32 addr, u8 b, int retry, int delay); +extern int cts_sram_writew_retry(const struct cts_device *cts_dev, + u32 addr, u16 w, int retry, int delay); +extern int cts_sram_writel_retry(const struct cts_device *cts_dev, + u32 addr, u32 l, int retry, int delay); +extern int cts_sram_writesb_retry(const struct cts_device *cts_dev, + u32 addr, const void *src, size_t len, int retry, int delay); +extern int cts_sram_writesb_check_crc_retry(const struct cts_device *cts_dev, + u32 addr, const void *src, size_t len, u32 crc, int retry); + +extern int cts_sram_readb_retry(const struct cts_device *cts_dev, + u32 addr, u8 *b, int retry, int delay); +extern int cts_sram_readw_retry(const struct cts_device *cts_dev, + u32 addr, u16 *w, int retry, int delay); +extern int cts_sram_readl_retry(const struct cts_device *cts_dev, + u32 addr, u32 *l, int retry, int delay); +extern int cts_sram_readsb_retry(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay); + +extern int cts_fw_reg_writeb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 b, int retry, int delay); +extern int cts_fw_reg_writew_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 w, int retry, int delay); +extern int cts_fw_reg_writel_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 l, int retry, int delay); +extern int cts_fw_reg_writesb_retry(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len, int retry, int delay); + +extern int cts_fw_reg_readb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b, int retry, int delay); +extern int cts_fw_reg_readw_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w, int retry, int delay); +extern int cts_fw_reg_readl_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l, int retry, int delay); +extern int cts_fw_reg_readsb_retry(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, + int retry, int delay); +extern int cts_fw_reg_readsb_retry_delay_idle(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, int retry, int delay, int idle); + +extern int cts_hw_reg_writeb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 b, int retry, int delay); +extern int cts_hw_reg_writew_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 w, int retry, int delay); +extern int cts_hw_reg_writel_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 l, int retry, int delay); +extern int cts_hw_reg_writesb_retry(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len, int retry, int delay); + +extern int cts_hw_reg_readb_retry(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b, int retry, int delay); +extern int cts_hw_reg_readw_retry(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w, int retry, int delay); +extern int cts_hw_reg_readl_retry(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l, int retry, int delay); +extern int cts_hw_reg_readsb_retry(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, int retry, int delay); + +static inline int cts_hw_reg_readb(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b) +{ + return cts_hw_reg_readb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_hw_reg_writeb(const struct cts_device *cts_dev, + u32 reg_addr, u8 b) +{ + return cts_hw_reg_writeb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_hw_reg_writew(const struct cts_device *cts_dev, + u32 reg_addr, u16 w) +{ + return cts_hw_reg_writew_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_fw_reg_writeb(const struct cts_device *cts_dev, + u32 reg_addr, u8 b) +{ + return cts_fw_reg_writeb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_fw_reg_writew(const struct cts_device *cts_dev, + u32 reg_addr, u16 w) +{ + return cts_fw_reg_writew_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_fw_reg_writel(const struct cts_device *cts_dev, + u32 reg_addr, u32 l) +{ + return cts_fw_reg_writel_retry(cts_dev, reg_addr, l, 1, 0); +} + +static inline int cts_fw_reg_writesb(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len) +{ + return cts_fw_reg_writesb_retry(cts_dev, reg_addr, src, len, 1, 0); +} + +static inline int cts_fw_reg_readb(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b) +{ + return cts_fw_reg_readb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_fw_reg_readw(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w) +{ + return cts_fw_reg_readw_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_fw_reg_readl(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l) +{ + return cts_fw_reg_readl_retry(cts_dev, reg_addr, l, 1, 0); +} + +static inline int cts_fw_reg_readsb(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len) +{ + return cts_fw_reg_readsb_retry(cts_dev, reg_addr, dst, len, 1, 0); +} + +static inline int cts_fw_reg_readsb_delay_idle(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len, int idle) +{ + return cts_fw_reg_readsb_retry_delay_idle(cts_dev, reg_addr, dst, len, + 1, 0, idle); +} + +static inline int cts_hw_reg_writeb_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u8 b) +{ + return cts_hw_reg_writeb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_hw_reg_writew_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u16 w) +{ + return cts_hw_reg_writew_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_hw_reg_writel_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u32 l) +{ + return cts_hw_reg_writel_retry(cts_dev, reg_addr, l, 1, 0); +} + +static inline int cts_hw_reg_writesb(const struct cts_device *cts_dev, + u32 reg_addr, const void *src, size_t len) +{ + return cts_hw_reg_writesb_retry(cts_dev, reg_addr, src, len, 1, 0); +} + +static inline int cts_hw_reg_readb_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u8 *b) +{ + return cts_hw_reg_readb_retry(cts_dev, reg_addr, b, 1, 0); +} + +static inline int cts_hw_reg_readw_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u16 *w) +{ + return cts_hw_reg_readw_retry(cts_dev, reg_addr, w, 1, 0); +} + +static inline int cts_hw_reg_readl_relaxed(const struct cts_device *cts_dev, + u32 reg_addr, u32 *l) +{ + return cts_hw_reg_readl_retry(cts_dev, reg_addr, l, 1, 0); +} + +static inline int cts_hw_reg_readsb(const struct cts_device *cts_dev, + u32 reg_addr, void *dst, size_t len) +{ + return cts_hw_reg_readsb_retry(cts_dev, reg_addr, dst, len, 1, 0); +} + +static inline int cts_sram_writeb(const struct cts_device *cts_dev, + u32 addr, u8 b) +{ + return cts_sram_writeb_retry(cts_dev, addr, b, 1, 0); +} + +static inline int cts_sram_writew(const struct cts_device *cts_dev, + u32 addr, u16 w) +{ + return cts_sram_writew_retry(cts_dev, addr, w, 1, 0); +} + +static inline int cts_sram_writel(const struct cts_device *cts_dev, + u32 addr, u32 l) +{ + return cts_sram_writel_retry(cts_dev, addr, l, 1, 0); +} + +static inline int cts_sram_writesb(const struct cts_device *cts_dev, u32 addr, + const void *src, size_t len) +{ + return cts_sram_writesb_retry(cts_dev, addr, src, len, 1, 0); +} + +static inline int cts_sram_readb(const struct cts_device *cts_dev, + u32 addr, u8 *b) +{ + return cts_sram_readb_retry(cts_dev, addr, b, 1, 0); +} + +static inline int cts_sram_readw(const struct cts_device *cts_dev, + u32 addr, u16 *w) +{ + return cts_sram_readw_retry(cts_dev, addr, w, 1, 0); +} + +static inline int cts_sram_readl(const struct cts_device *cts_dev, + u32 addr, u32 *l) +{ + return cts_sram_readl_retry(cts_dev, addr, l, 1, 0); +} + +static inline int cts_sram_readsb(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len) +{ + return cts_sram_readsb_retry(cts_dev, addr, dst, len, 1, 0); +} + +#ifdef CONFIG_CTS_I2C_HOST +static inline void cts_set_program_addr(struct cts_device *cts_dev) +{ + cts_dev->rtdata.slave_addr = CTS_DEV_PROGRAM_MODE_I2CADDR; + cts_dev->rtdata.program_mode = true; + cts_dev->rtdata.addr_width = CTS_DEV_PROGRAM_MODE_ADDR_WIDTH; +} + +static inline void cts_set_normal_addr(struct cts_device *cts_dev) +{ + cts_dev->rtdata.slave_addr = CTS_DEV_NORMAL_MODE_I2CADDR; + cts_dev->rtdata.program_mode = false; + cts_dev->rtdata.addr_width = CTS_DEV_NORMAL_MODE_ADDR_WIDTH; +} +#else +static inline void cts_set_program_addr(struct cts_device *cts_dev) +{ + cts_dev->rtdata.slave_addr = CTS_DEV_PROGRAM_MODE_SPIADDR; + cts_dev->rtdata.program_mode = true; + cts_dev->rtdata.addr_width = CTS_DEV_PROGRAM_MODE_ADDR_WIDTH; +} + +static inline void cts_set_normal_addr(struct cts_device *cts_dev) +{ + cts_dev->rtdata.slave_addr = CTS_DEV_NORMAL_MODE_SPIADDR; + cts_dev->rtdata.program_mode = false; + cts_dev->rtdata.addr_width = CTS_DEV_NORMAL_MODE_ADDR_WIDTH; +} +#endif + +extern int cts_irq_handler(struct cts_device *cts_dev); +extern void cts_firmware_upgrade_work(struct work_struct *work); + +extern bool cts_is_device_suspended(const struct cts_device *cts_dev); +extern int cts_suspend_device(struct cts_device *cts_dev); +extern int cts_resume_device(struct cts_device *cts_dev); + +extern bool cts_is_device_program_mode(const struct cts_device *cts_dev); +extern int cts_enter_program_mode(struct cts_device *cts_dev); +extern int cts_enter_normal_mode(struct cts_device *cts_dev); + +extern int cts_probe_device(struct cts_device *cts_dev); +extern int cts_send_command(const struct cts_device *cts_dev, u8 cmd); + +extern int cts_get_hwid(struct cts_device *cts_dev, u32 *hwid); +extern int cts_read_sram_normal_mode(const struct cts_device *cts_dev, + u32 addr, void *dst, size_t len, int retry, int delay); + +#ifdef CFG_CTS_GESTURE +extern void cts_enable_gesture_wakeup(struct cts_device *cts_dev); +extern void cts_disable_gesture_wakeup(struct cts_device *cts_dev); +extern bool cts_is_gesture_wakeup_enabled(const struct cts_device *cts_dev); +extern int cts_get_gesture_info(struct cts_device *cts_dev, void *gesture_info); +extern int enter_gesture_pocket_mode(struct cts_device *cts_dev); +extern int exit_gesture_pocket_mode(struct cts_device *cts_dev); +#endif /* CFG_CTS_GESTURE */ + +extern int cts_set_int_data_types(struct cts_device *cts_dev, u16 types); +extern int cts_set_int_data_method(struct cts_device *cts_dev, u8 method); + +#ifdef CONFIG_CTS_ESD_PROTECTION +extern void cts_init_esd_protection(struct chipone_ts_data *cts_data); +extern void cts_enable_esd_protection(struct chipone_ts_data *cts_data); +extern void cts_disable_esd_protection(struct chipone_ts_data *cts_data); +extern void cts_deinit_esd_protection(struct chipone_ts_data *cts_data); +#else /* CONFIG_CTS_ESD_PROTECTION */ +static inline void cts_init_esd_protection(struct chipone_ts_data *cts_data) +{ +} + +static inline void cts_enable_esd_protection(struct chipone_ts_data *cts_data) +{ +} + +static inline void cts_disable_esd_protection(struct chipone_ts_data *cts_data) +{ +} + +static inline void cts_deinit_esd_protection(struct chipone_ts_data *cts_data) +{ +} +#endif /* CONFIG_CTS_ESD_PROTECTION */ + +#ifdef CFG_CTS_HEARTBEAT_MECHANISM +void cts_show_touch_debug_msg(void *debug); +void cts_heartbeat_mechanism_work(struct work_struct *work); +void cts_enable_heartbeat_mechanism(struct chipone_ts_data *cts_data); +void cts_disable_heartbeat_mechanism(struct chipone_ts_data *cts_data); +#endif + +#ifdef CONFIG_CTS_GLOVE +extern int cts_enter_glove_mode(struct cts_device *cts_dev); +extern int cts_exit_glove_mode(struct cts_device *cts_dev); +int cts_is_glove_enabled(const struct cts_device *cts_dev); +#else +static inline int cts_enter_glove_mode(struct cts_device *cts_dev) +{ + return 0; +} + +static inline int cts_exit_glove_mode(struct cts_device *cts_dev) +{ + return 0; +} + +static inline int cts_is_glove_enabled(const struct cts_device *cts_dev) +{ + return 0; +} +#endif + +#ifdef CONFIG_CTS_CHARGER_DETECT +extern bool cts_is_charger_exist(struct cts_device *cts_dev); +extern int cts_set_dev_charger_attached(struct cts_device *cts_dev, + bool attached); +#else /* CONFIG_CTS_CHARGER_DETECT */ +static inline bool cts_is_charger_exist(struct cts_device *cts_dev) +{ + return false; +} + +static inline int cts_set_dev_charger_attached(struct cts_device *cts_dev, + bool attached) +{ + return 0; +} +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_EARJACK_DETECT +extern bool cts_is_earjack_exist(struct cts_device *cts_dev); +extern int cts_set_dev_earjack_attached(struct cts_device *cts_dev, bool attached); +#else /* CONFIG_CTS_EARJACK_DETECT */ +static inline bool cts_is_earjack_exist(struct cts_device *cts_dev) +{ + return false; +} +static inline int cts_set_dev_earjack_attached(struct cts_device *cts_dev, bool attached) +{ + return 0; +} +#endif /* CONFIG_CTS_EARJACK_DETECT */ + +#ifdef CONFIG_CTS_LEGACY_TOOL +extern int cts_tool_init(struct chipone_ts_data *cts_data); +extern void cts_tool_deinit(struct chipone_ts_data *data); +#else /* CONFIG_CTS_LEGACY_TOOL */ +static inline int cts_tool_init(struct chipone_ts_data *cts_data) +{ + return 0; +} + +static inline void cts_tool_deinit(struct chipone_ts_data *data) +{ +} +#endif /* CONFIG_CTS_LEGACY_TOOL */ + +extern bool cts_is_device_enabled(const struct cts_device *cts_dev); +extern int cts_start_device(struct cts_device *cts_dev); +extern int cts_stop_device(struct cts_device *cts_dev); + +#ifdef CFG_CTS_FW_LOG_REDIRECT +extern int cts_enable_fw_log_redirect(struct cts_device *cts_dev); +extern int cts_disable_fw_log_redirect(struct cts_device *cts_dev); +extern bool cts_is_fw_log_redirect(struct cts_device *cts_dev); +extern int cts_fw_log_show_finish(struct cts_device *cts_dev); +#endif + +#ifdef CFG_CTS_UPDATE_CRCCHECK +extern int cts_sram_writesb_boot_crc_retry(const struct cts_device *cts_dev, + size_t len, u32 crc, int retry); +#endif + +extern const char *cts_dev_boot_mode2str(u8 boot_mode); +extern bool cts_is_fwid_valid(u16 fwid); + +extern int cts_reset_device(struct cts_device *cts_dev); +extern int touch_set_state(int state, int panel_idx); +extern int kstrtobool(const char *s, bool *res); +#endif /* CTS_CORE_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_driver.c b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_driver.c new file mode 100644 index 000000000000..00f2e693279a --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_driver.c @@ -0,0 +1,1392 @@ +/* + * Copyright (C) 2023 Motorola Mobility LLC + * + * 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. + */ + +#ifdef CONFIG_CTS_I2C_HOST +#define LOG_TAG "I2CDrv" +#else +#define LOG_TAG "SPIDrv" +#endif + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sysfs.h" +#include "cts_charger_detect.h" +#include "cts_earjack_detect.h" +#include "cts_oem.h" +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +#include +#endif + +static void cts_resume_work_func(struct work_struct *work); +#ifdef CFG_CTS_DRM_NOTIFIER +#if defined(CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS) +#include +#else +#include +#endif +static struct drm_panel *active_panel; +static int check_dt(struct device_node *np); +#endif +bool cts_show_debug_log; +#ifdef CTS_MTK_GET_PANEL +static char *active_panel_name; +#endif + +module_param_named(debug_log, cts_show_debug_log, bool, 0660); +MODULE_PARM_DESC(debug_log, "Show debug log control"); + +struct chipone_ts_data *g_cts_data; + +#ifdef CHIPONE_SENSOR_EN +extern int __attribute__ ((weak)) sensors_classdev_register(struct device *parent, struct sensors_classdev *sensors_cdev); +extern void __attribute__ ((weak)) sensors_classdev_unregister(struct sensors_classdev *sensors_cdev); +#endif + +enum touch_state { + TOUCH_DEEP_SLEEP_STATE = 0, + TOUCH_LOW_POWER_STATE, +}; + +int cts_suspend(struct chipone_ts_data *cts_data) +{ + int ret; + + cts_info("Suspend"); + + cts_lock_device(&cts_data->cts_dev); + ret = cts_suspend_device(&cts_data->cts_dev); + cts_unlock_device(&cts_data->cts_dev); + + if (ret) + cts_err("Suspend device failed %d", ret); + + ret = cts_stop_device(&cts_data->cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } +#ifdef CTS_STOWED_MODE_EN + atomic_set(&cts_data->post_suspended, 1); + cts_lock_device(&cts_data->cts_dev); + cts_err("stowed_get = %d\n", cts_data->pdata->stowed_get); + if(cts_data->pdata->stowed_get && cts_data->cts_dev.rtdata.gesture_wakeup_enabled) + { + ret = enter_gesture_pocket_mode(&cts_data->cts_dev); + if(ret) + { + cts_info("Failed to set stowed mode%d\n", cts_data->pdata->stowed_get); + } + else + { + cts_data->pdata->stowed_set = cts_data->pdata->stowed_get; + cts_info("Enable stowed mode %d success.\n", cts_data->pdata->stowed_set); + } + + } + cts_unlock_device(&cts_data->cts_dev); +#endif + + +#ifdef CFG_CTS_GESTURE + /* Enable IRQ wake if gesture wakeup enabled */ + if (cts_is_gesture_wakeup_enabled(&cts_data->cts_dev)) { + ret = cts_plat_enable_irq_wake(cts_data->pdata); + if (ret) { + cts_err("Enable IRQ wake failed %d", ret); + return ret; + } + ret = cts_plat_enable_irq(cts_data->pdata); + if (ret) { + cts_err("Enable IRQ failed %d", ret); + return ret; + } + } else { + ret = gpio_get_value(cts_data->pdata->rst_gpio); + cts_dbg("rst pull flag:%d, rst gpio val:%d", cts_data->pdata->rst_pull_flag, ret); + if (cts_data->pdata->rst_pull_flag && ret) { + mdelay(10); + gpio_set_value(cts_data->pdata->rst_gpio, 0); + } + } +#endif /* CFG_CTS_GESTURE */ + +/** - To avoid waking up while not sleeping, + *delay 20ms to ensure reliability + */ + msleep(20); + + return 0; +} + +int cts_resume(struct chipone_ts_data *cts_data) +{ + int ret; + + cts_info("Resume"); + +#ifdef CTS_STOWED_MODE_EN + cts_data->pdata->stowed_set = 0; + atomic_set(&cts_data->post_suspended, 0); +#endif + +#ifdef CFG_CTS_GESTURE + if (cts_is_gesture_wakeup_enabled(&cts_data->cts_dev)) { + ret = cts_plat_disable_irq_wake(cts_data->pdata); + if (ret) + cts_warn("Disable IRQ wake failed %d", ret); + ret = cts_plat_disable_irq(cts_data->pdata); + if (ret < 0) + cts_err("Disable IRQ failed %d", ret); + } +#endif /* CFG_CTS_GESTURE */ + + cts_lock_device(&cts_data->cts_dev); + ret = cts_resume_device(&cts_data->cts_dev); + cts_unlock_device(&cts_data->cts_dev); + if (ret) { + cts_warn("Resume device failed %d", ret); + return ret; + } + + ret = cts_start_device(&cts_data->cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + return ret; + } + + return 0; +} + +static void cts_resume_work_func(struct work_struct *work) +{ + struct chipone_ts_data *cts_data = + container_of(work, struct chipone_ts_data, ts_resume_work); + cts_info("%s", __func__); + cts_resume(cts_data); +} + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER +#ifdef CFG_CTS_DRM_NOTIFIER +#ifdef CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS +static void fb_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *client_data) +{ + struct chipone_ts_data *cts_data = client_data; + + if (!notification) { + pr_err("Invalid notification\n"); + return; + } + + cts_dbg("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) { + queue_work(cts_data->workqueue, + &cts_data->ts_resume_work); + } + break; + + case DRM_PANEL_EVENT_BLANK: + if (notification->notif_data.early_trigger) { +#ifdef CHIPONE_SENSOR_EN +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + if (cts_data->s_tap_flag || cts_data->d_tap_flag) { + cts_enable_gesture_wakeup(&cts_data->cts_dev); + g_cts_data->should_enable_gesture = true; + } + else { + cts_disable_gesture_wakeup(&cts_data->cts_dev); + g_cts_data->should_enable_gesture = false; + } +#endif + if (g_cts_data->should_enable_gesture) + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); + else + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); +#endif + cts_suspend(cts_data); + } + break; + + case DRM_PANEL_EVENT_BLANK_LP: + cts_dbg("received lp event\n"); + break; + + case DRM_PANEL_EVENT_FPS_CHANGE: + cts_dbg("Received fps change old fps:%d new fps:%d\n", + notification->notif_data.old_fps, + notification->notif_data.new_fps); + break; + + default: + cts_dbg("notification serviced :%d\n", + notification->notif_type); + break; + } +} +#else +static int fb_notifier_callback(struct notifier_block *nb, + unsigned long action, void *data) +{ + volatile int blank; + const struct cts_platform_data *pdata = + container_of(nb, struct cts_platform_data, fb_notifier); + struct chipone_ts_data *cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + struct drm_panel_notifier *evdata = data; + + cts_dbg("FB notifier callback"); + if (!evdata || !cts_data) + return 0; + + blank = *(int *)evdata->data; + cts_dbg("action=%lu, blank=%d\n", action, blank); + + if (action == DRM_PANEL_EARLY_EVENT_BLANK) { + if (blank == DRM_PANEL_BLANK_POWERDOWN) { +#ifdef CHIPONE_SENSOR_EN +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + if (cts_data->s_tap_flag || cts_data->d_tap_flag) { + cts_enable_gesture_wakeup(&cts_data->cts_dev); + g_cts_data->should_enable_gesture = true; + } + else { + cts_disable_gesture_wakeup(&cts_data->cts_dev); + g_cts_data->should_enable_gesture = false; + } +#endif + if (g_cts_data->should_enable_gesture) + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); + else + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); +#endif + cts_suspend(cts_data); + } + } else if (evdata->data) { + blank = *(int *)evdata->data; + if (action == DRM_PANEL_EVENT_BLANK) { + if (blank == DRM_PANEL_BLANK_UNBLANK) + /* cts_resume(cts_data); */ + queue_work(cts_data->workqueue, + &cts_data->ts_resume_work); + } + } + + return 0; +} +#endif +#else +static int fb_notifier_callback(struct notifier_block *nb, + unsigned long action, void *data) +{ + volatile int blank; + const struct cts_platform_data *pdata = + container_of(nb, struct cts_platform_data, fb_notifier); + struct chipone_ts_data *cts_data = + container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + struct fb_event *evdata = data; + + cts_info("FB notifier callback"); + + if (evdata && evdata->data) { + if (action == FB_EVENT_BLANK) { + blank = *(int *)evdata->data; + if (blank == FB_BLANK_UNBLANK) { + /* cts_resume(cts_data); */ + queue_work(cts_data->workqueue, + &cts_data->ts_resume_work); + return NOTIFY_OK; + } + } else if (action == FB_EARLY_EVENT_BLANK) { + blank = *(int *)evdata->data; + if (blank == FB_BLANK_POWERDOWN) { + cts_suspend(cts_data); + return NOTIFY_OK; + } + } + } + + return NOTIFY_DONE; +} +#endif + +static int cts_init_pm_fb_notifier(struct chipone_ts_data *cts_data) +{ + cts_info("Init FB notifier"); + +#ifndef CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS + cts_data->pdata->fb_notifier.notifier_call = fb_notifier_callback; +#endif + +#ifdef CFG_CTS_DRM_NOTIFIER + { + int ret = -ENODEV; + + if (active_panel) { +#ifdef CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS + void *cookie; + cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, active_panel, + &fb_notifier_callback, cts_data); + if (!cookie) { + cts_err("Failed to register for panel events\n"); + return ret; + } + cts_info("registered for panel notifications panel: 0x%x\n", + active_panel); + cts_data->notifier_cookie = cookie; + ret = 0; +#else + ret =drm_panel_notifier_register(active_panel, + &cts_data->pdata->fb_notifier); +#endif + if (ret) + cts_err("register drm_notifier failed. ret=%d\n", ret); + } + return ret; + } +#else + return fb_register_client(&cts_data->pdata->fb_notifier); +#endif +} + +static int cts_deinit_pm_fb_notifier(struct chipone_ts_data *cts_data) +{ + cts_info("Deinit FB notifier"); +#ifdef CFG_CTS_DRM_NOTIFIER + { + int ret = 0; + + if (active_panel) { +#if defined(CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS) + if (cts_data->notifier_cookie) + panel_event_notifier_unregister(cts_data->notifier_cookie); +#else + ret = drm_panel_notifier_unregister(active_panel, + &cts_data->pdata->fb_notifier); +#endif + if (ret) + cts_err("Error occurred while unregistering drm_notifier.\n"); + } + return ret; + } +#else + return fb_unregister_client(&cts_data->pdata->fb_notifier); +#endif +} +#endif /* CONFIG_CTS_PM_FB_NOTIFIER */ + +#ifdef CFG_CTS_DRM_NOTIFIER +static int 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)) { + cts_info("check active_panel"); + active_panel = panel; + return 0; + } + } + if (node) + cts_err("%s: %s not actived", __func__, node->name); + return -ENODEV; +} + +static int check_default_tp(struct device_node *dt, const char *prop) +{ + const char *active_tp; + const char *compatible; + char *start; + int ret; + + ret = of_property_read_string(dt->parent, prop, &active_tp); + if (ret) { + cts_err("%s:fail to read %s %d", __func__, prop, ret); + return -ENODEV; + } + + ret = of_property_read_string(dt, "compatible", &compatible); + if (ret < 0) { + cts_err("%s:fail to read %s %d", __func__, "compatible", ret); + return -ENODEV; + } + + start = strnstr(active_tp, compatible, strlen(active_tp)); + if (start == NULL) { + cts_err("no match compatible, %s, %s", compatible, active_tp); + ret = -ENODEV; + } + + return ret; +} +#endif + +#ifdef CTS_MTK_GET_PANEL +char panel_name[50] = { 0 }; + +static int cts_get_panel(void) +{ + int ret = -1; + + cts_info("Enter cts_get_panel"); + if (saved_command_line) { + char *sub; + char key_prefix[] = "mipi_mot_vid_"; + char ic_prefix[] = "icnl"; + + cts_info("saved_command_line is %s", saved_command_line); + sub = strstr(saved_command_line, key_prefix); + if (sub) { + char *d; + int n, len, len_max = 50; + + d = strstr(sub, " "); + if (d) + n = strlen(sub) - strlen(d); + else + n = strlen(sub); + + if (n > len_max) + len = len_max; + else + len = n; + + strncpy(panel_name, sub, len); + active_panel_name = panel_name; + if (strstr(active_panel_name, ic_prefix)) + cts_info("active_panel_name=%s", active_panel_name); + else { + cts_info("Not chipone panel!"); + return ret; + } + + } else { + cts_info("chipone active panel not found!"); + return ret; + } + } else { + cts_info("saved_command_line null!"); + return ret; + } + + return 0; +} +#endif + + +#ifdef CHIPONE_SENSOR_EN +static struct sensors_classdev __maybe_unused sensors_touch_cdev = { + + .name = "dt-gesture", + .vendor = "chipone", + .version = 1, + .type = SENSOR_TYPE_MOTO_DOUBLE_TAP, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + /* WAKE_UP & SPECIAL_REPORT */ + .flags = 1 | 6, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +static int chipone_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + cts_info("Gesture set enable %d!", enable); + mutex_lock(&g_cts_data->state_mutex); + if (enable == 1) { + cts_enable_gesture_wakeup(&g_cts_data->cts_dev); + g_cts_data->should_enable_gesture = true; + } else if (enable == 0) { + cts_disable_gesture_wakeup(&g_cts_data->cts_dev); + g_cts_data->should_enable_gesture = false; + } else { + cts_info("unknown enable symbol\n"); + } + mutex_unlock(&g_cts_data->state_mutex); + return 0; +} +static int chipone_sensor_init(struct chipone_ts_data *data) +{ + struct chipone_sensor_platform_data *sensor_pdata; + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + cts_err("Failed to allocate device"); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct chipone_sensor_platform_data), + GFP_KERNEL); + if (!sensor_pdata) { + cts_err("Failed to allocate memory"); + goto free_sensor_pdata; + } + data->sensor_pdata = sensor_pdata; + + __set_bit(EV_KEY, sensor_input_dev->evbit); + __set_bit(BTN_TRIGGER_HAPPY3, sensor_input_dev->keybit); +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + __set_bit(BTN_TRIGGER_HAPPY6, sensor_input_dev->keybit); +#endif + __set_bit(EV_SYN, sensor_input_dev->evbit); + + sensor_input_dev->name = "double-tap"; + data->sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + cts_err("Unable to register device, err=%d", err); + goto free_sensor_input_dev; + } + + sensor_pdata->ps_cdev = sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = chipone_sensor_set_enable; + sensor_pdata->data = data; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(data->sensor_pdata->input_sensor_dev); +free_sensor_input_dev: + input_free_device(data->sensor_pdata->input_sensor_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, sensor_pdata); + data->sensor_pdata = NULL; +exit: + return 1; +} + +int chipone_sensor_remove(struct chipone_ts_data *data) +{ + sensors_classdev_unregister(&data->sensor_pdata->ps_cdev); + input_unregister_device(data->sensor_pdata->input_sensor_dev); + devm_kfree(&data->sensor_pdata->input_sensor_dev->dev, + data->sensor_pdata); + data->sensor_pdata = NULL; + data->wakeable = false; + data->should_enable_gesture = false; + return 0; +} +#endif + +#ifdef CTS_CHECK_DEVICE_BOOTMODE +static bool cts_is_charger_mode(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool charger_mode = false; + const char *bootargs = NULL; + char *bootmode = NULL; + char *end = NULL; + + if (!np) + return charger_mode; + +#ifdef CONFIG_BOOT_CONFIG + if (!of_property_read_string(np, "mmi,bootconfig", &bootargs)) { +#else + if (!of_property_read_string(np, "bootargs", &bootargs)) { +#endif + bootmode = strstr(bootargs, "androidboot.mode="); + if (bootmode) { + end = strpbrk(bootmode, " "); + bootmode = strpbrk(bootmode, "="); + } + if (bootmode && + end > bootmode && + strnstr(bootmode, "charger", end - bootmode)) { + charger_mode = true; + } + } + of_node_put(np); + cts_info("Charger mode = %d", charger_mode); + + return charger_mode; +} +#endif + +#ifdef CONFIG_CTS_I2C_HOST +static int cts_driver_probe(struct i2c_client *client, + const struct i2c_device_id *id) +#else +static int cts_driver_probe(struct spi_device *client) +#endif +{ + struct chipone_ts_data *cts_data = NULL; + int ret = 0; + +#ifdef CHIPONE_SENSOR_EN + static bool initialized_sensor; +#endif + +#ifdef CTS_MTK_GET_PANEL + ret = cts_get_panel(); + if (ret) { + cts_info("MTK get chipone panel error"); + return ret; + } +#endif + +#ifdef CFG_CTS_DRM_NOTIFIER + { + struct device_node *dp = client->dev.of_node; + + if (check_dt(dp)) { + if (!check_default_tp(dp, "qcom,i2c-touch-active")) + ret = -EPROBE_DEFER; + else + ret = -ENODEV; + + cts_err("%s: %s not actived\n", __func__, dp->name); + return ret; + } + } +#else +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + if (client->dev.of_node && !mmi_device_is_available(client->dev.of_node)) { + cts_err("%s : mmi: device not supported\n", __func__); + return -ENODEV; + } +#endif +#endif + +#ifdef CTS_CHECK_DEVICE_BOOTMODE + if (cts_is_charger_mode()) { + cts_info("Charger mode, ignore insmod chipone modules.\n"); + return -ENODEV; + } +#endif + +#ifdef CONFIG_CTS_I2C_HOST + cts_info("Probe i2c client: name='%s' addr=0x%02x flags=0x%02x irq=%d", + client->name, client->addr, client->flags, client->irq); + +#if !defined(CONFIG_MTK_PLATFORM) + if (client->addr != CTS_DEV_NORMAL_MODE_I2CADDR) { + cts_err("Probe i2c addr 0x%02x != driver config addr 0x%02x", + client->addr, CTS_DEV_NORMAL_MODE_I2CADDR); + return -ENODEV; + }; +#endif + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + cts_err("Check functionality failed"); + return -ENODEV; + } +#endif + + cts_data = kzalloc(sizeof(struct chipone_ts_data), GFP_KERNEL); + if (cts_data == NULL) { + cts_err("Allocate chipone_ts_data failed"); + return -ENOMEM; + } + cts_data->pdata = kzalloc(sizeof(struct cts_platform_data), GFP_KERNEL); + if (cts_data->pdata == NULL) { + cts_err("Allocate cts_platform_data failed"); + ret = -ENOMEM; + goto err_free_cts_data; + } +#ifdef CONFIG_CTS_I2C_HOST + i2c_set_clientdata(client, cts_data); + cts_data->i2c_client = client; + cts_data->device = &client->dev; +#else + spi_set_drvdata(client, cts_data); + cts_data->spi_client = client; + cts_data->device = &client->dev; +#endif + + cts_init_platform_data(cts_data->pdata, client); + + cts_data->cts_dev.pdata = cts_data->pdata; + cts_data->pdata->cts_dev = &cts_data->cts_dev; + + g_cts_data = cts_data; + + cts_data->workqueue = + create_singlethread_workqueue(CFG_CTS_DEVICE_NAME "-workqueue"); + if (cts_data->workqueue == NULL) { + cts_err("Create workqueue failed"); + ret = -ENOMEM; + goto err_deinit_platform_data; + } +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_data->esd_workqueue = + create_singlethread_workqueue(CFG_CTS_DEVICE_NAME "-esd_workqueue"); + if (cts_data->esd_workqueue == NULL) { + cts_err("Create esd workqueue failed"); + ret = -ENOMEM; + goto err_destroy_workqueue; + } +#endif + +#ifdef CFG_CTS_HEARTBEAT_MECHANISM + cts_data->heart_workqueue = + create_singlethread_workqueue(CFG_CTS_DEVICE_NAME "-heart_workqueue"); + if (cts_data->heart_workqueue == NULL) { + cts_err("Create heart workqueue failed"); + ret = -ENOMEM; + goto err_destroy_esd_workqueue; + } +#endif + + ret = cts_plat_request_resource(cts_data->pdata); + if (ret < 0) { + cts_err("Request resource failed %d", ret); + goto err_destroy_heart_workqueue; + } + + ret = cts_reset_device(&cts_data->cts_dev); + if (ret < 0) { + cts_err("Reset device failed %d", ret); + goto err_free_resource; + } + + ret = cts_probe_device(&cts_data->cts_dev); + if (ret) { + cts_err("Probe device failed %d", ret); + goto err_free_resource; + } + + ret = cts_plat_init_touch_device(cts_data->pdata); + if (ret < 0) { + cts_err("Init touch device failed %d", ret); + goto err_free_resource; + } + + ret = cts_plat_init_vkey_device(cts_data->pdata); + if (ret < 0) { + cts_err("Init vkey device failed %d", ret); + goto err_deinit_touch_device; + } + + ret = cts_plat_init_gesture(cts_data->pdata); + if (ret < 0) { + cts_err("Init gesture failed %d", ret); + goto err_deinit_vkey_device; + } +#ifdef TOUCHSCREEN_PM_BRL_SPI + if (cts_data->pdata->gesture_wait_pm) + init_waitqueue_head(&cts_data->pm_wq); + atomic_set(&cts_data->pm_resume, 1); +#endif + + cts_init_esd_protection(cts_data); + + ret = cts_tool_init(cts_data); + if (ret < 0) + cts_warn("Init tool node failed %d", ret); + + ret = cts_sysfs_add_device(&client->dev); + if (ret < 0) + cts_warn("Add sysfs entry for device failed %d", ret); + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + ret = cts_init_pm_fb_notifier(cts_data); + if (ret) { + cts_err("Init FB notifier failed %d", ret); + goto err_deinit_sysfs; + } +#endif + + ret = cts_plat_request_irq(cts_data->pdata); + if (ret < 0) { + cts_err("Request IRQ failed %d", ret); + goto err_register_fb; + } + +#ifdef CONFIG_CTS_CHARGER_DETECT + ret = cts_charger_detect_init(cts_data); + if (ret) + cts_err("Init charger detect failed %d", ret); + /* Ignore this error */ +#endif + +#ifdef CONFIG_CTS_EARJACK_DETECT + ret = cts_earjack_detect_init(cts_data); + if (ret) { + cts_err("Init earjack detect failed %d", ret); + // Ignore this error + } +#endif + + ret = cts_oem_init(cts_data); + if (ret < 0) { + cts_warn("Init oem specific faild %d", ret); + goto err_deinit_oem; + } + +#ifdef CFG_CTS_HEARTBEAT_MECHANISM + INIT_DELAYED_WORK(&cts_data->heart_work, cts_heartbeat_mechanism_work); +#endif + + /* Init firmware upgrade work and schedule */ + INIT_DELAYED_WORK(&cts_data->fw_upgrade_work, cts_firmware_upgrade_work); + queue_delayed_work(cts_data->workqueue, &cts_data->fw_upgrade_work, + msecs_to_jiffies(15 * 1000)); + + INIT_WORK(&cts_data->ts_resume_work, cts_resume_work_func); + +#ifdef CHIPONE_SENSOR_EN + mutex_init(&cts_data->state_mutex); + //unknown screen state + cts_data->screen_state = SCREEN_UNKNOWN; + if (!initialized_sensor) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&(cts_data->gesture_wakelock), WAKE_LOCK_SUSPEND, "dt-wake-lock"); +#else + PM_WAKEUP_REGISTER(cts_data->device, cts_data->gesture_wakelock, "dt-wake-lock"); + if (!cts_data->gesture_wakelock) { + cts_err("ILITEK Driver failed to load. wakeup_source_init failed."); + return -ENOMEM; + } +#endif + if (!chipone_sensor_init(cts_data)) + initialized_sensor = true; + } +#endif + + return 0; + +err_deinit_oem: + cts_oem_deinit(cts_data); + +#ifdef CONFIG_CTS_EARJACK_DETECT + cts_earjack_detect_deinit(cts_data); +#endif +#ifdef CONFIG_CTS_CHARGER_DETECT + cts_charger_detect_deinit(cts_data); +#endif + cts_plat_free_irq(cts_data->pdata); + +err_register_fb: +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + cts_deinit_pm_fb_notifier(cts_data); +err_deinit_sysfs: +#endif + cts_sysfs_remove_device(&client->dev); +#ifdef CONFIG_CTS_LEGACY_TOOL + cts_tool_deinit(cts_data); +#endif + +#ifdef CONFIG_CTS_ESD_PROTECTION + cts_deinit_esd_protection(cts_data); +#endif + +#ifdef CFG_CTS_GESTURE + cts_plat_deinit_gesture(cts_data->pdata); +#endif + +err_deinit_vkey_device: +#ifdef CONFIG_CTS_VIRTUALKEY + cts_plat_deinit_vkey_device(cts_data->pdata); +#endif + +err_deinit_touch_device: + cts_plat_deinit_touch_device(cts_data->pdata); + +err_free_resource: + cts_plat_free_resource(cts_data->pdata); + +err_destroy_heart_workqueue: +#ifdef CFG_CTS_HEARTBEAT_MECHANISM + destroy_workqueue(cts_data->heart_workqueue); +err_destroy_esd_workqueue: +#endif + +#ifdef CONFIG_CTS_ESD_PROTECTION + destroy_workqueue(cts_data->esd_workqueue); +err_destroy_workqueue: +#endif + destroy_workqueue(cts_data->workqueue); +err_deinit_platform_data: + cts_deinit_platform_data(cts_data->pdata); + kfree(cts_data->pdata); +err_free_cts_data: + kfree(cts_data); + + cts_err("Probe failed %d", ret); + + return ret; +} + +#ifdef CONFIG_CTS_I2C_HOST +static int cts_driver_remove(struct i2c_client *client) +#else +static int cts_driver_remove(struct spi_device *client) +#endif +{ + struct chipone_ts_data *cts_data; + int ret = 0; + + cts_info("Remove"); + +#ifdef CONFIG_CTS_I2C_HOST + cts_data = (struct chipone_ts_data *)i2c_get_clientdata(client); +#else + cts_data = (struct chipone_ts_data *)spi_get_drvdata(client); +#endif + if (cts_data) { + ret = cts_stop_device(&cts_data->cts_dev); + if (ret) + cts_warn("Stop device failed %d", ret); + +#ifdef CONFIG_CTS_CHARGER_DETECT + cts_charger_detect_deinit(cts_data); +#endif + +#ifdef CONFIG_CTS_EARJACK_DETECT + cts_earjack_detect_deinit(cts_data); +#endif + + cts_plat_free_irq(cts_data->pdata); + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + cts_deinit_pm_fb_notifier(cts_data); +#endif + + cts_tool_deinit(cts_data); + + cts_sysfs_remove_device(&client->dev); + + cts_deinit_esd_protection(cts_data); + + cts_plat_deinit_touch_device(cts_data->pdata); + + cts_plat_deinit_vkey_device(cts_data->pdata); + + cts_plat_deinit_gesture(cts_data->pdata); + + cts_plat_free_resource(cts_data->pdata); + + cts_oem_deinit(cts_data); + +#ifdef CFG_CTS_HEARTBEAT_MECHANISM + if (cts_data->heart_workqueue) + destroy_workqueue(cts_data->heart_workqueue); +#endif + +#ifdef CONFIG_CTS_ESD_PROTECTION + if (cts_data->esd_workqueue) + destroy_workqueue(cts_data->esd_workqueue); +#endif + + if (cts_data->workqueue) + destroy_workqueue(cts_data->workqueue); + + cts_deinit_platform_data(cts_data->pdata); + + if (cts_data->pdata) + kfree(cts_data->pdata); + kfree(cts_data); + } else { + cts_warn("Chipone i2c driver remove while NULL chipone_ts_data"); + return -EINVAL; + } + + return ret; +} + +#ifdef CONFIG_CTS_PM_LEGACY +static int cts_i2c_driver_suspend(struct device *dev, pm_message_t state) +{ + cts_info("Suspend by legacy power management"); + return cts_suspend(dev_get_drvdata(dev)); +} + +static int cts_i2c_driver_resume(struct device *dev) +{ + cts_info("Resume by legacy power management"); + return cts_resume(dev_get_drvdata(dev)); +} +#endif /* CONFIG_CTS_PM_LEGACY */ + +#if defined(CONFIG_CTS_PM_GENERIC) +static int cts_i2c_driver_pm_suspend(struct device *dev) +{ + cts_info("Suspend by bus power management"); + return cts_suspend(dev_get_drvdata(dev)); +} + +static int cts_i2c_driver_pm_resume(struct device *dev) +{ + cts_info("Resume by bus power management"); + return cts_resume(dev_get_drvdata(dev)); +} +#elif defined(TOUCHSCREEN_PM_BRL_SPI) +static int cts_i2c_driver_pm_suspend(struct device *dev) +{ + struct chipone_ts_data *cts_data = + dev_get_drvdata(dev); + + cts_info("Suspend by bus power management"); + atomic_set(&cts_data->pm_resume, 0); + return 0; +} + +static int cts_i2c_driver_pm_resume(struct device *dev) +{ + struct chipone_ts_data *cts_data = + dev_get_drvdata(dev); + + cts_info("Resume by bus power management"); + atomic_set(&cts_data->pm_resume, 1); + if (cts_data->pdata->gesture_wait_pm) + wake_up_interruptible(&cts_data->pm_wq); + + return 0; +} +#endif + +#if defined(CONFIG_CTS_PM_GENERIC) || defined(TOUCHSCREEN_PM_BRL_SPI) +/* bus control the suspend/resume procedure */ +static const struct dev_pm_ops cts_i2c_driver_pm_ops = { + .suspend = cts_i2c_driver_pm_suspend, + .resume = cts_i2c_driver_pm_resume, +}; +#endif /* CONFIG_CTS_PM_GENERIC or TOUCHSCREEN_PM_BRL_SPI*/ + +#ifdef CONFIG_CTS_SYSFS +static ssize_t reset_pin_show(struct device_driver *driver, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_HAS_RESET_PIN: %c\n", +#ifdef CFG_CTS_HAS_RESET_PIN + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(reset_pin, S_IRUGO, reset_pin_show, NULL); +#else +static DRIVER_ATTR_RO(reset_pin); +#endif + +static ssize_t swap_xy_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_SWAP_XY: %c\n", +#ifdef CFG_CTS_SWAP_XY + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(swap_xy, S_IRUGO, swap_xy_show, NULL); +#else +static DRIVER_ATTR_RO(swap_xy); +#endif + +static ssize_t wrap_x_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_WRAP_X: %c\n", +#ifdef CFG_CTS_WRAP_X + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(wrap_x, S_IRUGO, wrap_x_show, NULL); +#else +static DRIVER_ATTR_RO(wrap_x); +#endif + +static ssize_t wrap_y_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_WRAP_Y: %c\n", +#ifdef CFG_CTS_WRAP_Y + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(wrap_y, S_IRUGO, wrap_y_show, NULL); +#else +static DRIVER_ATTR_RO(wrap_y); +#endif + +static ssize_t force_update_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_HAS_RESET_PIN: %c\n", +#ifdef CFG_CTS_FIRMWARE_FORCE_UPDATE + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(force_update, S_IRUGO, force_update_show, NULL); +#else +static DRIVER_ATTR_RO(force_update); +#endif + +static ssize_t max_touch_num_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_MAX_TOUCH_NUM: %d\n", + CFG_CTS_MAX_TOUCH_NUM); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(max_touch_num, S_IRUGO, max_touch_num_show, NULL); +#else +static DRIVER_ATTR_RO(max_touch_num); +#endif + +static ssize_t vkey_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CONFIG_CTS_VIRTUALKEY: %c\n", +#ifdef CONFIG_CTS_VIRTUALKEY + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(vkey, S_IRUGO, vkey_show, NULL); +#else +static DRIVER_ATTR_RO(vkey); +#endif + +static ssize_t gesture_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CFG_CTS_GESTURE: %c\n", +#ifdef CFG_CTS_GESTURE + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(gesture, S_IRUGO, gesture_show, NULL); +#else +static DRIVER_ATTR_RO(gesture); +#endif + +static ssize_t esd_protection_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CONFIG_CTS_ESD_PROTECTION: %c\n", +#ifdef CONFIG_CTS_ESD_PROTECTION + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(esd_protection, S_IRUGO, esd_protection_show, NULL); +#else +static DRIVER_ATTR_RO(esd_protection); +#endif + +static ssize_t slot_protocol_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "CONFIG_CTS_SLOTPROTOCOL: %c\n", +#ifdef CONFIG_CTS_SLOTPROTOCOL + 'Y' +#else + 'N' +#endif + ); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(slot_protocol, S_IRUGO, slot_protocol_show, NULL); +#else +static DRIVER_ATTR_RO(slot_protocol); +#endif + +static ssize_t max_xfer_size_show(struct device_driver *dev, char *buf) +{ +#ifdef CONFIG_CTS_I2C_HOST + return snprintf(buf, PAGE_SIZE, "CFG_CTS_MAX_I2C_XFER_SIZE: %d\n", + CFG_CTS_MAX_I2C_XFER_SIZE); +#else + return snprintf(buf, PAGE_SIZE, "CFG_CTS_MAX_SPI_XFER_SIZE: %d\n", + CFG_CTS_MAX_SPI_XFER_SIZE); +#endif +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(max_xfer_size, S_IRUGO, max_xfer_size_show, NULL); +#else +static DRIVER_ATTR_RO(max_xfer_size); +#endif + +static ssize_t driver_info_show(struct device_driver *dev, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "Driver version: %s\n", + CFG_CTS_DRIVER_VERSION); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +static DRIVER_ATTR(driver_info, S_IRUGO, driver_info_show, NULL); +#else +static DRIVER_ATTR_RO(driver_info); +#endif + +static struct attribute *cts_i2c_driver_config_attrs[] = { + &driver_attr_reset_pin.attr, + &driver_attr_swap_xy.attr, + &driver_attr_wrap_x.attr, + &driver_attr_wrap_y.attr, + &driver_attr_force_update.attr, + &driver_attr_max_touch_num.attr, + &driver_attr_vkey.attr, + &driver_attr_gesture.attr, + &driver_attr_esd_protection.attr, + &driver_attr_slot_protocol.attr, + &driver_attr_max_xfer_size.attr, + &driver_attr_driver_info.attr, + NULL +}; + +static const struct attribute_group cts_i2c_driver_config_group = { + .name = "config", + .attrs = cts_i2c_driver_config_attrs, +}; + +static const struct attribute_group *cts_i2c_driver_config_groups[] = { + &cts_i2c_driver_config_group, + NULL, +}; +#endif /* CONFIG_CTS_SYSFS */ + +#ifdef CONFIG_CTS_OF +static const struct of_device_id cts_i2c_of_match_table[] = { + {.compatible = CFG_CTS_OF_DEVICE_ID_NAME, }, + { }, +}; + +MODULE_DEVICE_TABLE(of, cts_i2c_of_match_table); +#endif /* CONFIG_CTS_OF */ + +#ifdef CONFIG_CTS_I2C_HOST +static const struct i2c_device_id cts_device_id_table[] = { + { CFG_CTS_DEVICE_NAME, 0 }, + { } +}; +#else +static const struct spi_device_id cts_device_id_table[] = { + { CFG_CTS_DEVICE_NAME, 0 }, + { } +}; +#endif + +#ifdef CONFIG_CTS_I2C_HOST +static struct i2c_driver cts_i2c_driver = { +#else +static struct spi_driver cts_spi_driver = { +#endif + .probe = cts_driver_probe, + .remove = cts_driver_remove, + .driver = { + .name = CFG_CTS_DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_CTS_OF + .of_match_table = of_match_ptr(cts_i2c_of_match_table), +#endif /* CONFIG_CTS_OF */ +#ifdef CONFIG_CTS_SYSFS + .groups = cts_i2c_driver_config_groups, +#endif /* CONFIG_CTS_SYSFS */ +#ifdef CONFIG_CTS_PM_LEGACY + .suspend = cts_i2c_driver_suspend, + .resume = cts_i2c_driver_resume, +#endif /* CONFIG_CTS_PM_LEGACY */ +#if defined(CONFIG_CTS_PM_GENERIC) || defined(TOUCHSCREEN_PM_BRL_SPI) + .pm = &cts_i2c_driver_pm_ops, +#endif /* CONFIG_CTS_PM_GENERIC */ + + }, + .id_table = cts_device_id_table, +}; + +static int __init cts_driver_init(void) +{ + cts_info("Chipone touch driver init, version: "CFG_CTS_DRIVER_VERSION); + +#ifdef CONFIG_CTS_I2C_HOST + cts_info(" - Register i2c driver"); + return i2c_add_driver(&cts_i2c_driver); +#else + cts_info(" - Register spi driver"); + return spi_register_driver(&cts_spi_driver); +#endif +} + +static void __exit cts_driver_exit(void) +{ + cts_info("Exit"); + +#ifdef CONFIG_CTS_I2C_HOST + i2c_del_driver(&cts_i2c_driver); +#else + spi_unregister_driver(&cts_spi_driver); +#endif +} + +module_init(cts_driver_init); +module_exit(cts_driver_exit); + +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +MODULE_DESCRIPTION("Chipone TDDI touchscreen Driver for QualComm platform"); +MODULE_VERSION(CFG_CTS_DRIVER_VERSION); +MODULE_AUTHOR("Miao Defang "); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_earjack_detect.c b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_earjack_detect.c new file mode 100644 index 000000000000..4741e174defb --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_earjack_detect.c @@ -0,0 +1,650 @@ +#define LOG_TAG "Earjack" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sysfs.h" +#include "cts_strerror.h" + +#ifdef CONFIG_CTS_EARJACK_DETECT +struct cts_earjack_detect_data { + bool enable; + bool running; + bool state; + + const char *earjack_state_filepath; + struct delayed_work poll_work; + u32 poll_interval; + +#ifdef CONFIG_CTS_SYSFS + bool sysfs_attr_group_created; +#endif /* CONFIG_CTS_SYSFS */ + + struct chipone_ts_data *cts_data; +}; + +/* Over-ride setting for DTS */ +//#define CFG_CTS_DEF_EARJACK_DET_ENABLE + +#define CFG_CTS_DEF_EARJACK_POLL_INTERVAL 2000u +#if defined(CONFIG_MTK_PLATFORM) +#define CFG_CTS_DEF_EARJACK_STATE_FILEPATH \ + "/sys/bus/platform/drivers/Accdet_Driver/state" +#elif defined(CONFIG_ARCH_SPREADRUM) +#define CFG_CTS_DEF_EARJACK_STATE_FILEPATH \ + "/sys/kernel/headset/state" +#else +/* Current QCOM NOT support earjack detect, please disable it or define the file path */ +#define CFG_CTS_DEF_EARJACK_STATE_FILEPATH "" +#endif + +static const char *earjack_state_str(bool state) +{ + return state ? "ATTACHED" : "DETACHED"; +} + +static int parse_earjack_detect_dt(struct cts_earjack_detect_data *ed_data, + struct device_node *np) +{ + const char *filepath; + int ret; + + cts_info("Parse dt"); + +#ifdef CFG_CTS_DEF_EARJACK_DET_ENABLE + ed_data->enable = true; +#else /* CFG_CTS_DEF_EARJACK_DET_ENABLE */ + ed_data->enable = + of_property_read_bool(np, "chipone,touch-earjack-detect-enable"); +#endif /* CFG_CTS_DEF_EARJACK_DET_ENABLE */ + + ret = of_property_read_string(np, + "chipone,touch-earjack-state-filepath", &filepath); + if (ret) { + cts_warn("Parse state filepath failed %d(%s)", ret, cts_strerror(ret)); + filepath = CFG_CTS_DEF_EARJACK_STATE_FILEPATH; + } + ed_data->earjack_state_filepath = kstrdup(filepath, GFP_KERNEL); + if (ed_data->earjack_state_filepath == NULL) { + cts_err("Dup earjack state filepath failed"); + return -ENOMEM; + } + + ed_data->poll_interval = CFG_CTS_DEF_EARJACK_POLL_INTERVAL; + ret = of_property_read_u32(np, + "chipone,touch-earjack-poll-interval", &ed_data->poll_interval); + if (ret) { + cts_warn("Parse poll interval failed %d(%s)", ret, cts_strerror(ret)); + } + + return 0; +} + +static int start_earjack_detect(struct cts_earjack_detect_data *ed_data) +{ + if (!ed_data->enable) { + cts_warn("Start detect while NOT enabled"); + return -EINVAL; + } + + if (ed_data->running) { + cts_warn("Start detect while already RUNNING"); + return 0; + } + + if (ed_data->earjack_state_filepath == NULL || + ed_data->earjack_state_filepath[0] == '\n') { + cts_warn("Start detect with filepath = NULL/NUL"); + return -EINVAL; + } + + cts_info("Start detect check file: '%s'", ed_data->earjack_state_filepath); + + if (!queue_delayed_work(ed_data->cts_data->workqueue, + &ed_data->poll_work, + msecs_to_jiffies(ed_data->poll_interval))) { + cts_warn("Queue detect work while already on the queue"); + } + + ed_data->running = true; + + return 0; +} + +static int stop_earjack_detect(struct cts_earjack_detect_data *ed_data) +{ + if (!ed_data->running) { + cts_warn("Stop detect while NOT running"); + return 0; + } + + cts_info("Stop detect"); + + if (!cancel_delayed_work_sync(&ed_data->poll_work)) { + cts_warn("Cancel poll work while NOT pending"); + } + ed_data->running = false; + + return 0; +} + +static int get_earjack_state(struct cts_earjack_detect_data *ed_data) +{ + int ret; + char buff[10]; + u32 state; +#ifndef CFG_CTS_FOR_GKI + struct file *file = NULL; + loff_t pos = 0; + int read_size; +#endif + + cts_dbg("Get state from file '%s'", ed_data->earjack_state_filepath); + +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): filp_open is forbiddon with GKI Version!", __func__); +#else + file = filp_open(ed_data->earjack_state_filepath, O_RDONLY, 0); + if (IS_ERR(file)) { + ret = (int)PTR_ERR(file); + cts_err("Open file '%s' failed %d(%s)", + ed_data->earjack_state_filepath, ret, cts_strerror(ret)); + return PTR_ERR(file); + } + + memset(buff, 0, sizeof(buff)); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + read_size = kernel_read(file, buff, sizeof(buff), &pos); +#else + read_size = kernel_read(file, pos, buff, sizeof(buff)); +#endif + if (read_size < 0) { + cts_err("Read state file '%s' failed %d(%s)", + ed_data->earjack_state_filepath, + read_size, cts_strerror(read_size)); + filp_close(file, NULL); + return read_size; + } + + cts_dbg("Read state file content: '%s'", buff); + + ret = filp_close(file, NULL); + if (ret) { + cts_warn("Close file '%s' failed %d(%s)", + ed_data->earjack_state_filepath, ret, cts_strerror(ret)); + } +#endif + + ret = kstrtou32(buff, 0, &state); + if (ret) { + cts_err("Invalid string from state file: '%s'", buff); + return ret; + } + + /* Assume "0" means detached */ + ed_data->state = !!state; + + cts_dbg("State: %s", earjack_state_str(ed_data->state)); + + return 0; +} + +/* Sysfs */ +#ifdef CONFIG_CTS_SYSFS +extern int argc; +extern char *argv[]; +extern int parse_arg(const char *buf, size_t count); + +#define EARJACK_DET_SYSFS_GROUP_NAME "earjack-det" + +static int enable_earjack_detect(struct cts_earjack_detect_data *ed_data) +{ + cts_info("Enable detect"); + + ed_data->enable = true; + + return 0; +} + +static int disable_earjack_detect(struct cts_earjack_detect_data *ed_data) +{ + int ret; + + cts_info("Disable detect"); + + ret = stop_earjack_detect(ed_data); + if (ret) { + cts_err("Stop detect failed %d(%s)", ret, cts_strerror(ret)); + } + + ed_data->enable = false; + + return 0; +} + +static ssize_t earjack_detect_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_earjack_detect_data *ed_data = cts_data->earjack_detect_data; + + cts_info("Read sysfs '"EARJACK_DET_SYSFS_GROUP_NAME"/%s'", attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, + "Earjack detect: %s\n", + ed_data->enable ? "ENABLED" : "DISABLED"); +} + +/* Echo 0/n/N/of/Of/Of/OF/1/y/Y/on/oN/On/ON > enable */ +static ssize_t earjack_detect_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_earjack_detect_data *ed_data = cts_data->earjack_detect_data; + bool enable; + int ret; + + cts_info("Write sysfs '"EARJACK_DET_SYSFS_GROUP_NAME"/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num of args"); + return -EINVAL; + } + + ret = kstrtobool(argv[0], &enable); + if (ret) { + cts_err("Invalid param of enable"); + return ret; + } + + if (enable) { + ret = enable_earjack_detect(ed_data); + } else { + ret = disable_earjack_detect(ed_data); + } + if (ret) { + cts_err("%s earjack detect failed %d(%s)", + enable ? "Enable" : "Disable", ret, cts_strerror(ret)); + return ret; + } + + return count; +} +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + earjack_detect_enable_show, earjack_detect_enable_store); + +static ssize_t earjack_detect_running_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_earjack_detect_data *ed_data = cts_data->earjack_detect_data; + + cts_info("Read sysfs '"EARJACK_DET_SYSFS_GROUP_NAME"/%s'", + attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, + "Earjack detect: %sRunning\n", + ed_data->running ? "" : "Not-"); +} + +/* Echo 0/n/N/of/Of/Of/OF/1/y/Y/on/oN/On/ON > runing */ +static ssize_t earjack_detect_running_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_earjack_detect_data *ed_data = cts_data->earjack_detect_data; + bool running; + int ret; + + cts_info("Write sysfs '"EARJACK_DET_SYSFS_GROUP_NAME"/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num of args"); + return -EINVAL; + } + + ret = kstrtobool(argv[0], &running); + if (ret) { + cts_err("Invalid param of running"); + return ret; + } + + if (running) { + ret = start_earjack_detect(ed_data); + } else { + ret = stop_earjack_detect(ed_data); + } + if (ret) { + cts_err("%s earjack detect failed %d(%s)", + running ? "Start" : "Stop", ret, cts_strerror(ret)); + return ret; + } + + return count; +} +static DEVICE_ATTR(running, S_IWUSR | S_IRUGO, + earjack_detect_running_show, earjack_detect_running_store); + +static ssize_t earjack_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_earjack_detect_data *ed_data = cts_data->earjack_detect_data; + int ret; + + cts_info("Read sysfs '"EARJACK_DET_SYSFS_GROUP_NAME"/%s'", + attr->attr.name); + + ret = get_earjack_state(ed_data); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Get earjack state failed %d(%s)\n", + ret, cts_strerror(ret)); + } + + return scnprintf(buf, PAGE_SIZE, + "Earjack state: %s\n", earjack_state_str(ed_data->state)); +} +static DEVICE_ATTR(state, S_IRUGO, earjack_state_show, NULL); + +static ssize_t earjack_detect_param_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_earjack_detect_data *ed_data = cts_data->earjack_detect_data; + + cts_info("Read sysfs '"EARJACK_DET_SYSFS_GROUP_NAME"/%s'", + attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, + "Filepath: '%s'\n" + "Poll int: %dms\n", + ed_data->earjack_state_filepath, ed_data->poll_interval); +} + +/* echo filepath [interval] > param */ +static ssize_t earjack_detect_param_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_earjack_detect_data *ed_data = cts_data->earjack_detect_data; + u32 interval; + int ret; + + cts_info("Write sysfs '"EARJACK_DET_SYSFS_GROUP_NAME"/%s' size %zu", + attr->attr.name, count); + + parse_arg(buf, count); + + if (argc < 1 || argc > 2) { + cts_err("Invalid num of args"); + return -EINVAL; + } + + interval = ed_data->poll_interval; + if (argc > 1) { + ret = kstrtou32(argv[1], 0, &interval); + if (ret) { + cts_err("Arg interval is invalid"); + return -EINVAL; + } + } + + if (ed_data->earjack_state_filepath) { + kfree(ed_data->earjack_state_filepath); + } + ed_data->earjack_state_filepath = kstrdup(argv[0], GFP_KERNEL); + if (ed_data->earjack_state_filepath == NULL) { + cts_err("Dup earjack state filepath failed"); + return -ENOMEM; + } + ed_data->poll_interval = interval; + + return count; +} +static DEVICE_ATTR(param, S_IWUSR | S_IRUGO, + earjack_detect_param_show, earjack_detect_param_store); + +static struct attribute *earjack_detect_attrs[] = { + &dev_attr_enable.attr, + &dev_attr_running.attr, + &dev_attr_state.attr, + &dev_attr_param.attr, + NULL +}; + +static const struct attribute_group earjack_detect_attr_group = { + .name = EARJACK_DET_SYSFS_GROUP_NAME, + .attrs = earjack_detect_attrs, +}; +#endif /* CONFIG_CTS_SYSFS */ + +static void poll_earjack_state_work(struct work_struct *work) +{ + struct cts_earjack_detect_data *ed_data; + bool prev_state = false; + int ret; + + cts_dbg("Poll earjack state work"); + + ed_data = container_of(to_delayed_work(work), + struct cts_earjack_detect_data, poll_work); + + prev_state = ed_data->state; + + ret = get_earjack_state(ed_data); + if (ret) { + cts_err("Get state failed %d(%s)", ret, cts_strerror(ret)); + } else { + if (ed_data->state != prev_state) { + cts_info("State changed: %s -> %s", + earjack_state_str(prev_state), + earjack_state_str(ed_data->state)); + + cts_lock_device(&ed_data->cts_data->cts_dev); + ret = cts_set_dev_earjack_attached( + &ed_data->cts_data->cts_dev, ed_data->state); + cts_unlock_device(&ed_data->cts_data->cts_dev); + if (ret) { + cts_err("Set dev earjack attached to %s failed %d(%s)", + earjack_state_str(ed_data->state), + ret, cts_strerror(ret)); + /* Set to previous state, try set again in next loop */ + ed_data->state = prev_state; + } + } + } + + if (!queue_delayed_work(ed_data->cts_data->workqueue, + &ed_data->poll_work, + msecs_to_jiffies(ed_data->poll_interval))) { + cts_warn("Queue detect work while already on the queue"); + } +} + +int cts_earjack_detect_init(struct chipone_ts_data *cts_data) +{ + struct cts_earjack_detect_data *ed_data; + int ret = 0; + + if (cts_data == NULL) { + cts_err("Init detect with cts_data = NULL"); + return -EFAULT; + } + + ed_data = kzalloc(sizeof(*ed_data), GFP_KERNEL); + if (ed_data == NULL) { + cts_err("Alloc earjack detect data failed"); + return -ENOMEM; + } + + cts_info("Init detect"); + +#ifdef CONFIG_CTS_OF + ret = parse_earjack_detect_dt(ed_data, cts_data->device->of_node); +#else /* CONFIG_CTS_OF */ +#ifdef CFG_CTS_DEF_EARJACK_DET_ENABLE + ed_data->enable = true; +#else /* CFG_CTS_DEF_EARJACK_DET_ENABLE */ + ed_data->enable = false; +#endif /* CFG_CTS_DEF_EARJACK_DET_ENABLE */ + ed_data->poll_interval = CFG_CTS_DEF_EARJACK_POLL_INTERVAL; + ed_data->earjack_state_filepath = kstrdup( + CFG_CTS_DEF_EARJACK_STATE_FILEPATH, GFP_KERNEL); + if (ed_data->earjack_state_filepath == NULL) { + cts_err("Dup earjack state filepath failed"); + ret = -ENOMEM; + } +#endif /* CONFIG_CTS_OF */ + if (ret) { + cts_err("Get detect param failed %d(%s)", + ret, cts_strerror(ret)); + goto free_ed_data; + } + + cts_info("Detect: %sABLED", ed_data->enable ? "EN" : "DIS"); + cts_info(" Filepath: '%s'", ed_data->earjack_state_filepath); + cts_info(" Poll Int: %dms", ed_data->poll_interval); + + INIT_DELAYED_WORK(&ed_data->poll_work, poll_earjack_state_work); + +#ifdef CONFIG_CTS_SYSFS + cts_info("Create sysfs attr group '%s'", EARJACK_DET_SYSFS_GROUP_NAME); + ret = sysfs_create_group(&cts_data->device->kobj, + &earjack_detect_attr_group); + if (ret) { + cts_warn("Create sysfs attr group '%s' failed %d(%s)", + EARJACK_DET_SYSFS_GROUP_NAME, ret, cts_strerror(ret)); + } else { + ed_data->sysfs_attr_group_created = true; + } +#endif /* CONFIG_CTS_SYSFS */ + + cts_data->earjack_detect_data = ed_data; + ed_data->cts_data = cts_data; + + return 0; + +free_ed_data: + kfree(ed_data); + + return ret; +} + +int cts_earjack_detect_deinit(struct chipone_ts_data *cts_data) +{ + struct cts_earjack_detect_data *ed_data; + int ret; + + if (cts_data == NULL) { + cts_err("Deinit detect with cts_data = NULL"); + return -EFAULT; + } + + ed_data = cts_data->earjack_detect_data; + if (ed_data == NULL) { + cts_warn("Deinit detect with earjack_detect_data = NULL"); + return 0; + } + + cts_info("Deinit detect"); + + if (ed_data->running) { + ret = stop_earjack_detect(ed_data); + if (ret) { + cts_err("Stop detect failed %d(%s)", + ret, cts_strerror(ret)); + } + } + +#ifdef CONFIG_CTS_SYSFS + if (ed_data->sysfs_attr_group_created) { + cts_info("Remove sysfs attr group '%s'", EARJACK_DET_SYSFS_GROUP_NAME); + sysfs_remove_group(&cts_data->device->kobj, &earjack_detect_attr_group); + ed_data->sysfs_attr_group_created = false; + } +#endif /* CONFIG_CTS_SYSFS */ + + if(ed_data->earjack_state_filepath) { + kfree(ed_data->earjack_state_filepath); + ed_data->earjack_state_filepath = NULL; + } + + kfree(ed_data); + cts_data->earjack_detect_data = NULL; + + return 0; +} + +int cts_is_earjack_attached(struct chipone_ts_data *cts_data, bool *attached) +{ + struct cts_earjack_detect_data *ed_data; + int ret; + + if (cts_data == NULL) { + cts_err("Get state with cts_data = NULL"); + return -EFAULT; + } + + ed_data = cts_data->earjack_detect_data; + if (ed_data == NULL) { + cts_err("Get state with earjack_detect_data = NULL"); + return -ENODEV; + } + + ret = get_earjack_state(ed_data); + if (ret) { + cts_err("Get state failed %d(%s)", ret, cts_strerror(ret)); + return ret; + } + + cts_info("Get curr state: %s", earjack_state_str(ed_data->state)); + + *attached = ed_data->state; + + return 0; +} + +int cts_start_earjack_detect(struct chipone_ts_data *cts_data) +{ + struct cts_earjack_detect_data *ed_data; + + if (cts_data == NULL) { + cts_err("Start detect with cts_data = NULL"); + return -EFAULT; + } + + ed_data = cts_data->earjack_detect_data; + if (ed_data == NULL) { + cts_err("Start detect with earjack_detect_data = NULL"); + return -ENODEV; + } + + return start_earjack_detect(ed_data); +} + +int cts_stop_earjack_detect(struct chipone_ts_data *cts_data) +{ + struct cts_earjack_detect_data *ed_data; + + if (cts_data == NULL) { + cts_err("Stop detect with cts_data = NULL"); + return -EFAULT; + } + + ed_data = cts_data->earjack_detect_data; + if (ed_data == NULL) { + cts_err("Stop detect with earjack_detect_data = NULL"); + return -ENODEV; + } + + return stop_earjack_detect(ed_data); +} +#endif /* CONFIG_CTS_EARJACK_DETECT */ + diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_earjack_detect.h b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_earjack_detect.h new file mode 100644 index 000000000000..1a0368df6413 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_earjack_detect.h @@ -0,0 +1,40 @@ +#ifndef CTS_EARJACK_DETECT_H +#define CTS_EARJACK_DETECT_H + +#include "cts_config.h" + +struct chipone_ts_data; + +#ifdef CONFIG_CTS_EARJACK_DETECT +extern int cts_earjack_detect_init(struct chipone_ts_data *cts_data); +extern int cts_earjack_detect_deinit(struct chipone_ts_data *cts_data); +extern int cts_start_earjack_detect(struct chipone_ts_data *cts_data); +extern int cts_stop_earjack_detect(struct chipone_ts_data *cts_data); +extern int cts_is_earjack_attached(struct chipone_ts_data *cts_data, + bool *attached); +#else /* CONFIG_CTS_EARJACK_DETECT */ +static inline int cts_earjack_detect_init(struct chipone_ts_data *cts_data) +{ + return -ENOTSUPP; +} +static inline int cts_earjack_detect_deinit(struct chipone_ts_data *cts_data) +{ + return -ENOTSUPP; +} +static inline int cts_start_earjack_detect(struct chipone_ts_data *cts_data) +{ + return -ENODEV; +} +static inline int cts_stop_earjack_detect(struct chipone_ts_data *cts_data) +{ + return -ENODEV; +} +static inline int cts_is_earjack_attached(struct chipone_ts_data *cts_data, + bool *attached) +{ + return -ENODEV; +} +#endif /* CONFIG_CTS_EARJACK_DETECT */ + +#endif /* CTS_EARJACK_DETECT_H */ + diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_firmware.c b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_firmware.c new file mode 100644 index 000000000000..c9d828f2952a --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_firmware.c @@ -0,0 +1,1113 @@ +#define LOG_TAG "Firmware" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sfctrl.h" +#include "cts_spi_flash.h" +#include "cts_firmware.h" +#include "cts_strerror.h" +#include +#include +#include + + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE +#include "cts_builtin_firmware.h" +#define NUM_DRIVER_BUILTIN_FIRMWARE ARRAY_SIZE(cts_driver_builtin_firmwares) +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ + +#define CTS_FIRMWARE_MULTI_SECTION_FILE_SIZE (0x20000) +#define CTS_SECTION_ENABLE_FLAG (0x0000C35A) + +#ifndef CONFIG_CTS_ICTYPE_ICNL9951 +enum cts_firmware_section_offset { + CTS_FIRMWARE_SECTION_OFFSET = 0x00000000, + CTS_FIRMWARE_CRC_SECTION_OFFSET = 0x17000, + CTS_DDIPARAM_SECTION_OFFSET = 0x00019000, + CTS_DDIPARAM_CRC_SECTION_OFFSET = 0x1B000, +}; +#else +enum cts_firmware_section_offset { + CTS_FIRMWARE_SECTION_OFFSET = 0x00000000, + CTS_FIRMWARE_CRC_SECTION_OFFSET = 0x30000, + CTS_DDIPARAM_SECTION_OFFSET = 0x00031000, + CTS_DDIPARAM_CRC_SECTION_OFFSET = 0x33000, +}; +#endif + +struct cts_firmware_sect_info { + const u8 *firmware_sect; + size_t firmware_sect_size; + const u8 *firmware_crc_sect; + size_t firmware_crc_sect_size; + u32 firmware_sect_crc; + + const u8 *ddiparam_sect; + size_t ddiparam_sect_size; + const u8 *ddiparam_crc_sect; + size_t ddiparam_crc_sect_size; + u32 ddiparam_sect_crc; + +}; + +#define FIRMWARE_SECTION(firmware) \ + ((firmware)->data) +#define FIRMWARE_CRC_SECTION(firmware) \ + ((firmware)->data + CTS_FIRMWARE_CRC_SECTION_OFFSET) +#define DDIPARAM_SECTION(firmware) \ + ((firmware)->data + CTS_DDIPARAM_SECTION_OFFSET) +#define DDIPARAM_CRC_SECTION(firmware) \ + ((firmware)->data + CTS_DDIPARAM_CRC_SECTION_OFFSET) + +#define FIRMWARE_SECTION_CRC(firmware) \ + (get_unaligned_le32(FIRMWARE_CRC_SECTION(firmware))) +#define FIRMWARE_SECTION_SIZE(firmware) \ + (get_unaligned_le32(FIRMWARE_CRC_SECTION(firmware) + 4)) +#define FIRMWARE_SECTION_CRC_ENABLE(firmware) \ + (get_unaligned_le32(FIRMWARE_CRC_SECTION(firmware) + 8)) +#define FIRMWARE_CRC_SECTION_SIZE (20) + +#define DDIPARAM_SECTION_ENABLE(firmware) \ + (get_unaligned_le32(DDIPARAM_CRC_SECTION(firmware))) +#define DDIPARAM_SECTION_CRC_ENABLE(firmware) \ + (get_unaligned_le32(DDIPARAM_CRC_SECTION(firmware) + 4)) +#define DDIPARAM_SECTION_CRC(firmware) \ + (get_unaligned_le32(DDIPARAM_CRC_SECTION(firmware) + 8)) +#define DDIPARAM_SECTION_SIZE(firmware) \ + (get_unaligned_le32(DDIPARAM_CRC_SECTION(firmware) + 12)) +#define DDIPARAM_CRC_SECTION_SIZE (17) + +u16 cts_crc16(const u8 *data, size_t len) +{ + u16 crc = 0; + + const static u16 crc16_table[] = { + 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, + 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, + 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, + 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, + 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, + 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, + 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, + 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, + 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, + 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, + 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, + 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, + 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, + 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, + 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, + 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, + 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202, + }; + + while (len) { + crc = (crc << 8) ^ crc16_table[((crc >> 8) ^ *data) & 0xFF]; + data++; + len--; + } + + return crc; +} + +u32 cts_crc32(const u8 *data, size_t len) +{ + const static u32 crc32_table[] = { + 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9, 0x130476DC, + 0x17C56B6B, + 0x1A864DB2, 0x1E475005, 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, + 0x2B4BCB61, + 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD, 0x4C11DB70, + 0x48D0C6C7, + 0x4593E01E, 0x4152FDA9, 0x5F15ADAC, 0x5BD4B01B, 0x569796C2, + 0x52568B75, + 0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011, 0x791D4014, + 0x7DDC5DA3, + 0x709F7B7A, 0x745E66CD, 0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, + 0x95609039, + 0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5, 0xBE2B5B58, + 0xBAEA46EF, + 0xB7A96036, 0xB3687D81, 0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, + 0xA06C0B5D, + 0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49, 0xC7361B4C, + 0xC3F706FB, + 0xCEB42022, 0xCA753D95, 0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, + 0xFF79A6F1, + 0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D, 0x34867077, + 0x30476DC0, + 0x3D044B19, 0x39C556AE, 0x278206AB, 0x23431B1C, 0x2E003DC5, + 0x2AC12072, + 0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16, 0x018AEB13, + 0x054BF6A4, + 0x0808D07D, 0x0CC9CDCA, 0x7897AB07, 0x7C56B6B0, 0x71159069, + 0x75D48DDE, + 0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02, 0x5E9F46BF, + 0x5A5E5B08, + 0x571D7DD1, 0x53DC6066, 0x4D9B3063, 0x495A2DD4, 0x44190B0D, + 0x40D816BA, + 0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E, 0xBFA1B04B, + 0xBB60ADFC, + 0xB6238B25, 0xB2E29692, 0x8AAD2B2F, 0x8E6C3698, 0x832F1041, + 0x87EE0DF6, + 0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A, 0xE0B41DE7, + 0xE4750050, + 0xE9362689, 0xEDF73B3E, 0xF3B06B3B, 0xF771768C, 0xFA325055, + 0xFEF34DE2, + 0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686, 0xD5B88683, + 0xD1799B34, + 0xDC3ABDED, 0xD8FBA05A, 0x690CE0EE, 0x6DCDFD59, 0x608EDB80, + 0x644FC637, + 0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB, 0x4F040D56, + 0x4BC510E1, + 0x46863638, 0x42472B8F, 0x5C007B8A, 0x58C1663D, 0x558240E4, + 0x51435D53, + 0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47, 0x36194D42, + 0x32D850F5, + 0x3F9B762C, 0x3B5A6B9B, 0x0315D626, 0x07D4CB91, 0x0A97ED48, + 0x0E56F0FF, + 0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623, 0xF12F560E, + 0xF5EE4BB9, + 0xF8AD6D60, 0xFC6C70D7, 0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, + 0xEF68060B, + 0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F, 0xC423CD6A, + 0xC0E2D0DD, + 0xCDA1F604, 0xC960EBB3, 0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, + 0xB07DABA7, + 0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B, 0x9B3660C6, + 0x9FF77D71, + 0x92B45BA8, 0x9675461F, 0x8832161A, 0x8CF30BAD, 0x81B02D74, + 0x857130C3, + 0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640, 0x4E8EE645, + 0x4A4FFBF2, + 0x470CDD2B, 0x43CDC09C, 0x7B827D21, 0x7F436096, 0x7200464F, + 0x76C15BF8, + 0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24, 0x119B4BE9, + 0x155A565E, + 0x18197087, 0x1CD86D30, 0x029F3D35, 0x065E2082, 0x0B1D065B, + 0x0FDC1BEC, + 0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088, 0x2497D08D, + 0x2056CD3A, + 0x2D15EBE3, 0x29D4F654, 0xC5A92679, 0xC1683BCE, 0xCC2B1D17, + 0xC8EA00A0, + 0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C, 0xE3A1CBC1, + 0xE760D676, + 0xEA23F0AF, 0xEEE2ED18, 0xF0A5BD1D, 0xF464A0AA, 0xF9278673, + 0xFDE69BC4, + 0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0, 0x9ABC8BD5, + 0x9E7D9662, + 0x933EB0BB, 0x97FFAD0C, 0xAFB010B1, 0xAB710D06, 0xA6322BDF, + 0xA2F33668, + 0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4 + }; + + u32 crc = 0; + + while (len) { + crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ *data) & 0xFF]; + + data++; + len--; + } + + return crc; +} + +static int calc_crc_in_flash(const struct cts_device *cts_dev, + u32 flash_addr, size_t size, u32 *crc) +{ + return cts_dev->hwdata->sfctrl->ops->calc_flash_crc(cts_dev, + flash_addr, size, crc); +} + +static bool is_multi_section_firmware(const struct cts_firmware *firmware) +{ + return (firmware->size == CTS_FIRMWARE_MULTI_SECTION_FILE_SIZE); +} + +static bool is_single_section_firmware(const struct cts_firmware *firmware) +{ + return !(is_multi_section_firmware(firmware)); +} + +static bool is_firmware_size_valid(const struct cts_firmware *firmware) +{ + return (firmware->size > 0x102 && + firmware->size <= CTS_FIRMWARE_MULTI_SECTION_FILE_SIZE); +} + +static bool is_multi_section_firmware_valid(const struct cts_firmware *firmware) +{ + u32 crc; + + crc = cts_crc32(FIRMWARE_SECTION(firmware), FIRMWARE_SECTION_SIZE(firmware)); + if (crc != FIRMWARE_SECTION_CRC(firmware)) { + cts_err("Firmware-section crc mismatch crc-section %08x != %08x" + ", File maybe broken!!!", crc, + FIRMWARE_SECTION_CRC(firmware)); + return false; + } + + if (DDIPARAM_SECTION_ENABLE(firmware) == CTS_SECTION_ENABLE_FLAG) { + crc = cts_crc32(DDIPARAM_SECTION(firmware), + DDIPARAM_SECTION_SIZE(firmware)); + if (crc != DDIPARAM_SECTION_CRC(firmware)) { + cts_err("DDIParam-section crc mismatch crc-section %08x != %08x" + ", File maybe broken!!!", crc, + DDIPARAM_SECTION_CRC(firmware)); + return false; + } + } else + cts_info("DDIParam-section is NOT enabled"); + + return true; +} + +static bool is_firmware_valid(const struct cts_firmware *firmware) +{ + if (firmware && firmware->data && is_firmware_size_valid(firmware)) { + if (is_single_section_firmware(firmware) + || is_multi_section_firmware_valid(firmware)) + return true; + } + + return false; +} + +static void parse_single_section_firmware(const struct cts_firmware *firmware, + struct cts_firmware_sect_info *info) +{ + static u8 crc_sect[20] = {0xff}; + + info->firmware_sect = firmware->data; + info->firmware_sect_size = firmware->size; + info->firmware_sect_crc = cts_crc32(firmware->data, firmware->size); + + put_unaligned_le32(info->firmware_sect_crc, crc_sect); + put_unaligned_le32(info->firmware_sect_size, crc_sect + 4); + put_unaligned_le32(~0x0000C35A, crc_sect + 8); /* Enable CRC check */ + put_unaligned_le32(0x7473756E, crc_sect + 16); + info->firmware_crc_sect = crc_sect; + info->firmware_crc_sect_size = sizeof(crc_sect); +} + +static void parse_multi_section_firmware(const struct cts_firmware *firmware, + struct cts_firmware_sect_info *info) +{ + info->firmware_sect = FIRMWARE_SECTION(firmware); + info->firmware_sect_size = FIRMWARE_SECTION_SIZE(firmware); + info->firmware_crc_sect = FIRMWARE_CRC_SECTION(firmware); + info->firmware_crc_sect_size = FIRMWARE_CRC_SECTION_SIZE; + info->firmware_sect_crc = FIRMWARE_SECTION_CRC(firmware); + + if (DDIPARAM_SECTION_ENABLE(firmware) == CTS_SECTION_ENABLE_FLAG) { + info->ddiparam_sect = DDIPARAM_SECTION(firmware); + info->ddiparam_sect_size = DDIPARAM_SECTION_SIZE(firmware); + info->ddiparam_crc_sect = DDIPARAM_CRC_SECTION(firmware); + info->ddiparam_crc_sect_size = DDIPARAM_CRC_SECTION_SIZE; + info->ddiparam_sect_crc = DDIPARAM_SECTION_CRC(firmware); + } +} + +static int parse_firmware(const struct cts_firmware *firmware, + struct cts_firmware_sect_info *info) +{ + memset(info, 0, sizeof(*info)); + + if (is_multi_section_firmware(firmware)) + parse_multi_section_firmware(firmware, info); + else + parse_single_section_firmware(firmware, info); + + cts_info(" Firmware section size: %zu", info->firmware_sect_size); + if (info->ddiparam_crc_sect) + cts_info(" DDIParam section size: %zu", info->ddiparam_sect_size); + + return 0; +} + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE +#ifdef CONFIG_CTS_SYSFS +int cts_get_num_driver_builtin_firmware(void) +{ + return NUM_DRIVER_BUILTIN_FIRMWARE; +} + +const struct cts_firmware *cts_request_driver_builtin_firmware_by_name(const char *name) +{ + const struct cts_firmware *firmware; + int i; + + cts_info("Request driver builtin by name '%s'", name); + + firmware = cts_driver_builtin_firmwares; + for (i = 0; i < NUM_DRIVER_BUILTIN_FIRMWARE; i++, firmware++) { + if (strcmp(firmware->name, name) == 0) { + if (is_firmware_valid(firmware)) { + cts_info("Found driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu ver: %04x", + firmware->name, firmware->hwid, + firmware->fwid, firmware->size, + FIRMWARE_VERSION(firmware)); + return firmware; + } + + cts_warn("Found driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu invalid", + firmware->name, firmware->hwid, firmware->hwid, + firmware->size); + } + } + + return NULL; +} + +const struct cts_firmware *cts_request_driver_builtin_firmware_by_index( + u32 index) +{ + const struct cts_firmware *firmware; + + cts_info("Request driver builtin by index %u", index); + + if (index < NUM_DRIVER_BUILTIN_FIRMWARE) { + firmware = cts_driver_builtin_firmwares + index; + if (is_firmware_valid(firmware)) { + cts_info("Found driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu ver: %04x", + firmware->name, firmware->hwid, firmware->fwid, + firmware->size, FIRMWARE_VERSION(firmware)); + return firmware; + } + cts_warn("Found driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu INVALID", + firmware->name, firmware->hwid, firmware->hwid, + firmware->size); + } else + cts_warn("Request driver builtin by index %u too large >= %zu", + index, NUM_DRIVER_BUILTIN_FIRMWARE); + + return NULL; +} +#endif /* CONFIG_CTS_SYSFS */ + +static const struct cts_firmware *cts_request_newer_driver_builtin_firmware( + u32 hwid, u16 fwid, u16 device_fw_ver) +{ +#define MATCH_HWID(firmware, hwid) \ + ((hwid) == CTS_DEV_HWID_ANY || (firmware)->hwid == (hwid)) +#define MATCH_FWID(firmware, fwid) \ + ((fwid) == CTS_DEV_FWID_ANY || (firmware)->fwid == (fwid)) + + const struct cts_firmware *firmware = NULL; + int i; + + cts_info + ("Request driver builtin if match hwid: %06x fwid: %04x && ver > %04x", + hwid, fwid, device_fw_ver); + + firmware = cts_driver_builtin_firmwares; + for (i = 0; i < ARRAY_SIZE(cts_driver_builtin_firmwares); i++, firmware++) { + if (MATCH_HWID(firmware, hwid) && MATCH_FWID(firmware, fwid)) { + if (!is_firmware_valid(firmware)) { + cts_err("Found driver builtin '%s' " + "hwid: %06x fwid: %04x data: %p size: %zu INVALID", + firmware->name, firmware->hwid, + firmware->fwid, firmware->data, + firmware->size); + continue; + } + + cts_info("Found matched driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu ver: %04x", + firmware->name, firmware->hwid, firmware->fwid, + firmware->size, FIRMWARE_VERSION(firmware)); + + if (FIRMWARE_VERSION(firmware) > device_fw_ver) { + cts_info("Found newer driver builtin '%s' " + "hwid: %06x fwid: %04x size: %zu ver: %04x > %04x", + firmware->name, firmware->hwid, + firmware->fwid, firmware->size, + FIRMWARE_VERSION(firmware), + device_fw_ver); + return firmware; + } + } + } + + cts_info("No newer driver builtin found"); + + return NULL; + +#undef MATCH_HWID +#undef MATCH_FWID +} +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ + +#ifdef CFG_CTS_FIRMWARE_IN_FS +bool is_filesystem_mounted(const char *filepath) +{ +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): some functions are forbiddon with GKI Version!", __func__); + return false; +#else + struct path root_path; + struct path path; + int ret; + + ret = kern_path("/", LOOKUP_FOLLOW, &root_path); + if (ret) + return false; + + ret = kern_path(filepath, LOOKUP_FOLLOW, &path); + if (ret) + goto err_put_root_path; + + if (path.mnt->mnt_sb == root_path.mnt->mnt_sb) + /* not mounted */ + ret = false; + else + ret = true; + + path_put(&path); +err_put_root_path: + path_put(&root_path); + + return !!ret; +#endif +} + +static int cts_request_firmware_full_filepath(struct cts_firmware *firmware, + const char *filepath, int curr_version) +{ +#ifdef CFG_CTS_FOR_GKI + cts_info("forbiddon with GKI! Please use request_firmware!"); + return -EPERM; +#else + struct file *file; + u16 version; + u8 buff[2]; + loff_t pos = 0; + int read_size; + int ret = 0; + + file = filp_open(filepath, O_RDONLY, 0); + if (IS_ERR(file)) { + cts_err("Open file '%s' failed %ld", filepath, PTR_ERR(file)); + return PTR_ERR(file); + } + + firmware->size = file_inode(file)->i_size; + if (!is_firmware_size_valid(firmware)) { + cts_info("File '%s' size: %zu invalid", filepath, firmware->size); + ret = -EINVAL; + goto err_close_file; + } + + pos = FIRMWARE_VERSION_OFFSET; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + read_size = kernel_read(file, buff, 2, &pos); +#else + read_size = kernel_read(file, pos, buff, 2); +#endif + if (read_size < 0) { + cts_err("Read version from offset 0x100 failed"); + ret = -EIO; + goto err_close_file; + } + version = get_unaligned_le16(buff); + + if (version <= curr_version) { + cts_info("File '%s' size: %zu version: %04x <= %04x", + filepath, firmware->size, version, curr_version); + ret = -EINVAL; + goto err_close_file; + } + + cts_info("File '%s' size: %zu version: %04x", + filepath, firmware->size, version); + + firmware->data = (u8 *) vmalloc(firmware->size); + if (firmware->data == NULL) { + cts_err("Request form fs alloc firmware data failed"); + ret = -ENOMEM; + goto err_close_file; + } + + pos = 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + read_size = kernel_read(file, firmware->data, firmware->size, &pos); +#else + read_size = kernel_read(file, pos, firmware->data, firmware->size); +#endif + if (read_size < 0 || read_size != firmware->size) { + cts_err("Request from fs read whole file failed %d", read_size); + ret = -EIO; + goto err_free_firmware_data; + } + + { + int r = filp_close(file, NULL); + + if (r) + cts_warn("Close file '%s' failed %d", filepath, r); + } + + return 0; + +err_free_firmware_data: + vfree(firmware->data); + firmware->data = NULL; + +err_close_file:{ + int r = filp_close(file, NULL); + + if (r) + cts_warn("Close file '%s' failed %d", filepath, r); + } + + return ret; +#endif +} + +static int cts_wrap_request_firmware(struct cts_firmware *firmware, + const char *name, struct device *device, int curr_version) +{ + int ret; + + ret = request_firmware(&firmware->fw, name, device); + if (ret) { + cts_err("Could not load firmware from %s: %d", name, ret); + return ret; + } + + /* Map firmware structure to cts_firmware */ + cts_info("cts_wrap_request_firmware +++\n"); + firmware->size = firmware->fw->size; + firmware->data = vmalloc(firmware->size); + if (firmware->data == NULL) { + cts_err("vmalloc firmware data failed"); + release_firmware(firmware->fw); + return -ENOMEM; + } + + memcpy(firmware->data, firmware->fw->data, firmware->size); + release_firmware(firmware->fw); + + /* Check firmware */ + if (!is_firmware_valid(firmware)) { + cts_err("Request firmware file INVALID, size %zu", firmware->size); + ret = -EINVAL; + goto err_release_firmware; + } + + /* Check firmware version */ + if (curr_version > 0 && curr_version >= FIRMWARE_VERSION(firmware)) { + cts_info("Request firmware file with version 0x%04x < 0x%04x", + FIRMWARE_VERSION(firmware), curr_version); + ret = -EINVAL; + goto err_release_firmware; + } + +err_release_firmware: + return ret; +} + +const struct cts_firmware *cts_request_newer_firmware_from_fs( + const struct cts_device *cts_dev, const char *filepath, u16 curr_version) +{ + struct cts_firmware *firmware = NULL; + int ret = 0; + + cts_info("Request from file '%s' if version > %04x", filepath, curr_version); + + if (filepath == NULL || filepath[0] == '\0') { + cts_err("Request from file path INVALID %p", filepath); + return NULL; + } + + firmware = (struct cts_firmware *)kzalloc(sizeof(*firmware), GFP_KERNEL); + if (firmware == NULL) { + cts_err("Request from file alloc struct cts_firmware failed"); + return NULL; + } + + if (strchr(filepath, '/') != NULL) { /* Full path */ + cts_info("Filepath is fullpath, direct read it out"); + ret = cts_request_firmware_full_filepath(firmware, filepath, curr_version); + } else { /* ONLY file name is givin */ + cts_info("Filepath is only filename, use request_firmware()"); + ret = cts_wrap_request_firmware(firmware, filepath, + &cts_dev->pdata->ts_input_dev->dev, curr_version); + } + + if (ret) { + cts_err("Request from file '%s' failed %d", filepath, ret); + kfree(firmware); + return NULL; + } else { + return firmware; + } +} + +const struct cts_firmware *cts_request_firmware_from_fs( + const struct cts_device *cts_dev, const char *filepath) +{ + cts_info("Request from file '%s'", filepath); + + return cts_request_newer_firmware_from_fs(cts_dev, filepath, 0); +} + +int cts_update_firmware_from_file(struct cts_device *cts_dev, + const char *filepath, bool to_flash) +{ + const struct cts_firmware *firmware; + int ret; + + cts_info("Update from file '%s' to %s", filepath, + to_flash ? "flash" : "sram"); + + firmware = cts_request_firmware_from_fs(cts_dev, filepath); + if (firmware == NULL) { + cts_err("Request from file '%s' failed", filepath); + return -EFAULT; + } + + ret = cts_update_firmware(cts_dev, firmware, to_flash); + if (ret) { + cts_err("Update to %s from file failed %d", + to_flash ? "flash" : "sram", ret); + goto err_release_firmware; + } + + cts_info("Update from file success"); + +err_release_firmware: + cts_release_firmware(firmware); + + return ret; +} +#endif /*CFG_CTS_FIRMWARE_IN_FS */ + +const struct cts_firmware *cts_request_firmware(const struct cts_device *cts_dev, + u32 hwid, u16 fwid, u16 curr_firmware_ver) +{ + const struct cts_firmware *firmware_builtin = NULL; + const struct cts_firmware *firmware_from_file = NULL; + + if (hwid == CTS_DEV_HWID_INVALID) + hwid = CTS_DEV_HWID_ANY; + if (fwid == CTS_DEV_FWID_INVALID) + fwid = CTS_DEV_FWID_ANY; + + cts_info("Request newer if match hwid: %06x fwid: %04x && ver > %04x", + hwid, fwid, curr_firmware_ver); + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE + firmware_builtin = cts_request_newer_driver_builtin_firmware( + hwid, fwid, curr_firmware_ver); +#endif + +#ifdef CFG_CTS_FIRMWARE_IN_FS +#ifndef CFG_CTS_FOR_GKI + if (is_filesystem_mounted(CFG_CTS_FIRMWARE_FILEPATH)) +#endif + firmware_from_file = cts_request_newer_firmware_from_fs(cts_dev, +#ifdef CFG_CTS_FW_UPDATE_FILE_LOAD + cts_dev->config_fw_name[0] ? cts_dev->config_fw_name : CFG_CTS_FIRMWARE_FILENAME, +#else + CFG_CTS_FIRMWARE_FILENAME, +#endif + firmware_builtin ? FIRMWARE_VERSION(firmware_builtin) : curr_firmware_ver); +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + + return firmware_from_file ? firmware_from_file : firmware_builtin; +} + +void cts_release_firmware(const struct cts_firmware *firmware) +{ + cts_info("Release firmware %p", firmware); + + if (firmware) { + if (firmware->data) { + /* Use request_firmware() get struct firmware * */ + cts_info("Release firmware from request_firmware"); + vfree(firmware->data); + kfree(firmware); + } else if (firmware->name == NULL) { + /* Direct read from file */ + cts_info("Release firmware from direct-load"); + vfree(firmware->data); + kfree(firmware); + } else + /* Builtin firmware with non-NULL name, no need to free */ + cts_info("Release firmware from driver built-in"); + } +} + +static int validate_flash_data(const struct cts_device *cts_dev, u32 flash_addr, + const u8 *data, size_t size, u8 *buf, bool calc_crc, u32 crc) +{ + int ret, i; + bool free_data = false; + u32 crc_flash; + + cts_info("Validate flash data from 0x%06x size %zu by %s", + flash_addr, size, calc_crc ? "check-crc" : "direct-readback"); + + if (calc_crc) { + ret = calc_crc_in_flash(cts_dev, flash_addr, size, &crc_flash); + if (ret) { + cts_err("Calc data in flash from 0x%06x size %zu crc failed %d, " + "try to validate by direct readback", flash_addr, size, ret); + /* FALL through by direct compare data read back */ + } else { + if (crc_flash != crc) { + cts_err("Crc in flash from 0x%06x size %zu mismatch 0x%08x != 0x%08x", + flash_addr, size, crc_flash, crc); + /* FALL through by direct compare data read back */ + } else { + cts_info("Flash data crc correct"); + return 0; + } + } + } + + if (buf == NULL) { + buf = (u8 *) kmalloc(size, GFP_KERNEL); + if (buf == NULL) { + cts_err("Validate flash data allocate mem failed"); + return -ENOMEM; + } + + free_data = true; + } + + ret = cts_read_flash(cts_dev, flash_addr, buf, size); + if (ret) { + cts_err("Read flash from 0x%06x size %zu failed %d", + flash_addr, size, ret); + goto err_free_buf; + } + + for (i = 0; i < size; i++) { + if (buf[i] != data[i]) { + if (ret == 0) { + cts_err("Flash data from 0x%06x size %zu first bytes diff:", + flash_addr, size); + } + + if (ret < 100) { + cts_err(" 0x%06x: %02x %02x", i, buf[i], data[i]); + } else if (ret == 100) { + cts_err(" ..."); + } + ret++; + } + } + +err_free_buf: + if (free_data) + kfree(buf); + + return ret; +} + +static int cts_program_firmware_from_sram_to_flash(const struct cts_device *cts_dev, + const struct cts_firmware_sect_info *firmware_info) +{ + int ret; + u8 crc_sect_buf[FIRMWARE_CRC_SECTION_SIZE]; + + cts_info("Program firmware size %zu", + firmware_info->firmware_sect_size); + + /* Write firmware to SRAM for easy enter normal mode after update */ + cts_info("Write firmware section to sram"); + ret = cts_sram_writesb_check_crc_retry(cts_dev, 0, + firmware_info->firmware_sect, + firmware_info->firmware_sect_size, + firmware_info->firmware_sect_crc, 3); + if (ret) { + cts_err("Write firmware section to sram failed %d", ret); + return ret; + } + + ret = cts_program_flash_from_sram(cts_dev, 4, 4, + firmware_info->firmware_sect_size - 4); + if (ret) { + cts_err("Program firmware section from sram failed %d", ret); + return ret; + } + + ret = cts_program_flash(cts_dev, CTS_FIRMWARE_CRC_SECTION_OFFSET, + firmware_info->firmware_crc_sect, + firmware_info->firmware_crc_sect_size); + if (ret) { + cts_err("Program firmware crc section failed %d", ret); + return ret; + } + + ret = validate_flash_data(cts_dev, CTS_FIRMWARE_CRC_SECTION_OFFSET, + firmware_info->firmware_crc_sect, + firmware_info->firmware_crc_sect_size, + crc_sect_buf, false, 0); + if (ret) { + cts_err("Validate Firmware-CRC section failed %d", ret); + return ret; + } + + ret = cts_program_flash(cts_dev, 0, firmware_info->firmware_sect, 4); + if (ret) { + cts_err("Program firmware section fist 4bytes failed %d", ret); + return ret; + } + + ret = validate_flash_data(cts_dev, CTS_FIRMWARE_SECTION_OFFSET, + firmware_info->firmware_sect, + firmware_info->firmware_sect_size, NULL, true, + firmware_info->firmware_sect_crc); + if (ret) { + cts_err("Validate firmware section failed %d", ret); + return ret; + } + + return 0; +} + +static int cts_program_ddiparam(const struct cts_device *cts_dev, + const struct cts_firmware_sect_info *firmware_info) +{ + int ret; + u8 crc_sect_buf[DDIPARAM_CRC_SECTION_SIZE]; + + cts_info("Program DDIParam size: %zu", firmware_info->ddiparam_sect_size); + + ret = cts_program_flash(cts_dev, CTS_DDIPARAM_SECTION_OFFSET, + firmware_info->ddiparam_sect, firmware_info->ddiparam_sect_size); + if (ret) { + cts_err("Program DDIParam section failed %d", ret); + return ret; + } + + ret = validate_flash_data(cts_dev, CTS_DDIPARAM_SECTION_OFFSET, + firmware_info->ddiparam_sect, + firmware_info->ddiparam_sect_size, NULL, true, + firmware_info->ddiparam_sect_crc); + if (ret) { + cts_err("Validate DDIParam section failed %d", ret); + return ret; + } + + ret = cts_program_flash(cts_dev, CTS_DDIPARAM_CRC_SECTION_OFFSET, + firmware_info->ddiparam_crc_sect, + firmware_info->ddiparam_crc_sect_size); + if (ret) { + cts_err("Program DDIParam-CRC section failed %d", ret); + return ret; + } + + ret = validate_flash_data(cts_dev, CTS_DDIPARAM_CRC_SECTION_OFFSET, + firmware_info->ddiparam_crc_sect, + firmware_info->ddiparam_crc_sect_size, + crc_sect_buf, false, 0); + if (ret) { + cts_err("Validate DDIParam-CRC section failed %d", ret); + return ret; + } + + return 0; +} + +int cts_update_firmware(struct cts_device *cts_dev, + const struct cts_firmware *firmware, bool to_flash) +{ + struct cts_firmware_sect_info firmware_info; + int ret, retries; + bool enabled = cts_is_device_enabled(cts_dev); + +#ifdef CONFIG_CTS_I2C_HOST + to_flash = true; +#else + to_flash = false; +#endif + + cts_info("Update firmware to %s ver: %04x size: %zu", + to_flash ? "flash" : "sram", + FIRMWARE_VERSION(firmware), firmware->size); + + if (parse_firmware(firmware, &firmware_info)) { + cts_err("Parse firmware failed"); + return -EINVAL; + } + + if (enabled) { + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d(%s)", + ret, cts_strerror(ret)); + return ret; + } + } + + cts_dev->rtdata.updating = true; + + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Device enter program mode failed %d", ret); + goto out; + } + + if (to_flash) { + ret = cts_prepare_flash_operation(cts_dev); + if (ret) { + cts_warn("Prepare flash operation failed %d", ret); + /* Go through and try */ + } + } + + cts_info("Write firmware section to sram size %zu", + firmware_info.firmware_sect_size); + ret = cts_sram_writesb_check_crc_retry(cts_dev, 0, + firmware_info.firmware_sect, + firmware_info.firmware_sect_size, + firmware_info.firmware_sect_crc, 3); + if (ret) { + cts_err("Write firmware section to sram failed %d", ret); + } + +#ifdef CFG_CTS_UPDATE_CRCCHECK + cts_sram_writesb_boot_crc_retry(cts_dev, + firmware_info.firmware_sect_size, + firmware_info.firmware_sect_crc, 3); +#endif + + if (!to_flash) { + goto out; + } + + if (to_flash && !cts_dev->rtdata.has_flash) { + cts_err("Update firmware to flash is UNKNOWN/NON-EXIST"); + ret = -ENODEV; + goto post_flash_operation; + } + + retries = 0; + do { + retries++; + + ret = cts_erase_flash(cts_dev, CTS_FIRMWARE_CRC_SECTION_OFFSET, + firmware_info.firmware_crc_sect_size); + if (ret) { + cts_err("Erase firmware crc section failed %d retries %d", + ret, retries); + continue; + } + + ret = cts_erase_flash(cts_dev, 0, firmware_info.firmware_sect_size); + if (ret) { + cts_err("Erase firmware section failed %d retries %d", + ret, retries); + continue; + } + + ret = cts_program_firmware_from_sram_to_flash(cts_dev, &firmware_info); + if (ret) { + cts_err("Program firmware & crc section failed %d retries %d", + ret, retries); + } + } while (ret && retries < 3); + + if (ret == 0 && firmware_info.ddiparam_sect_size != 0) { + retries = 0; + do { + retries++; + + ret = cts_erase_flash(cts_dev, CTS_DDIPARAM_CRC_SECTION_OFFSET, + firmware_info.ddiparam_crc_sect_size); + if (ret) { + cts_err("Erase DDIParam crc secction failed %d, retries %d", + ret, retries); + continue; + } + + ret = cts_erase_flash(cts_dev, CTS_DDIPARAM_SECTION_OFFSET, + firmware_info.ddiparam_sect_size); + if (ret) { + cts_err("Erase DDIParam section failed %d, retries %d", + ret, retries); + continue; + } + + ret = cts_program_ddiparam(cts_dev, &firmware_info); + if (ret) { + cts_err("Program DDIParam & crc section failed %d retries %d", + ret, retries); + } + } while (ret && retries < 3); + } + +post_flash_operation: + cts_post_flash_operation(cts_dev); + +out: + cts_dev->rtdata.updating = false; + + if (ret == 0) { + if (firmware_info.firmware_sect_size <= + cts_dev->hwdata->sfctrl->xchg_sram_base) { + ret = cts_enter_normal_mode(cts_dev); + if (ret) { + cts_err("Enter normal mode failed %d", ret); + /* return ret; */ + } + } + } + +#ifdef CONFIG_CTS_CHARGER_DETECT + if (cts_is_charger_exist(cts_dev)) { + int r = cts_set_dev_charger_attached(cts_dev, true); + if (r) + cts_err("Set dev charger attached failed %d", r); + } +#endif + +#ifdef CONFIG_CTS_EARJACK_DETECT + if (cts_is_earjack_exist(cts_dev)) { + int r = cts_set_dev_earjack_attached(cts_dev, true); + if (r) + cts_err("Set dev earjack attached failed %d", r); + } +#endif + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) + cts_enter_glove_mode(cts_dev); +#endif + +#ifdef CFG_CTS_FW_LOG_REDIRECT + if (cts_is_fw_log_redirect(cts_dev)) + cts_enable_fw_log_redirect(cts_dev); +#endif + + return ret; +} + +bool cts_is_firmware_updating(const struct cts_device *cts_dev) +{ + return cts_dev->rtdata.updating; +} diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_firmware.h b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_firmware.h new file mode 100644 index 000000000000..35fe5a6939fe --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_firmware.h @@ -0,0 +1,68 @@ +#ifndef CTS_FIRMWARE_H +#define CTS_FIRMWARE_H + +#include "cts_config.h" +#include +#include + +struct cts_firmware { + const char *name; /* MUST set to non-NULL if driver builtin firmware */ + u32 hwid; + u16 fwid; + + u8 *data; + size_t size; + const struct firmware *fw; +}; + +#define FIRMWARE_VERSION_OFFSET 0x100 +#define FIRMWARE_VERSION(firmware) \ + get_unaligned_le16((firmware)->data + FIRMWARE_VERSION_OFFSET) + +struct cts_device; + +extern const struct cts_firmware *cts_request_firmware( + const struct cts_device *cts_dev, u32 hwid, u16 fwid, u16 device_fw_ver); +extern void cts_release_firmware(const struct cts_firmware *firmware); + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE +extern int cts_get_num_driver_builtin_firmware(void); +extern const struct cts_firmware *cts_request_driver_builtin_firmware_by_name( + const char *name); +extern const struct cts_firmware *cts_request_driver_builtin_firmware_by_index( + u32 index); +#else /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ +static inline int cts_get_num_driver_builtin_firmware(void) +{ + return 0; +} + +static inline const struct cts_firmware +*cts_request_driver_builtin_firmware_by_name(const char *name) +{ + return NULL; +} + +static inline const struct cts_firmware +*cts_request_driver_builtin_firmware_by_index(u32 index) +{ + return NULL; +} +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ +extern bool cts_is_firmware_updating(const struct cts_device *cts_dev); + +extern int cts_update_firmware(struct cts_device *cts_dev, + const struct cts_firmware *firmware, bool to_flash); + +#ifdef CFG_CTS_FIRMWARE_IN_FS +extern const struct cts_firmware *cts_request_firmware_from_fs( + const struct cts_device *cts_dev, const char *filepath); +extern int cts_update_firmware_from_file(struct cts_device *cts_dev, + const char *filepath, bool to_flash); + +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + +extern u16 cts_crc16(const u8 *data, size_t len); +extern u32 cts_crc32(const u8 *data, size_t len); + +#endif /* CTS_FIRMWARE_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_oem.c b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_oem.c new file mode 100644 index 000000000000..7ad8e33e05ed --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_oem.c @@ -0,0 +1,1705 @@ +#define LOG_TAG "Oem" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_oem.h" +#include "cts_test.h" +#include "cts_tcs.h" +#include "cts_firmware.h" + +#include +#include +#include + +/* Following options override device tree settings */ +#define OEM_OF_DEF_PROPVAL_TEST_RESET_PIN true +#define OEM_OF_DEF_PROPVAL_TEST_INT_PIN true +#define OEM_OF_DEF_PROPVAL_TEST_RAWDATA true +#define OEM_OF_DEF_PROPVAL_TEST_NOISE true +#define OEM_OF_DEF_PROPVAL_TEST_OPEN true +#define OEM_OF_DEF_PROPVAL_TEST_SHORT true +#define OEM_OF_DEF_PROPVAL_TEST_COMP_CAP true + +/* Default settings if device tree NOT exist */ +#define OEM_OF_DEF_PROPVAL_RAWDATA_FRAMES 1 +#define OEM_OF_DEF_PROPVAL_RAWDATA_MIN 1200 +#define OEM_OF_DEF_PROPVAL_RAWDATA_MAX 2800 +#define OEM_OF_DEF_PROPVAL_NOISE_FRAMES 50 +#define OEM_OF_DEF_PROPVAL_NOISE_MAX 80 +#define OEM_OF_DEF_PROPVAL_OPEN_MIN 1980 +#define OEM_OF_DEF_PROPVAL_SHORT_MIN 500 +#define OEM_OF_DEF_PROPVAL_COMP_CAP_MIN 1 +#define OEM_OF_DEF_PROPVAL_COMP_CAP_MAX 126 + +#define OEM_OF_PROPNAME_PREFIX "chipone," //modify + +#define OEM_OF_PROPNAME_TEST_RESET_PIN OEM_OF_PROPNAME_PREFIX"test-reset-pin" +#define OEM_OF_PROPNAME_TEST_INT_PIN OEM_OF_PROPNAME_PREFIX"test-int-pin" +#define OEM_OF_PROPNAME_TEST_RAWDATA OEM_OF_PROPNAME_PREFIX"test-rawdata" +#define OEM_OF_PROPNAME_RAWDATA_FRAMES OEM_OF_PROPNAME_PREFIX"test-rawdata-frames" +#define OEM_OF_PROPNAME_RAWDATA_MIN OEM_OF_PROPNAME_PREFIX"rawdata-min" +#define OEM_OF_PROPNAME_RAWDATA_MAX OEM_OF_PROPNAME_PREFIX"rawdata-max" +#define OEM_OF_PROPNAME_TEST_NOISE OEM_OF_PROPNAME_PREFIX"test-noise" +#define OEM_OF_PROPNAME_NOISE_FRAMES OEM_OF_PROPNAME_PREFIX"test-noise-frames" +#define OEM_OF_PROPNAME_NOISE_MAX OEM_OF_PROPNAME_PREFIX"noise-max" +#define OEM_OF_PROPNAME_TEST_OPEN OEM_OF_PROPNAME_PREFIX"test-open" +#define OEM_OF_PROPNAME_OPEN_MIN OEM_OF_PROPNAME_PREFIX"open-min" +#define OEM_OF_PROPNAME_TEST_SHORT OEM_OF_PROPNAME_PREFIX"test-short" +#define OEM_OF_PROPNAME_SHORT_MIN OEM_OF_PROPNAME_PREFIX"short-min" +#define OEM_OF_PROPNAME_TEST_COMP_CAP OEM_OF_PROPNAME_PREFIX"test-compensate-cap" +#define OEM_OF_PROPNAME_COMP_CAP_MIN OEM_OF_PROPNAME_PREFIX"compensate-cap-min" +#define OEM_OF_PROPNAME_COMP_CAP_MAX OEM_OF_PROPNAME_PREFIX"compensate-cap-max" + +#define OEM_SELFTEST_PROC_FILENAME "cts_selftest" +#define OEM_LIMIT_PROC_FILENAME "cts_limit" +#define OEM_FACTORY_TEST_PROC_FILENAME "cts_factory_test" +#define OEM_RAWDATA_PROC_FILENAME "cts_rawdata" +#define OEM_MANUAL_PROC_FILENAME "cts_manual" +#define OEM_DIFFDATA_PROC_FILENAME "cts_diffdata" + +#define OEM_TEST_DATA_DIR "/sdcard" +#define OEM_RAWDATA_TEST_DATA_FILEPATH OEM_TEST_DATA_DIR"/FWMutualTest.csv" +#define OEM_NOISE_TEST_DATA_FILEPATH OEM_TEST_DATA_DIR"/NoiseTest.csv" +#define OEM_OPEN_TEST_DATA_FILEPATH OEM_TEST_DATA_DIR"/OpenTest.csv" +#define OEM_SHORT_TEST_DATA_FILEPATH OEM_TEST_DATA_DIR"/ShortTest.csv" +#define OEM_COMP_CAP_TEST_DATA_FILEPATH OEM_TEST_DATA_DIR"/FWCCTest.csv" + + +#pragma pack(1) +struct cts_limit { + u8 header[7]; + u8 version; + struct { + unsigned int_item : 1; + unsigned reset_item : 1; + unsigned normal_rawdata_item : 1; + unsigned normal_noise_item : 1; + unsigned open_item : 1; + unsigned short_item : 1; + unsigned comp_cap_item : 1; + unsigned gstr_rawdata_item : 1; + unsigned gstrlp_rawdata_item : 1; + unsigned gstr_noise_item : 1; + unsigned gstrlp_noise_item : 1; + unsigned type : 5; + }; + u16 normal_rawdata_frames; + u16 normal_rawdata_min; + u16 normal_rawdata_max; + u16 normal_noise_frames; + u16 normal_noise_max; + u16 normal_open_min; + u16 normal_short_min; + u16 normal_compcap_min; + u16 normal_compcap_max; + u16 gstr_rawdata_frames; + u16 gstr_rawdata_min; + u16 gstr_rawdata_max; + u16 gstrlp_rawdata_frames; + u16 gstrlp_rawdata_min; + u16 gstrlp_rawdata_max; + u16 gstr_noise_frames; + u16 gstr_noise_max; + u16 gstrlp_noise_frames; + u16 gstrlp_noise_max; +}; +#pragma pack() + +struct cts_oem_data { + struct proc_dir_entry *selftest_proc_entry; + struct proc_dir_entry *limit_entry; + struct proc_dir_entry *factory_test_entry; + struct proc_dir_entry *manual_proc_entry; + struct proc_dir_entry *diffdata_proc_entry; + struct proc_dir_entry *rawdata_proc_entry; + + bool test_config_from_dt_has_parsed; + + /* Test configuration from device tree */ + bool test_reset_pin; + int reset_pin_test_result; + + bool test_int_pin; + int int_pin_test_result; + + bool test_rawdata; + u32 rawdata_test_frames; + int rawdata_test_result; + u16 *rawdata_test_data; + int rawdata_test_data_buff_size; + int rawdata_test_data_wr_size; + int rawdata_min; + int rawdata_max; + + bool test_noise; + u32 noise_test_frames; + int noise_test_result; + u16 *noise_test_data; + int noise_test_data_buff_size; + int noise_test_data_wr_size; + int noise_max; + + bool test_open; + int open_test_result; + u16 *open_test_data; + int open_test_data_buff_size; + int open_test_data_wr_size; + int open_min; + + bool test_short; + int short_test_result; + u16 *short_test_data; + int short_test_data_buff_size; + int short_test_data_wr_size; + int short_min; + + bool test_comp_cap; + int comp_cap_test_result; + u8 *comp_cap_test_data; + int comp_cap_test_data_buff_size; + int comp_cap_test_data_wr_size; + int comp_cap_min; + int comp_cap_max; + + struct chipone_ts_data *cts_data; + +}; + +struct proc_dir_entry *cts_tp_work_proc; + +#define ALLOC_TEST_DATA_MEM(type, size) \ + do { \ + if (oem_data->test_##type) { \ + if (oem_data->type##_test_data == NULL) { \ + cts_info(" - Alloc " #type " test data mem size %d", size); \ + oem_data->type##_test_data = kmalloc(size, GFP_KERNEL); \ + if (oem_data->type##_test_data == NULL) { \ + cts_err("Alloc " #type " test data mem failed"); \ + return -ENOMEM; \ + } \ + oem_data->type##_test_data_buff_size = size; \ + } \ + memset(oem_data->type##_test_data, 0, size); \ + } \ + } while (0) + +/* NOTE: Any test data mem alloc failed will NOT clean other mem */ +static int alloc_sleftest_data_mem(struct cts_oem_data *oem_data, int nodes) +{ + cts_info("Alloc selftest data"); + + ALLOC_TEST_DATA_MEM(rawdata, + nodes * 2 * oem_data->rawdata_test_frames); + ALLOC_TEST_DATA_MEM(noise, + nodes * 2 * (oem_data->noise_test_frames + 3)); + ALLOC_TEST_DATA_MEM(open, nodes * 2); + ALLOC_TEST_DATA_MEM(short, nodes * 2 * 10); + ALLOC_TEST_DATA_MEM(comp_cap, nodes); + + return 0; +} +#undef ALLOC_TEST_DATA_MEM + +#define FREE_TEST_DATA_MEM(type) \ + do { \ + if (oem_data->type##_test_data) { \ + cts_info("- Free " #type " test data mem"); \ + kfree(oem_data->type##_test_data); \ + oem_data->type##_test_data = NULL; \ + oem_data->type##_test_data_buff_size = 0; \ + } \ + } while(0) + +static void free_selftest_data_mem(struct cts_oem_data *oem_data) +{ + cts_info("Free selftest data"); + + FREE_TEST_DATA_MEM(rawdata); + FREE_TEST_DATA_MEM(noise); + FREE_TEST_DATA_MEM(open); + FREE_TEST_DATA_MEM(short); + FREE_TEST_DATA_MEM(comp_cap); +} +#undef FREE_TEST_DATA_MEM + +static int parse_selftest_dt(struct cts_oem_data *oem_data, + struct device_node *np) +{ + int ret; + + cts_info("Parse selftest dt"); + + /** reset pin **/ + oem_data->test_reset_pin = OEM_OF_DEF_PROPVAL_TEST_RESET_PIN || + of_property_read_bool(np, OEM_OF_PROPNAME_TEST_RESET_PIN); + + /** int pin **/ + oem_data->test_int_pin = OEM_OF_DEF_PROPVAL_TEST_INT_PIN || + of_property_read_bool(np, OEM_OF_PROPNAME_TEST_INT_PIN); + + /** rawdata **/ + oem_data->test_rawdata = OEM_OF_DEF_PROPVAL_TEST_RAWDATA || + of_property_read_bool(np, OEM_OF_PROPNAME_TEST_RAWDATA); + if (oem_data->test_rawdata) { + oem_data->rawdata_test_frames = OEM_OF_DEF_PROPVAL_RAWDATA_FRAMES; + ret = of_property_read_u32(np, OEM_OF_PROPNAME_RAWDATA_FRAMES, + &oem_data->rawdata_test_frames); + if (ret) { + cts_warn("Parse '"OEM_OF_PROPNAME_RAWDATA_FRAMES"' failed %d", ret); + } + oem_data->rawdata_min = OEM_OF_DEF_PROPVAL_RAWDATA_MIN; + ret = of_property_read_u32(np, OEM_OF_PROPNAME_RAWDATA_MIN, + (u32 *)&oem_data->rawdata_min); + if (ret) { + cts_warn("Parse '"OEM_OF_PROPNAME_RAWDATA_MIN"' failed %d", ret); + } + oem_data->rawdata_max = OEM_OF_DEF_PROPVAL_RAWDATA_MAX; + ret = of_property_read_u32(np, OEM_OF_PROPNAME_RAWDATA_MAX, + (u32 *)&oem_data->rawdata_max); + if (ret) { + cts_warn("Parse '"OEM_OF_PROPNAME_RAWDATA_MAX"' failed %d", ret); + } + } + + /** nosie **/ + oem_data->test_noise = OEM_OF_DEF_PROPVAL_TEST_NOISE || + of_property_read_bool(np, OEM_OF_PROPNAME_TEST_NOISE); + if (oem_data->test_noise) { + oem_data->noise_test_frames = OEM_OF_DEF_PROPVAL_NOISE_FRAMES; + ret = of_property_read_u32(np, OEM_OF_PROPNAME_NOISE_FRAMES, + &oem_data->noise_test_frames); + if (ret) { + cts_warn("Parse '"OEM_OF_PROPNAME_NOISE_FRAMES"' failed %d", ret); + } + oem_data->noise_max = OEM_OF_DEF_PROPVAL_NOISE_MAX; + ret = of_property_read_u32(np, OEM_OF_PROPNAME_NOISE_MAX, + (u32 *)&oem_data->noise_max); + if (ret) { + cts_warn("Parse '"OEM_OF_PROPNAME_NOISE_MAX"' failed %d", ret); + } + } + + /** open **/ + oem_data->test_open = OEM_OF_DEF_PROPVAL_TEST_OPEN || + of_property_read_bool(np, OEM_OF_PROPNAME_TEST_OPEN); + if (oem_data->test_open) { + oem_data->open_min = OEM_OF_DEF_PROPVAL_OPEN_MIN; + ret = of_property_read_u32(np, OEM_OF_PROPNAME_OPEN_MIN, + (u32 *)&oem_data->open_min); + if (ret) { + cts_warn("Parse '"OEM_OF_PROPNAME_OPEN_MIN"' failed %d", ret); + } + } + + /** short **/ + oem_data->test_short = OEM_OF_DEF_PROPVAL_TEST_SHORT || + of_property_read_bool(np, OEM_OF_PROPNAME_TEST_SHORT); + if (oem_data->test_short) { + oem_data->short_min = OEM_OF_DEF_PROPVAL_SHORT_MIN; + ret = of_property_read_u32(np, OEM_OF_PROPNAME_SHORT_MIN, + (u32 *)&oem_data->short_min); + if (ret) { + cts_warn("Parse '"OEM_OF_PROPNAME_SHORT_MIN"' failed %d", ret); + } + } + + /** comp cap **/ + oem_data->test_comp_cap = OEM_OF_DEF_PROPVAL_TEST_COMP_CAP || + of_property_read_bool(np, OEM_OF_PROPNAME_TEST_COMP_CAP); + if (oem_data->test_comp_cap) { + oem_data->comp_cap_min = OEM_OF_DEF_PROPVAL_COMP_CAP_MIN; + ret = of_property_read_u32(np, OEM_OF_PROPNAME_COMP_CAP_MIN, + (u32 *)&oem_data->comp_cap_min); + if (ret) { + cts_warn("Parse '"OEM_OF_PROPNAME_COMP_CAP_MIN"' failed %d", ret); + } + oem_data->comp_cap_max = OEM_OF_DEF_PROPVAL_COMP_CAP_MAX; + ret = of_property_read_u32(np, OEM_OF_PROPNAME_COMP_CAP_MAX, + (u32 *)&oem_data->comp_cap_max); + if (ret) { + cts_warn("Parse '"OEM_OF_PROPNAME_COMP_CAP_MAX"' failed %d", ret); + } + } + + oem_data->test_config_from_dt_has_parsed = true; + + return 0; +} + +static void print_selftest_config(const struct cts_oem_data *oem_data) +{ + cts_info("Seltest configuration:"); + + /** reset pin **/ + cts_info(" - %-32s = %c", OEM_OF_PROPNAME_TEST_RESET_PIN, + oem_data->test_reset_pin ? 'Y' : 'N'); + + /** int pin **/ + cts_info(" - %-32s = %c", OEM_OF_PROPNAME_TEST_INT_PIN, + oem_data->test_int_pin ? 'Y' : 'N'); + + /** rawdata **/ + cts_info(" - %-32s = %c", OEM_OF_PROPNAME_TEST_RAWDATA, + oem_data->test_rawdata ? 'Y' : 'N'); + if (oem_data->test_rawdata) { + cts_info(" - %-32s = %u", OEM_OF_PROPNAME_RAWDATA_FRAMES, + oem_data->rawdata_test_frames); + cts_info(" - %-32s = %d", OEM_OF_PROPNAME_RAWDATA_MIN, + oem_data->rawdata_min); + cts_info(" - %-32s = %d", OEM_OF_PROPNAME_RAWDATA_MAX, + oem_data->rawdata_max); + } + + /** noise **/ + cts_info(" - %-32s = %c", OEM_OF_PROPNAME_TEST_NOISE, + oem_data->test_noise ? 'Y' : 'N'); + if (oem_data->test_noise) { + cts_info(" - %-32s = %u", OEM_OF_PROPNAME_NOISE_FRAMES , + oem_data->noise_test_frames); + cts_info(" - %-32s = %d", OEM_OF_PROPNAME_NOISE_MAX, + oem_data->noise_max); + } + + /** open **/ + cts_info(" - %-32s = %c", OEM_OF_PROPNAME_TEST_OPEN, + oem_data->test_open ? 'Y' : 'N'); + if (oem_data->test_open) { + cts_info(" - %-32s = %d", OEM_OF_PROPNAME_OPEN_MIN, + oem_data->open_min); + } + + /** short **/ + cts_info(" - %-32s = %c", OEM_OF_PROPNAME_TEST_SHORT, + oem_data->test_short ? 'Y' : 'N'); + if (oem_data->test_short) { + cts_info(" - %-32s = %d", OEM_OF_PROPNAME_SHORT_MIN, + oem_data->short_min); + } + + /** comp cap **/ + cts_info(" - %-32s = %c", OEM_OF_PROPNAME_TEST_COMP_CAP, + oem_data->test_comp_cap ? 'Y' : 'N'); + if (oem_data->test_comp_cap) { + cts_info(" - %-32s = %d", OEM_OF_PROPNAME_COMP_CAP_MIN, + oem_data->comp_cap_min); + cts_info(" - %-32s = %d", OEM_OF_PROPNAME_COMP_CAP_MAX, + oem_data->comp_cap_max); + } +} + +static void do_selftest(struct cts_oem_data *oem_data) +{ + struct cts_test_param test_param; + + cts_info("Do selftest"); + + /** reset pin test **/ + if (oem_data->test_reset_pin) { + memset(&test_param, 0, sizeof(test_param)); + test_param.test_item = CTS_TEST_RESET_PIN; + oem_data->reset_pin_test_result = + cts_test_reset_pin(&oem_data->cts_data->cts_dev, &test_param); + if (oem_data->reset_pin_test_result) { + cts_err("Test reset pin failed %d", oem_data->reset_pin_test_result); + } + } + + /** int pin test **/ + if (oem_data->test_int_pin) { + memset(&test_param, 0, sizeof(test_param)); + test_param.test_item = CTS_TEST_INT_PIN; + oem_data->int_pin_test_result = + cts_test_int_pin(&oem_data->cts_data->cts_dev, &test_param); + if (oem_data->int_pin_test_result) { + cts_err("Test int pin failed %d", oem_data->int_pin_test_result); + } + } + + /** rawdata test **/ + if (oem_data->test_rawdata) { + struct cts_rawdata_test_priv_param priv_param = {0}; + memset(&test_param, 0, sizeof(test_param)); + oem_data->rawdata_test_data_wr_size = 0; + + test_param.test_item = CTS_TEST_RAWDATA; + test_param.flags = + CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE; + test_param.min = &oem_data->rawdata_min; + test_param.max = &oem_data->rawdata_max; + test_param.test_data_buf = oem_data->rawdata_test_data; + test_param.test_data_buf_size = oem_data->rawdata_test_data_buff_size; + test_param.test_data_wr_size = &oem_data->rawdata_test_data_wr_size; + + priv_param.frames = oem_data->rawdata_test_frames; + test_param.priv_param = &priv_param; + test_param.priv_param_size = sizeof(priv_param); + + oem_data->rawdata_test_result = + cts_test_rawdata(&oem_data->cts_data->cts_dev, &test_param); + if (oem_data->rawdata_test_result) { + cts_err("Test rawdata failed %d", oem_data->rawdata_test_result); + } + } + + /** noise test **/ + if (oem_data->test_noise) { + struct cts_noise_test_priv_param priv_param = {0}; + memset(&test_param, 0, sizeof(test_param)); + oem_data->noise_test_data_wr_size = 0; + + test_param.test_item = CTS_TEST_NOISE; + test_param.flags = + CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE; + test_param.max = &oem_data->noise_max; + test_param.test_data_buf = oem_data->noise_test_data; + test_param.test_data_buf_size = oem_data->noise_test_data_buff_size; + test_param.test_data_wr_size = &oem_data->noise_test_data_wr_size; + + priv_param.frames = oem_data->noise_test_frames; + test_param.priv_param = &priv_param; + test_param.priv_param_size = sizeof(priv_param); + + oem_data->noise_test_result = + cts_test_noise(&oem_data->cts_data->cts_dev, &test_param); + if (oem_data->noise_test_result) { + cts_err("Test noise failed %d", oem_data->noise_test_result); + } + } + + /** open test **/ + if (oem_data->test_open) { + memset(&test_param, 0, sizeof(test_param)); + oem_data->open_test_data_wr_size = 0; + test_param.test_item = CTS_TEST_OPEN; + test_param.flags = + CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE; + test_param.min = &oem_data->open_min; + test_param.test_data_buf = oem_data->open_test_data; + test_param.test_data_buf_size = oem_data->open_test_data_buff_size; + test_param.test_data_wr_size = &oem_data->open_test_data_wr_size; + + oem_data->open_test_result = + cts_test_open(&oem_data->cts_data->cts_dev, &test_param); + if (oem_data->open_test_result) { + cts_err("Test open failed %d", oem_data->open_test_result); + } + } + + /** short test **/ + if (oem_data->test_short) { + memset(&test_param, 0, sizeof(test_param)); + oem_data->short_test_data_wr_size = 0; + test_param.test_item = CTS_TEST_SHORT; + test_param.flags = + CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE; + test_param.min = &oem_data->short_min; + test_param.test_data_buf = oem_data->short_test_data; + test_param.test_data_buf_size = oem_data->short_test_data_buff_size; + test_param.test_data_wr_size = &oem_data->short_test_data_wr_size; + + oem_data->short_test_result = + cts_test_short(&oem_data->cts_data->cts_dev, &test_param); + if (oem_data->short_test_result) { + cts_err("Test short failed %d", oem_data->short_test_result); + } + } + + /** comp cap test **/ + if (oem_data->test_comp_cap) { + memset(&test_param, 0, sizeof(test_param)); + oem_data->comp_cap_test_data_wr_size = 0; + test_param.test_item = CTS_TEST_COMPENSATE_CAP; + test_param.flags = + CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE; + test_param.min = &oem_data->comp_cap_min; + test_param.max = &oem_data->comp_cap_max; + test_param.test_result = &oem_data->comp_cap_test_result; + test_param.test_data_buf = oem_data->comp_cap_test_data; + test_param.test_data_buf_size = oem_data->comp_cap_test_data_buff_size; + test_param.test_data_wr_size = &oem_data->comp_cap_test_data_wr_size; + + oem_data->comp_cap_test_result = + cts_test_compensate_cap(&oem_data->cts_data->cts_dev, &test_param); + if (oem_data->comp_cap_test_result) { + cts_err("Test compensate cap failed %d", oem_data->comp_cap_test_result); + } + } +} + +static int dump_tsdata_row_to_buffer(char *buf, size_t size, const u16 *data, + int cols, const char *prefix, const char *suffix, char seperator) +{ + int c, count = 0; + + if (prefix) { + count += scnprintf(buf, size, "%s", prefix); + } + + for (c = 0; c < cols; c++) { + count += scnprintf(buf + count, size - count, + "%4u%c ", data[c], seperator); + } + + if (suffix) { + count += scnprintf(buf + count, size - count, "%s", suffix); + } + + return count; +} + +static int dump_tsdata_to_csv_file(const char *filepath, + int flags, const u16 *data, int frames, int rows, int cols, int hw_cols) +{ +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): some functions are forbiddon with GKI Version!", __func__); + return -EPERM; +#else + loff_t pos = 0; + int i, r, ret; + struct file *file; + + cts_info("Dump tsdata to csv file: '%s' flags: 0x%x data: %p frames: %d row: %d col: %d hw_col: %d", + filepath, flags, data, frames, rows, cols, hw_cols); + + file = filp_open(filepath, flags, 0666); + if (IS_ERR(file)) { + cts_err("Open file '%s' failed %ld", filepath, PTR_ERR(file)); + return PTR_ERR(file); + } + + for (i = 0; i < frames; i++) { + for (r = 0; r < rows; r++) { + char linebuf[256]; + int len; + + len = dump_tsdata_row_to_buffer(linebuf, sizeof(linebuf), + data, cols, NULL, "\n", ','); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + ret = kernel_write(file, linebuf, len, &pos); +#else + ret = kernel_write(file, linebuf, len, pos); + pos += len; +#endif + if (ret != len) { + cts_err("Write to file '%s' failed %d", filepath, ret); + goto close_file; + } + data += hw_cols; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + ret = kernel_write(file, "\n", 1, &pos); +#else + ret = kernel_write(file, "\n", 1, pos); + pos ++; +#endif + if (ret != 1) { + cts_err("Write newline to file '%s' failed %d", filepath, ret); + goto close_file; + } + } + +close_file: { + int r = filp_close(file, NULL); + if (r) { + cts_err("Close file '%s' failed %d", filepath, r); + } + } + + return ret; +#endif +} + +static void dump_tsdata_to_seq_file(struct seq_file *m, + const u16 *data, int rows, int cols, int hw_cols) +{ + int r; + + for (r = 0; r < rows; r++) { + char linebuf[256]; + int len; + + len = dump_tsdata_row_to_buffer(linebuf, sizeof(linebuf), + data, cols, NULL, "\n", ','); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + (void) len; +#endif + seq_puts(m, linebuf); + + data += hw_cols; + } +} + +static int dump_comp_cap_row_to_buffer(char *buf, size_t size, const u8 *cap, + int cols, const char *prefix, const char *suffix, char seperator) +{ + int c, count = 0; + + if (prefix) { + count += scnprintf(buf, size, "%s", prefix); + } + + for (c = 0; c < cols; c++) { + count += scnprintf(buf + count, size - count, + "%3u%c ", cap[c], seperator); + } + + if (suffix) { + count += scnprintf(buf + count, size - count, "%s", suffix); + } + + return count; +} + +static int dump_comp_cap_to_csv_file(const char *filepath, + int flags, const u8 *cap, int rows, int cols, int hw_cols) +{ +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): some functions are forbiddon with GKI Version!", __func__); + return -EPERM; +#else + struct file *file; + int r, ret = 0; + loff_t pos = 0; + + cts_info("Dump compensate cap to csv file: '%s' flags: 0x%x row: %d col: %d", + filepath, flags, rows, cols); + + file = filp_open(filepath, flags, 0666); + if (IS_ERR(file)) { + cts_err("Open file '%s' failed %ld", filepath, PTR_ERR(file)); + return PTR_ERR(file); + } + + for (r = 0; r < rows; r++) { + char linebuf[256]; + int len; + + len = dump_comp_cap_row_to_buffer(linebuf, sizeof(linebuf), + cap, cols, NULL, "\n", ','); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + ret = kernel_write(file, linebuf, len, &pos); +#else + ret = kernel_write(file, linebuf, len, pos); + pos += len; +#endif + if (ret != len) { + cts_err("Write to file '%s' failed %d", filepath, ret); + goto close_file; + } + + cap += hw_cols; + } + +close_file: { + int r = filp_close(file, NULL); + if (r) { + cts_err("Close file '%s' failed %d", filepath, ret); + } + } + + return ret; +#endif +} + +static void dump_comp_cap_to_seq_file(struct seq_file *m, + const u8 *data, int rows, int cols, int hw_cols) +{ + int r; + + for (r = 0; r < rows; r++) { + char linebuf[256]; + int len; + + len = dump_comp_cap_row_to_buffer(linebuf, sizeof(linebuf), + data, cols, NULL, "\n", ','); + seq_puts(m, linebuf); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + (void) len; +#endif + data += hw_cols; + } +} + +static int save_selftest_data_to_file(struct cts_oem_data *oem_data) +{ + int rows, cols, hw_cols; + int ret; + + cts_info("Save selftest data to file"); + + rows = oem_data->cts_data->cts_dev.fwdata.rows; + cols = oem_data->cts_data->cts_dev.fwdata.cols; + hw_cols = oem_data->cts_data->cts_dev.hwdata->num_col; + + if (oem_data->test_rawdata) { + ret = dump_tsdata_to_csv_file(OEM_RAWDATA_TEST_DATA_FILEPATH, + O_RDWR | O_CREAT | O_TRUNC, oem_data->rawdata_test_data, + oem_data->rawdata_test_frames, rows, cols, hw_cols); + if (ret < 0) { + cts_err("Dump rawdata test data to file failed"); + return ret; + } + } + + if (oem_data->test_noise) { + ret = dump_tsdata_to_csv_file(OEM_NOISE_TEST_DATA_FILEPATH, + O_RDWR | O_CREAT | O_TRUNC, oem_data->noise_test_data, + oem_data->noise_test_frames, rows, cols, hw_cols); + if (ret < 0) { + cts_err("Dump noise test data to file failed"); + return ret; + } + } + + if (oem_data->test_open) { + ret = dump_tsdata_to_csv_file(OEM_OPEN_TEST_DATA_FILEPATH, + O_RDWR | O_CREAT | O_TRUNC, oem_data->open_test_data, + 1, rows, cols, hw_cols); + if (ret < 0) { + cts_err("Dump open test data to file failed"); + return ret; + } + } + + if (oem_data->test_short) { + ret = dump_tsdata_to_csv_file(OEM_SHORT_TEST_DATA_FILEPATH, + O_RDWR | O_CREAT | O_TRUNC, oem_data->short_test_data, + 10, rows, cols, hw_cols); + if (ret < 0) { + cts_err("Dump short test data to file failed"); + return ret; + } + } + + if (oem_data->test_comp_cap) { + ret = dump_comp_cap_to_csv_file(OEM_COMP_CAP_TEST_DATA_FILEPATH, + O_RDWR | O_CREAT | O_TRUNC, + oem_data->comp_cap_test_data, rows, cols, hw_cols); + if (ret < 0) { + cts_err("Dump compensate cap test data to file failed"); + return ret; + } + } + + return 0; +} + +static void *selftest_seq_start(struct seq_file *m, loff_t *pos) +{ + return *pos < 1 ? (void *)1 : NULL; +} + +static void *selftest_seq_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return NULL; +} + +static void selftest_seq_stop(struct seq_file *m, void *v) +{ + return; +} + +static int selftest_seq_show(struct seq_file *m, void *v) +{ + struct chipone_ts_data *cts_data = (struct chipone_ts_data *)m->private; + struct cts_oem_data *oem_data = NULL; + int i, rows, cols, hw_rows, hw_cols; + + cts_info("Show seq selftest"); + + if (cts_data == NULL) { + cts_err("Selftest seq file private data = NULL"); + return -EFAULT; + } + + oem_data = cts_data->oem_data; + rows = cts_data->cts_dev.fwdata.rows; + cols = cts_data->cts_dev.fwdata.cols; + hw_rows = cts_data->cts_dev.hwdata->num_row; + hw_cols = cts_data->cts_dev.hwdata->num_col; + + seq_printf(m, "FW Version %04x!\n\n", cts_data->cts_dev.fwdata.version); + + if (oem_data->test_reset_pin) { + seq_printf(m, "Reset-Pin Test %s!\n\n", + oem_data->reset_pin_test_result == 0 ? "PASS" : "FAIL"); + } + if (oem_data->test_int_pin) { + seq_printf(m, "Int-Pin Test %s!\n\n", + oem_data->int_pin_test_result == 0 ? "PASS" : "FAIL"); + } + if (oem_data->test_rawdata) { + seq_printf(m, "FW Rawdata Test"); + if (oem_data->rawdata_test_result == 0) { + seq_printf(m, " PASS!\n\n"); + } else if (oem_data->rawdata_test_result > 0) { + seq_printf(m, " FAIL!\n"); + for(i = 0; i < oem_data->rawdata_test_frames; i++) { + dump_tsdata_to_seq_file(m, oem_data->rawdata_test_data + + i * hw_rows * hw_cols, rows, cols, hw_cols); + seq_putc(m, '\n'); + } + } else { + seq_printf(m, " ERROR(%d)!\n\n", oem_data->rawdata_test_result); + } + } + if (oem_data->test_noise) { + seq_printf(m, "Noise Test"); + if (oem_data->noise_test_result == 0) { + seq_printf(m, " PASS!\n\n"); + } else if (oem_data->noise_test_result > 0) { + seq_printf(m, " FAIL!\n"); + for(i = 0; i < oem_data->noise_test_frames; i++) { + dump_tsdata_to_seq_file(m, oem_data->noise_test_data + + i * hw_rows * hw_cols, rows, cols, hw_cols); + seq_putc(m, '\n'); + } + } else { + seq_printf(m, " ERROR(%d)!\n\n", oem_data->noise_test_result); + } + } + if (oem_data->test_open) { + seq_printf(m, "Open Test"); + if (oem_data->open_test_result == 0) { + seq_printf(m, " PASS!\n\n"); + } else if (oem_data->open_test_result > 0) { + seq_printf(m, " FAIL!\n"); + dump_tsdata_to_seq_file(m, oem_data->open_test_data, + rows, cols, hw_cols); + } else { + seq_printf(m, " ERROR(%d)!\n\n", oem_data->open_test_result); + } + } + if (oem_data->test_short) { + seq_printf(m, "Short Test"); + if (oem_data->short_test_result == 0) { + seq_printf(m, " PASS!\n\n"); + } else if (oem_data->short_test_result > 0) { + seq_printf(m, " FAIL!\n"); + for (i = 0; i < 10; i++) { + dump_tsdata_to_seq_file(m, oem_data->short_test_data + + i * hw_rows * hw_cols, rows, cols, hw_cols); + seq_putc(m, '\n'); + } + } else { + seq_printf(m, " ERROR(%d)!\n\n", oem_data->short_test_result); + } + } + if (oem_data->test_comp_cap) { + seq_printf(m, "Compensate-Cap Test"); + if (oem_data->comp_cap_test_result == 0) { + seq_printf(m, " PASS!\n\n"); + } else if (oem_data->comp_cap_test_result > 0) { + seq_printf(m, " FAIL!\n"); + dump_comp_cap_to_seq_file(m, oem_data->comp_cap_test_data, + rows, cols, hw_cols); + } else { + seq_printf(m, " ERROR(%d)!\n\n", oem_data->comp_cap_test_result); + } + } + + return 0; +} + +const struct seq_operations selftest_seq_ops = { + .start = selftest_seq_start, + .next = selftest_seq_next, + .stop = selftest_seq_stop, + .show = selftest_seq_show, +}; + +static int32_t selftest_proc_open(struct inode *inode, struct file *file) +{ + struct chipone_ts_data *cts_data = PDE_DATA(inode); + struct cts_oem_data *oem_data = NULL; + int ret; + + if (cts_data == NULL) { + cts_err("Open selftest proc with cts_data = NULL"); + return -EFAULT; + } + + oem_data = cts_data->oem_data; + if (oem_data == NULL) { + cts_err("Open selftest proc with oem_data = NULL"); + return -EFAULT; + } + + cts_info("Open '/proc/" OEM_SELFTEST_PROC_FILENAME "'"); + + if (!oem_data->test_config_from_dt_has_parsed) { +#ifndef CONFIG_CTS_I2C_HOST + ret = parse_selftest_dt(oem_data, cts_data->pdata->spi_client->dev.of_node); +#else + ret = parse_selftest_dt(oem_data, cts_data->device->of_node); +#endif + if (ret) { + cts_err("Parse selftest dt failed %d", ret); + return ret; + } + } + + print_selftest_config(oem_data); + + ret = alloc_sleftest_data_mem(oem_data, + cts_data->cts_dev.hwdata->num_row * cts_data->cts_dev.hwdata->num_col); + if (ret) { + cts_err("Alloc test data mem failed"); + return ret; + } + + do_selftest(oem_data); + + ret = save_selftest_data_to_file(oem_data); + if (ret) { + cts_err("Save selftest data to file failed %d", ret); + } + + ret = seq_open(file, &selftest_seq_ops); + if (ret) { + cts_err("Open selftest seq file failed %d", ret); + return ret; + } + + ((struct seq_file *)file->private_data)->private = cts_data; + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) +static const struct file_operations selftest_proc_fops = { + .owner = THIS_MODULE, + .open = selftest_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#else +static const struct proc_ops selftest_proc_fops = { + .proc_open = selftest_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; +#endif + +static int cts_rawdata_show(struct seq_file *m, void *v) +{ + struct chipone_ts_data *cts_data = m->private; + struct cts_device *cts_dev; + u8 *rawdata; + s16 *data; + u8 rows, cols; + u8 i, j; + + cts_dev = &cts_data->cts_dev; + rows = cts_dev->hwdata->num_row; + cols = cts_dev->hwdata->num_col; + rawdata = kzalloc(rows * cols * 2, GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate rawdata failed"); + return -ENOMEM; + } + + cts_lock_device(cts_dev); + cts_tcs_top_get_rawdata(cts_dev, rawdata, rows * cols * 2); + cts_unlock_device(cts_dev); + + data = (s16 *)rawdata; + for (i = 0; i < rows; i++) { + for (j = 0; j < cols; j++) { + seq_printf(m, "%5d", data[i * cols + j]); + } + seq_printf(m, "\n"); + } + + kfree(rawdata); + return 0; +} +static int cts_rawdata_open(struct inode *inode, struct file *file) +{ + return single_open(file, cts_rawdata_show, PDE_DATA(inode)); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) +static const struct file_operations cts_rawdata_ops = { + .owner = THIS_MODULE, + .open = cts_rawdata_open, + .read = seq_read, +}; +#else +static const struct proc_ops cts_rawdata_ops = { + .proc_open = cts_rawdata_open, + .proc_read = seq_read, +}; +#endif + +static int cts_diffdata_show(struct seq_file *m, void *v) +{ + struct chipone_ts_data *cts_data = m->private; + struct cts_device *cts_dev; + u8 *rawdata; + s16 *data; + u8 rows, cols; + u8 i, j; + + cts_dev = &cts_data->cts_dev; + rows = cts_dev->hwdata->num_row; + cols = cts_dev->hwdata->num_col; + rawdata = kzalloc(rows * cols * 2, GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate rawdata failed"); + return -ENOMEM; + } + + cts_lock_device(cts_dev); + cts_tcs_top_get_real_diff(cts_dev, rawdata, rows * cols * 2); + cts_unlock_device(cts_dev); + + data = (s16 *)rawdata; + for (i = 0; i < rows; i++) { + for (j = 0; j < cols; j++) { + seq_printf(m, "%5d", data[i * cols + j]); + } + seq_printf(m, "\n"); + } + + kfree(rawdata); + return 0; +} +static int cts_diffdata_open(struct inode *inode, struct file *file) +{ + return single_open(file, cts_diffdata_show, PDE_DATA(inode)); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) +static const struct file_operations cts_diffdata_ops = { + .owner = THIS_MODULE, + .open = cts_diffdata_open, + .read = seq_read, +}; +#else +static const struct proc_ops cts_diffdata_ops = { + .proc_open = cts_diffdata_open, + .proc_read = seq_read, +}; +#endif + +static int cts_manual_show(struct seq_file *m, void *v) +{ + struct chipone_ts_data *cts_data = m->private; + struct cts_device *cts_dev; + u8 *rawdata; + s16 *data; + u8 rows, cols; + u8 i, j; + + cts_dev = &cts_data->cts_dev; + rows = cts_dev->hwdata->num_row; + cols = cts_dev->hwdata->num_col; + rawdata = kzalloc(rows * cols * 2, GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate rawdata failed"); + return -ENOMEM; + } + + cts_lock_device(cts_dev); + cts_tcs_top_get_manual_diff(cts_dev, rawdata, rows * cols * 2); + cts_unlock_device(cts_dev); + + data = (s16 *)rawdata; + for (i = 0; i < rows; i++) { + for (j = 0; j < cols; j++) { + seq_printf(m, "%5d", data[i * cols + j]); + } + seq_printf(m, "\n"); + } + + kfree(rawdata); + return 0; +} +static int cts_manual_open(struct inode *inode, struct file *file) +{ + return single_open(file, cts_manual_show, PDE_DATA(inode)); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) +static const struct file_operations cts_manual_ops = { + .owner = THIS_MODULE, + .open = cts_manual_open, + .read = seq_read, +}; +#else +static const struct proc_ops cts_manual_ops = { + .proc_open = cts_manual_open, + .proc_read = seq_read, +}; +#endif + +/* Use limit bin */ +static int cts_limit_show(struct seq_file *m, void *v) +{ + struct chipone_ts_data *cts_data = m->private; + struct cts_device *cts_dev = &cts_data->cts_dev; + const struct firmware *limit_fw = NULL; + struct cts_limit limit; + char header[7] = {'C', 'h', 'i', 'p', 'o', 'n', 'e'}; + int ret; + + ret = request_firmware(&limit_fw, CFG_CTS_FACTORY_LIMIT_FILENAME, + &cts_dev->pdata->ts_input_dev->dev); + if (ret) { + seq_printf(m, "Request %s failed!\n", CFG_CTS_FACTORY_LIMIT_FILENAME); + return 0; + } + + ret = memcmp(header, limit_fw->data, sizeof(header)); + if (ret) { + seq_printf(m, "LIMIT file was not matched\n"); + release_firmware(limit_fw); + return 0; + } else { + cts_info("Matched limit file!"); + } + + memcpy(&limit, limit_fw->data, limit_fw->size); + release_firmware(limit_fw); + + seq_printf(m, "Limit version: %x, type:%d\n", limit.version, limit.type); + seq_printf(m, "\n"); + seq_printf(m, "Switch status:\n"); + seq_printf(m, " int_item : %s\n", limit.int_item ? "Y" : "N"); + seq_printf(m, " reset_item : %s\n", limit.reset_item ? "Y" : "N"); + seq_printf(m, " normal_rawdata_item : %s\n", limit.normal_rawdata_item ? "Y" : "N"); + seq_printf(m, " normal_noise_item : %s\n", limit.normal_noise_item ? "Y" : "N"); + seq_printf(m, " open_item : %s\n", limit.open_item ? "Y" : "N"); + seq_printf(m, " short_item : %s\n", limit.short_item ? "Y" : "N"); + seq_printf(m, " comp_cap_item : %s\n", limit.comp_cap_item ? "Y" : "N"); + seq_printf(m, " gstr_rawdata_item : %s\n", limit.gstr_rawdata_item ? "Y" : "N"); + seq_printf(m, " gstrlp_rawdata_item : %s\n", limit.gstrlp_rawdata_item ? "Y" : "N"); + seq_printf(m, " gstr_noise_item : %s\n", limit.gstr_noise_item ? "Y" : "N"); + seq_printf(m, " gstrlp_noise_item : %s\n", limit.gstrlp_noise_item ? "Y" : "N"); + seq_printf(m, "\n"); + seq_printf(m, "Threshold:\n"); + seq_printf(m, " normal_rawdata_frames : %d\n", limit.normal_rawdata_frames); + seq_printf(m, " normal_rawdata_min : %d\n", limit.normal_rawdata_min); + seq_printf(m, " normal_rawdata_max : %d\n", limit.normal_rawdata_max); + seq_printf(m, " normal_noise_frames : %d\n", limit.normal_noise_frames); + seq_printf(m, " normal_noise_max : %d\n", limit.normal_noise_max); + seq_printf(m, " normal_open_min : %d\n", limit.normal_open_min); + seq_printf(m, " normal_short_min : %d\n", limit.normal_short_min); + seq_printf(m, " normal_compcap_min : %d\n", limit.normal_compcap_min); + seq_printf(m, " normal_compcap_max : %d\n", limit.normal_compcap_max); + seq_printf(m, " gstr_rawdata_frames : %d\n", limit.gstr_rawdata_frames); + seq_printf(m, " gstr_rawdata_min : %d\n", limit.gstr_rawdata_min); + seq_printf(m, " gstr_rawdata_max : %d\n", limit.gstr_rawdata_max); + seq_printf(m, " gstrlp_rawdata_frames : %d\n", limit.gstrlp_rawdata_frames); + seq_printf(m, " gstrlp_rawdata_min : %d\n", limit.gstrlp_rawdata_min); + seq_printf(m, " gstrlp_rawdata_max : %d\n", limit.gstrlp_rawdata_max); + seq_printf(m, " gstr_noise_frames : %d\n", limit.gstr_noise_frames); + seq_printf(m, " gstr_noise_max : %d\n", limit.gstr_noise_max); + seq_printf(m, " gstrlp_noise_frames : %d\n", limit.gstrlp_noise_frames); + seq_printf(m, " gstrlp_noise_max : %d\n", limit.gstrlp_noise_max); + + return 0; +} +static int cts_limit_open(struct inode *inode, struct file *file) +{ + return single_open(file, cts_limit_show, PDE_DATA(inode)); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) +static const struct file_operations cts_limit_ops = { + .owner = THIS_MODULE, + .open = cts_limit_open, + .read = seq_read, +}; +#else +static const struct proc_ops cts_limit_ops = { + .proc_open = cts_limit_open, + .proc_read = seq_read, +}; +#endif + +static int cts_factory_test_show(struct seq_file *m, void *v) +{ +#define TOUCH_DATA_DIRECTORY_PREFIX "/sdcard" +#define RAWDATA_TEST_DATA_FILENAME "rawdata-test-data.txt" +#define NOISE_TEST_DATA_FILENAME "noise-test-data.txt" +#define OPEN_TEST_DATA_FILENAME "open-test-data.txt" +#define SHORT_TEST_DATA_FILENAME "short-test-data.txt" +#define COMP_CAP_TEST_DATA_FILENAME "comp-cap-test-data.txt" + struct chipone_ts_data *cts_data = m->private; + struct cts_device *cts_dev = &cts_data->cts_dev; + + s64 reset_pin_test_elapsed_time = 0; + struct cts_test_param reset_pin_test_param = { + .test_item = CTS_TEST_RESET_PIN, + .flags = 0, + .elapsed_time_ms = &reset_pin_test_elapsed_time, + }; + s64 int_pin_test_elapsed_time = 0; + struct cts_test_param int_pin_test_param = { + .test_item = CTS_TEST_INT_PIN, + .flags = 0, + .elapsed_time_ms = &int_pin_test_elapsed_time, + }; + struct cts_rawdata_test_priv_param rawdata_test_priv_param = { + .frames = 16, + //.work_mode = 0, + }; + s64 rawdata_test_elapsed_time = 0; + struct cts_test_param rawdata_test_param = { + .test_item = CTS_TEST_RAWDATA, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = NULL, + .num_invalid_node = 0, + .invalid_nodes = NULL, + .elapsed_time_ms = &rawdata_test_elapsed_time, + .priv_param = &rawdata_test_priv_param, + .priv_param_size = sizeof(rawdata_test_priv_param), + }; + struct cts_noise_test_priv_param noise_test_priv_param = { + .frames = 50, + //.work_mode = 0, + }; + s64 noise_test_elapsed_time = 0; + struct cts_test_param noise_test_param = { + .test_item = CTS_TEST_NOISE, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = NULL, + .num_invalid_node = 0, + .invalid_nodes = NULL, + .elapsed_time_ms = &noise_test_elapsed_time, + .priv_param = &noise_test_priv_param, + .priv_param_size = sizeof(noise_test_priv_param), + }; + s64 open_test_elapsed_time = 0; + struct cts_test_param open_test_param = { + .test_item = CTS_TEST_OPEN, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = NULL, + .num_invalid_node = 0, + .invalid_nodes = NULL, + .elapsed_time_ms = &open_test_elapsed_time, + }; + s64 short_test_elapsed_time = 0; + struct cts_test_param short_test_param = { + .test_item = CTS_TEST_SHORT, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = NULL, + .num_invalid_node = 0, + .invalid_nodes = NULL, + .elapsed_time_ms = &short_test_elapsed_time, + }; + s64 comp_cap_test_elapsed_time = 0; + struct cts_test_param comp_cap_test_param = { + .test_item = CTS_TEST_COMPENSATE_CAP, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = NULL, + .num_invalid_node = 0, + .invalid_nodes = NULL, + .elapsed_time_ms = &comp_cap_test_elapsed_time, + }; + const struct firmware *limit_fw = NULL; + struct cts_limit limit; + char header[7] = {'C', 'h', 'i', 'p', 'o', 'n', 'e'}; + int normal_rawdata_frames; + int normal_rawdata_min; + int normal_rawdata_max; + int normal_noise_frames; + int normal_noise_max; + int normal_open_min; + int normal_short_min; + int normal_compcap_min; + int normal_compcap_max; + int int_pin_test_result; + int reset_pin_test_result; + int rawdata_test_result; + int noise_test_result; + int open_test_result; + int short_test_result; + int comp_cap_test_result; + char touch_data_filepath[256]; + ktime_t start_time, end_time, delta_time; + u16 fw_version; + int ret; + + ret = request_firmware(&limit_fw, CFG_CTS_FACTORY_LIMIT_FILENAME, + &cts_dev->pdata->ts_input_dev->dev); + if (ret) { + cts_err("Request %s failed!", CFG_CTS_FACTORY_LIMIT_FILENAME); + return 0; + } + + ret = memcmp(header, limit_fw->data, sizeof(header)); + if (ret) { + cts_err("LIMIT file was not matched"); + release_firmware(limit_fw); + return 0; + } else { + cts_info("Matched limit file!"); + } + + memcpy(&limit, limit_fw->data, limit_fw->size); + release_firmware(limit_fw); + + cts_info("limit version : %x", limit.version); + cts_info("int_item : %s", limit.int_item ? "Y" : "N"); + cts_info("reset_item : %s", limit.reset_item ? "Y" : "N"); + cts_info("normal_rawdata_item : %s", limit.normal_rawdata_item ? "Y" : "N"); + cts_info("normal_noise_item : %s", limit.normal_noise_item ? "Y" : "N"); + cts_info("open_item : %s", limit.open_item ? "Y" : "N"); + cts_info("short_item : %s", limit.short_item ? "Y" : "N"); + cts_info("comp_cap_item : %s", limit.comp_cap_item ? "Y" : "N"); + cts_info("gstr_rawdata_item : %s", limit.gstr_rawdata_item ? "Y" : "N"); + cts_info("gstrlp_rawdata_item : %s", limit.gstrlp_rawdata_item ? "Y" : "N"); + cts_info("gstr_noise_item : %s", limit.gstr_noise_item ? "Y" : "N"); + cts_info("gstrlp_noise_item : %s", limit.gstrlp_noise_item ? "Y" : "N"); + cts_info("type : %d", limit.type); + cts_info("----------------------------"); + cts_info("normal_rawdata_frames : %d", limit.normal_rawdata_frames); + cts_info("normal_rawdata_min : %d", limit.normal_rawdata_min); + cts_info("normal_rawdata_max : %d", limit.normal_rawdata_max); + cts_info("normal_noise_frames : %d", limit.normal_noise_frames); + cts_info("normal_noise_max : %d", limit.normal_noise_max); + cts_info("normal_open_min : %d", limit.normal_open_min); + cts_info("normal_short_min : %d", limit.normal_short_min); + cts_info("normal_compcap_min : %d", limit.normal_compcap_min); + cts_info("normal_compcap_max : %d", limit.normal_compcap_max); + cts_info("gstr_rawdata_frames : %d", limit.gstr_rawdata_frames); + cts_info("gstr_rawdata_min : %d", limit.gstr_rawdata_min); + cts_info("gstr_rawdata_max : %d", limit.gstr_rawdata_max); + cts_info("gstrlp_rawdata_frames : %d", limit.gstrlp_rawdata_frames); + cts_info("gstrlp_rawdata_min : %d", limit.gstrlp_rawdata_min); + cts_info("gstrlp_rawdata_max : %d", limit.gstrlp_rawdata_max); + cts_info("gstr_noise_frames : %d", limit.gstr_noise_frames); + cts_info("gstr_noise_max : %d", limit.gstr_noise_max); + cts_info("gstrlp_noise_frames : %d", limit.gstrlp_noise_frames); + cts_info("gstrlp_noise_max : %d", limit.gstrlp_noise_max); + + normal_rawdata_frames = limit.normal_rawdata_frames; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + (void) normal_rawdata_frames; +#endif + normal_rawdata_min = limit.normal_rawdata_min; + normal_rawdata_max = limit.normal_rawdata_max; + normal_noise_frames = limit.normal_noise_frames; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + (void)normal_noise_frames; +#endif + normal_noise_max = limit.normal_noise_max; + normal_open_min = limit.normal_open_min; + normal_short_min = limit.normal_short_min; + normal_compcap_min = limit.normal_compcap_min; + normal_compcap_max = limit.normal_compcap_max; + + rawdata_test_priv_param.frames = limit.normal_rawdata_frames; + noise_test_priv_param.frames = limit.normal_noise_frames; + rawdata_test_param.min = &normal_rawdata_min; + rawdata_test_param.max = &normal_rawdata_max; + noise_test_param.max = &normal_noise_max; + open_test_param.min = &normal_open_min; + short_test_param.min = &normal_short_min; + comp_cap_test_param.min = &normal_compcap_min; + comp_cap_test_param.max = &normal_compcap_max; + + snprintf(touch_data_filepath, sizeof(touch_data_filepath), + TOUCH_DATA_DIRECTORY_PREFIX"/"RAWDATA_TEST_DATA_FILENAME); + rawdata_test_param.test_data_filepath = + kstrdup(touch_data_filepath, GFP_KERNEL); + snprintf(touch_data_filepath, sizeof(touch_data_filepath), + TOUCH_DATA_DIRECTORY_PREFIX"/"NOISE_TEST_DATA_FILENAME); + noise_test_param.test_data_filepath = + kstrdup(touch_data_filepath, GFP_KERNEL); + snprintf(touch_data_filepath, sizeof(touch_data_filepath), + TOUCH_DATA_DIRECTORY_PREFIX"/"OPEN_TEST_DATA_FILENAME); + open_test_param.test_data_filepath = + kstrdup(touch_data_filepath, GFP_KERNEL); + snprintf(touch_data_filepath, sizeof(touch_data_filepath), + TOUCH_DATA_DIRECTORY_PREFIX"/"SHORT_TEST_DATA_FILENAME); + short_test_param.test_data_filepath = + kstrdup(touch_data_filepath, GFP_KERNEL); + snprintf(touch_data_filepath, sizeof(touch_data_filepath), + TOUCH_DATA_DIRECTORY_PREFIX"/"COMP_CAP_TEST_DATA_FILENAME); + comp_cap_test_param.test_data_filepath = + kstrdup(touch_data_filepath, GFP_KERNEL); + + start_time = ktime_get(); + + ret = cts_tcs_get_fw_ver(cts_dev, &fw_version); + if (ret) { + seq_printf(m, "Factory test get firmware version failed %d\n", ret); + fw_version = 0; + } + + if (limit.int_item) { + int_pin_test_result = cts_test_int_pin(cts_dev, &int_pin_test_param); + } + if (limit.reset_item) { + reset_pin_test_result = cts_test_reset_pin(cts_dev, &reset_pin_test_param); + } + if (limit.normal_rawdata_item) { + rawdata_test_result = cts_test_rawdata(cts_dev, &rawdata_test_param); + } + if (limit.normal_noise_item) { + noise_test_result = cts_test_noise(cts_dev, &noise_test_param); + } + if (limit.open_item) { + open_test_result = cts_test_open(cts_dev, &open_test_param); + } + if (limit.short_item) { + short_test_result = cts_test_short(cts_dev, &short_test_param); + } + if (limit.comp_cap_item) { + comp_cap_test_result = + cts_test_compensate_cap(cts_dev, &comp_cap_test_param); + } + + if (rawdata_test_param.test_data_filepath) { + kfree(rawdata_test_param.test_data_filepath); + } + if (noise_test_param.test_data_filepath) { + kfree(noise_test_param.test_data_filepath); + } + if (open_test_param.test_data_filepath) { + kfree(open_test_param.test_data_filepath); + } + if (short_test_param.test_data_filepath) { + kfree(short_test_param.test_data_filepath); + } + if (comp_cap_test_param.test_data_filepath) { + kfree(comp_cap_test_param.test_data_filepath); + } + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + seq_printf(m, "Factory test, total ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + + seq_printf(m, "Firmware Version: 0x%04X\n", fw_version); + + if (limit.int_item) { + seq_printf(m, "Int-Pin : %s,\n", int_pin_test_result ? "Failed" : "Pass"); + } + if (limit.reset_item) { + seq_printf(m, "Reset-Pin: %s\n", reset_pin_test_result ? "Failed" : "Pass"); + } + if (limit.normal_rawdata_item) { + seq_printf(m, "Rawdata : %s\n", rawdata_test_result ? "Failed" : "Pass"); + } + if (limit.normal_noise_item) { + seq_printf(m, "Noise : %s\n", noise_test_result ? "Failed" : "Pass"); + } + if (limit.open_item) { + seq_printf(m, "Open : %s\n", open_test_result ? "Failed" : "Pass"); + } + if (limit.short_item) { + seq_printf(m, "Short : %s\n", short_test_result ? "Failed" : "Pass"); + } + if (limit.comp_cap_item) { + seq_printf(m, "Comp-Cap : %s\n", comp_cap_test_result ? "Failed" : "Pass"); + } + + return 0; + +#undef TOUCH_DATA_DIRECTORY_PREFIX +#undef RAWDATA_TEST_DATA_FILENAME +#undef NOISE_TEST_DATA_FILENAME +#undef OPEN_TEST_DATA_FILENAME +#undef SHORT_TEST_DATA_FILENAME +#undef COMP_CAP_TEST_DATA_FILENAME +} +static int cts_factory_test_open(struct inode *inode, struct file *file) +{ + return single_open(file, cts_factory_test_show, PDE_DATA(inode)); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) +static const struct file_operations cts_factory_test_ops = { + .owner = THIS_MODULE, + .open = cts_factory_test_open, + .read = seq_read, +}; +#else +static const struct proc_ops cts_factory_test_ops = { + .proc_open = cts_factory_test_open, + .proc_read = seq_read, +}; +#endif + + +int cts_oem_init(struct chipone_ts_data *cts_data) +{ + struct cts_oem_data *oem_data = NULL; + int ret; + + if (cts_data == NULL) { + cts_err("Init with cts_data = NULL"); + return -EINVAL; + } + + cts_info("Init"); + + cts_data->oem_data = NULL; + + oem_data = kzalloc(sizeof(*oem_data), GFP_KERNEL); + if (oem_data == NULL) { + cts_err("Alloc oem data failed"); + return -ENOMEM; + } + + cts_info(" - Create '/proc/"OEM_SELFTEST_PROC_FILENAME"'"); + oem_data->selftest_proc_entry = + proc_create_data(OEM_SELFTEST_PROC_FILENAME, + S_IRUGO, NULL, &selftest_proc_fops, cts_data); + if (oem_data->selftest_proc_entry == NULL) { + cts_err("Create '/proc/"OEM_SELFTEST_PROC_FILENAME"' failed"); + ret = -EFAULT; + goto free_oem_data; + } + + oem_data->limit_entry = + proc_create_data(OEM_LIMIT_PROC_FILENAME, + S_IRUGO, NULL, &cts_limit_ops, cts_data); + if (oem_data->limit_entry == NULL) { + cts_err("Create '/proc/"OEM_LIMIT_PROC_FILENAME"' failed"); + ret = -EFAULT; + goto free_oem_data; + } + + oem_data->factory_test_entry = + proc_create_data(OEM_FACTORY_TEST_PROC_FILENAME, + S_IRUGO, NULL, &cts_factory_test_ops, cts_data); + if (oem_data->factory_test_entry == NULL) { + cts_err("Create '/proc/"OEM_FACTORY_TEST_PROC_FILENAME"' failed"); + ret = -EFAULT; + goto free_oem_data; + } + + oem_data->rawdata_proc_entry = proc_create_data(OEM_RAWDATA_PROC_FILENAME, + S_IRUGO, NULL, &cts_rawdata_ops, cts_data); + if (oem_data->rawdata_proc_entry == NULL) { + cts_err("Create '/proc/"OEM_RAWDATA_PROC_FILENAME"' failed"); + ret = -EFAULT; + goto free_oem_data; + } + + oem_data->manual_proc_entry = proc_create_data(OEM_MANUAL_PROC_FILENAME, + S_IRUGO, NULL, &cts_manual_ops, cts_data); + if (oem_data->manual_proc_entry == NULL) { + cts_err("Create '/proc/"OEM_MANUAL_PROC_FILENAME"' failed"); + ret = -EFAULT; + goto free_oem_data; + } + + oem_data->diffdata_proc_entry = proc_create_data(OEM_DIFFDATA_PROC_FILENAME, + S_IRUGO, NULL, &cts_diffdata_ops, cts_data); + if (oem_data->diffdata_proc_entry == NULL) { + cts_err("Create '/proc/"OEM_DIFFDATA_PROC_FILENAME"' failed"); + ret = -EFAULT; + goto free_oem_data; + } + + cts_data->oem_data = oem_data; + oem_data->cts_data = cts_data; + return 0; + +free_oem_data: + kfree(oem_data); + return ret; +} + +int cts_oem_deinit(struct chipone_ts_data *cts_data) +{ + struct cts_oem_data *oem_data = NULL; + + if (cts_data == NULL) { + cts_err("Deinit with cts_data = NULL"); + return -EINVAL; + } + + if (cts_data->oem_data == NULL) { + cts_warn("Deinit with oem_data = NULL"); + return 0; + } + + cts_info("Deinit"); + + oem_data = cts_data->oem_data; + + if (oem_data->selftest_proc_entry) { + cts_info(" Remove '/proc/"OEM_SELFTEST_PROC_FILENAME"'"); + remove_proc_entry(OEM_SELFTEST_PROC_FILENAME, NULL); + } + + if (oem_data->limit_entry) { + cts_info(" Remove '/proc/"OEM_LIMIT_PROC_FILENAME"'"); + remove_proc_entry(OEM_LIMIT_PROC_FILENAME, NULL); + } + if (oem_data->factory_test_entry) { + cts_info(" Remove '/proc/"OEM_FACTORY_TEST_PROC_FILENAME"'"); + remove_proc_entry(OEM_FACTORY_TEST_PROC_FILENAME, NULL); + } + + if (oem_data->rawdata_proc_entry) { + cts_info(" Remove '/proc/"OEM_RAWDATA_PROC_FILENAME"'"); + remove_proc_entry(OEM_RAWDATA_PROC_FILENAME, NULL); + } + + if (oem_data->manual_proc_entry) { + cts_info(" Remove '/proc/"OEM_MANUAL_PROC_FILENAME"'"); + remove_proc_entry(OEM_MANUAL_PROC_FILENAME, NULL); + } + + if (oem_data->diffdata_proc_entry) { + cts_info(" Remove '/proc/"OEM_DIFFDATA_PROC_FILENAME"'"); + remove_proc_entry(OEM_DIFFDATA_PROC_FILENAME, NULL); + } + + free_selftest_data_mem(oem_data); + + kfree(cts_data->oem_data); + cts_data->oem_data = NULL; + + return 0; +} + diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_oem.h b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_oem.h new file mode 100644 index 000000000000..ca5001ed9222 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_oem.h @@ -0,0 +1,10 @@ +#ifndef CTS_OEM_H +#define CTS_OEM_H + +struct chipone_ts_data; + +extern int cts_oem_init(struct chipone_ts_data *cts_data); +extern int cts_oem_deinit(struct chipone_ts_data *cts_data); + +#endif /* CTS_VENDOR_H */ + diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_platform.c b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_platform.c new file mode 100644 index 000000000000..b6203cdad5db --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_platform.c @@ -0,0 +1,1364 @@ +#define LOG_TAG "Plat" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_firmware.h" +#include "cts_sysfs.h" +#include "cts_tcs.h" +extern struct chipone_ts_data *g_cts_data; +static struct wakeup_source *gesture_wakelock; + +#ifdef CFG_CTS_FW_LOG_REDIRECT +size_t cts_plat_get_max_fw_log_size(struct cts_platform_data *pdata) +{ + return CTS_FW_LOG_BUF_LEN; +} + +u8 *cts_plat_get_fw_log_buf(struct cts_platform_data *pdata, size_t size) +{ + return pdata->fw_log_buf; +} +#endif + +size_t cts_plat_get_max_i2c_xfer_size(struct cts_platform_data *pdata) +{ + return CFG_CTS_MAX_I2C_XFER_SIZE; +} + +#ifdef CONFIG_CTS_I2C_HOST +u8 *cts_plat_get_i2c_xfer_buf(struct cts_platform_data *pdata, size_t xfer_size) +{ + return pdata->i2c_fifo_buf; +} + +int cts_plat_i2c_write(struct cts_platform_data *pdata, u8 i2c_addr, + const void *src, size_t len, int retry, int delay) +{ + int ret = 0, retries = 0; + + struct i2c_msg msg = { + .flags = 0, + .addr = i2c_addr, + .buf = (u8 *) src, + .len = len, + }; + + do { + ret = i2c_transfer(pdata->i2c_client->adapter, &msg, 1); + if (ret != 1) { + if (ret >= 0) + ret = -EIO; + + if (delay) + mdelay(delay); + continue; + } else + return 0; + } while (++retries < retry); + + return ret; +} + +int cts_plat_i2c_read(struct cts_platform_data *pdata, u8 i2c_addr, + const u8 *wbuf, size_t wlen, void *rbuf, size_t rlen, + int retry, int delay) +{ + int num_msg, ret = 0, retries = 0; + + struct i2c_msg msgs[2] = { + { + .addr = i2c_addr, + .flags = 0, + .buf = (u8 *) wbuf, + .len = wlen + }, + { + .addr = i2c_addr, + .flags = I2C_M_RD, + .buf = (u8 *) rbuf, + .len = rlen + } + }; + + if (wbuf == NULL || wlen == 0) + num_msg = 1; + else + num_msg = 2; + + do { + ret = i2c_transfer(pdata->i2c_client->adapter, + msgs + ARRAY_SIZE(msgs) - num_msg, num_msg); + + if (ret != num_msg) { + if (ret >= 0) + ret = -EIO; + + if (delay) + mdelay(delay); + continue; + } else + return 0; + } while (++retries < retry); + + return ret; +} +#else +void dump_spi_common(const char *prefix, u8 *data, size_t datalen) +{ + u8 str[1024]; + int offset = 0; + int i; + + offset += snprintf(str + offset, sizeof(str) - offset, "%s", prefix); + for (i = 0; i < datalen; i++) { + offset += snprintf(str + offset, sizeof(str) - offset, " %02x", data[i]); + } + cts_err("%s", str); +} + +#ifdef CFG_CTS_MANUAL_CS +int cts_plat_set_cs(struct cts_platform_data *pdata, int val) +{ + if (val) + gpio_set_value(pdata->cs_gpio, 1); + else + gpio_set_value(pdata->cs_gpio, 0); + + return 0; +} +#endif + +int cts_spi_send_recv(struct cts_platform_data *pdata, size_t len, + u8 *tx_buffer, u8 *rx_buffer) +{ + struct chipone_ts_data *cts_data; + struct spi_message msg; + struct spi_transfer cmd = { +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + .delay_usecs = 0, +#endif + .speed_hz = pdata->spi_speed * 1000u, + .tx_buf = tx_buffer, + .rx_buf = rx_buffer, + .len = len, + /* .tx_dma = 0, */ + /* .rx_dma = 0, */ + }; + int ret = 0; + + cts_data = container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(pdata, 0); +#endif + spi_message_init(&msg); + spi_message_add_tail(&cmd, &msg); + ret = spi_sync(cts_data->spi_client, &msg); + if (ret) + cts_err("spi sync failed %d", ret); + + udelay(100); + +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(pdata, 1); +#endif + return ret; +} + +size_t cts_plat_get_max_spi_xfer_size(struct cts_platform_data *pdata) +{ + return CFG_CTS_MAX_SPI_XFER_SIZE; +} + +u8 *cts_plat_get_spi_xfer_buf(struct cts_platform_data *pdata, size_t xfer_size) +{ + return pdata->spi_cache_buf; +} + +int cts_plat_spi_write(struct cts_platform_data *pdata, u8 dev_addr, + const void *src, size_t len, int retry, int delay) +{ + int ret = 0, retries = 0; + u16 crc16_calc; + size_t data_len; + + if (len > CFG_CTS_MAX_SPI_XFER_SIZE) { + cts_err("write too much data:wlen=%zu", len); + return -EIO; + } + + if (pdata->cts_dev->rtdata.program_mode) { +#ifdef CONFIG_CTS_ICTYPE_ICNL9922C + data_len = len - 3; + pdata->spi_tx_buf[0] = dev_addr; + memcpy(&pdata->spi_tx_buf[1], src, 3); + put_unaligned_be24(data_len, &pdata->spi_tx_buf[4]); + crc16_calc = (u16) cts_crc16(pdata->spi_tx_buf, 7); + put_unaligned_be16(~crc16_calc, &pdata->spi_tx_buf[7]); + memcpy(&pdata->spi_tx_buf[13], (char *)src + 3, data_len); + crc16_calc = (u16) cts_crc16((char *)src + 3, data_len); + put_unaligned_be16(~crc16_calc, &pdata->spi_tx_buf[len + 10]); + memset(pdata->spi_tx_buf + len + 12, 0, 3); + + do { + ret = cts_spi_send_recv(pdata, len + 15, pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI write failed %d", ret); + if (delay) + mdelay(delay); + } else + return 0; + } while (++retries < retry); +#else + pdata->spi_tx_buf[0] = dev_addr; + memcpy(&pdata->spi_tx_buf[1], src, len); + do { + ret = cts_spi_send_recv(pdata, len + 1, pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI write failed %d", ret); + if (delay) + mdelay(delay); + } else + return 0; + } while (++retries < retry); +#endif + } else { + data_len = len - 2; + pdata->spi_tx_buf[0] = dev_addr; + pdata->spi_tx_buf[1] = *((u8 *) src + 1); + pdata->spi_tx_buf[2] = *((u8 *) src); + put_unaligned_le16(data_len, &pdata->spi_tx_buf[3]); + crc16_calc = (u16) cts_crc32(pdata->spi_tx_buf, 5); + put_unaligned_le16(crc16_calc, &pdata->spi_tx_buf[5]); + memcpy(&pdata->spi_tx_buf[7], (char *)src + 2, data_len); + crc16_calc = (u16) cts_crc32((char *)src + 2, data_len); + put_unaligned_le16(crc16_calc, &pdata->spi_tx_buf[7 + data_len]); + do { + ret = cts_spi_send_recv(pdata, len + 7, pdata->spi_tx_buf, + pdata->spi_rx_buf); + udelay(10 * data_len); + if (ret) { + cts_err("SPI write failed %d", ret); + if (delay) + mdelay(delay); + } else + return 0; + } while (++retries < retry); + } + return ret; +} + +int cts_plat_spi_read(struct cts_platform_data *pdata, u8 dev_addr, + const u8 *wbuf, size_t wlen, void *rbuf, size_t rlen, + int retry, int delay) +{ + int ret = 0, retries = 0; + u16 crc16_calc, crc16_recv; + + if (wlen > CFG_CTS_MAX_SPI_XFER_SIZE + || rlen > CFG_CTS_MAX_SPI_XFER_SIZE) { + cts_err("write/read too much data:wlen=%zd, rlen=%zd", wlen, rlen); + return -EIO; + } + + if (pdata->cts_dev->rtdata.program_mode) { +#ifdef CONFIG_CTS_ICTYPE_ICNL9922C + memset(pdata->spi_tx_buf, 0, CFG_CTS_MAX_SPI_XFER_SIZE); + pdata->spi_tx_buf[0] = dev_addr | 0x01; + memcpy(&pdata->spi_tx_buf[1], wbuf, wlen); + put_unaligned_be24(rlen, &pdata->spi_tx_buf[4]); + crc16_calc = (u16) cts_crc16(pdata->spi_tx_buf, 7); + put_unaligned_be16(~crc16_calc, &pdata->spi_tx_buf[7]); + + do { + ret = cts_spi_send_recv(pdata, wlen + rlen + 15, + pdata->spi_tx_buf, pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) + mdelay(delay); + continue; + } + memcpy(rbuf, pdata->spi_rx_buf + wlen + 10, rlen); + crc16_calc = (u16) cts_crc16(rbuf, rlen); + crc16_recv = get_unaligned_be16(&pdata->spi_rx_buf[wlen + rlen + 10]); + if (crc16_recv != (uint16_t)(~crc16_calc)) { + cts_err("SPI RX CRC error: rx_crc %04x != %04x", + crc16_recv, ~crc16_calc); + continue; + } + return 0; + } while (++retries < retry); +#else + pdata->spi_tx_buf[0] = dev_addr | 0x01; + memcpy(&pdata->spi_tx_buf[1], wbuf, wlen); + do { + ret = cts_spi_send_recv(pdata, rlen + 5, pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) + mdelay(delay); + continue; + } + memcpy(rbuf, pdata->spi_rx_buf + 5, rlen); + return 0; + } while (++retries < retry); +#endif + } else { + do { + if (wlen != 0) { + pdata->spi_tx_buf[0] = dev_addr | 0x01; + pdata->spi_tx_buf[1] = wbuf[1]; + pdata->spi_tx_buf[2] = wbuf[0]; + put_unaligned_le16(rlen, &pdata->spi_tx_buf[3]); + crc16_calc = (u16) cts_crc32(pdata->spi_tx_buf, 5); + put_unaligned_le16(crc16_calc, &pdata->spi_tx_buf[5]); + ret = cts_spi_send_recv(pdata, 7, pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) + mdelay(delay); + continue; + } + } + memset(pdata->spi_tx_buf, 0, 7); + pdata->spi_tx_buf[0] = dev_addr | 0x01; + udelay(100); + ret = cts_spi_send_recv(pdata, rlen + 2, + pdata->spi_tx_buf, pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) + mdelay(delay); + continue; + } + memcpy(rbuf, pdata->spi_rx_buf, rlen); + crc16_calc = (u16) cts_crc32(pdata->spi_rx_buf, rlen); + crc16_recv = get_unaligned_le16(&pdata->spi_rx_buf[rlen]); + if (crc16_recv != crc16_calc) { + cts_err("SPI RX CRC error: rx_crc %04x != %04x", + crc16_recv, crc16_calc); + continue; + } + return 0; + } while (++retries < retry); + } + if (retries >= retry) + cts_err("SPI read too much retry"); + + return -EIO; +} + +int cts_plat_spi_read_delay_idle(struct cts_platform_data *pdata, u8 dev_addr, + const u8 *wbuf, size_t wlen, void *rbuf, + size_t rlen, int retry, int delay, int idle) +{ + int ret = 0, retries = 0; + u16 crc; + + if (wlen > CFG_CTS_MAX_SPI_XFER_SIZE + || rlen > CFG_CTS_MAX_SPI_XFER_SIZE) { + cts_err("write/read too much data:wlen=%zu, rlen=%zu", wlen, rlen); + return -E2BIG; + } + + if (pdata->cts_dev->rtdata.program_mode) { +#ifdef CONFIG_CTS_ICTYPE_ICNL9922C + pdata->spi_tx_buf[0] = dev_addr | 0x01; + memcpy(&pdata->spi_tx_buf[1], wbuf, wlen); + put_unaligned_be24(rlen, &pdata->spi_tx_buf[4]); + crc = (u16) cts_crc32(pdata->spi_tx_buf, 7); + put_unaligned_be16(crc, &pdata->spi_tx_buf[7]); + do { + ret = cts_spi_send_recv(pdata, wlen + rlen + 15, + pdata->spi_tx_buf, pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) + mdelay(delay); + continue; + } + memcpy(rbuf, pdata->spi_rx_buf + wlen + 10, rlen); + crc = (u16) cts_crc32(rbuf, rlen); + if (get_unaligned_le16(&pdata->spi_rx_buf[wlen + rlen + 10]) != crc) { + cts_err("SPI RX CRC error: rx_crc %04x != %04x", + get_unaligned_le16(&pdata->spi_rx_buf[wlen + rlen + 10]), crc); + continue; + } + return 0; + } while (++retries < retry); +#else + pdata->spi_tx_buf[0] = dev_addr | 0x01; + memcpy(&pdata->spi_tx_buf[1], wbuf, wlen); + do { + ret = cts_spi_send_recv(pdata, rlen + 5, pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) + mdelay(delay); + continue; + } + memcpy(rbuf, pdata->spi_rx_buf + 5, rlen); + return 0; + } while (++retries < retry); +#endif + } else { + do { + if (wlen != 0) { + pdata->spi_tx_buf[0] = dev_addr | 0x01; + pdata->spi_tx_buf[1] = wbuf[1]; + pdata->spi_tx_buf[2] = wbuf[0]; + put_unaligned_le16(rlen, &pdata->spi_tx_buf[3]); + crc = (u16) cts_crc32(pdata->spi_tx_buf, 5); + put_unaligned_le16(crc, &pdata->spi_tx_buf[5]); + ret = cts_spi_send_recv(pdata, 7, pdata->spi_tx_buf, + pdata->spi_rx_buf); + if (ret) { + cts_err("SPI read failed %d", ret); + if (delay) + mdelay(delay); + continue; + } + } + memset(pdata->spi_tx_buf, 0, 7); + pdata->spi_tx_buf[0] = dev_addr | 0x01; + udelay(idle); + ret = cts_spi_send_recv(pdata, rlen + 2, + pdata->spi_tx_buf, pdata->spi_rx_buf); + if (ret) { + if (delay) + mdelay(delay); + continue; + } + memcpy(rbuf, pdata->spi_rx_buf, rlen); + crc = (u16) cts_crc32(pdata->spi_rx_buf, rlen); + if (get_unaligned_le16(&pdata->spi_rx_buf[rlen]) != crc) + continue; + return 0; + } while (++retries < retry); + } + if (retries >= retry) + cts_err("cts_plat_spi_read error"); + + return -EIO; +} +#endif + +int cts_plat_is_normal_mode(struct cts_platform_data *pdata) +{ + struct chipone_ts_data *cts_data; + u16 fwid; +/* + u8 tx_buf[4] = { 0 }; + u32 addr; +*/ + int ret; + + cts_set_normal_addr(pdata->cts_dev); + cts_data = container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + (void)cts_data; +#endif + ret = cts_tcs_get_fw_id(pdata->cts_dev, &fwid); +/* + addr = CTS_DEVICE_FW_REG_CHIP_TYPE; + put_unaligned_be16(addr, tx_buf); + ret = cts_plat_spi_read(pdata, CTS_DEV_NORMAL_MODE_SPIADDR, + tx_buf, 2, &fwid, 2, 3, 10); + fwid = be16_to_cpu(fwid); +*/ + if (ret || !cts_is_fwid_valid(fwid)) + return false; + + return true; +} + +static void cts_plat_handle_irq(struct cts_platform_data *pdata) +{ + int ret; + + cts_dbg("Handle IRQ"); + + cts_lock_device(pdata->cts_dev); + ret = cts_irq_handler(pdata->cts_dev); + if (ret) + cts_err("Device handle IRQ failed %d", ret); + cts_unlock_device(pdata->cts_dev); +} + +static irqreturn_t cts_plat_irq_handler(int irq, void *dev_id) +{ + struct cts_platform_data *pdata; +#ifndef CONFIG_GENERIC_HARDIRQS + struct chipone_ts_data *cts_data; +#endif +#ifdef TOUCHSCREEN_PM_BRL_SPI + int ret = 0; + struct chipone_ts_data *cts_data; +#endif + + cts_dbg("IRQ handler"); + + pdata = (struct cts_platform_data *)dev_id; + if (pdata == NULL) { + cts_err("IRQ handler with NULL dev_id"); + return IRQ_NONE; + } + +#ifdef TOUCHSCREEN_PM_BRL_SPI + cts_data = container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + if ((cts_data->cts_dev.rtdata.suspended) && + (cts_data->cts_dev.rtdata.gesture_wakeup_enabled)) { + if (pdata->gesture_wait_pm) { + PM_WAKEUP_EVENT(cts_data->gesture_wakelock, 3000); + /* Waiting for pm resume completed */ + ret = wait_event_interruptible_timeout(cts_data->pm_wq, atomic_read(&cts_data->pm_resume), msecs_to_jiffies(700)); + if (!ret) { + cts_err("system(spi) can't finished resuming procedure."); + return IRQ_HANDLED; + } + } + } +#endif + +#ifdef CONFIG_GENERIC_HARDIRQS + cts_plat_handle_irq(pdata); +#else + cts_data = container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); + + if (queue_work(cts_data->workqueue, &pdata->ts_irq_work)) { + cts_dbg("IRQ queue work"); + cts_plat_disable_irq(pdata); + } else + cts_warn("IRQ handler queue work failed as already on the queue"); +#endif /* CONFIG_GENERIC_HARDIRQS */ + + return IRQ_HANDLED; +} + +#ifndef CONFIG_GENERIC_HARDIRQS +static void cts_plat_touch_dev_irq_work(struct work_struct *work) +{ + struct cts_platform_data *pdata = + container_of(work, struct cts_platform_data, ts_irq_work); + + cts_dbg("IRQ work"); + + cts_plat_handle_irq(pdata); + + cts_plat_enable_irq(pdata); +} +#endif /* CONFIG_GENERIC_HARDIRQS */ + +#ifdef CONFIG_CTS_OF +static int cts_plat_parse_dt(struct cts_platform_data *pdata, + struct device_node *dev_node) +{ + int ret = 0; + + cts_info("Parse device tree"); + + pdata->int_gpio = of_get_named_gpio(dev_node, CFG_CTS_OF_INT_GPIO_NAME, 0); + if (!gpio_is_valid(pdata->int_gpio)) { + cts_err("Parse INT GPIO from dt failed %d", pdata->int_gpio); + pdata->int_gpio = -1; + } + cts_info(" %-12s: %d", "int gpio", pdata->int_gpio); + + pdata->irq = gpio_to_irq(pdata->int_gpio); + if (pdata->irq < 0) { + cts_err("Parse irq failed %d", ret); + return pdata->irq; + } + cts_info(" %-12s: %d", "irq num", pdata->irq); + +#ifdef CFG_CTS_HAS_RESET_PIN + pdata->rst_gpio = of_get_named_gpio(dev_node, CFG_CTS_OF_RST_GPIO_NAME, 0); + if (!gpio_is_valid(pdata->rst_gpio)) { + cts_err("Parse RST GPIO from dt failed %d", pdata->rst_gpio); + pdata->rst_gpio = -1; + } + cts_info(" %-12s: %d", "rst gpio", pdata->rst_gpio); + + pdata->rst_pull_flag = of_property_read_bool(dev_node, "chipone,rst-pull-flag"); + if (!pdata->rst_pull_flag) { + cts_err("Parse RST PULL FLAG from dt failed %d", pdata->rst_pull_flag); + pdata->rst_pull_flag = false; + } + cts_info(" %-12s: %d", "rst pull flag", pdata->rst_pull_flag); +#endif /* CFG_CTS_HAS_RESET_PIN */ + +#ifdef TOUCHSCREEN_PM_BRL_SPI + pdata->gesture_wait_pm = of_property_read_bool(dev_node, "chipone,gesture-wait-pm"); + if (!pdata->gesture_wait_pm) { + cts_err("Parse gesture wait pm from dt failed %d", pdata->gesture_wait_pm); + pdata->gesture_wait_pm = false; + } + cts_info(" %-12s: %d", "gesture wait pm", pdata->gesture_wait_pm); +#endif + +#ifdef CFG_CTS_MANUAL_CS + pdata->cs_gpio = of_get_named_gpio(dev_node, CFG_CTS_OF_CS_GPIO_NAME, 0); + if (!gpio_is_valid(pdata->cs_gpio)) { + cts_err("Parse CS GPIO from dt failed %d", pdata->cs_gpio); + pdata->cs_gpio = -1; + } + cts_info(" %-12s: %d", "cs gpio", pdata->cs_gpio); +#endif + + ret = of_property_read_u32(dev_node, CFG_CTS_OF_X_RESOLUTION_NAME, + &pdata->res_x); + if (ret) + cts_warn("Parse X resolution from dt failed %d", ret); + + cts_info(" %-12s: %d", "X resolution", pdata->res_x); + + ret = of_property_read_u32(dev_node, CFG_CTS_OF_Y_RESOLUTION_NAME, + &pdata->res_y); + if (ret) + cts_warn("Parse Y resolution from dt failed %d", ret); + + cts_info(" %-12s: %d", "Y resolution", pdata->res_y); + + if (of_property_read_u32(dev_node, "chipone,def-build-id", &pdata->build_id)) { + pdata->build_id = 0; + cts_info("chipone,build_id undefined."); + } else + cts_info("chipone,build_id=0x%04X", pdata->build_id); + + if (of_property_read_u32(dev_node, "chipone,def-config-id", &pdata->config_id)) { + pdata->config_id = 0; + cts_info("chipone,config_id undefined."); + } else + cts_info("chipone,config_id=0x%04X", pdata->config_id); + +#ifdef CFG_CTS_FW_UPDATE_SYS + ret = of_property_read_string(dev_node, CFG_CTS_OF_PANEL_SUPPLIER, + &pdata->panel_supplier); + if (ret) { + pdata->panel_supplier = NULL; + cts_warn("read panel supplier failed, ret=%d", ret); + } else + cts_info("panel supplier=%s", (char *)pdata->panel_supplier); +#endif + +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + if (!of_property_read_u32(dev_node, "chipone,supported_gesture_type", &pdata->supported_gesture_type)) + cts_info("chipone,supported_gesture_type=%02x\n", pdata->supported_gesture_type); +#endif + return 0; +} +#endif /* CONFIG_CTS_OF */ + +#ifdef CFG_CTS_FORCE_UP +static void cts_plat_touch_event_timeout_work(struct work_struct *work) +{ + struct cts_platform_data *pdata = container_of(work, + struct cts_platform_data, touch_event_timeout_work.work); + + cts_warn("Touch event timeout work"); + + cts_plat_release_all_touch(pdata); +} +#endif + +#ifndef CONFIG_CTS_I2C_HOST +int cts_plat_spi_setup(struct cts_platform_data *pdata) +{ + int ret; + + pdata->spi_client->chip_select = 0; + pdata->spi_client->mode = SPI_MODE_0; + pdata->spi_client->bits_per_word = 8; + + cts_info("chip_select :%d", pdata->spi_client->chip_select); + cts_info("spi_mode :%d", pdata->spi_client->mode); + cts_info("bits_per_word:%d", pdata->spi_client->bits_per_word); + + ret = spi_setup(pdata->spi_client); + if (ret) + cts_err("spi_setup err!"); + return 0; +} +#endif + +#ifdef CONFIG_CTS_I2C_HOST +int cts_init_platform_data(struct cts_platform_data *pdata, + struct i2c_client *i2c_client) +#else +int cts_init_platform_data(struct cts_platform_data *pdata, + struct spi_device *spi) +#endif +{ + struct input_dev *input_dev; + int ret = 0; + + cts_info("cts_init_platform_data Init"); + +#ifdef CONFIG_CTS_OF + { + struct device *dev; + +#ifdef CONFIG_CTS_I2C_HOST + dev = &i2c_client->dev; +#else + dev = &spi->dev; +#endif /* CONFIG_CTS_I2C_HOST */ + ret = cts_plat_parse_dt(pdata, dev->of_node); + if (ret) { + cts_err("Parse dt failed %d", ret); + return ret; + } + } +#endif /* CONFIG_CTS_OF */ + +#ifdef CONFIG_CTS_I2C_HOST + pdata->i2c_client = i2c_client; + pdata->i2c_client->irq = pdata->irq; +#else + pdata->spi_client = spi; + pdata->spi_client->irq = pdata->irq; +#endif /* CONFIG_CTS_I2C_HOST */ + + mutex_init(&pdata->dev_lock); + spin_lock_init(&pdata->irq_lock); + + input_dev = input_allocate_device(); + if (input_dev == NULL) { + cts_err("Failed to allocate input device."); + return -ENOMEM; + } + + /** - Init input device */ + input_dev->name = CFG_CTS_DEVICE_NAME; + input_dev->name = CFG_CTS_DEVICE_NAME; +#ifdef CONFIG_CTS_I2C_HOST + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &pdata->i2c_client->dev; +#else + input_dev->id.bustype = BUS_SPI; + input_dev->dev.parent = &pdata->spi_client->dev; +#endif /* CONFIG_CTS_I2C_HOST */ + input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); +#ifdef CFG_CTS_PALM_DETECT + set_bit(CFG_CTS_PALM_EVENT, input_dev->keybit); +#endif + +#ifdef CFG_CTS_SWAP_XY + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, pdata->res_y, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, pdata->res_x, 0, 0); +#else /* CFG_CTS_SWAP_XY */ + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, pdata->res_x, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, pdata->res_y, 0, 0); +#endif /* CFG_CTS_SWAP_XY */ + + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, + CFG_CTS_MAX_TOUCH_NUM * 2, 0, 0); + + input_set_capability(input_dev, EV_KEY, BTN_TOUCH); + +#ifdef CONFIG_CTS_SLOTPROTOCOL + input_mt_init_slots(input_dev, CFG_CTS_MAX_TOUCH_NUM, 0); +#endif /* CONFIG_CTS_SLOTPROTOCOL */ + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + __set_bit(EV_ABS, input_dev->evbit); + input_set_drvdata(input_dev, pdata); + ret = input_register_device(input_dev); + if (ret) { + cts_err("Failed to register input device"); + return ret; + } + + pdata->ts_input_dev = input_dev; + +#if !defined(CONFIG_GENERIC_HARDIRQS) + INIT_WORK(&pdata->ts_irq_work, cts_plat_touch_dev_irq_work); +#endif /* CONFIG_GENERIC_HARDIRQS */ + +#ifdef CONFIG_CTS_VIRTUALKEY + { + u8 vkey_keymap[CFG_CTS_NUM_VKEY] = CFG_CTS_VKEY_KEYCODES; + + memcpy(pdata->vkey_keycodes, vkey_keymap, sizeof(vkey_keymap)); + pdata->vkey_num = CFG_CTS_NUM_VKEY; + } +#endif /* CONFIG_CTS_VIRTUALKEY */ + +#ifdef CFG_CTS_GESTURE + { + int gesture_keymap[CFG_CTS_NUM_GESTURE][2] = CFG_CTS_GESTURE_KEYMAP; + + memcpy(pdata->gesture_keymap, gesture_keymap, sizeof(gesture_keymap)); + pdata->gesture_num = CFG_CTS_NUM_GESTURE; + } +#endif /* CFG_CTS_GESTURE */ + +#ifdef CFG_CTS_FORCE_UP + INIT_DELAYED_WORK(&pdata->touch_event_timeout_work, + cts_plat_touch_event_timeout_work); +#endif + +#ifndef CONFIG_CTS_I2C_HOST + pdata->spi_speed = CFG_CTS_SPI_SPEED_KHZ; + cts_plat_spi_setup(pdata); +#endif + return 0; +} + +int cts_deinit_platform_data(struct cts_platform_data *pdata) +{ + cts_info("De-Init platform_data"); + input_unregister_device(pdata->ts_input_dev); + return 0; +} + +int cts_plat_request_resource(struct cts_platform_data *pdata) +{ + int ret; + + cts_info("Request resource"); + + ret = gpio_request_one(pdata->int_gpio, GPIOF_IN, + CFG_CTS_DEVICE_NAME "-int"); + if (ret) { + cts_err("Request INT gpio (%d) failed %d", pdata->int_gpio, ret); + goto err_out; + } +#ifdef CFG_CTS_HAS_RESET_PIN + ret = gpio_request_one(pdata->rst_gpio, GPIOF_OUT_INIT_HIGH, + CFG_CTS_DEVICE_NAME "-rst"); + if (ret) { + cts_err("Request RST gpio (%d) failed %d", pdata->rst_gpio, ret); + goto err_free_int; + } +#endif /* CFG_CTS_HAS_RESET_PIN */ + +#ifdef CFG_CTS_MANUAL_CS + ret = gpio_request_one(pdata->cs_gpio, GPIOF_OUT_INIT_HIGH, + CFG_CTS_DEVICE_NAME "-cs"); + if (ret) { + cts_err("Request CS gpio (%d) failed %d", pdata->cs_gpio, ret); + goto err_request_cs_gpio; + } +#endif + + return 0; + +#ifdef CFG_CTS_MANUAL_CS +err_request_cs_gpio: +#endif + +#ifdef CONFIG_CTS_REGULATOR +err_free_rst: +#endif /* CONFIG_CTS_REGULATOR */ +#ifdef CFG_CTS_HAS_RESET_PIN + gpio_free(pdata->rst_gpio); +err_free_int: +#endif /* CFG_CTS_HAS_RESET_PIN */ + gpio_free(pdata->int_gpio); +err_out: + return ret; +} + +void cts_plat_free_resource(struct cts_platform_data *pdata) +{ + cts_info("Free resource"); + + if (gpio_is_valid(pdata->int_gpio)) + gpio_free(pdata->int_gpio); + +#ifdef CFG_CTS_HAS_RESET_PIN + if (gpio_is_valid(pdata->rst_gpio)) + gpio_free(pdata->rst_gpio); + +#endif /* CFG_CTS_HAS_RESET_PIN */ +#ifdef CFG_CTS_MANUAL_CS + if (gpio_is_valid(pdata->cs_gpio)) + gpio_free(pdata->cs_gpio); + +#endif +} + +int cts_plat_request_irq(struct cts_platform_data *pdata) +{ + int ret; + + cts_info("Request IRQ"); + +#ifdef CONFIG_GENERIC_HARDIRQS + ret = request_threaded_irq(pdata->irq, NULL, cts_plat_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, CFG_CTS_DRIVER_NAME, pdata); +#else /* CONFIG_GENERIC_HARDIRQS */ + ret = request_irq(pdata->irq, cts_plat_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, CFG_CTS_DRIVER_NAME, pdata); +#endif /* CONFIG_GENERIC_HARDIRQS */ + if (ret) { + cts_err("Request IRQ failed %d", ret); + return ret; + } + + cts_plat_disable_irq(pdata); + + return 0; +} + +void cts_plat_free_irq(struct cts_platform_data *pdata) +{ + free_irq(pdata->irq, pdata); +} + +int cts_plat_enable_irq(struct cts_platform_data *pdata) +{ + unsigned long irqflags; + + cts_dbg("Enable IRQ"); + + if (pdata->irq > 0) { + spin_lock_irqsave(&pdata->irq_lock, irqflags); + if (pdata->irq_is_disable) {/* && !cts_is_device_suspended(pdata->chip)) */ + cts_dbg("Real enable IRQ"); + enable_irq(pdata->irq); + pdata->irq_is_disable = false; + } + spin_unlock_irqrestore(&pdata->irq_lock, irqflags); + + return 0; + } + + return -ENODEV; +} + +int cts_plat_disable_irq(struct cts_platform_data *pdata) +{ + unsigned long irqflags; + + cts_dbg("Disable IRQ"); + + if (pdata->irq > 0) { + spin_lock_irqsave(&pdata->irq_lock, irqflags); + if (!pdata->irq_is_disable) { + cts_dbg("Real disable IRQ"); + disable_irq_nosync(pdata->irq); + pdata->irq_is_disable = true; + } + spin_unlock_irqrestore(&pdata->irq_lock, irqflags); + + return 0; + } + + return -ENODEV; +} + +#ifdef CFG_CTS_HAS_RESET_PIN +int cts_plat_reset_device(struct cts_platform_data *pdata) +{ + /* !!!can not be modified */ + /* !!!can not be modified */ + /* !!!can not be modified */ + cts_info("Reset device"); + + gpio_set_value(pdata->rst_gpio, 1); + mdelay(1); + gpio_set_value(pdata->rst_gpio, 0); + mdelay(10); + gpio_set_value(pdata->rst_gpio, 1); + mdelay(40); + + return 0; +} + +int cts_plat_set_reset(struct cts_platform_data *pdata, int val) +{ + cts_info("Set Reset to %s", val ? "HIGH" : "LOW"); + if (val) + gpio_set_value(pdata->rst_gpio, 1); + else + gpio_set_value(pdata->rst_gpio, 0); + + return 0; +} +#endif /* CFG_CTS_HAS_RESET_PIN */ + +int cts_plat_get_int_pin(struct cts_platform_data *pdata) +{ + return gpio_get_value(pdata->int_gpio); +} + +int cts_plat_power_up_device(struct cts_platform_data *pdata) +{ + cts_info("Power up device"); + + return 0; +} + +int cts_plat_power_down_device(struct cts_platform_data *pdata) +{ + cts_info("Power down device"); + + return 0; +} + +int cts_plat_init_touch_device(struct cts_platform_data *pdata) +{ + cts_info("Init touch device"); + + return 0; +} + +void cts_plat_deinit_touch_device(struct cts_platform_data *pdata) +{ + cts_info("De-init touch device"); + +#ifndef CONFIG_GENERIC_HARDIRQS + if (work_pending(&pdata->ts_irq_work)) + cancel_work_sync(&pdata->ts_irq_work); +#endif /* CONFIG_GENERIC_HARDIRQS */ +} + +#ifdef CFG_CTS_PALM_DETECT +void cts_report_palm_event(struct cts_platform_data *pdata) +{ + input_report_key(pdata->ts_input_dev, CFG_CTS_PALM_EVENT, 1); + input_sync(pdata->ts_input_dev); + msleep(100); + input_report_key(pdata->ts_input_dev, CFG_CTS_PALM_EVENT, 0); + input_sync(pdata->ts_input_dev); +} +#endif + +int cts_plat_process_touch_msg(struct cts_platform_data *pdata, + struct cts_device_touch_msg *msgs, int num) +{ + struct chipone_ts_data *cts_data; + struct input_dev *input_dev = pdata->ts_input_dev; + int i; + int contact = 0; +#ifdef CONFIG_CTS_SLOTPROTOCOL + static unsigned char finger_last[CFG_CTS_MAX_TOUCH_NUM] = { 0 }; + unsigned char finger_current[CFG_CTS_MAX_TOUCH_NUM] = { 0 }; +#endif + + cts_dbg("Process touch %d msgs", num); + cts_data = container_of(pdata->cts_dev, struct chipone_ts_data, cts_dev); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + (void) cts_data; +#endif + if (num == 0 || num > CFG_CTS_MAX_TOUCH_NUM) + return 0; + + for (i = 0; i < num; i++) { + u16 x, y; + + x = le16_to_cpu(msgs[i].x); + y = le16_to_cpu(msgs[i].y); + +#ifdef CFG_CTS_SWAP_XY + swap(x, y); +#endif /* CFG_CTS_SWAP_XY */ +#ifdef CFG_CTS_WRAP_X + x = wrap(pdata->res_x, x); +#endif /* CFG_CTS_WRAP_X */ +#ifdef CFG_CTS_WRAP_Y + y = wrap(pdata->res_y, y); +#endif /* CFG_CTS_WRAP_Y */ + cts_dbg(" Process touch msg[%d]: id[%u] ev=%u x=%u y=%u p=%u", + i, msgs[i].id, msgs[i].event, x, y, msgs[i].pressure); + if (msgs[i].event == CTS_DEVICE_TOUCH_EVENT_DOWN + || msgs[i].event == CTS_DEVICE_TOUCH_EVENT_MOVE + || msgs[i].event == CTS_DEVICE_TOUCH_EVENT_STAY) { + if (msgs[i].id < CFG_CTS_MAX_TOUCH_NUM) + finger_current[msgs[i].id] = 1; + } +#ifdef CONFIG_CTS_SLOTPROTOCOL + /* input_mt_slot(input_dev, msgs[i].id); */ + switch (msgs[i].event) { + case CTS_DEVICE_TOUCH_EVENT_DOWN: + case CTS_DEVICE_TOUCH_EVENT_MOVE: + case CTS_DEVICE_TOUCH_EVENT_STAY: + contact++; + #ifdef CONFIG_GTP_LAST_TIME + cts_data->last_event_time = ktime_get_boottime(); + #endif + input_mt_slot(input_dev, msgs[i].id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, msgs[i].pressure); + input_report_abs(input_dev, ABS_MT_PRESSURE, msgs[i].pressure); + break; + + case CTS_DEVICE_TOUCH_EVENT_UP: + /* input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); */ + break; + + default: + cts_warn("Process touch msg with unknwon event %u id %u", + msgs[i].event, msgs[i].id); + break; + } +#else /* CONFIG_CTS_SLOTPROTOCOL */ + /** + * If the driver reports one of BTN_TOUCH or ABS_PRESSURE + * in addition to the ABS_MT events, the last SYN_MT_REPORT event + * may be omitted. Otherwise, the last SYN_REPORT will be dropped + * by the input core, resulting in no zero-contact event + * reaching userland. + */ + switch (msgs[i].event) { + case CTS_DEVICE_TOUCH_EVENT_DOWN: + case CTS_DEVICE_TOUCH_EVENT_MOVE: + case CTS_DEVICE_TOUCH_EVENT_STAY: + contact++; + input_report_abs(input_dev, ABS_MT_PRESSURE, msgs[i].pressure); + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, msgs[i].pressure); + input_report_key(input_dev, BTN_TOUCH, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); + input_mt_sync(input_dev); + break; + + case CTS_DEVICE_TOUCH_EVENT_UP: + break; + default: + cts_warn("Process touch msg with unknwon event %u id %u", + msgs[i].event, msgs[i].id); + break; + } +#endif /* CONFIG_CTS_SLOTPROTOCOL */ + } + +#ifdef CONFIG_CTS_SLOTPROTOCOL + for (i = 0; i < CFG_CTS_MAX_TOUCH_NUM; i++) { + if (finger_last[i] != 0 && finger_current[i] == 0) { + input_mt_slot(input_dev, i); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + } + finger_last[i] = finger_current[i]; + } + input_report_key(input_dev, BTN_TOUCH, contact > 0); +#else + if (contact == 0) { + input_report_key(input_dev, BTN_TOUCH, 0); + input_mt_sync(input_dev); + } +#endif + input_sync(input_dev); +#ifdef CFG_CTS_FORCE_UP + if (contact) { + if (delayed_work_pending(&pdata->touch_event_timeout_work)) { + mod_delayed_work(cts_data->workqueue, + &pdata->touch_event_timeout_work, msecs_to_jiffies(100)); + } else { + queue_delayed_work(cts_data->workqueue, + &pdata->touch_event_timeout_work, msecs_to_jiffies(100)); + } + } else { + cancel_delayed_work_sync(&pdata->touch_event_timeout_work); + } +#endif + +#ifdef CFG_CTS_HEARTBEAT_MECHANISM + if (contact) { + if (delayed_work_pending(&cts_data->heart_work)) { + mod_delayed_work(cts_data->heart_workqueue, + &cts_data->heart_work, msecs_to_jiffies(2000)); + } else { + queue_delayed_work(cts_data->heart_workqueue, + &cts_data->heart_work, msecs_to_jiffies(2000)); + } + } +#endif + + return 0; +} + +int cts_plat_release_all_touch(struct cts_platform_data *pdata) +{ + struct input_dev *input_dev = pdata->ts_input_dev; +#if defined(CONFIG_CTS_SLOTPROTOCOL) + int id; +#endif /* CONFIG_CTS_SLOTPROTOCOL */ + + cts_info("Release all touch"); +#ifdef CONFIG_CTS_SLOTPROTOCOL + for (id = 0; id < CFG_CTS_MAX_TOUCH_NUM; id++) { + input_mt_slot(input_dev, id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + } + input_report_key(input_dev, BTN_TOUCH, 0); +#else + input_report_key(input_dev, BTN_TOUCH, 0); + input_mt_sync(input_dev); +#endif /* CONFIG_CTS_SLOTPROTOCOL */ + input_sync(input_dev); + + return 0; +} + +#ifdef CONFIG_CTS_VIRTUALKEY +int cts_plat_init_vkey_device(struct cts_platform_data *pdata) +{ + int i; + + cts_info("Init VKey"); + + pdata->vkey_state = 0; + + for (i = 0; i < pdata->vkey_num; i++) { + input_set_capability(pdata->ts_input_dev, + EV_KEY, pdata->vkey_keycodes[i]); + } + + return 0; +} + +void cts_plat_deinit_vkey_device(struct cts_platform_data *pdata) +{ + cts_info("De-init VKey"); + + pdata->vkey_state = 0; +} + +int cts_plat_process_vkey(struct cts_platform_data *pdata, u8 vkey_state) +{ + u8 event; + int i; + + event = pdata->vkey_state ^ vkey_state; + + cts_dbg("Process vkey state=0x%02x, event=0x%02x", vkey_state, event); + + for (i = 0; i < pdata->vkey_num; i++) { + input_report_key(pdata->ts_input_dev, pdata->vkey_keycodes[i], + vkey_state & BIT(i) ? 1 : 0); + } + + pdata->vkey_state = vkey_state; + + return 0; +} + +int cts_plat_release_all_vkey(struct cts_platform_data *pdata) +{ + int i; + + cts_info("Release all vkeys"); + + for (i = 0; i < pdata->vkey_num; i++) { + if (pdata->vkey_state & BIT(i)) { + input_report_key(pdata->ts_input_dev, pdata->vkey_keycodes[i], 0); + } + } + + pdata->vkey_state = 0; + + return 0; +} +#endif /* CONFIG_CTS_VIRTUALKEY */ + +#ifdef CFG_CTS_GESTURE +int cts_plat_enable_irq_wake(struct cts_platform_data *pdata) +{ + int ret; + + cts_info("Enable IRQ wake"); + + if (pdata->irq > 0) { + ret = enable_irq_wake(pdata->irq); + if (ret < 0) { + cts_err("Enable irq wake failed"); + return -EINVAL; + } + pdata->irq_wake_enabled = true; + return 0; + } + + cts_warn("Enable irq wake while irq invalid %d", pdata->irq); + return -ENODEV; +} + +int cts_plat_disable_irq_wake(struct cts_platform_data *pdata) +{ + int ret; + + cts_info("Disable IRQ wake"); + + if (pdata->irq > 0) { + ret = disable_irq_wake(pdata->irq); + if (ret < 0) { + cts_warn("Disable irq wake while already disabled"); + return -EINVAL; + } + pdata->irq_wake_enabled = false; + return 0; + } + + cts_warn("Disable irq wake while irq invalid %d", pdata->irq); + return -ENODEV; +} + +int cts_plat_init_gesture(struct cts_platform_data *pdata) +{ + int i; + + cts_info("Init gesture"); + + /* TODO: If system will issure enable/disable command, comment following line. */ + /* cts_enable_gesture_wakeup(pdata->cts_dev); */ + + for (i = 0; i < pdata->gesture_num; i++) { + input_set_capability(pdata->ts_input_dev, EV_KEY, + pdata->gesture_keymap[i][1]); + } + + return 0; +} + +void cts_plat_deinit_gesture(struct cts_platform_data *pdata) +{ + cts_info("De-init gesture"); +} + +int cts_plat_process_gesture_info(struct cts_platform_data *pdata, + struct cts_device_gesture_info *gesture_info) +{ + int i; + + cts_info("Process gesture, id=0x%02x", gesture_info->gesture_id); + +#if defined(CFG_CTS_GESTURE_REPORT_KEY) + for (i = 0; i < CFG_CTS_NUM_GESTURE; i++) { + if (gesture_info->gesture_id == pdata->gesture_keymap[i][0]) { + cts_info("Report key[%u]", pdata->gesture_keymap[i][1]); +#ifdef CHIPONE_SENSOR_EN + PM_WAKEUP_EVENT(gesture_wakelock, 5000); + input_report_key(g_cts_data->sensor_pdata->input_sensor_dev, pdata->gesture_keymap[i][1], 1); + input_sync(g_cts_data->sensor_pdata->input_sensor_dev); + input_report_key(g_cts_data->sensor_pdata->input_sensor_dev, pdata->gesture_keymap[i][1], 0); + input_sync(g_cts_data->sensor_pdata->input_sensor_dev); +#else + input_report_key(pdata->ts_input_dev, pdata->gesture_keymap[i][1], 1); + input_sync(pdata->ts_input_dev); + + input_report_key(pdata->ts_input_dev, pdata->gesture_keymap[i][1], 0); + input_sync(pdata->ts_input_dev); +#endif + return 0; + } + } +#endif /* CFG_CTS_GESTURE_REPORT_KEY */ + + cts_warn("Process unrecognized gesture id=%u", gesture_info->gesture_id); + + return -EINVAL; +} + +#endif /* CFG_CTS_GESTURE */ diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_platform.h b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_platform.h new file mode 100644 index 000000000000..7c771bd054ce --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_platform.h @@ -0,0 +1,289 @@ +#ifndef CTS_PLATFORM_H +#define CTS_PLATFORM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* #include */ +#include + +#ifdef CONFIG_OF +#include +#include +#include +#endif /* CONFIG_OF */ + +#include +#include + +#include +#include + +#include "cts_config.h" +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +#include +#else +#include +#endif +#include "cts_core.h" + +extern bool cts_show_debug_log; + +#ifndef LOG_TAG +#define LOG_TAG "" +#endif /* LOG_TAG */ + +enum cts_driver_log_level { + CTS_DRIVER_LOG_ERROR, + CTS_DRIVER_LOG_WARN, + CTS_DRIVER_LOG_INFO, + CTS_DRIVER_LOG_DEBUG, +}; + +extern int cts_start_driver_log_redirect(const char *filepath, bool append_to_file, + char *log_buffer, int log_buf_size, int log_level); +extern void cts_stop_driver_log_redirect(void); +extern int cts_get_driver_log_redirect_size(void); +extern void cts_log(int level, const char *fmt, ...); + +#define cts_err(fmt, ...) \ + cts_log(CTS_DRIVER_LOG_ERROR, "CTS-" LOG_TAG " " fmt"\n", ##__VA_ARGS__) +#define cts_warn(fmt, ...) \ + cts_log(CTS_DRIVER_LOG_WARN, "CTS-" LOG_TAG " " fmt"\n", ##__VA_ARGS__) +#define cts_info(fmt, ...) \ + cts_log(CTS_DRIVER_LOG_INFO, "CTS-" LOG_TAG " " fmt"\n", ##__VA_ARGS__) +#define cts_dbg(fmt, ...) \ + cts_log(CTS_DRIVER_LOG_DEBUG, "CTS-" LOG_TAG " " fmt"\n", ##__VA_ARGS__) + + +struct cts_device; +struct cts_device_touch_msg; +struct cts_device_gesture_info; + +struct cts_platform_data { + int irq; + int int_gpio; +#ifdef CFG_CTS_HAS_RESET_PIN + int rst_gpio; + bool rst_pull_flag; +#endif + +#ifdef CFG_CTS_MANUAL_CS + int cs_gpio; +#endif + + u32 res_x; + u32 res_y; + +#ifdef CONFIG_CTS_VIRTUALKEY + u8 vkey_num; + u8 vkey_state; + u8 vkey_keycodes[CFG_CTS_MAX_VKEY_NUM]; +#endif + +#ifdef CFG_CTS_FW_UPDATE_SYS + const char *panel_supplier; +#endif + u32 build_id; + u32 config_id; + + struct cts_device *cts_dev; + + struct input_dev *ts_input_dev; + +#ifndef CONFIG_GENERIC_HARDIRQS + struct work_struct ts_irq_work; +#endif + + struct mutex dev_lock; + struct spinlock irq_lock; + bool irq_is_disable; + +#ifdef CFG_CTS_GESTURE + u8 gesture_num; + int gesture_keymap[CFG_CTS_NUM_GESTURE][2]; + bool irq_wake_enabled; +#endif + +#ifdef CONFIG_CTS_PM_FB_NOTIFIER + struct notifier_block fb_notifier; +#endif + +#ifdef CFG_CTS_FORCE_UP + struct delayed_work touch_event_timeout_work; +#endif + +#ifdef CFG_CTS_FW_LOG_REDIRECT + u8 fw_log_buf[CTS_FW_LOG_BUF_LEN]; +#endif + +#ifdef CONFIG_CTS_I2C_HOST + struct i2c_client *i2c_client; + u8 i2c_fifo_buf[CFG_CTS_MAX_I2C_XFER_SIZE]; + u8 i2c_rbuf[ALIGN(CFG_CTS_MAX_I2C_READ_SIZE, 4)]; +#else + struct spi_device *spi_client; + u8 spi_cache_buf[ALIGN(CFG_CTS_MAX_SPI_XFER_SIZE + 10, 4)]; + u8 spi_rx_buf[ALIGN(CFG_CTS_MAX_SPI_XFER_SIZE + 10, 4)]; + u8 spi_tx_buf[ALIGN(CFG_CTS_MAX_SPI_XFER_SIZE + 10, 4)]; + u32 spi_speed; +#endif + +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + int supported_gesture_type; +#endif + +#ifdef CTS_STOWED_MODE_EN + int stowed_set; + int stowed_get; +#endif + +#ifdef TOUCHSCREEN_PM_BRL_SPI + bool gesture_wait_pm; +#endif +}; + +#ifdef CONFIG_CTS_I2C_HOST +extern size_t cts_plat_get_max_i2c_xfer_size(struct cts_platform_data *pdata); +extern u8 *cts_plat_get_i2c_xfer_buf(struct cts_platform_data *pdata, + size_t xfer_size); +extern int cts_plat_i2c_write(struct cts_platform_data *pdata, u8 i2c_addr, + const void *src, size_t len, int retry, int delay); +extern int cts_plat_i2c_read(struct cts_platform_data *pdata, u8 i2c_addr, + const u8 *wbuf, size_t wlen, void *rbuf, + size_t rlen, int retry, int delay); +#else /* CONFIG_CTS_I2C_HOST */ +extern size_t cts_plat_get_max_spi_xfer_size(struct cts_platform_data *pdata); +extern u8 *cts_plat_get_spi_xfer_buf(struct cts_platform_data *pdata, + size_t xfer_size); +extern int cts_plat_spi_write(struct cts_platform_data *pdata, u8 i2c_addr, + const void *src, size_t len, int retry, int delay); +extern int cts_plat_spi_read(struct cts_platform_data *pdata, u8 i2c_addr, + const u8 *wbuf, size_t wlen, void *rbuf, + size_t rlen, int retry, int delay); +extern int cts_plat_spi_read_delay_idle(struct cts_platform_data *pdata, + u8 dev_addr, const u8 *wbuf, size_t wlen, void *rbuf, size_t rlen, + int retry, int delay, int idle); +extern int cts_spi_send_recv(struct cts_platform_data *pdata, size_t len, + u8 *tx_buffer, u8 *rx_buffer); +#endif /* CONFIG_CTS_I2C_HOST */ + +#ifdef CONFIG_CTS_I2C_HOST +extern int cts_init_platform_data(struct cts_platform_data *pdata, + struct i2c_client *i2c_client); +#else +extern int cts_init_platform_data(struct cts_platform_data *pdata, + struct spi_device *spi); +#endif +extern int cts_plat_is_normal_mode(struct cts_platform_data *pdata); + +extern int cts_deinit_platform_data(struct cts_platform_data *pdata); +extern int cts_plat_request_resource(struct cts_platform_data *pdata); +extern void cts_plat_free_resource(struct cts_platform_data *pdata); + +extern int cts_plat_request_irq(struct cts_platform_data *pdata); +extern void cts_plat_free_irq(struct cts_platform_data *pdata); +extern int cts_plat_enable_irq(struct cts_platform_data *pdata); +extern int cts_plat_disable_irq(struct cts_platform_data *pdata); + +#ifdef CFG_CTS_HAS_RESET_PIN +extern int cts_plat_reset_device(struct cts_platform_data *pdata); +#else /* CFG_CTS_HAS_RESET_PIN */ +static inline int cts_plat_reset_device(struct cts_platform_data *pdata) +{ + return 0; +} +#endif /* CFG_CTS_HAS_RESET_PIN */ + +extern int cts_plat_init_touch_device(struct cts_platform_data *pdata); +extern void cts_plat_deinit_touch_device(struct cts_platform_data *pdata); +#ifdef CFG_CTS_PALM_DETECT +void cts_report_palm_event(struct cts_platform_data *pdata); +#endif +extern int cts_plat_process_touch_msg(struct cts_platform_data *pdata, + struct cts_device_touch_msg *msgs, int num); +extern int cts_plat_release_all_touch(struct cts_platform_data *pdata); + +#ifdef CONFIG_CTS_VIRTUALKEY +extern int cts_plat_init_vkey_device(struct cts_platform_data *pdata); +extern void cts_plat_deinit_vkey_device(struct cts_platform_data *pdata); +extern int cts_plat_process_vkey(struct cts_platform_data *pdata, + u8 vkey_state); +extern int cts_plat_release_all_vkey(struct cts_platform_data *pdata); +#else /* CONFIG_CTS_VIRTUALKEY */ +static inline int cts_plat_init_vkey_device(struct cts_platform_data *pdata) +{ + return 0; +} + +static inline void cts_plat_deinit_vkey_device(struct cts_platform_data *pdata) +{ +} + +static inline int cts_plat_process_vkey(struct cts_platform_data *pdata, + u8 vkey_state) +{ + return 0; +} + +static inline int cts_plat_release_all_vkey(struct cts_platform_data *pdata) +{ + return 0; +} +#endif /* CONFIG_CTS_VIRTUALKEY */ + +#ifdef CFG_CTS_GESTURE +extern int cts_plat_enable_irq_wake(struct cts_platform_data *pdata); +extern int cts_plat_disable_irq_wake(struct cts_platform_data *pdata); + +extern int cts_plat_init_gesture(struct cts_platform_data *pdata); +extern void cts_plat_deinit_gesture(struct cts_platform_data *pdata); +extern int cts_plat_process_gesture_info(struct cts_platform_data *pdata, + struct cts_device_gesture_info *gesture_info); +#else /* CFG_CTS_GESTURE */ +static inline int cts_plat_init_gesture(struct cts_platform_data *pdata) +{ + return 0; +} + +static inline void cts_plat_deinit_gesture(struct cts_platform_data *pdata) +{ +} +#endif /* CFG_CTS_GESTURE */ + +#ifdef CFG_CTS_FW_LOG_REDIRECT +extern size_t cts_plat_get_max_fw_log_size(struct cts_platform_data *pdata); +extern u8 *cts_plat_get_fw_log_buf(struct cts_platform_data *pdata, + size_t size); +#endif + +extern int cts_plat_set_reset(struct cts_platform_data *pdata, int val); +extern int cts_plat_get_int_pin(struct cts_platform_data *pdata); + +#ifdef CFG_CTS_MANUAL_CS +extern int cts_plat_set_cs(struct cts_platform_data *pdata, int val); +#endif +#endif /* CTS_PLATFORM_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_sfctrl.h b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_sfctrl.h new file mode 100644 index 000000000000..c0744477f995 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_sfctrl.h @@ -0,0 +1,36 @@ +#ifndef CTS_SFCTRL_H +#define CTS_SFCTRL_H + +#include "cts_core.h" +#include "cts_spi_flash.h" + +struct cts_device; + +struct cts_sfctrl_ops { + int (*rdid)(const struct cts_device *cts_dev, u32 *id); + int (*se)(const struct cts_device *cts_dev, u32 sector_addr); + int (*be)(const struct cts_device *cts_dev, u32 sector_addr); + int (*read)(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t len); + int (*read_to_sram)(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t len); + int (*program)(const struct cts_device *cts_dev, + u32 flash_addr, const void *src, size_t len); + int (*program_from_sram)(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t len); + int (*calc_sram_crc)(const struct cts_device *cts_dev, + u32 sram_addr, size_t len, u32 *crc); + int (*calc_flash_crc)(const struct cts_device *cts_dev, + u32 flash_addr, size_t len, u32 *crc); +}; + +struct cts_sfctrl { + u32 reg_base; + u32 xchg_sram_base; + size_t xchg_sram_size; + const struct cts_sfctrl_ops *ops; +}; + +extern const struct cts_sfctrl_ops cts_sfctrlv2_ops; + +#endif /* CTS_SFCTRL_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_sfctrlv2.c b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_sfctrlv2.c new file mode 100644 index 000000000000..5244ac22250a --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_sfctrlv2.c @@ -0,0 +1,339 @@ +#define LOG_TAG "SFCtrl" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sfctrl.h" + +#define rSFCTRLv2_CMD_SEL (0x0000) +#define rSFCTRLv2_FLASH_ADDR (0x0004) +#define rSFCTRLv2_SRAM_ADDR (0x0008) +#define rSFCTRLv2_DATA_LENGTH (0x000C) +#define rSFCTRLv2_START_DEXC (0x0010) +#define rSFCTRLv2_RELEASE_FLASH (0x0014) +#define rSFCTRLv2_HW_STATE (0x0018) +#define rSFCTRLv2_CRC_RESULT (0x001C) +#define rSFCTRLv2_SRAM_CRC_START (0x0020) +#define rSFCTRLv2_FLASH_CRC_START (0x0022) +#define rSFCTRLv2_SF_BUSY (0x0024) + +/** Constants for register @ref rSFCTRLv2_CMD_SEL */ +enum sfctrlv2_cmd { + SFCTRLv2_CMD_FAST_READ = 0x01u, + SFCTRLv2_CMD_SE = 0x02u, + SFCTRLv2_CMD_BE = 0x03u, + SFCTRLv2_CMD_PP = 0x04u, + SFCTRLv2_CMD_RDSR = 0x05u, + SFCTRLv2_CMD_RDID = 0x06u +}; + +/** SPI flash controller v2 operation flags. */ +enum sfctrlv2_opflags { + SFCTRLv2_OPFLAG_READ = BIT(0), + SFCTRLv2_OPFLAG_SET_FLASH_ADDR = BIT(1), + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG = BIT(2), + SFCTRLv2_OPFLAG_SET_DATA_LENGTH = BIT(3), + SFCTRLv2_OPFLAG_WAIT_WIP_CLR = BIT(4), +}; + +#define SFCTRLv2_CMD_RDID_FLAGS \ + (SFCTRLv2_OPFLAG_READ | \ + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG) + +#define SFCTRLv2_CMD_RDSR_FLAGS \ + (SFCTRLv2_OPFLAG_READ | \ + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG) + +#define SFCTRLv2_CMD_SE_FLAGS \ + (SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \ + SFCTRLv2_OPFLAG_WAIT_WIP_CLR) + +#define SFCTRLv2_CMD_BE_FLAGS \ + (SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \ + SFCTRLv2_OPFLAG_WAIT_WIP_CLR) + +#define SFCTRLv2_CMD_PP_FLAGS \ + (SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \ + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG | \ + SFCTRLv2_OPFLAG_SET_DATA_LENGTH | \ + SFCTRLv2_OPFLAG_WAIT_WIP_CLR) + +#define SFCTRLv2_CMD_FAST_READ_FLAGS \ + (SFCTRLv2_OPFLAG_READ | \ + SFCTRLv2_OPFLAG_SET_FLASH_ADDR | \ + SFCTRLv2_OPFLAG_SRAM_DATA_XCHG | \ + SFCTRLv2_OPFLAG_SET_DATA_LENGTH) + +#define sfctrl_reg_addr(cts_dev, offset) \ + ((cts_dev)->hwdata->sfctrl->reg_base + offset) + +#define DEFINE_SFCTRL_REG_ACCESS_FUNC(access_type, data_type) \ + static inline int sfctrl_reg_ ## access_type(const struct cts_device *cts_dev, \ + u32 reg, data_type data) { \ + return cts_hw_reg_ ## access_type(cts_dev, sfctrl_reg_addr(cts_dev, reg), data); \ + } + +DEFINE_SFCTRL_REG_ACCESS_FUNC(writeb_relaxed, u8) + DEFINE_SFCTRL_REG_ACCESS_FUNC(writew_relaxed, u16) + DEFINE_SFCTRL_REG_ACCESS_FUNC(writel_relaxed, u32) + DEFINE_SFCTRL_REG_ACCESS_FUNC(readb_relaxed, u8 *) + DEFINE_SFCTRL_REG_ACCESS_FUNC(readw_relaxed, u16 *) + DEFINE_SFCTRL_REG_ACCESS_FUNC(readl_relaxed, u32 *) +#define sfctrl_write_reg_check_ret(access_type, reg, val) \ + do { \ + int ret; \ + cts_dbg("Write " #reg " to 0x%x", val); \ + ret = sfctrl_reg_ ## access_type(cts_dev, reg, val); \ + if (ret) { \ + cts_err("Write " #reg " failed %d", ret); \ + return ret; \ + } \ + } while (0) +#define sfctrl_read_reg_check_ret(access_type, reg, val) \ + do { \ + int ret; \ + cts_dbg("Read " #reg ""); \ + ret = sfctrl_reg_ ## access_type(cts_dev, reg, val); \ + if (ret) { \ + cts_err("Read " #reg " failed %d", ret); \ + return ret; \ + } \ + } while (0) +static int wait_sfctrl_xfer_comp(const struct cts_device *cts_dev) +{ + int retries = 0; + u8 status; + + do { + sfctrl_read_reg_check_ret(readb_relaxed, rSFCTRLv2_SF_BUSY, &status); + if (status == 0) + return 0; + mdelay(20); + } while (status && retries++ < 50); + + return -ETIMEDOUT; +} + +static int sfctrlv2_rdsr(const struct cts_device *cts_dev, u8 *status) +{ +#define RDSR_XCHG_SRAM_ADDR (cts_dev->hwdata->sfctrl->xchg_sram_base + cts_dev->hwdata->sfctrl->xchg_sram_size - 1) + + int ret; + + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_CMD_SEL, + SFCTRLv2_CMD_RDSR); + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_SRAM_ADDR, + RDSR_XCHG_SRAM_ADDR); + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_START_DEXC, 1); + + ret = wait_sfctrl_xfer_comp(cts_dev); + if (ret != 0) { + cts_err("Wait sfctrl ready failed %d", ret); + return ret; + } + + ret = cts_sram_readb(cts_dev, RDSR_XCHG_SRAM_ADDR, status); + if (ret) { + cts_err("Read exchange sram failed %d", ret); + return ret; + } +#undef RDSR_XCHG_SRAM_ADDR + + return 0; +} + +static int wait_flash_wip_clear(const struct cts_device *cts_dev) +{ +#define FLASH_SR_WIP BIT(0) /*!< Write in progress */ + + int ret, retries = 0; + u8 status; + + do { + ret = sfctrlv2_rdsr(cts_dev, &status); + if (ret) { + cts_err("Read flash status register failed %d", ret); + return ret; + } + + if (status & FLASH_SR_WIP) + mdelay(1); + else + return 0; + } while (status & FLASH_SR_WIP && retries++ < 1000); + + return -ETIMEDOUT; +#undef FLASH_SR_WIP +} + +static int sfctrlv2_transfer(const struct cts_device *cts_dev, + u8 cmd, void *data, u32 flash_addr, u32 sram_addr, + size_t size, u32 flags) +{ + int ret; + + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_CMD_SEL, cmd); + + if (flags & SFCTRLv2_OPFLAG_SRAM_DATA_XCHG) { + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_SRAM_ADDR, + sram_addr); + +/** - Write data to exchange SRAM if operation is write and + * data != NULL(data not in SRAM) + */ + if ((!(flags & SFCTRLv2_OPFLAG_READ)) && data) { + ret = cts_sram_writesb(cts_dev, sram_addr, data, size); + if (ret) { + cts_err("Write data to exchange sram 0x%06x size %zu failed %d", + sram_addr, size, ret); + return ret; + } + } + } + + if (flags & SFCTRLv2_OPFLAG_SET_FLASH_ADDR) { + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_FLASH_ADDR, + flash_addr); + } + if (flags & SFCTRLv2_OPFLAG_SET_DATA_LENGTH) { + sfctrl_write_reg_check_ret(writel_relaxed, + rSFCTRLv2_DATA_LENGTH, (u32) size); + } + + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_START_DEXC, 1); + + ret = wait_sfctrl_xfer_comp(cts_dev); + if (ret != 0) { + cts_err("Wait sfctrl ready failed %d", ret); + return ret; + } + + ret = wait_flash_wip_clear(cts_dev); + if (flags & SFCTRLv2_OPFLAG_WAIT_WIP_CLR && ret != 0) { + cts_err("Wait WIP clear failed %d", ret); + return ret; + } + + if ((flags & SFCTRLv2_OPFLAG_READ) && data) { + ret = cts_sram_readsb(cts_dev, sram_addr, data, size); + if (ret) { + cts_err("Read data from exchange sram 0x%06x size %zu failed %d", + sram_addr, size, ret); + return ret; + } + } + + return 0; +} + +static int sfctrlv2_rdid(const struct cts_device *cts_dev, u32 *id) +{ + int ret; + u8 buf[4]; + + ret = sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_RDID, buf, + 0, cts_dev->hwdata->sfctrl->xchg_sram_base, 3, + SFCTRLv2_CMD_RDID_FLAGS); + *id = ret ? 0 : get_unaligned_be24(buf); + + return ret; +} + +static int sfctrlv2_se(const struct cts_device *cts_dev, u32 sector_addr) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_SE, NULL, + sector_addr, 0, 0, SFCTRLv2_CMD_SE_FLAGS); +} + +static int sfctrlv2_be(const struct cts_device *cts_dev, u32 block_addr) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_BE, NULL, + block_addr, 0, 0, SFCTRLv2_CMD_BE_FLAGS); +} + +static int sfctrlv2_pp(const struct cts_device *cts_dev, + u32 flash_addr, const void *src, size_t size) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_PP, (void *)src, + flash_addr, cts_dev->hwdata->sfctrl->xchg_sram_base, size, + SFCTRLv2_CMD_PP_FLAGS); +} + +static int sfctrlv2_program_flash_from_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t len) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_PP, NULL, + flash_addr, sram_addr, len, SFCTRLv2_CMD_PP_FLAGS); +} + +static int sfctrlv2_read(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t size) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_FAST_READ, dst, + flash_addr, cts_dev->hwdata->sfctrl->xchg_sram_base, size, + SFCTRLv2_CMD_FAST_READ_FLAGS); +} + +static int sfctrlv2_read_flash_to_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t size) +{ + return sfctrlv2_transfer(cts_dev, SFCTRLv2_CMD_FAST_READ, NULL, + flash_addr, sram_addr, size, SFCTRLv2_CMD_FAST_READ_FLAGS); +} + +int sfctrlv2_calc_sram_crc(const struct cts_device *cts_dev, + u32 sram_addr, size_t size, u32 *crc) +{ + int ret; + + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_SRAM_ADDR, sram_addr); + mdelay(1); + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_DATA_LENGTH, (u32) size); + mdelay(1); + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_SRAM_CRC_START, 1); + mdelay(10); + + ret = wait_sfctrl_xfer_comp(cts_dev); + if (ret != 0) { + cts_err("Wait sfctrl ready failed %d", ret); + return ret; + } + + sfctrl_read_reg_check_ret(readl_relaxed, rSFCTRLv2_CRC_RESULT, crc); + + return 0; +} + +int sfctrlv2_calc_flash_crc(const struct cts_device *cts_dev, + u32 flash_addr, size_t size, u32 *crc) +{ + int ret; + + cts_info("Calc crc from flash 0x%06x size %zu", flash_addr, size); + + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_FLASH_ADDR, flash_addr); + sfctrl_write_reg_check_ret(writel_relaxed, rSFCTRLv2_DATA_LENGTH, (u32) size); + sfctrl_write_reg_check_ret(writeb_relaxed, rSFCTRLv2_FLASH_CRC_START, 1); + + ret = wait_sfctrl_xfer_comp(cts_dev); + if (ret != 0) { + cts_err("Wait sfctrl ready failed %d", ret); + return ret; + } + + sfctrl_read_reg_check_ret(readl_relaxed, rSFCTRLv2_CRC_RESULT, crc); + + return 0; +} + +const struct cts_sfctrl_ops cts_sfctrlv2_ops = { + .rdid = sfctrlv2_rdid, + .se = sfctrlv2_se, + .be = sfctrlv2_be, + .read = sfctrlv2_read, + .read_to_sram = sfctrlv2_read_flash_to_sram, + .program = sfctrlv2_pp, + .program_from_sram = sfctrlv2_program_flash_from_sram, + .calc_sram_crc = sfctrlv2_calc_sram_crc, + .calc_flash_crc = sfctrlv2_calc_flash_crc, +}; diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_spi_flash.c b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_spi_flash.c new file mode 100644 index 000000000000..654dc8066304 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_spi_flash.c @@ -0,0 +1,650 @@ +#define LOG_TAG "Flash" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_sfctrl.h" +#include "cts_spi_flash.h" +#include "cts_strerror.h" + +/* NOTE: double check command sets and memory organization when you add + * more flash chips. This current list focusses on newer chips, which + * have been converging on command sets which including JEDEC ID. + */ +static const struct cts_flash cts_flashes[] = { + /* Winbond */ + { "Winbond,W25Q10EW", + 0xEF6011, 256, 0x1000, 0x8000, 0x20000}, + { "Winbond,W25Q20EW", + 0xEF6012, 256, 0x1000, 0x8000, 0x40000}, + { "Winbond,W25Q40EW", + 0xEF6013, 256, 0x1000, 0x8000, 0x80000}, + { "Winbond,W25Q20BW", + 0xEF5012, 256, 0x1000, 0x8000, 0x40000}, + { "Winbond,W25Q40BW", + 0xEF5013, 256, 0x1000, 0x8000, 0x80000}, + + /* Giga device */ + { "Giga,GD25LQ10B", + 0xC86011, 256, 0x1000, 0x8000, 0x20000}, + { "Giga,GD25LD20CEIGR", + 0xC86012, 256, 0x1000, 0x8000, 0x40000}, + { "Giga,GD25LD40CEIGR", + 0xC86013, 256, 0x1000, 0x8000, 0x80000}, + + /* Macronix */ + { "Macronix,MX25U1001E", + 0xC22531, 32, 0x1000, 0x10000, 0x20000}, + { "Macronix,MX25R2035F", + 0xC22812, 256, 0x1000, 0x8000, 0x40000}, + { "Macronix,MX25R4035F", + 0xC22813, 256, 0x1000, 0x8000, 0x80000}, + + /* Puya-Semi */ + { "Puya-Semi,P25Q20LUVHIR", + 0x856012, 256, 0x1000, 0x8000, 0x40000}, + { "Puya-Semi,P25Q40LUXHIR", + 0x856013, 256, 0x1000, 0x8000, 0x80000}, + { "Puya-Semi,P25Q11L", + 0x854011, 256, 0x1000, 0x8000, 0x20000}, + { "Puya-Semi,P25Q21L", + 0x854012, 256, 0x1000, 0x8000, 0x40000}, + { "Puya-Semi,P25T22L", + 0x854412, 256, 0x1000, 0x8000, 0x40000}, + + + /* Boya */ + { "Boya,BY25D10", + 0x684011, 256, 0x1000, 0x8000, 0x20000}, + + /* EON */ + { "EoN,EN25S20A", + 0x1C3812, 256, 0x1000, 0x8000, 0x40000}, + + /* XTXTECH */ + { "XTX,XT25Q01", + 0x0B6011, 256, 0x1000, 0, 0x20000}, + { "XTX,XT25Q02", + 0x0B6012, 256, 0x1000, 0, 0x40000}, + + /* Xinxin-Semi */ + { "Xinxin-Semi,XM25QU20B", + 0x205012, 256, 0x1000, 0x8000, 0x40000}, + { "Xinxin-Semi,XM25QU40B", + 0x205013, 256, 0x1000, 0x8000, 0x80000}, + + { "KangYong,XK25Q20T", + 0xEB6012, 256, 0x1000, 0x8000, 0x40000}, +}; + +static const struct cts_flash *find_flash_by_jedec_id(u32 jedec_id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cts_flashes); i++) { + if (cts_flashes[i].jedec_id == jedec_id) { + return &cts_flashes[i]; + } + } + + return NULL; +} + +static int probe_flash(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Probe flash"); + + if (cts_dev->hwdata->sfctrl->ops->rdid != NULL) { + u32 id; + + cts_info("Read JEDEC ID"); + + ret = cts_dev->hwdata->sfctrl->ops->rdid(cts_dev, &id); + if (ret) { + cts_err("Read JEDEC ID failed %d(%s)", + ret, cts_strerror(ret)); + return ret; + } + + cts_dev->flash = find_flash_by_jedec_id(id); + if (cts_dev->flash == NULL) { + cts_err("Unknown JEDEC ID: %06x", id); + return -ENODEV; + } + + cts_info("Flash type: '%s'", cts_dev->flash->name); + return 0; + } else { + cts_err("Probe flash with sfctrl->ops->rdid == NULL"); + return -ENOTSUPP; + } +} + +/** Make sure sector addr is sector aligned && < flash total size */ +static int erase_sector_retry(const struct cts_device *cts_dev, + u32 sector_addr, int retry) +{ + int ret, retries; + + cts_info(" Erase sector 0x%06x", sector_addr); + + retries = 0; + do { + retries++; + ret = cts_dev->hwdata->sfctrl->ops->se(cts_dev, sector_addr); + if (ret) { + cts_err("Erase sector 0x%06x failed %d(%s) retries %d", + sector_addr, ret, cts_strerror(ret), retries); + continue; + } + } while (retries < retry); + + return ret; +} + +/** Make sure sector addr is sector aligned && < flash total size */ +static inline int erase_sector(const struct cts_device *cts_dev, + u32 sector_addr) +{ + return erase_sector_retry(cts_dev, sector_addr, CTS_FLASH_ERASE_DEFAULT_RETRY); +} + +/** Make sure block addr is block aligned && < flash total size */ +static int erase_block_retry(const struct cts_device *cts_dev, + u32 block_addr, int retry) +{ + int ret, retries; + + cts_info(" Erase block 0x%06x", block_addr); + + retries = 0; + do { + retries++; + + ret = cts_dev->hwdata->sfctrl->ops->be(cts_dev, block_addr); + if (ret) { + cts_err("Erase block 0x%06x failed %d(%s) retries %d", + block_addr, ret, cts_strerror(ret), retries); + continue; + } + } while (retries < retry); + + return ret; +} + +/** Make sure block addr is block aligned && < flash total size */ +static inline int erase_block(const struct cts_device *cts_dev, + u32 block_addr) +{ + return erase_block_retry(cts_dev, block_addr, + CTS_FLASH_ERASE_DEFAULT_RETRY); +} + +int cts_prepare_flash_operation(struct cts_device *cts_dev) +{ + int ret; + u8 diva = 0; + bool program_mode = cts_is_device_program_mode(cts_dev); + u32 hwid; + bool enabled = cts_is_device_enabled(cts_dev); + + cts_info("Prepare for flash operation"); + + if (!program_mode) { + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Enter program mode failed %d(%s)", + ret, cts_strerror(ret)); + goto err_start_device; + } + } + + ret = cts_hw_reg_readb_retry(cts_dev, CTS_DEV_HW_REG_CLK_DIV_CFG, &diva, 5, 0); + if (ret) { + cts_warn("Read DIVA failed %d(%s)", ret, cts_strerror(ret)); + } else { + cts_dbg("Device DIVA = %d", diva); + } + + if (ret == 0 && diva == 0x0C) { + cts_info("DIVA is ready already"); + } else { + int retries; + + /* Set HCLK to 10MHz */ + hwid = cts_dev->hwdata->hwid; + if (hwid == CTS_DEV_HWID_ICNL9911 || hwid == CTS_DEV_HWID_ICNL9911C \ + || hwid == CTS_DEV_HWID_ICNL9951) { + ret = cts_hw_reg_writeb_retry(cts_dev, CTS_DEV_HW_REG_CLK_DIV_CFG, 0x0C, 5, 0); + if (ret) { + cts_err("Write DIVA failed %d(%s)", + ret, cts_strerror(ret)); + goto err_enter_normal_mode; + } + } + + /* Reset SFCTL */ + ret = cts_hw_reg_writeb_retry(cts_dev, CTS_DEV_HW_REG_RESET_CONFIG, 0xFB, 5, 0); + if (ret) { + cts_err("Reset sfctl failed %d(%s)", + ret, cts_strerror(ret)); + goto err_enter_normal_mode; + } + + retries = 0; + do { + u8 state; + + ret = cts_hw_reg_readb_relaxed(cts_dev, CTS_DEV_HW_REG_HW_STATUS, &state); + if (ret == 0 && (state & 0x40) != 0) + goto init_flash; + + mdelay(2); + } while (++retries < 1000); + + cts_warn("Wait SFCTRL ready failed %d(%s)", + ret, cts_strerror(ret)); + // Go through and try + } + +init_flash: + if (cts_dev->flash == NULL) { + cts_info("Flash is not initialized, try to probe..."); + if ((ret = probe_flash(cts_dev)) != 0) { + cts_dev->rtdata.has_flash = false; + cts_warn("Probe flash failed %d(%s)", + ret, cts_strerror(ret)); + return 0; + } + } + cts_dev->rtdata.has_flash = true; + return 0; + +err_enter_normal_mode: + if (!program_mode) { + int r = cts_enter_normal_mode(cts_dev); + if (r) { + cts_err("Enter normal mode failed %d(%s)", + r, cts_strerror(r)); + } + } +err_start_device: + if (enabled) { + int r = cts_start_device(cts_dev); + if (r) { + cts_err("Start device failed %d(%s)", + r, cts_strerror(r)); + } + } + + return ret; +} + +int cts_post_flash_operation(struct cts_device *cts_dev) +{ + u32 hwid; + cts_info("Post flash operation"); + + hwid = cts_dev->hwdata->hwid; + if (hwid == CTS_DEV_HWID_ICNL9911 || hwid == CTS_DEV_HWID_ICNL9911C \ + ||hwid == CTS_DEV_HWID_ICNL9951) { + return cts_hw_reg_writeb_retry(cts_dev, CTS_DEV_HW_REG_CLK_DIV_CFG, 4, 5, 0); + } + return 0; +} + +int cts_read_flash_retry(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t size, int retry) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret; + + cts_info("Read from 0x%06x size %zu", flash_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || + sfctrl->ops->read == NULL) { + cts_err("Read not supported"); + return -ENOTSUPP; + } + + if (flash_addr > flash->total_size) { + cts_err("Read from 0x%06x > flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Read actually from 0x%06x size %zu", flash_addr, size); + + while (size) { + size_t l; + + l = min(sfctrl->xchg_sram_size, size); + + ret = sfctrl->ops->read(cts_dev, flash_addr, dst, l); + if(ret < 0) { + cts_err("Read from 0x%06x size %zu failed %d(%s)", + flash_addr, size, ret, cts_strerror(ret)); + return ret; + } + + dst += l; + size -= l; + flash_addr += l; + } + + return 0; +} + +int cts_read_flash_to_sram_retry(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t size, int retry) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret, retries; + + cts_info("Read from 0x%06x to sram 0x%06x size %zu", + flash_addr, sram_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || + sfctrl->ops->read_to_sram == NULL) { + cts_err("Read to sram not supported"); + return -ENOTSUPP; + } + + if (flash_addr > flash->total_size) { + cts_err("Read to sram from 0x%06x > flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Read to sram actually from 0x%06x size %zu", + flash_addr, size); + + retries = 0; + do { + retries++; + ret = sfctrl->ops->read_to_sram(cts_dev, + flash_addr, sram_addr, size); + if(ret) { + cts_err("Read from 0x%06x to sram 0x%06x size %zu " + "failed %d(%s) retries %d", + flash_addr, sram_addr, size, + ret, cts_strerror(ret), retries); + continue; + } + } while (retries < retry); + + return ret; +} + +int cts_read_flash_to_sram_check_crc_retry(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t size, u32 crc, int retry) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret, retries; + + cts_info("Read from 0x%06x to sram 0x%06x size %zu with crc check", + flash_addr, sram_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || + sfctrl->ops->read_to_sram == NULL || + sfctrl->ops->calc_sram_crc == NULL) { + cts_err("Read to sram check crc not supported"); + return -ENOTSUPP; + } + + if (flash_addr > flash->total_size) { + cts_err("Read to sram from 0x%06x > flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Read to sram actually from 0x%06x size %zu with crc check", + flash_addr, size); + + retries = 0; + do { + u32 crc_sram; + + retries++; + + ret = sfctrl->ops->read_to_sram(cts_dev, + flash_addr, sram_addr, size); + if(ret) { + cts_err("Read from 0x%06x to sram 0x%06x size %zu " + "failed %d(%s) retries %d", + flash_addr, sram_addr, size, + ret, cts_strerror(ret), retries); + continue; + } + + ret = sfctrl->ops->calc_sram_crc(cts_dev, + sram_addr, size, &crc_sram); + if (ret) { + cts_err("Get crc for read " + "from 0x%06x to sram 0x%06x size %zu " + "failed %d(%s) retries %d", + flash_addr, sram_addr, size, + ret, cts_strerror(ret), retries); + continue; + } + + if (crc == crc_sram) { + return 0; + } + + cts_err("Check crc for read " + "from 0x%06x to sram 0x%06x size %zu " + "mismatch %x != %x, retries %d", + flash_addr, sram_addr, size, crc, crc_sram, retries); + ret = -EFAULT; + } while (retries < retry); + + return ret; +} + +int cts_program_flash(const struct cts_device *cts_dev, + u32 flash_addr, const void *src, size_t size) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret; + + cts_info("Program to 0x%06x size %zu", flash_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || + sfctrl->ops->program == NULL) { + cts_err("Program not supported"); + return -ENOTSUPP; + } + + if (flash_addr >= flash->total_size) { + cts_err("Program from 0x%06x >= flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Program actually to 0x%06x size %zu", flash_addr, size); + + while (size) { + size_t l, offset; + + l = min(flash->page_size, size); + offset = flash_addr & (flash->page_size - 1); + + if (offset) { + l = min(flash->page_size - offset, l); + } + + ret = sfctrl->ops->program(cts_dev, flash_addr, src, l); + if(ret) { + cts_err("Program to 0x%06x size %zu failed %d(%s)", + flash_addr, l, ret, cts_strerror(ret)); + return ret; + } + + src += l; + size -= l; + flash_addr += l; + } + + return 0; +} + +int cts_program_flash_from_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t size) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret; + + cts_info("Program to 0x%06x from sram 0x%06x size %zu", + flash_addr, sram_addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || + sfctrl->ops == NULL || + sfctrl->ops->program_from_sram == NULL) { + cts_err("Program from sram not supported"); + return -ENOTSUPP; + } + + if (flash_addr >= flash->total_size) { + cts_err("Program from 0x%06x >= flash size 0x%06zx", + flash_addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - flash_addr); + + cts_info("Program actually to 0x%06x from sram 0x%06x size %zu", + flash_addr, sram_addr, size); + + while (size) { + size_t l, offset; + + l = min(flash->page_size, size); + offset = flash_addr & (flash->page_size - 1); + + if (offset) { + l = min(flash->page_size - offset, l); + } + + ret = sfctrl->ops->program_from_sram(cts_dev, + flash_addr, sram_addr, l); + if(ret) { + cts_err("Program to 0x%06x from sram 0x%06x size %zu " + "failed %d(%s)", + flash_addr, sram_addr, l, ret, cts_strerror(ret)); + return ret; + } + + size -= l; + flash_addr += l; + sram_addr += l; + } + + return 0; +} + +int cts_erase_flash(const struct cts_device *cts_dev, u32 addr, size_t size) +{ + const struct cts_sfctrl *sfctrl; + const struct cts_flash *flash; + int ret; + + cts_info("Erase from 0x%06x size %zu", addr, size); + + sfctrl = cts_dev->hwdata->sfctrl; + flash = cts_dev->flash; + + if (flash == NULL || + sfctrl == NULL || sfctrl->ops == NULL || + sfctrl->ops->se == NULL || sfctrl->ops->be == NULL || + flash == NULL) { + cts_err("Oops"); + return -EINVAL; + } + + /* Addr and size MUST sector aligned */ + addr = rounddown(addr, flash->sector_size); + size = roundup(size, flash->sector_size); + + if (addr > flash->total_size) { + cts_err("Erase from 0x%06x > flash size 0x%06zx", + addr, flash->total_size); + return -EINVAL; + } + size = min(size, flash->total_size - addr); + + cts_info("Erase actually from 0x%06x size %zu", addr, size); + + if (flash->block_size) { + while (addr != ALIGN(addr, flash->block_size) && + size >= flash->sector_size) { + ret = erase_sector(cts_dev, addr); + if (ret) { + cts_err("Erase sector 0x%06x size 0x%04zx failed %d(%s)", + addr, flash->sector_size, ret, cts_strerror(ret)); + return ret; + } + addr += flash->sector_size; + size -= flash->sector_size; + } + + while (size >= flash->block_size) { + ret = erase_block(cts_dev, addr); + if (ret) { + cts_err("Erase block 0x%06x size 0x%04zx failed %d(%s)", + addr, flash->block_size, ret, cts_strerror(ret)); + return ret; + } + addr += flash->block_size; + size -= flash->block_size; + } + } + + while (size >= flash->sector_size) { + ret = erase_sector(cts_dev, addr); + if (ret) { + cts_err("Erase sector 0x%06x size 0x%04zx failed %d(%s)", + addr, flash->sector_size, ret, cts_strerror(ret)); + return ret; + } + addr += flash->sector_size; + size -= flash->sector_size; + } + + return 0; +} + diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_spi_flash.h b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_spi_flash.h new file mode 100644 index 000000000000..e7d18df534fc --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_spi_flash.h @@ -0,0 +1,61 @@ +#ifndef CTS_SPI_FLASH_H +#define CTS_SPI_FLASH_H + +struct cts_flash { + const char *name; + u32 jedec_id; /* Device ID by command 0x9F */ + size_t page_size; /* Page size by command 0x02 */ + size_t sector_size; /* Sector size by command 0x20 */ + +/* Block size by command 0x52, + * if 0 means block erase NOT supported + */ + size_t block_size; + size_t total_size; + +}; + +#define CTS_FLASH_READ_DEFAULT_RETRY (3) +#define CTS_FLASH_ERASE_DEFAULT_RETRY (3) + +struct cts_device; + +extern int cts_prepare_flash_operation(struct cts_device *cts_dev); +extern int cts_post_flash_operation(struct cts_device *cts_dev); + +extern int cts_read_flash_retry(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t size, int retry); +static inline int cts_read_flash(const struct cts_device *cts_dev, + u32 flash_addr, void *dst, size_t size) +{ + return cts_read_flash_retry(cts_dev, flash_addr, dst, size, + CTS_FLASH_READ_DEFAULT_RETRY); +} + +extern int cts_read_flash_to_sram_retry(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t size, int retry); +static inline int cts_read_flash_to_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t size) +{ + return cts_read_flash_to_sram_retry(cts_dev, + flash_addr, sram_addr, size, CTS_FLASH_READ_DEFAULT_RETRY); +} + +extern int cts_read_flash_to_sram_check_crc_retry(const struct cts_device + *cts_dev, u32 flash_addr, u32 sram_addr, size_t size, u32 crc, int retry); +static inline int cts_read_flash_to_sram_check_crc(const struct cts_device + *cts_dev, u32 flash_addr, u32 sram_addr, size_t size, u32 crc) +{ + return cts_read_flash_to_sram_check_crc_retry(cts_dev, + flash_addr, sram_addr, size, crc, CTS_FLASH_READ_DEFAULT_RETRY); +} + +extern int cts_erase_flash(const struct cts_device *cts_dev, u32 addr, + size_t size); + +extern int cts_program_flash(const struct cts_device *cts_dev, + u32 flash_addr, const void *src, size_t size); +extern int cts_program_flash_from_sram(const struct cts_device *cts_dev, + u32 flash_addr, u32 sram_addr, size_t size); + +#endif /* CTS_SPI_FLASH_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_strerror.c b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_strerror.c new file mode 100644 index 000000000000..a4de04a90a07 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_strerror.c @@ -0,0 +1,172 @@ +#include +#include + +#define ERROR_TEXT_ENTRY(errno, err_desc_str) \ + [(errno)] = #errno ", " err_desc_str + +static const char *const _error_text[] = { + [0] = "Success", + ERROR_TEXT_ENTRY(EPERM, "Operation not permitted"), + ERROR_TEXT_ENTRY(ENOENT, "No such file or directory"), + ERROR_TEXT_ENTRY(ESRCH, "No such process"), + ERROR_TEXT_ENTRY(EINTR, "Interrupted system call"), + ERROR_TEXT_ENTRY(EIO, "Input/output error"), + ERROR_TEXT_ENTRY(ENXIO, "No such device or address"), + ERROR_TEXT_ENTRY(E2BIG, "Argument list too long"), + ERROR_TEXT_ENTRY(ENOEXEC, "Exec format error"), + ERROR_TEXT_ENTRY(EBADF, "Bad file descriptor"), + ERROR_TEXT_ENTRY(ECHILD, "No child processes"), + ERROR_TEXT_ENTRY(EAGAIN, "Resource temporarily unavailable"), + ERROR_TEXT_ENTRY(ENOMEM, "Cannot allocate memory"), + ERROR_TEXT_ENTRY(EACCES, "Permission denied"), + ERROR_TEXT_ENTRY(EFAULT, "Bad address"), + ERROR_TEXT_ENTRY(ENOTBLK, "Block device required"), + ERROR_TEXT_ENTRY(EBUSY, "Device or resource busy"), + ERROR_TEXT_ENTRY(EEXIST, "File exists"), + ERROR_TEXT_ENTRY(EXDEV, "Invalid cross-device link"), + ERROR_TEXT_ENTRY(ENODEV, "No such device"), + ERROR_TEXT_ENTRY(ENOTDIR, "Not a directory"), + ERROR_TEXT_ENTRY(EISDIR, "Is a directory"), + ERROR_TEXT_ENTRY(EINVAL, "Invalid argument"), + ERROR_TEXT_ENTRY(EMFILE, "Too many open files"), + ERROR_TEXT_ENTRY(ENFILE, "Too many open files in system"), + ERROR_TEXT_ENTRY(ENOTTY, "Inappropriate ioctl for device"), + ERROR_TEXT_ENTRY(ETXTBSY, "Text file busy"), + ERROR_TEXT_ENTRY(EFBIG, "File too large"), + ERROR_TEXT_ENTRY(ENOSPC, "No space left on device"), + ERROR_TEXT_ENTRY(ESPIPE, "Illegal seek"), + ERROR_TEXT_ENTRY(EROFS, "Read-only file system"), + ERROR_TEXT_ENTRY(EMLINK, "Too many links"), + ERROR_TEXT_ENTRY(EPIPE, "Broken pipe"), + ERROR_TEXT_ENTRY(EDOM, "Numerical argument out of domain"), + ERROR_TEXT_ENTRY(ERANGE, "Numerical result out of range"), + ERROR_TEXT_ENTRY(EDEADLK, "Resource deadlock avoided"), + ERROR_TEXT_ENTRY(ENAMETOOLONG, "File name too long"), + ERROR_TEXT_ENTRY(ENOLCK, "No locks available"), + ERROR_TEXT_ENTRY(ENOSYS, "Function not implemented"), + ERROR_TEXT_ENTRY(ENOTEMPTY, "Directory not empty"), + ERROR_TEXT_ENTRY(ELOOP, "Too many levels of symbolic links"), + ERROR_TEXT_ENTRY(EWOULDBLOCK, "Operation would block"), + ERROR_TEXT_ENTRY(ENOMSG, "No message of desired type"), + ERROR_TEXT_ENTRY(EIDRM, "Identifier removed"), + ERROR_TEXT_ENTRY(ECHRNG, "Channel number out of range"), + ERROR_TEXT_ENTRY(EL2NSYNC, "Level 2 not synchronized"), + ERROR_TEXT_ENTRY(EL3HLT, "Level 3 halted"), + ERROR_TEXT_ENTRY(EL3RST, "Level 3 reset"), + ERROR_TEXT_ENTRY(ELNRNG, "Link number out of range"), + ERROR_TEXT_ENTRY(EUNATCH, "Protocol driver not attached"), + ERROR_TEXT_ENTRY(ENOCSI, "No CSI structure available"), + ERROR_TEXT_ENTRY(EL2HLT, "Level 2 halted"), + ERROR_TEXT_ENTRY(EBADE, "Invalid exchange"), + ERROR_TEXT_ENTRY(EBADR, "Invalid request descriptor"), + ERROR_TEXT_ENTRY(EXFULL, "Exchange full"), + ERROR_TEXT_ENTRY(ENOANO, "No anode"), + ERROR_TEXT_ENTRY(EBADRQC, "Invalid request code"), + ERROR_TEXT_ENTRY(EBADSLT, "Invalid slot"), + ERROR_TEXT_ENTRY(EDEADLOCK, "File locking deadlock error"), + ERROR_TEXT_ENTRY(EBFONT, "Bad font file format"), + ERROR_TEXT_ENTRY(ENOSTR, "Device not a stream"), + ERROR_TEXT_ENTRY(ENODATA, "No data available"), + ERROR_TEXT_ENTRY(ETIME, "Timer expired"), + ERROR_TEXT_ENTRY(ENOSR, "Out of streams resources"), + ERROR_TEXT_ENTRY(ENONET, "Machine is not on the network"), + ERROR_TEXT_ENTRY(ENOPKG, "Package not installed"), + ERROR_TEXT_ENTRY(EREMOTE, "Object is remote"), + ERROR_TEXT_ENTRY(ENOLINK, "Link has been severed"), + ERROR_TEXT_ENTRY(EADV, "Advertise error"), + ERROR_TEXT_ENTRY(ESRMNT, "Srmount error"), + ERROR_TEXT_ENTRY(ECOMM, "Communication error on send"), + ERROR_TEXT_ENTRY(EPROTO, "Protocol error"), + ERROR_TEXT_ENTRY(EMULTIHOP, "Multihop attempted"), + ERROR_TEXT_ENTRY(EDOTDOT, "RFS specific error"), + ERROR_TEXT_ENTRY(EBADMSG, "Bad message"), + ERROR_TEXT_ENTRY(EOVERFLOW, "Value too large for defined data type"), + ERROR_TEXT_ENTRY(ENOTUNIQ, "Name not unique on network"), + ERROR_TEXT_ENTRY(EBADFD, "File descriptor in bad state"), + ERROR_TEXT_ENTRY(EREMCHG, "Remote address changed"), + ERROR_TEXT_ENTRY(ELIBACC, "Can not access a needed shared library"), + ERROR_TEXT_ENTRY(ELIBBAD, "Accessing a corrupted shared library"), + ERROR_TEXT_ENTRY(ELIBSCN, ".lib section in a.out corrupted"), + ERROR_TEXT_ENTRY(ELIBMAX, "Attempting to link in too many shared libraries"), + ERROR_TEXT_ENTRY(ELIBEXEC, "Cannot exec a shared library directly"), + ERROR_TEXT_ENTRY(EILSEQ, "Invalid or incomplete multibyte or wide character"), + ERROR_TEXT_ENTRY(ERESTART, "Interrupted system call should be restarted"), + ERROR_TEXT_ENTRY(ESTRPIPE, "Streams pipe error"), + ERROR_TEXT_ENTRY(EUSERS, "Too many users"), + ERROR_TEXT_ENTRY(ENOTSOCK, "Socket operation on non-socket"), + ERROR_TEXT_ENTRY(EDESTADDRREQ, "Destination address required"), + ERROR_TEXT_ENTRY(EMSGSIZE, "Message too long"), + ERROR_TEXT_ENTRY(EPROTOTYPE, "Protocol wrong type for socket"), + ERROR_TEXT_ENTRY(ENOPROTOOPT, "Protocol not available"), + ERROR_TEXT_ENTRY(EPROTONOSUPPORT, "Protocol not supported"), + ERROR_TEXT_ENTRY(ESOCKTNOSUPPORT, "Socket type not supported"), + ERROR_TEXT_ENTRY(EOPNOTSUPP, "Operation not supported"), + ERROR_TEXT_ENTRY(EPFNOSUPPORT, "Protocol family not supported"), + ERROR_TEXT_ENTRY(EAFNOSUPPORT, "Address family not supported by protocol"), + ERROR_TEXT_ENTRY(EADDRINUSE, "Address already in use"), + ERROR_TEXT_ENTRY(EADDRNOTAVAIL, "Cannot assign requested address"), + ERROR_TEXT_ENTRY(ENETDOWN, "Network is down"), + ERROR_TEXT_ENTRY(ENETUNREACH, "Network is unreachable"), + ERROR_TEXT_ENTRY(ENETRESET, "Network dropped connection on reset"), + ERROR_TEXT_ENTRY(ECONNABORTED, "Software caused connection abort"), + ERROR_TEXT_ENTRY(ECONNRESET, "Connection reset by peer"), + ERROR_TEXT_ENTRY(ENOBUFS, "No buffer space available"), + ERROR_TEXT_ENTRY(EISCONN, "Transport endpoint is already connected"), + ERROR_TEXT_ENTRY(ENOTCONN, "Transport endpoint is not connected"), + ERROR_TEXT_ENTRY(ESHUTDOWN, "Cannot send after transport endpoint shutdown"), + ERROR_TEXT_ENTRY(ETOOMANYREFS, "Too many references: cannot splice"), + ERROR_TEXT_ENTRY(ETIMEDOUT, "Connection timed out"), + ERROR_TEXT_ENTRY(ECONNREFUSED, "Connection refused"), + ERROR_TEXT_ENTRY(EHOSTDOWN, "Host is down"), + ERROR_TEXT_ENTRY(EHOSTUNREACH, "No route to host"), + ERROR_TEXT_ENTRY(EALREADY, "Operation already in progress"), + ERROR_TEXT_ENTRY(EINPROGRESS, "Operation now in progress"), + ERROR_TEXT_ENTRY(ESTALE, "Stale file handle"), + ERROR_TEXT_ENTRY(EUCLEAN, "Structure needs cleaning"), + ERROR_TEXT_ENTRY(ENOTNAM, "Not a XENIX named type file"), + ERROR_TEXT_ENTRY(ENAVAIL, "No XENIX semaphores available"), + ERROR_TEXT_ENTRY(EISNAM, "Is a named type file"), + ERROR_TEXT_ENTRY(EREMOTEIO, "Remote I/O error"), + ERROR_TEXT_ENTRY(EDQUOT, "Disk quota exceeded"), + ERROR_TEXT_ENTRY(ENOMEDIUM, "No medium found"), + ERROR_TEXT_ENTRY(EMEDIUMTYPE, "Wrong medium type"), + ERROR_TEXT_ENTRY(ECANCELED, "Operation canceled"), + ERROR_TEXT_ENTRY(ENOKEY, "Required key not available"), + ERROR_TEXT_ENTRY(EKEYEXPIRED, "Key has expired"), + ERROR_TEXT_ENTRY(EKEYREVOKED, "Key has been revoked"), + ERROR_TEXT_ENTRY(EKEYREJECTED, "Key was rejected by service"), + ERROR_TEXT_ENTRY(EOWNERDEAD, "Owner died"), + ERROR_TEXT_ENTRY(ENOTRECOVERABLE, "State not recoverable"), + ERROR_TEXT_ENTRY(ERFKILL, "Operation not possible due to RF-kill"), + ERROR_TEXT_ENTRY(EHWPOISON, "Memory page has hardware error"), + ERROR_TEXT_ENTRY(ERESTARTSYS, "Restart system"), + ERROR_TEXT_ENTRY(ERESTARTNOINTR, "Restart monitor"), + ERROR_TEXT_ENTRY(ERESTARTNOHAND, "Restart if no handler"), + ERROR_TEXT_ENTRY(ENOIOCTLCMD, "No ioctl command"), + ERROR_TEXT_ENTRY(ERESTART_RESTARTBLOCK, "Restart by calling sys_restart_syscall"), + ERROR_TEXT_ENTRY(EPROBE_DEFER, "Driver requests probe retry"), + ERROR_TEXT_ENTRY(EOPENSTALE, "Open found a stale dentry"), + ERROR_TEXT_ENTRY(EBADHANDLE, "Illegal NFS file handle"), + ERROR_TEXT_ENTRY(ENOTSYNC, "Update synchronization mismatch"), + ERROR_TEXT_ENTRY(EBADCOOKIE, "Cookie is stale"), + ERROR_TEXT_ENTRY(ENOTSUPP, "Operation is not supported"), + ERROR_TEXT_ENTRY(ETOOSMALL, "Buffer or request is too small"), + ERROR_TEXT_ENTRY(ESERVERFAULT, "An untranslatable error occurred"), + ERROR_TEXT_ENTRY(EBADTYPE, "Type not supported by server"), + ERROR_TEXT_ENTRY(EJUKEBOX, "Request initiated, but will not complete before timeout"), + ERROR_TEXT_ENTRY(EIOCBQUEUED, "IOCB queued, will get completion event"), +}; + +#define ERROR_TEXT_ARRAY_SIZE (sizeof(_error_text) / sizeof(_error_text[0])) +const char *cts_strerror(int errno) +{ + if (errno > 0) { + return ""; + } else if (-errno < (int) ERROR_TEXT_ARRAY_SIZE) { + const char *s = _error_text[-errno]; + if (s) { + return s; + } + } + return ""; +} diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_strerror.h b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_strerror.h new file mode 100644 index 000000000000..361c9db003cf --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_strerror.h @@ -0,0 +1,9 @@ +#ifndef CTS_STRERROR_H +#define CTS_STRERROR_H + +#define CTS_ERR_FMT_STR "%d(%s)" +#define CTS_ERR_ARG(errno) cts_strerror(errno) +const char *cts_strerror(int errno); + +#endif + diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_sysfs.c b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_sysfs.c new file mode 100644 index 000000000000..312d746d40ba --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_sysfs.c @@ -0,0 +1,3454 @@ +#define LOG_TAG "Sysfs" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_tcs.h" +#include "cts_test.h" +#include "cts_sfctrl.h" +#include "cts_spi_flash.h" +#include "cts_firmware.h" +#include "cts_strerror.h" + +#ifdef CONFIG_CTS_SYSFS + +#define SPLIT_LINE_STR \ + "-----------------------------------------------"\ + "------------------------------------------------\n" +#define ROW_NUM_FORMAT_STR "%2d | " +#define COL_NUM_FORMAT_STR "%4u " +#define DATA_FORMAT_STR "%5d" + +#define RAWDATA_BUFFER_SIZE(cts_dev) \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * 2) + +#define MAX_ARG_NUM (100) +#define MAX_ARG_LENGTH (1024) +#define HW_STUB_ADDR (0XF000) + +static char cmdline_param[MAX_ARG_LENGTH + 1]; +int argc; +char *argv[MAX_ARG_NUM]; + +static int jitter_test_frame = 10; + + +int parse_arg(const char *buf, size_t count) +{ + char *p; + + memcpy(cmdline_param, buf, min((size_t)MAX_ARG_LENGTH, count)); + cmdline_param[count] = '\0'; + + argc = 0; + p = strim(cmdline_param); + if (p == NULL || p[0] == '\0') + return 0; + + while (p && p[0] != '\0' && argc < MAX_ARG_NUM) + argv[argc++] = strsep(&p, " ,"); + + return argc; +} + + +#ifdef CFG_CTS_FW_UPDATE_SYS +static ssize_t cts_panel_supplier_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *data = dev_get_drvdata(dev); + + if (data->pdata && data->pdata->panel_supplier) { + return scnprintf(buf, PAGE_SIZE, "%s\n", + data->pdata->panel_supplier); + } + return 0; +} + +static ssize_t buildid_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "0000-%04x\n", + cts_data->cts_dev.fwdata.version); +} + +static ssize_t forcereflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 0) + return -EINVAL; + + cts_data->force_reflash = (input == 0) ? false : true; + + cts_info("%s force_reflash=%d, count=%zu", __func__, + (cts_data->force_reflash ? 1 : 0), count); + return count; +} + +static ssize_t flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + cts_is_firmware_updating(&cts_data->cts_dev) ? 1 : 0); +} + +static bool is_reflash_filename_valid(const struct chipone_ts_data *cts_data, + const char *filename) +{ + char prefix[CFG_CTS_FW_FILE_NAME_MAX_LEN]; + + if (cts_data->pdata->panel_supplier != NULL) { + snprintf(prefix, sizeof(prefix), "%s-%s-%s-", + CFG_CTS_FW_FILE_NAME_VENDOR, + cts_data->pdata->panel_supplier, + cts_data->cts_dev.hwdata->name); + } else { + /* panel supplier not set, just check vendor. */ + snprintf(prefix, sizeof(prefix), "%s", + CFG_CTS_FW_FILE_NAME_VENDOR); + } + + cts_info("%s: prefix=%s", __func__, prefix); + if (strncmp(filename, prefix, strlen(prefix))) { + cts_err("%s: invalid FW file.", __func__); + return false; + } + + return true; +} + +static ssize_t doreflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + int ret; + + cts_info("doreflash FW filename: %s len: %zu", buf, count); + + if (count > CFG_CTS_FW_FILE_NAME_MAX_LEN) { + cts_err("doreflash FW filename is too long %zu > %d", + count, CFG_CTS_FW_FILE_NAME_MAX_LEN); + return -EINVAL; + } + + if (cts_is_device_suspended(&cts_data->cts_dev)) { + cts_err("In suspend state, try again later"); + return -EAGAIN; + } + + if (cts_is_firmware_updating(&cts_data->cts_dev)) { + cts_err("In FW flashing state, try again later"); + return -EAGAIN; + } + + if (!cts_data->force_reflash) { + /* Check filename if force_reflash is false */ + if (!is_reflash_filename_valid(cts_data, buf)) { + cts_err("Invalid firmware filename '%*.s'", (int)count, buf); + return -EINVAL; + } + } + + strncpy(cts_data->cts_dev.config_fw_name, buf, count); + + /* If use echo xxx > doreflash, 0x0A will append to the string, + * if use echo -n xxx > doreflash, nothing will append. + */ + if (cts_data->cts_dev.config_fw_name[count - 1] == '\n') + cts_data->cts_dev.config_fw_name[count - 1] = '\0'; + else + cts_data->cts_dev.config_fw_name[count] = '\0'; + + cts_stop_device(&cts_data->cts_dev); + + cts_lock_device(&cts_data->cts_dev); + ret = cts_update_firmware_from_file(&cts_data->cts_dev, + cts_data->cts_dev.config_fw_name, false); + cts_unlock_device(&cts_data->cts_dev); + + if (ret) + cts_err("Update firmware from file '%s' failed %d", + cts_data->cts_dev.config_fw_name, ret); + + cts_start_device(&cts_data->cts_dev); + + cts_data->force_reflash = false; + + cts_info("%s: end", __func__); + + return ret ? ret : count; +} + +static ssize_t cts_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + bool val; + + /* TBD: check if cts is power to ready for flash. + * set "1" if power on ready. + */ + val = cts_is_device_suspended(&cts_data->cts_dev); + return scnprintf(buf, PAGE_SIZE, "%d\n", val == false); +} + +static ssize_t cts_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + /* set chip IC type to productinfo */ + return scnprintf(buf, PAGE_SIZE, "%s\n", + cts_data->cts_dev.hwdata->name); +} + +/* add sys entries for FW update */ +/* static DEVICE_ATTR(drv_irq, S_IRUGO | S_IWUSR, drv_irq_show, drv_irq_store); */ +/* static DEVICE_ATTR(reset, S_IWUSR | S_IWGRP, NULL, reset_store); */ +static DEVICE_ATTR(panel_supplier, 0444, cts_panel_supplier_show, NULL); +static DEVICE_ATTR(buildid, S_IRUGO, buildid_show, NULL); +static DEVICE_ATTR(forcereflash, S_IWUSR | S_IWGRP, NULL, forcereflash_store); +static DEVICE_ATTR(flashprog, S_IRUGO, flashprog_show, NULL); +static DEVICE_ATTR(doreflash, S_IWUSR | S_IWGRP, NULL, doreflash_store); +static DEVICE_ATTR(poweron, S_IRUGO, cts_poweron_show, NULL); +static DEVICE_ATTR(productinfo, S_IRUGO, cts_productinfo_show, NULL); + +static struct attribute *cts_dev_fw_up_atts[] = { + /** + * &dev_attr_drv_irq.attr, + * &dev_attr_reset.attr, + */ + &dev_attr_buildid.attr, + &dev_attr_forcereflash.attr, + &dev_attr_flashprog.attr, + &dev_attr_doreflash.attr, + &dev_attr_poweron.attr, + &dev_attr_productinfo.attr, + &dev_attr_panel_supplier.attr, + NULL +}; + +static const struct attribute_group cts_dev_fw_up_attr_group = { + .attrs = cts_dev_fw_up_atts, +}; +#endif + + +static ssize_t write_tcs_register_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 addr; + int i, ret; + u8 *data = NULL; + + parse_arg(buf, count); + + cts_info("Write firmware register '%.*s'", (int)count, buf); + + if (argc < 2) { + cts_err("Too few args %d", argc); + return -EFAULT; + } + + ret = kstrtou16(argv[0], 0, &addr); + if (ret) { + cts_err("Invalid address %s", argv[0]); + return -EINVAL; + } + + data = (u8 *) kmalloc(argc - 1, GFP_KERNEL); + if (data == NULL) { + cts_err("Allocate buffer for write data failed\n"); + return -ENOMEM; + } + + for (i = 1; i < argc; i++) { + ret = kstrtou8(argv[i], 0, data + i - 1); + if (ret) { + cts_err("Invalid value %s", argv[i]); + goto free_data; + } + } + + ret = cts_tcs_write(cts_dev, addr, data, argc - 1); + if (ret) { + cts_err("Write tcs register addr: 0x%04x size: %d failed", + addr, argc - 1); + goto free_data; + } + +free_data: + kfree(data); + + return (ret < 0 ? ret : count); +} +static DEVICE_ATTR(write_tcs_reg, S_IWUSR, NULL, write_tcs_register_store); + +static ssize_t read_tcs_register_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#define PRINT_ROW_SIZE (16) + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 addr, size, i, remaining; + u8 *data = NULL; + ssize_t count = 0; + int ret; + + cts_info("Read tcs register "); + + if (argc != 2) { + return snprintf(buf, PAGE_SIZE, + "Invalid num args %d\n" + " 1. echo (0x)addr size > read_reg\n" + " 2. cat read_reg\n", argc); + } + + ret = kstrtou16(argv[0], 0, &addr); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid address: %s\n", argv[0]); + } + ret = kstrtou16(argv[1], 0, &size); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid size: %s\n", argv[1]); + + data = (u8 *) kmalloc(size, GFP_KERNEL); + if (data == NULL) { + return snprintf(buf, PAGE_SIZE, "Allocate buffer for read data failed\n"); + } + + cts_info("Read tcs register from 0x%04x size %u", addr, size); + cts_lock_device(cts_dev); + ret = cts_tcs_read(cts_dev, addr, data, (size_t)size); + cts_unlock_device(cts_dev); + if (ret) { + count = snprintf(buf, PAGE_SIZE, + "Read tcs register from 0x%04x size %u failed %d\n", addr, size, ret); + goto err_free_data; + } + + remaining = size; + for (i = 0; i < size && count <= PAGE_SIZE; i += PRINT_ROW_SIZE) { + size_t linelen = min((size_t)remaining, (size_t)PRINT_ROW_SIZE); + + remaining -= PRINT_ROW_SIZE; + + count += snprintf(buf + count, PAGE_SIZE - count, "%04x: ", addr); + + /* Lower version kernel return void */ + hex_dump_to_buffer(data + i, linelen, PRINT_ROW_SIZE, 1, + buf + count, PAGE_SIZE - count, true); + count += strlen(buf + count); + + if (count < PAGE_SIZE) { + buf[count++] = '\n'; + addr += PRINT_ROW_SIZE; + } else { + break; + } + } + +err_free_data: + kfree(data); + + return count; +#undef PRINT_ROW_SIZE +} + +static ssize_t read_tcs_register_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + parse_arg(buf, count); + + return (argc == 0 ? 0 : count); +} + +static DEVICE_ATTR(read_tcs_reg, S_IWUSR | S_IRUSR, + read_tcs_register_show, read_tcs_register_store); + +static ssize_t read_hw_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#define PRINT_ROW_SIZE (16) + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u32 addr, size, i, remaining; + u8 *data = NULL; + ssize_t count = 0; + int ret; + + cts_info("Read hw register"); + + if (argc != 2) { + return snprintf(buf, PAGE_SIZE, + "Invalid num args %d\n" + " 1. echo addr size > read_hw_reg\n" + " 2. cat read_hw_reg\n", argc); + } + + ret = kstrtou32(argv[0], 0, &addr); + if (ret) { + return snprintf(buf, PAGE_SIZE, "Invalid address: %s\n", argv[0]); + } + ret = kstrtou32(argv[1], 0, &size); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid size: %s\n", argv[1]); + + data = (u8 *) kmalloc(size, GFP_KERNEL); + if (data == NULL) + return snprintf(buf, PAGE_SIZE, "Allocate buffer for read data failed\n"); + + cts_info("Read hw register from 0x%04x size %u", addr, size); + cts_lock_device(cts_dev); + + if (cts_dev->rtdata.program_mode) { + for (i = 0; i < size; i++) { + ret = cts_dev_readb(cts_dev, addr + i, data + i, 3, 10); + if (ret) { + count = snprintf(buf, PAGE_SIZE, "Write hw register error\n"); + goto err_free_data; + } + } + } else { + ret = cts_tcs_read_hw_reg(cts_dev, addr, data, size); + if (ret < 0) { + count = snprintf(buf, PAGE_SIZE, "Read hw register error\n"); + goto err_free_data; + } + } + + remaining = size; + for (i = 0; i < size && count <= PAGE_SIZE; i += PRINT_ROW_SIZE) { + size_t linelen = min((size_t)remaining, (size_t)PRINT_ROW_SIZE); + + remaining -= PRINT_ROW_SIZE; + + count += snprintf(buf + count, PAGE_SIZE - count, "%04x: ", addr); + + /* Lower version kernel return void */ + hex_dump_to_buffer(data + i, linelen, PRINT_ROW_SIZE, 1, + buf + count, PAGE_SIZE - count, true); + count += strlen(buf + count); + + if (count < PAGE_SIZE) { + buf[count++] = '\n'; + addr += PRINT_ROW_SIZE; + } else { + break; + } + } + +err_free_data: + cts_unlock_device(cts_dev); + kfree(data); + + return count; +#undef PRINT_ROW_SIZE +} + +/* echo addr size > read_reg */ +static ssize_t read_hw_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + parse_arg(buf, count); + + return (argc == 0 ? 0 : count); +} + +static DEVICE_ATTR(read_hw_reg, S_IRUSR | S_IWUSR, read_hw_reg_show, + read_hw_reg_store); + +static ssize_t write_hw_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u32 addr; + int i, ret; + u8 *data = NULL; + + parse_arg(buf, count); + + cts_info("Write hw register"); + + if (argc < 2) { + cts_err("Too few args %d", argc); + return -EFAULT; + } + + ret = kstrtou32(argv[0], 0, &addr); + if (ret) { + cts_err("Invalid address %s", argv[0]); + return -EINVAL; + } + + data = (u8 *) kmalloc(argc - 1, GFP_KERNEL); + if (data == NULL) { + cts_err("Allocate buffer for write data failed\n"); + return -ENOMEM; + } + + for (i = 1; i < argc; i++) { + ret = kstrtou8(argv[i], 0, data + i - 1); + if (ret) { + cts_err("Invalid value %s", argv[i]); + goto free_data; + } + } + + cts_lock_device(cts_dev); + + if (cts_dev->rtdata.program_mode) { + for (i = 0; i < argc - 1; i++) { + ret = cts_dev_writeb(cts_dev, addr + i, data[i], 3, 10); + if (ret) { + cts_err("Write hw register error"); + break; + } + } + } else { + ret = cts_tcs_write_hw_reg(cts_dev, addr, data, argc - 1); + if (ret < 0) + cts_err("Write hw register error"); + } + + cts_unlock_device(cts_dev); +free_data: + kfree(data); + + return (ret < 0 ? ret : count); +} + +static DEVICE_ATTR(write_hw_reg, S_IWUSR, NULL, write_hw_reg_store); + +static ssize_t curr_firmware_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Current firmware version: %04x\n", + cts_data->cts_dev.fwdata.version); +} + +static DEVICE_ATTR(curr_version, S_IRUGO, curr_firmware_version_show, NULL); + +static ssize_t curr_ddi_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Current ddi version: %02x\n", + cts_data->cts_dev.fwdata.ddi_version); +} + +static DEVICE_ATTR(curr_ddi_version, S_IRUGO, curr_ddi_version_show, NULL); + +static ssize_t rows_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Num rows: %u\n", + cts_data->cts_dev.fwdata.rows); +} + +static DEVICE_ATTR(rows, S_IRUGO, rows_show, NULL); + +static ssize_t cols_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Num cols: %u\n", + cts_data->cts_dev.fwdata.cols); +} + +static DEVICE_ATTR(cols, S_IRUGO, cols_show, NULL); + +static ssize_t res_x_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "X Resolution: %u\n", + cts_data->cts_dev.fwdata.res_x); +} + +static DEVICE_ATTR(res_x, S_IRUGO, res_x_show, NULL); + +static ssize_t res_y_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Y Resolution: %u\n", + cts_data->cts_dev.fwdata.res_y); +} + +static DEVICE_ATTR(res_y, S_IRUGO, res_y_show, NULL); + +static ssize_t esd_protection_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + u8 esd_protection; + + cts_lock_device(cts_dev); + ret = cts_tcs_get_esd_protection(cts_dev, &esd_protection); + cts_unlock_device(cts_dev); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Read firmware ESD protection register failed %d\n", ret); + + return snprintf(buf, PAGE_SIZE, "ESD protection: %u\n", esd_protection); +} + +static DEVICE_ATTR(esd_protection, S_IRUGO, esd_protection_show, NULL); + +static ssize_t monitor_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + u8 value; + + cts_lock_device(cts_dev); + ret = cts_tcs_is_mnt_enabled(cts_dev, &value); + cts_unlock_device(cts_dev); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Read firmware monitor enable register failed %d\n", ret); + + return snprintf(buf, PAGE_SIZE, "Monitor mode: %s\n", + value & BIT(0) ? "Enable" : "Disable"); +} + +static ssize_t monitor_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + u8 value, enable = 0; + + if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') + enable = 1; + + cts_info("Write firmware monitor mode to '%c', %s", + buf[0], enable ? "Enable" : "Disable"); + + cts_lock_device(cts_dev); + ret = cts_fw_reg_readb(&cts_data->cts_dev, 0x8000 + 344, &value); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("Write firmware monitor enable register failed %d", ret); + return -EIO; + } + + if ((value & BIT(0)) && enable) + cts_info("Monitor mode already enabled"); + else if ((value & BIT(0)) == 0 && enable == 0) + cts_info("Monitor mode already disabled"); + else { + if (enable) + value |= BIT(0); + else + value &= ~BIT(0); + + cts_lock_device(cts_dev); + ret = cts_fw_reg_writeb(&cts_data->cts_dev, 0x8000 + 344, value); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("Write firmware monitor enable register failed %d", ret); + return -EIO; + } + } + + return count; +} + +static DEVICE_ATTR(monitor_mode, S_IRUGO, monitor_mode_show, + monitor_mode_store); + +static ssize_t auto_compensate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int ret; + u8 value; + + cts_lock_device(cts_dev); + ret = cts_tcs_is_cneg_enabled(&cts_data->cts_dev, &value); + cts_unlock_device(cts_dev); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Read auto compensate enable register failed %d\n", ret); + + return snprintf(buf, PAGE_SIZE, "Auto compensate: %s\n", + value ? "Enable" : "Disable"); +} + +static DEVICE_ATTR(auto_compensate, S_IRUGO, auto_compensate_show, NULL); + +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE +static ssize_t driver_builtin_firmware_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i, count = 0; + + count += snprintf(buf + count, PAGE_SIZE - count, + "Total %d builtin firmware:\n", cts_get_num_driver_builtin_firmware()); + + for (i = 0; i < cts_get_num_driver_builtin_firmware(); i++) { + const struct cts_firmware *firmware = + cts_request_driver_builtin_firmware_by_index(i); + if (firmware) + count += snprintf(buf + count, PAGE_SIZE - count, + "%-2d: hwid: %04x fwid: %04x ver: %04x size: %6zu desc: %s\n", + i, firmware->hwid, firmware->fwid, + FIRMWARE_VERSION(firmware), + firmware->size, firmware->name); + else + count += snprintf(buf + count, PAGE_SIZE - count, + "%-2d: INVALID\n", i); + } + + return count; +} + +/* echo index/name [flash/sram] > driver_builtin_firmware */ +static ssize_t driver_builtin_firmware_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + const struct cts_firmware *firmware; + bool to_flash = true; + int ret, index = -1; + + parse_arg(buf, count); + + if (argc != 1 && argc != 2) { + cts_err("Invalid num args %d\n" + " echo index/name [flash/sram] > driver_builtin_firmware\n", + argc); + return -EFAULT; + } + + if (isdigit(*argv[0])) + index = simple_strtoul(argv[0], NULL, 0); + + if (argc > 1) { + if (strncasecmp(argv[1], "flash", 5) == 0) + to_flash = true; + else if (strncasecmp(argv[1], "sram", 4) == 0) + to_flash = false; + else { + cts_err("Invalid location '%s', must be 'flash' or 'sram'", argv[1]); + return -EINVAL; + } + } + + cts_info("Update driver builtin firmware '%s' to %s", + argv[1], to_flash ? "flash" : "sram"); + + if (index >= 0 && index < cts_get_num_driver_builtin_firmware()) + firmware = cts_request_driver_builtin_firmware_by_index(index); + else + firmware = cts_request_driver_builtin_firmware_by_name(argv[0]); + + if (firmware) { + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + return ret; + } + + cts_lock_device(cts_dev); + ret = cts_update_firmware(cts_dev, firmware, to_flash); + cts_unlock_device(cts_dev); + + if (ret) { + cts_err("Update firmware failed %d", ret); + goto err_start_device; + } + + ret = cts_start_device(cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + return ret; + } + } else { + cts_err("Firmware '%s' NOT found", argv[0]); + return -ENOENT; + } + + return count; + +err_start_device: + cts_start_device(cts_dev); + + return ret; +} + +static DEVICE_ATTR(driver_builtin_firmware, S_IWUSR | S_IRUGO, + driver_builtin_firmware_show, driver_builtin_firmware_store); +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ + +#ifdef CFG_CTS_FIRMWARE_IN_FS +/* echo filepath [flash/sram] > update_firmware_from_file */ +static ssize_t update_firmware_from_file_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + const struct cts_firmware *firmware; + bool to_flash = true; + int ret; + + parse_arg(buf, count); + + if (argc > 2) { + cts_err("Invalid num args %d\n" + " echo filepath [flash/sram] > update_from_file\n", + argc); + return -EFAULT; + } else if (argc > 1) { + if (strncasecmp(argv[1], "flash", 5) == 0) + to_flash = true; + else if (strncasecmp(argv[1], "sram", 4) == 0) + to_flash = false; + else { + cts_err("Invalid location '%s', must be 'flash' or 'sram'", argv[1]); + return -EINVAL; + } + } + + cts_info("Update firmware from file '%s'", argv[0]); + + firmware = cts_request_firmware_from_fs(cts_dev, argv[0]); + if (firmware == NULL) { + cts_err("Request firmware from file '%s' failed", argv[0]); + return -ENOENT; + } + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto err_release_firmware; + } + + cts_lock_device(cts_dev); + ret = cts_update_firmware(cts_dev, firmware, to_flash); + cts_unlock_device(cts_dev); + + if (ret) { + cts_err("Update firmware failed %d", ret); + goto err_release_firmware; + } + + ret = cts_start_device(cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + goto err_release_firmware; + } + + ret = count; + +err_release_firmware: + cts_release_firmware(firmware); + + return ret; +} + +static DEVICE_ATTR(update_from_file, S_IWUSR, NULL, + update_firmware_from_file_store); +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + +static ssize_t updating_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Updating: %s\n", + cts_data->cts_dev.rtdata.updating ? "Y" : "N"); +} + +static DEVICE_ATTR(updating, S_IRUGO, updating_show, NULL); + +static struct attribute *cts_dev_firmware_atts[] = { + &dev_attr_curr_version.attr, + &dev_attr_curr_ddi_version.attr, + &dev_attr_rows.attr, + &dev_attr_cols.attr, + &dev_attr_res_x.attr, + &dev_attr_res_y.attr, + &dev_attr_esd_protection.attr, + &dev_attr_monitor_mode.attr, + &dev_attr_auto_compensate.attr, +#ifdef CFG_CTS_DRIVER_BUILTIN_FIRMWARE + &dev_attr_driver_builtin_firmware.attr, +#endif /* CFG_CTS_DRIVER_BUILTIN_FIRMWARE */ +#ifdef CFG_CTS_FIRMWARE_IN_FS + &dev_attr_update_from_file.attr, +#endif /* CFG_CTS_FIRMWARE_IN_FS */ + &dev_attr_updating.attr, + NULL +}; + +static const struct attribute_group cts_dev_firmware_attr_group = { + .name = "cts_firmware", + .attrs = cts_dev_firmware_atts, +}; + +static ssize_t flash_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + const struct cts_flash *flash; + + if (cts_dev->flash == NULL) { + bool program_mode; + bool enabled; + int ret; + + program_mode = cts_is_device_program_mode(cts_dev); + enabled = cts_is_device_enabled(cts_dev); + + ret = cts_prepare_flash_operation(cts_dev); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Prepare flash operation failed %d", ret); + + cts_post_flash_operation(cts_dev); + + if (!program_mode) { + ret = cts_enter_normal_mode(cts_dev); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Enter normal mode failed %d", ret); + } + + if (enabled) { + ret = cts_start_device(cts_dev); + if (ret) + return snprintf(buf, PAGE_SIZE, + "Start device failed %d", ret); + } + + if (cts_dev->flash == NULL) + return snprintf(buf, PAGE_SIZE, "Flash not found\n"); + } + + flash = cts_dev->flash; + return snprintf(buf, PAGE_SIZE, + "%s:\n" + " JEDEC ID : %06X\n" + " Page size : 0x%zx\n" + " Sector size: 0x%zx\n" + " Block size : 0x%zx\n" + " Total size : 0x%zx\n", + flash->name, flash->jedec_id, flash->page_size, + flash->sector_size, flash->block_size, + flash->total_size); +} + +static DEVICE_ATTR(info, S_IRUGO, flash_info_show, NULL); + +static ssize_t read_flash_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u32 flash_addr, size, i, remaining; + u8 *data = NULL; + ssize_t count = 0; + int ret; + bool program_mode; + bool enabled; +#ifndef CFG_CTS_FOR_GKI + loff_t pos = 0; +#endif + + if (argc != 2 && argc != 3) + return snprintf(buf, PAGE_SIZE, "Invalid num args %d\n", argc); + + ret = kstrtou32(argv[0], 0, &flash_addr); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid flash addr: %s\n", argv[0]); + ret = kstrtou32(argv[1], 0, &size); + if (ret) + return snprintf(buf, PAGE_SIZE, "Invalid size: %s\n", argv[1]); + + data = (u8 *) kmalloc(size, GFP_KERNEL); + if (data == NULL) + return snprintf(buf, PAGE_SIZE, "Allocate buffer for read data failed\n"); + + cts_info("Read flash from 0x%06x size %u%s%s", flash_addr, size, + argc == 3 ? " to file " : "", argc == 3 ? argv[2] : ""); + + cts_lock_device(cts_dev); + program_mode = cts_is_device_program_mode(cts_dev); + enabled = cts_is_device_enabled(cts_dev); + + ret = cts_prepare_flash_operation(cts_dev); + if (ret) { + count += snprintf(buf, PAGE_SIZE, "Prepare flash operation failed %d", ret); + goto err_free_data; + } + + ret = cts_read_flash(cts_dev, flash_addr, data, size); + if (ret) { + count = snprintf(buf, PAGE_SIZE, "Read flash data failed %d\n", ret); + goto err_post_flash_operation; + } + + if (argc == 3) { +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): some functions are forbiddon with GKI Version!", __func__); +#else + struct file *file; + + cts_info("Write flash data to file '%s'", argv[2]); + + file = filp_open(argv[2], O_RDWR | O_CREAT | O_TRUNC, 0666); + if (IS_ERR(file)) { + count += snprintf(buf, PAGE_SIZE, "Open file '%s' failed %ld", + argv[2], PTR_ERR(file)); + goto err_post_flash_operation; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(file, data, size, &pos); +#else + ret = kernel_write(file, data, size, pos); +#endif + if (ret != size) + count += snprintf(buf, PAGE_SIZE, + "Write flash data to file '%s' failed %d", argv[2], ret); + + ret = filp_close(file, NULL); + if (ret) + count += snprintf(buf, PAGE_SIZE, + "Close file '%s' failed %d", argv[2], ret); +#endif + } else { +#define PRINT_ROW_SIZE (16) + remaining = size; + for (i = 0; i < size && count <= PAGE_SIZE; i += PRINT_ROW_SIZE) { + size_t linelen = min((size_t)remaining, (size_t)PRINT_ROW_SIZE); + + remaining -= PRINT_ROW_SIZE; + + count += snprintf(buf + count, PAGE_SIZE - count - 1, + "%04x-%04x: ", flash_addr >> 16, flash_addr & 0xFFFF); + /* Lower version kernel return void */ + hex_dump_to_buffer(data + i, linelen, PRINT_ROW_SIZE, 1, + buf + count, PAGE_SIZE - count - 1, true); + count += strlen(buf + count); + buf[count++] = '\n'; + flash_addr += linelen; +#undef PRINT_ROW_SIZE + } + } + +err_post_flash_operation: + cts_post_flash_operation(cts_dev); + + if (!program_mode) { + int r = cts_enter_normal_mode(cts_dev); + + if (r) + count += snprintf(buf, PAGE_SIZE, "Enter normal mode failed %d", r); + } + + if (enabled) { + int r = cts_start_device(cts_dev); + + if (r) { + cts_unlock_device(cts_dev); + return snprintf(buf, PAGE_SIZE, "Start device failed %d", r); + } + } +err_free_data: + cts_unlock_device(cts_dev); + kfree(data); + + return (ret < 0 ? ret : count); +} + +/* echo start_addr size [filepath] > read */ +static ssize_t read_flash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(read, S_IWUSR | S_IRUGO, read_flash_show, read_flash_store); + +/* echo addr size > erase */ +static ssize_t erase_flash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u32 flash_addr, size; + int ret; + bool program_mode; + bool enabled; + + parse_arg(buf, count); + + if (argc != 2) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + ret = kstrtou32(argv[0], 0, &flash_addr); + if (ret) { + cts_err("Invalid flash addr: %s", argv[0]); + return -EINVAL; + } + ret = kstrtou32(argv[1], 0, &size); + if (ret) { + cts_err("Invalid size: %s", argv[1]); + return -EINVAL; + } + + cts_info("Erase flash from 0x%06x size %u", flash_addr, size); + + cts_lock_device(cts_dev); + program_mode = cts_is_device_program_mode(cts_dev); + enabled = cts_is_device_enabled(cts_dev); + + ret = cts_prepare_flash_operation(cts_dev); + if (ret) { + cts_err("Prepare flash operation failed %d", ret); + cts_unlock_device(cts_dev); + return ret; + } + + ret = cts_erase_flash(cts_dev, flash_addr, size); + if (ret) { + cts_err("Erase flash from 0x%06x size %u failed %d", + flash_addr, size, ret); + goto err_post_flash_operation; + } + +err_post_flash_operation: + cts_post_flash_operation(cts_dev); + + if (!program_mode) { + int r = cts_enter_normal_mode(cts_dev); + + if (r) + cts_err("Enter normal mode failed %d", r); + } + + if (enabled) { + int r = cts_start_device(cts_dev); + + if (r) + cts_err("Start device failed %d", r); + } + cts_unlock_device(cts_dev); + + return (ret < 0 ? ret : count); +} + +static DEVICE_ATTR(erase, S_IWUSR, NULL, erase_flash_store); + +static struct attribute *cts_dev_flash_attrs[] = { + &dev_attr_info.attr, + &dev_attr_read.attr, + &dev_attr_erase.attr, + + NULL +}; + +static const struct attribute_group cts_dev_flash_attr_group = { + .name = "flash", + .attrs = cts_dev_flash_attrs, +}; + +static ssize_t open_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + struct cts_test_param test_param = { + .test_item = CTS_TEST_OPEN, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = "/sdcard/open-test-data.txt", + .num_invalid_node = 0, + .invalid_nodes = NULL, + }; + int min = 0; + int ret; + ktime_t start_time, end_time, delta_time; + + if (argc != 1) { + return scnprintf(buf, PAGE_SIZE, "Invalid num args %d\n", argc); + } + + ret = kstrtoint(argv[0], 0, &min); + if (ret) { + return scnprintf(buf, PAGE_SIZE, "Invalid min thres: %s\n", argv[0]); + } + + cts_info("Open test, threshold = %u", min); + + test_param.min = &min; + + start_time = ktime_get(); + + ret = cts_test_open(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret > 0) { + return scnprintf(buf, PAGE_SIZE, + "Open test has %d nodes FAIL, min: %u, ELAPSED TIME: %lldms\n", + ret, min, ktime_to_ms(delta_time)); + } else if (ret < 0) { + return scnprintf(buf, PAGE_SIZE, + "Open test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + return scnprintf(buf, PAGE_SIZE, + "Open test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + } + +} + +/* echo threshod > open_test */ +static ssize_t open_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(open_test, S_IWUSR | S_IRUGO, open_test_show, + open_test_store); + +static ssize_t short_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + struct cts_test_param test_param = { + .test_item = CTS_TEST_SHORT, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = "/sdcard/short-test-data.txt", + .num_invalid_node = 0, + .invalid_nodes = NULL, + }; + int min = 0; + int ret; + ktime_t start_time, end_time, delta_time; + + if (argc != 1) { + return scnprintf(buf, PAGE_SIZE, "Invalid num args %d\n", argc); + } + + ret = kstrtoint(argv[0], 0, &min); + if (ret) { + return scnprintf(buf, PAGE_SIZE, "Invalid min thres: %s\n", argv[0]); + } + + cts_info("Short test, threshold = %u", min); + + test_param.min = &min; + + start_time = ktime_get(); + + ret = cts_test_short(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret > 0) { + return scnprintf(buf, PAGE_SIZE, + "Short test has %d nodes FAIL, min: %u, ELAPSED TIME: %lldms\n", + ret, min, ktime_to_ms(delta_time)); + } else if (ret < 0) { + return scnprintf(buf, PAGE_SIZE, + "Short test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + return scnprintf(buf, PAGE_SIZE, + "Short test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + } +} + +/* echo threshod > short_test */ +static ssize_t short_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(short_test, S_IWUSR | S_IRUGO, + short_test_show, short_test_store); + +static ssize_t testing_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Testting: %s\n", + cts_data->cts_dev.rtdata.testing ? "Y" : "N"); +} + +static DEVICE_ATTR(testing, S_IRUGO, testing_show, NULL); + +#ifdef CFG_CTS_HAS_RESET_PIN +static ssize_t reset_pin_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef CFG_CTS_HAS_RESET_PIN + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + struct cts_test_param test_param = { + .test_item = CTS_TEST_RESET_PIN, + .flags = 0, + }; + int ret; + ktime_t start_time, end_time, delta_time; + + start_time = ktime_get(); + + ret = cts_test_reset_pin(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Reset-Pin test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + return scnprintf(buf, PAGE_SIZE, + "Reset-Pin test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + } +#else /* CFG_CTS_HAS_RESET_PIN */ + return scnprintf(buf, PAGE_SIZE, + "Reset-Pin test NOT supported(CFG_CTS_HAS_RESET_PIN not defined)\n"); +#endif +} +static DEVICE_ATTR(reset_pin_test, S_IRUGO, reset_pin_test_show, NULL); +#endif + +static ssize_t int_pin_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + struct cts_test_param test_param = { + .test_item = CTS_TEST_INT_PIN, + .flags = 0, + }; + int ret; + ktime_t start_time, end_time, delta_time; + + start_time = ktime_get(); + + ret = cts_test_int_pin(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Int-Pin test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + return scnprintf(buf, PAGE_SIZE, + "Int-Pin test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + } +} +static DEVICE_ATTR(int_pin_test, S_IRUGO, int_pin_test_show, NULL); + +static ssize_t compensate_cap_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + struct cts_test_param test_param = { + .test_item = CTS_TEST_COMPENSATE_CAP, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = + "/sdcard/comp-cap-test-data.txt", + .num_invalid_node = 0, + .invalid_nodes = NULL, + }; + int min = 0, max = 0; + int ret; + ktime_t start_time, end_time, delta_time; + + if (argc != 2) { + return scnprintf(buf, PAGE_SIZE, "Invalid num args\n" + "USAGE:\n" + " 1. echo min max > compensate_cap_test\n" + " 2. cat compensate_cap_test\n"); + } + + ret = kstrtoint(argv[0], 0, &min); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid min thres: %s\n", argv[0]); + } + + ret = kstrtoint(argv[1], 0, &max); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid max thres: %s\n", argv[1]); + } + + cts_info("Compensate cap test, min: %u, max: %u", min, max); + + test_param.min = &min; + test_param.max = &max; + + start_time = ktime_get(); + + ret = cts_test_compensate_cap(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret > 0) { + return scnprintf(buf, PAGE_SIZE, + "Compensate cap test has %d nodes FAIL, " + "threshold[%u, %u], ELAPSED TIME: %lldms\n", + ret, min, max, ktime_to_ms(delta_time)); + } else if (ret < 0) { + return scnprintf(buf, PAGE_SIZE, + "Compensate cap test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + return scnprintf(buf, PAGE_SIZE, + "Compensate cap test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + } +} + +/* echo threshod > short_test */ +static ssize_t compensate_cap_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(compensate_cap_test, S_IWUSR | S_IRUGO, + compensate_cap_test_show, compensate_cap_test_store); + +static ssize_t rawdata_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + struct cts_rawdata_test_priv_param priv_param = { + .frames = 16, + //.work_mode = 0, + }; + struct cts_test_param test_param = { + .test_item = CTS_TEST_RAWDATA, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MIN | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = "/sdcard/rawdata-test-data.txt", + .num_invalid_node = 0, + .invalid_nodes = NULL, + .priv_param = &priv_param, + .priv_param_size = sizeof(priv_param), + }; + + int min, max; + int ret; + ktime_t start_time, end_time, delta_time; + + if (argc < 2 || argc > 3) { + return scnprintf(buf, PAGE_SIZE, "Invalid num args\n" + "USAGE:\n" + " 1. echo min max [frames] > rawdata_test\n" + " 2. cat rawdata_test\n"); + } + + ret = kstrtoint(argv[0], 0, &min); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid min thres: %s\n", argv[0]); + } + + ret = kstrtoint(argv[1], 0, &max); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid max thres: %s\n", argv[1]); + } + + if (argc > 2) { + ret = kstrtou32(argv[2], 0, &priv_param.frames); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid frames: %s\n", argv[2]); + } + } + cts_info("Rawdata test, frames: %u min: %d, max: %d", + priv_param.frames, min, max); + + test_param.min = &min; + test_param.max = &max; + + start_time = ktime_get(); + + ret = cts_test_rawdata(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret > 0) { + return scnprintf(buf, PAGE_SIZE, + "Rawdata test has %d nodes FAIL, threshold[%u, %u], " + "ELAPSED TIME: %lldms\n", + ret, min, max, ktime_to_ms(delta_time)); + } else if (ret < 0) { + return scnprintf(buf, PAGE_SIZE, + "Rawdata test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + return scnprintf(buf, PAGE_SIZE, + "Rawdata test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + } +} + +/* echo threshod > short_test */ +static ssize_t rawdata_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + parse_arg(buf, count); + + return count; +} + +static DEVICE_ATTR(rawdata_test, S_IWUSR | S_IRUGO, + rawdata_test_show, rawdata_test_store); + +static ssize_t noise_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + struct cts_noise_test_priv_param priv_param = { + .frames = 50, + //.work_mode = 0, + }; + struct cts_test_param test_param = { + .test_item = CTS_TEST_NOISE, + .flags = CTS_TEST_FLAG_VALIDATE_DATA | + CTS_TEST_FLAG_VALIDATE_MAX | + CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE | + CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE, + .test_data_filepath = + "/sdcard/noise-test-data.txt", + .num_invalid_node = 0, + .invalid_nodes = NULL, + .priv_param = &priv_param, + .priv_param_size = sizeof(priv_param), + }; + + int max; + int ret; + ktime_t start_time, end_time, delta_time; + + if (argc < 1 || argc > 2) { + return scnprintf(buf, PAGE_SIZE, "Invalid num args\n" + "USAGE:\n" + " 1. echo threshold [frames] > noise_test\n" + " 2. cat noise_test\n"); + } + + ret = kstrtoint(argv[0], 0, &max); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid max thres: %s\n", argv[0]); + } + + if (argc > 1) { + ret = kstrtou32(argv[1], 0, &priv_param.frames); + if (ret) { + return scnprintf(buf, PAGE_SIZE, + "Invalid frames: %s\n", argv[1]); + } + } + cts_info("Noise test, frames: %u threshold: %d", + priv_param.frames, max); + + test_param.max = &max; + + start_time = ktime_get(); + + ret = cts_test_noise(cts_dev, &test_param); + + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret > 0) { + return scnprintf(buf, PAGE_SIZE, + "Noise test has %d nodes FAIL, max: %u, ELAPSED TIME: %lldms\n", + ret, max, ktime_to_ms(delta_time)); + } else if (ret < 0) { + return scnprintf(buf, PAGE_SIZE, + "Noise test FAIL %d(%s), ELAPSED TIME: %lldms\n", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + return scnprintf(buf, PAGE_SIZE, + "Noise test PASS, ELAPSED TIME: %lldms\n", + ktime_to_ms(delta_time)); + } +} + +static ssize_t noise_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + parse_arg(buf, count); + + return count; +} +static DEVICE_ATTR(noise_test, S_IWUSR | S_IRUGO, + noise_test_show, noise_test_store); + +static struct attribute *cts_dev_test_atts[] = { + &dev_attr_open_test.attr, + &dev_attr_short_test.attr, + &dev_attr_testing.attr, +#ifdef CFG_CTS_HAS_RESET_PIN + &dev_attr_reset_pin_test.attr, +#endif + &dev_attr_int_pin_test.attr, + &dev_attr_compensate_cap_test.attr, + &dev_attr_rawdata_test.attr, + &dev_attr_noise_test.attr, + NULL +}; + +static const struct attribute_group cts_dev_test_attr_group = { + .name = "test", + .attrs = cts_dev_test_atts, +}; + +static ssize_t ic_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "IC Type : %s\n", + cts_data->cts_dev.hwdata->name); +} + +static DEVICE_ATTR(ic_type, S_IRUGO, ic_type_show, NULL); + +static ssize_t program_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return snprintf(buf, PAGE_SIZE, "Program mode: %s\n", + cts_data->cts_dev.rtdata.program_mode ? "Y" : "N"); +} + +static ssize_t program_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + int ret; + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + if (*argv[0] == '1' || tolower(*argv[0]) == 'y') { + ret = cts_enter_program_mode(&cts_data->cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + return ret; + } + } else if (*argv[0] == '0' || tolower(*argv[0]) == 'n') { + ret = cts_enter_normal_mode(&cts_data->cts_dev); + if (ret) { + cts_err("Exit program mode failed %d", ret); + return ret; + } + } else + cts_err("Invalid args"); + + return count; +} + +static DEVICE_ATTR(program_mode, S_IWUSR | S_IRUGO, + program_mode_show, program_mode_store); + +static ssize_t rawdata_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 *rawdata = NULL; + int ret, r, c, count = 0; + u32 max, min, sum, average; + int max_r, max_c, min_r, min_c; + bool data_valid = true; + + cts_info("Show rawdata"); + + rawdata = (u16 *) kmalloc(RAWDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata == NULL) + return snprintf(buf, PAGE_SIZE, + "Allocate memory for rawdata failed\n"); + + cts_lock_device(cts_dev); + ret = cts_tcs_top_get_rawdata(cts_dev, (u8 *) rawdata, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret < 0) { + cts_err("Get rawdata failed"); + goto err_free_rawdata; + } + data_valid = true; + + if (data_valid) { + max = min = rawdata[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + u16 val = rawdata[r * cts_dev->hwdata->num_col + c]; + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count += snprintf(buf + count, PAGE_SIZE - count, + SPLIT_LINE_STR + "Raw data MIN: [%d][%d]=%u, MAX: [%d][%d]=%u, AVG=%u\n" + SPLIT_LINE_STR + " | ", min_r, min_c, min, max_r, max_c, + max, average); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += snprintf(buf + count, PAGE_SIZE - count, + COL_NUM_FORMAT_STR, c); + + count += snprintf(buf + count, PAGE_SIZE - count, + "\n" SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows && count < PAGE_SIZE; r++) { + count += snprintf(buf + count, PAGE_SIZE - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols && count < PAGE_SIZE; c++) + count += snprintf(buf + count, PAGE_SIZE - count - 1, + DATA_FORMAT_STR, rawdata[r * cts_dev->hwdata->num_col + c]); + + buf[count++] = '\n'; + } + } + +err_free_rawdata: + cts_unlock_device(cts_dev); + kfree(rawdata); + + return data_valid ? count : ret; +} + +static DEVICE_ATTR(rawdata, S_IRUGO, rawdata_show, NULL); + +static ssize_t diffdata_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + s16 *diffdata = NULL; + int ret, r, c, count = 0; + int max, min, sum, average; + int max_r, max_c, min_r, min_c; + bool data_valid = true; + + cts_info("Show diffdata"); + + diffdata = (s16 *) kmalloc(RAWDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (diffdata == NULL) { + cts_err("Allocate memory for diffdata failed"); + return -ENOMEM; + } + + cts_lock_device(cts_dev); + ret = cts_tcs_top_get_real_diff(cts_dev, (u8 *) diffdata, + RAWDATA_BUFFER_SIZE(cts_dev)); + cts_unlock_device(cts_dev); + if (ret < 0) { + cts_err("Get diffdata failed"); + goto err_free_diffdata; + } + data_valid = true; + + if (data_valid) { + max = min = diffdata[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + s16 val = diffdata[r * cts_dev->hwdata->num_col + c]; + + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count += snprintf(buf + count, PAGE_SIZE - count, + SPLIT_LINE_STR + "Diff data MIN: [%d][%d]=%d, MAX: [%d][%d]=%d, AVG=%d\n" + SPLIT_LINE_STR + " | ", min_r, min_c, min, max_r, max_c, + max, average); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += snprintf(buf + count, PAGE_SIZE - count, + COL_NUM_FORMAT_STR, c); + + count += snprintf(buf + count, PAGE_SIZE - count, + "\n" SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count += snprintf(buf + count, PAGE_SIZE - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += snprintf(buf + count, PAGE_SIZE - count, + DATA_FORMAT_STR, diffdata[r * cts_dev->hwdata->num_col + c]); + + buf[count++] = '\n'; + } + } + +err_free_diffdata: + kfree(diffdata); + + return data_valid ? count : ret; +} + +static DEVICE_ATTR(diffdata, S_IRUGO, diffdata_show, NULL); + + +static ssize_t manualdiff_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + s16 *manualdiff = NULL; + int ret, r, c, count = 0; + int max, min, sum, average; + int max_r, max_c, min_r, min_c; + bool data_valid = true; + + cts_info("Show manualdiff"); + + manualdiff = (s16 *) kmalloc(RAWDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (manualdiff == NULL) { + cts_err("Allocate memory for manualdiff failed"); + return -ENOMEM; + } + + cts_lock_device(cts_dev); + ret = cts_tcs_top_get_manual_diff(cts_dev, (u8 *) manualdiff, + RAWDATA_BUFFER_SIZE(cts_dev)); + cts_unlock_device(cts_dev); + if (ret < 0) { + cts_err("Get manualdiff failed"); + goto err_free_manualdiff; + } + data_valid = true; + + if (data_valid) { + max = min = manualdiff[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + s16 val = manualdiff[r * cts_dev->hwdata->num_col + c]; + + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count += snprintf(buf + count, PAGE_SIZE - count, + SPLIT_LINE_STR + "Diff data MIN: [%d][%d]=%d, MAX: [%d][%d]=%d, AVG=%d\n" + SPLIT_LINE_STR + " | ", min_r, min_c, min, max_r, max_c, + max, average); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += snprintf(buf + count, PAGE_SIZE - count, + COL_NUM_FORMAT_STR, c); + + count += snprintf(buf + count, PAGE_SIZE - count, + "\n" SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count += snprintf(buf + count, PAGE_SIZE - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += snprintf(buf + count, PAGE_SIZE - count, + DATA_FORMAT_STR, manualdiff[r * cts_dev->hwdata->num_col + c]); + + buf[count++] = '\n'; + } + } + +err_free_manualdiff: + kfree(manualdiff); + + return data_valid ? count : ret; +} + +static DEVICE_ATTR(manualdiff, S_IRUGO, manualdiff_show, NULL); + +static ssize_t jitter_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + u16 cnt; + int ret; + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + ret = kstrtou16(argv[0], 0, &cnt); + if (ret == 0) { + if (cnt > 2 && cnt < 10000) + jitter_test_frame = cnt; + } + + cts_info("jitter test frame: %d", jitter_test_frame); + return count; +} + +static ssize_t jitter_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + s16 *rawdata = NULL; + s16 *rawdata_min = NULL; + s16 *rawdata_max = NULL; + int ret, r, c, count = 0; + int max, min, sum, average; + int max_r, max_c, min_r, min_c; + bool data_valid = false; + int i; + + cts_info("Show jitter"); + cts_lock_device(cts_dev); + rawdata = (s16 *) kmalloc(RAWDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate memory for rawdata failed"); + ret = -ENOMEM; + goto err_jitter_show_exit; + } + rawdata_min = (s16 *) kmalloc(RAWDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata_min == NULL) { + cts_err("Allocate memory for rawdata failed"); + ret = -ENOMEM; + goto err_free_rawdata; + } + rawdata_max = (s16 *) kmalloc(RAWDATA_BUFFER_SIZE(cts_dev), GFP_KERNEL); + if (rawdata_max == NULL) { + cts_err("Allocate memory for rawdata failed"); + ret = -ENOMEM; + goto err_free_rawdata_min; + } + + for (i = 0; i < RAWDATA_BUFFER_SIZE(cts_dev) / 2; i++) { + rawdata_min[i] = 32767; + rawdata_max[i] = -32768; + } + + for (i = 0; i < jitter_test_frame; i++) { + ret = cts_tcs_top_get_rawdata(cts_dev, (u8 *) rawdata, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret) { + cts_err("Get raw data failed %d", ret); + msleep(5); + continue; + } + for (r = 0; r < cts_dev->hwdata->num_row; r++) { + for (c = 0; c < cts_dev->hwdata->num_col; c++) { + int index; + + index = r * cts_dev->hwdata->num_col + c; + if (rawdata_min[index] > rawdata[index]) + rawdata_min[index] = rawdata[index]; + else if (rawdata_max[index] < rawdata[index]) + rawdata_max[index] = rawdata[index]; + } + } + } + data_valid = true; + + if (data_valid) { + max = -32768; + min = 32767; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + s16 val; + int index = r * cts_dev->hwdata->num_col + c; + + val = rawdata_max[index] - rawdata_min[index]; + rawdata[index] = val; + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count += snprintf(buf + count, PAGE_SIZE - count, + SPLIT_LINE_STR + "Jitter data MIN: [%d][%d]=%d, MAX: [%d][%d]=%d, AVG=%d, TOTAL FRAME=%d\n" + SPLIT_LINE_STR + " | ", min_r, min_c, min, max_r, max_c, + max, average, jitter_test_frame); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += snprintf(buf + count, PAGE_SIZE - count, COL_NUM_FORMAT_STR, c); + + count += snprintf(buf + count, PAGE_SIZE - count, "\n" SPLIT_LINE_STR); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count += snprintf(buf + count, PAGE_SIZE - count, ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += snprintf(buf + count, PAGE_SIZE - count, + DATA_FORMAT_STR, rawdata[r * cts_dev->hwdata->num_col + c]); + + buf[count++] = '\n'; + } + } + + //err_free_rawdata_max: + kfree(rawdata_max); +err_free_rawdata_min: + kfree(rawdata_min); +err_free_rawdata: + kfree(rawdata); +err_jitter_show_exit: + cts_unlock_device(cts_dev); + return data_valid ? count : ret; +} + +static DEVICE_ATTR(jitter, S_IRUSR | S_IWUSR, jitter_show, jitter_store); + +static ssize_t compensate_cap_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 *cap = NULL; + int ret; + ssize_t count = 0; + int r, c, min, max, max_r, max_c, min_r, min_c, sum, average; + + cts_info("Read '%s'", attr->attr.name); + + cap = kzalloc(cts_dev->hwdata->num_row * cts_dev->hwdata->num_col, GFP_KERNEL); + if (cap == NULL) + return scnprintf(buf, PAGE_SIZE, + "Allocate mem for compensate cap failed\n"); + + cts_lock_device(cts_dev); + ret = cts_tcs_top_get_cnegdata(cts_dev, cap, + cts_dev->hwdata->num_row * cts_dev->hwdata->num_col); + cts_unlock_device(cts_dev); + if (ret) { + kfree(cap); + return scnprintf(buf, PAGE_SIZE, "Get compensate cap failed %d\n", ret); + } + + max = min = cap[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + u16 val = cap[r * cts_dev->hwdata->num_col + c]; + + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count += scnprintf(buf + count, PAGE_SIZE - count, + "--------------------------------------"\ + "--------------------------------------\n" + " Compensatete Cap MIN: [%d][%d]=%u, MAX: [%d][%d]=%u, AVG=%u\n" + "---+----------------------------------"\ + "--------------------------------------\n" + " |", min_r, min_c, min, max_r, max_c, max, + average); + for (c = 0; c < cts_dev->fwdata.cols; c++) + count += scnprintf(buf + count, PAGE_SIZE - count, " %3u", c); + + count += scnprintf(buf + count, PAGE_SIZE - count, + "\n" + "---+----------------------------------"\ + "--------------------------------------\n"); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count += scnprintf(buf + count, PAGE_SIZE - count, "%2u |", r); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += scnprintf(buf + count, PAGE_SIZE - count, " %3u", + cap[r * cts_dev->hwdata->num_col + c]); + } + buf[count++] = '\n'; + } + count += scnprintf(buf + count, PAGE_SIZE - count, + "---+----------------------------------"\ + "--------------------------------------\n"); + + kfree(cap); + + return count; +} + +static DEVICE_ATTR(compensate_cap, S_IRUGO, compensate_cap_show, NULL); + +#ifdef CFG_CTS_HAS_RESET_PIN +static ssize_t reset_pin_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + cts_info("Read RESET-PIN"); + + return snprintf(buf, PAGE_SIZE, + "Reset pin: %d, status: %d\n", + cts_data->pdata->rst_gpio, + gpio_get_value(cts_data->pdata->rst_gpio)); +} + +static ssize_t reset_pin_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + cts_info("Write RESET-PIN"); + cts_info("Chip staus maybe changed"); + + cts_plat_set_reset(cts_dev->pdata, (buf[0] == '1') ? 1 : 0); + return count; +} + +static DEVICE_ATTR(reset_pin, S_IRUSR | S_IWUSR, reset_pin_show, + reset_pin_store); +#endif + +static ssize_t irq_pin_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + cts_info("Read IRQ-PIN"); + + return snprintf(buf, PAGE_SIZE, + "IRQ pin: %d, status: %d\n", + cts_data->pdata->int_gpio, + gpio_get_value(cts_data->pdata->int_gpio)); +} + +static DEVICE_ATTR(irq_pin, S_IRUGO, irq_pin_show, NULL); + +static ssize_t irq_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct irq_desc *desc; + + cts_info("Read IRQ-INFO"); + + desc = irq_to_desc(cts_data->pdata->irq); + if (desc == NULL) { + return snprintf(buf, PAGE_SIZE, + "IRQ: %d descriptor not found\n", + cts_data->pdata->irq); + } + + return scnprintf(buf, PAGE_SIZE, + "IRQ num: %d, depth: %u, " + "count: %u, unhandled: %u, last unhandled eslape: %lu, irq flags: 0x%x, int_mode: %s\n", + cts_data->pdata->irq, desc->depth, + desc->irq_count, desc->irqs_unhandled, + desc->last_unhandled, desc->action->flags, + (desc->action->flags & IRQF_TRIGGER_MASK ) == IRQF_TRIGGER_RISING ? "IRQF_TRIGGER_RISING" : + (desc->action->flags & IRQF_TRIGGER_MASK ) == IRQF_TRIGGER_FALLING ? "IRQF_TRIGGER_FALLING" : + (desc->action->flags & IRQF_TRIGGER_MASK ) == IRQF_TRIGGER_HIGH ? "IRQF_TRIGGER_HIGH" : + (desc->action->flags & IRQF_TRIGGER_MASK ) == IRQF_TRIGGER_LOW ? "IRQF_TRIGGER_LOW " : "IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING"); +} + +static DEVICE_ATTR(irq_info, S_IRUGO, irq_info_show, NULL); + +#ifdef CFG_CTS_FW_LOG_REDIRECT +static ssize_t fw_log_redirect_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + return snprintf(buf, PAGE_SIZE, "Fw log redirect is %s\n", + cts_is_fw_log_redirect(cts_dev) ? "enable" : "disable"); +} + +static ssize_t fw_log_redirect_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 enable = 0; + + if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') + enable = 1; + if (enable) + cts_enable_fw_log_redirect(cts_dev); + else + cts_disable_fw_log_redirect(cts_dev); + + return count; +} + +static DEVICE_ATTR(fw_log_redirect, S_IRUSR | S_IWUSR, fw_log_redirect_show, + fw_log_redirect_store); +#endif + +#ifndef CONFIG_CTS_I2C_HOST +static ssize_t debug_spi_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + return snprintf(buf, PAGE_SIZE, "spi_speed=%d\n", + cts_dev->pdata->spi_speed); +} + +static ssize_t debug_spi_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + u16 s = 0; + int ret = 0; + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + ret = kstrtou16(argv[0], 0, &s); + if (ret) { + cts_err("Invalid spi speed: %s", argv[0]); + return -EINVAL; + } + + cts_dev->pdata->spi_speed = s; + + return count; +} + +static DEVICE_ATTR(debug_spi, S_IRUSR | S_IWUSR, debug_spi_show, + debug_spi_store); +#endif + +#ifdef CFG_CTS_GESTURE +static ssize_t gesture_en_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + return sprintf(buf, "Gesture wakup is %s\n", + cts_is_gesture_wakeup_enabled(cts_dev) ? "enable" : "disable"); +} + +static ssize_t gesture_en_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 enable = 0; + + if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') + enable = 1; + + if (enable) + cts_enable_gesture_wakeup(cts_dev); + else + cts_disable_gesture_wakeup(cts_dev); + + return count; +} + +static DEVICE_ATTR(gesture_en, S_IRUSR | S_IWUSR, gesture_en_show, + gesture_en_store); +#endif + +static ssize_t int_data_types_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%#04x\n", + cts_data->cts_dev.fwdata.int_data_types); +} + +static ssize_t int_data_types_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + u16 type = 0; + int ret = 0; + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + ret = kstrtou16(argv[0], 0, &type); + if (ret) { + cts_err("Invalid int data types: %s", argv[0]); + return -EINVAL; + } + + cts_lock_device(&cts_data->cts_dev); + ret = cts_set_int_data_types(&cts_data->cts_dev, type); + cts_unlock_device(&cts_data->cts_dev); + if (ret) + return -EIO; + return count; +} + +static DEVICE_ATTR(int_data_types, S_IWUSR | S_IRUGO, + int_data_types_show, int_data_types_store); + +static ssize_t int_data_method_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + cts_data->cts_dev.fwdata.int_data_method); +} + +static ssize_t int_data_method_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + u8 method = 0; + int ret = 0; + + parse_arg(buf, count); + + if (argc != 1) { + cts_err("Invalid num args %d", argc); + return -EFAULT; + } + + ret = kstrtou8(argv[0], 0, &method); + if (ret) { + cts_err("Invalid int data method: %s", argv[0]); + return -EINVAL; + } else if (method >= INT_DATA_METHOD_CNT) { + cts_err("Invalid int data method: %s", argv[0]); + return -EINVAL; + } + + cts_lock_device(&cts_data->cts_dev); + ret = cts_set_int_data_method(&cts_data->cts_dev, method); + cts_unlock_device(&cts_data->cts_dev); + if (ret) + return -EIO; + return count; +} + +static DEVICE_ATTR(int_data_method, S_IWUSR | S_IRUGO, + int_data_method_show, int_data_method_store); + + +static ssize_t cts_charger_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 val; + int ret; + + cts_info("Read sysfs '/%s'", attr->attr.name); + + cts_lock_device(cts_dev); + ret = cts_tcs_get_charger_plug(cts_dev, &val); + cts_unlock_device(cts_dev); + + if (ret) { + cts_err("Get charger state failed %d(%s)", ret, cts_strerror(ret)); + return -1; + } + switch (val) { + case 0: return scnprintf(buf, PAGE_SIZE, "Detached\n"); + case 1: return scnprintf(buf, PAGE_SIZE, "Attached\n"); + default: return scnprintf(buf, PAGE_SIZE, "Read error\n"); + } +} + +static ssize_t cts_charger_state_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int state; + int ret; + + cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); + + switch (buf[0]) { + case '0': state = 0; break; + case '1': state = 1; break; + default: + cts_err("Invalid arg for state"); + return -EINVAL; + } + + cts_info("state = %d", state); + + cts_lock_device(cts_dev); + ret = cts_tcs_set_charger_plug(cts_dev, state); + cts_unlock_device(cts_dev); + + if (ret) { + cts_err("Set charger state failed %d(%s)", ret, cts_strerror(ret)); + return -EIO; + } + + return count; +} +static DEVICE_ATTR(charger_state, S_IRUGO | S_IWUSR, + cts_charger_state_show, cts_charger_state_store); + +static ssize_t cts_earjack_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 val; + int ret; + + cts_info("Read sysfs '/%s'", attr->attr.name); + + cts_lock_device(cts_dev); + ret = cts_tcs_get_earjack_plug(cts_dev, &val); + cts_unlock_device(cts_dev); + + if (ret) { + cts_err("Get earjack state failed %d(%s)", + ret, cts_strerror(ret)); + return -1; + } + switch (val) { + case 0: return scnprintf(buf, PAGE_SIZE, "Detached\n"); + case 1: return scnprintf(buf, PAGE_SIZE, "Attached\n"); + default: return scnprintf(buf, PAGE_SIZE, "Read error\n"); + } +} + +static ssize_t cts_earjack_state_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int state; + int ret; + + cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); + + switch (buf[0]) { + case '0': state = 0; break; + case '1': state = 1; break; + default: + cts_err("Invalid arg for state"); + return -EINVAL; + } + + cts_info("state = %d", state); + + cts_lock_device(cts_dev); + ret = cts_tcs_set_earjack_plug(cts_dev, state); + cts_unlock_device(cts_dev); + + if (ret) { + cts_err("Set earjack state failed %d(%s)", ret, cts_strerror(ret)); + return -EIO; + } + + return count; +} +static DEVICE_ATTR(earjack_state, S_IRUGO | S_IWUSR, + cts_earjack_state_show, cts_earjack_state_store); + +static ssize_t cts_edge_restain_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 direction; + int ret; + + cts_info("Read sysfs '/%s'", attr->attr.name); + + cts_lock_device(cts_dev); + ret = cts_tcs_get_panel_direction(cts_dev, &direction); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("get panel direction failed!"); + return scnprintf(buf, PAGE_SIZE, "Read error\n"); + } + + return scnprintf(buf, PAGE_SIZE, "direction: 0x%02x\n", direction); +} + +static ssize_t cts_edge_restain_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 direction; + int ret; + + cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); + + switch (buf[0]) { + case '0': direction = 0; break;//normal + case '1': direction = 1; break;//notch left + case '2': direction = 2; break;//notch right + default: + cts_err("Invalid arg for mode"); + return -EINVAL; + } + cts_info("direction = %d", direction); + + cts_lock_device(cts_dev); + ret = cts_tcs_set_panel_direction(cts_dev, direction); + cts_unlock_device(cts_dev); + + if (ret) { + cts_err("Set edge restain failed"); + return -EIO; + } + + return count; +} +static DEVICE_ATTR(edge_restain, S_IWUSR | S_IRUGO, + cts_edge_restain_show, cts_edge_restain_store); + +static ssize_t cts_game_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 enabled; + int ret; + + cts_info("Read sysfs '/%s'", attr->attr.name); + + cts_lock_device(cts_dev); + ret = cts_tcs_get_game_mode(cts_dev, &enabled); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("get game mode failed!"); + return scnprintf(buf, PAGE_SIZE, "Read error\n"); + } + + return scnprintf(buf, PAGE_SIZE, "game mode: 0x%02x\n", enabled); +} + +static ssize_t cts_game_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int enable = -1; + int ret; + + cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); + + if (count >= 1) { + if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') { + enable = 1; + } else if (buf[0] == 'N' || buf[0] == 'n' || buf[0] == '0') { + enable = 0; + } + } + if (enable == -1) { + cts_err("Invalid arg for game mode enable"); + return -EINVAL; + } + + cts_lock_device(cts_dev); + ret = cts_tcs_set_game_mode(cts_dev, enable); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("Set game mode failed"); + return -EIO; + } + + return count; +} +static DEVICE_ATTR(game_mode, S_IWUSR | S_IRUGO, + cts_game_mode_show, cts_game_mode_store); + +#ifdef CONFIG_CTS_TP_PROXIMITY +static ssize_t cts_proximity_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + + cts_info("Read sysfs '/%s'", attr->attr.name); + + return scnprintf(buf, PAGE_SIZE, "proximity mode: %d\n", + status->proximity); +} + +static ssize_t cts_proximity_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int enable = -1; + int ret; + + cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); + + if (count >= 1) { + if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') { + enable = 1; + } else if (buf[0] == 'N' || buf[0] == 'n' || buf[0] == '0') { + enable = 0; + } + } + if (enable == -1) { + cts_err("Invalid arg for proximity mode enable"); + return -EINVAL; + } + + cts_lock_device(cts_dev); + ret = cts_tcs_set_proximity_mode(cts_dev, enable); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("Set proximity mode failed"); + return -EIO; + } + + return count; +} +static DEVICE_ATTR(proximity_mode, S_IWUSR | S_IRUGO, + cts_proximity_mode_show, cts_proximity_mode_store); +#endif + + +static ssize_t cts_tcs_cmd_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + + return snprintf(buf, PAGE_SIZE, "chipone_cmds.bin size:%zu\n", + cts_dev->rtdata.tcscmd_len * sizeof(u16)); +} + +static ssize_t cts_tcs_cmd_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + const struct firmware *tcscmds; + char *update = "update"; + int ret; + + cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); + + ret = memcmp(update, buf, count - 1); + if (ret) { + cts_err("Not update tcs cmd buf, %s", buf); + return count; + } + + ret = request_firmware(&tcscmds, "chipone_cmds.bin", dev); + if (ret) { + cts_err("Could not load firmware from chipone_cmds.bin: %d", ret); + return ret; + } + + if (tcscmds->size > PAGE_SIZE) { + cts_err("tcscmds length is over one PAGE_SIZE"); + return count; + } + + memcpy((u8 *)cts_dev->rtdata.tcscmd, tcscmds->data, tcscmds->size); + cts_dev->rtdata.tcscmd_len = tcscmds->size / sizeof(u16); + + release_firmware(tcscmds); + cts_info("update tcscmds successfully"); + + return count; +} +static DEVICE_ATTR(tcs_cmd, S_IWUSR | S_IRUSR, + cts_tcs_cmd_show, cts_tcs_cmd_store); + +static u16 cts_find_tcs_cmd(struct cts_device *cts_dev, u8 class_id, u8 cmd_id) +{ + struct cts_tcs_cmd *tcmd; + u8 cmd_buf[2]; + int i; + + for (i = 0; i < cts_dev->rtdata.tcscmd_len; i++) { + put_unaligned_le16(cts_dev->rtdata.tcscmd[i], cmd_buf); + tcmd = (struct cts_tcs_cmd *)cmd_buf; + if (class_id == tcmd->class_id && cmd_id == tcmd->cmd_id) { + cts_info("Found tcs cmd:0x%04x: { %d, %d, %d, %d, %d}", + cts_dev->rtdata.tcscmd[i], tcmd->base_flag, + tcmd->class_id, tcmd->cmd_id, + tcmd->is_read, tcmd->is_write); + return cts_dev->rtdata.tcscmd[i]; + } + } + + return 0; +} +static ssize_t cts_read_tcs_cmd_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + int count = 0; + u8 *read_buf; + int i; + int ret; + + read_buf = kzalloc(cts_dev->rtdata.curr_len, GFP_KERNEL); + if (read_buf == NULL) { + return snprintf(buf, PAGE_SIZE, "kzalloc read_buf failed\n"); + } + + if (!(cts_dev->rtdata.curr_cmd & BIT(14))) { + return snprintf(buf, PAGE_SIZE, "This cmd %04x is not readable!\n", + cts_dev->rtdata.curr_cmd); + } + + cts_lock_device(cts_dev); + ret = cts_tcs_read(cts_dev, cts_dev->rtdata.curr_cmd, read_buf, + cts_dev->rtdata.curr_len); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("read tcscmd: 0x%04x, failed", cts_dev->rtdata.curr_cmd); + return ret; + } + + count += snprintf(buf + count, PAGE_SIZE - count, "read_tcs(0x%02x): ", + cts_dev->rtdata.curr_cmd); + for (i = 0; i < cts_dev->rtdata.curr_len; i++) { + count += snprintf(buf + count, PAGE_SIZE - count, "%02x ", read_buf[i]); + } + count += snprintf(buf + count, PAGE_SIZE - count, "\n"); + return count; +} +static ssize_t cts_read_tcs_cmd_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u16 cmd; + u8 class_id, cmd_id, read_len; + int ret; + + cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); + parse_arg(buf, count); + + if (argc != 3) { + cts_err("usage: echo class_id cmd_id read_len > read_tcs"); + return count; + } + + ret = kstrtou8(argv[0], 0, &class_id); + if (ret) { + cts_err("kstrtou8 class_id %d failed", class_id); + return ret; + } + + ret = kstrtou8(argv[1], 0, &cmd_id); + if (ret) { + cts_err("kstrtou8 cmd_id %d failed", cmd_id); + return ret; + } + + ret = kstrtou8(argv[2], 0, &read_len); + if (ret) { + cts_err("kstrtou8 read_len %d failed", read_len); + return ret; + } + + cmd = cts_find_tcs_cmd(cts_dev, class_id, cmd_id); + if (cmd == 0) { + cts_err("Not found classid %d, cmdid %d", class_id, cmd_id); + return count; + } + + cts_dev->rtdata.curr_cmd = cmd; + cts_dev->rtdata.curr_len = read_len; + cts_info("tcs cmd:%02x", cmd); + + return count; +} +static DEVICE_ATTR(read_tcs_cmd, S_IWUSR | S_IRUSR, + cts_read_tcs_cmd_show, cts_read_tcs_cmd_store); + + +static ssize_t cts_write_tcs_cmd_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + struct cts_device *cts_dev = &cts_data->cts_dev; + u8 class_id, cmd_id; + u16 cmd; + u8 wbuf[32]; + int wlen; + int i = 0; + int ret; + + cts_info("Write sysfs '/%s' size %zu", attr->attr.name, count); + parse_arg(buf, count); + + if (argc < 3) { + cts_err("usage: echo class_id cmd_id x1 x2 .. > write_tcs"); + return count; + } + + ret = kstrtou8(argv[0], 0, &class_id); + if (ret) { + cts_err("kstrtou8 class_id %d failed", i); + return ret; + } + + ret = kstrtou8(argv[1], 0, &cmd_id); + if (ret) { + cts_err("kstrtou8 cmd_id %d failed", i); + return ret; + } + + wlen = argc - 2; + if (wlen > sizeof(wbuf)) { + cts_warn("write size is limit %zu bytes.", sizeof(wbuf)); + wlen = sizeof(wbuf); + } + + for (i = 0; i < wlen; i++) { + ret = kstrtou8(argv[i + 2], 0, wbuf + i); + if (ret) { + cts_err("kstrtou8 wbuf %d failed", i); + return ret; + } + } + + cmd = cts_find_tcs_cmd(cts_dev, class_id, cmd_id); + if (cmd == 0) { + cts_err("Not found classid %d, cmdid %d", class_id, cmd_id); + return count; + } + + cts_lock_device(cts_dev); + ret = cts_tcs_write(cts_dev, cmd, wbuf, wlen); + cts_unlock_device(cts_dev); + if (ret) { + cts_err("write tcscmd: 0x%04x, failed", cmd); + return ret; + } + + return count; +} +static DEVICE_ATTR(write_tcs_cmd, S_IWUSR, NULL, cts_write_tcs_cmd_store); + +static struct attribute *cts_dev_misc_atts[] = { + &dev_attr_ic_type.attr, + &dev_attr_program_mode.attr, + &dev_attr_rawdata.attr, + &dev_attr_diffdata.attr, + &dev_attr_manualdiff.attr, + &dev_attr_jitter.attr, +#ifdef CFG_CTS_HAS_RESET_PIN + &dev_attr_reset_pin.attr, +#endif + &dev_attr_irq_pin.attr, + &dev_attr_irq_info.attr, +#ifdef CFG_CTS_FW_LOG_REDIRECT + &dev_attr_fw_log_redirect.attr, +#endif + &dev_attr_compensate_cap.attr, + &dev_attr_read_tcs_reg.attr, + &dev_attr_write_tcs_reg.attr, + &dev_attr_read_hw_reg.attr, + &dev_attr_write_hw_reg.attr, +#ifndef CONFIG_CTS_I2C_HOST + &dev_attr_debug_spi.attr, +#endif +#ifdef CFG_CTS_GESTURE + &dev_attr_gesture_en.attr, +#endif /* CFG_CTS_GESTURE */ + &dev_attr_int_data_types.attr, + &dev_attr_int_data_method.attr, + &dev_attr_charger_state.attr, + &dev_attr_earjack_state.attr, + &dev_attr_edge_restain.attr, + &dev_attr_game_mode.attr, +#ifdef CONFIG_CTS_TP_PROXIMITY + &dev_attr_proximity_mode.attr, +#endif + &dev_attr_tcs_cmd.attr, + &dev_attr_read_tcs_cmd.attr, + &dev_attr_write_tcs_cmd.attr, + NULL +}; + +static const struct attribute_group cts_dev_misc_attr_group = { + .name = "misc", + .attrs = cts_dev_misc_atts, +}; + +static const struct attribute_group *cts_dev_attr_groups[] = { + &cts_dev_firmware_attr_group, +#ifdef CFG_CTS_FW_UPDATE_SYS + &cts_dev_fw_up_attr_group, +#endif + &cts_dev_flash_attr_group, + &cts_dev_test_attr_group, + &cts_dev_misc_attr_group, + NULL +}; + +extern struct chipone_ts_data *g_cts_data; +extern int cts_suspend(struct chipone_ts_data *cts_data); +extern int cts_resume(struct chipone_ts_data *cts_data); +static ssize_t cts_suspend_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = g_cts_data; + struct cts_device *cts_dev = &cts_data->cts_dev; + return snprintf(buf, PAGE_SIZE, "%s\n", + cts_dev->rtdata.suspended ? "suspend" : "resume"); +} +static ssize_t cts_suspend_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + if (count >= 1) { + if (buf[0] == '1') { + cts_suspend(g_cts_data); + } else if (buf[0] == '0') { + cts_resume(g_cts_data); + } else { + cts_err("usage: echo 0/1 > ts_suspend"); + } + } + + return count; +} +static DEVICE_ATTR(ts_suspend, S_IWUSR | S_IRUGO, + cts_suspend_show, cts_suspend_store); + +static struct attribute *cts_dev_ts_suspend_atts[] = { + &dev_attr_ts_suspend.attr, + NULL +}; + +static struct attribute_group ts_suspend_attr_group = { + .attrs = cts_dev_ts_suspend_atts, +}; + + +#include +#include + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *data = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!data) { + cts_err("Read 'path' with chipone_ts_data NULL"); + return (ssize_t) 0; + } +#ifdef CONFIG_CTS_I2C_HOST + path = kobject_get_path(&data->i2c_client->dev.kobj, GFP_KERNEL); +#else + path = kobject_get_path(&data->spi_client->dev.kobj, GFP_KERNEL); +#endif + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "chipone"); +} + +#ifdef CONFIG_GTP_LAST_TIME +static ssize_t timestamp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + ktime_t last_ktime; + struct timespec64 last_ts; + + cts_lock_device(&cts_data->cts_dev); + last_ktime = cts_data->last_event_time; + cts_data->last_event_time = 0; + cts_unlock_device(&cts_data->cts_dev); + + last_ts = ktime_to_timespec64(last_ktime); + return scnprintf(buf, PAGE_SIZE, "%lld.%ld\n", last_ts.tv_sec, last_ts.tv_nsec); +} +#endif + +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL +static ssize_t gesture_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%02x\n", cts_data->pdata->supported_gesture_type); +} + +static ssize_t gesture_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int value = 0; + int err = 0; + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + cts_lock_device(&cts_data->cts_dev); + err = sscanf(buf, "%d", &value); + if (err < 0) { + cts_err("error: Failed to convert value"); + return -EINVAL; + } + + switch (value) { + case 0x10: + cts_info("[%s %d]: zero tap disable", __func__, __LINE__); + break; + case 0x11: + break; + case 0x20: + cts_info("[%s %d]: single tap disable", __func__, __LINE__); + cts_data->s_tap_flag = 0; + break; + case 0x21: + cts_info("[%s %d]: single tap enable", __func__, __LINE__); + cts_data->s_tap_flag = 1; + break; + case 0x30: + cts_info("[%s %d]: double tap disable", __func__, __LINE__); + cts_data->d_tap_flag = 0; + break; + case 0x31: + cts_info("[%s %d]: double tap enable", __func__, __LINE__); + cts_data->d_tap_flag = 1; + break; + default: + cts_info("[%s %d]: unsupport gesture mode type", __func__, __LINE__); + } + cts_unlock_device(&cts_data->cts_dev); + + return count; +} +#endif + +#ifdef CTS_STOWED_MODE_EN +static ssize_t stowed_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + cts_info("Stowed state = %d.\n", cts_data->pdata->stowed_set); + return scnprintf(buf, PAGE_SIZE, "0x%d\n", cts_data->pdata->stowed_set); +} + +static ssize_t stowed_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned long mode = 0; + int ret = 0; + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + + ret = kstrtoul(buf, 0, &mode); + if (ret < 0) { + cts_err("error: Failed to convert value"); + return -EINVAL; + } + cts_lock_device(&cts_data->cts_dev); + cts_data->pdata->stowed_get = mode; + if(cts_data->pdata->stowed_set == mode) + { + cts_err("The mode = %lu is same, so not to write\n", mode); + ret = size; + goto exit; + } + if((atomic_read(&cts_data->post_suspended) == 1) && (cts_data->cts_dev.rtdata.gesture_wakeup_enabled == true)) + { + switch (mode) + { + case 1: + ret = enter_gesture_pocket_mode(&cts_data->cts_dev); + if(ret) + { + cts_info("enter stowed mode failed\n"); + goto exit; + } + break; + case 0: + ret = exit_gesture_pocket_mode(&cts_data->cts_dev); + if(ret) + { + cts_info("exit stowed mode failed\n"); + goto exit; + } + break; + default: + cts_info("unsupport stowed mode type\n"); + } + } + else + { + cts_info("Skip stowed mode, setting post_suspended:%d\n", cts_data->post_suspended); + ret = size; + goto exit; + } + cts_data->pdata->stowed_set = mode; + cts_err("Success to set stowed mode %lu\n", mode); + ret = size; +exit: + cts_unlock_device(&cts_data->cts_dev); + return ret; +} +#endif + +/* Attribute: vendor (RO) */ +static ssize_t ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct chipone_ts_data *ts = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%s%s\n%s%04x\n%s%04x\n", + "Product ID: ", ts->cts_dev.hwdata->name, + "Build ID: ", ts->cts_dev.fwdata.version + ? ts->cts_dev.fwdata.version + : ts->pdata->build_id, + "Config ID: ", ts->cts_dev.fwdata.ddi_version + ? ts->cts_dev.fwdata.ddi_version + : ts->pdata->config_id); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_RO(ic_ver), +#ifdef CONFIG_GTP_LAST_TIME + __ATTR_RO(timestamp), +#endif +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + __ATTR_RW(gesture), +#endif +#ifdef CTS_STOWED_MODE_EN + __ATTR_RW(stowed), +#endif + __ATTR_NULL +}; + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +/******************************************************* + *Description: + * Chipone touchscreen FW function class. file node + * initial function. + * + * return: + * Executive outcomes. 0---succeed. -1---failed. + *******************************************************/ +static int cts_fw_class_init(void *_data, bool create) +{ + struct chipone_ts_data *data = _data; + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + static struct class *touchscreen_class; + static struct device *ts_class_dev; + dev_t devno; + + cts_info("%s touchscreen class files", create ? "Add" : "Remove"); + + if (create) { + if (data->cts_dev.hwdata->name != NULL) + error = alloc_chrdev_region(&devno, 0, 1, data->cts_dev.hwdata->name); + else + error = alloc_chrdev_region(&devno, 0, 1, CFG_CTS_CHIP_NAME); + + if (error) { + cts_info("Alloc input devno failed %d", error); + return error; + } + + cts_info("Create class 'touchscreen'"); + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + cts_err("Create class 'touchscreen' failed %ld", + PTR_ERR(touchscreen_class)); + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + + if (data->cts_dev.hwdata->name != NULL) { + ts_class_dev = device_create(touchscreen_class, NULL, + devno, data, "%s", data->cts_dev.hwdata->name); + cts_info("Create device for IC: %s", data->cts_dev.hwdata->name); + } else { + ts_class_dev = device_create(touchscreen_class, NULL, + devno, data, "%s", CFG_CTS_CHIP_NAME); + cts_info("Create device '" CFG_CTS_CHIP_NAME "'"); + } + if (IS_ERR(ts_class_dev)) { + cts_err("Create device '" CFG_CTS_CHIP_NAME + "'failed %ld", PTR_ERR(ts_class_dev)); + error = PTR_ERR(ts_class_dev); + ts_class_dev = NULL; + return error; + } + + cts_info("Create attr files"); + for (i = 0; attrs[i].attr.name != NULL; ++i) { + cts_info(" Create attr file '%s'", attrs[i].attr.name); + error = device_create_file(ts_class_dev, &attrs[i]); + if (error) { + cts_err("Create attr file '%s' failed %d", + attrs[i].attr.name, error); + break; + } + } + + if (error) + goto device_destroy; + else + cts_info("Create /sys/class/touchscreen/ Succeeded"); + } else { + if (!touchscreen_class || !ts_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + cts_info("Remove device file '%s'", attrs[i].attr.name); + device_remove_file(ts_class_dev, &attrs[i]); + } + device_unregister(ts_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, devno); + ts_class_dev = NULL; + class_unregister(touchscreen_class); + cts_err("Creating touchscreen class failed %d", error); + + return -ENODEV; +} + +int cts_sysfs_add_device(struct device *dev) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + int ret, i; + + cts_info("Add device attr groups"); + + /*Low version kernel NOT support sysfs_create_groups() */ + for (i = 0; cts_dev_attr_groups[i]; i++) { + ret = sysfs_create_group(&dev->kobj, cts_dev_attr_groups[i]); + if (ret) { + while (--i >= 0) + sysfs_remove_group(&dev->kobj, cts_dev_attr_groups[i]); + break; + } + } + + if (ret) { + cts_err("Add device attr failed %d", ret); + return ret; + } + + ret = sysfs_create_link(NULL, &dev->kobj, "chipone-tddi"); + if (ret) + cts_err("Create sysfs link error:%d", ret); + + ret = cts_fw_class_init(cts_data, true); + if (ret) { + cts_err("Create touchscreen class failed. ret=%d", ret); + return ret; + } + + /* Only for sprd platform */ + cts_data->suspend_kobj = kobject_create_and_add("touchscreen", NULL); + if (cts_data->suspend_kobj == NULL) { + cts_err("Create touchscreen failed"); + return 0; + } + + ret = sysfs_create_group(cts_data->suspend_kobj, &ts_suspend_attr_group); + if (ret) { + kobject_put(cts_data->suspend_kobj); + cts_info("Create ts_suspend failed"); + return 0; + } + cts_info("Create ts_suspend successfully"); + + return 0; +} + +void cts_sysfs_remove_device(struct device *dev) +{ + struct chipone_ts_data *cts_data = dev_get_drvdata(dev); + int i; + + cts_info("Remove device attr groups"); + + sysfs_remove_link(NULL, "chipone-tddi"); + /*Low version kernel NOT support sysfs_remove_groups() */ + for (i = 0; cts_dev_attr_groups[i]; i++) + sysfs_remove_group(&dev->kobj, cts_dev_attr_groups[i]); + + cts_fw_class_init(cts_data, false); + + sysfs_remove_group(cts_data->suspend_kobj, &ts_suspend_attr_group); + kobject_put(cts_data->suspend_kobj); +} + +#undef SPLIT_LINE_STR +#undef ROW_NUM_FORMAT_STR +#undef COL_NUM_FORMAT_STR +#undef DATA_FORMAT_STR + +#endif /* CONFIG_CTS_SYSFS */ diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_sysfs.h b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_sysfs.h new file mode 100644 index 000000000000..c59959cfaef2 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_sysfs.h @@ -0,0 +1,24 @@ +#ifndef CTS_SYSFS_H +#define CTS_SYSFS_H + +#include "cts_config.h" + +struct device; + +extern u16 speed; + +#ifdef CONFIG_CTS_SYSFS +extern int cts_sysfs_add_device(struct device *dev); +extern void cts_sysfs_remove_device(struct device *dev); +#else /* CONFIG_CTS_SYSFS */ +static inline int cts_sysfs_add_device(struct device *dev) +{ + return -ENOTSUPP; +} + +static inline void cts_sysfs_remove_device(struct device *dev) +{ +} +#endif /* CONFIG_CTS_SYSFS */ + +#endif /* CTS_SYSFS_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_tcs.c b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_tcs.c new file mode 100644 index 000000000000..072d7a8129d8 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_tcs.c @@ -0,0 +1,1601 @@ +#include + +#define LOG_TAG "TCS" + +#include "cts_config.h" +#include "cts_firmware.h" +#include "cts_platform.h" +#include "cts_tcs.h" + +#define TCS_WR_ADDR (0xF0) +#define TCS_RD_ADDR (0xF1) + +#define TCS_READ_BIT (14) +#define TCS_WRITE_BIT (13) +/* raw touch info without data */ +#define TOUCH_INFO_SIZ sizeof(struct cts_device_touch_info) +#define TCS_REPLY_TAIL_SIZ sizeof(tcs_rx_tail) +#define INT_DATA_INFO_SIZ (64) +#define INT_DATA_TYPE_LEN_SIZ (4) +#define INT_DATA_VALID_INDEX (TOUCH_INFO_SIZ + 62) + +#define CTS_I2C_MAX_TRANS_SIZE (48) + + + +#pragma pack(1) +#ifdef CONFIG_CTS_I2C_HOST +typedef struct { + u16 cmd; + u16 datlen; + u8 check_l; + u8 check_h; +} tcs_tx_head; + +typedef struct { + u8 ecode; + u16 cmd; + u8 check_l; + u8 check_h; +} tcs_rx_tail; +#else /* CONFIG_CTS_I2C_HOST */ +typedef struct { + u8 addr; + u16 cmd; + u16 datlen; + u16 crc16; +} tcs_tx_head; +typedef struct { + u8 ecode; + u16 cmd; + u16 crc16; +} tcs_rx_tail; +#endif /* CONFIG_CTS_I2C_HOST */ +#pragma pack() + + +#define CMD_INFO_CHIP_FW_ID_RO (0x4003) +#define CMD_INFO_FW_VER_RO (0x4005) +#define CMD_INFO_TOUCH_XY_INFO_RO (0x4007) +#define CMD_INFO_MODULE_ID_RO (0x4011) +#define CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW (0x6101) +#define CMD_TP_DATA_READ_START_RO (0x4102) +#define CMD_TP_DATA_COORDINATES_RO (0x4103) +#define CMD_TP_DATA_RAW_RO (0x4104) +#define CMD_TP_DATA_DIFF_RO (0x4105) +#define CMD_TP_DATA_BASE_RO (0x4106) +#define CMD_TP_DATA_CNEG_RO (0x410a) +#define CMD_TP_DATA_WR_REG_RAM_SEQUENCE_WO (0x2114) +#define CMD_TP_DATA_WR_REG_RAM_BATCH_WO (0x2115) +#define CMD_TP_DATA_WR_DDI_REG_SEQUENCE_WO (0x2116) +#define CMD_GET_DATA_BY_POLLING_RO (0x4123) +#define CMD_TP_DATA_STATUS_RO (0x4125) +#define CMD_SYS_STS_READ_RO (0x4200) +#define CMD_SYS_STS_WORK_MODE_RW (0x6201) +#define CMD_SYS_STS_DAT_RDY_FLAG_RW (0x6203) +#define CMD_SYS_STS_PWR_STATE_RW (0x6204) +#define CMD_SYS_STS_CHARGER_PLUGIN_RW (0x6205) +#define CMD_SYS_STS_DDI_CODE_VER_RO (0x4206) +#define CMD_SYS_STS_DAT_TRANS_IN_NORMAL_RW (0x6207) +#define CMD_SYS_STS_VSTIM_LVL_RW (0x6208) +#define CMD_SYS_STS_CNEG_RDY_FLAG_RW (0x6211) +#define CMD_SYS_STS_EP_PLUGIN_RW (0x6213) +#define CMD_SYS_STS_RESET_WO (0x2216) +#define CMD_SYS_STS_INT_TEST_EN_RW (0x6217) +#define CMD_SYS_STS_SET_INT_PIN_RW (0x6218) +#define CMD_SYS_STS_CNEG_RD_EN_RW (0x6219) +#define CMD_SYS_STS_HI_SENSE_EN_RW (0x621a) +#define CMD_SYS_STS_INT_MODE_RW (0x6223) +#define CMD_SYS_STS_INT_KEEP_TIME_RW (0x6224) +#define CMD_SYS_STS_CURRENT_WORKMODE_RO (0x4233) +#define CMD_SYS_STS_DATA_CAPTURE_SUPPORT_RO (0x423f) +#define CMD_SYS_STS_DATA_CAPTURE_EN_RW (0x6240) +#define CMD_SYS_STS_DATA_CAPTURE_FUNC_MAP_RW (0x6241) +#define CMD_SYS_STS_PANEL_DIRECTION_RW (0x6242) +#define CMD_SYS_STS_GAME_MODE_RW (0x624e) +#define CMD_SYS_STS_POCKET_MODE_EN_RW (0x6250) +#define CMD_SYS_STS_PRODUCTION_TEST_EN_RW (0x6252) +#define CMD_GSTR_WAKEUP_EN_RW (0x6301) +#define CMD_GSTR_DAT_RDY_FLAG_GSTR_RW (0x631e) +#define CMD_GSTR_ENTER_MAP_RW (0x6328) +#define CMD_MNT_EN_RW (0x6401) +#define CMD_MNT_FORCE_EXIT_MNT_WO (0x2403) +#define CMD_DDI_ESD_EN_RW (0x6501) +#define CMD_DDI_ESD_OPTIONS_RW (0x6502) +#define CMD_CNEG_EN_RW (0x6601) +#define CMD_CNEG_OPTIONS_RW (0x6602) +#define CMD_COORD_FLIP_X_EN_RW (0x6702) +#define CMD_COORD_FLIP_Y_EN_RW (0x6703) +#define CMD_COORD_SWAP_AXES_EN_RW (0x6704) +#define CMD_PARA_PROXI_EN_RW (0x692a) +#define CMD_PARA_KUNCKLE_RW (0x694b) +#define CMD_OPENSHORT_EN_RW (0x6b01) +#define CMD_OPENSHORT_MODE_SEL_RW (0x6b02) +#define CMD_OPENSHORT_SHORT_SEL_RW (0x6b03) +#define CMD_OPENSHORT_SHORT_DISP_ON_EN_RW (0x6b04) + +void dump_spi(const char *prefix, u8 *data, size_t datalen) +{ + u8 str[1024]; + int offset = 0; + int i; + + offset += snprintf(str + offset, sizeof(str) - offset, "%s", prefix); + for (i = 0; i < datalen; i++) { + offset += snprintf(str + offset, sizeof(str) - offset, " %02x", data[i]); + } + cts_err("%s", str); +} + + +#ifdef CONFIG_CTS_I2C_HOST +static int cts_tcs_i2c_read_pack(u8 *tx, u16 cmd, u16 rdatalen) +{ + tcs_tx_head *txhdr = (tcs_tx_head *) tx; + u16 is_read; + int packlen = 0; + + is_read = cmd & BIT(TCS_READ_BIT); + if (0 == is_read) { + return packlen; + } + txhdr->cmd = (cmd & ~BIT(TCS_WRITE_BIT)); + txhdr->datlen = rdatalen; + txhdr->check_l = ~((txhdr->cmd & 0xff) + + ((txhdr->cmd >> 8) & 0xff) + + (rdatalen & 0xff) + + ((rdatalen >> 8) & 0xff)); + txhdr->check_h = 1; + packlen += sizeof(tcs_tx_head); + + return packlen; +} + +static int cts_tcs_i2c_write_pack(u8 *tx, u16 cmd, u8 *wdata, u16 wlen) +{ + tcs_tx_head *txhdr = (tcs_tx_head *) tx; + int packlen = 0; + u8 check_l = 0; + u16 is_write; + int i; + + is_write = cmd & BIT(TCS_WRITE_BIT); + if (0 == is_write) { + return packlen; + } + txhdr->cmd = (cmd & ~BIT(TCS_READ_BIT)); + txhdr->datlen = wlen; + txhdr->check_l = ~((txhdr->cmd & 0xff) + + ((txhdr->cmd >> 8) & 0xff) + + (wlen & 0xff) + + ((wlen >> 8) & 0xff)); + txhdr->check_h = 1; + packlen += sizeof(tcs_tx_head); + + if (wlen > 0) { + memcpy(tx + sizeof(tcs_tx_head), wdata, wlen); + for (i = 0; i < wlen; i++) + check_l += wdata[i]; + *(tx + sizeof(tcs_tx_head) + wlen) = ~check_l; + *(tx + sizeof(tcs_tx_head) + wlen + 1) = 1; + packlen += wlen + 2; + } + + return packlen; +} + +static int cts_tcs_i2c_read(const struct cts_device *cts_dev, u16 cmd, + u8 *buf, size_t len) +{ + int txlen; + int size; + u16 cmd_recv; + u16 cmd_send; + u8 error_code; + int ret; + + size = len + sizeof(tcs_rx_tail); + + txlen = cts_tcs_i2c_read_pack(cts_dev->pdata->i2c_fifo_buf, cmd, len); + + ret = cts_plat_i2c_read(cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR, + cts_dev->pdata->i2c_fifo_buf, txlen, cts_dev->pdata->i2c_rbuf, size, + 3, 10); + + if (ret == 0) { + error_code = *(cts_dev->pdata->i2c_rbuf + size - 5); + cmd_send = get_unaligned_le16(cts_dev->pdata->i2c_fifo_buf); + cmd_recv = get_unaligned_le16(cts_dev->pdata->i2c_rbuf + size - 4); + if (error_code != 0) { + cts_err("error code:0x%02x, send %04x, %04x recv", error_code, + cmd_send, cmd_recv); + return -EIO; + } + memcpy(buf, cts_dev->pdata->i2c_rbuf, len); + } + + return ret; +} + +static int cts_tcs_i2c_write(const struct cts_device *cts_dev, + u16 cmd, u8 *wbuf, size_t wlen) +{ + int txlen; + + txlen = cts_tcs_i2c_write_pack(cts_dev->pdata->i2c_fifo_buf, cmd, wbuf, wlen); + + return cts_plat_i2c_write(cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR, + cts_dev->pdata->i2c_fifo_buf, txlen, 3, 10); +} + +static int cts_tcs_i2c_read_touch(const struct cts_device *cts_dev, + u16 cmd, u8 *buf, size_t len) +{ + u16 cmd_recv; + u16 cmd_send; + u8 error_code; + int txlen; + int ret; + + txlen = cts_tcs_i2c_read_pack(cts_dev->pdata->i2c_fifo_buf, + cmd, len - sizeof(tcs_rx_tail)); + ret = cts_plat_i2c_read(cts_dev->pdata, CTS_DEV_NORMAL_MODE_I2CADDR, + cts_dev->pdata->i2c_fifo_buf, txlen, cts_dev->pdata->i2c_rbuf, len, 3, 10); + if (ret == 0) { + error_code = *(cts_dev->pdata->i2c_rbuf + len - 5); + cmd_send = get_unaligned_le16(cts_dev->pdata->i2c_fifo_buf); + cmd_recv = get_unaligned_le16(buf + len - 4); + if (error_code != 0) { + cts_err("error code:0x%02x"); + return -EIO; + } + memcpy(buf, cts_dev->pdata->i2c_rbuf, len - sizeof(tcs_rx_tail)); + } + return ret; +} +#else +static int cts_tcs_spi_xtrans(const struct cts_device *cts_dev, u8 *tx, + size_t txlen, u8 *rx, size_t rxlen) +{ + struct chipone_ts_data *cts_data = container_of(cts_dev, + struct chipone_ts_data, cts_dev); + struct spi_transfer xfer[2]; + struct spi_message msg; + u16 crc16_recv, crc16_calc; + u16 cmd_recv, cmd_send; + int ret; + + memset(&xfer[0], 0, sizeof(struct spi_transfer)); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + xfer[0].delay_usecs = 0; +#endif + xfer[0].speed_hz = cts_dev->pdata->spi_speed * 1000u; + xfer[0].tx_buf = tx; + xfer[0].rx_buf = NULL; + xfer[0].len = txlen; + + spi_message_init(&msg); + spi_message_add_tail(&xfer[0], &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 0); +#endif + ret = spi_sync(cts_data->spi_client, &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 1); +#endif + if (ret < 0) { + cts_err("spi_sync xfer[0] failed: %d", ret); + return ret; + } + udelay(100); + + memset(&xfer[1], 0, sizeof(struct spi_transfer)); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + xfer[1].delay_usecs = 0; +#endif + xfer[1].speed_hz = cts_dev->pdata->spi_speed * 1000u; + xfer[1].tx_buf = NULL; + xfer[1].rx_buf = rx; + xfer[1].len = rxlen; + + spi_message_init(&msg); + spi_message_add_tail(&xfer[1], &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 0); +#endif + ret = spi_sync(cts_data->spi_client, &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 1); +#endif + if (ret < 0) { + cts_err("spi_sync xfer[1] failed: %d", ret); + return ret; + } + + cmd_recv = get_unaligned_le16(rx +rxlen - 4); + cmd_send = get_unaligned_le16(tx + 1); + if (cmd_recv != cmd_send) { + cts_dbg("cmd check error, send %04x != %04x recv", cmd_send, cmd_recv); + // return -EIO; + } + + crc16_recv = get_unaligned_le16(rx + rxlen - 2); + crc16_calc = cts_crc16(rx, rxlen - 2); + if (crc16_recv != crc16_calc) { + cts_err("crc error: recv %04x != %04x calc", crc16_recv, crc16_calc); + return -EIO; + } + udelay(100); + + return 0; +} + +static int cts_tcs_spi_xtrans_1_cs(const struct cts_device *cts_dev, u8 *tx, + size_t txlen, u8 *rx, size_t rxlen) +{ + struct chipone_ts_data *cts_data = container_of(cts_dev, + struct chipone_ts_data, cts_dev); + struct spi_transfer xfer[1]; + struct spi_message msg; + u16 crc16_recv, crc16_calc; + u16 cmd_recv, cmd_send; + int ret; + + memset(&xfer[0], 0, sizeof(struct spi_transfer)); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + xfer[0].delay_usecs = 0; +#endif + xfer[0].speed_hz = cts_dev->pdata->spi_speed * 1000u; + xfer[0].tx_buf = tx; + xfer[0].rx_buf = rx; + xfer[0].len = txlen > rxlen ? txlen : rxlen; + + spi_message_init(&msg); + spi_message_add_tail(&xfer[0], &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 0); +#endif + ret = spi_sync(cts_data->spi_client, &msg); +#ifdef CFG_CTS_MANUAL_CS + cts_plat_set_cs(cts_dev->pdata, 1); +#endif + if (ret < 0) { + cts_err("spi_sync xfer[0] failed: %d", ret); + return ret; + } + + cmd_recv = get_unaligned_le16(rx + rxlen - 4); + cmd_send = get_unaligned_le16(tx + 1); + if (cmd_recv != cmd_send) { + cts_dbg("cmd check error, send %04x != %04x recv", cmd_send, cmd_recv); + // return -EIO; + } + + crc16_recv = get_unaligned_le16(rx + rxlen - 2); + crc16_calc = cts_crc16(rx, rxlen - 2); + if (crc16_recv != crc16_calc) { + cts_err("1cs crc error: recv %04x != %04x calc", crc16_recv, crc16_calc); + return -EIO; + } + + return 0; +} + +static int cts_tcs_spi_read_pack(u8 *tx, u16 cmd, u16 rdatalen) +{ + tcs_tx_head *txhdr = (tcs_tx_head *) tx; + u16 is_read; + int packlen = 0; + u16 crc16; + + is_read = cmd & BIT(TCS_READ_BIT); + if (0 == is_read) { + return packlen; + } + txhdr->addr = TCS_RD_ADDR; + txhdr->cmd = (cmd & ~BIT(TCS_WRITE_BIT)); + txhdr->datlen = rdatalen; + crc16 = cts_crc16((const u8 *)txhdr, offsetof(tcs_tx_head, crc16)); + txhdr->crc16 = crc16; + packlen += sizeof(tcs_tx_head); + + return packlen; +} + +static int cts_tcs_spi_write_pack(u8 *tx, u16 cmd, u8 *wdata, u16 wdatalen) +{ + tcs_tx_head *txhdr = (tcs_tx_head *) tx; + u16 is_write; + int packlen = 0; + u16 crc16; + + is_write = cmd & BIT(TCS_WRITE_BIT); + if (0 == is_write) { + return packlen; + } + txhdr->addr = TCS_WR_ADDR; + txhdr->cmd = (cmd & ~BIT(TCS_READ_BIT)); + txhdr->datlen = wdatalen; + crc16 = cts_crc16((const u8 *)txhdr, offsetof(tcs_tx_head, crc16)); + txhdr->crc16 = crc16; + packlen += sizeof(tcs_tx_head); + + if (wdatalen > 0) { + memcpy(tx + sizeof(tcs_tx_head), wdata, wdatalen); + crc16 = cts_crc16(wdata, wdatalen); + *(tx + sizeof(tcs_tx_head) + wdatalen) = ((crc16 >> 0) & 0xFF); + *(tx + sizeof(tcs_tx_head) + wdatalen + 1) = ((crc16 >> 8) & 0xFF); + packlen += wdatalen + sizeof(crc16); + } + + return packlen; +} + +static int cts_tcs_spi_read(const struct cts_device *cts_dev, + u16 cmd, u8 *rdata, size_t rdatalen) +{ + int txlen; + int ret; + + txlen = cts_tcs_spi_read_pack(cts_dev->pdata->spi_tx_buf, cmd, rdatalen); + if (0 == txlen) { + cts_err("spi read pack len: %d", txlen); + return -EINVAL; + } + ret = cts_tcs_spi_xtrans(cts_dev, cts_dev->pdata->spi_tx_buf, txlen, + cts_dev->pdata->spi_rx_buf, rdatalen + sizeof(tcs_rx_tail)); + if (ret) { + return ret; + } + + memcpy(rdata, cts_dev->pdata->spi_rx_buf, rdatalen); + + return ret; +} + +static int cts_tcs_spi_read_1_cs(struct cts_device *cts_dev, + u16 cmd, u8 *rdata, size_t rdatalen) +{ + int txlen; + int ret; + + txlen = cts_tcs_spi_read_pack(cts_dev->pdata->spi_tx_buf, cmd, rdatalen); + if (0 == txlen) { + cts_err("spi read pack len: %d", txlen); + return -EINVAL; + } + ret = cts_tcs_spi_xtrans_1_cs(cts_dev, cts_dev->pdata->spi_tx_buf, txlen, + cts_dev->pdata->spi_rx_buf, rdatalen); + if (ret) { + return ret; + } + + memcpy(rdata, cts_dev->pdata->spi_rx_buf, rdatalen); + + return ret; +} + +static int cts_tcs_spi_write(const struct cts_device *cts_dev, + u16 cmd, u8 *data, size_t wlen) +{ + int txlen; + int ret; + + txlen = cts_tcs_spi_write_pack(cts_dev->pdata->spi_tx_buf, cmd, data, wlen); + if (0 == txlen) { + cts_err("spi write pack len: %d", txlen); + return -EINVAL; + } + ret = cts_tcs_spi_xtrans(cts_dev, cts_dev->pdata->spi_tx_buf, txlen, + cts_dev->pdata->spi_rx_buf, sizeof(tcs_rx_tail)); + return ret; +} +#endif + +int cts_tcs_tool_xtrans(const struct cts_device *cts_dev, u8 *tx, size_t txlen, + u8 *rx, size_t rxlen) +{ +#ifdef CONFIG_CTS_I2C_HOST + cts_err("Not implement!!!"); + return 0; +#else + return cts_tcs_spi_xtrans(cts_dev, tx, txlen, rx, rxlen); +#endif +} + +int cts_tcs_read(const struct cts_device *cts_dev, + u16 cmd, u8 *buf, size_t len) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_tcs_i2c_read(cts_dev, cmd, buf, len); +#else + return cts_tcs_spi_read(cts_dev, cmd, buf, len); +#endif +} +int cts_tcs_write(const struct cts_device *cts_dev, + u16 cmd, u8 *buf, size_t len) +{ +#ifdef CONFIG_CTS_I2C_HOST + return cts_tcs_i2c_write(cts_dev, cmd, buf, len); +#else + return cts_tcs_spi_write(cts_dev, cmd, buf, len); +#endif +} + +int cts_tcs_get_fw_ver(const struct cts_device *cts_dev, u16 *fwver) +{ + u8 buf[4] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_FW_VER_RO, buf, sizeof(buf)); + if (ret == 0) { + *fwver = buf[0] | (buf[1] << 8); + } + return ret; +} + +int cts_tcs_get_lib_ver(const struct cts_device *cts_dev, u16 *libver) +{ + u8 buf[4] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_FW_VER_RO, buf, sizeof(buf)); + if (ret == 0) { + *libver = buf[2] | (buf[3] << 8); + } + return ret; +} + +int cts_tcs_get_fw_id(const struct cts_device *cts_dev, u16 *fwid) +{ + u8 buf[4] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_CHIP_FW_ID_RO, buf, sizeof(buf)); + if (ret == 0) { + *fwid = buf[0] | (buf[1] << 8); + } + + return ret; +} + +int cts_tcs_get_ddi_ver(const struct cts_device *cts_dev, u8 *ddiver) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_DDI_CODE_VER_RO, buf, sizeof(buf)); + if (ret == 0) { + *ddiver = buf[0]; + } + return ret; +} + +int cts_tcs_get_res_x(const struct cts_device *cts_dev, u16 *res_x) +{ + u8 buf[10] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_TOUCH_XY_INFO_RO, buf, sizeof(buf)); + if (ret == 0) { + *res_x = buf[0] | (buf[1] << 8); + } + return ret; +} + +int cts_tcs_get_res_y(const struct cts_device *cts_dev, u16 *res_y) +{ + u8 buf[10] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_TOUCH_XY_INFO_RO, buf, sizeof(buf)); + if (ret == 0) { + *res_y = buf[2] | (buf[3] << 8); + } + return ret; +} + +int cts_tcs_get_rows(const struct cts_device *cts_dev, u8 *rows) +{ + u8 buf[10] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_TOUCH_XY_INFO_RO, buf, sizeof(buf)); + if (ret == 0) { + *rows = buf[5]; + } + return ret; +} + +int cts_tcs_get_cols(const struct cts_device *cts_dev, u8 *cols) +{ + u8 buf[10] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_TOUCH_XY_INFO_RO, buf, sizeof(buf)); + if (ret == 0) { + *cols = buf[4]; + } + return ret; +} + +int cts_tcs_get_flip_x(const struct cts_device *cts_dev, bool *flip_x) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_COORD_FLIP_X_EN_RW, buf, sizeof(buf)); + if (ret == 0) { + *flip_x = !!buf[0]; + } + return ret; +} + +int cts_tcs_get_flip_y(const struct cts_device *cts_dev, bool *flip_y) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_COORD_FLIP_Y_EN_RW, buf, sizeof(buf)); + if (ret == 0) { + *flip_y = !!buf[0]; + } + return ret; +} + +int cts_tcs_get_swap_axes(const struct cts_device *cts_dev, bool *swap_axes) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_COORD_SWAP_AXES_EN_RW, buf, sizeof(buf)); + if (ret == 0) { + *swap_axes = !!buf[0]; + } + return ret; +} + +int cts_tcs_get_int_mode(const struct cts_device *cts_dev, u8 *int_mode) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_INT_MODE_RW, buf, sizeof(buf)); + if (ret == 0) { + *int_mode = buf[0]; + } + return ret; +} + +int cts_tcs_get_int_keep_time(const struct cts_device *cts_dev, + u16 *int_keep_time) +{ + u8 buf[2] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_INT_KEEP_TIME_RW, buf, sizeof(buf)); + if (ret == 0) { + *int_keep_time = (buf[0] | (buf[1] << 8)); + } + return ret; + +} + +int cts_tcs_get_rawdata_target(const struct cts_device *cts_dev, + u16 *rawdata_target) +{ + u8 buf[2] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_CNEG_OPTIONS_RW, buf, sizeof(buf)); + if (ret == 0) { + *rawdata_target = (buf[0] | (buf[1] << 8)); + } + return ret; + +} + +int cts_tcs_get_esd_method(const struct cts_device *cts_dev, u8 *esd_method) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_DDI_ESD_OPTIONS_RW, buf, sizeof(buf)); + if (ret == 0) { + *esd_method = buf[0]; + } + return ret; +} + +int cts_tcs_get_esd_protection(const struct cts_device *cts_dev, + u8 *esd_protection) +{ + u8 buf[4] = { 0 }; + int ret; + + buf[0] = 0x01; + buf[1] = 0x56; + buf[2] = 0x81; + buf[3] = 0x00; + + ret = cts_tcs_write(cts_dev, CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, + buf, sizeof(buf)); + if (ret != 0) + return ret; + + ret = cts_tcs_read(cts_dev, CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, + esd_protection, sizeof(u8)); + + return ret; +} + +int cts_tcs_get_data_ready_flag(const struct cts_device *cts_dev, u8 *ready) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_DAT_RDY_FLAG_RW, buf, sizeof(buf)); + if (ret == 0) { + *ready = buf[0]; + } + return ret; +} + +int cts_tcs_clr_gstr_ready_flag(const struct cts_device *cts_dev) +{ + u8 ready = 0; + + return cts_tcs_write(cts_dev, CMD_GSTR_DAT_RDY_FLAG_GSTR_RW, + &ready, sizeof(ready)); +} + +int cts_tcs_clr_data_ready_flag(const struct cts_device *cts_dev) +{ + u8 ready = 0; + + return cts_tcs_write(cts_dev, CMD_SYS_STS_DAT_RDY_FLAG_RW, + &ready, sizeof(ready)); +} + +int cts_tcs_read_hw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + u8 buf[4] = { 0 }; + int ret; + + buf[0] = 1; + buf[1] = ((addr >> 0) & 0xFF); + buf[2] = ((addr >> 8) & 0xFF); + buf[3] = ((addr >> 16) & 0xFF); + + ret = cts_tcs_write(cts_dev, CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, + buf, sizeof(buf)); + if (ret != 0) + return ret; + + ret = cts_tcs_read(cts_dev, CMD_TP_DATA_READ_START_RO, regbuf, size); + + return ret; +} + +int cts_tcs_write_hw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + u8 *buf; + int ret; + + buf = kmalloc(size + 6, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + buf[0] = ((size >> 0) & 0xFF); + buf[1] = ((size >> 8) & 0xFF); + buf[2] = ((addr >> 0) & 0xFF); + buf[3] = ((addr >> 8) & 0xFF); + buf[4] = ((addr >> 16) & 0xFF); + buf[5] = 0x00; + memcpy(buf + 6, regbuf, size); + + ret = cts_tcs_write(cts_dev, CMD_TP_DATA_WR_REG_RAM_SEQUENCE_WO, + buf, size + 6); + if (ret != 0) { + kfree(buf); + return ret; + } + + kfree(buf); + + return ret; +} + +int cts_tcs_read_ddi_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + u8 buf[2] = { 0 }; + int ret; + + buf[0] = 2; + buf[1] = addr; + + ret = cts_tcs_write(cts_dev, CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, + buf, sizeof(buf)); + if (ret != 0) + return ret; + + ret = cts_tcs_read(cts_dev, CMD_TP_DATA_READ_START_RO, regbuf, size); + if (ret != 0) + return ret; + + return 0; +} + +int cts_tcs_write_ddi_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + u8 *buf; + int ret; + + buf = kmalloc(size + 6, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + buf[0] = ((size >> 0) & 0xFF); + buf[1] = ((size >> 8) & 0xFF); + buf[2] = addr; + buf[3] = 0; + buf[4] = 0; + buf[5] = 0; + memcpy(buf + 6, regbuf, size); + + ret = cts_tcs_write(cts_dev, CMD_TP_DATA_WR_DDI_REG_SEQUENCE_WO, + buf, size + 6); + if (ret != 0) { + kfree(buf); + return ret; + } + + kfree(buf); + + return ret; + +} + +int cts_tcs_read_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + return cts_tcs_read(cts_dev, CMD_SYS_STS_READ_RO, regbuf, size); +} + +int cts_tcs_write_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size) +{ + u8 buf[4] = { 0 }; + int ret; + + buf[0] = 0x01; + buf[1] = ((addr >> 0) & 0xFF); + buf[2] = ((addr >> 8) & 0xFF); + buf[3] = ((addr >> 16) & 0xFF); + + ret = cts_tcs_write(cts_dev, CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, + buf, sizeof(buf)); + if (ret != 0) + return ret; + + ret = cts_tcs_write(cts_dev, CMD_TP_DATA_OFFSET_AND_TYPE_CFG_RW, + regbuf, sizeof(buf)); + if (ret != 0) + return ret; + + return ret; +} + +int cts_tcs_calc_int_data_size(struct cts_device *cts_dev) +{ +#define INT_DATA_TYPE_U8_SIZ \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * sizeof(u8)) +#define INT_DATA_TYPE_U16_SIZ \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * sizeof(u16)) + + int data_size = TOUCH_INFO_SIZ + TCS_REPLY_TAIL_SIZ; + u16 data_types = cts_dev->fwdata.int_data_types; + u8 data_method = cts_dev->fwdata.int_data_method; + + if (data_method == INT_DATA_METHOD_NONE) { + cts_dev->fwdata.int_data_size = data_size; + return 0; + } else if (data_method == INT_DATA_METHOD_DEBUG) { + data_size += INT_DATA_INFO_SIZ; + data_size += (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U16_SIZ); + cts_dev->fwdata.int_data_size = data_size; + return 0; + } + + cts_info("data_method:%d, data_type:%d", data_method, data_types); + if (data_types != INT_DATA_TYPE_NONE) { + data_size += INT_DATA_INFO_SIZ; + if ((data_types & INT_DATA_TYPE_RAWDATA)) { + data_size += (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U16_SIZ); + } + if ((data_types & INT_DATA_TYPE_MANUAL_DIFF)) { + data_size += (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U16_SIZ); + } + if ((data_types & INT_DATA_TYPE_REAL_DIFF)) { + data_size += (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U16_SIZ); + } + if ((data_types & INT_DATA_TYPE_NOISE_DIFF)) { + data_size += (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U16_SIZ); + } + if ((data_types & INT_DATA_TYPE_BASEDATA)) { + data_size += (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U16_SIZ); + } + if ((data_types & INT_DATA_TYPE_CNEGDATA)) { + data_size += (INT_DATA_TYPE_LEN_SIZ + INT_DATA_TYPE_U8_SIZ); + } + } + + cts_info("data_size: %d", data_size); + cts_dev->fwdata.int_data_size = data_size; + return 0; +} + +int cts_tcs_polling_data(struct cts_device *cts_dev, + u8 *buf, size_t size) +{ + int retries = 100; + u8 ready = 0; + int ret; + + size_t data_size = cts_dev->fwdata.int_data_size; + + if (!data_size) + data_size = TOUCH_INFO_SIZ + TCS_REPLY_TAIL_SIZ; + + do { + ret = cts_tcs_get_data_ready_flag(cts_dev, &ready); + if (!ret && ready) + break; + mdelay(10); + } while (!ready && --retries); + + if (!ready) { + cts_err("time out wait for data rdy"); + return -EIO; + } + + retries = 3; + do { +#ifdef CONFIG_CTS_I2C_HOST + ret = cts_tcs_i2c_read_touch(cts_dev, CMD_GET_DATA_BY_POLLING_RO, + cts_dev->int_data, data_size); +#else + ret = cts_tcs_spi_read_1_cs(cts_dev, CMD_GET_DATA_BY_POLLING_RO, + cts_dev->int_data, data_size); +#endif + mdelay(1); + } while (ret && --retries); + + if (cts_tcs_clr_data_ready_flag(cts_dev)) + cts_err("Clear data ready flag failed"); + + return ret; +} + +int cts_tcs_polling_test_data(struct cts_device *cts_dev, + u8 *buf, size_t size) +{ + int offset = TOUCH_INFO_SIZ + INT_DATA_INFO_SIZ + INT_DATA_TYPE_LEN_SIZ; + int retries = 5; + int ret; + + while (retries--) { + ret = cts_tcs_polling_data(cts_dev, buf, size); + if (!ret) { + memcpy(buf, cts_dev->int_data + offset, size); + break; + } + } + + return ret; +} + +static int polling_data(struct cts_device *cts_dev, u8 *buf, size_t size, + enum int_data_type type) +{ + u8 old_int_data_method; + u16 old_int_data_types; + int offset = TOUCH_INFO_SIZ + INT_DATA_INFO_SIZ + INT_DATA_TYPE_LEN_SIZ; + int retries = 5; + int ret; + + old_int_data_types = cts_dev->fwdata.int_data_types; + old_int_data_method = cts_dev->fwdata.int_data_method; + + cts_set_int_data_types(cts_dev, type); + cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); + + while (retries--) { + ret = cts_tcs_polling_data(cts_dev, buf, size); + if (!ret) { + memcpy(buf, cts_dev->int_data + offset, size); + break; + } + } + + cts_set_int_data_method(cts_dev, old_int_data_method); + cts_set_int_data_types(cts_dev, old_int_data_types); + + return ret; +} + +int cts_test_polling_rawdata(struct cts_device *cts_dev, u8 *buf, size_t size) +{ + int offset = TOUCH_INFO_SIZ + INT_DATA_INFO_SIZ + INT_DATA_TYPE_LEN_SIZ; + int retries = 5; + int ret; + + while (retries--) { + ret = cts_tcs_polling_data(cts_dev, buf, size); + if (!ret) { + if (cts_dev->int_data[INT_DATA_VALID_INDEX]) { + memcpy(buf, cts_dev->int_data + offset, size); + break; + } + } + } + + return ret; +} + +int cts_tcs_top_get_rawdata(struct cts_device *cts_dev, u8 *buf, size_t size) +{ + return polling_data(cts_dev, buf, size, INT_DATA_TYPE_RAWDATA); +} + +int cts_tcs_top_get_manual_diff(struct cts_device *cts_dev, u8 *buf, size_t size) +{ + return polling_data(cts_dev, buf, size, INT_DATA_TYPE_MANUAL_DIFF); +} + +int cts_tcs_top_get_real_diff(struct cts_device *cts_dev, u8 *buf, size_t size) +{ + return polling_data(cts_dev, buf, size, INT_DATA_TYPE_REAL_DIFF); +} + +int cts_tcs_top_get_noise_diff(struct cts_device *cts_dev, u8 *buf, size_t size) +{ + return polling_data(cts_dev, buf, size, INT_DATA_TYPE_NOISE_DIFF); +} + +int cts_tcs_top_get_basedata(struct cts_device *cts_dev, u8 *buf, size_t size) +{ + return polling_data(cts_dev, buf, size, INT_DATA_TYPE_BASEDATA); +} + +int cts_tcs_top_get_cnegdata(struct cts_device *cts_dev, u8 *buf, size_t size) +{ + return polling_data(cts_dev, buf, size, INT_DATA_TYPE_CNEGDATA); +} + +int cts_tcs_reset_device(const struct cts_device *cts_dev) +{ +#ifdef CONFIG_CTS_ICTYPE_ICNL9922 + u8 buf[2] = { 0x01, 0xfe }; + int ret; + + cts_info("ICNL9922 use software reset"); + /* normal */ + cts_info("tp reset in normal mode"); + ret = cts_tcs_write(cts_dev, CMD_SYS_STS_RESET_WO, + buf, sizeof(buf)); + if (!ret) { + mdelay(40); + return 0; + } + /* program */ + cts_info("tp reset in program mode"); + ret = cts_hw_reg_writeb(cts_dev, CTS_DEV_HW_REG_RESET_CONFIG, 0xfd); + if (!ret) { + mdelay(40); + return 0; + } + return ret; +#else + return cts_plat_reset_device(cts_dev->pdata); +#endif +} + +int cts_tcs_set_int_test(const struct cts_device *cts_dev, u8 enable) +{ + return cts_tcs_write(cts_dev, CMD_SYS_STS_INT_TEST_EN_RW, &enable, + sizeof(enable)); +} + +int cts_tcs_set_int_pin(const struct cts_device *cts_dev, u8 high) +{ + return cts_tcs_write(cts_dev, CMD_SYS_STS_SET_INT_PIN_RW, &high, + sizeof(high)); +} + +int cts_tcs_get_module_id(const struct cts_device *cts_dev, u32 *modId) +{ + u8 buf[4] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_INFO_MODULE_ID_RO, buf, sizeof(buf)); + if (ret == 0) { + *modId = *(u32 *)buf; + } + return ret; +} + + +int cts_tcs_get_gestureinfo(struct cts_device *cts_dev, + struct cts_device_gesture_info *gesture_info) +{ + size_t size = sizeof(*gesture_info) + TCS_REPLY_TAIL_SIZ; + int ret; + +#ifdef CONFIG_CTS_I2C_HOST + ret = cts_tcs_i2c_read_touch(cts_dev, CMD_TP_DATA_COORDINATES_RO, + cts_dev->int_data, size); +#else + ret = cts_tcs_spi_read_1_cs(cts_dev, CMD_TP_DATA_COORDINATES_RO, + cts_dev->int_data, size); +#endif + if (cts_tcs_clr_gstr_ready_flag(cts_dev)) { + cts_err("Clear gesture ready flag failed"); + } + if (ret < 0) { + cts_err("Get gesture info failed: ret=%d", ret); + return ret; + } + + memcpy(gesture_info, cts_dev->int_data, sizeof(*gesture_info)); + + return ret; +} + +int cts_tcs_get_touchinfo(struct cts_device *cts_dev, + struct cts_device_touch_info *touch_info) +{ + size_t size = cts_dev->fwdata.int_data_size; + int ret; + + if (!size) { + size = TOUCH_INFO_SIZ + TCS_REPLY_TAIL_SIZ; + } + + memset(touch_info, 0, sizeof(*touch_info)); + +#ifdef CONFIG_CTS_I2C_HOST + ret = cts_tcs_i2c_read_touch(cts_dev, CMD_TP_DATA_COORDINATES_RO, + cts_dev->int_data, size); + if (unlikely(ret != 0)) { + cts_err("cts_tcs_i2c_read_touch failed"); + return ret; + } +#else + ret = cts_tcs_spi_read_1_cs(cts_dev, CMD_TP_DATA_COORDINATES_RO, + cts_dev->int_data, size); + if (unlikely(ret != 0)) { + cts_err("tcs_spi_read_1_cs failed"); + return ret; + } +#endif + memcpy(touch_info, cts_dev->int_data, sizeof(*touch_info)); + +#ifdef CFG_CTS_HEARTBEAT_MECHANISM + if (unlikely((touch_info->debug_msg.reset_flag & 0xFFFFFF) != 0xFFFFFF + || (touch_info->debug_msg.reset_flag & 0xFFFF) != 0xFFFF)) { + cts_err("reset flag:0x%08x error", touch_info->debug_msg.reset_flag); + cts_show_touch_debug_msg(&touch_info->debug_msg); + } +#endif + + return ret; +} + +int cts_tcs_get_touch_status(struct cts_device *cts_dev) +{ + return cts_tcs_read(cts_dev, CMD_TP_DATA_STATUS_RO, + cts_dev->int_data, TOUCH_INFO_SIZ); +} + +int cts_tcs_get_workmode(const struct cts_device *cts_dev, u8 *workmode) +{ + u8 buf = 0; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_CURRENT_WORKMODE_RO, + &buf, sizeof(buf)); + if (ret == 0) { + *workmode = buf; + } + + return ret; +} + +int cts_tcs_set_workmode(const struct cts_device *cts_dev, u8 workmode) +{ + return cts_tcs_write(cts_dev, CMD_SYS_STS_WORK_MODE_RW, + &workmode, sizeof(workmode)); +} + +int cts_tcs_get_curr_mode(const struct cts_device *cts_dev, u8 *currmode) +{ + u8 buf = 0; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_CURRENT_WORKMODE_RO, + &buf, sizeof(buf)); + if (ret == 0) { + *currmode = buf; + } + + return ret; +} + +int cts_tcs_set_openshort_mode(const struct cts_device *cts_dev, u8 mode) +{ + return cts_tcs_write(cts_dev, CMD_OPENSHORT_MODE_SEL_RW, &mode, + sizeof(mode)); +} + +int cts_tcs_set_tx_vol(const struct cts_device *cts_dev, u8 txvol) +{ + return cts_tcs_write(cts_dev, CMD_SYS_STS_VSTIM_LVL_RW, &txvol, + sizeof(txvol)); +} + +int cts_tcs_set_short_test_type(const struct cts_device *cts_dev, + u8 short_type) +{ + return cts_tcs_write(cts_dev, CMD_OPENSHORT_SHORT_SEL_RW, + &short_type, sizeof(short_type)); +} + +int cts_tcs_is_openshort_enabled(const struct cts_device *cts_dev, u8 *enabled) +{ + u8 buf = 0; + int ret; + + ret = cts_tcs_write(cts_dev, CMD_OPENSHORT_EN_RW, &buf, sizeof(buf)); + if (ret == 0) { + *enabled = buf; + } + + return ret; +} + +int cts_tcs_set_openshort_enable(const struct cts_device *cts_dev, u8 enable) +{ + return cts_tcs_write(cts_dev, CMD_OPENSHORT_EN_RW, &enable, sizeof(enable)); +} + +int cts_tcs_set_esd_enable(const struct cts_device *cts_dev, u8 enable) +{ + return cts_tcs_write(cts_dev, CMD_DDI_ESD_EN_RW, &enable, sizeof(enable)); +} + +int cts_tcs_is_cneg_enabled(const struct cts_device *cts_dev, u8 *enabled) +{ + return cts_tcs_read(cts_dev, CMD_CNEG_EN_RW, enabled, sizeof(*enabled)); +} + +int cts_tcs_is_mnt_enabled(const struct cts_device *cts_dev, u8 *enabled) +{ + return cts_tcs_read(cts_dev, CMD_MNT_EN_RW, enabled, sizeof(*enabled)); +} + +int cts_tcs_set_cneg_enable(const struct cts_device *cts_dev, u8 enable) +{ + return cts_tcs_write(cts_dev, CMD_CNEG_EN_RW, &enable, sizeof(enable)); +} + +int cts_tcs_set_mnt_enable(const struct cts_device *cts_dev, u8 enable) +{ + return cts_tcs_write(cts_dev, CMD_MNT_EN_RW, &enable, sizeof(enable)); +} + +int cts_tcs_is_display_on(const struct cts_device *cts_dev, u8 *display_on) +{ + u8 buf = 0; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_OPENSHORT_SHORT_DISP_ON_EN_RW, + &buf, sizeof(buf)); + if (ret == 0) { + *display_on = buf; + } + + return ret; +} + +int cts_tcs_set_pwr_mode(const struct cts_device *cts_dev, u8 pwr_mode) +{ + return cts_tcs_write(cts_dev, CMD_SYS_STS_PWR_STATE_RW, + &pwr_mode, sizeof(pwr_mode)); +} + +int cts_tcs_set_display_on(const struct cts_device *cts_dev, u8 display_on) +{ + return cts_tcs_write(cts_dev, CMD_OPENSHORT_SHORT_DISP_ON_EN_RW, + &display_on, sizeof(display_on)); +} + + +int cts_tcs_set_charger_plug(struct cts_device *cts_dev, u8 set) +{ + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + int ret; + + cts_info("Set charger enable:%d", set); + status->charger = set; + + ret = cts_tcs_write(cts_dev, CMD_SYS_STS_CHARGER_PLUGIN_RW, &set, 1); + if (ret < 0) { + cts_info("Set charger failed!"); + } + + return ret; +} + +int cts_tcs_get_charger_plug(const struct cts_device *cts_dev, u8 *isset) +{ + u8 buf = 0; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_CHARGER_PLUGIN_RW,&buf, sizeof(buf)); + if (ret == 0) { + *isset = buf; + } + + return ret; +} + +int cts_tcs_set_earjack_plug(struct cts_device *cts_dev, u8 set) +{ + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + int ret; + + cts_info("Set earjack enable:%d", set); + + status->earjack = set; + + ret = cts_tcs_write(cts_dev, CMD_SYS_STS_EP_PLUGIN_RW, &set, 1); + if (ret) { + cts_info("Set earjack failed!"); + } + + return ret; +} + +int cts_tcs_get_earjack_plug(const struct cts_device *cts_dev, u8 *isset) +{ + u8 buf = 0; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_EP_PLUGIN_RW, &buf, sizeof(buf)); + if (ret == 0) { + *isset = buf; + } + + return ret; +} + +int cts_tcs_set_panel_direction(const struct cts_device *cts_dev, u8 direction) +{ + return cts_tcs_write(cts_dev, CMD_SYS_STS_PANEL_DIRECTION_RW, + &direction, sizeof(direction)); +} + +int cts_tcs_get_panel_direction(const struct cts_device *cts_dev, u8 *direction) +{ + u8 buf = 0; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_PANEL_DIRECTION_RW, + &buf, sizeof(buf)); + if (ret == 0) { + *direction = buf; + } + + return ret; +} + +int cts_tcs_set_game_mode(struct cts_device *cts_dev, u8 enable) +{ + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + int ret; + + cts_info("Set game enable:%d", enable); + + status->game = enable; + + ret = cts_tcs_write(cts_dev, CMD_SYS_STS_GAME_MODE_RW, + &enable, sizeof(enable)); + if (ret) { + cts_err("Set game failed!"); + } + + return ret; +} + +int cts_tcs_get_game_mode(const struct cts_device *cts_dev, u8 *enabled) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_GAME_MODE_RW, buf, sizeof(buf)); + if (ret == 0) { + *enabled = buf[0]; + } + + return ret; +} + +int cts_tcs_get_has_int_data(const struct cts_device *cts_dev, bool *has) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_DATA_CAPTURE_SUPPORT_RO, + buf, sizeof(buf)); + if (ret == 0) { + *has = !!buf[0]; + } + return ret; +} + +int cts_tcs_get_int_data_types(const struct cts_device *cts_dev, u16 *type) +{ + u8 buf[2] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_DATA_CAPTURE_FUNC_MAP_RW, + buf, sizeof(buf)); + if (ret == 0) { + *type = buf[0] | (buf[1] << 8); + } + return ret; +} + +int cts_tcs_set_int_data_types(const struct cts_device *cts_dev, u16 type) +{ + return cts_tcs_write(cts_dev, CMD_SYS_STS_DATA_CAPTURE_FUNC_MAP_RW, + (u8 *) &type, sizeof(type)); +} + +int cts_tcs_get_int_data_method(const struct cts_device *cts_dev, u8 *method) +{ + u8 buf[1] = { 0 }; + int ret; + + ret = cts_tcs_read(cts_dev, CMD_SYS_STS_DATA_CAPTURE_EN_RW, + buf, sizeof(buf)); + if (ret == 0) { + *method = buf[0]; + } + return ret; +} + +int cts_tcs_set_int_data_method(const struct cts_device *cts_dev, u8 method) +{ + return cts_tcs_write(cts_dev, CMD_SYS_STS_DATA_CAPTURE_EN_RW, + &method, sizeof(method)); +} + +int cts_tcs_set_proximity_mode(struct cts_device *cts_dev, u8 enable) +{ + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + int ret; + + cts_info("Set proximity enable:%d", enable); + status->proximity = enable; + + ret = cts_tcs_write(cts_dev, CMD_PARA_PROXI_EN_RW, &enable, 1); + if (ret != 0) { + cts_err("Set proximity failed!"); + } + + return ret; +} + +int cts_tcs_set_knuckle_mode(struct cts_device *cts_dev, u8 enable) +{ + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + int ret; + + cts_info("Set knuckle enable:%d", enable); + status->knuckle = enable; + + ret = cts_tcs_write(cts_dev, CMD_PARA_KUNCKLE_RW, &enable, 1); + if (ret != 0) { + cts_err("Set knuckle failed!"); + } + + return ret; +} + +int cts_tcs_set_glove_mode(struct cts_device *cts_dev, u8 enable) +{ + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + int ret; + + cts_info("Set glove enable:%d", enable); + status->glove = enable; + + ret = cts_tcs_write(cts_dev, CMD_SYS_STS_HI_SENSE_EN_RW, &enable, 1); + if (ret != 0) { + cts_err("Set glove failed!"); + } + + return ret; +} + +int cts_tcs_set_pocket_enable(struct cts_device *cts_dev, u8 enable) +{ + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + int ret; + + cts_info("Set pocket enable:%d", enable); + status->pocket = enable; + + ret = cts_tcs_write(cts_dev, CMD_SYS_STS_POCKET_MODE_EN_RW, &enable, 1); + if (ret != 0) { + cts_err("Set pocket failed!"); + } + + return ret; +} + +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL +int cts_tcs_set_gesture_en_mask(const struct cts_device *cts_dev, bool d_tap, bool s_tap) +{ + int ret; + u8 buf[4] = {0}; + + buf[0] = d_tap ? 1 : 0; + buf[2] = s_tap ? 0x80 : 0; + + cts_info("set gesture bit:0x%02x%02x%02x", buf[2], buf[1], buf[0]); + + ret = cts_tcs_spi_write(cts_dev, CMD_GSTR_ENTER_MAP_RW, + buf, sizeof(buf)); + return ret; +} +#endif + +void cts_tcs_reinit_fw_status(struct cts_device *cts_dev) +{ + struct cts_firmware_status *status = (struct cts_firmware_status *) + &cts_dev->rtdata.firmware_status; + cts_tcs_set_charger_plug(cts_dev, status->charger); + cts_tcs_set_proximity_mode(cts_dev, status->proximity); + cts_tcs_set_earjack_plug(cts_dev, status->earjack); + cts_tcs_set_knuckle_mode(cts_dev, status->knuckle); + cts_tcs_set_glove_mode(cts_dev, status->glove); + cts_tcs_set_pocket_enable(cts_dev, status->pocket); + cts_tcs_set_game_mode(cts_dev, status->game); +} + + +int cts_tcs_set_product_en(struct cts_device *cts_dev, u8 enable) +{ + int ret; + ret = cts_tcs_write(cts_dev, CMD_SYS_STS_PRODUCTION_TEST_EN_RW, + &enable, sizeof(enable)); + if (ret) { + cts_err("Set product_en failed!"); + } + + return ret; +} diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_tcs.h b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_tcs.h new file mode 100644 index 000000000000..4be515fc92d7 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_tcs.h @@ -0,0 +1,108 @@ +#ifndef _CTS_TCS_H_ +#define _CTS_TCS_H_ + +int cts_tcs_read(const struct cts_device *cts_dev, + u16 cmd, u8 *buf, size_t len); +int cts_tcs_write(const struct cts_device *cts_dev, + u16 cmd, u8 *buf, size_t len); +int cts_tcs_get_fw_ver(const struct cts_device *cts_dev, u16 *fwver); +int cts_tcs_get_lib_ver(const struct cts_device *cts_dev, u16 *libver); +int cts_tcs_get_ddi_ver(const struct cts_device *cts_dev, u8 *ddiver); +int cts_tcs_get_res_x(const struct cts_device *cts_dev, u16 *res_x); +int cts_tcs_get_res_y(const struct cts_device *cts_dev, u16 *res_y); +int cts_tcs_get_rows(const struct cts_device *cts_dev, u8 *rows); +int cts_tcs_get_cols(const struct cts_device *cts_dev, u8 *cols); +int cts_tcs_get_flip_x(const struct cts_device *cts_dev, bool *flip_x); +int cts_tcs_get_flip_y(const struct cts_device *cts_dev, bool *flip_y); +int cts_tcs_get_swap_axes(const struct cts_device *cts_dev, bool *swap_axes); +int cts_tcs_clr_gstr_ready_flag(const struct cts_device *cts_dev); + +int cts_tcs_get_int_mode(const struct cts_device *cts_dev, u8 *int_mode); +int cts_tcs_get_int_keep_time(const struct cts_device *cts_dev, + u16 *int_keep_time); +int cts_tcs_get_rawdata_target(const struct cts_device *cts_dev, + u16 *rawdata_target); +int cts_tcs_get_esd_method(const struct cts_device *cts_dev, u8 *esd_method); + +int cts_tcs_get_touchinfo(struct cts_device *cts_dev, + struct cts_device_touch_info *touch_info); +int cts_tcs_get_gestureinfo(struct cts_device *cts_dev, + struct cts_device_gesture_info *gesture_info); +int cts_tcs_get_touch_status(struct cts_device *cts_dev); +int cts_tcs_get_esd_protection(const struct cts_device *cts_dev, + u8 *esd_protection); + +int cts_tcs_read_hw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); +int cts_tcs_write_hw_reg(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); +int cts_tcs_read_ddi_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size); +int cts_tcs_write_ddi_reg(const struct cts_device *cts_dev, u32 addr, + u8 *regbuf, size_t size); +int cts_tcs_read_reg(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); +int cts_tcs_write_reg(const struct cts_device *cts_dev, u32 addr, + u8 *buf, size_t size); + +int cts_tcs_get_fw_id(const struct cts_device *cts_dev, u16 *fwid); +int cts_tcs_get_workmode(const struct cts_device *cts_dev, u8 *workmode); +int cts_tcs_set_workmode(const struct cts_device *cts_dev, u8 workmode); +int cts_tcs_set_openshort_mode(const struct cts_device *cts_dev, u8 mode); +int cts_tcs_get_curr_mode(const struct cts_device *cts_dev, u8 *currmode); +int cts_tcs_set_tx_vol(const struct cts_device *cts_dev, u8 txvol); + +int cts_tcs_set_short_test_type(const struct cts_device *cts_dev, u8 short_type); +int cts_tcs_set_openshort_enable(const struct cts_device *cts_dev, u8 enable); +int cts_tcs_is_openshort_enabled(const struct cts_device *cts_dev, u8 *enabled); + +int cts_tcs_set_esd_enable(const struct cts_device *cts_dev, u8 enable); +int cts_tcs_set_cneg_enable(const struct cts_device *cts_dev, u8 enable); +int cts_tcs_set_mnt_enable(const struct cts_device *cts_dev, u8 enable); +int cts_tcs_is_display_on(const struct cts_device *cts_dev, u8 *display_on); +int cts_tcs_set_display_on(const struct cts_device *cts_dev, u8 display_on); +int cts_tcs_is_cneg_enabled(const struct cts_device *cts_dev, u8 *enabled); +int cts_tcs_is_mnt_enabled(const struct cts_device *cts_dev, u8 *enabled); +int cts_tcs_set_pwr_mode(const struct cts_device *cts_dev, u8 pwr_mode); +int cts_tcs_get_has_int_data(const struct cts_device *cts_dev, bool *has); +int cts_tcs_get_int_data_types(const struct cts_device *cts_dev, u16 *type); +int cts_tcs_set_int_data_types(const struct cts_device *cts_dev, u16 type); +int cts_tcs_get_int_data_method(const struct cts_device *cts_dev, u8 *method); +int cts_tcs_set_int_data_method(const struct cts_device *cts_dev, u8 method); +int cts_tcs_calc_int_data_size(struct cts_device *cts_dev); + +int cts_tcs_polling_data(struct cts_device *cts_dev,u8 *buf, size_t size); +int cts_tcs_polling_test_data(struct cts_device *cts_dev, + u8 *buf, size_t size); +int cts_tcs_top_get_rawdata(struct cts_device *cts_dev, u8 *buf, size_t size); +int cts_tcs_top_get_manual_diff(struct cts_device *cts_dev, u8 *buf, + size_t size); +int cts_tcs_top_get_real_diff(struct cts_device *cts_dev, u8 *buf, size_t size); +int cts_tcs_top_get_noise_diff(struct cts_device *cts_dev, u8 *buf,size_t size); +int cts_tcs_top_get_basedata(struct cts_device *cts_dev, u8 *buf, size_t size); +int cts_tcs_top_get_cnegdata(struct cts_device *cts_dev, u8 *buf, size_t size); +int cts_tcs_reset_device(const struct cts_device *cts_dev); +int cts_tcs_set_int_test(const struct cts_device *cts_dev, u8 enable); +int cts_tcs_set_int_pin(const struct cts_device *cts_dev, u8 high); +int cts_tcs_get_module_id(const struct cts_device *cts_dev, u32 *modId); + +int cts_tcs_tool_xtrans(const struct cts_device *cts_dev, u8 *tx, size_t txlen, + u8 *rx, size_t rxlen); + +int cts_tcs_set_charger_plug(struct cts_device *cts_dev, u8 set); +int cts_tcs_get_charger_plug(const struct cts_device *cts_dev, u8 *isset); +int cts_tcs_set_earjack_plug(struct cts_device *cts_dev, u8 set); +int cts_tcs_get_earjack_plug(const struct cts_device *cts_dev, u8 *isset); +int cts_tcs_set_panel_direction(const struct cts_device *cts_dev, u8 direction); +int cts_tcs_get_panel_direction(const struct cts_device *cts_dev, u8 *direction); +int cts_tcs_set_game_mode(struct cts_device *cts_dev, u8 enable); +int cts_tcs_get_game_mode(const struct cts_device *cts_dev, u8 *enabled); +int cts_tcs_set_proximity_mode(struct cts_device *cts_dev, u8 enable); +void cts_tcs_reinit_fw_status(struct cts_device *cts_dev); + +int cts_tcs_set_product_en(struct cts_device *cts_dev, u8 enable); +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL +extern int cts_tcs_set_gesture_en_mask(const struct cts_device *cts_dev, bool d_tap, bool s_tap); +#endif + +#endif /* _CTS_TCS_H_ */ diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_test.c b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_test.c new file mode 100644 index 000000000000..b3512c008153 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_test.c @@ -0,0 +1,2346 @@ +#define LOG_TAG "Test" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_strerror.h" +#include "cts_test.h" +#include "cts_tcs.h" + +#ifdef CTS_CONFIG_MKDIR_FOR_CTS_TEST +/* for ksys_mkdir/sys_mkdir */ +#include +#include +#endif /* CTS_CONFIG_MKDIR_FOR_CTS_TEST */ + +const char *cts_test_item_str(int test_item) +{ +#define case_test_item(item) \ + case CTS_TEST_ ## item: return #item "-TEST" + + switch (test_item) { + case_test_item(RESET_PIN); + case_test_item(INT_PIN); + case_test_item(RAWDATA); + case_test_item(NOISE); + case_test_item(OPEN); + case_test_item(SHORT); + case_test_item(COMPENSATE_CAP); + + default: + return "INVALID"; + } +#undef case_test_item +} + +/* ICNL9911/S/C */ +#define CTS_FIRMWARE_WORK_MODE_NORMAL (0x00) +#define CTS_FIRMWARE_WORK_MODE_FACTORY (0x01) +#define CTS_FIRMWARE_WORK_MODE_CONFIG (0x02) +#define CTS_FIRMWARE_WORK_MODE_TEST (0x03) + +/* ICNL9916/9916C/9922 */ +#define CTS_FIRMWARE_WORK_MODE_CFG (0x01) +#define CTS_FIRMWARE_WORK_MODE_OPEN_SHORT (0x02) + +#define CTS_TEST_SHORT (0x01) +#define CTS_TEST_OPEN (0x02) + +#define CTS_SHORT_TEST_UNDEFINED (0x00) +#define CTS_SHORT_TEST_BETWEEN_COLS (0x01) +#define CTS_SHORT_TEST_BETWEEN_ROWS (0x02) +#define CTS_SHORT_TEST_BETWEEN_GND (0x03) + +#define RAWDATA_BUFFER_SIZE(cts_dev) \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * 2) + +int disable_fw_esd_protection(struct cts_device *cts_dev) +{ + return cts_fw_reg_writeb(cts_dev, 0x8000 + 342, 1); +} + +int disable_fw_monitor_mode(struct cts_device *cts_dev) +{ + int ret; + u8 value; + + ret = cts_fw_reg_readb(cts_dev, 0x8000 + 344, &value); + if (ret) + return ret; + + if (value & BIT(0)) + return cts_fw_reg_writeb(cts_dev, 0x8000 + 344, value & (~BIT(0))); + + return 0; +} + +int disable_fw_auto_compensate(struct cts_device *cts_dev) +{ + return cts_fw_reg_writeb(cts_dev, 0x8000 + 276, 1); +} + +int set_fw_work_mode(const struct cts_device *cts_dev, u8 mode) +{ + int ret, retries; + u8 pwr_mode; + + cts_info("Set firmware work mode to %u", mode); + + ret = cts_fw_reg_writeb(cts_dev, CTS_DEVICE_FW_REG_WORK_MODE, mode); + if (ret) { + cts_err("Write firmware work mode register failed %d", ret); + return ret; + } + + ret = cts_fw_reg_readb(cts_dev, 0x05, &pwr_mode); + if (ret) { + cts_err("Read firmware power mode register failed %d", ret); + return ret; + } + + if (pwr_mode == 1) { + ret = cts_send_command(cts_dev, CTS_CMD_QUIT_GESTURE_MONITOR); + if (ret) { + cts_err("Send cmd QUIT_GESTURE_MONITOR failed %d", ret); + return ret; + } + + msleep(50); + } + + retries = 0; + do { + u8 sys_busy, curr_mode; + + msleep(10); + + ret = cts_fw_reg_readb(cts_dev, 0x01, &sys_busy); + if (ret) { + cts_err("Read firmware system busy register failed %d", ret); + return ret; + } + if (sys_busy) + continue; + + ret = cts_fw_reg_readb(cts_dev, 0x3F, &curr_mode); + if (ret) { + cts_err("Read firmware current work mode failed %d", ret); + return ret; + } + + if (curr_mode == mode || curr_mode == 0xFF) + break; + } while (retries++ < 1000); + + return (retries >= 1000 ? -ETIMEDOUT : 0); +} + +int set_display_state(const struct cts_device *cts_dev, bool active) +{ + int ret; + u8 access_flag; + + cts_info("Set display state to %s", active ? "ACTIVE" : "SLEEP"); + + ret = cts_hw_reg_readb_relaxed(cts_dev, CTS_DEV_HW_REG_DDI_REG_CTRL, &access_flag); + if (ret) { + cts_err("Read display access flag failed %d", ret); + return ret; + } + + ret = cts_hw_reg_writeb_relaxed(cts_dev, CTS_DEV_HW_REG_DDI_REG_CTRL, access_flag | 0x01); + if (ret) { + cts_err("Write display access flag %02x failed %d", access_flag, ret); + return ret; + } + + if (active) { + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3C044, 0x55); + if (ret) { + cts_err("Write DCS-CMD11 fail"); + return ret; + } + + msleep(100); + + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3C0A4, 0x55); + if (ret) { + cts_err("Write DCS-CMD29 fail"); + return ret; + } + + msleep(100); + } else { + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3C0A0, 0x55); + if (ret) { + cts_err("Write DCS-CMD28 fail"); + return ret; + } + + msleep(100); + + ret = cts_hw_reg_writeb_relaxed(cts_dev, 0x3C040, 0x55); + if (ret) { + cts_err("Write DCS-CMD10 fail"); + return ret; + } + + msleep(100); + } + + ret = cts_hw_reg_writeb_relaxed(cts_dev, CTS_DEV_HW_REG_DDI_REG_CTRL, access_flag); + if (ret) { + cts_err("Restore display access flag %02x failed %d", access_flag, ret); + return ret; + } + + return 0; +} + +struct cts_fw_short_test_param { + u8 type; + u32 col_pattern[2]; + u32 row_pattern[2]; +}; + +bool set_short_test_type(struct cts_device *cts_dev, u8 type) +{ + static struct cts_fw_short_test_param param = { + .type = CTS_SHORT_TEST_BETWEEN_COLS, + .col_pattern = { 0, 0 }, + .row_pattern = { 0, 0 } + }; + int i, ret; + + cts_info("Set short test type to %u", type); + + param.type = type; + for (i = 0; i < 5; i++) { + u8 type_readback; + + ret = cts_fw_reg_writesb(cts_dev, 0x5000, ¶m, sizeof(param)); + if (ret) { + cts_err("Set short test type to %u failed %d", type, ret); + continue; + } + ret = cts_fw_reg_readb(cts_dev, 0x5000, &type_readback); + if (ret) { + cts_err("Get short test type failed %d", ret); + continue; + } + if (type == type_readback) + return 0; + else { + cts_err("Set test type %u != readback %u", type, type_readback); + continue; + } + } + + return ret; +} + +int cts_write_file(struct file *filp, const void *data, size_t size) +{ +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): kernel_write is forbiddon with GKI Version!", __func__); + return -EPERM; +#else + loff_t pos; + ssize_t ret; + + pos = filp->f_pos; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(filp, data, size, &pos); +#else + ret = kernel_write(filp, data, size, pos); +#endif + + if (ret >= 0) { + filp->f_pos += ret; + } + + return ret; +#endif +} + +#ifdef CTS_CONFIG_MKDIR_FOR_CTS_TEST +/* copied from fs/namei.c */ +long do_mkdirat(int dfd, const char __user *pathname, umode_t mode) +{ + struct dentry *dentry; + struct path path; + int error; + unsigned int lookup_flags = LOOKUP_DIRECTORY; + +retry: + dentry = user_path_create(dfd, pathname, &path, lookup_flags); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + if (!IS_POSIXACL(path.dentry->d_inode)) + mode &= ~current_umask(); + error = security_path_mkdir(&path, dentry, mode); + if (!error) + error = vfs_mkdir(path.dentry->d_inode, dentry, mode); + done_path_create(&path, dentry); + if (retry_estale(error, lookup_flags)) { + lookup_flags |= LOOKUP_REVAL; + goto retry; + } + return error; +} + +/* Make directory for filepath + * If filepath = "/A/B/C/D.file", it will make dir /A/B/C recursive + * like userspace mkdir -p + */ +int cts_mkdir_for_file(const char *filepath, umode_t mode) +{ +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): some functions are forbiddon with GKI Version!", __func__); + return -EPERM; +#else + char *dirname = NULL; + int dirname_len; + char *s; + int ret; + mm_segment_t fs; + + if (filepath == NULL) { + cts_err("Create dir for file with filepath = NULL"); + return -EINVAL; + } + + if (filepath[0] == '\0' || filepath[0] != '/') { + cts_err("Create dir for file with invalid filepath[0]: %c", filepath[0]); + return -EINVAL; + } + + dirname_len = strrchr(filepath, '/') - filepath; + if (dirname_len == 0) { + cts_warn("Create dir for file '%s' in root dir", filepath); + return 0; + } + + dirname = kstrndup(filepath, dirname_len, GFP_KERNEL); + if (dirname == NULL) { + cts_err("Create dir alloc mem for dirname failed"); + return -ENOMEM; + } + + cts_info("Create dir '%s' for file '%s'", dirname, filepath); + + fs = get_fs(); + set_fs(KERNEL_DS); + + s = dirname + 1; /* Skip leading '/' */ + while (1) { + char c = '\0'; + + /* Bypass leading non-'/'s and then subsequent '/'s */ + while (*s) { + if (*s == '/') { + do { + ++s; + } while (*s == '/'); + c = *s; /* Save current char */ + *s = '\0'; /* and replace it with nul */ + break; + } + ++s; + } + + cts_dbg(" - Create dir '%s'", dirname); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) + ret = ksys_mkdir(dirname, 0777); +#else + ret = sys_mkdir(dirname, 0777); +#endif + if (ret < 0 && ret != -EEXIST) { + cts_info("Create dir '%s' failed %d(%s)", dirname, ret, + cts_strerror(ret)); + /* Remove any inserted nul from the path */ + *s = c; + break; + } + /* Reset ret to 0 if return -EEXIST */ + ret = 0; + + if (c) { + /* Remove any inserted nul from the path */ + *s = c; + } else { + break; + } + } + + set_fs(fs); + + if (dirname) { + kfree(dirname); + } + + return ret; +#endif +} +#endif /* CTS_CONFIG_MKDIR_FOR_CTS_TEST */ + +struct file *cts_test_data_filp; +int cts_start_dump_test_data_to_file(const char *filepath, bool append_to_file) +{ + int ret; + +#define START_BANNER \ + ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n" + + cts_info("Start dump test data to file '%s'", filepath); + +#ifdef CTS_CONFIG_MKDIR_FOR_CTS_TEST + ret = cts_mkdir_for_file(filepath, 0777); + if (ret) { + cts_err("Create dir for test data file failed %d", ret); + return ret; + } +#endif /* CTS_CONFIG_MKDIR_FOR_CTS_TEST */ + +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): filp_open is forbiddon with GKI Version!", __func__); + ret = -EPERM; + return ret; +#else + cts_test_data_filp = filp_open(filepath, + O_WRONLY | O_CREAT | (append_to_file ? O_APPEND : O_TRUNC), + S_IRUGO | S_IWUGO); + if (IS_ERR(cts_test_data_filp)) { + ret = PTR_ERR(cts_test_data_filp); + cts_test_data_filp = NULL; + cts_err("Open file '%p' for test data failed %d", cts_test_data_filp, ret); + return ret; + } + + cts_write_file(cts_test_data_filp, START_BANNER, strlen(START_BANNER)); + + return 0; +#endif +#undef START_BANNER +} + +void cts_stop_dump_test_data_to_file(void) +{ +#define END_BANNER \ + "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n" +#ifndef CFG_CTS_FOR_GKI + int r; +#endif + + cts_info("Stop dump test data to file"); + + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, END_BANNER, strlen(END_BANNER)); +#ifndef CFG_CTS_FOR_GKI + r = filp_close(cts_test_data_filp, NULL); + if (r) { + cts_err("Close test data file failed %d", r); + } +#endif + cts_test_data_filp = NULL; + } else { + cts_warn("Stop dump tsdata to file with filp = NULL"); + } +#undef END_BANNER +} + +static void cts_dump_tsdata(struct cts_device *cts_dev, + const char *desc, const u16 *data, bool to_console) +{ +#define SPLIT_LINE_STR \ + "--------------------------------------------------------"\ + "--------------------------------------------------------" +#define ROW_NUM_FORMAT_STR "%2d | " +#define COL_NUM_FORMAT_STR "%-5u " +#define DATA_FORMAT_STR "%-5u " + + int r, c; + u32 max, min, sum, average; + int max_r, max_c, min_r, min_c; + char line_buf[128]; + int count = 0; + + max = min = data[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + u16 val = data[r * cts_dev->hwdata->num_col + c]; + + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count = 0; + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + " %s test data MIN: [%u][%u]=%u, MAX: [%u][%u]=%u, AVG=%u", + desc, min_r, min_c, min, max_r, max_c, max, average); + if (to_console) { + cts_info(SPLIT_LINE_STR); + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } + + count = 0; + count += scnprintf(line_buf + count, sizeof(line_buf) - count, " | "); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + COL_NUM_FORMAT_STR, c); + } + if (to_console) { + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count = 0; + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + DATA_FORMAT_STR, data[r * cts_dev->hwdata->num_col + c]); + } + if (to_console) { + cts_info("%s", line_buf); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + } + } + if (to_console) { + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } +#undef SPLIT_LINE_STR +#undef ROW_NUM_FORMAT_STR +#undef COL_NUM_FORMAT_STR +#undef DATA_FORMAT_STR +} + +static bool is_invalid_node(u32 *invalid_nodes, u32 num_invalid_nodes, + u16 row, u16 col) +{ + int i; + + for (i = 0; i < num_invalid_nodes; i++) { + if (MAKE_INVALID_NODE(row, col) == invalid_nodes[i]) { + return true; + } + } + + return false; +} + +/* Return number of failed nodes */ +static int validate_tsdata(struct cts_device *cts_dev, const char *desc, u16 *data, + u32 *invalid_nodes, u32 num_invalid_nodes, bool per_node, int *min, int *max) +{ +#define SPLIT_LINE_STR \ + "------------------------------" + + int r, c; + int failed_cnt = 0; + + cts_info("%s validate data: %s, num invalid node: %u, thresh[0]=[%d, %d]", + desc, per_node ? "Per-Node" : "Uniform-Threshold", + num_invalid_nodes, min ? min[0] : INT_MIN, max ? max[0] : INT_MAX); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + int offset = r * cts_dev->hwdata->num_col + c; + + if (num_invalid_nodes && + is_invalid_node(invalid_nodes, num_invalid_nodes, r, c)) { + continue; + } + + if ((min != NULL && data[offset] < min[per_node ? offset : 0]) || + (max != NULL && data[offset] > max[per_node ? offset : 0])) { + if (failed_cnt == 0) { + cts_info(SPLIT_LINE_STR); + cts_info("%s failed nodes:", desc); + } + failed_cnt++; + + cts_info(" %3d: [%-2d][%-2d] = %u", failed_cnt, r, c, data[offset]); + } + } + } + + if (failed_cnt) { + cts_info(SPLIT_LINE_STR); + cts_info("%s test %d node total failed", desc, failed_cnt); + } + + return failed_cnt; + +#undef SPLIT_LINE_STR +} + +static int wait_fw_to_normal_work(struct cts_device *cts_dev) +{ + int i = 0; + int ret; + + cts_info("Wait fw to normal work"); + + do { + u8 work_mode; + + ret = cts_tcs_get_workmode(cts_dev, &work_mode); + if (ret) { + cts_err("Get fw curr work mode failed %d", work_mode); + continue; + } else { + if (work_mode == CTS_FIRMWARE_WORK_MODE_NORMAL) + return 0; + } + + mdelay(10); + } while (++i < 100); + + return ret ? ret : -ETIMEDOUT; +} +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +static int wait_fw_to_curr_mode(struct cts_device *cts_dev) +{ + int i = 0; + int ret; + u8 work_mode; + + cts_info("Wait fw to curr work mode"); + + do { + ret = cts_tcs_get_curr_mode(cts_dev, &work_mode); + if (ret) { + cts_err("Get fw curr work mode failed %d", work_mode); + continue; + } else if (work_mode == CTS_FIRMWARE_WORK_MODE_OPEN_SHORT) { + return 0; + } + mdelay(10); + } while (++i < 100); + + cts_err("Get work_mode: %d != %d", work_mode, CTS_FIRMWARE_WORK_MODE_OPEN_SHORT); + + return -ETIMEDOUT; +} +#else +static int wait_fw_to_curr_mode(struct cts_device *cts_dev) +{ + int i = 0; + int ret; + u8 work_mode; + cts_info("Wait fw to curr work mode"); + + do { + ret = cts_tcs_get_curr_mode(cts_dev, &work_mode); + if (ret) { + cts_err("Get fw curr work mode failed %d", work_mode); + continue; + } else if (work_mode == CTS_FIRMWARE_WORK_MODE_OPEN_SHORT) { + return 0; + } + mdelay(10); + } while (++i < 100); + cts_err("Get work_mode: %d != %d", work_mode, CTS_FIRMWARE_WORK_MODE_OPEN_SHORT); + return -ETIMEDOUT; +} +#endif +static int prepare_test(struct cts_device *cts_dev) +{ + int ret; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + int i = 0; + u8 workmode = -1; +#else + int i = 0; + u8 workmode = -1; +#endif + + cts_info("Prepare test"); + + cts_reset_device(cts_dev); + + ret = wait_fw_to_normal_work(cts_dev); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + return ret; + } + + ret = cts_tcs_set_workmode(cts_dev, CTS_FIRMWARE_WORK_MODE_CFG); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_CONFIG failed %d", ret); + return ret; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + mdelay(30); + do { + ret = cts_tcs_get_workmode(cts_dev, &workmode); + if (ret) { + cts_err("Get real workmode to FACTORY MODE failed %d", ret); + } else if (workmode == CTS_FIRMWARE_WORK_MODE_CFG) { + break; + } + mdelay(30); + cts_err("Get workmode: %d, CTS_FIRMWARE_WORK_MODE_CFG: %d, retry count: %d", + workmode, CTS_FIRMWARE_WORK_MODE_CFG, i); + } while (i++ < 10); + if (workmode != CTS_FIRMWARE_WORK_MODE_CFG) + return -EINVAL; +#else + mdelay(30); + do { + ret = cts_tcs_get_workmode(cts_dev, &workmode); + if (ret) { + cts_err("Get real workmode to FACTORY MODE failed %d", ret); + } else if (workmode == CTS_FIRMWARE_WORK_MODE_CFG) { + break; + } + mdelay(30); + cts_err("Get workmode: %d, CTS_FIRMWARE_WORK_MODE_CFG: %d, retry count: %d", + workmode, CTS_FIRMWARE_WORK_MODE_CFG, i); + } while (i++ < 10); + if (workmode != CTS_FIRMWARE_WORK_MODE_CFG) + return -EINVAL; +#endif + + ret = cts_tcs_set_product_en(cts_dev, 1); + if (ret) { + cts_err("Set product en failed %d", ret); + return ret; + } + + ret = cts_tcs_set_workmode(cts_dev, CTS_FIRMWARE_WORK_MODE_NORMAL); + if (ret) { + cts_err("Set firmware work mode to CTS_FIRMWARE_WORK_MODE_NORMAL failed %d", ret); + return ret; + } + + ret = wait_fw_to_normal_work(cts_dev); + if (ret) { + cts_err("Wait fw to normal work failed %d", ret); + return ret; + } + + cts_dev->rtdata.testing = true; + + return 0; +} + +#define SHORT_COLS_TEST_LOOP 3 +#define SHORT_ROWS_TEST_LOOP 6 +static void post_test(struct cts_device *cts_dev) +{ + int ret; + + cts_info("Post test"); + + cts_reset_device(cts_dev); + + ret = cts_tcs_set_workmode(cts_dev, CTS_FIRMWARE_WORK_MODE_NORMAL); + if (ret) + cts_err("Set firmware work mode to WORK_MODE_NORMAL failed %d", ret); + + ret = wait_fw_to_normal_work(cts_dev); + if (ret) + cts_err("Wait fw to normal work failed %d", ret); + + cts_dev->rtdata.testing = false; +} + +#ifdef CFG_CTS_HAS_RESET_PIN +int cts_test_reset_pin(struct cts_device *cts_dev, struct cts_test_param *param) +{ + ktime_t start_time, end_time, delta_time; + int ret; + + if (cts_dev == NULL || param == NULL) { + cts_err("Reset-pin test with invalid param: cts_dev: %p test param: %p", + cts_dev, param); + return -EINVAL; + } + + cts_info("Reset-Pin test, flags: 0x%08x, drive log file: '%s' buf size: %d", + param->flags, param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto show_test_result; + } + + cts_lock_device(cts_dev); + + cts_plat_set_reset(cts_dev->pdata, 0); + mdelay(50); + + if (cts_plat_is_normal_mode(cts_dev->pdata)) { + ret = -EIO; + cts_err("Device is alive while reset is low"); + } + cts_plat_set_reset(cts_dev->pdata, 1); + mdelay(50); + + { + int r = wait_fw_to_normal_work(cts_dev); + if (r) { + cts_err("Wait fw to normal work failed %d", r); + } + } + + if (!cts_plat_is_normal_mode(cts_dev->pdata)) { + ret = -EIO; + cts_err("Device is offline while reset is high"); + } + +#ifdef CONFIG_CTS_CHARGER_DETECT + if (cts_is_charger_exist(cts_dev)) { + int r = cts_set_dev_charger_attached(cts_dev, true); + if (r) { + cts_err("Set dev charger attached failed %d", r); + } + } +#endif /* CONFIG_CTS_CHARGER_DETECT */ + +#ifdef CONFIG_CTS_EARJACK_DETECT + if (cts_is_earjack_exist(cts_dev)) { + int r = cts_set_dev_earjack_attached(cts_dev, true); + if (r) + cts_err("Set dev earjack attached failed %d", r); + } +#endif /* CONFIG_CTS_EARJACK_DETECT */ + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) { + cts_enter_glove_mode(cts_dev); + } +#endif + +#ifdef CFG_CTS_FW_LOG_REDIRECT + if (cts_is_fw_log_redirect(cts_dev)) { + cts_enable_fw_log_redirect(cts_dev); + } +#endif + + cts_unlock_device(cts_dev); + + { + int r = cts_start_device(cts_dev); + if (r) { + cts_err("Start device failed %d", r); + } + } + + if (!cts_dev->rtdata.program_mode) { + cts_set_normal_addr(cts_dev); + } + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret) { + cts_info("Reset-Pin test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + cts_info("Reset-Pin test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + } + + return ret; +} +#endif + +int cts_test_int_pin(struct cts_device *cts_dev, struct cts_test_param *param) +{ + ktime_t start_time, end_time, delta_time; + int ret; + + if (cts_dev == NULL || param == NULL) { + cts_err("Int-pin test with invalid param: cts_dev: %p test param: %p", + cts_dev, param); + return -EINVAL; + } + + cts_info("Int-Pin test, flags: 0x%08x, drive log file: '%s' buf size: %d", + param->flags, param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto show_test_result; + } + + cts_lock_device(cts_dev); + ret = cts_tcs_set_int_test(cts_dev, 1); + if (ret) { + cts_err("Enable Int Test failed"); + goto unlock_device; + } + ret = cts_tcs_set_int_pin(cts_dev, 1); + if (ret) { + cts_err("Enable Int Test High failed"); + goto exit_int_test; + } + mdelay(10); + if (cts_plat_get_int_pin(cts_dev->pdata) == 0) { + cts_err("INT pin state != HIGH"); + ret = -EFAULT; + goto exit_int_test; + } + + ret = cts_tcs_set_int_pin(cts_dev, 0); + if (ret) { + cts_err("Enable Int Test LOW failed"); + goto exit_int_test; + } + mdelay(10); + if (cts_plat_get_int_pin(cts_dev->pdata) != 0) { + cts_err("INT pin state != LOW"); + ret = -EFAULT; + goto exit_int_test; + } + +exit_int_test: + { + int r = cts_tcs_set_int_test(cts_dev, 0); + if (r) { + cts_err("Disable Int Test failed %d", r); + } + } + mdelay(10); + +unlock_device: + cts_unlock_device(cts_dev); + + { + int r = cts_start_device(cts_dev); + if (r) { + cts_err("Start device failed %d", r); + } + } + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret) { + cts_info("Int-Pin test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + cts_info("Int-Pin test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + } + + return ret; +} + + +extern int cts_test_polling_rawdata(struct cts_device *cts_dev, + u8 *buf, size_t size); +int cts_test_rawdata(struct cts_device *cts_dev, struct cts_test_param *param) +{ + struct cts_rawdata_test_priv_param *priv_param; + bool driver_validate_data = false; + bool validate_data_per_node = false; + bool stop_test_if_validate_fail = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_file = false; + int num_nodes; + int tsdata_frame_size; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + int frame = 0; + int count = 3; +#else + int frame = 0; + int count = 3; +#endif + + int fail_frame = 0; + u16 *rawdata = NULL; + ktime_t start_time, end_time, delta_time; + int i; + int ret; + + if (cts_dev == NULL || param == NULL || + param->priv_param_size != sizeof(*priv_param) || + param->priv_param == NULL) { + cts_err("Rawdata test with invalid param: priv param: %p size: %d", + param->priv_param, param->priv_param_size); + return -EINVAL; + } + + priv_param = param->priv_param; + if (priv_param->frames <= 0) { + cts_info("Rawdata test with too little frame %u", priv_param->frames); + return -EINVAL; + } + + num_nodes = cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; + tsdata_frame_size = 2 * num_nodes; + + driver_validate_data = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); + validate_data_per_node = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + dump_test_data_to_user = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_console = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); + dump_test_data_to_file = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + stop_test_if_validate_fail = + !!(param->flags & CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED); + + cts_info("Rawdata test, flags: 0x%08x, frames: %d, num invalid node: %u, " + "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", + param->flags, priv_param->frames, param->num_invalid_node, + param->test_data_filepath, param->test_data_buf_size, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + if (dump_test_data_to_user) { + rawdata = (u16 *) param->test_data_buf; + } else { + rawdata = (u16 *) kmalloc(tsdata_frame_size, GFP_KERNEL); + if (rawdata == NULL) { + cts_err("Allocate memory for rawdata failed"); + ret = -ENOMEM; + goto show_test_result; + } + } + + /* Stop device to avoid un-wanted interrrupt */ + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto free_mem; + } + + if (dump_test_data_to_file) { + int r = cts_start_dump_test_data_to_file(param->test_data_filepath, + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); + if (r) { + cts_err("Start dump test data to file failed %d", r); + } + } + + cts_lock_device(cts_dev); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +try_again: +#else +try_again: +#endif + ret = prepare_test(cts_dev); + if (ret) { + cts_err("Prepare test failed %d", ret); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + goto prepare_try; +#else + goto prepare_try; +#endif + } + + cts_set_int_data_types(cts_dev, INT_DATA_TYPE_RAWDATA); + cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); + + for (frame = 0; frame < priv_param->frames; frame++) { + bool data_valid = false; + + for (i = 0; i < 3; i++) { + ret = cts_test_polling_rawdata(cts_dev, (u8 *)rawdata, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret < 0) { + cts_err("Get raw data failed: %d", ret); + mdelay(30); + } else { + data_valid = true; + break; + } + } + + if (!data_valid) { + ret = -EIO; + break; + } + + if (dump_test_data_to_user) { + *param->test_data_wr_size += tsdata_frame_size; + } + + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_tsdata(cts_dev, "Rawdata", rawdata, dump_test_data_to_console); + } + + if (driver_validate_data) { + ret = validate_tsdata(cts_dev, "Rawdata", rawdata, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + if (ret) { + cts_err("Rawdata test failed %d", ret); + fail_frame++; + cts_err("Rawdata test has %d nodes failed", ret); + if (stop_test_if_validate_fail) { + break; + } + } + } + + if (dump_test_data_to_user) { + rawdata += num_nodes; + } + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +prepare_try: +#else +prepare_try: +#endif + post_test(cts_dev); + cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); + cts_set_int_data_types(cts_dev, INT_DATA_TYPE_NONE); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + if (ret < 0 && count--) { + if (dump_test_data_to_user) { + *param->test_data_wr_size = 0; + rawdata = (u16 *) param->test_data_buf; + } + goto try_again; + } + + if (dump_test_data_to_file) { + cts_stop_dump_test_data_to_file(); + } +#else + if (ret < 0 && count--) { + if (dump_test_data_to_user) { + *param->test_data_wr_size = 0; + rawdata = (u16 *) param->test_data_buf; + } + goto try_again; + } + + if (dump_test_data_to_file) { + cts_stop_dump_test_data_to_file(); + } +#endif + cts_unlock_device(cts_dev); + { + int r = cts_start_device(cts_dev); + if (r) { + cts_err("Start device failed %d", r); + } + } + +free_mem: + if (!dump_test_data_to_user && rawdata != NULL) { + kfree(rawdata); + } + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + + if (ret) { + cts_info("Rawdata test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else if (fail_frame > 0) { + cts_info("Rawdata test has %d frame(s) FAIL, ELAPSED TIME: %lldms", + fail_frame, ktime_to_ms(delta_time)); + } else if (fail_frame == 0) { + cts_info("Rawdata test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + } + + return (ret ? ret : (fail_frame ? fail_frame : 0)); +} + +int cts_test_noise(struct cts_device *cts_dev, struct cts_test_param *param) +{ + struct cts_noise_test_priv_param *priv_param; + bool driver_validate_data = false; + bool validate_data_per_node = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_file = false; + int num_nodes; + int tsdata_frame_size; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + int frame = 0; +#else + int frame = 0; +#endif + u16 *buffer = NULL; + int buf_size = 0; + u16 *curr_rawdata = NULL; + u16 *max_rawdata = NULL; + u16 *min_rawdata = NULL; + u16 *noise = NULL; + bool first_frame = true; + bool data_valid = false; + ktime_t start_time, end_time, delta_time; + int i; + int ret; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + int count = 3; +#else + int count = 3; +#endif + + if (cts_dev == NULL || param == NULL || + param->priv_param_size != sizeof(*priv_param) || + param->priv_param == NULL) { + cts_err("Noise test with invalid param: priv param: %p size: %d", + param->priv_param, param->priv_param_size); + return -EINVAL; + } + + priv_param = param->priv_param; + if (priv_param->frames < 2) { + cts_err("Noise test with too little frame %u", priv_param->frames); + return -EINVAL; + } + + num_nodes = cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; + tsdata_frame_size = 2 * num_nodes; + + driver_validate_data = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); + validate_data_per_node = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + dump_test_data_to_user = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_console = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); + dump_test_data_to_file = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + + cts_info("Noise test, flags: 0x%08x, frames: %d, num invalid node: %u, " + "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", + param->flags, priv_param->frames, param->num_invalid_node, + param->test_data_filepath, param->test_data_buf_size, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + buf_size = (driver_validate_data ? 4 : 1) * tsdata_frame_size; + buffer = (u16 *) kmalloc(buf_size, GFP_KERNEL); + if (buffer == NULL) { + cts_err("Alloc mem for touch data failed"); + ret = -ENOMEM; + goto show_test_result; + } + + curr_rawdata = buffer; + if (driver_validate_data) { + max_rawdata = curr_rawdata + 1 * num_nodes; + min_rawdata = curr_rawdata + 2 * num_nodes; + noise = curr_rawdata + 3 * num_nodes; + } + + /* Stop device to avoid un-wanted interrrupt */ + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto free_mem; + } + + if (dump_test_data_to_file) { + int r = cts_start_dump_test_data_to_file(param->test_data_filepath, + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); + if (r) { + cts_err("Start dump test data to file failed %d", r); + } + } + + cts_lock_device(cts_dev); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +try_again: +#else +try_again: +#endif + ret = prepare_test(cts_dev); + if (ret) { + cts_err("Prepare test failed %d", ret); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + goto prepare_try; +#else + goto prepare_try; +#endif + } + + cts_set_int_data_types(cts_dev, INT_DATA_TYPE_RAWDATA); + cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); + + for (frame = 0; frame < priv_param->frames; frame++) { + for (i = 0; i < 3; i++) { + ret = cts_test_polling_rawdata(cts_dev, (u8 *)curr_rawdata, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret < 0) { + cts_err("Get raw data failed: %d", ret); + mdelay(30); + } else { + data_valid = true; + break; + } + } + + if (i >= 3) { + cts_err("Read rawdata failed"); + ret = -EIO; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + goto prepare_try; +#else + goto prepare_try; +#endif + } + + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_tsdata(cts_dev, "Noise-rawdata", curr_rawdata, + dump_test_data_to_console); + } + + if (dump_test_data_to_user) { + memcpy(param->test_data_buf + frame * tsdata_frame_size, + curr_rawdata, tsdata_frame_size); + *param->test_data_wr_size += tsdata_frame_size; + } + + if (driver_validate_data) { + if (unlikely(first_frame)) { + memcpy(max_rawdata, curr_rawdata, tsdata_frame_size); + memcpy(min_rawdata, curr_rawdata, tsdata_frame_size); + first_frame = false; + } else { + for (i = 0; i < num_nodes; i++) { + if (curr_rawdata[i] > max_rawdata[i]) { + max_rawdata[i] = curr_rawdata[i]; + } else if (curr_rawdata[i] < min_rawdata[i]) { + min_rawdata[i] = curr_rawdata[i]; + } + } + } + } + } + + data_valid = true; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +prepare_try: +#else +prepare_try: +#endif + post_test(cts_dev); + cts_set_int_data_method(cts_dev, INT_DATA_METHOD_NONE); + cts_set_int_data_types(cts_dev, INT_DATA_TYPE_NONE); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + if (ret < 0 && count--) { + if (dump_test_data_to_user) { + *param->test_data_wr_size = 0; + } + goto try_again; + } + + if (dump_test_data_to_file) { + cts_stop_dump_test_data_to_file(); + } +#else + if (ret < 0 && count--) { + if (dump_test_data_to_user) { + *param->test_data_wr_size = 0; + } + goto try_again; + } + + if (dump_test_data_to_file) { + cts_stop_dump_test_data_to_file(); + } +#endif + cts_unlock_device(cts_dev); + { + int r = cts_start_device(cts_dev); + if (r) { + cts_err("Start device failed %d", r); + } + } + + if (driver_validate_data && data_valid) { + for (i = 0; i < num_nodes; i++) { + noise[i] = max_rawdata[i] - min_rawdata[i]; + } + + if (dump_test_data_to_user) { + memcpy(param->test_data_buf + (frame + 0) * tsdata_frame_size, + noise, tsdata_frame_size); + memcpy(param->test_data_buf + (frame + 1) * tsdata_frame_size, + max_rawdata, tsdata_frame_size); + memcpy(param->test_data_buf + (frame + 2) * tsdata_frame_size, + min_rawdata, tsdata_frame_size); + } + + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_tsdata(cts_dev, "Noise", noise, + dump_test_data_to_console); + cts_dump_tsdata(cts_dev, "Rawdata MAX", max_rawdata, + dump_test_data_to_console); + cts_dump_tsdata(cts_dev, "Rawdata MIN", min_rawdata, + dump_test_data_to_console); + } + + ret = validate_tsdata(cts_dev, "Noise test", noise, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + } + +free_mem: + if (buffer) { + kfree(buffer); + } + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret > 0) { + cts_info("Noise test has %d nodes FAIL, ELAPSED TIME: %lldms", + ret, ktime_to_ms(delta_time)); + } else if (ret < 0) { + cts_info("Noise test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + cts_info("Noise test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + } + + return ret; +} + +/* Return 0 success + negative value while error occurs + positive value means how many nodes fail */ +int cts_test_open(struct cts_device *cts_dev, struct cts_test_param *param) +{ + bool driver_validate_data = false; + bool validate_data_per_node = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_file = false; + int num_nodes; + int tsdata_frame_size; + int ret; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + int count = 3; +#else + int count = 3; +#endif + u16 *test_result = NULL; + bool recovery_display_state = false; + u8 need_display_on; + ktime_t start_time, end_time, delta_time; + u8 old_int_data_method = cts_dev->fwdata.int_data_method; + u16 old_int_data_types = cts_dev->fwdata.int_data_types; + + if (cts_dev == NULL || param == NULL) { + cts_err("Open test with invalid param: cts_dev: %p test param: %p", + cts_dev, param); + return -EINVAL; + } + + num_nodes = cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; + tsdata_frame_size = 2 * num_nodes; + + driver_validate_data = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); + validate_data_per_node = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + dump_test_data_to_user = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_console = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); + dump_test_data_to_file = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + + cts_info("Open test, flags: 0x%08x, num invalid node: %u, " + "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", + param->flags, param->num_invalid_node, + param->test_data_filepath, param->test_data_buf_size, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + if (dump_test_data_to_user) { + test_result = (u16 *) param->test_data_buf; + } else { + test_result = (u16 *) kmalloc(tsdata_frame_size, GFP_KERNEL); + if (test_result == NULL) { + cts_err("Allocate memory for test result faild"); + ret = -ENOMEM; + goto show_test_result; + } + } + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto free_mem; + } + + cts_lock_device(cts_dev); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +try_again: +#else +try_again: +#endif + ret = prepare_test(cts_dev); + if (ret) { + cts_err("Prepare test failed %d", ret); + goto err_free_test_result; + } + + ret = cts_tcs_is_display_on(cts_dev, &need_display_on); + if (ret) { + cts_err("Read need display on register failed %d", ret); + goto err_free_test_result; + } + + if (need_display_on == 0) { + ret = cts_tcs_set_display_on(cts_dev, 0x00); + if (ret) { + cts_err("Set display state to SLEEP failed %d", ret); + goto err_free_test_result; + } + recovery_display_state = true; + } + + ret = cts_tcs_set_openshort_mode(cts_dev, CTS_TEST_OPEN); + if (ret) { + cts_err("Set test type to OPEN_TEST failed %d", ret); + goto err_recovery_display_state; + } + + ret = cts_tcs_set_workmode(cts_dev, CTS_FIRMWARE_WORK_MODE_OPEN_SHORT); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_TEST failed %d", ret); + goto err_recovery_display_state; + } + + ret = wait_fw_to_curr_mode(cts_dev); + if (ret) { + cts_err("wait_to_curr_mode failed %d", ret); + goto err_recovery_display_state; + } + + cts_set_int_data_types(cts_dev, INT_DATA_TYPE_RAWDATA); + cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); + + ret = cts_tcs_polling_test_data(cts_dev, (u8 *)test_result, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret) { + cts_err("Read test result failed %d", ret); + goto err_recovery_display_state; + } + +err_recovery_display_state: + if (recovery_display_state) { + int r = cts_tcs_set_display_on(cts_dev, 0x01); + if (r) { + cts_err("Set display state to ACTIVE failed %d", r); + } + } + + if (dump_test_data_to_user) { + *param->test_data_wr_size += tsdata_frame_size; + } + + if (dump_test_data_to_file) { + int r = cts_start_dump_test_data_to_file(param->test_data_filepath, + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); + if (r) { + cts_err("Start dump test data to file failed %d", r); + } + } + + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_tsdata(cts_dev, "Open-circuit", test_result, + dump_test_data_to_console); + } + + if (dump_test_data_to_file) { + cts_stop_dump_test_data_to_file(); + } + + if (driver_validate_data) { + ret = validate_tsdata(cts_dev, "Open-circuit", test_result, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + } + +err_free_test_result: + post_test(cts_dev); + + cts_set_int_data_method(cts_dev, old_int_data_method); + cts_set_int_data_types(cts_dev, old_int_data_types); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + if (ret < 0 && count--) { + if (dump_test_data_to_user) { + *param->test_data_wr_size = 0; + test_result = (u16 *) param->test_data_buf; + } + goto try_again; + } +#else + if (ret < 0 && count--) { + if (dump_test_data_to_user) { + *param->test_data_wr_size = 0; + test_result = (u16 *) param->test_data_buf; + } + goto try_again; + } +#endif + +#ifdef CONFIG_CTS_CHARGER_DETECT + if (cts_is_charger_exist(cts_dev)) { + int r = cts_set_dev_charger_attached(cts_dev, true); + if (r) { + cts_err("Set dev charger attached failed %d", r); + } + } +#endif + +#ifdef CONFIG_CTS_EARJACK_DETECT + if (cts_is_earjack_exist(cts_dev)) { + int r = cts_set_dev_earjack_attached(cts_dev, true); + if (r) + cts_err("Set dev earjack attached failed %d", r); + } +#endif + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) { + cts_enter_glove_mode(cts_dev); + } +#endif + +#ifdef CFG_CTS_FW_LOG_REDIRECT + if (cts_is_fw_log_redirect(cts_dev)) { + cts_enable_fw_log_redirect(cts_dev); + } +#endif + + cts_unlock_device(cts_dev); + cts_start_device(cts_dev); + +free_mem: + if (!dump_test_data_to_user && test_result) { + kfree(test_result); + } + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret > 0) { + cts_info("Open test has %d nodes FAIL, ELAPSED TIME: %lldms", + ret, ktime_to_ms(delta_time)); + } else if (ret < 0) { + cts_info("Open test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + cts_info("Open test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + } + + return ret; +} + +/* Return 0 success + negative value while error occurs + positive value means how many nodes fail */ +int cts_test_short(struct cts_device *cts_dev, struct cts_test_param *param) +{ + bool driver_validate_data = false; + bool validate_data_per_node = false; + bool stop_if_failed = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_file = false; + int num_nodes; + int tsdata_frame_size; + int loopcnt; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + int count = 3; +#else + int count = 3; +#endif + int ret; + u16 *test_result = NULL; + bool recovery_display_state = false; + u8 need_display_on; + ktime_t start_time, end_time, delta_time; + u8 old_int_data_method = cts_dev->fwdata.int_data_method; + u16 old_int_data_types = cts_dev->fwdata.int_data_types; + + if (cts_dev == NULL || param == NULL) { + cts_err("Short test with invalid param: cts_dev: %p test param: %p", + cts_dev, param); + return -EINVAL; + } + + num_nodes = cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; + tsdata_frame_size = 2 * num_nodes; + + driver_validate_data = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); + validate_data_per_node = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + dump_test_data_to_user = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_console = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); + dump_test_data_to_file = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + stop_if_failed = + !!(param->flags & CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED); + + cts_info("Short test, flags: 0x%08x, num invalid node: %u, " + "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", + param->flags, param->num_invalid_node, + param->test_data_filepath, param->test_data_buf_size, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + if (dump_test_data_to_user) { + test_result = (u16 *) param->test_data_buf; + } else { + test_result = (u16 *) kmalloc(tsdata_frame_size, GFP_KERNEL); + if (test_result == NULL) { + cts_err("Allocate test result buffer failed"); + ret = -ENOMEM; + goto show_test_result; + } + } + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto err_free_test_result; + } + + cts_lock_device(cts_dev); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +try_again: +#else +try_again: +#endif + ret = prepare_test(cts_dev); + if (ret) { + cts_err("Prepare test failed %d", ret); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + goto prepare_try; +#else + goto prepare_try; +#endif + } + ret = cts_tcs_is_display_on(cts_dev, &need_display_on); + if (ret) { + cts_err("Read need display on register failed %d", ret); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + goto prepare_try; +#else + goto prepare_try; +#endif + } + + if (need_display_on == 0) { + ret = cts_tcs_set_display_on(cts_dev, 0x00); + if (ret) { + cts_err("Set display state to SLEEP failed %d", ret); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + goto recovery_display_state; +#else + goto recovery_display_state; +#endif + } + recovery_display_state = true; + } + + cts_info("Test short to GND"); + ret = cts_tcs_set_short_test_type(cts_dev, CTS_SHORT_TEST_UNDEFINED); + if (ret) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + cts_err("Set short test type failed %d", ret); +#else + cts_err("Set short test type failed %d", ret); +#endif + goto recovery_display_state; + } + ret = cts_tcs_set_openshort_mode(cts_dev, CTS_TEST_SHORT); + if (ret) { + cts_err("Set test type to SHORT failed %d", ret); + goto recovery_display_state; + } + ret = cts_tcs_set_workmode(cts_dev, CTS_FIRMWARE_WORK_MODE_OPEN_SHORT); + if (ret) { + cts_err("Set firmware work mode to WORK_MODE_TEST failed %d", ret); + goto recovery_display_state; + } + + ret = wait_fw_to_curr_mode(cts_dev); + if (ret) { + cts_err("wait_to_curr_mode failed %d", ret); + goto recovery_display_state; + } + + cts_set_int_data_types(cts_dev, INT_DATA_TYPE_RAWDATA); + cts_set_int_data_method(cts_dev, INT_DATA_METHOD_POLLING); + + ret = cts_tcs_set_short_test_type(cts_dev, CTS_SHORT_TEST_BETWEEN_GND); + if (ret) { + cts_err("Set short test type to SHORT_TO_GND failed %d", ret); + goto recovery_display_state; + } + + ret = cts_tcs_polling_test_data(cts_dev, (u8 *)test_result, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret) { + cts_err("Read test result failed %d", ret); + goto recovery_display_state; + } + if (dump_test_data_to_user) { + *param->test_data_wr_size += tsdata_frame_size; + } + if (dump_test_data_to_file) { + int r = cts_start_dump_test_data_to_file(param->test_data_filepath, + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); + if (r) { + cts_err("Start dump test data to file failed %d", r); + } + } + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_tsdata(cts_dev, "GND-short", test_result, + dump_test_data_to_console); + } + if (driver_validate_data) { + ret = validate_tsdata(cts_dev, "GND-short", test_result, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + if (ret) { + cts_err("Short to GND test failed %d", ret); + if (stop_if_failed) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + goto recovery_display_state; +#else + goto recovery_display_state; +#endif + } + } + } + if (dump_test_data_to_user) { + test_result += num_nodes; + } + + /* Short between colums */ + cts_info("Test short between columns"); + ret = cts_tcs_set_short_test_type(cts_dev, CTS_SHORT_TEST_BETWEEN_COLS); + if (ret) { + cts_err("Set short test type to BETWEEN_COLS failed %d", ret); + goto recovery_display_state; + } + + for (loopcnt = 0; loopcnt < SHORT_COLS_TEST_LOOP; loopcnt++) { + ret = cts_tcs_polling_test_data(cts_dev, (u8 *)test_result, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret) { + cts_err("Read test result failed %d", ret); + goto recovery_display_state; + } + + if (dump_test_data_to_user) { + *param->test_data_wr_size += tsdata_frame_size; + } + + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_tsdata(cts_dev, "Col-short", test_result, + dump_test_data_to_console); + } + + if (driver_validate_data) { + ret = validate_tsdata(cts_dev, "Col-short", test_result, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + if (ret) { + cts_err("Short between columns test failed %d", ret); + if (stop_if_failed) { + goto recovery_display_state; + } + } + } + if (dump_test_data_to_user) { + test_result += num_nodes; + } + } + + /* Short between rows */ + cts_info("Test short between rows"); + ret = cts_tcs_set_short_test_type(cts_dev, CTS_SHORT_TEST_BETWEEN_ROWS); + if (ret) { + cts_err("Set short test type to BETWEEN_ROWS failed %d", ret); + goto recovery_display_state; + } + + for (loopcnt = 0; loopcnt < SHORT_ROWS_TEST_LOOP; loopcnt++) { + ret = cts_tcs_polling_test_data(cts_dev, (u8 *)test_result, + RAWDATA_BUFFER_SIZE(cts_dev)); + if (ret) { + cts_err("Read test result failed %d", ret); + goto recovery_display_state; + } + if (dump_test_data_to_user) { + *param->test_data_wr_size += tsdata_frame_size; + } + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_tsdata(cts_dev, "Row-short", test_result, + dump_test_data_to_console); + } + if (driver_validate_data) { + ret = validate_tsdata(cts_dev, "Row-short", test_result, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + if (ret) { + cts_err("Short between rows test failed %d", ret); + if (stop_if_failed) { + goto recovery_display_state; + } + } + } + if (dump_test_data_to_user) { + test_result += num_nodes; + } + } + +recovery_display_state: + if (recovery_display_state) { + int r = set_display_state(cts_dev, true); + if (r) { + cts_err("Set display state to ACTIVE failed %d", r); + } + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +prepare_try: + post_test(cts_dev); + cts_set_int_data_method(cts_dev, old_int_data_method); + cts_set_int_data_types(cts_dev, old_int_data_types); + + if (ret < 0 && count--) { + if (dump_test_data_to_user) { + *param->test_data_wr_size = 0; + test_result = (u16 *) param->test_data_buf; + } + goto try_again; + } +#else +prepare_try: + post_test(cts_dev); + cts_set_int_data_method(cts_dev, old_int_data_method); + cts_set_int_data_types(cts_dev, old_int_data_types); + + if (ret < 0 && count--) { + if (dump_test_data_to_user) { + *param->test_data_wr_size = 0; + test_result = (u16 *) param->test_data_buf; + } + goto try_again; + } +#endif + + if (dump_test_data_to_file) { + cts_stop_dump_test_data_to_file(); + } +#ifdef CONFIG_CTS_CHARGER_DETECT + if (cts_is_charger_exist(cts_dev)) { + int r = cts_set_dev_charger_attached(cts_dev, true); + if (r) { + cts_err("Set dev charger attached failed %d", r); + } + } +#endif + +#ifdef CONFIG_CTS_EARJACK_DETECT + if (cts_is_earjack_exist(cts_dev)) { + int r = cts_set_dev_earjack_attached(cts_dev, true); + if (r) + cts_err("Set dev earjack attached failed %d", r); + } +#endif + +#ifdef CONFIG_CTS_GLOVE + if (cts_is_glove_enabled(cts_dev)) { + cts_enter_glove_mode(cts_dev); + } +#endif + +#ifdef CFG_CTS_FW_LOG_REDIRECT + if (cts_is_fw_log_redirect(cts_dev)) { + cts_enable_fw_log_redirect(cts_dev); + } +#endif + + cts_unlock_device(cts_dev); + + cts_start_device(cts_dev); + +err_free_test_result: + if (!dump_test_data_to_user && test_result) { + kfree(test_result); + } + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret > 0) { + cts_info("Short test has %d nodes FAIL, ELAPSED TIME: %lldms", + ret, ktime_to_ms(delta_time)); + } else if (ret < 0) { + cts_info("Short test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + cts_info("Short test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + } + + return ret; +} + +static int validate_comp_cap(struct cts_device *cts_dev, const char *desc, u8 *cap, + u32 *invalid_nodes, u32 num_invalid_nodes, bool per_node, int *min, int *max) +{ +#define SPLIT_LINE_STR \ + "------------------------------" + + int r, c; + int failed_cnt = 0; + + cts_info("Validate %s data: %s, num invalid node: %u, thresh[0]=[%d, %d]", + desc, per_node ? "Per-Node" : "Uniform-Threshold", + num_invalid_nodes, min ? min[0] : INT_MIN, max ? max[0] : INT_MAX); + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + int offset = r * cts_dev->hwdata->num_col + c; + + if (num_invalid_nodes && + is_invalid_node(invalid_nodes, num_invalid_nodes, r, c)) { + continue; + } + + if ((min != NULL && cap[offset] < min[per_node ? offset : 0]) || + (max != NULL && cap[offset] > max[per_node ? offset : 0])) { + if (failed_cnt == 0) { + cts_info(SPLIT_LINE_STR); + cts_info("%s failed nodes:", desc); + } + failed_cnt++; + + cts_info(" %3d: [%-2d][%-2d] = %u", failed_cnt, r, c, cap[offset]); + } + } + } + + if (failed_cnt) { + cts_info(SPLIT_LINE_STR); + cts_info("%s test %d node total failed", desc, failed_cnt); + } + + return failed_cnt; + +#undef SPLIT_LINE_STR +} + +void cts_dump_comp_cap(struct cts_device *cts_dev, u8 *cap, bool to_console) +{ +#define SPLIT_LINE_STR \ + "-----------------------------------------------------------------------------" +#define ROW_NUM_FORMAT_STR "%2d | " +#define COL_NUM_FORMAT_STR "%3u " +#define DATA_FORMAT_STR "%4d" + + int r, c; + u32 max, min, sum, average; + int max_r, max_c, min_r, min_c; + char line_buf[128]; + int count; + + max = min = cap[0]; + sum = 0; + max_r = max_c = min_r = min_c = 0; + for (r = 0; r < cts_dev->fwdata.rows; r++) { + for (c = 0; c < cts_dev->fwdata.cols; c++) { + u16 val = cap[r * cts_dev->hwdata->num_col + c]; + + sum += val; + if (val > max) { + max = val; + max_r = r; + max_c = c; + } else if (val < min) { + min = val; + min_r = r; + min_c = c; + } + } + } + average = sum / (cts_dev->fwdata.rows * cts_dev->fwdata.cols); + + count = 0; + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + " Compensate Cap MIN: [%u][%u]=%u, MAX: [%u][%u]=%u, AVG=%u", + min_r, min_c, min, max_r, max_c, max, average); + if (to_console) { + cts_info(SPLIT_LINE_STR); + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } + + count = 0; + count += scnprintf(line_buf + count, sizeof(line_buf) - count, " "); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + COL_NUM_FORMAT_STR, c); + } + if (to_console) { + cts_info("%s", line_buf); + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } + + for (r = 0; r < cts_dev->fwdata.rows; r++) { + count = 0; + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + ROW_NUM_FORMAT_STR, r); + for (c = 0; c < cts_dev->fwdata.cols; c++) { + count += scnprintf(line_buf + count, sizeof(line_buf) - count, + DATA_FORMAT_STR, cap[r * cts_dev->hwdata->num_col + c]); + } + if (to_console) { + cts_info("%s", line_buf); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, line_buf, count); + cts_write_file(cts_test_data_filp, "\n", 1); + } + } + + if (to_console) { + cts_info(SPLIT_LINE_STR); + } + if (cts_test_data_filp) { + cts_write_file(cts_test_data_filp, SPLIT_LINE_STR, strlen(SPLIT_LINE_STR)); + cts_write_file(cts_test_data_filp, "\n", 1); + } +#undef SPLIT_LINE_STR +#undef ROW_NUM_FORMAT_STR +#undef COL_NUM_FORMAT_STR +#undef DATA_FORMAT_STR +} + +int cts_test_compensate_cap(struct cts_device *cts_dev, + struct cts_test_param *param) +{ + bool driver_validate_data = false; + bool validate_data_per_node = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_file = false; + int num_nodes; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + int count = 3; +#else + int count = 3; +#endif + u8 *cap = NULL; + int ret = 0; + ktime_t start_time, end_time, delta_time; + + if (cts_dev == NULL || param == NULL) { + cts_err("Compensate cap test with invalid param: cts_dev: %p test param: %p", + cts_dev, param); + return -EINVAL; + } + + num_nodes = cts_dev->hwdata->num_row * cts_dev->hwdata->num_col; + + driver_validate_data = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_DATA); + if (driver_validate_data) { + validate_data_per_node = + !!(param->flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + } + dump_test_data_to_user = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_console = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE); + dump_test_data_to_file = + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + + cts_info("Compensate cap test, flags: 0x%08x num invalid node: %u, " + "test data file: '%s' buf size: %d, drive log file: '%s' buf size: %d", + param->flags, param->num_invalid_node, + param->test_data_filepath, param->test_data_buf_size, + param->driver_log_filepath, param->driver_log_buf_size); + + start_time = ktime_get(); + + if (dump_test_data_to_user) { + cap = (u8 *) param->test_data_buf; + } else { + cap = (u8 *) kzalloc(num_nodes, GFP_KERNEL); + if (cap == NULL) { + cts_err("Alloc mem for compensate cap failed"); + ret = -ENOMEM; + goto show_test_result; + } + } + + /* Stop device to avoid un-wanted interrrupt */ + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + goto free_mem; + } + + cts_lock_device(cts_dev); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +try_again: +#else +try_again: +#endif + ret = prepare_test(cts_dev); + if (ret) { + cts_err("Prepare test failed %d", ret); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + goto prepare_try; +#else + goto prepare_try; +#endif + } + ret = cts_tcs_top_get_cnegdata(cts_dev, cap, num_nodes); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +prepare_try: +#else +prepare_try: +#endif + post_test(cts_dev); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + if (ret && count--) + goto try_again; +#else + if (ret && count--) + goto try_again; +#endif + + cts_unlock_device(cts_dev); + if (ret) { + cts_err("Get compensate cap failed %d", ret); + goto start_device; + } + + if (dump_test_data_to_user) { + *param->test_data_wr_size = num_nodes; + } + + if (dump_test_data_to_file) { + int r = cts_start_dump_test_data_to_file(param->test_data_filepath, + !!(param->flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND)); + if (r) { + cts_err("Start dump test data to file failed %d", r); + } + } + + if (dump_test_data_to_console || dump_test_data_to_file) { + cts_dump_comp_cap(cts_dev, cap, dump_test_data_to_console); + } + + if (dump_test_data_to_file) { + cts_stop_dump_test_data_to_file(); + } + + if (driver_validate_data) { + ret = validate_comp_cap(cts_dev, "Compensate-Cap", cap, param->invalid_nodes, + param->num_invalid_node, validate_data_per_node, param->min, param->max); + } + +start_device: + { + int r = cts_start_device(cts_dev); + if (r) { + cts_err("Start device failed %d", r); + } + } + +free_mem: + if (!dump_test_data_to_user && cap) { + kfree(cap); + } + +show_test_result: + end_time = ktime_get(); + delta_time = ktime_sub(end_time, start_time); + if (ret > 0) { + cts_info("Compensate-Cap test has %d nodes FAIL, ELAPSED TIME: %lldms", + ret, ktime_to_ms(delta_time)); + } else if (ret < 0) { + cts_info("Compensate-Cap test FAIL %d(%s), ELAPSED TIME: %lldms", + ret, cts_strerror(ret), ktime_to_ms(delta_time)); + } else { + cts_info("Compensate-Cap test PASS, ELAPSED TIME: %lldms", + ktime_to_ms(delta_time)); + } + + return ret; +} diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_test.h b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_test.h new file mode 100644 index 000000000000..6cab0f2f7e74 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_test.h @@ -0,0 +1,106 @@ +#ifndef CTS_TEST_H +#define CTS_TEST_H + +struct cts_device; + +#define CTS_TEST_FLAG_RESET_BEFORE_TEST (1u << 0) +#define CTS_TEST_FLAG_RESET_AFTER_TEST (1u << 1) +#define CTS_TEST_FLAG_DISPLAY_ON (1u << 2) +#define CTS_TEST_FLAG_DISABLE_GAS (1u << 3) +#define CTS_TEST_FLAG_DISABLE_LINESHIFT (1u << 4) + +#define CTS_TEST_FLAG_VALIDATE_DATA (1u << 8) +#define CTS_TEST_FLAG_VALIDATE_PER_NODE (1u << 9) +#define CTS_TEST_FLAG_VALIDATE_MIN (1u << 10) +#define CTS_TEST_FLAG_VALIDATE_MAX (1u << 11) +#define CTS_TEST_FLAG_VALIDATE_SKIP_INVALID_NODE (1u << 12) +#define CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED (1u << 13) + +#define CTS_TEST_FLAG_DUMP_TEST_DATA_TO_CONSOLE (1u << 16) +#define CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE (1u << 17) +#define CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE (1u << 18) +#define CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND (1u << 19) +#define CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_CSV (1u << 20) + +#define CTS_TEST_FLAG_DRIVER_LOG_TO_USERSPACE (1u << 24) +#define CTS_TEST_FLAG_DRIVER_LOG_TO_FILE (1u << 25) +#define CTS_TEST_FLAG_DRIVER_LOG_TO_FILE_APPEND (1u << 26) + +#define MAKE_INVALID_NODE(r, c) (((c) << 16) | (r)) +#define INVALID_NODE_ROW(node) ((u16)(node)) +#define INVALID_NODE_COL(node) ((u16)((node) >> 16)) + +enum cts_test_item { + CTS_TEST_RESET_PIN = 1, + CTS_TEST_INT_PIN, + CTS_TEST_RAWDATA, + CTS_TEST_NOISE, + CTS_TEST_OPEN, + CTS_TEST_SHORT, + CTS_TEST_COMPENSATE_CAP, +}; + +#pragma pack(1) +struct cts_test_param { + int test_item; + + __u32 flags; + + __u32 num_invalid_node; + __u32 *invalid_nodes; + int *min; + int *max; + + int *test_result; + s64 *elapsed_time_ms; + + void *test_data_buf; + int test_data_buf_size; + int *test_data_wr_size; + const char *test_data_filepath; + + int driver_log_level; + char *driver_log_buf; + int driver_log_buf_size; + int *driver_log_wr_size; + const char *driver_log_filepath; + + void *priv_param; + int priv_param_size; +}; +#pragma pack() + +struct cts_rawdata_test_priv_param { + __u32 frames; + //__u8 work_mode; +}; + +struct cts_noise_test_priv_param { + __u32 frames; + //__u8 work_mode; +}; + +extern const char *cts_test_item_str(int test_item); +extern int cts_write_file(struct file *filp, const void *data, size_t size); +extern int cts_mkdir_for_file(const char *filepath, umode_t mode); + +extern int cts_start_dump_test_data_to_file(const char *filepath, + bool append_to_file); +extern void cts_stop_dump_test_data_to_file(void); + +extern int cts_test_reset_pin(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_int_pin(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_rawdata(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_noise(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_open(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_short(struct cts_device *cts_dev, + struct cts_test_param *param); +extern int cts_test_compensate_cap(struct cts_device *cts_dev, + struct cts_test_param *param); + +#endif /* CTS_TEST_H */ diff --git a/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_tool.c b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_tool.c new file mode 100644 index 000000000000..ca58282ec197 --- /dev/null +++ b/drivers/input/touchscreen/chipone_tddi_v2_mmi/cts_tool.c @@ -0,0 +1,1214 @@ +#define LOG_TAG "Tool" + +#include "cts_config.h" +#include "cts_platform.h" +#include "cts_core.h" +#include "cts_firmware.h" +#include "cts_test.h" +#include "cts_tcs.h" + +#ifdef CONFIG_CTS_LEGACY_TOOL + +#define CTS_TOOL_IOCTL_TCS_RW_BUF_MAX_SIZ 1024 +#define CTS_TOOL_IOCTL_TCS_RW_OPFLAG_READ 1 +#define CTS_TOOL_IOCTL_TCS_RW_OPFLAG_WRITE 2 + +#pragma pack(1) +/** Tool command structure */ +struct cts_tool_cmd { + u8 cmd; + u8 flag; + u8 circle; + u8 times; + u8 retry; + u32 data_len; + u8 addr_len; + u8 addr[4]; + u8 tcs[2]; + u8 data[PAGE_SIZE]; + +}; +struct cts_ioctl_tcs_rw_data { + u32 opflags; + u32 txlen; + u32 rxlen; + u8 __user *txbuf; + u8 __user *rxbuf; +}; +#pragma pack() + +#define CTS_TOOL_CMD_HEADER_LENGTH (16) + +enum cts_tool_cmd_code { + CTS_TOOL_CMD_GET_PANEL_PARAM = 0, + CTS_TOOL_CMD_GET_DOWNLOAD_STATUS = 2, + CTS_TOOL_CMD_GET_RAW_DATA = 4, + CTS_TOOL_CMD_GET_DIFF_DATA = 6, + CTS_TOOL_CMD_READ_HOSTCOMM = 12, + CTS_TOOL_CMD_READ_ADC_STATUS = 14, + CTS_TOOL_CMD_READ_GESTURE_INFO = 16, + CTS_TOOL_CMD_READ_HOSTCOMM_MULTIBYTE = 18, + CTS_TOOL_CMD_READ_PROGRAM_MODE_MULTIBYTE = 20, + CTS_TOOL_CMD_READ_ICTYPE = 22, + CTS_TOOL_CMD_I2C_DIRECT_READ = 24, + CTS_TOOL_CMD_GET_DRIVER_INFO = 26, + CTS_TOOL_CMD_TCS_READ_HW_CMD = 28, + CTS_TOOL_CMD_TCS_READ_DDI_CMD = 30, + CTS_TOOL_CMD_TCS_READ_FW_CMD = 32, + CTS_TOOL_CMD_GET_BASE_DATA = 34, + CTS_TOOL_CMD_GET_INT_DATAS = 36, + + CTS_TOOL_CMD_UPDATE_PANEL_PARAM_IN_SRAM = 1, + CTS_TOOL_CMD_DOWNLOAD_FIRMWARE_WITH_FILENAME = 3, + CTS_TOOL_CMD_DOWNLOAD_FIRMWARE = 5, + CTS_TOOL_CMD_WRITE_HOSTCOMM = 11, + CTS_TOOL_CMD_WRITE_HOSTCOMM_MULTIBYTE = 15, + CTS_TOOL_CMD_WRITE_PROGRAM_MODE_MULTIBYTE = 17, + CTS_TOOL_CMD_I2C_DIRECT_WRITE = 19, + CTS_TOOL_CMD_TCS_WRITE_HW_CMD = 21, + CTS_TOOL_CMD_TCS_WRITE_DDI_CMD = 23, + CTS_TOOL_CMD_TCS_WRITE_FW_CMD = 25, + CTS_TOOL_CMD_TCS_ENABLE_GET_RAWDATA_CMD = 27, + CTS_TOOL_CMD_TCS_DISABLE_GET_RAWDATA_CMD = 29, + CTS_TOOL_CMD_SET_INT_DATA_TYPE_AND_METHOD = 31, + +}; + +struct cts_test_ioctl_data { + __u32 ntests; + struct cts_test_param __user *tests; +}; + + +#define CTS_TOOL_IOCTL_GET_DRIVER_VERSION _IOR('C', 0x00, u32 *) +#define CTS_TOOL_IOCTL_GET_DEVICE_TYPE _IOR('C', 0x01, u32 *) +#define CTS_TOOL_IOCTL_GET_FW_VERSION _IOR('C', 0x02, u16 *) +#define CTS_TOOL_IOCTL_GET_RESOLUTION _IOR('C', 0x03, u32 *) /* X in LSW, Y in MSW */ +#define CTS_TOOL_IOCTL_GET_ROW_COL _IOR('C', 0x04, u16 *) /* row in LSW, col in MSW */ +#define CTS_TOOL_IOCTL_GET_MODULE_ID _IOWR('C', 0x05, u32 *) + +#define CTS_TOOL_IOCTL_TEST _IOWR('C', 0x10, struct cts_test_ioctl_data *) +#define CTS_TOOL_IOCTL_RDWR_REG _IOWR('C', 0x20, struct cts_rdwr_reg_ioctl_data *) +#define CTS_TOOL_IOCTL_UPGRADE_FW _IOWR('C', 0x21, struct cts_upgrade_fw_ioctl_data *) + +#define CTS_TOOL_IOCTL_TCS_RW _IOWR('C', 0x30, struct cts_ioctl_tcs_rw_data *) + +#define CTS_DRIVER_VERSION_CODE ((CFG_CTS_DRIVER_MAJOR_VERSION << 16) | \ + (CFG_CTS_DRIVER_MINOR_VERSION << 8) | \ + (CFG_CTS_DRIVER_PATCH_VERSION << 0)) + +static struct cts_tool_cmd cts_tool_cmd; +static char cts_tool_firmware_filepath[PATH_MAX]; +/* If CFG_CTS_MAX_I2C_XFER_SIZE < 58(PC tool length), this is neccessary */ +#ifdef CONFIG_CTS_I2C_HOST +static u32 cts_tool_direct_access_addr = 0; +#endif /* CONFIG_CTS_I2C_HOST */ + +static int cts_tool_open(struct inode *inode, struct file *file) +{ + file->private_data = PDE_DATA(inode); + return 0; +} + +static ssize_t cts_tool_read(struct file *file, + char __user *buffer, size_t count, loff_t *ppos) +{ +#define RAWDATA_BUFFER_SIZE(cts_dev) \ + (cts_dev->hwdata->num_row * cts_dev->hwdata->num_col * 2) + struct chipone_ts_data *cts_data; + struct cts_tool_cmd *cmd; + struct cts_device *cts_dev; + int ret = 0; + + cts_data = (struct chipone_ts_data *)file->private_data; + if (cts_data == NULL) { + cts_err("Read with private_data = NULL"); + return -EIO; + } + + cmd = &cts_tool_cmd; + cts_dev = &cts_data->cts_dev; + cts_lock_device(cts_dev); + + cts_dbg("read: flag:%d, addr:0x%06x, tcm:0x%04x, datalen:%d", + cmd->cmd, get_unaligned_le32(cmd->addr), + get_unaligned_le16(cmd->tcs), cmd->data_len); + + switch (cmd->cmd) { + case CTS_TOOL_CMD_TCS_READ_HW_CMD: + cts_tcs_read_hw_reg(cts_dev, get_unaligned_le32(cmd->addr), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_TCS_READ_DDI_CMD: + cts_tcs_read_ddi_reg(cts_dev, get_unaligned_le32(cmd->addr), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_TCS_READ_FW_CMD: + cts_tcs_read_reg(cts_dev, get_unaligned_le16(cmd->tcs), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_GET_PANEL_PARAM: + cts_info("Get panel param len: %u", cmd->data_len); + break; + case CTS_TOOL_CMD_GET_DOWNLOAD_STATUS: + cmd->data[0] = 100; + cts_info("Get update status = %hhu", cmd->data[0]); + break; + + case CTS_TOOL_CMD_GET_RAW_DATA: + case CTS_TOOL_CMD_GET_DIFF_DATA: + case CTS_TOOL_CMD_GET_BASE_DATA: + cts_info("Get %s data row: %u col: %u len: %u", + cmd->cmd == CTS_TOOL_CMD_GET_RAW_DATA ? "raw" : "diff", + cmd->addr[1], cmd->addr[0], cmd->data_len); + + if (cmd->cmd == CTS_TOOL_CMD_GET_RAW_DATA) { + ret = cts_tcs_top_get_rawdata(cts_dev, (u8 *) cmd->data, + RAWDATA_BUFFER_SIZE(cts_dev)); + } else if (cmd->cmd == CTS_TOOL_CMD_GET_DIFF_DATA) { + ret = cts_tcs_top_get_manual_diff(cts_dev, (u8 *) cmd->data, + RAWDATA_BUFFER_SIZE(cts_dev)); + } else if (cmd->cmd == CTS_TOOL_CMD_GET_BASE_DATA) { + ret = cts_tcs_top_get_basedata(cts_dev, (u8 *) cmd->data, + RAWDATA_BUFFER_SIZE(cts_dev)); + } + if (ret) { + cts_err("Get %s data failed %d", + cmd->cmd == CTS_TOOL_CMD_GET_RAW_DATA ? "raw" : + cmd->cmd == + CTS_TOOL_CMD_GET_DIFF_DATA ? "diff" : "base", + ret); + } + break; + case CTS_TOOL_CMD_GET_INT_DATAS: + memcpy((u8 *)cmd->data, cts_dev->int_data, cts_dev->fwdata.int_data_size); + cmd->data_len = cts_dev->fwdata.int_data_size; + break; + case CTS_TOOL_CMD_READ_HOSTCOMM: + ret = cts_fw_reg_readb(cts_dev, get_unaligned_le16(cmd->addr), cmd->data); + if (ret) { + cts_err("Read firmware reg addr 0x%04x failed %d", + get_unaligned_le16(cmd->addr), ret); + } else { + cts_dbg("Read firmware reg addr 0x%04x, val=0x%02x", + get_unaligned_le16(cmd->addr), cmd->data[0]); + } + break; + +#ifdef CFG_CTS_GESTURE + case CTS_TOOL_CMD_READ_GESTURE_INFO: + ret = cts_get_gesture_info(cts_dev, cmd->data); + if (ret) + cts_err("Get gesture info failed %d", ret); + break; +#endif /* CFG_CTS_GESTURE */ + + case CTS_TOOL_CMD_READ_HOSTCOMM_MULTIBYTE: + cmd->data_len = min((size_t)cmd->data_len, sizeof(cmd->data)); + ret = cts_fw_reg_readsb(cts_dev, get_unaligned_le16(cmd->addr), + cmd->data, cmd->data_len); + if (ret) { + cts_err("Read firmware reg addr 0x%04x len %u failed %d", + get_unaligned_le16(cmd->addr), cmd->data_len, ret); + } else { + cts_dbg("Read firmware reg addr 0x%04x len %u", + get_unaligned_le16(cmd->addr), cmd->data_len); + } + break; + + case CTS_TOOL_CMD_READ_PROGRAM_MODE_MULTIBYTE: + cts_dbg("Read under program mode addr 0x%06x len %u", + (cmd->flag << 16) | get_unaligned_le16(cmd->addr), + cmd->data_len); + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + break; + } + + ret = cts_sram_readsb(&cts_data->cts_dev, + get_unaligned_le16(cmd->addr), cmd->data, cmd->data_len); + if (ret) { + cts_err("Read under program mode I2C xfer failed %d", + ret); + } + + ret = cts_enter_normal_mode(cts_dev); + if (ret) { + cts_err("Enter normal mode failed %d", ret); + break; + } + break; + + case CTS_TOOL_CMD_READ_ICTYPE: + cts_info("Get IC type"); + break; + +#ifdef CONFIG_CTS_I2C_HOST + case CTS_TOOL_CMD_I2C_DIRECT_READ: + { + u32 addr_width; + char *wr_buff = NULL; + u8 addr_buff[4]; + size_t left_size, max_xfer_size; + u8 *data; + + if (cmd->addr[0] != CTS_DEV_PROGRAM_MODE_I2CADDR) { + cmd->addr[0] = CTS_DEV_NORMAL_MODE_I2CADDR; + addr_width = 2; + } else { + addr_width = + cts_dev->hwdata->program_addr_width; + } + + cts_dbg("Direct read from i2c_addr 0x%02x addr 0x%06x size %u", + cmd->addr[0], cts_tool_direct_access_addr, cmd->data_len); + + left_size = cmd->data_len; + max_xfer_size = cts_plat_get_max_i2c_xfer_size(cts_dev->pdata); + data = cmd->data; + while (left_size) { + size_t xfer_size = min(left_size, max_xfer_size); + ret = cts_plat_i2c_read(cts_data->pdata, cmd->addr[0], wr_buff, + addr_width, data, xfer_size, 1, 0); + if (ret) { + cts_err("Direct read i2c_addr 0x%02x addr 0x%06x " + "len %zu failed %d", cmd->addr[0], + cts_tool_direct_access_addr, + xfer_size, ret); + break; + } + + left_size -= xfer_size; + if (left_size) { + data += xfer_size; + cts_tool_direct_access_addr += xfer_size; + if (addr_width == 2) { + put_unaligned_be16(cts_tool_direct_access_addr, addr_buff); + } else if (addr_width == 3) { + put_unaligned_be24(cts_tool_direct_access_addr, addr_buff); + } + wr_buff = addr_buff; + } + } + } + break; +#endif + case CTS_TOOL_CMD_GET_DRIVER_INFO: + break; + + default: + cts_warn("Read unknown command %u", cmd->cmd); + ret = -EINVAL; + break; + } + + cts_unlock_device(cts_dev); + + if (ret == 0) { + if (cmd->cmd == CTS_TOOL_CMD_I2C_DIRECT_READ) { + ret = copy_to_user(buffer + CTS_TOOL_CMD_HEADER_LENGTH, + cmd->data, cmd->data_len); + } else { + ret = copy_to_user(buffer, cmd->data, cmd->data_len); + } + if (ret) { + cts_err("Copy data to user buffer failed %d", ret); + return 0; + } + + return cmd->data_len; + } + + return 0; +} + +static ssize_t cts_tool_write(struct file *file, + const char __user *buffer, size_t count, loff_t *ppos) +{ + struct chipone_ts_data *cts_data; + struct cts_device *cts_dev; + struct cts_tool_cmd *cmd; + int ret = 0; + + if (count < CTS_TOOL_CMD_HEADER_LENGTH || count > PAGE_SIZE) { + cts_err("Write len %zu invalid", count); + return -EFAULT; + } + + cts_data = (struct chipone_ts_data *)file->private_data; + if (cts_data == NULL) { + cts_err("Write with private_data = NULL"); + return -EIO; + } + + cmd = &cts_tool_cmd; + ret = copy_from_user(cmd, buffer, CTS_TOOL_CMD_HEADER_LENGTH); + cts_info("write: flag:%d, addr:0x%06x, tcmd:0x%04x data:0x%02x, datalen:%d", + cmd->cmd, get_unaligned_le32(cmd->addr), + get_unaligned_le16(cmd->tcs), cmd->data[0], cmd->data_len); + + if (ret) { + cts_err("Copy command header from user buffer failed %d", ret); + return -EIO; + } else + ret = CTS_TOOL_CMD_HEADER_LENGTH; + + if (cmd->data_len > PAGE_SIZE) { + cts_err("Write with invalid count %d", cmd->data_len); + return -EIO; + } + + if (cmd->cmd & BIT(0)) { + if (cmd->data_len) { + ret = copy_from_user(cmd->data, buffer + CTS_TOOL_CMD_HEADER_LENGTH, + cmd->data_len); + if (ret) { + cts_err("Copy command payload from user buffer len %u failed %d", + cmd->data_len, ret); + return -EIO; + } + } + } else { + /* Prepare for read command */ + cts_dbg("Write read command(%d) header, prepare read size: %d", + cmd->cmd, cmd->data_len); + return CTS_TOOL_CMD_HEADER_LENGTH + cmd->data_len; + } + + cts_dev = &cts_data->cts_dev; + cts_lock_device(cts_dev); + + switch (cmd->cmd) { + case CTS_TOOL_CMD_TCS_WRITE_HW_CMD: + cts_tcs_write_hw_reg(cts_dev, get_unaligned_le32(cmd->addr), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_TCS_WRITE_DDI_CMD: + cts_tcs_write_ddi_reg(cts_dev, get_unaligned_le32(cmd->addr), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_TCS_WRITE_FW_CMD: + cts_tcs_write_reg(cts_dev, get_unaligned_le16(cmd->tcs), + cmd->data, cmd->data_len); + break; + case CTS_TOOL_CMD_TCS_ENABLE_GET_RAWDATA_CMD: + case CTS_TOOL_CMD_TCS_DISABLE_GET_RAWDATA_CMD: + cts_info("Do nothing"); + break; + case CTS_TOOL_CMD_SET_INT_DATA_TYPE_AND_METHOD: + cts_set_int_data_types(cts_dev, cmd->data[0]); + cts_set_int_data_method(cts_dev, cmd->data[1]); + break; + case CTS_TOOL_CMD_UPDATE_PANEL_PARAM_IN_SRAM: + cts_info("Write panel param len %u data\n", cmd->data_len); + ret = cts_fw_reg_writesb(cts_dev, CTS_DEVICE_FW_REG_PANEL_PARAM, + cmd->data, cmd->data_len); + if (ret) { + cts_err("Write panel param failed %d", ret); + break; + } + + ret = cts_send_command(cts_dev, CTS_CMD_RESET); + + mdelay(100); + + break; + + case CTS_TOOL_CMD_DOWNLOAD_FIRMWARE_WITH_FILENAME: + cts_info("Write firmware path: '%.*s'", cmd->data_len, cmd->data); + + memcpy(cts_tool_firmware_filepath, cmd->data, cmd->data_len); + cts_tool_firmware_filepath[cmd->data_len] = '\0'; + break; + + case CTS_TOOL_CMD_DOWNLOAD_FIRMWARE: + cts_info("Start download firmware path: '%s'", cts_tool_firmware_filepath); + + ret = cts_stop_device(cts_dev); + if (ret) { + cts_err("Stop device failed %d", ret); + break; + } +/** + *TODO: Use async mode such as thread, otherwise, + *host can not get update status. + */ + ret = cts_update_firmware_from_file(cts_dev, + cts_tool_firmware_filepath, true); + if (ret) + cts_err("Updata firmware failed %d", ret); + + ret = cts_start_device(cts_dev); + if (ret) { + cts_err("Start device failed %d", ret); + break; + } + break; + + case CTS_TOOL_CMD_WRITE_HOSTCOMM: + cts_dbg("Write firmware reg addr: 0x%04x val=0x%02x", + get_unaligned_le16(cmd->addr), cmd->data[0]); + + ret = cts_fw_reg_writeb(cts_dev, get_unaligned_le16(cmd->addr), + cmd->data[0]); + if (ret) { + cts_err("Write firmware reg addr: 0x%04x val=0x%02x failed %d", + get_unaligned_le16(cmd->addr), cmd->data[0], ret); + } + break; + + case CTS_TOOL_CMD_WRITE_HOSTCOMM_MULTIBYTE: + cts_dbg("Write firmare reg addr: 0x%04x len %u", + get_unaligned_le16(cmd->addr), cmd->data_len); + ret = cts_fw_reg_writesb(cts_dev, get_unaligned_le16(cmd->addr), + cmd->data, cmd->data_len); + if (ret) { + cts_err("Write firmare reg addr: 0x%04x len %u failed %d", + get_unaligned_le16(cmd->addr), cmd->data_len, ret); + } + break; + + case CTS_TOOL_CMD_WRITE_PROGRAM_MODE_MULTIBYTE: + cts_dbg("Write to addr 0x%06x size %u under program mode", + (cmd->flag << 16) | (cmd->addr[1] << 8) | cmd->addr[0], + cmd->data_len); + ret = cts_enter_program_mode(cts_dev); + if (ret) { + cts_err("Enter program mode failed %d", ret); + break; + } + + ret = cts_sram_writesb(cts_dev, (cmd->flag << 16) | (cmd->addr[1] << 8) | + cmd->addr[0], cmd->data, cmd->data_len); + if (ret) + cts_err("Write program mode multibyte failed %d", ret); + + ret = cts_enter_normal_mode(cts_dev); + if (ret) { + cts_err("Enter normal mode failed %d", ret); + break; + } + + break; + +#ifdef CONFIG_CTS_I2C_HOST + case CTS_TOOL_CMD_I2C_DIRECT_WRITE: + { + u32 addr_width; + size_t left_payload_size; /* Payload exclude address field */ + size_t max_xfer_size; + char *payload; + + if (cmd->addr[0] != CTS_DEV_PROGRAM_MODE_I2CADDR) { + cmd->addr[0] = CTS_DEV_NORMAL_MODE_I2CADDR; + addr_width = 2; + cts_tool_direct_access_addr = get_unaligned_be16(cmd->data); + } else { + addr_width = cts_dev->hwdata->program_addr_width; + cts_tool_direct_access_addr = get_unaligned_be24(cmd->data); + } + + if (cmd->data_len < addr_width) { + cts_err("Direct write too short %d < address width %d", + cmd->data_len, addr_width); + ret = -EINVAL; + break; + } + + cts_dbg("Direct write to i2c_addr 0x%02x addr 0x%06x size %u", + cmd->addr[0], cts_tool_direct_access_addr, cmd->data_len); + + left_payload_size = cmd->data_len - addr_width; + max_xfer_size = cts_plat_get_max_i2c_xfer_size(cts_dev->pdata); + payload = cmd->data + addr_width; + do { + size_t xfer_payload_size = min(left_payload_size, + max_xfer_size - addr_width); + size_t xfer_len = xfer_payload_size + addr_width; + + ret = cts_plat_i2c_write(cts_data->pdata, + cmd->addr[0], payload - addr_width, xfer_len, 1, 0); + if (ret) { + cts_err("Direct write i2c_addr 0x%02x addr 0x%06x len %zu failed %d", + cmd->addr[0], cts_tool_direct_access_addr, + xfer_len, ret); + break; + } + + left_payload_size -= xfer_payload_size; + if (left_payload_size) { + payload += xfer_payload_size; + cts_tool_direct_access_addr += xfer_payload_size; + if (addr_width == 2) { + put_unaligned_be16(cts_tool_direct_access_addr, + payload - addr_width); + } else if (addr_width == 3) { + put_unaligned_be24(cts_tool_direct_access_addr, + payload - addr_width); + } + } + } while (left_payload_size); + } + break; +#endif + default: + cts_warn("Write unknown command %u", cmd->cmd); + ret = -EINVAL; + break; + } + + cts_unlock_device(cts_dev); + + return ret ? 0 : cmd->data_len + CTS_TOOL_CMD_HEADER_LENGTH; +} + + +static long cts_ioctl_tcs_rw(struct cts_device *cts_dev, + struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret; + struct cts_ioctl_tcs_rw_data rwdata; + u8 *txbuf = NULL; + u8 *rxbuf = NULL; + + if (copy_from_user(&rwdata, + (struct cts_ioctl_tcs_rw_data __user *)arg, + sizeof(rwdata))) { + cts_err("Copy ioctl tcs rw arg to kernel failed"); + return -EFAULT; + } + + if ((rwdata.opflags != CTS_TOOL_IOCTL_TCS_RW_OPFLAG_READ) && + (rwdata.opflags != CTS_TOOL_IOCTL_TCS_RW_OPFLAG_WRITE)) + { + cts_err("ioctl tcs rw with invalid opflags %u", rwdata.opflags); + return -EINVAL; + } + + if (rwdata.txlen > CTS_TOOL_IOCTL_TCS_RW_BUF_MAX_SIZ) { + cts_err("Send too long: %d", rwdata.txlen); + return -EINVAL; + } + + if (rwdata.rxlen > CTS_TOOL_IOCTL_TCS_RW_BUF_MAX_SIZ) { + cts_err("Recv too long: %d", rwdata.rxlen); + return -EINVAL; + } + + if (!rwdata.txbuf || !rwdata.rxbuf) { + cts_err("Txbuf of Rxbuf is NULL!"); + return -EINVAL; + } + + txbuf = memdup_user(rwdata.txbuf, rwdata.txlen); + if (IS_ERR(txbuf)) { + ret = PTR_ERR(txbuf); + cts_err("Memdup txbuf failed %d", ret); + txbuf = NULL; + goto free_buf; + } + + rxbuf = memdup_user(rwdata.rxbuf, rwdata.rxlen); + if (IS_ERR(rxbuf)) { + ret = PTR_ERR(rxbuf); + cts_err("Memdup rxbuf failed %d", ret); + rxbuf = NULL; + goto free_buf; + } + + cts_lock_device(cts_dev); + ret = cts_tcs_tool_xtrans(cts_dev, txbuf, rwdata.txlen, rxbuf, rwdata.rxlen); + cts_unlock_device(cts_dev); + if (ret < 0) { + cts_err("Trans failed %d", ret); + goto free_buf; + } + + ret = copy_to_user(rwdata.rxbuf, rxbuf, rwdata.rxlen); + if (ret < 0) { + cts_err("Copy to user failed %d", ret); + goto free_buf; + } + + ret = 0; + +free_buf: + if (txbuf) { + kfree(txbuf); + txbuf = NULL; + } + if (rxbuf) { + kfree(rxbuf); + rxbuf = NULL; + } + + return ret; +} + +static int cts_ioctl_test(struct cts_device *cts_dev, + u32 ntests, struct cts_test_param *tests) +{ + u32 num_nodes = 0; + int i, ret = 0; + + cts_info("ioctl test total %u items", ntests); + + num_nodes = cts_dev->fwdata.rows * cts_dev->fwdata.cols; + + for (i = 0; i < ntests; i++) { + bool validate_data = false; + bool validate_data_per_node = false; + bool validate_min = false; + bool validate_max = false; + bool skip_invalid_node = false; + bool stop_test_if_validate_fail = false; + bool dump_test_data_to_console = false; + bool dump_test_data_to_user = false; + bool dump_test_data_to_file = false; + bool dump_test_data_to_file_append = false; + bool driver_log_to_user = false; + bool driver_log_to_file = false; + bool driver_log_to_file_append = false; + u32 __user *user_min_threshold = NULL; + u32 __user *user_max_threshold = NULL; + u32 __user *user_invalid_nodes = NULL; + int __user *user_test_result = NULL; + void __user *user_test_data = NULL; + int __user *user_test_data_wr_size = NULL; + const char __user *user_test_data_filepath = NULL; + void __user *user_driver_log_buf = NULL; + int __user *user_driver_log_wr_size = NULL; + const char __user *user_driver_log_filepath = NULL; + void __user *user_priv_param = NULL; + int test_result = 0; + int test_data_wr_size = 0; + int driver_log_wr_size = 0; + + cts_info("ioctl test item %d: %d(%s) flags: %08x priv param size: %d", + i, tests[i].test_item, cts_test_item_str(tests[i].test_item), + tests[i].flags, tests[i].priv_param_size); + /* + * Validate arguement + */ + validate_data = + !!(tests[i].flags & CTS_TEST_FLAG_VALIDATE_DATA); + validate_data_per_node = + !!(tests[i].flags & CTS_TEST_FLAG_VALIDATE_PER_NODE); + validate_min = + !!(tests[i].flags & CTS_TEST_FLAG_VALIDATE_MIN); + validate_max = + !!(tests[i].flags & CTS_TEST_FLAG_VALIDATE_MAX); + skip_invalid_node = + !!(tests[i].flags & CTS_TEST_FLAG_VALIDATE_SKIP_INVALID_NODE); + stop_test_if_validate_fail = + !!(tests[i].flags & CTS_TEST_FLAG_STOP_TEST_IF_VALIDATE_FAILED); + dump_test_data_to_user = + !!(tests[i].flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_USERSPACE); + dump_test_data_to_file = + !!(tests[i].flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE); + dump_test_data_to_file_append = + !!(tests[i].flags & CTS_TEST_FLAG_DUMP_TEST_DATA_TO_FILE_APPEND); + driver_log_to_user = + !!(tests[i].flags & CTS_TEST_FLAG_DRIVER_LOG_TO_USERSPACE); + driver_log_to_file = + !!(tests[i].flags & CTS_TEST_FLAG_DRIVER_LOG_TO_FILE); + driver_log_to_file_append = + !!(tests[i].flags & CTS_TEST_FLAG_DRIVER_LOG_TO_FILE_APPEND); + + if (tests[i].test_result == NULL) { + cts_err("Result pointer = NULL"); + return -EFAULT; + } + + if (validate_data) { + cts_info(" - Flag: Validate data"); + + if (validate_data_per_node) { + cts_info(" - Flag: Validate data per-node"); + } + if (validate_min) { + cts_info(" - Flag: Validate min threshold"); + } + if (validate_max) { + cts_info(" - Flag: Validate max threshold"); + } + if (stop_test_if_validate_fail) { + cts_info(" - Flag: Stop test if validate fail"); + } + + if (validate_min && tests[i].min == NULL) { + cts_err("Min threshold pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + if (validate_max && tests[i].max == NULL) { + cts_err("Max threshold pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + if (skip_invalid_node) { + cts_info(" - Flag: Skip invalid node"); + + if (tests[i].num_invalid_node == 0 || + tests[i].num_invalid_node >= num_nodes) { + cts_err("Num invalid node %u out of range[0, %u]", + tests[i].num_invalid_node, num_nodes); + ret = -EINVAL; + goto store_result; + } + + if (tests[i].invalid_nodes == NULL) { + cts_err("Invalid nodes pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + } + } + + if (dump_test_data_to_console) { + cts_info(" - Flag: Dump test data to console"); + } + + if (dump_test_data_to_user) { + cts_info(" - Flag: Dump test data to user, size: %d", + tests[i].test_data_buf_size); + + if (tests[i].test_data_buf == NULL) { + cts_err("Test data pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + if (tests[i].test_data_wr_size == NULL) { + cts_err("Test data write size pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + if (tests[i].test_data_buf_size < num_nodes) { + cts_err("Test data size %d too small < %u", + tests[i].test_data_buf_size, num_nodes); + ret = -EINVAL; + goto store_result; + } + } + + if (dump_test_data_to_file) { + cts_info(" - Flag: Dump test data to file%s", + dump_test_data_to_file_append ? "[Append]" : ""); + + if (tests[i].test_data_filepath == NULL) { + cts_err("Test data filepath = NULL"); + ret = -EINVAL; + goto store_result; + } + } + + if (driver_log_to_user) { + cts_info(" - Flag: Dump driver log to user, size: %d", + tests[i].driver_log_buf_size); + + if (tests[i].driver_log_buf == NULL) { + cts_err("Driver log buf pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + if (tests[i].driver_log_wr_size == NULL) { + cts_err("Driver log write size pointer = NULL"); + ret = -EINVAL; + goto store_result; + } + if (tests[i].driver_log_buf_size < 1024) { + cts_err("Driver log buf size %d too small < 1024", + tests[i].test_data_buf_size); + ret = -EINVAL; + goto store_result; + } + } + + if (driver_log_to_file) { + cts_info(" - Flag: Dump driver log to file %s", + driver_log_to_file_append ? "[Append]" : ""); + + if (tests[i].driver_log_filepath == NULL) { + cts_err("Driver log filepath = NULL"); + ret = -EINVAL; + goto store_result; + } + } + + /* + * Dump input parameter from user, + * Aallocate memory for output, + * Replace __user pointer with kernel pointer. + */ + user_test_result = (int __user *)tests[i].test_result; + tests[i].test_result = &test_result; + + if (validate_data) { + int num_threshold = validate_data_per_node ? num_nodes : 1; + int threshold_size = num_threshold * sizeof(tests[i].min[0]); + + if (validate_min) { + user_min_threshold = (int __user *)tests[i].min; + tests[i].min = memdup_user(user_min_threshold, threshold_size); + if (IS_ERR(tests[i].min)) { + ret = PTR_ERR(tests[i].min); + tests[i].min = NULL; + cts_err("Memdup min threshold from user failed %d", ret); + goto store_result; + } + } else { + tests[i].min = NULL; + } + if (validate_max) { + user_max_threshold = (int __user *)tests[i].max; + tests[i].max = memdup_user(user_max_threshold, threshold_size); + if (IS_ERR(tests[i].max)) { + ret = PTR_ERR(tests[i].max); + tests[i].max = NULL; + cts_err("Memdup max threshold from user failed %d", ret); + goto store_result; + } + } else { + tests[i].max = NULL; + } + if (skip_invalid_node) { + user_invalid_nodes = (u32 __user *)tests[i].invalid_nodes; + tests[i].invalid_nodes = memdup_user(user_invalid_nodes, + tests[i].num_invalid_node * sizeof(tests[i].invalid_nodes[0])); + if (IS_ERR(tests[i].invalid_nodes)) { + ret = PTR_ERR(tests[i].invalid_nodes); + tests[i].invalid_nodes = NULL; + cts_err("Memdup invalid node from user failed %d", ret); + goto store_result; + } + } + } + + if (dump_test_data_to_user) { + user_test_data = (void __user *)tests[i].test_data_buf; + tests[i].test_data_buf = vmalloc(tests[i].test_data_buf_size); + if (tests[i].test_data_buf == NULL) { + ret = -ENOMEM; + cts_err("Alloc test data mem failed"); + goto store_result; + } + user_test_data_wr_size = (int __user *)tests[i].test_data_wr_size; + tests[i].test_data_wr_size = &test_data_wr_size; + } + + if (dump_test_data_to_file) { +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): strndup_user is forbiddon with GKI Version!", __func__); +#else + user_test_data_filepath = (const char __user *)tests[i].test_data_filepath; + tests[i].test_data_filepath = strndup_user(user_test_data_filepath, PATH_MAX); + if (tests[i].test_data_filepath == NULL) { + cts_err("Strdup test data filepath failed"); + goto store_result; + } +#endif + } + + if (driver_log_to_user) { + user_driver_log_buf = (void __user *)tests[i].driver_log_buf; + tests[i].driver_log_buf = kmalloc(tests[i].driver_log_buf_size, GFP_KERNEL); + if (tests[i].driver_log_buf == NULL) { + ret = -ENOMEM; + cts_err("Alloc driver log mem failed"); + goto store_result; + } + user_driver_log_wr_size = (int __user *)tests[i].driver_log_wr_size; + tests[i].driver_log_wr_size = &driver_log_wr_size; + } + + if (driver_log_to_file) { +#ifdef CFG_CTS_FOR_GKI + cts_info("%s(): strndup_user is forbiddon with GKI Version!", __func__); +#else + user_driver_log_filepath = (const char __user *)tests[i].driver_log_filepath; + tests[i].driver_log_filepath = strndup_user(user_driver_log_filepath, PATH_MAX); + if (tests[i].driver_log_filepath == NULL) { + cts_err("Strdup driver log filepath failed"); + goto store_result; + } +#endif + cts_info("Log driver log to file '%s'", tests[i].driver_log_filepath); + } + + if (driver_log_to_file || driver_log_to_user) { + ret = cts_start_driver_log_redirect( + tests[i].driver_log_filepath, driver_log_to_file_append, + tests[i].driver_log_buf, tests[i].driver_log_buf_size, + tests[i].driver_log_level); + if (ret) { + cts_err("Start driver log redirect failed %d", ret); + goto store_result; + } + } + + if (tests[i].priv_param_size && tests[i].priv_param) { + user_priv_param = (void __user *)tests[i].priv_param; + tests[i].priv_param = memdup_user(user_priv_param, tests[i].priv_param_size); + if (IS_ERR(tests[i].priv_param)) { + ret = PTR_ERR(tests[i].priv_param); + tests[i].priv_param = NULL; + cts_err("Memdup priv param from user failed %d", ret); + goto store_result; + } + } + + /* + * Do test + */ + switch (tests[i].test_item) { + case CTS_TEST_RESET_PIN: + ret = cts_test_reset_pin(cts_dev, &tests[i]); + break; + case CTS_TEST_INT_PIN: + ret = cts_test_int_pin(cts_dev, &tests[i]); + break; + case CTS_TEST_RAWDATA: + ret = cts_test_rawdata(cts_dev, &tests[i]); + break; + case CTS_TEST_NOISE: + ret = cts_test_noise(cts_dev, &tests[i]); + break; + case CTS_TEST_OPEN: + ret = cts_test_open(cts_dev, &tests[i]); + break; + case CTS_TEST_SHORT: + ret = cts_test_short(cts_dev, &tests[i]); + break; + case CTS_TEST_COMPENSATE_CAP: + ret = cts_test_compensate_cap(cts_dev, &tests[i]); + break; + default: + ret = ENOTSUPP; + cts_err("Un-supported test item"); + break; + } + +/* + * Copy result and test data back to userspace. + */ +store_result: + if (dump_test_data_to_user) { + if (user_test_data != NULL && test_data_wr_size > 0) { + cts_info("Copy test data to user, size: %d", test_data_wr_size); + if (copy_to_user(user_test_data, tests[i].test_data_buf, + test_data_wr_size)) { + cts_err("Copy test data to user failed"); + test_data_wr_size = 0; + // Skip this error + } + } + + if (user_test_data_wr_size != NULL) { + put_user(test_data_wr_size, user_test_data_wr_size); + } + } + + if (driver_log_to_user) { + driver_log_wr_size = cts_get_driver_log_redirect_size(); + if (user_driver_log_buf != NULL && driver_log_wr_size > 0) { + cts_info("Copy driver log to user, size: %d", driver_log_wr_size); + if (copy_to_user(user_driver_log_buf, tests[i].driver_log_buf, + driver_log_wr_size)) { + cts_err("Copy driver log to user failed"); + driver_log_wr_size = 0; + // Skip this error + } + } + + if (user_driver_log_wr_size != NULL) { + put_user(driver_log_wr_size, user_driver_log_wr_size); + } + } + + if (user_test_result != NULL) { + put_user(ret, user_test_result); + } else if (tests[i].test_result != NULL) { + put_user(ret, tests[i].test_result); + } + + /* + * Free memory + */ + if (dump_test_data_to_user) { + if (user_test_data != NULL && tests[i].test_data_buf != NULL) { + vfree(tests[i].test_data_buf); + } + } + if (validate_data) { + if (validate_min && user_min_threshold != NULL && tests[i].min != NULL) { + kfree(tests[i].min); + } + if (validate_max && user_max_threshold != NULL && tests[i].max != NULL) { + kfree(tests[i].max); + } + if (skip_invalid_node) { + if (user_invalid_nodes != NULL && tests[i].invalid_nodes != NULL) { + kfree(tests[i].invalid_nodes); + } + } + } + + if (dump_test_data_to_file && + user_test_data_filepath != NULL && + tests[i].test_data_filepath != NULL) { + kfree(tests[i].test_data_filepath); + } + + if (driver_log_to_user) { + if (user_driver_log_buf != NULL && + tests[i].driver_log_buf != NULL) { + kfree(tests[i].driver_log_buf); + } + } + + if (driver_log_to_file) { + if (user_driver_log_filepath != NULL && + tests[i].driver_log_filepath != NULL) { + kfree(tests[i].driver_log_filepath); + } + } + + if (driver_log_to_file || driver_log_to_user) { + cts_stop_driver_log_redirect(); + } + + if (user_priv_param && tests[i].priv_param) { + kfree(tests[i].priv_param); + } + + if (ret && stop_test_if_validate_fail) { + break; + } + } + + kfree(tests); + + return ret; +} + + +static long cts_tool_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct chipone_ts_data *cts_data; + struct cts_device *cts_dev; + unsigned int modid; + int ret; + + cts_info("ioctl, cmd=0x%016x, arg=0x%016lx", cmd, arg); + + cts_data = file->private_data; + if (cts_data == NULL) { + cts_err("IOCTL with private data = NULL"); + return -EFAULT; + } + + cts_dev = &cts_data->cts_dev; + + switch (cmd) { + case CTS_TOOL_IOCTL_GET_DRIVER_VERSION: + return put_user(CTS_DRIVER_VERSION_CODE, + (unsigned int __user *)arg); + case CTS_TOOL_IOCTL_GET_DEVICE_TYPE: + return put_user(cts_dev->hwdata->hwid, + (unsigned int __user *)arg); + case CTS_TOOL_IOCTL_GET_FW_VERSION: + return put_user(cts_dev->fwdata.version, + (unsigned short __user *)arg); + case CTS_TOOL_IOCTL_GET_ROW_COL:{ + u16 rows_cols = (cts_dev->fwdata.rows << 8) | cts_dev->fwdata.cols; + return put_user(rows_cols, + (unsigned short __user *)arg); + } + case CTS_TOOL_IOCTL_GET_MODULE_ID: + ret = cts_tcs_get_module_id(cts_dev, &modid); + if (ret) { + cts_err("Get module Id failed"); + return -EFAULT; + } + return put_user(modid, + (unsigned int __user *)arg); + + case CTS_TOOL_IOCTL_TEST:{ + struct cts_test_ioctl_data test_arg; + struct cts_test_param *tests_pa; + + if (copy_from_user(&test_arg, (struct cts_test_ioctl_data __user *)arg, + sizeof(test_arg))) { + cts_err("Copy ioctl test arg to kernel failed"); + return -EFAULT; + } + + if (test_arg.ntests > 8) { + cts_err("ioctl test with too many tests %u", test_arg.ntests); + return -EINVAL; + } + + tests_pa = memdup_user(test_arg.tests, + test_arg.ntests * sizeof(struct cts_test_param)); + if (IS_ERR(tests_pa)) { + int ret = PTR_ERR(tests_pa); + cts_err("Memdump test param to kernel failed %d", ret); + return ret; + } + + return cts_ioctl_test(cts_dev, test_arg.ntests, tests_pa); + } + case CTS_TOOL_IOCTL_TCS_RW:{ + return cts_ioctl_tcs_rw(cts_dev, file, cmd, arg); + } + + default: + break; + } + + return -ENOTSUPP; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 6, 0) +static struct file_operations cts_tool_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = cts_tool_open, + .read = cts_tool_read, + .write = cts_tool_write, + .unlocked_ioctl = cts_tool_ioctl, +}; +#else +static struct proc_ops cts_tool_fops = { + .proc_lseek = no_llseek, + .proc_open = cts_tool_open, + .proc_read = cts_tool_read, + .proc_write = cts_tool_write, + .proc_ioctl = cts_tool_ioctl, +}; +#endif + +int cts_tool_init(struct chipone_ts_data *cts_data) +{ + cts_info("Init"); + + cts_data->procfs_entry = proc_create_data(CFG_CTS_TOOL_PROC_FILENAME, + 0666, NULL, &cts_tool_fops, cts_data); + if (cts_data->procfs_entry == NULL) { + cts_err("Create proc entry failed"); + return -EFAULT; + } + + return 0; +} + +void cts_tool_deinit(struct chipone_ts_data *data) +{ + cts_info("Deinit"); + + if (data->procfs_entry) + remove_proc_entry(CFG_CTS_TOOL_PROC_FILENAME, NULL); +} +#endif /* CONFIG_CTS_LEGACY_TOOL */ diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/Android.mk b/drivers/input/touchscreen/focaltech_0flash_mmi/Android.mk new file mode 100644 index 000000000000..cfd680fd15c6 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/Android.mk @@ -0,0 +1,34 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifneq ($(FOCALTECH_TOUCH_IC_NAME),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME=$(FOCALTECH_TOUCH_IC_NAME) +else + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME=ft8719 +endif + +ifneq ($(BOARD_USES_DOUBLE_TAP),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_0FLASH_MMI_ENABLE_DOUBLE_TAP=y +endif + +ifneq ($(BOARD_USES_TOUCH_PALM),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_0FLASH_MMI_ENABLE_PALM=y +endif + +ifneq ($(BOARD_USES_PEN_NOTIFIER),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_0FLASH_MMI_PEN_NOTIFIER=y +endif + +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_MMI=y +endif + +ifneq ($(BOARD_USES_PANEL_NOTIFICATIONS),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_PANEL_NOTIFICATIONS=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := focaltech_0flash_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/Kbuild b/drivers/input/touchscreen/focaltech_0flash_mmi/Kbuild new file mode 100644 index 000000000000..47f4ee17aed6 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/Kbuild @@ -0,0 +1,64 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/focaltech_0flash_mmi \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/focaltech_0flash_mmi/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test + +obj-m := focaltech_0flash_mmi.o +focaltech_0flash_mmi-objs += focaltech_core.o +focaltech_0flash_mmi-objs += focaltech_esdcheck.o +focaltech_0flash_mmi-objs += focaltech_ex_fun.o +focaltech_0flash_mmi-objs += focaltech_ex_mode.o +focaltech_0flash_mmi-objs += focaltech_flash.o +focaltech_0flash_mmi-objs += focaltech_gesture.o +focaltech_0flash_mmi-objs += focaltech_point_report_check.o +focaltech_0flash_mmi-objs += focaltech_test/focaltech_test.o +focaltech_0flash_mmi-objs += focaltech_test/focaltech_test_ini.o + +ifneq ($(filter ft8719,$(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8719 + focaltech_0flash_mmi-objs += focaltech_test/supported_ic/focaltech_test_ft8719.o + focaltech_0flash_mmi-objs += focaltech_flash_ft8719.o + focaltech_0flash_mmi-objs += focaltech_spi_v1_1.o +endif +ifneq ($(filter ft8756,$(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8756 + focaltech_0flash_mmi-objs += focaltech_test/supported_ic/focaltech_test_ft8756.o + focaltech_0flash_mmi-objs += focaltech_flash_ft8756.o + focaltech_0flash_mmi-objs += focaltech_spi_v1_1_1.o +endif +ifneq ($(filter ft8006s_aa,$(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8006S_AA + EXTRA_CFLAGS += -DFOCALTECH_ESD_EN +ifneq (,$(filter %factory,$(TARGET_PRODUCT))) + EXTRA_CFLAGS += -DFOCALTECH_SUSPEND_IRQ_ENABLE +endif + focaltech_0flash_mmi-objs += focaltech_test/supported_ic/focaltech_test_ft8006sp.o + focaltech_0flash_mmi-objs += focaltech_flash_ft8006s.o + focaltech_0flash_mmi-objs += focaltech_spi_v1_1_1.o +endif +ifneq ($(filter ft8009,$(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8009 + focaltech_0flash_mmi-objs += focaltech_test/supported_ic/focaltech_test_ft8756.o + focaltech_0flash_mmi-objs += focaltech_flash_ft8756.o + focaltech_0flash_mmi-objs += focaltech_spi_v1_1_1.o +endif +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI + focaltech_0flash_mmi-objs += focaltech_ts_mmi.o +endif +ifneq ($(filter m y,$(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_ENABLE_DOUBLE_TAP)),) + EXTRA_CFLAGS += -DFOCALTECH_SENSOR_EN +endif +ifneq ($(filter m y,$(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_ENABLE_PALM)),) + EXTRA_CFLAGS += -DFOCALTECH_PALM_SENSOR_EN +endif +ifneq ($(filter m y,$(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_PEN_NOTIFIER)),) + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../misc/pen/Module.symvers + EXTRA_CFLAGS += -DFOCALTECH_PEN_NOTIFIER +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_FOCALTECH_PANEL_NOTIFICATIONS)),) +EXTRA_CFLAGS += -DFOCALTECH_CONFIG_PANEL_NOTIFICATIONS +endif diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/Kconfig b/drivers/input/touchscreen/focaltech_0flash_mmi/Kconfig new file mode 100755 index 000000000000..0c327bac9a96 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/Kconfig @@ -0,0 +1,17 @@ +# +# Focaltech Touchscreen driver configuration +# + +config TOUCHSCREEN_FTS + bool "Focaltech Touchscreen" + depends on I2C + default n + help + Say Y here if you have Focaltech touch panel. + If unsure, say N. + +config TOUCHSCREEN_FTS_DIRECTORY + string "Focaltech ts directory name" + default "focaltech_touch" + depends on TOUCHSCREEN_FTS + diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/Makefile b/drivers/input/touchscreen/focaltech_0flash_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/Makefile.kernel b/drivers/input/touchscreen/focaltech_0flash_mmi/Makefile.kernel new file mode 100755 index 000000000000..8901b44be958 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/Makefile.kernel @@ -0,0 +1,15 @@ +# Makefile for the focaltech touchscreen drivers. + + +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_core.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_ex_fun.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_ex_mode.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_gesture.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_esdcheck.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_point_report_check.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_test/ + + +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_spi.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_flash.o + diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_common.h b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_common.h new file mode 100755 index 000000000000..88579282cac5 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_common.h @@ -0,0 +1,185 @@ +/* + * + * 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.1 20190420" + +#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 (FTS_CHIP_TYPE & FLAGBITS(0, FLAG_ICSERIALS_LEN-1)) +#define IC_TO_SERIALS(x) ((x) & FLAGBITS(0, FLAG_ICSERIALS_LEN-1)) +#define FTS_CHIP_IDC ((FTS_CHIP_TYPE & FLAGBIT(FLAG_IDC_BIT)) == FLAGBIT(FLAG_IDC_BIT)) +#define FTS_HID_SUPPORTTED ((FTS_CHIP_TYPE & FLAGBIT(FLAG_HID_BIT)) == FLAGBIT(FLAG_HID_BIT)) + +#if defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8756) +#define FTS_CHIP_TYPE_MAPPING {{0x15, 0x87, 0x56, 0x87, 0x56, 0xF7, 0xA6, 0x00, 0x00}} +#elif defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8006S_AA) +#define FTS_CHIP_TYPE_MAPPING {{0x19, 0x86, 0x32, 0x86, 0x32, 0x86, 0xC2, 0x00, 0x00}} +#elif defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8719) +#define FTS_CHIP_TYPE_MAPPING {{0x0D,0x87, 0x19, 0x87, 0x19, 0x87, 0xA9, 0x87, 0xB9}} +#elif defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8009) +#define FTS_CHIP_TYPE_MAPPING {{0x17, 0x80, 0x09, 0x80, 0x09, 0x80, 0xA9, 0x00, 0x00}} +#else +#define FTS_CHIP_TYPE_MAPPING {{0x0D,0x87, 0x19, 0x87, 0x19, 0x87, 0xA9, 0x87, 0xB9}} +#endif + +#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 10 +#define FTS_CMD_READ_ID 0x90 +#define FTS_CMD_READ_ID_LEN 4 +#define FTS_CMD_READ_ID_LEN_INCELL 1 +/*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_VALUE 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_PEN_DETECTION 0xC2 +#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 1 + +/***************************************************************************** +* 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...) do { \ + pr_debug("[FTS_TS]%s:"fmt"\n", __func__, ##args); \ +} while (0) + +#define FTS_FUNC_ENTER() do { \ + pr_debug("[FTS_TS]%s: Enter\n", __func__); \ +} while (0) + +#define FTS_FUNC_EXIT() do { \ + pr_debug("[FTS_TS]%s: Exit(%d)\n", __func__, __LINE__); \ +} while (0) +#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__ */ diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_config.h b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_config.h new file mode 100755 index 000000000000..6b83ebec0d54 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_config.h @@ -0,0 +1,305 @@ +/* + * + * 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 _FT7250 0x72500807 +#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 _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 _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 _FT3267 0x32670004 +#define _FT3367 0x33670004 + +#define _FT3327DQQ_XXX 0x3327D482 +#define _FT5446DQS_XXX 0x5446D482 + +#define _FT3518 0x35180481 +#define _FT3558 0x35580481 +#define _FT3528 0x35280481 +#define _FT5536 0x55360481 + +#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 _FT6346U 0x6346D085 +#define _FT6346G 0x6346A085 +#define _FT3067 0x30670085 +#define _FT3068 0x30680085 +#define _FT3168 0x31680085 +#define _FT3268 0x32680085 + +/*************************************************/ + +/* + * choose your ic chip type of focaltech + */ +#if defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8756) +#define FTS_CHIP_TYPE _FT8756 +#define FTS_CHIP_NAME "ft8756" +#elif defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8006S_AA) +#define FTS_CHIP_TYPE _FT8006S_AA +#define FTS_CHIP_NAME "ft8006s_aa" +#elif defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8719) +#define FTS_CHIP_TYPE _FT8719 +#define FTS_CHIP_NAME "ft8719" +#elif defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8009) +#define FTS_CHIP_TYPE _FT8009 +#define FTS_CHIP_NAME "ft8009" +#else +#define FTS_CHIP_TYPE _FT8719 +#define FTS_CHIP_NAME "ft8719" +#endif + +/******************* 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 + */ +#ifdef FOCALTECH_SENSOR_EN +#define FTS_GESTURE_EN 1 +#else +#define FTS_GESTURE_EN 0 +#endif + +/* + * ESD check & protection + * default: disable + */ +#ifdef FOCALTECH_ESD_EN +#define FTS_ESDCHECK_EN 1 +#if defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8006S_AA) +#define FTS_DEBUG_ESD_EN 1 +#else +#define FTS_DEBUG_ESD_EN 0 +#endif +#else +#define FTS_ESDCHECK_EN 0 +#endif + +/* + * irq enable when fts suspend + * default: disable + */ +#ifdef FOCALTECH_SUSPEND_IRQ_ENABLE +#define FTS_SUSPEND_IRQ_EN 1 +#else +#define FTS_SUSPEND_IRQ_EN 0 +#endif + +/* + * FTS CONFIG_DRM_PANEL check & protection + * default: disable + */ +#if defined(CONFIG_DRM_PANEL) && defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8006S_AA) +#define FTS_CONFIG_DRM_PANEL 1 +#else +#define FTS_CONFIG_DRM_PANEL 0 +#endif + +/* + * Production test enable + * 1: enable, 0:disable(default) + */ +#define FTS_TEST_EN 0 + +/* + * Nodes for tools, please keep enable + */ +#define FTS_SYSFS_NODE_EN 1 +#define FTS_APK_NODE_EN 1 + +/* + * Pinctrl enable + * default: disable + */ +#define FTS_PINCTRL_EN 0 + +/* + * Customer power enable + * enable it when customer need control TP power + * default: disable + */ +#define FTS_POWER_SOURCE_CUST_EN 0 + +/****************************************************/ + +/********************** Upgrade ****************************/ +/* + * auto upgrade + */ +#define FTS_AUTO_UPGRADE_EN 0 + +/* + * auto upgrade for lcd cfg + */ +#define FTS_AUTO_LIC_UPGRADE_EN 0 + +/* + * Numbers of modules support + */ +#define FTS_GET_MODULE_NUM 0 + +/* + * Usb detect support + */ +#define FTS_USB_DETECT_EN 1 + +/* + * 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 0x0000 +#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 "" +#define FTS_MODULE2_NAME "" +#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 + * NOTE: git may ignore xxx.i file + */ +#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.h" + +/* + * 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.h" + +/*********************************************************/ + +#endif /* _LINUX_FOCLATECH_CONFIG_H_ */ diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_core.c b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_core.c new file mode 100755 index 000000000000..e47a17ea0082 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_core.c @@ -0,0 +1,2728 @@ +/* + * + * 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 +#include +#include +#include +#include +#include + +#ifdef FOCALTECH_PEN_NOTIFIER +#include +#endif +#include "focaltech_config.h" +#ifdef CONFIG_DRM +#if FTS_CONFIG_DRM_PANEL + #include +#else + #include +#endif +#elif defined(CONFIG_FB) + #include + #include +#elif defined(CONFIG_HAS_EARLYSUSPEND) + #include +#define FTS_SUSPEND_LEVEL 1 /* Early-suspend level */ +#endif +#include "focaltech_core.h" + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +extern int fts_mmi_dev_register(struct fts_ts_data *ts_data); +extern void fts_mmi_dev_unregister(struct fts_ts_data *ts_data); +#endif + +#ifdef FOCALTECH_CONFIG_PANEL_NOTIFICATIONS +#define register_panel_notifier panel_register_notifier +#define unregister_panel_notifier panel_unregister_notifier +enum touch_state { + TOUCH_DEEP_SLEEP_STATE = 0, + TOUCH_LOW_POWER_STATE, +}; +#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 2800000 +#define FTS_VTG_MAX_UV 3300000 +#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; + +#ifdef FOCALTECH_PALM_SENSOR_EN +static struct sensors_classdev __maybe_unused palm_sensors_touch_cdev = { + .name = "palm-gesture", + .vendor = "Focaltech", + .version = 1, + .type = SENSOR_TYPE_MOTO_TOUCH_PALM, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#endif + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +#ifdef FOCALTECH_PEN_NOTIFIER +static int fts_mcu_pen_detect_set(uint8_t pen_detect); +#endif +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +static int fts_ts_suspend(struct device *dev); +static int fts_ts_resume(struct device *dev); +#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 reg_value = 0; + u8 chip_id = fts_data->ic_info.ids.chip_idh; + + do { + ret = fts_read_reg(FTS_REG_CHIP_ID, ®_value); + if ((ret < 0) || (reg_value != chip_id)) { + FTS_INFO("TP Not Ready, ReadData = 0x%x", reg_value); + } else if (reg_value == chip_id) { + FTS_INFO("TP Ready, Device ID = 0x%x", reg_value); + 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 */ +#ifdef FOCALTECH_PALM_SENSOR_EN + if (ts_data->palm_detection_enabled) { + fts_write_reg(0xB0, 0x01); + FTS_INFO("Resume palm detect mode."); + } +#endif +#if FTS_GESTURE_EN + fts_gesture_recovery(ts_data); +#endif +#ifdef FOCALTECH_PEN_NOTIFIER + fts_mcu_pen_detect_set(ts_data->fts_pen_detect_flag); +#endif + FTS_FUNC_EXIT(); +} + +int fts_reset_proc(int hdelayms) +{ + FTS_DEBUG("tp reset"); + gpio_direction_output(fts_data->pdata->reset_gpio, 0); + msleep(5); + 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) { + disable_irq_nosync(fts_data->irq); + 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) { + enable_irq(fts_data->irq); + 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}; + + ret = fts_write(buf, 3); + if (ret < 0) { + FTS_ERROR("hid2std cmd write fail"); + } else { + msleep(10); + 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 ((0xEB == buf[0]) && (0xAA == buf[1]) && (0x08 == buf[2])) { + 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; + struct ft_chip_t ctype[] = FTS_CHIP_TYPE_MAPPING; + 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 }; + + ts_data->ic_info.is_incell = FTS_CHIP_IDC; + ts_data->ic_info.hid_supported = FTS_HID_SUPPORTTED; + + for (cnt = 0; cnt < 3; cnt++) { + fts_reset_proc(0); + mdelay(8); + + ret = fts_read_bootid(ts_data, &chip_id[0]); + if (ret < 0) { + FTS_DEBUG("read boot id fail,retry:%d", cnt); + continue; + } + + ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], INVALID); + if (ret < 0) { + FTS_DEBUG("can't get ic informaton,retry:%d", cnt); + continue; + } + + break; + } + + if (cnt >= 3) { + FTS_ERROR("get ic informaton fail"); + return -EIO; + } + + + 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)) { +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + if ( FTS_TOUCH_DOWN == events[i].flag && data->imports + && data->imports->report_touch_event) { + struct touch_event_data touch_event; + memset(&touch_event, 0, sizeof(touch_event)); + touch_event.type = TS_COORDINATE_ACTION_PRESS; + touch_event.id = events[i].id; + touch_event.x = events[i].x; + touch_event.y = events[i].y; + touch_event.major = events[i].area; + data->imports->report_touch_event(&touch_event, data->input_dev); + if (touch_event.skip_report) { + /* input event is reported by touchscreen class. + * vendor touch event may need update based on touch_event + */ + continue; + } + } +#endif + 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++; +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + if (data->imports && data->imports->report_touch_event) { + struct touch_event_data touch_event; + memset(&touch_event, 0, sizeof(touch_event)); + touch_event.type = TS_COORDINATE_ACTION_RELEASE; + touch_event.id = events[i].id; + data->imports->report_touch_event(&touch_event, data->input_dev); + if (touch_event.skip_report) { + /* input event is reported by touchscreen class. + * vendor touch event may need update based on touch_event + */ + continue; + } + } +#endif + 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); + +#if defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8756) || \ + defined (CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8009) + buf[0] = 0x01; + ret = fts_read(buf, 1, buf + 1, data->pnt_buf_size - 1); + if ((0xEF == buf[1]) && (0xEF == buf[2]) && (0xEF == buf[3])) +#elif defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8006S_AA) + buf[0] = 0x01; + ret = fts_read(buf, 1, buf + 1, data->pnt_buf_size - 1); + if (((0xEF == buf[2]) && (0xEF == buf[3]) && (0xEF == buf[4])) + || ((ret < 0) && (0xEF == buf[1]))) +#elif defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8719) + ret = fts_read(NULL, 0, buf + 1, data->pnt_buf_size - 1); + if ((0xEF == buf[2]) && (0xEF == buf[3]) && (0xEF == buf[4])) +#endif + { + /* check if need recovery fw */ + fts_fw_recovery(); + return 1; + } + + if ((ret < 0) || ((buf[1] & 0xF0) != 0x90)) { + FTS_ERROR("touch data(%x) abnormal,ret:%d", buf[1], ret); + return -EIO; + } + +#if FTS_GESTURE_EN + ret = fts_gesture_readdata(data, buf + FTS_TOUCH_DATA_LEN); + if (0 == ret) { + FTS_INFO("succuss to get gesture data in irq handler"); + return 1; + } +#endif + + + if (data->log_level >= 3) { + fts_show_touch_buffer(buf, data->pnt_buf_size); + } + + return 0; +} + +#ifdef FOCALTECH_PALM_SENSOR_EN +static void fts_palm_report(bool active) { + if (active) { + input_report_abs(fts_data->palm_sensor_pdata->input_sensor_dev, + ABS_DISTANCE, 1); + input_sync(fts_data->palm_sensor_pdata->input_sensor_dev); + FTS_INFO("%s: palm report 1\n", __func__); + } else { + input_report_abs(fts_data->palm_sensor_pdata->input_sensor_dev, + ABS_DISTANCE, 0); + input_sync(fts_data->palm_sensor_pdata->input_sensor_dev); + FTS_INFO("%s: palm report 0\n", __func__); + } +} + +static int fts_palm_detect(u8 reg_data) { + int fd_val = reg_data & 0x03; + + FTS_DEBUG("%s: 0x01=0x%0X!, fd_val=%d\n", __func__, reg_data, fd_val); + if (fd_val == 1) { + FTS_INFO("%s: palm detect!\n", __func__); + return 1; + } + if (fd_val == 2) { + FTS_INFO("%s: palm leave!\n", __func__); + return 2; + } + return 0; +} +#endif + +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; +#ifdef FOCALTECH_PALM_SENSOR_EN + int pd_state = 0; +#endif + + ret = fts_read_touchdata(data); + if (ret) { + return ret; + } + +#ifdef FOCALTECH_PALM_SENSOR_EN + if (data->palm_detection_enabled) { + pd_state = fts_palm_detect(buf[1]); + if (pd_state > 0) { + if (pd_state == 1) { + del_timer(&fts_data->palm_release_fimer); + fts_palm_report(true); + return -1; + } else if (pd_state == 2) { + mod_timer(&fts_data->palm_release_fimer, + jiffies + msecs_to_jiffies(fts_data->palm_release_delay_ms)); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&fts_data->palm_gesture_wakelock, + fts_data->palm_release_delay_ms); +#else + PM_WAKEUP_EVENT(fts_data->palm_gesture_wakelock, + fts_data->palm_release_delay_ms); +#endif + return -1; + } + } + if (buf[1] & 0x08) { + FTS_ERROR("Invalid palm detect value. 0x%0X", buf[1]); + return -1; + } + } +#endif + + 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; +} + +#if FTS_USB_DETECT_EN +static void fts_mcu_usb_detect_set(uint8_t usb_connected) +{ + uint8_t write_data = 0; + uint8_t read_data = 0; + uint8_t retry_cnt = 0; + int ret = 0; + + do{ + if (usb_connected == 0x01) { + write_data= 0x01; + ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, write_data); + if (ret < 0) + FTS_ERROR("set register USB IN fail, ret=%d", ret); + FTS_INFO("%s: USB detect status IN!\n", __func__); + } else { + write_data= 0x00; + ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, write_data); + if (ret < 0) + FTS_ERROR("set register USB OUT fail, ret=%d", ret); + FTS_INFO("%s: USB detect status OUT!\n", __func__); + } + + ret = fts_read_reg(FTS_REG_CHARGER_MODE_EN, &read_data); + if (ret < 0) + FTS_ERROR("read 8b register fail, ret=%d", ret); + retry_cnt++; + }while((write_data != read_data) && retry_cnt < FTS_REG_RETRY_TIMES); +} + +void fts_cable_detect_func(bool force_renew) +{ + struct fts_ts_data *ts_data = fts_data; + uint8_t connect_status = 0; + connect_status = ts_data->usb_detect_flag; + + if ((connect_status != ts_data->usb_connected) || force_renew) { + if (connect_status) { + ts_data->usb_connected = 0x01; + } else { + ts_data->usb_connected = 0x00; + } + + fts_mcu_usb_detect_set(ts_data->usb_connected); + FTS_INFO("%s: Cable status change: 0x%2.2X\n", __func__, ts_data->usb_connected); + } +} +#endif + +#ifdef FOCALTECH_PEN_NOTIFIER +static int fts_mcu_pen_detect_set(uint8_t pen_detect) +{ + uint8_t write_data = 0; + uint8_t read_data = 0; + uint8_t retry_cnt = 0; + int ret = 0; + + do{ + if (pen_detect == PEN_DETECTION_INSERT) { + write_data= 0x01; + ret = fts_write_reg(FTS_REG_PEN_DETECTION, write_data); + if (ret < 0) { + FTS_ERROR("set register PEN IN fail, ret=%d", ret); + return ret; + } + FTS_INFO("%s: PEN detect status IN!\n", __func__); + } else if (pen_detect == PEN_DETECTION_PULL) { + write_data= 0x00; + ret = fts_write_reg(FTS_REG_PEN_DETECTION, write_data); + if (ret < 0) { + FTS_ERROR("set register PEN OUT fail, ret=%d", ret); + return ret; + } + FTS_INFO("%s: PEN detect status OUT!\n", __func__); + } + + ret = fts_read_reg(FTS_REG_PEN_DETECTION, &read_data); + if (ret < 0) + FTS_ERROR("read 8b register fail, ret=%d", ret); + retry_cnt++; + }while((write_data != read_data) && retry_cnt < FTS_REG_RETRY_TIMES); + + if (retry_cnt >= FTS_REG_RETRY_TIMES) { + FTS_ERROR("write pen status fail"); + return -EIO; + } + + return 0; +} +#endif + +static void fts_irq_read_report(void) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + +#if FTS_USB_DETECT_EN + fts_cable_detect_func(false); +#endif + +#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) && !ts_data->suspended) { + 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) +{ + fts_irq_read_report(); + 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; + + 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); + + 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; + 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; +} + + +#ifdef FOCALTECH_PALM_SENSOR_EN +static void fts_palm_sensor_release_timer_handler(unsigned long data) +{ + fts_palm_report(false); +} + +static int _fts_palm_sensor_set_enable(unsigned int enable) +{ + FTS_INFO("Palm gesture set enable %d!", enable); +/* + * If palm detect function is enabled, interrupt will not disable, IC works in + * normal mode. But in case touch event is reported to input subsystem, skip + * touch event when suspend flag is true. So input subsystem will not take + * wakelock because no one report event. + * In this case, we still need read data from IC, so AP can not enter suspend. + */ + if (enable == 1) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock(&fts_data->palm_gesture_read_wakelock); +#else + PM_STAY_AWAKE(fts_data->palm_gesture_read_wakelock); +#endif + fts_data->palm_detection_enabled = true; + fts_write_reg(0xB0, 0x01); + } else if (enable == 0) { + fts_data->palm_detection_enabled = false; + if (timer_pending(&fts_data->palm_release_fimer)) { + fts_palm_report(false); + del_timer(&fts_data->palm_release_fimer); + } + fts_write_reg(0xB0, 0x00); +#ifdef CONFIG_HAS_WAKELOCK + wake_unlock(&fts_data->palm_gesture_read_wakelock); +#else + PM_RELAX(fts_data->palm_gesture_read_wakelock); +#endif + } else { + FTS_INFO("unknown enable symbol\n"); + } + return 0; +} + +static int fts_palm_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + int ret = 0; + + mutex_lock(&fts_data->suspend_resume_mutex); + if (!fts_data->suspended) + ret = _fts_palm_sensor_set_enable(enable); + else { + FTS_INFO("Gesture lazy set enable %d!", enable); + if (enable == 1) + fts_data->palm_detection_lazy_set = PALM_SENSOR_LAZY_SET_ENABLE; + else if (enable == 0) + fts_data->palm_detection_lazy_set = PALM_SENSOR_LAZY_SET_DISABLE; + else + FTS_INFO("unknown enable symbol\n"); + } + mutex_unlock(&fts_data->suspend_resume_mutex); + fts_palm_report(false); + + return ret; +} + +static int fts_palm_sensor_init(struct fts_ts_data *data) +{ + struct focaltech_sensor_platform_data *sensor_pdata; + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + FTS_ERROR("Failed to allocate device"); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct focaltech_sensor_platform_data), + GFP_KERNEL); + if (!sensor_pdata) { + FTS_ERROR("Failed to allocate memory"); + goto free_sensor_pdata; + } + data->palm_sensor_pdata = sensor_pdata; + + __set_bit(EV_ABS, sensor_input_dev->evbit); + __set_bit(EV_SYN, sensor_input_dev->evbit); + input_set_abs_params(sensor_input_dev, ABS_DISTANCE, + 0, 5, 0, 0); + sensor_input_dev->name = "palm_detect"; + data->palm_sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + FTS_ERROR("Unable to register device, err=%d", err); + goto free_sensor_input_dev; + } + + sensor_pdata->ps_cdev = palm_sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = fts_palm_sensor_set_enable; + sensor_pdata->data = data; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&data->palm_gesture_wakelock, WAKE_LOCK_SUSPEND, "palm_detect_wl"); +#else + PM_WAKEUP_REGISTER(NULL, data->palm_gesture_wakelock, "palm_detect_wl"); + if (!data->palm_gesture_wakelock) { + FTS_ERROR("Unable to register device, err=%d", err); + goto unregister_sensor_input_device; + } +#endif +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&data->palm_gesture_read_wakelock, WAKE_LOCK_SUSPEND, "palm_read_wl"); +#else + PM_WAKEUP_REGISTER(NULL, data->palm_gesture_read_wakelock, "palm_read_wl"); + if (!data->palm_gesture_wakelock) { + FTS_ERROR("Unable to register device, err=%d", err); + goto unregister_sensor_input_device; + } +#endif + + data->palm_release_fimer.function = fts_palm_sensor_release_timer_handler; + init_timer(&data->palm_release_fimer); + data->palm_release_delay_ms = 850; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(data->palm_sensor_pdata->input_sensor_dev); +free_sensor_input_dev: + input_free_device(data->palm_sensor_pdata->input_sensor_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, sensor_pdata); + data->palm_sensor_pdata = NULL; +exit: + return 1; +} + +int fts_palm_sensor_remove(struct fts_ts_data *data) +{ + sensors_classdev_unregister(&data->palm_sensor_pdata->ps_cdev); + input_unregister_device(data->palm_sensor_pdata->input_sensor_dev); + devm_kfree(&data->palm_sensor_pdata->input_sensor_dev->dev, + data->palm_sensor_pdata); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&data->palm_gesture_wakelock); +#else + PM_WAKEUP_UNREGISTER(data->palm_gesture_wakelock); +#endif +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&data->palm_gesture_read_wakelock); +#else + PM_WAKEUP_UNREGISTER(data->palm_gesture_read_wakelock); +#endif + data->palm_sensor_pdata = NULL; + data->palm_detection_enabled = false; + return 0; +} +#endif + +#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 */ + +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 = 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); + } + } + 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 = 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); + } + } + 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; + } + + if (regulator_count_voltages(ts_data->vdd) > 0) { + 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); + regulator_put(ts_data->vdd); + return ret; + } + } + + ts_data->vcc_i2c = regulator_get(ts_data->dev, "vcc_i2c"); + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + if (regulator_count_voltages(ts_data->vcc_i2c) > 0) { + 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); + regulator_put(ts_data->vcc_i2c); + } + } + } + +#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)) { + if(!data->pdata->share_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; + struct fts_ts_data *ts_data = fts_data; + 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->share_reset_gpio = of_property_read_bool(np, "focaltech,share_reset_gpio"); + if (pdata->share_reset_gpio) + FTS_INFO("TP reset pin is shared with LCD"); + + pdata->always_on_vio = of_property_read_bool(np, "focaltech,always_on_vio"); + if (pdata->always_on_vio) + FTS_INFO("TP VIO always on."); + + pdata->dlfw_in_resume = of_property_read_bool(np, "focaltech,dlfw_in_resume"); + if (pdata->dlfw_in_resume) + FTS_INFO("Reset touch when firmware abnormal in resume."); + + pdata->report_gesture_key = of_property_read_bool(np, "focaltech,report_gesture_key"); + if (pdata->report_gesture_key) + FTS_INFO("Report tap gesture as key."); + + pdata->notify_to_panel = of_property_read_bool(np, "focaltech,notify_to_panel"); + if (pdata->notify_to_panel) + FTS_INFO("Notify touch status to panel."); + + 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_string(np, "focaltech,panel-supplier", + &ts_data->panel_supplier); + if (ret < 0) { + ts_data->panel_supplier = NULL; + FTS_ERROR("Unable to read panel supplier\n"); + } else { + FTS_INFO("panel supplier is %s", (char *)ts_data->panel_supplier); + } + + FTS_FUNC_EXIT(); + return 0; +} + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if defined(CONFIG_FB) || 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); +} +#endif +#endif + +#ifdef FOCALTECH_PEN_NOTIFIER +static int pen_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + int ret = 0; + + struct fts_ts_data *ts_data = + container_of(self, struct fts_ts_data, pen_notif); + FTS_INFO("Received event(%lu) for pen detection\n", event); + + if (event == PEN_DETECTION_INSERT) + ts_data->fts_pen_detect_flag = PEN_DETECTION_INSERT; + else if (event == PEN_DETECTION_PULL) + ts_data->fts_pen_detect_flag = PEN_DETECTION_PULL; + + if ((fts_data->suspended) || (!fts_data->fw_is_running)) { + FTS_INFO("touch in suspend or no firmware, so store."); + } else { + ret = fts_mcu_pen_detect_set(ts_data->fts_pen_detect_flag); + if (ret < 0) { + FTS_ERROR("write pen state fail"); + } + } + + return 0; +} +#endif + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#ifdef CONFIG_DRM +#if FTS_CONFIG_DRM_PANEL +struct drm_panel *active_panel; +static int drm_check_dt(struct device_node *np) +{ + int i = 0; + int count = 0; + struct device_node *node = NULL; + struct drm_panel *panel = NULL; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) { + FTS_ERROR("find drm_panel count(%d) fail", count); + return -ENODEV; + } + + 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)) { + FTS_INFO("find drm_panel successfully"); + active_panel = panel; + return 0; + } + } + + FTS_ERROR("no find drm_panel"); + return -ENODEV; +} + +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_ERROR("not match this driver\n"); + ret = -ENODEV; + goto out; + } + ret = 0; +out: + kfree(active_tp); + return ret; +} + +int drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data) +{ + struct drm_panel_notifier *evdata = data; + int *blank = NULL; + struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data, fb_notif); + + if (!evdata) { + FTS_ERROR("evdata is null"); + return 0; + } + + if (!((event == DRM_PANEL_EARLY_EVENT_BLANK ) + || (event == DRM_PANEL_EVENT_BLANK))) { + FTS_INFO("event(%lu) do not need process\n", event); + return 0; + } + + blank = evdata->data; + FTS_INFO("DRM event:%lu,blank:%d", event, *blank); + switch (*blank) { + case DRM_PANEL_BLANK_UNBLANK: + if (DRM_PANEL_EARLY_EVENT_BLANK == event) { + FTS_INFO("resume: event = %lu, not care\n", event); + } else if (DRM_PANEL_EVENT_BLANK == event) { + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + } + break; + case DRM_PANEL_BLANK_POWERDOWN: + if (DRM_PANEL_EARLY_EVENT_BLANK == event) { + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); + } else if (DRM_PANEL_EVENT_BLANK == event) { + FTS_INFO("suspend: event = %lu, not care\n", event); + } + break; + default: + FTS_INFO("DRM BLANK(%d) do not need process\n", *blank); + break; + } + + return 0; +} +#else +#ifndef FOCALTECH_CONFIG_PANEL_NOTIFICATIONS +int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank; + struct fts_ts_data *ts_data = + container_of(self, struct fts_ts_data, fb_notif); + + if (!evdata || (evdata->id != 0)) { + return 0; + } + + if (!(event == MSM_DRM_EARLY_EVENT_BLANK || event == MSM_DRM_EVENT_BLANK)) { + FTS_INFO("event(%lu) do not need process\n", event); + return 0; + } + blank = evdata->data; + FTS_INFO("DRM event:%lu,blank:%d", event, *blank); + switch (*blank) { + case MSM_DRM_BLANK_UNBLANK: + if (MSM_DRM_EARLY_EVENT_BLANK == event) { + FTS_INFO("resume: event = %lu, not care\n", event); + } else if (MSM_DRM_EVENT_BLANK == event) { + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + } + break; + case MSM_DRM_BLANK_POWERDOWN: + if (MSM_DRM_EARLY_EVENT_BLANK == event) { + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); +#ifdef FOCALTECH_PALM_SENSOR_EN + if (ts_data->palm_detection_enabled) { + FTS_INFO("palm detection is enabled"); + return 1; + } +#endif +#ifdef FOCALTECH_SENSOR_EN + if (fts_data->should_enable_gesture) { + FTS_INFO("double tap gesture suspend\n"); + return 1; + } +#endif + } else if (MSM_DRM_EVENT_BLANK == event) { + FTS_INFO("suspend: event = %lu, not care\n", event); + } + break; + default: + FTS_INFO("DAM BLANK(%d) do not need process\n", *blank); + break; + } + + return 0; +} +#else +int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + struct fts_ts_data *ts_data = + container_of(self, struct fts_ts_data, fb_notif); + + if (!evdata || (evdata->id != 0)) { + return 0; + } + + FTS_INFO("DRM event:%lu", event); + switch (event) { + case PANEL_EVENT_DISPLAY_ON: + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + break; + case PANEL_EVENT_PRE_DISPLAY_OFF: + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); +#ifdef FOCALTECH_SENSOR_EN + if (fts_data->should_enable_gesture) { + FTS_INFO("double tap gesture suspend\n"); + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); + fts_gesture_suspend(ts_data); + } else { +#endif + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); + fts_ts_suspend(ts_data->dev); +#ifdef FOCALTECH_SENSOR_EN + } +#endif + break; + default: + break; + } + + return 0; +} +#endif +#endif +#elif defined(CONFIG_FB) +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 +#endif + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if FTS_USB_DETECT_EN +static int fts_charger_notifier_callback(struct notifier_block *nb, + unsigned long val, void *v) { + int ret = 0; + struct power_supply *psy = NULL; + struct fts_ts_data *ts = container_of(nb, struct fts_ts_data, charger_notif); + union power_supply_propval prop; + + psy= power_supply_get_by_name("usb"); + if (!psy) { + FTS_ERROR("Couldn't get usbpsy\n"); + return -EINVAL; + } + if (!strcmp(psy->desc->name, "usb")) { + if (psy && ts && val == POWER_SUPPLY_PROP_STATUS) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,&prop); + if (ret < 0) { + FTS_ERROR("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret); + return ret; + } else { + ts->usb_detect_flag = prop.intval; + //FTS_ERROR("usb prop.intval =%d\n", prop.intval); + } + } + } + return 0; +} +#endif +#endif + +static int fts_ts_probe_entry(struct fts_ts_data *ts_data) +{ + int ret = 0; + int pdata_size = sizeof(struct fts_ts_platform_data); +#if FTS_CONFIG_DRM_PANEL + struct device_node *dp = ts_data->dev->of_node; +#endif + + 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"); +#if FTS_CONFIG_DRM_PANEL + if (drm_check_dt(dp)) { + FTS_ERROR("parse drm-panel fail"); + if (!fts_ts_check_default_tp(dp, "qcom,spi-touch-active")) + ret = -EPROBE_DEFER; + else + ret = -ENODEV; + return ret; + } +#endif + } 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->suspend_resume_mutex); +#ifdef FOCALTECH_SENSOR_EN + mutex_init(&ts_data->state_mutex); + //unknown screen state + ts_data->screen_state = SCREEN_UNKNOWN; +#endif + + /* 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_gpio_configure(ts_data); + if (ret) { + FTS_ERROR("configure the gpios fail"); + goto err_gpio_config; + } + +#if FTS_POWER_SOURCE_CUST_EN + ret = fts_power_source_init(ts_data); + if (ret) { + FTS_ERROR("fail to get power(regulator)"); + goto err_power_init; + } +#endif + +#if (!FTS_CHIP_IDC) + fts_reset_proc(200); +#endif + + ret = fts_get_ic_information(ts_data); + if (ret) { + FTS_ERROR("not focal IC, unregister driver"); + goto err_irq_req; + } + +#if FTS_APK_NODE_EN + ret = fts_create_apk_debug_channel(ts_data); + if (ret) { + FTS_ERROR("create apk debug node fail"); + } +#endif + +#if FTS_SYSFS_NODE_EN + ret = fts_create_sysfs(ts_data); + if (ret) { + FTS_ERROR("create sysfs node fail"); + } +#endif + +#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"); + } + +#if FTS_GESTURE_EN + ret = fts_gesture_init(ts_data); + if (ret) { + FTS_ERROR("init gesture fail"); + } +#endif + +#ifdef FOCALTECH_PALM_SENSOR_EN + ret = fts_palm_sensor_init(ts_data); + if (ret) { + FTS_ERROR("init palm detect sensor fail"); + } +#endif + +#if FTS_TEST_EN + ret = fts_test_init(ts_data); + if (ret) { + FTS_ERROR("init production test fail"); + } +#endif + +#if FTS_ESDCHECK_EN + ret = fts_esdcheck_init(ts_data); + if (ret) { + FTS_ERROR("init esd check fail"); + } +#endif + + ret = fts_irq_registration(ts_data); + if (ret) { + FTS_ERROR("request irq failed"); + goto err_irq_req; + } + + ret = fts_fwupg_init(ts_data); + if (ret) { + FTS_ERROR("init fw upgrade fail"); + } + +#ifdef FOCALTECH_PEN_NOTIFIER + ts_data->fts_pen_detect_flag = PEN_DETECTION_PULL; + ts_data->pen_notif.notifier_call = pen_notifier_callback; + ret = pen_detection_register_client(&ts_data->pen_notif); + if (ret) { + FTS_ERROR("[PEN]Unable to register pen_notifier: %d\n", ret); + } +#endif + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#ifdef CONFIG_DRM + if (ts_data->ts_workqueue) { + INIT_WORK(&ts_data->resume_work, fts_resume_work); + } + ts_data->fb_notif.notifier_call = drm_notifier_callback; +#if FTS_CONFIG_DRM_PANEL + if (active_panel) { + ret = drm_panel_notifier_register(active_panel, &ts_data->fb_notif); + if (ret) + FTS_ERROR("[DRM]drm_panel_notifier_register fail: %d\n", ret); + } else { + FTS_ERROR("[DRM]drm_panel_notifier_register fail: active_panel NULL!\n"); + } +#else +#ifndef FOCALTECH_CONFIG_PANEL_NOTIFICATIONS + ret = msm_drm_register_client(&ts_data->fb_notif); + if (ret) { + FTS_ERROR("[DRM]Unable to register fb_notifier: %d\n", ret); + } +#else + ret = register_panel_notifier(&ts_data->fb_notif); + if(ret) { + FTS_ERROR("register panel_notifier failed. ret=%d\n", ret); + } +#endif +#endif +#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 +#endif + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if FTS_USB_DETECT_EN + ts_data->usb_connected = 0x00; + ts_data->charger_notif.notifier_call = fts_charger_notifier_callback; + ret = power_supply_reg_notifier(&ts_data->charger_notif); + if (ret) { + FTS_ERROR("Unable to register charger_notifier: %d\n",ret); + goto err_register_charger_notify_failed; + } +#endif +#endif + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + fts_mmi_dev_register(ts_data); +#endif + + FTS_FUNC_EXIT(); + return 0; + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if FTS_USB_DETECT_EN +err_register_charger_notify_failed: +if (ts_data->charger_notif.notifier_call) + power_supply_unreg_notifier(&ts_data->charger_notif); +#endif +#endif + +err_irq_req: +#if FTS_POWER_SOURCE_CUST_EN +err_power_init: + fts_power_source_exit(ts_data); +#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); +err_gpio_config: + 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(); + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + fts_mmi_dev_unregister(ts_data); +#endif + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if FTS_USB_DETECT_EN + if (ts_data->charger_notif.notifier_call) + power_supply_unreg_notifier(&ts_data->charger_notif); +#endif +#endif + +#if FTS_POINT_REPORT_CHECK_EN + fts_point_report_check_exit(ts_data); +#endif + +#if FTS_APK_NODE_EN + fts_release_apk_debug_channel(ts_data); +#endif + +#if FTS_SYSFS_NODE_EN + fts_remove_sysfs(ts_data); +#endif + + fts_ex_mode_exit(ts_data); + + fts_fwupg_exit(ts_data); + +#if FTS_TEST_EN + fts_test_exit(ts_data); +#endif + +#if FTS_ESDCHECK_EN + fts_esdcheck_exit(ts_data); +#endif + +#ifdef FOCALTECH_PALM_SENSOR_EN + fts_palm_sensor_remove(ts_data); +#endif + +#if FTS_GESTURE_EN + fts_gesture_exit(ts_data); +#endif + + 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); +#ifdef FOCALTECH_PEN_NOTIFIER + if (pen_detection_unregister_client(&ts_data->pen_notif)) + FTS_ERROR("Error occurred while unregistering pen_notifier.\n"); +#endif +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#ifdef CONFIG_DRM +#if FTS_CONFIG_DRM_PANEL + if (active_panel) { + if(drm_panel_notifier_unregister(active_panel, &ts_data->fb_notif)) + FTS_ERROR("Error occurred while unregistering panel fb_notifier.\n"); + } +#else + if (msm_drm_unregister_client(&ts_data->fb_notif)) + FTS_ERROR("Error occurred while unregistering fb_notifier.\n"); +#endif +#elif defined(CONFIG_FB) + if (fb_unregister_client(&ts_data->fb_notif)) + FTS_ERROR("Error occurred while unregistering fb_notifier.\n"); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts_data->early_suspend); +#endif +#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; +} + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +static int _fts_ts_suspend(struct device *dev) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + +#ifdef FOCALTECH_SENSOR_EN + mutex_lock(&ts_data->state_mutex); +#endif + + FTS_FUNC_ENTER(); + if (ts_data->suspended) { +#ifdef FOCALTECH_SENSOR_EN + mutex_unlock(&ts_data->state_mutex); +#endif + FTS_INFO("Already in suspend state"); + return 0; + } + + if (ts_data->fw_loading) { +#ifdef FOCALTECH_SENSOR_EN + mutex_unlock(&ts_data->state_mutex); +#endif + FTS_INFO("fw upgrade in process, can't suspend"); + return 0; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_suspend(); +#endif + +#ifdef FOCALTECH_PALM_SENSOR_EN + if (ts_data->palm_detection_enabled) { + ret = enable_irq_wake(ts_data->irq); + if (ret) { + FTS_DEBUG("enable_irq_wake(irq:%d) fail", ts_data->irq); + } + fts_release_all_finger(); + ts_data->suspended = true; + FTS_INFO("Enter from palm detect suspend mode."); + return 0; + } +#endif + +#if FTS_GESTURE_EN +#ifdef FOCALTECH_SENSOR_EN + if (ts_data->should_enable_gesture) { +#endif + if (fts_gesture_suspend(ts_data) == 0) { + /* Enter into gesture mode(suspend) */ + ts_data->suspended = true; +#ifdef FOCALTECH_SENSOR_EN + ts_data->screen_state = SCREEN_OFF; + ts_data->wakeable = true; + mutex_unlock(&ts_data->state_mutex); + FTS_INFO("Enter gesture suspend mode."); +#endif + return 0; + } +#ifdef FOCALTECH_SENSOR_EN + } +#endif +#endif + +#if (!FTS_SUSPEND_IRQ_EN) + fts_irq_disable(); +#endif + + /* TP enter sleep mode */ + ret = fts_write_reg(FTS_REG_POWER_MODE, FTS_REG_POWER_MODE_SLEEP_VALUE); + if (ret < 0) + FTS_ERROR("set TP to sleep mode fail, ret=%d", ret); + + /* TP delay 50ms then lcd entery suspend*/ + mdelay(50); + + 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 + } + + if (!ts_data->pdata->always_on_vio) { + FTS_INFO("Set reset pin to 0 in suspend."); + gpio_direction_output(ts_data->pdata->reset_gpio, 0); + } + + fts_release_all_finger(); + ts_data->suspended = true; + FTS_FUNC_EXIT(); +#ifdef FOCALTECH_SENSOR_EN + ts_data->screen_state = SCREEN_OFF; + mutex_unlock(&ts_data->state_mutex); +#endif + return 0; +} + +static int fts_ts_suspend(struct device *dev) +{ + int ret = 0; + mutex_lock(&fts_data->suspend_resume_mutex); + ret = _fts_ts_suspend(dev); + mutex_unlock(&fts_data->suspend_resume_mutex); + + return ret; +} + +static int _fts_ts_resume(struct device *dev) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + +#ifdef FOCALTECH_SENSOR_EN + mutex_lock(&ts_data->state_mutex); +#endif + + FTS_FUNC_ENTER(); + if (!ts_data->suspended) { +#ifdef FOCALTECH_SENSOR_EN + mutex_unlock(&ts_data->state_mutex); +#endif + FTS_DEBUG("Already in awake state"); + return 0; + } + + fts_release_all_finger(); + + if (!ts_data->ic_info.is_incell) { +#if FTS_POWER_SOURCE_CUST_EN + fts_power_source_resume(ts_data); +#endif + //fts_reset_proc(200); + } + + if (!ts_data->pdata->always_on_vio) { + FTS_INFO("Reset IC in resume"); + fts_reset_proc(200); + } + +#if (!FTS_SUSPEND_IRQ_EN) + fts_irq_enable(); +#endif + + if (ts_data->pdata->dlfw_in_resume) { + ret = fts_wait_tp_to_valid(); + if(ret){ + FTS_INFO("wait tp to valid abnormal,need download fw"); + fts_fw_resume(true); + msleep(10); + } + } + fts_tp_state_recovery(ts_data); + +#if FTS_ESDCHECK_EN + fts_esdcheck_resume(); +#endif + +#ifdef FOCALTECH_PALM_SENSOR_EN + if (ts_data->palm_detection_enabled) { + int ret = 0; + ret = disable_irq_wake(ts_data->irq); + if (ret) { + FTS_DEBUG("disable_irq_wake(irq:%d) fail", ts_data->irq); + } + FTS_INFO("Exit from palm detect suspend mode."); + ts_data->suspended = false; + goto CHECK_LAZY_SET; + } +#endif + +#if FTS_GESTURE_EN + +#ifdef FOCALTECH_SENSOR_EN + if (ts_data->wakeable) { +#endif + if (fts_gesture_resume(ts_data) == 0) { + ts_data->suspended = false; +#ifdef FOCALTECH_SENSOR_EN + ts_data->screen_state = SCREEN_ON; + ts_data->wakeable = false; + mutex_unlock(&ts_data->state_mutex); + FTS_INFO("Exit from gesture suspend mode."); +#endif + return 0; + } +#ifdef FOCALTECH_SENSOR_EN + } +#endif +#endif + + ts_data->suspended = false; + +#if FTS_USB_DETECT_EN + fts_cable_detect_func(true); +#endif + + FTS_FUNC_EXIT(); +#ifdef FOCALTECH_SENSOR_EN + mutex_unlock(&ts_data->state_mutex); + ts_data->screen_state = SCREEN_ON; +#endif +#ifdef FOCALTECH_PALM_SENSOR_EN +CHECK_LAZY_SET: + if (ts_data->palm_detection_lazy_set != PALM_SENSOR_LAZY_SET_NONE) { + _fts_palm_sensor_set_enable( + (ts_data->palm_detection_lazy_set == PALM_SENSOR_LAZY_SET_ENABLE) ? 1 : 0); + FTS_INFO("Palm sensor lazy set done, clear flag."); + ts_data->palm_detection_lazy_set = PALM_SENSOR_LAZY_SET_NONE; + } +#endif + return 0; +} + +static int fts_ts_resume(struct device *dev) +{ + int ret = 0; + mutex_lock(&fts_data->suspend_resume_mutex); + ret = _fts_ts_resume(dev); + mutex_unlock(&fts_data->suspend_resume_mutex); + + return ret; +} +#endif + +/***************************************************************************** +* TP Driver +*****************************************************************************/ +static int fts_ts_probe(struct spi_device *spi) +{ + int ret = 0; + struct fts_ts_data *ts_data = NULL; + + FTS_INFO("Touch Screen(SPI BUS) driver prboe..."); +#if defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8756) || \ + defined (CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8006S_AA) || \ + defined (CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8009) + spi->mode = SPI_MODE_0; +#elif defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8719) + spi->mode = SPI_MODE_1; +#else + spi->mode = SPI_MODE_1; +#endif + + spi->bits_per_word = 8; + if (spi->max_speed_hz > FTS_SPI_CLK_MAX) + spi->max_speed_hz = FTS_SPI_CLK_MAX; + + ret = spi_setup(spi); + if (ret) { + FTS_ERROR("spi setup fail"); + 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->spi = spi; + ts_data->dev = &spi->dev; + ts_data->log_level = 1; + 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; +} + +static int fts_ts_remove(struct spi_device *spi) +{ + return fts_ts_remove_entry(spi_get_drvdata(spi)); +} + +static const struct spi_device_id fts_ts_id[] = { + {FTS_DRIVER_NAME, 0}, + {}, +}; +static const struct of_device_id fts_dt_match[] = { + {.compatible = "focaltech,fts", }, + {}, +}; +MODULE_DEVICE_TABLE(of, fts_dt_match); + +static struct spi_driver fts_ts_driver = { + .probe = fts_ts_probe, + .remove = fts_ts_remove, + .driver = { + .name = FTS_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(fts_dt_match), + }, + .id_table = fts_ts_id, +}; + +static int __init fts_ts_init(void) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + ret = spi_register_driver(&fts_ts_driver); + if ( ret != 0 ) { + FTS_ERROR("Focaltech touch screen driver init failed!"); + } + FTS_FUNC_EXIT(); + return ret; +} + +static void __exit fts_ts_exit(void) +{ + spi_unregister_driver(&fts_ts_driver); +} + +module_init(fts_ts_init); +module_exit(fts_ts_exit); + +MODULE_AUTHOR("FocalTech Driver Team"); +MODULE_DESCRIPTION("FocalTech Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_core.h b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_core.h new file mode 100755 index 000000000000..b8ec0a556307 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_core.h @@ -0,0 +1,366 @@ +/* + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "focaltech_common.h" +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +#include +#endif +#ifdef FTS_USB_DETECT_EN +#include +#endif +#if defined(FOCALTECH_SENSOR_EN) || defined(FOCALTECH_PALM_SENSOR_EN) +#include +#define SENSOR_TYPE_MOTO_TOUCH_PALM (SENSOR_TYPE_DEVICE_PRIVATE_BASE + 31) +#endif +#ifdef FOCALTECH_CONFIG_PANEL_NOTIFICATIONS +#include +#endif + +/***************************************************************************** +* 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_SPI_CLK_MAX 10000000 + +#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 +#define FTS_REG_RETRY_TIMES 5 + + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +#ifdef FOCALTECH_SENSOR_EN +/* display state */ +enum display_state { + SCREEN_UNKNOWN, + SCREEN_OFF, + SCREEN_ON, +}; +#endif +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 irq_gpio; + u32 irq_gpio_flags; + u32 reset_gpio; + u32 reset_gpio_flags; + bool always_on_vio; + bool dlfw_in_resume; + bool report_gesture_key; + bool share_reset_gpio; + bool have_key; + bool notify_to_panel; + 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; +}; + +#if defined(FOCALTECH_SENSOR_EN) || defined(FOCALTECH_PALM_SENSOR_EN) +struct focaltech_sensor_platform_data { + struct input_dev *input_sensor_dev; + struct sensors_classdev ps_cdev; + int sensor_opened; + char sensor_data; /* 0 near, 1 far */ + struct fts_ts_data *data; +}; +#endif + +#ifdef FOCALTECH_PALM_SENSOR_EN +enum palm_sensor_lazy_set { + PALM_SENSOR_LAZY_SET_NONE = 0, + PALM_SENSOR_LAZY_SET_ENABLE, + PALM_SENSOR_LAZY_SET_DISABLE, +}; +#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 mutex suspend_resume_mutex; + struct ftxxxx_proc proc; + spinlock_t irq_lock; + struct mutex report_mutex; + struct mutex bus_lock; + int irq; + int log_level; + int fw_is_running; /* confirm fw is running when using spi:default 0 */ + bool suspended; + bool fw_loading; + bool force_reflash; + bool irq_disabled; + bool power_disabled; + bool glove_mode; + bool cover_mode; + bool charger_mode; + /* multi-touch */ + struct ts_event *events; + u8 *bus_tx_buf; + u8 *bus_rx_buf; + u8 *point_buf; + int pnt_buf_size; + int touchs; + int key_state; + int touch_point; + int point_num; + const char *panel_supplier; + 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 +#ifdef FOCALTECH_PEN_NOTIFIER + int fts_pen_detect_flag; + struct notifier_block pen_notif; +#endif +#if defined(CONFIG_FB) || defined(CONFIG_DRM) + struct notifier_block fb_notif; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif + +#if FTS_USB_DETECT_EN + bool usb_detect_flag; + uint8_t usb_connected; + struct notifier_block charger_notif; +#endif + +#ifdef FOCALTECH_SENSOR_EN + bool wakeable; + bool should_enable_gesture; + enum display_state screen_state; + struct mutex state_mutex; + struct focaltech_sensor_platform_data *sensor_pdata; +#endif + +#ifdef FOCALTECH_PALM_SENSOR_EN + bool palm_detection_enabled; + enum palm_sensor_lazy_set palm_detection_lazy_set; + struct focaltech_sensor_platform_data *palm_sensor_pdata; + struct timer_list palm_release_fimer; + unsigned int palm_release_delay_ms; +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock palm_gesture_wakelock; +#else + struct wakeup_source *palm_gesture_wakelock; +#endif +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock palm_gesture_read_wakelock; +#else + struct wakeup_source *palm_gesture_read_wakelock; +#endif +#endif + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + struct ts_mmi_class_methods *imports; +#endif +}; + +/***************************************************************************** +* 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 */ +#if FTS_GESTURE_EN +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); +#endif + +/* Apk and functions */ +#if FTS_APK_NODE_EN +int fts_create_apk_debug_channel(struct fts_ts_data *); +void fts_release_apk_debug_channel(struct fts_ts_data *); +#endif + +/* ADB functions */ +#if FTS_SYSFS_NODE_EN +int fts_create_sysfs(struct fts_ts_data *ts_data); +int fts_remove_sysfs(struct fts_ts_data *ts_data); +#endif + +/* 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_fw_recovery(void); +int fts_fw_resume(bool need_reset); + +int fts_upgrade_bin(char *fw_name, bool force); +int fts_enter_test_environment(bool test_state); +int fts_fw_update_vendor_name(const char* name); + +/* 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_power_source_ctrl(struct fts_ts_data *ts_data, int enable); + +#endif /* __LINUX_FOCALTECH_CORE_H__ */ diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_esdcheck.c b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_esdcheck.c new file mode 100755 index 000000000000..993f531fbad5 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_esdcheck.c @@ -0,0 +1,485 @@ +/* + * + * 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 + +#if (!FTS_DEBUG_EN) +#if FTS_DEBUG_ESD_EN +#undef FTS_DEBUG +#undef FTS_FUNC_ENTER +#undef FTS_FUNC_EXIT +#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) + +#endif +#endif + +/***************************************************************************** +* 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 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("[ESD]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("[ESD]: Read ESD_SATURATE(0xED) failed 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, 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; +} + +/***************************************************************************** +* Name: get_chip_id +* Brief: Read Chip Id 3 times +* Input: +* Output: +* Return: 1(ture) - Read Chip Id 3 times failed +* 0(false) - Read Chip Id pass +*****************************************************************************/ +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(®_addr, 1, ®_value, 1); + if (ret < 0) { + FTS_ERROR("[ESD]: Read Reg 0xA3 failed 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("[ESD]: Read Chip id 3 times failed, 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(®_addr, 1, ®_value, 1); + if (ret < 0) { + FTS_ERROR("[ESD]: Read Reg 0x91 failed ret = %d!!", ret); + fts_esdcheck_data.nack_cnt++; + } else { + if ( 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; + } + + /* if read flow work cnt 5 times and the value are all the same, then need hardware_reset */ + if (fts_esdcheck_data.flow_work_hold_cnt >= 5) { + FTS_DEBUG("[ESD]: Flow Work Cnt(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_DEBUG("[ESD]: In interrupt state, not check esd, return immediately!!"); + return 0; + } + + /* 2. check power state, if suspend, no need check esd */ + if (fts_esdcheck_data.suspend == 1) { + FTS_DEBUG("[ESD]: In suspend, not check esd, return immediately!!"); + /* 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("[ESD]: In apk or adb command mode, not check esd, return immediately!!"); + return 0; + } + + /* 4. In factory mode, can't check esd */ + reg_addr = FTS_REG_WORKMODE; + ret = fts_read_reg(reg_addr, ®_value); + if ( ret < 0 ) { + fts_esdcheck_data.nack_cnt++; + } else if ( (reg_value & 0x70) != FTS_REG_WORKMODE_WORK_VALUE) { + FTS_INFO("[ESD]: not in work mode(reg=0x%x), no check esd, return immediately!!", 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("[ESD]: 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) +{ + u8 val = 0; + struct fts_ts_data *ts_data = container_of(work, + struct fts_ts_data, esdcheck_work.work); + + if (ENABLE == fts_esdcheck_data.mode) { + if (ts_data->ic_info.is_incell) { + fts_read_reg(FTS_REG_ESDCHECK_DISABLE, &val); + if (0xA5 == val) { + fts_esdcheck_data.mode = DISABLE; + FTS_DEBUG("[ESD]: is_incell, read REG 0x%x=0x%x\n, return", FTS_REG_ESDCHECK_DISABLE, val); + return; + } + } + 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; + return 0; +} + +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]: ESD check start!!"); + fts_esdcheck_data.flow_work_hold_cnt = 0; + fts_esdcheck_data.flow_work_cnt_last = 0; + queue_delayed_work(ts_data->ts_workqueue, &ts_data->esdcheck_work, + msecs_to_jiffies(ESDCHECK_WAIT_TIME)); + } else { + FTS_DEBUG("[ESD]: ESD check stop!!"); + cancel_delayed_work_sync(&ts_data->esdcheck_work); + } + } else { + FTS_DEBUG("[ESD]: ESD should disable!!"); + 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_FUNC_EXIT(); + return 0; +} + +int fts_esdcheck_resume( void ) +{ + FTS_FUNC_ENTER(); + fts_esdcheck_switch(ENABLE); + fts_esdcheck_data.suspend = 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_data.mode = DISABLE; + fts_esdcheck_switch(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 failed!"); + 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 function"); + return -EINVAL; + } + + memset((u8 *)&fts_esdcheck_data, 0, sizeof(struct fts_esdcheck_st)); + + fts_esdcheck_data.mode = ENABLE; + 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 */ + diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_ex_fun.c b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_ex_fun.c new file mode 100755 index 000000000000..47e6d9b7fa02 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_ex_fun.c @@ -0,0 +1,1516 @@ +/* + * + * 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 "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; + memcpy(proc->cmd, writebuf + 1, writelen); + proc->cmd_len = writelen; + 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(proc->cmd, proc->cmd_len, 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 file_operations fts_proc_fops = { + .owner = THIS_MODULE, + .read = fts_debug_read, + .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; + memcpy(proc->cmd, writebuf + 1, writelen); + proc->cmd_len = writelen; + 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(proc->cmd, proc->cmd_len, 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; +} + +static ssize_t reset_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + + if ('1' != buf[0]) { + pr_err("Invalid argument for reset\n"); + return -EINVAL; + } + + mutex_lock(&input_dev->mutex); + fts_reset_proc(0); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* 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; +} + +static ssize_t drv_irq_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s\n", + fts_data->irq_disabled? "DISABLED" : "ENABLED"); +} + +static ssize_t drv_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; +} + + +static ssize_t fts_poweron_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *data = fts_data; + struct input_dev *input_dev = data->input_dev; + bool val; + + mutex_lock(&input_dev->mutex); + val = data->power_disabled; + mutex_unlock(&input_dev->mutex); + + return scnprintf(buf, PAGE_SIZE, "%d\n", val == false); +} + + +static ssize_t fts_productinfo_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s\n", FTS_CHIP_NAME); +} + +static ssize_t fts_ic_ver_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_VENDOR_ID, &val); + count += snprintf(buf + count, PAGE_SIZE, "Product ID: 0x%02x\n", val); + fts_read_reg(FTS_REG_FW_VER, &val); + count += snprintf(buf + count, PAGE_SIZE, "Build ID: 0000-%02x\n", val); + count += scnprintf(buf + count, PAGE_SIZE, "IC: %s\n", FTS_CHIP_NAME); +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + mutex_unlock(&input_dev->mutex); + return count; +} + +static ssize_t fts_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "focaltech"); +} +/* fts_tpfwver interface */ +static ssize_t fts_tpfwver_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_tpfwver_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +static ssize_t buildid_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, "0000-%02x\n", fwver); + + mutex_unlock(&input_dev->mutex); + return num_read_chars; +} + +/* 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; +} + +/* fts_reg_raw */ +static ssize_t fts_tprwreg_raw_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); + + 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) { + buf[0] = (char)rw_op.val; + count = 1; + } + } + } else { + if (RWREG_OP_READ == rw_op.type) { + memcpy(buf, rw_op.opbuf, rw_op.len); + count = rw_op.len; + } + } + 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!"); + + } else { + 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(®, 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; + } + } + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* fts_upgrade_bin interface */ +static ssize_t fts_fwupgradebin_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +static ssize_t fts_fwupgradebin_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char fwname[FILE_NAME_LENGTH] = { 0 }; + struct input_dev *input_dev = fts_data->input_dev; + + if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) { + FTS_ERROR("fw bin name's length(%d) fail", (int)count); + return -EINVAL; + } + memset(fwname, 0, sizeof(fwname)); + snprintf(fwname, FILE_NAME_LENGTH, "%s", buf); + fwname[count - 1] = '\0'; + + FTS_INFO("upgrade with bin file through sysfs node"); + mutex_lock(&input_dev->mutex); + fts_upgrade_bin(fwname, 0); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* fts_force_upgrade interface */ +static ssize_t fts_fwforceupg_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +static ssize_t fts_fwforceupg_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char fwname[FILE_NAME_LENGTH]; + struct input_dev *input_dev = fts_data->input_dev; + + if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) { + FTS_ERROR("fw bin name's length(%d) fail", (int)count); + return -EINVAL; + } + memset(fwname, 0, sizeof(fwname)); + snprintf(fwname, FILE_NAME_LENGTH, "%s", buf); + fwname[count - 1] = '\0'; + + FTS_INFO("force upgrade through sysfs node"); + mutex_lock(&input_dev->mutex); + fts_upgrade_bin(fwname, 1); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t forcereflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 0) + return -EINVAL; + + fts_data->force_reflash = (input == 0) ? false : true; + + return count; +} + +static ssize_t flashprog_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", (fts_data->fw_loading) ? 1 : 0); +} + +static ssize_t doreflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char fwname[FILE_NAME_LENGTH]; + char prefix[FILE_NAME_LENGTH] = "focaltech"; + char template[FILE_NAME_LENGTH]; + struct input_dev *input_dev = fts_data->input_dev; + + if (count > FILE_NAME_LENGTH) { + FTS_ERROR("%s: FW filename is too long\n", __func__); + return -EINVAL; + } + + if (!fts_data->force_reflash) { + if (strncmp(buf, prefix, strnlen(prefix, sizeof(prefix)))) { + FTS_ERROR("%s: FW does not belong to Focaltech\n", __func__); + return -EINVAL; + } + + snprintf(template, sizeof(template), "-%s-", FTS_CHIP_NAME); + if (!strnstr(buf + strnlen(prefix, sizeof(prefix)), template, count)) { + FTS_ERROR("%s: FW does not belong to %s\n", __func__, FTS_CHIP_NAME); + return -EINVAL; + } + } + + memset(fwname, 0, sizeof(fwname)); + scnprintf(fwname, sizeof(fwname), "%s", buf); + fwname[count - 1] = '\0'; + + mutex_lock(&input_dev->mutex); + fts_fw_update_vendor_name(fwname); + fts_fw_recovery(); + mutex_unlock(&input_dev->mutex); + fts_data->force_reflash = false; + + 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 += snprintf(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 += snprintf(buf + count, PAGE_SIZE, "reset gpio:%d,int gpio:%d,irq:%d\n", + pdata->reset_gpio, pdata->irq_gpio, ts_data->irq); + + count += snprintf(buf + count, PAGE_SIZE, "IC ID:0x%02x%02x\n", + ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl); + 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; +} + +static ssize_t fts_panel_supplier_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *data = dev_get_drvdata(dev); + + if (data->panel_supplier) + return scnprintf(buf, PAGE_SIZE, "%s\n", + data->panel_supplier); + return 0; +} +static DEVICE_ATTR(panel_supplier, 0444, fts_panel_supplier_show, NULL); + +/* get the fw version example:cat fw_version */ +static DEVICE_ATTR(fts_fw_version, S_IRUGO | S_IWUSR, fts_tpfwver_show, fts_tpfwver_store); + +/* 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_reg_raw, S_IRUGO, fts_tprwreg_raw_show, NULL); +/* upgrade from fw bin file example:echo "*.bin" > fts_upgrade_bin */ +static DEVICE_ATTR(fts_upgrade_bin, S_IRUGO | S_IWUSR, fts_fwupgradebin_show, fts_fwupgradebin_store); +static DEVICE_ATTR(fts_force_upgrade, S_IRUGO | S_IWUSR, fts_fwforceupg_show, fts_fwforceupg_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); +static DEVICE_ATTR(drv_irq, S_IRUGO | S_IWUSR, drv_irq_show, drv_irq_store); +static DEVICE_ATTR(reset, S_IWUSR | S_IWGRP, NULL, reset_store); +static DEVICE_ATTR(buildid, S_IRUGO, buildid_show, NULL); +static DEVICE_ATTR(forcereflash, S_IWUSR | S_IWGRP, NULL, forcereflash_store); +static DEVICE_ATTR(flashprog, S_IRUGO, flashprog_show, NULL); +static DEVICE_ATTR(doreflash, S_IWUSR | S_IWGRP, NULL, doreflash_store); +static DEVICE_ATTR(poweron, S_IRUGO, fts_poweron_show, NULL); +static DEVICE_ATTR(productinfo, S_IRUGO, fts_productinfo_show, NULL); +static DEVICE_ATTR(ic_ver, S_IRUGO, fts_ic_ver_show, NULL); +static DEVICE_ATTR(name, S_IRUGO, fts_name_show, NULL); + +/* 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_reg_raw.attr, + &dev_attr_fts_dump_reg.attr, + &dev_attr_fts_upgrade_bin.attr, + &dev_attr_fts_force_upgrade.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, + &dev_attr_drv_irq.attr, + &dev_attr_reset.attr, + &dev_attr_buildid.attr, + &dev_attr_forcereflash.attr, + &dev_attr_flashprog.attr, + &dev_attr_doreflash.attr, + &dev_attr_poweron.attr, + &dev_attr_productinfo.attr, + &dev_attr_ic_ver.attr, + &dev_attr_name.attr, + &dev_attr_panel_supplier.attr, + NULL +}; + +static struct attribute_group fts_attribute_group = { + .attrs = fts_attributes +}; + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#include +#include + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *data = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!data) { + pr_err("cannot get fts_ts_data pointer\n"); + return (ssize_t)0; + } + path = kobject_get_path(&data->spi->dev.kobj, GFP_KERNEL); + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "focaltech"); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_NULL +}; +#endif + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +static int fts_sysfs_class(void *_data, bool create) +{ + struct fts_ts_data *data = _data; + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + static struct class *touchscreen_class; + static struct device *ts_class_dev; + static int minor; + + if (create) { + minor = input_get_new_minor(data->spi->chip_select, + 1, false); + if (minor < 0) + minor = input_get_new_minor(TSDEV_MINOR_BASE, + TSDEV_MINOR_MAX, true); + pr_info("assigned minor %d\n", minor); + + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + + ts_class_dev = device_create(touchscreen_class, NULL, + MKDEV(INPUT_MAJOR, minor), + data, FTS_CHIP_NAME); + if (IS_ERR(ts_class_dev)) { + error = PTR_ERR(ts_class_dev); + ts_class_dev = NULL; + return error; + } + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + error = device_create_file(ts_class_dev, &attrs[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + } else { + if (!touchscreen_class || !ts_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(ts_class_dev, &attrs[i]); + + device_unregister(ts_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor)); + ts_class_dev = NULL; + class_unregister(touchscreen_class); + pr_err("error creating touchscreen class\n"); + + return -ENODEV; +} +#endif + +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; + } +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + ret = fts_sysfs_class(ts_data, true); + if (ret) { + FTS_ERROR("[EX]: fts_sysfs_class() failed!!"); + sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group); + return -ENOMEM; + } + FTS_INFO("[EX]: sysfs_create_group() succeeded!!"); +#endif + + return ret; +} + +int fts_remove_sysfs(struct fts_ts_data *ts_data) +{ + sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group); +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + fts_sysfs_class(ts_data, false); +#endif + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_ex_mode.c b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_ex_mode.c new file mode 100755 index 000000000000..c0c970b12e48 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_ex_mode.c @@ -0,0 +1,307 @@ +/* + * + * 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, +}; + +/***************************************************************************** +* 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; + u8 m_val = 0; + + if (value) + m_val = 0x01; + else + m_val = 0x00; + + switch (mode) { + case MODE_GLOVE: + ret = fts_write_reg(FTS_REG_GLOVE_MODE_EN, m_val); + if (ret < 0) { + FTS_ERROR("MODE_GLOVE switch to %d fail", m_val); + } + break; + case MODE_COVER: + ret = fts_write_reg(FTS_REG_COVER_MODE_EN, m_val); + if (ret < 0) { + FTS_ERROR("MODE_COVER switch to %d fail", m_val); + } + break; + case MODE_CHARGER: + ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, m_val); + if (ret < 0) { + FTS_ERROR("MODE_CHARGER switch to %d fail", m_val); + } + 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 = snprintf(buf + count, PAGE_SIZE, "Glove Mode:%s\n", + ts_data->glove_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "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 = snprintf(buf + count, PAGE_SIZE, "Cover Mode:%s\n", + ts_data->cover_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "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 = snprintf(buf + count, PAGE_SIZE, "Charger Mode:%s\n", + ts_data->charger_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "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; +} + + +/* 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 struct attribute *fts_touch_mode_attrs[] = { + &dev_attr_fts_glove_mode.attr, + &dev_attr_fts_cover_mode.attr, + &dev_attr_fts_charger_mode.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); + } + + 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; + + 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; +} diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_flash.c b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_flash.c new file mode 100755 index 000000000000..39d97dad9d08 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_flash.c @@ -0,0 +1,634 @@ +/* + * + * 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: 2017-12-06 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" +#include "focaltech_flash.h" + +/***************************************************************************** +* Static variables +*****************************************************************************/ +#define FTS_FW_REQUEST_SUPPORT 1 +/* Example: focaltech_ts_fw_tianma.bin */ +#define FTS_FW_NAME_PREX_WITH_REQUEST "" + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +u8 fw_file[] = { +}; + +u8 fw_file2[] = { +}; + +u8 fw_file3[] = { +}; + +struct upgrade_module module_list[] = { + {FTS_MODULE_ID, FTS_MODULE_NAME, fw_file, sizeof(fw_file)}, + {FTS_MODULE2_ID, FTS_MODULE2_NAME, fw_file2, sizeof(fw_file2)}, + {FTS_MODULE3_ID, FTS_MODULE3_NAME, fw_file3, sizeof(fw_file3)}, +}; + +struct fts_upgrade *fwupgrade; + +int fts_check_bootid(void) +{ + int ret = 0; + u8 cmd = 0; + u8 id[2] = { 0 }; + struct ft_chip_t *chip_id = &fts_data->ic_info.ids; + + cmd = FTS_CMD_READ_ID; + ret = fts_read(&cmd, 1, id, 2); + if (ret < 0) { + FTS_ERROR("read boot id fail"); + return ret; + } + + FTS_INFO("read boot id:0x%02x 0x%02x", id[0], id[1]); + if ((chip_id->chip_idh == id[0]) && (chip_id->chip_idl == id[1])) { + return 0; + } + + return -EIO; +} + +static int fts_fw_download(const u8 *buf, u32 len, bool need_reset) +{ + int ret = 0; + int i = 0; + struct fts_ts_data *ts_data = fts_data; + + FTS_INFO("fw upgrade download function"); + if (!buf || (len < FTS_MIN_LEN)) { + FTS_ERROR("fw/len(%d) is invalid", len); + return -EINVAL; + } + + ts_data->fw_loading = 1; + fts_irq_disable(); +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(DISABLE); +#endif + + for (i = 0; i < 3; i++) { + FTS_INFO("fw download times:%d", i + 1); + ret = fts_fw_write_start(buf, len, need_reset); + if (0 == ret) + break; + } + if (i >= 3) { + FTS_ERROR("fw download fail"); + ret = -EIO; + goto err_fw_download; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ENABLE); +#endif + + ret = 0; +err_fw_download: + fts_irq_enable(); + ts_data->fw_loading = 0; + + return ret; + } + +static int fts_read_file(char *file_name, u8 **file_buf) +{ + int ret = 0; + char file_path[FILE_NAME_LENGTH] = { 0 }; + struct file *filp = NULL; + struct inode *inode; + mm_segment_t old_fs; + loff_t pos; + loff_t file_len = 0; + + if ((NULL == file_name) || (NULL == file_buf)) { + FTS_ERROR("filename/filebuf is NULL"); + return -EINVAL; + } + + snprintf(file_path, FILE_NAME_LENGTH, "%s%s", FTS_FW_BIN_FILEPATH, file_name); + filp = filp_open(file_path, O_RDONLY, 0); + if (IS_ERR(filp)) { + FTS_ERROR("open %s file fail", file_path); + return -ENOENT; + } + +#if 1 + inode = filp->f_inode; +#else + /* reserved for linux earlier verion */ + inode = filp->f_dentry->d_inode; +#endif + + file_len = inode->i_size; + *file_buf = (u8 *)vmalloc(file_len); + if (NULL == *file_buf) { + FTS_ERROR("file buf malloc fail"); + filp_close(filp, NULL); + return -ENOMEM; + } + old_fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + ret = vfs_read(filp, *file_buf, file_len , &pos); + if (ret < 0) + FTS_ERROR("read file fail"); + FTS_INFO("file len:%d read len:%d pos:%d", (u32)file_len, ret, (u32)pos); + filp_close(filp, NULL); + set_fs(old_fs); + + return ret; +} + +int fts_upgrade_bin(char *fw_name, bool force) +{ + int ret = 0; + u32 fw_file_len = 0; + u8 *fw_file_buf = NULL; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("start upgrade with fw bin"); + if (NULL == upg) { + FTS_ERROR("upgrade/func is null"); + return -EINVAL; + } + + if (upg->ts_data->fw_loading) { + FTS_INFO("fw is loading, not download again"); + return -EINVAL; + } + + ret = fts_read_file(fw_name, &fw_file_buf); + if ((ret < 0) || (ret < FTS_MIN_LEN) || (ret > FTS_MAX_LEN_FILE)) { + FTS_ERROR("read fw bin file(sdcard) fail, len:%d", fw_file_len); + goto err_bin; + } + + fw_file_len = ret; + FTS_INFO("fw bin file len:%d", fw_file_len); + ret = fts_fw_download(fw_file_buf, fw_file_len, true); + if (ret < 0) { + FTS_ERROR("upgrade fw bin failed"); + goto err_bin; + } + + FTS_INFO("upgrade fw bin success"); + +err_bin: + if (fw_file_buf) { + vfree(fw_file_buf); + fw_file_buf = NULL; + } + return ret; +} + + +int fts_enter_test_environment(bool test_state) +{ + int ret = 0; + u8 detach_flag = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("fw test download function"); + if (!upg) { + FTS_ERROR("upg is null"); + return -EINVAL; + } + + if (fts_data->fw_loading) { + FTS_INFO("fw is loading, not download again"); + return -EINVAL; + } + +#if defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8756) || \ + defined (CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8009) + if (!upg->fw || (upg->fw_length <= FTS_MAX_LEN_APP_FT8756)) { +#else + if (!upg->fw || (upg->fw_length <= FTS_MAX_LEN_APP)) { +#endif + FTS_INFO("not multi-app"); + return 0; + } + + if (test_state) { +#if defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8756) || \ + defined (CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8009) + ret = fts_fw_download(upg->fw + FTS_MAX_LEN_APP_FT8756, upg->fw_length, true); +#else + ret = fts_fw_download(upg->fw + FTS_MAX_LEN_APP, upg->fw_length, true); +#endif + } else { + ret = fts_fw_download(upg->fw, upg->fw_length, true); + } + if (ret < 0) { + FTS_ERROR("fw(app2) download fail"); + return ret; + } + + msleep(50); + ret = fts_read_reg(FTS_REG_FACTORY_MODE_DETACH_FLAG, &detach_flag); + FTS_INFO("regb4:0x%02x", detach_flag); + + return 0; +} + +int fts_fw_resume(bool need_reset) +{ + int ret = 0; + struct fts_upgrade *upg = fwupgrade; + const struct firmware *fw = NULL; + char fwname[FILE_NAME_LENGTH] = { 0 }; + + FTS_INFO("fw upgrade resume function"); + if (!upg || !upg->fw) { + FTS_ERROR("upg/fw is null"); + return -EINVAL; + } + + if (upg->ts_data->fw_loading) { + FTS_INFO("fw is loading, not download again"); + return -EINVAL; + } + + snprintf(fwname, FILE_NAME_LENGTH, "%s%s.bin", \ + FTS_FW_NAME_PREX_WITH_REQUEST, upg->module_info->vendor_name); + + /* 1. request firmware */ + ret = request_firmware(&fw, fwname, upg->ts_data->dev); + if (ret != 0) { + FTS_ERROR("%s:firmware(%s) request fail,ret=%d\n", + __func__, fwname, ret); + FTS_INFO("download fw from bootimage"); + ret = fts_fw_download(upg->fw, upg->fw_length, need_reset); + } else { + FTS_INFO("firmware(%s) request successfully", fwname); + ret = fts_fw_download(fw->data, fw->size, need_reset); + } + if (ret < 0) { + FTS_ERROR("fw resume download failed"); + return ret; + } + + /* update to newest fw if needed */ + if (fw != NULL) { + u8 *tmpbuf = NULL; + if (upg->fw_from_request) + vfree(upg->fw); + tmpbuf = vmalloc(fw->size); + if (NULL == tmpbuf) { + FTS_ERROR("fw buffer vmalloc fail"); + ret = -ENOMEM; + goto FTS_FW_RESUME_VMALLOC_ERROR; + } else { + memcpy(tmpbuf, fw->data, fw->size); + upg->fw = tmpbuf; + upg->fw_length = fw->size; + upg->fw_from_request = 1; + } + } + +FTS_FW_RESUME_VMALLOC_ERROR: + if (fw != NULL) { + release_firmware(fw); + fw = NULL; + } + + return ret; +} + +int fts_fw_recovery(void) +{ + int ret = 0; + u8 boot_state = 0; + u8 chip_id = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("check if boot recovery"); + if (!upg || !upg->ts_data) { + FTS_ERROR("upg/ts_data is null"); + return -EINVAL; + } + + if (upg->ts_data->fw_loading) { + FTS_INFO("fw is loading, not download again"); + return -EINVAL; + } + + upg->ts_data->fw_is_running = false; + if (!upg->ts_data->force_reflash) { + ret = fts_check_bootid(); + if (ret < 0) { + FTS_ERROR("check boot id fail"); + upg->ts_data->fw_is_running = true; + return ret; + } + + ret = fts_read_reg(0xD0, &boot_state); + if (ret < 0) { + FTS_ERROR("read boot state failed, ret=%d", ret); + upg->ts_data->fw_is_running = true; + return ret; + } + +#if defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8756) || \ + defined (CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8006S_AA) || \ + defined (CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8009) + if (boot_state != 0x01) { +#else + if (boot_state != 0x02) { +#endif + FTS_INFO("not in boot mode(0x%x),exit", boot_state); + upg->ts_data->fw_is_running = true; + return -EIO; + } + + FTS_INFO("abnormal situation,need download fw"); + } + + ret = fts_fw_resume(false); + if (ret < 0) { + FTS_ERROR("fts_fw_resume fail"); + return ret; + } + msleep(10); + ret = fts_read_reg(FTS_REG_CHIP_ID, &chip_id); + FTS_INFO("read chip id:0x%02x", chip_id); + + fts_tp_state_recovery(upg->ts_data); + + FTS_INFO("boot recovery pass"); + return ret; +} + +int fts_fw_update_vendor_name(const char* name) { + struct fts_upgrade *upg = fwupgrade; + char* pos; + int len; + + if (!upg || !upg->module_info) { + FTS_ERROR("upgrade struct not init"); + return -EINVAL; + } + + len = strlen(name); + if (len > FILE_NAME_LENGTH) { + FTS_ERROR("vendor name is too long"); + return -EINVAL; + } + pos = strstr(name, ".bin"); + if (pos == NULL) + snprintf(upg->module_info->vendor_name, FILE_NAME_LENGTH, "%s", name); + else { + len = len - strlen(".bin"); + snprintf(upg->module_info->vendor_name, len + 1, "%s", name); + } + return 0; +} + +static int fts_fwupg_get_module_info(struct fts_upgrade *upg) +{ + 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) { + 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 fw file successfully"); + break; + } + } + if (i >= FTS_GET_MODULE_NUM) { + FTS_ERROR("no module id match, don't get file"); + return -ENODATA; + } + } + + 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 }; + + 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 (0 == ret) { + 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) { + msleep(500); + 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); + } + + 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_work(struct work_struct *work) +{ + int ret = 0; + u8 chip_id = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("fw upgrade work function"); + if (!upg || !upg->ts_data) { + FTS_ERROR("upg/ts_data is null"); + return ; + } + + /* get fw */ + ret = fts_fwupg_get_fw_file(upg); + if (ret < 0) { + FTS_ERROR("get file fail, can't upgrade"); + return ; + } + +#if !FTS_AUTO_UPGRADE_EN + FTS_INFO("FTS_AUTO_UPGRADE_EN is disabled, not upgrade when power on"); + return ; +#endif + + if (upg->ts_data->fw_loading) { + FTS_INFO("fw is loading, not download again"); + return ; + } + + ret = fts_fw_download(upg->fw, upg->fw_length, true); + if (ret < 0) { + FTS_ERROR("fw auto download failed"); + } else { + msleep(50); + ret = fts_read_reg(FTS_REG_CHIP_ID, &chip_id); + FTS_INFO("read chip id:0x%02x", chip_id); + } +} + +int fts_fwupg_init(struct fts_ts_data *ts_data) +{ + 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; + } + + fwupgrade = (struct fts_upgrade *)kzalloc(sizeof(*fwupgrade), GFP_KERNEL); + if (NULL == fwupgrade) { + FTS_ERROR("malloc memory for upgrade fail"); + return -ENOMEM; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(DISABLE); +#endif + + 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; +} diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_flash.h b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_flash.h new file mode 100755 index 000000000000..458f774bbfc8 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_flash.h @@ -0,0 +1,186 @@ +/************************************************************************ +* 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 10 +#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_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 200 +#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 6 +#define FTS_RETRIES_ECC_CAL 10 +#define FTS_RETRIES_DELAY_ECC_CAL 50 +#define FTS_CMD_ECC_READ 0x66 +#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_FT8756 (88 * 1024) +#define FTS_MAX_LEN_APP (64 * 1024) +#define FTS_MAX_LEN_APP_PARAMS (32 * 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, +}; + +/***************************************************************************** +* 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; + 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 (*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_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; + int module_id; + bool fw_from_request; + u8 *fw; + u32 fw_length; + u8 *lic; + u32 lic_length; +}; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +int fts_check_bootid(void); +int fts_fw_write_start(const u8 *buf, u32 len, bool need_reset); + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +#endif diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_flash_ft8006s.c b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_flash_ft8006s.c new file mode 100644 index 000000000000..39ec4a32d9c1 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_flash_ft8006s.c @@ -0,0 +1,462 @@ +/* + * + * 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: 2017-12-06 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" +#include "focaltech_flash.h" + +/***************************************************************************** +* Static variables +*****************************************************************************/ +#define FTS_READ_BOOT_ID_TIMEOUT 3 +#define FTS_FLASH_PACKET_LENGTH_SPI (32 * 1024 - 16) +#define FTS_FLASH_DELAY_INIT 12 +#define FTS_FLASH_DRWR_SUPPORT 0 +#define FTS_FLASH_HALF_LENGTH 0 +#define FTS_CMD_ECC_LENGTH_MAX (128 * 1024) +#define FTS_ROMBOOT_CMD_ECC_FINISH_OK 0xA5 + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +static int fts_fwupg_hardware_reset_to_boot(void) +{ + fts_reset_proc(0); + mdelay(8); + + return 0; +} + +static int fts_enter_into_boot(void) +{ + int ret = 0; + int i = 0; + int j = 0; + u8 cmd[2] = { 0 }; + + FTS_INFO("enter into boot environment"); + for (i = 0; i < FTS_UPGRADE_LOOP; i++) { + /* hardware tp reset to boot */ + fts_fwupg_hardware_reset_to_boot(); + mdelay(FTS_FLASH_DELAY_INIT); + + /* enter into boot & check boot id*/ + for (j = 0; j < FTS_READ_BOOT_ID_TIMEOUT; j++) { + cmd[0] = FTS_CMD_START1; + ret = fts_write(cmd, 1); + if (ret >= 0) { + mdelay(FTS_FLASH_DELAY_INIT); + ret = fts_check_bootid(); + if (0 == ret) { + FTS_INFO("boot id check pass, retry=%d", i); + return 0; + } + } + } + } + + return -EIO; +} + + +static int fts_dpram_write(u32 saddr, const u8 *buf, u32 len, bool wpram) +{ + int ret = 0; + int i = 0; + int j = 0; + u8 *cmd = NULL; + u32 addr = 0; + u32 baseaddr = wpram ? FTS_PRAM_SADDR : FTS_DRAM_SADDR; + u32 offset = 0; + u32 remainder = 0; + u32 packet_number = 0; + u32 packet_len = 0; + u32 packet_size = FTS_FLASH_PACKET_LENGTH_SPI; + + FTS_INFO("dpram write"); + if (NULL == buf) { + FTS_ERROR("fw buf is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > FTS_MAX_LEN_APP)) { + FTS_ERROR("fw length(%d) fail", len); + return -EINVAL; + } + + cmd = vmalloc(packet_size + FTS_CMD_WRITE_LEN); + if (NULL == cmd) { + FTS_ERROR("malloc memory for pram write buffer fail"); + return -ENOMEM; + } + memset(cmd, 0, packet_size + FTS_CMD_WRITE_LEN); + + packet_number = len / packet_size; + remainder = len % packet_size; + if (remainder > 0) + packet_number++; + packet_len = packet_size; + FTS_INFO("write data, num:%d remainder:%d", packet_number, remainder); + + for (i = 0; i < packet_number; i++) { + offset = i * packet_size; + addr = saddr + offset + baseaddr; + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + /* set pram address */ + cmd[0] = FTS_ROMBOOT_CMD_SET_PRAM_ADDR; + cmd[1] = BYTE_OFF_16(addr); + cmd[2] = BYTE_OFF_8(addr); + cmd[3] = BYTE_OFF_0(addr); + ret = fts_write(&cmd[0], FTS_ROMBOOT_CMD_SET_PRAM_ADDR_LEN); + if (ret < 0) { + FTS_ERROR("set pram(%d) addr(%d) fail", i, addr); + goto write_pram_err; + } + + /* write pram data */ + cmd[0] = FTS_ROMBOOT_CMD_WRITE; + for (j = 0; j < packet_len; j++) { + cmd[1 + j] = buf[offset + j]; + } + ret = fts_write(&cmd[0], 1 + packet_len); + if (ret < 0) { + FTS_ERROR("write fw to pram(%d) fail", i); + goto write_pram_err; + } + } + +write_pram_err: + if (cmd) { + vfree(cmd); + cmd = NULL; + } + return ret; +} + +static int fts_ecc_cal_tp(u32 ecc_saddr, u32 ecc_len, u16 *ecc_value) +{ + int ret = 0; + int i = 0; + u8 cmd[FTS_ROMBOOT_CMD_ECC_NEW_LEN] = { 0 }; + u8 value[2] = { 0 }; + + FTS_INFO("ecc calc in tp"); + cmd[0] = FTS_ROMBOOT_CMD_ECC; + cmd[1] = BYTE_OFF_16(ecc_saddr); + cmd[2] = BYTE_OFF_8(ecc_saddr); + cmd[3] = BYTE_OFF_0(ecc_saddr); + cmd[4] = BYTE_OFF_16(ecc_len); + cmd[5] = BYTE_OFF_8(ecc_len); + cmd[6] = BYTE_OFF_0(ecc_len); + + /* make boot to calculate ecc in pram */ + ret = fts_write(cmd, FTS_ROMBOOT_CMD_ECC_NEW_LEN); + if (ret < 0) { + FTS_ERROR("ecc calc cmd fail"); + return ret; + } + mdelay(2); + + /* wait boot calculate ecc finish */ + cmd[0] = FTS_ROMBOOT_CMD_ECC_FINISH; + for (i = 0; i < FTS_ECC_FINISH_TIMEOUT; i++) { + ret = fts_read(cmd, 1, value, 1); + if (ret < 0) { + FTS_ERROR("ecc finish cmd fail"); + return ret; + } + if (FTS_ROMBOOT_CMD_ECC_FINISH_OK == value[0]) + break; + mdelay(1); + } + if (i >= FTS_ECC_FINISH_TIMEOUT) { + FTS_ERROR("wait ecc finish timeout,ecc_finish=%x", value[0]); + return -EIO; + } + + /* get ecc value calculate in boot */ + cmd[0] = FTS_ROMBOOT_CMD_ECC_READ; + ret = fts_read(cmd, 1, value, 2); + if (ret < 0) { + FTS_ERROR("ecc read cmd fail"); + return ret; + } + + *ecc_value = ((u16)(value[0] << 8) + value[1]) & 0x0000FFFF; + return 0; +} + +static int fts_ecc_cal_host(const u8 *data, u32 data_len, u16 *ecc_value) +{ + u16 ecc = 0; + u16 i = 0; + u16 j = 0; + u16 al2_fcs_coef = AL2_FCS_COEF; + + for (i = 0; i < data_len; i += 2 ) { + ecc ^= ((data[i] << 8) | (data[i + 1])); + for (j = 0; j < 16; j ++) { + if (ecc & 0x01) + ecc = (u16)((ecc >> 1) ^ al2_fcs_coef); + else + ecc >>= 1; + } + } + + *ecc_value = ecc & 0x0000FFFF; + return 0; +} + +static int fts_ecc_check(const u8 *buf, u32 len, u32 ecc_saddr) +{ + int ret = 0; + int i = 0; + u16 ecc_in_host = 0; + u16 ecc_in_tp = 0; + int packet_length = 0; + int packet_number = 0; + int packet_remainder = 0; + int offset = 0; + u32 packet_size = FTS_CMD_ECC_LENGTH_MAX; + + FTS_INFO("ecc check"); + packet_number = len / packet_size; + packet_remainder = len % packet_size; + if (packet_remainder) + packet_number++; + packet_length = packet_size; + + for (i = 0; i < packet_number; i++) { + /* last packet */ + if ((i == (packet_number - 1)) && packet_remainder) + packet_length = packet_remainder; + + ret = fts_ecc_cal_host(buf + offset, packet_length, &ecc_in_host); + if (ret < 0) { + FTS_ERROR("ecc in host calc fail"); + return ret; + } + + ret = fts_ecc_cal_tp(ecc_saddr + offset, packet_length, &ecc_in_tp); + if (ret < 0) { + FTS_ERROR("ecc in tp calc fail"); + return ret; + } + + FTS_DEBUG("ecc in tp:%04x,host:%04x,i:%d", ecc_in_tp, ecc_in_host, i); + if (ecc_in_tp != ecc_in_host) { + FTS_ERROR("ecc_in_tp(%x) != ecc_in_host(%x), ecc check fail", + ecc_in_tp, ecc_in_host); + return -EIO; + } + + offset += packet_length; + } + + return 0; +} + +static int fts_pram_write_ecc(const u8 *buf, u32 len) +{ + int ret = 0; + u32 pram_app_size = 0; + u16 code_len = 0; + u16 code_len_n = 0; + u32 pram_start_addr = 0; + + FTS_INFO("begin to write pram app(bin len:%d)", len); + /* get pram app length */ + code_len = ((u16)buf[FTS_APP_INFO_OFFSET + 0] << 8) + + buf[FTS_APP_INFO_OFFSET + 1]; + code_len_n = ((u16)buf[FTS_APP_INFO_OFFSET + 2] << 8) + + buf[FTS_APP_INFO_OFFSET + 3]; + if ((code_len + code_len_n) != 0xFFFF) { + FTS_ERROR("pram code len(%x %x) fail", code_len, code_len_n); + return -EINVAL; + } + + if (FTS_FLASH_HALF_LENGTH) { + pram_app_size = ((u32) code_len) * 2; + } else { + pram_app_size = (u32) code_len; + } + + if ((pram_app_size < FTS_MIN_LEN) || (pram_app_size > FTS_MAX_LEN_APP)) { + FTS_ERROR("pram app length(%d) is invalid", pram_app_size); + return -EINVAL; + } + + FTS_INFO("pram app length in fact:%d", pram_app_size); + /* write pram */ + ret = fts_dpram_write(pram_start_addr, buf, pram_app_size, true); + if (ret < 0) { + FTS_ERROR("write pram fail"); + return ret; + } + + /* check ecc */ + ret = fts_ecc_check(buf, pram_app_size, pram_start_addr); + if (ret < 0) { + FTS_ERROR("pram ecc check fail"); + return ret; + } + + FTS_INFO("pram app write successfully"); + return 0; +} + +static int fts_dram_write_ecc(const u8 *buf, u32 len) +{ + int ret = 0; + u32 dram_size = 0; + u32 pram_app_size = 0; + u32 dram_start_addr = 0; + u16 const_len = 0; + u16 const_len_n = 0; + const u8 *dram_buf = NULL; + + FTS_INFO("begin to write dram data(bin len:%d)", len); + /* get dram data length */ + const_len = ((u16)buf[FTS_APP_INFO_OFFSET + 0x8] << 8) + + buf[FTS_APP_INFO_OFFSET + 0x9]; + const_len_n = ((u16)buf[FTS_APP_INFO_OFFSET + 0x0A] << 8) + + buf[FTS_APP_INFO_OFFSET + 0x0B]; + if (((const_len + const_len_n) != 0xFFFF) || (const_len == 0)) { + FTS_INFO("no support dram,const len(%x %x)", const_len, const_len_n); + return 0; + } + + if (FTS_FLASH_HALF_LENGTH) { + dram_size = ((u32) const_len) * 2; + } else { + dram_size = (u32) const_len; + } + + if ((dram_size <= 0) || (dram_size > FTS_MAX_LEN_APP_PARAMS)) { + FTS_ERROR("dram data length(%d) is invalid", dram_size); + return -EINVAL; + } + + pram_app_size = ((u32)(((u16)buf[FTS_APP_INFO_OFFSET + 0] << 8) + + buf[FTS_APP_INFO_OFFSET + 1])) * 2; + + dram_buf = buf + pram_app_size; + FTS_INFO("dram buf length in fact:%d,offset:%d", dram_size, pram_app_size); + /* write pram */ + ret = fts_dpram_write(dram_start_addr, dram_buf, dram_size, false); + if (ret < 0) { + FTS_ERROR("write dram fail"); + return ret; + } + + /* check ecc */ + ret = fts_ecc_check(dram_buf, dram_size, dram_start_addr); + if (ret < 0) { + FTS_ERROR("dram ecc check fail"); + return ret; + } + + FTS_INFO("dram data write successfully"); + return 0; +} + +static int fts_pram_start(void) +{ + int ret = 0; + u8 cmd = FTS_ROMBOOT_CMD_START_APP; + + FTS_INFO("remap to start pram"); + ret = fts_write(&cmd, 1); + if (ret < 0) { + FTS_ERROR("write start pram cmd fail"); + return ret; + } + + return 0; +} + +/* + * description: download fw to IC and run + * + * param - buf: const, fw data buffer + * len: length of fw + * + * return 0 if success, otherwise return error code + */ +int fts_fw_write_start(const u8 *buf, u32 len, bool need_reset) +{ + int ret = 0; + + FTS_INFO("begin to write and start fw(bin len:%d)", len); + fts_data->fw_is_running = false; + + if (need_reset) { + /* enter into boot environment */ + ret = fts_enter_into_boot(); + if (ret < 0) { + FTS_ERROR("enter into boot environment fail"); + return ret; + } + } + + /* write pram */ + ret = fts_pram_write_ecc(buf, len); + if (ret < 0) { + FTS_ERROR("write pram fail"); + return ret; + } + + if (FTS_FLASH_DRWR_SUPPORT) { + /* write dram */ + ret = fts_dram_write_ecc(buf, len); + if (ret < 0) { + FTS_ERROR("write dram fail"); + return ret; + } + } + + /* remap pram and run fw */ + ret = fts_pram_start(); + if (ret < 0) { + FTS_ERROR("pram start fail"); + return ret; + } + + fts_data->fw_is_running = true; + FTS_INFO("fw download successfully"); + return 0; +} + diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_flash_ft8719.c b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_flash_ft8719.c new file mode 100755 index 000000000000..5048ff1a984f --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_flash_ft8719.c @@ -0,0 +1,364 @@ +/* + * + * 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: 2017-12-06 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" +#include "focaltech_flash.h" + +/***************************************************************************** +* Static variables +*****************************************************************************/ +#define FTS_READ_BOOT_ID_TIMEOUT 3 +#define FTS_FLASH_PACKET_LENGTH_SPI_LOW (4 * 1024 - 4) +#define FTS_FLASH_PACKET_LENGTH_SPI (64 * 1024) + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +static int fts_fwupg_hardware_reset_to_boot(void) +{ + fts_reset_proc(0); + mdelay(8); + + return 0; +} + +static int fts_enter_into_boot(void) +{ + int ret = 0; + int i = 0; + int j = 0; + u8 cmd = 0; + + FTS_INFO("enter into boot environment"); + for (i = 0; i < FTS_UPGRADE_LOOP; i++) { + /* hardware tp reset to boot */ + fts_fwupg_hardware_reset_to_boot(); + + /* enter into boot & check boot id*/ + for (j = 0; j < FTS_READ_BOOT_ID_TIMEOUT; j++) { + cmd = FTS_CMD_START1; + ret = fts_write(&cmd, 1); + if (ret >= 0) { + mdelay(8); + ret = fts_check_bootid(); + if (0 == ret) { + FTS_INFO("boot id check pass, retry=%d", i); + return 0; + } + } + } + } + + return -EIO; +} + +static bool fts_check_fast_download(void) +{ + int ret = 0; + u8 cmd[6] = {0xF2, 0x00, 0x78, 0x0A, 0x00, 0x02}; + u8 value = 0; + u8 value2[2] = { 0 }; + + ret = fts_read_reg(0xdb, &value); + if (ret < 0) { + FTS_ERROR("read 0xdb fail"); + goto read_err; + } + + ret = fts_read(cmd, 6, value2, 2); + if (ret < 0) { + FTS_ERROR("read f2 fail"); + goto read_err; + } + + FTS_INFO("0xdb = 0x%x, 0xF2 = 0x%x", value, value2[0]); + if ((value >= 0x18) && (value2[0] == 0x55)) { + FTS_INFO("IC support fast-download"); + return true; + } + +read_err: + FTS_INFO("IC not support fast-download"); + return false; +} + +static int fts_pram_write(u32 saddr, const u8 *buf, u32 len) +{ + int ret = 0; + int i = 0; + int j = 0; + u8 *cmd; + u32 addr = 0; + u32 offset = 0; + u32 remainder = 0; + u32 packet_number; + u32 packet_len = 0; + u32 packet_size = FTS_FLASH_PACKET_LENGTH_SPI; + bool fd_support = true; + + FTS_INFO("pram write"); + if (NULL == buf) { + FTS_ERROR("fw buf is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > FTS_MAX_LEN_APP)) { + FTS_ERROR("fw length(%d) fail", len); + return -EINVAL; + } + + fd_support = fts_check_fast_download(); + if (!fd_support) + packet_size = FTS_FLASH_PACKET_LENGTH_SPI_LOW; + cmd = kzalloc(packet_size + FTS_CMD_WRITE_LEN, GFP_KERNEL); + if (NULL == cmd) { + FTS_ERROR("malloc memory for pram write buffer fail"); + return -ENOMEM; + } + + packet_number = len / packet_size; + remainder = len % packet_size; + if (remainder > 0) + packet_number++; + packet_len = packet_size; + + cmd[0] = FTS_ROMBOOT_CMD_WRITE; + for (i = 0; i < packet_number; i++) { + offset = i * packet_size; + addr = saddr + offset; + cmd[1] = BYTE_OFF_16(addr); + cmd[2] = BYTE_OFF_8(addr); + cmd[3] = BYTE_OFF_0(addr); + + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + cmd[4] = BYTE_OFF_8(packet_len); + cmd[5] = BYTE_OFF_0(packet_len); + + for (j = 0; j < packet_len; j++) { + cmd[FTS_CMD_WRITE_LEN + j] = buf[offset + j]; + } + ret = fts_write(&cmd[0], FTS_CMD_WRITE_LEN + packet_len); + if (ret < 0) { + FTS_ERROR("write fw to pram(%d) fail", i); + goto write_pram_err; + } + + if (!fd_support) + mdelay(3); + } + +write_pram_err: + if (cmd) { + kfree(cmd); + cmd = NULL; + } + return ret; +} + +static int fts_ecc_cal_tp(u32 ecc_saddr, u32 ecc_len, u16 *ecc_value) +{ + int ret = 0; + int i = 0; + u8 cmd[FTS_ROMBOOT_CMD_ECC_NEW_LEN] = { 0 }; + u8 value[2] = { 0 }; + + FTS_INFO("ecc calc in tp"); + cmd[0] = FTS_ROMBOOT_CMD_ECC; + cmd[1] = BYTE_OFF_16(ecc_saddr); + cmd[2] = BYTE_OFF_8(ecc_saddr); + cmd[3] = BYTE_OFF_0(ecc_saddr); + cmd[4] = BYTE_OFF_16(ecc_len); + cmd[5] = BYTE_OFF_8(ecc_len); + cmd[6] = BYTE_OFF_0(ecc_len); + + /* make boot to calculate ecc in pram */ + ret = fts_write(cmd, FTS_ROMBOOT_CMD_ECC_NEW_LEN); + if (ret < 0) { + FTS_ERROR("ecc calc cmd fail"); + return ret; + } + mdelay(3); + + /* wait boot calculate ecc finish */ + cmd[0] = FTS_ROMBOOT_CMD_ECC_FINISH; + for (i = 0; i < FTS_ECC_FINISH_TIMEOUT; i++) { + ret = fts_read(cmd, 1, value, 1); + if (ret < 0) { + FTS_ERROR("ecc finish cmd fail"); + return ret; + } + if (0 == value[0]) + break; + mdelay(1); + } + if (i >= FTS_ECC_FINISH_TIMEOUT) { + FTS_ERROR("wait ecc finish timeout"); + return -EIO; + } + + /* get ecc value calculate in boot */ + cmd[0] = FTS_ROMBOOT_CMD_ECC_READ; + ret = fts_read(cmd, 1, value, 2); + if (ret < 0) { + FTS_ERROR("ecc read cmd fail"); + return ret; + } + + *ecc_value = ((u16)(value[0] << 8) + value[1]) & 0x0000FFFF; + return 0; +} + +static int fts_ecc_cal_host(const u8 *data, u32 data_len, u16 *ecc_value) +{ + u16 ecc = 0; + u16 i = 0; + u16 j = 0; + u16 al2_fcs_coef = AL2_FCS_COEF; + + for (i = 0; i < data_len; i += 2 ) { + ecc ^= ((data[i] << 8) | (data[i + 1])); + for (j = 0; j < 16; j ++) { + if (ecc & 0x01) + ecc = (u16)((ecc >> 1) ^ al2_fcs_coef); + else + ecc >>= 1; + } + } + + *ecc_value = ecc & 0x0000FFFF; + return 0; +} + +static int fts_pram_start(void) +{ + int ret = 0; + u8 cmd = FTS_ROMBOOT_CMD_START_APP; + + FTS_INFO("remap to start pram"); + ret = fts_write(&cmd, 1); + if (ret < 0) { + FTS_ERROR("write start pram cmd fail"); + return ret; + } + + return 0; +} + +/* + * description: download fw to IC and run + * + * param - buf: const, fw data buffer + * len: length of fw + * + * return 0 if success, otherwise return error code + */ +int fts_fw_write_start(const u8 *buf, u32 len, bool need_reset) +{ + int ret = 0; + u16 ecc_in_host = 0; + u16 ecc_in_tp = 0; + u32 fw_start_addr = 0; + u32 fw_len = 0; + u16 code_len = 0; + u16 code_len_n = 0; + + FTS_INFO("begin to write and start fw(bin len:%d)", len); + /* get app length */ + code_len = ((u16)buf[FTS_APP_INFO_OFFSET + 0] << 8) + + buf[FTS_APP_INFO_OFFSET + 1]; + code_len_n = ((u16)buf[FTS_APP_INFO_OFFSET + 2] << 8) + + buf[FTS_APP_INFO_OFFSET + 3]; + if ((code_len + code_len_n) != 0xFFFF) { + FTS_ERROR("code len(%x %x) fail", code_len, code_len_n); + return -EINVAL; + } + + fw_len = (u32)code_len; + if ((fw_len < FTS_MIN_LEN) || (fw_len > FTS_MAX_LEN_APP)) { + FTS_ERROR("fw length(%d) is invalid", fw_len); + return -EINVAL; + } + + FTS_INFO("fw length in fact:%d", fw_len); + fts_data->fw_is_running = false; + + if (need_reset) { + /* enter into boot environment */ + ret = fts_enter_into_boot(); + if (ret < 0) { + FTS_ERROR("enter into boot environment fail"); + return ret; + } + } + + /* write pram */ + ret = fts_pram_write(fw_start_addr, buf, fw_len); + if (ret < 0) { + FTS_ERROR("write pram fail"); + return ret; + } + + /* ecc check */ + ret = fts_ecc_cal_host(buf, fw_len, &ecc_in_host); + if (ret < 0) { + FTS_ERROR("ecc in host calc fail"); + return ret; + } + + ret = fts_ecc_cal_tp(fw_start_addr, fw_len, &ecc_in_tp); + if (ret < 0) { + FTS_ERROR("ecc in tp calc fail"); + return ret; + } + + FTS_INFO("ecc in tp:%04x host:%04x", ecc_in_tp, ecc_in_host); + if (ecc_in_tp != ecc_in_host) { + FTS_ERROR("ecc check fail"); + return -EIO; + } + + /* remap pram and run fw */ + ret = fts_pram_start(); + if (ret < 0) { + FTS_ERROR("pram start fail"); + return ret; + } + + fts_data->fw_is_running = true; + FTS_INFO("fw download successfully"); + return 0; +} + diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_flash_ft8756.c b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_flash_ft8756.c new file mode 100644 index 000000000000..3f4b7f182d82 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_flash_ft8756.c @@ -0,0 +1,448 @@ +/* + * + * 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: 2017-12-06 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_core.h" +#include "focaltech_flash.h" + +/***************************************************************************** +* Static variables +*****************************************************************************/ +#define FTS_READ_BOOT_ID_TIMEOUT 3 +#define FTS_FLASH_PACKET_LENGTH_SPI (32 * 1024 - 16) + +#define FTS_CMD_ECC_LENGTH_MAX 32766 +#define FTS_ROMBOOT_CMD_ECC_FINISH_OK 0xA5 + + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +static int fts_fwupg_hardware_reset_to_boot(void) +{ + fts_reset_proc(0); + mdelay(8); + + return 0; +} + +static int fts_enter_into_boot(void) +{ + int ret = 0; + int i = 0; + int j = 0; + u8 cmd[2] = { 0 }; + + FTS_INFO("enter into boot environment"); + for (i = 0; i < FTS_UPGRADE_LOOP; i++) { + /* hardware tp reset to boot */ + fts_fwupg_hardware_reset_to_boot(); + + /* enter into boot & check boot id*/ + for (j = 0; j < FTS_READ_BOOT_ID_TIMEOUT; j++) { + cmd[0] = FTS_CMD_START1; + ret = fts_write(cmd, 1); + if (ret >= 0) { + mdelay(8); + ret = fts_check_bootid(); + if (0 == ret) { + FTS_INFO("boot id check pass, retry=%d", i); + return 0; + } + } + } + } + + return -EIO; +} + + +static int fts_dpram_write(u32 saddr, const u8 *buf, u32 len, bool wpram) +{ + int ret = 0; + int i = 0; + int j = 0; + u8 *cmd = NULL; + u32 addr = 0; + u32 baseaddr = wpram ? FTS_PRAM_SADDR : FTS_DRAM_SADDR; + u32 offset = 0; + u32 remainder = 0; + u32 packet_number = 0; + u32 packet_len = 0; + u32 packet_size = FTS_FLASH_PACKET_LENGTH_SPI; + + FTS_INFO("dpram write"); + if (NULL == buf) { + FTS_ERROR("fw buf is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > FTS_MAX_LEN_APP)) { + FTS_ERROR("fw length(%d) fail", len); + return -EINVAL; + } + + cmd = vmalloc(packet_size + FTS_CMD_WRITE_LEN); + if (NULL == cmd) { + FTS_ERROR("malloc memory for pram write buffer fail"); + return -ENOMEM; + } + memset(cmd, 0, packet_size + FTS_CMD_WRITE_LEN); + + packet_number = len / packet_size; + remainder = len % packet_size; + if (remainder > 0) + packet_number++; + packet_len = packet_size; + FTS_INFO("write data, num:%d remainder:%d", packet_number, remainder); + + for (i = 0; i < packet_number; i++) { + offset = i * packet_size; + addr = saddr + offset + baseaddr; + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + /* set pram address */ + cmd[0] = FTS_ROMBOOT_CMD_SET_PRAM_ADDR; + cmd[1] = BYTE_OFF_16(addr); + cmd[2] = BYTE_OFF_8(addr); + cmd[3] = BYTE_OFF_0(addr); + ret = fts_write(&cmd[0], FTS_ROMBOOT_CMD_SET_PRAM_ADDR_LEN); + if (ret < 0) { + FTS_ERROR("set pram(%d) addr(%d) fail", i, addr); + goto write_pram_err; + } + + /* write pram data */ + cmd[0] = FTS_ROMBOOT_CMD_WRITE; + for (j = 0; j < packet_len; j++) { + cmd[1 + j] = buf[offset + j]; + } + ret = fts_write(&cmd[0], 1 + packet_len); + if (ret < 0) { + FTS_ERROR("write fw to pram(%d) fail", i); + goto write_pram_err; + } + } + +write_pram_err: + if (cmd) { + vfree(cmd); + cmd = NULL; + } + return ret; +} + +static int fts_ecc_cal_tp(u32 ecc_saddr, u32 ecc_len, u16 *ecc_value) +{ + int ret = 0; + int i = 0; + u8 cmd[FTS_ROMBOOT_CMD_ECC_NEW_LEN] = { 0 }; + u8 value[2] = { 0 }; + + FTS_INFO("ecc calc in tp"); + cmd[0] = FTS_ROMBOOT_CMD_ECC; + cmd[1] = BYTE_OFF_16(ecc_saddr); + cmd[2] = BYTE_OFF_8(ecc_saddr); + cmd[3] = BYTE_OFF_0(ecc_saddr); + cmd[4] = BYTE_OFF_16(ecc_len); + cmd[5] = BYTE_OFF_8(ecc_len); + cmd[6] = BYTE_OFF_0(ecc_len); + + /* make boot to calculate ecc in pram */ + ret = fts_write(cmd, FTS_ROMBOOT_CMD_ECC_NEW_LEN); + if (ret < 0) { + FTS_ERROR("ecc calc cmd fail"); + return ret; + } + mdelay(2); + + /* wait boot calculate ecc finish */ + cmd[0] = FTS_ROMBOOT_CMD_ECC_FINISH; + for (i = 0; i < FTS_ECC_FINISH_TIMEOUT; i++) { + ret = fts_read(cmd, 1, value, 1); + if (ret < 0) { + FTS_ERROR("ecc finish cmd fail"); + return ret; + } + if (FTS_ROMBOOT_CMD_ECC_FINISH_OK == value[0]) + break; + mdelay(1); + } + if (i >= FTS_ECC_FINISH_TIMEOUT) { + FTS_ERROR("wait ecc finish timeout,ecc_finish=%x", value[0]); + return -EIO; + } + + /* get ecc value calculate in boot */ + cmd[0] = FTS_ROMBOOT_CMD_ECC_READ; + ret = fts_read(cmd, 1, value, 2); + if (ret < 0) { + FTS_ERROR("ecc read cmd fail"); + return ret; + } + + *ecc_value = ((u16)(value[0] << 8) + value[1]) & 0x0000FFFF; + return 0; +} + +static int fts_ecc_cal_host(const u8 *data, u32 data_len, u16 *ecc_value) +{ + u16 ecc = 0; + u16 i = 0; + u16 j = 0; + u16 al2_fcs_coef = AL2_FCS_COEF; + + for (i = 0; i < data_len; i += 2 ) { + ecc ^= ((data[i] << 8) | (data[i + 1])); + for (j = 0; j < 16; j ++) { + if (ecc & 0x01) + ecc = (u16)((ecc >> 1) ^ al2_fcs_coef); + else + ecc >>= 1; + } + } + + *ecc_value = ecc & 0x0000FFFF; + return 0; +} + +static int fts_ecc_check(const u8 *buf, u32 len, u32 ecc_saddr) +{ + int ret = 0; + int i = 0; + u16 ecc_in_host = 0; + u16 ecc_in_tp = 0; + int packet_length = 0; + int packet_number = 0; + int packet_remainder = 0; + int offset = 0; + u32 packet_size = FTS_CMD_ECC_LENGTH_MAX; + + packet_number = len / packet_size; + packet_remainder = len % packet_size; + if (packet_remainder) + packet_number++; + packet_length = packet_size; + + for (i = 0; i < packet_number; i++) { + /* last packet */ + if ((i == (packet_number - 1)) && packet_remainder) + packet_length = packet_remainder; + + ret = fts_ecc_cal_host(buf + offset, packet_length, &ecc_in_host); + if (ret < 0) { + FTS_ERROR("ecc in host calc fail"); + return ret; + } + + ret = fts_ecc_cal_tp(ecc_saddr + offset, packet_length, &ecc_in_tp); + if (ret < 0) { + FTS_ERROR("ecc in tp calc fail"); + return ret; + } + + FTS_DEBUG("ecc in tp:%04x,host:%04x,i:%d", ecc_in_tp, ecc_in_host, i); + if (ecc_in_tp != ecc_in_host) { + FTS_ERROR("ecc_in_tp(%x) != ecc_in_host(%x), ecc check fail", + ecc_in_tp, ecc_in_host); + return -EIO; + } + + offset += packet_length; + } + + return 0; +} + +static int fts_pram_write_ecc(const u8 *buf, u32 len) +{ + int ret = 0; + u32 pram_app_size = 0; + u16 code_len = 0; + u16 code_len_n = 0; + u32 pram_start_addr = 0; + + FTS_INFO("begin to write pram app(bin len:%d)", len); + /* get pram app length */ + code_len = ((u16)buf[FTS_APP_INFO_OFFSET + 0] << 8) + + buf[FTS_APP_INFO_OFFSET + 1]; + code_len_n = ((u16)buf[FTS_APP_INFO_OFFSET + 2] << 8) + + buf[FTS_APP_INFO_OFFSET + 3]; + if ((code_len + code_len_n) != 0xFFFF) { + FTS_ERROR("pram code len(%x %x) fail", code_len, code_len_n); + return -EINVAL; + } + + pram_app_size = (u32)code_len; + pram_app_size = pram_app_size * 2; + if ((pram_app_size < FTS_MIN_LEN) || (pram_app_size > FTS_MAX_LEN_APP)) { + FTS_ERROR("pram app length(%d) is invalid", pram_app_size); + return -EINVAL; + } + + FTS_INFO("pram app length in fact:%d", pram_app_size); + /* write pram */ + ret = fts_dpram_write(pram_start_addr, buf, pram_app_size, true); + if (ret < 0) { + FTS_ERROR("write pram fail"); + return ret; + } + + /* check ecc */ + ret = fts_ecc_check(buf, pram_app_size, pram_start_addr); + if (ret < 0) { + FTS_ERROR("pram ecc check fail"); + return ret; + } + + FTS_INFO("pram app write successfully"); + return 0; +} + +static int fts_dram_write_ecc(const u8 *buf, u32 len) +{ + int ret = 0; + u32 dram_size = 0; + u32 pram_app_size = 0; + u32 dram_start_addr = 0; + u16 const_len = 0; + u16 const_len_n = 0; + const u8 *dram_buf = NULL; + + FTS_INFO("begin to write dram data(bin len:%d)", len); + /* get dram data length */ + const_len = ((u16)buf[FTS_APP_INFO_OFFSET + 0x8] << 8) + + buf[FTS_APP_INFO_OFFSET + 0x9]; + const_len_n = ((u16)buf[FTS_APP_INFO_OFFSET + 0x0A] << 8) + + buf[FTS_APP_INFO_OFFSET + 0x0B]; + if (((const_len + const_len_n) != 0xFFFF) || (const_len == 0)) { + FTS_INFO("no support dram,const len(%x %x)", const_len, const_len_n); + return 0; + } + + dram_size = ((u32)const_len) * 2; + if ((dram_size <= 0) || (dram_size > FTS_MAX_LEN_APP_PARAMS)) { + FTS_ERROR("dram data length(%d) is invalid", dram_size); + return -EINVAL; + } + + pram_app_size = ((u32)(((u16)buf[FTS_APP_INFO_OFFSET + 0] << 8) + + buf[FTS_APP_INFO_OFFSET + 1])) * 2; + + dram_buf = buf + pram_app_size; + FTS_INFO("dram buf length in fact:%d,offset:%d", dram_size, pram_app_size); + /* write pram */ + ret = fts_dpram_write(dram_start_addr, dram_buf, dram_size, false); + if (ret < 0) { + FTS_ERROR("write dram fail"); + return ret; + } + + /* check ecc */ + ret = fts_ecc_check(dram_buf, dram_size, dram_start_addr); + if (ret < 0) { + FTS_ERROR("dram ecc check fail"); + return ret; + } + + FTS_INFO("dram data write successfully"); + return 0; +} + +static int fts_pram_start(void) +{ + int ret = 0; + u8 cmd = FTS_ROMBOOT_CMD_START_APP; + + FTS_INFO("remap to start pram"); + ret = fts_write(&cmd, 1); + if (ret < 0) { + FTS_ERROR("write start pram cmd fail"); + return ret; + } + + return 0; +} + +/* + * description: download fw to IC and run + * + * param - buf: const, fw data buffer + * len: length of fw + * + * return 0 if success, otherwise return error code + */ +int fts_fw_write_start(const u8 *buf, u32 len, bool need_reset) +{ + int ret = 0; + + FTS_INFO("begin to write and start fw(bin len:%d)", len); + fts_data->fw_is_running = false; + + if (need_reset) { + /* enter into boot environment */ + ret = fts_enter_into_boot(); + if (ret < 0) { + FTS_ERROR("enter into boot environment fail"); + return ret; + } + } + + /* write pram */ + ret = fts_pram_write_ecc(buf, len); + if (ret < 0) { + FTS_ERROR("write pram fail"); + return ret; + } + + /* write dram */ + ret = fts_dram_write_ecc(buf, len); + if (ret < 0) { + FTS_ERROR("write dram fail"); + return ret; + } + + /* remap pram and run fw */ + ret = fts_pram_start(); + if (ret < 0) { + FTS_ERROR("pram start fail"); + return ret; + } + + fts_data->fw_is_running = true; + FTS_INFO("fw download successfully"); + return 0; +} + diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_gesture.c b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_gesture.c new file mode 100755 index 000000000000..4775b8b4af57 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_gesture.c @@ -0,0 +1,685 @@ +/* + * + * 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" +#if FTS_GESTURE_EN +/****************************************************************************** +* 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 + +#ifdef FOCALTECH_SENSOR_EN +#define REPORT_MAX_COUNT 10000 +#endif + +/***************************************************************************** +* 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]; + u8 mode; + u8 active; +}; + +/***************************************************************************** +* Static variables +*****************************************************************************/ +static struct fts_gesture_st fts_gesture_data; +#ifdef FOCALTECH_SENSOR_EN +#ifdef CONFIG_HAS_WAKELOCK +static struct wake_lock gesture_wakelock; +#else +static struct wakeup_source *gesture_wakelock; +#endif +static struct sensors_classdev __maybe_unused sensors_touch_cdev = { + .name = "dt-gesture", + .vendor = "Focaltech", + .version = 1, + .type = SENSOR_TYPE_MOTO_DOUBLE_TAP, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + /* WAKE_UP & SPECIAL_REPORT */ + .flags = 1 | 6, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#endif + +/***************************************************************************** +* 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 input_dev *input_dev = fts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_read_reg(FTS_REG_GESTURE_EN, &val); + count = snprintf(buf, PAGE_SIZE, "Gesture Mode:%s\n", + fts_gesture_data.mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "Reg(0xD0)=%d\n", val); + mutex_unlock(&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 input_dev *input_dev = fts_data->input_dev; + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_DEBUG("enable gesture"); + fts_gesture_data.mode = ENABLE; + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_DEBUG("disable gesture"); + fts_gesture_data.mode = DISABLE; + } + mutex_unlock(&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, +}; + +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; + int ret = 0; +#ifdef FOCALTECH_SENSOR_EN + static int report_cnt = 0; +#endif + + 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_GESTURE_U; + 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); +#ifdef FOCALTECH_SENSOR_EN +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + if (!(fts_data->wakeable && fts_data->should_enable_gesture)) { + FTS_INFO("Gesture got but wakeable not set. Skip this gesture."); + return; + } + if (fts_data->pdata->report_gesture_key) { + input_report_key(fts_data->sensor_pdata->input_sensor_dev, KEY_F1, 1); + input_sync(fts_data->sensor_pdata->input_sensor_dev); + input_report_key(fts_data->sensor_pdata->input_sensor_dev, KEY_F1, 0); + input_sync(fts_data->sensor_pdata->input_sensor_dev); + ++report_cnt; + } else { + input_report_abs(fts_data->sensor_pdata->input_sensor_dev, + ABS_DISTANCE, ++report_cnt); + input_sync(fts_data->sensor_pdata->input_sensor_dev); + } +#else + if (!fts_data->wakeable) { + FTS_INFO("Gesture got but wakeable not set. Skip this gesture."); + return; + } + /* report single tap */ + if (gesture == KEY_GESTURE_U) { + if (fts_data->imports && fts_data->imports->report_gesture) { + struct gesture_event_data event; + + FTS_INFO("invoke imported report gesture function\n"); + event.evcode = 1; + /* call class method */ + ret = fts_data->imports->report_gesture(&event); + ++report_cnt; + } + } +#endif + FTS_INFO("input report: %d", report_cnt); + if (report_cnt >= REPORT_MAX_COUNT) + report_cnt = 0; + + if (!ret) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&gesture_wakelock, msecs_to_jiffies(5000)); +#else + PM_WAKEUP_EVENT(gesture_wakelock, 5000); +#endif + } +#else + input_report_key(input_dev, gesture, 1); + input_sync(input_dev); + input_report_key(input_dev, gesture, 0); + input_sync(input_dev); +#endif + } +} + +#ifdef FOCALTECH_SENSOR_EN +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +static int fts_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + FTS_INFO("Gesture set enable %d!", enable); + mutex_lock(&fts_data->state_mutex); + if (enable == 1) + fts_data->should_enable_gesture = true; + else if (enable == 0) + fts_data->should_enable_gesture = false; + else + FTS_INFO("unknown enable symbol\n"); + + mutex_unlock(&fts_data->state_mutex); + return 0; +} + +static int fts_sensor_init(struct fts_ts_data *data) +{ + struct focaltech_sensor_platform_data *sensor_pdata; + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + FTS_ERROR("Failed to allocate device"); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct focaltech_sensor_platform_data), + GFP_KERNEL); + if (!sensor_pdata) { + FTS_ERROR("Failed to allocate memory"); + goto free_sensor_pdata; + } + data->sensor_pdata = sensor_pdata; + + if (data->pdata->report_gesture_key) { + __set_bit(EV_KEY, sensor_input_dev->evbit); + __set_bit(KEY_F1, sensor_input_dev->keybit); + } + else { + __set_bit(EV_ABS, sensor_input_dev->evbit); + input_set_abs_params(sensor_input_dev, ABS_DISTANCE, + 0, REPORT_MAX_COUNT, 0, 0); + } + __set_bit(EV_SYN, sensor_input_dev->evbit); + + sensor_input_dev->name = "double-tap"; + data->sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + FTS_ERROR("Unable to register device, err=%d", err); + goto free_sensor_input_dev; + } + + sensor_pdata->ps_cdev = sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = fts_sensor_set_enable; + sensor_pdata->data = data; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(data->sensor_pdata->input_sensor_dev); +free_sensor_input_dev: + input_free_device(data->sensor_pdata->input_sensor_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, sensor_pdata); + data->sensor_pdata = NULL; +exit: + return 1; +} + +int fts_sensor_remove(struct fts_ts_data *data) +{ + sensors_classdev_unregister(&data->sensor_pdata->ps_cdev); + input_unregister_device(data->sensor_pdata->input_sensor_dev); + devm_kfree(&data->sensor_pdata->input_sensor_dev->dev, + data->sensor_pdata); + data->sensor_pdata = NULL; + data->wakeable = false; + data->should_enable_gesture = false; + return 0; +} +#endif +#endif + +/***************************************************************************** +* 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 || (DISABLE == gesture->mode)) { + return 1; + } + + if (!data) { + FTS_ERROR("gesture data buffer is null"); + ret = -EINVAL; + return ret; + } + + memcpy(buf, data, FTS_GESTURE_DATA_LEN); + if (buf[0] != ENABLE) { + FTS_DEBUG("gesture not enable in fw, don't process gesture"); + return 1; + } + + + /* 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 ((ENABLE == fts_gesture_data.mode) && (ENABLE == fts_gesture_data.active)) { + 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 ret = 0; + int i = 0; + u8 state = 0xFF; + + FTS_INFO("gesture suspend..."); + /* gesture not enable, return immediately */ + if (fts_gesture_data.mode == DISABLE) { + FTS_DEBUG("gesture is disabled"); + return -EINVAL; + } + + 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("Enter into gesture(suspend) fail"); + fts_gesture_data.active = DISABLE; + return -EIO; + } + + ret = enable_irq_wake(ts_data->irq); + if (ret) { + FTS_DEBUG("enable_irq_wake(irq:%d) fail", ts_data->irq); + } + + fts_gesture_data.active = ENABLE; + FTS_INFO("Enter into gesture(suspend) successfully!"); + return 0; +} + +int fts_gesture_resume(struct fts_ts_data *ts_data) +{ + int ret = 0; + int i = 0; + u8 state = 0xFF; + + FTS_INFO("gesture resume..."); + /* gesture not enable, return immediately */ + if (fts_gesture_data.mode == DISABLE) { + FTS_DEBUG("gesture is disabled"); + return -EINVAL; + } + + if (fts_gesture_data.active == DISABLE) { + FTS_DEBUG("gesture active is disable, return immediately"); + return -EINVAL; + } + + fts_gesture_data.active = DISABLE; + 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("exit gesture(resume) fail"); + return -EIO; + } + + ret = disable_irq_wake(ts_data->irq); + if (ret) { + FTS_DEBUG("disable_irq_wake(irq:%d) fail", ts_data->irq); + } + + FTS_INFO("resume from gesture successfully"); + return 0; +} + +int fts_gesture_init(struct fts_ts_data *ts_data) +{ + struct input_dev *input_dev = ts_data->input_dev; +#ifdef FOCALTECH_SENSOR_EN + static bool initialized_sensor; +#endif + + 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); + +#ifdef FOCALTECH_SENSOR_EN + if (!initialized_sensor) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&gesture_wakelock, WAKE_LOCK_SUSPEND, "poll-wake-lock"); +#else + PM_WAKEUP_REGISTER(ts_data->dev, gesture_wakelock, "poll-wake-lock"); + if (!gesture_wakelock) { + FTS_ERROR("failed to allocate wakeup source\n"); + return -ENOMEM; + } +#endif +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + if (!fts_sensor_init(ts_data)) +#endif + initialized_sensor = true; + + } +#endif + + fts_create_gesture_sysfs(ts_data->dev); + + memset(&fts_gesture_data, 0, sizeof(struct fts_gesture_st)); + fts_gesture_data.mode = ENABLE; + fts_gesture_data.active = DISABLE; + + 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; +} +#endif diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_point_report_check.c b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_point_report_check.c new file mode 100755 index 000000000000..1296af1551db --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/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 */ + diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_spi_v1_1.c b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_spi_v1_1.c new file mode 100644 index 000000000000..b0c9c97bc61c --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_spi_v1_1.c @@ -0,0 +1,480 @@ +/* + * + * 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_spi.c +* +* Author: FocalTech Driver Team +* +* Created: 2018-11-17 +* +* Abstract: spi communication with TP +* +* Version: v1.1 +* +* Revision History: +* +************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define STATUS_PACKAGE 0x05 +#define COMMAND_PACKAGE 0xC0 +#define DATA_PACKAGE 0x3F +#define BUSY_QUERY_TIMEOUT 100 +#define BUSY_QUERY_DELAY 150 /* unit: us */ +#define CS_HIGH_DELAY 150 /* unit: us */ +#define DELAY_AFTER_FIRST_BYTE 30 +#define SPI_HEADER_LENGTH 4 +#define SPI_BUF_LENGTH 256 + +#define DATA_CRC_EN 0x20 +#define WRITE_CMD 0x00 +#define READ_CMD (0x80 | DATA_CRC_EN) + +#define CD_PACKAGE_BUFLEN 1 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ + +/***************************************************************************** +* Static variables +*****************************************************************************/ + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* 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[2]; + + if (!spi || !tx_buf || !rx_buf || !len) { + FTS_ERROR("spi_device/tx_buf/rx_buf/len(%d) is invalid", len); + return -EINVAL; + } + + memset(&xfer[0], 0, sizeof(struct spi_transfer)); + memset(&xfer[1], 0, sizeof(struct spi_transfer)); + + spi_message_init(&msg); + xfer[0].tx_buf = &tx_buf[0]; + xfer[0].rx_buf = &rx_buf[0]; + xfer[0].len = 1; + xfer[0].delay_usecs = DELAY_AFTER_FIRST_BYTE; + spi_message_add_tail(&xfer[0], &msg); + + if (len > CD_PACKAGE_BUFLEN) { + xfer[1].tx_buf = &tx_buf[CD_PACKAGE_BUFLEN]; + xfer[1].rx_buf = &rx_buf[CD_PACKAGE_BUFLEN]; + xfer[1].len = len - CD_PACKAGE_BUFLEN; + spi_message_add_tail(&xfer[1], &msg); + } + + ret = spi_sync(spi, &msg); + if (ret) { + FTS_ERROR("spi_sync fail,ret:%d", ret); + return ret; + } + + udelay(CS_HIGH_DELAY); + return ret; +} + +static void crckermit(u8 *data, u16 len, u16 *crc_out) +{ + u16 i = 0; + u16 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_wait_idle(void) +{ + int ret = 0; + int i = 0; + int status = 0xFF; + int idle_status = 0x01; + int status_mask = 0x81; + int retry_timeout = BUSY_QUERY_TIMEOUT; + u8 *txbuf = fts_data->bus_tx_buf; + u8 *rxbuf = fts_data->bus_rx_buf; + u32 txlen = 0; + + if (!fts_data->fw_is_running) + idle_status = 0x00; + + memset(txbuf, 0x00, SPI_BUF_LENGTH); + memset(rxbuf, 0x00, SPI_BUF_LENGTH); + txbuf[0] = STATUS_PACKAGE; + txlen = CD_PACKAGE_BUFLEN + 1; + for (i = 0; i < retry_timeout; i++) { + udelay(BUSY_QUERY_DELAY); + ret = fts_spi_transfer(txbuf, rxbuf, txlen); + if (ret >= 0) { + status = (int)rxbuf[CD_PACKAGE_BUFLEN]; + if ((status & status_mask) == idle_status) { + break; + } + } + } + + if (i >= retry_timeout) { + FTS_ERROR("spi is busy, status:0x%x", status); + return -EIO; + } + + return (int)status; +} + +static int fts_cmdpkg_wirte(u8 ctrl, u8 *cmd, u32 cmdlen) +{ + int i = 0; + u8 *txbuf = fts_data->bus_tx_buf; + u8 *rxbuf = fts_data->bus_rx_buf; + u32 txlen = 0; + + if ((!cmd) || (!cmdlen) + || (cmdlen > (FTX_MAX_COMMMAND_LENGTH - SPI_HEADER_LENGTH))) { + FTS_ERROR("cmd/cmdlen(%d) is invalid", cmdlen); + return -EINVAL; + } + + txbuf[0] = COMMAND_PACKAGE; + txlen = CD_PACKAGE_BUFLEN; + txbuf[txlen++] = ctrl | (cmdlen & 0x0F); + for (i = 0; i < cmdlen; i++) { + txbuf[txlen++] = cmd[i]; + } + + return fts_spi_transfer(txbuf, rxbuf, txlen); +} + +static int fts_boot_write(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + u8 *txbuf = NULL; + u8 *rxbuf = NULL; + u32 txlen = 0; + u8 tmpcmd[FTX_MAX_COMMMAND_LENGTH] = { 0 }; + + if ((!cmd) || (!cmdlen) + || (cmdlen > (FTX_MAX_COMMMAND_LENGTH - SPI_HEADER_LENGTH))) { + FTS_ERROR("cmd/cmdlen(%d) is invalid", cmdlen); + return -EINVAL; + } + + mutex_lock(&ts_data->bus_lock); + /* wait spi idle */ + ret = fts_wait_idle(); + if (ret < 0) { + FTS_ERROR("wait spi idle fail"); + goto err_boot_write; + } + + /* write cmd */ + memcpy(tmpcmd, cmd, cmdlen); + if (ts_data->fw_is_running && data && datalen) { + tmpcmd[cmdlen++] = (datalen >> 8) & 0xFF; + tmpcmd[cmdlen++] = datalen & 0xFF; + } + ret = fts_cmdpkg_wirte(WRITE_CMD, tmpcmd, cmdlen); + if (ret < 0) { + FTS_ERROR("command package wirte fail"); + goto err_boot_write; + } + + /* have data, transfer data */ + if (data && datalen) { + /* wait spi idle */ + ret = fts_wait_idle(); + if (ret < 0) { + FTS_ERROR("wait spi idle from cmd fail"); + goto err_boot_write; + } + + /* write data */ + if (datalen > (SPI_BUF_LENGTH - SPI_HEADER_LENGTH)) { + txbuf = kzalloc(datalen + SPI_HEADER_LENGTH, GFP_KERNEL); + if (NULL == txbuf) { + FTS_ERROR("txbuf malloc fail"); + ret = -ENOMEM; + goto err_boot_write; + } + + rxbuf = kzalloc(datalen + SPI_HEADER_LENGTH, GFP_KERNEL); + if (NULL == rxbuf) { + FTS_ERROR("rxbuf malloc fail"); + ret = -ENOMEM; + goto err_boot_write; + } + } else { + txbuf = ts_data->bus_tx_buf; + rxbuf = ts_data->bus_rx_buf; + memset(txbuf, 0x00, SPI_BUF_LENGTH); + memset(rxbuf, 0x00, SPI_BUF_LENGTH); + } + + txbuf[0] = DATA_PACKAGE; + txlen = CD_PACKAGE_BUFLEN + datalen; + memcpy(txbuf + CD_PACKAGE_BUFLEN, data, datalen); + + ret = fts_spi_transfer(txbuf, rxbuf, txlen); + if (ret < 0) { + FTS_ERROR("spi_transfer(wirte) fail"); + } + } + +err_boot_write: + if (datalen > (SPI_BUF_LENGTH - SPI_HEADER_LENGTH)) { + if (txbuf) { + kfree(txbuf); + txbuf = NULL; + } + + if (rxbuf) { + kfree(rxbuf); + rxbuf = NULL; + } + } + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +int fts_write(u8 *writebuf, u32 writelen) +{ + u8 *cmd = NULL; + u32 cmdlen = 0; + u8 *data = NULL; + u32 datalen = 0; + + if (!writebuf || !writelen) { + FTS_ERROR("writebuf/len is invalid"); + return -EINVAL; + } + + if (1 == writelen) { + cmd = writebuf; + cmdlen = 1; + data = NULL; + datalen = 0; + } else { + cmd = writebuf; + cmdlen = 1; + if (!fts_data->fw_is_running) { + if ((cmd[0] == 0xAE) || (cmd[0] == 0x85) || (cmd[0] == 0xF2)) { + cmdlen = 6; + } else if (cmd[0] == 0xCC) { + cmdlen = 7; + } + } + data = writebuf + cmdlen; + datalen = writelen - cmdlen; + } + + return fts_boot_write(&cmd[0], cmdlen, data, datalen); +} + +int fts_write_reg(u8 addr, u8 value) +{ + return fts_boot_write(&addr, 1, &value, 1); +} + +int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + u8 *txbuf = NULL; + u8 *rxbuf = NULL; + u32 txlen = 0; + u8 ctrl = READ_CMD; + u8 tmpcmd[FTX_MAX_COMMMAND_LENGTH] = { 0 }; + u8 cmdaddr = -1; + + mutex_lock(&ts_data->bus_lock); + if (cmd && cmdlen) { + /* wait spi idle */ + ret = fts_wait_idle(); + if (ret < 0) { + FTS_ERROR("wait spi idle fail"); + goto boot_read_err; + } + + /* write cmd */ + cmdaddr = cmd[0]; + memcpy(tmpcmd, cmd, cmdlen); + if (ts_data->fw_is_running) { + tmpcmd[cmdlen++] = (datalen >> 8) & 0xFF; + tmpcmd[cmdlen++] = datalen & 0xFF; + } + ret = fts_cmdpkg_wirte(ctrl, tmpcmd, cmdlen); + if (ret < 0) { + FTS_ERROR("command package wirte fail"); + goto boot_read_err; + } + + /* wait spi idle */ + ret = fts_wait_idle(); + if (ret < 0) { + FTS_ERROR("wait spi idle from cmd fail"); + goto boot_read_err; + } + } + + if (data && datalen) { + /* write data */ + if (datalen > (SPI_BUF_LENGTH - SPI_HEADER_LENGTH)) { + txbuf = kzalloc(datalen + SPI_HEADER_LENGTH, GFP_KERNEL); + if (NULL == txbuf) { + FTS_ERROR("txbuf malloc fail"); + ret = -ENOMEM; + goto boot_read_err; + } + + rxbuf = kzalloc(datalen + SPI_HEADER_LENGTH, GFP_KERNEL); + if (NULL == rxbuf) { + FTS_ERROR("rxbuf malloc fail"); + ret = -ENOMEM; + goto boot_read_err; + } + } else { + txbuf = ts_data->bus_tx_buf; + rxbuf = ts_data->bus_rx_buf; + memset(txbuf, 0x00, SPI_BUF_LENGTH); + memset(rxbuf, 0x00, SPI_BUF_LENGTH); + } + + txbuf[0] = DATA_PACKAGE; + txlen = CD_PACKAGE_BUFLEN + datalen; + if (ctrl & DATA_CRC_EN) { + txlen = txlen + 2; + } + ret = fts_spi_transfer(txbuf, rxbuf, txlen); + if (ret < 0) { + FTS_ERROR("spi_transfer(read) fail"); + goto boot_read_err; + } + memcpy(data, rxbuf + CD_PACKAGE_BUFLEN, datalen); + /* crc check */ + if (ctrl & DATA_CRC_EN) { + ret = rdata_check(rxbuf + CD_PACKAGE_BUFLEN, + txlen - CD_PACKAGE_BUFLEN); + if (ret < 0) { + FTS_INFO("read data(addr:%x) crc check incorrect", cmdaddr); + goto boot_read_err; + } + } + } +boot_read_err: + if (datalen > (SPI_BUF_LENGTH - SPI_HEADER_LENGTH)) { + if (txbuf) { + kfree(txbuf); + txbuf = NULL; + } + + if (rxbuf) { + kfree(rxbuf); + rxbuf = NULL; + } + } + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +int fts_read_reg(u8 addr, u8 *value) +{ + return fts_read(&addr, 1, value, 1); +} + +int fts_bus_init(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + ts_data->bus_tx_buf = kzalloc(SPI_BUF_LENGTH, GFP_KERNEL); + if (NULL == ts_data->bus_tx_buf) { + FTS_ERROR("failed to allocate memory for bus_tx_buf"); + return -ENOMEM; + } + + ts_data->bus_rx_buf = kzalloc(SPI_BUF_LENGTH, GFP_KERNEL); + if (NULL == ts_data->bus_rx_buf) { + FTS_ERROR("failed to allocate memory for bus_rx_buf"); + return -ENOMEM; + } + FTS_FUNC_EXIT(); + return 0; +} + +int fts_bus_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; +} diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_spi_v1_1_1.c b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_spi_v1_1_1.c new file mode 100755 index 000000000000..d5d1538aa22b --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_spi_v1_1_1.c @@ -0,0 +1,339 @@ +/* + * + * 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_spi.c +* +* Author: FocalTech Driver Team +* +* Created: 2019-03-21 +* +* Abstract: new spi protocol communication with TP +* +* FocalVersion: v1.0 +* Version: v1.1.1 +* +* Revision History: +* +************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* 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 (SPI_DUMMY_BYTE + 6) +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ + +/***************************************************************************** +* Static variables +*****************************************************************************/ + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* 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, u16 len, u16 *crc_out) +{ + u16 i = 0; + u16 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; +} + +int fts_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; + 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 (NULL == txbuf) { + FTS_ERROR("txbuf malloc fail"); + ret = -ENOMEM; + goto err_write; + } + + rxbuf = kzalloc(txlen_need, GFP_KERNEL); + if (NULL == rxbuf) { + 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 ((0 == ret) && ((rxbuf[3] & 0xA0) == 0)) { + break; + } else { + FTS_DEBUG("data write(status=%x),retry=%d,ret=%d", + rxbuf[3], i, ret); + ret = -EIO; + udelay(CS_HIGH_DELAY); + } + } + +err_write: + if (txlen_need > SPI_BUF_LENGTH) { + if (txbuf) { + kfree(txbuf); + txbuf = NULL; + } + + if (rxbuf) { + kfree(rxbuf); + rxbuf = NULL; + } + } + + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +int fts_write_reg(u8 addr, u8 value) +{ + u8 writebuf[2] = { 0 }; + + writebuf[0] = addr; + writebuf[1] = value; + return fts_write(writebuf, 2); +} + +int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen) +{ + 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 = datalen + SPI_HEADER_LENGTH; + 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 (NULL == txbuf) { + FTS_ERROR("txbuf malloc fail"); + ret = -ENOMEM; + goto err_read; + } + + rxbuf = kzalloc(txlen_need, GFP_KERNEL); + if (NULL == rxbuf) { + 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 ((0 == ret) && ((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_INFO("read data(addr:%x) crc check incorrect", cmd[0]); + goto err_read; + } + } + break; + } else { + FTS_INFO("data read(status=%x),retry=%d,ret=%d", + rxbuf[3], i, ret); + ret = -EIO; + udelay(CS_HIGH_DELAY); + } + } + +err_read: + if (txlen_need > SPI_BUF_LENGTH) { + if (txbuf) { + kfree(txbuf); + txbuf = NULL; + } + + if (rxbuf) { + kfree(rxbuf); + rxbuf = NULL; + } + } + + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +int fts_read_reg(u8 addr, u8 *value) +{ + return fts_read(&addr, 1, value, 1); +} + +int fts_bus_init(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + ts_data->bus_tx_buf = kzalloc(SPI_BUF_LENGTH, GFP_KERNEL); + if (NULL == ts_data->bus_tx_buf) { + FTS_ERROR("failed to allocate memory for bus_tx_buf"); + return -ENOMEM; + } + + ts_data->bus_rx_buf = kzalloc(SPI_BUF_LENGTH, GFP_KERNEL); + if (NULL == ts_data->bus_rx_buf) { + FTS_ERROR("failed to allocate memory for bus_rx_buf"); + return -ENOMEM; + } + FTS_FUNC_EXIT(); + return 0; +} + +int fts_bus_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; +} diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/Makefile b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/Makefile new file mode 100755 index 000000000000..062610eba31a --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_test.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_test_ini.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += supported_ic/ diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/focaltech_test.c b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/focaltech_test.c new file mode 100755 index 000000000000..ebe23b1d73c9 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/focaltech_test.c @@ -0,0 +1,2054 @@ +/* + * + * 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_test.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-01 +* +* Modify: +* +* Abstract: create char device and proc node for the comm between APK and TP +* +************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_test.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +struct fts_test *fts_ftest; + +struct test_funcs *test_func_list[] = { +#ifdef CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8719 + &test_func_ft8719, +#endif +#ifdef CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8006S_AA + &test_func_ft8006sp, +#endif +#if defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8756) || \ + defined (CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8009) + &test_func_ft8756, +#endif +}; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* functions body +*****************************************************************************/ +void sys_delay(int ms) +{ + msleep(ms); +} + +int focal_abs(int value) +{ + if (value < 0) + value = 0 - value; + + return value; +} + +void *fts_malloc(size_t size) +{ + return kzalloc(size, GFP_KERNEL); +} + +void fts_free_proc(void *p) +{ + return kfree(p); +} + +void print_buffer(int *buffer, int length, int line_num) +{ + int i = 0; + int j = 0; + int tmpline = 0; + char *tmpbuf = NULL; + int tmplen = 0; + int cnt = 0; + struct fts_test *tdata = fts_ftest; + + if (tdata && tdata->ts_data && (tdata->ts_data->log_level < 3)) { + return; + } + + if ((NULL == buffer) || (length <= 0)) { + FTS_TEST_DBG("buffer/length(%d) fail", length); + return; + } + + tmpline = line_num ? line_num : length; + tmplen = tmpline * 6 + 128; + tmpbuf = kzalloc(tmplen, GFP_KERNEL); + + for (i = 0; i < length; i = i + tmpline) { + cnt = 0; + for (j = 0; j < tmpline; j++) { + cnt += snprintf(tmpbuf + cnt, tmplen - cnt, "%5d ", buffer[i + j]); + if ((cnt >= tmplen) || ((i + j + 1) >= length)) + break; + } + FTS_TEST_DBG("%s", tmpbuf); + } + + if (tmpbuf) { + kfree(tmpbuf); + tmpbuf = NULL; + } +} + +/******************************************************************** + * test read/write interface + *******************************************************************/ +static int fts_test_bus_read( + u8 *cmd, int cmdlen, u8 *data, int datalen) +{ + int ret = 0; + + ret = fts_read(cmd, cmdlen, data, datalen); + if (ret < 0) + return ret; + else + return 0; +} + +static int fts_test_bus_write(u8 *writebuf, int writelen) +{ + int ret = 0; + + ret = fts_write(writebuf, writelen); + if (ret < 0) + return ret; + else + return 0; +} + +int fts_test_read_reg(u8 addr, u8 *val) +{ + return fts_test_bus_read(&addr, 1, val, 1); +} + +int fts_test_write_reg(u8 addr, u8 val) +{ + int ret; + u8 cmd[2] = {0}; + + cmd[0] = addr; + cmd[1] = val; + ret = fts_test_bus_write(cmd, 2); + + return ret; +} + +int fts_test_read(u8 addr, u8 *readbuf, int readlen) +{ + int ret = 0; + int i = 0; + int packet_length = 0; + int packet_num = 0; + int packet_remainder = 0; + int offset = 0; + int byte_num = readlen; + + packet_num = byte_num / BYTES_PER_TIME; + packet_remainder = byte_num % BYTES_PER_TIME; + if (packet_remainder) + packet_num++; + + if (byte_num < BYTES_PER_TIME) { + packet_length = byte_num; + } else { + packet_length = BYTES_PER_TIME; + } + /* FTS_TEST_DBG("packet num:%d, remainder:%d", packet_num, packet_remainder); */ + + ret = fts_test_bus_read(&addr, 1, &readbuf[offset], packet_length); + if (ret < 0) { + FTS_TEST_ERROR("read buffer fail"); + return ret; + } + for (i = 1; i < packet_num; i++) { + offset += packet_length; + if ((i == (packet_num - 1)) && packet_remainder) { + packet_length = packet_remainder; + } + + ret = fts_test_bus_read(&addr, 1, &readbuf[offset], + packet_length); + + if (ret < 0) { + FTS_TEST_ERROR("read buffer fail"); + return ret; + } + } + + return 0; +} + +int fts_test_write(u8 addr, u8 *writebuf, int writelen) +{ + int ret = 0; + int i = 0; + u8 *data = NULL; + int packet_length = 0; + int packet_num = 0; + int packet_remainder = 0; + int offset = 0; + int byte_num = writelen; + + data = fts_malloc(BYTES_PER_TIME + 1); + if (!data) { + FTS_TEST_ERROR("malloc memory for bus write data fail"); + return -ENOMEM; + } + + packet_num = byte_num / BYTES_PER_TIME; + packet_remainder = byte_num % BYTES_PER_TIME; + if (packet_remainder) + packet_num++; + + if (byte_num < BYTES_PER_TIME) { + packet_length = byte_num; + } else { + packet_length = BYTES_PER_TIME; + } + /* FTS_TEST_DBG("packet num:%d, remainder:%d", packet_num, packet_remainder); */ + + data[0] = addr; + for (i = 0; i < packet_num; i++) { + if (i != 0) { + data[0] = addr + 1; + } + if ((i == (packet_num - 1)) && packet_remainder) { + packet_length = packet_remainder; + } + memcpy(&data[1], &writebuf[offset], packet_length); + + ret = fts_test_bus_write(data, packet_length + 1); + if (ret < 0) { + FTS_TEST_ERROR("write buffer fail"); + fts_free(data); + return ret; + } + + offset += packet_length; + } + + fts_free(data); + return 0; +} + +/******************************************************************** + * test global function enter work/factory mode + *******************************************************************/ +int enter_work_mode(void) +{ + int ret = 0; + u8 mode = 0; + int i = 0; + int j = 0; + + FTS_TEST_FUNC_ENTER(); + + ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode); + if ((ret >= 0) && (0x00 == mode)) + return 0; + + for (i = 0; i < ENTER_WORK_FACTORY_RETRIES; i++) { + ret = fts_test_write_reg(DEVIDE_MODE_ADDR, 0x00); + if (ret >= 0) { + sys_delay(FACTORY_TEST_DELAY); + for (j = 0; j < 20; j++) { + ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode); + if ((ret >= 0) && (0x00 == mode)) { + FTS_TEST_INFO("enter work mode success"); + return 0; + } else + sys_delay(FACTORY_TEST_DELAY); + } + } + + sys_delay(50); + } + + if (i >= ENTER_WORK_FACTORY_RETRIES) { + FTS_TEST_ERROR("Enter work mode fail"); + return -EIO; + } + + FTS_TEST_FUNC_EXIT(); + return 0; +} + +int enter_factory_mode(void) +{ + int ret = 0; + u8 mode = 0; + int i = 0; + int j = 0; + + ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode); + if ((ret >= 0) && (0x40 == mode)) + return 0; + + for (i = 0; i < ENTER_WORK_FACTORY_RETRIES; i++) { + ret = fts_test_write_reg(DEVIDE_MODE_ADDR, 0x40); + if (ret >= 0) { + sys_delay(FACTORY_TEST_DELAY); + for (j = 0; j < 20; j++) { + ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode); + if ((ret >= 0) && (0x40 == mode)) { + FTS_TEST_INFO("enter factory mode success"); + sys_delay(200); + return 0; + } else + sys_delay(FACTORY_TEST_DELAY); + } + } + + sys_delay(50); + } + + if (i >= ENTER_WORK_FACTORY_RETRIES) { + FTS_TEST_ERROR("Enter factory mode fail"); + return -EIO; + } + + return 0; +} + +/* + * read_mass_data - read rawdata/short test data + * addr - register addr which read data from + * byte_num - read data length, unit:byte + * buf - save data + * + * return 0 if read data succuss, otherwise return error code + */ +int read_mass_data(u8 addr, int byte_num, int *buf) +{ + int ret = 0; + int i = 0; + u8 *data = NULL; + + data = (u8 *)fts_malloc(byte_num * sizeof(u8)); + if (NULL == data) { + FTS_TEST_SAVE_ERR("mass data buffer malloc fail\n"); + return -ENOMEM; + } + + /* read rawdata buffer */ + FTS_TEST_INFO("mass data len:%d", byte_num); + ret = fts_test_read(addr, data, byte_num); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read mass data fail\n"); + goto read_massdata_err; + } + + for (i = 0; i < byte_num; i = i + 2) { + buf[i >> 1] = (int)(short)((data[i] << 8) + data[i + 1]); + } + + ret = 0; +read_massdata_err: + fts_free(data); + return ret; +} + +int short_get_adcdata_incell(u8 retval, u8 ch_num, int byte_num, int *adc_buf) +{ + int ret = 0; + int times = 0; + u8 short_state = 0; + + FTS_TEST_FUNC_ENTER(); + + /* Start ADC sample */ + ret = fts_test_write_reg(FACTORY_REG_SHORT_TEST_EN, 0x01); + if (ret) { + FTS_TEST_SAVE_ERR("start short test fail\n"); + goto adc_err; + } + + sys_delay(ch_num * FACTORY_TEST_DELAY); + for (times = 0; times < FACTORY_TEST_RETRY; times++) { + ret = fts_test_read_reg(FACTORY_REG_SHORT_TEST_STATE, &short_state); + if ((ret >= 0) && (retval == short_state)) + break; + else + FTS_TEST_DBG("reg%x=%x,retry:%d", + FACTORY_REG_SHORT_TEST_STATE, short_state, times); + + sys_delay(FACTORY_TEST_RETRY_DELAY); + } + if (times >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("short test timeout, ADC data not OK\n"); + ret = -EIO; + goto adc_err; + } + + ret = read_mass_data(FACTORY_REG_SHORT_ADDR, byte_num, adc_buf); + if (ret) { + FTS_TEST_SAVE_ERR("get short(adc) data fail\n"); + } + +adc_err: + FTS_TEST_FUNC_EXIT(); + return ret; +} + +/* + * wait_state_update - wait fw status update + */ +int wait_state_update(u8 retval) +{ + int ret = 0; + int times = 0; + u8 state = 0xFF; + + while (times++ < FACTORY_TEST_RETRY) { + sys_delay(FACTORY_TEST_DELAY); + /* Wait register status update */ + state = 0xFF; + ret = fts_test_read_reg(FACTORY_REG_PARAM_UPDATE_STATE, &state); + if ((ret >= 0) && (retval == state)) + break; + else + FTS_TEST_DBG("reg%x=%x,retry:%d", \ + FACTORY_REG_PARAM_UPDATE_STATE, state, times); + } + + if (times >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("Wait State Update fail\n"); + return -EIO; + } + + return 0; +} + +/* + * start_scan - start to scan a frame + */ +int start_scan(void) +{ + int ret = 0; + u8 addr = 0; + u8 val = 0; + u8 finish_val = 0; + int times = 0; + struct fts_test *tdata = fts_ftest; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("test/func is null\n"); + return -EINVAL; + } + + if (SCAN_SC == tdata->func->startscan_mode) { + /* sc ic */ + addr = FACTORY_REG_SCAN_ADDR2; + val = 0x01; + finish_val = 0x00; + } else { + addr = DEVIDE_MODE_ADDR; + val = 0xC0; + finish_val = 0x40; + } + + /* write register to start scan */ + ret = fts_test_write_reg(addr, val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write start scan mode fail\n"); + return ret; + } + + /* Wait for the scan to complete */ + while (times++ < FACTORY_TEST_RETRY) { + sys_delay(FACTORY_TEST_DELAY); + + ret = fts_test_read_reg(addr, &val); + if ((ret >= 0) && (val == finish_val)) { + break; + } else + FTS_TEST_DBG("reg%x=%x,retry:%d", addr, val, times); + } + + if (times >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("scan timeout\n"); + return -EIO; + } + + return 0; +} + +static int read_rawdata( + u8 off_addr, + u8 off_val, + u8 rawdata_addr, + int byte_num, + int *data) +{ + int ret = 0; + + /* set line addr or rawdata start addr */ + ret = fts_test_write_reg(off_addr, off_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wirte line/start addr fail\n"); + return ret; + } + + ret = read_mass_data(rawdata_addr, byte_num, data); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read rawdata fail\n"); + return ret; + } + + return 0; +} + +int get_rawdata(int *data) +{ + int ret = 0; + u8 val = 0; + u8 addr = 0; + u8 rawdata_addr = 0; + int byte_num = 0; + struct fts_test *tdata = fts_ftest; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("test/func is null\n"); + return -EINVAL; + } + + /* enter factory mode */ + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("failed to enter factory mode,ret=%d\n", ret); + return ret; + } + + /* start scanning */ + ret = start_scan(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("scan fail\n"); + return ret; + } + + /* read rawdata */ + if (IC_HW_INCELL == tdata->func->hwtype) { + val = 0xAD; + addr = FACTORY_REG_LINE_ADDR; + rawdata_addr = FACTORY_REG_RAWDATA_ADDR; + } else if (IC_HW_MC_SC == tdata->func->hwtype) { + val = 0xAA; + addr = FACTORY_REG_LINE_ADDR; + rawdata_addr = FACTORY_REG_RAWDATA_ADDR_MC_SC; + } else { + val = 0x0; + addr = FACTORY_REG_RAWDATA_SADDR_SC; + rawdata_addr = FACTORY_REG_RAWDATA_ADDR_SC; + } + + byte_num = tdata->node.node_num * 2; + ret = read_rawdata(addr, val, rawdata_addr, byte_num, data); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read rawdata fail\n"); + return ret; + } + + return 0; +} + +/* + * chip_clb - auto clb + */ +int chip_clb(void) +{ + int ret = 0; + u8 val = 0; + int times = 0; + + /* start clb */ + ret = fts_test_write_reg(FACTORY_REG_CLB, 0x04); + if (ret) { + FTS_TEST_SAVE_ERR("write start clb fail\n"); + return ret; + } + + while (times++ < FACTORY_TEST_RETRY) { + sys_delay(FACTORY_TEST_RETRY_DELAY); + ret = fts_test_read_reg(FACTORY_REG_CLB, &val); + if ((0 == ret) && (0x02 == val)) { + /* clb ok */ + break; + } else + FTS_TEST_DBG("reg%x=%x,retry:%d", FACTORY_REG_CLB, val, times); + } + + if (times >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("chip clb timeout\n"); + return -EIO; + } + + return 0; +} + +/* + * get_cb_incell - get cb data for incell IC + */ +int get_cb_incell(u16 saddr, int byte_num, int *cb_buf) +{ + int ret = 0; + int i = 0; + u8 cb_addr = 0; + u8 addr_h = 0; + u8 addr_l = 0; + int read_num = 0; + int packet_num = 0; + int packet_remainder = 0; + int offset = 0; + int addr = 0; + u8 *data = NULL; + + data = (u8 *)fts_malloc(byte_num * sizeof(u8)); + if (NULL == data) { + FTS_TEST_SAVE_ERR("cb buffer malloc fail\n"); + return -ENOMEM; + } + + packet_num = byte_num / BYTES_PER_TIME; + packet_remainder = byte_num % BYTES_PER_TIME; + if (packet_remainder) + packet_num++; + read_num = BYTES_PER_TIME; + + FTS_TEST_INFO("cb packet:%d,remainder:%d", packet_num, packet_remainder); + cb_addr = FACTORY_REG_CB_ADDR; + for (i = 0; i < packet_num; i++) { + offset = read_num * i; + addr = saddr + offset; + addr_h = (addr >> 8) & 0xFF; + addr_l = addr & 0xFF; + if ((i == (packet_num - 1)) && packet_remainder) { + read_num = packet_remainder; + } + + ret = fts_test_write_reg(FACTORY_REG_CB_ADDR_H, addr_h); + if (ret) { + FTS_TEST_SAVE_ERR("write cb addr high fail\n"); + goto TEST_CB_ERR; + } + ret = fts_test_write_reg(FACTORY_REG_CB_ADDR_L, addr_l); + if (ret) { + FTS_TEST_SAVE_ERR("write cb addr low fail\n"); + goto TEST_CB_ERR; + } + + ret = fts_test_read(cb_addr, data + offset, read_num); + if (ret) { + FTS_TEST_SAVE_ERR("read cb fail\n"); + goto TEST_CB_ERR; + } + } + + for (i = 0; i < byte_num; i++) { + cb_buf[i] = data[i]; + } + +TEST_CB_ERR: + fts_free(data); + return ret; +} + +int get_cb_sc(int byte_num, int *cb_buf, enum byte_mode mode) +{ + int ret = 0; + int i = 0; + int read_num = 0; + int packet_num = 0; + int packet_remainder = 0; + int offset = 0; + u8 cb_addr = 0; + u8 off_addr = 0; + struct fts_test *tdata = fts_ftest; + u8 *cb = NULL; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("test/func is null\n"); + return -EINVAL; + } + + cb = (u8 *)fts_malloc(byte_num * sizeof(u8)); + if (NULL == cb) { + FTS_TEST_SAVE_ERR("malloc memory for cb buffer fail\n"); + return -ENOMEM; + } + + if (IC_HW_MC_SC == tdata->func->hwtype) { + cb_addr = FACTORY_REG_MC_SC_CB_ADDR; + off_addr = FACTORY_REG_MC_SC_CB_ADDR_OFF; + } else if (IC_HW_SC == tdata->func->hwtype) { + cb_addr = FACTORY_REG_SC_CB_ADDR; + off_addr = FACTORY_REG_SC_CB_ADDR_OFF; + } + + packet_num = byte_num / BYTES_PER_TIME; + packet_remainder = byte_num % BYTES_PER_TIME; + if (packet_remainder) + packet_num++; + read_num = BYTES_PER_TIME; + offset = 0; + + FTS_TEST_INFO("cb packet:%d,remainder:%d", packet_num, packet_remainder); + for (i = 0; i < packet_num; i++) { + if ((i == (packet_num - 1)) && packet_remainder) { + read_num = packet_remainder; + } + + ret = fts_test_write_reg(off_addr, offset); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write cb addr offset fail\n"); + goto cb_err; + } + + ret = fts_test_read(cb_addr, cb + offset, read_num); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read cb fail\n"); + goto cb_err; + } + + offset += read_num; + } + + if (DATA_ONE_BYTE == mode) { + for (i = 0; i < byte_num; i++) { + cb_buf[i] = cb[i]; + } + } else if (DATA_TWO_BYTE == mode) { + for (i = 0; i < byte_num; i = i + 2) { + cb_buf[i >> 1] = (int)(((int)(cb[i]) << 8) + cb[i + 1]); + } + } + + ret = 0; +cb_err: + fts_free(cb); + return ret; +} + +bool compare_data(int *data, int min, int max, int min_vk, int max_vk, bool key) +{ + int i = 0; + bool result = true; + struct fts_test *tdata = fts_ftest; + int rx = tdata->node.rx_num; + int node_va = tdata->node.node_num - tdata->node.key_num; + + if (!data || !tdata->node_valid) { + FTS_TEST_SAVE_ERR("data/node_valid is null\n"); + return false; + } + + for (i = 0; i < node_va; i++) { + if (0 == tdata->node_valid[i]) + continue; + + if ((data[i] < min) || (data[i] > max)) { + FTS_TEST_SAVE_ERR("test fail,node(%4d,%4d)=%5d,range=(%5d,%5d)\n", + i / rx + 1, i % rx + 1, data[i], min, max); + result = false; + } + } + + if (key) { + for (i = node_va; i < tdata->node.node_num; i++) { + if (0 == tdata->node_valid[i]) + continue; + + if ((data[i] < min_vk) || (data[i] > max_vk)) { + FTS_TEST_SAVE_ERR("test fail,node(%4d,%4d)=%5d,range=(%5d,%5d)\n", + i / rx + 1, i % rx + 1, + data[i], min_vk, max_vk); + result = false; + } + } + } + + return result; +} + +bool compare_array(int *data, int *min, int *max, bool key) +{ + int i = 0; + bool result = true; + struct fts_test *tdata = fts_ftest; + int rx = tdata->node.rx_num; + int node_num = tdata->node.node_num; + + if (!data || !min || !max || !tdata->node_valid) { + FTS_TEST_SAVE_ERR("data/min/max/node_valid is null\n"); + return false; + } + + if (!key) { + node_num -= tdata->node.key_num; + } + for (i = 0; i < node_num; i++) { + if (0 == tdata->node_valid[i]) + continue; + + if ((data[i] < min[i]) || (data[i] > max[i])) { + FTS_TEST_SAVE_ERR("test fail,node(%4d,%4d)=%5d,range=(%5d,%5d)\n", + i / rx + 1, i % rx + 1, data[i], min[i], max[i]); + result = false; + } + } + + return result; +} + +/* + * show_data - show and save test data to testresult.txt + */ +void show_data(int *data, bool key) +{ +#if TXT_SUPPORT + int i = 0; + int j = 0; + struct fts_test *tdata = fts_ftest; + int node_num = tdata->node.node_num; + int tx_num = tdata->node.tx_num; + int rx_num = tdata->node.rx_num; + + FTS_TEST_FUNC_ENTER(); + for (i = 0; i < tx_num; i++) { + FTS_TEST_SAVE_INFO("Ch/Tx_%02d: ", i + 1); + for (j = 0; j < rx_num; j++) { + FTS_TEST_SAVE_INFO("%5d, ", data[i * rx_num + j]); + } + FTS_TEST_SAVE_INFO("\n"); + } + + if (key) { + FTS_TEST_SAVE_INFO("Ch/Tx_%02d: ", tx_num + 1); + for (i = tx_num * rx_num; i < node_num; i++) { + FTS_TEST_SAVE_INFO("%5d, ", data[i]); + } + FTS_TEST_SAVE_INFO("\n"); + } + FTS_TEST_FUNC_EXIT(); +#endif +} + +/* mc_sc only */ +/* Only V3 Pattern has mapping & no-mapping */ +int mapping_switch(u8 mapping) +{ + int ret = 0; + u8 val = 0xFF; + struct fts_test *tdata = fts_ftest; + + if (tdata->v3_pattern) { + ret = fts_test_read_reg(FACTORY_REG_NOMAPPING, &val); + if (ret < 0) { + FTS_TEST_ERROR("read 0x54 register fail"); + return ret; + } + + if (val != mapping) { + ret = fts_test_write_reg(FACTORY_REG_NOMAPPING, mapping); + if (ret < 0) { + FTS_TEST_ERROR("write 0x54 register fail"); + return ret; + } + sys_delay(FACTORY_TEST_DELAY); + } + } + + return 0; +} + +bool get_fw_wp(u8 wp_ch_sel, enum wp_type water_proof_type) +{ + bool fw_wp_state = false; + + switch (water_proof_type) { + case WATER_PROOF_ON: + /* bit5: 0-check in wp on, 1-not check */ + fw_wp_state = !(wp_ch_sel & 0x20); + break; + case WATER_PROOF_ON_TX: + /* Bit6: 0-check Rx+Tx in wp mode 1-check one channel + Bit2: 0-check Tx in wp mode; 1-check Rx in wp mode + */ + fw_wp_state = (!(wp_ch_sel & 0x40) || !(wp_ch_sel & 0x04)); + break; + case WATER_PROOF_ON_RX: + fw_wp_state = (!(wp_ch_sel & 0x40) || (wp_ch_sel & 0x04)); + break; + case WATER_PROOF_OFF: + /* bit7: 0-check in wp off, 1-not check */ + fw_wp_state = !(wp_ch_sel & 0x80); + break; + case WATER_PROOF_OFF_TX: + /* Bit1-0: 00-check Tx in non-wp mode + 01-check Rx in non-wp mode + 10:check Rx+Tx in non-wp mode + */ + fw_wp_state = ((0x0 == (wp_ch_sel & 0x03)) || (0x02 == (wp_ch_sel & 0x03))); + break; + case WATER_PROOF_OFF_RX: + fw_wp_state = ((0x01 == (wp_ch_sel & 0x03)) || (0x02 == (wp_ch_sel & 0x03))); + break; + default: + break; + } + + return fw_wp_state; +} + +int get_cb_mc_sc(u8 wp, int byte_num, int *cb_buf, enum byte_mode mode) +{ + int ret = 0; + + /* 1:waterproof 0:non-waterproof */ + ret = fts_test_write_reg(FACTORY_REG_MC_SC_MODE, wp); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get mc_sc mode fail\n"); + return ret; + } + + /* read cb */ + ret = get_cb_sc(byte_num, cb_buf, mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get sc cb fail\n"); + return ret; + } + + return 0; +} + +int get_rawdata_mc_sc(enum wp_type wp, int *data) +{ + int ret = 0; + u8 val = 0; + u8 addr = 0; + u8 rawdata_addr = 0; + int byte_num = 0; + struct fts_test *tdata = fts_ftest; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("test/func is null\n"); + return -EINVAL; + } + + addr = FACTORY_REG_LINE_ADDR; + rawdata_addr = FACTORY_REG_RAWDATA_ADDR_MC_SC; + if (WATER_PROOF_ON == wp) { + val = 0xAC; + } else { + val = 0xAB; + } + + byte_num = tdata->sc_node.node_num * 2; + ret = read_rawdata(addr, val, rawdata_addr, byte_num, data); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read rawdata fail\n"); + return ret; + } + + return 0; +} + +int get_rawdata_mc(u8 fre, u8 fir, int *rawdata) +{ + int ret = 0; + int i = 0; + + if (NULL == rawdata ) { + FTS_TEST_SAVE_ERR("rawdata buffer is null\n"); + return -EINVAL; + } + + /* set frequecy high/low */ + ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, fre); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set frequecy fail,ret=%d\n", ret); + return ret; + } + + /* fir enable/disable */ + ret = fts_test_write_reg(FACTORY_REG_FIR, fir); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set fir fail,ret=%d\n", ret); + return ret; + } + + /* get rawdata */ + for (i = 0; i < 3; i++) { + /* lost 3 frames, in order to obtain stable data */ + ret = get_rawdata(rawdata); + } + if (ret < 0) { + FTS_TEST_SAVE_ERR("get rawdata fail,ret=%d\n", ret); + return ret; + } + + return 0; +} + +int short_get_adc_data_mc(u8 retval, int byte_num, int *adc_buf, u8 mode) +{ + int ret = 0; + int i = 0; + u8 short_state = 0; + + FTS_TEST_FUNC_ENTER(); + /* select short test mode & start test */ + ret = fts_test_write_reg(FACTROY_REG_SHORT_TEST_EN, mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write short test mode fail\n"); + goto test_err; + } + + for (i = 0; i < FACTORY_TEST_RETRY; i++) { + sys_delay(FACTORY_TEST_RETRY_DELAY); + + ret = fts_test_read_reg(FACTROY_REG_SHORT_TEST_EN, &short_state); + if ((ret >= 0) && (retval == short_state)) + break; + else + FTS_TEST_DBG("reg%x=%x,retry:%d", + FACTROY_REG_SHORT_TEST_EN, short_state, i); + } + if (i >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("short test timeout, ADC data not OK\n"); + ret = -EIO; + goto test_err; + } + + ret = read_mass_data(FACTORY_REG_SHORT_ADDR_MC, byte_num, adc_buf); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get short(adc) data fail\n"); + } + + FTS_TEST_DBG("adc data:\n"); + print_buffer(adc_buf, byte_num / 2, 0); +test_err: + FTS_TEST_FUNC_EXIT(); + return ret; +} + +bool compare_mc_sc(bool tx_check, bool rx_check, int *data, int *min, int *max) +{ + int i = 0; + bool result = true; + struct fts_test *tdata = fts_ftest; + + if (rx_check) { + for (i = 0; i < tdata->sc_node.rx_num; i++) { + if (0 == tdata->node_valid_sc[i]) + continue; + + if ((data[i] < min[i]) || (data[i] > max[i])) { + FTS_TEST_SAVE_ERR("test fail,rx%d=%5d,range=(%5d,%5d)\n", + i + 1, data[i], min[i], max[i]); + result = false; + } + } + } + + if (tx_check) { + for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) { + if (0 == tdata->node_valid_sc[i]) + continue; + + if ((data[i] < min[i]) || (data[i] > max[i])) { + FTS_TEST_SAVE_INFO("test fail,tx%d=%5d,range=(%5d,%5d)\n", + i - tdata->sc_node.rx_num + 1, + data[i], min[i], max[i]); + result = false; + } + } + } + + return result; +} + +void show_data_mc_sc(int *data) +{ + int i = 0; + struct fts_test *tdata = fts_ftest; + + FTS_TEST_SAVE_INFO("SCap Rx: "); + for (i = 0; i < tdata->sc_node.rx_num; i++) { + FTS_TEST_SAVE_INFO( "%5d, ", data[i]); + } + FTS_TEST_SAVE_INFO("\n"); + + FTS_TEST_SAVE_INFO("SCap Tx: "); + for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) { + FTS_TEST_SAVE_INFO( "%5d, ", data[i]); + } + FTS_TEST_SAVE_INFO("\n"); +} +/* mc_sc end*/ + +#if CSV_SUPPORT || TXT_SUPPORT +static int fts_test_save_test_data(char *file_name, char *data_buf, int len) +{ + struct file *pfile = NULL; + char filepath[128]; + loff_t pos; + mm_segment_t old_fs; + + FTS_TEST_FUNC_ENTER(); + memset(filepath, 0, sizeof(filepath)); + sprintf(filepath, "%s%s", FTS_INI_FILE_PATH, file_name); + if (NULL == pfile) { + pfile = filp_open(filepath, O_TRUNC | O_CREAT | O_RDWR, 0); + } + if (IS_ERR(pfile)) { + FTS_TEST_ERROR("error occured while opening file %s.", filepath); + return -EIO; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + vfs_write(pfile, data_buf, len, &pos); + filp_close(pfile, NULL); + set_fs(old_fs); + + FTS_TEST_FUNC_EXIT(); + return 0; +} +#endif + +static int fts_test_malloc_free_data_txt(struct fts_test *tdata, bool allocate) +{ +#if TXT_SUPPORT + if (true == allocate) { + tdata->testresult = vmalloc(TXT_BUFFER_LEN); + if (NULL == tdata->testresult) { + FTS_TEST_ERROR("tdata->testresult malloc fail\n"); + return -ENOMEM; + } + + tdata->testresult_len = 0; + FTS_TEST_SAVE_INFO("FW version:0x%02x\n", tdata->fw_ver); + FTS_TEST_SAVE_INFO("tx_num:%d, rx_num:%d, key_num:%d\n", + tdata->node.tx_num, tdata->node.rx_num, + tdata->node.key_num); + } else { + if (tdata->testresult) { + vfree(tdata->testresult); + tdata->testresult = NULL; + } + } +#endif + return 0; +} + +static void fts_test_save_data_csv(struct fts_test *tdata) +{ +#if CSV_SUPPORT + int i = 0; + int j = 0; + int k = 0; + int tx = 0; + int rx = 0; + int node_num = 0; + int offset = 0; + int start_line = 11; + int data_count = 0; + char *csv_buffer = NULL; + char *line2_buffer = NULL; + int csv_length = 0; + int line2_length = 0; + int csv_item_count = 0; + struct fts_test_data *td = &tdata->testdata; + struct item_info *info = NULL; + + FTS_TEST_INFO("save data in csv format"); + csv_buffer = vmalloc(CSV_BUFFER_LEN); + if (!csv_buffer) { + FTS_TEST_ERROR("csv_buffer malloc fail\n"); + return ; + } + + line2_buffer = vmalloc(CSV_LINE2_BUFFER_LEN); + if (!line2_buffer) { + FTS_TEST_ERROR("line2_buffer malloc fail\n"); + goto csv_save_err; + } + + FTS_TEST_INFO("test item count:%d", td->item_count); + /* line 1 */ + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "ECC, 85, 170, IC Name, %s, IC Code, %x\n", \ + tdata->ini.ic_name, \ + (tdata->ini.ic_code >> IC_CODE_OFFSET)); + + /* line 2 */ + for (i = 0; i < td->item_count; i++) { + info = &td->info[i]; + if (info->mc_sc) { + node_num = tdata->sc_node.node_num; + /* set max len of tx/rx to column */ + rx = (tdata->sc_node.tx_num > tdata->sc_node.rx_num) + ? tdata->sc_node.tx_num : tdata->sc_node.rx_num; + } else { + if (info->key_support && (tdata->node.key_num > 0)) + node_num = (tdata->node.tx_num + 1) * tdata->node.rx_num; + else + node_num = tdata->node.tx_num * tdata->node.rx_num; + rx = tdata->node.rx_num; + } + + if (info->datalen > node_num) { + data_count = (info->datalen - 1 ) / node_num + 1; + tx = (node_num - 1 ) / rx + 1; + } else { + data_count = 1; + tx = ((info->datalen - 1) / rx) + 1; + } + + for (j = 1; j <= data_count; j++) { + line2_length += snprintf(line2_buffer + line2_length, \ + CSV_LINE2_BUFFER_LEN - line2_length, \ + "%s, %d, %d, %d, %d, %d, ", \ + info->name, info->code, tx, rx, + start_line, j); + start_line += tx; + csv_item_count++; + } + } + + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "TestItem Num, %d, ", \ + csv_item_count); + + if (line2_length > 0) { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "%s", line2_buffer); + } + + /* line 3 ~ 10 "\n" */ + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "\n\n\n\n\n\n\n\n\n"); + + /* line 11 ~ data area */ + for (i = 0; i < td->item_count; i++) { + info = &td->info[i]; + if (!info->data) { + FTS_TEST_ERROR("test item data is null"); + return; + } + + if (info->mc_sc) { + offset = 0; + for (j = 0; j < info->datalen; j++) { + for (k = 0; k < tdata->sc_node.node_num; k++) { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "%d, ", info->data[offset + k]); + if ((k + 1) == tdata->sc_node.rx_num) { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "\n"); + } + } + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "\n"); + offset += k; + j += k; + } + } else { + for (j = 0; j < info->datalen; j++) { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "%d, ", info->data[j]); + if (((j + 1) % tdata->node.rx_num) == 0) { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, + "\n"); + } + } + } + } + FTS_TEST_INFO("csv length:%d", csv_length); + fts_test_save_test_data(FTS_CSV_FILE_NAME, csv_buffer, csv_length); + +csv_save_err: + if (csv_buffer) { + vfree(csv_buffer); + csv_buffer = NULL; + } +#endif +} + +static void fts_test_save_result_txt(struct fts_test *tdata) +{ +#if TXT_SUPPORT + if (!tdata || !tdata->testresult) { + FTS_TEST_ERROR("test result is null"); + return; + } + + FTS_TEST_INFO("test result length in txt:%d", tdata->testresult_len); + fts_test_save_test_data(FTS_TXT_FILE_NAME, tdata->testresult, + tdata->testresult_len); +#endif +} + +/***************************************************************************** +* Name: fts_test_save_data +* Brief: Save test data. +* If multi-data of MC, length of data package must be tx*rx,(tx+1)*rx +* If multi-data of MC-SC, length of data package should be (tx+rx)*2 +* Need fill 0 when no actual data +* Input: +* Output: +* Return: +*****************************************************************************/ +void fts_test_save_data(char *name, int code, int *data, int datacnt, + bool mc_sc, bool key, bool result) +{ + int datalen = datacnt; + struct fts_test *tdata = fts_ftest; + struct fts_test_data *td = &tdata->testdata; + struct item_info *info = &td->info[td->item_count]; + + if (!name || !data) { + FTS_TEST_ERROR("name/data is null"); + return ; + } + + strncpy(info->name, name, TEST_ITEM_NAME_MAX); + info->code = code; + info->mc_sc = mc_sc; + info->key_support = key; + info->result = result; + if (datalen <= 0) { + if (mc_sc) { + datalen = tdata->sc_node.node_num * 2; + } else { + if (key && (tdata->node.key_num > 0)) + datalen = (tdata->node.tx_num + 1) * tdata->node.rx_num; + else + datalen = tdata->node.tx_num * tdata->node.rx_num; + + } + } + + FTS_TEST_DBG("name:%s,len:%d", name, datalen); + info->data = fts_malloc(datalen * sizeof(int)); + if (!info->data) { + FTS_TEST_ERROR("malloc memory for item(%d) data fail", td->item_count); + info->datalen = 0; + return ; + } + memcpy(info->data, data, datalen * sizeof(int)); + info->datalen = datalen; + + td->item_count++; +} + +static void fts_test_free_data(struct fts_test *tdata) +{ + int i = 0; + struct fts_test_data *td = &tdata->testdata; + + for (i = 0; i < td->item_count; i++) { + if (td->info[i].data) { + fts_free(td->info[i].data); + } + } +} + +static int fts_test_malloc_free_incell(struct fts_test *tdata, bool allocate) +{ + struct incell_threshold *thr = &tdata->ic.incell.thr; + int buflen = tdata->node.node_num * sizeof(int); + + if (true == allocate) { + FTS_TEST_INFO("buflen:%d", buflen); + fts_malloc_r(thr->rawdata_min, buflen); + fts_malloc_r(thr->rawdata_max, buflen); + if (tdata->func->rawdata2_support) { + fts_malloc_r(thr->rawdata2_min, buflen); + fts_malloc_r(thr->rawdata2_max, buflen); + } + fts_malloc_r(thr->cb_min, buflen); + fts_malloc_r(thr->cb_max, buflen); + } else { + fts_free(thr->rawdata_min); + fts_free(thr->rawdata_max); + if (tdata->func->rawdata2_support) { + fts_free(thr->rawdata2_min); + fts_free(thr->rawdata2_max); + } + fts_free(thr->cb_min); + fts_free(thr->cb_max); + } + + return 0; +} + +static int fts_test_malloc_free_mc_sc(struct fts_test *tdata, bool allocate) +{ + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + int buflen = tdata->node.node_num * sizeof(int); + int buflen_sc = tdata->sc_node.node_num * sizeof(int); + + if (true == allocate) { + fts_malloc_r(thr->rawdata_h_min, buflen); + fts_malloc_r(thr->rawdata_h_max, buflen); + if (tdata->func->rawdata2_support) { + fts_malloc_r(thr->rawdata_l_min, buflen); + fts_malloc_r(thr->rawdata_l_max, buflen); + } + fts_malloc_r(thr->tx_linearity_max, buflen); + fts_malloc_r(thr->tx_linearity_min, buflen); + fts_malloc_r(thr->rx_linearity_max, buflen); + fts_malloc_r(thr->rx_linearity_min, buflen); + + fts_malloc_r(thr->scap_cb_off_min, buflen_sc); + fts_malloc_r(thr->scap_cb_off_max, buflen_sc); + fts_malloc_r(thr->scap_cb_on_min, buflen_sc); + fts_malloc_r(thr->scap_cb_on_max, buflen_sc); + + fts_malloc_r(thr->scap_rawdata_off_min, buflen_sc); + fts_malloc_r(thr->scap_rawdata_off_max, buflen_sc); + fts_malloc_r(thr->scap_rawdata_on_min, buflen_sc); + fts_malloc_r(thr->scap_rawdata_on_max, buflen_sc); + + fts_malloc_r(thr->panel_differ_min, buflen); + fts_malloc_r(thr->panel_differ_max, buflen); + } else { + fts_free(thr->rawdata_h_min); + fts_free(thr->rawdata_h_max); + if (tdata->func->rawdata2_support) { + fts_free(thr->rawdata_l_min); + fts_free(thr->rawdata_l_max); + } + fts_free(thr->tx_linearity_max); + fts_free(thr->tx_linearity_min); + fts_free(thr->rx_linearity_max); + fts_free(thr->rx_linearity_min); + + fts_free(thr->scap_cb_off_min); + fts_free(thr->scap_cb_off_max); + fts_free(thr->scap_cb_on_min); + fts_free(thr->scap_cb_on_max); + + fts_free(thr->scap_rawdata_off_min); + fts_free(thr->scap_rawdata_off_max); + fts_free(thr->scap_rawdata_on_min); + fts_free(thr->scap_rawdata_on_max); + + fts_free(thr->panel_differ_min); + fts_free(thr->panel_differ_max); + } + + return 0; +} + +static int fts_test_malloc_free_sc(struct fts_test *tdata, bool allocate) +{ + struct sc_threshold *thr = &tdata->ic.sc.thr; + int buflen = tdata->node.node_num * sizeof(int); + + if (true == allocate) { + fts_malloc_r(thr->rawdata_min, buflen); + fts_malloc_r(thr->rawdata_max, buflen); + fts_malloc_r(thr->cb_min, buflen); + fts_malloc_r(thr->cb_max, buflen); + fts_malloc_r(thr->dcb_sort, buflen); + fts_malloc_r(thr->dcb_base, buflen); + } else { + fts_free(thr->rawdata_min); + fts_free(thr->rawdata_max); + fts_free(thr->cb_min); + fts_free(thr->cb_max); + fts_free(thr->dcb_sort); + fts_free(thr->dcb_base); + } + + return 0; +} + +static int fts_test_malloc_free_thr(struct fts_test *tdata, bool allocate) +{ + int ret = 0; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("tdata/func is NULL\n"); + return -EINVAL; + } + + if (true == allocate) { + fts_malloc_r(tdata->node_valid, tdata->node.node_num * sizeof(int)); + fts_malloc_r(tdata->node_valid_sc, tdata->sc_node.node_num * sizeof(int)); + } else { + fts_free(tdata->node_valid); + fts_free(tdata->node_valid_sc); + } + + switch (tdata->func->hwtype) { + case IC_HW_INCELL: + ret = fts_test_malloc_free_incell(tdata, allocate); + break; + case IC_HW_MC_SC: + ret = fts_test_malloc_free_mc_sc(tdata, allocate); + break; + case IC_HW_SC: + ret = fts_test_malloc_free_sc(tdata, allocate); + break; + default: + FTS_TEST_SAVE_ERR("test ic type(%d) fail\n", tdata->func->hwtype); + ret = -EINVAL; + break; + } + + return ret; +} + +/* default enable all test item */ +static void fts_test_init_item(struct fts_test *tdata) +{ + switch (tdata->func->hwtype) { + case IC_HW_INCELL: + tdata->ic.incell.u.tmp = 0xFFFFFFFF; + break; + case IC_HW_MC_SC: + tdata->ic.mc_sc.u.tmp = 0xFFFFFFFF; + break; + case IC_HW_SC: + tdata->ic.sc.u.tmp = 0xFFFFFFFF; + break; + } +} + +static int get_tx_rx_num(u8 tx_rx_reg, u8 *ch_num, u8 ch_num_max) +{ + int ret = 0; + int i = 0; + + for (i = 0; i < 3; i++) { + ret = fts_test_read_reg(tx_rx_reg, ch_num); + if ((ret < 0) || (*ch_num > ch_num_max)) { + sys_delay(50); + } else + break; + } + + if (i >= 3) { + FTS_TEST_ERROR("get channel num fail"); + return -EIO; + } + + return 0; +} +static int get_key_num(int *key_num_en, int max_key_num) +{ + int ret = 0; + u8 key_en = 0; + + if (!max_key_num) { + FTS_TEST_DBG("not support key, don't read key num register"); + return 0; + } + + ret = fts_test_read_reg(FACTORY_REG_LEFT_KEY, &key_en); + if (ret >= 0) { + if (key_en & 0x01) { + (*key_num_en)++; + } + + if (key_en & 0x02) { + (*key_num_en)++; + } + + if (key_en & 0x04) { + (*key_num_en)++; + } + } + + ret = fts_test_read_reg(FACTORY_REG_RIGHT_KEY, &key_en); + if (ret >= 0) { + if (key_en & 0x01) { + (*key_num_en)++; + } + + if (key_en & 0x02) { + (*key_num_en)++; + } + + if (key_en & 0x04) { + (*key_num_en)++; + } + } + + if (*key_num_en > max_key_num) { + FTS_TEST_ERROR("get key num, fw:%d > max:%d", *key_num_en, max_key_num); + return -EIO; + } + + return ret; +} + +static int get_channel_num(struct fts_test *tdata) +{ + int ret = 0; + u8 tx_num = 0; + u8 rx_num = 0; + int key_num = 0; + + /* node structure */ + if (IC_HW_SC == tdata->func->hwtype) { + ret = get_tx_rx_num(FACTORY_REG_CH_NUM_SC, &tx_num, NUM_MAX_SC); + if (ret < 0) { + FTS_TEST_ERROR("get channel number fail"); + return ret; + } + + ret = get_tx_rx_num(FACTORY_REG_KEY_NUM_SC, &rx_num, KEY_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get key number fail"); + return ret; + } + + tdata->node.tx_num = 2; + tdata->node.rx_num = tx_num / 2; + tdata->node.channel_num = tx_num; + tdata->node.node_num = tx_num; + key_num = rx_num; + } else { + ret = get_tx_rx_num(FACTORY_REG_CHX_NUM, &tx_num, TX_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get tx_num fail"); + return ret; + } + + ret = get_tx_rx_num(FACTORY_REG_CHY_NUM, &rx_num, RX_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get rx_num fail"); + return ret; + } + + if (IC_HW_INCELL == tdata->func->hwtype) { + ret = get_key_num(&key_num, tdata->func->key_num_total); + if (ret < 0) { + FTS_TEST_ERROR("get key_num fail"); + return ret; + } + } else if (IC_HW_MC_SC == tdata->func->hwtype) { + key_num = tdata->func->key_num_total; + } + tdata->node.tx_num = tx_num; + tdata->node.rx_num = rx_num; + if (IC_HW_INCELL == tdata->func->hwtype) + tdata->node.channel_num = tx_num * rx_num; + else if (IC_HW_MC_SC == tdata->func->hwtype) + tdata->node.channel_num = tx_num + rx_num; + tdata->node.node_num = tx_num * rx_num; + } + + /* key */ + tdata->node.key_num = key_num; + tdata->node.node_num += tdata->node.key_num; + + /* sc node structure */ + tdata->sc_node = tdata->node; + if (IC_HW_MC_SC == tdata->func->hwtype) { + if (tdata->v3_pattern) { + ret = get_tx_rx_num(FACTORY_REG_CHX_NUM_NOMAP, &tx_num, TX_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get no-mappint tx_num fail"); + return ret; + } + + ret = get_tx_rx_num(FACTORY_REG_CHY_NUM_NOMAP, &rx_num, TX_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get no-mapping rx_num fail"); + return ret; + } + + tdata->sc_node.tx_num = tx_num; + tdata->sc_node.rx_num = rx_num; + } + tdata->sc_node.channel_num = tx_num + rx_num; + tdata->sc_node.node_num = tx_num + rx_num; + } + + if (tdata->node.tx_num > TX_NUM_MAX) { + FTS_TEST_ERROR("tx num(%d) fail", tdata->node.tx_num); + return -EIO; + } + + if (tdata->node.rx_num > RX_NUM_MAX) { + FTS_TEST_ERROR("rx num(%d) fail", tdata->node.rx_num); + return -EIO; + } + + FTS_TEST_INFO("node_num:%d, tx:%d, rx:%d, key:%d", + tdata->node.node_num, tdata->node.tx_num, + tdata->node.rx_num, tdata->node.key_num); + return 0; +} + +static int fts_test_init_basicinfo(struct fts_test *tdata) +{ + int ret = 0; + u8 val = 0; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("tdata/func is NULL\n"); + return -EINVAL; + } + + fts_test_read_reg(REG_FW_VERSION, &val); + tdata->fw_ver = val; + + if (IC_HW_INCELL == tdata->func->hwtype) { + fts_test_read_reg(REG_VA_TOUCH_THR, &val); + tdata->va_touch_thr = val; + fts_test_read_reg(REG_VKEY_TOUCH_THR, &val); + tdata->vk_touch_thr = val; + } + + /* enter factory mode */ + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail\n"); + return ret; + } + + if (IC_HW_MC_SC == tdata->func->hwtype) { + fts_test_read_reg(FACTORY_REG_PATTERN, &val); + tdata->v3_pattern = (1 == val) ? true : false; + fts_test_read_reg(FACTORY_REG_NOMAPPING, &val); + tdata->mapping = val; + } + + /* enter into factory mode and read tx/rx num */ + ret = get_channel_num(tdata); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get channel number fail\n"); + return ret; + } + + return ret; +} + +static int fts_test_main_init(void) +{ + int ret = 0; + struct fts_test *tdata = fts_ftest; + + FTS_TEST_FUNC_ENTER(); + /* Init fts_test_data to 0 before test, */ + memset(&tdata->testdata, 0, sizeof(struct fts_test_data)); + + /* get basic information: tx/rx num ... */ + ret = fts_test_init_basicinfo(tdata); + if (ret < 0) { + FTS_TEST_ERROR("test init basicinfo fail"); + return ret; + } + + /* allocate memory for test threshold */ + ret = fts_test_malloc_free_thr(tdata, true); + if (ret < 0) { + FTS_TEST_ERROR("test malloc for threshold fail"); + return ret; + } + + /* default enable all test item */ + fts_test_init_item(tdata); + + ret = fts_test_malloc_free_data_txt(tdata, true); + if (ret < 0) { + FTS_TEST_ERROR("allocate memory for test data(txt) fail"); + return ret; + } + + /* allocate test data buffer */ + tdata->buffer_length = (tdata->node.tx_num + 1) * tdata->node.rx_num; + tdata->buffer_length *= sizeof(int) * 2; + FTS_TEST_INFO("test buffer length:%d", tdata->buffer_length); + tdata->buffer = (int *)fts_malloc(tdata->buffer_length); + if (NULL == tdata->buffer) { + FTS_TEST_ERROR("test buffer(%d) malloc fail", tdata->buffer_length); + return -ENOMEM; + } + memset(tdata->buffer, 0, tdata->buffer_length); + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int fts_test_main_exit(void) +{ + struct fts_test *tdata = fts_ftest; + + FTS_TEST_FUNC_ENTER(); + fts_test_save_data_csv(tdata); + fts_test_save_result_txt(tdata); + + /* free memory */ + fts_test_malloc_free_data_txt(tdata, false); + fts_test_malloc_free_thr(tdata, false); + + /* free test data */ + fts_test_free_data(tdata); + + /*free test data buffer*/ + fts_free(tdata->buffer); + + FTS_TEST_FUNC_EXIT(); + return 0; +} + + +/* + * fts_test_get_testparams - get test parameter from ini + */ +static int fts_test_get_testparams(char *config_name) +{ + int ret = 0; + + ret = fts_test_get_testparam_from_ini(config_name); + + return ret; +} + +static int fts_test_start(void) +{ + int testresult = 0; + struct fts_test *tdata = fts_ftest; + + tdata->testdata.item_count = 0; + if (tdata && tdata->func && tdata->func->start_test) { + testresult = tdata->func->start_test(); + } else { + FTS_TEST_ERROR("test func/start_test func is null"); + } + + return testresult; +} + +/* + * fts_test_entry - test main entry + * + * warning - need disable irq & esdcheck before call this function + * + */ +static int fts_test_entry(char *ini_file_name) +{ + int ret = 0; + + /* test initialize */ + ret = fts_test_main_init(); + if (ret < 0) { + FTS_TEST_ERROR("fts_test_main_init fail"); + goto test_err; + } + + /*Read parse configuration file*/ + FTS_TEST_SAVE_INFO("ini_file_name:%s\n", ini_file_name); + ret = fts_test_get_testparams(ini_file_name); + if (ret < 0) { + FTS_TEST_ERROR("get testparam fail"); + goto test_err; + } + + /* Start testing according to the test configuration */ + if (true == fts_test_start()) { + FTS_TEST_SAVE_INFO("\n\n=======Tp test pass.\n"); + } else { + FTS_TEST_SAVE_INFO("\n\n=======Tp test failure.\n"); + } + + ret = 0; +test_err: + fts_test_main_exit(); + enter_work_mode(); + return ret; +} + +static ssize_t fts_test_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +static ssize_t fts_test_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + char fwname[128] = {0}; + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev; + + if (ts_data->suspended) { + FTS_INFO("In suspend, no test, return now"); + return -EINVAL; + } + + input_dev = ts_data->input_dev; + memset(fwname, 0, sizeof(fwname)); + sprintf(fwname, "%s", buf); + fwname[count - 1] = '\0'; + FTS_TEST_DBG("fwname:%s.", fwname); + + mutex_lock(&input_dev->mutex); + disable_irq(ts_data->irq); + +#if defined(FTS_ESDCHECK_EN) && (FTS_ESDCHECK_EN) + fts_esdcheck_switch(DISABLE); +#endif + + ret = fts_enter_test_environment(1); + if (ret < 0) { + FTS_ERROR("enter test environment fail"); + } else { + fts_test_entry(fwname); + } + ret = fts_enter_test_environment(0); + if (ret < 0) { + FTS_ERROR("enter normal environment fail"); + } + +#if defined(FTS_ESDCHECK_EN) && (FTS_ESDCHECK_EN) + fts_esdcheck_switch(ENABLE); +#endif + + enable_irq(ts_data->irq); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* test from test.ini +* example:echo "***.ini" > fts_test +*/ +static DEVICE_ATTR(fts_test, S_IRUGO | S_IWUSR, fts_test_show, fts_test_store); + +static struct attribute *fts_test_attributes[] = { + &dev_attr_fts_test.attr, + NULL +}; + +static struct attribute_group fts_test_attribute_group = { + .attrs = fts_test_attributes +}; + +static int fts_test_func_init(struct fts_ts_data *ts_data) +{ + int i = 0; + int j = 0; + int ic_stype = ts_data->ic_info.ids.type; + struct test_funcs *func = test_func_list[0]; + int func_count = sizeof(test_func_list) / sizeof(test_func_list[0]); + + FTS_TEST_INFO("init test function"); + if (0 == func_count) { + FTS_TEST_SAVE_ERR("test functions list is NULL, fail\n"); + return -ENODATA; + } + + fts_ftest = (struct fts_test *)kzalloc(sizeof(*fts_ftest), GFP_KERNEL); + if (NULL == fts_ftest) { + FTS_TEST_ERROR("malloc memory for test fail"); + return -ENOMEM; + } + + for (i = 0; i < func_count; i++) { + func = test_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_TEST_INFO("match test function,type:%x", (int)func->ctype[j]); + fts_ftest->func = func; + } + } + } + if (NULL == fts_ftest->func) { + FTS_TEST_ERROR("no test function match, can't test"); + return -ENODATA; + } + + fts_ftest->ts_data = fts_data; + return 0; +} + +int fts_test_init(struct fts_ts_data *ts_data) +{ + int ret = 0; + + FTS_TEST_FUNC_ENTER(); + /* get test function, must be the first step */ + ret = fts_test_func_init(ts_data); + if (ret < 0) { + FTS_TEST_SAVE_ERR("test functions init fail"); + return ret; + } + + ret = sysfs_create_group(&ts_data->dev->kobj, &fts_test_attribute_group); + if (0 != ret) { + FTS_TEST_ERROR("sysfs(test) create fail"); + sysfs_remove_group(&ts_data->dev->kobj, &fts_test_attribute_group); + } else { + FTS_TEST_DBG("sysfs(test) create successfully"); + } + FTS_TEST_FUNC_EXIT(); + + return ret; +} + +int fts_test_exit(struct fts_ts_data *ts_data) +{ + FTS_TEST_FUNC_ENTER(); + + sysfs_remove_group(&ts_data->dev->kobj, &fts_test_attribute_group); + fts_free(fts_ftest); + FTS_TEST_FUNC_EXIT(); + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/focaltech_test.h b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/focaltech_test.h new file mode 100755 index 000000000000..c94ba52258ef --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/focaltech_test.h @@ -0,0 +1,606 @@ +/************************************************************************ +* Copyright (C) 2012-2019, Focaltech Systems (R)£¬All Rights Reserved. +* +* File Name: focaltech_test.h +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-01 +* +* Abstract: test entry for all IC +* +************************************************************************/ +#ifndef _TEST_LIB_H +#define _TEST_LIB_H + +/***************************************************************************** +* Included header files +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include //iic +#include //msleep +#include +#include +#include + +#include "../focaltech_core.h" +#include "focaltech_test_ini.h" + +/***************************************************************************** +* Macro definitions using #define +*****************************************************************************/ +#define FTS_INI_FILE_PATH "/mnt/sdcard/" +#define FTS_CSV_FILE_NAME "testdata.csv" +#define FTS_TXT_FILE_NAME "testresult.txt" +#define false 0 +#define true 1 +#define TEST_ICSERIES_LEN (8) +#define TEST_ICSERIES(x) ((x) >> TEST_ICSERIES_LEN) + +#define TEST_OPEN_MAX_VALUE (255) +#define BYTES_PER_TIME (32) /* max:128 */ +/* CSV & TXT */ +#define CSV_LINE2_BUFFER_LEN (1024) +#define CSV_BUFFER_LEN (1024*80*5) +#define TXT_BUFFER_LEN (1024*80*5) + +/*----------------------------------------------------------- +Test Status +-----------------------------------------------------------*/ +#define RESULT_NULL 0 +#define RESULT_PASS 1 +#define RESULT_NG 2 + +#define TX_NUM_MAX 60 +#define RX_NUM_MAX 60 +#define NUM_MAX ((TX_NUM_MAX)*(RX_NUM_MAX)) +#define NUM_MAX_SC (144) +#define KEY_NUM_MAX 6 +#define TEST_ITEM_COUNT_MAX 32 +#define TEST_ITEM_NAME_MAX 32 +#define TEST_SHORT_RES_MAX 0xFFFF + +/* + * factory test registers + */ +#define ENTER_WORK_FACTORY_RETRIES 5 + +#define START_SCAN_RETRIES_INCELL 20 +#define START_SCAN_RETRIES_DELAY_INCELL 16 +#define FACTORY_TEST_RETRY 50 +#define FACTORY_TEST_DELAY 18 +#define FACTORY_TEST_RETRY_DELAY 100 + +#define DEVIDE_MODE_ADDR 0x00 +#define REG_FW_VERSION 0xA6 +#define REG_VA_TOUCH_THR 0x80 +#define REG_VKEY_TOUCH_THR 0x82 + +#define FACTORY_REG_LINE_ADDR 0x01 +#define FACTORY_REG_CHX_NUM 0x02 +#define FACTORY_REG_CHY_NUM 0x03 +#define FACTORY_REG_CLB 0x04 +#define FACTORY_REG_DATA_SELECT 0x06 +#define FACTORY_REG_RAWBUF_SELECT 0x09 +#define FACTORY_REG_KEY_CBWIDTH 0x0B +#define FACTORY_REG_PARAM_UPDATE_STATE 0x0E +#define FACTORY_REG_SHORT_TEST_EN 0x0F +#define FACTORY_REG_SHORT_TEST_STATE 0x10 +#define FACTORY_REG_LCD_NOISE_START 0x11 +#define FACTORY_REG_LCD_NOISE_FRAME 0x12 +#define FACTORY_REG_LCD_NOISE_NUMBER 0x13 +#define FACTORY_REG_LCD_NOISE_TEST_STATE 0x13 +#define FACTORY_REG_LCD_NOISE_TTHR 0x14 +#define FACTORY_REG_OPEN_START 0x15 +#define FACTORY_REG_OPEN_STATE 0x16 +#define FACTORY_REG_OPEN_IDLE 0x03 +#define FACTORY_REG_OPEN_BUSY 0x01 +#define FACTORY_REG_CB_ADDR_H 0x18 +#define FACTORY_REG_CB_ADDR_L 0x19 +#define FACTORY_REG_ORDER_ADDR_H 0x1A +#define FACTORY_REG_ORDER_ADDR_L 0x1B +#define FACTORY_REG_LCD_NOISE_STATE 0x1E +#define FACTORY_REG_KEYSHORT_EN 0x2E +#define FACTORY_REG_KEYSHORT_STATE 0x2F + +#define FACTORY_REG_LEFT_KEY 0x1E +#define FACTORY_REG_RIGHT_KEY 0x1F +#define FACTORY_REG_OPEN_REG20 0x20 +#define FACTORY_REG_OPEN_REG21 0x21 +#define FACTORY_REG_OPEN_REG22 0x22 +#define FACTORY_REG_OPEN_REG23 0x23 +#define FACTORY_REG_OPEN_REG2E 0x2E +#define FACTORY_REG_OPEN_REG86 0x86 +#define FACTORY_REG_K1 0x31 +#define FACTORY_REG_K2 0x32 +#define FACTORY_REG_RAWDATA_ADDR 0x6A +#define FACTORY_REG_ORDER_ADDR 0x6C +#define FACTORY_REG_CB_ADDR 0x6E +#define FACTORY_REG_SHORT_ADDR 0x89 +#define FACTORY_REG_RAWDATA_TEST_EN 0x9E +#define FACTORY_REG_CB_TEST_EN 0x9F +#define FACTORY_REG_OPEN_TEST_EN 0xA0 + +/* mc_sc */ +#define FACTORY_REG_FRE_LIST 0x0A +#define FACTORY_REG_NORMALIZE 0x16 +#define FACTORY_REG_RAWDATA_ADDR_MC_SC 0x36 +#define FACTORY_REG_PATTERN 0x53 +#define FACTORY_REG_NOMAPPING 0x54 +#define FACTORY_REG_CHX_NUM_NOMAP 0x55 +#define FACTORY_REG_CHY_NUM_NOMAP 0x56 +#define FACTORY_REG_WC_SEL 0x09 +#define FACTORY_REG_MC_SC_MODE 0x44 +#define FACTORY_REG_MC_SC_CB_ADDR_OFF 0x45 +#define FACTORY_REG_MC_SC_CB_ADDR 0x4E +#define FACTROY_REG_SHORT_TEST_EN 0x07 +#define FACTROY_REG_SHORT_CA 0x01 +#define FACTROY_REG_SHORT_CC 0x02 +#define FACTROY_REG_SHORT_CG 0x03 +#define FACTROY_REG_SHORT_OFFSET 0x04 +#define FACTROY_REG_SHORT_AB_CH 0x58 +#define FACTROY_REG_SHORT_DELAY 0x5A +#define FACTORY_REG_SHORT_ADDR_MC 0xF4 +#define FACTORY_REG_FIR 0xFB + +/* sc */ +#define FACTORY_REG_SCAN_ADDR2 0x08 +#define FACTORY_REG_CH_NUM_SC 0x0A +#define FACTORY_REG_KEY_NUM_SC 0x0B +#define FACTORY_REG_SC_CB_ADDR_OFF 0x33 +#define FACTORY_REG_SC_CB_ADDR 0x39 +#define FACTORY_REG_RAWDATA_SADDR_SC 0x34 +#define FACTORY_REG_RAWDATA_ADDR_SC 0x35 +#define FACTORY_REG_CB_SEL 0x41 +#define FACTORY_REG_FMODE 0xAE + +#define TEST_RETVAL_00 0x00 +#define TEST_RETVAL_AA 0xAA + +/***************************************************************************** +* enumerations, structures and unions +*****************************************************************************/ +struct item_info { + char name[TEST_ITEM_NAME_MAX]; + int code; + int *data; + int datalen; + int result; + int mc_sc; + int key_support; +}; + +struct fts_test_data { + int item_count; + struct item_info info[TEST_ITEM_COUNT_MAX]; +}; + +/* incell */ +struct incell_testitem { + u32 short_test : 1; + u32 open_test : 1; + u32 cb_test : 1; + u32 rawdata_test : 1; + u32 lcdnoise_test : 1; + u32 keyshort_test : 1; + u32 mux_open_test : 1; +}; + +struct incell_threshold_b { + int short_res_min; + int short_res_vk_min; + int open_cb_min; + int open_k1_check; + int open_k1_value; + int open_k2_check; + int open_k2_value; + int cb_min; + int cb_max; + int cb_vkey_check; + int cb_min_vk; + int cb_max_vk; + int rawdata_min; + int rawdata_max; + int rawdata_vkey_check; + int rawdata_min_vk; + int rawdata_max_vk; + int lcdnoise_frame; + int lcdnoise_coefficient; + int lcdnoise_coefficient_vkey; + int open_nmos; + int keyshort_k1; + int keyshort_cb_max; + int rawdata2_min; + int rawdata2_max; + int mux_open_cb_min; +}; + +struct incell_threshold { + struct incell_threshold_b basic; + int *rawdata_min; + int *rawdata_max; + int *rawdata2_min; + int *rawdata2_max; + int *cb_min; + int *cb_max; +}; + +struct incell_test { + struct incell_threshold thr; + union { + int tmp; + struct incell_testitem item; + } u; +}; + +/* mc_sc */ +enum mapping_type { + MAPPING = 0, + NO_MAPPING = 1, +}; + +struct mc_sc_testitem { + u32 rawdata_test : 1; + u32 rawdata_uniformity_test : 1; + u32 scap_cb_test : 1; + u32 scap_rawdata_test : 1; + u32 short_test : 1; + u32 panel_differ_test : 1; +}; + +struct mc_sc_threshold_b { + int rawdata_h_min; + int rawdata_h_max; + int rawdata_set_hfreq; + int rawdata_l_min; + int rawdata_l_max; + int rawdata_set_lfreq; + int uniformity_check_tx; + int uniformity_check_rx; + int uniformity_check_min_max; + int uniformity_tx_hole; + int uniformity_rx_hole; + int uniformity_min_max_hole; + int scap_cb_off_min; + int scap_cb_off_max; + int scap_cb_wp_off_check; + int scap_cb_on_min; + int scap_cb_on_max; + int scap_cb_wp_on_check; + int scap_rawdata_off_min; + int scap_rawdata_off_max; + int scap_rawdata_wp_off_check; + int scap_rawdata_on_min; + int scap_rawdata_on_max; + int scap_rawdata_wp_on_check; + int short_cg; + int short_cc; + int panel_differ_min; + int panel_differ_max; + +}; + +struct mc_sc_threshold { + struct mc_sc_threshold_b basic; + int *rawdata_h_min; + int *rawdata_h_max; + int *rawdata_l_min; + int *rawdata_l_max; + int *tx_linearity_max; + int *tx_linearity_min; + int *rx_linearity_max; + int *rx_linearity_min; + int *scap_cb_off_min; + int *scap_cb_off_max; + int *scap_cb_on_min; + int *scap_cb_on_max; + int *scap_rawdata_off_min; + int *scap_rawdata_off_max; + int *scap_rawdata_on_min; + int *scap_rawdata_on_max; + int *panel_differ_min; + int *panel_differ_max; +}; + +struct mc_sc_test { + struct mc_sc_threshold thr; + union { + u32 tmp; + struct mc_sc_testitem item; + } u; +}; + +/* sc */ +struct sc_testitem { + u32 rawdata_test : 1; + u32 cb_test : 1; + u32 delta_cb_test : 1; + u32 short_test : 1; +}; + +struct sc_threshold_b { + int rawdata_min; + int rawdata_max; + int cb_min; + int cb_max; + int dcb_base; + int dcb_differ_max; + int dcb_key_check; + int dcb_key_differ_max; + int dcb_ds1; + int dcb_ds2; + int dcb_ds3; + int dcb_ds4; + int dcb_ds5; + int dcb_ds6; + int dcb_critical_check; + int dcb_cs1; + int dcb_cs2; + int dcb_cs3; + int dcb_cs4; + int dcb_cs5; + int dcb_cs6; + int short_min; +}; + +struct sc_threshold { + struct sc_threshold_b basic; + int *rawdata_min; + int *rawdata_max; + int *cb_min; + int *cb_max; + int *dcb_sort; + int *dcb_base; +}; + +struct sc_test { + struct sc_threshold thr; + union { + u32 tmp; + struct sc_testitem item; + } u; +}; + +enum test_hw_type { + IC_HW_INCELL = 1, + IC_HW_MC_SC, + IC_HW_SC, +}; + +enum test_scan_mode { + SCAN_NORMAL = 0, + SCAN_SC, +}; + +struct fts_test_node { + int channel_num; + int tx_num; + int rx_num; + int node_num; + int key_num; +}; + +struct fts_test { + struct fts_ts_data *ts_data; + struct fts_test_node node; + struct fts_test_node sc_node; + u8 fw_ver; + u8 va_touch_thr; + u8 vk_touch_thr; + bool key_support; + bool v3_pattern; + u8 mapping; + u8 normalize; + int test_num; + int *buffer; + int buffer_length; + int *node_valid; + int *node_valid_sc; + int basic_thr_count; + int code1; + int code2; + int offset; + union { + struct incell_test incell; + struct mc_sc_test mc_sc; + struct sc_test sc; + } ic; + + struct test_funcs *func; + struct fts_test_data testdata; + char *testresult; + int testresult_len; + struct ini_data ini; +}; + +struct test_funcs { + u64 ctype[FTX_MAX_COMPATIBLE_TYPE]; + enum test_hw_type hwtype; + int startscan_mode; + int key_num_total; + bool rawdata2_support; + bool force_touch; + int (*param_init)(void); + int (*init)(void); + int (*start_test)(void); +}; + +enum byte_mode { + DATA_ONE_BYTE, + DATA_TWO_BYTE, +}; +/* mc_sc */ +enum normalize_type { + NORMALIZE_OVERALL, + NORMALIZE_AUTO, +}; + +enum wp_type { + WATER_PROOF_OFF = 0, + WATER_PROOF_ON = 1, + WATER_PROOF_ON_TX, + WATER_PROOF_ON_RX, + WATER_PROOF_OFF_TX, + WATER_PROOF_OFF_RX, +}; +/* mc end */ + +/* sc */ +enum factory_mode { + FACTORY_NORMAL, + FACTORY_TESTMODE_1, + FACTORY_TESTMODE_2, +}; + +enum dcb_sort_num { + DCB_SORT_MIN = 1, + DCB_SORT_MAX = 6, +}; + +struct dcb_sort_d { + int ch_num; + int deviation; + int critical; + int min; + int max; +}; +/* sc end */ + +enum csv_itemcode_incell { + CODE_ENTER_FACTORY_MODE = 0, + CODE_RAWDATA_TEST = 7, + CODE_CB_TEST = 12, + CODE_SHORT_TEST = 15, + CODE_OPEN_TEST = 25, + CODE_LCD_NOISE_TEST = 27, + CODE_MUX_OPEN_TEST = 41, +}; + +enum csv_itemcode_mc_sc { + CODE_M_RAWDATA_TEST = 7, + CODE_M_SCAP_CB_TEST = 9, + CODE_M_SCAP_RAWDATA_TEST = 10, + CODE_M_WEAK_SHORT_CIRCUIT_TEST = 15, + CODE_M_RAWDATA_UNIFORMITY_TEST = 16, + CODE_M_PANELDIFFER_TEST = 20, +}; + +enum csv_itemcode_sc { + CODE_S_RAWDATA_TEST = 7, + CODE_S_CB_TEST = 13, + CODE_S_DCB_TEST = 14, +}; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +#ifdef CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8719 +extern struct test_funcs test_func_ft8719; +#endif +#ifdef CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8006S_AA +extern struct test_funcs test_func_ft8006sp; +#endif +#if defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8756) || \ + defined (CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8009) +extern struct test_funcs test_func_ft8756; +#endif + +extern struct fts_test *fts_ftest; + +void sys_delay(int ms); +int focal_abs(int value); +void print_buffer(int *buffer, int length, int line_num); +int fts_test_read_reg(u8 addr, u8 *val); +int fts_test_write_reg(u8 addr, u8 val); +int fts_test_read(u8 addr, u8 *readbuf, int readlen); +int fts_test_write(u8 addr, u8 *writebuf, int writelen); +int enter_work_mode(void); +int enter_factory_mode(void); +int read_mass_data(u8 addr, int byte_num, int *buf); +int chip_clb(void); +int wait_state_update(u8 retval); +int get_cb_incell(u16 saddr, int byte_num, int *cb_buf); +int short_get_adcdata_incell(u8 retval, u8 ch_num, int byte_num, int *adc_buf); +int start_scan(void); +int get_rawdata(int *data); +int get_cb_sc(int byte_num, int *cb_buf, enum byte_mode mode); +bool compare_data(int *data, int min, int max, int min_vk, int max_vk, bool key); +bool compare_array(int *data, int *min, int *max, bool key); +void show_data(int *data, bool key); +/* mc_sc */ +int mapping_switch(u8 mapping); +bool get_fw_wp(u8 wp_channel_select, enum wp_type water_proof_type); +int get_cb_mc_sc(u8 wp, int byte_num, int *cb_buf, enum byte_mode mode); +int get_rawdata_mc_sc(enum wp_type wp, int *data); +int get_rawdata_mc(u8 fre, u8 fir, int *rawdata); +int short_get_adc_data_mc(u8 retval, int byte_num, int *adc_buf, u8 mode); +bool compare_mc_sc(bool, bool, int *, int *, int *); +void show_data_mc_sc(int *data); +void *fts_malloc(size_t size); +void fts_free_proc(void *p); +void fts_test_save_data(char *name, int code, int *data, int datacnt, + bool mc_sc, bool key, bool result); + +#define fts_malloc_r(p, size) do {\ + if (NULL == p) {\ + p = fts_malloc(size);\ + if (NULL == p) {\ + return -ENOMEM;\ + }\ + }\ +} while(0) + +#define fts_free(p) do {\ + if (p) {\ + fts_free_proc(p);\ + p = NULL;\ + }\ +} while(0) + +#define CSV_SUPPORT 1 +#define TXT_SUPPORT 1 + +#define FTS_TEST_DBG(fmt, args...) do { \ +printk("[FTS_TS][TEST]%s:"fmt"\n", __func__, ##args); \ +} while (0) + +#define FTS_TEST_FUNC_ENTER() do { \ + printk("[FTS_TS][TEST]%s: Enter\n", __func__); \ +} while (0) + +#define FTS_TEST_FUNC_EXIT() do { \ + printk("[FTS_TS][TEST]%s: Exit(%d)\n", __func__, __LINE__); \ +} while (0) + +#define FTS_TEST_INFO(fmt, args...) do { \ + printk(KERN_ERR "[FTS_TS/I][TEST]%s:"fmt"\n", __func__, ##args); \ +} while (0) + +#define FTS_TEST_ERROR(fmt, args...) do { \ + printk(KERN_ERR "[FTS_TS/E][TEST]%s:"fmt"\n", __func__, ##args); \ +} while (0) + +#define FTS_TEST_SAVE_INFO(fmt, args...) do { \ + if (fts_ftest->testresult) { \ + fts_ftest->testresult_len += snprintf( \ + fts_ftest->testresult + fts_ftest->testresult_len, \ + TXT_BUFFER_LEN, \ + fmt, ##args);\ + } \ +} while (0) + +#define FTS_TEST_SAVE_ERR(fmt, args...) do { \ + if (fts_ftest->testresult && (fts_ftest->testresult_len < TXT_BUFFER_LEN)) { \ + fts_ftest->testresult_len += snprintf( \ + fts_ftest->testresult + fts_ftest->testresult_len, \ + TXT_BUFFER_LEN, \ + fmt, ##args);\ + } \ + printk(KERN_ERR "[FTS_TS/E][TEST]%s:"fmt"\n", __func__, ##args);\ +} while (0) +#endif diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/focaltech_test_ini.c b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/focaltech_test_ini.c new file mode 100755 index 000000000000..f3cfe081224d --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/focaltech_test_ini.c @@ -0,0 +1,1330 @@ +/************************************************************************ +* Copyright (C) 2012-2019, Focaltech Systems (R)£¬All Rights Reserved. +* +* File Name: focaltech_test_ini.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-01 +* +* Abstract: parsing function of INI file +* +************************************************************************/ +#include "focaltech_test.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +struct ini_ic_type ic_types[] = { + {"FT5X46", 0x54000002}, + {"FT5X46i", 0x54010002}, + {"FT5526", 0x54020002}, + {"FT3X17", 0x54030002}, + {"FT5436", 0x54040002}, + {"FT3X27", 0x54050002}, + {"FT5526i", 0x54060002}, + {"FT5416", 0x54070002}, + {"FT5426", 0x54080002}, + {"FT5435", 0x54090002}, + {"FT7681", 0x540A0002}, + {"FT7661", 0x540B0002}, + {"FT7511", 0x540C0002}, + {"FT7421", 0x540D0002}, + {"FT7311", 0x54100002}, + {"FT3327DQQ-001", 0x41000082}, + {"FT5446DQS-W01", 0x40000082}, + + {"FT5452", 0x55000081}, + {"FT3518", 0x55010081}, + {"FT3558", 0x55020081}, + {"FT3528", 0x55030081}, + {"FT5536", 0x55040081}, + + {"FT5472", 0x8F000083}, + {"FT5446U", 0x8F010083}, + {"FT5456U", 0x8F020083}, + {"FT3417U", 0x8F030083}, + {"FT5426U", 0x8F040083}, + {"FT3428", 0x8F050083}, + {"FT3437U", 0x8F060083}, + + {"FT5822", 0x58000001}, + {"FT5626", 0x58010001}, + {"FT5726", 0x58020001}, + {"FT5826B", 0x58030001}, + {"FT3617", 0x58040001}, + {"FT3717", 0x58050001}, + {"FT7811", 0x58060001}, + {"FT5826S", 0x58070001}, + {"FT3517U", 0x58090001}, + {"FT3557", 0x580A0001}, + + {"FT6X36", 0x63000003}, + {"FT3X07", 0x63010003}, + {"FT6416", 0x63020003}, + {"FT6336G/U", 0x63030003}, + {"FT7401", 0x63040003}, + {"FT3407U", 0x63050003}, + {"FT6236U", 0x63060003}, + {"FT6436U", 0x63070003}, + + {"FT3267", 0x63080004}, + {"FT3367", 0x63090004}, + + {"FT6216", 0x64000084}, + {"FT7302", 0x64010084}, + {"FT7202", 0x64020084}, + {"FT3308", 0x64030084}, + + {"FT8607", 0x81000009}, + {"FT8716", 0x82000005}, + {"FT8716U", 0x44000005}, + {"FT8716F", 0x8A000005}, + {"FT8613", 0x4500000C}, + + {"FT8736", 0x83000006}, + + {"FT8006M", 0x87000007}, + {"FT8201", 0x87010010}, + {"FT7250", 0x87020007}, + + {"FT8006U", 0x8900000B}, + {"FT8006S", 0x8901000B}, + {"FT8006S-AA", 0x9B000019}, + + {"FT8719", 0x8E00000D}, + {"FT8615", 0x9100000F}, + + {"FT8739", 0x8D00000E}, + + {"FT8006P", 0x93000011}, + + {"FT7251", 0x8C000012}, + {"FT7252", 0x92000013}, + + {"FT8613S", 0x94000014}, + + {"FT8756", 0x95000015}, + {"FT8656", 0x95010018}, + + {"FT8302", 0x97000016}, + + {"FT8009", 0x98000017}, + + {"FT3068", 0x65010085}, + {"FT3168", 0x65020085}, + {"FT3067", 0x65030085}, + {"FT3268", 0x65040085}, + {"FT6346U", 0x65050085}, + {"FT6346G", 0x65060085}, +}; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +/* Works only for digits and letters, but small and fast */ +#define TOLOWER(x) ((x) | 0x20) +static int fts_strncmp(const char *cs, const char *ct, int count) +{ + u8 c1 = 0, c2 = 0; + + while (count) { + if ((cs == NULL) || (ct == NULL)) + return -1; + c1 = TOLOWER(*cs++); + c2 = TOLOWER(*ct++); + if (c1 != c2) + return c1 < c2 ? -1 : 1; + if (!c1) + break; + count--; + } + + return 0; +} + +static int isspace(int x) +{ + if (x == ' ' || x == '\t' || x == '\n' || x == '\f' || x == '\b' || x == '\r') + return 1; + else + return 0; +} + +static int isdigit(int x) +{ + if (x <= '9' && x >= '0') + return 1; + else + return 0; +} + +static long fts_atol(char *nptr) +{ + int c; /* current char */ + long total; /* current total */ + int sign; /* if ''-'', then negative, otherwise positive */ + /* skip whitespace */ + while ( isspace((int)(unsigned char)*nptr) ) + ++nptr; + c = (int)(unsigned char) * nptr++; + sign = c; /* save sign indication */ + if (c == '-' || c == '+') + c = (int)(unsigned char) * nptr++; /* skip sign */ + total = 0; + while (isdigit(c)) { + total = 10 * total + (c - '0'); /* accumulate digit */ + c = (int)(unsigned char) * nptr++; /* get next char */ + } + if (sign == '-') + return -total; + else + return total; /* return result, negated if necessary */ +} + +static int fts_atoi(char *nptr) +{ + return (int)fts_atol(nptr); +} + +static int fts_test_get_ini_size(char *config_name) +{ + struct file *pfile = NULL; + struct inode *inode = NULL; + off_t fsize = 0; + char filepath[128]; + + FTS_TEST_FUNC_ENTER(); + + memset(filepath, 0, sizeof(filepath)); + sprintf(filepath, "%s%s", FTS_INI_FILE_PATH, config_name); + + if (NULL == pfile) + pfile = filp_open(filepath, O_RDONLY, 0); + if (IS_ERR(pfile)) { + FTS_TEST_ERROR("error occured while opening file %s.", filepath); + return -EIO; + } + +#if 1 + inode = pfile->f_inode; +#else + /* reserved for linux earlier verion */ + inode = pfile->f_dentry->d_inode; +#endif + fsize = inode->i_size; + filp_close(pfile, NULL); + + FTS_TEST_FUNC_ENTER(); + + return fsize; +} + +static int fts_test_read_ini_data(char *config_name, char *config_buf) +{ + struct file *pfile = NULL; + struct inode *inode = NULL; + off_t fsize = 0; + char filepath[128]; + loff_t pos = 0; + mm_segment_t old_fs; + + FTS_TEST_FUNC_ENTER(); + + memset(filepath, 0, sizeof(filepath)); + sprintf(filepath, "%s%s", FTS_INI_FILE_PATH, config_name); + if (NULL == pfile) { + pfile = filp_open(filepath, O_RDONLY, 0); + } + if (IS_ERR(pfile)) { + FTS_TEST_ERROR("error occured while opening file %s.", filepath); + return -EIO; + } + +#if 1 + inode = pfile->f_inode; +#else + /* reserved for linux earlier verion */ + inode = pfile->f_dentry->d_inode; +#endif + fsize = inode->i_size; + old_fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + vfs_read(pfile, config_buf, fsize, &pos); + filp_close(pfile, NULL); + set_fs(old_fs); + + FTS_TEST_FUNC_EXIT(); + return 0; +} + +static int fts_test_get_ini_default(struct ini_data *ini, char *fwname) +{ + int ret = 0; + int inisize = 0; + + inisize = fts_test_get_ini_size(fwname); + FTS_TEST_DBG("ini file size:%d", inisize); + if (inisize <= 0) { + FTS_TEST_ERROR("get ini file size fail"); + return -ENODATA; + } + + ini->data = vmalloc(inisize + 1); + if (NULL == ini->data) { + FTS_TEST_ERROR("malloc memory for ini data fail"); + return -ENOMEM; + } + memset(ini->data, 0, inisize + 1); + ini->length = inisize + 1; + + ret = fts_test_read_ini_data(fwname, ini->data); + if (ret) { + FTS_TEST_ERROR("read ini file fail"); + return -ENODATA; + } + ini->data[inisize] = '\n'; /* last line is null line */ + + return 0; +} + +static int fts_test_get_ini_via_request_firmware(struct ini_data *ini, char *fwname) +{ + int ret = 0; + const struct firmware *fw = NULL; + struct device *dev = &fts_data->input_dev->dev; + + ret = request_firmware(&fw, fwname, dev); + if (0 == ret) { + FTS_TEST_INFO("firmware request(%s) success", fwname); + ini->data = vmalloc(fw->size + 1); + if (NULL == ini->data) { + FTS_TEST_ERROR("ini->data buffer vmalloc fail"); + ret = -ENOMEM; + } else { + memcpy(ini->data, fw->data, fw->size); + ini->data[fw->size] = '\n'; + ini->length = fw->size + 1; + } + } else { + FTS_TEST_INFO("firmware request(%s) fail,ret=%d", fwname, ret); + } + + if (fw != NULL) { + release_firmware(fw); + fw = NULL; + } + + return ret; +} + + +static void str_space_remove(char *str) +{ + char *t = str; + char *s = str; + + while (*t != '\0') { + if (*t != ' ') { + *s = *t; + s++; + } + t++; + } + + *s = '\0'; +} + +static void print_ini_data(struct ini_data *ini) +{ + int i = 0; + int j = 0; + struct ini_section *section = NULL; + struct ini_keyword *keyword = NULL; + struct fts_test *tdata = fts_ftest; + + if (tdata && tdata->ts_data && (tdata->ts_data->log_level < 10)) { + return; + } + + if (!ini || !ini->tmp) { + FTS_TEST_DBG("ini is null"); + return; + } + + FTS_TEST_DBG("section num:%d, keyword num total:%d", + ini->section_num, ini->keyword_num_total); + for (i = 0; i < ini->section_num; i++) { + section = &ini->section[i]; + FTS_TEST_DBG("section name:[%s] keyword num:%d", + section->name, section->keyword_num); + for (j = 0; j < section->keyword_num; j++) { + keyword = §ion->keyword[j]; + FTS_TEST_DBG("%s=%s", keyword->name, keyword->value); + } + } +} + +static int ini_get_line(char *filedata, char *line_data, int *line_len) +{ + int i = 0; + int line_length = 0; + int type; + + /* get a line data */ + for (i = 0; i < MAX_INI_LINE_LEN; i++) { + if (('\n' == filedata[i]) || ('\r' == filedata[i])) { + line_data[line_length++] = '\0'; + if (('\n' == filedata[i + 1]) || ('\r' == filedata[i + 1])) { + line_length++; + } + break; + } else { + line_data[line_length++] = filedata[i]; + } + } + + if (i >= MAX_INI_LINE_LEN) { + FTS_TEST_ERROR("line length(%d)>max(%d)", line_length, MAX_INI_LINE_LEN); + return -ENODATA; + } + + /* remove space */ + str_space_remove(line_data); + + /* confirm line type */ + if (('\0' == line_data[0]) || ('#' == line_data[0])) { + type = LINE_OTHER; + } else if ('[' == line_data[0]) { + type = LINE_SECTION; + } else { + type = LINE_KEYWORD; /* key word */ + } + + *line_len = line_length; + return type; +} + +static int ini_parse_keyword(struct ini_data *ini, char *line_buffer) +{ + int i = 0; + int offset = 0; + int length = strlen(line_buffer); + struct ini_section *section = NULL; + + for (i = 0; i < length; i++) { + if (line_buffer[i] == '=') + break; + } + + if ((i == 0) || (i >= length)) { + FTS_TEST_ERROR("mark(=)in keyword line fail"); + return -ENODATA; + } + + if ((ini->section_num > 0) && (ini->section_num < MAX_INI_SECTION_NUM)) { + section = &ini->section[ini->section_num - 1]; + } + + if (NULL == section) { + FTS_TEST_ERROR("section is null"); + return -ENODATA; + } + + offset = ini->keyword_num_total; + if (offset > MAX_KEYWORD_NUM) { + FTS_TEST_ERROR("keyword num(%d)>max(%d),please check MAX_KEYWORD_NUM", + ini->keyword_num_total, MAX_KEYWORD_NUM); + return -ENODATA; + } + memcpy(ini->tmp[offset].name, &line_buffer[0], i); + ini->tmp[offset].name[i] = '\0'; + memcpy(ini->tmp[offset].value, &line_buffer[i + 1], length - i - 1); + ini->tmp[offset].value[length - i - 1] = '\0'; + section->keyword_num++; + ini->keyword_num_total++; + + return 0; +} + +static int ini_parse_section(struct ini_data *ini, char *line_buffer) +{ + int length = strlen(line_buffer); + struct ini_section *section = NULL; + + if ((length <= 2) || (length > MAX_KEYWORD_NAME_LEN)) { + FTS_TEST_ERROR("section line length fail"); + return -EINVAL; + } + + if ((ini->section_num < 0) || (ini->section_num >= MAX_INI_SECTION_NUM)) { + FTS_TEST_ERROR("section_num(%d) fail", ini->section_num); + return -EINVAL; + } + section = &ini->section[ini->section_num]; + memcpy(section->name, line_buffer + 1, length - 2); + section->name[length - 2] = '\0'; + FTS_TEST_INFO("section:%s, keyword offset:%d", + section->name, ini->keyword_num_total); + section->keyword = (struct ini_keyword *)&ini->tmp[ini->keyword_num_total]; + section->keyword_num = 0; + ini->section_num++; + if (ini->section_num > MAX_INI_SECTION_NUM) { + FTS_TEST_ERROR("section num(%d)>max(%d), please check MAX_INI_SECTION_NUM", + ini->section_num, MAX_INI_SECTION_NUM); + return -ENOMEM; + } + + return 0; +} + +static int ini_init_inidata(struct ini_data *ini) +{ + int pos = 0; + int ret = 0; + char line_buffer[MAX_INI_LINE_LEN] = { 0 }; + int line_len = 0; + + if (!ini || !ini->data || !ini->tmp) { + FTS_TEST_DBG("ini/data/tmp is null"); + return -EINVAL; + } + + while (pos < ini->length) { + ret = ini_get_line(ini->data + pos, line_buffer, &line_len); + if (ret < 0) { + FTS_TEST_ERROR("ini_get_line fail"); + return ret; + } else if (ret == LINE_KEYWORD) { + ret = ini_parse_keyword(ini, line_buffer); + if (ret < 0) { + FTS_TEST_ERROR("ini_parse_keyword fail"); + return ret; + } + } else if (ret == LINE_SECTION) { + ret = ini_parse_section(ini, line_buffer); + if (ret < 0) { + FTS_TEST_ERROR("ini_parse_section fail"); + return ret; + } + } + + pos += line_len; + } + + print_ini_data(ini); + return 0; +} + +static int ini_get_key(char *section_name, char *key_name, char *value) +{ + int i = 0; + int j = 0; + struct ini_data *ini = &fts_ftest->ini; + struct ini_section *section; + struct ini_keyword *keyword; + int key_len = 0; + int log_level = fts_ftest->ts_data->log_level; + + if (log_level >= 10) { + FTS_TEST_DBG("section name:%s, key name:%s", section_name, key_name); + FTS_TEST_DBG("section num:%d", ini->section_num); + } + + for (i = 0; i < ini->section_num; i++) { + section = &ini->section[i]; + key_len = strlen(section_name); + if (key_len != strlen(section->name)) + continue; + if (fts_strncmp(section->name, section_name, key_len) != 0) + continue; + + if (log_level >= 10) { + FTS_TEST_DBG("section name:%s keyword num:%d", + section->name, section->keyword_num); + } + for (j = 0; j < section->keyword_num; j++) { + keyword = §ion->keyword[j]; + key_len = strlen(key_name); + if (key_len == strlen(keyword->name)) { + if (0 == fts_strncmp(keyword->name, key_name, key_len)) { + key_len = strlen(keyword->value); + memcpy(value, keyword->value, key_len); + if (log_level >= 3) { + FTS_TEST_DBG("section:%s,%s=%s", + section_name, key_name, value); + } + + return key_len; + } + } + } + } + + return -ENODATA; +} + +/* return keyword's value length if success */ +static int ini_get_string_value(char *section_name, char *key_name, char *rval) +{ + if (!section_name || !key_name || !rval) { + FTS_TEST_ERROR("section_name/key_name/rval is null"); + return -EINVAL; + } + + return ini_get_key(section_name, key_name, rval); +} + +int get_keyword_value(char *section, char *name, int *value) +{ + int ret = 0; + char str[MAX_KEYWORD_VALUE_LEN] = { 0 }; + + ret = ini_get_string_value(section, name, str); + if (ret > 0) { + /* search successfully, so change value, otherwise keep default */ + *value = fts_atoi(str); + } + + return ret; +} + +static void fts_init_buffer(int *buffer, int value, int len, bool key_check, int key_value, int key_len) +{ + int i = 0; + int va_len = 0; + + if (NULL == buffer) { + FTS_TEST_ERROR("buffer is null\n"); + return; + } + + va_len = len - key_len; + if (va_len < 0) { + FTS_TEST_ERROR("total len(0x%x) less key len(0x%x)\n", len, key_len); + return; + } + + for (i = 0; i < len; i++) { + buffer[i] = value; + } + + if (key_check) { + for (i = 0; i < key_len; i++) { + buffer[va_len + i] = key_value; + } + } + +} + +static int get_test_item(char name[][MAX_KEYWORD_NAME_LEN], int length, int *val) +{ + int i = 0; + int ret = 0; + int tmpval = 0; + + if (length > TEST_ITEM_COUNT_MAX) { + FTS_TEST_SAVE_ERR("test item count(%d) > max(%d)\n", + length, TEST_ITEM_COUNT_MAX); + return -EINVAL; + } + + FTS_TEST_INFO("test items in total of driver:%d", length); + *val = 0; + for (i = 0; i < length; i++) { + tmpval = 0; + ret = get_value_testitem(name[i], &tmpval); + if (ret < 0) { + FTS_TEST_DBG("test item:%s not found", name[i]); + } else { + FTS_TEST_DBG("test item:%s=%d", name[i], tmpval); + *val |= (tmpval << i); + } + } + + return 0; +} + +static int get_basic_threshold(char name[][MAX_KEYWORD_NAME_LEN], int length, int *val) +{ + int i = 0; + int ret = 0; + struct fts_test *tdata = fts_ftest; + int log_level = tdata->ts_data->log_level; + + FTS_TEST_INFO("basic_thr string length(%d), count(%d)\n", length, tdata->basic_thr_count); + if (length > fts_ftest->basic_thr_count) { + FTS_TEST_SAVE_ERR("basic_thr string length > count\n"); + return -EINVAL; + } + + for (i = 0; i < length; i++) { + ret = get_value_basic(name[i], &val[i]); + if (log_level >= 3) { + if (ret < 0) { + FTS_TEST_DBG("basic thr:%s not found", name[i]); + } else { + FTS_TEST_DBG("basic thr:%s=%d", name[i], val[i]); + } + } + } + + return 0; +} + +static void get_detail_threshold(char *key_name, bool is_prex, int *thr) +{ + char str[MAX_KEYWORD_VALUE_LEN] = { 0 }; + char str_temp[MAX_KEYWORD_NAME_LEN] = { 0 }; + char str_tmp[MAX_KEYWORD_VALUE_ONE_LEN] = { 0 }; + struct fts_test *tdata = fts_ftest; + int divider_pos = 0; + int index = 0; + int i = 0; + int j = 0; + int k = 0; + int tx_num = 0; + int rx_num = 0; + + if (!key_name || !thr) { + FTS_TEST_ERROR("key_name/thr is null"); + return; + } + + if (is_prex) { + tx_num = tdata->node.tx_num; + rx_num = tdata->node.rx_num; + } + for (i = 0; i < tx_num + 1; i++) { + if (is_prex) { + snprintf(str_temp, MAX_KEYWORD_NAME_LEN, "%s%d", key_name, (i + 1)); + } else { + snprintf(str_temp, MAX_KEYWORD_NAME_LEN, "%s", key_name); + } + divider_pos = ini_get_string_value("SpecialSet", str_temp, str); + if (divider_pos <= 0) + continue; + index = 0; + k = 0; + memset(str_tmp, 0, sizeof(str_tmp)); + for (j = 0; j < divider_pos; j++) { + if (',' == str[j]) { + thr[i * rx_num + k] = (short)(fts_atoi(str_tmp)); + index = 0; + memset(str_tmp, 0x00, sizeof(str_tmp)); + k++; + } else { + if (' ' == str[j]) + continue; + str_tmp[index] = str[j]; + index++; + } + } + } +} + +static int init_node_valid(void) +{ + char str[MAX_KEYWORD_NAME_LEN] = {0}; + int i = 0; + int j = 0; + int chy = 0; + int node_num = 0; + int cnt = 0; + struct fts_test *tdata = fts_ftest; + + if (!tdata || !tdata->node_valid || !tdata->node_valid_sc) { + FTS_TEST_ERROR("tdata/node_valid/node_valid_sc is null"); + return -EINVAL; + } + + chy = tdata->node.rx_num; + node_num = tdata->node.node_num; + fts_init_buffer(tdata->node_valid, 1 , node_num, false, 0, 0); + if ((tdata->func->hwtype == IC_HW_INCELL) || (tdata->func->hwtype == IC_HW_MC_SC)) { + for (cnt = 0; cnt < node_num; cnt++) { + i = cnt / chy + 1; + j = cnt % chy + 1; + snprintf(str, MAX_KEYWORD_NAME_LEN, "InvalidNode[%d][%d]", i, j); + get_keyword_value("INVALID_NODE", str, &tdata->node_valid[cnt]); + } + } + + if (tdata->func->hwtype == IC_HW_MC_SC) { + chy = tdata->sc_node.rx_num; + node_num = tdata->sc_node.node_num; + fts_init_buffer(tdata->node_valid_sc, 1, node_num, false, 0, 0); + + for (cnt = 0; cnt < node_num; cnt++) { + i = (cnt >= chy) ? 2 : 1; + j = (cnt >= chy) ? (cnt - chy + 1) : (cnt + 1); + snprintf(str, MAX_KEYWORD_NAME_LEN, "InvalidNodeS[%d][%d]", i, j); + get_keyword_value("INVALID_NODES", str, &tdata->node_valid_sc[cnt]); + } + } + + print_buffer(tdata->node_valid, tdata->node.node_num, tdata->node.rx_num); + print_buffer(tdata->node_valid_sc, tdata->sc_node.node_num, tdata->sc_node.rx_num); + return 0; +} + +/* incell */ +static int get_test_item_incell(void) +{ + int ret = 0; + char item_name[][MAX_KEYWORD_NAME_LEN] = TEST_ITEM_INCELL; + int length = sizeof(item_name) / MAX_KEYWORD_NAME_LEN; + int item_val = 0; + + ret = get_test_item(item_name, length, &item_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get test item fail\n"); + return ret; + } + + fts_ftest->ic.incell.u.tmp = item_val; + return 0; +} + +static char bthr_name_incell[][MAX_KEYWORD_NAME_LEN] = BASIC_THRESHOLD_INCELL; +static int get_test_threshold_incell(void) +{ + int ret = 0; + int length = sizeof(bthr_name_incell) / MAX_KEYWORD_NAME_LEN; + struct fts_test *tdata = fts_ftest; + struct incell_threshold *thr = &tdata->ic.incell.thr; + int node_num = tdata->node.node_num; + int key_num = tdata->node.key_num; + bool raw_key_check = thr->basic.rawdata_vkey_check; + bool cb_key_check = thr->basic.cb_vkey_check; + + tdata->basic_thr_count = sizeof(struct incell_threshold_b) / sizeof(int); + /* get standard basic threshold */ + ret = get_basic_threshold(bthr_name_incell, length, (int *)&thr->basic); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get basic thr fail\n"); + return ret; + } + + /* basic special set by ic */ + if (tdata->func->param_init) { + ret = tdata->func->param_init(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("special basic thr init fail\n"); + return ret; + } + } + + /* init buffer */ + fts_init_buffer(thr->rawdata_max, thr->basic.rawdata_max, node_num, raw_key_check, thr->basic.rawdata_max_vk, key_num); + fts_init_buffer(thr->rawdata_min, thr->basic.rawdata_min, node_num, raw_key_check, thr->basic.rawdata_min_vk, key_num); + if (tdata->func->rawdata2_support) { + fts_init_buffer(thr->rawdata2_max, thr->basic.rawdata2_max, node_num, false, 0, 0); + fts_init_buffer(thr->rawdata2_min, thr->basic.rawdata2_min, node_num, false, 0, 0); + } + fts_init_buffer(thr->cb_max, thr->basic.cb_max, node_num, cb_key_check, thr->basic.cb_max_vk, key_num); + fts_init_buffer(thr->cb_min, thr->basic.cb_min, node_num, cb_key_check, thr->basic.cb_min_vk, key_num); + + /* detail threshold */ + get_detail_threshold("RawData_Max_Tx", true, thr->rawdata_max); + get_detail_threshold("RawData_Min_Tx", true, thr->rawdata_min); + get_detail_threshold("CB_Max_Tx", true, thr->cb_max); + get_detail_threshold("CB_Min_Tx", true, thr->cb_min); + + return 0; +} + +static void print_thr_incell(void) +{ + struct fts_test *tdata = fts_ftest; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + if (tdata->ts_data->log_level < 3) { + return; + } + + FTS_TEST_DBG("short_res_min:%d", thr->basic.short_res_min); + FTS_TEST_DBG("short_res_vk_min:%d", thr->basic.short_res_vk_min); + FTS_TEST_DBG("open_cb_min:%d", thr->basic.open_cb_min); + FTS_TEST_DBG("open_k1_check:%d", thr->basic.open_k1_check); + FTS_TEST_DBG("open_k1_value:%d", thr->basic.open_k1_value); + FTS_TEST_DBG("open_k2_check:%d", thr->basic.open_k2_check); + FTS_TEST_DBG("open_k2_value:%d", thr->basic.open_k2_value); + FTS_TEST_DBG("cb_min:%d", thr->basic.cb_min); + FTS_TEST_DBG("cb_max:%d", thr->basic.cb_max); + FTS_TEST_DBG("cb_vkey_check:%d", thr->basic.cb_vkey_check); + FTS_TEST_DBG("cb_min_vk:%d", thr->basic.cb_min_vk); + FTS_TEST_DBG("cb_max_vk:%d", thr->basic.cb_max_vk); + FTS_TEST_DBG("rawdata_min:%d", thr->basic.rawdata_min); + FTS_TEST_DBG("rawdata_max:%d", thr->basic.rawdata_max); + FTS_TEST_DBG("rawdata_vkey_check:%d", thr->basic.rawdata_vkey_check); + FTS_TEST_DBG("rawdata_min_vk:%d", thr->basic.rawdata_min_vk); + FTS_TEST_DBG("rawdata_max_vk:%d", thr->basic.rawdata_max_vk); + FTS_TEST_DBG("lcdnoise_frame:%d", thr->basic.lcdnoise_frame); + FTS_TEST_DBG("lcdnoise_coefficient:%d", thr->basic.lcdnoise_coefficient); + FTS_TEST_DBG("lcdnoise_coefficient_vkey:%d", thr->basic.lcdnoise_coefficient_vkey); + + FTS_TEST_DBG("open_nmos:%d", thr->basic.open_nmos); + FTS_TEST_DBG("keyshort_k1:%d", thr->basic.keyshort_k1); + FTS_TEST_DBG("keyshort_cb_max:%d", thr->basic.keyshort_cb_max); + FTS_TEST_DBG("rawdata2_min:%d", thr->basic.rawdata2_min); + FTS_TEST_DBG("rawdata2_max:%d", thr->basic.rawdata2_max); + + + print_buffer(thr->rawdata_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->cb_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->cb_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata2_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata2_max, tdata->node.node_num, tdata->node.rx_num); +} + +static int ini_init_test_incell(void) +{ + int ret = 0; + + ret = get_test_item_incell(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get incell test item fail\n"); + return ret; + } + + + ret = get_test_threshold_incell(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get incell threshold fail\n"); + return ret; + } + + print_thr_incell(); + return 0; +} + +/* mc_sc */ +static int get_test_item_mc_sc(void) +{ + int ret = 0; + char item_name[][MAX_KEYWORD_NAME_LEN] = TEST_ITEM_MC_SC; + int length = sizeof(item_name) / MAX_KEYWORD_NAME_LEN; + int item_val = 0; + + ret = get_test_item(item_name, length, &item_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get test item fail\n"); + return ret; + } + + fts_ftest->ic.mc_sc.u.tmp = item_val; + FTS_TEST_INFO("test item:0x%x in ini", fts_ftest->ic.mc_sc.u.tmp); + return 0; +} + +static char bthr_name_mc_sc[][MAX_KEYWORD_NAME_LEN] = BASIC_THRESHOLD_MC_SC; +static int get_test_threshold_mc_sc(void) +{ + int ret = 0; + int length = sizeof(bthr_name_mc_sc) / MAX_KEYWORD_NAME_LEN; + struct fts_test *tdata = fts_ftest; + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + int node_num = tdata->node.node_num; + int sc_num = tdata->sc_node.node_num; + + tdata->basic_thr_count = sizeof(struct mc_sc_threshold_b) / sizeof(int); + /* get standard basic threshold */ + ret = get_basic_threshold(bthr_name_mc_sc, length, (int *)&thr->basic); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get basic thr fail\n"); + return ret; + } + + /* basic special set by ic */ + if (tdata->func->param_init) { + ret = tdata->func->param_init(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("special basic thr init fail\n"); + return ret; + } + } + + /* init buffer */ + fts_init_buffer(thr->rawdata_h_min, thr->basic.rawdata_h_min, node_num, false, 0, 0); + fts_init_buffer(thr->rawdata_h_max, thr->basic.rawdata_h_max, node_num, false, 0, 0); + if (tdata->func->rawdata2_support) { + fts_init_buffer(thr->rawdata_l_min, thr->basic.rawdata_l_min, node_num, false, 0, 0); + fts_init_buffer(thr->rawdata_l_max, thr->basic.rawdata_l_max, node_num, false, 0, 0); + } + fts_init_buffer(thr->tx_linearity_max, thr->basic.uniformity_tx_hole, node_num, false, 0, 0); + fts_init_buffer(thr->tx_linearity_min, 0, node_num, false, 0, 0); + fts_init_buffer(thr->rx_linearity_max, thr->basic.uniformity_rx_hole, node_num, false, 0, 0); + fts_init_buffer(thr->rx_linearity_min, 0, node_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_off_min, thr->basic.scap_cb_off_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_off_max, thr->basic.scap_cb_off_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_on_min, thr->basic.scap_cb_on_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_on_max, thr->basic.scap_cb_on_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_off_min, thr->basic.scap_rawdata_off_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_off_max, thr->basic.scap_rawdata_off_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_on_min, thr->basic.scap_rawdata_on_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_on_max, thr->basic.scap_rawdata_on_max, sc_num, false, 0, 0); + fts_init_buffer(thr->panel_differ_min, thr->basic.panel_differ_min, node_num, false, 0, 0); + fts_init_buffer(thr->panel_differ_max, thr->basic.panel_differ_max, node_num, false, 0, 0); + + /* detail threshold */ + get_detail_threshold("RawData_Min_High_Tx", true, thr->rawdata_h_min); + get_detail_threshold("RawData_Max_High_Tx", true, thr->rawdata_h_max); + if (tdata->func->rawdata2_support) { + get_detail_threshold("RawData_Min_Low_Tx", true, thr->rawdata_l_min); + get_detail_threshold("RawData_Max_Low_Tx", true, thr->rawdata_l_max); + } + get_detail_threshold("Tx_Linearity_Max_Tx", true, thr->tx_linearity_max); + get_detail_threshold("Rx_Linearity_Max_Tx", true, thr->rx_linearity_max); + get_detail_threshold("ScapCB_OFF_Min_", true, thr->scap_cb_off_min); + get_detail_threshold("ScapCB_OFF_Max_", true, thr->scap_cb_off_max); + get_detail_threshold("ScapCB_ON_Min_", true, thr->scap_cb_on_min); + get_detail_threshold("ScapCB_ON_Max_", true, thr->scap_cb_on_max); + get_detail_threshold("ScapRawData_OFF_Min_", true, thr->scap_rawdata_off_min); + get_detail_threshold("ScapRawData_OFF_Max_", true, thr->scap_rawdata_off_max); + get_detail_threshold("ScapRawData_ON_Min_", true, thr->scap_rawdata_on_min); + get_detail_threshold("ScapRawData_ON_Max_", true, thr->scap_rawdata_on_max); + get_detail_threshold("Panel_Differ_Min_Tx", true, thr->panel_differ_min); + get_detail_threshold("Panel_Differ_Max_Tx", true, thr->panel_differ_max); + + return 0; +} + +static void print_thr_mc_sc(void) +{ + struct fts_test *tdata = fts_ftest; + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + + if (tdata->ts_data->log_level < 3) { + return; + } + + FTS_TEST_DBG("rawdata_h_min:%d", thr->basic.rawdata_h_min); + FTS_TEST_DBG("rawdata_h_max:%d", thr->basic.rawdata_h_max); + FTS_TEST_DBG("rawdata_set_hfreq:%d", thr->basic.rawdata_set_hfreq); + FTS_TEST_DBG("rawdata_l_min:%d", thr->basic.rawdata_l_min); + FTS_TEST_DBG("rawdata_l_max:%d", thr->basic.rawdata_l_max); + FTS_TEST_DBG("rawdata_set_lfreq:%d", thr->basic.rawdata_set_lfreq); + FTS_TEST_DBG("uniformity_check_tx:%d", thr->basic.uniformity_check_tx); + FTS_TEST_DBG("uniformity_check_rx:%d", thr->basic.uniformity_check_rx); + FTS_TEST_DBG("uniformity_check_min_max:%d", thr->basic.uniformity_check_min_max); + FTS_TEST_DBG("uniformity_tx_hole:%d", thr->basic.uniformity_tx_hole); + FTS_TEST_DBG("uniformity_rx_hole:%d", thr->basic.uniformity_rx_hole); + FTS_TEST_DBG("uniformity_min_max_hole:%d", thr->basic.uniformity_min_max_hole); + FTS_TEST_DBG("scap_cb_off_min:%d", thr->basic.scap_cb_off_min); + FTS_TEST_DBG("scap_cb_off_max:%d", thr->basic.scap_cb_off_max); + FTS_TEST_DBG("scap_cb_wp_off_check:%d", thr->basic.scap_cb_wp_off_check); + FTS_TEST_DBG("scap_cb_on_min:%d", thr->basic.scap_cb_on_min); + FTS_TEST_DBG("scap_cb_on_max:%d", thr->basic.scap_cb_on_max); + FTS_TEST_DBG("scap_cb_wp_on_check:%d", thr->basic.scap_cb_wp_on_check); + FTS_TEST_DBG("scap_rawdata_off_min:%d", thr->basic.scap_rawdata_off_min); + FTS_TEST_DBG("scap_rawdata_off_max:%d", thr->basic.scap_rawdata_off_max); + FTS_TEST_DBG("scap_rawdata_wp_off_check:%d", thr->basic.scap_rawdata_wp_off_check); + FTS_TEST_DBG("scap_rawdata_on_min:%d", thr->basic.scap_rawdata_on_min); + FTS_TEST_DBG("scap_rawdata_on_max:%d", thr->basic.scap_rawdata_on_max); + FTS_TEST_DBG("scap_rawdata_wp_on_check:%d", thr->basic.scap_rawdata_wp_on_check); + FTS_TEST_DBG("short_cg:%d", thr->basic.short_cg); + FTS_TEST_DBG("short_cc:%d", thr->basic.short_cc); + FTS_TEST_DBG("panel_differ_min:%d", thr->basic.panel_differ_min); + FTS_TEST_DBG("panel_differ_max:%d", thr->basic.panel_differ_max); + + print_buffer(thr->rawdata_h_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_h_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_l_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_l_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->scap_cb_off_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_off_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_on_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_on_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_off_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_off_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_on_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_on_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->panel_differ_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->panel_differ_max, tdata->node.node_num, tdata->node.rx_num); +} + +static int ini_init_test_mc_sc(void) +{ + int ret = 0; + + ret = get_test_item_mc_sc(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get mc_sc test item fail\n"); + return ret; + } + + ret = get_test_threshold_mc_sc(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get mc_sc threshold fail\n"); + return ret; + } + + print_thr_mc_sc(); + return 0; +} + +/* sc */ +static int get_test_item_sc(void) +{ + int ret = 0; + char item_name[][MAX_KEYWORD_NAME_LEN] = TEST_ITEM_SC; + int length = sizeof(item_name) / MAX_KEYWORD_NAME_LEN; + int item_val = 0; + + ret = get_test_item(item_name, length, &item_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get test item fail\n"); + return ret; + } + + fts_ftest->ic.sc.u.tmp = item_val; + return 0; +} + +static char bthr_name_sc[][MAX_KEYWORD_NAME_LEN] = BASIC_THRESHOLD_SC; +static int get_test_threshold_sc(void) +{ + int ret = 0; + int length = sizeof(bthr_name_sc) / MAX_KEYWORD_NAME_LEN; + struct fts_test *tdata = fts_ftest; + struct sc_threshold *thr = &tdata->ic.sc.thr; + int node_num = tdata->node.node_num; + + tdata->basic_thr_count = sizeof(struct sc_threshold_b) / sizeof(int); + /* get standard basic threshold */ + ret = get_basic_threshold(bthr_name_sc, length, (int *)&thr->basic); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get basic thr fail\n"); + return ret; + } + + /* basic special set by ic */ + if (tdata->func->param_init) { + ret = tdata->func->param_init(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("special basic thr init fail\n"); + return ret; + } + } + + /* init buffer */ + fts_init_buffer(thr->rawdata_min, thr->basic.rawdata_min, node_num, false, 0, 0); + fts_init_buffer(thr->rawdata_max, thr->basic.rawdata_max, node_num, false, 0, 0); + fts_init_buffer(thr->cb_min, thr->basic.cb_min, node_num, false, 0, 0); + fts_init_buffer(thr->cb_max, thr->basic.cb_max, node_num, false, 0, 0); + fts_init_buffer(thr->dcb_sort, 0, node_num, false, 0, 0); + fts_init_buffer(thr->dcb_base, thr->basic.dcb_base, node_num, false, 0, 0); + + /* detail threshold */ + get_detail_threshold("RawDataTest_Min", false, thr->rawdata_min); + get_detail_threshold("RawDataTest_Max", false, thr->rawdata_max); + get_detail_threshold("CbTest_Min", false, thr->cb_min); + get_detail_threshold("CbTest_Max", false, thr->cb_max); + get_detail_threshold("DeltaCxTest_Sort", false, thr->dcb_sort); + get_detail_threshold("DeltaCbTest_Base", false, thr->dcb_base); + + return 0; +} + +static void print_thr_sc(void) +{ + struct fts_test *tdata = fts_ftest; + struct sc_threshold *thr = &tdata->ic.sc.thr; + + if (tdata->ts_data->log_level < 3) { + return; + } + + FTS_TEST_DBG("rawdata_min:%d", thr->basic.rawdata_min); + FTS_TEST_DBG("rawdata_max:%d", thr->basic.rawdata_max); + FTS_TEST_DBG("cb_min:%d", thr->basic.cb_min); + FTS_TEST_DBG("cb_max:%d", thr->basic.cb_max); + FTS_TEST_DBG("dcb_differ_max:%d", thr->basic.dcb_differ_max); + FTS_TEST_DBG("dcb_key_check:%d", thr->basic.dcb_key_check); + FTS_TEST_DBG("dcb_key_differ_max:%d", thr->basic.dcb_key_differ_max); + FTS_TEST_DBG("dcb_ds1:%d", thr->basic.dcb_ds1); + FTS_TEST_DBG("dcb_ds2:%d", thr->basic.dcb_ds2); + FTS_TEST_DBG("dcb_ds3:%d", thr->basic.dcb_ds3); + FTS_TEST_DBG("dcb_ds4:%d", thr->basic.dcb_ds4); + FTS_TEST_DBG("dcb_ds5:%d", thr->basic.dcb_ds5); + FTS_TEST_DBG("dcb_ds6:%d", thr->basic.dcb_ds6); + FTS_TEST_DBG("dcb_critical_check:%d", thr->basic.dcb_critical_check); + FTS_TEST_DBG("dcb_cs1:%d", thr->basic.dcb_cs1); + FTS_TEST_DBG("dcb_cs2:%d", thr->basic.dcb_cs2); + FTS_TEST_DBG("dcb_cs3:%d", thr->basic.dcb_cs3); + FTS_TEST_DBG("dcb_cs4:%d", thr->basic.dcb_cs4); + FTS_TEST_DBG("dcb_cs5:%d", thr->basic.dcb_cs5); + FTS_TEST_DBG("dcb_cs6:%d", thr->basic.dcb_cs6); + + print_buffer(thr->rawdata_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->cb_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->cb_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->dcb_sort, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->dcb_base, tdata->node.node_num, tdata->node.rx_num); +} + +static int ini_init_test_sc(void) +{ + int ret = 0; + + ret = get_test_item_sc(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get sc test item fail\n"); + return ret; + } + + ret = get_test_threshold_sc(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get sc threshold fail\n"); + return ret; + } + + print_thr_sc(); + return 0; +} + +static u32 ini_get_ic_code(char *ic_name) +{ + int i = 0; + int type_size = 0; + int ini_icname_len = 0; + int ic_types_len = 0; + + ini_icname_len = strlen(ic_name); + type_size = sizeof(ic_types) / sizeof(ic_types[0]); + for (i = 0; i < type_size; i++) { + ic_types_len = strlen(ic_name); + if (ini_icname_len == ic_types_len) { + if (0 == strncmp(ic_name, ic_types[i].ic_name, ic_types_len)) + return ic_types[i].ic_type; + } + } + + FTS_TEST_ERROR("no IC type match"); + return 0; +} + + +static void ini_init_interface(struct ini_data *ini) +{ + char str[MAX_KEYWORD_VALUE_LEN] = { 0 }; + u32 value = 0; + struct fts_test *tdata = fts_ftest; + + /* IC type */ + ini_get_string_value("Interface", "IC_Type", str); + snprintf(ini->ic_name, MAX_IC_NAME_LEN, "%s", str); + + value = ini_get_ic_code(str); + ini->ic_code = value; + FTS_TEST_INFO("ic name:%s, ic code:%x", ini->ic_name, ini->ic_code); + + if (IC_HW_MC_SC == tdata->func->hwtype) { + get_value_interface("Normalize_Type", &value); + tdata->normalize = (u8)value; + FTS_TEST_DBG("normalize:%d", tdata->normalize); + } +} + +static int ini_init_test(struct ini_data *ini) +{ + int ret = 0; + struct fts_test *tdata = fts_ftest; + + /* interface init */ + ini_init_interface(ini); + + /* node valid */ + ret = init_node_valid(); + if (ret < 0) { + FTS_TEST_ERROR("init node valid fail"); + return ret; + } + + switch (tdata->func->hwtype) { + case IC_HW_INCELL: + ret = ini_init_test_incell(); + break; + case IC_HW_MC_SC: + ret = ini_init_test_mc_sc(); + break; + case IC_HW_SC: + ret = ini_init_test_sc(); + break; + default: + FTS_TEST_SAVE_ERR("test ic type(%d) fail\n", tdata->func->hwtype); + ret = -EINVAL; + break; + } + + return ret; +} + +/* + * fts_test_get_testparam_from_ini - get test parameters from ini + * + * read, parse the configuration file, initialize the test variable + * + * return 0 if succuss, else errro code + */ +int fts_test_get_testparam_from_ini(char *config_name) +{ + int ret = 0; + struct ini_data *ini = &fts_ftest->ini; + + ret = fts_test_get_ini_via_request_firmware(ini, config_name); + if (ret != 0) { + ret = fts_test_get_ini_default(ini, config_name); + if (ret < 0) { + FTS_TEST_ERROR("get ini(default) fail"); + goto get_ini_err; + } + } + + ini->keyword_num_total = 0; + ini->section_num = 0; + + ini->tmp = vmalloc(sizeof(struct ini_keyword) * MAX_KEYWORD_NUM); + if (NULL == ini->tmp) { + FTS_TEST_ERROR("malloc memory for ini tmp fail"); + ret = -ENOMEM; + goto get_ini_err; + } + memset(ini->tmp, 0, sizeof(struct ini_keyword) * MAX_KEYWORD_NUM); + + /* parse ini data to get keyword name&value */ + ret = ini_init_inidata(ini); + if (ret < 0) { + FTS_TEST_ERROR("ini_init_inidata fail"); + goto get_ini_err; + } + + /* parse threshold & test item */ + ret = ini_init_test(ini); + if (ret < 0) { + FTS_TEST_ERROR("ini init fail"); + goto get_ini_err; + } + + ret = 0; +get_ini_err: + if (ini->tmp) { + vfree(ini->tmp); + ini->tmp = NULL; + } + + if (ini->data) { + vfree(ini->data); + ini->data = NULL; + } + + return ret; +} diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/focaltech_test_ini.h b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/focaltech_test_ini.h new file mode 100755 index 000000000000..13677b23360a --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/focaltech_test_ini.h @@ -0,0 +1,141 @@ +/************************************************************************ +* Copyright (C) 2012-2019, Focaltech Systems (R)£¬All Rights Reserved. +* +* File Name: focaltech_test_ini.h +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-01 +* +* Abstract: parsing function of INI file +* +************************************************************************/ +#ifndef _INI_H +#define _INI_H +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define MAX_KEYWORD_NUM (1000) +#define MAX_KEYWORD_NAME_LEN (50) +#define MAX_KEYWORD_VALUE_LEN (512) +#define MAX_KEYWORD_VALUE_ONE_LEN (16) +#define MAX_INI_LINE_LEN (MAX_KEYWORD_NAME_LEN + MAX_KEYWORD_VALUE_LEN) +#define MAX_INI_SECTION_NUM (20) +#define MAX_IC_NAME_LEN (32) +#define MAX_TEST_ITEM (20) +#define IC_CODE_OFFSET (16) + +/***************************************************************************** +* enumerations, structures and unions +*****************************************************************************/ +struct ini_ic_type { + char ic_name[MAX_IC_NAME_LEN]; + u32 ic_type; +}; + +enum line_type { + LINE_SECTION = 1, + LINE_KEYWORD = 2 , + LINE_OTHER = 3, +}; + +struct ini_keyword { + char name[MAX_KEYWORD_NAME_LEN]; + char value[MAX_KEYWORD_VALUE_LEN]; +}; + +struct ini_section { + char name[MAX_KEYWORD_NAME_LEN]; + int keyword_num; + /* point to ini.tmp, don't need free */ + struct ini_keyword *keyword; +}; + +struct ini_data { + char *data; + int length; + int keyword_num_total; + int section_num; + struct ini_section section[MAX_INI_SECTION_NUM]; + struct ini_keyword *tmp; + char ic_name[MAX_IC_NAME_LEN]; + u32 ic_code; +}; + +#define TEST_ITEM_INCELL { \ + "SHORT_CIRCUIT_TEST", \ + "OPEN_TEST", \ + "CB_TEST", \ + "RAWDATA_TEST", \ + "LCD_NOISE_TEST", \ + "KEY_SHORT_TEST", \ + "MUX_OPEN_TEST", \ +} + +#define BASIC_THRESHOLD_INCELL { \ + "ShortCircuit_ResMin", "ShortCircuit_VkResMin", \ + "OpenTest_CBMin", "OpenTest_Check_K1", "OpenTest_K1Threshold", "OpenTest_Check_K2", "OpenTest_K2Threshold", \ + "CBTest_Min", "CBTest_Max", \ + "CBTest_VKey_Check", "CBTest_Min_Vkey", "CBTest_Max_Vkey", \ + "RawDataTest_Min", "RawDataTest_Max", \ + "RawDataTest_VKey_Check", "RawDataTest_Min_VKey", "RawDataTest_Max_VKey", \ + "LCD_NoiseTest_Frame", "LCD_NoiseTest_Coefficient", "LCD_NoiseTest_Coefficient_key", \ +} + + +#define TEST_ITEM_MC_SC { \ + "RAWDATA_TEST", \ + "UNIFORMITY_TEST", \ + "SCAP_CB_TEST", \ + "SCAP_RAWDATA_TEST", \ + "WEAK_SHORT_CIRCUIT_TEST", \ + "PANEL_DIFFER_TEST", \ +} + +#define BASIC_THRESHOLD_MC_SC { \ + "RawDataTest_High_Min", "RawDataTest_High_Max", "RawDataTest_HighFreq", \ + "RawDataTest_Low_Min", "RawDataTest_Low_Max", "RawDataTest_LowFreq", \ + "UniformityTest_Check_Tx", "UniformityTest_Check_Rx","UniformityTest_Check_MinMax", \ + "UniformityTest_Tx_Hole", "UniformityTest_Rx_Hole", "UniformityTest_MinMax_Hole", \ + "SCapCbTest_OFF_Min", "SCapCbTest_OFF_Max", "ScapCBTest_SetWaterproof_OFF", \ + "SCapCbTest_ON_Min", "SCapCbTest_ON_Max", "ScapCBTest_SetWaterproof_ON", \ + "SCapRawDataTest_OFF_Min", "SCapRawDataTest_OFF_Max", "SCapRawDataTest_SetWaterproof_OFF", \ + "SCapRawDataTest_ON_Min", "SCapRawDataTest_ON_Max", "SCapRawDataTest_SetWaterproof_ON", \ + "WeakShortTest_CG", "WeakShortTest_CC", \ + "PanelDifferTest_Min", "PanelDifferTest_Max", \ +} + +#define TEST_ITEM_SC { \ + "RAWDATA_TEST", \ + "CB_TEST", \ + "DELTA_CB_TEST", \ + "WEAK_SHORT_TEST", \ +} + +#define BASIC_THRESHOLD_SC { \ + "RawDataTest_Min", "RawDataTest_Max", \ + "CbTest_Min", "CbTest_Max", \ + "DeltaCbTest_Base", "DeltaCbTest_Differ_Max", \ + "DeltaCbTest_Include_Key_Test", "DeltaCbTest_Key_Differ_Max", \ + "DeltaCbTest_Deviation_S1", "DeltaCbTest_Deviation_S2", "DeltaCbTest_Deviation_S3", \ + "DeltaCbTest_Deviation_S4", "DeltaCbTest_Deviation_S5", "DeltaCbTest_Deviation_S6", \ + "DeltaCbTest_Set_Critical", "DeltaCbTest_Critical_S1", "DeltaCbTest_Critical_S2", \ + "DeltaCbTest_Critical_S3", "DeltaCbTest_Critical_S4", \ + "DeltaCbTest_Critical_S5", "DeltaCbTest_Critical_S6", \ +} + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +int fts_test_get_testparam_from_ini(char *config_name); +int get_keyword_value(char *section, char *name, int *value); + +#define get_value_interface(name, value) \ + get_keyword_value("Interface", name, value) +#define get_value_basic(name, value) \ + get_keyword_value("Basic_Threshold", name, value) +#define get_value_detail(name, value) \ + get_keyword_value("SpecialSet", name, value) +#define get_value_testitem(name, value) \ + get_keyword_value("TestItem", name, value) +#endif /* _INI_H */ diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/supported_ic/Makefile b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/supported_ic/Makefile new file mode 100755 index 000000000000..e913de1de94b --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/supported_ic/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_test_ft8719.o \ No newline at end of file diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/supported_ic/focaltech_test_ft8006sp.c b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/supported_ic/focaltech_test_ft8006sp.c new file mode 100644 index 000000000000..0e0977eb5405 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/supported_ic/focaltech_test_ft8006sp.c @@ -0,0 +1,544 @@ +/************************************************************************ +* Copyright (C) 2012-2019, Focaltech Systems (R), All Rights Reserved. +* +* File Name: Focaltech_test_ft8006sp.c +* +* Author: Focaltech Driver Team +* +* Created: 2019-07-02 +* +* Abstract: +* +************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "../focaltech_test.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ + +/******************************************************************************* +* Private constant and macro definitions using #define +*******************************************************************************/ + +/******************************************************************************* +* Private enumerations, structures and unions using typedef +*******************************************************************************/ + +/******************************************************************************* +* Static variables +*******************************************************************************/ + +/******************************************************************************* +* Global variable or extern global variabls/functions +*******************************************************************************/ + +/******************************************************************************* +* Static function prototypes +*******************************************************************************/ +static int ft8006sp_short_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + int i = 0; + bool tmp_result = false; + int byte_num = 0; + int ch_num = 0; + int min = 0; + int max = 0; + int tmp_adc = 0; + int *adcdata = NULL; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: Short Circuit Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + adcdata = tdata->buffer; + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + byte_num = tdata->node.node_num * 2; + ch_num = tdata->node.rx_num; + ret = short_get_adcdata_incell(TEST_RETVAL_AA, ch_num, byte_num, adcdata); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get adc data fail\n"); + goto test_err; + } + + /* calculate resistor */ + for (i = 0; i < tdata->node.node_num; i++) { + tmp_adc = adcdata[i]; + if (tmp_adc <= 0) { + adcdata[i] = -1; + continue; + } else if ((tmp_adc >= 1) && (tmp_adc <= 941)) { + tmp_adc = 941; + } + + adcdata[i] = (6090 * 4096 / (74 * tmp_adc - 17 * 4096)) - 20; + if (adcdata[i] > 3000) { + adcdata[i] = 3000; + } + } + + /* save */ + show_data(adcdata, false); + + /* compare */ + min = thr->basic.short_res_min; + max = TEST_SHORT_RES_MAX; + tmp_result = compare_data(adcdata, min, max, min, max, false); + +test_err: + ret = fts_test_write_reg(FACTORY_REG_SHORT_TEST_STATE, 0x03); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write idle to short test state fail\n"); + } + + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ Short Circuit Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("------ Short Circuit Test NG\n"); + } + + /* save test data */ + fts_test_save_data("Short Circuit Test", CODE_SHORT_TEST, + adcdata, 0, false, false, *test_result); + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft8006sp_open_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + int i = 0; + bool tmp_result = false; + u8 state = 0; + int byte_num = 0; + int min = 0; + int max = 0; + int *opendata = NULL; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: Open Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + opendata = tdata->buffer; + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_write_reg(FACTORY_REG_OPEN_START, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("start open test fail\n"); + goto test_err; + } + + /* check test status */ + for (i = 0; i < FACTORY_TEST_RETRY; i++) { + sys_delay(FACTORY_TEST_RETRY_DELAY); + ret = fts_test_read_reg(FACTORY_REG_OPEN_STATE, &state); + if ((ret >= 0) && (TEST_RETVAL_AA == state)) { + break; + } else { + FTS_TEST_DBG("reg%x=%x,retry:%d\n", + FACTORY_REG_OPEN_STATE, state, i); + } + } + if (i >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("open test timeout\n"); + goto restore_reg; + } + + /* get cb data */ + byte_num = tdata->node.node_num; + ret = get_cb_incell(0, byte_num, opendata); + if (ret) { + FTS_TEST_SAVE_ERR("get CB fail\n"); + goto restore_reg; + } + + /* save */ + show_data(opendata, false); + + /* compare */ + min = thr->basic.open_cb_min; + max = TEST_OPEN_MAX_VALUE; + tmp_result = compare_data(opendata, min, max, 0, 0, false); + +restore_reg: + /* auto clb */ + ret = chip_clb(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("auto clb fail\n"); + } + +test_err: + ret = fts_test_write_reg(FACTORY_REG_OPEN_STATE, 0x03); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write idle to open test state fail\n"); + } + + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ Open Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("------ Open Test NG\n"); + } + + /*save test data*/ + fts_test_save_data("Open Test", CODE_OPEN_TEST, + opendata, 0, false, false, *test_result); + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft8006sp_cb_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + bool tmp_result = false; + int byte_num = 0; + int *cbdata = NULL; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: CB Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + cbdata = tdata->buffer; + + if (!thr->cb_min || !thr->cb_max || !test_result) { + FTS_TEST_SAVE_ERR("cb_min/max test_result is null\n"); + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + /* cb test enable */ + ret = fts_test_write_reg(FACTORY_REG_CB_TEST_EN, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("cb test enable fail\n"); + goto test_err; + } + + /* auto clb */ + ret = chip_clb(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("auto clb fail\n"); + goto test_err; + } + + byte_num = tdata->node.node_num; + ret = get_cb_incell(0, byte_num, cbdata); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get cb fail\n"); + goto test_err; + } + + /* save */ + show_data(cbdata, false); + + /* compare */ + tmp_result = compare_array(cbdata, thr->cb_min, thr->cb_max, false); + +test_err: + /* cb test disable */ + ret = fts_test_write_reg(FACTORY_REG_CB_TEST_EN, 0x00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("cb test disable fail\n"); + } + + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ CB Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("------ CB Test NG\n"); + } + + /*save test data*/ + fts_test_save_data("CB Test", CODE_CB_TEST, + cbdata, 0, false, false, *test_result); + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft8006sp_rawdata_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + bool tmp_result = false; + int *rawdata = NULL; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: RawData Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + rawdata = tdata->buffer; + + if (!thr->rawdata_min || !thr->rawdata_max || !test_result) { + FTS_TEST_SAVE_ERR("rawdata_min/max test_result is null\n"); + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + /* rawdata test enable */ + ret = fts_test_write_reg(FACTORY_REG_RAWDATA_TEST_EN, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("rawdata test enable fail\n"); + goto test_err; + } + + /* read rawdata */ + ret = get_rawdata(rawdata); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get RawData fail,ret=%d\n", ret); + goto test_err; + } + + /* save */ + show_data(rawdata, false); + + /* compare */ + tmp_result = compare_array(rawdata, + thr->rawdata_min, + thr->rawdata_max, + false); + ret = 0; + +test_err: + /* rawdata test disble */ + ret = fts_test_write_reg(FACTORY_REG_RAWDATA_TEST_EN, 0x00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("rawdata test disable fail\n"); + } + + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ RawData Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("------ RawData Test NG\n"); + } + + /*save test data*/ + fts_test_save_data("RawData Test", CODE_RAWDATA_TEST, + rawdata, 0, false, false, *test_result); + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft8006sp_lcdnoise_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + int i = 0; + bool tmp_result = false; + u8 old_mode = 0; + u8 status = 0; + u8 touch_thr = 0; + int byte_num = 0; + int frame_num = 0; + int max = 0; + int *lcdnoise = NULL; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: LCD Noise Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + lcdnoise = tdata->buffer; + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_LCD_NOISE_TTHR, &touch_thr); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read reg14 fail\n"); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_DATA_SELECT, &old_mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read reg06 fail\n"); + goto test_err; + } + + ret = fts_test_write_reg(FACTORY_REG_DATA_SELECT, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 1 to reg06 fail\n"); + goto test_err; + } + + ret = fts_test_write_reg(FACTORY_REG_LINE_ADDR, 0xAD); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write reg01 fail\n"); + goto test_err; + } + + frame_num = thr->basic.lcdnoise_frame; + ret = fts_test_write_reg(FACTORY_REG_LCD_NOISE_FRAME, frame_num / 4); + if (ret < 0) { + FTS_TEST_SAVE_INFO("write frame num fail\n"); + goto test_err; + } + + /* start test */ + ret = fts_test_write_reg(FACTORY_REG_LCD_NOISE_START, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_INFO("start lcdnoise test fail\n"); + goto test_err; + } + + /* check test status */ + sys_delay(frame_num * FACTORY_TEST_DELAY / 2); + for (i = 0; i < FACTORY_TEST_RETRY; i++) { + status = 0xFF; + ret = fts_test_read_reg(FACTORY_REG_LCD_NOISE_TEST_STATE, &status); + if ((ret >= 0) && (TEST_RETVAL_AA == status)) { + break; + } else { + FTS_TEST_DBG("reg%x=%x,retry:%d\n", + FACTORY_REG_LCD_NOISE_TEST_STATE, status, i); + } + sys_delay(FACTORY_TEST_RETRY_DELAY); + } + if (i >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("lcdnoise test timeout\n"); + goto test_err; + } + /* read lcdnoise */ + byte_num = tdata->node.node_num * 2; + ret = read_mass_data(FACTORY_REG_RAWDATA_ADDR, byte_num, lcdnoise); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read rawdata fail\n"); + goto test_err; + } + + /* save */ + show_data(lcdnoise, false); + + /* compare */ + max = thr->basic.lcdnoise_coefficient * touch_thr * 32 / 100; + FTS_TEST_DBG("touch thr:%d, max:%d", touch_thr, max); + tmp_result = compare_data(lcdnoise, 0, max, 0, 0, false); + +test_err: + ret = fts_test_write_reg(FACTORY_REG_LCD_NOISE_START, 0x00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 0 to reg11 fail\n"); + } + + ret = fts_test_write_reg(FACTORY_REG_DATA_SELECT, old_mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore reg06 fail\n"); + } + + ret = fts_test_write_reg(FACTORY_REG_LCD_NOISE_TEST_STATE, 0x03); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write idle to lcdnoise test state fail\n"); + } + + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ LCD Noise Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("------ LCD Noise Test NG\n"); + } + + /*save test data*/ + fts_test_save_data("LCD Noise Test", CODE_LCD_NOISE_TEST, + lcdnoise, 0, false, false, *test_result); + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int start_test_ft8006sp(void) +{ + int ret = 0; + struct fts_test *tdata = fts_ftest; + struct incell_testitem *test_item = &tdata->ic.incell.u.item; + bool temp_result = false; + bool test_result = true; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_INFO("test item:0x%x", fts_ftest->ic.incell.u.tmp); + + if (!tdata || !tdata->testresult || !tdata->buffer) { + FTS_TEST_ERROR("tdata is null"); + return -EINVAL; + } + + /* short test */ + if (true == test_item->short_test) { + ret = ft8006sp_short_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* open test */ + if (true == test_item->open_test) { + ret = ft8006sp_open_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* cb test */ + if (true == test_item->cb_test) { + ret = ft8006sp_cb_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* rawdata test */ + if (true == test_item->rawdata_test) { + ret = ft8006sp_rawdata_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* lcd noise test */ + if (true == test_item->lcdnoise_test) { + ret = ft8006sp_lcdnoise_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + return test_result; +} + +struct test_funcs test_func_ft8006sp = { + .ctype = {0x19}, + .hwtype = IC_HW_INCELL, + .key_num_total = 0, + .start_test = start_test_ft8006sp, +}; diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/supported_ic/focaltech_test_ft8719.c b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/supported_ic/focaltech_test_ft8719.c new file mode 100755 index 000000000000..fcc162fbf93c --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/supported_ic/focaltech_test_ft8719.c @@ -0,0 +1,618 @@ +/************************************************************************ +* Copyright (C) 2012-2019, Focaltech Systems (R), All Rights Reserved. +* +* File Name: Focaltech_test_ft8719.c +* +* Author: Focaltech Driver Team +* +* Created: 2017-12-01 +* +* Abstract: +* +************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "../focaltech_test.h" + +/******************************************************************************* +* Private constant and macro definitions using #define +*******************************************************************************/ +#define CODE2_SHORT_TEST 14 +#define CODE2_OPEN_TEST 24 +#define CODE2_LCD_NOISE_TEST 15 + +/******************************************************************************* +* Private enumerations, structures and unions using typedef +*******************************************************************************/ + +/******************************************************************************* +* Static variables +*******************************************************************************/ + +/******************************************************************************* +* Global variable or extern global variabls/functions +*******************************************************************************/ + +/******************************************************************************* +* Static function prototypes +*******************************************************************************/ +static int ft8719_short_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + int i = 0; + bool tmp_result = false; + int byte_num = 0; + int ch_num = 0; + int min = 0; + int max = 0; + int tmp_adc = 0; + int *adcdata = NULL; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: Short Circuit Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + adcdata = tdata->buffer; + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + byte_num = tdata->node.node_num * 2; + ch_num = tdata->node.rx_num; + ret = short_get_adcdata_incell(TEST_RETVAL_AA, ch_num, byte_num, adcdata); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get adc data fail\n"); + goto test_err; + } + + /* calculate resistor */ + for (i = 0; i < tdata->node.node_num; i++) { + tmp_adc = adcdata[i]; + if (tmp_adc < 50) { + adcdata[i] = -1; + continue; + } else if (tmp_adc < 200) { + tmp_adc = 200; + } + + adcdata[i] = 252000 / (tmp_adc - 120) - 60; + } + + /* save */ + show_data(adcdata, true); + + /* compare */ + min = thr->basic.short_res_min; + max = TEST_SHORT_RES_MAX; + tmp_result = compare_data(adcdata, min, max, min, max, true); + + ret = 0; +test_err: + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ Short Circuit Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("------ Short Circuit Test NG\n"); + } + + /*save test data*/ + fts_test_save_data("Short Circuit Test", CODE2_SHORT_TEST, + adcdata, 0, false, true, *test_result); + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft8719_open_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + bool tmp_result = false; + int byte_num = 0; + u8 reg20_val = 0; + u8 reg21_val = 0; + u8 k1 = 0; + u8 k2 = 0; + int min = 0; + int max = 0; + int *opendata = NULL; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: Open Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + opendata = tdata->buffer; + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + /* backup defaul value */ + ret = fts_test_read_reg(FACTORY_REG_OPEN_REG20, ®20_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read reg 0x20 fail\n"); + goto test_err; + } + ret = fts_test_read_reg(FACTORY_REG_OPEN_REG21, ®21_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read reg 0x21 fail\n"); + goto test_err; + } + if (thr->basic.open_k1_check) { + ret = fts_test_read_reg(FACTORY_REG_K1, &k1); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read k1 fail\n"); + goto test_err; + } + } + if (thr->basic.open_k2_check) { + ret = fts_test_read_reg(FACTORY_REG_K2, &k2); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read k2 fail\n"); + goto test_err; + } + } + + /* open test enable */ + ret = fts_test_write_reg(FACTORY_REG_OPEN_TEST_EN, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("open test enable fail\n"); + goto test_err; + } + + /* set open test environment */ + ret = fts_test_write_reg(FACTORY_REG_OPEN_REG20, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write reg 0x20 fail\n"); + goto restore_reg; + } + + ret = fts_test_write_reg(FACTORY_REG_OPEN_REG21, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write reg 0x21 fail\n"); + goto restore_reg; + } + + if (thr->basic.open_k1_check) { + ret = fts_test_write_reg(FACTORY_REG_K1, thr->basic.open_k1_value); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write reg k1 fail\n"); + goto restore_reg; + } + } + + if (thr->basic.open_k2_check) { + ret = fts_test_write_reg(FACTORY_REG_K2, thr->basic.open_k2_value); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write reg k2 fail\n"); + goto restore_reg; + } + } + + /* wait fw state update before clb */ + ret = wait_state_update(TEST_RETVAL_00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + goto restore_reg; + } + /* auto clb */ + ret = chip_clb(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("auto clb fail\n"); + goto restore_reg; + } + + /* get cb data */ + byte_num = tdata->node.tx_num * tdata->node.rx_num; + ret = get_cb_incell(0, byte_num, opendata); + if (ret) { + FTS_TEST_SAVE_ERR("get CB fail\n"); + goto restore_reg; + } + + /* save */ + show_data(opendata, false); + + /* compare */ + min = thr->basic.open_cb_min; + max = TEST_OPEN_MAX_VALUE; + tmp_result = compare_data(opendata, min, max, 0, 0, false); + +restore_reg: + ret = fts_test_write_reg(FACTORY_REG_OPEN_REG20, reg20_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore reg 0x20 fail\n"); + } + + ret = fts_test_write_reg(FACTORY_REG_OPEN_REG21, reg21_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore reg 0x21 fail\n"); + } + + if (thr->basic.open_k1_check) { + ret = fts_test_write_reg(FACTORY_REG_K1, k1); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore reg k1 fail\n"); + } + } + + if (thr->basic.open_k2_check) { + ret = fts_test_write_reg(FACTORY_REG_K2, k2); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore reg k2 fail\n"); + } + } + + /* open test disable */ + ret = fts_test_write_reg(FACTORY_REG_OPEN_TEST_EN, 0x00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("open test disable fail\n"); + } + + /* wait fw state update before clb */ + ret = wait_state_update(TEST_RETVAL_00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + } + /* auto clb */ + ret = chip_clb(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("auto clb fail\n"); + } + +test_err: + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ Open Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("------ Open Test NG\n"); + } + + /* save test data */ + fts_test_save_data("Open Test", CODE2_OPEN_TEST, + opendata, 0, false, false, *test_result); + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft8719_cb_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + bool tmp_result = false; + bool key_check = false; + int byte_num = 0; + int *cbdata = NULL; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: CB Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + cbdata = tdata->buffer; + key_check = thr->basic.cb_vkey_check; + + if (!thr->cb_min || !thr->cb_max || !test_result) { + FTS_TEST_SAVE_ERR("cb_min/max test_result is null\n"); + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + /* cb test enable */ + ret = fts_test_write_reg(FACTORY_REG_CB_TEST_EN, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("cb test enable fail\n"); + goto test_err; + } + + /* auto clb */ + ret = chip_clb(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("auto clb fail\n"); + goto test_err; + } + + byte_num = tdata->node.node_num; + ret = get_cb_incell(0, byte_num, cbdata); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get cb fail\n"); + goto test_err; + } + + /* save */ + show_data(cbdata, key_check); + + /* compare */ + tmp_result = compare_array(cbdata, thr->cb_min, thr->cb_max, key_check); + +test_err: + /* cb test disable */ + ret = fts_test_write_reg(FACTORY_REG_CB_TEST_EN, 0x00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("cb test disable fail\n"); + } + + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ CB Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("------ CB Test NG\n"); + } + + /* save test data */ + fts_test_save_data("CB Test", CODE_CB_TEST, + cbdata, 0, false, key_check, *test_result); + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft8719_rawdata_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + int i = 0; + bool tmp_result = false; + bool key_check = false; + int *rawdata = NULL; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: RawData Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + rawdata = tdata->buffer; + key_check = thr->basic.rawdata_vkey_check; + + if (!thr->rawdata_min || !thr->rawdata_max || !test_result) { + FTS_TEST_SAVE_ERR("rawdata_min/max test_result is null\n"); + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + /* rawdata test enable */ + ret = fts_test_write_reg(FACTORY_REG_RAWDATA_TEST_EN, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("rawdata test enable fail\n"); + goto test_err; + } + + /* read rawdata */ + for (i = 0 ; i < 3; i++) { + ret = get_rawdata(rawdata); + } + if (ret < 0) { + FTS_TEST_SAVE_ERR("get RawData fail,ret=%d\n", ret); + goto test_err; + } + + /* save */ + show_data(rawdata, key_check); + + /* compare */ + tmp_result = compare_array(rawdata, + thr->rawdata_min, + thr->rawdata_max, + key_check); + +test_err: + /* rawdata test disble */ + ret = fts_test_write_reg(FACTORY_REG_RAWDATA_TEST_EN, 0x00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("rawdata test disable fail\n"); + } + + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ RawData Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("------ RawData Test NG\n"); + } + + /* save test data */ + fts_test_save_data("RawData Test", CODE_RAWDATA_TEST, + rawdata, 0, false, key_check, *test_result); + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft8719_lcdnoise_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + int i = 0; + bool tmp_result = false; + int byte_num = 0; + u8 old_mode = 0; + u8 status = 0; + int frame_num = 0; + int max = 0; + int max_vk = 0; + int *lcdnoise = NULL; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: LCD Noise Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + lcdnoise = tdata->buffer; + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_DATA_SELECT, &old_mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read reg06 fail\n"); + goto test_err; + } + + ret = fts_test_write_reg(FACTORY_REG_DATA_SELECT, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 1 to reg06 fail\n"); + goto restore_reg; + } + + ret = fts_test_write_reg(FACTORY_REG_LINE_ADDR, 0xAD); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write reg01 fail\n"); + goto restore_reg; + } + + frame_num = thr->basic.lcdnoise_frame; + ret = fts_test_write_reg(FACTORY_REG_LCD_NOISE_FRAME, frame_num / 4); + if (ret < 0) { + FTS_TEST_SAVE_INFO("write frame num fail\n"); + goto restore_reg; + } + + /* start test */ + ret = fts_test_write_reg(FACTORY_REG_LCD_NOISE_START, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_INFO("start lcdnoise test fail\n"); + goto restore_reg; + } + + /* check test status */ + sys_delay(frame_num * FACTORY_TEST_DELAY / 2); + for (i = 0; i < FACTORY_TEST_RETRY; i++) { + status = 0xFF; + ret = fts_test_read_reg(FACTORY_REG_LCD_NOISE_START, &status); + if ((ret >= 0) && (TEST_RETVAL_AA == status)) { + break; + } else { + FTS_TEST_DBG("reg%x=%x,retry:%d\n", + FACTORY_REG_LCD_NOISE_START, status, i); + } + sys_delay(FACTORY_TEST_RETRY_DELAY); + } + if (i >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("lcdnoise test timeout\n"); + goto restore_reg; + } + /* read lcdnoise */ + byte_num = tdata->node.node_num * 2; + ret = read_mass_data(FACTORY_REG_RAWDATA_ADDR, byte_num, lcdnoise); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read rawdata fail\n"); + goto restore_reg; + } + + /* save */ + show_data(lcdnoise, true); + + /* compare */ + max = thr->basic.lcdnoise_coefficient * tdata->va_touch_thr * 32 / 100; + max_vk = thr->basic.lcdnoise_coefficient_vkey * tdata->vk_touch_thr * 32 / 100; + tmp_result = compare_data(lcdnoise, 0, max, 0, max_vk, true); + +restore_reg: + ret = fts_test_write_reg(FACTORY_REG_LCD_NOISE_START, 0x00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 0 to reg11 fail\n"); + } + + ret = fts_test_write_reg(FACTORY_REG_DATA_SELECT, old_mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore reg06 fail\n"); + } +test_err: + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ LCD Noise Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("------ LCD Noise Test NG\n"); + } + + /* save test data */ + fts_test_save_data("LCD Noise Test", CODE2_LCD_NOISE_TEST, + lcdnoise, 0, false, true, *test_result); + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int start_test_ft8719(void) +{ + int ret = 0; + struct fts_test *tdata = fts_ftest; + struct incell_testitem *test_item = &tdata->ic.incell.u.item; + bool temp_result = false; + bool test_result = true; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_INFO("test item:0x%x", fts_ftest->ic.incell.u.tmp); + + if (!tdata || !tdata->buffer) { + FTS_TEST_ERROR("tdata is null"); + return -EINVAL; + } + + /* short test */ + if (true == test_item->short_test) { + ret = ft8719_short_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* open test */ + if (true == test_item->open_test) { + ret = ft8719_open_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* cb test */ + if (true == test_item->cb_test) { + ret = ft8719_cb_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* rawdata test */ + if (true == test_item->rawdata_test) { + ret = ft8719_rawdata_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* lcd noise test */ + if (true == test_item->lcdnoise_test) { + ret = ft8719_lcdnoise_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + return test_result; +} + +struct test_funcs test_func_ft8719 = { + .ctype = {0x0D, 0x0F}, + .hwtype = IC_HW_INCELL, + .key_num_total = 4, + .start_test = start_test_ft8719, +}; diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/supported_ic/focaltech_test_ft8756.c b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/supported_ic/focaltech_test_ft8756.c new file mode 100644 index 000000000000..2f70bd93f38e --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_test/supported_ic/focaltech_test_ft8756.c @@ -0,0 +1,578 @@ +/************************************************************************ +* Copyright (C) 2012-2019, Focaltech Systems (R), All Rights Reserved. +* +* File Name: Focaltech_test_ft8756.c +* +* Author: Focaltech Driver Team +* +* Created: 2019-03-02 +* +* Abstract: +* +************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "../focaltech_test.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ + +/***************************************************************************** +* Static variables +*****************************************************************************/ + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +static int ft8756_short_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + int i = 0; + bool tmp_result = false; + int byte_num = 0; + int ch_num = 0; + int min = 0; + int max = 0; + int tmp_adc = 0; + int *adcdata = NULL; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: Short Circuit Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + adcdata = tdata->buffer; + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + byte_num = tdata->node.node_num * 2; + ch_num = tdata->node.rx_num; + ret = short_get_adcdata_incell(TEST_RETVAL_AA, ch_num, byte_num, adcdata); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get adc data fail\n"); + goto test_err; + } + + /* calculate resistor */ + for (i = 0; i < tdata->node.node_num; i++) { + tmp_adc = adcdata[i]; + if (tmp_adc > 3500) { + tmp_adc = 3500; + } + adcdata[i] = (5160960 + 555 * 32 * tmp_adc) / (1146880 - 320 * tmp_adc); + } + + /* save */ + show_data(adcdata, false); + + /* compare */ + min = thr->basic.short_res_min; + max = TEST_SHORT_RES_MAX; + tmp_result = compare_data(adcdata, min, max, min, max, false); + + ret = 0; +test_err: + ret = fts_test_write_reg(FACTORY_REG_SHORT_TEST_STATE, 0x03); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write idle to short test state fail\n"); + } + + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ Short Circuit Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("------ Short Circuit Test NG\n"); + } + + /* save test data */ + fts_test_save_data("Short Circuit Test", CODE_SHORT_TEST, + adcdata, 0, false, false, *test_result); + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft8756_open_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + int i = 0; + bool tmp_result = false; + u8 state = 0; + int byte_num = 0; + int min = 0; + int max = 0; + u8 k1 = 0; + u8 k2 = 0; + int *opendata = NULL; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: Open Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + opendata = tdata->buffer; + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + if (thr->basic.open_k1_check) { + ret = fts_test_read_reg(FACTORY_REG_K1, &k1); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read k1 fail\n"); + goto test_err; + } + } + if (thr->basic.open_k2_check) { + ret = fts_test_read_reg(FACTORY_REG_K2, &k2); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read k2 fail\n"); + goto restore_reg; + } + } + + if (thr->basic.open_k1_check) { + ret = fts_test_write_reg(FACTORY_REG_K1, thr->basic.open_k1_value); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write reg k1 fail\n"); + goto restore_reg; + } + } + + if (thr->basic.open_k2_check) { + ret = fts_test_write_reg(FACTORY_REG_K2, thr->basic.open_k2_value); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write reg k2 fail\n"); + goto restore_reg; + } + } + + ret = fts_test_write_reg(FACTORY_REG_OPEN_START, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("start open test fail\n"); + goto restore_reg; + } + + /* check test status */ + for (i = 0; i < FACTORY_TEST_RETRY; i++) { + sys_delay(FACTORY_TEST_RETRY_DELAY); + ret = fts_test_read_reg(FACTORY_REG_OPEN_STATE, &state); + if ((ret >= 0) && (TEST_RETVAL_AA == state)) { + break; + } else { + FTS_TEST_DBG("reg%x=%x,retry:%d\n", + FACTORY_REG_OPEN_STATE, state, i); + } + } + if (i >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("open test timeout\n"); + goto restore_reg; + } + + /* get cb data */ + byte_num = tdata->node.node_num; + ret = get_cb_incell(0, byte_num, opendata); + if (ret) { + FTS_TEST_SAVE_ERR("get CB fail\n"); + goto restore_reg; + } + + /* save */ + show_data(opendata, false); + + /* compare */ + min = thr->basic.open_cb_min; + max = TEST_OPEN_MAX_VALUE; + tmp_result = compare_data(opendata, min, max, 0, 0, false); + +restore_reg: + /* Restore reg */ + if (thr->basic.open_k1_check) { + ret = fts_test_write_reg(FACTORY_REG_K1, k1); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore reg k1 fail\n"); + } + } + if (thr->basic.open_k2_check) { + ret = fts_test_write_reg(FACTORY_REG_K2, k2); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore reg k2 fail\n"); + } + } + + /* auto clb */ + ret = chip_clb(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("auto clb fail\n"); + } + +test_err: + ret = fts_test_write_reg(FACTORY_REG_OPEN_STATE, 0x03); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write idle to open test state fail\n"); + } + + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ Open Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("------ Open Test NG\n"); + } + + /*save test data*/ + fts_test_save_data("Open Test", CODE_OPEN_TEST, + opendata, 0, false, false, *test_result); + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft8756_cb_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + bool tmp_result = false; + int byte_num = 0; + int *cbdata = NULL; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: CB Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + cbdata = tdata->buffer; + + if (!thr->cb_min || !thr->cb_max || !test_result) { + FTS_TEST_SAVE_ERR("cb_min/max test_result is null\n"); + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + /* cb test enable */ + ret = fts_test_write_reg(FACTORY_REG_CB_TEST_EN, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("cb test enable fail\n"); + goto test_err; + } + + /* auto clb */ + ret = chip_clb(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("auto clb fail\n"); + goto test_err; + } + + byte_num = tdata->node.node_num; + ret = get_cb_incell(0, byte_num, cbdata); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get cb fail\n"); + goto test_err; + } + + /* save */ + show_data(cbdata, false); + + /* compare */ + tmp_result = compare_array(cbdata, thr->cb_min, thr->cb_max, false); + +test_err: + /* cb test disable */ + ret = fts_test_write_reg(FACTORY_REG_CB_TEST_EN, 0x00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("cb test disable fail\n"); + } + + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ CB Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("------ CB Test NG\n"); + } + + /*save test data*/ + fts_test_save_data("CB Test", CODE_CB_TEST, + cbdata, 0, false, false, *test_result); + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft8756_rawdata_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + bool tmp_result = false; + int *rawdata = NULL; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: RawData Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + rawdata = tdata->buffer; + + if (!thr->rawdata_min || !thr->rawdata_max || !test_result) { + FTS_TEST_SAVE_ERR("rawdata_min/max test_result is null\n"); + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + /* rawdata test enable */ + ret = fts_test_write_reg(FACTORY_REG_RAWDATA_TEST_EN, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("rawdata test enable fail\n"); + goto test_err; + } + + /* read rawdata */ + ret = get_rawdata(rawdata); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get RawData fail,ret=%d\n", ret); + goto test_err; + } + + /* save */ + show_data(rawdata, false); + + /* compare */ + tmp_result = compare_array(rawdata, + thr->rawdata_min, + thr->rawdata_max, + false); + ret = 0; + +test_err: + /* rawdata test disble */ + ret = fts_test_write_reg(FACTORY_REG_RAWDATA_TEST_EN, 0x00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("rawdata test disable fail\n"); + } + + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ RawData Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("------ RawData Test NG\n"); + } + + /*save test data*/ + fts_test_save_data("RawData Test", CODE_RAWDATA_TEST, + rawdata, 0, false, false, *test_result); + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft8756_lcdnoise_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + int i = 0; + bool tmp_result = false; + u8 old_mode = 0; + u8 status = 0; + int byte_num = 0; + int frame_num = 0; + int max = 0; + int *lcdnoise = NULL; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: LCD Noise Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + lcdnoise = tdata->buffer; + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_DATA_SELECT, &old_mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read reg06 fail\n"); + goto test_err; + } + + ret = fts_test_write_reg(FACTORY_REG_DATA_SELECT, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 1 to reg06 fail\n"); + goto test_err; + } + + ret = fts_test_write_reg(FACTORY_REG_LINE_ADDR, 0xAD); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write reg01 fail\n"); + goto test_err; + } + + frame_num = thr->basic.lcdnoise_frame; + ret = fts_test_write_reg(FACTORY_REG_LCD_NOISE_FRAME, frame_num / 4); + if (ret < 0) { + FTS_TEST_SAVE_INFO("write frame num fail\n"); + goto test_err; + } + + /* start test */ + ret = fts_test_write_reg(FACTORY_REG_LCD_NOISE_START, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_INFO("start lcdnoise test fail\n"); + goto test_err; + } + + /* check test status */ + sys_delay(frame_num * FACTORY_TEST_DELAY / 2); + for (i = 0; i < FACTORY_TEST_RETRY; i++) { + status = 0xFF; + ret = fts_test_read_reg(FACTORY_REG_LCD_NOISE_TEST_STATE, &status); + if ((ret >= 0) && (TEST_RETVAL_AA == status)) { + break; + } else { + FTS_TEST_DBG("reg%x=%x,retry:%d\n", + FACTORY_REG_LCD_NOISE_TEST_STATE, status, i); + } + sys_delay(FACTORY_TEST_RETRY_DELAY); + } + if (i >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("lcdnoise test timeout\n"); + goto test_err; + } + /* read lcdnoise */ + byte_num = tdata->node.node_num * 2; + ret = read_mass_data(FACTORY_REG_RAWDATA_ADDR, byte_num, lcdnoise); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read rawdata fail\n"); + goto test_err; + } + + /* save */ + show_data(lcdnoise, false); + + /* compare */ + max = thr->basic.lcdnoise_coefficient * tdata->va_touch_thr * 32 / 100; + FTS_TEST_DBG("touch thr:%d, max:%d", tdata->va_touch_thr, max); + tmp_result = compare_data(lcdnoise, 0, max, 0, 0, false); + +test_err: + ret = fts_test_write_reg(FACTORY_REG_LCD_NOISE_START, 0x00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 0 to reg11 fail\n"); + } + + ret = fts_test_write_reg(FACTORY_REG_DATA_SELECT, old_mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore reg06 fail\n"); + } + + ret = fts_test_write_reg(FACTORY_REG_LCD_NOISE_TEST_STATE, 0x03); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write idle to lcdnoise test state fail\n"); + } + + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ LCD Noise Test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("------ LCD Noise Test NG\n"); + } + + /*save test data*/ + fts_test_save_data("LCD Noise Test", CODE_LCD_NOISE_TEST, + lcdnoise, 0, false, false, *test_result); + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int start_test_ft8756(void) +{ + int ret = 0; + struct fts_test *tdata = fts_ftest; + struct incell_testitem *test_item = &tdata->ic.incell.u.item; + bool temp_result = false; + bool test_result = true; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_INFO("test item:0x%x", fts_ftest->ic.incell.u.tmp); + + if (!tdata || !tdata->testresult || !tdata->buffer) { + FTS_TEST_ERROR("tdata is null"); + return -EINVAL; + } + + /* short test */ + if (true == test_item->short_test) { + ret = ft8756_short_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* open test */ + if (true == test_item->open_test) { + ret = ft8756_open_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* cb test */ + if (true == test_item->cb_test) { + ret = ft8756_cb_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* rawdata test */ + if (true == test_item->rawdata_test) { + ret = ft8756_rawdata_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* lcd noise test */ + if (true == test_item->lcdnoise_test) { + ret = ft8756_lcdnoise_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + return test_result; +} + +struct test_funcs test_func_ft8756 = { + .ctype = {0x15, 0x18}, + .hwtype = IC_HW_INCELL, + .key_num_total = 0, + .start_test = start_test_ft8756, +}; diff --git a/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_ts_mmi.c b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_ts_mmi.c new file mode 100644 index 000000000000..a0b00bc5dc89 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_mmi/focaltech_ts_mmi.c @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include "focaltech_core.h" + +#define GET_TS_DATA(dev) { \ + ts_data = dev_get_drvdata(dev); \ + if (!ts_data) { \ + FTS_ERROR("Failed to get driver data"); \ + return -ENODEV; \ + } \ +} +static int fts_mmi_methods_get_vendor(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", "focaltech"); +} + +static int fts_mmi_methods_get_productinfo(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_INFO_LEN, "%s", FTS_CHIP_NAME); +} + +static int fts_mmi_methods_get_config_id(struct device *dev, void *cdata) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + ssize_t num_read_chars = 0; + u8 fwver = 0; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + 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 + num_read_chars = scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%02x", fwver); + + mutex_unlock(&input_dev->mutex); + return num_read_chars; +} + +/*return firmware version*/ +static int fts_mmi_methods_get_build_id(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%04x", 0); +} + +static int fts_mmi_methods_get_bus_type(struct device *dev, void *idata) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + TO_INT(idata) = input_dev->id.bustype == BUS_SPI ? + TOUCHSCREEN_MMI_BUS_TYPE_SPI : TOUCHSCREEN_MMI_BUS_TYPE_I2C; + return 0; +} + +static int fts_mmi_methods_get_irq_status(struct device *dev, void *idata) +{ + struct fts_ts_platform_data *pdata; + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + pdata = ts_data->pdata; + + TO_INT(idata) = gpio_get_value(pdata->irq_gpio); + return 0; +} + +static int fts_mmi_methods_get_drv_irq(struct device *dev, void *idata) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + TO_INT(idata) = ts_data->irq_disabled ? 0 : 1; + return 0; +} + +static int fts_mmi_methods_get_poweron(struct device *dev, void *idata) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + TO_INT(idata) = ts_data->power_disabled == false ? 1 : 0; + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int fts_mmi_methods_get_flashprog(struct device *dev, void *idata) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + TO_INT(idata) = (ts_data->fw_loading) ? 1 : 0; + + return 0; +} + +static int fts_mmi_methods_drv_irq(struct device *dev, int state) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + int ret = 0; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + + if (state == 1) { + FTS_INFO("enable irq"); + fts_irq_enable(); + } else if (state == 0) { + FTS_INFO("disable irq"); + fts_irq_disable(); + } else { + dev_err(dev, "%s: invalid value\n", __func__); + ret = -EINVAL; + } + + mutex_unlock(&input_dev->mutex); + return ret; +} + +#if FTS_POWER_SOURCE_CUST_EN +static int fts_mmi_methods_power(struct device *dev, int state) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + + return fts_power_source_ctrl(ts_data, state); +} +#endif + +static int fts_mmi_methods_reset(struct device *dev, int type) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_reset_proc(0); + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int fts_mmi_firmware_update(struct device *dev, char *fwname) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + + ts_data->force_reflash = true; + + mutex_lock(&input_dev->mutex); + fts_fw_update_vendor_name(fwname); + fts_fw_recovery(); + mutex_unlock(&input_dev->mutex); + + return 0; +} + +#if FTS_USB_DETECT_EN +static int fts_mmi_charger_mode(struct device *dev, int mode) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + ts_data->usb_detect_flag = !!mode; + + return 0; +} +#endif + +static int fts_mmi_panel_state(struct device *dev, + enum ts_mmi_pm_mode from, enum ts_mmi_pm_mode to) +{ + struct fts_ts_platform_data *pdata; + struct fts_ts_data *ts_data; + int ret = 0; + + GET_TS_DATA(dev); + pdata = ts_data->pdata; + +#ifdef FOCALTECH_SENSOR_EN + mutex_lock(&ts_data->state_mutex); +#endif + + switch (to) { + case TS_MMI_PM_DEEPSLEEP: +#ifdef CONFIG_PANEL_NOTIFICATIONS + if (pdata->notify_to_panel) + touch_set_state(TS_MMI_PM_DEEPSLEEP, TOUCH_PANEL_IDX_PRIMARY); +#endif + ret = fts_write_reg(FTS_REG_POWER_MODE, FTS_REG_POWER_MODE_SLEEP_VALUE); + if (ret < 0) + FTS_ERROR("set TP to sleep mode fail, ret=%d", ret); + + /* TP delay 50ms then lcd entery suspend*/ + mdelay(50); + break; + + case TS_MMI_PM_GESTURE: +#ifdef CONFIG_PANEL_NOTIFICATIONS + if (pdata->notify_to_panel) + touch_set_state(TS_MMI_PM_GESTURE, TOUCH_PANEL_IDX_PRIMARY); +#endif +#if FTS_GESTURE_EN + if (fts_gesture_suspend(ts_data) == 0) { + /* Enter into gesture mode(suspend) */ +#ifdef FOCALTECH_SENSOR_EN + ts_data->wakeable = true; +#endif + } +#endif + break; + + case TS_MMI_PM_ACTIVE: + break; + default: + dev_warn(dev, "panel mode %d is invalid.\n", to); + ret = -EINVAL; + break; + } + +#ifdef FOCALTECH_SENSOR_EN + mutex_unlock(&ts_data->state_mutex); +#endif + return ret; + +} + +static int fts_mmi_pre_resume(struct device *dev) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + +#ifdef FOCALTECH_SENSOR_EN + mutex_lock(&ts_data->state_mutex); +#endif + + FTS_FUNC_ENTER(); + + fts_release_all_finger(); + + FTS_FUNC_EXIT(); +#ifdef FOCALTECH_SENSOR_EN + mutex_unlock(&ts_data->state_mutex); +#endif + return 0; +} + +static int fts_mmi_post_resume(struct device *dev) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + +#ifdef FOCALTECH_SENSOR_EN + mutex_lock(&ts_data->state_mutex); +#endif + if (!ts_data->pdata->always_on_vio) { + FTS_INFO("Reset IC in resume"); + fts_reset_proc(200); + } + + fts_tp_state_recovery(ts_data); + +#if FTS_ESDCHECK_EN + fts_esdcheck_resume(); +#endif + +#if FTS_GESTURE_EN +#ifdef FOCALTECH_SENSOR_EN + if (ts_data->wakeable) { +#endif + if (fts_gesture_resume(ts_data) == 0) { +#ifdef FOCALTECH_SENSOR_EN + ts_data->wakeable = false; + FTS_INFO("Exit from gesture suspend mode."); +#endif + goto exit; + } +#ifdef FOCALTECH_SENSOR_EN + } +#endif +#endif + +exit: + FTS_FUNC_EXIT(); + ts_data->suspended = false; +#ifdef FOCALTECH_SENSOR_EN + mutex_unlock(&ts_data->state_mutex); + ts_data->screen_state = SCREEN_ON; +#endif + return 0; +} + +static int fts_mmi_pre_suspend(struct device *dev) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); +#ifdef FOCALTECH_SENSOR_EN + mutex_lock(&ts_data->state_mutex); +#endif + FTS_FUNC_ENTER(); + +#if FTS_ESDCHECK_EN + FTS_INFO("fts_esdcheck_suspend"); + fts_esdcheck_suspend(); +#endif + + FTS_FUNC_EXIT(); +#ifdef FOCALTECH_SENSOR_EN + mutex_unlock(&ts_data->state_mutex); +#endif + return 0; +} + +static int fts_mmi_post_suspend(struct device *dev) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + +#ifdef FOCALTECH_SENSOR_EN + mutex_lock(&ts_data->state_mutex); +#endif + FTS_FUNC_ENTER(); + +#ifdef FOCALTECH_SENSOR_EN + if (!ts_data->wakeable) { +#endif + if (!ts_data->pdata->always_on_vio) { + FTS_INFO("Set reset pin to 0 in suspend."); + gpio_direction_output(ts_data->pdata->reset_gpio, 0); + } + fts_release_all_finger(); +#ifdef FOCALTECH_SENSOR_EN + } +#endif + + ts_data->suspended = true; + FTS_FUNC_EXIT(); +#ifdef FOCALTECH_SENSOR_EN + ts_data->screen_state = SCREEN_OFF; + mutex_unlock(&ts_data->state_mutex); +#endif + return 0; +} + +static struct ts_mmi_methods fts_mmi_methods = { + .get_vendor = fts_mmi_methods_get_vendor, + .get_productinfo = fts_mmi_methods_get_productinfo, + .get_build_id = fts_mmi_methods_get_build_id, + .get_config_id = fts_mmi_methods_get_config_id, + .get_bus_type = fts_mmi_methods_get_bus_type, + .get_irq_status = fts_mmi_methods_get_irq_status, + .get_drv_irq = fts_mmi_methods_get_drv_irq, + .get_flashprog = fts_mmi_methods_get_flashprog, + .get_poweron = fts_mmi_methods_get_poweron, + /* SET methods */ + .reset = fts_mmi_methods_reset, + .drv_irq = fts_mmi_methods_drv_irq, +#if FTS_POWER_SOURCE_CUST_EN + .power = fts_mmi_methods_power, +#endif +#if FTS_USB_DETECT_EN + .charger_mode = fts_mmi_charger_mode, +#endif + /* Firmware */ + .firmware_update = fts_mmi_firmware_update, + /* PM callback */ + .panel_state = fts_mmi_panel_state, + .pre_resume = fts_mmi_pre_resume, + .post_resume = fts_mmi_post_resume, + .pre_suspend = fts_mmi_pre_suspend, + .post_suspend = fts_mmi_post_suspend, + +}; + +int fts_mmi_dev_register(struct fts_ts_data *ts_data) { + int ret; + + ret = ts_mmi_dev_register(ts_data->dev, &fts_mmi_methods); + if (ret) { + dev_err(ts_data->dev, "Failed to register ts mmi\n"); + return ret; + } + + /* initialize class imported methods */ + ts_data->imports = &fts_mmi_methods.exports; + + return 0; +} + +void fts_mmi_dev_unregister(struct fts_ts_data *ts_data) { + ts_mmi_dev_unregister(ts_data->dev); +} + diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Android.mk b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Android.mk new file mode 100644 index 000000000000..981183028167 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Android.mk @@ -0,0 +1,49 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifneq ($(FOCALTECH_TOUCH_IC_NAME),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME=$(FOCALTECH_TOUCH_IC_NAME) +else + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME=ft8726 +endif + +ifneq ($(BOARD_USES_DOUBLE_TAP),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_0FLASH_MMI_ENABLE_DOUBLE_TAP=y +endif + +ifneq ($(MOTO_PANEL_CHECK_TOUCH_STATE),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_0FLASH_MMI_NOTIFY_TOUCH_STATE=y +endif + +ifneq ($(FOCALTECH_ESDCHECK_ENABLE),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_ESD_EN=y +endif + +ifneq ($(FOCALTECH_DEBUG_ENABLE),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_DEBUG_EN=y +endif + +ifneq ($(BOARD_USES_PANEL_NOTIFICATIONS),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_PANEL_NOTIFICATIONS=y +endif + +ifneq ($(FOCALTECH_PINCTRL_ENABLE),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_PINCTRL_EN=y +endif + +ifeq ($(TOUCHSCREEN_LAST_TIME),true) + KERNEL_CFLAGS += CONFIG_FOCALTECH_LAST_TIME=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := focaltech_0flash_v2_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + +ifneq ($(BOARD_USES_DOUBLE_TAP),) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/sensors_class.ko +endif + +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +KBUILD_OPTIONS_GKI += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Kbuild b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Kbuild new file mode 100644 index 000000000000..93714d15bba8 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Kbuild @@ -0,0 +1,50 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/focaltech_0flash_v2_mmi \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/focaltech_0flash_v2_mmi/include + +obj-m := focaltech_0flash_v2_mmi.o +focaltech_0flash_v2_mmi-objs += focaltech_core.o +focaltech_0flash_v2_mmi-objs += focaltech_esdcheck.o +focaltech_0flash_v2_mmi-objs += focaltech_ex_fun.o +focaltech_0flash_v2_mmi-objs += focaltech_ex_mode.o +focaltech_0flash_v2_mmi-objs += focaltech_flash.o +focaltech_0flash_v2_mmi-objs += focaltech_gesture.o +focaltech_0flash_v2_mmi-objs += focaltech_point_report_check.o +focaltech_0flash_v2_mmi-objs += focaltech_spi.o + +ifneq ($(filter m y,$(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_ENABLE_DOUBLE_TAP)),) + EXTRA_CFLAGS += -DFOCALTECH_SENSOR_EN + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_NOTIFY_TOUCH_STATE)),) + EXTRA_CFLAGS += -DFTS_SET_TOUCH_STATE +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_FOCALTECH_ESD_EN)),) +EXTRA_CFLAGS += -DFOCALTECH_ESD_EN +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_FOCALTECH_PANEL_NOTIFICATIONS)),) +EXTRA_CFLAGS += -DFOCALTECH_CONFIG_PANEL_NOTIFICATIONS +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_FOCALTECH_DEBUG_EN)),) +EXTRA_CFLAGS += -DFOCALTECH_DEBUG_EN +endif + +ifneq ($(filter ft8057,$(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME)),) +EXTRA_CFLAGS += -DCONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8057 +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_FOCALTECH_PINCTRL_EN)),) +EXTRA_CFLAGS += -DFOCALTECH_PINCTRL_EN +endif + +ifneq ($(filter m y,$(CONFIG_FOCALTECH_LAST_TIME)),) + EXTRA_CFLAGS += -DFTS_LAST_TIME_EN +endif + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/../../kernel/msm-$(MODULE_KERNEL_VERSION)/Module.symvers diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Kconfig b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Kconfig new file mode 100755 index 000000000000..e2bf1085bbf0 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Kconfig @@ -0,0 +1,16 @@ +# +# Focaltech Touchscreen driver configuration +# + +config TOUCHSCREEN_FTS + bool "Focaltech Touchscreen" + default n + help + Say Y here if you have Focaltech touch panel. + If unsure, say N. + +config TOUCHSCREEN_FTS_DIRECTORY + string "Focaltech ts directory name" + default "focaltech_touch" + depends on TOUCHSCREEN_FTS + diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Makefile b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Makefile.kernel b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Makefile.kernel new file mode 100755 index 000000000000..e48fa8a073f2 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/Makefile.kernel @@ -0,0 +1,14 @@ +# Makefile for the focaltech touchscreen drivers. + + +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_core.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_ex_fun.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_ex_mode.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_gesture.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_esdcheck.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_point_report_check.o + + +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_spi.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_flash.o + diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_common.h b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_common.h new file mode 100644 index 000000000000..f581764f6ff5 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_common.h @@ -0,0 +1,200 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2020, 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.4 20211214" + +#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 (FTS_CHIP_TYPE & FLAGBITS(0, FLAG_ICSERIALS_LEN-1)) +#define IC_TO_SERIALS(x) ((x) & FLAGBITS(0, FLAG_ICSERIALS_LEN-1)) +#define FTS_CHIP_IDC ((FTS_CHIP_TYPE & FLAGBIT(FLAG_IDC_BIT)) == FLAGBIT(FLAG_IDC_BIT)) +#define FTS_HID_SUPPORTTED ((FTS_CHIP_TYPE & FLAGBIT(FLAG_HID_BIT)) == FLAGBIT(FLAG_HID_BIT)) + +#define FTS_MAX_CHIP_IDS 8 + +#if defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8726) +#define FTS_CHIP_TYPE_MAPPING {{0x1C, 0x87, 0x20, 0x87, 0x20, 0x87, 0xA0, 0x00, 0x00}} +#define FTS_CHIP_ID_MAPPING {{0x1C, {0x8726, 0x872A, 0x872B}}} +#elif defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8057) +#define FTS_CHIP_TYPE_MAPPING {{0x28, 0x80, 0x57, 0x80, 0x57, 0x80, 0xA7, 0x00, 0x00}} +#define FTS_CHIP_ID_MAPPING {{0x1C, {0x8726, 0x872A, 0x872B}}} +#else +#define FTS_CHIP_TYPE_MAPPING {{0x1C, 0x87, 0x20, 0x87, 0x20, 0x87, 0xA0, 0x00, 0x00}} +#define FTS_CHIP_ID_MAPPING {{0x1C, {0x8726, 0x872A, 0x872B}}} +#endif + +#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_INFO 0x5E + +/*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_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 { + u16 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 ft_chip_id_t { + u16 type; + u16 chip_ids[FTS_MAX_CHIP_IDS]; +}; + +struct ts_ic_info { + bool is_incell; + bool hid_supported; + struct ft_chip_t ids; + struct ft_chip_id_t cid; +}; + +extern bool dbg_level_en; + +/***************************************************************************** +* 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...) do { \ + pr_debug("[FTS_TS]%s:"fmt"\n", __func__, ##args); \ +} while (0) + +#define FTS_FUNC_ENTER() do { \ + pr_debug("[FTS_TS]%s: Enter\n", __func__); \ +} while (0) + +#define FTS_FUNC_EXIT() do { \ + pr_debug("[FTS_TS]%s: Exit(%d)\n", __func__, __LINE__); \ +} while (0) +#endif + +#define FTS_DBG_LEVEL(fmt, args...) do { \ + if (dbg_level_en) \ + printk("[FTS_TS/D]%s:"fmt"\n", __func__, ##args); \ +} while (0) + +#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__ */ diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_config.h b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_config.h new file mode 100644 index 000000000000..a8090b73d952 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_config.h @@ -0,0 +1,340 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 _FT8607 0x86070809 +#define _FT8006U 0x8006D80B +#define _FT8006S 0x8006A80B +#define _FT8613 0x8613080C +#define _FT8719 0x8719080D +#define _FT8739 0x8739080E +#define _FT8615 0x8615080F +#define _FT8201 0x82010810 +#define _FT8201AA 0x8201A810 +#define _FT8006P 0x86220811 +#define _FT8613S 0x8613C814 +#define _FT8756 0x87560815 +#define _FT8656 0x86560815 +#define _FT8756M 0x8756A815 +#define _FT8302 0x83020816 +#define _FT8009 0x80090817 +#define _FT8006S_AA 0x86320819 +#define _FT7250 0x7250081A +#define _FT7120 0x7120081B +#define _FT8720 0x8720081C +#define _FT8726 0x8726081C +#define _FT8720H 0x8720E81C +#define _FT8720M 0x8720F81C +#define _FT8016 0x8016081D +#define _FT2388 0x2388081E +#define _FT8006S_AB 0x8642081F +#define _FT8722 0x87220820 +#define _FT8201AB 0x8201B821 +#define _FT8203 0x82030821 +#define _FT8201M 0x8201C821 +#define _FT8006S_AN 0x8006B822 +#define _FT7131 0x71310823 +#define _FT7250AB 0x7250B824 +#define _FT7130 0x71300825 +#define _FT8205 0x82050826 +#define _FT8057 0x80570828 + + + + +#define _FT5426 0x54260402 +#define _FT5436 0x54360402 +#define _FT5526 0x55260402 +#define _FT5446 0x54460402 +#define _FT5346 0x53460402 +#define _FT7661 0x76610402 +#define _FT7511 0x75110402 +#define _FT7421 0x74210402 +#define _FT7681 0x76810402 +#define _FT3417 0x34170402 +#define _FT3517 0x35170402 +#define _FT3327 0x33270402 +#define _FT3427 0x34270402 +#define _FT7311 0x73110402 +#define _FT5526_V00 0x5526C402 + +#define _FT5726 0x57260401 +#define _FT5826S 0x5826C401 +#define _FT7811 0x78110401 +#define _FT3617 0x36170401 +#define _FT3717 0x37170401 + +#define _FT6236U 0x6236D003 +#define _FT6336G 0x6336A003 +#define _FT6336U 0x6336D003 +#define _FT6436U 0x6436D003 +#define _FT6436T 0x6436E003 + +#define _FT3267 0x32670004 +#define _FT3367 0x33670004 + +#define _FT5446_Q03 0x5446C482 +#define _FT5446_P03 0x5446A481 +#define _FT5446_N03 0x5446A489 +#define _FT5426_003 0x5426D482 +#define _FT5526_003 0x5526D482 +#define _FT3437_003 0x34370482 + +#define _FT3518 0x35180481 +#define _FT3518U 0x3518D481 +#define _FT3558 0x35580481 +#define _FT3528 0x35280481 +#define _FT5536 0x55360481 +#define _FT3418 0x34180481 + +#define _FT5446U 0x5446D083 +#define _FT5456U 0x5456D083 +#define _FT3417U 0x3417D083 +#define _FT5426U 0x5426D083 +#define _FT3428 0x34280083 + +#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 _FT3168G 0x3168A085 + +#define _FT5726_003 0x5726D486 +#define _FT5726_V03 0x5726C486 +#define _FT3617_003 0x3617D486 + +#define _FT3618 0x36180487 +#define _FT5646 0x56460487 +#define _FT3A58 0x3A580487 +#define _FT3B58 0x3B580487 +#define _FT3D58 0x3D580487 +#define _FT3D59 0x3D590487 +#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 +#define _FT3658G 0x3658A488 + +#define _FT3519 0x35190489 +#define _FT3519T 0x3519E489 +#define _FT3419 0x34190489 +#define _FT5536U_003 0x5536D489 +#define _FT5426G 0x5426A489 +#define _FT3437_N03 0x34370489 + +#define _FT3680 0x3680008A +#define _FT368A 0x368A008A +#define _FT3681 0x3681008A +#define _FT3881 0x3881008A + +#define _FT3169 0x3169008B +#define _FT3269 0x3269008B + + + +/*************************************************/ + +/* + * choose your ic chip type of focaltech + */ +#if defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8726) +#define FTS_CHIP_TYPE _FT8726 +#define FTS_CHIP_NAME "ft8726" +#elif defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT8057) +#define FTS_CHIP_TYPE _FT8057 +#define FTS_CHIP_NAME "ft8057" +#else +#define FTS_CHIP_TYPE _FT8726 +#define FTS_CHIP_NAME "ft8726" +#endif + +#define FTS_PRIMARY_NAME "primary" + +/******************* Enables *********************/ +/*********** 1 to enable, 0 to disable ***********/ + +/* + * show debug log info + * enable it for debug, disable it for release + */ +#ifdef FOCALTECH_DEBUG_EN +#define FTS_DEBUG_EN 1 +#else +#define FTS_DEBUG_EN 0 +#endif + +/* + * 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 + +/* + * Stylus PEN enable + * 1:enable(default),0:disable +*/ +#define FTS_PEN_EN 0 + +/* + * Gesture function enable + * default: disable + */ +#ifdef FOCALTECH_SENSOR_EN +#define FTS_GESTURE_EN 1 +#else +#define FTS_GESTURE_EN 0 +#endif + +/* + * ESD check & protection + * default: disable + */ +#ifdef FOCALTECH_ESD_EN +#define FTS_ESDCHECK_EN 1 +#else +#define FTS_ESDCHECK_EN 0 +#endif + + +/* + * Pinctrl enable + * default: disable + */ +#ifdef FOCALTECH_PINCTRL_EN +#define FTS_PINCTRL_EN 1 +#else +#define FTS_PINCTRL_EN 0 +#endif +/* + * Customer power enable + * enable it when customer need control TP power + * default: disable + */ +#define FTS_POWER_SOURCE_CUST_EN 0 + +/****************************************************/ + +/********************** Upgrade ****************************/ +/* + * auto upgrade + */ +#define FTS_AUTO_UPGRADE_EN 0 + +/* + * auto upgrade for lcd cfg + */ +#define FTS_AUTO_LIC_UPGRADE_EN 0 + +/* + * Numbers of modules support + */ +#define FTS_GET_MODULE_NUM 0 + +/* + * Usb detect support + */ +#if (FTS_CHIP_TYPE == _FT8726) +#define FTS_USB_DETECT_EN 0 +#else +#define FTS_USB_DETECT_EN 1 +#endif + +/* + * 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 0x0000 +#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 "" +#define FTS_MODULE2_NAME "" +#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 + * NOTE: git may ignore xxx.i file + */ +#define FTS_UPGRADE_FW_FILE "include/firmware/fw_sample.h" + +/* + * 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.h" + +/* + * 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.h" + +/*********************************************************/ + +#endif /* _LINUX_FOCLATECH_CONFIG_H_ */ diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_core.c b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_core.c new file mode 100644 index 000000000000..41ebfb0f3421 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_core.c @@ -0,0 +1,2512 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 +#include +#include +#include +#include +#include +#if defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) +#include +#else +#include +#endif +#elif defined(CONFIG_FB) +#include +#include +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#define FTS_SUSPEND_LEVEL 1 /* Early-suspend level */ +#endif +#include "focaltech_core.h" + +#ifdef FOCALTECH_CONFIG_PANEL_NOTIFICATIONS +#define register_panel_notifier panel_register_notifier +#define unregister_panel_notifier panel_unregister_notifier +enum touch_state { + TOUCH_DEEP_SLEEP_STATE = 0, + TOUCH_LOW_POWER_STATE, +}; +#endif + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_DRIVER_NAME "fts_ts" +#define FTS_DRIVER_PEN_NAME "fts_ts,pen" +#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 2800000 +#define FTS_VTG_MAX_UV 3300000 +#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; + +bool dbg_level_en = 0; + +#ifdef FTS_LAST_TIME_EN +static bool time_flag = 1; +#endif + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +static int fts_ts_suspend(struct device *dev); +static int fts_ts_resume(struct device *dev); + +int fts_check_cid(struct fts_ts_data *ts_data, u8 id_h) +{ + int i = 0; + struct ft_chip_id_t *cid = &ts_data->ic_info.cid; + u8 cid_h = 0x0; + + if (cid->type == 0) + return -ENODATA; + + for (i = 0; i < FTS_MAX_CHIP_IDS; i++) { + cid_h = ((cid->chip_ids[i] >> 8) & 0x00FF); + if (cid_h && (id_h == cid_h)) { + return 0; + } + } + + return -ENODATA; +} + +/***************************************************************************** +* 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; + struct fts_ts_data *ts_data = fts_data; + u8 chip_idh = ts_data->ic_info.ids.chip_idh; + + do { + ret = fts_read_reg(FTS_REG_CHIP_ID, &idh); + if ((idh == chip_idh) || (fts_check_cid(ts_data, idh) == 0)) { + FTS_INFO("TP Ready,Device ID:0x%02x", idh); + return 0; + } else + FTS_DEBUG("TP Not Ready,ReadData:0x%02x,ret:%d", idh, ret); + + 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 */ +#if FTS_GESTURE_EN + fts_gesture_recovery(ts_data); +#endif + 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) { + disable_irq_nosync(fts_data->irq); + 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) { + enable_irq(fts_data->irq); + 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"); + } else { + msleep(10); + 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 ((0xEB == buf[0]) && (0xAA == buf[1]) && (0x08 == buf[2])) { + FTS_DEBUG("hidi2c change to stdi2c successful"); + } else { + FTS_DEBUG("hidi2c change to stdi2c not support or fail"); + } + } +} + +static int fts_match_cid(struct fts_ts_data *ts_data, + u16 type, u8 id_h, u8 id_l, bool force) +{ +#ifdef FTS_CHIP_ID_MAPPING + u32 i = 0; + u32 j = 0; + struct ft_chip_id_t chip_id_list[] = FTS_CHIP_ID_MAPPING; + u32 cid_entries = sizeof(chip_id_list) / sizeof(struct ft_chip_id_t); + u16 id = (id_h << 8) + id_l; + + memset(&ts_data->ic_info.cid, 0, sizeof(struct ft_chip_id_t)); + for (i = 0; i < cid_entries; i++) { + if (!force && (type == chip_id_list[i].type)) { + break; + } else if (force && (type == chip_id_list[i].type)) { + FTS_INFO("match cid,type:0x%x", (int)chip_id_list[i].type); + ts_data->ic_info.cid = chip_id_list[i]; + return 0; + } + } + + if (i >= cid_entries) { + return -ENODATA; + } + + for (j = 0; j < FTS_MAX_CHIP_IDS; j++) { + if (id == chip_id_list[i].chip_ids[j]) { + FTS_DEBUG("cid:%x==%x", id, chip_id_list[i].chip_ids[j]); + FTS_INFO("match cid,type:0x%x", (int)chip_id_list[i].type); + ts_data->ic_info.cid = chip_id_list[i]; + return 0; + } + } + + return -ENODATA; +#else + return -EINVAL; +#endif +} + + +static int fts_get_chip_types( + struct fts_ts_data *ts_data, + u8 id_h, u8 id_l, bool fw_valid) +{ + u32 i = 0; + struct ft_chip_t ctype[] = FTS_CHIP_TYPE_MAPPING; + 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_INFO("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)) + || (!fts_match_cid(ts_data, ctype[i].type, id_h, id_l, 0))) + 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; + } + + fts_match_cid(ts_data, ctype[i].type, id_h, id_l, 1); + 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 }; + + ts_data->ic_info.is_incell = FTS_CHIP_IDC; + ts_data->ic_info.hid_supported = FTS_HID_SUPPORTTED; + + for (cnt = 0; cnt < 3; cnt++) { + fts_reset_proc(0); + mdelay(FTS_CMD_START_DELAY + (cnt * 8)); + + ret = fts_read_bootid(ts_data, &chip_id[0]); + if (ret < 0) { + FTS_DEBUG("read boot id fail,retry:%d", cnt); + continue; + } + + ret = fts_get_chip_types(ts_data, chip_id[0], chip_id[1], INVALID); + if (ret < 0) { + FTS_DEBUG("can't get ic informaton,retry:%d", cnt); + continue; + } + + break; + } + + if (cnt >= 3) { + FTS_ERROR("get ic informaton fail"); + return -EIO; + } + + + FTS_INFO("get ic information, chip id = 0x%02x%02x(cid type=0x%x)", + ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl, + ts_data->ic_info.cid.type); + + return 0; +} + +/***************************************************************************** +* Reprot related +*****************************************************************************/ +static void fts_show_touch_buffer(u8 *data, u32 datalen) +{ + u32 i = 0; + u32 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("touch_buf:%s", tmpbuf); + + if (tmpbuf) { + kfree(tmpbuf); + tmpbuf = NULL; + } +} + +void fts_release_all_finger(void) +{ + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; +#if FTS_MT_PROTOCOL_B_EN + u32 finger_count = 0; + u32 max_touches = ts_data->pdata->max_touch_number; +#endif + + 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); + +#if FTS_PEN_EN + input_report_key(ts_data->pen_dev, BTN_TOOL_PEN, 0); + input_report_key(ts_data->pen_dev, BTN_TOUCH, 0); + input_sync(ts_data->pen_dev); +#endif + + ts_data->touch_points = 0; + ts_data->key_state = 0; + mutex_unlock(&ts_data->report_mutex); +} + +/***************************************************************************** +* 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 *ts_data, struct ts_event *kevent) +{ + int i = 0; + int x = kevent->x; + int y = kevent->y; + int *x_dim = &ts_data->pdata->key_x_coords[0]; + int *y_dim = &ts_data->pdata->key_y_coords[0]; + + if (!ts_data->pdata->have_key) { + return -EINVAL; + } + for (i = 0; i < ts_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(kevent->flag) + && !(ts_data->key_state & (1 << i))) { + input_report_key(ts_data->input_dev, ts_data->pdata->keys[i], 1); + ts_data->key_state |= (1 << i); + FTS_DEBUG("Key%d(%d,%d) DOWN!", i, x, y); + } else if (EVENT_UP(kevent->flag) + && (ts_data->key_state & (1 << i))) { + input_report_key(ts_data->input_dev, ts_data->pdata->keys[i], 0); + ts_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 *ts_data, struct ts_event *events) +{ + int i = 0; + int touch_down_point_cur = 0; + int touch_point_pre = ts_data->touch_points; + u32 max_touch_num = ts_data->pdata->max_touch_number; + bool touch_event_coordinate = false; + struct input_dev *input_dev = ts_data->input_dev; +#ifdef FTS_LAST_TIME_EN + bool b_touch_down = 0; +#endif + + for (i = 0; i < ts_data->touch_event_num; i++) { + if (fts_input_report_key(ts_data, &events[i]) == 0) { + continue; + } + + touch_event_coordinate = true; + if (EVENT_DOWN(events[i].flag)) { + input_mt_slot(input_dev, events[i].id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true); +#if FTS_REPORT_PRESSURE_EN + input_report_abs(input_dev, ABS_MT_PRESSURE, events[i].p); +#endif + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, events[i].area); + input_report_abs(input_dev, ABS_MT_POSITION_X, events[i].x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, events[i].y); + + touch_down_point_cur |= (1 << events[i].id); + touch_point_pre |= (1 << events[i].id); + +#ifdef FTS_LAST_TIME_EN + if (FTS_TOUCH_DOWN == events[i].flag) { + b_touch_down = 1; + } +#endif + + if ((ts_data->log_level >= 2) || + (dbg_level_en && (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 { + input_mt_slot(input_dev, events[i].id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + touch_point_pre &= ~(1 << events[i].id); + FTS_DBG_LEVEL("[B]P%d UP!", events[i].id); + } + } + + if (unlikely(touch_point_pre ^ touch_down_point_cur)) { + for (i = 0; i < max_touch_num; i++) { + if ((1 << i) & (touch_point_pre ^ touch_down_point_cur)) { + FTS_DBG_LEVEL("[B]P%d UP!", i); + input_mt_slot(input_dev, i); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + } + } + } + + if (touch_down_point_cur) + input_report_key(input_dev, BTN_TOUCH, 1); + else if (touch_event_coordinate || ts_data->touch_points) { + if (ts_data->touch_points && dbg_level_en) + FTS_DEBUG("[B]Points All Up!"); + input_report_key(input_dev, BTN_TOUCH, 0); +#ifdef FTS_LAST_TIME_EN + //enable time_flag when touch All Up for next touch session + time_flag = 1; +#endif + } + +#ifdef FTS_LAST_TIME_EN + //check b_touch_down && time_flag to get boot time only once in one touch session, + //to avoid impact touch performance for multi touch & moving cases + if (b_touch_down && time_flag) { + ts_data->last_event_time = ktime_get_boottime(); + time_flag = 0; + //FTS_DEBUG("time_flag, save last_event_time when touch Down"); + } +#endif + + ts_data->touch_points = touch_down_point_cur; + input_sync(input_dev); + return 0; +} +#else +static int fts_input_report_a(struct fts_ts_data *ts_data, struct ts_event *events) +{ + int i = 0; + int touch_down_point_num_cur = 0; + bool touch_event_coordinate = false; + struct input_dev *input_dev = ts_data->input_dev; + + for (i = 0; i < ts_data->touch_event_num; i++) { + if (fts_input_report_key(ts_data, &events[i]) == 0) { + continue; + } + + touch_event_coordinate = true; + if (EVENT_DOWN(events[i].flag)) { + input_report_abs(input_dev, ABS_MT_TRACKING_ID, events[i].id); +#if FTS_REPORT_PRESSURE_EN + input_report_abs(input_dev, ABS_MT_PRESSURE, events[i].p); +#endif + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, events[i].area); + input_report_abs(input_dev, ABS_MT_POSITION_X, events[i].x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, events[i].y); + input_mt_sync(input_dev); + + touch_down_point_num_cur++; + if ((ts_data->log_level >= 2) || + ((1 == ts_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); + } + } + } + + if (touch_down_point_num_cur) + input_report_key(input_dev, BTN_TOUCH, 1); + else if (touch_event_coordinate || ts_data->touch_points) { + if (ts_data->touch_points && (ts_data->log_level >= 1)) + FTS_DEBUG("[A]Points All Up!"); + input_report_key(input_dev, BTN_TOUCH, 0); + input_mt_sync(input_dev); + } + + ts_data->touch_points = touch_down_point_num_cur; + input_sync(input_dev); + return 0; +} +#endif + +#if FTS_PEN_EN +static int fts_input_pen_report(struct fts_ts_data *ts_data, u8 *pen_buf) +{ + struct input_dev *pen_dev = ts_data->pen_dev; + struct pen_event *pevt = &ts_data->pevent; + + /*get information of stylus*/ + pevt->inrange = (pen_buf[2] & 0x20) ? 1 : 0; + pevt->tip = (pen_buf[2] & 0x01) ? 1 : 0; + pevt->flag = pen_buf[3] >> 6; + pevt->id = pen_buf[5] >> 4; + pevt->x = ((pen_buf[3] & 0x0F) << 8) + pen_buf[4]; + pevt->y = ((pen_buf[5] & 0x0F) << 8) + pen_buf[6]; + pevt->p = ((pen_buf[7] & 0x0F) << 8) + pen_buf[8]; + pevt->tilt_x = (short)((pen_buf[9] << 8) + pen_buf[10]); + pevt->tilt_y = (short)((pen_buf[11] << 8) + pen_buf[12]); + pevt->azimuth = ((pen_buf[13] << 8) + pen_buf[14]); + pevt->tool_type = BTN_TOOL_PEN; + + input_report_key(pen_dev, BTN_STYLUS, !!(pen_buf[2] & 0x02)); + input_report_key(pen_dev, BTN_STYLUS2, !!(pen_buf[2] & 0x08)); + + switch (ts_data->pen_etype) { + case STYLUS_DEFAULT: + if (pevt->tip && pevt->p) { + if ((ts_data->log_level >= 2) || (!pevt->down)) + FTS_DEBUG("[PEN]x:%d,y:%d,p:%d,tip:%d,flag:%d,tilt:%d,%d DOWN", + pevt->x, pevt->y, pevt->p, pevt->tip, pevt->flag, + pevt->tilt_x, pevt->tilt_y); + input_report_abs(pen_dev, ABS_X, pevt->x); + input_report_abs(pen_dev, ABS_Y, pevt->y); + input_report_abs(pen_dev, ABS_PRESSURE, pevt->p); + input_report_abs(pen_dev, ABS_TILT_X, pevt->tilt_x); + input_report_abs(pen_dev, ABS_TILT_Y, pevt->tilt_y); + input_report_key(pen_dev, BTN_TOUCH, 1); + input_report_key(pen_dev, BTN_TOOL_PEN, 1); + pevt->down = 1; + } else if (!pevt->tip && pevt->down) { + FTS_DEBUG("[PEN]x:%d,y:%d,p:%d,tip:%d,flag:%d,tilt:%d,%d UP", + pevt->x, pevt->y, pevt->p, pevt->tip, pevt->flag, + pevt->tilt_x, pevt->tilt_y); + input_report_abs(pen_dev, ABS_X, pevt->x); + input_report_abs(pen_dev, ABS_Y, pevt->y); + input_report_abs(pen_dev, ABS_PRESSURE, pevt->p); + input_report_key(pen_dev, BTN_TOUCH, 0); + input_report_key(pen_dev, BTN_TOOL_PEN, 0); + pevt->down = 0; + } + input_sync(pen_dev); + break; + case STYLUS_HOVER: + if (ts_data->log_level >= 1) + FTS_DEBUG("[PEN][%02X]x:%d,y:%d,p:%d,tip:%d,flag:%d,tilt:%d,%d,%d", + pen_buf[2], pevt->x, pevt->y, pevt->p, pevt->tip, + pevt->flag, pevt->tilt_x, pevt->tilt_y, pevt->azimuth); + input_report_abs(pen_dev, ABS_X, pevt->x); + input_report_abs(pen_dev, ABS_Y, pevt->y); + input_report_abs(pen_dev, ABS_Z, pevt->azimuth); + input_report_abs(pen_dev, ABS_PRESSURE, pevt->p); + input_report_abs(pen_dev, ABS_TILT_X, pevt->tilt_x); + input_report_abs(pen_dev, ABS_TILT_Y, pevt->tilt_y); + input_report_key(pen_dev, BTN_TOOL_PEN, EVENT_DOWN(pevt->flag)); + input_report_key(pen_dev, BTN_TOUCH, pevt->tip); + input_sync(pen_dev); + break; + default: + FTS_ERROR("Unknown stylus event"); + break; + } + + return 0; +} +#endif + +static int fts_read_touchdata(struct fts_ts_data *ts_data, u8 *buf) +{ + int ret = 0; + + ts_data->touch_addr = 0x01; + //FTS_DEBUG("touch_size=%d", ts_data->touch_size); + ret = fts_read(&ts_data->touch_addr, 1, buf, ts_data->touch_size); + + if (((0xEF == buf[1]) && (0xEF == buf[2]) && (0xEF == buf[3])) + || ((ret < 0) && (0xEF == buf[0]))) { + fts_release_all_finger(); + /* check if need recovery fw */ + fts_fw_recovery(); + ts_data->fw_is_running = true; + return 1; + } else if (ret < 0) { + FTS_ERROR("touch data(%x) abnormal,ret:%d", buf[1], ret); + return ret; + } + + return 0; +} + + +static int fts_read_parse_touchdata(struct fts_ts_data *ts_data, u8 *touch_buf) +{ + int ret = 0; +#if FTS_GESTURE_EN + u8 gesture_en = 0xFF; +#endif + + memset(touch_buf, 0xFF, FTS_MAX_TOUCH_BUF); + ts_data->ta_size = ts_data->touch_size; + + /*read touch data*/ + ret = fts_read_touchdata(ts_data, touch_buf); + if (ret < 0) { + FTS_ERROR("read touch data fails"); + return TOUCH_ERROR; + } + + if (ts_data->log_level >= 3) + fts_show_touch_buffer(touch_buf, ts_data->ta_size); + + if (ret) + return TOUCH_IGNORE; + +#if FTS_GESTURE_EN + /*gesture*/ + if (ts_data->suspended && ts_data->gesture_support) { + ret = fts_read_reg(FTS_REG_GESTURE_EN, &gesture_en); + if ((ret >= 0) && (gesture_en == ENABLE)) + return TOUCH_GESTURE; + else + FTS_DEBUG("gesture not enable in fw, don't process gesture"); + } +#endif + + if ((touch_buf[1] == 0xFF) && (touch_buf[2] == 0xFF) + && (touch_buf[3] == 0xFF) && (touch_buf[4] == 0xFF)) { + FTS_INFO("touch buff is 0xff, need recovery state"); + return TOUCH_FW_INIT; + } + + return ((touch_buf[FTS_TOUCH_E_NUM] >> 4) & 0x0F); +} + +#if FTS_USB_DETECT_EN +static void fts_mcu_usb_detect_set(uint8_t usb_connected) +{ + uint8_t write_data = 0; + uint8_t read_data = 0; + uint8_t retry_cnt = 0; + int ret = 0; + + do{ + if (usb_connected == 0x01) { + write_data= 0x01; + ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, write_data); + if (ret < 0) + FTS_ERROR("set register USB IN fail, ret=%d", ret); + FTS_INFO("%s: USB detect status IN!\n", __func__); + } else { + write_data= 0x00; + ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, write_data); + if (ret < 0) + FTS_ERROR("set register USB OUT fail, ret=%d", ret); + FTS_INFO("%s: USB detect status OUT!\n", __func__); + } + + ret = fts_read_reg(FTS_REG_CHARGER_MODE_EN, &read_data); + if (ret < 0) + FTS_ERROR("read 8b register fail, ret=%d", ret); + retry_cnt++; + }while((write_data != read_data) && retry_cnt < FTS_REG_RETRY_TIMES); +} + +void fts_cable_detect_func(bool force_renew) +{ + struct fts_ts_data *ts_data = fts_data; + uint8_t connect_status = 0; + connect_status = ts_data->usb_detect_flag; + + if ((connect_status != ts_data->usb_connected) || force_renew) { + if (connect_status) { + ts_data->usb_connected = 0x01; + } else { + ts_data->usb_connected = 0x00; + } + + fts_mcu_usb_detect_set(ts_data->usb_connected); + FTS_INFO("%s: Cable status change: 0x%2.2X\n", __func__, ts_data->usb_connected); + } +} +#endif + +static int fts_irq_read_report(struct fts_ts_data *ts_data) +{ + int i = 0; + int max_touch_num = ts_data->pdata->max_touch_number; + int touch_etype = 0; + u8 event_num = 0; + u8 finger_num = 0; + u8 pointid = 0; + u8 base = 0; + u8 *touch_buf = ts_data->touch_buf; + struct ts_event *events = ts_data->events; + +#if FTS_USB_DETECT_EN + fts_cable_detect_func(false); +#endif + + touch_etype = fts_read_parse_touchdata(ts_data, touch_buf); + switch (touch_etype) { + case TOUCH_DEFAULT: + finger_num = touch_buf[FTS_TOUCH_E_NUM] & 0x0F; + if (finger_num > max_touch_num) { + FTS_ERROR("invalid point_num(%d)", finger_num); + return -EIO; + } + + for (i = 0; i < max_touch_num; i++) { + base = FTS_ONE_TCH_LEN * i + 2; + pointid = (touch_buf[FTS_TOUCH_OFF_ID_YH + 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; + } + + events[i].id = pointid; + events[i].flag = touch_buf[FTS_TOUCH_OFF_E_XH + base] >> 6; + events[i].x = ((touch_buf[FTS_TOUCH_OFF_E_XH + base] & 0x0F) << 8) \ + + (touch_buf[FTS_TOUCH_OFF_XL + base] & 0xFF); + events[i].y = ((touch_buf[FTS_TOUCH_OFF_ID_YH + base] & 0x0F) << 8) \ + + (touch_buf[FTS_TOUCH_OFF_YL + base] & 0xFF); + events[i].p = touch_buf[FTS_TOUCH_OFF_PRE + base]; + events[i].area = touch_buf[FTS_TOUCH_OFF_AREA + base]; + if (events[i].p <= 0) events[i].p = 0x3F; + if (events[i].area <= 0) events[i].area = 0x09; + + event_num++; + if (EVENT_DOWN(events[i].flag) && (finger_num == 0)) { + FTS_INFO("abnormal touch data from fw"); + return -EIO; + } + } + + if (event_num == 0) { + FTS_INFO("no touch point information(%02x)", touch_buf[2]); + return -EIO; + } + ts_data->touch_event_num = event_num; + + mutex_lock(&ts_data->report_mutex); +#if FTS_MT_PROTOCOL_B_EN + fts_input_report_b(ts_data, events); +#else + fts_input_report_a(ts_data, events); +#endif + mutex_unlock(&ts_data->report_mutex); + break; + +#if FTS_PEN_EN + case TOUCH_PEN: + mutex_lock(&ts_data->report_mutex); + fts_input_pen_report(ts_data, touch_buf); + mutex_unlock(&ts_data->report_mutex); + break; +#endif + + case TOUCH_EVENT_NUM: + event_num = touch_buf[FTS_TOUCH_E_NUM] & 0x0F; + if (!event_num || (event_num > max_touch_num)) { + FTS_ERROR("invalid touch event num(%d)", event_num); + return -EIO; + } + + ts_data->touch_event_num = event_num; + for (i = 0; i < event_num; i++) { + base = FTS_ONE_TCH_LEN * i + 2; + pointid = (touch_buf[FTS_TOUCH_OFF_ID_YH + base]) >> 4; + if (pointid >= max_touch_num) { + FTS_ERROR("touch point ID(%d) beyond max_touch_number(%d)", + pointid, max_touch_num); + return -EINVAL; + } + + events[i].id = pointid; + events[i].flag = touch_buf[FTS_TOUCH_OFF_E_XH + base] >> 6; + events[i].x = ((touch_buf[FTS_TOUCH_OFF_E_XH + base] & 0x0F) << 8) \ + + (touch_buf[FTS_TOUCH_OFF_XL + base] & 0xFF); + events[i].y = ((touch_buf[FTS_TOUCH_OFF_ID_YH + base] & 0x0F) << 8) \ + + (touch_buf[FTS_TOUCH_OFF_YL + base] & 0xFF); + events[i].p = touch_buf[FTS_TOUCH_OFF_PRE + base]; + events[i].area = touch_buf[FTS_TOUCH_OFF_AREA + base]; + if (events[i].p <= 0) events[i].p = 0x3F; + if (events[i].area <= 0) events[i].area = 0x09; + } + + mutex_lock(&ts_data->report_mutex); +#if FTS_MT_PROTOCOL_B_EN + fts_input_report_b(ts_data, events); +#else + fts_input_report_a(ts_data, events); +#endif + mutex_unlock(&ts_data->report_mutex); + break; + + case TOUCH_EXTRA_MSG: + if (!ts_data->touch_analysis_support) { + FTS_ERROR("touch_analysis is disabled"); + return -EINVAL; + } + + event_num = touch_buf[FTS_TOUCH_E_NUM] & 0x0F; + if (!event_num || (event_num > max_touch_num)) { + FTS_ERROR("invalid touch event num(%d)", event_num); + return -EIO; + } + + ts_data->touch_event_num = event_num; + for (i = 0; i < event_num; i++) { + base = FTS_ONE_TCH_LEN * i + 4; + pointid = (touch_buf[FTS_TOUCH_OFF_ID_YH + base]) >> 4; + if (pointid >= max_touch_num) { + FTS_ERROR("touch point ID(%d) beyond max_touch_number(%d)", + pointid, max_touch_num); + return -EINVAL; + } + + events[i].id = pointid; + events[i].flag = touch_buf[FTS_TOUCH_OFF_E_XH + base] >> 6; + events[i].x = ((touch_buf[FTS_TOUCH_OFF_E_XH + base] & 0x0F) << 8) \ + + (touch_buf[FTS_TOUCH_OFF_XL + base] & 0xFF); + events[i].y = ((touch_buf[FTS_TOUCH_OFF_ID_YH + base] & 0x0F) << 8) \ + + (touch_buf[FTS_TOUCH_OFF_YL + base] & 0xFF); + events[i].p = touch_buf[FTS_TOUCH_OFF_PRE + base]; + events[i].area = touch_buf[FTS_TOUCH_OFF_AREA + base]; + if (events[i].p <= 0) events[i].p = 0x3F; + if (events[i].area <= 0) events[i].area = 0x09; + } + + mutex_lock(&ts_data->report_mutex); +#if FTS_MT_PROTOCOL_B_EN + fts_input_report_b(ts_data, events); +#else + fts_input_report_a(ts_data, events); +#endif + mutex_unlock(&ts_data->report_mutex); + break; + +#if FTS_GESTURE_EN + case TOUCH_GESTURE: + FTS_DEBUG("handle TOUCH_GESTURE"); + if (0 == fts_gesture_readdata(ts_data, touch_buf)) { + FTS_INFO("succuss to get gesture data in irq handler"); + } + break; +#endif + + case TOUCH_FW_INIT: + fts_release_all_finger(); + fts_tp_state_recovery(ts_data); + break; + + case TOUCH_IGNORE: + case TOUCH_ERROR: + break; + + default: + FTS_INFO("unknown touch event(%d)", touch_etype); + break; + } + + return 0; +} + +static irqreturn_t fts_irq_handler(int irq, void *data) +{ + struct fts_ts_data *ts_data = fts_data; +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM + int ret = 0; + + if ((ts_data->suspended) && (ts_data->pm_suspend)) { + ret = wait_for_completion_timeout( + &ts_data->pm_completion, + msecs_to_jiffies(FTS_TIMEOUT_COMERR_PM)); + if (!ret) { + FTS_ERROR("Bus don't resume from pm(deep),timeout,skip irq"); + return IRQ_HANDLED; + } + } +#endif + + ts_data->intr_jiffies = jiffies; +#if FTS_POINT_REPORT_CHECK_EN + fts_prc_queue_work(ts_data); +#endif + fts_irq_read_report(ts_data); + if (ts_data->touch_analysis_support && ts_data->ta_flag) { + ts_data->ta_flag = 0; + if (ts_data->ta_buf && ts_data->ta_size) + memcpy(ts_data->ta_buf, ts_data->touch_buf, ts_data->ta_size); + wake_up_interruptible(&ts_data->ts_waitqueue); + } + + 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; + + 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); + + return ret; +} + +#if FTS_PEN_EN +static int fts_input_pen_init(struct fts_ts_data *ts_data) +{ + int ret = 0; + struct input_dev *pen_dev; + struct fts_ts_platform_data *pdata = ts_data->pdata; + + FTS_FUNC_ENTER(); + pen_dev = input_allocate_device(); + if (!pen_dev) { + FTS_ERROR("Failed to allocate memory for input_pen device"); + return -ENOMEM; + } + + pen_dev->dev.parent = ts_data->dev; + pen_dev->name = FTS_DRIVER_PEN_NAME; + 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(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, pdata->x_min, pdata->x_max, 0, 0); + input_set_abs_params(pen_dev, ABS_Y, pdata->y_min, pdata->y_max, 0, 0); + input_set_abs_params(pen_dev, ABS_PRESSURE, 0, 4096, 0, 0); + input_set_abs_params(pen_dev, ABS_TILT_X, -9000, 9000, 0, 0); + input_set_abs_params(pen_dev, ABS_TILT_Y, -9000, 9000, 0, 0); + input_set_abs_params(pen_dev, ABS_Z, 0, 36000, 0, 0); + + ret = input_register_device(pen_dev); + if (ret) { + FTS_ERROR("Input device registration failed"); + input_free_device(pen_dev); + pen_dev = NULL; + return ret; + } + + ts_data->pen_dev = pen_dev; + ts_data->pen_etype = STYLUS_DEFAULT; + FTS_FUNC_EXIT(); + return 0; +} +#endif + +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; + } + +#if FTS_PEN_EN + ret = fts_input_pen_init(ts_data); + if (ret) { + FTS_ERROR("Input-pen device registration failed"); + input_set_drvdata(input_dev, NULL); + input_free_device(input_dev); + input_dev = NULL; + return ret; + } +#endif + + ts_data->input_dev = input_dev; + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_buffer_init(struct fts_ts_data *ts_data) +{ + ts_data->touch_buf = (u8 *)kzalloc(FTS_MAX_TOUCH_BUF, GFP_KERNEL); + if (!ts_data->touch_buf) { + FTS_ERROR("failed to alloc memory for touch buf"); + return -ENOMEM; + } + + ts_data->touch_size = FTS_TOUCH_DATA_LEN; + + + ts_data->touch_analysis_support = 0; + ts_data->ta_flag = 0; + ts_data->ta_size = 0; + + return 0; +} + +#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 */ + +/***************************************************************************** +* Power Control +*****************************************************************************/ +#if FTS_POWER_SOURCE_CUST_EN +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 = 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); + } + } + 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 = 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); + } + } + 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; + } + + if (regulator_count_voltages(ts_data->vdd) > 0) { + 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); + regulator_put(ts_data->vdd); + return ret; + } + } + + ts_data->vcc_i2c = regulator_get(ts_data->dev, "vcc_i2c"); + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + if (regulator_count_voltages(ts_data->vcc_i2c) > 0) { + 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); + regulator_put(ts_data->vcc_i2c); + } + } + } + +#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->report_gesture_key = of_property_read_bool(np, "focaltech,report_gesture_key"); + if (pdata->report_gesture_key) + FTS_INFO("Report tap gesture as key."); + + 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_string(np, "focaltech,panel-supplier", + &fts_data->panel_supplier); + if (ret < 0) { + fts_data->panel_supplier = NULL; + FTS_ERROR("Unable to read panel supplier\n"); + } else { + FTS_INFO("panel supplier is %s", (char *)fts_data->panel_supplier); + } + FTS_FUNC_EXIT(); + return 0; +} + +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); +} + +#if defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) +static struct drm_panel *active_panel; + +static int drm_check_dt(struct device_node *np) +{ + int i = 0; + int count = 0; + struct device_node *node = NULL; + struct drm_panel *panel = NULL; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) { + FTS_ERROR("find drm_panel count(%d) fail", count); + return -ENODEV; + } + + 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)) { + FTS_INFO("find drm_panel successfully"); + active_panel = panel; + return 0; + } + } + + FTS_ERROR("no find drm_panel"); + return -ENODEV; +} + +static int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct drm_panel_notifier *evdata = data; + int *blank = NULL; + struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data, + fb_notif); + + if (!evdata) { + FTS_ERROR("evdata is null"); + return 0; + } + + if (!((event == DRM_PANEL_EARLY_EVENT_BLANK ) + || (event == DRM_PANEL_EVENT_BLANK))) { + FTS_INFO("event(%lu) do not need process\n", event); + return 0; + } + + blank = evdata->data; + FTS_DBG_LEVEL("DRM event:%lu,blank:%d", event, *blank); + switch (*blank) { + case DRM_PANEL_BLANK_UNBLANK: + if (DRM_PANEL_EARLY_EVENT_BLANK == event) { + FTS_INFO("resume: event = %lu, not care\n", event); + } else if (DRM_PANEL_EVENT_BLANK == event) { + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + } + break; + case DRM_PANEL_BLANK_POWERDOWN: + if (DRM_PANEL_EARLY_EVENT_BLANK == event) { + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); + } else if (DRM_PANEL_EVENT_BLANK == event) { + FTS_INFO("suspend: event = %lu, not care\n", event); + } + break; + default: + FTS_INFO("DRM BLANK(%d) do not need process\n", *blank); + break; + } + + return 0; +} +#else +#ifndef FOCALTECH_CONFIG_PANEL_NOTIFICATIONS +static int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank = NULL; + struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data, + fb_notif); + + if (!evdata) { + FTS_ERROR("evdata is null"); + return 0; + } + + if (!((event == MSM_DRM_EARLY_EVENT_BLANK ) + || (event == MSM_DRM_EVENT_BLANK))) { + FTS_INFO("event(%lu) do not need process\n", event); + return 0; + } + + blank = evdata->data; + FTS_INFO("DRM event:%lu,blank:%d", event, *blank); + switch (*blank) { + case MSM_DRM_BLANK_UNBLANK: + if (MSM_DRM_EARLY_EVENT_BLANK == event) { + FTS_INFO("resume: event = %lu, not care\n", event); + } else if (MSM_DRM_EVENT_BLANK == event) { + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + } + break; + case MSM_DRM_BLANK_POWERDOWN: + if (MSM_DRM_EARLY_EVENT_BLANK == event) { + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); +#ifdef FOCALTECH_SENSOR_EN + if (fts_data->gesture_support) { + FTS_INFO("double tap gesture suspend\n"); + return 1; + } +#endif + } else if (MSM_DRM_EVENT_BLANK == event) { + FTS_INFO("suspend: event = %lu, not care\n", event); + } + break; + default: + FTS_INFO("DRM BLANK(%d) do not need process\n", *blank); + break; + } + + return 0; +} +#else +int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + struct fts_ts_data *ts_data = + container_of(self, struct fts_ts_data, fb_notif); + + if (!evdata || (evdata->id != 0)) { + return 0; + } + + FTS_INFO("DRM event:%lu", event); + switch (event) { + case PANEL_EVENT_DISPLAY_ON: + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + break; + case PANEL_EVENT_PRE_DISPLAY_OFF: + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); +#ifdef FOCALTECH_SENSOR_EN + if (fts_data->gesture_support) { + FTS_INFO("double tap gesture suspend\n"); + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); + fts_gesture_suspend(ts_data); + } else { +#endif + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); + fts_ts_suspend(ts_data->dev); +#ifdef FOCALTECH_SENSOR_EN + } +#endif + break; + default: + break; + } + + return 0; +} +#endif +#endif +#elif defined(CONFIG_FB) +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 (!evdata) { + FTS_ERROR("evdata is null"); + return 0; + } + + 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); + + cancel_work_sync(&fts_data->resume_work); + 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); + + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); +} +#endif + +#if FTS_USB_DETECT_EN +static int fts_charger_notifier_callback(struct notifier_block *nb, + unsigned long val, void *v) { + int ret = 0; + struct power_supply *psy = NULL; + struct fts_ts_data *ts = container_of(nb, struct fts_ts_data, charger_notif); + union power_supply_propval prop; + + psy= power_supply_get_by_name("usb"); + if (!psy) { + FTS_ERROR("Couldn't get usbpsy\n"); + return -EINVAL; + } + if (!strcmp(psy->desc->name, "usb")) { + if (psy && ts && val == POWER_SUPPLY_PROP_STATUS) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,&prop); + if (ret < 0) { + FTS_ERROR("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret); + return ret; + } else { + ts->usb_detect_flag = prop.intval; + //FTS_ERROR("usb prop.intval =%d\n", prop.intval); + if(ts->usb_detect_flag != ts->usb_connected){ + if (ts->usb_detect_flag) { + ts->usb_connected = 0x01; + } else { + ts->usb_connected = 0x00; + } + if(!ts->suspended){ + fts_mcu_usb_detect_set(ts->usb_connected); + FTS_INFO("%s: Cable status change: 0x%2.2X\n", __func__, ts->usb_connected); + } + } + } + } + } + return 0; +} +#endif + +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"); + +#if defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) + ret = drm_check_dt(ts_data->dev->of_node); + if (ret) { + FTS_ERROR("parse drm-panel fail"); + return -ENODEV; + } +#endif +#endif + } 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); + init_waitqueue_head(&ts_data->ts_waitqueue); +#ifdef FOCALTECH_SENSOR_EN + mutex_init(&ts_data->state_mutex); + //unknown screen state + ts_data->screen_state = SCREEN_UNKNOWN; +#endif + + /* 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_buffer_init(ts_data); + if (ret) { + FTS_ERROR("buffer init fail"); + goto err_buffer_init; + } + + ret = fts_gpio_configure(ts_data); + if (ret) { + FTS_ERROR("configure the gpios fail"); + goto err_gpio_config; + } + +#if FTS_POWER_SOURCE_CUST_EN + ret = fts_power_source_init(ts_data); + if (ret) { + FTS_ERROR("fail to get power(regulator)"); + goto err_power_init; + } +#endif + +#if FTS_PINCTRL_EN && (!FTS_POWER_SOURCE_CUST_EN) + fts_pinctrl_init(ts_data); + fts_pinctrl_select_normal(ts_data); +#endif + +#if (!FTS_CHIP_IDC) + fts_reset_proc(200); +#endif + + ret = fts_get_ic_information(ts_data); + if (ret) { + FTS_ERROR("not focal IC, unregister driver"); + goto err_irq_req; + } + + 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"); + } + +#if FTS_GESTURE_EN + ret = fts_gesture_init(ts_data); + if (ret) { + FTS_ERROR("init gesture fail"); + } +#endif + +#if FTS_ESDCHECK_EN + ret = fts_esdcheck_init(ts_data); + if (ret) { + FTS_ERROR("init esd check fail"); + } +#endif + + ret = fts_irq_registration(ts_data); + if (ret) { + FTS_ERROR("request irq failed"); + goto err_irq_req; + } + + ret = fts_fwupg_init(ts_data); + if (ret) { + FTS_ERROR("init fw upgrade fail"); + } + + if (ts_data->ts_workqueue) { + INIT_WORK(&ts_data->resume_work, fts_resume_work); + } + +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM + init_completion(&ts_data->pm_completion); + ts_data->pm_suspend = false; +#endif + +#if defined(CONFIG_DRM) + ts_data->fb_notif.notifier_call = drm_notifier_callback; +#if defined(CONFIG_DRM_PANEL) + if (active_panel) { + ret = drm_panel_notifier_register(active_panel, &ts_data->fb_notif); + if (ret) + FTS_ERROR("[DRM]drm_panel_notifier_register fail: %d\n", ret); + } +#else +#ifndef FOCALTECH_CONFIG_PANEL_NOTIFICATIONS + ret = msm_drm_register_client(&ts_data->fb_notif); + if (ret) { + FTS_ERROR("[DRM]Unable to register fb_notifier: %d\n", ret); + } +#else + ret = register_panel_notifier(&ts_data->fb_notif); + if(ret) { + FTS_ERROR("register panel_notifier failed. ret=%d\n", ret); + } +#endif +#endif +#elif defined(CONFIG_FB) + 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 + +#if FTS_USB_DETECT_EN + ts_data->usb_connected = 0x00; + ts_data->charger_notif.notifier_call = fts_charger_notifier_callback; + ret = power_supply_reg_notifier(&ts_data->charger_notif); + if (ret) { + FTS_ERROR("Unable to register charger_notifier: %d\n",ret); + goto err_register_charger_notify_failed; + } +#endif + + FTS_FUNC_EXIT(); + return 0; + +#if FTS_USB_DETECT_EN +err_register_charger_notify_failed: +if (ts_data->charger_notif.notifier_call) + power_supply_unreg_notifier(&ts_data->charger_notif); +#endif + +err_irq_req: +#if FTS_POWER_SOURCE_CUST_EN +err_power_init: + fts_power_source_exit(ts_data); +#endif + +#if FTS_PINCTRL_EN && (!FTS_POWER_SOURCE_CUST_EN) + fts_pinctrl_select_release(ts_data); +#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); +err_gpio_config: + kfree_safe(ts_data->touch_buf); +err_buffer_init: + input_unregister_device(ts_data->input_dev); +#if FTS_PEN_EN + input_unregister_device(ts_data->pen_dev); +#endif +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(); + + cancel_work_sync(&fts_data->resume_work); + +#if FTS_USB_DETECT_EN + if (ts_data->charger_notif.notifier_call) + power_supply_unreg_notifier(&ts_data->charger_notif); +#endif + +#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 + +#if FTS_GESTURE_EN + fts_gesture_exit(ts_data); +#endif + + free_irq(ts_data->irq, ts_data); + + fts_bus_exit(ts_data); + + input_unregister_device(ts_data->input_dev); +#if FTS_PEN_EN + input_unregister_device(ts_data->pen_dev); +#endif + + if (ts_data->ts_workqueue) + destroy_workqueue(ts_data->ts_workqueue); + +#if defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) + if (active_panel) + drm_panel_notifier_unregister(active_panel, &ts_data->fb_notif); +#else + if (msm_drm_unregister_client(&ts_data->fb_notif)) + FTS_ERROR("[DRM]Error occurred while unregistering fb_notifier.\n"); +#endif +#elif defined(CONFIG_FB) + if (fb_unregister_client(&ts_data->fb_notif)) + FTS_ERROR("[FB]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 + +#if FTS_PINCTRL_EN && (!FTS_POWER_SOURCE_CUST_EN) + fts_pinctrl_select_release(ts_data); +#endif + + kfree_safe(ts_data->touch_buf); + 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; + +#ifdef FOCALTECH_SENSOR_EN + mutex_lock(&ts_data->state_mutex); +#endif + + FTS_FUNC_ENTER(); + if (ts_data->suspended) { +#ifdef FOCALTECH_SENSOR_EN + mutex_unlock(&ts_data->state_mutex); +#endif + FTS_INFO("Already in suspend state"); + return 0; + } + + if (ts_data->fw_loading) { +#ifdef FOCALTECH_SENSOR_EN + mutex_unlock(&ts_data->state_mutex); +#endif + FTS_INFO("fw upgrade in process, can't suspend"); + return 0; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_suspend(ts_data); +#endif + +#if FTS_GESTURE_EN +#ifdef FOCALTECH_SENSOR_EN + if (ts_data->gesture_support && (fts_gesture_suspend(ts_data) == 0)) { + ts_data->screen_state = SCREEN_OFF; + ts_data->wakeable = true; + mutex_unlock(&ts_data->state_mutex); + FTS_INFO("tap gesture suspend\n"); + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); +#else + if (fts_gesture_suspend(ts_data) == 0) { +#endif + /* Enter into gesture mode(suspend) */ + ts_data->suspended = true; + return 0; + } +#endif + + 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); + +#ifdef FOCALTECH_SENSOR_EN + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); +#endif + + 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 + } + +#if FTS_PINCTRL_EN && (!FTS_POWER_SOURCE_CUST_EN) + fts_pinctrl_select_suspend(ts_data); +#endif + fts_release_all_finger(); + ts_data->suspended = true; + FTS_FUNC_EXIT(); +#ifdef FOCALTECH_SENSOR_EN + ts_data->screen_state = SCREEN_OFF; + mutex_unlock(&ts_data->state_mutex); +#endif + return 0; +} + +static int fts_ts_resume(struct device *dev) +{ + struct fts_ts_data *ts_data = fts_data; + +#ifdef FOCALTECH_SENSOR_EN + mutex_lock(&ts_data->state_mutex); +#endif + + FTS_FUNC_ENTER(); + if (!ts_data->suspended) { +#ifdef FOCALTECH_SENSOR_EN + mutex_unlock(&ts_data->state_mutex); +#endif + FTS_DEBUG("Already in awake state"); + return 0; + } + + ts_data->suspended = false; + fts_release_all_finger(); + + if (!ts_data->ic_info.is_incell) { +#if FTS_POWER_SOURCE_CUST_EN + fts_power_source_resume(ts_data); +#endif + fts_reset_proc(200); + } + + +#if FTS_PINCTRL_EN && (!FTS_POWER_SOURCE_CUST_EN) + fts_pinctrl_select_normal(ts_data); +#endif + + fts_wait_tp_to_valid(); + fts_ex_mode_recovery(ts_data); + +#if FTS_ESDCHECK_EN + fts_esdcheck_resume(ts_data); +#endif + +#if FTS_GESTURE_EN +#ifdef FOCALTECH_SENSOR_EN + if (ts_data->wakeable && (fts_gesture_resume(ts_data) == 0)) { + ts_data->screen_state = SCREEN_ON; + ts_data->wakeable = false; + mutex_unlock(&ts_data->state_mutex); + FTS_INFO("Exit from tap gesture suspend mode."); +#else + if (fts_gesture_resume(ts_data) == 0) { +#endif + ts_data->suspended = false; + + return 0; + } +#endif + + ts_data->suspended = false; + +#if FTS_USB_DETECT_EN + fts_cable_detect_func(true); +#endif + + FTS_FUNC_EXIT(); +#ifdef FOCALTECH_SENSOR_EN + mutex_unlock(&ts_data->state_mutex); + ts_data->screen_state = SCREEN_ON; +#endif + + return 0; +} + +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM +static int fts_pm_suspend(struct device *dev) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + FTS_INFO("system enters into pm_suspend"); + ts_data->pm_suspend = true; + reinit_completion(&ts_data->pm_completion); + return 0; +} + +static int fts_pm_resume(struct device *dev) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + FTS_INFO("system resumes from pm_suspend"); + ts_data->pm_suspend = false; + complete(&ts_data->pm_completion); + return 0; +} + +static const struct dev_pm_ops fts_dev_pm_ops = { + .suspend = fts_pm_suspend, + .resume = fts_pm_resume, +}; +#endif + +/***************************************************************************** +* TP Driver +*****************************************************************************/ +static int fts_ts_probe(struct spi_device *spi) +{ + int ret = 0; + struct fts_ts_data *ts_data = NULL; + + FTS_INFO("Touch Screen(SPI BUS) driver prboe..."); + 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 = (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->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; +} + +static int fts_ts_remove(struct spi_device *spi) +{ + return fts_ts_remove_entry(spi_get_drvdata(spi)); +} + +static const struct spi_device_id fts_ts_id[] = { + {FTS_DRIVER_NAME, 0}, + {}, +}; +static const struct of_device_id fts_dt_match[] = { + {.compatible = "focaltech,fts", }, + {}, +}; +MODULE_DEVICE_TABLE(of, fts_dt_match); + +static struct spi_driver fts_ts_driver = { + .probe = fts_ts_probe, + .remove = fts_ts_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_id, +}; + +static int __init fts_ts_init(void) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + ret = spi_register_driver(&fts_ts_driver); + if ( ret != 0 ) { + FTS_ERROR("Focaltech touch screen driver init failed!"); + } + FTS_FUNC_EXIT(); + return ret; +} + +static void __exit fts_ts_exit(void) +{ + spi_unregister_driver(&fts_ts_driver); +} + +module_init(fts_ts_init); +module_exit(fts_ts_exit); + +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +MODULE_AUTHOR("FocalTech Driver Team"); +MODULE_DESCRIPTION("FocalTech Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_core.h b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_core.h new file mode 100644 index 000000000000..487411730570 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_core.h @@ -0,0 +1,411 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "focaltech_common.h" +#ifdef FTS_USB_DETECT_EN +#include +#endif + +#if defined(FOCALTECH_SENSOR_EN) +#include +#endif +#ifdef FOCALTECH_CONFIG_PANEL_NOTIFICATIONS +#include +#endif +#ifdef FTS_LAST_TIME_EN +#include +#endif + +/***************************************************************************** +* 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_COORDS_ARR_SIZE 4 +#define FTS_ONE_TCH_LEN 6 +#define FTS_TOUCH_DATA_LEN (FTS_MAX_POINTS_SUPPORT * FTS_ONE_TCH_LEN + 2) + +#define FTS_GESTURE_POINTS_MAX 6 +#define FTS_GESTURE_DATA_LEN (FTS_GESTURE_POINTS_MAX * 4 + 4) + +#define FTS_SIZE_PEN 15 +#define FTS_SIZE_DEFAULT 15 + +#define FTS_MAX_ID 0x0A +#define FTS_TOUCH_OFF_E_XH 0 +#define FTS_TOUCH_OFF_XL 1 +#define FTS_TOUCH_OFF_ID_YH 2 +#define FTS_TOUCH_OFF_YL 3 +#define FTS_TOUCH_OFF_PRE 4 +#define FTS_TOUCH_OFF_AREA 5 +#define FTS_TOUCH_E_NUM 1 +#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 FTS_MAX_COMPATIBLE_TYPE 4 +#define FTS_MAX_COMMMAND_LENGTH 16 + +#define FTS_MAX_TOUCH_BUF 4096 + + +/***************************************************************************** +* 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 1 +#define FTS_TIMEOUT_COMERR_PM 700 + +#define FTS_REG_RETRY_TIMES 5 + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +#ifdef FOCALTECH_SENSOR_EN +/* display state */ +enum display_state { + SCREEN_UNKNOWN, + SCREEN_OFF, + SCREEN_ON, +}; +#endif + +struct ftxxxx_proc { + struct proc_dir_entry *proc_entry; + u8 opmode; + u8 cmd_len; + u8 cmd[FTS_MAX_COMMMAND_LENGTH]; +}; + +struct fts_ts_platform_data { + u32 irq_gpio; + u32 irq_gpio_flags; + u32 reset_gpio; + u32 reset_gpio_flags; + bool report_gesture_key; + 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; +}; + +#if defined(FOCALTECH_SENSOR_EN) +struct focaltech_sensor_platform_data { + struct input_dev *input_sensor_dev; + struct sensors_classdev ps_cdev; + int sensor_opened; + char sensor_data; /* 0 near, 1 far */ + struct fts_ts_data *data; +}; +#endif + +struct pen_event { + int down; + int inrange; + int tip; + 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 tilt_x; + int tilt_y; + int azimuth; + int tool_type; +}; + +struct fts_ts_data { + struct i2c_client *client; + struct spi_device *spi; + struct device *dev; + struct input_dev *input_dev; + struct input_dev *pen_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; + wait_queue_head_t ts_waitqueue; + struct ftxxxx_proc proc; + struct ftxxxx_proc proc_ta; + spinlock_t irq_lock; + struct mutex report_mutex; + struct mutex bus_lock; + unsigned long intr_jiffies; + 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 force_reflash; + bool irq_disabled; + bool power_disabled; + bool glove_mode; + bool cover_mode; + bool charger_mode; + bool touch_analysis_support; + bool prc_support; + bool prc_mode; + bool esd_support; + + bool gesture_support; /* gesture enable or disable, default: disable */ + u8 gesture_bmode; /*gesture buffer mode*/ +#ifdef FOCALTECH_SENSOR_EN + bool wakeable; + enum display_state screen_state; + struct mutex state_mutex; + struct focaltech_sensor_platform_data *sensor_pdata; +#endif + + u8 pen_etype; + struct pen_event pevent; + struct ts_event events[FTS_MAX_POINTS_SUPPORT]; /* multi-touch */ + u8 touch_addr; + u32 touch_size; + u8 *touch_buf; + int touch_event_num; + int touch_points; + int key_state; + int ta_flag; + u32 ta_size; + u8 *ta_buf; + + u8 *bus_tx_buf; + u8 *bus_rx_buf; + int bus_type; + const char *panel_supplier; + 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 + +#if FTS_USB_DETECT_EN + bool usb_detect_flag; + uint8_t usb_connected; + struct notifier_block charger_notif; +#endif + +#ifdef FTS_LAST_TIME_EN + ktime_t last_event_time; +#endif +}; + +#ifdef FTS_SET_TOUCH_STATE +enum touch_panel_id { + TOUCH_PANEL_IDX_PRIMARY = 0, + TOUCH_PANEL_MAX_IDX, +}; + +enum touch_state { + TOUCH_DEEP_SLEEP_STATE = 0, + TOUCH_LOW_POWER_STATE, +}; +#endif + +enum _FTS_BUS_TYPE { + BUS_TYPE_NONE, + BUS_TYPE_I2C, + BUS_TYPE_SPI, + BUS_TYPE_SPI_V2, +}; + +enum _FTS_TOUCH_ETYPE { + TOUCH_DEFAULT = 0x00, + TOUCH_EVENT_NUM = 0x02, + TOUCH_EXTRA_MSG = 0x08, + TOUCH_PEN = 0x0B, + TOUCH_GESTURE = 0x80, + TOUCH_FW_INIT = 0x81, + TOUCH_IGNORE = 0xFE, + TOUCH_ERROR = 0xFF, +}; + +enum _FTS_STYLUS_ETYPE { + STYLUS_DEFAULT, + STYLUS_HOVER, +}; + +enum _FTS_GESTURE_BMODE { + GESTURE_BM_REG, + GESTURE_BM_TOUCH, +}; + +/***************************************************************************** +* 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); +int fts_spi_transfer_direct(u8 *writebuf, u32 writelen, u8 *readbuf, u32 readlen); + +/* Gesture functions */ +#if FTS_GESTURE_EN +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); +#endif + +/* 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); +void fts_esdcheck_switch(struct fts_ts_data *ts_data, bool enable); +void fts_esdcheck_proc_busy(struct fts_ts_data *ts_data, bool proc_debug); +void fts_esdcheck_suspend(struct fts_ts_data *ts_data); +void fts_esdcheck_resume(struct fts_ts_data *ts_data); +#endif + +#ifdef FTS_SET_TOUCH_STATE +int touch_set_state(int state, int panel_idx); +int check_touch_state(int *state, int panel_idx); +#endif + +/* Host test */ + +/* Point Report Check*/ +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); + +/* FW upgrade */ +int fts_fwupg_init(struct fts_ts_data *ts_data); +int fts_fwupg_exit(struct fts_ts_data *ts_data); +int fts_fw_resume(bool need_reset); +int fts_fw_recovery(void); +int fts_upgrade_bin(char *fw_name, bool force); +int fts_enter_test_environment(bool test_state); +int fts_fw_update_vendor_name(const char* name); + +/* Other */ +int fts_reset_proc(int hdelayms); +int fts_check_cid(struct fts_ts_data *ts_data, u8 id_h); +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); +#endif /* __LINUX_FOCALTECH_CORE_H__ */ diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_esdcheck.c b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_esdcheck.c new file mode 100644 index 000000000000..cb76d3530dfb --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_esdcheck.c @@ -0,0 +1,452 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 +#define ESD_INTR_INTERVALS 200 /* unit:ms */ + +/***************************************************************************** +* 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 idh = 0; + u8 chip_id = ts_data->ic_info.ids.chip_idh; + + for (i = 0; i < 3; i++) { + ret = fts_read_reg(FTS_REG_CHIP_ID, &idh); + if (ret < 0) { + FTS_ERROR("read chip id fail,ret:%d", ret); + fts_esdcheck_data.nack_cnt++; + } else { + if ((idh == chip_id) || (fts_check_cid(ts_data, idh) == 0)) { + break; + } else { + FTS_DEBUG("read chip_id:%x,retry:%d", idh, 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(®_addr, 1, ®_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; + unsigned long intr_timeout = msecs_to_jiffies(ESD_INTR_INTERVALS); + + /* 1. esdcheck is interrupt, then return */ + intr_timeout += ts_data->intr_jiffies; + if (time_before(jiffies, intr_timeout)) { + 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, ®_value); + if ( ret < 0 ) { + fts_esdcheck_data.nack_cnt++; + } else if ( (reg_value & 0x70) == FTS_REG_WORKMODE_FACTORY_VALUE) { + FTS_DEBUG("in factory mode(%x), no check esd", reg_value); + return 0; + } + + /* 5. Get Chip ID */ + hardware_reset = get_chip_id(ts_data); + + /* 6. 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); + } + + /* 7. 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 (ts_data->esd_support && fts_esdcheck_data.mode) { +#if LCD_ESD_PATCH + idc_esdcheck_lcderror(ts_data); +#endif + esdcheck_algorithm(ts_data); + queue_delayed_work(ts_data->ts_workqueue, &ts_data->esdcheck_work, + msecs_to_jiffies(ESDCHECK_WAIT_TIME)); + } +} + +/***************************************************************************** +* 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: +*****************************************************************************/ +void fts_esdcheck_proc_busy(struct fts_ts_data *ts_data, bool proc_debug) +{ + if (ts_data->esd_support) { + fts_esdcheck_data.proc_debug = proc_debug; + } +} + +/***************************************************************************** +* Name: fts_esdcheck_switch +* Brief: FTS esd check function switch. +* Input: enable: 1 - Enable esd check +* 0 - Disable esd check +* Output: +* Return: +*****************************************************************************/ +void fts_esdcheck_switch(struct fts_ts_data *ts_data, bool enable) +{ + if (ts_data->esd_support) { + 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(); + } +} + +void fts_esdcheck_suspend(struct fts_ts_data *ts_data) +{ + if (ts_data->esd_support) { + FTS_FUNC_ENTER(); + fts_esdcheck_switch(ts_data, DISABLE); + fts_esdcheck_data.suspend = 1; + fts_esdcheck_data.intr = 0; + fts_esdcheck_data.intr_cnt = 0; + FTS_FUNC_EXIT(); + } +} + +void fts_esdcheck_resume(struct fts_ts_data *ts_data) +{ + if (ts_data->esd_support) { + FTS_FUNC_ENTER(); + fts_esdcheck_switch(ts_data, ENABLE); + fts_esdcheck_data.suspend = 0; + fts_esdcheck_data.intr = 0; + fts_esdcheck_data.intr_cnt = 0; + FTS_FUNC_EXIT(); + } +} + +static ssize_t fts_esdcheck_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_DEBUG("enable esdcheck"); + ts_data->esd_support = ENABLE; + fts_esdcheck_switch(ts_data, ENABLE); + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_DEBUG("disable esdcheck"); + fts_esdcheck_switch(ts_data, DISABLE); + ts_data->esd_support = 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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count = snprintf(buf, PAGE_SIZE, "Esd check: %s\n", \ + ts_data->esd_support ? "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, +}; + +static 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 function"); + 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_create_esd_sysfs(ts_data->dev); + ts_data->esd_support = FTS_ESDCHECK_EN; + fts_esdcheck_switch(ts_data, ENABLE); + FTS_FUNC_EXIT(); + return 0; +} + +int fts_esdcheck_exit(struct fts_ts_data *ts_data) +{ + fts_esdcheck_data.mode = DISABLE; + ts_data->esd_support = DISABLE; + cancel_delayed_work_sync(&ts_data->esdcheck_work); + sysfs_remove_group(&ts_data->dev->kobj, &fts_esd_group); + return 0; +} +#endif /* FTS_ESDCHECK_EN */ + diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_ex_fun.c b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_ex_fun.c new file mode 100644 index 000000000000..51b13c1478e7 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_ex_fun.c @@ -0,0 +1,1883 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 "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_WRITE_DATA_DIRECT 16 +#define PROC_READ_DATA_DIRECT 17 +#define PROC_CONFIGURE 18 +#define PROC_CONFIGURE_INTR 20 +#define PROC_GET_DRIVER_INFO 21 +#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 = PDE_DATA(file_inode(filp)); + struct ftxxxx_proc *proc = &ts_data->proc; + + if (buflen < 1) { + FTS_ERROR("apk proc count(%d) fail", buflen); + 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]; + if (buflen == 1) { + ret = buflen; + goto proc_write_err; + } + + switch (proc->opmode) { + case PROC_SET_TEST_FLAG: + FTS_DEBUG("[APK]: PROC_SET_TEST_FLAG = %x", writebuf[1]); +#if FTS_ESDCHECK_EN + if (writebuf[1] == 0) { + fts_esdcheck_switch(ts_data, ENABLE); + } else { + fts_esdcheck_switch(ts_data, 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 >= FTS_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; + 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_HW_RESET: + if (buflen < PROC_BUF_SIZE) { + 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; + + case PROC_READ_DATA_DIRECT: + writelen = buflen - 1; + if (writelen >= FTS_MAX_COMMMAND_LENGTH) { + FTS_ERROR("cmd(PROC_READ_DATA_DIRECT) length(%d) fail", writelen); + goto proc_write_err; + } + memcpy(proc->cmd, writebuf + 1, writelen); + proc->cmd_len = writelen; + break; + + case PROC_WRITE_DATA_DIRECT: + writelen = buflen - 1; + ret = fts_spi_transfer_direct(writebuf + 1, writelen, NULL, 0); + if (ret < 0) { + FTS_ERROR("PROC_WRITE_DATA_DIRECT write error"); + goto proc_write_err; + } + break; + + case PROC_CONFIGURE: + ts_data->spi->mode = writebuf[1]; + ts_data->spi->bits_per_word = writebuf[2]; + ts_data->spi->max_speed_hz = *(u32 *)(writebuf + 4); + FTS_INFO("spi,mode=%d,bits=%d,speed=%d", ts_data->spi->mode, + ts_data->spi->bits_per_word, ts_data->spi->max_speed_hz); + ret = spi_setup(ts_data->spi); + if (ret) { + FTS_ERROR("spi setup fail"); + goto proc_write_err; + } + break; + + case PROC_CONFIGURE_INTR: + if (writebuf[1] == 0) + fts_irq_disable(); + else + fts_irq_enable(); + 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 = PDE_DATA(file_inode(filp)); + struct ftxxxx_proc *proc = &ts_data->proc; + + if (buflen <= 0) { + FTS_ERROR("apk proc read count(%d) fail", buflen); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == readbuf) { + FTS_ERROR("apk proc buf zalloc fail"); + return -ENOMEM; + } + } else { + readbuf = tmpbuf; + } + + 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_READ_DATA: + num_read_chars = buflen; + ret = fts_read(proc->cmd, proc->cmd_len, readbuf, num_read_chars); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA read error"); + goto proc_read_err; + } + break; + + case PROC_READ_DATA_DIRECT: + num_read_chars = buflen; + ret = fts_spi_transfer_direct(proc->cmd, proc->cmd_len, readbuf, num_read_chars); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA_DIRECT read error"); + goto proc_read_err; + } + break; + + case PROC_GET_DRIVER_INFO: + if (buflen >= 64) { + num_read_chars = buflen; + readbuf[0] = ts_data->bus_type; + snprintf(&readbuf[32], buflen - 32, "%s", FTS_DRIVER_VERSION); + } + break; + + default: + break; + } + + ret = num_read_chars; +proc_read_err: + if ((num_read_chars > 0) && copy_to_user(buff, readbuf, num_read_chars)) { + FTS_ERROR("copy to user error"); + ret = -EFAULT; + } + + if ((buflen > PROC_BUF_SIZE) && readbuf) { + kfree(readbuf); + readbuf = NULL; + } + return ret; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) +static const struct proc_ops fts_proc_fops = { + .proc_read = fts_debug_read, + .proc_write = fts_debug_write, +}; +#else +static const struct file_operations fts_proc_fops = { + .owner = THIS_MODULE, + .read = fts_debug_read, + .write = fts_debug_write, +}; +#endif +#else +static int fts_debug_write( + struct file *filp, const char __user *buff, unsigned long count, 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) { + FTS_ERROR("apk proc wirte count(%d) fail", buflen); + 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 FTS_ESDCHECK_EN + if (writebuf[1] == 0) { + fts_esdcheck_switch(ts_data, ENABLE); + } else { + fts_esdcheck_switch(ts_data, 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 >= FTS_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; + 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_HW_RESET: + if (buflen < PROC_BUF_SIZE) { + 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; + + case PROC_READ_DATA_DIRECT: + writelen = buflen - 1; + if (writelen >= FTS_MAX_COMMMAND_LENGTH) { + FTS_ERROR("cmd(PROC_READ_DATA_DIRECT) length(%d) fail", writelen); + goto proc_write_err; + } + memcpy(proc->cmd, writebuf + 1, writelen); + proc->cmd_len = writelen; + break; + + case PROC_WRITE_DATA_DIRECT: + writelen = buflen - 1; + ret = fts_spi_transfer_direct(writebuf + 1, writelen, NULL, 0); + if (ret < 0) { + FTS_ERROR("PROC_WRITE_DATA_DIRECT write error"); + goto proc_write_err; + } + break; + + case PROC_CONFIGURE: + ts_data->spi->mode = writebuf[1]; + ts_data->spi->bits_per_word = writebuf[2]; + ts_data->spi->max_speed_hz = *(u32 *)(writebuf + 4); + FTS_INFO("spi,mode=%d,bits=%d,speed=%d", ts_data->spi->mode, + ts_data->spi->bits_per_word, ts_data->spi->max_speed_hz); + ret = spi_setup(ts_data->spi); + if (ret) { + FTS_ERROR("spi setup fail"); + goto proc_write_err; + } + break; + + case PROC_CONFIGURE_INTR: + if (writebuf[1] == 0) + fts_irq_disable(); + else + fts_irq_enable(); + 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) { + FTS_ERROR("apk proc read count(%d) fail", buflen); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == readbuf) { + FTS_ERROR("apk proc buf zalloc fail"); + return -ENOMEM; + } + } else { + readbuf = tmpbuf; + } + + 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_READ_DATA: + num_read_chars = buflen; + ret = fts_read(proc->cmd, proc->cmd_len, readbuf, num_read_chars); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA read error"); + goto proc_read_err; + } + break; + + case PROC_READ_DATA_DIRECT: + num_read_chars = buflen; + ret = fts_spi_transfer_direct(proc->cmd, proc->cmd_len, readbuf, num_read_chars); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA_DIRECT read error"); + goto proc_read_err; + } + break; + + case PROC_GET_DRIVER_INFO: + if (buflen >= 64) { + num_read_chars = buflen; + readbuf[0] = ts_data->bus_type; + snprintf(&readbuf[16], buflen - 16, "%s", FTS_DRIVER_VERSION); + } + break; + + default: + break; + } + + ret = num_read_chars; +proc_read_err: + if (!memcpy(page, readbuf, num_read_chars)) { + FTS_ERROR("copy to user error"); + ret = -EFAULT; + } + + if ((buflen > PROC_BUF_SIZE) && readbuf) { + kfree(readbuf); + readbuf = NULL; + } + return ret; +} +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) +static int fts_ta_open(struct inode *inode, struct file *file) +{ + struct fts_ts_data *ts_data = PDE_DATA(inode); + + if (ts_data->touch_analysis_support) { + FTS_INFO("fts_ta open"); + ts_data->ta_buf = kzalloc(FTS_MAX_TOUCH_BUF, GFP_KERNEL); + if (!ts_data->ta_buf) { + FTS_ERROR("kzalloc for ta_buf fails"); + return -ENOMEM; + } + } + return 0; +} + +static int fts_ta_release(struct inode *inode, struct file *file) +{ + struct fts_ts_data *ts_data = PDE_DATA(inode); + + if (ts_data->touch_analysis_support) { + FTS_INFO("fts_ta close"); + ts_data->ta_flag = 0; + if (ts_data->ta_buf) { + kfree(ts_data->ta_buf); + ts_data->ta_buf = NULL; + } + } + return 0; +} + +static ssize_t fts_ta_read( + struct file *filp, char __user *buff, size_t count, loff_t *ppos) +{ + int read_num = (int)count; + struct fts_ts_data *ts_data = PDE_DATA(file_inode(filp)); + + if (!ts_data->touch_analysis_support || !ts_data->ta_buf) { + FTS_ERROR("touch_analysis is disabled, or ta_buf is NULL"); + return -EINVAL; + } + + if (!(filp->f_flags & O_NONBLOCK)) { + ts_data->ta_flag = 1; + wait_event_interruptible(ts_data->ts_waitqueue, !ts_data->ta_flag); + } + + read_num = (ts_data->ta_size < read_num) ? ts_data->ta_size : read_num; + if ((read_num > 0) && (copy_to_user(buff, ts_data->ta_buf, read_num))) { + FTS_ERROR("copy to user error"); + return -EFAULT; + } + + return read_num; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) +static const struct proc_ops fts_procta_fops = { + .proc_open = fts_ta_open, + .proc_release = fts_ta_release, + .proc_read = fts_ta_read, +}; +#else +static const struct file_operations fts_procta_fops = { + .open = fts_ta_open, + .release = fts_ta_release, + .read = fts_ta_read, +}; +#endif +#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_data(PROC_NAME, 0777, NULL, &fts_proc_fops, ts_data); + 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 + + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) + ts_data->proc_ta.proc_entry = proc_create_data("fts_ta", 0777, NULL, \ + &fts_procta_fops, ts_data); + if (!ts_data->proc_ta.proc_entry) { + FTS_ERROR("create proc_ta entry fail"); + return -ENOMEM; + } +#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 + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) + if (ts_data->proc_ta.proc_entry) + proc_remove(ts_data->proc_ta.proc_entry); +#endif +} + +/************************************************************************ + * sysfs interface + ***********************************************************************/ +/* fts_hw_reset interface */ +static ssize_t fts_hw_reset_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_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; +} + +static ssize_t reset_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct input_dev *input_dev = fts_data->input_dev; + + if ('1' != buf[0]) { + pr_err("Invalid argument for reset\n"); + return -EINVAL; + } + + mutex_lock(&input_dev->mutex); + fts_reset_proc(0); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* fts_irq interface */ +static ssize_t fts_irq_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct irq_desc *desc = irq_to_desc(ts_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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_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; +} + +static ssize_t drv_irq_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s\n", + fts_data->irq_disabled? "DISABLED" : "ENABLED"); +} + +static ssize_t drv_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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_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"); + ts_data->fw_is_running = false; + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_INFO("[EX-FUN]set to fw mode"); + ts_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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + if (true == ts_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; +} + +static ssize_t fts_poweron_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *data = fts_data; + struct input_dev *input_dev = data->input_dev; + bool val; + + mutex_lock(&input_dev->mutex); + val = data->power_disabled; + mutex_unlock(&input_dev->mutex); + + return scnprintf(buf, PAGE_SIZE, "%d\n", val == false); +} + +static ssize_t fts_productinfo_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s\n", FTS_CHIP_NAME); +} + +static ssize_t fts_ic_ver_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; +#if FTS_ESDCHECK_EN + struct fts_ts_data *data = fts_data; +#endif + mutex_lock(&input_dev->mutex); +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(data, 1); +#endif + fts_read_reg(FTS_REG_VENDOR_ID, &val); + count += snprintf(buf + count, PAGE_SIZE, "Product ID: 0x%02x\n", val); + fts_read_reg(FTS_REG_FW_VER, &val); + count += snprintf(buf + count, PAGE_SIZE, "Build ID: 0000-%02x\n", val); + count += scnprintf(buf + count, PAGE_SIZE, "IC: %s\n", FTS_CHIP_NAME); +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(data, 0); +#endif + mutex_unlock(&input_dev->mutex); + return count; +} + +static ssize_t fts_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "focaltech"); +} + +/* fts_tpfwver interface */ +static ssize_t fts_tpfwver_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int ret = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + ssize_t num_read_chars = 0; + u8 fwver = 0; + + mutex_lock(&input_dev->mutex); + + ret = fts_read_reg(FTS_REG_FW_VER, &fwver); + if ((ret < 0) || (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_tpfwver_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +static ssize_t buildid_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(ts_data, 1); +#endif + fts_read_reg(FTS_REG_FW_VER, &fwver); +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(ts_data, 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, "0000-%02x\n", fwver); + + mutex_unlock(&input_dev->mutex); + return num_read_chars; +} + +/* fts_rw_reg */ +static ssize_t fts_tprwreg_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + int i; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_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 (rw_op.len < 0) { + FTS_ERROR("cmd buffer error!"); + + } else { + 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(®, 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; + } + } + } + + mutex_unlock(&input_dev->mutex); + return count; +} + +/* fts_upgrade_bin interface */ +static ssize_t fts_fwupgradebin_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +static ssize_t fts_fwupgradebin_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char fwname[FILE_NAME_LENGTH] = { 0 }; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) { + FTS_ERROR("fw bin name's length(%d) fail", (int)count); + return -EINVAL; + } + memset(fwname, 0, sizeof(fwname)); + snprintf(fwname, FILE_NAME_LENGTH, "%s", buf); + fwname[count - 1] = '\0'; + + FTS_INFO("upgrade with bin file through sysfs node"); + mutex_lock(&input_dev->mutex); + fts_upgrade_bin(fwname, 0); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* fts_force_upgrade interface */ +static ssize_t fts_fwforceupg_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +static ssize_t fts_fwforceupg_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char fwname[FILE_NAME_LENGTH]; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) { + FTS_ERROR("fw bin name's length(%d) fail", (int)count); + return -EINVAL; + } + memset(fwname, 0, sizeof(fwname)); + snprintf(fwname, FILE_NAME_LENGTH, "%s", buf); + fwname[count - 1] = '\0'; + + FTS_INFO("force upgrade through sysfs node"); + mutex_lock(&input_dev->mutex); + fts_upgrade_bin(fwname, 1); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t forcereflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 0) + return -EINVAL; + + fts_data->force_reflash = (input == 0) ? false : true; + + return count; +} + +static ssize_t flashprog_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", (fts_data->fw_loading) ? 1 : 0); +} + +static ssize_t doreflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char fwname[FILE_NAME_LENGTH]; + char prefix[FILE_NAME_LENGTH] = "focaltech"; + char template[FILE_NAME_LENGTH]; + struct input_dev *input_dev = fts_data->input_dev; + + if (count > FILE_NAME_LENGTH) { + FTS_ERROR("%s: FW filename is too long\n", __func__); + return -EINVAL; + } + + if (!fts_data->force_reflash) { + if (strncmp(buf, prefix, strnlen(prefix, sizeof(prefix)))) { + FTS_ERROR("%s: FW does not belong to Focaltech\n", __func__); + return -EINVAL; + } + + snprintf(template, sizeof(template), "-%s-", FTS_CHIP_NAME); + if (!strnstr(buf + strnlen(prefix, sizeof(prefix)), template, count)) { + FTS_ERROR("%s: FW does not belong to %s\n", __func__, FTS_CHIP_NAME); + return -EINVAL; + } + } + + memset(fwname, 0, sizeof(fwname)); + scnprintf(fwname, sizeof(fwname), "%s", buf); + fwname[count - 1] = '\0'; + + mutex_lock(&input_dev->mutex); + fts_fw_update_vendor_name(fwname); + fts_fw_recovery(); + mutex_unlock(&input_dev->mutex); + fts_data->force_reflash = false; + + 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 = dev_get_drvdata(dev); + struct fts_ts_platform_data *pdata = ts_data->pdata; + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(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 += snprintf(buf + count, PAGE_SIZE, + "reset gpio:%d,int gpio:%d,irq:%d\n", + pdata->reset_gpio, pdata->irq_gpio, ts_data->irq); + + count += snprintf(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 += snprintf(buf + count, PAGE_SIZE, "BUS:%s,addr:0x%x\n", + "I2C", ts_data->client->addr); + } else { + count += snprintf(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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + + 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_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); + + 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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "touch point buffer:\n"); + for (i = 0; i < FTS_TOUCH_DATA_LEN; i++) { + count += snprintf(buf + count, PAGE_SIZE, "%02x ", ts_data->touch_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 node */ +static ssize_t fts_log_level_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "log level:%d\n", + ts_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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + sscanf(buf, "%d", &value); + FTS_DEBUG("log level:%d->%d", ts_data->log_level, value); + ts_data->log_level = value; + if (ts_data->log_level > 1) { + dbg_level_en = 1; + FTS_DEBUG("debug log level: 1"); + } else { + dbg_level_en = 0; + FTS_DEBUG("debug log level: 0"); + } + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +/* fts_pen node */ +static ssize_t fts_pen_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "pen event:%s\n", + ts_data->pen_etype ? "hover" : "default"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_pen_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + sscanf(buf, "%d", &value); + FTS_DEBUG("pen event:%d->%d", ts_data->pen_etype, value); + ts_data->pen_etype = value; + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +/* fts_touch_size node */ +static ssize_t fts_touchsize_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "touch size:%d\n", ts_data->touch_size); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_touchsize_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + sscanf(buf, "%d", &value); + if ((value > 2) && (value < FTS_MAX_TOUCH_BUF)) { + FTS_DEBUG("touch size:%d->%d", ts_data->touch_size, value); + ts_data->touch_size = value; + } else + FTS_DEBUG("touch size:%d invalid", value); + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +/* fts_ta_mode node */ +static ssize_t fts_tamode_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "touch analysis:%s\n", \ + ts_data->touch_analysis_support ? "Enable" : "Disable"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_tamode_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + sscanf(buf, "%d", &value); + ts_data->touch_analysis_support = !!value; + FTS_DEBUG("set touch analysis:%d", ts_data->touch_analysis_support); + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +static ssize_t fts_panel_supplier_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *data = dev_get_drvdata(dev); + + if (data->panel_supplier) + return scnprintf(buf, PAGE_SIZE, "%s\n", + data->panel_supplier); + return 0; +} +static DEVICE_ATTR(panel_supplier, 0444, fts_panel_supplier_show, NULL); + +/* get the fw version example:cat fw_version */ +static DEVICE_ATTR(fts_fw_version, S_IRUGO | S_IWUSR, fts_tpfwver_show, fts_tpfwver_store); + +/* 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); +/* upgrade from fw bin file example:echo "*.bin" > fts_upgrade_bin */ +static DEVICE_ATTR(fts_upgrade_bin, S_IRUGO | S_IWUSR, fts_fwupgradebin_show, fts_fwupgradebin_store); +static DEVICE_ATTR(fts_force_upgrade, S_IRUGO | S_IWUSR, fts_fwforceupg_show, fts_fwforceupg_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); +static DEVICE_ATTR(drv_irq, S_IRUGO | S_IWUSR, drv_irq_show, drv_irq_store); +static DEVICE_ATTR(fts_pen, S_IRUGO | S_IWUSR, fts_pen_show, fts_pen_store); +static DEVICE_ATTR(fts_touch_size, S_IRUGO | S_IWUSR, fts_touchsize_show, fts_touchsize_store); +static DEVICE_ATTR(fts_ta_mode, S_IRUGO | S_IWUSR, fts_tamode_show, fts_tamode_store); +static DEVICE_ATTR(reset, S_IWUSR | S_IWGRP, NULL, reset_store); +static DEVICE_ATTR(buildid, S_IRUGO, buildid_show, NULL); +static DEVICE_ATTR(forcereflash, S_IWUSR | S_IWGRP, NULL, forcereflash_store); +static DEVICE_ATTR(flashprog, S_IRUGO, flashprog_show, NULL); +static DEVICE_ATTR(doreflash, S_IWUSR | S_IWGRP, NULL, doreflash_store); +static DEVICE_ATTR(poweron, S_IRUGO, fts_poweron_show, NULL); +static DEVICE_ATTR(productinfo, S_IRUGO, fts_productinfo_show, NULL); +static DEVICE_ATTR(ic_ver, S_IRUGO, fts_ic_ver_show, NULL); +static DEVICE_ATTR(name, S_IRUGO, fts_name_show, NULL); + +/* 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_upgrade_bin.attr, + &dev_attr_fts_force_upgrade.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, + &dev_attr_fts_pen.attr, + &dev_attr_fts_touch_size.attr, + &dev_attr_fts_ta_mode.attr, + &dev_attr_drv_irq.attr, + &dev_attr_reset.attr, + &dev_attr_buildid.attr, + &dev_attr_forcereflash.attr, + &dev_attr_flashprog.attr, + &dev_attr_doreflash.attr, + &dev_attr_poweron.attr, + &dev_attr_productinfo.attr, + &dev_attr_ic_ver.attr, + &dev_attr_name.attr, + &dev_attr_panel_supplier.attr, + NULL +}; + +static struct attribute_group fts_attribute_group = { + .attrs = fts_attributes +}; + +#include +#include + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *data = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!data) { + pr_err("cannot get fts_ts_data pointer\n"); + return (ssize_t)0; + } + path = kobject_get_path(&data->spi->dev.kobj, GFP_KERNEL); + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "focaltech"); +} + +static ssize_t panel_supplier_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return fts_panel_supplier_show(dev, attr, buf); +} + +static ssize_t ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return fts_ic_ver_show(dev, attr, buf); +} + +static ssize_t productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return fts_productinfo_show(dev, attr, buf); +} + +static ssize_t debug_level_en_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + sscanf(buf, "%d", &value); + FTS_DEBUG("dbg log level:%d->%d", dbg_level_en, value); + dbg_level_en = value; + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +static ssize_t debug_level_en_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "debug log level:%d\n", + dbg_level_en); + mutex_unlock(&input_dev->mutex); + + return count; +} + +#ifdef FTS_LAST_TIME_EN +static ssize_t timestamp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + ktime_t last_ktime; + struct timespec64 last_ts; + + mutex_lock(&input_dev->mutex); + last_ktime = ts_data->last_event_time; + ts_data->last_event_time = 0; + mutex_unlock(&input_dev->mutex); + + last_ts = ktime_to_timespec64(last_ktime); + return scnprintf(buf, PAGE_SIZE, "%lld.%ld\n", last_ts.tv_sec, last_ts.tv_nsec); +} +#endif + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_RO(ic_ver), + __ATTR_RO(panel_supplier), + __ATTR_RO(productinfo), +#ifdef FTS_LAST_TIME_EN + __ATTR_RO(timestamp), +#endif + __ATTR(debug_level_en, S_IRUGO | S_IWUSR | S_IWGRP, debug_level_en_show, debug_level_en_store), + __ATTR_NULL +}; + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +static int fts_sysfs_class(void *_data, bool create) +{ + struct fts_ts_data *data = _data; + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + static struct class *touchscreen_class; + static struct device *ts_class_dev; + static int minor; + + if (create) { + minor = input_get_new_minor(data->spi->chip_select, + 1, false); + if (minor < 0) + minor = input_get_new_minor(TSDEV_MINOR_BASE, + TSDEV_MINOR_MAX, true); + pr_info("assigned minor %d\n", minor); + + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + +#ifdef FTS_LAST_TIME_EN + ts_class_dev = device_create(touchscreen_class, NULL, + MKDEV(INPUT_MAJOR, minor), + data, FTS_PRIMARY_NAME); +#else + ts_class_dev = device_create(touchscreen_class, NULL, + MKDEV(INPUT_MAJOR, minor), + data, FTS_CHIP_NAME); +#endif + + if (IS_ERR(ts_class_dev)) { + error = PTR_ERR(ts_class_dev); + ts_class_dev = NULL; + return error; + } + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + error = device_create_file(ts_class_dev, &attrs[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + } else { + if (!touchscreen_class || !ts_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(ts_class_dev, &attrs[i]); + + device_unregister(ts_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor)); + ts_class_dev = NULL; + class_unregister(touchscreen_class); + pr_err("error creating touchscreen class\n"); + + return -ENODEV; +} + +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!!"); + } + + ret = fts_sysfs_class(ts_data, true); + if (ret) { + FTS_ERROR("[EX]: fts_sysfs_class() failed!!"); + sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group); + return -ENOMEM; + } + + return ret; +} + +int fts_remove_sysfs(struct fts_ts_data *ts_data) +{ + sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group); + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_ex_mode.c b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_ex_mode.c new file mode 100644 index 000000000000..38157ffa0677 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_ex_mode.c @@ -0,0 +1,307 @@ +/* + * + * FocalTech ftxxxx TouchScreen driver. + * + * Copyright (c) 2012-2020, 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, +}; + +/***************************************************************************** +* 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; + u8 m_val = 0; + + if (value) + m_val = 0x01; + else + m_val = 0x00; + + switch (mode) { + case MODE_GLOVE: + ret = fts_write_reg(FTS_REG_GLOVE_MODE_EN, m_val); + if (ret < 0) { + FTS_ERROR("MODE_GLOVE switch to %d fail", m_val); + } + break; + case MODE_COVER: + ret = fts_write_reg(FTS_REG_COVER_MODE_EN, m_val); + if (ret < 0) { + FTS_ERROR("MODE_COVER switch to %d fail", m_val); + } + break; + case MODE_CHARGER: + ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, m_val); + if (ret < 0) { + FTS_ERROR("MODE_CHARGER switch to %d fail", m_val); + } + 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 = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_read_reg(FTS_REG_GLOVE_MODE_EN, &val); + count = snprintf(buf + count, PAGE_SIZE, "Glove Mode:%s\n", + ts_data->glove_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "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 = dev_get_drvdata(dev); + + 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 = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_read_reg(FTS_REG_COVER_MODE_EN, &val); + count = snprintf(buf + count, PAGE_SIZE, "Cover Mode:%s\n", + ts_data->cover_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "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 = dev_get_drvdata(dev); + + 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 = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_read_reg(FTS_REG_CHARGER_MODE_EN, &val); + count = snprintf(buf + count, PAGE_SIZE, "Charger Mode:%s\n", + ts_data->charger_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "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 = dev_get_drvdata(dev); + + 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; +} + + +/* 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 struct attribute *fts_touch_mode_attrs[] = { + &dev_attr_fts_glove_mode.attr, + &dev_attr_fts_cover_mode.attr, + &dev_attr_fts_charger_mode.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); + } + + 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; + + 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) successfully"); + } + + 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; +} diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_flash.c b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_flash.c new file mode 100644 index 000000000000..d1bb667d93e0 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_flash.c @@ -0,0 +1,1357 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2020, 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: 2017-12-06 +* +* 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 */ +#if FTS_AUTO_UPGRADE_EN +#define FTS_FW_NAME_PREX_WITH_REQUEST "focaltech_ts_fw_" +#else +#define FTS_FW_NAME_PREX_WITH_REQUEST "" +#endif + +#define FTS_READ_BOOT_ID_TIMEOUT 3 +#define FTS_FLASH_PACKET_LENGTH_SPI_LOW (4 * 1024 - 4) +#define FTS_FLASH_PACKET_LENGTH_SPI (32 * 1024 - 16) + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +u8 fw_file[] = { +}; + +u8 fw_file2[] = { +}; + +u8 fw_file3[] = { +}; + +struct upgrade_module module_list[] = { + {FTS_MODULE_ID, FTS_MODULE_NAME, fw_file, sizeof(fw_file)}, + {FTS_MODULE2_ID, FTS_MODULE2_NAME, fw_file2, sizeof(fw_file2)}, + {FTS_MODULE3_ID, FTS_MODULE3_NAME, fw_file3, sizeof(fw_file3)}, +}; + +struct upgrade_setting_nf upgrade_setting_list[] = { + {0x87, 0x19, 0, (64 * 1024), (128 * 1024), 0x00, 0x02, 8, 1, 1, 1, 0, 0}, + {0x86, 0x22, 0, (64 * 1024), (128 * 1024), 0x00, 0x02, 8, 1, 1, 0, 0, 0}, + {0x87, 0x56, 0, (88 * 1024), 32766, 0xA5, 0x01, 8, 0, 2, 0, 1, 0}, + {0x80, 0x09, 0, (88 * 1024), 32766, 0xA5, 0x01, 8, 0, 2, 0, 1, 0}, + {0x86, 0x32, 0, (64 * 1024), (128 * 1024), 0xA5, 0x01, 12, 0, 1, 0, 0, 0}, + {0x86, 0x42, 0, (64 * 1024), (128 * 1024), 0xA5, 0x01, 12, 0, 1, 0, 0, 0}, + {0x87, 0x20, 0, (88 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 1, 0}, + {0x87, 0x22, 0, (88 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 1, 0}, + {0x82, 0x01, 0, (96 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 0, 0}, + {0xF0, 0xC6, 0, (84 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 1, 0}, + {0x56, 0x62, 0, (128 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 4, 0, 0, 5}, + {0x82, 0x05, 0, (120 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 0, 0}, + {0x80, 0x57, 0, (84 * 1024), (128 * 1024), 0xA5, 0x01, 8, 0, 2, 0, 1, 0}, +}; + +struct fts_upgrade *fwupgrade; + +static int fts_check_bootid(void) +{ + int ret = 0; + u8 cmd = 0; + u8 id[2] = { 0 }; + struct fts_upgrade *upg = fwupgrade; + struct ft_chip_t *chip_id; + + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + chip_id = &upg->ts_data->ic_info.ids; + + cmd = FTS_CMD_READ_ID; + ret = fts_read(&cmd, 1, id, 2); + if (ret < 0) { + FTS_ERROR("read boot id(0x%02x 0x%02x) fail", id[0], id[1]); + return ret; + } + + FTS_INFO("read boot id:0x%02x 0x%02x", id[0], id[1]); + if ((chip_id->rom_idh == id[0]) && (chip_id->rom_idl == id[1])) { + return 0; + } + + return -EIO; +} + +static int fts_fwupg_hardware_reset_to_boot(void) +{ + fts_reset_proc(0); + return 0; +} + +static int fts_enter_into_boot(void) +{ + int ret = 0; + int i = 0; + int j = 0; + u8 cmd[2] = { 0 }; + struct fts_upgrade *upg = fwupgrade; + + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + FTS_INFO("enter into boot environment"); + for (i = 0; i < FTS_UPGRADE_LOOP; i++) { + /* hardware tp reset to boot */ + fts_fwupg_hardware_reset_to_boot(); + mdelay(upg->setting_nf->delay_init + i); + + /* enter into boot & check boot id*/ + for (j = 0; j < FTS_READ_BOOT_ID_TIMEOUT; j++) { + cmd[0] = FTS_CMD_START1; + ret = fts_write(cmd, 1); + if (ret >= 0) { + mdelay(upg->setting_nf->delay_init); + ret = fts_check_bootid(); + if (0 == ret) { + FTS_INFO("boot id check pass, retry=%d", i); + return 0; + } + } + } + } + + return -EIO; +} + +static bool fts_check_fast_download(void) +{ + int ret = 0; + u8 cmd[6] = {0xF2, 0x00, 0x78, 0x0A, 0x00, 0x02}; + u8 value = 0; + u8 value2[2] = { 0 }; + + ret = fts_read_reg(0xdb, &value); + if (ret < 0) { + FTS_ERROR("read 0xdb fail"); + goto read_err; + } + + ret = fts_read(cmd, 6, value2, 2); + if (ret < 0) { + FTS_ERROR("read f2 fail"); + goto read_err; + } + + FTS_INFO("0xdb = 0x%x, 0xF2 = 0x%x", value, value2[0]); + if ((value >= 0x18) && (value2[0] == 0x55)) { + FTS_INFO("IC support fast-download"); + return true; + } + +read_err: + FTS_INFO("IC not support fast-download"); + return false; +} + +static int fts_dpram_write_pe(u32 saddr, const u8 *buf, u32 len, bool wpram) +{ + int ret = 0; + int i = 0; + int j = 0; + u8 *cmd = NULL; + u32 addr = 0; + u32 offset = 0; + u32 remainder = 0; + u32 packet_number = 0; + u32 packet_len = 0; + u32 packet_size = FTS_FLASH_PACKET_LENGTH_SPI; + bool fd_support = true; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("dpram write"); + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + if (!buf) { + FTS_ERROR("fw buf is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > upg->setting_nf->app2_offset)) { + FTS_ERROR("fw length(%d) fail", len); + return -EINVAL; + } + + if (upg->setting_nf->fd_check) { + fd_support = fts_check_fast_download(); + if (!fd_support) + packet_size = FTS_FLASH_PACKET_LENGTH_SPI_LOW; + } + + cmd = vmalloc(packet_size + FTS_CMD_WRITE_LEN + 1); + if (NULL == cmd) { + FTS_ERROR("malloc memory for pram write buffer fail"); + return -ENOMEM; + } + memset(cmd, 0, packet_size + FTS_CMD_WRITE_LEN + 1); + + packet_number = len / packet_size; + remainder = len % packet_size; + if (remainder > 0) + packet_number++; + packet_len = packet_size; + FTS_INFO("write data, num:%d remainder:%d", packet_number, remainder); + + cmd[0] = FTS_ROMBOOT_CMD_WRITE; + for (i = 0; i < packet_number; i++) { + offset = i * packet_size; + addr = saddr + offset; + cmd[1] = BYTE_OFF_16(addr); + cmd[2] = BYTE_OFF_8(addr); + cmd[3] = BYTE_OFF_0(addr); + + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + cmd[4] = BYTE_OFF_8(packet_len); + cmd[5] = BYTE_OFF_0(packet_len); + + for (j = 0; j < packet_len; j++) { + cmd[FTS_CMD_WRITE_LEN + j] = buf[offset + j]; + } + + ret = fts_write(&cmd[0], FTS_CMD_WRITE_LEN + packet_len); + if (ret < 0) { + FTS_ERROR("write fw to pram(%d) fail", i); + goto write_pram_err; + } + + if (!fd_support) + mdelay(3); + } + +write_pram_err: + if (cmd) { + vfree(cmd); + cmd = NULL; + } + return ret; +} + +static int fts_dpram_write(u32 saddr, const u8 *buf, u32 len, bool wpram) +{ + int ret = 0; + int i = 0; + int j = 0; + u8 *cmd = NULL; + u32 addr = 0; + u32 baseaddr = wpram ? FTS_PRAM_SADDR : FTS_DRAM_SADDR; + u32 offset = 0; + u32 remainder = 0; + u32 packet_number = 0; + u32 packet_len = 0; + u32 packet_size = FTS_FLASH_PACKET_LENGTH_SPI; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("dpram write"); + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + if (!buf) { + FTS_ERROR("fw buf is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > upg->setting_nf->app2_offset)) { + FTS_ERROR("fw length(%d) fail", len); + return -EINVAL; + } + + cmd = vmalloc(packet_size + FTS_CMD_WRITE_LEN + 1); + if (NULL == cmd) { + FTS_ERROR("malloc memory for pram write buffer fail"); + return -ENOMEM; + } + memset(cmd, 0, packet_size + FTS_CMD_WRITE_LEN + 1); + + packet_number = len / packet_size; + remainder = len % packet_size; + if (remainder > 0) + packet_number++; + packet_len = packet_size; + FTS_INFO("write data, num:%d remainder:%d", packet_number, remainder); + + for (i = 0; i < packet_number; i++) { + offset = i * packet_size; + addr = saddr + offset + baseaddr; + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + /* set pram address */ + cmd[0] = FTS_ROMBOOT_CMD_SET_PRAM_ADDR; + cmd[1] = BYTE_OFF_16(addr); + cmd[2] = BYTE_OFF_8(addr); + cmd[3] = BYTE_OFF_0(addr); + ret = fts_write(&cmd[0], FTS_ROMBOOT_CMD_SET_PRAM_ADDR_LEN); + if (ret < 0) { + FTS_ERROR("set pram(%d) addr(%d) fail", i, addr); + goto write_pram_err; + } + + /* write pram data */ + cmd[0] = FTS_ROMBOOT_CMD_WRITE; + for (j = 0; j < packet_len; j++) { + cmd[1 + j] = buf[offset + j]; + } + ret = fts_write(&cmd[0], 1 + packet_len); + if (ret < 0) { + FTS_ERROR("write fw to pram(%d) fail", i); + goto write_pram_err; + } + } + +write_pram_err: + if (cmd) { + vfree(cmd); + cmd = NULL; + } + return ret; +} + +static int fts_ecc_cal_tp(u32 ecc_saddr, u32 ecc_len, u16 *ecc_value) +{ + int ret = 0; + int i = 0; + u8 cmd[FTS_ROMBOOT_CMD_ECC_NEW_LEN] = { 0 }; + u8 value[2] = { 0 }; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("ecc calc in tp"); + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + cmd[0] = FTS_ROMBOOT_CMD_ECC; + cmd[1] = BYTE_OFF_16(ecc_saddr); + cmd[2] = BYTE_OFF_8(ecc_saddr); + cmd[3] = BYTE_OFF_0(ecc_saddr); + cmd[4] = BYTE_OFF_16(ecc_len); + cmd[5] = BYTE_OFF_8(ecc_len); + cmd[6] = BYTE_OFF_0(ecc_len); + + /* make boot to calculate ecc in pram */ + ret = fts_write(cmd, FTS_ROMBOOT_CMD_ECC_NEW_LEN); + if (ret < 0) { + FTS_ERROR("ecc calc cmd fail"); + return ret; + } + mdelay(2); + + /* wait boot calculate ecc finish */ + if (upg->setting_nf->ecc_delay) { + mdelay(upg->setting_nf->ecc_delay); + } else { + cmd[0] = FTS_ROMBOOT_CMD_ECC_FINISH; + for (i = 0; i < FTS_ECC_FINISH_TIMEOUT; i++) { + ret = fts_read(cmd, 1, value, 1); + if (ret < 0) { + FTS_ERROR("ecc finish cmd fail"); + return ret; + } + if (upg->setting_nf->eccok_val == value[0]) + break; + mdelay(1); + } + if (i >= FTS_ECC_FINISH_TIMEOUT) { + FTS_ERROR("wait ecc finish timeout,ecc_finish=%x", value[0]); + return -EIO; + } + } + + /* get ecc value calculate in boot */ + cmd[0] = FTS_ROMBOOT_CMD_ECC_READ; + ret = fts_read(cmd, 1, value, 2); + if (ret < 0) { + FTS_ERROR("ecc read cmd fail"); + return ret; + } + + *ecc_value = ((u16)(value[0] << 8) + value[1]) & 0x0000FFFF; + return 0; +} + +static int fts_ecc_cal_host(const u8 *data, u32 data_len, u16 *ecc_value) +{ + u16 ecc = 0; + u32 i = 0; + u32 j = 0; + u16 al2_fcs_coef = AL2_FCS_COEF; + + for (i = 0; i < data_len; i += 2 ) { + ecc ^= ((data[i] << 8) | (data[i + 1])); + for (j = 0; j < 16; j ++) { + if (ecc & 0x01) + ecc = (u16)((ecc >> 1) ^ al2_fcs_coef); + else + ecc >>= 1; + } + } + + *ecc_value = ecc & 0x0000FFFF; + return 0; +} + +static int fts_ecc_check(const u8 *buf, u32 len, u32 ecc_saddr) +{ + int ret = 0; + int i = 0; + u16 ecc_in_host = 0; + u16 ecc_in_tp = 0; + int packet_length = 0; + int packet_number = 0; + int packet_remainder = 0; + int offset = 0; + u32 packet_size = FTS_MAX_LEN_FILE; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("ecc check"); + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + if (upg->setting_nf->ecclen_max) { + packet_size = upg->setting_nf->ecclen_max; + } + + packet_number = len / packet_size; + packet_remainder = len % packet_size; + if (packet_remainder) + packet_number++; + packet_length = packet_size; + + for (i = 0; i < packet_number; i++) { + /* last packet */ + if ((i == (packet_number - 1)) && packet_remainder) + packet_length = packet_remainder; + + ret = fts_ecc_cal_host(buf + offset, packet_length, &ecc_in_host); + if (ret < 0) { + FTS_ERROR("ecc in host calc fail"); + return ret; + } + + ret = fts_ecc_cal_tp(ecc_saddr + offset, packet_length, &ecc_in_tp); + if (ret < 0) { + FTS_ERROR("ecc in tp calc fail"); + return ret; + } + + FTS_DEBUG("ecc in tp:%04x,host:%04x,i:%d", ecc_in_tp, ecc_in_host, i); + if (ecc_in_tp != ecc_in_host) { + FTS_ERROR("ecc_in_tp(%x) != ecc_in_host(%x), ecc check fail", + ecc_in_tp, ecc_in_host); + return -EIO; + } + + offset += packet_length; + } + + return 0; +} + +static int fts_pram_write_ecc(const u8 *buf, u32 len) +{ + int ret = 0; + u32 pram_app_size = 0; + u16 code_len = 0; + u16 code_len_n = 0; + u32 pram_start_addr = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("begin to write pram app(bin len:%d)", len); + if (!upg || !upg->setting_nf) { + FTS_ERROR("upgrade/setting_nf is null"); + return -EINVAL; + } + + /* get pram app length */ + code_len = ((u16)buf[FTS_APP_INFO_OFFSET + 0] << 8) + + buf[FTS_APP_INFO_OFFSET + 1]; + code_len_n = ((u16)buf[FTS_APP_INFO_OFFSET + 2] << 8) + + buf[FTS_APP_INFO_OFFSET + 3]; + if ((code_len + code_len_n) != 0xFFFF) { + FTS_ERROR("pram code len(%x %x) fail", code_len, code_len_n); + return -EINVAL; + } + + pram_app_size = ((u32)code_len) * upg->setting_nf->length_coefficient; + FTS_INFO("pram app length in fact:%d", pram_app_size); + + /* write pram */ + if (upg->setting_nf->spi_pe) + ret = fts_dpram_write_pe(pram_start_addr, buf, pram_app_size, true); + else + ret = fts_dpram_write(pram_start_addr, buf, pram_app_size, true); + if (ret < 0) { + FTS_ERROR("write pram fail"); + return ret; + } + + /* check ecc */ + ret = fts_ecc_check(buf, pram_app_size, pram_start_addr); + if (ret < 0) { + FTS_ERROR("pram ecc check fail"); + return ret; + } + + FTS_INFO("pram app write successfully"); + return 0; +} + +static int fts_dram_write_ecc(const u8 *buf, u32 len) +{ + int ret = 0; + u32 dram_size = 0; + u32 pram_app_size = 0; + u32 dram_start_addr = 0; + u16 const_len = 0; + u16 const_len_n = 0; + const u8 *dram_buf = NULL; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("begin to write dram data(bin len:%d)", len); + if (!upg || !upg->setting_nf) { + FTS_ERROR("upgrade/setting_nf is null"); + return -EINVAL; + } + + /* get dram data length */ + const_len = ((u16)buf[FTS_APP_INFO_OFFSET + 0x8] << 8) + + buf[FTS_APP_INFO_OFFSET + 0x9]; + const_len_n = ((u16)buf[FTS_APP_INFO_OFFSET + 0x0A] << 8) + + buf[FTS_APP_INFO_OFFSET + 0x0B]; + if (((const_len + const_len_n) != 0xFFFF) || (const_len == 0)) { + FTS_INFO("no support dram,const len(%x %x)", const_len, const_len_n); + return 0; + } + + dram_size = ((u32)const_len) * upg->setting_nf->length_coefficient; + pram_app_size = ((u32)(((u16)buf[FTS_APP_INFO_OFFSET + 0] << 8) + + buf[FTS_APP_INFO_OFFSET + 1])); + pram_app_size = pram_app_size * upg->setting_nf->length_coefficient; + + dram_buf = buf + pram_app_size; + FTS_INFO("dram buf length in fact:%d,offset:%d", dram_size, pram_app_size); + /* write pram */ + ret = fts_dpram_write(dram_start_addr, dram_buf, dram_size, false); + if (ret < 0) { + FTS_ERROR("write dram fail"); + return ret; + } + + /* check ecc */ + ret = fts_ecc_check(dram_buf, dram_size, dram_start_addr); + if (ret < 0) { + FTS_ERROR("dram ecc check fail"); + return ret; + } + + FTS_INFO("dram data write successfully"); + return 0; +} + +static int fts_pram_start(void) +{ + int ret = 0; + u8 cmd = FTS_ROMBOOT_CMD_START_APP; + + FTS_INFO("remap to start pram"); + ret = fts_write(&cmd, 1); + if (ret < 0) { + FTS_ERROR("write start pram cmd fail"); + return ret; + } + + msleep(10); + return 0; +} + +/* + * description: download fw to IC and run + * + * param - buf: const, fw data buffer + * len: length of fw + * + * return 0 if success, otherwise return error code + */ +static int fts_fw_write_start(const u8 *buf, u32 len, bool need_reset) +{ + int ret = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("begin to write and start fw(bin len:%d)", len); + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + upg->ts_data->fw_is_running = false; + + if (need_reset) { + /* enter into boot environment */ + ret = fts_enter_into_boot(); + if (ret < 0) { + FTS_ERROR("enter into boot environment fail"); + return ret; + } + } + + /* write pram */ + ret = fts_pram_write_ecc(buf, len); + if (ret < 0) { + FTS_ERROR("write pram fail"); + return ret; + } + + if (upg->setting_nf->drwr_support) { + /* write dram */ + ret = fts_dram_write_ecc(buf, len); + if (ret < 0) { + FTS_ERROR("write dram fail"); + return ret; + } + } + + /* remap pram and run fw */ + ret = fts_pram_start(); + if (ret < 0) { + FTS_ERROR("pram start fail"); + return ret; + } + + upg->ts_data->fw_is_running = true; + FTS_INFO("fw download successfully"); + return 0; +} + +static int fts_fw_download(const u8 *buf, u32 len, bool need_reset) +{ + int ret = 0; + int i = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("fw upgrade download function"); + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + if (!buf || (len < FTS_MIN_LEN)) { + FTS_ERROR("fw/len(%d) is invalid", len); + return -EINVAL; + } + + upg->ts_data->fw_loading = 1; + fts_irq_disable(); +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(upg->ts_data, DISABLE); +#endif + + for (i = 0; i < 3; i++) { + FTS_INFO("fw download times:%d", i + 1); + ret = fts_fw_write_start(buf, len, need_reset); + if (0 == ret) + break; + } + if (i >= 3) { + FTS_ERROR("fw download fail"); + ret = -EIO; + goto err_fw_download; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(upg->ts_data, ENABLE); +#endif + + ret = 0; +err_fw_download: + fts_irq_enable(); + upg->ts_data->fw_loading = 0; + + return ret; +} + + +static int fts_read_file_default(char *file_name, u8 **file_buf) +{ + int ret = 0; + char file_path[FILE_NAME_LENGTH] = { 0 }; + struct file *filp = NULL; + struct inode *inode; + mm_segment_t old_fs; + loff_t pos; + loff_t file_len = 0; + + if ((NULL == file_name) || (NULL == file_buf)) { + FTS_ERROR("filename/filebuf is NULL"); + return -EINVAL; + } + + snprintf(file_path, FILE_NAME_LENGTH, "%s%s", FTS_FW_BIN_FILEPATH, file_name); + filp = filp_open(file_path, O_RDONLY, 0); + if (IS_ERR(filp)) { + FTS_ERROR("open %s file fail", file_path); + return -ENOENT; + } + +#if 1 + inode = filp->f_inode; +#else + /* reserved for linux earlier verion */ + inode = filp->f_dentry->d_inode; +#endif + + file_len = inode->i_size; + *file_buf = (u8 *)vmalloc(file_len); + if (NULL == *file_buf) { + FTS_ERROR("file buf malloc fail"); + filp_close(filp, NULL); + return -ENOMEM; + } + old_fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + ret = vfs_read(filp, *file_buf, file_len, &pos); +#else + ret = kernel_read(filp, *file_buf, file_len , &pos); +#endif + if (ret < 0) + FTS_ERROR("read file fail"); + FTS_INFO("file len:%d read len:%d pos:%d", (u32)file_len, ret, (u32)pos); + filp_close(filp, NULL); + set_fs(old_fs); + + return ret; +} + +static int fts_read_file_request_firmware(char *file_name, u8 **file_buf) +{ + int ret = 0; + const struct firmware *fw = NULL; + char fwname[FILE_NAME_LENGTH] = { 0 }; + struct fts_upgrade *upg = fwupgrade; + +#if !FTS_FW_REQUEST_SUPPORT + return -EINVAL; +#endif + + snprintf(fwname, FILE_NAME_LENGTH, "%s", file_name); + ret = request_firmware(&fw, fwname, upg->ts_data->dev); + if (0 == ret) { + FTS_INFO("firmware(%s) request successfully", fwname); + *file_buf = vmalloc(fw->size); + if (NULL == *file_buf) { + FTS_ERROR("fw buffer vmalloc fail"); + ret = -ENOMEM; + } else { + memcpy(*file_buf, fw->data, fw->size); + ret = fw->size; + } + } else { + FTS_INFO("firmware(%s) request fail,ret=%d", fwname, ret); + ret = -EIO; + } + + if (fw != NULL) { + release_firmware(fw); + fw = NULL; + } + + return ret; +} + +static int fts_read_file(char *file_name, u8 **file_buf) +{ + int ret = 0; + + ret = fts_read_file_request_firmware(file_name, file_buf); + if (ret < 0) { + ret = fts_read_file_default(file_name, file_buf); + if (ret < 0) { + FTS_ERROR("get fw file(default) fail"); + return ret; + } + } + + return ret; +} + +int fts_upgrade_bin(char *fw_name, bool force) +{ + int ret = 0; + u32 fw_file_len = 0; + u8 *fw_file_buf = NULL; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("start upgrade with fw bin"); + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + if (upg->ts_data->fw_loading) { + FTS_INFO("fw is loading, not download again"); + return -EINVAL; + } + + ret = fts_read_file(fw_name, &fw_file_buf); + if ((ret < 0) || (ret < FTS_MIN_LEN)) { + FTS_ERROR("read fw bin file(%s) fail, len:%d", fw_name, ret); + goto err_bin; + } + + fw_file_len = ret; + FTS_INFO("fw bin file len:%d", fw_file_len); + ret = fts_fw_download(fw_file_buf, fw_file_len, true); + if (ret < 0) { + FTS_ERROR("upgrade fw bin failed"); + goto err_bin; + } + + FTS_INFO("upgrade fw bin success"); + +err_bin: + if (fw_file_buf) { + vfree(fw_file_buf); + fw_file_buf = NULL; + } + return ret; +} + + +int fts_enter_test_environment(bool test_state) +{ + int ret = 0; + int i = 0; + u8 detach_flag = 0; + u32 app_offset = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("fw test download function"); + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upgrade/ts_data/setting_nf is null"); + return -EINVAL; + } + + if (upg->ts_data->fw_loading) { + FTS_INFO("fw is loading, not download again"); + return -EINVAL; + } + + if (!upg->fw || (upg->fw_length <= upg->setting_nf->app2_offset)) { + FTS_INFO("not multi-app"); + return 0; + } + + if (test_state) { + app_offset = upg->setting_nf->app2_offset; + } + + /*download firmware*/ + upg->ts_data->fw_loading = 1; + for (i = 0; i < 3; i++) { + FTS_INFO("fw download times:%d", i + 1); + ret = fts_fw_write_start(upg->fw + app_offset, upg->fw_length, true); + if (0 == ret) + break; + } + upg->ts_data->fw_loading = 0; + + if (i >= 3) { + FTS_ERROR("fw(addr:%x) download fail", app_offset); + return -EIO; + } + + msleep(50); + ret = fts_read_reg(FTS_REG_FACTORY_MODE_DETACH_FLAG, &detach_flag); + FTS_INFO("regb4:0x%02x", detach_flag); + + return 0; +} + +int fts_fw_resume(bool need_reset) +{ + int ret = 0; + struct fts_upgrade *upg = fwupgrade; + const struct firmware *fw = NULL; + char fwname[FILE_NAME_LENGTH] = { 0 }; + bool get_fw_i_flag = true; + const u8 *fw_buf = NULL; + u32 fwlen = 0; + + FTS_INFO("fw upgrade resume function"); + if (!upg || !upg->fw) { + FTS_ERROR("upg/fw is null"); + return -EINVAL; + } + + if (upg->ts_data->fw_loading) { + FTS_INFO("fw is loading, not download again"); + return -EINVAL; + } + + if (FTS_FW_REQUEST_SUPPORT) { + 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); + fw_buf = fw->data; + fwlen = fw->size; + get_fw_i_flag = false; + } else { + FTS_ERROR("%s:firmware(%s) request fail,ret=%d\n", + __func__, fwname, ret); + } + } + + if (get_fw_i_flag) { + FTS_INFO("download fw from bootimage"); + fw_buf = upg->fw; + fwlen = upg->fw_length; + } + + ret = fts_fw_download(fw_buf, fwlen, need_reset); + if (ret < 0) { + FTS_ERROR("fw resume download failed"); + } + + /* update to newest fw if needed */ + if (fw != NULL) { + u8 *tmpbuf = NULL; + if (upg->fw_from_request) + vfree(upg->fw); + tmpbuf = vmalloc(fw->size); + if (NULL == tmpbuf) { + FTS_ERROR("fw buffer vmalloc fail"); + ret = -ENOMEM; + goto FTS_FW_RESUME_VMALLOC_ERROR; + } else { + memcpy(tmpbuf, fw->data, fw->size); + upg->fw = tmpbuf; + upg->fw_length = fw->size; + upg->fw_from_request = 1; + FTS_INFO("upg->fw_length = %d", upg->fw_length); + } + } + +FTS_FW_RESUME_VMALLOC_ERROR: + if (FTS_FW_REQUEST_SUPPORT && fw != NULL) { + release_firmware(fw); + fw = NULL; + } + + return ret; +} + +int fts_fw_recovery(void) +{ + int ret = 0; + u8 boot_state = 0; + u8 chip_id = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("check if boot recovery"); + if (!upg || !upg->ts_data || !upg->setting_nf) { + FTS_ERROR("upg/ts_data/setting_nf is null"); + return -EINVAL; + } + + if (upg->ts_data->fw_loading) { + FTS_INFO("fw is loading, not download again"); + return -EINVAL; + } + + upg->ts_data->fw_is_running = false; + if (!upg->ts_data->force_reflash) { + ret = fts_check_bootid(); + if (ret < 0) { + FTS_ERROR("check boot id fail"); + upg->ts_data->fw_is_running = true; + return ret; + } + + ret = fts_read_reg(0xD0, &boot_state); + if (ret < 0) { + FTS_ERROR("read boot state failed, ret=%d", ret); + upg->ts_data->fw_is_running = true; + return ret; + } + + if (boot_state != upg->setting_nf->upgsts_boot) { + FTS_INFO("not in boot mode(0x%x),exit", boot_state); + upg->ts_data->fw_is_running = true; + return -EIO; + } + + FTS_INFO("abnormal situation,need download fw"); + } //!force_reflash + + ret = fts_fw_resume(false); + if (ret < 0) { + FTS_ERROR("fts_fw_resume fail"); + return ret; + } + + ret = fts_read_reg(FTS_REG_CHIP_ID, &chip_id); + FTS_INFO("read chip id:0x%02x", chip_id); + + fts_tp_state_recovery(upg->ts_data); + + FTS_INFO("boot recovery pass"); + return ret; +} + +static int fts_fwupg_get_module_info(struct fts_upgrade *upg) +{ + 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) { + 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 fw file successfully"); + break; + } + } + if (i >= FTS_GET_MODULE_NUM) { + FTS_ERROR("no module id match, don't get file"); + return -ENODATA; + } + } + + upg->module_info = info; + return 0; +} + +int fts_fw_update_vendor_name(const char* name) { + struct fts_upgrade *upg = fwupgrade; + char* pos; + int len; + + FTS_FUNC_ENTER(); + if (!upg) { + FTS_ERROR("fwupgrade null"); + return -EINVAL; + } + + if (!upg->module_info) { + FTS_INFO("upgrade get module_info"); + fts_fwupg_get_module_info(upg); + } + + if (!upg->module_info) { + FTS_ERROR("upgrade module_info init fail"); + return -EINVAL; + } + + len = strlen(name); + if (len > FILE_NAME_LENGTH) { + FTS_ERROR("vendor name is too long"); + return -EINVAL; + } + pos = strstr(name, ".bin"); + if (pos == NULL) + snprintf(upg->module_info->vendor_name, FILE_NAME_LENGTH, "%s", name); + else { + len = len - strlen(".bin"); + snprintf(upg->module_info->vendor_name, len + 1, "%s", name); + } + FTS_INFO("upgrade name=%s", name); + 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 !FTS_AUTO_UPGRADE_EN + if (fts_data->panel_supplier) { + FTS_INFO("fts_data->panel_supplier=%s\n", fts_data->panel_supplier); + snprintf(fwname, FILE_NAME_LENGTH, "%s%s.bin", \ + FTS_FW_NAME_PREX_WITH_REQUEST, \ + fts_data->panel_supplier); + } + else +#endif + 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 (0 == ret) { + 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) { + msleep(500); + 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); + } + + FTS_INFO("upgrade fw file len:%d", upg->fw_length); + if (upg->fw_length < FTS_MIN_LEN) { + FTS_ERROR("fw file len(%d) fail", upg->fw_length); + return -ENODATA; + } + + return ret; +} + +static void fts_fwupg_work(struct work_struct *work) +{ + int ret = 0; + u8 chip_id = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("fw upgrade work function"); + if (!upg || !upg->ts_data) { + FTS_ERROR("upg/ts_data is null"); + return ; + } + + /* get fw */ + ret = fts_fwupg_get_fw_file(upg); + if (ret < 0) { +#if !FTS_AUTO_UPGRADE_EN + FTS_INFO("FTS_AUTO_UPGRADE_EN disabled, upgrade later"); +#else + FTS_ERROR("get file fail, can't upgrade"); +#endif + return ; + } + +#if !FTS_AUTO_UPGRADE_EN + FTS_INFO("FTS_AUTO_UPGRADE_EN is disabled, not upgrade when power on"); + return; +#endif + + if (upg->ts_data->fw_loading) { + FTS_INFO("fw is loading, not download again"); + return ; + } + + ret = fts_fw_download(upg->fw, upg->fw_length, true); + if (ret < 0) { + FTS_ERROR("fw auto download failed"); + } else { + msleep(50); + ret = fts_read_reg(FTS_REG_CHIP_ID, &chip_id); + FTS_INFO("read chip id:0x%02x", chip_id); + } +} + +int fts_fwupg_init(struct fts_ts_data *ts_data) +{ + int i = 0; + struct upgrade_setting_nf *setting = &upgrade_setting_list[0]; + int setting_count = + sizeof(upgrade_setting_list) / sizeof(upgrade_setting_list[0]); + + FTS_FUNC_ENTER(); + 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 == setting_count) { + FTS_ERROR("no upgrade settings in tp driver, init fail"); + return -ENODATA; + } + + fwupgrade = (struct fts_upgrade *)kzalloc(sizeof(*fwupgrade), GFP_KERNEL); + if (NULL == fwupgrade) { + FTS_ERROR("malloc memory for upgrade fail"); + return -ENOMEM; + } + + if (1 == setting_count) { + fwupgrade->setting_nf = setting; + } else { + for (i = 0; i < setting_count; i++) { + setting = &upgrade_setting_list[i]; + if ((setting->rom_idh == ts_data->ic_info.ids.rom_idh) + && (setting->rom_idl == ts_data->ic_info.ids.rom_idl)) { + FTS_INFO("match upgrade setting,type(ID):0x%02x%02x", + setting->rom_idh, setting->rom_idl); + fwupgrade->setting_nf = setting; + } + } + } + + if (NULL == fwupgrade->setting_nf) { + FTS_ERROR("no upgrade settings match, can't upgrade"); + kfree(fwupgrade); + fwupgrade = NULL; + return -ENODATA; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ts_data, DISABLE); +#endif + + fwupgrade->ts_data = ts_data; + INIT_WORK(&ts_data->fwupg_work, fts_fwupg_work); + queue_work(ts_data->ts_workqueue, &ts_data->fwupg_work); + + FTS_FUNC_EXIT(); + return 0; +} + +int fts_fwupg_exit(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + cancel_work_sync(&ts_data->fwupg_work); + + if (fwupgrade) { + if (fwupgrade->fw_from_request) { + vfree(fwupgrade->fw); + fwupgrade->fw = NULL; + } + + kfree(fwupgrade); + fwupgrade = NULL; + } + FTS_FUNC_EXIT(); + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_flash.h b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_flash.h new file mode 100644 index 000000000000..547f0bbca523 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_flash.h @@ -0,0 +1,214 @@ +/************************************************************************ +* Copyright (c) 2012-2020, 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_FWCFG 0xA8 + +#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_DATA_LEN 0xB0 +#define FTS_CMD_APP_DATA_LEN_INCELL 0x7A +#define FTS_CMD_DATA_LEN_LEN 4 +#define FTS_CMD_SET_WFLASH_ADDR 0xAB +#define FTS_CMD_SET_RFLASH_ADDR 0xAC +#define FTS_LEN_SET_ADDR 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 (256 * 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, + UPGRADE_SPEC_V_1_1 = 0x0101, + UPGRADE_SPEC_V_1_2 = 0x0102, +}; + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +/* IC info */ +struct upgrade_func { + u16 ctype[FTS_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; + u8 spi_pe; + u8 length_coefficient; + u8 fd_check; + u8 drwr_support; + u8 ecc_delay; +}; + +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 +*****************************************************************************/ + + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +#endif diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_gesture.c b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_gesture.c new file mode 100644 index 000000000000..602116492376 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_gesture.c @@ -0,0 +1,714 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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" + +#if FTS_GESTURE_EN +/****************************************************************************** +* 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 + +#ifdef FOCALTECH_SENSOR_EN +#define REPORT_MAX_COUNT 10000 +#endif + +/***************************************************************************** +* 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]; + u8 active; +}; + +/***************************************************************************** +* Static variables +*****************************************************************************/ +static struct fts_gesture_st fts_gesture_data; +#ifdef FOCALTECH_SENSOR_EN +#ifdef CONFIG_HAS_WAKELOCK +static struct wake_lock gesture_wakelock; +#else +static struct wakeup_source *gesture_wakelock; +#endif +static struct sensors_classdev __maybe_unused sensors_touch_cdev = { + .name = "dt-gesture", + .vendor = "Focaltech", + .version = 1, + .type = SENSOR_TYPE_MOTO_DOUBLE_TAP, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + /* WAKE_UP & SPECIAL_REPORT */ + .flags = 1 | 6, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#endif + +/***************************************************************************** +* 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 = dev_get_drvdata(dev); + + 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_support ? "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 = dev_get_drvdata(dev); + + mutex_lock(&ts_data->input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_INFO("enable gesture"); + ts_data->gesture_support = ENABLE; + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_INFO("disable gesture"); + ts_data->gesture_support = 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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_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; +} + +static ssize_t fts_gesture_bm_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + mutex_lock(&ts_data->input_dev->mutex); + count = snprintf(buf, PAGE_SIZE, "gesture bmode:%d\n", + ts_data->gesture_bmode); + mutex_unlock(&ts_data->input_dev->mutex); + + return count; +} + +static ssize_t fts_gesture_bm_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + int value = 0xFF; + int ret = 0; + + mutex_lock(&ts_data->input_dev->mutex); + ret = sscanf(buf, "%d", &value); + if (ret == 1) { + FTS_DEBUG("gesture bmode:%d->%d", ts_data->gesture_bmode, value); + ts_data->gesture_bmode = value; + } + mutex_unlock(&ts_data->input_dev->mutex); + + return count; +} + +/* 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 DEVICE_ATTR(fts_gesture_bm, S_IRUGO | S_IWUSR, + fts_gesture_bm_show, fts_gesture_bm_store); + +static struct attribute *fts_gesture_mode_attrs[] = { + &dev_attr_fts_gesture_mode.attr, + &dev_attr_fts_gesture_buf.attr, + &dev_attr_fts_gesture_bm.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; +#ifdef FOCALTECH_SENSOR_EN + int ret = 0; + static int report_cnt = 0; +#endif + + 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_GESTURE_U; + 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); +#ifdef FOCALTECH_SENSOR_EN + if (!(fts_data->wakeable && fts_data->gesture_support)) { + FTS_INFO("Gesture got but wakeable not set. Skip this gesture."); + return; + } + + /* report single tap */ + if (gesture == KEY_GESTURE_U) { + if (fts_data->pdata && fts_data->pdata->report_gesture_key) { + FTS_DEBUG("report_gesture_key"); + input_report_key(fts_data->sensor_pdata->input_sensor_dev, KEY_F1, 1); + input_sync(fts_data->sensor_pdata->input_sensor_dev); + input_report_key(fts_data->sensor_pdata->input_sensor_dev, KEY_F1, 0); + input_sync(fts_data->sensor_pdata->input_sensor_dev); + ++report_cnt; + } else { + FTS_DEBUG("report_gesture_key not set, input_report_abs"); + input_report_abs(fts_data->sensor_pdata->input_sensor_dev, + ABS_DISTANCE, ++report_cnt); + input_sync(fts_data->sensor_pdata->input_sensor_dev); + } + } + + FTS_INFO("input report: %d", report_cnt); + if (report_cnt >= REPORT_MAX_COUNT) + report_cnt = 0; + + if (!ret) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&gesture_wakelock, msecs_to_jiffies(5000)); +#else + PM_WAKEUP_EVENT(gesture_wakelock, 5000); +#endif + } +#else + input_report_key(input_dev, gesture, 1); + input_sync(input_dev); + input_report_key(input_dev, gesture, 0); + input_sync(input_dev); +#endif + } +} + +#ifdef FOCALTECH_SENSOR_EN +static int fts_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + FTS_INFO("Gesture set enable %d!", enable); + mutex_lock(&fts_data->state_mutex); + if (enable == 1) + fts_data->gesture_support = true; + else if (enable == 0) + fts_data->gesture_support = false; + else + FTS_INFO("unknown enable symbol\n"); + + mutex_unlock(&fts_data->state_mutex); + return 0; +} + +static int fts_sensor_init(struct fts_ts_data *data) +{ + struct focaltech_sensor_platform_data *sensor_pdata; + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + FTS_ERROR("Failed to allocate device"); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct focaltech_sensor_platform_data), + GFP_KERNEL); + if (!sensor_pdata) { + FTS_ERROR("Failed to allocate memory"); + goto free_sensor_pdata; + } + data->sensor_pdata = sensor_pdata; + + if (data->pdata->report_gesture_key) { + __set_bit(EV_KEY, sensor_input_dev->evbit); + __set_bit(KEY_F1, sensor_input_dev->keybit); + } + else { + __set_bit(EV_ABS, sensor_input_dev->evbit); + input_set_abs_params(sensor_input_dev, ABS_DISTANCE, + 0, REPORT_MAX_COUNT, 0, 0); + } + __set_bit(EV_SYN, sensor_input_dev->evbit); + + sensor_input_dev->name = "double-tap"; + data->sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + FTS_ERROR("Unable to register device, err=%d", err); + goto free_sensor_input_dev; + } + + sensor_pdata->ps_cdev = sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = fts_sensor_set_enable; + sensor_pdata->data = data; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(data->sensor_pdata->input_sensor_dev); +free_sensor_input_dev: + input_free_device(data->sensor_pdata->input_sensor_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, sensor_pdata); + data->sensor_pdata = NULL; +exit: + return 1; +} + +int fts_sensor_remove(struct fts_ts_data *data) +{ + sensors_classdev_unregister(&data->sensor_pdata->ps_cdev); + input_unregister_device(data->sensor_pdata->input_sensor_dev); + devm_kfree(&data->sensor_pdata->input_sensor_dev->dev, + data->sensor_pdata); + data->sensor_pdata = NULL; + data->wakeable = false; + data->gesture_support = false; + return 0; +} +#endif + +/***************************************************************************** +* 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 +* 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 *touch_buf) +{ + 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->gesture_support || !ts_data->suspended) { + return -EINVAL; + } + + FTS_FUNC_ENTER(); + if (ts_data->gesture_bmode == GESTURE_BM_TOUCH) { + memcpy(buf, touch_buf + FTS_TOUCH_DATA_LEN, FTS_GESTURE_DATA_LEN); + } else { + 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) +{ + FTS_FUNC_ENTER(); + if (ts_data->gesture_support && (ENABLE == fts_gesture_data.active)) { + 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(); + /* gesture not enable, return immediately */ + if (!ts_data->gesture_support) { + FTS_INFO("gesture is disabled"); + return -EINVAL; + } + + 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); + fts_gesture_data.active = DISABLE; + return -EIO; + } + + if (enable_irq_wake(ts_data->irq)) { + FTS_DEBUG("enable_irq_wake(irq:%d) fail", ts_data->irq); + } + + fts_gesture_data.active = ENABLE; + 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(); + + /* gesture not enable, return immediately */ + if (!ts_data->gesture_support) { + FTS_INFO("gesture is disabled"); + return -EINVAL; + } + + if (fts_gesture_data.active == DISABLE) { + FTS_DEBUG("gesture active is disable, return immediately"); + return -EINVAL; + } + + if (disable_irq_wake(ts_data->irq)) { + FTS_DEBUG("disable_irq_wake(irq:%d) fail", ts_data->irq); + } + fts_gesture_data.active = DISABLE; + 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; +#ifdef FOCALTECH_SENSOR_EN + static bool initialized_sensor; +#endif + + 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); + +#ifdef FOCALTECH_SENSOR_EN + if (!initialized_sensor) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&gesture_wakelock, WAKE_LOCK_SUSPEND, "poll-wake-lock"); +#else + PM_WAKEUP_REGISTER(ts_data->dev, gesture_wakelock, "poll-wake-lock"); + if (!gesture_wakelock) { + FTS_ERROR("failed to allocate wakeup source\n"); + return -ENOMEM; + } +#endif + + if (!fts_sensor_init(ts_data)) + initialized_sensor = true; + + } +#endif + + fts_create_gesture_sysfs(ts_data->dev); + + memset(&fts_gesture_data, 0, sizeof(struct fts_gesture_st)); + ts_data->gesture_bmode = GESTURE_BM_REG; + ts_data->gesture_support = DISABLE; // disable by default + fts_gesture_data.active = DISABLE; + + if ((ts_data->ic_info.ids.type <= 0x25) + || (ts_data->ic_info.ids.type == 0x87) + || (ts_data->ic_info.ids.type == 0x88)) { + FTS_INFO("ic type:0x%02x,GESTURE_BM_TOUCH", ts_data->ic_info.ids.type); + ts_data->touch_size += FTS_GESTURE_DATA_LEN; + ts_data->gesture_bmode = GESTURE_BM_TOUCH; + } + + 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; +} +#endif diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_point_report_check.c b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_point_report_check.c new file mode 100644 index 000000000000..bc790d7ea1dd --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_point_report_check.c @@ -0,0 +1,187 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 */ +#define PRC_INTR_INTERVALS 100 /* unit:ms */ + +/***************************************************************************** +* Static variables +*****************************************************************************/ + +/***************************************************************************** +* 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); + unsigned long cur_jiffies = jiffies; + unsigned long intr_timeout = msecs_to_jiffies(PRC_INTR_INTERVALS); + + if (ts_data->prc_support && !ts_data->suspended) { + intr_timeout += ts_data->intr_jiffies; + if (time_after(cur_jiffies, intr_timeout)) { + if (ts_data->touch_points) { + fts_release_all_finger(); + if (ts_data->log_level >= 3) + FTS_DEBUG("prc trigger interval:%dms", + jiffies_to_msecs(cur_jiffies - ts_data->intr_jiffies)); + } + ts_data->prc_mode = 0; + } else { + queue_delayed_work(ts_data->ts_workqueue, &ts_data->prc_work, + msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME)); + ts_data->prc_mode = 1; + } + } else { + ts_data->prc_mode = 0; + } +} + +/***************************************************************************** +* 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) +{ + if (ts_data->prc_support && !ts_data->prc_mode && !ts_data->suspended) { + queue_delayed_work(ts_data->ts_workqueue, &ts_data->prc_work, + msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME)); + ts_data->prc_mode = 1; + } +} + + +static ssize_t fts_prc_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_DEBUG("enable prc"); + ts_data->prc_support = ENABLE; + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_DEBUG("disable prc"); + cancel_delayed_work_sync(&ts_data->prc_work); + ts_data->prc_support = DISABLE; + } + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_prc_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count = snprintf(buf, PAGE_SIZE, "PRC: %s\n", \ + ts_data->prc_support ? "Enable" : "Disable"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static DEVICE_ATTR(fts_prc, S_IRUGO | S_IWUSR, fts_prc_show, fts_prc_store); + +/***************************************************************************** +* 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) +{ + int ret = 0; + + 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; + } + + ret = sysfs_create_file(&ts_data->dev->kobj, &dev_attr_fts_prc.attr); + if ( ret < 0) { + FTS_ERROR("create prc sysfs fail"); + } + + ts_data->prc_support = FTS_POINT_REPORT_CHECK_EN; + 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(); + cancel_delayed_work_sync(&ts_data->prc_work); + sysfs_remove_file(&ts_data->dev->kobj, &dev_attr_fts_prc.attr); + FTS_FUNC_EXIT(); + return 0; +} +#endif /* FTS_POINT_REPORT_CHECK_EN */ diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_spi.c b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_spi.c new file mode 100644 index 000000000000..561ef562487b --- /dev/null +++ b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/focaltech_spi.c @@ -0,0 +1,452 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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_spi.c +* +* Author: FocalTech Driver Team +* +* Created: 2019-03-21 +* +* Abstract: new spi protocol communication with TP +* +* Version: v1.0 +* +* Revision History: +* +************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define SPI_RETRY_NUMBER 3 +#define CS_HIGH_DELAY 150 /* unit: us */ +#define SPI_BUF_LENGTH 4096 + +#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*/ +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ + +/***************************************************************************** +* Static variables +*****************************************************************************/ + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* 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 fts_spi_buf_show(u8 *data, int datalen) +{ + int i = 0; + int count = 0; + int size = 0; + char *tmpbuf = NULL; + + if (!data || (datalen <= 0)) { + FTS_ERROR("data/datalen is invalid"); + return; + } + + size = (datalen > 256) ? 256 : datalen; + tmpbuf = kzalloc(1024, GFP_KERNEL); + if (!tmpbuf) { + FTS_ERROR("tmpbuf zalloc fail"); + return; + } + + for (i = 0; i < size; i++) + count += snprintf(tmpbuf + count, 1024 - count, "%02X ", data[i]); + + FTS_DEBUG("%s", tmpbuf); + if (tmpbuf) { + kfree(tmpbuf); + tmpbuf = NULL; + } +} + +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) { + fts_spi_buf_show(rdata, rlen); + return -EIO; + } + + return 0; +} + +int fts_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 (NULL == txbuf) { + FTS_ERROR("txbuf malloc fail"); + ret = -ENOMEM; + goto err_write; + } + + rxbuf = kzalloc(txlen_need, GFP_KERNEL); + if (NULL == rxbuf) { + 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 ((0 == ret) && ((rxbuf[3] & 0xA0) == 0)) { + break; + } else { + 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) { + if (txbuf) { + kfree(txbuf); + txbuf = NULL; + } + + if (rxbuf) { + kfree(rxbuf); + rxbuf = NULL; + } + } + + udelay(CS_HIGH_DELAY); + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +int fts_write_reg(u8 addr, u8 value) +{ + u8 writebuf[2] = { 0 }; + + writebuf[0] = addr; + writebuf[1] = value; + return fts_write(writebuf, 2); +} + +int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen) +{ + 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 = 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 (NULL == txbuf) { + FTS_ERROR("txbuf malloc fail"); + ret = -ENOMEM; + goto err_read; + } + + rxbuf = kzalloc(txlen_need, GFP_KERNEL); + if (NULL == rxbuf) { + 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 ((0 == ret) && ((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; + } else { + 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) { + if (txbuf) { + kfree(txbuf); + txbuf = NULL; + } + + if (rxbuf) { + kfree(rxbuf); + rxbuf = NULL; + } + } + + udelay(CS_HIGH_DELAY); + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +int fts_read_reg(u8 addr, u8 *value) +{ + return fts_read(&addr, 1, value, 1); +} + + +int fts_spi_transfer_direct(u8 *writebuf, u32 writelen, u8 *readbuf, u32 readlen) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + u8 *txbuf = NULL; + u8 *rxbuf = NULL; + bool read_cmd = (readbuf && readlen) ? 1 : 0; + u32 txlen = (read_cmd) ? readlen : writelen; + + if (!writebuf || !writelen) { + FTS_ERROR("writebuf/len is invalid"); + return -EINVAL; + } + + mutex_lock(&ts_data->bus_lock); + if (txlen > SPI_BUF_LENGTH) { + txbuf = kzalloc(txlen, GFP_KERNEL); + if (NULL == txbuf) { + FTS_ERROR("txbuf malloc fail"); + ret = -ENOMEM; + goto err_spi_dir; + } + + rxbuf = kzalloc(txlen, GFP_KERNEL); + if (NULL == rxbuf) { + FTS_ERROR("rxbuf malloc fail"); + ret = -ENOMEM; + goto err_spi_dir; + } + } 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); + } + + memcpy(txbuf, writebuf, writelen); + ret = fts_spi_transfer(txbuf, rxbuf, txlen); + if (ret < 0) { + FTS_ERROR("data read(addr:%x) fail,status:%x,ret:%d", txbuf[0], rxbuf[3], ret); + goto err_spi_dir; + } + + if (read_cmd) { + memcpy(readbuf, rxbuf, txlen); + } + + ret = 0; +err_spi_dir: + if (txlen > SPI_BUF_LENGTH) { + if (txbuf) { + kfree(txbuf); + txbuf = NULL; + } + + if (rxbuf) { + kfree(rxbuf); + rxbuf = NULL; + } + } + + udelay(CS_HIGH_DELAY); + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +int fts_bus_init(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + ts_data->bus_tx_buf = kzalloc(SPI_BUF_LENGTH, GFP_KERNEL); + if (NULL == ts_data->bus_tx_buf) { + FTS_ERROR("failed to allocate memory for bus_tx_buf"); + return -ENOMEM; + } + + ts_data->bus_rx_buf = kzalloc(SPI_BUF_LENGTH, GFP_KERNEL); + if (NULL == ts_data->bus_rx_buf) { + FTS_ERROR("failed to allocate memory for bus_rx_buf"); + return -ENOMEM; + } + + ts_data->dummy_byte = SPI_DUMMY_BYTE; + FTS_FUNC_EXIT(); + return 0; +} + +int fts_bus_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; +} + diff --git a/drivers/input/touchscreen/focaltech_0flash_v2_mmi/include/firmware/fw_sample.h b/drivers/input/touchscreen/focaltech_0flash_v2_mmi/include/firmware/fw_sample.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/input/touchscreen/focaltech_mmi/Android.mk b/drivers/input/touchscreen/focaltech_mmi/Android.mk new file mode 100644 index 000000000000..f3299646f0cb --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := focaltech_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/focaltech_mmi/Kbuild b/drivers/input/touchscreen/focaltech_mmi/Kbuild new file mode 100644 index 000000000000..229af9197872 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/Kbuild @@ -0,0 +1,13 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/focaltech_mmi + +obj-m := focaltech_mmi.o +focaltech_mmi-objs += focaltech_ts.o +focaltech_mmi-objs += focaltech_flash.o +focaltech_mmi-objs += focaltech_upgrade_ft8716.o +focaltech_mmi-objs += focaltech_upgrade_idc.o +focaltech_mmi-objs += focaltech_upgrade_ft5x46.o +focaltech_mmi-objs += focaltech_upgrade_ft5422u.o +focaltech_mmi-objs += focaltech_upgrade_ft8006u.o diff --git a/drivers/input/touchscreen/focaltech_mmi/Makefile b/drivers/input/touchscreen/focaltech_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/focaltech_mmi/focaltech_flash.c b/drivers/input/touchscreen/focaltech_mmi/focaltech_flash.c new file mode 100644 index 000000000000..5171f26899e9 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/focaltech_flash.c @@ -0,0 +1,2005 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2010-2016, 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. + * + */ + +#include +#include +#include +#include +#include +#include "focaltech_flash.h" +#include "focaltech_upgrade_common.h" + +static struct fts_upgrade_fun *fts_updatefun_curr; +static struct ft_chip_t *fts_chip_type_curr; +static const struct ft_ts_platform_data *fts_pdata_curr; +static const char *fts_fw_name_curr; + +int fts_i2c_read(struct i2c_client *client, char *writebuf, + int writelen, char *readbuf, int readlen) +{ + int ret; + + if (writelen > 0) { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) + dev_err(&client->dev, "%s: i2c read error.\n", + __func__); + } else { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s:i2c read error.\n", __func__); + } + return ret; +} + +int fts_i2c_write(struct i2c_client *client, char *writebuf, int writelen) +{ + int ret; + + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s: i2c write error.\n", __func__); + + return ret; +} + +int fts_i2c_write_reg(struct i2c_client *client, u8 addr, const u8 val) +{ + u8 buf[2] = {0}; + + buf[0] = addr; + buf[1] = val; + + return fts_i2c_write(client, buf, sizeof(buf)); +} + +int fts_i2c_read_reg(struct i2c_client *client, u8 addr, u8 *val) +{ + return fts_i2c_read(client, &addr, 1, val, 1); +} + +/************************************************************************ +* Name: fts_ctpm_i2c_hid2std +* Brief: HID to I2C +* Input: i2c info +* Output: no +* Return: fail =0 +***********************************************************************/ +int fts_ctpm_i2c_hid2std(struct i2c_client *client) +{ + u8 buf[5] = {0}; + int bRet = 0; + + if (FTS_CHIP_IDC(fts_chip_type_curr->chip_type)) + return 0; + + buf[0] = 0xeb; + buf[1] = 0xaa; + buf[2] = 0x09; + bRet = fts_i2c_write(client, buf, 3); + msleep(20); + buf[0] = buf[1] = buf[2] = 0; + fts_i2c_read(client, buf, 0, buf, 3); + + if ((buf[0] == 0xeb) && (buf[1] == 0xaa) && (buf[2] == 0x08)) { + FTS_DEBUG("hidi2c change to stdi2c successful\n"); + bRet = 1; + } else { + FTS_ERROR("hidi2c change to stdi2c error\n"); + bRet = 0; + } + + return bRet; +} + +/************************************************************************ +* Name: fts_ctpm_rom_or_pram_reset +* Brief: RST CMD(07), reset to romboot(maybe->bootloader) +* Input: +* Output: +* Return: +***********************************************************************/ +void fts_ctpm_rom_or_pram_reset(struct i2c_client *client) +{ + u8 rst_cmd = FTS_REG_RESET_FW; + + FTS_INFO("[UPGRADE] Reset to romboot/bootloader\n"); + fts_i2c_write(client, &rst_cmd, 1); + /* The delay can't be changed */ + msleep(300); +} + +/************************************************************************ +* Name: fts_ctpm_get_pram_or_rom_id +* Brief: 0 +* Input: 0 +* Output: 0 +* Return: 0 +***********************************************************************/ +enum FW_STATUS fts_ctpm_get_pram_or_rom_id(struct i2c_client *client) +{ + u8 buf[4]; + u8 reg_val[2] = {0}; + enum FW_STATUS inRomBoot = FTS_RUN_IN_ERROR; + + fts_ctpm_i2c_hid2std(client); + + /*Enter upgrade mode*/ + /*send 0x55 in time windows*/ + buf[0] = FTS_UPGRADE_55; + buf[1] = FTS_UPGRADE_AA; + fts_i2c_write(client, buf, 2); + + msleep(20); + + buf[0] = 0x90; + buf[1] = buf[2] = buf[3] = 0x00; + fts_i2c_read(client, buf, 4, reg_val, 2); + + FTS_DEBUG("[UPGRADE] ROM/PRAM/Bootloader id:0x%02x%02x\n", + reg_val[0], reg_val[1]); + if ((reg_val[0] == 0x00) || (reg_val[0] == 0xFF)) + inRomBoot = FTS_RUN_IN_ERROR; + else if ((reg_val[0] == fts_chip_type_curr->pramboot_idh) && + (reg_val[1] == fts_chip_type_curr->pramboot_idl)) + inRomBoot = FTS_RUN_IN_PRAM; + else if ((reg_val[0] == fts_chip_type_curr->rom_idh) && + (reg_val[1] == fts_chip_type_curr->rom_idl)) + inRomBoot = FTS_RUN_IN_ROM; + else if ((reg_val[0] == fts_chip_type_curr->bootloader_idh) && + (reg_val[1] == fts_chip_type_curr->bootloader_idl)) + inRomBoot = FTS_RUN_IN_BOOTLOADER; + + return inRomBoot; +} + +/************************************************************************ +* Name: fts_ctpm_get_app_ver +* Brief: get app file version +* Input: +* Output: +* Return: fw version +***********************************************************************/ +int fts_ctpm_get_app_ver(void) +{ + int i_ret = 0; + + if (fts_updatefun_curr->get_app_bin_file_ver) + i_ret = fts_updatefun_curr->get_app_bin_file_ver + (fts_fw_name_curr); + + if (i_ret < 0) + i_ret = 0; + + return i_ret; +} + +/************************************************************************ +* Name: fts_ctpm_fw_upgrade +* Brief: fw upgrade entry funciotn +* Input: +* Output: +* Return: 0 - upgrade successfully +* <0 - upgrade failed +***********************************************************************/ +int fts_ctpm_fw_upgrade(struct i2c_client *client) +{ + int i_ret = 0; + + if (fts_updatefun_curr->upgrade_with_app_bin_file) + i_ret = fts_updatefun_curr->upgrade_with_app_bin_file(client, + fts_fw_name_curr); + + return i_ret; +} + +/************************************************************************ +* Name: fts_ctpm_check_fw_status +* Brief: Check App is valid or not +* Input: +* Output: +* Return: -EIO - I2C communication error +* FTS_RUN_IN_APP - APP valid +* 0 - APP invalid +***********************************************************************/ +static int fts_ctpm_check_fw_status(struct i2c_client *client) +{ + u8 chip_id1 = 0; + u8 chip_id2 = 0; + int fw_status = FTS_RUN_IN_ERROR; + int i = 0; + int ret = 0; + int i2c_noack_retry = 0; + + for (i = 0; i < 5; i++) { + ret = fts_i2c_read_reg(client, FTS_REG_CHIP_ID, &chip_id1); + if (ret < 0) { + i2c_noack_retry++; + continue; + } + ret = fts_i2c_read_reg(client, FTS_REG_CHIP_ID2, &chip_id2); + if (ret < 0) { + i2c_noack_retry++; + continue; + } + if (chip_id1 != fts_chip_type_curr->chip_idh) + continue; + if (FTS_CHIP_IDC(fts_chip_type_curr->chip_type) && + (chip_id2 != fts_chip_type_curr->chip_idl)) + continue; + fw_status = FTS_RUN_IN_APP; + break; + } + + FTS_DEBUG("[UPGRADE] chip_id=%02x%02x, chip_types.chip_idh=%02x%02x\n", + chip_id1, chip_id2, + fts_chip_type_curr->chip_idh, fts_chip_type_curr->chip_idl); + + /* I2C No ACK 5 times, then return -EIO */ + if (i2c_noack_retry >= 5) + return -EIO; + + /* not get correct ID, need check pram/rom/bootloader */ + if (i >= 5) + fw_status = fts_ctpm_get_pram_or_rom_id(client); + + return fw_status; +} + +/************************************************************************ +* Name: fts_ctpm_check_fw_ver +* Brief: Check vendor id is valid or not +* Input: +* Output: +* Return: 1 - vendor id valid +* 0 - vendor id invalid +***********************************************************************/ +static int fts_ctpm_check_fw_ver(struct i2c_client *client) +{ + /* FW version check is done in moto FW upgrade script */ + return 1; +} + +/************************************************************************ +* Name: fts_ctpm_check_need_upgrade +* Brief: +* Input: +* Output: +* Return: 1 - Need upgrade +* 0 - No upgrade +***********************************************************************/ +static int fts_ctpm_check_need_upgrade(struct i2c_client *client) +{ + int fw_status = 0; + int bUpgradeFlag = 0; + + FTS_FUNC_ENTER(); + + /* 1. veriry FW APP is valid or not */ + fw_status = fts_ctpm_check_fw_status(client); + FTS_DEBUG("[UPGRADE] fw_status = %d\n", fw_status); + if (fw_status < 0) { + /* I2C no ACK, return immediately */ + FTS_ERROR("[UPGRADE] I2C NO ACK,exit upgrade\n"); + return -EIO; + } else if (fw_status == FTS_RUN_IN_ERROR) + FTS_ERROR("[UPGRADE] IC Type Fail\n"); + else if (fw_status == FTS_RUN_IN_APP) { + if (fts_ctpm_check_fw_ver(client) == 1) { + FTS_INFO("[UPGRADE] need upgrade fw\n"); + bUpgradeFlag = 1; + } else { + FTS_INFO("[UPGRADE] Don't need upgrade fw\n"); + bUpgradeFlag = 0; + } + } else { + /* if app is invalid, reset to run ROM */ + FTS_INFO("[UPGRADE] FW APP invalid\n"); + fts_ctpm_rom_or_pram_reset(client); + + bUpgradeFlag = 1; + } + + FTS_FUNC_EXIT(); + + return bUpgradeFlag; +} + +/************************************************************************ +* Name: fts_ctpm_auto_upgrade +* Brief: auto upgrade +* Input: +* Output: +* Return: 0 - no upgrade +***********************************************************************/ +int fts_ctpm_auto_upgrade(struct i2c_client *client, + const char *fw_name, + const struct ft_ts_platform_data *pdata) +{ + int i_ret = 0; + int bUpgradeFlag = 0; + u8 uc_upgrade_times = 0; + + FTS_DEBUG("[UPGRADE] set update function and type by ID\n"); + + if (pdata->family_id == FT8716_ID) { + ft8716_set_upgrade_function(&fts_updatefun_curr); + ft8716_set_chip_id(&fts_chip_type_curr); + } + + if (pdata->family_id == FT5X46_ID) { + ft5x46_set_upgrade_function(&fts_updatefun_curr); + ft5x46_set_chip_id(&fts_chip_type_curr); + } + + if ((pdata->family_id == FT8006U_ID) || (pdata->family_id == FT5422U_ID)) { + return fts_fwupg_do_upgrade(fw_name); + } + + /* no IC upgrade function enabled, return error */ + if ((fts_updatefun_curr == NULL) || (fts_chip_type_curr == NULL)) { + FTS_ERROR("[UPGRADE] ID(0x%02x) not support\n", + pdata->family_id); + return -EINVAL; + } + fts_pdata_curr = pdata; + fts_fw_name_curr = fw_name; + + FTS_DEBUG("[UPGRADE] check upgrade need or not\n"); + bUpgradeFlag = fts_ctpm_check_need_upgrade(client); + FTS_DEBUG("[UPGRADE] bUpgradeFlag = 0x%x\n", bUpgradeFlag); + if (bUpgradeFlag <= 0) { + FTS_INFO("[UPGRADE] No Upgrade, exit(%d)\n", bUpgradeFlag); + return 0; + } + + /* FW Upgrade */ + do { + FTS_DEBUG("[UPGRADE] star upgrade(%d)\n", uc_upgrade_times); + + i_ret = fts_ctpm_fw_upgrade(client); + if (i_ret != 0) { + /* upgrade fail, reset to run ROM BOOT.. + * if app in flash is ok, TP will work success + */ + FTS_ERROR("[UPGRADE] upgrade fail, reset now\n"); + fts_ctpm_rom_or_pram_reset(client); + continue; + } +; FTS_INFO("[UPGRADE] Upgrade success, reset now\n"); + fts_ctpm_rom_or_pram_reset(client); + break; + } while (++uc_upgrade_times < 2); + + return i_ret; +} + +struct fts_upgrade g_upgrade; +struct fts_upgrade *fwupgrade; + +struct ft_chip_t ft8006u_fct = { + .type = 0x0B, + .chip_idh = 0xF0, + .chip_idl = 0x06, + .rom_idh = 0xF0, + .rom_idl = 0x06, + .pramboot_idh = 0xF0, + .pramboot_idl = 0xA6, + .bootloader_idh = 0x00, + .bootloader_idl = 0x00, + .chip_type = 0x8006D80B, +}; + +struct fts_ts_data g_fts_data; +struct fts_ts_data *fts_data; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +static u16 fts_pram_ecc_calc_host(u8 *pbuf, u16 length) +{ + u16 ecc = 0; + u16 i = 0; + u16 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_8006); + else + ecc >>= 1; + } + } + + return ecc; +} + +/************************************************************************ + * fts_pram_ecc_cal - Calculate and get pramboot ecc + * + * return pramboot ecc of tp if success, otherwise return error code + ***********************************************************************/ +static int fts_pram_ecc_cal_algo( + struct i2c_client *client, + u32 start_addr, + u32 ecc_length) +{ + int ret = 0; + int i = 0; + int ecc = 0; + u8 val[2] = { 0 }; + u8 cmd[FTS_ROMBOOT_CMD_ECC_NEW_LEN] = { 0 }; + + FTS_DEBUG("[UPGRADE]read out pramboot checksum\n"); + 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_i2c_write(client, cmd, FTS_ROMBOOT_CMD_ECC_NEW_LEN); + if (ret < 0) { + FTS_ERROR("[UPGRADE]write pramboot ecc cal cmd fail\n"); + return ret; + } + + cmd[0] = FTS_ROMBOOT_CMD_ECC_FINISH; + for (i = 0; i < 100; i++) { + msleep(1); + ret = fts_i2c_read(client, cmd, 1, val, 1); + if (ret < 0) { + FTS_ERROR("[UPGRADE]ecc_finish read cmd fail\n"); + return ret; + } + if (0 == val[0]) + break; + } + if (i >= 100) { + FTS_ERROR("[UPGRADE]wait ecc finish fail\n"); + return -EIO; + } + + cmd[0] = FTS_CMD_READ_ECC; + ret = fts_i2c_read(client, cmd, 1, val, 2); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read pramboot ecc fail\n"); + return ret; + } + + ecc = ((u16)(val[0] << 8) + val[1]) & 0x0000FFFF; + return ecc; +} + +static int fts_pram_ecc_cal_xor(struct i2c_client *client) +{ + int ret = 0; + u8 reg_val = 0; + + FTS_DEBUG("[UPGRADE]read out pramboot checksum\n"); + + ret = fts_i2c_read_reg(client, FTS_ROMBOOT_CMD_ECC, ®_val); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read pramboot ecc fail\n"); + return ret; + } + + return (int)reg_val; +} + +int fts_pram_ecc_cal(struct i2c_client *client, u32 saddr, u32 len) +{ + if ((NULL == fwupgrade) && (NULL == fwupgrade->func)) { + FTS_ERROR("[UPGRADE]fwupgrade/func is null\n"); + return -EINVAL; + } + + if (fwupgrade->func->newmode) { + return fts_pram_ecc_cal_algo(client, saddr, len); + } else { + return fts_pram_ecc_cal_xor(client); + } +} + +/************************************************************************ + * fts_pram_write_buf - write pramboot data and calculate ecc + * + * return pramboot ecc of host if success, otherwise return error code + ***********************************************************************/ +int fts_pram_write_buf(struct i2c_client *client, 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; + + FTS_DEBUG("[UPGRADE]write pramboot to pram\n"); + if ((NULL == fwupgrade) && (NULL == fwupgrade->func)) { + FTS_ERROR("[UPGRADE]fwupgrade/func is null\n"); + return -EINVAL; + } + + if (NULL == buf) { + FTS_ERROR("[UPGRADE]pramboot buf is null\n"); + return -EINVAL; + } + + FTS_DEBUG("[UPGRADE]pramboot len=%d\n", len); + if ((len < PRAMBOOT_MIN_SIZE) || (len > PRAMBOOT_MAX_SIZE)) { + FTS_ERROR("[UPGRADE]pramboot length(%d) fail\n", 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; + + packet_buf[0] = FTS_ROMBOOT_CMD_WRITE; + for (i = 0; i < packet_number; i++) { + offset = i * FTS_FLASH_PACKET_LENGTH; + packet_buf[1] = BYTE_OFF_16(offset); + packet_buf[2] = BYTE_OFF_8(offset); + packet_buf[3] = BYTE_OFF_0(offset); + + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + packet_buf[4] = BYTE_OFF_8(packet_len); + packet_buf[5] = BYTE_OFF_0(packet_len); + + for (j = 0; j < packet_len; j++) { + packet_buf[FTS_CMD_WRITE_LEN + j] = buf[offset + j]; + if (!fwupgrade->func->newmode) { + ecc_tmp ^= packet_buf[FTS_CMD_WRITE_LEN + j]; + } + } + + ret = fts_i2c_write(client, packet_buf, packet_len + FTS_CMD_WRITE_LEN); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pramboot write data(%d) fail\n", i); + return ret; + } + } + + if (fwupgrade->func->newmode) { + ecc_in_host = (int)fts_pram_ecc_calc_host(buf, len); + } else { + ecc_in_host = (int)ecc_tmp; + } + + return ecc_in_host; +} + +/************************************************************************ + * fts_pram_start - remap to start pramboot + * + * return 0 if success, otherwise return error code + ***********************************************************************/ +int fts_pram_start(struct i2c_client *client) +{ + u8 cmd = FTS_ROMBOOT_CMD_START_APP; + int ret = 0; + + FTS_DEBUG("[UPGRADE]remap to start pramboot\n"); + + ret = fts_i2c_write(client, &cmd, 1); + if (ret < 0) { + FTS_ERROR("[UPGRADE]write start pram cmd fail\n"); + return ret; + } + msleep(FTS_DELAY_PRAMBOOT_START); + + return 0; +} + +/************************************************************************ + * fts_pram_init - initialize pramboot + * + * return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_pram_init(struct i2c_client *client) +{ + int ret = 0; + u8 reg_val = 0; + u8 wbuf[3] = { 0 }; + + FTS_DEBUG("[UPGRADE]pramboot initialization\n"); + + /* read flash ID */ + wbuf[0] = FTS_CMD_FLASH_TYPE; + ret = fts_i2c_read(client, wbuf, 1, ®_val, 1); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read flash type fail\n"); + return ret; + } + + /* set flash clk */ + wbuf[0] = FTS_CMD_FLASH_TYPE; + wbuf[1] = reg_val; + wbuf[2] = 0x00; + ret = fts_i2c_write(client, wbuf, 3); + if (ret < 0) { + FTS_ERROR("[UPGRADE]write flash type fail\n"); + return ret; + } + + return 0; +} + +/************************************************************************ +* Name: fts_pram_write_init +* Brief: wirte pramboot to pram and initialize +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_pram_write_init(struct i2c_client *client) +{ + int ret = 0; + bool state = 0; + enum FW_STATUS status = FTS_RUN_IN_ERROR; + struct fts_upgrade *upg = fwupgrade; + + FTS_DEBUG("[UPGRADE]**********pram write and init**********\n"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("[UPGRADE]upgrade/func is null\n"); + return -EINVAL; + } + + if (!upg->func->pramboot_supported) { + FTS_ERROR("[UPGRADE]ic not support pram\n"); + return -EINVAL; + } + + FTS_DEBUG("[UPGRADE]check whether tp is in romboot or not \n"); + /* need reset to romboot when non-romboot state */ + ret = fts_fwupg_get_boot_state(client, &status); + if (status != FTS_RUN_IN_ROM) { + if (FTS_RUN_IN_PRAM == status) { + FTS_INFO("[UPGRADE]tp is in pramboot, need send reset cmd before upgrade\n"); + ret = fts_pram_init(client); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pramboot(before) init fail\n"); + return ret; + } + } + + FTS_INFO("[UPGRADE]tp isn't in romboot, need send reset to romboot\n"); + ret = fts_fwupg_reset_to_romboot(client); + if (ret < 0) { + FTS_ERROR("[UPGRADE]reset to romboot fail\n"); + return ret; + } + } + + /* check the length of the pramboot */ + if (upg->func->fts_8006u) + ret = fts_ft8006u_pram_write_remap(client); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pram write fail, ret=%d\n", ret); + return ret; + } + + FTS_DEBUG("[UPGRADE]after write pramboot, confirm run in pramboot\n"); + state = fts_fwupg_check_state(client, FTS_RUN_IN_PRAM); + if (!state) { + FTS_ERROR("[UPGRADE]not in pramboot\n"); + return -EIO; + } + + ret = fts_pram_init(client); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pramboot init fail\n"); + return ret; + } + + return 0; +} +/***************************************************************************** +* 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(struct i2c_client *client) +{ + int ret = 0; + int cnt = 0; + u8 reg_value = 0; + u8 chip_id = fts_data->ic_info.ids.chip_idh; + + do { + ret = fts_i2c_read_reg(client, FTS_REG_CHIP_ID, ®_value); + if ((ret < 0) || (reg_value != chip_id)) { + FTS_DEBUG("[UPGRADE]TP Not Ready, ReadData = 0x%x\n", reg_value); + FTS_DEBUG("[UPGRADE]TP Not Ready, chip_id = 0x%x\n", chip_id); + } else if (reg_value == chip_id) { + FTS_INFO("[UPGRADE]TP Ready, Device ID = 0x%x\n", reg_value); + return 0; + } + cnt++; + msleep(INTERVAL_READ_REG); + } while ((cnt * INTERVAL_READ_REG) < TIMEOUT_READ_REG); + + return -EIO; +} +/************************************************************************ +* Name: fts_fwupg_check_fw_valid +* Brief: check fw in tp is valid or not +* Input: +* Output: +* Return: return true if fw is valid, otherwise return false +***********************************************************************/ +bool fts_fwupg_check_fw_valid(struct i2c_client *client) +{ + int ret = 0; + + ret = fts_wait_tp_to_valid(client); + if (ret < 0) { + FTS_INFO("[UPGRADE]tp fw invaild\n"); + return false; + } + + FTS_DEBUG("[UPGRADE]tp fw vaild\n"); + return true; +} +/************************************************************************ +* HID to standard I2C +***********************************************************************/ +void fts_i2c_hid2std(struct i2c_client *client) +{ + int ret = 0; + u8 buf[3] = {0xeb, 0xaa, 0x09}; + + ret = fts_i2c_write(client, buf, 3); + if (ret < 0) + FTS_ERROR("[UPGRADE]hid2std cmd write fail\n"); + else { + msleep(10); + buf[0] = buf[1] = buf[2] = 0; + ret = fts_i2c_read(client, NULL, 0, buf, 3); + if (ret < 0) + FTS_ERROR("[UPGRADE]hid2std cmd read fail\n"); + else if ((0xeb == buf[0]) && (0xaa == buf[1]) && (0x08 == buf[2])) { + FTS_DEBUG("[UPGRADE]hidi2c change to stdi2c successful\n"); + } else { + FTS_ERROR("[UPGRADE]hidi2c change to stdi2c fail\n"); + } + } +} +/************************************************************************ +* 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 +***********************************************************************/ +int fts_fwupg_get_boot_state(struct i2c_client *client, 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 = fts_data->ic_info.ids; + struct fts_upgrade *upg = fwupgrade; + + FTS_DEBUG("[UPGRADE]**********read boot id**********\n"); + if ((NULL == fw_sts) || (NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("[UPGRADE]upgrade/func/fw_sts is null\n"); + return -EINVAL; + } + + if (upg->func->hid_supported) + fts_i2c_hid2std(client); + + cmd[0] = FTS_CMD_START1; + cmd[1] = FTS_CMD_START2; + ret = fts_i2c_write(client, cmd, 2); + if (ret < 0) { + FTS_ERROR("[UPGRADE]write 55 aa cmd fail\n"); + 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) + cmd_len = FTS_CMD_READ_ID_LEN_INCELL; + else + cmd_len = FTS_CMD_READ_ID_LEN; + ret = fts_i2c_read(client, cmd, cmd_len, val, 2); + if (ret < 0) { + FTS_ERROR("[UPGRADE]write 90 cmd fail\n"); + return ret; + } + + FTS_DEBUG("[UPGRADE]read boot id:0x%02x%02x\n", val[0], val[1]); + if ((val[0] == ids.rom_idh) && (val[1] == ids.rom_idl)) { + FTS_INFO("[UPGRADE]tp run in romboot\n"); + *fw_sts = FTS_RUN_IN_ROM; + } else if ((val[0] == ids.pramboot_idh) && (val[1] == ids.pramboot_idl)) { + FTS_INFO("[UPGRADE]tp run in pramboot\n"); + *fw_sts = FTS_RUN_IN_PRAM; + } else if ((val[0] == ids.bootloader_idh) && (val[1] == ids.bootloader_idl)) { + FTS_INFO("[UPGRADE]tp run in bootloader\n"); + *fw_sts = FTS_RUN_IN_BOOTLOADER; + } + + return 0; +} + +/************************************************************************ +* Name: fts_fwupg_check_state +* Brief: confirm tp run in romboot/pramboot/bootloader +* Input: +* Output: +* Return: return true if state is match, otherwise return false +***********************************************************************/ +bool fts_fwupg_check_state(struct i2c_client *client, 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(client, &cstate); + 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(struct i2c_client *client) +{ + int ret = 0; + u8 cmd = FTS_CMD_RESET; + + FTS_DEBUG("[UPGRADE]reset in boot environment\n"); + ret = fts_i2c_write(client, &cmd, 1); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pram/rom/bootloader reset cmd write fail\n"); + return ret; + } + + msleep(FTS_DELAY_UPGRADE_RESET); + return 0; +} + +/************************************************************************ +* Name: fts_fwupg_reset_to_boot +* Brief: reset to boot environment +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_fwupg_reset_to_boot(struct i2c_client *client) +{ + int ret = 0; + + FTS_DEBUG("[UPGRADE]send 0xAA and 0x55 to FW, reset to boot environment\n"); + + ret = fts_i2c_write_reg(client, FTS_REG_UPGRADE, FTS_UPGRADE_AA); + if (ret < 0) { + FTS_ERROR("[UPGRADE]write FC=0xAA fail\n"); + return ret; + } + msleep(FTS_DELAY_FC_AA); + + ret = fts_i2c_write_reg(client, FTS_REG_UPGRADE, FTS_UPGRADE_55); + if (ret < 0) { + FTS_ERROR("[UPGRADE]write FC=0x55 fail\n"); + 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 +***********************************************************************/ +int fts_fwupg_reset_to_romboot(struct i2c_client *client) +{ + int ret = 0; + int i = 0; + u8 cmd = FTS_CMD_RESET; + enum FW_STATUS state = FTS_RUN_IN_ERROR; + + ret = fts_i2c_write(client, &cmd, 1); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pram/rom/bootloader reset cmd write fail\n"); + return ret; + } + mdelay(10); + + for (i = 0; i < FTS_UPGRADE_LOOP; i++) { + ret = fts_fwupg_get_boot_state(client, &state); + if (FTS_RUN_IN_ROM == state) + break; + mdelay(5); + } + if (i >= FTS_UPGRADE_LOOP) { + FTS_ERROR("[UPGRADE]reset to romboot fail\n"); + return -EIO; + } + + return 0; +} + +/************************************************************************ +* Name: fts_fwupg_enter_into_boot +* Brief: enter into boot environment, ready for upgrade +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_fwupg_enter_into_boot(struct i2c_client *client) +{ + int ret = 0; + bool fwvalid = false; + bool state = false; + struct fts_upgrade *upg = fwupgrade; + + FTS_DEBUG("[UPGRADE]***********enter into pramboot/bootloader***********\n"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("[UPGRADE]upgrade/func is null\n"); + return -EINVAL; + } + + fwvalid = fts_fwupg_check_fw_valid(client); + if (fwvalid) { + ret = fts_fwupg_reset_to_boot(client); + if (ret < 0) { + FTS_ERROR("[UPGRADE]enter into romboot/bootloader fail\n"); + return ret; + } + } + + if (upg->func->pramboot_supported) { + FTS_DEBUG("[UPGRADE]pram supported, write pramboot and init\n"); + /* pramboot */ + ret = fts_pram_write_init(client); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pram write_init fail\n"); + return ret; + } + } else { + FTS_DEBUG("[UPGRADE]pram not supported, confirm in bootloader\n"); + /* bootloader */ + state = fts_fwupg_check_state(client, FTS_RUN_IN_BOOTLOADER); + if (!state) { + FTS_ERROR("[UPGRADE]fw not in bootloader, fail\n"); + return -EIO; + } + } + + return 0; +} + +/************************************************************************ + * 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( + struct i2c_client *client, + 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_i2c_read(client, &cmd , 1, val, FTS_CMD_FLASH_STATUS_LEN); + read_status = (((u16)val[0]) << 8) + val[1]; + if (flash_status == read_status) + return true; + 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(struct i2c_client *client, u32 delay) +{ + int ret = 0; + u8 cmd = 0; + bool flag = false; + + FTS_DEBUG("[UPGRADE]**********erase now**********\n"); + + /*send to erase flash*/ + cmd = FTS_CMD_ERASE_APP; + ret = fts_i2c_write(client, &cmd, 1); + if (ret < 0) { + FTS_ERROR("[UPGRADE]erase cmd fail\n"); + return ret; + } + msleep(delay); + + /* read status 0xF0AA: success */ + flag = fts_fwupg_check_flash_status(client, FTS_CMD_FLASH_STATUS_ERASE_OK, + FTS_RETRIES_REASE, FTS_RETRIES_DELAY_REASE); + if (!flag) { + FTS_ERROR("[UPGRADE]ecc flash status check fail\n"); + 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(struct i2c_client *client, u32 saddr, u32 len) +{ + int ret = 0; + u32 i = 0; + u8 wbuf[FTS_CMD_ECC_CAL_LEN] = { 0 }; + u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 }; + u32 packet_num = 0; + u32 packet_len = 0; + u32 remainder = 0; + u32 addr = 0; + u32 offset = 0; + + FTS_DEBUG( "**********read out checksum**********"); + + /* check sum init */ + wbuf[0] = FTS_CMD_ECC_INIT; + ret = fts_i2c_write(client, wbuf, 1); + if (ret < 0) { + FTS_ERROR("[UPGRADE]ecc init cmd write fail\n"); + return ret; + } + + 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_DEBUG("[UPGRADE]ecc calc num:%d, remainder:%d\n", 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 ((i == (packet_num - 1)) && remainder) + packet_len = remainder; + wbuf[4] = BYTE_OFF_8(packet_len); + wbuf[5] = BYTE_OFF_0(packet_len); + + FTS_DEBUG("[UPGRADE]ecc calc startaddr:0x%04x, len:%d\n", addr, packet_len); + ret = fts_i2c_write(client, wbuf, FTS_CMD_ECC_CAL_LEN); + if (ret < 0) { + FTS_ERROR("[UPGRADE]ecc calc cmd write fail\n"); + return ret; + } + + msleep(packet_len / 256); + + /* read status if check sum is finished */ + ret = fts_fwupg_check_flash_status(client, FTS_CMD_FLASH_STATUS_ECC_OK, + FTS_RETRIES_ECC_CAL, FTS_RETRIES_DELAY_ECC_CAL); + if (ret < 0) { + FTS_ERROR("[UPGRADE]ecc flash status read fail\n"); + return ret; + } + } + + /* read out check sum */ + wbuf[0] = FTS_CMD_ECC_READ; + ret = fts_i2c_read(client, wbuf, 1, val, 1); + if (ret < 0) { + FTS_ERROR( "ecc read cmd write fail"); + return ret; + } + + return val[0]; +} + +/************************************************************************ + * 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( + struct i2c_client *client, + 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; + u8 packet_buf[FTS_FLASH_PACKET_LENGTH + FTS_CMD_WRITE_LEN] = { 0 }; + u8 ecc_in_host = 0; + u8 cmd = 0; + u8 val[FTS_CMD_FLASH_STATUS_LEN] = { 0 }; + u16 read_status = 0; + u16 wr_ok = 0; + + FTS_DEBUG( "**********write data to flash**********"); + + if ((NULL == buf) || (0 == len)) { + FTS_ERROR("[UPGRADE]buf is NULL or len is 0\n"); + return -EINVAL; + } + + FTS_DEBUG("[UPGRADE]data buf start addr=0x%x, len=0x%x\n", 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_DEBUG("[UPGRADE]write data, num:%d remainder:%d\n", packet_number, remainder); + + packet_buf[0] = FTS_CMD_WRITE; + for (i = 0; i < packet_number; i++) { + offset = i * FTS_FLASH_PACKET_LENGTH; + addr = saddr + offset; + packet_buf[1] = BYTE_OFF_16(addr); + packet_buf[2] = BYTE_OFF_8(addr); + packet_buf[3] = BYTE_OFF_0(addr); + + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + packet_buf[4] = BYTE_OFF_8(packet_len); + packet_buf[5] = BYTE_OFF_0(packet_len); + + for (j = 0; j < packet_len; j++) { + packet_buf[FTS_CMD_WRITE_LEN + j] = buf[offset + j]; + ecc_in_host ^= packet_buf[FTS_CMD_WRITE_LEN + j]; + } + + ret = fts_i2c_write(client, packet_buf, packet_len + FTS_CMD_WRITE_LEN); + if (ret < 0) { + FTS_ERROR("[UPGRADE]app write fail\n"); + 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_i2c_read(client, &cmd , 1, val, FTS_CMD_FLASH_STATUS_LEN); + read_status = (((u16)val[0]) << 8) + val[1]; + if (wr_ok == read_status) { + break; + } + mdelay(FTS_RETRIES_DELAY_WRITE); + } + } + + return (int)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 + ***********************************************************************/ +int fts_flash_read_buf(struct i2c_client *client, 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]; + + if ((NULL == buf) || (0 == len)) { + FTS_ERROR("[UPGRADE]buf is NULL or len is 0\n"); + 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_DEBUG("[UPGRADE]read packet_number:%d, remainder:%d\n", packet_number, remainder); + + wbuf[0] = FTS_CMD_READ; + for (i = 0; i < packet_number; i++) { + offset = i * FTS_FLASH_PACKET_LENGTH; + addr = saddr + offset; + wbuf[1] = BYTE_OFF_16(addr); + wbuf[2] = BYTE_OFF_8(addr); + wbuf[3] = BYTE_OFF_0(addr); + + /* last packet */ + if ((i == (packet_number - 1)) && remainder) + packet_len = remainder; + + ret = fts_i2c_write(client, wbuf, FTS_CMD_READ_LEN); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pram/bootloader write 03 command fail\n"); + return ret; + } + + msleep(FTS_CMD_READ_DELAY); /* must wait, otherwise read wrong data */ + ret = fts_i2c_read(client, NULL, 0, buf + offset, packet_len); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pram/bootloader read 03 command fail\n"); + 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 + ***********************************************************************/ +int fts_flash_read(struct i2c_client *client, u32 addr, u8 *buf, u32 len) +{ + int ret = 0; + + FTS_DEBUG("[UPGRADE]***********read flash***********\n"); + + if ((NULL == buf) || (0 == len)) { + FTS_ERROR("[UPGRADE]buf is NULL or len is 0\n"); + return -EINVAL; + } + + ret = fts_fwupg_enter_into_boot(client); + if (ret < 0) { + FTS_ERROR("[UPGRADE]enter into pramboot/bootloader fail\n"); + goto read_flash_err; + } + + ret = fts_flash_read_buf(client, addr, buf, len); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read flash fail\n"); + goto read_flash_err; + } + +read_flash_err: + /* reset to normal boot */ + ret = fts_fwupg_reset_in_boot(client); + if (ret < 0) + FTS_ERROR("[UPGRADE]reset to normal boot fail\n"); + return ret; +} + +/************************************************************************ + * Name: fts_read_file + * Brief: read file + * Input: file name + * Output: + * Return: return file len if succuss, otherwise return error code + ***********************************************************************/ +int fts_read_file(char *file_name, u8 **file_buf) +{ + int ret = 0; + char file_path[FILE_NAME_LENGTH] = { 0 }; + struct file *filp = NULL; + struct inode *inode; + mm_segment_t old_fs; + loff_t pos; + loff_t file_len = 0; + + if ((NULL == file_name) || (NULL == file_buf)) { + FTS_ERROR("[UPGRADE]filename/filebuf is NULL\n"); + return -EINVAL; + } + + snprintf(file_path, FILE_NAME_LENGTH, "%s%s", FTS_FW_BIN_FILEPATH, file_name); + filp = filp_open(file_path, O_RDONLY, 0); + if (IS_ERR(filp)) { + FTS_ERROR("[UPGRADE]open %s file fail\n", file_path); + return -ENOENT; + } + + inode = filp->f_inode; + file_len = inode->i_size; + *file_buf = (u8 *)vmalloc(file_len); + if (NULL == *file_buf) { + FTS_ERROR("[UPGRADE]file buf malloc fail\n"); + filp_close(filp, NULL); + return -ENOMEM; + } + old_fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + ret = vfs_read(filp, *file_buf, file_len , &pos); + if (ret < 0) + FTS_ERROR("[UPGRADE]read file fail\n"); + FTS_DEBUG("[UPGRADE]file len:%x read len:%x pos:%x\n", (u32)file_len, ret, (u32)pos); + filp_close(filp, NULL); + set_fs(old_fs); + + return ret; +} +#define FTS_USE_FIRMWARE 1 +/************************************************************************ +* Name: fts_upgrade_bin +* Brief: +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_upgrade_bin(struct i2c_client *client, char *fw_name, bool force) +{ + int ret = 0; + u32 fw_file_len = 0; + u8 *fw_file_buf; + struct fts_upgrade *upg = fwupgrade; +#if FTS_USE_FIRMWARE + const struct firmware *fw; +#endif + + FTS_DEBUG("[UPGRADE]start upgrade with fw bin\n"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("[UPGRADE]upgrade/func is null\n"); + return -EINVAL; + } + +#if FTS_USE_FIRMWARE + ret = request_firmware(&fw, fw_name, &client->dev); + if (ret < 0) { + FTS_ERROR("[UPGRADE] Request firmware failed - %s (%d)\n", + fw_name, ret); + return ret; + } + fw_file_buf = (u8*)fw->data; + fw_file_len = (u32)fw->size; +#else + ret = fts_read_file(fw_name, &fw_file_buf); + if ((ret < 0) || (ret < FTS_MIN_LEN) || (ret > FTS_MAX_LEN_FILE)) { + FTS_ERROR("[UPGRADE]read fw bin file(sdcard) fail, len:%d\n", ret); + goto err_bin; + } + fw_file_len = ret; +#endif + FTS_DEBUG("[UPGRADE]fw bin file len:%x\n", fw_file_len); + if (force) { + if (upg->func->force_upgrade) { + ret = upg->func->force_upgrade(client, fw_file_buf, fw_file_len); + } else { + FTS_INFO("[UPGRADE]force_upgrade function is null, no upgrade\n"); + goto err_bin; + } + } else { + if (FTS_AUTO_LIC_UPGRADE_EN) { + if (upg->func->lic_upgrade) + ret = upg->func->lic_upgrade(client, fw_file_buf, fw_file_len); + else + FTS_INFO("[UPGRADE]lic_upgrade function is null, no upgrade\n"); + } + if (upg->func->upgrade) + ret = upg->func->upgrade(client, fw_file_buf, fw_file_len); + else + FTS_INFO("[UPGRADE]upgrade function is null, no upgrade\n"); + } + + if (ret < 0) { + FTS_ERROR("[UPGRADE]upgrade fw bin failed\n"); + fts_fwupg_reset_in_boot(client); + goto err_bin; + } + + FTS_INFO("[UPGRADE]upgrade fw bin success\n"); + +err_bin: +#if FTS_USE_FIRMWARE + release_firmware(fw); +#else + if (fw_file_buf) { + vfree(fw_file_buf); + fw_file_buf = NULL; + } +#endif + return ret; +} + +static int fts_lic_get_vid_in_tp(struct i2c_client *client, u16 *vid) +{ + int ret = 0; + u8 val[2] = { 0 }; + + if (NULL == vid) { + FTS_ERROR("[UPGRADE]vid is NULL\n"); + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, FTS_REG_VENDOR_ID, &val[0]); + if (fts_data->ic_info.is_incell) + ret = fts_i2c_read_reg(client, FTS_REG_MODULE_ID, &val[1]); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read vid from tp fail\n"); + return ret; + } + + *vid = *(u16 *)val; + return 0; +} + +static int fts_lic_get_vid_in_host(u16 *vid) +{ + u8 val[2] = { 0 }; + u8 *licbuf = NULL; + u32 conf_saddr = 0; + struct fts_upgrade *upg = fwupgrade; + + if (!upg || !upg->func || !upg->lic || !vid) { + FTS_ERROR("[UPGRADE]upgrade/func/get_hlic_ver/lic/vid is null\n"); + return -EINVAL; + } + + if (upg->lic_length < FTS_MAX_LEN_SECTOR) { + FTS_ERROR("[UPGRADE]lic length(%x) fail\n", 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; +} + +int fts_lic_get_ver_in_tp(struct i2c_client *client, u8 *ver) +{ + int ret = 0; + + if (NULL == ver) { + FTS_ERROR("[UPGRADE]ver is NULL\n"); + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, FTS_REG_LIC_VER, ver); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read lcd initcode ver from tp fail\n"); + return ret; + } + + return 0; +} + +static int fts_lic_get_ver_in_host(u8 *ver) +{ + int ret = 0; + struct fts_upgrade *upg = fwupgrade; + + if (!upg || !upg->func || !upg->func->get_hlic_ver || !upg->lic) { + FTS_ERROR("[UPGRADE]upgrade/func/get_hlic_ver/lic is null\n"); + return -EINVAL; + } + + ret = upg->func->get_hlic_ver(upg->lic); + if (ret < 0) { + FTS_ERROR("[UPGRADE]get host lcd initial code version fail\n"); + return ret; + } + + *ver = (u8)ret; + return ret; +} + +/* check if lcd init code need upgrade +* true-need false-no need +*/ +static bool fts_lic_need_upgrade(struct i2c_client *client) +{ + 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(client); + if ( !fwvalid) { + FTS_INFO("[UPGRADE]fw is invalid, no upgrade lcd init code\n"); + return false; + } + + ret = fts_lic_get_vid_in_host(&vid_in_host); + if (ret < 0) { + FTS_ERROR("[UPGRADE]vendor id in host invalid\n"); + } + + ret = fts_lic_get_vid_in_tp(client, &vid_in_tp); + if (ret < 0) { + FTS_ERROR("[UPGRADE]vendor id in tp invalid\n"); + } + + FTS_DEBUG("[UPGRADE]vid in tp:%x, host:%x\n", vid_in_tp, vid_in_host); + if (vid_in_tp != vid_in_host) { + FTS_INFO("[UPGRADE]vendor id in tp&host are different, no upgrade lic\n"); + } + + ret = fts_lic_get_ver_in_host(&initcode_ver_in_host); + if (ret < 0) { + FTS_ERROR("[UPGRADE]init code in host invalid\n"); + return false; + } + + ret = fts_lic_get_ver_in_tp(client, &initcode_ver_in_tp); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read reg0xE4 fail\n"); + return false; + } + + FTS_DEBUG("[UPGRADE]lcd initial code version in tp:%x, host:%x\n", + initcode_ver_in_tp, initcode_ver_in_host); + if (0xA5 == initcode_ver_in_tp) { + FTS_INFO("[UPGRADE]lcd init code ver is 0xA5, don't upgade init code\n"); + return false; + } else if (0xFF == initcode_ver_in_tp) { + FTS_DEBUG("[UPGRADE]lcd init code in tp is invalid, need upgrade init code\n"); + return true; + } else if (initcode_ver_in_tp < initcode_ver_in_host) + return true; + else + return false; +} + +int fts_lic_upgrade(struct i2c_client *client, struct fts_upgrade *upg) +{ + int ret = 0; + bool hlic_upgrade = false; + int upgrade_count = 0; + u8 ver = 0; + + FTS_DEBUG("[UPGRADE]lcd initial code auto upgrade function\n"); + if ((!upg) || (!upg->func) || (!upg->func->lic_upgrade)) { + FTS_ERROR("[UPGRADE]lcd upgrade function is null\n"); + return -EINVAL; + } + + hlic_upgrade = fts_lic_need_upgrade(client); + FTS_DEBUG("[UPGRADE]lcd init code upgrade flag:%d\n", hlic_upgrade); + if (hlic_upgrade) { + FTS_INFO("[UPGRADE]lcd initial code need upgrade, upgrade begin...\n"); + do { + FTS_INFO("[UPGRADE]lcd initial code upgrade times:%d\n", upgrade_count); + upgrade_count++; + + ret = upg->func->lic_upgrade(client, upg->lic, upg->lic_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(client); + } else { + fts_lic_get_ver_in_tp(client, &ver); + FTS_INFO("[UPGRADE]success upgrade to lcd initcode ver:%02x\n", ver); + break; + } + } while (upgrade_count < 2); + } else { + FTS_INFO("[UPGRADE]lcd initial code don't need upgrade\n"); + } + + return ret; +} + + +static int fts_param_get_ver_in_tp(struct i2c_client *client, u8 *ver) +{ + int ret = 0; + + if (NULL == ver) { + FTS_ERROR("[UPGRADE]ver is NULL\n"); + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, FTS_REG_IDE_PARA_VER_ID, ver); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read fw param ver from tp fail\n"); + return ret; + } + + if ((0x00 == *ver) || (0xFF == *ver)) { + FTS_INFO("[UPGRADE]param version in tp invalid\n"); + return -EIO; + } + + return 0; +} + +static int fts_param_get_ver_in_host(u8 *ver) +{ + struct fts_upgrade *upg = fwupgrade; + + if ((!upg) || (!upg->func) || (!upg->fw) || (!ver)) { + FTS_ERROR("[UPGRADE]fts_data/upgrade/func/fw/ver is NULL\n"); + return -EINVAL; + } + + if (upg->fw_length < upg->func->paramcfgveroff) { + FTS_ERROR("[UPGRADE]fw len(%x) < paramcfg ver offset(%x)\n", + upg->fw_length, upg->func->paramcfgveroff); + return -EINVAL; + } + + FTS_DEBUG("[UPGRADE]fw paramcfg version offset:%x\n", upg->func->paramcfgveroff); + *ver = upg->fw[upg->func->paramcfgveroff]; + + if ((0x00 == *ver) || (0xFF == *ver)) { + FTS_INFO("[UPGRADE]param version in host invalid\n"); + return -EIO; + } + + return 0; +} + +/************************************************************************ + * fts_param_need_upgrade - check fw paramcfg need upgrade or not + * + * Return: return true if paramcfg need upgrade + ***********************************************************************/ +static bool fts_param_need_upgrade(struct i2c_client *client) +{ + int ret = 0; + u8 val = 0; + u8 ver_in_host = 0; + u8 ver_in_tp = 0; + bool fwvalid = false; + + fwvalid = fts_fwupg_check_fw_valid(client); + if ( !fwvalid) { + FTS_INFO("[UPGRADE]fw is invalid, no upgrade paramcfg\n"); + return false; + } + + ret = fts_param_get_ver_in_host(&ver_in_host); + if (ret < 0) { + FTS_ERROR("[UPGRADE]param version in host invalid\n"); + return false; + } + + ret = fts_i2c_read_reg(client, FTS_REG_IDE_PARA_STATUS, &val); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read IDE PARAM STATUS in tp fail\n"); + return false; + } + + if ((val & 0x80) != 0x80) { + FTS_INFO("[UPGRADE]no IDE VER in tp\n"); + return false; + } else if ((val & 0x7F) != 0x00) { + FTS_INFO("[UPGRADE]IDE VER, param invalid, need upgrade param\n"); + return true; + } + + ret = fts_param_get_ver_in_tp(client, &ver_in_tp); + if (ret < 0) { + FTS_ERROR("[UPGRADE]get IDE param ver in tp fail\n"); + return false; + } + + FTS_INFO("[UPGRADE]fw paramcfg version in tp:%x, host:%x\n", ver_in_tp, ver_in_host); + if (ver_in_tp < ver_in_host) + return true; + + return false; +} + +/************************************************************************ + * fts_fwupg_get_ver_in_tp - read fw ver from tp register + * + * return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_fwupg_get_ver_in_tp(struct i2c_client *client, u8 *ver) +{ + int ret = 0; + + if (NULL == ver) { + FTS_ERROR("[UPGRADE]ver is NULL\n"); + return -EINVAL; + } + + ret = fts_i2c_read_reg(client, FTS_REG_FW_VER, ver); + if (ret < 0) { + FTS_ERROR("[UPGRADE]read fw ver from tp fail\n"); + return ret; + } + + return 0; +} + +/************************************************************************ + * fts_fwupg_get_ver_in_host - read fw ver in host fw image + * + * return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_fwupg_get_ver_in_host(u8 *ver) +{ + struct fts_upgrade *upg = fwupgrade; + + if ((!upg) || (!upg->func) || (!upg->fw) || (!ver)) { + FTS_ERROR("[UPGRADE]fts_data/upgrade/func/fw/ver is NULL\n"); + return -EINVAL; + } + + if (upg->fw_length < upg->func->fwveroff) { + FTS_ERROR("[UPGRADE]fw len(%x) < fw ver offset(%x)\n", + upg->fw_length, upg->func->fwveroff); + return -EINVAL; + } + + FTS_DEBUG("[UPGRADE]fw version offset:%x\n", upg->func->fwveroff); + *ver = upg->fw[upg->func->fwveroff]; + return 0; +} + +/************************************************************************ + * fts_fwupg_need_upgrade - check fw need upgrade or not + * + * Return: return true if fw need upgrade + ***********************************************************************/ +static bool fts_fwupg_need_upgrade(struct i2c_client *client) +{ + int ret = 0; + bool fwvalid = false; + u8 fw_ver_in_host = 0; + u8 fw_ver_in_tp = 0; + + fwvalid = fts_fwupg_check_fw_valid(client); + if (fwvalid) { + ret = fts_fwupg_get_ver_in_host(&fw_ver_in_host); + if (ret < 0) { + FTS_ERROR("[UPGRADE]get fw ver in host fail\n"); + return false; + } + + ret = fts_fwupg_get_ver_in_tp(client, &fw_ver_in_tp); + if (ret < 0) { + FTS_ERROR("[UPGRADE]get fw ver in tp fail\n"); + return false; + } + + FTS_INFO("[UPGRADE]fw version in tp:%x, host:%x\n", fw_ver_in_tp, fw_ver_in_host); + if (fw_ver_in_tp < fw_ver_in_host) + return true; + } else { + FTS_INFO("[UPGRADE]fw invalid, need upgrade fw\n"); + return true; + } + + return false; +} + +/************************************************************************ + * Name: fts_fw_upgrade + * Brief: fw upgrade main entry + * Input: + * Output: + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +int fts_fwupg_upgrade(struct i2c_client *client, struct fts_upgrade *upg) +{ + int ret = 0; + bool upgrade_flag = false; + int upgrade_count = 0; + u8 ver = 0; + + FTS_DEBUG("[UPGRADE]fw auto upgrade function\n"); + if ((NULL == upg) || (NULL == upg->func)) { + FTS_ERROR("[UPGRADE]upg/upg->func is null\n"); + return -EINVAL; + } + + upgrade_flag = fts_fwupg_need_upgrade(client); + FTS_DEBUG("[UPGRADE]fw upgrade flag:%d\n", upgrade_flag); + do { + upgrade_count++; + if (upgrade_flag) { + FTS_INFO("[UPGRADE]upgrade fw app(times:%d)\n", upgrade_count); + if (upg->func->upgrade) { + ret = upg->func->upgrade(client, upg->fw, upg->fw_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(client); + } else { + fts_fwupg_get_ver_in_tp(client, &ver); + FTS_INFO("[UPGRADE]success upgrade to fw version %02x\n", ver); + break; + } + } else { + FTS_ERROR("[UPGRADE]upgrade func/upgrade is null, return immediately\n"); + ret = -ENODATA; + break; + } + } else { + FTS_INFO("[UPGRADE]fw don't need upgrade\n"); + if (upg->func->param_upgrade) { + if (fts_param_need_upgrade(client)) { + FTS_INFO("[UPGRADE]upgrade param area(times:%d)\n", upgrade_count); + ret = upg->func->param_upgrade(client, upg->fw, upg->fw_length); + if (ret < 0) { + fts_fwupg_reset_in_boot(client); + } else { + fts_param_get_ver_in_tp(client, &ver); + FTS_INFO("[UPGRADE]success upgrade to fw param version %02x\n", ver); + break; + } + } else { + FTS_INFO("[UPGRADE]param don't need upgrade\n"); + break; + } + } else + break; + } + } while (upgrade_count < 2); + + return ret; +} +bool FTS_AUTO_LIC_UPGRADE_EN = false; + +/* + * fts_fwupg_init_ic_detail - for ic detail initialaztion + */ +static void fts_fwupg_init_ic_detail(void) +{ + struct fts_upgrade *upg = fwupgrade; + + if (upg && upg->func && upg->func->init) + upg->func->init(); +} +/* + * fts_fwupg_do_upgrade - fw upgrade work function + * 1. get fw image/file + * 2. call upgrade main function(fts_fwupg_auto_upgrade) + */ +int fts_fwupg_do_upgrade(const char *fwname) +{ + struct fts_ts_data *ts_data = fts_data; + fts_fwupg_init_ic_detail(); + return fts_upgrade_bin(ts_data->client, (char*)fwname, false); +} + +int fts_extra_exit(void) +{ + return 0; +} + +int fts_extra_init(struct i2c_client *client, struct input_dev *input_dev, struct ft_ts_platform_data *pdata) +{ + int type = pdata->family_id; + + fts_data = &g_fts_data; + fwupgrade = &g_upgrade; + fts_data->client = client; + fts_data->input_dev = input_dev; + fts_data->pdata = pdata; + + if (FT8006U_ID == type) { + FTS_AUTO_LIC_UPGRADE_EN = true; + fwupgrade->func = &upgrade_func_ft8006u; + fts_data->ic_info.ids = ft8006u_fct; + fts_data->ic_info.is_incell = true; + fts_data->ic_info.hid_supported = false; + } else if (FT5422U_ID == type) { + fwupgrade->func = &upgrade_func_ft5422u; + fts_data->ic_info.ids = ft5422u_fct; + fts_data->ic_info.is_incell = false; + fts_data->ic_info.hid_supported = true; + } + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_mmi/focaltech_flash.h b/drivers/input/touchscreen/focaltech_mmi/focaltech_flash.h new file mode 100644 index 000000000000..4b306841d763 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/focaltech_flash.h @@ -0,0 +1,234 @@ +#ifndef __LINUX_FOCALTECH_FLASH_H__ +#define __LINUX_FOCALTECH_FLASH_H__ + +#include +#include +#include +#include "focaltech_upgrade_common.h" + +extern int fts_ctpm_auto_upgrade(struct i2c_client *client, + const char *fw_name, + const struct ft_ts_platform_data *pdata); + +int fts_ctpm_i2c_hid2std(struct i2c_client *client); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +extern bool FTS_AUTO_LIC_UPGRADE_EN; + +struct ts_ic_info { + bool is_incell; + bool hid_supported; + struct ft_chip_t ids; +}; + +struct fts_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct ts_ic_info ic_info; + struct workqueue_struct *ts_workqueue; + struct delayed_work esdcheck_work; + struct ft_ts_platform_data *pdata; +}; +extern struct fts_ts_data *fts_data; + +#define INTERVAL_READ_REG 100 /* unit:ms */ +#define TIMEOUT_READ_REG 1000 /* unit:ms */ +#define FTS_GET_VENDOR_ID_NUM 0 + +#define FTS_REG_LIC_VER 0xE4 +#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 FTS_CMD_START1 0x55 +#define FTS_CMD_START2 0xAA +#define FTS_CMD_READ_ID 0x90 +#define INTERVAL_READ_REG 100 //unit:ms +#define TIMEOUT_READ_REG 1000 //unit:ms + +#define FTS_REG_IDE_PARA_VER_ID 0xB5 +#define FTS_REG_IDE_PARA_STATUS 0xB6 +#define FTS_REG_VENDOR_ID 0xA8 +#define FTS_REG_MODULE_ID 0xE3 + +#define FTS_CMD_RESET 0x07 +#define FTS_ROMBOOT_CMD_WRITE 0xAE +#define FTS_ROMBOOT_CMD_START_APP 0x08 +#define FTS_DELAY_PRAMBOOT_START 10 +#define FTS_ROMBOOT_CMD_ECC 0xCC + +#define FTS_CMD_READ 0x03 +#define FTS_CMD_READ_DELAY 1 +#define FTS_CMD_READ_LEN 4 +#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 200 +#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 6 +#define FTS_RETRIES_ECC_CAL 10 +#define FTS_RETRIES_DELAY_ECC_CAL 50 +#define FTS_CMD_ECC_READ 0x66 +#define FTS_CMD_DATA_LEN 0xB0 +#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_UPGRADE_AA 0xAA +#define FTS_UPGRADE_55 0x55 +#define FTS_DELAY_FC_AA 10 +#define FTS_UPGRADE_LOOP 30 +#define FTS_HEADER_LEN 32 +#define FTS_FW_BIN_FILEPATH "/sdcard/" + +#define FTX_MAX_COMPATIBLE_TYPE 4 + +#define FTS_ROMBOOT_CMD_ECC_NEW_LEN 7 +#define FTS_ROMBOOT_CMD_ECC_FINISH 0xCE +#define FTS_CMD_READ_ECC 0xCD +#define AL2_FCS_COEF_8006 ((1 << 15) + (1 << 10) + (1 << 3)) + +#define I2C_BUFFER_LENGTH_MAXINUM 256 +#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 10 +#define FTS_CMD_READ_ID 0x90 +#define FTS_CMD_READ_ID_LEN 4 +#define FTS_CMD_READ_ID_LEN_INCELL 1 + +#define FTS_REG_ESD_SATURATE 0xED +#define FTS_REG_ESDCHECK_DISABLE 0x8D + +#define FTS_SYSFS_ECHO_ON(buf) (buf[0] == '1') +#define FTS_SYSFS_ECHO_OFF(buf) (buf[0] == '0') + +enum FW_FLASH_MODE { + FLASH_MODE_APP, + FLASH_MODE_LIC, + FLASH_MODE_PARAM, + FLASH_MODE_ALL, +}; + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +/* IC info */ +struct upgrade_func { + u64 ctype[FTX_MAX_COMPATIBLE_TYPE]; + int newmode; + u32 fwveroff; + u32 fwcfgoff; + u32 appoff; + u32 licoff; + u32 paramcfgoff; + u32 paramcfgveroff; + u32 paramcfg2off; + bool hid_supported; + bool pramboot_supported; + bool fts_8006u; + bool fts_5422u; + u8 *pramboot; + u32 pb_length; + int (*init)(void); + int (*upgrade)(struct i2c_client *, u8 *, u32); + int (*get_hlic_ver)(u8 *); + int (*lic_upgrade)(struct i2c_client *, u8 *, u32); + int (*param_upgrade)(struct i2c_client *, u8 *, u32); + int (*force_upgrade)(struct i2c_client *, u8 *, u32); +}; + +struct fts_upgrade { + u8 *fw; + u32 fw_length; + u8 *lic; + u32 lic_length; + struct upgrade_func *func; +}; + +struct upgrade_fw { + u16 vendor_id; + u8 *fw_file; + u32 fw_len; +}; + + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +extern struct fts_upgrade *fwupgrade; +extern struct upgrade_func upgrade_func_ft8006; +extern struct upgrade_func upgrade_func_ft8006u; +extern struct upgrade_func upgrade_func_ft5422u; +extern struct ft_chip_t ft5422u_fct; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +bool fts_fwupg_check_fw_valid(struct i2c_client *client); +int fts_fwupg_get_boot_state(struct i2c_client *client, enum FW_STATUS *fw_sts); +bool fts_fwupg_check_state(struct i2c_client *client, enum FW_STATUS rstate); +int fts_fwupg_reset_in_boot(struct i2c_client *client); +int fts_fwupg_reset_to_boot(struct i2c_client *client); +int fts_fwupg_reset_to_romboot(struct i2c_client *client); +int fts_fwupg_enter_into_boot(struct i2c_client *client); +int fts_fwupg_erase(struct i2c_client *client, u32 delay); +int fts_fwupg_ecc_cal(struct i2c_client *client, u32 saddr, u32 len); +int fts_flash_write_buf(struct i2c_client *client, u32 saddr, u8 *buf, u32 len, u32 delay); +void fts_fwupg_auto_upgrade(struct fts_ts_data *ts_data); +void fts_i2c_hid2std(struct i2c_client *client); +int fts_ft8006u_pram_write_remap(struct i2c_client *client); +int fts_pram_write_buf(struct i2c_client *client, u8 *buf, u32 len); +int fts_pram_ecc_cal(struct i2c_client *client, u32 saddr, u32 len); +int fts_pram_start(struct i2c_client *client); + +int fts_fwupg_do_upgrade(const char *fwname); +int fts_lic_get_ver_in_tp(struct i2c_client *client, u8 *ver); + +int fts_extra_init(struct i2c_client *client, struct input_dev *input_dev, struct ft_ts_platform_data *pdata); +int fts_extra_exit(void); +#endif diff --git a/drivers/input/touchscreen/focaltech_mmi/focaltech_ts.c b/drivers/input/touchscreen/focaltech_mmi/focaltech_ts.c new file mode 100644 index 000000000000..0ee0bf73e08a --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/focaltech_ts.c @@ -0,0 +1,3942 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2010 Focal tech Ltd. + * Copyright (c) 2012-2016, The Linux Foundation. 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "focaltech_flash.h" + + +#if defined(CONFIG_FB) +#include +#include + +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +/* Early-suspend level */ +#define FT_SUSPEND_LEVEL 1 +#endif + +#if defined(CONFIG_TOUCHSCREEN_FOCALTECH_SECURE_TOUCH_MMI) +#include +#include +#include +#include +static irqreturn_t ft_ts_interrupt(int irq, void *data); +#endif + +#include +#include +#include + +#define FT_DRIVER_VERSION 0x02 + +#define FT_META_REGS 3 +#define FT_ONE_TCH_LEN 6 +#define FT_TCH_LEN(x) (FT_META_REGS + FT_ONE_TCH_LEN * x) + +#define FT_PRESS 0x7F +#define FT_MAX_ID 0x0F +#define FT_TOUCH_X_H_POS 3 +#define FT_TOUCH_X_L_POS 4 +#define FT_TOUCH_Y_H_POS 5 +#define FT_TOUCH_Y_L_POS 6 +#define FT_TD_STATUS 2 +#define FT_TOUCH_EVENT_POS 3 +#define FT_TOUCH_ID_POS 5 +#define FT_TOUCH_DOWN 0 +#define FT_TOUCH_CONTACT 2 + +/* register address*/ +#define FT_REG_DEV_MODE 0x00 +#define FT_DEV_MODE_REG_CAL 0x02 +#define FT_REG_FW_ID 0xA1 +#define FT_REG_ID 0xA3 +#define FT_REG_PMODE 0xA5 +#define FT_REG_FW_VER 0xA6 +#define FT_REG_FW_VENDOR_ID 0xA8 +#define FT_REG_POINT_RATE 0x88 +#define FT_REG_THGROUP 0x80 +#define FT_REG_ECC 0xCC +#define FT_REG_RESET_FW 0x07 +#define FT_REG_FW_MIN_VER 0xB2 +#define FT_REG_FW_SUB_MIN_VER 0xB3 +#define FT_REG_I2C_MODE 0xEB +#define FT_REG_FW_LEN 0xB0 +#define FT_REG_CHG_STATE 0x8B + +/* i2c mode register value */ +#define FT_VAL_I2C_MODE 0xAA +#define FT_VAL_I2C_MODE_STD 0x09 +#define FT_VAL_I2C_MODE_STD_CFM 0x08 + +/* gesture register address*/ +#define FT_REG_GESTURE_ENABLE 0xD0 +#define FT_REG_GESTURE_OUTPUT 0xD3 + +/* gesture register bits*/ +#define FT_GESTURE_DOUBLECLICK_COORD_X 100 +#define FT_GESTURE_DOUBLECLICK_COORD_Y 100 +#define FT_GESTURE_WAKEUP_TIMEOUT 500 +#define FT_GESTURE_DEFAULT_TRACKING_ID 0x0A +#define FT_GESTURE_DOUBLECLICK_ID 0x24 +#define FT_GESTURE_POINTER_NUM_MAX 128 +#define FT_GESTURE_POINTER_SIZEOF 4 +#define FT_GESTURE_ID_FLAG_SIZE 1 +#define FT_GESTURE_POINTER_NUM_FLAG_SIZE 1 +/* 6 bytes are taken to mark which gesture is supported in firmware */ +#define FT_GESTURE_SET_FLAG_SIZE 6 +#define I2C_TRANSFER_MAX_BYTE 255 +#define FT_GESTURE_DATA_HEADER (FT_GESTURE_ID_FLAG_SIZE + \ + FT_GESTURE_POINTER_NUM_FLAG_SIZE + \ + FT_GESTURE_SET_FLAG_SIZE) + +/* power register bits*/ +#define FT_PMODE_ACTIVE 0x00 +#define FT_PMODE_MONITOR 0x01 +#define FT_PMODE_STANDBY 0x02 +#define FT_PMODE_HIBERNATE 0x03 +#define FT_FACTORYMODE_VALUE 0x40 +#define FT_WORKMODE_VALUE 0x00 +#define FT_RST_CMD_REG1 0xFC +#define FT_RST_CMD_REG2 0xBC +#define FT_READ_ID_REG 0x90 +#define FT_ERASE_APP_REG 0x61 +#define FT_ERASE_PANEL_REG 0x63 +#define FT_FW_START_REG 0xBF + +#define FT_STATUS_NUM_TP_MASK 0x0F + +#define FT_VTG_MIN_UV 2600000 +#define FT_VTG_MAX_UV 3300000 +#define FT_I2C_VTG_MIN_UV 1800000 +#define FT_I2C_VTG_MAX_UV 1800000 + +#define FT_COORDS_ARR_SIZE 4 +#define MAX_BUTTONS 4 + +#define FT_8BIT_SHIFT 8 +#define FT_4BIT_SHIFT 4 +#define FT_FW_NAME_MAX_LEN 50 + +#define FT5316_ID 0x0A +#define FT5306I_ID 0x55 +#define FT6X06_ID 0x06 +#define FT6X36_ID 0x36 + +#define FT_UPGRADE_AA 0xAA +#define FT_UPGRADE_55 0x55 + +#define FT_FW_MIN_SIZE 8 +#define FT_FW_MAX_SIZE 65536 + +/* Firmware file is not supporting minor and sub minor so use 0 */ +#define FT_FW_FILE_MAJ_VER(x) ((x)->data[(x)->size - 2]) +#define FT_FW_FILE_MIN_VER(x) 0 +#define FT_FW_FILE_SUB_MIN_VER(x) 0 +#define FT_FW_FILE_VENDOR_ID(x) ((x)->data[(x)->size - 1]) + +#define FT_FW_FILE_MAJ_VER_FT6X36(x) ((x)->data[0x10a]) +#define FT_FW_FILE_VENDOR_ID_FT6X36(x) ((x)->data[0x108]) + +/** +* Application data verification will be run before upgrade flow. +* Firmware image stores some flags with negative and positive value +* in corresponding addresses, we need pick them out do some check to +* make sure the application data is valid. +*/ +#define FT_FW_CHECK(x, ts_data) \ + (ts_data->family_id == FT6X36_ID ? \ + (((x)->data[0x104] ^ (x)->data[0x105]) == 0xFF \ + && ((x)->data[0x106] ^ (x)->data[0x107]) == 0xFF) : \ + (((x)->data[(x)->size - 8] ^ (x)->data[(x)->size - 6]) == 0xFF \ + && ((x)->data[(x)->size - 7] ^ (x)->data[(x)->size - 5]) == 0xFF \ + && ((x)->data[(x)->size - 3] ^ (x)->data[(x)->size - 4]) == 0xFF)) + +#define FT_MAX_TRIES 5 +#define FT_RETRY_DLY 20 + +#define FT_MAX_WR_BUF 10 +#define FT_MAX_RD_BUF 3 +#define FT_FW_PKT_LEN 128 +#define FT_FW_PKT_META_LEN 6 +#define FT_FW_PKT_DLY_MS 20 +#define FT_FW_LAST_PKT 0x6ffa +#define FT_EARSE_DLY_MS 100 +#define FT_55_AA_DLY_NS 5000 + +#define FT_UPGRADE_LOOP 30 +#define FT_CAL_START 0x04 +#define FT_CAL_FIN 0x00 +#define FT_CAL_STORE 0x05 +#define FT_CAL_RETRY 100 +#define FT_REG_CAL 0x00 +#define FT_CAL_MASK 0x70 + +#define FT_INFO_MAX_LEN 512 + +#define FT_BLOADER_SIZE_OFF 12 +#define FT_BLOADER_NEW_SIZE 30 +#define FT_DATA_LEN_OFF_OLD_FW 8 +#define FT_DATA_LEN_OFF_NEW_FW 14 +#define FT_FINISHING_PKT_LEN_OLD_FW 6 +#define FT_FINISHING_PKT_LEN_NEW_FW 12 +#define FT_MAGIC_BLOADER_Z7 0x7bfa +#define FT_MAGIC_BLOADER_LZ4 0x6ffa +#define FT_MAGIC_BLOADER_GZF_30 0x7ff4 +#define FT_MAGIC_BLOADER_GZF 0x7bf4 + +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" +#define PINCTRL_STATE_RELEASE "pmx_ts_release" + +#define EXP_FN_DET_INTERVAL 1000 /* ms */ + +static irqreturn_t ft_ts_interrupt(int irq, void *data); + +enum { + FT_BLOADER_VERSION_LZ4 = 0, + FT_BLOADER_VERSION_Z7 = 1, + FT_BLOADER_VERSION_GZF = 2, +}; + +enum { + FT_FT5336_FAMILY_ID_0x11 = 0x11, + FT_FT5336_FAMILY_ID_0x12 = 0x12, + FT_FT5336_FAMILY_ID_0x13 = 0x13, + FT_FT5336_FAMILY_ID_0x14 = 0x14, +}; + +#define FT_STORE_TS_INFO(buf, id, fw_maj, fw_min, fw_sub_min) \ + snprintf(buf, FT_INFO_MAX_LEN, \ + "vendor name = Focaltech\n" \ + "model = 0x%x\n" \ + "fw_version = %d.%d.%d\n", \ + id, fw_maj, fw_min, fw_sub_min) +#define FT_TS_INFO_SYSFS_DIR_NAME "ts_info" +static char *ts_info_buff; + +#define FT_STORE_TS_DBG_INFO(buf, id, name, max_tch, group_id, \ + fw_vkey_support, fw_name, fw_maj, fw_min, fw_sub_min) \ + snprintf(buf, FT_INFO_MAX_LEN, \ + "controller\t= focaltech\n" \ + "model\t\t= 0x%x\n" \ + "name\t\t= %s\n" \ + "max_touches\t= %d\n" \ + "drv_ver\t\t= 0x%x\n" \ + "group_id\t= 0x%x\n" \ + "fw_vkey_support\t= %s\n" \ + "fw_name\t\t= %s\n" \ + "fw_ver\t\t= %d.%d.%d\n", id, name, \ + max_tch, FT_DRIVER_VERSION, group_id, \ + fw_vkey_support, fw_name, fw_maj, fw_min, \ + fw_sub_min) + +#define FT_DEBUG_DIR_NAME "ts_debug" +#define FT_FW_ID_LEN 2 + +#define FT_PROC_DIR_NAME "ftxxxx-debug" + +enum { + PROC_UPGRADE = 0, + PROC_READ_REGISTER = 1, + PROC_WRITE_REGISTER = 2, + PROC_WRITE_DATA = 6, + PROC_READ_DATA = 7 +}; + +struct ft_clip_area { + unsigned xul_clip, yul_clip, xbr_clip, ybr_clip; + unsigned inversion; /* clip inside (when 1) or ouside otherwise */ +}; + +enum { + FT_MOD_CHARGER, + FT_MOD_FPS, + FT_MOD_MAX +}; + +struct ft_modifiers { + int mods_num; + struct semaphore list_sema; + struct list_head mod_head; + +}; + +struct config_modifier { + const char *name; + int id; + bool effective; + struct ft_clip_area *clipa; + struct list_head link; +}; + +struct ft_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + const struct ft_ts_platform_data *pdata; + struct ft_gesture_platform_data *gesture_pdata; + struct regulator *vdd; + struct regulator *vcc_i2c; + bool regulator_en; + struct mutex ft_clk_io_ctrl_mutex; + char fw_name[FT_FW_NAME_MAX_LEN]; + bool loading_fw; + u8 family_id; + struct dentry *dir; + u16 addr; + bool suspended; + char *ts_info; + u8 *tch_data; + u32 tch_data_len; + u8 fw_ver[3]; + u8 fw_vendor_id; + const char *panel_supplier; + struct kobject *ts_info_kobj; +#if defined(CONFIG_FB) + struct work_struct fb_notify_work; + struct notifier_block fb_notif; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; +#if defined(CONFIG_TOUCHSCREEN_FOCALTECH_SECURE_TOUCH_MMI) + atomic_t st_enabled; + atomic_t st_pending_irqs; + struct completion st_powerdown; + struct completion st_irq_processed; + bool st_initialized; + struct clk *core_clk; + struct clk *iface_clk; +#endif + bool flash_enabled; + bool force_reflash; + u8 fw_id[FT_FW_ID_LEN]; + bool irq_enabled; + struct proc_dir_entry *proc_entry; + u8 proc_operate_mode; + struct notifier_block ft_reboot; + /* moto TP modifiers */ + bool patching_enabled; + struct ft_modifiers modifiers; + bool charger_detection_enabled; + struct work_struct ps_notify_work; + struct notifier_block ps_notif; + bool ps_is_present; + + bool fps_detection_enabled; + bool is_fps_registered; /* FPS notif registration might be delayed */ + struct notifier_block fps_notif; + + bool clipping_on; + struct ft_clip_area *clipa; +}; + +struct ft_exp_fn { + bool inserted; + int (*func_init)(struct ft_ts_data *data); + void (*func_remove)(struct ft_ts_data *data); + void (*func_attn)(struct ft_ts_data *data, unsigned char intr_mask); + struct list_head link; +}; + +struct ft_exp_fn_ctrl { + bool inited; + struct mutex list_mutex; + struct list_head fn_list; + struct delayed_work det_work; + struct workqueue_struct *det_workqueue; + struct ft_ts_data *data_ptr; +}; +static DEFINE_MUTEX(exp_fn_ctrl_mutex); +static struct ft_exp_fn_ctrl exp_fn_ctrl; + + +struct touch_up_down { + int mismatch; + unsigned char up_down; + unsigned int counter; +}; + +struct touch_area_stats { + struct touch_up_down *ud; + ssize_t ud_len; + ssize_t ud_id; + ssize_t unknown_counter; + const char *name; +}; + +static struct touch_up_down display_ud[20]; +static struct touch_area_stats display_ud_stats = { + .ud = display_ud, + .ud_len = ARRAY_SIZE(display_ud), + .name = "ts" +}; + +__attribute__((weak)) +int FPS_register_notifier(struct notifier_block *nb, unsigned long stype, + bool report) +{ + return -ENODEV; +} + +__attribute__((weak)) +int FPS_unregister_notifier(struct notifier_block *nb, unsigned long stype) +{ + return -ENODEV; +} + +static void ud_set_id(struct touch_area_stats *tas, int id) +{ + tas->ud_id = id; +} + +static void ud_log_status(struct touch_area_stats *tas, bool down) +{ + struct touch_up_down *ud = tas->ud; + ssize_t id = tas->ud_id; + + if (id >= tas->ud_len) + tas->unknown_counter++; + + if (!down) { /* up */ + if (ud[id].up_down == 0x10) { + pr_debug("%s UP[%zu]\n", tas->name, id); + ud[id].up_down |= 1; + ud[id].mismatch--; + } + } else if (down) { /* down */ + if (ud[id].up_down == 0) { + ud[id].up_down |= (1 << 4); + pr_debug("%s DOWN[%zu]\n", tas->name, id); + ud[id].mismatch++; + } else if (ud[id].up_down == 0x10) + return; + } + + if (ud[id].up_down == 0x11) { + pr_debug("%s CLEAR[%zu]\n", tas->name, id); + ud[id].up_down = 0; + ud[id].counter++; + } +} + +static void TSI_state(struct input_dev *dev, unsigned int tool, bool status) +{ + ud_log_status(&display_ud_stats, status); + input_mt_report_slot_state(dev, tool, status); +} + +static void TSI_id(struct input_dev *dev, int id) +{ + ud_set_id(&display_ud_stats, id); + input_mt_slot(dev, id); +} + +#define input_mt_report_slot_state TSI_state +#define input_mt_slot TSI_id + +static int ft_ts_start(struct device *dev); +static int ft_ts_stop(struct device *dev); +static void ft_resume_ps_chg_cm_state(struct ft_ts_data *data); +static struct config_modifier *ft_modifier_by_id( + struct ft_ts_data *data, int id); + +#if defined(CONFIG_TOUCHSCREEN_FOCALTECH_SECURE_TOUCH_MMI) +static void ft_secure_touch_init(struct ft_ts_data *data) +{ + data->st_initialized = 0; + + init_completion(&data->st_powerdown); + init_completion(&data->st_irq_processed); + + /* Get clocks */ + data->core_clk = devm_clk_get(&data->client->dev, "core_clk"); + if (IS_ERR(data->core_clk)) { + data->core_clk = NULL; + dev_warn(&data->client->dev, + "%s: core_clk is not defined\n", __func__); + } + + data->iface_clk = devm_clk_get(&data->client->dev, "iface_clk"); + if (IS_ERR(data->iface_clk)) { + data->iface_clk = NULL; + dev_warn(&data->client->dev, + "%s: iface_clk is not defined", __func__); + } + data->st_initialized = 1; +} + +static void ft_secure_touch_notify(struct ft_ts_data *data) +{ + sysfs_notify(&data->input_dev->dev.kobj, NULL, "secure_touch"); +} + +static irqreturn_t ft_filter_interrupt(struct ft_ts_data *data) +{ + if (atomic_read(&data->st_enabled)) { + if (atomic_cmpxchg(&data->st_pending_irqs, 0, 1) == 0) { + reinit_completion(&data->st_irq_processed); + ft_secure_touch_notify(data); + wait_for_completion_interruptible( + &data->st_irq_processed); + } + return IRQ_HANDLED; + } + return IRQ_NONE; +} + +/* + * 'blocking' variable will have value 'true' when we want to prevent the driver + * from accessing the xPU/SMMU protected HW resources while the session is + * active. + */ +static void ft_secure_touch_stop(struct ft_ts_data *data, bool blocking) +{ + if (atomic_read(&data->st_enabled)) { + atomic_set(&data->st_pending_irqs, -1); + ft_secure_touch_notify(data); + if (blocking) + wait_for_completion_interruptible( + &data->st_powerdown); + } +} + +static int ft_clk_prepare_enable(struct ft_ts_data *data) +{ + int ret; + + ret = clk_prepare_enable(data->iface_clk); + if (ret) { + dev_err(&data->client->dev, + "error on clk_prepare_enable(iface_clk):%d\n", ret); + return ret; + } + + ret = clk_prepare_enable(data->core_clk); + if (ret) { + clk_disable_unprepare(data->iface_clk); + dev_err(&data->client->dev, + "error clk_prepare_enable(core_clk):%d\n", ret); + } + return ret; +} + +static void ft_clk_disable_unprepare(struct ft_ts_data *data) +{ + clk_disable_unprepare(data->core_clk); + clk_disable_unprepare(data->iface_clk); +} + +static int ft_bus_get(struct ft_ts_data *data) +{ + int retval; + + mutex_lock(&data->ft_clk_io_ctrl_mutex); + retval = pm_runtime_get_sync(data->client->adapter->dev.parent); + if (retval >= 0 && data->core_clk != NULL && data->iface_clk != NULL) { + retval = ft_clk_prepare_enable(data); + if (retval) + pm_runtime_put_sync(data->client->adapter->dev.parent); + } + mutex_unlock(&data->ft_clk_io_ctrl_mutex); + return retval; +} + +static void ft_bus_put(struct ft_ts_data *data) +{ + mutex_lock(&data->ft_clk_io_ctrl_mutex); + if (data->core_clk != NULL && data->iface_clk != NULL) + ft_clk_disable_unprepare(data); + pm_runtime_put_sync(data->client->adapter->dev.parent); + mutex_unlock(&data->ft_clk_io_ctrl_mutex); +} + +static ssize_t ft_secure_touch_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d", atomic_read(&data->st_enabled)); +} + +/* + * Accept only "0" and "1" valid values. + * "0" will reset the st_enabled flag, then wake up the reading process and + * the interrupt handler. + * The bus driver is notified via pm_runtime that it is not required to stay + * awake anymore. + * It will also make sure the queue of events is emptied in the controller, + * in case a touch happened in between the secure touch being disabled and + * the local ISR being ungated. + * "1" will set the st_enabled flag and clear the st_pending_irqs flag. + * The bus driver is requested via pm_runtime to stay awake. + */ +static ssize_t ft_secure_touch_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + unsigned long value; + int err = 0; + + if (count > 2) + return -EINVAL; + err = kstrtoul(buf, 10, &value); + if (err != 0) + return err; + + if (!data->st_initialized) + return -EIO; + + err = count; + switch (value) { + case 0: + if (atomic_read(&data->st_enabled) == 0) + break; + ft_bus_put(data); + atomic_set(&data->st_enabled, 0); + ft_secure_touch_notify(data); + complete(&data->st_irq_processed); + ft_ts_interrupt(data->client->irq, data); + complete(&data->st_powerdown); + break; + + case 1: + if (atomic_read(&data->st_enabled)) { + err = -EBUSY; + break; + } + synchronize_irq(data->client->irq); + if (ft_bus_get(data) < 0) { + dev_err(&data->client->dev, "ft_bus_get failed\n"); + err = -EIO; + break; + } + reinit_completion(&data->st_powerdown); + reinit_completion(&data->st_irq_processed); + atomic_set(&data->st_enabled, 1); + atomic_set(&data->st_pending_irqs, 0); + break; + + default: + dev_err(&data->client->dev, "unsupported value: %lu\n", value); + err = -EINVAL; + break; + } + return err; +} + +/* + * This function returns whether there are pending interrupts, or + * other error conditions that need to be signaled to the userspace library, + * according tot he following logic: + * - st_enabled is 0 if secure touch is not enabled, returning -EBADF + * - st_pending_irqs is -1 to signal that secure touch is in being stopped, + * returning -EINVAL + * - st_pending_irqs is 1 to signal that there is a pending irq, returning + * the value "1" to the sysfs read operation + * - st_pending_irqs is 0 (only remaining case left) if the pending interrupt + * has been processed, so the interrupt handler can be allowed to continue. + */ +static ssize_t ft_secure_touch_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + int val = 0; + + if (atomic_read(&data->st_enabled) == 0) + return -EBADF; + if (atomic_cmpxchg(&data->st_pending_irqs, -1, 0) == -1) + return -EINVAL; + if (atomic_cmpxchg(&data->st_pending_irqs, 1, 0) == 1) + val = 1; + else + complete(&data->st_irq_processed); + return scnprintf(buf, PAGE_SIZE, "%u", val); +} +#else +static void ft_secure_touch_init(struct ft_ts_data *data) +{ +} +static irqreturn_t ft_filter_interrupt(struct ft_ts_data *data) +{ + return IRQ_NONE; +} +static void ft_secure_touch_stop(struct ft_ts_data *data, bool blocking) +{ +} +#endif + +static struct device_attribute st_attrs[] = { +#if defined(CONFIG_TOUCHSCREEN_FOCALTECH_SECURE_TOUCH_MMI) + __ATTR(secure_touch_enable, (S_IRUGO | S_IWUSR | S_IWGRP), + ft_secure_touch_enable_show, + ft_secure_touch_enable_store), + __ATTR(secure_touch, S_IRUGO, + ft_secure_touch_show, NULL), +#endif +}; + +static int ft_i2c_read(struct i2c_client *client, char *writebuf, + int writelen, char *readbuf, int readlen) +{ + int ret; + + if (writelen > 0) { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) + dev_err(&client->dev, "%s: i2c read error.\n", + __func__); + } else { + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = readlen, + .buf = readbuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s:i2c read error.\n", __func__); + } + return ret; +} + +static int ft_i2c_write(struct i2c_client *client, char *writebuf, + int writelen) +{ + int ret; + + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = writelen, + .buf = writebuf, + }, + }; + ret = i2c_transfer(client->adapter, msgs, 1); + if (ret < 0) + dev_err(&client->dev, "%s: i2c write error.\n", __func__); + + return ret; +} + +static int ft_write_reg(struct i2c_client *client, u8 addr, const u8 val) +{ + u8 buf[2] = {0}; + + buf[0] = addr; + buf[1] = val; + + return ft_i2c_write(client, buf, sizeof(buf)); +} + +static int ft_read_reg(struct i2c_client *client, u8 addr, u8 *val) +{ + return ft_i2c_read(client, &addr, 1, val, 1); +} + +static void ft_irq_disable(struct ft_ts_data *data) +{ + if (data->irq_enabled) { + disable_irq(data->client->irq); + data->irq_enabled = false; + } +} + +static void ft_irq_enable(struct ft_ts_data *data) +{ + if (!data->irq_enabled) { + enable_irq(data->client->irq); + data->irq_enabled = true; + } +} + +static const char *ft_find_vendor_name( + const struct ft_ts_platform_data *pdata, u8 id) +{ + int i; + + if (pdata->num_vendor_ids == 0) + return NULL; + + for (i = 0; i < pdata->num_vendor_ids; i++) + if (id == pdata->vendor_ids[i]) { + pr_info("vendor id 0x%02x panel supplier is %s\n", + id, pdata->vendor_names[i]); + return pdata->vendor_names[i]; + } + + return NULL; +} + +static void ft_update_fw_vendor_id(struct ft_ts_data *data) +{ + struct i2c_client *client = data->client; + u8 reg_addr; + int err; + + reg_addr = FT_REG_FW_VENDOR_ID; + err = ft_i2c_read(client, ®_addr, 1, &data->fw_vendor_id, 1); + if (err < 0) + dev_err(&client->dev, "fw vendor id read failed"); + data->panel_supplier = ft_find_vendor_name(data->pdata, + data->fw_vendor_id); +} + +static void ft_update_fw_ver(struct ft_ts_data *data) +{ + struct i2c_client *client = data->client; + u8 reg_addr; + int err; + + reg_addr = FT_REG_FW_VER; + err = ft_i2c_read(client, ®_addr, 1, &data->fw_ver[0], 1); + if (err < 0) + dev_err(&client->dev, "fw major version read failed"); + + reg_addr = FT_REG_FW_MIN_VER; + err = ft_i2c_read(client, ®_addr, 1, &data->fw_ver[1], 1); + if (err < 0) + dev_err(&client->dev, "fw minor version read failed"); + + reg_addr = FT_REG_FW_SUB_MIN_VER; + err = ft_i2c_read(client, ®_addr, 1, &data->fw_ver[2], 1); + if (err < 0) + dev_err(&client->dev, "fw sub minor version read failed"); + + dev_info(&client->dev, "Firmware version = %d.%d.%d\n", + data->fw_ver[0], data->fw_ver[1], data->fw_ver[2]); +} + +static void ft_update_fw_id(struct ft_ts_data *data) +{ + struct i2c_client *client = data->client; + u8 reg_addr; + int err; + + reg_addr = FT_REG_FW_ID; + err = ft_i2c_read(client, ®_addr, 1, + &data->fw_id[0], FT_FW_ID_LEN); + if (err < 0) + dev_err(&client->dev, "fw id read failed"); + + dev_info(&client->dev, "Firmware id = 0x%02x%02x\n", + data->fw_id[0], data->fw_id[1]); +} + +static void ft_clear_fw_id(struct ft_ts_data *data) +{ + data->fw_id[0] = 0; + data->fw_id[1] = 0; + data->fw_ver[0] = 0; +} + +static void ft_ud_fix_mismatch(struct ft_ts_data *data) +{ + int i; + struct input_dev *ip_dev = data->input_dev; + + for (i = 0; i < display_ud_stats.ud_len; i++) { + if (display_ud[i].mismatch) { + pr_debug("%s MISMATCH[%d]\n", display_ud_stats.name, i); + input_mt_slot(ip_dev, i); + input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 0); + } + } +} + +static irqreturn_t ft_ts_interrupt(int irq, void *dev_id) +{ + struct ft_ts_data *data = dev_id; + struct input_dev *ip_dev; + int rc, i; + u32 id, x, y, status, num_touches; + u8 reg, *buf; + bool update_input = false; + + num_touches = 0; + + if (!data) { + pr_err("%s: Invalid data\n", __func__); + return IRQ_HANDLED; + } + + if (ft_filter_interrupt(data) == IRQ_HANDLED) + return IRQ_HANDLED; + + ip_dev = data->input_dev; + buf = data->tch_data; + + /* + * Read touch data start from register FT_REG_DEV_MODE. + * The touch x/y value start from FT_TOUCH_X_H/L_POS and + * FT_TOUCH_Y_H/L_POS in buf. + */ + reg = FT_REG_DEV_MODE; + rc = ft_i2c_read(data->client, ®, 1, buf, data->tch_data_len); + if (rc < 0) { + dev_err(&data->client->dev, "%s: read data fail\n", __func__); + return IRQ_HANDLED; + } + + for (i = 0; i < data->pdata->num_max_touches; i++) { + /* + * Getting the finger ID of the touch event incase of + * multiple touch events + */ + id = (buf[FT_TOUCH_ID_POS + FT_ONE_TCH_LEN * i]) >> 4; + if (id >= FT_MAX_ID) + break; + + update_input = true; + + x = (buf[FT_TOUCH_X_H_POS + FT_ONE_TCH_LEN * i] & 0x0F) << 8 | + (buf[FT_TOUCH_X_L_POS + FT_ONE_TCH_LEN * i]); + y = (buf[FT_TOUCH_Y_H_POS + FT_ONE_TCH_LEN * i] & 0x0F) << 8 | + (buf[FT_TOUCH_Y_L_POS + FT_ONE_TCH_LEN * i]); + + if (data->pdata->x_flip) + x = data->pdata->x_max - x; + if (data->pdata->y_flip) + y = data->pdata->y_max - y; + + status = buf[FT_TOUCH_EVENT_POS + FT_ONE_TCH_LEN * i] >> 6; + + num_touches = buf[FT_TD_STATUS] & FT_STATUS_NUM_TP_MASK; + + /* invalid combination */ + if (!num_touches && !status && !id) + break; + + if (data->clipping_on && data->clipa) { + bool inside; + + inside = (x >= data->clipa->xul_clip) && + (x <= data->clipa->xbr_clip) && + (y >= data->clipa->yul_clip) && + (y <= data->clipa->ybr_clip); + + if (inside == data->clipa->inversion) { + /* Touch might still be active, but we're */ + /* sending release anyway to avoid touch */ + /* stuck at the last reported position. */ + /* Driver will suppress reporting to UI */ + /* touch events from within clipping area */ + status = 1; + + dev_dbg(&data->client->dev, + "finger id-%d (%d,%d) within clipping area\n", + id, x, y); + } + } + + input_mt_slot(ip_dev, id); + if (status == FT_TOUCH_DOWN || status == FT_TOUCH_CONTACT) { + input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 1); + input_report_abs(ip_dev, ABS_MT_POSITION_X, x); + input_report_abs(ip_dev, ABS_MT_POSITION_Y, y); + } else { + input_mt_report_slot_state(ip_dev, MT_TOOL_FINGER, 0); + } + } + + /* report up event if FW missed to do */ + if (num_touches == 0) + ft_ud_fix_mismatch(data); + + if (update_input) { + input_mt_report_pointer_emulation(ip_dev, false); + input_sync(ip_dev); + } + + return IRQ_HANDLED; +} + +static int ft_gpio_configure(struct ft_ts_data *data, bool on) +{ + int err = 0; + + if (on) { + if (gpio_is_valid(data->pdata->irq_gpio)) { + err = gpio_request(data->pdata->irq_gpio, + "ft_irq_gpio"); + if (err) { + dev_err(&data->client->dev, + "irq gpio request failed"); + goto err_irq_gpio_req; + } + + err = gpio_direction_input(data->pdata->irq_gpio); + if (err) { + dev_err(&data->client->dev, + "set_direction for irq gpio failed\n"); + goto err_irq_gpio_dir; + } + } + + if (gpio_is_valid(data->pdata->reset_gpio)) { + err = gpio_request(data->pdata->reset_gpio, + "ft_reset_gpio"); + if (err) { + dev_err(&data->client->dev, + "reset gpio request failed"); + goto err_irq_gpio_dir; + } + + err = gpio_direction_output(data->pdata->reset_gpio, 0); + if (err) { + dev_err(&data->client->dev, + "set_direction for reset gpio failed\n"); + goto err_reset_gpio_dir; + } + msleep(data->pdata->hard_rst_dly); + gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + } + + return 0; + } + if (gpio_is_valid(data->pdata->irq_gpio)) + gpio_free(data->pdata->irq_gpio); + if (gpio_is_valid(data->pdata->reset_gpio)) + gpio_free(data->pdata->reset_gpio); + + 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: + return err; +} + +static int ft_power_on(struct ft_ts_data *data, bool on) +{ + int rc; + + if (!on) + goto power_off; + + rc = regulator_enable(data->vdd); + if (rc) { + dev_err(&data->client->dev, + "Regulator vdd enable failed rc=%d\n", rc); + return rc; + } + + rc = regulator_enable(data->vcc_i2c); + if (rc) { + dev_err(&data->client->dev, + "Regulator vcc_i2c enable failed rc=%d\n", rc); + regulator_disable(data->vdd); + } + + return rc; + +power_off: + rc = regulator_disable(data->vdd); + if (rc) { + dev_err(&data->client->dev, + "Regulator vdd disable failed rc=%d\n", rc); + return rc; + } + + if (!IS_ERR(data->vcc_i2c)) { + rc = regulator_disable(data->vcc_i2c); + if (rc) { + dev_err(&data->client->dev, + "Regulator vcc_i2c disable failed rc=%d\n", rc); + rc = regulator_enable(data->vdd); + if (rc) { + dev_err(&data->client->dev, + "Regulator vdd enable failed rc=%d\n", rc); + } + } + } + + return rc; +} + +static int ft_power_init(struct ft_ts_data *data, bool on) +{ + int rc; + + if (!on) + goto pwr_deinit; + + data->vdd = regulator_get(&data->client->dev, "vdd"); + if (IS_ERR(data->vdd)) { + rc = PTR_ERR(data->vdd); + dev_err(&data->client->dev, + "Regulator get failed vdd rc=%d\n", rc); + return rc; + } + + if (regulator_count_voltages(data->vdd) > 0) { + rc = regulator_set_voltage(data->vdd, FT_VTG_MIN_UV, + FT_VTG_MAX_UV); + if (rc) { + dev_err(&data->client->dev, + "Regulator set_vtg failed vdd rc=%d\n", rc); + goto reg_vdd_put; + } + } + + data->vcc_i2c = regulator_get(&data->client->dev, "vcc_i2c"); + if (IS_ERR(data->vcc_i2c)) { + rc = PTR_ERR(data->vcc_i2c); + dev_err(&data->client->dev, + "Regulator get failed vcc_i2c rc=%d\n", rc); + goto reg_vdd_set_vtg; + } + + if (regulator_count_voltages(data->vcc_i2c) > 0) { + rc = regulator_set_voltage(data->vcc_i2c, FT_I2C_VTG_MIN_UV, + FT_I2C_VTG_MAX_UV); + if (rc) { + dev_err(&data->client->dev, + "Regulator set_vtg failed vcc_i2c rc=%d\n", rc); + goto reg_vcc_i2c_put; + } + } + + return 0; + +reg_vcc_i2c_put: + regulator_put(data->vcc_i2c); + data->vcc_i2c = NULL; +reg_vdd_set_vtg: + if (regulator_count_voltages(data->vdd) > 0) + regulator_set_voltage(data->vdd, 0, FT_VTG_MAX_UV); +reg_vdd_put: + regulator_put(data->vdd); + data->vdd = NULL; + + return rc; + +pwr_deinit: + if (!IS_ERR_OR_NULL(data->vdd)) { + if (regulator_count_voltages(data->vdd) > 0) + regulator_set_voltage(data->vdd, 0, FT_VTG_MAX_UV); + + regulator_put(data->vdd); + } + + if (!IS_ERR_OR_NULL(data->vcc_i2c)) { + if (regulator_count_voltages(data->vcc_i2c) > 0) + regulator_set_voltage(data->vcc_i2c, 0, FT_I2C_VTG_MAX_UV); + + regulator_put(data->vcc_i2c); + } + + return 0; +} + +static int ft_ts_pinctrl_init(struct ft_ts_data *data) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + data->ts_pinctrl = devm_pinctrl_get(&(data->client->dev)); + if (IS_ERR_OR_NULL(data->ts_pinctrl)) { + retval = PTR_ERR(data->ts_pinctrl); + dev_dbg(&data->client->dev, + "Target does not use pinctrl %d\n", retval); + goto err_pinctrl_get; + } + + data->pinctrl_state_active + = pinctrl_lookup_state(data->ts_pinctrl, + PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(data->pinctrl_state_active)) { + retval = PTR_ERR(data->pinctrl_state_active); + dev_err(&data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + data->pinctrl_state_suspend + = pinctrl_lookup_state(data->ts_pinctrl, + PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(data->pinctrl_state_suspend)) { + retval = PTR_ERR(data->pinctrl_state_suspend); + dev_err(&data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + data->pinctrl_state_release + = pinctrl_lookup_state(data->ts_pinctrl, + PINCTRL_STATE_RELEASE); + if (IS_ERR_OR_NULL(data->pinctrl_state_release)) { + retval = PTR_ERR(data->pinctrl_state_release); + dev_dbg(&data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_RELEASE, retval); + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(data->ts_pinctrl); +err_pinctrl_get: + data->ts_pinctrl = NULL; + return retval; +} + +static ssize_t ft_ud_stat(struct ft_ts_data *data, char *buf, ssize_t size) +{ + int i; + ssize_t total = 0; + + total += scnprintf(buf + total, size - total, "screen: "); + for (i = 0; i < display_ud_stats.ud_len; i++) + if (display_ud[i].mismatch) + total += scnprintf(buf + total, size - total, + "%d)%u,%d ", i, + display_ud[i].counter, + display_ud[i].mismatch); + else if (display_ud[i].counter) + total += scnprintf(buf + total, size - total, + "%d)%u ", i, + display_ud[i].counter); + return total; +} + +#ifdef CONFIG_PM +static int ft_ts_start(struct device *dev) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + int err; + + if (data->ts_pinctrl) { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_active); + if (err < 0) + dev_err(dev, "Cannot get active pinctrl state\n"); + } + + if (gpio_is_valid(data->pdata->reset_gpio)) { + err = gpio_request(data->pdata->reset_gpio, + "ft_reset_gpio"); + if (err) { + dev_err(&data->client->dev, + "reset gpio request failed"); + goto err_gpio_configuration; + } + + err = gpio_direction_output(data->pdata->reset_gpio, 0); + if (err) { + dev_err(&data->client->dev, + "set_direction for reset gpio failed\n"); + if (gpio_is_valid(data->pdata->reset_gpio)) + gpio_free(data->pdata->reset_gpio); + goto err_gpio_configuration; + } + + gpio_set_value_cansleep(data->pdata->reset_gpio, 0); + usleep_range(800, 1000); + if (data->regulator_en) { + if (data->pdata->power_on) { + err = data->pdata->power_on(true); + if (err) { + dev_err(dev, "power on failed"); + return err; + } + } else { + err = ft_power_on(data, true); + if (err) { + dev_err(dev, "power on failed"); + return err; + } + } + } + msleep(data->pdata->hard_rst_dly); + gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + } + + msleep(data->pdata->soft_rst_dly); + + ft_irq_enable(data); + data->suspended = false; + + return 0; + +err_gpio_configuration: + if (data->ts_pinctrl) { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_suspend); + if (err < 0) + dev_err(dev, "Cannot get suspend pinctrl state\n"); + } + return err; +} + +static int ft_ts_stop(struct device *dev) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + char txbuf[2]; + static char ud_stats[PAGE_SIZE]; + int i, err; + + ft_irq_disable(data); + + /* print UD statistics */ + ft_ud_stat(data, ud_stats, sizeof(ud_stats)); + pr_info("%s\n", ud_stats); + /* release all touches */ + for (i = 0; i < data->pdata->num_max_touches; i++) { + input_mt_slot(data->input_dev, i); + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, 0); + } + input_mt_report_pointer_emulation(data->input_dev, false); + input_sync(data->input_dev); + + if (gpio_is_valid(data->pdata->reset_gpio) || (data->pdata->force_send_sleep == true)) { + txbuf[0] = FT_REG_PMODE; + txbuf[1] = FT_PMODE_HIBERNATE; + ft_i2c_write(data->client, txbuf, sizeof(txbuf)); + } + + if (data->regulator_en) { + if (data->pdata->power_on) { + err = data->pdata->power_on(false); + if (err) { + dev_err(dev, "power off failed"); + goto pwr_off_fail; + } + } else { + err = ft_power_on(data, false); + if (err) { + dev_err(dev, "power off failed"); + goto pwr_off_fail; + } + } + } + + if (data->ts_pinctrl) { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_suspend); + if (err < 0) + dev_err(dev, "Cannot get suspend pinctrl state\n"); + } + + err = ft_gpio_configure(data, false); + if (err < 0) { + dev_err(&data->client->dev, + "failed to put gpios in suspend state\n"); + goto gpio_configure_fail; + } + + data->suspended = true; + + return 0; + +gpio_configure_fail: + if (data->ts_pinctrl) { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_active); + if (err < 0) + dev_err(dev, "Cannot get active pinctrl state\n"); + } + if (data->regulator_en) { + if (data->pdata->power_on) { + err = data->pdata->power_on(true); + if (err) + dev_err(dev, "power on failed"); + } else { + err = ft_power_on(data, true); + if (err) + dev_err(dev, "power on failed"); + } + } +pwr_off_fail: + if (gpio_is_valid(data->pdata->reset_gpio)) { + gpio_set_value_cansleep(data->pdata->reset_gpio, 0); + msleep(data->pdata->hard_rst_dly); + gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + } + ft_irq_enable(data); + return err; +} + +static int ft_ts_suspend(struct device *dev) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + + if (data->loading_fw) { + dev_info(dev, "Firmware loading in process...\n"); + return 0; + } + + if (data->suspended) { + dev_info(dev, "Already in suspend state\n"); + return 0; + } + + data->flash_enabled = false; + ft_secure_touch_stop(data, true); + + return ft_ts_stop(dev); +} + +static int ft_ts_resume(struct device *dev) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + int err; + + if (!data->suspended) { + dev_dbg(dev, "Already in awake state\n"); + return 0; + } + + data->flash_enabled = true; + ft_secure_touch_stop(data, true); + + err = ft_ts_start(dev); + if (err < 0) + return err; + + if (data->charger_detection_enabled) + ft_resume_ps_chg_cm_state(data); + + return 0; +} + +static const struct dev_pm_ops ft_ts_pm_ops = { +#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)) + .suspend = ft_ts_suspend, + .resume = ft_ts_resume, +#endif +}; + +#else +static int ft_ts_suspend(struct device *dev) +{ + return 0; +} + +static int ft_ts_resume(struct device *dev) +{ + return 0; +} + +#endif + +#if defined(CONFIG_FB) +static void fb_notify_resume_work(struct work_struct *work) +{ + struct ft_ts_data *data = + container_of(work, struct ft_ts_data, fb_notify_work); + ft_ts_resume(&data->client->dev); +} + +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *d) +{ + struct fb_event *evdata = d; + int *blank; + struct ft_ts_data *data = + container_of(self, struct ft_ts_data, fb_notif); + + if ((event == FB_EARLY_EVENT_BLANK || event == FB_EVENT_BLANK) && + evdata && evdata->data && data && data->client) { + blank = evdata->data; + pr_debug("fb notification: event = %lu blank = %d\n", event, + *blank); + if (data->pdata->resume_in_workqueue) { + if (event == FB_EARLY_EVENT_BLANK) { + if (*blank != FB_BLANK_POWERDOWN) + return 0; + flush_work(&data->fb_notify_work); + ft_ts_suspend(&data->client->dev); + } else if (*blank == FB_BLANK_UNBLANK || + (*blank == FB_BLANK_NORMAL && + data->suspended)) { + schedule_work(&data->fb_notify_work); + } + } else { + if (event == FB_EARLY_EVENT_BLANK) { + if (*blank != FB_BLANK_POWERDOWN) + return 0; + ft_ts_suspend(&data->client->dev); + } else if (*blank == FB_BLANK_UNBLANK || + (*blank == FB_BLANK_NORMAL && + data->suspended)) { + ft_ts_resume(&data->client->dev); + } + } + } + + return 0; +} +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void ft_ts_early_suspend(struct early_suspend *handler) +{ + struct ft_ts_data *data = container_of(handler, + struct ft_ts_data, + early_suspend); + + /* + * During early suspend/late resume, the driver doesn't access xPU/SMMU + * protected HW resources. So, there is no compelling need to block, + * but notifying the userspace that a power event has occurred is + * enough. Hence 'blocking' variable can be set to false. + */ + ft_secure_touch_stop(data, false); + ft_ts_suspend(&data->client->dev); +} + +static void ft_ts_late_resume(struct early_suspend *handler) +{ + struct ft_ts_data *data = container_of(handler, + struct ft_ts_data, + early_suspend); + + ft_secure_touch_stop(data, false); + ft_ts_resume(&data->client->dev); +} +#endif + +static int ft_set_charger_state(struct ft_ts_data *data, + bool enable) +{ + int retval; + struct device *dev = &data->client->dev; + + /* disable IRQ to set CHARGER_STATE reg */ + ft_irq_disable(data); + if (enable) { + retval = ft_write_reg(data->client, + FT_REG_CHG_STATE, 1); + if (retval < 0) + dev_err(dev, "enable chg state failed(%d)\n", + retval); + else + dev_info(dev, "set chg state\n"); + } else { + retval = ft_write_reg(data->client, + FT_REG_CHG_STATE, 0); + if (retval < 0) + dev_err(dev, "enable chg state failed(%d)\n", + retval); + else + dev_info(dev, "unset chg state\n"); + } + ft_irq_enable(data); + + return (retval > 0) ? 0 : retval; +} + +static int ps_get_state(struct ft_ts_data *data, + struct power_supply *psy, bool *present) +{ + struct device *dev = &data->client->dev; + union power_supply_propval pval = {0}; + int retval; + + retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, + &pval); + if (retval) { + dev_err(dev, "%s psy get property failed\n", psy->desc->name); + return retval; + } + *present = (pval.intval) ? true : false; + dev_dbg(dev, "%s is %s\n", psy->desc->name, + (*present) ? "present" : "not present"); + return 0; +} + +static void ft_update_ps_chg_cm_state( + struct ft_ts_data *data, bool present) +{ + struct config_modifier *cm = NULL; + + down(&data->modifiers.list_sema); + list_for_each_entry(cm, &data->modifiers.mod_head, link) { + if (cm->id == FT_MOD_CHARGER) { + cm->effective = present; + break; + } + } + up(&data->modifiers.list_sema); +} + +static void ft_resume_ps_chg_cm_state(struct ft_ts_data *data) +{ + struct config_modifier *cm = NULL; + bool is_active = false; + + down(&data->modifiers.list_sema); + list_for_each_entry(cm, &data->modifiers.mod_head, link) { + if (cm->id == FT_MOD_CHARGER) { + is_active = cm->effective; + break; + } + } + up(&data->modifiers.list_sema); + + /* FT_REG_CHG_STATE bit is 0 when powerup + * So only need set this bit when need to be set as 1 + */ + if (is_active) + ft_set_charger_state(data, true); +} + +static void ps_notify_callback_work(struct work_struct *work) +{ + struct ft_ts_data *data = + container_of(work, struct ft_ts_data, ps_notify_work); + bool present = data->ps_is_present; + struct device *dev = &data->client->dev; + int retval; + + /* enable IC if it in suspend state */ + if (data->suspended) { + dev_dbg(dev, "charger resumes tp ic\n"); + ft_ts_resume(&data->client->dev); + } + + retval = ft_set_charger_state(data, present); + if (retval) { + dev_err(dev, "set charger state failed rc=%d\n", retval); + return; + } + + ft_update_ps_chg_cm_state(data, present); +} + +static int ps_notify_callback(struct notifier_block *self, + unsigned long event, void *p) +{ + struct ft_ts_data *data = + container_of(self, struct ft_ts_data, ps_notif); + struct power_supply *psy = p; + struct device *dev = NULL; + bool present; + int retval; + + if ((event == PSY_EVENT_PROP_ADDED || event == PSY_EVENT_PROP_CHANGED) + && psy && psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, "usb", sizeof("usb")) && data) { + dev = &data->client->dev; + dev_dbg(dev, "ps notification: event = %lu\n", event); + retval = ps_get_state(data, psy, &present); + if (retval) { + dev_err(dev, "psy get property failed\n"); + return retval; + } + + if (event == PSY_EVENT_PROP_CHANGED) { + if (data->ps_is_present == present) { + dev_info(dev, "ps present state not change\n"); + return 0; + } + } + data->ps_is_present = present; + schedule_work(&data->ps_notify_work); + } + + return 0; +} + +static void ft_detection_work(struct work_struct *work) +{ + struct ft_exp_fn *exp_fhandler, *next_list_entry; + struct ft_ts_data *data; + int error; + bool det_again = false; + + mutex_lock(&exp_fn_ctrl_mutex); + data = exp_fn_ctrl.data_ptr; + mutex_unlock(&exp_fn_ctrl_mutex); + + if (data == NULL) { + if (exp_fn_ctrl.det_workqueue) + queue_delayed_work(exp_fn_ctrl.det_workqueue, + &exp_fn_ctrl.det_work, + msecs_to_jiffies(EXP_FN_DET_INTERVAL)); + return; + } + + mutex_lock(&exp_fn_ctrl.list_mutex); + if (list_empty(&exp_fn_ctrl.fn_list)) + goto release_mutex; + + list_for_each_entry_safe(exp_fhandler, + next_list_entry, + &exp_fn_ctrl.fn_list, + link) { + if (exp_fhandler->func_init == NULL) { + if (exp_fhandler->inserted == true) { + if (exp_fhandler->func_init != NULL) + exp_fhandler->func_remove(data); + list_del(&exp_fhandler->link); + kfree(exp_fhandler); + } + continue; + } + + if (exp_fhandler->inserted == true) + continue; + + error = exp_fhandler->func_init(data); + if (error < 0) { + pr_err("Failed to init exp function. err=%d\n", error); + det_again = true; + continue; + } + exp_fhandler->inserted = true; + } + +release_mutex: + mutex_unlock(&exp_fn_ctrl.list_mutex); + if (det_again) { + if (exp_fn_ctrl.det_workqueue) + queue_delayed_work(exp_fn_ctrl.det_workqueue, + &exp_fn_ctrl.det_work, + msecs_to_jiffies(EXP_FN_DET_INTERVAL)); + } +} + +void ft_new_function(bool insert, + int (*func_init)(struct ft_ts_data *data), + void (*func_remove)(struct ft_ts_data *data), + void (*func_attn)(struct ft_ts_data *data, + unsigned char intr_mask)) +{ + struct ft_exp_fn *exp_fhandler; + + mutex_lock(&exp_fn_ctrl_mutex); + if (!exp_fn_ctrl.inited) { + mutex_init(&exp_fn_ctrl.list_mutex); + INIT_LIST_HEAD(&exp_fn_ctrl.fn_list); + exp_fn_ctrl.det_workqueue = + create_singlethread_workqueue("rmi_det_workqueue"); + if (IS_ERR_OR_NULL(exp_fn_ctrl.det_workqueue)) + pr_err("unable to create a workqueue\n"); + INIT_DELAYED_WORK(&exp_fn_ctrl.det_work, + ft_detection_work); + exp_fn_ctrl.inited = true; + } + mutex_unlock(&exp_fn_ctrl_mutex); + + mutex_lock(&exp_fn_ctrl.list_mutex); + if (insert) { + exp_fhandler = kzalloc(sizeof(*exp_fhandler), GFP_KERNEL); + if (!exp_fhandler) { + pr_err("failed to alloc mem for expansion function\n"); + goto exit; + } + exp_fhandler->func_init = func_init; + exp_fhandler->func_attn = func_attn; + exp_fhandler->func_remove = func_remove; + exp_fhandler->inserted = false; + list_add_tail(&exp_fhandler->link, &exp_fn_ctrl.fn_list); + } else { + list_for_each_entry(exp_fhandler, &exp_fn_ctrl.fn_list, link) { + if (exp_fhandler->func_init == func_init) { + exp_fhandler->inserted = false; + exp_fhandler->func_init = NULL; + exp_fhandler->func_attn = NULL; + goto exit; + } + } + } + +exit: + mutex_unlock(&exp_fn_ctrl.list_mutex); + if (exp_fn_ctrl.det_workqueue) + queue_delayed_work(exp_fn_ctrl.det_workqueue, + &exp_fn_ctrl.det_work, 0); +} +EXPORT_SYMBOL(ft_new_function); + +static int fps_notifier_callback(struct notifier_block *self, + unsigned long event, void *p) +{ + int fps_state = *(int *)p; + struct ft_ts_data *data = + container_of(self, struct ft_ts_data, fps_notif); + + if (data && event == 0xBEEF && data->client) { + struct config_modifier *cm = + ft_modifier_by_id(data, FT_MOD_FPS); + if (!cm) { + dev_err(&data->client->dev, + "No FPS modifier found\n"); + goto done; + } + + if (fps_state) {/* on */ + data->clipping_on = true; + data->clipa = cm->clipa; + cm->effective = true; + } else {/* off */ + data->clipping_on = false; + data->clipa = NULL; + cm->effective = false; + } + pr_info("FPS: clipping is %s\n", + data->clipping_on ? "ON" : "OFF"); + } +done: + return 0; +} + +static int ft_fps_register_init(struct ft_ts_data *data) +{ + int retval; + + retval = FPS_register_notifier(&data->fps_notif, 0xBEEF, false); + if (retval < 0) { + dev_err(&data->client->dev, + "Failed to register fps_notifier: %d\n", retval); + return retval; + } + data->is_fps_registered = true; + dev_info(&data->client->dev, + "Register fps_notifier OK\n"); + + return 0; +} + +static int ft_auto_cal(struct i2c_client *client) +{ + struct ft_ts_data *data = i2c_get_clientdata(client); + u8 temp = 0, i; + + /* set to factory mode */ + msleep(2 * data->pdata->soft_rst_dly); + ft_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE); + msleep(data->pdata->soft_rst_dly); + + /* start calibration */ + ft_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_START); + msleep(2 * data->pdata->soft_rst_dly); + for (i = 0; i < FT_CAL_RETRY; i++) { + ft_read_reg(client, FT_REG_CAL, &temp); + /* return to normal mode, calibration finish */ + if (((temp & FT_CAL_MASK) >> FT_4BIT_SHIFT) == FT_CAL_FIN) + break; + } + + /*calibration OK */ + msleep(2 * data->pdata->soft_rst_dly); + ft_write_reg(client, FT_REG_DEV_MODE, FT_FACTORYMODE_VALUE); + msleep(data->pdata->soft_rst_dly); + + /* store calibration data */ + ft_write_reg(client, FT_DEV_MODE_REG_CAL, FT_CAL_STORE); + msleep(2 * data->pdata->soft_rst_dly); + + /* set to normal mode */ + ft_write_reg(client, FT_REG_DEV_MODE, FT_WORKMODE_VALUE); + msleep(2 * data->pdata->soft_rst_dly); + + return 0; +} + +static int ft_fw_upgrade_start(struct i2c_client *client, + const u8 *data, u32 data_len) +{ + struct ft_ts_data *ts_data = i2c_get_clientdata(client); + struct fw_upgrade_info info = ts_data->pdata->info; + u8 reset_reg; + u8 w_buf[FT_MAX_WR_BUF] = {0}, r_buf[FT_MAX_RD_BUF] = {0}; + u8 pkt_buf[FT_FW_PKT_LEN + FT_FW_PKT_META_LEN]; + int i, j, temp; + u32 pkt_num, pkt_len; + u8 is_5336_new_bootloader = false; + u8 is_5336_fwsize_30 = false; + u8 fw_ecc; + + /* determine firmware size */ + if (*(data + data_len - FT_BLOADER_SIZE_OFF) == FT_BLOADER_NEW_SIZE) + is_5336_fwsize_30 = true; + else + is_5336_fwsize_30 = false; + + for (i = 0, j = 0; i < FT_UPGRADE_LOOP; i++) { + msleep(FT_EARSE_DLY_MS); + /* reset - write 0xaa and 0x55 to reset register */ + if (ts_data->family_id == FT6X06_ID + || ts_data->family_id == FT6X36_ID) + reset_reg = FT_RST_CMD_REG2; + else + reset_reg = FT_RST_CMD_REG1; + + ft_write_reg(client, reset_reg, FT_UPGRADE_AA); + msleep(info.delay_aa); + + ft_write_reg(client, reset_reg, FT_UPGRADE_55); + if (i <= (FT_UPGRADE_LOOP / 2)) + msleep(info.delay_55 + i * 3); + else + msleep(info.delay_55 - (i - (FT_UPGRADE_LOOP / 2)) * 2); + + /* Set i2c to std i2c mode */ + w_buf[0] = FT_REG_I2C_MODE; + w_buf[1] = FT_VAL_I2C_MODE; + w_buf[2] = FT_VAL_I2C_MODE_STD; + ft_i2c_read(client, w_buf, 3, r_buf, 3); + if (r_buf[0] != FT_REG_I2C_MODE || + r_buf[1] != FT_VAL_I2C_MODE || + r_buf[2] != FT_VAL_I2C_MODE_STD_CFM) { + dev_err(&client->dev, + "set std i2c error. r_val = 0x%02x%02x%02x\n", + r_buf[0], r_buf[1], r_buf[2]); + continue; + } + + /* Enter upgrade mode */ + w_buf[0] = FT_UPGRADE_55; + ft_i2c_write(client, w_buf, 1); + usleep_range(FT_55_AA_DLY_NS, FT_55_AA_DLY_NS + 1); + w_buf[0] = FT_UPGRADE_AA; + ft_i2c_write(client, w_buf, 1); + + /* check READ_ID */ + msleep(info.delay_readid); + w_buf[0] = FT_READ_ID_REG; + w_buf[1] = 0x00; + w_buf[2] = 0x00; + w_buf[3] = 0x00; + + ft_i2c_read(client, w_buf, 4, r_buf, 2); + + if (r_buf[0] != info.upgrade_id_1 + || r_buf[1] != info.upgrade_id_2) { + dev_err(&client->dev, "Upgrade ID mismatch(%d), IC=0x%x 0x%x, info=0x%x 0x%x\n", + i, r_buf[0], r_buf[1], + info.upgrade_id_1, info.upgrade_id_2); + } else + break; + } + + if (i >= FT_UPGRADE_LOOP) { + dev_err(&client->dev, "Abort upgrade\n"); + return -EIO; + } + + w_buf[0] = 0xcd; + ft_i2c_read(client, w_buf, 1, r_buf, 1); + + if (r_buf[0] <= 4) + is_5336_new_bootloader = FT_BLOADER_VERSION_LZ4; + else if (r_buf[0] == 7) + is_5336_new_bootloader = FT_BLOADER_VERSION_Z7; + else if (r_buf[0] >= 0x0f && + ((ts_data->family_id == FT_FT5336_FAMILY_ID_0x11) || + (ts_data->family_id == FT_FT5336_FAMILY_ID_0x12) || + (ts_data->family_id == FT_FT5336_FAMILY_ID_0x13) || + (ts_data->family_id == FT_FT5336_FAMILY_ID_0x14))) + is_5336_new_bootloader = FT_BLOADER_VERSION_GZF; + else + is_5336_new_bootloader = FT_BLOADER_VERSION_LZ4; + + dev_dbg(&client->dev, "bootloader type=%d, r_buf=0x%x, family_id=0x%x\n", + is_5336_new_bootloader, r_buf[0], ts_data->family_id); + /* is_5336_new_bootloader = FT_BLOADER_VERSION_GZF; */ + + /* erase app and panel paramenter area */ + w_buf[0] = FT_ERASE_APP_REG; + ft_i2c_write(client, w_buf, 1); + msleep(info.delay_erase_flash); + + if (is_5336_fwsize_30) { + w_buf[0] = FT_ERASE_PANEL_REG; + ft_i2c_write(client, w_buf, 1); + } + msleep(FT_EARSE_DLY_MS); + + w_buf[0] = FT_REG_FW_LEN; + w_buf[1] = (u8)((data_len>>16) & 0xff); + w_buf[2] = (u8)((data_len>>8) & 0xff); + w_buf[3] = (u8)((data_len) & 0xff); + ft_i2c_write(client, w_buf, 4); + + pkt_num = (data_len) / FT_FW_PKT_LEN; + pkt_len = FT_FW_PKT_LEN; + pkt_buf[0] = FT_FW_START_REG; + pkt_buf[1] = 0x00; + fw_ecc = 0; + + for (i = 0; i < pkt_num; i++) { + temp = i * FT_FW_PKT_LEN; + pkt_buf[2] = (u8) (temp >> FT_8BIT_SHIFT); + pkt_buf[3] = (u8) temp; + pkt_buf[4] = (u8) (pkt_len >> FT_8BIT_SHIFT); + pkt_buf[5] = (u8) pkt_len; + + for (j = 0; j < FT_FW_PKT_LEN; j++) { + pkt_buf[6 + j] = data[i * FT_FW_PKT_LEN + j]; + fw_ecc ^= pkt_buf[6 + j]; + } + + ft_i2c_write(client, pkt_buf, + FT_FW_PKT_LEN + FT_FW_PKT_META_LEN); + msleep(FT_FW_PKT_DLY_MS); + } + + /* send remaining bytes */ + if ((data_len) % FT_FW_PKT_LEN > 0) { + temp = pkt_num * FT_FW_PKT_LEN; + pkt_buf[2] = (u8) (temp >> FT_8BIT_SHIFT); + pkt_buf[3] = (u8) temp; + temp = (data_len) % FT_FW_PKT_LEN; + pkt_buf[4] = (u8) (temp >> FT_8BIT_SHIFT); + pkt_buf[5] = (u8) temp; + + for (i = 0; i < temp; i++) { + pkt_buf[6 + i] = data[pkt_num * FT_FW_PKT_LEN + i]; + fw_ecc ^= pkt_buf[6 + i]; + } + + ft_i2c_write(client, pkt_buf, temp + FT_FW_PKT_META_LEN); + msleep(FT_FW_PKT_DLY_MS); + } + + /* send the finishing packet */ + if (is_5336_new_bootloader == FT_BLOADER_VERSION_LZ4 || + is_5336_new_bootloader == FT_BLOADER_VERSION_Z7) { + for (i = 0; i < FT_FINISHING_PKT_LEN_OLD_FW; i++) { + if (is_5336_new_bootloader == FT_BLOADER_VERSION_Z7) + temp = FT_MAGIC_BLOADER_Z7 + i; + else if (is_5336_new_bootloader == + FT_BLOADER_VERSION_LZ4) + temp = FT_MAGIC_BLOADER_LZ4 + i; + pkt_buf[2] = (u8)(temp >> 8); + pkt_buf[3] = (u8)temp; + temp = 1; + pkt_buf[4] = (u8)(temp >> 8); + pkt_buf[5] = (u8)temp; + pkt_buf[6] = data[data_len + i]; + fw_ecc ^= pkt_buf[6]; + + ft_i2c_write(client, + pkt_buf, temp + FT_FW_PKT_META_LEN); + msleep(FT_FW_PKT_DLY_MS); + } + } else if (is_5336_new_bootloader == FT_BLOADER_VERSION_GZF) { + for (i = 0; i < FT_FINISHING_PKT_LEN_NEW_FW; i++) { + if (is_5336_fwsize_30) + temp = FT_MAGIC_BLOADER_GZF_30 + i; + else + temp = FT_MAGIC_BLOADER_GZF + i; + pkt_buf[2] = (u8)(temp >> 8); + pkt_buf[3] = (u8)temp; + temp = 1; + pkt_buf[4] = (u8)(temp >> 8); + pkt_buf[5] = (u8)temp; + pkt_buf[6] = data[data_len + i]; + fw_ecc ^= pkt_buf[6]; + + ft_i2c_write(client, + pkt_buf, temp + FT_FW_PKT_META_LEN); + msleep(FT_FW_PKT_DLY_MS); + + } + } + + /* verify checksum */ + w_buf[0] = FT_REG_ECC; + ft_i2c_read(client, w_buf, 1, r_buf, 1); + if (r_buf[0] != fw_ecc) { + dev_err(&client->dev, "ECC error! dev_ecc=%02x fw_ecc=%02x\n", + r_buf[0], fw_ecc); + return -EIO; + } + + /* reset */ + w_buf[0] = FT_REG_RESET_FW; + ft_i2c_write(client, w_buf, 1); + msleep(ts_data->pdata->soft_rst_dly); + + dev_info(&client->dev, "Firmware upgrade successful\n"); + + return 0; +} + +static int ft_fw_upgrade(struct device *dev, bool force) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + const struct firmware *fw = NULL; + int rc; + u8 fw_file_maj, fw_file_min, fw_file_sub_min, fw_file_vendor_id; + bool fw_upgrade = false; + + if (data->suspended) { + dev_err(dev, "Device is in suspend state: Exit FW upgrade\n"); + return -EBUSY; + } + + rc = request_firmware(&fw, data->fw_name, dev); + if (rc < 0) { + dev_err(dev, "Request firmware failed - %s (%d)\n", + data->fw_name, rc); + return rc; + } + + if (fw->size < FT_FW_MIN_SIZE || fw->size > FT_FW_MAX_SIZE) { + dev_err(dev, "Invalid firmware size (%zu)\n", fw->size); + rc = -EIO; + goto rel_fw; + } + + if (data->family_id == FT6X36_ID) { + fw_file_maj = FT_FW_FILE_MAJ_VER_FT6X36(fw); + fw_file_vendor_id = FT_FW_FILE_VENDOR_ID_FT6X36(fw); + } else { + fw_file_maj = FT_FW_FILE_MAJ_VER(fw); + fw_file_vendor_id = FT_FW_FILE_VENDOR_ID(fw); + } + fw_file_min = FT_FW_FILE_MIN_VER(fw); + fw_file_sub_min = FT_FW_FILE_SUB_MIN_VER(fw); + fw_file_vendor_id = FT_FW_FILE_VENDOR_ID(fw); + + dev_info(dev, "Current firmware: %d.%d.%d", data->fw_ver[0], + data->fw_ver[1], data->fw_ver[2]); + dev_info(dev, "New firmware: %d.%d.%d", fw_file_maj, + fw_file_min, fw_file_sub_min); + + if (force) + fw_upgrade = true; + else if ((data->fw_ver[0] < fw_file_maj) && + data->fw_vendor_id == fw_file_vendor_id) + fw_upgrade = true; + + if (!fw_upgrade) { + dev_info(dev, "Exiting fw upgrade...\n"); + rc = -EFAULT; + goto rel_fw; + } + + /* start firmware upgrade */ + if (FT_FW_CHECK(fw, data)) { + rc = ft_fw_upgrade_start(data->client, fw->data, fw->size); + if (rc < 0) + dev_err(dev, "update failed (%d). try later...\n", rc); + else if (data->pdata->info.auto_cal) + ft_auto_cal(data->client); + } else { + dev_err(dev, "FW format error\n"); + rc = -EIO; + } + + ft_update_fw_ver(data); + ft_update_fw_id(data); + + FT_STORE_TS_DBG_INFO(data->ts_info, data->family_id, data->pdata->name, + data->pdata->num_max_touches, data->pdata->group_id, + data->pdata->fw_vkey_support ? "yes" : "no", + data->pdata->fw_name, data->fw_ver[0], + data->fw_ver[1], data->fw_ver[2]); + FT_STORE_TS_INFO(ts_info_buff, data->family_id, data->fw_ver[0], + data->fw_ver[1], data->fw_ver[2]); +rel_fw: + release_firmware(fw); + return rc; +} + +static ssize_t ft_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + bool val; + + mutex_lock(&data->input_dev->mutex); + val = data->suspended; + mutex_unlock(&data->input_dev->mutex); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + val == false && + data->flash_enabled); +} + +static DEVICE_ATTR(poweron, 0444, ft_poweron_show, NULL); + +static ssize_t ft_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%s\n", + data->pdata->name); +} + +static DEVICE_ATTR(productinfo, 0444, ft_productinfo_show, NULL); + +static ssize_t ft_force_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + unsigned int input; + + if (kstrtouint(buf, 10, &input) != 1) + return -EINVAL; + + data->force_reflash = (input == 0) ? false : true; + + return count; +} + +static DEVICE_ATTR(forcereflash, 0220, NULL, ft_force_reflash_store); + +static ssize_t ft_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + (data->loading_fw) ? 1 : 0); +} + +static DEVICE_ATTR(flashprog, 0444, ft_flashprog_show, NULL); + +static ssize_t ft_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + int retval; + char prefix[FT_FW_NAME_MAX_LEN] = "focaltech"; + char template[FT_FW_NAME_MAX_LEN]; + + if (count > FT_FW_NAME_MAX_LEN) { + dev_err(&data->client->dev, + "%s: FW filename is too long\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (data->suspended) { + dev_err(&data->client->dev, + "%s: In suspend state, try again later\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (data->loading_fw) { + dev_err(&data->client->dev, + "%s: In FW flashing state, try again later\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!data->force_reflash) { + if (strncmp(buf, prefix, + strnlen(prefix, sizeof(prefix)))) { + dev_err(&data->client->dev, + "%s: FW does not belong to Focaltech\n", + __func__); + retval = -EINVAL; + goto exit; + } + + snprintf(template, sizeof(template), "-%s-", + data->pdata->name); + if (!strnstr(buf + strnlen(prefix, sizeof(prefix)), template, + count)) { + dev_err(&data->client->dev, + "%s: FW does not belong to %s\n", + __func__, + data->pdata->name); + retval = -EINVAL; + goto exit; + } + } + + strlcpy(data->fw_name, buf, count); + dev_dbg(&data->client->dev, + "%s: FW filename: %s\n", + __func__, + data->fw_name); + + mutex_lock(&data->input_dev->mutex); + ft_irq_disable(data); + data->loading_fw = true; + if(data->pdata->fw_update_mmi) { + retval = fts_ctpm_auto_upgrade(data->client, + data->fw_name, + data->pdata); + } else { + retval = ft_fw_upgrade(dev, true); + } + + if (retval) + dev_err(&data->client->dev, + "%s: FW %s upgrade failed\n", + __func__, + data->pdata->name); + ft_irq_enable(data); + data->loading_fw = false; + mutex_unlock(&data->input_dev->mutex); + + retval = count; + + ft_update_fw_ver(data); + ft_update_fw_vendor_id(data); + ft_update_fw_id(data); +exit: + data->fw_name[0] = 0; + return retval; +} + +static DEVICE_ATTR(doreflash, 0220, NULL, ft_do_reflash_store); + +static ssize_t ft_build_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%02x%02x-%02x\n", + data->fw_id[0], data->fw_id[1], data->fw_ver[0]); +} + +static DEVICE_ATTR(buildid, 0444, ft_build_id_show, NULL); + +static ssize_t ft_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + struct ft_ts_data *data = dev_get_drvdata(dev); + + if ('1' != buf[0]) { + pr_err("Invalid argument for reset\n"); + return -EINVAL; + } + + mutex_lock(&data->input_dev->mutex); + ft_irq_disable(data); + retval = gpio_direction_output(data->pdata->reset_gpio, 0); + if (retval == 0) { + gpio_set_value_cansleep(data->pdata->reset_gpio, 0); + msleep(data->pdata->hard_rst_dly); + gpio_set_value_cansleep(data->pdata->reset_gpio, 1); + } else { + dev_err(&data->client->dev, + "set direction for reset gpio failed\n"); + } + msleep(100); + ft_irq_enable(data); + mutex_unlock(&data->input_dev->mutex); + return count; +} + +static DEVICE_ATTR(reset, 0220, NULL, ft_reset_store); + +static ssize_t ft_drv_irq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%s\n", + data->irq_enabled ? "ENABLED" : "DISABLED"); +} + +static ssize_t ft_panel_supplier_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + + if (data->panel_supplier) + return scnprintf(buf, PAGE_SIZE, "%s\n", + data->panel_supplier); + return 0; +} +static DEVICE_ATTR(panel_supplier, 0444, ft_panel_supplier_show, NULL); + +static ssize_t ft_drv_irq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value = 0; + int err = 0; + struct ft_ts_data *data = dev_get_drvdata(dev); + + err = kstrtoul(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + switch (value) { + case 0: + /* Disable irq */ + ft_irq_disable(data); + break; + case 1: + /* Enable irq */ + ft_irq_enable(data); + break; + default: + pr_err("Invalid value\n"); + return -EINVAL; + } + return count; +} + +static DEVICE_ATTR(drv_irq, 0664, ft_drv_irq_show, ft_drv_irq_store); + +static ssize_t ft_ud_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + ssize_t total = 0; + + total += scnprintf(buf + total, PAGE_SIZE - total, "display:\n"); + for (i = 0; i < display_ud_stats.ud_len; i++) + total += scnprintf(buf + total, PAGE_SIZE - total, + "[%d]: full cycles-%u, mismatch-%d\n", i, + display_ud[i].counter, + display_ud[i].mismatch); + + return total; +} + +static DEVICE_ATTR(tsi, 0444, ft_ud_show, NULL); + +static const struct attribute *ft_attrs[] = { + &dev_attr_poweron.attr, + &dev_attr_productinfo.attr, + &dev_attr_forcereflash.attr, + &dev_attr_flashprog.attr, + &dev_attr_doreflash.attr, + &dev_attr_buildid.attr, + &dev_attr_reset.attr, + &dev_attr_panel_supplier.attr, + &dev_attr_drv_irq.attr, + &dev_attr_tsi.attr, + NULL +}; + +static ssize_t ts_info_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + strlcpy(buf, ts_info_buff, FT_INFO_MAX_LEN); + return strnlen(buf, FT_INFO_MAX_LEN); +} +static struct kobj_attribute ts_info_attr = __ATTR_RO(ts_info); + +#include +#include + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ft_ts_data *data = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!data) { + pr_err("cannot get ft_data pointer\n"); + return (ssize_t)0; + } + path = kobject_get_path(&data->client->dev.kobj, GFP_KERNEL); + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "focaltech"); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_NULL +}; + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +static int ft_ts_sysfs_class(void *_data, bool create) +{ + struct ft_ts_data *data = _data; + const struct ft_ts_platform_data *pdata = data->pdata; + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + static struct class *touchscreen_class; + static struct device *ts_class_dev; + static int minor; + + if (create) { + minor = input_get_new_minor(data->client->addr, + 1, false); + if (minor < 0) + minor = input_get_new_minor(TSDEV_MINOR_BASE, + TSDEV_MINOR_MAX, true); + pr_info("assigned minor %d\n", minor); + + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + + ts_class_dev = device_create(touchscreen_class, NULL, + MKDEV(INPUT_MAJOR, minor), + data, "%s", pdata->name); + if (IS_ERR(ts_class_dev)) { + error = PTR_ERR(ts_class_dev); + ts_class_dev = NULL; + return error; + } + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + error = device_create_file(ts_class_dev, &attrs[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + } else { + if (!touchscreen_class || !ts_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(ts_class_dev, &attrs[i]); + + device_unregister(ts_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor)); + ts_class_dev = NULL; + class_unregister(touchscreen_class); + pr_err("error creating touchscreen class\n"); + + return -ENODEV; +} + +static bool ft_debug_addr_is_valid(int addr) +{ + if (addr < 0 || addr > 0xFF) { + pr_err("FT reg address is invalid: 0x%x\n", addr); + return false; + } + + return true; +} + +static int ft_debug_data_set(void *_data, u64 val) +{ + struct ft_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + + if (ft_debug_addr_is_valid(data->addr)) + dev_info(&data->client->dev, + "Writing into FT registers not supported\n"); + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +static int ft_debug_data_get(void *_data, u64 *val) +{ + struct ft_ts_data *data = _data; + int rc; + u8 reg = 0; + + mutex_lock(&data->input_dev->mutex); + + if (ft_debug_addr_is_valid(data->addr)) { + rc = ft_read_reg(data->client, data->addr, ®); + if (rc < 0) + dev_err(&data->client->dev, + "FT read register 0x%x failed (%d)\n", + data->addr, rc); + else + *val = reg; + } + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_data_fops, ft_debug_data_get, + ft_debug_data_set, "0x%02llX\n"); + +static int ft_debug_addr_set(void *_data, u64 val) +{ + struct ft_ts_data *data = _data; + + if (ft_debug_addr_is_valid(val)) { + mutex_lock(&data->input_dev->mutex); + data->addr = val; + mutex_unlock(&data->input_dev->mutex); + } + + return 0; +} + +static int ft_debug_addr_get(void *_data, u64 *val) +{ + struct ft_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + + if (ft_debug_addr_is_valid(data->addr)) + *val = data->addr; + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_addr_fops, ft_debug_addr_get, + ft_debug_addr_set, "0x%02llX\n"); + +static int ft_debug_suspend_set(void *_data, u64 val) +{ + struct ft_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + + if (val) + ft_ts_suspend(&data->client->dev); + else + ft_ts_resume(&data->client->dev); + + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +static int ft_debug_suspend_get(void *_data, u64 *val) +{ + struct ft_ts_data *data = _data; + + mutex_lock(&data->input_dev->mutex); + *val = data->suspended; + mutex_unlock(&data->input_dev->mutex); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(debug_suspend_fops, ft_debug_suspend_get, + ft_debug_suspend_set, "%lld\n"); + +static int ft_debug_dump_info(struct seq_file *m, void *v) +{ + struct ft_ts_data *data = m->private; + + seq_printf(m, "%s\n", data->ts_info); + + return 0; +} + +static int debugfs_dump_info_open(struct inode *inode, struct file *file) +{ + return single_open(file, ft_debug_dump_info, inode->i_private); +} + +static const struct file_operations debug_dump_info_fops = { + .owner = THIS_MODULE, + .open = debugfs_dump_info_open, + .read = seq_read, + .release = single_release, +}; + +static ssize_t ft_proc_read(struct file *filp, char __user *buff, + size_t count, loff_t *ppos) +{ + unsigned char *read_buf = NULL; + size_t read_buf_len = (count > PAGE_SIZE) ? count : PAGE_SIZE; + u8 regval = 0x00; + u8 regaddr = 0x00; + struct ft_ts_data *data = PDE_DATA(filp->f_inode); + int num_read_chars = 0; + int retval; + + if (data == NULL) { + pr_err("%s: cannot get ts data\n", __func__); + return -EFAULT; + } + read_buf = kmalloc(read_buf_len, GFP_KERNEL); + if (read_buf == NULL) + return -ENOMEM; + + mutex_lock(&data->input_dev->mutex); + switch (data->proc_operate_mode) { + case PROC_UPGRADE: + regaddr = FT_REG_FW_VER; + retval = ft_read_reg(data->client, + regaddr, ®val); + if (retval < 0) + num_read_chars = snprintf(read_buf, read_buf_len, + "get fw version failed.\n"); + else + num_read_chars = snprintf(read_buf, read_buf_len, + "current fw version:0x%02x\n", regval); + break; + case PROC_READ_REGISTER: + retval = ft_i2c_read(data->client, NULL, 0, read_buf, 1); + if (retval < 0) + dev_err(&data->client->dev, + "%s: read I2C error\n", __func__); + num_read_chars = 1; + break; + case PROC_READ_DATA: + retval = ft_i2c_read(data->client, + NULL, 0, read_buf, count); + if (retval < 0) + dev_err(&data->client->dev, + "%s: read I2C error\n", __func__); + num_read_chars = count; + break; + default: + break; + } + + mutex_unlock(&data->input_dev->mutex); + + if (copy_to_user(buff, read_buf, num_read_chars)) { + dev_err(&data->client->dev, + "%s: copy to user error\n", __func__); + kfree(read_buf); + return -EFAULT; + } + kfree(read_buf); + + return num_read_chars; +} + +static ssize_t ft_proc_write(struct file *filp, const char __user *buff, + size_t count, loff_t *ppos) +{ + unsigned char *write_buf = NULL; + size_t write_buf_len = count; + struct ft_ts_data *data = PDE_DATA(filp->f_inode); + int writelen = 0; + int retval; + + if (data == NULL) { + pr_err("%s: cannot get ts data\n", __func__); + return -EFAULT; + } + write_buf = kmalloc(write_buf_len, GFP_KERNEL); + if (write_buf == NULL) + return -ENOMEM; + + if (copy_from_user(write_buf, buff, count)) { + dev_err(&data->client->dev, + "%s: copy from user error\n", __func__); + kfree(write_buf); + return -EFAULT; + } + + mutex_lock(&data->input_dev->mutex); + data->proc_operate_mode = write_buf[0]; + switch (data->proc_operate_mode) { + case PROC_READ_REGISTER: + retval = ft_i2c_write(data->client, write_buf + 1, 1); + if (retval < 0) + dev_err(&data->client->dev, + "%s: write I2C error\n", __func__); + break; + case PROC_WRITE_REGISTER: + retval = ft_i2c_write(data->client, write_buf + 1, 2); + if (retval < 0) + dev_err(&data->client->dev, + "%s: write I2C error\n", __func__); + break; + case PROC_READ_DATA: + case PROC_WRITE_DATA: + writelen = count - 1; + if (writelen > 0) { + retval = ft_i2c_write(data->client, + write_buf + 1, writelen); + if (retval < 0) + dev_err(&data->client->dev, + "%s: write I2C error\n", __func__); + } + break; + default: + dev_err(&data->client->dev, + "%s: unsupport opt %d\n", + __func__, data->proc_operate_mode); + break; + } + + mutex_unlock(&data->input_dev->mutex); + kfree(write_buf); + + return count; +} + +static const struct file_operations ft_proc_ops = { + .owner = THIS_MODULE, + .read = ft_proc_read, + .write = ft_proc_write +}; + +static int ft_create_proc_entry(struct ft_ts_data *data) +{ + struct proc_dir_entry *entry = data->proc_entry; + struct i2c_client *client = data->client; + + entry = proc_create_data(FT_PROC_DIR_NAME, + 0664, + NULL, + &ft_proc_ops, + data); + if (entry == NULL) { + dev_err(&client->dev, "Couldn't create proc entry\n"); + return -ENOMEM; + } + dev_info(&client->dev, "Create proc entry success\n"); + + return 0; +} + +static void ft_remove_proc_entry(struct ft_ts_data *data) +{ + remove_proc_entry(FT_PROC_DIR_NAME, NULL); +} + +#ifdef CONFIG_OF +static int ft_get_dt_coords(struct device *dev, char *name, + struct ft_ts_platform_data *pdata) +{ + u32 coords[FT_COORDS_ARR_SIZE]; + struct property *prop; + struct device_node *np = dev->of_node; + int coords_size, rc; + + 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 != FT_COORDS_ARR_SIZE) { + dev_err(dev, "invalid %s\n", name); + return -EINVAL; + } + + rc = of_property_read_u32_array(np, name, coords, coords_size); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read %s\n", name); + return rc; + } + + if (!strcmp(name, "focaltech,panel-coords")) { + pdata->panel_minx = coords[0]; + pdata->panel_miny = coords[1]; + pdata->panel_maxx = coords[2]; + pdata->panel_maxy = coords[3]; + } else if (!strcmp(name, "focaltech,display-coords")) { + pdata->x_min = coords[0]; + pdata->y_min = coords[1]; + pdata->x_max = coords[2]; + pdata->y_max = coords[3]; + } else { + dev_err(dev, "unsupported property %s\n", name); + return -EINVAL; + } + + return 0; +} + +/* ASCII names order MUST match enum */ +static const char const *ascii_names[] = {"charger", "fps", "na"}; + +static int ft_modifier_name2id(const char *name) +{ + int i, len2cmp, chosen = -1; + + for (i = 0; i < FT_MOD_MAX; i++) { + len2cmp = min_t(int, strlen(name), strlen(ascii_names[i])); + if (!strncmp(name, ascii_names[i], len2cmp)) { + chosen = i; + break; + } + } + return chosen; +} + +static struct config_modifier *ft_modifier_by_id( + struct ft_ts_data *data, int id) +{ + struct config_modifier *cm, *found = NULL; + + down(&data->modifiers.list_sema); + list_for_each_entry(cm, &data->modifiers.mod_head, link) { + pr_debug("walk-thru: ptr=%p modifier[%s] id=%d\n", + cm, cm->name, cm->id); + if (cm->id == id) { + found = cm; + break; + } + } + up(&data->modifiers.list_sema); + pr_debug("returning modifier id=%d[%d]\n", found ? found->id : -1, id); + return found; +} + +static void ft_dt_parse_modifier(struct ft_ts_data *data, + struct device_node *parent, struct config_modifier *config, + const char *modifier_name, bool active) +{ + struct device *dev = &data->client->dev; + char node_name[64]; + struct ft_clip_area clipa; + struct device_node *np_config; + int err; + + scnprintf(node_name, 63, "%s-%s", modifier_name, + active ? "active" : "suspended"); + np_config = of_find_node_by_name(parent, node_name); + if (!np_config) { + dev_dbg(dev, "%s: node does not exist\n", node_name); + return; + } + + err = of_property_read_u32_array(np_config, "touch-clip-area", + (unsigned int *)&clipa, sizeof(clipa)/sizeof(unsigned int)); + if (!err) { + config->clipa = kzalloc(sizeof(clipa), GFP_KERNEL); + if (!config->clipa) { + dev_err(dev, "clip area allocation failure\n"); + return; + } + memcpy(config->clipa, &clipa, sizeof(clipa)); + pr_notice("using touch clip area in %s\n", node_name); + } + + of_node_put(np_config); +} + +static int ft_dt_parse_modifiers(struct ft_ts_data *data) +{ + struct device *dev = &data->client->dev; + struct device_node *np = dev->of_node; + struct device_node *np_mod; + int i, num_names, ret = 0; + char node_name[64]; + static const char **modifiers_names; + + sema_init(&data->modifiers.list_sema, 1); + INIT_LIST_HEAD(&data->modifiers.mod_head); + data->modifiers.mods_num = 0; + + num_names = of_property_count_strings(np, "config_modifier-names"); + if (num_names < 0) { + dev_err(dev, "Cannot parse config_modifier-names: %d\n", + num_names); + return -ENODEV; + } + + modifiers_names = devm_kzalloc(dev, + sizeof(*modifiers_names) * num_names, GFP_KERNEL); + if (!modifiers_names) + return -ENOMEM; + + for (i = 0; i < num_names; i++) { + ret = of_property_read_string_index(np, "config_modifier-names", + i, &modifiers_names[i]); + if (ret < 0) { + dev_err(dev, "Cannot parse modifier-names: %d\n", ret); + return ret; + } + } + + data->modifiers.mods_num = num_names; + + for (i = 0; i < num_names; i++) { + int id; + struct config_modifier *cm, *config; + + scnprintf(node_name, 63, "config_modifier-%s", + modifiers_names[i]); + np_mod = of_find_node_by_name(np, node_name); + if (!np_mod) { + dev_warn(dev, "cannot find modifier node %s\n", + node_name); + continue; + } + + /* check for duplicate nodes in devtree */ + id = ft_modifier_name2id(modifiers_names[i]); + dev_info(dev, "processing modifier %s[%d]\n", node_name, id); + list_for_each_entry(cm, &data->modifiers.mod_head, link) { + if (cm->id == id) { + dev_err(dev, "duplicate modifier node %s\n", + node_name); + return -EFAULT; + } + } + /* allocate modifier's structure */ + config = devm_kzalloc(dev, sizeof(*config), GFP_KERNEL); + if (!config) + return -ENOMEM; + + list_add_tail(&config->link, &data->modifiers.mod_head); + config->name = modifiers_names[i]; + config->id = id; + + if (of_property_read_bool(np_mod, "enable-notification")) { + switch (id) { + case FT_MOD_CHARGER: + pr_notice("using charger detection\n"); + data->charger_detection_enabled = true; + break; + case FT_MOD_FPS: + pr_notice("using fingerprint sensor detection\n"); + data->fps_detection_enabled = true; + break; + default: + pr_notice("no notification found\n"); + break; + } + } else { + config->effective = true; + dev_dbg(dev, "modifier %s enabled unconditionally\n", + node_name); + } + + dev_dbg(dev, "processing modifier %s[%d]\n", + node_name, config->id); + + ft_dt_parse_modifier(data, np_mod, config, + modifiers_names[i], true); + ft_dt_parse_modifier(data, np_mod, config, + modifiers_names[i], false); + + of_node_put(np_mod); + } + + return 0; +} + +static int ft_parse_dt(struct device *dev, + struct ft_ts_platform_data *pdata) +{ + int rc; + struct device_node *np = dev->of_node; + struct property *prop; + u32 temp_val, num_buttons; + u32 button_map[MAX_BUTTONS]; + u32 num_vendor_ids, i; + + pdata->name = "focaltech"; + rc = of_property_read_string(np, "focaltech,name", &pdata->name); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read name\n"); + return rc; + } + + rc = ft_get_dt_coords(dev, "focaltech,panel-coords", pdata); + if (rc && (rc != -EINVAL)) + return rc; + + rc = ft_get_dt_coords(dev, "focaltech,display-coords", pdata); + if (rc) + return rc; + + pdata->i2c_pull_up = of_property_read_bool(np, + "focaltech,i2c-pull-up"); + + pdata->no_force_update = of_property_read_bool(np, + "focaltech,no-force-update"); + /* 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) + return pdata->reset_gpio; + + pdata->irq_gpio = of_get_named_gpio_flags(np, "focaltech,irq-gpio", + 0, &pdata->irq_gpio_flags); + if (pdata->irq_gpio < 0) + return pdata->irq_gpio; + + pdata->fw_name = "ft_fw.bin"; + rc = of_property_read_string(np, "focaltech,fw-name", &pdata->fw_name); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw name\n"); + return rc; + } + + rc = of_property_read_u32(np, "focaltech,group-id", &temp_val); + if (!rc) + pdata->group_id = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,hard-reset-delay-ms", + &temp_val); + if (!rc) + pdata->hard_rst_dly = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,soft-reset-delay-ms", + &temp_val); + if (!rc) + pdata->soft_rst_dly = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,num-max-touches", &temp_val); + if (!rc) + pdata->num_max_touches = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "focaltech,fw-delay-aa-ms", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw delay aa\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.delay_aa = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-delay-55-ms", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw delay 55\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.delay_55 = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-upgrade-id1", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw upgrade id1\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.upgrade_id_1 = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-upgrade-id2", &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw upgrade id2\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.upgrade_id_2 = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-delay-readid-ms", + &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw delay read id\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.delay_readid = temp_val; + + rc = of_property_read_u32(np, "focaltech,fw-delay-era-flsh-ms", + &temp_val); + if (rc && (rc != -EINVAL)) { + dev_err(dev, "Unable to read fw delay erase flash\n"); + return rc; + } else if (rc != -EINVAL) + pdata->info.delay_erase_flash = temp_val; + + pdata->info.auto_cal = of_property_read_bool(np, + "focaltech,fw-auto-cal"); + + pdata->fw_vkey_support = of_property_read_bool(np, + "focaltech,fw-vkey-support"); + + pdata->ignore_id_check = of_property_read_bool(np, + "focaltech,ignore-id-check"); + + pdata->gesture_support = of_property_read_bool(np, + "focaltech,gesture-support"); + + pdata->resume_in_workqueue = of_property_read_bool(np, + "focaltech,resume-in-workqueue"); + + if (of_property_read_bool(np, "focaltech,fw-update-mmi")) { + pr_notice("using mmi firmware update\n"); + pdata->fw_update_mmi = true; + } + + if (of_property_read_bool(np, "focaltech,force-send-sleep")) { + pr_notice("using mmi force send sleep cmd\n"); + pdata->force_send_sleep = true; + } + + if (of_property_read_bool(np, "focaltech,x-flip")) { + pr_notice("using flipped X axis\n"); + pdata->x_flip = true; + } + + if (of_property_read_bool(np, "focaltech,y-flip")) { + pr_notice("using flipped Y axis\n"); + pdata->y_flip = true; + } + + rc = of_property_read_u32(np, "focaltech,family-id", &temp_val); + if (!rc) + pdata->family_id = temp_val; + else + return rc; + + prop = of_find_property(np, "focaltech,button-map", NULL); + if (prop) { + num_buttons = prop->length / sizeof(temp_val); + if (num_buttons > MAX_BUTTONS) + return -EINVAL; + + rc = of_property_read_u32_array(np, + "focaltech,button-map", button_map, + num_buttons); + if (rc) { + dev_err(dev, "Unable to read key codes\n"); + return rc; + } + } + + num_vendor_ids = of_property_count_elems_of_size(np, + "focaltech,vendor_ids", sizeof(u32)); + if ((num_vendor_ids > 0) && (num_vendor_ids < MAX_PANEL_SUPPLIERS)) { + const char *name; + + rc = of_property_read_u32_array(np, + "focaltech,vendor_ids", pdata->vendor_ids, + num_vendor_ids); + if (rc) { + dev_err(dev, "Unable to read vendor ids\n"); + return rc; + } + prop = of_find_property(np, "focaltech,vendor_names", NULL); + if (!prop) { + dev_err(dev, "Unable to read vendor names\n"); + return rc; + } + for (name = of_prop_next_string(prop, NULL), i = 0; + i < num_vendor_ids; + name = of_prop_next_string(prop, name), i++) + pdata->vendor_names[i] = name; + + pdata->num_vendor_ids = num_vendor_ids; + } + + return 0; +} +#else +static int ft_parse_dt(struct device *dev, + struct ft_ts_platform_data *pdata) +{ + return -ENODEV; +} +#endif + +static int ft_reboot(struct notifier_block *nb, + unsigned long event, + void *unused) +{ + struct ft_ts_data *data = + container_of(nb, struct ft_ts_data, ft_reboot); + + dev_info(&data->client->dev, "touch shutdown\n"); + + if (data->is_fps_registered) { + FPS_unregister_notifier(&data->fps_notif, 0xBEEF); + data->is_fps_registered = false; + } + + if (data->charger_detection_enabled) { + power_supply_unreg_notifier(&data->ps_notif); + data->charger_detection_enabled = false; + } + +#if defined(CONFIG_FB) + fb_unregister_client(&data->fb_notif); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&data->early_suspend); +#endif + + if (data->irq_enabled) { + ft_irq_disable(data); + free_irq(data->client->irq, data); + } + + if (gpio_is_valid(data->pdata->reset_gpio)) { + gpio_direction_output(data->pdata->reset_gpio, 0); + msleep(data->pdata->hard_rst_dly); + gpio_free(data->pdata->reset_gpio); + } + if (gpio_is_valid(data->pdata->irq_gpio)) + gpio_free(data->pdata->irq_gpio); + + if (data->regulator_en) { + if (data->pdata->power_on) + data->pdata->power_on(false); + else + ft_power_on(data, false); + if (data->pdata->power_init) + data->pdata->power_init(false); + else + ft_power_init(data, false); + data->regulator_en = false; + } + + return NOTIFY_DONE; +} + +static int ft_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ft_ts_platform_data *pdata; + struct ft_ts_data *data; + struct input_dev *input_dev; + struct dentry *temp; + u8 reg_value = 0; + u8 reg_addr; + u8 lic_ver = 0; + int err, retval, attr_count; + + if (client->dev.of_node) { + pdata = devm_kzalloc(&client->dev, + sizeof(struct ft_ts_platform_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + err = ft_parse_dt(&client->dev, pdata); + if (err) { + dev_err(&client->dev, "DT parsing failed\n"); + return err; + } + } else + pdata = client->dev.platform_data; + + if (!pdata) { + dev_err(&client->dev, "Invalid pdata\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "I2C not supported\n"); + return -ENODEV; + } + + data = devm_kzalloc(&client->dev, + sizeof(struct ft_ts_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->tch_data_len = FT_TCH_LEN(pdata->num_max_touches); + data->tch_data = devm_kzalloc(&client->dev, + data->tch_data_len, GFP_KERNEL); + if (!data->tch_data) + return -ENOMEM; + + data->client = client; + data->pdata = pdata; + data->force_reflash = (data->pdata->no_force_update) ? false : true; + data->flash_enabled = true; + data->irq_enabled = false; + + data->patching_enabled = true; + err = ft_dt_parse_modifiers(data); + if (err) + data->patching_enabled = false; + + i2c_set_clientdata(client, data); + + if (pdata->power_init) { + err = pdata->power_init(true); + if (err) + dev_warn(&client->dev, "power init failed"); + else + data->regulator_en = true; + } else { + err = ft_power_init(data, true); + if (err) + dev_warn(&client->dev, "power init failed"); + else + data->regulator_en = true; + } + + if (data->regulator_en) { + if (pdata->power_on) { + err = pdata->power_on(true); + if (err) { + dev_err(&client->dev, "power on failed"); + goto regulator_en_err; + } + } else { + err = ft_power_on(data, true); + if (err) { + dev_err(&client->dev, "power on failed"); + goto regulator_en_err; + } + } + } + + err = ft_ts_pinctrl_init(data); + if (!err && data->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. + */ + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_active); + if (err < 0) { + dev_err(&client->dev, + "failed to select pin to active state"); + } + } + + err = ft_gpio_configure(data, true); + if (err < 0) { + dev_err(&client->dev, + "Failed to configure the gpios\n"); + goto gpio_config_err; + } + + /* make sure CTP already finish startup process */ + msleep(data->pdata->soft_rst_dly); + + /* check the controller id */ + reg_addr = FT_REG_ID; + err = ft_i2c_read(client, ®_addr, 1, ®_value, 1); + if (err < 0) { + dev_err(&client->dev, "version read failed"); + goto free_gpio; + } + + dev_info(&client->dev, "Device ID = 0x%x\n", reg_value); + + if ((pdata->family_id != reg_value) && (!pdata->ignore_id_check)) { + dev_err(&client->dev, "%s:Unsupported controller\n", __func__); + goto free_gpio; + } + + data->family_id = pdata->family_id; + + /*get some register information */ + reg_addr = FT_REG_POINT_RATE; + ft_i2c_read(client, ®_addr, 1, ®_value, 1); + if (err < 0) + dev_err(&client->dev, "report rate read failed"); + + dev_info(&client->dev, "report rate = %dHz\n", reg_value * 10); + + reg_addr = FT_REG_THGROUP; + err = ft_i2c_read(client, ®_addr, 1, ®_value, 1); + if (err < 0) + dev_err(&client->dev, "threshold read failed"); + + dev_dbg(&client->dev, "touch threshold = %d\n", reg_value * 4); + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&client->dev, "failed to allocate input device\n"); + goto free_gpio; + } + + data->input_dev = input_dev; + input_dev->name = "focaltech_ts"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + input_set_drvdata(input_dev, data); + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + input_mt_init_slots(input_dev, pdata->num_max_touches, 0); + 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); + + err = input_register_device(input_dev); + if (err) { + dev_err(&client->dev, "Input device registration failed\n"); + goto input_register_device_err; + } + + if ((pdata->family_id == FT8006U_ID) || (pdata->family_id == FT5422U_ID)) { + fts_extra_init(client, input_dev, pdata); + } + + err = request_threaded_irq(client->irq, NULL, + ft_ts_interrupt, + /* + * the interrupt trigger mode will be set in Device Tree with property + * "interrupts", so here we just need to set the flag IRQF_ONESHOT + */ + IRQF_ONESHOT, + client->dev.driver->name, data); + if (err) { + dev_err(&client->dev, "request irq failed\n"); + goto request_irq_err; + } + + /* request_threaded_irq enable irq,so set the flag irq_enabled */ + data->irq_enabled = true; + + err = ft_ts_sysfs_class(data, true); + if (err) { + dev_err(&client->dev, "sys class files creation failed\n"); + goto sysfs_class_err; + } + + err = sysfs_create_files(&client->dev.kobj, ft_attrs); + if (err) { + dev_err(&client->dev, "sys files creation failed\n"); + goto sysfs_create_files_err; + } + + data->dir = debugfs_create_dir(FT_DEBUG_DIR_NAME, NULL); + if (data->dir == NULL || IS_ERR(data->dir)) { + pr_err("debugfs_create_dir failed(%ld)\n", PTR_ERR(data->dir)); + err = PTR_ERR(data->dir); + goto debugfs_create_dir_err; + } + + temp = debugfs_create_file("addr", S_IRUSR | S_IWUSR, data->dir, data, + &debug_addr_fops); + if (temp == NULL || IS_ERR(temp)) { + pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); + err = PTR_ERR(temp); + goto free_debug_dir; + } + + temp = debugfs_create_file("data", S_IRUSR | S_IWUSR, data->dir, data, + &debug_data_fops); + if (temp == NULL || IS_ERR(temp)) { + pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); + err = PTR_ERR(temp); + goto free_debug_dir; + } + + temp = debugfs_create_file("suspend", S_IRUSR | S_IWUSR, data->dir, + data, &debug_suspend_fops); + if (temp == NULL || IS_ERR(temp)) { + pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); + err = PTR_ERR(temp); + goto free_debug_dir; + } + + temp = debugfs_create_file("dump_info", S_IRUSR | S_IWUSR, data->dir, + data, &debug_dump_info_fops); + if (temp == NULL || IS_ERR(temp)) { + pr_err("debugfs_create_file failed: rc=%ld\n", PTR_ERR(temp)); + err = PTR_ERR(temp); + goto free_debug_dir; + } + + err = ft_create_proc_entry(data); + if (err) { + dev_err(&client->dev, "create proc entry failed"); + goto free_debug_dir; + } + + data->ts_info = devm_kzalloc(&client->dev, + FT_INFO_MAX_LEN, GFP_KERNEL); + if (!data->ts_info) + goto free_proc_entry; + + /*creation touch panel info kobj*/ + data->ts_info_kobj = kobject_create_and_add(FT_TS_INFO_SYSFS_DIR_NAME, + kernel_kobj); + if (!data->ts_info_kobj) { + dev_err(&client->dev, "kobject creation failed.\n"); + } else { + err = sysfs_create_file(data->ts_info_kobj, &ts_info_attr.attr); + if (err) { + kobject_put(data->ts_info_kobj); + dev_err(&client->dev, "sysfs creation failed.\n"); + } else { + ts_info_buff = devm_kzalloc(&client->dev, + FT_INFO_MAX_LEN, GFP_KERNEL); + if (!ts_info_buff) + goto free_ts_info; + } + } + FT_STORE_TS_DBG_INFO(data->ts_info, data->family_id, data->pdata->name, + data->pdata->num_max_touches, data->pdata->group_id, + data->pdata->fw_vkey_support ? "yes" : "no", + data->pdata->fw_name, data->fw_ver[0], + data->fw_ver[1], data->fw_ver[2]); + FT_STORE_TS_INFO(ts_info_buff, data->family_id, data->fw_ver[0], + data->fw_ver[1], data->fw_ver[2]); + + /*Initialize secure touch */ + ft_secure_touch_init(data); + ft_secure_touch_stop(data, true); + mutex_init(&(data->ft_clk_io_ctrl_mutex)); + + /* Creation of secure touch sysfs files */ + for (attr_count = 0; attr_count < ARRAY_SIZE(st_attrs); attr_count++) { + retval = sysfs_create_file(&data->input_dev->dev.kobj, + &st_attrs[attr_count].attr); + if (retval < 0) { + dev_err(&client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + goto free_secure_touch_sysfs; + } + } + + ft_update_fw_ver(data); + ft_update_fw_vendor_id(data); + ft_update_fw_id(data); + + if (pdata->family_id == FT8006U_ID) { + fts_lic_get_ver_in_tp(client, &lic_ver); + /* + * If display initial code isn't written fully, then this value should be + * a negative value with signed(e.g. 0xFF), therefore need to clear the buildid + * info then this will cause script update the touch firmware again. + */ + dev_info(&client->dev, "Display init code ver:%d\n", + (s8)lic_ver); + if ((s8)lic_ver < 0) { + ft_clear_fw_id(data); + } + } + +#if defined(CONFIG_FB) + INIT_WORK(&data->fb_notify_work, fb_notify_resume_work); + data->fb_notif.notifier_call = fb_notifier_callback; + + err = fb_register_client(&data->fb_notif); + if (err) + dev_err(&client->dev, "Unable to register fb_notifier: %d\n", + err); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + + FT_SUSPEND_LEVEL; + data->early_suspend.suspend = ft_ts_early_suspend; + data->early_suspend.resume = ft_ts_late_resume; + register_early_suspend(&data->early_suspend); +#endif + + mutex_lock(&exp_fn_ctrl_mutex); + if (!exp_fn_ctrl.inited) { + mutex_init(&exp_fn_ctrl.list_mutex); + INIT_LIST_HEAD(&exp_fn_ctrl.fn_list); + exp_fn_ctrl.det_workqueue = + create_singlethread_workqueue("rmi_det_workqueue"); + if (IS_ERR_OR_NULL(exp_fn_ctrl.det_workqueue)) + pr_err("unable to create a workqueue\n"); + INIT_DELAYED_WORK(&exp_fn_ctrl.det_work, + ft_detection_work); + exp_fn_ctrl.inited = true; + } + exp_fn_ctrl.data_ptr = data; + mutex_unlock(&exp_fn_ctrl_mutex); + + if (data->charger_detection_enabled) { + struct power_supply *psy = NULL; + + INIT_WORK(&data->ps_notify_work, ps_notify_callback_work); + data->ps_notif.notifier_call = ps_notify_callback; + err = power_supply_reg_notifier(&data->ps_notif); + if (err) { + dev_err(&client->dev, + "Unable to register ps_notifier: %d\n", err); + goto free_fb_notifier; + } + + /* if power supply supplier registered brfore TP + * ps_notify_callback will not receive PSY_EVENT_PROP_ADDED + * event, and will cause miss to set TP into charger state. + * So check PS state in probe. + */ + psy = power_supply_get_by_name("usb"); + if (psy) { + retval = ps_get_state(data, psy, &data->ps_is_present); + if (retval) { + dev_err(&client->dev, + "psy get property failed rc=%d\n", + retval); + goto free_fb_notifier; + } + + retval = ft_set_charger_state(data, + data->ps_is_present); + if (retval) { + dev_err(&client->dev, + "set charger state failed rc=%d\n", + retval); + goto free_fb_notifier; + } + + ft_update_ps_chg_cm_state(data, + data->ps_is_present); + } + } + + if (data->fps_detection_enabled) { + data->fps_notif.notifier_call = fps_notifier_callback; + dev_dbg(&client->dev, "registering FPS notifier\n"); + ft_new_function(true, + ft_fps_register_init, + NULL, + NULL); + } + + data->ft_reboot.notifier_call = ft_reboot; + data->ft_reboot.next = NULL; + data->ft_reboot.priority = 1; + err = register_reboot_notifier(&data->ft_reboot); + if (err) { + dev_err(&client->dev, "register for reboot failed\n"); + goto reboot_register_err; + } + + return 0; + +reboot_register_err: + if (data->is_fps_registered) + FPS_unregister_notifier(&data->fps_notif, 0xBEEF); + if (data->charger_detection_enabled) + power_supply_unreg_notifier(&data->ps_notif); +free_fb_notifier: +#if defined(CONFIG_FB) + if (fb_unregister_client(&data->fb_notif)) + dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n"); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&data->early_suspend); +#endif +free_secure_touch_sysfs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&data->input_dev->dev.kobj, + &st_attrs[attr_count].attr); + } +free_ts_info: + kobject_put(data->ts_info_kobj); +free_proc_entry: + ft_remove_proc_entry(data); +free_debug_dir: + debugfs_remove_recursive(data->dir); +debugfs_create_dir_err: + sysfs_remove_files(&client->dev.kobj, ft_attrs); +sysfs_create_files_err: + ft_ts_sysfs_class(data, false); +sysfs_class_err: + free_irq(client->irq, data); + data->irq_enabled = false; +request_irq_err: + input_unregister_device(input_dev); +input_register_device_err: + data->input_dev = NULL; + input_free_device(input_dev); +free_gpio: + ft_gpio_configure(data, false); +gpio_config_err: + if (data->ts_pinctrl) { + if (IS_ERR_OR_NULL(data->pinctrl_state_release)) { + devm_pinctrl_put(data->ts_pinctrl); + data->ts_pinctrl = NULL; + } else { + err = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_release); + if (err) + pr_err("failed to select relase pinctrl state\n"); + } + } + + if (data->regulator_en) { + if (pdata->power_on) + pdata->power_on(false); + else + ft_power_on(data, false); + } +regulator_en_err: + if (pdata->power_init) + pdata->power_init(false); + else + ft_power_init(data, false); + return err; +} + +static int ft_ts_remove(struct i2c_client *client) +{ + struct ft_ts_data *data = i2c_get_clientdata(client); + int retval, attr_count; + + if (exp_fn_ctrl.inited) { + cancel_delayed_work_sync(&exp_fn_ctrl.det_work); + flush_workqueue(exp_fn_ctrl.det_workqueue); + destroy_workqueue(exp_fn_ctrl.det_workqueue); + } + + if (data->is_fps_registered) + FPS_unregister_notifier(&data->fps_notif, 0xBEEF); + + unregister_reboot_notifier(&data->ft_reboot); + + if (data->charger_detection_enabled) + power_supply_unreg_notifier(&data->ps_notif); + +#if defined(CONFIG_FB) + if (fb_unregister_client(&data->fb_notif)) + dev_err(&client->dev, "Error occurred while unregistering fb_notifier.\n"); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&data->early_suspend); +#endif + + for (attr_count = 0; attr_count < ARRAY_SIZE(st_attrs); attr_count++) { + sysfs_remove_file(&data->input_dev->dev.kobj, + &st_attrs[attr_count].attr); + } + + ft_remove_proc_entry(data); + debugfs_remove_recursive(data->dir); + sysfs_remove_files(&client->dev.kobj, ft_attrs); + ft_ts_sysfs_class(data, false); + + free_irq(client->irq, data); + data->irq_enabled = false; + + ft_gpio_configure(data, false); + + if ((data->pdata->family_id == FT8006U_ID) || (data->pdata->family_id == FT5422U_ID)) { + fts_extra_exit(); + } + + if (data->ts_pinctrl) { + if (IS_ERR_OR_NULL(data->pinctrl_state_release)) { + devm_pinctrl_put(data->ts_pinctrl); + data->ts_pinctrl = NULL; + } else { + retval = pinctrl_select_state(data->ts_pinctrl, + data->pinctrl_state_release); + if (retval < 0) + pr_err("failed to select release pinctrl state\n"); + } + } + + if (data->regulator_en) { + if (data->pdata->power_on) + data->pdata->power_on(false); + else + ft_power_on(data, false); + } + + if (data->pdata->power_init) + data->pdata->power_init(false); + else + ft_power_init(data, false); + + input_unregister_device(data->input_dev); + data->input_dev = NULL; + input_free_device(data->input_dev); + kobject_put(data->ts_info_kobj); + return 0; +} + +static const struct i2c_device_id ft_ts_id[] = { + {"focaltech_ts", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, ft_ts_id); + +#ifdef CONFIG_OF +static const struct of_device_id ft_match_table[] = { + { .compatible = "focaltech,mmi",}, + { }, +}; +#else +#define ft_match_table NULL +#endif + +static struct i2c_driver ft_ts_driver = { + .probe = ft_ts_probe, + .remove = ft_ts_remove, + .driver = { + .name = "focaltech_ts", + .owner = THIS_MODULE, + .of_match_table = ft_match_table, +#ifdef CONFIG_PM + .pm = &ft_ts_pm_ops, +#endif + }, + .id_table = ft_ts_id, +}; + +static int __init ft_ts_init(void) +{ + return i2c_add_driver(&ft_ts_driver); +} +module_init(ft_ts_init); + +static void __exit ft_ts_exit(void) +{ + i2c_del_driver(&ft_ts_driver); +} +module_exit(ft_ts_exit); + +MODULE_DESCRIPTION("FocalTech mmi TouchScreen driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_common.h b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_common.h new file mode 100644 index 000000000000..07d8eae51821 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_common.h @@ -0,0 +1,178 @@ +#ifndef __LINUX_FOCALTECH_UPGRADE_COMMON_H__ +#define __LINUX_FOCALTECH_UPGRADE_COMMON_H__ + +#include +#include +#include "focaltech_flash.h" + +#define FTS_MAX_TRIES 5 +#define FTS_RETRY_DLY 20 +#define FTS_MAX_WR_BUF 10 +#define FTS_MAX_RD_BUF 2 +#define FTS_FW_PKT_META_LEN 6 +#define FTS_FW_PKT_DLY_MS 20 +#define FTS_FW_LAST_PKT 0x6ffa +#define FTS_EARSE_DLY_MS 100 +#define FTS_55_AA_DLY_NS 5000 +#define FTS_CAL_START 0x04 +#define FTS_CAL_FIN 0x00 +#define FTS_CAL_STORE 0x05 +#define FTS_CAL_RETRY 100 +#define FTS_REG_CAL 0x00 +#define FTS_CAL_MASK 0x70 +#define FTS_BLOADER_SIZE_OFF 12 +#define FTS_BLOADER_NEW_SIZE 30 +#define FTS_DATA_LEN_OFF_OLD_FW 8 +#define FTS_DATA_LEN_OFF_NEW_FW 14 +#define FTS_FINISHING_PKT_LEN_OLD_FW 6 +#define FTS_FINISHING_PKT_LEN_NEW_FW 12 +#define FTS_MAGIC_BLOADER_Z7 0x7bfa +#define FTS_MAGIC_BLOADER_LZ4 0x6ffa +#define FTS_MAGIC_BLOADER_GZF_30 0x7ff4 +#define FTS_MAGIC_BLOADER_GZF 0x7bf4 +#define FTS_REG_ECC 0xCC +#define FTS_RST_CMD_REG2 0xBC +#define FTS_READ_ID_REG 0x90 +#define FTS_ERASE_APP_REG 0x61 +#define FTS_ERASE_PARAMS_CMD 0x63 +#define FTS_FW_WRITE_CMD 0xBF +#define FTS_REG_RESET_FW 0x07 +#define FTS_RST_CMD_REG1 0xFC +#define LEN_FLASH_ECC_MAX 0xFFFE + +#define BL_VERSION_LZ4 0 +#define BL_VERSION_Z7 1 +#define BL_VERSION_GZF 2 + +#define FTS_PACKET_LENGTH 128 +#define FTS_SETTING_BUF_LEN 128 + +#define FTS_UPGRADE_LOOP 30 +#define FTS_MAX_POINTS_2 2 +#define FTS_MAX_POINTS_5 5 +#define FTS_MAX_POINTS_10 10 +#define AUTO_CLB_NEED 1 +#define AUTO_CLB_NONEED 0 +#define FTS_UPGRADE_AA 0xAA +#define FTS_UPGRADE_55 0x55 + +/* 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_CHIP_ID 0xA3 +#define FTS_REG_CHIP_ID2 0x9F +#define FTS_REG_POWER_MODE 0xA5 +#define FTS_REG_POWER_MODE_SLEEP_VALUE 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_GLOVE_MODE_EN 0xC0 +#define FTS_REG_COVER_MODE_EN 0xC1 +#define FTS_REG_CHARGER_MODE_EN 0x8B +#define FTS_REG_GESTURE_EN 0xD0 +#define FTS_REG_GESTURE_OUTPUT_ADDRESS 0xD3 +#define FTS_REG_ESD_SATURATE 0xED + +#define FLAGBIT(x) (0x00000001 << (x)) +#define FLAGBITS(x, y) ((0xFFFFFFFF >> (32 - (y) - 1)) << (x)) + +#define FLAG_ICSERIALS_LEN 5 +#define FLAG_IDC_BIT 11 + +#define IC_SERIALS(x) ((x) & FLAGBITS(0, FLAG_ICSERIALS_LEN - 1)) +#define FTS_CHIP_IDC(x) (((x) & FLAGBIT(FLAG_IDC_BIT)) == \ + FLAGBIT(FLAG_IDC_BIT)) + +#define FTS_DEBUG pr_debug +#define FTS_ERROR pr_err +#define FTS_INFO pr_info +#define FTS_FUNC_ENTER() pr_debug("[FTS]%s: Enter\n", __func__) +#define FTS_FUNC_EXIT() pr_debug("[FTS]%s: Exit(%d)\n", __func__, \ + __LINE__) + +enum ENUM_APP_INFO { + APP_LEN = 0x00, + APP_LEN_NE = 0x02, + APP_P1_ECC = 0x04, + APP_P1_ECC_NE = 0x06, + APP_P2_ECC = 0x08, + APP_P2_ECC_NE = 0x0A, + APP_LEN_H = 0x12, + APP_LEN_H_NE = 0x14, + APP_BLR_ID = 0x1C, + APP_BLR_ID_NE = 0x1D, + PBOOT_ID_H = 0x1E, + PBOOT_ID_L = 0x1F +}; + +enum FW_STATUS { + FTS_RUN_IN_ERROR, + FTS_RUN_IN_APP, + FTS_RUN_IN_ROM, + FTS_RUN_IN_PRAM, + FTS_RUN_IN_BOOTLOADER +}; + +struct ft_chip_t { + unsigned long type; + unsigned char chip_idh; + unsigned char chip_idl; + unsigned char rom_idh; + unsigned char rom_idl; + unsigned char pramboot_idh; + unsigned char pramboot_idl; + unsigned char bootloader_idh; + unsigned char bootloader_idl; + unsigned long chip_type; +}; + +struct fts_upgrade_fun { + int (*get_app_bin_file_ver)(const char *); + int (*upgrade_with_app_bin_file)(struct i2c_client *, const char *); + int (*upgrade_with_lcd_cfg_bin_file)(struct i2c_client *, const char *); +}; + +extern int fts_i2c_read(struct i2c_client *client, + char *writebuf, int writelen, + char *readbuf, int readlen); +extern int fts_i2c_write(struct i2c_client *client, + char *writebuf, int writelen); +extern int fts_i2c_write_reg(struct i2c_client *client, + u8 addr, const u8 val); +extern int fts_i2c_read_reg(struct i2c_client *client, u8 addr, u8 *val); +extern enum FW_STATUS fts_ctpm_get_pram_or_rom_id + (struct i2c_client *client); + +extern bool fts_ecc_check(const u8 *pbt_buf, + u32 star_addr, u32 len, u16 ecc_addr); + +static inline u16 fts_data_word(const u8 *pbt_buf, u32 addr) +{ + return ((u16)pbt_buf[addr] << 8) + pbt_buf[addr + 1]; +} + + +extern int fts_ctpm_upgrade_idc_init(struct i2c_client *client, + u32 dw_length); +extern void fts_ctpm_start_pramboot(struct i2c_client *client); +extern int fts_ctpm_start_fw_upgrade(struct i2c_client *client); +extern bool fts_ctpm_check_run_state(struct i2c_client *client, + int state); +extern int fts_ctpm_upgrade_ecc(struct i2c_client *client, + u32 startaddr, u32 length); +extern int fts_ctpm_pramboot_ecc(struct i2c_client *client); +extern int fts_ctpm_erase_flash(struct i2c_client *client); +extern int fts_ctpm_write_pramboot_for_idc(struct i2c_client *client, + u32 length, const u8 *readbuf); +extern int fts_ctpm_write_app_for_idc(struct i2c_client *client, + u32 length, const u8 *readbuf); +extern void ft8716_set_upgrade_function(struct fts_upgrade_fun **curr); +extern void ft8716_set_chip_id(struct ft_chip_t **curr); +extern void ft5x46_set_upgrade_function(struct fts_upgrade_fun **curr); +extern void ft5x46_set_chip_id(struct ft_chip_t **curr); + +#endif diff --git a/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft5422u.c b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft5422u.c new file mode 100644 index 000000000000..9d4d34be9e55 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft5422u.c @@ -0,0 +1,184 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2018, 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_ft5422u.c +* +* Author: Focaltech Driver Team +* +* Created: 2017-07-22 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_flash.h" + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +struct ft_chip_t ft5422u_fct = { + .type = 0x82, + .chip_idh = 0x54, + .chip_idl = 0x22, + .rom_idh = 0x54, + .rom_idl = 0x22, + .pramboot_idh = 0x54, + .pramboot_idl = 0x2D, + .bootloader_idh = 0x54, + .bootloader_idl = 0x2E, + .chip_type = 0x5446C482, +}; + +/************************************************************************ +* Name: fts_ft5422u_upgrade +* Brief: +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_ft5422u_upgrade(struct i2c_client *client, 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; + + u8 wbuf[7] = { 0 }; + u8 reg_val[4] = {0}; + u32 temp = 0; + int i; + + if (NULL == buf) { + FTS_ERROR("fw buf is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > (116 * 1024))) { + FTS_ERROR("fw buffer len(%x) fail", len); + return -EINVAL; + } + + /* enter into upgrade environment */ + ret = fts_fwupg_enter_into_boot(client); + 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_i2c_write(client, 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_i2c_write(client, cmd, FTS_CMD_DATA_LEN_LEN); + if (ret < 0) { + FTS_ERROR("data len cmd write fail"); + goto fw_reset; + } + + ret = fts_fwupg_erase(client, FTS_REASE_APP_DELAY); + if (ret < 0) { + FTS_ERROR("erase cmd write fail"); + goto fw_reset; + } + + /* write app */ + start_addr = upgrade_func_ft5422u.appoff; + ecc_in_host = fts_flash_write_buf(client, start_addr, buf, len, 1); + if (ecc_in_host < 0 ) { + FTS_ERROR("lcd initial code write fail"); + goto fw_reset; + } + + FTS_DEBUG("[UPGRADE]: read out checksum!!"); + wbuf[0] = FTS_CMD_ECC_INIT; + fts_i2c_write(client, wbuf, 1); + + temp = 0; + wbuf[0] = FTS_CMD_ECC_CAL; + wbuf[1] = BYTE_OFF_16(temp); + wbuf[2] = BYTE_OFF_8(temp); + wbuf[3] = BYTE_OFF_0(temp); + + wbuf[4] = BYTE_OFF_16(len); + wbuf[5] = BYTE_OFF_8(len); + wbuf[6] = BYTE_OFF_0(len); + ret = fts_i2c_write(client, wbuf, 7); + msleep(len / 256); + + for (i = 0; i < FTS_RETRIES_ECC_CAL; i++) { + wbuf[0] = FTS_CMD_FLASH_STATUS; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, 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); + } + wbuf[0] = FTS_CMD_ECC_READ; + fts_i2c_read(client, wbuf, 1, reg_val, 1); + + 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(client); + 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(client); + if (ret < 0) { + FTS_ERROR("reset to normal boot fail"); + } + return -EIO; +} + +struct upgrade_func upgrade_func_ft5422u = { + .ctype = {0x82}, + .fwveroff = 0x0014, + .fwcfgoff = 0xD780, + .appoff = 0x0000, + .pramboot_supported = false, + .hid_supported = true, + .fts_5422u = true, + .upgrade = fts_ft5422u_upgrade, +}; diff --git a/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft5x46.c b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft5x46.c new file mode 100644 index 000000000000..351fde6741b7 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft5x46.c @@ -0,0 +1,451 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2010-2016, 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. + * + */ + +#include +#include "focaltech_flash.h" +#include "focaltech_upgrade_common.h" + +#define APP_FILE_MAX_SIZE (60 * 1024) +#define APP_FILE_MIN_SIZE (8) +#define CONFIG_START_ADDR (0xD780) +#define CONFIG_VENDOR_ID_OFFSET (0x04) +#define CONFIG_PROJECT_ID_OFFSET (0x20) +#define CONFIG_VENDOR_ID_ADDR (CONFIG_START_ADDR+CONFIG_VENDOR_ID_OFFSET) +#define CONFIG_PROJECT_ID_ADDR (CONFIG_START_ADDR+CONFIG_PROJECT_ID_OFFSET) + +static int fts_5x46_get_app_bin_file_ver(const char *firmware_name); +static int fts_5x46_fw_upgrade_with_app_bin_file( + struct i2c_client *client, const char *firmware_name); + + +static struct fts_upgrade_fun fts_updatefun_5x46 = { + .get_app_bin_file_ver = fts_5x46_get_app_bin_file_ver, + .upgrade_with_app_bin_file = fts_5x46_fw_upgrade_with_app_bin_file, + .upgrade_with_lcd_cfg_bin_file = NULL, +}; + +static struct ft_chip_t fts_chip_type_5x46 = { + .type = 0x02, + .chip_idh = 0x54, + .chip_idl = 0x22, + .rom_idh = 0x54, + .rom_idl = 0x22, + .pramboot_idh = 0x00, + .pramboot_idl = 0x00, + .bootloader_idh = 0x54, + .bootloader_idl = 0x2C, + .chip_type = 0x54220002, +}; + +inline void ft5x46_set_upgrade_function(struct fts_upgrade_fun **curr) +{ + *curr = &fts_updatefun_5x46; +} +inline void ft5x46_set_chip_id(struct ft_chip_t **curr) +{ + *curr = &fts_chip_type_5x46; +} + +static int fts_5x46_get_app_bin_file_ver(const char *firmware_name) +{ + int fw_ver = 0; + int rc; + const struct firmware *fw = NULL; + + FTS_FUNC_ENTER(); + + rc = request_firmware(&fw, firmware_name, NULL); + if (rc < 0) { + FTS_ERROR("[UPGRADE] Request firmware failed - %s (%d)\n", + firmware_name, rc); + return rc; + } + if (fw->size < APP_FILE_MIN_SIZE || fw->size > APP_FILE_MAX_SIZE) { + FTS_ERROR("[UPGRADE] FW length(%zd) error\n", fw->size); + return -EIO; + goto file_ver_rel_fw; + } + + fw_ver = fw->data[fw->size - 2]; + +file_ver_rel_fw: + release_firmware(fw); + FTS_FUNC_EXIT(); + + return fw_ver; +} + + +/************************************************************************ +* Name: fts_ft5436_upgrade_use_buf +* Brief: fw upgrade +* Input: i2c info, file buf, file len +* Output: no +* Return: fail <0 +***********************************************************************/ +static int fts_ft5x46_upgrade_use_buf(struct i2c_client *client, + const u8 *pbt_buf, u32 dw_lenth) +{ + u8 reg_val[4] = {0}; + u32 i = 0; + u32 packet_number; + u32 j = 0; + u32 temp; + u32 lenght; + u8 packet_buf[FTS_PACKET_LENGTH + 6]; + u8 auc_i2c_write_buf[10]; + u8 upgrade_ecc; + int i_ret; + + fts_ctpm_i2c_hid2std(client); + + for (i = 0; i < FTS_UPGRADE_LOOP; i++) + { + /*********Step 1:Reset CTPM *****/ + fts_i2c_write_reg(client, FTS_RST_CMD_REG1, FTS_UPGRADE_AA); + msleep(10); + fts_i2c_write_reg(client, FTS_RST_CMD_REG1, FTS_UPGRADE_55); + msleep(200); + + /*********Step 2:Enter upgrade mode *****/ + fts_ctpm_i2c_hid2std(client); + msleep(5); + + auc_i2c_write_buf[0] = FTS_UPGRADE_55; + auc_i2c_write_buf[1] = FTS_UPGRADE_AA; + i_ret = fts_i2c_write(client, auc_i2c_write_buf, 2); + if (i_ret < 0) + { + FTS_ERROR("[UPGRADE]: failed writing 0x55 and 0xaa!!"); + continue; + } + + /*********Step 3:Check bootloader ID *****/ + msleep(1); + auc_i2c_write_buf[0] = FTS_READ_ID_REG; + auc_i2c_write_buf[1] = auc_i2c_write_buf[2] = auc_i2c_write_buf[3] = 0x00; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, auc_i2c_write_buf, 4, reg_val, 2); + if ((reg_val[0] == 0x54) && (reg_val[1] != 0)) + { + FTS_DEBUG("[UPGRADE]: read bootload id ok!! ID1 = 0x%x,ID2 = 0x%x!!",reg_val[0], reg_val[1]); + break; + } + else + { + FTS_ERROR("[UPGRADE]: read bootload id fail!! ID1 = 0x%x,ID2 = 0x%x!!",reg_val[0], reg_val[1]); + continue; + } + } + + if (i >= FTS_UPGRADE_LOOP) + { + FTS_ERROR("[UPGRADE]: failed writing 0x55 and 0xaa : i = %d!!", i); + return -EIO; + } + + /*Step 4:erase app and panel paramenter area*/ + FTS_DEBUG("[UPGRADE]: erase app and panel paramenter area!!"); + auc_i2c_write_buf[0] = FTS_ERASE_APP_REG; + fts_i2c_write(client, auc_i2c_write_buf, 1); + msleep(1350); + for (i = 0; i < 15; i++) + { + auc_i2c_write_buf[0] = 0x6a; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 2); + if ((0xF0 == reg_val[0]) && (0xAA == reg_val[1])) + { + break; + } + msleep(50); + } + FTS_DEBUG("[UPGRADE]: erase app area reg_val[0] = %x reg_val[1] = %x!!", reg_val[0], reg_val[1]); + + auc_i2c_write_buf[0] = 0xB0; + auc_i2c_write_buf[1] = (u8) ((dw_lenth >> 16) & 0xFF); + auc_i2c_write_buf[2] = (u8) ((dw_lenth >> 8) & 0xFF); + auc_i2c_write_buf[3] = (u8) (dw_lenth & 0xFF); + fts_i2c_write(client, auc_i2c_write_buf, 4); + + /*********Step 5:write firmware(FW) to ctpm flash*********/ + upgrade_ecc = 0; + FTS_DEBUG("[UPGRADE]: write FW to ctpm flash!!"); + temp = 0; + packet_number = (dw_lenth) / FTS_PACKET_LENGTH; + packet_buf[0] = FTS_FW_WRITE_CMD; + packet_buf[1] = 0x00; + + for (j = 0; j < packet_number; j++) + { + temp = j * FTS_PACKET_LENGTH; + packet_buf[2] = (u8) (temp >> 8); + packet_buf[3] = (u8) temp; + lenght = FTS_PACKET_LENGTH; + packet_buf[4] = (u8) (lenght >> 8); + packet_buf[5] = (u8) lenght; + for (i = 0; i < FTS_PACKET_LENGTH; i++) + { + packet_buf[6 + i] = pbt_buf[j * FTS_PACKET_LENGTH + i]; + upgrade_ecc ^= packet_buf[6 + i]; + } + fts_i2c_write(client, packet_buf, FTS_PACKET_LENGTH + 6); + msleep(10); + + for (i = 0; i < 30; i++) + { + auc_i2c_write_buf[0] = 0x6a; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 2); + if ((j + 0x1000) == (((reg_val[0]) << 8) | reg_val[1])) + { + break; + } + FTS_DEBUG("[UPGRADE]: reg_val[0] = %x reg_val[1] = %x!!", reg_val[0], reg_val[1]); + //msleep(1); + msleep(1000); + } + } + + if ((dw_lenth) % FTS_PACKET_LENGTH > 0) + { + temp = packet_number * FTS_PACKET_LENGTH; + packet_buf[2] = (u8) (temp >> 8); + packet_buf[3] = (u8) temp; + temp = (dw_lenth) % FTS_PACKET_LENGTH; + packet_buf[4] = (u8) (temp >> 8); + packet_buf[5] = (u8) temp; + for (i = 0; i < temp; i++) + { + packet_buf[6 + i] = pbt_buf[packet_number * FTS_PACKET_LENGTH + i]; + upgrade_ecc ^= packet_buf[6 + i]; + } + fts_i2c_write(client, packet_buf, temp + 6); + msleep(10); + + for (i = 0; i < 30; i++) + { + auc_i2c_write_buf[0] = 0x6a; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 2); + + if ((0x1000 + ((packet_number * FTS_PACKET_LENGTH)/((dw_lenth) % FTS_PACKET_LENGTH))) == (((reg_val[0]) << 8) | reg_val[1])) + { + break; + } + FTS_DEBUG("[UPGRADE]: reg_val[0] = %x reg_val[1] = %x reg_val[2] = 0x%x!!", reg_val[0], reg_val[1], (((packet_number * FTS_PACKET_LENGTH)/((dw_lenth) % FTS_PACKET_LENGTH))+0x1000)); + //msleep(1); + msleep(100); + } + } + + msleep(50); + + /*********Step 6: read out checksum***********************/ + /*send the opration head */ + FTS_DEBUG("[UPGRADE]: read out checksum!!"); + auc_i2c_write_buf[0] = 0x64; + fts_i2c_write(client, auc_i2c_write_buf, 1); + msleep(300); + + temp = 0; + auc_i2c_write_buf[0] = 0x65; + auc_i2c_write_buf[1] = (u8)(temp >> 16); + auc_i2c_write_buf[2] = (u8)(temp >> 8); + auc_i2c_write_buf[3] = (u8)(temp); + temp = dw_lenth; + auc_i2c_write_buf[4] = (u8)(temp >> 8); + auc_i2c_write_buf[5] = (u8)(temp); + i_ret = fts_i2c_write(client, auc_i2c_write_buf, 6); + msleep(dw_lenth/256); + + for (i = 0; i < 100; i++) + { + auc_i2c_write_buf[0] = 0x6a; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, auc_i2c_write_buf, 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(1); + } + auc_i2c_write_buf[0] = 0x66; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 1); + if (reg_val[0] != upgrade_ecc) + { + FTS_ERROR("[UPGRADE]: ecc error! FW=%02x upgrade_ecc=%02x!!", reg_val[0], upgrade_ecc); + return -EIO; + } + FTS_DEBUG("[UPGRADE]: checksum %x %x!!",reg_val[0],upgrade_ecc); + + FTS_DEBUG("[UPGRADE]: reset the new FW!!"); + auc_i2c_write_buf[0] = FTS_REG_RESET_FW; + fts_i2c_write(client, auc_i2c_write_buf, 1); + msleep(200); + + fts_ctpm_i2c_hid2std(client); + + return 0; +} + +#define AL2_FCS_COEF ((1 << 7) + (1 << 6) + (1 << 5)) +/***************************************************************************** +* Name: ecc_calc +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static u8 ecc_calc(const u8 *pbt_buf, u16 start, u16 length) +{ + u8 cFcs = 0; + u16 i, j; + + for ( i = 0; i < length; i++ ) + { + cFcs ^= pbt_buf[start++]; + for (j = 0; j < 8; j ++) + { + if (cFcs & 1) + { + cFcs = (u8)((cFcs >> 1) ^ AL2_FCS_COEF); + } + else + { + cFcs >>= 1; + } + } + } + return cFcs; +} + + +/***************************************************************************** +* Name: fts_check_app_bin_valid +* Brief: +* Input: +* Output: +* Return: +*****************************************************************************/ +static bool fts_5x46_check_app_bin_valid(const u8 *pbt_buf, u32 dw_lenth) +{ + u8 ecc1; + u8 ecc2; + u16 len1; + u16 len2; + u8 cal_ecc; + u16 usAddrInfo; + + /* 1. First Byte */ + if (pbt_buf[0] != 0x02) + { + FTS_DEBUG("[UPGRADE]APP.BIN Verify- the first byte(%x) error", pbt_buf[0]); + return false; + } + + usAddrInfo = dw_lenth - 8; + + /* 2.len */ + len1 = pbt_buf[usAddrInfo++] << 8; + len1 += pbt_buf[usAddrInfo++]; + + len2 = pbt_buf[usAddrInfo++] << 8; + len2 += pbt_buf[usAddrInfo++]; + + if ((len1 + len2) != 0xFFFF) + { + FTS_DEBUG("[UPGRADE]APP.BIN Verify- LENGTH(%04x) XOR error", len1); + return false; + } + + /* 3.ecc */ + ecc1 = pbt_buf[usAddrInfo++]; + ecc2 = pbt_buf[usAddrInfo++]; + + if ((ecc1 + ecc2) != 0xFF) + { + FTS_DEBUG("[UPGRADE]APP.BIN Verify- ECC(%x) XOR error", ecc1); + return false; + } + + cal_ecc = ecc_calc(pbt_buf, 0x0, len1); + + if (ecc1 != cal_ecc) + { + FTS_DEBUG("[UPGRADE]APP.BIN Verify- ECC calc error"); + return false; + } + return true; +} + +/************************************************************************ +* Name: fts_5436_fw_upgrade_with_app_bin_file +* Brief: upgrade with *.bin file +* Input: i2c info, file name +* Output: no +* Return: success =0 +***********************************************************************/ +static int fts_5x46_fw_upgrade_with_app_bin_file(struct i2c_client *client, const char *firmware_name) +{ + int i_ret = 0; + bool ecc_ok = false; + const struct firmware *fw = NULL; + + FTS_INFO("[UPGRADE] start upgrade with app.bin\n"); + + i_ret = request_firmware(&fw, firmware_name, &client->dev); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] Request firmware failed - %s (%d)\n", + firmware_name, i_ret); + return i_ret; + } + + if (fw->size < APP_FILE_MIN_SIZE || fw->size > APP_FILE_MAX_SIZE) { + FTS_ERROR("[UPGRADE] FW length(%zd) error\n", fw->size); + i_ret = -EINVAL; + goto file_upgrade_rel_fw; + } + + ecc_ok = fts_5x46_check_app_bin_valid(fw->data, fw->size); + + if (ecc_ok) + { + FTS_INFO("[UPGRADE] app.bin ecc ok"); + i_ret = fts_ft5x46_upgrade_use_buf(client, fw->data, fw->size); + if (i_ret != 0) + { + FTS_ERROR("[UPGRADE]: upgrade app.bin failed"); + } + else + { + FTS_INFO("[UPGRADE]: upgrade app.bin succeed"); + } + } + else + { + FTS_ERROR("[UPGRADE] app.bin ecc failed"); + } + + return i_ret; + +file_upgrade_rel_fw: + release_firmware(fw); + return i_ret; +} + diff --git a/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft8006u.c b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft8006u.c new file mode 100644 index 000000000000..1cf7803e669b --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft8006u.c @@ -0,0 +1,944 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2010-2017, 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_ft8006u.c +* +* Author: Focaltech Driver Team +* +* Created: 2017-07-22 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ + +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "focaltech_flash.h" + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +#define FT8006U_PRAMBOOT_FW_NAME "FT8006U_Pramboot.bin" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define MAX_BANK_DATA 0x80 +#define MAX_GAMMA_LEN 0x180 +#define LIC_CHECKSUM_H_OFF 0x01 +#define LIC_CHECKSUM_L_OFF 0x00 +#define LIC_LCD_ECC_H_OFF 0x05 +#define LIC_LCD_ECC_L_OFF 0x04 +#define LIC_BANKECC_H_OFF 0x0F +#define LIC_BANKECC_L_OFF 0x0E +#define LIC_REG_2 0xB2 +#define LIC_BANK_START_ADDR 0x0A + +static int gamma_enable[] = { 0x042c, 0x91, 0x80, 0x00, 0x12, 0x01 }; +bool gamma_has_enable = false; + +union short_bits { + u16 dshort; + struct bits { + u16 bit0: 1; + u16 bit1: 1; + u16 bit2: 1; + u16 bit3: 1; + u16 bit4: 1; + u16 bit5: 1; + u16 bit6: 1; + u16 bit7: 1; + u16 bit8: 1; + u16 bit9: 1; + u16 bit10: 1; + u16 bit11: 1; + u16 bit12: 1; + u16 bit13: 1; + u16 bit14: 1; + u16 bit15: 1; + } bits; +}; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/* calculate lcd init code ecc */ +static int cal_lcdinitcode_ecc(u8 *buf, u16 *ecc_val) +{ + u32 bank_crc_en = 0; + u8 bank_data[MAX_BANK_DATA] = { 0 }; + u16 bank_len = 0; + u16 bank_addr = 0; + u32 bank_num = 0; + u16 file_len = 0; + u16 pos = 0; + int i = 0; + union short_bits ecc; + union short_bits ecc_last; + union short_bits temp_byte; + u8 bank_mapping[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, + 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x18, 0x19, 0x1A, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x22, 0x23, 0x24 + }; /* Actaul mipi bank */ + u8 banknum = 0; + + ecc.dshort = 0; + ecc_last.dshort = 0; + temp_byte.dshort = 0; + + file_len = (u16)(((u16)buf[3] << 8) + buf[2]); + if ((file_len >= FTS_MAX_LEN_SECTOR) || (file_len <= FTS_MIN_LEN)) { + FTS_ERROR("host lcd init code len(%x) is too large", file_len); + return -EINVAL; + } + + bank_crc_en = (u32)(((u32)buf[9] << 24) + ((u32)buf[8] << 16) + \ + ((u32)buf[7] << 8) + (u32)buf[6]); + FTS_INFO("lcd init code len=%x bank en=%x", file_len, bank_crc_en); + + pos = LIC_BANK_START_ADDR; /* addr of first bank */ + while (pos < file_len) { + bank_addr = (u16)(((u16)buf[pos + 0] << 8 ) + buf[pos + 1]); + bank_len = (u16)(((u16)buf[pos + 2] << 8 ) + buf[pos + 3]); + if (bank_len > MAX_BANK_DATA) + return -EINVAL; + memset(bank_data, 0, MAX_BANK_DATA); + memcpy(bank_data, buf + pos + 4, bank_len); + + bank_num = (bank_addr - 0x8000) / MAX_BANK_DATA; + if ((bank_num == 0x15) || (bank_num == 0x16)) + bank_num = 0x14; + if (bank_num == 0x1B) + bank_num = 0x1A; + for (i = 0; i < sizeof(bank_mapping) / sizeof(u8); i++) { + if (bank_num == bank_mapping[i]) { + banknum = i; + break; + } + } + if (i >= sizeof(bank_mapping) / sizeof(u8)) { + FTS_INFO("actual mipi bank(%d) not find in bank mapping, need jump", bank_num); + } else { + if ((bank_crc_en >> banknum) & 0x01) { + for (i = 0; i < bank_len; i++) { + temp_byte.dshort = (u16)bank_data[i]; + + ecc.bits.bit0 = ecc_last.bits.bit8 ^ ecc_last.bits.bit9 ^ ecc_last.bits.bit10 ^ ecc_last.bits.bit11 + ^ ecc_last.bits.bit12 ^ ecc_last.bits.bit13 ^ ecc_last.bits.bit14 ^ ecc_last.bits.bit15 + ^ temp_byte.bits.bit0 ^ temp_byte.bits.bit1 ^ temp_byte.bits.bit2 ^ temp_byte.bits.bit3 + ^ temp_byte.bits.bit4 ^ temp_byte.bits.bit5 ^ temp_byte.bits.bit6 ^ temp_byte.bits.bit7; + + ecc.bits.bit1 = ecc_last.bits.bit9 ^ ecc_last.bits.bit10 ^ ecc_last.bits.bit11 ^ ecc_last.bits.bit12 + ^ ecc_last.bits.bit13 ^ ecc_last.bits.bit14 ^ ecc_last.bits.bit15 + ^ temp_byte.bits.bit1 ^ temp_byte.bits.bit2 ^ temp_byte.bits.bit3 ^ temp_byte.bits.bit4 + ^ temp_byte.bits.bit5 ^ temp_byte.bits.bit6 ^ temp_byte.bits.bit7; + + ecc.bits.bit2 = ecc_last.bits.bit8 ^ ecc_last.bits.bit9 ^ temp_byte.bits.bit0 ^ temp_byte.bits.bit1; + + ecc.bits.bit3 = ecc_last.bits.bit9 ^ ecc_last.bits.bit10 ^ temp_byte.bits.bit1 ^ temp_byte.bits.bit2; + + ecc.bits.bit4 = ecc_last.bits.bit10 ^ ecc_last.bits.bit11 ^ temp_byte.bits.bit2 ^ temp_byte.bits.bit3; + + ecc.bits.bit5 = ecc_last.bits.bit11 ^ ecc_last.bits.bit12 ^ temp_byte.bits.bit3 ^ temp_byte.bits.bit4; + + ecc.bits.bit6 = ecc_last.bits.bit12 ^ ecc_last.bits.bit13 ^ temp_byte.bits.bit4 ^ temp_byte.bits.bit5; + + ecc.bits.bit7 = ecc_last.bits.bit13 ^ ecc_last.bits.bit14 ^ temp_byte.bits.bit5 ^ temp_byte.bits.bit6; + + ecc.bits.bit8 = ecc_last.bits.bit0 ^ ecc_last.bits.bit14 ^ ecc_last.bits.bit15 ^ temp_byte.bits.bit6 ^ temp_byte.bits.bit7; + + ecc.bits.bit9 = ecc_last.bits.bit1 ^ ecc_last.bits.bit15 ^ temp_byte.bits.bit7; + + ecc.bits.bit10 = ecc_last.bits.bit2; + + ecc.bits.bit11 = ecc_last.bits.bit3; + + ecc.bits.bit12 = ecc_last.bits.bit4; + + ecc.bits.bit13 = ecc_last.bits.bit5; + + ecc.bits.bit14 = ecc_last.bits.bit6; + + ecc.bits.bit15 = ecc_last.bits.bit7 ^ ecc_last.bits.bit8 ^ ecc_last.bits.bit9 ^ ecc_last.bits.bit10 + ^ ecc_last.bits.bit11 ^ ecc_last.bits.bit12 ^ ecc_last.bits.bit13 ^ ecc_last.bits.bit14 ^ ecc_last.bits.bit15 + ^ temp_byte.bits.bit0 ^ temp_byte.bits.bit1 ^ temp_byte.bits.bit2 ^ temp_byte.bits.bit3 + ^ temp_byte.bits.bit4 ^ temp_byte.bits.bit5 ^ temp_byte.bits.bit6 ^ temp_byte.bits.bit7; + + ecc_last.dshort = ecc.dshort; + + } + } + } + pos += bank_len + 4; + } + + *ecc_val = ecc.dshort; + return 0; +} + + +/* calculate lcd init code checksum */ +static u16 cal_lcdinitcode_checksum(u8 *ptr , int length) +{ + /* CRC16 */ + u16 cfcs = 0; + int i = 0; + int j = 0; + + length = (length % 2 == 0) ? length : (length - 1); + + for (i = 0; i < length; i += 2) { + cfcs ^= ((ptr[i] << 8) + ptr[i + 1]); + for (j = 0; j < 16; j++) { + if (cfcs & 1) + cfcs = (u16)((cfcs >> 1) ^ ((1 << 15) + (1 << 10) + (1 << 3))); + else + cfcs >>= 1; + } + } + return cfcs; +} + +/* + * check_initial_code_valid - check initial code valid or not + */ +static int check_initial_code_valid(struct i2c_client *client, u8 *buf) +{ + int ret = 0; + u16 initcode_ecc = 0; + u16 buf_ecc = 0; + u16 initcode_checksum = 0; + u16 buf_checksum = 0; + u16 hlic_len = 0; + + hlic_len = (u16)(((u16)buf[3]) << 8) + buf[2]; + if ((hlic_len >= FTS_MAX_LEN_SECTOR) || (hlic_len <= FTS_MIN_LEN)) { + FTS_ERROR("host lcd init code len(%x) is too large", hlic_len); + return -EINVAL; + } + + initcode_checksum = cal_lcdinitcode_checksum(buf + 2, hlic_len - 2); + buf_checksum = ((u16)((u16)buf[1] << 8) + buf[0]); + FTS_DEBUG("lcd init code calc checksum:0x%04x,0x%04x", initcode_checksum, buf_checksum); + if (initcode_checksum != buf_checksum) { + FTS_ERROR("Initial Code checksum fail"); + return -EINVAL; + } + + ret = cal_lcdinitcode_ecc(buf, &initcode_ecc); + if (ret < 0) { + FTS_ERROR("lcd init code ecc calculate fail"); + return ret; + } + buf_ecc = ((u16)((u16)buf[5] << 8) + buf[4]); + FTS_DEBUG("lcd init code cal ecc:%04x, %04x", initcode_ecc, buf_ecc); + if (initcode_ecc != buf_ecc) { + FTS_ERROR("Initial Code ecc check fail"); + return -EINVAL; + } + + return 0; +} + +static int print_data(u8 *buf, u32 len) +{ + int i = 0; + int n = 0; + u8 *p = NULL; + + p = kmalloc(len * 4, GFP_KERNEL); + memset(p, 0, len * 4); + + for (i = 0; i < len; i++) + n += sprintf(p + n, "%02x ", buf[i]); + + FTS_DEBUG("%s", p); + + kfree(p); + return 0; +} + +/* + * description : find the address of one bank in initcode + * + * parameters : + * initcode : initcode + * bank_start_addr - the address of one bank, search bank from this address + * bank_sign - bank signature, 2 bytes + * bank_pos - return the position of the bank + * return: return 0 if success, otherwise return error code + */ +static int find_bank( + u8 *initcode, + u16 bank_start_addr, + u16 bank_sign, + u16 *bank_pos) +{ + u16 file_len = 0; + u16 pos = 0; + u8 bank[2] = { 0 }; + + file_len = (u16)(((u16)initcode[3] << 8) + initcode[2]); + if ((file_len >= FTS_MAX_LEN_SECTOR) || (file_len <= FTS_MIN_LEN)) { + FTS_ERROR("host lcd init code len(%x) is too large", file_len); + return -EINVAL; + } + + bank[0] = bank_sign >> 8; + bank[1] = bank_sign; + pos = bank_start_addr; + while (pos < file_len) { + if ((initcode[pos] == bank[0]) + && (initcode[pos + 1] == bank[1])) { + FTS_INFO("bank(%x %x) find", bank[0], bank[1]); + *bank_pos = pos; + return 0; + } else { + pos += ((u16)initcode[pos + 2] << 8 ) + initcode[pos + 3] + 4; + } + } + + return -ENODATA; +} + +static int read_3gamma(struct i2c_client *client, u8 **gamma, u16 *len) +{ + int ret = 0; + int i = 0; + int packet_num = 0; + int packet_len = 0; + int remainder = 0; + u8 cmd[4] = { 0 }; + u32 addr = 0x01D000; + u8 gamma_header[0x20] = { 0 }; + u16 gamma_len = 0; + u16 gamma_len_n = 0; + u16 pos = 0; + u8 *pgamma = NULL; + int j = 0; + u8 gamma_ecc = 0; + + cmd[0] = 0x03; + cmd[1] = (u8)(addr >> 16); + cmd[2] = (u8)(addr >> 8); + cmd[3] = (u8)addr; + ret = fts_i2c_write(client, cmd, 4); + msleep(10); + ret = fts_i2c_read(client, NULL, 0, gamma_header, 0x20); + if (ret < 0) { + FTS_ERROR("read 3-gamma header fail"); + return ret; + } + + gamma_len = (u16)((u16)gamma_header[0] << 8) + gamma_header[1]; + gamma_len_n = (u16)((u16)gamma_header[2] << 8) + gamma_header[3]; + + if ((gamma_len + gamma_len_n) != 0xFFFF) { + FTS_INFO("gamma length check fail:%x %x", gamma_len, gamma_len); + return -EIO; + } + + if ((gamma_header[4] + gamma_header[5]) != 0xFF) { + FTS_INFO("gamma ecc check fail:%x %x", gamma_header[4], gamma_header[5]); + return -EIO; + } + + if (gamma_len > MAX_GAMMA_LEN) { + FTS_ERROR("gamma data len(%d) is too long", gamma_len); + return -EINVAL; + } + + *gamma = kmalloc(MAX_GAMMA_LEN, GFP_KERNEL); + if (NULL == *gamma) { + FTS_ERROR("malloc gamma memory fail"); + return -ENOMEM; + } + pgamma = *gamma; + + packet_num = gamma_len / 256; + packet_len = 256; + remainder = gamma_len % 256; + if (remainder) + packet_num++; + FTS_INFO("3-gamma len:%d", gamma_len); + cmd[0] = 0x03; + addr += 0x20; + for (i = 0; i < packet_num; i++) { + addr += i * 256; + cmd[1] = (u8)(addr >> 16); + cmd[2] = (u8)(addr >> 8); + cmd[3] = (u8)addr; + if ((i == packet_num - 1) && remainder) + packet_len = remainder; + ret = fts_i2c_write(client, cmd, 4); + msleep(10); + ret = fts_i2c_read(client, NULL, 0, pgamma + i * 256, packet_len); + if (ret < 0) { + FTS_ERROR("read 3-gamma data fail"); + return ret; + } + } + + /* ecc */ + for (j = 0; j < gamma_len; j++) + gamma_ecc ^= pgamma[j]; + FTS_DEBUG("backup_3gamma_ecc: 0x%x, 0x%x", gamma_ecc, gamma_header[0x04]); + if (gamma_ecc != gamma_header[0x04]) { + FTS_ERROR("back gamma ecc check fail:%x %x", gamma_ecc, gamma_header[0x04]); + return -EIO; + } + + /* check last byte is 91 80 00 19 01 */ + pos = gamma_len - 5; + if ((gamma_enable[1] == pgamma[pos]) && (gamma_enable[2] == pgamma[pos + 1]) + && (gamma_enable[3] == pgamma[pos + 2]) && (gamma_enable[4] == pgamma[pos + 3])) { + gamma_has_enable = true; + } + + if (false == gamma_has_enable) { + FTS_INFO("3-gamma has no gamma enable info"); + pgamma[gamma_len++] = gamma_enable[1]; + pgamma[gamma_len++] = gamma_enable[2]; + pgamma[gamma_len++] = gamma_enable[3]; + pgamma[gamma_len++] = gamma_enable[4]; + pgamma[gamma_len++] = gamma_enable[5]; + } + + *len = gamma_len; + + FTS_DEBUG("read 3-gamma data:"); + print_data(*gamma, gamma_len); + + return 0; +} + +static int replace_3gamma(u8 *initcode, u8 *gamma, u16 gamma_len) +{ + int ret = 0; + u16 gamma_pos = 0; + int gamma_analog[] = {0x003B, 0x85, 0x00, 0x00, 0x2D, 0x2D}; + int gamma_digital1[] = {0x0374, 0x8D, 0x00, 0x00, 0x80, 0x80}; + int gamma_digital2[] = {0x03F8, 0x8D, 0x80, 0x00, 0x14, 0x14}; + u16 bank_addr = 0; + u16 bank_saddr = 0; + u16 bank_sign = 0; + u16 bank_len = 0; + + /* Analog Gamma */ + bank_saddr = LIC_BANK_START_ADDR; + bank_sign = ((u16)gamma_analog[1] << 8) + gamma_analog[2]; + ret = find_bank(initcode, LIC_BANK_START_ADDR, bank_sign, &bank_addr); + if (ret < 0) { + FTS_ERROR("find bank analog gamma fail"); + goto find_gamma_bank_err; + } + bank_len = ((u16)initcode[bank_addr + 2] << 8 ) + initcode[bank_addr + 3]; + memcpy(initcode + bank_addr + 4 , gamma + gamma_pos + 4, bank_len); + initcode[bank_addr + 4] = 0xA5; + gamma_pos += bank_len + 4; + + /* Digital1 Gamma */ + bank_saddr = bank_addr; + bank_sign = ((u16)gamma_digital1[1] << 8) + gamma_digital1[2]; + ret = find_bank(initcode, bank_saddr, bank_sign, &bank_addr); + if (ret < 0) { + FTS_ERROR("find bank analog gamma fail"); + goto find_gamma_bank_err; + } + bank_len = ((u16)initcode[bank_addr + 2] << 8 ) + initcode[bank_addr + 3]; + memcpy(initcode + bank_addr + 4 , gamma + gamma_pos + 4, bank_len); + gamma_pos += bank_len + 4; + + /* Digital2 Gamma */ + bank_saddr = bank_addr; + bank_sign = ((u16)gamma_digital2[1] << 8) + gamma_digital2[2]; + ret = find_bank(initcode, bank_saddr, bank_sign, &bank_addr); + if (ret < 0) { + FTS_ERROR("find bank analog gamma fail"); + goto find_gamma_bank_err; + } + bank_len = ((u16)initcode[bank_addr + 2] << 8 ) + initcode[bank_addr + 3]; + memcpy(initcode + bank_addr + 4 , gamma + gamma_pos + 4, bank_len); + gamma_pos += bank_len + 4; + + /* enable Gamma */ + bank_saddr = bank_addr; + bank_sign = ((u16)gamma_enable[1] << 8) + gamma_enable[2]; + ret = find_bank(initcode, bank_saddr, bank_sign, &bank_addr); + if (ret < 0) { + FTS_ERROR("find bank analog gamma fail"); + goto find_gamma_bank_err; + } + if (gamma[gamma_pos + 4]) + initcode[bank_addr + 4 + 15] |= 0x01; + else + initcode[bank_addr + 4 + 15] &= 0xFE; + gamma_pos += 1 + 4; + + FTS_DEBUG("replace 3-gamma data:"); + print_data(initcode, 1100); + + return 0; + +find_gamma_bank_err: + FTS_INFO("3-gamma bank(%02x %02x) not find", + gamma[gamma_pos], gamma[gamma_pos + 1]); + return -ENODATA; +} + +static int cal_replace_ecc(u8 *initcode) +{ + int ret = 0; + u16 initcode_ecc = 0; + u16 bank31 = 0x9200; + u16 bank31_addr = 0; + + ret = cal_lcdinitcode_ecc(initcode, &initcode_ecc); + if (ret < 0) { + FTS_ERROR("lcd init code ecc calculate fail"); + return ret; + } + FTS_INFO("lcd init code cal ecc:%04x", initcode_ecc); + initcode[LIC_LCD_ECC_H_OFF] = (u8)(initcode_ecc >> 8); + initcode[LIC_LCD_ECC_L_OFF] = (u8)(initcode_ecc); + ret = find_bank(initcode, LIC_BANK_START_ADDR, bank31, &bank31_addr); + if (ret < 0) { + FTS_ERROR("find bank 31 fail"); + return ret; + } + FTS_INFO("lcd init code ecc bank addr:0x%04x", bank31_addr); + initcode[bank31_addr + LIC_BANKECC_H_OFF + 4] = (u8)(initcode_ecc >> 8); + initcode[bank31_addr + LIC_BANKECC_L_OFF + 4] = (u8)(initcode_ecc); + + return 0; +} + +/* + * read_replace_3gamma - read and replace 3-gamma data + */ +static int read_replace_3gamma(struct i2c_client *client, u8 *buf, bool flag) +{ + int ret = 0; + u16 initcode_checksum = 0; + u8 *tmpbuf = NULL; + u8 *gamma = NULL; + u16 gamma_len = 0; + u16 hlic_len = 0; + int base_addr = 0; + int i = 0; + + FTS_FUNC_ENTER(); + + ret = read_3gamma(client, &gamma, &gamma_len); + if (ret < 0) { + FTS_INFO("no vaid 3-gamma data, not replace"); + if (gamma) { + kfree(gamma); + gamma = NULL; + } + return 0; + } + + base_addr = 0; + for (i = 0; i < 2; i++) { + if (i == 1) { + if (flag == true) + base_addr = 0x7C0; + else + break; + } + + tmpbuf = buf + base_addr; + ret = replace_3gamma(tmpbuf, gamma, gamma_len); + if (ret < 0) { + FTS_ERROR("replace 3-gamma fail"); + goto REPLACE_GAMMA_ERR; + } + + ret = cal_replace_ecc(tmpbuf); + if (ret < 0) { + FTS_ERROR("lcd init code ecc calculate/replace fail"); + goto REPLACE_GAMMA_ERR; + } + + hlic_len = (u16)(((u16)tmpbuf[3]) << 8) + tmpbuf[2]; + if ((hlic_len >= FTS_MAX_LEN_SECTOR) || (hlic_len <= FTS_MIN_LEN)) { + FTS_ERROR("host lcd init code len(%x) is too large", hlic_len); + ret = -EINVAL; + goto REPLACE_GAMMA_ERR; + } + initcode_checksum = cal_lcdinitcode_checksum(tmpbuf + 2, hlic_len - 2); + FTS_INFO("lcd init code calc checksum:0x%04x", initcode_checksum); + tmpbuf[LIC_CHECKSUM_H_OFF] = (u8)(initcode_checksum >> 8); + tmpbuf[LIC_CHECKSUM_L_OFF] = (u8)(initcode_checksum); + } + + if (gamma) { + kfree(gamma); + gamma = NULL; + } + + FTS_FUNC_EXIT(); + return 0; + +REPLACE_GAMMA_ERR: + if (gamma) { + kfree(gamma); + gamma = NULL; + } + return ret; +} + +static int fts_ft8006u_upgrade_mode( + struct i2c_client *client, + enum FW_FLASH_MODE mode, + u8 *buf, + u32 len) +{ + int ret = 0; + bool flag = false; + u32 start_addr = 0; + u8 cmd[4] = { 0 }; + u32 delay = 0; + int ecc_in_host = 0; + int ecc_in_tp = 0; + + if ((NULL == buf) || (len < FTS_MIN_LEN)) { + FTS_ERROR("buffer/len(%x) is invalid", len); + return -EINVAL; + } + + /* enter into upgrade environment */ + ret = fts_fwupg_enter_into_boot(client); + 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; + start_addr = upgrade_func_ft8006u.appoff; + if (FLASH_MODE_LIC == mode) { + /* lcd initial code upgrade */ + /* read replace 3-gamma yet */ + ret = read_replace_3gamma(client, buf, flag); + if (ret < 0) { + FTS_ERROR("replace 3-gamma fail, not upgrade lcd init code"); + goto fw_reset; + } + cmd[1] = FLASH_MODE_LIC_VALUE; + start_addr = upgrade_func_ft8006u.licoff; + } else if (FLASH_MODE_PARAM == mode) { + cmd[1] = FLASH_MODE_PARAM_VALUE; + start_addr = upgrade_func_ft8006u.paramcfgoff; + } + FTS_DEBUG("flash mode:0x%02x, start addr=0x%04x", cmd[1], start_addr); + + ret = fts_i2c_write(client, cmd, 2); + if (ret < 0) { + FTS_ERROR("upgrade mode(09) cmd write fail"); + goto fw_reset; + } + + delay = FTS_ERASE_SECTOR_DELAY * (len / FTS_MAX_LEN_SECTOR); + ret = fts_fwupg_erase(client, delay); + if (ret < 0) { + FTS_ERROR("erase cmd write fail"); + goto fw_reset; + } + + /* write app */ + ecc_in_host = fts_flash_write_buf(client, start_addr, buf, len, 1); + if (ecc_in_host < 0 ) { + FTS_ERROR("lcd initial code write fail"); + goto fw_reset; + } + + /* ecc */ + ecc_in_tp = fts_fwupg_ecc_cal(client, 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(client); + if (ret < 0) + FTS_ERROR("reset to normal boot fail"); + + msleep(400); + return 0; + +fw_reset: + return -EIO; +} + +/* + * fts_ft8006u_get_hlic_ver - read host lcd init code version + * + * return 0 if host lcd init code is valid, otherwise return error code + */ +static int fts_ft8006u_get_hlic_ver(u8 *initcode) +{ + u8 *hlic_buf = initcode; + u16 hlic_len = 0; + u8 hlic_ver[2] = { 0 }; + + hlic_len = (u16)(((u16)hlic_buf[3]) << 8) + hlic_buf[2]; + FTS_INFO("host lcd init code len:%x", hlic_len); + if ((hlic_len >= FTS_MAX_LEN_SECTOR) || (hlic_len <= FTS_MIN_LEN)) { + FTS_ERROR("host lcd init code len(%x) is too large", hlic_len); + return -EINVAL; + } + + hlic_ver[0] = hlic_buf[hlic_len]; + hlic_ver[1] = hlic_buf[hlic_len + 1]; + + FTS_DEBUG("host lcd init code ver:%x %x", hlic_ver[0], hlic_ver[1]); + if (0xFF != (hlic_ver[0] + hlic_ver[1])) { + FTS_ERROR("host lcd init code version check fail"); + return -EINVAL; + } + + return hlic_ver[0]; + } + +/************************************************************************ +* Name: fts_ft8006u_upgrade +* Brief: +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_ft8006u_upgrade(struct i2c_client *client, u8 *buf, u32 len) +{ + int ret = 0; + u8 *tmpbuf = NULL; + u32 app_len = 0; + + FTS_DEBUG("fw app upgrade..."); + if (NULL == buf) { + FTS_ERROR("fw buf is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > FTS_MAX_LEN_FILE)) { + FTS_ERROR("fw buffer len(%x) fail", len); + return -EINVAL; + } + + app_len = len - upgrade_func_ft8006u.appoff; + tmpbuf = buf + upgrade_func_ft8006u.appoff; + ret = fts_ft8006u_upgrade_mode(client, FLASH_MODE_APP, tmpbuf, app_len); + if (ret < 0) { + FTS_INFO("fw upgrade fail,reset to normal boot"); + if (fts_fwupg_reset_in_boot(client) < 0) + FTS_ERROR("reset to normal boot fail"); + return ret; + } + + return 0; +} + +/************************************************************************ +* Name: fts_ft8006u_lic_upgrade +* Brief: +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_ft8006u_lic_upgrade(struct i2c_client *client, u8 *buf, u32 len) +{ + int ret = 0; + u8 *tmpbuf = NULL; + u32 lic_len = 0; + + FTS_DEBUG("lcd initial code upgrade..."); + if (NULL == buf) { + FTS_ERROR("lcd initial code buffer is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > FTS_MAX_LEN_FILE)) { + FTS_ERROR("lcd initial code buffer len(%x) fail", len); + return -EINVAL; + } + + ret = check_initial_code_valid(client, buf); + if (ret < 0) { + FTS_ERROR("initial code invalid, not upgrade lcd init code"); + return -EINVAL; + } + + /* remalloc memory for initcode, need change content of initcode afterwise */ + lic_len = FTS_MAX_LEN_SECTOR; + tmpbuf = kmalloc(lic_len, GFP_KERNEL); + if (NULL == tmpbuf) { + FTS_INFO("initial code buf malloc fail"); + return -EINVAL; + } + memcpy(tmpbuf, buf, lic_len); + + ret = fts_ft8006u_upgrade_mode(client, FLASH_MODE_LIC, tmpbuf, lic_len); + if (ret < 0) { + FTS_INFO("lcd initial code upgrade fail,reset to normal boot"); + if (fts_fwupg_reset_in_boot(client) < 0) + FTS_ERROR("reset to normal boot fail"); + if (tmpbuf) { + kfree(tmpbuf); + tmpbuf = NULL; + } + return ret; + } + + if (tmpbuf) { + kfree(tmpbuf); + tmpbuf = NULL; + } + return 0; +} + +/************************************************************************ + * Name: fts_ft8006u_param_upgrade + * Brief: + * Input: buf - all.bin + * len - len of all.bin + * Output: + * Return: return 0 if success, otherwise return error code + ***********************************************************************/ +static int fts_ft8006u_param_upgrade(struct i2c_client *client, u8 *buf, u32 len) +{ + int ret = 0; + u8 *tmpbuf = NULL; + u32 param_length = 0; + + FTS_DEBUG("parameter configure upgrade..."); + if (NULL == buf) { + FTS_ERROR("fw file buffer is null"); + return -EINVAL; + } + + if ((len < FTS_MIN_LEN) || (len > FTS_MAX_LEN_FILE)) { + FTS_ERROR("fw file buffer len(%x) fail", len); + return -EINVAL; + } + + tmpbuf = buf + upgrade_func_ft8006u.paramcfgoff; + param_length = len - upgrade_func_ft8006u.paramcfgoff; + ret = fts_ft8006u_upgrade_mode(client, FLASH_MODE_PARAM, tmpbuf, param_length); + if (ret < 0) { + FTS_INFO("fw upgrade fail,reset to normal boot"); + if (fts_fwupg_reset_in_boot(client) < 0) + FTS_ERROR("reset to normal boot fail"); + return ret; + } + + return 0; +} + +/************************************************************************ + * fts_ft8006u_pram_write_remap - write pramboot to pram and start pramboot + * + * return 0 if success, otherwise return error code +***********************************************************************/ +int fts_ft8006u_pram_write_remap(struct i2c_client *client) +{ + int ret = 0; + int ecc_in_host = 0; + int ecc_in_tp = 0; + u8 *pb_buf = NULL; + u32 pb_len = 0; + const struct firmware *fw = NULL; + //struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("[UPGRADE]write pram and remap\n"); + + ret = request_firmware(&fw, FT8006U_PRAMBOOT_FW_NAME, &client->dev); + if (ret < 0) { + FTS_ERROR("[UPGRADE] Request pramboot failed - %s (%d)\n", + FT8006U_PRAMBOOT_FW_NAME, ret); + return ret; + } + + if (fw->size < FTS_MIN_LEN) { + FTS_ERROR("[UPGRADE]pramboot length(%x) fail\n", (unsigned int)fw->size); + return -EINVAL; + } + + pb_buf = (u8*)fw->data; + pb_len= (u32)fw->size; + + /* write pramboot to pram */ + ecc_in_host = fts_pram_write_buf(client, 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(client, 0, pb_len); + if (ecc_in_tp < 0) { + FTS_ERROR( "read pramboot ecc fail"); + return ecc_in_tp; + } + + FTS_DEBUG("[UPGRADE]pram ecc in tp:%x, host:%x\n", ecc_in_tp, ecc_in_host); + /* pramboot checksum != fw checksum, upgrade fail */ + if (ecc_in_host != ecc_in_tp) { + FTS_ERROR("[UPGRADE]pramboot ecc check fail\n"); + return -EIO; + } + + /*start pram*/ + ret = fts_pram_start(client); + if (ret < 0) { + FTS_ERROR("[UPGRADE]pram start fail\n"); + return ret; + } + + return 0; +} + +struct upgrade_func upgrade_func_ft8006u = { + .ctype = {0x0B}, + .fwveroff = 0x210E, + .fwcfgoff = 0x1F80, + .appoff = 0x2000, + .licoff = 0x0000, + .paramcfgoff = 0x12000, + .paramcfgveroff = 0x12004, + .pramboot_supported = true, + .hid_supported = false, + .fts_8006u = true, + .upgrade = fts_ft8006u_upgrade, + .get_hlic_ver = fts_ft8006u_get_hlic_ver, + .lic_upgrade = fts_ft8006u_lic_upgrade, + .param_upgrade = fts_ft8006u_param_upgrade, +}; diff --git a/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft8716.c b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft8716.c new file mode 100644 index 000000000000..b69498847a84 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_ft8716.c @@ -0,0 +1,396 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2010-2016, 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. + * + */ + + +#include +#include "focaltech_flash.h" +#include "focaltech_upgrade_common.h" + +#define APP_FILE_MAX_SIZE (64 * 1024) +#define APP_FILE_MIN_SIZE (8) +#define APP_FILE_VER_MAPPING (0x10E) +#define APP_FILE_VENDORID_MAPPING (0x10C) +#define APP_FILE_CHIPID_MAPPING (0x11E) +#define CONFIG_START_ADDR (0x0000) +#define CONFIG_VENDOR_ID_OFFSET (0x04) +#define CONFIG_PROJECT_ID_OFFSET (0x20) +#define CONFIG_VENDOR_ID_ADDR (CONFIG_START_ADDR+ \ + CONFIG_VENDOR_ID_OFFSET) +#define CONFIG_PROJECT_ID_ADDR (CONFIG_START_ADDR+ \ + CONFIG_PROJECT_ID_OFFSET) + +#define FW_CFG_TOTAL_SIZE 0x00 +#define APP1_START 0x00 +#define APP1_LEN 0x100 +#define APP_VERIF_ADDR (APP1_START + APP1_LEN) +#define APP_VERIF_LEN 0x20 +#define APP1_ECC_ADDR (APP_VERIF_ADDR + APP_P1_ECC) +#define APP2_START (APP_VERIF_ADDR + APP_VERIF_LEN + \ + FW_CFG_TOTAL_SIZE) +#define APP2_ECC_ADDR (APP_VERIF_ADDR + APP_P2_ECC) + +#define FT8716_PRAMBOOT_FW_NAME "FT8716_Pramboot.bin" + +static int fts_ctpm_get_app_bin_file_ver(const char *firmware_name); +static int fts_ctpm_fw_upgrade_with_app_bin_file( + struct i2c_client *client, const char *firmware_name); + +static struct fts_upgrade_fun fts_updatefun_8716 = { + .get_app_bin_file_ver = fts_ctpm_get_app_bin_file_ver, + .upgrade_with_app_bin_file = fts_ctpm_fw_upgrade_with_app_bin_file, + .upgrade_with_lcd_cfg_bin_file = NULL, +}; + + +static struct ft_chip_t fts_chip_type_8716 = { + .type = 0x05, + .chip_idh = 0x87, + .chip_idl = 0x16, + .rom_idh = 0x87, + .rom_idl = 0x16, + .pramboot_idh = 0x87, + .pramboot_idl = 0xA6, + .bootloader_idh = 0x00, + .bootloader_idl = 0x00, + .chip_type = 0x87160805, +}; + +inline void ft8716_set_upgrade_function(struct fts_upgrade_fun **curr) +{ + *curr = &fts_updatefun_8716; +} +inline void ft8716_set_chip_id(struct ft_chip_t **curr) +{ + *curr = &fts_chip_type_8716; +} + +static int fts_ctpm_get_app_bin_file_ver(const char *firmware_name) +{ + int fw_ver = 0; + int rc; + const struct firmware *fw = NULL; + + FTS_FUNC_ENTER(); + + rc = request_firmware(&fw, firmware_name, NULL); + if (rc < 0) { + FTS_ERROR("[UPGRADE] Request firmware failed - %s (%d)\n", + firmware_name, rc); + return rc; + } + if (fw->size < APP_FILE_MIN_SIZE || fw->size > APP_FILE_MAX_SIZE) { + FTS_ERROR("[UPGRADE] FW length(%zd) error\n", fw->size); + return -EIO; + goto file_ver_rel_fw; + } + + fw_ver = fw->data[APP_FILE_VER_MAPPING]; + +file_ver_rel_fw: + release_firmware(fw); + FTS_FUNC_EXIT(); + + return fw_ver; +} + +static int fts_ctpm_get_vendor_id_flash(struct i2c_client *client) +{ + int i_ret = 0; + u8 vendorid[4] = {0}; + u8 auc_i2c_write_buf[10]; + u8 i = 0; + + for (i = 0; i < FTS_UPGRADE_LOOP; i++) { + auc_i2c_write_buf[0] = 0x03; + auc_i2c_write_buf[1] = 0x00; + + auc_i2c_write_buf[2] = (u8)((CONFIG_VENDOR_ID_ADDR - 1) >> 8); + auc_i2c_write_buf[3] = (u8)(CONFIG_VENDOR_ID_ADDR - 1); + i_ret = fts_i2c_read(client, auc_i2c_write_buf, 4, vendorid, 2); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] read flash i_ret = %d\n", i_ret); + continue; + } + break; + } + + FTS_INFO("[UPGRADE] vendor id from flash rom: 0x%x\n", vendorid[1]); + if (i >= FTS_UPGRADE_LOOP) { + FTS_ERROR("[UPGRADE] read vendor id failed\n"); + return -EIO; + } + + return 0; +} + +static int fts_ctpm_write_pram(struct i2c_client *client, + const char *pramfw_name) +{ + int i_ret; + bool inrom = false; + const struct firmware *pramfw = NULL; + + FTS_FUNC_ENTER(); + + i_ret = request_firmware(&pramfw, pramfw_name, &client->dev); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] Request firmware failed - %s (%d)\n", + pramfw_name, i_ret); + return i_ret; + } + + /*check the length of the pramboot*/ + if ((pramfw->size > APP_FILE_MAX_SIZE) || + (pramfw->size < APP_FILE_MIN_SIZE)) { + FTS_ERROR("[UPGRADE] pramboot length(%zd) fail\n", + pramfw->size); + i_ret = -EINVAL; + goto write_pram_rel_fw; + } + + /*send command to FW, reset and start write pramboot*/ + i_ret = fts_ctpm_start_fw_upgrade(client); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] send upgrade cmd to FW error\n"); + goto write_pram_rel_fw; + } + + /*check run in rom or not! if run in rom, will write pramboot*/ + inrom = fts_ctpm_check_run_state(client, FTS_RUN_IN_ROM); + if (!inrom) { + FTS_ERROR("[UPGRADE] not run in rom, write pramboot fail\n"); + i_ret = -EIO; + goto write_pram_rel_fw; + } + + /*write pramboot to pram*/ + i_ret = fts_ctpm_write_pramboot_for_idc(client, + pramfw->size, pramfw->data); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] write pramboot fail\n"); + goto write_pram_rel_fw; + } + + /*read out checksum*/ + i_ret = fts_ctpm_pramboot_ecc(client); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] write pramboot ecc error\n"); + goto write_pram_rel_fw; + } + + /*start pram*/ + fts_ctpm_start_pramboot(client); + + FTS_FUNC_EXIT(); + +write_pram_rel_fw: + release_firmware(pramfw); + return i_ret; +} + +static int fts_ctpm_write_app(struct i2c_client *client, + const u8 *pbt_buf, u32 dw_length) +{ + u32 temp; + int i_ret; + bool inpram = false; + + FTS_FUNC_ENTER(); + + /*check run in pramboot or not if not rum in pramboot, can not upgrade*/ + inpram = fts_ctpm_check_run_state(client, FTS_RUN_IN_PRAM); + if (!inpram) { + FTS_ERROR("[UPGRADE] not run in pram, upgrade fail\n"); + return -EIO; + } + + /*upgrade init*/ + i_ret = fts_ctpm_upgrade_idc_init(client, dw_length); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] upgrade init error, upgrade fail\n"); + return i_ret; + } + + /*read vendor id from flash, if vendor id error, can not upgrade*/ + i_ret = fts_ctpm_get_vendor_id_flash(client); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] read vendor id in flash fail\n"); + return i_ret; + } + + /*erase the app erea in flash*/ + i_ret = fts_ctpm_erase_flash(client); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] erase flash error\n"); + return i_ret; + } + + /*start to write app*/ + i_ret = fts_ctpm_write_app_for_idc(client, dw_length, pbt_buf); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] write app error\n"); + return i_ret; + } + + /*read check sum*/ + temp = 0x1000; + i_ret = fts_ctpm_upgrade_ecc(client, temp, dw_length); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] ecc error\n"); + return i_ret; + } + + FTS_FUNC_EXIT(); + + return 0; +} + +static int fts_ctpm_fw_upgrade_use_buf(struct i2c_client *client, + const u8 *pbt_buf, u32 fwsize) +{ + int i_ret = 0; + + FTS_FUNC_ENTER(); + FTS_DEBUG("[UPGRADE] pramboot\n"); + i_ret = fts_ctpm_write_pram(client, FT8716_PRAMBOOT_FW_NAME); + if (i_ret != 0) { + FTS_ERROR("[UPGRADE] write pram failed\n"); + return -EIO; + } + + i_ret = fts_ctpm_write_app(client, pbt_buf, fwsize); + + FTS_FUNC_EXIT(); + + return i_ret; +} + +static int fts_ctpm_check_vendor_id(struct i2c_client *client, + const u8 *data) +{ + u8 vendor_id; + + fts_i2c_read_reg(client, FTS_REG_VENDOR_ID, &vendor_id); + if ((vendor_id != 0x8D) && + (data[APP_FILE_VENDORID_MAPPING] != vendor_id)) { + FTS_ERROR("[UPGRADE] vendor id(%02x)[%02x] is error\n", + data[APP_FILE_VENDORID_MAPPING], vendor_id); + return -EINVAL; + } + return 0; +} + +static int fts_ctpm_check_chip_id(const u8 *data) +{ + if ((data[APP_FILE_CHIPID_MAPPING] != + fts_chip_type_8716.pramboot_idh) || + (data[APP_FILE_CHIPID_MAPPING + 1] != + fts_chip_type_8716.pramboot_idl)) { + FTS_ERROR("[UPGRADE] chip id(%02X%02X)[%02X%02X] is error\n", + data[APP_FILE_CHIPID_MAPPING], + data[APP_FILE_CHIPID_MAPPING + 1], + fts_chip_type_8716.pramboot_idh, + fts_chip_type_8716.pramboot_idl); + return -EINVAL; + } + return 0; +} + +static bool fts_ctpm_check_app_bin_valid(const u8 *pbt_buf) +{ + u32 len; + + if (pbt_buf[0] != 0x02) { + FTS_ERROR("[UPGRADE] APP.BIN Verify the first byte(%x) error\n", + pbt_buf[0]); + return -EINVAL; + } + + if (!fts_ecc_check(pbt_buf, APP1_START, APP1_LEN, APP1_ECC_ADDR)) { + FTS_ERROR("[UPGRADE] APP.BIN Verify- ecc1 error\n"); + return -EINVAL; + } + + if ((fts_data_word(pbt_buf, APP_VERIF_ADDR + APP_LEN) + + fts_data_word(pbt_buf, APP_VERIF_ADDR + APP_LEN_NE)) != + 0xFFFF) { + FTS_ERROR("[UPGRADE] APP.BIN Verify- Length XOR error\n"); + return -EINVAL; + } + + len = fts_data_word(pbt_buf, APP_VERIF_ADDR + APP_LEN); + FTS_DEBUG("%x %x %x %x", APP2_START, len, + ((u32)fts_data_word(pbt_buf, APP_VERIF_ADDR + APP_LEN_H) << 16), + fts_data_word(pbt_buf, APP_VERIF_ADDR + APP_LEN)); + len -= APP2_START; + if (!fts_ecc_check(pbt_buf, APP2_START, len, APP2_ECC_ADDR)) { + FTS_ERROR("[UPGRADE] APP.BIN Verify- ecc2 error\n"); + return -EINVAL; + } + + return 0; +} + +static int fts_ctpm_fw_upgrade_with_app_bin_file + (struct i2c_client *client, const char *firmware_name) +{ + int i_ret = 0; + const struct firmware *fw = NULL; + + FTS_INFO("[UPGRADE] start upgrade with app.bin\n"); + + i_ret = request_firmware(&fw, firmware_name, &client->dev); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] Request firmware failed - %s (%d)\n", + firmware_name, i_ret); + return i_ret; + } + + if (fw->size < APP_FILE_MIN_SIZE || fw->size > APP_FILE_MAX_SIZE) { + FTS_ERROR("[UPGRADE] FW length(%zd) error\n", fw->size); + i_ret = -EINVAL; + goto file_upgrade_rel_fw; + } + + i_ret = fts_ctpm_check_vendor_id(client, fw->data); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] check vendor id failed\n"); + goto file_upgrade_rel_fw; + } + + i_ret = fts_ctpm_check_chip_id(fw->data); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] check chip id failed\n"); + goto file_upgrade_rel_fw; + } + + i_ret = fts_ctpm_check_app_bin_valid(fw->data); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] app.bin ecc failed\n"); + goto file_upgrade_rel_fw; + } + + i_ret = fts_ctpm_fw_upgrade_use_buf(client, fw->data, fw->size); + if (i_ret < 0) { + FTS_ERROR("[UPGRADE] upgrade app.bin failed\n"); + goto file_upgrade_rel_fw; + } + +file_upgrade_rel_fw: + release_firmware(fw); + return i_ret; +} diff --git a/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_idc.c b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_idc.c new file mode 100644 index 000000000000..90ed285a4c9f --- /dev/null +++ b/drivers/input/touchscreen/focaltech_mmi/focaltech_upgrade_idc.c @@ -0,0 +1,410 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2010-2016, 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. + * + */ +#include +#include +#include +#include "focaltech_flash.h" +#include "focaltech_upgrade_common.h" + +static u8 upgrade_ecc; + +int fts_ctpm_upgrade_idc_init(struct i2c_client *client, u32 dw_length) +{ + int i_ret = 0; + u8 reg_val_id[4] = {0}; + u8 auc_i2c_write_buf[10]; + + FTS_INFO("[UPGRADE] Upgrade setting Init\n"); + + /*read flash ID*/ + auc_i2c_write_buf[0] = 0x05; + reg_val_id[0] = 0x00; + i_ret = fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val_id, 1); + if (i_ret < 0) + return -EIO; + + if (dw_length == 0) + return -EIO; + + /*check the fw length by flash type*/ + if (0x81 == (int)reg_val_id[0]) { + if (dw_length > 1024 * 60) + return -EIO; + } else if (0x80 == (int)reg_val_id[0]) { + if (dw_length > 1024 * 64) + return -EIO; + } + + /*set flash clk*/ + auc_i2c_write_buf[0] = 0x05; + auc_i2c_write_buf[1] = reg_val_id[0]; + auc_i2c_write_buf[2] = 0x00; + fts_i2c_write(client, auc_i2c_write_buf, 3); + + /*send upgrade type to reg 0x09: 0x0B: upgrade; 0x0A: download*/ + auc_i2c_write_buf[0] = 0x09; + auc_i2c_write_buf[1] = 0x0B; + fts_i2c_write(client, auc_i2c_write_buf, 2); + + return 0; +} + + +void fts_ctpm_start_pramboot(struct i2c_client *client) +{ + u8 auc_i2c_write_buf[10]; + + FTS_INFO("[UPGRADE] start pramboot\n"); + auc_i2c_write_buf[0] = 0x08; + fts_i2c_write(client, auc_i2c_write_buf, 1); + msleep(20); +} + + +int fts_ctpm_start_fw_upgrade(struct i2c_client *client) +{ + int i_ret = 0; + + /*send the soft upgrade commond to FW, and start upgrade*/ + FTS_INFO("[UPGRADE] send 0xAA and 0x55 to FW, start upgrade\n"); + + i_ret = fts_i2c_write_reg(client, FTS_RST_CMD_REG1, FTS_UPGRADE_AA); + msleep(20); + i_ret = fts_i2c_write_reg(client, FTS_RST_CMD_REG1, FTS_UPGRADE_55); + msleep(200); + + return i_ret; +} + + +bool fts_ctpm_check_run_state(struct i2c_client *client, int rstate) +{ + int i = 0; + enum FW_STATUS cstate = FTS_RUN_IN_ERROR; + + for (i = 0; i < FTS_UPGRADE_LOOP; i++) { + cstate = fts_ctpm_get_pram_or_rom_id(client); + FTS_DEBUG("[UPGRADE] run state = %d\n", cstate); + + if (cstate == rstate) + return true; + msleep(20); + } + + return false; +} + + +int fts_ctpm_pramboot_ecc(struct i2c_client *client) +{ + u8 auc_i2c_write_buf[10]; + u8 reg_val[4] = {0}; + + FTS_FUNC_ENTER(); + + /*read out checksum if pramboot checksum != host checksum upgrade fail*/ + FTS_INFO("[UPGRADE] read out pramboot checksum\n"); + auc_i2c_write_buf[0] = 0xcc; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 1); + /*pramboot checksum != host checksum, upgrade fail*/ + if (reg_val[0] != upgrade_ecc) { + FTS_ERROR("[UPGRADE] checksum fail pramboot=%x, host=%x\n", + reg_val[0], upgrade_ecc); + return -EIO; + } + FTS_DEBUG("[UPGRADE] checksum success pramboot=%x, host=%x\n", + reg_val[0], upgrade_ecc); + msleep(100); + + FTS_FUNC_EXIT(); + + return 0; +} + + +int fts_ctpm_upgrade_ecc(struct i2c_client *client, + u32 startaddr, u32 length) +{ + u32 i = 0; + u8 auc_i2c_write_buf[10]; + u32 temp; + u8 reg_val[4] = {0}; + int i_ret = 0; + + FTS_INFO("[UPGRADE] read out checksum\n"); + + /*check sum init*/ + auc_i2c_write_buf[0] = 0x64; + fts_i2c_write(client, auc_i2c_write_buf, 1); + msleep(300); + + /*send commond to pramboot to start checksum*/ + auc_i2c_write_buf[0] = 0x65; + auc_i2c_write_buf[1] = (u8)(startaddr >> 16); + auc_i2c_write_buf[2] = (u8)(startaddr >> 8); + auc_i2c_write_buf[3] = (u8)(startaddr); + + if (length > LEN_FLASH_ECC_MAX) + temp = LEN_FLASH_ECC_MAX; + else + temp = length; + + auc_i2c_write_buf[4] = (u8)(temp >> 8); + auc_i2c_write_buf[5] = (u8)(temp); + i_ret = fts_i2c_write(client, auc_i2c_write_buf, 6); + msleep(length / 256); + + /*read status : if check sum is finished*/ + for (i = 0; i < 100; i++) { + auc_i2c_write_buf[0] = 0x6a; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 2); + + if (0xF0 == reg_val[0] && 0x55 == reg_val[1]) + break; + + msleep(20); + } + + if (length > LEN_FLASH_ECC_MAX) { + temp = LEN_FLASH_ECC_MAX; + auc_i2c_write_buf[0] = 0x65; + auc_i2c_write_buf[1] = (u8)(temp >> 16); + auc_i2c_write_buf[2] = (u8)(temp >> 8); + auc_i2c_write_buf[3] = (u8)(temp); + temp = length - LEN_FLASH_ECC_MAX; + auc_i2c_write_buf[4] = (u8)(temp >> 8); + auc_i2c_write_buf[5] = (u8)(temp); + i_ret = fts_i2c_write(client, auc_i2c_write_buf, 6); + + msleep(length / 256); + + for (i = 0; i < 100; i++) { + auc_i2c_write_buf[0] = 0x6a; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 2); + + if (0xF0 == reg_val[0] && 0x55 == reg_val[1]) + break; + + msleep(20); + } + } + + /*read out check sum*/ + auc_i2c_write_buf[0] = 0x66; + i_ret = fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 1); + if (reg_val[0] != upgrade_ecc) { /*if check sum fail, upgrade fail*/ + FTS_ERROR("[UPGRADE] ecc error! FW=%02x upgrade_ecc=%02x\n", + reg_val[0], upgrade_ecc); + return -EIO; + } + + FTS_DEBUG("[UPGRADE] ecc success : FW=%02x upgrade_ecc=%02x\n", + reg_val[0], upgrade_ecc); + + upgrade_ecc = 0; + + return i_ret; +} + + +int fts_ctpm_erase_flash(struct i2c_client *client) +{ + u32 i = 0; + u8 auc_i2c_write_buf[10]; + u8 reg_val[4] = {0}; + + FTS_INFO("[UPGRADE] erase app now\n"); + + /*send to erase flash*/ + auc_i2c_write_buf[0] = 0x61; + fts_i2c_write(client, auc_i2c_write_buf, 1); + msleep(1350); + + for (i = 0; i < 15; i++) { + /*get the erase app status, if get 0xF0AA£¬erase flash success*/ + auc_i2c_write_buf[0] = 0x6a; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 2); + + /*erase flash success*/ + if (0xF0 == reg_val[0] && 0xAA == reg_val[1]) + break; + + msleep(50); + } + + /*erase flash fail*/ + if ((0xF0 != reg_val[0] || 0xAA != reg_val[1]) && (i >= 15)) { + FTS_ERROR("[UPGRADE] erase app error.reset tp and reload FW\n"); + return -EIO; + } + FTS_DEBUG("[UPGRADE] erase app ok\n"); + + return 0; +} + + +int fts_ctpm_write_pramboot_for_idc(struct i2c_client *client, + u32 length, const u8 *readbuf) +{ + u32 i = 0; + u32 j; + u32 temp; + u32 packet_number; + u8 packet_buf[FTS_PACKET_LENGTH + 6]; + + upgrade_ecc = 0; + FTS_INFO("[UPGRADE] write pramboot to pram\n"); + + temp = 0; + packet_number = (length) / FTS_PACKET_LENGTH; + if ((length) % FTS_PACKET_LENGTH > 0) + packet_number++; + + packet_buf[0] = 0xae; + packet_buf[1] = 0x00; + + for (j = 0; j < packet_number; j++) { + temp = j * FTS_PACKET_LENGTH; + packet_buf[2] = (u8) (temp >> 8); + packet_buf[3] = (u8) temp; + if (j < (packet_number - 1)) + temp = FTS_PACKET_LENGTH; + else + temp = (length) % FTS_PACKET_LENGTH; + + packet_buf[4] = (u8) (temp >> 8); + packet_buf[5] = (u8) temp; + + for (i = 0; i < temp; i++) { + packet_buf[6 + i] = readbuf[j * FTS_PACKET_LENGTH + i]; + upgrade_ecc ^= packet_buf[6 + i]; + } + fts_i2c_write(client, packet_buf, temp + 6); + } + + return 0; +} + + +int fts_ctpm_write_app_for_idc(struct i2c_client *client, + u32 length, const u8 *readbuf) +{ + u32 j; + u32 i = 0; + u32 packet_number; + u32 temp; + u32 writelenght; + u8 packet_buf[FTS_PACKET_LENGTH + 6]; + u8 auc_i2c_write_buf[10]; + u8 reg_val[4] = {0}; + + FTS_INFO("[UPGRADE] write app to flash\n"); + + upgrade_ecc = 0; + + packet_number = (length) / FTS_PACKET_LENGTH; + if (((length) % FTS_PACKET_LENGTH) > 0) + packet_number++; + + packet_buf[0] = 0xbf; + + for (j = 0; j < packet_number; j++) { + temp = 0x1000 + j * FTS_PACKET_LENGTH; + + if (j < (packet_number - 1)) + writelenght = FTS_PACKET_LENGTH; + else + writelenght = ((length) % FTS_PACKET_LENGTH); + + packet_buf[1] = (u8) (temp >> 16); + packet_buf[2] = (u8) (temp >> 8); + packet_buf[3] = (u8) temp; + packet_buf[4] = (u8) (writelenght >> 8); + packet_buf[5] = (u8) writelenght; + + for (i = 0; i < writelenght; i++) { + packet_buf[6 + i] = readbuf[(temp - 0x1000 + i)]; + upgrade_ecc ^= packet_buf[6 + i]; + } + + fts_i2c_write(client, packet_buf, (writelenght + 6)); + + for (i = 0; i < 30; i++) { + /*read status and check if the app write is finished*/ + auc_i2c_write_buf[0] = 0x6a; + reg_val[0] = reg_val[1] = 0x00; + fts_i2c_read(client, auc_i2c_write_buf, 1, reg_val, 2); + + if ((j + 0x20 + 0x1000) == + (((reg_val[0]) << 8) | reg_val[1])) + break; + + msleep(20); + } + } + msleep(50); + + return 0; +} + + +#define AL2_FCS_COEF ((1 << 15) + (1 << 10) + (1 << 3)) + +static u16 fts_crc_calc(const u8 *pbt_buf, u32 addr, u16 length) +{ + u16 cFcs = 0; + u16 i, j; + + FTS_DEBUG("[UPGRADE][ECC] %04x data:%04x, len:%04x\n", + (addr), fts_data_word(pbt_buf, (addr)), length); + for (i = 0; i < length; i++) { + cFcs ^= fts_data_word(pbt_buf, (addr + i * 2)); + for (j = 0; j < 16; j++) { + if (cFcs & 1) + cFcs = (u16)((cFcs >> 1) ^ AL2_FCS_COEF); + else + cFcs >>= 1; + } + } + return cFcs; +} + +bool fts_ecc_check(const u8 *pbt_buf, u32 star_addr, u32 len, u16 ecc_addr) +{ + u16 ecc1; + u16 ecc2; + u16 cal_ecc; + + ecc1 = fts_data_word(pbt_buf, ecc_addr); + ecc2 = fts_data_word(pbt_buf, ecc_addr + 2); + + if ((ecc1 + ecc2) != 0xFFFF) + return false; + + cal_ecc = fts_crc_calc(pbt_buf, star_addr, (len / 2)); + + FTS_DEBUG("[UPGRADE][ECC] ecc1 = %x, cal_ecc = %x\n", ecc1, cal_ecc); + if (ecc1 != cal_ecc) { + FTS_DEBUG("[UPGRADE][ECC] ecc error\n"); + return false; + } + return true; +} + diff --git a/drivers/input/touchscreen/focaltech_touch_v3/Android.mk b/drivers/input/touchscreen/focaltech_touch_v3/Android.mk new file mode 100644 index 000000000000..bc25407b4655 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/Android.mk @@ -0,0 +1,111 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(call is-board-platform-in-list,taro kalama), true) + +ifneq ($(FOCALTECH_TOUCH_IC_NAME),) + KBUILD_OPTIONS += CONFIG_INPUT_FOCALTECH_V3_MMI_IC_NAME=$(FOCALTECH_TOUCH_IC_NAME) +else + KBUILD_OPTIONS += CONFIG_INPUT_FOCALTECH_V3_MMI_IC_NAME=ft3681 +endif + +ifeq ($(BOARD_USES_DOUBLE_TAP),true) + KBUILD_OPTIONS += CONFIG_INPUT_FOCALTECH_V3_MMI_ENABLE_DOUBLE_TAP=y +ifeq ($(FTS_DOUBLE_TAP_CONTROL),true) + KBUILD_OPTIONS += CONFIG_FTS_DOUBLE_TAP_CONTROL=y +endif +endif + +ifneq ($(BOARD_USES_TOUCH_PALM),) + KBUILD_OPTIONS += CONFIG_INPUT_FOCALTECH_V3_MMI_ENABLE_PALM=y +endif + +ifneq ($(BOARD_USES_PEN_NOTIFIER),) + KBUILD_OPTIONS += CONFIG_INPUT_FOCALTECH_V3_MMI_PEN_NOTIFIER=y +endif + +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KBUILD_OPTIONS += CONFIG_INPUT_TOUCHSCREEN_MMI=y +endif + +ifeq ($(DRM_PANEL_EVENT_NOTIFICATIONS),true) + KBUILD_OPTIONS += CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS=y +endif + +ifeq ($(FTS_NOISE_TEST_P2P),true) + KBUILD_OPTIONS += CONFIG_FTS_NOISE_TEST_P2P=y +endif + +ifeq ($(FTS_COMPATIBLE_WITH_GKI),true) + KBUILD_OPTIONS += CONFIG_FTS_COMPATIBLE_WITH_GKI=y +endif + +ifeq ($(FTS_SUPPORT_HIGH_RESOLUTION),true) + KBUILD_OPTIONS += CONFIG_FTS_SUPPORT_HIGH_RESOLUTION=y +endif + +ifeq ($(FTS_VDD_GPIO_CONTROL),true) + KBUILD_OPTIONS += CONFIG_FTS_VDD_GPIO_CONTROL=y +endif + +ifeq ($(TOUCHSCREEN_LAST_TIME),true) + KBUILD_OPTIONS += CONFIG_FTS_LAST_TIME=y +endif + +else +ifneq ($(FOCALTECH_TOUCH_IC_NAME),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_V3_MMI_IC_NAME=$(FOCALTECH_TOUCH_IC_NAME) +else + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_V3_MMI_IC_NAME=ft3681 +endif + +ifeq ($(BOARD_USES_DOUBLE_TAP),true) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_V3_MMI_ENABLE_DOUBLE_TAP=y +endif + +ifeq ($(FTS_DOUBLE_TAP_CONTROL),true) + KERNEL_CFLAGS += CONFIG_FTS_DOUBLE_TAP_CONTROL=y +endif + +ifneq ($(BOARD_USES_TOUCH_PALM),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_V3_MMI_ENABLE_PALM=y +endif + +ifneq ($(BOARD_USES_PEN_NOTIFIER),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_V3_MMI_PEN_NOTIFIER=y +endif + +ifeq ($(FTS_SUPPORT_HIGH_RESOLUTION),true) + KERNEL_CFLAGS += CONFIG_FTS_SUPPORT_HIGH_RESOLUTION=y +endif + +ifeq ($(FTS_VDD_GPIO_CONTROL),true) + KERNEL_CFLAGS += CONFIG_FTS_VDD_GPIO_CONTROL=y +endif + +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_MMI=y +endif + +ifeq ($(DRM_PANEL_NOTIFICATIONS),true) + KERNEL_CFLAGS += CONFIG_DRM_PANEL_NOTIFICATIONS=y +endif + +ifeq ($(TOUCHSCREEN_LAST_TIME),true) + KERNEL_CFLAGS += CONFIG_FTS_LAST_TIME=y +endif + +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := focaltech_v3.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/mmi_info.ko +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +endif + +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +KBUILD_OPTIONS_GKI += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/focaltech_touch_v3/Kbuild b/drivers/input/touchscreen/focaltech_touch_v3/Kbuild new file mode 100644 index 000000000000..c03284477988 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/Kbuild @@ -0,0 +1,86 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/focaltech_touch_v3 \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/focaltech_touch_v3/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test + +obj-m := focaltech_v3.o +focaltech_v3-objs += focaltech_core.o +focaltech_v3-objs += focaltech_ex_fun.o +focaltech_v3-objs += focaltech_ex_mode.o +focaltech_v3-objs += focaltech_gesture.o +focaltech_v3-objs += focaltech_esdcheck.o +focaltech_v3-objs += focaltech_point_report_check.o +focaltech_v3-objs += focaltech_flash.o +focaltech_v3-objs += focaltech_flash/focaltech_upgrade_ft3681.o +focaltech_v3-objs += focaltech_spi.o +focaltech_v3-objs += focaltech_test/focaltech_test.o +focaltech_v3-objs += focaltech_test/focaltech_test_ini.o +focaltech_v3-objs += focaltech_test/supported_ic/focaltech_test_ft3681.o +focaltech_v3-objs += focaltech_ts_config.o + +ifneq ($(filter ft3681,$(CONFIG_INPUT_FOCALTECH_V3_MMI_IC_NAME)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_FOCALTECH_V3_MMI_IC_NAME_FT3681 +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers + EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI + focaltech_v3-objs += focaltech_ts_mmi.o +endif +ifneq ($(filter m y,$(CONFIG_INPUT_FOCALTECH_V3_MMI_ENABLE_DOUBLE_TAP)),) + EXTRA_CFLAGS += -DFOCALTECH_SENSOR_EN +endif +ifneq ($(filter m y,$(CONFIG_INPUT_FOCALTECH_V3_MMI_ENABLE_PALM)),) + EXTRA_CFLAGS += -DFOCALTECH_PALM_SENSOR_EN +endif +ifneq ($(filter m y,$(CONFIG_INPUT_FOCALTECH_V3_MMI_PEN_NOTIFIER)),) + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../misc/pen/Module.symvers + EXTRA_CFLAGS += -DFOCALTECH_PEN_NOTIFIER +endif + +ifneq ($(filter m y, $(CONFIG_DRM_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_NOTIFICATIONS +endif + +ifneq ($(filter m y, $(CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_EVENT_NOTIFICATIONS +endif + +ifneq ($(filter m y, $(CONFIG_FTS_NOISE_TEST_P2P)),) + EXTRA_CFLAGS += -DCONFIG_FTS_NOISE_TEST_P2P +endif + +ifneq ($(filter m y, $(CONFIG_FTS_COMPATIBLE_WITH_GKI)),) + EXTRA_CFLAGS += -DCONFIG_FTS_COMPATIBLE_WITH_GKI +endif + +ifneq ($(filter m y, $(CONFIG_FTS_DOUBLE_TAP_CONTROL)),) + EXTRA_CFLAGS += -DCONFIG_FTS_DOUBLE_TAP_CONTROL +endif + +ifneq ($(filter m y, $(CONFIG_FTS_SUPPORT_HIGH_RESOLUTION)),) + EXTRA_CFLAGS += -DCONFIG_FTS_SUPPORT_HIGH_RESOLUTION +endif + +ifneq ($(filter m y, $(CONFIG_FTS_VDD_GPIO_CONTROL)),) + EXTRA_CFLAGS += -DCONFIG_FTS_VDD_GPIO_CONTROL +endif + +ifneq ($(filter m y,$(CONFIG_FTS_LAST_TIME)),) + EXTRA_CFLAGS += -DCONFIG_FTS_LAST_TIME +endif + +ifneq ($(findstring _factory,$(TARGET_PRODUCT)),) +USE_TARGET_PRODUCT := $(strip $(subst _factory,, $(TARGET_PRODUCT))) +else +USE_TARGET_PRODUCT := $(TARGET_PRODUCT) +endif + +ifneq ($(filter bangkk%, $(USE_TARGET_PRODUCT)), ) + EXTRA_CFLAGS += -DPRODUCT_BANGKK +endif + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/../../kernel/msm-$(MODULE_KERNEL_VERSION)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/focaltech_touch_v3/Kconfig b/drivers/input/touchscreen/focaltech_touch_v3/Kconfig new file mode 100644 index 000000000000..919ad060a770 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/Kconfig @@ -0,0 +1,15 @@ +# +# Focaltech Touchscreen driver configuration +# + +config TOUCHSCREEN_FTS_V3 + bool "Focaltech Touchscreen v3" + default n + help + Say Y here if you have Focaltech touch panel. + If unsure, say N. + +config TOUCHSCREEN_FTS_DIRECTORY_V3 + string "Focaltech ts v3 directory name" + default "focaltech_touch_v3" + depends on TOUCHSCREEN_FTS_V3 diff --git a/drivers/input/touchscreen/focaltech_touch_v3/Makefile b/drivers/input/touchscreen/focaltech_touch_v3/Makefile new file mode 100644 index 000000000000..58b445cdea0e --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/Makefile @@ -0,0 +1,18 @@ +all: modules + +modules: + $(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 + + +KBUILD_EXTRA_SYMBOLS += $(M)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(M)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/focaltech_touch_v3/Makefile.kernel b/drivers/input/touchscreen/focaltech_touch_v3/Makefile.kernel new file mode 100644 index 000000000000..d69dc56e9d15 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/Makefile.kernel @@ -0,0 +1,16 @@ +# Makefile for the focaltech touchscreen drivers. + + +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_core.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_ex_fun.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_ex_mode.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_gesture.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_esdcheck.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_point_report_check.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_test/ + + +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_spi.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_flash.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_flash/ + diff --git a/drivers/input/touchscreen/focaltech_touch_v3/firmware/focaltech_ts_fw_csot.bin b/drivers/input/touchscreen/focaltech_touch_v3/firmware/focaltech_ts_fw_csot.bin new file mode 100644 index 000000000000..26a68bd4784f Binary files /dev/null and b/drivers/input/touchscreen/focaltech_touch_v3/firmware/focaltech_ts_fw_csot.bin differ diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_common.h b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_common.h new file mode 100644 index 000000000000..dfbc9497a711 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_common.h @@ -0,0 +1,178 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2020, 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.4 20211214" + +#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 (FTS_CHIP_TYPE & FLAGBITS(0, FLAG_ICSERIALS_LEN-1)) +#define IC_TO_SERIALS(x) ((x) & FLAGBITS(0, FLAG_ICSERIALS_LEN-1)) +#define FTS_CHIP_IDC ((FTS_CHIP_TYPE & FLAGBIT(FLAG_IDC_BIT)) == FLAGBIT(FLAG_IDC_BIT)) +#define FTS_HID_SUPPORTTED ((FTS_CHIP_TYPE & FLAGBIT(FLAG_HID_BIT)) == FLAGBIT(FLAG_HID_BIT)) + +#define FTS_MAX_CHIP_IDS 8 + +#define FTS_CHIP_TYPE_MAPPING {{0x8A, 0x56, 0x62, 0x56, 0x62, 0x56, 0xE2, 0x00, 0x00}} + + +#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_INFO 0x5E + +/*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_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_REG_POCKET_MODE_EN 0x8B + +#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 { + u16 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 ft_chip_id_t { + u16 type; + u16 chip_ids[FTS_MAX_CHIP_IDS]; +}; + +struct ts_ic_info { + bool is_incell; + bool hid_supported; + struct ft_chip_t ids; + struct ft_chip_id_t cid; +}; + +/***************************************************************************** +* 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__ */ diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_config.h b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_config.h new file mode 100644 index 000000000000..d7019d45ba4e --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_config.h @@ -0,0 +1,317 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 _FT8607 0x86070809 +#define _FT8006U 0x8006D80B +#define _FT8006S 0x8006A80B +#define _FT8613 0x8613080C +#define _FT8719 0x8719080D +#define _FT8739 0x8739080E +#define _FT8615 0x8615080F +#define _FT8201 0x82010810 +#define _FT8201AA 0x8201A810 +#define _FT8006P 0x86220811 +#define _FT8613S 0x8613C814 +#define _FT8756 0x87560815 +#define _FT8656 0x86560815 +#define _FT8756M 0x8756A815 +#define _FT8302 0x83020816 +#define _FT8009 0x80090817 +#define _FT8006S_AA 0x86320819 +#define _FT7250 0x7250081A +#define _FT7120 0x7120081B +#define _FT8720 0x8720081C +#define _FT8726 0x8726081C +#define _FT8720H 0x8720E81C +#define _FT8720M 0x8720F81C +#define _FT8016 0x8016081D +#define _FT2388 0x2388081E +#define _FT8006S_AB 0x8642081F +#define _FT8722 0x87220820 +#define _FT8201AB 0x8201B821 +#define _FT8203 0x82030821 +#define _FT8201M 0x8201C821 +#define _FT8006S_AN 0x8006B822 +#define _FT7131 0x71310823 +#define _FT7250AB 0x7250B824 +#define _FT7130 0x71300825 +#define _FT8205 0x82050826 + + + +#define _FT5426 0x54260402 +#define _FT5436 0x54360402 +#define _FT5526 0x55260402 +#define _FT5446 0x54460402 +#define _FT5346 0x53460402 +#define _FT7661 0x76610402 +#define _FT7511 0x75110402 +#define _FT7421 0x74210402 +#define _FT7681 0x76810402 +#define _FT3417 0x34170402 +#define _FT3517 0x35170402 +#define _FT3327 0x33270402 +#define _FT3427 0x34270402 +#define _FT7311 0x73110402 +#define _FT5526_V00 0x5526C402 + +#define _FT5726 0x57260401 +#define _FT5826S 0x5826C401 +#define _FT7811 0x78110401 +#define _FT3617 0x36170401 +#define _FT3717 0x37170401 + +#define _FT6236U 0x6236D003 +#define _FT6336G 0x6336A003 +#define _FT6336U 0x6336D003 +#define _FT6436U 0x6436D003 +#define _FT6436T 0x6436E003 + +#define _FT3267 0x32670004 +#define _FT3367 0x33670004 + +#define _FT5446_Q03 0x5446C482 +#define _FT5446_P03 0x5446A481 +#define _FT5446_N03 0x5446A489 +#define _FT5426_003 0x5426D482 +#define _FT5526_003 0x5526D482 +#define _FT3437_003 0x34370482 + +#define _FT3518 0x35180481 +#define _FT3518U 0x3518D481 +#define _FT3558 0x35580481 +#define _FT3528 0x35280481 +#define _FT5536 0x55360481 +#define _FT3418 0x34180481 + +#define _FT5446U 0x5446D083 +#define _FT5456U 0x5456D083 +#define _FT3417U 0x3417D083 +#define _FT5426U 0x5426D083 +#define _FT3428 0x34280083 + +#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 _FT3168G 0x3168A085 + +#define _FT5726_003 0x5726D486 +#define _FT5726_V03 0x5726C486 +#define _FT3617_003 0x3617D486 + +#define _FT3618 0x36180487 +#define _FT5646 0x56460487 +#define _FT3A58 0x3A580487 +#define _FT3B58 0x3B580487 +#define _FT3D58 0x3D580487 +#define _FT3D59 0x3D590487 +#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 +#define _FT3658G 0x3658A488 + +#define _FT3519 0x35190489 +#define _FT3519T 0x3519E489 +#define _FT3419 0x34190489 +#define _FT5536U_003 0x5536D489 +#define _FT5426G 0x5426A489 +#define _FT3437_N03 0x34370489 + +#define _FT3680 0x3680008A +#define _FT368A 0x368A008A +#define _FT3681 0x3681008A +#define _FT3881 0x3881008A + +#define _FT3169 0x3169008B +#define _FT3269 0x3269008B + + + +/*************************************************/ + +/* + * choose your ic chip type of focaltech + */ +#define FTS_CHIP_TYPE _FT3681 +#define FTS_CHIP_NAME "ft3681" + +/******************* Enables *********************/ +/*********** 1 to enable, 0 to disable ***********/ + +/* + * show debug log info + * enable it for debug, disable it for release + */ +#define FTS_DEBUG_EN 1 + +/* + * 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 + +/* + * Stylus PEN enable + * 1:enable(default),0:disable +*/ +#define FTS_PEN_EN 0 + +/* + * Gesture function enable + * default: disable + */ +#ifdef FOCALTECH_SENSOR_EN +#define FTS_GESTURE_EN 1 +#else +#define FTS_GESTURE_EN 0 +#endif + +/* + * ESD check & protection + * default: disable + */ +#define FTS_ESDCHECK_EN 0 + +/* + * Host test enable + * 1: enable, 0:disable(default) + */ +#define FTS_TEST_EN 1 + +/* + * 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 0 + +/* + * auto upgrade for lcd cfg + */ +#define FTS_AUTO_LIC_UPGRADE_EN 0 + +/* + * Numbers of modules support + */ +#define FTS_GET_MODULE_NUM 0 + +/* + * Usb detect support + */ +#define FTS_USB_DETECT_EN 1 + +/* + * 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 0x0000 +#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 "" +#define FTS_MODULE2_NAME "" +#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_ */ diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_core.c b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_core.c new file mode 100644 index 000000000000..cd1cefa93584 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_core.c @@ -0,0 +1,2739 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 +#include +#include +#include +#include +#include +#if defined(CONFIG_FB) +#include +#include +#elif defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) +#include +#else +#include +#endif +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#define FTS_SUSPEND_LEVEL 1 /* Early-suspend level */ +#endif +#include "focaltech_core.h" +#include + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +extern int fts_mmi_dev_register(struct fts_ts_data *ts_data); +extern void fts_mmi_dev_unregister(struct fts_ts_data *ts_data); +#endif + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_DRIVER_NAME "fts_ts" +#define FTS_DRIVER_PEN_NAME "fts_ts,pen" +#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 2800000 +#define FTS_VTG_MAX_UV 3300000 +#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; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +static int fts_ts_suspend(struct device *dev); +static int fts_ts_resume(struct device *dev); +#endif + +int fts_check_cid(struct fts_ts_data *ts_data, u8 id_h) +{ + int i = 0; + struct ft_chip_id_t *cid = &ts_data->ic_info.cid; + u8 cid_h = 0x0; + + if (cid->type == 0) + return -ENODATA; + + for (i = 0; i < FTS_MAX_CHIP_IDS; i++) { + cid_h = ((cid->chip_ids[i] >> 8) & 0x00FF); + if (cid_h && (id_h == cid_h)) { + return 0; + } + } + + return -ENODATA; +} + +/***************************************************************************** +* 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; + struct fts_ts_data *ts_data = fts_data; + u8 chip_idh = ts_data->ic_info.ids.chip_idh; + + do { + ret = fts_read_reg(FTS_REG_CHIP_ID, &idh); + if ((idh == chip_idh) || (fts_check_cid(ts_data, idh) == 0)) { + FTS_INFO("TP Ready,Device ID:0x%02x", idh); + return 0; + } else + FTS_DEBUG("TP Not Ready,ReadData:0x%02x,ret:%d", idh, ret); + + 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(); +} + +void fts_tp_resume_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) { + disable_irq_nosync(fts_data->irq); + 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) { + enable_irq(fts_data->irq); + 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"); + } else { + msleep(10); + 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 ((0xEB == buf[0]) && (0xAA == buf[1]) && (0x08 == buf[2])) { + FTS_DEBUG("hidi2c change to stdi2c successful"); + } else { + FTS_DEBUG("hidi2c change to stdi2c not support or fail"); + } + } +} + +static int fts_match_cid(struct fts_ts_data *ts_data, + u16 type, u8 id_h, u8 id_l, bool force) +{ +#ifdef FTS_CHIP_ID_MAPPING + u32 i = 0; + u32 j = 0; + struct ft_chip_id_t chip_id_list[] = FTS_CHIP_ID_MAPPING; + u32 cid_entries = sizeof(chip_id_list) / sizeof(struct ft_chip_id_t); + u16 id = (id_h << 8) + id_l; + + memset(&ts_data->ic_info.cid, 0, sizeof(struct ft_chip_id_t)); + for (i = 0; i < cid_entries; i++) { + if (!force && (type == chip_id_list[i].type)) { + break; + } else if (force && (type == chip_id_list[i].type)) { + FTS_INFO("match cid,type:0x%x", (int)chip_id_list[i].type); + ts_data->ic_info.cid = chip_id_list[i]; + return 0; + } + } + + if (i >= cid_entries) { + return -ENODATA; + } + + for (j = 0; j < FTS_MAX_CHIP_IDS; j++) { + if (id == chip_id_list[i].chip_ids[j]) { + FTS_DEBUG("cid:%x==%x", id, chip_id_list[i].chip_ids[j]); + FTS_INFO("match cid,type:0x%x", (int)chip_id_list[i].type); + ts_data->ic_info.cid = chip_id_list[i]; + return 0; + } + } + + return -ENODATA; +#else + return -EINVAL; +#endif +} + + +static int fts_get_chip_types( + struct fts_ts_data *ts_data, + u8 id_h, u8 id_l, bool fw_valid) +{ + u32 i = 0; + struct ft_chip_t ctype[] = FTS_CHIP_TYPE_MAPPING; + 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_INFO("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)) + || (!fts_match_cid(ts_data, ctype[i].type, id_h, id_l, 0))) + 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; + } + + fts_match_cid(ts_data, ctype[i].type, id_h, id_l, 1); + 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 }; + + ts_data->ic_info.is_incell = FTS_CHIP_IDC; + ts_data->ic_info.hid_supported = FTS_HID_SUPPORTTED; + + + 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("chip id 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(cid type=0x%x)", + ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl, + ts_data->ic_info.cid.type); + + return 0; +} + +/***************************************************************************** +* Reprot related +*****************************************************************************/ +static void fts_show_touch_buffer(u8 *data, u32 datalen) +{ + u32 i = 0; + u32 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("touch_buf:%s", tmpbuf); + + if (tmpbuf) { + kfree(tmpbuf); + tmpbuf = NULL; + } +} + +void fts_release_all_finger(void) +{ + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; +#if FTS_MT_PROTOCOL_B_EN + u32 finger_count = 0; + u32 max_touches = ts_data->pdata->max_touch_number; +#endif + + 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); + +#if FTS_PEN_EN + input_report_key(ts_data->pen_dev, BTN_TOOL_PEN, 0); + input_report_key(ts_data->pen_dev, BTN_TOUCH, 0); + input_sync(ts_data->pen_dev); +#endif + + ts_data->touch_points = 0; + ts_data->key_state = 0; + mutex_unlock(&ts_data->report_mutex); +} + +/***************************************************************************** +* 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 *ts_data, struct ts_event *kevent) +{ + int i = 0; + int x = kevent->x; + int y = kevent->y; + int *x_dim = &ts_data->pdata->key_x_coords[0]; + int *y_dim = &ts_data->pdata->key_y_coords[0]; + + if (!ts_data->pdata->have_key) { + return -EINVAL; + } + for (i = 0; i < ts_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(kevent->flag) + && !(ts_data->key_state & (1 << i))) { + input_report_key(ts_data->input_dev, ts_data->pdata->keys[i], 1); + ts_data->key_state |= (1 << i); + FTS_DEBUG("Key%d(%d,%d) DOWN!", i, x, y); + } else if (EVENT_UP(kevent->flag) + && (ts_data->key_state & (1 << i))) { + input_report_key(ts_data->input_dev, ts_data->pdata->keys[i], 0); + ts_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 *ts_data, struct ts_event *events) +{ + int i = 0; + int touch_down_point_cur = 0; + int touch_point_pre = ts_data->touch_points; + u32 max_touch_num = ts_data->pdata->max_touch_number; + bool touch_event_coordinate = false; + struct input_dev *input_dev = ts_data->input_dev; + + for (i = 0; i < ts_data->touch_event_num; i++) { + if (fts_input_report_key(ts_data, &events[i]) == 0) { + continue; + } + + touch_event_coordinate = true; + if (EVENT_DOWN(events[i].flag)) { + input_mt_slot(input_dev, events[i].id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, true); +#if FTS_REPORT_PRESSURE_EN + input_report_abs(input_dev, ABS_MT_PRESSURE, events[i].p); +#endif + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, events[i].area); + input_report_abs(input_dev, ABS_MT_POSITION_X, events[i].x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, events[i].y); + + touch_down_point_cur |= (1 << events[i].id); + touch_point_pre |= (1 << events[i].id); + + if ((ts_data->log_level >= 2) || + ((1 == ts_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); + } +#ifdef CONFIG_FTS_LAST_TIME + if (FTS_TOUCH_DOWN == events[i].flag) { + ts_data->last_event_time = ktime_get_boottime(); + FTS_DEBUG("TOUCH: [%d] logged timestamp\n", i); + } +#endif + } else { + input_mt_slot(input_dev, events[i].id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + touch_point_pre &= ~(1 << events[i].id); + if (ts_data->log_level >= 1) FTS_DEBUG("[B]P%d UP!", events[i].id); + } + } + + if (unlikely(touch_point_pre ^ touch_down_point_cur)) { + for (i = 0; i < max_touch_num; i++) { + if ((1 << i) & (touch_point_pre ^ touch_down_point_cur)) { + if (ts_data->log_level >= 1) FTS_DEBUG("[B]P%d UP!", i); + input_mt_slot(input_dev, i); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + } + } + } + + if (touch_down_point_cur) + input_report_key(input_dev, BTN_TOUCH, 1); + else if (touch_event_coordinate || ts_data->touch_points) { + if (ts_data->touch_points && (ts_data->log_level >= 1)) + FTS_DEBUG("[B]Points All Up!"); + input_report_key(input_dev, BTN_TOUCH, 0); + } + + ts_data->touch_points = touch_down_point_cur; + input_sync(input_dev); + return 0; +} +#else +static int fts_input_report_a(struct fts_ts_data *ts_data, struct ts_event *events) +{ + int i = 0; + int touch_down_point_num_cur = 0; + bool touch_event_coordinate = false; + struct input_dev *input_dev = ts_data->input_dev; + + for (i = 0; i < ts_data->touch_event_num; i++) { + if (fts_input_report_key(ts_data, &events[i]) == 0) { + continue; + } + + touch_event_coordinate = true; + if (EVENT_DOWN(events[i].flag)) { + input_report_abs(input_dev, ABS_MT_TRACKING_ID, events[i].id); +#if FTS_REPORT_PRESSURE_EN + input_report_abs(input_dev, ABS_MT_PRESSURE, events[i].p); +#endif + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, events[i].area); + input_report_abs(input_dev, ABS_MT_POSITION_X, events[i].x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, events[i].y); + input_mt_sync(input_dev); + + touch_down_point_num_cur++; + if ((ts_data->log_level >= 2) || + ((1 == ts_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); + } + } + } + + if (touch_down_point_num_cur) + input_report_key(input_dev, BTN_TOUCH, 1); + else if (touch_event_coordinate || ts_data->touch_points) { + if (ts_data->touch_points && (ts_data->log_level >= 1)) + FTS_DEBUG("[A]Points All Up!"); + input_report_key(input_dev, BTN_TOUCH, 0); + input_mt_sync(input_dev); + } + + ts_data->touch_points = touch_down_point_num_cur; + input_sync(input_dev); + return 0; +} +#endif + +#if FTS_PEN_EN +static int fts_input_pen_report(struct fts_ts_data *ts_data, u8 *pen_buf) +{ + struct input_dev *pen_dev = ts_data->pen_dev; + struct pen_event *pevt = &ts_data->pevent; + + /*get information of stylus*/ + pevt->inrange = (pen_buf[2] & 0x20) ? 1 : 0; + pevt->tip = (pen_buf[2] & 0x01) ? 1 : 0; + pevt->flag = pen_buf[3] >> 6; + pevt->id = pen_buf[5] >> 4; + pevt->x = ((pen_buf[3] & 0x0F) << 8) + pen_buf[4]; + pevt->y = ((pen_buf[5] & 0x0F) << 8) + pen_buf[6]; + pevt->p = ((pen_buf[7] & 0x0F) << 8) + pen_buf[8]; + pevt->tilt_x = (short)((pen_buf[9] << 8) + pen_buf[10]); + pevt->tilt_y = (short)((pen_buf[11] << 8) + pen_buf[12]); + pevt->azimuth = ((pen_buf[13] << 8) + pen_buf[14]); + pevt->tool_type = BTN_TOOL_PEN; + + input_report_key(pen_dev, BTN_STYLUS, !!(pen_buf[2] & 0x02)); + input_report_key(pen_dev, BTN_STYLUS2, !!(pen_buf[2] & 0x08)); + + switch (ts_data->pen_etype) { + case STYLUS_DEFAULT: + if (pevt->tip && pevt->p) { + if ((ts_data->log_level >= 2) || (!pevt->down)) + FTS_DEBUG("[PEN]x:%d,y:%d,p:%d,tip:%d,flag:%d,tilt:%d,%d DOWN", + pevt->x, pevt->y, pevt->p, pevt->tip, pevt->flag, + pevt->tilt_x, pevt->tilt_y); + input_report_abs(pen_dev, ABS_X, pevt->x); + input_report_abs(pen_dev, ABS_Y, pevt->y); + input_report_abs(pen_dev, ABS_PRESSURE, pevt->p); + input_report_abs(pen_dev, ABS_TILT_X, pevt->tilt_x); + input_report_abs(pen_dev, ABS_TILT_Y, pevt->tilt_y); + input_report_key(pen_dev, BTN_TOUCH, 1); + input_report_key(pen_dev, BTN_TOOL_PEN, 1); + pevt->down = 1; + } else if (!pevt->tip && pevt->down) { + FTS_DEBUG("[PEN]x:%d,y:%d,p:%d,tip:%d,flag:%d,tilt:%d,%d UP", + pevt->x, pevt->y, pevt->p, pevt->tip, pevt->flag, + pevt->tilt_x, pevt->tilt_y); + input_report_abs(pen_dev, ABS_X, pevt->x); + input_report_abs(pen_dev, ABS_Y, pevt->y); + input_report_abs(pen_dev, ABS_PRESSURE, pevt->p); + input_report_key(pen_dev, BTN_TOUCH, 0); + input_report_key(pen_dev, BTN_TOOL_PEN, 0); + pevt->down = 0; + } + input_sync(pen_dev); + break; + case STYLUS_HOVER: + if (ts_data->log_level >= 1) + FTS_DEBUG("[PEN][%02X]x:%d,y:%d,p:%d,tip:%d,flag:%d,tilt:%d,%d,%d", + pen_buf[2], pevt->x, pevt->y, pevt->p, pevt->tip, + pevt->flag, pevt->tilt_x, pevt->tilt_y, pevt->azimuth); + input_report_abs(pen_dev, ABS_X, pevt->x); + input_report_abs(pen_dev, ABS_Y, pevt->y); + input_report_abs(pen_dev, ABS_Z, pevt->azimuth); + input_report_abs(pen_dev, ABS_PRESSURE, pevt->p); + input_report_abs(pen_dev, ABS_TILT_X, pevt->tilt_x); + input_report_abs(pen_dev, ABS_TILT_Y, pevt->tilt_y); + input_report_key(pen_dev, BTN_TOOL_PEN, EVENT_DOWN(pevt->flag)); + input_report_key(pen_dev, BTN_TOUCH, pevt->tip); + input_sync(pen_dev); + break; + default: + FTS_ERROR("Unknown stylus event"); + break; + } + + return 0; +} +#endif + +static int fts_read_touchdata(struct fts_ts_data *ts_data, u8 *buf) +{ + int ret = 0; + + ts_data->touch_addr = 0x01; + ret = fts_read(&ts_data->touch_addr, 1, buf, ts_data->touch_size); + + + if (ret < 0) { + FTS_ERROR("touch data(%x) abnormal,ret:%d", buf[1], ret); + return ret; + } + + + return 0; +} + +#ifdef FOCALTECH_PALM_SENSOR_EN +static int fts_palm_detect(u8 reg_data) { + int fd_val = reg_data & 0x03; + + FTS_DEBUG("%s: 0x01=0x%0X!, fd_val=%d\n", __func__, reg_data, fd_val); + if (fd_val == 1) { + FTS_INFO("%s: palm detect!\n", __func__); + return 1; + } + if (fd_val == 2) { + FTS_INFO("%s: palm leave!\n", __func__); + return 2; + } + return 0; +} +#endif + +static int fts_read_parse_touchdata(struct fts_ts_data *ts_data, u8 *touch_buf) +{ + int ret = 0; + u8 gesture_en = 0xFF; + #ifdef FOCALTECH_PALM_SENSOR_EN + int pd_state = 0; + #endif + + memset(touch_buf, 0xFF, FTS_MAX_TOUCH_BUF); + ts_data->ta_size = ts_data->touch_size; + +#ifdef FOCALTECH_SENSOR_EN + fts_read_report_fod_event(ts_data); +#endif + + /*read touch data*/ + ret = fts_read_touchdata(ts_data, touch_buf); + if (ret < 0) { + FTS_ERROR("read touch data fails"); + return TOUCH_ERROR; + } + +#ifdef FOCALTECH_PALM_SENSOR_EN + if (data->palm_detection_enabled) { + pd_state = fts_palm_detect(buf[1]); + if (pd_state > 0) { + if (pd_state == 1) { + del_timer(&fts_data->palm_release_fimer); + if (fts_data->imports && fts_data->imports->report_palm) + fts_data->imports->report_palm(true); + return -1; + } else if (pd_state == 2) { + mod_timer(&fts_data->palm_release_fimer, + jiffies + msecs_to_jiffies(fts_data->palm_release_delay_ms)); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&fts_data->palm_gesture_wakelock, + fts_data->palm_release_delay_ms); +#else + PM_WAKEUP_EVENT(fts_data->palm_gesture_wakelock, + fts_data->palm_release_delay_ms); +#endif + return -1; + } + } + if (buf[1] & 0x08) { + FTS_ERROR("Invalid palm detect value. 0x%0X", buf[1]); + return -1; + } + } +#endif + + if (ts_data->log_level >= 3) + fts_show_touch_buffer(touch_buf, ts_data->ta_size); + + if (ret) + return TOUCH_IGNORE; + + /*gesture*/ + if (ts_data->suspended && ts_data->gesture_support) { + ret = fts_read_reg(FTS_REG_GESTURE_EN, &gesture_en); + if ((ret >= 0) && (gesture_en == ENABLE)) + return TOUCH_GESTURE; + else + FTS_DEBUG("gesture not enable in fw, don't process gesture"); + } + + if ((touch_buf[1] == 0xFF) && (touch_buf[2] == 0xFF) + && (touch_buf[3] == 0xFF) && (touch_buf[4] == 0xFF)) { + FTS_INFO("touch buff is 0xff, need recovery state"); + return TOUCH_FW_INIT; + } + + return ((touch_buf[FTS_TOUCH_E_NUM] >> 4) & 0x0F); +} + +#if FTS_USB_DETECT_EN +static void fts_mcu_usb_detect_set(uint8_t usb_connected) +{ + uint8_t write_data = 0; + uint8_t read_data = 0; + uint8_t retry_cnt = 0; + int ret = 0; + + do{ + if (usb_connected == 0x01) { + write_data= 0x01; + ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, write_data); + if (ret < 0) + FTS_ERROR("set register USB IN fail, ret=%d", ret); + FTS_INFO("%s: USB detect status IN!\n", __func__); + } else { + write_data= 0x00; + ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, write_data); + if (ret < 0) + FTS_ERROR("set register USB OUT fail, ret=%d", ret); + FTS_INFO("%s: USB detect status OUT!\n", __func__); + } + + ret = fts_read_reg(FTS_REG_CHARGER_MODE_EN, &read_data); + if (ret < 0) + FTS_ERROR("read 8b register fail, ret=%d", ret); + retry_cnt++; + }while((write_data != read_data) && retry_cnt < FTS_REG_RETRY_TIMES); +} + +void fts_cable_detect_func(bool force_renew) +{ + struct fts_ts_data *ts_data = fts_data; + uint8_t connect_status = 0; + connect_status = ts_data->usb_detect_flag; + + if ((connect_status != ts_data->usb_connected) || force_renew) { + if (connect_status) { + ts_data->usb_connected = 0x01; + } else { + ts_data->usb_connected = 0x00; + } + + fts_mcu_usb_detect_set(ts_data->usb_connected); + FTS_INFO("%s: Cable status change: 0x%2.2X\n", __func__, ts_data->usb_connected); + } +} +#endif + + +static int fts_irq_read_report(struct fts_ts_data *ts_data) +{ + int i = 0; + int max_touch_num = ts_data->pdata->max_touch_number; + int touch_etype = 0; + u8 event_num = 0; + u8 finger_num = 0; + u8 pointid = 0; + u8 base = 0; + u8 *touch_buf = ts_data->touch_buf; + struct ts_event *events = ts_data->events; + +#if FTS_USB_DETECT_EN + fts_cable_detect_func(false); +#endif + +#if FTS_ESDCHECK_EN + fts_esdcheck_set_intr(1); +#endif + + touch_etype = fts_read_parse_touchdata(ts_data, touch_buf); + switch (touch_etype) { + case TOUCH_DEFAULT: + finger_num = touch_buf[FTS_TOUCH_E_NUM] & 0x0F; + if (finger_num > max_touch_num) { + FTS_ERROR("invalid point_num(%d)", finger_num); + return -EIO; + } + +#ifdef CONFIG_FTS_SUPPORT_HIGH_RESOLUTION + for (i = 0; i < max_touch_num; i++) { + base = FTS_ONE_TCH_LEN * i + 2; + pointid = (touch_buf[FTS_TOUCH_OFF_ID_YH + 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; + } + + events[i].id = pointid; + events[i].flag = touch_buf[FTS_TOUCH_OFF_E_XH + base] >> 6; + + //bit[15:12] | bit[11:4] | bit[3:0] + events[i].x = ((touch_buf[FTS_TOUCH_OFF_E_XH + base] & 0x0F) << 12) \ + | ((touch_buf[FTS_TOUCH_OFF_XL + base] & 0xFF) << 4) \ + | ((touch_buf[FTS_TOUCH_OFF_PRE + base] >> 4) & 0x0F); + + //bit[15:12] | bit[11:4] | bit[3:0] + events[i].y = ((touch_buf[FTS_TOUCH_OFF_ID_YH + base] & 0x0F) << 12) \ + | ((touch_buf[FTS_TOUCH_OFF_YL + base] & 0xFF) << 4) \ + | (touch_buf[FTS_TOUCH_OFF_PRE + base] & 0x0F); + + events[i].p = touch_buf[FTS_TOUCH_OFF_PRE + base] & 0x0F; + events[i].area = touch_buf[FTS_TOUCH_OFF_AREA + base]; + if (events[i].p <= 0) events[i].p = 0x3F; + if (events[i].area <= 0) events[i].area = 0x09; + + event_num++; + if (EVENT_DOWN(events[i].flag) && (finger_num == 0)) { + FTS_INFO("abnormal touch data from fw"); + return -EIO; + } + } +#else + for (i = 0; i < max_touch_num; i++) { + base = FTS_ONE_TCH_LEN * i + 2; + pointid = (touch_buf[FTS_TOUCH_OFF_ID_YH + 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; + } + + events[i].id = pointid; + events[i].flag = touch_buf[FTS_TOUCH_OFF_E_XH + base] >> 6; + events[i].x = ((touch_buf[FTS_TOUCH_OFF_E_XH + base] & 0x0F) << 8) \ + + (touch_buf[FTS_TOUCH_OFF_XL + base] & 0xFF); + events[i].y = ((touch_buf[FTS_TOUCH_OFF_ID_YH + base] & 0x0F) << 8) \ + + (touch_buf[FTS_TOUCH_OFF_YL + base] & 0xFF); + events[i].p = touch_buf[FTS_TOUCH_OFF_PRE + base]; + events[i].area = touch_buf[FTS_TOUCH_OFF_AREA + base]; + if (events[i].p <= 0) events[i].p = 0x3F; + if (events[i].area <= 0) events[i].area = 0x09; + + event_num++; + if (EVENT_DOWN(events[i].flag) && (finger_num == 0)) { + FTS_INFO("abnormal touch data from fw"); + return -EIO; + } + } +#endif + + if (event_num == 0) { + FTS_INFO("no touch point information(%02x)", touch_buf[2]); + return -EIO; + } + ts_data->touch_event_num = event_num; + + mutex_lock(&ts_data->report_mutex); +#if FTS_MT_PROTOCOL_B_EN + fts_input_report_b(ts_data, events); +#else + fts_input_report_a(ts_data, events); +#endif + mutex_unlock(&ts_data->report_mutex); + +#ifdef FOCALTECH_SENSOR_EN + if (ts_data->fod_event) { + input_report_key(ts_data->input_dev, ts_data->fod_event, 1); + input_sync(ts_data->input_dev); + input_report_key(ts_data->input_dev, ts_data->fod_event, 0); + input_sync(ts_data->input_dev); + FTS_INFO("report BTN_TRIGGER_HAPPY code(%d)", ts_data->fod_event); + ts_data->fod_event = 0; + } +#endif + break; + +#if FTS_PEN_EN + case TOUCH_PEN: + mutex_lock(&ts_data->report_mutex); + fts_input_pen_report(ts_data, touch_buf); + mutex_unlock(&ts_data->report_mutex); + break; +#endif + + case TOUCH_EVENT_NUM: + event_num = touch_buf[FTS_TOUCH_E_NUM] & 0x0F; + if (!event_num || (event_num > max_touch_num)) { + FTS_ERROR("invalid touch event num(%d)", event_num); + return -EIO; + } + + ts_data->touch_event_num = event_num; + for (i = 0; i < event_num; i++) { + base = FTS_ONE_TCH_LEN * i + 2; + pointid = (touch_buf[FTS_TOUCH_OFF_ID_YH + base]) >> 4; + if (pointid >= max_touch_num) { + FTS_ERROR("touch point ID(%d) beyond max_touch_number(%d)", + pointid, max_touch_num); + return -EINVAL; + } + + events[i].id = pointid; + events[i].flag = touch_buf[FTS_TOUCH_OFF_E_XH + base] >> 6; + events[i].x = ((touch_buf[FTS_TOUCH_OFF_E_XH + base] & 0x0F) << 8) \ + + (touch_buf[FTS_TOUCH_OFF_XL + base] & 0xFF); + events[i].y = ((touch_buf[FTS_TOUCH_OFF_ID_YH + base] & 0x0F) << 8) \ + + (touch_buf[FTS_TOUCH_OFF_YL + base] & 0xFF); + events[i].p = touch_buf[FTS_TOUCH_OFF_PRE + base]; + events[i].area = touch_buf[FTS_TOUCH_OFF_AREA + base]; + if (events[i].p <= 0) events[i].p = 0x3F; + if (events[i].area <= 0) events[i].area = 0x09; + } + + mutex_lock(&ts_data->report_mutex); +#if FTS_MT_PROTOCOL_B_EN + fts_input_report_b(ts_data, events); +#else + fts_input_report_a(ts_data, events); +#endif + mutex_unlock(&ts_data->report_mutex); + break; + + case TOUCH_EXTRA_MSG: + if (!ts_data->touch_analysis_support) { + FTS_ERROR("touch_analysis is disabled"); + return -EINVAL; + } + + event_num = touch_buf[FTS_TOUCH_E_NUM] & 0x0F; + if (!event_num || (event_num > max_touch_num)) { + FTS_ERROR("invalid touch event num(%d)", event_num); + return -EIO; + } + + ts_data->touch_event_num = event_num; + for (i = 0; i < event_num; i++) { + base = FTS_ONE_TCH_LEN * i + 4; + pointid = (touch_buf[FTS_TOUCH_OFF_ID_YH + base]) >> 4; + if (pointid >= max_touch_num) { + FTS_ERROR("touch point ID(%d) beyond max_touch_number(%d)", + pointid, max_touch_num); + return -EINVAL; + } + + events[i].id = pointid; + events[i].flag = touch_buf[FTS_TOUCH_OFF_E_XH + base] >> 6; + events[i].x = ((touch_buf[FTS_TOUCH_OFF_E_XH + base] & 0x0F) << 8) \ + + (touch_buf[FTS_TOUCH_OFF_XL + base] & 0xFF); + events[i].y = ((touch_buf[FTS_TOUCH_OFF_ID_YH + base] & 0x0F) << 8) \ + + (touch_buf[FTS_TOUCH_OFF_YL + base] & 0xFF); + events[i].p = touch_buf[FTS_TOUCH_OFF_PRE + base]; + events[i].area = touch_buf[FTS_TOUCH_OFF_AREA + base]; + if (events[i].p <= 0) events[i].p = 0x3F; + if (events[i].area <= 0) events[i].area = 0x09; + } + + mutex_lock(&ts_data->report_mutex); +#if FTS_MT_PROTOCOL_B_EN + fts_input_report_b(ts_data, events); +#else + fts_input_report_a(ts_data, events); +#endif + mutex_unlock(&ts_data->report_mutex); + break; + + case TOUCH_GESTURE: + if (0 == fts_gesture_readdata(ts_data, touch_buf)) { + FTS_INFO("succuss to get gesture data in irq handler"); + } + break; + + case TOUCH_FW_INIT: + fts_release_all_finger(); + fts_tp_state_recovery(ts_data); + break; + + case TOUCH_IGNORE: + case TOUCH_ERROR: + break; + + default: + FTS_INFO("unknown touch event(%d)", touch_etype); + break; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_set_intr(0); +#endif + + return 0; +} + +static irqreturn_t fts_irq_handler(int irq, void *data) +{ + struct fts_ts_data *ts_data = fts_data; +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM + int ret = 0; + + if ((ts_data->suspended) && (ts_data->pm_suspend)) { + ret = wait_for_completion_timeout( + &ts_data->pm_completion, + msecs_to_jiffies(FTS_TIMEOUT_COMERR_PM)); + if (!ret) { + FTS_ERROR("Bus don't resume from pm(deep),timeout,skip irq"); + return IRQ_HANDLED; + } + } +#endif + + ts_data->intr_jiffies = jiffies; + fts_prc_queue_work(ts_data); + fts_irq_read_report(ts_data); + if (ts_data->touch_analysis_support && ts_data->ta_flag) { + ts_data->ta_flag = 0; + if (ts_data->ta_buf && ts_data->ta_size) + memcpy(ts_data->ta_buf, ts_data->touch_buf, ts_data->ta_size); + wake_up_interruptible(&ts_data->ts_waitqueue); + } + + 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; + + 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); + + return ret; +} + +#if FTS_PEN_EN +static int fts_input_pen_init(struct fts_ts_data *ts_data) +{ + int ret = 0; + struct input_dev *pen_dev; + struct fts_ts_platform_data *pdata = ts_data->pdata; + + FTS_FUNC_ENTER(); + pen_dev = input_allocate_device(); + if (!pen_dev) { + FTS_ERROR("Failed to allocate memory for input_pen device"); + return -ENOMEM; + } + + pen_dev->dev.parent = ts_data->dev; + pen_dev->name = FTS_DRIVER_PEN_NAME; + 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(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, pdata->x_min, pdata->x_max, 0, 0); + input_set_abs_params(pen_dev, ABS_Y, pdata->y_min, pdata->y_max, 0, 0); + input_set_abs_params(pen_dev, ABS_PRESSURE, 0, 4096, 0, 0); + input_set_abs_params(pen_dev, ABS_TILT_X, -9000, 9000, 0, 0); + input_set_abs_params(pen_dev, ABS_TILT_Y, -9000, 9000, 0, 0); + input_set_abs_params(pen_dev, ABS_Z, 0, 36000, 0, 0); + + ret = input_register_device(pen_dev); + if (ret) { + FTS_ERROR("Input device registration failed"); + input_free_device(pen_dev); + pen_dev = NULL; + return ret; + } + + ts_data->pen_dev = pen_dev; + ts_data->pen_etype = STYLUS_DEFAULT; + FTS_FUNC_EXIT(); + return 0; +} +#endif + +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]); + } + +#ifdef FOCALTECH_SENSOR_EN + input_set_capability(input_dev, EV_KEY, BTN_TRIGGER_HAPPY1); + input_set_capability(input_dev, EV_KEY, BTN_TRIGGER_HAPPY2); +#endif + +#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; + } + +#if FTS_PEN_EN + ret = fts_input_pen_init(ts_data); + if (ret) { + FTS_ERROR("Input-pen device registration failed"); + input_set_drvdata(input_dev, NULL); + input_free_device(input_dev); + input_dev = NULL; + return ret; + } +#endif + + ts_data->input_dev = input_dev; + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_buffer_init(struct fts_ts_data *ts_data) +{ + ts_data->touch_buf = (u8 *)kzalloc(FTS_MAX_TOUCH_BUF, GFP_KERNEL); + if (!ts_data->touch_buf) { + FTS_ERROR("failed to alloc memory for touch buf"); + return -ENOMEM; + } + + ts_data->touch_size = FTS_TOUCH_DATA_LEN; + + + ts_data->touch_analysis_support = 0; + ts_data->ta_flag = 0; + ts_data->ta_size = 0; + + return 0; +} + +#ifdef FOCALTECH_PALM_SENSOR_EN +static void fts_palm_sensor_release_timer_handler(struct timer_list *t) +{ + if (fts_data->imports && fts_data->imports->report_palm) + fts_data->imports->report_palm(false); +} + +int _fts_palm_sensor_set_enable(unsigned int enable) +{ + FTS_INFO("Palm gesture set enable %d!", enable); +/* + * If palm detect function is enabled, interrupt will not disable, IC works in + * normal mode. But in case touch event is reported to input subsystem, skip + * touch event when suspend flag is true. So input subsystem will not take + * wakelock because no one report event. + * In this case, we still need read data from IC, so AP can not enter suspend. + */ + if (enable == 1) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock(&fts_data->palm_gesture_read_wakelock); +#else + PM_STAY_AWAKE(fts_data->palm_gesture_read_wakelock); +#endif + fts_data->palm_detection_enabled = true; + fts_write_reg(0xB0, 0x01); + } else if (enable == 0) { + fts_data->palm_detection_enabled = false; + if (timer_pending(&fts_data->palm_release_fimer)) { + if (fts_data->imports && fts_data->imports->report_palm) + fts_data->imports->report_palm(false); + del_timer(&fts_data->palm_release_fimer); + } + fts_write_reg(0xB0, 0x00); +#ifdef CONFIG_HAS_WAKELOCK + wake_unlock(&fts_data->palm_gesture_read_wakelock); +#else + PM_RELAX(fts_data->palm_gesture_read_wakelock); +#endif + } else { + FTS_INFO("unknown enable symbol\n"); + } + return 0; +} + +int fts_palm_sensor_set_enable(unsigned int enable) +{ + int ret = 0; + + if (!fts_data->suspended) + ret = _fts_palm_sensor_set_enable(enable); + else { + FTS_INFO("Gesture lazy set enable %d!", enable); + if (enable == 1) + fts_data->palm_detection_lazy_set = PALM_SENSOR_LAZY_SET_ENABLE; + else if (enable == 0) + fts_data->palm_detection_lazy_set = PALM_SENSOR_LAZY_SET_DISABLE; + else + FTS_INFO("unknown enable symbol\n"); + } + if (fts_data->imports && fts_data->imports->report_palm) + fts_data->imports->report_palm(false); + return ret; +} + +static int fts_palm_sensor_init(struct fts_ts_data *data) +{ +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&data->palm_gesture_wakelock, WAKE_LOCK_SUSPEND, "palm_detect_wl"); +#else + PM_WAKEUP_REGISTER(NULL, data->palm_gesture_wakelock, "palm_detect_wl"); + if (!data->palm_gesture_wakelock) { + FTS_ERROR("Unable to register device"); + goto exit; + } +#endif +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&data->palm_gesture_read_wakelock, WAKE_LOCK_SUSPEND, "palm_read_wl"); +#else + PM_WAKEUP_REGISTER(NULL, data->palm_gesture_read_wakelock, "palm_read_wl"); + if (!data->palm_gesture_wakelock) { + FTS_ERROR("Unable to register device"); + goto exit; + } +#endif + + timer_setup(&data->palm_release_fimer, fts_palm_sensor_release_timer_handler, 0); + data->palm_release_delay_ms = 850; + + return 0; + +exit: + return 1; +} + +int fts_palm_sensor_remove(struct fts_ts_data *data) +{ +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&data->palm_gesture_wakelock); +#else + PM_WAKEUP_UNREGISTER(data->palm_gesture_wakelock); +#endif +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&data->palm_gesture_read_wakelock); +#else + PM_WAKEUP_UNREGISTER(data->palm_gesture_read_wakelock); +#endif + data->palm_detection_enabled = false; + return 0; +} +#endif + +#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)) { + ts->pins_active = pinctrl_lookup_state(ts->pinctrl, "cli_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)) { + ts->pins_suspend = pinctrl_lookup_state(ts->pinctrl, "cli_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)) { + ts->pins_release = pinctrl_lookup_state(ts->pinctrl, "cli_pmx_ts_release"); + if (IS_ERR_OR_NULL(ts->pins_release)) { + FTS_ERROR("Pin state[release] not found"); + ret = PTR_ERR(ts->pins_release); + } + } + + FTS_INFO("Pinctrl init success"); + 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 */ + +#ifdef CONFIG_FTS_VDD_GPIO_CONTROL +static int fts_vdd_gpio_low(struct fts_ts_data *data) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + + ret = gpio_direction_output(data->pdata->vdd_gpio, 0); + if (ret) { + FTS_ERROR("[GPIO]set_direction for reset gpio failed"); + goto err_vdd_gpio_dir; + } + + FTS_FUNC_EXIT(); + return 0; + +err_vdd_gpio_dir: + if (gpio_is_valid(data->pdata->vdd_gpio)) + gpio_free(data->pdata->vdd_gpio); + + return ret; +} + +static int fts_vdd_gpio_high(struct fts_ts_data *data) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + + ret = gpio_direction_output(data->pdata->vdd_gpio, 1); + if (ret) { + FTS_ERROR("[GPIO]set_direction for reset gpio failed"); + goto err_vdd_gpio_dir; + } + + FTS_FUNC_EXIT(); + return 0; + +err_vdd_gpio_dir: + if (gpio_is_valid(data->pdata->vdd_gpio)) + gpio_free(data->pdata->vdd_gpio); + + return ret; +} +#endif + +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 = regulator_enable(ts_data->vdd); + if (ret) { + FTS_ERROR("enable vdd regulator failed,ret=%d", ret); + } + +#ifdef CONFIG_FTS_VDD_GPIO_CONTROL + fts_vdd_gpio_high(ts_data); +#endif + + 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); + } + } + +#if FTS_PINCTRL_EN + fts_pinctrl_select_normal(ts_data); +#endif + ts_data->power_disabled = false; + } + } else { + if (!ts_data->power_disabled) { + FTS_DEBUG("regulator disable !"); + gpio_direction_output(ts_data->pdata->reset_gpio, 1); + msleep(1); + ret = regulator_disable(ts_data->vdd); + if (ret) { + FTS_ERROR("disable vdd regulator failed,ret=%d", ret); + } + +#ifdef CONFIG_FTS_VDD_GPIO_CONTROL + fts_vdd_gpio_low(ts_data); +#endif + + 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); + } + } +#if FTS_PINCTRL_EN + if (ts_data->gesture_support == true) { + fts_pinctrl_select_suspend(ts_data); + } +#endif + if (gpio_is_valid(ts_data->pdata->reset_gpio)) { + gpio_direction_output(ts_data->pdata->reset_gpio, 0); + FTS_INFO("Set gpio_reset to 0 success"); + } + + 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; + } + + if (regulator_count_voltages(ts_data->vdd) > 0) { + 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); + regulator_put(ts_data->vdd); + return ret; + } + } + + ts_data->vcc_i2c = regulator_get(ts_data->dev, "vcc_i2c"); + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + if (regulator_count_voltages(ts_data->vcc_i2c) > 0) { + 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); + regulator_put(ts_data->vcc_i2c); + } + } + } + +#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; +} + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +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 +#endif /* FTS_POWER_SOURCE_CUST_EN */ + +static int fts_gpio_configure(struct fts_ts_data *data) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + +#ifdef CONFIG_FTS_VDD_GPIO_CONTROL + if (gpio_is_valid(data->pdata->vdd_gpio)) { + ret = gpio_request(data->pdata->vdd_gpio, "fts_vdd_gpio"); + if (ret) { + FTS_ERROR("[GPIO]vdd_gpio request failed"); + goto err_vdd_gpio_dir; + } + + ret = gpio_direction_output(data->pdata->vdd_gpio, 0); + if (ret) { + FTS_ERROR("[GPIO]set_direction for reset gpio failed"); + goto err_vdd_gpio_dir; + } + } +#endif + + /* 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, 0); + 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); +#ifdef CONFIG_FTS_VDD_GPIO_CONTROL +err_vdd_gpio_dir: + if (gpio_is_valid(data->pdata->vdd_gpio)) + gpio_free(data->pdata->vdd_gpio); +#endif +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]); + } + + +#ifdef CONFIG_FTS_VDD_GPIO_CONTROL + pdata->vdd_gpio = of_get_named_gpio_flags(np, "focaltech,vdd-gpio", + 0, &pdata->vdd_gpio_flags); + if (pdata->vdd_gpio < 0) + FTS_ERROR("Unable to get vdd_gpio"); +#endif + + /* 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; + } + + pdata->pocket_mode_ctrl = of_property_read_bool(np, "fts,pocket-mode-ctrl"); + if (pdata->pocket_mode_ctrl) + FTS_INFO("Support fts touch pocket mode"); + +#ifdef CONFIG_FTS_VDD_GPIO_CONTROL + FTS_INFO("max touch number:%d, irq gpio:%d, reset gpio:%d, vdd_gpio:%d", + pdata->max_touch_number, pdata->irq_gpio, pdata->reset_gpio, pdata->vdd_gpio); +#else + FTS_INFO("max touch number:%d, irq gpio:%d, reset gpio:%d,", + pdata->max_touch_number, pdata->irq_gpio, pdata->reset_gpio); +#endif + + pdata->edge_ctrl = of_property_read_bool(np, + "focaltech,edge-ctrl"); + if (pdata->edge_ctrl) + FTS_INFO("support focaltech edge mode"); + + pdata->interpolation_ctrl = of_property_read_bool(np, + "focaltech,interpolation-ctrl"); + if (pdata->interpolation_ctrl) + FTS_INFO("support focaltech interpolation mode"); + + pdata->report_rate_ctrl = of_property_read_bool(np, + "focaltech,report_rate-ctrl"); + if (pdata->report_rate_ctrl) + FTS_INFO("support focaltech report rate switch mode"); + + pdata->sample_ctrl = of_property_read_bool(np, + "focaltech,sample-ctrl"); + if (pdata->sample_ctrl) + FTS_INFO("support focaltech sample mode"); + + + FTS_FUNC_EXIT(); + return 0; +} + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +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); +} + +#if defined(CONFIG_FB) +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 (!evdata) { + FTS_ERROR("evdata is null"); + return 0; + } + + 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_DRM) +#if defined(CONFIG_DRM_PANEL) +static struct drm_panel *active_panel; + +static int drm_check_dt(struct device_node *np) +{ + int i = 0; + int count = 0; + struct device_node *node = NULL; + struct drm_panel *panel = NULL; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) { + FTS_ERROR("find drm_panel count(%d) fail", count); + return -ENODEV; + } + + 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)) { + FTS_INFO("find drm_panel successfully"); + active_panel = panel; + return 0; + } + } + + FTS_ERROR("no find drm_panel"); + return -ENODEV; +} + +static int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank = NULL; + struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data, + fb_notif); + + if (!evdata) { + FTS_ERROR("evdata is null"); + return 0; + } + + if (!((event == DRM_PANEL_EARLY_EVENT_BLANK ) + || (event == DRM_PANEL_EVENT_BLANK))) { + FTS_INFO("event(%lu) do not need process\n", event); + return 0; + } + + blank = evdata->data; + FTS_INFO("DRM event:%lu,blank:%d", event, *blank); + switch (*blank) { + case DRM_PANEL_BLANK_UNBLANK: + if (DRM_PANEL_EARLY_EVENT_BLANK == event) { + FTS_INFO("resume: event = %lu, not care\n", event); + } else if (DRM_PANEL_EVENT_BLANK == event) { + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + } + break; + case DRM_PANEL_BLANK_POWERDOWN: + if (DRM_PANEL_EARLY_EVENT_BLANK == event) { + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); + } else if (DRM_PANEL_EVENT_BLANK == event) { + FTS_INFO("suspend: event = %lu, not care\n", event); + } + break; + default: + FTS_INFO("DRM BLANK(%d) do not need process\n", *blank); + break; + } + + return 0; +} +#else +static int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank = NULL; + struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data, + fb_notif); + + if (!evdata) { + FTS_ERROR("evdata is null"); + return 0; + } + + if (!((event == MSM_DRM_EARLY_EVENT_BLANK ) + || (event == MSM_DRM_EVENT_BLANK))) { + FTS_INFO("event(%lu) do not need process\n", event); + return 0; + } + + blank = evdata->data; + FTS_INFO("DRM event:%lu,blank:%d", event, *blank); + switch (*blank) { + case MSM_DRM_BLANK_UNBLANK: + if (MSM_DRM_EARLY_EVENT_BLANK == event) { + FTS_INFO("resume: event = %lu, not care\n", event); + } else if (MSM_DRM_EVENT_BLANK == event) { + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + } + break; + case MSM_DRM_BLANK_POWERDOWN: + if (MSM_DRM_EARLY_EVENT_BLANK == event) { + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); + } else if (MSM_DRM_EVENT_BLANK == event) { + FTS_INFO("suspend: event = %lu, not care\n", event); + } + break; + default: + FTS_INFO("DRM BLANK(%d) do not need process\n", *blank); + break; + } + + return 0; +} +#endif +#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); + + cancel_work_sync(&fts_data->resume_work); + 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); + + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); +} +#endif +#endif + +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); + + if (ts_data->spi->dev.of_node && !mmi_device_is_available(ts_data->spi->dev.of_node)) { + FTS_ERROR("mmi: device not supported\n"); + return -ENODEV; + } + + 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"); + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) + ret = drm_check_dt(ts_data->dev->of_node); + if (ret) { + FTS_ERROR("parse drm-panel fail"); + } +#endif +#endif +#endif + } 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); + init_waitqueue_head(&ts_data->ts_waitqueue); + + /* 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_buffer_init(ts_data); + if (ret) { + FTS_ERROR("buffer init fail"); + goto err_buffer_init; + } + + ret = fts_gpio_configure(ts_data); + if (ret) { + FTS_ERROR("configure the gpios fail"); + goto err_gpio_config; + } + +#if FTS_POWER_SOURCE_CUST_EN + ret = fts_power_source_init(ts_data); + if (ret) { + FTS_ERROR("fail to get power(regulator)"); + goto err_power_init; + } +#endif + +#if (!FTS_CHIP_IDC) + fts_reset_proc(200); +#endif + + ret = fts_get_ic_information(ts_data); + if (ret) { + FTS_ERROR("not focal IC, unregister driver"); + goto err_irq_req; + } + + 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"); + } + + ret = fts_point_report_check_init(ts_data); + if (ret) { + FTS_ERROR("init point report check fail"); + } + + 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"); + } + +#ifdef FOCALTECH_PALM_SENSOR_EN + ret = fts_palm_sensor_init(ts_data); + if (ret) { + FTS_ERROR("init palm detect sensor fail"); + } +#endif + +#if FTS_TEST_EN + ret = fts_test_init(ts_data); + if (ret) { + FTS_ERROR("init host test fail"); + } +#endif + +#if FTS_ESDCHECK_EN + ret = fts_esdcheck_init(ts_data); + if (ret) { + FTS_ERROR("init esd check fail"); + } +#endif + + ret = fts_irq_registration(ts_data); + if (ret) { + FTS_ERROR("request irq failed"); + goto err_irq_req; + } + + ret = fts_fwupg_init(ts_data); + if (ret) { + FTS_ERROR("init fw upgrade fail"); + } + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + if (ts_data->ts_workqueue) { + INIT_WORK(&ts_data->resume_work, fts_resume_work); + } +#endif + +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM + init_completion(&ts_data->pm_completion); + ts_data->pm_suspend = false; +#endif + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if defined(CONFIG_FB) + 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_DRM) + ts_data->fb_notif.notifier_call = drm_notifier_callback; +#if defined(CONFIG_DRM_PANEL) + if (active_panel) { + ret = drm_panel_notifier_register(active_panel, &ts_data->fb_notif); + if (ret) + FTS_ERROR("[DRM]drm_panel_notifier_register fail: %d\n", ret); + } +#else + ret = msm_drm_register_client(&ts_data->fb_notif); + if (ret) { + FTS_ERROR("[DRM]Unable to register fb_notifier: %d\n", ret); + } +#endif +#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 +#endif +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + fts_mmi_dev_register(ts_data); +#endif + + FTS_FUNC_EXIT(); + return 0; + +err_irq_req: +#if FTS_POWER_SOURCE_CUST_EN +err_power_init: + fts_power_source_exit(ts_data); +#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); +err_gpio_config: + kfree_safe(ts_data->touch_buf); +err_buffer_init: + input_unregister_device(ts_data->input_dev); +#if FTS_PEN_EN + input_unregister_device(ts_data->pen_dev); +#endif +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(); + + cancel_work_sync(&fts_data->resume_work); + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + fts_mmi_dev_unregister(ts_data); +#endif + fts_point_report_check_exit(ts_data); + 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_TEST_EN + fts_test_exit(ts_data); +#endif + +#if FTS_ESDCHECK_EN + fts_esdcheck_exit(ts_data); +#endif + +#ifdef FOCALTECH_PALM_SENSOR_EN + fts_palm_sensor_remove(ts_data); +#endif + + fts_gesture_exit(ts_data); + + free_irq(ts_data->irq, ts_data); + + fts_bus_exit(ts_data); + + input_unregister_device(ts_data->input_dev); +#if FTS_PEN_EN + input_unregister_device(ts_data->pen_dev); +#endif + + if (ts_data->ts_workqueue) + destroy_workqueue(ts_data->ts_workqueue); + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if defined(CONFIG_FB) + if (fb_unregister_client(&ts_data->fb_notif)) + FTS_ERROR("[FB]Error occurred while unregistering fb_notifier."); +#elif defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) + if (active_panel) + drm_panel_notifier_unregister(active_panel, &ts_data->fb_notif); +#else + if (msm_drm_unregister_client(&ts_data->fb_notif)) + FTS_ERROR("[DRM]Error occurred while unregistering fb_notifier.\n"); +#endif +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts_data->early_suspend); +#endif +#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->touch_buf); + kfree_safe(ts_data->pdata); + kfree_safe(ts_data); + + FTS_FUNC_EXIT(); + + return 0; +} + +#ifdef FOCALTECH_SENSOR_EN +bool fts_is_fod_resume(struct fts_ts_data *ts_data) +{ + unsigned long fod_timeout = msecs_to_jiffies(3000); + + fod_timeout += ts_data->fod_jiffies; + if (time_before(jiffies, fod_timeout)) { + return true; + } + + return false; +} +#endif + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +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 FOCALTECH_PALM_SENSOR_EN + if (ts_data->palm_detection_lazy_set == PALM_SENSOR_LAZY_SET_ENABLE) { + FTS_INFO("palm detection enabled, can't suspend"); + return 0; + } +#endif + +#if FTS_ESDCHECK_EN + fts_esdcheck_suspend(ts_data); +#endif + +#ifdef FOCALTECH_PALM_SENSOR_EN + if (ts_data->palm_detection_enabled) { + ret = enable_irq_wake(ts_data->irq); + if (ret) { + FTS_DEBUG("enable_irq_wake(irq:%d) fail", ts_data->irq); + } + fts_release_all_finger(); + ts_data->suspended = true; + FTS_INFO("Enter from palm detect suspend mode."); + return 0; + } +#endif + + if (ts_data->gesture_support) { + 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 + } + } + + fts_release_all_finger(); + ts_data->suspended = true; + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_ts_resume(struct device *dev) +{ + struct fts_ts_data *ts_data = fts_data; + + FTS_FUNC_ENTER(); + if (!ts_data->suspended) { + FTS_DEBUG("Already in awake state"); + return 0; + } + + +#ifdef FOCALTECH_PALM_SENSOR_EN + if (ts_data->palm_detection_lazy_set == PALM_SENSOR_LAZY_SET_ENABLE) { + FTS_INFO("palm detection enabled, don't need to resume"); + return 0; + } +#endif + + ts_data->suspended = false; + fts_release_all_finger(); + + if (!ts_data->ic_info.is_incell) { +#if FTS_POWER_SOURCE_CUST_EN + fts_power_source_resume(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(ts_data); +#endif + + if (ts_data->gesture_support) { + fts_gesture_resume(ts_data); + } + else { + fts_irq_enable(); + } + +#if FTS_USB_DETECT_EN + fts_cable_detect_func(true); +#endif + + FTS_FUNC_EXIT(); + return 0; +} +#endif + +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM +static int fts_pm_suspend(struct device *dev) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + FTS_INFO("system enters into pm_suspend"); + ts_data->pm_suspend = true; + reinit_completion(&ts_data->pm_completion); + return 0; +} + +static int fts_pm_resume(struct device *dev) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + FTS_INFO("system resumes from pm_suspend"); + ts_data->pm_suspend = false; + complete(&ts_data->pm_completion); + return 0; +} + +static const struct dev_pm_ops fts_dev_pm_ops = { + .suspend = fts_pm_suspend, + .resume = fts_pm_resume, +}; +#endif + +/***************************************************************************** +* TP Driver +*****************************************************************************/ +static int fts_ts_probe(struct spi_device *spi) +{ + int ret = 0; + struct fts_ts_data *ts_data = NULL; + + FTS_INFO("Touch Screen(SPI BUS) driver probe..."); + + spi->chip_select = 0; + 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 = (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->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; +} + +static int fts_ts_remove(struct spi_device *spi) +{ + return fts_ts_remove_entry(spi_get_drvdata(spi)); +} + +static const struct spi_device_id fts_ts_id[] = { + {FTS_DRIVER_NAME, 0}, + {}, +}; +static const struct of_device_id fts_dt_match[] = { + {.compatible = "focaltech,fts", }, + {}, +}; +MODULE_DEVICE_TABLE(of, fts_dt_match); + +static struct spi_driver fts_ts_driver = { + .probe = fts_ts_probe, + .remove = fts_ts_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_id, +}; + +static int __init fts_ts_init(void) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + ret = spi_register_driver(&fts_ts_driver); + if ( ret != 0 ) { + FTS_ERROR("Focaltech touch screen driver init failed!"); + } + FTS_FUNC_EXIT(); + return ret; +} + +static void __exit fts_ts_exit(void) +{ + spi_unregister_driver(&fts_ts_driver); +} + +module_init(fts_ts_init); +module_exit(fts_ts_exit); + +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +MODULE_AUTHOR("FocalTech Driver Team"); +MODULE_DESCRIPTION("FocalTech Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_core.h b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_core.h new file mode 100644 index 000000000000..eec08c0315f2 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_core.h @@ -0,0 +1,432 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "focaltech_common.h" +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +#include +#endif +#ifdef FTS_USB_DETECT_EN +#include +#endif +#ifdef CONFIG_FTS_LAST_TIME +#include +#endif + +/***************************************************************************** +* 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_COORDS_ARR_SIZE 4 +#define FTS_ONE_TCH_LEN 6 +#define FTS_TOUCH_DATA_LEN (FTS_MAX_POINTS_SUPPORT * FTS_ONE_TCH_LEN + 2) + +#define FTS_GESTURE_POINTS_MAX 6 +#define FTS_GESTURE_DATA_LEN (FTS_GESTURE_POINTS_MAX * 4 + 4) + +#define FTS_SIZE_PEN 15 +#define FTS_SIZE_DEFAULT 15 + +#define FTS_MAX_ID 0x0A +#define FTS_TOUCH_OFF_E_XH 0 +#define FTS_TOUCH_OFF_XL 1 +#define FTS_TOUCH_OFF_ID_YH 2 +#define FTS_TOUCH_OFF_YL 3 +#define FTS_TOUCH_OFF_PRE 4 +#define FTS_TOUCH_OFF_AREA 5 +#define FTS_TOUCH_E_NUM 1 +#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 FTS_MAX_COMPATIBLE_TYPE 4 +#define FTS_MAX_COMMMAND_LENGTH 16 + +#define FTS_MAX_TOUCH_BUF 4096 + +#ifdef FTS_USB_DETECT_EN +#define FTS_REG_RETRY_TIMES 5 +#endif + + +/***************************************************************************** +* 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 1 +#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[FTS_MAX_COMMMAND_LENGTH]; +}; + +struct fts_ts_platform_data { + u32 irq_gpio; + u32 irq_gpio_flags; + u32 reset_gpio; + u32 reset_gpio_flags; +#ifdef CONFIG_FTS_VDD_GPIO_CONTROL + u32 vdd_gpio; + u32 vdd_gpio_flags; +#endif + 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; + bool pocket_mode_ctrl; + bool edge_ctrl; + bool interpolation_ctrl; + bool report_rate_ctrl; + bool sample_ctrl; +}; + +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; +}; + + +#ifdef FOCALTECH_PALM_SENSOR_EN +enum palm_sensor_lazy_set { + PALM_SENSOR_LAZY_SET_NONE = 0, + PALM_SENSOR_LAZY_SET_ENABLE, + PALM_SENSOR_LAZY_SET_DISABLE, +}; +#endif + +struct pen_event { + int down; + int inrange; + int tip; + 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 tilt_x; + int tilt_y; + int azimuth; + int tool_type; +}; +struct fts_mode_info { + int pocket_mode; + int interpolation; + int sample; + int report_rate_mode; + int edge_mode[2]; +}; +struct fts_ts_data { + struct i2c_client *client; + struct spi_device *spi; + struct device *dev; + struct input_dev *input_dev; + struct input_dev *pen_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; + wait_queue_head_t ts_waitqueue; + struct ftxxxx_proc proc; + struct ftxxxx_proc proc_ta; + struct ftxxxx_proc proc_raw; + spinlock_t irq_lock; + struct mutex report_mutex; + struct mutex bus_lock; + unsigned long intr_jiffies; + int irq; + int log_level; + int fw_is_running; /* confirm fw is running when using spi:default 0 */ + int dummy_byte; + int refresh_rate; +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM + struct completion pm_completion; + bool pm_suspend; +#endif + bool suspended; + bool fw_loading; + bool force_reflash; + bool irq_disabled; + bool power_disabled; + bool glove_mode; + bool cover_mode; + bool charger_mode; + bool touch_analysis_support; + bool prc_support; + bool prc_mode; + bool esd_support; + + bool gesture_support; /* gesture enable or disable, default: disable */ + u8 gesture_bmode; /*gesture buffer mode*/ + + u8 pen_etype; + struct pen_event pevent; + struct ts_event events[FTS_MAX_POINTS_SUPPORT]; /* multi-touch */ + u8 touch_addr; + u32 touch_size; + u8 *touch_buf; + int touch_event_num; + int touch_points; + int key_state; + int ta_flag; + u32 ta_size; + u8 *ta_buf; + + u8 *bus_tx_buf; + u8 *bus_rx_buf; + int bus_type; + 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 + +#if FTS_USB_DETECT_EN + bool usb_detect_flag; + uint8_t usb_connected; +#endif + +#ifdef FOCALTECH_SENSOR_EN + bool wakeable; + bool fod_suspended; + unsigned char gesture_type; + int zerotap_data[1]; + int zero_enable; + int fod_event; + unsigned long fod_jiffies; +#endif + u8 gsx_cmd; + +#ifdef FOCALTECH_PALM_SENSOR_EN + bool palm_detection_enabled; + enum palm_sensor_lazy_set palm_detection_lazy_set; + struct timer_list palm_release_fimer; + unsigned int palm_release_delay_ms; +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock palm_gesture_wakelock; +#else + struct wakeup_source *palm_gesture_wakelock; +#endif +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock palm_gesture_read_wakelock; +#else + struct wakeup_source *palm_gesture_read_wakelock; +#endif +#endif + + struct mutex mode_lock; + struct fts_mode_info set_mode; + struct fts_mode_info get_mode; +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + struct ts_mmi_class_methods *imports; +#endif +#ifdef CONFIG_FTS_LAST_TIME + ktime_t last_event_time; +#endif + +}; + +enum _FTS_BUS_TYPE { + BUS_TYPE_NONE, + BUS_TYPE_I2C, + BUS_TYPE_SPI, + BUS_TYPE_SPI_V2, +}; + +enum _FTS_TOUCH_ETYPE { + TOUCH_DEFAULT = 0x00, + TOUCH_EVENT_NUM = 0x02, + TOUCH_EXTRA_MSG = 0x08, + TOUCH_PEN = 0x0B, + TOUCH_GESTURE = 0x80, + TOUCH_FW_INIT = 0x81, + TOUCH_IGNORE = 0xFE, + TOUCH_ERROR = 0xFF, +}; + +enum _FTS_STYLUS_ETYPE { + STYLUS_DEFAULT, + STYLUS_HOVER, +}; + +enum _FTS_GESTURE_BMODE { + GESTURE_BM_REG, + GESTURE_BM_TOUCH, +}; + +/***************************************************************************** +* 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); +int fts_spi_transfer_direct(u8 *writebuf, u32 writelen, u8 *readbuf, u32 readlen); + +/* 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); +#ifdef FOCALTECH_SENSOR_EN +void fts_read_report_fod_event(struct fts_ts_data *ts_data); +#endif + +/* 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 */ +int fts_esdcheck_init(struct fts_ts_data *ts_data); +int fts_esdcheck_exit(struct fts_ts_data *ts_data); +void fts_esdcheck_switch(struct fts_ts_data *ts_data, bool enable); +void fts_esdcheck_proc_busy(struct fts_ts_data *ts_data, bool proc_debug); +void fts_esdcheck_suspend(struct fts_ts_data *ts_data); +void fts_esdcheck_resume(struct fts_ts_data *ts_data); + +/* Host 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*/ +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); + +/* FW upgrade */ +int fts_fwupg_init(struct fts_ts_data *ts_data); +int fts_fwupg_exit(struct fts_ts_data *ts_data); +int fts_upgrade_bin(char *fw_name, bool force); +void fts_fwupg_bin(void); +int fts_enter_test_environment(bool test_state); +int fts_fw_update_vendor_name(const char* name); + +/* Other */ +int fts_reset_proc(int hdelayms); +int fts_check_cid(struct fts_ts_data *ts_data, u8 id_h); +int fts_wait_tp_to_valid(void); +void fts_release_all_finger(void); +void fts_tp_state_recovery(struct fts_ts_data *ts_data); +void fts_tp_resume_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_power_source_ctrl(struct fts_ts_data *ts_data, int enable); +#ifdef FOCALTECH_SENSOR_EN +bool fts_is_fod_resume(struct fts_ts_data *ts_data); +#endif +#endif /* __LINUX_FOCALTECH_CORE_H__ */ diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_esdcheck.c b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_esdcheck.c new file mode 100644 index 000000000000..7d382eccb1bf --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_esdcheck.c @@ -0,0 +1,451 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 +#define ESD_INTR_INTERVALS 200 /* unit:ms */ + +/***************************************************************************** +* 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 idh = 0; + u8 chip_id = ts_data->ic_info.ids.chip_idh; + + for (i = 0; i < 3; i++) { + ret = fts_read_reg(FTS_REG_CHIP_ID, &idh); + if (ret < 0) { + FTS_ERROR("read chip id fail,ret:%d", ret); + fts_esdcheck_data.nack_cnt++; + } else { + if ((idh == chip_id) || (fts_check_cid(ts_data, idh) == 0)) { + break; + } else { + FTS_DEBUG("read chip_id:%x,retry:%d", idh, 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(®_addr, 1, ®_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; + unsigned long intr_timeout = msecs_to_jiffies(ESD_INTR_INTERVALS); + + /* 1. esdcheck is interrupt, then return */ + intr_timeout += ts_data->intr_jiffies; + if (time_before(jiffies, intr_timeout)) { + 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, ®_value); + if ( ret < 0 ) { + fts_esdcheck_data.nack_cnt++; + } else if ( (reg_value & 0x70) == FTS_REG_WORKMODE_FACTORY_VALUE) { + FTS_DEBUG("in factory mode(%x), no check esd", reg_value); + return 0; + } + + /* 5. Get Chip ID */ + hardware_reset = get_chip_id(ts_data); + + /* 6. 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); + } + + /* 7. 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 (ts_data->esd_support && fts_esdcheck_data.mode) { +#if LCD_ESD_PATCH + idc_esdcheck_lcderror(ts_data); +#endif + esdcheck_algorithm(ts_data); + queue_delayed_work(ts_data->ts_workqueue, &ts_data->esdcheck_work, + msecs_to_jiffies(ESDCHECK_WAIT_TIME)); + } +} + +/***************************************************************************** +* 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: +*****************************************************************************/ +void fts_esdcheck_proc_busy(struct fts_ts_data *ts_data, bool proc_debug) +{ + if (ts_data->esd_support) { + fts_esdcheck_data.proc_debug = proc_debug; + } +} + +/***************************************************************************** +* Name: fts_esdcheck_switch +* Brief: FTS esd check function switch. +* Input: enable: 1 - Enable esd check +* 0 - Disable esd check +* Output: +* Return: +*****************************************************************************/ +void fts_esdcheck_switch(struct fts_ts_data *ts_data, bool enable) +{ + if (ts_data->esd_support) { + 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(); + } +} + +void fts_esdcheck_suspend(struct fts_ts_data *ts_data) +{ + if (ts_data->esd_support) { + FTS_FUNC_ENTER(); + fts_esdcheck_switch(ts_data, DISABLE); + fts_esdcheck_data.suspend = 1; + fts_esdcheck_data.intr = 0; + fts_esdcheck_data.intr_cnt = 0; + FTS_FUNC_EXIT(); + } +} + +void fts_esdcheck_resume(struct fts_ts_data *ts_data) +{ + if (ts_data->esd_support) { + FTS_FUNC_ENTER(); + fts_esdcheck_switch(ts_data, ENABLE); + fts_esdcheck_data.suspend = 0; + fts_esdcheck_data.intr = 0; + fts_esdcheck_data.intr_cnt = 0; + FTS_FUNC_EXIT(); + } +} + +static ssize_t fts_esdcheck_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_DEBUG("enable esdcheck"); + ts_data->esd_support = ENABLE; + fts_esdcheck_switch(ts_data, ENABLE); + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_DEBUG("disable esdcheck"); + fts_esdcheck_switch(ts_data, DISABLE); + ts_data->esd_support = 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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count = snprintf(buf, PAGE_SIZE, "Esd check: %s\n", \ + ts_data->esd_support ? "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, +}; + +static 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 function"); + 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_create_esd_sysfs(ts_data->dev); + ts_data->esd_support = FTS_ESDCHECK_EN; + fts_esdcheck_switch(ts_data, ENABLE); + FTS_FUNC_EXIT(); + return 0; +} + +int fts_esdcheck_exit(struct fts_ts_data *ts_data) +{ + fts_esdcheck_data.mode = DISABLE; + ts_data->esd_support = DISABLE; + cancel_delayed_work_sync(&ts_data->esdcheck_work); + sysfs_remove_group(&ts_data->dev->kobj, &fts_esd_group); + return 0; +} +#endif /* FTS_ESDCHECK_EN */ diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ex_fun.c b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ex_fun.c new file mode 100644 index 000000000000..4d1c71052482 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ex_fun.c @@ -0,0 +1,1518 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 "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_WRITE_DATA_DIRECT 16 +#define PROC_READ_DATA_DIRECT 17 +#define PROC_CONFIGURE 18 +#define PROC_CONFIGURE_INTR 20 +#define PROC_GET_DRIVER_INFO 21 +#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 = PDE_DATA(file_inode(filp)); + struct ftxxxx_proc *proc = &ts_data->proc; + + if (buflen < 1) { + FTS_ERROR("apk proc count(%d) fail", buflen); + 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]; + if (buflen == 1) { + ret = buflen; + goto proc_write_err; + } + + 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(ts_data, ENABLE); +#endif + } else { +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ts_data, 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 >= FTS_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; + 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_HW_RESET: + if (buflen < PROC_BUF_SIZE) { + 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; + + case PROC_READ_DATA_DIRECT: + writelen = buflen - 1; + if (writelen >= FTS_MAX_COMMMAND_LENGTH) { + FTS_ERROR("cmd(PROC_READ_DATA_DIRECT) length(%d) fail", writelen); + goto proc_write_err; + } + memcpy(proc->cmd, writebuf + 1, writelen); + proc->cmd_len = writelen; + break; + + case PROC_WRITE_DATA_DIRECT: + writelen = buflen - 1; + ret = fts_spi_transfer_direct(writebuf + 1, writelen, NULL, 0); + if (ret < 0) { + FTS_ERROR("PROC_WRITE_DATA_DIRECT write error"); + goto proc_write_err; + } + break; + + case PROC_CONFIGURE: + ts_data->spi->mode = writebuf[1]; + ts_data->spi->bits_per_word = writebuf[2]; + ts_data->spi->max_speed_hz = *(u32 *)(writebuf + 4); + FTS_INFO("spi,mode=%d,bits=%d,speed=%d", ts_data->spi->mode, + ts_data->spi->bits_per_word, ts_data->spi->max_speed_hz); + ret = spi_setup(ts_data->spi); + if (ret) { + FTS_ERROR("spi setup fail"); + goto proc_write_err; + } + break; + + case PROC_CONFIGURE_INTR: + if (writebuf[1] == 0) + fts_irq_disable(); + else + fts_irq_enable(); + 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 = PDE_DATA(file_inode(filp)); + struct ftxxxx_proc *proc = &ts_data->proc; + + if (buflen <= 0) { + FTS_ERROR("apk proc read count(%d) fail", buflen); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == readbuf) { + FTS_ERROR("apk proc 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_READ_DATA: + num_read_chars = buflen; + ret = fts_read(proc->cmd, proc->cmd_len, readbuf, num_read_chars); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA read error"); + goto proc_read_err; + } + break; + + case PROC_READ_DATA_DIRECT: + num_read_chars = buflen; + ret = fts_spi_transfer_direct(proc->cmd, proc->cmd_len, readbuf, num_read_chars); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA_DIRECT read error"); + goto proc_read_err; + } + break; + + case PROC_GET_DRIVER_INFO: + if (buflen >= 64) { + num_read_chars = buflen; + readbuf[0] = ts_data->bus_type; + snprintf(&readbuf[32], buflen - 32, "%s", FTS_DRIVER_VERSION); + } + break; + + default: + break; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + + ret = num_read_chars; +proc_read_err: + if ((num_read_chars > 0) && copy_to_user(buff, readbuf, num_read_chars)) { + FTS_ERROR("copy to user error"); + ret = -EFAULT; + } + + if ((buflen > PROC_BUF_SIZE) && readbuf) { + kfree(readbuf); + readbuf = NULL; + } + return ret; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) +static const struct proc_ops fts_proc_fops = { + .proc_read = fts_debug_read, + .proc_write = fts_debug_write, +}; +#else +static const struct file_operations fts_proc_fops = { + .owner = THIS_MODULE, + .read = fts_debug_read, + .write = fts_debug_write, +}; +#endif +#else +static int fts_debug_write( + struct file *filp, const char __user *buff, unsigned long count, 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) { + FTS_ERROR("apk proc wirte count(%d) fail", buflen); + 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(ts_data, ENABLE); +#endif + } else { +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ts_data, 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 >= FTS_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; + 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_HW_RESET: + if (buflen < PROC_BUF_SIZE) { + 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; + + case PROC_READ_DATA_DIRECT: + writelen = buflen - 1; + if (writelen >= FTS_MAX_COMMMAND_LENGTH) { + FTS_ERROR("cmd(PROC_READ_DATA_DIRECT) length(%d) fail", writelen); + goto proc_write_err; + } + memcpy(proc->cmd, writebuf + 1, writelen); + proc->cmd_len = writelen; + break; + + case PROC_WRITE_DATA_DIRECT: + writelen = buflen - 1; + ret = fts_spi_transfer_direct(writebuf + 1, writelen, NULL, 0); + if (ret < 0) { + FTS_ERROR("PROC_WRITE_DATA_DIRECT write error"); + goto proc_write_err; + } + break; + + case PROC_CONFIGURE: + ts_data->spi->mode = writebuf[1]; + ts_data->spi->bits_per_word = writebuf[2]; + ts_data->spi->max_speed_hz = *(u32 *)(writebuf + 4); + FTS_INFO("spi,mode=%d,bits=%d,speed=%d", ts_data->spi->mode, + ts_data->spi->bits_per_word, ts_data->spi->max_speed_hz); + ret = spi_setup(ts_data->spi); + if (ret) { + FTS_ERROR("spi setup fail"); + goto proc_write_err; + } + break; + + case PROC_CONFIGURE_INTR: + if (writebuf[1] == 0) + fts_irq_disable(); + else + fts_irq_enable(); + 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) { + FTS_ERROR("apk proc read count(%d) fail", buflen); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == readbuf) { + FTS_ERROR("apk proc 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_READ_DATA: + num_read_chars = buflen; + ret = fts_read(proc->cmd, proc->cmd_len, readbuf, num_read_chars); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA read error"); + goto proc_read_err; + } + break; + + case PROC_READ_DATA_DIRECT: + num_read_chars = buflen; + ret = fts_spi_transfer_direct(proc->cmd, proc->cmd_len, readbuf, num_read_chars); + if (ret < 0) { + FTS_ERROR("PROC_READ_DATA_DIRECT read error"); + goto proc_read_err; + } + break; + + case PROC_GET_DRIVER_INFO: + if (buflen >= 64) { + num_read_chars = buflen; + readbuf[0] = ts_data->bus_type; + snprintf(&readbuf[16], buflen - 16, "%s", FTS_DRIVER_VERSION); + } + break; + + default: + break; + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + + ret = num_read_chars; +proc_read_err: + if (!memcpy(page, readbuf, num_read_chars)) { + FTS_ERROR("copy to user error"); + ret = -EFAULT; + } + + if ((buflen > PROC_BUF_SIZE) && readbuf) { + kfree(readbuf); + readbuf = NULL; + } + return ret; +} +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) +static int fts_ta_open(struct inode *inode, struct file *file) +{ + struct fts_ts_data *ts_data = PDE_DATA(inode); + + if (ts_data->touch_analysis_support) { + FTS_INFO("fts_ta open"); + ts_data->ta_buf = kzalloc(FTS_MAX_TOUCH_BUF, GFP_KERNEL); + if (!ts_data->ta_buf) { + FTS_ERROR("kzalloc for ta_buf fails"); + return -ENOMEM; + } + } + return 0; +} + +static int fts_ta_release(struct inode *inode, struct file *file) +{ + struct fts_ts_data *ts_data = PDE_DATA(inode); + + if (ts_data->touch_analysis_support) { + FTS_INFO("fts_ta close"); + ts_data->ta_flag = 0; + if (ts_data->ta_buf) { + kfree(ts_data->ta_buf); + ts_data->ta_buf = NULL; + } + } + return 0; +} + +static ssize_t fts_ta_read( + struct file *filp, char __user *buff, size_t count, loff_t *ppos) +{ + int read_num = (int)count; + struct fts_ts_data *ts_data = PDE_DATA(file_inode(filp)); + + if (!ts_data->touch_analysis_support || !ts_data->ta_buf) { + FTS_ERROR("touch_analysis is disabled, or ta_buf is NULL"); + return -EINVAL; + } + + if (!(filp->f_flags & O_NONBLOCK)) { + ts_data->ta_flag = 1; + wait_event_interruptible(ts_data->ts_waitqueue, !ts_data->ta_flag); + } + + read_num = (ts_data->ta_size < read_num) ? ts_data->ta_size : read_num; + if ((read_num > 0) && (copy_to_user(buff, ts_data->ta_buf, read_num))) { + FTS_ERROR("copy to user error"); + return -EFAULT; + } + + return read_num; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) +static const struct proc_ops fts_procta_fops = { + .proc_open = fts_ta_open, + .proc_release = fts_ta_release, + .proc_read = fts_ta_read, +}; +#else +static const struct file_operations fts_procta_fops = { + .open = fts_ta_open, + .release = fts_ta_release, + .read = fts_ta_read, +}; +#endif +#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_data(PROC_NAME, 0777, NULL, &fts_proc_fops, ts_data); + 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 + + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) + ts_data->proc_ta.proc_entry = proc_create_data("fts_ta", 0777, NULL, \ + &fts_procta_fops, ts_data); + if (!ts_data->proc_ta.proc_entry) { + FTS_ERROR("create proc_ta entry fail"); + return -ENOMEM; + } +#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 + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)) + if (ts_data->proc_ta.proc_entry) + proc_remove(ts_data->proc_ta.proc_entry); +#endif +} + +/************************************************************************ + * sysfs interface + ***********************************************************************/ +/* fts_hw_reset interface */ +static ssize_t fts_hw_reset_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct irq_desc *desc = irq_to_desc(ts_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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_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"); + ts_data->fw_is_running = false; + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_INFO("[EX-FUN]set to fw mode"); + ts_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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + if (true == ts_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_tpfwver_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int ret = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + 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 + + ret = fts_read_reg(FTS_REG_FW_VER, &fwver); + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(1); +#endif + + if ((ret < 0) || (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_tpfwver_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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_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!"); + + } else { + 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(®, 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; + } + } + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + + mutex_unlock(&input_dev->mutex); + return count; +} + +/* fts_upgrade_bin interface */ +static ssize_t fts_fwupgradebin_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +static ssize_t fts_fwupgradebin_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char fwname[FILE_NAME_LENGTH] = { 0 }; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) { + FTS_ERROR("fw bin name's length(%d) fail", (int)count); + return -EINVAL; + } + memset(fwname, 0, sizeof(fwname)); + snprintf(fwname, FILE_NAME_LENGTH, "%s", buf); + fwname[count - 1] = '\0'; + + FTS_INFO("upgrade with bin file through sysfs node"); + mutex_lock(&input_dev->mutex); + fts_upgrade_bin(fwname, 0); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* fts_force_upgrade interface */ +static ssize_t fts_fwforceupg_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +static ssize_t fts_fwforceupg_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char fwname[FILE_NAME_LENGTH]; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) { + FTS_ERROR("fw bin name's length(%d) fail", (int)count); + return -EINVAL; + } + memset(fwname, 0, sizeof(fwname)); + snprintf(fwname, FILE_NAME_LENGTH, "%s", buf); + fwname[count - 1] = '\0'; + + FTS_INFO("force upgrade through sysfs node"); + mutex_lock(&input_dev->mutex); + fts_upgrade_bin(fwname, 1); + 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 = dev_get_drvdata(dev); + struct fts_ts_platform_data *pdata = ts_data->pdata; + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(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 += snprintf(buf + count, PAGE_SIZE, + "reset gpio:%d,int gpio:%d,irq:%d\n", + pdata->reset_gpio, pdata->irq_gpio, ts_data->irq); + + count += snprintf(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 += snprintf(buf + count, PAGE_SIZE, "BUS:%s,addr:0x%x\n", + "I2C", ts_data->client->addr); + } else { + count += snprintf(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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_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_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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "touch point buffer:\n"); + for (i = 0; i < FTS_TOUCH_DATA_LEN; i++) { + count += snprintf(buf + count, PAGE_SIZE, "%02x ", ts_data->touch_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 node */ +static ssize_t fts_log_level_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "log level:%d\n", + ts_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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + sscanf(buf, "%d", &value); + FTS_DEBUG("log level:%d->%d", ts_data->log_level, value); + ts_data->log_level = value; + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +/* fts_pen node */ +static ssize_t fts_pen_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "pen event:%s\n", + ts_data->pen_etype ? "hover" : "default"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_pen_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + sscanf(buf, "%d", &value); + FTS_DEBUG("pen event:%d->%d", ts_data->pen_etype, value); + ts_data->pen_etype = value; + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +/* fts_touch_size node */ +static ssize_t fts_touchsize_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "touch size:%d\n", ts_data->touch_size); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_touchsize_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + sscanf(buf, "%d", &value); + if ((value > 2) && (value < FTS_MAX_TOUCH_BUF)) { + FTS_DEBUG("touch size:%d->%d", ts_data->touch_size, value); + ts_data->touch_size = value; + } else + FTS_DEBUG("touch size:%d invalid", value); + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +/* fts_ta_mode node */ +static ssize_t fts_tamode_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count += snprintf(buf + count, PAGE_SIZE, "touch analysis:%s\n", \ + ts_data->touch_analysis_support ? "Enable" : "Disable"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_tamode_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + FTS_FUNC_ENTER(); + mutex_lock(&input_dev->mutex); + sscanf(buf, "%d", &value); + ts_data->touch_analysis_support = !!value; + FTS_DEBUG("set touch analysis:%d", ts_data->touch_analysis_support); + mutex_unlock(&input_dev->mutex); + FTS_FUNC_EXIT(); + + return count; +} + +/* get the fw version example:cat fw_version */ +static DEVICE_ATTR(fts_fw_version, S_IRUGO | S_IWUSR, fts_tpfwver_show, fts_tpfwver_store); + +/* 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); +/* upgrade from fw bin file example:echo "*.bin" > fts_upgrade_bin */ +static DEVICE_ATTR(fts_upgrade_bin, S_IRUGO | S_IWUSR, fts_fwupgradebin_show, fts_fwupgradebin_store); +static DEVICE_ATTR(fts_force_upgrade, S_IRUGO | S_IWUSR, fts_fwforceupg_show, fts_fwforceupg_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); +static DEVICE_ATTR(fts_pen, S_IRUGO | S_IWUSR, fts_pen_show, fts_pen_store); +static DEVICE_ATTR(fts_touch_size, S_IRUGO | S_IWUSR, fts_touchsize_show, fts_touchsize_store); +static DEVICE_ATTR(fts_ta_mode, S_IRUGO | S_IWUSR, fts_tamode_show, fts_tamode_store); + +/* 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_upgrade_bin.attr, + &dev_attr_fts_force_upgrade.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, + &dev_attr_fts_pen.attr, + &dev_attr_fts_touch_size.attr, + &dev_attr_fts_ta_mode.attr, + 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; +} diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ex_mode.c b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ex_mode.c new file mode 100644 index 000000000000..38157ffa0677 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ex_mode.c @@ -0,0 +1,307 @@ +/* + * + * FocalTech ftxxxx TouchScreen driver. + * + * Copyright (c) 2012-2020, 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, +}; + +/***************************************************************************** +* 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; + u8 m_val = 0; + + if (value) + m_val = 0x01; + else + m_val = 0x00; + + switch (mode) { + case MODE_GLOVE: + ret = fts_write_reg(FTS_REG_GLOVE_MODE_EN, m_val); + if (ret < 0) { + FTS_ERROR("MODE_GLOVE switch to %d fail", m_val); + } + break; + case MODE_COVER: + ret = fts_write_reg(FTS_REG_COVER_MODE_EN, m_val); + if (ret < 0) { + FTS_ERROR("MODE_COVER switch to %d fail", m_val); + } + break; + case MODE_CHARGER: + ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, m_val); + if (ret < 0) { + FTS_ERROR("MODE_CHARGER switch to %d fail", m_val); + } + 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 = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_read_reg(FTS_REG_GLOVE_MODE_EN, &val); + count = snprintf(buf + count, PAGE_SIZE, "Glove Mode:%s\n", + ts_data->glove_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "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 = dev_get_drvdata(dev); + + 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 = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_read_reg(FTS_REG_COVER_MODE_EN, &val); + count = snprintf(buf + count, PAGE_SIZE, "Cover Mode:%s\n", + ts_data->cover_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "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 = dev_get_drvdata(dev); + + 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 = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_read_reg(FTS_REG_CHARGER_MODE_EN, &val); + count = snprintf(buf + count, PAGE_SIZE, "Charger Mode:%s\n", + ts_data->charger_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "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 = dev_get_drvdata(dev); + + 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; +} + + +/* 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 struct attribute *fts_touch_mode_attrs[] = { + &dev_attr_fts_glove_mode.attr, + &dev_attr_fts_cover_mode.attr, + &dev_attr_fts_charger_mode.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); + } + + 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; + + 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) successfully"); + } + + 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; +} diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_flash.c b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_flash.c new file mode 100644 index 000000000000..99b9bd21a5be --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_flash.c @@ -0,0 +1,2249 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 "" + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +u8 fw_file[] = { +#include FTS_UPGRADE_FW_FILE +}; + +u8 fw_file2[] = { +#include FTS_UPGRADE_FW2_FILE +}; + +u8 fw_file3[] = { +#include FTS_UPGRADE_FW3_FILE +}; + +struct upgrade_module module_list[] = { + {FTS_MODULE_ID, FTS_MODULE_NAME, fw_file, sizeof(fw_file)}, + {FTS_MODULE2_ID, FTS_MODULE2_NAME, fw_file2, sizeof(fw_file2)}, + {FTS_MODULE3_ID, FTS_MODULE3_NAME, fw_file3, sizeof(fw_file3)}, +}; + +struct upgrade_func *upgrade_func_list[] = { + &upgrade_func_ft5662, +}; + +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 (upg->ts_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, ®_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 ((ECC_CHECK_MODE_CRC16 == upg->func->pram_ecc_check_mode) || + (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) { + return fts_pram_ecc_cal_algo(upg, saddr, len); + } else { + 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 (ECC_CHECK_MODE_XOR == upg->func->pram_ecc_check_mode) { + 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 ((ECC_CHECK_MODE_CRC16 == upg->func->pram_ecc_check_mode) || + (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, ®_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; +} + +/************************************************************************ +* Name: fts_fwupg_enter_into_boot +* Brief: enter into boot environment, ready for upgrade +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_fwupg_enter_into_boot(void) +{ + int ret = 0; + bool fwvalid = false; + bool state = false; + struct fts_upgrade *upg = fwupgrade; + + 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 */ + 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; +} + +/************************************************************************ + * 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); + } + + FTS_ERROR("flash status fail,ok:%04x,read:%04x,retries:%d", + flash_status, read_status, i); + 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 ((ECC_CHECK_MODE_CRC16 == upg->func->fw_ecc_check_mode) || + (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 ((ECC_CHECK_MODE_CRC16 == upg->func->fw_ecc_check_mode) || + (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 (wr_ok == read_status) { + break; + } + mdelay(FTS_RETRIES_DELAY_WRITE); + } + } + + ecc_in_host = (int)ecc_tmp; + if ((ECC_CHECK_MODE_CRC16 == upg->func->fw_ecc_check_mode) || + (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 + ***********************************************************************/ +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; +} + +static int __maybe_unused fts_read_file_default(char *file_name, u8 **file_buf) +{ + int ret = 0; + char file_path[FILE_NAME_LENGTH] = { 0 }; + struct file *filp = NULL; + struct inode *inode; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + mm_segment_t old_fs; +#endif + loff_t pos; + loff_t file_len = 0; + + if ((NULL == file_name) || (NULL == file_buf)) { + FTS_ERROR("filename/filebuf is NULL"); + return -EINVAL; + } + + snprintf(file_path, FILE_NAME_LENGTH, "%s%s", FTS_FW_BIN_FILEPATH, file_name); + filp = filp_open(file_path, O_RDONLY, 0); + if (IS_ERR(filp)) { + FTS_ERROR("open %s file fail", file_path); + return -ENOENT; + } + +#if 1 + inode = filp->f_inode; +#else + /* reserved for linux earlier verion */ + inode = filp->f_dentry->d_inode; +#endif + + file_len = inode->i_size; + *file_buf = (u8 *)vmalloc(file_len); + if (NULL == *file_buf) { + FTS_ERROR("file buf malloc fail"); + filp_close(filp, NULL); + return -ENOMEM; + } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + old_fs = get_fs(); + set_fs(KERNEL_DS); +#endif + pos = 0; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + ret = vfs_read(filp, *file_buf, file_len , &pos); +#else + ret = kernel_read(filp, *file_buf, file_len , &pos); +#endif + if (ret < 0) + FTS_ERROR("read file fail"); + FTS_INFO("file len:%d read len:%d pos:%d", (u32)file_len, ret, (u32)pos); + filp_close(filp, NULL); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + set_fs(old_fs); +#endif + + return ret; +} + +static int fts_read_file_request_firmware(char *file_name, u8 **file_buf) +{ + int ret = 0; + const struct firmware *fw = NULL; + char fwname[FILE_NAME_LENGTH] = { 0 }; + struct fts_upgrade *upg = fwupgrade; + +#if !FTS_FW_REQUEST_SUPPORT + return -EINVAL; +#endif + + snprintf(fwname, FILE_NAME_LENGTH, "%s", file_name); + ret = request_firmware(&fw, fwname, upg->ts_data->dev); + if (0 == ret) { + FTS_INFO("firmware(%s) request successfully", fwname); + *file_buf = vmalloc(fw->size); + if (NULL == *file_buf) { + FTS_ERROR("fw buffer vmalloc fail"); + ret = -ENOMEM; + } else { + memcpy(*file_buf, fw->data, fw->size); + ret = fw->size; + } + } else { + FTS_INFO("firmware(%s) request fail,ret=%d", fwname, ret); + ret = -EIO; + } + + if (fw != NULL) { + release_firmware(fw); + fw = NULL; + } + + return ret; +} + +static int fts_read_file(char *file_name, u8 **file_buf) +{ + int ret = 0; + + ret = fts_read_file_request_firmware(file_name, file_buf); + if (ret < 0) { +#ifdef CONFIG_FTS_COMPATIBLE_WITH_GKI + ret = fts_read_file_request_firmware(file_name, file_buf); +#else + ret = fts_read_file_default(file_name, file_buf); +#endif + if (ret < 0) { + FTS_ERROR("get fw file(default) fail"); + return ret; + } + } + + return ret; +} + +int fts_upgrade_bin(char *fw_name, bool force) +{ + int ret = 0; + u32 fw_file_len = 0; + u8 *fw_file_buf = NULL; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("start upgrade with fw bin"); + if ((!upg) || (!upg->func) || !upg->ts_data) { + FTS_ERROR("upgrade/func/ts_data is null"); + return -EINVAL; + } + + upg->ts_data->fw_loading = 1; + fts_irq_disable(); +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(upg->ts_data, DISABLE); +#endif + + ret = fts_read_file(fw_name, &fw_file_buf); + if ((ret < 0) || (ret < FTS_MIN_LEN)) { + FTS_ERROR("read fw bin file(%s) fail, len:%d", fw_name, ret); + goto err_bin; + } + + fw_file_len = ret; + FTS_INFO("fw bin file len:%d", fw_file_len); + if (force) { + if (upg->func->force_upgrade) { + ret = upg->func->force_upgrade(fw_file_buf, fw_file_len); + } else { + FTS_INFO("force_upgrade function is null, no upgrade"); + goto err_bin; + } + } else { +#if FTS_AUTO_LIC_UPGRADE_EN + if (upg->func->lic_upgrade) { + ret = upg->func->lic_upgrade(fw_file_buf, fw_file_len); + } else { + FTS_INFO("lic_upgrade function is null, no upgrade"); + } +#endif + if (upg->func->upgrade) { + ret = upg->func->upgrade(fw_file_buf, fw_file_len); + } else { + FTS_INFO("upgrade function is null, no upgrade"); + } + } + + if (ret < 0) { + FTS_ERROR("upgrade fw bin failed"); + fts_fwupg_reset_in_boot(); + goto err_bin; + } + + FTS_INFO("upgrade fw bin success"); + ret = 0; + +err_bin: +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(upg->ts_data, ENABLE); +#endif + fts_irq_enable(); + upg->ts_data->fw_loading = 0; + + if (fw_file_buf) { + vfree(fw_file_buf); + fw_file_buf = NULL; + } + 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->ts_data || !upg->lic || !vid) { + FTS_ERROR("upgrade/func/ts_data/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 (upg->ts_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("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("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("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; + u8 cmd = FTS_CMD_READ_FWCFG; + u32 fwcfg_addr = 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_1) { + 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) { + FTS_ERROR("no module id match, don't get file"); + return -ENODATA; + } + } + + upg->module_info = info; + return 0; +} + +int fts_fw_update_vendor_name(const char* name) { + struct fts_upgrade *upg = fwupgrade; + char* pos; + int len; + + fts_fwupg_get_module_info(fwupgrade); + + if (!upg || !upg->module_info) { + FTS_ERROR("upgrade struct not init"); + return -EINVAL; + } + + len = strlen(name); + if (len > FILE_NAME_LENGTH) { + FTS_ERROR("vendor name is too long"); + return -EINVAL; + } + pos = strstr(name, ".bin"); + if (pos == NULL) + snprintf(upg->module_info->vendor_name, FILE_NAME_LENGTH, "%s", name); + else { + len = len - strlen(".bin"); + snprintf(upg->module_info->vendor_name, len + 1, "%s", name); + } + 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; + } + + 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 (0 == ret) { + 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) { + 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); + } +} + +void fts_fwupg_bin(void) +{ + int ret = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("fw upgrade bin 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(upg->ts_data, 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(upg->ts_data, ENABLE); +#endif + fts_irq_enable(); + upg->ts_data->fw_loading = 0; +} + +/***************************************************************************** + * 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(upg->ts_data, 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(upg->ts_data, 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; + u16 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 < FTS_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(); + cancel_work_sync(&ts_data->fwupg_work); + + if (fwupgrade) { + if (fwupgrade->fw_from_request) { + vfree(fwupgrade->fw); + fwupgrade->fw = NULL; + } + + kfree(fwupgrade); + fwupgrade = NULL; + } + FTS_FUNC_EXIT(); + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_flash.h b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_flash.h new file mode 100644 index 000000000000..4b165cc8d6d7 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_flash.h @@ -0,0 +1,222 @@ +/************************************************************************ +* Copyright (c) 2012-2020, 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_FWCFG 0xA8 + +#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_DATA_LEN 0xB0 +#define FTS_CMD_APP_DATA_LEN_INCELL 0x7A +#define FTS_CMD_DATA_LEN_LEN 4 +#define FTS_CMD_SET_WFLASH_ADDR 0xAB +#define FTS_CMD_SET_RFLASH_ADDR 0xAC +#define FTS_LEN_SET_ADDR 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 (256 * 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, + UPGRADE_SPEC_V_1_1 = 0x0101, + UPGRADE_SPEC_V_1_2 = 0x0102, +}; + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +/* IC info */ +struct upgrade_func { + u16 ctype[FTS_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; + u8 spi_pe; + u8 length_coefficient; + u8 fd_check; + u8 drwr_support; + u8 ecc_delay; +}; + +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_ft5662; + + +/***************************************************************************** +* 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_flash_read_buf(u32 saddr, u8 *buf, u32 len); +int fts_fwupg_upgrade(struct fts_upgrade *upg); +#endif diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_flash/Makefile b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_flash/Makefile new file mode 100644 index 000000000000..0e11aeeb3b92 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_flash/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_TOUCHSCREEN_FTS_V3) += focaltech_upgrade_ft3681.o \ No newline at end of file diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_flash/focaltech_upgrade_ft3681.c b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_flash/focaltech_upgrade_ft3681.c new file mode 100644 index 000000000000..f7b2a1e0d912 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_flash/focaltech_upgrade_ft3681.c @@ -0,0 +1,445 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2020, 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_ft5662.c +* +* Author: Focaltech Driver Team +* +* Created: 2021-08-05 +* +* Abstract: +* +* Reference: +* +*****************************************************************************/ +/***************************************************************************** +* 1.Included header files +*****************************************************************************/ +#include "../focaltech_flash.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +u8 pb_file_ft5662[] = { +#include "../include/pramboot/FT5662_Pramboot_V1.3_20211109.i" +}; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +static int ft5662_fwupg_get_boot_state(enum FW_STATUS *fw_sts) +{ + int ret = 0; + u8 cmd = 0; + u8 val[2] = { 0 }; + + FTS_INFO("**********read boot id**********"); + if (!fw_sts) { + FTS_ERROR("fw_sts is null"); + return -EINVAL; + } + + cmd = FTS_CMD_START1; + ret = fts_write(&cmd, 1); + if (ret < 0) { + FTS_ERROR("write 55 cmd fail"); + return ret; + } + + msleep(FTS_CMD_START_DELAY); + cmd = FTS_CMD_READ_ID; + ret = fts_read(&cmd, 1, 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]); + if ((val[0] == 0x56) && (val[1] == 0x62)) { + FTS_INFO("tp run in romboot"); + *fw_sts = FTS_RUN_IN_ROM; + } else if ((val[0] == 0x56) && (val[1] == 0xE2)) { + FTS_INFO("tp run in pramboot"); + *fw_sts = FTS_RUN_IN_PRAM; + } + + return 0; +} + +static bool ft5662_fwupg_check_state(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 = ft5662_fwupg_get_boot_state(&cstate); + /* FTS_DEBUG("fw state=%d, retries=%d", cstate, i); */ + if (cstate == rstate) + return true; + msleep(FTS_DELAY_READ_ID); + } + + return false; +} + +static int ft5662_fwupg_reset_to_romboot(void) +{ + 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 = ft5662_fwupg_get_boot_state(&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 void ft5662_crc16_calc_host(u8 *pbuf, u32 length, u16 *ecc) +{ + u32 i = 0; + u32 j = 0; + u16 tmp_ecc = 0; + + for ( i = 0; i < length; i += 2 ) { + tmp_ecc ^= ((pbuf[i] << 8) | (pbuf[i + 1])); + for (j = 0; j < 16; j ++) { + if (tmp_ecc & 0x01) + tmp_ecc = (u16)((tmp_ecc >> 1) ^ AL2_FCS_COEF); + else + tmp_ecc >>= 1; + } + } + + *ecc = tmp_ecc; +} + +static int ft5662_pram_ecc_cal(u32 start_addr, u32 ecc_length, u16 *ecc) +{ + int ret = 0; + u8 val[2] = { 0 }; + u8 cmd[FTS_ROMBOOT_CMD_ECC_NEW_LEN] = { 0 }; + + FTS_INFO("read out pramboot checksum"); + 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; + } + + msleep(10); + 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]); + return 0; +} + +static int ft5662_pram_write_buf(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 }; + u32 cmdlen = 0; + + FTS_INFO("write pramboot to pram,pramboot len=%d", len); + if (!buf || (len < PRAMBOOT_MIN_SIZE) || (len > PRAMBOOT_MAX_SIZE)) { + FTS_ERROR("buf/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 (fts_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]; + } + + ret = fts_write(packet_buf, packet_len + cmdlen); + if (ret < 0) { + FTS_ERROR("pramboot write data(%d) fail", i); + return ret; + } + } + + return 0; +} + +static int ft5662_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_ft5662_write_pramboot_private(void) +{ + int ret = 0; + bool state = 0; + enum FW_STATUS status = FTS_RUN_IN_ERROR; + u16 ecc_in_host = 0; + u16 ecc_in_tp = 0; + u8 *pb_buf = pb_file_ft5662; + u32 pb_len = sizeof(pb_file_ft5662); + + FTS_INFO("**********pram write and init**********"); + if (pb_len < FTS_MIN_LEN) { + FTS_ERROR("pramboot length(%d) fail", pb_len); + return -EINVAL; + } + + FTS_DEBUG("check whether tp is in romboot or not "); + /* need reset to romboot when non-romboot state */ + ret = ft5662_fwupg_get_boot_state(&status); + if (status != FTS_RUN_IN_ROM) { + FTS_INFO("tp isn't in romboot, need send reset to romboot"); + ret = ft5662_fwupg_reset_to_romboot(); + if (ret < 0) { + FTS_ERROR("reset to romboot fail"); + return ret; + } + } + + /* write pramboot to pram */ + ret = ft5662_pram_write_buf(pb_buf, pb_len); + if (ret < 0) { + FTS_ERROR( "write pramboot buffer fail"); + return ret; + } + + /* check CRC */ + ft5662_crc16_calc_host(pb_buf, pb_len, &ecc_in_host); + ret = ft5662_pram_ecc_cal(0, pb_len, &ecc_in_tp); + if (ret < 0) { + FTS_ERROR( "read pramboot ecc fail"); + return ret; + } + + 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 = ft5662_pram_start(); + if (ret < 0) { + FTS_ERROR("pram start fail"); + return ret; + } + + FTS_DEBUG("after write pramboot, confirm run in pramboot"); + state = ft5662_fwupg_check_state(FTS_RUN_IN_PRAM); + if (!state) { + FTS_ERROR("not in pramboot"); + return -EIO; + } + + return 0; +} + + +/************************************************************************ +* Name: fts_ft5662_upgrade +* Brief: +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_ft5662_upgrade(u8 *buf, u32 len) +{ + int ret = 0; + u32 start_addr = 0; + u32 delay = 0; + u8 cmd[4] = { 0 }; + int ecc_in_host = 0; + int ecc_in_tp = 0; + + + if ((NULL == buf) || (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_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_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; + } + + delay = FTS_ERASE_SECTOR_DELAY * (len / FTS_MAX_LEN_SECTOR); + ret = fts_fwupg_erase(delay); + if (ret < 0) { + FTS_ERROR("erase cmd write fail"); + goto fw_reset; + } + + /* write app */ + 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; + } + + FTS_INFO( "**********read out checksum**********"); + /* 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_ft5662 = { + .ctype = {0x8A}, + .fwveroff = 0x010E, + .fwcfgoff = 0x0F80, + .appoff = 0x0000, + .upgspec_version = UPGRADE_SPEC_V_1_1, + .pramboot_supported = true, + .pramboot = pb_file_ft5662, + .pb_length = sizeof(pb_file_ft5662), + .write_pramboot_private = fts_ft5662_write_pramboot_private, + .hid_supported = false, + .upgrade = fts_ft5662_upgrade, +}; diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_gesture.c b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_gesture.c new file mode 100644 index 000000000000..89f5e57c08e7 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_gesture.c @@ -0,0 +1,721 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 KEY_GESTURE_F1 KEY_F1 + +#define GESTURE_LEFT 0x20 +#define GESTURE_RIGHT 0x21 +#define GESTURE_UP 0x22 +#define GESTURE_DOWN 0x23 +#define GESTURE_DOUBLECLICK 0x24 +#define GESTURE_SINGLECLICK 0x27 +#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 + +#ifdef FOCALTECH_SENSOR_EN +#define REPORT_MAX_COUNT 10000 +#endif + +/***************************************************************************** +* 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; +#ifdef FOCALTECH_SENSOR_EN +#ifdef CONFIG_HAS_WAKELOCK +static struct wake_lock gesture_wakelock; +#else +static struct wakeup_source *gesture_wakelock; +#endif +#endif + +/***************************************************************************** +* 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 = dev_get_drvdata(dev); + + 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_support ? "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 = dev_get_drvdata(dev); + + mutex_lock(&ts_data->input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_DEBUG("enable gesture"); + ts_data->gesture_support = ENABLE; + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_DEBUG("disable gesture"); + ts_data->gesture_support = 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 fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_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; +} + +static ssize_t fts_gesture_bm_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + mutex_lock(&ts_data->input_dev->mutex); + count = snprintf(buf, PAGE_SIZE, "gesture bmode:%d\n", + ts_data->gesture_bmode); + mutex_unlock(&ts_data->input_dev->mutex); + + return count; +} + +static ssize_t fts_gesture_bm_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + int value = 0xFF; + int ret = 0; + + mutex_lock(&ts_data->input_dev->mutex); + ret = sscanf(buf, "%d", &value); + if (ret == 1) { + FTS_DEBUG("gesture bmode:%d->%d", ts_data->gesture_bmode, value); + ts_data->gesture_bmode = value; + } + mutex_unlock(&ts_data->input_dev->mutex); + + return count; +} + +/* 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 DEVICE_ATTR(fts_gesture_bm, S_IRUGO | S_IWUSR, + fts_gesture_bm_show, fts_gesture_bm_store); + +static struct attribute *fts_gesture_mode_attrs[] = { + &dev_attr_fts_gesture_mode.attr, + &dev_attr_fts_gesture_buf.attr, + &dev_attr_fts_gesture_bm.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; +} + +#ifdef FOCALTECH_SENSOR_EN +void fts_read_report_fod_event(struct fts_ts_data *ts_data) +{ + u8 cmd = 0xE1; + u8 fod_data[9] = {0}; + u16 fod_x = 0; + u16 fod_y = 0; + u8 fod_id = 0; + u8 fod_down = 0; + int ret = 0; + static bool last_fod_down = false; + struct gesture_event_data event; + int fod_down_interval = 0; + static unsigned long start = 0; + + ret = fts_read(&cmd, 1, fod_data, 9); + if (ret < 0) + { + FTS_ERROR("read fod data fail"); + return; + } + fod_id = fod_data[1]; + fod_x = (fod_data[4] << 8) + fod_data[5]; + fod_y = (fod_data[6] << 8) + fod_data[7]; + fod_down = fod_data[8]; //1 up, 0 down + + //FTS_INFO("fod_id = %02x, fod_down = %d", fod_id, fod_down); + if (fod_id != 0x26) + { + return; + } + + if (ts_data->fod_suspended) + { + if (fod_down == 0) + { + fod_down_interval = (int)jiffies_to_msecs(jiffies - start); + event.evcode = 2; + event.evdata.x = 0; + event.evdata.y = 0; + FTS_INFO("Get FOD-DOWN gesture:%d interval:%d", ts_data->zerotap_data[0], fod_down_interval); + if (fod_down_interval > 2000) + ts_data->zerotap_data[0] = 0; + + if (fod_down_interval > 0 && fod_down_interval < 250 && ts_data->zerotap_data[0]) + { + return; + } + start = jiffies; + ts_data->fod_jiffies = jiffies; + if (ts_data->zerotap_data[0] > 6) + { + FTS_INFO("FOD-DOWN too many times %d", ts_data->zerotap_data[0]); + } + else + { + ret = fts_data->imports->report_gesture(&event); + FTS_INFO("report fod down event %d", ts_data->zerotap_data[0]); + if (!ret) + { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&gesture_wakelock, msecs_to_jiffies(3000)); +#else + PM_WAKEUP_EVENT(gesture_wakelock, 3000); +#endif + } + ts_data->zerotap_data[0]++; + } + } + else if (fod_down == 1) + { + FTS_INFO("Get FOD-UP gesture"); + event.evcode = 3; + event.evdata.x = 0; + event.evdata.y = 0; + ret = fts_data->imports->report_gesture(&event); + if (!ret) + { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&gesture_wakelock, msecs_to_jiffies(3000)); +#else + PM_WAKEUP_EVENT(gesture_wakelock, 3000); +#endif + } + ts_data->zerotap_data[0] = 0; + } + } + else + { + if (fod_down == 1) + { + //FTS_INFO("Get FOD-UP normal"); + FTS_INFO("report BTN_TRIGGER_HAPPY2"); + ts_data->fod_event = BTN_TRIGGER_HAPPY2; + last_fod_down = false; + } + else if (fod_down == 0) + { + //FTS_INFO("Get FOD-DOWN normal"); + if (last_fod_down == false) + { + ts_data->fod_event = BTN_TRIGGER_HAPPY1; + last_fod_down = true; + FTS_INFO("report BTN_TRIGGER_HAPPY1"); + } + } + } +} +#endif + +static void fts_gesture_report(struct input_dev *input_dev, int gesture_id) +{ + int gesture; +#ifdef FOCALTECH_SENSOR_EN + int ret = 0; + static int report_cnt = 0; +#endif + + 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_GESTURE_U; + break; + case GESTURE_SINGLECLICK: + gesture = KEY_GESTURE_F1; + 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); +#ifdef FOCALTECH_SENSOR_EN + if (!fts_data->wakeable) { + FTS_INFO("Gesture got but wakeable not set. Skip this gesture."); + return; + } +#ifdef CONFIG_FTS_DOUBLE_TAP_CONTROL + /* report double tap */ + if (gesture == KEY_GESTURE_U) { + if (fts_data->imports && fts_data->imports->report_gesture) { + struct gesture_event_data event; + + FTS_INFO("invoke imported report double tap gesture function\n"); + event.evcode = 4; + /* call class method */ + ret = fts_data->imports->report_gesture(&event); + ++report_cnt; + } + /* report single tap */ + } else if (gesture == KEY_GESTURE_F1) { + if (fts_data->imports && fts_data->imports->report_gesture) { + struct gesture_event_data event; + + FTS_INFO("invoke imported report single tap gesture function\n"); + event.evcode = 1; + /* call class method */ + ret = fts_data->imports->report_gesture(&event); + ++report_cnt; + } + } +#else + /* report single tap */ + if (gesture == KEY_GESTURE_U) { + if (fts_data->imports && fts_data->imports->report_gesture) { + struct gesture_event_data event; + + FTS_INFO("invoke imported report gesture function\n"); + event.evcode = 1; + /* call class method */ + ret = fts_data->imports->report_gesture(&event); + ++report_cnt; + } + } +#endif + + FTS_INFO("input report: %d", report_cnt); + if (report_cnt >= REPORT_MAX_COUNT) + report_cnt = 0; + + if (!ret) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&gesture_wakelock, msecs_to_jiffies(5000)); +#else + PM_WAKEUP_EVENT(gesture_wakelock, 5000); +#endif + } +#else + input_report_key(input_dev, gesture, 1); + input_sync(input_dev); + input_report_key(input_dev, gesture, 0); + input_sync(input_dev); +#endif + } +} + +/***************************************************************************** +* 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 +* 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 *touch_buf) +{ + 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->gesture_support) { + FTS_ERROR("gesture no support"); + return -EINVAL; + } + + if (ts_data->gesture_bmode == GESTURE_BM_TOUCH) { + memcpy(buf, touch_buf + FTS_TOUCH_DATA_LEN, FTS_GESTURE_DATA_LEN); + } else { + 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_support && 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++) { +#ifdef CONFIG_FTS_DOUBLE_TAP_CONTROL + fts_write_reg(0xD1, ts_data->gsx_cmd); +#else + fts_write_reg(0xD1, 0xFF); +#endif + +#ifdef FOCALTECH_SENSOR_EN + if(fts_data->zero_enable) { + fts_write_reg(0xCF, 0x02); + } +#endif + 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; +#ifdef FOCALTECH_SENSOR_EN + static bool initialized_sensor; +#endif + + 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); + +#ifdef FOCALTECH_SENSOR_EN + if (!initialized_sensor) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&gesture_wakelock, WAKE_LOCK_SUSPEND, "poll-wake-lock"); +#else + PM_WAKEUP_REGISTER(ts_data->dev, gesture_wakelock, "poll-wake-lock"); + if (!gesture_wakelock) { + FTS_ERROR("failed to allocate wakeup source\n"); + return -ENOMEM; + } +#endif + + initialized_sensor = true; + + } +#endif + + fts_create_gesture_sysfs(ts_data->dev); + + memset(&fts_gesture_data, 0, sizeof(struct fts_gesture_st)); + ts_data->gesture_bmode = GESTURE_BM_REG; + ts_data->gesture_support = FTS_GESTURE_EN; + + if ((ts_data->ic_info.ids.type <= 0x25) + || (ts_data->ic_info.ids.type == 0x87) + || (ts_data->ic_info.ids.type == 0x88)) { + FTS_INFO("ic type:0x%02x,GESTURE_BM_TOUCH", ts_data->ic_info.ids.type); + ts_data->touch_size += FTS_GESTURE_DATA_LEN; + ts_data->gesture_bmode = GESTURE_BM_TOUCH; + } + + 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; +} diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_point_report_check.c b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_point_report_check.c new file mode 100644 index 000000000000..8b117a8bb431 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_point_report_check.c @@ -0,0 +1,185 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define POINT_REPORT_CHECK_WAIT_TIME 200 /* unit:ms */ +#define PRC_INTR_INTERVALS 100 /* unit:ms */ + +/***************************************************************************** +* Static variables +*****************************************************************************/ + +/***************************************************************************** +* 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); + unsigned long cur_jiffies = jiffies; + unsigned long intr_timeout = msecs_to_jiffies(PRC_INTR_INTERVALS); + + if (ts_data->prc_support && !ts_data->suspended) { + intr_timeout += ts_data->intr_jiffies; + if (time_after(cur_jiffies, intr_timeout)) { + if (ts_data->touch_points) { + fts_release_all_finger(); + if (ts_data->log_level >= 3) + FTS_DEBUG("prc trigger interval:%dms", + jiffies_to_msecs(cur_jiffies - ts_data->intr_jiffies)); + } + ts_data->prc_mode = 0; + } else { + queue_delayed_work(ts_data->ts_workqueue, &ts_data->prc_work, + msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME)); + ts_data->prc_mode = 1; + } + } else { + ts_data->prc_mode = 0; + } +} + +/***************************************************************************** +* 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) +{ + if (ts_data->prc_support && !ts_data->prc_mode && !ts_data->suspended) { + queue_delayed_work(ts_data->ts_workqueue, &ts_data->prc_work, + msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME)); + ts_data->prc_mode = 1; + } +} + + +static ssize_t fts_prc_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + if (FTS_SYSFS_ECHO_ON(buf)) { + FTS_DEBUG("enable prc"); + ts_data->prc_support = ENABLE; + } else if (FTS_SYSFS_ECHO_OFF(buf)) { + FTS_DEBUG("disable prc"); + cancel_delayed_work_sync(&ts_data->prc_work); + ts_data->prc_support = DISABLE; + } + mutex_unlock(&input_dev->mutex); + + return count; +} + +static ssize_t fts_prc_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int count; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + count = snprintf(buf, PAGE_SIZE, "PRC: %s\n", \ + ts_data->prc_support ? "Enable" : "Disable"); + mutex_unlock(&input_dev->mutex); + + return count; +} + +static DEVICE_ATTR(fts_prc, S_IRUGO | S_IWUSR, fts_prc_show, fts_prc_store); + +/***************************************************************************** +* 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) +{ + int ret = 0; + + 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; + } + + ret = sysfs_create_file(&ts_data->dev->kobj, &dev_attr_fts_prc.attr); + if ( ret < 0) { + FTS_ERROR("create prc sysfs fail"); + } + + ts_data->prc_support = FTS_POINT_REPORT_CHECK_EN; + 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(); + cancel_delayed_work_sync(&ts_data->prc_work); + sysfs_remove_file(&ts_data->dev->kobj, &dev_attr_fts_prc.attr); + FTS_FUNC_EXIT(); + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_spi.c b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_spi.c new file mode 100644 index 000000000000..004eeae287ee --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_spi.c @@ -0,0 +1,452 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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_spi.c +* +* Author: FocalTech Driver Team +* +* Created: 2019-03-21 +* +* Abstract: new spi protocol communication with TP +* +* Version: v1.0 +* +* Revision History: +* +************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_core.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define SPI_RETRY_NUMBER 3 +#define CS_HIGH_DELAY 150 /* unit: us */ +#define SPI_BUF_LENGTH 4096 + +#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*/ +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ + +/***************************************************************************** +* Static variables +*****************************************************************************/ + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* 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 fts_spi_buf_show(u8 *data, int datalen) +{ + int i = 0; + int count = 0; + int size = 0; + char *tmpbuf = NULL; + + if (!data || (datalen <= 0)) { + FTS_ERROR("data/datalen is invalid"); + return; + } + + size = (datalen > 256) ? 256 : datalen; + tmpbuf = kzalloc(1024, GFP_KERNEL); + if (!tmpbuf) { + FTS_ERROR("tmpbuf zalloc fail"); + return; + } + + for (i = 0; i < size; i++) + count += snprintf(tmpbuf + count, 1024 - count, "%02X ", data[i]); + + FTS_DEBUG("%s", tmpbuf); + if (tmpbuf) { + kfree(tmpbuf); + tmpbuf = NULL; + } +} + +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) { + fts_spi_buf_show(rdata, rlen); + return -EIO; + } + + return 0; +} + +int fts_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 (NULL == txbuf) { + FTS_ERROR("txbuf malloc fail"); + ret = -ENOMEM; + goto err_write; + } + + rxbuf = kzalloc(txlen_need, GFP_KERNEL); + if (NULL == rxbuf) { + 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 ((0 == ret) && ((rxbuf[3] & 0xA0) == 0)) { + break; + } else { + 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) { + if (txbuf) { + kfree(txbuf); + txbuf = NULL; + } + + if (rxbuf) { + kfree(rxbuf); + rxbuf = NULL; + } + } + + udelay(CS_HIGH_DELAY); + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +int fts_write_reg(u8 addr, u8 value) +{ + u8 writebuf[2] = { 0 }; + + writebuf[0] = addr; + writebuf[1] = value; + return fts_write(writebuf, 2); +} + +int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen) +{ + 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 = 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 (NULL == txbuf) { + FTS_ERROR("txbuf malloc fail"); + ret = -ENOMEM; + goto err_read; + } + + rxbuf = kzalloc(txlen_need, GFP_KERNEL); + if (NULL == rxbuf) { + 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 ((0 == ret) && ((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; + } else { + 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) { + if (txbuf) { + kfree(txbuf); + txbuf = NULL; + } + + if (rxbuf) { + kfree(rxbuf); + rxbuf = NULL; + } + } + + udelay(CS_HIGH_DELAY); + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +int fts_read_reg(u8 addr, u8 *value) +{ + return fts_read(&addr, 1, value, 1); +} + + +int fts_spi_transfer_direct(u8 *writebuf, u32 writelen, u8 *readbuf, u32 readlen) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + u8 *txbuf = NULL; + u8 *rxbuf = NULL; + bool read_cmd = (readbuf && readlen) ? 1 : 0; + u32 txlen = (read_cmd) ? readlen : writelen; + + if (!writebuf || !writelen) { + FTS_ERROR("writebuf/len is invalid"); + return -EINVAL; + } + + mutex_lock(&ts_data->bus_lock); + if (txlen > SPI_BUF_LENGTH) { + txbuf = kzalloc(txlen, GFP_KERNEL); + if (NULL == txbuf) { + FTS_ERROR("txbuf malloc fail"); + ret = -ENOMEM; + goto err_spi_dir; + } + + rxbuf = kzalloc(txlen, GFP_KERNEL); + if (NULL == rxbuf) { + FTS_ERROR("rxbuf malloc fail"); + ret = -ENOMEM; + goto err_spi_dir; + } + } 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); + } + + memcpy(txbuf, writebuf, writelen); + ret = fts_spi_transfer(txbuf, rxbuf, txlen); + if (ret < 0) { + FTS_ERROR("data read(addr:%x) fail,status:%x,ret:%d", txbuf[0], rxbuf[3], ret); + goto err_spi_dir; + } + + if (read_cmd) { + memcpy(readbuf, rxbuf, txlen); + } + + ret = 0; +err_spi_dir: + if (txlen > SPI_BUF_LENGTH) { + if (txbuf) { + kfree(txbuf); + txbuf = NULL; + } + + if (rxbuf) { + kfree(rxbuf); + rxbuf = NULL; + } + } + + udelay(CS_HIGH_DELAY); + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +int fts_bus_init(struct fts_ts_data *ts_data) +{ + FTS_FUNC_ENTER(); + ts_data->bus_tx_buf = kzalloc(SPI_BUF_LENGTH, GFP_KERNEL); + if (NULL == ts_data->bus_tx_buf) { + FTS_ERROR("failed to allocate memory for bus_tx_buf"); + return -ENOMEM; + } + + ts_data->bus_rx_buf = kzalloc(SPI_BUF_LENGTH, GFP_KERNEL); + if (NULL == ts_data->bus_rx_buf) { + FTS_ERROR("failed to allocate memory for bus_rx_buf"); + return -ENOMEM; + } + + ts_data->dummy_byte = SPI_DUMMY_BYTE; + FTS_FUNC_EXIT(); + return 0; +} + +int fts_bus_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; +} + diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/Makefile b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/Makefile new file mode 100644 index 000000000000..d08b7f8ec8c0 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_TOUCHSCREEN_FTS_V3) += focaltech_test.o +obj-$(CONFIG_TOUCHSCREEN_FTS_V3) += focaltech_test_ini.o +obj-$(CONFIG_TOUCHSCREEN_FTS_V3) += supported_ic/ diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/focaltech_test.c b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/focaltech_test.c new file mode 100644 index 000000000000..ce96c23fbe83 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/focaltech_test.c @@ -0,0 +1,2471 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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_test.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-01 +* +* Modify: +* +* Abstract: create char device and proc node for the comm between APK and TP +* +************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_test.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +struct fts_test *fts_ftest; + +struct test_funcs *test_func_list[] = { + &test_func_ft5662, +}; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* functions body +*****************************************************************************/ +void sys_delay(int ms) +{ + msleep(ms); +} + +int fts_abs(int value) +{ + if (value < 0) + value = 0 - value; + + return value; +} + +void *fts_malloc(size_t size) +{ + return kzalloc(size, GFP_KERNEL); +} + +void fts_free_proc(void *p) +{ + return kfree(p); +} + +void print_buffer(int *buffer, int length, int line_num) +{ + int i = 0; + int j = 0; + int tmpline = 0; + char *tmpbuf = NULL; + int tmplen = 0; + int cnt = 0; + struct fts_test *tdata = fts_ftest; + + if (tdata && tdata->ts_data && (tdata->ts_data->log_level < 3)) { + return; + } + + if ((NULL == buffer) || (length <= 0)) { + FTS_TEST_DBG("buffer/length(%d) fail", length); + return; + } + + tmpline = line_num ? line_num : length; + tmplen = tmpline * 6 + 128; + tmpbuf = kzalloc(tmplen, GFP_KERNEL); + + for (i = 0; i < length; i = i + tmpline) { + cnt = 0; + for (j = 0; j < tmpline; j++) { + cnt += snprintf(tmpbuf + cnt, tmplen - cnt, "%5d ", buffer[i + j]); + if ((cnt >= tmplen) || ((i + j + 1) >= length)) + break; + } + FTS_TEST_DBG("%s", tmpbuf); + } + + if (tmpbuf) { + kfree(tmpbuf); + tmpbuf = NULL; + } +} + +/******************************************************************** + * test read/write interface + *******************************************************************/ +static int fts_test_bus_read( + u8 *cmd, int cmdlen, u8 *data, int datalen) +{ + int ret = 0; + + ret = fts_read(cmd, cmdlen, data, datalen); + if (ret < 0) + return ret; + else + return 0; +} + +static int fts_test_bus_write(u8 *writebuf, int writelen) +{ + int ret = 0; + + ret = fts_write(writebuf, writelen); + if (ret < 0) + return ret; + else + return 0; +} + +int fts_test_read_reg(u8 addr, u8 *val) +{ + return fts_test_bus_read(&addr, 1, val, 1); +} + +int fts_test_write_reg(u8 addr, u8 val) +{ + int ret; + u8 cmd[2] = {0}; + + cmd[0] = addr; + cmd[1] = val; + ret = fts_test_bus_write(cmd, 2); + + return ret; +} + +int fts_test_read(u8 addr, u8 *readbuf, int readlen) +{ + int ret = 0; + int i = 0; + int packet_length = 0; + int packet_num = 0; + int packet_remainder = 0; + int offset = 0; + int byte_num = readlen; + + packet_num = byte_num / BYTES_PER_TIME; + packet_remainder = byte_num % BYTES_PER_TIME; + if (packet_remainder) + packet_num++; + + if (byte_num < BYTES_PER_TIME) { + packet_length = byte_num; + } else { + packet_length = BYTES_PER_TIME; + } + /* FTS_TEST_DBG("packet num:%d, remainder:%d", packet_num, packet_remainder); */ + + ret = fts_test_bus_read(&addr, 1, &readbuf[offset], packet_length); + if (ret < 0) { + FTS_TEST_ERROR("read buffer fail"); + return ret; + } + for (i = 1; i < packet_num; i++) { + offset += packet_length; + if ((i == (packet_num - 1)) && packet_remainder) { + packet_length = packet_remainder; + } + + ret = fts_test_bus_read(&addr, 1, &readbuf[offset], + packet_length); + + if (ret < 0) { + FTS_TEST_ERROR("read buffer fail"); + return ret; + } + } + + return 0; +} + +int fts_test_read_one(u8 addr, u8 *readbuf, int readlen) +{ + return fts_test_bus_read(&addr, 1, readbuf, readlen); +} + +int fts_test_write(u8 addr, u8 *writebuf, int writelen) +{ + int ret = 0; + int i = 0; + u8 *data = NULL; + int packet_length = 0; + int packet_num = 0; + int packet_remainder = 0; + int offset = 0; + int byte_num = writelen; + + data = fts_malloc(BYTES_PER_TIME + 1); + if (!data) { + FTS_TEST_ERROR("malloc memory for bus write data fail"); + return -ENOMEM; + } + + packet_num = byte_num / BYTES_PER_TIME; + packet_remainder = byte_num % BYTES_PER_TIME; + if (packet_remainder) + packet_num++; + + if (byte_num < BYTES_PER_TIME) { + packet_length = byte_num; + } else { + packet_length = BYTES_PER_TIME; + } + /* FTS_TEST_DBG("packet num:%d, remainder:%d", packet_num, packet_remainder); */ + + data[0] = addr; + for (i = 0; i < packet_num; i++) { + if (i != 0) { + data[0] = addr + 1; + } + if ((i == (packet_num - 1)) && packet_remainder) { + packet_length = packet_remainder; + } + memcpy(&data[1], &writebuf[offset], packet_length); + + ret = fts_test_bus_write(data, packet_length + 1); + if (ret < 0) { + FTS_TEST_ERROR("write buffer fail"); + fts_free(data); + return ret; + } + + offset += packet_length; + } + + fts_free(data); + return 0; +} + +/******************************************************************** + * test global function enter work/factory mode + *******************************************************************/ +int enter_work_mode(void) +{ + int ret = 0; + u8 mode = 0; + int i = 0; + int j = 0; + + FTS_TEST_FUNC_ENTER(); + + ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode); + if ((ret >= 0) && (0x00 == mode)) + return 0; + + for (i = 0; i < ENTER_WORK_FACTORY_RETRIES; i++) { + ret = fts_test_write_reg(DEVIDE_MODE_ADDR, 0x00); + if (ret >= 0) { + sys_delay(FACTORY_TEST_DELAY); + for (j = 0; j < 20; j++) { + ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode); + if ((ret >= 0) && (0x00 == mode)) { + FTS_TEST_INFO("enter work mode success"); + return 0; + } else + sys_delay(FACTORY_TEST_DELAY); + } + } + + sys_delay(50); + } + + if (i >= ENTER_WORK_FACTORY_RETRIES) { + FTS_TEST_ERROR("Enter work mode fail"); + return -EIO; + } + + FTS_TEST_FUNC_EXIT(); + return 0; +} + +int enter_factory_mode(void) +{ + int ret = 0; + u8 mode = 0; + int i = 0; + int j = 0; + + ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode); + if ((ret >= 0) && (0x40 == mode)) + return 0; + + for (i = 0; i < ENTER_WORK_FACTORY_RETRIES; i++) { + ret = fts_test_write_reg(DEVIDE_MODE_ADDR, 0x40); + if (ret >= 0) { + sys_delay(FACTORY_TEST_DELAY); + for (j = 0; j < 20; j++) { + ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode); + if ((ret >= 0) && (0x40 == mode)) { + FTS_TEST_INFO("enter factory mode success"); + sys_delay(200); + return 0; + } else + sys_delay(FACTORY_TEST_DELAY); + } + } + + sys_delay(50); + } + + if (i >= ENTER_WORK_FACTORY_RETRIES) { + FTS_TEST_ERROR("Enter factory mode fail"); + return -EIO; + } + + return 0; +} + +/* + * read_mass_data - read rawdata/short test data + * addr - register addr which read data from + * byte_num - read data length, unit:byte + * buf - save data + * + * return 0 if read data succuss, otherwise return error code + */ +int read_mass_data(u8 addr, int byte_num, int *buf) +{ + int ret = 0; + int i = 0; + u8 *data = NULL; + + data = (u8 *)fts_malloc(byte_num * sizeof(u8)); + if (NULL == data) { + FTS_TEST_SAVE_ERR("mass data buffer malloc fail\n"); + return -ENOMEM; + } + + /* read rawdata buffer */ + FTS_TEST_INFO("mass data len:%d", byte_num); + ret = fts_test_read(addr, data, byte_num); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read mass data fail\n"); + goto read_massdata_err; + } + + for (i = 0; i < byte_num; i = i + 2) { + buf[i >> 1] = (int)(short)((data[i] << 8) + data[i + 1]); + } + + ret = 0; +read_massdata_err: + fts_free(data); + return ret; +} + +int read_mass_data_u16(u8 addr, int byte_num, int *buf) +{ + int ret = 0; + int i = 0; + u8 *data = NULL; + + data = (u8 *)fts_malloc(byte_num * sizeof(u8)); + if (NULL == data) { + FTS_TEST_SAVE_ERR("mass data buffer malloc fail\n"); + return -ENOMEM; + } + + /* read rawdata buffer */ + FTS_TEST_INFO("mass data len:%d", byte_num); + ret = fts_test_read(addr, data, byte_num); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read mass data fail\n"); + goto read_massdata_err; + } + + for (i = 0; i < byte_num; i = i + 2) { + buf[i >> 1] = (int)(u16)((data[i] << 8) + data[i + 1]); + } + + ret = 0; +read_massdata_err: + fts_free(data); + return ret; +} + +int short_get_adcdata_incell(u8 retval, u8 ch_num, int byte_num, int *adc_buf) +{ + int ret = 0; + int times = 0; + u8 short_state = 0; + + FTS_TEST_FUNC_ENTER(); + + /* Start ADC sample */ + ret = fts_test_write_reg(FACTORY_REG_SHORT_TEST_EN, 0x01); + if (ret) { + FTS_TEST_SAVE_ERR("start short test fail\n"); + goto adc_err; + } + + sys_delay(ch_num * FACTORY_TEST_DELAY); + for (times = 0; times < FACTORY_TEST_RETRY; times++) { + ret = fts_test_read_reg(FACTORY_REG_SHORT_TEST_STATE, &short_state); + if ((ret >= 0) && (retval == short_state)) + break; + else + FTS_TEST_DBG("reg%x=%x,retry:%d", + FACTORY_REG_SHORT_TEST_STATE, short_state, times); + + sys_delay(FACTORY_TEST_RETRY_DELAY); + } + if (times >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("short test timeout, ADC data not OK\n"); + ret = -EIO; + goto adc_err; + } + + ret = read_mass_data(FACTORY_REG_SHORT_ADDR, byte_num, adc_buf); + if (ret) { + FTS_TEST_SAVE_ERR("get short(adc) data fail\n"); + } + +adc_err: + FTS_TEST_FUNC_EXIT(); + return ret; +} + +/* + * wait_state_update - wait fw status update + */ +int wait_state_update(u8 retval) +{ + int ret = 0; + int times = 0; + u8 addr = 0; + u8 state = 0xFF; + struct fts_test *tdata = fts_ftest; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("test/func is null\n"); + return -EINVAL; + } + + if (IC_HW_INCELL == tdata->func->hwtype) { + addr = FACTORY_REG_PARAM_UPDATE_STATE; + } else { + addr = FACTORY_REG_PARAM_UPDATE_STATE_TOUCH; + } + + while (times++ < FACTORY_TEST_RETRY) { + sys_delay(FACTORY_TEST_DELAY); + /* Wait register status update */ + state = 0xFF; + ret = fts_test_read_reg(addr, &state); + if ((ret >= 0) && (retval == state)) + break; + else + FTS_TEST_DBG("reg%x=%x,retry:%d", addr, state, times); + } + + if (times >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("Wait State Update fail,reg%x=%x\n", addr, state); + return -EIO; + } + + return 0; +} + +/* + * start_scan - start to scan a frame + */ +int start_scan(void) +{ + int ret = 0; + u8 addr = 0; + u8 val = 0; + u8 finish_val = 0; + int times = 0; + struct fts_test *tdata = fts_ftest; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("test/func is null\n"); + return -EINVAL; + } + + if (SCAN_SC == tdata->func->startscan_mode) { + /* sc ic */ + addr = FACTORY_REG_SCAN_ADDR2; + val = 0x01; + finish_val = 0x00; + } else { + addr = DEVIDE_MODE_ADDR; + val = 0xC0; + finish_val = 0x40; + } + + /* write register to start scan */ + ret = fts_test_write_reg(addr, val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write start scan mode fail\n"); + return ret; + } + + /* Wait for the scan to complete */ + while (times++ < FACTORY_TEST_RETRY) { + sys_delay(FACTORY_TEST_DELAY); + + ret = fts_test_read_reg(addr, &val); + if ((ret >= 0) && (val == finish_val)) { + break; + } else + FTS_TEST_DBG("reg%x=%x,retry:%d", addr, val, times); + } + + if (times >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("scan timeout\n"); + return -EIO; + } + + return 0; +} + +static int read_rawdata( + struct fts_test *tdata, + u8 off_addr, + u8 off_val, + u8 rawdata_addr, + int byte_num, + int *data) +{ + int ret = 0; + + /* set line addr or rawdata start addr */ + ret = fts_test_write_reg(off_addr, off_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wirte line/start addr fail\n"); + return ret; + } + + if (tdata->func->raw_u16) + ret = read_mass_data_u16(rawdata_addr, byte_num, data); + else + ret = read_mass_data(rawdata_addr, byte_num, data); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read rawdata fail\n"); + return ret; + } + + return 0; +} + +int get_rawdata(int *data) +{ + int ret = 0; + u8 val = 0; + u8 addr = 0; + u8 rawdata_addr = 0; + int byte_num = 0; + struct fts_test *tdata = fts_ftest; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("test/func is null\n"); + return -EINVAL; + } + + /* enter factory mode */ + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("failed to enter factory mode,ret=%d\n", ret); + return ret; + } + + /* start scanning */ + ret = start_scan(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("scan fail\n"); + return ret; + } + + /* read rawdata */ + if (IC_HW_INCELL == tdata->func->hwtype) { + val = 0xAD; + addr = FACTORY_REG_LINE_ADDR; + rawdata_addr = FACTORY_REG_RAWDATA_ADDR; + } else if (IC_HW_MC_SC == tdata->func->hwtype) { + val = 0xAA; + addr = FACTORY_REG_LINE_ADDR; + rawdata_addr = FACTORY_REG_RAWDATA_ADDR_MC_SC; + } else { + val = 0x0; + addr = FACTORY_REG_RAWDATA_SADDR_SC; + rawdata_addr = FACTORY_REG_RAWDATA_ADDR_SC; + } + + byte_num = tdata->node.node_num * 2; + ret = read_rawdata(tdata, addr, val, rawdata_addr, byte_num, data); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read rawdata fail\n"); + return ret; + } + + return 0; +} + +/* + * chip_clb - auto clb + */ +int chip_clb(void) +{ + int ret = 0; + u8 val = 0; + int times = 0; + + /* start clb */ + ret = fts_test_write_reg(FACTORY_REG_CLB, 0x04); + if (ret) { + FTS_TEST_SAVE_ERR("write start clb fail\n"); + return ret; + } + + while (times++ < FACTORY_TEST_RETRY) { + sys_delay(FACTORY_TEST_RETRY_DELAY); + ret = fts_test_read_reg(FACTORY_REG_CLB, &val); + if ((0 == ret) && (0x02 == val)) { + /* clb ok */ + break; + } else + FTS_TEST_DBG("reg%x=%x,retry:%d", FACTORY_REG_CLB, val, times); + } + + if (times >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("chip clb timeout\n"); + return -EIO; + } + + return 0; +} + +/* + * get_cb_incell - get cb data for incell IC + */ +int get_cb_incell(u16 saddr, int byte_num, int *cb_buf) +{ + int ret = 0; + int i = 0; + u8 cb_addr = 0; + u8 addr_h = 0; + u8 addr_l = 0; + int read_num = 0; + int packet_num = 0; + int packet_remainder = 0; + int offset = 0; + int addr = 0; + u8 *data = NULL; + + data = (u8 *)fts_malloc(byte_num * sizeof(u8)); + if (NULL == data) { + FTS_TEST_SAVE_ERR("cb buffer malloc fail\n"); + return -ENOMEM; + } + + packet_num = byte_num / BYTES_PER_TIME; + packet_remainder = byte_num % BYTES_PER_TIME; + if (packet_remainder) + packet_num++; + read_num = BYTES_PER_TIME; + + FTS_TEST_INFO("cb packet:%d,remainder:%d", packet_num, packet_remainder); + cb_addr = FACTORY_REG_CB_ADDR; + for (i = 0; i < packet_num; i++) { + offset = read_num * i; + addr = saddr + offset; + addr_h = (addr >> 8) & 0xFF; + addr_l = addr & 0xFF; + if ((i == (packet_num - 1)) && packet_remainder) { + read_num = packet_remainder; + } + + ret = fts_test_write_reg(FACTORY_REG_CB_ADDR_H, addr_h); + if (ret) { + FTS_TEST_SAVE_ERR("write cb addr high fail\n"); + goto TEST_CB_ERR; + } + ret = fts_test_write_reg(FACTORY_REG_CB_ADDR_L, addr_l); + if (ret) { + FTS_TEST_SAVE_ERR("write cb addr low fail\n"); + goto TEST_CB_ERR; + } + + ret = fts_test_read(cb_addr, data + offset, read_num); + if (ret) { + FTS_TEST_SAVE_ERR("read cb fail\n"); + goto TEST_CB_ERR; + } + } + + for (i = 0; i < byte_num; i++) { + cb_buf[i] = data[i]; + } + +TEST_CB_ERR: + fts_free(data); + return ret; +} + +int get_cb_sc(int byte_num, int *cb_buf, enum byte_mode mode) +{ + int ret = 0; + int i = 0; + int read_num = 0; + int packet_num = 0; + int packet_remainder = 0; + int offset = 0; + u8 cb_addr = 0; + u8 off_addr = 0; + u8 off_h_addr = 0; + struct fts_test *tdata = fts_ftest; + u8 *cb = NULL; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("test/func is null\n"); + return -EINVAL; + } + + cb = (u8 *)fts_malloc(byte_num * sizeof(u8)); + if (NULL == cb) { + FTS_TEST_SAVE_ERR("malloc memory for cb buffer fail\n"); + return -ENOMEM; + } + + if (IC_HW_MC_SC == tdata->func->hwtype) { + cb_addr = FACTORY_REG_MC_SC_CB_ADDR; + off_addr = FACTORY_REG_MC_SC_CB_ADDR_OFF; + off_h_addr = FACTORY_REG_MC_SC_CB_H_ADDR_OFF; + } else if (IC_HW_SC == tdata->func->hwtype) { + cb_addr = FACTORY_REG_SC_CB_ADDR; + off_addr = FACTORY_REG_SC_CB_ADDR_OFF; + } + + packet_num = byte_num / BYTES_PER_TIME; + packet_remainder = byte_num % BYTES_PER_TIME; + if (packet_remainder) + packet_num++; + read_num = BYTES_PER_TIME; + offset = 0; + + FTS_TEST_INFO("cb packet:%d,remainder:%d", packet_num, packet_remainder); + for (i = 0; i < packet_num; i++) { + if ((i == (packet_num - 1)) && packet_remainder) { + read_num = packet_remainder; + } + + ret = fts_test_write_reg(off_addr, offset); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write cb addr offset fail\n"); + goto cb_err; + } + + if (tdata->func->cb_high_support) { + ret = fts_test_write_reg(off_h_addr, offset >> 8); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write cb_h addr offset fail\n"); + goto cb_err; + } + } + + ret = fts_test_read(cb_addr, cb + offset, read_num); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read cb fail\n"); + goto cb_err; + } + + offset += read_num; + } + + if (DATA_ONE_BYTE == mode) { + for (i = 0; i < byte_num; i++) { + cb_buf[i] = cb[i]; + } + } else if (DATA_TWO_BYTE == mode) { + for (i = 0; i < byte_num; i = i + 2) { + cb_buf[i >> 1] = (int)(((int)(cb[i]) << 8) + cb[i + 1]); + } + } + + ret = 0; +cb_err: + fts_free(cb); + return ret; +} + +bool compare_data(int *data, int min, int max, int min_vk, int max_vk, bool key) +{ + int i = 0; + bool result = true; + struct fts_test *tdata = fts_ftest; + int rx = tdata->node.rx_num; + int node_va = tdata->node.node_num - tdata->node.key_num; + + if (!data || !tdata->node_valid) { + FTS_TEST_SAVE_ERR("data/node_valid is null\n"); + return false; + } + + for (i = 0; i < node_va; i++) { + if (0 == tdata->node_valid[i]) + continue; + + if ((data[i] < min) || (data[i] > max)) { + FTS_TEST_SAVE_ERR("test fail,node(%4d,%4d)=%5d,range=(%5d,%5d)\n", + i / rx + 1, i % rx + 1, data[i], min, max); + result = false; + } + } + + if (key) { + for (i = node_va; i < tdata->node.node_num; i++) { + if (0 == tdata->node_valid[i]) + continue; + + if ((data[i] < min_vk) || (data[i] > max_vk)) { + FTS_TEST_SAVE_ERR("test fail,node(%4d,%4d)=%5d,range=(%5d,%5d)\n", + i / rx + 1, i % rx + 1, + data[i], min_vk, max_vk); + result = false; + } + } + } + + return result; +} + +bool compare_array(int *data, int *min, int *max, bool key) +{ + int i = 0; + bool result = true; + struct fts_test *tdata = fts_ftest; + int rx = tdata->node.rx_num; + int node_num = tdata->node.node_num; + + if (!data || !min || !max || !tdata->node_valid) { + FTS_TEST_SAVE_ERR("data/min/max/node_valid is null\n"); + return false; + } + + if (!key) { + node_num -= tdata->node.key_num; + } + for (i = 0; i < node_num; i++) { + if (0 == tdata->node_valid[i]) + continue; + + if ((data[i] < min[i]) || (data[i] > max[i])) { + FTS_TEST_SAVE_ERR("test fail,node(%4d,%4d)=%5d,range=(%5d,%5d)\n", + i / rx + 1, i % rx + 1, data[i], min[i], max[i]); + result = false; + } + } + + return result; +} + +/* + * show_data - show and save test data to testresult.txt + */ +void show_data(int *data, bool key) +{ +#if TXT_SUPPORT + int i = 0; + int j = 0; + struct fts_test *tdata = fts_ftest; + int node_num = tdata->node.node_num; + int tx_num = tdata->node.tx_num; + int rx_num = tdata->node.rx_num; + + FTS_TEST_FUNC_ENTER(); + for (i = 0; i < tx_num; i++) { + FTS_TEST_SAVE_INFO("Ch/Tx_%02d: ", i + 1); + for (j = 0; j < rx_num; j++) { + FTS_TEST_SAVE_INFO("%5d, ", data[i * rx_num + j]); + } + FTS_TEST_SAVE_INFO("\n"); + } + + if (key) { + FTS_TEST_SAVE_INFO("Ch/Tx_%02d: ", tx_num + 1); + for (i = tx_num * rx_num; i < node_num; i++) { + FTS_TEST_SAVE_INFO("%5d, ", data[i]); + } + FTS_TEST_SAVE_INFO("\n"); + } + FTS_TEST_FUNC_EXIT(); +#endif +} + +/* mc_sc only */ +/* Only V3 Pattern has mapping & no-mapping */ +int mapping_switch(u8 mapping) +{ + int ret = 0; + u8 val = 0xFF; + struct fts_test *tdata = fts_ftest; + + if (tdata->v3_pattern) { + ret = fts_test_read_reg(FACTORY_REG_NOMAPPING, &val); + if (ret < 0) { + FTS_TEST_ERROR("read 0x54 register fail"); + return ret; + } + + if (val != mapping) { + ret = fts_test_write_reg(FACTORY_REG_NOMAPPING, mapping); + if (ret < 0) { + FTS_TEST_ERROR("write 0x54 register fail"); + return ret; + } + sys_delay(FACTORY_TEST_DELAY); + } + } + + return 0; +} + +bool get_fw_wp(u8 wp_ch_sel, enum wp_type water_proof_type) +{ + bool fw_wp_state = false; + + switch (water_proof_type) { + case WATER_PROOF_ON: + /* bit5: 0-check in wp on, 1-not check */ + fw_wp_state = !(wp_ch_sel & 0x20); + break; + case WATER_PROOF_ON_TX: + /* Bit6: 0-check Rx+Tx in wp mode 1-check one channel + Bit2: 0-check Tx in wp mode; 1-check Rx in wp mode + */ + fw_wp_state = (!(wp_ch_sel & 0x40) || !(wp_ch_sel & 0x04)); + break; + case WATER_PROOF_ON_RX: + fw_wp_state = (!(wp_ch_sel & 0x40) || (wp_ch_sel & 0x04)); + break; + case WATER_PROOF_OFF: + /* bit7: 0-check in wp off, 1-not check */ + fw_wp_state = !(wp_ch_sel & 0x80); + break; + case WATER_PROOF_OFF_TX: + /* Bit1-0: 00-check Tx in non-wp mode + 01-check Rx in non-wp mode + 10:check Rx+Tx in non-wp mode + */ + fw_wp_state = ((0x0 == (wp_ch_sel & 0x03)) || (0x02 == (wp_ch_sel & 0x03))); + break; + case WATER_PROOF_OFF_RX: + fw_wp_state = ((0x01 == (wp_ch_sel & 0x03)) || (0x02 == (wp_ch_sel & 0x03))); + break; + default: + break; + } + + return fw_wp_state; +} + +int get_cb_mc_sc(u8 wp, int byte_num, int *cb_buf, enum byte_mode mode) +{ + int ret = 0; + + /* 1:waterproof 0:non-waterproof */ + ret = fts_test_write_reg(FACTORY_REG_MC_SC_MODE, wp); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set mc_sc mode fail\n"); + return ret; + } + + if (fts_ftest->func->param_update_support) { + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + return ret; + } + } + + /* read cb */ + ret = get_cb_sc(byte_num, cb_buf, mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get sc cb fail\n"); + return ret; + } + + return 0; +} + +int get_rawdata_mc_sc(enum wp_type wp, int *data) +{ + int ret = 0; + u8 val = 0; + u8 addr = 0; + u8 rawdata_addr = 0; + int byte_num = 0; + struct fts_test *tdata = fts_ftest; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("test/func is null\n"); + return -EINVAL; + } + + byte_num = tdata->sc_node.node_num * 2; + addr = FACTORY_REG_LINE_ADDR; + rawdata_addr = FACTORY_REG_RAWDATA_ADDR_MC_SC; + if (WATER_PROOF_ON == wp) { + val = 0xAC; + } else if (WATER_PROOF_OFF == wp) { + val = 0xAB; + } else if (HIGH_SENSITIVITY == wp) { + val = 0xA0; + } else if (HOV == wp) { + val = 0xA1; + byte_num = 4 * 2; + } + + ret = read_rawdata(tdata, addr, val, rawdata_addr, byte_num, data); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read rawdata fail\n"); + return ret; + } + + return 0; +} + +int get_rawdata_mc(u8 fre, u8 fir, int *rawdata) +{ + int ret = 0; + int i = 0; + + if (NULL == rawdata ) { + FTS_TEST_SAVE_ERR("rawdata buffer is null\n"); + return -EINVAL; + } + + /* set frequecy high/low */ + ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, fre); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set frequecy fail,ret=%d\n", ret); + return ret; + } + + /* fir enable/disable */ + ret = fts_test_write_reg(FACTORY_REG_FIR, fir); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set fir fail,ret=%d\n", ret); + return ret; + } + + /* get rawdata */ + for (i = 0; i < 3; i++) { + /* lost 3 frames, in order to obtain stable data */ + ret = get_rawdata(rawdata); + } + if (ret < 0) { + FTS_TEST_SAVE_ERR("get rawdata fail,ret=%d\n", ret); + return ret; + } + + return 0; +} + +int short_get_adc_data_mc(u8 retval, int byte_num, int *adc_buf, u8 mode) +{ + int ret = 0; + int i = 0; + u8 short_state = 0; + u8 short_state_reg = 0; + u8 short_en_reg = 0; + u8 short_data_reg = 0; + struct fts_test *tdata = fts_ftest; + + FTS_TEST_FUNC_ENTER(); + if (tdata->func->mc_sc_short_v2) { + short_en_reg = FACTROY_REG_SHORT2_TEST_EN; + short_state_reg = FACTROY_REG_SHORT2_TEST_STATE; + short_data_reg = FACTORY_REG_SHORT2_ADDR_MC; + + ret = fts_test_write_reg(FACTROY_REG_SHORT2_TEST_STATE, 0x03); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write short state fail\n"); + goto test_err; + } + } else { + short_en_reg = FACTROY_REG_SHORT_TEST_EN; + short_state_reg = FACTROY_REG_SHORT_TEST_EN; + short_data_reg = FACTORY_REG_SHORT_ADDR_MC; + } + + /* select short test mode & start test */ + ret = fts_test_write_reg(short_en_reg, mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write short test mode fail\n"); + goto test_err; + } + + for (i = 0; i < FACTORY_TEST_RETRY; i++) { + sys_delay(FACTORY_TEST_RETRY_DELAY); + + ret = fts_test_read_reg(short_state_reg, &short_state); + if ((ret >= 0) && (retval == short_state)) + break; + else + FTS_TEST_DBG("reg%x=%x,retry:%d", short_state_reg, short_state, i); + } + if (i >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("short test timeout, ADC data not OK\n"); + ret = -EIO; + goto test_err; + } + + ret = read_mass_data(short_data_reg, byte_num, adc_buf); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get short(adc) data fail\n"); + } + + FTS_TEST_DBG("adc data:\n"); + print_buffer(adc_buf, byte_num / 2, 0); +test_err: + FTS_TEST_FUNC_EXIT(); + return ret; +} + +bool compare_mc_sc(bool tx_check, bool rx_check, int *data, int *min, int *max) +{ + int i = 0; + bool result = true; + struct fts_test *tdata = fts_ftest; + + if (rx_check) { + for (i = 0; i < tdata->sc_node.rx_num; i++) { + if (0 == tdata->node_valid_sc[i]) + continue; + + if ((data[i] < min[i]) || (data[i] > max[i])) { + FTS_TEST_SAVE_ERR("test fail,rx%d=%5d,range=(%5d,%5d)\n", + i + 1, data[i], min[i], max[i]); + result = false; + } + } + } + + if (tx_check) { + for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) { + if (0 == tdata->node_valid_sc[i]) + continue; + + if ((data[i] < min[i]) || (data[i] > max[i])) { + FTS_TEST_SAVE_INFO("test fail,tx%d=%5d,range=(%5d,%5d)\n", + i - tdata->sc_node.rx_num + 1, + data[i], min[i], max[i]); + result = false; + } + } + } + + return result; +} + +void show_data_mc_sc(int *data) +{ + int i = 0; + struct fts_test *tdata = fts_ftest; + + FTS_TEST_SAVE_INFO("SCap Rx: "); + for (i = 0; i < tdata->sc_node.rx_num; i++) { + FTS_TEST_SAVE_INFO( "%5d, ", data[i]); + } + FTS_TEST_SAVE_INFO("\n"); + + FTS_TEST_SAVE_INFO("SCap Tx: "); + for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) { + FTS_TEST_SAVE_INFO( "%5d, ", data[i]); + } + FTS_TEST_SAVE_INFO("\n"); +} +/* mc_sc end*/ + +#if CSV_SUPPORT || TXT_SUPPORT +#ifndef CONFIG_FTS_COMPATIBLE_WITH_GKI +static int fts_test_save_test_data(char *file_name, char *data_buf, int len) +{ + struct file *pfile = NULL; + char filepath[FILE_NAME_LENGTH] = { 0 }; + loff_t pos; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + mm_segment_t old_fs; +#endif + + FTS_TEST_FUNC_ENTER(); + memset(filepath, 0, sizeof(filepath)); + snprintf(filepath, FILE_NAME_LENGTH, "%s%s", FTS_OUT_FILE_PATH, file_name); + FTS_INFO("save test data to %s", filepath); + if (NULL == pfile) { + pfile = filp_open(filepath, O_TRUNC | O_CREAT | O_RDWR, 0666); + } + if (IS_ERR(pfile)) { + FTS_TEST_ERROR("error occured while opening file %s.", filepath); + return -EIO; + } + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + old_fs = get_fs(); + set_fs(KERNEL_DS); +#endif + pos = 0; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + vfs_write(pfile, data_buf, len, &pos); +#else + kernel_write(pfile, data_buf, len, &pos); +#endif + filp_close(pfile, NULL); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + set_fs(old_fs); +#endif + + FTS_TEST_FUNC_EXIT(); + return 0; +} +#else +static int fts_test_save_test_data(char *file_name, char *data_buf, int len) +{ + return 0; +} +#endif + +#if defined(TEST_SAVE_FAIL_RESULT) && TEST_SAVE_FAIL_RESULT +void fts_test_save_fail_result( + struct fts_test *tdata, char *prefix, char *suffix, char *buf, int len) +{ + char file_name[128]; + + if (false == tdata->result) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + snprintf(file_name, 128, "%s_%ld_%ld%s", prefix, + (long)tdata->tv.tv_sec, ((long)tdata->tv.tv_nsec)/1000, suffix); +#else + snprintf(file_name, 128, "%s_%ld_%ld%s", prefix, + (long)tdata->tv.tv_sec, (long)tdata->tv.tv_usec, suffix); +#endif + fts_test_save_test_data(file_name, buf, len); + } +} +#endif +#endif + +static int fts_test_malloc_free_data_txt(struct fts_test *tdata, bool allocate) +{ +#if TXT_SUPPORT + if (true == allocate) { + tdata->testresult = vmalloc(TXT_BUFFER_LEN); + if (NULL == tdata->testresult) { + FTS_TEST_ERROR("tdata->testresult malloc fail\n"); + return -ENOMEM; + } + + tdata->testresult_len = 0; + FTS_TEST_SAVE_INFO("FW version:0x%02x\n", tdata->fw_ver); + FTS_TEST_SAVE_INFO("tx_num:%d, rx_num:%d, key_num:%d\n", + tdata->node.tx_num, tdata->node.rx_num, + tdata->node.key_num); + } else { + if (tdata->testresult) { + vfree(tdata->testresult); + tdata->testresult = NULL; + } + } +#endif + return 0; +} + +#if CSV_SUPPORT +static int fts_test_get_item_count_scap_csv(int index) +{ + int ret = 0; + int i = 0; + int select = 0; + u8 wc_sel = 0; + u8 hc_sel = 0; + u8 scap_select[4] = { 0 }; + + /* get waterproof channel select */ + ret = fts_test_read_reg(FACTORY_REG_WC_SEL, &wc_sel); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read water_channel_sel fail,ret=%d\n", ret); + return index; + } + + ret = fts_test_read_reg(FACTORY_REG_HC_SEL, &hc_sel); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read high_channel_sel fail,ret=%d\n", ret); + return index; + } + + scap_select[0] = get_fw_wp(wc_sel, WATER_PROOF_ON); + scap_select[1] = get_fw_wp(wc_sel, WATER_PROOF_OFF); + scap_select[2] = (hc_sel & 0x03) ? 1 : 0; + scap_select[3] = (hc_sel & 0x04) ? 1 : 0; + + for (i = 0; i < 4; i++) { + if (scap_select[i]) + select++; + if (select == index) + break; + } + + return (i + 1); +} +#endif + +static void fts_test_save_data_csv(struct fts_test *tdata) +{ +#if CSV_SUPPORT + int i = 0; + int j = 0; + int index = 0; + int k = 0; + int tx = 0; + int rx = 0; + int node_num = 0; + int offset = 0; + int start_line = 11; + int data_count = 0; + char *csv_buffer = NULL; + char *line2_buffer = NULL; + int csv_length = 0; + int line2_length = 0; + int csv_item_count = 0; + struct fts_test_data *td = &tdata->testdata; + struct item_info *info = NULL; + + FTS_TEST_INFO("save data in csv format"); + csv_buffer = vmalloc(CSV_BUFFER_LEN); + if (!csv_buffer) { + FTS_TEST_ERROR("csv_buffer malloc fail\n"); + return ; + } + + line2_buffer = vmalloc(CSV_LINE2_BUFFER_LEN); + if (!line2_buffer) { + FTS_TEST_ERROR("line2_buffer malloc fail\n"); + goto csv_save_err; + } + + FTS_TEST_INFO("test item count:%d", td->item_count); + /* line 1 */ + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "ECC, 85, 170, IC Name, %s, IC Code, %x\n", \ + tdata->ini.ic_name, \ + (tdata->ini.ic_code >> IC_CODE_OFFSET)); + + /* line 2 */ + for (i = 0; i < td->item_count; i++) { + info = &td->info[i]; + if (info->mc_sc) { + node_num = tdata->sc_node.node_num; + /* set max len of tx/rx to column */ + rx = (tdata->sc_node.tx_num > tdata->sc_node.rx_num) + ? tdata->sc_node.tx_num : tdata->sc_node.rx_num; + } else { + if (info->key_support && (tdata->node.key_num > 0)) + node_num = (tdata->node.tx_num + 1) * tdata->node.rx_num; + else + node_num = tdata->node.tx_num * tdata->node.rx_num; + rx = tdata->node.rx_num; + } + + if (info->datalen > node_num) { + data_count = (info->datalen - 1 ) / node_num + 1; + tx = (node_num - 1 ) / rx + 1; + } else { + data_count = 1; + tx = ((info->datalen - 1) / rx) + 1; + } + + for (j = 1; j <= data_count; j++) { + index = j; + + if (tdata->func->hwtype == IC_HW_MC_SC) { + /*MC_SC, rawdata index will be 2*/ + if ((info->code == CODE_M_RAWDATA_TEST) && (data_count == 1)) { + index = 2; + } + + /*MC_SC, SCAP index will be 1~4*/ + if ((info->code == CODE_M_SCAP_CB_TEST) + || (info->code == CODE_M_SCAP_RAWDATA_TEST)) { + index = fts_test_get_item_count_scap_csv(j); + } + } + + line2_length += snprintf(line2_buffer + line2_length, \ + CSV_LINE2_BUFFER_LEN - line2_length, \ + "%s, %d, %d, %d, %d, %d, ", \ + info->name, info->code, tx, rx, + start_line, index); + start_line += tx; + csv_item_count++; + } + } + + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "TestItem Num, %d, ", \ + csv_item_count); + + if (line2_length > 0) { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "%s", line2_buffer); + } + + /* line 3 ~ 10 "\n" */ + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "\n\n\n\n\n\n\n\n\n"); + + /* line 11 ~ data area */ + for (i = 0; i < td->item_count; i++) { + info = &td->info[i]; + if (!info->data) { + FTS_TEST_ERROR("test item data is null"); + goto csv_save_err; + } + + if (info->mc_sc) { + offset = 0; + for (j = 0; j < info->datalen;) { + for (k = 0; k < tdata->sc_node.node_num; k++) { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "%d, ", info->data[offset + k]); + if ((k + 1) == tdata->sc_node.rx_num) { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "\n"); + } + } + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "\n"); + offset += k; + j += k; + } + } else { + for (j = 0; j < info->datalen; j++) { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "%d, ", info->data[j]); + if (((j + 1) % tdata->node.rx_num) == 0) { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, + "\n"); + } + } + } + } + FTS_TEST_INFO("csv length:%d", csv_length); + fts_test_save_test_data(FTS_CSV_FILE_NAME, csv_buffer, csv_length); + +#if defined(TEST_SAVE_FAIL_RESULT) && TEST_SAVE_FAIL_RESULT + fts_test_save_fail_result(tdata, "testdata_fail", ".csv", + csv_buffer, csv_length); +#endif + + +csv_save_err: + if (line2_buffer) { + vfree(line2_buffer); + line2_buffer = NULL; + } + + if (csv_buffer) { + vfree(csv_buffer); + csv_buffer = NULL; + } +#endif +} + +static void fts_test_save_data_csv_private(struct fts_test *tdata) +{ +#if CSV_SUPPORT + char *csv_buffer = NULL; + int csv_length = 0; + + FTS_TEST_INFO("save data in csv format"); + +#ifndef CONFIG_FTS_COMPATIBLE_WITH_GKI + csv_buffer = vmalloc(CSV_BUFFER_LEN); + if (!csv_buffer) { + FTS_TEST_ERROR("csv_buffer malloc fail\n"); + return ; + } +#else + if(!fts_ftest->csv_buffer) + return; + + csv_buffer = fts_ftest->csv_buffer; +#endif + + if (tdata->func && tdata->func->save_data_private) + tdata->func->save_data_private(csv_buffer, &csv_length); + + FTS_TEST_INFO("csv length:%d", csv_length); +#ifdef CONFIG_FTS_COMPATIBLE_WITH_GKI + fts_ftest->csv_result_len = csv_length; +#endif + fts_test_save_test_data(FTS_CSV_FILE_NAME, csv_buffer, csv_length); + +#if defined(TEST_SAVE_FAIL_RESULT) && TEST_SAVE_FAIL_RESULT + fts_test_save_fail_result(tdata, "testdata_fail", ".csv", + csv_buffer, csv_length); +#endif + +#ifndef CONFIG_FTS_COMPATIBLE_WITH_GKI + if (csv_buffer) { + vfree(csv_buffer); + csv_buffer = NULL; + } +#endif +#endif +} + +static void fts_test_save_result_txt(struct fts_test *tdata) +{ +#if TXT_SUPPORT + if (!tdata || !tdata->testresult) { + FTS_TEST_ERROR("test result is null"); + return; + } + +#ifdef CONFIG_FTS_COMPATIBLE_WITH_GKI + if(!fts_ftest->txt_buffer) + return; + + memcpy(fts_ftest->txt_buffer, tdata->testresult, tdata->testresult_len); + fts_ftest->txt_result_len = tdata->testresult_len; +#endif + + FTS_TEST_INFO("test result length in txt:%d", tdata->testresult_len); + fts_test_save_test_data(FTS_TXT_FILE_NAME, tdata->testresult, + tdata->testresult_len); + +#if defined(TEST_SAVE_FAIL_RESULT) && TEST_SAVE_FAIL_RESULT + fts_test_save_fail_result(tdata, "testresult_fail", ".txt", + tdata->testresult, tdata->testresult_len); +#endif + +#endif +} + +/***************************************************************************** +* Name: fts_test_save_data +* Brief: Save test data. +* If multi-data of MC, length of data package must be tx*rx,(tx+1)*rx +* If multi-data of MC-SC, length of data package should be (tx+rx)*2 +* Need fill 0 when no actual data +* Input: +* Output: +* Return: +*****************************************************************************/ +void fts_test_save_data(char *name, int code, int *data, int datacnt, + bool mc_sc, bool key, bool result) +{ + int datalen = datacnt; + struct fts_test *tdata = fts_ftest; + struct fts_test_data *td = &tdata->testdata; + struct item_info *info = &td->info[td->item_count]; + + if (!name || !data) { + FTS_TEST_ERROR("name/data is null"); + return ; + } + + strlcpy(info->name, name, TEST_ITEM_NAME_MAX - 1); + info->code = code; + info->mc_sc = mc_sc; + info->key_support = key; + info->result = result; + if (datalen <= 0) { + if (mc_sc) { + datalen = tdata->sc_node.node_num * 2; + } else { + if (key && (tdata->node.key_num > 0)) + datalen = (tdata->node.tx_num + 1) * tdata->node.rx_num; + else + datalen = tdata->node.tx_num * tdata->node.rx_num; + + } + } + + FTS_TEST_DBG("name:%s,len:%d", name, datalen); + info->data = fts_malloc(datalen * sizeof(int)); + if (!info->data) { + FTS_TEST_ERROR("malloc memory for item(%d) data fail", td->item_count); + info->datalen = 0; + return ; + } + memcpy(info->data, data, datalen * sizeof(int)); + info->datalen = datalen; + + td->item_count++; +} + +static void fts_test_free_data(struct fts_test *tdata) +{ + int i = 0; + struct fts_test_data *td = &tdata->testdata; + + for (i = 0; i < td->item_count; i++) { + if (td->info[i].data) { + fts_free(td->info[i].data); + } + } +} + +static int fts_test_malloc_free_incell(struct fts_test *tdata, bool allocate) +{ + struct incell_threshold *thr = &tdata->ic.incell.thr; + int buflen = tdata->node.node_num * sizeof(int); + + if (true == allocate) { + FTS_TEST_INFO("buflen:%d", buflen); + fts_malloc_r(thr->rawdata_min, buflen); + fts_malloc_r(thr->rawdata_max, buflen); + if (tdata->func->rawdata2_support) { + fts_malloc_r(thr->rawdata2_min, buflen); + fts_malloc_r(thr->rawdata2_max, buflen); + } + fts_malloc_r(thr->cb_min, buflen); + fts_malloc_r(thr->cb_max, buflen); + } else { + fts_free(thr->rawdata_min); + fts_free(thr->rawdata_max); + if (tdata->func->rawdata2_support) { + fts_free(thr->rawdata2_min); + fts_free(thr->rawdata2_max); + } + fts_free(thr->cb_min); + fts_free(thr->cb_max); + } + + return 0; +} + +static int fts_test_malloc_free_mc_sc(struct fts_test *tdata, bool allocate) +{ + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + int buflen = tdata->node.node_num * sizeof(int); + int buflen_sc = tdata->sc_node.node_num * sizeof(int); + + if (true == allocate) { + fts_malloc_r(thr->rawdata_h_min, buflen); + fts_malloc_r(thr->rawdata_h_max, buflen); + if (tdata->func->rawdata2_support) { + fts_malloc_r(thr->rawdata_l_min, buflen); + fts_malloc_r(thr->rawdata_l_max, buflen); + } + fts_malloc_r(thr->tx_linearity_max, buflen); + fts_malloc_r(thr->tx_linearity_min, buflen); + fts_malloc_r(thr->rx_linearity_max, buflen); + fts_malloc_r(thr->rx_linearity_min, buflen); + + fts_malloc_r(thr->mcap_cmb_min, buflen); + fts_malloc_r(thr->mcap_cmb_max, buflen); + + fts_malloc_r(thr->scap_cb_off_min, buflen_sc); + fts_malloc_r(thr->scap_cb_off_max, buflen_sc); + fts_malloc_r(thr->scap_cb_on_min, buflen_sc); + fts_malloc_r(thr->scap_cb_on_max, buflen_sc); + fts_malloc_r(thr->scap_cb_hi_min, buflen_sc); + fts_malloc_r(thr->scap_cb_hi_max, buflen_sc); + fts_malloc_r(thr->scap_cb_hov_min, buflen_sc); + fts_malloc_r(thr->scap_cb_hov_max, buflen_sc); + fts_malloc_r(thr->scap_cb_off_cf_min, buflen_sc); + fts_malloc_r(thr->scap_cb_off_cf_max, buflen_sc); + fts_malloc_r(thr->scap_cb_on_cf_min, buflen_sc); + fts_malloc_r(thr->scap_cb_on_cf_max, buflen_sc); + fts_malloc_r(thr->scap_cb_hi_cf_min, buflen_sc); + fts_malloc_r(thr->scap_cb_hi_cf_max, buflen_sc); + + fts_malloc_r(thr->scap_rawdata_off_min, buflen_sc); + fts_malloc_r(thr->scap_rawdata_off_max, buflen_sc); + fts_malloc_r(thr->scap_rawdata_on_min, buflen_sc); + fts_malloc_r(thr->scap_rawdata_on_max, buflen_sc); + fts_malloc_r(thr->scap_rawdata_hi_min, buflen_sc); + fts_malloc_r(thr->scap_rawdata_hi_max, buflen_sc); + fts_malloc_r(thr->scap_rawdata_hov_min, buflen_sc); + fts_malloc_r(thr->scap_rawdata_hov_max, buflen_sc); + + fts_malloc_r(thr->panel_differ_min, buflen); + fts_malloc_r(thr->panel_differ_max, buflen); +#if defined(CONFIG_FTS_NOISE_TEST_P2P) + fts_malloc_r(thr->noise_min, buflen); + fts_malloc_r(thr->noise_max, buflen); +#endif + } else { + fts_free(thr->rawdata_h_min); + fts_free(thr->rawdata_h_max); + if (tdata->func->rawdata2_support) { + fts_free(thr->rawdata_l_min); + fts_free(thr->rawdata_l_max); + } + fts_free(thr->tx_linearity_max); + fts_free(thr->tx_linearity_min); + fts_free(thr->rx_linearity_max); + fts_free(thr->rx_linearity_min); + + fts_free(thr->mcap_cmb_min); + fts_free(thr->mcap_cmb_max); + + fts_free(thr->scap_cb_off_min); + fts_free(thr->scap_cb_off_max); + fts_free(thr->scap_cb_on_min); + fts_free(thr->scap_cb_on_max); + fts_free(thr->scap_cb_hi_min); + fts_free(thr->scap_cb_hi_max); + fts_free(thr->scap_cb_hov_min); + fts_free(thr->scap_cb_hov_max); + fts_free(thr->scap_cb_off_cf_min); + fts_free(thr->scap_cb_off_cf_max); + fts_free(thr->scap_cb_on_cf_min); + fts_free(thr->scap_cb_on_cf_max); + fts_free(thr->scap_cb_hi_cf_min); + fts_free(thr->scap_cb_hi_cf_max); + + fts_free(thr->scap_rawdata_off_min); + fts_free(thr->scap_rawdata_off_max); + fts_free(thr->scap_rawdata_on_min); + fts_free(thr->scap_rawdata_on_max); + fts_free(thr->scap_rawdata_hi_min); + fts_free(thr->scap_rawdata_hi_max); + fts_free(thr->scap_rawdata_hov_min); + fts_free(thr->scap_rawdata_hov_max); + + fts_free(thr->panel_differ_min); + fts_free(thr->panel_differ_max); +#if defined(CONFIG_FTS_NOISE_TEST_P2P) + fts_free(thr->noise_min); + fts_free(thr->noise_max); +#endif + } + + return 0; +} + +static int fts_test_malloc_free_sc(struct fts_test *tdata, bool allocate) +{ + struct sc_threshold *thr = &tdata->ic.sc.thr; + int buflen = tdata->node.node_num * sizeof(int); + + if (true == allocate) { + fts_malloc_r(thr->rawdata_min, buflen); + fts_malloc_r(thr->rawdata_max, buflen); + fts_malloc_r(thr->cb_min, buflen); + fts_malloc_r(thr->cb_max, buflen); + fts_malloc_r(thr->dcb_sort, buflen); + fts_malloc_r(thr->dcb_base, buflen); + } else { + fts_free(thr->rawdata_min); + fts_free(thr->rawdata_max); + fts_free(thr->cb_min); + fts_free(thr->cb_max); + fts_free(thr->dcb_sort); + fts_free(thr->dcb_base); + } + + return 0; +} + +static int fts_test_malloc_free_thr(struct fts_test *tdata, bool allocate) +{ + int ret = 0; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("tdata/func is NULL\n"); + return -EINVAL; + } + + if (true == allocate) { + fts_malloc_r(tdata->node_valid, tdata->node.node_num * sizeof(int)); + fts_malloc_r(tdata->node_valid_sc, tdata->sc_node.node_num * sizeof(int)); + } else { + fts_free(tdata->node_valid); + fts_free(tdata->node_valid_sc); + } + + switch (tdata->func->hwtype) { + case IC_HW_INCELL: + ret = fts_test_malloc_free_incell(tdata, allocate); + break; + case IC_HW_MC_SC: + ret = fts_test_malloc_free_mc_sc(tdata, allocate); + break; + case IC_HW_SC: + ret = fts_test_malloc_free_sc(tdata, allocate); + break; + default: + FTS_TEST_SAVE_ERR("test ic type(%d) fail\n", tdata->func->hwtype); + ret = -EINVAL; + break; + } + + return ret; +} + +/* default enable all test item */ +static void fts_test_init_item(struct fts_test *tdata) +{ + switch (tdata->func->hwtype) { + case IC_HW_INCELL: + tdata->ic.incell.u.tmp = 0xFFFFFFFF; + break; + case IC_HW_MC_SC: + tdata->ic.mc_sc.u.tmp = 0xFFFFFFFF; + break; + case IC_HW_SC: + tdata->ic.sc.u.tmp = 0xFFFFFFFF; + break; + } +} + +static int get_tx_rx_num(u8 tx_rx_reg, u8 *ch_num, u8 ch_num_max) +{ + int ret = 0; + int i = 0; + + for (i = 0; i < 3; i++) { + ret = fts_test_read_reg(tx_rx_reg, ch_num); + if ((ret < 0) || (*ch_num > ch_num_max)) { + sys_delay(50); + } else + break; + } + + if (i >= 3) { + FTS_TEST_ERROR("get channel num fail"); + return -EIO; + } + + return 0; +} +static int get_key_num(int *key_num_en, int max_key_num) +{ + int ret = 0; + u8 key_en = 0; + + if (!max_key_num) { + FTS_TEST_DBG("not support key, don't read key num register"); + return 0; + } + + ret = fts_test_read_reg(FACTORY_REG_LEFT_KEY, &key_en); + if (ret >= 0) { + if (key_en & 0x01) { + (*key_num_en)++; + } + + if (key_en & 0x02) { + (*key_num_en)++; + } + + if (key_en & 0x04) { + (*key_num_en)++; + } + } + + ret = fts_test_read_reg(FACTORY_REG_RIGHT_KEY, &key_en); + if (ret >= 0) { + if (key_en & 0x01) { + (*key_num_en)++; + } + + if (key_en & 0x02) { + (*key_num_en)++; + } + + if (key_en & 0x04) { + (*key_num_en)++; + } + } + + if (*key_num_en > max_key_num) { + FTS_TEST_ERROR("get key num, fw:%d > max:%d", *key_num_en, max_key_num); + return -EIO; + } + + return ret; +} + +static int get_channel_num(struct fts_test *tdata) +{ + int ret = 0; + u8 tx_num = 0; + u8 rx_num = 0; + int key_num = 0; + + /* node structure */ + if (IC_HW_SC == tdata->func->hwtype) { + ret = get_tx_rx_num(FACTORY_REG_CH_NUM_SC, &tx_num, NUM_MAX_SC); + if (ret < 0) { + FTS_TEST_ERROR("get channel number fail"); + return ret; + } + + ret = get_tx_rx_num(FACTORY_REG_KEY_NUM_SC, &rx_num, KEY_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get key number fail"); + return ret; + } + + tdata->node.tx_num = 1; + tdata->node.rx_num = tx_num; + tdata->node.channel_num = tx_num; + tdata->node.node_num = tx_num; + key_num = rx_num; + } else { + ret = get_tx_rx_num(FACTORY_REG_CHX_NUM, &tx_num, TX_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get tx_num fail"); + return ret; + } + + ret = get_tx_rx_num(FACTORY_REG_CHY_NUM, &rx_num, RX_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get rx_num fail"); + return ret; + } + + if (IC_HW_INCELL == tdata->func->hwtype) { + ret = get_key_num(&key_num, tdata->func->key_num_total); + if (ret < 0) { + FTS_TEST_ERROR("get key_num fail"); + return ret; + } + } else if (IC_HW_MC_SC == tdata->func->hwtype) { + key_num = tdata->func->key_num_total; + } + tdata->node.tx_num = tx_num; + tdata->node.rx_num = rx_num; + if (IC_HW_INCELL == tdata->func->hwtype) + tdata->node.channel_num = tx_num * rx_num; + else if (IC_HW_MC_SC == tdata->func->hwtype) + tdata->node.channel_num = tx_num + rx_num; + tdata->node.node_num = tx_num * rx_num; + } + + /* key */ + tdata->node.key_num = key_num; + tdata->node.node_num += tdata->node.key_num; + + /* sc node structure */ + tdata->sc_node = tdata->node; + if (IC_HW_MC_SC == tdata->func->hwtype) { + if (tdata->v3_pattern) { + ret = get_tx_rx_num(FACTORY_REG_CHX_NUM_NOMAP, &tx_num, TX_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get no-mappint tx_num fail"); + return ret; + } + + ret = get_tx_rx_num(FACTORY_REG_CHY_NUM_NOMAP, &rx_num, TX_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get no-mapping rx_num fail"); + return ret; + } + + tdata->sc_node.tx_num = tx_num; + tdata->sc_node.rx_num = rx_num; + } + tdata->sc_node.channel_num = tx_num + rx_num; + tdata->sc_node.node_num = tx_num + rx_num; + } + + if (tdata->node.tx_num > TX_NUM_MAX) { + FTS_TEST_ERROR("tx num(%d) fail", tdata->node.tx_num); + return -EIO; + } + + if (tdata->node.rx_num > RX_NUM_MAX) { + FTS_TEST_ERROR("rx num(%d) fail", tdata->node.rx_num); + return -EIO; + } + + FTS_TEST_INFO("node_num:%d, tx:%d, rx:%d, key:%d", + tdata->node.node_num, tdata->node.tx_num, + tdata->node.rx_num, tdata->node.key_num); + return 0; +} + +static int fts_test_init_basicinfo(struct fts_test *tdata) +{ + int ret = 0; + u8 val = 0; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("tdata/func is NULL\n"); + return -EINVAL; + } + + fts_test_read_reg(REG_FW_VERSION, &val); + tdata->fw_ver = val; + + if (IC_HW_INCELL == tdata->func->hwtype) { + fts_test_read_reg(REG_VA_TOUCH_THR, &val); + tdata->va_touch_thr = val; + fts_test_read_reg(REG_VKEY_TOUCH_THR, &val); + tdata->vk_touch_thr = val; + } + + /* enter factory mode */ + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail\n"); + return ret; + } + + if (IC_HW_MC_SC == tdata->func->hwtype) { + fts_test_read_reg(FACTORY_REG_PATTERN, &val); + tdata->v3_pattern = (1 == val) ? true : false; + fts_test_read_reg(FACTORY_REG_NOMAPPING, &val); + tdata->mapping = val; + } + + /* enter into factory mode and read tx/rx num */ + ret = get_channel_num(tdata); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get channel number fail\n"); + return ret; + } + + return ret; +} + +static int fts_test_main_init(void) +{ + int ret = 0; + struct fts_test *tdata = fts_ftest; + + FTS_TEST_FUNC_ENTER(); + /* Init fts_test_data to 0 before test, */ + memset(&tdata->testdata, 0, sizeof(struct fts_test_data)); + + /* get basic information: tx/rx num ... */ + ret = fts_test_init_basicinfo(tdata); + if (ret < 0) { + FTS_TEST_ERROR("test init basicinfo fail"); + return ret; + } + + /* allocate memory for test threshold */ + ret = fts_test_malloc_free_thr(tdata, true); + if (ret < 0) { + FTS_TEST_ERROR("test malloc for threshold fail"); + return ret; + } + + /* default enable all test item */ + fts_test_init_item(tdata); + + ret = fts_test_malloc_free_data_txt(tdata, true); + if (ret < 0) { + FTS_TEST_ERROR("allocate memory for test data(txt) fail"); + return ret; + } + + /* allocate test data buffer */ + tdata->buffer_length = (tdata->node.tx_num + 1) * tdata->node.rx_num; + tdata->buffer_length *= sizeof(int) * 2; + FTS_TEST_INFO("test buffer length:%d", tdata->buffer_length); + tdata->buffer = (int *)fts_malloc(tdata->buffer_length); + if (NULL == tdata->buffer) { + FTS_TEST_ERROR("test buffer(%d) malloc fail", tdata->buffer_length); + return -ENOMEM; + } + memset(tdata->buffer, 0, tdata->buffer_length); + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int fts_test_main_exit(void) +{ + struct fts_test *tdata = fts_ftest; + + FTS_TEST_FUNC_ENTER(); + if (tdata->func && tdata->func->save_data_private) + fts_test_save_data_csv_private(tdata); + else + fts_test_save_data_csv(tdata); + fts_test_save_result_txt(tdata); + + /* free memory */ + fts_test_malloc_free_data_txt(tdata, false); + fts_test_malloc_free_thr(tdata, false); + + /* free test data */ + fts_test_free_data(tdata); + + /*free test data buffer*/ + fts_free(tdata->buffer); + + FTS_TEST_FUNC_EXIT(); + return 0; +} + + +/* + * fts_test_get_testparams - get test parameter from ini + */ +static int fts_test_get_testparams(char *config_name) +{ + int ret = 0; + + ret = fts_test_get_testparam_from_ini(config_name); + + return ret; +} + +static int fts_test_start(void) +{ + int testresult = 0; + struct fts_test *tdata = fts_ftest; + + if (tdata && tdata->func && tdata->func->start_test) { + tdata->testdata.item_count = 0; + testresult = tdata->func->start_test(); + } else { + FTS_TEST_ERROR("test func/start_test func is null"); + } + + return testresult; +} + +/* + * fts_test_entry - test main entry + * + * warning - need disable irq & esdcheck before call this function + * + */ +static int fts_test_entry(char *ini_file_name) +{ + int ret = 0; + + /* test initialize */ + ret = fts_test_main_init(); + if (ret < 0) { + FTS_TEST_ERROR("fts_test_main_init fail"); + goto test_err; + } + + /*Read parse configuration file*/ + FTS_TEST_SAVE_INFO("ini_file_name:%s\n", ini_file_name); + ret = fts_test_get_testparams(ini_file_name); + if (ret < 0) { + FTS_TEST_ERROR("get testparam fail"); + goto test_err; + } + + /* Start testing according to the test configuration */ + if (true == fts_test_start()) { + FTS_TEST_SAVE_INFO("\n\n=======Tp test pass.\n"); + fts_ftest->result = true; + } else { + FTS_TEST_SAVE_INFO("\n\n=======Tp test failure.\n"); + fts_ftest->result = false; +#if defined(TEST_SAVE_FAIL_RESULT) && TEST_SAVE_FAIL_RESULT +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + ktime_get_real_ts64(&(fts_ftest->tv)); +#else + do_gettimeofday(&(fts_ftest->tv)); +#endif +#endif + } + + ret = 0; +test_err: + fts_test_main_exit(); + enter_work_mode(); + return ret; +} + +static ssize_t fts_test_show( + struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + ssize_t size = 0; + char test_result[TEST_ICSERIES_LEN] = { 0 }; + char fwname[FILE_NAME_LENGTH] = { 0 }; + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + struct input_dev *input_dev = ts_data->input_dev; + + if (ts_data->suspended) { + FTS_INFO("In suspend, no test, return now"); + return -EINVAL; + } + + memset(fwname, 0, sizeof(fwname)); + strncpy(fwname, FTS_LIMIT_FILE_NAME, sizeof(FTS_LIMIT_FILE_NAME)); + FTS_TEST_DBG("fwname:%s.", fwname); + + mutex_lock(&input_dev->mutex); + fts_irq_disable(); +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ts_data, DISABLE); +#endif + ret = fts_enter_test_environment(1); + if (ret < 0) { + FTS_ERROR("enter test environment fail"); + } else { + fts_test_entry(fwname); + } + ret = fts_enter_test_environment(0); + if (ret < 0) { + FTS_ERROR("enter normal environment fail"); + } +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ts_data, ENABLE); +#endif + fts_irq_enable(); + if (fts_ftest->result == true) { + strncpy(test_result, FTS_TEST_RESULT_PASS, sizeof(FTS_TEST_RESULT_PASS)); + } else { + strncpy(test_result, FTS_TEST_RESULT_FAIL, sizeof(FTS_TEST_RESULT_FAIL)); + } + + size = snprintf(buf, PAGE_SIZE, "tp test result:[%s]\n", test_result); + mutex_unlock(&input_dev->mutex); + + return size; +} + +/* test from test.ini +* example:echo "***.ini" > fts_test +*/ +static DEVICE_ATTR(fts_test, S_IRUGO, fts_test_show, NULL); + +static struct attribute *fts_test_attributes[] = { + &dev_attr_fts_test.attr, + NULL +}; + +static struct attribute_group fts_test_attribute_group = { + .attrs = fts_test_attributes +}; + +#ifdef CONFIG_FTS_COMPATIBLE_WITH_GKI +static int fts_test_proc_show(struct seq_file *file, void *v) +{ + struct ftxxxx_proc *proc = NULL; + struct fts_ts_data * fts_data = (struct fts_ts_data *)file->private; + if (!fts_data) { + FTS_ERROR("no fts_ts_data set"); + return -EIO; + } + proc = &fts_data->proc_raw; + + switch (proc->opmode) { + case PROC_READ_CSV_DATA: + FTS_INFO("fts test read csv"); + seq_printf(file, "%s", fts_ftest->csv_buffer); + break; + + case PROC_READ_TXT_DATA: + FTS_INFO("fts test read txt"); + seq_printf(file, "%s", fts_ftest->txt_buffer); + break; + + default: + break; + } + + return 0; +} + +static int fts_test_proc_open(struct inode *inode, struct file *file) +{ + return single_open_size(file, fts_test_proc_show, PDE_DATA(inode), PAGE_SIZE * 10); +} + +static ssize_t fts_test_proc_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 ret = 0; + struct fts_ts_data *ts_data = fts_data; + struct ftxxxx_proc *proc = &ts_data->proc_raw; + + if (buflen < 1) { + FTS_ERROR("fts test proc wirte count(%d) fail", buflen); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + writebuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == writebuf) { + FTS_ERROR("fts test proc wirte buf zalloc fail"); + return -ENOMEM; + } + } else { + writebuf = tmpbuf; + } + + if (copy_from_user(writebuf, buff, buflen)) { + FTS_ERROR("fts test copy from user error!!"); + ret = -EFAULT; + goto proc_write_err; + } + + proc->opmode = writebuf[0]; + FTS_INFO("proc->opmode = %d", proc->opmode); + if (buflen == 1) { + ret = buflen; + goto proc_write_err; + } + +proc_write_err: + if ((buflen > PROC_BUF_SIZE) && writebuf) { + kfree(writebuf); + writebuf = NULL; + } + + return ret; +} + +static const struct proc_ops tp_test_proc_fops = +{ + .proc_open = fts_test_proc_open, + .proc_read = seq_read, + .proc_write = fts_test_proc_write, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +#endif + +static int fts_test_func_init(struct fts_ts_data *ts_data) +{ + int i = 0; + int j = 0; + u16 ic_stype = ts_data->ic_info.ids.type; + struct test_funcs *func = test_func_list[0]; + int func_count = sizeof(test_func_list) / sizeof(test_func_list[0]); +#ifdef CONFIG_FTS_COMPATIBLE_WITH_GKI + struct ftxxxx_proc *proc = NULL; +#endif + + FTS_TEST_INFO("init test function"); + if (0 == func_count) { + FTS_TEST_SAVE_ERR("test functions list is NULL, fail\n"); + return -ENODATA; + } + +#ifdef CONFIG_FTS_COMPATIBLE_WITH_GKI + proc = &ts_data->proc_raw; + proc->proc_entry = proc_create_data(FTS_PROC_TP_DIFFER, 0444, NULL, &tp_test_proc_fops, + ts_data); + if (proc->proc_entry == NULL) { + FTS_TEST_ERROR("[focal] %s() - ERROR: create fts_tp_differ proc() failed.", __func__); + return -ENOMEM; + } +#endif + + fts_ftest = (struct fts_test *)kzalloc(sizeof(*fts_ftest), GFP_KERNEL); + if (NULL == fts_ftest) { + FTS_TEST_ERROR("malloc memory for test fail"); + return -ENOMEM; + } + + for (i = 0; i < func_count; i++) { + func = test_func_list[i]; + for (j = 0; j < FTS_MAX_COMPATIBLE_TYPE; j++) { + if (0 == func->ctype[j]) + break; + else if (func->ctype[j] == ic_stype) { + FTS_TEST_INFO("match test function,type:%x", (int)func->ctype[j]); + fts_ftest->func = func; + } + } + } + if (NULL == fts_ftest->func) { + FTS_TEST_ERROR("no test function match, can't test"); + return -ENODATA; + } + + fts_ftest->ts_data = ts_data; + return 0; +} + +int fts_test_init(struct fts_ts_data *ts_data) +{ + int ret = 0; + + FTS_TEST_FUNC_ENTER(); + /* get test function, must be the first step */ + ret = fts_test_func_init(ts_data); + if (ret < 0) { + FTS_TEST_SAVE_ERR("test functions init fail"); + return ret; + } + + ret = sysfs_create_group(&ts_data->dev->kobj, &fts_test_attribute_group); + if (0 != ret) { + FTS_TEST_ERROR("sysfs(test) create fail"); + sysfs_remove_group(&ts_data->dev->kobj, &fts_test_attribute_group); + } else { + FTS_TEST_DBG("sysfs(test) create successfully"); + } + +#ifdef CONFIG_FTS_COMPATIBLE_WITH_GKI + fts_ftest->csv_buffer = vmalloc(CSV_BUFFER_LEN); + if (!fts_ftest->csv_buffer) { + FTS_TEST_ERROR("csv_buffer malloc fail\n"); + } + + fts_ftest->txt_buffer = vmalloc(TXT_BUFFER_LEN); + if (!fts_ftest->txt_buffer) { + FTS_TEST_ERROR("txt_buffer malloc fail\n"); + } +#endif + + FTS_TEST_FUNC_EXIT(); + + return ret; +} + +int fts_test_exit(struct fts_ts_data *ts_data) +{ + FTS_TEST_FUNC_ENTER(); + + sysfs_remove_group(&ts_data->dev->kobj, &fts_test_attribute_group); +#ifdef CONFIG_FTS_COMPATIBLE_WITH_GKI + fts_free(fts_ftest->csv_buffer); + fts_free(fts_ftest->txt_buffer); +#endif + fts_free(fts_ftest); + FTS_TEST_FUNC_EXIT(); + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/focaltech_test.h b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/focaltech_test.h new file mode 100644 index 000000000000..bf808334d1ad --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/focaltech_test.h @@ -0,0 +1,730 @@ +/************************************************************************ +* Copyright (c) 2012-2020, FocalTech Systems, Ltd., all rights reserved. +* +* File Name: focaltech_test.h +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-01 +* +* Abstract: test entry for all IC +* +************************************************************************/ +#ifndef _TEST_LIB_H +#define _TEST_LIB_H + +/***************************************************************************** +* Included header files +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include //iic +#include //msleep +#include +#include +#include +#include +#include "../focaltech_core.h" +#include "focaltech_test_ini.h" + +/***************************************************************************** +* Macro definitions using #define +*****************************************************************************/ +#define FTS_INI_FILE_PATH "/vendor/firmware/" +#define FTS_OUT_FILE_PATH "/data/vendor/touchrec/" +#define FTS_CSV_FILE_NAME "testdata.csv" +#define FTS_TXT_FILE_NAME "testresult.txt" +#define FTS_LIMIT_FILE_NAME "csot_focal_test_limits.ini" +#define FTS_TEST_RESULT_PASS "PASS" +#define FTS_TEST_RESULT_FAIL "FAIL" +#define false 0 +#define true 1 +#define TEST_ICSERIES_LEN (8) +#define TEST_ICSERIES(x) ((x) >> TEST_ICSERIES_LEN) + +#define TEST_OPEN_MAX_VALUE (255) +#define BYTES_PER_TIME (32) /* max:128 */ +/* CSV & TXT */ +#define CSV_LINE2_BUFFER_LEN (1024) +#define CSV_BUFFER_LEN (1024*80*5) +#define TXT_BUFFER_LEN (1024*80*5) + +#define TEST_SAVE_FAIL_RESULT 1 + +/*----------------------------------------------------------- +Test Status +-----------------------------------------------------------*/ +#define RESULT_NULL 0 +#define RESULT_PASS 1 +#define RESULT_NG 2 + +#define TX_NUM_MAX 60 +#define RX_NUM_MAX 100 +#define SC_NUM_MAX ((TX_NUM_MAX) + (RX_NUM_MAX)) +#define NUM_MAX_SC (144) +#define KEY_NUM_MAX 6 +#define TEST_ITEM_COUNT_MAX 32 +#define TEST_ITEM_NAME_MAX 32 +#define TEST_SHORT_RES_MAX 0xFFFF + +/* + * factory test registers + */ +#define ENTER_WORK_FACTORY_RETRIES 5 + +#define START_SCAN_RETRIES_INCELL 20 +#define START_SCAN_RETRIES_DELAY_INCELL 16 +#define FACTORY_TEST_RETRY 50 +#define FACTORY_TEST_DELAY 18 +#define FACTORY_TEST_RETRY_DELAY 100 + +#define DEVIDE_MODE_ADDR 0x00 +#define REG_FW_VERSION 0xA6 +#define REG_VA_TOUCH_THR 0x80 +#define REG_VKEY_TOUCH_THR 0x82 + +#define FACTORY_REG_LINE_ADDR 0x01 +#define FACTORY_REG_CHX_NUM 0x02 +#define FACTORY_REG_CHY_NUM 0x03 +#define FACTORY_REG_CLB 0x04 +#define FACTORY_REG_DATA_SELECT 0x06 +#define FACTORY_REG_RAWBUF_SELECT 0x09 +#define FACTORY_REG_KEY_CBWIDTH 0x0B +#define FACTORY_REG_PARAM_UPDATE_STATE 0x0E +#define FACTORY_REG_PARAM_UPDATE_STATE_TOUCH 0xB5 +#define FACTORY_REG_SHORT_TEST_EN 0x0F +#define FACTORY_REG_SHORT_TEST_STATE 0x10 +#define FACTORY_REG_LCD_NOISE_START 0x11 +#define FACTORY_REG_LCD_NOISE_FRAME 0x12 +#define FACTORY_REG_LCD_NOISE_TEST_STATE 0x13 +#define FACTORY_REG_LCD_NOISE_TTHR 0x14 +#define FACTORY_REG_OPEN_START 0x15 +#define FACTORY_REG_OPEN_STATE 0x16 +#define FACTORY_REG_OPEN_ADDR 0xCF +#define FACTORY_REG_OPEN_IDLE 0x03 +#define FACTORY_REG_OPEN_BUSY 0x01 +#define FACTORY_REG_CB_ADDR_H 0x18 +#define FACTORY_REG_CB_ADDR_L 0x19 +#define FACTORY_REG_ORDER_ADDR_H 0x1A +#define FACTORY_REG_ORDER_ADDR_L 0x1B +#define FACTORY_REG_LCD_NOISE_STATE 0x1E +#define FACTORY_REG_KEYSHORT_EN 0x2E +#define FACTORY_REG_KEYSHORT_STATE 0x2F + +#define FACTORY_REG_LEFT_KEY 0x1E +#define FACTORY_REG_RIGHT_KEY 0x1F +#define FACTORY_REG_OPEN_REG20 0x20 +#define FACTORY_REG_OPEN_REG21 0x21 +#define FACTORY_REG_OPEN_REG22 0x22 +#define FACTORY_REG_OPEN_REG23 0x23 +#define FACTORY_REG_OPEN_REG2E 0x2E +#define FACTORY_REG_OPEN_REG86 0x86 +#define FACTORY_REG_K1 0x31 +#define FACTORY_REG_K2 0x32 +#define FACTORY_REG_RAWDATA_ADDR 0x6A +#define FACTORY_REG_ORDER_ADDR 0x6C +#define FACTORY_REG_CB_ADDR 0x6E +#define FACTORY_REG_SHORT_ADDR 0x89 +#define FACTORY_REG_RAWDATA_TEST_EN 0x9E +#define FACTORY_REG_CB_TEST_EN 0x9F +#define FACTORY_REG_OPEN_TEST_EN 0xA0 +#define FACTORY_REG_RAWDATA_TARGET 0xCA + + +/* mc_sc */ +#define FACTORY_REG_FRE_LIST 0x0A +#define FACTORY_REG_DATA_TYPE 0x5B +#define FACTORY_REG_NORMALIZE 0x16 +#define FACTORY_REG_RAWDATA_ADDR_MC_SC 0x36 +#define FACTORY_REG_PATTERN 0x53 +#define FACTORY_REG_NOMAPPING 0x54 +#define FACTORY_REG_CHX_NUM_NOMAP 0x55 +#define FACTORY_REG_CHY_NUM_NOMAP 0x56 +#define FACTORY_REG_WC_SEL 0x09 +#define FACTORY_REG_HC_SEL 0x0F +#define FACTORY_REG_MC_SC_MODE 0x44 +#define FACTORY_REG_MC_SC_CB_ADDR_OFF 0x45 +#define FACTORY_REG_MC_SC_CB_H_ADDR_OFF 0x49 +#define FACTORY_REG_MC_SC_CB_ADDR 0x4E +#define FACTROY_REG_SHORT_TEST_EN 0x07 +#define FACTROY_REG_SHORT_CA 0x01 +#define FACTROY_REG_SHORT_CC 0x02 +#define FACTROY_REG_SHORT_CG 0x03 +#define FACTROY_REG_SHORT_OFFSET 0x04 +#define FACTROY_REG_SHORT_AB_CH 0x58 +#define FACTROY_REG_SHORT_RES_LEVEL 0x5A +#define FACTORY_REG_GCB 0xBD +#define FACTORY_REG_SHORT_ADDR_MC 0xF4 +#define FACTORY_REG_FIR 0xFB + +#define FACTROY_REG_SCAP_CFG 0x58 +#define FACTROY_REG_SCAP_GCB_TX 0xBC +#define FACTROY_REG_SCAP_GCB_RX 0xBE +#define FACTROY_REG_CB_BUF_SEL 0xBF + +#define FACTROY_REG_SHORT2_TEST_EN 0xC0 +#define FACTROY_REG_SHORT2_CA 0x01 +#define FACTROY_REG_SHORT2_CC 0x02 +#define FACTROY_REG_SHORT2_CG 0x03 +#define FACTROY_REG_SHORT2_OFFSET 0x04 +#define FACTROY_REG_SHORT2_RES_LEVEL 0xC1 +#define FACTROY_REG_SHORT2_DEALY 0xC2 +#define FACTROY_REG_SHORT2_TEST_STATE 0xC3 +#define FACTORY_REG_SHORT2_ADDR_MC 0xC4 +#define FACTROY_REG_SHORT2_AB_CH 0xC6 + +/* sc */ +#define FACTORY_REG_SCAN_ADDR2 0x08 +#define FACTORY_REG_CH_NUM_SC 0x0A +#define FACTORY_REG_KEY_NUM_SC 0x0B +#define FACTORY_REG_SC_CB_ADDR_OFF 0x33 +#define FACTORY_REG_SC_CB_ADDR 0x39 +#define FACTORY_REG_RAWDATA_SADDR_SC 0x34 +#define FACTORY_REG_RAWDATA_ADDR_SC 0x35 +#define FACTORY_REG_CB_SEL 0x41 +#define FACTORY_REG_FMODE 0xAE + +#define TEST_RETVAL_00 0x00 +#define TEST_RETVAL_AA 0xAA + +#define FTS_MAX_SORT_SC 32768 +#define FTS_MIN_SORT_SC 0 + +#ifdef CONFIG_FTS_COMPATIBLE_WITH_GKI +#define FTS_PROC_TP_DIFFER "fts_tp_test" +#define PROC_READ_CSV_DATA 1 +#define PROC_READ_TXT_DATA 2 +#define PROC_BUF_SIZE 256 +#endif + +/***************************************************************************** +* enumerations, structures and unions +*****************************************************************************/ +struct item_info { + char name[TEST_ITEM_NAME_MAX]; + int code; + int *data; + int datalen; + int result; + int mc_sc; + int key_support; +}; + +struct fts_test_data { + int item_count; + struct item_info info[TEST_ITEM_COUNT_MAX]; +}; + +/* incell */ +struct incell_testitem { + u32 short_test : 1; + u32 open_test : 1; + u32 cb_test : 1; + u32 rawdata_test : 1; + u32 lcdnoise_test : 1; + u32 keyshort_test : 1; + u32 mux_open_test : 1; +}; + +struct incell_threshold_b { + int short_res_min; + int short_res_vk_min; + int open_cb_min; + int open_k1_check; + int open_k1_value; + int open_k2_check; + int open_k2_value; + int cb_min; + int cb_max; + int cb_vkey_check; + int cb_min_vk; + int cb_max_vk; + int rawdata_min; + int rawdata_max; + int rawdata_vkey_check; + int rawdata_min_vk; + int rawdata_max_vk; + int lcdnoise_frame; + int lcdnoise_coefficient; + int lcdnoise_coefficient_vkey; + int open_diff_min; + int open_diff_max; + int open_nmos; + int keyshort_k1; + int keyshort_cb_max; + int rawdata2_min; + int rawdata2_max; + int mux_open_cb_min; + int open_delta_V; +}; + +struct incell_threshold { + struct incell_threshold_b basic; + int *rawdata_min; + int *rawdata_max; + int *rawdata2_min; + int *rawdata2_max; + int *cb_min; + int *cb_max; +}; + +struct incell_test { + struct incell_threshold thr; + union { + int tmp; + struct incell_testitem item; + } u; +}; + +/* mc_sc */ +enum mapping_type { + MAPPING = 0, + NO_MAPPING = 1, +}; + +struct mc_sc_testitem { + u32 rawdata_test : 1; + u32 rawdata_uniformity_test : 1; + u32 scap_cb_test : 1; + u32 scap_rawdata_test : 1; + u32 short_test : 1; + u32 panel_differ_test : 1; + u32 noise_test : 1; + u32 mcap_cmb_test : 1; +}; + +struct mc_sc_threshold_b { + int rawdata_h_min; + int rawdata_h_max; + int rawdata_set_hfreq; + int rawdata_l_min; + int rawdata_l_max; + int rawdata_set_lfreq; + int uniformity_check_tx; + int uniformity_check_rx; + int uniformity_check_min_max; + int uniformity_tx_hole; + int uniformity_rx_hole; + int uniformity_min_max_hole; + int scap_cb_off_min; + int scap_cb_off_max; + int scap_cb_wp_off_check; + int scap_cb_on_min; + int scap_cb_on_max; + int scap_cb_wp_on_check; + int scap_rawdata_off_min; + int scap_rawdata_off_max; + int scap_rawdata_wp_off_check; + int scap_rawdata_on_min; + int scap_rawdata_on_max; + int scap_rawdata_wp_on_check; + int short_cg; + int short_cc; + int panel_differ_min; + int panel_differ_max; + int scap_cb_hi_min; + int scap_cb_hi_max; + int scap_cb_hi_check; + int scap_rawdata_hi_min; + int scap_rawdata_hi_max; + int scap_rawdata_hi_check; + int scap_cb_hov_min; + int scap_cb_hov_max; + int scap_cb_hov_check; + int scap_rawdata_hov_min; + int scap_rawdata_hov_max; + int scap_rawdata_hov_check; + + int scap_cb_on_gcb_min; + int scap_cb_on_gcb_max; + int scap_cb_off_gcb_min; + int scap_cb_off_gcb_max; + int scap_cb_hi_gcb_min; + int scap_cb_hi_gcb_max; + int scap_cb_on_cf_min; + int scap_cb_on_cf_max; + int scap_cb_off_cf_min; + int scap_cb_off_cf_max; + int scap_cb_hi_cf_min; + int scap_cb_hi_cf_max; + + int mcap_cmb_min; + int mcap_cmb_max; + + int noise_max; + int noise_framenum; + int noise_mode; + int noise_polling; +}; + +struct mc_sc_threshold { + struct mc_sc_threshold_b basic; + int *rawdata_h_min; + int *rawdata_h_max; + int *rawdata_l_min; + int *rawdata_l_max; + int *tx_linearity_max; + int *tx_linearity_min; + int *rx_linearity_max; + int *rx_linearity_min; + int *scap_cb_off_min; + int *scap_cb_off_max; + int *scap_cb_on_min; + int *scap_cb_on_max; + int *scap_cb_hi_min; + int *scap_cb_hi_max; + int *scap_cb_hov_min; + int *scap_cb_hov_max; + int *scap_rawdata_off_min; + int *scap_rawdata_off_max; + int *scap_rawdata_on_min; + int *scap_rawdata_on_max; + int *scap_rawdata_hi_min; + int *scap_rawdata_hi_max; + int *scap_rawdata_hov_min; + int *scap_rawdata_hov_max; + int *panel_differ_min; + int *panel_differ_max; +#if defined(CONFIG_FTS_NOISE_TEST_P2P) + int *noise_min; + int *noise_max; +#endif + + int *scap_cb_on_cf_min; + int *scap_cb_on_cf_max; + int *scap_cb_off_cf_min; + int *scap_cb_off_cf_max; + int *scap_cb_hi_cf_min; + int *scap_cb_hi_cf_max; + + int *mcap_cmb_min; + int *mcap_cmb_max; +}; + +struct mc_sc_test { + struct mc_sc_threshold thr; + union { + u32 tmp; + struct mc_sc_testitem item; + } u; +}; + +/* sc */ +struct sc_testitem { + u32 rawdata_test : 1; + u32 cb_test : 1; + u32 delta_cb_test : 1; + u32 short_test : 1; +}; + +struct sc_threshold_b { + int rawdata_min; + int rawdata_max; + int cb_min; + int cb_max; + int dcb_base; + int dcb_differ_max; + int dcb_key_check; + int dcb_key_differ_max; + int dcb_ds1; + int dcb_ds2; + int dcb_ds3; + int dcb_ds4; + int dcb_ds5; + int dcb_ds6; + int dcb_critical_check; + int dcb_cs1; + int dcb_cs2; + int dcb_cs3; + int dcb_cs4; + int dcb_cs5; + int dcb_cs6; + int short_min; +}; + +struct sc_threshold { + struct sc_threshold_b basic; + int *rawdata_min; + int *rawdata_max; + int *cb_min; + int *cb_max; + int *dcb_sort; + int *dcb_base; +}; + +struct sc_test { + struct sc_threshold thr; + union { + u32 tmp; + struct sc_testitem item; + } u; +}; + +enum test_hw_type { + IC_HW_INCELL = 1, + IC_HW_MC_SC, + IC_HW_SC, +}; + +enum test_scan_mode { + SCAN_NORMAL = 0, + SCAN_SC, +}; + +struct fts_test_node { + int channel_num; + int tx_num; + int rx_num; + int node_num; + int key_num; +}; + +struct fts_test { + struct fts_ts_data *ts_data; + struct fts_test_node node; + struct fts_test_node sc_node; + u8 fw_ver; + u8 va_touch_thr; + u8 vk_touch_thr; + bool key_support; + bool v3_pattern; + u8 mapping; + u8 normalize; + u8 fre_num; + int test_num; +#ifdef CONFIG_FTS_COMPATIBLE_WITH_GKI + char *csv_buffer; + int csv_result_len; + char *txt_buffer; + int txt_result_len; +#endif + int *item1_data; + int *item2_data; + int *item3_data; + int *item4_data; + int *item5_data; + int *item6_data; + int *buffer; + int buffer_length; + int *node_valid; + int *node_valid_sc; + int basic_thr_count; + int csv_item_cnt; + int csv_item_scb; + int csv_item_sraw; + int code1; + int code2; + int offset; + int null_noise_max; + union { + struct incell_test incell; + struct mc_sc_test mc_sc; + struct sc_test sc; + } ic; + + struct test_funcs *func; + struct fts_test_data testdata; + char *testresult; + int testresult_len; + int result; +#if defined(TEST_SAVE_FAIL_RESULT) && TEST_SAVE_FAIL_RESULT +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + struct timespec64 tv; +#else + struct timeval tv; +#endif +#endif + struct ini_data ini; +}; + +struct test_funcs { + u16 ctype[FTS_MAX_COMPATIBLE_TYPE]; + enum test_hw_type hwtype; + int startscan_mode; + int key_num_total; + bool rawdata2_support; + bool force_touch; + bool mc_sc_short_v2; + bool raw_u16; + bool cb_high_support; + bool param_update_support; + int (*param_init)(void); + int (*init)(void); + int (*start_test)(void); + void (*save_data_private)(char *buf, int *len); +}; + +enum byte_mode { + DATA_ONE_BYTE, + DATA_TWO_BYTE, +}; +/* mc_sc */ +enum normalize_type { + NORMALIZE_OVERALL, + NORMALIZE_AUTO, +}; + +enum wp_type { + WATER_PROOF_OFF = 0, + WATER_PROOF_ON = 1, + HIGH_SENSITIVITY = 2, + HOV = 3, + WATER_PROOF_ON_TX = 100, + WATER_PROOF_ON_RX, + WATER_PROOF_OFF_TX, + WATER_PROOF_OFF_RX, +}; +/* mc end */ + +/* sc */ +enum factory_mode { + FACTORY_NORMAL, + FACTORY_TESTMODE_1, + FACTORY_TESTMODE_2, +}; + +enum dcb_sort_num { + DCB_SORT_MIN = 1, + DCB_SORT_MAX = 6, +}; + +struct dcb_sort_d { + int ch_num; + int deviation; + int critical; + int min; + int max; +}; +/* sc end */ + +enum csv_itemcode_incell { + CODE_ENTER_FACTORY_MODE = 0, + CODE_RAWDATA_TEST = 7, + CODE_CB_TEST = 12, + CODE_SHORT_TEST = 15, + CODE_OPEN_TEST = 25, + CODE_LCD_NOISE_TEST = 27, + CODE_MUX_OPEN_TEST = 41, +}; + +enum csv_itemcode_mc_sc { + CODE_M_RAWDATA_TEST = 7, + CODE_M_SCAP_CB_TEST = 9, + CODE_M_SCAP_RAWDATA_TEST = 10, + CODE_M_WEAK_SHORT_CIRCUIT_TEST = 15, + CODE_M_RAWDATA_UNIFORMITY_TEST = 16, + CODE_M_PANELDIFFER_TEST = 20, + CODE_M_NOISE_TEST = 14, + CODE_M_CMB_TEST = 39, +}; + +enum csv_itemcode_sc { + CODE_S_RAWDATA_TEST = 7, + CODE_S_CB_TEST = 13, + CODE_S_DCB_TEST = 14, +}; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +extern struct test_funcs test_func_ft5662; + +extern struct fts_test *fts_ftest; + +void sys_delay(int ms); +int fts_abs(int value); +void print_buffer(int *buffer, int length, int line_num); +int fts_test_read_reg(u8 addr, u8 *val); +int fts_test_write_reg(u8 addr, u8 val); +int fts_test_read(u8 addr, u8 *readbuf, int readlen); +int fts_test_read_one(u8 addr, u8 *readbuf, int readlen); +int fts_test_write(u8 addr, u8 *writebuf, int writelen); +int enter_work_mode(void); +int enter_factory_mode(void); +int read_mass_data(u8 addr, int byte_num, int *buf); +int chip_clb(void); +int wait_state_update(u8 retval); +int get_cb_incell(u16 saddr, int byte_num, int *cb_buf); +int short_get_adcdata_incell(u8 retval, u8 ch_num, int byte_num, int *adc_buf); +int start_scan(void); +int get_rawdata(int *data); +int get_cb_sc(int byte_num, int *cb_buf, enum byte_mode mode); +bool compare_data(int *data, int min, int max, int min_vk, int max_vk, bool key); +bool compare_array(int *data, int *min, int *max, bool key); +void show_data(int *data, bool key); +/* mc_sc */ +int mapping_switch(u8 mapping); +bool get_fw_wp(u8 wp_channel_select, enum wp_type water_proof_type); +int get_cb_mc_sc(u8 wp, int byte_num, int *cb_buf, enum byte_mode mode); +int get_rawdata_mc_sc(enum wp_type wp, int *data); +int get_rawdata_mc(u8 fre, u8 fir, int *rawdata); +int short_get_adc_data_mc(u8 retval, int byte_num, int *adc_buf, u8 mode); +bool compare_mc_sc(bool, bool, int *, int *, int *); +void show_data_mc_sc(int *data); +void *fts_malloc(size_t size); +void fts_free_proc(void *p); +void fts_test_save_data(char *name, int code, int *data, int datacnt, + bool mc_sc, bool key, bool result); + +#define fts_malloc_r(p, size) do {\ + if (NULL == p) {\ + p = fts_malloc(size);\ + if (NULL == p) {\ + return -ENOMEM;\ + }\ + }\ +} while(0) + +#define fts_free(p) do {\ + if (p) {\ + fts_free_proc(p);\ + p = NULL;\ + }\ +} while(0) + +#define CSV_SUPPORT 1 +#define TXT_SUPPORT 1 + +#define FTS_TEST_DBG(fmt, args...) do { \ +printk("[FTS_TS][TEST]%s:"fmt"\n", __func__, ##args); \ +} while (0) + +#define FTS_TEST_FUNC_ENTER() do { \ + printk("[FTS_TS][TEST]%s: Enter\n", __func__); \ +} while (0) + +#define FTS_TEST_FUNC_EXIT() do { \ + printk("[FTS_TS][TEST]%s: Exit(%d)\n", __func__, __LINE__); \ +} while (0) + +#define FTS_TEST_INFO(fmt, args...) do { \ + printk(KERN_ERR "[FTS_TS/I][TEST]%s:"fmt"\n", __func__, ##args); \ +} while (0) + +#define FTS_TEST_ERROR(fmt, args...) do { \ + printk(KERN_ERR "[FTS_TS/E][TEST]%s:"fmt"\n", __func__, ##args); \ +} while (0) + +#define FTS_TEST_SAVE_INFO(fmt, args...) do { \ + if (fts_ftest->testresult) { \ + fts_ftest->testresult_len += snprintf( \ + fts_ftest->testresult + fts_ftest->testresult_len, \ + TXT_BUFFER_LEN, \ + fmt, ##args);\ + } \ +} while (0) + +#define FTS_TEST_SAVE_ERR(fmt, args...) do { \ + if (fts_ftest->testresult && (fts_ftest->testresult_len < TXT_BUFFER_LEN)) { \ + fts_ftest->testresult_len += snprintf( \ + fts_ftest->testresult + fts_ftest->testresult_len, \ + TXT_BUFFER_LEN, \ + fmt, ##args);\ + } \ + printk(KERN_ERR "[FTS_TS/E][TEST]%s:"fmt"\n", __func__, ##args);\ +} while (0) +#endif diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/focaltech_test_ini.c b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/focaltech_test_ini.c new file mode 100644 index 000000000000..e48b5cde420b --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/focaltech_test_ini.c @@ -0,0 +1,1447 @@ +/************************************************************************ +* Copyright (c) 2012-2020, Focaltech Systems (R)£¬All Rights Reserved. +* +* File Name: focaltech_test_ini.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-01 +* +* Abstract: parsing function of INI file +* +************************************************************************/ +#include "focaltech_test.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_INI_REQUEST_SUPPORT 1 + +struct ini_ic_type ic_types[] = { + {"FT5X46", 0x54000002}, + {"FT5X46i", 0x54010002}, + {"FT5526", 0x54020002}, + {"FT3X17", 0x54030002}, + {"FT5436", 0x54040002}, + {"FT3X27", 0x54050002}, + {"FT5526i", 0x54060002}, + {"FT5416", 0x54070002}, + {"FT5426", 0x54080002}, + {"FT5435", 0x54090002}, + {"FT7681", 0x540A0002}, + {"FT7661", 0x540B0002}, + {"FT7511", 0x540C0002}, + {"FT7421", 0x540D0002}, + {"FT7311", 0x54100002}, + + {"FT5526_003", 0x40020082}, + {"FT5426_003", 0x40030082}, + {"FT3427G_003", 0x40040082}, + {"FT3427_003", 0x40050082}, + {"FT5446_003", 0x40000082}, + {"FT5446_Q03", 0x40000082}, + {"FT5446_P03", 0x55060081}, + {"FT5446DQS-W01", 0x40000082}, + + {"FT5452", 0x55000081}, + {"FT3518", 0x55010081}, + {"FT3558", 0x55020081}, + {"FT3528", 0x55030081}, + {"FT5536", 0x55040081}, + {"FT3418", 0x55070081}, + {"FT5536L", 0x55080081}, + + {"FT5472", 0x8F000083}, + {"FT5446U", 0x8F010083}, + {"FT5456U", 0x8F020083}, + {"FT3417U", 0x8F030083}, + {"FT5426U", 0x8F040083}, + {"FT3428", 0x8F050083}, + {"FT3437U", 0x8F060083}, + + {"FT5822", 0x58000001}, + {"FT5626", 0x58010001}, + {"FT5726", 0x58020001}, + {"FT5826B", 0x58030001}, + {"FT3617", 0x58040001}, + {"FT3717", 0x58050001}, + {"FT7811", 0x58060001}, + {"FT5826S", 0x58070001}, + {"FT3517U", 0x58090001}, + {"FT3557", 0x580A0001}, + + {"FT6X36", 0x63000003}, + {"FT3X07", 0x63010003}, + {"FT6416", 0x63020003}, + {"FT6336G/U", 0x63030003}, + {"FT7401", 0x63040003}, + {"FT3407U", 0x63050003}, + {"FT6236U", 0x63060003}, + {"FT6436U", 0x63070003}, + + {"FT3267", 0x63080004}, + {"FT3367", 0x63090004}, + + {"FT6216", 0x64000084}, + {"FT7302", 0x64010084}, + {"FT7202", 0x64020084}, + {"FT3308", 0x64030084}, + {"FT6446", 0x64040084}, + + {"FT8607", 0x81000009}, + {"FT8716", 0x82000005}, + {"FT8716U", 0x44000005}, + {"FT8716F", 0x8A000005}, + {"FT8613", 0x4500000C}, + + {"FT8736", 0x83000006}, + + {"FT8201", 0x87010010}, + {"FT7250", 0x8702001A}, + + {"FT8006U", 0x8900000B}, + {"FT8006S", 0x8901000B}, + {"FT8006S-AA", 0x9B000019}, + {"FT8016", 0x9B01001D}, + + {"FT8719", 0x8E00000D}, + {"FT8615", 0x9100000F}, + + {"FT8739", 0x8D00000E}, + + {"FT8006P", 0x93000011}, + {"FT7120", 0x9E00001B}, + + {"FT8613S", 0x94000014}, + + {"FT8756", 0x95000015}, + {"FT8656", 0x95010018}, + + {"FT8302", 0x97000016}, + + {"FT8009", 0x98000017}, + + {"FT8720", 0x9C00001C}, + + {"FT3068", 0x65010085}, + {"FT3168", 0x65020085}, + {"FT3067", 0x65030085}, + {"FT3268", 0x65040085}, + {"FT6346U", 0x65050085}, + {"FT6146", 0x65060085}, + {"FT6346G", 0x65070085}, + + {"FT5726_V03", 0x580C0086}, + {"FT5726_003", 0x580C0086}, + + {"FT3618", 0x59010087}, + {"FT5646", 0x59020087}, + {"FT3A58", 0x59030087}, + {"FT3B58", 0x59040087}, + {"FT3D58", 0x59050087}, + {"FT5A36", 0x59060087}, + {"FT5B36", 0x59070087}, + {"FT5D36", 0x59080087}, + {"FT5A46", 0x59090087}, + {"FT5B46", 0x590A0087}, + {"FT5D46", 0x590B0087}, + {"FT5936", 0x590C0087}, + {"FT5946", 0x590D0087}, + + {"FT3658U", 0x5A010088}, + + {"FT2388", 0x9D00001E}, +}; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +/* Works only for digits and letters, but small and fast */ +#define TOLOWER(x) ((x) | 0x20) +static int fts_strncmp(const char *cs, const char *ct, int count) +{ + u8 c1 = 0, c2 = 0; + + while (count) { + if ((*cs == '\0') || (*ct == '\0')) + return -1; + c1 = TOLOWER(*cs++); + c2 = TOLOWER(*ct++); + if (c1 != c2) + return c1 < c2 ? -1 : 1; + if (!c1) + break; + count--; + } + + return 0; +} + +static int fts_isspace(int x) +{ + if (x == ' ' || x == '\t' || x == '\n' || x == '\f' || x == '\b' || x == '\r') + return 1; + else + return 0; +} + +static int fts_isdigit(int x) +{ + if (x <= '9' && x >= '0') + return 1; + else + return 0; +} + +static long fts_atol(char *nptr) +{ + int c; /* current char */ + long total; /* current total */ + int sign; /* if ''-'', then negative, otherwise positive */ + /* skip whitespace */ + while ( fts_isspace((int)(unsigned char)*nptr) ) + ++nptr; + c = (int)(unsigned char) * nptr++; + sign = c; /* save sign indication */ + if (c == '-' || c == '+') + c = (int)(unsigned char) * nptr++; /* skip sign */ + total = 0; + while (fts_isdigit(c)) { + total = 10 * total + (c - '0'); /* accumulate digit */ + c = (int)(unsigned char) * nptr++; /* get next char */ + } + if (sign == '-') + return -total; + else + return total; /* return result, negated if necessary */ +} + +static int fts_atoi(char *nptr) +{ + return (int)fts_atol(nptr); +} + +static int fts_test_get_ini_size(char *config_name) +{ + struct file *pfile = NULL; + struct inode *inode = NULL; + off_t fsize = 0; + char filepath[FILE_NAME_LENGTH] = { 0 }; + + FTS_TEST_FUNC_ENTER(); + + memset(filepath, 0, sizeof(filepath)); + snprintf(filepath, FILE_NAME_LENGTH, "%s%s", + FTS_INI_FILE_PATH, config_name); + + if (NULL == pfile) + pfile = filp_open(filepath, O_RDONLY, 0); + if (IS_ERR(pfile)) { + FTS_TEST_ERROR("error occured while opening file %s.", filepath); + return -EIO; + } + +#if 1 + inode = pfile->f_inode; +#else + /* reserved for linux earlier verion */ + inode = pfile->f_dentry->d_inode; +#endif + fsize = inode->i_size; + filp_close(pfile, NULL); + + FTS_TEST_FUNC_ENTER(); + + return fsize; +} + +static int fts_test_read_ini_data(char *config_name, char *config_buf) +{ + struct file *pfile = NULL; + struct inode *inode = NULL; + off_t fsize = 0; + char filepath[FILE_NAME_LENGTH] = { 0 }; + loff_t pos = 0; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + mm_segment_t old_fs; +#endif + + FTS_TEST_FUNC_ENTER(); + + memset(filepath, 0, sizeof(filepath)); + snprintf(filepath, FILE_NAME_LENGTH, "%s%s", + FTS_INI_FILE_PATH, config_name); + if (NULL == pfile) { + pfile = filp_open(filepath, O_RDONLY, 0); + } + if (IS_ERR(pfile)) { + FTS_TEST_ERROR("error occured while opening file %s.", filepath); + return -EIO; + } + +#if 1 + inode = pfile->f_inode; +#else + /* reserved for linux earlier verion */ + inode = pfile->f_dentry->d_inode; +#endif + fsize = inode->i_size; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + old_fs = get_fs(); + set_fs(KERNEL_DS); +#endif + pos = 0; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + vfs_read(pfile, config_buf, fsize, &pos); +#else + kernel_read(pfile, config_buf, fsize, &pos); +#endif + filp_close(pfile, NULL); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + set_fs(old_fs); +#endif + + FTS_TEST_FUNC_EXIT(); + return 0; +} + +static int __maybe_unused fts_test_get_ini_default(struct ini_data *ini, char *fwname) +{ + int ret = 0; + int inisize = 0; + + inisize = fts_test_get_ini_size(fwname); + FTS_TEST_DBG("ini file size:%d", inisize); + if (inisize <= 0) { + FTS_TEST_ERROR("get ini file size fail"); + return -ENODATA; + } + + ini->data = vmalloc(inisize + 1); + if (NULL == ini->data) { + FTS_TEST_ERROR("malloc memory for ini data fail"); + return -ENOMEM; + } + memset(ini->data, 0, inisize + 1); + ini->length = inisize + 1; + + ret = fts_test_read_ini_data(fwname, ini->data); + if (ret) { + FTS_TEST_ERROR("read ini file fail"); + return -ENODATA; + } + ini->data[inisize] = '\n'; /* last line is null line */ + + return 0; +} + +static int fts_test_get_ini_via_request_firmware(struct ini_data *ini, char *fwname) +{ + int ret = 0; + const struct firmware *fw = NULL; + struct device *dev = &fts_data->input_dev->dev; + +#if !FTS_INI_REQUEST_SUPPORT + return -EINVAL; +#endif + + ret = request_firmware(&fw, fwname, dev); + if (0 == ret) { + FTS_TEST_INFO("firmware request(%s) success", fwname); + ini->data = vmalloc(fw->size + 1); + if (NULL == ini->data) { + FTS_TEST_ERROR("ini->data buffer vmalloc fail"); + ret = -ENOMEM; + } else { + memcpy(ini->data, fw->data, fw->size); + ini->data[fw->size] = '\n'; + ini->length = fw->size + 1; + } + } else { + FTS_TEST_INFO("firmware request(%s) fail,ret=%d", fwname, ret); + } + + if (fw != NULL) { + release_firmware(fw); + fw = NULL; + } + + return ret; +} + + +static void str_space_remove(char *str) +{ + char *t = str; + char *s = str; + + while (*t != '\0') { + if (*t != ' ') { + *s = *t; + s++; + } + t++; + } + + *s = '\0'; +} + +static void print_ini_data(struct ini_data *ini) +{ + int i = 0; + int j = 0; + struct ini_section *section = NULL; + struct ini_keyword *keyword = NULL; + struct fts_test *tdata = fts_ftest; + + if (tdata && tdata->ts_data && (tdata->ts_data->log_level < 10)) { + return; + } + + if (!ini || !ini->tmp) { + FTS_TEST_DBG("ini is null"); + return; + } + + FTS_TEST_DBG("section num:%d, keyword num total:%d", + ini->section_num, ini->keyword_num_total); + for (i = 0; i < ini->section_num; i++) { + section = &ini->section[i]; + FTS_TEST_DBG("section name:[%s] keyword num:%d", + section->name, section->keyword_num); + for (j = 0; j < section->keyword_num; j++) { + keyword = §ion->keyword[j]; + FTS_TEST_DBG("%s=%s", keyword->name, keyword->value); + } + } +} + +static int ini_get_line(char *filedata, char *line_data, int *line_len) +{ + int i = 0; + int line_length = 0; + int type; + + /* get a line data */ + for (i = 0; i < MAX_INI_LINE_LEN; i++) { + if (('\n' == filedata[i]) || ('\r' == filedata[i])) { + line_data[line_length++] = '\0'; + if (('\n' == filedata[i + 1]) || ('\r' == filedata[i + 1])) { + line_length++; + } + break; + } else { + line_data[line_length++] = filedata[i]; + } + } + + if (i >= MAX_INI_LINE_LEN) { + FTS_TEST_ERROR("line length(%d)>max(%d)", line_length, MAX_INI_LINE_LEN); + return -ENODATA; + } + + /* remove space */ + str_space_remove(line_data); + + /* confirm line type */ + if (('\0' == line_data[0]) || ('#' == line_data[0])) { + type = LINE_OTHER; + } else if ('[' == line_data[0]) { + type = LINE_SECTION; + } else { + type = LINE_KEYWORD; /* key word */ + } + + *line_len = line_length; + return type; +} + +static int ini_parse_keyword(struct ini_data *ini, char *line_buffer) +{ + int i = 0; + int offset = 0; + int length = strlen(line_buffer); + struct ini_section *section = NULL; + + for (i = 0; i < length; i++) { + if (line_buffer[i] == '=') + break; + } + + if ((i == 0) || (i >= length)) { + FTS_TEST_ERROR("mark(=)in keyword line fail"); + return -ENODATA; + } + + if ((ini->section_num > 0) && (ini->section_num < MAX_INI_SECTION_NUM)) { + section = &ini->section[ini->section_num - 1]; + } + + if (NULL == section) { + FTS_TEST_ERROR("section is null"); + return -ENODATA; + } + + offset = ini->keyword_num_total; + if (offset > MAX_KEYWORD_NUM) { + FTS_TEST_ERROR("keyword num(%d)>max(%d),please check MAX_KEYWORD_NUM", + ini->keyword_num_total, MAX_KEYWORD_NUM); + return -ENODATA; + } + memcpy(ini->tmp[offset].name, &line_buffer[0], i); + ini->tmp[offset].name[i] = '\0'; + memcpy(ini->tmp[offset].value, &line_buffer[i + 1], length - i - 1); + ini->tmp[offset].value[length - i - 1] = '\0'; + section->keyword_num++; + ini->keyword_num_total++; + + return 0; +} + +static int ini_parse_section(struct ini_data *ini, char *line_buffer) +{ + int length = strlen(line_buffer); + struct ini_section *section = NULL; + + if ((length <= 2) || (length > MAX_KEYWORD_NAME_LEN)) { + FTS_TEST_ERROR("section line length fail"); + return -EINVAL; + } + + if ((ini->section_num < 0) || (ini->section_num >= MAX_INI_SECTION_NUM)) { + FTS_TEST_ERROR("section_num(%d) fail", ini->section_num); + return -EINVAL; + } + section = &ini->section[ini->section_num]; + memcpy(section->name, line_buffer + 1, length - 2); + section->name[length - 2] = '\0'; + FTS_TEST_INFO("section:%s, keyword offset:%d", + section->name, ini->keyword_num_total); + section->keyword = (struct ini_keyword *)&ini->tmp[ini->keyword_num_total]; + section->keyword_num = 0; + ini->section_num++; + if (ini->section_num > MAX_INI_SECTION_NUM) { + FTS_TEST_ERROR("section num(%d)>max(%d), please check MAX_INI_SECTION_NUM", + ini->section_num, MAX_INI_SECTION_NUM); + return -ENOMEM; + } + + return 0; +} + +static int ini_init_inidata(struct ini_data *ini) +{ + int pos = 0; + int ret = 0; + char line_buffer[MAX_INI_LINE_LEN] = { 0 }; + int line_len = 0; + + if (!ini || !ini->data || !ini->tmp) { + FTS_TEST_DBG("ini/data/tmp is null"); + return -EINVAL; + } + + while (pos < ini->length) { + ret = ini_get_line(ini->data + pos, line_buffer, &line_len); + if (ret < 0) { + FTS_TEST_ERROR("ini_get_line fail"); + return ret; + } else if (ret == LINE_KEYWORD) { + ret = ini_parse_keyword(ini, line_buffer); + if (ret < 0) { + FTS_TEST_ERROR("ini_parse_keyword fail"); + return ret; + } + } else if (ret == LINE_SECTION) { + ret = ini_parse_section(ini, line_buffer); + if (ret < 0) { + FTS_TEST_ERROR("ini_parse_section fail"); + return ret; + } + } + + pos += line_len; + } + + print_ini_data(ini); + return 0; +} + +static int ini_get_key(char *section_name, char *key_name, char *value) +{ + int i = 0; + int j = 0; + struct ini_data *ini = &fts_ftest->ini; + struct ini_section *section; + struct ini_keyword *keyword; + int key_len = 0; + int log_level = fts_ftest->ts_data->log_level; + + if (log_level >= 10) { + FTS_TEST_DBG("section name:%s, key name:%s", section_name, key_name); + FTS_TEST_DBG("section num:%d", ini->section_num); + } + + for (i = 0; i < ini->section_num; i++) { + section = &ini->section[i]; + key_len = strlen(section_name); + if (key_len != strlen(section->name)) + continue; + if (fts_strncmp(section->name, section_name, key_len) != 0) + continue; + + if (log_level >= 10) { + FTS_TEST_DBG("section name:%s keyword num:%d", + section->name, section->keyword_num); + } + for (j = 0; j < section->keyword_num; j++) { + keyword = §ion->keyword[j]; + key_len = strlen(key_name); + if (key_len == strlen(keyword->name)) { + if (0 == fts_strncmp(keyword->name, key_name, key_len)) { + key_len = strlen(keyword->value); + memcpy(value, keyword->value, key_len); + if (log_level >= 3) { + FTS_TEST_DBG("section:%s,%s=%s", + section_name, key_name, value); + } + + return key_len; + } + } + } + } + + return -ENODATA; +} + +/* return keyword's value length if success */ +static int ini_get_string_value(char *section_name, char *key_name, char *rval) +{ + if (!section_name || !key_name || !rval) { + FTS_TEST_ERROR("section_name/key_name/rval is null"); + return -EINVAL; + } + + return ini_get_key(section_name, key_name, rval); +} + +int get_keyword_value(char *section, char *name, int *value) +{ + int ret = 0; + char str[MAX_KEYWORD_VALUE_LEN] = { 0 }; + + ret = ini_get_string_value(section, name, str); + if (ret > 0) { + /* search successfully, so change value, otherwise keep default */ + *value = fts_atoi(str); + } + + return ret; +} + +static void fts_init_buffer(int *buffer, int value, int len, bool key_check, int key_value, int key_len) +{ + int i = 0; + int va_len = 0; + + if (NULL == buffer) { + FTS_TEST_ERROR("buffer is null\n"); + return; + } + + va_len = len - key_len; + if (va_len < 0) { + FTS_TEST_ERROR("total len(0x%x) less key len(0x%x)\n", len, key_len); + return; + } + + for (i = 0; i < len; i++) { + buffer[i] = value; + } + + if (key_check) { + for (i = 0; i < key_len; i++) { + buffer[va_len + i] = key_value; + } + } + +} + +static int get_test_item(char name[][MAX_KEYWORD_NAME_LEN], int length, int *val) +{ + int i = 0; + int ret = 0; + int tmpval = 0; + + if (length > TEST_ITEM_COUNT_MAX) { + FTS_TEST_SAVE_ERR("test item count(%d) > max(%d)\n", + length, TEST_ITEM_COUNT_MAX); + return -EINVAL; + } + + FTS_TEST_INFO("test items in total of driver:%d", length); + *val = 0; + for (i = 0; i < length; i++) { + tmpval = 0; + ret = get_value_testitem(name[i], &tmpval); + if (ret < 0) { + FTS_TEST_DBG("test item:%s not found", name[i]); + } else { + FTS_TEST_DBG("test item:%s=%d", name[i], tmpval); + *val |= (tmpval << i); + } + } + + return 0; +} + +static int get_basic_threshold(char name[][MAX_KEYWORD_NAME_LEN], int length, int *val) +{ + int i = 0; + int ret = 0; + struct fts_test *tdata = fts_ftest; + int log_level = tdata->ts_data->log_level; + + FTS_TEST_INFO("basic_thr string length(%d), count(%d)\n", length, tdata->basic_thr_count); + if (length > fts_ftest->basic_thr_count) { + FTS_TEST_SAVE_ERR("basic_thr string length > count\n"); + return -EINVAL; + } + + for (i = 0; i < length; i++) { + ret = get_value_basic(name[i], &val[i]); + if (log_level >= 3) { + if (ret < 0) { + FTS_TEST_DBG("basic thr:%s not found", name[i]); + } else { + FTS_TEST_DBG("basic thr:%s=%d", name[i], val[i]); + } + } + } + + return 0; +} + +static void get_detail_threshold(char *key_name, bool is_prex, int *thr, int node_num) +{ + char str[MAX_KEYWORD_VALUE_LEN] = { 0 }; + char str_temp[MAX_KEYWORD_NAME_LEN] = { 0 }; + char str_tmp[MAX_KEYWORD_VALUE_ONE_LEN] = { 0 }; + struct fts_test *tdata = fts_ftest; + int divider_pos = 0; + int index = 0; + int i = 0; + int j = 0; + int k = 0; + int tx_num = 0; + int rx_num = 0; + int thr_pos = 0; + + if (!key_name || !thr) { + FTS_TEST_ERROR("key_name/thr is null"); + return; + } + + if (is_prex) { + tx_num = tdata->node.tx_num; + rx_num = tdata->node.rx_num; + } + for (i = 0; i < tx_num + 1; i++) { + if (is_prex) { + snprintf(str_temp, MAX_KEYWORD_NAME_LEN, "%s%d", key_name, (i + 1)); + } else { + snprintf(str_temp, MAX_KEYWORD_NAME_LEN, "%s", key_name); + } + divider_pos = ini_get_string_value("SpecialSet", str_temp, str); + if (divider_pos <= 0) + continue; + index = 0; + k = 0; + memset(str_tmp, 0, sizeof(str_tmp)); + for (j = 0; j < divider_pos; j++) { + if (',' == str[j]) { + thr_pos = i * rx_num + k; + if (thr_pos >= node_num) { + FTS_TEST_ERROR("key:%s %d,dthr_num(%d>=%d) fail", + key_name, i, thr_pos, node_num); + break; + } + thr[thr_pos] = (int)(fts_atoi(str_tmp)); + index = 0; + memset(str_tmp, 0x00, sizeof(str_tmp)); + k++; + } else { + if (' ' == str[j]) + continue; + str_tmp[index] = str[j]; + index++; + } + } + } +} + +static int init_node_valid(void) +{ + char str[MAX_KEYWORD_NAME_LEN] = {0}; + int i = 0; + int j = 0; + int chy = 0; + int node_num = 0; + int cnt = 0; + struct fts_test *tdata = fts_ftest; + + if (!tdata || !tdata->node_valid || !tdata->node_valid_sc) { + FTS_TEST_ERROR("tdata/node_valid/node_valid_sc is null"); + return -EINVAL; + } + + chy = tdata->node.rx_num; + node_num = tdata->node.node_num; + fts_init_buffer(tdata->node_valid, 1 , node_num, false, 0, 0); + if ((tdata->func->hwtype == IC_HW_INCELL) || (tdata->func->hwtype == IC_HW_MC_SC)) { + for (cnt = 0; cnt < node_num; cnt++) { + i = cnt / chy + 1; + j = cnt % chy + 1; + snprintf(str, MAX_KEYWORD_NAME_LEN, "InvalidNode[%d][%d]", i, j); + get_keyword_value("INVALID_NODE", str, &tdata->node_valid[cnt]); + } + } + + if (tdata->func->hwtype == IC_HW_MC_SC) { + chy = tdata->sc_node.rx_num; + node_num = tdata->sc_node.node_num; + fts_init_buffer(tdata->node_valid_sc, 1, node_num, false, 0, 0); + + for (cnt = 0; cnt < node_num; cnt++) { + i = (cnt >= chy) ? 2 : 1; + j = (cnt >= chy) ? (cnt - chy + 1) : (cnt + 1); + snprintf(str, MAX_KEYWORD_NAME_LEN, "InvalidNodeS[%d][%d]", i, j); + get_keyword_value("INVALID_NODES", str, &tdata->node_valid_sc[cnt]); + } + } + + print_buffer(tdata->node_valid, tdata->node.node_num, tdata->node.rx_num); + print_buffer(tdata->node_valid_sc, tdata->sc_node.node_num, tdata->sc_node.rx_num); + return 0; +} + +/* incell */ +static int get_test_item_incell(void) +{ + int ret = 0; + char item_name[][MAX_KEYWORD_NAME_LEN] = TEST_ITEM_INCELL; + int length = sizeof(item_name) / MAX_KEYWORD_NAME_LEN; + int item_val = 0; + + ret = get_test_item(item_name, length, &item_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get test item fail\n"); + return ret; + } + + fts_ftest->ic.incell.u.tmp = item_val; + return 0; +} + +static char bthr_name_incell[][MAX_KEYWORD_NAME_LEN] = BASIC_THRESHOLD_INCELL; +static int get_test_threshold_incell(void) +{ + int ret = 0; + int length = sizeof(bthr_name_incell) / MAX_KEYWORD_NAME_LEN; + struct fts_test *tdata = fts_ftest; + struct incell_threshold *thr = &tdata->ic.incell.thr; + int node_num = tdata->node.node_num; + int key_num = tdata->node.key_num; + bool raw_key_check = thr->basic.rawdata_vkey_check; + bool cb_key_check = thr->basic.cb_vkey_check; + + tdata->basic_thr_count = sizeof(struct incell_threshold_b) / sizeof(int); + /* get standard basic threshold */ + ret = get_basic_threshold(bthr_name_incell, length, (int *)&thr->basic); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get basic thr fail\n"); + return ret; + } + + /* basic special set by ic */ + if (tdata->func->param_init) { + ret = tdata->func->param_init(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("special basic thr init fail\n"); + return ret; + } + } + + /* init buffer */ + fts_init_buffer(thr->rawdata_max, thr->basic.rawdata_max, node_num, raw_key_check, thr->basic.rawdata_max_vk, key_num); + fts_init_buffer(thr->rawdata_min, thr->basic.rawdata_min, node_num, raw_key_check, thr->basic.rawdata_min_vk, key_num); + if (tdata->func->rawdata2_support) { + fts_init_buffer(thr->rawdata2_max, thr->basic.rawdata2_max, node_num, false, 0, 0); + fts_init_buffer(thr->rawdata2_min, thr->basic.rawdata2_min, node_num, false, 0, 0); + } + fts_init_buffer(thr->cb_max, thr->basic.cb_max, node_num, cb_key_check, thr->basic.cb_max_vk, key_num); + fts_init_buffer(thr->cb_min, thr->basic.cb_min, node_num, cb_key_check, thr->basic.cb_min_vk, key_num); + + /* detail threshold */ + get_detail_threshold("RawData_Max_Tx", true, thr->rawdata_max, node_num); + get_detail_threshold("RawData_Min_Tx", true, thr->rawdata_min, node_num); + get_detail_threshold("CB_Max_Tx", true, thr->cb_max, node_num); + get_detail_threshold("CB_Min_Tx", true, thr->cb_min, node_num); + + return 0; +} + +static void print_thr_incell(void) +{ + struct fts_test *tdata = fts_ftest; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + if (tdata->ts_data->log_level < 3) { + return; + } + + FTS_TEST_DBG("short_res_min:%d", thr->basic.short_res_min); + FTS_TEST_DBG("short_res_vk_min:%d", thr->basic.short_res_vk_min); + FTS_TEST_DBG("open_cb_min:%d", thr->basic.open_cb_min); + FTS_TEST_DBG("open_k1_check:%d", thr->basic.open_k1_check); + FTS_TEST_DBG("open_k1_value:%d", thr->basic.open_k1_value); + FTS_TEST_DBG("open_k2_check:%d", thr->basic.open_k2_check); + FTS_TEST_DBG("open_k2_value:%d", thr->basic.open_k2_value); + FTS_TEST_DBG("cb_min:%d", thr->basic.cb_min); + FTS_TEST_DBG("cb_max:%d", thr->basic.cb_max); + FTS_TEST_DBG("cb_vkey_check:%d", thr->basic.cb_vkey_check); + FTS_TEST_DBG("cb_min_vk:%d", thr->basic.cb_min_vk); + FTS_TEST_DBG("cb_max_vk:%d", thr->basic.cb_max_vk); + FTS_TEST_DBG("rawdata_min:%d", thr->basic.rawdata_min); + FTS_TEST_DBG("rawdata_max:%d", thr->basic.rawdata_max); + FTS_TEST_DBG("rawdata_vkey_check:%d", thr->basic.rawdata_vkey_check); + FTS_TEST_DBG("rawdata_min_vk:%d", thr->basic.rawdata_min_vk); + FTS_TEST_DBG("rawdata_max_vk:%d", thr->basic.rawdata_max_vk); + FTS_TEST_DBG("lcdnoise_frame:%d", thr->basic.lcdnoise_frame); + FTS_TEST_DBG("lcdnoise_coefficient:%d", thr->basic.lcdnoise_coefficient); + FTS_TEST_DBG("lcdnoise_coefficient_vkey:%d", thr->basic.lcdnoise_coefficient_vkey); + FTS_TEST_DBG("open_diff_min:%d", thr->basic.open_diff_min); + FTS_TEST_DBG("open_diff_max:%d", thr->basic.open_diff_max); + + FTS_TEST_DBG("open_nmos:%d", thr->basic.open_nmos); + FTS_TEST_DBG("keyshort_k1:%d", thr->basic.keyshort_k1); + FTS_TEST_DBG("keyshort_cb_max:%d", thr->basic.keyshort_cb_max); + FTS_TEST_DBG("rawdata2_min:%d", thr->basic.rawdata2_min); + FTS_TEST_DBG("rawdata2_max:%d", thr->basic.rawdata2_max); + FTS_TEST_DBG("mux_open_cb_min:%d", thr->basic.mux_open_cb_min); + FTS_TEST_DBG("open_delta_V:%d", thr->basic.open_delta_V); + + print_buffer(thr->rawdata_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->cb_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->cb_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata2_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata2_max, tdata->node.node_num, tdata->node.rx_num); +} + +static int ini_init_test_incell(void) +{ + int ret = 0; + + ret = get_test_item_incell(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get incell test item fail\n"); + return ret; + } + + + ret = get_test_threshold_incell(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get incell threshold fail\n"); + return ret; + } + + print_thr_incell(); + return 0; +} + +/* mc_sc */ +static int get_test_item_mc_sc(void) +{ + int ret = 0; + char item_name[][MAX_KEYWORD_NAME_LEN] = TEST_ITEM_MC_SC; + int length = sizeof(item_name) / MAX_KEYWORD_NAME_LEN; + int item_val = 0; + + ret = get_test_item(item_name, length, &item_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get test item fail\n"); + return ret; + } + + fts_ftest->ic.mc_sc.u.tmp = item_val; + FTS_TEST_INFO("test item:0x%x in ini", fts_ftest->ic.mc_sc.u.tmp); + return 0; +} + +static char bthr_name_mc_sc[][MAX_KEYWORD_NAME_LEN] = BASIC_THRESHOLD_MC_SC; +static int get_test_threshold_mc_sc(void) +{ + int ret = 0; + int length = sizeof(bthr_name_mc_sc) / MAX_KEYWORD_NAME_LEN; + struct fts_test *tdata = fts_ftest; + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + int node_num = tdata->node.node_num; + int sc_num = tdata->sc_node.node_num; + + tdata->basic_thr_count = sizeof(struct mc_sc_threshold_b) / sizeof(int); + /* get standard basic threshold */ + ret = get_basic_threshold(bthr_name_mc_sc, length, (int *)&thr->basic); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get basic thr fail\n"); + return ret; + } + + /* basic special set by ic */ + if (tdata->func->param_init) { + ret = tdata->func->param_init(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("special basic thr init fail\n"); + return ret; + } + } + + /* init buffer */ + fts_init_buffer(thr->rawdata_h_min, thr->basic.rawdata_h_min, node_num, false, 0, 0); + fts_init_buffer(thr->rawdata_h_max, thr->basic.rawdata_h_max, node_num, false, 0, 0); + if (tdata->func->rawdata2_support) { + fts_init_buffer(thr->rawdata_l_min, thr->basic.rawdata_l_min, node_num, false, 0, 0); + fts_init_buffer(thr->rawdata_l_max, thr->basic.rawdata_l_max, node_num, false, 0, 0); + } + fts_init_buffer(thr->tx_linearity_max, thr->basic.uniformity_tx_hole, node_num, false, 0, 0); + fts_init_buffer(thr->tx_linearity_min, 0, node_num, false, 0, 0); + fts_init_buffer(thr->rx_linearity_max, thr->basic.uniformity_rx_hole, node_num, false, 0, 0); + fts_init_buffer(thr->rx_linearity_min, 0, node_num, false, 0, 0); + fts_init_buffer(thr->mcap_cmb_max, thr->basic.mcap_cmb_max, node_num, false, 0, 0); + fts_init_buffer(thr->mcap_cmb_min, thr->basic.mcap_cmb_min, node_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_off_min, thr->basic.scap_cb_off_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_off_max, thr->basic.scap_cb_off_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_on_min, thr->basic.scap_cb_on_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_on_max, thr->basic.scap_cb_on_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_hi_min, thr->basic.scap_cb_hi_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_hi_max, thr->basic.scap_cb_hi_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_hov_min, thr->basic.scap_cb_hov_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_hov_max, thr->basic.scap_cb_hov_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_off_cf_min, thr->basic.scap_cb_off_cf_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_off_cf_max, thr->basic.scap_cb_off_cf_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_on_cf_min, thr->basic.scap_cb_on_cf_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_on_cf_max, thr->basic.scap_cb_on_cf_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_hi_cf_min, thr->basic.scap_cb_hi_cf_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_hi_cf_max, thr->basic.scap_cb_hi_cf_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_off_min, thr->basic.scap_rawdata_off_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_off_max, thr->basic.scap_rawdata_off_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_on_min, thr->basic.scap_rawdata_on_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_on_max, thr->basic.scap_rawdata_on_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_hi_min, thr->basic.scap_rawdata_hi_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_hi_max, thr->basic.scap_rawdata_hi_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_hov_min, thr->basic.scap_rawdata_hov_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_hov_max, thr->basic.scap_rawdata_hov_max, sc_num, false, 0, 0); + fts_init_buffer(thr->panel_differ_min, thr->basic.panel_differ_min, node_num, false, 0, 0); + fts_init_buffer(thr->panel_differ_max, thr->basic.panel_differ_max, node_num, false, 0, 0); +#if defined(CONFIG_FTS_NOISE_TEST_P2P) + fts_init_buffer(thr->noise_min, 0, node_num, false, 0, 0); + fts_init_buffer(thr->noise_max, thr->basic.noise_max, node_num, false, 0, 0); +#endif + /* detail threshold */ + get_detail_threshold("RawData_Min_High_Tx", true, thr->rawdata_h_min, node_num); + get_detail_threshold("RawData_Max_High_Tx", true, thr->rawdata_h_max, node_num); + if (tdata->func->rawdata2_support) { + get_detail_threshold("RawData_Min_Low_Tx", true, thr->rawdata_l_min, node_num); + get_detail_threshold("RawData_Max_Low_Tx", true, thr->rawdata_l_max, node_num); + } + get_detail_threshold("Tx_Linearity_Max_Tx", true, thr->tx_linearity_max, node_num); + get_detail_threshold("Rx_Linearity_Max_Tx", true, thr->rx_linearity_max, node_num); + get_detail_threshold("McapCMBTest_Min_Hole", true, thr->mcap_cmb_min, node_num); + get_detail_threshold("McapCMBTest_Max_Hole", true, thr->mcap_cmb_max, node_num); + get_detail_threshold("ScapCB_OFF_Min_", true, thr->scap_cb_off_min, sc_num); + get_detail_threshold("ScapCB_OFF_Max_", true, thr->scap_cb_off_max, sc_num); + get_detail_threshold("ScapCB_ON_Min_", true, thr->scap_cb_on_min, sc_num); + get_detail_threshold("ScapCB_ON_Max_", true, thr->scap_cb_on_max, sc_num); + get_detail_threshold("ScapCB_High_Min_", true, thr->scap_cb_hi_min, sc_num); + get_detail_threshold("ScapCB_High_Max_", true, thr->scap_cb_hi_max, sc_num); + get_detail_threshold("ScapCB_Hov_Min_", true, thr->scap_cb_hov_min, sc_num); + get_detail_threshold("ScapCB_Hov_Max_", true, thr->scap_cb_hov_max, sc_num); + get_detail_threshold("ScapRawData_OFF_Min_", true, thr->scap_rawdata_off_min, sc_num); + get_detail_threshold("ScapRawData_OFF_Max_", true, thr->scap_rawdata_off_max, sc_num); + get_detail_threshold("ScapRawData_ON_Min_", true, thr->scap_rawdata_on_min, sc_num); + get_detail_threshold("ScapRawData_ON_Max_", true, thr->scap_rawdata_on_max, sc_num); + get_detail_threshold("ScapRawData_High_Min_", true, thr->scap_rawdata_hi_min, sc_num); + get_detail_threshold("ScapRawData_High_Max_", true, thr->scap_rawdata_hi_max, sc_num); + get_detail_threshold("ScapRawData_Hov_Min_", true, thr->scap_rawdata_hov_min, sc_num); + get_detail_threshold("ScapRawData_Hov_Max_", true, thr->scap_rawdata_hov_max, sc_num); + get_detail_threshold("Panel_Differ_Min_Tx", true, thr->panel_differ_min, node_num); + get_detail_threshold("Panel_Differ_Max_Tx", true, thr->panel_differ_max, node_num); +#if defined(CONFIG_FTS_NOISE_TEST_P2P) + get_detail_threshold("NoistTestCoefficient_Tx", true, thr->noise_max, node_num); +#endif + + return 0; +} + +static void print_thr_mc_sc(void) +{ + struct fts_test *tdata = fts_ftest; + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + + if (tdata->ts_data->log_level < 3) { + return; + } + + FTS_TEST_DBG("rawdata_h_min:%d", thr->basic.rawdata_h_min); + FTS_TEST_DBG("rawdata_h_max:%d", thr->basic.rawdata_h_max); + FTS_TEST_DBG("rawdata_set_hfreq:%d", thr->basic.rawdata_set_hfreq); + FTS_TEST_DBG("rawdata_l_min:%d", thr->basic.rawdata_l_min); + FTS_TEST_DBG("rawdata_l_max:%d", thr->basic.rawdata_l_max); + FTS_TEST_DBG("rawdata_set_lfreq:%d", thr->basic.rawdata_set_lfreq); + FTS_TEST_DBG("uniformity_check_tx:%d", thr->basic.uniformity_check_tx); + FTS_TEST_DBG("uniformity_check_rx:%d", thr->basic.uniformity_check_rx); + FTS_TEST_DBG("uniformity_check_min_max:%d", thr->basic.uniformity_check_min_max); + FTS_TEST_DBG("uniformity_tx_hole:%d", thr->basic.uniformity_tx_hole); + FTS_TEST_DBG("uniformity_rx_hole:%d", thr->basic.uniformity_rx_hole); + FTS_TEST_DBG("uniformity_min_max_hole:%d", thr->basic.uniformity_min_max_hole); + FTS_TEST_DBG("scap_cb_off_min:%d", thr->basic.scap_cb_off_min); + FTS_TEST_DBG("scap_cb_off_max:%d", thr->basic.scap_cb_off_max); + FTS_TEST_DBG("scap_cb_wp_off_check:%d", thr->basic.scap_cb_wp_off_check); + FTS_TEST_DBG("scap_cb_on_min:%d", thr->basic.scap_cb_on_min); + FTS_TEST_DBG("scap_cb_on_max:%d", thr->basic.scap_cb_on_max); + FTS_TEST_DBG("scap_cb_wp_on_check:%d", thr->basic.scap_cb_wp_on_check); + FTS_TEST_DBG("scap_rawdata_off_min:%d", thr->basic.scap_rawdata_off_min); + FTS_TEST_DBG("scap_rawdata_off_max:%d", thr->basic.scap_rawdata_off_max); + FTS_TEST_DBG("scap_rawdata_wp_off_check:%d", thr->basic.scap_rawdata_wp_off_check); + FTS_TEST_DBG("scap_rawdata_on_min:%d", thr->basic.scap_rawdata_on_min); + FTS_TEST_DBG("scap_rawdata_on_max:%d", thr->basic.scap_rawdata_on_max); + FTS_TEST_DBG("scap_rawdata_wp_on_check:%d", thr->basic.scap_rawdata_wp_on_check); + FTS_TEST_DBG("short_cg:%d", thr->basic.short_cg); + FTS_TEST_DBG("short_cc:%d", thr->basic.short_cc); + FTS_TEST_DBG("panel_differ_min:%d", thr->basic.panel_differ_min); + FTS_TEST_DBG("panel_differ_max:%d", thr->basic.panel_differ_max); + + print_buffer(thr->rawdata_h_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_h_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_l_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_l_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->tx_linearity_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rx_linearity_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->mcap_cmb_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->mcap_cmb_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->scap_cb_off_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_off_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_on_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_on_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_hi_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_hi_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_hov_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_hov_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_off_cf_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_off_cf_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_on_cf_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_on_cf_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_hi_cf_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_hi_cf_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_off_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_off_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_on_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_on_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_hi_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_hi_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_hov_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_hov_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->panel_differ_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->panel_differ_max, tdata->node.node_num, tdata->node.rx_num); +#if defined(CONFIG_FTS_NOISE_TEST_P2P) + print_buffer(thr->noise_max, tdata->node.node_num, tdata->node.rx_num); +#endif +} + +static int ini_init_test_mc_sc(void) +{ + int ret = 0; + + ret = get_test_item_mc_sc(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get mc_sc test item fail\n"); + return ret; + } + + ret = get_test_threshold_mc_sc(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get mc_sc threshold fail\n"); + return ret; + } + + print_thr_mc_sc(); + return 0; +} + +/* sc */ +static int get_test_item_sc(void) +{ + int ret = 0; + char item_name[][MAX_KEYWORD_NAME_LEN] = TEST_ITEM_SC; + int length = sizeof(item_name) / MAX_KEYWORD_NAME_LEN; + int item_val = 0; + + ret = get_test_item(item_name, length, &item_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get test item fail\n"); + return ret; + } + + fts_ftest->ic.sc.u.tmp = item_val; + return 0; +} + +static char bthr_name_sc[][MAX_KEYWORD_NAME_LEN] = BASIC_THRESHOLD_SC; +static int get_test_threshold_sc(void) +{ + int ret = 0; + int length = sizeof(bthr_name_sc) / MAX_KEYWORD_NAME_LEN; + struct fts_test *tdata = fts_ftest; + struct sc_threshold *thr = &tdata->ic.sc.thr; + int node_num = tdata->node.node_num; + + tdata->basic_thr_count = sizeof(struct sc_threshold_b) / sizeof(int); + /* get standard basic threshold */ + ret = get_basic_threshold(bthr_name_sc, length, (int *)&thr->basic); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get basic thr fail\n"); + return ret; + } + + /* basic special set by ic */ + if (tdata->func->param_init) { + ret = tdata->func->param_init(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("special basic thr init fail\n"); + return ret; + } + } + + /* init buffer */ + fts_init_buffer(thr->rawdata_min, thr->basic.rawdata_min, node_num, false, 0, 0); + fts_init_buffer(thr->rawdata_max, thr->basic.rawdata_max, node_num, false, 0, 0); + fts_init_buffer(thr->cb_min, thr->basic.cb_min, node_num, false, 0, 0); + fts_init_buffer(thr->cb_max, thr->basic.cb_max, node_num, false, 0, 0); + fts_init_buffer(thr->dcb_sort, 0, node_num, false, 0, 0); + fts_init_buffer(thr->dcb_base, thr->basic.dcb_base, node_num, false, 0, 0); + + /* detail threshold */ + get_detail_threshold("RawDataTest_Min", false, thr->rawdata_min, node_num); + get_detail_threshold("RawDataTest_Max", false, thr->rawdata_max, node_num); + get_detail_threshold("CbTest_Min", false, thr->cb_min, node_num); + get_detail_threshold("CbTest_Max", false, thr->cb_max, node_num); + get_detail_threshold("DeltaCxTest_Sort", false, thr->dcb_sort, node_num); + get_detail_threshold("DeltaCbTest_Base", false, thr->dcb_base, node_num); + + return 0; +} + +static void print_thr_sc(void) +{ + struct fts_test *tdata = fts_ftest; + struct sc_threshold *thr = &tdata->ic.sc.thr; + + if (tdata->ts_data->log_level < 3) { + return; + } + + FTS_TEST_DBG("rawdata_min:%d", thr->basic.rawdata_min); + FTS_TEST_DBG("rawdata_max:%d", thr->basic.rawdata_max); + FTS_TEST_DBG("cb_min:%d", thr->basic.cb_min); + FTS_TEST_DBG("cb_max:%d", thr->basic.cb_max); + FTS_TEST_DBG("dcb_differ_max:%d", thr->basic.dcb_differ_max); + FTS_TEST_DBG("dcb_key_check:%d", thr->basic.dcb_key_check); + FTS_TEST_DBG("dcb_key_differ_max:%d", thr->basic.dcb_key_differ_max); + FTS_TEST_DBG("dcb_ds1:%d", thr->basic.dcb_ds1); + FTS_TEST_DBG("dcb_ds2:%d", thr->basic.dcb_ds2); + FTS_TEST_DBG("dcb_ds3:%d", thr->basic.dcb_ds3); + FTS_TEST_DBG("dcb_ds4:%d", thr->basic.dcb_ds4); + FTS_TEST_DBG("dcb_ds5:%d", thr->basic.dcb_ds5); + FTS_TEST_DBG("dcb_ds6:%d", thr->basic.dcb_ds6); + FTS_TEST_DBG("dcb_critical_check:%d", thr->basic.dcb_critical_check); + FTS_TEST_DBG("dcb_cs1:%d", thr->basic.dcb_cs1); + FTS_TEST_DBG("dcb_cs2:%d", thr->basic.dcb_cs2); + FTS_TEST_DBG("dcb_cs3:%d", thr->basic.dcb_cs3); + FTS_TEST_DBG("dcb_cs4:%d", thr->basic.dcb_cs4); + FTS_TEST_DBG("dcb_cs5:%d", thr->basic.dcb_cs5); + FTS_TEST_DBG("dcb_cs6:%d", thr->basic.dcb_cs6); + + print_buffer(thr->rawdata_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->cb_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->cb_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->dcb_sort, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->dcb_base, tdata->node.node_num, tdata->node.rx_num); +} + +static int ini_init_test_sc(void) +{ + int ret = 0; + + ret = get_test_item_sc(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get sc test item fail\n"); + return ret; + } + + ret = get_test_threshold_sc(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get sc threshold fail\n"); + return ret; + } + + print_thr_sc(); + return 0; +} + +static u32 ini_get_ic_code(char *ic_name) +{ + int i = 0; + int type_size = 0; + int ini_icname_len = 0; + int ic_types_len = 0; + + ini_icname_len = strlen(ic_name); + type_size = sizeof(ic_types) / sizeof(ic_types[0]); + for (i = 0; i < type_size; i++) { + ic_types_len = strlen(ic_name); + if (ini_icname_len == ic_types_len) { + if (0 == strncmp(ic_name, ic_types[i].ic_name, ic_types_len)) + return ic_types[i].ic_type; + } + } + + FTS_TEST_ERROR("no IC type match"); + return 0; +} + + +static void ini_init_interface(struct ini_data *ini) +{ + char str[MAX_KEYWORD_VALUE_LEN] = { 0 }; + u32 value = 0; + struct fts_test *tdata = fts_ftest; + + /* IC type */ + ini_get_string_value("Interface", "IC_Type", str); + snprintf(ini->ic_name, MAX_IC_NAME_LEN, "%s", str); + + value = ini_get_ic_code(str); + ini->ic_code = value; + FTS_TEST_INFO("ic name:%s, ic code:%x", ini->ic_name, ini->ic_code); + + if (IC_HW_MC_SC == tdata->func->hwtype) { + get_value_interface("Normalize_Type", &value); + tdata->normalize = (u8)value; + FTS_TEST_DBG("normalize:%d", tdata->normalize); + } +} + +static int ini_init_test(struct ini_data *ini) +{ + int ret = 0; + struct fts_test *tdata = fts_ftest; + + /* interface init */ + ini_init_interface(ini); + + /* node valid */ + ret = init_node_valid(); + if (ret < 0) { + FTS_TEST_ERROR("init node valid fail"); + return ret; + } + + switch (tdata->func->hwtype) { + case IC_HW_INCELL: + ret = ini_init_test_incell(); + break; + case IC_HW_MC_SC: + ret = ini_init_test_mc_sc(); + break; + case IC_HW_SC: + ret = ini_init_test_sc(); + break; + default: + FTS_TEST_SAVE_ERR("test ic type(%d) fail\n", tdata->func->hwtype); + ret = -EINVAL; + break; + } + + return ret; +} + +/* + * fts_test_get_testparam_from_ini - get test parameters from ini + * + * read, parse the configuration file, initialize the test variable + * + * return 0 if succuss, else errro code + */ +int fts_test_get_testparam_from_ini(char *config_name) +{ + int ret = 0; + struct ini_data *ini = &fts_ftest->ini; + + ret = fts_test_get_ini_via_request_firmware(ini, config_name); + if (ret != 0) { +#ifdef CONFIG_FTS_COMPATIBLE_WITH_GKI + ret = fts_test_get_ini_via_request_firmware(ini, config_name); +#else + ret = fts_test_get_ini_default(ini, config_name); +#endif + if (ret < 0) { + FTS_TEST_ERROR("get ini(default) fail"); + goto get_ini_err; + } + } + + ini->keyword_num_total = 0; + ini->section_num = 0; + + ini->tmp = vmalloc(sizeof(struct ini_keyword) * MAX_KEYWORD_NUM); + if (NULL == ini->tmp) { + FTS_TEST_ERROR("malloc memory for ini tmp fail"); + ret = -ENOMEM; + goto get_ini_err; + } + memset(ini->tmp, 0, sizeof(struct ini_keyword) * MAX_KEYWORD_NUM); + + /* parse ini data to get keyword name&value */ + ret = ini_init_inidata(ini); + if (ret < 0) { + FTS_TEST_ERROR("ini_init_inidata fail"); + goto get_ini_err; + } + + /* parse threshold & test item */ + ret = ini_init_test(ini); + if (ret < 0) { + FTS_TEST_ERROR("ini init fail"); + goto get_ini_err; + } + + ret = 0; +get_ini_err: + if (ini->tmp) { + vfree(ini->tmp); + ini->tmp = NULL; + } + + if (ini->data) { + vfree(ini->data); + ini->data = NULL; + } + + return ret; +} diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/focaltech_test_ini.h b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/focaltech_test_ini.h new file mode 100644 index 000000000000..f6f76518470a --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/focaltech_test_ini.h @@ -0,0 +1,148 @@ +/************************************************************************ +* Copyright (c) 2012-2020, Focaltech Systems (R)£¬All Rights Reserved. +* +* File Name: focaltech_test_ini.h +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-01 +* +* Abstract: parsing function of INI file +* +************************************************************************/ +#ifndef _INI_H +#define _INI_H +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define MAX_KEYWORD_NUM (1000) +#define MAX_KEYWORD_NAME_LEN (50) +#define MAX_KEYWORD_VALUE_LEN (512) +#define MAX_KEYWORD_VALUE_ONE_LEN (16) +#define MAX_INI_LINE_LEN (MAX_KEYWORD_NAME_LEN + MAX_KEYWORD_VALUE_LEN) +#define MAX_INI_SECTION_NUM (20) +#define MAX_IC_NAME_LEN (32) +#define MAX_TEST_ITEM (20) +#define IC_CODE_OFFSET (16) + +/***************************************************************************** +* enumerations, structures and unions +*****************************************************************************/ +struct ini_ic_type { + char ic_name[MAX_IC_NAME_LEN]; + u32 ic_type; +}; + +enum line_type { + LINE_SECTION = 1, + LINE_KEYWORD = 2 , + LINE_OTHER = 3, +}; + +struct ini_keyword { + char name[MAX_KEYWORD_NAME_LEN]; + char value[MAX_KEYWORD_VALUE_LEN]; +}; + +struct ini_section { + char name[MAX_KEYWORD_NAME_LEN]; + int keyword_num; + /* point to ini.tmp, don't need free */ + struct ini_keyword *keyword; +}; + +struct ini_data { + char *data; + int length; + int keyword_num_total; + int section_num; + struct ini_section section[MAX_INI_SECTION_NUM]; + struct ini_keyword *tmp; + char ic_name[MAX_IC_NAME_LEN]; + u32 ic_code; +}; + +#define TEST_ITEM_INCELL { \ + "SHORT_CIRCUIT_TEST", \ + "OPEN_TEST", \ + "CB_TEST", \ + "RAWDATA_TEST", \ + "LCD_NOISE_TEST", \ + "KEY_SHORT_TEST", \ + "MUX_OPEN_TEST", \ +} + +#define BASIC_THRESHOLD_INCELL { \ + "ShortCircuit_ResMin", "ShortCircuit_VkResMin", \ + "OpenTest_CBMin", "OpenTest_Check_K1", "OpenTest_K1Threshold", "OpenTest_Check_K2", "OpenTest_K2Threshold", \ + "CBTest_Min", "CBTest_Max", \ + "CBTest_VKey_Check", "CBTest_Min_Vkey", "CBTest_Max_Vkey", \ + "RawDataTest_Min", "RawDataTest_Max", \ + "RawDataTest_VKey_Check", "RawDataTest_Min_VKey", "RawDataTest_Max_VKey", \ + "LCD_NoiseTest_Frame", "LCD_NoiseTest_Coefficient", "LCD_NoiseTest_Coefficient_key", \ + "OpenTest_DifferMin", "OpenTest_DifferMax", \ +} + + +#define TEST_ITEM_MC_SC { \ + "RAWDATA_TEST", \ + "UNIFORMITY_TEST", \ + "SCAP_CB_TEST", \ + "SCAP_RAWDATA_TEST", \ + "WEAK_SHORT_CIRCUIT_TEST", \ + "PANEL_DIFFER_TEST", \ + "NOISE_TEST", \ + "Mcap_CMB_TEST", \ +} + +#define BASIC_THRESHOLD_MC_SC { \ + "RawDataTest_High_Min", "RawDataTest_High_Max", "RawDataTest_HighFreq", \ + "RawDataTest_Low_Min", "RawDataTest_Low_Max", "RawDataTest_LowFreq", \ + "UniformityTest_Check_Tx", "UniformityTest_Check_Rx","UniformityTest_Check_MinMax", \ + "UniformityTest_Tx_Hole", "UniformityTest_Rx_Hole", "UniformityTest_MinMax_Hole", \ + "SCapCbTest_OFF_Min", "SCapCbTest_OFF_Max", "ScapCBTest_SetWaterproof_OFF", \ + "SCapCbTest_ON_Min", "SCapCbTest_ON_Max", "ScapCBTest_SetWaterproof_ON", \ + "SCapRawDataTest_OFF_Min", "SCapRawDataTest_OFF_Max", "SCapRawDataTest_SetWaterproof_OFF", \ + "SCapRawDataTest_ON_Min", "SCapRawDataTest_ON_Max", "SCapRawDataTest_SetWaterproof_ON", \ + "WeakShortTest_CG", "WeakShortTest_CC", \ + "PanelDifferTest_Min", "PanelDifferTest_Max", \ + "SCapCbTest_High_Min", "SCapCbTest_High_Max", "ScapCBTest_SetHighSensitivity", \ + "SCapRawDataTest_High_Min", "SCapRawDataTest_High_Max", "SCapRawDataTest_SetHighSensitivity", \ + "SCapCbTest_Hov_Min", "SCapCbTest_Hov_Max", "ScapCBTest_SetHov", \ + "SCapRawDataTest_Hov_Min", "SCapRawDataTest_Hov_Max", "SCapRawDataTest_SetHov", \ +} + +#define TEST_ITEM_SC { \ + "RAWDATA_TEST", \ + "CB_TEST", \ + "DELTA_CB_TEST", \ + "WEAK_SHORT_TEST", \ +} + +#define BASIC_THRESHOLD_SC { \ + "RawDataTest_Min", "RawDataTest_Max", \ + "CbTest_Min", "CbTest_Max", \ + "DeltaCbTest_Base", "DeltaCbTest_Differ_Max", \ + "DeltaCbTest_Include_Key_Test", "DeltaCbTest_Key_Differ_Max", \ + "DeltaCbTest_Deviation_S1", "DeltaCbTest_Deviation_S2", "DeltaCbTest_Deviation_S3", \ + "DeltaCbTest_Deviation_S4", "DeltaCbTest_Deviation_S5", "DeltaCbTest_Deviation_S6", \ + "DeltaCbTest_Set_Critical", "DeltaCbTest_Critical_S1", "DeltaCbTest_Critical_S2", \ + "DeltaCbTest_Critical_S3", "DeltaCbTest_Critical_S4", \ + "DeltaCbTest_Critical_S5", "DeltaCbTest_Critical_S6", \ +} + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +int fts_test_get_testparam_from_ini(char *config_name); +int get_keyword_value(char *section, char *name, int *value); + +#define get_value_interface(name, value) \ + get_keyword_value("Interface", name, value) +#define get_value_basic(name, value) \ + get_keyword_value("Basic_Threshold", name, value) +#define get_value_detail(name, value) \ + get_keyword_value("SpecialSet", name, value) +#define get_value_testitem(name, value) \ + get_keyword_value("TestItem", name, value) +#endif /* _INI_H */ diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/supported_ic/Makefile b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/supported_ic/Makefile new file mode 100644 index 000000000000..48bd9086b630 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/supported_ic/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_TOUCHSCREEN_FTS_V3) += focaltech_test_ft3681.o \ No newline at end of file diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/supported_ic/focaltech_test_ft3681.c b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/supported_ic/focaltech_test_ft3681.c new file mode 100644 index 000000000000..177f4d433677 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_test/supported_ic/focaltech_test_ft3681.c @@ -0,0 +1,2544 @@ +/************************************************************************ +* Copyright (c) 2012-2021, Focaltech Systems (R), All Rights Reserved. +* +* File Name: Focaltech_test_ft5662.c +* +* Author: Focaltech Driver Team +* +* Created: 2021-08-16 +* +* Abstract: +* +************************************************************************/ + +/***************************************************************************** +* included header files +*****************************************************************************/ +#include "../focaltech_test.h" + +/***************************************************************************** +* private constant and macro definitions using #define +*****************************************************************************/ + +/***************************************************************************** +* Static function +*****************************************************************************/ + +static int short_test_ch_to_all( + struct fts_test *tdata, int *res, u8 *ab_ch, bool *result) +{ + int ret = 0; + int i = 0; + int min_cc = tdata->ic.mc_sc.thr.basic.short_cc; + int ch_num = tdata->sc_node.tx_num + tdata->sc_node.rx_num; + int byte_num = ch_num * 2; + u8 ab_ch_num = 0; + + FTS_TEST_DBG("short test:channel to all other\n"); + /*get resistance data*/ + ret = short_get_adc_data_mc(TEST_RETVAL_AA, byte_num, &res[0], \ + FACTROY_REG_SHORT2_CA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get weak short data fail,ret:%d\n", ret); + return ret; + } + + *result = true; + for (i = 0; i < ch_num; i++) { + if (res[i] < min_cc) { + ab_ch_num++; + ab_ch[ab_ch_num] = i; + *result = false; + } + } + + if (ab_ch_num) { + print_buffer(res, ch_num, ch_num); + ab_ch[0] = ab_ch_num; + printk("[FTS_TS]ab_ch:"); + for (i = 0; i < ab_ch_num + 1; i++) { + printk("%2d ", ab_ch[i]); + } + printk("\n"); + } + + return 0; +} + +static int short_test_ch_to_gnd( + struct fts_test *tdata, int *res, u8 *ab_ch, bool *result) +{ + int ret = 0; + int i = 0; + int min_cg = tdata->ic.mc_sc.thr.basic.short_cg; + int tx_num = tdata->sc_node.tx_num; + int byte_num = 0; + u8 ab_ch_num = 0; + bool is_cg_short = false; + + FTS_TEST_DBG("short test:channel to gnd\n"); + ab_ch_num = ab_ch[0]; + ret = fts_test_write(FACTROY_REG_SHORT2_AB_CH, ab_ch, ab_ch_num + 1); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write abnormal channel fail\n"); + return ret; + } + + /*get resistance data*/ + byte_num = ab_ch_num * 2; + ret = short_get_adc_data_mc(TEST_RETVAL_AA, byte_num, &res[0], \ + FACTROY_REG_SHORT2_CG); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get weak short data fail,ret:%d\n", ret); + return ret; + } + + *result = true; + for (i = 0; i < ab_ch_num; i++) { + if (res[i] < min_cg) { + *result = false; + if (!is_cg_short) { + FTS_TEST_SAVE_INFO("\nGND Short:\n"); + is_cg_short = true; + } + + if (ab_ch[i + 1] <= tx_num) { + FTS_TEST_SAVE_INFO("Tx%d with GND:", ab_ch[i + 1]); + } else { + FTS_TEST_SAVE_INFO( "Rx%d with GND:", (ab_ch[i + 1] - tx_num)); + } + FTS_TEST_SAVE_INFO("%d(K)\n", res[i]); + } + } + + return 0; +} + +static int short_test_ch_to_ch( + struct fts_test *tdata, int *res, u8 *ab_ch, bool *result) +{ + int ret = 0; + int i = 0; + int j = 0; + int adc_cnt = 0; + int min_cc = tdata->ic.mc_sc.thr.basic.short_cc; + int tx_num = tdata->sc_node.tx_num; + int ch_num = tdata->sc_node.tx_num + tdata->sc_node.rx_num; + int byte_num = 0; + int tmp_num = 0; + u8 ab_ch_num = 0; + bool is_cc_short = false; + + FTS_TEST_DBG("short test:channel to channel\n"); + ab_ch_num = ab_ch[0]; + if (ab_ch_num < 2) { + FTS_TEST_DBG("abnormal channel number<2, not run ch_ch test"); + return ret; + } + + ret = fts_test_write(FACTROY_REG_SHORT2_AB_CH, ab_ch, ab_ch_num + 1); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write abnormal channel fail\n"); + return ret; + } + + /*get resistance data*/ + /*channel to channel: num * (num - 1) / 2, max. node_num*/ + tmp_num = ab_ch_num * (ab_ch_num - 1) / 2; + tmp_num = (tmp_num > ch_num) ? ch_num : tmp_num; + byte_num = tmp_num * 2; + ret = short_get_adc_data_mc(TEST_RETVAL_AA, byte_num, &res[0], \ + FACTROY_REG_SHORT2_CC); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get weak short data fail,ret:%d\n", ret); + return ret; + } + + *result = true; + for (i = 0; i < ab_ch_num; i++) { + for (j = i + 1; j < ab_ch_num; j++) { + if (adc_cnt >= tmp_num) + break; + + if (res[adc_cnt] < min_cc) { + *result = false; + if (!is_cc_short) { + FTS_TEST_SAVE_INFO("\nMutual Short:\n"); + is_cc_short = true; + } + + if (ab_ch[i + 1] <= tx_num) { + FTS_TEST_SAVE_INFO("Tx%d with", (ab_ch[i + 1])); + } else { + FTS_TEST_SAVE_INFO("Rx%d with", (ab_ch[i + 1] - tx_num)); + } + + if (ab_ch[j + 1] <= tx_num) { + FTS_TEST_SAVE_INFO(" Tx%d", (ab_ch[j + 1] ) ); + } else { + FTS_TEST_SAVE_INFO(" Rx%d", (ab_ch[j + 1] - tx_num)); + } + FTS_TEST_SAVE_INFO(":%d(K)\n", res[adc_cnt]); + } + adc_cnt++; + } + } + + return 0; +} + +static int get_cb_ft5662(int *cb_buf, int byte_num, bool is_cf) +{ + int ret = 0; + int i = 0; + u8 cb[SC_NUM_MAX] = { 0 }; + + if (byte_num > SC_NUM_MAX) { + FTS_TEST_SAVE_ERR("CB byte(%d)>max(%d)", byte_num, SC_NUM_MAX); + return -EINVAL; + } + + ret = fts_test_write_reg(FACTORY_REG_MC_SC_CB_H_ADDR_OFF, 0); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write cb_h addr offset fail\n"); + return ret; + } + + + ret = fts_test_write_reg(FACTORY_REG_MC_SC_CB_ADDR_OFF, 0); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write cb addr offset fail\n"); + return ret; + } + + ret = fts_test_read(FACTORY_REG_MC_SC_CB_ADDR, cb, byte_num); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read cb fail\n"); + return ret; + } + + for (i = 0; i < byte_num; i++) { + if (is_cf) + cb_buf[i] = (cb[i] & 0x80) ? -((cb[i] & 0x3F)) : (cb[i] & 0x3F); + else + cb_buf[i] = (cb[i] & 0x80) ? -((cb[i] & 0x7F)) : (cb[i] & 0x7F); + } + + return 0; +} + +static int scap_cb_cc(struct fts_test *tdata, int *scap_cb, bool *result) +{ + int ret = 0; + int i = 0; + u8 wc_sel = 0; + u8 hc_sel = 0; + u8 hov_high = 0; + int byte_num = tdata->sc_node.node_num; + bool tmp_result = false; + bool tmp2_result = false; + bool tmp3_result = false; + bool fw_wp_check = false; + bool tx_check = false; + bool rx_check = false; + int *scb_tmp = NULL; + int *scb_cf = NULL; + int scb_cnt = 0; + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + + FTS_TEST_FUNC_ENTER(); + if (!thr->scap_cb_on_min || !thr->scap_cb_on_max + || !thr->scap_cb_off_min || !thr->scap_cb_off_max + || !thr->scap_cb_hi_min || !thr->scap_cb_hi_max + || !thr->scap_cb_on_cf_min || !thr->scap_cb_on_cf_max + || !thr->scap_cb_off_cf_min || !thr->scap_cb_off_cf_max + || !thr->scap_cb_hi_cf_min || !thr->scap_cb_hi_cf_max) { + FTS_TEST_SAVE_ERR("scap_cb_on/off/hi/cf_min/max is null\n"); + ret = -EINVAL; + return ret; + } + + /* get waterproof channel select */ + ret = fts_test_read_reg(FACTORY_REG_WC_SEL, &wc_sel); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read water_channel_sel fail,ret=%d\n", ret); + return ret; + } + + ret = fts_test_read_reg(FACTORY_REG_HC_SEL, &hc_sel); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read high_channel_sel fail,ret=%d\n", ret); + return ret; + } + + /* water proof on check */ + tmp_result = true; + fw_wp_check = get_fw_wp(wc_sel, WATER_PROOF_ON); + if (thr->basic.scap_cb_wp_on_check && fw_wp_check) { + tdata->csv_item_cnt += 2; + tdata->csv_item_scb |= 0x01; + scb_tmp = scap_cb + scb_cnt; + scb_cf = scb_tmp + tdata->sc_node.node_num; + /* 1:waterproof 0:non-waterproof */ + ret = fts_test_write_reg(FACTORY_REG_MC_SC_MODE, WATER_PROOF_ON); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set mc_sc mode fail\n"); + return ret; + } + + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + return ret; + } + + ret = get_cb_ft5662(scb_tmp, byte_num, false); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read sc_cb fail,ret=%d\n", ret); + return ret; + } + + ret = fts_test_write_reg(FACTROY_REG_CB_BUF_SEL, 1); + if (ret) { + FTS_TEST_SAVE_ERR("restore sc mode fail,ret=%d\n", ret); + return ret; + } + + ret = get_cb_ft5662(scb_cf, byte_num, true); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get sc_cb fail,ret=%d\n", ret); + return ret; + } + + ret = fts_test_write_reg(FACTROY_REG_CB_BUF_SEL, 0); + if (ret) { + FTS_TEST_SAVE_ERR("restore sc mode fail,ret=%d\n", ret); + return ret; + } + + /* show Scap CB */ + FTS_TEST_SAVE_INFO("scap_cb in waterproof on mode:\n"); + show_data_mc_sc(scb_tmp); + FTS_TEST_SAVE_INFO("scap_cb_cf:\n"); + show_data_mc_sc(scb_cf); + + /* compare */ + tx_check = get_fw_wp(wc_sel, WATER_PROOF_ON_TX); + rx_check = get_fw_wp(wc_sel, WATER_PROOF_ON_RX); + for (i = 0; i < tdata->sc_node.node_num; i++) { + if (0 == tdata->node_valid_sc[i]) + continue; + + if ((i == 0) || (i == tdata->sc_node.rx_num)) + continue; + + if ((rx_check && (i < tdata->sc_node.rx_num)) || + (tx_check && (i >= tdata->sc_node.rx_num))) { + if ((scb_tmp[i] < thr->scap_cb_on_min[i]) || + (scb_tmp[i] > thr->scap_cb_on_max[i])) { + FTS_TEST_SAVE_ERR("test fail,CH%d=%5d,range=(%5d,%5d)\n", + i + 1, scb_tmp[i], + thr->scap_cb_on_min[i], + thr->scap_cb_on_max[i]); + tmp_result = false; + } + + if ((scb_cf[i] < thr->scap_cb_on_cf_min[i]) || + (scb_cf[i] > thr->scap_cb_on_cf_max[i])) { + FTS_TEST_SAVE_ERR("test2 fail,CH%d=%5d,range=(%5d,%5d)\n", + i + 1, scb_cf[i], + thr->scap_cb_on_cf_min[i], + thr->scap_cb_on_cf_max[i]); + tmp_result = false; + } + } + } + + scb_cnt += tdata->sc_node.node_num * 2; + } + + /* water proof off check */ + tmp2_result = true; + fw_wp_check = get_fw_wp(wc_sel, WATER_PROOF_OFF); + if (thr->basic.scap_cb_wp_off_check && fw_wp_check) { + tdata->csv_item_cnt += 2; + tdata->csv_item_scb |= 0x02; + scb_tmp = scap_cb + scb_cnt; + scb_cf = scb_tmp + tdata->sc_node.node_num; + /* 1:waterproof 0:non-waterproof */ + ret = fts_test_write_reg(FACTORY_REG_MC_SC_MODE, WATER_PROOF_OFF); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set mc_sc mode fail\n"); + return ret; + } + + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + return ret; + } + + ret = get_cb_ft5662(scb_tmp, byte_num, false); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read sc_cb fail,ret=%d\n", ret); + return ret; + } + + ret = fts_test_write_reg(FACTROY_REG_CB_BUF_SEL, 1); + if (ret) { + FTS_TEST_SAVE_ERR("restore sc mode fail,ret=%d\n", ret); + return ret; + } + + ret = get_cb_ft5662(scb_cf, byte_num, true); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get sc_cb fail,ret=%d\n", ret); + return ret; + } + + ret = fts_test_write_reg(FACTROY_REG_CB_BUF_SEL, 0); + if (ret) { + FTS_TEST_SAVE_ERR("restore sc mode fail,ret=%d\n", ret); + return ret; + } + + /* show Scap CB */ + FTS_TEST_SAVE_INFO("scap_cb in waterproof off mode:\n"); + show_data_mc_sc(scb_tmp); + FTS_TEST_SAVE_INFO("scap_cb_cf:\n"); + show_data_mc_sc(scb_cf); + + /* compare */ + tx_check = get_fw_wp(wc_sel, WATER_PROOF_OFF_TX); + rx_check = get_fw_wp(wc_sel, WATER_PROOF_OFF_RX); + for (i = 0; i < tdata->sc_node.node_num; i++) { + if (0 == tdata->node_valid_sc[i]) + continue; + + if ((i == 0) || (i == tdata->sc_node.rx_num)) + continue; + + if ((rx_check && (i < tdata->sc_node.rx_num)) || + (tx_check && (i >= tdata->sc_node.rx_num))) { + if ((scb_tmp[i] < thr->scap_cb_off_min[i]) || + (scb_tmp[i] > thr->scap_cb_off_max[i])) { + FTS_TEST_SAVE_ERR("test fail,CH%d=%5d,range=(%5d,%5d)\n", + i + 1, scb_tmp[i], + thr->scap_cb_off_min[i], + thr->scap_cb_off_max[i]); + tmp2_result = false; + } + + if ((scb_cf[i] < thr->scap_cb_off_cf_min[i]) || + (scb_cf[i] > thr->scap_cb_off_cf_max[i])) { + FTS_TEST_SAVE_ERR("test2 fail,CH%d=%5d,range=(%5d,%5d)\n", + i + 1, scb_cf[i], + thr->scap_cb_off_cf_min[i], + thr->scap_cb_off_cf_max[i]); + tmp2_result = false; + } + } + } + + scb_cnt += tdata->sc_node.node_num * 2; + } + + /*high mode*/ + tmp3_result = true; + hov_high = (hc_sel & 0x03); + if (thr->basic.scap_cb_hi_check && hov_high) { + tdata->csv_item_cnt += 2; + tdata->csv_item_scb |= 0x04; + scb_tmp = scap_cb + scb_cnt; + scb_cf = scb_tmp + tdata->sc_node.node_num; + /* 1:waterproof 0:non-waterproof */ + ret = fts_test_write_reg(FACTORY_REG_MC_SC_MODE, HIGH_SENSITIVITY); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set mc_sc mode fail\n"); + return ret; + } + + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + return ret; + } + + ret = get_cb_ft5662(scb_tmp, byte_num, false); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read sc_cb fail,ret=%d\n", ret); + return ret; + } + + ret = fts_test_write_reg(FACTROY_REG_CB_BUF_SEL, 1); + if (ret) { + FTS_TEST_SAVE_ERR("restore sc mode fail,ret=%d\n", ret); + return ret; + } + + ret = get_cb_ft5662(scb_cf, byte_num, true); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get sc_cb fail,ret=%d\n", ret); + return ret; + } + + ret = fts_test_write_reg(FACTROY_REG_CB_BUF_SEL, 0); + if (ret) { + FTS_TEST_SAVE_ERR("restore sc mode fail,ret=%d\n", ret); + return ret; + } + + /* show Scap CB */ + FTS_TEST_SAVE_INFO("scap_cb in high mode:\n"); + show_data_mc_sc(scb_tmp); + FTS_TEST_SAVE_INFO("scap_cb_cf:\n"); + show_data_mc_sc(scb_cf); + + /* compare */ + tx_check = ((hov_high == 1) || (hov_high == 3)); + rx_check = ((hov_high == 2) || (hov_high == 3)); + for (i = 0; i < tdata->sc_node.node_num; i++) { + if (0 == tdata->node_valid_sc[i]) + continue; + + if ((i == 0) || (i == tdata->sc_node.rx_num)) + continue; + + if ((rx_check && (i < tdata->sc_node.rx_num)) || + (tx_check && (i >= tdata->sc_node.rx_num))) { + if ((scb_tmp[i] < thr->scap_cb_hi_min[i]) || + (scb_tmp[i] > thr->scap_cb_hi_max[i])) { + FTS_TEST_SAVE_ERR("test fail,CH%d=%5d,range=(%5d,%5d)\n", + i + 1, scb_tmp[i], + thr->scap_cb_hi_min[i], + thr->scap_cb_hi_max[i]); + tmp3_result = false; + } + + if ((scb_cf[i] < thr->scap_cb_hi_cf_min[i]) || + (scb_cf[i] > thr->scap_cb_hi_cf_max[i])) { + FTS_TEST_SAVE_ERR("test2 fail,CH%d=%5d,range=(%5d,%5d)\n", + i + 1, scb_cf[i], + thr->scap_cb_hi_cf_min[i], + thr->scap_cb_hi_cf_max[i]); + tmp3_result = false; + } + } + } + + scb_cnt += tdata->sc_node.node_num * 2; + } + + *result = tmp_result && tmp2_result && tmp3_result; + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int scap_cb_ccbypass(struct fts_test *tdata, int *scap_cb, bool *result) +{ + int ret = 0; + int i = 0; + u8 wc_sel = 0; + u8 hc_sel = 0; + u8 hov_high = 0; + u8 scap_gcb_tx = 0; + u8 scap_gcb_rx = 0; + int byte_num = tdata->sc_node.node_num; + bool tmp_result = false; + bool tmp2_result = false; + bool tmp3_result = false; + bool fw_wp_check = false; + bool tx_check = false; + bool rx_check = false; + int *scb_tmp = NULL; + int scb_cnt = 0; + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + + FTS_TEST_FUNC_ENTER(); + if (!thr->scap_cb_on_min || !thr->scap_cb_on_max + || !thr->scap_cb_off_min || !thr->scap_cb_off_max + || !thr->scap_cb_hi_min || !thr->scap_cb_hi_max) { + FTS_TEST_SAVE_ERR("scap_cb_on/off/hi_min/max is null\n"); + ret = -EINVAL; + return ret; + } + + /* get waterproof channel select */ + ret = fts_test_read_reg(FACTORY_REG_WC_SEL, &wc_sel); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read water_channel_sel fail,ret=%d\n", ret); + return ret; + } + + ret = fts_test_read_reg(FACTORY_REG_HC_SEL, &hc_sel); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read high_channel_sel fail,ret=%d\n", ret); + return ret; + } + + /* water proof on check */ + tmp_result = true; + fw_wp_check = get_fw_wp(wc_sel, WATER_PROOF_ON); + if (thr->basic.scap_cb_wp_on_check && fw_wp_check) { + tdata->csv_item_cnt += 2; + tdata->csv_item_scb |= 0x01; + scb_tmp = scap_cb + scb_cnt; + /* 1:waterproof 0:non-waterproof */ + ret = fts_test_write_reg(FACTORY_REG_MC_SC_MODE, WATER_PROOF_ON); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set mc_sc mode fail\n"); + return ret; + } + + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + return ret; + } + + ret = get_cb_ft5662(scb_tmp, byte_num, false); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read sc_cb fail,ret=%d\n", ret); + return ret; + } + + ret = fts_test_read_reg(FACTROY_REG_SCAP_GCB_RX, &scap_gcb_rx); + if (ret) { + FTS_TEST_SAVE_ERR("read GCB_RX fail,ret=%d\n", ret); + return ret; + } + + ret = fts_test_read_reg(FACTROY_REG_SCAP_GCB_TX, &scap_gcb_tx); + if (ret) { + FTS_TEST_SAVE_ERR("read GCB_TX fail,ret=%d\n", ret); + return ret; + } + scb_tmp[tdata->sc_node.node_num] = scap_gcb_rx; + scb_tmp[tdata->sc_node.node_num + tdata->sc_node.rx_num] = scap_gcb_tx; + + /* show Scap CB */ + FTS_TEST_SAVE_INFO("scap_cb in waterproof on mode:\n"); + show_data_mc_sc(scb_tmp); + FTS_TEST_SAVE_INFO("GCB RX:%d,TX:%d\n", scap_gcb_rx, scap_gcb_tx); + + /* compare */ + tx_check = get_fw_wp(wc_sel, WATER_PROOF_ON_TX); + rx_check = get_fw_wp(wc_sel, WATER_PROOF_ON_RX); + for (i = 0; i < tdata->sc_node.node_num; i++) { + if (0 == tdata->node_valid_sc[i]) + continue; + + if ((rx_check && (i < tdata->sc_node.rx_num)) || + (tx_check && (i >= tdata->sc_node.rx_num))) { + if ((scb_tmp[i] < thr->scap_cb_on_min[i]) || + (scb_tmp[i] > thr->scap_cb_on_max[i])) { + FTS_TEST_SAVE_ERR("test fail,CH%d=%5d,range=(%5d,%5d)\n", + i + 1, scb_tmp[i], + thr->scap_cb_on_min[i], + thr->scap_cb_on_max[i]); + tmp_result = false; + } + } + } + + if (rx_check && ((scap_gcb_rx < thr->basic.scap_cb_on_gcb_min) || + (scap_gcb_rx > thr->basic.scap_cb_on_gcb_max))) { + FTS_TEST_SAVE_ERR("test fail,gcb_rx:%5d,range=(%5d,%5d)\n", + scap_gcb_rx, + thr->basic.scap_cb_on_gcb_min, + thr->basic.scap_cb_on_gcb_max); + tmp_result = false; + } + + if (tx_check && ((scap_gcb_tx < thr->basic.scap_cb_on_gcb_min) || + (scap_gcb_tx > thr->basic.scap_cb_on_gcb_max))) { + FTS_TEST_SAVE_ERR("test fail,gcb_tx:%5d,range=(%5d,%5d)\n", + scap_gcb_tx, + thr->basic.scap_cb_on_gcb_min, + thr->basic.scap_cb_on_gcb_max); + tmp_result = false; + } + + scb_cnt += tdata->sc_node.node_num * 2; + } + + /* water proof off check */ + tmp2_result = true; + fw_wp_check = get_fw_wp(wc_sel, WATER_PROOF_OFF); + if (thr->basic.scap_cb_wp_off_check && fw_wp_check) { + tdata->csv_item_cnt += 2; + tdata->csv_item_scb |= 0x02; + scb_tmp = scap_cb + scb_cnt; + /* 1:waterproof 0:non-waterproof */ + ret = fts_test_write_reg(FACTORY_REG_MC_SC_MODE, WATER_PROOF_OFF); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set mc_sc mode fail\n"); + return ret; + } + + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + return ret; + } + + ret = get_cb_ft5662(scb_tmp, byte_num, false); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read sc_cb fail,ret=%d\n", ret); + return ret; + } + + ret = fts_test_read_reg(FACTROY_REG_SCAP_GCB_RX, &scap_gcb_rx); + if (ret) { + FTS_TEST_SAVE_ERR("read GCB_RX fail,ret=%d\n", ret); + return ret; + } + + ret = fts_test_read_reg(FACTROY_REG_SCAP_GCB_TX, &scap_gcb_tx); + if (ret) { + FTS_TEST_SAVE_ERR("read GCB_TX fail,ret=%d\n", ret); + return ret; + } + scb_tmp[tdata->sc_node.node_num] = scap_gcb_rx; + scb_tmp[tdata->sc_node.node_num + tdata->sc_node.rx_num] = scap_gcb_tx; + + /* show Scap CB */ + FTS_TEST_SAVE_INFO("scap_cb in waterproof off mode:\n"); + show_data_mc_sc(scb_tmp); + FTS_TEST_SAVE_INFO("GCB RX:%d,TX:%d\n", scap_gcb_rx, scap_gcb_tx); + + /* compare */ + tx_check = get_fw_wp(wc_sel, WATER_PROOF_OFF_TX); + rx_check = get_fw_wp(wc_sel, WATER_PROOF_OFF_RX); + for (i = 0; i < tdata->sc_node.node_num; i++) { + if (0 == tdata->node_valid_sc[i]) + continue; + + if ((rx_check && (i < tdata->sc_node.rx_num)) || + (tx_check && (i >= tdata->sc_node.rx_num))) { + if ((scb_tmp[i] < thr->scap_cb_off_min[i]) || + (scb_tmp[i] > thr->scap_cb_off_max[i])) { + FTS_TEST_SAVE_ERR("test fail,CH%d=%5d,range=(%5d,%5d)\n", + i + 1, scb_tmp[i], + thr->scap_cb_off_min[i], + thr->scap_cb_off_max[i]); + tmp2_result = false; + } + } + } + + if (rx_check && ((scap_gcb_rx < thr->basic.scap_cb_off_gcb_min) || + (scap_gcb_rx > thr->basic.scap_cb_off_gcb_max))) { + FTS_TEST_SAVE_ERR("test fail,gcb_rx:%5d,range=(%5d,%5d)\n", + scap_gcb_rx, + thr->basic.scap_cb_off_gcb_min, + thr->basic.scap_cb_off_gcb_max); + tmp2_result = false; + } + + if (tx_check && ((scap_gcb_tx < thr->basic.scap_cb_off_gcb_min) || + (scap_gcb_tx > thr->basic.scap_cb_off_gcb_max))) { + FTS_TEST_SAVE_ERR("test fail,gcb_tx:%5d,range=(%5d,%5d)\n", + scap_gcb_tx, + thr->basic.scap_cb_off_gcb_min, + thr->basic.scap_cb_off_gcb_max); + tmp2_result = false; + } + + scb_cnt += tdata->sc_node.node_num * 2; + } + + /*high mode*/ + tmp3_result = true; + hov_high = (hc_sel & 0x03); + if (thr->basic.scap_cb_hi_check && hov_high) { + tdata->csv_item_cnt += 2; + tdata->csv_item_scb |= 0x04; + scb_tmp = scap_cb + scb_cnt; + /* 1:waterproof 0:non-waterproof */ + ret = fts_test_write_reg(FACTORY_REG_MC_SC_MODE, HIGH_SENSITIVITY); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set mc_sc mode fail\n"); + return ret; + } + + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + return ret; + } + + ret = get_cb_ft5662(scb_tmp, byte_num, false); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read sc_cb fail,ret=%d\n", ret); + return ret; + } + + ret = fts_test_read_reg(FACTROY_REG_SCAP_GCB_RX, &scap_gcb_rx); + if (ret) { + FTS_TEST_SAVE_ERR("read GCB_RX fail,ret=%d\n", ret); + return ret; + } + + ret = fts_test_read_reg(FACTROY_REG_SCAP_GCB_TX, &scap_gcb_tx); + if (ret) { + FTS_TEST_SAVE_ERR("read GCB_TX fail,ret=%d\n", ret); + return ret; + } + scb_tmp[tdata->sc_node.node_num] = scap_gcb_rx; + scb_tmp[tdata->sc_node.node_num + tdata->sc_node.rx_num] = scap_gcb_tx; + + /* show Scap CB */ + FTS_TEST_SAVE_INFO("scap_cb in high mode:\n"); + show_data_mc_sc(scb_tmp); + FTS_TEST_SAVE_INFO("GCB RX:%d,TX:%d\n", scap_gcb_rx, scap_gcb_tx); + + /* compare */ + tx_check = ((hov_high == 1) || (hov_high == 3)); + rx_check = ((hov_high == 2) || (hov_high == 3)); + for (i = 0; i < tdata->sc_node.node_num; i++) { + if (0 == tdata->node_valid_sc[i]) + continue; + + if ((rx_check && (i < tdata->sc_node.rx_num)) || + (tx_check && (i >= tdata->sc_node.rx_num))) { + if ((scb_tmp[i] < thr->scap_cb_hi_min[i]) || + (scb_tmp[i] > thr->scap_cb_hi_max[i])) { + FTS_TEST_SAVE_ERR("test fail,CH%d=%5d,range=(%5d,%5d)\n", + i + 1, scb_tmp[i], + thr->scap_cb_hi_min[i], + thr->scap_cb_hi_max[i]); + tmp3_result = false; + } + } + } + + if (rx_check && ((scap_gcb_rx < thr->basic.scap_cb_hi_gcb_min) || + (scap_gcb_rx > thr->basic.scap_cb_hi_gcb_max))) { + FTS_TEST_SAVE_ERR("test fail,gcb_rx:%5d,range=(%5d,%5d)\n", + scap_gcb_rx, + thr->basic.scap_cb_hi_gcb_min, + thr->basic.scap_cb_hi_gcb_max); + tmp3_result = false; + } + + if (tx_check && ((scap_gcb_tx < thr->basic.scap_cb_hi_gcb_min) || + (scap_gcb_tx > thr->basic.scap_cb_hi_gcb_max))) { + FTS_TEST_SAVE_ERR("test fail,gcb_tx:%5d,range=(%5d,%5d)\n", + scap_gcb_tx, + thr->basic.scap_cb_hi_gcb_min, + thr->basic.scap_cb_hi_gcb_max); + tmp3_result = false; + } + + scb_cnt += tdata->sc_node.node_num * 2; + } + + *result = tmp_result && tmp2_result && tmp3_result; + FTS_TEST_FUNC_EXIT(); + return ret; +} + +/* + * start_scan - start to scan a frame + */ +static int start_scan_ft5662(int frame_num) +{ + int ret = 0; + u8 addr = 0; + u8 val = 0; + u8 finish_val = 0; + int times = 0; + struct fts_test *tdata = fts_ftest; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("test/func is null\n"); + return -EINVAL; + } + + addr = DEVIDE_MODE_ADDR; + val = 0xC0; + finish_val = 0x40; + + /* write register to start scan */ + ret = fts_test_write_reg(addr, val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write start scan mode fail\n"); + return ret; + } + + sys_delay(frame_num * FACTORY_TEST_DELAY / 2); + /* Wait for the scan to complete */ + while (times++ < 100) { + sys_delay(FACTORY_TEST_DELAY); + + ret = fts_test_read_reg(addr, &val); + if ((ret >= 0) && (val == finish_val)) { + break; + } else + FTS_TEST_DBG("reg%x=%x,retry:%d", addr, val, times); + } + + if (times >= 100) { + FTS_TEST_SAVE_ERR("scan timeout\n"); + return -EIO; + } + + return 0; +} + +static int get_noise_ft5662(struct fts_test *tdata, int *noise_data, u8 fre) +{ + int ret = 0; + int byte_num = 0; + + ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, fre); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set frequecy fail,ret=%d\n", ret); + return ret; + } + + /* wait fw state update */ + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + return ret; + } + + ret = start_scan_ft5662(tdata->ic.mc_sc.thr.basic.noise_framenum); + if (ret < 0) { + FTS_TEST_SAVE_ERR("start_scan fail,ret=%d\n", ret); + return ret; + } + + ret = fts_test_write_reg(0x01, 0xAA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 0x01 fail,ret=%d\n", ret); + return ret; + } + + byte_num = tdata->node.node_num * 2; + ret = read_mass_data(0xCE, byte_num, noise_data); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read noise fail\n"); + return ret; + } + + return 0; +} + +static void show_null_noise(struct fts_test *tdata, int *null_noise) +{ + int i = 0; + int j = 0; + int cnt = 0; + char tmpbuf[512] = { 0 }; + + /*show noise*/ + FTS_TEST_INFO("null noise:%d", null_noise[0]); + FTS_TEST_SAVE_INFO("null noise:%d\n", null_noise[0]); + for (i = 0; i < tdata->fre_num; i++) { + for (j = 0; j < tdata->node.rx_num; j++) { + cnt += snprintf(tmpbuf + cnt, 512 - cnt, "%5d,", \ + null_noise[i * tdata->node.rx_num + j + 1]); + } + cnt = 0; + FTS_TEST_INFO("%s", tmpbuf); + FTS_TEST_SAVE_INFO("%s\n", tmpbuf); + } +} + +static int get_null_noise(struct fts_test *tdata) +{ + int ret = 0; + int *null_noise; + int null_byte_num = 0; + + null_byte_num = tdata->node.rx_num * tdata->fre_num + 1; + FTS_TEST_INFO("null noise num:%d", null_byte_num); + null_noise = kzalloc(null_byte_num * sizeof(int), GFP_KERNEL); + if (!null_noise) { + FTS_TEST_ERROR("null_noise malloc fail"); + return -ENOMEM; + } + + ret = fts_test_write_reg(0x01, 0xB0); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 0x01 fail,ret=%d\n", ret); + goto err_null_noise; + } + + ret = read_mass_data(0xCE, null_byte_num * 2, null_noise); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read null noise fail\n"); + goto err_null_noise; + } + + tdata->null_noise_max = null_noise[0]; + show_null_noise(tdata, null_noise); + ret = 0; + +err_null_noise: + kfree(null_noise); + null_noise = NULL; + return ret; +} + +static int ft5662_rawdata_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + int i = 0; + int *rawdata = NULL; + u8 fre = 0; + u8 data_type = 0; + bool result = false; + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: rawdata test\n"); + rawdata = tdata->item1_data; + tdata->csv_item_cnt++; + + if (!rawdata || !thr->rawdata_h_min || !thr->rawdata_h_max) { + FTS_TEST_SAVE_ERR("rawdata_h_min/max is null\n"); + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("failed to enter factory mode,ret=%d\n", ret); + goto test_err; + } + + /* rawdata test in mapping mode */ + ret = mapping_switch(MAPPING); + if (ret < 0) { + FTS_TEST_SAVE_ERR("switch mapping fail,ret=%d\n", ret); + goto test_err; + } + + /* save origin value */ + ret = fts_test_read_reg(FACTORY_REG_FRE_LIST, &fre); + if (ret) { + FTS_TEST_SAVE_ERR("read 0x0A fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_DATA_TYPE, &data_type); + if (ret) { + FTS_TEST_SAVE_ERR("read 0x5B fail,ret=%d\n", ret); + goto test_err; + } + + /* set frequecy high */ + ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, 0x81); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set frequecy fail,ret=%d\n", ret); + goto restore_reg; + } + + /* wait fw state update */ + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + goto restore_reg; + } + + ret = fts_test_write_reg(FACTORY_REG_DATA_TYPE, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set raw type fail,ret=%d\n", ret); + goto restore_reg; + } + + /*********************GET RAWDATA*********************/ + for (i = 0; i < 3; i++) { + /* lost 3 frames, in order to obtain stable data */ + ret = get_rawdata(rawdata); + } + if (ret < 0) { + FTS_TEST_SAVE_ERR("get rawdata fail,ret=%d\n", ret); + goto restore_reg; + } + + /* show test data */ + show_data(rawdata, false); + + /* compare */ + result = compare_array(rawdata, + thr->rawdata_h_min, + thr->rawdata_h_max, + false); + +restore_reg: + /* set the origin value */ + ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, fre); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore reg0A fail,ret=%d\n", ret); + } + + /* wait fw state update */ + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + } + + ret = fts_test_write_reg(FACTORY_REG_DATA_TYPE, data_type); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set raw type fail,ret=%d\n", ret); + } + +test_err: + if (result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ rawdata test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("------ rawdata test NG\n"); + } + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft5662_uniformity_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + int index = 0; + int row = 0; + int col = 1; + int i = 0; + int deviation = 0; + int max = 0; + int min = 0; + int uniform = 0; + int *rawdata = NULL; + int *rawdata_linearity = NULL; + int *rl_tmp = NULL; + int rl_cnt = 0; + int offset = 0; + int offset2 = 0; + int tx_num = 0; + int rx_num = 0; + u8 fre = 0; + u8 data_type = 0; + struct mc_sc_threshold *thr = &fts_ftest->ic.mc_sc.thr; + bool result = false; + bool result2 = false; + bool result3 = false; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: rawdata unfiormity test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + rawdata = tdata->buffer; + tx_num = tdata->node.tx_num; + rx_num = tdata->node.rx_num; + + rawdata_linearity = tdata->item2_data; + if (!rawdata_linearity) { + FTS_TEST_SAVE_ERR("rawdata_linearity buffer fail"); + ret = -ENOMEM; + goto test_err; + } + + if (!thr->tx_linearity_max || !thr->rx_linearity_max + || !tdata->node_valid) { + FTS_TEST_SAVE_ERR("tx/rx_lmax/node_valid is null\n"); + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("failed to enter factory mode,ret=%d\n", ret); + goto test_err; + } + + /* rawdata unfiormity test in mapping mode */ + ret = mapping_switch(MAPPING); + if (ret) { + FTS_TEST_SAVE_ERR("failed to switch_to_mapping,ret=%d", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_FRE_LIST, &fre); + if (ret) { + FTS_TEST_SAVE_ERR("read 0x0A fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_DATA_TYPE, &data_type); + if (ret) { + FTS_TEST_SAVE_ERR("read 0x5B fail,ret=%d\n", ret); + goto test_err; + } + + /* set frequecy high */ + ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, 0x81); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set frequecy fail,ret=%d\n", ret); + goto restore_reg; + } + + /* wait fw state update */ + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + goto restore_reg; + } + + ret = fts_test_write_reg(FACTORY_REG_DATA_TYPE, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set raw type fail,ret=%d\n", ret); + goto restore_reg; + } + + /* change register value before,need to lose 3 frame data */ + for (index = 0; index < 3; ++index) { + ret = get_rawdata(rawdata); + } + if (ret < 0) { + FTS_TEST_SAVE_ERR("get rawdata fail,ret=%d\n", ret); + goto restore_reg; + } + print_buffer(rawdata, tdata->node.node_num, tdata->node.rx_num); + + result = true; + if (thr->basic.uniformity_check_tx) { + FTS_TEST_SAVE_INFO("Check Tx Linearity\n"); + tdata->csv_item_cnt++; + rl_tmp = rawdata_linearity + rl_cnt; + for (row = 0; row < tx_num; row++) { + for (col = 1; col < rx_num; col++) { + offset = row * rx_num + col; + offset2 = row * rx_num + col - 1; + deviation = abs( rawdata[offset] - rawdata[offset2]); + max = max(rawdata[offset], rawdata[offset2]); + max = max ? max : 1; + rl_tmp[offset] = 100 * deviation / max; + } + } + /*show data in result.txt*/ + FTS_TEST_SAVE_INFO(" Tx Linearity:\n"); + show_data(rl_tmp, false); + FTS_TEST_SAVE_INFO("\n" ); + + /* compare */ + result = compare_array(rl_tmp, + thr->tx_linearity_min, + thr->tx_linearity_max, + false); + + rl_cnt += tdata->node.node_num; + } + + result2 = true; + if (thr->basic.uniformity_check_rx) { + FTS_TEST_SAVE_INFO("Check Rx Linearity\n"); + tdata->csv_item_cnt++; + rl_tmp = rawdata_linearity + rl_cnt; + for (row = 1; row < tx_num; row++) { + for (col = 0; col < rx_num; col++) { + offset = row * rx_num + col; + offset2 = (row - 1) * rx_num + col; + deviation = abs(rawdata[offset] - rawdata[offset2]); + max = max(rawdata[offset], rawdata[offset2]); + max = max ? max : 1; + rl_tmp[offset] = 100 * deviation / max; + } + } + + FTS_TEST_SAVE_INFO("Rx Linearity:\n"); + show_data(rl_tmp, false); + FTS_TEST_SAVE_INFO("\n"); + + /* compare */ + result2 = compare_array(rl_tmp, + thr->rx_linearity_min, + thr->rx_linearity_max, + false); + rl_cnt += tdata->node.node_num; + } + + result3 = true; + if (thr->basic.uniformity_check_min_max) { + FTS_TEST_SAVE_INFO("Check Min/Max\n") ; + min = 100000; + max = -100000; + for (i = 0; i < tdata->node.node_num; i++) { + if (0 == tdata->node_valid[i]) + continue; + min = min(min, rawdata[i]); + max = max(max, rawdata[i]); + } + max = !max ? 1 : max; + uniform = 100 * abs(min) / abs(max); + + FTS_TEST_SAVE_INFO("min:%d, max:%d, get value of min/max:%d\n", + min, max, uniform); + if (uniform < thr->basic.uniformity_min_max_hole) { + result3 = false; + FTS_TEST_SAVE_ERR("min_max out of range, set value: %d\n", + thr->basic.uniformity_min_max_hole); + } + } + +restore_reg: + /* set the origin value */ + ret = fts_test_write_reg(FACTORY_REG_DATA_TYPE, data_type); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set raw type fail,ret=%d\n", ret); + } + + ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, fre); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore 0x0A fail,ret=%d\n", ret); + } + + /* wait fw state update */ + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + } + +test_err: + if (result && result2 && result3) { + *test_result = true; + FTS_TEST_SAVE_INFO("uniformity test is OK\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_ERR("uniformity test is NG\n"); + } + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft5662_scap_cb_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + u8 sc_mode = 0; + u8 scap_cfg = 0; + bool tmp_result = false; + bool cc_en = false; + int *scap_cb = NULL; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: Scap CB Test\n"); + scap_cb = tdata->item3_data; + + if (!scap_cb) { + FTS_TEST_SAVE_ERR("scap_cb fails"); + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + /* SCAP CB is in no-mapping mode */ + ret = mapping_switch(NO_MAPPING); + if (ret < 0) { + FTS_TEST_SAVE_ERR("switch no-mapping fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_MC_SC_MODE, &sc_mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read sc_mode fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTROY_REG_SCAP_CFG, &scap_cfg); + if (ret) { + FTS_TEST_SAVE_ERR("read reg58 fail,ret=%d\n", ret); + goto test_err; + } + + cc_en = !(scap_cfg & 0x80); + FTS_TEST_INFO("cc_en:%d", cc_en); + if (cc_en) { + ret = scap_cb_cc(tdata, scap_cb, &tmp_result); + } else { + ret = scap_cb_ccbypass(tdata, scap_cb, &tmp_result); + } + if (ret < 0) { + FTS_TEST_SAVE_ERR("scap_cb fail,ret:%d", ret); + } + + ret = fts_test_write_reg(FACTORY_REG_MC_SC_MODE, sc_mode);/* set the origin value */ + if (ret) { + FTS_TEST_SAVE_ERR("restore sc mode fail,ret=%d\n", ret); + goto test_err; + } + + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + } + +test_err: + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("\n------ scap cb test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_ERR("\n------ scap cb test NG\n"); + } + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft5662_scap_rawdata_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + int i = 0; + bool tmp_result = false; + bool tmp2_result = false; + bool tmp3_result = false; + bool fw_wp_check = false; + bool tx_check = false; + bool rx_check = false; + bool cc_en = false; + int *scap_rawdata = NULL; + int *srawdata_tmp = NULL; + int srawdata_cnt = 0; + u8 wc_sel = 0; + u8 hc_sel = 0; + u8 hov_high = 0; + u8 data_type = 0; + u8 scap_cfg = 0; + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: Scap Rawdata Test\n"); + scap_rawdata = tdata->item4_data; + + if (!scap_rawdata) { + FTS_TEST_SAVE_ERR("scap rawdata fails"); + ret = -EINVAL; + goto test_err; + } + + if (!thr->scap_rawdata_on_min || !thr->scap_rawdata_on_max + || !thr->scap_rawdata_off_min || !thr->scap_rawdata_off_max + || !thr->scap_rawdata_hi_min || !thr->scap_rawdata_hi_max) { + FTS_TEST_SAVE_ERR("scap_rawdata_on/off/hi_min/max is null\n"); + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + /* SCAP RAWDATA is in no-mapping mode */ + ret = mapping_switch(NO_MAPPING); + if (ret < 0) { + FTS_TEST_SAVE_ERR("switch no-mapping fail,ret=%d\n", ret); + goto test_err; + } + + /* get waterproof channel select */ + ret = fts_test_read_reg(FACTORY_REG_WC_SEL, &wc_sel); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read water_channel_sel fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_HC_SEL, &hc_sel); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read high_channel_sel fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_DATA_TYPE, &data_type); + if (ret) { + FTS_TEST_SAVE_ERR("read 0x5B fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTROY_REG_SCAP_CFG, &scap_cfg); + if (ret) { + FTS_TEST_SAVE_ERR("read reg58 fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_write_reg(FACTORY_REG_DATA_TYPE, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set raw type fail,ret=%d\n", ret); + goto restore_reg; + } + + /* scan rawdata */ + ret = start_scan(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("scan scap rawdata fail\n"); + goto restore_reg; + } + + ret = start_scan(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("scan scap rawdata(2) fail\n"); + goto restore_reg; + } + + cc_en = !(scap_cfg & 0x80); + FTS_TEST_INFO("cc_en:%d", cc_en); + + /* water proof on check */ + tmp_result = true; + fw_wp_check = get_fw_wp(wc_sel, WATER_PROOF_ON); + if (thr->basic.scap_rawdata_wp_on_check && fw_wp_check) { + tdata->csv_item_cnt++; + tdata->csv_item_sraw |= 0x01; + srawdata_tmp = scap_rawdata + srawdata_cnt; + ret = get_rawdata_mc_sc(WATER_PROOF_ON, srawdata_tmp); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get scap(WP_ON) rawdata fail\n"); + goto restore_reg; + } + + FTS_TEST_SAVE_INFO("scap_rawdata in waterproof on mode:\n"); + show_data_mc_sc(srawdata_tmp); + + /* compare */ + tx_check = get_fw_wp(wc_sel, WATER_PROOF_ON_TX); + rx_check = get_fw_wp(wc_sel, WATER_PROOF_ON_RX); + for (i = 0; i < tdata->sc_node.node_num; i++) { + if (0 == tdata->node_valid_sc[i]) + continue; + + if (cc_en && ((i == 0) || (i == tdata->sc_node.rx_num))) + continue; + + if ((rx_check && (i < tdata->sc_node.rx_num)) || + (tx_check && (i >= tdata->sc_node.rx_num))) { + if ((srawdata_tmp[i] < thr->scap_rawdata_on_min[i]) || + (srawdata_tmp[i] > thr->scap_rawdata_on_max[i])) { + FTS_TEST_SAVE_ERR("test fail,CH%d=%5d,range=(%5d,%5d)\n", + i + 1, srawdata_tmp[i], + thr->scap_rawdata_on_min[i], + thr->scap_rawdata_on_max[i]); + tmp_result = false; + } + } + } + + srawdata_cnt += tdata->sc_node.node_num; + } + + /* water proof off check */ + tmp2_result = true; + fw_wp_check = get_fw_wp(wc_sel, WATER_PROOF_OFF); + if (thr->basic.scap_rawdata_wp_off_check && fw_wp_check) { + tdata->csv_item_cnt++; + tdata->csv_item_sraw |= 0x02; + srawdata_tmp = scap_rawdata + srawdata_cnt; + ret = get_rawdata_mc_sc(WATER_PROOF_OFF, srawdata_tmp); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get scap(WP_OFF) rawdata fail\n"); + goto restore_reg; + } + + FTS_TEST_SAVE_INFO("scap_rawdata in waterproof off mode:\n"); + show_data_mc_sc(srawdata_tmp); + + /* compare */ + tx_check = get_fw_wp(wc_sel, WATER_PROOF_OFF_TX); + rx_check = get_fw_wp(wc_sel, WATER_PROOF_OFF_RX); + for (i = 0; i < tdata->sc_node.node_num; i++) { + if (0 == tdata->node_valid_sc[i]) + continue; + + if (cc_en && ((i == 0) || (i == tdata->sc_node.rx_num))) + continue; + + if ((rx_check && (i < tdata->sc_node.rx_num)) || + (tx_check && (i >= tdata->sc_node.rx_num))) { + if ((srawdata_tmp[i] < thr->scap_rawdata_off_min[i]) || + (srawdata_tmp[i] > thr->scap_rawdata_off_max[i])) { + FTS_TEST_SAVE_ERR("test fail,CH%d=%5d,range=(%5d,%5d)\n", + i + 1, srawdata_tmp[i], + thr->scap_rawdata_off_min[i], + thr->scap_rawdata_off_max[i]); + tmp2_result = false; + } + } + } + srawdata_cnt += tdata->sc_node.node_num; + } + + /*high mode*/ + tmp3_result = true; + hov_high = (hc_sel & 0x03); + if (thr->basic.scap_rawdata_hi_check && hov_high) { + tdata->csv_item_cnt++; + tdata->csv_item_sraw |= 0x04; + srawdata_tmp = scap_rawdata + srawdata_cnt; + ret = get_rawdata_mc_sc(HIGH_SENSITIVITY, srawdata_tmp); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get scap(HS) rawdata fail\n"); + goto restore_reg; + } + + FTS_TEST_SAVE_INFO("scap_rawdata in hs mode:\n"); + show_data_mc_sc(srawdata_tmp); + + /* compare */ + tx_check = ((hov_high == 1) || (hov_high == 3)); + rx_check = ((hov_high == 2) || (hov_high == 3)); + for (i = 0; i < tdata->sc_node.node_num; i++) { + if (0 == tdata->node_valid_sc[i]) + continue; + + if (cc_en && ((i == 0) || (i == tdata->sc_node.rx_num))) + continue; + + if ((rx_check && (i < tdata->sc_node.rx_num)) || + (tx_check && (i >= tdata->sc_node.rx_num))) { + if ((srawdata_tmp[i] < thr->scap_rawdata_hi_min[i]) || + (srawdata_tmp[i] > thr->scap_rawdata_hi_max[i])) { + FTS_TEST_SAVE_ERR("test fail,CH%d=%5d,range=(%5d,%5d)\n", + i + 1, srawdata_tmp[i], + thr->scap_rawdata_hi_min[i], + thr->scap_rawdata_hi_max[i]); + tmp3_result = false; + } + } + } + + srawdata_cnt += tdata->sc_node.node_num; + } + +restore_reg: + ret = fts_test_write_reg(FACTORY_REG_DATA_TYPE, data_type); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set raw type fail,ret=%d\n", ret); + } + +test_err: + if (tmp_result && tmp2_result && tmp3_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("\n------ scap rawdata test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("\n------ scap rawdata test NG\n"); + } + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft5662_short_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + int ch_num = 0; + int short_res[SC_NUM_MAX + 1] = { 0 }; + u8 ab_ch[SC_NUM_MAX + 1] = { 0 }; + bool ca_result = false; + bool cg_result = false; + bool cc_result = false; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: Short Test\n"); + ch_num = tdata->sc_node.tx_num + tdata->sc_node.rx_num; + + if (ch_num >= SC_NUM_MAX) { + FTS_TEST_SAVE_ERR("sc_node ch_num(%d)>max(%d)", ch_num, SC_NUM_MAX); + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + /* short is in no-mapping mode */ + ret = mapping_switch(NO_MAPPING); + if (ret < 0) { + FTS_TEST_SAVE_ERR("switch no-mapping fail,ret=%d\n", ret); + goto test_err; + } + + /* get short resistance and exceptional channel */ + ret = short_test_ch_to_all(tdata, short_res, ab_ch, &ca_result); + if (ret < 0) { + FTS_TEST_SAVE_ERR("short test of channel to all fails\n"); + goto test_err; + } + + if (!ca_result) { + /*weak short fail, get short values*/ + ret = short_test_ch_to_gnd(tdata, short_res, ab_ch, &cg_result); + if (ret < 0) { + FTS_TEST_SAVE_ERR("short test of channel to gnd fails\n"); + goto test_err; + } + + ret = short_test_ch_to_ch(tdata, short_res, ab_ch, &cc_result); + if (ret < 0) { + FTS_TEST_SAVE_ERR("short test of channel to channel fails\n"); + goto test_err; + } + } + +test_err: + if (ca_result) { + FTS_TEST_SAVE_INFO("------ short test PASS\n"); + *test_result = true; + } else { + FTS_TEST_SAVE_ERR("------ short test NG\n"); + *test_result = false; + } + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft5662_panel_differ_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + bool tmp_result = false; + int i = 0; + u8 fre = 0; + u8 g_cb = 0; + u8 normalize = 0; + u8 data_type = 0; + int *panel_differ = NULL; + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: Panel Differ Test\n"); + panel_differ = tdata->item5_data; + tdata->csv_item_cnt++; + + if (!panel_differ || !thr->panel_differ_min || !thr->panel_differ_max) { + FTS_TEST_SAVE_ERR("panel_differ_h_min/max is null\n"); + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("failed to enter factory mode,ret=%d\n", ret); + goto test_err; + } + + /* panel differ test in mapping mode */ + ret = mapping_switch(MAPPING); + if (ret < 0) { + FTS_TEST_SAVE_ERR("switch mapping fail,ret=%d\n", ret); + goto test_err; + } + + /* save origin value */ + ret = fts_test_read_reg(FACTORY_REG_FRE_LIST, &fre); + if (ret) { + FTS_TEST_SAVE_ERR("read 0x0A fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_DATA_TYPE, &data_type); + if (ret) { + FTS_TEST_SAVE_ERR("read 0x5B fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_GCB, &g_cb); + if (ret) { + FTS_TEST_SAVE_ERR("read regBD fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_NORMALIZE, &normalize); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read normalize fail,ret=%d\n", ret); + goto test_err; + } + + /* set frequecy high */ + ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, 0x81); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set frequecy fail,ret=%d\n", ret); + goto restore_reg; + } + + /* wait fw state update */ + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + goto restore_reg; + } + + ret = fts_test_write_reg(FACTORY_REG_DATA_TYPE, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set raw type fail,ret=%d\n", ret); + goto restore_reg; + } + + ret = fts_test_write_reg(FACTORY_REG_GCB, 0x00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set fir fail,ret=%d\n", ret); + goto restore_reg; + } + + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + goto restore_reg; + } + + /* set to overall normalize */ + ret = fts_test_write_reg(FACTORY_REG_NORMALIZE, 0x00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write normalize fail,ret=%d\n", ret); + goto restore_reg; + } + + /* get rawdata */ + for (i = 0; i < 3; i++) { + ret = get_rawdata(panel_differ); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get rawdata fail\n"); + goto restore_reg; + } + } + + for (i = 0; i < tdata->node.node_num; i++) { + panel_differ[i] = panel_differ[i] / 10; + } + + /* show test data */ + show_data(panel_differ, false); + + /* compare */ + tmp_result = compare_array(panel_differ, + thr->panel_differ_min, + thr->panel_differ_max, + false); + +restore_reg: + /* set the origin value */ + ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, fre); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore 0x0A fail,ret=%d\n", ret); + } + + /* wait fw state update */ + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + } + + ret = fts_test_write_reg(FACTORY_REG_DATA_TYPE, data_type); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set raw type fail,ret=%d\n", ret); + } + + ret = fts_test_write_reg(FACTORY_REG_GCB, g_cb); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore 0xBD fail,ret=%d\n", ret); + } + + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + } + + ret = fts_test_write_reg(FACTORY_REG_NORMALIZE, normalize); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore normalize fail,ret=%d\n", ret); + } + +test_err: + /* result */ + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ panel differ test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_ERR("------ panel differ test NG\n"); + } + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft5662_noise_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + bool tmp_result = false; + int i = 0; + u8 fre = 0; + u8 reg1a_val = 0; + u8 reg1b_val = 0; + u8 touch_value = 0; + u8 data_sel = 0; + u8 frame_h_byte = 0; + u8 frame_l_byte = 0; +#ifndef CONFIG_FTS_NOISE_TEST_P2P + int nose_test_max = 0; +#endif + int *noise = NULL; + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: Noise Test\n"); + noise = tdata->item6_data; + tdata->csv_item_cnt++; + +#if defined(CONFIG_FTS_NOISE_TEST_P2P) + if (!noise || !thr->noise_min || !thr->noise_max) { + FTS_TEST_SAVE_ERR("noise/noise_min/noise_max is null\n"); +#else + if (!noise) { + FTS_TEST_SAVE_ERR("noise is null\n"); +#endif + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("failed to enter factory mode,ret=%d\n", ret); + goto test_err; + } + + /* panel differ test in mapping mode */ + ret = mapping_switch(MAPPING); + if (ret < 0) { + FTS_TEST_SAVE_ERR("switch mapping fail,ret=%d\n", ret); + goto test_err; + } + + /* save origin value */ + ret = fts_test_read_reg(0x0D, &touch_value); + if (ret) { + FTS_TEST_SAVE_ERR("read touch_value fail,ret=%d\n", ret); + goto test_err; + } +#if defined(CONFIG_FTS_NOISE_TEST_P2P) + FTS_TEST_INFO("noise(touch) value:%d", touch_value); + for (i = 0; i < tdata->node.node_num; i++) { + thr->noise_max[i] = touch_value * 4 * thr->noise_max[i] / 100; + } +#else + nose_test_max = touch_value * 4 * thr->basic.noise_max / 100; + FTS_TEST_INFO("noise max::%d,%d", nose_test_max, touch_value); +#endif + + ret = fts_test_read_reg(FACTORY_REG_FRE_LIST, &fre); + if (ret) { + FTS_TEST_SAVE_ERR("read 0x0A fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_DATA_SELECT, &data_sel); + if (ret) { + FTS_TEST_SAVE_ERR("read 0x06 fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(0x1A, ®1a_val); + if (ret) { + FTS_TEST_SAVE_ERR("read reg1a fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(0x1B, ®1b_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read reg1b fail,ret=%d\n", ret); + goto test_err; + } + FTS_TEST_INFO("fre:%d,data_sel:%d,reg1a:%d,reg1b:%d", fre, data_sel, reg1a_val, reg1b_val); + + + ret = fts_test_write_reg(FACTORY_REG_DATA_SELECT, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set data_sel fail,ret=%d\n", ret); + goto restore_reg; + } + + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + goto restore_reg; + } + + ret = fts_test_write_reg(0x1A, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set reg1A fail,ret=%d\n", ret); + goto restore_reg; + } + + frame_h_byte = BYTE_OFF_8(thr->basic.noise_framenum); + frame_l_byte = BYTE_OFF_0(thr->basic.noise_framenum); + FTS_TEST_INFO("noise frame num:%d,%d", frame_h_byte, frame_l_byte); + ret = fts_test_write_reg(0x1c, frame_h_byte); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 0x1c fail,ret=%d\n", ret); + goto restore_reg; + } + + ret = fts_test_write_reg(0x1d, frame_l_byte); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 0x1d fail,ret=%d\n", ret); + goto restore_reg; + } + + FTS_TEST_INFO("noise_mode = %d", thr->basic.noise_mode); + ret = fts_test_write_reg(0x1B, thr->basic.noise_mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set reg1B fail,ret=%d\n", ret); + goto restore_reg; + } + sys_delay(20); + + if (thr->basic.noise_polling) { + FTS_TEST_INFO("fre_num:%d", tdata->fre_num); + for (i = tdata->fre_num - 1; i > 0; i--) { + ret = get_noise_ft5662(tdata, &noise[tdata->node.node_num * i] , i); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get noise fails,ret=%d\n", ret); + goto restore_reg; + } + } + } + + ret = get_noise_ft5662(tdata, &noise[0], 0x00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get noise fails,ret=%d\n", ret); + goto restore_reg; + } + + /* show test data */ + show_data(noise, false); + + /* compare */ +#if defined(CONFIG_FTS_NOISE_TEST_P2P) + tmp_result = compare_array(noise, thr->noise_min, thr->noise_max, false); +#else + tmp_result = compare_data(noise, 0, nose_test_max, 0, 0, false); +#endif + + get_null_noise(tdata); + +restore_reg: + /* set the origin value */ + ret = fts_test_write_reg(0x1A, reg1a_val); + if (ret) { + FTS_TEST_SAVE_ERR("restore reg1a fail,ret=%d\n", ret); + } + + ret = fts_test_write_reg(0x1B, reg1b_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore reg1b fail,ret=%d\n", ret); + } + + ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, fre); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore 0x0A fail,ret=%d\n", ret); + } + + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + } + + ret = fts_test_write_reg(FACTORY_REG_DATA_SELECT, data_sel); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore data_sel fail,ret=%d\n", ret); + } + + ret = wait_state_update(TEST_RETVAL_AA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wait state update fail\n"); + } + +test_err: + /* result */ + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ noise test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_ERR("------ noise test NG\n"); + } + + FTS_TEST_FUNC_EXIT(); + return ret; +} + + +static int malloc_item_data(struct fts_test *tdata) +{ + tdata->item1_data = fts_malloc(tdata->node.node_num * sizeof(int)); + if (!tdata->item1_data) { + FTS_TEST_SAVE_ERR("memory malloc fails"); + return -ENOMEM; + } + + tdata->item2_data = fts_malloc(tdata->node.node_num * sizeof(int) * 2); + if (!tdata->item2_data) { + FTS_TEST_SAVE_ERR("memory malloc fails"); + return -ENOMEM; + } + + tdata->item3_data = fts_malloc(tdata->sc_node.node_num * sizeof(int) * 6); + if (!tdata->item3_data) { + FTS_TEST_SAVE_ERR("memory malloc fails"); + return -ENOMEM; + } + + tdata->item4_data = fts_malloc(tdata->sc_node.node_num * sizeof(int) * 3); + if (!tdata->item4_data) { + FTS_TEST_SAVE_ERR("memory malloc fails"); + return -ENOMEM; + } + + tdata->item5_data = fts_malloc(tdata->node.node_num * sizeof(int)); + if (!tdata->item5_data) { + FTS_TEST_SAVE_ERR("memory malloc fails"); + return -ENOMEM; + } + + tdata->item6_data = fts_malloc(tdata->node.node_num * sizeof(int) * tdata->fre_num); + if (!tdata->item6_data) { + FTS_TEST_SAVE_ERR("memory malloc fails"); + return -ENOMEM; + } + + return 0; +} + +static void free_item_data(struct fts_test *tdata) +{ + fts_free(tdata->item1_data); + fts_free(tdata->item2_data); + fts_free(tdata->item3_data); + fts_free(tdata->item4_data); + fts_free(tdata->item5_data); + fts_free(tdata->item6_data); +} + +static int start_test_ft5662(void) +{ + int ret = 0; + struct fts_test *tdata = fts_ftest; + struct mc_sc_testitem *test_item = &tdata->ic.mc_sc.u.item; + bool temp_result = false; + bool test_result = true; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_INFO("test item:0x%x", tdata->ic.mc_sc.u.tmp); + + ret = fts_test_read_reg(0x14, &tdata->fre_num); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read fre_num fails"); + return ret; + } + FTS_TEST_INFO("fre_num:%d", tdata->fre_num); + + ret = malloc_item_data(tdata); + if (ret < 0) { + FTS_TEST_SAVE_ERR("memory malloc fails"); + return -ENOMEM; + } + + /* rawdata test */ + if (true == test_item->rawdata_test) { + ret = ft5662_rawdata_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + if (true == test_item->rawdata_uniformity_test) { + ret = ft5662_uniformity_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* scap_cb test */ + if (true == test_item->scap_cb_test) { + ret = ft5662_scap_cb_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* scap_rawdata test */ + if (true == test_item->scap_rawdata_test) { + ret = ft5662_scap_rawdata_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* short test */ + if (true == test_item->short_test) { + ret = ft5662_short_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + /* panel differ test */ + if (true == test_item->panel_differ_test) { + ret = ft5662_panel_differ_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* noise test */ + if (true == test_item->noise_test) { + ret = ft5662_noise_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* restore mapping state */ + fts_test_write_reg(FACTORY_REG_NOMAPPING, tdata->mapping); + + FTS_TEST_FUNC_EXIT(); + return test_result; +} + +static int param_init_ft5662(void) +{ + struct mc_sc_threshold *thr = &fts_ftest->ic.mc_sc.thr; + + get_value_basic("SCapCbTest_ON_Global_Cb_Min", &thr->basic.scap_cb_on_gcb_min); + get_value_basic("SCapCbTest_ON_Global_Cb_Max", &thr->basic.scap_cb_on_gcb_max); + get_value_basic("SCapCbTest_OFF_Global_Cb_Min", &thr->basic.scap_cb_off_gcb_min); + get_value_basic("SCapCbTest_OFF_Global_Cb_Max", &thr->basic.scap_cb_off_gcb_max); + get_value_basic("SCapCbTest_High_Global_Cb_Min", &thr->basic.scap_cb_hi_gcb_min); + get_value_basic("SCapCbTest_High_Global_Cb_Max", &thr->basic.scap_cb_hi_gcb_max); + FTS_TEST_INFO("GCB,On(%d,%d),Off(%d,%d),High(%d,%d)", + thr->basic.scap_cb_on_gcb_min, thr->basic.scap_cb_on_gcb_max, + thr->basic.scap_cb_off_gcb_min, thr->basic.scap_cb_off_gcb_max, + thr->basic.scap_cb_hi_gcb_min, thr->basic.scap_cb_hi_gcb_max + ); + + get_value_basic("SCapCbTest_ON_Cf_Cb_Min", &thr->basic.scap_cb_on_cf_min); + get_value_basic("SCapCbTest_ON_Cf_Cb_Max", &thr->basic.scap_cb_on_cf_max); + get_value_basic("SCapCbTest_OFF_Cf_Cb_Min", &thr->basic.scap_cb_off_cf_min); + get_value_basic("SCapCbTest_OFF_Cf_Cb_Max", &thr->basic.scap_cb_off_cf_max); + get_value_basic("SCapCbTest_High_Cf_Cb_Min", &thr->basic.scap_cb_hi_cf_min); + get_value_basic("SCapCbTest_High_Cf_Cb_Max", &thr->basic.scap_cb_hi_cf_max); + + get_value_basic("NoiseTest_Max", &thr->basic.noise_max); + get_value_basic("NoiseTest_Frames", &thr->basic.noise_framenum); + get_value_basic("NoiseTest_FwNoiseMode", &thr->basic.noise_mode); + get_value_basic("Polling_Frequency", &thr->basic.noise_polling); + FTS_TEST_INFO("noise_max:%d,frame_num:%d,noise_mode:%d,polling:%d", thr->basic.noise_max, + thr->basic.noise_framenum, thr->basic.noise_mode, thr->basic.noise_polling); + + return 0; +} + + +static void save_data_ft5662(char *buf, int *data_length) +{ + struct fts_test *tdata = fts_ftest; + struct mc_sc_testitem *test_item = &tdata->ic.mc_sc.u.item; + struct mc_sc_threshold *thr = &fts_ftest->ic.mc_sc.thr; + u32 cnt = 0; + u32 tmp_cnt = 0; + int *tmp_data = NULL; + int line_num = 11; + int i = 0; + int j = 0; + u8 tx = tdata->node.tx_num; + u8 rx = tdata->node.rx_num; + u8 sc_rx = (tdata->sc_node.tx_num > tdata->sc_node.rx_num) ? tdata->sc_node.tx_num : tdata->sc_node.rx_num; + u8 tmp_rx = 0; + u8 scap_cfg = 0xFF; + bool cc_en = false; + + fts_test_read_reg(FACTROY_REG_SCAP_CFG, &scap_cfg); + cc_en = !(scap_cfg & 0x80); + FTS_TEST_INFO("cc_en:%d", cc_en); + + /* line 1 */ + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "ECC, 85, 170, IC Name, %s, IC Code, %x\n", tdata->ini.ic_name, 0); + + /*line 2*/ + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "TestItem Num, %d, ", tdata->csv_item_cnt); + if (true == test_item->rawdata_test) { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%s, %d, %d, %d, %d, %d, ", "Rawdata Test", CODE_M_RAWDATA_TEST, tx, rx, line_num, 2); + line_num += tx; + } + + if (true == test_item->rawdata_uniformity_test) { + if (thr->basic.uniformity_check_tx) { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%s, %d, %d, %d, %d, %d, ", + "Rawdata Uniformity Test", CODE_M_RAWDATA_UNIFORMITY_TEST, tx, rx, line_num, 1); + line_num += tx; + } + if (thr->basic.uniformity_check_rx) { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%s, %d, %d, %d, %d, %d, ", + "Rawdata Uniformity Test", CODE_M_RAWDATA_UNIFORMITY_TEST, tx, rx, line_num, 2); + line_num += tx; + } + } + + if (true == test_item->scap_cb_test) { + tmp_rx = (cc_en) ? sc_rx : 1; + if (tdata->csv_item_scb & 0x01) { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%s, %d, %d, %d, %d, %d, ", "SCAP CB Test", CODE_M_SCAP_CB_TEST, 2, sc_rx, line_num, 1); + line_num += 2; + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%s, %d, %d, %d, %d, %d, ", "SCAP CB Test", CODE_M_SCAP_CB_TEST, 2, tmp_rx, line_num, 2); + line_num += 2; + } + + if (tdata->csv_item_scb & 0x02) { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%s, %d, %d, %d, %d, %d, ", "SCAP CB Test", CODE_M_SCAP_CB_TEST, 2, sc_rx, line_num, 3); + line_num += 2; + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%s, %d, %d, %d, %d, %d, ", "SCAP CB Test", CODE_M_SCAP_CB_TEST, 2, tmp_rx, line_num, 4); + line_num += 2; + } + + if (tdata->csv_item_scb & 0x04) { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%s, %d, %d, %d, %d, %d, ", "SCAP CB Test", CODE_M_SCAP_CB_TEST, 2, sc_rx, line_num, 5); + line_num += 2; + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%s, %d, %d, %d, %d, %d, ", "SCAP CB Test", CODE_M_SCAP_CB_TEST, 2, tmp_rx, line_num, 6); + line_num += 2; + } + } + + if (true == test_item->scap_rawdata_test) { + if (tdata->csv_item_sraw & 0x01) { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%s, %d, %d, %d, %d, %d, ", "SCAP Rawdata Test", CODE_M_SCAP_RAWDATA_TEST, 2, sc_rx, line_num, 1); + line_num += 2; + } + + if (tdata->csv_item_sraw & 0x02) { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%s, %d, %d, %d, %d, %d, ", "SCAP Rawdata Test", CODE_M_SCAP_RAWDATA_TEST, 2, sc_rx, line_num, 2); + line_num += 2; + } + + if (tdata->csv_item_sraw & 0x04) { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%s, %d, %d, %d, %d, %d, ", "SCAP Rawdata Test", CODE_M_SCAP_RAWDATA_TEST, 2, sc_rx, line_num, 3); + line_num += 2; + } + } + + if (true == test_item->panel_differ_test) { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%s, %d, %d, %d, %d, %d, ", "Panel Differ Test", CODE_M_PANELDIFFER_TEST, tx, rx, line_num, 1); + line_num += tx; + } + + if (true == test_item->noise_test) { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%s, %d, %d, %d, %d, %d, ", "Noise Test", CODE_M_NOISE_TEST, tx, rx, line_num, 1); + line_num += tx; + + for (i = 1; thr->basic.noise_polling && (i < tdata->fre_num); i++) { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%s, %d, %d, %d, %d, %d, ", "Noise Test", CODE_M_NOISE_TEST, tx, rx, line_num, 1 + i); + line_num += tx; + } + + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%s, %d, %d, %d, %d, %d, ", "Null Noise", 41, 1, 1, line_num, 1); + line_num += 1; + } + + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n\n\n\n\n\n\n\n\n"); + + /*data*/ + if (true == test_item->rawdata_test && tdata->item1_data) { + for (i = 0; i < tdata->node.node_num; i++) { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tdata->item1_data[i]); + if (((i + 1) % tdata->node.rx_num) == 0) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + } + } + + if (true == test_item->rawdata_uniformity_test && tdata->item2_data) { + tmp_cnt = 0; + if (thr->basic.uniformity_check_tx) { + tmp_data = tdata->item2_data + tmp_cnt; + for (i = 0; i < tdata->node.node_num; i++) { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + if (((i + 1) % tdata->node.rx_num) == 0) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + } + tmp_cnt += tdata->node.node_num; + } + + if (thr->basic.uniformity_check_rx) { + tmp_data = tdata->item2_data + tmp_cnt; + for (i = 0; i < tdata->node.node_num; i++) { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + if (((i + 1) % tdata->node.rx_num) == 0) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + } + tmp_cnt += tdata->node.node_num; + } + } + + if (true == test_item->scap_cb_test && tdata->item3_data) { + tmp_cnt = 0; + if (tdata->csv_item_scb & 0x01) { + tmp_data = tdata->item3_data + tmp_cnt; + for (i = 0; i < tdata->sc_node.rx_num; i++) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + tmp_cnt += tdata->sc_node.node_num; + + tmp_data = tdata->item3_data + tmp_cnt; + if (cc_en) { + for (i = 0; i < tdata->sc_node.rx_num; i++) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + } else { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,\n", tmp_data[0]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,\n", tmp_data[tdata->sc_node.rx_num]); + } + tmp_cnt += tdata->sc_node.node_num; + } + + if (tdata->csv_item_scb & 0x02) { + tmp_data = tdata->item3_data + tmp_cnt; + for (i = 0; i < tdata->sc_node.rx_num; i++) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + tmp_cnt += tdata->sc_node.node_num; + + tmp_data = tdata->item3_data + tmp_cnt; + if (cc_en) { + for (i = 0; i < tdata->sc_node.rx_num; i++) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + } else { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,\n", tmp_data[0]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,\n", tmp_data[tdata->sc_node.rx_num]); + } + tmp_cnt += tdata->sc_node.node_num; + } + + if (tdata->csv_item_scb & 0x04) { + tmp_data = tdata->item3_data + tmp_cnt; + for (i = 0; i < tdata->sc_node.rx_num; i++) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + tmp_cnt += tdata->sc_node.node_num; + + tmp_data = tdata->item3_data + tmp_cnt; + if (cc_en) { + for (i = 0; i < tdata->sc_node.rx_num; i++) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + } else { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,\n", tmp_data[0]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,\n", tmp_data[tdata->sc_node.rx_num]); + } + tmp_cnt += tdata->sc_node.node_num; + } + } + + if (true == test_item->scap_rawdata_test && tdata->item4_data) { + tmp_cnt = 0; + if (tdata->csv_item_sraw & 0x01) { + tmp_data = tdata->item4_data + tmp_cnt; + for (i = 0; i < tdata->sc_node.rx_num; i++) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + tmp_cnt += tdata->sc_node.node_num; + } + + if (tdata->csv_item_sraw & 0x02) { + tmp_data = tdata->item4_data + tmp_cnt; + for (i = 0; i < tdata->sc_node.rx_num; i++) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + tmp_cnt += tdata->sc_node.node_num; + } + + if (tdata->csv_item_sraw & 0x04) { + tmp_data = tdata->item4_data + tmp_cnt; + for (i = 0; i < tdata->sc_node.rx_num; i++) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + tmp_cnt += tdata->sc_node.node_num; + } + } + + if (true == test_item->panel_differ_test && tdata->item5_data) { + for (i = 0; i < tdata->node.node_num; i++) { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tdata->item5_data[i]); + if (((i + 1) % tdata->node.rx_num) == 0) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + } + } + + if (true == test_item->noise_test && tdata->item6_data) { + for (i = 0; i < tdata->node.node_num; i++) { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tdata->item6_data[i]); + if (((i + 1) % tdata->node.rx_num) == 0) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + } + + for (j = 1; thr->basic.noise_polling && (j < tdata->fre_num); j++) { + tmp_data = &tdata->item6_data[tdata->node.node_num * j]; + for (i = 0; i < tdata->node.node_num; i++) { + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,", tmp_data[i]); + if (((i + 1) % tdata->node.rx_num) == 0) + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "\n"); + } + } + cnt += snprintf(buf + cnt, CSV_BUFFER_LEN - cnt, "%d,\n", tdata->null_noise_max); + } + + *data_length = cnt; + free_item_data(tdata); +} + + +struct test_funcs test_func_ft5662 = { + .ctype = {0x8A}, + .hwtype = IC_HW_MC_SC, + .key_num_total = 0, + .mc_sc_short_v2 = true, + .cb_high_support = true, + .param_update_support = true, + .param_init = param_init_ft5662, + .start_test = start_test_ft5662, + .save_data_private = save_data_ft5662, +}; diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_config.c b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_config.c new file mode 100644 index 000000000000..23e2a29673c2 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_config.c @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2022 Motorola Mobility LLC + * + * 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. + */ +#include "focaltech_ts_config.h" + +struct focaltech_ic_report_rate_config report_rate_config_info = { +#if defined(PRODUCT_BANGKK) + .rate_config_count = 2, + .refresh_rate_ctrl = 0, + .interpolation_ctrl = 1, + { + { + .interpolation_flag = 0, + .report_rate = 240, + .command = 0, + }, + { + .interpolation_flag = 1, + .report_rate = 360, + .command = 1, + }, + } +#endif +}; + +int fts_mmi_get_report_rate(struct fts_ts_data *ts_data) +{ + int refresh_rate_ctrl = 0; + int interpolation_ctrl = 0; + int interpolation_flag = 0; + int refresh_rate = 0; + int i = 0; + + refresh_rate_ctrl = ts_data->pdata->report_rate_ctrl; + interpolation_ctrl = ts_data->pdata->interpolation_ctrl; + + interpolation_flag = ts_data->get_mode.interpolation; + refresh_rate = ts_data->refresh_rate; + + FTS_DEBUG("refresh_rate_ctrl: %d, interpolation_ctrl: %d, interpolation_flag: %d, refresh_rate: %d", + refresh_rate_ctrl, interpolation_ctrl, interpolation_flag, refresh_rate); + + if (refresh_rate_ctrl == 0 && interpolation_ctrl == 1) { + for (i = 0; i < report_rate_config_info.rate_config_count; i++) { + if (interpolation_flag == report_rate_config_info.report_rate_info[i].interpolation_flag) { + break; + } + } + } else if (refresh_rate_ctrl == 1 && interpolation_ctrl == 1) { + for (i = 0; i < report_rate_config_info.rate_config_count; i++) { + if ((interpolation_flag == report_rate_config_info.report_rate_info[i].interpolation_flag) && + ((refresh_rate >= report_rate_config_info.report_rate_info[i].refresh_rate[0]) && + (refresh_rate <= report_rate_config_info.report_rate_info[i].refresh_rate[1]))) { + break; + } + } + } else if (refresh_rate_ctrl == 1 && interpolation_ctrl == 0) { + for (i = 0; i < report_rate_config_info.rate_config_count; i++) { + if ((refresh_rate >= report_rate_config_info.report_rate_info[i].refresh_rate[0]) && + (refresh_rate <= report_rate_config_info.report_rate_info[i].refresh_rate[1])) { + break; + } + } + } else { + //refresh_rate_ctrl = 0, interpolation_ctrl = 0 + i = 0; + } + + if (i == report_rate_config_info.rate_config_count) { + FTS_ERROR("Get config report rate fail"); + return -1; + } else { + FTS_DEBUG("Get config report rate %dHZ, command : 0x%02x ", + report_rate_config_info.report_rate_info[i].report_rate, + report_rate_config_info.report_rate_info[i].command); + return report_rate_config_info.report_rate_info[i].command; + } +} + diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_config.h b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_config.h new file mode 100644 index 000000000000..95b7a154a945 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_config.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 Motorola Mobility LLC + * + * 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. + */ +#ifndef __FOCALTECH_TS_CONFIG_H__ +#define __FOCALTECH_TS_CONFIG_H__ + +#include +#include "focaltech_core.h" + +#define MAX_REPORT_RATE_CONFIG 12 + +#define REPORT_RATE_CMD_240HZ 0 +#define REPORT_RATE_CMD_360HZ 1 + + +struct report_rate_config { + bool interpolation_flag; //if enable interpolation report rate + u16 refresh_rate[2]; //display refresh rate + u16 report_rate; //touch report rate + u16 command; //report rate switch command +}; + +struct focaltech_ic_report_rate_config { + u8 rate_config_count; //the count of report rate combination + bool refresh_rate_ctrl; //if support report rate change according to refresh rate + bool interpolation_ctrl; //if support interpolation report rate + struct report_rate_config report_rate_info[MAX_REPORT_RATE_CONFIG]; +}; + +int fts_mmi_get_report_rate(struct fts_ts_data *ts_data); + +#endif diff --git a/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_mmi.c b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_mmi.c new file mode 100644 index 000000000000..81208b517c27 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/focaltech_ts_mmi.c @@ -0,0 +1,1072 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include "focaltech_core.h" +#include "focaltech_ts_config.h" + +#define EDGE_SWITCH_CMD 0x17 +#define ROTATE_DEFAULT_0 0x00 +#define ROTATE_LEFT_90 0X40 +#define ROTATE_RIGHT_90 0x80 +#define DEFAULT_EDGE 0x00 +#define SMALL_EDGE 0x40 +#define BIG_EDGE 0x80 + +/* hal settings */ +#define ROTATE_0 0 +#define ROTATE_90 1 +#define ROTATE_180 2 +#define ROTATE_270 3 +#define BIG_MODE 1 +#define SMALL_MODE 2 +#define DEFAULT_MODE 0 +#define MAX_ATTRS_ENTRIES 10 + +#define NORMAL_DEFAULT_MODE 10 +#define NORMAL_SMALL_MODE 11 +#define NORMAL_BIG_MODE 12 + +#define NORMAL_DEFAULT_EDGE 0x08 +#define NORMAL_SMALL_EDGE 0x48 +#define NORMAL_BIG_EDGE 0x88 + +#define FTS_CMD_REPORT_RATE_ADDR 0x8E +#define FTS_CMD_SAMPLE_SWITCH 0x8A +#define FTS_ACTIVE_LAST_TIME 10 +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +//extern int ts_mmi_dev_register(struct device *parent, struct ts_mmi_methods *mdata); +//extern void ts_mmi_dev_unregister(struct device *parent); +#endif + +#define GET_TS_DATA(dev) { \ + ts_data = dev_get_drvdata(dev); \ + if (!ts_data) { \ + FTS_ERROR("Failed to get driver data"); \ + return -ENODEV; \ + } \ +} + +static ssize_t fts_edge_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t fts_edge_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t fts_interpolation_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t fts_interpolation_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t fts_sample_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t fts_sample_show(struct device *dev, + struct device_attribute *attr, char *buf); + + +static DEVICE_ATTR(edge, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_edge_show, fts_edge_store); +static DEVICE_ATTR(interpolation, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_interpolation_show, fts_interpolation_store); +static DEVICE_ATTR(sample, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_sample_show, fts_sample_store); + +#define ADD_ATTR(name) { \ + if (idx < MAX_ATTRS_ENTRIES) { \ + dev_info(dev, "%s: [%d] adding %p\n", __func__, idx, &dev_attr_##name.attr); \ + ext_attributes[idx] = &dev_attr_##name.attr; \ + idx++; \ + } else { \ + dev_err(dev, "%s: cannot add attribute '%s'\n", __func__, #name); \ + } \ +} + +static struct attribute *ext_attributes[MAX_ATTRS_ENTRIES]; +static struct attribute_group ext_attr_group = { + .attrs = ext_attributes, +}; + +int fts_set_edge_mode(struct fts_mode_info mode) +{ + u8 reg_value_8c = 0; + u8 reg_value_8d = 0; + int ret = 0; + + switch (mode.edge_mode[0]) { + case ROTATE_DEFAULT_0: + reg_value_8c = 0; + break; + case ROTATE_LEFT_90: + reg_value_8c = 1; + break; + case ROTATE_RIGHT_90: + reg_value_8c = 2; + break; + default: + FTS_ERROR("Invalid edge mode!"); + return -EINVAL; + } + + switch (mode.edge_mode[1]) { + case DEFAULT_EDGE: + reg_value_8d = 0; + break; + case SMALL_EDGE: + reg_value_8d = 1; + break; + case BIG_EDGE: + reg_value_8d = 2; + break; + case NORMAL_DEFAULT_EDGE: + reg_value_8d = 4; + break; + case NORMAL_SMALL_EDGE: + reg_value_8d = 5; + break; + case NORMAL_BIG_EDGE: + reg_value_8d = 6; + break; + default: + FTS_ERROR("Invalid edge mode!"); + return -EINVAL; + } + + //In landscape mode and not in normal edge suppression + if((reg_value_8c > 0) && (reg_value_8d < 3)) + reg_value_8d++; + + ret = fts_write_reg(0x8C, reg_value_8c); + if (ret < 0) { + FTS_ERROR("set reg 0x8C fail, ret=%d", ret); + return -EINVAL; + } + + ret = fts_write_reg(0x8D, reg_value_8d); + if (ret < 0) { + FTS_ERROR("set reg 0x8D fail, ret=%d", ret); + return -EINVAL; + } + + FTS_INFO("Write edge value (%02x %02x)", + reg_value_8c, reg_value_8d); + return 0; +} + +static ssize_t fts_edge_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + int edge_cmd[2] = { 0 }; + unsigned int args[2] = { 0 }; + struct fts_ts_data *ts_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + ret = sscanf(buf, "%d %d", &args[0], &args[1]); + if (ret < 2) + return -EINVAL; + + switch (args[0]) { + case DEFAULT_MODE: + edge_cmd[1] = DEFAULT_EDGE; + break; + case SMALL_MODE: + edge_cmd[1] = SMALL_EDGE; + break; + case BIG_MODE: + edge_cmd[1] = BIG_EDGE; + break; + case NORMAL_DEFAULT_MODE: + edge_cmd[1] = NORMAL_DEFAULT_EDGE; + break; + case NORMAL_SMALL_MODE: + edge_cmd[1] = NORMAL_SMALL_EDGE; + break; + case NORMAL_BIG_MODE: + edge_cmd[1] = NORMAL_BIG_EDGE; + break; + default: + FTS_ERROR("Invalid edge mode: %d!", args[0]); + return -EINVAL; + } + + if (ROTATE_0 == args[1]) { + edge_cmd[0] = ROTATE_DEFAULT_0; + } else if (ROTATE_90 == args[1]) { + edge_cmd[0] = ROTATE_RIGHT_90; + } else if (ROTATE_270 == args[1]) { + edge_cmd[0] = ROTATE_LEFT_90; + } else { + FTS_ERROR("Invalid rotation mode: %d!", args[1]); + return -EINVAL; + } + + mutex_lock(&ts_data->mode_lock); + memcpy(ts_data->get_mode.edge_mode, edge_cmd, sizeof(edge_cmd)); + if (!memcmp(ts_data->set_mode.edge_mode, edge_cmd, sizeof(edge_cmd))) { + FTS_INFO("The value (%02x %02x) is same,so not write.", + edge_cmd[0], edge_cmd[1]); + ret = size; + goto exit; + } + + if (ts_data->power_disabled) { + FTS_ERROR("The touch is in sleep state, restore the value when resume"); + ret = size; + goto exit; + } + + ret = fts_set_edge_mode(ts_data->get_mode); + if (ret < 0) { + FTS_ERROR("failed to send edge switch cmd"); + goto exit; + } + + memcpy(ts_data->set_mode.edge_mode, edge_cmd, sizeof(edge_cmd)); + msleep(20); + ret = size; + FTS_INFO("Success to set edge = %02x, rotation = %02x", edge_cmd[1], edge_cmd[0]); + +exit: + mutex_unlock(&ts_data->mode_lock); + return ret; +} + +static ssize_t fts_edge_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *ts_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + FTS_INFO("edge area = %02x, rotation = %02x\n", + ts_data->set_mode.edge_mode[1], ts_data->set_mode.edge_mode[0]); + return scnprintf(buf, PAGE_SIZE, "0x%02x 0x%02x", + ts_data->set_mode.edge_mode[1], ts_data->set_mode.edge_mode[0]); +} + +static int fts_mmi_set_report_rate(struct fts_ts_data *ts_data) +{ + int ret = 0; + int mode = 0; + + mode = fts_mmi_get_report_rate(ts_data); + if (mode == -1) { + return -EINVAL; + } + + ts_data->get_mode.report_rate_mode = mode; + if (ts_data->set_mode.report_rate_mode == mode) { + FTS_DEBUG("The value = %d is same, so not to write", mode); + return 0; + } + + if (ts_data->power_disabled) { + FTS_DEBUG("The touch is in sleep state, restore the value when resume\n"); + return 0; + } + + ret = fts_write_reg(FTS_CMD_REPORT_RATE_ADDR, mode); + if (ret < 0) { + FTS_ERROR("failed to set report rate, mode = %d", mode); + return -EINVAL; + } + msleep(20); + + ts_data->set_mode.report_rate_mode = mode; + + FTS_INFO("Success to set %s\n", mode == REPORT_RATE_CMD_240HZ ? "REPORT_RATE_240HZ" : + (mode == REPORT_RATE_CMD_360HZ ? "REPORT_RATE_360HZ" : + "Unsupported")); + + return ret; +} + +static ssize_t fts_interpolation_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + unsigned long mode = 0; + struct fts_ts_data *ts_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + ret = kstrtoul(buf, 0, &mode); + if (ret < 0) { + FTS_ERROR("Failed to convert value."); + return -EINVAL; + } + + mutex_lock(&ts_data->mode_lock); + ts_data->get_mode.interpolation = mode; + ret = fts_mmi_set_report_rate(ts_data); + if (ret < 0) + goto exit; + + ret = size; + ts_data->set_mode.interpolation = mode; +exit: + mutex_unlock(&ts_data->mode_lock); + return ret; +} + +static ssize_t fts_interpolation_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *ts_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + FTS_INFO("interpolation = %d.", ts_data->set_mode.interpolation); + return scnprintf(buf, PAGE_SIZE, "0x%02x", ts_data->set_mode.interpolation); +} + +static ssize_t fts_sample_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + unsigned long mode = 0; + struct fts_ts_data *ts_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + ret = kstrtoul(buf, 0, &mode); + if (ret < 0) { + FTS_INFO("Failed to convert value.\n"); + return -EINVAL; + } + + mutex_lock(&ts_data->mode_lock); + + if (mode >= 1) + mode = FTS_ACTIVE_LAST_TIME; + + ts_data->get_mode.sample = mode; + if (ts_data->set_mode.sample == mode) { + FTS_DEBUG("The value = %lu is same, so not to write", mode); + ret = size; + goto exit; + } + + if (ts_data->power_disabled) { + FTS_DEBUG("The touch is in sleep state, restore the value when resume\n"); + ret = size; + goto exit; + } + + if (mode >= 1) + mode = FTS_ACTIVE_LAST_TIME; + ret = fts_write_reg(FTS_CMD_SAMPLE_SWITCH, mode); + if (ret < 0) { + FTS_ERROR("failed to set report rate, mode = %lu", mode); + goto exit; + } + + ts_data->set_mode.sample = mode; + FTS_INFO("Success to set %lu\n", mode); + + ret = size; +exit: + mutex_unlock(&ts_data->mode_lock); + return ret; +} + +static ssize_t fts_sample_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *ts_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + FTS_INFO("sample = %d.\n", ts_data->set_mode.sample); + return scnprintf(buf, PAGE_SIZE, "0x%02x", ts_data->set_mode.sample); +} + +static int fts_mmi_methods_get_vendor(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", "focaltech"); +} + +static int fts_mmi_methods_get_productinfo(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_INFO_LEN, "%s", FTS_CHIP_NAME); +} + +static int fts_mmi_methods_get_config_id(struct device *dev, void *cdata) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + ssize_t num_read_chars = 0; + u8 fwver = 0; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + 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 + num_read_chars = scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%02x", fwver); + + mutex_unlock(&input_dev->mutex); + return num_read_chars; +} + +/*return firmware version*/ +static int fts_mmi_methods_get_build_id(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%04x", 0); +} + +static int fts_mmi_methods_get_bus_type(struct device *dev, void *idata) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + TO_INT(idata) = input_dev->id.bustype == BUS_SPI ? + TOUCHSCREEN_MMI_BUS_TYPE_SPI : TOUCHSCREEN_MMI_BUS_TYPE_I2C; + return 0; +} + +static int fts_mmi_methods_get_irq_status(struct device *dev, void *idata) +{ + struct fts_ts_platform_data *pdata; + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + pdata = ts_data->pdata; + + TO_INT(idata) = gpio_get_value(pdata->irq_gpio); + return 0; +} + +static int fts_mmi_methods_get_drv_irq(struct device *dev, void *idata) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + TO_INT(idata) = ts_data->irq_disabled ? 0 : 1; + return 0; +} + +static int fts_mmi_methods_get_poweron(struct device *dev, void *idata) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + TO_INT(idata) = ts_data->power_disabled == false ? 1 : 0; + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int fts_mmi_methods_get_flashprog(struct device *dev, void *idata) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + TO_INT(idata) = (ts_data->fw_loading) ? 1 : 0; + + return 0; +} + +static int fts_mmi_methods_drv_irq(struct device *dev, int state) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + int ret = 0; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + + if (state == 1) { + FTS_INFO("enable irq"); + fts_irq_enable(); + } else if (state == 0) { + FTS_INFO("disable irq"); + fts_irq_disable(); + } else { + dev_err(dev, "%s: invalid value\n", __func__); + ret = -EINVAL; + } + + mutex_unlock(&input_dev->mutex); + return ret; +} + +#if FTS_POWER_SOURCE_CUST_EN +static int fts_mmi_methods_power(struct device *dev, int state) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + + return fts_power_source_ctrl(ts_data, state); +} +#endif + +static int fts_mmi_methods_reset(struct device *dev, int type) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_reset_proc(0); + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int fts_mmi_firmware_update(struct device *dev, char *fwname) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + + ts_data->force_reflash = true; + + fts_fw_update_vendor_name(fwname); + fts_fwupg_bin(); + + return 0; +} + +#if FTS_USB_DETECT_EN +static int fts_mmi_charger_mode(struct device *dev, int mode) +{ + struct fts_ts_data *ts_data; + int ret = 0; + uint8_t read_data = 0; + + GET_TS_DATA(dev); + mutex_lock(&ts_data->mode_lock); + + ret = fts_read_reg(FTS_REG_CHARGER_MODE_EN, &read_data); + if (ret < 0) { + FTS_ERROR("read 8b register fail, ret=%d", ret); + mutex_unlock(&ts_data->mode_lock); + return -EINVAL; + } + + if (ts_data->pdata->pocket_mode_ctrl) { + read_data |= (!!mode); + ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, read_data); + if(ret < 0){ + FTS_ERROR("Failed to set charger mode\n"); + mutex_unlock(&ts_data->mode_lock); + return -EINVAL; + } + + FTS_INFO("Success to %s charger mode, 0x8B = 0x%02x\n", mode ? "Enable" : "Disable", + read_data); + } else { + ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, mode); + if(ret < 0){ + FTS_ERROR("Failed to set charger mode\n"); + mutex_unlock(&ts_data->mode_lock); + return -EINVAL; + } + + FTS_INFO("Success to %s charger mode, 0x8B = 0x%02x\n", mode ? "Enable" : "Disable", + mode); + } + + mutex_unlock(&ts_data->mode_lock); + return 0; +} +#endif + +static int fts_mmi_panel_state(struct device *dev, + enum ts_mmi_pm_mode from, enum ts_mmi_pm_mode to) +{ + struct fts_ts_platform_data *pdata; + struct fts_ts_data *ts_data; + int ret = 0; +#if defined(CONFIG_FTS_DOUBLE_TAP_CONTROL) + u8 gesture_command = 0; + unsigned char gesture_type = 0; +#endif + + GET_TS_DATA(dev); + pdata = ts_data->pdata; +#ifdef FOCALTECH_SENSOR_EN + ts_data->zero_enable = 0; +#endif + + switch (to) { + case TS_MMI_PM_DEEPSLEEP: + ret = fts_write_reg(FTS_REG_POWER_MODE, FTS_REG_POWER_MODE_SLEEP); + ts_data->gesture_support = false; + if (ret < 0) + FTS_ERROR("set TP to sleep mode fail, ret=%d", ret); + break; + + case TS_MMI_PM_GESTURE: +#if defined(CONFIG_FTS_DOUBLE_TAP_CONTROL) + if (ts_data->imports && ts_data->imports->get_gesture_type) { + ret = ts_data->imports->get_gesture_type(ts_data->dev, &gesture_type); + } + + if (gesture_type & TS_MMI_GESTURE_SINGLE) { + gesture_command |= (1 << 7); + FTS_INFO("Enable GESTURE_CLI_SINGLE command: %02x", gesture_command); + } + if (gesture_type & TS_MMI_GESTURE_DOUBLE) { + gesture_command |= (1 << 4); + FTS_INFO("Enable GESTURE_CLI_DOUBLE command: %02x", gesture_command); + } + if (gesture_type & TS_MMI_GESTURE_ZERO) { +#ifdef FOCALTECH_SENSOR_EN + ts_data->zero_enable = 1; +#endif + FTS_INFO("Enable GESTURE_CLI_ZERO command: %02x", gesture_command); + } + + FTS_INFO("CLI GESTURE SWITCH command: %02x", gesture_command); + ts_data->gsx_cmd = gesture_command; +#endif + +#if FTS_GESTURE_EN + if (fts_gesture_suspend(ts_data) == 0) { + /* Enter into gesture mode(suspend) */ +#ifdef FOCALTECH_SENSOR_EN + ts_data->wakeable = true; + ts_data->gesture_support = true; +#endif + } +#endif + break; + + case TS_MMI_PM_ACTIVE: + break; + default: + dev_warn(dev, "panel mode %d is invalid.\n", to); + ret = -EINVAL; + break; + } + + return ret; + +} + +static int fts_mmi_pre_resume(struct device *dev) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + FTS_FUNC_ENTER(); + + fts_release_all_finger(); + + if (ts_data->gesture_support == false) { + mutex_lock(&input_dev->mutex); + fts_reset_proc(150); + FTS_INFO("Reset IC in %s for deep sleep", __func__); + mutex_unlock(&input_dev->mutex); + } + + + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_mmi_post_resume(struct device *dev) +{ + struct fts_ts_data *ts_data; + struct fts_ts_platform_data *pdata; + int ret = 0; + uint8_t read_data = 0; + + GET_TS_DATA(dev); + pdata = ts_data->pdata; +#ifdef FOCALTECH_SENSOR_EN + ts_data->fod_suspended = false; +#endif + if (ts_data->gesture_support == true) { +#ifdef FOCALTECH_SENSOR_EN + if(!fts_is_fod_resume(ts_data)) { +#endif + FTS_INFO("Reset IC in post resume"); + fts_reset_proc(200); +#ifdef FOCALTECH_SENSOR_EN + } +#endif + } + + //fts_tp_state_recovery(ts_data); + fts_tp_resume_recovery(ts_data); + +#if FTS_ESDCHECK_EN + fts_esdcheck_resume(); +#endif + +#if FTS_GESTURE_EN +#ifdef FOCALTECH_SENSOR_EN + if (ts_data->wakeable) { + fts_write_reg(0xCF, 0x02); + FTS_INFO("Enable fod report for normal mode in %s", __func__); +#endif + if (fts_gesture_resume(ts_data) == 0) { +#ifdef FOCALTECH_SENSOR_EN + ts_data->wakeable = false; + FTS_INFO("Exit from gesture suspend mode."); +#endif + goto exit; + } +#ifdef FOCALTECH_SENSOR_EN + } +#endif +#endif + +#if FTS_GESTURE_EN +exit: +#endif + + mutex_lock(&ts_data->mode_lock); + /* All IC status are cleared after reset */ + memset(&ts_data->set_mode, 0 , sizeof(ts_data->set_mode)); + /* restore data */ + if (ts_data->pdata->pocket_mode_ctrl && ts_data->get_mode.pocket_mode) { + if(fts_read_reg(FTS_REG_POCKET_MODE_EN, &read_data) >= 0) { + read_data |= (1 << 7); + ret = fts_write_reg(FTS_REG_POCKET_MODE_EN, read_data); + if(ret < 0){ + FTS_ERROR("Failed to set pocket mode\n"); + } else { + ts_data->set_mode.pocket_mode = ts_data->get_mode.pocket_mode; + FTS_INFO("Success enable pocket mode, 0x8B = 0x%02x\n", read_data); + } + } else { + FTS_ERROR("read 8b register fail, ret=%d", ret); + } + } + + if (pdata->interpolation_ctrl && ts_data->get_mode.interpolation) { + ret = fts_write_reg(FTS_CMD_REPORT_RATE_ADDR, ts_data->get_mode.report_rate_mode); + if (ret >= 0) { + ts_data->set_mode.interpolation = ts_data->get_mode.interpolation; + ts_data->set_mode.report_rate_mode = ts_data->get_mode.report_rate_mode; + + FTS_INFO("Success to %s interpolation mode\n", + ts_data->get_mode.report_rate_mode == REPORT_RATE_CMD_240HZ ? "REPORT_RATE_240HZ" : + (ts_data->get_mode.report_rate_mode == REPORT_RATE_CMD_360HZ ? "REPORT_RATE_360HZ" : + "Unsupported")); + } + } + + if (pdata->edge_ctrl) { + ret = fts_set_edge_mode(ts_data->get_mode); + if (!ret) { + memcpy(ts_data->set_mode.edge_mode, ts_data->get_mode.edge_mode, + sizeof(ts_data->get_mode.edge_mode)); + msleep(20); + FTS_INFO("Success to set edge area = %02x, rotation = %02x", + ts_data->get_mode.edge_mode[1], ts_data->get_mode.edge_mode[0]); + } + } + + if (pdata->sample_ctrl && ts_data->get_mode.sample) { + ret = fts_write_reg(FTS_CMD_REPORT_RATE_ADDR, ts_data->get_mode.sample); + if (ret >= 0) { + ts_data->set_mode.sample = ts_data->get_mode.sample; + msleep(20); + FTS_INFO("Success to %d sample mode\n", ts_data->get_mode.sample); + } + } + + mutex_unlock(&ts_data->mode_lock); + + FTS_FUNC_EXIT(); + ts_data->suspended = false; + + return 0; +} + +static int fts_mmi_pre_suspend(struct device *dev) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + + FTS_FUNC_ENTER(); + +#if FTS_ESDCHECK_EN + FTS_INFO("fts_esdcheck_suspend"); + fts_esdcheck_suspend(); +#endif + + FTS_FUNC_EXIT(); + + return 0; +} + +static int fts_mmi_post_suspend(struct device *dev) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + + FTS_FUNC_ENTER(); + +#ifdef FOCALTECH_SENSOR_EN + if (!ts_data->wakeable) { +#endif + fts_release_all_finger(); +#ifdef FOCALTECH_SENSOR_EN + } + ts_data->fod_suspended = true; +#endif + + ts_data->suspended = true; + FTS_FUNC_EXIT(); + + return 0; +} + +#ifdef FOCALTECH_PALM_SENSOR_EN +int fts_mmi_palm_set_enable(struct device *dev, unsigned int enable) +{ + fts_palm_sensor_set_enable(enable); + return 0; +} +#endif + +#ifdef CONFIG_FTS_LAST_TIME +static ssize_t fts_ts_timestamp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *ts_data; + ktime_t last_ktime; + struct timespec64 last_ts; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + mutex_lock(&ts_data->mode_lock); + last_ktime = ts_data->last_event_time; + ts_data->last_event_time = 0; + mutex_unlock(&ts_data->mode_lock); + + last_ts = ktime_to_timespec64(last_ktime); + + return scnprintf(buf, PAGE_SIZE, "%lld.%ld\n", last_ts.tv_sec, last_ts.tv_nsec); +} +static DEVICE_ATTR(timestamp, S_IRUGO, fts_ts_timestamp_show, NULL); +#endif + +static ssize_t fts_mmi_pocket_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *ts_data; + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", ts_data->set_mode.pocket_mode); +} + +static ssize_t fts_mmi_pocket_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + unsigned long value = 0; + int ret = 0; + struct fts_ts_data *ts_data; + uint8_t read_data = 0; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + FTS_FUNC_ENTER(); + + mutex_lock(&ts_data->mode_lock); + ret = kstrtoul(buf, 0, &value); + if (ret < 0) { + dev_err(dev, "pocket_mode: Failed to convert value\n"); + mutex_unlock(&ts_data->mode_lock); + FTS_FUNC_EXIT(); + return -EINVAL; + } + switch (value) { + case 0x10: + case 0x20: + dev_info(dev, "%s: touch pocket mode disable\n", __func__); + ts_data->get_mode.pocket_mode = 0; + break; + case 0x11: + case 0x21: + dev_info(dev, "%s: touch pocket mode enable\n", __func__); + ts_data->get_mode.pocket_mode = 1; + break; + default: + dev_info(dev, "%s: unsupport pocket mode type, value = %lu\n", __func__, value); + mutex_unlock(&ts_data->mode_lock); + FTS_FUNC_EXIT(); + return -EINVAL; + } + + if (ts_data->set_mode.pocket_mode == ts_data->get_mode.pocket_mode) { + FTS_INFO("The value = %d is same, so not to write", ts_data->get_mode.pocket_mode); + goto exit; + } + + if (ts_data->power_disabled == 1) { + FTS_INFO("The touch is in sleep state, restore the value when resume\n"); + goto exit; + } + + ret = fts_read_reg(FTS_REG_POCKET_MODE_EN, &read_data); + if (ret < 0) { + FTS_ERROR("read 8b register fail, ret=%d", ret); + goto exit; + } + + if(ts_data->get_mode.pocket_mode) { + read_data |= (1 << 7); + } else { + read_data &= ~(1 << 7); + } + + ret = fts_write_reg(FTS_REG_POCKET_MODE_EN, read_data); + if(ret < 0){ + FTS_ERROR("Failed to set pocket mode\n"); + goto exit; + } + + ts_data->set_mode.pocket_mode = ts_data->get_mode.pocket_mode; + msleep(20); + + FTS_INFO("Success set %d to pocket mode, 0x8B = 0x%02x\n", ts_data->set_mode.pocket_mode, + read_data); +exit: + mutex_unlock(&ts_data->mode_lock); + FTS_FUNC_EXIT(); + return size; +} + +static DEVICE_ATTR(pocket_mode, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_mmi_pocket_mode_show, fts_mmi_pocket_mode_store); + +static int fts_mmi_extend_attribute_group(struct device *dev, struct attribute_group **group) +{ + int idx = 0; + struct fts_ts_platform_data *pdata = NULL; + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + pdata = ts_data->pdata; + + if (pdata->edge_ctrl) + ADD_ATTR(edge); + + if (pdata->interpolation_ctrl) + ADD_ATTR(interpolation); + +#ifdef CONFIG_FTS_LAST_TIME + ADD_ATTR(timestamp); +#endif + + if(pdata->pocket_mode_ctrl) + ADD_ATTR(pocket_mode); + + if (pdata->sample_ctrl) + ADD_ATTR(sample); + + if (idx) { + ext_attributes[idx] = NULL; + *group = &ext_attr_group; + } else + *group = NULL; + + return 0; +} + +static struct ts_mmi_methods fts_mmi_methods = { + .get_vendor = fts_mmi_methods_get_vendor, + .get_productinfo = fts_mmi_methods_get_productinfo, + .get_build_id = fts_mmi_methods_get_build_id, + .get_config_id = fts_mmi_methods_get_config_id, + .get_bus_type = fts_mmi_methods_get_bus_type, + .get_irq_status = fts_mmi_methods_get_irq_status, + .get_drv_irq = fts_mmi_methods_get_drv_irq, + .get_flashprog = fts_mmi_methods_get_flashprog, + .get_poweron = fts_mmi_methods_get_poweron, + /* SET methods */ + .reset = fts_mmi_methods_reset, + .drv_irq = fts_mmi_methods_drv_irq, +#if FTS_POWER_SOURCE_CUST_EN + .power = fts_mmi_methods_power, +#endif +#if FTS_USB_DETECT_EN + .charger_mode = fts_mmi_charger_mode, +#endif +#ifdef FOCALTECH_PALM_SENSOR_EN + .palm_set_enable = fts_mmi_palm_set_enable, +#endif + /* Firmware */ + .firmware_update = fts_mmi_firmware_update, + /* vendor specific attribute group */ + .extend_attribute_group = fts_mmi_extend_attribute_group, + /* PM callback */ + .panel_state = fts_mmi_panel_state, + .pre_resume = fts_mmi_pre_resume, + .post_resume = fts_mmi_post_resume, + .pre_suspend = fts_mmi_pre_suspend, + .post_suspend = fts_mmi_post_suspend, + +}; + +int fts_mmi_dev_register(struct fts_ts_data *ts_data) { + int ret; + + mutex_init(&ts_data->mode_lock); + ret = ts_mmi_dev_register(ts_data->dev, &fts_mmi_methods); + if (ret) { + dev_err(ts_data->dev, "Failed to register ts mmi\n"); + mutex_destroy(&ts_data->mode_lock); + return ret; + } + + /* initialize class imported methods */ + ts_data->imports = &fts_mmi_methods.exports; + + return 0; +} + +void fts_mmi_dev_unregister(struct fts_ts_data *ts_data) { + mutex_destroy(&ts_data->mode_lock); + ts_mmi_dev_unregister(ts_data->dev); +} + diff --git a/drivers/input/touchscreen/focaltech_touch_v3/include/firmware/fw_sample.i b/drivers/input/touchscreen/focaltech_touch_v3/include/firmware/fw_sample.i new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/input/touchscreen/focaltech_touch_v3/include/pramboot/FT5662_Pramboot_V1.3_20211109.i b/drivers/input/touchscreen/focaltech_touch_v3/include/pramboot/FT5662_Pramboot_V1.3_20211109.i new file mode 100644 index 000000000000..3ab2c667d957 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_touch_v3/include/pramboot/FT5662_Pramboot_V1.3_20211109.i @@ -0,0 +1,538 @@ +0x0, 0x0, 0x0, 0x60,0x0, 0x0, 0x1, 0x6c,0x0, 0x0, 0x1, 0x6c,0x0, 0x0, 0x1, 0x6c, +0x0, 0x0, 0x1, 0x6c,0x0, 0x0, 0x1, 0x6c,0x0, 0x0, 0x1, 0x6c,0x0, 0x0, 0x1, 0x6c, +0x0, 0x0, 0x1, 0x6c,0x0, 0x0, 0x1, 0x6c,0x0, 0x0, 0x1, 0x6c,0x0, 0x0, 0x1, 0x6c, +0x0, 0x0, 0x1, 0x6c,0x0, 0x0, 0x1, 0x6c,0x0, 0x0, 0x1, 0x6c,0x0, 0x0, 0x1, 0x6c, +0x0, 0x0, 0x1, 0x6c,0x0, 0x0, 0x1, 0x6c,0x0, 0x0, 0x1, 0x6c,0x0, 0x0, 0x1, 0x6c, +0x0, 0x0, 0x1, 0x6c,0x0, 0x0, 0x1, 0x6c,0x0, 0x0, 0x1, 0x6c,0x0, 0x0, 0x1, 0x6c, +0xe0,0x60,0x3c,0x0, 0xe1,0xf0,0xcc,0x2, 0xe0,0x60,0x3c,0x10,0xea,0x0, 0xc8,0x4, +0xe1,0xbf,0xc8,0x7c,0xfa,0x0, 0xcf,0x84,0xf0,0x0, 0xcf,0xb0,0xe0,0x0, 0x14,0x0, +0xe0,0x60,0x3c,0x17,0xea,0x0, 0xcc,0x4, 0xe0,0x0, 0xcc,0x0, 0xe0,0x40,0x14,0xc4, +0x3c,0x79,0x1, 0x7, 0xea,0x0, 0xcd,0x4, 0xe0,0x2, 0xcd,0x58,0x3d,0x28,0xd0,0x69, +0xe0,0x2, 0x14,0x40,0x3c,0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +0x46,0x54,0x35,0x36,0x36,0x32,0x5f,0x50,0x72,0x61,0x6d,0x62,0x6f,0x6f,0x74,0x0, +0x56,0x31,0x2e,0x30,0x4e,0x6f,0x76,0x20,0x20,0x39,0x20,0x32,0x30,0x32,0x31,0x0, +0x8, 0x67,0xf7,0x98,0x48,0xa1,0xb7,0x5e,0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0xfe, +0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +0xea,0x0, 0xcc,0x4, 0xe0,0x8, 0xcc,0x0, 0x14,0x80,0xea,0x0, 0xcd,0x4, 0xe0,0x38, +0xcd,0x44,0x3d,0x28,0xd0,0x46,0xea,0x0, 0xcc,0x0, 0xe0,0x0, 0xcc,0x0, 0x24,0x2, +0x3c,0x1, 0x14,0x0, 0x14,0x80,0xd4,0x4c,0x3a,0x80,0x0, 0x0, 0x3a,0x80,0x0, 0x0, +0x3d,0xe8,0xe2,0x0, 0xcd,0x3, 0x5, 0xab,0xe0,0xc, 0x3c,0x49,0xe2,0x0, 0xc6,0x3, +0x2e,0x26,0xc5,0x7c,0xe0,0xc, 0x35,0x21,0xe2,0x0, 0xc6,0x18,0xe1,0x80,0xa7,0x99, +0x26,0x6, 0xe0,0x3, 0x16,0xc0,0x3e,0xac,0x3f,0x6f,0x3e,0x82,0xc5,0x70,0x6, 0x93, +0xe1,0x80,0xa7,0x19,0xe1,0x80,0xbf,0x9b,0xe1,0x80,0xa7,0x99,0xe1,0x80,0xbf,0x1b, +0xe1,0x80,0xa7,0x19,0xe1,0x80,0xbf,0x9b,0xe1,0x80,0xa7,0x99,0xc5,0x70,0xe1,0x80, +0xbf,0x1b,0x6, 0x6f,0xe1,0x80,0xbf,0x9b,0xe2,0x0, 0xc5,0x3, 0xc5,0x7f,0x6, 0x87, +0xe1,0x80,0x86,0x19,0xc5,0x7f,0xe1,0x80,0xae,0x1b,0x6, 0x7b,0x38,0x82,0x0, 0x0, +0xe2,0x0, 0xcd,0x3, 0x5, 0xab,0xe7,0x28,0xcc,0x89,0xe7,0x50,0xcc,0x89,0xe0,0x41, +0x3e,0x48,0xe2,0x0, 0xc6,0x3, 0xe0,0xb, 0x3e,0x18,0xe0,0xd, 0x3d,0x2c,0x3e,0x6d, +0xe2,0x0, 0xc6,0xc, 0x26,0x5, 0xe0,0x4, 0x17,0xa4,0x3f,0xac,0x3f,0x82,0xe1,0xff, +0xc6,0xf0,0x6, 0x8b,0xe1,0x80,0xbc,0x9b,0xe1,0x80,0xbc,0x9b,0xe1,0x80,0xbc,0x9b, +0xc6,0xf0,0xe1,0x80,0xbc,0x9b,0x6, 0x77,0x3d,0x18,0xe0,0xc0,0xac,0xba,0xac,0x9a, +0xac,0xaa,0xac,0x88,0xac,0x98,0xac,0xa8,0x38,0x82,0x3d,0xe8,0xc5,0x7f,0x6, 0x85, +0xe1,0x80,0xac,0x9b,0xc5,0x7f,0x6, 0x7d,0x38,0x82,0x8, 0xb1,0xe0,0x60,0x3c,0x87, +0xea,0x9, 0x7f,0xd0,0xe0,0x0, 0x15,0x60,0x3c,0x6f,0xdf,0x8b,0xe0,0x60,0x3c,0x17, +0x8, 0xe1,0xc0,0x7c,0x17,0xea,0x6f,0x87,0xe0,0x0, 0x1f,0x83,0x8f,0x8f,0xe2,0x0, +0xcf,0xe6,0x1, 0x37,0x2, 0x93,0xe2,0x0, 0xcf,0x85,0x1, 0x2a,0xe2,0x0, 0xcf,0xde, +0xe0,0x0, 0x1, 0x45,0xe2,0x0, 0xcf,0x83,0x1, 0xa8,0xe0,0x0, 0x1f,0xc3,0xa7,0x8f, +0x3c,0x1f,0xe0,0x1, 0x8c,0x8, 0x6c,0x7, 0x0, 0xa0,0xe2,0x1, 0xcf,0x90,0x1, 0xb, +0xe2,0x1, 0xcf,0xa8,0x1, 0x2e,0xe2,0x0, 0xcf,0xea,0x1, 0x97,0x2c,0x24,0xe0,0x0, +0x1f,0x8c,0x0, 0x84,0x2c,0x6, 0xe0,0x0, 0x1f,0xa1,0x9f,0x8f,0x37,0xc8,0x0, 0x8c, +0xe2,0x0, 0xcc,0x1, 0x1, 0x8a,0xe0,0x0, 0x1f,0xa1,0x9f,0x8f,0x0, 0x85,0x2c,0x5, +0xe0,0x0, 0x1f,0x91,0x8f,0x8f,0x6f,0x87,0x4f,0x87,0x3c,0x6f,0xc0,0x4, 0x38,0x82, +0x2c,0x4, 0xe0,0x0, 0x1f,0xb6,0x0, 0xea,0xe2,0x0, 0xcc,0x1, 0x1, 0xf6,0xe0,0x0, +0x1f,0xb6,0x0, 0xec,0xe2,0x0, 0xcc,0x1, 0x1, 0xf0,0xe0,0x0, 0x1f,0x8c,0x0, 0xe6, +0xe0,0x0, 0x1f,0x85,0x3c,0x1f,0x8c,0x8, 0x0, 0xc7,0xe0,0x0, 0x1f,0x95,0xa7,0x8f, +0x0, 0xfa,0xe0,0x0, 0x1f,0x2e,0x17,0x80,0xaf,0x8e,0x3e,0xee,0xe0,0x0, 0x1f,0x83, +0x8f,0x8f,0xe2,0x0, 0xcf,0xe1,0xe0,0x1, 0x1, 0x32,0x2, 0xac,0xe2,0x0, 0xcf,0x87, +0xe0,0x2, 0x1, 0x5d,0x2, 0x8f,0xe2,0x0, 0xcf,0x83,0xe0,0x2, 0x1, 0x25,0xe2,0x0, +0xcf,0x85,0xe0,0x0, 0x1, 0x5f,0xe2,0x0, 0xcf,0x81,0x1, 0x8d,0x17,0x81,0xaf,0x8d, +0x0, 0x8a,0xe2,0x0, 0xcf,0x90,0xe0,0x0, 0x1, 0x6a,0x2, 0x86,0xe2,0x0, 0xcf,0x89, +0xe0,0x0, 0x1, 0x6e,0x38,0x82,0xe2,0x0, 0xcf,0xd5,0x1, 0x19,0xe2,0x0, 0xcf,0xde, +0x1, 0xfa,0xe0,0x0, 0x1f,0x95,0xe0,0x0, 0x1e,0xb1,0xbe,0x8f,0x17,0x81,0xaf,0x8e, +0x0, 0xf2,0xe2,0x0, 0xcf,0xfa,0xe0,0x0, 0x1, 0x68,0x2, 0x94,0xe2,0x0, 0xcf,0xe5, +0xe0,0x1, 0x1, 0x43,0x2, 0x89,0xe2,0x0, 0xcf,0xe4,0x1, 0xe5,0xe0,0x0, 0x1f,0xb6, +0x17,0x0, 0xb7,0xf, 0x0, 0xe0,0xe2,0x0, 0xcf,0xe6,0x1, 0x69,0xe2,0x0, 0xcf,0xea, +0x0, 0xcd,0xe2,0x1, 0xcf,0xa8,0x1, 0x4b,0x2, 0x8b,0xe2,0x1, 0xcf,0x90,0x1, 0xd3, +0x2c,0x52,0x17,0x81,0xaf,0x8e,0xe0,0x0, 0x1f,0xb6,0xb4,0xf, 0x0, 0xcc,0xe2,0x1, +0xcf,0xaa,0x1, 0x65,0xe2,0x1, 0xcf,0xbf,0x1, 0xc6,0xe2,0x0, 0xcc,0x5, 0xe0,0x0, +0x2, 0xfd,0xea,0x7, 0x7f,0xc4,0xe9,0xff,0xc7,0xfc,0x34,0x21,0x3c,0x1f,0x94,0x8, +0x3c,0x1f,0x3c,0x2, 0xff,0x70,0x1, 0xa4,0x1, 0xc8,0x0, 0xb2,0x0, 0xc2,0x0, 0xd0, +0xe2,0x0, 0xcc,0x1, 0x1, 0x88,0xe0,0x0, 0x1f,0x91,0xac,0x8f,0x17,0x1, 0xaf,0xd, +0xe7,0xff,0x0, 0xaa,0xe2,0x0, 0xcc,0x2, 0x1, 0xfa,0xe0,0x0, 0x1f,0x92,0x17,0x1, +0xac,0x8f,0xe0,0x0, 0x1f,0x8d,0xaf,0xf, 0x0, 0xf2,0xe2,0x0, 0xcc,0x1, 0xe7,0xff, +0x1, 0x9b,0xe0,0x0, 0x1f,0x86,0xac,0x8f,0xe7,0xff,0x0, 0x96,0xe2,0x0, 0xcc,0x1, +0x1, 0x84,0xe0,0x0, 0x1f,0xbd,0x0, 0xf8,0xe2,0x0, 0xcc,0x2, 0xe7,0xff,0x1, 0x8c, +0xe0,0x0, 0x1f,0xa2,0x0, 0xf1,0xe2,0x0, 0xcc,0x3, 0xe7,0xff,0x2, 0x85,0xea,0x8, +0x7f,0xc2,0xe9,0xff,0xc7,0xfc,0x3c,0x1f,0x8c,0x8, 0x3c,0x1f,0xe0,0x0, 0x1f,0xba, +0x3c,0x2, 0x4, 0x4, 0xd4,0xe, 0xe4,0x9, 0x34,0xa0,0xbc,0x8f,0xe7,0xfe,0x0, 0xf4, +0xa7,0xf, 0x3c,0x9e,0xbc,0x8f,0xe7,0xff,0x0, 0x8b,0xe0,0x0, 0x1f,0x93,0x17,0x4, +0xbf,0xf, 0x17,0x0, 0xe0,0x0, 0x1f,0xb6,0xb7,0xf, 0x17,0x0, 0xe0,0x0, 0x1f,0xb4, +0xbf,0xf, 0xe7,0xfe,0x0, 0xe1,0xe0,0x0, 0x1f,0xc3,0xa7,0xf, 0xa7,0x9e,0x3c,0x9f, +0xbc,0x9e,0xe7,0xfe,0x0, 0xd9,0xe0,0x0, 0x1f,0xc3,0x34,0xa8,0xa7,0x8f,0xb4,0x8f, +0xe7,0xfe,0x0, 0xd2,0xe0,0x0, 0x1f,0xc3,0xa7,0xf, 0x9f,0x8e,0x3c,0x9f,0xb4,0x8e, +0xe0,0x0, 0x1f,0xa5,0xe7,0xfe,0x0, 0xe6,0xe0,0x0, 0x1e,0xa5,0xe0,0x0, 0x1f,0xc3, +0x16,0x1, 0xa7,0xf, 0x9f,0x8d,0x3e,0x1f,0xb6,0xd, 0x3f,0x9e,0xe0,0x1, 0xac,0x8f, +0x9f,0x8e,0x9e,0x8d,0x3e,0xff,0xe7,0xfe,0x4, 0xb7,0xe0,0x0, 0x1e,0xc0,0x17,0x80, +0x8e,0xd, 0xe0,0x2f,0x3f,0x8c,0xaf,0x8d,0xe0,0x0, 0x1f,0xbf,0xbf,0xf, 0x17,0x2, +0xe0,0x0, 0x1f,0x93,0x0, 0xc6,0xe2,0x0, 0xcc,0x6, 0xe7,0xfe,0x2, 0xa5,0xea,0x9, +0x7f,0xfe,0xe9,0xff,0xc7,0xfc,0x3c,0x1f,0x8c,0x8, 0x3c,0x1f,0x3c,0x2, 0x8, 0x8, +0x14,0x22,0x30,0x3c,0x42,0x0, 0xe4,0x9, 0x34,0xa0,0xe0,0x0, 0x1f,0x87,0xe7,0xff, +0x0, 0x9e,0xe0,0x0, 0x1f,0x87,0xa7,0xf, 0x34,0xa8,0x3c,0x9e,0xe7,0xff,0x0, 0x97, +0xe0,0x0, 0x1f,0x7, 0xa7,0x8e,0x3c,0x9f,0xbc,0x8e,0xe7,0xfe,0x0, 0x85,0xe4,0x9, +0x34,0xa0,0xe0,0x0, 0x1f,0xa4,0xe7,0xff,0x0, 0x8a,0xe0,0x0, 0x1f,0xa4,0x0, 0xec, +0xe0,0x0, 0x1f,0x24,0xa7,0x8e,0x3c,0x9f,0xbc,0x8e,0x17,0x3, 0xe0,0x0, 0x1f,0x93, +0xe7,0xff,0x0, 0x90,0xe2,0x0, 0xcc,0x2, 0x1, 0x1a,0xe2,0x0, 0xcc,0x3, 0x1, 0x1e, +0xe2,0x0, 0xcc,0x1, 0xe7,0xfd,0x1, 0xe8,0xe0,0x0, 0x1f,0xc0,0x8f,0xf, 0xe0,0x0, +0x1f,0xc3,0x27,0xa, 0xe0,0x0, 0x1f,0x2d,0xbf,0xf, 0xe4,0x9, 0x34,0xa0,0xa7,0x8f, +0xbc,0x9f,0xe7,0xfd,0x0, 0xd9,0xe0,0x0, 0x1f,0x29,0x0, 0xf7,0xe0,0x0, 0x1f,0xc3, +0x34,0xa8,0xa7,0x8f,0xa7,0x1f,0x3c,0x9e,0x0, 0xf4,0xe0,0x0, 0x1f,0xc3,0xa7,0x8f, +0xa6,0x9f,0x3c,0x9d,0xe0,0x0, 0x1e,0xbf,0xbc,0x9f,0xbf,0x8d,0x17,0x81,0xaf,0x8e, +0x17,0x1, 0xe0,0x0, 0x1f,0x93,0xe7,0xfe,0x0, 0xdd,0xe0,0x0, 0x1f,0x93,0x17,0x9, +0xe7,0xfe,0x0, 0xd8,0x8, 0xb3,0xe0,0x0, 0x1f,0x94,0xa1,0xf, 0x9f,0x92,0xe6,0xff, +0xcf,0xff,0x27,0x9f,0x9f,0x92,0x17,0x0, 0xe7,0x0, 0xcf,0x9e,0xe0,0x0, 0x19,0x9d, +0xb7,0x92,0x9c,0xb2,0x9f,0x83,0xe2,0x1, 0xc4,0xff,0x2f,0x84,0xe0,0x0, 0x1f,0x83, +0xac,0x8f,0x9c,0x3, 0xde,0x77,0x9f,0x83,0xc7,0x81,0xb7,0x83,0xe0,0x0, 0x1f,0xae, +0x8f,0x8f,0x27,0x87,0x14,0x0, 0xde,0x16,0x9f,0xb2,0xe7,0x20,0xcf,0x88,0xb7,0xb2, +0x9f,0x92,0xe6,0xfe,0xcf,0xff,0x27,0x93,0x9f,0x92,0x17,0x0, 0xe7,0x1, 0xcf,0x9e, +0xb7,0x92,0xe0,0x0, 0x1f,0x9d,0x9f,0xf, 0xc7,0x1, 0xb7,0xf, 0x9c,0xf, 0xe2,0x1, +0xc4,0x7f,0xde,0x0, 0x9f,0xb2,0xe7,0x20,0xcf,0x88,0xb7,0xb2,0x9, 0xe1,0xe0,0x0, +0x1f,0x9d,0x17,0x0, 0xb7,0xf, 0x16,0x80,0xe0,0x0, 0x1f,0x94,0xa7,0x8f,0x9f,0x7f, +0xe7,0x0, 0xcf,0x1d,0xb7,0x7f,0x38,0x82,0xe0,0x0, 0x1f,0xbc,0xa7,0x8f,0xa7,0xf, +0xe2,0x1, 0xc7,0x0, 0x27,0x3d,0xe0,0x0, 0x1f,0x4, 0xe0,0x1, 0x15,0x80,0xa7,0xe, +0xbd,0x9f,0x86,0x9e,0xe0,0x0, 0x1d,0x99,0xe2,0x0, 0xce,0x80,0xe0,0x1, 0x3, 0x15, +0xe0,0x0, 0x1e,0x8b,0xa6,0xd, 0x8e,0x8e,0x36,0xa2,0x3e,0x9c,0xa6,0x8d,0xe0,0x8, +0x9d,0x5f,0xe6,0xef,0xce,0xfd,0xe7,0x0, 0xcd,0x1d,0xe0,0x8, 0xb5,0x5f,0x8e,0x8e, +0x36,0xa2,0x3e,0x1d,0xa6,0x8c,0xe0,0x8, 0xb6,0xff,0x8e,0xae,0xe0,0xc, 0x36,0xa8, +0x8e,0xbe,0x3e,0x9c,0xe0,0x9, 0xb6,0x9f,0x8e,0x8e,0xe2,0x0, 0xce,0x81,0x1, 0x87, +0xe0,0x8, 0x9f,0x5f,0xe7,0x8, 0xcf,0x1d,0xe0,0x8, 0xb7,0x5f,0x16,0x81,0xe0,0x8, +0x9f,0x1f,0xe7,0x0, 0xcf,0x1d,0xe0,0x8, 0xb7,0x1f,0x17,0x0, 0xaf,0xb, 0xe0,0x4, +0xa7,0x1f,0xe6,0xfd,0xcf,0x5e,0x27,0x37,0xe0,0xa, 0x9e,0xbf,0x17,0x1, 0xe7,0x6, +0xce,0x9e,0xe0,0xa, 0xb6,0xbf,0xe0,0xa, 0x9e,0xbf,0xe7,0x5, 0xce,0x9e,0xe0,0xa, +0xb6,0xbf,0xe0,0xa, 0x9e,0xbf,0xe7,0x4, 0xce,0x9e,0xe0,0xa, 0xb6,0xbf,0xe0,0x0, +0x1e,0x99,0x8e,0xd, 0x26,0x20,0x16,0x0, 0xae,0xd, 0xa6,0xbf,0xe3,0xc0,0xc6,0xbf, +0xe0,0x2, 0x2e,0xa7,0xe0,0x0, 0x1d,0x84,0xa7,0xb, 0x3c,0xeb,0x8e,0xe, 0xe2,0x0, +0xce,0x64,0xe0,0x1, 0x1, 0x45,0xe0,0x0, 0x2, 0xcd,0xe2,0x0, 0xce,0x10,0xe0,0x1, +0x1, 0x12,0x2, 0xb5,0xe2,0x0, 0xce,0x7, 0xe0,0x2, 0x1, 0xd, 0xe2,0x0, 0xce,0x9, +0xe0,0x1, 0x1, 0xf, 0xe0,0x4, 0xa7,0x1f,0xe2,0xe, 0xc7,0x0, 0x27,0x1b,0xe0,0xa, +0x9e,0xbf,0x17,0x1, 0xe7,0xe, 0xce,0x9e,0xe0,0xa, 0xb6,0xbf,0xe0,0xa, 0x9e,0xbf, +0xe7,0xd, 0xce,0x9e,0xe0,0xa, 0xb6,0xbf,0xe0,0xa, 0x9e,0xbf,0xe7,0xc, 0xce,0x9e, +0xe0,0xa, 0xb6,0xbf,0x16,0x80,0xe0,0x8, 0x9f,0x5f,0xe7,0x8, 0xcf,0x1d,0xe0,0x8, +0xb7,0x5f,0x9f,0x1f,0xe6,0xfd,0xcf,0x7e,0x27,0x3, 0x16,0x4, 0xbe,0x1f,0xe0,0x7, +0x16,0xff,0xbe,0x9f,0x38,0x82,0x17,0x1, 0xe7,0xff,0x0, 0x9a,0xe2,0x0, 0xce,0x55, +0xe0,0x1, 0x1, 0x6, 0xe2,0x0, 0xce,0x61,0x1, 0xce,0xe0,0x0, 0x1f,0x13,0x15,0x84, +0xbd,0x8e,0xe0,0x0, 0x1f,0x36,0xb6,0x8e,0xe0,0x0, 0x1f,0x34,0xbe,0x8e,0x0, 0xc3, +0xe2,0x1, 0xce,0x2a,0xe0,0x0, 0x1, 0x74,0x2, 0x96,0xe2,0x0, 0xce,0x65,0xe0,0x0, +0x1, 0x74,0xe2,0x0, 0xce,0x7a,0xe7,0xff,0x1, 0xb7,0x8e,0xce,0x8e,0x5e,0xe4,0xd, +0x36,0xa0,0x36,0x28,0x8f,0x6e,0x3e,0x9c,0x3f,0x1d,0xe0,0x0, 0x1e,0xba,0xbf,0xd, +0xe7,0xff,0x0, 0xaa,0xe2,0x1, 0xce,0x2c,0xe0,0x0, 0x1, 0x7d,0x4, 0xb9,0xe2,0x1, +0xce,0x3f,0xe7,0xff,0x1, 0xa1,0xe0,0x0, 0x1e,0x40,0x16,0x80,0x8d,0x8c,0x8d,0x2e, +0xe0,0x2d,0x3e,0x8b,0xae,0x8c,0x35,0x28,0xe0,0x0, 0x1e,0xc3,0x8f,0x3e,0xa5,0x8d, +0x3f,0x1a,0xb7,0xb, 0xe3,0xff,0x15,0x7f,0xe0,0x0, 0x1f,0xe, 0xa7,0xe, 0x3f,0x5a, +0xbf,0x1b,0xe0,0x0, 0x1f,0x3f,0xbd,0x8e,0x15,0x82,0xe0,0x0, 0x1f,0x13,0xbd,0x8e, +0x8f,0xc, 0x27,0x32,0xe0,0x0, 0x1f,0x2d,0xbf,0xd, 0xe0,0x0, 0x1f,0x42,0xbf,0x9, +0x0, 0x9f,0xe0,0x0, 0x1e,0x86,0x8f,0x4e,0xaf,0xd, 0xe7,0xfe,0x0, 0xf5,0xe0,0x0, +0x1e,0xbd,0x8e,0x4e,0x8f,0x5e,0xae,0xd, 0xe0,0x0, 0x1e,0xa2,0x0, 0xf6,0x8e,0xce, +0x8e,0x5e,0xe4,0xd, 0x36,0xa0,0x36,0x28,0x8f,0x6e,0x3e,0x9c,0x3f,0x1d,0xe0,0x0, +0x1e,0x8e,0xbf,0xd, 0xe0,0x0, 0x1f,0x43,0xa7,0xe, 0xc7,0x4, 0xbf,0xb, 0xe6,0xef, +0xce,0x7e,0xe0,0x9, 0x9e,0xbf,0xe7,0x0, 0xce,0x9c,0xe0,0x9, 0xb6,0xbf,0xe0,0x9, +0xb7,0x5f,0xe7,0xfe,0x0, 0xd1,0xe0,0x0, 0x1f,0x29,0x0, 0xcf,0xe0,0x0, 0x1f,0x36, +0xb6,0x8e,0xe7,0xfe,0x0, 0xc9,0x8e,0xce,0x8e,0x5e,0xe4,0xd, 0x36,0xa0,0x36,0x28, +0x3e,0x9c,0x8e,0x6e,0x3e,0x9c,0xe0,0x0, 0x1e,0x7, 0xbe,0x8c,0x8e,0xfe,0xe0,0x1, +0x8e,0xe, 0xe4,0xd, 0x36,0xa0,0x36,0x28,0xe0,0x1, 0x8f,0x1e,0x3e,0x9c,0x3f,0x1d, +0xe0,0x0, 0x1e,0xa4,0x16,0x3, 0xbf,0xd, 0xe0,0x0, 0x1f,0x13,0xbe,0xe, 0xe7,0xfe, +0x0, 0xab,0xe0,0x0, 0x1e,0x40,0x8d,0x8c,0xe0,0x2d,0x3e,0x8b,0xae,0x8c,0x8e,0xc, +0xe0,0x0, 0x1e,0xc3,0x26,0x24,0xe0,0x0, 0x1e,0x2d,0xbe,0xd, 0xa6,0xd, 0xe0,0x0, +0x1d,0x8b,0x3e,0xec,0xa5,0x8b,0xc6,0x88,0xe6,0xd0,0xce,0x8d,0xbe,0xbb,0x8e,0xce, +0x8d,0xde,0xe4,0xd, 0x36,0xa0,0x8f,0x6e,0x35,0xa8,0x3e,0x9b,0x3f,0x1d,0xe0,0x0, +0x1e,0x8e,0xbf,0xd, 0xe3,0xff,0x16,0xff,0x3f,0x5d,0xbf,0x1c,0x16,0x81,0xe0,0x0, +0x1f,0x3f,0xbe,0xe, 0xe0,0x0, 0x1f,0x13,0xe7,0xfe,0x0, 0xba,0xe0,0x0, 0x1e,0x29, +0x0, 0xdd,0xe0,0x0, 0x1f,0x13,0x15,0x89,0xbd,0x8e,0xe7,0xfd,0x0, 0xf5,0x9e,0xff, +0xe7,0x6, 0xce,0x9e,0xb6,0xff,0x9e,0xff,0xe7,0x7, 0xce,0x9e,0xb6,0xff,0xe7,0xfd, +0x0, 0xeb,0x8, 0xb2,0x3f,0x83,0x14,0x0, 0xe0,0x0, 0xd8,0xe3,0xd3,0xd3,0xd3,0xe3, +0xe0,0x0, 0xd8,0xf7,0xd6,0x1c,0xe0,0x0, 0x1f,0xa7,0xa1,0xf, 0x9f,0xb2,0xe2,0x4, +0xc7,0x80,0x27,0x94,0xe0,0x1, 0x9f,0xd2,0xcf,0x89,0xe0,0x1, 0xb7,0xd2,0xe0,0x0, +0xd9,0xd6,0xe0,0x0, 0xda,0x58,0xe0,0x0, 0xd9,0x7b,0xe0,0x0, 0xd9,0x40,0xe1,0xff, +0x17,0xff,0xe0,0x2, 0xbf,0x82,0x3b,0x0, 0x9, 0x61,0xd4,0x6c,0x0, 0xf5,0x8, 0xb7, +0xf8,0x0, 0xc, 0x3e,0xc0,0x7c,0xe0,0x0, 0x19,0x88,0xdf,0xd4,0xe0,0x0, 0x1a,0x93, +0xe0,0x0, 0xd8,0x1d,0x3a,0x65,0xf0,0x2, 0x14,0x4, 0xe0,0x1, 0x13,0xff,0xf2,0xff, +0x14,0xff,0xf3,0xff,0x15,0x7f,0xf3,0x0, 0x15,0x80,0xf4,0xff,0x16,0x7f,0xfa,0x0, +0xce,0x82,0xac,0x3, 0xd7,0xbd,0xa7,0x85,0xc7,0xff,0xe2,0x0, 0xcf,0x88,0x2, 0xfc, +0xea,0x14,0x7f,0x22,0xe9,0xff,0xc7,0x7c,0x37,0xa1,0x3f,0x9e,0x97,0x8f,0x3f,0x9e, +0x3f,0x82,0x0, 0x68,0x0, 0x12,0x0, 0xb6,0x0, 0xdc,0xff,0xe4,0xff,0xe4,0xff,0xe4, +0xff,0xe4,0x2, 0x50,0x17,0x80,0xe0,0x0, 0x19,0xc, 0xbf,0x84,0xe7,0xe0,0x17,0x81, +0xe0,0x0, 0x1f,0x3d,0xb7,0x82,0x14,0x88,0xe0,0x0, 0x1b,0x3f,0x8e,0x8e,0xa7,0x86, +0xe2,0x0, 0xce,0x8b,0x3c,0x9f,0x1, 0x9, 0x8e,0x8e,0xe2,0x0, 0xce,0x8a,0x1, 0x5, +0x8f,0xe, 0xe2,0x1, 0xcf,0x5a,0x1, 0x8e,0xa4,0x1f,0x9d,0xf, 0xd1,0x31,0xa7,0x6, +0xa7,0x9e,0x9f,0xe, 0xe1,0x2f,0x3f,0xbe,0xe0,0x20,0xc7,0x80,0xb7,0x82,0xd1,0xe, +0x0, 0xc3,0xa4,0x1f,0x9d,0xf, 0xd7,0x3f,0x0, 0xf3,0x8f,0x3, 0x17,0x80,0xe0,0x0, +0x19,0xc, 0xbf,0x84,0xe2,0x0, 0xcf,0x20,0xe7,0xe0,0x17,0x81,0xe0,0x0, 0x1b,0x3f, +0xb7,0x82,0xa7,0x86,0x1, 0x87,0xa7,0x1f,0xe0,0x2, 0xc7,0x0, 0xe8,0x40,0x3f,0x7d, +0x2, 0x87,0x3c,0xef,0xa4,0x1f,0xe0,0x2, 0x15,0x0, 0xc4,0x88,0xd6,0xe4,0xe0,0x0, +0x1f,0x94,0xa7,0x6, 0xa7,0x8f,0xe0,0x1, 0x8e,0x8e,0x9f,0x3f,0xe7,0x20,0xcf,0xd, +0xb7,0x3f,0xe7,0x0, 0x17,0x80,0x0, 0xd3,0x17,0x80,0xe0,0x0, 0x19,0xc, 0xbf,0x84, +0xe7,0xe0,0x17,0x81,0xb7,0x82,0xe0,0x0, 0x1f,0x87,0xe0,0x0, 0x1d,0x36,0xa4,0xf, +0xe0,0x0, 0x1f,0xa4,0xa4,0x8f,0xd1,0x4b,0xe7,0xe0,0x17,0xd5,0x0, 0xc0,0x14,0x0, +0xe0,0x0, 0x1b,0xc, 0xe7,0xe0,0x17,0x81,0xbc,0x4, 0xf0,0x0, 0x1f,0x3a,0xb7,0x86, +0xe8,0x0, 0xa7,0x8e,0xe7,0xfe,0x27,0xf9,0xe0,0x0, 0x1f,0xbd,0x8f,0xf, 0xe2,0x0, +0xcf,0xa, 0x1, 0xb4,0xd7,0xcf,0x14,0x2, 0xd7,0xb9,0xe8,0x0, 0xa7,0x8e,0xe8,0x40, +0x3f,0xfa,0x5, 0x8e,0x14,0x3, 0xd7,0xb2,0x8f,0x83,0xe2,0x0, 0xcf,0xa0,0x1, 0x3, +0x14,0x4, 0xd7,0xac,0xe7,0xe1,0x17,0xaa,0xb7,0x86,0xe7,0xff,0x0, 0x9a,0xe8,0x0, +0xa7,0x8e,0xe8,0x40,0x3f,0xfb,0x5, 0xf7,0xe8,0x0, 0xa7,0x8e,0xf0,0x0, 0x17,0x18, +0xe8,0xf, 0x3f,0x9c,0x37,0xcc,0xc7,0x81,0x39,0x6f,0xe2,0x1, 0xc1,0x7f,0xc1,0x17, +0xe8,0x40,0x39,0x7e,0x4, 0x68,0xe8,0x40,0x3c,0x6e,0xe2,0x1, 0xc4,0x7f,0xf0,0x0, +0xc7,0x1, 0xd7,0x78,0xf3,0xff,0xc7,0x7f,0x0, 0xf4,0x8f,0xf, 0xe2,0x1, 0xcf,0x5a, +0x1, 0x83,0xd7,0xac,0x0, 0xd8,0x8f,0xf, 0xe2,0x0, 0xcf,0xb, 0xe0,0x0, 0x1, 0xcf, +0xe8,0x0, 0xa7,0x8e,0xe0,0x1f,0xc7,0xff,0x37,0xcc,0xc7,0x81,0x39,0x6f,0xe8,0x0, +0xa7,0x8e,0xe2,0x1, 0xc1,0x7f,0xe8,0x40,0x3f,0xf9,0xf0,0x0, 0x17,0x1, 0x5, 0xb6, +0xe8,0x40,0x3c,0x6e,0xe2,0x1, 0xc4,0x7f,0xf0,0x0, 0xc7,0x1, 0xd7,0x53,0xf3,0xff, +0xc7,0x7f,0xf2,0x0, 0xcf,0x8, 0x1, 0xf5,0x14,0x1, 0xd7,0x60,0x14,0x2, 0xd7,0x5e, +0x8f,0x83,0xe2,0x0, 0xcf,0xa0,0x1, 0x86,0xe1,0x2f,0x39,0x5f,0x39,0x6f,0xe2,0x1, +0xc1,0x7f,0xf0,0x0, 0x17,0x18,0x3f,0xe2,0xe3,0xff,0xc7,0xff,0xe8,0x40,0x3f,0xfe, +0xe7,0xff,0x5, 0xa2,0xe8,0x40,0x3c,0x6e,0xe2,0x1, 0xc4,0x7f,0xf0,0x0, 0xc7,0x1, +0xd7,0x31,0xf3,0xff,0xc7,0x7f,0x0, 0xf0,0xe8,0x40,0x3c,0x6e,0xe2,0x1, 0xc4,0x7f, +0xf0,0x0, 0xc7,0x1, 0xd7,0x27,0xf3,0xff,0xc7,0x7f,0x3f,0xe2,0xe3,0xff,0xc7,0xff, +0xe8,0x40,0x3f,0xfe,0x2, 0xf2,0xe7,0xff,0x0, 0x87,0x8f,0x8f,0xe0,0x0, 0x1f,0x22, +0xf0,0x40,0x3f,0x6f,0xf4,0x1, 0xcf,0x0, 0x39,0x68,0x8f,0x8e,0x39,0x7f,0xe7,0xfe, +0x3, 0xfb,0xe8,0x40,0x3c,0x6e,0x7f,0x1, 0xd7,0xd, 0xe8,0x40,0x3f,0xee,0xc7,0x81, +0xc1,0x1, 0xf0,0x40,0x3f,0x6f,0xe3,0xff,0xc1,0x7f,0xf2,0x1, 0xc7,0x7f,0x67,0x1, +0x0, 0xed,0x17,0x80,0xbf,0x84,0xe8,0x0, 0xbf,0x88,0xd6,0xb9,0xe0,0x1, 0x14,0x7f, +0xe0,0x0, 0xd8,0x57,0xe0,0x0, 0x1f,0xa7,0xa7,0x8f,0xe0,0x2, 0x9f,0x3f,0xe7,0x20, +0xcf,0x7, 0xe0,0x2, 0xb7,0x3f,0xe7,0xfd,0x0, 0xb8,0xe0,0x0, 0x1f,0x86,0x8f,0x8f, +0xe2,0x0, 0xcf,0x8c,0x1, 0x94,0x8, 0xb2,0xe0,0x0, 0x1f,0xa7,0x17,0x0, 0xa1,0xf, +0xe0,0x0, 0x14,0x64,0x9f,0xb2,0xe7,0x6, 0xcf,0x9e,0xb7,0xb2,0xe0,0x0, 0xd8,0x39, +0x9f,0xb2,0x17,0x1, 0xe7,0x6, 0xcf,0x9e,0xb7,0xb2,0x9, 0x61,0x38,0x82,0x8, 0xb7, +0xe0,0x0, 0x1f,0xbd,0x3a,0x68,0x8f,0xf, 0x3a,0xe9,0xe2,0x0, 0xcf,0xa, 0x39,0xea, +0x1, 0xb, 0x8f,0xf, 0xe2,0x1, 0xcf,0x5a,0x1, 0x7, 0x8f,0x8f,0xe2,0x0, 0xcf,0x8b, +0x1, 0x83,0xe0,0x20,0xc2,0x0, 0xe0,0x0, 0x1f,0xb4,0xa7,0xf, 0x39,0x6f,0x3f,0x13, +0xbf,0xf, 0xe0,0x0, 0x1f,0x8, 0x8f,0xe, 0xe2,0x0, 0xcf,0x20,0x1, 0x87,0xe0,0xe, +0x39,0x94,0xea,0x0, 0xcf,0x82,0x3f,0x7f,0x2, 0xb7,0xe0,0x0, 0x1b,0x3a,0x21,0xa2, +0xa7,0x2, 0xa7,0x86,0x3f,0x7f,0x2, 0x9e,0xe2,0x22,0xca,0x0, 0x1, 0x97,0xe2,0x0, +0xc9,0x9f,0xe0,0x0, 0x1f,0xa8,0x5, 0xa9,0x17,0x20,0xb7,0xf, 0x3c,0xe5,0x9b,0x8f, +0xe0,0x0, 0x1c,0x35,0x3d,0x67,0xd9,0xfd,0x3d,0x67,0xe0,0x1, 0x14,0xff,0x3c,0x65, +0xda,0x30,0xe0,0x0, 0x1f,0xbe,0x17,0x1, 0xaf,0xf, 0x3d,0x63,0x3c,0xe5,0x3c,0x64, +0xd5,0xd2,0xa7,0x2, 0xa7,0x86,0x3f,0x7f,0x1, 0x8f,0xe0,0x0, 0x19,0x3e,0x8f,0x82, +0x27,0x8b,0xe0,0x0, 0x1f,0xa8,0xe0,0x0, 0x1c,0xb5,0x9d,0xf, 0xe0,0x22,0x14,0x0, +0xd5,0xc2,0x17,0x80,0xaf,0x82,0xb, 0xe1,0xb1,0x8f,0x0, 0xd9,0x8, 0xb7,0xf8,0x0, +0xc, 0x3c,0xe1,0xfe,0xc0,0x0, 0xe0,0x0, 0x1f,0xbd,0x39,0x69,0x8f,0xf, 0x3b,0x6a, +0xe2,0x0, 0xcf,0xa, 0x9a,0xa, 0x1, 0x7, 0x8f,0x8f,0xe2,0x0, 0xcf,0x8b,0x1, 0x83, +0xe0,0x20,0xc4,0x0, 0xeb,0xff,0xca,0xfc,0xe2,0x0, 0x7f,0x84,0xf0,0x0, 0x1c,0x88, +0xf0,0x2, 0x14,0x0, 0xfa,0x0, 0xcd,0x2, 0x3a,0x9f,0xf0,0x0, 0x15,0x80,0x29,0x7, +0xb2,0x6, 0xe0,0x2, 0xc0,0x0, 0xf8,0x0, 0xe, 0x28,0xb, 0xe1,0xe8,0x0, 0x8f,0x89, +0xe9,0x23,0x39,0x58,0xe2,0x0, 0xcf,0xa0,0xf0,0x40,0x3e,0x63,0xf3,0xff,0xc6,0x7f, +0xe0,0x7, 0x39,0x98,0x1, 0x84,0xe8,0x40,0x3b,0xfa,0x2, 0xeb,0x3d,0x63,0xe2,0x0, +0x7c,0x84,0xd5,0x41,0x3d,0x64,0xe8,0x40,0x3d,0xeb,0xf0,0x9, 0x36,0x41,0x3c,0x65, +0xd0,0x4e,0x3a,0x68,0x39,0x23,0x3c,0x67,0x0, 0xdb,0x24,0xbf,0xe0,0x0, 0x1f,0xa0, +0x17,0x1, 0xa6,0x8f,0x3d,0xfe,0x9f,0xdd,0xe7,0xc, 0xcf,0x9e,0xb7,0xdd,0xe0,0x0, +0x1f,0xb2,0xa7,0x8f,0xe0,0x8, 0xb7,0x1f,0xe0,0x2, 0x9f,0x3f,0x1, 0xb0,0x16,0x2, +0xe7,0x4, 0xcf,0x2c,0xe0,0x2, 0xb7,0x3f,0x16,0x0, 0x9f,0x1f,0xe7,0x7, 0xcf,0x1c, +0xb7,0x1f,0x17,0x1, 0xb4,0x3f,0xe0,0x2, 0xb5,0x1f,0xbc,0xdf,0xe0,0x8, 0xb7,0x3f, +0xe7,0x2, 0x17,0x38,0xe0,0x9, 0xb7,0x3f,0x17,0x1, 0xe0,0x3, 0x9e,0x5f,0xe7,0x7, +0xce,0x1e,0xe0,0x3, 0xb6,0x5f,0xe0,0x4, 0xb7,0x1f,0xe0,0x3, 0x9e,0x1f,0xe6,0xff, +0xce,0x7c,0x26,0xf, 0x17,0x3, 0xe0,0x1, 0xbf,0x5f,0x17,0x0, 0xe0,0x6, 0x9d,0x7f, +0x9f,0xdd,0xe7,0xc, 0xcf,0x9e,0xb7,0xdd,0x3c,0x6a,0x38,0x82,0x16,0x0, 0x0, 0xd1, +0xe0,0x8, 0x9e,0x5f,0x26,0x6b,0xe0,0x9, 0xb7,0x1f,0x0, 0xe8,0xe6,0xcf,0xcc,0x8, +0xe7,0xff,0x0, 0xb5,0x8, 0xb1,0xe8,0x0, 0xc, 0x37,0xc0,0x6c,0xe0,0x60,0x38,0x88, +0xe0,0x60,0x3c,0x9, 0x78,0x85,0xe0,0x60,0x38,0x8a,0x7c,0x4, 0x78,0x83,0xe0,0x60, +0x3c,0xc, 0xe0,0x60,0x38,0x8b,0x7c,0x2, 0x78,0x81,0x3b,0x0, 0xdb,0x74,0x17,0x81, +0xe0,0x61,0x3f,0x91,0x38,0x83,0x60,0x85,0x64,0x4, 0xe0,0x60,0x38,0x98,0x60,0x83, +0xe0,0x60,0x3c,0x19,0x64,0x2, 0xe0,0x60,0x38,0x9a,0x60,0x81,0xc0,0x14,0xe0,0x60, +0x3c,0x1c,0xf0,0x0, 0xb, 0xa8,0xe0,0x60,0x38,0x9b,0x8, 0xa1,0x38,0x0, 0x8, 0xb1, +0xe8,0x0, 0xc, 0x37,0xc0,0x6c,0xe0,0x60,0x38,0x88,0xe0,0x60,0x3c,0x9, 0x78,0x85, +0xe0,0x60,0x38,0x8a,0x7c,0x4, 0x78,0x83,0xe0,0x60,0x3c,0xc, 0xe0,0x60,0x38,0x8b, +0x7c,0x2, 0x78,0x81,0x3b,0x0, 0xdb,0x84,0x17,0x82,0xe0,0x61,0x3f,0x91,0x38,0x83, +0x60,0x85,0x64,0x4, 0xe0,0x60,0x38,0x98,0x60,0x83,0xe0,0x60,0x3c,0x19,0x64,0x2, +0xe0,0x60,0x38,0x9a,0x60,0x81,0xc0,0x14,0xe0,0x60,0x3c,0x1c,0xf0,0x0, 0xb, 0xa8, +0xe0,0x60,0x38,0x9b,0x8, 0xa1,0x38,0x0, 0x8, 0xb1,0xe8,0x0, 0xc, 0x37,0xc0,0x6c, +0xe0,0x60,0x38,0x88,0xe0,0x60,0x3c,0x9, 0x78,0x85,0xe0,0x60,0x38,0x8a,0x7c,0x4, +0x78,0x83,0xe0,0x60,0x3c,0xc, 0xe0,0x60,0x38,0x8b,0x7c,0x2, 0x78,0x81,0x3b,0x0, +0xd2,0xdd,0x17,0xa0,0xe0,0x61,0x3f,0x91,0x38,0x83,0x60,0x85,0x64,0x4, 0xe0,0x60, +0x38,0x98,0x60,0x83,0xe0,0x60,0x3c,0x19,0x64,0x2, 0xe0,0x60,0x38,0x9a,0x60,0x81, +0xc0,0x14,0xe0,0x60,0x3c,0x1c,0xf0,0x0, 0xb, 0xa8,0xe0,0x60,0x38,0x9b,0x8, 0xa1, +0x38,0x0, 0x8, 0xb1,0xe8,0x0, 0xc, 0x37,0xc0,0x6c,0xe0,0x60,0x38,0x88,0xe0,0x60, +0x3c,0x9, 0x78,0x85,0xe0,0x60,0x38,0x8a,0x7c,0x4, 0x78,0x83,0xe0,0x60,0x3c,0xc, +0xe0,0x60,0x38,0x8b,0x7c,0x2, 0x78,0x81,0x3b,0x0, 0xd3,0xb2,0xe0,0x80,0x17,0x80, +0xe0,0x61,0x3f,0x91,0x38,0x83,0x60,0x85,0x64,0x4, 0xe0,0x60,0x38,0x98,0x60,0x83, +0xe0,0x60,0x3c,0x19,0x64,0x2, 0xe0,0x60,0x38,0x9a,0x60,0x81,0xc0,0x14,0xe0,0x60, +0x3c,0x1c,0xf0,0x0, 0xb, 0xa8,0xe0,0x60,0x38,0x9b,0x8, 0xa1,0x38,0x0, 0x8, 0xb1, +0xe8,0x0, 0xc, 0x37,0xc0,0x6c,0xe0,0x60,0x38,0x88,0xe0,0x60,0x3c,0x9, 0x78,0x85, +0xe0,0x60,0x38,0x8a,0x7c,0x4, 0x78,0x83,0xe0,0x60,0x3c,0xc, 0xe0,0x60,0x38,0x8b, +0x7c,0x2, 0x78,0x81,0x3b,0x0, 0xd6,0x63,0xe0,0x2, 0x17,0x80,0xe0,0x61,0x3f,0x91, +0x38,0x83,0x60,0x85,0x64,0x4, 0xe0,0x60,0x38,0x98,0x60,0x83,0xe0,0x60,0x3c,0x19, +0x64,0x2, 0xe0,0x60,0x38,0x9a,0x60,0x81,0xc0,0x14,0xe0,0x60,0x3c,0x1c,0xf0,0x0, +0xb, 0xa8,0xe0,0x60,0x38,0x9b,0x8, 0xa1,0x38,0x0, 0x8, 0xb1,0xe8,0x0, 0xc, 0x37, +0xc0,0x6c,0xe0,0x60,0x38,0x88,0xe0,0x60,0x3c,0x9, 0x78,0x85,0xe0,0x60,0x38,0x8a, +0x7c,0x4, 0x78,0x83,0xe0,0x60,0x3c,0xc, 0xe0,0x60,0x38,0x8b,0x7c,0x2, 0x78,0x81, +0x3b,0x0, 0xd5,0xf9,0xe0,0x8, 0x17,0x80,0xe0,0x61,0x3f,0x91,0x38,0x83,0x60,0x85, +0x64,0x4, 0xe0,0x60,0x38,0x98,0x60,0x83,0xe0,0x60,0x3c,0x19,0x64,0x2, 0xe0,0x60, +0x38,0x9a,0x60,0x81,0xc0,0x14,0xe0,0x60,0x3c,0x1c,0xf0,0x0, 0xb, 0xa8,0xe0,0x60, +0x38,0x9b,0x8, 0xa1,0x38,0x0, 0x8, 0xb1,0xe8,0x0, 0xc, 0x37,0xc0,0x6c,0xe0,0x60, +0x38,0x88,0xe0,0x60,0x3c,0x9, 0x78,0x85,0xe0,0x60,0x38,0x8a,0x7c,0x4, 0x78,0x83, +0xe0,0x60,0x3c,0xc, 0xe0,0x60,0x38,0x8b,0x7c,0x2, 0x78,0x81,0x3b,0x0, 0xda,0xad, +0xe0,0x10,0x17,0x80,0xe0,0x61,0x3f,0x91,0x38,0x83,0x60,0x85,0x64,0x4, 0xe0,0x60, +0x38,0x98,0x60,0x83,0xe0,0x60,0x3c,0x19,0x64,0x2, 0xe0,0x60,0x38,0x9a,0x60,0x81, +0xc0,0x14,0xe0,0x60,0x3c,0x1c,0xf0,0x0, 0xb, 0xa8,0xe0,0x60,0x38,0x9b,0x8, 0xa1, +0x38,0x0, 0x17,0x81,0xe0,0x6a,0x3f,0x90,0xe0,0x6a,0x3f,0x91,0xe0,0x6a,0x3f,0x95, +0xe0,0x6a,0x3f,0x9b,0xe0,0x6a,0x3f,0x9e,0x17,0x82,0xe0,0x6a,0x3f,0x9a,0xe0,0x6a, +0x3f,0x98,0x38,0x82,0x8, 0xb1,0xd8,0x62,0xe0,0x60,0x3f,0x87,0xe0,0x0, 0x1f,0x17, +0xe0,0x1, 0xbf,0xf, 0xe0,0x0, 0x1f,0x3b,0xe0,0x1, 0xbf,0x1f,0xe0,0x0, 0x1f,0x16, +0xe0,0x1, 0xbf,0x5f,0xe0,0x0, 0x1f,0x1e,0xe0,0x2, 0xbf,0x6f,0xe0,0x0, 0x1f,0x30, +0xe0,0x2, 0xbf,0x2f,0xe0,0x0, 0x1f,0x2b,0xe0,0x2, 0xbf,0xf, 0xe0,0x0, 0x1f,0x1b, +0xe0,0x2, 0xbf,0x3f,0x8, 0xe1,0xe0,0x0, 0x1f,0xc1,0xe0,0x8, 0x16,0x80,0xa7,0x8f, +0x16,0x0, 0x3f,0x6f,0x3e,0x9f,0xe1,0x80,0xbe,0x1e,0x3e,0xfe,0x1, 0xfd,0xe0,0x0, +0x1f,0x18,0xea,0x0, 0xcd,0x88,0xe6,0xd0,0xcf,0xe, 0x3f,0x1b,0xe0,0x0, 0x1e,0x21, +0xe0,0xa, 0xbf,0x5f,0xea,0x0, 0xcd,0xa, 0xe0,0x15,0xbf,0x2f,0xe6,0xd0,0xce,0xc, +0xbf,0x7f,0x3e,0x1a,0xe0,0x12,0xbe,0xf, 0xe0,0x0, 0x1e,0x31,0xe0,0x0, 0x1e,0xb6, +0xe6,0xd0,0xce,0xc, 0x3e,0x1b,0xe0,0xb, 0xbe,0x6f,0xe6,0xd0,0xce,0x8d,0xe0,0x0, +0x1e,0x6, 0xe0,0x1, 0xbf,0x1f,0xe6,0xd0,0xce,0xc, 0x3e,0x1b,0xe0,0x2, 0xbe,0xf, +0x3e,0x9a,0xe0,0x0, 0x1e,0xc, 0xe0,0xc, 0xbf,0x1f,0xe6,0xd0,0xce,0xc, 0x3e,0x1a, +0xe0,0xd, 0xbe,0x2f,0xe0,0x0, 0x1e,0x5, 0xe6,0xd0,0xce,0xc, 0x3e,0x1b,0xe0,0x15, +0xbe,0xf, 0xe0,0xc, 0xbf,0x4f,0xe0,0xc, 0xbf,0x5f,0xe0,0xc, 0xbe,0xef,0xe0,0x0, +0x1e,0xba,0xe6,0xd0,0xce,0x8d,0x3e,0x9b,0xe0,0xf, 0xbe,0xaf,0xe0,0x0, 0x1e,0xc3, +0xa6,0xd, 0x3e,0xec,0xc6,0x84,0xe6,0xd0,0xce,0x8d,0x3e,0x9b,0xe0,0x15,0xbe,0xbf, +0xe0,0x15,0xbe,0xcf,0x3e,0xec,0xc6,0x88,0xe6,0xd0,0xce,0x8d,0x3e,0x9b,0xe0,0x17, +0xbe,0xff,0xbe,0xbf,0xe0,0x1e,0xbf,0x1f,0x38,0x82,0xe0,0x0, 0x1f,0xc3,0x17,0x8, +0xa7,0x8f,0xe0,0x0, 0x1e,0xc1,0x3f,0x1f,0xa6,0x8d,0xe6,0xd0,0xcf,0xe, 0xea,0x0, +0xce,0xa, 0x3e,0x1e,0xc7,0x84,0xbe,0x3d,0xe6,0xd0,0xcf,0x8f,0xea,0x0, 0xce,0x8, +0x3f,0x9c,0xe0,0x15,0xbf,0xbd,0x3f,0x1c,0xe0,0x15,0xbf,0xcd,0xe0,0x17,0xbf,0x7d, +0x38,0x82,0x8, 0xb3,0xe0,0x0, 0x1f,0xa0,0x11,0x81,0xa7,0x8f,0x9f,0x5f,0xe7,0x9, +0xcf,0x13,0xb7,0x5f,0xe0,0x0, 0x1f,0xa3,0xa1,0xf, 0xe0,0x0, 0x1f,0xc1,0xe0,0x3, +0x9f,0x72,0xa7,0x8f,0xe6,0xef,0xcf,0x9f,0xe7,0x0, 0xcf,0x7f,0xe0,0x3, 0xb7,0x72, +0xdf,0x63,0x17,0x88,0xbf,0xa2,0xe1,0xff,0x17,0xff,0xb9,0x82,0xbf,0xb2,0xe0,0x0, +0x1f,0xa7,0xe0,0x0, 0x1f,0x39,0xa7,0x8f,0x8e,0xe, 0xe0,0x2, 0x9e,0x9f,0xe7,0xe, +0xce,0x9c,0xe0,0x2, 0xb6,0x9f,0x8e,0x8e,0xe0,0x2, 0x9e,0x1f,0xe6,0xfe,0xce,0xfd, +0xe7,0xf, 0xce,0x1d,0xe0,0x2, 0xb6,0x1f,0x16,0x80,0xe0,0x1, 0x9f,0x92,0xe7,0x9, +0xcf,0x9d,0xe0,0x1, 0xb7,0x92,0x8f,0x8e,0xe0,0x1, 0x9f,0x12,0x37,0xc7,0xe7,0x8, +0xcf,0x1f,0xe0,0x0, 0x1f,0xa6,0xe0,0x1, 0xb7,0x12,0x8f,0xf, 0xe0,0x1, 0x9f,0x92, +0xe7,0x20,0xcf,0x8e,0xe0,0x1, 0xb7,0x92,0xe0,0x1, 0x17,0x6a,0xe0,0x3, 0x9f,0x92, +0xe7,0x20,0xcf,0x8e,0xe0,0x3, 0xb7,0x92,0xe0,0x1, 0x9f,0xb2,0xe7,0x0, 0xcf,0xad, +0xe0,0x1, 0xb7,0xb2,0x17,0xa0,0xe0,0x61,0x3f,0x91,0xe0,0x61,0x3f,0x82,0xcf,0xa0, +0xe0,0x61,0x3f,0x92,0x9, 0xe1,0xe2,0x0, 0xcc,0x65,0xe0,0x1, 0x1, 0x1e,0x2, 0x9e, +0xe2,0x0, 0xcc,0x9, 0xe0,0x1, 0x1, 0xd, 0x2, 0x8a,0xe2,0x0, 0xcc,0x3, 0xe0,0x0, +0x1, 0x73,0xe2,0x0, 0xcc,0x7, 0xe0,0x0, 0x1, 0x7e,0x38,0x82,0xe2,0x0, 0xcc,0x61, +0xe0,0x1, 0x1, 0x32,0xe2,0x0, 0xcc,0x64,0x1, 0x14,0xe2,0x0, 0xcc,0x55,0x1, 0xf6, +0xe0,0x0, 0x1f,0xb6,0x16,0x80,0xb6,0x8f,0x0, 0xf1,0xe2,0x1, 0xcc,0x2b,0x1, 0x26, +0x2, 0x8d,0xe2,0x0, 0xcc,0x7a,0xe0,0x0, 0x1, 0x5f,0xe2,0x1, 0xcc,0x2a,0x1, 0xe6, +0xe0,0x0, 0x1f,0xb6,0x17,0x0, 0xb7,0xf, 0x0, 0xe1,0xe2,0x1, 0xcc,0x3f,0x1, 0x1d, +0xe2,0x1, 0xcc,0x71,0xe0,0x1, 0x1, 0x1d,0xe2,0x1, 0xcc,0x2c,0x1, 0xd7,0xe0,0x0, +0x1f,0xc3,0x16,0x81,0xa7,0x8f,0xa7,0x1f,0x37,0x48,0xbf,0x1f,0xe0,0x0, 0x1f,0x3f, +0xbf,0x8e,0xe0,0x0, 0x1f,0x93,0xbe,0x8f,0x0, 0xc9,0xe0,0x0, 0x1f,0xc3,0xa7,0xf, +0xa7,0x9e,0x37,0xc8,0xbf,0x9e,0x0, 0xc2,0xe0,0x0, 0x1f,0xa3,0xa7,0x8f,0xe0,0x2, +0x9f,0x1f,0xe0,0x2, 0x9f,0x9f,0x3e,0xef,0xe0,0x0, 0x1f,0xc3,0xe2,0x1, 0xc6,0xff, +0xa6,0xf, 0x3f,0xee,0xe3,0xfe,0xc7,0x80,0x3f,0x9d,0xb7,0x8c,0x17,0x0, 0xe0,0x0, +0x1f,0xc0,0x8e,0x8f,0xe0,0x2e,0x3f,0xd, 0xaf,0xf, 0x16,0x82,0xe0,0x0, 0x1f,0x3f, +0xbe,0xe, 0xe0,0x0, 0x1f,0x13,0xbe,0x8e,0x8f,0x8f,0x27,0x88,0xe0,0x0, 0x1f,0xc3, +0xe0,0x0, 0x1f,0x2d,0xbf,0xf, 0xe7,0xfe,0x0, 0x8a,0xe0,0x0, 0x1f,0xc3,0xe0,0x0, +0x1f,0x29,0x0, 0xf9,0xe0,0x0, 0x1f,0xc0,0x17,0x0, 0x8e,0x8f,0xe0,0x2e,0x3f,0xd, +0xaf,0xf, 0x0, 0xeb,0xe0,0x0, 0x1f,0x3a,0xa7,0x8e,0x37,0xc8,0xbf,0x8e,0xe7,0xff, +0x0, 0x86,0xe0,0x0, 0x1f,0x93,0x17,0x9, 0xbf,0xf, 0xe7,0xff,0x0, 0x80,0xe0,0x0, +0x1f,0x98,0xe0,0x0, 0x1f,0x3d,0x8e,0x8f,0xae,0x8e,0x8f,0x9f,0xe0,0x0, 0x1f,0x22, +0xaf,0x8e,0xe7,0xfe,0x0, 0xf4,0xe0,0x0, 0x1f,0x98,0x8f,0xf, 0xe4,0xd, 0x37,0x20, +0xe0,0x0, 0x1f,0x7, 0xbe,0x8e,0x8e,0x1f,0xa5,0x8e,0xe0,0xd, 0x36,0x28,0x3e,0x9b, +0xbe,0x8e,0x8e,0x2f,0xa6,0x8e,0x3e,0x9c,0xbe,0x8e,0x8f,0x3f,0xe4,0xd, 0x37,0x20, +0xe0,0x0, 0x1f,0x24,0xbe,0x8e,0x8e,0x4f,0xa5,0x8e,0xe0,0xd, 0x36,0x28,0x3e,0x9b, +0xbe,0x8e,0x8e,0xdf,0xa7,0x8e,0x3f,0x9d,0xbf,0x8e,0x16,0x83,0xe0,0x0, 0x1f,0x93, +0xe7,0xff,0x0, 0x83,0xe0,0x0, 0x1f,0x93,0x17,0x4, 0xbf,0xf, 0x16,0x80,0xe0,0x0, +0x1f,0xb6,0x17,0x0, 0xb6,0x8f,0xe0,0x0, 0x1f,0xb4,0xe7,0xff,0x0, 0xbf,0xe0,0x0, +0x1f,0x98,0x8f,0xf, 0xe2,0x1, 0xcf,0x20,0x1, 0x8d,0xe0,0x0, 0x1f,0xa3,0x16,0x81, +0xa7,0x8f,0xe0,0x1, 0x9f,0x1f,0xe7,0x8, 0xcf,0x1d,0xe0,0x1, 0xb7,0x1f,0xe7,0xfe, +0x0, 0xae,0x8f,0x8f,0xe2,0x0, 0xcf,0xd0,0xe7,0xfe,0x1, 0xa9,0xe0,0x0, 0x1f,0xa3, +0x16,0x80,0xa7,0x8f,0xe0,0x1, 0x9f,0x1f,0x0, 0xef,0x8, 0xb2,0xe0,0x0, 0x1f,0xa3, +0xa1,0xf, 0xa7,0x82,0xe0,0x1, 0xcf,0x80,0xbf,0x82,0xa7,0x92,0xe3,0xfe,0xc7,0x80, +0x27,0x97,0x9f,0x92,0x17,0x1, 0xe7,0x3, 0xcf,0x9e,0xb7,0x92,0xa7,0x92,0xe2,0x8, +0xc7,0x80,0x27,0x85,0x9f,0x92,0xe7,0x2, 0xcf,0x9e,0xb7,0x92,0xe1,0xff,0x17,0xff, +0xbf,0xb2,0xe7,0xfe,0x17,0x7f,0xa7,0x82,0x3f,0xde,0xbf,0x82,0x9, 0x61,0x9f,0x12, +0xe7,0x3, 0xcf,0x1f,0xb7,0x12,0x9f,0x12,0xe7,0x2, 0xcf,0x1f,0xb7,0x12,0xe0,0x1, +0x9c,0x72,0xe2,0x1, 0xc4,0x7f,0xde,0xe0,0x0, 0xea,0xe0,0x0, 0x1f,0xac,0x16,0x81, +0xa7,0x8f,0x9f,0x5f,0xe7,0x9, 0xcf,0x18,0xb7,0x5f,0x9f,0x5f,0xe7,0x8, 0xcf,0x19, +0xb7,0x5f,0x9f,0x5f,0xe7,0x7, 0xcf,0x1d,0xb7,0x5f,0x38,0x82,0xe0,0x0, 0x1f,0xa0, +0x17,0x1, 0xa7,0x8f,0xe0,0x4, 0x16,0x4, 0x9e,0xdf,0xe7,0xa, 0xce,0x9e,0xb6,0xdf, +0x16,0x80,0xe0,0x0, 0x1f,0xac,0xa7,0x8f,0xbe,0x1f,0x16,0x4, 0xbe,0xaf,0xbe,0xbf, +0xbe,0x4f,0xe0,0x0, 0x1e,0x27,0xa6,0xc, 0xe0,0x1, 0x9d,0xdc,0xe7,0xf, 0xcd,0x9d, +0xe0,0x1, 0xb5,0xdc,0xe0,0x80,0x16,0x0, 0xe0,0x61,0x3e,0x11,0xe0,0x61,0x3e,0x2, +0xe7,0x7f,0x15,0xff,0x3e,0x5b,0xe0,0x61,0x3e,0x12,0x9e,0x3f,0xe7,0x9, 0xce,0x1e, +0xb6,0x3f,0x9e,0x3f,0xe7,0x8, 0xce,0x1e,0xb6,0x3f,0x9e,0x3f,0xe7,0xb, 0xce,0x1e, +0xb6,0x3f,0x9e,0x3f,0xe7,0xa, 0xce,0x1e,0xb6,0x3f,0xe0,0x0, 0x1e,0x33,0x8d,0x8c, +0x9e,0x3f,0xe7,0xc, 0xce,0x2b,0xb6,0x3f,0x9e,0x3f,0xe7,0x1, 0xce,0x1e,0xb6,0x3f, +0x9e,0x3f,0xe7,0x0, 0xce,0x1d,0xb6,0x3f,0x9e,0x5f,0xe7,0xc, 0xce,0x2d,0xb6,0x5f, +0x9e,0x3f,0xe7,0x6, 0xce,0x1e,0xb6,0x3f,0x9f,0x3f,0xe7,0x7, 0xcf,0x1d,0xb7,0x3f, +0x38,0x82,0xe0,0x0, 0x1f,0xac,0xa7,0x8f,0xbc,0xf, 0x9f,0x3f,0xe6,0xf1,0xcf,0x7e, +0x27,0x7d,0xa4,0xf, 0xe2,0x1, 0xc4,0x7f,0x38,0x82,0x8, 0xb3,0xe0,0x1, 0x14,0x48, +0xd3,0x4f,0xe0,0x0, 0x1f,0xac,0x11,0x80,0xa1,0xf, 0x14,0x5, 0x9f,0xb2,0xe7,0x2, +0xcf,0x93,0xb7,0xb2,0xdf,0xe7,0x3c,0x63,0xdf,0xe5,0x9f,0xb2,0x17,0x1, 0xe7,0x2, +0xcf,0x9e,0x3c,0x5e,0xb7,0xb2,0x9, 0xe1,0x8, 0xb2,0xe0,0x0, 0x1f,0xac,0x17,0x0, +0xa1,0xf, 0x9f,0xb2,0xe7,0x2, 0xcf,0x9e,0xb7,0xb2,0x24,0x9, 0x14,0x6, 0xdf,0xd2, +0x9f,0xb2,0x17,0x1, 0xe7,0x2, 0xcf,0x9e,0xb7,0xb2,0x9, 0x61,0x14,0x4, 0x0, 0xf8, +0x24,0x93,0xe0,0x0, 0x1f,0xac,0x17,0x0, 0xa7,0x8f,0xc4,0xff,0x3e,0xe8,0xbf,0xf, +0xe0,0xc, 0x3e,0xa8,0x3c,0xfc,0x2, 0x89,0x3c,0x19,0x9f,0x3f,0xe6,0xf1,0xcf,0x7e, +0x27,0x7d,0xa7,0x8f,0xaf,0x88,0x38,0x82,0x9e,0x3f,0xe6,0xf1,0xce,0x7c,0x26,0x7d, +0xbf,0xf, 0xa6,0xf, 0xe1,0x80,0xae,0x1d,0x0, 0xec,0x24,0x90,0xe0,0x0, 0x1f,0xac, +0x3f,0x68,0xe1,0x80,0x8e,0x9e,0xa7,0x8f,0x3c,0x19,0xbe,0x8f,0x3f,0x78,0x1, 0x87, +0x9f,0x3f,0xe6,0xf1,0xcf,0x7e,0x27,0x7d,0xa7,0x8f,0x38,0x82,0x9e,0xbf,0xe6,0xf1, +0xce,0xfd,0x26,0xfd,0xe1,0x80,0x8e,0x9e,0xbe,0x8f,0xa6,0x8f,0x0, 0xf0,0xe0,0x0, +0x1f,0xac,0xa7,0x8f,0xe0,0x1, 0x9f,0x1f,0xe6,0xfe,0xcf,0x7e,0x27,0x8, 0xe0,0x1, +0x9f,0x1f,0x16,0x81,0xe7,0x2, 0xcf,0x1d,0xe0,0x1, 0xb7,0x1f,0x9f,0x3f,0x37,0x4f, +0x27,0x6, 0x9f,0x3f,0x16,0x80,0xe7,0xf, 0xcf,0x1d,0xb7,0x3f,0x9f,0xbf,0x38,0x82, +0x8, 0xb3,0xe0,0x0, 0x1f,0xac,0x11,0x80,0xa7,0x8f,0x39,0x68,0x9f,0x3f,0x14,0xb, +0xe7,0x2, 0xcf,0x13,0xb7,0x3f,0xdf,0x6e,0xe6,0xe8,0xcc,0x2, 0xdf,0x6b,0xe6,0xf0, +0xcc,0x2, 0xdf,0x68,0x3c,0x62,0xe2,0x1, 0xc4,0x7f,0xdf,0x64,0x3c,0x63,0x9, 0xa1, +0xe7,0xfe,0x0, 0xe1,0x8, 0xb7,0xe0,0x0, 0x1f,0xac,0x12,0x80,0xa1,0xf, 0x13,0x81, +0x9f,0x52,0x3a,0x68,0xe7,0xe, 0xcf,0x25,0xb7,0x52,0x3b,0x69,0x9f,0x52,0x39,0xef, +0xe7,0xc, 0xcf,0x27,0xb7,0x52,0x9f,0x32,0xe7,0x2, 0xcf,0x15,0xb7,0x32,0x24,0xa2, +0xe0,0x1, 0x14,0x3b,0xdf,0x47,0x9f,0xd2,0x3c,0xe5,0xe7,0xe, 0xcf,0xa7,0x3c,0x65, +0xb7,0xd2,0xde,0xdc,0xe6,0xe8,0xcc,0x4, 0xdf,0x3d,0xe6,0xf0,0xcc,0x4, 0xdf,0x3a, +0x3c,0x64,0xe2,0x1, 0xc4,0x7f,0xdf,0x36,0x14,0x81,0x3c,0x69,0xde,0xcf,0x14,0x0, +0xdf,0x31,0x2b,0x7, 0xa7,0x83,0x16,0x81,0x9f,0x5f,0xe7,0xe, 0xcf,0x2d,0xb7,0x5f, +0xb, 0xe1,0x14,0x3b,0xdf,0x27,0x0, 0xe7,0x8, 0xb3,0x39,0x69,0x39,0xea,0xdf,0xa9, +0x3c,0xe3,0x3c,0x62,0xdf,0x56,0xe0,0x0, 0x1f,0xac,0x16,0x81,0xa7,0x8f,0x9f,0x3f, +0xe7,0x2, 0xcf,0x1d,0xb7,0x3f,0x9, 0xe1,0x8, 0xb3,0xe0,0x0, 0x1f,0x9a,0x39,0x69, +0x8c,0x8f,0x39,0xea,0xe2,0x1, 0xc4,0x80,0xdf,0xae,0x3c,0xe3,0x3c,0x62,0xdf,0x41, +0xe0,0x0, 0x1f,0xac,0x16,0x81,0xa7,0x8f,0x9f,0x3f,0xe7,0x2, 0xcf,0x1d,0xb7,0x3f, +0x16,0x80,0x9f,0x5f,0xe7,0xe, 0xcf,0x2d,0xb7,0x5f,0x9f,0x5f,0xe7,0xc, 0xcf,0x2d, +0xb7,0x5f,0x9, 0xe1,0xe0,0x0, 0x1f,0x9a,0x3f,0x68,0x8d,0x8f,0x3e,0xe9,0xe2,0x0, +0xcd,0xbb,0x3e,0x6a,0x1, 0x5, 0x8f,0x8f,0xe2,0x1, 0xcf,0xbb,0x1, 0x85,0x3d,0x6c, +0x3c,0xed,0x3c,0x6e,0x0, 0xd2,0x0, 0xc1,0x8, 0xb2,0xe0,0x0, 0x1f,0xac,0x16,0x80, +0xa7,0x8f,0x39,0x69,0x9f,0x3f,0xe7,0x2, 0xcf,0x1d,0xb7,0x3f,0xde,0xdb,0xe6,0xe8, +0xcc,0x2, 0xde,0xd8,0xe6,0xf0,0xcc,0x2, 0xde,0xd5,0x3c,0x62,0xe2,0x1, 0xc4,0x7f, +0x9, 0x21,0xe7,0xfd,0x0, 0xd0,0x8, 0xb4,0x3a,0x68,0x14,0x1, 0x39,0xea,0x39,0x69, +0xde,0xec,0x3c,0xe4,0x14,0x2, 0xdf,0xe1,0x3c,0xe3,0x3c,0x62,0xdf,0x17,0xe0,0x0, +0x1f,0xac,0x16,0x81,0xa7,0x8f,0x9f,0x3f,0xe7,0x2, 0xcf,0x1d,0xb7,0x3f,0xde,0xc6, +0x2c,0x7f,0xa, 0x61,0x8, 0xb7,0xf8,0x0, 0xc, 0x38,0xe0,0x2, 0x13,0x80,0xe6,0xf8, +0xc9,0x88,0xe0,0x3, 0x3b,0xa3,0xe3,0xff,0xc1,0xff,0x39,0xfa,0x4, 0x83,0xdf,0xdc, +0x0, 0xa1,0x39,0x6a,0x3a,0xe9,0x3a,0x68,0x39,0x23,0x3a,0x13,0x3d,0x63,0xe3,0xff, +0xc1,0x7f,0x39,0x95,0xdf,0xd1,0x3a,0xe2,0x3b,0x64,0xe0,0x18,0x39,0xa4,0xe2,0x1, +0xca,0xff,0xf0,0x9, 0x3c,0x16,0x2, 0x92,0xe0,0xa, 0x31,0x48,0xe0,0x9, 0x35,0x28, +0xe5,0xfe,0xc5,0x0, 0xe0,0x8, 0x3a,0x19,0x3d,0x12,0xe3,0xff,0xc5,0x7f,0x3c,0x93, +0x2d,0x5f,0x14,0x1, 0xf8,0x0, 0xc, 0x28,0xb, 0xe1,0x3c,0x66,0x3d,0x67,0xe1,0xfe, +0xc2,0x80,0xdf,0xb2,0xe3,0xff,0xc2,0xff,0xe0,0x2, 0xc3,0x0, 0x0, 0xe1,0x8, 0xb5, +0x14,0x1, 0xde,0x9b,0xe0,0x0, 0x1f,0xac,0x12,0x0, 0xa1,0xf, 0x14,0x5, 0x9f,0xb2, +0x11,0x81,0xe7,0x2, 0xcf,0x94,0xb7,0xb2,0xde,0x6d,0x3c,0x64,0xde,0x6b,0x9f,0xb2, +0x3a,0xe8,0xe7,0x2, 0xcf,0x93,0xb7,0xb2,0x3c,0x63,0x9f,0xb2,0xe7,0x2, 0xcf,0x94, +0xb7,0xb2,0xde,0x60,0x3c,0x65,0xe2,0x1, 0xc4,0x3, 0xde,0x5c,0x9f,0xb2,0xe7,0x2, +0xcf,0x93,0xb7,0xb2,0xde,0x63,0x39,0xe8,0x2c,0x7e,0x9f,0xb2,0xe7,0x2, 0xcf,0x98, +0xb7,0xb2,0x14,0x5, 0xde,0x4f,0x3c,0x63,0xde,0x4d,0xe0,0x0, 0x1f,0xaa,0x17,0x1, +0xac,0xf, 0x9f,0xb2,0xe7,0x2, 0xcf,0x9e,0xb7,0xb2,0xa, 0xe1,0x8, 0xb2,0x14,0x1, +0xde,0x64,0xe0,0x0, 0x1f,0xac,0x17,0x0, 0xa1,0xf, 0x14,0x1, 0x9f,0xb2,0xe7,0x2, +0xcf,0x9e,0xb7,0xb2,0xde,0x37,0x14,0x1c,0xde,0x35,0x9f,0xb2,0x17,0x1, 0xe7,0x2, +0xcf,0x9e,0xb7,0xb2,0xde,0x3b,0x2c,0x7f,0x9, 0x61,0x8, 0xb5,0xe0,0x0, 0x1f,0xac, +0x12,0x0, 0xa1,0x8f,0xe0,0x1, 0x14,0x1f,0x9f,0xb3,0xe7,0x2, 0xcf,0x94,0xb7,0xb3, +0xde,0x21,0x3c,0x64,0xde,0x1f,0x3a,0xe8,0x3c,0x64,0xde,0x1c,0xe6,0xd8,0xc9,0x8, +0x3c,0x64,0xde,0x18,0x9f,0xb3,0x39,0x48,0x17,0x1, 0xe7,0x2, 0xcf,0x9e,0xe3,0xff, +0xc1,0x7f,0xe0,0xc0,0x14,0x11,0xe0,0x22,0x39,0x8, 0xb7,0xb3,0x21,0x6, 0xe0,0x1, +0x17,0xc8,0xe0,0x25,0x3a,0x8f,0x2a,0x84,0xe0,0x0, 0x14,0x40,0xa, 0xe1,0x14,0x20, +0x0, 0xfe,0x8, 0xb2,0x39,0x68,0x14,0x1, 0xde,0x20,0xe0,0x9, 0x31,0x2c,0x14,0x20, +0xdf,0x14,0xe0,0x0, 0x1f,0xac,0x16,0x81,0xa7,0x8f,0x9f,0x3f,0xe7,0x2, 0xcf,0x1d, +0xb7,0x3f,0xdd,0xfc,0x2c,0x7f,0x14,0x1, 0x9, 0x61,0x8, 0xb2,0x39,0x68,0x14,0x1, +0xde,0xc, 0xe0,0x9, 0x31,0x2f,0xe0,0x0, 0x14,0x52,0xde,0xff,0xe0,0x0, 0x1f,0xac, +0x16,0x81,0xa7,0x8f,0x9f,0x3f,0xe7,0x2, 0xcf,0x1d,0xb7,0x3f,0xdd,0xe7,0x2c,0x7f, +0x9, 0x61,0x8, 0xb2,0x39,0x68,0x14,0x1, 0xdd,0xf8,0xe4,0x9, 0x31,0x20,0xe0,0x1, +0x14,0x58,0xde,0xeb,0xe0,0x0, 0x1f,0xac,0x16,0x81,0xa7,0x8f,0x9f,0x3f,0xe7,0x2, +0xcf,0x1d,0xb7,0x3f,0xdd,0xd3,0x2c,0x7f,0x9, 0x61,0x8, 0xb2,0x14,0x1, 0xdd,0xe5, +0xe0,0x0, 0x1f,0xac,0x17,0x0, 0xa1,0xf, 0xe0,0x0, 0x14,0x60,0x9f,0xb2,0xe7,0x2, +0xcf,0x9e,0xb7,0xb2,0xdd,0xb7,0x9f,0xb2,0x17,0x1, 0xe7,0x2, 0xcf,0x9e,0xb7,0xb2, +0xdd,0xbd,0x2c,0x7f,0x9, 0x61,0x3f,0xe9,0xc7,0xff,0x3f,0x6f,0xe2,0x1, 0xc7,0x7f, +0xe2,0x0, 0xcf,0x6, 0x2, 0x93,0xea,0x3f,0x7f,0xe0,0xe9,0xff,0xc7,0xfc,0x3f,0x9e, +0x8f,0xf, 0xe0,0x0, 0x1f,0xc4,0xe1,0x2f,0x3f,0xbe,0xe0,0x0, 0x1f,0x45,0xe1,0x2f, +0x3f,0xbe,0xe0,0x0, 0x1f,0x9, 0xb7,0x8e,0x38,0x82,0x17,0x1, 0x0, 0xf3,0xe0,0x0, +0x1f,0xa0,0xe0,0xab,0x17,0x2a,0xa7,0x8f,0xe2,0x0, 0xcc,0x7, 0xe0,0x1, 0xb7,0x1f, +0x2, 0x85,0x9f,0x3f,0xe7,0x0, 0xcf,0x38,0xb7,0x3f,0x14,0x0, 0x9f,0x5f,0xe7,0xe, +0xcf,0x18,0xb7,0x5f,0x9f,0xbf,0xe6,0xfd,0xcf,0xdf,0x3c,0xef,0x0, 0xcd,0xe0,0x0, +0x1f,0xa0,0xe0,0xab,0x17,0x2a,0xa7,0x8f,0x16,0x1, 0xe0,0x1, 0xb7,0x1f,0x15,0x80, +0x9f,0x5f,0xe7,0x7, 0xcf,0x1c,0xb7,0x5f,0x17,0x0, 0xe0,0x0, 0x1f,0xa7,0xa7,0x8f, +0xb7,0x5f,0xe0,0x7, 0x17,0x7f,0xe0,0x1, 0xb7,0x1f,0xe0,0x1, 0x9f,0x7f,0xe0,0x7, +0xcf,0x7c,0xe0,0x1, 0xb7,0x7f,0xe0,0x1, 0x9f,0x5f,0xe3,0xf8,0xc7,0x0, 0xe0,0x1, +0xb7,0x5f,0x17,0xf, 0xe0,0x2, 0x9e,0x9f,0xe7,0x8, 0xce,0xce,0xe0,0x2, 0xb6,0x9f, +0x9e,0x9f,0xe3,0xfd,0xc6,0xff,0xb6,0x9f,0xe0,0x2, 0x9e,0x9f,0xe7,0xc, 0xce,0x9b, +0xe0,0x2, 0xb6,0x9f,0xe0,0x1, 0x9e,0xdf,0xe0,0x4, 0xce,0x80,0xe0,0x1, 0xb6,0xdf, +0x9e,0xbf,0xce,0xc0,0xb6,0xbf,0x9e,0x9f,0xce,0xc0,0xb6,0x9f,0xe0,0x5, 0x9e,0x9f, +0xe7,0x8, 0xce,0xce,0xe0,0x5, 0xb6,0x9f,0xe0,0x5, 0x9e,0x9f,0xe7,0xc, 0xce,0xce, +0xe0,0x5, 0xb6,0x9f,0xe0,0x5, 0x9e,0xbf,0xe7,0x0, 0xce,0xce,0xe0,0x5, 0xb6,0xbf, +0xe0,0x5, 0x9e,0x9f,0xe7,0x0, 0xce,0xce,0xe0,0x5, 0xb6,0x9f,0xe0,0x2, 0x9f,0x3f, +0xe7,0xe, 0xcf,0x1c,0xe0,0x2, 0xb7,0x3f,0x38,0x82,0xe0,0x0, 0x1f,0x9c,0x17,0x0, +0xb7,0xf, 0x16,0x81,0xe0,0x0, 0x1f,0xa0,0x16,0x0, 0xa7,0x8f,0x9f,0x5f,0xe7,0x1, +0xcf,0x1d,0xb7,0x5f,0xe0,0x0, 0x1f,0xaf,0xa7,0x8f,0x9f,0x7f,0xe7,0x5, 0xcf,0x1c, +0xb7,0x7f,0x9f,0x7f,0xe7,0x4, 0xcf,0x1c,0xb7,0x7f,0x16,0x2, 0x9f,0x5f,0xe7,0x0, +0xcf,0x2c,0xb7,0x5f,0x17,0x32,0xb7,0x1f,0xa7,0x4f,0x3f,0x4d,0xbf,0x4f,0x9f,0x7f, +0xe7,0x4, 0xcf,0x1d,0xb7,0x7f,0xe0,0x61,0x3f,0x82,0xe0,0x8, 0xcf,0x80,0xe0,0x61, +0x3f,0x92,0x38,0x82,0xe0,0x0, 0x1f,0x1c,0x9f,0x8e,0xc7,0x81,0xb7,0x8e,0xe0,0x0, +0x1f,0xaf,0xa7,0xf, 0xa7,0xce,0xcf,0x81,0xbf,0xce,0x38,0x82,0xe0,0x0, 0x1f,0xa0, +0x17,0x1, 0xa7,0x8f,0x16,0x0, 0x9e,0xdf,0xe7,0x2, 0xce,0x9e,0xb6,0xdf,0x9e,0xdf, +0xe7,0xd, 0xce,0x9e,0xb6,0xdf,0xe0,0x0, 0x1f,0x9f,0xa7,0x8f,0x9e,0xbf,0xe7,0x0, +0xce,0x9e,0xb6,0xbf,0x9e,0x9f,0xe7,0x6, 0xce,0x9c,0xb6,0x9f,0x16,0x2f,0x9e,0xdf, +0xe7,0x0, 0xce,0xfc,0xb6,0xdf,0x9e,0xbf,0xe7,0x1, 0xce,0x9e,0xb6,0xbf,0x9e,0xff, +0xe7,0x4, 0xce,0x9e,0xb6,0xff,0x9e,0x9f,0xe7,0x6, 0xce,0x9e,0xb6,0x9f,0xe0,0x61, +0x3f,0x82,0xe0,0x2, 0xcf,0x80,0xe0,0x61,0x3f,0x92,0x38,0x82,0xe0,0x0, 0x1f,0x9f, +0xa7,0x8f,0x9f,0x5f,0xe6,0xf9,0xcf,0x1e,0x27,0xa, 0x9f,0x7f,0x16,0x81,0xe7,0x4, +0xcf,0x1d,0xb7,0x7f,0x9f,0x1f,0xe7,0x6, 0xcf,0x1d,0xb7,0x1f,0x38,0x82,0xe0,0x0, +0x1f,0x89,0x9f,0x8f,0x3c,0x3f,0xc4,0x7b,0x34,0x4, 0xc4,0x7f,0xe3,0xff,0xcc,0x7f, +0x1, 0x82,0x38,0x82,0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xf1,0xeb,0x0, 0xcf,0x80,0x9f,0x5f, +0x16,0x81,0xe7,0x0, 0xcf,0x1d,0xb7,0x5f,0x17,0x0, 0x9e,0xbf,0xe0,0x1, 0x16,0x7f, +0xe7,0x4, 0xce,0xae,0xb6,0xbf,0xe0,0x0, 0x1f,0xc6,0x9e,0xbf,0xe7,0x20,0xce,0x8c, +0xb6,0xbf,0x9e,0x9f,0xe7,0x1, 0xce,0x9e,0xb6,0x9f,0x9e,0x9f,0xe7,0x0, 0xce,0x9e, +0xe0,0x0, 0x1f,0x37,0xb6,0x9f,0x8e,0x8e,0xe2,0x0, 0xce,0x81,0x5, 0x88,0x8f,0xe, +0x9e,0xdf,0xe2,0x1, 0xc7,0x7e,0xe7,0x20,0xce,0x8e,0xb6,0xdf,0xe0,0x61,0x3f,0x82, +0xcf,0x81,0xe0,0x61,0x3f,0x92,0xe0,0x61,0x3f,0x82,0xcf,0x82,0xe0,0x61,0x3f,0x92, +0x38,0x82,0x3f,0xe8,0xc7,0xf8,0x3f,0x6f,0xe2,0x1, 0xc7,0x7f,0x17,0xb5,0xe1,0x2f, +0x3f,0xae,0x2f,0x95,0x3f,0xe8,0xc7,0xc1,0x3f,0x6f,0xe2,0x1, 0xc7,0x7f,0x17,0xae, +0xe1,0x2f,0x3f,0xae,0x2f,0x8c,0x3f,0xe8,0xe1,0xff,0xc7,0x91,0xe2,0x0, 0xcf,0x86, +0x5, 0x86,0xe0,0x0, 0x17,0xf7,0xe0,0x28,0x3c,0xf, 0x38,0x82,0x14,0x1, 0x0, 0xfe, +0xe0,0x0, 0x1f,0x10,0xe0,0x0, 0x1e,0x8a,0xe0,0x0, 0x1e,0xf, 0x3f,0xee,0xe6,0xd0, +0xce,0x8d,0xe1,0x80,0xbe,0x9e,0x3f,0x7c,0x1, 0xfd,0xe0,0x0, 0x1f,0x21,0xe6,0xd0, +0xcf,0xe, 0xe0,0x12,0xbf,0xf, 0xe0,0x0, 0x1f,0x38,0xe6,0xd0,0xcf,0xe, 0xbf,0x1f, +0xe0,0x0, 0x1f,0x31,0xe6,0xd0,0xcf,0xe, 0xe0,0xb, 0xbf,0x6f,0xe0,0x0, 0x1f,0x36, +0xe6,0xd0,0xcf,0xe, 0xe0,0xc, 0xbf,0x6f,0xe0,0x0, 0x1f,0x5, 0xe6,0xd0,0xcf,0xe, +0xe0,0x15,0xbf,0xf, 0xe0,0x0, 0x1f,0xc, 0xe6,0xd0,0xcf,0xe, 0xe0,0xd, 0xbf,0x2f, +0x38,0x82,0x8, 0xb4,0xe0,0x0, 0x19,0xc8,0x17,0x3, 0xe0,0x2, 0x9f,0x93,0x11,0x1, +0xe7,0x8, 0xcf,0xce,0xe0,0x2, 0xb7,0x93,0xe0,0x2, 0x9f,0xb3,0xe0,0x0, 0x1a,0x37, +0xe7,0xf, 0xcf,0x92,0xe0,0x2, 0xb7,0xb3,0xdf,0xbc,0xeb,0x0, 0xcf,0x80,0x9f,0x3f, +0xe7,0x6, 0xcf,0x12,0xb7,0x3f,0x17,0x22,0xe0,0x4, 0x9f,0x93,0xe7,0xb, 0xcf,0x92, +0xe0,0x0, 0x19,0x49,0xe0,0x4, 0xb7,0x93,0xe0,0xc, 0x9f,0x92,0xe7,0x28,0xcf,0x8e, +0xe0,0xc, 0xb7,0x92,0x17,0x0, 0xe0,0xc, 0x9f,0x92,0xe7,0x20,0xcf,0x8e,0xe0,0xc, +0xb7,0x92,0x8f,0x84,0xe0,0x8, 0x37,0xc1,0xdf,0x7d,0xe0,0x0, 0x24,0x6c,0x8f,0x84, +0xe0,0xd, 0x9f,0x12,0x37,0xc1,0xe2,0x0, 0xc7,0xfe,0xe7,0x0, 0xcf,0x7f,0xe0,0xd, +0xb7,0x12,0xe0,0x2, 0xa7,0xe3,0xe6,0xf9,0xca,0x1f,0x3c,0x64,0xdf,0x6b,0x17,0x1, +0xe4,0xf, 0x32,0x28,0xe0,0x0, 0x24,0x61,0xe0,0xd, 0x9e,0x92,0xe4,0xf, 0x37,0xc8, +0xe7,0xf, 0xce,0x9e,0xe0,0xd, 0xb6,0x92,0xe0,0xd, 0x9f,0x12,0xe7,0x8, 0xcf,0x7f, +0xe0,0xd, 0xb7,0x12,0xe0,0x1, 0x17,0x80,0xbf,0xa2,0xe1,0xff,0x17,0xff,0xbf,0x92, +0x17,0x0, 0xe0,0x1, 0x9f,0xd2,0xe7,0x8, 0xcf,0x9e,0xe0,0x1, 0xb7,0xd2,0xe1,0x40, +0x17,0x94,0xe0,0x4, 0xbf,0x82,0xe1,0x0, 0x17,0x0, 0xe0,0x0, 0x1f,0x84,0xe0,0x4, +0xbf,0x52,0xa7,0x8f,0xe0,0x9, 0x9e,0xb2,0xe6,0xef,0xce,0x7f,0xe7,0x0, 0xce,0x9c, +0xe0,0x9, 0xb6,0xb2,0x16,0x83,0xe0,0x9, 0xb7,0xd2,0xe0,0x8, 0x17,0xc0,0xe0,0x9, +0xb7,0xf2,0xe0,0x4, 0xbf,0x22,0xe0,0x1, 0x9f,0x52,0xe7,0x20,0xcf,0xd, 0xe0,0x1, +0xb7,0x52,0xe0,0xd, 0xb7,0xd2,0x17,0xff,0xe0,0xd, 0xb7,0xf2,0xe0,0x12,0x17,0x80, +0xe0,0x6, 0xbf,0xd2,0xe0,0xe, 0x17,0x87,0xe0,0x5, 0xbf,0x92,0xe0,0x61,0x3f,0x82, +0xe0,0x10,0xcf,0x80,0xe0,0x61,0x3f,0x92,0xe0,0x10,0x17,0x80,0xe0,0x61,0x3f,0x91, +0xa, 0x61,0xe0,0xd, 0x9f,0x92,0xe0,0x0, 0x17,0x70,0xe7,0x0, 0xcf,0xfe,0xe0,0xd, +0xb7,0x92,0xe7,0xff,0x0, 0x98,0xe0,0xd, 0x9f,0x92,0xe7,0xf, 0xcf,0x9e,0xe0,0xd, +0xb7,0x92,0x17,0x8, 0xe0,0xd, 0x9f,0x92,0xe7,0x8, 0xcf,0xfe,0xe0,0xd, 0xb7,0x92, +0xe0,0xd, 0x9f,0x92,0xe6,0xf1,0xcf,0x9f,0xe0,0x2, 0xbf,0xe3,0xe7,0xff,0x0, 0x9c, +0x2, 0x2, 0x4, 0x4, 0x6, 0x6, 0x8, 0x0, 0x0, 0x4, 0x14,0x0, 0x0, 0x4, 0x10,0x0, +0x80,0x0, 0xc, 0x0, 0x80,0x0, 0xf, 0x0, 0x80,0x0, 0xe, 0x0, 0x80,0x0, 0x6, 0x0, +0x80,0x0, 0xa, 0x0, 0x80,0x0, 0x2, 0x0, 0x80,0x0, 0x3, 0x0, 0x80,0x0, 0x1, 0x0, +0x80,0x0, 0x0, 0x0, 0x46,0x54,0x49,0x43,0x10,0x56,0xe2,0x0, 0x35,0x36,0x36,0x32, +0x0, 0x0, 0x0, 0x0, 0x56,0xe2,0x80,0x0, 0x0, 0x4, 0x7, 0x70,0x1, 0xb, 0x3b,0x0, +0x0, 0x40,0x3, 0x2, 0x70,0x0, 0x0, 0x0, 0xea,0xea,0xea,0xea,0xea,0x0, 0x0, 0x0, +0x0, 0x4, 0x18,0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +0x0, 0x4, 0x4, 0x6, 0x0, 0x4, 0x0, 0x2c,0x0, 0x4, 0xc, 0x0, 0x0, 0x4, 0x4, 0x20, +0x0, 0x4, 0x4, 0x10,0x0, 0x4, 0x9, 0x7c,0x0, 0x4, 0x0, 0x1c,0x0, 0x4, 0x0, 0x28, +0x0, 0x0, 0x20,0x18,0x0, 0x4, 0x4, 0x22,0x0, 0x4, 0x4, 0x8, 0x0, 0x4, 0x5, 0x60, +0x0, 0x4, 0x18,0x0, 0x0, 0x4, 0x14,0x0, 0x0, 0x4, 0x0, 0x12,0x0, 0x4, 0x4, 0x2b, +0x0, 0x4, 0x4, 0x24,0x0, 0x0, 0x20,0x30,0x0, 0x4, 0x5, 0x64,0x0, 0x0, 0xf, 0x98, +0x0, 0x0, 0xe, 0xe4,0x0, 0x4, 0x9, 0xa4,0x0, 0x4, 0x4, 0x14,0x0, 0x4, 0x0, 0x1a, +0x0, 0x0, 0x11,0x6, 0x0, 0x4, 0xa, 0x30,0x0, 0x4, 0x4, 0x4, 0x0, 0x0, 0xf, 0xf2, +0x0, 0x0, 0x20,0x34,0x0, 0x0, 0x20,0x40,0x0, 0x4, 0x0, 0x10,0x0, 0x4, 0x4, 0x0, +0x0, 0x0, 0x20,0x28,0x0, 0x4, 0x4, 0xc, 0x0, 0x4, 0x4, 0xa, 0x0, 0x4, 0x0, 0x1e, +0x0, 0x0, 0x20,0x3c,0x0, 0x4, 0x9, 0x80,0x0, 0x4, 0x5, 0x68,0x0, 0x4, 0x4, 0x29, +0x0, 0x0, 0x10,0x4e,0x0, 0x0, 0x20,0x24,0x0, 0x4, 0x7, 0x70,0x0, 0x4, 0x4, 0x7, +0x0, 0x0, 0x20,0x38,0x0, 0x0, 0x10,0xaa,0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x20,0x2c, +0x0, 0x4, 0x0, 0x1f,0x0, 0x4, 0x4, 0x18,0x0, 0x4, 0x9, 0x84,0x0, 0x4, 0x4, 0x2, +0x0, 0x4, 0x0, 0x20,0x0, 0x4, 0x0, 0x24,0x0, 0x4, 0x4, 0x2a,0x0, 0x4, 0x4, 0x1c, +0x0, 0x0, 0xf, 0x3e,0x0, 0x0, 0x20,0x20,0x0, 0x4, 0x0, 0x19,0x0, 0x4, 0x4, 0x28, +0x0, 0x4, 0x9, 0x78,0x0, 0x4, 0x0, 0x18,0x0, 0x0, 0x20,0x1c,0x0, 0x4, 0x18,0x4, +0x0, 0x4, 0x0, 0x14,0x3, 0xd0,0x90,0x0, 0x0, 0xf, 0x42,0x40,0x80,0x0, 0xa, 0x0, +0x80,0x0, 0x4, 0x0, 0x80,0x0, 0x1, 0x0, 0x80,0x0, 0xc, 0x0, \ No newline at end of file diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/Android.mk b/drivers/input/touchscreen/focaltech_v2_mmi/Android.mk new file mode 100644 index 000000000000..ce800fd48b26 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/Android.mk @@ -0,0 +1,41 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifneq ($(FOCALTECH_TOUCH_IC_NAME),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME=$(FOCALTECH_TOUCH_IC_NAME) +else + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME=ft3518u +endif + +ifneq ($(BOARD_USES_DOUBLE_TAP),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_MMI_ENABLE_DOUBLE_TAP=y +endif + +ifneq ($(BOARD_USES_TOUCH_PALM),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_MMI_ENABLE_PALM=y +endif + +ifneq ($(BOARD_USES_PEN_NOTIFIER),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_MMI_PEN_NOTIFIER=y +endif + +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_MMI=y +endif + +ifeq ($(DRM_PANEL_NOTIFICATIONS),true) + KERNEL_CFLAGS += CONFIG_DRM_PANEL_NOTIFICATIONS=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := focaltech_v2_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/mmi_info.ko +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +endif + +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +KBUILD_OPTIONS_GKI += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/Kbuild b/drivers/input/touchscreen/focaltech_v2_mmi/Kbuild new file mode 100644 index 000000000000..187de9410c2b --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/Kbuild @@ -0,0 +1,44 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/focaltech_v2_mmi \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/focaltech_v2_mmi/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_test + +obj-m := focaltech_v2_mmi.o +focaltech_v2_mmi-objs += focaltech_core.o +focaltech_v2_mmi-objs += focaltech_ex_fun.o +focaltech_v2_mmi-objs += focaltech_ex_mode.o +focaltech_v2_mmi-objs += focaltech_gesture.o +focaltech_v2_mmi-objs += focaltech_esdcheck.o +focaltech_v2_mmi-objs += focaltech_point_report_check.o +focaltech_v2_mmi-objs += focaltech_flash.o +focaltech_v2_mmi-objs += focaltech_flash/focaltech_upgrade_ft3518u.o +focaltech_v2_mmi-objs += focaltech_flash/focaltech_upgrade_ft5452i.o +focaltech_v2_mmi-objs += focaltech_i2c.o + +ifneq ($(filter ft3519,$(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT3519 +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers + EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI + focaltech_v2_mmi-objs += focaltech_ts_mmi.o +endif +ifneq ($(filter m y,$(CONFIG_INPUT_FOCALTECH_MMI_ENABLE_DOUBLE_TAP)),) + EXTRA_CFLAGS += -DFOCALTECH_SENSOR_EN +endif +ifneq ($(filter m y,$(CONFIG_INPUT_FOCALTECH_MMI_ENABLE_PALM)),) + EXTRA_CFLAGS += -DFOCALTECH_PALM_SENSOR_EN +endif +ifneq ($(filter m y,$(CONFIG_INPUT_FOCALTECH_MMI_PEN_NOTIFIER)),) + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../misc/pen/Module.symvers + EXTRA_CFLAGS += -DFOCALTECH_PEN_NOTIFIER +endif + +ifneq ($(filter m y, $(CONFIG_DRM_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_NOTIFICATIONS +endif +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/../../kernel/msm-$(MODULE_KERNEL_VERSION)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/Kconfig b/drivers/input/touchscreen/focaltech_v2_mmi/Kconfig new file mode 100644 index 000000000000..0c327bac9a96 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/Kconfig @@ -0,0 +1,17 @@ +# +# Focaltech Touchscreen driver configuration +# + +config TOUCHSCREEN_FTS + bool "Focaltech Touchscreen" + depends on I2C + default n + help + Say Y here if you have Focaltech touch panel. + If unsure, say N. + +config TOUCHSCREEN_FTS_DIRECTORY + string "Focaltech ts directory name" + default "focaltech_touch" + depends on TOUCHSCREEN_FTS + diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/Makefile b/drivers/input/touchscreen/focaltech_v2_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/Makefile.kernel b/drivers/input/touchscreen/focaltech_v2_mmi/Makefile.kernel new file mode 100755 index 000000000000..279d9d7a510e --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/Makefile.kernel @@ -0,0 +1,17 @@ +# Makefile for the focaltech touchscreen drivers. + + +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_core.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_ex_fun.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_ex_mode.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_gesture.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_esdcheck.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_point_report_check.o + + +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_flash.o +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_flash/ + + + +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_i2c.o diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_common.h b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_common.h new file mode 100644 index 000000000000..64de650df99f --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_common.h @@ -0,0 +1,171 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 (FTS_CHIP_TYPE & FLAGBITS(0, FLAG_ICSERIALS_LEN-1)) +#define IC_TO_SERIALS(x) ((x) & FLAGBITS(0, FLAG_ICSERIALS_LEN-1)) +#define FTS_CHIP_IDC ((FTS_CHIP_TYPE & FLAGBIT(FLAG_IDC_BIT)) == FLAGBIT(FLAG_IDC_BIT)) +#define FTS_HID_SUPPORTTED ((FTS_CHIP_TYPE & FLAGBIT(FLAG_HID_BIT)) == FLAGBIT(FLAG_HID_BIT)) + +#if defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT3519) +#define FTS_CHIP_TYPE_MAPPING {{0x89, 0x54, 0x52, 0x54, 0x52, 0x54, 0x5B, 0x54, 0x5E}} +#else +#define FTS_CHIP_TYPE_MAPPING {{0x81, 0x54, 0x52, 0x54, 0x52, 0x00, 0x00, 0x54, 0x5C}} +#endif + +#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_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 1 + +/***************************************************************************** +* 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__ */ diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_config.h b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_config.h new file mode 100644 index 000000000000..22431acca2ae --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_config.h @@ -0,0 +1,292 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 _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 _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 _FT3519 0x35190489 + +#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 + +/*************************************************/ + +/* + * choose your ic chip type of focaltech + */ + #if defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT3519) +#define FTS_CHIP_TYPE _FT3519 +#define FTS_CHIP_NAME "ft3519" +#else +#define FTS_CHIP_TYPE _FT3518U +#define FTS_CHIP_NAME "ft3518u" +#endif + +/******************* 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 + */ +#ifdef FOCALTECH_SENSOR_EN +#define FTS_GESTURE_EN 1 +#else +#define FTS_GESTURE_EN 0 +#endif + +/* + * ESD check & protection + * default: disable + */ +#define FTS_ESDCHECK_EN 0 + + +/* + * Pinctrl enable + * default: disable + */ +#define FTS_PINCTRL_EN 0 + +/* + * 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 0 + +/* + * auto upgrade for lcd cfg + */ +#define FTS_AUTO_LIC_UPGRADE_EN 0 + +/* + * Numbers of modules support + */ +#define FTS_GET_MODULE_NUM 0 + +/* + * Usb detect support + */ +#define FTS_USB_DETECT_EN 1 + +/* + * 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 0x0000 +#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 "" +#define FTS_MODULE2_NAME "" +#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_ */ diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_core.c b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_core.c new file mode 100644 index 000000000000..b06a734f4184 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_core.c @@ -0,0 +1,2235 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 +#include +#include +#include +#include +#include +#if defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) +#include +#else +#include +#endif +#elif defined(CONFIG_FB) +#include +#include +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#define FTS_SUSPEND_LEVEL 1 /* Early-suspend level */ +#endif +#include "focaltech_core.h" +#include + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +extern int fts_mmi_dev_register(struct fts_ts_data *ts_data); +extern void fts_mmi_dev_unregister(struct fts_ts_data *ts_data); +#endif + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_DRIVER_NAME "fts_ts" +#define INTERVAL_READ_REG 50 /* 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_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; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +static int fts_ts_suspend(struct device *dev); +static int fts_ts_resume(struct device *dev); +#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) { + disable_irq_nosync(fts_data->irq); + 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) { + enable_irq(fts_data->irq); + 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"); + } else { + msleep(10); + 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 ((0xEB == buf[0]) && (0xAA == buf[1]) && (0x08 == buf[2])) { + 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; + struct ft_chip_t ctype[] = FTS_CHIP_TYPE_MAPPING; + 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 }; + + ts_data->ic_info.is_incell = FTS_CHIP_IDC; + ts_data->ic_info.hid_supported = FTS_HID_SUPPORTTED; + + + 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("chip id 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; + } + } + +#if defined(CONFIG_INPUT_FOCALTECH_0FLASH_MMI_IC_NAME_FT3519) + chip_id[0] = 0x54; + chip_id[1] = 0x5E; +#else + chip_id[0] = 0x54; + chip_id[1] = 0x5C; +#endif + + 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; +} + +#ifdef FOCALTECH_PALM_SENSOR_EN +static int fts_palm_detect(u8 reg_data) { + int fd_val = reg_data & 0x03; + + FTS_DEBUG("%s: 0x01=0x%0X!, fd_val=%d\n", __func__, reg_data, fd_val); + if (fd_val == 1) { + FTS_INFO("%s: palm detect!\n", __func__); + return 1; + } + if (fd_val == 2) { + FTS_INFO("%s: palm leave!\n", __func__); + return 2; + } + return 0; +} +#endif + +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; +#ifdef FOCALTECH_PALM_SENSOR_EN + int pd_state = 0; +#endif + + ret = fts_read_touchdata(data); + if (ret) { + return ret; + } + +#ifdef FOCALTECH_PALM_SENSOR_EN + if (data->palm_detection_enabled) { + pd_state = fts_palm_detect(buf[1]); + if (pd_state > 0) { + if (pd_state == 1) { + del_timer(&fts_data->palm_release_fimer); + if (fts_data->imports && fts_data->imports->report_palm) + fts_data->imports->report_palm(true); + return -1; + } else if (pd_state == 2) { + mod_timer(&fts_data->palm_release_fimer, + jiffies + msecs_to_jiffies(fts_data->palm_release_delay_ms)); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&fts_data->palm_gesture_wakelock, + fts_data->palm_release_delay_ms); +#else + PM_WAKEUP_EVENT(fts_data->palm_gesture_wakelock, + fts_data->palm_release_delay_ms); +#endif + return -1; + } + } + if (buf[1] & 0x08) { + FTS_ERROR("Invalid palm detect value. 0x%0X", buf[1]); + return -1; + } + } +#endif + + 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; +} + +#if FTS_USB_DETECT_EN +static void fts_mcu_usb_detect_set(uint8_t usb_connected) +{ + uint8_t write_data = 0; + uint8_t read_data = 0; + uint8_t retry_cnt = 0; + int ret = 0; + + do{ + if (usb_connected == 0x01) { + write_data= 0x01; + ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, write_data); + if (ret < 0) + FTS_ERROR("set register USB IN fail, ret=%d", ret); + FTS_INFO("%s: USB detect status IN!\n", __func__); + } else { + write_data= 0x00; + ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, write_data); + if (ret < 0) + FTS_ERROR("set register USB OUT fail, ret=%d", ret); + FTS_INFO("%s: USB detect status OUT!\n", __func__); + } + + ret = fts_read_reg(FTS_REG_CHARGER_MODE_EN, &read_data); + if (ret < 0) + FTS_ERROR("read 8b register fail, ret=%d", ret); + retry_cnt++; + }while((write_data != read_data) && retry_cnt < FTS_REG_RETRY_TIMES); +} + +void fts_cable_detect_func(bool force_renew) +{ + struct fts_ts_data *ts_data = fts_data; + uint8_t connect_status = 0; + connect_status = ts_data->usb_detect_flag; + + if ((connect_status != ts_data->usb_connected) || force_renew) { + if (connect_status) { + ts_data->usb_connected = 0x01; + } else { + ts_data->usb_connected = 0x00; + } + + fts_mcu_usb_detect_set(ts_data->usb_connected); + FTS_INFO("%s: Cable status change: 0x%2.2X\n", __func__, ts_data->usb_connected); + } +} +#endif + +static void fts_irq_read_report(void) +{ + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + +#if FTS_USB_DETECT_EN + fts_cable_detect_func(false); +#endif + +#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) +{ +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + + if ((ts_data->suspended) && (ts_data->pm_suspend)) { + ret = wait_for_completion_timeout( + &ts_data->pm_completion, + msecs_to_jiffies(FTS_TIMEOUT_COMERR_PM)); + if (!ret) { + FTS_ERROR("Bus don't resume from pm(deep),timeout,skip irq"); + return IRQ_HANDLED; + } + } +#endif + + fts_irq_read_report(); + 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; + + 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); + + 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 = ts_data->pdata->max_touch_number; + ts_data->pnt_buf_size = point_num * FTS_ONE_TCH_LEN + 3; + 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; +} + +#ifdef FOCALTECH_PALM_SENSOR_EN +static void fts_palm_sensor_release_timer_handler(struct timer_list *t) +{ + if (fts_data->imports && fts_data->imports->report_palm) + fts_data->imports->report_palm(false); +} + +int _fts_palm_sensor_set_enable(unsigned int enable) +{ + FTS_INFO("Palm gesture set enable %d!", enable); +/* + * If palm detect function is enabled, interrupt will not disable, IC works in + * normal mode. But in case touch event is reported to input subsystem, skip + * touch event when suspend flag is true. So input subsystem will not take + * wakelock because no one report event. + * In this case, we still need read data from IC, so AP can not enter suspend. + */ + if (enable == 1) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock(&fts_data->palm_gesture_read_wakelock); +#else + PM_STAY_AWAKE(fts_data->palm_gesture_read_wakelock); +#endif + fts_data->palm_detection_enabled = true; + fts_write_reg(0xB0, 0x01); + } else if (enable == 0) { + fts_data->palm_detection_enabled = false; + if (timer_pending(&fts_data->palm_release_fimer)) { + if (fts_data->imports && fts_data->imports->report_palm) + fts_data->imports->report_palm(false); + del_timer(&fts_data->palm_release_fimer); + } + fts_write_reg(0xB0, 0x00); +#ifdef CONFIG_HAS_WAKELOCK + wake_unlock(&fts_data->palm_gesture_read_wakelock); +#else + PM_RELAX(fts_data->palm_gesture_read_wakelock); +#endif + } else { + FTS_INFO("unknown enable symbol\n"); + } + return 0; +} + +int fts_palm_sensor_set_enable(unsigned int enable) +{ + int ret = 0; + + if (!fts_data->suspended) + ret = _fts_palm_sensor_set_enable(enable); + else { + FTS_INFO("Gesture lazy set enable %d!", enable); + if (enable == 1) + fts_data->palm_detection_lazy_set = PALM_SENSOR_LAZY_SET_ENABLE; + else if (enable == 0) + fts_data->palm_detection_lazy_set = PALM_SENSOR_LAZY_SET_DISABLE; + else + FTS_INFO("unknown enable symbol\n"); + } + if (fts_data->imports && fts_data->imports->report_palm) + fts_data->imports->report_palm(false); + return ret; +} + +static int fts_palm_sensor_init(struct fts_ts_data *data) +{ +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&data->palm_gesture_wakelock, WAKE_LOCK_SUSPEND, "palm_detect_wl"); +#else + PM_WAKEUP_REGISTER(NULL, data->palm_gesture_wakelock, "palm_detect_wl"); + if (!data->palm_gesture_wakelock) { + FTS_ERROR("Unable to register device"); + goto exit; + } +#endif +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&data->palm_gesture_read_wakelock, WAKE_LOCK_SUSPEND, "palm_read_wl"); +#else + PM_WAKEUP_REGISTER(NULL, data->palm_gesture_read_wakelock, "palm_read_wl"); + if (!data->palm_gesture_wakelock) { + FTS_ERROR("Unable to register device"); + goto exit; + } +#endif + + timer_setup(&data->palm_release_fimer, fts_palm_sensor_release_timer_handler, 0); + data->palm_release_delay_ms = 850; + + return 0; + +exit: + return 1; +} + +int fts_palm_sensor_remove(struct fts_ts_data *data) +{ +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&data->palm_gesture_wakelock); +#else + PM_WAKEUP_UNREGISTER(data->palm_gesture_wakelock); +#endif +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&data->palm_gesture_read_wakelock); +#else + PM_WAKEUP_UNREGISTER(data->palm_gesture_read_wakelock); +#endif + data->palm_detection_enabled = false; + return 0; +} +#endif + +#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 */ + +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 = 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); + } + } + 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 = 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); + } + } + 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; + } + + if (regulator_count_voltages(ts_data->vdd) > 0) { + 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); + regulator_put(ts_data->vdd); + return ret; + } + ret = regulator_set_load(ts_data->vdd, FTS_LOAD_MAX_UA); + if (ret) { + FTS_ERROR("vdd regulator set_load failed ret=%d", ret); + regulator_put(ts_data->vdd); + return ret; + } + } + + ts_data->vcc_i2c = regulator_get(ts_data->dev, "vcc_i2c"); + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + if (regulator_count_voltages(ts_data->vcc_i2c) > 0) { + 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); + regulator_put(ts_data->vcc_i2c); + } + } + } + +#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; +} + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +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 +#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; + } + +#if (FTS_CHIP_IDC) + 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; + } +#endif + } + + FTS_FUNC_EXIT(); + return 0; + +#if (FTS_CHIP_IDC) +err_reset_gpio_dir: + if (gpio_is_valid(data->pdata->reset_gpio)) + gpio_free(data->pdata->reset_gpio); +#endif +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); + + FTS_FUNC_EXIT(); + return 0; +} + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +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); +} + +#if defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) +struct drm_panel *active_panel; + +static int drm_check_dt(struct device_node *np) +{ + int i = 0; + int count = 0; + struct device_node *node = NULL; + struct drm_panel *panel = NULL; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) { + FTS_ERROR("find drm_panel count(%d) fail", count); + return -ENODEV; + } + + 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)) { + FTS_INFO("find drm_panel successfully"); + active_panel = panel; + return 0; + } + } + + FTS_ERROR("no find drm_panel"); + return -ENODEV; +} + +static int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct drm_panel_notifier *evdata = data; + int *blank = NULL; + struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data, + fb_notif); + + if (!evdata) { + FTS_ERROR("evdata is null"); + return 0; + } + + if (!((event == DRM_PANEL_EARLY_EVENT_BLANK ) + || (event == DRM_PANEL_EVENT_BLANK))) { + FTS_INFO("event(%lu) do not need process\n", event); + return 0; + } + + blank = evdata->data; + FTS_INFO("DRM event:%lu,blank:%d", event, *blank); + switch (*blank) { + case DRM_PANEL_BLANK_UNBLANK: + if (DRM_PANEL_EARLY_EVENT_BLANK == event) { + FTS_INFO("resume: event = %lu, not care\n", event); + } else if (DRM_PANEL_EVENT_BLANK == event) { + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + } + break; + case DRM_PANEL_BLANK_POWERDOWN: + if (DRM_PANEL_EARLY_EVENT_BLANK == event) { + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); + } else if (DRM_PANEL_EVENT_BLANK == event) { + FTS_INFO("suspend: event = %lu, not care\n", event); + } + break; + default: + FTS_INFO("DRM BLANK(%d) do not need process\n", *blank); + break; + } + + return 0; +} +#else +static int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank = NULL; + struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data, + fb_notif); + + if (!evdata) { + FTS_ERROR("evdata is null"); + return 0; + } + + if (!((event == MSM_DRM_EARLY_EVENT_BLANK ) + || (event == MSM_DRM_EVENT_BLANK))) { + FTS_INFO("event(%lu) do not need process\n", event); + return 0; + } + + blank = evdata->data; + FTS_INFO("DRM event:%lu,blank:%d", event, *blank); + switch (*blank) { + case MSM_DRM_BLANK_UNBLANK: + if (MSM_DRM_EARLY_EVENT_BLANK == event) { + FTS_INFO("resume: event = %lu, not care\n", event); + } else if (MSM_DRM_EVENT_BLANK == event) { + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + } + break; + case MSM_DRM_BLANK_POWERDOWN: + if (MSM_DRM_EARLY_EVENT_BLANK == event) { + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); + } else if (MSM_DRM_EVENT_BLANK == event) { + FTS_INFO("suspend: event = %lu, not care\n", event); + } + break; + default: + FTS_INFO("DRM BLANK(%d) do not need process\n", *blank); + break; + } + + return 0; +} +#endif +#elif defined(CONFIG_FB) +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 (!evdata) { + FTS_ERROR("evdata is null"); + return 0; + } + + 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); + + cancel_work_sync(&fts_data->resume_work); + 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); + + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); +} +#endif +#endif + +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); + + if (ts_data->client->dev.of_node && !mmi_device_is_available(ts_data->client->dev.of_node)) { + FTS_ERROR("mmi: device not supported\n"); + return -ENODEV; + } + + 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"); + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) + ret = drm_check_dt(ts_data->dev->of_node); + if (ret) { + FTS_ERROR("parse drm-panel fail"); + } +#endif +#endif +#endif + } 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); + + /* 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_gpio_configure(ts_data); + if (ret) { + FTS_ERROR("configure the gpios fail"); + goto err_gpio_config; + } + +#if FTS_POWER_SOURCE_CUST_EN + ret = fts_power_source_init(ts_data); + if (ret) { + FTS_ERROR("fail to get power(regulator)"); + goto err_power_init; + } +#endif + +#if (!FTS_CHIP_IDC) + fts_reset_proc(150); +#endif + + ret = fts_get_ic_information(ts_data); + if (ret) { + FTS_ERROR("not focal IC, unregister driver"); + goto err_irq_req; + } + + 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"); + } + + +#ifdef FOCALTECH_PALM_SENSOR_EN + ret = fts_palm_sensor_init(ts_data); + if (ret) { + FTS_ERROR("init palm detect sensor fail"); + } +#endif + +#if FTS_ESDCHECK_EN + ret = fts_esdcheck_init(ts_data); + if (ret) { + FTS_ERROR("init esd check fail"); + } +#endif + + ret = fts_irq_registration(ts_data); + if (ret) { + FTS_ERROR("request irq failed"); + goto err_irq_req; + } + + ret = fts_fwupg_init(ts_data); + if (ret) { + FTS_ERROR("init fw upgrade fail"); + } + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + if (ts_data->ts_workqueue) { + INIT_WORK(&ts_data->resume_work, fts_resume_work); + } +#endif + +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM + init_completion(&ts_data->pm_completion); + ts_data->pm_suspend = false; +#endif + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if defined(CONFIG_DRM) + ts_data->fb_notif.notifier_call = drm_notifier_callback; +#if defined(CONFIG_DRM_PANEL) + if (active_panel) { + ret = drm_panel_notifier_register(active_panel, &ts_data->fb_notif); + if (ret) + FTS_ERROR("[DRM]drm_panel_notifier_register fail: %d\n", ret); + } +#else + ret = msm_drm_register_client(&ts_data->fb_notif); + if (ret) { + FTS_ERROR("[DRM]Unable to register fb_notifier: %d\n", ret); + } +#endif +#elif defined(CONFIG_FB) + 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 +#endif + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + fts_mmi_dev_register(ts_data); +#endif + + FTS_FUNC_EXIT(); + return 0; + +err_irq_req: +#if FTS_POWER_SOURCE_CUST_EN +err_power_init: + fts_power_source_exit(ts_data); +#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); +err_gpio_config: + 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(); + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + fts_mmi_dev_unregister(ts_data); +#endif + +#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 + +#ifdef FOCALTECH_PALM_SENSOR_EN + fts_palm_sensor_remove(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); + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) + if (active_panel) + drm_panel_notifier_unregister(active_panel, &ts_data->fb_notif); +#else + if (msm_drm_unregister_client(&ts_data->fb_notif)) + FTS_ERROR("[DRM]Error occurred while unregistering fb_notifier.\n"); +#endif +#elif defined(CONFIG_FB) + if (fb_unregister_client(&ts_data->fb_notif)) + FTS_ERROR("[FB]Error occurred while unregistering fb_notifier."); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts_data->early_suspend); +#endif +#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; +} + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +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 FOCALTECH_PALM_SENSOR_EN + if (ts_data->palm_detection_lazy_set == PALM_SENSOR_LAZY_SET_ENABLE) { + FTS_INFO("palm detection enabled, can't suspend"); + return 0; + } +#endif + +#if FTS_ESDCHECK_EN + fts_esdcheck_suspend(); +#endif + +#ifdef FOCALTECH_PALM_SENSOR_EN + if (ts_data->palm_detection_enabled) { + ret = enable_irq_wake(ts_data->irq); + if (ret) { + FTS_DEBUG("enable_irq_wake(irq:%d) fail", ts_data->irq); + } + fts_release_all_finger(); + ts_data->suspended = true; + FTS_INFO("Enter from palm detect suspend mode."); + return 0; + } +#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 + } + } + + fts_release_all_finger(); + ts_data->suspended = true; + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_ts_resume(struct device *dev) +{ + struct fts_ts_data *ts_data = fts_data; + + FTS_FUNC_ENTER(); + if (!ts_data->suspended) { + FTS_DEBUG("Already in awake state"); + return 0; + } + +#ifdef FOCALTECH_PALM_SENSOR_EN + if (ts_data->palm_detection_lazy_set == PALM_SENSOR_LAZY_SET_ENABLE) { + FTS_INFO("palm detection enabled, don't need to resume"); + return 0; + } +#endif + + fts_release_all_finger(); + + if (!ts_data->ic_info.is_incell) { +#if FTS_POWER_SOURCE_CUST_EN + fts_power_source_resume(ts_data); +#endif + fts_reset_proc(150); + } + + 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; +#if FTS_USB_DETECT_EN + fts_cable_detect_func(true); +#endif + + FTS_FUNC_EXIT(); + return 0; +} +#endif + +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM +static int fts_pm_suspend(struct device *dev) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + FTS_INFO("system enters into pm_suspend"); + ts_data->pm_suspend = true; + reinit_completion(&ts_data->pm_completion); + return 0; +} + +static int fts_pm_resume(struct device *dev) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + FTS_INFO("system resumes from pm_suspend"); + ts_data->pm_suspend = false; + complete(&ts_data->pm_completion); + return 0; +} + +static const struct dev_pm_ops fts_dev_pm_ops = { + .suspend = fts_pm_suspend, + .resume = fts_pm_resume, +}; +#endif + +/***************************************************************************** +* TP Driver +*****************************************************************************/ + +static int fts_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + struct fts_ts_data *ts_data = NULL; + + FTS_INFO("Touch Screen(I2C BUS) driver prboe..."); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + FTS_ERROR("I2C not supported"); + return -ENODEV; + } + + /* 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; +} + +static int fts_ts_remove(struct i2c_client *client) +{ + return fts_ts_remove_entry(i2c_get_clientdata(client)); +} + +static const struct i2c_device_id fts_ts_id[] = { + {FTS_DRIVER_NAME, 0}, + {}, +}; +static const struct of_device_id fts_dt_match[] = { + {.compatible = "focaltech,fts", }, + {}, +}; +MODULE_DEVICE_TABLE(of, fts_dt_match); + +static struct i2c_driver fts_ts_driver = { + .probe = fts_ts_probe, + .remove = fts_ts_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_id, +}; + +static int __init fts_ts_init(void) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + ret = i2c_add_driver(&fts_ts_driver); + if ( ret != 0 ) { + FTS_ERROR("Focaltech touch screen driver init failed!"); + } + FTS_FUNC_EXIT(); + return ret; +} + +static void __exit fts_ts_exit(void) +{ + i2c_del_driver(&fts_ts_driver); +} +module_init(fts_ts_init); +module_exit(fts_ts_exit); + +MODULE_AUTHOR("FocalTech Driver Team"); +MODULE_DESCRIPTION("FocalTech Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_core.h b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_core.h new file mode 100644 index 000000000000..1da855397976 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_core.h @@ -0,0 +1,338 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "focaltech_common.h" +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +#include +#endif +#ifdef FTS_USB_DETECT_EN +#include +#endif + +/***************************************************************************** +* 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 +#ifdef FTS_USB_DETECT_EN +#define FTS_REG_RETRY_TIMES 5 +#endif + + +/***************************************************************************** +* 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 1 +#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 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; +}; + +#ifdef FOCALTECH_PALM_SENSOR_EN +enum palm_sensor_lazy_set { + PALM_SENSOR_LAZY_SET_NONE = 0, + PALM_SENSOR_LAZY_SET_ENABLE, + PALM_SENSOR_LAZY_SET_DISABLE, +}; +#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 ftxxxx_proc proc; + spinlock_t irq_lock; + struct mutex report_mutex; + struct mutex bus_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 force_reflash; + bool irq_disabled; + bool power_disabled; + bool glove_mode; + bool cover_mode; + bool charger_mode; + bool gesture_mode; /* gesture enable or disable, default: disable */ + /* multi-touch */ + struct ts_event *events; + u8 *bus_tx_buf; + u8 *bus_rx_buf; + int bus_type; + u8 *point_buf; + 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 + +#if FTS_USB_DETECT_EN + bool usb_detect_flag; + uint8_t usb_connected; +#endif + +#ifdef FOCALTECH_SENSOR_EN + bool wakeable; +#endif + +#ifdef FOCALTECH_PALM_SENSOR_EN + bool palm_detection_enabled; + enum palm_sensor_lazy_set palm_detection_lazy_set; + struct timer_list palm_release_fimer; + unsigned int palm_release_delay_ms; +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock palm_gesture_wakelock; +#else + struct wakeup_source *palm_gesture_wakelock; +#endif +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock palm_gesture_read_wakelock; +#else + struct wakeup_source *palm_gesture_read_wakelock; +#endif +#endif + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + struct ts_mmi_class_methods *imports; +#endif +}; + +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); + +#ifdef FOCALTECH_PALM_SENSOR_EN +int fts_palm_sensor_set_enable(unsigned int enable); +#endif + +/* 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 + + +/* 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_upgrade_bin(char *fw_name, bool force); +void fts_fwupg_bin(void); +int fts_enter_test_environment(bool test_state); +int fts_fw_update_vendor_name(const char* name); + +/* 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_power_source_ctrl(struct fts_ts_data *ts_data, int enable); +#endif /* __LINUX_FOCALTECH_CORE_H__ */ diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_esdcheck.c b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_esdcheck.c new file mode 100644 index 000000000000..1bf1251df2f9 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_esdcheck.c @@ -0,0 +1,464 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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(®_addr, 1, ®_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(®_addr, 1, ®_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, ®_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 function"); + 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 */ + diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_ex_fun.c b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_ex_fun.c new file mode 100644 index 000000000000..4d7cd8d0fefd --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_ex_fun.c @@ -0,0 +1,1191 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 "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) { + FTS_ERROR("apk proc wirte count(%d) fail", buflen); + 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 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) { + FTS_ERROR("apk proc read count(%d) fail", buflen); + 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 + + ret = num_read_chars; +proc_read_err: + if (copy_to_user(buff, readbuf, num_read_chars)) { + FTS_ERROR("copy to user error"); + ret = -EFAULT; + } + + if ((buflen > PROC_BUF_SIZE) && readbuf) { + kfree(readbuf); + readbuf = NULL; + } + return ret; +} + +static const struct file_operations fts_proc_fops = { + .owner = THIS_MODULE, + .read = fts_debug_read, + .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) { + FTS_ERROR("apk proc wirte count(%d) fail", buflen); + 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) { + FTS_ERROR("apk proc read count(%d) fail", buflen); + 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 + + ret = num_read_chars; +proc_read_err: + if (copy_to_user(buff, readbuf, num_read_chars)) { + FTS_ERROR("copy to user error"); + ret = -EFAULT; + } + + 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_tpfwver_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int ret = 0; + 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 + ret = fts_read_reg(FTS_REG_FW_VER, &fwver); +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + if ((ret < 0) || (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_tpfwver_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!"); + + } else { + 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(®, 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; + } + } + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* fts_upgrade_bin interface */ +static ssize_t fts_fwupgradebin_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +static ssize_t fts_fwupgradebin_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char fwname[FILE_NAME_LENGTH] = { 0 }; + struct input_dev *input_dev = fts_data->input_dev; + + if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) { + FTS_ERROR("fw bin name's length(%d) fail", (int)count); + return -EINVAL; + } + memset(fwname, 0, sizeof(fwname)); + snprintf(fwname, FILE_NAME_LENGTH, "%s", buf); + fwname[count - 1] = '\0'; + + FTS_INFO("upgrade with bin file through sysfs node"); + mutex_lock(&input_dev->mutex); + fts_upgrade_bin(fwname, 0); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* fts_force_upgrade interface */ +static ssize_t fts_fwforceupg_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +static ssize_t fts_fwforceupg_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char fwname[FILE_NAME_LENGTH]; + struct input_dev *input_dev = fts_data->input_dev; + + if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) { + FTS_ERROR("fw bin name's length(%d) fail", (int)count); + return -EINVAL; + } + memset(fwname, 0, sizeof(fwname)); + snprintf(fwname, FILE_NAME_LENGTH, "%s", buf); + fwname[count - 1] = '\0'; + + FTS_INFO("force upgrade through sysfs node"); + mutex_lock(&input_dev->mutex); + fts_upgrade_bin(fwname, 1); + 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 += snprintf(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 += snprintf(buf + count, PAGE_SIZE, + "reset gpio:%d,int gpio:%d,irq:%d\n", + pdata->reset_gpio, pdata->irq_gpio, ts_data->irq); + + count += snprintf(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 += snprintf(buf + count, PAGE_SIZE, "BUS:%s,addr:0x%x\n", + "I2C", ts_data->client->addr); + } else { + count += snprintf(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_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; +} + +/* get the fw version example:cat fw_version */ +static DEVICE_ATTR(fts_fw_version, S_IRUGO | S_IWUSR, fts_tpfwver_show, fts_tpfwver_store); + +/* 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); +/* upgrade from fw bin file example:echo "*.bin" > fts_upgrade_bin */ +static DEVICE_ATTR(fts_upgrade_bin, S_IRUGO | S_IWUSR, fts_fwupgradebin_show, fts_fwupgradebin_store); +static DEVICE_ATTR(fts_force_upgrade, S_IRUGO | S_IWUSR, fts_fwforceupg_show, fts_fwforceupg_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); + +/* 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_upgrade_bin.attr, + &dev_attr_fts_force_upgrade.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, + 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; +} diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_ex_mode.c b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_ex_mode.c new file mode 100644 index 000000000000..848c524467fe --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_ex_mode.c @@ -0,0 +1,307 @@ +/* + * + * FocalTech ftxxxx TouchScreen driver. + * + * Copyright (c) 2012-2020, 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, +}; + +/***************************************************************************** +* 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; + u8 m_val = 0; + + if (value) + m_val = 0x01; + else + m_val = 0x00; + + switch (mode) { + case MODE_GLOVE: + ret = fts_write_reg(FTS_REG_GLOVE_MODE_EN, m_val); + if (ret < 0) { + FTS_ERROR("MODE_GLOVE switch to %d fail", m_val); + } + break; + case MODE_COVER: + ret = fts_write_reg(FTS_REG_COVER_MODE_EN, m_val); + if (ret < 0) { + FTS_ERROR("MODE_COVER switch to %d fail", m_val); + } + break; + case MODE_CHARGER: + ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, m_val); + if (ret < 0) { + FTS_ERROR("MODE_CHARGER switch to %d fail", m_val); + } + 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 = snprintf(buf + count, PAGE_SIZE, "Glove Mode:%s\n", + ts_data->glove_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "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 = snprintf(buf + count, PAGE_SIZE, "Cover Mode:%s\n", + ts_data->cover_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "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 = snprintf(buf + count, PAGE_SIZE, "Charger Mode:%s\n", + ts_data->charger_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "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; +} + + +/* 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 struct attribute *fts_touch_mode_attrs[] = { + &dev_attr_fts_glove_mode.attr, + &dev_attr_fts_cover_mode.attr, + &dev_attr_fts_charger_mode.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); + } + + 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; + + 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; +} diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_flash.c b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_flash.c new file mode 100644 index 000000000000..170d2103bd54 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_flash.c @@ -0,0 +1,2180 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 "" + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +u8 fw_file[] = { +}; + +u8 fw_file2[] = { +}; + +u8 fw_file3[] = { +}; + +struct upgrade_module module_list[] = { + {FTS_MODULE_ID, FTS_MODULE_NAME, fw_file, sizeof(fw_file)}, + {FTS_MODULE2_ID, FTS_MODULE2_NAME, fw_file2, sizeof(fw_file2)}, + {FTS_MODULE3_ID, FTS_MODULE3_NAME, fw_file3, sizeof(fw_file3)}, +}; + +struct upgrade_func *upgrade_func_list[] = { + &upgrade_func_ft5452i, + &upgrade_func_ft5452, +}; + +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, ®_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 ((ECC_CHECK_MODE_CRC16 == upg->func->pram_ecc_check_mode) || + (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) { + return fts_pram_ecc_cal_algo(upg, saddr, len); + } else { + 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 (ECC_CHECK_MODE_XOR == upg->func->pram_ecc_check_mode) { + 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 ((ECC_CHECK_MODE_CRC16 == upg->func->pram_ecc_check_mode) || + (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, ®_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; +} + +/************************************************************************ +* Name: fts_fwupg_enter_into_boot +* Brief: enter into boot environment, ready for upgrade +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_fwupg_enter_into_boot(void) +{ + int ret = 0; + bool fwvalid = false; + bool state = false; + struct fts_upgrade *upg = fwupgrade; + + 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 */ + 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; +} + +/************************************************************************ + * 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 ((ECC_CHECK_MODE_CRC16 == upg->func->fw_ecc_check_mode) || + (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 ((ECC_CHECK_MODE_CRC16 == upg->func->fw_ecc_check_mode) || + (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 (wr_ok == read_status) { + break; + } + mdelay(FTS_RETRIES_DELAY_WRITE); + } + } + + ecc_in_host = (int)ecc_tmp; + if ((ECC_CHECK_MODE_CRC16 == upg->func->fw_ecc_check_mode) || + (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; +} + +static int fts_read_file(char *file_name, u8 **file_buf) +{ + int ret = 0; + char file_path[FILE_NAME_LENGTH] = { 0 }; + struct file *filp = NULL; + struct inode *inode; + mm_segment_t old_fs; + loff_t pos; + loff_t file_len = 0; + + if ((NULL == file_name) || (NULL == file_buf)) { + FTS_ERROR("filename/filebuf is NULL"); + return -EINVAL; + } + + snprintf(file_path, FILE_NAME_LENGTH, "%s%s", FTS_FW_BIN_FILEPATH, file_name); + filp = filp_open(file_path, O_RDONLY, 0); + if (IS_ERR(filp)) { + FTS_ERROR("open %s file fail", file_path); + return -ENOENT; + } + +#if 1 + inode = filp->f_inode; +#else + /* reserved for linux earlier verion */ + inode = filp->f_dentry->d_inode; +#endif + + file_len = inode->i_size; + *file_buf = (u8 *)vmalloc(file_len); + if (NULL == *file_buf) { + FTS_ERROR("file buf malloc fail"); + filp_close(filp, NULL); + return -ENOMEM; + } + old_fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + ret = vfs_read(filp, *file_buf, file_len , &pos); +#endif + if (ret < 0) + FTS_ERROR("read file fail"); + FTS_INFO("file len:%d read len:%d pos:%d", (u32)file_len, ret, (u32)pos); + filp_close(filp, NULL); + set_fs(old_fs); + + return ret; +} + +int fts_upgrade_bin(char *fw_name, bool force) +{ + int ret = 0; + u32 fw_file_len = 0; + u8 *fw_file_buf = NULL; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("start upgrade with fw bin"); + if ((!upg) || (!upg->func) || !upg->ts_data) { + FTS_ERROR("upgrade/func/ts_data is null"); + return -EINVAL; + } + + upg->ts_data->fw_loading = 1; + fts_irq_disable(); +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(DISABLE); +#endif + + ret = fts_read_file(fw_name, &fw_file_buf); + if ((ret < 0) || (ret < FTS_MIN_LEN)) { + FTS_ERROR("read fw bin file(%s) fail, len:%d", fw_name, ret); + goto err_bin; + } + + fw_file_len = ret; + FTS_INFO("fw bin file len:%d", fw_file_len); + if (force) { + if (upg->func->force_upgrade) { + ret = upg->func->force_upgrade(fw_file_buf, fw_file_len); + } else { + FTS_INFO("force_upgrade function is null, no upgrade"); + goto err_bin; + } + } else { +#if FTS_AUTO_LIC_UPGRADE_EN + if (upg->func->lic_upgrade) { + ret = upg->func->lic_upgrade(fw_file_buf, fw_file_len); + } else { + FTS_INFO("lic_upgrade function is null, no upgrade"); + } +#endif + if (upg->func->upgrade) { + ret = upg->func->upgrade(fw_file_buf, fw_file_len); + } else { + FTS_INFO("upgrade function is null, no upgrade"); + } + } + + if (ret < 0) { + FTS_ERROR("upgrade fw bin failed"); + fts_fwupg_reset_in_boot(); + goto err_bin; + } + + FTS_INFO("upgrade fw bin success"); + ret = 0; + +err_bin: +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ENABLE); +#endif + fts_irq_enable(); + upg->ts_data->fw_loading = 0; + + if (fw_file_buf) { + vfree(fw_file_buf); + fw_file_buf = NULL; + } + 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) { + FTS_ERROR("no module id match, don't get file"); + return -ENODATA; + } + } + + upg->module_info = info; + return 0; +} + +int fts_fw_update_vendor_name(const char* name) { + struct fts_upgrade *upg = fwupgrade; + char* pos; + int len; + + fts_fwupg_get_module_info(fwupgrade); + + if (!upg || !upg->module_info) { + FTS_ERROR("upgrade struct not init"); + return -EINVAL; + } + + len = strlen(name); + if (len > FILE_NAME_LENGTH) { + FTS_ERROR("vendor name is too long"); + return -EINVAL; + } + pos = strstr(name, ".bin"); + if (pos == NULL) + snprintf(upg->module_info->vendor_name, FILE_NAME_LENGTH, "%s", name); + else { + len = len - strlen(".bin"); + snprintf(upg->module_info->vendor_name, len + 1, "%s", name); + } + 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; + } + + 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 (0 == ret) { + 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) { + 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); + } +} + +void fts_fwupg_bin(void) +{ + int ret = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("fw upgrade bin 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; +} + +/***************************************************************************** + * 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; +} diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_flash.h b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_flash.h new file mode 100644 index 000000000000..7d0464632763 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_flash.h @@ -0,0 +1,216 @@ +/************************************************************************ +* Copyright (c) 2012-2020, 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_DATA_LEN 0xB0 +#define FTS_CMD_APP_DATA_LEN_INCELL 0x7A +#define FTS_CMD_DATA_LEN_LEN 4 +#define FTS_CMD_SET_WFLASH_ADDR 0xAB +#define FTS_CMD_SET_RFLASH_ADDR 0xAC +#define FTS_LEN_SET_ADDR 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 (256 * 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_ft5452i; +extern struct upgrade_func upgrade_func_ft5452; + +/***************************************************************************** +* 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 diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_flash/Makefile b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_flash/Makefile new file mode 100644 index 000000000000..75f5a547c552 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_flash/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_upgrade_ft3518u.o \ No newline at end of file diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_flash/focaltech_upgrade_ft3518u.c b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_flash/focaltech_upgrade_ft3518u.c new file mode 100644 index 000000000000..e89bd9a22cf1 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_flash/focaltech_upgrade_ft3518u.c @@ -0,0 +1,180 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2020, 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) || (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_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("flash 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, +}; diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_flash/focaltech_upgrade_ft5452i.c b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_flash/focaltech_upgrade_ft5452i.c new file mode 100644 index 000000000000..31ba0ef3ee96 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_flash/focaltech_upgrade_ft5452i.c @@ -0,0 +1,630 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2020, 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" + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +u8 pb_file_ft5452i[] = { +#include "../include/pramboot/FT5452i_Pramboot_v4.1_20210427.i" +}; + +extern struct fts_upgrade *fwupgrade; + + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +/************************************************************************ +* Name: fts_ft5452_upgrade +* Brief: +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ + +static u16 fts_ft5452i_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 int fts_ft5452i_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[8] = { 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); + cmd[7] = 0xCC; + ret = fts_write(cmd, 8); + 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; + } + tmp = 0x33; + 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_ft5452i_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, ®_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_ft5452i_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[3] = { 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, 3); + if (ret < 0) { + FTS_ERROR("write 90 cmd fail"); + return ret; + } + FTS_INFO("read boot id:0x%02x%02x%02x", val[0], val[1], val[2]); + + 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 bool fts_ft5452i_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_ft5452i_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; +} + + +static int fts_ft5452i_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_ft5452i_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 int fts_ft5452i_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; + + 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; + + for (j = 0; j < packet_len; j++) { + packet_buf[cmdlen + j] = buf[offset + j]; + if (ECC_CHECK_MODE_XOR == upg->func->pram_ecc_check_mode) { + 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 ((ECC_CHECK_MODE_CRC16 == upg->func->pram_ecc_check_mode) || + (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) { + ecc_in_host = (int)fts_ft5452i_crc16_calc_host(buf, len); + } else { + ecc_in_host = (int)ecc_tmp; + } + + return ecc_in_host; +} + +static int fts_ft5452i_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_ft5452i_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_ft5452i_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_ft5452i_pram_ecc_cal_algo(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_ft5452i_pram_start(); + if (ret < 0) { + FTS_ERROR("pram start fail"); + return ret; + } + + return 0; +} + + +static int fts_ft5452i_write_pramboot_private(void) +{ + int ret = 0; + bool state = 0; + enum FW_STATUS status = FTS_RUN_IN_ERROR; + struct fts_upgrade *upg = fwupgrade; + + 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_ft5452i_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_ft5452i_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_ft5452i_fwupg_reset_to_romboot(upg); + if (ret < 0) { + FTS_ERROR("reset to romboot fail"); + return ret; + } + } + + /* check the length of the pramboot */ + ret = fts_ft5452i_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_ft5452i_fwupg_check_state(upg, FTS_RUN_IN_PRAM); + if (!state) { + FTS_ERROR("not in pramboot"); + return -EIO; + } + + ret = fts_ft5452i_pram_init(); + if (ret < 0) { + FTS_ERROR("pramboot init fail"); + return ret; + } + + return 0; +} + + +static int fts_ft5452i_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) || (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_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_ft5452i.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; + } + + 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_ft5452i = { + .ctype = {0x89}, + .fwveroff = 0x010E, + .fwcfgoff = 0x1FFB0, + .appoff = 0x0000, + .pramboot_supported = true, + .pramboot = pb_file_ft5452i, + .pb_length = sizeof(pb_file_ft5452i), + .pram_ecc_check_mode = ECC_CHECK_MODE_CRC16, + .write_pramboot_private = fts_ft5452i_write_pramboot_private, + .new_return_value_from_ic = true, + .hid_supported = false, + .upgrade = fts_ft5452i_upgrade, +}; diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_gesture.c b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_gesture.c new file mode 100644 index 000000000000..cca5c77a6ce0 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_gesture.c @@ -0,0 +1,526 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 + +#ifdef FOCALTECH_SENSOR_EN +#define REPORT_MAX_COUNT 10000 +#endif + +/***************************************************************************** +* 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; +#ifdef FOCALTECH_SENSOR_EN +#ifdef CONFIG_HAS_WAKELOCK +static struct wake_lock gesture_wakelock; +#else +static struct wakeup_source *gesture_wakelock; +#endif +#endif + +/***************************************************************************** +* 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; +#ifdef FOCALTECH_SENSOR_EN + int ret = 0; + static int report_cnt = 0; +#endif + + 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_GESTURE_U; + 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); +#ifdef FOCALTECH_SENSOR_EN + if (!fts_data->wakeable) { + FTS_INFO("Gesture got but wakeable not set. Skip this gesture."); + return; + } + /* report single tap */ + if (gesture == KEY_GESTURE_U) { + if (fts_data->imports && fts_data->imports->report_gesture) { + struct gesture_event_data event; + + FTS_INFO("invoke imported report gesture function\n"); + event.evcode = 1; + /* call class method */ + ret = fts_data->imports->report_gesture(&event); + ++report_cnt; + } + } + + FTS_INFO("input report: %d", report_cnt); + if (report_cnt >= REPORT_MAX_COUNT) + report_cnt = 0; + + if (!ret) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&gesture_wakelock, msecs_to_jiffies(5000)); +#else + PM_WAKEUP_EVENT(gesture_wakelock, 5000); +#endif + } +#else + input_report_key(input_dev, gesture, 1); + input_sync(input_dev); + input_report_key(input_dev, gesture, 0); + input_sync(input_dev); +#endif + } +} + +/***************************************************************************** +* 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; +#ifdef FOCALTECH_SENSOR_EN + static bool initialized_sensor; +#endif + + 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); + +#ifdef FOCALTECH_SENSOR_EN + if (!initialized_sensor) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&gesture_wakelock, WAKE_LOCK_SUSPEND, "poll-wake-lock"); +#else + PM_WAKEUP_REGISTER(ts_data->dev, gesture_wakelock, "poll-wake-lock"); + if (!gesture_wakelock) { + FTS_ERROR("failed to allocate wakeup source\n"); + return -ENOMEM; + } +#endif + + initialized_sensor = true; + + } +#endif + + 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; +} diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_i2c.c b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_i2c.c new file mode 100644 index 000000000000..e75d5c7b52a7 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_i2c.c @@ -0,0 +1,193 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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" + +/***************************************************************************** +* 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 +*****************************************************************************/ + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* functions body +*****************************************************************************/ +int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen) +{ + int ret = 0; + int i = 0; + struct fts_ts_data *ts_data = fts_data; + 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) { + FTS_ERROR("i2c_transfer(read) fail,ret:%d", ret); + } else { + memcpy(data, ts_data->bus_rx_buf, datalen); + break; + } + } + + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +int fts_write(u8 *writebuf, u32 writelen) +{ + int ret = 0; + int i = 0; + struct fts_ts_data *ts_data = fts_data; + 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) { + FTS_ERROR("i2c_transfer(write) fail,ret:%d", ret); + } else { + break; + } + } + mutex_unlock(&ts_data->bus_lock); + 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) +{ + FTS_FUNC_ENTER(); + ts_data->bus_tx_buf = kzalloc(I2C_BUF_LENGTH, GFP_KERNEL); + if (NULL == ts_data->bus_tx_buf) { + FTS_ERROR("failed to allocate memory for bus_tx_buf"); + return -ENOMEM; + } + + ts_data->bus_rx_buf = kzalloc(I2C_BUF_LENGTH, GFP_KERNEL); + if (NULL == ts_data->bus_rx_buf) { + FTS_ERROR("failed to allocate memory for bus_rx_buf"); + return -ENOMEM; + } + FTS_FUNC_EXIT(); + return 0; +} + +int fts_bus_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; +} \ No newline at end of file diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_point_report_check.c b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_point_report_check.c new file mode 100644 index 000000000000..0b6847f8b395 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_point_report_check.c @@ -0,0 +1,135 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 */ + diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_ts_mmi.c b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_ts_mmi.c new file mode 100644 index 000000000000..d81f832323dd --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/focaltech_ts_mmi.c @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include "focaltech_core.h" + +#define GET_TS_DATA(dev) { \ + ts_data = dev_get_drvdata(dev); \ + if (!ts_data) { \ + FTS_ERROR("Failed to get driver data"); \ + return -ENODEV; \ + } \ +} +static int fts_mmi_methods_get_vendor(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", "focaltech"); +} + +static int fts_mmi_methods_get_productinfo(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_INFO_LEN, "%s", FTS_CHIP_NAME); +} + +static int fts_mmi_methods_get_build_id(struct device *dev, void *cdata) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + ssize_t num_read_chars = 0; + u8 fwver = 0; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + 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 + num_read_chars = scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%02x", fwver); + + mutex_unlock(&input_dev->mutex); + return num_read_chars; +} + +/*return firmware version*/ +static int fts_mmi_methods_get_config_id(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%04x", 0); +} + +static int fts_mmi_methods_get_bus_type(struct device *dev, void *idata) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + TO_INT(idata) = input_dev->id.bustype == BUS_SPI ? + TOUCHSCREEN_MMI_BUS_TYPE_SPI : TOUCHSCREEN_MMI_BUS_TYPE_I2C; + return 0; +} + +static int fts_mmi_methods_get_irq_status(struct device *dev, void *idata) +{ + struct fts_ts_platform_data *pdata; + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + pdata = ts_data->pdata; + + TO_INT(idata) = gpio_get_value(pdata->irq_gpio); + return 0; +} + +static int fts_mmi_methods_get_drv_irq(struct device *dev, void *idata) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + TO_INT(idata) = ts_data->irq_disabled ? 0 : 1; + return 0; +} + +static int fts_mmi_methods_get_poweron(struct device *dev, void *idata) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + TO_INT(idata) = ts_data->power_disabled == false ? 1 : 0; + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int fts_mmi_methods_get_flashprog(struct device *dev, void *idata) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + TO_INT(idata) = (ts_data->fw_loading) ? 1 : 0; + + return 0; +} + +static int fts_mmi_methods_drv_irq(struct device *dev, int state) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + int ret = 0; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + + if (state == 1) { + FTS_INFO("enable irq"); + fts_irq_enable(); + } else if (state == 0) { + FTS_INFO("disable irq"); + fts_irq_disable(); + } else { + dev_err(dev, "%s: invalid value\n", __func__); + ret = -EINVAL; + } + + mutex_unlock(&input_dev->mutex); + return ret; +} + +#if FTS_POWER_SOURCE_CUST_EN +static int fts_mmi_methods_power(struct device *dev, int state) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + + return fts_power_source_ctrl(ts_data, state); +} +#endif + +static int fts_mmi_methods_reset(struct device *dev, int type) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_reset_proc(0); + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int fts_mmi_methods_pinctrl(struct device *dev, int on) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + + if (on == TS_MMI_PINCTL_ON) { + mutex_lock(&input_dev->mutex); + fts_reset_proc(150); + mutex_unlock(&input_dev->mutex); + } + + return 0; +} + +static int fts_mmi_firmware_update(struct device *dev, char *fwname) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + + ts_data->force_reflash = true; + + mutex_lock(&input_dev->mutex); + fts_fw_update_vendor_name(fwname); + fts_fwupg_bin(); + mutex_unlock(&input_dev->mutex); + + return 0; +} + +#if FTS_USB_DETECT_EN +static int fts_mmi_charger_mode(struct device *dev, int mode) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + ts_data->usb_detect_flag = !!mode; + + return 0; +} +#endif + +static int fts_mmi_panel_state(struct device *dev, + enum ts_mmi_pm_mode from, enum ts_mmi_pm_mode to) +{ + struct fts_ts_platform_data *pdata; + struct fts_ts_data *ts_data; + int ret = 0; + + GET_TS_DATA(dev); + pdata = ts_data->pdata; + + switch (to) { + case TS_MMI_PM_DEEPSLEEP: + ret = fts_write_reg(FTS_REG_POWER_MODE, FTS_REG_POWER_MODE_SLEEP); + ts_data->gesture_mode = false; + if (ret < 0) + FTS_ERROR("set TP to sleep mode fail, ret=%d", ret); + break; + + case TS_MMI_PM_GESTURE: +#if FTS_GESTURE_EN + if (fts_gesture_suspend(ts_data) == 0) { + /* Enter into gesture mode(suspend) */ +#ifdef FOCALTECH_SENSOR_EN + ts_data->wakeable = true; + ts_data->gesture_mode = true; +#endif + } +#endif + break; + + case TS_MMI_PM_ACTIVE: + break; + default: + dev_warn(dev, "panel mode %d is invalid.\n", to); + ret = -EINVAL; + break; + } + + return ret; + +} + +static int fts_mmi_pre_resume(struct device *dev) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + + FTS_FUNC_ENTER(); + + fts_release_all_finger(); + + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_mmi_post_resume(struct device *dev) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + +/* if (!ts_data->pdata->always_on_vio) { + FTS_INFO("Reset IC in resume"); + fts_reset_proc(200); + }*/ + + fts_tp_state_recovery(ts_data); + +#if FTS_ESDCHECK_EN + fts_esdcheck_resume(); +#endif + +#if FTS_GESTURE_EN +#ifdef FOCALTECH_SENSOR_EN + if (ts_data->wakeable) { +#endif + if (fts_gesture_resume(ts_data) == 0) { +#ifdef FOCALTECH_SENSOR_EN + ts_data->wakeable = false; + FTS_INFO("Exit from gesture suspend mode."); +#endif + goto exit; + } +#ifdef FOCALTECH_SENSOR_EN + } +#endif +#endif + +#if FTS_GESTURE_EN +exit: +#endif + FTS_FUNC_EXIT(); + ts_data->suspended = false; + + return 0; +} + +static int fts_mmi_pre_suspend(struct device *dev) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + + FTS_FUNC_ENTER(); + +#if FTS_ESDCHECK_EN + FTS_INFO("fts_esdcheck_suspend"); + fts_esdcheck_suspend(); +#endif + + FTS_FUNC_EXIT(); + + return 0; +} + +static int fts_mmi_post_suspend(struct device *dev) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + + FTS_FUNC_ENTER(); + +#ifdef FOCALTECH_SENSOR_EN + if (!ts_data->wakeable) { +#endif + fts_release_all_finger(); +#ifdef FOCALTECH_SENSOR_EN + } +#endif + + ts_data->suspended = true; + FTS_FUNC_EXIT(); + + return 0; +} + +#ifdef FOCALTECH_PALM_SENSOR_EN +int fts_mmi_palm_set_enable(struct device *dev, unsigned int enable) +{ + fts_palm_sensor_set_enable(enable); + return 0; +} +#endif + +static struct ts_mmi_methods fts_mmi_methods = { + .get_vendor = fts_mmi_methods_get_vendor, + .get_productinfo = fts_mmi_methods_get_productinfo, + .get_build_id = fts_mmi_methods_get_build_id, + .get_config_id = fts_mmi_methods_get_config_id, + .get_bus_type = fts_mmi_methods_get_bus_type, + .get_irq_status = fts_mmi_methods_get_irq_status, + .get_drv_irq = fts_mmi_methods_get_drv_irq, + .get_flashprog = fts_mmi_methods_get_flashprog, + .get_poweron = fts_mmi_methods_get_poweron, + /* SET methods */ + .reset = fts_mmi_methods_reset, + .pinctrl = fts_mmi_methods_pinctrl, + .drv_irq = fts_mmi_methods_drv_irq, +#if FTS_POWER_SOURCE_CUST_EN + .power = fts_mmi_methods_power, +#endif +#if FTS_USB_DETECT_EN + .charger_mode = fts_mmi_charger_mode, +#endif +#ifdef FOCALTECH_PALM_SENSOR_EN + .palm_set_enable = fts_mmi_palm_set_enable, +#endif + /* Firmware */ + .firmware_update = fts_mmi_firmware_update, + /* PM callback */ + .panel_state = fts_mmi_panel_state, + .pre_resume = fts_mmi_pre_resume, + .post_resume = fts_mmi_post_resume, + .pre_suspend = fts_mmi_pre_suspend, + .post_suspend = fts_mmi_post_suspend, + +}; + +int fts_mmi_dev_register(struct fts_ts_data *ts_data) { + int ret; + + ret = ts_mmi_dev_register(ts_data->dev, &fts_mmi_methods); + if (ret) { + dev_err(ts_data->dev, "Failed to register ts mmi\n"); + return ret; + } + + /* initialize class imported methods */ + ts_data->imports = &fts_mmi_methods.exports; + + return 0; +} + +void fts_mmi_dev_unregister(struct fts_ts_data *ts_data) { + ts_mmi_dev_unregister(ts_data->dev); +} + diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/include/firmware/fw_sample.i b/drivers/input/touchscreen/focaltech_v2_mmi/include/firmware/fw_sample.i new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/input/touchscreen/focaltech_v2_mmi/include/pramboot/FT5452i_Pramboot_v4.1_20210427.i b/drivers/input/touchscreen/focaltech_v2_mmi/include/pramboot/FT5452i_Pramboot_v4.1_20210427.i new file mode 100644 index 000000000000..a619803e882a --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v2_mmi/include/pramboot/FT5452i_Pramboot_v4.1_20210427.i @@ -0,0 +1,452 @@ +0x2, 0x0, 0x46,0x32,0x0, 0x0, 0xb2,0x0, 0xff,0xff,0xff,0x2, 0x7, 0x45,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0x2, 0xd, 0x9a,0xff,0xff,0xff,0xff,0xff,0x2, 0xb, 0x91,0xff,0xff, +0xff,0xff,0xff,0x2, 0x0, 0x3, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0x2, 0xb, 0xdb,0x75,0x84,0x1, 0x7e,0x44,0x1f,0xff,0xe4,0x7a,0x49, +0xb0,0x1b,0x44,0x78,0xf9,0x7e,0xf8,0x13,0xff,0xd2,0x7, 0xc2,0x8, 0xc2,0x9, 0x75, +0x22,0x0, 0x75,0x27,0xff,0xd2,0x0, 0xc2,0x2, 0xc2,0x3, 0xc2,0x4, 0xc2,0x6, 0x7e, +0x4, 0x0, 0xff,0x7e,0x14,0x1, 0x45,0xb, 0xa, 0x40,0x5d,0x44,0x68,0x1a,0x69,0x20, +0x0, 0x2, 0xb, 0xe, 0xb, 0x44,0x80,0xa, 0x7e,0xb, 0xb0,0x7a,0x29,0xb0,0xb, 0x24, +0xb, 0xc, 0x1b,0x44,0x78,0xf2,0x80,0xdf,0x2, 0x19,0x93,0x6d,0x0, 0x74,0x10,0x4d, +0x0, 0x78,0xb, 0x4d,0x22,0x78,0x27,0x8d,0x31,0x7d,0x12,0x6d,0x22,0x22,0x7d,0x43, +0x7d,0x32,0x6d,0x22,0x2f,0x11,0x2d,0x44,0x50,0x2, 0xa5,0xf, 0xbf,0x10,0x40,0x4, +0x9f,0x10,0xb, 0x90,0x14,0x78,0xed,0x7f,0x1, 0x6d,0x22,0x7d,0x34,0x22,0x7d,0x41, +0x7d,0x13,0x8d,0x24,0x7d,0x2, 0x2f,0x0, 0x40,0x4, 0xbd,0x4, 0x40,0x4, 0x9d,0x4, +0xb, 0x14,0x14,0x78,0xf1,0x7d,0x23,0x7d,0x31,0x7d,0x10,0x6d,0x0, 0x22,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0x1c,0x40,0xe3,0xbf,0x93,0x6c,0xdd,0x22,0xff,0x0, 0xff,0x0, 0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xca,0xf8,0x7f,0x20,0x80,0xa, 0x7e,0x1b,0xf0,0x7a,0x2b,0xf0,0xb, 0x34,0xb, 0x54, +0x69,0xff,0xff,0xfc,0x7d,0xef,0x1b,0xe4,0x79,0xef,0xff,0xfc,0x4d,0xff,0x78,0xe6, +0x7f,0x10,0xda,0xf8,0x22,0x0, 0x1, 0x2, 0x79,0x0, 0x0, 0xa, 0x2, 0x7a,0x54,0x5b, +0xaa,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0, 0x1, 0x2, 0x84,0x0, 0x0, 0x1, 0x2, +0x85,0xff,0x0, 0x2, 0x2, 0x86,0x0, 0x0, 0x0, 0x1, 0x3, 0x8c,0x0, 0x0, 0x1, 0x2, +0x44,0x0, 0x0, 0x1, 0x2, 0x46,0x0, 0x0, 0x2, 0x2, 0x61,0x0, 0x0, 0x0, 0x4, 0x2, +0x63,0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x67,0x0, 0x0, 0x0, 0x1, 0x2, 0x69,0x0, +0x0, 0x1, 0x2, 0x6a,0xb, 0x0, 0x1, 0x2, 0x6b,0x0, 0x0, 0x0, 0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0x56,0x31,0x2e,0x32,0x5f,0x41,0x70,0x72,0x20,0x32,0x37,0x20,0x32,0x30,0x32,0x31, +0x46,0x54,0x53,0x35,0x34,0x35,0x32,0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +0x7c,0x97,0x7c,0x8b,0x7f,0x70,0xd2,0xc9,0x7a,0x81,0xf2,0x75,0xf3,0x80,0xa, 0x59, +0x1b,0x54,0xf5,0xf4,0x75,0xf5,0x1, 0x6c,0xaa,0x80,0x14,0xe5,0xf5,0x54,0x80,0xb4, +0x80,0xf9,0xe5,0xf6,0xa, 0x3a,0x2d,0x3f,0x7d,0x2e,0x7a,0x1b,0xb0,0xb, 0xa0,0xbc, +0x9a,0x38,0xe8,0xc2,0xc9,0x22,0xd2,0xcf,0xe5,0xcc,0x54,0xf8,0xf5,0xcc,0xa9,0xd0, +0xce,0xa9,0xd2,0xc9,0xa9,0xd0,0xce,0xa9,0xd3,0xc9,0xa9,0xd5,0xca,0x75,0xeb,0x23, +0x75,0xed,0xf, 0x75,0xac,0x40,0x75,0xad,0x30,0xd2,0x86,0x75,0xec,0xff,0x75,0xee, +0xff,0xa9,0xc1,0xea,0xa9,0xc7,0x94,0xa9,0xc4,0x94,0xc2,0xae,0x7e,0x8, 0x0, 0xc, +0x74,0x21,0x7e,0x70,0x1, 0x91,0x20,0xe5,0xc, 0xbe,0xb0,0x9, 0x28,0x5, 0xa9,0xc3, +0xe2,0x80,0x3, 0xa9,0xd3,0xe2,0xe5,0xc, 0x7a,0xb3,0x2, 0x7d,0x22,0x7e,0x35,0x25, +0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, 0x7a,0x1f,0x12,0xb0,0x7e,0x35,0x25,0x7d, +0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, 0x7a,0x1f,0x12,0xac,0x7e,0x35,0x25,0x2e,0x34, +0x0, 0xa, 0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0xa, 0x7a,0x1f,0x10,0xc, 0x7e,0x35, +0x25,0x2e,0x34,0x0, 0xa, 0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, 0x7a,0x1f,0x12, +0xfc,0x22,0x7e,0x34,0x0, 0x4, 0x7e,0x54,0x0, 0x4, 0x7e,0x44,0x0, 0xff,0x69,0x12, +0x0, 0x2, 0xb, 0x2a,0x0, 0x79,0x30,0x0, 0x1e,0x7e,0x34,0x1, 0x0, 0x7e,0x24,0x10, +0x0, 0x9f,0x0, 0x59,0x12,0x0, 0x2, 0x1b,0x28,0x0, 0xb, 0x26,0x1b,0x34,0x78,0xf3, +0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, 0x7a,0x1f,0x10,0x1c, +0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, 0x7a,0x1f,0x10,0x20, +0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, 0x7a,0x1f,0x11,0x54, +0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, 0x7a,0x1f,0x12,0xa8, +0x7e,0x35,0x25,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, 0x7a,0x1f,0x12,0xb0,0x7e, +0x35,0x25,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, 0x7a,0x1f,0x12,0xac,0x7e,0x35, +0x25,0x2e,0x34,0x0, 0xa, 0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0xa, 0x7a,0x1f,0x10, +0xc, 0x7e,0x35,0x25,0x2e,0x34,0x0, 0xa, 0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, +0x7a,0x1f,0x12,0xfc,0x7e,0x34,0x2, 0x63,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, +0x7a,0x1f,0x12,0xc0,0x7e,0x34,0x2, 0x7a,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0xa, +0x7a,0x1f,0x12,0x40,0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, +0x7a,0x1f,0x11,0x84,0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, +0x7a,0x1f,0x11,0x7c,0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, +0x7a,0x1f,0x11,0x88,0x7e,0x34,0x2, 0x67,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0xa, +0x7a,0x1f,0x11,0xa8,0x7e,0x34,0x2, 0x79,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0xa, +0x7a,0x1f,0x13,0x30,0x7e,0x34,0x2, 0x79,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0xa, +0x7a,0x1f,0x11,0x98,0x7e,0x34,0x2, 0x79,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, +0x7a,0x1f,0x11,0x90,0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, +0x7a,0x1f,0x11,0x94,0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, +0x7a,0x1f,0x10,0x40,0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, +0x7a,0x1f,0x10,0x24,0x22,0xa9,0xc4,0xe1,0xa9,0xc0,0xe1,0xa9,0xd0,0xce,0xd2,0xcc, +0x7e,0x34,0xff,0xff,0x7e,0x54,0x0, 0x4, 0x7e,0x44,0x0, 0xff,0x69,0x12,0x0, 0x2, +0xb, 0x2a,0x0, 0x79,0x30,0x0, 0x6, 0x7e,0x34,0x0, 0x1, 0x1b,0xa, 0x30,0xa9,0xc1, +0xe2,0xa9,0xc2,0xe2,0xd2,0x9e,0xc2,0x9d,0x43,0xeb,0xb4,0x7e,0x34,0x0, 0xef,0x7e, +0x54,0x0, 0x4, 0x7e,0x44,0x0, 0xff,0x69,0x12,0x0, 0x2, 0xb, 0x2a,0x0, 0x79,0x30, +0x0, 0x18,0x2e,0x14,0x0, 0xa, 0xb, 0xa, 0x30,0x5e,0x70,0xfc,0x1b,0xa, 0x30,0x7e, +0x34,0x0, 0x3, 0x7e,0x54,0x0, 0x4, 0x7e,0x44,0x0, 0xff,0x69,0x12,0x0, 0x2, 0xb, +0x2a,0x0, 0x79,0x30,0x0, 0x8, 0x7e,0x34,0x0, 0x8, 0x79,0x30,0x0, 0x4, 0x91,0xf2, +0xd2,0xac,0x22,0xc2,0xad,0x75,0x91,0x0, 0xc2,0x90,0xc2,0x91,0xa9,0xc0,0x93,0x75, +0x92,0x70,0xc2,0xc0,0xa9,0xd5,0xb7,0xd2,0xbd,0xd2,0xe8,0xd2,0xad,0x22,0x80,0x14, +0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, 0x7d,0x23,0x1b,0x34,0x4d,0x22,0x78,0xe4,0x22,0xc2,0xa9,0xc2, +0x8c,0x43,0x89,0x2, 0x75,0x8c,0x1, 0x75,0x8a,0x0, 0xd2,0xa9,0x22,0xc2,0x8c,0x6d, +0x33,0x7a,0x37,0x2, 0x86,0xd2,0x8c,0x22,0x7e,0x37,0x2, 0x86,0xb, 0x34,0x7a,0x37, +0x2, 0x86,0x22,0xe1,0x38,0xca,0x39,0xf1,0x38,0xda,0x39,0x32,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0x7c,0xab,0x7e,0x70,0xef,0x7e,0xb3,0x2, 0x44,0x24,0xfd,0x68,0x5c,0x1b,0xb1,0x68, +0x48,0x24,0x9f,0x68,0x67,0x1b,0xb2,0x68,0x69,0x24,0xda,0x68,0x1f,0x24,0xc4,0x68, +0x53,0x1b,0xb2,0x68,0x51,0x24,0xf5,0x78,0x6e,0x7e,0xa3,0x2, 0x6e,0x7c,0xba,0x4, +0x7a,0xb3,0x2, 0x6e,0xa, 0x3a,0x9, 0x73,0x2, 0x51,0x80,0x5b,0x4c,0xaa,0x78,0x5, +0x7e,0x70,0x54,0x80,0x52,0xbe,0xa0,0x1, 0x78,0x5, 0x7e,0x70,0x5b,0x80,0x48,0xbe, +0xa0,0x2, 0x78,0x43,0x7e,0x70,0xaa,0x80,0x3e,0x4c,0xaa,0x78,0x2, 0x80,0x5, 0xbe, +0xa0,0x1, 0x78,0x33,0x7e,0x70,0x1, 0x80,0x2e,0xa, 0x3a,0x2e,0x35,0x25,0x9, 0x73, +0x0, 0xa, 0x80,0x23,0x80,0x6, 0x7e,0x73,0x3, 0x8c,0x80,0x1b,0x7e,0x73,0x2, 0x79, +0x80,0x15,0x4c,0xaa,0x78,0x8, 0x7e,0x37,0x2, 0x67,0xa, 0x36,0x80,0x9, 0xbe,0xa0, +0x1, 0x78,0x4, 0x7e,0x37,0x2, 0x67,0x7c,0xb7,0x22,0x7c,0x97,0x7c,0x8b,0xc2,0x2, +0x7e,0xb3,0x2, 0x44,0x14,0x78,0x2, 0x41,0xa2,0x1b,0xb1,0x78,0x2, 0x41,0x22,0x1b, +0xb1,0x78,0x2, 0x41,0xa2,0x1b,0xb1,0x78,0x2, 0x41,0x65,0x14,0x78,0x2, 0x41,0x81, +0x14,0x78,0x2, 0x41,0x87,0x24,0xf9,0x78,0x2, 0x41,0x9d,0x24,0xbb,0x68,0x55,0x24, +0xf6,0x78,0x2, 0x61,0x7e,0x1b,0xb1,0x78,0x2, 0x61,0x6c,0x14,0x78,0x2, 0x61,0x76, +0x1b,0xb1,0x78,0x2, 0x41,0xa5,0x14,0x78,0x2, 0x41,0xa7,0x14,0x78,0x2, 0x41,0xa2, +0x1b,0xb2,0x78,0x2, 0x41,0xa2,0x24,0xda,0x68,0x2e,0x24,0xe6,0x68,0x28,0x24,0xfa, +0x68,0x3f,0x24,0xf1,0x68,0x74,0x24,0xf3,0x78,0x2, 0x41,0xa2,0x24,0xec,0x78,0x2, +0x61,0x8c,0x24,0x5, 0x68,0x2, 0x61,0x90,0xbe,0x80,0x1, 0x68,0x2, 0x61,0x90,0x7a, +0x93,0x2, 0x6e,0x22,0x61,0x86,0x61,0x86,0xbe,0x80,0x4, 0x50,0xb, 0xe4,0x7a,0xb3, +0x2, 0x79,0xa, 0x58,0x19,0x95,0x2, 0x47,0x4c,0x88,0x68,0x2, 0x61,0x90,0xd2,0x2, +0x22,0x7c,0xb8,0x14,0x68,0xa, 0x14,0x68,0xf, 0x14,0x68,0x1e,0x24,0x3, 0x78,0x2a, +0xa, 0x39,0x7d,0x23,0x6d,0x33,0x80,0xc, 0x7c,0x29,0x6c,0x33,0x6d,0x0, 0x7e,0x1f, +0x2, 0x63,0x2f,0x10,0x7a,0x1f,0x2, 0x63,0x80,0x10,0xa, 0x19,0x6d,0x0, 0x7e,0x1f, +0x2, 0x63,0x2f,0x10,0x7a,0x1f,0x2, 0x63,0xd2,0x3, 0x7c,0xb8,0x14,0x68,0x19,0x14, +0x68,0x27,0x14,0x68,0x26,0x14,0x68,0x25,0x14,0x68,0x2a,0x24,0x5, 0x78,0x48,0x20, +0x1, 0x2, 0x41,0xcc,0x41,0xc6,0x41,0xcc,0xa, 0x39,0x7d,0x3, 0x6d,0x11,0x7e,0x35, +0x25,0x59,0x13,0x0, 0x2, 0x1b,0x38,0x0, 0x22,0x41,0xef,0x61,0x4, 0x7c,0x69,0x6c, +0x77,0x6d,0x22,0x61,0x1c,0xa, 0x59,0x6d,0x44,0x7e,0x25,0x25,0x7d,0x32,0xb, 0x36, +0x49,0x13,0x0, 0x2, 0xb, 0x38,0x0, 0x2f,0x2, 0x59,0x13,0x0, 0x2, 0x1b,0x38,0x0, +0x6d,0x33,0x59,0x32,0x0, 0x8, 0x22,0x7e,0xb3,0x2, 0x79,0x6c,0xb9,0x7a,0xb3,0x2, +0x79,0x7e,0x25,0x25,0x2e,0x24,0x0, 0x8, 0xb, 0x28,0x50,0x7d,0x35,0xb, 0x34,0x1b, +0x28,0x30,0x2e,0x55,0x25,0x19,0x95,0x0, 0xa, 0x7e,0x55,0x25,0x49,0x15,0x0, 0x8, +0x6d,0x0, 0x49,0x35,0x0, 0x6, 0x49,0x25,0x0, 0x4, 0xbf,0x1, 0x50,0x2, 0x61,0x90, +0x20,0x1, 0x3, 0xd3,0x80,0x1, 0xc3,0x92,0x1, 0x7a,0x55,0x23,0x7e,0x34,0x0, 0x2, +0x61,0x67,0x7c,0xb8,0x14,0x68,0x15,0x14,0x68,0x14,0x14,0x68,0x13,0x24,0x3, 0x68, +0x2, 0x61,0x90,0x20,0x1, 0x2, 0x41,0xcc,0x41,0xc6,0x41,0xcc,0x41,0xd4,0x41,0xef, +0xa, 0x59,0x6d,0x44,0x7e,0x15,0x25,0x49,0x31,0x0, 0x2, 0xb, 0x18,0x20,0x2f,0x12, +0x59,0x31,0x0, 0x2, 0x1b,0x18,0x20,0x7e,0x35,0x25,0x7a,0x35,0x23,0xd2,0x2, 0x7e, +0x34,0x0, 0x1, 0x61,0x67,0x75,0xe6,0x0, 0x75,0xe7,0x0, 0xe4,0x7e,0x34,0x1, 0x6, +0x7e,0x24,0x0, 0xff,0x7a,0x1b,0xb0,0x7e,0x34,0x1, 0x7, 0x7a,0x1b,0xb0,0xd2,0x6, +0x22,0x75,0xe6,0x0, 0xd2,0x9f,0x22,0xbe,0x80,0x1, 0x78,0x5, 0x7a,0x93,0x2, 0x6a, +0x22,0xbe,0x80,0x2, 0x68,0x2, 0x61,0x90,0x7a,0x93,0x2, 0x46,0x22,0x7a,0x93,0x2, +0x69,0x22,0xd2,0x2, 0x22,0x61,0x86,0x7c,0xb8,0x14,0x68,0x28,0x14,0x68,0x40,0x14, +0x68,0x52,0x14,0x68,0x61,0x14,0x68,0x70,0x14,0x78,0x2, 0x61,0x45,0x24,0x6, 0x68, +0x2, 0x61,0x90,0x30,0x1, 0x6, 0x7e,0x34,0x0, 0x28,0x80,0x4, 0x7e,0x34,0x1, 0x36, +0x7a,0x35,0x25,0x22,0xa, 0x39,0x6d,0x22,0x7e,0x15,0x25,0x59,0x31,0x0, 0x2, 0x1b, +0x18,0x20,0x49,0x31,0x0, 0x2, 0xb, 0x18,0x20,0x7d,0x23,0x6d,0x33,0x80,0x4e,0xa, +0x59,0x7c,0xab,0xe4,0x6d,0x44,0x7e,0x15,0x25,0x49,0x31,0x0, 0x2, 0xb, 0x18,0x20, +0x2f,0x12,0x80,0x39,0xa, 0x59,0x6d,0x44,0x7e,0x15,0x25,0x49,0x31,0x0, 0x2, 0xb, +0x18,0x20,0x2f,0x12,0x80,0x27,0xa, 0x39,0x7d,0x23,0x6d,0x33,0x7e,0x15,0x25,0x59, +0x31,0x0, 0x6, 0x59,0x21,0x0, 0x4, 0x22,0xa, 0x59,0x7c,0xab,0xe4,0x6d,0x44,0x7e, +0x15,0x25,0xb, 0x16,0x49,0x31,0x0, 0x2, 0xb, 0x18,0x20,0x2f,0x12,0x59,0x31,0x0, +0x2, 0x1b,0x18,0x20,0x22,0xa, 0x59,0x6d,0x44,0x7e,0x35,0x25,0x7d,0x23,0xb, 0x26, +0x49,0x12,0x0, 0x2, 0xb, 0x28,0x0, 0x2f,0x2, 0x59,0x12,0x0, 0x2, 0x1b,0x28,0x0, +0x7a,0x35,0x23,0x7e,0x34,0x0, 0x3, 0x7a,0x37,0x2, 0x61,0x22,0x7e,0x34,0x0, 0x4, +0x7a,0x37,0x2, 0x61,0x80,0x10,0xe4,0x7c,0x78,0x7c,0x69,0x2, 0x18,0x0, 0x7e,0x34, +0x0, 0x5, 0x7a,0x37,0x2, 0x61,0xe4,0x7a,0xb3,0x2, 0x79,0x22,0x7a,0x93,0x2, 0x84, +0x22,0xca,0x7b,0xca,0x2b,0xca,0x1b,0xca,0xb, 0xd2,0x0, 0x30,0x90,0x22,0xc2,0x90, +0x7e,0x71,0x91,0x7e,0xb3,0x2, 0x6b,0x70,0x4, 0x7a,0x73,0x2, 0x44,0x11,0x9a,0x7e, +0xb3,0x2, 0x6b,0x4, 0x7a,0xb3,0x2, 0x6b,0x30,0x2, 0x5, 0xe4,0x11,0x0, 0xf5,0x91, +0x30,0x91,0xf, 0xc2,0x91,0x7e,0xb3,0x2, 0x6b,0x4, 0x7a,0xb3,0x2, 0x6b,0x11,0x0, +0xf5,0x91,0xda,0xb, 0xda,0x1b,0xda,0x2b,0xda,0x7b,0x32,0xca,0xb8,0xa9,0xc0,0x93, +0xe4,0x7a,0xb3,0x2, 0x6b,0xda,0xb8,0x32,0x7c,0x7b,0x2e,0x70,0xf9,0x78,0x2, 0x81, +0xe0,0x1b,0x70,0x78,0x2, 0x81,0xfc,0x1b,0x70,0x78,0x2, 0xa1,0x2, 0x2e,0x70,0xf9, +0x78,0x2, 0xa1,0x13,0x2e,0x70,0xb1,0x78,0x2, 0xa1,0x8c,0x1b,0x71,0x78,0x2, 0xa1, +0x79,0x1b,0x70,0x78,0x2, 0xa1,0x83,0x1b,0x71,0x78,0x2, 0xa1,0x1c,0x1b,0x70,0x78, +0x2, 0xa1,0x1e,0x2e,0x70,0xd5,0x68,0x1f,0x2e,0x70,0xe6,0x68,0x1a,0x1b,0x70,0x68, +0x29,0x1b,0x70,0x78,0x2, 0x81,0xb8,0x1b,0x72,0x68,0xe, 0x2e,0x70,0xf1,0x68,0x32, +0x2e,0x70,0x6a,0x68,0x2, 0xa1,0x99,0xa1,0x94,0x7e,0x1f,0x2, 0x63,0x7c,0x76,0x7c, +0x65,0xa, 0x24,0x7a,0x1f,0x2, 0x63,0xd2,0x3, 0x22,0x7e,0x15,0x25,0x49,0x31,0x0, +0x2, 0xb, 0x18,0x20,0x7c,0x76,0x7c,0x65,0xa, 0x24,0x59,0x31,0x0, 0x2, 0x1b,0x18, +0x20,0x22,0x7e,0x14,0x0, 0x4, 0x7e,0x4, 0x0, 0xff,0x69,0x30,0x0, 0x2, 0xb, 0xa, +0x20,0x69,0x11,0x0, 0x10,0x6d,0x0, 0x7e,0x35,0x25,0x59,0x13,0x0, 0x6, 0x59,0x3, +0x0, 0x4, 0x7a,0x35,0x23,0x7e,0x34,0x0, 0x2, 0x7a,0x37,0x2, 0x61,0x20,0x1, 0x3, +0xd3,0x80,0x1, 0xc3,0x92,0x1, 0x30,0x1, 0x6, 0x7e,0x34,0x0, 0x28,0x80,0x4, 0x7e, +0x34,0x1, 0x36,0x7a,0x35,0x25,0x80,0x25,0x7e,0x15,0x25,0x49,0x31,0x0, 0x2, 0xb, +0x18,0x20,0x7c,0x76,0x7c,0x65,0xa, 0x24,0x59,0x31,0x0, 0x2, 0x1b,0x18,0x20,0x7e, +0x35,0x25,0x7a,0x35,0x23,0x7e,0x34,0x0, 0x1, 0x7a,0x37,0x2, 0x61,0x2, 0x4, 0xad, +0x75,0xe6,0x0, 0x75,0xe7,0x0, 0xe4,0x7e,0x34,0x1, 0x6, 0x7e,0x24,0x0, 0xff,0x7a, +0x1b,0xb0,0x7e,0x34,0x1, 0x7, 0x7a,0x1b,0xb0,0xd2,0x6, 0x22,0x75,0xe6,0x0, 0xd2, +0x9f,0x22,0x7e,0x73,0x2, 0x88,0x7a,0x73,0x2, 0x6a,0x7e,0x73,0x2, 0x89,0x7a,0x73, +0x2, 0x46,0x22,0x7e,0x73,0x2, 0x88,0x7a,0x73,0x2, 0x69,0x22,0x80,0x76,0x7e,0x73, +0x2, 0x89,0xa, 0x37,0x6d,0x22,0x7c,0x67,0x6c,0x77,0x7e,0x33,0x2, 0x88,0xa, 0x13, +0x7d,0x1, 0x6d,0x11,0x2f,0x1, 0x7e,0x73,0x2, 0x8a,0xa, 0x37,0x2f,0x10,0x7e,0x15, +0x23,0x59,0x31,0x0, 0x2, 0x1b,0x18,0x20,0x7e,0x73,0x2, 0x8c,0xa, 0x37,0x6d,0x22, +0x7c,0x67,0x6c,0x77,0x7e,0x13,0x2, 0x8b,0xa, 0x51,0x7d,0x45,0x6d,0x55,0x2f,0x21, +0x7e,0x73,0x2, 0x8d,0xa, 0x37,0x2f,0x12,0x59,0x31,0x0, 0x6, 0x59,0x21,0x0, 0x4, +0x7e,0x34,0x0, 0x3, 0x7a,0x37,0x2, 0x61,0x22,0x7e,0x34,0x0, 0x4, 0x7a,0x37,0x2, +0x61,0x80,0x11,0x74,0x1, 0x6c,0x77,0x6c,0x66,0x2, 0x18,0x0, 0x7e,0x34,0x0, 0x5, +0x7a,0x37,0x2, 0x61,0xe4,0x7a,0xb3,0x2, 0x79,0x22,0xca,0x7b,0xca,0x2b,0xca,0x1b, +0xca,0xb, 0xc2,0x0, 0x7e,0x14,0x0, 0x4, 0x7e,0x4, 0x0, 0xff,0x69,0x50,0x0, 0x2, +0xb, 0xa, 0x40,0xb, 0x2a,0x30,0x4e,0x70,0x80,0x1b,0x2a,0x30,0x69,0x30,0x0, 0x2, +0xb, 0xa, 0x20,0x69,0x11,0x0, 0x2, 0x5e,0x14,0xff,0x0, 0x68,0x3f,0xb, 0x1a,0x10, +0x4e,0x30,0x8, 0x1b,0x1a,0x10,0x7e,0x14,0x0, 0x4, 0x7e,0x4, 0x0, 0xff,0x69,0x50, +0x0, 0x2, 0xb, 0xa, 0x40,0x69,0x12,0x0, 0x2, 0xa, 0x2, 0x1e,0x4, 0x1e,0x4, 0x5e, +0x4, 0x0, 0x1, 0x78,0xc, 0xa, 0x12,0x1e,0x14,0x5e,0x14,0x0, 0x1, 0x78,0x2, 0xc1, +0x96,0xb, 0x2a,0x10,0x4e,0x30,0x4, 0x1b,0x2a,0x10,0xc1,0x96,0x69,0x11,0x0, 0x2, +0x1e,0x14,0x1e,0x14,0x1e,0x14,0x5e,0x14,0x0, 0x1, 0x68,0x6, 0x69,0x51,0x0, 0xe, +0x71,0xe8,0x7e,0x34,0x0, 0x4, 0x7e,0x24,0x0, 0xff,0x69,0x11,0x0, 0x2, 0xb, 0x1a, +0x0, 0x69,0x30,0x0, 0x2, 0x1e,0x34,0x1e,0x34,0x5e,0x34,0x0, 0x1, 0x68,0x9, 0xb, +0xa, 0x30,0x5e,0x70,0xdf,0x1b,0xa, 0x30,0x7e,0x34,0x0, 0x4, 0x7e,0x24,0x0, 0xff, +0x69,0x11,0x0, 0x2, 0xb, 0x1a,0x0, 0x69,0x30,0x0, 0x2, 0x1e,0x34,0x5e,0x34,0x0, +0x1, 0x68,0xd, 0x2e,0x14,0x0, 0x6, 0xb, 0xa, 0x30,0x4e,0x70,0x2, 0x1b,0xa, 0x30, +0x7e,0x34,0x0, 0x4, 0x7e,0x24,0x0, 0xff,0x69,0x11,0x0, 0x2, 0xb, 0x1a,0x0, 0x69, +0x30,0x0, 0x2, 0x5e,0x34,0x0, 0x1, 0x68,0xd, 0x2e,0x14,0x0, 0x6, 0xb, 0xa, 0x30, +0x4e,0x70,0x1, 0x1b,0xa, 0x30,0x7e,0x34,0xff,0xff,0x7e,0x54,0x0, 0x4, 0x7e,0x44, +0x0, 0xff,0x69,0x12,0x0, 0x2, 0xb, 0x2a,0x0, 0x79,0x30,0x0, 0x6, 0xb, 0xa, 0x30, +0x5e,0x34,0x0, 0x3, 0x1b,0xa, 0x30,0xda,0xb, 0xda,0x1b,0xda,0x2b,0xda,0x7b,0x32, +0x7f,0x50,0x7f,0x41,0x6c,0xaa,0xbe,0x58,0x0, 0x0, 0x38,0x3a,0x7e,0x58,0xff,0xff, +0x7e,0xa0,0x1, 0x80,0x31,0x7e,0x18,0x0, 0x80,0x7a,0x1d,0x10,0x7f,0x14,0x7e,0x8, +0x3, 0x8d,0x12,0x14,0x93,0x9e,0x58,0x0, 0x80,0x2e,0x48,0x0, 0x80,0x6d,0x44,0x9, +0x34,0x3, 0x8d,0x7e,0x1d,0xc, 0x7e,0x1b,0xb0,0x6c,0xb3,0x7a,0x1b,0xb0,0xb, 0x44, +0xbe,0x44,0x0, 0x80,0x78,0xe9,0xbe,0x58,0x0, 0x80,0x38,0xc9,0x4c,0xaa,0x68,0x2, +0xb, 0x5c,0x7a,0x5d,0x10,0x7f,0x14,0x7e,0x8, 0x3, 0x8d,0x12,0x14,0x93,0x6d,0x44, +0x80,0x11,0x9, 0x64,0x3, 0x8d,0x7e,0xd, 0xc, 0x7e,0xb, 0x70,0x6c,0x76,0x7a,0xb, +0x70,0xb, 0x44,0x7d,0x34,0x6d,0x22,0xbf,0x15,0x40,0xe7,0x22,0xca,0x3b,0x7f,0x60, +0x7f,0x71,0x7e,0x3d,0xc, 0x75,0x14,0x0, 0xa2,0x3, 0xe4,0x33,0x78,0xc, 0x7a,0x3d, +0x15,0x7f,0x17,0x7f,0x6, 0x12,0x11,0x6e,0xe1,0xf6,0x7f,0x57,0x7f,0x7, 0x2f,0x3, +0x7a,0xd, 0x10,0x7e,0x1f,0x2, 0x63,0xbf,0x1, 0x28,0x2, 0xe1,0xf6,0x4d,0xef,0x78, +0x3, 0x75,0x27,0x0, 0xe5,0x27,0xbe,0xb0,0x4, 0x50,0x46,0xe5,0x27,0xa, 0x2b,0x7e, +0x34,0x0, 0x4, 0x9d,0x32,0x7a,0x71,0x14,0xa, 0x37,0x6d,0x22,0xbf,0x13,0x28,0x4, +0x7c,0xbf,0xf5,0x14,0xe5,0x14,0xa, 0x3b,0xca,0x39,0xe5,0x27,0xa, 0x1b,0x2e,0x14, +0x2, 0x6f,0x6d,0x0, 0x7f,0x16,0x12,0x1, 0x20,0x1b,0xfd,0xe5,0x14,0x25,0x27,0xf5, +0x27,0xe5,0x14,0xa, 0x3b,0x6d,0x22,0x2f,0x51,0xe5,0x14,0xa, 0x1b,0x2d,0xd1,0x9f, +0x31,0xbe,0x38,0x0, 0x0, 0x28,0xa, 0x7a,0x3d,0x15,0x7f,0x15,0x7f,0x6, 0x12,0x11, +0x6e,0x7e,0xf, 0x2, 0x63,0x7e,0x1d,0x10,0xbf,0x10,0x78,0x1a,0xe5,0x27,0xb4,0x4, +0x15,0x7e,0x18,0x0, 0x4, 0x7a,0x1d,0x15,0x9f,0x11,0x7e,0x8, 0x2, 0x6f,0x12,0x11, +0x6e,0x75,0x27,0xff,0xc2,0x3, 0xda,0x3b,0x22,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xa9,0xc2,0xb4,0x74,0x9f,0x11,0x68,0x7e,0x8, 0x0, 0xc, 0x7e,0x18,0x0, 0x1, 0x11, +0x83,0xa9,0xd2,0xb4,0xe5,0xc, 0x60,0x5, 0xe5,0xc, 0xb4,0xff,0x2, 0xc3,0x22,0xd3, +0x22,0xd2,0xc8,0x43,0xed,0xf, 0xc2,0xea,0x75,0xb3,0x13,0xa9,0xd1,0xb4,0xa9,0xc0, +0xb4,0x22,0xca,0xf8,0x7e,0xf0,0x10,0x7e,0x34,0x15,0x0, 0xc2,0xa, 0x7d,0x23,0x1b, +0x34,0x4d,0x22,0x78,0xf8,0x11,0x0, 0x92,0xa, 0x20,0xa, 0x19,0x75,0xed,0x9f,0xd2, +0x8, 0xa9,0xd1,0xea,0xa9,0xd5,0xca,0xa9,0xc1,0xea,0x80,0x1, 0x0, 0x7c,0xaf,0x1b, +0xf0,0x4c,0xaa,0x78,0xf7,0xda,0xf8,0x22,0x7c,0x7b,0x7a,0x71,0xb5,0xa9,0x36,0xb3, +0xfc,0xa9,0xc6,0xb3,0xd3,0x22,0x75,0xb5,0x0, 0xa9,0x36,0xb3,0xfc,0xa9,0xc6,0xb3, +0xe5,0xb5,0x22,0x7f,0x61,0x7f,0x70,0x9f,0x11,0x80,0x13,0x75,0xb5,0x0, 0xa9,0x36, +0xb3,0xfc,0xa9,0xc6,0xb3,0xe5,0xb5,0x7a,0x7b,0xb0,0xb, 0xf4,0xb, 0x1c,0xbf,0x16, +0x40,0xe9,0x22,0xa9,0xc2,0xb4,0x30,0xa, 0x6, 0x74,0x6, 0x11,0x68,0x80,0x4, 0x74, +0x4, 0x11,0x68,0xa9,0xd2,0xb4,0x22,0xa9,0xc2,0xb4,0x74,0x5, 0x11,0x68,0x11,0x76, +0xa9,0xd2,0xb4,0x30,0xe0,0x2, 0xd3,0x22,0xc3,0x22,0xca,0xf8,0x7f,0x1, 0x7c,0xfb, +0x7c,0xb1,0xf5,0x1f,0x7c,0xb2,0xf5,0x1e,0x7c,0xb3,0xf5,0x1d,0xa9,0xc2,0xb4,0x7c, +0xbf,0x11,0x68,0xe5,0x1f,0x11,0x68,0xe5,0x1e,0x11,0x68,0xe5,0x1d,0x11,0x68,0xda, +0xf8,0x22,0x7f,0x70,0x7f,0x61,0xe5,0x22,0xb4,0x1, 0x1d,0x7e,0x34,0xf0,0x0, 0x7e, +0x24,0x0, 0x1, 0xbf,0x61,0x50,0x11,0x7e,0xd, 0x19,0x2f,0x6, 0x7e,0x34,0xe0,0x0, +0x7e,0x24,0x0, 0x1, 0xbf,0x1, 0x38,0x55,0xd2,0xa, 0x11,0xa3,0x74,0x2, 0x7f,0x16, +0x11,0xca,0x9f,0x11,0x80,0x14,0x7d,0x13,0x2d,0x1f,0x7d,0xe, 0x7e,0xb, 0xb0,0xf5, +0xb5,0xa9,0x36,0xb3,0xfc,0xa9,0xc6,0xb3,0xb, 0x1c,0x7e,0xd, 0x19,0xbf,0x10,0x40, +0xe5,0xa9,0xd2,0xb4,0x12,0x7, 0x2d,0x11,0xb7,0x50,0xa, 0x7e,0x37,0x2, 0x86,0xbe, +0x34,0x1, 0xf4,0x28,0xf2,0x7e,0x37,0x2, 0x86,0xbe,0x34,0x1, 0xf4,0x38,0x3, 0x2, +0x7, 0x43,0xc2,0x86,0x7e,0x34,0x13,0x88,0x12,0x6, 0xfe,0xd2,0x86,0x22,0xca,0x3b, +0x7f,0x30,0x7f,0x51,0x7e,0x4d,0x15,0x7f,0x15,0x5e,0x34,0x0, 0x7f,0x7d,0x53,0x7e, +0x44,0x0, 0x80,0x9d,0x45,0x7d,0x34,0x6d,0x22,0xbf,0x14,0x40,0xc, 0x7a,0x4d,0x19, +0x7f,0x15,0x7f,0x3, 0x11,0xf2,0xd3,0x80,0x48,0x7d,0x34,0x6d,0x22,0x7a,0x1d,0x19, +0x7f,0x15,0x7f,0x3, 0x11,0xf2,0x7d,0x34,0x6d,0x22,0x9f,0x41,0x2f,0x51,0x2d,0x74, +0x80,0x19,0x7e,0x18,0x0, 0x80,0x7a,0x1d,0x19,0x7f,0x15,0x7f,0x3, 0x11,0xf2,0x9e, +0x48,0x0, 0x80,0x2e,0x74,0x0, 0x80,0x2e,0x58,0x0, 0x80,0xbe,0x48,0x0, 0x80,0x50, +0xe1,0xbe,0x48,0x0, 0x0, 0x68,0x9, 0x7a,0x4d,0x19,0x7f,0x15,0x7f,0x3, 0x11,0xf2, +0xd3,0xda,0x3b,0x22,0x7c,0x6b,0xd2,0xa, 0x11,0xa3,0x74,0x20,0xca,0xb8,0xa, 0x36, +0x6d,0x22,0x74,0xc, 0x2f,0x11,0x14,0x78,0xfb,0xda,0xb8,0x11,0xca,0xa9,0xd2,0xb4, +0x12,0x7, 0x2d,0x11,0xb7,0x50,0xa, 0x7e,0x37,0x2, 0x86,0xbe,0x34,0x1, 0xf4,0x28, +0xf2,0x7e,0x37,0x2, 0x86,0xbe,0x34,0x1, 0xf4,0x38,0x5, 0x12,0x7, 0x43,0xd3,0x22, +0xc2,0x86,0x7e,0x34,0x13,0x88,0x12,0x6, 0xfe,0xd2,0x86,0xc3,0x22,0xd2,0xa, 0x11, +0xa3,0xa9,0xc2,0xb4,0x74,0x60,0x11,0x68,0xa9,0xd2,0xb4,0x11,0xb7,0x40,0xfc,0xd3, +0x22,0x7c,0x7b,0xa9,0xc2,0xb4,0xa9,0xc6,0xb3,0x75,0xb5,0x5, 0xa9,0x36,0xb3,0xfc, +0xa9,0xc6,0xb3,0x75,0xb5,0x0, 0xa9,0x36,0xb3,0xfc,0xa9,0xc6,0xb3,0x75,0xb5,0x0, +0xa9,0x36,0xb3,0xfc,0xa9,0xc6,0xb3,0x7a,0x71,0xb5,0xa9,0x36,0xb3,0xfc,0xa9,0xc6, +0xb3,0x75,0xb5,0x0, 0xa9,0x36,0xb3,0xfc,0xa9,0xc6,0xb3,0x75,0xb5,0x0, 0xa9,0x36, +0xb3,0xfc,0xa9,0xc6,0xb3,0x7e,0x61,0xb5,0x75,0xb5,0x0, 0xa9,0x36,0xb3,0xfc,0xa9, +0xc6,0xb3,0x7e,0x71,0xb5,0xa9,0xd2,0xb4,0x7c,0x46,0x7c,0x64,0x22,0x7d,0x43,0x7c, +0x5b,0xa, 0x58,0x7d,0x34,0x7c,0x47,0xa9,0xc2,0xb4,0xa9,0xc6,0xb3,0x75,0xb5,0x1, +0xa9,0x36,0xb3,0xfc,0xa9,0xc6,0xb3,0x75,0xb5,0x0, 0xa9,0x36,0xb3,0xfc,0xa9,0xc6, +0xb3,0x75,0xb5,0x0, 0xa9,0x36,0xb3,0xfc,0xa9,0xc6,0xb3,0x7a,0x51,0xb5,0xa9,0x36, +0xb3,0xfc,0xa9,0xc6,0xb3,0xf5,0xb5,0xa9,0x36,0xb3,0xfc,0xa9,0xc6,0xb3,0x7a,0x41, +0xb5,0xa9,0x36,0xb3,0xfc,0xa9,0xc6,0xb3,0xa9,0xd2,0xb4,0x22,0x7c,0x7b,0xbe,0x70, +0x0, 0x28,0x2, 0x61,0x22,0x74,0x1, 0x7e,0x34,0xdf,0xff,0x51,0x9d,0x74,0x6, 0x51, +0x41,0x7d,0x13,0x6c,0x33,0x7d,0x31,0x51,0x9d,0x74,0x6, 0x7d,0x31,0x51,0x9d,0x74, +0x6, 0x7d,0x31,0x51,0x9d,0x74,0x2, 0x51,0x41,0x7d,0x13,0x4e,0x30,0x1, 0x7d,0x31, +0x41,0x9d,0x74,0x6, 0x51,0x41,0x7d,0x13,0x7e,0x30,0xa5,0x7d,0x31,0x51,0x9d,0x7e, +0x30,0xf, 0x74,0x6, 0x7d,0x31,0x51,0x9d,0x7e,0x30,0x6a,0x74,0x6, 0x7d,0x31,0x51, +0x9d,0x7e,0x34,0x0, 0x5, 0x2, 0x6, 0xfe,0xca,0x79,0x7c,0xf7,0x7c,0xeb,0x74,0x1, +0x51,0xec,0x74,0x1, 0x6d,0x33,0x51,0x9d,0xe4,0x51,0x41,0x7d,0x13,0x5e,0x14,0x80, +0x0, 0x7c,0xf, 0x6c,0x11,0x3e,0x4, 0x4d,0x10,0x7d,0x31,0x51,0x9d,0x6d,0x0, 0xbe, +0xe0,0x1, 0x78,0x6, 0x7e,0x14,0x0, 0x8, 0x80,0x2, 0x7d,0x10,0x74,0x4, 0x7d,0x31, +0x51,0x9d,0x4e,0x30,0x10,0x4e,0x30,0x40,0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34, +0x0, 0x19,0x12,0x6, 0xfe,0x4e,0x30,0x1, 0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34, +0xa2,0x1c,0x12,0x6, 0xfe,0x5e,0x30,0xbf,0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34, +0x0, 0x19,0x12,0x6, 0xfe,0x5e,0x30,0xfe,0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34, +0x0, 0x19,0x12,0x6, 0xfe,0x5e,0x30,0xef,0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34, +0x0, 0x5, 0x12,0x6, 0xfe,0xe4,0x51,0xec,0x7e,0x34,0xff,0xf7,0x74,0x4, 0x51,0x9d, +0xda,0x79,0x22,0xca,0xf8,0x7c,0xfb,0x74,0x1, 0x51,0xec,0xe4,0x6d,0x33,0x51,0x9d, +0x74,0x1, 0x6d,0x33,0x51,0x9d,0x6d,0x0, 0xbe,0xf0,0x1, 0x78,0x6, 0x7e,0x14,0x0, +0x8, 0x80,0x2, 0x7d,0x10,0x74,0x4, 0x7d,0x31,0x51,0x9d,0x4e,0x30,0x10,0x4e,0x30, +0x40,0x4e,0x30,0x4, 0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34,0x0, 0x19,0x12,0x6, +0xfe,0x4e,0x30,0x1, 0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34,0xa2,0x1c,0x12,0x6, +0xfe,0x5e,0x30,0xbf,0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34,0x0, 0xa0,0x12,0x6, +0xfe,0x5e,0x30,0xfe,0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34,0x0, 0x19,0x12,0x6, +0xfe,0x5e,0x30,0xef,0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34,0x0, 0x1, 0x12,0x6, +0xfe,0xe4,0x51,0xec,0x7e,0x34,0xff,0xf7,0x74,0x4, 0x51,0x9d,0xda,0xf8,0x22,0x7f, +0x1, 0x7c,0xb1,0xf5,0x16,0x7c,0xb2,0xf5,0x15,0x7c,0xb3,0xf5,0x14,0xa9,0xc2,0xb4, +0x74,0xb, 0x11,0x68,0xe5,0x16,0x11,0x68,0xe5,0x15,0x11,0x68,0xe5,0x14,0x11,0x68, +0xe4,0x1, 0x68,0x7f,0x70,0x7f,0x1, 0x91,0x6f,0x7f,0x7, 0x7e,0x1d,0x10,0x11,0x83, +0xa9,0xd2,0xb4,0xd3,0x22,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0x7c,0xa7,0xa, 0xf6,0x6d,0xee,0x60,0x2, 0x1, 0xb0,0x1b,0xa0,0x68,0x23,0x1b,0xa0, +0x68,0x2e,0x1b,0xa0,0x68,0x3a,0x1b,0xa0,0x68,0x44,0x1b,0xa0,0x68,0x50,0x1b,0xa0, +0x68,0x6d,0x2e,0xa0,0x6, 0x68,0x2, 0x21,0x15,0x7e,0x34,0x0, 0x28,0x7a,0x35,0x25, +0x22,0x7d,0xf, 0x6d,0x11,0x7e,0x35,0x25,0x59,0x13,0x0, 0x2, 0x1b,0x38,0x0, 0x22, +0x7d,0x4e,0x7c,0x89,0x7d,0x5f,0x7c,0x9a,0x7c,0xab,0xe4,0x7e,0x15,0x25,0x80,0x2e, +0x7e,0x15,0x25,0x49,0x31,0x0, 0x2, 0xb, 0x18,0x20,0x2f,0x17,0x80,0x29,0x7d,0x2f, +0x6d,0x33,0x7e,0x15,0x25,0x59,0x31,0x0, 0x6, 0x59,0x21,0x0, 0x4, 0x22,0x7d,0x4e, +0x7c,0x89,0x7d,0x5f,0x7c,0x9a,0x7c,0xab,0xe4,0x7e,0x15,0x25,0xb, 0x16,0x49,0x31, +0x0, 0x2, 0xb, 0x18,0x20,0x2f,0x12,0x59,0x31,0x0, 0x2, 0x1b,0x18,0x20,0x22,0x7e, +0x35,0x25,0x7d,0x23,0xb, 0x26,0x49,0x12,0x0, 0x2, 0xb, 0x28,0x0, 0x2f,0x7, 0x59, +0x12,0x0, 0x2, 0x1b,0x28,0x0, 0x7e,0x24,0x0, 0x6, 0x7a,0x27,0x2, 0x61,0x80,0x5d, +0x7e,0x73,0x2, 0x89,0xa, 0x37,0x6d,0x22,0x7c,0x67,0x6c,0x77,0x7e,0x33,0x2, 0x88, +0xa, 0x13,0x7d,0x1, 0x6d,0x11,0x2f,0x1, 0x7e,0x73,0x2, 0x8a,0xa, 0x37,0x2f,0x10, +0x7e,0x15,0x25,0x59,0x31,0x0, 0x2, 0x1b,0x18,0x20,0x7e,0x73,0x2, 0x8c,0xa, 0x37, +0x6d,0x22,0x7c,0x67,0x6c,0x77,0x7e,0x13,0x2, 0x8b,0xa, 0x51,0x7d,0x45,0x6d,0x55, +0x2f,0x21,0x7e,0x73,0x2, 0x8d,0xa, 0x37,0x2f,0x12,0x59,0x31,0x0, 0x6, 0x59,0x21, +0x0, 0x4, 0x7e,0x34,0x0, 0x6, 0x7a,0x37,0x2, 0x61,0x7e,0x35,0x25,0x7a,0x35,0x23, +0xe4,0x7a,0xb3,0x2, 0x79,0x22,0x75,0xa8,0x0, 0x75,0xdf,0x0, 0x12,0x4, 0x56,0x12, +0x6, 0x65,0x12,0x10,0x21,0xd2,0x7, 0x12,0x10,0x32,0x12,0x6, 0xe3,0x12,0x7, 0x1d, +0xd2,0xaf,0x22,0x7e,0xb3,0x2, 0x69,0xb4,0xc, 0xb, 0xc2,0x86,0x7e,0x34,0x0, 0x64, +0x12,0x6, 0xfe,0xd2,0x86,0x22,0x7e,0xa3,0x2, 0x6a,0xbe,0xa0,0xb, 0x78,0x3a,0x7e, +0x1f,0x2, 0x63,0xbe,0x18,0x0, 0x0, 0x68,0x12,0x7e,0x14,0xe0,0x0, 0x7e,0x4, 0x0, +0x1, 0xbf,0x10,0x38,0x6, 0xa2,0x3, 0xe4,0x33,0x78,0x3, 0x74,0x1, 0x22,0x7e,0x45, +0x23,0x49,0xf4,0x0, 0x6, 0x49,0xe4,0x0, 0x4, 0x49,0x34,0x0, 0x2, 0xb, 0x48,0x20, +0x2f,0x17,0xbf,0x10,0x40,0xb, 0x74,0x1, 0x22,0xbe,0xa0,0xa, 0x68,0x3, 0x74,0x1, +0x22,0xe4,0x22,0x6d,0x33,0x7a,0x37,0x2, 0x61,0x7e,0x34,0x0, 0x28,0x7a,0x35,0x25, +0x31,0x16,0x7e,0x37,0x2, 0x61,0x1b,0x34,0x68,0x6d,0x1b,0x35,0x78,0x2, 0x41,0x4f, +0x1b,0x34,0x78,0x2, 0x41,0x81,0x1b,0x34,0x78,0x2, 0x61,0x3f,0x1b,0x34,0x78,0x2, +0x61,0x70,0xb, 0x36,0x68,0x2, 0x81,0x27,0x6d,0x33,0x7a,0x37,0x2, 0x61,0x7a,0x37, +0x2, 0x67,0x31,0x46,0x60,0x2, 0x81,0x27,0x7e,0x15,0x23,0x49,0x31,0x0, 0x6, 0x49, +0x21,0x0, 0x4, 0x7a,0x1d,0xc, 0x49,0x31,0x0, 0x2, 0xb, 0x18,0x20,0x2e,0x14,0x0, +0xa, 0x6d,0x0, 0x12,0xf, 0x3c,0x7e,0x55,0x23,0x49,0x15,0x0, 0x6, 0x49,0x5, 0x0, +0x4, 0x49,0x35,0x0, 0x2, 0xb, 0x58,0x20,0x12,0x0, 0x9d,0x2e,0x34,0x10,0x0, 0x7a, +0x37,0x2, 0x67,0x31,0x33,0x81,0x27,0x6d,0x33,0x7a,0x37,0x2, 0x61,0x7a,0x37,0x2, +0x67,0x7e,0x18,0x1, 0x0, 0x7a,0x1d,0x10,0x7e,0x15,0x23,0x49,0x31,0x0, 0x2, 0xb, +0x18,0x20,0x2e,0x14,0x0, 0xa, 0x6d,0x0, 0x12,0x14,0x93,0x30,0x0, 0x9, 0x7e,0x35, +0x23,0x9, 0xb3,0x0, 0xa, 0xf5,0x91,0x31,0x33,0x7e,0x34,0xf0,0x88,0x80,0x2c,0x6d, +0x33,0x7a,0x37,0x2, 0x61,0x7a,0x37,0x2, 0x67,0x7e,0x18,0x2, 0x79,0x7a,0x1d,0xc, +0x7e,0x55,0x23,0x49,0x35,0x0, 0x2, 0xb, 0x58,0x20,0x49,0x15,0x0, 0x6, 0x49,0x5, +0x0, 0x4, 0x12,0xe, 0xc0,0x31,0x33,0x7e,0x34,0xf0,0x55,0x7a,0x37,0x2, 0x67,0x81, +0x27,0x75,0x22,0x0, 0x6d,0x33,0x7a,0x37,0x2, 0x61,0x7a,0x37,0x2, 0x67,0x7e,0xb3, +0x2, 0x6a,0xb4,0xa, 0xe, 0x30,0x8, 0x6, 0xe4,0x12,0x13,0xe3,0x81,0x1d,0x12,0x12, +0x2d,0x81,0x1d,0x7e,0xb3,0x2, 0x6a,0xb4,0xb, 0x67,0x7e,0x1f,0x2, 0x63,0xbe,0x18, +0x0, 0x0, 0x78,0x2, 0x81,0x27,0x7e,0x14,0xe0,0x0, 0x7e,0x4, 0x0, 0x1, 0xbf,0x10, +0x28,0x2, 0x81,0x27,0xa2,0x3, 0xe4,0x33,0x78,0x2, 0x81,0x27,0x7d,0x13,0x5e,0x14, +0xf, 0xff,0x78,0x14,0x74,0xc, 0x7f,0x1, 0x1e,0x14,0x1e,0x4, 0x50,0x3, 0x4e,0x20, +0x80,0x14,0x78,0xf4,0x7c,0xb3,0x80,0x12,0x74,0xc, 0x1e,0x34,0x1e,0x24,0x50,0x3, +0x4e,0x60,0x80,0x14,0x78,0xf4,0x7f,0x21,0xb, 0x2c,0xf5,0xb, 0x75,0xa, 0x0, 0x80, +0x7, 0xe5,0xa, 0x12,0x11,0xe4,0x5, 0xa, 0xe5,0xb, 0xbe,0xb1,0xa, 0x38,0xf2,0x81, +0x1d,0x20,0x8, 0x2, 0x81,0x1d,0x7e,0x73,0x2, 0x6a,0xa, 0x57,0x9e,0x54,0x0, 0x80, +0xf5,0x8, 0x6d,0x66,0x80,0xd, 0xe4,0x7e,0xa1,0x8, 0xa, 0x3a,0x2d,0x36,0x12,0x13, +0x48,0xb, 0x64,0x7e,0x73,0x2, 0x46,0xa, 0x37,0xbd,0x36,0x18,0xe9,0x81,0x1d,0x75, +0x22,0x1, 0x6d,0x33,0x7a,0x37,0x2, 0x61,0x6d,0x22,0x7a,0x27,0x2, 0x67,0x30,0x8, +0x2, 0x81,0x1d,0x7d,0x63,0xbe,0x64,0x0, 0x1e,0x48,0x6, 0xbe,0x64,0x0, 0x1f,0x48, +0x5, 0x7c,0xbd,0x12,0x11,0xe4,0xb, 0x64,0xbe,0x64,0x0, 0x20,0x78,0xe7,0x81,0x1d, +0x6d,0x33,0x7a,0x37,0x2, 0x61,0x7a,0x37,0x2, 0x67,0x30,0x8, 0x2, 0x81,0x1d,0x7e, +0x15,0x23,0x49,0x31,0x0, 0x2, 0xb, 0x18,0x20,0x7d,0x3, 0x5e,0x4, 0xf, 0xff,0x68, +0x2, 0x81,0x27,0x49,0x31,0x0, 0x6, 0x49,0x21,0x0, 0x4, 0x4d,0x23,0x78,0x2, 0x81, +0x27,0x49,0x31,0x0, 0x2, 0xb, 0x18,0x20,0x74,0xc, 0x1e,0x34,0x1e,0x24,0x50,0x3, +0x4e,0x60,0x80,0x14,0x78,0xf4,0x7c,0xb7,0xf5,0x8, 0x49,0x31,0x0, 0x6, 0x49,0x21, +0x0, 0x4, 0x7d,0x3, 0x5e,0x4, 0xf, 0xff,0x78,0x1a,0x49,0x31,0x0, 0x6, 0x49,0x21, +0x0, 0x4, 0x74,0xc, 0x1e,0x34,0x1e,0x24,0x50,0x3, 0x4e,0x60,0x80,0x14,0x78,0xf4, +0x7c,0xe7,0x80,0x1a,0x49,0x31,0x0, 0x6, 0x49,0x21,0x0, 0x4, 0x74,0xc, 0x1e,0x34, +0x1e,0x24,0x50,0x3, 0x4e,0x60,0x80,0x14,0x78,0xf4,0x7c,0xe7,0xb, 0xe0,0xa, 0x2e, +0xe5,0x8, 0xa, 0x3b,0x2d,0x32,0xbe,0x34,0x0, 0x1f,0x18,0x1b,0x6c,0xff,0x80,0x9, +0xe5,0x8, 0x2c,0xbf,0x12,0x11,0xe4,0xb, 0xf0,0xbc,0xef,0x38,0xf3,0x7e,0x34,0xf0, +0xaa,0x7a,0x37,0x2, 0x67,0x31,0x33,0x20,0x6, 0x2, 0x21,0xa2,0x7e,0x34,0x13,0x88, +0x12,0x6, 0xfe,0x7e,0x34,0x13,0x88,0x12,0x6, 0xfe,0x75,0xe9,0xff,0x21,0xa2,0xff, diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/Android.mk b/drivers/input/touchscreen/focaltech_v3_mmi/Android.mk new file mode 100644 index 000000000000..ea69db58f654 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/Android.mk @@ -0,0 +1,31 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifneq ($(FOCALTECH_TOUCH_IC_NAME),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_MMI_IC_NAME=$(FOCALTECH_TOUCH_IC_NAME) +else + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_MMI_IC_NAME=ft3519 +endif + +ifneq ($(BOARD_USES_DOUBLE_TAP),) + KERNEL_CFLAGS += CONFIG_INPUT_FOCALTECH_MMI_ENABLE_DOUBLE_TAP=y +endif + +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_MMI=y +endif + +ifneq ($(FOCALTECH_TOUCH_TEST_SUPPORT),) + KERNEL_CFLAGS += CONFIG_INPUT_FTS_TEST_SUPPORT=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := focaltech_v3_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +endif + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/Kbuild b/drivers/input/touchscreen/focaltech_v3_mmi/Kbuild new file mode 100644 index 000000000000..31f5d2c5329a --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/Kbuild @@ -0,0 +1,47 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/focaltech_v3_mmi \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/focaltech_v3_mmi/include + +obj-m := focaltech_v3_mmi.o +focaltech_v3_mmi-objs += focaltech_core.o +focaltech_v3_mmi-objs += focaltech_ex_fun.o +focaltech_v3_mmi-objs += focaltech_ex_mode.o +focaltech_v3_mmi-objs += focaltech_gesture.o +focaltech_v3_mmi-objs += focaltech_esdcheck.o +focaltech_v3_mmi-objs += focaltech_point_report_check.o +focaltech_v3_mmi-objs += focaltech_flash.o +focaltech_v3_mmi-objs += focaltech_i2c.o + +ifneq ($(filter ft3519,$(CONFIG_INPUT_FOCALTECH_MMI_IC_NAME)),) + EXTRA_CFLAGS += -DCONFIG_FTS_IC_NAME_FT3519 + focaltech_v3_mmi-objs += focaltech_flash/focaltech_upgrade_ft5452i.o +endif + +ifneq ($(filter ft3518u,$(CONFIG_INPUT_FOCALTECH_MMI_IC_NAME)),) + EXTRA_CFLAGS += -DCONFIG_FTS_IC_NAME_FT3518U + focaltech_v3_mmi-objs += focaltech_flash/focaltech_upgrade_ft3518u.o +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers + EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI + focaltech_v3_mmi-objs += focaltech_ts_mmi.o +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_FOCALTECH_MMI_ENABLE_DOUBLE_TAP)),) + EXTRA_CFLAGS += -DFOCALTECH_SENSOR_EN +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_FTS_TEST_SUPPORT)),) + EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/focaltech_mtk_v2_mmi/focaltech_test + EXTRA_CFLAGS += -DFTS_TEST_SUPPORT + focaltech_mtk_v2_mmi-objs += focaltech_test/focaltech_test.o + focaltech_mtk_v2_mmi-objs += focaltech_test/focaltech_test_ini.o + +ifneq ($(filter ft3519,$(CONFIG_INPUT_FTS_MTK_V2_MMI_IC_NAME)),) + focaltech_mtk_v2_mmi-objs += focaltech_test/supported_ic/focaltech_test_ft5452i.o +endif +endif + diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/Makefile b/drivers/input/touchscreen/focaltech_v3_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_common.h b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_common.h new file mode 100644 index 000000000000..c791890faeea --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_common.h @@ -0,0 +1,179 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2020, 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.3.5 20210514" + +#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 (FTS_CHIP_TYPE & FLAGBITS(0, FLAG_ICSERIALS_LEN-1)) +#define IC_TO_SERIALS(x) ((x) & FLAGBITS(0, FLAG_ICSERIALS_LEN-1)) +#define FTS_CHIP_IDC ((FTS_CHIP_TYPE & FLAGBIT(FLAG_IDC_BIT)) == FLAGBIT(FLAG_IDC_BIT)) +#define FTS_HID_SUPPORTTED ((FTS_CHIP_TYPE & FLAGBIT(FLAG_HID_BIT)) == FLAGBIT(FLAG_HID_BIT)) + +#define FTS_MAX_CHIP_IDS 8 + +#if defined(CONFIG_FTS_IC_NAME_FT3519) +#define FTS_CHIP_TYPE_MAPPING {{0x89, 0x54, 0x52, 0x54, 0x52, 0x54, 0x5B, 0x54, 0x5E}} +#else +#define FTS_CHIP_TYPE_MAPPING {{0x81, 0x54, 0x52, 0x54, 0x52, 0x00, 0x00, 0x54, 0x5C}} +#endif + +#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_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 { + u16 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 ft_chip_id_t { + u16 type; + u16 chip_ids[FTS_MAX_CHIP_IDS]; +}; + +struct ts_ic_info { + bool is_incell; + bool hid_supported; + struct ft_chip_t ids; + struct ft_chip_id_t cid; +}; + +/***************************************************************************** +* 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__ */ diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_config.h b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_config.h new file mode 100644 index 000000000000..079664319b45 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_config.h @@ -0,0 +1,319 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 _FT8607 0x86070809 +#define _FT8006U 0x8006D80B +#define _FT8006S 0x8006A80B +#define _FT8613 0x8613080C +#define _FT8719 0x8719080D +#define _FT8739 0x8739080E +#define _FT8615 0x8615080F +#define _FT8201 0x82010810 +#define _FT8201AA 0x8201A810 +#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 _FT8720H 0x8720E81C +#define _FT8720M 0x8720F81C +#define _FT8016 0x8016081D +#define _FT2388 0x2388081E +#define _FT8006S_AB 0x8642081F +#define _FT8722 0x87220820 +#define _FT8201AB 0x8201B821 +#define _FT8203 0x82030821 + + +#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 _FT3327G_003 0x3327A482 +#define _FT3427_003 0x3427D482 +#define _FT3427G_003 0x3427A482 +#define _FT5446_003 0x5446D482 +#define _FT5446_Q03 0x5446C482 +#define _FT5446_P03 0x5446A481 +#define _FT5446_N03 0x5446A489 +#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 _FT3519 0x35190489 + +#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 +#define _FT3658G 0x3658A488 + +/*************************************************/ + +/* + * choose your ic chip type of focaltech + */ +#ifdef CONFIG_FTS_IC_NAME_FT3519 +#define FTS_CHIP_TYPE _FT3519 +#define FTS_CHIP_NAME "ft3519" +#elif defined(CONFIG_FTS_IC_NAME_FT3518U) +#define FTS_CHIP_TYPE _FT3518U +#define FTS_CHIP_NAME "ft3518" +#else +#define FTS_CHIP_TYPE _FT3519 +#define FTS_CHIP_NAME "ft3519" +#endif + +/******************* Enables *********************/ +/*********** 1 to enable, 0 to disable ***********/ + +/* + * show debug log info + * enable it for debug, disable it for release + */ +#define FTS_DEBUG_EN 1 + +/* + * 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 + +/* + * Stylus PEN enable + * 1:enable(default),0:disable +*/ +#define FTS_PEN_EN 0 + +/* + * 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) + */ +#ifdef FTS_TEST_SUPPORT +#define FTS_TEST_EN 1 +#else +#define FTS_TEST_EN 0 +#endif + +/* + * Pinctrl enable + * default: disable + */ +#define FTS_PINCTRL_EN 0 + +/* + * 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 0 + +#define FTS_FW_FILE_I_INCELL_CHECK 1 + +/* + * auto upgrade for lcd cfg + */ +#define FTS_AUTO_LIC_UPGRADE_EN 0 + +/* + * Numbers of modules support + */ +#define FTS_GET_MODULE_NUM 0 + +/* + * Usb detect support + */ +#define FTS_USB_DETECT_EN 0 + +/* + * 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 0x0000 +#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 "" +#define FTS_MODULE2_NAME "" +#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.h" + +/* + * 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.h" + +/* + * 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.h" + +/*********************************************************/ + +#endif /* _LINUX_FOCLATECH_CONFIG_H_ */ diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_core.c b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_core.c new file mode 100644 index 000000000000..ca880eb050b0 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_core.c @@ -0,0 +1,2192 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 +#include +#include +#include +#include +#include +#if defined(CONFIG_FB) +#include +#include +#elif defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) +#include +#else +#include +#endif +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#define FTS_SUSPEND_LEVEL 1 /* Early-suspend level */ +#endif +#include "focaltech_core.h" + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +extern int fts_mmi_dev_register(struct fts_ts_data *ts_data); +extern void fts_mmi_dev_unregister(struct fts_ts_data *ts_data); +#endif + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_DRIVER_NAME "fts_ts" +#define FTS_DRIVER_PEN_NAME "fts_ts,pen" +#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 2800000 +#define FTS_VTG_MAX_UV 3300000 +#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; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +static int fts_ts_suspend(struct device *dev); +static int fts_ts_resume(struct device *dev); +#endif + +int fts_check_cid(struct fts_ts_data *ts_data, u8 id_h) +{ + int i = 0; + struct ft_chip_id_t *cid = &ts_data->ic_info.cid; + u8 cid_h = 0x0; + + if (cid->type == 0) + return -ENODATA; + + for (i = 0; i < FTS_MAX_CHIP_IDS; i++) { + cid_h = ((cid->chip_ids[i] >> 8) & 0x00FF); + if (cid_h && (id_h == cid_h)) { + return 0; + } + } + + return -ENODATA; +} + +/***************************************************************************** +* 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; + struct fts_ts_data *ts_data = fts_data; + u8 chip_idh = ts_data->ic_info.ids.chip_idh; + + do { + ret = fts_read_reg(FTS_REG_CHIP_ID, &idh); + if ((idh == chip_idh) || (fts_check_cid(ts_data, idh) == 0)) { + FTS_INFO("TP Ready,Device ID:0x%02x", idh); + return 0; + } else + FTS_DEBUG("TP Not Ready,ReadData:0x%02x,ret:%d", idh, ret); + + 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) { + disable_irq_nosync(fts_data->irq); + 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) { + enable_irq(fts_data->irq); + 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"); + } else { + msleep(10); + 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 ((0xEB == buf[0]) && (0xAA == buf[1]) && (0x08 == buf[2])) { + FTS_DEBUG("hidi2c change to stdi2c successful"); + } else { + FTS_DEBUG("hidi2c change to stdi2c not support or fail"); + } + } +} + +static int fts_match_cid(struct fts_ts_data *ts_data, + u16 type, u8 id_h, u8 id_l, bool force) +{ +#ifdef FTS_CHIP_ID_MAPPING + u32 i = 0; + u32 j = 0; + struct ft_chip_id_t chip_id_list[] = FTS_CHIP_ID_MAPPING; + u32 cid_entries = sizeof(chip_id_list) / sizeof(struct ft_chip_id_t); + u16 id = (id_h << 8) + id_l; + + memset(&ts_data->ic_info.cid, 0, sizeof(struct ft_chip_id_t)); + for (i = 0; i < cid_entries; i++) { + if (!force && (type == chip_id_list[i].type)) { + break; + } else if (force && (type == chip_id_list[i].type)) { + FTS_INFO("match cid,type:0x%x", (int)chip_id_list[i].type); + ts_data->ic_info.cid = chip_id_list[i]; + return 0; + } + } + + if (i >= cid_entries) { + return -ENODATA; + } + + for (j = 0; j < FTS_MAX_CHIP_IDS; j++) { + if (id == chip_id_list[i].chip_ids[j]) { + FTS_DEBUG("cid:%x==%x", id, chip_id_list[i].chip_ids[j]); + FTS_INFO("match cid,type:0x%x", (int)chip_id_list[i].type); + ts_data->ic_info.cid = chip_id_list[i]; + return 0; + } + } + + return -ENODATA; +#else + return -EINVAL; +#endif +} + + +static int fts_get_chip_types( + struct fts_ts_data *ts_data, + u8 id_h, u8 id_l, bool fw_valid) +{ + u32 i = 0; + struct ft_chip_t ctype[] = FTS_CHIP_TYPE_MAPPING; + 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)) + || (!fts_match_cid(ts_data, ctype[i].type, id_h, id_l, 0))) + 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; + } + + fts_match_cid(ts_data, ctype[i].type, id_h, id_l, 1); + 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 }; + + ts_data->ic_info.is_incell = FTS_CHIP_IDC; + ts_data->ic_info.hid_supported = FTS_HID_SUPPORTTED; + + + 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("chip id 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(cid type=0x%x)", + ts_data->ic_info.ids.chip_idh, ts_data->ic_info.ids.chip_idl, + ts_data->ic_info.cid.type); + + 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 fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev = ts_data->input_dev; +#if FTS_MT_PROTOCOL_B_EN + u32 finger_count = 0; + u32 max_touches = ts_data->pdata->max_touch_number; +#endif + + 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); + +#if FTS_PEN_EN + input_report_key(ts_data->pen_dev, BTN_TOOL_PEN, 0); + input_report_key(ts_data->pen_dev, BTN_TOUCH, 0); + input_sync(ts_data->pen_dev); +#endif + + ts_data->touchs = 0; + ts_data->key_state = 0; + mutex_unlock(&ts_data->report_mutex); +} + +/***************************************************************************** +* 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 + +#if FTS_PEN_EN +static int fts_input_pen_report(struct fts_ts_data *data) +{ + struct input_dev *pen_dev = data->pen_dev; + struct pen_event *pevt = &data->pevent; + u8 *buf = data->point_buf; + + + if (buf[3] & 0x08) + input_report_key(pen_dev, BTN_STYLUS, 1); + else + input_report_key(pen_dev, BTN_STYLUS, 0); + + if (buf[3] & 0x02) + input_report_key(pen_dev, BTN_STYLUS2, 1); + else + input_report_key(pen_dev, BTN_STYLUS2, 0); + + pevt->inrange = (buf[3] & 0x20) ? 1 : 0; + pevt->tip = (buf[3] & 0x01) ? 1 : 0; + pevt->x = ((buf[4] & 0x0F) << 8) + buf[5]; + pevt->y = ((buf[6] & 0x0F) << 8) + buf[7]; + pevt->p = ((buf[8] & 0x0F) << 8) + buf[9]; + pevt->id = buf[6] >> 4; + pevt->flag = buf[4] >> 6; + pevt->tilt_x = (buf[10] << 8) + buf[11]; + pevt->tilt_y = (buf[12] << 8) + buf[13]; + pevt->tool_type = BTN_TOOL_PEN; + + if (data->log_level >= 2 || + ((1 == data->log_level) && (FTS_TOUCH_DOWN == pevt->flag))) { + FTS_DEBUG("[PEN]x:%d,y:%d,p:%d,inrange:%d,tip:%d,flag:%d DOWN", + pevt->x, pevt->y, pevt->p, pevt->inrange, + pevt->tip, pevt->flag); + } + + if ( (data->log_level >= 1) && (!pevt->inrange)) { + FTS_DEBUG("[PEN]UP"); + } + + input_report_abs(pen_dev, ABS_X, pevt->x); + input_report_abs(pen_dev, ABS_Y, pevt->y); + input_report_abs(pen_dev, ABS_PRESSURE, pevt->p); + + /* check if the pen support tilt event */ + if ((pevt->tilt_x != 0) || (pevt->tilt_y != 0)) { + input_report_abs(pen_dev, ABS_TILT_X, pevt->tilt_x); + input_report_abs(pen_dev, ABS_TILT_Y, pevt->tilt_y); + } + + input_report_key(pen_dev, BTN_TOUCH, pevt->tip); + input_report_key(pen_dev, BTN_TOOL_PEN, pevt->inrange); + input_sync(pen_dev); + + return 0; +} +#endif + + +static int fts_read_touchdata(struct fts_ts_data *data) +{ + int ret = 0; + int size = 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; + } + } + +#if defined(FTS_HIGH_REPORT) && FTS_HIGH_REPORT + if (data->point_num >= data->pdata->max_touch_number) + size = data->pnt_buf_size - 1; + else if (data->point_num > 2) + size = data->point_num * 6 + 3; + else + size = FTS_SIZE_DEFAULT; +#if defined(FTS_PEN_EN) && FTS_PEN_EN + size = FTS_SIZE_DEFAULT; +#endif + + ret = fts_read(buf, 1, buf + 1, size); + if (ret < 0) { + FTS_ERROR("read touchdata failed, ret:%d", ret); + return ret; + } + + if ((buf[size] != 0xFF) && (size < (data->pnt_buf_size - 1)) +#if defined(FTS_PEN_EN) && FTS_PEN_EN + && ((buf[2] & 0xF0) != 0xB0) +#endif + ) { + buf[0] += size; + ret = fts_read(buf, 1, buf + 1 + size, data->pnt_buf_size - 1 - size); + if (ret < 0) { + FTS_ERROR("read touchdata2 failed, ret:%d", ret); + return ret; + } + } +#else + size = data->pnt_buf_size - 1; + ret = fts_read(buf, 1, buf + 1, size); + if (ret < 0) { + FTS_ERROR("read touchdata failed, ret:%d", ret); + return ret; + } +#endif + + 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; + } + +#if FTS_PEN_EN + if ((buf[2] & 0xF0) == 0xB0) { + fts_input_pen_report(data); + return 2; + } +#endif + + 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); + data->point_num = 0; + return -EIO; + } + } + + if (data->point_num > max_touch_num) { + FTS_INFO("invalid point_num(%d)", data->point_num); + data->point_num = 0; + 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(%02x)", buf[2]); + 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) +{ +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM + int ret = 0; + struct fts_ts_data *ts_data = fts_data; + + if ((ts_data->suspended) && (ts_data->pm_suspend)) { + ret = wait_for_completion_timeout( + &ts_data->pm_completion, + msecs_to_jiffies(FTS_TIMEOUT_COMERR_PM)); + if (!ret) { + FTS_ERROR("Bus don't resume from pm(deep),timeout,skip irq"); + return IRQ_HANDLED; + } + } +#endif + + fts_irq_read_report(); + 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; + + 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); + + return ret; +} + +#if FTS_PEN_EN +static int fts_input_pen_init(struct fts_ts_data *ts_data) +{ + int ret = 0; + struct input_dev *pen_dev; + struct fts_ts_platform_data *pdata = ts_data->pdata; + + FTS_FUNC_ENTER(); + pen_dev = input_allocate_device(); + if (!pen_dev) { + FTS_ERROR("Failed to allocate memory for input_pen device"); + return -ENOMEM; + } + + pen_dev->dev.parent = ts_data->dev; + pen_dev->name = FTS_DRIVER_PEN_NAME; + 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(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, pdata->x_min, pdata->x_max, 0, 0); + input_set_abs_params(pen_dev, ABS_Y, pdata->y_min, pdata->y_max, 0, 0); + input_set_abs_params(pen_dev, ABS_PRESSURE, 0, 4096, 0, 0); + + ret = input_register_device(pen_dev); + if (ret) { + FTS_ERROR("Input device registration failed"); + input_free_device(pen_dev); + pen_dev = NULL; + return ret; + } + + ts_data->pen_dev = pen_dev; + FTS_FUNC_EXIT(); + return 0; +} +#endif + +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; + } + +#if FTS_PEN_EN + ret = fts_input_pen_init(ts_data); + if (ret) { + FTS_ERROR("Input-pen device registration failed"); + input_set_drvdata(input_dev, NULL); + input_free_device(input_dev); + input_dev = NULL; + return ret; + } +#endif + + 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 = ts_data->pdata->max_touch_number; + ts_data->pnt_buf_size = point_num * FTS_ONE_TCH_LEN + 3; + 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 */ + +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 = 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); + } + } + 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 = 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); + } + } + 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; + } + + if (regulator_count_voltages(ts_data->vdd) > 0) { + 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); + regulator_put(ts_data->vdd); + return ret; + } + } + + ts_data->vcc_i2c = regulator_get(ts_data->dev, "vcc_i2c"); + if (!IS_ERR_OR_NULL(ts_data->vcc_i2c)) { + if (regulator_count_voltages(ts_data->vcc_i2c) > 0) { + 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); + regulator_put(ts_data->vcc_i2c); + } + } + } + +#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; +} + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +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 //CONFIG_INPUT_TOUCHSCREEN_MMI +#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_string(np, "focaltech,panel-supplier", + &fts_data->panel_supplier); + if (ret < 0) { + fts_data->panel_supplier = NULL; + FTS_ERROR("Unable to read panel supplier\n"); + } else { + FTS_INFO("panel supplier is %s", (char *)fts_data->panel_supplier); + } + + FTS_FUNC_EXIT(); + return 0; +} + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +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); +} + +#if defined(CONFIG_FB) +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 (!evdata) { + FTS_ERROR("evdata is null"); + return 0; + } + + 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_DRM) +#if defined(CONFIG_DRM_PANEL) +static struct drm_panel *active_panel; + +static int drm_check_dt(struct device_node *np) +{ + int i = 0; + int count = 0; + struct device_node *node = NULL; + struct drm_panel *panel = NULL; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) { + FTS_ERROR("find drm_panel count(%d) fail", count); + return -ENODEV; + } + + 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)) { + FTS_INFO("find drm_panel successfully"); + active_panel = panel; + return 0; + } + } + + FTS_ERROR("no find drm_panel"); + return -ENODEV; +} + +static int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank = NULL; + struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data, + fb_notif); + + if (!evdata) { + FTS_ERROR("evdata is null"); + return 0; + } + + if (!((event == DRM_PANEL_EARLY_EVENT_BLANK ) + || (event == DRM_PANEL_EVENT_BLANK))) { + FTS_INFO("event(%lu) do not need process\n", event); + return 0; + } + + blank = evdata->data; + FTS_INFO("DRM event:%lu,blank:%d", event, *blank); + switch (*blank) { + case DRM_PANEL_BLANK_UNBLANK: + if (DRM_PANEL_EARLY_EVENT_BLANK == event) { + FTS_INFO("resume: event = %lu, not care\n", event); + } else if (DRM_PANEL_EVENT_BLANK == event) { + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + } + break; + case DRM_PANEL_BLANK_POWERDOWN: + if (DRM_PANEL_EARLY_EVENT_BLANK == event) { + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); + } else if (DRM_PANEL_EVENT_BLANK == event) { + FTS_INFO("suspend: event = %lu, not care\n", event); + } + break; + default: + FTS_INFO("DRM BLANK(%d) do not need process\n", *blank); + break; + } + + return 0; +} +#else +static int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank = NULL; + struct fts_ts_data *ts_data = container_of(self, struct fts_ts_data, + fb_notif); + + if (!evdata) { + FTS_ERROR("evdata is null"); + return 0; + } + + if (!((event == MSM_DRM_EARLY_EVENT_BLANK ) + || (event == MSM_DRM_EVENT_BLANK))) { + FTS_INFO("event(%lu) do not need process\n", event); + return 0; + } + + blank = evdata->data; + FTS_INFO("DRM event:%lu,blank:%d", event, *blank); + switch (*blank) { + case MSM_DRM_BLANK_UNBLANK: + if (MSM_DRM_EARLY_EVENT_BLANK == event) { + FTS_INFO("resume: event = %lu, not care\n", event); + } else if (MSM_DRM_EVENT_BLANK == event) { + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); + } + break; + case MSM_DRM_BLANK_POWERDOWN: + if (MSM_DRM_EARLY_EVENT_BLANK == event) { + cancel_work_sync(&fts_data->resume_work); + fts_ts_suspend(ts_data->dev); + } else if (MSM_DRM_EVENT_BLANK == event) { + FTS_INFO("suspend: event = %lu, not care\n", event); + } + break; + default: + FTS_INFO("DRM BLANK(%d) do not need process\n", *blank); + break; + } + + return 0; +} +#endif +#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); + + cancel_work_sync(&fts_data->resume_work); + 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); + + queue_work(fts_data->ts_workqueue, &fts_data->resume_work); +} +#endif +#endif //CONFIG_INPUT_TOUCHSCREEN_MMI + +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"); + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) + ret = drm_check_dt(ts_data->dev->of_node); + if (ret) { + FTS_ERROR("parse drm-panel fail"); + } +#endif +#endif +#endif + } 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); + + /* 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_gpio_configure(ts_data); + if (ret) { + FTS_ERROR("configure the gpios fail"); + goto err_gpio_config; + } + +#if FTS_POWER_SOURCE_CUST_EN + ret = fts_power_source_init(ts_data); + if (ret) { + FTS_ERROR("fail to get power(regulator)"); + goto err_power_init; + } +#endif + +#if (!FTS_CHIP_IDC) + fts_reset_proc(200); +#endif + + ret = fts_get_ic_information(ts_data); + if (ret) { + FTS_ERROR("not focal IC, unregister driver"); + goto err_irq_req; + } + + 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_TEST_EN + ret = fts_test_init(ts_data); + if (ret) { + FTS_ERROR("init production test fail"); + } +#endif + +#if FTS_ESDCHECK_EN + ret = fts_esdcheck_init(ts_data); + if (ret) { + FTS_ERROR("init esd check fail"); + } +#endif + + ret = fts_irq_registration(ts_data); + if (ret) { + FTS_ERROR("request irq failed"); + goto err_irq_req; + } + + ret = fts_fwupg_init(ts_data); + if (ret) { + FTS_ERROR("init fw upgrade fail"); + } + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + if (ts_data->ts_workqueue) { + INIT_WORK(&ts_data->resume_work, fts_resume_work); + } +#endif + +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM + init_completion(&ts_data->pm_completion); + ts_data->pm_suspend = false; +#endif + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if defined(CONFIG_FB) + 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_DRM) + ts_data->fb_notif.notifier_call = drm_notifier_callback; +#if defined(CONFIG_DRM_PANEL) + if (active_panel) { + ret = drm_panel_notifier_register(active_panel, &ts_data->fb_notif); + if (ret) + FTS_ERROR("[DRM]drm_panel_notifier_register fail: %d\n", ret); + } +#else + ret = msm_drm_register_client(&ts_data->fb_notif); + if (ret) { + FTS_ERROR("[DRM]Unable to register fb_notifier: %d\n", ret); + } +#endif +#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 +#endif + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + fts_mmi_dev_register(ts_data); +#endif + + FTS_FUNC_EXIT(); + return 0; + +err_irq_req: +#if FTS_POWER_SOURCE_CUST_EN +err_power_init: + fts_power_source_exit(ts_data); +#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); +err_gpio_config: + kfree_safe(ts_data->point_buf); + kfree_safe(ts_data->events); +err_report_buffer: + input_unregister_device(ts_data->input_dev); +#if FTS_PEN_EN + input_unregister_device(ts_data->pen_dev); +#endif +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(); + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + fts_mmi_dev_unregister(ts_data); +#endif + +#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_TEST_EN + fts_test_exit(ts_data); +#endif + +#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 FTS_PEN_EN + input_unregister_device(ts_data->pen_dev); +#endif + + if (ts_data->ts_workqueue) + destroy_workqueue(ts_data->ts_workqueue); + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if defined(CONFIG_FB) + if (fb_unregister_client(&ts_data->fb_notif)) + FTS_ERROR("[FB]Error occurred while unregistering fb_notifier."); +#elif defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) + if (active_panel) + drm_panel_notifier_unregister(active_panel, &ts_data->fb_notif); +#else + if (msm_drm_unregister_client(&ts_data->fb_notif)) + FTS_ERROR("[DRM]Error occurred while unregistering fb_notifier.\n"); +#endif +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts_data->early_suspend); +#endif +#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; +} + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +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; + } + +#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 + } + } + + fts_release_all_finger(); + ts_data->suspended = true; + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_ts_resume(struct device *dev) +{ + struct fts_ts_data *ts_data = fts_data; + + FTS_FUNC_ENTER(); + if (!ts_data->suspended) { + FTS_DEBUG("Already in awake state"); + return 0; + } + + fts_release_all_finger(); + + if (!ts_data->ic_info.is_incell) { +#if FTS_POWER_SOURCE_CUST_EN + fts_power_source_resume(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; + FTS_FUNC_EXIT(); + return 0; +} +#endif + +#if defined(CONFIG_PM) && FTS_PATCH_COMERR_PM +static int fts_pm_suspend(struct device *dev) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + FTS_INFO("system enters into pm_suspend"); + ts_data->pm_suspend = true; + reinit_completion(&ts_data->pm_completion); + return 0; +} + +static int fts_pm_resume(struct device *dev) +{ + struct fts_ts_data *ts_data = dev_get_drvdata(dev); + + FTS_INFO("system resumes from pm_suspend"); + ts_data->pm_suspend = false; + complete(&ts_data->pm_completion); + return 0; +} + +static const struct dev_pm_ops fts_dev_pm_ops = { + .suspend = fts_pm_suspend, + .resume = fts_pm_resume, +}; +#endif + +/***************************************************************************** +* TP Driver +*****************************************************************************/ + +static int fts_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + struct fts_ts_data *ts_data = NULL; + + FTS_INFO("Touch Screen(I2C BUS) driver prboe..."); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + FTS_ERROR("I2C not supported"); + return -ENODEV; + } + + /* 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; +} + +static int fts_ts_remove(struct i2c_client *client) +{ + return fts_ts_remove_entry(i2c_get_clientdata(client)); +} + +static const struct i2c_device_id fts_ts_id[] = { + {FTS_DRIVER_NAME, 0}, + {}, +}; +static const struct of_device_id fts_dt_match[] = { + {.compatible = "focaltech,fts", }, + {}, +}; +MODULE_DEVICE_TABLE(of, fts_dt_match); + +static struct i2c_driver fts_ts_driver = { + .probe = fts_ts_probe, + .remove = fts_ts_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_id, +}; + +static int __init fts_ts_init(void) +{ + int ret = 0; + + FTS_FUNC_ENTER(); + ret = i2c_add_driver(&fts_ts_driver); + if ( ret != 0 ) { + FTS_ERROR("Focaltech touch screen driver init failed!"); + } + FTS_FUNC_EXIT(); + return ret; +} + +static void __exit fts_ts_exit(void) +{ + i2c_del_driver(&fts_ts_driver); +} +module_init(fts_ts_init); +module_exit(fts_ts_exit); + +MODULE_AUTHOR("FocalTech Driver Team"); +MODULE_DESCRIPTION("FocalTech Touchscreen Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_core.h b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_core.h new file mode 100644 index 000000000000..bef9faf57ee0 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_core.h @@ -0,0 +1,320 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "focaltech_common.h" +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +#include +#endif + +/***************************************************************************** +* 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 FTS_MAX_COMPATIBLE_TYPE 4 +#define FTS_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 + +#define FTS_HIGH_REPORT 0 +#define FTS_SIZE_DEFAULT 15 + + +/***************************************************************************** +* Private enumerations, structures and unions using typedef +*****************************************************************************/ +struct ftxxxx_proc { + struct proc_dir_entry *proc_entry; + u8 opmode; + u8 cmd_len; + u8 cmd[FTS_MAX_COMMMAND_LENGTH]; +}; + +struct fts_ts_platform_data { + 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; +}; + +struct pen_event { + int inrange; + int tip; + 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 tilt_x; + int tilt_y; + int tool_type; +}; + +struct fts_ts_data { + struct i2c_client *client; + struct spi_device *spi; + struct device *dev; + struct input_dev *input_dev; + struct input_dev *pen_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 ftxxxx_proc proc; + spinlock_t irq_lock; + struct mutex report_mutex; + struct mutex bus_lock; + unsigned long intr_jiffies; + 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 force_reflash; + bool irq_disabled; + bool power_disabled; + bool glove_mode; + bool cover_mode; + bool charger_mode; + bool gesture_mode; /* gesture enable or disable, default: disable */ + bool prc_mode; + struct pen_event pevent; + /* multi-touch */ + struct ts_event *events; + u8 *bus_tx_buf; + u8 *bus_rx_buf; + int bus_type; + u8 *point_buf; + int pnt_buf_size; + int touchs; + int key_state; + int touch_point; + int point_num; + const char *panel_supplier; + 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 + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + struct ts_mmi_class_methods *imports; +#endif +}; + +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_upgrade_bin(char *fw_name, bool force); +void fts_fwupg_bin(void); +int fts_enter_test_environment(bool test_state); +int fts_fw_update_vendor_name(const char* name); + +/* Other */ +int fts_reset_proc(int hdelayms); +int fts_check_cid(struct fts_ts_data *ts_data, u8 id_h); +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_power_source_ctrl(struct fts_ts_data *ts_data, int enable); +#endif /* __LINUX_FOCALTECH_CORE_H__ */ diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_esdcheck.c b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_esdcheck.c new file mode 100644 index 000000000000..8bc40e808bce --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_esdcheck.c @@ -0,0 +1,462 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 idh = 0; + u8 chip_id = ts_data->ic_info.ids.chip_idh; + + for (i = 0; i < 3; i++) { + ret = fts_read_reg(FTS_REG_CHIP_ID, &idh); + if (ret < 0) { + FTS_ERROR("read chip id fail,ret:%d", ret); + fts_esdcheck_data.nack_cnt++; + } else { + if ((idh == chip_id) || (fts_check_cid(ts_data, idh) == 0)) { + break; + } else { + FTS_DEBUG("read chip_id:%x,retry:%d", idh, 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(®_addr, 1, ®_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, ®_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 function"); + 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 */ + diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_ex_fun.c b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_ex_fun.c new file mode 100644 index 000000000000..c0a27df59278 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_ex_fun.c @@ -0,0 +1,1419 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 "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_WRITE_DATA_DIRECT 16 +#define PROC_READ_DATA_DIRECT 17 +#define PROC_CONFIGURE 18 +#define PROC_CONFIGURE_INTR 20 +#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) { + FTS_ERROR("apk proc count(%d) fail", buflen); + 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 >= FTS_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: + if (buflen < PROC_BUF_SIZE) { + 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; + + + case PROC_CONFIGURE_INTR: + if (writebuf[1] == 0) + fts_irq_disable(); + else + fts_irq_enable(); + 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) { + FTS_ERROR("apk proc read count(%d) fail", buflen); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == readbuf) { + FTS_ERROR("apk proc 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 + + ret = num_read_chars; +proc_read_err: + if (copy_to_user(buff, readbuf, num_read_chars)) { + FTS_ERROR("copy to user error"); + ret = -EFAULT; + } + + if ((buflen > PROC_BUF_SIZE) && readbuf) { + kfree(readbuf); + readbuf = NULL; + } + return ret; +} + +static const struct file_operations fts_proc_fops = { + .owner = THIS_MODULE, + .read = fts_debug_read, + .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) { + FTS_ERROR("apk proc wirte count(%d) fail", buflen); + 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 >= FTS_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: + if (buflen < PROC_BUF_SIZE) { + 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; + + + case PROC_CONFIGURE_INTR: + if (writebuf[1] == 0) + fts_irq_disable(); + else + fts_irq_enable(); + 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) { + FTS_ERROR("apk proc read count(%d) fail", buflen); + return -EINVAL; + } + + if (buflen > PROC_BUF_SIZE) { + readbuf = (u8 *)kzalloc(buflen * sizeof(u8), GFP_KERNEL); + if (NULL == readbuf) { + FTS_ERROR("apk proc 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 + + ret = num_read_chars; +proc_read_err: + if (copy_to_user(buff, readbuf, num_read_chars)) { + FTS_ERROR("copy to user error"); + ret = -EFAULT; + } + + 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; +} + +static ssize_t fts_productinfo_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s\n", FTS_CHIP_NAME); +} + +static ssize_t fts_ic_ver_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_VENDOR_ID, &val); + count += snprintf(buf + count, PAGE_SIZE, "Product ID: 0x%02x\n", val); + fts_read_reg(FTS_REG_FW_VER, &val); + count += snprintf(buf + count, PAGE_SIZE, "Build ID: 0000-%02x\n", val); + count += scnprintf(buf + count, PAGE_SIZE, "IC: %s\n", FTS_CHIP_NAME); +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + mutex_unlock(&input_dev->mutex); + return count; +} + +/* fts_tpfwver interface */ +static ssize_t fts_tpfwver_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + int ret = 0; + 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 + ret = fts_read_reg(FTS_REG_FW_VER, &fwver); +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + if ((ret < 0) || (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_tpfwver_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return -EPERM; +} + +static ssize_t buildid_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, "0000-%02x\n", fwver); + + mutex_unlock(&input_dev->mutex); + return num_read_chars; +} + +/* 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!"); + + } else { + 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(®, 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; + } + } + } + +#if FTS_ESDCHECK_EN + fts_esdcheck_proc_busy(0); +#endif + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* fts_upgrade_bin interface */ +static ssize_t fts_fwupgradebin_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +static ssize_t fts_fwupgradebin_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char fwname[FILE_NAME_LENGTH] = { 0 }; + struct input_dev *input_dev = fts_data->input_dev; + + if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) { + FTS_ERROR("fw bin name's length(%d) fail", (int)count); + return -EINVAL; + } + memset(fwname, 0, sizeof(fwname)); + snprintf(fwname, FILE_NAME_LENGTH, "%s", buf); + fwname[count - 1] = '\0'; + + FTS_INFO("upgrade with bin file through sysfs node"); + mutex_lock(&input_dev->mutex); + fts_upgrade_bin(fwname, 0); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* fts_force_upgrade interface */ +static ssize_t fts_fwforceupg_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + return -EPERM; +} + +static ssize_t fts_fwforceupg_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char fwname[FILE_NAME_LENGTH]; + struct input_dev *input_dev = fts_data->input_dev; + + if ((count <= 1) || (count >= FILE_NAME_LENGTH - 32)) { + FTS_ERROR("fw bin name's length(%d) fail", (int)count); + return -EINVAL; + } + memset(fwname, 0, sizeof(fwname)); + snprintf(fwname, FILE_NAME_LENGTH, "%s", buf); + fwname[count - 1] = '\0'; + + FTS_INFO("force upgrade through sysfs node"); + mutex_lock(&input_dev->mutex); + fts_upgrade_bin(fwname, 1); + 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 += snprintf(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 += snprintf(buf + count, PAGE_SIZE, + "reset gpio:%d,int gpio:%d,irq:%d\n", + pdata->reset_gpio, pdata->irq_gpio, ts_data->irq); + + count += snprintf(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 += snprintf(buf + count, PAGE_SIZE, "BUS:%s,addr:0x%x\n", + "I2C", ts_data->client->addr); + } else { + count += snprintf(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_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; +} + +static ssize_t fts_panel_supplier_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *data = dev_get_drvdata(dev); + + if (data->panel_supplier) + return scnprintf(buf, PAGE_SIZE, "%s\n", + data->panel_supplier); + return 0; +} +static DEVICE_ATTR(panel_supplier, 0444, fts_panel_supplier_show, NULL); + +/* get the fw version example:cat fw_version */ +static DEVICE_ATTR(fts_fw_version, S_IRUGO | S_IWUSR, fts_tpfwver_show, fts_tpfwver_store); + +/* 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); +/* upgrade from fw bin file example:echo "*.bin" > fts_upgrade_bin */ +static DEVICE_ATTR(fts_upgrade_bin, S_IRUGO | S_IWUSR, fts_fwupgradebin_show, fts_fwupgradebin_store); +static DEVICE_ATTR(fts_force_upgrade, S_IRUGO | S_IWUSR, fts_fwforceupg_show, fts_fwforceupg_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); +static DEVICE_ATTR(buildid, S_IRUGO, buildid_show, NULL); +static DEVICE_ATTR(productinfo, S_IRUGO, fts_productinfo_show, NULL); +static DEVICE_ATTR(ic_ver, S_IRUGO, fts_ic_ver_show, NULL); + +/* 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_upgrade_bin.attr, + &dev_attr_fts_force_upgrade.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, + &dev_attr_buildid.attr, + &dev_attr_productinfo.attr, + &dev_attr_ic_ver.attr, + &dev_attr_panel_supplier.attr, + NULL +}; + +static struct attribute_group fts_attribute_group = { + .attrs = fts_attributes +}; + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#include +#include + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_data *data = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!data) { + pr_err("cannot get ft_data pointer\n"); + return (ssize_t)0; + } + path = kobject_get_path(&data->client->dev.kobj, GFP_KERNEL); + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "focaltech"); +} + +static ssize_t panel_supplier_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return fts_panel_supplier_show(dev, attr, buf); +} + +static ssize_t ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return fts_ic_ver_show(dev, attr, buf); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_RO(ic_ver), + __ATTR_RO(panel_supplier), + __ATTR_NULL +}; + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +static int ft_ts_sysfs_class(void *_data, bool create) +{ + struct fts_ts_data *data = _data; + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + static struct class *touchscreen_class; + static struct device *ts_class_dev; + static int minor; + + if (create) { + minor = input_get_new_minor(data->client->addr, + 1, false); + if (minor < 0) + minor = input_get_new_minor(TSDEV_MINOR_BASE, + TSDEV_MINOR_MAX, true); + pr_info("assigned minor %d\n", minor); + + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + + ts_class_dev = device_create(touchscreen_class, NULL, + MKDEV(INPUT_MAJOR, minor), + data, "%s", FTS_CHIP_NAME); + if (IS_ERR(ts_class_dev)) { + error = PTR_ERR(ts_class_dev); + ts_class_dev = NULL; + return error; + } + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + error = device_create_file(ts_class_dev, &attrs[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + } else { + if (!touchscreen_class || !ts_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(ts_class_dev, &attrs[i]); + + device_unregister(ts_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor)); + ts_class_dev = NULL; + class_unregister(touchscreen_class); + pr_err("error creating touchscreen class\n"); + + return -ENODEV; +} +#endif + +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!!"); + } + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + ret = ft_ts_sysfs_class(ts_data, true); + if (ret) { + FTS_ERROR("[EX]: fts_sysfs_class() failed!!"); + sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group); + return -ENOMEM; + } +#endif + + return ret; +} + +int fts_remove_sysfs(struct fts_ts_data *ts_data) +{ + sysfs_remove_group(&ts_data->dev->kobj, &fts_attribute_group); +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + ft_ts_sysfs_class(ts_data, false); +#endif + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_ex_mode.c b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_ex_mode.c new file mode 100644 index 000000000000..848c524467fe --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_ex_mode.c @@ -0,0 +1,307 @@ +/* + * + * FocalTech ftxxxx TouchScreen driver. + * + * Copyright (c) 2012-2020, 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, +}; + +/***************************************************************************** +* 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; + u8 m_val = 0; + + if (value) + m_val = 0x01; + else + m_val = 0x00; + + switch (mode) { + case MODE_GLOVE: + ret = fts_write_reg(FTS_REG_GLOVE_MODE_EN, m_val); + if (ret < 0) { + FTS_ERROR("MODE_GLOVE switch to %d fail", m_val); + } + break; + case MODE_COVER: + ret = fts_write_reg(FTS_REG_COVER_MODE_EN, m_val); + if (ret < 0) { + FTS_ERROR("MODE_COVER switch to %d fail", m_val); + } + break; + case MODE_CHARGER: + ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, m_val); + if (ret < 0) { + FTS_ERROR("MODE_CHARGER switch to %d fail", m_val); + } + 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 = snprintf(buf + count, PAGE_SIZE, "Glove Mode:%s\n", + ts_data->glove_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "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 = snprintf(buf + count, PAGE_SIZE, "Cover Mode:%s\n", + ts_data->cover_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "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 = snprintf(buf + count, PAGE_SIZE, "Charger Mode:%s\n", + ts_data->charger_mode ? "On" : "Off"); + count += snprintf(buf + count, PAGE_SIZE, "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; +} + + +/* 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 struct attribute *fts_touch_mode_attrs[] = { + &dev_attr_fts_glove_mode.attr, + &dev_attr_fts_cover_mode.attr, + &dev_attr_fts_charger_mode.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); + } + + 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; + + 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; +} diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_flash.c b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_flash.c new file mode 100644 index 000000000000..e235bc555c3d --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_flash.c @@ -0,0 +1,2192 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 */ +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +#define FTS_FW_NAME_PREX_WITH_REQUEST "" +#else +#define FTS_FW_NAME_PREX_WITH_REQUEST "focaltech_ts_fw_" +#endif + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +u8 fw_file[] = { +}; + +u8 fw_file2[] = { +}; + +u8 fw_file3[] = { +}; +#else +u8 fw_file[] = { +#include FTS_UPGRADE_FW_FILE +}; + +u8 fw_file2[] = { +#include FTS_UPGRADE_FW2_FILE +}; + +u8 fw_file3[] = { +#include FTS_UPGRADE_FW3_FILE +}; +#endif + +struct upgrade_module module_list[] = { + {FTS_MODULE_ID, FTS_MODULE_NAME, fw_file, sizeof(fw_file)}, + {FTS_MODULE2_ID, FTS_MODULE2_NAME, fw_file2, sizeof(fw_file2)}, + {FTS_MODULE3_ID, FTS_MODULE3_NAME, fw_file3, sizeof(fw_file3)}, +}; + +struct upgrade_func *upgrade_func_list[] = { + &upgrade_func_ft5452i, +}; + +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[3] = { 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, 3); + if (ret < 0) { + FTS_ERROR("write 90 cmd fail"); + return ret; + } + FTS_INFO("read boot id:0x%02x%02x%02x", val[0], val[1], val[2]); + + 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[8] = { 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); + cmd[7] = 0xCC; + ret = fts_write(cmd, 8); + 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; + } + tmp = 0x33; + 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, ®_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 ((ECC_CHECK_MODE_CRC16 == upg->func->pram_ecc_check_mode) || + (upg->func->upgspec_version >= UPGRADE_SPEC_V_1_0)) { + return fts_pram_ecc_cal_algo(upg, saddr, len); + } else { + 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; + + 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; + + for (j = 0; j < packet_len; j++) { + packet_buf[cmdlen + j] = buf[offset + j]; + if (ECC_CHECK_MODE_XOR == upg->func->pram_ecc_check_mode) { + 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 ((ECC_CHECK_MODE_CRC16 == upg->func->pram_ecc_check_mode) || + (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, ®_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; +} + +/************************************************************************ +* Name: fts_fwupg_enter_into_boot +* Brief: enter into boot environment, ready for upgrade +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +int fts_fwupg_enter_into_boot(void) +{ + int ret = 0; + bool fwvalid = false; + bool state = false; + struct fts_upgrade *upg = fwupgrade; + + 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 */ + 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; +} + +/************************************************************************ + * 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 ((ECC_CHECK_MODE_CRC16 == upg->func->fw_ecc_check_mode) || + (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 ((ECC_CHECK_MODE_CRC16 == upg->func->fw_ecc_check_mode) || + (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 (wr_ok == read_status) { + break; + } + mdelay(FTS_RETRIES_DELAY_WRITE); + } + } + + ecc_in_host = (int)ecc_tmp; + if ((ECC_CHECK_MODE_CRC16 == upg->func->fw_ecc_check_mode) || + (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 + ***********************************************************************/ +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; +} + +static int fts_read_file(char *file_name, u8 **file_buf) +{ + int ret = 0; + char file_path[FILE_NAME_LENGTH] = { 0 }; + struct file *filp = NULL; + struct inode *inode; + mm_segment_t old_fs; + loff_t pos; + loff_t file_len = 0; + + if ((NULL == file_name) || (NULL == file_buf)) { + FTS_ERROR("filename/filebuf is NULL"); + return -EINVAL; + } + + snprintf(file_path, FILE_NAME_LENGTH, "%s%s", FTS_FW_BIN_FILEPATH, file_name); + filp = filp_open(file_path, O_RDONLY, 0); + if (IS_ERR(filp)) { + FTS_ERROR("open %s file fail", file_path); + return -ENOENT; + } + +#if 1 + inode = filp->f_inode; +#else + /* reserved for linux earlier verion */ + inode = filp->f_dentry->d_inode; +#endif + + file_len = inode->i_size; + *file_buf = (u8 *)vmalloc(file_len); + if (NULL == *file_buf) { + FTS_ERROR("file buf malloc fail"); + filp_close(filp, NULL); + return -ENOMEM; + } + old_fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + ret = vfs_read(filp, *file_buf, file_len , &pos); + if (ret < 0) + FTS_ERROR("read file fail"); + FTS_INFO("file len:%d read len:%d pos:%d", (u32)file_len, ret, (u32)pos); + filp_close(filp, NULL); + set_fs(old_fs); + + return ret; +} + +int fts_upgrade_bin(char *fw_name, bool force) +{ + int ret = 0; + u32 fw_file_len = 0; + u8 *fw_file_buf = NULL; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("start upgrade with fw bin"); + if ((!upg) || (!upg->func) || !upg->ts_data) { + FTS_ERROR("upgrade/func/ts_data is null"); + return -EINVAL; + } + + upg->ts_data->fw_loading = 1; + fts_irq_disable(); +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(DISABLE); +#endif + + ret = fts_read_file(fw_name, &fw_file_buf); + if ((ret < 0) || (ret < FTS_MIN_LEN)) { + FTS_ERROR("read fw bin file(%s) fail, len:%d", fw_name, ret); + goto err_bin; + } + + fw_file_len = ret; + FTS_INFO("fw bin file len:%d", fw_file_len); + if (force) { + if (upg->func->force_upgrade) { + ret = upg->func->force_upgrade(fw_file_buf, fw_file_len); + } else { + FTS_INFO("force_upgrade function is null, no upgrade"); + goto err_bin; + } + } else { +#if FTS_AUTO_LIC_UPGRADE_EN + if (upg->func->lic_upgrade) { + ret = upg->func->lic_upgrade(fw_file_buf, fw_file_len); + } else { + FTS_INFO("lic_upgrade function is null, no upgrade"); + } +#endif + if (upg->func->upgrade) { + ret = upg->func->upgrade(fw_file_buf, fw_file_len); + } else { + FTS_INFO("upgrade function is null, no upgrade"); + } + } + + if (ret < 0) { + FTS_ERROR("upgrade fw bin failed"); + fts_fwupg_reset_in_boot(); + goto err_bin; + } + + FTS_INFO("upgrade fw bin success"); + ret = 0; + +err_bin: +#if FTS_ESDCHECK_EN + fts_esdcheck_switch(ENABLE); +#endif + fts_irq_enable(); + upg->ts_data->fw_loading = 0; + + if (fw_file_buf) { + vfree(fw_file_buf); + fw_file_buf = NULL; + } + 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 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 { + 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) { + FTS_ERROR("no module id match, don't get file"); + return -ENODATA; + } + } + + upg->module_info = info; + return 0; +} + +int fts_fw_update_vendor_name(const char* name) { + struct fts_upgrade *upg = fwupgrade; + char* pos; + int len; + + fts_fwupg_get_module_info(fwupgrade); + + if (!upg || !upg->module_info) { + FTS_ERROR("upgrade struct not init"); + return -EINVAL; + } + + len = strlen(name); + if (len > FILE_NAME_LENGTH) { + FTS_ERROR("vendor name is too long"); + return -EINVAL; + } + pos = strstr(name, ".bin"); + if (pos == NULL) + snprintf(upg->module_info->vendor_name, FILE_NAME_LENGTH, "%s", name); + else { + len = len - strlen(".bin"); + snprintf(upg->module_info->vendor_name, len + 1, "%s", name); + } + 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 }; + + FTS_FUNC_ENTER(); + if (!upg || !upg->ts_data || !upg->ts_data->dev) { + FTS_ERROR("upg/ts_data/dev is null"); + return -EINVAL; + } + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + if (fts_data->panel_supplier) { + FTS_INFO("fts_data->panel_supplier=%s\n", fts_data->panel_supplier); + snprintf(fwname, FILE_NAME_LENGTH, "%s%s.bin", \ + FTS_FW_NAME_PREX_WITH_REQUEST, \ + fts_data->panel_supplier); + } + else +#endif + 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 (0 == ret) { + 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) +{ + FTS_FUNC_ENTER(); + 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 FTS_FW_FILE_I_INCELL_CHECK + FTS_INFO("ret=%d, is_incell=%d",ret, upg->ts_data->ic_info.is_incell); + //only try get fw file i for non incell IC when load FW fail + if ((ret != 0) && upg->ts_data->ic_info.is_incell) { +#else + if (ret != 0) { +#endif + 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) { + FTS_ERROR("fw file len(%d) fail", upg->fw_length); + return -ENODATA; + } + + FTS_INFO("return ret:%d", ret); + 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); + } +} + +void fts_fwupg_bin(void) +{ + int ret = 0; + struct fts_upgrade *upg = fwupgrade; + + FTS_INFO("fw upgrade bin 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; +} + +/***************************************************************************** + * 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; + u16 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 < FTS_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; +} diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_flash.h b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_flash.h new file mode 100644 index 000000000000..d2aad3164004 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_flash.h @@ -0,0 +1,217 @@ +/************************************************************************ +* Copyright (c) 2012-2020, 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_DATA_LEN 0xB0 +#define FTS_CMD_APP_DATA_LEN_INCELL 0x7A +#define FTS_CMD_DATA_LEN_LEN 4 +#define FTS_CMD_SET_WFLASH_ADDR 0xAB +#define FTS_CMD_SET_RFLASH_ADDR 0xAC +#define FTS_LEN_SET_ADDR 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 (256 * 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 { + u16 ctype[FTS_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_ft5452i; + + +/***************************************************************************** +* 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_flash_read_buf(u32 saddr, u8 *buf, u32 len); +int fts_fwupg_upgrade(struct fts_upgrade *upg); +#endif diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_flash/Makefile b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_flash/Makefile new file mode 100644 index 000000000000..9e64c5446439 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_flash/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_TOUCHSCREEN_FTS) += focaltech_upgrade_ft5452i.o \ No newline at end of file diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_flash/focaltech_upgrade_ft3518.c b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_flash/focaltech_upgrade_ft3518.c new file mode 100644 index 000000000000..a7b0b5b61141 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_flash/focaltech_upgrade_ft3518.c @@ -0,0 +1,186 @@ +/* + * + * 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, +}; diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_flash/focaltech_upgrade_ft5452i.c b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_flash/focaltech_upgrade_ft5452i.c new file mode 100644 index 000000000000..b1e8538acab9 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_flash/focaltech_upgrade_ft5452i.c @@ -0,0 +1,194 @@ +/* + * + * FocalTech fts TouchScreen driver. + * + * Copyright (c) 2012-2020, 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" + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +u8 pb_file_ft5452i[] = { +#include "../include/pramboot/FT5452i_Pramboot_v4.1_20210427.h" +}; + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +/************************************************************************ +* Name: fts_ft5452_upgrade +* Brief: +* Input: +* Output: +* Return: return 0 if success, otherwise return error code +***********************************************************************/ +static int fts_ft5452i_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) || (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_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_ft5452i.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; + } + + 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_ft5452i = { + .ctype = {0x89}, + .fwveroff = 0x010E, + .fwcfgoff = 0x1FFB0, + .appoff = 0x0000, + .pramboot_supported = true, + .pramboot = pb_file_ft5452i, + .pb_length = sizeof(pb_file_ft5452i), + .pram_ecc_check_mode = ECC_CHECK_MODE_CRC16, + .new_return_value_from_ic = true, + .hid_supported = false, + .upgrade = fts_ft5452i_upgrade, +}; diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_gesture.c b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_gesture.c new file mode 100644 index 000000000000..2709a38bd0ae --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_gesture.c @@ -0,0 +1,460 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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_GESTURE_U; + 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; +} diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_i2c.c b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_i2c.c new file mode 100644 index 000000000000..de454e10a0e9 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_i2c.c @@ -0,0 +1,193 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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" + +/***************************************************************************** +* 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 +*****************************************************************************/ + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* functions body +*****************************************************************************/ +int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen) +{ + int ret = 0; + int i = 0; + struct fts_ts_data *ts_data = fts_data; + 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) { + FTS_ERROR("i2c_transfer(read) fail,ret:%d", ret); + } else { + memcpy(data, ts_data->bus_rx_buf, datalen); + break; + } + } + + mutex_unlock(&ts_data->bus_lock); + return ret; +} + +int fts_write(u8 *writebuf, u32 writelen) +{ + int ret = 0; + int i = 0; + struct fts_ts_data *ts_data = fts_data; + 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) { + FTS_ERROR("i2c_transfer(write) fail,ret:%d", ret); + } else { + break; + } + } + mutex_unlock(&ts_data->bus_lock); + 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) +{ + FTS_FUNC_ENTER(); + ts_data->bus_tx_buf = kzalloc(I2C_BUF_LENGTH, GFP_KERNEL); + if (NULL == ts_data->bus_tx_buf) { + FTS_ERROR("failed to allocate memory for bus_tx_buf"); + return -ENOMEM; + } + + ts_data->bus_rx_buf = kzalloc(I2C_BUF_LENGTH, GFP_KERNEL); + if (NULL == ts_data->bus_rx_buf) { + FTS_ERROR("failed to allocate memory for bus_rx_buf"); + return -ENOMEM; + } + FTS_FUNC_EXIT(); + return 0; +} + +int fts_bus_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; +} \ No newline at end of file diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_point_report_check.c b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_point_report_check.c new file mode 100644 index 000000000000..1d1ee513392c --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_point_report_check.c @@ -0,0 +1,129 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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 */ +#define PRC_INTR_INTERVALS 100 /* 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); + unsigned long cur_jiffies = jiffies; + unsigned long intr_timeout = msecs_to_jiffies(PRC_INTR_INTERVALS); + + intr_timeout += ts_data->intr_jiffies; + if (time_after(cur_jiffies, intr_timeout)) { + fts_release_all_finger(); + ts_data->prc_mode = 0; + //FTS_DEBUG("interval:%lu", (cur_jiffies - ts_data->intr_jiffies) * 1000 / HZ); + } else { + queue_delayed_work(ts_data->ts_workqueue, &ts_data->prc_work, + msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME)); + ts_data->prc_mode = 1; + } +} + +/***************************************************************************** +* 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) +{ + ts_data->intr_jiffies = jiffies; + if (!ts_data->prc_mode) { + queue_delayed_work(ts_data->ts_workqueue, &ts_data->prc_work, + msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME)); + ts_data->prc_mode = 1; + } +} + +/***************************************************************************** +* 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 */ + diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/Makefile b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/Makefile new file mode 100644 index 000000000000..d6b5cd01fb0c --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_TOUCHSCREEN_FT3519) += focaltech_test.o +obj-$(CONFIG_TOUCHSCREEN_FT3519) += focaltech_test_ini.o +obj-$(CONFIG_TOUCHSCREEN_FT3519) += supported_ic/ diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/focaltech_test.c b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/focaltech_test.c new file mode 100644 index 000000000000..404541f44e12 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/focaltech_test.c @@ -0,0 +1,2228 @@ +/* + * + * FocalTech TouchScreen driver. + * + * Copyright (c) 2012-2020, 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_test.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-01 +* +* Modify: +* +* Abstract: create char device and proc node for the comm between APK and TP +* +************************************************************************/ + +/***************************************************************************** +* Included header files +*****************************************************************************/ +#include "focaltech_test.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +struct fts_test *fts_ftest; + +struct test_funcs *test_func_list[] = { + &test_func_ft5452i, +}; + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ + +/***************************************************************************** +* functions body +*****************************************************************************/ +void sys_delay(int ms) +{ + msleep(ms); +} + +int fts_abs(int value) +{ + if (value < 0) + value = 0 - value; + + return value; +} + +void *fts_malloc(size_t size) +{ + return kzalloc(size, GFP_KERNEL); +} + +void fts_free_proc(void *p) +{ + return kfree(p); +} + +void print_buffer(int *buffer, int length, int line_num) +{ + int i = 0; + int j = 0; + int tmpline = 0; + char *tmpbuf = NULL; + int tmplen = 0; + int cnt = 0; + struct fts_test *tdata = fts_ftest; + + if (tdata && tdata->ts_data && (tdata->ts_data->log_level < 3)) { + return; + } + + if ((NULL == buffer) || (length <= 0)) { + FTS_TEST_DBG("buffer/length(%d) fail", length); + return; + } + + tmpline = line_num ? line_num : length; + tmplen = tmpline * 6 + 128; + tmpbuf = kzalloc(tmplen, GFP_KERNEL); + + for (i = 0; i < length; i = i + tmpline) { + cnt = 0; + for (j = 0; j < tmpline; j++) { + cnt += snprintf(tmpbuf + cnt, tmplen - cnt, "%5d ", buffer[i + j]); + if ((cnt >= tmplen) || ((i + j + 1) >= length)) + break; + } + FTS_TEST_DBG("%s", tmpbuf); + } + + if (tmpbuf) { + kfree(tmpbuf); + tmpbuf = NULL; + } +} + +/******************************************************************** + * test read/write interface + *******************************************************************/ +static int fts_test_bus_read( + u8 *cmd, int cmdlen, u8 *data, int datalen) +{ + int ret = 0; + + ret = fts_read(cmd, cmdlen, data, datalen); + if (ret < 0) + return ret; + else + return 0; +} + +static int fts_test_bus_write(u8 *writebuf, int writelen) +{ + int ret = 0; + + ret = fts_write(writebuf, writelen); + if (ret < 0) + return ret; + else + return 0; +} + +int fts_test_read_reg(u8 addr, u8 *val) +{ + return fts_test_bus_read(&addr, 1, val, 1); +} + +int fts_test_write_reg(u8 addr, u8 val) +{ + int ret; + u8 cmd[2] = {0}; + + cmd[0] = addr; + cmd[1] = val; + ret = fts_test_bus_write(cmd, 2); + + return ret; +} + +int fts_test_read(u8 addr, u8 *readbuf, int readlen) +{ + int ret = 0; + int i = 0; + int packet_length = 0; + int packet_num = 0; + int packet_remainder = 0; + int offset = 0; + int byte_num = readlen; + + packet_num = byte_num / BYTES_PER_TIME; + packet_remainder = byte_num % BYTES_PER_TIME; + if (packet_remainder) + packet_num++; + + if (byte_num < BYTES_PER_TIME) { + packet_length = byte_num; + } else { + packet_length = BYTES_PER_TIME; + } + /* FTS_TEST_DBG("packet num:%d, remainder:%d", packet_num, packet_remainder); */ + + ret = fts_test_bus_read(&addr, 1, &readbuf[offset], packet_length); + if (ret < 0) { + FTS_TEST_ERROR("read buffer fail"); + return ret; + } + for (i = 1; i < packet_num; i++) { + offset += packet_length; + if ((i == (packet_num - 1)) && packet_remainder) { + packet_length = packet_remainder; + } + + + ret = fts_test_bus_read(NULL, 0, &readbuf[offset], + packet_length); + if (ret < 0) { + FTS_TEST_ERROR("read buffer fail"); + return ret; + } + } + + return 0; +} + +int fts_test_write(u8 addr, u8 *writebuf, int writelen) +{ + int ret = 0; + int i = 0; + u8 *data = NULL; + int packet_length = 0; + int packet_num = 0; + int packet_remainder = 0; + int offset = 0; + int byte_num = writelen; + + data = fts_malloc(BYTES_PER_TIME + 1); + if (!data) { + FTS_TEST_ERROR("malloc memory for bus write data fail"); + return -ENOMEM; + } + + packet_num = byte_num / BYTES_PER_TIME; + packet_remainder = byte_num % BYTES_PER_TIME; + if (packet_remainder) + packet_num++; + + if (byte_num < BYTES_PER_TIME) { + packet_length = byte_num; + } else { + packet_length = BYTES_PER_TIME; + } + /* FTS_TEST_DBG("packet num:%d, remainder:%d", packet_num, packet_remainder); */ + + data[0] = addr; + for (i = 0; i < packet_num; i++) { + if (i != 0) { + data[0] = addr + 1; + } + if ((i == (packet_num - 1)) && packet_remainder) { + packet_length = packet_remainder; + } + memcpy(&data[1], &writebuf[offset], packet_length); + + ret = fts_test_bus_write(data, packet_length + 1); + if (ret < 0) { + FTS_TEST_ERROR("write buffer fail"); + fts_free(data); + return ret; + } + + offset += packet_length; + } + + fts_free(data); + return 0; +} + +/******************************************************************** + * test global function enter work/factory mode + *******************************************************************/ +int enter_work_mode(void) +{ + int ret = 0; + u8 mode = 0; + int i = 0; + int j = 0; + + FTS_TEST_FUNC_ENTER(); + + ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode); + if ((ret >= 0) && (0x00 == mode)) + return 0; + + for (i = 0; i < ENTER_WORK_FACTORY_RETRIES; i++) { + ret = fts_test_write_reg(DEVIDE_MODE_ADDR, 0x00); + if (ret >= 0) { + sys_delay(FACTORY_TEST_DELAY); + for (j = 0; j < 20; j++) { + ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode); + if ((ret >= 0) && (0x00 == mode)) { + FTS_TEST_INFO("enter work mode success"); + return 0; + } else + sys_delay(FACTORY_TEST_DELAY); + } + } + + sys_delay(50); + } + + if (i >= ENTER_WORK_FACTORY_RETRIES) { + FTS_TEST_ERROR("Enter work mode fail"); + return -EIO; + } + + FTS_TEST_FUNC_EXIT(); + return 0; +} + +int enter_factory_mode(void) +{ + int ret = 0; + u8 mode = 0; + int i = 0; + int j = 0; + + ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode); + if ((ret >= 0) && (0x40 == mode)) + return 0; + + for (i = 0; i < ENTER_WORK_FACTORY_RETRIES; i++) { + ret = fts_test_write_reg(DEVIDE_MODE_ADDR, 0x40); + if (ret >= 0) { + sys_delay(FACTORY_TEST_DELAY); + for (j = 0; j < 20; j++) { + ret = fts_test_read_reg(DEVIDE_MODE_ADDR, &mode); + if ((ret >= 0) && (0x40 == mode)) { + FTS_TEST_INFO("enter factory mode success"); + sys_delay(200); + return 0; + } else + sys_delay(FACTORY_TEST_DELAY); + } + } + + sys_delay(50); + } + + if (i >= ENTER_WORK_FACTORY_RETRIES) { + FTS_TEST_ERROR("Enter factory mode fail"); + return -EIO; + } + + return 0; +} + +/* + * read_mass_data - read rawdata/short test data + * addr - register addr which read data from + * byte_num - read data length, unit:byte + * buf - save data + * + * return 0 if read data succuss, otherwise return error code + */ +int read_mass_data(u8 addr, int byte_num, int *buf) +{ + int ret = 0; + int i = 0; + u8 *data = NULL; + + data = (u8 *)fts_malloc(byte_num * sizeof(u8)); + if (NULL == data) { + FTS_TEST_SAVE_ERR("mass data buffer malloc fail\n"); + return -ENOMEM; + } + + /* read rawdata buffer */ + FTS_TEST_INFO("mass data len:%d", byte_num); + ret = fts_test_read(addr, data, byte_num); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read mass data fail\n"); + goto read_massdata_err; + } + + for (i = 0; i < byte_num; i = i + 2) { + buf[i >> 1] = (int)(short)((data[i] << 8) + data[i + 1]); + } + + ret = 0; +read_massdata_err: + fts_free(data); + return ret; +} + +int read_mass_data_u16(u8 addr, int byte_num, int *buf) +{ + int ret = 0; + int i = 0; + u8 *data = NULL; + + data = (u8 *)fts_malloc(byte_num * sizeof(u8)); + if (NULL == data) { + FTS_TEST_SAVE_ERR("mass data buffer malloc fail\n"); + return -ENOMEM; + } + + /* read rawdata buffer */ + FTS_TEST_INFO("mass data len:%d", byte_num); + ret = fts_test_read(addr, data, byte_num); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read mass data fail\n"); + goto read_massdata_err; + } + + for (i = 0; i < byte_num; i = i + 2) { + buf[i >> 1] = (int)(u16)((data[i] << 8) + data[i + 1]); + } + + ret = 0; +read_massdata_err: + fts_free(data); + return ret; +} + +int short_get_adcdata_incell(u8 retval, u8 ch_num, int byte_num, int *adc_buf) +{ + int ret = 0; + int times = 0; + u8 short_state = 0; + + FTS_TEST_FUNC_ENTER(); + + /* Start ADC sample */ + ret = fts_test_write_reg(FACTORY_REG_SHORT_TEST_EN, 0x01); + if (ret) { + FTS_TEST_SAVE_ERR("start short test fail\n"); + goto adc_err; + } + + sys_delay(ch_num * FACTORY_TEST_DELAY); + for (times = 0; times < FACTORY_TEST_RETRY; times++) { + ret = fts_test_read_reg(FACTORY_REG_SHORT_TEST_STATE, &short_state); + if ((ret >= 0) && (retval == short_state)) + break; + else + FTS_TEST_DBG("reg%x=%x,retry:%d", + FACTORY_REG_SHORT_TEST_STATE, short_state, times); + + sys_delay(FACTORY_TEST_RETRY_DELAY); + } + if (times >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("short test timeout, ADC data not OK\n"); + ret = -EIO; + goto adc_err; + } + + ret = read_mass_data(FACTORY_REG_SHORT_ADDR, byte_num, adc_buf); + if (ret) { + FTS_TEST_SAVE_ERR("get short(adc) data fail\n"); + } + +adc_err: + FTS_TEST_FUNC_EXIT(); + return ret; +} + +/* + * wait_state_update - wait fw status update + */ +int wait_state_update(u8 retval) +{ + int ret = 0; + int times = 0; + u8 state = 0xFF; + + while (times++ < FACTORY_TEST_RETRY) { + sys_delay(FACTORY_TEST_DELAY); + /* Wait register status update */ + state = 0xFF; + ret = fts_test_read_reg(FACTORY_REG_PARAM_UPDATE_STATE, &state); + if ((ret >= 0) && (retval == state)) + break; + else + FTS_TEST_DBG("reg%x=%x,retry:%d", \ + FACTORY_REG_PARAM_UPDATE_STATE, state, times); + } + + if (times >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("Wait State Update fail\n"); + return -EIO; + } + + return 0; +} + +/* + * start_scan - start to scan a frame + */ +int start_scan(void) +{ + int ret = 0; + u8 addr = 0; + u8 val = 0; + u8 finish_val = 0; + int times = 0; + struct fts_test *tdata = fts_ftest; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("test/func is null\n"); + return -EINVAL; + } + + if (SCAN_SC == tdata->func->startscan_mode) { + /* sc ic */ + addr = FACTORY_REG_SCAN_ADDR2; + val = 0x01; + finish_val = 0x00; + } else { + addr = DEVIDE_MODE_ADDR; + val = 0xC0; + finish_val = 0x40; + } + + /* write register to start scan */ + ret = fts_test_write_reg(addr, val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write start scan mode fail\n"); + return ret; + } + + /* Wait for the scan to complete */ + while (times++ < FACTORY_TEST_RETRY) { + sys_delay(FACTORY_TEST_DELAY); + + ret = fts_test_read_reg(addr, &val); + if ((ret >= 0) && (val == finish_val)) { + break; + } else + FTS_TEST_DBG("reg%x=%x,retry:%d", addr, val, times); + } + + if (times >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("scan timeout\n"); + return -EIO; + } + + return 0; +} + +static int read_rawdata( + struct fts_test *tdata, + u8 off_addr, + u8 off_val, + u8 rawdata_addr, + int byte_num, + int *data) +{ + int ret = 0; + + /* set line addr or rawdata start addr */ + ret = fts_test_write_reg(off_addr, off_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("wirte line/start addr fail\n"); + return ret; + } + + if (tdata->func->raw_u16) + ret = read_mass_data_u16(rawdata_addr, byte_num, data); + else + ret = read_mass_data(rawdata_addr, byte_num, data); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read rawdata fail\n"); + return ret; + } + + return 0; +} + +int get_rawdata(int *data) +{ + int ret = 0; + u8 val = 0; + u8 addr = 0; + u8 rawdata_addr = 0; + int byte_num = 0; + struct fts_test *tdata = fts_ftest; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("test/func is null\n"); + return -EINVAL; + } + + /* enter factory mode */ + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("failed to enter factory mode,ret=%d\n", ret); + return ret; + } + + /* start scanning */ + ret = start_scan(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("scan fail\n"); + return ret; + } + + /* read rawdata */ + if (IC_HW_INCELL == tdata->func->hwtype) { + val = 0xAD; + addr = FACTORY_REG_LINE_ADDR; + rawdata_addr = FACTORY_REG_RAWDATA_ADDR; + } else if (IC_HW_MC_SC == tdata->func->hwtype) { + val = 0xAA; + addr = FACTORY_REG_LINE_ADDR; + rawdata_addr = FACTORY_REG_RAWDATA_ADDR_MC_SC; + } else { + val = 0x0; + addr = FACTORY_REG_RAWDATA_SADDR_SC; + rawdata_addr = FACTORY_REG_RAWDATA_ADDR_SC; + } + + byte_num = tdata->node.node_num * 2; + ret = read_rawdata(tdata, addr, val, rawdata_addr, byte_num, data); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read rawdata fail\n"); + return ret; + } + + return 0; +} + +/* + * chip_clb - auto clb + */ +int chip_clb(void) +{ + int ret = 0; + u8 val = 0; + int times = 0; + + /* start clb */ + ret = fts_test_write_reg(FACTORY_REG_CLB, 0x04); + if (ret) { + FTS_TEST_SAVE_ERR("write start clb fail\n"); + return ret; + } + + while (times++ < FACTORY_TEST_RETRY) { + sys_delay(FACTORY_TEST_RETRY_DELAY); + ret = fts_test_read_reg(FACTORY_REG_CLB, &val); + if ((0 == ret) && (0x02 == val)) { + /* clb ok */ + break; + } else + FTS_TEST_DBG("reg%x=%x,retry:%d", FACTORY_REG_CLB, val, times); + } + + if (times >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("chip clb timeout\n"); + return -EIO; + } + + return 0; +} + +/* + * get_cb_incell - get cb data for incell IC + */ +int get_cb_incell(u16 saddr, int byte_num, int *cb_buf) +{ + int ret = 0; + int i = 0; + u8 cb_addr = 0; + u8 addr_h = 0; + u8 addr_l = 0; + int read_num = 0; + int packet_num = 0; + int packet_remainder = 0; + int offset = 0; + int addr = 0; + u8 *data = NULL; + + data = (u8 *)fts_malloc(byte_num * sizeof(u8)); + if (NULL == data) { + FTS_TEST_SAVE_ERR("cb buffer malloc fail\n"); + return -ENOMEM; + } + + packet_num = byte_num / BYTES_PER_TIME; + packet_remainder = byte_num % BYTES_PER_TIME; + if (packet_remainder) + packet_num++; + read_num = BYTES_PER_TIME; + + FTS_TEST_INFO("cb packet:%d,remainder:%d", packet_num, packet_remainder); + cb_addr = FACTORY_REG_CB_ADDR; + for (i = 0; i < packet_num; i++) { + offset = read_num * i; + addr = saddr + offset; + addr_h = (addr >> 8) & 0xFF; + addr_l = addr & 0xFF; + if ((i == (packet_num - 1)) && packet_remainder) { + read_num = packet_remainder; + } + + ret = fts_test_write_reg(FACTORY_REG_CB_ADDR_H, addr_h); + if (ret) { + FTS_TEST_SAVE_ERR("write cb addr high fail\n"); + goto TEST_CB_ERR; + } + ret = fts_test_write_reg(FACTORY_REG_CB_ADDR_L, addr_l); + if (ret) { + FTS_TEST_SAVE_ERR("write cb addr low fail\n"); + goto TEST_CB_ERR; + } + + ret = fts_test_read(cb_addr, data + offset, read_num); + if (ret) { + FTS_TEST_SAVE_ERR("read cb fail\n"); + goto TEST_CB_ERR; + } + } + + for (i = 0; i < byte_num; i++) { + cb_buf[i] = data[i]; + } + +TEST_CB_ERR: + fts_free(data); + return ret; +} + +int get_cb_sc(int byte_num, int *cb_buf, enum byte_mode mode) +{ + int ret = 0; + int i = 0; + int read_num = 0; + int packet_num = 0; + int packet_remainder = 0; + int offset = 0; + u8 cb_addr = 0; + u8 off_addr = 0; + u8 off_h_addr = 0; + struct fts_test *tdata = fts_ftest; + u8 *cb = NULL; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("test/func is null\n"); + return -EINVAL; + } + + cb = (u8 *)fts_malloc(byte_num * sizeof(u8)); + if (NULL == cb) { + FTS_TEST_SAVE_ERR("malloc memory for cb buffer fail\n"); + return -ENOMEM; + } + + if (IC_HW_MC_SC == tdata->func->hwtype) { + cb_addr = FACTORY_REG_MC_SC_CB_ADDR; + off_addr = FACTORY_REG_MC_SC_CB_ADDR_OFF; + off_h_addr = FACTORY_REG_MC_SC_CB_H_ADDR_OFF; + } else if (IC_HW_SC == tdata->func->hwtype) { + cb_addr = FACTORY_REG_SC_CB_ADDR; + off_addr = FACTORY_REG_SC_CB_ADDR_OFF; + } + + packet_num = byte_num / BYTES_PER_TIME; + packet_remainder = byte_num % BYTES_PER_TIME; + if (packet_remainder) + packet_num++; + read_num = BYTES_PER_TIME; + offset = 0; + + FTS_TEST_INFO("cb packet:%d,remainder:%d", packet_num, packet_remainder); + for (i = 0; i < packet_num; i++) { + if ((i == (packet_num - 1)) && packet_remainder) { + read_num = packet_remainder; + } + + ret = fts_test_write_reg(off_addr, offset); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write cb addr offset fail\n"); + goto cb_err; + } + + if (tdata->func->cb_high_support) { + ret = fts_test_write_reg(off_h_addr, offset >> 8); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write cb_h addr offset fail\n"); + goto cb_err; + } + } + + ret = fts_test_read(cb_addr, cb + offset, read_num); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read cb fail\n"); + goto cb_err; + } + + offset += read_num; + } + + if (DATA_ONE_BYTE == mode) { + for (i = 0; i < byte_num; i++) { + cb_buf[i] = cb[i]; + } + } else if (DATA_TWO_BYTE == mode) { + for (i = 0; i < byte_num; i = i + 2) { + cb_buf[i >> 1] = (int)(((int)(cb[i]) << 8) + cb[i + 1]); + } + } + + ret = 0; +cb_err: + fts_free(cb); + return ret; +} + +bool compare_data(int *data, int min, int max, int min_vk, int max_vk, bool key) +{ + int i = 0; + bool result = true; + struct fts_test *tdata = fts_ftest; + int rx = tdata->node.rx_num; + int node_va = tdata->node.node_num - tdata->node.key_num; + + if (!data || !tdata->node_valid) { + FTS_TEST_SAVE_ERR("data/node_valid is null\n"); + return false; + } + + for (i = 0; i < node_va; i++) { + if (0 == tdata->node_valid[i]) + continue; + + if ((data[i] < min) || (data[i] > max)) { + FTS_TEST_SAVE_ERR("test fail,node(%4d,%4d)=%5d,range=(%5d,%5d)\n", + i / rx + 1, i % rx + 1, data[i], min, max); + result = false; + } + } + + if (key) { + for (i = node_va; i < tdata->node.node_num; i++) { + if (0 == tdata->node_valid[i]) + continue; + + if ((data[i] < min_vk) || (data[i] > max_vk)) { + FTS_TEST_SAVE_ERR("test fail,node(%4d,%4d)=%5d,range=(%5d,%5d)\n", + i / rx + 1, i % rx + 1, + data[i], min_vk, max_vk); + result = false; + } + } + } + + return result; +} + +bool compare_array(int *data, int *min, int *max, bool key) +{ + int i = 0; + bool result = true; + struct fts_test *tdata = fts_ftest; + int rx = tdata->node.rx_num; + int node_num = tdata->node.node_num; + + if (!data || !min || !max || !tdata->node_valid) { + FTS_TEST_SAVE_ERR("data/min/max/node_valid is null\n"); + return false; + } + + if (!key) { + node_num -= tdata->node.key_num; + } + for (i = 0; i < node_num; i++) { + if (0 == tdata->node_valid[i]) + continue; + + if ((data[i] < min[i]) || (data[i] > max[i])) { + FTS_TEST_SAVE_ERR("test fail,node(%4d,%4d)=%5d,range=(%5d,%5d)\n", + i / rx + 1, i % rx + 1, data[i], min[i], max[i]); + result = false; + } + } + + return result; +} + +/* + * show_data - show and save test data to testresult.txt + */ +void show_data(int *data, bool key) +{ +#if TXT_SUPPORT + int i = 0; + int j = 0; + struct fts_test *tdata = fts_ftest; + int node_num = tdata->node.node_num; + int tx_num = tdata->node.tx_num; + int rx_num = tdata->node.rx_num; + + FTS_TEST_FUNC_ENTER(); + for (i = 0; i < tx_num; i++) { + FTS_TEST_SAVE_INFO("Ch/Tx_%02d: ", i + 1); + for (j = 0; j < rx_num; j++) { + FTS_TEST_SAVE_INFO("%5d, ", data[i * rx_num + j]); + } + FTS_TEST_SAVE_INFO("\n"); + } + + if (key) { + FTS_TEST_SAVE_INFO("Ch/Tx_%02d: ", tx_num + 1); + for (i = tx_num * rx_num; i < node_num; i++) { + FTS_TEST_SAVE_INFO("%5d, ", data[i]); + } + FTS_TEST_SAVE_INFO("\n"); + } + FTS_TEST_FUNC_EXIT(); +#endif +} + +/* mc_sc only */ +/* Only V3 Pattern has mapping & no-mapping */ +int mapping_switch(u8 mapping) +{ + int ret = 0; + u8 val = 0xFF; + struct fts_test *tdata = fts_ftest; + + if (tdata->v3_pattern) { + ret = fts_test_read_reg(FACTORY_REG_NOMAPPING, &val); + if (ret < 0) { + FTS_TEST_ERROR("read 0x54 register fail"); + return ret; + } + + if (val != mapping) { + ret = fts_test_write_reg(FACTORY_REG_NOMAPPING, mapping); + if (ret < 0) { + FTS_TEST_ERROR("write 0x54 register fail"); + return ret; + } + sys_delay(FACTORY_TEST_DELAY); + } + } + + return 0; +} + +bool get_fw_wp(u8 wp_ch_sel, enum wp_type water_proof_type) +{ + bool fw_wp_state = false; + + switch (water_proof_type) { + case WATER_PROOF_ON: + /* bit5: 0-check in wp on, 1-not check */ + fw_wp_state = !(wp_ch_sel & 0x20); + break; + case WATER_PROOF_ON_TX: + /* Bit6: 0-check Rx+Tx in wp mode 1-check one channel + Bit2: 0-check Tx in wp mode; 1-check Rx in wp mode + */ + fw_wp_state = (!(wp_ch_sel & 0x40) || !(wp_ch_sel & 0x04)); + break; + case WATER_PROOF_ON_RX: + fw_wp_state = (!(wp_ch_sel & 0x40) || (wp_ch_sel & 0x04)); + break; + case WATER_PROOF_OFF: + /* bit7: 0-check in wp off, 1-not check */ + fw_wp_state = !(wp_ch_sel & 0x80); + break; + case WATER_PROOF_OFF_TX: + /* Bit1-0: 00-check Tx in non-wp mode + 01-check Rx in non-wp mode + 10:check Rx+Tx in non-wp mode + */ + fw_wp_state = ((0x0 == (wp_ch_sel & 0x03)) || (0x02 == (wp_ch_sel & 0x03))); + break; + case WATER_PROOF_OFF_RX: + fw_wp_state = ((0x01 == (wp_ch_sel & 0x03)) || (0x02 == (wp_ch_sel & 0x03))); + break; + default: + break; + } + + return fw_wp_state; +} + +int get_cb_mc_sc(u8 wp, int byte_num, int *cb_buf, enum byte_mode mode) +{ + int ret = 0; + + /* 1:waterproof 0:non-waterproof */ + ret = fts_test_write_reg(FACTORY_REG_MC_SC_MODE, wp); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get mc_sc mode fail\n"); + return ret; + } + + /* read cb */ + ret = get_cb_sc(byte_num, cb_buf, mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get sc cb fail\n"); + return ret; + } + + return 0; +} + +int get_rawdata_mc_sc(enum wp_type wp, int *data) +{ + int ret = 0; + u8 val = 0; + u8 addr = 0; + u8 rawdata_addr = 0; + int byte_num = 0; + struct fts_test *tdata = fts_ftest; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("test/func is null\n"); + return -EINVAL; + } + + byte_num = tdata->sc_node.node_num * 2; + addr = FACTORY_REG_LINE_ADDR; + rawdata_addr = FACTORY_REG_RAWDATA_ADDR_MC_SC; + if (WATER_PROOF_ON == wp) { + val = 0xAC; + } else if (WATER_PROOF_OFF == wp) { + val = 0xAB; + } else if (HIGH_SENSITIVITY == wp) { + val = 0xA0; + } else if (HOV == wp) { + val = 0xA1; + byte_num = 4 * 2; + } + + ret = read_rawdata(tdata, addr, val, rawdata_addr, byte_num, data); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read rawdata fail\n"); + return ret; + } + + return 0; +} + +int get_rawdata_mc(u8 fre, u8 fir, int *rawdata) +{ + int ret = 0; + int i = 0; + + if (NULL == rawdata ) { + FTS_TEST_SAVE_ERR("rawdata buffer is null\n"); + return -EINVAL; + } + + /* set frequecy high/low */ + ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, fre); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set frequecy fail,ret=%d\n", ret); + return ret; + } + + /* fir enable/disable */ + ret = fts_test_write_reg(FACTORY_REG_FIR, fir); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set fir fail,ret=%d\n", ret); + return ret; + } + + /* get rawdata */ + for (i = 0; i < 3; i++) { + /* lost 3 frames, in order to obtain stable data */ + ret = get_rawdata(rawdata); + } + if (ret < 0) { + FTS_TEST_SAVE_ERR("get rawdata fail,ret=%d\n", ret); + return ret; + } + + return 0; +} + +int short_get_adc_data_mc(u8 retval, int byte_num, int *adc_buf, u8 mode) +{ + int ret = 0; + int i = 0; + u8 short_state = 0; + u8 short_state_reg = 0; + u8 short_en_reg = 0; + u8 short_data_reg = 0; + struct fts_test *tdata = fts_ftest; + + FTS_TEST_FUNC_ENTER(); + if (tdata->func->mc_sc_short_v2) { + short_en_reg = FACTROY_REG_SHORT2_TEST_EN; + short_state_reg = FACTROY_REG_SHORT2_TEST_STATE; + short_data_reg = FACTORY_REG_SHORT2_ADDR_MC; + } else { + short_en_reg = FACTROY_REG_SHORT_TEST_EN; + short_state_reg = FACTROY_REG_SHORT_TEST_EN; + short_data_reg = FACTORY_REG_SHORT_ADDR_MC; + } + + /* select short test mode & start test */ + ret = fts_test_write_reg(short_en_reg, mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write short test mode fail\n"); + goto test_err; + } + + for (i = 0; i < FACTORY_TEST_RETRY; i++) { + sys_delay(FACTORY_TEST_RETRY_DELAY); + + ret = fts_test_read_reg(short_state_reg, &short_state); + if ((ret >= 0) && (retval == short_state)) + break; + else + FTS_TEST_DBG("reg%x=%x,retry:%d", short_state_reg, short_state, i); + } + if (i >= FACTORY_TEST_RETRY) { + FTS_TEST_SAVE_ERR("short test timeout, ADC data not OK\n"); + ret = -EIO; + goto test_err; + } + + ret = read_mass_data(short_data_reg, byte_num, adc_buf); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get short(adc) data fail\n"); + } + + FTS_TEST_DBG("adc data:\n"); + print_buffer(adc_buf, byte_num / 2, 0); +test_err: + FTS_TEST_FUNC_EXIT(); + return ret; +} + +bool compare_mc_sc(bool tx_check, bool rx_check, int *data, int *min, int *max) +{ + int i = 0; + bool result = true; + struct fts_test *tdata = fts_ftest; + + if (rx_check) { + for (i = 0; i < tdata->sc_node.rx_num; i++) { + if (0 == tdata->node_valid_sc[i]) + continue; + + if ((data[i] < min[i]) || (data[i] > max[i])) { + FTS_TEST_SAVE_ERR("test fail,rx%d=%5d,range=(%5d,%5d)\n", + i + 1, data[i], min[i], max[i]); + result = false; + } + } + } + + if (tx_check) { + for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) { + if (0 == tdata->node_valid_sc[i]) + continue; + + if ((data[i] < min[i]) || (data[i] > max[i])) { + FTS_TEST_SAVE_INFO("test fail,tx%d=%5d,range=(%5d,%5d)\n", + i - tdata->sc_node.rx_num + 1, + data[i], min[i], max[i]); + result = false; + } + } + } + + return result; +} + +void show_data_mc_sc(int *data) +{ + int i = 0; + struct fts_test *tdata = fts_ftest; + + FTS_TEST_SAVE_INFO("SCap Rx: "); + for (i = 0; i < tdata->sc_node.rx_num; i++) { + FTS_TEST_SAVE_INFO( "%5d, ", data[i]); + } + FTS_TEST_SAVE_INFO("\n"); + + FTS_TEST_SAVE_INFO("SCap Tx: "); + for (i = tdata->sc_node.rx_num; i < tdata->sc_node.node_num; i++) { + FTS_TEST_SAVE_INFO( "%5d, ", data[i]); + } + FTS_TEST_SAVE_INFO("\n"); +} +/* mc_sc end*/ + +#if CSV_SUPPORT || TXT_SUPPORT +static int fts_test_save_test_data(char *file_name, char *data_buf, int len) +{ + struct file *pfile = NULL; + char filepath[FILE_NAME_LENGTH] = { 0 }; + loff_t pos; + mm_segment_t old_fs; + + FTS_TEST_FUNC_ENTER(); + memset(filepath, 0, sizeof(filepath)); + snprintf(filepath, FILE_NAME_LENGTH, "%s%s", FTS_INI_FILE_PATH, file_name); + FTS_INFO("save test data to %s", filepath); + if (NULL == pfile) { + pfile = filp_open(filepath, O_TRUNC | O_CREAT | O_RDWR, 0); + } + if (IS_ERR(pfile)) { + FTS_TEST_ERROR("error occured while opening file %s.", filepath); + return -EIO; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + vfs_write(pfile, data_buf, len, &pos); + filp_close(pfile, NULL); + set_fs(old_fs); + + FTS_TEST_FUNC_EXIT(); + return 0; +} + +#if defined(TEST_SAVE_FAIL_RESULT) && TEST_SAVE_FAIL_RESULT +void fts_test_save_fail_result( + struct fts_test *tdata, char *prefix, char *suffix, char *buf, int len) +{ + char file_name[128]; + + if (false == tdata->result) { + snprintf(file_name, 128, "%s_%ld_%ld%s", prefix, + (long)tdata->tv.tv_sec, (long)tdata->tv.tv_usec, suffix); + fts_test_save_test_data(file_name, buf, len); + } +} +#endif +#endif + +static int fts_test_malloc_free_data_txt(struct fts_test *tdata, bool allocate) +{ +#if TXT_SUPPORT + if (true == allocate) { + tdata->testresult = vmalloc(TXT_BUFFER_LEN); + if (NULL == tdata->testresult) { + FTS_TEST_ERROR("tdata->testresult malloc fail\n"); + return -ENOMEM; + } + + tdata->testresult_len = 0; + FTS_TEST_SAVE_INFO("FW version:0x%02x\n", tdata->fw_ver); + FTS_TEST_SAVE_INFO("tx_num:%d, rx_num:%d, key_num:%d\n", + tdata->node.tx_num, tdata->node.rx_num, + tdata->node.key_num); + } else { + if (tdata->testresult) { + vfree(tdata->testresult); + tdata->testresult = NULL; + } + } +#endif + return 0; +} + +#if CSV_SUPPORT +static int fts_test_get_item_count_scap_csv(int index) +{ + int ret = 0; + int i = 0; + int select = 0; + u8 wc_sel = 0; + u8 hc_sel = 0; + u8 scap_select[4] = { 0 }; + + /* get waterproof channel select */ + ret = fts_test_read_reg(FACTORY_REG_WC_SEL, &wc_sel); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read water_channel_sel fail,ret=%d\n", ret); + return index; + } + + ret = fts_test_read_reg(FACTORY_REG_HC_SEL, &hc_sel); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read high_channel_sel fail,ret=%d\n", ret); + return index; + } + + scap_select[0] = get_fw_wp(wc_sel, WATER_PROOF_ON); + scap_select[1] = get_fw_wp(wc_sel, WATER_PROOF_OFF); + scap_select[2] = (hc_sel & 0x03) ? 1 : 0; + scap_select[3] = (hc_sel & 0x04) ? 1 : 0; + + for (i = 0; i < 4; i++) { + if (scap_select[i]) + select++; + if (select == index) + break; + } + + return (i + 1); +} +#endif + +static void fts_test_save_data_csv(struct fts_test *tdata) +{ +#if CSV_SUPPORT + int i = 0; + int j = 0; + int index = 0; + int k = 0; + int tx = 0; + int rx = 0; + int node_num = 0; + int offset = 0; + int start_line = 11; + int data_count = 0; + char *csv_buffer = NULL; + char *line2_buffer = NULL; + int csv_length = 0; + int line2_length = 0; + int csv_item_count = 0; + struct fts_test_data *td = &tdata->testdata; + struct item_info *info = NULL; + + FTS_TEST_INFO("save data in csv format"); + csv_buffer = vmalloc(CSV_BUFFER_LEN); + if (!csv_buffer) { + FTS_TEST_ERROR("csv_buffer malloc fail\n"); + return ; + } + + line2_buffer = vmalloc(CSV_LINE2_BUFFER_LEN); + if (!line2_buffer) { + FTS_TEST_ERROR("line2_buffer malloc fail\n"); + goto csv_save_err; + } + + FTS_TEST_INFO("test item count:%d", td->item_count); + /* line 1 */ + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "ECC, 85, 170, IC Name, %s, IC Code, %x\n", \ + tdata->ini.ic_name, \ + (tdata->ini.ic_code >> IC_CODE_OFFSET)); + + /* line 2 */ + for (i = 0; i < td->item_count; i++) { + info = &td->info[i]; + if (info->mc_sc) { + node_num = tdata->sc_node.node_num; + /* set max len of tx/rx to column */ + rx = (tdata->sc_node.tx_num > tdata->sc_node.rx_num) + ? tdata->sc_node.tx_num : tdata->sc_node.rx_num; + } else { + if (info->key_support && (tdata->node.key_num > 0)) + node_num = (tdata->node.tx_num + 1) * tdata->node.rx_num; + else + node_num = tdata->node.tx_num * tdata->node.rx_num; + rx = tdata->node.rx_num; + } + + if (info->datalen > node_num) { + data_count = (info->datalen - 1 ) / node_num + 1; + tx = (node_num - 1 ) / rx + 1; + } else { + data_count = 1; + tx = ((info->datalen - 1) / rx) + 1; + } + + for (j = 1; j <= data_count; j++) { + index = j; + + if (tdata->func->hwtype == IC_HW_MC_SC) { + /*MC_SC, rawdata index will be 2*/ + if ((info->code == CODE_M_RAWDATA_TEST) && (data_count == 1)) { + index = 2; + } + + /*MC_SC, SCAP index will be 1~4*/ + if ((info->code == CODE_M_SCAP_CB_TEST) + || (info->code == CODE_M_SCAP_RAWDATA_TEST)) { + index = fts_test_get_item_count_scap_csv(j); + } + } + + line2_length += snprintf(line2_buffer + line2_length, \ + CSV_LINE2_BUFFER_LEN - line2_length, \ + "%s, %d, %d, %d, %d, %d, ", \ + info->name, info->code, tx, rx, + start_line, index); + start_line += tx; + csv_item_count++; + } + } + + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "TestItem Num, %d, ", \ + csv_item_count); + + if (line2_length > 0) { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "%s", line2_buffer); + } + + /* line 3 ~ 10 "\n" */ + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "\n\n\n\n\n\n\n\n\n"); + + /* line 11 ~ data area */ + for (i = 0; i < td->item_count; i++) { + info = &td->info[i]; + if (!info->data) { + FTS_TEST_ERROR("test item data is null"); + goto csv_save_err; + } + + if (info->mc_sc) { + offset = 0; + for (j = 0; j < info->datalen;) { + for (k = 0; k < tdata->sc_node.node_num; k++) { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "%d, ", info->data[offset + k]); + if ((k + 1) == tdata->sc_node.rx_num) { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "\n"); + } + } + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "\n"); + offset += k; + j += k; + } + } else { + for (j = 0; j < info->datalen; j++) { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "%d, ", info->data[j]); + if (((j + 1) % tdata->node.rx_num) == 0) { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, + "\n"); + } + } + } + } + FTS_TEST_INFO("csv length:%d", csv_length); + fts_test_save_test_data(FTS_CSV_FILE_NAME, csv_buffer, csv_length); + +#if defined(TEST_SAVE_FAIL_RESULT) && TEST_SAVE_FAIL_RESULT + fts_test_save_fail_result(tdata, "testdata_fail", ".csv", + csv_buffer, csv_length); +#endif + + +csv_save_err: + if (line2_buffer) { + vfree(line2_buffer); + line2_buffer = NULL; + } + + if (csv_buffer) { + vfree(csv_buffer); + csv_buffer = NULL; + } +#endif +} + +static void fts_test_save_result_txt(struct fts_test *tdata) +{ +#if TXT_SUPPORT + if (!tdata || !tdata->testresult) { + FTS_TEST_ERROR("test result is null"); + return; + } + + FTS_TEST_INFO("test result length in txt:%d", tdata->testresult_len); + fts_test_save_test_data(FTS_TXT_FILE_NAME, tdata->testresult, + tdata->testresult_len); + +#if defined(TEST_SAVE_FAIL_RESULT) && TEST_SAVE_FAIL_RESULT + fts_test_save_fail_result(tdata, "testresult_fail", ".txt", + tdata->testresult, tdata->testresult_len); +#endif + +#endif +} + +/***************************************************************************** +* Name: fts_test_save_data +* Brief: Save test data. +* If multi-data of MC, length of data package must be tx*rx,(tx+1)*rx +* If multi-data of MC-SC, length of data package should be (tx+rx)*2 +* Need fill 0 when no actual data +* Input: +* Output: +* Return: +*****************************************************************************/ +void fts_test_save_data(char *name, int code, int *data, int datacnt, + bool mc_sc, bool key, bool result) +{ + int datalen = datacnt; + struct fts_test *tdata = fts_ftest; + struct fts_test_data *td = &tdata->testdata; + struct item_info *info = &td->info[td->item_count]; + + if (!name || !data) { + FTS_TEST_ERROR("name/data is null"); + return ; + } + + strlcpy(info->name, name, TEST_ITEM_NAME_MAX - 1); + info->code = code; + info->mc_sc = mc_sc; + info->key_support = key; + info->result = result; + if (datalen <= 0) { + if (mc_sc) { + datalen = tdata->sc_node.node_num * 2; + } else { + if (key && (tdata->node.key_num > 0)) + datalen = (tdata->node.tx_num + 1) * tdata->node.rx_num; + else + datalen = tdata->node.tx_num * tdata->node.rx_num; + + } + } + + FTS_TEST_DBG("name:%s,len:%d", name, datalen); + info->data = fts_malloc(datalen * sizeof(int)); + if (!info->data) { + FTS_TEST_ERROR("malloc memory for item(%d) data fail", td->item_count); + info->datalen = 0; + return ; + } + memcpy(info->data, data, datalen * sizeof(int)); + info->datalen = datalen; + + td->item_count++; +} + +static void fts_test_free_data(struct fts_test *tdata) +{ + int i = 0; + struct fts_test_data *td = &tdata->testdata; + + for (i = 0; i < td->item_count; i++) { + if (td->info[i].data) { + fts_free(td->info[i].data); + } + } +} + +static int fts_test_malloc_free_incell(struct fts_test *tdata, bool allocate) +{ + struct incell_threshold *thr = &tdata->ic.incell.thr; + int buflen = tdata->node.node_num * sizeof(int); + + if (true == allocate) { + FTS_TEST_INFO("buflen:%d", buflen); + fts_malloc_r(thr->rawdata_min, buflen); + fts_malloc_r(thr->rawdata_max, buflen); + if (tdata->func->rawdata2_support) { + fts_malloc_r(thr->rawdata2_min, buflen); + fts_malloc_r(thr->rawdata2_max, buflen); + } + fts_malloc_r(thr->cb_min, buflen); + fts_malloc_r(thr->cb_max, buflen); + } else { + fts_free(thr->rawdata_min); + fts_free(thr->rawdata_max); + if (tdata->func->rawdata2_support) { + fts_free(thr->rawdata2_min); + fts_free(thr->rawdata2_max); + } + fts_free(thr->cb_min); + fts_free(thr->cb_max); + } + + return 0; +} + +static int fts_test_malloc_free_mc_sc(struct fts_test *tdata, bool allocate) +{ + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + int buflen = tdata->node.node_num * sizeof(int); + int buflen_sc = tdata->sc_node.node_num * sizeof(int); + + if (true == allocate) { + fts_malloc_r(thr->rawdata_h_min, buflen); + fts_malloc_r(thr->rawdata_h_max, buflen); + if (tdata->func->rawdata2_support) { + fts_malloc_r(thr->rawdata_l_min, buflen); + fts_malloc_r(thr->rawdata_l_max, buflen); + } + fts_malloc_r(thr->tx_linearity_max, buflen); + fts_malloc_r(thr->tx_linearity_min, buflen); + fts_malloc_r(thr->rx_linearity_max, buflen); + fts_malloc_r(thr->rx_linearity_min, buflen); + + fts_malloc_r(thr->scap_cb_off_min, buflen_sc); + fts_malloc_r(thr->scap_cb_off_max, buflen_sc); + fts_malloc_r(thr->scap_cb_on_min, buflen_sc); + fts_malloc_r(thr->scap_cb_on_max, buflen_sc); + fts_malloc_r(thr->scap_cb_hi_min, buflen_sc); + fts_malloc_r(thr->scap_cb_hi_max, buflen_sc); + fts_malloc_r(thr->scap_cb_hov_min, buflen_sc); + fts_malloc_r(thr->scap_cb_hov_max, buflen_sc); + + fts_malloc_r(thr->scap_rawdata_off_min, buflen_sc); + fts_malloc_r(thr->scap_rawdata_off_max, buflen_sc); + fts_malloc_r(thr->scap_rawdata_on_min, buflen_sc); + fts_malloc_r(thr->scap_rawdata_on_max, buflen_sc); + fts_malloc_r(thr->scap_rawdata_hi_min, buflen_sc); + fts_malloc_r(thr->scap_rawdata_hi_max, buflen_sc); + fts_malloc_r(thr->scap_rawdata_hov_min, buflen_sc); + fts_malloc_r(thr->scap_rawdata_hov_max, buflen_sc); + + fts_malloc_r(thr->panel_differ_min, buflen); + fts_malloc_r(thr->panel_differ_max, buflen); + } else { + fts_free(thr->rawdata_h_min); + fts_free(thr->rawdata_h_max); + if (tdata->func->rawdata2_support) { + fts_free(thr->rawdata_l_min); + fts_free(thr->rawdata_l_max); + } + fts_free(thr->tx_linearity_max); + fts_free(thr->tx_linearity_min); + fts_free(thr->rx_linearity_max); + fts_free(thr->rx_linearity_min); + + fts_free(thr->scap_cb_off_min); + fts_free(thr->scap_cb_off_max); + fts_free(thr->scap_cb_on_min); + fts_free(thr->scap_cb_on_max); + fts_free(thr->scap_cb_hi_min); + fts_free(thr->scap_cb_hi_max); + fts_free(thr->scap_cb_hov_min); + fts_free(thr->scap_cb_hov_max); + + fts_free(thr->scap_rawdata_off_min); + fts_free(thr->scap_rawdata_off_max); + fts_free(thr->scap_rawdata_on_min); + fts_free(thr->scap_rawdata_on_max); + fts_free(thr->scap_rawdata_hi_min); + fts_free(thr->scap_rawdata_hi_max); + fts_free(thr->scap_rawdata_hov_min); + fts_free(thr->scap_rawdata_hov_max); + + fts_free(thr->panel_differ_min); + fts_free(thr->panel_differ_max); + } + + return 0; +} + +static int fts_test_malloc_free_sc(struct fts_test *tdata, bool allocate) +{ + struct sc_threshold *thr = &tdata->ic.sc.thr; + int buflen = tdata->node.node_num * sizeof(int); + + if (true == allocate) { + fts_malloc_r(thr->rawdata_min, buflen); + fts_malloc_r(thr->rawdata_max, buflen); + fts_malloc_r(thr->cb_min, buflen); + fts_malloc_r(thr->cb_max, buflen); + fts_malloc_r(thr->dcb_sort, buflen); + fts_malloc_r(thr->dcb_base, buflen); + } else { + fts_free(thr->rawdata_min); + fts_free(thr->rawdata_max); + fts_free(thr->cb_min); + fts_free(thr->cb_max); + fts_free(thr->dcb_sort); + fts_free(thr->dcb_base); + } + + return 0; +} + +static int fts_test_malloc_free_thr(struct fts_test *tdata, bool allocate) +{ + int ret = 0; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("tdata/func is NULL\n"); + return -EINVAL; + } + + if (true == allocate) { + fts_malloc_r(tdata->node_valid, tdata->node.node_num * sizeof(int)); + fts_malloc_r(tdata->node_valid_sc, tdata->sc_node.node_num * sizeof(int)); + } else { + fts_free(tdata->node_valid); + fts_free(tdata->node_valid_sc); + } + + switch (tdata->func->hwtype) { + case IC_HW_INCELL: + ret = fts_test_malloc_free_incell(tdata, allocate); + break; + case IC_HW_MC_SC: + ret = fts_test_malloc_free_mc_sc(tdata, allocate); + break; + case IC_HW_SC: + ret = fts_test_malloc_free_sc(tdata, allocate); + break; + default: + FTS_TEST_SAVE_ERR("test ic type(%d) fail\n", tdata->func->hwtype); + ret = -EINVAL; + break; + } + + return ret; +} + +/* default enable all test item */ +static void fts_test_init_item(struct fts_test *tdata) +{ + switch (tdata->func->hwtype) { + case IC_HW_INCELL: + tdata->ic.incell.u.tmp = 0xFFFFFFFF; + break; + case IC_HW_MC_SC: + tdata->ic.mc_sc.u.tmp = 0xFFFFFFFF; + break; + case IC_HW_SC: + tdata->ic.sc.u.tmp = 0xFFFFFFFF; + break; + } +} + +static int get_tx_rx_num(u8 tx_rx_reg, u8 *ch_num, u8 ch_num_max) +{ + int ret = 0; + int i = 0; + + for (i = 0; i < 3; i++) { + ret = fts_test_read_reg(tx_rx_reg, ch_num); + if ((ret < 0) || (*ch_num > ch_num_max)) { + sys_delay(50); + } else + break; + } + + if (i >= 3) { + FTS_TEST_ERROR("get channel num fail"); + return -EIO; + } + + return 0; +} +static int get_key_num(int *key_num_en, int max_key_num) +{ + int ret = 0; + u8 key_en = 0; + + if (!max_key_num) { + FTS_TEST_DBG("not support key, don't read key num register"); + return 0; + } + + ret = fts_test_read_reg(FACTORY_REG_LEFT_KEY, &key_en); + if (ret >= 0) { + if (key_en & 0x01) { + (*key_num_en)++; + } + + if (key_en & 0x02) { + (*key_num_en)++; + } + + if (key_en & 0x04) { + (*key_num_en)++; + } + } + + ret = fts_test_read_reg(FACTORY_REG_RIGHT_KEY, &key_en); + if (ret >= 0) { + if (key_en & 0x01) { + (*key_num_en)++; + } + + if (key_en & 0x02) { + (*key_num_en)++; + } + + if (key_en & 0x04) { + (*key_num_en)++; + } + } + + if (*key_num_en > max_key_num) { + FTS_TEST_ERROR("get key num, fw:%d > max:%d", *key_num_en, max_key_num); + return -EIO; + } + + return ret; +} + +static int get_channel_num(struct fts_test *tdata) +{ + int ret = 0; + u8 tx_num = 0; + u8 rx_num = 0; + int key_num = 0; + + /* node structure */ + if (IC_HW_SC == tdata->func->hwtype) { + ret = get_tx_rx_num(FACTORY_REG_CH_NUM_SC, &tx_num, NUM_MAX_SC); + if (ret < 0) { + FTS_TEST_ERROR("get channel number fail"); + return ret; + } + + ret = get_tx_rx_num(FACTORY_REG_KEY_NUM_SC, &rx_num, KEY_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get key number fail"); + return ret; + } + + tdata->node.tx_num = 1; + tdata->node.rx_num = tx_num; + tdata->node.channel_num = tx_num; + tdata->node.node_num = tx_num; + key_num = rx_num; + } else { + ret = get_tx_rx_num(FACTORY_REG_CHX_NUM, &tx_num, TX_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get tx_num fail"); + return ret; + } + + ret = get_tx_rx_num(FACTORY_REG_CHY_NUM, &rx_num, RX_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get rx_num fail"); + return ret; + } + + if (IC_HW_INCELL == tdata->func->hwtype) { + ret = get_key_num(&key_num, tdata->func->key_num_total); + if (ret < 0) { + FTS_TEST_ERROR("get key_num fail"); + return ret; + } + } else if (IC_HW_MC_SC == tdata->func->hwtype) { + key_num = tdata->func->key_num_total; + } + tdata->node.tx_num = tx_num; + tdata->node.rx_num = rx_num; + if (IC_HW_INCELL == tdata->func->hwtype) + tdata->node.channel_num = tx_num * rx_num; + else if (IC_HW_MC_SC == tdata->func->hwtype) + tdata->node.channel_num = tx_num + rx_num; + tdata->node.node_num = tx_num * rx_num; + } + + /* key */ + tdata->node.key_num = key_num; + tdata->node.node_num += tdata->node.key_num; + + /* sc node structure */ + tdata->sc_node = tdata->node; + if (IC_HW_MC_SC == tdata->func->hwtype) { + if (tdata->v3_pattern) { + ret = get_tx_rx_num(FACTORY_REG_CHX_NUM_NOMAP, &tx_num, TX_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get no-mappint tx_num fail"); + return ret; + } + + ret = get_tx_rx_num(FACTORY_REG_CHY_NUM_NOMAP, &rx_num, TX_NUM_MAX); + if (ret < 0) { + FTS_TEST_ERROR("get no-mapping rx_num fail"); + return ret; + } + + tdata->sc_node.tx_num = tx_num; + tdata->sc_node.rx_num = rx_num; + } + tdata->sc_node.channel_num = tx_num + rx_num; + tdata->sc_node.node_num = tx_num + rx_num; + } + + if (tdata->node.tx_num > TX_NUM_MAX) { + FTS_TEST_ERROR("tx num(%d) fail", tdata->node.tx_num); + return -EIO; + } + + if (tdata->node.rx_num > RX_NUM_MAX) { + FTS_TEST_ERROR("rx num(%d) fail", tdata->node.rx_num); + return -EIO; + } + + FTS_TEST_INFO("node_num:%d, tx:%d, rx:%d, key:%d", + tdata->node.node_num, tdata->node.tx_num, + tdata->node.rx_num, tdata->node.key_num); + return 0; +} + +static int fts_test_init_basicinfo(struct fts_test *tdata) +{ + int ret = 0; + u8 val = 0; + + if ((NULL == tdata) || (NULL == tdata->func)) { + FTS_TEST_SAVE_ERR("tdata/func is NULL\n"); + return -EINVAL; + } + + fts_test_read_reg(REG_FW_VERSION, &val); + tdata->fw_ver = val; + + if (IC_HW_INCELL == tdata->func->hwtype) { + fts_test_read_reg(REG_VA_TOUCH_THR, &val); + tdata->va_touch_thr = val; + fts_test_read_reg(REG_VKEY_TOUCH_THR, &val); + tdata->vk_touch_thr = val; + } + + /* enter factory mode */ + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail\n"); + return ret; + } + + if (IC_HW_MC_SC == tdata->func->hwtype) { + fts_test_read_reg(FACTORY_REG_PATTERN, &val); + tdata->v3_pattern = (1 == val) ? true : false; + fts_test_read_reg(FACTORY_REG_NOMAPPING, &val); + tdata->mapping = val; + } + + /* enter into factory mode and read tx/rx num */ + ret = get_channel_num(tdata); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get channel number fail\n"); + return ret; + } + + return ret; +} + +static int fts_test_main_init(void) +{ + int ret = 0; + struct fts_test *tdata = fts_ftest; + + FTS_TEST_FUNC_ENTER(); + /* Init fts_test_data to 0 before test, */ + memset(&tdata->testdata, 0, sizeof(struct fts_test_data)); + + /* get basic information: tx/rx num ... */ + ret = fts_test_init_basicinfo(tdata); + if (ret < 0) { + FTS_TEST_ERROR("test init basicinfo fail"); + return ret; + } + + /* allocate memory for test threshold */ + ret = fts_test_malloc_free_thr(tdata, true); + if (ret < 0) { + FTS_TEST_ERROR("test malloc for threshold fail"); + return ret; + } + + /* default enable all test item */ + fts_test_init_item(tdata); + + ret = fts_test_malloc_free_data_txt(tdata, true); + if (ret < 0) { + FTS_TEST_ERROR("allocate memory for test data(txt) fail"); + return ret; + } + + /* allocate test data buffer */ + tdata->buffer_length = (tdata->node.tx_num + 1) * tdata->node.rx_num; + tdata->buffer_length *= sizeof(int) * 2; + FTS_TEST_INFO("test buffer length:%d", tdata->buffer_length); + tdata->buffer = (int *)fts_malloc(tdata->buffer_length); + if (NULL == tdata->buffer) { + FTS_TEST_ERROR("test buffer(%d) malloc fail", tdata->buffer_length); + return -ENOMEM; + } + memset(tdata->buffer, 0, tdata->buffer_length); + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int fts_test_main_exit(void) +{ + struct fts_test *tdata = fts_ftest; + + FTS_TEST_FUNC_ENTER(); + fts_test_save_data_csv(tdata); + fts_test_save_result_txt(tdata); + + /* free memory */ + fts_test_malloc_free_data_txt(tdata, false); + fts_test_malloc_free_thr(tdata, false); + + /* free test data */ + fts_test_free_data(tdata); + + /*free test data buffer*/ + fts_free(tdata->buffer); + + FTS_TEST_FUNC_EXIT(); + return 0; +} + + +/* + * fts_test_get_testparams - get test parameter from ini + */ +static int fts_test_get_testparams(char *config_name) +{ + int ret = 0; + + ret = fts_test_get_testparam_from_ini(config_name); + + return ret; +} + +static int fts_test_start(void) +{ + int testresult = 0; + struct fts_test *tdata = fts_ftest; + + if (tdata && tdata->func && tdata->func->start_test) { + tdata->testdata.item_count = 0; + testresult = tdata->func->start_test(); + } else { + FTS_TEST_ERROR("test func/start_test func is null"); + } + + return testresult; +} + +/* + * fts_test_entry - test main entry + * + * warning - need disable irq & esdcheck before call this function + * + */ +static int fts_test_entry(char *ini_file_name) +{ + int ret = 0; + + /* test initialize */ + ret = fts_test_main_init(); + if (ret < 0) { + FTS_TEST_ERROR("fts_test_main_init fail"); + goto test_err; + } + + /*Read parse configuration file*/ + FTS_TEST_SAVE_INFO("ini_file_name:%s\n", ini_file_name); + ret = fts_test_get_testparams(ini_file_name); + if (ret < 0) { + FTS_TEST_ERROR("get testparam fail"); + goto test_err; + } + + /* Start testing according to the test configuration */ + if (true == fts_test_start()) { + FTS_TEST_SAVE_INFO("\n\n=======Tp test pass.\n"); + fts_ftest->result = true; + } else { + FTS_TEST_SAVE_INFO("\n\n=======Tp test failure.\n"); + fts_ftest->result = false; +#if defined(TEST_SAVE_FAIL_RESULT) && TEST_SAVE_FAIL_RESULT + do_gettimeofday(&(fts_ftest->tv)); +#endif + } + + ret = 0; +test_err: + fts_test_main_exit(); + enter_work_mode(); + return ret; +} + +static ssize_t fts_test_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 size = 0; + + mutex_lock(&input_dev->mutex); + size += snprintf(buf + size, PAGE_SIZE, "FTS_INI_FILE_PATH:%s\n", + FTS_INI_FILE_PATH); + size += snprintf(buf + size, PAGE_SIZE, "FTS_CSV_FILE_NAME:%s\n", + FTS_CSV_FILE_NAME); + size += snprintf(buf + size, PAGE_SIZE, "FTS_TXT_FILE_NAME:%s\n", + FTS_TXT_FILE_NAME); + mutex_unlock(&input_dev->mutex); + + return size; +} + +static ssize_t fts_test_store( + struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret = 0; + char fwname[FILE_NAME_LENGTH] = { 0 }; + struct fts_ts_data *ts_data = fts_data; + struct input_dev *input_dev; + + if (ts_data->suspended) { + FTS_INFO("In suspend, no test, return now"); + return -EINVAL; + } + + input_dev = ts_data->input_dev; + memset(fwname, 0, sizeof(fwname)); + snprintf(fwname, FILE_NAME_LENGTH, "%s", buf); + fwname[count - 1] = '\0'; + FTS_TEST_DBG("fwname:%s.", fwname); + + mutex_lock(&input_dev->mutex); + fts_irq_disable(); + +#if defined(FTS_ESDCHECK_EN) && (FTS_ESDCHECK_EN) + fts_esdcheck_switch(DISABLE); +#endif + + ret = fts_enter_test_environment(1); + if (ret < 0) { + FTS_ERROR("enter test environment fail"); + } else { + fts_test_entry(fwname); + } + ret = fts_enter_test_environment(0); + if (ret < 0) { + FTS_ERROR("enter normal environment fail"); + } + +#if defined(FTS_ESDCHECK_EN) && (FTS_ESDCHECK_EN) + fts_esdcheck_switch(ENABLE); +#endif + + fts_irq_enable(); + mutex_unlock(&input_dev->mutex); + + return count; +} + +/* test from test.ini +* example:echo "***.ini" > fts_test +*/ +static DEVICE_ATTR(fts_test, S_IRUGO | S_IWUSR, fts_test_show, fts_test_store); + +static struct attribute *fts_test_attributes[] = { + &dev_attr_fts_test.attr, + NULL +}; + +static struct attribute_group fts_test_attribute_group = { + .attrs = fts_test_attributes +}; + +static int fts_test_func_init(struct fts_ts_data *ts_data) +{ + int i = 0; + int j = 0; + u16 ic_stype = ts_data->ic_info.ids.type; + struct test_funcs *func = test_func_list[0]; + int func_count = sizeof(test_func_list) / sizeof(test_func_list[0]); + + FTS_TEST_INFO("init test function"); + if (0 == func_count) { + FTS_TEST_SAVE_ERR("test functions list is NULL, fail\n"); + return -ENODATA; + } + + fts_ftest = (struct fts_test *)kzalloc(sizeof(*fts_ftest), GFP_KERNEL); + if (NULL == fts_ftest) { + FTS_TEST_ERROR("malloc memory for test fail"); + return -ENOMEM; + } + + for (i = 0; i < func_count; i++) { + func = test_func_list[i]; + for (j = 0; j < FTS_MAX_COMPATIBLE_TYPE; j++) { + if (0 == func->ctype[j]) + break; + else if (func->ctype[j] == ic_stype) { + FTS_TEST_INFO("match test function,type:%x", (int)func->ctype[j]); + fts_ftest->func = func; + } + } + } + if (NULL == fts_ftest->func) { + FTS_TEST_ERROR("no test function match, can't test"); + return -ENODATA; + } + + fts_ftest->ts_data = fts_data; + return 0; +} + +int fts_test_init(struct fts_ts_data *ts_data) +{ + int ret = 0; + + FTS_TEST_FUNC_ENTER(); + /* get test function, must be the first step */ + ret = fts_test_func_init(ts_data); + if (ret < 0) { + FTS_TEST_SAVE_ERR("test functions init fail"); + return ret; + } + + ret = sysfs_create_group(&ts_data->dev->kobj, &fts_test_attribute_group); + if (0 != ret) { + FTS_TEST_ERROR("sysfs(test) create fail"); + sysfs_remove_group(&ts_data->dev->kobj, &fts_test_attribute_group); + } else { + FTS_TEST_DBG("sysfs(test) create successfully"); + } + FTS_TEST_FUNC_EXIT(); + + return ret; +} + +int fts_test_exit(struct fts_ts_data *ts_data) +{ + FTS_TEST_FUNC_ENTER(); + + sysfs_remove_group(&ts_data->dev->kobj, &fts_test_attribute_group); + fts_free(fts_ftest); + FTS_TEST_FUNC_EXIT(); + return 0; +} diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/focaltech_test.h b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/focaltech_test.h new file mode 100644 index 000000000000..0f09a5fa461c --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/focaltech_test.h @@ -0,0 +1,648 @@ +/************************************************************************ +* Copyright (c) 2012-2020, FocalTech Systems, Ltd., all rights reserved. +* +* File Name: focaltech_test.h +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-01 +* +* Abstract: test entry for all IC +* +************************************************************************/ +#ifndef _TEST_LIB_H +#define _TEST_LIB_H + +/***************************************************************************** +* Included header files +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include //iic +#include //msleep +#include +#include +#include +#include +#include "../focaltech_core.h" +#include "focaltech_test_ini.h" + +/***************************************************************************** +* Macro definitions using #define +*****************************************************************************/ +#define FTS_INI_FILE_PATH "/mnt/sdcard/" +#define FTS_CSV_FILE_NAME "testdata.csv" +#define FTS_TXT_FILE_NAME "testresult.txt" +#define false 0 +#define true 1 +#define TEST_ICSERIES_LEN (8) +#define TEST_ICSERIES(x) ((x) >> TEST_ICSERIES_LEN) + +#define TEST_OPEN_MAX_VALUE (255) +#define BYTES_PER_TIME (32) /* max:128 */ +/* CSV & TXT */ +#define CSV_LINE2_BUFFER_LEN (1024) +#define CSV_BUFFER_LEN (1024*80*5) +#define TXT_BUFFER_LEN (1024*80*5) + +#define TEST_SAVE_FAIL_RESULT 0 + +/*----------------------------------------------------------- +Test Status +-----------------------------------------------------------*/ +#define RESULT_NULL 0 +#define RESULT_PASS 1 +#define RESULT_NG 2 + +#define TX_NUM_MAX 60 +#define RX_NUM_MAX 100 +#define SC_NUM_MAX ((TX_NUM_MAX) + (RX_NUM_MAX)) +#define NUM_MAX_SC (144) +#define KEY_NUM_MAX 6 +#define TEST_ITEM_COUNT_MAX 32 +#define TEST_ITEM_NAME_MAX 32 +#define TEST_SHORT_RES_MAX 0xFFFF + +/* + * factory test registers + */ +#define ENTER_WORK_FACTORY_RETRIES 5 + +#define START_SCAN_RETRIES_INCELL 20 +#define START_SCAN_RETRIES_DELAY_INCELL 16 +#define FACTORY_TEST_RETRY 50 +#define FACTORY_TEST_DELAY 18 +#define FACTORY_TEST_RETRY_DELAY 100 + +#define DEVIDE_MODE_ADDR 0x00 +#define REG_FW_VERSION 0xA6 +#define REG_VA_TOUCH_THR 0x80 +#define REG_VKEY_TOUCH_THR 0x82 + +#define FACTORY_REG_LINE_ADDR 0x01 +#define FACTORY_REG_CHX_NUM 0x02 +#define FACTORY_REG_CHY_NUM 0x03 +#define FACTORY_REG_CLB 0x04 +#define FACTORY_REG_DATA_SELECT 0x06 +#define FACTORY_REG_RAWBUF_SELECT 0x09 +#define FACTORY_REG_KEY_CBWIDTH 0x0B +#define FACTORY_REG_PARAM_UPDATE_STATE 0x0E +#define FACTORY_REG_SHORT_TEST_EN 0x0F +#define FACTORY_REG_SHORT_TEST_STATE 0x10 +#define FACTORY_REG_LCD_NOISE_START 0x11 +#define FACTORY_REG_LCD_NOISE_FRAME 0x12 +#define FACTORY_REG_LCD_NOISE_TEST_STATE 0x13 +#define FACTORY_REG_LCD_NOISE_TTHR 0x14 +#define FACTORY_REG_OPEN_START 0x15 +#define FACTORY_REG_OPEN_STATE 0x16 +#define FACTORY_REG_OPEN_ADDR 0xCF +#define FACTORY_REG_OPEN_IDLE 0x03 +#define FACTORY_REG_OPEN_BUSY 0x01 +#define FACTORY_REG_CB_ADDR_H 0x18 +#define FACTORY_REG_CB_ADDR_L 0x19 +#define FACTORY_REG_ORDER_ADDR_H 0x1A +#define FACTORY_REG_ORDER_ADDR_L 0x1B +#define FACTORY_REG_LCD_NOISE_STATE 0x1E +#define FACTORY_REG_KEYSHORT_EN 0x2E +#define FACTORY_REG_KEYSHORT_STATE 0x2F + +#define FACTORY_REG_LEFT_KEY 0x1E +#define FACTORY_REG_RIGHT_KEY 0x1F +#define FACTORY_REG_OPEN_REG20 0x20 +#define FACTORY_REG_OPEN_REG21 0x21 +#define FACTORY_REG_OPEN_REG22 0x22 +#define FACTORY_REG_OPEN_REG23 0x23 +#define FACTORY_REG_OPEN_REG2E 0x2E +#define FACTORY_REG_OPEN_REG86 0x86 +#define FACTORY_REG_K1 0x31 +#define FACTORY_REG_K2 0x32 +#define FACTORY_REG_RAWDATA_ADDR 0x6A +#define FACTORY_REG_ORDER_ADDR 0x6C +#define FACTORY_REG_CB_ADDR 0x6E +#define FACTORY_REG_SHORT_ADDR 0x89 +#define FACTORY_REG_RAWDATA_TEST_EN 0x9E +#define FACTORY_REG_CB_TEST_EN 0x9F +#define FACTORY_REG_OPEN_TEST_EN 0xA0 +#define FACTORY_REG_RAWDATA_TARGET 0xCA + + +/* mc_sc */ +#define FACTORY_REG_FRE_LIST 0x0A +#define FACTORY_REG_DATA_TYPE 0x5B +#define FACTORY_REG_NORMALIZE 0x16 +#define FACTORY_REG_RAWDATA_ADDR_MC_SC 0x36 +#define FACTORY_REG_PATTERN 0x53 +#define FACTORY_REG_NOMAPPING 0x54 +#define FACTORY_REG_CHX_NUM_NOMAP 0x55 +#define FACTORY_REG_CHY_NUM_NOMAP 0x56 +#define FACTORY_REG_WC_SEL 0x09 +#define FACTORY_REG_HC_SEL 0x0F +#define FACTORY_REG_MC_SC_MODE 0x44 +#define FACTORY_REG_MC_SC_CB_ADDR_OFF 0x45 +#define FACTORY_REG_MC_SC_CB_H_ADDR_OFF 0x49 +#define FACTORY_REG_MC_SC_CB_ADDR 0x4E +#define FACTROY_REG_SHORT_TEST_EN 0x07 +#define FACTROY_REG_SHORT_CA 0x01 +#define FACTROY_REG_SHORT_CC 0x02 +#define FACTROY_REG_SHORT_CG 0x03 +#define FACTROY_REG_SHORT_OFFSET 0x04 +#define FACTROY_REG_SHORT_AB_CH 0x58 +#define FACTROY_REG_SHORT_RES_LEVEL 0x5A +#define FACTORY_REG_SHORT_ADDR_MC 0xF4 +#define FACTORY_REG_FIR 0xFB + +#define FACTROY_REG_SHORT2_TEST_EN 0xC0 +#define FACTROY_REG_SHORT2_CA 0x01 +#define FACTROY_REG_SHORT2_CC 0x02 +#define FACTROY_REG_SHORT2_CG 0x03 +#define FACTROY_REG_SHORT2_OFFSET 0x04 +#define FACTROY_REG_SHORT2_RES_LEVEL 0xC1 +#define FACTROY_REG_SHORT2_DEALY 0xC2 +#define FACTROY_REG_SHORT2_TEST_STATE 0xC3 +#define FACTORY_REG_SHORT2_ADDR_MC 0xC4 +#define FACTROY_REG_SHORT2_AB_CH 0xC6 + +/* sc */ +#define FACTORY_REG_SCAN_ADDR2 0x08 +#define FACTORY_REG_CH_NUM_SC 0x0A +#define FACTORY_REG_KEY_NUM_SC 0x0B +#define FACTORY_REG_SC_CB_ADDR_OFF 0x33 +#define FACTORY_REG_SC_CB_ADDR 0x39 +#define FACTORY_REG_RAWDATA_SADDR_SC 0x34 +#define FACTORY_REG_RAWDATA_ADDR_SC 0x35 +#define FACTORY_REG_CB_SEL 0x41 +#define FACTORY_REG_FMODE 0xAE + +#define TEST_RETVAL_00 0x00 +#define TEST_RETVAL_AA 0xAA + +#define FTS_MAX_SORT_SC 32768 +#define FTS_MIN_SORT_SC 0 + +/***************************************************************************** +* enumerations, structures and unions +*****************************************************************************/ +struct item_info { + char name[TEST_ITEM_NAME_MAX]; + int code; + int *data; + int datalen; + int result; + int mc_sc; + int key_support; +}; + +struct fts_test_data { + int item_count; + struct item_info info[TEST_ITEM_COUNT_MAX]; +}; + +/* incell */ +struct incell_testitem { + u32 short_test : 1; + u32 open_test : 1; + u32 cb_test : 1; + u32 rawdata_test : 1; + u32 lcdnoise_test : 1; + u32 keyshort_test : 1; + u32 mux_open_test : 1; +}; + +struct incell_threshold_b { + int short_res_min; + int short_res_vk_min; + int open_cb_min; + int open_k1_check; + int open_k1_value; + int open_k2_check; + int open_k2_value; + int cb_min; + int cb_max; + int cb_vkey_check; + int cb_min_vk; + int cb_max_vk; + int rawdata_min; + int rawdata_max; + int rawdata_vkey_check; + int rawdata_min_vk; + int rawdata_max_vk; + int lcdnoise_frame; + int lcdnoise_coefficient; + int lcdnoise_coefficient_vkey; + int open_diff_min; + int open_nmos; + int keyshort_k1; + int keyshort_cb_max; + int rawdata2_min; + int rawdata2_max; + int mux_open_cb_min; + int open_delta_V; +}; + +struct incell_threshold { + struct incell_threshold_b basic; + int *rawdata_min; + int *rawdata_max; + int *rawdata2_min; + int *rawdata2_max; + int *cb_min; + int *cb_max; +}; + +struct incell_test { + struct incell_threshold thr; + union { + int tmp; + struct incell_testitem item; + } u; +}; + +/* mc_sc */ +enum mapping_type { + MAPPING = 0, + NO_MAPPING = 1, +}; + +struct mc_sc_testitem { + u32 rawdata_test : 1; + u32 rawdata_uniformity_test : 1; + u32 scap_cb_test : 1; + u32 scap_rawdata_test : 1; + u32 short_test : 1; + u32 panel_differ_test : 1; +}; + +struct mc_sc_threshold_b { + int rawdata_h_min; + int rawdata_h_max; + int rawdata_set_hfreq; + int rawdata_l_min; + int rawdata_l_max; + int rawdata_set_lfreq; + int uniformity_check_tx; + int uniformity_check_rx; + int uniformity_check_min_max; + int uniformity_tx_hole; + int uniformity_rx_hole; + int uniformity_min_max_hole; + int scap_cb_off_min; + int scap_cb_off_max; + int scap_cb_wp_off_check; + int scap_cb_on_min; + int scap_cb_on_max; + int scap_cb_wp_on_check; + int scap_rawdata_off_min; + int scap_rawdata_off_max; + int scap_rawdata_wp_off_check; + int scap_rawdata_on_min; + int scap_rawdata_on_max; + int scap_rawdata_wp_on_check; + int short_cg; + int short_cc; + int panel_differ_min; + int panel_differ_max; + int scap_cb_hi_min; + int scap_cb_hi_max; + int scap_cb_hi_check; + int scap_rawdata_hi_min; + int scap_rawdata_hi_max; + int scap_rawdata_hi_check; + int scap_cb_hov_min; + int scap_cb_hov_max; + int scap_cb_hov_check; + int scap_rawdata_hov_min; + int scap_rawdata_hov_max; + int scap_rawdata_hov_check; +}; + +struct mc_sc_threshold { + struct mc_sc_threshold_b basic; + int *rawdata_h_min; + int *rawdata_h_max; + int *rawdata_l_min; + int *rawdata_l_max; + int *tx_linearity_max; + int *tx_linearity_min; + int *rx_linearity_max; + int *rx_linearity_min; + int *scap_cb_off_min; + int *scap_cb_off_max; + int *scap_cb_on_min; + int *scap_cb_on_max; + int *scap_cb_hi_min; + int *scap_cb_hi_max; + int *scap_cb_hov_min; + int *scap_cb_hov_max; + int *scap_rawdata_off_min; + int *scap_rawdata_off_max; + int *scap_rawdata_on_min; + int *scap_rawdata_on_max; + int *scap_rawdata_hi_min; + int *scap_rawdata_hi_max; + int *scap_rawdata_hov_min; + int *scap_rawdata_hov_max; + int *panel_differ_min; + int *panel_differ_max; +}; + +struct mc_sc_test { + struct mc_sc_threshold thr; + union { + u32 tmp; + struct mc_sc_testitem item; + } u; +}; + +/* sc */ +struct sc_testitem { + u32 rawdata_test : 1; + u32 cb_test : 1; + u32 delta_cb_test : 1; + u32 short_test : 1; +}; + +struct sc_threshold_b { + int rawdata_min; + int rawdata_max; + int cb_min; + int cb_max; + int dcb_base; + int dcb_differ_max; + int dcb_key_check; + int dcb_key_differ_max; + int dcb_ds1; + int dcb_ds2; + int dcb_ds3; + int dcb_ds4; + int dcb_ds5; + int dcb_ds6; + int dcb_critical_check; + int dcb_cs1; + int dcb_cs2; + int dcb_cs3; + int dcb_cs4; + int dcb_cs5; + int dcb_cs6; + int short_min; +}; + +struct sc_threshold { + struct sc_threshold_b basic; + int *rawdata_min; + int *rawdata_max; + int *cb_min; + int *cb_max; + int *dcb_sort; + int *dcb_base; +}; + +struct sc_test { + struct sc_threshold thr; + union { + u32 tmp; + struct sc_testitem item; + } u; +}; + +enum test_hw_type { + IC_HW_INCELL = 1, + IC_HW_MC_SC, + IC_HW_SC, +}; + +enum test_scan_mode { + SCAN_NORMAL = 0, + SCAN_SC, +}; + +struct fts_test_node { + int channel_num; + int tx_num; + int rx_num; + int node_num; + int key_num; +}; + +struct fts_test { + struct fts_ts_data *ts_data; + struct fts_test_node node; + struct fts_test_node sc_node; + u8 fw_ver; + u8 va_touch_thr; + u8 vk_touch_thr; + bool key_support; + bool v3_pattern; + u8 mapping; + u8 normalize; + int test_num; + int *buffer; + int buffer_length; + int *node_valid; + int *node_valid_sc; + int basic_thr_count; + int code1; + int code2; + int offset; + union { + struct incell_test incell; + struct mc_sc_test mc_sc; + struct sc_test sc; + } ic; + + struct test_funcs *func; + struct fts_test_data testdata; + char *testresult; + int testresult_len; + int result; +#if defined(TEST_SAVE_FAIL_RESULT) && TEST_SAVE_FAIL_RESULT + struct timeval tv; +#endif + struct ini_data ini; +}; + +struct test_funcs { + u16 ctype[FTS_MAX_COMPATIBLE_TYPE]; + enum test_hw_type hwtype; + int startscan_mode; + int key_num_total; + bool rawdata2_support; + bool force_touch; + bool mc_sc_short_v2; + bool raw_u16; + bool cb_high_support; + int (*param_init)(void); + int (*init)(void); + int (*start_test)(void); +}; + +enum byte_mode { + DATA_ONE_BYTE, + DATA_TWO_BYTE, +}; +/* mc_sc */ +enum normalize_type { + NORMALIZE_OVERALL, + NORMALIZE_AUTO, +}; + +enum wp_type { + WATER_PROOF_OFF = 0, + WATER_PROOF_ON = 1, + HIGH_SENSITIVITY = 2, + HOV = 3, + WATER_PROOF_ON_TX = 100, + WATER_PROOF_ON_RX, + WATER_PROOF_OFF_TX, + WATER_PROOF_OFF_RX, +}; +/* mc end */ + +/* sc */ +enum factory_mode { + FACTORY_NORMAL, + FACTORY_TESTMODE_1, + FACTORY_TESTMODE_2, +}; + +enum dcb_sort_num { + DCB_SORT_MIN = 1, + DCB_SORT_MAX = 6, +}; + +struct dcb_sort_d { + int ch_num; + int deviation; + int critical; + int min; + int max; +}; +/* sc end */ + +enum csv_itemcode_incell { + CODE_ENTER_FACTORY_MODE = 0, + CODE_RAWDATA_TEST = 7, + CODE_CB_TEST = 12, + CODE_SHORT_TEST = 15, + CODE_OPEN_TEST = 25, + CODE_LCD_NOISE_TEST = 27, + CODE_MUX_OPEN_TEST = 41, +}; + +enum csv_itemcode_mc_sc { + CODE_M_RAWDATA_TEST = 7, + CODE_M_SCAP_CB_TEST = 9, + CODE_M_SCAP_RAWDATA_TEST = 10, + CODE_M_WEAK_SHORT_CIRCUIT_TEST = 15, + CODE_M_RAWDATA_UNIFORMITY_TEST = 16, + CODE_M_PANELDIFFER_TEST = 20, +}; + +enum csv_itemcode_sc { + CODE_S_RAWDATA_TEST = 7, + CODE_S_CB_TEST = 13, + CODE_S_DCB_TEST = 14, +}; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +extern struct test_funcs test_func_ft5452i; + +extern struct fts_test *fts_ftest; + +void sys_delay(int ms); +int fts_abs(int value); +void print_buffer(int *buffer, int length, int line_num); +int fts_test_read_reg(u8 addr, u8 *val); +int fts_test_write_reg(u8 addr, u8 val); +int fts_test_read(u8 addr, u8 *readbuf, int readlen); +int fts_test_write(u8 addr, u8 *writebuf, int writelen); +int enter_work_mode(void); +int enter_factory_mode(void); +int read_mass_data(u8 addr, int byte_num, int *buf); +int chip_clb(void); +int wait_state_update(u8 retval); +int get_cb_incell(u16 saddr, int byte_num, int *cb_buf); +int short_get_adcdata_incell(u8 retval, u8 ch_num, int byte_num, int *adc_buf); +int start_scan(void); +int get_rawdata(int *data); +int get_cb_sc(int byte_num, int *cb_buf, enum byte_mode mode); +bool compare_data(int *data, int min, int max, int min_vk, int max_vk, bool key); +bool compare_array(int *data, int *min, int *max, bool key); +void show_data(int *data, bool key); +/* mc_sc */ +int mapping_switch(u8 mapping); +bool get_fw_wp(u8 wp_channel_select, enum wp_type water_proof_type); +int get_cb_mc_sc(u8 wp, int byte_num, int *cb_buf, enum byte_mode mode); +int get_rawdata_mc_sc(enum wp_type wp, int *data); +int get_rawdata_mc(u8 fre, u8 fir, int *rawdata); +int short_get_adc_data_mc(u8 retval, int byte_num, int *adc_buf, u8 mode); +bool compare_mc_sc(bool, bool, int *, int *, int *); +void show_data_mc_sc(int *data); +void *fts_malloc(size_t size); +void fts_free_proc(void *p); +void fts_test_save_data(char *name, int code, int *data, int datacnt, + bool mc_sc, bool key, bool result); + +#define fts_malloc_r(p, size) do {\ + if (NULL == p) {\ + p = fts_malloc(size);\ + if (NULL == p) {\ + return -ENOMEM;\ + }\ + }\ +} while(0) + +#define fts_free(p) do {\ + if (p) {\ + fts_free_proc(p);\ + p = NULL;\ + }\ +} while(0) + +#define CSV_SUPPORT 1 +#define TXT_SUPPORT 1 + +#define FTS_TEST_DBG(fmt, args...) do { \ +printk("[FTS_TS][TEST]%s:"fmt"\n", __func__, ##args); \ +} while (0) + +#define FTS_TEST_FUNC_ENTER() do { \ + printk("[FTS_TS][TEST]%s: Enter\n", __func__); \ +} while (0) + +#define FTS_TEST_FUNC_EXIT() do { \ + printk("[FTS_TS][TEST]%s: Exit(%d)\n", __func__, __LINE__); \ +} while (0) + +#define FTS_TEST_INFO(fmt, args...) do { \ + printk(KERN_ERR "[FTS_TS/I][TEST]%s:"fmt"\n", __func__, ##args); \ +} while (0) + +#define FTS_TEST_ERROR(fmt, args...) do { \ + printk(KERN_ERR "[FTS_TS/E][TEST]%s:"fmt"\n", __func__, ##args); \ +} while (0) + +#define FTS_TEST_SAVE_INFO(fmt, args...) do { \ + if (fts_ftest->testresult) { \ + fts_ftest->testresult_len += snprintf( \ + fts_ftest->testresult + fts_ftest->testresult_len, \ + TXT_BUFFER_LEN, \ + fmt, ##args);\ + } \ +} while (0) + +#define FTS_TEST_SAVE_ERR(fmt, args...) do { \ + if (fts_ftest->testresult && (fts_ftest->testresult_len < TXT_BUFFER_LEN)) { \ + fts_ftest->testresult_len += snprintf( \ + fts_ftest->testresult + fts_ftest->testresult_len, \ + TXT_BUFFER_LEN, \ + fmt, ##args);\ + } \ + printk(KERN_ERR "[FTS_TS/E][TEST]%s:"fmt"\n", __func__, ##args);\ +} while (0) +#endif diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/focaltech_test_ini.c b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/focaltech_test_ini.c new file mode 100644 index 000000000000..64380a94ced3 --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/focaltech_test_ini.c @@ -0,0 +1,1408 @@ +/************************************************************************ +* Copyright (c) 2012-2020, Focaltech Systems (R)£¬All Rights Reserved. +* +* File Name: focaltech_test_ini.c +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-01 +* +* Abstract: parsing function of INI file +* +************************************************************************/ +#include "focaltech_test.h" + +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define FTS_INI_REQUEST_SUPPORT 0 + +struct ini_ic_type ic_types[] = { + {"FT5X46", 0x54000002}, + {"FT5X46i", 0x54010002}, + {"FT5526", 0x54020002}, + {"FT3X17", 0x54030002}, + {"FT5436", 0x54040002}, + {"FT3X27", 0x54050002}, + {"FT5526i", 0x54060002}, + {"FT5416", 0x54070002}, + {"FT5426", 0x54080002}, + {"FT5435", 0x54090002}, + {"FT7681", 0x540A0002}, + {"FT7661", 0x540B0002}, + {"FT7511", 0x540C0002}, + {"FT7421", 0x540D0002}, + {"FT7311", 0x54100002}, + + {"FT5526_003", 0x40020082}, + {"FT5426_003", 0x40030082}, + {"FT3427G_003", 0x40040082}, + {"FT3427_003", 0x40050082}, + {"FT5446_003", 0x40000082}, + {"FT5446_Q03", 0x40000082}, + {"FT5446_P03", 0x55060081}, + {"FT5446DQS-W01", 0x40000082}, + + {"FT5452", 0x55000081}, + {"FT3518", 0x55010081}, + {"FT3558", 0x55020081}, + {"FT3528", 0x55030081}, + {"FT5536", 0x55040081}, + {"FT3418", 0x55070081}, + {"FT5536L", 0x55080081}, + + {"FT5472", 0x8F000083}, + {"FT5446U", 0x8F010083}, + {"FT5456U", 0x8F020083}, + {"FT3417U", 0x8F030083}, + {"FT5426U", 0x8F040083}, + {"FT3428", 0x8F050083}, + {"FT3437U", 0x8F060083}, + + {"FT5822", 0x58000001}, + {"FT5626", 0x58010001}, + {"FT5726", 0x58020001}, + {"FT5826B", 0x58030001}, + {"FT3617", 0x58040001}, + {"FT3717", 0x58050001}, + {"FT7811", 0x58060001}, + {"FT5826S", 0x58070001}, + {"FT3517U", 0x58090001}, + {"FT3557", 0x580A0001}, + + {"FT6X36", 0x63000003}, + {"FT3X07", 0x63010003}, + {"FT6416", 0x63020003}, + {"FT6336G/U", 0x63030003}, + {"FT7401", 0x63040003}, + {"FT3407U", 0x63050003}, + {"FT6236U", 0x63060003}, + {"FT6436U", 0x63070003}, + + {"FT3267", 0x63080004}, + {"FT3367", 0x63090004}, + + {"FT6216", 0x64000084}, + {"FT7302", 0x64010084}, + {"FT7202", 0x64020084}, + {"FT3308", 0x64030084}, + {"FT6446", 0x64040084}, + + {"FT8607", 0x81000009}, + {"FT8716", 0x82000005}, + {"FT8716U", 0x44000005}, + {"FT8716F", 0x8A000005}, + {"FT8613", 0x4500000C}, + + {"FT8736", 0x83000006}, + + {"FT8201", 0x87010010}, + {"FT7250", 0x8702001A}, + + {"FT8006U", 0x8900000B}, + {"FT8006S", 0x8901000B}, + {"FT8006S-AA", 0x9B000019}, + {"FT8016", 0x9B01001D}, + + {"FT8719", 0x8E00000D}, + {"FT8615", 0x9100000F}, + + {"FT8739", 0x8D00000E}, + + {"FT8006P", 0x93000011}, + {"FT7120", 0x9E00001B}, + + {"FT7251", 0x8C000012}, + {"FT7252", 0x92000013}, + + {"FT8613S", 0x94000014}, + + {"FT8756", 0x95000015}, + {"FT8656", 0x95010018}, + + {"FT8302", 0x97000016}, + + {"FT8009", 0x98000017}, + + {"FT8720", 0x9C00001C}, + + {"FT3068", 0x65010085}, + {"FT3168", 0x65020085}, + {"FT3067", 0x65030085}, + {"FT3268", 0x65040085}, + {"FT6346U", 0x65050085}, + {"FT6146", 0x65060085}, + {"FT6346G", 0x65070085}, + + {"FT5726_V03", 0x580C0086}, + {"FT5726_003", 0x580C0086}, + + {"FT3618", 0x59010087}, + {"FT5646", 0x59020087}, + {"FT3A58", 0x59030087}, + {"FT3B58", 0x59040087}, + {"FT3D58", 0x59050087}, + {"FT5A36", 0x59060087}, + {"FT5B36", 0x59070087}, + {"FT5D36", 0x59080087}, + {"FT5A46", 0x59090087}, + {"FT5B46", 0x590A0087}, + {"FT5D46", 0x590B0087}, + {"FT5936", 0x590C0087}, + {"FT5946", 0x590D0087}, + + {"FT3658U", 0x5A010088}, + + {"FT2388", 0x9D00001E}, +}; + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ + +/***************************************************************************** +* Static function prototypes +*****************************************************************************/ +/* Works only for digits and letters, but small and fast */ +#define TOLOWER(x) ((x) | 0x20) +static int fts_strncmp(const char *cs, const char *ct, int count) +{ + u8 c1 = 0, c2 = 0; + + while (count) { + if ((*cs == '\0') || (*ct == '\0')) + return -1; + c1 = TOLOWER(*cs++); + c2 = TOLOWER(*ct++); + if (c1 != c2) + return c1 < c2 ? -1 : 1; + if (!c1) + break; + count--; + } + + return 0; +} + +static int fts_isspace(int x) +{ + if (x == ' ' || x == '\t' || x == '\n' || x == '\f' || x == '\b' || x == '\r') + return 1; + else + return 0; +} + +static int fts_isdigit(int x) +{ + if (x <= '9' && x >= '0') + return 1; + else + return 0; +} + +static long fts_atol(char *nptr) +{ + int c; /* current char */ + long total; /* current total */ + int sign; /* if ''-'', then negative, otherwise positive */ + /* skip whitespace */ + while ( fts_isspace((int)(unsigned char)*nptr) ) + ++nptr; + c = (int)(unsigned char) * nptr++; + sign = c; /* save sign indication */ + if (c == '-' || c == '+') + c = (int)(unsigned char) * nptr++; /* skip sign */ + total = 0; + while (fts_isdigit(c)) { + total = 10 * total + (c - '0'); /* accumulate digit */ + c = (int)(unsigned char) * nptr++; /* get next char */ + } + if (sign == '-') + return -total; + else + return total; /* return result, negated if necessary */ +} + +static int fts_atoi(char *nptr) +{ + return (int)fts_atol(nptr); +} + +static int fts_test_get_ini_size(char *config_name) +{ + struct file *pfile = NULL; + struct inode *inode = NULL; + off_t fsize = 0; + char filepath[FILE_NAME_LENGTH] = { 0 }; + + FTS_TEST_FUNC_ENTER(); + + memset(filepath, 0, sizeof(filepath)); + snprintf(filepath, FILE_NAME_LENGTH, "%s%s", + FTS_INI_FILE_PATH, config_name); + + if (NULL == pfile) + pfile = filp_open(filepath, O_RDONLY, 0); + if (IS_ERR(pfile)) { + FTS_TEST_ERROR("error occured while opening file %s.", filepath); + return -EIO; + } + +#if 1 + inode = pfile->f_inode; +#else + /* reserved for linux earlier verion */ + inode = pfile->f_dentry->d_inode; +#endif + fsize = inode->i_size; + filp_close(pfile, NULL); + + FTS_TEST_FUNC_ENTER(); + + return fsize; +} + +static int fts_test_read_ini_data(char *config_name, char *config_buf) +{ + struct file *pfile = NULL; + struct inode *inode = NULL; + off_t fsize = 0; + char filepath[FILE_NAME_LENGTH] = { 0 }; + loff_t pos = 0; + mm_segment_t old_fs; + + FTS_TEST_FUNC_ENTER(); + + memset(filepath, 0, sizeof(filepath)); + snprintf(filepath, FILE_NAME_LENGTH, "%s%s", + FTS_INI_FILE_PATH, config_name); + if (NULL == pfile) { + pfile = filp_open(filepath, O_RDONLY, 0); + } + if (IS_ERR(pfile)) { + FTS_TEST_ERROR("error occured while opening file %s.", filepath); + return -EIO; + } + +#if 1 + inode = pfile->f_inode; +#else + /* reserved for linux earlier verion */ + inode = pfile->f_dentry->d_inode; +#endif + fsize = inode->i_size; + old_fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + vfs_read(pfile, config_buf, fsize, &pos); + filp_close(pfile, NULL); + set_fs(old_fs); + + FTS_TEST_FUNC_EXIT(); + return 0; +} + +static int fts_test_get_ini_default(struct ini_data *ini, char *fwname) +{ + int ret = 0; + int inisize = 0; + + inisize = fts_test_get_ini_size(fwname); + FTS_TEST_DBG("ini file size:%d", inisize); + if (inisize <= 0) { + FTS_TEST_ERROR("get ini file size fail"); + return -ENODATA; + } + + ini->data = vmalloc(inisize + 1); + if (NULL == ini->data) { + FTS_TEST_ERROR("malloc memory for ini data fail"); + return -ENOMEM; + } + memset(ini->data, 0, inisize + 1); + ini->length = inisize + 1; + + ret = fts_test_read_ini_data(fwname, ini->data); + if (ret) { + FTS_TEST_ERROR("read ini file fail"); + return -ENODATA; + } + ini->data[inisize] = '\n'; /* last line is null line */ + + return 0; +} + +static int fts_test_get_ini_via_request_firmware(struct ini_data *ini, char *fwname) +{ + int ret = 0; + const struct firmware *fw = NULL; + struct device *dev = &fts_data->input_dev->dev; + +#if !FTS_INI_REQUEST_SUPPORT + return -EINVAL; +#endif + + ret = request_firmware(&fw, fwname, dev); + if (0 == ret) { + FTS_TEST_INFO("firmware request(%s) success", fwname); + ini->data = vmalloc(fw->size + 1); + if (NULL == ini->data) { + FTS_TEST_ERROR("ini->data buffer vmalloc fail"); + ret = -ENOMEM; + } else { + memcpy(ini->data, fw->data, fw->size); + ini->data[fw->size] = '\n'; + ini->length = fw->size + 1; + } + } else { + FTS_TEST_INFO("firmware request(%s) fail,ret=%d", fwname, ret); + } + + if (fw != NULL) { + release_firmware(fw); + fw = NULL; + } + + return ret; +} + + +static void str_space_remove(char *str) +{ + char *t = str; + char *s = str; + + while (*t != '\0') { + if (*t != ' ') { + *s = *t; + s++; + } + t++; + } + + *s = '\0'; +} + +static void print_ini_data(struct ini_data *ini) +{ + int i = 0; + int j = 0; + struct ini_section *section = NULL; + struct ini_keyword *keyword = NULL; + struct fts_test *tdata = fts_ftest; + + if (tdata && tdata->ts_data && (tdata->ts_data->log_level < 10)) { + return; + } + + if (!ini || !ini->tmp) { + FTS_TEST_DBG("ini is null"); + return; + } + + FTS_TEST_DBG("section num:%d, keyword num total:%d", + ini->section_num, ini->keyword_num_total); + for (i = 0; i < ini->section_num; i++) { + section = &ini->section[i]; + FTS_TEST_DBG("section name:[%s] keyword num:%d", + section->name, section->keyword_num); + for (j = 0; j < section->keyword_num; j++) { + keyword = §ion->keyword[j]; + FTS_TEST_DBG("%s=%s", keyword->name, keyword->value); + } + } +} + +static int ini_get_line(char *filedata, char *line_data, int *line_len) +{ + int i = 0; + int line_length = 0; + int type; + + /* get a line data */ + for (i = 0; i < MAX_INI_LINE_LEN; i++) { + if (('\n' == filedata[i]) || ('\r' == filedata[i])) { + line_data[line_length++] = '\0'; + if (('\n' == filedata[i + 1]) || ('\r' == filedata[i + 1])) { + line_length++; + } + break; + } else { + line_data[line_length++] = filedata[i]; + } + } + + if (i >= MAX_INI_LINE_LEN) { + FTS_TEST_ERROR("line length(%d)>max(%d)", line_length, MAX_INI_LINE_LEN); + return -ENODATA; + } + + /* remove space */ + str_space_remove(line_data); + + /* confirm line type */ + if (('\0' == line_data[0]) || ('#' == line_data[0])) { + type = LINE_OTHER; + } else if ('[' == line_data[0]) { + type = LINE_SECTION; + } else { + type = LINE_KEYWORD; /* key word */ + } + + *line_len = line_length; + return type; +} + +static int ini_parse_keyword(struct ini_data *ini, char *line_buffer) +{ + int i = 0; + int offset = 0; + int length = strlen(line_buffer); + struct ini_section *section = NULL; + + for (i = 0; i < length; i++) { + if (line_buffer[i] == '=') + break; + } + + if ((i == 0) || (i >= length)) { + FTS_TEST_ERROR("mark(=)in keyword line fail"); + return -ENODATA; + } + + if ((ini->section_num > 0) && (ini->section_num < MAX_INI_SECTION_NUM)) { + section = &ini->section[ini->section_num - 1]; + } + + if (NULL == section) { + FTS_TEST_ERROR("section is null"); + return -ENODATA; + } + + offset = ini->keyword_num_total; + if (offset > MAX_KEYWORD_NUM) { + FTS_TEST_ERROR("keyword num(%d)>max(%d),please check MAX_KEYWORD_NUM", + ini->keyword_num_total, MAX_KEYWORD_NUM); + return -ENODATA; + } + memcpy(ini->tmp[offset].name, &line_buffer[0], i); + ini->tmp[offset].name[i] = '\0'; + memcpy(ini->tmp[offset].value, &line_buffer[i + 1], length - i - 1); + ini->tmp[offset].value[length - i - 1] = '\0'; + section->keyword_num++; + ini->keyword_num_total++; + + return 0; +} + +static int ini_parse_section(struct ini_data *ini, char *line_buffer) +{ + int length = strlen(line_buffer); + struct ini_section *section = NULL; + + if ((length <= 2) || (length > MAX_KEYWORD_NAME_LEN)) { + FTS_TEST_ERROR("section line length fail"); + return -EINVAL; + } + + if ((ini->section_num < 0) || (ini->section_num >= MAX_INI_SECTION_NUM)) { + FTS_TEST_ERROR("section_num(%d) fail", ini->section_num); + return -EINVAL; + } + section = &ini->section[ini->section_num]; + memcpy(section->name, line_buffer + 1, length - 2); + section->name[length - 2] = '\0'; + FTS_TEST_INFO("section:%s, keyword offset:%d", + section->name, ini->keyword_num_total); + section->keyword = (struct ini_keyword *)&ini->tmp[ini->keyword_num_total]; + section->keyword_num = 0; + ini->section_num++; + if (ini->section_num > MAX_INI_SECTION_NUM) { + FTS_TEST_ERROR("section num(%d)>max(%d), please check MAX_INI_SECTION_NUM", + ini->section_num, MAX_INI_SECTION_NUM); + return -ENOMEM; + } + + return 0; +} + +static int ini_init_inidata(struct ini_data *ini) +{ + int pos = 0; + int ret = 0; + char line_buffer[MAX_INI_LINE_LEN] = { 0 }; + int line_len = 0; + + if (!ini || !ini->data || !ini->tmp) { + FTS_TEST_DBG("ini/data/tmp is null"); + return -EINVAL; + } + + while (pos < ini->length) { + ret = ini_get_line(ini->data + pos, line_buffer, &line_len); + if (ret < 0) { + FTS_TEST_ERROR("ini_get_line fail"); + return ret; + } else if (ret == LINE_KEYWORD) { + ret = ini_parse_keyword(ini, line_buffer); + if (ret < 0) { + FTS_TEST_ERROR("ini_parse_keyword fail"); + return ret; + } + } else if (ret == LINE_SECTION) { + ret = ini_parse_section(ini, line_buffer); + if (ret < 0) { + FTS_TEST_ERROR("ini_parse_section fail"); + return ret; + } + } + + pos += line_len; + } + + print_ini_data(ini); + return 0; +} + +static int ini_get_key(char *section_name, char *key_name, char *value) +{ + int i = 0; + int j = 0; + struct ini_data *ini = &fts_ftest->ini; + struct ini_section *section; + struct ini_keyword *keyword; + int key_len = 0; + int log_level = fts_ftest->ts_data->log_level; + + if (log_level >= 10) { + FTS_TEST_DBG("section name:%s, key name:%s", section_name, key_name); + FTS_TEST_DBG("section num:%d", ini->section_num); + } + + for (i = 0; i < ini->section_num; i++) { + section = &ini->section[i]; + key_len = strlen(section_name); + if (key_len != strlen(section->name)) + continue; + if (fts_strncmp(section->name, section_name, key_len) != 0) + continue; + + if (log_level >= 10) { + FTS_TEST_DBG("section name:%s keyword num:%d", + section->name, section->keyword_num); + } + for (j = 0; j < section->keyword_num; j++) { + keyword = §ion->keyword[j]; + key_len = strlen(key_name); + if (key_len == strlen(keyword->name)) { + if (0 == fts_strncmp(keyword->name, key_name, key_len)) { + key_len = strlen(keyword->value); + memcpy(value, keyword->value, key_len); + if (log_level >= 3) { + FTS_TEST_DBG("section:%s,%s=%s", + section_name, key_name, value); + } + + return key_len; + } + } + } + } + + return -ENODATA; +} + +/* return keyword's value length if success */ +static int ini_get_string_value(char *section_name, char *key_name, char *rval) +{ + if (!section_name || !key_name || !rval) { + FTS_TEST_ERROR("section_name/key_name/rval is null"); + return -EINVAL; + } + + return ini_get_key(section_name, key_name, rval); +} + +int get_keyword_value(char *section, char *name, int *value) +{ + int ret = 0; + char str[MAX_KEYWORD_VALUE_LEN] = { 0 }; + + ret = ini_get_string_value(section, name, str); + if (ret > 0) { + /* search successfully, so change value, otherwise keep default */ + *value = fts_atoi(str); + } + + return ret; +} + +static void fts_init_buffer(int *buffer, int value, int len, bool key_check, int key_value, int key_len) +{ + int i = 0; + int va_len = 0; + + if (NULL == buffer) { + FTS_TEST_ERROR("buffer is null\n"); + return; + } + + va_len = len - key_len; + if (va_len < 0) { + FTS_TEST_ERROR("total len(0x%x) less key len(0x%x)\n", len, key_len); + return; + } + + for (i = 0; i < len; i++) { + buffer[i] = value; + } + + if (key_check) { + for (i = 0; i < key_len; i++) { + buffer[va_len + i] = key_value; + } + } + +} + +static int get_test_item(char name[][MAX_KEYWORD_NAME_LEN], int length, int *val) +{ + int i = 0; + int ret = 0; + int tmpval = 0; + + if (length > TEST_ITEM_COUNT_MAX) { + FTS_TEST_SAVE_ERR("test item count(%d) > max(%d)\n", + length, TEST_ITEM_COUNT_MAX); + return -EINVAL; + } + + FTS_TEST_INFO("test items in total of driver:%d", length); + *val = 0; + for (i = 0; i < length; i++) { + tmpval = 0; + ret = get_value_testitem(name[i], &tmpval); + if (ret < 0) { + FTS_TEST_DBG("test item:%s not found", name[i]); + } else { + FTS_TEST_DBG("test item:%s=%d", name[i], tmpval); + *val |= (tmpval << i); + } + } + + return 0; +} + +static int get_basic_threshold(char name[][MAX_KEYWORD_NAME_LEN], int length, int *val) +{ + int i = 0; + int ret = 0; + struct fts_test *tdata = fts_ftest; + int log_level = tdata->ts_data->log_level; + + FTS_TEST_INFO("basic_thr string length(%d), count(%d)\n", length, tdata->basic_thr_count); + if (length > fts_ftest->basic_thr_count) { + FTS_TEST_SAVE_ERR("basic_thr string length > count\n"); + return -EINVAL; + } + + for (i = 0; i < length; i++) { + ret = get_value_basic(name[i], &val[i]); + if (log_level >= 3) { + if (ret < 0) { + FTS_TEST_DBG("basic thr:%s not found", name[i]); + } else { + FTS_TEST_DBG("basic thr:%s=%d", name[i], val[i]); + } + } + } + + return 0; +} + +static void get_detail_threshold(char *key_name, bool is_prex, int *thr, int node_num) +{ + char str[MAX_KEYWORD_VALUE_LEN] = { 0 }; + char str_temp[MAX_KEYWORD_NAME_LEN] = { 0 }; + char str_tmp[MAX_KEYWORD_VALUE_ONE_LEN] = { 0 }; + struct fts_test *tdata = fts_ftest; + int divider_pos = 0; + int index = 0; + int i = 0; + int j = 0; + int k = 0; + int tx_num = 0; + int rx_num = 0; + int thr_pos = 0; + + if (!key_name || !thr) { + FTS_TEST_ERROR("key_name/thr is null"); + return; + } + + if (is_prex) { + tx_num = tdata->node.tx_num; + rx_num = tdata->node.rx_num; + } + for (i = 0; i < tx_num + 1; i++) { + if (is_prex) { + snprintf(str_temp, MAX_KEYWORD_NAME_LEN, "%s%d", key_name, (i + 1)); + } else { + snprintf(str_temp, MAX_KEYWORD_NAME_LEN, "%s", key_name); + } + divider_pos = ini_get_string_value("SpecialSet", str_temp, str); + if (divider_pos <= 0) + continue; + index = 0; + k = 0; + memset(str_tmp, 0, sizeof(str_tmp)); + for (j = 0; j < divider_pos; j++) { + if (',' == str[j]) { + thr_pos = i * rx_num + k; + if (thr_pos >= node_num) { + FTS_TEST_ERROR("key:%s %d,dthr_num(%d>=%d) fail", + key_name, i, thr_pos, node_num); + break; + } + thr[thr_pos] = (int)(fts_atoi(str_tmp)); + index = 0; + memset(str_tmp, 0x00, sizeof(str_tmp)); + k++; + } else { + if (' ' == str[j]) + continue; + str_tmp[index] = str[j]; + index++; + } + } + } +} + +static int init_node_valid(void) +{ + char str[MAX_KEYWORD_NAME_LEN] = {0}; + int i = 0; + int j = 0; + int chy = 0; + int node_num = 0; + int cnt = 0; + struct fts_test *tdata = fts_ftest; + + if (!tdata || !tdata->node_valid || !tdata->node_valid_sc) { + FTS_TEST_ERROR("tdata/node_valid/node_valid_sc is null"); + return -EINVAL; + } + + chy = tdata->node.rx_num; + node_num = tdata->node.node_num; + fts_init_buffer(tdata->node_valid, 1 , node_num, false, 0, 0); + if ((tdata->func->hwtype == IC_HW_INCELL) || (tdata->func->hwtype == IC_HW_MC_SC)) { + for (cnt = 0; cnt < node_num; cnt++) { + i = cnt / chy + 1; + j = cnt % chy + 1; + snprintf(str, MAX_KEYWORD_NAME_LEN, "InvalidNode[%d][%d]", i, j); + get_keyword_value("INVALID_NODE", str, &tdata->node_valid[cnt]); + } + } + + if (tdata->func->hwtype == IC_HW_MC_SC) { + chy = tdata->sc_node.rx_num; + node_num = tdata->sc_node.node_num; + fts_init_buffer(tdata->node_valid_sc, 1, node_num, false, 0, 0); + + for (cnt = 0; cnt < node_num; cnt++) { + i = (cnt >= chy) ? 2 : 1; + j = (cnt >= chy) ? (cnt - chy + 1) : (cnt + 1); + snprintf(str, MAX_KEYWORD_NAME_LEN, "InvalidNodeS[%d][%d]", i, j); + get_keyword_value("INVALID_NODES", str, &tdata->node_valid_sc[cnt]); + } + } + + print_buffer(tdata->node_valid, tdata->node.node_num, tdata->node.rx_num); + print_buffer(tdata->node_valid_sc, tdata->sc_node.node_num, tdata->sc_node.rx_num); + return 0; +} + +/* incell */ +static int get_test_item_incell(void) +{ + int ret = 0; + char item_name[][MAX_KEYWORD_NAME_LEN] = TEST_ITEM_INCELL; + int length = sizeof(item_name) / MAX_KEYWORD_NAME_LEN; + int item_val = 0; + + ret = get_test_item(item_name, length, &item_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get test item fail\n"); + return ret; + } + + fts_ftest->ic.incell.u.tmp = item_val; + return 0; +} + +static char bthr_name_incell[][MAX_KEYWORD_NAME_LEN] = BASIC_THRESHOLD_INCELL; +static int get_test_threshold_incell(void) +{ + int ret = 0; + int length = sizeof(bthr_name_incell) / MAX_KEYWORD_NAME_LEN; + struct fts_test *tdata = fts_ftest; + struct incell_threshold *thr = &tdata->ic.incell.thr; + int node_num = tdata->node.node_num; + int key_num = tdata->node.key_num; + bool raw_key_check = thr->basic.rawdata_vkey_check; + bool cb_key_check = thr->basic.cb_vkey_check; + + tdata->basic_thr_count = sizeof(struct incell_threshold_b) / sizeof(int); + /* get standard basic threshold */ + ret = get_basic_threshold(bthr_name_incell, length, (int *)&thr->basic); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get basic thr fail\n"); + return ret; + } + + /* basic special set by ic */ + if (tdata->func->param_init) { + ret = tdata->func->param_init(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("special basic thr init fail\n"); + return ret; + } + } + + /* init buffer */ + fts_init_buffer(thr->rawdata_max, thr->basic.rawdata_max, node_num, raw_key_check, thr->basic.rawdata_max_vk, key_num); + fts_init_buffer(thr->rawdata_min, thr->basic.rawdata_min, node_num, raw_key_check, thr->basic.rawdata_min_vk, key_num); + if (tdata->func->rawdata2_support) { + fts_init_buffer(thr->rawdata2_max, thr->basic.rawdata2_max, node_num, false, 0, 0); + fts_init_buffer(thr->rawdata2_min, thr->basic.rawdata2_min, node_num, false, 0, 0); + } + fts_init_buffer(thr->cb_max, thr->basic.cb_max, node_num, cb_key_check, thr->basic.cb_max_vk, key_num); + fts_init_buffer(thr->cb_min, thr->basic.cb_min, node_num, cb_key_check, thr->basic.cb_min_vk, key_num); + + /* detail threshold */ + get_detail_threshold("RawData_Max_Tx", true, thr->rawdata_max, node_num); + get_detail_threshold("RawData_Min_Tx", true, thr->rawdata_min, node_num); + get_detail_threshold("CB_Max_Tx", true, thr->cb_max, node_num); + get_detail_threshold("CB_Min_Tx", true, thr->cb_min, node_num); + + return 0; +} + +static void print_thr_incell(void) +{ + struct fts_test *tdata = fts_ftest; + struct incell_threshold *thr = &tdata->ic.incell.thr; + + if (tdata->ts_data->log_level < 3) { + return; + } + + FTS_TEST_DBG("short_res_min:%d", thr->basic.short_res_min); + FTS_TEST_DBG("short_res_vk_min:%d", thr->basic.short_res_vk_min); + FTS_TEST_DBG("open_cb_min:%d", thr->basic.open_cb_min); + FTS_TEST_DBG("open_k1_check:%d", thr->basic.open_k1_check); + FTS_TEST_DBG("open_k1_value:%d", thr->basic.open_k1_value); + FTS_TEST_DBG("open_k2_check:%d", thr->basic.open_k2_check); + FTS_TEST_DBG("open_k2_value:%d", thr->basic.open_k2_value); + FTS_TEST_DBG("cb_min:%d", thr->basic.cb_min); + FTS_TEST_DBG("cb_max:%d", thr->basic.cb_max); + FTS_TEST_DBG("cb_vkey_check:%d", thr->basic.cb_vkey_check); + FTS_TEST_DBG("cb_min_vk:%d", thr->basic.cb_min_vk); + FTS_TEST_DBG("cb_max_vk:%d", thr->basic.cb_max_vk); + FTS_TEST_DBG("rawdata_min:%d", thr->basic.rawdata_min); + FTS_TEST_DBG("rawdata_max:%d", thr->basic.rawdata_max); + FTS_TEST_DBG("rawdata_vkey_check:%d", thr->basic.rawdata_vkey_check); + FTS_TEST_DBG("rawdata_min_vk:%d", thr->basic.rawdata_min_vk); + FTS_TEST_DBG("rawdata_max_vk:%d", thr->basic.rawdata_max_vk); + FTS_TEST_DBG("lcdnoise_frame:%d", thr->basic.lcdnoise_frame); + FTS_TEST_DBG("lcdnoise_coefficient:%d", thr->basic.lcdnoise_coefficient); + FTS_TEST_DBG("lcdnoise_coefficient_vkey:%d", thr->basic.lcdnoise_coefficient_vkey); + FTS_TEST_DBG("open_diff_min:%d", thr->basic.open_diff_min); + + FTS_TEST_DBG("open_nmos:%d", thr->basic.open_nmos); + FTS_TEST_DBG("keyshort_k1:%d", thr->basic.keyshort_k1); + FTS_TEST_DBG("keyshort_cb_max:%d", thr->basic.keyshort_cb_max); + FTS_TEST_DBG("rawdata2_min:%d", thr->basic.rawdata2_min); + FTS_TEST_DBG("rawdata2_max:%d", thr->basic.rawdata2_max); + FTS_TEST_DBG("mux_open_cb_min:%d", thr->basic.mux_open_cb_min); + FTS_TEST_DBG("open_delta_V:%d", thr->basic.open_delta_V); + + print_buffer(thr->rawdata_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->cb_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->cb_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata2_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata2_max, tdata->node.node_num, tdata->node.rx_num); +} + +static int ini_init_test_incell(void) +{ + int ret = 0; + + ret = get_test_item_incell(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get incell test item fail\n"); + return ret; + } + + + ret = get_test_threshold_incell(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get incell threshold fail\n"); + return ret; + } + + print_thr_incell(); + return 0; +} + +/* mc_sc */ +static int get_test_item_mc_sc(void) +{ + int ret = 0; + char item_name[][MAX_KEYWORD_NAME_LEN] = TEST_ITEM_MC_SC; + int length = sizeof(item_name) / MAX_KEYWORD_NAME_LEN; + int item_val = 0; + + ret = get_test_item(item_name, length, &item_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get test item fail\n"); + return ret; + } + + fts_ftest->ic.mc_sc.u.tmp = item_val; + FTS_TEST_INFO("test item:0x%x in ini", fts_ftest->ic.mc_sc.u.tmp); + return 0; +} + +static char bthr_name_mc_sc[][MAX_KEYWORD_NAME_LEN] = BASIC_THRESHOLD_MC_SC; +static int get_test_threshold_mc_sc(void) +{ + int ret = 0; + int length = sizeof(bthr_name_mc_sc) / MAX_KEYWORD_NAME_LEN; + struct fts_test *tdata = fts_ftest; + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + int node_num = tdata->node.node_num; + int sc_num = tdata->sc_node.node_num; + + tdata->basic_thr_count = sizeof(struct mc_sc_threshold_b) / sizeof(int); + /* get standard basic threshold */ + ret = get_basic_threshold(bthr_name_mc_sc, length, (int *)&thr->basic); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get basic thr fail\n"); + return ret; + } + + /* basic special set by ic */ + if (tdata->func->param_init) { + ret = tdata->func->param_init(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("special basic thr init fail\n"); + return ret; + } + } + + /* init buffer */ + fts_init_buffer(thr->rawdata_h_min, thr->basic.rawdata_h_min, node_num, false, 0, 0); + fts_init_buffer(thr->rawdata_h_max, thr->basic.rawdata_h_max, node_num, false, 0, 0); + if (tdata->func->rawdata2_support) { + fts_init_buffer(thr->rawdata_l_min, thr->basic.rawdata_l_min, node_num, false, 0, 0); + fts_init_buffer(thr->rawdata_l_max, thr->basic.rawdata_l_max, node_num, false, 0, 0); + } + fts_init_buffer(thr->tx_linearity_max, thr->basic.uniformity_tx_hole, node_num, false, 0, 0); + fts_init_buffer(thr->tx_linearity_min, 0, node_num, false, 0, 0); + fts_init_buffer(thr->rx_linearity_max, thr->basic.uniformity_rx_hole, node_num, false, 0, 0); + fts_init_buffer(thr->rx_linearity_min, 0, node_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_off_min, thr->basic.scap_cb_off_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_off_max, thr->basic.scap_cb_off_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_on_min, thr->basic.scap_cb_on_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_on_max, thr->basic.scap_cb_on_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_hi_min, thr->basic.scap_cb_hi_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_hi_max, thr->basic.scap_cb_hi_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_hov_min, thr->basic.scap_cb_hov_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_cb_hov_max, thr->basic.scap_cb_hov_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_off_min, thr->basic.scap_rawdata_off_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_off_max, thr->basic.scap_rawdata_off_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_on_min, thr->basic.scap_rawdata_on_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_on_max, thr->basic.scap_rawdata_on_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_hi_min, thr->basic.scap_rawdata_hi_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_hi_max, thr->basic.scap_rawdata_hi_max, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_hov_min, thr->basic.scap_rawdata_hov_min, sc_num, false, 0, 0); + fts_init_buffer(thr->scap_rawdata_hov_max, thr->basic.scap_rawdata_hov_max, sc_num, false, 0, 0); + fts_init_buffer(thr->panel_differ_min, thr->basic.panel_differ_min, node_num, false, 0, 0); + fts_init_buffer(thr->panel_differ_max, thr->basic.panel_differ_max, node_num, false, 0, 0); + + /* detail threshold */ + get_detail_threshold("RawData_Min_High_Tx", true, thr->rawdata_h_min, node_num); + get_detail_threshold("RawData_Max_High_Tx", true, thr->rawdata_h_max, node_num); + if (tdata->func->rawdata2_support) { + get_detail_threshold("RawData_Min_Low_Tx", true, thr->rawdata_l_min, node_num); + get_detail_threshold("RawData_Max_Low_Tx", true, thr->rawdata_l_max, node_num); + } + get_detail_threshold("Tx_Linearity_Max_Tx", true, thr->tx_linearity_max, node_num); + get_detail_threshold("Rx_Linearity_Max_Tx", true, thr->rx_linearity_max, node_num); + get_detail_threshold("ScapCB_OFF_Min_", true, thr->scap_cb_off_min, sc_num); + get_detail_threshold("ScapCB_OFF_Max_", true, thr->scap_cb_off_max, sc_num); + get_detail_threshold("ScapCB_ON_Min_", true, thr->scap_cb_on_min, sc_num); + get_detail_threshold("ScapCB_ON_Max_", true, thr->scap_cb_on_max, sc_num); + get_detail_threshold("ScapCB_High_Min_", true, thr->scap_cb_hi_min, sc_num); + get_detail_threshold("ScapCB_High_Max_", true, thr->scap_cb_hi_max, sc_num); + get_detail_threshold("ScapCB_Hov_Min_", true, thr->scap_cb_hov_min, sc_num); + get_detail_threshold("ScapCB_Hov_Max_", true, thr->scap_cb_hov_max, sc_num); + get_detail_threshold("ScapRawData_OFF_Min_", true, thr->scap_rawdata_off_min, sc_num); + get_detail_threshold("ScapRawData_OFF_Max_", true, thr->scap_rawdata_off_max, sc_num); + get_detail_threshold("ScapRawData_ON_Min_", true, thr->scap_rawdata_on_min, sc_num); + get_detail_threshold("ScapRawData_ON_Max_", true, thr->scap_rawdata_on_max, sc_num); + get_detail_threshold("ScapRawData_High_Min_", true, thr->scap_rawdata_hi_min, sc_num); + get_detail_threshold("ScapRawData_High_Max_", true, thr->scap_rawdata_hi_max, sc_num); + get_detail_threshold("ScapRawData_Hov_Min_", true, thr->scap_rawdata_hov_min, sc_num); + get_detail_threshold("ScapRawData_Hov_Max_", true, thr->scap_rawdata_hov_max, sc_num); + get_detail_threshold("Panel_Differ_Min_Tx", true, thr->panel_differ_min, node_num); + get_detail_threshold("Panel_Differ_Max_Tx", true, thr->panel_differ_max, node_num); + + return 0; +} + +static void print_thr_mc_sc(void) +{ + struct fts_test *tdata = fts_ftest; + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + + if (tdata->ts_data->log_level < 3) { + return; + } + + FTS_TEST_DBG("rawdata_h_min:%d", thr->basic.rawdata_h_min); + FTS_TEST_DBG("rawdata_h_max:%d", thr->basic.rawdata_h_max); + FTS_TEST_DBG("rawdata_set_hfreq:%d", thr->basic.rawdata_set_hfreq); + FTS_TEST_DBG("rawdata_l_min:%d", thr->basic.rawdata_l_min); + FTS_TEST_DBG("rawdata_l_max:%d", thr->basic.rawdata_l_max); + FTS_TEST_DBG("rawdata_set_lfreq:%d", thr->basic.rawdata_set_lfreq); + FTS_TEST_DBG("uniformity_check_tx:%d", thr->basic.uniformity_check_tx); + FTS_TEST_DBG("uniformity_check_rx:%d", thr->basic.uniformity_check_rx); + FTS_TEST_DBG("uniformity_check_min_max:%d", thr->basic.uniformity_check_min_max); + FTS_TEST_DBG("uniformity_tx_hole:%d", thr->basic.uniformity_tx_hole); + FTS_TEST_DBG("uniformity_rx_hole:%d", thr->basic.uniformity_rx_hole); + FTS_TEST_DBG("uniformity_min_max_hole:%d", thr->basic.uniformity_min_max_hole); + FTS_TEST_DBG("scap_cb_off_min:%d", thr->basic.scap_cb_off_min); + FTS_TEST_DBG("scap_cb_off_max:%d", thr->basic.scap_cb_off_max); + FTS_TEST_DBG("scap_cb_wp_off_check:%d", thr->basic.scap_cb_wp_off_check); + FTS_TEST_DBG("scap_cb_on_min:%d", thr->basic.scap_cb_on_min); + FTS_TEST_DBG("scap_cb_on_max:%d", thr->basic.scap_cb_on_max); + FTS_TEST_DBG("scap_cb_wp_on_check:%d", thr->basic.scap_cb_wp_on_check); + FTS_TEST_DBG("scap_rawdata_off_min:%d", thr->basic.scap_rawdata_off_min); + FTS_TEST_DBG("scap_rawdata_off_max:%d", thr->basic.scap_rawdata_off_max); + FTS_TEST_DBG("scap_rawdata_wp_off_check:%d", thr->basic.scap_rawdata_wp_off_check); + FTS_TEST_DBG("scap_rawdata_on_min:%d", thr->basic.scap_rawdata_on_min); + FTS_TEST_DBG("scap_rawdata_on_max:%d", thr->basic.scap_rawdata_on_max); + FTS_TEST_DBG("scap_rawdata_wp_on_check:%d", thr->basic.scap_rawdata_wp_on_check); + FTS_TEST_DBG("short_cg:%d", thr->basic.short_cg); + FTS_TEST_DBG("short_cc:%d", thr->basic.short_cc); + FTS_TEST_DBG("panel_differ_min:%d", thr->basic.panel_differ_min); + FTS_TEST_DBG("panel_differ_max:%d", thr->basic.panel_differ_max); + + print_buffer(thr->rawdata_h_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_h_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_l_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_l_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->tx_linearity_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rx_linearity_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->scap_cb_off_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_off_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_on_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_on_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_hi_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_hi_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_hov_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_cb_hov_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_off_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_off_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_on_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_on_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_hi_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_hi_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_hov_min, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->scap_rawdata_hov_max, tdata->sc_node.node_num, tdata->sc_node.rx_num); + print_buffer(thr->panel_differ_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->panel_differ_max, tdata->node.node_num, tdata->node.rx_num); +} + +static int ini_init_test_mc_sc(void) +{ + int ret = 0; + + ret = get_test_item_mc_sc(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get mc_sc test item fail\n"); + return ret; + } + + ret = get_test_threshold_mc_sc(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get mc_sc threshold fail\n"); + return ret; + } + + print_thr_mc_sc(); + return 0; +} + +/* sc */ +static int get_test_item_sc(void) +{ + int ret = 0; + char item_name[][MAX_KEYWORD_NAME_LEN] = TEST_ITEM_SC; + int length = sizeof(item_name) / MAX_KEYWORD_NAME_LEN; + int item_val = 0; + + ret = get_test_item(item_name, length, &item_val); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get test item fail\n"); + return ret; + } + + fts_ftest->ic.sc.u.tmp = item_val; + return 0; +} + +static char bthr_name_sc[][MAX_KEYWORD_NAME_LEN] = BASIC_THRESHOLD_SC; +static int get_test_threshold_sc(void) +{ + int ret = 0; + int length = sizeof(bthr_name_sc) / MAX_KEYWORD_NAME_LEN; + struct fts_test *tdata = fts_ftest; + struct sc_threshold *thr = &tdata->ic.sc.thr; + int node_num = tdata->node.node_num; + + tdata->basic_thr_count = sizeof(struct sc_threshold_b) / sizeof(int); + /* get standard basic threshold */ + ret = get_basic_threshold(bthr_name_sc, length, (int *)&thr->basic); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get basic thr fail\n"); + return ret; + } + + /* basic special set by ic */ + if (tdata->func->param_init) { + ret = tdata->func->param_init(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("special basic thr init fail\n"); + return ret; + } + } + + /* init buffer */ + fts_init_buffer(thr->rawdata_min, thr->basic.rawdata_min, node_num, false, 0, 0); + fts_init_buffer(thr->rawdata_max, thr->basic.rawdata_max, node_num, false, 0, 0); + fts_init_buffer(thr->cb_min, thr->basic.cb_min, node_num, false, 0, 0); + fts_init_buffer(thr->cb_max, thr->basic.cb_max, node_num, false, 0, 0); + fts_init_buffer(thr->dcb_sort, 0, node_num, false, 0, 0); + fts_init_buffer(thr->dcb_base, thr->basic.dcb_base, node_num, false, 0, 0); + + /* detail threshold */ + get_detail_threshold("RawDataTest_Min", false, thr->rawdata_min, node_num); + get_detail_threshold("RawDataTest_Max", false, thr->rawdata_max, node_num); + get_detail_threshold("CbTest_Min", false, thr->cb_min, node_num); + get_detail_threshold("CbTest_Max", false, thr->cb_max, node_num); + get_detail_threshold("DeltaCxTest_Sort", false, thr->dcb_sort, node_num); + get_detail_threshold("DeltaCbTest_Base", false, thr->dcb_base, node_num); + + return 0; +} + +static void print_thr_sc(void) +{ + struct fts_test *tdata = fts_ftest; + struct sc_threshold *thr = &tdata->ic.sc.thr; + + if (tdata->ts_data->log_level < 3) { + return; + } + + FTS_TEST_DBG("rawdata_min:%d", thr->basic.rawdata_min); + FTS_TEST_DBG("rawdata_max:%d", thr->basic.rawdata_max); + FTS_TEST_DBG("cb_min:%d", thr->basic.cb_min); + FTS_TEST_DBG("cb_max:%d", thr->basic.cb_max); + FTS_TEST_DBG("dcb_differ_max:%d", thr->basic.dcb_differ_max); + FTS_TEST_DBG("dcb_key_check:%d", thr->basic.dcb_key_check); + FTS_TEST_DBG("dcb_key_differ_max:%d", thr->basic.dcb_key_differ_max); + FTS_TEST_DBG("dcb_ds1:%d", thr->basic.dcb_ds1); + FTS_TEST_DBG("dcb_ds2:%d", thr->basic.dcb_ds2); + FTS_TEST_DBG("dcb_ds3:%d", thr->basic.dcb_ds3); + FTS_TEST_DBG("dcb_ds4:%d", thr->basic.dcb_ds4); + FTS_TEST_DBG("dcb_ds5:%d", thr->basic.dcb_ds5); + FTS_TEST_DBG("dcb_ds6:%d", thr->basic.dcb_ds6); + FTS_TEST_DBG("dcb_critical_check:%d", thr->basic.dcb_critical_check); + FTS_TEST_DBG("dcb_cs1:%d", thr->basic.dcb_cs1); + FTS_TEST_DBG("dcb_cs2:%d", thr->basic.dcb_cs2); + FTS_TEST_DBG("dcb_cs3:%d", thr->basic.dcb_cs3); + FTS_TEST_DBG("dcb_cs4:%d", thr->basic.dcb_cs4); + FTS_TEST_DBG("dcb_cs5:%d", thr->basic.dcb_cs5); + FTS_TEST_DBG("dcb_cs6:%d", thr->basic.dcb_cs6); + + print_buffer(thr->rawdata_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->rawdata_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->cb_min, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->cb_max, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->dcb_sort, tdata->node.node_num, tdata->node.rx_num); + print_buffer(thr->dcb_base, tdata->node.node_num, tdata->node.rx_num); +} + +static int ini_init_test_sc(void) +{ + int ret = 0; + + ret = get_test_item_sc(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get sc test item fail\n"); + return ret; + } + + ret = get_test_threshold_sc(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get sc threshold fail\n"); + return ret; + } + + print_thr_sc(); + return 0; +} + +static u32 ini_get_ic_code(char *ic_name) +{ + int i = 0; + int type_size = 0; + int ini_icname_len = 0; + int ic_types_len = 0; + + ini_icname_len = strlen(ic_name); + type_size = sizeof(ic_types) / sizeof(ic_types[0]); + for (i = 0; i < type_size; i++) { + ic_types_len = strlen(ic_name); + if (ini_icname_len == ic_types_len) { + if (0 == strncmp(ic_name, ic_types[i].ic_name, ic_types_len)) + return ic_types[i].ic_type; + } + } + + FTS_TEST_ERROR("no IC type match"); + return 0; +} + + +static void ini_init_interface(struct ini_data *ini) +{ + char str[MAX_KEYWORD_VALUE_LEN] = { 0 }; + u32 value = 0; + struct fts_test *tdata = fts_ftest; + + /* IC type */ + ini_get_string_value("Interface", "IC_Type", str); + snprintf(ini->ic_name, MAX_IC_NAME_LEN, "%s", str); + + value = ini_get_ic_code(str); + ini->ic_code = value; + FTS_TEST_INFO("ic name:%s, ic code:%x", ini->ic_name, ini->ic_code); + + if (IC_HW_MC_SC == tdata->func->hwtype) { + get_value_interface("Normalize_Type", &value); + tdata->normalize = (u8)value; + FTS_TEST_DBG("normalize:%d", tdata->normalize); + } +} + +static int ini_init_test(struct ini_data *ini) +{ + int ret = 0; + struct fts_test *tdata = fts_ftest; + + /* interface init */ + ini_init_interface(ini); + + /* node valid */ + ret = init_node_valid(); + if (ret < 0) { + FTS_TEST_ERROR("init node valid fail"); + return ret; + } + + switch (tdata->func->hwtype) { + case IC_HW_INCELL: + ret = ini_init_test_incell(); + break; + case IC_HW_MC_SC: + ret = ini_init_test_mc_sc(); + break; + case IC_HW_SC: + ret = ini_init_test_sc(); + break; + default: + FTS_TEST_SAVE_ERR("test ic type(%d) fail\n", tdata->func->hwtype); + ret = -EINVAL; + break; + } + + return ret; +} + +/* + * fts_test_get_testparam_from_ini - get test parameters from ini + * + * read, parse the configuration file, initialize the test variable + * + * return 0 if succuss, else errro code + */ +int fts_test_get_testparam_from_ini(char *config_name) +{ + int ret = 0; + struct ini_data *ini = &fts_ftest->ini; + + ret = fts_test_get_ini_via_request_firmware(ini, config_name); + if (ret != 0) { + ret = fts_test_get_ini_default(ini, config_name); + if (ret < 0) { + FTS_TEST_ERROR("get ini(default) fail"); + goto get_ini_err; + } + } + + ini->keyword_num_total = 0; + ini->section_num = 0; + + ini->tmp = vmalloc(sizeof(struct ini_keyword) * MAX_KEYWORD_NUM); + if (NULL == ini->tmp) { + FTS_TEST_ERROR("malloc memory for ini tmp fail"); + ret = -ENOMEM; + goto get_ini_err; + } + memset(ini->tmp, 0, sizeof(struct ini_keyword) * MAX_KEYWORD_NUM); + + /* parse ini data to get keyword name&value */ + ret = ini_init_inidata(ini); + if (ret < 0) { + FTS_TEST_ERROR("ini_init_inidata fail"); + goto get_ini_err; + } + + /* parse threshold & test item */ + ret = ini_init_test(ini); + if (ret < 0) { + FTS_TEST_ERROR("ini init fail"); + goto get_ini_err; + } + + ret = 0; +get_ini_err: + if (ini->tmp) { + vfree(ini->tmp); + ini->tmp = NULL; + } + + if (ini->data) { + vfree(ini->data); + ini->data = NULL; + } + + return ret; +} diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/focaltech_test_ini.h b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/focaltech_test_ini.h new file mode 100644 index 000000000000..051393efdd2a --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/focaltech_test_ini.h @@ -0,0 +1,146 @@ +/************************************************************************ +* Copyright (c) 2012-2020, Focaltech Systems (R)£¬All Rights Reserved. +* +* File Name: focaltech_test_ini.h +* +* Author: Focaltech Driver Team +* +* Created: 2016-08-01 +* +* Abstract: parsing function of INI file +* +************************************************************************/ +#ifndef _INI_H +#define _INI_H +/***************************************************************************** +* Private constant and macro definitions using #define +*****************************************************************************/ +#define MAX_KEYWORD_NUM (1000) +#define MAX_KEYWORD_NAME_LEN (50) +#define MAX_KEYWORD_VALUE_LEN (512) +#define MAX_KEYWORD_VALUE_ONE_LEN (16) +#define MAX_INI_LINE_LEN (MAX_KEYWORD_NAME_LEN + MAX_KEYWORD_VALUE_LEN) +#define MAX_INI_SECTION_NUM (20) +#define MAX_IC_NAME_LEN (32) +#define MAX_TEST_ITEM (20) +#define IC_CODE_OFFSET (16) + +/***************************************************************************** +* enumerations, structures and unions +*****************************************************************************/ +struct ini_ic_type { + char ic_name[MAX_IC_NAME_LEN]; + u32 ic_type; +}; + +enum line_type { + LINE_SECTION = 1, + LINE_KEYWORD = 2 , + LINE_OTHER = 3, +}; + +struct ini_keyword { + char name[MAX_KEYWORD_NAME_LEN]; + char value[MAX_KEYWORD_VALUE_LEN]; +}; + +struct ini_section { + char name[MAX_KEYWORD_NAME_LEN]; + int keyword_num; + /* point to ini.tmp, don't need free */ + struct ini_keyword *keyword; +}; + +struct ini_data { + char *data; + int length; + int keyword_num_total; + int section_num; + struct ini_section section[MAX_INI_SECTION_NUM]; + struct ini_keyword *tmp; + char ic_name[MAX_IC_NAME_LEN]; + u32 ic_code; +}; + +#define TEST_ITEM_INCELL { \ + "SHORT_CIRCUIT_TEST", \ + "OPEN_TEST", \ + "CB_TEST", \ + "RAWDATA_TEST", \ + "LCD_NOISE_TEST", \ + "KEY_SHORT_TEST", \ + "MUX_OPEN_TEST", \ +} + +#define BASIC_THRESHOLD_INCELL { \ + "ShortCircuit_ResMin", "ShortCircuit_VkResMin", \ + "OpenTest_CBMin", "OpenTest_Check_K1", "OpenTest_K1Threshold", "OpenTest_Check_K2", "OpenTest_K2Threshold", \ + "CBTest_Min", "CBTest_Max", \ + "CBTest_VKey_Check", "CBTest_Min_Vkey", "CBTest_Max_Vkey", \ + "RawDataTest_Min", "RawDataTest_Max", \ + "RawDataTest_VKey_Check", "RawDataTest_Min_VKey", "RawDataTest_Max_VKey", \ + "LCD_NoiseTest_Frame", "LCD_NoiseTest_Coefficient", "LCD_NoiseTest_Coefficient_key", \ + "OpenTest_DifferMin", \ +} + + +#define TEST_ITEM_MC_SC { \ + "RAWDATA_TEST", \ + "UNIFORMITY_TEST", \ + "SCAP_CB_TEST", \ + "SCAP_RAWDATA_TEST", \ + "WEAK_SHORT_CIRCUIT_TEST", \ + "PANEL_DIFFER_TEST", \ +} + +#define BASIC_THRESHOLD_MC_SC { \ + "RawDataTest_High_Min", "RawDataTest_High_Max", "RawDataTest_HighFreq", \ + "RawDataTest_Low_Min", "RawDataTest_Low_Max", "RawDataTest_LowFreq", \ + "UniformityTest_Check_Tx", "UniformityTest_Check_Rx","UniformityTest_Check_MinMax", \ + "UniformityTest_Tx_Hole", "UniformityTest_Rx_Hole", "UniformityTest_MinMax_Hole", \ + "SCapCbTest_OFF_Min", "SCapCbTest_OFF_Max", "ScapCBTest_SetWaterproof_OFF", \ + "SCapCbTest_ON_Min", "SCapCbTest_ON_Max", "ScapCBTest_SetWaterproof_ON", \ + "SCapRawDataTest_OFF_Min", "SCapRawDataTest_OFF_Max", "SCapRawDataTest_SetWaterproof_OFF", \ + "SCapRawDataTest_ON_Min", "SCapRawDataTest_ON_Max", "SCapRawDataTest_SetWaterproof_ON", \ + "WeakShortTest_CG", "WeakShortTest_CC", \ + "PanelDifferTest_Min", "PanelDifferTest_Max", \ + "SCapCbTest_High_Min", "SCapCbTest_High_Max", "ScapCBTest_SetHighSensitivity", \ + "SCapRawDataTest_High_Min", "SCapRawDataTest_High_Max", "SCapRawDataTest_SetHighSensitivity", \ + "SCapCbTest_Hov_Min", "SCapCbTest_Hov_Max", "ScapCBTest_SetHov", \ + "SCapRawDataTest_Hov_Min", "SCapRawDataTest_Hov_Max", "SCapRawDataTest_SetHov", \ +} + +#define TEST_ITEM_SC { \ + "RAWDATA_TEST", \ + "CB_TEST", \ + "DELTA_CB_TEST", \ + "WEAK_SHORT_TEST", \ +} + +#define BASIC_THRESHOLD_SC { \ + "RawDataTest_Min", "RawDataTest_Max", \ + "CbTest_Min", "CbTest_Max", \ + "DeltaCbTest_Base", "DeltaCbTest_Differ_Max", \ + "DeltaCbTest_Include_Key_Test", "DeltaCbTest_Key_Differ_Max", \ + "DeltaCbTest_Deviation_S1", "DeltaCbTest_Deviation_S2", "DeltaCbTest_Deviation_S3", \ + "DeltaCbTest_Deviation_S4", "DeltaCbTest_Deviation_S5", "DeltaCbTest_Deviation_S6", \ + "DeltaCbTest_Set_Critical", "DeltaCbTest_Critical_S1", "DeltaCbTest_Critical_S2", \ + "DeltaCbTest_Critical_S3", "DeltaCbTest_Critical_S4", \ + "DeltaCbTest_Critical_S5", "DeltaCbTest_Critical_S6", \ +} + +/***************************************************************************** +* Global variable or extern global variabls/functions +*****************************************************************************/ +int fts_test_get_testparam_from_ini(char *config_name); +int get_keyword_value(char *section, char *name, int *value); + +#define get_value_interface(name, value) \ + get_keyword_value("Interface", name, value) +#define get_value_basic(name, value) \ + get_keyword_value("Basic_Threshold", name, value) +#define get_value_detail(name, value) \ + get_keyword_value("SpecialSet", name, value) +#define get_value_testitem(name, value) \ + get_keyword_value("TestItem", name, value) +#endif /* _INI_H */ diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/supported_ic/Makefile b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/supported_ic/Makefile new file mode 100644 index 000000000000..253e346ced7f --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/supported_ic/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_TOUCHSCREEN_FT3519) += focaltech_test_ft5452i.o \ No newline at end of file diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/supported_ic/focaltech_test_ft5452i.c b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/supported_ic/focaltech_test_ft5452i.c new file mode 100644 index 000000000000..fc5c73becf1e --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_test/supported_ic/focaltech_test_ft5452i.c @@ -0,0 +1,1488 @@ +/************************************************************************ +* Copyright (c) 2012-2020, Focaltech Systems (R), All Rights Reserved. +* +* File Name: Focaltech_test_ft5452.c +* +* Author: Focaltech Driver Team +* +* Created: 2018-03-08 +* +* Abstract: +* +************************************************************************/ + +/***************************************************************************** +* included header files +*****************************************************************************/ +#include "../focaltech_test.h" + +/***************************************************************************** +* private constant and macro definitions using #define +*****************************************************************************/ +static int short_test_channel_to_all( + struct fts_test *tdata, + int *ca_short_resistance, + int *adc_data, + int offset, + bool *result) +{ + int ret = 0; + int i = 0; + int err_ch_num = 0; + int tx_num = 0; + int rx_num = 0; + int ch_num = 0; + int code_1 = 1437; + int code = 0; + struct mc_sc_threshold *thr = &fts_ftest->ic.mc_sc.thr; + int min_cc = thr->basic.short_cc; + int numerator = 0; + int denominator = 0; + + FTS_TEST_FUNC_ENTER(); + *result = false; + tx_num = tdata->sc_node.tx_num; + rx_num = tdata->sc_node.rx_num; + ch_num = tx_num + rx_num; + + /*get adc data for channel to all*/ + ret = fts_test_write_reg(FACTROY_REG_SHORT_RES_LEVEL, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 5A fails,ret:%d\n", ret); + goto TEST_END; + } + + ret = short_get_adc_data_mc(TEST_RETVAL_00, (ch_num + 1) * 2, \ + adc_data, FACTROY_REG_SHORT_CA); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get adc data of ca fails,ret:%d\n", ret); + goto TEST_END; + } + + for (i = 0; i < ch_num; i++) { + code = adc_data[i]; + denominator = code_1 - code; + if (denominator <= 0) { + ca_short_resistance[i] = min_cc; + } else { + numerator = (code - offset + 395) * 111; + ca_short_resistance[i] = fts_abs(numerator / denominator - 3); + } + + if (ca_short_resistance[i] < min_cc) { + err_ch_num++; + } + } + + if (err_ch_num > 0) { + /* print all Adc value */ + FTS_TEST_SAVE_INFO("Offset:%d, Code1:%d\n", offset, code_1); + print_buffer(adc_data, ch_num, ch_num); + *result = false; + } else { + *result = true; + } + +TEST_END: + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int short_test_channel_to_gnd( + struct fts_test *tdata, + int *ca_short_resistance, + int *adc_data, + int offset) +{ + int ret = 0; + int error_num = 0; + int min_70k_num = 0; + int ch_num = 0; + int tx_num = 0; + int rx_num = 0; + int min_cg = 0; + int min_cc = 0; + int i = 0; + int code_1 = 1437; + int code = 0; + int numerator = 0; + int denominator = 0; + int tmp_res = 0; + bool is_short_gnd = false; + int *cg_short_resistance = NULL; + int *tmp_adc_data = NULL; + u8 *w_buf = NULL; + u8 *error_ch = NULL; + u8 *min_70k_ch = NULL; + struct mc_sc_threshold *thr = &fts_ftest->ic.mc_sc.thr; + + FTS_TEST_FUNC_ENTER(); + tx_num = tdata->sc_node.tx_num; + rx_num = tdata->sc_node.rx_num; + ch_num = tx_num + rx_num; + min_cc = thr->basic.short_cc; + min_cg = thr->basic.short_cg; + + /*store channels of abnormal*/ + error_ch = fts_malloc(ch_num * sizeof(u8)); + if (NULL == error_ch) { + FTS_TEST_SAVE_ERR("error_ch buffer malloc fail\n"); + ret = -ENOMEM; + goto TEST_END; + } + + /*store channels of resistor which are less than 70k*/ + min_70k_ch = fts_malloc(ch_num * sizeof(u8)); + if (NULL == min_70k_ch) { + FTS_TEST_SAVE_ERR("min_70k_ch buffer malloc fail\n"); + ret = -ENOMEM; + goto TEST_END; + } + + w_buf = fts_malloc((ch_num + 1) * sizeof(u8)); + if (NULL == w_buf) { + FTS_TEST_SAVE_ERR("w_buf buffer malloc fail\n"); + ret = -ENOMEM; + goto TEST_END; + } + + tmp_adc_data = fts_malloc((ch_num + 1) * sizeof(int)); + if (NULL == tmp_adc_data) { + FTS_TEST_SAVE_ERR("adc_data buffer malloc fail\n"); + ret = -ENOMEM; + goto TEST_END; + } + + /*store resistor after channel to gnd test*/ + cg_short_resistance = fts_malloc(ch_num * sizeof(int)); + if (NULL == cg_short_resistance) { + FTS_TEST_SAVE_ERR("cg_short_resistance buffer malloc fail\n"); + ret = -ENOMEM; + goto TEST_END; + } + + /*get abnormal channels from the 1st test step*/ + for (i = 0; i < ch_num; i++) { + cg_short_resistance[i] = ca_short_resistance[i]; + if (ca_short_resistance[i] < min_cc) { + error_ch[error_num++] = (u8)(i + 1); + } + } + + if (error_num > 0) { + /*write error channel numbers*/ + w_buf[0] = (u8)error_num; + for (i = 0; i < error_num; i++) { + w_buf[1 + i] = error_ch[i]; + FTS_TEST_DBG("cg:%d,", error_ch[i]); + } + + ret = fts_test_write(FACTROY_REG_SHORT_AB_CH, w_buf, error_num + 1); + if (ret < 0) { + FTS_TEST_SAVE_ERR("failed to set error channels,ret:%d\n", ret); + goto TEST_END; + } + + /*get adc data of test of channel to gnd*/ + ret = fts_test_write_reg(FACTROY_REG_SHORT_RES_LEVEL, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 5A fails,ret:%d\n", ret); + goto TEST_END; + } + + ret = short_get_adc_data_mc(TEST_RETVAL_00, error_num * 2, \ + tmp_adc_data, FACTROY_REG_SHORT_CG); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get adc data of cg fails,ret:%d\n", ret); + goto TEST_END; + } + + for (i = 0; i < error_num; i++) { + code = tmp_adc_data[i]; + denominator = code_1 - code; + if (denominator <= 0) { + tmp_res = min_cg; + } else { + numerator = (code - offset + 395) * 111; + tmp_res = fts_abs(numerator / denominator - 3); + } + + /*resistor for channel to gnd fails*/ + if (tmp_res < min_cg) { + cg_short_resistance[error_ch[i] - 1] = tmp_res; + adc_data[error_ch[i] - 1] = code; + + if (!is_short_gnd) { + is_short_gnd = true; + FTS_TEST_SAVE_INFO("\nGND Short:\n"); + } + if (tmp_res > 70) { + if (error_ch[i] <= tx_num) { + FTS_TEST_SAVE_INFO("Tx%d with GND:", error_ch[i]); + } else { + FTS_TEST_SAVE_INFO( "Rx%d with GND:", (error_ch[i] - tx_num)); + } + + FTS_TEST_SAVE_INFO("%d(K), ADC:%d\n", tmp_res, code); + } else { + min_70k_ch[min_70k_num++] = error_ch[i]; + } + } + } + } + + if (min_70k_num > 0) { + /*resistor of some channels are less than 70k*/ + + memset(tmp_adc_data, 0, (ch_num + 1) * sizeof(int)); + w_buf[0] = (u8)min_70k_num; + for (i = 0; i < min_70k_num; i++) { + w_buf[1 + i] = min_70k_ch[i]; + FTS_TEST_DBG("cg:%d,", min_70k_ch[i]); + } + + ret = fts_test_write(FACTROY_REG_SHORT_AB_CH, w_buf, min_70k_num + 1); + if (ret < 0) { + FTS_TEST_SAVE_ERR("failed to set error channels,ret:%d\n", ret); + goto TEST_END; + } + + ret = fts_test_write_reg(FACTROY_REG_SHORT_RES_LEVEL, 0x00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 5A fails,ret:%d\n", ret); + goto TEST_END; + } + + ret = short_get_adc_data_mc(TEST_RETVAL_00, min_70k_num * 2, \ + tmp_adc_data, FACTROY_REG_SHORT_CG); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get adc data of cg(70K) fails,ret:%d\n", ret); + goto TEST_END; + } + + for (i = 0; i < min_70k_num; i++) { + code = tmp_adc_data[i]; + denominator = code_1 - code; + if (denominator <= 0) { + tmp_res = min_cg; + } else { + numerator = (code - offset + 395) * 4; + tmp_res = fts_abs(numerator / denominator - 3); + } + + if (tmp_res < min_cg) { + cg_short_resistance[min_70k_ch[i] - 1] = tmp_res; + adc_data[min_70k_ch[i] - 1] = tmp_adc_data[i]; + if (min_70k_ch[i] <= tx_num) { + FTS_TEST_SAVE_INFO("Tx%d with GND:", min_70k_ch[i]); + } else { + FTS_TEST_SAVE_INFO("Rx%d with GND:", (min_70k_ch[i] - tx_num)); + } + + FTS_TEST_SAVE_INFO("%d(K), ADC:%d\n", tmp_res, code); + } + } + } + + if (is_short_gnd) { + FTS_TEST_DBG("channel to gnd test fail"); + } + ret = 0; +TEST_END: + + fts_free(error_ch); + fts_free (min_70k_ch); + fts_free (tmp_adc_data); + fts_free(cg_short_resistance); + fts_free(w_buf); + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int short_test_channel_to_channel( + struct fts_test *tdata, + int *ca_short_resistance, + int *adc_data, + int offset) +{ + int ret = 0; + int error_num = 0; + int tmp_num = 0; + int min_70k_num = 0; + int ch_num = 0; + int rx_num = 0; + int tx_num = 0; + int min_cc = 0; + int i = 0; + int j = 0; + int k = 0; + int code_1 = 1437; + int code = 0; + int numerator = 0; + int denominator = 0; + int tmp_res = 0; + int adc_count = 0; + bool is_short_mutual = false; + int *cc_short_resistance = NULL; + int *tmp_adc_data = NULL; + u8 *w_buf = NULL; + u8 *error_ch = NULL; + u8 *min_70k_ch = NULL ; + struct mc_sc_threshold *thr = &fts_ftest->ic.mc_sc.thr; + + FTS_TEST_FUNC_ENTER(); + tx_num = tdata->sc_node.tx_num; + rx_num = tdata->sc_node.rx_num; + ch_num = tx_num + rx_num; + min_cc = thr->basic.short_cc; + + w_buf = fts_malloc((ch_num + 1) * sizeof(u8)); + if (NULL == w_buf) { + FTS_TEST_SAVE_ERR("w_buf buffer malloc fail\n"); + ret = -ENOMEM; + goto TEST_END; + } + + error_ch = fts_malloc(ch_num * sizeof(u8)); + if (NULL == error_ch) { + FTS_TEST_SAVE_ERR("error_ch buffer malloc fail\n"); + ret = -ENOMEM; + goto TEST_END; + } + min_70k_ch = fts_malloc(ch_num * sizeof(u8)); + if (NULL == min_70k_ch) { + FTS_TEST_SAVE_ERR("min_70k_ch buffer malloc fail\n"); + ret = -ENOMEM; + goto TEST_END; + } + + tmp_adc_data = fts_malloc((ch_num + 1) * sizeof(int)); + if (NULL == tmp_adc_data) { + FTS_TEST_SAVE_ERR("adc_data buffer malloc fail\n"); + ret = -ENOMEM; + goto TEST_END; + } + + /*store resistor after channel to gnd test*/ + cc_short_resistance = fts_malloc(ch_num * sizeof(int)); + if (NULL == cc_short_resistance) { + FTS_TEST_SAVE_ERR("cc_short_resistance buffer malloc fail\n"); + ret = -ENOMEM; + goto TEST_END; + } + + /*get abnormal channels from the 1st test step*/ + for (i = 0; i < ch_num; i++) { + cc_short_resistance[i] = ca_short_resistance[i]; + if (ca_short_resistance[i] < min_cc) { + error_ch[error_num++] = (u8)(i + 1); + } + } + + /*channel to channel, must have 2 or multiple of 2*/ + if (error_num >= 2) { + w_buf[0] = (u8)error_num; + for ( i = 0; i < error_num; i++) { + w_buf[1 + i] = error_ch[i]; + FTS_TEST_DBG("cc:%d,", error_ch[i]); + } + + ret = fts_test_write(FACTROY_REG_SHORT_AB_CH, w_buf, error_num + 1); + if (ret < 0) { + FTS_TEST_SAVE_ERR("failed to set error channels,ret:%d\n", ret); + goto TEST_END; + } + + /*get adc data of test of channel to channel*/ + ret = fts_test_write_reg(FACTROY_REG_SHORT_RES_LEVEL, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 5A fails,ret:%d\n", ret); + goto TEST_END; + } + + /*channel to channel: num * (num - 1) / 2, max. node_num*/ + tmp_num = error_num * (error_num - 1) / 2; + tmp_num = (tmp_num > ch_num) ? ch_num : tmp_num; + ret = short_get_adc_data_mc(TEST_RETVAL_00, tmp_num * 2, \ + tmp_adc_data, FACTROY_REG_SHORT_CC); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get adc data of cc fails,ret:%d\n", ret); + goto TEST_END; + } + + for (i = 0; i < error_num; i++) { + for (j = i + 1; j < error_num; j++) { + if (adc_count >= tmp_num) + break; + code = tmp_adc_data[adc_count]; + denominator = code_1 - code; + if (denominator <= 0) { + tmp_res = min_cc; + } else { + numerator = (code - offset + 395) * 111; + tmp_res = fts_abs(numerator / denominator - 3); + } + + if (tmp_res < min_cc) { + cc_short_resistance[error_ch[i] - 1] = tmp_res; + cc_short_resistance[error_ch[j] - 1] = tmp_res; + adc_data[error_ch[i] - 1] = code; + adc_data[error_ch[j] - 1] = code; + + if (!is_short_mutual) { + FTS_TEST_SAVE_INFO("\nMutual Short:\n"); + is_short_mutual = true; + } + if (tmp_res > 70) { + if (error_ch[i] <= tx_num) { + FTS_TEST_SAVE_INFO("Tx%d with", (error_ch[i])); + } else { + FTS_TEST_SAVE_INFO("Rx%d with", (error_ch[i] - tx_num)); + } + + if (error_ch[j] <= tx_num) { + FTS_TEST_SAVE_INFO(" Tx%d", (error_ch[j] ) ); + } else { + FTS_TEST_SAVE_INFO(" Rx%d", (error_ch[j] - tx_num)); + } + FTS_TEST_SAVE_INFO(":%d(K), ADC:%d\n", tmp_res, code); + } else { + int has_ch_i = 0; + int has_ch_j = 0; + + for (k = 0; k < min_70k_num; k++) { + if (min_70k_ch[k] == error_ch[i]) + has_ch_i = 1; + else if (min_70k_ch[k] == error_ch[j]) + has_ch_j = 1; + } + + if (!has_ch_i) + min_70k_ch[min_70k_num++] = error_ch[i]; + if (!has_ch_j) + min_70k_ch[min_70k_num++] = error_ch[j]; + } + } + + adc_count++; + } + } + } + + if (min_70k_num > 0) { + /*resistor of some channels are less than 70k*/ + memset(tmp_adc_data, 0, (ch_num + 1) * sizeof(int)); + w_buf[0] = (u8)min_70k_num; + for (i = 0; i < min_70k_num; i++) { + w_buf[1 + i] = min_70k_ch[i]; + FTS_TEST_DBG("cc(70K):%d,", min_70k_ch[i]); + } + + ret = fts_test_write(FACTROY_REG_SHORT_AB_CH, w_buf, min_70k_num + 1); + if (ret < 0) { + FTS_TEST_SAVE_ERR("failed to set error channels,ret:%d\n", ret); + goto TEST_END; + } + + ret = fts_test_write_reg(FACTROY_REG_SHORT_RES_LEVEL, 0x00); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write 5A fails,ret:%d\n", ret); + goto TEST_END; + } + + tmp_num = min_70k_num * (min_70k_num - 1) / 2; + tmp_num = (tmp_num > ch_num) ? ch_num : tmp_num; + ret = short_get_adc_data_mc(TEST_RETVAL_00, tmp_num * 2, \ + tmp_adc_data, FACTROY_REG_SHORT_CC); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get adc data of cc(70K) fails,ret:%d\n", ret); + goto TEST_END; + } + + adc_count = 0; + for (i = 0; i < min_70k_num; i++) { + for (j = i + 1; j < min_70k_num; j++) { + if (adc_count >= tmp_num) + break; + code = tmp_adc_data[adc_count]; + denominator = code_1 - code; + if (denominator <= 0) { + tmp_res = min_cc; + } else { + numerator = (code - offset + 395) * 4; + tmp_res = fts_abs(numerator / denominator - 3); + } + + if (tmp_res < min_cc) { + cc_short_resistance[min_70k_ch[i] - 1] = tmp_res; + cc_short_resistance[min_70k_ch[j] - 1] = tmp_res; + adc_data[min_70k_ch[i] - 1] = code; + adc_data[min_70k_ch[j] - 1] = code; + if (min_70k_ch[i] <= tx_num) { + FTS_TEST_SAVE_INFO("Tx%d with", (min_70k_ch[i])); + } else { + FTS_TEST_SAVE_INFO("Rx%d with", (min_70k_ch[i] - tx_num)); + } + + if (min_70k_ch[j] <= tx_num) { + FTS_TEST_SAVE_INFO(" Tx%d", (min_70k_ch[j])); + } else { + FTS_TEST_SAVE_INFO(" Rx%d", (min_70k_ch[j] - tx_num)); + } + + FTS_TEST_SAVE_INFO(":%d(K), ADC:%d\n", tmp_res, code); + } + + adc_count++; + } + } + } + + if (is_short_mutual) { + FTS_TEST_DBG("channel to channel test fail"); + } + ret = 0; +TEST_END: + + fts_free(error_ch); + fts_free(min_70k_ch); + fts_free (tmp_adc_data); + fts_free(cc_short_resistance); + fts_free(w_buf); + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft5452_rawdata_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + int i = 0; + int *rawdata = NULL; + u8 fre = 0; + u8 fir = 0; + u8 normalize = 0; + bool result = false; + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: rawdata test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + rawdata = tdata->buffer; + + if (!thr->rawdata_h_min || !thr->rawdata_h_max) { + FTS_TEST_SAVE_ERR("rawdata_h_min/max is null\n"); + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("failed to enter factory mode,ret=%d\n", ret); + goto test_err; + } + + /* rawdata test in mapping mode */ + ret = mapping_switch(MAPPING); + if (ret < 0) { + FTS_TEST_SAVE_ERR("switch mapping fail,ret=%d\n", ret); + goto test_err; + } + + /* save origin value */ + ret = fts_test_read_reg(FACTORY_REG_NORMALIZE, &normalize); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read normalize fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_FRE_LIST, &fre); + if (ret) { + FTS_TEST_SAVE_ERR("read 0x0A fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_FIR, &fir); + if (ret) { + FTS_TEST_SAVE_ERR("read 0xFB error,ret=%d\n", ret); + goto test_err; + } + + /* set to auto normalize */ + if (normalize != 0x01) { + ret = fts_test_write_reg(FACTORY_REG_NORMALIZE, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write normalize fail,ret=%d\n", ret); + goto restore_reg; + } + } + + /* set frequecy high */ + ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, 0x81); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set frequecy fail,ret=%d\n", ret); + goto restore_reg; + } + + /* fir enable */ + ret = fts_test_write_reg(FACTORY_REG_FIR, 1); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set fir fail,ret=%d\n", ret); + goto restore_reg; + } + + /*********************GET RAWDATA*********************/ + for (i = 0; i < 3; i++) { + /* lost 3 frames, in order to obtain stable data */ + ret = get_rawdata(rawdata); + } + if (ret < 0) { + FTS_TEST_SAVE_ERR("get rawdata fail,ret=%d\n", ret); + goto restore_reg; + } + + /* show test data */ + show_data(rawdata, false); + + /* compare */ + result = compare_array(rawdata, + thr->rawdata_h_min, + thr->rawdata_h_max, + false); + +restore_reg: + /* set the origin value */ + ret = fts_test_write_reg(FACTORY_REG_NORMALIZE, normalize); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore normalize fail,ret=%d\n", ret); + } + + ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, fre); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore 0x0A fail,ret=%d\n", ret); + } + + ret = fts_test_write_reg(FACTORY_REG_FIR, fir); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore 0xFB fail,ret=%d\n", ret); + } + +test_err: + if (result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ rawdata test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("------ rawdata test NG\n"); + } + + /* save test data */ + fts_test_save_data("Rawdata Test", CODE_M_RAWDATA_TEST, + rawdata, 0, false, false, *test_result); + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft5452_uniformity_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + int index = 0; + int row = 0; + int col = 1; + int i = 0; + int deviation = 0; + int max = 0; + int min = 0; + int uniform = 0; + int *rawdata = NULL; + int *rawdata_linearity = NULL; + int *rl_tmp = NULL; + int rl_cnt = 0; + int offset = 0; + int offset2 = 0; + int tx_num = 0; + int rx_num = 0; + u8 fre = 0; + u8 fir = 0; + u8 normalize = 0; + struct mc_sc_threshold *thr = &fts_ftest->ic.mc_sc.thr; + bool result = false; + bool result2 = false; + bool result3 = false; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: rawdata unfiormity test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + rawdata = tdata->buffer; + tx_num = tdata->node.tx_num; + rx_num = tdata->node.rx_num; + + if (!thr->tx_linearity_max || !thr->rx_linearity_max + || !tdata->node_valid) { + FTS_TEST_SAVE_ERR("tx/rx_lmax/node_valid is null\n"); + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("failed to enter factory mode,ret=%d\n", ret); + goto test_err; + } + + rawdata_linearity = fts_malloc(tdata->node.node_num * 2 * sizeof(int)); + if (!rawdata_linearity) { + FTS_TEST_SAVE_ERR("rawdata_linearity buffer malloc fail"); + ret = -ENOMEM; + goto test_err; + } + + /* rawdata unfiormity test in mapping mode */ + ret = mapping_switch(MAPPING); + if (ret) { + FTS_TEST_SAVE_ERR("failed to switch_to_mapping,ret=%d", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_NORMALIZE, &normalize); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read normalize fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_FRE_LIST, &fre); + if (ret) { + FTS_TEST_SAVE_ERR("read 0x0A fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_FIR, &fir); + if (ret) { + FTS_TEST_SAVE_ERR("read 0xFB error,ret=%d\n", ret); + goto test_err; + } + + if (normalize != 0x01) { + ret = fts_test_write_reg(FACTORY_REG_NORMALIZE, 0x01); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write normalize fail,ret=%d\n", ret); + goto restore_reg; + } + } + + /* set frequecy high */ + ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, 0x81); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set frequecy fail,ret=%d\n", ret); + goto restore_reg; + } + sys_delay(10); + + /* fir enable */ + ret = fts_test_write_reg(FACTORY_REG_FIR, 1); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set fir fail,ret=%d\n", ret); + goto restore_reg; + } + sys_delay(10); + + /* change register value before,need to lose 3 frame data */ + for (index = 0; index < 3; ++index) { + ret = get_rawdata(rawdata); + } + if (ret < 0) { + FTS_TEST_SAVE_ERR("get rawdata fail,ret=%d\n", ret); + goto restore_reg; + } + print_buffer(rawdata, tdata->node.node_num, tdata->node.rx_num); + + result = true; + if (thr->basic.uniformity_check_tx) { + FTS_TEST_SAVE_INFO("Check Tx Linearity\n"); + rl_tmp = rawdata_linearity + rl_cnt; + for (row = 0; row < tx_num; row++) { + for (col = 1; col < rx_num; col++) { + offset = row * rx_num + col; + offset2 = row * rx_num + col - 1; + deviation = abs( rawdata[offset] - rawdata[offset2]); + max = max(rawdata[offset], rawdata[offset2]); + max = max ? max : 1; + rl_tmp[offset] = 100 * deviation / max; + } + } + /*show data in result.txt*/ + FTS_TEST_SAVE_INFO(" Tx Linearity:\n"); + show_data(rl_tmp, false); + FTS_TEST_SAVE_INFO("\n" ); + + /* compare */ + result = compare_array(rl_tmp, + thr->tx_linearity_min, + thr->tx_linearity_max, + false); + + rl_cnt += tdata->node.node_num; + } + + result2 = true; + if (thr->basic.uniformity_check_rx) { + FTS_TEST_SAVE_INFO("Check Rx Linearity\n"); + rl_tmp = rawdata_linearity + rl_cnt; + for (row = 1; row < tx_num; row++) { + for (col = 0; col < rx_num; col++) { + offset = row * rx_num + col; + offset2 = (row - 1) * rx_num + col; + deviation = abs(rawdata[offset] - rawdata[offset2]); + max = max(rawdata[offset], rawdata[offset2]); + max = max ? max : 1; + rl_tmp[offset] = 100 * deviation / max; + } + } + + FTS_TEST_SAVE_INFO("Rx Linearity:\n"); + show_data(rl_tmp, false); + FTS_TEST_SAVE_INFO("\n"); + + /* compare */ + result2 = compare_array(rl_tmp, + thr->rx_linearity_min, + thr->rx_linearity_max, + false); + rl_cnt += tdata->node.node_num; + } + + result3 = true; + if (thr->basic.uniformity_check_min_max) { + FTS_TEST_SAVE_INFO("Check Min/Max\n") ; + min = 100000; + max = -100000; + for (i = 0; i < tdata->node.node_num; i++) { + if (0 == tdata->node_valid[i]) + continue; + min = min(min, rawdata[i]); + max = max(max, rawdata[i]); + } + max = !max ? 1 : max; + uniform = 100 * abs(min) / abs(max); + + FTS_TEST_SAVE_INFO("min:%d, max:%d, get value of min/max:%d\n", + min, max, uniform); + if (uniform < thr->basic.uniformity_min_max_hole) { + result3 = false; + FTS_TEST_SAVE_ERR("min_max out of range, set value: %d\n", + thr->basic.uniformity_min_max_hole); + } + } + +restore_reg: + /* set the origin value */ + ret = fts_test_write_reg(FACTORY_REG_NORMALIZE, normalize); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore normalize fail,ret=%d\n", ret); + } + + ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, fre); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore 0x0A fail,ret=%d\n", ret); + } + + ret = fts_test_write_reg(FACTORY_REG_FIR, fir); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore 0xFB fail,ret=%d\n", ret); + } + +test_err: + if (result && result2 && result3) { + *test_result = true; + FTS_TEST_SAVE_INFO("uniformity test is OK\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_ERR("uniformity test is NG\n"); + } + + fts_test_save_data("Rawdata Uniformity Test", + CODE_M_RAWDATA_UNIFORMITY_TEST, rawdata_linearity, + tdata->node.node_num * 2, false, false, *test_result); + + fts_free(rawdata_linearity); + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft5452_scap_cb_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + bool tmp_result = false; + bool tmp2_result = false; + u8 wc_sel = 0; + u8 sc_mode = 0; + int byte_num = 0; + bool fw_wp_check = false; + bool tx_check = false; + bool rx_check = false; + int *scap_cb = NULL; + int *scb_tmp = NULL; + int scb_cnt = 0; + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: Scap CB Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + scap_cb = tdata->buffer; + byte_num = tdata->sc_node.node_num * 2; + + if ((tdata->sc_node.node_num * 2) > tdata->buffer_length) { + FTS_TEST_SAVE_ERR("scap cb num(%d) > buffer length(%d)", + tdata->sc_node.node_num * 2, + tdata->buffer_length); + ret = -EINVAL; + goto test_err; + } + + if (!thr->scap_cb_on_min || !thr->scap_cb_on_max + || !thr->scap_cb_off_min || !thr->scap_cb_off_max) { + FTS_TEST_SAVE_ERR("scap_cb_on/off_min/max is null\n"); + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + /* SCAP CB is in no-mapping mode */ + ret = mapping_switch(NO_MAPPING); + if (ret < 0) { + FTS_TEST_SAVE_ERR("switch no-mapping fail,ret=%d\n", ret); + goto test_err; + } + + /* get waterproof channel select */ + ret = fts_test_read_reg(FACTORY_REG_WC_SEL, &wc_sel); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read water_channel_sel fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_MC_SC_MODE, &sc_mode); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read sc_mode fail,ret=%d\n", ret); + goto test_err; + } + + /* water proof on check */ + fw_wp_check = get_fw_wp(wc_sel, WATER_PROOF_ON); + if (thr->basic.scap_cb_wp_on_check && fw_wp_check) { + scb_tmp = scap_cb + scb_cnt; + /* 1:waterproof 0:non-waterproof */ + ret = get_cb_mc_sc(WATER_PROOF_ON, byte_num, scb_tmp, DATA_TWO_BYTE); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read sc_cb fail,ret=%d\n", ret); + goto restore_reg; + } + + /* show Scap CB */ + FTS_TEST_SAVE_INFO("scap_cb in waterproof on mode:\n"); + show_data_mc_sc(scb_tmp); + + /* compare */ + tx_check = get_fw_wp(wc_sel, WATER_PROOF_ON_TX); + rx_check = get_fw_wp(wc_sel, WATER_PROOF_ON_RX); + tmp_result = compare_mc_sc(tx_check, rx_check, scb_tmp, + thr->scap_cb_on_min, + thr->scap_cb_on_max); + + scb_cnt += tdata->sc_node.node_num; + } else { + tmp_result = true; + } + + /* water proof off check */ + fw_wp_check = get_fw_wp(wc_sel, WATER_PROOF_OFF); + if (thr->basic.scap_cb_wp_off_check && fw_wp_check) { + scb_tmp = scap_cb + scb_cnt; + /* 1:waterproof 0:non-waterproof */ + ret = get_cb_mc_sc(WATER_PROOF_OFF, byte_num, scb_tmp, DATA_TWO_BYTE); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read sc_cb fail,ret=%d\n", ret); + goto restore_reg; + } + + /* show Scap CB */ + FTS_TEST_SAVE_INFO("scap_cb in waterproof off mode:\n"); + show_data_mc_sc(scb_tmp); + + /* compare */ + tx_check = get_fw_wp(wc_sel, WATER_PROOF_OFF_TX); + rx_check = get_fw_wp(wc_sel, WATER_PROOF_OFF_RX); + tmp2_result = compare_mc_sc(tx_check, rx_check, scb_tmp, + thr->scap_cb_off_min, + thr->scap_cb_off_max); + + scb_cnt += tdata->sc_node.node_num; + } else { + tmp2_result = true; + } + +restore_reg: + ret = fts_test_write_reg(FACTORY_REG_MC_SC_MODE, sc_mode);/* set the origin value */ + if (ret) { + FTS_TEST_SAVE_ERR("write sc mode fail,ret=%d\n", ret); + } +test_err: + if (tmp_result && tmp2_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("\n------ scap cb test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_ERR("\n------ scap cb test NG\n"); + } + + /* save test data */ + fts_test_save_data("SCAP CB Test", CODE_M_SCAP_CB_TEST, + scap_cb, scb_cnt, true, false, *test_result); + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft5452_scap_rawdata_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + bool tmp_result = false; + bool tmp2_result = false; + u8 wc_sel = 0; + bool fw_wp_check = false; + bool tx_check = false; + bool rx_check = false; + int *scap_rawdata = NULL; + int *srawdata_tmp = NULL; + int srawdata_cnt = 0; + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: Scap Rawdata Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + scap_rawdata = tdata->buffer; + + if ((tdata->sc_node.node_num * 2) > tdata->buffer_length) { + FTS_TEST_SAVE_ERR("scap rawdata num(%d) > buffer length(%d)", + tdata->sc_node.node_num * 2, + tdata->buffer_length); + ret = -EINVAL; + goto test_err; + } + + if (!thr->scap_rawdata_on_min || !thr->scap_rawdata_on_max + || !thr->scap_rawdata_off_min || !thr->scap_rawdata_off_max) { + FTS_TEST_SAVE_ERR("scap_rawdata_on/off_min/max is null\n"); + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + /* SCAP RAWDATA is in no-mapping mode */ + ret = mapping_switch(NO_MAPPING); + if (ret < 0) { + FTS_TEST_SAVE_ERR("switch no-mapping fail,ret=%d\n", ret); + goto test_err; + } + + /* get waterproof channel select */ + ret = fts_test_read_reg(FACTORY_REG_WC_SEL, &wc_sel); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read water_channel_sel fail,ret=%d\n", ret); + goto test_err; + } + + /* scan rawdata */ + ret = start_scan(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("scan scap rawdata fail\n"); + goto test_err; + } + + /* water proof on check */ + fw_wp_check = get_fw_wp(wc_sel, WATER_PROOF_ON); + if (thr->basic.scap_rawdata_wp_on_check && fw_wp_check) { + srawdata_tmp = scap_rawdata + srawdata_cnt; + ret = get_rawdata_mc_sc(WATER_PROOF_ON, srawdata_tmp); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get scap(WP_ON) rawdata fail\n"); + goto test_err; + } + + FTS_TEST_SAVE_INFO("scap_rawdata in waterproof on mode:\n"); + show_data_mc_sc(srawdata_tmp); + + /* compare */ + tx_check = get_fw_wp(wc_sel, WATER_PROOF_ON_TX); + rx_check = get_fw_wp(wc_sel, WATER_PROOF_ON_RX); + tmp_result = compare_mc_sc(tx_check, rx_check, srawdata_tmp, + thr->scap_rawdata_on_min, + thr->scap_rawdata_on_max); + + srawdata_cnt += tdata->sc_node.node_num; + } else { + tmp_result = true; + } + + /* water proof off check */ + fw_wp_check = get_fw_wp(wc_sel, WATER_PROOF_OFF); + if (thr->basic.scap_rawdata_wp_off_check && fw_wp_check) { + srawdata_tmp = scap_rawdata + srawdata_cnt; + ret = get_rawdata_mc_sc(WATER_PROOF_OFF, srawdata_tmp); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get scap(WP_OFF) rawdata fail\n"); + goto test_err; + } + + FTS_TEST_SAVE_INFO("scap_rawdata in waterproof off mode:\n"); + show_data_mc_sc(srawdata_tmp); + + /* compare */ + tx_check = get_fw_wp(wc_sel, WATER_PROOF_OFF_TX); + rx_check = get_fw_wp(wc_sel, WATER_PROOF_OFF_RX); + tmp2_result = compare_mc_sc(tx_check, rx_check, srawdata_tmp, + thr->scap_rawdata_off_min, + thr->scap_rawdata_off_max); + + srawdata_cnt += tdata->sc_node.node_num; + } else { + tmp2_result = true; + } + +test_err: + if (tmp_result && tmp2_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("\n------ scap rawdata test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_INFO("\n------ scap rawdata test NG\n"); + } + + /* save data */ + fts_test_save_data("SCAP Rawdata Test", CODE_M_SCAP_RAWDATA_TEST, + scap_rawdata, srawdata_cnt, true, false, *test_result); + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft5452_short_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + int ch_num = 0; + int *adc_data = NULL; + int *ca_short_resistance = NULL; + int offset = 0; + u8 short_delay = 0; + bool weak_short_result = false; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: Short Test\n"); + ch_num = tdata->sc_node.tx_num + tdata->sc_node.rx_num; + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("enter factory mode fail,ret=%d\n", ret); + goto test_err; + } + + /* short is in no-mapping mode */ + ret = mapping_switch(NO_MAPPING); + if (ret < 0) { + FTS_TEST_SAVE_ERR("switch no-mapping fail,ret=%d\n", ret); + goto test_err; + } + + adc_data = fts_malloc((ch_num + 3) * sizeof(int)); + if (NULL == adc_data) { + FTS_TEST_SAVE_ERR("adc_data buffer malloc fail\n"); + ret = -ENOMEM; + goto test_err; + } + + ca_short_resistance = fts_malloc(ch_num * sizeof(int)); + if (NULL == ca_short_resistance) { + FTS_TEST_SAVE_ERR("short_resistance buffer malloc fail\n"); + ret = -ENOMEM; + goto test_err; + } + + ret = fts_test_read_reg(FACTROY_REG_SHORT_RES_LEVEL, &short_delay); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read 5A fails\n"); + goto test_err; + } + + ret = short_get_adc_data_mc(TEST_RETVAL_00, 1 * 2, &offset, \ + FACTROY_REG_SHORT_OFFSET); + if (ret < 0) { + FTS_TEST_SAVE_ERR("failed to get weak short data,ret=%d\n", ret); + goto restore_reg; + } + + offset -= 1024; + + /* get short resistance and exceptional channel */ + ret = short_test_channel_to_all(tdata, ca_short_resistance, adc_data, offset, \ + &weak_short_result); + if (ret < 0) { + FTS_TEST_SAVE_ERR("short test of channel to all fails\n"); + goto restore_reg; + } + + if (!weak_short_result) { + /*weak short fail, get short values*/ + ret = short_test_channel_to_gnd(tdata, ca_short_resistance, \ + adc_data, offset); + if (ret < 0) { + FTS_TEST_SAVE_ERR("short test of channel to gnd fails\n"); + goto restore_reg; + } + + ret = short_test_channel_to_channel(tdata, ca_short_resistance, \ + adc_data, offset); + if (ret < 0) { + FTS_TEST_SAVE_ERR("short test of channel to channel fails\n"); + goto restore_reg; + } + + } + +restore_reg: + ret = fts_test_write_reg(FACTROY_REG_SHORT_RES_LEVEL, short_delay); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore 5A fails"); + } + +test_err: + fts_free(adc_data); + fts_free(ca_short_resistance); + + if (weak_short_result) { + FTS_TEST_SAVE_INFO("------ short test PASS\n"); + *test_result = true; + } else { + FTS_TEST_SAVE_ERR("------ short test NG\n"); + *test_result = false; + } + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int ft5452_panel_differ_test(struct fts_test *tdata, bool *test_result) +{ + int ret = 0; + bool tmp_result = false; + int i = 0; + u8 fre = 0; + u8 fir = 0; + u8 normalize = 0; + int *panel_differ = NULL; + struct mc_sc_threshold *thr = &tdata->ic.mc_sc.thr; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_SAVE_INFO("\n============ Test Item: Panel Differ Test\n"); + memset(tdata->buffer, 0, tdata->buffer_length); + panel_differ = tdata->buffer; + + if (!thr->panel_differ_min || !thr->panel_differ_max) { + FTS_TEST_SAVE_ERR("panel_differ_h_min/max is null\n"); + ret = -EINVAL; + goto test_err; + } + + ret = enter_factory_mode(); + if (ret < 0) { + FTS_TEST_SAVE_ERR("failed to enter factory mode,ret=%d\n", ret); + goto test_err; + } + + /* panel differ test in mapping mode */ + ret = mapping_switch(MAPPING); + if (ret < 0) { + FTS_TEST_SAVE_ERR("switch mapping fail,ret=%d\n", ret); + goto test_err; + } + + /* save origin value */ + ret = fts_test_read_reg(FACTORY_REG_NORMALIZE, &normalize); + if (ret < 0) { + FTS_TEST_SAVE_ERR("read normalize fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_FRE_LIST, &fre); + if (ret) { + FTS_TEST_SAVE_ERR("read 0x0A fail,ret=%d\n", ret); + goto test_err; + } + + ret = fts_test_read_reg(FACTORY_REG_FIR, &fir); + if (ret) { + FTS_TEST_SAVE_ERR("read 0xFB fail,ret=%d\n", ret); + goto test_err; + } + + /* set to overall normalize */ + if (normalize != NORMALIZE_OVERALL) { + ret = fts_test_write_reg(FACTORY_REG_NORMALIZE, NORMALIZE_OVERALL); + if (ret < 0) { + FTS_TEST_SAVE_ERR("write normalize fail,ret=%d\n", ret); + goto restore_reg; + } + } + + /* set frequecy high */ + ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, 0x81); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set frequecy fail,ret=%d\n", ret); + goto restore_reg; + } + + /* fir disable */ + ret = fts_test_write_reg(FACTORY_REG_FIR, 0); + if (ret < 0) { + FTS_TEST_SAVE_ERR("set fir fail,ret=%d\n", ret); + goto restore_reg; + } + + /* get rawdata */ + for (i = 0; i < 3; i++) { + ret = get_rawdata(panel_differ); + if (ret < 0) { + FTS_TEST_SAVE_ERR("get rawdata fail\n"); + goto restore_reg; + } + } + + for (i = 0; i < tdata->node.node_num; i++) { + panel_differ[i] = panel_differ[i] / 10; + } + + /* show test data */ + show_data(panel_differ, false); + + /* compare */ + tmp_result = compare_array(panel_differ, + thr->panel_differ_min, + thr->panel_differ_max, + false); + +restore_reg: + /* set the origin value */ + ret = fts_test_write_reg(FACTORY_REG_NORMALIZE, normalize); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore normalize fail,ret=%d\n", ret); + } + + ret = fts_test_write_reg(FACTORY_REG_FRE_LIST, fre); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore 0x0A fail,ret=%d\n", ret); + } + + ret = fts_test_write_reg(FACTORY_REG_FIR, fir); + if (ret < 0) { + FTS_TEST_SAVE_ERR("restore 0xFB fail,ret=%d\n", ret); + } +test_err: + /* result */ + if (tmp_result) { + *test_result = true; + FTS_TEST_SAVE_INFO("------ panel differ test PASS\n"); + } else { + *test_result = false; + FTS_TEST_SAVE_ERR("------ panel differ test NG\n"); + } + + /* save test data */ + fts_test_save_data("Panel Differ Test", CODE_M_PANELDIFFER_TEST, + panel_differ, 0, false, false, *test_result); + + FTS_TEST_FUNC_EXIT(); + return ret; +} + +static int start_test_ft5452i(void) +{ + int ret = 0; + struct fts_test *tdata = fts_ftest; + struct mc_sc_testitem *test_item = &tdata->ic.mc_sc.u.item; + bool temp_result = false; + bool test_result = true; + + FTS_TEST_FUNC_ENTER(); + FTS_TEST_INFO("test item:0x%x", fts_ftest->ic.mc_sc.u.tmp); + + /* rawdata test */ + if (true == test_item->rawdata_test) { + ret = ft5452_rawdata_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + if (true == test_item->rawdata_uniformity_test) { + ret = ft5452_uniformity_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* scap_cb test */ + if (true == test_item->scap_cb_test) { + ret = ft5452_scap_cb_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* scap_rawdata test */ + if (true == test_item->scap_rawdata_test) { + ret = ft5452_scap_rawdata_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* short test */ + if (true == test_item->short_test) { + ret = ft5452_short_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + /* panel differ test */ + if (true == test_item->panel_differ_test) { + ret = ft5452_panel_differ_test(tdata, &temp_result); + if ((ret < 0) || (false == temp_result)) { + test_result = false; + } + } + + /* restore mapping state */ + fts_test_write_reg(FACTORY_REG_NOMAPPING, tdata->mapping); + + FTS_TEST_FUNC_EXIT(); + return test_result; +} + +struct test_funcs test_func_ft5452i = { + .ctype = {0x89}, + .hwtype = IC_HW_MC_SC, + .key_num_total = 0, + .start_test = start_test_ft5452i, +}; diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_ts_mmi.c b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_ts_mmi.c new file mode 100644 index 000000000000..d81f832323dd --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/focaltech_ts_mmi.c @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include "focaltech_core.h" + +#define GET_TS_DATA(dev) { \ + ts_data = dev_get_drvdata(dev); \ + if (!ts_data) { \ + FTS_ERROR("Failed to get driver data"); \ + return -ENODEV; \ + } \ +} +static int fts_mmi_methods_get_vendor(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", "focaltech"); +} + +static int fts_mmi_methods_get_productinfo(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_INFO_LEN, "%s", FTS_CHIP_NAME); +} + +static int fts_mmi_methods_get_build_id(struct device *dev, void *cdata) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + ssize_t num_read_chars = 0; + u8 fwver = 0; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + 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 + num_read_chars = scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%02x", fwver); + + mutex_unlock(&input_dev->mutex); + return num_read_chars; +} + +/*return firmware version*/ +static int fts_mmi_methods_get_config_id(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%04x", 0); +} + +static int fts_mmi_methods_get_bus_type(struct device *dev, void *idata) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + TO_INT(idata) = input_dev->id.bustype == BUS_SPI ? + TOUCHSCREEN_MMI_BUS_TYPE_SPI : TOUCHSCREEN_MMI_BUS_TYPE_I2C; + return 0; +} + +static int fts_mmi_methods_get_irq_status(struct device *dev, void *idata) +{ + struct fts_ts_platform_data *pdata; + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + pdata = ts_data->pdata; + + TO_INT(idata) = gpio_get_value(pdata->irq_gpio); + return 0; +} + +static int fts_mmi_methods_get_drv_irq(struct device *dev, void *idata) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + TO_INT(idata) = ts_data->irq_disabled ? 0 : 1; + return 0; +} + +static int fts_mmi_methods_get_poweron(struct device *dev, void *idata) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + TO_INT(idata) = ts_data->power_disabled == false ? 1 : 0; + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int fts_mmi_methods_get_flashprog(struct device *dev, void *idata) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + TO_INT(idata) = (ts_data->fw_loading) ? 1 : 0; + + return 0; +} + +static int fts_mmi_methods_drv_irq(struct device *dev, int state) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + int ret = 0; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + + if (state == 1) { + FTS_INFO("enable irq"); + fts_irq_enable(); + } else if (state == 0) { + FTS_INFO("disable irq"); + fts_irq_disable(); + } else { + dev_err(dev, "%s: invalid value\n", __func__); + ret = -EINVAL; + } + + mutex_unlock(&input_dev->mutex); + return ret; +} + +#if FTS_POWER_SOURCE_CUST_EN +static int fts_mmi_methods_power(struct device *dev, int state) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + + return fts_power_source_ctrl(ts_data, state); +} +#endif + +static int fts_mmi_methods_reset(struct device *dev, int type) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + + mutex_lock(&input_dev->mutex); + fts_reset_proc(0); + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int fts_mmi_methods_pinctrl(struct device *dev, int on) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + + if (on == TS_MMI_PINCTL_ON) { + mutex_lock(&input_dev->mutex); + fts_reset_proc(150); + mutex_unlock(&input_dev->mutex); + } + + return 0; +} + +static int fts_mmi_firmware_update(struct device *dev, char *fwname) +{ + struct fts_ts_data *ts_data; + struct input_dev *input_dev; + + GET_TS_DATA(dev); + input_dev = ts_data->input_dev; + + ts_data->force_reflash = true; + + mutex_lock(&input_dev->mutex); + fts_fw_update_vendor_name(fwname); + fts_fwupg_bin(); + mutex_unlock(&input_dev->mutex); + + return 0; +} + +#if FTS_USB_DETECT_EN +static int fts_mmi_charger_mode(struct device *dev, int mode) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + ts_data->usb_detect_flag = !!mode; + + return 0; +} +#endif + +static int fts_mmi_panel_state(struct device *dev, + enum ts_mmi_pm_mode from, enum ts_mmi_pm_mode to) +{ + struct fts_ts_platform_data *pdata; + struct fts_ts_data *ts_data; + int ret = 0; + + GET_TS_DATA(dev); + pdata = ts_data->pdata; + + switch (to) { + case TS_MMI_PM_DEEPSLEEP: + ret = fts_write_reg(FTS_REG_POWER_MODE, FTS_REG_POWER_MODE_SLEEP); + ts_data->gesture_mode = false; + if (ret < 0) + FTS_ERROR("set TP to sleep mode fail, ret=%d", ret); + break; + + case TS_MMI_PM_GESTURE: +#if FTS_GESTURE_EN + if (fts_gesture_suspend(ts_data) == 0) { + /* Enter into gesture mode(suspend) */ +#ifdef FOCALTECH_SENSOR_EN + ts_data->wakeable = true; + ts_data->gesture_mode = true; +#endif + } +#endif + break; + + case TS_MMI_PM_ACTIVE: + break; + default: + dev_warn(dev, "panel mode %d is invalid.\n", to); + ret = -EINVAL; + break; + } + + return ret; + +} + +static int fts_mmi_pre_resume(struct device *dev) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + + FTS_FUNC_ENTER(); + + fts_release_all_finger(); + + FTS_FUNC_EXIT(); + return 0; +} + +static int fts_mmi_post_resume(struct device *dev) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + +/* if (!ts_data->pdata->always_on_vio) { + FTS_INFO("Reset IC in resume"); + fts_reset_proc(200); + }*/ + + fts_tp_state_recovery(ts_data); + +#if FTS_ESDCHECK_EN + fts_esdcheck_resume(); +#endif + +#if FTS_GESTURE_EN +#ifdef FOCALTECH_SENSOR_EN + if (ts_data->wakeable) { +#endif + if (fts_gesture_resume(ts_data) == 0) { +#ifdef FOCALTECH_SENSOR_EN + ts_data->wakeable = false; + FTS_INFO("Exit from gesture suspend mode."); +#endif + goto exit; + } +#ifdef FOCALTECH_SENSOR_EN + } +#endif +#endif + +#if FTS_GESTURE_EN +exit: +#endif + FTS_FUNC_EXIT(); + ts_data->suspended = false; + + return 0; +} + +static int fts_mmi_pre_suspend(struct device *dev) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + + FTS_FUNC_ENTER(); + +#if FTS_ESDCHECK_EN + FTS_INFO("fts_esdcheck_suspend"); + fts_esdcheck_suspend(); +#endif + + FTS_FUNC_EXIT(); + + return 0; +} + +static int fts_mmi_post_suspend(struct device *dev) +{ + struct fts_ts_data *ts_data; + + GET_TS_DATA(dev); + + FTS_FUNC_ENTER(); + +#ifdef FOCALTECH_SENSOR_EN + if (!ts_data->wakeable) { +#endif + fts_release_all_finger(); +#ifdef FOCALTECH_SENSOR_EN + } +#endif + + ts_data->suspended = true; + FTS_FUNC_EXIT(); + + return 0; +} + +#ifdef FOCALTECH_PALM_SENSOR_EN +int fts_mmi_palm_set_enable(struct device *dev, unsigned int enable) +{ + fts_palm_sensor_set_enable(enable); + return 0; +} +#endif + +static struct ts_mmi_methods fts_mmi_methods = { + .get_vendor = fts_mmi_methods_get_vendor, + .get_productinfo = fts_mmi_methods_get_productinfo, + .get_build_id = fts_mmi_methods_get_build_id, + .get_config_id = fts_mmi_methods_get_config_id, + .get_bus_type = fts_mmi_methods_get_bus_type, + .get_irq_status = fts_mmi_methods_get_irq_status, + .get_drv_irq = fts_mmi_methods_get_drv_irq, + .get_flashprog = fts_mmi_methods_get_flashprog, + .get_poweron = fts_mmi_methods_get_poweron, + /* SET methods */ + .reset = fts_mmi_methods_reset, + .pinctrl = fts_mmi_methods_pinctrl, + .drv_irq = fts_mmi_methods_drv_irq, +#if FTS_POWER_SOURCE_CUST_EN + .power = fts_mmi_methods_power, +#endif +#if FTS_USB_DETECT_EN + .charger_mode = fts_mmi_charger_mode, +#endif +#ifdef FOCALTECH_PALM_SENSOR_EN + .palm_set_enable = fts_mmi_palm_set_enable, +#endif + /* Firmware */ + .firmware_update = fts_mmi_firmware_update, + /* PM callback */ + .panel_state = fts_mmi_panel_state, + .pre_resume = fts_mmi_pre_resume, + .post_resume = fts_mmi_post_resume, + .pre_suspend = fts_mmi_pre_suspend, + .post_suspend = fts_mmi_post_suspend, + +}; + +int fts_mmi_dev_register(struct fts_ts_data *ts_data) { + int ret; + + ret = ts_mmi_dev_register(ts_data->dev, &fts_mmi_methods); + if (ret) { + dev_err(ts_data->dev, "Failed to register ts mmi\n"); + return ret; + } + + /* initialize class imported methods */ + ts_data->imports = &fts_mmi_methods.exports; + + return 0; +} + +void fts_mmi_dev_unregister(struct fts_ts_data *ts_data) { + ts_mmi_dev_unregister(ts_data->dev); +} + diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/include/firmware/fw_sample.h b/drivers/input/touchscreen/focaltech_v3_mmi/include/firmware/fw_sample.h new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/drivers/input/touchscreen/focaltech_v3_mmi/include/pramboot/FT5452i_Pramboot_v4.1_20210427.h b/drivers/input/touchscreen/focaltech_v3_mmi/include/pramboot/FT5452i_Pramboot_v4.1_20210427.h new file mode 100644 index 000000000000..a619803e882a --- /dev/null +++ b/drivers/input/touchscreen/focaltech_v3_mmi/include/pramboot/FT5452i_Pramboot_v4.1_20210427.h @@ -0,0 +1,452 @@ +0x2, 0x0, 0x46,0x32,0x0, 0x0, 0xb2,0x0, 0xff,0xff,0xff,0x2, 0x7, 0x45,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0x2, 0xd, 0x9a,0xff,0xff,0xff,0xff,0xff,0x2, 0xb, 0x91,0xff,0xff, +0xff,0xff,0xff,0x2, 0x0, 0x3, 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0x2, 0xb, 0xdb,0x75,0x84,0x1, 0x7e,0x44,0x1f,0xff,0xe4,0x7a,0x49, +0xb0,0x1b,0x44,0x78,0xf9,0x7e,0xf8,0x13,0xff,0xd2,0x7, 0xc2,0x8, 0xc2,0x9, 0x75, +0x22,0x0, 0x75,0x27,0xff,0xd2,0x0, 0xc2,0x2, 0xc2,0x3, 0xc2,0x4, 0xc2,0x6, 0x7e, +0x4, 0x0, 0xff,0x7e,0x14,0x1, 0x45,0xb, 0xa, 0x40,0x5d,0x44,0x68,0x1a,0x69,0x20, +0x0, 0x2, 0xb, 0xe, 0xb, 0x44,0x80,0xa, 0x7e,0xb, 0xb0,0x7a,0x29,0xb0,0xb, 0x24, +0xb, 0xc, 0x1b,0x44,0x78,0xf2,0x80,0xdf,0x2, 0x19,0x93,0x6d,0x0, 0x74,0x10,0x4d, +0x0, 0x78,0xb, 0x4d,0x22,0x78,0x27,0x8d,0x31,0x7d,0x12,0x6d,0x22,0x22,0x7d,0x43, +0x7d,0x32,0x6d,0x22,0x2f,0x11,0x2d,0x44,0x50,0x2, 0xa5,0xf, 0xbf,0x10,0x40,0x4, +0x9f,0x10,0xb, 0x90,0x14,0x78,0xed,0x7f,0x1, 0x6d,0x22,0x7d,0x34,0x22,0x7d,0x41, +0x7d,0x13,0x8d,0x24,0x7d,0x2, 0x2f,0x0, 0x40,0x4, 0xbd,0x4, 0x40,0x4, 0x9d,0x4, +0xb, 0x14,0x14,0x78,0xf1,0x7d,0x23,0x7d,0x31,0x7d,0x10,0x6d,0x0, 0x22,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0x1c,0x40,0xe3,0xbf,0x93,0x6c,0xdd,0x22,0xff,0x0, 0xff,0x0, 0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xca,0xf8,0x7f,0x20,0x80,0xa, 0x7e,0x1b,0xf0,0x7a,0x2b,0xf0,0xb, 0x34,0xb, 0x54, +0x69,0xff,0xff,0xfc,0x7d,0xef,0x1b,0xe4,0x79,0xef,0xff,0xfc,0x4d,0xff,0x78,0xe6, +0x7f,0x10,0xda,0xf8,0x22,0x0, 0x1, 0x2, 0x79,0x0, 0x0, 0xa, 0x2, 0x7a,0x54,0x5b, +0xaa,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x0, 0x1, 0x2, 0x84,0x0, 0x0, 0x1, 0x2, +0x85,0xff,0x0, 0x2, 0x2, 0x86,0x0, 0x0, 0x0, 0x1, 0x3, 0x8c,0x0, 0x0, 0x1, 0x2, +0x44,0x0, 0x0, 0x1, 0x2, 0x46,0x0, 0x0, 0x2, 0x2, 0x61,0x0, 0x0, 0x0, 0x4, 0x2, +0x63,0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x2, 0x67,0x0, 0x0, 0x0, 0x1, 0x2, 0x69,0x0, +0x0, 0x1, 0x2, 0x6a,0xb, 0x0, 0x1, 0x2, 0x6b,0x0, 0x0, 0x0, 0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0x56,0x31,0x2e,0x32,0x5f,0x41,0x70,0x72,0x20,0x32,0x37,0x20,0x32,0x30,0x32,0x31, +0x46,0x54,0x53,0x35,0x34,0x35,0x32,0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +0x7c,0x97,0x7c,0x8b,0x7f,0x70,0xd2,0xc9,0x7a,0x81,0xf2,0x75,0xf3,0x80,0xa, 0x59, +0x1b,0x54,0xf5,0xf4,0x75,0xf5,0x1, 0x6c,0xaa,0x80,0x14,0xe5,0xf5,0x54,0x80,0xb4, +0x80,0xf9,0xe5,0xf6,0xa, 0x3a,0x2d,0x3f,0x7d,0x2e,0x7a,0x1b,0xb0,0xb, 0xa0,0xbc, +0x9a,0x38,0xe8,0xc2,0xc9,0x22,0xd2,0xcf,0xe5,0xcc,0x54,0xf8,0xf5,0xcc,0xa9,0xd0, +0xce,0xa9,0xd2,0xc9,0xa9,0xd0,0xce,0xa9,0xd3,0xc9,0xa9,0xd5,0xca,0x75,0xeb,0x23, +0x75,0xed,0xf, 0x75,0xac,0x40,0x75,0xad,0x30,0xd2,0x86,0x75,0xec,0xff,0x75,0xee, +0xff,0xa9,0xc1,0xea,0xa9,0xc7,0x94,0xa9,0xc4,0x94,0xc2,0xae,0x7e,0x8, 0x0, 0xc, +0x74,0x21,0x7e,0x70,0x1, 0x91,0x20,0xe5,0xc, 0xbe,0xb0,0x9, 0x28,0x5, 0xa9,0xc3, +0xe2,0x80,0x3, 0xa9,0xd3,0xe2,0xe5,0xc, 0x7a,0xb3,0x2, 0x7d,0x22,0x7e,0x35,0x25, +0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, 0x7a,0x1f,0x12,0xb0,0x7e,0x35,0x25,0x7d, +0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, 0x7a,0x1f,0x12,0xac,0x7e,0x35,0x25,0x2e,0x34, +0x0, 0xa, 0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0xa, 0x7a,0x1f,0x10,0xc, 0x7e,0x35, +0x25,0x2e,0x34,0x0, 0xa, 0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, 0x7a,0x1f,0x12, +0xfc,0x22,0x7e,0x34,0x0, 0x4, 0x7e,0x54,0x0, 0x4, 0x7e,0x44,0x0, 0xff,0x69,0x12, +0x0, 0x2, 0xb, 0x2a,0x0, 0x79,0x30,0x0, 0x1e,0x7e,0x34,0x1, 0x0, 0x7e,0x24,0x10, +0x0, 0x9f,0x0, 0x59,0x12,0x0, 0x2, 0x1b,0x28,0x0, 0xb, 0x26,0x1b,0x34,0x78,0xf3, +0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, 0x7a,0x1f,0x10,0x1c, +0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, 0x7a,0x1f,0x10,0x20, +0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, 0x7a,0x1f,0x11,0x54, +0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, 0x7a,0x1f,0x12,0xa8, +0x7e,0x35,0x25,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, 0x7a,0x1f,0x12,0xb0,0x7e, +0x35,0x25,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, 0x7a,0x1f,0x12,0xac,0x7e,0x35, +0x25,0x2e,0x34,0x0, 0xa, 0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0xa, 0x7a,0x1f,0x10, +0xc, 0x7e,0x35,0x25,0x2e,0x34,0x0, 0xa, 0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, +0x7a,0x1f,0x12,0xfc,0x7e,0x34,0x2, 0x63,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, +0x7a,0x1f,0x12,0xc0,0x7e,0x34,0x2, 0x7a,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0xa, +0x7a,0x1f,0x12,0x40,0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, +0x7a,0x1f,0x11,0x84,0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, +0x7a,0x1f,0x11,0x7c,0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, +0x7a,0x1f,0x11,0x88,0x7e,0x34,0x2, 0x67,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0xa, +0x7a,0x1f,0x11,0xa8,0x7e,0x34,0x2, 0x79,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0xa, +0x7a,0x1f,0x13,0x30,0x7e,0x34,0x2, 0x79,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0xa, +0x7a,0x1f,0x11,0x98,0x7e,0x34,0x2, 0x79,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, +0x7a,0x1f,0x11,0x90,0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, +0x7a,0x1f,0x11,0x94,0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, +0x7a,0x1f,0x10,0x40,0x7e,0x34,0x2, 0x88,0x7d,0x23,0x6d,0x33,0x2e,0x18,0x0, 0x8, +0x7a,0x1f,0x10,0x24,0x22,0xa9,0xc4,0xe1,0xa9,0xc0,0xe1,0xa9,0xd0,0xce,0xd2,0xcc, +0x7e,0x34,0xff,0xff,0x7e,0x54,0x0, 0x4, 0x7e,0x44,0x0, 0xff,0x69,0x12,0x0, 0x2, +0xb, 0x2a,0x0, 0x79,0x30,0x0, 0x6, 0x7e,0x34,0x0, 0x1, 0x1b,0xa, 0x30,0xa9,0xc1, +0xe2,0xa9,0xc2,0xe2,0xd2,0x9e,0xc2,0x9d,0x43,0xeb,0xb4,0x7e,0x34,0x0, 0xef,0x7e, +0x54,0x0, 0x4, 0x7e,0x44,0x0, 0xff,0x69,0x12,0x0, 0x2, 0xb, 0x2a,0x0, 0x79,0x30, +0x0, 0x18,0x2e,0x14,0x0, 0xa, 0xb, 0xa, 0x30,0x5e,0x70,0xfc,0x1b,0xa, 0x30,0x7e, +0x34,0x0, 0x3, 0x7e,0x54,0x0, 0x4, 0x7e,0x44,0x0, 0xff,0x69,0x12,0x0, 0x2, 0xb, +0x2a,0x0, 0x79,0x30,0x0, 0x8, 0x7e,0x34,0x0, 0x8, 0x79,0x30,0x0, 0x4, 0x91,0xf2, +0xd2,0xac,0x22,0xc2,0xad,0x75,0x91,0x0, 0xc2,0x90,0xc2,0x91,0xa9,0xc0,0x93,0x75, +0x92,0x70,0xc2,0xc0,0xa9,0xd5,0xb7,0xd2,0xbd,0xd2,0xe8,0xd2,0xad,0x22,0x80,0x14, +0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, +0x0, 0x0, 0x0, 0x0, 0x7d,0x23,0x1b,0x34,0x4d,0x22,0x78,0xe4,0x22,0xc2,0xa9,0xc2, +0x8c,0x43,0x89,0x2, 0x75,0x8c,0x1, 0x75,0x8a,0x0, 0xd2,0xa9,0x22,0xc2,0x8c,0x6d, +0x33,0x7a,0x37,0x2, 0x86,0xd2,0x8c,0x22,0x7e,0x37,0x2, 0x86,0xb, 0x34,0x7a,0x37, +0x2, 0x86,0x22,0xe1,0x38,0xca,0x39,0xf1,0x38,0xda,0x39,0x32,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0x7c,0xab,0x7e,0x70,0xef,0x7e,0xb3,0x2, 0x44,0x24,0xfd,0x68,0x5c,0x1b,0xb1,0x68, +0x48,0x24,0x9f,0x68,0x67,0x1b,0xb2,0x68,0x69,0x24,0xda,0x68,0x1f,0x24,0xc4,0x68, +0x53,0x1b,0xb2,0x68,0x51,0x24,0xf5,0x78,0x6e,0x7e,0xa3,0x2, 0x6e,0x7c,0xba,0x4, +0x7a,0xb3,0x2, 0x6e,0xa, 0x3a,0x9, 0x73,0x2, 0x51,0x80,0x5b,0x4c,0xaa,0x78,0x5, +0x7e,0x70,0x54,0x80,0x52,0xbe,0xa0,0x1, 0x78,0x5, 0x7e,0x70,0x5b,0x80,0x48,0xbe, +0xa0,0x2, 0x78,0x43,0x7e,0x70,0xaa,0x80,0x3e,0x4c,0xaa,0x78,0x2, 0x80,0x5, 0xbe, +0xa0,0x1, 0x78,0x33,0x7e,0x70,0x1, 0x80,0x2e,0xa, 0x3a,0x2e,0x35,0x25,0x9, 0x73, +0x0, 0xa, 0x80,0x23,0x80,0x6, 0x7e,0x73,0x3, 0x8c,0x80,0x1b,0x7e,0x73,0x2, 0x79, +0x80,0x15,0x4c,0xaa,0x78,0x8, 0x7e,0x37,0x2, 0x67,0xa, 0x36,0x80,0x9, 0xbe,0xa0, +0x1, 0x78,0x4, 0x7e,0x37,0x2, 0x67,0x7c,0xb7,0x22,0x7c,0x97,0x7c,0x8b,0xc2,0x2, +0x7e,0xb3,0x2, 0x44,0x14,0x78,0x2, 0x41,0xa2,0x1b,0xb1,0x78,0x2, 0x41,0x22,0x1b, +0xb1,0x78,0x2, 0x41,0xa2,0x1b,0xb1,0x78,0x2, 0x41,0x65,0x14,0x78,0x2, 0x41,0x81, +0x14,0x78,0x2, 0x41,0x87,0x24,0xf9,0x78,0x2, 0x41,0x9d,0x24,0xbb,0x68,0x55,0x24, +0xf6,0x78,0x2, 0x61,0x7e,0x1b,0xb1,0x78,0x2, 0x61,0x6c,0x14,0x78,0x2, 0x61,0x76, +0x1b,0xb1,0x78,0x2, 0x41,0xa5,0x14,0x78,0x2, 0x41,0xa7,0x14,0x78,0x2, 0x41,0xa2, +0x1b,0xb2,0x78,0x2, 0x41,0xa2,0x24,0xda,0x68,0x2e,0x24,0xe6,0x68,0x28,0x24,0xfa, +0x68,0x3f,0x24,0xf1,0x68,0x74,0x24,0xf3,0x78,0x2, 0x41,0xa2,0x24,0xec,0x78,0x2, +0x61,0x8c,0x24,0x5, 0x68,0x2, 0x61,0x90,0xbe,0x80,0x1, 0x68,0x2, 0x61,0x90,0x7a, +0x93,0x2, 0x6e,0x22,0x61,0x86,0x61,0x86,0xbe,0x80,0x4, 0x50,0xb, 0xe4,0x7a,0xb3, +0x2, 0x79,0xa, 0x58,0x19,0x95,0x2, 0x47,0x4c,0x88,0x68,0x2, 0x61,0x90,0xd2,0x2, +0x22,0x7c,0xb8,0x14,0x68,0xa, 0x14,0x68,0xf, 0x14,0x68,0x1e,0x24,0x3, 0x78,0x2a, +0xa, 0x39,0x7d,0x23,0x6d,0x33,0x80,0xc, 0x7c,0x29,0x6c,0x33,0x6d,0x0, 0x7e,0x1f, +0x2, 0x63,0x2f,0x10,0x7a,0x1f,0x2, 0x63,0x80,0x10,0xa, 0x19,0x6d,0x0, 0x7e,0x1f, +0x2, 0x63,0x2f,0x10,0x7a,0x1f,0x2, 0x63,0xd2,0x3, 0x7c,0xb8,0x14,0x68,0x19,0x14, +0x68,0x27,0x14,0x68,0x26,0x14,0x68,0x25,0x14,0x68,0x2a,0x24,0x5, 0x78,0x48,0x20, +0x1, 0x2, 0x41,0xcc,0x41,0xc6,0x41,0xcc,0xa, 0x39,0x7d,0x3, 0x6d,0x11,0x7e,0x35, +0x25,0x59,0x13,0x0, 0x2, 0x1b,0x38,0x0, 0x22,0x41,0xef,0x61,0x4, 0x7c,0x69,0x6c, +0x77,0x6d,0x22,0x61,0x1c,0xa, 0x59,0x6d,0x44,0x7e,0x25,0x25,0x7d,0x32,0xb, 0x36, +0x49,0x13,0x0, 0x2, 0xb, 0x38,0x0, 0x2f,0x2, 0x59,0x13,0x0, 0x2, 0x1b,0x38,0x0, +0x6d,0x33,0x59,0x32,0x0, 0x8, 0x22,0x7e,0xb3,0x2, 0x79,0x6c,0xb9,0x7a,0xb3,0x2, +0x79,0x7e,0x25,0x25,0x2e,0x24,0x0, 0x8, 0xb, 0x28,0x50,0x7d,0x35,0xb, 0x34,0x1b, +0x28,0x30,0x2e,0x55,0x25,0x19,0x95,0x0, 0xa, 0x7e,0x55,0x25,0x49,0x15,0x0, 0x8, +0x6d,0x0, 0x49,0x35,0x0, 0x6, 0x49,0x25,0x0, 0x4, 0xbf,0x1, 0x50,0x2, 0x61,0x90, +0x20,0x1, 0x3, 0xd3,0x80,0x1, 0xc3,0x92,0x1, 0x7a,0x55,0x23,0x7e,0x34,0x0, 0x2, +0x61,0x67,0x7c,0xb8,0x14,0x68,0x15,0x14,0x68,0x14,0x14,0x68,0x13,0x24,0x3, 0x68, +0x2, 0x61,0x90,0x20,0x1, 0x2, 0x41,0xcc,0x41,0xc6,0x41,0xcc,0x41,0xd4,0x41,0xef, +0xa, 0x59,0x6d,0x44,0x7e,0x15,0x25,0x49,0x31,0x0, 0x2, 0xb, 0x18,0x20,0x2f,0x12, +0x59,0x31,0x0, 0x2, 0x1b,0x18,0x20,0x7e,0x35,0x25,0x7a,0x35,0x23,0xd2,0x2, 0x7e, +0x34,0x0, 0x1, 0x61,0x67,0x75,0xe6,0x0, 0x75,0xe7,0x0, 0xe4,0x7e,0x34,0x1, 0x6, +0x7e,0x24,0x0, 0xff,0x7a,0x1b,0xb0,0x7e,0x34,0x1, 0x7, 0x7a,0x1b,0xb0,0xd2,0x6, +0x22,0x75,0xe6,0x0, 0xd2,0x9f,0x22,0xbe,0x80,0x1, 0x78,0x5, 0x7a,0x93,0x2, 0x6a, +0x22,0xbe,0x80,0x2, 0x68,0x2, 0x61,0x90,0x7a,0x93,0x2, 0x46,0x22,0x7a,0x93,0x2, +0x69,0x22,0xd2,0x2, 0x22,0x61,0x86,0x7c,0xb8,0x14,0x68,0x28,0x14,0x68,0x40,0x14, +0x68,0x52,0x14,0x68,0x61,0x14,0x68,0x70,0x14,0x78,0x2, 0x61,0x45,0x24,0x6, 0x68, +0x2, 0x61,0x90,0x30,0x1, 0x6, 0x7e,0x34,0x0, 0x28,0x80,0x4, 0x7e,0x34,0x1, 0x36, +0x7a,0x35,0x25,0x22,0xa, 0x39,0x6d,0x22,0x7e,0x15,0x25,0x59,0x31,0x0, 0x2, 0x1b, +0x18,0x20,0x49,0x31,0x0, 0x2, 0xb, 0x18,0x20,0x7d,0x23,0x6d,0x33,0x80,0x4e,0xa, +0x59,0x7c,0xab,0xe4,0x6d,0x44,0x7e,0x15,0x25,0x49,0x31,0x0, 0x2, 0xb, 0x18,0x20, +0x2f,0x12,0x80,0x39,0xa, 0x59,0x6d,0x44,0x7e,0x15,0x25,0x49,0x31,0x0, 0x2, 0xb, +0x18,0x20,0x2f,0x12,0x80,0x27,0xa, 0x39,0x7d,0x23,0x6d,0x33,0x7e,0x15,0x25,0x59, +0x31,0x0, 0x6, 0x59,0x21,0x0, 0x4, 0x22,0xa, 0x59,0x7c,0xab,0xe4,0x6d,0x44,0x7e, +0x15,0x25,0xb, 0x16,0x49,0x31,0x0, 0x2, 0xb, 0x18,0x20,0x2f,0x12,0x59,0x31,0x0, +0x2, 0x1b,0x18,0x20,0x22,0xa, 0x59,0x6d,0x44,0x7e,0x35,0x25,0x7d,0x23,0xb, 0x26, +0x49,0x12,0x0, 0x2, 0xb, 0x28,0x0, 0x2f,0x2, 0x59,0x12,0x0, 0x2, 0x1b,0x28,0x0, +0x7a,0x35,0x23,0x7e,0x34,0x0, 0x3, 0x7a,0x37,0x2, 0x61,0x22,0x7e,0x34,0x0, 0x4, +0x7a,0x37,0x2, 0x61,0x80,0x10,0xe4,0x7c,0x78,0x7c,0x69,0x2, 0x18,0x0, 0x7e,0x34, +0x0, 0x5, 0x7a,0x37,0x2, 0x61,0xe4,0x7a,0xb3,0x2, 0x79,0x22,0x7a,0x93,0x2, 0x84, +0x22,0xca,0x7b,0xca,0x2b,0xca,0x1b,0xca,0xb, 0xd2,0x0, 0x30,0x90,0x22,0xc2,0x90, +0x7e,0x71,0x91,0x7e,0xb3,0x2, 0x6b,0x70,0x4, 0x7a,0x73,0x2, 0x44,0x11,0x9a,0x7e, +0xb3,0x2, 0x6b,0x4, 0x7a,0xb3,0x2, 0x6b,0x30,0x2, 0x5, 0xe4,0x11,0x0, 0xf5,0x91, +0x30,0x91,0xf, 0xc2,0x91,0x7e,0xb3,0x2, 0x6b,0x4, 0x7a,0xb3,0x2, 0x6b,0x11,0x0, +0xf5,0x91,0xda,0xb, 0xda,0x1b,0xda,0x2b,0xda,0x7b,0x32,0xca,0xb8,0xa9,0xc0,0x93, +0xe4,0x7a,0xb3,0x2, 0x6b,0xda,0xb8,0x32,0x7c,0x7b,0x2e,0x70,0xf9,0x78,0x2, 0x81, +0xe0,0x1b,0x70,0x78,0x2, 0x81,0xfc,0x1b,0x70,0x78,0x2, 0xa1,0x2, 0x2e,0x70,0xf9, +0x78,0x2, 0xa1,0x13,0x2e,0x70,0xb1,0x78,0x2, 0xa1,0x8c,0x1b,0x71,0x78,0x2, 0xa1, +0x79,0x1b,0x70,0x78,0x2, 0xa1,0x83,0x1b,0x71,0x78,0x2, 0xa1,0x1c,0x1b,0x70,0x78, +0x2, 0xa1,0x1e,0x2e,0x70,0xd5,0x68,0x1f,0x2e,0x70,0xe6,0x68,0x1a,0x1b,0x70,0x68, +0x29,0x1b,0x70,0x78,0x2, 0x81,0xb8,0x1b,0x72,0x68,0xe, 0x2e,0x70,0xf1,0x68,0x32, +0x2e,0x70,0x6a,0x68,0x2, 0xa1,0x99,0xa1,0x94,0x7e,0x1f,0x2, 0x63,0x7c,0x76,0x7c, +0x65,0xa, 0x24,0x7a,0x1f,0x2, 0x63,0xd2,0x3, 0x22,0x7e,0x15,0x25,0x49,0x31,0x0, +0x2, 0xb, 0x18,0x20,0x7c,0x76,0x7c,0x65,0xa, 0x24,0x59,0x31,0x0, 0x2, 0x1b,0x18, +0x20,0x22,0x7e,0x14,0x0, 0x4, 0x7e,0x4, 0x0, 0xff,0x69,0x30,0x0, 0x2, 0xb, 0xa, +0x20,0x69,0x11,0x0, 0x10,0x6d,0x0, 0x7e,0x35,0x25,0x59,0x13,0x0, 0x6, 0x59,0x3, +0x0, 0x4, 0x7a,0x35,0x23,0x7e,0x34,0x0, 0x2, 0x7a,0x37,0x2, 0x61,0x20,0x1, 0x3, +0xd3,0x80,0x1, 0xc3,0x92,0x1, 0x30,0x1, 0x6, 0x7e,0x34,0x0, 0x28,0x80,0x4, 0x7e, +0x34,0x1, 0x36,0x7a,0x35,0x25,0x80,0x25,0x7e,0x15,0x25,0x49,0x31,0x0, 0x2, 0xb, +0x18,0x20,0x7c,0x76,0x7c,0x65,0xa, 0x24,0x59,0x31,0x0, 0x2, 0x1b,0x18,0x20,0x7e, +0x35,0x25,0x7a,0x35,0x23,0x7e,0x34,0x0, 0x1, 0x7a,0x37,0x2, 0x61,0x2, 0x4, 0xad, +0x75,0xe6,0x0, 0x75,0xe7,0x0, 0xe4,0x7e,0x34,0x1, 0x6, 0x7e,0x24,0x0, 0xff,0x7a, +0x1b,0xb0,0x7e,0x34,0x1, 0x7, 0x7a,0x1b,0xb0,0xd2,0x6, 0x22,0x75,0xe6,0x0, 0xd2, +0x9f,0x22,0x7e,0x73,0x2, 0x88,0x7a,0x73,0x2, 0x6a,0x7e,0x73,0x2, 0x89,0x7a,0x73, +0x2, 0x46,0x22,0x7e,0x73,0x2, 0x88,0x7a,0x73,0x2, 0x69,0x22,0x80,0x76,0x7e,0x73, +0x2, 0x89,0xa, 0x37,0x6d,0x22,0x7c,0x67,0x6c,0x77,0x7e,0x33,0x2, 0x88,0xa, 0x13, +0x7d,0x1, 0x6d,0x11,0x2f,0x1, 0x7e,0x73,0x2, 0x8a,0xa, 0x37,0x2f,0x10,0x7e,0x15, +0x23,0x59,0x31,0x0, 0x2, 0x1b,0x18,0x20,0x7e,0x73,0x2, 0x8c,0xa, 0x37,0x6d,0x22, +0x7c,0x67,0x6c,0x77,0x7e,0x13,0x2, 0x8b,0xa, 0x51,0x7d,0x45,0x6d,0x55,0x2f,0x21, +0x7e,0x73,0x2, 0x8d,0xa, 0x37,0x2f,0x12,0x59,0x31,0x0, 0x6, 0x59,0x21,0x0, 0x4, +0x7e,0x34,0x0, 0x3, 0x7a,0x37,0x2, 0x61,0x22,0x7e,0x34,0x0, 0x4, 0x7a,0x37,0x2, +0x61,0x80,0x11,0x74,0x1, 0x6c,0x77,0x6c,0x66,0x2, 0x18,0x0, 0x7e,0x34,0x0, 0x5, +0x7a,0x37,0x2, 0x61,0xe4,0x7a,0xb3,0x2, 0x79,0x22,0xca,0x7b,0xca,0x2b,0xca,0x1b, +0xca,0xb, 0xc2,0x0, 0x7e,0x14,0x0, 0x4, 0x7e,0x4, 0x0, 0xff,0x69,0x50,0x0, 0x2, +0xb, 0xa, 0x40,0xb, 0x2a,0x30,0x4e,0x70,0x80,0x1b,0x2a,0x30,0x69,0x30,0x0, 0x2, +0xb, 0xa, 0x20,0x69,0x11,0x0, 0x2, 0x5e,0x14,0xff,0x0, 0x68,0x3f,0xb, 0x1a,0x10, +0x4e,0x30,0x8, 0x1b,0x1a,0x10,0x7e,0x14,0x0, 0x4, 0x7e,0x4, 0x0, 0xff,0x69,0x50, +0x0, 0x2, 0xb, 0xa, 0x40,0x69,0x12,0x0, 0x2, 0xa, 0x2, 0x1e,0x4, 0x1e,0x4, 0x5e, +0x4, 0x0, 0x1, 0x78,0xc, 0xa, 0x12,0x1e,0x14,0x5e,0x14,0x0, 0x1, 0x78,0x2, 0xc1, +0x96,0xb, 0x2a,0x10,0x4e,0x30,0x4, 0x1b,0x2a,0x10,0xc1,0x96,0x69,0x11,0x0, 0x2, +0x1e,0x14,0x1e,0x14,0x1e,0x14,0x5e,0x14,0x0, 0x1, 0x68,0x6, 0x69,0x51,0x0, 0xe, +0x71,0xe8,0x7e,0x34,0x0, 0x4, 0x7e,0x24,0x0, 0xff,0x69,0x11,0x0, 0x2, 0xb, 0x1a, +0x0, 0x69,0x30,0x0, 0x2, 0x1e,0x34,0x1e,0x34,0x5e,0x34,0x0, 0x1, 0x68,0x9, 0xb, +0xa, 0x30,0x5e,0x70,0xdf,0x1b,0xa, 0x30,0x7e,0x34,0x0, 0x4, 0x7e,0x24,0x0, 0xff, +0x69,0x11,0x0, 0x2, 0xb, 0x1a,0x0, 0x69,0x30,0x0, 0x2, 0x1e,0x34,0x5e,0x34,0x0, +0x1, 0x68,0xd, 0x2e,0x14,0x0, 0x6, 0xb, 0xa, 0x30,0x4e,0x70,0x2, 0x1b,0xa, 0x30, +0x7e,0x34,0x0, 0x4, 0x7e,0x24,0x0, 0xff,0x69,0x11,0x0, 0x2, 0xb, 0x1a,0x0, 0x69, +0x30,0x0, 0x2, 0x5e,0x34,0x0, 0x1, 0x68,0xd, 0x2e,0x14,0x0, 0x6, 0xb, 0xa, 0x30, +0x4e,0x70,0x1, 0x1b,0xa, 0x30,0x7e,0x34,0xff,0xff,0x7e,0x54,0x0, 0x4, 0x7e,0x44, +0x0, 0xff,0x69,0x12,0x0, 0x2, 0xb, 0x2a,0x0, 0x79,0x30,0x0, 0x6, 0xb, 0xa, 0x30, +0x5e,0x34,0x0, 0x3, 0x1b,0xa, 0x30,0xda,0xb, 0xda,0x1b,0xda,0x2b,0xda,0x7b,0x32, +0x7f,0x50,0x7f,0x41,0x6c,0xaa,0xbe,0x58,0x0, 0x0, 0x38,0x3a,0x7e,0x58,0xff,0xff, +0x7e,0xa0,0x1, 0x80,0x31,0x7e,0x18,0x0, 0x80,0x7a,0x1d,0x10,0x7f,0x14,0x7e,0x8, +0x3, 0x8d,0x12,0x14,0x93,0x9e,0x58,0x0, 0x80,0x2e,0x48,0x0, 0x80,0x6d,0x44,0x9, +0x34,0x3, 0x8d,0x7e,0x1d,0xc, 0x7e,0x1b,0xb0,0x6c,0xb3,0x7a,0x1b,0xb0,0xb, 0x44, +0xbe,0x44,0x0, 0x80,0x78,0xe9,0xbe,0x58,0x0, 0x80,0x38,0xc9,0x4c,0xaa,0x68,0x2, +0xb, 0x5c,0x7a,0x5d,0x10,0x7f,0x14,0x7e,0x8, 0x3, 0x8d,0x12,0x14,0x93,0x6d,0x44, +0x80,0x11,0x9, 0x64,0x3, 0x8d,0x7e,0xd, 0xc, 0x7e,0xb, 0x70,0x6c,0x76,0x7a,0xb, +0x70,0xb, 0x44,0x7d,0x34,0x6d,0x22,0xbf,0x15,0x40,0xe7,0x22,0xca,0x3b,0x7f,0x60, +0x7f,0x71,0x7e,0x3d,0xc, 0x75,0x14,0x0, 0xa2,0x3, 0xe4,0x33,0x78,0xc, 0x7a,0x3d, +0x15,0x7f,0x17,0x7f,0x6, 0x12,0x11,0x6e,0xe1,0xf6,0x7f,0x57,0x7f,0x7, 0x2f,0x3, +0x7a,0xd, 0x10,0x7e,0x1f,0x2, 0x63,0xbf,0x1, 0x28,0x2, 0xe1,0xf6,0x4d,0xef,0x78, +0x3, 0x75,0x27,0x0, 0xe5,0x27,0xbe,0xb0,0x4, 0x50,0x46,0xe5,0x27,0xa, 0x2b,0x7e, +0x34,0x0, 0x4, 0x9d,0x32,0x7a,0x71,0x14,0xa, 0x37,0x6d,0x22,0xbf,0x13,0x28,0x4, +0x7c,0xbf,0xf5,0x14,0xe5,0x14,0xa, 0x3b,0xca,0x39,0xe5,0x27,0xa, 0x1b,0x2e,0x14, +0x2, 0x6f,0x6d,0x0, 0x7f,0x16,0x12,0x1, 0x20,0x1b,0xfd,0xe5,0x14,0x25,0x27,0xf5, +0x27,0xe5,0x14,0xa, 0x3b,0x6d,0x22,0x2f,0x51,0xe5,0x14,0xa, 0x1b,0x2d,0xd1,0x9f, +0x31,0xbe,0x38,0x0, 0x0, 0x28,0xa, 0x7a,0x3d,0x15,0x7f,0x15,0x7f,0x6, 0x12,0x11, +0x6e,0x7e,0xf, 0x2, 0x63,0x7e,0x1d,0x10,0xbf,0x10,0x78,0x1a,0xe5,0x27,0xb4,0x4, +0x15,0x7e,0x18,0x0, 0x4, 0x7a,0x1d,0x15,0x9f,0x11,0x7e,0x8, 0x2, 0x6f,0x12,0x11, +0x6e,0x75,0x27,0xff,0xc2,0x3, 0xda,0x3b,0x22,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xa9,0xc2,0xb4,0x74,0x9f,0x11,0x68,0x7e,0x8, 0x0, 0xc, 0x7e,0x18,0x0, 0x1, 0x11, +0x83,0xa9,0xd2,0xb4,0xe5,0xc, 0x60,0x5, 0xe5,0xc, 0xb4,0xff,0x2, 0xc3,0x22,0xd3, +0x22,0xd2,0xc8,0x43,0xed,0xf, 0xc2,0xea,0x75,0xb3,0x13,0xa9,0xd1,0xb4,0xa9,0xc0, +0xb4,0x22,0xca,0xf8,0x7e,0xf0,0x10,0x7e,0x34,0x15,0x0, 0xc2,0xa, 0x7d,0x23,0x1b, +0x34,0x4d,0x22,0x78,0xf8,0x11,0x0, 0x92,0xa, 0x20,0xa, 0x19,0x75,0xed,0x9f,0xd2, +0x8, 0xa9,0xd1,0xea,0xa9,0xd5,0xca,0xa9,0xc1,0xea,0x80,0x1, 0x0, 0x7c,0xaf,0x1b, +0xf0,0x4c,0xaa,0x78,0xf7,0xda,0xf8,0x22,0x7c,0x7b,0x7a,0x71,0xb5,0xa9,0x36,0xb3, +0xfc,0xa9,0xc6,0xb3,0xd3,0x22,0x75,0xb5,0x0, 0xa9,0x36,0xb3,0xfc,0xa9,0xc6,0xb3, +0xe5,0xb5,0x22,0x7f,0x61,0x7f,0x70,0x9f,0x11,0x80,0x13,0x75,0xb5,0x0, 0xa9,0x36, +0xb3,0xfc,0xa9,0xc6,0xb3,0xe5,0xb5,0x7a,0x7b,0xb0,0xb, 0xf4,0xb, 0x1c,0xbf,0x16, +0x40,0xe9,0x22,0xa9,0xc2,0xb4,0x30,0xa, 0x6, 0x74,0x6, 0x11,0x68,0x80,0x4, 0x74, +0x4, 0x11,0x68,0xa9,0xd2,0xb4,0x22,0xa9,0xc2,0xb4,0x74,0x5, 0x11,0x68,0x11,0x76, +0xa9,0xd2,0xb4,0x30,0xe0,0x2, 0xd3,0x22,0xc3,0x22,0xca,0xf8,0x7f,0x1, 0x7c,0xfb, +0x7c,0xb1,0xf5,0x1f,0x7c,0xb2,0xf5,0x1e,0x7c,0xb3,0xf5,0x1d,0xa9,0xc2,0xb4,0x7c, +0xbf,0x11,0x68,0xe5,0x1f,0x11,0x68,0xe5,0x1e,0x11,0x68,0xe5,0x1d,0x11,0x68,0xda, +0xf8,0x22,0x7f,0x70,0x7f,0x61,0xe5,0x22,0xb4,0x1, 0x1d,0x7e,0x34,0xf0,0x0, 0x7e, +0x24,0x0, 0x1, 0xbf,0x61,0x50,0x11,0x7e,0xd, 0x19,0x2f,0x6, 0x7e,0x34,0xe0,0x0, +0x7e,0x24,0x0, 0x1, 0xbf,0x1, 0x38,0x55,0xd2,0xa, 0x11,0xa3,0x74,0x2, 0x7f,0x16, +0x11,0xca,0x9f,0x11,0x80,0x14,0x7d,0x13,0x2d,0x1f,0x7d,0xe, 0x7e,0xb, 0xb0,0xf5, +0xb5,0xa9,0x36,0xb3,0xfc,0xa9,0xc6,0xb3,0xb, 0x1c,0x7e,0xd, 0x19,0xbf,0x10,0x40, +0xe5,0xa9,0xd2,0xb4,0x12,0x7, 0x2d,0x11,0xb7,0x50,0xa, 0x7e,0x37,0x2, 0x86,0xbe, +0x34,0x1, 0xf4,0x28,0xf2,0x7e,0x37,0x2, 0x86,0xbe,0x34,0x1, 0xf4,0x38,0x3, 0x2, +0x7, 0x43,0xc2,0x86,0x7e,0x34,0x13,0x88,0x12,0x6, 0xfe,0xd2,0x86,0x22,0xca,0x3b, +0x7f,0x30,0x7f,0x51,0x7e,0x4d,0x15,0x7f,0x15,0x5e,0x34,0x0, 0x7f,0x7d,0x53,0x7e, +0x44,0x0, 0x80,0x9d,0x45,0x7d,0x34,0x6d,0x22,0xbf,0x14,0x40,0xc, 0x7a,0x4d,0x19, +0x7f,0x15,0x7f,0x3, 0x11,0xf2,0xd3,0x80,0x48,0x7d,0x34,0x6d,0x22,0x7a,0x1d,0x19, +0x7f,0x15,0x7f,0x3, 0x11,0xf2,0x7d,0x34,0x6d,0x22,0x9f,0x41,0x2f,0x51,0x2d,0x74, +0x80,0x19,0x7e,0x18,0x0, 0x80,0x7a,0x1d,0x19,0x7f,0x15,0x7f,0x3, 0x11,0xf2,0x9e, +0x48,0x0, 0x80,0x2e,0x74,0x0, 0x80,0x2e,0x58,0x0, 0x80,0xbe,0x48,0x0, 0x80,0x50, +0xe1,0xbe,0x48,0x0, 0x0, 0x68,0x9, 0x7a,0x4d,0x19,0x7f,0x15,0x7f,0x3, 0x11,0xf2, +0xd3,0xda,0x3b,0x22,0x7c,0x6b,0xd2,0xa, 0x11,0xa3,0x74,0x20,0xca,0xb8,0xa, 0x36, +0x6d,0x22,0x74,0xc, 0x2f,0x11,0x14,0x78,0xfb,0xda,0xb8,0x11,0xca,0xa9,0xd2,0xb4, +0x12,0x7, 0x2d,0x11,0xb7,0x50,0xa, 0x7e,0x37,0x2, 0x86,0xbe,0x34,0x1, 0xf4,0x28, +0xf2,0x7e,0x37,0x2, 0x86,0xbe,0x34,0x1, 0xf4,0x38,0x5, 0x12,0x7, 0x43,0xd3,0x22, +0xc2,0x86,0x7e,0x34,0x13,0x88,0x12,0x6, 0xfe,0xd2,0x86,0xc3,0x22,0xd2,0xa, 0x11, +0xa3,0xa9,0xc2,0xb4,0x74,0x60,0x11,0x68,0xa9,0xd2,0xb4,0x11,0xb7,0x40,0xfc,0xd3, +0x22,0x7c,0x7b,0xa9,0xc2,0xb4,0xa9,0xc6,0xb3,0x75,0xb5,0x5, 0xa9,0x36,0xb3,0xfc, +0xa9,0xc6,0xb3,0x75,0xb5,0x0, 0xa9,0x36,0xb3,0xfc,0xa9,0xc6,0xb3,0x75,0xb5,0x0, +0xa9,0x36,0xb3,0xfc,0xa9,0xc6,0xb3,0x7a,0x71,0xb5,0xa9,0x36,0xb3,0xfc,0xa9,0xc6, +0xb3,0x75,0xb5,0x0, 0xa9,0x36,0xb3,0xfc,0xa9,0xc6,0xb3,0x75,0xb5,0x0, 0xa9,0x36, +0xb3,0xfc,0xa9,0xc6,0xb3,0x7e,0x61,0xb5,0x75,0xb5,0x0, 0xa9,0x36,0xb3,0xfc,0xa9, +0xc6,0xb3,0x7e,0x71,0xb5,0xa9,0xd2,0xb4,0x7c,0x46,0x7c,0x64,0x22,0x7d,0x43,0x7c, +0x5b,0xa, 0x58,0x7d,0x34,0x7c,0x47,0xa9,0xc2,0xb4,0xa9,0xc6,0xb3,0x75,0xb5,0x1, +0xa9,0x36,0xb3,0xfc,0xa9,0xc6,0xb3,0x75,0xb5,0x0, 0xa9,0x36,0xb3,0xfc,0xa9,0xc6, +0xb3,0x75,0xb5,0x0, 0xa9,0x36,0xb3,0xfc,0xa9,0xc6,0xb3,0x7a,0x51,0xb5,0xa9,0x36, +0xb3,0xfc,0xa9,0xc6,0xb3,0xf5,0xb5,0xa9,0x36,0xb3,0xfc,0xa9,0xc6,0xb3,0x7a,0x41, +0xb5,0xa9,0x36,0xb3,0xfc,0xa9,0xc6,0xb3,0xa9,0xd2,0xb4,0x22,0x7c,0x7b,0xbe,0x70, +0x0, 0x28,0x2, 0x61,0x22,0x74,0x1, 0x7e,0x34,0xdf,0xff,0x51,0x9d,0x74,0x6, 0x51, +0x41,0x7d,0x13,0x6c,0x33,0x7d,0x31,0x51,0x9d,0x74,0x6, 0x7d,0x31,0x51,0x9d,0x74, +0x6, 0x7d,0x31,0x51,0x9d,0x74,0x2, 0x51,0x41,0x7d,0x13,0x4e,0x30,0x1, 0x7d,0x31, +0x41,0x9d,0x74,0x6, 0x51,0x41,0x7d,0x13,0x7e,0x30,0xa5,0x7d,0x31,0x51,0x9d,0x7e, +0x30,0xf, 0x74,0x6, 0x7d,0x31,0x51,0x9d,0x7e,0x30,0x6a,0x74,0x6, 0x7d,0x31,0x51, +0x9d,0x7e,0x34,0x0, 0x5, 0x2, 0x6, 0xfe,0xca,0x79,0x7c,0xf7,0x7c,0xeb,0x74,0x1, +0x51,0xec,0x74,0x1, 0x6d,0x33,0x51,0x9d,0xe4,0x51,0x41,0x7d,0x13,0x5e,0x14,0x80, +0x0, 0x7c,0xf, 0x6c,0x11,0x3e,0x4, 0x4d,0x10,0x7d,0x31,0x51,0x9d,0x6d,0x0, 0xbe, +0xe0,0x1, 0x78,0x6, 0x7e,0x14,0x0, 0x8, 0x80,0x2, 0x7d,0x10,0x74,0x4, 0x7d,0x31, +0x51,0x9d,0x4e,0x30,0x10,0x4e,0x30,0x40,0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34, +0x0, 0x19,0x12,0x6, 0xfe,0x4e,0x30,0x1, 0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34, +0xa2,0x1c,0x12,0x6, 0xfe,0x5e,0x30,0xbf,0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34, +0x0, 0x19,0x12,0x6, 0xfe,0x5e,0x30,0xfe,0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34, +0x0, 0x19,0x12,0x6, 0xfe,0x5e,0x30,0xef,0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34, +0x0, 0x5, 0x12,0x6, 0xfe,0xe4,0x51,0xec,0x7e,0x34,0xff,0xf7,0x74,0x4, 0x51,0x9d, +0xda,0x79,0x22,0xca,0xf8,0x7c,0xfb,0x74,0x1, 0x51,0xec,0xe4,0x6d,0x33,0x51,0x9d, +0x74,0x1, 0x6d,0x33,0x51,0x9d,0x6d,0x0, 0xbe,0xf0,0x1, 0x78,0x6, 0x7e,0x14,0x0, +0x8, 0x80,0x2, 0x7d,0x10,0x74,0x4, 0x7d,0x31,0x51,0x9d,0x4e,0x30,0x10,0x4e,0x30, +0x40,0x4e,0x30,0x4, 0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34,0x0, 0x19,0x12,0x6, +0xfe,0x4e,0x30,0x1, 0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34,0xa2,0x1c,0x12,0x6, +0xfe,0x5e,0x30,0xbf,0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34,0x0, 0xa0,0x12,0x6, +0xfe,0x5e,0x30,0xfe,0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34,0x0, 0x19,0x12,0x6, +0xfe,0x5e,0x30,0xef,0x74,0x4, 0x7d,0x31,0x51,0x9d,0x7e,0x34,0x0, 0x1, 0x12,0x6, +0xfe,0xe4,0x51,0xec,0x7e,0x34,0xff,0xf7,0x74,0x4, 0x51,0x9d,0xda,0xf8,0x22,0x7f, +0x1, 0x7c,0xb1,0xf5,0x16,0x7c,0xb2,0xf5,0x15,0x7c,0xb3,0xf5,0x14,0xa9,0xc2,0xb4, +0x74,0xb, 0x11,0x68,0xe5,0x16,0x11,0x68,0xe5,0x15,0x11,0x68,0xe5,0x14,0x11,0x68, +0xe4,0x1, 0x68,0x7f,0x70,0x7f,0x1, 0x91,0x6f,0x7f,0x7, 0x7e,0x1d,0x10,0x11,0x83, +0xa9,0xd2,0xb4,0xd3,0x22,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, +0x7c,0xa7,0xa, 0xf6,0x6d,0xee,0x60,0x2, 0x1, 0xb0,0x1b,0xa0,0x68,0x23,0x1b,0xa0, +0x68,0x2e,0x1b,0xa0,0x68,0x3a,0x1b,0xa0,0x68,0x44,0x1b,0xa0,0x68,0x50,0x1b,0xa0, +0x68,0x6d,0x2e,0xa0,0x6, 0x68,0x2, 0x21,0x15,0x7e,0x34,0x0, 0x28,0x7a,0x35,0x25, +0x22,0x7d,0xf, 0x6d,0x11,0x7e,0x35,0x25,0x59,0x13,0x0, 0x2, 0x1b,0x38,0x0, 0x22, +0x7d,0x4e,0x7c,0x89,0x7d,0x5f,0x7c,0x9a,0x7c,0xab,0xe4,0x7e,0x15,0x25,0x80,0x2e, +0x7e,0x15,0x25,0x49,0x31,0x0, 0x2, 0xb, 0x18,0x20,0x2f,0x17,0x80,0x29,0x7d,0x2f, +0x6d,0x33,0x7e,0x15,0x25,0x59,0x31,0x0, 0x6, 0x59,0x21,0x0, 0x4, 0x22,0x7d,0x4e, +0x7c,0x89,0x7d,0x5f,0x7c,0x9a,0x7c,0xab,0xe4,0x7e,0x15,0x25,0xb, 0x16,0x49,0x31, +0x0, 0x2, 0xb, 0x18,0x20,0x2f,0x12,0x59,0x31,0x0, 0x2, 0x1b,0x18,0x20,0x22,0x7e, +0x35,0x25,0x7d,0x23,0xb, 0x26,0x49,0x12,0x0, 0x2, 0xb, 0x28,0x0, 0x2f,0x7, 0x59, +0x12,0x0, 0x2, 0x1b,0x28,0x0, 0x7e,0x24,0x0, 0x6, 0x7a,0x27,0x2, 0x61,0x80,0x5d, +0x7e,0x73,0x2, 0x89,0xa, 0x37,0x6d,0x22,0x7c,0x67,0x6c,0x77,0x7e,0x33,0x2, 0x88, +0xa, 0x13,0x7d,0x1, 0x6d,0x11,0x2f,0x1, 0x7e,0x73,0x2, 0x8a,0xa, 0x37,0x2f,0x10, +0x7e,0x15,0x25,0x59,0x31,0x0, 0x2, 0x1b,0x18,0x20,0x7e,0x73,0x2, 0x8c,0xa, 0x37, +0x6d,0x22,0x7c,0x67,0x6c,0x77,0x7e,0x13,0x2, 0x8b,0xa, 0x51,0x7d,0x45,0x6d,0x55, +0x2f,0x21,0x7e,0x73,0x2, 0x8d,0xa, 0x37,0x2f,0x12,0x59,0x31,0x0, 0x6, 0x59,0x21, +0x0, 0x4, 0x7e,0x34,0x0, 0x6, 0x7a,0x37,0x2, 0x61,0x7e,0x35,0x25,0x7a,0x35,0x23, +0xe4,0x7a,0xb3,0x2, 0x79,0x22,0x75,0xa8,0x0, 0x75,0xdf,0x0, 0x12,0x4, 0x56,0x12, +0x6, 0x65,0x12,0x10,0x21,0xd2,0x7, 0x12,0x10,0x32,0x12,0x6, 0xe3,0x12,0x7, 0x1d, +0xd2,0xaf,0x22,0x7e,0xb3,0x2, 0x69,0xb4,0xc, 0xb, 0xc2,0x86,0x7e,0x34,0x0, 0x64, +0x12,0x6, 0xfe,0xd2,0x86,0x22,0x7e,0xa3,0x2, 0x6a,0xbe,0xa0,0xb, 0x78,0x3a,0x7e, +0x1f,0x2, 0x63,0xbe,0x18,0x0, 0x0, 0x68,0x12,0x7e,0x14,0xe0,0x0, 0x7e,0x4, 0x0, +0x1, 0xbf,0x10,0x38,0x6, 0xa2,0x3, 0xe4,0x33,0x78,0x3, 0x74,0x1, 0x22,0x7e,0x45, +0x23,0x49,0xf4,0x0, 0x6, 0x49,0xe4,0x0, 0x4, 0x49,0x34,0x0, 0x2, 0xb, 0x48,0x20, +0x2f,0x17,0xbf,0x10,0x40,0xb, 0x74,0x1, 0x22,0xbe,0xa0,0xa, 0x68,0x3, 0x74,0x1, +0x22,0xe4,0x22,0x6d,0x33,0x7a,0x37,0x2, 0x61,0x7e,0x34,0x0, 0x28,0x7a,0x35,0x25, +0x31,0x16,0x7e,0x37,0x2, 0x61,0x1b,0x34,0x68,0x6d,0x1b,0x35,0x78,0x2, 0x41,0x4f, +0x1b,0x34,0x78,0x2, 0x41,0x81,0x1b,0x34,0x78,0x2, 0x61,0x3f,0x1b,0x34,0x78,0x2, +0x61,0x70,0xb, 0x36,0x68,0x2, 0x81,0x27,0x6d,0x33,0x7a,0x37,0x2, 0x61,0x7a,0x37, +0x2, 0x67,0x31,0x46,0x60,0x2, 0x81,0x27,0x7e,0x15,0x23,0x49,0x31,0x0, 0x6, 0x49, +0x21,0x0, 0x4, 0x7a,0x1d,0xc, 0x49,0x31,0x0, 0x2, 0xb, 0x18,0x20,0x2e,0x14,0x0, +0xa, 0x6d,0x0, 0x12,0xf, 0x3c,0x7e,0x55,0x23,0x49,0x15,0x0, 0x6, 0x49,0x5, 0x0, +0x4, 0x49,0x35,0x0, 0x2, 0xb, 0x58,0x20,0x12,0x0, 0x9d,0x2e,0x34,0x10,0x0, 0x7a, +0x37,0x2, 0x67,0x31,0x33,0x81,0x27,0x6d,0x33,0x7a,0x37,0x2, 0x61,0x7a,0x37,0x2, +0x67,0x7e,0x18,0x1, 0x0, 0x7a,0x1d,0x10,0x7e,0x15,0x23,0x49,0x31,0x0, 0x2, 0xb, +0x18,0x20,0x2e,0x14,0x0, 0xa, 0x6d,0x0, 0x12,0x14,0x93,0x30,0x0, 0x9, 0x7e,0x35, +0x23,0x9, 0xb3,0x0, 0xa, 0xf5,0x91,0x31,0x33,0x7e,0x34,0xf0,0x88,0x80,0x2c,0x6d, +0x33,0x7a,0x37,0x2, 0x61,0x7a,0x37,0x2, 0x67,0x7e,0x18,0x2, 0x79,0x7a,0x1d,0xc, +0x7e,0x55,0x23,0x49,0x35,0x0, 0x2, 0xb, 0x58,0x20,0x49,0x15,0x0, 0x6, 0x49,0x5, +0x0, 0x4, 0x12,0xe, 0xc0,0x31,0x33,0x7e,0x34,0xf0,0x55,0x7a,0x37,0x2, 0x67,0x81, +0x27,0x75,0x22,0x0, 0x6d,0x33,0x7a,0x37,0x2, 0x61,0x7a,0x37,0x2, 0x67,0x7e,0xb3, +0x2, 0x6a,0xb4,0xa, 0xe, 0x30,0x8, 0x6, 0xe4,0x12,0x13,0xe3,0x81,0x1d,0x12,0x12, +0x2d,0x81,0x1d,0x7e,0xb3,0x2, 0x6a,0xb4,0xb, 0x67,0x7e,0x1f,0x2, 0x63,0xbe,0x18, +0x0, 0x0, 0x78,0x2, 0x81,0x27,0x7e,0x14,0xe0,0x0, 0x7e,0x4, 0x0, 0x1, 0xbf,0x10, +0x28,0x2, 0x81,0x27,0xa2,0x3, 0xe4,0x33,0x78,0x2, 0x81,0x27,0x7d,0x13,0x5e,0x14, +0xf, 0xff,0x78,0x14,0x74,0xc, 0x7f,0x1, 0x1e,0x14,0x1e,0x4, 0x50,0x3, 0x4e,0x20, +0x80,0x14,0x78,0xf4,0x7c,0xb3,0x80,0x12,0x74,0xc, 0x1e,0x34,0x1e,0x24,0x50,0x3, +0x4e,0x60,0x80,0x14,0x78,0xf4,0x7f,0x21,0xb, 0x2c,0xf5,0xb, 0x75,0xa, 0x0, 0x80, +0x7, 0xe5,0xa, 0x12,0x11,0xe4,0x5, 0xa, 0xe5,0xb, 0xbe,0xb1,0xa, 0x38,0xf2,0x81, +0x1d,0x20,0x8, 0x2, 0x81,0x1d,0x7e,0x73,0x2, 0x6a,0xa, 0x57,0x9e,0x54,0x0, 0x80, +0xf5,0x8, 0x6d,0x66,0x80,0xd, 0xe4,0x7e,0xa1,0x8, 0xa, 0x3a,0x2d,0x36,0x12,0x13, +0x48,0xb, 0x64,0x7e,0x73,0x2, 0x46,0xa, 0x37,0xbd,0x36,0x18,0xe9,0x81,0x1d,0x75, +0x22,0x1, 0x6d,0x33,0x7a,0x37,0x2, 0x61,0x6d,0x22,0x7a,0x27,0x2, 0x67,0x30,0x8, +0x2, 0x81,0x1d,0x7d,0x63,0xbe,0x64,0x0, 0x1e,0x48,0x6, 0xbe,0x64,0x0, 0x1f,0x48, +0x5, 0x7c,0xbd,0x12,0x11,0xe4,0xb, 0x64,0xbe,0x64,0x0, 0x20,0x78,0xe7,0x81,0x1d, +0x6d,0x33,0x7a,0x37,0x2, 0x61,0x7a,0x37,0x2, 0x67,0x30,0x8, 0x2, 0x81,0x1d,0x7e, +0x15,0x23,0x49,0x31,0x0, 0x2, 0xb, 0x18,0x20,0x7d,0x3, 0x5e,0x4, 0xf, 0xff,0x68, +0x2, 0x81,0x27,0x49,0x31,0x0, 0x6, 0x49,0x21,0x0, 0x4, 0x4d,0x23,0x78,0x2, 0x81, +0x27,0x49,0x31,0x0, 0x2, 0xb, 0x18,0x20,0x74,0xc, 0x1e,0x34,0x1e,0x24,0x50,0x3, +0x4e,0x60,0x80,0x14,0x78,0xf4,0x7c,0xb7,0xf5,0x8, 0x49,0x31,0x0, 0x6, 0x49,0x21, +0x0, 0x4, 0x7d,0x3, 0x5e,0x4, 0xf, 0xff,0x78,0x1a,0x49,0x31,0x0, 0x6, 0x49,0x21, +0x0, 0x4, 0x74,0xc, 0x1e,0x34,0x1e,0x24,0x50,0x3, 0x4e,0x60,0x80,0x14,0x78,0xf4, +0x7c,0xe7,0x80,0x1a,0x49,0x31,0x0, 0x6, 0x49,0x21,0x0, 0x4, 0x74,0xc, 0x1e,0x34, +0x1e,0x24,0x50,0x3, 0x4e,0x60,0x80,0x14,0x78,0xf4,0x7c,0xe7,0xb, 0xe0,0xa, 0x2e, +0xe5,0x8, 0xa, 0x3b,0x2d,0x32,0xbe,0x34,0x0, 0x1f,0x18,0x1b,0x6c,0xff,0x80,0x9, +0xe5,0x8, 0x2c,0xbf,0x12,0x11,0xe4,0xb, 0xf0,0xbc,0xef,0x38,0xf3,0x7e,0x34,0xf0, +0xaa,0x7a,0x37,0x2, 0x67,0x31,0x33,0x20,0x6, 0x2, 0x21,0xa2,0x7e,0x34,0x13,0x88, +0x12,0x6, 0xfe,0x7e,0x34,0x13,0x88,0x12,0x6, 0xfe,0x75,0xe9,0xff,0x21,0xa2,0xff, diff --git a/drivers/input/touchscreen/gcore_mmi/Android.mk b/drivers/input/touchscreen/gcore_mmi/Android.mk new file mode 100644 index 000000000000..baff42925c01 --- /dev/null +++ b/drivers/input/touchscreen/gcore_mmi/Android.mk @@ -0,0 +1,21 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifneq ($(BOARD_USES_DOUBLE_TAP),) + KERNEL_CFLAGS += CONFIG_INPUT_GCORE_MMI_ENABLE_DOUBLE_TAP=y +endif + +ifneq ($(MOTO_PANEL_CHECK_TOUCH_STATE),) + KERNEL_CFLAGS += CONFIG_INPUT_GCORE_MMI_NOTIFY_TOUCH_STATE=y +endif + +include $(CLEAR_VARS) +ifneq ($(BOARD_USES_DOUBLE_TAP),) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/sensors_class.ko +endif +LOCAL_MODULE := gcore_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +KBUILD_OPTIONS_GKI += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/gcore_mmi/Kbuild b/drivers/input/touchscreen/gcore_mmi/Kbuild new file mode 100644 index 000000000000..b32a0894f218 --- /dev/null +++ b/drivers/input/touchscreen/gcore_mmi/Kbuild @@ -0,0 +1,23 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/gcore_mmi + +ifeq ($(filter m y, $(CONFIG_TOUCHSCREEN_GCORE_CHIPSET)),) + obj-m += gcore_mmi.o +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_GCORE_MMI_ENABLE_DOUBLE_TAP)),) + EXTRA_CFLAGS += -DGCORE_SENSOR_EN + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_GCORE_MMI_NOTIFY_TOUCH_STATE)),) + EXTRA_CFLAGS += -DGCORE_SET_TOUCH_STATE +endif + +gcore_mmi-objs += gcore_drv_main.o +gcore_mmi-objs += gcore_drv_qcom.o +gcore_mmi-objs += gcore_drv_fw_update.o +gcore_mmi-objs += gcore_drv_interface.o +gcore_mmi-objs += gcore_drv_mp_test.o diff --git a/drivers/input/touchscreen/gcore_mmi/Makefile b/drivers/input/touchscreen/gcore_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/gcore_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/gcore_mmi/gcore_drv_common.h b/drivers/input/touchscreen/gcore_mmi/gcore_drv_common.h new file mode 100755 index 000000000000..938534ec46a3 --- /dev/null +++ b/drivers/input/touchscreen/gcore_mmi/gcore_drv_common.h @@ -0,0 +1,489 @@ +/* + * GalaxyCore touchscreen driver + * + * Copyright (C) 2021 GalaxyCore Incorporated + * + * Copyright (C) 2021 Neo Chen + * + * 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 GCORE_TPD_COMMON_H_ +#define GCORE_TPD_COMMON_H_ + +/*----------------------------------------------------------*/ +/* INCLUDE FILE */ +/*----------------------------------------------------------*/ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +//#define GCORE_SENSOR_EN 1 +#define CHARGER_NOTIFIER 0 + +/* display state */ +#ifdef GCORE_SENSOR_EN +#include + +enum display_state { + SCREEN_UNKNOWN, + SCREEN_OFF, + SCREEN_ON, +}; +struct gcore_sensor_platform_data { + struct input_dev *input_sensor_dev; + struct sensors_classdev ps_cdev; + int sensor_opened; + char sensor_data; /* 0 near, 1 far */ + struct gcore_dev *data; +}; +#endif + +/*----------------------------------------------------------*/ +/* GALAXYCORE TOUCH DEVICE DRIVER RELEASE VERSION */ +/*----------------------------------------------------------*/ +#define TOUCH_DRIVER_RELEASE_VERISON ("1.0.0.0") + +/*----------------------------------------------------------*/ +/* COMPILE OPTION DEFINITION */ +/*----------------------------------------------------------*/ + +/* + * Note. + * The below compile option is used to enable the specific platform this driver running on. + */ +//#define CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM +//#define CONFIG_TOUCH_DRIVER_RUN_ON_RK_PLATFORM +//#define CONFIG_TOUCH_DRIVER_RUN_ON_SPRD_PLATFORM +#define CONFIG_TOUCH_DRIVER_RUN_ON_QCOM_PLATFORM + +/* + * Note. + * The below compile option is used to enable the chip type used + */ +//#define CONFIG_ENABLE_CHIP_TYPE_GC7371 +//#define CONFIG_ENABLE_CHIP_TYPE_GC7271 +//#define CONFIG_ENABLE_CHIP_TYPE_GC7202 +#define CONFIG_ENABLE_CHIP_TYPE_GC7372 + + +/* + * Note. + * The below compile option is used to enable the IC interface used + */ +//#define CONFIG_TOUCH_DRIVER_INTERFACE_I2C +#define CONFIG_TOUCH_DRIVER_INTERFACE_SPI + + +/* + * Note. + * The below compile option is used to enable the fw download type + * Hostdownload for 0 flash,flashdownload for 1 flash + * FW download function need another fw_update module, if needed, contact to our colleague + */ +#define CONFIG_GCORE_AUTO_UPDATE_FW_HOSTDOWNLOAD +//#define CONFIG_GCORE_AUTO_UPDATE_FW_FLASHDOWNLOAD + + +/* + * Note. + * The below compile option is used to enable function transfer fw raw data to App or Tool + * By default,this compile option is disable + * Connect to App or Tool function need another interface module, if needed, contact to our colleague + */ +#define CONFIG_ENABLE_FW_RAWDATA + + +/* + * Note. + * The below compile option is used to enable enable/disable multi-touch procotol for reporting touch point to input sub-system + * If this compile option is defined, Type B procotol is enabled + * Else, Type A procotol is enabled + * By default, this compile option is enable + */ +#define CONFIG_ENABLE_TYPE_B_PROCOTOL + + +/* + * Note. + * The below compile option is used to enable/disable log output for touch driver + * If is set as 1, the log output is enabled + * By default, the debug log is set as 0 + */ +#define CONFIG_ENABLE_DEBUG_LOG (1) + + +/* + * Note. + * The below compile option is used to enable GESTURE WAKEUP function + */ +#define CONFIG_ENABLE_GESTURE_WAKEUP + + +/* + * Note. + * The below compile option is used for GESTURE WAKEUP special int, + * Only for sprd platform + */ +//#define CONFIG_GESTURE_SPECIAL_INT + + +/* + * Note. + * The below compile option is used to enable update firmware by bin file + * Else, the firmware is from array in driver + */ +#define CONFIG_UPDATE_FIRMWARE_BY_BIN_FILE + + +/* + * Note. + * This compile option is used for MTK platform only + * This compile option is used for MTK legacy platform different + */ +#define CONFIG_MTK_LEGACY_PLATFORM + + +#define CONFIG_GCORE_HOSTDOWNLOAD_ESD_PROTECT + + +/* + * Note. + * Misc Debug Option + */ +//#define CONFIG_DEBUG_SPI_SPEED + + + +#ifdef CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM +#include "tpd.h" +#ifdef CONFIG_MTK_LEGACY_PLATFORM +#include "mtk_spi.h" +#endif +#endif + + +/* + * Log define + */ +#define GTP_ERROR(fmt, arg...) pr_err("[%s:%d] "fmt"\n", __func__, __LINE__, ##arg) +#define GTP_DEBUG(fmt, arg...) \ + do { \ + if (CONFIG_ENABLE_DEBUG_LOG) \ + pr_err("[%s:%d]"fmt"\n", __func__, __LINE__, ##arg);\ + } while (0) + + +#define GTP_DRIVER_NAME "gcore" +#define GTP_MAX_TOUCH 10 +#define DEMO_DATA_SIZE (6 * GTP_MAX_TOUCH + 2 + 2) + +static const struct of_device_id tpd_of_match[] = { + {.compatible = "gcore,touchscreen"}, + {}, +}; + + +/* + * LCD Resolution + */ + +//#define TOUCH_SCREEN_X_MAX (720) //(1080) +//#define TOUCH_SCREEN_Y_MAX (1560) //(2340) +#define TOUCH_SCREEN_X_MAX (1080) +#define TOUCH_SCREEN_Y_MAX (2460) + + +/* + * Raw Data + */ + +#if defined(CONFIG_ENABLE_CHIP_TYPE_GC7371) +#define RAW_DATA_SIZE (1296) +#define RAWDATA_ROW (36) +#define RAWDATA_COLUMN (18) +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7271) +#define RAW_DATA_SIZE (1080) +#define RAWDATA_ROW (36) +#define RAWDATA_COLUMN (15) +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7202) +#define RAW_DATA_SIZE (1152) +#define RAWDATA_ROW (32) +#define RAWDATA_COLUMN (18) +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7372) +#define RAW_DATA_SIZE (1216) +#define RAWDATA_ROW (38) +#define RAWDATA_COLUMN (16) +#endif + +#define DEMO_RAWDATA_SIZE (DEMO_DATA_SIZE + RAW_DATA_SIZE) +#define FW_SIZE (64 * 1024) + +#define ERR_ALLOC_MEM(X) ((IS_ERR(X) || X == NULL) ? 1 : 0) + +#define BIT0 (1<<0) // 0x01 +#define BII1 (1<<1) // 0x02 +#define BIT2 (1<<2) // 0x04 +#define BIT3 (1<<3) // 0x08 +#define BIT4 (1<<4) // 0x10 +#define BIT5 (1<<5) // 0x20 +#define BIT6 (1<<6) // 0x40 +#define BIT7 (1<<7) // 0x80 + +/* + * GESTURE WAKEUP + */ +#ifdef CONFIG_ENABLE_GESTURE_WAKEUP +#define GESTURE_NULL 0 +#define GESTURE_DOUBLE_CLICK 1 +#define GESTURE_UP 2 +#define GESTURE_DOWN 3 +#define GESTURE_LEFT 4 +#define GESTURE_RIGHT 5 +#define GESTURE_C 6 +#define GESTURE_E 7 +#define GESTURE_M 8 +#define GESTURE_N 9 +#define GESTURE_O 10 +#define GESTURE_S 11 +#define GESTURE_V 12 +#define GESTURE_W 13 +#define GESTURE_Z 14 + +#define GESTURE_MAX 15 + +#define GESTURE_KEY KEY_POWER +#endif + +#ifdef CONFIG_ENABLE_FW_RAWDATA +enum FW_MODE { + DEMO, + RAWDATA, + DEMO_RAWDATA +}; +#endif + +enum fw_event_type { + FW_UPDATE = 0, + FW_READ_REG, + FW_WRITE_REG, + FW_READ_OPEN, + FW_READ_SHORT, + FW_EDGE_0, + FW_EDGE_90, + FW_CHARGER_PLUG, + FW_CHARGER_UNPLUG, + FW_HEADSET_PLUG, + FW_HEADSET_UNPLUG, + FW_READ_RAWDATA, + FW_READ_DIFFDATA, + FW_GESTURE_ENABLE, + FW_GESTURE_DISABLE, +}; + +enum GCORE_TS_STAT { + TS_NORMAL = 0, + TS_SUSPEND, +}; + +struct gcore_dev { + struct input_dev *input_device; + struct task_struct *thread; + u8 *touch_data; + +#if defined(CONFIG_TOUCH_DRIVER_INTERFACE_I2C) + struct i2c_client *bus_device; +#elif defined(CONFIG_TOUCH_DRIVER_INTERFACE_SPI) + struct spi_device *bus_device; +#endif + + int touch_irq; + spinlock_t irq_flag_lock; + int irq_flag; + int tpd_flag; + + struct mutex transfer_lock; + wait_queue_head_t wait; + + int irq_gpio; + int rst_gpio; + void (*rst_output)(int rst, int level); + void (*irq_enable)(struct gcore_dev *gdev); + void (*irq_disable)(struct gcore_dev *gdev); + +#ifdef CONFIG_ENABLE_FW_RAWDATA + enum FW_MODE fw_mode; + wait_queue_head_t usr_wait; + bool usr_read; + bool data_ready; +#endif + + /* for driver request event and fw reply with interrupt */ + enum fw_event_type fw_event; + u8 *firmware; + int fw_xfer; + + struct workqueue_struct *fwu_workqueue; + struct delayed_work fwu_work; + + bool tp_suspend; + +#ifdef GCORE_SENSOR_EN + bool wakeable; + bool should_enable_gesture; + bool gesture_enabled; + uint32_t report_gesture_key; + enum display_state screen_state; + struct mutex state_mutex; + struct gcore_sensor_platform_data *sensor_pdata; +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock gesture_wakelock; +#else + struct wakeup_source *gesture_wakelock; +#endif +#endif + +#ifdef CONFIG_ENABLE_GESTURE_WAKEUP + bool gesture_wakeup_en; +#endif +#ifdef CONFIG_GESTURE_SPECIAL_INT + int ges_irq_gpio; + unsigned int ges_irq; + bool ges_irq_en; +#endif + +#ifdef CONFIG_DRM + struct notifier_block drm_notifier; +#endif + +}; + +enum exp_fn { + GCORE_FW_UPDATE = 0, + GCORE_FS_INTERFACE, + GCORE_MP_TEST, +}; + +struct gcore_exp_fn { + enum exp_fn fn_type; + bool wait_int; + bool event_flag; + int (*init)(struct gcore_dev *); + void (*remove)(struct gcore_dev *); + struct list_head link; +}; + + +struct gcore_exp_fn_data { + bool initialized; + bool fn_inited; + struct list_head list; + struct gcore_dev *gdev; +}; + +#ifdef GCORE_SET_TOUCH_STATE +#define MAX_PANEL_IDX 2 +enum touch_panel_id { + TOUCH_PANEL_IDX_PRIMARY = 0, + TOUCH_PANEL_MAX_IDX, +}; + +enum touch_state { + TOUCH_DEEP_SLEEP_STATE = 0, + TOUCH_LOW_POWER_STATE, +}; +#endif + +/* + * Declaration + */ + +#ifdef GCORE_SET_TOUCH_STATE +int touch_set_state(int state, int panel_idx); +#endif + +extern s32 gcore_bus_read(u8 *buffer, s32 len); +extern s32 gcore_bus_write(const u8 *buffer, s32 len); +extern int gcore_touch_bus_init(void); +extern void gcore_touch_bus_exit(void); +extern void gcore_new_function_register(struct gcore_exp_fn *exp_fn); +extern void gcore_new_function_unregister(struct gcore_exp_fn *exp_fn); +extern s32 gcore_spi_write_then_read(u8 *txbuf, s32 n_tx, u8 *rxbuf, s32 n_rx); + +extern int gcore_read_fw_version(u8 *version, int length); +extern int gcore_auto_update_hostdownload_proc(void *fw_buf); +extern int gcore_auto_update_flashdownload_proc(void *fw_buf); +extern int gcore_fw_mode_set_proc2(u8 mode); +extern s32 gcore_fw_read_reg(u32 addr, u8 *buffer, s32 len); +extern s32 gcore_fw_write_reg(u32 addr, u8 *buffer, s32 len); +extern int gcore_create_attribute(struct device *dev); +extern int gcore_idm_read_id(u8 *id, int length); + +extern int gcore_enter_idm_mode(void); +extern int gcore_exit_idm_mode(void); +extern s32 gcore_idm_read_reg(u32 addr, u8 *buffer, s32 len); +extern s32 gcore_idm_write_reg(u32 addr, u8 *buffer, s32 len); +extern void gcore_irq_disable(struct gcore_dev *gdev); +extern void gcore_irq_enable(struct gcore_dev *gdev); + +extern int gcore_fw_event_notify(enum fw_event_type event); + +extern struct gcore_exp_fn fw_update_fn; +extern struct gcore_exp_fn fs_interf_fn; +extern struct gcore_exp_fn mp_test_fn; +extern struct gcore_exp_fn_data fn_data; + + +extern unsigned char gcore_mp_FW[]; +extern void gcore_request_firmware_update_work(struct work_struct *work); +extern void gcore_touch_release_all_point(struct input_dev *dev); + + +#ifdef CONFIG_TOUCH_DRIVER_RUN_ON_SPRD_PLATFORM +extern int gcore_sysfs_add_device(struct device *dev); +#endif + +#ifdef CONFIG_DRM +extern int gcore_ts_drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); +#endif + +extern int gcore_start_mp_test(void); + +#define RESUME_USES_WORKQ 1 +#if RESUME_USES_WORKQ +extern void gcore_resume_wq_init(void); +#endif + +#if CHARGER_NOTIFIER +struct usb_charger_detection { + struct notifier_block charger_notif; + u8 usb_connected; + struct workqueue_struct *gcore_charger_notify_wq; + struct work_struct charger_notify_work; +}; + +extern int gcore_charger_notifier_init(void); +#endif + +extern int gcore_idm_tddi_reset(void); + +#endif /* GCORE_TPD_COMMON_H_ */ + diff --git a/drivers/input/touchscreen/gcore_mmi/gcore_drv_firmware.h b/drivers/input/touchscreen/gcore_mmi/gcore_drv_firmware.h new file mode 100644 index 000000000000..a24d9d8a2c29 --- /dev/null +++ b/drivers/input/touchscreen/gcore_mmi/gcore_drv_firmware.h @@ -0,0 +1,4121 @@ +/* + * GalaxyCore touchscreen driver + * + * Copyright (C) 2021 GalaxyCore Incorporated + * + * Copyright (C) 2021 Neo Chen + * + * 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 _GCORE_FIRMWARE_H_ +#define _GCORE_FIRMWARE_H_ + +unsigned char gcore_mp_FW[] = { + 0x34, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, + 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, + 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, + 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, + 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, + 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, + 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, + 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, + 0xB4, 0x26, 0x00, 0x00, 0x34, 0x27, 0x00, 0x00, 0x80, 0x27, 0x00, 0x00, 0xCC, 0x27, 0x00, 0x00, + 0x1C, 0x28, 0x00, 0x00, 0xB0, 0x2A, 0x00, 0x00, 0xA4, 0x2B, 0x00, 0x00, 0xF0, 0x2B, 0x00, 0x00, + 0x3C, 0x2C, 0x00, 0x00, 0xE4, 0x2C, 0x00, 0x00, 0x24, 0x2D, 0x00, 0x00, 0x84, 0x2D, 0x00, 0x00, + 0xD0, 0x2D, 0x00, 0x00, 0x18, 0x2E, 0x00, 0x00, 0x60, 0x2E, 0x00, 0x00, 0xC0, 0x2E, 0x00, 0x00, + 0x0C, 0x2F, 0x00, 0x00, 0x54, 0x2F, 0x00, 0x00, 0x88, 0x2F, 0x00, 0x00, 0xE4, 0x2F, 0x00, 0x00, + 0x44, 0x30, 0x00, 0x00, 0x90, 0x30, 0x00, 0x00, 0xF0, 0x30, 0x00, 0x00, 0x38, 0x31, 0x00, 0x00, + 0x80, 0x31, 0x00, 0x00, 0xD8, 0x31, 0x00, 0x00, 0x28, 0x32, 0x00, 0x00, 0x70, 0x32, 0x00, 0x00, + 0xC8, 0x32, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, + 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, 0xE6, 0x0F, 0x00, 0x00, + 0x41, 0x38, 0x16, 0x0C, 0x42, 0x38, 0x13, 0x08, 0x41, 0x32, 0x2B, 0x10, 0x6B, 0x10, 0x01, 0xE4, + 0x7F, 0x04, 0x8C, 0x60, 0x61, 0xD8, 0x00, 0x50, 0x01, 0x21, 0x06, 0x64, 0x60, 0xA2, 0x68, 0x53, + 0x61, 0xA2, 0x01, 0x22, 0xF8, 0x0B, 0x66, 0x10, 0x00, 0x32, 0x40, 0xAB, 0x3C, 0x78, 0x00, 0x32, + 0xED, 0x07, 0x00, 0x00, 0xBC, 0x13, 0x00, 0x30, 0x3C, 0x18, 0x00, 0x30, 0x7C, 0x52, 0x00, 0x30, + 0x62, 0x10, 0x00, 0xB3, 0x3C, 0x78, 0x00, 0x00, 0x04, 0x1D, 0x00, 0x30, 0xC4, 0x14, 0x22, 0x14, + 0x66, 0x13, 0xEE, 0x5B, 0x61, 0xB8, 0x00, 0x32, 0x65, 0x13, 0x26, 0x13, 0xC6, 0x13, 0xA7, 0x13, + 0x60, 0xB8, 0x00, 0x33, 0x00, 0x34, 0x60, 0xA9, 0x61, 0x42, 0x0C, 0x5F, 0x80, 0xA8, 0x0C, 0x5E, + 0x80, 0xA8, 0x0C, 0x5D, 0x80, 0xA8, 0x00, 0x98, 0xC0, 0x60, 0x00, 0x30, 0x00, 0x22, 0x00, 0xAB, + 0x03, 0xEA, 0x40, 0x02, 0x01, 0x21, 0xCA, 0x64, 0xED, 0x0B, 0x61, 0x98, 0x00, 0x32, 0x43, 0xDC, + 0x84, 0x04, 0x01, 0x32, 0x7A, 0x12, 0x40, 0xB3, 0x00, 0x32, 0x7A, 0x12, 0x40, 0xA3, 0x00, 0x32, + 0x79, 0x12, 0x40, 0xB3, 0x79, 0x12, 0x40, 0xAB, 0x79, 0x12, 0x40, 0xA3, 0x08, 0x32, 0x79, 0x12, + 0x40, 0xA3, 0x00, 0x32, 0x78, 0x12, 0x40, 0xB3, 0x02, 0x14, 0x84, 0x14, 0x3C, 0x78, 0x00, 0x00, + 0xD4, 0x14, 0x22, 0x14, 0x42, 0x38, 0x3A, 0x0C, 0x02, 0x38, 0x28, 0x0C, 0x44, 0x38, 0x0A, 0x0C, + 0x45, 0x38, 0x05, 0x08, 0x6E, 0x12, 0x60, 0x83, 0x41, 0x3B, 0x37, 0x0C, 0x00, 0x30, 0x02, 0x14, + 0x94, 0x14, 0x6A, 0x12, 0x40, 0x8B, 0x6D, 0x12, 0x89, 0x74, 0x60, 0x8B, 0xCD, 0x74, 0xCA, 0x64, + 0xF6, 0x0B, 0x20, 0x12, 0x00, 0x32, 0xA0, 0x12, 0x80, 0x12, 0x00, 0xEA, 0x40, 0x02, 0x61, 0x42, + 0x00, 0x22, 0xCC, 0x5D, 0x0A, 0x64, 0xD0, 0x60, 0xC0, 0x8E, 0x60, 0x8B, 0x6D, 0x5E, 0x60, 0xA9, + 0x01, 0x21, 0xF6, 0x0B, 0x01, 0x30, 0x02, 0x14, 0x94, 0x14, 0x41, 0x38, 0xE0, 0x0B, 0x73, 0x11, + 0x63, 0xD8, 0x84, 0x04, 0x43, 0x3B, 0xDB, 0x0B, 0x02, 0xEA, 0x80, 0x04, 0x3C, 0x11, 0x11, 0x11, + 0x00, 0xE0, 0x10, 0x06, 0x01, 0x30, 0x02, 0x14, 0x94, 0x14, 0x6C, 0x11, 0x43, 0xD8, 0x84, 0x04, + 0x24, 0x33, 0xCA, 0x64, 0xCC, 0x0B, 0xF1, 0x07, 0x00, 0x33, 0xEA, 0x11, 0xD5, 0x11, 0x24, 0x35, + 0x60, 0xB8, 0x61, 0xB8, 0x60, 0x98, 0x8F, 0x6C, 0x83, 0xE4, 0x3F, 0x02, 0x61, 0x98, 0x01, 0x42, + 0x94, 0x60, 0x18, 0x60, 0x0A, 0x65, 0x22, 0x5B, 0x00, 0x88, 0x61, 0x43, 0xDC, 0x60, 0x00, 0xAB, + 0xC5, 0x74, 0xF6, 0x0B, 0x40, 0x98, 0x00, 0x22, 0x61, 0x98, 0x40, 0xB8, 0x0F, 0x23, 0x02, 0xEA, + 0x40, 0x02, 0xCD, 0x74, 0x61, 0xB8, 0x8E, 0x64, 0xE6, 0x0B, 0x02, 0xEA, 0x80, 0x04, 0x25, 0x11, + 0x18, 0x10, 0x00, 0xE0, 0xDF, 0x05, 0xBF, 0x07, 0xD4, 0x14, 0x40, 0x38, 0x04, 0x0C, 0x41, 0x38, + 0x60, 0x0C, 0x94, 0x14, 0x00, 0xE0, 0x86, 0x07, 0x01, 0x30, 0x9F, 0x10, 0x00, 0xE0, 0x6E, 0x07, + 0x00, 0x33, 0x55, 0x2B, 0x74, 0xA4, 0x73, 0xA4, 0x72, 0xA4, 0x71, 0xA4, 0x0A, 0x33, 0xBB, 0x10, + 0xDB, 0x10, 0xFC, 0x10, 0x70, 0xA4, 0x00, 0x33, 0x76, 0x2B, 0x76, 0xA6, 0x7A, 0xA6, 0x7E, 0xA6, + 0x72, 0xA5, 0x76, 0xA5, 0x7A, 0xA5, 0x7E, 0xA5, 0x72, 0xA7, 0x00, 0xE0, 0x65, 0x07, 0x01, 0x30, + 0x00, 0xE0, 0x68, 0x07, 0x01, 0x30, 0x27, 0x04, 0x04, 0x1D, 0x00, 0x30, 0x44, 0x30, 0x00, 0x30, + 0xBC, 0x13, 0x00, 0x30, 0xC8, 0x34, 0x00, 0x30, 0x48, 0x39, 0x00, 0x30, 0x00, 0x00, 0x00, 0x30, + 0x40, 0x30, 0x00, 0x30, 0x64, 0x4A, 0x00, 0x30, 0xC4, 0x34, 0x00, 0x30, 0x9C, 0x21, 0x00, 0x30, + 0x04, 0x00, 0x00, 0x30, 0x98, 0x21, 0x00, 0x30, 0x74, 0x52, 0x00, 0x30, 0x08, 0x1D, 0x00, 0x30, + 0xA0, 0x21, 0x00, 0x30, 0xA0, 0x0E, 0x00, 0x50, 0xA0, 0x41, 0x00, 0x50, 0xA0, 0x40, 0x00, 0x50, + 0xA0, 0x42, 0x00, 0x50, 0x00, 0xE0, 0x2A, 0x07, 0x55, 0x33, 0x74, 0xA4, 0x73, 0xA4, 0x72, 0xA4, + 0x71, 0xA4, 0x05, 0x33, 0x70, 0xA4, 0x00, 0x33, 0x7F, 0x2B, 0x75, 0xA6, 0x79, 0xA6, 0x7D, 0xA6, + 0x71, 0xA5, 0x75, 0xA5, 0x79, 0xA5, 0x7D, 0xA5, 0x71, 0xA7, 0x00, 0xE0, 0x25, 0x07, 0x94, 0x14, + 0x00, 0x30, 0xC3, 0x13, 0x00, 0xE0, 0x26, 0x07, 0x01, 0x30, 0x82, 0x13, 0x00, 0xE0, 0x0E, 0x07, + 0x55, 0x33, 0xA1, 0x13, 0x74, 0xA6, 0x73, 0xA6, 0x72, 0xA6, 0x71, 0xA6, 0x05, 0x33, 0xFF, 0x12, + 0x70, 0xA6, 0x00, 0x33, 0x77, 0x2B, 0x76, 0xA5, 0x7A, 0xA5, 0x7E, 0xA5, 0x72, 0xA4, 0x76, 0xA4, + 0x7A, 0xA4, 0x7E, 0xA4, 0x72, 0xA7, 0x00, 0x33, 0x7F, 0x2B, 0x75, 0xA5, 0x79, 0xA5, 0x7D, 0xA5, + 0x71, 0xA4, 0x75, 0xA4, 0x79, 0xA4, 0x7D, 0xA4, 0x71, 0xA7, 0x00, 0xE0, 0xFD, 0x06, 0x01, 0x30, + 0x00, 0xE0, 0x00, 0x07, 0x01, 0x30, 0x00, 0xE0, 0xE9, 0x06, 0x00, 0x33, 0x55, 0x2B, 0x74, 0xA6, + 0x73, 0xA6, 0x72, 0xA6, 0x71, 0xA6, 0x0A, 0x33, 0x70, 0xA6, 0x00, 0x33, 0x76, 0x2B, 0x76, 0xA5, + 0x7A, 0xA5, 0x7E, 0xA5, 0x72, 0xA4, 0x76, 0xA4, 0x7A, 0xA4, 0x7E, 0xA4, 0x72, 0xA7, 0x00, 0x33, + 0x75, 0xA5, 0x79, 0xA5, 0x7D, 0xA5, 0x71, 0xA4, 0x75, 0xA4, 0x79, 0xA4, 0x7D, 0xA4, 0x71, 0xA7, + 0x00, 0xE0, 0xDA, 0x06, 0x94, 0x14, 0x00, 0x00, 0xD3, 0x14, 0x24, 0x33, 0xC0, 0x64, 0x2B, 0x08, + 0x7F, 0x11, 0x02, 0x40, 0x0C, 0x60, 0x60, 0x90, 0x00, 0x30, 0x0C, 0x78, 0x00, 0xE0, 0xD2, 0x06, + 0x01, 0x30, 0x97, 0x11, 0x00, 0xE0, 0xBA, 0x06, 0x3F, 0x32, 0x7A, 0x11, 0x00, 0x35, 0x5B, 0xA3, + 0x7F, 0x33, 0x00, 0x2D, 0x71, 0xA4, 0x0F, 0x33, 0xB4, 0xA4, 0x70, 0xA4, 0xB3, 0xA4, 0xB2, 0xA4, + 0x00, 0xE0, 0xBA, 0x06, 0x01, 0x30, 0x00, 0xE0, 0xBD, 0x06, 0x01, 0x30, 0x00, 0xE0, 0xA6, 0x06, + 0x00, 0x33, 0x10, 0x2B, 0x74, 0xA4, 0xB3, 0xA4, 0x0F, 0x33, 0xB2, 0xA4, 0xB1, 0xA4, 0x70, 0xA4, + 0x00, 0xE0, 0xAA, 0x06, 0x93, 0x14, 0x00, 0xE0, 0xAD, 0x06, 0x01, 0x30, 0xA4, 0x11, 0x00, 0xE0, + 0x95, 0x06, 0x3F, 0x32, 0x67, 0x11, 0x00, 0x34, 0x5B, 0xA3, 0x0B, 0x33, 0x00, 0x2C, 0x70, 0xA5, + 0x94, 0xA5, 0x93, 0xA5, 0x92, 0xA5, 0x91, 0xA5, 0x00, 0xE0, 0x96, 0x06, 0x01, 0x30, 0x00, 0xE0, + 0x99, 0x06, 0x01, 0x30, 0x00, 0xE0, 0x82, 0x06, 0x00, 0x33, 0x02, 0x2B, 0x74, 0xA5, 0x0F, 0x33, + 0x93, 0xA5, 0x92, 0xA5, 0x91, 0xA5, 0x70, 0xA5, 0x00, 0xE0, 0x86, 0x06, 0x93, 0x14, 0x00, 0xE0, + 0x89, 0x06, 0x01, 0x30, 0xB2, 0x10, 0x00, 0xE0, 0x71, 0x06, 0x3F, 0x32, 0x75, 0x10, 0x00, 0x34, + 0x5B, 0xA3, 0x0D, 0x33, 0x00, 0x2C, 0x70, 0xA5, 0x94, 0xA5, 0x93, 0xA5, 0x92, 0xA5, 0x91, 0xA5, + 0x00, 0xE0, 0x72, 0x06, 0x01, 0x30, 0x00, 0xE0, 0x75, 0x06, 0x01, 0x30, 0x00, 0xE0, 0x5E, 0x06, + 0x00, 0x33, 0x04, 0x2B, 0xDC, 0x07, 0x00, 0xE0, 0x6D, 0x06, 0x01, 0x30, 0xA4, 0x10, 0x00, 0xE0, + 0x55, 0x06, 0x3F, 0x32, 0x67, 0x10, 0x00, 0x34, 0x0E, 0x04, 0x00, 0x00, 0xA0, 0x0E, 0x00, 0x50, + 0xA0, 0x41, 0x00, 0x50, 0xA0, 0x40, 0x00, 0x50, 0xA0, 0x42, 0x00, 0x50, 0x50, 0x34, 0x00, 0x00, + 0xA0, 0x72, 0x00, 0x50, 0x5B, 0xA3, 0x0E, 0x33, 0x00, 0x2C, 0x70, 0xA5, 0x94, 0xA5, 0x93, 0xA5, + 0x92, 0xA5, 0x91, 0xA5, 0x00, 0xE0, 0x48, 0x06, 0x01, 0x30, 0x00, 0xE0, 0x4B, 0x06, 0x01, 0x30, + 0x00, 0xE0, 0x34, 0x06, 0x00, 0x33, 0x08, 0x2B, 0xB2, 0x07, 0x00, 0xE0, 0x43, 0x06, 0x01, 0x30, + 0x85, 0x13, 0x00, 0xE0, 0x2B, 0x06, 0x3F, 0x32, 0x64, 0x13, 0x00, 0x35, 0x5B, 0xA3, 0x00, 0x33, + 0x40, 0x2B, 0x71, 0xA4, 0x0F, 0x33, 0x00, 0x2D, 0x70, 0xA4, 0xB4, 0xA4, 0xB3, 0xA4, 0xB2, 0xA4, + 0x00, 0xE0, 0x2A, 0x06, 0x01, 0x30, 0x00, 0xE0, 0x2D, 0x06, 0x01, 0x30, 0x00, 0xE0, 0x16, 0x06, + 0x00, 0x33, 0x20, 0x2B, 0x70, 0x07, 0x00, 0xE0, 0x25, 0x06, 0x01, 0x30, 0x96, 0x12, 0x00, 0xE0, + 0x0D, 0x06, 0x3F, 0x32, 0x75, 0x12, 0x00, 0x35, 0x5B, 0xA3, 0x00, 0x33, 0x20, 0x2B, 0x71, 0xA4, + 0x0F, 0x33, 0x00, 0x2D, 0x70, 0xA4, 0xB4, 0xA4, 0xB3, 0xA4, 0xB2, 0xA4, 0x00, 0xE0, 0x0C, 0x06, + 0x01, 0x30, 0x00, 0xE0, 0x0F, 0x06, 0x01, 0x30, 0x00, 0xE0, 0xF8, 0x05, 0x00, 0x33, 0x40, 0x2B, + 0x52, 0x07, 0x00, 0xE0, 0x07, 0x06, 0x01, 0x30, 0x87, 0x12, 0x00, 0xE0, 0xEF, 0x05, 0x3F, 0x32, + 0x66, 0x12, 0x00, 0x35, 0x5B, 0xA3, 0x00, 0x33, 0x10, 0x2B, 0x71, 0xA4, 0x0F, 0x33, 0x00, 0x2D, + 0x70, 0xA4, 0xB4, 0xA4, 0xB3, 0xA4, 0xB2, 0xA4, 0x00, 0xE0, 0xEE, 0x05, 0x01, 0x30, 0x00, 0xE0, + 0xF1, 0x05, 0x01, 0x30, 0x00, 0xE0, 0xDA, 0x05, 0x7F, 0x33, 0x35, 0x07, 0x00, 0xE0, 0xEA, 0x05, + 0x01, 0x30, 0x99, 0x11, 0x00, 0xE0, 0xD2, 0x05, 0x3F, 0x32, 0x78, 0x11, 0x00, 0x35, 0x5B, 0xA3, + 0x00, 0x33, 0x08, 0x2B, 0x71, 0xA4, 0x0F, 0x33, 0x00, 0x2D, 0x70, 0xA4, 0xB4, 0xA4, 0xB3, 0xA4, + 0xB2, 0xA4, 0x00, 0xE0, 0xD1, 0x05, 0x01, 0x30, 0x00, 0xE0, 0xD4, 0x05, 0x01, 0x30, 0x00, 0xE0, + 0xBD, 0x05, 0x00, 0x33, 0x01, 0x2B, 0xB4, 0xA4, 0x73, 0xA4, 0x17, 0x07, 0x00, 0xE0, 0xCA, 0x05, + 0x01, 0x30, 0x89, 0x11, 0x00, 0xE0, 0xB2, 0x05, 0x3F, 0x32, 0x68, 0x11, 0x00, 0x35, 0x5B, 0xA3, + 0x00, 0x33, 0x04, 0x2B, 0x71, 0xA4, 0x0F, 0x33, 0x00, 0x2D, 0x70, 0xA4, 0xB4, 0xA4, 0xB3, 0xA4, + 0xB2, 0xA4, 0x00, 0xE0, 0xB1, 0x05, 0x01, 0x30, 0x00, 0xE0, 0xB4, 0x05, 0x01, 0x30, 0x00, 0xE0, + 0x9D, 0x05, 0x00, 0x33, 0x02, 0x2B, 0xB4, 0xA4, 0x73, 0xA4, 0xF7, 0x06, 0x00, 0xE0, 0xAA, 0x05, + 0x01, 0x30, 0x99, 0x10, 0x00, 0xE0, 0x92, 0x05, 0x3F, 0x32, 0x78, 0x10, 0x00, 0x35, 0x5B, 0xA3, + 0x00, 0x33, 0x02, 0x2B, 0x71, 0xA4, 0x0F, 0x33, 0x00, 0x2D, 0x70, 0xA4, 0xB4, 0xA4, 0xB3, 0xA4, + 0xB2, 0xA4, 0x00, 0xE0, 0x91, 0x05, 0x01, 0x30, 0x00, 0xE0, 0x94, 0x05, 0x01, 0x30, 0x00, 0xE0, + 0x7D, 0x05, 0x00, 0x33, 0x04, 0x2B, 0xB4, 0xA4, 0x73, 0xA4, 0xD7, 0x06, 0x00, 0xE0, 0x8A, 0x05, + 0x01, 0x30, 0x89, 0x10, 0x00, 0xE0, 0x72, 0x05, 0x3F, 0x32, 0x68, 0x10, 0x00, 0x35, 0x5B, 0xA3, + 0x00, 0x33, 0x01, 0x2B, 0x71, 0xA4, 0x0F, 0x33, 0x00, 0x2D, 0x70, 0xA4, 0xB4, 0xA4, 0xB3, 0xA4, + 0x06, 0x04, 0x00, 0x00, 0xA0, 0x0E, 0x00, 0x50, 0xA0, 0x72, 0x00, 0x50, 0xB2, 0xA4, 0x00, 0xE0, + 0x6B, 0x05, 0x01, 0x30, 0x00, 0xE0, 0x6E, 0x05, 0x01, 0x30, 0x00, 0xE0, 0x57, 0x05, 0x00, 0x33, + 0x08, 0x2B, 0xB4, 0xA4, 0x73, 0xA4, 0xB1, 0x06, 0x00, 0xE0, 0x64, 0x05, 0x01, 0x30, 0x83, 0x13, + 0x00, 0xE0, 0x4C, 0x05, 0x3F, 0x32, 0x62, 0x13, 0x00, 0x35, 0x5B, 0xA3, 0x7F, 0x33, 0x00, 0x2D, + 0x72, 0xA4, 0x0F, 0x33, 0xB4, 0xA4, 0x70, 0xA4, 0xB3, 0xA4, 0xB1, 0xA4, 0x00, 0xE0, 0x4C, 0x05, + 0x01, 0x30, 0x00, 0xE0, 0x4F, 0x05, 0x01, 0x30, 0x00, 0xE0, 0x38, 0x05, 0x00, 0x33, 0x10, 0x2B, + 0xB4, 0xA4, 0x73, 0xA4, 0x92, 0x06, 0x00, 0xE0, 0x45, 0x05, 0x01, 0x30, 0x93, 0x12, 0x00, 0xE0, + 0x2D, 0x05, 0x3F, 0x32, 0x72, 0x12, 0x00, 0x35, 0x5B, 0xA3, 0x00, 0x33, 0x40, 0x2B, 0x72, 0xA4, + 0x0F, 0x33, 0x00, 0x2D, 0x70, 0xA4, 0xB4, 0xA4, 0xB3, 0xA4, 0xB1, 0xA4, 0x00, 0xE0, 0x2C, 0x05, + 0x01, 0x30, 0x00, 0xE0, 0x2F, 0x05, 0x01, 0x30, 0x00, 0xE0, 0x18, 0x05, 0x00, 0x33, 0x20, 0x2B, + 0xB4, 0xA4, 0x73, 0xA4, 0x72, 0x06, 0x00, 0xE0, 0x25, 0x05, 0x01, 0x30, 0x83, 0x12, 0x00, 0xE0, + 0x0D, 0x05, 0x3F, 0x32, 0x62, 0x12, 0x00, 0x35, 0x5B, 0xA3, 0x00, 0x33, 0x20, 0x2B, 0x72, 0xA4, + 0x0F, 0x33, 0x00, 0x2D, 0x70, 0xA4, 0xB4, 0xA4, 0xB3, 0xA4, 0xB1, 0xA4, 0x00, 0xE0, 0x0C, 0x05, + 0x01, 0x30, 0x00, 0xE0, 0x0F, 0x05, 0x01, 0x30, 0x00, 0xE0, 0xF8, 0x04, 0x00, 0x33, 0x40, 0x2B, + 0xB4, 0xA4, 0x73, 0xA4, 0x52, 0x06, 0x00, 0xE0, 0x05, 0x05, 0x01, 0x30, 0x93, 0x11, 0x00, 0xE0, + 0xED, 0x04, 0x3F, 0x32, 0x72, 0x11, 0x00, 0x35, 0x5B, 0xA3, 0x00, 0x33, 0x10, 0x2B, 0x72, 0xA4, + 0x0F, 0x33, 0x00, 0x2D, 0x70, 0xA4, 0xB4, 0xA4, 0xB3, 0xA4, 0xB1, 0xA4, 0x00, 0xE0, 0xEC, 0x04, + 0x01, 0x30, 0x00, 0xE0, 0xEF, 0x04, 0x01, 0x30, 0x00, 0xE0, 0xD8, 0x04, 0x7F, 0x33, 0xB4, 0xA4, + 0x73, 0xA4, 0x33, 0x06, 0x00, 0xE0, 0xE6, 0x04, 0x01, 0x30, 0x84, 0x11, 0x00, 0xE0, 0xCE, 0x04, + 0x3F, 0x32, 0x63, 0x11, 0x00, 0x35, 0x5B, 0xA3, 0x00, 0x33, 0x08, 0x2B, 0x72, 0xA4, 0x0F, 0x33, + 0x00, 0x2D, 0x70, 0xA4, 0xB4, 0xA4, 0xB3, 0xA4, 0xB1, 0xA4, 0x00, 0xE0, 0xCD, 0x04, 0x01, 0x30, + 0x00, 0xE0, 0xD0, 0x04, 0x01, 0x30, 0x00, 0xE0, 0xB9, 0x04, 0xB4, 0xA4, 0xB3, 0xA4, 0x00, 0x33, + 0x01, 0x2B, 0x72, 0xA4, 0x0F, 0x33, 0xB1, 0xA4, 0x70, 0xA4, 0x00, 0xE0, 0xBD, 0x04, 0x93, 0x14, + 0x00, 0xE0, 0xC0, 0x04, 0x01, 0x30, 0xD2, 0x10, 0x00, 0xE0, 0xA8, 0x04, 0x3F, 0x33, 0x8F, 0x10, + 0x7B, 0xA6, 0x40, 0x33, 0x00, 0x35, 0x79, 0xA6, 0x00, 0x33, 0x04, 0x2B, 0x72, 0xA4, 0x0F, 0x33, + 0x00, 0x2D, 0x70, 0xA4, 0xB4, 0xA4, 0xB3, 0xA4, 0xB1, 0xA4, 0x00, 0xE0, 0xA5, 0x04, 0x01, 0x30, + 0x00, 0xE0, 0xA8, 0x04, 0x01, 0x30, 0x00, 0xE0, 0x91, 0x04, 0x1F, 0x33, 0x7B, 0xA6, 0x00, 0x33, + 0x3F, 0x2B, 0x79, 0xA6, 0x00, 0x33, 0x05, 0x04, 0xA0, 0x0E, 0x00, 0x50, 0xA0, 0x72, 0x00, 0x50, + 0xB4, 0xA4, 0xB3, 0xA4, 0x02, 0x2B, 0xCE, 0x07, 0x00, 0xE0, 0x94, 0x04, 0x01, 0x30, 0x85, 0x13, + 0x00, 0xE0, 0x7C, 0x04, 0x00, 0x33, 0x02, 0x2B, 0x72, 0xA4, 0x0F, 0x33, 0x00, 0x35, 0x00, 0x2D, + 0x70, 0xA4, 0xB4, 0xA4, 0xB3, 0xA4, 0xB1, 0xA4, 0x00, 0xE0, 0x7E, 0x04, 0x01, 0x30, 0x00, 0xE0, + 0x81, 0x04, 0x01, 0x30, 0x00, 0xE0, 0x6A, 0x04, 0x7B, 0x12, 0x3F, 0x32, 0x5B, 0xA3, 0x00, 0x33, + 0xB4, 0xA4, 0xB3, 0xA4, 0x04, 0x2B, 0xAE, 0x07, 0x00, 0xE0, 0x74, 0x04, 0x01, 0x30, 0x95, 0x12, + 0x00, 0xE0, 0x5C, 0x04, 0x00, 0x33, 0x01, 0x2B, 0x72, 0xA4, 0x0F, 0x33, 0x00, 0x35, 0x00, 0x2D, + 0x70, 0xA4, 0xB4, 0xA4, 0xB3, 0xA4, 0xB1, 0xA4, 0x00, 0xE0, 0x5E, 0x04, 0x01, 0x30, 0x00, 0xE0, + 0x61, 0x04, 0x01, 0x30, 0x00, 0xE0, 0x4A, 0x04, 0x6B, 0x12, 0x3F, 0x32, 0x5B, 0xA3, 0x00, 0x33, + 0xB4, 0xA4, 0xB3, 0xA4, 0x08, 0x2B, 0x8E, 0x07, 0x00, 0xE0, 0x54, 0x04, 0x01, 0x30, 0x85, 0x12, + 0x00, 0xE0, 0x3C, 0x04, 0x7F, 0x33, 0x00, 0x35, 0x73, 0xA4, 0x0F, 0x33, 0x00, 0x2D, 0x70, 0xA4, + 0xB4, 0xA4, 0xB2, 0xA4, 0xB1, 0xA4, 0x00, 0xE0, 0x3F, 0x04, 0x01, 0x30, 0x00, 0xE0, 0x42, 0x04, + 0x01, 0x30, 0x00, 0xE0, 0x2B, 0x04, 0x7C, 0x11, 0x3F, 0x32, 0x5B, 0xA3, 0x00, 0x33, 0xB4, 0xA4, + 0xB3, 0xA4, 0x10, 0x2B, 0x6F, 0x07, 0x00, 0xE0, 0x35, 0x04, 0x01, 0x30, 0x95, 0x11, 0x00, 0xE0, + 0x1D, 0x04, 0x00, 0x33, 0x40, 0x2B, 0x73, 0xA4, 0x0F, 0x33, 0x00, 0x35, 0x00, 0x2D, 0x70, 0xA4, + 0xB4, 0xA4, 0xB2, 0xA4, 0xB1, 0xA4, 0x00, 0xE0, 0x1F, 0x04, 0x01, 0x30, 0x00, 0xE0, 0x22, 0x04, + 0x01, 0x30, 0x00, 0xE0, 0x0B, 0x04, 0x6C, 0x11, 0x3F, 0x32, 0x5B, 0xA3, 0x00, 0x33, 0xB4, 0xA4, + 0xB3, 0xA4, 0x20, 0x2B, 0x4F, 0x07, 0x00, 0xE0, 0x15, 0x04, 0x01, 0x30, 0x85, 0x11, 0x00, 0xE0, + 0xFD, 0x03, 0x00, 0x33, 0x20, 0x2B, 0x73, 0xA4, 0x0F, 0x33, 0x00, 0x35, 0x00, 0x2D, 0x70, 0xA4, + 0xB4, 0xA4, 0xB2, 0xA4, 0xB1, 0xA4, 0x00, 0xE0, 0xFF, 0x03, 0x01, 0x30, 0x00, 0xE0, 0x02, 0x04, + 0x01, 0x30, 0x00, 0xE0, 0xEB, 0x03, 0x7C, 0x10, 0x3F, 0x32, 0x5B, 0xA3, 0x00, 0x33, 0xB4, 0xA4, + 0xB3, 0xA4, 0x40, 0x2B, 0x2F, 0x07, 0x00, 0xE0, 0xF5, 0x03, 0x01, 0x30, 0x95, 0x10, 0x00, 0xE0, + 0xDD, 0x03, 0x00, 0x33, 0x10, 0x2B, 0x73, 0xA4, 0x0F, 0x33, 0x00, 0x35, 0x00, 0x2D, 0x70, 0xA4, + 0xB4, 0xA4, 0xB2, 0xA4, 0xB1, 0xA4, 0x00, 0xE0, 0xDF, 0x03, 0x01, 0x30, 0x00, 0xE0, 0xE2, 0x03, + 0x01, 0x30, 0x00, 0xE0, 0xCB, 0x03, 0x6C, 0x10, 0x3F, 0x32, 0x5B, 0xA3, 0xB4, 0xA4, 0xB3, 0xA4, + 0x7F, 0x33, 0x10, 0x07, 0x00, 0xE0, 0xD6, 0x03, 0x01, 0x30, 0x86, 0x10, 0x00, 0xE0, 0xBE, 0x03, + 0x00, 0x33, 0x08, 0x2B, 0x73, 0xA4, 0x0F, 0x33, 0x00, 0x35, 0x00, 0x2D, 0x06, 0x04, 0x00, 0x00, + 0xA0, 0x0E, 0x00, 0x50, 0xA0, 0x72, 0x00, 0x50, 0x70, 0xA4, 0xB4, 0xA4, 0xB2, 0xA4, 0xB1, 0xA4, + 0x00, 0xE0, 0xBA, 0x03, 0x01, 0x30, 0x00, 0xE0, 0xBD, 0x03, 0x01, 0x30, 0x00, 0xE0, 0xA6, 0x03, + 0x64, 0x13, 0x3F, 0x32, 0x5B, 0xA3, 0xB4, 0xA4, 0xB3, 0xA4, 0xB2, 0xA4, 0x00, 0x33, 0x01, 0x2B, + 0x71, 0xA4, 0x0F, 0x33, 0x70, 0xA4, 0x00, 0xE0, 0xA7, 0x03, 0x93, 0x14, 0x00, 0xE0, 0xAA, 0x03, + 0x01, 0x30, 0x9D, 0x12, 0x00, 0xE0, 0x92, 0x03, 0x00, 0x33, 0x04, 0x2B, 0x73, 0xA4, 0x0F, 0x33, + 0x00, 0x35, 0x00, 0x2D, 0x70, 0xA4, 0xB4, 0xA4, 0xB2, 0xA4, 0xB1, 0xA4, 0x00, 0xE0, 0x94, 0x03, + 0x01, 0x30, 0x00, 0xE0, 0x97, 0x03, 0x01, 0x30, 0x00, 0xE0, 0x80, 0x03, 0x71, 0x12, 0x3F, 0x32, + 0x5B, 0xA3, 0x00, 0x33, 0xB4, 0xA4, 0xB3, 0xA4, 0xB2, 0xA4, 0x02, 0x2B, 0xDA, 0x07, 0x00, 0xE0, + 0x89, 0x03, 0x01, 0x30, 0x8C, 0x12, 0x00, 0xE0, 0x71, 0x03, 0x00, 0x33, 0x02, 0x2B, 0x73, 0xA4, + 0x0F, 0x33, 0x00, 0x35, 0x00, 0x2D, 0x70, 0xA4, 0xB4, 0xA4, 0xB2, 0xA4, 0xB1, 0xA4, 0x00, 0xE0, + 0x73, 0x03, 0x01, 0x30, 0x00, 0xE0, 0x76, 0x03, 0x01, 0x30, 0x00, 0xE0, 0x5F, 0x03, 0x61, 0x12, + 0x3F, 0x32, 0x5B, 0xA3, 0x00, 0x33, 0xB4, 0xA4, 0xB3, 0xA4, 0xB2, 0xA4, 0x04, 0x2B, 0xB9, 0x07, + 0x00, 0xE0, 0x68, 0x03, 0x01, 0x30, 0x9C, 0x11, 0x00, 0xE0, 0x50, 0x03, 0x00, 0x33, 0x01, 0x2B, + 0x73, 0xA4, 0x0F, 0x33, 0x00, 0x35, 0x00, 0x2D, 0x70, 0xA4, 0xB4, 0xA4, 0xB2, 0xA4, 0xB1, 0xA4, + 0x00, 0xE0, 0x52, 0x03, 0x01, 0x30, 0x00, 0xE0, 0x55, 0x03, 0x01, 0x30, 0x00, 0xE0, 0x3E, 0x03, + 0x70, 0x11, 0x3F, 0x32, 0x5B, 0xA3, 0x00, 0x33, 0xB4, 0xA4, 0xB3, 0xA4, 0xB2, 0xA4, 0x08, 0x2B, + 0x98, 0x07, 0x00, 0xE0, 0x47, 0x03, 0x01, 0x30, 0x8B, 0x11, 0x00, 0xE0, 0x2F, 0x03, 0x7F, 0x33, + 0x00, 0x35, 0x74, 0xA4, 0x0F, 0x33, 0x00, 0x2D, 0x70, 0xA4, 0xB3, 0xA4, 0xB2, 0xA4, 0xB1, 0xA4, + 0x00, 0xE0, 0x32, 0x03, 0x01, 0x30, 0x00, 0xE0, 0x35, 0x03, 0x01, 0x30, 0x00, 0xE0, 0x1E, 0x03, + 0x60, 0x11, 0x3F, 0x32, 0x5B, 0xA3, 0x00, 0x33, 0xB4, 0xA4, 0xB3, 0xA4, 0xB2, 0xA4, 0x10, 0x2B, + 0x78, 0x07, 0x00, 0xE0, 0x27, 0x03, 0x01, 0x30, 0x9B, 0x10, 0x00, 0xE0, 0x0F, 0x03, 0x00, 0x33, + 0x40, 0x2B, 0x74, 0xA4, 0x0F, 0x33, 0x00, 0x35, 0x00, 0x2D, 0x70, 0xA4, 0xB3, 0xA4, 0xB2, 0xA4, + 0xB1, 0xA4, 0x00, 0xE0, 0x11, 0x03, 0x01, 0x30, 0x00, 0xE0, 0x14, 0x03, 0x01, 0x30, 0x00, 0xE0, + 0xFD, 0x02, 0x70, 0x10, 0x3F, 0x32, 0x5B, 0xA3, 0x00, 0x33, 0xB4, 0xA4, 0xB3, 0xA4, 0xB2, 0xA4, + 0x20, 0x2B, 0x57, 0x07, 0x00, 0xE0, 0x06, 0x03, 0x01, 0x30, 0xAB, 0x10, 0x00, 0xE0, 0xEE, 0x02, + 0x00, 0x33, 0x20, 0x2B, 0x74, 0xA5, 0x0F, 0x33, 0x00, 0x34, 0x00, 0x2C, 0x70, 0xA5, 0x93, 0xA5, + 0x92, 0xA5, 0x91, 0xA5, 0x00, 0xE0, 0xF0, 0x02, 0x01, 0x30, 0x00, 0xE0, 0xF3, 0x02, 0x05, 0x04, + 0xA0, 0x72, 0x00, 0x50, 0xA0, 0x0E, 0x00, 0x50, 0x01, 0x30, 0x00, 0xE0, 0xD7, 0x02, 0x3F, 0x32, + 0x65, 0x13, 0x5B, 0xA3, 0x00, 0x33, 0x40, 0x2B, 0x94, 0xA5, 0x93, 0xA5, 0x92, 0xA5, 0x71, 0xA5, + 0x90, 0xA5, 0x00, 0xE0, 0xD9, 0x02, 0x93, 0x14, 0x00, 0xE0, 0xDC, 0x02, 0x01, 0x30, 0xBF, 0x12, + 0x00, 0xE0, 0xC4, 0x02, 0x00, 0x33, 0x10, 0x2B, 0x74, 0xA5, 0x0F, 0x33, 0x00, 0x34, 0x00, 0x2C, + 0x70, 0xA5, 0x93, 0xA5, 0x92, 0xA5, 0x91, 0xA5, 0x00, 0xE0, 0xC6, 0x02, 0x01, 0x30, 0x00, 0xE0, + 0xC9, 0x02, 0x01, 0x30, 0x00, 0xE0, 0xB2, 0x02, 0x3F, 0x32, 0x73, 0x12, 0x5B, 0xA3, 0x7F, 0x33, + 0x94, 0xA5, 0x93, 0xA5, 0x92, 0xA5, 0x71, 0xA5, 0x90, 0xA5, 0x00, 0xE0, 0xB5, 0x02, 0x93, 0x14, + 0x00, 0xE0, 0xB8, 0x02, 0x01, 0x30, 0xAD, 0x12, 0x00, 0xE0, 0xA0, 0x02, 0x00, 0x33, 0x08, 0x2B, + 0x74, 0xA5, 0x0F, 0x33, 0x00, 0x34, 0x00, 0x2C, 0x70, 0xA5, 0x93, 0xA5, 0x92, 0xA5, 0x91, 0xA5, + 0x00, 0xE0, 0xA2, 0x02, 0x01, 0x30, 0x00, 0xE0, 0xA5, 0x02, 0x01, 0x30, 0x00, 0xE0, 0x8E, 0x02, + 0x3F, 0x32, 0x61, 0x12, 0x5B, 0xA3, 0x0E, 0x33, 0x94, 0xA5, 0x93, 0xA5, 0x92, 0xA5, 0x91, 0xA5, + 0x70, 0xA5, 0x00, 0xE0, 0x91, 0x02, 0x93, 0x14, 0x00, 0xE0, 0x94, 0x02, 0x01, 0x30, 0xBB, 0x11, + 0x00, 0xE0, 0x7C, 0x02, 0x00, 0x33, 0x04, 0x2B, 0x74, 0xA5, 0x0F, 0x33, 0x00, 0x34, 0x00, 0x2C, + 0x70, 0xA5, 0x93, 0xA5, 0x92, 0xA5, 0x91, 0xA5, 0x00, 0xE0, 0x7E, 0x02, 0x01, 0x30, 0x00, 0xE0, + 0x81, 0x02, 0x01, 0x30, 0x00, 0xE0, 0x6A, 0x02, 0x3F, 0x32, 0x6F, 0x11, 0x5B, 0xA3, 0x0D, 0x33, + 0x94, 0xA5, 0x93, 0xA5, 0x92, 0xA5, 0x91, 0xA5, 0x70, 0xA5, 0x00, 0xE0, 0x6D, 0x02, 0x93, 0x14, + 0x00, 0xE0, 0x70, 0x02, 0x01, 0x30, 0xA9, 0x11, 0x00, 0xE0, 0x58, 0x02, 0x00, 0x33, 0x02, 0x2B, + 0x74, 0xA5, 0x0F, 0x33, 0x00, 0x34, 0x00, 0x2C, 0x70, 0xA5, 0x93, 0xA5, 0x92, 0xA5, 0x91, 0xA5, + 0x00, 0xE0, 0x5A, 0x02, 0x01, 0x30, 0x00, 0xE0, 0x5D, 0x02, 0x01, 0x30, 0x00, 0xE0, 0x46, 0x02, + 0x3F, 0x32, 0x7D, 0x10, 0x5B, 0xA3, 0x0B, 0x33, 0x94, 0xA5, 0x93, 0xA5, 0x92, 0xA5, 0x91, 0xA5, + 0x70, 0xA5, 0x00, 0xE0, 0x49, 0x02, 0x93, 0x14, 0x00, 0xE0, 0x4C, 0x02, 0x01, 0x30, 0xB7, 0x10, + 0x00, 0xE0, 0x34, 0x02, 0x00, 0x33, 0x01, 0x2B, 0x74, 0xA5, 0x0F, 0x33, 0x00, 0x34, 0x00, 0x2C, + 0x70, 0xA5, 0x93, 0xA5, 0x92, 0xA5, 0x91, 0xA5, 0x00, 0xE0, 0x36, 0x02, 0x01, 0x30, 0x00, 0xE0, + 0x39, 0x02, 0x01, 0x30, 0x00, 0xE0, 0x22, 0x02, 0x6B, 0x10, 0x3F, 0x32, 0x5B, 0xA3, 0x40, 0x32, + 0x59, 0xA3, 0x07, 0x33, 0x94, 0xA5, 0x93, 0xA5, 0x92, 0xA5, 0x91, 0xA5, 0x70, 0xA5, 0x00, 0xE0, + 0x23, 0x02, 0x93, 0x14, 0x00, 0xE0, 0x26, 0x02, 0x01, 0x30, 0xA4, 0x10, 0x00, 0xE0, 0x0E, 0x02, + 0x06, 0x04, 0x00, 0x00, 0xA0, 0x72, 0x00, 0x50, 0xA0, 0x0E, 0x00, 0x50, 0x3F, 0x32, 0x7A, 0x10, + 0x00, 0x34, 0x5B, 0xA3, 0x07, 0x33, 0x00, 0x2C, 0x70, 0xA5, 0x94, 0xA5, 0x93, 0xA5, 0x92, 0xA5, + 0x91, 0xA5, 0x00, 0xE0, 0x09, 0x02, 0x01, 0x30, 0x00, 0xE0, 0x0C, 0x02, 0x01, 0x30, 0x00, 0xE0, + 0xF5, 0x01, 0x00, 0x33, 0x01, 0x2B, 0x00, 0xE8, 0x73, 0xFB, 0x00, 0x00, 0x3C, 0x78, 0x00, 0x00, + 0xD0, 0x14, 0x40, 0x38, 0x02, 0x08, 0x90, 0x14, 0x00, 0xE0, 0x26, 0x04, 0x4B, 0x10, 0x62, 0xD8, + 0x84, 0x04, 0x00, 0x23, 0xCC, 0x74, 0x62, 0xDC, 0x84, 0x04, 0x90, 0x14, 0xD1, 0x14, 0x04, 0xEA, + 0x88, 0x13, 0xC8, 0x30, 0x00, 0x2C, 0x11, 0x75, 0x00, 0xE0, 0xC0, 0x00, 0x40, 0x3C, 0xFA, 0x0B, + 0x91, 0x14, 0x00, 0x00, 0xA0, 0x72, 0x00, 0x50, 0x04, 0x1D, 0x00, 0x30, 0xC4, 0x14, 0x80, 0xE4, + 0x03, 0x20, 0xC3, 0x6C, 0x40, 0x3C, 0x10, 0x0C, 0x04, 0x36, 0x92, 0x61, 0x98, 0x64, 0xC2, 0xC4, + 0x40, 0x0C, 0x40, 0x3E, 0x08, 0x0C, 0x44, 0x75, 0x03, 0x6D, 0x78, 0x58, 0xA0, 0xA4, 0x00, 0x24, + 0x0E, 0x65, 0xFD, 0x0B, 0x9A, 0x60, 0x90, 0x41, 0xB8, 0x41, 0x50, 0x6D, 0x44, 0x6D, 0x88, 0x41, + 0x50, 0x6D, 0x82, 0x4A, 0x40, 0x3C, 0x09, 0x0C, 0xE2, 0x44, 0x0F, 0x6D, 0xDC, 0x5B, 0xA0, 0xB4, + 0x03, 0x24, 0x1A, 0x65, 0xFD, 0x0B, 0x1F, 0x6D, 0x42, 0xE4, 0x03, 0x20, 0xD0, 0x60, 0x40, 0x3A, + 0x07, 0x0C, 0x44, 0x74, 0x8C, 0x60, 0x20, 0xA3, 0x00, 0x23, 0xCA, 0x64, 0xFD, 0x0B, 0x84, 0x14, + 0xC4, 0x14, 0x03, 0x33, 0x60, 0xC4, 0x24, 0x20, 0x40, 0x3C, 0x04, 0x08, 0xC4, 0x68, 0x40, 0x3B, + 0x0D, 0x0C, 0x40, 0x3A, 0x0A, 0x0C, 0x03, 0x6D, 0x84, 0x60, 0x60, 0x81, 0x00, 0x21, 0x4A, 0x64, + 0xCC, 0x74, 0x60, 0xA4, 0x00, 0x24, 0xFA, 0x0B, 0x84, 0x14, 0x62, 0x4A, 0x40, 0x3B, 0x0C, 0x0C, + 0xE2, 0x43, 0x03, 0x6D, 0xC7, 0x6C, 0xDC, 0x59, 0xA0, 0x93, 0x03, 0x23, 0x8E, 0x65, 0xA0, 0xB4, + 0x03, 0x24, 0xFB, 0x0B, 0xDF, 0x6C, 0x42, 0xE4, 0x03, 0x20, 0xAC, 0x58, 0x40, 0x3A, 0x4C, 0x60, + 0xEC, 0x0F, 0x84, 0x60, 0x46, 0x61, 0x60, 0x81, 0x84, 0x5D, 0x00, 0x21, 0x86, 0x64, 0xCC, 0x74, + 0x60, 0xA4, 0xFA, 0x0B, 0x84, 0x14, 0x00, 0x00, 0x79, 0x10, 0x47, 0x93, 0x42, 0xEC, 0x00, 0x02, + 0x47, 0xB3, 0x47, 0x93, 0x42, 0xEC, 0x20, 0x00, 0x47, 0xB3, 0x47, 0x93, 0x42, 0xEC, 0x10, 0x00, + 0x47, 0xB3, 0x43, 0x93, 0x95, 0x3A, 0x43, 0xB3, 0x72, 0x10, 0x00, 0x32, 0x00, 0x2A, 0x40, 0xB3, + 0x41, 0xB3, 0x42, 0xB3, 0x43, 0xB3, 0x44, 0xB3, 0x45, 0xB3, 0x46, 0xB3, 0x47, 0xB3, 0x42, 0x93, + 0x8E, 0x3A, 0x8F, 0x3A, 0x42, 0xB3, 0x41, 0x93, 0x87, 0x3A, 0x8E, 0x3A, 0x41, 0xB3, 0x3C, 0x78, + 0xD0, 0x14, 0x00, 0xE0, 0x07, 0x08, 0x01, 0x30, 0x00, 0xE0, 0x54, 0x08, 0x00, 0xE0, 0xD2, 0x05, + 0x00, 0x31, 0x07, 0x30, 0x00, 0xE0, 0x78, 0x07, 0x90, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x40, + 0x00, 0xE4, 0x00, 0xE0, 0x20, 0xEA, 0x00, 0x80, 0x00, 0xC0, 0x20, 0x64, 0x2B, 0x11, 0x01, 0xC0, + 0x21, 0x64, 0x80, 0xC1, 0x20, 0x74, 0x2A, 0x11, 0x87, 0x6F, 0x0A, 0x11, 0x2A, 0x11, 0x4B, 0x11, + 0x86, 0x60, 0x40, 0x3A, 0x08, 0x0C, 0x60, 0x90, 0x60, 0xB1, 0x03, 0x20, 0x03, 0x21, 0x03, 0x2A, + 0x40, 0x3A, 0xFA, 0x0B, 0x00, 0xE0, 0x20, 0x12, 0x20, 0x38, 0x3D, 0x08, 0x00, 0x28, 0x20, 0x38, + 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, + 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, + 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, + 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, + 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, + 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, + 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, 0x03, 0x6C, + 0x03, 0x6C, 0xC5, 0x0F, 0x3C, 0x78, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x30, + 0x0C, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x3C, 0x0F, 0x00, 0x30, 0xD4, 0x14, 0xCE, 0xE5, + 0x8B, 0x14, 0x02, 0xEA, 0x80, 0x04, 0x00, 0x31, 0x03, 0x18, 0xFF, 0xE3, 0xF9, 0xFE, 0x03, 0x1B, + 0x62, 0xB8, 0xCF, 0x6D, 0x70, 0x11, 0x00, 0x30, 0x90, 0x11, 0x20, 0x36, 0x61, 0xB8, 0xA7, 0xE4, + 0x7F, 0x04, 0x2D, 0x11, 0x9F, 0x6C, 0x12, 0x33, 0x03, 0x04, 0x60, 0x81, 0x00, 0x21, 0x64, 0x43, + 0xC0, 0x60, 0x61, 0x43, 0xD0, 0x60, 0x60, 0x8B, 0x60, 0xAA, 0x98, 0x60, 0x4A, 0x65, 0xF6, 0x0B, + 0x00, 0x20, 0x50, 0x38, 0x01, 0x27, 0xEC, 0x0B, 0x07, 0xEA, 0x8F, 0x01, 0x15, 0x1E, 0x03, 0x1A, + 0x4B, 0x6D, 0x00, 0x31, 0x00, 0x30, 0x65, 0xD8, 0x00, 0x50, 0xDD, 0x64, 0x04, 0x08, 0x00, 0x21, + 0x44, 0x74, 0x0C, 0x60, 0x01, 0x25, 0x5A, 0x65, 0xF7, 0x0B, 0x05, 0x33, 0x4C, 0x64, 0x31, 0x08, + 0x40, 0xB8, 0x00, 0xE0, 0x61, 0x11, 0x40, 0x98, 0x01, 0x74, 0xCB, 0x6C, 0x20, 0x8B, 0x42, 0x60, + 0x20, 0xAB, 0x01, 0x23, 0xD6, 0x64, 0xFB, 0x0B, 0x48, 0x33, 0x8C, 0x60, 0x8C, 0x61, 0x6E, 0xE4, + 0x8B, 0x04, 0xCA, 0x64, 0xDE, 0x0B, 0x00, 0x30, 0x20, 0x36, 0xE2, 0x98, 0xA7, 0xE4, 0x7F, 0x04, + 0x21, 0x98, 0x9F, 0x6C, 0x12, 0x33, 0xE0, 0xB8, 0x03, 0x04, 0x60, 0x81, 0x00, 0x21, 0xE0, 0x8A, + 0x98, 0x60, 0x96, 0x64, 0x64, 0x43, 0xC0, 0x60, 0x61, 0x43, 0xD0, 0x60, 0xE0, 0xAB, 0xF6, 0x0B, + 0x00, 0x20, 0x50, 0x38, 0xE0, 0x98, 0x01, 0x27, 0xEA, 0x0B, 0xCE, 0xE5, 0x8B, 0x04, 0x94, 0x14, + 0x00, 0x30, 0xD3, 0x07, 0xE1, 0x34, 0x00, 0x00, 0xBC, 0x13, 0x00, 0x30, 0xD2, 0x14, 0xA9, 0x13, + 0x60, 0x85, 0x40, 0x3B, 0x06, 0x0C, 0x80, 0x85, 0x10, 0x75, 0x41, 0x3C, 0x2E, 0x0C, 0x92, 0x14, + 0x85, 0x13, 0x11, 0x30, 0x6B, 0x8C, 0x71, 0x43, 0x71, 0x4B, 0x6B, 0xAC, 0x23, 0xEA, 0x07, 0x00, + 0x40, 0x94, 0x62, 0xC4, 0x43, 0x20, 0xB0, 0x3B, 0x60, 0xB4, 0x60, 0x13, 0x60, 0xAC, 0x60, 0x13, + 0x64, 0xAC, 0x23, 0xEA, 0xC0, 0x07, 0x43, 0x94, 0x62, 0xC4, 0x43, 0x20, 0xB7, 0x3B, 0x63, 0xB4, + 0x03, 0xEA, 0xC0, 0xFF, 0x43, 0x94, 0x62, 0xC4, 0x43, 0x20, 0x63, 0xEC, 0xC0, 0x8F, 0x63, 0xB4, + 0x00, 0xE0, 0xB2, 0x0A, 0x63, 0x94, 0x63, 0xEC, 0x01, 0x00, 0x63, 0xB4, 0x00, 0xE0, 0xF2, 0x10, + 0x80, 0x85, 0x10, 0x75, 0x41, 0x3C, 0xD4, 0x0B, 0xAF, 0x12, 0x11, 0x30, 0x6B, 0x8D, 0x71, 0x43, + 0x71, 0x4B, 0x6B, 0xAD, 0x23, 0xEA, 0x07, 0x00, 0x40, 0x95, 0x62, 0xC4, 0x43, 0x20, 0xB0, 0x3B, + 0x60, 0xB5, 0x6A, 0x12, 0x60, 0xAD, 0x6A, 0x12, 0x64, 0xAD, 0x23, 0xEA, 0xC0, 0x07, 0x43, 0x95, + 0x62, 0xC4, 0x43, 0x20, 0xB7, 0x3B, 0x63, 0xB5, 0x03, 0xEA, 0xC0, 0xFF, 0x43, 0x95, 0x62, 0xC4, + 0x43, 0x20, 0x63, 0xEC, 0xC0, 0x8F, 0x63, 0xB5, 0x00, 0xE0, 0x86, 0x0A, 0x63, 0x95, 0x0C, 0x6D, + 0x83, 0xB5, 0x00, 0xE0, 0xC7, 0x10, 0x92, 0x14, 0x7E, 0x11, 0x5F, 0x11, 0x40, 0x38, 0x40, 0xB3, + 0x07, 0x0C, 0x41, 0x38, 0x02, 0x0C, 0x3C, 0x78, 0x27, 0x32, 0x40, 0xB3, 0x3C, 0x78, 0x26, 0x32, + 0x40, 0xB3, 0x3C, 0x78, 0x77, 0x11, 0x10, 0x32, 0x40, 0xB3, 0x58, 0x11, 0x40, 0xB3, 0x3C, 0x78, + 0x74, 0x11, 0x55, 0x11, 0x41, 0x38, 0x40, 0xB3, 0x26, 0x32, 0x40, 0xB3, 0x12, 0x0C, 0x40, 0x38, + 0x0C, 0x0C, 0x42, 0x38, 0x04, 0x08, 0x72, 0x11, 0x03, 0x32, 0x40, 0xB3, 0x6D, 0x11, 0x10, 0x32, + 0x40, 0xB3, 0x4E, 0x11, 0x40, 0xB3, 0x3C, 0x78, 0x6D, 0x11, 0x01, 0x32, 0x40, 0xB3, 0xF7, 0x07, + 0x6B, 0x11, 0x02, 0x32, 0x40, 0xB3, 0xF3, 0x07, 0xC4, 0x14, 0x66, 0x11, 0x86, 0x11, 0x41, 0x38, + 0x80, 0xB3, 0x26, 0x34, 0x80, 0xB3, 0x87, 0x0C, 0x40, 0x38, 0x81, 0x0C, 0x42, 0x38, 0x34, 0x0C, + 0x60, 0x11, 0x10, 0x30, 0x41, 0x39, 0x00, 0xB3, 0x00, 0x11, 0x00, 0xB3, 0x1E, 0x10, 0x00, 0xB3, + 0x27, 0x30, 0x00, 0xB3, 0x5E, 0x0C, 0x40, 0x39, 0x43, 0x0C, 0x42, 0x39, 0x1F, 0x08, 0xDD, 0x10, + 0x00, 0x35, 0x10, 0x37, 0x5B, 0x6C, 0xD7, 0x6C, 0x9C, 0x5E, 0x02, 0x5B, 0xC8, 0x60, 0x60, 0x83, + 0x60, 0xA1, 0x00, 0x21, 0x52, 0x64, 0xC1, 0x74, 0xF9, 0x0B, 0x0F, 0x25, 0x03, 0xEA, 0x60, 0x01, + 0x55, 0x75, 0xFF, 0x26, 0xD6, 0x64, 0xEF, 0x0B, 0x8C, 0x60, 0x33, 0x10, 0x73, 0x10, 0x00, 0x82, + 0x00, 0x22, 0x00, 0xA3, 0x00, 0x23, 0x4E, 0x64, 0xFB, 0x0B, 0x6A, 0x10, 0x10, 0x32, 0x40, 0xB3, + 0x4A, 0x10, 0x40, 0xB3, 0x84, 0x14, 0x6A, 0x10, 0x03, 0x30, 0x00, 0xB3, 0x18, 0x04, 0x00, 0x00, + 0x94, 0x21, 0x00, 0x30, 0x00, 0x02, 0x00, 0x40, 0x3C, 0x0F, 0x00, 0x30, 0x08, 0x1D, 0x00, 0x30, + 0x00, 0xFF, 0x00, 0x50, 0x55, 0xAA, 0x66, 0x00, 0x66, 0x99, 0x55, 0x00, 0x00, 0x19, 0x00, 0x50, + 0x00, 0x79, 0x00, 0x50, 0x08, 0x8F, 0x00, 0x50, 0x00, 0x8F, 0x00, 0x50, 0xB2, 0x07, 0xE7, 0x13, + 0x01, 0x35, 0x00, 0x36, 0x45, 0x3D, 0x11, 0x0C, 0x5F, 0x6C, 0xDB, 0x6C, 0x87, 0xE4, 0x0F, 0x00, + 0x02, 0x5B, 0xC8, 0x60, 0x60, 0x83, 0x60, 0xA1, 0x00, 0x21, 0x52, 0x64, 0xC1, 0x74, 0xF9, 0x0B, + 0x59, 0x3D, 0x0F, 0x26, 0x99, 0x75, 0xCA, 0x0F, 0x00, 0x25, 0xFF, 0x27, 0x55, 0x75, 0xEB, 0x07, + 0xDB, 0x12, 0x00, 0x35, 0x10, 0x37, 0x5B, 0x6C, 0xD7, 0x6C, 0x9C, 0x5E, 0x02, 0x5B, 0xC8, 0x60, + 0x60, 0x83, 0x60, 0xA1, 0x00, 0x21, 0x52, 0x64, 0xC1, 0x74, 0xF9, 0x0B, 0x0F, 0x25, 0x03, 0xEA, + 0x90, 0x03, 0x55, 0x75, 0xFF, 0x26, 0xD6, 0x64, 0xEF, 0x0B, 0xB0, 0x07, 0x71, 0x12, 0x01, 0x30, + 0x00, 0xB3, 0x7F, 0x07, 0x6F, 0x12, 0x02, 0x30, 0x00, 0xB3, 0x7B, 0x07, 0xC4, 0x14, 0x8E, 0x12, + 0x67, 0x94, 0x70, 0x43, 0x7F, 0x4B, 0x41, 0x3B, 0xFC, 0x0F, 0x65, 0x94, 0xB7, 0x3B, 0x65, 0xB4, + 0x6A, 0x12, 0x8B, 0x12, 0x41, 0x38, 0x80, 0xB3, 0x26, 0x34, 0x80, 0xB3, 0x5D, 0x0C, 0x40, 0x38, + 0x57, 0x0C, 0x42, 0x38, 0x32, 0x0C, 0x65, 0x12, 0x10, 0x30, 0x41, 0x39, 0x00, 0xB3, 0x05, 0x12, + 0x00, 0xB3, 0x03, 0x12, 0x00, 0xB3, 0x27, 0x30, 0x00, 0xB3, 0x2B, 0x0C, 0x42, 0x39, 0x1F, 0x08, + 0xC1, 0x12, 0x00, 0x35, 0x5B, 0x6C, 0xD7, 0x6C, 0x86, 0xE4, 0x0F, 0x00, 0xE0, 0x81, 0x00, 0x21, + 0x52, 0x64, 0x02, 0x5B, 0xC8, 0x60, 0xE0, 0xA3, 0xC1, 0x74, 0xF9, 0x0B, 0x0F, 0x25, 0x03, 0xEA, + 0x60, 0x01, 0x55, 0x75, 0xFF, 0x26, 0xD6, 0x64, 0xEE, 0x0B, 0x8C, 0x60, 0x37, 0x11, 0x78, 0x11, + 0x00, 0x83, 0x00, 0x23, 0x4E, 0x64, 0x00, 0xA2, 0x00, 0x22, 0xFB, 0x0B, 0x6F, 0x11, 0x10, 0x32, + 0x40, 0xB3, 0x50, 0x11, 0x40, 0xB3, 0x84, 0x14, 0x6A, 0x11, 0x03, 0x30, 0x00, 0xB3, 0xCC, 0x07, + 0xC7, 0x11, 0x00, 0x35, 0x5B, 0x6C, 0xD7, 0x6C, 0x86, 0xE4, 0x0F, 0x00, 0xE0, 0x81, 0x00, 0x21, + 0x52, 0x64, 0x02, 0x5B, 0xC8, 0x60, 0xE0, 0xA3, 0xC1, 0x74, 0xF9, 0x0B, 0x0F, 0x25, 0x03, 0xEA, + 0x10, 0x04, 0x55, 0x75, 0xFF, 0x26, 0xD6, 0x64, 0xEE, 0x0B, 0x8C, 0x60, 0x25, 0x11, 0x66, 0x11, + 0x00, 0x83, 0x00, 0x23, 0x4E, 0x64, 0x00, 0xA2, 0x00, 0x22, 0xFB, 0x0B, 0xD8, 0x07, 0x79, 0x10, + 0x01, 0x30, 0x00, 0xB3, 0xA9, 0x07, 0x77, 0x10, 0x02, 0x30, 0x00, 0xB3, 0xA5, 0x07, 0x00, 0x00, + 0x3C, 0x78, 0x00, 0x00, 0x5D, 0x10, 0x60, 0x92, 0x40, 0x3B, 0xFE, 0x0F, 0x00, 0x33, 0x60, 0xB2, + 0x3C, 0x78, 0x00, 0x00, 0xD4, 0x14, 0x3C, 0x16, 0x02, 0xEA, 0x68, 0x01, 0x00, 0x31, 0x01, 0xB8, + 0x02, 0x18, 0xFF, 0xE3, 0xED, 0xFC, 0x76, 0x10, 0x8F, 0x6C, 0x60, 0xB8, 0x61, 0x93, 0x00, 0x23, + 0x61, 0xB2, 0x4B, 0x6C, 0x68, 0x82, 0x40, 0x3B, 0x29, 0x08, 0x41, 0x92, 0x03, 0x33, 0x8C, 0x64, + 0x20, 0x0C, 0x1C, 0x16, 0x94, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x50, 0x00, 0x40, 0x00, 0x50, + 0x00, 0x19, 0x00, 0x50, 0x80, 0x00, 0x00, 0x40, 0x00, 0xFF, 0x00, 0x50, 0x55, 0xAA, 0x66, 0x00, + 0x66, 0x99, 0x55, 0x00, 0x00, 0x79, 0x00, 0x50, 0x08, 0x8F, 0x00, 0x50, 0x00, 0x8F, 0x00, 0x50, + 0x08, 0x81, 0x00, 0x50, 0x00, 0x81, 0x00, 0x50, 0x8C, 0x52, 0x00, 0x30, 0x94, 0x21, 0x00, 0x30, + 0x61, 0x91, 0x63, 0xE4, 0x01, 0x20, 0x40, 0x3B, 0xDD, 0x0B, 0x60, 0x98, 0x68, 0x83, 0x40, 0x3B, + 0xD9, 0x0B, 0xB0, 0x13, 0x60, 0x85, 0x48, 0x3B, 0x97, 0x0C, 0x8F, 0x13, 0xC1, 0x98, 0x00, 0x30, + 0x19, 0x04, 0x41, 0x40, 0x90, 0x60, 0x20, 0x8A, 0xC5, 0x75, 0x20, 0x85, 0x00, 0x29, 0x23, 0xC4, + 0x21, 0x40, 0x46, 0x6C, 0x5C, 0x68, 0x20, 0xAA, 0x20, 0x8A, 0xE0, 0x85, 0x01, 0x2F, 0xDC, 0x70, + 0xC4, 0x6C, 0xCD, 0x74, 0x60, 0xAA, 0x03, 0xEA, 0x40, 0x02, 0x00, 0x20, 0xC2, 0x64, 0x01, 0x26, + 0x2E, 0x0C, 0x40, 0x85, 0x01, 0x33, 0x8C, 0x64, 0x10, 0x08, 0x40, 0x8E, 0x89, 0x74, 0x01, 0xEA, + 0x47, 0x05, 0x48, 0x64, 0xDF, 0x0F, 0x40, 0x8E, 0x89, 0x74, 0x01, 0xEA, 0x81, 0x05, 0x84, 0x64, + 0xEB, 0x0B, 0x41, 0x40, 0x90, 0x60, 0xE1, 0x07, 0x60, 0x85, 0xCC, 0x74, 0x41, 0x3B, 0xE4, 0x0B, + 0x40, 0x8E, 0x89, 0x74, 0x01, 0xEA, 0x47, 0x05, 0x48, 0x64, 0xDE, 0x0B, 0x41, 0x40, 0x90, 0x60, + 0x20, 0x8A, 0xE0, 0x85, 0x00, 0x2F, 0xDC, 0x70, 0x45, 0x74, 0xCE, 0x6C, 0xC4, 0x68, 0x60, 0xAA, + 0x03, 0xEA, 0x40, 0x02, 0x00, 0x20, 0xC2, 0x64, 0x01, 0x26, 0xD4, 0x0B, 0x14, 0x1F, 0x9F, 0x6D, + 0x00, 0x32, 0x02, 0x1B, 0x47, 0x2A, 0x5C, 0x19, 0x8E, 0x60, 0x78, 0x5A, 0x61, 0x43, 0xD0, 0x60, + 0x60, 0x8B, 0x60, 0xA6, 0x00, 0x26, 0x5A, 0x64, 0xF9, 0x0B, 0x02, 0x31, 0x00, 0x30, 0x02, 0x1A, + 0xFF, 0xE3, 0x54, 0xFE, 0xD8, 0x32, 0x02, 0x1B, 0x8E, 0x60, 0x7C, 0x5A, 0x61, 0x43, 0xD0, 0x60, + 0x60, 0x8B, 0x60, 0xA7, 0x00, 0x27, 0xDA, 0x65, 0xF9, 0x0B, 0x02, 0x31, 0x01, 0x30, 0x02, 0x1A, + 0xFF, 0xE3, 0x44, 0xFE, 0x59, 0x11, 0x67, 0x92, 0x70, 0x43, 0x7F, 0x4B, 0x41, 0x3B, 0xFC, 0x0F, + 0x65, 0x92, 0xB7, 0x3B, 0x65, 0xB2, 0x76, 0x11, 0x56, 0x11, 0x00, 0x31, 0x40, 0xB3, 0x26, 0x32, + 0x40, 0xB3, 0x55, 0x11, 0x21, 0xA2, 0x24, 0x31, 0x20, 0xA2, 0x03, 0x31, 0x21, 0xA2, 0x10, 0x32, + 0x40, 0xB3, 0x52, 0x11, 0x40, 0xB3, 0x60, 0x85, 0x41, 0x3B, 0x0A, 0x0C, 0x60, 0x85, 0x40, 0x3B, + 0x49, 0x0F, 0x60, 0x85, 0x00, 0x2B, 0xCC, 0x74, 0x60, 0xA5, 0x1C, 0x16, 0x94, 0x14, 0x60, 0x98, + 0x01, 0x32, 0x48, 0xA3, 0xF4, 0x07, 0x02, 0x31, 0x00, 0x30, 0x14, 0x1F, 0x82, 0x11, 0x9F, 0x6D, + 0x02, 0x1A, 0xFF, 0xE3, 0xA5, 0xFE, 0x00, 0x31, 0x02, 0x1B, 0x47, 0x29, 0x5C, 0x18, 0x4E, 0x60, + 0x78, 0x59, 0x40, 0x86, 0x00, 0x26, 0x1A, 0x64, 0x61, 0x43, 0xD0, 0x60, 0x40, 0xAB, 0xF9, 0x0B, + 0x02, 0x31, 0x01, 0x30, 0x02, 0x1A, 0xFF, 0xE3, 0x93, 0xFE, 0xD8, 0x31, 0x02, 0x1B, 0x4E, 0x60, + 0x7C, 0x59, 0x40, 0x87, 0x00, 0x27, 0x9E, 0x65, 0x61, 0x43, 0xD0, 0x60, 0x40, 0xAB, 0xF9, 0x0B, + 0x46, 0x07, 0x00, 0x00, 0xD4, 0x14, 0xCE, 0xE5, 0x8B, 0x14, 0x02, 0xEA, 0x80, 0x04, 0x00, 0x31, + 0x03, 0x18, 0xFF, 0xE3, 0xED, 0xFB, 0x52, 0x10, 0x60, 0x92, 0x40, 0x3B, 0xFE, 0x0F, 0x00, 0x33, + 0x90, 0x10, 0x60, 0xB2, 0x70, 0x10, 0x11, 0x30, 0x4B, 0x8B, 0x51, 0x42, 0x51, 0x4A, 0x4B, 0xAB, + 0x22, 0xEA, 0x07, 0x00, 0x20, 0x93, 0x41, 0xC4, 0x42, 0x20, 0xB0, 0x3A, 0xB1, 0x3A, 0x15, 0x04, + 0x04, 0x00, 0x00, 0x30, 0xA0, 0x21, 0x00, 0x30, 0x80, 0x00, 0x00, 0x40, 0x00, 0xFF, 0x00, 0x50, + 0x55, 0xAA, 0x66, 0x00, 0xA0, 0x55, 0x00, 0x50, 0x66, 0x99, 0x55, 0x00, 0x8C, 0x52, 0x00, 0x30, + 0x20, 0x26, 0x00, 0x30, 0x00, 0x02, 0x00, 0x40, 0x40, 0xB3, 0x02, 0xEA, 0xFF, 0x3F, 0x8A, 0x6C, + 0xE1, 0x13, 0x40, 0xAB, 0x91, 0x74, 0x44, 0xAB, 0x22, 0xEA, 0xC0, 0x07, 0x23, 0x93, 0x41, 0xC4, + 0x42, 0x20, 0xB7, 0x3A, 0x43, 0xB3, 0x02, 0xEA, 0xC0, 0xFF, 0x23, 0x93, 0x41, 0xC4, 0x42, 0x20, + 0x42, 0xEC, 0xC0, 0xB3, 0x43, 0xB3, 0x43, 0x93, 0x42, 0xEC, 0x01, 0x00, 0x43, 0xB3, 0x00, 0xE0, + 0xF3, 0x07, 0x00, 0xE0, 0x37, 0x0E, 0xDF, 0x6C, 0x48, 0x31, 0x07, 0xE4, 0x3F, 0x02, 0x41, 0x41, + 0x90, 0x60, 0x40, 0x8A, 0x40, 0xAB, 0x01, 0x23, 0x0E, 0x64, 0x00, 0x21, 0xF9, 0x0B, 0x0F, 0x12, + 0x01, 0xEA, 0xB0, 0x01, 0x41, 0x41, 0x90, 0x60, 0x40, 0x8A, 0x40, 0xAB, 0x01, 0x23, 0x0E, 0x64, + 0x00, 0x21, 0xF9, 0x0B, 0x6A, 0x12, 0x8F, 0x6C, 0x60, 0xB8, 0x60, 0x93, 0x41, 0x3B, 0x23, 0x0C, + 0x60, 0x92, 0x88, 0x12, 0x42, 0x3B, 0x00, 0x33, 0x20, 0x0C, 0x00, 0xEA, 0x40, 0x02, 0x20, 0x8F, + 0x41, 0x43, 0x00, 0x23, 0x0E, 0x64, 0x90, 0x60, 0x20, 0xAA, 0x01, 0x27, 0xF9, 0x0B, 0x60, 0x98, + 0x60, 0x93, 0x41, 0x3B, 0x2B, 0x0C, 0x60, 0x98, 0x60, 0x93, 0x42, 0x3B, 0xA7, 0x0C, 0x60, 0x98, + 0x60, 0x93, 0x45, 0x3B, 0x10, 0x0D, 0x60, 0x98, 0x60, 0x93, 0x44, 0x3B, 0x10, 0x0D, 0xCE, 0xE5, + 0x8B, 0x04, 0x94, 0x14, 0x00, 0x33, 0x97, 0x11, 0x48, 0x36, 0xA3, 0xE4, 0x0F, 0x00, 0x5F, 0x6C, + 0x55, 0x75, 0x8F, 0x6C, 0x00, 0x23, 0xCD, 0x74, 0x00, 0x89, 0x4E, 0x65, 0x41, 0x42, 0x90, 0x60, + 0x00, 0xAA, 0x58, 0x60, 0x8F, 0x6C, 0xF7, 0x0B, 0x02, 0xEA, 0x40, 0x02, 0x01, 0x27, 0x8E, 0x64, + 0xED, 0x0B, 0x60, 0x98, 0x60, 0x93, 0x41, 0x3B, 0xD7, 0x0B, 0x6B, 0x11, 0x62, 0xB8, 0x60, 0x83, + 0x40, 0x3B, 0x3A, 0x0C, 0x62, 0x98, 0x24, 0x37, 0x60, 0x83, 0xCE, 0x61, 0x9D, 0x74, 0xA3, 0x5B, + 0x22, 0x23, 0x40, 0xB8, 0x55, 0x75, 0x61, 0xB8, 0xE4, 0x11, 0x04, 0x45, 0x83, 0x6C, 0xCE, 0x58, + 0x61, 0x42, 0x2C, 0x5C, 0x20, 0x89, 0x00, 0x22, 0x9A, 0x64, 0x45, 0x74, 0xDC, 0x60, 0x20, 0xAB, + 0xF8, 0x0B, 0x40, 0xE4, 0x0B, 0x00, 0x0F, 0x20, 0x61, 0x42, 0x2C, 0x5C, 0x20, 0x89, 0x00, 0x22, + 0x82, 0x64, 0x45, 0x74, 0xDC, 0x60, 0x20, 0xAB, 0xF8, 0x0B, 0x60, 0x98, 0x04, 0x43, 0x4E, 0x58, + 0x0B, 0x20, 0x61, 0x42, 0x2C, 0x5C, 0x20, 0x89, 0x00, 0x22, 0x82, 0x64, 0x45, 0x74, 0xDC, 0x60, + 0x20, 0xAB, 0xF8, 0x0B, 0x60, 0x98, 0x01, 0x2B, 0xCD, 0x74, 0x01, 0x25, 0x60, 0xB8, 0x55, 0x75, + 0x61, 0x98, 0x4E, 0x65, 0xD3, 0x0B, 0x62, 0x98, 0x60, 0x83, 0x43, 0x3B, 0xA1, 0x0B, 0x0C, 0x10, + 0x00, 0x36, 0xA0, 0xE4, 0x23, 0x00, 0xE9, 0x10, 0xA0, 0xB8, 0x2A, 0x10, 0x12, 0x32, 0xA6, 0xE4, + 0x0F, 0x10, 0x13, 0x04, 0x3C, 0x0F, 0x00, 0x30, 0xBC, 0x13, 0x00, 0x30, 0x8C, 0x21, 0x00, 0x30, + 0xC0, 0x2B, 0x00, 0x30, 0x88, 0x21, 0x00, 0x30, 0x08, 0x1D, 0x00, 0x30, 0xE0, 0x34, 0x00, 0x00, + 0xE1, 0x34, 0x00, 0x00, 0x40, 0x81, 0x00, 0x21, 0x61, 0x59, 0x64, 0x43, 0xD4, 0x60, 0x61, 0x43, + 0xDC, 0x60, 0x60, 0x8B, 0xCD, 0x74, 0x44, 0x42, 0x98, 0x60, 0x41, 0x42, 0x90, 0x60, 0x60, 0xAA, + 0x60, 0x98, 0x4E, 0x64, 0xF0, 0x0B, 0x00, 0x26, 0x50, 0x3E, 0xD8, 0x0B, 0x00, 0x32, 0x00, 0xEA, + 0x40, 0x02, 0x61, 0x42, 0x2C, 0x5C, 0x20, 0x89, 0x00, 0x22, 0x0A, 0x64, 0x45, 0x74, 0xDC, 0x60, + 0x20, 0xAB, 0xF8, 0x0B, 0xCE, 0xE5, 0x8B, 0x04, 0x94, 0x14, 0x77, 0x13, 0x00, 0x31, 0x62, 0xB8, + 0x0F, 0x6C, 0xF6, 0x13, 0x60, 0x80, 0x40, 0x80, 0x44, 0x42, 0x84, 0x60, 0x41, 0x42, 0x90, 0x60, + 0x40, 0x8A, 0x64, 0x43, 0xC4, 0x60, 0x00, 0x21, 0x44, 0x39, 0x89, 0x74, 0x61, 0x43, 0xDC, 0x60, + 0x40, 0xAB, 0xF1, 0x0B, 0x0C, 0x30, 0xA2, 0x98, 0x60, 0x85, 0x40, 0x85, 0x44, 0x42, 0x80, 0x60, + 0x41, 0x42, 0x90, 0x60, 0x40, 0x8A, 0x64, 0x43, 0xC0, 0x60, 0x00, 0x20, 0x50, 0x38, 0x89, 0x74, + 0x61, 0x43, 0xDC, 0x60, 0x40, 0xAB, 0xF1, 0x0B, 0x23, 0x30, 0xA2, 0x98, 0x60, 0x85, 0x40, 0x85, + 0x49, 0x58, 0x44, 0x42, 0x84, 0x60, 0x41, 0x42, 0x90, 0x60, 0x40, 0x8A, 0x6D, 0x58, 0x64, 0x43, + 0xC4, 0x60, 0x00, 0x21, 0x4C, 0x39, 0x89, 0x74, 0x61, 0x43, 0xDC, 0x60, 0x40, 0xAB, 0xEF, 0x0B, + 0x62, 0x98, 0x40, 0x83, 0x24, 0x33, 0xCA, 0x64, 0x23, 0x0B, 0x19, 0x12, 0x00, 0x36, 0xA0, 0xE4, + 0x23, 0x00, 0xA0, 0xB8, 0x37, 0x12, 0x12, 0x32, 0xA6, 0xE4, 0x0F, 0x10, 0x03, 0x04, 0x40, 0x81, + 0x00, 0x21, 0x61, 0x59, 0x64, 0x43, 0xD4, 0x60, 0x61, 0x43, 0xDC, 0x60, 0x60, 0x8B, 0xCD, 0x74, + 0x44, 0x42, 0x98, 0x60, 0x41, 0x42, 0x90, 0x60, 0x60, 0xAA, 0x60, 0x98, 0x4E, 0x64, 0xF0, 0x0B, + 0x00, 0x26, 0x50, 0x3E, 0xE8, 0x0B, 0x00, 0x32, 0x00, 0xEA, 0x40, 0x02, 0x61, 0x42, 0x2C, 0x5C, + 0x20, 0x89, 0x00, 0x22, 0x0A, 0x64, 0x45, 0x74, 0xDC, 0x60, 0x20, 0xAB, 0xF8, 0x0B, 0xCE, 0xE5, + 0x8B, 0x04, 0x94, 0x14, 0x04, 0x12, 0xFF, 0xE3, 0x67, 0xFD, 0xF2, 0x06, 0xE3, 0x12, 0x07, 0xE4, + 0x2B, 0x0A, 0xFF, 0xE3, 0x61, 0xFD, 0x68, 0x87, 0x41, 0x3B, 0xEA, 0x0A, 0x67, 0xD8, 0xAC, 0x0E, + 0xCC, 0x74, 0x04, 0x3B, 0x0C, 0x08, 0x67, 0xD8, 0xAC, 0x0E, 0x44, 0x3B, 0x5C, 0x0C, 0x67, 0xD8, + 0xAC, 0x0E, 0x00, 0x23, 0xCC, 0x74, 0x67, 0xDC, 0xAC, 0x0E, 0xDA, 0x06, 0x58, 0x11, 0x03, 0x1E, + 0x62, 0xD8, 0x98, 0x11, 0x00, 0x23, 0xCD, 0x74, 0x62, 0xDC, 0x98, 0x11, 0xA7, 0xE4, 0xAF, 0x0E, + 0x1B, 0x6C, 0x00, 0x32, 0x07, 0xEA, 0x40, 0x02, 0x61, 0x42, 0x00, 0x22, 0x2C, 0x5C, 0xCA, 0x65, + 0xD4, 0x60, 0x20, 0x89, 0x60, 0x8B, 0x4E, 0x60, 0x20, 0xA8, 0x01, 0x20, 0xF6, 0x0B, 0x00, 0x30, + 0xAC, 0x11, 0x20, 0x34, 0xC0, 0xB8, 0x60, 0x98, 0x8F, 0x6C, 0xE3, 0xE4, 0x7F, 0x04, 0x25, 0x11, + 0x12, 0x33, 0x03, 0x04, 0x60, 0x81, 0x00, 0x21, 0xC0, 0x8A, 0x90, 0x60, 0xCA, 0x65, 0x64, 0x43, + 0xC0, 0x60, 0x61, 0x43, 0xD4, 0x60, 0xC0, 0xAB, 0xF6, 0x0B, 0x00, 0x20, 0x50, 0x38, 0x60, 0x98, + 0x01, 0x23, 0x60, 0xB8, 0xE9, 0x0B, 0xFF, 0xE3, 0x0B, 0xFB, 0xFF, 0x10, 0x1D, 0x10, 0x67, 0xE4, + 0x7F, 0x04, 0x00, 0x32, 0x60, 0xB8, 0x61, 0x42, 0xCC, 0x5F, 0x20, 0x8E, 0x80, 0x88, 0x47, 0x74, + 0x53, 0x75, 0x45, 0x65, 0x02, 0x0C, 0x80, 0xAE, 0x20, 0x98, 0xC4, 0x60, 0x20, 0x8B, 0x47, 0x74, + 0x55, 0x64, 0x02, 0x0C, 0x80, 0xAB, 0x03, 0xEA, 0x40, 0x02, 0x00, 0x22, 0xCA, 0x64, 0x01, 0x20, + 0xEB, 0x0B, 0x86, 0x06, 0x00, 0x32, 0x00, 0xEA, 0x40, 0x02, 0xA7, 0xE4, 0xAF, 0x0E, 0x61, 0x42, + 0x2C, 0x5C, 0x20, 0x89, 0x00, 0x22, 0x0A, 0x64, 0x45, 0x74, 0xD4, 0x60, 0x20, 0xAB, 0xF8, 0x0B, + 0x97, 0x07, 0x00, 0x00, 0x88, 0x21, 0x00, 0x30, 0x08, 0x1D, 0x00, 0x30, 0xE0, 0x34, 0x00, 0x00, + 0xE1, 0x34, 0x00, 0x00, 0xC0, 0x2B, 0x00, 0x30, 0x94, 0x21, 0x00, 0x30, 0x94, 0x31, 0x00, 0x30, + 0xBC, 0x13, 0x00, 0x30, 0xC8, 0x34, 0x00, 0x30, 0xD1, 0x14, 0x3A, 0x16, 0x03, 0x6D, 0x02, 0xEA, + 0x68, 0x01, 0x00, 0x31, 0x3B, 0x6C, 0xFF, 0xE3, 0xC3, 0xF9, 0x90, 0x74, 0xFB, 0x6C, 0x5A, 0x19, + 0x40, 0xA3, 0x00, 0x23, 0x4E, 0x64, 0xFD, 0x0B, 0x5F, 0x12, 0x67, 0x92, 0x70, 0x43, 0x7F, 0x4B, + 0x41, 0x3B, 0xFC, 0x0F, 0x65, 0x92, 0xB7, 0x3B, 0x65, 0xB2, 0x7C, 0x12, 0x5C, 0x12, 0x00, 0x31, + 0x40, 0xB3, 0x26, 0x32, 0x02, 0x30, 0x40, 0xB3, 0x5A, 0x12, 0x20, 0xA2, 0x10, 0x32, 0x02, 0x31, + 0x40, 0xB3, 0x59, 0x12, 0x40, 0xB3, 0xBB, 0x6C, 0xFF, 0xE3, 0xA0, 0xFB, 0x1A, 0x16, 0x91, 0x14, + 0xD0, 0x14, 0x76, 0x12, 0x21, 0xEA, 0x3F, 0x00, 0x47, 0x93, 0x42, 0xE4, 0x00, 0x33, 0x42, 0xEC, + 0x00, 0x01, 0x47, 0xB3, 0x12, 0x30, 0x47, 0x93, 0x42, 0xE4, 0x00, 0x3C, 0x42, 0xEC, 0x00, 0x04, + 0x47, 0xB3, 0x4C, 0x93, 0x42, 0xEC, 0x00, 0x04, 0x4C, 0xB3, 0x4C, 0x93, 0x42, 0xEC, 0x00, 0x08, + 0x4C, 0xB3, 0x4C, 0x93, 0x42, 0xEC, 0x00, 0x40, 0x4C, 0xB3, 0x4C, 0x93, 0xB6, 0x3A, 0x4C, 0xB3, + 0x4C, 0x93, 0x84, 0x6C, 0x4C, 0xB3, 0x4C, 0x93, 0xB7, 0x3A, 0x4C, 0xB3, 0x00, 0xE0, 0xC4, 0x05, + 0x13, 0x30, 0x00, 0xE0, 0xC1, 0x05, 0x90, 0x14, 0xD2, 0x14, 0x3A, 0x16, 0x3B, 0x6C, 0x02, 0xEA, + 0x68, 0x01, 0x00, 0x31, 0x9E, 0x11, 0xFF, 0xE3, 0x6B, 0xF9, 0x60, 0x94, 0x42, 0x3B, 0x5A, 0x0D, + 0x02, 0x3B, 0x3C, 0x09, 0x41, 0x3B, 0x38, 0x09, 0xBA, 0x11, 0x45, 0xE4, 0x8F, 0x03, 0x32, 0x11, + 0x67, 0x91, 0x70, 0x43, 0x7F, 0x4B, 0x41, 0x3B, 0xFC, 0x0F, 0x65, 0x91, 0xB7, 0x3B, 0x65, 0xB1, + 0x6E, 0x11, 0x2F, 0x11, 0x00, 0x30, 0x20, 0xB3, 0x26, 0x31, 0x20, 0xB3, 0x2D, 0x11, 0x00, 0xA1, + 0x10, 0x31, 0x02, 0x30, 0x20, 0xB3, 0x2C, 0x11, 0x20, 0xB3, 0x00, 0x31, 0xFF, 0xE3, 0x46, 0xFB, + 0x97, 0x6C, 0x01, 0x31, 0x02, 0x30, 0xFF, 0xE3, 0x41, 0xFB, 0x60, 0x94, 0x45, 0x3B, 0x2C, 0x0D, + 0x60, 0x94, 0x44, 0x3B, 0x29, 0x0D, 0x40, 0x11, 0x67, 0x92, 0x70, 0x43, 0x7F, 0x4B, 0x41, 0x3B, + 0xFC, 0x0F, 0x65, 0x92, 0xB7, 0x3B, 0x65, 0xB2, 0x26, 0x31, 0x5C, 0x10, 0x7C, 0x10, 0x01, 0x30, + 0x60, 0xB2, 0x20, 0xB2, 0x20, 0x11, 0x00, 0xB1, 0x10, 0x31, 0x00, 0x30, 0x20, 0xB2, 0x3A, 0x10, + 0x20, 0xB2, 0x60, 0xB2, 0x27, 0x33, 0x60, 0xB2, 0x7C, 0x10, 0x10, 0xA3, 0x01, 0x30, 0x11, 0xA3, + 0x02, 0x30, 0x12, 0xA3, 0x06, 0x30, 0x13, 0xA3, 0x03, 0x30, 0x14, 0xA3, 0x07, 0x30, 0x15, 0xA3, + 0x04, 0x30, 0x16, 0xA3, 0x08, 0x30, 0x17, 0xA3, 0x05, 0x30, 0x18, 0xA3, 0x09, 0x30, 0x19, 0xA3, + 0x10, 0x33, 0x60, 0xB2, 0x20, 0xB2, 0x48, 0x10, 0x67, 0x92, 0x70, 0x43, 0x7F, 0x4B, 0x41, 0x3B, + 0xFC, 0x0F, 0x65, 0x92, 0xB7, 0x3B, 0x65, 0xB2, 0x26, 0x31, 0x44, 0x10, 0x64, 0x10, 0x02, 0x30, + 0x60, 0xB2, 0x15, 0x04, 0x80, 0x00, 0x00, 0x40, 0x00, 0xFF, 0x00, 0x50, 0x55, 0xAA, 0x66, 0x00, + 0xA0, 0x45, 0x00, 0x50, 0x66, 0x99, 0x55, 0x00, 0x00, 0x01, 0x00, 0x40, 0x8C, 0x21, 0x00, 0x30, + 0x08, 0x00, 0x00, 0x30, 0x00, 0x19, 0x00, 0x50, 0xA0, 0x02, 0x00, 0x50, 0x20, 0xB2, 0x33, 0x12, + 0x00, 0xB1, 0x10, 0x31, 0x0A, 0x30, 0x20, 0xB2, 0x31, 0x12, 0x20, 0xB2, 0x60, 0xB2, 0x27, 0x33, + 0x60, 0xB2, 0x70, 0x12, 0x10, 0xA3, 0x0B, 0x30, 0x11, 0xA3, 0x13, 0x30, 0x12, 0xA3, 0x0F, 0x30, + 0x13, 0xA3, 0x12, 0x30, 0x14, 0xA3, 0x0E, 0x30, 0x15, 0xA3, 0x11, 0x30, 0x16, 0xA3, 0x0D, 0x30, + 0x17, 0xA3, 0x10, 0x30, 0x18, 0xA3, 0x0C, 0x30, 0x19, 0xA3, 0x67, 0x12, 0x08, 0x30, 0x1E, 0xA3, + 0x04, 0x30, 0x1F, 0xA3, 0x48, 0x30, 0x63, 0xE4, 0xFF, 0x06, 0x12, 0xA3, 0x16, 0x30, 0x13, 0xA3, + 0x08, 0x30, 0x03, 0xDC, 0x16, 0x06, 0x12, 0x30, 0x03, 0xDC, 0x17, 0x06, 0x00, 0x30, 0x37, 0x28, + 0x03, 0xDC, 0x1A, 0x0C, 0x0D, 0x30, 0x63, 0xE4, 0xFF, 0x05, 0x03, 0xDC, 0x1B, 0x06, 0x00, 0x30, + 0x77, 0x28, 0x03, 0xDC, 0x1E, 0x0C, 0x09, 0x30, 0x03, 0xDC, 0x1F, 0x0C, 0x10, 0x33, 0x60, 0xB2, + 0x20, 0xB2, 0x56, 0x11, 0x67, 0x92, 0x70, 0x43, 0x7F, 0x4B, 0x41, 0x3B, 0xFC, 0x0F, 0x65, 0x92, + 0xB7, 0x3B, 0x65, 0xB2, 0x72, 0x11, 0x53, 0x11, 0x00, 0x31, 0x40, 0xB3, 0x26, 0x32, 0x40, 0xB3, + 0x51, 0x11, 0x21, 0xA2, 0x24, 0x31, 0x20, 0xA2, 0x03, 0x31, 0x21, 0xA2, 0x10, 0x32, 0x40, 0xB3, + 0x47, 0x11, 0x40, 0xB3, 0x6D, 0x11, 0x40, 0x93, 0x40, 0x3A, 0xFE, 0x0F, 0x00, 0x32, 0x40, 0xB3, + 0x40, 0x93, 0x40, 0x3A, 0xFE, 0x0F, 0x00, 0x32, 0x40, 0xB3, 0x44, 0x11, 0x67, 0x92, 0x70, 0x43, + 0x7F, 0x4B, 0x41, 0x3B, 0xFC, 0x0F, 0x65, 0x92, 0xB7, 0x3B, 0x65, 0xB2, 0x26, 0x31, 0x60, 0x11, + 0x40, 0x11, 0x03, 0x30, 0x40, 0xB3, 0x20, 0xB3, 0x38, 0x10, 0x00, 0xB1, 0x10, 0x31, 0x20, 0xB3, + 0x37, 0x10, 0x20, 0xB3, 0x40, 0xB3, 0x27, 0x32, 0x40, 0xB3, 0x60, 0x94, 0x41, 0x3B, 0x5E, 0x0C, + 0x60, 0x94, 0x42, 0x3B, 0x5F, 0x0C, 0x76, 0x10, 0x10, 0x32, 0x40, 0xB3, 0x50, 0x10, 0x40, 0xB3, + 0x52, 0x10, 0x67, 0x92, 0x70, 0x43, 0x7F, 0x4B, 0x41, 0x3B, 0xFC, 0x0F, 0x65, 0x92, 0xB7, 0x3B, + 0x65, 0xB2, 0x6F, 0x10, 0x4F, 0x10, 0x40, 0xB3, 0x26, 0x32, 0x40, 0xB3, 0x60, 0x94, 0x44, 0x3B, + 0x34, 0x0C, 0x60, 0x94, 0x45, 0x3B, 0x31, 0x0C, 0x6B, 0x10, 0x00, 0x32, 0x41, 0xA3, 0x24, 0x32, + 0x40, 0xA3, 0x03, 0x32, 0x41, 0xA3, 0x13, 0x04, 0x00, 0x19, 0x00, 0x50, 0x66, 0x99, 0x55, 0x00, + 0xA0, 0x02, 0x00, 0x50, 0xA0, 0x49, 0x00, 0x50, 0x80, 0x00, 0x00, 0x40, 0x00, 0xFF, 0x00, 0x50, + 0x55, 0xAA, 0x66, 0x00, 0xA0, 0x55, 0x00, 0x50, 0x8C, 0x52, 0x00, 0x30, 0x78, 0x10, 0x10, 0x32, + 0x40, 0xB3, 0x58, 0x10, 0x40, 0xB3, 0x1A, 0x16, 0x92, 0x14, 0x03, 0x2B, 0x01, 0x3B, 0xFC, 0x0B, + 0xFB, 0x6C, 0x00, 0x32, 0x5A, 0x19, 0x7F, 0x2A, 0x40, 0xA3, 0x00, 0x23, 0x4E, 0x64, 0xFD, 0x0B, + 0xB1, 0x10, 0x45, 0xE4, 0x8F, 0x03, 0xBC, 0x06, 0x50, 0x10, 0x00, 0x33, 0x5F, 0x2B, 0x60, 0xA2, + 0x0A, 0x33, 0x61, 0xA2, 0xCA, 0x07, 0xBB, 0x6C, 0x02, 0x31, 0x02, 0x30, 0xFF, 0xE3, 0x0E, 0xFA, + 0xD3, 0x06, 0xAB, 0x10, 0x45, 0xE4, 0x8F, 0x03, 0xAB, 0x06, 0x6A, 0x10, 0x01, 0x32, 0x56, 0xA3, + 0xA3, 0x07, 0x68, 0x10, 0x02, 0x32, 0x56, 0xA3, 0x9F, 0x07, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x50, + 0x66, 0x99, 0x55, 0x00, 0x18, 0x05, 0x00, 0x30, 0xA0, 0x54, 0x00, 0x50, 0x28, 0x0A, 0x00, 0x30, + 0xA0, 0x0D, 0x00, 0x50, 0xC3, 0x14, 0x01, 0x34, 0x5B, 0x10, 0x04, 0xC4, 0x20, 0x40, 0x03, 0x74, + 0x60, 0x92, 0x00, 0xC4, 0x84, 0x24, 0xD0, 0x68, 0x72, 0x43, 0xB2, 0x4B, 0x60, 0x92, 0x6E, 0x4B, + 0x6E, 0x43, 0xD4, 0x6C, 0x60, 0xB2, 0x25, 0xEA, 0xFF, 0x3F, 0x60, 0x92, 0x62, 0x43, 0x72, 0x4B, + 0xC0, 0x6C, 0xC0, 0x92, 0x72, 0x43, 0x62, 0x4B, 0xA6, 0xC4, 0x45, 0x20, 0x40, 0x39, 0xD4, 0x6C, + 0x60, 0xB2, 0x0E, 0x0C, 0x41, 0x39, 0x02, 0x0C, 0x83, 0x14, 0x66, 0x92, 0x0C, 0x6C, 0x66, 0x92, + 0x12, 0x40, 0x6E, 0x4B, 0x12, 0x48, 0x6E, 0x43, 0xC0, 0x6C, 0x66, 0xB2, 0x83, 0x14, 0x66, 0x92, + 0x0C, 0x69, 0x92, 0x44, 0x72, 0x4C, 0x86, 0x92, 0x8E, 0x4C, 0x8E, 0x44, 0x0C, 0x6D, 0x86, 0xB2, + 0x83, 0x14, 0x00, 0x00, 0x80, 0x00, 0x00, 0x40, 0x25, 0x11, 0x46, 0x91, 0x62, 0x42, 0x52, 0x4B, + 0x01, 0x33, 0xC0, 0x70, 0xC8, 0x6C, 0x06, 0x91, 0x72, 0x43, 0x22, 0xEA, 0xFF, 0x3F, 0x40, 0xC4, + 0x42, 0x20, 0x62, 0x4B, 0xC8, 0x6C, 0x66, 0xB1, 0x3C, 0x78, 0x00, 0x00, 0x5C, 0x10, 0x00, 0x31, + 0x66, 0x92, 0x62, 0x43, 0x01, 0x29, 0x43, 0x70, 0x12, 0x4B, 0x04, 0x68, 0x23, 0xEA, 0xFF, 0x3F, + 0x26, 0x92, 0x61, 0xC4, 0x43, 0x20, 0x10, 0x40, 0x0C, 0x6C, 0x06, 0xB2, 0x3C, 0x78, 0x00, 0x00, + 0x73, 0x10, 0x66, 0x93, 0x62, 0x43, 0x72, 0x4B, 0x03, 0xC4, 0x80, 0x40, 0x00, 0xE4, 0x01, 0x20, + 0x3C, 0x78, 0x00, 0x00, 0xD2, 0x14, 0x8E, 0x10, 0x25, 0xEA, 0xFF, 0x3F, 0x66, 0x94, 0x62, 0x43, + 0x72, 0x4B, 0x46, 0x94, 0x63, 0xE4, 0x80, 0x30, 0x95, 0x68, 0x70, 0x43, 0xC8, 0x6C, 0x66, 0xB4, + 0xFF, 0xE3, 0x34, 0xF8, 0x66, 0x94, 0x62, 0x43, 0x72, 0x4B, 0x46, 0x94, 0x63, 0xEC, 0x80, 0x00, + 0xA2, 0xC4, 0x45, 0x20, 0x70, 0x43, 0xD4, 0x6C, 0x66, 0xB4, 0x92, 0x14, 0x80, 0x00, 0x00, 0x40, + 0xD0, 0x14, 0x49, 0x10, 0x05, 0x30, 0x63, 0x92, 0x98, 0x3B, 0x63, 0xB2, 0x67, 0x10, 0x42, 0x93, + 0x42, 0xEC, 0x04, 0x00, 0x42, 0xB3, 0x42, 0x93, 0x42, 0xEC, 0x40, 0x00, 0x42, 0xB3, 0x00, 0xE0, + 0xAB, 0x03, 0x90, 0x14, 0x80, 0x00, 0x00, 0x40, 0xC0, 0x00, 0x00, 0x40, 0x62, 0x10, 0x03, 0xDC, + 0x32, 0x10, 0x3C, 0x78, 0x00, 0x01, 0x00, 0x40, 0x6A, 0x10, 0x58, 0x93, 0x42, 0xEC, 0x01, 0x00, + 0x58, 0xB3, 0x58, 0x93, 0x42, 0xE4, 0x01, 0x30, 0x58, 0xB3, 0x3C, 0x78, 0x65, 0x10, 0x58, 0x93, + 0x42, 0xEC, 0x80, 0x00, 0x58, 0xB3, 0x58, 0x93, 0x42, 0xE4, 0x80, 0x30, 0x58, 0xB3, 0x3C, 0x78, + 0x00, 0x01, 0x00, 0x40, 0xD1, 0x14, 0x02, 0x38, 0x11, 0x08, 0x01, 0x40, 0x69, 0x10, 0x0C, 0x60, + 0x40, 0x88, 0x89, 0x10, 0x29, 0x10, 0x0A, 0x10, 0x63, 0x94, 0xB1, 0x3B, 0x63, 0xB4, 0xFF, 0xE3, + 0x61, 0xF7, 0x63, 0x94, 0x91, 0x3B, 0x63, 0xB4, 0x91, 0x14, 0x00, 0x32, 0xF3, 0x07, 0x00, 0x00, + 0x04, 0x35, 0x00, 0x00, 0x40, 0x02, 0x00, 0x40, 0x3C, 0x18, 0x00, 0x30, 0x00, 0x08, 0x01, 0x30, + 0xD1, 0x14, 0x41, 0x38, 0x2E, 0x0C, 0x40, 0x38, 0x2B, 0x08, 0x73, 0x13, 0x08, 0x31, 0x41, 0x93, + 0x42, 0xEC, 0x03, 0x00, 0x41, 0xB3, 0x41, 0x93, 0x42, 0xE4, 0x80, 0x30, 0x41, 0xB3, 0x41, 0x93, + 0x42, 0xEC, 0x04, 0x00, 0x41, 0xB3, 0x41, 0x93, 0x84, 0x6C, 0x41, 0xB3, 0x48, 0x93, 0x42, 0xE4, + 0x01, 0x30, 0x48, 0xB3, 0x48, 0x93, 0x42, 0xE4, 0x02, 0x30, 0x48, 0xB3, 0x48, 0x93, 0x42, 0xE4, + 0x10, 0x30, 0x48, 0xB3, 0x47, 0x93, 0x42, 0xE4, 0xF0, 0x30, 0x42, 0xEC, 0x10, 0x00, 0x47, 0xB3, + 0x47, 0x93, 0x48, 0x6C, 0x27, 0xB3, 0x41, 0x93, 0x42, 0xEC, 0x40, 0x00, 0x41, 0xB3, 0x91, 0x14, + 0x9E, 0x12, 0x04, 0x31, 0x61, 0x94, 0xC4, 0x6C, 0x61, 0xB4, 0x61, 0x94, 0x63, 0xEC, 0x08, 0x00, + 0x61, 0xB4, 0x63, 0x94, 0x63, 0xE4, 0x10, 0x30, 0x63, 0xB4, 0x43, 0x94, 0xB5, 0x3A, 0x43, 0xB4, + 0x63, 0x94, 0xC4, 0x6C, 0x63, 0xB4, 0x63, 0x94, 0x63, 0xE4, 0x20, 0x30, 0x63, 0xB4, 0x63, 0x94, + 0x63, 0xEC, 0x00, 0x04, 0x63, 0xB4, 0x63, 0x94, 0x92, 0x3B, 0x63, 0xB4, 0x63, 0x94, 0x93, 0x3B, + 0x63, 0xB4, 0x63, 0x94, 0xB4, 0x3B, 0x63, 0xB4, 0x66, 0x94, 0x63, 0xE4, 0x0F, 0x30, 0x63, 0xEC, + 0x09, 0x00, 0x66, 0xB4, 0x67, 0x94, 0xC0, 0x6C, 0x67, 0xB4, 0x67, 0x94, 0x63, 0xEC, 0x02, 0x00, + 0x67, 0xB4, 0x69, 0x94, 0x0C, 0x6C, 0x09, 0xB4, 0x04, 0x30, 0x69, 0x94, 0x63, 0xE4, 0x02, 0x30, + 0x69, 0xB4, 0x69, 0x94, 0x63, 0xE4, 0x04, 0x30, 0x69, 0xB4, 0x69, 0x94, 0x63, 0xE4, 0xF0, 0x30, + 0x63, 0xEC, 0x10, 0x00, 0x69, 0xB4, 0x7E, 0x11, 0x49, 0x94, 0x62, 0xC4, 0x43, 0x20, 0x63, 0xEC, + 0x00, 0x01, 0x69, 0xB4, 0x69, 0x94, 0xB8, 0x3B, 0x69, 0xB4, 0x69, 0x94, 0xB9, 0x3B, 0x69, 0xB4, + 0x69, 0x94, 0x63, 0xE4, 0x08, 0x30, 0x69, 0xB4, 0x00, 0xE0, 0xE6, 0x02, 0x61, 0x94, 0x63, 0xEC, + 0x40, 0x00, 0x61, 0xB4, 0x91, 0x14, 0x00, 0x00, 0x4F, 0x11, 0x02, 0xB2, 0x60, 0x92, 0x78, 0x43, + 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, 0x02, 0x92, 0x3C, 0x78, 0x00, 0x00, 0x4A, 0x11, 0x00, 0xE4, + 0x01, 0x20, 0x63, 0x92, 0x63, 0xE4, 0x01, 0x30, 0xC0, 0x6C, 0x63, 0xB2, 0x3C, 0x78, 0x00, 0x00, + 0xD1, 0x14, 0x85, 0x11, 0x0A, 0x30, 0x63, 0x94, 0x63, 0xE4, 0x01, 0x30, 0x63, 0xB4, 0xFF, 0xE3, + 0x2D, 0xF7, 0x05, 0x33, 0x93, 0x6C, 0x62, 0xB4, 0x60, 0x92, 0x78, 0x43, 0xE3, 0xC7, 0x80, 0x28, + 0xFC, 0x0F, 0x62, 0x92, 0x9C, 0x10, 0x01, 0x32, 0xFF, 0x33, 0x62, 0xB4, 0x60, 0x94, 0x78, 0x43, + 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, 0x62, 0x94, 0xC8, 0x68, 0xCC, 0x74, 0x40, 0x3B, 0xF5, 0x0B, + 0x0A, 0x30, 0xFF, 0xE3, 0x13, 0xF7, 0x63, 0x94, 0x63, 0xEC, 0x01, 0x00, 0x63, 0xB4, 0x91, 0x14, + 0xD1, 0x14, 0x91, 0x10, 0x0A, 0x30, 0x63, 0x94, 0x63, 0xE4, 0x01, 0x30, 0x63, 0xB4, 0xFF, 0xE3, + 0x05, 0xF7, 0x06, 0x33, 0x62, 0xB4, 0x60, 0x94, 0x78, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, + 0x0A, 0x30, 0x62, 0x94, 0xFF, 0xE3, 0xFA, 0xF6, 0x63, 0x94, 0x63, 0xEC, 0x01, 0x00, 0x63, 0xB4, + 0x91, 0x14, 0x00, 0x00, 0xD2, 0x14, 0x03, 0x6D, 0xA3, 0x10, 0xFF, 0xE3, 0xE3, 0xFF, 0xFF, 0xE3, + 0xB9, 0xFF, 0x07, 0x04, 0xC0, 0x01, 0x00, 0x40, 0x40, 0x02, 0x00, 0x40, 0x00, 0xFF, 0xFF, 0x00, + 0x63, 0x95, 0x63, 0xE4, 0x01, 0x30, 0x63, 0xB5, 0x0A, 0x30, 0xFF, 0xE3, 0xDF, 0xF6, 0x20, 0x33, + 0x97, 0x6C, 0x62, 0xB5, 0x60, 0x92, 0x78, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, 0x62, 0x92, + 0x04, 0xC4, 0x43, 0x70, 0x62, 0xB2, 0x57, 0x13, 0x60, 0x92, 0x78, 0x43, 0xE3, 0xC7, 0x80, 0x28, + 0xFC, 0x0F, 0x62, 0x92, 0x04, 0xC4, 0x83, 0x70, 0x62, 0xB2, 0x52, 0x13, 0x60, 0x92, 0x78, 0x43, + 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, 0x10, 0x75, 0x62, 0x92, 0x82, 0xB2, 0x8D, 0x13, 0x60, 0x94, + 0x78, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, 0x0A, 0x30, 0x62, 0x94, 0xFF, 0xE3, 0xB6, 0xF6, + 0x63, 0x94, 0x63, 0xEC, 0x01, 0x00, 0x63, 0xB4, 0xFF, 0xE3, 0x7C, 0xFF, 0x92, 0x14, 0x00, 0x00, + 0xD2, 0x14, 0x03, 0x6D, 0xA3, 0x13, 0xFF, 0xE3, 0x9D, 0xFF, 0xFF, 0xE3, 0x73, 0xFF, 0x63, 0x95, + 0x63, 0xE4, 0x01, 0x30, 0x63, 0xB5, 0x0A, 0x30, 0xFF, 0xE3, 0xA0, 0xF6, 0xD8, 0x33, 0x97, 0x6C, + 0x62, 0xB5, 0x60, 0x92, 0x78, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, 0x62, 0x92, 0x04, 0xC4, + 0x43, 0x70, 0x62, 0xB2, 0x57, 0x12, 0x60, 0x92, 0x78, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, + 0x62, 0x92, 0x04, 0xC4, 0x83, 0x70, 0x62, 0xB2, 0x52, 0x12, 0x60, 0x92, 0x78, 0x43, 0xE3, 0xC7, + 0x80, 0x28, 0xFC, 0x0F, 0x10, 0x75, 0x62, 0x92, 0x82, 0xB2, 0x8E, 0x12, 0x60, 0x94, 0x78, 0x43, + 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, 0x0A, 0x30, 0x62, 0x94, 0xFF, 0xE3, 0x77, 0xF6, 0x63, 0x94, + 0x63, 0xEC, 0x01, 0x00, 0x63, 0xB4, 0xFF, 0xE3, 0x3D, 0xFF, 0x92, 0x14, 0xD4, 0x14, 0x4B, 0x6D, + 0xE4, 0x12, 0x83, 0x6D, 0x07, 0x6D, 0xFF, 0xE3, 0x5D, 0xFF, 0xFF, 0xE3, 0x33, 0xFF, 0x63, 0x97, + 0x63, 0xE4, 0x01, 0x30, 0x63, 0xB7, 0x0A, 0x30, 0xFF, 0xE3, 0x60, 0xF6, 0x02, 0x33, 0x9F, 0x6C, + 0x62, 0xB7, 0x60, 0x92, 0x78, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, 0x62, 0x92, 0x06, 0xC4, + 0x43, 0x70, 0x62, 0xB2, 0x57, 0x11, 0x60, 0x92, 0x78, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, + 0x62, 0x92, 0x06, 0xC4, 0x83, 0x70, 0x62, 0xB2, 0x52, 0x11, 0x60, 0x92, 0x78, 0x43, 0xE3, 0xC7, + 0x80, 0x28, 0xFC, 0x0F, 0x98, 0x75, 0x62, 0x92, 0xC2, 0xB2, 0x4E, 0x11, 0x60, 0x92, 0x78, 0x43, + 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, 0x40, 0x3D, 0x62, 0x92, 0x0F, 0x0C, 0x53, 0x6C, 0x09, 0x11, + 0x50, 0x61, 0x60, 0x81, 0x62, 0xB0, 0x60, 0x90, 0x78, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, + 0x00, 0x21, 0x46, 0x65, 0x62, 0x90, 0xF6, 0x0B, 0x0A, 0x30, 0xFF, 0xE3, 0x27, 0xF6, 0x41, 0x11, + 0x63, 0x92, 0x63, 0xEC, 0x01, 0x00, 0x63, 0xB2, 0xFF, 0xE3, 0xEC, 0xFE, 0x94, 0x14, 0x00, 0x00, + 0xD4, 0x14, 0x21, 0x14, 0x4B, 0x6D, 0x03, 0xEA, 0x00, 0x01, 0x80, 0x74, 0x89, 0x5B, 0x11, 0x75, + 0x83, 0x6D, 0x50, 0x65, 0xC7, 0x6D, 0x06, 0x0C, 0x97, 0x6C, 0xFF, 0xE3, 0xA1, 0xFF, 0x01, 0x14, + 0x94, 0x14, 0x93, 0x6C, 0x60, 0xB8, 0xFF, 0xE3, 0x9B, 0xFF, 0x52, 0x61, 0x60, 0x98, 0x55, 0x75, + 0xD0, 0x61, 0x4C, 0x65, 0x90, 0x61, 0x17, 0x6D, 0x03, 0x08, 0x80, 0x34, 0x81, 0x44, 0x11, 0x75, + 0x93, 0x6C, 0x5F, 0x6C, 0x1B, 0x6C, 0xFF, 0xE3, 0x8B, 0xFF, 0x16, 0x65, 0xEF, 0x0B, 0x01, 0x14, + 0x94, 0x14, 0x00, 0x00, 0xD4, 0x14, 0xE7, 0x10, 0x03, 0x6D, 0x63, 0x97, 0x63, 0xE4, 0x01, 0x30, + 0x63, 0xB7, 0x0A, 0x30, 0x8B, 0x6D, 0x47, 0x6D, 0xFF, 0xE3, 0xE8, 0xF5, 0x3B, 0x33, 0x03, 0x04, + 0xC0, 0x01, 0x00, 0x40, 0x9F, 0x6C, 0x62, 0xB7, 0x60, 0x92, 0x78, 0x43, 0xE3, 0xC7, 0x80, 0x28, + 0xFC, 0x0F, 0x62, 0x92, 0x04, 0xC4, 0x43, 0x70, 0x62, 0xB2, 0x51, 0x13, 0x60, 0x92, 0x78, 0x43, + 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, 0x62, 0x92, 0x04, 0xC4, 0x83, 0x70, 0x62, 0xB2, 0x4C, 0x13, + 0x60, 0x92, 0x78, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, 0x10, 0x75, 0x62, 0x92, 0x82, 0xB2, + 0x47, 0x13, 0x60, 0x92, 0x78, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, 0x62, 0x92, 0xFF, 0x33, + 0x62, 0xB2, 0x43, 0x13, 0x60, 0x92, 0x78, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, 0x62, 0x92, + 0x68, 0x92, 0x63, 0xEC, 0x01, 0x00, 0x68, 0xB2, 0x40, 0x3E, 0x68, 0x92, 0x63, 0xEC, 0x04, 0x00, + 0x68, 0xB2, 0x68, 0x92, 0x63, 0xEC, 0x08, 0x00, 0x68, 0xB2, 0x68, 0x92, 0x63, 0xE4, 0x10, 0x30, + 0x68, 0xB2, 0x68, 0x92, 0x63, 0xEC, 0x02, 0x00, 0x68, 0xB2, 0x16, 0x0C, 0x17, 0x6D, 0x14, 0x12, + 0xFF, 0x33, 0x62, 0xB0, 0x60, 0x90, 0x78, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, 0x62, 0x90, + 0x58, 0x4B, 0x63, 0xA4, 0x40, 0xA4, 0x50, 0x4B, 0x41, 0xA4, 0x48, 0x4B, 0x42, 0xA4, 0x03, 0x24, + 0x75, 0x5C, 0x8C, 0x65, 0xEE, 0x0F, 0x6A, 0x12, 0x48, 0x93, 0x42, 0xE4, 0x04, 0x30, 0x48, 0xB3, + 0x48, 0x93, 0x42, 0xE4, 0x08, 0x30, 0x48, 0xB3, 0x43, 0x93, 0x42, 0xEC, 0x01, 0x00, 0x43, 0xB3, + 0x94, 0x14, 0x00, 0x00, 0xD2, 0x14, 0x82, 0x12, 0x43, 0x6D, 0x63, 0x94, 0x63, 0xE4, 0x01, 0x30, + 0x63, 0xB4, 0x0A, 0x30, 0xFF, 0xE3, 0x72, 0xF5, 0x9F, 0x33, 0x93, 0x6C, 0x62, 0xB4, 0x60, 0x92, + 0x78, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, 0x17, 0x6C, 0x99, 0x11, 0x62, 0x92, 0x4A, 0x5D, + 0xFF, 0x33, 0x62, 0xB4, 0x60, 0x94, 0x78, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, 0x62, 0x94, + 0x60, 0xA0, 0x00, 0x20, 0x82, 0x64, 0xF5, 0x0B, 0x0A, 0x30, 0xFF, 0xE3, 0x57, 0xF5, 0x63, 0x94, + 0x63, 0xEC, 0x01, 0x00, 0x63, 0xB4, 0x92, 0x14, 0xC4, 0x14, 0x40, 0x39, 0x04, 0x08, 0x00, 0xEA, + 0xFF, 0xFF, 0x84, 0x14, 0x83, 0x6D, 0xE3, 0x59, 0xAA, 0x11, 0xDD, 0x75, 0x00, 0x27, 0xC0, 0x61, + 0x00, 0xEA, 0xFF, 0xFF, 0x20, 0x86, 0x08, 0x33, 0x01, 0x34, 0x20, 0xC4, 0x42, 0x24, 0x90, 0x68, + 0x01, 0x48, 0x40, 0x3A, 0x03, 0x0C, 0x15, 0x6C, 0x01, 0x74, 0x00, 0x2B, 0xCC, 0x74, 0x21, 0x49, + 0x40, 0x3B, 0xF4, 0x0B, 0x00, 0x26, 0x9E, 0x65, 0xEE, 0x0B, 0x84, 0x14, 0xD1, 0x14, 0x9C, 0x10, + 0x0A, 0x30, 0x63, 0x94, 0x63, 0xE4, 0x01, 0x30, 0x63, 0xB4, 0xFF, 0xE3, 0x27, 0xF5, 0xB9, 0x33, + 0x62, 0xB4, 0x60, 0x94, 0x78, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0xFC, 0x0F, 0x0A, 0x30, 0x62, 0x94, + 0xFF, 0xE3, 0x1C, 0xF5, 0x63, 0x94, 0x63, 0xEC, 0x01, 0x00, 0x63, 0xB4, 0x64, 0x30, 0xFF, 0xE3, + 0x15, 0xF5, 0x91, 0x14, 0xD1, 0x14, 0x8E, 0x10, 0x0A, 0x30, 0x63, 0x94, 0x63, 0xE4, 0x01, 0x30, + 0x63, 0xB4, 0xFF, 0xE3, 0x0B, 0xF5, 0xAB, 0x33, 0x62, 0xB4, 0x60, 0x94, 0x78, 0x43, 0xE3, 0xC7, + 0x80, 0x28, 0xFC, 0x0F, 0x0A, 0x30, 0x62, 0x94, 0xFF, 0xE3, 0x00, 0xF5, 0x63, 0x94, 0x63, 0xEC, + 0x01, 0x00, 0x63, 0xB4, 0x64, 0x30, 0xFF, 0xE3, 0xF9, 0xF4, 0x91, 0x14, 0xC0, 0x01, 0x00, 0x40, + 0x08, 0x84, 0xFF, 0xFF, 0xC1, 0x14, 0x6D, 0x10, 0x40, 0x34, 0x21, 0x93, 0x50, 0x6C, 0x21, 0xB3, + 0x00, 0xE4, 0x01, 0x20, 0x41, 0x93, 0x90, 0x6C, 0x41, 0xB3, 0x06, 0x40, 0x42, 0x93, 0x42, 0xE4, + 0x40, 0x30, 0x80, 0x6C, 0x42, 0xB3, 0x41, 0x93, 0x42, 0xE4, 0x40, 0x30, 0x41, 0xB3, 0x41, 0x93, + 0x42, 0xE4, 0x40, 0x30, 0x41, 0xB3, 0x81, 0x14, 0x40, 0x01, 0x00, 0x40, 0xD1, 0x14, 0x22, 0xEA, + 0x00, 0x40, 0x8F, 0x10, 0x70, 0x92, 0x63, 0xEC, 0x00, 0x04, 0x70, 0xB2, 0x00, 0xAC, 0x09, 0x30, + 0x00, 0xE0, 0x62, 0x00, 0x61, 0x94, 0x40, 0x32, 0xC8, 0x6C, 0x61, 0xB4, 0x61, 0x94, 0xC8, 0x6C, + 0x61, 0xB4, 0x62, 0x94, 0xC8, 0x6C, 0x62, 0xB4, 0x61, 0x94, 0x63, 0xE4, 0x40, 0x30, 0x61, 0xB4, + 0x61, 0x94, 0x63, 0xE4, 0x40, 0x30, 0x61, 0xB4, 0x91, 0x14, 0x00, 0x00, 0x40, 0x01, 0x00, 0x40, + 0x66, 0x10, 0x43, 0x93, 0x42, 0xE4, 0x01, 0x30, 0x43, 0xB3, 0x43, 0x93, 0xB0, 0x3A, 0x43, 0xB3, + 0x01, 0x32, 0x63, 0x10, 0x40, 0xB3, 0x3C, 0x78, 0x00, 0x02, 0x00, 0x40, 0x68, 0x4A, 0x00, 0x30, + 0x83, 0xEA, 0x81, 0x00, 0x43, 0x93, 0x90, 0x3A, 0x43, 0xB3, 0x43, 0x93, 0x42, 0xEC, 0x01, 0x00, + 0x43, 0xB3, 0x00, 0x32, 0x7D, 0x13, 0x40, 0xB3, 0x3C, 0x78, 0x00, 0x00, 0x5A, 0x13, 0x00, 0xE4, + 0x01, 0x20, 0x70, 0x40, 0x03, 0x92, 0x90, 0x38, 0x0C, 0x6C, 0x03, 0xB2, 0x3C, 0x78, 0x00, 0x00, + 0xD0, 0x14, 0x75, 0x13, 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0x03, 0x0C, 0xFF, 0xE3, + 0xD1, 0xFF, 0x90, 0x14, 0xD0, 0x14, 0x71, 0x13, 0x60, 0x93, 0x41, 0x3B, 0x02, 0x0C, 0x90, 0x14, + 0xFF, 0xE3, 0xD8, 0xFF, 0x90, 0x14, 0x00, 0x00, 0x4D, 0x13, 0x01, 0x33, 0x21, 0x92, 0x03, 0xC4, + 0x20, 0x40, 0x20, 0xC4, 0x23, 0x24, 0x02, 0x6C, 0x61, 0xB2, 0x61, 0x92, 0x0C, 0x68, 0x01, 0xB2, + 0x3C, 0x78, 0x00, 0x00, 0x47, 0x13, 0x01, 0x33, 0xC0, 0x70, 0x00, 0x92, 0x0C, 0x6C, 0x00, 0xB2, + 0x3C, 0x78, 0x00, 0x00, 0x44, 0x13, 0x01, 0x33, 0xC0, 0x70, 0x00, 0x92, 0x0C, 0x6C, 0x00, 0xB2, + 0x3C, 0x78, 0x00, 0x00, 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x07, 0x30, 0xFF, 0xE3, 0xF8, 0xFB, + 0x07, 0x30, 0xFF, 0xE3, 0xE3, 0xFB, 0x07, 0x30, 0xFF, 0xE3, 0xF2, 0xFB, 0x07, 0x30, 0xFF, 0xE3, + 0xDD, 0xFB, 0x07, 0x30, 0xFF, 0xE3, 0xEC, 0xFB, 0x07, 0x30, 0xFF, 0xE3, 0xD7, 0xFB, 0x07, 0x30, + 0xFF, 0xE3, 0xE6, 0xFB, 0x70, 0x12, 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0x19, 0x08, + 0x6F, 0x12, 0x41, 0x93, 0xB0, 0x3A, 0x41, 0xB3, 0x41, 0x93, 0x90, 0x3A, 0x41, 0xB3, 0x41, 0x93, + 0x42, 0xEC, 0x01, 0x00, 0x41, 0xB3, 0x41, 0x93, 0x42, 0xE4, 0x01, 0x30, 0x41, 0xB3, 0x67, 0x12, + 0x60, 0x93, 0x41, 0x3B, 0x09, 0x0C, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, + 0xFF, 0xE3, 0x70, 0xFF, 0xE6, 0x07, 0xFF, 0xE3, 0x7D, 0xFF, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, + 0x63, 0x14, 0x61, 0x14, 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x7B, 0x11, 0x63, 0x93, 0x6D, 0x43, + 0xE3, 0xC7, 0x80, 0x28, 0x13, 0x08, 0x7A, 0x11, 0x41, 0x93, 0x42, 0xEC, 0x02, 0x00, 0x41, 0xB3, + 0x41, 0x93, 0x42, 0xE4, 0x02, 0x30, 0x41, 0xB3, 0x74, 0x11, 0x60, 0x93, 0x41, 0x3B, 0x09, 0x0C, + 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0xFF, 0xE3, 0x4B, 0xFF, 0xEC, 0x07, + 0xFF, 0xE3, 0x58, 0xFF, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0x00, 0x00, + 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x68, 0x11, 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, + 0x13, 0x08, 0x67, 0x11, 0x41, 0x93, 0x42, 0xEC, 0x04, 0x00, 0x41, 0xB3, 0x41, 0x93, 0x42, 0xE4, + 0x04, 0x30, 0x41, 0xB3, 0x61, 0x11, 0x60, 0x93, 0x41, 0x3B, 0x09, 0x0C, 0xEE, 0xD9, 0x00, 0x20, + 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0xFF, 0xE3, 0x25, 0xFF, 0xEC, 0x07, 0xFF, 0xE3, 0x32, 0xFF, + 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0x00, 0x00, 0x60, 0x14, 0x62, 0x14, + 0xD0, 0x14, 0x75, 0x10, 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0x16, 0x08, 0x74, 0x10, + 0x64, 0x30, 0x41, 0x93, 0x42, 0xEC, 0x08, 0x00, 0x41, 0xB3, 0x41, 0x93, 0x42, 0xE4, 0x08, 0x30, + 0x41, 0xB3, 0xFF, 0xE3, 0x79, 0xFB, 0x6D, 0x10, 0x60, 0x93, 0x41, 0x3B, 0x09, 0x0C, 0xEE, 0xD9, + 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0xFF, 0xE3, 0xFC, 0xFE, 0xE9, 0x07, 0xFF, 0xE3, + 0x09, 0xFF, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0x60, 0x14, 0x62, 0x14, + 0xD2, 0x14, 0x0B, 0x04, 0x00, 0x02, 0x00, 0x40, 0x68, 0x4A, 0x00, 0x30, 0x80, 0x00, 0x00, 0x40, + 0x00, 0xE1, 0x00, 0xE0, 0x80, 0xE1, 0x00, 0xE0, 0x24, 0x14, 0x6A, 0x13, 0x63, 0x93, 0x6D, 0x43, + 0xE3, 0xC7, 0x80, 0x28, 0xFF, 0x08, 0x00, 0x33, 0x27, 0x13, 0x6E, 0xDC, 0x03, 0x00, 0x00, 0x33, + 0x46, 0x13, 0x61, 0xB8, 0x62, 0xB8, 0x63, 0xB8, 0x60, 0x91, 0x61, 0xB8, 0x61, 0x98, 0x6C, 0x4B, + 0x63, 0xE4, 0x0F, 0x20, 0x6E, 0xDC, 0x03, 0x00, 0x61, 0x98, 0x63, 0xE4, 0x01, 0x20, 0x40, 0x3B, + 0x19, 0x0C, 0x01, 0x33, 0x00, 0xEA, 0xFD, 0x07, 0x61, 0xB2, 0x82, 0xE4, 0x0B, 0x00, 0x0D, 0x04, + 0x64, 0x91, 0x62, 0xB8, 0x6E, 0xD8, 0x03, 0x00, 0x03, 0x2B, 0xCC, 0x74, 0x6E, 0xDC, 0x03, 0x00, + 0x64, 0x8A, 0xCD, 0x74, 0x0C, 0x64, 0xAF, 0x0C, 0x6E, 0xD8, 0x03, 0x00, 0xCC, 0x74, 0x03, 0x3B, + 0xF0, 0x0B, 0x61, 0x98, 0x63, 0xE4, 0x10, 0x20, 0x40, 0x3B, 0x45, 0x0C, 0x2E, 0x12, 0x63, 0x91, + 0x6A, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0x4A, 0x0C, 0x63, 0x91, 0xB6, 0x3B, 0x63, 0xB1, 0x66, 0x91, + 0x68, 0x43, 0x70, 0x4B, 0x63, 0xB8, 0x63, 0x98, 0x40, 0x3B, 0xF0, 0x0C, 0x00, 0x33, 0x28, 0x12, + 0x08, 0x12, 0x0C, 0x60, 0x00, 0x90, 0x02, 0xB8, 0x8C, 0x59, 0x02, 0x98, 0x00, 0x74, 0x00, 0xA4, + 0x02, 0x5B, 0x82, 0x98, 0x04, 0xC4, 0x84, 0x70, 0x04, 0x60, 0x80, 0xA0, 0x06, 0x5B, 0x82, 0x98, + 0x04, 0xC4, 0x44, 0x70, 0x04, 0x60, 0x80, 0xA0, 0x0A, 0x5B, 0x82, 0x98, 0x04, 0x60, 0x98, 0x4C, + 0x80, 0xA0, 0x03, 0x23, 0x03, 0x98, 0xCD, 0x74, 0x0C, 0x64, 0xE3, 0x0F, 0x16, 0x11, 0x63, 0x90, + 0x96, 0x3B, 0x63, 0xB0, 0x40, 0x33, 0x00, 0x81, 0xC2, 0x64, 0x97, 0x0C, 0x01, 0x33, 0x32, 0x11, + 0x62, 0xDC, 0x04, 0x22, 0x00, 0x33, 0x64, 0xAA, 0x00, 0x33, 0x61, 0xB2, 0x68, 0x91, 0x63, 0xEC, + 0x04, 0x00, 0x68, 0xB1, 0x60, 0x92, 0x41, 0x3B, 0xB6, 0x0C, 0x04, 0x14, 0xEE, 0xD9, 0x02, 0x20, + 0xA1, 0x98, 0x80, 0x98, 0x03, 0x14, 0x63, 0x14, 0x61, 0x14, 0x64, 0x91, 0x62, 0xB8, 0x6E, 0xD8, + 0x03, 0x00, 0x40, 0x3B, 0x11, 0x0C, 0x64, 0x8A, 0xCD, 0x74, 0x01, 0xEA, 0x00, 0x08, 0x4C, 0x64, + 0x0B, 0x08, 0x64, 0x8A, 0xCD, 0x74, 0x22, 0x5B, 0x45, 0x74, 0x24, 0xAA, 0x00, 0x11, 0x22, 0x98, + 0x44, 0x74, 0xC0, 0x60, 0x20, 0xA3, 0x2E, 0xD8, 0x03, 0x00, 0x01, 0x33, 0x4C, 0x64, 0x12, 0x08, + 0x64, 0x8A, 0xCD, 0x74, 0x01, 0xEA, 0x00, 0x08, 0x4C, 0x64, 0x0C, 0x08, 0x64, 0x8A, 0xCD, 0x74, + 0x22, 0x5B, 0x45, 0x74, 0x24, 0xAA, 0x16, 0x10, 0x22, 0x98, 0x01, 0xC4, 0x81, 0x70, 0xC0, 0x60, + 0x20, 0xA3, 0x2E, 0xD8, 0x03, 0x00, 0x02, 0x33, 0x4C, 0x64, 0x12, 0x08, 0x64, 0x8A, 0xCD, 0x74, + 0x01, 0xEA, 0x00, 0x08, 0x4C, 0x64, 0x0C, 0x08, 0x64, 0x8A, 0xCD, 0x74, 0x22, 0x5B, 0x45, 0x74, + 0x24, 0xAA, 0x0B, 0x10, 0x22, 0x98, 0x01, 0xC4, 0x41, 0x70, 0xC0, 0x60, 0x20, 0xA3, 0x66, 0x10, + 0x01, 0x34, 0x07, 0x93, 0x10, 0x6C, 0x07, 0xB3, 0x28, 0x93, 0x50, 0x6C, 0x28, 0xB3, 0x9F, 0x07, + 0x00, 0x02, 0x00, 0x40, 0x40, 0x02, 0x00, 0x40, 0x68, 0x4A, 0x00, 0x30, 0x74, 0x4A, 0x00, 0x30, + 0x00, 0x10, 0x01, 0x30, 0x64, 0x8A, 0xCD, 0x74, 0xA2, 0x5B, 0x55, 0x75, 0xA4, 0xAA, 0xD0, 0x60, + 0xA2, 0x98, 0x54, 0x75, 0xA0, 0xA3, 0x64, 0x8A, 0xCD, 0x74, 0xA2, 0x5B, 0x55, 0x75, 0xA4, 0xAA, + 0xD0, 0x60, 0xA2, 0x98, 0x05, 0xC4, 0x85, 0x70, 0xA0, 0xA3, 0x64, 0x8A, 0xCD, 0x74, 0xA2, 0x5B, + 0x55, 0x75, 0xA4, 0xAA, 0xD0, 0x60, 0xA2, 0x98, 0x05, 0xC4, 0x45, 0x70, 0xA0, 0xA3, 0x64, 0x8A, + 0xCD, 0x74, 0xA2, 0x5B, 0x55, 0x75, 0xA4, 0xAA, 0xD0, 0x60, 0xA2, 0x98, 0xB8, 0x4D, 0xA0, 0xA3, + 0x2C, 0x07, 0xFF, 0xE3, 0xDF, 0xFD, 0x00, 0x07, 0x01, 0x81, 0xAA, 0x33, 0xC2, 0x64, 0x67, 0x0B, + 0x04, 0x81, 0x73, 0x33, 0xC2, 0x64, 0x63, 0x0B, 0x05, 0x81, 0x71, 0x33, 0xC2, 0x64, 0x5F, 0x0B, + 0x7A, 0x13, 0x00, 0x30, 0x00, 0xB3, 0x1A, 0x13, 0x68, 0x81, 0xCC, 0x74, 0x60, 0xB0, 0x66, 0x81, + 0x68, 0x43, 0x07, 0x81, 0xC0, 0x6C, 0x17, 0x13, 0x60, 0xA8, 0x62, 0x81, 0x68, 0x43, 0x23, 0x81, + 0xC4, 0x6C, 0x62, 0xDC, 0x06, 0x14, 0x62, 0xD8, 0x06, 0x14, 0xCD, 0x74, 0x40, 0x3B, 0x07, 0x0C, + 0x62, 0xD8, 0x06, 0x14, 0xCD, 0x74, 0x64, 0x31, 0xC4, 0x64, 0x41, 0x0B, 0x64, 0x33, 0x62, 0xDC, + 0x06, 0x14, 0x3D, 0x07, 0xFF, 0xE3, 0xBE, 0xFD, 0x49, 0x07, 0x2B, 0x13, 0x30, 0x07, 0x00, 0x00, + 0x60, 0x14, 0x62, 0x14, 0xD1, 0x14, 0x69, 0x13, 0x83, 0x93, 0x63, 0xE4, 0x3F, 0x01, 0x63, 0x93, + 0x6D, 0x43, 0x98, 0x44, 0xE3, 0xC7, 0x80, 0x28, 0x9B, 0x4C, 0x3F, 0x08, 0x50, 0x3C, 0x41, 0x0C, + 0x64, 0xE4, 0x1D, 0x20, 0x22, 0x13, 0x55, 0x3B, 0x22, 0x0C, 0x54, 0x3C, 0x4E, 0x0C, 0x58, 0x3C, + 0x54, 0x0C, 0x5E, 0x12, 0x05, 0x04, 0x62, 0x92, 0x63, 0xE4, 0x08, 0x30, 0x62, 0xB2, 0x62, 0x92, + 0x7C, 0x43, 0x7F, 0x4B, 0x41, 0x3B, 0xF8, 0x0F, 0x7A, 0x12, 0x41, 0x93, 0x42, 0xEC, 0x20, 0x00, + 0x41, 0xB3, 0x41, 0x93, 0x42, 0xE4, 0x20, 0x30, 0x41, 0xB3, 0x60, 0x91, 0x41, 0x3B, 0x43, 0x0C, + 0xEE, 0xD9, 0x01, 0x20, 0x80, 0x98, 0x02, 0x14, 0x63, 0x14, 0x61, 0x14, 0x61, 0xD8, 0x0A, 0x14, + 0xCD, 0x74, 0x02, 0xEA, 0xC1, 0x04, 0x8C, 0x64, 0x0D, 0x08, 0x61, 0xD8, 0x0A, 0x14, 0xCD, 0x74, + 0x42, 0x5B, 0x89, 0x74, 0x41, 0xDC, 0x0A, 0x14, 0x4B, 0x12, 0xC8, 0x60, 0x40, 0x83, 0x67, 0x12, + 0x40, 0xA3, 0x01, 0x33, 0x61, 0xB1, 0xCE, 0x07, 0xFF, 0xE3, 0x5C, 0xFD, 0x50, 0x3C, 0xC1, 0x0B, + 0x23, 0x12, 0x02, 0xEA, 0x00, 0x08, 0x64, 0x89, 0xCD, 0x74, 0x8C, 0x64, 0xC3, 0x0B, 0x64, 0x89, + 0xCD, 0x74, 0x42, 0x5B, 0x89, 0x74, 0x44, 0xA9, 0x5C, 0x11, 0x00, 0x82, 0x00, 0x74, 0x41, 0xE4, + 0x0B, 0x00, 0xC8, 0x60, 0x00, 0xA3, 0xB6, 0x07, 0x00, 0x33, 0x64, 0xA9, 0x00, 0x33, 0x61, 0xB1, + 0x01, 0x33, 0x61, 0xDC, 0x06, 0x22, 0xAE, 0x07, 0x00, 0x33, 0x61, 0xDC, 0x0A, 0x14, 0x00, 0x33, + 0x61, 0xB1, 0xA8, 0x07, 0xFF, 0xE3, 0x46, 0xFD, 0xEE, 0xD9, 0x01, 0x20, 0x80, 0x98, 0x02, 0x14, + 0x63, 0x14, 0x61, 0x14, 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x70, 0x11, 0x63, 0x93, 0x6D, 0x43, + 0xE3, 0xC7, 0x80, 0x28, 0x13, 0x08, 0x6B, 0x11, 0x41, 0x93, 0x42, 0xEC, 0x40, 0x00, 0x41, 0xB3, + 0x41, 0x93, 0x42, 0xE4, 0x40, 0x30, 0x41, 0xB3, 0x65, 0x11, 0x60, 0x93, 0x41, 0x3B, 0x09, 0x0C, + 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0xFF, 0xE3, 0x13, 0xFD, 0xEC, 0x07, + 0xFF, 0xE3, 0x20, 0xFD, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0x00, 0x00, + 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x7D, 0x10, 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, + 0x13, 0x08, 0x78, 0x10, 0x41, 0x93, 0x42, 0xEC, 0x80, 0x00, 0x41, 0xB3, 0x41, 0x93, 0x42, 0xE4, + 0x80, 0x30, 0x41, 0xB3, 0x72, 0x10, 0x60, 0x93, 0x41, 0x3B, 0x09, 0x0C, 0xEE, 0xD9, 0x00, 0x20, + 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0xFF, 0xE3, 0xED, 0xFC, 0xEC, 0x07, 0xFF, 0xE3, 0xFA, 0xFC, + 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0x00, 0x00, 0x60, 0x14, 0x62, 0x14, + 0xD0, 0x14, 0x6A, 0x10, 0x63, 0x93, 0x13, 0x04, 0x04, 0x1D, 0x00, 0x30, 0x8C, 0x21, 0x00, 0x30, + 0x90, 0x21, 0x00, 0x30, 0x74, 0x4A, 0x00, 0x30, 0xC0, 0x00, 0x00, 0x40, 0x68, 0x4A, 0x00, 0x30, + 0x80, 0x00, 0x00, 0x40, 0x3C, 0x18, 0x00, 0x30, 0x00, 0x02, 0x00, 0x40, 0x6D, 0x43, 0xE3, 0xC7, + 0x80, 0x28, 0x36, 0x08, 0x82, 0xEA, 0x83, 0x00, 0x60, 0x92, 0x7F, 0x43, 0x40, 0x3B, 0x16, 0x0C, + 0x60, 0x92, 0x63, 0xE4, 0x01, 0x30, 0x60, 0xB2, 0x00, 0xEA, 0xC1, 0x04, 0x7E, 0x13, 0x24, 0x8B, + 0x45, 0x74, 0x04, 0x64, 0x22, 0x08, 0x24, 0x8B, 0x45, 0x74, 0x02, 0x59, 0x01, 0x74, 0x04, 0xAB, + 0x0B, 0x23, 0x44, 0x82, 0x88, 0x74, 0x4C, 0x60, 0x40, 0xA1, 0x56, 0x13, 0x60, 0x92, 0x7E, 0x43, + 0xE3, 0xC7, 0x80, 0x28, 0x09, 0x0C, 0x60, 0x92, 0x63, 0xE4, 0x02, 0x30, 0x60, 0xB2, 0x01, 0x32, + 0x71, 0x13, 0x43, 0xDC, 0x07, 0x22, 0x51, 0x13, 0x63, 0x92, 0x90, 0x3B, 0x63, 0xB2, 0xEE, 0xD9, + 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0x00, 0x32, 0x44, 0xAB, 0xE7, 0x07, 0xFF, 0xE3, + 0x91, 0xFC, 0xC9, 0x07, 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x68, 0x13, 0x63, 0x93, 0x6D, 0x43, + 0xE3, 0xC7, 0x80, 0x28, 0x0D, 0x08, 0x01, 0x30, 0xFF, 0xE3, 0x46, 0xFC, 0x62, 0x13, 0x60, 0x93, + 0x41, 0x3B, 0x09, 0x0C, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0xFF, 0xE3, + 0x79, 0xFC, 0xF2, 0x07, 0xFF, 0xE3, 0x86, 0xFC, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, + 0x61, 0x14, 0x00, 0x00, 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x78, 0x12, 0x63, 0x93, 0x6D, 0x43, + 0xE3, 0xC7, 0x80, 0x28, 0x1D, 0x08, 0x76, 0x12, 0x41, 0x93, 0x42, 0xEC, 0x00, 0x04, 0x41, 0xB3, + 0x41, 0x93, 0x42, 0xE4, 0x00, 0x34, 0x41, 0xB3, 0x23, 0xEA, 0x00, 0x40, 0x54, 0x93, 0x42, 0xEC, + 0x00, 0x08, 0x54, 0xB3, 0x54, 0x93, 0x42, 0xE4, 0x00, 0x38, 0x54, 0xB3, 0x6A, 0x12, 0x60, 0x93, + 0x41, 0x3B, 0x09, 0x0C, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0xFF, 0xE3, + 0x49, 0xFC, 0xE2, 0x07, 0xFF, 0xE3, 0x56, 0xFC, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, + 0x61, 0x14, 0x00, 0x00, 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x60, 0x12, 0x63, 0x93, 0x6D, 0x43, + 0xE3, 0xC7, 0x80, 0x28, 0x13, 0x08, 0x7E, 0x11, 0x41, 0x93, 0x42, 0xEC, 0x00, 0x08, 0x41, 0xB3, + 0x41, 0x93, 0x42, 0xE4, 0x00, 0x38, 0x41, 0xB3, 0x77, 0x11, 0x60, 0x93, 0x41, 0x3B, 0x09, 0x0C, + 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0xFF, 0xE3, 0x23, 0xFC, 0xEC, 0x07, + 0xFF, 0xE3, 0x30, 0xFC, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0x00, 0x00, + 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x6D, 0x11, 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, + 0x0C, 0x08, 0xFF, 0xE3, 0xC5, 0xF8, 0x68, 0x11, 0x60, 0x93, 0x41, 0x3B, 0x0E, 0x0C, 0xEE, 0xD9, + 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0xFF, 0xE3, 0x04, 0xFC, 0xFF, 0xE3, 0xB8, 0xF8, + 0x61, 0x11, 0x60, 0x93, 0x41, 0x3B, 0xF4, 0x0B, 0xFF, 0xE3, 0x0C, 0xFC, 0xEE, 0xD9, 0x00, 0x20, + 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0x00, 0x00, 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x7B, 0x10, + 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0x12, 0x08, 0x79, 0x10, 0x41, 0x93, 0x42, 0xEC, + 0x00, 0x20, 0x41, 0xB3, 0x41, 0x93, 0x8D, 0x3A, 0x41, 0xB3, 0x73, 0x10, 0x60, 0x93, 0x41, 0x3B, + 0x09, 0x0C, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0xFF, 0xE3, 0xDA, 0xFB, + 0xED, 0x07, 0xFF, 0xE3, 0xE7, 0xFB, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, + 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x69, 0x10, 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, + 0x1D, 0x08, 0x67, 0x10, 0x41, 0x93, 0x42, 0xEC, 0x00, 0x40, 0x41, 0xB3, 0x0A, 0x04, 0x00, 0x00, + 0x80, 0x01, 0x00, 0x40, 0x68, 0x4A, 0x00, 0x30, 0x00, 0x02, 0x00, 0x40, 0x80, 0x00, 0x00, 0x40, + 0x41, 0x93, 0x8E, 0x3A, 0x41, 0xB3, 0x83, 0xEA, 0x84, 0x00, 0x60, 0x93, 0x41, 0x3B, 0x09, 0x0C, + 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0xFF, 0xE3, 0xAB, 0xFB, 0xE2, 0x07, + 0xFF, 0xE3, 0xB8, 0xFB, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0x00, 0x00, + 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x79, 0x13, 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, + 0x13, 0x08, 0x77, 0x13, 0x4C, 0x93, 0x42, 0xEC, 0x02, 0x00, 0x4C, 0xB3, 0x4C, 0x93, 0x42, 0xE4, + 0x02, 0x30, 0x4C, 0xB3, 0x70, 0x13, 0x60, 0x93, 0x41, 0x3B, 0x09, 0x0C, 0xEE, 0xD9, 0x00, 0x20, + 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0xFF, 0xE3, 0x85, 0xFB, 0xEC, 0x07, 0xFF, 0xE3, 0x92, 0xFB, + 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0x00, 0x00, 0x60, 0x14, 0x62, 0x14, + 0xD0, 0x14, 0x66, 0x13, 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0x11, 0x08, 0x65, 0x13, + 0x41, 0x93, 0xB0, 0x3A, 0x41, 0xB3, 0x41, 0x93, 0x90, 0x3A, 0x41, 0xB3, 0x7E, 0x12, 0x60, 0x93, + 0x41, 0x3B, 0x09, 0x0C, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0xFF, 0xE3, + 0x61, 0xFB, 0xEE, 0x07, 0xFF, 0xE3, 0x6E, 0xFB, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, + 0x61, 0x14, 0x00, 0x00, 0x60, 0x14, 0x62, 0x14, 0x54, 0x12, 0x04, 0x04, 0x63, 0x92, 0xB1, 0x3B, + 0x63, 0xB2, 0x63, 0x92, 0x6C, 0x43, 0x7F, 0x4B, 0x41, 0x3B, 0xF9, 0x0F, 0x6F, 0x12, 0x43, 0x93, + 0x91, 0x3A, 0x43, 0xB3, 0x43, 0x93, 0x42, 0xE4, 0x01, 0x30, 0x43, 0xB3, 0x01, 0x32, 0x6A, 0x12, + 0x43, 0xDC, 0x08, 0x22, 0x63, 0x14, 0x61, 0x14, 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x67, 0x12, + 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0x1B, 0x08, 0x65, 0x12, 0x47, 0x93, 0x42, 0xEC, + 0x01, 0x00, 0x47, 0xB3, 0x47, 0x93, 0x42, 0xE4, 0x01, 0x30, 0x47, 0xB3, 0x47, 0x93, 0x42, 0xEC, + 0x02, 0x00, 0x47, 0xB3, 0x47, 0x93, 0x42, 0xE4, 0x02, 0x30, 0x47, 0xB3, 0x7A, 0x11, 0x60, 0x93, + 0x41, 0x3B, 0x09, 0x0C, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0xFF, 0xE3, + 0x19, 0xFB, 0xE4, 0x07, 0xFF, 0xE3, 0x26, 0xFB, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, + 0x61, 0x14, 0x00, 0x00, 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x70, 0x11, 0x63, 0x93, 0x6D, 0x43, + 0xE3, 0xC7, 0x80, 0x28, 0x1E, 0x08, 0x2C, 0x11, 0x01, 0x33, 0x61, 0xDC, 0x09, 0x22, 0x6C, 0x11, + 0x47, 0x93, 0x42, 0xEC, 0x04, 0x00, 0x47, 0xB3, 0x47, 0x93, 0x42, 0xE4, 0x04, 0x30, 0x47, 0xB3, + 0x47, 0x93, 0x42, 0xEC, 0x08, 0x00, 0x47, 0xB3, 0x47, 0x93, 0x42, 0xE4, 0x08, 0x30, 0x47, 0xB3, + 0x60, 0x91, 0x41, 0x3B, 0x09, 0x0C, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, + 0xFF, 0xE3, 0xE8, 0xFA, 0xE1, 0x07, 0xFF, 0xE3, 0xF5, 0xFA, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, + 0x63, 0x14, 0x61, 0x14, 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x78, 0x10, 0x63, 0x93, 0x6D, 0x43, + 0xE3, 0xC7, 0x80, 0x28, 0x14, 0x08, 0x34, 0x10, 0x01, 0x33, 0x61, 0xDC, 0x0A, 0x22, 0x74, 0x10, + 0x4C, 0x93, 0xBE, 0x3A, 0x4C, 0xB3, 0x4C, 0x93, 0x9E, 0x3A, 0x4C, 0xB3, 0x60, 0x91, 0x41, 0x3B, + 0x09, 0x0C, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0xFF, 0xE3, 0xC2, 0xFA, + 0xEB, 0x07, 0xFF, 0xE3, 0xCF, 0xFA, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, + 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x65, 0x10, 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, + 0x1E, 0x08, 0x09, 0x04, 0x68, 0x4A, 0x00, 0x30, 0x00, 0x02, 0x00, 0x40, 0x00, 0x01, 0x00, 0x40, + 0x80, 0x00, 0x00, 0x40, 0x3D, 0x13, 0x23, 0xEA, 0x00, 0x3F, 0x4C, 0x91, 0x8D, 0x68, 0xB8, 0x3A, + 0x4C, 0xB1, 0x4C, 0x91, 0x62, 0xC4, 0x43, 0x20, 0x6C, 0xB1, 0x79, 0x13, 0x60, 0x93, 0x41, 0x3B, + 0x09, 0x0C, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0xFF, 0xE3, 0x92, 0xFA, + 0xEA, 0x07, 0xFF, 0xE3, 0x9F, 0xFA, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, + 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x6F, 0x13, 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, + 0x11, 0x08, 0x6D, 0x13, 0x41, 0x93, 0xB6, 0x3A, 0x41, 0xB3, 0x41, 0x93, 0x96, 0x3A, 0x41, 0xB3, + 0x67, 0x13, 0x60, 0x93, 0x41, 0x3B, 0x09, 0x0C, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, + 0x61, 0x14, 0xFF, 0xE3, 0x6F, 0xFA, 0xEE, 0x07, 0xFF, 0xE3, 0x7C, 0xFA, 0xEE, 0xD9, 0x00, 0x20, + 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0x00, 0x00, 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x7D, 0x12, + 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0x11, 0x08, 0x7B, 0x12, 0x41, 0x93, 0xB7, 0x3A, + 0x41, 0xB3, 0x41, 0x93, 0x97, 0x3A, 0x41, 0xB3, 0x75, 0x12, 0x60, 0x93, 0x41, 0x3B, 0x09, 0x0C, + 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0xFF, 0xE3, 0x4B, 0xFA, 0xEE, 0x07, + 0xFF, 0xE3, 0x58, 0xFA, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0x00, 0x00, + 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x6B, 0x12, 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, + 0x19, 0x08, 0x46, 0x12, 0x6D, 0x92, 0x70, 0x43, 0x7F, 0x4B, 0x41, 0x3B, 0x02, 0x0C, 0x6D, 0x92, + 0x42, 0x12, 0x6C, 0x92, 0xBF, 0x3B, 0x6C, 0xB2, 0x6C, 0x92, 0x61, 0x43, 0x61, 0x4B, 0x6C, 0xB2, + 0x7F, 0x11, 0x60, 0x93, 0x41, 0x3B, 0x09, 0x0C, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, + 0x61, 0x14, 0xFF, 0xE3, 0x1F, 0xFA, 0xE6, 0x07, 0xFF, 0xE3, 0x2C, 0xFA, 0xEE, 0xD9, 0x00, 0x20, + 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0x00, 0x00, 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x75, 0x11, + 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0x15, 0x08, 0x70, 0x11, 0x4C, 0x93, 0x42, 0xEC, + 0x00, 0x80, 0x4C, 0xB3, 0x4C, 0x93, 0x8F, 0x3A, 0x4C, 0xB3, 0x01, 0x32, 0x6C, 0x11, 0x43, 0xDC, + 0x0B, 0x22, 0x60, 0x93, 0x41, 0x3B, 0x09, 0x0C, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, + 0x61, 0x14, 0xFF, 0xE3, 0xF7, 0xF9, 0xEA, 0x07, 0xFF, 0xE3, 0x04, 0xFA, 0xEE, 0xD9, 0x00, 0x20, + 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0x00, 0x00, 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x61, 0x11, + 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0x12, 0x08, 0x7C, 0x10, 0x4C, 0x93, 0x42, 0xEC, + 0x00, 0x20, 0x4C, 0xB3, 0x4C, 0x93, 0x8D, 0x3A, 0x4C, 0xB3, 0x79, 0x10, 0x60, 0x93, 0x41, 0x3B, + 0x09, 0x0C, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0xFF, 0xE3, 0xD2, 0xF9, + 0xED, 0x07, 0xFF, 0xE3, 0xDF, 0xF9, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, + 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x6F, 0x10, 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, + 0x11, 0x08, 0x6D, 0x10, 0x41, 0x93, 0xBB, 0x3A, 0x41, 0xB3, 0x41, 0x93, 0x9B, 0x3A, 0x41, 0xB3, + 0x67, 0x10, 0x60, 0x93, 0x41, 0x3B, 0x11, 0x0C, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, + 0x61, 0x14, 0xFF, 0xE3, 0xAF, 0xF9, 0xEE, 0x07, 0x00, 0x01, 0x00, 0x40, 0x68, 0x4A, 0x00, 0x30, + 0x00, 0x02, 0x00, 0x40, 0x80, 0x00, 0x00, 0x40, 0xFF, 0xE3, 0xB4, 0xF9, 0xEE, 0xD9, 0x00, 0x20, + 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, 0x00, 0x00, 0x60, 0x14, 0x62, 0x14, 0xD0, 0x14, 0x72, 0x10, + 0x63, 0x93, 0x6D, 0x43, 0xE3, 0xC7, 0x80, 0x28, 0x14, 0x08, 0x30, 0x10, 0x01, 0x33, 0x61, 0xDC, + 0x0C, 0x22, 0x6F, 0x10, 0x41, 0x93, 0xBC, 0x3A, 0x41, 0xB3, 0x41, 0x93, 0x9C, 0x3A, 0x41, 0xB3, + 0x60, 0x91, 0x41, 0x3B, 0x09, 0x0C, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, 0x63, 0x14, 0x61, 0x14, + 0xFF, 0xE3, 0x80, 0xF9, 0xEB, 0x07, 0xFF, 0xE3, 0x8D, 0xF9, 0xEE, 0xD9, 0x00, 0x20, 0x01, 0x14, + 0x63, 0x14, 0x61, 0x14, 0x00, 0x02, 0x00, 0x40, 0x68, 0x4A, 0x00, 0x30, 0x80, 0x00, 0x00, 0x40, + 0x44, 0x10, 0x60, 0x92, 0x40, 0x3B, 0xFE, 0x0F, 0x00, 0x33, 0x60, 0xB2, 0x3C, 0x78, 0x00, 0x00, + 0x88, 0x52, 0x00, 0x30, 0x40, 0x39, 0x03, 0x08, 0x00, 0xC0, 0x20, 0x2C, 0x40, 0x38, 0x02, 0x08, + 0x3C, 0x78, 0xC2, 0x14, 0x43, 0x6D, 0x45, 0x6D, 0xE0, 0xC7, 0x80, 0x28, 0x03, 0x0C, 0x02, 0x6C, + 0x00, 0x20, 0xE1, 0xC7, 0x80, 0x28, 0x03, 0x0C, 0x46, 0x6C, 0x00, 0x21, 0x01, 0x32, 0xC3, 0x6C, + 0x03, 0xC4, 0x44, 0x7C, 0x90, 0x70, 0x10, 0x70, 0xC7, 0x6C, 0x03, 0xC4, 0x44, 0x7C, 0x00, 0x24, + 0xC3, 0x6C, 0xD1, 0x70, 0x12, 0x6D, 0x20, 0x24, 0x90, 0x70, 0x10, 0x70, 0x40, 0x3A, 0x09, 0x0C, + 0x00, 0xC4, 0x20, 0x4C, 0xCD, 0x60, 0x4C, 0x64, 0x02, 0x0C, 0xC6, 0x60, 0x89, 0x60, 0xF9, 0x0F, + 0x0B, 0x6C, 0x4F, 0x6C, 0x05, 0xC4, 0x25, 0x4C, 0x05, 0x0C, 0x02, 0x6C, 0x00, 0x20, 0x46, 0x6C, + 0x00, 0x21, 0x82, 0x14, 0xD1, 0x14, 0x68, 0x11, 0x00, 0x31, 0x0F, 0x6C, 0x47, 0x11, 0x8E, 0x60, + 0xFF, 0xE3, 0x26, 0xED, 0x86, 0x11, 0xFF, 0xE3, 0xAD, 0xED, 0x60, 0x94, 0x41, 0x3B, 0x17, 0x0C, + 0x40, 0x3B, 0x2E, 0x0C, 0x42, 0x3B, 0x1D, 0x0C, 0x43, 0x3B, 0xF8, 0x0B, 0x00, 0x33, 0x01, 0x30, + 0x64, 0xDC, 0x22, 0x21, 0x60, 0xB4, 0xFF, 0xE3, 0x9D, 0xE6, 0x01, 0x30, 0xFF, 0xE3, 0xD4, 0xF5, + 0x64, 0x30, 0xFF, 0xE3, 0x81, 0xF5, 0x60, 0x94, 0x41, 0x3B, 0xEB, 0x0B, 0x04, 0xD8, 0x46, 0x12, + 0x01, 0x74, 0xFF, 0xE3, 0x3B, 0xF3, 0xFF, 0xE3, 0x91, 0xF3, 0x02, 0x33, 0x60, 0xB4, 0xDE, 0x07, + 0x64, 0xD8, 0x22, 0x21, 0x64, 0xD8, 0x22, 0x21, 0x40, 0x3B, 0x13, 0x08, 0x04, 0xD8, 0x22, 0x21, + 0xFF, 0xE3, 0xE0, 0xE6, 0x41, 0x38, 0xD2, 0x0B, 0x03, 0x33, 0x60, 0xB4, 0xCF, 0x07, 0x64, 0xD8, + 0x22, 0x21, 0x40, 0x3B, 0xCB, 0x0F, 0xFF, 0xE3, 0x9B, 0xE6, 0x01, 0x33, 0x60, 0xB4, 0xC6, 0x07, + 0xFF, 0xE3, 0xF2, 0xF0, 0x64, 0xD8, 0x84, 0x04, 0x00, 0x23, 0xCC, 0x74, 0x64, 0xDC, 0x84, 0x04, + 0xE6, 0x07, 0x00, 0x00, 0x3C, 0x0F, 0x00, 0x30, 0xA0, 0x52, 0x00, 0x30, 0x04, 0x1D, 0x00, 0x30, + 0x28, 0x0D, 0x00, 0x00, 0xE0, 0x0C, 0x00, 0x00, 0x98, 0x0C, 0x00, 0x00, 0x50, 0x0C, 0x00, 0x00, + 0x08, 0x0C, 0x00, 0x00, 0xB4, 0x0B, 0x00, 0x00, 0x72, 0x0B, 0x00, 0x00, 0x32, 0x0B, 0x00, 0x00, + 0xF0, 0x0A, 0x00, 0x00, 0xAE, 0x0A, 0x00, 0x00, 0x6C, 0x0A, 0x00, 0x00, 0x14, 0x0A, 0x00, 0x00, + 0xD6, 0x09, 0x00, 0x00, 0x96, 0x09, 0x00, 0x00, 0x56, 0x09, 0x00, 0x00, 0x18, 0x09, 0x00, 0x00, + 0xD8, 0x08, 0x00, 0x00, 0x98, 0x08, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0xF4, 0x07, 0x00, 0x00, + 0xB6, 0x07, 0x00, 0x00, 0x76, 0x07, 0x00, 0x00, 0x36, 0x07, 0x00, 0x00, 0xF8, 0x06, 0x00, 0x00, + 0xAC, 0x06, 0x00, 0x00, 0x6C, 0x06, 0x00, 0x00, 0x2C, 0x06, 0x00, 0x00, 0xEC, 0x05, 0x00, 0x00, + 0xB2, 0x05, 0x00, 0x00, 0x76, 0x05, 0x00, 0x00, 0x3A, 0x05, 0x00, 0x00, 0x1C, 0x04, 0x00, 0x00, + 0xE6, 0x04, 0x00, 0x00, 0xAE, 0x04, 0x00, 0x00, 0x66, 0x04, 0x00, 0x00, 0x74, 0x0D, 0x00, 0x00, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, + 0x22, 0x23, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x41, 0x00, 0x80, 0x04, 0xC1, 0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x40, 0x04, 0x13, 0x0C, 0x37, 0xF0, 0x4F, 0x05, 0x40, 0x04, 0x13, 0x0C, + 0x37, 0xF0, 0x4F, 0x05, 0x40, 0x04, 0x13, 0x0C, 0x37, 0xF0, 0x4F, 0x05, 0x40, 0x04, 0x13, 0x0C, + 0x37, 0xF0, 0x4F, 0x05, 0x40, 0x04, 0x13, 0x0C, 0x37, 0xF0, 0x4F, 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, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, + 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, + 0xDB, 0xB6, 0x00, 0x3A, 0xD9, 0x08, 0x57, 0x3A, 0x1D, 0x40, 0x55, 0x55, 0x31, 0x40, 0x55, 0x15, + 0x81, 0x40, 0x7F, 0x3F, 0x2B, 0x41, 0x55, 0x15, 0xB3, 0xC0, 0xFF, 0x3F, 0x31, 0xC0, 0xFF, 0xFF, + 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, + 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, + 0x11, 0x21, 0x04, 0x04, 0x19, 0x02, 0xDC, 0x16, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, + 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, + 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0xB6, 0x00, 0x3A, 0xD9, 0x08, 0x57, 0x3A, 0x1D, 0x40, 0x55, 0x55, + 0x31, 0x40, 0x55, 0x15, 0x81, 0x40, 0x7F, 0x3F, 0x2B, 0x41, 0x55, 0x15, 0xB3, 0xC0, 0xFF, 0x3F, + 0x31, 0xC0, 0xFF, 0xFF, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, + 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, + 0x4F, 0x00, 0x00, 0x00, 0x11, 0x21, 0x84, 0x18, 0x19, 0x02, 0xDC, 0x16, 0xDB, 0x02, 0x5A, 0x3A, + 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, + 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0xB6, 0x00, 0x3A, 0xD9, 0x08, 0x57, 0x3A, + 0x1D, 0x40, 0x55, 0x55, 0x31, 0x40, 0x55, 0x15, 0x81, 0xC0, 0xFD, 0x3D, 0x2B, 0x41, 0x55, 0x15, + 0xB3, 0xC0, 0xFF, 0x3F, 0x31, 0xC0, 0xFF, 0xFF, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, + 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, 0x11, 0x21, 0xC4, 0x1C, 0x19, 0x02, 0xDC, 0x16, + 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, + 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0xB6, 0x00, 0x3A, + 0xD9, 0x08, 0x57, 0x3A, 0x1D, 0x40, 0x55, 0x55, 0x31, 0x40, 0x55, 0x15, 0x81, 0xC0, 0xF7, 0x37, + 0x2B, 0x41, 0x55, 0x15, 0xB3, 0xC0, 0xFF, 0x3F, 0x31, 0xC0, 0xFF, 0xFF, 0x1D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, + 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, 0x11, 0x21, 0x04, 0x21, + 0x19, 0x02, 0xDC, 0x16, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, + 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, + 0xDB, 0xB6, 0x00, 0x3A, 0xD9, 0x08, 0x57, 0x3A, 0x1D, 0x40, 0x55, 0x55, 0x31, 0x40, 0x55, 0x15, + 0x81, 0xC0, 0xDF, 0x1F, 0x2B, 0x41, 0x55, 0x15, 0xB3, 0xC0, 0xFF, 0x3F, 0x31, 0xC0, 0xFF, 0xFF, + 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, + 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x20, + 0x11, 0x21, 0x44, 0x25, 0x19, 0x02, 0xDC, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xA8, 0x24, 0x00, 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, 0x00, + 0x24, 0x60, 0x10, 0x10, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 0x00, 0x88, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x15, 0x26, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x3F, + 0x00, 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, 0x20, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x03, + 0xFF, 0x05, 0x00, 0x00, 0x00, 0x64, 0x83, 0x00, 0x33, 0x13, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x55, 0x10, 0x10, 0x0B, 0x0B, 0x14, 0x14, 0x25, 0x25, 0x44, 0x77, 0x02, + 0x08, 0x07, 0x07, 0x00, 0x00, 0x63, 0x10, 0x63, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0D, 0x32, 0x0F, 0x0F, 0x0D, 0x58, 0x0F, 0x0F, 0x58, 0x58, 0x00, 0x00, + 0x58, 0x0D, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x00, 0x07, 0x07, 0x33, + 0x00, 0x00, 0x00, 0x00, 0x43, 0x24, 0x80, 0x44, 0x44, 0x45, 0x21, 0x25, 0x15, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x54, 0x04, 0x13, 0x0C, 0x37, 0xF5, 0x4F, 0x05, 0x54, 0x04, 0x13, 0x0C, + 0x37, 0xF5, 0x4F, 0x05, 0x54, 0x04, 0x13, 0x0C, 0x37, 0xF5, 0x4F, 0x05, 0x54, 0x04, 0x13, 0x0C, + 0x37, 0xF5, 0x4F, 0x05, 0x54, 0x04, 0x13, 0x0C, 0x37, 0xF5, 0x4F, 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, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, + 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, + 0xDB, 0xB6, 0x00, 0x3A, 0xD9, 0x08, 0x57, 0x3A, 0x1D, 0x40, 0x55, 0x55, 0x31, 0x40, 0x55, 0x55, + 0x59, 0x40, 0x55, 0x15, 0x13, 0x45, 0x55, 0x15, 0xB3, 0xC0, 0xFF, 0xFF, 0x31, 0xC0, 0xFF, 0xFF, + 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x21, 0x46, 0x4F, 0x00, 0x00, 0x00, + 0x4F, 0x40, 0x21, 0x46, 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x21, 0x46, 0x4F, 0x20, 0x00, 0x00, + 0x26, 0xC6, 0x08, 0x04, 0xD1, 0x06, 0xDC, 0x96, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, + 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, + 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0xB6, 0x00, 0x3A, 0xD9, 0x08, 0x57, 0x3A, 0x1D, 0x40, 0x55, 0x55, + 0x31, 0x40, 0x55, 0x55, 0x59, 0x40, 0x40, 0x00, 0x13, 0x45, 0x40, 0x00, 0xB3, 0xC0, 0xFF, 0xFF, + 0x31, 0xC0, 0xFF, 0xFF, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x21, 0x46, + 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x21, 0x46, 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x21, 0x46, + 0x4F, 0x00, 0x00, 0x00, 0x26, 0xC6, 0x88, 0x18, 0x11, 0x02, 0xDC, 0x16, 0xDB, 0x02, 0x5A, 0x3A, + 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, + 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0xB6, 0x00, 0x3A, 0xD9, 0x08, 0x57, 0x3A, + 0x1D, 0x40, 0x55, 0x55, 0x31, 0x40, 0x55, 0x55, 0x59, 0x00, 0x01, 0x01, 0x13, 0x05, 0x01, 0x01, + 0xB3, 0xC0, 0xFF, 0xFF, 0x31, 0xC0, 0xFF, 0xFF, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4F, 0x40, 0x21, 0x46, 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x21, 0x46, 0x4F, 0x00, 0x00, 0x00, + 0x4F, 0x40, 0x21, 0x46, 0x4F, 0x00, 0x00, 0x00, 0x26, 0xC6, 0xC8, 0x1C, 0x11, 0x02, 0xDC, 0x16, + 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, + 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0xB6, 0x00, 0x3A, + 0xD9, 0x08, 0x57, 0x3A, 0x1D, 0x40, 0x55, 0x55, 0x31, 0x40, 0x55, 0x55, 0x59, 0x00, 0x04, 0x04, + 0x13, 0x05, 0x04, 0x04, 0xB3, 0xC0, 0xFF, 0xFF, 0x31, 0xC0, 0xFF, 0xFF, 0x1D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x21, 0x46, 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x21, 0x46, + 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x21, 0x46, 0x4F, 0x00, 0x00, 0x00, 0x26, 0xC6, 0x08, 0x21, + 0x11, 0x02, 0xDC, 0x16, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, + 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, + 0xDB, 0xB6, 0x00, 0x3A, 0xD9, 0x08, 0x57, 0x3A, 0x1D, 0x40, 0x55, 0x55, 0x31, 0x40, 0x55, 0x55, + 0x59, 0x00, 0x10, 0x10, 0x13, 0x05, 0x10, 0x10, 0xB3, 0xC0, 0xFF, 0xFF, 0x31, 0xC0, 0xFF, 0xFF, + 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x21, 0x46, 0x4F, 0x00, 0x00, 0x00, + 0x4F, 0x40, 0x21, 0x46, 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x21, 0x46, 0x4F, 0x00, 0x00, 0x20, + 0x26, 0xC6, 0x48, 0x25, 0x11, 0x02, 0xDC, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xA8, 0x24, 0x00, 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, 0x00, + 0x24, 0x60, 0x10, 0x10, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 0x00, 0x88, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x15, 0x26, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x3F, + 0x00, 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, 0x20, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x03, + 0xFF, 0x05, 0x00, 0x00, 0x00, 0x64, 0x83, 0x00, 0x33, 0x13, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x55, 0x10, 0x10, 0x0B, 0x0B, 0x14, 0x14, 0x2B, 0x2B, 0x44, 0x77, 0x02, + 0x08, 0x07, 0x07, 0x00, 0x00, 0x63, 0x10, 0x63, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x38, 0x0F, 0x0F, 0x14, 0x5F, 0x0F, 0x0F, 0x5F, 0x5F, 0x00, 0x00, + 0x5F, 0x14, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x00, 0x01, 0x01, 0x33, + 0x00, 0x00, 0x00, 0x00, 0x43, 0x24, 0x80, 0x44, 0x44, 0x45, 0x21, 0x25, 0x15, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x04, 0x13, 0x0C, 0x37, 0xF0, 0x4F, 0x05, 0x40, 0x04, 0x13, 0x0C, + 0x37, 0xF0, 0x4F, 0x05, 0x40, 0x04, 0x13, 0x0C, 0x37, 0xF0, 0x4F, 0x05, 0x40, 0x04, 0x13, 0x0C, + 0x37, 0xF0, 0x4F, 0x05, 0x40, 0x04, 0x13, 0x0C, 0x37, 0xF0, 0x4F, 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, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, + 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, + 0xDB, 0xB6, 0x00, 0x3A, 0xD9, 0x08, 0x57, 0x3A, 0x1D, 0x40, 0x55, 0x55, 0x31, 0x40, 0x55, 0x15, + 0x81, 0x40, 0x7F, 0x3F, 0x2B, 0x41, 0x55, 0x15, 0xB3, 0xC0, 0xFF, 0x3F, 0x31, 0xC0, 0xFF, 0xFF, + 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, + 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, + 0x11, 0x21, 0x04, 0x04, 0x19, 0x02, 0xDC, 0x16, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, + 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, + 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0xB6, 0x00, 0x3A, 0xD9, 0x08, 0x57, 0x3A, 0x1D, 0x40, 0x55, 0x55, + 0x31, 0x40, 0x55, 0x15, 0x81, 0x40, 0x7F, 0x3F, 0x2B, 0x41, 0x55, 0x15, 0xB3, 0xC0, 0xFF, 0x3F, + 0x31, 0xC0, 0xFF, 0xFF, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, + 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, + 0x4F, 0x00, 0x00, 0x00, 0x11, 0x21, 0x84, 0x18, 0x19, 0x02, 0xDC, 0x16, 0xDB, 0x02, 0x5A, 0x3A, + 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, + 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0xB6, 0x00, 0x3A, 0xD9, 0x08, 0x57, 0x3A, + 0x1D, 0x40, 0x55, 0x55, 0x31, 0x40, 0x55, 0x15, 0x81, 0xC0, 0xFD, 0x3D, 0x2B, 0x41, 0x55, 0x15, + 0xB3, 0xC0, 0xFF, 0x3F, 0x31, 0xC0, 0xFF, 0xFF, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, + 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, 0x11, 0x21, 0xC4, 0x1C, 0x19, 0x02, 0xDC, 0x16, + 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, + 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0xB6, 0x00, 0x3A, + 0xD9, 0x08, 0x57, 0x3A, 0x1D, 0x40, 0x55, 0x55, 0x31, 0x40, 0x55, 0x15, 0x81, 0xC0, 0xF7, 0x37, + 0x2B, 0x41, 0x55, 0x15, 0xB3, 0xC0, 0xFF, 0x3F, 0x31, 0xC0, 0xFF, 0xFF, 0x1D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, + 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, 0x11, 0x21, 0x04, 0x21, + 0x19, 0x02, 0xDC, 0x16, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, + 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, 0xDB, 0x02, 0x5A, 0x3A, 0xDB, 0x04, 0x59, 0x3A, + 0xDB, 0xB6, 0x00, 0x3A, 0xD9, 0x08, 0x57, 0x3A, 0x1D, 0x40, 0x55, 0x55, 0x31, 0x40, 0x55, 0x15, + 0x81, 0xC0, 0xDF, 0x1F, 0x2B, 0x41, 0x55, 0x15, 0xB3, 0xC0, 0xFF, 0x3F, 0x31, 0xC0, 0xFF, 0xFF, + 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, + 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x00, 0x4F, 0x40, 0x61, 0x42, 0x4F, 0x00, 0x00, 0x20, + 0x11, 0x21, 0x44, 0x25, 0x19, 0x02, 0xDC, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xA8, 0x24, 0x00, 0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, 0x00, + 0x24, 0x60, 0x10, 0x10, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 0x00, 0x88, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x50, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x15, 0x26, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x81, 0x3F, + 0x00, 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, 0x20, 0x00, 0x20, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, 0x03, + 0xFF, 0x05, 0x00, 0x00, 0x00, 0x64, 0x83, 0x00, 0x33, 0x13, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x55, 0x10, 0x10, 0x0B, 0x0B, 0x14, 0x14, 0x25, 0x25, 0x44, 0x77, 0x02, + 0x08, 0x07, 0x07, 0x00, 0x00, 0x63, 0x10, 0x63, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0D, 0x0D, 0x00, 0x00, 0x0D, 0x0D, 0x00, 0x00, 0x58, 0x58, 0x00, 0x00, + 0x58, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x40, 0x00, 0x07, 0x07, 0x33, + 0x00, 0x00, 0x00, 0x00, 0x43, 0x24, 0x80, 0x44, 0x44, 0x45, 0x21, 0x25, 0x15, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x85, 0x02, 0x00, 0x62, 0x07, 0x13, 0x86, 0x00, 0x00, 0x00, 0x00, +}; +#endif /* _GCORE_FIRMWARE_H_ */ \ No newline at end of file diff --git a/drivers/input/touchscreen/gcore_mmi/gcore_drv_fw_update.c b/drivers/input/touchscreen/gcore_mmi/gcore_drv_fw_update.c new file mode 100644 index 000000000000..e6fa89700f58 --- /dev/null +++ b/drivers/input/touchscreen/gcore_mmi/gcore_drv_fw_update.c @@ -0,0 +1,2076 @@ +/* + * GalaxyCore touchscreen driver + * + * Copyright (C) 2021 GalaxyCore Incorporated + * + * Copyright (C) 2021 Neo Chen + * + * 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 +#include +#include "gcore_drv_common.h" +#include "gcore_drv_firmware.h" + + +#define FW_BIN_NAME "GCORE_FW.bin" + +struct gcore_dev *gdev_fwu; +struct task_struct *fwu_thread; + +DECLARE_COMPLETION(fw_update_complete); +u8 g_update_running; + +static int gcore_fw_update_fn_init(struct gcore_dev *gdev); +static void gcore_fw_update_fn_remove(struct gcore_dev *gdev); + +struct gcore_exp_fn fw_update_fn = { + .fn_type = GCORE_FW_UPDATE, + .wait_int = false, + .event_flag = false, + .init = gcore_fw_update_fn_init, + .remove = gcore_fw_update_fn_remove, +}; + + +#ifdef CONFIG_GCORE_AUTO_UPDATE_FW_FLASHDOWNLOAD + +#if defined(CONFIG_ENABLE_CHIP_TYPE_GC7371) +#define FW_OP_BIN_NAME "GC7371_Flash_Op.bin" +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7271) +#define FW_OP_BIN_NAME "GC7271_Flash_Op.bin" +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7202) +#define FW_OP_BIN_NAME "GC7202_Flash_Op.bin" +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7372) +#define FW_OP_BIN_NAME "GC7372_Flash_Op.bin" +#endif + +enum FLASH_OP { + READ_FLASH_ID, + FLASH_ERASE, + FLASH_PROGRAM, + FLASH_READ +}; + +enum UPDATE_STATUS { + UPDATE_STATUS_IDLE, + UPDATE_STATUS_RUNNING +}; + +struct fw_update_info { + int update_type; + int status; + int progress; + int max_progress; + + u8 *fw_data; + u32 fw_length; + u32 fw_burn_progress; + + u8 *flash_op_data; + u32 flash_op_length; + u8 *flash_op_buf; +}; + +struct fw_update_info update_info = { + .status = UPDATE_STATUS_IDLE, + .progress = 0, + .max_progress = 7, +}; + +#define GC7XXX_REQUEST_SIZE 64 + +#define GC7XXX_REPLY_SIZE 64 + +#define GC7XXX_MAX_TRANSFER_DATA (GC7XXX_REQUEST_SIZE - 8) + +#define FLASH_OP_TRANSFER_SIZE 1024 + +#define FW_VERSION_ADDR 0xFFF4 + +#endif + + +#ifdef CONFIG_TOUCH_DRIVER_RUN_ON_RK_PLATFORM +extern int dsi_send_packet(unsigned int id, unsigned char *packet, u32 n); +#endif + + +#ifdef CONFIG_ENABLE_CHIP_TYPE_GC7271 +int gcore_send_mipi_reset(void) +{ +#if defined(CONFIG_TOUCH_DRIVER_RUN_ON_RK_PLATFORM) + u8 cmd1[] = {0x01, 0x39, 0xFF, 0x55, 0xAA, 0x66}; + u8 cmd2[] = {0x01, 0x39, 0xFF, 0x26}; + u8 cmd3[] = {0x01, 0x39, 0x04, 0x01}; + + dsi_send_packet(0, cmd1, sizeof(cmd1)); + + msleep(10); + + dsi_send_packet(0, cmd2, sizeof(cmd1)); + + msleep(10); + + dsi_send_packet(0, cmd3, sizeof(cmd1)); + + msleep(10); +#elif defined(CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM) + /* TODO: no complete */ + msleep(10); +#elif defined(CONFIG_TOUCH_DRIVER_RUN_ON_SPRD_PLATFORM) + /* TODO: no complete */ + msleep(10); +#elif defined(CONFIG_TOUCH_DRIVER_RUN_ON_QCOM_PLATFORM) + /* TODO: no complete */ + msleep(10); +#endif + + return 0; +} +#endif + +#if defined(CONFIG_ENABLE_CHIP_TYPE_GC7202) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7372) +u8 idm_key[] = { + 0x4C, 0x70, 0xb8, 0x5c, 0x2e, 0x17, 0x0b, 0x05, 0x02, 0x81, 0xc0, 0x60, + 0x30, 0x98, 0x4c, 0x26, 0x13, 0x09, 0x84, 0x42, 0xa1, 0xd0, 0xe8, 0x74, + 0x3a, 0x9d, 0x4e, 0xa7, 0x53, 0x29, 0x14, 0x0a, 0x05, 0x02, 0x81, 0x40, + 0xa0, 0xd0, 0xe8, 0xf4, 0xfa, 0x7d, 0x3e, 0x9f, 0x4f, 0xa7, 0x53, 0x29, + 0x14, 0x0a, 0x05, 0x82, 0xc1, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff, 0x7f, + 0x3f, 0x9f, 0xcf, 0xe7, 0x73 +}; +#endif + +void gcore_reset(void) +{ + gdev_fwu->rst_output(gdev_fwu->rst_gpio, 0); + msleep(1); + gdev_fwu->rst_output(gdev_fwu->rst_gpio, 1); +} + +/* do not support spi for GC7371 */ +int gcore_enter_idm_mode(void) +{ +#if defined(CONFIG_ENABLE_CHIP_TYPE_GC7371) + u8 cmd[] = {0x95, 0x27, 0x0F, 0xF0}; +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7271) + u8 cmd[] = {0x95, 0x27, 0x0F, 0xF0}; +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7202) + u8 cmd[] = {0x4C, 0x0F, 0xF0}; +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7372) + u8 cmd[] = {0x4C, 0x0F, 0xF0}; +#endif + +#if defined(CONFIG_ENABLE_CHIP_TYPE_GC7371) + gcore_reset(); +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7271) + gcore_send_mipi_reset(); +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7202) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7372) + if (gcore_bus_write(idm_key, sizeof(idm_key))) { + GTP_ERROR("gc7202 write idm key fail"); + } +#endif + + return gcore_bus_write(cmd, sizeof(cmd)); + +} + +int gcore_exit_idm_mode(void) +{ +#if defined(CONFIG_ENABLE_CHIP_TYPE_GC7371) + u8 cmd[] = {0x95, 0x27, 0x0A, 0xF5}; + return gcore_bus_write(cmd, sizeof(cmd)); +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7271) + u8 cmd1[] = {0x95, 0x27, 0x05, 0xFA, 0xD0, 0x00, 0xFF, 0x00, 0x55, 0xAA, 0x66}; + u8 cmd2[] = {0x95, 0x27, 0x05, 0xFA, 0xD0, 0x00, 0xFF, 0x00, 0x26}; + u8 cmd3[] = {0x95, 0x27, 0x05, 0xFA, 0xD0, 0x00, 0x04, 0xA0, 0x00}; + int ret = 0; + + ret = gcore_bus_write(cmd1, sizeof(cmd1)); + if (ret) { + goto write_fail; + } + + msleep(1); + + ret = gcore_bus_write(cmd2, sizeof(cmd2)); + if (ret) { + goto write_fail; + } + + msleep(1); + + ret = gcore_bus_write(cmd3, sizeof(cmd3)); + if (ret) { + goto write_fail; + } + + return 0; + +write_fail: + GTP_ERROR("gc7271 exit idm fail"); + return -1; + +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7202) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7372) + u8 cmd[] = {0x4C, 0x0A, 0xF5}; + return gcore_bus_write(cmd, sizeof(cmd)); +#endif +} + + +s32 gcore_idm_write_reg(u32 addr, u8 *buffer, s32 len) +{ +#if defined(CONFIG_ENABLE_CHIP_TYPE_GC7371) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7271) + u8 cmd[] = {0x95, 0x27, 0x05, 0xFA}; +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7202) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7372) + u8 cmd[] = {0x4C, 0x05, 0xFA}; +#endif + u8 *buffer_send = NULL; + u32 len_send = 0; + int ret = 0; + + len_send = sizeof(cmd) + sizeof(addr) + len; + + buffer_send = kzalloc(len_send, GFP_KERNEL); + if (buffer_send == NULL) { + GTP_ERROR("buffer send allocate fail!"); + return -ENOMEM; + } + + memcpy(buffer_send, cmd, sizeof(cmd)); + + *(buffer_send + sizeof(cmd)) = (u8)(addr >> 24); + *(buffer_send + sizeof(cmd) + 1) = (u8)(addr >> 16); + *(buffer_send + sizeof(cmd) + 2) = (u8)(addr >> 8); + *(buffer_send + sizeof(cmd) + 3) = (u8)(addr); + + memcpy(buffer_send + sizeof(cmd) + 4, buffer, len); + + ret = gcore_bus_write(buffer_send, len_send); + if (ret) { + GTP_ERROR("idm write reg fail!"); + } + + kfree(buffer_send); + + return ret; + +} + +s32 gcore_idm_read_reg(u32 addr, u8 *buffer, s32 len) +{ +#if defined(CONFIG_ENABLE_CHIP_TYPE_GC7371) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7271) + u8 cmd[8] = {0x95, 0x27, 0x04, 0xFB, 0x00, 0x00, 0x00, 0x00}; +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7202) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7372) + u8 cmd[7] = {0x4C, 0x04, 0xFB, 0x00, 0x00, 0x00, 0x00}; +#endif + int cmd_len = sizeof(cmd); + int ret = 0; + + if (buffer == NULL) { + GTP_ERROR("receive buffer is null!"); + return -1; + } + + cmd[cmd_len - 4] = (u8)(addr >> 24); + cmd[cmd_len - 3] = (u8)(addr >> 16); + cmd[cmd_len - 2] = (u8)(addr >> 8); + cmd[cmd_len - 1] = (u8)(addr); + +#ifdef CONFIG_TOUCH_DRIVER_INTERFACE_I2C + ret = gcore_bus_write(cmd, sizeof(cmd)); + if (ret) { + GTP_ERROR("idm read wr address fail!"); + return -1; + } + + /* no sleep. delay precise 1 ms */ + mdelay(1); + + ret = gcore_bus_read(buffer, len); + if (ret) { + GTP_ERROR("idm read fail!"); + return -1; + } +#else + ret = gcore_spi_write_then_read(cmd, cmd_len, buffer, len); + if (ret) { + GTP_ERROR("idm spi write read fail!"); + return -1; + } +#endif + + return 0; +} + + +s32 gcore_fw_write_reg(u32 addr, u8 *buffer, s32 len) +{ + u8 cmd[] = {0x80, 0xB0}; + u8 *buffer_send = NULL; + u32 len_send = 0; + int ret = 0; + u8 rw_byte = 0; + + len_send = sizeof(cmd) + sizeof(addr) + sizeof(len) + sizeof(rw_byte) + len; + + buffer_send = kzalloc(len_send, GFP_KERNEL); + if (buffer_send == NULL) { + GTP_ERROR("buffer send allocate fail!"); + return -ENOMEM; + } + + memcpy(buffer_send, cmd, sizeof(cmd)); + + *(buffer_send + 2) = (u8)(addr >> 24); + *(buffer_send + 3) = (u8)(addr >> 16); + *(buffer_send + 4) = (u8)(addr >> 8); + *(buffer_send + 5) = (u8)(addr); + + + *(buffer_send + 6) = (u8)(len >> 24); + *(buffer_send + 7) = (u8)(len >> 16); + *(buffer_send + 8) = (u8)(len >> 8); + *(buffer_send + 9) = (u8)(len); + + *(buffer_send + 10) = rw_byte; + + memcpy(buffer_send + 11, buffer, len); + + mutex_lock(&gdev_fwu->transfer_lock); + + ret = gcore_bus_write(buffer_send, len_send); + if (ret) { + GTP_ERROR("idm write reg fail!"); + } + + mutex_unlock(&gdev_fwu->transfer_lock); + + kfree(buffer_send); + + return ret; +} +EXPORT_SYMBOL(gcore_fw_write_reg); + +s32 gcore_fw_read_reg(u32 addr, u8 *buffer, s32 len) +{ + u8 cmd[11] = {0x80, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}; + int ret = 0; + + gdev_fwu->fw_event = FW_READ_REG; + gdev_fwu->firmware = buffer; + gdev_fwu->fw_xfer = len; + + cmd[2] = (u8)(addr >> 24); + cmd[3] = (u8)(addr >> 16); + cmd[4] = (u8)(addr >> 8); + cmd[5] = (u8)(addr); + + cmd[6] = (u8)(len >> 24); + cmd[7] = (u8)(len >> 16); + cmd[8] = (u8)(len >> 8); + cmd[9] = (u8)(len); + + mutex_lock(&gdev_fwu->transfer_lock); + + fw_update_fn.wait_int = true; + + ret = gcore_bus_write(cmd, sizeof(cmd)); + if (ret) { + GTP_ERROR("idm read wr address fail!"); + return -1; + } + + mutex_unlock(&gdev_fwu->transfer_lock); + + wait_for_completion_interruptible(&fw_update_complete); + + + return 0; +} +EXPORT_SYMBOL(gcore_fw_read_reg); + +int gcore_fw_read_reg_reply(u8 *buf, int len) +{ + int ret = 0; + + if (buf == NULL) { + GTP_ERROR("receive buffer is null!"); + return -1; + } + + ret = gcore_bus_read(buf, len); + if (ret) { + GTP_ERROR("write cmd read flash id error."); + return -1; + } + + fw_update_fn.wait_int = false; + complete(&fw_update_complete); + + return 0; + +} + +int gcore_fw_event_notify(enum fw_event_type event) +{ + u8 cmd[] = { 0x80, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + u8 cmd_len = sizeof(cmd); + int ret = 0; + + switch (event) { + case FW_EDGE_0: + cmd[cmd_len - 1] = 0x02; + GTP_DEBUG("gcore fw event:edge 0"); + break; + + case FW_EDGE_90: + cmd[cmd_len - 1] = 0x03; + GTP_DEBUG("gcore fw event:edge 90"); + break; + + case FW_CHARGER_PLUG: + cmd[cmd_len - 1] = 0x04; + GTP_DEBUG("gcore fw event:charger plug"); + break; + + case FW_CHARGER_UNPLUG: + cmd[cmd_len - 1] = 0x05; + GTP_DEBUG("gcore fw event:charger unplug"); + break; + + case FW_HEADSET_PLUG: + cmd[cmd_len - 1] = 0x06; + GTP_DEBUG("gcore fw event:headset plug"); + break; + + case FW_HEADSET_UNPLUG: + cmd[cmd_len - 1] = 0x07; + GTP_DEBUG("gcore fw event:headset unplug"); + break; + + case FW_GESTURE_ENABLE: + cmd[cmd_len - 1] = 0x0A; + GTP_DEBUG("gcore fw event:gesture enable"); + break; + + case FW_GESTURE_DISABLE: + cmd[cmd_len - 1] = 0x0B; + GTP_DEBUG("gcore fw event:gesture disable"); + break; + + default: + GTP_DEBUG("unknown fw event type."); + break; + } + + mutex_lock(&gdev_fwu->transfer_lock); + + ret = gcore_bus_write(cmd, cmd_len); + if (ret) { + GTP_ERROR("gcore write fw event fail."); + } + + mutex_unlock(&gdev_fwu->transfer_lock); + + return ret; +} + +int gcore_reg_enable_write_on(void) +{ + u32 addr = 0xC0000020; + u8 value = 0x95; + + return gcore_idm_write_reg(addr, &value, 1); +} + +int gcore_reg_enable_write_off(void) +{ + u32 addr = 0xC0000020; + u8 value = 0x00; + + return gcore_idm_write_reg(addr, &value, 1); +} + +int gcore_read_fw_version(u8 *version, int length) +{ +#if defined(CONFIG_ENABLE_CHIP_TYPE_GC7271) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7202) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7372) +#ifdef CONFIG_GCORE_AUTO_UPDATE_FW_FLASHDOWNLOAD + u32 upgrade_addr = 0xC0000000 + ((0x14 << 2) + 1); + u8 value = BIT6; //bit6 write 1 +#else + u32 upgrade_addr = 0x40000000 + ((0x14 << 2)); + u8 value = BIT0; //bit0 write 1 +#endif +#endif + + if (g_update_running) { + GTP_ERROR("fw update is running, do not read version!"); + return 0; + } + mutex_lock(&gdev_fwu->transfer_lock); + gcore_enter_idm_mode(); + + msleep(1); + +#ifdef CONFIG_ENABLE_CHIP_TYPE_GC7372 + gcore_idm_read_reg(0x8002FFF4, version, length); +#else + gcore_idm_read_reg(0x8001FFF4, version, length); +#endif + GTP_DEBUG("fw version:%x %x %x %x", version[0], version[1], version[2], version[3]); + + msleep(1); + +#if defined(CONFIG_ENABLE_CHIP_TYPE_GC7271) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7202) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7372) + if (gcore_idm_write_reg(upgrade_addr, &value, 1)) { + GTP_ERROR("write reg rkucr0 upgrade fail"); + } + + msleep(1); +#endif + + gcore_exit_idm_mode(); + mutex_unlock(&gdev_fwu->transfer_lock); + + return 0; + +} +EXPORT_SYMBOL(gcore_read_fw_version); + +int gcore_read_ram_fw(u8 *buf, int length) +{ +#if defined(CONFIG_ENABLE_CHIP_TYPE_GC7271) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7202) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7372) +#ifdef CONFIG_GCORE_AUTO_UPDATE_FW_FLASHDOWNLOAD + u32 upgrade_addr = 0xC0000000 + ((0x14 << 2) + 1); + u8 value = BIT6; //bit6 write 1 +#else + u32 upgrade_addr = 0xC0000000 + ((0x14 << 2) + 1); + u8 value = BIT5; //bit5 write 1 +#endif +#endif + + gcore_enter_idm_mode(); + + msleep(1); + +#ifdef CONFIG_ENABLE_CHIP_TYPE_GC7372 + gcore_idm_read_reg(0x80020000, buf, length); +#else + gcore_idm_read_reg(0x80010000, buf, length); +#endif + GTP_DEBUG("fw ram:%x %x %x %x %x", buf[0], buf[1], buf[2], buf[3], buf[4]); + + msleep(1); + +#if defined(CONFIG_ENABLE_CHIP_TYPE_GC7271) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7202) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7372) + if (gcore_idm_write_reg(upgrade_addr, &value, 1)) { + GTP_ERROR("write reg rkucr0 upgrade fail"); + } + + msleep(1); +#endif + + gcore_exit_idm_mode(); + + return 0; + +} + +#define DUMP_FW_FILE_NAME "/sdcard/gcore_dump_fw.bin" + +int gcore_dump_fw_to_file(void) +{ + u8 *fw_buf = NULL; + int xfer_len = FW_SIZE; + struct file *file = NULL; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + mm_segment_t old_fs; +#endif + loff_t pos = 0; + +#ifdef CONFIG_TOUCH_DRIVER_INTERFACE_SPI +#ifdef CONFIG_MTK_SPI_MULTUPLE_1024 + +#if defined(CONFIG_ENABLE_CHIP_TYPE_GC7371) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7271) + xfer_len = FW_SIZE + 1024 - 8 - 2; +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7202) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7372) + xfer_len = FW_SIZE + 1024 - 7 - 2; +#endif + +#else + xfer_len = FW_SIZE; + +#endif +#endif + + fw_buf = (u8 *)kzalloc(xfer_len, GFP_KERNEL); + if (!fw_buf) { + GTP_ERROR("fw buf mem allocate fail"); + return -1; + } + + gcore_read_ram_fw(fw_buf, xfer_len); + + + file = filp_open(DUMP_FW_FILE_NAME, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (IS_ERR(file)) { + GTP_ERROR("Open file %s failed", DUMP_FW_FILE_NAME); + return -1; + } + pos = 0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + kernel_write(file, fw_buf, FW_SIZE, &pos); +#else + old_fs = get_fs(); + set_fs(KERNEL_DS); + __kernel_write(file, fw_buf, FW_SIZE, &pos); + set_fs(old_fs); +#endif + filp_close(file, NULL); + + if (fw_buf) { + kfree(fw_buf); + } + + return 0; +} + +int gcore_idm_read_id(u8 *id, int length) +{ + u8 cmd1[] = {0x55, 0xAA, 0x66}; + u8 unlock = 0x10; + u8 lock = 0x00; + u8 syssr0[4] = { 0 }; + u8 syscr5[4] = { 0 }; + + gcore_enter_idm_mode(); + + msleep(1); + + gcore_idm_read_reg(0xC0000000 + (0x27 << 2), syssr0, sizeof(syssr0)); + GTP_DEBUG("reg syssr0:%x %x %x %x", syssr0[0], syssr0[1], syssr0[2], syssr0[3]); + if (syssr0[1] & BIT7) { + GTP_DEBUG("dp_lock is lock!"); + } + + msleep(1); + + gcore_idm_read_reg(0xC0000000 + (0x25 << 2), syscr5, sizeof(syscr5)); + GTP_DEBUG("reg syscr5:%x %x %x %x", syscr5[0], syscr5[1], syscr5[2], syscr5[3]); + + msleep(1); + + syscr5[2] &= 0x7F; + gcore_idm_write_reg(0xC0000000 + (0x25 << 2) + 2, &syscr5[2], 1); + + GTP_DEBUG("test tp_sel end."); + + msleep(1); + + gcore_idm_write_reg(0xD000FF00, cmd1, sizeof(cmd1)); + + msleep(1); + + /* unlock page */ + gcore_idm_write_reg(0xD000FF00, &unlock, 1); + + msleep(1); + + /* read ID */ + gcore_idm_read_reg(0xD0000400, id, length); + + GTP_DEBUG("chip id:%x %x", id[0], id[1]); + msleep(1); + + /* lock page */ + gcore_idm_write_reg(0xD000FF00, &lock, 1); + + msleep(1); + + gcore_exit_idm_mode(); + + return 0; +} + +#ifdef CONFIG_ENABLE_FW_RAWDATA +int gcore_set_fw_mode(enum FW_MODE mode) +{ + u32 addr = 0xC0000026; + u8 value = mode; + + return gcore_idm_write_reg(addr, &value, 1); +} + +int gcore_reset_mcu(void) +{ + u8 cmd[] = {0x95, 0x27, 0x0A, 0xF5}; + return gcore_bus_write(cmd, sizeof(cmd)); +} + +/* + * set fw mode old way, new way use gcore_fw_mode_set_proc2 + * do not support spi for 7371 + */ +/* +int gcore_fw_mode_set_proc(enum FW_MODE mode) +{ + int ret = 0; + + ret = gcore_enter_idm_mode(); + if (ret) + { + GTP_ERROR("enter idm mode fail"); + goto set_mode_fail; + } + + ret = gcore_reg_enable_write_on(); + if (ret) + { + GTP_ERROR("reg enable write on fail"); + goto set_mode_fail; + } + + ret = gcore_set_fw_mode(mode); + if (ret) + { + GTP_ERROR("set fw mode fail"); + goto set_mode_fail; + } + + ret = gcore_reg_enable_write_off(); + if (ret) + { + GTP_ERROR("reg enable write off fail"); + goto set_mode_fail; + } + + ret = gcore_reset_mcu(); + if (ret) + { + GTP_ERROR("reset mcu fail"); + goto set_mode_fail; + } + + fw_mode_running = mode; + GTP_INFO("Set fw_mode %d success", mode); + + return 0; + +set_mode_fail: + return -1; + +} +*/ + +int gcore_fw_mode_set_proc2(u8 mode) +{ + u8 cmd[] = {0x40, 0xA0, 0x00, 0x00, 0x73, 0x71, 0x00, 0x00, mode}; + int ret = 0; + + ret = gcore_bus_write(cmd, sizeof(cmd)); + if (ret) { + GTP_ERROR("fw mode set fail!"); + return -1; + } + + gdev_fwu->fw_mode = mode; + GTP_DEBUG("Set fw_mode %d success", mode); + + return 0; +} +EXPORT_SYMBOL(gcore_fw_mode_set_proc2); +#endif + +int gcore_run_pram_fw(void) +{ + int ret = 0; + /* run flash op fw */ + u8 cmd1[] = {0x95, 0x27, 0x05, 0xFA, 0x40, 0x00, 0x00, 0x50, 0x81}; //remap + + ret = gcore_bus_write(cmd1, sizeof(cmd1)); + if (ret) { + GTP_ERROR("run flash op fw write error."); + return -1; + } + + ret = gcore_exit_idm_mode(); + if (ret) { + GTP_ERROR("run flash op fw exit idm fail."); + return -1; + } + + return 0; + +} + +/* + * after flashdownload,run fw soft work. + */ +int gcore_upgrade_soft_reset(void) +{ +#if defined(CONFIG_ENABLE_CHIP_TYPE_GC7371) + u8 abccr0_3[4] = { 0 }; + u32 abccr0 = 0xC0000000; + int ret = 0; +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7271) + u32 upgrade_addr = 0xC0000000 + ((0x14 << 2) + 1); + u8 value = BIT6; //bit6 write 1 +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7202) + u32 upgrade_addr = 0xC0000000 + ((0x14 << 2) + 1); + u8 value = BIT6; //bit6 write 1 +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7372) + u8 open = 0x00; + u8 close = 0x01; + u8 data1[] = {0x00, 0x01, 0x00, 0x00}; + u8 data2[] = {0x02, 0xEA, 0x00, 0x40, 0x23, 0xEA, 0x00, 0x40, 0x00, 0x30, + 0x54, 0xB3, 0x3C, 0x78, 0x00, 0x00}; +#endif + +#if defined(CONFIG_ENABLE_CHIP_TYPE_GC7371) + ret = gcore_enter_idm_mode(); + if (ret) { + GTP_ERROR("enter idm mode fail"); + goto run_fw_fail; + } + + msleep(1); + + ret = gcore_reg_enable_write_on(); + if (ret) { + GTP_ERROR("reg enable write on fail"); + goto run_fw_fail; + } + + msleep(1); + + ret = gcore_idm_read_reg(abccr0, abccr0_3, sizeof(abccr0_3)); + if (ret) { + GTP_ERROR("read reg abccr0 fail"); + goto run_fw_fail; + } + + msleep(1); + + ret = gcore_idm_write_reg(abccr0, &abccr0_3[0], 1); + if (ret) { + GTP_ERROR("write reg abccr0 fail"); + goto run_fw_fail; + } + + msleep(1); + + ret = gcore_exit_idm_mode(); + if (ret) { + GTP_ERROR("reset mcu fail"); + goto run_fw_fail; + } + +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7271) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7202) + + gcore_enter_idm_mode(); + + mdelay(5); + + if (gcore_idm_write_reg(upgrade_addr, &value, 1)) { + GTP_ERROR("write reg rkucr0 upgrade fail"); +// goto run_fw_fail; + } + + msleep(1); + + gcore_exit_idm_mode(); + +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7372) + + gcore_enter_idm_mode(); + + msleep(1); + + if (gcore_idm_write_reg(0x40000050, &open, 1)) { + GTP_ERROR("write reg fail"); + } + + msleep(1); + + if (gcore_idm_write_reg(0x80020000, data1, sizeof(data1))) { + GTP_ERROR("write reg fail"); + } + + msleep(1); + + if (gcore_idm_write_reg(0x80020100, data2, sizeof(data2))) { + GTP_ERROR("write reg fail"); + } + + msleep(1); + + if (gcore_idm_write_reg(0x40000050, &close, 1)) { + GTP_ERROR("write reg fail"); + } + + msleep(1); + + gcore_exit_idm_mode(); + +#endif + + return 0; + +//run_fw_fail: +// return -1; + +} + +#ifdef CONFIG_GCORE_AUTO_UPDATE_FW_HOSTDOWNLOAD + +#define STARTUP_UPDATE_DELAY_MS 1000 //ms + +int hostdownload_idm; + + + +/* +* a printk will cost over 1ms cpu times, so we should not use printk +* when it is time strict,like hostdownload read B3 and read coordinate date. +*/ +int gcore_auto_update_hostdownload(u8 *fw_buf) +{ + u8 result = 0; + u8 *fw_data = fw_buf; + u8 retry = 0; + + if (!fw_buf) { + return -1; + } + + if (gcore_bus_read(&result, 1)) { + GTP_ERROR("host download read error."); + return -1; + } + + GTP_DEBUG("read result:%x", result); + + switch (result) { + case 0xB1: + GTP_DEBUG("Start to update pram fw,times(%d)", retry); + retry++; + + if (gcore_bus_write(fw_data, FW_SIZE)) { + GTP_ERROR("host download write error."); + return -1; + } +/* + for (count = 0; count < 64; count++) + { + if (gcore_bus_write(fw_data, 1024)) + { + GTP_ERROR("host download write error."); + return -1; + } + msleep(1); + fw_data += 1024; + } +*/ +/* + for (count = 0; count < 64*1024; count=count + 56) + { + if ((count + 56) > (64*1024)) + { + len = (64*1024) - count; + } + else + { + len = 56; + } + + if (gcore_bus_write(fw_data, len)) + { + GTP_ERROR("host download read error."); + return -1; + } + + fw_data += len; + mdelay(2); + } +*/ + break; + + case 0xB2: + GTP_DEBUG("update pram fw retry 3 times fail!"); + break; + + case 0xB3: + GTP_DEBUG("update pram fw success..."); + fw_update_fn.wait_int = false; + complete(&fw_update_complete); + break; + + default: + GTP_DEBUG("pram fw read unknow result."); + fw_update_fn.wait_int = false; + complete(&fw_update_complete); + break; + } + + + return 0; +} + +int gcore_update_hostdownload_trigger(u8 *fw_buf) +{ + gdev_fwu->fw_event = FW_UPDATE; + gdev_fwu->firmware = fw_buf; + fw_update_fn.wait_int = true; + +#if defined(CONFIG_ENABLE_CHIP_TYPE_GC7371) + gcore_reset(); +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7271) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7202) \ + || defined(CONFIG_ENABLE_CHIP_TYPE_GC7372) +// gcore_reset(); //reset RESX pin + + if (gcore_upgrade_soft_reset()) { + GTP_ERROR("hostdownload soft reset fail."); + return -1; + } +#endif + + if (!wait_for_completion_interruptible_timeout(&fw_update_complete, 5 * HZ)) { + GTP_ERROR("hostdownload trigger timeout."); + return -1; + } + +// if (gcore_auto_update_hostdownload(fw_buf)) +// { +// GTP_ERROR("hostdownload write fail."); +// return -1; +// } + + return 0; +} + +int gcore_burn_fw_idm(u8 *fw_data) +{ + /* 0x80, 0x01, 0x00, 0x00 is address */ + u8 cmd[] = {0x95, 0x27, 0x05, 0xFA, 0x80, 0x01, 0x00, 0x00}; + u8 *addr_h = &cmd[6]; + u8 *fw_data_buf; + u32 fw_length = FW_SIZE; + u32 size_one_time = 1024; + u8 times = fw_length/size_one_time; + int ret = 0; + + fw_data_buf = kzalloc(size_one_time + 8, GFP_KERNEL); + if (fw_data_buf == NULL) { + GTP_ERROR("flash_op_buf mem allocate fail!"); + return -ENOMEM; + } + + while (times--) { + memcpy(fw_data_buf, cmd, 8); + memcpy(fw_data_buf + 8, fw_data, size_one_time); + + ret = gcore_bus_write(fw_data_buf, size_one_time + 8); + if (ret) { + GTP_ERROR("flash op fw addr write error."); + kfree(fw_data_buf); + return -1; + } + + fw_data += size_one_time; + + *addr_h += 4; /* FLASH_OP_TRANSFER_SIZE */ + + } + + kfree(fw_data_buf); + + return 0; + +} + +int gcore_update_hostdownload_idm(u8 *fw_buf) +{ + int progress = 0; + int ret = 0; + + ret = gcore_enter_idm_mode(); + if (ret) { + goto gcore_update_exit; + } + progress = 1; + + ret = gcore_burn_fw_idm(fw_buf); + if (ret) { + goto gcore_update_exit; + } + progress = 2; + + ret = gcore_run_pram_fw(); + if (ret) { + goto gcore_update_exit; + } + progress = 3; + + return 0; + +gcore_update_exit: + return -1; + +} + +int gcore_auto_update_hostdownload_proc(void *fw_buf) +{ + if (!hostdownload_idm) { + if (gcore_update_hostdownload_trigger(fw_buf)) { + GTP_ERROR("hostdownload proc fail!"); + return -1; + } + } else { + if (gcore_update_hostdownload_idm(fw_buf)) { + GTP_ERROR("hostdownload idm proc fail!"); + return -1; + } + } + + return 0; +} +EXPORT_SYMBOL(gcore_auto_update_hostdownload_proc); + +void set_hostdownload_idm(u8 value) +{ + hostdownload_idm = value; +} + + + +#endif //CONFIG_GCORE_AUTO_UPDATE_FW_HOSTDOWNLOAD + +#ifdef CONFIG_GCORE_AUTO_UPDATE_FW_FLASHDOWNLOAD +/* +* Maybe can disable interupt before progress 6 +*/ +void gcore_enter_update_mode(void) +{ + gdev_fwu->irq_disable(gdev_fwu); +} + +int gcore_update_prepare(u8 *fw_buf) +{ + const struct firmware *fw_op = NULL; + + update_info.fw_data = fw_buf; + update_info.fw_length = FW_SIZE; + update_info.fw_burn_progress = 0; + +#ifdef CONFIG_UPDATE_FIRMWARE_BY_BIN_FILE + if (request_firmware(&fw_op, FW_OP_BIN_NAME, &gdev_fwu->bus_device->dev)) { + GTP_ERROR("request firmware op fail!"); + return -1; + } + + update_info.flash_op_length = fw_op->size; + update_info.flash_op_data = (u8 *)kzalloc(update_info.flash_op_length, GFP_KERNEL); + if (update_info.flash_op_data == NULL) { + GTP_ERROR("flash op mem allocate fail!"); + goto fail1; + } + + memcpy(update_info.flash_op_data, fw_op->data, fw_op->size); + GTP_DEBUG("flash op data:%x %x %x %x %x %x", update_info.flash_op_data[0], update_info.flash_op_data[1], update_info.flash_op_data[2], \ + update_info.flash_op_data[3], update_info.flash_op_data[4], update_info.flash_op_data[5]); + + if (fw_op) { + release_firmware(fw_op); + } +#else + update_info.flash_op_length = sizeof(gcore_flash_op_FW); + update_info.flash_op_data = gcore_flash_op_FW; +#endif + + update_info.flash_op_buf = kzalloc(FLASH_OP_TRANSFER_SIZE + 8, GFP_KERNEL); + if (update_info.flash_op_buf == NULL) { + GTP_ERROR("flash_op_buf mem allocate fail!"); + goto fail2; + } + + + return 0; + +fail2: + if (update_info.flash_op_data) { + kfree(update_info.flash_op_data); + } +#ifdef CONFIG_UPDATE_FIRMWARE_BY_BIN_FILE +fail1: + if (fw_op) { + release_firmware(fw_op); + } +#endif + return -1; +} + +int gcore_update_judge(u8 *fw_buf) +{ + u8 cmd[] = {0x95, 0x27, 0x04, 0xFB, 0x80, 0x01, 0xFF, 0xF4}; + u8 ver[4] = { 0 }; //little-endian + u16 fw_major = 0; + u16 fw_minor = 0; + u32 fw_version = 0; + u16 fw_major_update = 0; + u16 fw_minor_update = 0; + u32 fw_version_update = 0; + + if (gcore_bus_write(cmd, sizeof(cmd))) { + GTP_ERROR("get firmware version write error!"); + return -1; + } + + if (gcore_bus_read(ver, sizeof(ver))) { + GTP_ERROR("get firmware version read error!"); + return -1; + } + + fw_major = (ver[1] << 8) | ver[0]; + fw_minor = (ver[3] << 8) | ver[2]; + fw_version = (fw_major << 16) | fw_minor; + + fw_major_update = (fw_buf[FW_VERSION_ADDR + 1] << 8) | fw_buf[FW_VERSION_ADDR]; + fw_minor_update = (fw_buf[FW_VERSION_ADDR + 3] << 8) | fw_buf[FW_VERSION_ADDR + 2]; + fw_version_update = (fw_major_update << 16) | fw_minor_update; + + GTP_DEBUG("fw_version_update=%x fw_version=%x", fw_version_update, fw_version); + + if (fw_version_update <= fw_version) { + GTP_ERROR("The version of the fw is not high than the IC's!"); + } + + return 0; +} + +int gcore_burn_flash_op_fw(void) +{ + /* 0x80, 0x01, 0x00, 0x00 is address */ + u8 cmd[] = {0x95, 0x27, 0x05, 0xFA, 0x80, 0x01, 0x00, 0x00}; + u8 *addr_h = &cmd[6]; + u8 times = update_info.flash_op_length/FLASH_OP_TRANSFER_SIZE; + int ret = 0; + u8 *flash_op_p = update_info.flash_op_data; + + while (times--) { + memcpy(update_info.flash_op_buf, cmd, 8); + memcpy(update_info.flash_op_buf + 8, flash_op_p, FLASH_OP_TRANSFER_SIZE); + + ret = gcore_bus_write(update_info.flash_op_buf, FLASH_OP_TRANSFER_SIZE + 8); + if (ret) { + GTP_ERROR("flash op fw addr write error."); + return -1; + } + + flash_op_p += FLASH_OP_TRANSFER_SIZE; + + *addr_h += 4; /* FLASH_OP_TRANSFER_SIZE */ + + } + + return 0; + +} + +int gcore_flash_program_proc(void) +{ + u8 cmd[GC7XXX_REQUEST_SIZE] = { 0 }; + int ret = 0; + u16 transfer_length = 0; + u8 flash_address_h = 0; + u8 flash_address_m = 0; + u8 flash_address_l = 0; + u8 data_length_H = 0; + u8 data_length_L = 0; + + if (update_info.fw_burn_progress == update_info.fw_length) { + GTP_DEBUG("fw_burn_progress is equal to fw_length."); + return -1; + } + + if ((update_info.fw_length - update_info.fw_burn_progress) > GC7XXX_MAX_TRANSFER_DATA) { + transfer_length = GC7XXX_MAX_TRANSFER_DATA; + } else { + transfer_length = update_info.fw_length - update_info.fw_burn_progress; + } + + flash_address_h = ((update_info.fw_burn_progress & 0xFF0000) >> 16); + flash_address_m = ((update_info.fw_burn_progress & 0xFF00) >> 8); + flash_address_l = (update_info.fw_burn_progress & 0xFF); + + data_length_H = ((transfer_length & 0xFF00) >> 8); + data_length_L = (transfer_length & 0xFF); + + cmd[0] = 0x40; + cmd[1] = flash_address_h; //flash_address17-23 + cmd[2] = 0xD2; + cmd[3] = 0x03; + cmd[4] = flash_address_m; //flash_address8-16 + cmd[5] = flash_address_l; //flash_address0-7 + cmd[6] = data_length_H; //data length H + cmd[7] = data_length_L; //data length L + + memcpy(&cmd[8], update_info.fw_data + update_info.fw_burn_progress, transfer_length); + + ret = gcore_bus_write(cmd, transfer_length + 8); + if (ret) { + GTP_ERROR("write cmd read flash id error."); + return -1; + } + + update_info.fw_burn_progress += transfer_length; + + return 0; + +} + +void gcore_flashdownload_cleanup(void) +{ + if (update_info.flash_op_buf != NULL) { + kfree(update_info.flash_op_buf); + update_info.flash_op_buf = NULL; + } + + if (update_info.flash_op_data != NULL) { + kfree(update_info.flash_op_data); + update_info.flash_op_data = NULL; + } +} + +int gcore_flash_op_request(enum FLASH_OP command) +{ + u8 cmd[GC7XXX_REQUEST_SIZE] = { 0 }; + int ret = 0; + + switch (command) { + case READ_FLASH_ID: + cmd[0] = 0x40; + cmd[1] = 0xE1; + cmd[2] = 0xD0; + cmd[3] = 0x03; + + ret = gcore_bus_write(cmd, 4); + if (ret) { + GTP_ERROR("write cmd read flash id error."); + return -1; + } + + break; + + case FLASH_ERASE: + cmd[0] = 0x40; + cmd[1] = 0xE1; + cmd[2] = 0xD1; + cmd[3] = 0x03; + cmd[4] = 0x00; + + ret = gcore_bus_write(cmd, 5); + if (ret) { + GTP_ERROR("write cmd read flash id error."); + return -1; + } + + break; + + case FLASH_PROGRAM: + ret = gcore_flash_program_proc(); + if (ret) { + GTP_ERROR("flash burn proc error."); + return -1; + } + + break; + + default: + + break; + } + + return 0; +} + +/* + * 2020-11-05:run fw soft do not work. + */ +int gcore_run_fw_soft(void) +{ + int ret = 0; + /* run fw */ + u8 cmd1[] = {0x95, 0x27, 0x05, 0xFA, 0x40, 0x00, 0x00, 0x50, 0x80}; + u8 cmd2[] = {0x95, 0x27, 0x0A, 0xF5}; + + ret = gcore_bus_write(cmd1, sizeof(cmd1)); + if (ret) { + GTP_ERROR("run flash op fw write error."); + return -1; + } + + ret = gcore_bus_write(cmd2, sizeof(cmd2)); + if (ret) { + GTP_ERROR("run flash op fw write error."); + return -1; + } + + return 0; + +} + +int gcore_flash_op_reply_proc(void) +{ + u8 reply[GC7XXX_REPLY_SIZE] = { 0 }; + u8 command = 0; + u32 flash_id = 0; + int ret = 0; + int result = 0; + + ret = gcore_bus_read(reply, GC7XXX_REPLY_SIZE); + if (ret) { + GTP_ERROR("flash op reply read error."); + return -1; + } + + command = reply[2]; + + switch (command) { + case 0xC0: /* Read Flash ID */ + flash_id = ((reply[8] << 16) | (reply[9] << 8) | (reply[10])); + if ((flash_id != 0x00) && (flash_id != 0xFF)) { + if (gcore_flash_op_request(FLASH_ERASE)) { + GTP_ERROR("flash erase error."); + } + update_info.progress = 7; + } else { + GTP_ERROR("invalid flash id."); + } + + break; + + case 0xC1: /* Flash Erase */ + result = reply[7]; + if (result == 1) { + if (update_info.fw_burn_progress < update_info.fw_length) { + if (gcore_flash_op_request(FLASH_PROGRAM)) { + GTP_ERROR("flash program error."); + return -1; + } + } + update_info.progress = 8; + } else { + GTP_ERROR("flash erase fail"); + } + + break; + + case 0xF9: /* Flash Program */ + case 0xFA: + result = reply[3]; + if (result == 0x01) { + if (update_info.fw_burn_progress < update_info.fw_length) { + if (gcore_flash_op_request(FLASH_PROGRAM)) { + GTP_ERROR("flash program error."); + return -1; + } + update_info.progress = 9; + } else { + GTP_DEBUG("flash download finish"); + msleep(100); + gcore_upgrade_soft_reset(); + gcore_flashdownload_cleanup(); + update_info.status = UPDATE_STATUS_IDLE; + update_info.progress = 10; + fw_update_fn.wait_int = false; + complete(&fw_update_complete); + } + + } else if (result == 0x02) { + GTP_DEBUG("flash download success"); + gcore_run_fw_soft(); + gcore_flashdownload_cleanup(); + update_info.status = UPDATE_STATUS_IDLE; + update_info.progress = 10; + fw_update_fn.wait_int = false; + complete(&fw_update_complete); + } else if (result == 0x00) { + GTP_DEBUG("flash download failed"); + return -1; + } + + break; + + case 0xC3: /* Flash Read */ + + break; + } + + return 0; + +} + +int gcore_auto_update_flashdownload_proc(void *fw_buf) +{ + int ret = 0; + + if (update_info.status != UPDATE_STATUS_IDLE) { + GTP_ERROR("Update process is running!"); + return -1; + } + + ret = gcore_update_prepare(fw_buf); + if (ret) { + return -1; + } + + update_info.status = UPDATE_STATUS_RUNNING; + update_info.max_progress = 10; + update_info.progress = 0; + + gcore_enter_update_mode(); + + mutex_lock(&gdev_fwu->transfer_lock); + +// gcore_reset(); //move to gcore_enter_idm_mode() + update_info.progress = 1; + + ret = gcore_enter_idm_mode(); + if (ret) { + goto fail1; + } + update_info.progress = 2; + + ret = gcore_update_judge(fw_buf); + if (ret) { + goto fail1; + } + update_info.progress = 3; + + ret = gcore_burn_flash_op_fw(); + if (ret) { + goto fail1; + } + update_info.progress = 4; + + ret = gcore_run_pram_fw(); + if (ret) { + goto fail1; + } + update_info.progress = 5; + + mdelay(1); + gdev_fwu->irq_enable(gdev_fwu); + + + gdev_fwu->fw_event = FW_UPDATE; + fw_update_fn.wait_int = true; + + ret = gcore_flash_op_request(READ_FLASH_ID); + if (ret) { + goto fail2; + } + update_info.progress = 6; + + mutex_unlock(&gdev_fwu->transfer_lock); + + if (!wait_for_completion_timeout(&fw_update_complete, 10 * HZ)) { + GTP_ERROR("update flashdownload timeout!"); + goto gcore_update_exit; + } + + return 0; + +fail1: + gdev_fwu->irq_enable(gdev_fwu); +fail2: + mutex_unlock(&gdev_fwu->transfer_lock); +gcore_update_exit: + GTP_ERROR("update flashdownload proc fail"); + gcore_flashdownload_cleanup(); + update_info.status = UPDATE_STATUS_IDLE; + + return -1; +} +EXPORT_SYMBOL(gcore_auto_update_flashdownload_proc); + +#endif //CONFIG_GCORE_AUTO_UPDATE_FW_FLASHDOWNLOAD + +u8 g_ret_update; + +void gcore_request_firmware_update_work(struct work_struct *work) +{ + static u8 *fw_buf; +#ifdef CONFIG_UPDATE_FIRMWARE_BY_BIN_FILE + const struct firmware *fw = NULL; + + if (IS_ERR_OR_NULL(fw_buf)) + fw_buf = (u8 *)kzalloc(FW_SIZE, GFP_KERNEL); + + if (ERR_ALLOC_MEM(fw_buf)) { + GTP_ERROR("fw buf mem allocate fail"); + } + + if (request_firmware(&fw, FW_BIN_NAME, &gdev_fwu->bus_device->dev)) { + GTP_ERROR("request firmware fail"); + return; + } + + memcpy(fw_buf, fw->data, fw->size); + + GTP_DEBUG("fw buf:%x %x %x %x %x %x", fw_buf[0], fw_buf[1], fw_buf[2], \ + fw_buf[3], fw_buf[4], fw_buf[5]); + + if (fw) { + release_firmware(fw); + } +#else + fw_buf = gcore_default_FW; +#endif + + g_ret_update = 0; + g_update_running = 1; + +#ifdef CONFIG_GCORE_AUTO_UPDATE_FW_FLASHDOWNLOAD + if (gcore_auto_update_flashdownload_proc(fw_buf)) { + GTP_ERROR("auto update flashdownload proc fail"); + } +#else + if (gcore_auto_update_hostdownload_proc(fw_buf)) { + GTP_ERROR("auto update hostdownload proc fail"); + } +#endif + + g_ret_update = 1; + g_update_running = 0; + + return; +} + +#ifdef CONFIG_ENABLE_FW_RAWDATA + +/* + * FW mode set sys interface + */ +static ssize_t fw_mode_set_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 mode = 0; + + if (!strncmp(buf, "-w", 2)) { + buf += 3; + if (!strncmp(buf, "fw_mode=", 8) && (sscanf(buf+8, "%d", &mode) == 1)) { + GTP_DEBUG("Set fw_mode is:%d", mode); + if (gcore_fw_mode_set_proc2(mode)) { + GTP_DEBUG("fw mode set proc fail"); + return -1; + } + } else { + GTP_DEBUG("Wrong parameters."); + return -1; + } + } + + return count; + +} + +#endif + + +/* + * FW reg rw sys interface + */ +static ssize_t fw_reg_rw_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 addr = 0; + u32 value = 0; + u32 length = 0; + u8 r_buff[20] = { 0 }; + + if (!strncmp(buf, "-w", 2)) { + buf += 3; + if (!strncmp(buf, "addr=", 5) && !strncmp(buf+14, "value=", 6) && \ + (sscanf(buf, "addr=%x value=%x", &addr, &value) == 2)) { + GTP_DEBUG("addr is:%x,value is:%x", addr, value); + if (gcore_fw_write_reg(addr, (u8 *)&value, 1)) { + GTP_ERROR("fw reg write fail"); + return -1; + } + } else { + GTP_ERROR("Wrong parameters."); + return -1; + } + } else if (!strncmp(buf, "-r", 2)) { + buf += 3; + if (!strncmp(buf, "addr=", 5) && !strncmp(buf+14, "len=", 4) && \ + (sscanf(buf, "addr=%x len=%d", &addr, &length) == 2)) { + GTP_DEBUG("addr is:%x,len is:%d", addr, length); + if (gcore_idm_read_reg(addr, r_buff, length)) { + GTP_ERROR("fw reg write fail"); + return -1; + } + GTP_DEBUG("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x", r_buff[0], r_buff[1], r_buff[2], \ + r_buff[3], r_buff[4], r_buff[5], r_buff[6], r_buff[7], r_buff[8], r_buff[9]); + } else { + GTP_ERROR("Wrong parameters."); + return -1; + } + } else if (!strncmp(buf, "-i", 2)) { + GTP_DEBUG("enter idm mode"); + gcore_enter_idm_mode(); + } else if (!strncmp(buf, "-o", 2)) { + GTP_DEBUG("exit idm mode"); + gcore_exit_idm_mode(); + } + + return count; + +} + + +/* + * FW update sys interface + */ +static ssize_t fw_update_flash_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + int enabled = 0; + + ret = kstrtoint(buf, 0, &enabled); + + if (enabled) { + GTP_DEBUG("fw update enable:%d", enabled); + + gcore_request_firmware_update_work(NULL); + + } else { + GTP_ERROR("fw update enable:%d", enabled); + } + + return count; + +} + +static ssize_t ret_update_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + if (g_ret_update) { + return sprintf(buf, "ok"); + } else { + return sprintf(buf, "fail"); + } +} + + +#ifdef CONFIG_DEBUG_SPI_SPEED + +static ssize_t spi_adjust(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct spi_device *spi; + + struct mt_chip_conf *chip_config; + + u32 high_time, low_time; + + spi = container_of(dev, struct spi_device, dev); + + chip_config = (struct mt_chip_conf *)spi->controller_data; + if (!chip_config) { + GTP_ERROR("chip_config is NULL"); + return -1; + } + + if (!buf) { + GTP_ERROR("buf is NULL"); + return -1; + } + + if (!strncmp(buf, "-w", 2)) { + buf += 3; + if (!strncmp(buf, "high_time=", 10) && (sscanf(buf+10, "%d", &high_time) == 1)) { + GTP_DEBUG("Set high_time is:%d", high_time); + chip_config->high_time = high_time; + } else if (!strncmp(buf, "low_time=", 9) && (sscanf(buf+9, "%d", &low_time) == 1)) { + GTP_DEBUG("Set low_time is:%d", low_time); + chip_config->low_time = low_time; + } else { + GTP_DEBUG("Wrong parameters."); + return -1; + } + } + + return count; +} + +#endif + +#ifdef CONFIG_GCORE_AUTO_UPDATE_FW_HOSTDOWNLOAD + +static ssize_t hostdl_idm_set_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + int enabled = 0; + u8 buff[2] = { 0 }; + + ret = kstrtoint(buf, 0, &enabled); + + if (enabled) { + GTP_DEBUG("hostdl idm enable:%d", enabled); + gcore_idm_read_id(buff, sizeof(buff)); + } else { + GTP_ERROR("hostdl idm enable:%d", enabled); + } + + return count; + +} + +#endif + +#ifdef CONFIG_ENABLE_GESTURE_WAKEUP +static ssize_t gesture_en_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "Gesture wakup is %s\n", + gdev_fwu->gesture_wakeup_en ? "enable":"disable"); +} + +static ssize_t gesture_en_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + u8 enable = 0; + + if (buf[0] == 'Y' || buf[0] == 'y' || buf[0] == '1') { + enable = 1; + } + if (enable) { + gdev_fwu->gesture_wakeup_en = true; + } else { + gdev_fwu->gesture_wakeup_en = false; + } + + return count; +} +static DEVICE_ATTR(gesture_en, S_IRUSR|S_IWUSR, gesture_en_show, gesture_en_store); + +#endif + +static ssize_t fw_dump_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + int enabled = 0; + + ret = kstrtoint(buf, 0, &enabled); + + if (enabled) { + GTP_DEBUG("fw save enable:%d", enabled); + + gcore_dump_fw_to_file(); + + } else { + GTP_ERROR("fw save enable:%d", enabled); + } + + return count; + +} + +#ifdef CONFIG_ENABLE_FW_RAWDATA +static DEVICE_ATTR(fw_mode, 0200, NULL, fw_mode_set_store); +#endif + +static DEVICE_ATTR(reg, 0200, NULL, fw_reg_rw_store); + +#ifdef CONFIG_DEBUG_SPI_SPEED +static DEVICE_ATTR(spi_speed, 0200, NULL, spi_adjust); +#endif + +static DEVICE_ATTR(fw_update, 0200, NULL, fw_update_flash_store); + +static DEVICE_ATTR(ret_update, S_IRUGO, ret_update_show, NULL); + +#ifdef CONFIG_GCORE_AUTO_UPDATE_FW_HOSTDOWNLOAD +static DEVICE_ATTR(idm, 0200, NULL, hostdl_idm_set_store); +#endif + +static DEVICE_ATTR(fw_dump, 0200, NULL, fw_dump_store); + +static struct device_attribute *gcore_attribute[] = { + &dev_attr_reg, +#ifdef CONFIG_DEBUG_SPI_SPEED + &dev_attr_spi_speed, +#endif +#ifdef CONFIG_ENABLE_FW_RAWDATA + &dev_attr_fw_mode, +#endif + &dev_attr_fw_update, + &dev_attr_ret_update, +#ifdef CONFIG_GCORE_AUTO_UPDATE_FW_HOSTDOWNLOAD + &dev_attr_idm, +#endif +#ifdef CONFIG_ENABLE_GESTURE_WAKEUP + &dev_attr_gesture_en, +#endif + &dev_attr_fw_dump, +}; + +int gcore_create_attribute(struct device *dev) +{ + int num = 0; + int i = 0; + int ret = 0; + + num = (int)ARRAY_SIZE(gcore_attribute); + + for (i = 0; i < num; i++) { + ret = device_create_file(dev, gcore_attribute[i]); + if (ret) { + break; + } + } + + return ret; +} +EXPORT_SYMBOL(gcore_create_attribute); + +static int fwu_event_handler(void *p) +{ + struct gcore_dev *gdev = (struct gcore_dev *)p; + struct sched_param param = {.sched_priority = 4}; + + sched_setscheduler(current, SCHED_RR, ¶m); + do { + set_current_state(TASK_INTERRUPTIBLE); + + wait_event_interruptible(gdev->wait, fw_update_fn.event_flag == true); + fw_update_fn.event_flag = false; + + if (mutex_is_locked(&gdev->transfer_lock)) { + GTP_DEBUG("fw event is locked, ignore"); + continue; + } + + mutex_lock(&gdev->transfer_lock); + + switch (gdev->fw_event) { + case FW_UPDATE: +#ifdef CONFIG_GCORE_AUTO_UPDATE_FW_HOSTDOWNLOAD + gcore_auto_update_hostdownload(gdev->firmware); +#else + gcore_flash_op_reply_proc(); +#endif + break; + + case FW_READ_REG: + gcore_fw_read_reg_reply(gdev->firmware, gdev->fw_xfer); + break; + + default: + break; + } + + mutex_unlock(&gdev->transfer_lock); + + } while (!kthread_should_stop()); + + return 0; +} + +int gcore_fw_update_fn_init(struct gcore_dev *gdev) +{ + gdev_fwu = gdev; + + fwu_thread = kthread_run(fwu_event_handler, gdev, "gcore_fwu"); + + if (gcore_create_attribute(&gdev->bus_device->dev)) { + GTP_ERROR("update fn create attribute fail"); + } + +#ifdef CONFIG_GCORE_AUTO_UPDATE_FW_HOSTDOWNLOAD +// gdev->fwu_workqueue = create_singlethread_workqueue("fwu_workqueue"); +// INIT_DELAYED_WORK(&gdev->fwu_work, gcore_request_firmware_update_work); + queue_delayed_work(gdev->fwu_workqueue, &gdev->fwu_work,\ + msecs_to_jiffies(STARTUP_UPDATE_DELAY_MS)); +#endif + + return 0; +} + +void gcore_fw_update_fn_remove(struct gcore_dev *gdev) +{ + int num = 0; + int i = 0; + + num = (int)ARRAY_SIZE(gcore_attribute); + + for (i = 0; i < num; i++) { + device_remove_file(&gdev->bus_device->dev, gcore_attribute[i]); + } + + kthread_stop(fwu_thread); + + return; +} + + +#if 0 + +static int __init gcore_fw_update_init(void) +{ + GTP_DEBUG("gcore_fw_update_init."); + + gcore_new_function_register(&fw_update_fn); + + return 0; +} + +static void __exit gcore_fw_update_exit(void) +{ + GTP_DEBUG("gcore_fw_update_exit."); + + gcore_new_function_unregister(&fw_update_fn); + + return; +} + +module_init(gcore_fw_update_init); +module_exit(gcore_fw_update_exit); + +MODULE_AUTHOR("GalaxyCore, Inc."); +MODULE_DESCRIPTION("GalaxyCore Touch FW Update Module"); +MODULE_LICENSE("GPL"); + +#endif diff --git a/drivers/input/touchscreen/gcore_mmi/gcore_drv_interface.c b/drivers/input/touchscreen/gcore_mmi/gcore_drv_interface.c new file mode 100644 index 000000000000..af11ddf69abd --- /dev/null +++ b/drivers/input/touchscreen/gcore_mmi/gcore_drv_interface.c @@ -0,0 +1,788 @@ +/* + * GalaxyCore touchscreen driver + * + * Copyright (C) 2021 GalaxyCore Incorporated + * + * Copyright (C) 2021 Neo Chen + * + * 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 "gcore_drv_common.h" +#include "gcore_ioctl.h" +#include +#include +#include +#include + + + +#define GCORE_PROC_FILE "gcore_app" + +#define GCORE_CHRDEV_NUM 2 + +struct gcore_dev *gdev_intf; +struct reg_msg msg_reg; + + +static int gcore_app_open(struct inode *inode, struct file *file); +static ssize_t gcore_app_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos); +static long gcore_app_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); + + +struct file_operations gcore_app_fops = { + .open = gcore_app_open, + .read = gcore_app_read, + .write = NULL, + .unlocked_ioctl = gcore_app_ioctl, + .compat_ioctl = gcore_app_ioctl, +}; + + +struct proc_dir_entry *gcore_proc_entry; + + +int gcore_app_open(struct inode *inode, struct file *file) +{ + GTP_DEBUG("gcore_cdev_open."); + + file->private_data = gdev_intf; + + return 0; +} + +ssize_t gcore_app_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + int ret = 0; + struct gcore_dev *gdev = file->private_data; + u8 *touch_data_p = gdev->touch_data; +// int i = 0; + + gdev->usr_read = true; + + GTP_DEBUG("app read before sleep."); + while (gdev->data_ready == false) { + if (wait_event_interruptible(gdev->usr_wait, gdev->data_ready == true)) + return -ERESTARTSYS; + } + GTP_DEBUG("app read after sleep."); +/* + for (i = 0; i < 576; i++) + { + *(touch_data_p + 65 + 2*i) = (i & 0xFF); + *(touch_data_p + 65 + 2*i + 1) = ((i & 0xFF00) >> 8); + } + + GTP_DEBUG("read data36 37 %x %x", *(touch_data_p+65+36), *(touch_data_p+65+37)); + + GTP_DEBUG("read data38 39 %x %x", *(touch_data_p+65+38), *(touch_data_p+65+39)); + + GTP_DEBUG("read data72 73 %x %x", *(touch_data_p+65+72), *(touch_data_p+65+73)); + + GTP_DEBUG("read data74 75 %x %x", *(touch_data_p+65+74), *(touch_data_p+65+75)); +*/ + ret = copy_to_user(buffer, touch_data_p + DEMO_DATA_SIZE, RAW_DATA_SIZE); + + gdev->usr_read = false; + gdev->data_ready = false; + + return RAW_DATA_SIZE; + +} + +ssize_t gcore_app_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + return count; +} + +long gcore_app_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct gcore_dev *gdev = filp->private_data; + u8 *fw_buf = NULL; + u8 read_data[4] = { 0 }; + u8 version[20] = { 0 }; + u8 rawdata_res[10] = { 0 }; + int ret = 0; + + GTP_DEBUG("IOCTL CMD:%x", cmd); + + if (_IOC_TYPE(cmd) != GALAXYCORE_MAGIC_NUMBER) { + GTP_ERROR("command type [%c] error!", _IOC_TYPE(cmd)); + return -ENOTTY; + } + + if (_IOC_NR(cmd) > GALAXYCORE_MAX_NR) { + GTP_ERROR("command numer [%d] exceeded!", _IOC_NR(cmd)); + return -ENOTTY; + } + + switch (cmd) { + case IOC_APP_READ_FW_VERSION: + + GTP_DEBUG("App ioctl get fw version"); + + gcore_read_fw_version(read_data, sizeof(read_data)); + GTP_DEBUG("app read data %x %x %x %x", read_data[0], read_data[1], read_data[2], read_data[3]); + + ret = snprintf(version, sizeof(version), "%d.%d.%d.%d", read_data[1], read_data[0], read_data[3], read_data[2]); + + if (copy_to_user((char *)arg, version, strlen(version) + 1)) { + return -EFAULT; + } + + break; + + case IOC_APP_UPDATE_FW: + fw_buf = (u8 *)kzalloc(FW_SIZE, GFP_KERNEL); + if (fw_buf == NULL) { + GTP_ERROR("fw data mem allocate fail"); + return -ENOMEM; + } + + if (copy_from_user(fw_buf, (char *)arg, FW_SIZE)) { + GTP_ERROR("copy fw_buf from user fail"); + return -EFAULT; + } + + GTP_DEBUG("fw_buf: %x %x %x %x", fw_buf[0], fw_buf[1], \ + fw_buf[2], fw_buf[3]); + + +#ifdef CONFIG_GCORE_AUTO_UPDATE_FW_FLASHDOWNLOAD + if (gcore_auto_update_flashdownload_proc(fw_buf)) { + GTP_ERROR("app flashdownload proc fail"); + kfree(fw_buf); + return -1; + } +#else + if (gcore_auto_update_hostdownload_proc(fw_buf)) { + GTP_ERROR("app hostdownload proc fail"); + kfree(fw_buf); + return -1; + } +#endif + + if (fw_buf != NULL) { + kfree(fw_buf); + fw_buf = NULL; + } + + break; + + case IOC_APP_DEMO: + mutex_lock(&gdev->transfer_lock); + gdev->irq_disable(gdev); + + if (gcore_fw_mode_set_proc2(0)) { + GTP_ERROR("fw mode set demo proc fail"); + return -1; + } + + gdev->irq_enable(gdev); + mutex_unlock(&gdev->transfer_lock); + + break; + + case IOC_APP_DEMO_RAWDATA: + mutex_lock(&gdev->transfer_lock); + gdev->irq_disable(gdev); + + if (gcore_fw_mode_set_proc2(2)) { + GTP_ERROR("fw mode set demo rawdata proc fail"); + return -1; + } + + gdev->irq_enable(gdev); + mutex_unlock(&gdev->transfer_lock); + + break; + + case IOC_APP_READ_REG: + ret = copy_from_user(&msg_reg, (struct reg_msg __user *)arg, sizeof(msg_reg)); + if (ret) { + GTP_ERROR("READ REG:copy from user fail."); + return -1; + } + + GTP_DEBUG("read reg: addr(%x) len(%d)", msg_reg.addr, msg_reg.length); + + gcore_fw_read_reg(msg_reg.addr, msg_reg.buffer, msg_reg.length); + + GTP_DEBUG("read msg buffer %x %x", msg_reg.buffer[0], msg_reg.buffer[1]); + + ret = copy_to_user((struct reg_msg __user *)arg, &msg_reg, sizeof(msg_reg)); + if (ret) { + return -1; + } + + break; + + case IOC_APP_WRITE_REG: + ret = copy_from_user(&msg_reg, (struct reg_msg __user *)arg, sizeof(msg_reg)); + if (ret) { + GTP_ERROR("WRITE REG:copy from user fail."); + return -1; + } + + GTP_DEBUG("write reg: addr(%x) len(%d)", msg_reg.addr, msg_reg.length); + + ret = gcore_fw_write_reg(msg_reg.addr, msg_reg.buffer, msg_reg.length); + if (ret) { + GTP_ERROR("app ioctl write reg fail"); + return -1; + } + + break; + + case IOC_APP_GET_RAWDATA_RES: + + GTP_DEBUG("App ioctl get rawdata xy"); + + snprintf(rawdata_res, sizeof(rawdata_res), "%d*%d", RAWDATA_ROW, RAWDATA_COLUMN); + + if (copy_to_user((char *)arg, rawdata_res, strlen(rawdata_res) + 1)) { + return -EFAULT; + } + + break; + + case IOC_APP_START_MP_TEST: + + GTP_DEBUG("App ioctl start mp test"); + + if (gcore_start_mp_test()) { + GTP_ERROR("App ioctl mp test fail!"); + return -1; + } + + break; + + default: + GTP_ERROR("ioctl unknow cmd!"); + return -ENOTTY; + break; + } + + return 0; +} + + +struct proc_dir_entry *gcore_mp_entry = NULL; + +#define GCORE_MP_FILE "gcore_mp" + +static ssize_t gcore_selftest_read(struct file *file, char __user *buffer, \ + size_t count, loff_t *ppos); + + +struct file_operations gcore_selftest_fops = { + .read = gcore_selftest_read, +}; + +ssize_t gcore_selftest_read(struct file *file, char __user *buffer, \ + size_t count, loff_t *ppos) +{ + int ret = 0; + u8 result = 0; + + GTP_DEBUG("gcore selftest read enter."); + + ret = gcore_start_mp_test(); + if (ret) { + result = 1; + GTP_DEBUG("selftest failed!"); + } else { + result = 0; + GTP_DEBUG("selftest success!"); + } + + ret = copy_to_user(buffer, &result, 1); + + return 1; +} + +int gcore_app_node_init(void) +{ + gcore_proc_entry = proc_create(GCORE_PROC_FILE, 0644, NULL, &gcore_app_fops); + if (gcore_proc_entry == NULL) { + GTP_ERROR("create proc entry gcore_app failed"); + return -1; + } + + gcore_mp_entry = proc_create(GCORE_MP_FILE, 0644, NULL, &gcore_selftest_fops); + if (gcore_mp_entry == NULL) { + GTP_ERROR("create proc entry gcore_mp failed"); + return -1; + } + + return 0; +} + +void gcore_app_node_deinit(void) +{ + if (gcore_proc_entry != NULL) { + remove_proc_entry(GCORE_PROC_FILE, NULL); + } + + if (gcore_mp_entry != NULL) { + remove_proc_entry(GCORE_MP_FILE, NULL); + } +} + +static int gcore_interface_fn_init(struct gcore_dev *gdev); +static void gcore_interface_fn_remove(struct gcore_dev *gdev); + +struct gcore_exp_fn fs_interf_fn = { + .fn_type = GCORE_FS_INTERFACE, + .wait_int = false, + .init = gcore_interface_fn_init, + .remove = gcore_interface_fn_remove, +}; + + +/****************** Tool **************************/ + +#define TOOL_HEADER_SIZE 8 + +static int gcore_tool_open(struct inode *inode, struct file *file); +static ssize_t gcore_tool_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos); +static ssize_t gcore_tool_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos); +static long gcore_tool_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); + +struct file_operations gcore_tool_fops = { + .open = gcore_tool_open, + .read = gcore_tool_read, + .write = gcore_tool_write, + .unlocked_ioctl = gcore_tool_ioctl, + .compat_ioctl = gcore_tool_ioctl, +}; + +enum TOOL_MODE { + LINE, + ISP +}; + +struct gcore_tool_data { + dev_t devno; + struct cdev gcore_cdev; + struct class *gcore_class; + struct device *gcore_dev; + + u8 *isp_reply_data; + u8 *tool_isp_buf; + + enum TOOL_MODE tmode; +}; + +struct gcore_tool_data tool_info = { + .isp_reply_data = NULL, + .tool_isp_buf = NULL, + .tmode = LINE, +}; + +int gcore_tool_line_read(struct gcore_dev *gdev, char __user *buffer) +{ + int ret = 0; + char tool_header[TOOL_HEADER_SIZE] = { 0 }; + int data_size = DEMO_DATA_SIZE; + u8 data_length_h = 0; + u8 data_length_l = 0; + u8 *touch_data_p = gdev->touch_data; + +// struct sched_param param = {.sched_priority = 4}; +// sched_setscheduler(current, SCHED_RR, ¶m); + + gdev->usr_read = true; + + GTP_DEBUG("read before sleep."); + while (gdev->data_ready == false) { + if (wait_event_interruptible(gdev->usr_wait, gdev->data_ready == true)) + return -ERESTARTSYS; + } + GTP_DEBUG("read after sleep."); + + tool_header[0] = 0x40; + tool_header[1] = 0xB1; + tool_header[2] = gdev->fw_mode; + + if (gdev->fw_mode == DEMO) { + data_size = DEMO_DATA_SIZE; + } else if (gdev->fw_mode == RAWDATA) { + data_size = RAW_DATA_SIZE; + } else if (gdev->fw_mode == DEMO_RAWDATA) { + data_size = DEMO_RAWDATA_SIZE; + } + + data_length_h = (u8)(data_size >> 8); + data_length_l = (u8)(data_size); + + tool_header[3] = 0x00; + tool_header[4] = 0x73; + tool_header[5] = 0x71; + tool_header[6] = data_length_h; + tool_header[7] = data_length_l; + + ret = copy_to_user(buffer, tool_header, 8); + ret = copy_to_user(buffer + 8, touch_data_p, data_size); + + gdev->data_ready = false; + gdev->usr_read = false; + + return data_size + TOOL_HEADER_SIZE; + +} + +int gcore_tool_isp_read(struct gcore_dev *gdev, char __user *buffer) +{ + int ret; + + GTP_DEBUG("isp read before sleep."); + while (fs_interf_fn.wait_int == false) { + if (wait_event_interruptible(gdev->wait, fs_interf_fn.wait_int == true)) + return -ERESTARTSYS; + } + GTP_DEBUG("isp read after sleep."); + + ret = gcore_bus_read(tool_info.isp_reply_data, 64); + if (ret) { + GTP_ERROR("write cmd read flash id error."); + return -1; + } + + ret = copy_to_user(buffer, tool_info.isp_reply_data, 64); + + return 64; +} + + +int tool_mode_is_isp(void) +{ + return (tool_info.tmode == ISP) ? 1 : 0; +} + +int gcore_tool_open(struct inode *inode, struct file *file) +{ + GTP_DEBUG("gcore_cdev_open."); + + tool_info.isp_reply_data = (u8 *)kzalloc(1024, GFP_KERNEL); + if (tool_info.isp_reply_data == NULL) { + GTP_ERROR("flash_op_buf mem allocate fail!"); + return -ENOMEM; + } + + tool_info.tool_isp_buf = (u8 *)kzalloc(1024, GFP_KERNEL); + if (tool_info.tool_isp_buf == NULL) { + GTP_ERROR("flash_op_buf mem allocate fail!"); + return -ENOMEM; + } + + file->private_data = gdev_intf; + + return 0; +} + +ssize_t gcore_tool_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) +{ + struct gcore_dev *gdev = file->private_data; + int ret = 0; + + switch (tool_info.tmode) { + case LINE: + ret = gcore_tool_line_read(gdev, buffer); + break; + + case ISP: + ret = gcore_tool_isp_read(gdev, buffer); + break; + + default: + break; + } + + return ret; + +} + +ssize_t gcore_tool_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos) +{ + int ret = 0; +// u8 buff_send[1024] = { 0 }; + + ret = copy_from_user(tool_info.tool_isp_buf, buffer, count); +// GTP_DEBUG("buff_send %x %x %x %x", buff_send[0], buff_send[1], buff_send[2], buff_send[3]); + GTP_DEBUG("count=%d", (int)count); + ret = gcore_bus_write(tool_info.tool_isp_buf, count); + if (ret) { + GTP_ERROR("write cmd read flash id error."); + return -1; + } + + return count; +} + +long gcore_tool_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ +// int ret = 0; + int idm_op = 0; + + GTP_DEBUG("IOCTL CMD:%x", cmd); + + if (_IOC_TYPE(cmd) != GALAXYCORE_MAGIC_NUMBER) { + GTP_ERROR("command type [%c] error!", _IOC_TYPE(cmd)); + return -ENOTTY; + } + + if (_IOC_NR(cmd) > GALAXYCORE_MAX_NR) { + GTP_ERROR("command numer [%d] exceeded!", _IOC_NR(cmd)); + return -ENOTTY; + } + + switch (cmd) { + case IOC_DEBUG_TIME_RST0: + GTP_DEBUG("set rst low"); + + break; + + case IOC_DEBUG_TIME_RST1: + GTP_DEBUG("set rst high"); + + break; + + case IOC_TOOL_MODE: + tool_info.tmode = (int)arg; + /* + ret = copy_from_user(&tool_info.tmode, &(int __user)arg, sizeof(int)); + if (ret) + { + return -EFAULT; + } + */ + + break; + + case IOC_TOOL_IDM_OPERATION: + idm_op = (int)arg; + if (idm_op == 0x01) { +// gcore_enter_idm_mode(); + } else if (idm_op == 0x02) { +// gcore_exit_idm_mode(); + } + + break; + + default: + return -ENOTTY; + + break; + } + + return 0; +} + +int gcore_tool_node_init(void) +{ + alloc_chrdev_region(&tool_info.devno, 0, GCORE_CHRDEV_NUM, "gcore"); + + cdev_init(&tool_info.gcore_cdev, &gcore_tool_fops); + + if (cdev_add(&tool_info.gcore_cdev, tool_info.devno, 1) < 0) { + GTP_ERROR("cdev_add fail!"); + goto cdev_add_fail; + } + + tool_info.gcore_class = class_create(THIS_MODULE, "gcore"); + if (IS_ERR(tool_info.gcore_class)) { + GTP_ERROR("class create fail!"); + goto cls_cre_fail; + } + + tool_info.gcore_dev = device_create(tool_info.gcore_class, NULL, tool_info.devno, NULL, "gcore"); + if (IS_ERR(tool_info.gcore_dev)) { + GTP_ERROR("device create fail!"); + goto dev_cre_fail; + } + + if (gcore_create_attribute(tool_info.gcore_dev)) { + GTP_ERROR("tool init create attribute fail"); + } + + return 0; + +dev_cre_fail: + class_destroy(tool_info.gcore_class); +cls_cre_fail: + cdev_del(&tool_info.gcore_cdev); +cdev_add_fail: + unregister_chrdev_region(tool_info.devno, 1); + return -1; +} + +void gcore_tool_node_deinit(void) +{ + device_destroy(tool_info.gcore_class, tool_info.devno); + class_destroy(tool_info.gcore_class); + cdev_del(&tool_info.gcore_cdev); + unregister_chrdev_region(tool_info.devno, 1); +} + +static ssize_t path_show(struct device *pDevice, struct device_attribute *pAttr, \ + char *pBuf) +{ + ssize_t blen; + const char *path; + + path = kobject_get_path(&gdev_intf->bus_device->dev.kobj, GFP_KERNEL); + blen = scnprintf(pBuf, PAGE_SIZE, "%s\n", path ? path : "na"); + kfree(path); + + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, struct device_attribute *attr, \ + char *buf) +{ + GTP_DEBUG("*** %s() vendor = %s ***", __func__, "galaxycore"); + + return scnprintf(buf, PAGE_SIZE, "galaxygcore\n"); +} + +/* Attribute: ic_ver (RO) */ +static ssize_t ic_ver_show(struct device *dev, struct device_attribute *attr, \ + char *buf) +{ + u8 read_data[4] = { 0 }; + + gcore_read_fw_version(read_data, sizeof(read_data)); + + return scnprintf(buf, PAGE_SIZE, "FW Ver:%d.%d.%d.%d\n", \ + read_data[1], read_data[0], read_data[3], read_data[2]); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_RO(ic_ver), + __ATTR_NULL +}; + + +static struct class *touchscreen_class; +static struct device *touchscreen_class_dev; + +int gcore_sys_node_init(void) +{ + int i = 0; + int ret = 0; + struct device_attribute *attrs = touchscreen_attributes; + + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + GTP_ERROR("create touchscreen class failed"); + return -1; + } + + touchscreen_class_dev = device_create(touchscreen_class, NULL, MKDEV(MAJOR(tool_info.devno), 1), \ + NULL, "ts_gcore"); + if (IS_ERR(touchscreen_class_dev)) { + GTP_ERROR("create touchscreen device failed"); + return -1; + } + + for (i = 0; attrs[i].attr.name != NULL; i++) { + ret = device_create_file(touchscreen_class_dev, &attrs[i]); + if (ret) { + break; + } + } + + return ret; + +} + +void gcore_sys_node_deinit(void) +{ + device_destroy(touchscreen_class, MKDEV(MAJOR(tool_info.devno), 1)); + class_destroy(touchscreen_class); +} + +int gcore_interface_fn_init(struct gcore_dev *gdev) +{ + GTP_DEBUG("gcore_interface_fn_init."); + + gdev_intf = gdev; + + if (gcore_app_node_init()) { + GTP_ERROR("app node init failed"); + return -1; + } + + if (gcore_tool_node_init()) { + GTP_ERROR("tool node init failed"); + goto fail1; + } + + if (gcore_sys_node_init()) { + GTP_ERROR("sys node init failed"); + goto fail2; + } + + return 0; + +fail2: + gcore_tool_node_deinit(); +fail1: + gcore_app_node_deinit(); + + return -1; +} + +void gcore_interface_fn_remove(struct gcore_dev *gdev) +{ + GTP_DEBUG("gcore_interface_fn_remove."); + + gcore_app_node_deinit(); + gcore_tool_node_deinit(); + gcore_sys_node_deinit(); + + return; +} + + +#if 0 + +static int __init gcore_interface_init(void) +{ + + GTP_DEBUG("gcore_interface_init."); + + gcore_new_function_register(&fs_interf_fn); + + return 0; +} + +static void __exit gcore_interface_exit(void) +{ + GTP_DEBUG("gcore_interface_exit."); + + gcore_new_function_unregister(&fs_interf_fn); + + return; +} + +module_init(gcore_interface_init); +module_exit(gcore_interface_exit); + +MODULE_AUTHOR("GalaxyCore, Inc."); +MODULE_DESCRIPTION("GalaxyCore Drv FS Interface."); +MODULE_LICENSE("GPL"); + +#endif diff --git a/drivers/input/touchscreen/gcore_mmi/gcore_drv_main.c b/drivers/input/touchscreen/gcore_mmi/gcore_drv_main.c new file mode 100755 index 000000000000..957b44e522c0 --- /dev/null +++ b/drivers/input/touchscreen/gcore_mmi/gcore_drv_main.c @@ -0,0 +1,1597 @@ +/* + * GalaxyCore touchscreen driver + * + * Copyright (C) 2021 GalaxyCore Incorporated + * + * Copyright (C) 2021 Neo Chen + * + * 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 "gcore_drv_common.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0)) + || (LINUX_VERSION_CODE > KERNEL_VERISON(4, 10, 0)) +#include +#endif +#ifdef CONFIG_DRM +#include + +struct drm_panel *gcore_active_panel; +#endif + + +#define GTP_TRANS_X(x, tpd_x_res, lcm_x_res) (((x)*(lcm_x_res))/(tpd_x_res)) +#define GTP_TRANS_Y(y, tpd_y_res, lcm_y_res) (((y)*(lcm_y_res))/(tpd_y_res)) + + +struct gcore_exp_fn_data fn_data = { + .initialized = false, + .fn_inited = false, +}; + +struct gcore_exp_fn *p_fwu_fn = &fw_update_fn; +struct gcore_exp_fn *p_intf_fn = &fs_interf_fn; +struct gcore_exp_fn *p_mp_fn = &mp_test_fn; + +#ifdef GCORE_SENSOR_EN +static struct sensors_classdev __maybe_unused sensors_touch_cdev = { + + .name = "dt-gesture", + .vendor = "gcore", + .version = 1, + .type = SENSOR_TYPE_MOTO_DOUBLE_TAP, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + /* WAKE_UP & SPECIAL_REPORT */ + .flags = 1 | 6, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#define REPORT_MAX_COUNT 10000 +#endif + +#ifdef GCORE_SENSOR_EN +static int gcore_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + GTP_DEBUG("Gesture set enable %d!", enable); + mutex_lock(&fn_data.gdev->state_mutex); + if (enable == 1) { + fn_data.gdev->should_enable_gesture = true; + } else if (enable == 0) { + fn_data.gdev->should_enable_gesture = false; + } else { + GTP_DEBUG("unknown enable symbol\n"); + } + mutex_unlock(&fn_data.gdev->state_mutex); + return 0; +} + +static int gcore_sensor_init(struct gcore_dev *data) +{ + struct gcore_sensor_platform_data *sensor_pdata; + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + GTP_ERROR("Failed to allocate device"); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct gcore_sensor_platform_data), + GFP_KERNEL); + if (!sensor_pdata) { + GTP_ERROR("Failed to allocate memory"); + goto free_sensor_pdata; + } + data->sensor_pdata = sensor_pdata; + + if (data->report_gesture_key) { + __set_bit(EV_KEY, sensor_input_dev->evbit); + __set_bit(KEY_F1, sensor_input_dev->keybit); + } else { + __set_bit(EV_ABS, sensor_input_dev->evbit); + input_set_abs_params(sensor_input_dev, ABS_DISTANCE, + 0, REPORT_MAX_COUNT, 0, 0); + } + __set_bit(EV_SYN, sensor_input_dev->evbit); + + sensor_input_dev->name = "double-tap"; + data->sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + GTP_ERROR("Unable to register device, err=%d", err); + goto free_sensor_input_dev; + } + + sensor_pdata->ps_cdev = sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = gcore_sensor_set_enable; + sensor_pdata->data = data; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(data->sensor_pdata->input_sensor_dev); +free_sensor_input_dev: + input_free_device(data->sensor_pdata->input_sensor_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, sensor_pdata); + data->sensor_pdata = NULL; +exit: + return 1; +} + +int gcore_sensor_remove(struct gcore_dev *data) +{ + sensors_classdev_unregister(&data->sensor_pdata->ps_cdev); + input_unregister_device(data->sensor_pdata->input_sensor_dev); + devm_kfree(&data->sensor_pdata->input_sensor_dev->dev, + data->sensor_pdata); + + data->sensor_pdata = NULL; + data->wakeable = false; + data->should_enable_gesture = false; + + return 0; +} +#endif + +#if 0 + +void gcore_new_function_register(struct gcore_exp_fn *exp_fn) +{ + GTP_DEBUG("gcore_new_function_register fn: %d", exp_fn->fn_type); + + if (!fn_data.initialized) { + INIT_LIST_HEAD(&fn_data.list); + fn_data.initialized = true; + } + + list_add_tail(&exp_fn->link, &fn_data.list); + + if (fn_data.fn_inited) { + if (exp_fn->init != NULL) { + exp_fn->init(fn_data.gdev); + } + } + + return; +} +EXPORT_SYMBOL(gcore_new_function_register); + +void gcore_new_function_unregister(struct gcore_exp_fn *exp_fn) +{ + struct gcore_exp_fn *fn = NULL; + struct gcore_exp_fn *fn_temp = NULL; + + GTP_DEBUG("gcore_new_function_unregister fn: %d", exp_fn->fn_type); + + if (!list_empty(&fn_data.list)) { + list_for_each_entry_safe(fn, fn_temp, &fn_data.list, link) { + if ((fn == exp_fn) && (fn->remove != NULL)) { + fn->remove(fn_data.gdev); + list_del(&fn->link); + } + } + } +} +EXPORT_SYMBOL(gcore_new_function_unregister); + +#endif + +void gcore_irq_enable(struct gcore_dev *gdev) +{ + unsigned long flags; + + spin_lock_irqsave(&gdev->irq_flag_lock, flags); + + if (gdev->irq_flag == 0) { + gdev->irq_flag = 1; + spin_unlock_irqrestore(&gdev->irq_flag_lock, flags); + enable_irq(gdev->touch_irq); + } else if (gdev->irq_flag == 1) { + spin_unlock_irqrestore(&gdev->irq_flag_lock, flags); + GTP_ERROR("Touch Eint already enabled!"); + } else { + spin_unlock_irqrestore(&gdev->irq_flag_lock, flags); + GTP_ERROR("Invalid irq_flag %d!", gdev->irq_flag); + } + /*GTP_DEBUG("Enable irq_flag=%d",g_touch.irq_flag);*/ + +} + +void gcore_irq_disable(struct gcore_dev *gdev) +{ + unsigned long flags; + + spin_lock_irqsave(&gdev->irq_flag_lock, flags); + + if (gdev->irq_flag == 0) { + spin_unlock_irqrestore(&gdev->irq_flag_lock, flags); + GTP_ERROR("Touch Eint already disable!"); + return; + } + + gdev->irq_flag = 0; + + spin_unlock_irqrestore(&gdev->irq_flag_lock, flags); + + disable_irq(gdev->touch_irq); +// disable_irq_nosync(gdev->touch_irq); + +} + +#ifndef CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM +int gcore_get_gpio_info(struct gcore_dev *gdev, struct device_node *node) +{ + int flag = 0; + + gdev->irq_gpio = of_get_named_gpio_flags(node, "gcore,irq-gpio", 0, (enum of_gpio_flags *)&flag); + if (!gpio_is_valid(gdev->irq_gpio)) { + GTP_ERROR("invalid gcore,irq-gpio:%d", gdev->irq_gpio); + return -1; + } + + if (gpio_request(gdev->irq_gpio, "gcore,irq-gpio")) { + GTP_ERROR("gpio %d request failed!", gdev->irq_gpio); + return -1; + } + + gdev->rst_gpio = of_get_named_gpio_flags(node, "gcore,rst-gpio", 0, (enum of_gpio_flags *)&flag); + if (!gpio_is_valid(gdev->rst_gpio)) { + GTP_ERROR("invalid gcore,reset-gpio:%d", gdev->rst_gpio); + return -1; + } + + if (gpio_request(gdev->rst_gpio, "gcore,rst-gpio")) { + GTP_ERROR("gpio %d request failed!", gdev->rst_gpio); + return -1; + } + +#if defined(CONFIG_TOUCH_DRIVER_RUN_ON_SPRD_PLATFORM) && defined(CONFIG_GESTURE_SPECIAL_INT) + gdev->ges_irq_gpio = of_get_named_gpio_flags(node, "ges,irq-gpio", 0, (enum of_gpio_flags *)&flag); + if (!gpio_is_valid(gdev->ges_irq_gpio)) { + GTP_ERROR("invalid ts,ges_irq_gpio:%d", gdev->ges_irq_gpio); + return -1; + } + + if (gpio_request(gdev->ges_irq_gpio, "ges,irq-gpio")) { + GTP_ERROR("gpio %d request failed!", gdev->ges_irq_gpio); + return -1; + } else { + gpio_set_debounce(gdev->ges_irq_gpio, 2); + } +#endif + +#ifdef GCORE_SENSOR_EN + if (of_property_read_bool(node, "gcore,report_gesture_key")) { + GTP_DEBUG("gcore,report_gesture_key set"); + gdev->report_gesture_key = 1; + } else { + gdev->report_gesture_key = 0; + } +#endif + + return 0; + +} + +void gcore_free_gpio(struct gcore_dev *gdev) +{ + gpio_free(gdev->irq_gpio); + gpio_free(gdev->rst_gpio); +} + +#endif + +#ifdef CONFIG_DRM +int gcore_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 -ENODEV; + + 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)) { + gcore_active_panel = panel; + GTP_DEBUG("%s:find\n", __func__); + return 0; + } + } + + GTP_ERROR("%s: not find\n", __func__); + return -ENODEV; +} +#endif + +void gcore_get_dts_info(struct gcore_dev *gdev) +{ +#if defined(CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM) + gdev->rst_gpio = GTP_RST_PORT; + gdev->irq_gpio = GTP_INT_PORT; +#elif defined(CONFIG_TOUCH_DRIVER_RUN_ON_RK_PLATFORM) \ + || defined(CONFIG_TOUCH_DRIVER_RUN_ON_SPRD_PLATFORM) \ + || defined(CONFIG_TOUCH_DRIVER_RUN_ON_QCOM_PLATFORM) + struct device_node *node = NULL; +/* struct device_node *node = client->dev.of_node; */ + + node = of_find_matching_node(node, tpd_of_match); + + if (gcore_get_gpio_info(gdev, node)) { + GTP_ERROR("dts get gpio info fail!"); + } +#endif +} + +int gcore_input_device_init(struct gcore_dev *gdev) +{ + //int tpd_res_x = TOUCH_SCREEN_X_MAX; + //int tpd_res_y = TOUCH_SCREEN_Y_MAX; + //int max_touch_num = GTP_MAX_TOUCH; + +#if defined(CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM) + gdev->input_device = tpd->dev; + + set_bit(EV_ABS, gdev->input_device->evbit); + set_bit(EV_KEY, gdev->input_device->evbit); + set_bit(EV_SYN, gdev->input_device->evbit); + set_bit(INPUT_PROP_DIRECT, gdev->input_device->propbit); + +#ifdef CONFIG_ENABLE_GESTURE_WAKEUP + set_bit(GESTURE_KEY, gdev->input_device->keybit); +#endif + +#ifdef CONFIG_ENABLE_TYPE_B_PROCOTOL + if (input_mt_init_slots(gdev->input_device, GTP_MAX_TOUCH, INPUT_MT_DIRECT)) { + GTP_ERROR("mt slot init fail."); + } +#else + /* for single-touch device */ + set_bit(BTN_TOUCH, gdev->input_device->keybit); +#endif + + return 0; +#elif defined(CONFIG_TOUCH_DRIVER_RUN_ON_RK_PLATFORM) \ + || defined(CONFIG_TOUCH_DRIVER_RUN_ON_SPRD_PLATFORM) \ + || defined(CONFIG_TOUCH_DRIVER_RUN_ON_QCOM_PLATFORM) + int tpd_res_x = TOUCH_SCREEN_X_MAX; + int tpd_res_y = TOUCH_SCREEN_Y_MAX; + //int max_touch_num = GTP_MAX_TOUCH; + + GTP_DEBUG("touch input device init."); + + gdev->input_device = input_allocate_device(); + if (gdev->input_device == NULL) { + GTP_ERROR("fail to allocate touch input device."); + return -1; + } + + gdev->input_device->name = GTP_DRIVER_NAME; + set_bit(EV_ABS, gdev->input_device->evbit); + set_bit(EV_KEY, gdev->input_device->evbit); + set_bit(EV_SYN, gdev->input_device->evbit); + set_bit(INPUT_PROP_DIRECT, gdev->input_device->propbit); + +#ifdef CONFIG_ENABLE_GESTURE_WAKEUP + set_bit(GESTURE_KEY, gdev->input_device->keybit); +#endif + + input_set_abs_params(gdev->input_device, ABS_MT_POSITION_X, 0, tpd_res_x, 0, 0); + input_set_abs_params(gdev->input_device, ABS_MT_POSITION_Y, 0, tpd_res_y, 0, 0); + +#ifdef CONFIG_ENABLE_TYPE_B_PROCOTOL + if (input_mt_init_slots(gdev->input_device, GTP_MAX_TOUCH, 0)) { + GTP_ERROR("mt slot init fail."); + } +#else + /* for single-touch device */ + set_bit(BTN_TOUCH, gdev->input_device->keybit); +#endif + + input_set_drvdata(gdev->input_device, gdev); + + if (input_register_device(gdev->input_device)) { + GTP_ERROR("input register device fail."); + return -1; + } + + return 0; +#endif +} + +void gcore_input_device_deinit(struct gcore_dev *gdev) +{ +#if defined(CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM) + return; +#elif defined(CONFIG_TOUCH_DRIVER_RUN_ON_RK_PLATFORM) \ + || defined(CONFIG_TOUCH_DRIVER_RUN_ON_SPRD_PLATFORM) \ + || defined(CONFIG_TOUCH_DRIVER_RUN_ON_QCOM_PLATFORM) + input_unregister_device(gdev->input_device); +#endif +} + +void gcore_touch_down(struct input_dev *dev, s32 x, s32 y, u8 id) +{ +#ifdef CONFIG_ENABLE_TYPE_B_PROCOTOL + input_mt_slot(dev, id); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); + input_report_abs(dev, ABS_MT_POSITION_X, x); + input_report_abs(dev, ABS_MT_POSITION_Y, y); +#ifdef CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM + input_report_abs(dev, ABS_MT_PRESSURE, 100); +#endif + +#else + input_report_key(dev, BTN_TOUCH, 1); //for single-touch device + input_report_abs(dev, ABS_MT_POSITION_X, x); + input_report_abs(dev, ABS_MT_POSITION_Y, y); + input_mt_sync(dev); +#endif +} + +void gcore_touch_up(struct input_dev *dev, u8 id) +{ +#ifdef CONFIG_ENABLE_TYPE_B_PROCOTOL + input_mt_slot(dev, id); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, false); +#else + input_report_key(dev, BTN_TOUCH, 0); //for single-touch device + input_mt_sync(dev); +#endif +} + +void gcore_touch_release_all_point(struct input_dev *dev) +{ + int i = 0; + + GTP_DEBUG("gcore touch release all point"); + +#ifdef CONFIG_ENABLE_TYPE_B_PROCOTOL + for (i = 0; i < GTP_MAX_TOUCH; i++) { + gcore_touch_up(dev, i); + } + + input_report_key(dev, BTN_TOUCH, 0); + input_report_key(dev, BTN_TOOL_FINGER, 0); +#else + gcore_touch_up(dev, 0); +#endif + + input_sync(dev); +} + + +#ifdef CONFIG_ENABLE_GESTURE_WAKEUP +void gcore_gesture_event_handler(struct gcore_dev *gdev, int id) +{ + +#ifdef GCORE_SENSOR_EN + static int report_cnt = 0; +#endif + + if ((!id) || (id >= GESTURE_MAX)) { + return; + } + + GTP_DEBUG("gesture id = %d", id); + + switch (id) { + case GESTURE_DOUBLE_CLICK: +#if 0 + GTP_DEBUG("double click gesture event"); + input_report_key(gdev->input_device, GESTURE_KEY, 1); + input_sync(gdev->input_device); + input_report_key(gdev->input_device, GESTURE_KEY, 0); + input_sync(gdev->input_device); +#endif + break; + + case GESTURE_UP: + break; + + case GESTURE_DOWN: + break; + + case GESTURE_LEFT: + break; + + case GESTURE_RIGHT: + break; + + case GESTURE_C: + GTP_DEBUG("double click gesture event"); + input_report_key(gdev->input_device, KEY_F24, 1); + input_sync(gdev->input_device); + input_report_key(gdev->input_device, KEY_F24, 0); + input_sync(gdev->input_device); + break; + + case GESTURE_E: + break; + + case GESTURE_M: + break; + + case GESTURE_N: + break; + + case GESTURE_O: + break; + + case GESTURE_S: + break; + + case GESTURE_V: + break; + + case GESTURE_W: + break; + + case GESTURE_Z: + break; + + default: + break; + } + + if (id > 0) { +#ifdef GCORE_SENSOR_EN + gdev->wakeable = fn_data.gdev->wakeable; + gdev->should_enable_gesture = fn_data.gdev->should_enable_gesture; + gdev->gesture_wakelock = fn_data.gdev->gesture_wakelock; + if (!(gdev->wakeable && gdev->should_enable_gesture)) { + GTP_DEBUG("Gesture got but wakeable not set. Skip this gesture."); + return; + } + if (gdev->report_gesture_key) { + input_report_key(gdev->sensor_pdata->input_sensor_dev, KEY_F1, 1); + input_sync(gdev->sensor_pdata->input_sensor_dev); + input_report_key(gdev->sensor_pdata->input_sensor_dev, KEY_F1, 0); + input_sync(gdev->sensor_pdata->input_sensor_dev); + ++report_cnt; + } else { + input_report_abs(gdev->sensor_pdata->input_sensor_dev, + ABS_DISTANCE, + ++report_cnt); + input_sync(gdev->sensor_pdata->input_sensor_dev); + } + GTP_DEBUG("input report: %d", report_cnt); + if (report_cnt >= REPORT_MAX_COUNT) { + report_cnt = 0; + } +#ifdef CONFIG_HAS_WAKELOCK + //wake_lock_timeout(&gesture_wakelock, msecs_to_jiffies(5000)); + wake_lock_timeout(&(gdev->gesture_wakelock), msecs_to_jiffies(5000)); +#else + //__pm_wakeup_event(gesture_wakelock, 5000); + PM_WAKEUP_EVENT(gdev->gesture_wakelock, 5000); +#endif +#else + GTP_DEBUG("tap click gesture event"); + input_report_key(gdev->input_device, GESTURE_KEY, 1); + input_sync(gdev->input_device); + input_report_key(gdev->input_device, GESTURE_KEY, 0); + input_sync(gdev->input_device); +#endif + } + +} +#endif + +int gc7202_tp_esd = 1; + +static u8 Cal8bitsChecksum(u8 *array, u16 length) +{ + s32 s32Checksum = 0; + u16 i; + + for (i = 0 ; i < length; i++) { + s32Checksum += array[i]; + } + + return (u8)(((~s32Checksum) + 1) & 0xFF); +} + +s32 gcore_touch_event_handler(struct gcore_dev *gdev) +{ + u8 *coor_data = NULL; + s32 i = 0; + u8 id = 0; + u8 status = 0; + bool touch_end = true; + u8 checksum = 0; +#ifdef CONFIG_ENABLE_TYPE_B_PROCOTOL + static unsigned long prev_touch; +#endif + unsigned long curr_touch = 0; + s32 input_x = 0; + s32 input_y = 0; + int data_size = DEMO_DATA_SIZE; +#if defined(CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM) + int tpd_res_x = 720;//tpd_dts_data.tpd_resolution[0]; + int tpd_res_y = 1640;//tpd_dts_data.tpd_resolution[1]; + int lcm_res_x = tpd_dts_data.tpd_resolution[0]; + int lcm_res_y = tpd_dts_data.tpd_resolution[1]; +#elif defined(CONFIG_TOUCH_DRIVER_RUN_ON_RK_PLATFORM) \ + || defined(CONFIG_TOUCH_DRIVER_RUN_ON_SPRD_PLATFORM) \ + || defined(CONFIG_TOUCH_DRIVER_RUN_ON_QCOM_PLATFORM) + int tpd_res_x = TOUCH_SCREEN_X_MAX; + int tpd_res_y = TOUCH_SCREEN_Y_MAX; + int lcm_res_x = TOUCH_SCREEN_X_MAX; + int lcm_res_y = TOUCH_SCREEN_Y_MAX; +#endif +#if defined(CONFIG_GCORE_AUTO_UPDATE_FW_HOSTDOWNLOAD) \ + && defined(CONFIG_GCORE_HOSTDOWNLOAD_ESD_PROTECT) + int sum = 0; + int count = 0; +#endif + + +#ifdef CONFIG_ENABLE_FW_RAWDATA + if (gdev->fw_mode == DEMO) { + data_size = DEMO_DATA_SIZE; + } else if (gdev->fw_mode == RAWDATA) { + data_size = RAW_DATA_SIZE; + } else if (gdev->fw_mode == DEMO_RAWDATA) { +#ifdef CONFIG_TOUCH_DRIVER_INTERFACE_SPI + data_size = 2048; //mtk platform spi transfer len request +#else + data_size = DEMO_RAWDATA_SIZE; +#endif + } +#endif + + if (gcore_bus_read(gdev->touch_data, data_size)) { + GTP_ERROR("touch data read error."); + return -1; + } + +#ifdef CONFIG_ENABLE_FW_RAWDATA + if (gdev->usr_read) { + gdev->data_ready = true; + wake_up_interruptible(&gdev->usr_wait); + + if (gdev->fw_mode == RAWDATA) { + return 0; + } + } +#endif + + coor_data = &gdev->touch_data[0]; + +#if 0 + for (i = 1; i <= 10; i++) { + GTP_DEBUG("p720 %d:%x %x %x %x %x %x", i, coor_data[6*i-6], coor_data[6*i-5], \ + coor_data[6*i-4], coor_data[6*i-3], coor_data[6*i-2], coor_data[6*i-1]); + } + + GTP_DEBUG("p720 64:%x %x %x %x %x", coor_data[59], coor_data[60], coor_data[61],\ + coor_data[62], coor_data[63]); +#endif + + +#if defined(CONFIG_GCORE_AUTO_UPDATE_FW_HOSTDOWNLOAD) \ + && defined(CONFIG_GCORE_HOSTDOWNLOAD_ESD_PROTECT) + + /* firmware watchdog, rehostdownload */ + if (coor_data[0] == 0xB1) { + /* santiy check coor data follow 0 */ + for (i = 1; i < 64; i++) { + sum += coor_data[i]; + } + + if (!sum) { + if (!gdev->tp_suspend) { + while ((!gc7202_tp_esd) && (count < 100)) { + msleep(10); + count++; + } + + gc7202_tp_esd = 0; + + GTP_DEBUG("watchdog:B1, goto hostdownload!"); + queue_delayed_work(gdev->fwu_workqueue, &gdev->fwu_work, \ + msecs_to_jiffies(1000)); + } + + return 0; + } + } +#endif + + checksum = Cal8bitsChecksum(coor_data, DEMO_DATA_SIZE - 1); + if (checksum != coor_data[DEMO_DATA_SIZE - 1]) { + GTP_ERROR("checksum error!"); + return -1; + } + + +#ifdef CONFIG_ENABLE_GESTURE_WAKEUP + gcore_gesture_event_handler(gdev, (coor_data[61] >> 3)); +#endif + + for (i = 1; i <= GTP_MAX_TOUCH; i++) { + if (coor_data[0] == 0xFF) { + coor_data += 6; + continue; + } + + touch_end = false; + id = coor_data[0] >> 3; + status = coor_data[0] & 0x03; + input_x = ((coor_data[1] << 4) | (coor_data[3] >> 4)); + input_y = ((coor_data[2] << 4) | (coor_data[3] & 0x0F)); + + input_x = GTP_TRANS_X(input_x, tpd_res_x, lcm_res_x); + input_y = GTP_TRANS_Y(input_y, tpd_res_y, lcm_res_y); + + set_bit(id, &curr_touch); + + gcore_touch_down(gdev->input_device, input_x, input_y, id - 1); + + coor_data += 6; + + } + +#ifdef CONFIG_ENABLE_TYPE_B_PROCOTOL + for (i = 1; i <= GTP_MAX_TOUCH; i++) { + if (!test_bit(i, &curr_touch) && test_bit(i, &prev_touch)) { + gcore_touch_up(gdev->input_device, i - 1); + } + + if (test_bit(i, &curr_touch)) { + set_bit(i, &prev_touch); + } else { + clear_bit(i, &prev_touch); + } + + clear_bit(i, &curr_touch); + } + + input_report_key(gdev->input_device, BTN_TOUCH, !touch_end); +#else + if (touch_end) { + gcore_touch_up(gdev->input_device, 0); + } +#endif + + input_sync(gdev->input_device); + + return 0; +} + +static irqreturn_t tpd_event_handler(int irq, void *dev_id) +{ + struct gcore_dev *gdev = (struct gcore_dev *)dev_id; + + if (mutex_is_locked(&gdev->transfer_lock)) { + GTP_DEBUG("touch is locked, ignore"); + return IRQ_HANDLED; + } + + mutex_lock(&gdev->transfer_lock); + /* don't reset before "if (tpd_halt..." */ + + if (gcore_touch_event_handler(gdev)) { + GTP_ERROR("touch event handler error."); + } + + mutex_unlock(&gdev->transfer_lock); + + return IRQ_HANDLED; +} + +static irqreturn_t tpd_eint_interrupt_handler(int irq, void *dev_id) +{ + struct gcore_dev *gdev = (struct gcore_dev *)dev_id; + struct gcore_exp_fn *exp_fn = NULL; + struct gcore_exp_fn *exp_fn_temp = NULL; + u8 found = 0; + + if (!list_empty(&fn_data.list)) { + list_for_each_entry_safe(exp_fn, exp_fn_temp, &fn_data.list, link) { + if (exp_fn->wait_int == true) { + exp_fn->event_flag = true; + found = 1; + break; + } + } + } + + if (!found) { + gdev->tpd_flag = 1; + return IRQ_WAKE_THREAD; + } else { + wake_up_interruptible(&gdev->wait); + return IRQ_HANDLED; + } + +} + +static int tpd_irq_registration(struct gcore_dev *gdev) +{ + int ret = 0; +#ifdef CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM + struct device_node *node = NULL; + u32 ints[2] = { 0, 0 }; +#endif + + GTP_DEBUG("Device Tree Tpd_irq_registration!"); + +#if defined(CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM) + node = of_find_matching_node(node, touch_of_match); + if (node) { + of_property_read_u32_array(node, "debounce", ints, ARRAY_SIZE(ints)); + gpio_set_debounce(ints[0], ints[1]); + + gdev->touch_irq = irq_of_parse_and_map(node, 0); + GTP_DEBUG("Device touch_irq = %d!", gdev->touch_irq); + } else { + GTP_ERROR("tpd request_irq can not find touch eint device node!."); + ret = -1; + } +#elif defined(CONFIG_TOUCH_DRIVER_RUN_ON_RK_PLATFORM) \ + || defined(CONFIG_TOUCH_DRIVER_RUN_ON_SPRD_PLATFORM) \ + || defined(CONFIG_TOUCH_DRIVER_RUN_ON_QCOM_PLATFORM) + gdev->touch_irq = gpio_to_irq(gdev->irq_gpio); + +#if defined(CONFIG_TOUCH_DRIVER_RUN_ON_SPRD_PLATFORM) && defined(CONFIG_GESTURE_SPECIAL_INT) + gdev->ges_irq = gpio_to_irq(gdev->ges_irq_gpio); + if (gdev->ges_irq) { + ret = request_irq(gdev->ges_irq, (irq_handler_t) tpd_eint_interrupt_handler, + IRQ_TYPE_LEVEL_LOW | IRQF_ONESHOT, "ges-int", gdev); + if (!ret) { + gdev->ges_irq_en = true; + gdev->gesture_wakeup_en = false; + gcore_enable_irq_wake(gdev); + gcore_ges_irq_disable(gdev); + } + } +#endif + +#endif + + ret = devm_request_threaded_irq(&gdev->bus_device->dev, gdev->touch_irq, + tpd_eint_interrupt_handler, + tpd_event_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, "TOUCH_PANEL-eint", gdev); + if (ret > 0) { + ret = -1; + GTP_ERROR("tpd request_irq IRQ LINE NOT AVAILABLE!."); + } + + return ret; +} + +void gcore_gpio_rst_output(int rst, int level) +{ + + GTP_DEBUG("gpio rst = %d output level = %d", rst, level); + +#if defined(CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM) + if (level) { + tpd_gpio_output(rst, 1); + } else { + tpd_gpio_output(rst, 0); + } +#elif defined(CONFIG_TOUCH_DRIVER_RUN_ON_RK_PLATFORM) \ + || defined(CONFIG_TOUCH_DRIVER_RUN_ON_SPRD_PLATFORM) \ + || defined(CONFIG_TOUCH_DRIVER_RUN_ON_QCOM_PLATFORM) + if (level) { + gpio_direction_output(rst, 1); + } else { + gpio_direction_output(rst, 0); + } + +#endif + +} + +int gcore_probe_device(void) +{ +#if defined(CONFIG_ENABLE_CHIP_TYPE_GC7371) + u16 chip_type = 0x7371; +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7271) + u16 chip_type = 0x7271; +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7202) + u16 chip_type = 0x7202; +#elif defined(CONFIG_ENABLE_CHIP_TYPE_GC7372) + u16 chip_type = 0x7372; +#endif + u8 buf[2] = { 0 }; + u16 id = 0; + int retries = 0; + int ret = 0; + + while (retries++ < 3) { + gcore_idm_read_id(buf, sizeof(buf)); + + id = (buf[0] << 8) | buf[1]; + + if (id == chip_type) { + GTP_DEBUG("GC%04x chip id match success", id); + ret = 0; + break; + } else { + GTP_ERROR("GC%04x chip id match fail! retries:%d", id, retries); + ret = -1; + } + } + + return ret; +} + +s32 gcore_init(struct gcore_dev *gdev) +{ + int data_size = DEMO_DATA_SIZE; + + GTP_DEBUG("gcore touch init."); + + gdev->touch_irq = 0; + gdev->irq_flag = 1; + + spin_lock_init(&gdev->irq_flag_lock); + mutex_init(&gdev->transfer_lock); + init_waitqueue_head(&gdev->wait); + + fn_data.gdev = gdev; + fn_data.fn_inited = true; + if (!fn_data.initialized) { + INIT_LIST_HEAD(&fn_data.list); + fn_data.initialized = true; + } + + gdev->rst_output = gcore_gpio_rst_output; + gdev->irq_enable = gcore_irq_enable; + gdev->irq_disable = gcore_irq_disable; + +#ifdef CONFIG_ENABLE_FW_RAWDATA + gdev->fw_mode = DEMO; + init_waitqueue_head(&gdev->usr_wait); + gdev->usr_read = false; + gdev->data_ready = false; + + data_size = DEMO_RAWDATA_SIZE; +#endif + + gdev->touch_data = (u8 *)kzalloc(data_size, GFP_KERNEL); + if (gdev->touch_data == NULL) { + GTP_ERROR("Allocate touch data mem fail!"); + return -ENOMEM; + } + + gcore_gpio_rst_output(gdev->rst_gpio, 1); + +#ifdef CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM + tpd_load_status = 1; +#endif + + gdev->fwu_workqueue = create_singlethread_workqueue("fwu_workqueue"); + INIT_DELAYED_WORK(&gdev->fwu_work, gcore_request_firmware_update_work); + + return 0; +} + +void gcore_deinit(struct gcore_dev *gdev) +{ + struct gcore_exp_fn *exp_fn = NULL; + struct gcore_exp_fn *exp_fn_temp = NULL; + + if (!list_empty(&fn_data.list)) { + list_for_each_entry_safe(exp_fn, exp_fn_temp, &fn_data.list, link) { + if (exp_fn->remove != NULL) { + exp_fn->remove(gdev); + } + } + } + + destroy_workqueue(gdev->fwu_workqueue); + + if (gdev->touch_data) { + kfree(gdev->touch_data); + } + +#ifndef CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM + gcore_free_gpio(gdev); +#endif + + gcore_input_device_deinit(gdev); + + if (gdev) { + kfree(gdev); + } +} + +int gcore_touch_probe(struct gcore_dev *gdev) +{ +#if 0 + struct gcore_exp_fn *exp_fn = NULL; + struct gcore_exp_fn *exp_fn_temp = NULL; +#endif + + GTP_DEBUG("tpd_registration start."); + + gcore_get_dts_info(gdev); + + gcore_input_device_init(gdev); + + if (gcore_init(gdev)) { + GTP_ERROR("gcore init fail!"); + } + + +#ifdef CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM + tpd_gpio_as_int(GTP_INT_PORT); +#endif + + msleep(50); + + /* EINT device tree, default EINT enable */ + tpd_irq_registration(gdev); + + list_add_tail(&p_fwu_fn->link, &fn_data.list); + if (p_fwu_fn->init != NULL) { + p_fwu_fn->init(fn_data.gdev); + } + + list_add_tail(&p_intf_fn->link, &fn_data.list); + if (p_intf_fn->init != NULL) { + p_intf_fn->init(fn_data.gdev); + } + + list_add_tail(&p_mp_fn->link, &fn_data.list); + if (p_mp_fn->init != NULL) { + p_mp_fn->init(fn_data.gdev); + } + +#if 0 + if (!list_empty(&fn_data.list)) { + list_for_each_entry_safe(exp_fn, exp_fn_temp, &fn_data.list, link) { + if (exp_fn->init != NULL) { + exp_fn->init(gdev); + } + } + } +#endif + +#ifdef CONFIG_TOUCH_DRIVER_RUN_ON_SPRD_PLATFORM + if (gcore_sysfs_add_device(&gdev->bus_device->dev)) { + GTP_ERROR("gcore sysfs add device fail!"); + } +#endif + +#ifdef CONFIG_TOUCH_DRIVER_RUN_ON_QCOM_PLATFORM +#ifdef CONFIG_DRM + GTP_DEBUG("Init notifier_drm struct"); + gdev->drm_notifier.notifier_call = gcore_ts_drm_notifier_callback; + + if (gcore_active_panel) { + if (drm_panel_notifier_register(gcore_active_panel, &gdev->drm_notifier) < 0) + GTP_ERROR("register notifier failed!"); + } +#endif + +#if RESUME_USES_WORKQ + gcore_resume_wq_init(); +#endif + +#if CHARGER_NOTIFIER + gcore_charger_notifier_init(); +#endif + +#endif + + return 0; +} + + +/*-------------------------------------------------*/ +/* Touch Driver I2C and SPI Function */ +/*-------------------------------------------------*/ +#if defined(CONFIG_TOUCH_DRIVER_INTERFACE_I2C) + +struct i2c_client *gcore_i2c_client; + +#define GTP_I2C_ADDRESS 0xF0 // slave address 0x78 << 1 +static const struct i2c_device_id tpd_i2c_id[] = { {GTP_DRIVER_NAME, 0}, {} }; +static unsigned short force[] = { 0, GTP_I2C_ADDRESS, I2C_CLIENT_END, I2C_CLIENT_END }; +static const unsigned short *const forces[] = { force, NULL }; + +s32 gcore_i2c_read(u8 *buffer, s32 len) +{ + s32 data_length = len; + s32 ret = 0; + + struct i2c_msg msgs[1] = { + { + .addr = gcore_i2c_client->addr, + .flags = I2C_M_RD, + .buf = buffer, + .len = data_length, +#ifdef CONFIG_TOUCH_DRIVER_RUN_ON_RK_PLATFORM + .scl_rate = 400000, +#endif + }, + }; + +/* +* According to i2c-mtk.c __mt_i2c_transfer(),mtk i2c always use DMA mode. +* Mtk i2c will copy the buffer to DMA memory automatic. so our buffer needn't +* allocate DMA memory.i2c max transfer size is 4K. +*/ +#ifdef CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM + if (len > 4096) { + GTP_ERROR("mtk i2c transfer size limit 4k!"); + return -1; + } +#endif + + ret = i2c_transfer(gcore_i2c_client->adapter, msgs, 1); + if (ret != 1) { + GTP_ERROR("I2C Transfer error!"); + return -1; + } + + return 0; + +} + +s32 gcore_i2c_write(u8 *buffer, s32 len) +{ + s32 ret = 0; + + struct i2c_msg msgs = { + .flags = 0, + .addr = gcore_i2c_client->addr, + .len = len, + .buf = buffer, +#ifdef CONFIG_TOUCH_DRIVER_RUN_ON_RK_PLATFORM + .scl_rate = 100000, +#endif + }; + +/* +* According to i2c-mtk.c __mt_i2c_transfer(),mtk i2c always use DMA mode. +* Mtk i2c will copy the buffer to DMA memory automatic. so our buffer needn't +* allocate DMA memory.i2c max transfer size is 4K. +*/ +#ifdef CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM + if (len > 4096) { + GTP_ERROR("mtk i2c transfer size limit 4k!"); + return -1; + } +#endif + + ret = i2c_transfer(gcore_i2c_client->adapter, &msgs, 1); + if (ret != 1) { + GTP_ERROR("I2C Transfer error!"); + return -1; + } + + return 0; + +} + +static s32 gcore_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct gcore_dev *touch_dev = NULL; + + GTP_DEBUG("tpd_i2c_probe start."); + + gcore_i2c_client = client; + + if (gcore_probe_device()) { + GTP_ERROR("gcore probe device fail!"); + return -1; + } + + touch_dev = kzalloc(sizeof(struct gcore_dev), GFP_KERNEL); + if (!touch_dev) { + GTP_ERROR("allocate touch_dev mem fail!"); + return -ENOMEM; + } + + touch_dev->bus_device = client; + + i2c_set_clientdata(client, touch_dev); + + if (gcore_touch_probe(touch_dev)) { + GTP_ERROR("touch registration fail!"); + return -1; + } + + return 0; +} + +static int gcore_i2c_remove(struct i2c_client *client) +{ + struct gcore_dev *gdev = i2c_get_clientdata(client); + + gcore_deinit(gdev); + + return 0; +} + +static int gcore_i2c_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + strcpy(info->type, "mtk-tpd"); + return 0; +} + +static struct i2c_driver tpd_i2c_driver = { + .probe = gcore_i2c_probe, + .remove = gcore_i2c_remove, + .detect = gcore_i2c_detect, + .driver.name = GTP_DRIVER_NAME, + .driver = { + .name = GTP_DRIVER_NAME, + .of_match_table = tpd_of_match, + }, + .id_table = tpd_i2c_id, + .address_list = (const unsigned short *)forces, +}; + +#elif defined(CONFIG_TOUCH_DRIVER_INTERFACE_SPI) + +struct spi_device *gcore_spi_slave; + +/* +* mtk spi use DMA mode when transfer length over 32 bytes. +* out buffer should allocate DMA memory over 32 bytes. +* TODO:put the allocate memory in init or probe will be better. +*/ +static s32 gcore_spi_setup_xfer(struct spi_transfer *xfer, u32 len) +{ + + xfer->len = len; + xfer->tx_buf = kzalloc(len, GFP_KERNEL | GFP_DMA); + if (xfer->tx_buf == NULL) { + GTP_ERROR("tx kzalloc fail!"); + return -ENOMEM; + } + + xfer->rx_buf = kzalloc(len, GFP_KERNEL | GFP_DMA); + if (xfer->rx_buf == NULL) { + GTP_ERROR("rx kzalloc fail!"); + kfree(xfer->tx_buf); + return -ENOMEM; + } + +// memset(xfer->rx_buf, 0, xfer->len); +// memset((u8 *)xfer->tx_buf, 0, xfer->len); + + return 0; + +} + +s32 gcore_spi_read(u8 *buffer, s32 len) +{ + struct spi_transfer transfer = {0,}; + struct spi_message msg; + int ret = 0; + +#ifdef CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM + // MTK spi.c if transfer > 1024 bytes, the lens must be a multiple of 1024 + if ((len > 1024) && (len % 1024)) { + GTP_ERROR("mtk spi tansfer len must be a multiple of 1024!"); + return -1; + } +#endif + + spi_message_init(&msg); + gcore_spi_setup_xfer(&transfer, len); + spi_message_add_tail(&transfer, &msg); + ret = spi_sync(gcore_spi_slave, &msg); + if (ret < 0) { + GTP_ERROR("spi message transfer error!"); + } + + memcpy(buffer, transfer.rx_buf, len); + + kfree(transfer.rx_buf); + kfree(transfer.tx_buf); + + return 0; +} + +s32 gcore_spi_write(const u8 *buffer, s32 len) +{ + struct spi_transfer transfer = {0,}; + struct spi_message msg; + int ret = 0; + //u8 *tmp = NULL; + +#ifdef CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM + // MTK spi.c if transfer > 1024 bytes, the lens must be a multiple of 1024 + if ((len > 1024) && (len % 1024)) { + GTP_ERROR("mtk spi tansfer len must be a multiple of 1024!"); + return -1; + } +#endif + + spi_message_init(&msg); + ret = gcore_spi_setup_xfer(&transfer, len); + if (ret) { + GTP_ERROR("gcore spi setup xfer fail!"); + return -1; + } + + memcpy((u8 *)transfer.tx_buf, buffer, len); + + spi_message_add_tail(&transfer, &msg); + ret = spi_sync(gcore_spi_slave, &msg); + if (ret < 0) { + GTP_ERROR("spi message transfer error!"); + } + + kfree(transfer.rx_buf); + kfree(transfer.tx_buf); + + return 0; +} + +#define SPI_DUMMY 2 + +s32 gcore_spi_write_then_read(u8 *txbuf, s32 n_tx, u8 *rxbuf, s32 n_rx) +{ + struct spi_transfer transfer = {0,}; + struct spi_message msg; + int ret = 0; + int total = n_tx + n_rx + SPI_DUMMY; + + spi_message_init(&msg); + gcore_spi_setup_xfer(&transfer, total); + + memcpy((u8 *)transfer.tx_buf, txbuf, n_tx); + + spi_message_add_tail(&transfer, &msg); + ret = spi_sync(gcore_spi_slave, &msg); + if (ret < 0) { + GTP_ERROR("spi message transfer error!"); + } + + memcpy(rxbuf, transfer.rx_buf + n_tx + SPI_DUMMY, n_rx); + + kfree(transfer.rx_buf); + kfree(transfer.tx_buf); + + return 0; +} + +void gcore_spi_config(void) +{ +#ifdef CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM +#ifdef CONFIG_MTK_LEGACY_PLATFORM + struct mt_chip_conf *spi_config; + + spi_config = (struct mt_chip_conf *)gcore_spi_slave->controller_data; + if (!spi_config) { + pr_err("spi config is error!!!"); + } + + spi_config->cpol = 1; + spi_config->cpha = 1; + spi_config->holdtime = 5000; + spi_config->setuptime = 5000; + spi_config->high_time = 40; + spi_config->low_time = 40; + + return; +#endif +#endif + + gcore_spi_slave->mode = SPI_MODE_3; + gcore_spi_slave->bits_per_word = 8; + gcore_spi_slave->max_speed_hz = 8000000; + gcore_spi_slave->chip_select = 0; + spi_setup(gcore_spi_slave); + +} + +static s32 gcore_spi_probe(struct spi_device *slave) +{ + struct gcore_dev *touch_dev = NULL; + +#ifdef GCORE_SENSOR_EN + static bool initialized_sensor; +#endif + + GTP_DEBUG("tpd_spi_probe start."); + +#ifdef CONFIG_DRM +{ + struct device_node *dp = slave->dev.of_node; + if (gcore_ts_check_dt(dp) < 0) { + GTP_ERROR("%s: %s not actived\n", __func__, dp->name); + return -ENODEV; + } +} +#endif + + gcore_spi_slave = slave; + gcore_spi_config(); + + if (gcore_probe_device()) { + GTP_ERROR("gcore probe device fail!"); + return -1; + } + + touch_dev = kzalloc(sizeof(struct gcore_dev), GFP_KERNEL); + if (!touch_dev) { + GTP_ERROR("allocate touch_dev mem fail!"); + return -ENOMEM; + } + + touch_dev->bus_device = slave; + + spi_set_drvdata(slave, touch_dev); + + if (gcore_touch_probe(touch_dev)) { + GTP_ERROR("touch registration fail!"); + return -1; + } + +#ifdef GCORE_SENSOR_EN + mutex_init(&touch_dev->state_mutex); + //unknown screen state + touch_dev->screen_state = SCREEN_UNKNOWN; + fn_data.gdev->screen_state = touch_dev->screen_state; +#endif + +#ifdef GCORE_SENSOR_EN + if (!initialized_sensor) { +#ifdef CONFIG_HAS_WAKELOCK + //wake_lock_init(&gesture_wakelock, WAKE_LOCK_SUSPEND, "dt-wake-lock"); + wake_lock_init(&(fn_data.gdev->gesture_wakelock), WAKE_LOCK_SUSPEND, "dt-wake-lock"); +#else + //gesture_wakelock = wakeup_source_register(&slave->dev, "dt-wake-lock"); + PM_WAKEUP_REGISTER(&slave->dev, fn_data.gdev->gesture_wakelock, "dt-wake-lock"); + if (!fn_data.gdev->gesture_wakelock) { + GTP_ERROR("failed to allocate wakeup source\n"); + return -1; + } +#endif + if (!gcore_sensor_init(fn_data.gdev)) + initialized_sensor = true; + } +#endif + + return 0; +} + +static int gcore_spi_remove(struct spi_device *slave) +{ + struct gcore_dev *gdev = spi_get_drvdata(slave); + + gcore_deinit(gdev); + + return 0; +} + +#if 0 +#ifdef CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM +#ifdef CONFIG_MTK_LEGACY_PLATFORM +static struct spi_board_info spi_board_devs[] __initdata = { + [0] = { + .modalias = GTP_DRIVER_NAME, + .bus_num = 1, + .chip_select = 1, + .mode = SPI_MODE_3, + }, +}; +#endif +#endif +#endif + +static struct spi_device_id tpd_spi_id = {GTP_DRIVER_NAME, 0}; + + +static struct spi_driver tpd_spi_driver = { + .probe = gcore_spi_probe, + .id_table = &tpd_spi_id, + .driver = { + .name = GTP_DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = tpd_of_match, + }, + .remove = gcore_spi_remove, +}; + +#endif //CONFIG_TOUCH_DRIVER_INTERFACE_I2C + +s32 gcore_bus_read(u8 *buffer, s32 len) +{ +#if defined(CONFIG_TOUCH_DRIVER_INTERFACE_I2C) + return gcore_i2c_read(buffer, len); +#elif defined(CONFIG_TOUCH_DRIVER_INTERFACE_SPI) + return gcore_spi_read(buffer, len); +#endif + +} +EXPORT_SYMBOL(gcore_bus_read); + +s32 gcore_bus_write(const u8 *buffer, s32 len) +{ +#if defined(CONFIG_TOUCH_DRIVER_INTERFACE_I2C) + return gcore_i2c_write((u8 *)buffer, len); +#elif defined(CONFIG_TOUCH_DRIVER_INTERFACE_SPI) + return gcore_spi_write(buffer, len); +#endif + +} +EXPORT_SYMBOL(gcore_bus_write); + +int gcore_touch_bus_init(void) +{ + GTP_DEBUG("touch_bus_init start."); + +#if defined(CONFIG_TOUCH_DRIVER_INTERFACE_I2C) + return i2c_add_driver(&tpd_i2c_driver); +#elif defined(CONFIG_TOUCH_DRIVER_INTERFACE_SPI) + +#if 0 +#ifdef CONFIG_TOUCH_DRIVER_RUN_ON_MTK_PLATFORM +#ifdef CONFIG_MTK_LEGACY_PLATFORM + spi_register_board_info(spi_board_devs, ARRAY_SIZE(spi_board_devs)); +#endif +#endif +#endif + + return spi_register_driver(&tpd_spi_driver); +#endif + +} + +void gcore_touch_bus_exit(void) +{ +#if defined(CONFIG_TOUCH_DRIVER_INTERFACE_I2C) + i2c_del_driver(&tpd_i2c_driver); +#elif defined(CONFIG_TOUCH_DRIVER_INTERFACE_SPI) + spi_unregister_driver(&tpd_spi_driver); +#endif + +} diff --git a/drivers/input/touchscreen/gcore_mmi/gcore_drv_mp_test.c b/drivers/input/touchscreen/gcore_mmi/gcore_drv_mp_test.c new file mode 100644 index 000000000000..0049bd382a30 --- /dev/null +++ b/drivers/input/touchscreen/gcore_mmi/gcore_drv_mp_test.c @@ -0,0 +1,1028 @@ +/* + * GalaxyCore touchscreen driver + * + * Copyright (C) 2021 GalaxyCore Incorporated + * + * Copyright (C) 2021 Neo Chen + * + * 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 "gcore_drv_common.h" +#include +#include +#include +#include + + +#define MP_TEST_PROC_FILENAME "ts03_selftest" + + +#define MP_TEST_OF_PREFIX "ts03," +#define MP_TEST_OF_ITEM_TEST_INT_PIN MP_TEST_OF_PREFIX"test-int-pin" +#define MP_TEST_OF_ITEM_TEST_CHIP_ID MP_TEST_OF_PREFIX"test-chip-id" +#define MP_TEST_OF_TEST_CHIP_ID MP_TEST_OF_PREFIX"chip-id" +#define MP_TEST_OF_ITEM_TEST_OPEN MP_TEST_OF_PREFIX"test-open" +#define MP_TEST_OF_TEST_OPEN_CB MP_TEST_OF_PREFIX"open-cb" +#define MP_TEST_OF_TEST_OPEN_MIN MP_TEST_OF_PREFIX"open-min" +#define MP_TEST_OF_ITEM_TEST_SHORT MP_TEST_OF_PREFIX"test-short" +#define MP_TEST_OF_TEST_SHORT_CB MP_TEST_OF_PREFIX"short-cb" +#define MP_TEST_OF_TEST_SHORT_MIN MP_TEST_OF_PREFIX"short-min" + + +#define MP_TEST_DATA_DIR "/data/vendor/touchpad" +#define MP_OPEN_TEST_DATA_FILEPATH MP_TEST_DATA_DIR"/OpenTest.csv" +#define MP_SHORT_TEST_DATA_FILEPATH MP_TEST_DATA_DIR"/ShortTest.csv" + + + +DECLARE_COMPLETION(mp_test_complete); + +struct gcore_mp_data { + struct proc_dir_entry *mp_proc_entry; + + int test_int_pin; + int int_pin_test_result; + + int test_chip_id; + int chip_id_test_result; + u8 chip_id[2]; + + int test_open; + int open_test_result; + int open_cb; + int open_min; + u8 *open_data; + u16 *open_node_val; + + int test_short; + int short_test_result; + int short_cb; + int short_min; + u8 *short_data; + u16 *short_node_val; + + struct gcore_dev *gdev; + +}; + +struct gcore_mp_data *g_mp_data; +struct task_struct *mp_thread; + +static int gcore_mp_test_fn_init(struct gcore_dev *gdev); +static void gcore_mp_test_fn_remove(struct gcore_dev *gdev); + +static void dump_mp_data_to_seq_file(struct seq_file *m, const u16 *data, + int rows, int cols); + + +struct gcore_exp_fn mp_test_fn = { + .fn_type = GCORE_MP_TEST, + .wait_int = false, + .event_flag = false, + .init = gcore_mp_test_fn_init, + .remove = gcore_mp_test_fn_remove, +}; + +static void *gcore_seq_start(struct seq_file *m, loff_t *pos) +{ + return *pos < 1 ? (void *)1 : NULL; +} + +static void *gcore_seq_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return NULL; +} + +static void gcore_seq_stop(struct seq_file *m, void *v) +{ + return; +} + +static int gcore_seq_show(struct seq_file *m, void *v) +{ + struct gcore_mp_data *mp_data = (struct gcore_mp_data *)m->private; + int rows = RAWDATA_ROW; + int cols = RAWDATA_COLUMN; + + GTP_DEBUG("gcore seq show"); + + if (mp_data == NULL) { + GTP_ERROR("seq_file private data = NULL"); + return -1; + } + + if (mp_data->test_int_pin) { + seq_printf(m, "Int-Pin Test %s!\n\n", + mp_data->int_pin_test_result == 0 ? "PASS" : "FAIL"); + } + + if (mp_data->test_chip_id) { + seq_printf(m, "Chip-id Test %s!\n\n", + mp_data->chip_id_test_result == 0 ? "PASS" : "FAIL"); + } + + if (mp_data->test_open) { + if (mp_data->open_test_result == 0) { + seq_printf(m, "Open Test PASS!\n\n"); + } else { + seq_printf(m, "Open Test FAIL!\n"); + dump_mp_data_to_seq_file(m, mp_data->open_node_val, rows, cols); + seq_putc(m, '\n'); + } + + } + + if (mp_data->test_short) { + if (mp_data->short_test_result == 0) { + seq_printf(m, "Short Test PASS!\n\n"); + } else { + seq_printf(m, "Short Test FAIL!\n"); + dump_mp_data_to_seq_file(m, mp_data->short_node_val, rows, cols); + seq_putc(m, '\n'); + } + + } + + return 0; +} + + +const struct seq_operations gcore_mptest_seq_ops = { + .start = gcore_seq_start, + .next = gcore_seq_next, + .stop = gcore_seq_stop, + .show = gcore_seq_show, +}; + +int gcore_parse_mp_test_dt(struct gcore_mp_data *mp_data) +{ + struct device_node *np = mp_data->gdev->bus_device->dev.of_node; + + GTP_DEBUG("gcore parse mp test dt"); + + mp_data->test_int_pin = of_property_read_bool(np, MP_TEST_OF_ITEM_TEST_INT_PIN); + + mp_data->test_chip_id = of_property_read_bool(np, MP_TEST_OF_ITEM_TEST_CHIP_ID); + if (mp_data->test_chip_id) { + of_property_read_u8_array(np, MP_TEST_OF_TEST_CHIP_ID, mp_data->chip_id, + sizeof(mp_data->chip_id)); + } + + mp_data->test_open = of_property_read_bool(np, MP_TEST_OF_ITEM_TEST_OPEN); + if (mp_data->test_open) { + of_property_read_u32(np, MP_TEST_OF_TEST_OPEN_CB, &mp_data->open_cb); + of_property_read_u32(np, MP_TEST_OF_TEST_OPEN_MIN, &mp_data->open_min); + } + + mp_data->test_short = of_property_read_bool(np, MP_TEST_OF_ITEM_TEST_SHORT); + if (mp_data->test_short) { + of_property_read_u32(np, MP_TEST_OF_TEST_SHORT_CB, &mp_data->short_cb); + of_property_read_u32(np, MP_TEST_OF_TEST_SHORT_MIN, &mp_data->short_min); + } + + + return 0; +} + +#define MP_TEST_INI "/vendor/etc/motorola/12m/gcore_mp_test.ini" + +static u8 *read_line(u8 *buf, int buf_len, struct file *fp) +{ + int ret; + int i = 0; + mm_segment_t fs; + + + fs = get_fs(); + set_fs(KERNEL_DS); + ret = kernel_read(fp, buf, buf_len, &(fp->f_pos)); + set_fs(fs); + + if (ret <= 0) + return NULL; + + while (buf[i] != '\n' && i < ret) { + i++; + } + + if (i < ret) { + fp->f_pos += i-ret; + } + + if (i < buf_len) { + buf[i] = 0; + } + return buf; +} + +int gcore_parse_mp_test_ini(struct gcore_mp_data *mp_data) +{ + struct file *f = NULL; + u8 *buff = NULL; + int buff_len = 20; + int chip_type = 0; + + buff = kzalloc(buff_len, GFP_KERNEL); + if (!buff) { + GTP_ERROR("file mem alloc fail!"); + return -1; + } + + f = filp_open(MP_TEST_INI, O_RDONLY, 644); + if (!f) { + GTP_ERROR("open mp test ini file fail!"); + return -1; + } + + /* [MP TEST] */ + buff = read_line(buff, buff_len, f); + GTP_DEBUG("ini read line %s", buff); + + /* test int pin */ + buff = read_line(buff, buff_len, f); + GTP_DEBUG("ini read line %s", buff); + sscanf(buff, "test-int-pin=%d", &mp_data->test_int_pin); + GTP_DEBUG("test-int-pin:%s", mp_data->test_int_pin ? "y" : "n"); + + buff = read_line(buff, buff_len, f); + GTP_DEBUG("ini read line %s", buff); + + /* test chip id */ + buff = read_line(buff, buff_len, f); + GTP_DEBUG("ini read line %s", buff); + sscanf(buff, "test-chip-id=%d", &mp_data->test_chip_id); + GTP_DEBUG("test-chip-id:%s", mp_data->test_chip_id ? "y" : "n"); + + buff = read_line(buff, buff_len, f); + GTP_DEBUG("ini read line %s", buff); + sscanf(buff, "chip-id=[%x]", &chip_type); + GTP_DEBUG("chip-id:%x", chip_type); + mp_data->chip_id[0] = (u8)(chip_type >> 8); + mp_data->chip_id[1] = (u8)chip_type; + + buff = read_line(buff, buff_len, f); + GTP_DEBUG("ini read line %s", buff); + + + buff = read_line(buff, buff_len, f); + GTP_DEBUG("ini read line %s", buff); + sscanf(buff, "test-open=%d", &mp_data->test_open); + GTP_DEBUG("test-open:%s", mp_data->test_open ? "y" : "n"); + + buff = read_line(buff, buff_len, f); + GTP_DEBUG("ini read line %s", buff); + sscanf(buff, "open_cb=%d", &mp_data->open_cb); + GTP_DEBUG("read open cb:%d", mp_data->open_cb); + + buff = read_line(buff, buff_len, f); + GTP_DEBUG("ini read line %s", buff); + sscanf(buff, "open_min=%d", &mp_data->open_min); + GTP_DEBUG("read open min:%d", mp_data->open_min); + + buff = read_line(buff, buff_len, f); + GTP_DEBUG("ini read line %s", buff); + + buff = read_line(buff, buff_len, f); + GTP_DEBUG("ini read line %s", buff); + sscanf(buff, "test-short=%d", &mp_data->test_short); + GTP_DEBUG("test-short:%s", mp_data->test_short ? "y" : "n"); + + buff = read_line(buff, buff_len, f); + GTP_DEBUG("ini read line %s", buff); + sscanf(buff, "short_cb=%d", &mp_data->short_cb); + GTP_DEBUG("read short cb:%d", mp_data->short_cb); + + buff = read_line(buff, buff_len, f); + GTP_DEBUG("ini read line %s", buff); + sscanf(buff, "short_min=%d", &mp_data->short_min); + GTP_DEBUG("read short min:%d", mp_data->short_min); + + if (f != NULL) { + filp_close(f, NULL); + } + + kfree(buff); + + return 0; +} + +int gcore_mp_test_int_pin(struct gcore_mp_data *mp_data) +{ + u8 read_buf[4] = { 0 }; + + GTP_DEBUG("gcore mp test int pin"); + + mutex_lock(&mp_data->gdev->transfer_lock); + mp_data->gdev->irq_disable(mp_data->gdev); + + gcore_enter_idm_mode(); + + msleep(1); + + gcore_idm_read_reg(0xC0000098, read_buf, sizeof(read_buf)); + GTP_DEBUG("reg addr: 0xC0000098 read_buf: %x %x %x %x", + read_buf[0], read_buf[1], read_buf[2], read_buf[3]); + + msleep(1); + + read_buf[0] &= 0x7F; + read_buf[2] &= 0x7F; + + + gcore_idm_write_reg(0xC0000098, read_buf, sizeof(read_buf)); + + msleep(1); + + if (gpio_get_value(mp_data->gdev->irq_gpio) == 1) { + GTP_ERROR("INT pin state != LOW test fail!"); + goto error; + } + + read_buf[2] |= BIT7; + + gcore_idm_write_reg(0xC0000098, read_buf, sizeof(read_buf)); + + msleep(1); + + if (gpio_get_value(mp_data->gdev->irq_gpio) == 0) { + GTP_ERROR("INT pin state != HIGH test fail!"); + goto error; + } + + gcore_exit_idm_mode(); + + mp_data->gdev->irq_enable(mp_data->gdev); + mutex_unlock(&mp_data->gdev->transfer_lock); + + return 0; + +error: + mp_data->gdev->irq_enable(mp_data->gdev); + mutex_unlock(&mp_data->gdev->transfer_lock); + return -1; +} + +int gcore_mp_test_chip_id(struct gcore_mp_data *mp_data) +{ + u8 buf[2] = { 0 }; + + GTP_DEBUG("gcore mp test chip id"); + + gcore_idm_read_id(buf, sizeof(buf)); + + if ((buf[0] == mp_data->chip_id[0]) && (buf[1] == mp_data->chip_id[1])) { + GTP_DEBUG("gcore mp test chip id match success!"); + return 0; + } else { + GTP_DEBUG("gcore mp test chip id match failed!"); + return -1; + } +} + + +int gcore_mp_test_item_open(struct gcore_mp_data *mp_data) +{ + u8 cb_high = (u8)(mp_data->open_cb >> 8); + u8 cb_low = (u8)mp_data->open_cb; + u8 cmd[] = {0x40, 0xAA, 0x00, 0x32, 0x73, 0x71, cb_high, cb_low, 0x01}; + struct gcore_dev *gdev = mp_data->gdev; + int ret = 0; + int i = 0; + u8 *opendata = NULL; + u16 node_data = 0; + + GTP_DEBUG("gcore mp test item open"); + + gdev->fw_event = FW_READ_OPEN; + gdev->firmware = mp_data->open_data; +#ifdef CONFIG_TOUCH_DRIVER_INTERFACE_SPI + gdev->fw_xfer = 2048; +#else + gdev->fw_xfer = RAW_DATA_SIZE; +#endif + + mutex_lock(&gdev->transfer_lock); + + mp_test_fn.wait_int = true; + + + ret = gcore_bus_write(cmd, sizeof(cmd)); + if (ret) { + GTP_ERROR("write mp test open cmd fail!"); + return -1; + } + + mutex_unlock(&gdev->transfer_lock); + + if (!wait_for_completion_interruptible_timeout(&mp_test_complete, 1 * HZ)) { + GTP_ERROR("mp test item open timeout."); + return -1; + } + + opendata = mp_data->open_data; + + for (i = 0; i < RAW_DATA_SIZE/2; i++) { + node_data = (opendata[1] << 8) | opendata[0]; + + mp_data->open_node_val[i] = node_data; + + opendata += 2; + } + + for (i = 0; i < RAW_DATA_SIZE/2; i++) { + node_data = mp_data->open_node_val[i]; +// GTP_DEBUG("i:%d open node_data:%d", i, node_data); + + + if ((i == 0) || (i == 8) || (i == 9) || (i == 17)) { + GTP_DEBUG("empty node need not judge."); + continue; + } + + if (node_data < mp_data->open_min) { + GTP_ERROR("i: %d node_data %d < open_min test fail!", \ + i, node_data); + + return -1; + } + + } + + return 0; +} + + +int gcore_mp_test_item_open_reply(u8 *buf, int len) +{ + int ret = 0; + + if (buf == NULL) { + GTP_ERROR("receive buffer is null!"); + return -1; + } + + ret = gcore_bus_read(buf, len); + if (ret) { + GTP_ERROR("read mp test open data error."); + return -1; + } + + mp_test_fn.wait_int = false; + complete(&mp_test_complete); + + + return 0; +} + + +int gcore_mp_test_item_short(struct gcore_mp_data *mp_data) +{ + u8 cb_high = (u8)(mp_data->short_cb >> 8); + u8 cb_low = (u8)mp_data->short_cb; + u8 cmd[] = {0x40, 0xAA, 0x00, 0x32, 0x73, 0x71, cb_high, cb_low, 0x02}; + struct gcore_dev *gdev = mp_data->gdev; + int ret = 0; + int i = 0; + u8 *shortdata = NULL; + u16 node_data = 0; + + GTP_DEBUG("gcore mp test item short"); + + gdev->fw_event = FW_READ_SHORT; + gdev->firmware = mp_data->short_data; +#ifdef CONFIG_TOUCH_DRIVER_INTERFACE_SPI + gdev->fw_xfer = 2048; +#else + gdev->fw_xfer = RAW_DATA_SIZE; +#endif + + + mutex_lock(&gdev->transfer_lock); + + mp_test_fn.wait_int = true; + + + ret = gcore_bus_write(cmd, sizeof(cmd)); + if (ret) { + GTP_ERROR("write mp test open cmd fail!"); + return -1; + } + + mutex_unlock(&gdev->transfer_lock); + + if (!wait_for_completion_interruptible_timeout(&mp_test_complete, 1 * HZ)) { + GTP_ERROR("mp test item open timeout."); + return -1; + } + + shortdata = mp_data->short_data; + + for (i = 0; i < RAW_DATA_SIZE/2; i++) { + node_data = (shortdata[1] << 8) | shortdata[0]; + + mp_data->short_node_val[i] = node_data; + + shortdata += 2; + } + + for (i = 0; i < RAW_DATA_SIZE/2; i++) { + node_data = mp_data->short_node_val[i]; +// GTP_DEBUG("i:%d short node_data:%d", i, node_data); + + if ((i == 0) || (i == 8) || (i == 9) || (i == 17)) { + GTP_DEBUG("empty node need not judge."); + continue; + } + + if (node_data < mp_data->short_min) { + GTP_ERROR("i: %d node_data %d < short_min test fail!", \ + i, node_data); + + return -1; + } + + } + + return 0; +} + +static int mp_test_event_handler(void *p) +{ + struct gcore_dev *gdev = (struct gcore_dev *)p; + struct sched_param param = {.sched_priority = 4}; + + sched_setscheduler(current, SCHED_RR, ¶m); + do { + set_current_state(TASK_INTERRUPTIBLE); + + wait_event_interruptible(gdev->wait, mp_test_fn.event_flag == true); + mp_test_fn.event_flag = false; + + if (mutex_is_locked(&gdev->transfer_lock)) { + GTP_DEBUG("fw event is locked, ignore"); + continue; + } + + mutex_lock(&gdev->transfer_lock); + + switch (gdev->fw_event) { + case FW_READ_OPEN: + gcore_mp_test_item_open_reply(gdev->firmware, gdev->fw_xfer); + break; + + case FW_READ_SHORT: + gcore_mp_test_item_open_reply(gdev->firmware, gdev->fw_xfer); + break; + + default: + break; + } + + mutex_unlock(&gdev->transfer_lock); + + } while (!kthread_should_stop()); + + return 0; +} + +int gcore_alloc_mp_test_mem(struct gcore_mp_data *mp_data) +{ + if (mp_data->test_open) { + if (mp_data->open_data == NULL) { +#ifdef CONFIG_TOUCH_DRIVER_INTERFACE_SPI + mp_data->open_data = kzalloc(2048, GFP_KERNEL); +#else + mp_data->open_data = kzalloc(RAW_DATA_SIZE, GFP_KERNEL); +#endif + if (!mp_data->open_data) { + GTP_ERROR("allocate mp test open_data mem fail!"); + return -ENOMEM; + } + } + + if (mp_data->open_node_val == NULL) { + mp_data->open_node_val = (u16 *)kzalloc(RAW_DATA_SIZE, GFP_KERNEL); + if (!mp_data->open_node_val) { + GTP_ERROR("allocate mp test open_node_val mem fail!"); + return -ENOMEM; + } + } + + } + + if (mp_data->test_short) { + if (mp_data->short_data == NULL) { +#ifdef CONFIG_TOUCH_DRIVER_INTERFACE_SPI + mp_data->short_data = kzalloc(2048, GFP_KERNEL); +#else + mp_data->short_data = kzalloc(RAW_DATA_SIZE, GFP_KERNEL); +#endif + if (!mp_data->short_data) { + GTP_ERROR("allocate mp test short_data mem fail!"); + return -ENOMEM; + } + } + + if (mp_data->short_node_val == NULL) { + mp_data->short_node_val = (u16 *)kzalloc(RAW_DATA_SIZE, GFP_KERNEL); + if (!mp_data->short_node_val) { + GTP_ERROR("allocate mp test short_node_val mem fail!"); + return -ENOMEM; + } + } + } + + return 0; +} + +void gcore_free_mp_test_mem(struct gcore_mp_data *mp_data) +{ + if (mp_data->test_open) { + if (mp_data->open_data) { + kfree(mp_data->open_data); + mp_data->open_data = NULL; + } + + if (mp_data->open_node_val) { + kfree(mp_data->open_node_val); + mp_data->open_node_val = NULL; + } + } + + if (mp_data->test_short) { + if (mp_data->short_data) { + kfree(mp_data->short_data); + mp_data->short_data = NULL; + } + + if (mp_data->short_node_val) { + kfree(mp_data->short_node_val); + mp_data->short_node_val = NULL; + } + } +} + +int dump_mp_data_row_to_buffer(char *buf, size_t size, const u16 *data, + int cols, const char *prefix, const char *suffix, char seperator) +{ + int c, count = 0; + + if (prefix) { + count += scnprintf(buf, size, "%s", prefix); + } + + for (c = 0; c < cols; c++) { + count += scnprintf(buf + count, size - count, + "%4u%c ", data[c], seperator); + } + + if (suffix) { + count += scnprintf(buf + count, size - count, "%s", suffix); + } + + return count; + +} + +int dump_mp_data_to_csv_file(const char *filepath, int flags, + const u16 *data, int rows, int cols) +{ + struct file *file; + int r = 0; + int ret = 0; + loff_t pos = 0; + const u16 *n_val = data; + + GTP_DEBUG("dump mp data to csv file: %s row: %d col: %d", + filepath, rows, cols); + + file = filp_open(filepath, flags, 0666); + if (IS_ERR(file)) { + GTP_ERROR("Open file %s failed %ld", filepath, PTR_ERR(file)); + return -1; + } + + for (r = 0; r < rows; r++) { + char linebuf[256]; + int len; + + len = dump_mp_data_row_to_buffer(linebuf, sizeof(linebuf), n_val, + cols, NULL, "\n", ','); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(file, linebuf, len, &pos); +#else + ret = kernel_write(file, linebuf, len, pos); + pos += len; +#endif + if (ret != len) { + GTP_ERROR("write to file %s failed %d", filepath, ret); + goto error; + } + + n_val += cols; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) + ret = kernel_write(file, "\n", 1, &pos); +#else + ret = kernel_write(file, "\n", 1, pos); + pos++; +#endif + if (ret != 1) { + GTP_ERROR("write newline to file %s failed %d", filepath, ret); + goto error; + } + + +error: + filp_close(file, NULL); + + return ret; + +} + +void dump_mp_data_to_seq_file(struct seq_file *m, const u16 *data, + int rows, int cols) +{ + int r = 0; + const u16 *n_vals = data; + + for (r = 0; r < rows; r++) { + char linebuf[256]; + int len; + + len = dump_mp_data_row_to_buffer(linebuf, sizeof(linebuf), + n_vals, cols, NULL, "\n", ','); + seq_puts(m, linebuf); + + n_vals += cols; + } +} + + +int gcore_save_mp_data_to_file(struct gcore_mp_data *mp_data) +{ + int rows = RAWDATA_ROW; + int cols = RAWDATA_COLUMN; + int ret = 0; + + GTP_DEBUG("save mp data to file"); + + if (mp_data->test_open) { + ret = dump_mp_data_to_csv_file(MP_OPEN_TEST_DATA_FILEPATH, + O_RDWR | O_CREAT | O_TRUNC, mp_data->open_node_val, rows, cols); + if (ret < 0) { + GTP_ERROR("dump mp open test data to file failed"); + return ret; + } + } + + if (mp_data->test_short) { + ret = dump_mp_data_to_csv_file(MP_SHORT_TEST_DATA_FILEPATH, + O_RDWR | O_CREAT | O_TRUNC, mp_data->short_node_val, rows, cols); + if (ret < 0) { + GTP_ERROR("dump mp short test data to file failed"); + return ret; + } + } + + return 0; +} + +int gcore_start_mp_test(void) +{ + struct gcore_mp_data *mp_data = g_mp_data; + int test_result = 0; + + GTP_DEBUG("gcore start mp test."); + + gcore_parse_mp_test_ini(mp_data); + + gcore_alloc_mp_test_mem(mp_data); + + if (mp_data->test_int_pin) { + mp_data->int_pin_test_result = gcore_mp_test_int_pin(mp_data); + if (mp_data->int_pin_test_result) { + GTP_DEBUG("Int pin test result fail!"); + } + } + + msleep(1); + + if (mp_data->test_chip_id) { + mp_data->chip_id_test_result = gcore_mp_test_chip_id(mp_data); + if (mp_data->chip_id_test_result) { + GTP_DEBUG("Chip id test result fail!"); + } + } + + msleep(1); + + if (mp_data->test_open || mp_data->test_short) { + GTP_DEBUG("mp test begin to updata mp bin"); + +#ifdef CONFIG_GCORE_AUTO_UPDATE_FW_HOSTDOWNLOAD + if (gcore_auto_update_hostdownload_proc(gcore_mp_FW)) { + GTP_ERROR("mp bin update hostdownload proc fail"); + } +// gcore_request_firmware_update_work(NULL); +#endif + else { + msleep(1); + + if (mp_data->test_open) { + mp_data->open_test_result = gcore_mp_test_item_open(mp_data); + if (mp_data->open_test_result) { + GTP_DEBUG("Open test result fail!"); + } + } + + msleep(1); + + if (mp_data->test_short) { + mp_data->short_test_result = gcore_mp_test_item_short(mp_data); + if (mp_data->short_test_result) { + GTP_DEBUG("Short test result fail!"); + } + } + } + + } + + gcore_save_mp_data_to_file(mp_data); + +#ifdef CONFIG_GCORE_AUTO_UPDATE_FW_HOSTDOWNLOAD + gcore_request_firmware_update_work(NULL); +#endif + + test_result = (mp_data->int_pin_test_result || mp_data->chip_id_test_result \ + || mp_data->open_test_result || mp_data->short_test_result) ? -1 : 0; + + GTP_DEBUG("start mp test result:%d", test_result); + + return test_result; + +} + +static int32_t gcore_mp_test_open(struct inode *inode, struct file *file) +{ + struct gcore_mp_data *mp_data = PDE_DATA(inode); + + if (mp_data == NULL) { + GTP_ERROR("Open selftest proc with mp data = NULL"); + return -1; + } + + GTP_DEBUG("gcore mp test open"); + + gcore_parse_mp_test_dt(mp_data); + + gcore_alloc_mp_test_mem(mp_data); + + if (mp_data->test_int_pin) { + mp_data->int_pin_test_result = gcore_mp_test_int_pin(mp_data); + } + + msleep(1); + + if (mp_data->test_chip_id) { + mp_data->chip_id_test_result = gcore_mp_test_chip_id(mp_data); + } + + msleep(1); + + if (mp_data->test_open || mp_data->test_short) { + GTP_DEBUG("mp test begin to updata mp bin"); + +#ifdef CONFIG_GCORE_AUTO_UPDATE_FW_HOSTDOWNLOAD + if (gcore_auto_update_hostdownload_proc(gcore_mp_FW)) { + GTP_ERROR("mp bin update hostdownload proc fail"); + } +// gcore_request_firmware_update_work(NULL); +#endif + else { + msleep(1); + + if (mp_data->test_open) { + mp_data->open_test_result = gcore_mp_test_item_open(mp_data); + } + + msleep(1); + + if (mp_data->test_short) { + mp_data->short_test_result = gcore_mp_test_item_short(mp_data); + } + } + + } + + gcore_save_mp_data_to_file(mp_data); + + + if (seq_open(file, &gcore_mptest_seq_ops)) { + GTP_ERROR("seq open fail!"); + return -1; + } + + ((struct seq_file *)file->private_data)->private = mp_data; + + return 0; +} + +struct file_operations gcore_mp_fops = { + .owner = THIS_MODULE, + .open = gcore_mp_test_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + + +int gcore_mp_test_fn_init(struct gcore_dev *gdev) +{ + struct gcore_mp_data *mp_data = NULL; + + GTP_DEBUG("gcore mp test fn init"); + + mp_thread = kthread_run(mp_test_event_handler, gdev, "gcore_mptest"); + + mp_data = kzalloc(sizeof(struct gcore_mp_data), GFP_KERNEL); + if (!mp_data) { + GTP_ERROR("allocate mp_data mem fail! failed"); + return -1; + } + + mp_data->gdev = gdev; + + g_mp_data = mp_data; + + mp_data->mp_proc_entry = proc_create_data(MP_TEST_PROC_FILENAME, + S_IRUGO, NULL, &gcore_mp_fops, mp_data); + if (mp_data->mp_proc_entry == NULL) { + GTP_ERROR("create mp test proc entry selftest failed"); + goto error; + } + + return 0; + +error: + kfree(mp_data); + return -1; + +} + +void gcore_mp_test_fn_remove(struct gcore_dev *gdev) +{ + kthread_stop(mp_thread); + + if (g_mp_data->mp_proc_entry) { + remove_proc_entry(MP_TEST_PROC_FILENAME, NULL); + } + + gcore_free_mp_test_mem(g_mp_data); + + if (g_mp_data) { + kfree(g_mp_data); + g_mp_data = NULL; + } + + return; +} + +#if 0 + +static int __init gcore_mp_test_init(void) +{ + GTP_DEBUG("gcore_fw_update_init."); + + gcore_new_function_register(&mp_test_fn); + + return 0; +} + +static void __exit gcore_mp_test_exit(void) +{ + GTP_DEBUG("gcore_fw_update_exit."); + + gcore_new_function_unregister(&mp_test_fn); + + return; +} + +module_init(gcore_mp_test_init); +module_exit(gcore_mp_test_exit); + +MODULE_AUTHOR("GalaxyCore, Inc."); +MODULE_DESCRIPTION("GalaxyCore Touch MP Test Module"); +MODULE_LICENSE("GPL"); + +#endif diff --git a/drivers/input/touchscreen/gcore_mmi/gcore_drv_qcom.c b/drivers/input/touchscreen/gcore_mmi/gcore_drv_qcom.c new file mode 100755 index 000000000000..ac19d04809c3 --- /dev/null +++ b/drivers/input/touchscreen/gcore_mmi/gcore_drv_qcom.c @@ -0,0 +1,410 @@ +/* + * GalaxyCore touchscreen driver + * + * Copyright (C) 2021 GalaxyCore Incorporated + * + * Copyright (C) 2021 Neo Chen + * + * 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 "gcore_drv_common.h" +#include + +#ifdef CONFIG_DRM +#include +#endif + +#if RESUME_USES_WORKQ +static struct workqueue_struct *resume_by_ddi_wq; +static struct work_struct resume_by_ddi_work; + +void gcore_resume_by_ddi_work(struct work_struct *work) +{ + gcore_request_firmware_update_work(NULL); + + gcore_touch_release_all_point(fn_data.gdev->input_device); + + fn_data.gdev->tp_suspend = false; +} + +void gcore_resume_wq_init(void) +{ + resume_by_ddi_wq = create_singlethread_workqueue("resume_by_ddi_wq"); + WARN_ON(!resume_by_ddi_wq); + INIT_WORK(&resume_by_ddi_work, gcore_resume_by_ddi_work); +} +#endif + +#if CHARGER_NOTIFIER +struct usb_charger_detection charger_detection; +static int usb_detect_flag; + +static void gcore_charger_notify_work(struct work_struct *work) +{ + GTP_DEBUG("enter gcore_charger_notify_work"); + if (1 == usb_detect_flag) { + gcore_fw_event_notify(FW_CHARGER_PLUG); + } else if (0 == usb_detect_flag) { + gcore_fw_event_notify(FW_CHARGER_UNPLUG); + } else { + GTP_DEBUG("Charger flag:%d not currently required!",usb_detect_flag); + } +} + +static int charger_notifier_callback(struct notifier_block *nb, + unsigned long val, void *v) +{ + int ret = 0; + struct power_supply *psy = NULL; + struct usb_charger_detection *charger_detection = + container_of(nb, struct usb_charger_detection, charger_notif); + union power_supply_propval prop; + + psy= power_supply_get_by_name("usb"); + if (!psy){ + return -EINVAL; + GTP_ERROR("Couldn't get usbpsy\n"); + } + + if (!strcmp(psy->desc->name, "usb")){ + if (psy && charger_detection && val == POWER_SUPPLY_PROP_STATUS) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,&prop); + if (ret < 0) { + GTP_ERROR("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret); + return ret; + }else{ + usb_detect_flag = prop.intval; + if(usb_detect_flag != charger_detection->usb_connected) { + if (1 == usb_detect_flag) { + charger_detection->usb_connected = 1; + }else{ + charger_detection->usb_connected = 0; + } + + queue_work(charger_detection->gcore_charger_notify_wq, \ + &charger_detection->charger_notify_work); + + } + } + } + } + return 0; +} + +int gcore_charger_notifier_init(void) +{ + struct power_supply *psy = NULL; + union power_supply_propval prop = {0}; + int ret = 0; + + GTP_ERROR("charger_detection on"); + + charger_detection.usb_connected = 0; + charger_detection.gcore_charger_notify_wq = create_singlethread_workqueue("gcore_charge_wq"); + if (!charger_detection.gcore_charger_notify_wq) { + GTP_ERROR("allocate gcore_charger_notify_wq failed"); + return -1; + } + + INIT_WORK(&charger_detection.charger_notify_work, gcore_charger_notify_work); + + charger_detection.charger_notif.notifier_call = charger_notifier_callback; + ret = power_supply_reg_notifier(&charger_detection.charger_notif); + if (ret) { + GTP_ERROR("Unable to register charger_notifier:%d\n", ret); + goto fail1; + } + + /* if power supply supplier registered brfore TP + * ps_notify_callback will not receive PSY_EVENT_PROP_ADDED + * event, and will cause miss to set TP into charger state. + * So check PS state in probe. + */ + psy = power_supply_get_by_name("usb"); + if (psy) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,&prop); + if (ret < 0) { + GTP_ERROR("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret); + goto fail1; + } else { + usb_detect_flag = prop.intval; + if (usb_detect_flag != charger_detection.usb_connected) { + if (1 == usb_detect_flag) { + charger_detection.usb_connected = 1; + } else { + charger_detection.usb_connected = 0; + } + + if (charger_detection.usb_connected) { + gcore_fw_event_notify(FW_CHARGER_PLUG); + } else { + gcore_fw_event_notify(FW_CHARGER_UNPLUG); + } + + } + } + } + + return 0; + +fail1: + if (charger_detection.charger_notif.notifier_call) + power_supply_unreg_notifier(&charger_detection.charger_notif); + + destroy_workqueue(charger_detection.gcore_charger_notify_wq); + + return -1; + +} +#endif + +#if defined(CONFIG_ENABLE_GESTURE_WAKEUP) && defined(CONFIG_GESTURE_SPECIAL_INT) + +int gcore_enable_irq_wake(struct gcore_dev *gdev) +{ + GTP_DEBUG("enable irq wake"); + + if (gdev->ges_irq) { + enable_irq_wake(gdev->ges_irq); + return 0; + } + + return -1; +} +EXPORT_SYMBOL(gcore_enable_irq_wake); + +void gcore_ges_irq_enable(struct gcore_dev *gdev) +{ + unsigned long flags; + + GTP_DEBUG("enable ges irq"); + + spin_lock_irqsave(&gdev->irq_flag_lock, flags); + + if (gdev->ges_irq_en == false) { + gdev->ges_irq_en = true; + spin_unlock_irqrestore(&gdev->irq_flag_lock, flags); + enable_irq(gdev->ges_irq); + } else if (gdev->ges_irq_en == true) { + spin_unlock_irqrestore(&gdev->irq_flag_lock, flags); + GTP_ERROR("gesture Eint already enabled!"); + } else { + spin_unlock_irqrestore(&gdev->irq_flag_lock, flags); + GTP_ERROR("Invalid irq_flag %d!", gdev->irq_flag); + } + /*GTP_DEBUG("Enable irq_flag=%d",g_touch.irq_flag);*/ + +} + +void gcore_ges_irq_disable(struct gcore_dev *gdev) +{ + unsigned long flags; + + GTP_DEBUG("disable ges irq"); + + spin_lock_irqsave(&gdev->irq_flag_lock, flags); + + if (gdev->ges_irq_en == false) { + spin_unlock_irqrestore(&gdev->irq_flag_lock, flags); + GTP_ERROR("gesture Eint already disable!"); + return; + } + + gdev->ges_irq_en = false; + + spin_unlock_irqrestore(&gdev->irq_flag_lock, flags); + + disable_irq_nosync(gdev->ges_irq); +} +EXPORT_SYMBOL(gcore_ges_irq_disable); +#endif + +void gcore_suspend(void) +{ +#ifdef GCORE_SENSOR_EN + struct gcore_dev *gdev = fn_data.gdev; +#endif + + GTP_DEBUG("enter gcore suspend"); + +#if defined(CONFIG_ENABLE_GESTURE_WAKEUP) && defined(CONFIG_GESTURE_SPECIAL_INT) + struct gcore_dev *gdev = fn_data.gdev; + if (fn_data.gdev->gesture_wakeup_en) { + gcore_enable_irq_wake(gdev); + + gcore_irq_disable(gdev); + + //sprd_pin_set(&gdev->bus_device->dev, "gpio_144_slp"); + + gcore_ges_irq_enable(gdev); + } +#endif + +#ifdef GCORE_SENSOR_EN + mutex_lock(&fn_data.gdev->state_mutex); + + fn_data.gdev->tp_suspend = true; + + if (fn_data.gdev->should_enable_gesture) { + gcore_fw_event_notify(FW_GESTURE_ENABLE); + fn_data.gdev->gesture_enabled = true; + fn_data.gdev->wakeable = true; + enable_irq_wake(gdev->touch_irq); + GTP_DEBUG("Enable gcore irq wake"); + } + else { + gcore_fw_event_notify(FW_GESTURE_DISABLE); + } + fn_data.gdev->screen_state = SCREEN_OFF; + mutex_unlock(&fn_data.gdev->state_mutex); +#else + fn_data.gdev->tp_suspend = true; + gcore_fw_event_notify(FW_GESTURE_DISABLE); +#endif + + msleep(20); + +} + +void gcore_resume(void) +{ +#ifdef GCORE_SENSOR_EN + struct gcore_dev *gdev = fn_data.gdev; +#endif + + GTP_DEBUG("enter gcore resume"); + +#if !RESUME_USES_WORKQ +#if defined(CONFIG_ENABLE_GESTURE_WAKEUP) && defined(CONFIG_GESTURE_SPECIAL_INT) + struct gcore_dev *gdev = fn_data.gdev; + if (gdev->gesture_wakeup_en) { + GTP_DEBUG("disable irq wake"); + + gcore_ges_irq_disable(gdev); + + //sprd_pin_set(&gdev->bus_device->dev, "gpio_144"); + + gcore_irq_enable(gdev); + } else { + queue_delayed_work(fn_data.gdev->fwu_workqueue, &fn_data.gdev->fwu_work, \ + msecs_to_jiffies(100)); + } +#else + //queue_delayed_work(fn_data.gdev->fwu_workqueue, &fn_data.gdev->fwu_work, msecs_to_jiffies(1000)); + gcore_request_firmware_update_work(NULL); +#endif + + gcore_touch_release_all_point(fn_data.gdev->input_device); + + fn_data.gdev->tp_suspend = false; +#else + +#ifdef GCORE_SENSOR_EN + mutex_lock(&fn_data.gdev->state_mutex); + + if(fn_data.gdev->wakeable) { + GTP_DEBUG("Disable gcore irq wake"); + disable_irq_wake(gdev->touch_irq); + fn_data.gdev->gesture_enabled = false; + fn_data.gdev->wakeable = false; + } + fn_data.gdev->screen_state = SCREEN_ON; + + queue_work(resume_by_ddi_wq, &(resume_by_ddi_work)); + GTP_DEBUG("TP resume work queued."); + + mutex_unlock(&fn_data.gdev->state_mutex); +#else + queue_work(resume_by_ddi_wq, &(resume_by_ddi_work)); + GTP_DEBUG("TP resume work queued."); +#endif + +#endif + +} + +#ifdef CONFIG_DRM +int gcore_ts_drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + unsigned int blank; + + struct drm_panel_notifier *evdata = data; + + if (!evdata) + return 0; + + blank = *(int *) (evdata->data); + GTP_DEBUG("event = %lu, blank = %d", event, blank); + + if (!(event == DRM_PANEL_EARLY_EVENT_BLANK || event == DRM_PANEL_EVENT_BLANK)) { + GTP_DEBUG("event(%lu) do not need process\n", event); + return 0; + } + + switch (blank) { + case DRM_PANEL_BLANK_POWERDOWN: + if (event == DRM_PANEL_EARLY_EVENT_BLANK) { + gcore_suspend(); +#ifdef GCORE_SET_TOUCH_STATE + if (fn_data.gdev->should_enable_gesture) { + GTP_DEBUG("double tap gesture suspend\n"); + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); + } else { + GTP_DEBUG("deep uspend\n"); + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); + } +#endif + } + break; + + case DRM_PANEL_BLANK_UNBLANK: + if (event == DRM_PANEL_EVENT_BLANK) { + gcore_resume(); + } + break; + + default: + break; + } + return 0; +} +#endif + +static int __init touch_driver_init(void) +{ + GTP_DEBUG("touch driver init."); + + if (gcore_touch_bus_init()) { + GTP_ERROR("bus init fail!"); + return -1; + } + + return 0; +} + +/* should never be called */ +static void __exit touch_driver_exit(void) +{ + gcore_touch_bus_exit(); +} + + + +module_init(touch_driver_init); +module_exit(touch_driver_exit); +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +MODULE_AUTHOR("GalaxyCore, Inc."); +MODULE_DESCRIPTION("GalaxyCore Touch Main Mudule"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/gcore_mmi/gcore_ioctl.h b/drivers/input/touchscreen/gcore_mmi/gcore_ioctl.h new file mode 100644 index 000000000000..e744b440148b --- /dev/null +++ b/drivers/input/touchscreen/gcore_mmi/gcore_ioctl.h @@ -0,0 +1,52 @@ +/* + * GalaxyCore touchscreen driver + * + * Copyright (C) 2021 GalaxyCore Incorporated + * + * Copyright (C) 2021 Neo Chen + * + * 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 _GCORE_IOCTL_H_ +#define _GCORE_IOCTL_H_ + +/* App */ +struct reg_msg { + u32 addr; + int length; + u8 buffer[50]; +}; + +#define GALAXYCORE_MAGIC_NUMBER 'G' +#define GALAXYCORE_MAX_NR 12 +#define IOC_APP_READ_FW_VERSION _IOR(GALAXYCORE_MAGIC_NUMBER, 0, char) +#define IOC_APP_UPDATE_FW _IOW(GALAXYCORE_MAGIC_NUMBER, 1, char) +#define IOC_APP_DEMO _IO(GALAXYCORE_MAGIC_NUMBER, 2) +#define IOC_APP_DEMO_RAWDATA _IO(GALAXYCORE_MAGIC_NUMBER, 3) +#define IOC_APP_READ_REG _IOR(GALAXYCORE_MAGIC_NUMBER, 4, struct reg_msg) +#define IOC_APP_WRITE_REG _IOW(GALAXYCORE_MAGIC_NUMBER, 5, struct reg_msg) +#define IOC_APP_GET_RAWDATA_RES _IOR(GALAXYCORE_MAGIC_NUMBER, 6, char) +#define IOC_APP_START_MP_TEST _IO(GALAXYCORE_MAGIC_NUMBER, 7) + +/* Tool */ +#define IOC_DEBUG_TIME_RST0 _IO(GALAXYCORE_MAGIC_NUMBER, 8) +#define IOC_DEBUG_TIME_RST1 _IO(GALAXYCORE_MAGIC_NUMBER, 9) +#define IOC_TOOL_MODE _IOW(GALAXYCORE_MAGIC_NUMBER, 10, int) +#define IOC_TOOL_IDM_OPERATION _IOW(GALAXYCORE_MAGIC_NUMBER, 11, int) + + +#endif /* _GCORE_IOCTL_H_ */ + + + + + diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/Android.mk b/drivers/input/touchscreen/goodix_berlin_mmi/Android.mk new file mode 100644 index 000000000000..6dc4ac6e2cb5 --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/Android.mk @@ -0,0 +1,84 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DRM_PANEL_EVENT_NOTIFICATIONS),true) + KBUILD_OPTIONS += CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS=y +endif + +ifeq ($(DRM_PANEL_NOTIFICATIONS),true) + KBUILD_OPTIONS += CONFIG_DRM_PANEL_NOTIFICATIONS=y +endif + +ifeq ($(TOUCHSCREEN_GOODIX_BRL_SPI),true) +ifeq ($(call is-board-platform-in-list,taro kalama parrot crow pineapple), true) + KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI=y +else + KERNEL_CFLAGS += CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI=y +endif +endif + +ifeq ($(GTP_LIMIT_USE_SUPPLIER),true) + KBUILD_OPTIONS += CONFIG_GTP_LIMIT_USE_SUPPLIER=y +endif + +ifeq ($(GTP_ENABLE_PM_QOS),true) + KBUILD_OPTIONS += CONFIG_GTP_ENABLE_PM_QOS=y +endif + +ifeq ($(GOODIX_ESD_ENABLE),true) + KBUILD_OPTIONS += CONFIG_GOODIX_ESD_ENABLE=y +endif + +ifeq ($(findstring factory, $(TARGET_PRODUCT)), factory) + KBUILD_OPTIONS += CONFIG_TARGET_BUILD_FACROTY=y +endif + +ifeq ($(TOUCHSCREEN_FOD),true) + KBUILD_OPTIONS += CONFIG_GTP_FOD=y +endif + +ifeq ($(TOUCHSCREEN_LAST_TIME),true) + KBUILD_OPTIONS += CONFIG_GTP_LAST_TIME=y +endif + +ifeq ($(GTP_ENABLE_DDA_STYLUS),true) +ifeq ($(MOTO_DDA_PASSIVE_STYLUS),true) + KBUILD_OPTIONS += CONFIG_MOTO_DDA_PASSIVE_STYLUS=y +else + KBUILD_OPTIONS += CONFIG_GTP_DDA_STYLUS=y +endif +endif + +ifeq ($(BOARD_USES_DOUBLE_TAP_CTRL),true) + KBUILD_OPTIONS += CONFIG_BOARD_USES_DOUBLE_TAP_CTRL=y +endif + +ifneq ($(GTP_ENABLE_TOUCH_PALM),) + KBUILD_OPTIONS += CONFIG_INPUT_GOODIX_MMI_ENABLE_PALM=y +endif + +ifeq ($(ENABLE_TP_GHOST_LOG_CAPTURE),true) + KBUILD_OPTIONS += CONFIG_GTP_GHOST_LOG_CAPTURE=y +endif + +ifeq ($(ENABLE_GTP_PALM_CANCEL),true) + KBUILD_OPTIONS += CONFIG_ENABLE_GTP_PALM_CANCEL=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := goodix_brl_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/mmi_info.ko +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KBUILD_OPTIONS += CONFIG_INPUT_TOUCHSCREEN_MMI=y + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +endif + +ifneq ($(BOARD_USES_PEN_NOTIFIER),) + KBUILD_OPTIONS += CONFIG_INPUT_GTP_PASSIVE_PEN_NOTIFIER=y + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/hall_pen.ko +endif + +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/Kbuild b/drivers/input/touchscreen/goodix_berlin_mmi/Kbuild new file mode 100644 index 000000000000..65bf4824597e --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/Kbuild @@ -0,0 +1,133 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m := goodix_brl_mmi.o +goodix_brl_mmi-objs := \ + goodix_brl_i2c.o \ + goodix_brl_spi.o \ + goodix_ts_core.o \ + goodix_brl_hw.o \ + goodix_cfg_bin.o \ + goodix_ts_utils.o \ + goodix_brl_fwupdate.o \ + goodix_ts_inspect.o \ + goodix_ts_tools.o \ + goodix_ts_config.o + +ifneq ($(filter m y, $(CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_EVENT_NOTIFICATIONS +endif + +ifneq ($(filter m y,$(CONFIG_DRM_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_NOTIFICATIONS +endif + +ifneq ($(filter m y,$(CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI)),) + EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_GOODIX_BRL_SPI +endif + +ifneq ($(filter m y,$(CONFIG_GTP_FOD)),) + EXTRA_CFLAGS += -DCONFIG_GTP_FOD + goodix_brl_mmi-objs += goodix_ts_gesture_fod.o +else + goodix_brl_mmi-objs += goodix_ts_gesture.o +endif + +ifneq ($(filter m y,$(CONFIG_GTP_LIMIT_USE_SUPPLIER)),) + EXTRA_CFLAGS += -DCONFIG_GTP_LIMIT_USE_SUPPLIER +endif + +ifneq ($(filter m y,$(CONFIG_GTP_ENABLE_PM_QOS)),) + EXTRA_CFLAGS += -DCONFIG_GTP_ENABLE_PM_QOS +endif + +ifneq ($(filter m y,$(CONFIG_GOODIX_ESD_ENABLE)),) + EXTRA_CFLAGS += -DCONFIG_GOODIX_ESD_ENABLE +endif + +ifneq ($(filter m y,$(CONFIG_TARGET_BUILD_FACROTY)),) + EXTRA_CFLAGS += -DGTP_SAVE_IN_CSV +endif + +ifneq ($(filter m y,$(CONFIG_GTP_LAST_TIME)),) + EXTRA_CFLAGS += -DCONFIG_GTP_LAST_TIME +endif + +ifneq ($(filter m y,$(CONFIG_GTP_DDA_STYLUS)),) + EXTRA_CFLAGS += -DCONFIG_GTP_DDA_STYLUS + goodix_brl_mmi-objs += goodix_ts_dda.o +endif + +ifneq ($(filter m y,$(CONFIG_MOTO_DDA_PASSIVE_STYLUS)),) + EXTRA_CFLAGS += -DCONFIG_MOTO_DDA_PASSIVESTYLUS +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI + goodix_brl_mmi-objs += goodix_ts_mmi.o +endif + +ifneq ($(filter m y, $(CONFIG_BOARD_USES_DOUBLE_TAP_CTRL)),) + EXTRA_CFLAGS += -DCONFIG_BOARD_USES_DOUBLE_TAP_CTRL +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_GOODIX_MMI_ENABLE_PALM)),) + EXTRA_CFLAGS += -DGOODIX_PALM_SENSOR_EN +endif + +ifeq ($(TARGET_BUILD_VARIANT), userdebug) +ifneq ($(filter m y, $(CONFIG_GTP_GHOST_LOG_CAPTURE)),) + EXTRA_CFLAGS += -DCONFIG_GTP_GHOST_LOG_CAPTURE + goodix_brl_mmi-objs += goodix_ts_log_capture.o +endif +endif + +ifneq ($(filter m y,$(CONFIG_ENABLE_GTP_PALM_CANCEL)),) + EXTRA_CFLAGS += -DCONFIG_ENABLE_GTP_PALM_CANCEL +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_GTP_PASSIVE_PEN_NOTIFIER)),) + EXTRA_CFLAGS += -DGTP_PEN_NOTIFIER +endif + +ifneq ($(findstring _factory,$(TARGET_PRODUCT)),) +USE_TARGET_PRODUCT := $(strip $(subst _factory,, $(TARGET_PRODUCT))) +else +USE_TARGET_PRODUCT := $(TARGET_PRODUCT) +endif + +ifneq ($(filter oneli, $(USE_TARGET_PRODUCT)), ) + EXTRA_CFLAGS += -DPRODUCT_ONELI +else ifneq ($(filter hiphi, $(USE_TARGET_PRODUCT)), ) + EXTRA_CFLAGS += -DPRODUCT_HIPHI +else ifneq ($(filter hiphic, $(USE_TARGET_PRODUCT)), ) + EXTRA_CFLAGS += -DPRODUCT_HIPHIC +else ifneq ($(filter li, $(USE_TARGET_PRODUCT)), ) + EXTRA_CFLAGS += -DPRODUCT_LI +else ifneq ($(filter eqs, $(USE_TARGET_PRODUCT)), ) + EXTRA_CFLAGS += -DPRODUCT_EQS +else ifneq ($(filter tundra%, $(USE_TARGET_PRODUCT)), ) + EXTRA_CFLAGS += -DPRODUCT_TUNDRA +else ifneq ($(filter miami%, $(USE_TARGET_PRODUCT)), ) + EXTRA_CFLAGS += -DPRODUCT_MIAMI +else ifneq ($(filter rtwo%, $(USE_TARGET_PRODUCT)), ) + EXTRA_CFLAGS += -DPRODUCT_RTWO +else ifneq ($(filter ctwo%, $(USE_TARGET_PRODUCT)), ) + EXTRA_CFLAGS += -DPRODUCT_CTWO +else ifneq ($(filter bronco, $(USE_TARGET_PRODUCT)), ) + EXTRA_CFLAGS += -DPRODUCT_BRONCO +else ifneq ($(filter felix, $(USE_TARGET_PRODUCT)), ) + EXTRA_CFLAGS += -DCONFIG_GTP_DISP_MODE +else ifneq ($(filter zeekr%, $(USE_TARGET_PRODUCT)), ) + EXTRA_CFLAGS += -DPRODUCT_ZEEKR +else ifneq ($(filter lynkco%, $(USE_TARGET_PRODUCT)), ) + EXTRA_CFLAGS += -DPRODUCT_LYNKCO +else ifneq ($(filter bangkk%, $(USE_TARGET_PRODUCT)), ) + EXTRA_CFLAGS += -DPRODUCT_BANGKK +endif +$(warning "william EXTRA_CFLAGS = $(EXTRA_CFLAGS)") + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/Kconfig b/drivers/input/touchscreen/goodix_berlin_mmi/Kconfig new file mode 100644 index 000000000000..223d5aea824e --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/Kconfig @@ -0,0 +1,20 @@ +# +# Goodix touchscreen driver configuration +# +menuconfig TOUCHSCREEN_GOODIX_BRL + tristate "Goodix berlin touchscreen" + help + Say Y here if you have a Goodix berlin series touch controller + to your system. + + If build module, say M. + If unsure, say N. + +if TOUCHSCREEN_GOODIX_BRL + +config TOUCHSCREEN_GOODIX_BRL_SPI + bool "support SPI bus connection" + help + Say Y here if the touchscreen is connected via SPI bus. + +endif diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/Makefile b/drivers/input/touchscreen/goodix_berlin_mmi/Makefile new file mode 100644 index 000000000000..28b34eea422a --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/Makefile @@ -0,0 +1,20 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(M)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(M)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers +ifneq ($(filter m y, $(CONFIG_INPUT_GTP_PASSIVE_PEN_NOTIFIER)),) +KBUILD_EXTRA_SYMBOLS += $(M)/../../../misc/hall/$(GKI_OBJ_MODULE_DIR)/Module.symvers +endif diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_fwupdate.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_fwupdate.c new file mode 100644 index 000000000000..6c05a3b08aae --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_fwupdate.c @@ -0,0 +1,1408 @@ + /* + * 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 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_set_fwname(char* fw_name) { + if (fw_name != NULL) { + strlcpy(goodix_fw_update_ctrl.fw_name, fw_name, + sizeof(goodix_fw_update_ctrl.fw_name)); + return 0; + } + else + return -EINVAL; +} + +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 */ + 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 + 6); + 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); + msleep(10); + } + + 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); + if (!ret) { + ts_info("firmware upgraded"); + return 0; + } else + 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 device *dev, struct device_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_fwsize_show( + struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; + int r = -EINVAL; + + if (fw_ctrl && fw_ctrl->fw_data.firmware) + r = snprintf(buf, PAGE_SIZE, "%zu\n", + fw_ctrl->fw_data.firmware->size); + return r; +} + +static ssize_t goodix_sysfs_fwsize_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; + struct firmware *fw; + u8 **data; + size_t size = 0; + + if (!fw_ctrl) + return -EINVAL; + + if (sscanf(buf, "%zu", &size) < 0 || !size) { + ts_err("Failed to get fwsize"); + return -EFAULT; + } + + /* use vmalloc to alloc huge memory */ + fw = vmalloc(sizeof(*fw) + size); + if (!fw) + return -ENOMEM; + mutex_lock(&fw_ctrl->mutex); + memset(fw, 0x00, sizeof(*fw) + size); + data = (u8 **)&fw->data; + *data = (u8 *)fw + sizeof(struct firmware); + fw->size = size; + fw_ctrl->fw_data.firmware = fw; + fw_ctrl->mode = UPDATE_MODE_SRC_SYSFS; + mutex_unlock(&fw_ctrl->mutex); + return count; +} + +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 fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; + struct firmware_data *fw_data; + + fw_data = &fw_ctrl->fw_data; + + if (!fw_data->firmware) { + ts_err("Need set fw image size first"); + return -ENOMEM; + } + + if (fw_data->firmware->size == 0) { + ts_err("Invalid firmware size"); + return -EINVAL; + } + + if (pos + count > fw_data->firmware->size) + return -EFAULT; + mutex_lock(&fw_ctrl->mutex); + memcpy((u8 *)&fw_data->firmware->data[pos], buf, count); + mutex_unlock(&fw_ctrl->mutex); + return count; +} + +/* return fw_update result */ +static ssize_t goodix_sysfs_result_show( + struct device *dev, struct device_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: + sprintf(str, "preparing"); + break; + case UPSTA_UPDATING: + sprintf(str, "updating"); + break; + case UPSTA_SUCCESS: + sprintf(str, "success"); + break; + case UPSTA_FAILED: + sprintf(str, "failed"); + break; + case UPSTA_NOTWORK: + default: + sprintf(str, "notwork"); + break; + } + + r = snprintf(buf, PAGE_SIZE, "result:%s spend_time:%dms\n", + str, fw_ctrl->spend_time); + + return r; +} + +static DEVICE_ATTR(update_en, 0220, NULL, goodix_sysfs_update_en_store); +static DEVICE_ATTR(fwsize, 0664, goodix_sysfs_fwsize_show, + goodix_sysfs_fwsize_store); +static DEVICE_ATTR(result, 0664, goodix_sysfs_result_show, NULL); + +static struct attribute *goodix_fwu_attrs[] = { + &dev_attr_update_en.attr, + &dev_attr_fwsize.attr, + &dev_attr_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 = 0666; + 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); + + 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.firmware) { + ts_err("Invalid firmware from sysfs"); + r = -EINVAL; + goto out; + } + } else { + ts_err("unknown update mode 0x%x", fwu_ctrl->mode); + r = -EINVAL; + goto out; + } + + ts_debug("notify update start"); + goodix_ts_blocking_notify(NOTIFY_FWUPDATE_START, NULL); + + /* ready to update */ + ts_debug("start update proc"); + r = goodix_fw_update_proc(fwu_ctrl); + + /* clean */ + if (fwu_ctrl->mode & UPDATE_MODE_SRC_HEAD) { + kfree(fwu_ctrl->fw_data.firmware); + fwu_ctrl->fw_data.firmware = 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; + + core_data->set_fw_name = goodix_set_fwname; + + 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); +} diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_hw.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_hw.c new file mode 100644 index 000000000000..885011598fdd --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_hw.c @@ -0,0 +1,1739 @@ + /* + * 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 "goodix_ts_mmi.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 + +#define TRIGGER_FRAME_CNT 50 +#define MAX_FRAME_LENGTH 2500 + +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, +}; + +#ifdef CONFIG_GTP_GHOST_LOG_CAPTURE +enum { + PROCESS_REF = 0, + PROCESS_RAW, + PROCESS_DIFF, + PROCESS_B_ARRAY +}; + +static struct frame_log_t { + u8 *buf; + int used; +} frame_log; +#endif + +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 after reset, ret:%d r_value:%02x", ret, r_value); + return -EINVAL; +} + +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; +} + +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) { + 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) { + ret = regulator_enable(cd->avdd); + if (ret < 0) { + ts_err("Failed to enable avdd:%d", ret); + goto power_off; + } + } + usleep_range(15000, 15100); + gpio_direction_output(reset_gpio, 1); + usleep_range(4000, 4100); + 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 (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) +{ + 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"); + + return 0; +} + +int brl_resume(struct goodix_ts_core *cd) +{ + return cd->hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); +} + +#define GOODIX_GESTURE_CMD 0xA6 +int brl_gesture(struct goodix_ts_core *cd, unsigned int gesture_type) +{ + struct goodix_ts_cmd cmd; + + cmd.cmd = GOODIX_GESTURE_CMD; + cmd.data[0] = gesture_type & 0xFF; + if (cd->bus->ic_type == IC_TYPE_BERLIN_D) { + cmd.len = 6; + cmd.data[1] = (gesture_type >> 8) & 0xFF; + } else if (cd->bus->ic_type == IC_TYPE_BERLIN_B) { + cmd.len = 8; + cmd.data[2] = (gesture_type >> 16) & 0xFF; + cmd.data[3] = (gesture_type >> 24) & 0xFF; + } else + cmd.len = 5; + + if (cd->hw_ops->send_cmd(cd, &cmd)) + ts_err("failed send gesture cmd"); + + return 0; +} + +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); + } + + msleep(GOODIX_NORMAL_RESET_DELAY_MS); + + ts_info("device connected"); + return ret; +} + +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(cd->irq); + ts_debug("Irq disabled"); + return 0; + } + ts_debug("warnning: irq deepth inbalance!"); + 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_info("cmd ack data %*ph", + (int)sizeof(cmd_ack), cmd_ack.buf); + if (cmd_ack.ack == CMD_ACK_OK) { + usleep_range(2000, 2100); + 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; +} + +#ifdef CONFIG_GTP_DISP_MODE +#define DISP_MODE_CMD_LEN 5 +#define DISP_MODE_CMD 0xbb +#define DISP_MODE_MAX 4 + +static int brl_set_display_mode(struct goodix_ts_core *cd, int mode) +{ + struct goodix_ts_cmd temp_cmd; + int ret; + + if (mode < 0 || mode > DISP_MODE_MAX) { + ts_err("invalid mode: %d", mode); + return -EINVAL; + } + temp_cmd.len = DISP_MODE_CMD_LEN; + temp_cmd.cmd = DISP_MODE_CMD; + temp_cmd.data[0] = mode; + + ret = brl_send_cmd(cd, &temp_cmd); + if (ret < 0) { + ts_err("failed to set display mode"); + return -EIO; + } + ts_info("set display mode: %d", temp_cmd.data[0]); + + return 0; +} +#else +static int inline brl_set_display_mode(struct goodix_ts_core *cd, int mode) { return -EINVAL; } +#endif + +#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; + 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; + + if (len > misc->fw_buffer_max_len) { + ts_err("config len exceed limit %d > %d", + len, misc->fw_buffer_max_len); + 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) + 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: %02x%02x%02x%02x", + version->touch_data_version_id, version->reserved[0], + version->reserved[1], version->reserved[2]); + + 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("frame_data_addr: 0x%04x", + misc->frame_data_addr); + 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 +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS +#define POINT_TYPE_PALM 0x04 +#endif +#define GOODIX_PALM_FLAG 0x10 + +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; + } +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS + if ((coor_data[0] & 0x0F) == POINT_TYPE_PALM) + touch_data->coords[id].plam_status = true; + else + touch_data->coords[id].plam_status = false; +#endif + 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; + } +} +#ifdef CONFIG_GTP_FOD +#define GOODIX_FP_EVENTS 0x8 +#define GOODIX_GESTURE_FOD_DOWN 0x46 +#define GOODIX_GESTURE_FOD_UP 0x55 +#endif +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; +#ifdef CONFIG_GTP_FOD + int fp_flags = 0; + static int pre_flags = 0; +#endif + int underwater_flag = 0; +#ifdef GOODIX_PALM_SENSOR_EN + int palm_flag = 0; +#endif + /* clean event buffer */ + memset(ts_event, 0, sizeof(*ts_event)); + /* copy pre-data to buffer */ + memcpy(buffer, pre_buf, pre_buf_len); + +#ifdef CONFIG_ENABLE_GTP_PALM_CANCEL + if (buffer[2] & GOODIX_PALM_FLAG) { + ts_event->touch_data.palm_on = true; + ts_debug("goodix palm on, touch num %d", pre_buf[2] & 0x0F); + } else + ts_event->touch_data.palm_on = false; +#endif + + if (cd->set_mode.liquid_detection) { + underwater_flag = buffer[2] & GOODIX_GESTURE_UNDER_WATER; + if (cd->liquid_status != underwater_flag) { + cd->liquid_status = underwater_flag; + /* call class method */ + cd->imports->report_liquid_detection_status(cd->bus->dev, cd->liquid_status? 1:0); + ts_info("under water flag changed to 0x%x\n", cd->liquid_status); + } + } + +#ifdef GOODIX_PALM_SENSOR_EN + if (cd->set_mode.palm_detection) { + palm_flag = buffer[2] & GOODIX_GESTURE_PALM_DETECTION; + if (palm_flag) { + mod_timer(&cd->palm_release_timer, + jiffies + msecs_to_jiffies(cd->palm_release_delay_ms)); + } + if (atomic_read(&cd->palm_status) != palm_flag) { + atomic_set(&cd->palm_status, palm_flag); + /* call class method */ + if (cd->imports && cd->imports->report_palm) + cd->imports->report_palm(palm_flag); + ts_info("palm detection flag changed to: 0x%x\n", palm_flag); + } + } +#endif + + 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; + } + } + + 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; + } + } + } +#ifdef CONFIG_GTP_FOD + ts_debug("touch pre_buf[0]=0x%x", pre_buf[0]); + fp_flags = pre_buf[0] & GOODIX_FP_EVENTS; + if(pre_flags != fp_flags) { + if(fp_flags) + ts_event->gesture_type = GOODIX_GESTURE_FOD_DOWN; + else + ts_event->gesture_type = GOODIX_GESTURE_FOD_UP; + } + pre_flags = fp_flags; +#endif + 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; + } + } + + /* Need to resend the stylus cmd after abnormal reseting */ + if (cd->board_data.stylus_mode_ctrl && cd->get_mode.stylus_mode + && cd->set_mode.stylus_mode) { + if ((buffer[3] & 0x20) == 0) { + struct goodix_ts_cmd stylus_cmd; + stylus_cmd.cmd = 0xA4; + stylus_cmd.len = 5; + stylus_cmd.data[0] = 0x01; + ret = brl_send_cmd(cd, &stylus_cmd); + if (ret < 0) + ts_err("Failed to send stylus enable command\n"); + else + ts_info("IC abnormal reset, resend stylus enable command\n"); + } + } + + return 0; +} + +#ifdef CONFIG_GTP_GHOST_LOG_CAPTURE +static int discard_frames = 3; +static int frame_cnt; +static int freq_index; +static int process; +static size_t total_cnt; + +int frame_log_capture_stop(struct goodix_ts_core *cd) +{ + cd->hw_ops->reset(cd, 100); + vfree(frame_log.buf); + ts_info("stop ghost log capture, trigger_enable state change to 0"); + return 0; +} + +int frame_log_capture_start(struct goodix_ts_core *cd) +{ + struct goodix_ts_cmd tmp_cmd; + + if (atomic_read(&cd->allow_capture) == 0) { + ts_info("Touch not active, not allow ghost log capture"); + return 0; + } + + if (atomic_read(&cd->trigger_enable) != 0) + return 0; + + frame_log.buf = vmalloc(4 * 1024); + if (!frame_log.buf) + return -ENOMEM; + + //init parameters + frame_log.used = 0; + cd->data_valid = 1; + discard_frames = 3; + frame_cnt = 0; + freq_index = 0; + process = 0; + total_cnt = 0; + + tmp_cmd.len = 5; + tmp_cmd.cmd = 0x90; + tmp_cmd.data[0] = 0x83; + cd->hw_ops->send_cmd(cd, &tmp_cmd); + atomic_set(&cd->trigger_enable, 1); + ts_info("start ghost log capture"); + return 0; +} + +static void goodix_cache_debug_log(struct goodix_ts_core *cd) +{ + u8 sync = 0; + u8 diff_cmd[] = {0x00, 0x00, 0x05, 0x90, 0x82, 0x17, 0x01}; + u8 raw_cmd[] = {0x00, 0x00, 0x05, 0x90, 0x81, 0x16, 0x01}; + u8 b_cmd[] = {0x00, 0x00, 0x05, 0x90, 0x87, 0x1C, 0x01}; + u8 freq_cmd[] = {0x00, 0x00, 0x05, 0x9C, 0x00, 0xA1, 0x00}; + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + struct goodix_ic_info_misc *misc = &cd->ic_info.misc; + u32 cmd_addr = misc->cmd_addr; + int freq_num = cd->ic_info.parm.mutual_freq_num; + u8 frame_type = 0; + u8 *frame_ptr = misc->frame_data_addr - misc->touch_data_addr + cd->trigger_buf; + int frame_len = le16_to_cpup((__le16 *)(frame_ptr + 3)); + + hw_ops->write(cd, misc->frame_data_addr, &sync, 1); + if (discard_frames > 0) { + discard_frames--; + return; + } + + if (process == PROCESS_REF) + frame_type = 0x01; + else if (process == PROCESS_DIFF) + frame_type = 0x03; + else if (process == PROCESS_RAW) + frame_type = 0x02; + else if (process == PROCESS_B_ARRAY) + frame_type = 0x04; + + memset(frame_log.buf + frame_log.used, 0xAA, 4); + frame_log.used += 4; + frame_log.buf[frame_log.used++] = frame_len & 0xFF; + frame_log.buf[frame_log.used++] = (frame_len >> 8) & 0xFF; + frame_log.buf[frame_log.used++] = frame_type; + + //judge if frame_len is within limit + if (frame_len > MAX_FRAME_LENGTH) { + ts_info("frame_len is too long, %d", frame_len); + frame_len = MAX_FRAME_LENGTH; + cd->data_valid = 0; + } + + memcpy(frame_log.buf + frame_log.used, frame_ptr, frame_len); + frame_log.used += frame_len; + + total_cnt += frame_log.used; + ts_info("frame log used:%d, total cnt:%zu", frame_log.used, total_cnt); + put_fifo_with_discard(frame_log.buf, frame_log.used); + memset(frame_log.buf, 0x0, sizeof(frame_log.used)); + frame_log.used = 0; + + if (process == PROCESS_REF) { + hw_ops->write(cd, cmd_addr, diff_cmd, (int)sizeof(diff_cmd)); + discard_frames = 3; + process = PROCESS_DIFF; + } else if (process == PROCESS_DIFF) { + frame_cnt++; + if (frame_cnt >= TRIGGER_FRAME_CNT) { + hw_ops->write(cd, cmd_addr, raw_cmd, (int)sizeof(raw_cmd)); + discard_frames = 3; + frame_cnt = 0; + process = PROCESS_RAW; + } + } else if (process == PROCESS_RAW) { + frame_cnt++; + if (frame_cnt >= TRIGGER_FRAME_CNT) { + discard_frames = 3; + frame_cnt = 0; + freq_cmd[4] = freq_index; + freq_cmd[5] += freq_index; + freq_index++; + if (freq_index > freq_num) { + process = 0; + freq_index = 0; + hw_ops->reset(cd, 100); + atomic_set(&cd->trigger_enable, 0); + total_cnt = 0; + vfree(frame_log.buf); + ts_info("Notify raw data capture down, data_valid:%d", cd->data_valid); + if (cd->data_valid == 1) + sysfs_notify(cd->imports->kobj_notify, NULL, "log_trigger"); + } else { + hw_ops->write(cd, cmd_addr, freq_cmd, (int)sizeof(freq_cmd)); + usleep_range(5000, 5100); + hw_ops->write(cd, cmd_addr, b_cmd, (int)sizeof(b_cmd)); + process = PROCESS_B_ARRAY; + } + } + } else if (process == PROCESS_B_ARRAY) { + discard_frames = 3; + frame_cnt = 0; + process = PROCESS_DIFF; + hw_ops->write(cd, cmd_addr, diff_cmd, (int)sizeof(diff_cmd)); + } +} +#endif + +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 = 0; + + pre_read_len = IRQ_EVENT_HEAD_LEN + + BYTES_PER_POINT * 2 + COOR_DATA_CHECKSUM_SIZE; +#ifdef CONFIG_GTP_GHOST_LOG_CAPTURE + if (atomic_read(&cd->trigger_enable) == 1) { + mutex_lock(&cd->frame_log_lock); + ret = hw_ops->read(cd, misc->touch_data_addr, cd->trigger_buf, sizeof(cd->trigger_buf)); + memcpy(pre_buf, cd->trigger_buf, pre_read_len); + goodix_cache_debug_log(cd); + mutex_unlock(&cd->frame_log_lock); + } else { + ret = hw_ops->read(cd, misc->touch_data_addr, + pre_buf, pre_read_len); + } +#else + ret = hw_ops->read(cd, misc->touch_data_addr, + pre_buf, pre_read_len); +#endif + if (ret) { + ts_debug("failed get event head data"); + return ret; + } + + if (checksum_cmp(pre_buf, IRQ_EVENT_HEAD_LEN, CHECKSUM_MODE_U8_LE)) { + ts_debug("touch head checksum err"); + ts_debug("touch_head %*ph", IRQ_EVENT_HEAD_LEN, pre_buf); + ts_event->retry = 1; + 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); + + } else 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]); + } else if (event_status & GOODIX_GESTURE_EVENT) { + ts_event->event_type = EVENT_GESTURE; + ts_event->gesture_report_info = pre_buf[2]; + ts_event->gesture_type = pre_buf[4]; +#ifdef CONFIG_GTP_FOD + memcpy(ts_event->gesture_data, &pre_buf[8], + GOODIX_GESTURE_DATA_LEN); +#endif + } else { + ts_info("Unsupported event status"); + return -EINVAL; + } + 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; + + 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, + .dev_confirm = brl_dev_confirm, + .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, + .display_mode = brl_set_display_mode, +}; + +struct goodix_ts_hw_ops *goodix_get_hw_ops(void) +{ + return &brl_hw_ops; +} diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_i2c.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_i2c.c new file mode 100644 index 000000000000..defa5cf03510 --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_i2c.c @@ -0,0 +1,283 @@ + /* + * 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 +#include +#include + +#include "goodix_ts_core.h" +#include + +#define TS_DRIVER_NAME "goodix_ts" +#define I2C_MAX_TRANSFER_SIZE 256 +#define GOODIX_BUS_RETRY_TIMES 20 +#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"); + + if (client->dev.of_node && !mmi_device_is_available(client->dev.of_node)) { + ts_err("mmi: device not supported\n"); + return -ENODEV; + } + + ret = i2c_check_functionality(client->adapter, + I2C_FUNC_I2C); + if (!ret) + return -EIO; + + /* 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; + + i2c_set_clientdata(client, goodix_pdev); + + 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: + i2c_set_clientdata(client, NULL); + 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"); + if (mmi_check_dynamic_device_node("goodix_ts_i2c")) + return i2c_add_driver(&goodix_i2c_driver); + return -ENODEV; +} + +void goodix_i2c_bus_exit(void) +{ + ts_info("Goodix i2c driver exit"); + i2c_del_driver(&goodix_i2c_driver); +} diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_spi.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_brl_spi.c new file mode 100644 index 000000000000..956f2dd209dc --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/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 +#include +#include + +#include "goodix_ts_core.h" +#include + +#define TS_DRIVER_NAME "goodix_ts" + +#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; + +/** + * 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) { + ts_err("alloc tx_buf failed, size:%d", + SPI_WRITE_PREFIX_LEN + len); + 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; + + ts_info("goodix spi probe in"); + + if (spi->dev.of_node && !mmi_device_is_available(spi->dev.of_node)) { + ts_err("%s mmi: device not supported\n", __func__); + return -ENODEV; + } + + /* 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; + + goodix_spi_bus.ic_type = ret; + goodix_spi_bus.bus_type = GOODIX_BUS_TYPE_SPI; + goodix_spi_bus.dev = &spi->dev; + if (goodix_spi_bus.ic_type == IC_TYPE_BERLIN_A) + goodix_spi_bus.read = goodix_spi_read_bra; + else + goodix_spi_bus.read = goodix_spi_read; + goodix_spi_bus.write = goodix_spi_write; + /* ts core device */ + goodix_pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL); + if (!goodix_pdev) + return -ENOMEM; + + spi_set_drvdata(spi, goodix_pdev); + + 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/platfrom/goodix_ts.0 + * goodix_pdev->dev.parent = &client->dev; + */ + goodix_pdev->dev.platform_data = &goodix_spi_bus; + 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: + spi_set_drvdata(spi, NULL); + 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,gt9916P",}, + {}, +}; +#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); +} diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_cfg_bin.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_cfg_bin.c new file mode 100644 index 000000000000..ef79a1fb5875 --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_cfg_bin.c @@ -0,0 +1,342 @@ + /* + * 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 "goodix_ts_mmi.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) { + ts_err("cfg_pkgs, allocate memory ERROR"); + 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++) { + if (cd->ic_configs[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) +{ +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) && defined(CONFIG_GTP_LIMIT_USE_SUPPLIER) + if (cd->supplier) { + snprintf(cd->board_data.cfg_bin_name, GOODIX_MAX_STR_LABLE_LEN, "%s_%s", + cd->supplier, TS_DEFAULT_CFG_BIN); + } +#endif + + if (cd->hw_ops->read_version(cd, &cd->fw_version)) { + ts_info("version info abnormal, abort"); + return -EINVAL; + } + + return goodix_get_config_data(cd, cd->fw_version.sensor_id); +} + + diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_config.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_config.c new file mode 100644 index 000000000000..9f703ade9dff --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_config.c @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2022 Motorola Mobility LLC + * + * 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. + */ +#include "goodix_ts_config.h" + +struct goodix_ic_report_rate_config report_rate_config_info = { +#if defined(PRODUCT_LI) || defined(PRODUCT_ONELI) || defined(PRODUCT_TUNDRA) \ + ||defined(PRODUCT_EQS) || defined(PRODUCT_RTWO) || defined(PRODUCT_BRONCO) \ + || defined(PRODUCT_ZEEKR) || defined(PRODUCT_LYNKCO) || defined(PRODUCT_BANGKK) \ + || defined(PRODUCT_CTWO) + .rate_config_count = 2, + .refresh_rate_ctrl = 0, + .interpolation_ctrl = 1, + { + { + .interpolation_flag = 0, + .report_rate = 240, + .command = 0x9D01, + }, + { + .interpolation_flag = 1, + .report_rate = 360, + .command = 0x9D02, + }, + } +#elif defined(PRODUCT_HIPHIC) + .rate_config_count = 3, + .refresh_rate_ctrl = 1, + .interpolation_ctrl = 1, + { + { + .interpolation_flag = 0, + .refresh_rate = {48, 144}, + .report_rate = 240, + .command = 0x9D01, + }, + { + .interpolation_flag = 1, + .refresh_rate = {144, 144}, + .report_rate = 576, + .command = 0xC103, + }, + { + .interpolation_flag = 1, + .refresh_rate = {48, 120}, + .report_rate = 480, + .command = 0xC102, + }, + } +#elif defined(PRODUCT_HIPHI) + .rate_config_count = 2, + .refresh_rate_ctrl = 0, + .interpolation_ctrl = 1, + { + { + .interpolation_flag = 0, + .report_rate = 360, + .command = 0x9D02, + }, + { + .interpolation_flag = 1, + .report_rate = 720, + .command = 0xC101, + }, + } +#endif +}; + +int goodix_ts_mmi_get_report_rate(struct goodix_ts_core *core_data) +{ + int refresh_rate_ctrl = 0; + int interpolation_ctrl = 0; + int interpolation_flag = 0; + int refresh_rate = 0; + int i = 0; + + refresh_rate_ctrl = core_data->board_data.report_rate_ctrl; + interpolation_ctrl = core_data->board_data.interpolation_ctrl; + + interpolation_flag = core_data->get_mode.interpolation; + refresh_rate = core_data->refresh_rate; + + ts_debug("refresh_rate_ctrl: %d, interpolation_ctrl: %d, interpolation_flag: %d, refresh_rate: %d", + refresh_rate_ctrl, interpolation_ctrl, interpolation_flag, refresh_rate); + + if (refresh_rate_ctrl == 0 && interpolation_ctrl == 1) { + for (i = 0; i < report_rate_config_info.rate_config_count; i++) { + if (interpolation_flag == report_rate_config_info.report_rate_info[i].interpolation_flag) { + break; + } + } + } else if (refresh_rate_ctrl == 1 && interpolation_ctrl == 1) { + for (i = 0; i < report_rate_config_info.rate_config_count; i++) { + if ((interpolation_flag == report_rate_config_info.report_rate_info[i].interpolation_flag) && + ((refresh_rate >= report_rate_config_info.report_rate_info[i].refresh_rate[0]) && + (refresh_rate <= report_rate_config_info.report_rate_info[i].refresh_rate[1]))) { + break; + } + } + } else if (refresh_rate_ctrl == 1 && interpolation_ctrl == 0) { + for (i = 0; i < report_rate_config_info.rate_config_count; i++) { + if ((refresh_rate >= report_rate_config_info.report_rate_info[i].refresh_rate[0]) && + (refresh_rate <= report_rate_config_info.report_rate_info[i].refresh_rate[1])) { + break; + } + } + } else { + //refresh_rate_ctrl = 0, interpolation_ctrl = 0 + i = 0; + } + + if (i == report_rate_config_info.rate_config_count) { + ts_err("Get config report rate fail"); + return -1; + } else { + ts_debug("Get config report rate %dHZ, command : 0x%02x ", + report_rate_config_info.report_rate_info[i].report_rate, + report_rate_config_info.report_rate_info[i].command); + return report_rate_config_info.report_rate_info[i].command; + } +} + diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_config.h b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_config.h new file mode 100644 index 000000000000..fea8c426e92e --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_config.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 Motorola Mobility LLC + * + * 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. + */ +#ifndef __GOODIX_TS_CONFIG_H__ +#define __GOODIX_TS_CONFIG_H__ + +#include +#include "goodix_ts_core.h" + +#define MAX_REPORT_RATE_CONFIG 12 +#define REPORT_RATE_CMD_180HZ 0x9D00 +#define REPORT_RATE_CMD_240HZ 0x9D01 +#define REPORT_RATE_CMD_360HZ 0x9D02 +#define REPORT_RATE_CMD_LOW 0x9D +#define REPORT_RATE_CMD_480HZ 0xC102 +#define REPORT_RATE_CMD_576HZ 0xC103 +#define REPORT_RATE_CMD_720HZ 0xC101 +#define REPORT_RATE_CMD_HIGH 0xC1 + +struct report_rate_config { + bool interpolation_flag; //if enable interpolation report rate + u16 refresh_rate[2]; //display refresh rate + u16 report_rate; //touch report rate + u16 command; //report rate switch command +}; + +struct goodix_ic_report_rate_config { + u8 rate_config_count; //the count of report rate combination + bool refresh_rate_ctrl; //if support report rate change according to refresh rate + bool interpolation_ctrl; //if support interpolation report rate + struct report_rate_config report_rate_info[MAX_REPORT_RATE_CONFIG]; +}; + +int goodix_ts_mmi_get_report_rate(struct goodix_ts_core *core_data); + +#endif diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_core.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_core.c new file mode 100644 index 000000000000..eb79956856f1 --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_core.c @@ -0,0 +1,2801 @@ + /* + * 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 +#include +#include +#include +#include "goodix_ts_core.h" + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) +#include +#define INPUT_TYPE_B_PROTOCOL +#endif + +#include "goodix_ts_mmi.h" + +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" +#define GOODIX_DEFAULT_CFG_NAME "goodix_cfg_group.cfg" +#define GOOIDX_INPUT_PHYS "goodix_ts/input0" + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)) +#define PDE_DATA(x) pde_data(x) +#endif + +struct goodix_module goodix_modules; +int core_module_prob_sate = CORE_MODULE_UNPROBED; + +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("goodix_register_ext_module IN"); + + goodix_core_module_init(); + INIT_WORK(&module->work, goodix_register_ext_module_work); + schedule_work(&module->work); + + ts_info("goodix_register_ext_module 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("goodix_register_ext_module_no_wait 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 goodix_ts_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 goodix_ts_chip_info_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; + struct goodix_fw_version chip_ver; + u8 temp_pid[8] = {0}; + int ret; + int cnt = -EINVAL; + + if (hw_ops->read_version) { + ret = hw_ops->read_version(core_data, &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(core_data, &core_data->ic_info); + if (!ret) { + cnt += snprintf(&buf[cnt], PAGE_SIZE, + "config_id:%x\n", core_data->ic_info.version.config_id); + cnt += snprintf(&buf[cnt], PAGE_SIZE, + "config_version:%x\n", core_data->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 goodix_ts_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 en; + int ret; + + if (sscanf(buf, "%d", &en) != 1) + return -EINVAL; + + if (en != 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) +{ + if (!buf || count <= 0) + return -EINVAL; + + if (buf[0] != '0') + goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); + else + 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, 0444, goodix_ts_driver_info_show, NULL); +static DEVICE_ATTR(chip_info, 0444, goodix_ts_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, 0444, goodix_ts_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) { + ts_err("rawdata_proc_show, input null ptr"); + return -EIO; + } + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ts_err("Failed to alloc rawdata info memory"); + 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_printf(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_printf(m, "\n"); + } + seq_printf(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_printf(m, "\n"); + } + +exit: + kfree(info); + return ret; +} + +static int rawdata_proc_open(struct inode *inode, struct file *file) +{ +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE + return single_open_size(file, rawdata_proc_show, PDE_DATA(inode), PAGE_SIZE * 10); +#else + return single_open(file, rawdata_proc_show, PDE_DATA(inode)); +#endif +} + +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +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) +{ + if (!proc_mkdir("goodix_ts", NULL)) + return; + proc_create_data("goodix_ts/tp_capacitance_data", + 0666, NULL, &rawdata_proc_fops, core_data); +} + +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; +} + +#ifdef 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; + } + + 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; + } + + 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; + } + + 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)) + strncpy(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)) + strncpy(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); + strncpy(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); + strncpy(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); + strncpy(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); + strncpy(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 ic compatible */ + r = of_property_read_string(node, "compatible", &name_tmp); + if (r) { + ts_err("get compatible failed"); + return r; + } else { + ts_info("ic_name form dt: %s", name_tmp); + strncpy(board_data->ic_name, name_tmp, sizeof(board_data->ic_name)); + } + + /*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); + + board_data->stylus_mode_ctrl = of_property_read_bool(node, + "goodix,stylus_mode-ctrl"); + if (board_data->stylus_mode_ctrl) { + ts_info("support goodix stylus mode"); + /* get stylus clock name */ + r = of_property_read_string(node, "goodix,stylus-clk-source", &name_tmp); + if (!r) { + ts_info("stylus clk source from source %s", name_tmp); + strncpy(board_data->stylus_clk_src, name_tmp, sizeof(board_data->stylus_clk_src)); + } + } + + board_data->sensitivity_ctrl = of_property_read_bool(node, + "goodix,sensitivity-ctrl"); + if (board_data->sensitivity_ctrl) + ts_info("support goodix sensitivity mode"); + + board_data->leather_mode_ctrl = of_property_read_bool(node, + "goodix,leather_mode-ctrl"); + if (board_data->leather_mode_ctrl) + ts_info("support goodix leather mode"); + + board_data->film_mode_ctrl = of_property_read_bool(node, + "goodix,film_mode-ctrl"); + if (board_data->film_mode_ctrl) + ts_info("support goodix film mode"); + + board_data->interpolation_ctrl = of_property_read_bool(node, + "goodix,interpolation-ctrl"); + if (board_data->interpolation_ctrl) + ts_info("support goodix interpolation mode"); + + board_data->sample_ctrl = of_property_read_bool(node, + "goodix,sample-ctrl"); + if (board_data->sample_ctrl) + ts_info("support goodix sample mode"); + + board_data->report_rate_ctrl = of_property_read_bool(node, + "goodix,report_rate-ctrl"); + if (board_data->report_rate_ctrl) + ts_info("support goodix report rate switch mode"); + + board_data->edge_ctrl = of_property_read_bool(node, + "goodix,edge-ctrl"); + if (board_data->edge_ctrl) + ts_info("support goodix edge mode"); + + board_data->stowed_mode_ctrl = of_property_read_bool(node, + "goodix,stowed-mode-ctrl"); + if (board_data->stowed_mode_ctrl) + ts_info("Support goodix touch stowed mode"); + + if (of_property_read_bool(node, "goodix,gesture-wait-pm")) { + ts_info("gesture-wait-pm set"); + board_data->gesture_wait_pm = true; + } else { + board_data->gesture_wait_pm = false; + } + + 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); + } + +#ifdef CONFIG_GTP_DDA_STYLUS + goodix_dda_process_pen_report(pen_data); +#endif + + /* 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); +} +#ifdef CONFIG_GTP_FOD +#define GOODIX_GESTURE_FOD_DOWN 0x46 +#define GOODIX_GESTURE_FOD_UP 0x55 +#endif +static void goodix_ts_report_finger(struct input_dev *dev, + struct goodix_touch_data *touch_data) +{ + unsigned int touch_num = touch_data->touch_num; + int i; + static uint8_t touchdown[GOODIX_MAX_TOUCH]; + struct goodix_ts_core *core_data = goodix_modules.core_data; + +#ifdef CONFIG_GTP_FOD + struct goodix_ts_event *ts_event = &goodix_modules.core_data->ts_event; +#endif + enum touch_event_mode type; + +#ifdef CONFIG_ENABLE_GTP_PALM_CANCEL + unsigned int tool_type; +#endif + + mutex_lock(&dev->mutex); + +#ifdef CONFIG_ENABLE_GTP_PALM_CANCEL + tool_type = touch_data->palm_on ? MT_TOOL_PALM : MT_TOOL_FINGER; +#endif + for (i = 0; i < GOODIX_MAX_TOUCH; i++) { + if (touch_data->coords[i].status == TS_TOUCH) { +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS + ts_debug("report: id %d, x %d, y %d, w %d, palm %d", i, + touch_data->coords[i].x, touch_data->coords[i].y, + touch_data->coords[i].w, touch_data->coords[i].plam_status); +#else + 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); +#endif + if (touchdown[i] == 0) { +#ifdef CONFIG_GTP_LAST_TIME + core_data->last_event_time = ktime_get_boottime(); + ts_debug("TOUCH: [%d] logged timestamp\n", i); +#endif + touchdown[i] = 1; + type = TS_COORDINATE_ACTION_PRESS; + } else + type = TS_COORDINATE_ACTION_MOVE; + + /* clip area handling */ + if (core_data->imports && core_data->imports->clip_touch_event) { + struct touch_event_data tev; + memset(&tev, 0, sizeof(tev)); + tev.type = type; /* differentiate PRESS and MOVE */ + tev.id = i; + tev.x = touch_data->coords[i].x; + tev.y = touch_data->coords[i].y; + core_data->imports->clip_touch_event(core_data->bus->dev, &tev, dev); + if (tev.skip_report) { + /* if active touch get clipped, */ + /* need to cancel it properly */ + if (tev.type == TS_COORDINATE_ACTION_FORCE_LIFT) { + /* prevent sending BTN_TOUCH event */ + touch_num--; + } + /* input event should not be reported */ + continue; + } + } + /* end of clip area handling */ + + input_mt_slot(dev, i); +#ifdef CONFIG_ENABLE_GTP_PALM_CANCEL +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS + if ((tool_type != MT_TOOL_PALM) && touch_data->coords[i].plam_status) + tool_type = MT_TOOL_PALM; + input_mt_report_slot_state(dev, tool_type, true); +#else + input_mt_report_slot_state(dev, tool_type, true); +#endif +#else + input_mt_report_slot_state(dev, MT_TOOL_FINGER, true); +#endif + 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); +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS + input_report_abs(dev, ABS_MT_PRESSURE, + touch_data->coords[i].w); +#endif + } else { + if (touchdown[i] == 1) { + ts_debug("TOUCH: [%d] release\n", i); + touchdown[i] = 0; + input_mt_slot(dev, i); +#ifdef CONFIG_ENABLE_GTP_PALM_CANCEL + input_mt_report_slot_state(dev, tool_type, false); +#else + input_mt_report_slot_state(dev, MT_TOOL_FINGER, false); +#endif + } + } + } + + input_report_key(dev, BTN_TOUCH, touch_num > 0 ? 1 : 0); + input_sync(dev); +#ifdef CONFIG_GTP_FOD + if(core_data->fod_enable) { + if(ts_event->gesture_type == GOODIX_GESTURE_FOD_DOWN && touch_num > 0) { + input_report_key(dev, BTN_TRIGGER_HAPPY1, 1); + input_sync(dev); + input_report_key(dev, BTN_TRIGGER_HAPPY1, 0); + input_sync(dev); + ts_info("report BTN_TRIGGER_HAPPY1"); + }else if(ts_event->gesture_type == GOODIX_GESTURE_FOD_UP) { + input_report_key(dev, BTN_TRIGGER_HAPPY2, 1); + input_sync(dev); + input_report_key(dev, BTN_TRIGGER_HAPPY2, 0); + input_sync(dev); + ts_info("report BTN_TRIGGER_HAPPY2"); + } + } + ts_debug("fod_enable= %d, gesture_type =%x, touch_num= %d", core_data->fod_enable, + ts_event->gesture_type, touch_num); +#endif + + 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; + +#ifdef CONFIG_GTP_ENABLE_PM_QOS + cpu_latency_qos_update_request(&core_data->goodix_pm_qos, 0); +#endif + + 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); + } + 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); + } + } + + if (!core_data->tools_ctrl_sync && !ts_event->retry) + hw_ops->after_event_handler(core_data); + ts_event->retry = 0; + +#ifdef CONFIG_GTP_ENABLE_PM_QOS + cpu_latency_qos_update_request(&core_data->goodix_pm_qos, PM_QOS_DEFAULT_VALUE); +#endif + + 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_ts_threadirq_func, + 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; + } + + ret = regulator_set_load(core_data->avdd, 50000); + if (ret) { + ts_err("set avdd load fail"); + return ret; + } + ret = regulator_set_voltage(core_data->avdd, 3000000, 3000000); + if (ret) { + ts_err("set avdd voltage fail"); + 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_stylus_clk_init - Config stylus clock + * return:0 ok, <0 failed + */ +static int goodix_ts_stylus_clk_init(struct goodix_ts_core *core_data) +{ + int ret = 0; + struct goodix_ts_board_data *ts_bdata = board_data(core_data); + struct goodix_bus_interface *bus_interface = core_data->bus; + if (!ts_bdata->stylus_mode_ctrl) { + ts_err("stylus not support!"); + return -EINVAL; + } + if (!strcmp(ts_bdata->stylus_clk_src, STYLUS_CLK_SRC_PMIC)) { + if (of_get_property(bus_interface->dev->of_node, "clocks", NULL)) { + core_data->stylus_clk = devm_clk_get(bus_interface->dev, "stylus_clk"); + if (IS_ERR_OR_NULL(core_data->stylus_clk)) { + ts_err("failed to get stylus clk\n"); + return -EINVAL; + } + } + } else if (!strcmp(ts_bdata->stylus_clk_src, STYLUS_CLK_SRC_GPIO)){ + if (IS_ERR_OR_NULL(core_data->pinctrl)) { + ts_info("Failed to get pinctrl handler[need confirm]"); + core_data->pinctrl = NULL; + return -EINVAL; + } + core_data->stylus_clk_active = pinctrl_lookup_state(core_data->pinctrl, + PINCTRL_STYLUS_CLK_ACTIVE); + if (IS_ERR_OR_NULL(core_data->stylus_clk_active)) { + ret = PTR_ERR(core_data->stylus_clk_active); + ts_err("Failed to get state clk pinctrl state:%s, ret:%d", + PINCTRL_STYLUS_CLK_ACTIVE, ret); + core_data->stylus_clk_active = NULL; + return ret; + } + ts_info("success get stylus clk pinctrl active state"); + + core_data->stylus_clk_suspend = pinctrl_lookup_state(core_data->pinctrl, + PINCTRL_STYLUS_CLK_SUSPEND); + if (IS_ERR_OR_NULL(core_data->stylus_clk_suspend)) { + ret = PTR_ERR(core_data->stylus_clk_suspend); + ts_err("Failed to get state clk pinctrl state:%s, ret:%d", + PINCTRL_STYLUS_CLK_SUSPEND, ret); + core_data->stylus_clk_suspend = NULL; + return ret; + } + ts_info("success get stylus clk pinctrl suspend state"); + } else { + ts_err("The stylus clock source is invalid"); + return -EINVAL; + } + + return ret; +} + +/** + * goodix_ts_pinctrl_init - Get pinctrl handler and pinctrl_state + * @core_data: pointer to touch core data + * return: 0 ok, <0 failed + */ +static int goodix_ts_pinctrl_init(struct goodix_ts_core *core_data) +{ + int r = 0; + + /* get pinctrl handler from of node */ + core_data->pinctrl = devm_pinctrl_get(core_data->bus->dev); + if (IS_ERR_OR_NULL(core_data->pinctrl)) { + ts_info("Failed to get pinctrl handler[need confirm]"); + core_data->pinctrl = NULL; + return -EINVAL; + } + ts_debug("success get pinctrl"); + /* active state */ + core_data->pin_sta_active = pinctrl_lookup_state(core_data->pinctrl, + PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(core_data->pin_sta_active)) { + r = PTR_ERR(core_data->pin_sta_active); + ts_err("Failed to get pinctrl state:%s, r:%d", + PINCTRL_STATE_ACTIVE, r); + core_data->pin_sta_active = NULL; + goto exit_pinctrl_put; + } + ts_debug("success get avtive pinctrl state"); + + /* suspend state */ + core_data->pin_sta_suspend = pinctrl_lookup_state(core_data->pinctrl, + PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(core_data->pin_sta_suspend)) { + r = PTR_ERR(core_data->pin_sta_suspend); + ts_err("Failed to get pinctrl state:%s, r:%d", + PINCTRL_STATE_SUSPEND, r); + core_data->pin_sta_suspend = NULL; + goto exit_pinctrl_put; + } + ts_debug("success get suspend pinctrl state"); + + return 0; +exit_pinctrl_put: + devm_pinctrl_put(core_data->pinctrl); + core_data->pinctrl = NULL; + return r; +} + +/** + * 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; + + __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); + +#ifdef INPUT_PROP_DIRECT + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); +#endif + + /* 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 CONFIG_MOTO_DDA_PASSIVESTYLUS + input_set_abs_params(input_dev, ABS_MT_PRESSURE, + 0, ts_bdata->panel_max_w, 0, 0); +#endif +#ifdef CONFIG_ENABLE_GTP_PALM_CANCEL + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, + MT_TOOL_FINGER, MT_TOOL_PALM, 0, 0); +#endif +#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); +#ifdef CONFIG_GTP_FOD + input_set_capability(input_dev, EV_KEY, BTN_TRIGGER_HAPPY1); + input_set_capability(input_dev, EV_KEY, BTN_TRIGGER_HAPPY2); +#endif + + 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, 1 * 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, 1 * 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); + + return 0; +} + +void goodix_ts_release_connects(struct goodix_ts_core *core_data) +{ + struct input_dev *input_dev = core_data->input_dev; + struct goodix_ts_event *ts_event; + int i; + + if (!input_dev) { + ts_err("Invalid input device"); + return; + } + 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); + + /* clean event buffer */ + ts_event = &core_data->ts_event; + memset(ts_event, 0, sizeof(*ts_event)); + + mutex_unlock(&input_dev->mutex); +} + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +/** + * 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); + + 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; +} +#endif + +#if defined(CONFIG_FB) && !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +/** + * 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 + + +#ifdef CONFIG_PM +#if !defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND) \ + && !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +/** + * 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); +} +#elif defined (CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI) +static int goodix_ts_pm_suspend(struct device *dev) +{ + struct goodix_ts_core *core_data = + dev_get_drvdata(dev); + + ts_debug("CALL BACK TP PM SUSPEND"); + + atomic_set(&core_data->pm_resume, 0); + + return 0; +} +/** + * 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); + + ts_debug("CALL BACK TP PM RESUME"); + + atomic_set(&core_data->pm_resume, 1); + + if (core_data->board_data.gesture_wait_pm) + wake_up_interruptible(&core_data->pm_wq); + + return 0; +} +#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; + int ret = 0; + + 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: + cd->update_status = 1; + hw_ops->irq_enable(cd, 0); + break; + case NOTIFY_FWUPDATE_SUCCESS: +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + if (cd->need_update_cfg) { + if (goodix_get_config_proc(cd)) { + ts_info("no valid ic config found"); + hw_ops->irq_enable(cd, 1); + break; + } + } +#endif + ret = hw_ops->get_ic_info(cd, &cd->ic_info); + if (ret) + ts_err("invalid ic info [ignore]"); + ret = goodix_send_ic_config(cd, CONFIG_TYPE_NORMAL); + if (ret) + ts_info("failed send normal config[ignore]"); + fallthrough; + 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); + cd->update_status = 0; + break; + default: + break; + } + return 0; +} + +int goodix_ts_stage2_init(struct goodix_ts_core *cd) +{ + int ret; + + /* 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; + } + } + /* 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_FB) && !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + 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 + /* create sysfs files */ + goodix_ts_sysfs_init(cd); + + /* create procfs files */ + goodix_ts_procfs_init(cd); + +#ifdef CONFIG_GOODIX_ESD_ENABLE + /* esd protector */ + goodix_ts_esd_init(cd); +#endif + + /* gesture init */ + gesture_module_init(); + + /* 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; + struct goodix_ts_core *cd = data; + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + + /* setp 1: 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; + } + + /* setp 2: get config data from config bin */ + if (goodix_get_config_proc(cd)) { + ts_info("no valid ic config found"); +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + cd->need_update_cfg = 1; +#endif + } else + ts_info("success get valid ic config"); + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + ret = goodix_do_fw_update(cd->ic_configs[CONFIG_TYPE_NORMAL], + UPDATE_MODE_BLOCK | UPDATE_MODE_SRC_REQUEST); + if (ret) + ts_err("failed do fw update"); +#endif + /* setp3: 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"); +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + goto stage2_init; +#endif + goto uninit_fw; + } + ret = hw_ops->get_ic_info(cd, &cd->ic_info); + if (ret) { + ts_err("invalid ic info, abort"); +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + goto stage2_init; +#endif + goto uninit_fw; + } + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + /* 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); +#endif + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +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; + + 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++) { + if (cd->ic_configs[i]) + kfree(cd->ic_configs[i]); + cd->ic_configs[i] = NULL; + } + 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; +} + +#ifdef GOODIX_PALM_SENSOR_EN +static void goodix_palm_sensor_release_timer_handler(struct timer_list *t) +{ + struct goodix_ts_core *cd = from_timer(cd, t, palm_release_timer); + + if (cd->imports && cd->imports->report_palm && atomic_read(&cd->palm_status)) { + cd->imports->report_palm(false); + ts_info("palm report far"); + atomic_set(&cd->palm_status, 0); + } +} +#endif + +#ifdef GTP_PEN_NOTIFIER +static int pen_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + int ret = 0; + struct goodix_ts_core *cd = container_of(self, + struct goodix_ts_core, pen_notif); + + ts_info("Received event(%lu) for pen detection\n", event); + + if (event == PEN_DETECTION_INSERT) + cd->gtp_pen_detect_flag = 0x0; + else if (event == PEN_DETECTION_PULL) + cd->gtp_pen_detect_flag = 0x1; + + mutex_lock_interruptible(&cd->mode_lock); + + if (cd->power_on == 0) { + ts_err("The touch is in sleep state, restore the value when resume\n"); + goto exit; + } + + ret = goodix_ts_send_cmd(cd, 0x32, 5, cd->gtp_pen_detect_flag, 0x00); + if (ret < 0) { + ts_err("failed to send passive pen mode cmd"); + goto exit; + } + +exit: + mutex_unlock(&cd->mode_lock); + return ret; +} +#endif + +/** + * 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; + + ts_info("goodix_ts_probe IN"); + + bus_interface = pdev->dev.platform_data; + if (!bus_interface) { + ts_err("Invalid touch device"); + core_module_prob_sate = CORE_MODULE_PROB_FAILED; + return -ENODEV; + } + + core_data = devm_kzalloc(&pdev->dev, + sizeof(struct goodix_ts_core), GFP_KERNEL); + if (!core_data) { + ts_err("Failed to allocate memory for core data"); + core_module_prob_sate = CORE_MODULE_PROB_FAILED; + return -ENOMEM; + } + + if (IS_ENABLED(CONFIG_OF) && bus_interface->dev->of_node) { + /* parse devicetree property */ + ret = goodix_parse_dt(bus_interface->dev->of_node, + &core_data->board_data); + if (ret) { + ts_err("failed parse device info form dts, %d", ret); + return -EINVAL; + } + } else { + ts_err("no valid device tree node found"); + return -ENODEV; + } + + core_data->hw_ops = goodix_get_hw_ops(); + if (!core_data->hw_ops) { + ts_err("hw ops is NULL"); + core_module_prob_sate = CORE_MODULE_PROB_FAILED; + return -EINVAL; + } + goodix_core_module_init(); + /* touch core layer is a platform driver */ + core_data->pdev = pdev; + core_data->bus = bus_interface; + platform_set_drvdata(pdev, core_data); + + /* get GPIO resource */ + 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; + } + + /* confirm it's goodix touch dev or not */ + ret = core_data->hw_ops->dev_confirm(core_data); + if (ret) { + ts_err("goodix device confirm failed"); + goto err_out; + } + + /* Pinctrl handle is optional. */ + ret = goodix_ts_pinctrl_init(core_data); + if (!ret && core_data->pinctrl) { + ret = pinctrl_select_state(core_data->pinctrl, + core_data->pin_sta_active); + if (ret < 0) + ts_err("Failed to select active pinstate, r:%d", ret); + } + + /* get stylus clock*/ + ret = goodix_ts_stylus_clk_init(core_data); + if (ret) + ts_err("failed get goodix stylus clock"); + + /* generic notifier callback */ + core_data->ts_notifier.notifier_call = goodix_generic_noti_callback; + goodix_ts_register_notifier(&core_data->ts_notifier); + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + ts_info("%s:goodix_ts_mmi_dev_register",__func__); + ret = goodix_ts_mmi_dev_register(pdev); + if (ret) { + ts_info("Failed register touchscreen mmi."); + goto err_out; + } +#endif + +#ifdef CONFIG_GTP_ENABLE_PM_QOS + cpu_latency_qos_add_request(&core_data->goodix_pm_qos, PM_QOS_DEFAULT_VALUE); +#endif + + PM_WAKEUP_REGISTER(bus_interface->dev, core_data->gesture_wakelock, + "goodix_gesture_wakelock"); + if (!core_data->gesture_wakelock) { + ts_info("allocate gesture wakeup source err!\n"); + ret = -ENOMEM; + goto err_register_gesture_wakelock; + } + + if (core_data->board_data.gesture_wait_pm) + init_waitqueue_head(&core_data->pm_wq); + atomic_set(&core_data->pm_resume, 1); + + /* debug node init */ + goodix_tools_init(); + +#ifdef CONFIG_GTP_DDA_STYLUS + goodix_stylus_dda_init(); + ret = goodix_stylus_dda_register_cdevice(); + if (ret) + ts_err("Failed register stylus dda device, %d", ret); +#endif + + core_data->init_stage = CORE_INIT_STAGE1; + goodix_modules.core_data = core_data; + core_module_prob_sate = CORE_MODULE_PROB_SUCCESS; + + /* Try start a thread to get config-bin info */ + ret = goodix_start_later_init(core_data); + if (ret) { + ts_err("Failed start cfg_bin_proc, %d", ret); + goto err_register_gesture_wakelock; + } + +#ifdef GOODIX_PALM_SENSOR_EN + timer_setup(&core_data->palm_release_timer, goodix_palm_sensor_release_timer_handler, 0); + core_data->palm_release_delay_ms = GOODIX_PALM_RELEASE_DELAY_MS; +#endif + +#ifdef CONFIG_GTP_GHOST_LOG_CAPTURE + goodix_log_capture_register_misc(core_data); + if (ret) + ts_err("Failed register log device, %d", ret); + + atomic_set(&core_data->allow_capture, 1); + ts_info("Enable ghost log capture after probe"); +#endif + +#ifdef GTP_PEN_NOTIFIER + core_data->gtp_pen_detect_flag = PEN_DETECTION_INSERT; + core_data->pen_notif.notifier_call = pen_notifier_callback; + ret = pen_detection_register_client(&core_data->pen_notif); + if (ret) + ts_err("[PEN]Unable to register pen_notifier: %d\n", ret); +#endif + + ts_info("goodix_ts_core probe success"); + return 0; + +err_register_gesture_wakelock: + PM_WAKEUP_UNREGISTER(core_data->gesture_wakelock); +err_out: + core_data->init_stage = CORE_INIT_FAIL; + 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; + +#ifdef CONFIG_GTP_ENABLE_PM_QOS + cpu_latency_qos_remove_request(&core_data->goodix_pm_qos); +#endif +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + ts_info("%s:goodix_ts_mmi_dev_unregister",__func__); + goodix_ts_mmi_dev_unregister(pdev); +#endif + + goodix_ts_unregister_notifier(&core_data->ts_notifier); +#ifdef CONFIG_GTP_DDA_STYLUS + goodix_stylus_dda_exit(); +#endif + goodix_tools_exit(); + +#ifdef CONFIG_GTP_GHOST_LOG_CAPTURE + goodix_log_capture_unregister_misc(core_data); +#endif + + if (core_data->init_stage >= CORE_INIT_STAGE2) { + gesture_module_exit(); + inspect_module_exit(); + hw_ops->irq_enable(core_data, false); + #if defined(CONFIG_FB) && !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + 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; +} + +#ifdef CONFIG_PM +static const struct dev_pm_ops dev_pm_ops = { +#if !defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND) \ + && !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + .suspend = goodix_ts_pm_suspend, + .resume = goodix_ts_pm_resume, +#elif defined (CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI) + .suspend = goodix_ts_pm_suspend, + .resume = goodix_ts_pm_resume, +#endif +}; +#endif + +static const struct platform_device_id ts_core_ids[] = { + {.name = GOODIX_CORE_DRIVER_NAME}, + {} +}; +MODULE_DEVICE_TABLE(platform, ts_core_ids); + +static struct platform_driver goodix_ts_driver = { + .driver = { + .name = GOODIX_CORE_DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .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; + + ts_info("Core layer init:%s", GOODIX_DRIVER_VERSION); +#ifdef CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI + ret = goodix_spi_bus_init(); +#else + ret = goodix_i2c_bus_init(); +#endif + 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); +#ifdef CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI + goodix_spi_bus_exit(); +#else + goodix_i2c_bus_exit(); +#endif +} + +late_initcall(goodix_ts_core_init); +module_exit(goodix_ts_core_exit); + +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +MODULE_DESCRIPTION("Goodix Touchscreen Core Module"); +MODULE_AUTHOR("Goodix, Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_core.h b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_core.h new file mode 100644 index 000000000000..d9d7357594bc --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_core.h @@ -0,0 +1,780 @@ +#ifndef _GOODIX_TS_CORE_H_ +#define _GOODIX_TS_CORE_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#include +#endif +#ifdef CONFIG_FB +#include +#include +#endif +#ifdef CONFIG_GTP_ENABLE_PM_QOS +#include +#endif +#ifdef CONFIG_GTP_LAST_TIME +#include +#endif + +#ifdef GTP_PEN_NOTIFIER +#include +#endif + +#define GOODIX_CORE_DRIVER_NAME "goodix_ts" +#define GOODIX_PEN_DRIVER_NAME "goodix_ts,pen" +#define GOODIX_DRIVER_VERSION "v1.2.3" +#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 64 +#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_PALM_RELEASE_DELAY_MS 200 + + +#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" + +#define PINCTRL_STYLUS_CLK_ACTIVE "stylus_clk_active" +#define PINCTRL_STYLUS_CLK_SUSPEND "stylus_clk_suspend" +#define STYLUS_CLK_SRC_GPIO "stylus_clk_gpio" +#define STYLUS_CLK_SRC_PMIC "stylus_clk_pmic" + +#define GOODIX_GESTURE_DOUBLE_TAP 0xCC +#define GOODIX_GESTURE_SINGLE_TAP 0x4C +#define GOODIX_GESTURE_FOD_DOWN 0x46 +#define GOODIX_GESTURE_FOD_UP 0x55 +#define GOODIX_GESTURE_UNDER_WATER 0x20 +#define GOODIX_GESTURE_PALM_DETECTION 0x40 + +#ifndef fallthrough +#define fallthrough do {} while (0) /* fallthrough */ +#endif + +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, +}; + +#define MAX_SCAN_FREQ_NUM 5 +#define MAX_SCAN_RATE_NUM 5 +#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 + * @pannel_key_map: key map + * @fw_name: name of the firmware image + */ +struct goodix_ts_board_data { + char ic_name[GOODIX_MAX_STR_LABLE_LEN]; + 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 pen_enable; + char fw_name[GOODIX_MAX_STR_LABLE_LEN]; + char cfg_bin_name[GOODIX_MAX_STR_LABLE_LEN]; + char stylus_clk_src[GOODIX_MAX_STR_LABLE_LEN]; /*stylus clock source*/ + + bool sensitivity_ctrl; + bool stylus_mode_ctrl; + bool film_mode_ctrl; + bool leather_mode_ctrl; + bool interpolation_ctrl; + bool sample_ctrl; + bool report_rate_ctrl; + bool edge_ctrl; + bool stowed_mode_ctrl; + bool gesture_wait_pm; +}; + +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 */ +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS + int plam_status; +#endif + 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; + bool palm_on; + 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 { + int retry; + enum ts_event_type event_type; + u8 request_code; /* represent the request type */ + u8 gesture_type; + u8 gesture_report_info; + struct goodix_touch_data touch_data; + struct goodix_pen_data pen_data; +#ifdef CONFIG_GTP_FOD + u8 gesture_data[GOODIX_GESTURE_DATA_LEN]; +#endif +}; + +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 (*dev_confirm)(struct goodix_ts_core *cd); + int (*resume)(struct goodix_ts_core *cd); + int (*suspend)(struct goodix_ts_core *cd); + int (*gesture)(struct goodix_ts_core *cd, unsigned 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); /* clean sync flag */ + int (*get_capacitance_data)(struct goodix_ts_core *cd, struct ts_rawdata_info *info); + int (*display_mode)(struct goodix_ts_core *cd, int mode); +}; + +/* + * 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_mode_info { + int film_mode; + int leather_mode; + int stylus_mode; + int interpolation; + int sample; + int report_rate_mode; + int edge_mode[2]; + int liquid_detection; +#ifdef GOODIX_PALM_SENSOR_EN + int palm_detection; +#endif + int stowed; +}; + +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; + struct pinctrl *pinctrl; + struct pinctrl_state *pin_sta_active; + struct pinctrl_state *pin_sta_suspend; + struct pinctrl_state *stylus_clk_active; + struct pinctrl_state *stylus_clk_suspend; + + int power_on; + int irq; + size_t irq_trig_cnt; + int liquid_status; +#ifdef GOODIX_PALM_SENSOR_EN + atomic_t palm_status; + struct timer_list palm_release_timer; + unsigned int palm_release_delay_ms; +#endif + + atomic_t irq_enabled; + atomic_t suspended; + atomic_t post_suspended; + /* when this flag is true, driver should not clean the sync flag */ + bool tools_ctrl_sync; + + struct wakeup_source *gesture_wakelock; + struct notifier_block ts_notifier; + struct goodix_ts_esd ts_esd; +#ifdef CONFIG_GTP_ENABLE_PM_QOS + struct pm_qos_request goodix_pm_qos; +#endif + +#ifdef CONFIG_FB + struct notifier_block fb_notifier; +#endif + + int refresh_rate; + struct clk *stylus_clk; + struct goodix_mode_info set_mode; + struct goodix_mode_info get_mode; + struct mutex mode_lock; + + /* touchscreen_mmi */ + struct ts_mmi_class_methods *imports; + int (*set_fw_name)(char* fw_name); + int update_status; + int gesture_enabled; + const char *supplier; + bool need_update_cfg; +#ifdef CONFIG_GTP_FOD + unsigned char gesture_type; + int zerotap_data[1]; + int fod_enable; +#endif +#ifdef CONFIG_GTP_LAST_TIME + ktime_t last_event_time; +#endif + unsigned short gesture_cmd; + atomic_t pm_resume; + wait_queue_head_t pm_wq; + +#ifdef CONFIG_GTP_GHOST_LOG_CAPTURE + atomic_t trigger_enable; + u8 trigger_buf[2500]; + atomic_t allow_capture; + bool data_valid; + struct mutex frame_log_lock; +#endif + +#ifdef GTP_PEN_NOTIFIER + int gtp_pen_detect_flag; + struct notifier_block pen_notif; +#endif +}; + +/* 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 *, char *); + ssize_t (*store)(struct goodix_ext_module *, const char *, size_t); +}; + +/* 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_gesture_enable(int enable); + +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); +void goodix_ts_release_connects(struct goodix_ts_core *core_data); + +int goodix_ts_power_on(struct goodix_ts_core *cd); +int goodix_ts_power_off(struct goodix_ts_core *cd); + +#ifdef CONFIG_GTP_DDA_STYLUS +void goodix_stylus_dda_init(void); +void goodix_stylus_dda_exit(void); +int goodix_stylus_dda_register_cdevice(void); +void goodix_dda_process_pen_report(struct goodix_pen_data *pen_data); +#endif + +#ifdef CONFIG_GTP_GHOST_LOG_CAPTURE +int frame_log_capture_start(struct goodix_ts_core *cd); +int frame_log_capture_stop(struct goodix_ts_core *cd); +void put_fifo_with_discard(char *log_buf, int len); +void clear_kfifo(void); +int goodix_log_capture_register_misc(struct goodix_ts_core *cd); +int goodix_log_capture_unregister_misc(struct goodix_ts_core *cd); +#endif + +#endif diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_dda.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_dda.c new file mode 100644 index 000000000000..cb207addb908 --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_dda.c @@ -0,0 +1,289 @@ +/* + * Copyright (C) 2022 Motorola Mobility LLC + * + * 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. + */ +#include +#include +#include +#include +#include "goodix_ts_core.h" + +//cdev registration +#define DRIVER_NAME "goodix_penraw_driver" +#define DEVICE_NAME "goodix_penraw" +#define MINOR_NUMBER_START ((const unsigned int)0) /* the minor number starts at */ +#define NUMBER_MINOR_NUMBER ((const unsigned int)1) /* the number of minor numbers */ +#define DDA_MAX_BUFFER 32 +#define MAX_IO_CONTROL_REPORT 16 + +/* for DDA goodix_penraw */ +enum{ + DATA_TYPE_RAW = 0 +}; + +struct dda_pen_coords { + signed char status; + signed char tool_type; + signed char tilt_x; + signed char tilt_y; + unsigned long int x; + unsigned long int y; + unsigned long int p; +}; + +struct dda_pen_info { + unsigned char frame_no; + unsigned char data_type; + unsigned char reserve[2]; + struct dda_pen_coords coords; +}; + +struct dda_io_pen_reports { + unsigned char report_num; + unsigned char reserve[3]; + struct dda_pen_info pen_info[MAX_IO_CONTROL_REPORT]; +}; + +struct dda_pen_report_buffer{ + unsigned char pen_report_num; + unsigned char frame_no; + unsigned char buffer_wp; + unsigned char reserve[1]; + struct dda_pen_info pen_report_buffer[DDA_MAX_BUFFER]; +}; + +struct dda_cdev_control{ + unsigned int major_number; /* the major number of the device */ + struct cdev penraw_char_dev; /* character device */ + struct class* penraw_char_dev_class; /* class object */ +}; + +//DDA cdev +static struct dda_cdev_control dda_cdev_ctrl; + +//DDA raw pen report buffer +static struct dda_pen_report_buffer dda_pen_report_buf; + +spinlock_t lock; +int open_count = 0; + +// Linux 2.0/2.2 +static int penraw_open(struct inode * inode, struct file * file) +{ + spin_lock(&lock); + if (open_count) { + spin_unlock(&lock); + return -EBUSY; + } + open_count++; + spin_unlock(&lock); + + return 0; +} + +// Linux 2.1: int type +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 1, 0) +static int penraw_close(struct inode * inode, struct file * file) +{ + spin_lock(&lock); + open_count--; + spin_unlock(&lock); + + return 0; +} +#else +static void penraw_close(struct inode * inode, struct file * file) +{ + spinlock(&lock); + open_count--; + spin_unlock(&lock); +} +#endif + +#define PENRAW_IOC_TYPE 'P' +#define PENRAW_GET_VALUES _IOR(PENRAW_IOC_TYPE, 0, struct dda_io_pen_reports) + +static struct dda_io_pen_reports pen_reports; // return report +static long penraw_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned long flags; + struct dda_pen_info *ppen_info; + unsigned char cnt; + unsigned char pen_buffer_rp; + unsigned char wp; + unsigned char num; + + switch (cmd) { + case PENRAW_GET_VALUES: + local_irq_save(flags); + wp = dda_pen_report_buf.buffer_wp; + num = dda_pen_report_buf.pen_report_num; + local_irq_restore(flags); + if(MAX_IO_CONTROL_REPORT <= num) { + pen_buffer_rp = (unsigned char)((wp + (DDA_MAX_BUFFER - + MAX_IO_CONTROL_REPORT)) % DDA_MAX_BUFFER); + } else { + pen_buffer_rp = 0; + } + memset(&pen_reports, 0, sizeof(pen_reports)); + pen_reports.report_num = num; + ppen_info = (struct dda_pen_info *)&pen_reports.pen_info[0]; + for(cnt = 0; cnt < num; cnt++) { + memcpy(ppen_info, &dda_pen_report_buf.pen_report_buffer[pen_buffer_rp], + sizeof(struct dda_pen_info)); + ppen_info++; + pen_buffer_rp++; + if(DDA_MAX_BUFFER == pen_buffer_rp) { + pen_buffer_rp = 0; + } + } + if (copy_to_user((void __user *)arg, &pen_reports, sizeof(pen_reports))) { + return -EFAULT; + } + break; + default: + ts_info("unsupported command %d", cmd); + return -EFAULT; + } + return 0; +} + +static struct file_operations penraw_fops = { + .owner = THIS_MODULE, + .open = penraw_open, + .release = penraw_close, + .unlocked_ioctl = penraw_ioctl, +}; + +/* functions for DDA Features */ +void goodix_dda_process_pen_report(struct goodix_pen_data *pen_data) { + struct dda_pen_info *ppen_info; + + if (pen_data->coords.status) { + // ioctl-DAA Buffering pen raw data + ppen_info = &dda_pen_report_buf.pen_report_buffer[0]; + ppen_info += dda_pen_report_buf.buffer_wp; + memset(ppen_info, 0, sizeof(struct dda_pen_info)); + ppen_info->coords.status = (signed char)pen_data->coords.status; + ppen_info->coords.tool_type = (signed char)pen_data->coords.tool_type; + ppen_info->coords.tilt_x = pen_data->coords.tilt_x; + ppen_info->coords.tilt_y = pen_data->coords.tilt_y; + ppen_info->coords.x = pen_data->coords.x; + ppen_info->coords.y = pen_data->coords.y; + ppen_info->coords.p = pen_data->coords.p; + ppen_info->frame_no = dda_pen_report_buf.frame_no; + ppen_info->data_type = DATA_TYPE_RAW; + dda_pen_report_buf.frame_no++; + if(MAX_IO_CONTROL_REPORT > dda_pen_report_buf.pen_report_num) { + // Max count: MAX_IO_CONTROL_REPORT + dda_pen_report_buf.pen_report_num++; + } + dda_pen_report_buf.buffer_wp++; + if(DDA_MAX_BUFFER == dda_pen_report_buf.buffer_wp) { + dda_pen_report_buf.buffer_wp = 0; + } + } +} + +void goodix_stylus_dda_init(void) { + dda_pen_report_buf.pen_report_num = 0; + dda_pen_report_buf.frame_no = 0; + dda_pen_report_buf.buffer_wp = 0; + memset(&dda_cdev_ctrl.penraw_char_dev, 0, sizeof(dda_cdev_ctrl.penraw_char_dev)); + dda_cdev_ctrl.penraw_char_dev_class = NULL; + spin_lock_init(&lock); +} + +int goodix_stylus_dda_register_cdevice(void) { + int ret; + int alloc_ret; + int cdev_err; + dev_t dev; + struct device *penraw_dev; + + ts_info("goodix stylus dda register cdevice start"); + /* get not assigned major numbers */ + alloc_ret = alloc_chrdev_region(&dev, MINOR_NUMBER_START, NUMBER_MINOR_NUMBER, + DRIVER_NAME); + if (alloc_ret != 0) { + ts_err("failed to alloc_chrdev_region()"); + unregister_chrdev_region(dev, NUMBER_MINOR_NUMBER); + return alloc_ret; + } + + /* get one number from the not-assigend numbers */ + dda_cdev_ctrl.major_number = MAJOR(dev); + + /* initialize cdev and function table */ + cdev_init(&dda_cdev_ctrl.penraw_char_dev, &penraw_fops); + dda_cdev_ctrl.penraw_char_dev.owner = THIS_MODULE; + + /* register the driver */ + cdev_err = cdev_add(&dda_cdev_ctrl.penraw_char_dev, dev, NUMBER_MINOR_NUMBER); + if (cdev_err != 0) { + ts_err(KERN_ERR "failed to cdev_add()"); + unregister_chrdev_region(dev, NUMBER_MINOR_NUMBER); + return cdev_err; + } + + /* register a class */ + dda_cdev_ctrl.penraw_char_dev_class = class_create(THIS_MODULE, DEVICE_NAME); + if (IS_ERR(dda_cdev_ctrl.penraw_char_dev_class)) { + ts_err("class_create()"); + cdev_del(&dda_cdev_ctrl.penraw_char_dev); + unregister_chrdev_region(dev, NUMBER_MINOR_NUMBER); + ret = PTR_ERR(dda_cdev_ctrl.penraw_char_dev_class); + dda_cdev_ctrl.penraw_char_dev_class = NULL; + //return error code + return ret; + } + + /* create devive /dev/goodix_penraw*/ + penraw_dev = device_create(dda_cdev_ctrl.penraw_char_dev_class, NULL, + MKDEV(dda_cdev_ctrl.major_number, 0), NULL, DEVICE_NAME); + if (IS_ERR(penraw_dev)) { + ts_err("device_create()"); + class_destroy(dda_cdev_ctrl.penraw_char_dev_class); + dda_cdev_ctrl.penraw_char_dev_class = NULL; + cdev_del(&dda_cdev_ctrl.penraw_char_dev); + unregister_chrdev_region(dev, NUMBER_MINOR_NUMBER); + ret = PTR_ERR(penraw_dev); + penraw_dev = NULL; + return ret; + } + + ts_info("goodix stylus dda register cdevice success"); + return 0; +} + +void goodix_stylus_dda_exit(void) { + dev_t dev = MKDEV(dda_cdev_ctrl.major_number, MINOR_NUMBER_START); + + /* remove "/dev/goodix_penraw" */ + if (NULL != dda_cdev_ctrl.penraw_char_dev_class) { + device_destroy(dda_cdev_ctrl.penraw_char_dev_class, + MKDEV(dda_cdev_ctrl.major_number, 0)); + } + + /* remove class */ + if (NULL != dda_cdev_ctrl.penraw_char_dev_class) { + class_destroy(dda_cdev_ctrl.penraw_char_dev_class); + } + + /* remove driver */ + cdev_del(&dda_cdev_ctrl.penraw_char_dev); + + /* release the major number */ + unregister_chrdev_region(dev, NUMBER_MINOR_NUMBER); + + ts_info("goodix_stylus_dda_exit success"); +} diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_gesture.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_gesture.c new file mode 100644 index 000000000000..0dc8d74dba87 --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_gesture.c @@ -0,0 +1,445 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "goodix_ts_core.h" +#include "goodix_ts_mmi.h" + +#define QUERYBIT(longlong, bit) (!!(longlong[bit/8] & (1 << bit%8))) + +#define GSX_GESTURE_TYPE_LEN 32 + +/* + * 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; + rwlock_t rwlock; + u8 gesture_type[GSX_GESTURE_TYPE_LEN]; + u8 gesture_data; + struct goodix_ext_module module; +}; + +static struct gesture_module *gsx_gesture; /*allocated in gesture init module*/ +static bool module_initialized; + + +int goodix_gesture_enable(int enable) +{ + int ret = 0; + + if (!module_initialized) + return 0; + + if (enable) { + if (atomic_read(&gsx_gesture->registered)) + ts_info("gesture module has been already registered"); + else + ret = goodix_register_ext_module_no_wait(&gsx_gesture->module); + } else { + if (!atomic_read(&gsx_gesture->registered)) + ts_info("gesture module has been already unregistered"); + else + ret = goodix_unregister_ext_module(&gsx_gesture->module); + } + + return ret; +} + +/** + * gsx_gesture_type_show - show valid gesture type + * + * @module: pointer to goodix_ext_module struct + * @buf: pointer to output buffer + * Returns >=0 - succeed,< 0 - failed + */ +static ssize_t gsx_gesture_type_show(struct goodix_ext_module *module, + char *buf) +{ + int count = 0, i, ret = 0; + unsigned char *type; + + type = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!type) + return -ENOMEM; + read_lock(&gsx_gesture->rwlock); + for (i = 0; i < 256; i++) { + if (QUERYBIT(gsx_gesture->gesture_type, i)) { + count += scnprintf(type + count, + PAGE_SIZE, "%02x,", i); + } + } + if (count > 0) + ret = scnprintf(buf, PAGE_SIZE, "%s\n", type); + read_unlock(&gsx_gesture->rwlock); + + kfree(type); + return ret; +} + +/** + * gsx_gesture_type_store - set vailed gesture + * + * @module: pointer to goodix_ext_module struct + * @buf: pointer to valid gesture type + * @count: length of buf + * Returns >0 - valid gestures, < 0 - failed + */ +static ssize_t gsx_gesture_type_store(struct goodix_ext_module *module, + const char *buf, size_t count) +{ + int i; + + if (count <= 0 || count > 256 || buf == NULL) { + ts_err("Parameter error"); + return -EINVAL; + } + + write_lock(&gsx_gesture->rwlock); + memset(gsx_gesture->gesture_type, 0, GSX_GESTURE_TYPE_LEN); + for (i = 0; i < count; i++) + gsx_gesture->gesture_type[buf[i]/8] |= (0x1 << buf[i]%8); + write_unlock(&gsx_gesture->rwlock); + + return count; +} + +static ssize_t gsx_gesture_enable_show(struct goodix_ext_module *module, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", + atomic_read(&gsx_gesture->registered)); +} + +static ssize_t gsx_gesture_enable_store(struct goodix_ext_module *module, + const char *buf, size_t count) +{ + bool val; + int ret; + + ret = strtobool(buf, &val); + if (ret < 0) + return ret; + + if (val) { + ret = goodix_gesture_enable(1); + return ret ? ret : count; + } else { + ret = goodix_gesture_enable(0); + return ret ? ret : count; + } +} + +static ssize_t gsx_gesture_data_show(struct goodix_ext_module *module, + char *buf) +{ + ssize_t count; + + read_lock(&gsx_gesture->rwlock); + count = scnprintf(buf, PAGE_SIZE, "gesture type code:0x%x\n", + gsx_gesture->gesture_data); + read_unlock(&gsx_gesture->rwlock); + + return count; +} + +const struct goodix_ext_attribute gesture_attrs[] = { + __EXTMOD_ATTR(type, 0666, gsx_gesture_type_show, + gsx_gesture_type_store), + __EXTMOD_ATTR(enable, 0666, gsx_gesture_enable_show, + gsx_gesture_enable_store), + __EXTMOD_ATTR(data, 0444, gsx_gesture_data_show, NULL) +}; + +static int gsx_gesture_init(struct goodix_ts_core *cd, + struct goodix_ext_module *module) +{ + if (!cd || !cd->hw_ops->gesture) { + ts_err("gesture unsupported"); + return -EINVAL; + } + + ts_info("gesture switch: ON"); + ts_debug("enable all gesture type"); + /* set all bit to 1 to enable all gesture wakeup */ + memset(gsx_gesture->gesture_type, 0xff, GSX_GESTURE_TYPE_LEN); + atomic_set(&gsx_gesture->registered, 1); + + return 0; +} + +static int gsx_gesture_exit(struct goodix_ts_core *cd, + struct goodix_ext_module *module) +{ + if (!cd || !cd->hw_ops->gesture) { + ts_err("gesture unsupported"); + return -EINVAL; + } + + ts_info("gesture switch: OFF"); + ts_debug("disable all gesture type"); + memset(gsx_gesture->gesture_type, 0x00, GSX_GESTURE_TYPE_LEN); + atomic_set(&gsx_gesture->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 ret; +#ifdef GOODIX_PALM_SENSOR_EN + int palm_flag = 0; +#endif + unsigned int gesture_cmd = 0; + + if (atomic_read(&cd->suspended) == 0) + return EVT_CONTINUE; + + if (cd->board_data.gesture_wait_pm) { + PM_WAKEUP_EVENT(cd->gesture_wakelock, 3000); + /* Waiting for pm resume completed */ + ret = wait_event_interruptible_timeout(cd->pm_wq, atomic_read(&cd->pm_resume), msecs_to_jiffies(700)); + if (!ret) { + ts_err("system(spi) can't finished resuming procedure."); + return IRQ_HANDLED; + } + } + + 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", + gs_event.event_type); + goto re_send_ges_cmd; + } + + if (QUERYBIT(gsx_gesture->gesture_type, + gs_event.gesture_type)) { + gsx_gesture->gesture_data = gs_event.gesture_type; +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +#ifdef GOODIX_PALM_SENSOR_EN + if (cd->set_mode.palm_detection) { + palm_flag = gs_event.gesture_report_info & GOODIX_GESTURE_PALM_DETECTION; + if (palm_flag) { + mod_timer(&cd->palm_release_timer, + jiffies + msecs_to_jiffies(cd->palm_release_delay_ms)); + } + if (atomic_read(&cd->palm_status) != palm_flag) { + atomic_set(&cd->palm_status, palm_flag); + /* call class method */ + if (cd->imports && cd->imports->report_palm) + cd->imports->report_palm(palm_flag); + ts_info("palm detection flag changed to: 0x%x\n", palm_flag); + } + goto gesture_ist_exit; + } +#endif + + if (cd->imports && cd->imports->report_gesture) { + struct gesture_event_data mmi_event; + + ts_info("invoke imported report gesture function, gesture_type = %d\n", + gs_event.gesture_type); + if(gs_event.gesture_type == GOODIX_GESTURE_SINGLE_TAP) { + mmi_event.evcode = 1; + } else if(gs_event.gesture_type == GOODIX_GESTURE_DOUBLE_TAP) { + mmi_event.evcode =4; + } + + /* call class method */ + ret = cd->imports->report_gesture(&mmi_event); + if (!ret) + PM_WAKEUP_EVENT(cd->gesture_wakelock, 3000); + goto gesture_ist_exit; + } +#else + /* do resume routine */ + ts_info("got valid gesture type 0x%x", + gs_event.gesture_type); + input_report_key(cd->input_dev, KEY_POWER, 1); + input_sync(cd->input_dev); + input_report_key(cd->input_dev, KEY_POWER, 0); + input_sync(cd->input_dev); + goto gesture_ist_exit; +#endif + } else { + ts_info("unsupported gesture:%x", gs_event.gesture_type); + } + +re_send_ges_cmd: +#if defined(PRODUCT_MIAMI) + gesture_cmd = 0x80; +#elif defined(CONFIG_BOARD_USES_DOUBLE_TAP_CTRL) + gesture_cmd = cd->gesture_cmd; +#endif + if (hw_ops->gesture(cd, gesture_cmd)) + ts_info("warning: failed re_send gesture cmd"); +gesture_ist_exit: + if (!cd->tools_ctrl_sync) + hw_ops->after_event_handler(cd); + 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; + + ret = hw_ops->gesture(cd, 0); + if (ret) + ts_err("failed enter gesture mode"); + else + ts_info("enter gesture mode"); + + 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; + + hw_ops->irq_enable(cd, false); + 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); + rwlock_init(&gsx_gesture->rwlock); + + /* 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_gesture_enable(1); + + 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_gesture_enable(0); + + /* 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; +} diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_gesture_fod.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_gesture_fod.c new file mode 100644 index 000000000000..00c97dd9c4f2 --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_gesture_fod.c @@ -0,0 +1,676 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "goodix_ts_core.h" +#include "goodix_ts_mmi.h" + +/* + * 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; + rwlock_t rwlock; + struct goodix_ts_core *ts_core; + u8 gesture_type; + struct goodix_ext_module module; +}; + +static struct gesture_module *gsx_gesture; /*allocated in gesture init module*/ +static bool module_initialized; +int goodix_gesture_enable(int enable) +{ + int ret = 0; + + if (!module_initialized) + return 0; + + if (enable) { + if (atomic_read(&gsx_gesture->registered)) + ts_info("gesture module has been already registered"); + else + ret = goodix_register_ext_module_no_wait(&gsx_gesture->module); + atomic_set(&gsx_gesture->registered, 1); + } else { + if (!atomic_read(&gsx_gesture->registered)) + ts_info("gesture module has been already unregistered"); + else + ret = goodix_unregister_ext_module(&gsx_gesture->module); + atomic_set(&gsx_gesture->registered, 0); + } + + return ret; +} +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 sprintf(buf, "%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 sprintf(buf, "%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 sprintf(buf, "%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; +} +#if 0 +/** + * gsx_gesture_type_show - show valid gesture type + * + * @module: pointer to goodix_ext_module struct + * @buf: pointer to output buffer + * Returns >=0 - succeed,< 0 - failed + */ +static ssize_t gsx_gesture_type_show(struct goodix_ext_module *module, + char *buf) +{ + int count = 0, i, ret = 0; + unsigned char *type; + + type = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!type) + return -ENOMEM; + read_lock(&gsx_gesture->rwlock); + for (i = 0; i < 256; i++) { + if (QUERYBIT(gsx_gesture->gesture_type, i)) { + count += scnprintf(type + count, + PAGE_SIZE, "%02x,", i); + } + } + if (count > 0) + ret = scnprintf(buf, PAGE_SIZE, "%s\n", type); + read_unlock(&gsx_gesture->rwlock); + + kfree(type); + return ret; +} + +/** + * gsx_gesture_type_store - set vailed gesture + * + * @module: pointer to goodix_ext_module struct + * @buf: pointer to valid gesture type + * @count: length of buf + * Returns >0 - valid gestures, < 0 - failed + */ +static ssize_t gsx_gesture_type_store(struct goodix_ext_module *module, + const char *buf, size_t count) +{ + int i; + + if (count <= 0 || count > 256 || buf == NULL) { + ts_err("Parameter error"); + return -EINVAL; + } + + write_lock(&gsx_gesture->rwlock); + memset(gsx_gesture->gesture_type, 0, GSX_GESTURE_TYPE_LEN); + for (i = 0; i < count; i++) + gsx_gesture->gesture_type[buf[i]/8] |= (0x1 << buf[i]%8); + write_unlock(&gsx_gesture->rwlock); + + return count; +} +#endif +static ssize_t gsx_gesture_enable_show(struct goodix_ext_module *module, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", + atomic_read(&gsx_gesture->registered)); +} + +static ssize_t gsx_gesture_enable_store(struct goodix_ext_module *module, + const char *buf, size_t count) +{ + bool val; + int ret; + + ret = strtobool(buf, &val); + if (ret < 0) + return ret; + + if (val) { + ret = goodix_gesture_enable(1); + return ret ? ret : count; + } else { + ret = goodix_gesture_enable(0); + return ret ? ret : count; + } +} + +static ssize_t gsx_gesture_data_show(struct goodix_ext_module *module, + char *buf) +{ + ssize_t count; + + read_lock(&gsx_gesture->rwlock); + count = scnprintf(buf, PAGE_SIZE, "gesture type code:0x%x\n", + gsx_gesture->gesture_type); + read_unlock(&gsx_gesture->rwlock); + + return count; +} + +const struct goodix_ext_attribute gesture_attrs[] = { +#if 0 + __EXTMOD_ATTR(type, 0666, gsx_gesture_type_show, + gsx_gesture_type_store), +#endif + __EXTMOD_ATTR(enable, 0666, gsx_gesture_enable_show, + gsx_gesture_enable_store), + __EXTMOD_ATTR(data, 0444, gsx_gesture_data_show, NULL), + __EXTMOD_ATTR(double_en, 0666, + gsx_double_type_show, gsx_double_type_store), + __EXTMOD_ATTR(single_en, 0666, + gsx_single_type_show, gsx_single_type_store), + __EXTMOD_ATTR(fod_en, 0666, + 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; + } + + ts_info("gesture switch: ON"); + ts_debug("enable all gesture type"); + + gsx->ts_core = cd; + /*enable all gesture wakeup by default */ + gsx->ts_core->gesture_type = GESTURE_SINGLE_TAP |GESTURE_FOD_PRESS | GESTURE_DOUBLE_TAP; + cd->zerotap_data[0] = 0; + //default on the fod event + cd->fod_enable = true; + atomic_set(&gsx_gesture->registered, 1); + return 0; +} + +static int gsx_gesture_exit(struct goodix_ts_core *cd, + struct goodix_ext_module *module) +{ + if (!cd || !cd->hw_ops->gesture) { + ts_err("gesture unsupported"); + return -EINVAL; + } + + ts_info("gesture switch: OFF"); + ts_debug("disable all gesture type"); + cd->gesture_type = 0; + atomic_set(&gsx_gesture->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}; + __maybe_unused int fodx, fody, overlay_area; + int ret; + unsigned int gesture_cmd = 0; +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + struct gesture_event_data mmi_event; + static unsigned long start = 0; + int fod_down_interval = 0; + int fod_down = cd->zerotap_data[0]; + int underwater_flag = 0; +#endif + if (atomic_read(&cd->suspended) == 0 || cd->gesture_type == 0) + return EVT_CONTINUE; + + if (cd->board_data.gesture_wait_pm) { + PM_WAKEUP_EVENT(cd->gesture_wakelock, 3000); + /* Waiting for pm resume completed */ + ret = wait_event_interruptible_timeout(cd->pm_wq, atomic_read(&cd->pm_resume), msecs_to_jiffies(700)); + if (!ret) { + ts_err("system(spi) can't finished resuming procedure."); + return IRQ_HANDLED; + } + } + + 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", + gs_event.event_type); + goto re_send_ges_cmd; + } + ts_debug("got gesture type 0x%x", gs_event.gesture_type); +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + if (cd->set_mode.liquid_detection) { + underwater_flag = gs_event.gesture_report_info & GOODIX_GESTURE_UNDER_WATER; + if ( cd->liquid_status != underwater_flag) { + cd->liquid_status = underwater_flag; + /* call class method */ + cd->imports->report_liquid_detection_status(cd->bus->dev, cd->liquid_status? 1:0); + ts_info("under water flag changed to: 0x%x\n", cd->liquid_status); + goto gesture_ist_exit; + } + } + + mmi_event.evcode =0; + if ( cd->imports && cd->imports->report_gesture) { + if(cd->gesture_type & GESTURE_SINGLE_TAP && gs_event.gesture_type == GOODIX_GESTURE_SINGLE_TAP) { + ts_info("get SINGLE-TAP gesture"); + mmi_event.evcode =1; + fod_down = 0; + + /* call class method */ + ret = cd->imports->report_gesture(&mmi_event); + if (!ret) + PM_WAKEUP_EVENT(cd->gesture_wakelock, 3000); + goto gesture_ist_exit; + } else if(cd->gesture_type & GESTURE_DOUBLE_TAP && gs_event.gesture_type == GOODIX_GESTURE_DOUBLE_TAP) { + ts_info("get DOUBLE-TAP gesture"); + mmi_event.evcode =4; + fod_down = 0; + + /* call class method */ + ret = cd->imports->report_gesture(&mmi_event); + if (!ret) + PM_WAKEUP_EVENT(cd->gesture_wakelock, 3000); + goto gesture_ist_exit; + } else if(cd->gesture_type & GESTURE_FOD_PRESS && gs_event.gesture_type == GOODIX_GESTURE_FOD_DOWN ){ + fod_down_interval = (int)jiffies_to_msecs(jiffies-start); + 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]; + //goodix firmware do not send coordinate, need mmi touch to define a vaild coordinate thru dts + mmi_event.evcode = 2; + mmi_event.evdata.x= 0; + mmi_event.evdata.y= 0; + + ts_info("Get FOD-DOWN gesture:%d interval:%d",fod_down,fod_down_interval); + if(fod_down_interval > 2000) + fod_down = 0; + if(fod_down_interval > 0 && fod_down_interval < 250 && fod_down) { + goto gesture_ist_exit; + } + start = jiffies; + //maximum allow send down event 7 times + if(fod_down < 6) { + ret = cd->imports->report_gesture(&mmi_event); + if (!ret) + PM_WAKEUP_EVENT(cd->gesture_wakelock, 3000); + } + fod_down++; + }else if(cd->gesture_type & GESTURE_FOD_PRESS && gs_event.gesture_type == GOODIX_GESTURE_FOD_UP) { + ts_info("Get FOD-UP gesture"); + mmi_event.evcode = 3; + mmi_event.evdata.x= 0; + mmi_event.evdata.y= 0; + ret = cd->imports->report_gesture(&mmi_event); + if (!ret) + PM_WAKEUP_EVENT(cd->gesture_wakelock, 500); + fod_down = 0; + } else { + ts_debug("not support gesture type[%02X] to wakeup, suspended =%d", gs_event.gesture_type, atomic_read(&cd->suspended)); + fod_down = 0; + } + } +#else + 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_POWER, 1); + input_sync(cd->input_dev); + input_report_key(cd->input_dev, KEY_POWER, 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_info("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; + } +#endif + +re_send_ges_cmd: +#if defined(PRODUCT_MIAMI) + gesture_cmd = 0x80; +#elif defined(CONFIG_BOARD_USES_DOUBLE_TAP_CTRL) + gesture_cmd = cd->gesture_cmd; +#endif + if (hw_ops->gesture(cd, gesture_cmd)) + ts_info("warning: failed re_send gesture cmd"); +gesture_ist_exit: + if (!cd->tools_ctrl_sync) + hw_ops->after_event_handler(cd); + cd->zerotap_data[0] = fod_down; + 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; + hw_ops->irq_enable(cd, false); + 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); + rwlock_init(&gsx_gesture->rwlock); + + /* 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_gesture_enable(1); + + 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_gesture_enable(0); + + /* 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; +} diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_inspect.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_inspect.c new file mode 100644 index 000000000000..fb494c93f06e --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_inspect.c @@ -0,0 +1,2965 @@ + /* + * 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 +#include +#include +#include +#include + + +/* test config */ +#define TOTAL_FRAME_NUM 1 /* rawdata test frames */ +#define NOISEDATA_TEST_TIMES 1 /* noise test frames */ + +#define GOODIX_RESULT_SAVE_PATH "/data/vendor/touchrec" +#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 == NULL){ + 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 || (0 == strlen(ptr)) || (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 || (0 == strlen(ptr))) { + 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 = ¶ms_bra; + else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B) + test_params->params_info = ¶ms_brb; + else + test_params->params_info = ¶ms_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; + + if (ts_core->supplier) + snprintf(limit_file, sizeof(limit_file), "%s_%s_test_limits_%d.csv", + ts_core->supplier, GOODIX_TEST_FILE_NAME, ts_core->fw_version.sensor_id); + else + snprintf(limit_file, sizeof(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->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); + if (firmware) + 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; + else + 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 % 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"); +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + return -EINVAL; +#endif + } + 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); + return strncat(&dest[dest_len], src, dest_size - dest_len - 1); +} + +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 GTP_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 += sprintf(&data[bytes], "\n"); + for (i = 0; i < cfg->len; i++) { + bytes += sprintf(&data[bytes], "0x%02x,", cfg->data[i]); + } + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\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 += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "
\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 += sprintf(&data[bytes], "NG\n"); + else + bytes += sprintf(&data[bytes], "OK\n"); + bytes += sprintf(&data[bytes], "GT%s\n", + ts->fw_version.patch_pid); + bytes += sprintf(&data[bytes], "%d\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 += sprintf(&data[bytes], "
\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("header write failed"); + goto save_end; + } + bytes = 0; + + /* item list */ + bytes += sprintf(&data[bytes], "\n"); + if (ts_test->test_result[GTP_CAP_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_CAP_TEST]) + bytes += sprintf(&data[bytes], + "\n"); + else + bytes += sprintf(&data[bytes], + "\n"); + } + + if (ts_test->test_result[GTP_DELTA_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_DELTA_TEST]) + bytes += sprintf(&data[bytes], + "\n"); + else + bytes += sprintf(&data[bytes], + "\n"); + } + + if (ts_test->test_result[GTP_NOISE_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_NOISE_TEST]) + bytes += sprintf(&data[bytes], + "\n"); + else + bytes += sprintf(&data[bytes], + "\n"); + } + + if (ts_test->test_result[GTP_SELFNOISE_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_SELFNOISE_TEST]) + bytes += sprintf(&data[bytes], + "\n"); + else + bytes += sprintf(&data[bytes], + "\n"); + } + + if (ts_test->test_result[GTP_SELFCAP_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_SELFCAP_TEST]) + bytes += sprintf(&data[bytes], + "\n"); + else + bytes += sprintf(&data[bytes], + "\n"); + } + + if (ts_test->test_result[GTP_SHORT_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_SHORT_TEST]) + bytes += sprintf(&data[bytes], + "\n"); + else + bytes += sprintf(&data[bytes], + "\n"); + } + + bytes += sprintf(&data[bytes], "\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) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "\n"); + + /* save short result */ + if (ts_test->test_result[GTP_SHORT_TEST]) { + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "%d\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 += sprintf(&data[bytes], "\n", r); + else if (chn2 == CHN_GND) + bytes += sprintf(&data[bytes], + "Chn2=\"GND\" ShortResistor= \"%dKom\"/>\n", r); + else if (chn2 & DRV_CHANNEL_FLAG) + bytes += sprintf(&data[bytes], + "Chn2=\"Tx%d\" ShortResistor= \"%dKom\"/>\n", + chn2 & 0x7f, r); + else + bytes += sprintf(&data[bytes], + "Chn2=\"Rx%d\" ShortResistor= \"%dKom\"/>\n", + chn2 & 0x7f, r); + } + bytes += sprintf(&data[bytes], "\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 += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "%d\n", + TOTAL_FRAME_NUM); + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx * rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->test_params.max_limits[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + /* BeyondRawdataUpperLimit */ + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx * rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->open_res.beyond_max_limit_cnt[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\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 += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx * rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->test_params.min_limits[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + /* BeyondRawdataLower limit */ + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx * rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->open_res.beyond_min_limit_cnt[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\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 += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx * rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->test_params.deviation_limits[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + /* BeyondAccordLimitCnt */ + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx * rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->open_res.beyond_accord_limit_cnt[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\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 += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "%d\n", + NOISEDATA_TEST_TIMES); + bytes += sprintf(&data[bytes], "%d\n", + ts_test->test_params.noise_threshold); + bytes += sprintf(&data[bytes], "\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 += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "1\n"); + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx + rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->test_params.self_max_limits[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + if ((tx + rx) % tx != 0) + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < tx + rx; i++) { + bytes += sprintf(&data[bytes], "%d,", + ts_test->test_params.self_min_limits[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + if ((tx + rx) % tx != 0) + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\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 += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "1\n"); + bytes += sprintf(&data[bytes], "%d\n", + ts_test->test_params.self_noise_threshold); + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("raw limit write failed"); + goto save_end; + } + bytes = 0; + } + + bytes += sprintf(&data[bytes], "\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) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < TOTAL_FRAME_NUM; i++) { + goodix_data_cal(ts_test->rawdata[i].data, len, stat_result); + bytes += sprintf(&data[bytes], + "\n", + i, len, stat_result[1], stat_result[2], stat_result[0]); + for (j = 0; j < len; j++) { + bytes += sprintf(&data[bytes], "%d,", ts_test->rawdata[i].data[j]); + if ((j + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + goodix_data_cal(ts_test->accord_arr[i].data, len, stat_result); + bytes += sprintf(&data[bytes], + "\n", + i, len, stat_result[1], stat_result[2], stat_result[0]); + for (j = 0; j < len; j++) { + bytes += sprintf(&data[bytes], "%d,", ts_test->accord_arr[i].data[j]); + if ((j + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("rawdata write fail."); + goto save_end; + } + bytes = 0; + } + + bytes += sprintf(&data[bytes], "\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) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "\n"); + for (i = 0; i < NOISEDATA_TEST_TIMES; i++) { + goodix_data_cal(ts_test->noisedata[i].data, len, stat_result); + bytes += sprintf(&data[bytes], + "\n", + i, len, stat_result[1], stat_result[2], stat_result[0]); + for (j = 0; j < len; j++) { + bytes += sprintf(&data[bytes], "%d,", ts_test->noisedata[i].data[j]); + if ((j + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + bytes += sprintf(&data[bytes], "\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) { + ts_err("noisedata write fail."); + goto save_end; + } + bytes = 0; + } + + bytes += sprintf(&data[bytes], "\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) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += sprintf(&data[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 += sprintf(&data[bytes], + "\n", + len, stat_result[1], stat_result[2], stat_result[0]); + for (i = 0; i < len; i++) { + bytes += sprintf(&data[bytes], "%d,", src_data[i]); + if ((i + 1) % tx == 0) + bytes += sprintf(&data[bytes], "\n"); + } + if (len % tx != 0) + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\n"); + bytes += sprintf(&data[bytes], "\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) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "\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 += sprintf(&data[bytes], "\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) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += sprintf(&data[bytes], "
\n"); + ret = fs_write(data, bytes, fp); + if (ret < 0) + ts_err("tail write failed"); + + kfree(data); + return ret; +} + + +static char *get_date_time_str(void) +{ + struct timespec64 now_time; + struct rtc_time rtc_now_time; + static char time_data_buf[128] = { 0 }; + + ktime_get_real_ts64(&now_time); + rtc_time64_to_tm(now_time.tv_sec, &rtc_now_time); + snprintf(time_data_buf, sizeof(time_data_buf), "%04d%02d%02d-%02d%02d%02d", + (rtc_now_time.tm_year + 1900), rtc_now_time.tm_mon + 1, + rtc_now_time.tm_mday, rtc_now_time.tm_hour, rtc_now_time.tm_min, + rtc_now_time.tm_sec); + + return time_data_buf; +} + +static void goodix_save_result_data(struct goodix_ts_test *ts_test) +{ + int ret = 0; + char save_path[100]; + struct file *fp = NULL; + struct goodix_ts_core *ts_core = ts_test->ts; + + /* format result file */ + if (ts_core->supplier) { + if (strstr(ts_test->test_info, "PASS")) + snprintf(save_path, sizeof(save_path), "%s/%s-%s-%s-pass.csv", + GOODIX_RESULT_SAVE_PATH, ts_core->supplier, + GOODIX_TEST_FILE_NAME, get_date_time_str()); + else + snprintf(save_path, sizeof(save_path), "%s/%s-%s-%s-fail.csv", + GOODIX_RESULT_SAVE_PATH, ts_core->supplier, + GOODIX_TEST_FILE_NAME, get_date_time_str()); + + } else + snprintf(save_path, sizeof(save_path), "%s/%s_mp_test.csv", + GOODIX_RESULT_SAVE_PATH, GOODIX_TEST_FILE_NAME); + 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 // GTP_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 (GTP_TEST_PASS == ts_test->test_result[i]) + 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) { + 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) { + 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); + strncpy(info->result, ts_test->test_info, TS_RAWDATA_RESULT_MAX - 1); + +#ifdef GTP_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) { + ts_err("Failed to alloc mem"); + return -ENOMEM; + } + + ts_test->ts = cd; + ret = goodix_tptest_prepare(ts_test); + if (ret < 0) { + ts_err("Failed to prepare TP test, exit"); + strncpy(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 goodix_ts_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) { + ts_err("Failed to alloc rawdata info memory"); + 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, S_IRUGO, goodix_ts_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; +} diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_log_capture.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_log_capture.c new file mode 100644 index 000000000000..6c38e56e8519 --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_log_capture.c @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2023 Motorola Mobility LLC + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include "goodix_ts_core.h" + +#define LOG_FIFO_SIZE 8172 +#define GOODIX_LOG_DEVICE_NAME "tp_tools" + +struct goodix_ts_log { + struct kfifo fifo; + wait_queue_head_t wq; + struct miscdevice miscdev; +}; + +static struct goodix_ts_log ts_log_dev; + +/* Save a byte to a FIFO and discard the oldest byte if FIFO is full */ +void put_fifo_with_discard(char *log_buf, int len) +{ + if (!kfifo_initialized(&ts_log_dev.fifo)) + return; + if (kfifo_is_full(&ts_log_dev.fifo)) { + kfifo_skip(&ts_log_dev.fifo); + ts_info("Save a byte to a FIFO and discard the oldest byte if FIFO is full"); + } + + kfifo_in(&ts_log_dev.fifo, log_buf, len); + + wake_up_interruptible(&ts_log_dev.wq); +} + +void clear_kfifo(void) +{ + if (kfifo_len(&ts_log_dev.fifo) != 0) { + kfifo_reset(&ts_log_dev.fifo); + } +} + +static __poll_t log_file_poll(struct file *file, + struct poll_table_struct *pt) +{ + struct goodix_ts_log *log = file->private_data; + + poll_wait(file, &log->wq, pt); + return !kfifo_is_empty(&log->fifo) ? (POLLPRI|POLLIN) : 0; +} + +static ssize_t log_file_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct goodix_ts_log *log = file->private_data; + unsigned int copied; + int ret = 0; + + if (kfifo_is_empty(&log->fifo)) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + ret = wait_event_interruptible(log->wq, + !kfifo_is_empty(&log->fifo)); + if (ret == -ERESTARTSYS) + return -EINTR; + } + ret = kfifo_to_user(&log->fifo, buffer, count, &copied); + if (ret) + return ret; + + return copied; +} + +static int goodix_log_device_open(struct inode *inode, struct file *filp) +{ + filp->private_data = &ts_log_dev; + ts_info("success open log device"); + + return 0; +} + +static const struct file_operations log_device_fops = { + .owner = THIS_MODULE, + .open = goodix_log_device_open, + .read = log_file_read, + .poll = log_file_poll, + .llseek = noop_llseek, +}; + +int goodix_log_capture_register_misc(struct goodix_ts_core *cd) +{ + int rc = 0; + + init_waitqueue_head(&ts_log_dev.wq); + /* Create FIFO datastructure */ + rc = kfifo_alloc(&ts_log_dev.fifo, + LOG_FIFO_SIZE, GFP_KERNEL); + if (rc) + return rc; + + mutex_init(&cd->frame_log_lock); + + ts_log_dev.miscdev.minor = MISC_DYNAMIC_MINOR; + ts_log_dev.miscdev.name = GOODIX_LOG_DEVICE_NAME; + ts_log_dev.miscdev.fops = &log_device_fops; + rc = misc_register(&ts_log_dev.miscdev); + if (rc) + return rc; + return 0; +} + +int goodix_log_capture_unregister_misc(struct goodix_ts_core *cd) +{ + kfifo_free(&ts_log_dev.fifo); + misc_deregister(&ts_log_dev.miscdev); + mutex_destroy(&cd->frame_log_lock); + return 0; +} diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_mmi.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_mmi.c new file mode 100644 index 000000000000..da4dc410d474 --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_mmi.c @@ -0,0 +1,1606 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * 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. + */ + +#include "goodix_ts_mmi.h" +#include +#include +#include "goodix_ts_config.h" + +#define GET_GOODIX_DATA(dev) { \ + pdev = dev_get_drvdata(dev); \ + if (!pdev) { \ + ts_err("Failed to get platform device"); \ + return -ENODEV; \ + } \ + core_data = platform_get_drvdata(pdev); \ + if (!core_data) { \ + ts_err("Failed to get driver data"); \ + return -ENODEV; \ + } \ +} + +static ssize_t goodix_ts_edge_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t goodix_ts_edge_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t goodix_ts_interpolation_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t goodix_ts_interpolation_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t goodix_ts_sample_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t goodix_ts_sample_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t goodix_ts_stylus_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t goodix_ts_stylus_mode_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t goodix_ts_sensitivity_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +#ifdef CONFIG_GTP_LAST_TIME +static ssize_t goodix_ts_timestamp_show(struct device *dev, + struct device_attribute *attr, char *buf); +#endif +#ifdef CONFIG_GTP_GHOST_LOG_CAPTURE +static ssize_t goodix_ts_log_trigger_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); +static ssize_t goodix_ts_log_trigger_show(struct device *dev, + struct device_attribute *attr, char *buf); +#endif +static ssize_t goodix_ts_stowed_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t goodix_ts_stowed_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static DEVICE_ATTR(edge, (S_IRUGO | S_IWUSR | S_IWGRP), + goodix_ts_edge_show, goodix_ts_edge_store); +static DEVICE_ATTR(interpolation, (S_IRUGO | S_IWUSR | S_IWGRP), + goodix_ts_interpolation_show, goodix_ts_interpolation_store); +static DEVICE_ATTR(sample, (S_IRUGO | S_IWUSR | S_IWGRP), + goodix_ts_sample_show, goodix_ts_sample_store); +static DEVICE_ATTR(stylus_mode, (S_IRUGO | S_IWUSR | S_IWGRP), + goodix_ts_stylus_mode_show, goodix_ts_stylus_mode_store); +static DEVICE_ATTR(sensitivity, (S_IRUGO | S_IWUSR | S_IWGRP), + NULL, goodix_ts_sensitivity_store); +#ifdef CONFIG_GTP_LAST_TIME +static DEVICE_ATTR(timestamp, S_IRUGO, goodix_ts_timestamp_show, NULL); +#endif +#ifdef CONFIG_GTP_GHOST_LOG_CAPTURE +static DEVICE_ATTR(log_trigger, (S_IRUGO | S_IWUSR | S_IWGRP), + goodix_ts_log_trigger_show, goodix_ts_log_trigger_store); +#endif +static DEVICE_ATTR(stowed, (S_IWUSR | S_IWGRP | S_IRUGO), + goodix_ts_stowed_show, goodix_ts_stowed_store); + +/* hal settings */ +#define ROTATE_0 0 +#define ROTATE_90 1 +#define ROTATE_180 2 +#define ROTATE_270 3 +#define BIG_MODE 1 +#define SMALL_MODE 2 +#define DEFAULT_MODE 0 +#define MAX_ATTRS_ENTRIES 10 + +#define NORMAL_DEFAULT_MODE 10 +#define NORMAL_SMALL_MODE 11 +#define NORMAL_BIG_MODE 12 + +#define ADD_ATTR(name) { \ + if (idx < MAX_ATTRS_ENTRIES) { \ + dev_info(dev, "%s: [%d] adding %p\n", __func__, idx, &dev_attr_##name.attr); \ + ext_attributes[idx] = &dev_attr_##name.attr; \ + idx++; \ + } else { \ + dev_err(dev, "%s: cannot add attribute '%s'\n", __func__, #name); \ + } \ +} + +static struct attribute *ext_attributes[MAX_ATTRS_ENTRIES]; +static struct attribute_group ext_attr_group = { + .attrs = ext_attributes, +}; + +static int goodix_ts_mmi_extend_attribute_group(struct device *dev, struct attribute_group **group) +{ + int idx = 0; + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + GET_GOODIX_DATA(dev); + + if (core_data->board_data.edge_ctrl) + ADD_ATTR(edge); + + if (core_data->board_data.interpolation_ctrl) + ADD_ATTR(interpolation); + + if (core_data->board_data.sample_ctrl) + ADD_ATTR(sample); + + if (core_data->board_data.stylus_mode_ctrl) + ADD_ATTR(stylus_mode); + + if (core_data->board_data.sensitivity_ctrl) + ADD_ATTR(sensitivity); + + if (core_data->board_data.stowed_mode_ctrl) + ADD_ATTR(stowed); + +#ifdef CONFIG_GTP_LAST_TIME + ADD_ATTR(timestamp); +#endif + +#ifdef CONFIG_GTP_GHOST_LOG_CAPTURE + ADD_ATTR(log_trigger); +#endif + + if (idx) { + ext_attributes[idx] = NULL; + *group = &ext_attr_group; + } else + *group = NULL; + + return 0; +} + +int goodix_ts_send_cmd(struct goodix_ts_core *core_data, + u8 cmd, u8 len, u8 subCmd, u8 subCmd2) +{ + int ret = 0; + struct goodix_ts_cmd ts_cmd; + + ts_cmd.cmd = cmd; + ts_cmd.len = len; + ts_cmd.data[0] = subCmd; + ts_cmd.data[1] = subCmd2; + + ret = core_data->hw_ops->send_cmd(core_data, &ts_cmd); + return ret; +} + +static int goodix_ts_film_mode(struct goodix_ts_core *core_data, int mode) +{ + int ret = 0; + + mutex_lock(&core_data->mode_lock); + core_data->get_mode.film_mode = mode; + if (core_data->set_mode.film_mode == mode) { + ts_debug("The value = %d is same,so not write.\n", mode); + goto exit; + } + + if (core_data->power_on == 0) { + ts_debug("The touch is in sleep state, restore the value when resume\n"); + goto exit; + } + + ret = goodix_ts_send_cmd(core_data, FILM_MODE_SWITCH_CMD, 5, mode, 0x00); + if (ret < 0) { + ts_err("failed to send leather mode cmd"); + goto exit; + } + + core_data->set_mode.film_mode = mode; + msleep(20); + ts_info("Success to %s film mode", mode ? "Enable" : "Disable"); +exit: + mutex_unlock(&core_data->mode_lock); + return ret; +} + +static int goodix_ts_leather_mode(struct goodix_ts_core *core_data, int mode) +{ + int ret = 0; + + mutex_lock(&core_data->mode_lock); + core_data->get_mode.leather_mode = mode; + if (core_data->set_mode.leather_mode == mode) { + ts_debug("The value = %d is same,so not write.\n", mode); + goto exit; + } + + if (core_data->power_on == 0) { + ts_debug("The touch is in sleep state, restore the value when resume\n"); + goto exit; + } + + ret = goodix_ts_send_cmd(core_data, LEATHER_MODE_SWITCH_CMD, 5, mode, 0x00); + if (ret < 0) { + ts_err("failed to send leather mode cmd"); + goto exit; + } + core_data->set_mode.leather_mode = mode; + msleep(20); + ts_info("Success to %s leather mode", mode ? "Enable" : "Disable"); +exit: + mutex_unlock(&core_data->mode_lock); + return ret; +} +/* + * This is a common interface to the HAL layer. + * mode = 0x00/0x01 Exit/Enter film sensitivity mode. + * mode = 0x10/0x11 Exit/Enter leahter sensitivity mode. + */ +static ssize_t goodix_ts_sensitivity_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + unsigned long mode = 0; + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_GOODIX_DATA(dev); + + ret = kstrtoul(buf, 0, &mode); + if (ret < 0) { + pr_info("Failed to convert value.\n"); + return -EINVAL; + } + + switch (mode) { + case 0x00: + if (core_data->board_data.film_mode_ctrl) + ret = goodix_ts_film_mode(core_data, EXIT_FILM_MODE); + break; + case 0x01: + if (core_data->board_data.film_mode_ctrl) + ret = goodix_ts_film_mode(core_data, ENTER_FILM_MODE); + break; + case 0x10: + if (core_data->board_data.leather_mode_ctrl) + ret = goodix_ts_leather_mode(core_data, EXIT_LEATHER_MODE); + break; + case 0x11: + if (core_data->board_data.leather_mode_ctrl) + ret = goodix_ts_leather_mode(core_data, ENTER_LEATHER_MODE); + break; + default: + ts_err("The mode = %lu does not support\n", mode); + return size; + } + if (!ret) + return size; + else + return ret; +} + +static int goodix_clock_enable(struct goodix_ts_core *core_data, bool mode) { + int ret = 0; + struct goodix_ts_board_data *ts_bdata; + + ts_bdata = board_data(core_data); + if (!ts_bdata) { + ts_err("Failed to get ts board data"); + return -ENODEV; + } + + if (mode) { + if (!strcmp(ts_bdata->stylus_clk_src, STYLUS_CLK_SRC_PMIC)) { + if (IS_ERR_OR_NULL(core_data->stylus_clk)) { + ts_err("failed to get stylus clk\n"); + return -EINVAL; + } + ret = clk_prepare_enable(core_data->stylus_clk); + if (ret) { + ts_err("failed to enable stylus clk\n"); + return ret; + } + } else if (!strcmp(ts_bdata->stylus_clk_src, STYLUS_CLK_SRC_GPIO)){ + if (IS_ERR_OR_NULL(core_data->stylus_clk_active)) { + ts_err("Failed to get state clk pinctrl state:%s", + PINCTRL_STYLUS_CLK_ACTIVE); + core_data->stylus_clk_active = NULL; + return -EINVAL; + } + ret = pinctrl_select_state(core_data->pinctrl, + core_data->stylus_clk_active); + if (ret < 0) { + ts_err("Failed to select active stylus clk state, ret:%d", ret); + return ret; + } + } else { + ts_err("stylus clock source is invalid"); + return -EINVAL; + } + ts_info("success to enable stylus clk\n"); + } else { + if (!strcmp(ts_bdata->stylus_clk_src, STYLUS_CLK_SRC_PMIC)) { + if (IS_ERR_OR_NULL(core_data->stylus_clk)) { + ts_err("failed to get stylus clk\n"); + return -EINVAL; + } + clk_disable_unprepare(core_data->stylus_clk); + } else if (!strcmp(ts_bdata->stylus_clk_src, STYLUS_CLK_SRC_GPIO)){ + if (IS_ERR_OR_NULL(core_data->stylus_clk_suspend)) { + ts_err("Failed to get state clk pinctrl state:%s", + PINCTRL_STYLUS_CLK_SUSPEND); + core_data->stylus_clk_suspend = NULL; + return -EINVAL; + } + ret = pinctrl_select_state(core_data->pinctrl, + core_data->stylus_clk_suspend); + if (ret < 0) { + ts_err("Failed to select stylus clk suspend state, ret:%d", ret); + return ret; + } + } else { + ts_err("stylus clock source is invalid"); + return -EINVAL; + } + ts_info("success to disable stylus clk\n"); + } + return ret; +} + +static int goodix_stylus_mode(struct goodix_ts_core *core_data, int mode) { + int ret = 0; + + if (mode) { + goodix_clock_enable(core_data, mode); + msleep(50); + ret = goodix_ts_send_cmd(core_data, STYLUS_MODE_SWITCH_CMD, 5, mode, 0x00); + if (ret < 0) { + ts_err("Failed to Disable stylus mode\n"); + return ret; + } + msleep(20); + } else { + ret = goodix_ts_send_cmd(core_data, STYLUS_MODE_SWITCH_CMD, 5, mode, 0x00); + if (ret < 0) { + ts_err("Failed to Disable stylus mode\n"); + return ret; + } + msleep(50); + goodix_clock_enable(core_data, mode); + } + + ts_info("Success to %s stylus mode", mode ? "Enable" : "Disable"); + return ret; +} + +static ssize_t goodix_ts_stylus_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + unsigned long mode = 0; + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_GOODIX_DATA(dev); + + ret = kstrtoul(buf, 0, &mode); + if (ret < 0) { + pr_info("Failed to convert value.\n"); + return -EINVAL; + } + + mutex_lock(&core_data->mode_lock); + core_data->get_mode.stylus_mode = mode; + if (core_data->set_mode.stylus_mode == mode) { + ts_debug("The value = %lu is same,so not write.\n", mode); + ret = size; + goto exit; + } + + if (core_data->power_on == 0) { + ts_debug("The touch is in sleep state, restore the value when resume\n"); + ret = size; + goto exit; + } + + ret = goodix_stylus_mode(core_data, mode); + if (!ret) + core_data->set_mode.stylus_mode = mode; +exit: + mutex_unlock(&core_data->mode_lock); + return ret; +} + +static ssize_t goodix_ts_stylus_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_GOODIX_DATA(dev); + + ts_info("Stylus mode = %d.\n", core_data->set_mode.stylus_mode); + return scnprintf(buf, PAGE_SIZE, "0x%02x", core_data->set_mode.stylus_mode); +} + +static int goodix_ts_mmi_set_report_rate(struct goodix_ts_core *core_data) +{ + int ret = 0; + int mode = 0; + + mode = goodix_ts_mmi_get_report_rate(core_data); + if (mode == -1) { + return -EINVAL; + } + + core_data->get_mode.report_rate_mode = mode; + if (core_data->set_mode.report_rate_mode == mode) { + ts_debug("The value = %d is same, so not to write", mode); + return 0; + } + + if (core_data->power_on == 0) { + ts_debug("The touch is in sleep state, restore the value when resume\n"); + return 0; + } + + //if now on high report rate and need switch to low report rate + if ((((core_data->set_mode.report_rate_mode >> 8) & 0xFF) == REPORT_RATE_CMD_HIGH) && + (((mode >> 8) & 0xFF) == REPORT_RATE_CMD_LOW)) { + ts_info("exit high report rate"); + ret = goodix_ts_send_cmd(core_data, EXIT_HIGH_REPORT_RATE_CMD >> 8, 5, + EXIT_HIGH_REPORT_RATE_CMD & 0xFF, 0x00); + if (ret < 0) { + ts_err("failed to exit high report rate"); + return -EINVAL; + } + msleep(20); + } + + //send switch command + ret = goodix_ts_send_cmd(core_data, mode >> 8, 5, + mode & 0xFF, 0x00); + if (ret < 0) { + ts_err("failed to set report rate, mode = %d", mode); + return -EINVAL; + } + msleep(20); + + core_data->set_mode.report_rate_mode = mode; + + ts_info("Success to set %s\n", mode == REPORT_RATE_CMD_240HZ ? "REPORT_RATE_240HZ" : + (mode == REPORT_RATE_CMD_360HZ ? "REPORT_RATE_360HZ" : + (mode == REPORT_RATE_CMD_480HZ ? "REPORT_RATE_480HZ" : + (mode == REPORT_RATE_CMD_576HZ ? "REPORT_RATE_576HZ" : + (mode == REPORT_RATE_CMD_720HZ ? "REPORT_RATE_720HZ" : + "Unsupported"))))); + + return ret; +} + +static ssize_t goodix_ts_interpolation_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + unsigned long mode = 0; + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_GOODIX_DATA(dev); + + ret = kstrtoul(buf, 0, &mode); + if (ret < 0) { + pr_info("Failed to convert value.\n"); + return -EINVAL; + } + + mutex_lock(&core_data->mode_lock); + core_data->get_mode.interpolation = mode; + ret = goodix_ts_mmi_set_report_rate(core_data); + if (ret < 0) + goto exit; + + ret = size; + core_data->set_mode.interpolation = mode; +exit: + mutex_unlock(&core_data->mode_lock); + return ret; +} + +static ssize_t goodix_ts_interpolation_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_GOODIX_DATA(dev); + + ts_info("interpolation = %d.\n", core_data->set_mode.interpolation); + return scnprintf(buf, PAGE_SIZE, "0x%02x", core_data->set_mode.interpolation); +} + +static ssize_t goodix_ts_sample_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + unsigned long mode = 0; + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_GOODIX_DATA(dev); + + ret = kstrtoul(buf, 0, &mode); + if (ret < 0) { + pr_info("Failed to convert value.\n"); + return -EINVAL; + } + + mutex_lock(&core_data->mode_lock); + core_data->get_mode.sample= mode; + if (core_data->set_mode.sample == mode) { + ts_debug("The value = %lu is same, so not to write", mode); + ret = size; + goto exit; + } + + if (core_data->power_on == 0) { + ts_debug("The touch is in sleep state, restore the value when resume\n"); + ret = size; + goto exit; + } + + ret = goodix_ts_send_cmd(core_data, SAMPLE_SWITCH_CMD, 5, + core_data->get_mode.sample, 0x00); + if (ret < 0) { + ts_err("failed to set sample rate, mode = %lu", mode); + goto exit; + } + + core_data->set_mode.sample = mode; + msleep(20); + ts_info("Success to set %lu\n", mode); + + ret = size; +exit: + mutex_unlock(&core_data->mode_lock); + return ret; +} + +static ssize_t goodix_ts_sample_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_GOODIX_DATA(dev); + + ts_info("sample = %d.\n", core_data->set_mode.sample); + return scnprintf(buf, PAGE_SIZE, "0x%02x", core_data->set_mode.sample); +} + +static ssize_t goodix_ts_stowed_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + unsigned long mode = 0; + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_GOODIX_DATA(dev); + + ret = kstrtoul(buf, 0, &mode); + if (ret < 0) { + pr_info("Failed to convert value.\n"); + return -EINVAL; + } + + mutex_lock(&core_data->mode_lock); + core_data->get_mode.stowed = mode; + if (core_data->set_mode.stowed == mode) { + ts_debug("The value = %lu is same, so not to write", mode); + ret = size; + goto exit; + } + + if ((atomic_read(&core_data->post_suspended) == 1) && (core_data->power_on == 1)) { + ret = goodix_ts_send_cmd(core_data, ENTER_STOWED_MODE_CMD, 5, + core_data->get_mode.stowed, 0x00); + if (ret < 0) { + ts_err("Failed to set stowed mode %lu\n", mode); + goto exit; + } + } else { + ts_info("Skip stowed mode setting post_suspended:%d, power_on:%d.\n", + atomic_read(&core_data->post_suspended), core_data->power_on); + ret = size; + goto exit; + } + + core_data->set_mode.stowed = mode; + ts_info("Success to set stowed mode %lu\n", mode); + + ret = size; +exit: + mutex_unlock(&core_data->mode_lock); + return ret; +} + +static ssize_t goodix_ts_stowed_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_GOODIX_DATA(dev); + + ts_info("Stowed state = %d.\n", core_data->set_mode.stowed); + return scnprintf(buf, PAGE_SIZE, "0x%02x", core_data->set_mode.stowed); +} + +static int goodix_ts_mmi_refresh_rate(struct device *dev, int freq) +{ + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + GET_GOODIX_DATA(dev); + + mutex_lock(&core_data->mode_lock); + core_data->refresh_rate = freq; + + if (core_data->board_data.interpolation_ctrl) + goodix_ts_mmi_set_report_rate(core_data); + mutex_unlock(&core_data->mode_lock); + return 0; +} + +/* + * HAL: args[0] suppression area, args[1] rotation direction. + * CMD: [06 17 data0 data1], + * data[0] rotation direction, data[1] suppression area. + */ +static ssize_t goodix_ts_edge_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + int edge_cmd[2] = { 0 }; + unsigned int args[2] = { 0 }; + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_GOODIX_DATA(dev); + + ret = sscanf(buf, "%d %d", &args[0], &args[1]); + if (ret < 2) + return -EINVAL; + + switch (args[0]) { + case DEFAULT_MODE: + edge_cmd[1] = DEFAULT_EDGE; + break; + case SMALL_MODE: + edge_cmd[1] = SMALL_EDGE; + break; + case BIG_MODE: + edge_cmd[1] = BIG_EDGE; + break; + case NORMAL_DEFAULT_MODE: + edge_cmd[1] = NORMAL_DEFAULT_EDGE; + break; + case NORMAL_SMALL_MODE: + edge_cmd[1] = NORMAL_SMALL_EDGE; + break; + case NORMAL_BIG_MODE: + edge_cmd[1] = NORMAL_BIG_EDGE; + break; + default: + ts_err("Invalid edge mode: %d!\n", args[0]); + return -EINVAL; + } + + if (ROTATE_0 == args[1]) { + edge_cmd[0] = ROTATE_DEFAULT_0; + } else if (ROTATE_90 == args[1]) { + edge_cmd[0] = ROTATE_RIGHT_90; + } else if (ROTATE_270 == args[1]) { + edge_cmd[0] = ROTATE_LEFT_90; + } else { + ts_err("Invalid rotation mode: %d!\n", args[1]); + return -EINVAL; + } + + mutex_lock(&core_data->mode_lock); + memcpy(core_data->get_mode.edge_mode, edge_cmd, sizeof(edge_cmd)); + if (!memcmp(core_data->set_mode.edge_mode, edge_cmd, sizeof(edge_cmd))) { + ts_debug("The value (%02x %02x) is same,so not write.\n", + edge_cmd[0], edge_cmd[1]); + ret = size; + goto exit; + } + + if (core_data->power_on == 0) { + ts_debug("The touch is in sleep state, restore the value when resume\n"); + ret = size; + goto exit; + } + + ret = goodix_ts_send_cmd(core_data, EDGE_SWITCH_CMD, 6, edge_cmd[0], edge_cmd[1]); + if (ret < 0) { + ts_err("failed to send edge switch cmd"); + goto exit; + } + + memcpy(core_data->set_mode.edge_mode, edge_cmd, sizeof(edge_cmd)); + msleep(20); + ret = size; + ts_info("Success to set edge = %02x, rotation = %02x", edge_cmd[1], edge_cmd[0]); +exit: + mutex_unlock(&core_data->mode_lock); + return ret; +} + +static ssize_t goodix_ts_edge_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_GOODIX_DATA(dev); + + ts_info("edge area = %02x, rotation = %02x\n", + core_data->set_mode.edge_mode[1], core_data->set_mode.edge_mode[0]); + return scnprintf(buf, PAGE_SIZE, "0x%02x 0x%02x", + core_data->set_mode.edge_mode[1], core_data->set_mode.edge_mode[0]); +} + +#ifdef CONFIG_GTP_LAST_TIME +static ssize_t goodix_ts_timestamp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev; + struct goodix_ts_core *core_data; + ktime_t last_ktime; + struct timespec64 last_ts; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_GOODIX_DATA(dev); + + mutex_lock(&core_data->mode_lock); + last_ktime = core_data->last_event_time; + core_data->last_event_time = 0; + mutex_unlock(&core_data->mode_lock); + + last_ts = ktime_to_timespec64(last_ktime); + + return scnprintf(buf, PAGE_SIZE, "%lld.%ld\n", last_ts.tv_sec, last_ts.tv_nsec); +} +#endif + +#ifdef CONFIG_GTP_GHOST_LOG_CAPTURE +static ssize_t goodix_ts_log_trigger_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_GOODIX_DATA(dev); + + if (!buf || count <= 0) + return 0; + + clear_kfifo(); + frame_log_capture_start(core_data); + + return count; +} + +static ssize_t goodix_ts_log_trigger_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_GOODIX_DATA(dev); + + return scnprintf(buf, PAGE_SIZE, "0x%02x", 0x01); +} +#endif + +static int goodix_ts_mmi_methods_get_vendor(struct device *dev, void *cdata) { + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", "goodix"); +} + +static int goodix_ts_mmi_methods_get_productinfo(struct device *dev, void *cdata) { + struct platform_device *pdev; + struct goodix_ts_core *core_data; + struct goodix_ts_board_data *ts_bdata; + char* ic_info; + + GET_GOODIX_DATA(dev); + + ts_bdata = board_data(core_data); + if (!ts_bdata) { + ts_err("Failed to get ts board data"); + return -ENODEV; + } + + ic_info = strstr(ts_bdata->ic_name, ","); + ic_info++; + + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", ic_info); +} + +#define TOUCH_CFG_VERSION_ADDR 0x10076 +static int goodix_ts_mmi_methods_get_build_id(struct device *dev, void *cdata) { + int ret; + struct platform_device *pdev; + struct goodix_ts_core *core_data; + u32 cfg_id; + + GET_GOODIX_DATA(dev); + + ret = core_data->hw_ops->read(core_data, TOUCH_CFG_VERSION_ADDR, + (u8*)&cfg_id, sizeof(cfg_id)); + if (ret) { + ts_info("failed get fw version data, %d", ret); + return -EINVAL; + } + + return snprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%04x", le32_to_cpu(cfg_id)); +} + +#define TOUCH_FW_VERSION_ADDR 0x1007E +/*return firmware version*/ +static int goodix_ts_mmi_methods_get_config_id(struct device *dev, void *cdata) { + int ret; + struct platform_device *pdev; + struct goodix_ts_core *core_data; + u8 fw_id[4] = {0}; + + GET_GOODIX_DATA(dev); + + ret = core_data->hw_ops->read(core_data, TOUCH_FW_VERSION_ADDR, + fw_id, sizeof(fw_id)); + if (ret) { + ts_info("failed get fw version data, %d", ret); + return -EINVAL; + } + + return snprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%02x%02x%02x%02x", + fw_id[0], fw_id[1], fw_id[2], fw_id[3]); +} + +static int goodix_ts_mmi_methods_get_bus_type(struct device *dev, void *idata) { + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + GET_GOODIX_DATA(dev); + + TO_INT(idata) = core_data->bus->bus_type == GOODIX_BUS_TYPE_I2C ? + TOUCHSCREEN_MMI_BUS_TYPE_I2C : TOUCHSCREEN_MMI_BUS_TYPE_SPI; + return 0; +} + +static int goodix_ts_mmi_methods_get_irq_status(struct device *dev, void *idata) { + struct platform_device *pdev; + struct goodix_ts_core *core_data; + struct goodix_ts_board_data *ts_bdata; + + GET_GOODIX_DATA(dev); + + ts_bdata = board_data(core_data); + if (!ts_bdata) { + ts_err("Failed to get ts board data"); + return -ENODEV; + } + TO_INT(idata) = gpio_get_value(ts_bdata->irq_gpio); + return 0; +} + +static int goodix_ts_mmi_methods_get_drv_irq(struct device *dev, void *idata) { + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + GET_GOODIX_DATA(dev); + + TO_INT(idata) = atomic_read(&core_data->irq_enabled); + return 0; +} + +static int goodix_ts_mmi_methods_get_poweron(struct device *dev, void *idata) { + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + GET_GOODIX_DATA(dev); + + TO_INT(idata) = core_data->power_on; + return 0; +} + +static int goodix_ts_mmi_methods_get_flashprog(struct device *dev, void *idata) { + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + GET_GOODIX_DATA(dev); + + TO_INT(idata) = core_data->update_status; + return 0; +} + +static int goodix_ts_mmi_methods_drv_irq(struct device *dev, int state) { + int ret = -ENODEV; + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + GET_GOODIX_DATA(dev); + + if (core_data->hw_ops->irq_enable) + ret = core_data->hw_ops->irq_enable(core_data, !(!state)); + + return ret; +} + +/* reset chip + * type:can control software reset and hardware reset, + * but GOODIX has no software reset, + * so the type parameter is not used here. + */ +static int goodix_ts_mmi_methods_reset(struct device *dev, int type) { + int ret = -ENODEV; + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + GET_GOODIX_DATA(dev); + + if (core_data->hw_ops->reset) + ret = core_data->hw_ops->reset(core_data, GOODIX_NORMAL_RESET_DELAY_MS); + return ret; +} + +static int goodix_ts_firmware_update(struct device *dev, char *fwname) { + int ret = -ENODEV; + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + GET_GOODIX_DATA(dev); + + ts_info("HW request update fw, %s", fwname); + /* set firmware image name */ + if (core_data->set_fw_name) + core_data->set_fw_name(fwname); + + ret = goodix_do_fw_update(core_data->ic_configs[CONFIG_TYPE_NORMAL], + UPDATE_MODE_SRC_REQUEST | UPDATE_MODE_BLOCK | UPDATE_MODE_FORCE); + if (ret) + ts_err("failed do fw update"); + + return 0; +} + +static int goodix_ts_mmi_methods_power(struct device *dev, int on) { + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + GET_GOODIX_DATA(dev); + + if (on == TS_MMI_POWER_ON) + return goodix_ts_power_on(core_data); + else if(on == TS_MMI_POWER_OFF) + return goodix_ts_power_off(core_data); + else { + ts_err("Invalid power parameter %d.\n", on); + return -EINVAL; + } +} + +#define CHARGER_MODE_CMD 0xAF +static int goodix_ts_mmi_charger_mode(struct device *dev, int mode) +{ + int ret = 0; + int timeout = 50; + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + GET_GOODIX_DATA(dev); + + /* 5000ms timeout */ + while (core_data->init_stage < CORE_INIT_STAGE2 && timeout--) + msleep(100); + + mutex_lock(&core_data->mode_lock); + ret = goodix_ts_send_cmd(core_data, CHARGER_MODE_CMD, 5, mode, 0x00); + if (ret < 0) { + ts_err("Failed to set charger mode\n"); + } + msleep(20); + ts_err("Success to %s charger mode\n", mode ? "Enable" : "Disable"); + mutex_unlock(&core_data->mode_lock); + + return 0; +} + +#if defined(CONFIG_BOARD_USES_DOUBLE_TAP_CTRL) +static unsigned int set_bit_in_pos(unsigned int val, int bit_pos) +{ + return val |= (1 << bit_pos); +} + +static unsigned int clear_bit_in_pos(unsigned int val, int bit_pos) +{ + return val &= ~(1 << bit_pos); +} +#endif + +static int goodix_berlin_gesture_setup(struct goodix_ts_core *core_data) +{ + const struct goodix_ts_hw_ops *hw_ops = core_data->hw_ops; + unsigned int gesture_cmd = 0; +#if defined(CONFIG_BOARD_USES_DOUBLE_TAP_CTRL) + int ret = 0; + unsigned char gesture_type = 0; + unsigned int (*mod_func)(unsigned int, int) = set_bit_in_pos; + + /* Goodix provided definitive description of gesture mode setting + for 9916 and 9966 touch ICs. It's safer to fail for any others + until it's confirmed!!! + */ + if (core_data->bus->ic_type != IC_TYPE_BERLIN_B && + core_data->bus->ic_type != IC_TYPE_BERLIN_D) { + ts_err("GT%s gestures not set\n", core_data->fw_version.patch_pid); + return -EINVAL; + } + + if (core_data->imports && core_data->imports->get_gesture_type) { + ret = core_data->imports->get_gesture_type(core_data->bus->dev, &gesture_type); + ts_info("Provisioned gestures 0x%02x; rc = %d\n", gesture_type, ret); + } + if (core_data->bus->ic_type == IC_TYPE_BERLIN_D) { + gesture_cmd = 0xFFFF; + mod_func = clear_bit_in_pos; + } + if (gesture_type & TS_MMI_GESTURE_ZERO) { + gesture_cmd = mod_func(gesture_cmd, 13); + ts_info("enable zero gesture mode cmd 0x%04x\n", gesture_cmd); + } + if (gesture_type & TS_MMI_GESTURE_SINGLE) { + gesture_cmd = mod_func(gesture_cmd, 12); + ts_info("enable single gesture mode cmd 0x%04x\n", gesture_cmd); + } + if (gesture_type & TS_MMI_GESTURE_DOUBLE) { + gesture_cmd = mod_func(gesture_cmd, 7); + ts_info("enable double gesture mode cmd 0x%04x\n", gesture_cmd); + } +#ifdef GOODIX_PALM_SENSOR_EN + if (gesture_type & TS_MMI_GESTURE_PALM) { + /* override previous setting */ + gesture_cmd = 0xFFFF; + ts_info("enable palm gesture mode cmd 0x%04x\n", gesture_cmd); + } +#endif + core_data->gesture_cmd = gesture_cmd; + if (core_data->bus->ic_type == IC_TYPE_BERLIN_D) { + /* TODO Is this really necessary??? */ + ret = goodix_ts_send_cmd(core_data, ENTER_GESTURE_MODE_CMD, 6, 0xFF, 0xFF); + if (ret < 0) { + ts_err("Failed to send enter gesture mode\n"); + } + } +#else +#if defined(PRODUCT_MIAMI) + /* TODO Check if Miami 9916??? */ + gesture_cmd = 0x80; +#endif +#endif + hw_ops->gesture(core_data, gesture_cmd); + ts_info("Send enable gesture mode 0x%x\n", gesture_cmd); + + return 0; +} + +static int goodix_ts_mmi_panel_state(struct device *dev, + enum ts_mmi_pm_mode from, enum ts_mmi_pm_mode to) +{ + struct platform_device *pdev; + struct goodix_ts_core *core_data; + const struct goodix_ts_hw_ops *hw_ops; + + GET_GOODIX_DATA(dev); + hw_ops = core_data->hw_ops; + + switch (to) { + case TS_MMI_PM_GESTURE: + hw_ops->irq_enable(core_data, false); + if (hw_ops->gesture) { + goodix_berlin_gesture_setup(core_data); + msleep(16); + hw_ops->irq_enable(core_data, true); + enable_irq_wake(core_data->irq); + core_data->gesture_enabled = true; + } + break; + case TS_MMI_PM_DEEPSLEEP: + /* enter sleep mode or power off */ + /* if (hw_ops->suspend) + hw_ops->suspend(core_data); + */ + core_data->gesture_enabled = false; + break; + case TS_MMI_PM_ACTIVE: + if (hw_ops->resume) + hw_ops->resume(core_data); + if (core_data->gesture_enabled) { + core_data->gesture_enabled = false; + hw_ops->irq_enable(core_data, true); + } + break; + default: + ts_err("Invalid power state parameter %d.\n", to); + return -EINVAL; + } + + return 0; +} + +static int goodix_ts_mmi_pre_resume(struct device *dev) { + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + ts_info("Resume start"); + GET_GOODIX_DATA(dev); + + atomic_set(&core_data->suspended, 0); + atomic_set(&core_data->post_suspended, 0); + if (core_data->gesture_enabled) { + core_data->hw_ops->irq_enable(core_data, false); + disable_irq_wake(core_data->irq); + } + + return 0; +} + +static int goodix_ts_mmi_post_resume(struct device *dev) { + int ret = 0; + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + GET_GOODIX_DATA(dev); + + /* open esd */ + goodix_ts_blocking_notify(NOTIFY_RESUME, NULL); + + mutex_lock(&core_data->mode_lock); + /* All IC status are cleared after reset */ + memset(&core_data->set_mode, 0 , sizeof(core_data->set_mode)); + /* restore data */ + if (core_data->board_data.stylus_mode_ctrl && core_data->get_mode.stylus_mode) { + ret = goodix_stylus_mode(core_data, core_data->get_mode.stylus_mode); + if (!ret) { + core_data->set_mode.stylus_mode = core_data->get_mode.stylus_mode; + ts_info("Success to %s stylus mode", core_data->get_mode.stylus_mode ? "Enable" : "Disable"); + } + } + + if (core_data->board_data.interpolation_ctrl && core_data->get_mode.interpolation) { + //send switch command + ret = goodix_ts_send_cmd(core_data, (core_data->get_mode.report_rate_mode) >> 8, 5, + (core_data->get_mode.report_rate_mode) & 0xFF, 0x00); + if (!ret) { + core_data->set_mode.interpolation = core_data->get_mode.interpolation; + core_data->set_mode.report_rate_mode = core_data->get_mode.report_rate_mode; + msleep(20); + + ts_info("Success to %s interpolation mode\n", + core_data->get_mode.report_rate_mode == REPORT_RATE_CMD_240HZ ? "REPORT_RATE_240HZ" : + (core_data->get_mode.report_rate_mode == REPORT_RATE_CMD_360HZ ? "REPORT_RATE_360HZ" : + (core_data->get_mode.report_rate_mode == REPORT_RATE_CMD_480HZ ? "REPORT_RATE_480HZ" : + (core_data->get_mode.report_rate_mode == REPORT_RATE_CMD_576HZ ? "REPORT_RATE_576HZ" : + (core_data->get_mode.report_rate_mode == REPORT_RATE_CMD_720HZ ? "REPORT_RATE_720HZ" : + "Unsupported"))))); + } + } + + if (core_data->board_data.sample_ctrl && core_data->get_mode.sample) { + ret = goodix_ts_send_cmd(core_data, SAMPLE_SWITCH_CMD, 5, + core_data->get_mode.sample, 0x00); + if (!ret) { + core_data->set_mode.sample = core_data->get_mode.sample; + msleep(20); + ts_info("Success to %d sample mode\n", core_data->get_mode.sample); + } + } + + if (core_data->board_data.leather_mode_ctrl && core_data->get_mode.leather_mode) { + ret = goodix_ts_send_cmd(core_data, LEATHER_MODE_SWITCH_CMD, 5, + core_data->get_mode.leather_mode, 0x00); + if (!ret) { + core_data->set_mode.leather_mode = core_data->get_mode.leather_mode; + msleep(20); + ts_info("Success to %s leather mode", core_data->get_mode.leather_mode ? "Enable" : "Disable"); + } + } + + if (core_data->board_data.film_mode_ctrl && core_data->get_mode.film_mode) { + ret = goodix_ts_send_cmd(core_data, FILM_MODE_SWITCH_CMD, 5, + core_data->get_mode.film_mode, 0x00); + if (!ret) { + core_data->set_mode.film_mode = core_data->get_mode.film_mode; + msleep(20); + ts_info("Success to %s film mode", core_data->get_mode.film_mode ? "Enable" : "Disable"); + } + } + + if (core_data->board_data.edge_ctrl) { + ret = goodix_ts_send_cmd(core_data, EDGE_SWITCH_CMD, 6, + core_data->get_mode.edge_mode[0], core_data->get_mode.edge_mode[1]); + if (!ret) { + memcpy(core_data->set_mode.edge_mode, core_data->get_mode.edge_mode, + sizeof(core_data->get_mode.edge_mode)); + msleep(20); + ts_info("Success to set edge area = %02x, rotation = %02x", + core_data->get_mode.edge_mode[1], core_data->get_mode.edge_mode[0]); + } + } + if (core_data->get_mode.liquid_detection) { + ret = goodix_ts_send_cmd(core_data, LIQUID_DETECTION_SWITCH_CMD, 5, + core_data->get_mode.liquid_detection, 0x00); + if (!ret) { + core_data->set_mode.liquid_detection = core_data->get_mode.liquid_detection; + msleep(20); + ts_info("Success to %d liquid detection mode\n", core_data->get_mode.liquid_detection); + } + } + +#ifdef GOODIX_PALM_SENSOR_EN + if (core_data->get_mode.palm_detection) { + ret = goodix_ts_send_cmd(core_data, PALM_DETECTION_SWITCH_CMD, 5, + core_data->get_mode.palm_detection, 0x00); + if (!ret) { + core_data->set_mode.palm_detection = core_data->get_mode.palm_detection; + msleep(20); + ts_info("Success to %d palm detection mode\n", core_data->get_mode.palm_detection); + } + } +#endif + + if (core_data->board_data.stowed_mode_ctrl) { + core_data->set_mode.stowed = 0; + } + +#ifdef GTP_PEN_NOTIFIER + if (core_data->gtp_pen_detect_flag) { + ret = goodix_ts_send_cmd(core_data, 0x32, 5, 0x1, 0x00); + if (ret < 0) + ts_err("failed to send passive pen mode cmd"); + } +#endif + mutex_unlock(&core_data->mode_lock); +#ifdef CONFIG_GTP_FOD + if(core_data->zerotap_data[0]) { + ts_info("FOD is down during PM resume fod_enable=%d",core_data->fod_enable); + } + core_data->zerotap_data[0] = 0; +#endif + +#ifdef CONFIG_GTP_GHOST_LOG_CAPTURE + atomic_set(&core_data->allow_capture, 1); + ts_info("Resume end, enable ghost log capture"); +#endif + + return 0; +} + +static int goodix_ts_mmi_pre_suspend(struct device *dev) { + int ret = 0; + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + GET_GOODIX_DATA(dev); + + ts_info("Suspend start"); + atomic_set(&core_data->suspended, 1); + +#ifdef CONFIG_GTP_GHOST_LOG_CAPTURE + //disable/stop ghost log capture + atomic_set(&core_data->allow_capture, 0); + ts_info("Suspend start, disable ghost log capture"); + + mutex_lock(&core_data->frame_log_lock); + if(atomic_read(&core_data->trigger_enable) == 1) { + atomic_set(&core_data->trigger_enable, 0); + core_data->data_valid = 0; + frame_log_capture_stop(core_data); + } + mutex_unlock(&core_data->frame_log_lock); +#endif + +#ifdef GOODIX_PALM_SENSOR_EN + if (core_data->set_mode.palm_detection) { + del_timer(&core_data->palm_release_timer); + } +#endif + + if (core_data->board_data.stylus_mode_ctrl && core_data->set_mode.stylus_mode) { + mutex_lock(&core_data->mode_lock); + ret = goodix_stylus_mode(core_data, 0x00); + if (!ret) { + ts_info("Success to exit stylus mode"); + core_data->set_mode.stylus_mode = 0x00; + } + mutex_unlock(&core_data->mode_lock); + } + + /* + * notify suspend event, inform the esd protector + * and charger detector to turn off the work + */ + goodix_ts_blocking_notify(NOTIFY_SUSPEND, NULL); + + return 0; +} + + +static int goodix_ts_mmi_post_suspend(struct device *dev) { + int ret = 0; + struct goodix_ts_core *core_data; + struct platform_device *pdev; + + GET_GOODIX_DATA(dev); + + goodix_ts_release_connects(core_data); + + atomic_set(&core_data->post_suspended, 1); + mutex_lock(&core_data->mode_lock); + if (core_data->board_data.stowed_mode_ctrl && core_data->get_mode.stowed) { + ret = goodix_ts_send_cmd(core_data, ENTER_STOWED_MODE_CMD, 5, core_data->get_mode.stowed, 0x00); + if (ret < 0) { + ts_err("Failed to set stowed mode %d\n", core_data->get_mode.stowed); + } else { + core_data->set_mode.stowed = core_data->get_mode.stowed; + ts_info("Enable stowed mode %d success.\n", core_data->set_mode.stowed); + } + } + mutex_unlock(&core_data->mode_lock); + + ts_info("Suspend end"); + return 0; +} +#ifdef CONFIG_GTP_FOD +static int goodix_ts_mmi_update_fps_mode(struct device *dev, int mode) { + struct goodix_ts_core *core_data; + struct platform_device *pdev; + + GET_GOODIX_DATA(dev); + + core_data->fod_enable = (mode >0) ? 0x01 : 0x00; + ts_info(" update_fps_mode %s:%d\n", (mode > 0) ? "enable" : "disable", mode); + return 0; +} +#endif + +static int goodix_ts_mmi_update_liquid_detect_mode(struct device *dev, int mode) { + int ret = 0; + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + GET_GOODIX_DATA(dev); + + mutex_lock(&core_data->mode_lock); + core_data->get_mode.liquid_detection= mode; + if (core_data->set_mode.liquid_detection == mode) { + ts_debug("The value = %d is same, so not to write", mode); + goto exit; + } + + if (core_data->power_on == 0) { + ts_debug("The touch is in sleep state, restore the value when resume\n"); + goto exit; + } + + //clear liquid status before enable detection + if (mode) + core_data->liquid_status = 0; + + ret = goodix_ts_send_cmd(core_data, LIQUID_DETECTION_SWITCH_CMD, 5, + core_data->get_mode.liquid_detection, 0x00); + if (ret < 0) { + ts_err("failed to set liquid detection, mode = %d", mode); + goto exit; + } + + core_data->set_mode.liquid_detection = mode; + msleep(20); + ts_info("Success to set %d\n", mode); + +exit: + mutex_unlock(&core_data->mode_lock); + return ret; +} + +#define Y_MIN_ID 2 +#define Y_MAX_ID 3 +#define SCREEN_X_MAX 1080 +#define SCREEN_Y_MAX 2992 +#define SCREEN_EXTENDED 2600 +#define SCREEN_PRIM_COMPACT 1980 +#define DISP_MODE_REAR 3 +#define DISP_MODE_FULL 4 +#define DISP_MODE_EXTENDED 0 +#define DISP_MODE_PRIM_COMPACT 1 +#define DISP_MODE_PRIM_PEEK 2 + +static int rdArray[4]; + +static int goodix_ts_mmi_active_region(struct device *dev, int *reg_data) +{ + struct goodix_ts_core *core_data; + struct platform_device *pdev; + int ret, dmode = -1; + + GET_GOODIX_DATA(dev); + + memcpy(rdArray, reg_data, sizeof(rdArray)); + if (rdArray[Y_MIN_ID] > 0) { /* REAR */ + dmode = DISP_MODE_REAR; /* Mode4 */ + } else { /* FULL or PRIMARY */ + if (rdArray[Y_MAX_ID] == SCREEN_Y_MAX) + dmode = DISP_MODE_FULL; /* Mode5 */ + else if (rdArray[Y_MAX_ID] > SCREEN_EXTENDED) + dmode = DISP_MODE_EXTENDED; /* Mode1 */ + else if (rdArray[Y_MAX_ID] > SCREEN_PRIM_COMPACT) + dmode = DISP_MODE_PRIM_COMPACT; /* Mode2 */ + else + dmode = DISP_MODE_PRIM_PEEK; /* Mode3 */ + } + if (dmode >= 0 ) { + ret = core_data->hw_ops->display_mode(core_data, dmode); + if (!ret) + ts_info("set active region: %d %d %d %d; dmode=%d\n", + rdArray[0], rdArray[1], rdArray[Y_MIN_ID], rdArray[Y_MAX_ID], dmode); + } else + dev_err(&pdev->dev, "Invalid display mode; reqion %d %d %d %d\n", + rdArray[0], rdArray[1], rdArray[Y_MIN_ID], rdArray[Y_MAX_ID]); + + return 0; +} + +static int goodix_ts_mmi_methods_get_active_region(struct device *dev, void *uidata) +{ + struct goodix_ts_core *core_data; + struct platform_device *pdev; + + GET_GOODIX_DATA(dev); + + memcpy((int *)uidata, rdArray, sizeof(rdArray)); + return 0; +} + +#ifdef GOODIX_PALM_SENSOR_EN +int goodix_ts_mmi_palm_set_enable(struct device *dev, unsigned int enable) +{ + int ret = 0; + struct platform_device *pdev; + struct goodix_ts_core *core_data; + + GET_GOODIX_DATA(dev); + + mutex_lock(&core_data->mode_lock); + core_data->get_mode.palm_detection= enable; + if (core_data->set_mode.palm_detection == enable) { + ts_info("The value = %d is same, so not to write", enable); + goto exit; + } + + if (core_data->power_on == 0) { + ts_info("The touch is in sleep state, restore the value when resume\n"); + goto exit; + } + + //clear palm status before enable detection + if (enable) + atomic_set(&core_data->palm_status, 0); + else { + if (core_data->imports && core_data->imports->report_palm) { + core_data->imports->report_palm(0); + ts_info("Disable palm detection, report far\n"); + } + del_timer(&core_data->palm_release_timer); + } + + ret = goodix_ts_send_cmd(core_data, PALM_DETECTION_SWITCH_CMD, 5, + core_data->get_mode.palm_detection, 0x00); + if (ret < 0) { + ts_err("failed to set palm detection, enable = %d", enable); + goto exit; + } + + core_data->set_mode.palm_detection = enable; + msleep(20); + ts_info("Success set palm detection to %d\n", enable); + +exit: + mutex_unlock(&core_data->mode_lock); + return ret; +} +#endif + +static struct ts_mmi_methods goodix_ts_mmi_methods = { + .get_vendor = goodix_ts_mmi_methods_get_vendor, + .get_productinfo = goodix_ts_mmi_methods_get_productinfo, + .get_build_id = goodix_ts_mmi_methods_get_build_id, + .get_config_id = goodix_ts_mmi_methods_get_config_id, + .get_bus_type = goodix_ts_mmi_methods_get_bus_type, + .get_irq_status = goodix_ts_mmi_methods_get_irq_status, + .get_drv_irq = goodix_ts_mmi_methods_get_drv_irq, + .get_poweron = goodix_ts_mmi_methods_get_poweron, + .get_flashprog = goodix_ts_mmi_methods_get_flashprog, + .get_active_region = goodix_ts_mmi_methods_get_active_region, + /* SET methods */ + .reset = goodix_ts_mmi_methods_reset, + .drv_irq = goodix_ts_mmi_methods_drv_irq, + .power = goodix_ts_mmi_methods_power, + .charger_mode = goodix_ts_mmi_charger_mode, + .refresh_rate = goodix_ts_mmi_refresh_rate, + .active_region = goodix_ts_mmi_active_region, + .update_liquid_detect_mode = goodix_ts_mmi_update_liquid_detect_mode, +#ifdef GOODIX_PALM_SENSOR_EN + .palm_set_enable = goodix_ts_mmi_palm_set_enable, +#endif + /* Firmware */ + .firmware_update = goodix_ts_firmware_update, + /* vendor specific attribute group */ + .extend_attribute_group = goodix_ts_mmi_extend_attribute_group, + /* PM callback */ + .panel_state = goodix_ts_mmi_panel_state, + .pre_resume = goodix_ts_mmi_pre_resume, + .post_resume = goodix_ts_mmi_post_resume, + .pre_suspend = goodix_ts_mmi_pre_suspend, + .post_suspend = goodix_ts_mmi_post_suspend, +#ifdef CONFIG_GTP_FOD + .update_fod_mode = goodix_ts_mmi_update_fps_mode, +#endif +}; + +int goodix_ts_mmi_dev_register(struct platform_device *pdev) { + int ret; + struct goodix_ts_core *core_data; + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver data"); + return -ENODEV; + } + mutex_init(&core_data->mode_lock); + ret = ts_mmi_dev_register(core_data->bus->dev, &goodix_ts_mmi_methods); + if (ret) { + dev_err(&pdev->dev, "Failed to register ts mmi\n"); + mutex_destroy(&core_data->mode_lock); + return ret; + } + + core_data->imports = &goodix_ts_mmi_methods.exports; + +#if defined(CONFIG_GTP_LIMIT_USE_SUPPLIER) + if (core_data->imports && core_data->imports->get_supplier) { + ret = core_data->imports->get_supplier(core_data->bus->dev, &core_data->supplier); + } +#endif + return 0; +} + +void goodix_ts_mmi_dev_unregister(struct platform_device *pdev) { + struct goodix_ts_core *core_data; + core_data = platform_get_drvdata(pdev); + if (!core_data) + ts_err("Failed to get driver data"); + mutex_destroy(&core_data->mode_lock); + ts_mmi_dev_unregister(core_data->bus->dev); +} diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_mmi.h b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_mmi.h new file mode 100644 index 000000000000..55bd1d483f60 --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_mmi.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * 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. + */ +#ifndef __GOODIX_TS_MMI_H__ +#define __GOODIX_TS_MMI_H__ + +#include +#include +#include +#include "goodix_ts_core.h" + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +int goodix_ts_mmi_dev_register(struct platform_device *ts_device); +void goodix_ts_mmi_dev_unregister(struct platform_device *ts_device); +#else +static int inline goodix_ts_mmi_dev_register(struct platform_device *ts_device) { + return -ENOSYS; +} +static int inline goodix_ts_mmi_dev_unregister(struct platform_device *ts_device) { + return -ENOSYS; +} +#endif +int goodix_ts_send_cmd(struct goodix_ts_core *core_data, + u8 cmd, u8 len, u8 subCmd, u8 subCmd2); + +#define LEATHER_MODE_SWITCH_CMD 0x26 +#define ENTER_LEATHER_MODE 0x01 +#define EXIT_LEATHER_MODE 0x00 + +#define FILM_MODE_SWITCH_CMD 0x27 +#define ENTER_FILM_MODE 0x01 +#define EXIT_FILM_MODE 0x00 + +#define EDGE_SWITCH_CMD 0x17 +#define ROTATE_DEFAULT_0 0x00 +#define ROTATE_LEFT_90 0X40 +#define ROTATE_RIGHT_90 0x80 +#define DEFAULT_EDGE 0x00 +#define SMALL_EDGE 0x40 +#define BIG_EDGE 0x80 + +#define NORMAL_DEFAULT_EDGE 0x08 +#define NORMAL_SMALL_EDGE 0x48 +#define NORMAL_BIG_EDGE 0x88 + +#define STYLUS_MODE_SWITCH_CMD 0xA4 + +#define EXIT_HIGH_REPORT_RATE_CMD 0xC100 +#define SAMPLE_SWITCH_CMD 0xC2 +#define ENTER_GESTURE_MODE_CMD 0xA6 +#define LIQUID_DETECTION_SWITCH_CMD 0x33 +#define PALM_DETECTION_SWITCH_CMD 0x93 +#define ENTER_STOWED_MODE_CMD 0x97 +#endif diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_tools.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_tools.c new file mode 100644 index 000000000000..9a69feeb6d5d --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 == NULL) { + 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 == NULL) { + 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"); +} diff --git a/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_utils.c b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_utils.c new file mode 100644 index 000000000000..6d55eb3d9eda --- /dev/null +++ b/drivers/input/touchscreen/goodix_berlin_mmi/goodix_ts_utils.c @@ -0,0 +1,179 @@ + /* + * 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) { + if (size < 2) + return 1; + for (i = 0; i < size - 2; i++) + cal_checksum += data[i]; + + r_checksum = data[size - 2] + (data[size - 1] << 8); + return (cal_checksum & 0xFFFF) == r_checksum ? 0 : 1; + } + + if (size < 4) + return 1; + for (i = 0; i < size - 4; i += 2) + cal_checksum += data[i] + (data[i + 1] << 8); + r_checksum = data[size - 4] + (data[size - 3] << 8) + + (data[size - 2] << 16) + (data[size - 1] << 24); + return cal_checksum == r_checksum ? 0 : 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; +} diff --git a/drivers/input/touchscreen/goodix_gt96x_mmi/Android.mk b/drivers/input/touchscreen/goodix_gt96x_mmi/Android.mk new file mode 100644 index 000000000000..b8a5ff7cf855 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gt96x_mmi/Android.mk @@ -0,0 +1,23 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(TOUCHSCREEN_GOODIX_BRL_SPI),true) +ifeq ($(call is-board-platform-in-list,taro kalama parrot crow pineapple), true) + KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI=y +else + KERNEL_CFLAGS += CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI=y +endif +endif + + +include $(CLEAR_VARS) +LOCAL_MODULE := goodix_gt96x_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KBUILD_OPTIONS += CONFIG_INPUT_TOUCHSCREEN_MMI=y + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +endif + +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/goodix_gt96x_mmi/Kbuild b/drivers/input/touchscreen/goodix_gt96x_mmi/Kbuild new file mode 100644 index 000000000000..8d599929b010 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gt96x_mmi/Kbuild @@ -0,0 +1,23 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m := goodix_gt96x_mmi.o +goodix_gt96x_mmi-objs := \ + goodix_brl_i2c.o \ + goodix_brl_spi.o \ + goodix_ts_core.o \ + goodix_brl_hw.o \ + goodix_cfg_bin.o \ + goodix_ts_utils.o \ + goodix_brl_fwupdate.o \ + goodix_ts_inspect.o \ + goodix_ts_tools.o \ + goodix_ts_gesture.o + +ifneq ($(filter m y,$(CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI)),) + EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_GOODIX_BRL_SPI +endif + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/goodix_gt96x_mmi/Kconfig b/drivers/input/touchscreen/goodix_gt96x_mmi/Kconfig new file mode 100644 index 000000000000..5ecade5c88c7 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gt96x_mmi/Kconfig @@ -0,0 +1,25 @@ +# +# Goodix touchscreen driver configuration +# +menuconfig TOUCHSCREEN_GOODIX_BRL + tristate "Goodix berlin touchscreen" + help + Say Y here if you have a Goodix berlin series touch controller + to your system. + + If build module, say M. + If unsure, say N. + +if TOUCHSCREEN_GOODIX_BRL + +config TOUCHSCREEN_GOODIX_BRL_SPI + bool "enable SPI connection" + help + Say Y here if the touchscreen is connected via SPI bus. + +config TOUCHSCREEN_GOODIX_BRL_I2C + bool "enable I2C connection" + help + Say Y here if the touchscreen is connected via I2C bus. + +endif diff --git a/drivers/input/touchscreen/goodix_gt96x_mmi/Makefile b/drivers/input/touchscreen/goodix_gt96x_mmi/Makefile new file mode 100644 index 000000000000..97bb0cc69699 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gt96x_mmi/Makefile @@ -0,0 +1,16 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(M)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_brl_fwupdate.c b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_brl_fwupdate.c new file mode 100644 index 000000000000..f975ee979162 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_brl_fwupdate.c @@ -0,0 +1,1218 @@ +/* + * 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 HOLD_CPU_REG_W 0x0002 +#define HOLD_CPU_REG_R 0x2000 +#define MISCTL_REG_BRA 0xD807 +#define MISCTL_REG_BRB 0xD807 +#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 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, + 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, + 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, + ENABLE_MISCTL_BRD, +}; + +enum compare_status { + COMPARE_EQUAL = 0, + COMPARE_NOCODE, + COMPARE_PIDMISMATCH, + COMPARE_FW_NOTEQUAL, + COMPARE_CFG_NOTEQUAL, +}; + +#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() + +/** + * 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 goodix_ts_core *cd, + 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 = cd->bus->ic_type; + int subsys_info_offset = cd->update_ctrl.update_info->subsys_info_offset; + int header_size = cd->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_ts_core *cd = fwu_ctrl->core_data; + struct goodix_fw_version *ic_ver = &cd->fw_version; + struct goodix_ic_info *ic_info = &cd->ic_info; + struct firmware_summary *fw_summary = &fwu_ctrl->fw_data.fw_summary; + u32 file_cfg_id; + + /* compare fw_version */ + if (!memcmp(ic_ver->rom_pid, GOODIX_NOCODE, 6) || + !memcmp(ic_ver->patch_pid, GOODIX_NOCODE, 6)) { + ts_info("there is no code in the chip"); + return COMPARE_NOCODE; + } + + if (memcmp(ic_ver->patch_pid, fw_summary->fw_pid, FW_PID_LEN)) { + ts_err("Product ID mismatch:%s != %s", + ic_ver->patch_pid, fw_summary->fw_pid); + return COMPARE_PIDMISMATCH; + } + + ret = memcmp(ic_ver->patch_vid, fw_summary->fw_vid, FW_VID_LEN); + if (ret) { + ts_info("active firmware version:%*ph", FW_VID_LEN, + ic_ver->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->len > 0) { + file_cfg_id = + goodix_get_file_config_id(fwu_ctrl->ic_config->data); + if (ic_info->version.config_id != file_cfg_id) { + ts_info("ic_cfg_id:0x%x != file_cfg_id:0x%x", + ic_info->version.config_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(struct goodix_ts_core *cd, 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 = cd->hw_ops->write(cd, addr, data, len); + if (r < 0) + goto exit; + + r = cd->hw_ops->read(cd, 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 goodix_ts_core *cd, struct firmware_data *fw_data) +{ + struct goodix_fw_version isp_fw_version; + struct fw_subsys_info *fw_isp; + u32 isp_ram_reg = cd->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(cd, 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(cd, 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); + + cd->hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); + /*check isp state */ + if (cd->hw_ops->read_version(cd, &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; + struct goodix_ts_core *cd = fwu_ctrl->core_data; + struct goodix_ts_hw_ops *hw_ops = cd->hw_ops; + u8 reg_val[4] = {0}; + u8 temp_buf[64] = {0}; + int retry = 20; + int r; + + /*reset IC*/ + ts_info("firmware update, reset"); + hw_ops->reset(cd, 5); + + retry = 100; + /* Hold cpu*/ + do { + reg_val[0] = 0x01; + reg_val[1] = 0x00; + r = hw_ops->write(cd, HOLD_CPU_REG_W, reg_val, 2); + r |= hw_ops->read(cd, HOLD_CPU_REG_R, &temp_buf[0], 4); + r |= hw_ops->read(cd, HOLD_CPU_REG_R, &temp_buf[4], 4); + r |= hw_ops->read(cd, 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 || + fwu_ctrl->core_data->bus->ic_type == IC_TYPE_NOTTINGHAM) + hw_ops->write(cd, misctl_reg, (u8 *)&enable_misctl_val, 4); + else + hw_ops->write(cd, 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 = hw_ops->write(cd, ESD_KEY_REG, reg_val, 1); + r |= hw_ops->read(cd, 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 = hw_ops->write(cd, watch_dog_reg, reg_val, 1); + ts_info("disable watch dog"); + + /* load ISP code and run form isp */ + r = goodix_load_isp(cd, &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_ts_core *cd, + struct goodix_flash_cmd *flash_cmd) +{ + int i, ret, retry; + struct goodix_flash_cmd tmp_cmd; + u32 flash_cmd_reg = cd->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 = cd->hw_ops->write(cd, 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 = cd->hw_ops->read(cd, 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 = cd->hw_ops->read(cd, 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(struct goodix_ts_core *cd, + u8 subsys_type, u8 *pkg, + u32 flash_addr, u16 pkg_len) +{ + int ret, retry; + struct goodix_flash_cmd flash_cmd; + u32 isp_buffer_reg = cd->update_ctrl.update_info->isp_buffer_reg; + + retry = 2; + do { + ret = cd->hw_ops->write(cd, 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(cd, &flash_cmd); + if (!ret) { + ts_info("success write package to 0x%05X, 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 goodix_ts_core *cd, + 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 0x%05X,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(cd, subsys->type, fw_packet, + subsys_base_addr + offset, data_size + 4); + if (r) { + ts_err("failed flash to 0x%05X,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->len > 0) { + subsys_cfg.data = fw_ctrl->ic_config->data; + subsys_cfg.size = GOODIX_CFG_MAX_SIZE; + subsys_cfg.flash_addr = config_data_reg; + subsys_cfg.type = CONFIG_DATA_TYPE; + r = goodix_flash_subsystem(fw_ctrl->core_data, &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_ctrl->core_data, 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) +{ + struct goodix_ts_core *cd = fwu_ctrl->core_data; + int ret; + + /* step 1: reset IC */ + cd->hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); + /* step 2: read version */ + ret = cd->hw_ops->read_version(cd, &cd->fw_version); + if (ret < 0) { + ts_err("still failed to read version after upgraded"); + return -EFAULT; + } + /* step 3: read ic info */ + ret = cd->hw_ops->get_ic_info(cd, &cd->ic_info); + if (ret < 0) { + ts_err("still failed to read ic info after upgraded"); + return -EFAULT; + } + + ret = goodix_fw_version_compare(fwu_ctrl); + if (ret == COMPARE_EQUAL || ret == COMPARE_CFG_NOTEQUAL) + return 0; + + return -EFAULT; +} + +/** + * 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->core_data, &fwu_ctrl->fw_data); + if (ret < 0) + return ret; + + if (!(fwu_ctrl->mode & UPDATE_MODE_FORCE)) { + ret = goodix_fw_version_compare(fwu_ctrl); + if (!ret) { + ts_info("no need to upgrade"); + return 0; + } + 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 device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int mode = 0; + struct device *device = + container_of(((struct kobject *)dev)->parent, struct device, kobj); + struct goodix_ts_core *cd = dev_get_drvdata(device); + + if (!buf || count <= 0) { + ts_err("invalid params"); + 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; + } + + goodix_do_fw_update(cd, mode); + return count; +} + +// 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 device *dev, struct device_attribute *attr, + char *buf) +{ + struct device *device = + container_of(((struct kobject *)dev)->parent, struct device, kobj); + struct goodix_ts_core *cd = dev_get_drvdata(device); + struct fw_update_ctrl *fw_ctrl = &cd->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: + sprintf(str, "preparing"); + break; + case UPSTA_UPDATING: + sprintf(str, "updating"); + break; + case UPSTA_SUCCESS: + sprintf(str, "success"); + break; + case UPSTA_FAILED: + sprintf(str, "failed"); + break; + case UPSTA_NOTWORK: + default: + sprintf(str, "notwork"); + break; + } + + r = snprintf(buf, PAGE_SIZE, "result:%s spend_time:%dms\n", + str, fw_ctrl->spend_time); + + return r; +} + +static DEVICE_ATTR(update_en, 0220, NULL, goodix_sysfs_update_en_store); +static DEVICE_ATTR(result, 0664, goodix_sysfs_result_show, NULL); + +static struct attribute *goodix_fwu_attrs[] = { + &dev_attr_update_en.attr, + &dev_attr_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(struct goodix_ts_core *cd) +{ + struct fw_update_ctrl *fw_ctrl = &cd->update_ctrl; + int i; + + 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_info("can't find Firmware image [%s]", name); + 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; + struct goodix_ts_core *cd = fwu_ctrl->core_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"); + if (cd->init_stage >= CORE_INIT_STAGE2) { + cd->hw_ops->irq_enable(cd, 0); + goodix_ts_esd_off(cd); + } + + goodix_get_config_proc(cd); + + 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) { + if (r == -ENOENT) { + ts_info("skip update"); + fwu_ctrl->status = UPSTA_NOTWORK; + } else { + ts_err("fw update failed, %d", r); + fwu_ctrl->status = UPSTA_FAILED; + } + } else { + ts_info("fw update success"); + fwu_ctrl->status = UPSTA_SUCCESS; + } + + end = ktime_get(); + fwu_ctrl->spend_time = ktime_to_ms(ktime_sub(end, start)); + if (cd->init_stage >= CORE_INIT_STAGE2) { + cd->hw_ops->irq_enable(cd, 1); + goodix_ts_esd_on(cd); + } + + return r; +} + +int goodix_do_fw_update(struct goodix_ts_core *cd, int mode) +{ + struct task_struct *fwu_thrd; + struct fw_update_ctrl *fwu_ctrl = &cd->update_ctrl; + int ret; + + fwu_ctrl->mode = mode; + fwu_ctrl->ic_config = &cd->ic_configs[CONFIG_TYPE_NORMAL]; + 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->update_ctrl.initialized) + return 0; + + ret = goodix_fw_sysfs_init(core_data, &core_data->update_ctrl); + if (ret) { + ts_err("failed create fwupate sysfs node"); + return ret; + } + + core_data->update_ctrl.initialized = true; + mutex_init(&core_data->update_ctrl.mutex); + core_data->update_ctrl.core_data = core_data; + strlcpy(core_data->update_ctrl.fw_name, core_data->board_data.fw_name, + sizeof(core_data->update_ctrl.fw_name)); + + if (core_data->bus->ic_type == IC_TYPE_BERLIN_A) + core_data->update_ctrl.update_info = &update_bra; + else if (core_data->bus->ic_type == IC_TYPE_BERLIN_B) + core_data->update_ctrl.update_info = &update_brb; + else + core_data->update_ctrl.update_info = &update_brd; + + ts_info("success init fw update"); + return 0; +} + +void goodix_fw_update_uninit(struct goodix_ts_core *core_data) +{ + if (core_data->update_ctrl.initialized) { + goodix_fw_sysfs_remove(core_data); + mutex_destroy(&core_data->update_ctrl.mutex); + } +} diff --git a/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_brl_hw.c b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_brl_hw.c new file mode 100644 index 000000000000..a75f0947eb59 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_brl_hw.c @@ -0,0 +1,1684 @@ + /* + * 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" + +/* 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}; + + if (cd->bus->bus_type == GOODIX_BUS_TYPE_SPI && + cd->bus->ic_type == IC_TYPE_BERLIN_A) + return 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) { + ts_err("device confirm failed, rx_buf:%*ph", 8, rx_buf); + return -EINVAL; + } + + 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; +} + +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) { + 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) { + ret = regulator_enable(cd->avdd); + if (ret < 0) { + ts_err("Failed to enable avdd:%d", ret); + goto power_off; + } + } + usleep_range(15000, 15100); + gpio_direction_output(reset_gpio, 1); + usleep_range(4000, 4100); + ret = brl_dev_confirm(cd); + if (ret < 0) + goto power_off; + ret = brl_reset_after(cd); + if (ret < 0) + goto power_off; + + msleep(GOODIX_NORMAL_RESET_DELAY_MS); + 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 (avdd_gpio > 0) + gpio_direction_output(avdd_gpio, 0); + else if (cd->avdd) + regulator_disable(cd->avdd); + return ret; +} + +int brl_suspend(struct goodix_ts_core *cd) +{ + u32 cmd_reg = cd->ic_info.misc.cmd_addr; + u8 sleep_cmd[] = {0x00, 0x00, 0x04, 0x84, 0x88, 0x00}; + + return cd->hw_ops->write(cd, cmd_reg, sleep_cmd, sizeof(sleep_cmd)); +} + +int brl_resume(struct goodix_ts_core *cd) +{ + return cd->hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS); +} + +#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 = 6; + + cmd.data[0] = gesture_type & 0xFF; + cmd.data[1] = (gesture_type >> 8) & 0xFF; + 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_info("Irq enabled"); + return 0; + } + + if (!enable && atomic_cmpxchg(&cd->irq_enabled, 1, 0)) { + disable_irq(cd->irq); + ts_info("Irq disabled"); + return 0; + } + ts_debug("warnning: irq deepth inbalance!"); + 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; + int ret; + + ret = bus->read(bus->dev, addr, data, len); + if (ret == 0) + ts_debug("[0x%04X]:%*ph", addr, len > 24 ? 24 : len, data); + + return ret; +} + +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; + int ret; + + ret = bus->write(bus->dev, addr, data, len); + if (ret == 0) + ts_debug("[0x%04X]:%*ph", addr, len > 24 ? 24 : len, data); + + return ret; +} + +/* 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; + + if (cd->update_ctrl.status == UPSTA_PREPARING || + cd->update_ctrl.status == UPSTA_UPDATING) { + ts_err("fw updating, skip cmd"); + return -EINVAL; + } + + if (misc->cmd_addr == 0x0000) { + ts_err("invalid cmd addr:0x0000, skip cmd"); + return -EINVAL; + } + + mutex_lock(&cd->cmd_lock); + 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"); + goto exit; + } + 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); + goto exit; + } + 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 + ret = 0; + goto exit; + } + 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"); + ret = -EINVAL; +exit: + mutex_unlock(&cd->cmd_lock); + return ret; +} + +/* read from flash */ +#define FLASH_CMD_R_START 0x09 +#define FLASH_CMD_W_START 0x0A +#define FLASH_CMD_RW_FINISH 0x0B +#define FLASH_CMD_STATE_READY 0x04 +#define FLASH_CMD_STATE_CHECKERR 0x05 +#define FLASH_CMD_STATE_DENY 0x06 +#define FLASH_CMD_STATE_OKAY 0x07 +static int goodix_flash_cmd(struct goodix_ts_core *cd, + uint8_t cmd, uint8_t status, + int retry_count) +{ + u32 cmd_addr = cd->ic_info.misc.cmd_addr; + struct goodix_ts_cmd temp_cmd; + int ret; + int i; + u8 rcv_buf[2]; + + temp_cmd.state = 0; + temp_cmd.ack = 0; + temp_cmd.len = 4; + temp_cmd.cmd = cmd; + goodix_append_checksum(&temp_cmd.buf[2], temp_cmd.len - 2, + CHECKSUM_MODE_U8_LE); + ret = brl_write(cd, cmd_addr, temp_cmd.buf, temp_cmd.len + 2); + if (ret < 0) { + ts_err("send flash cmd[%x] failed", cmd); + return ret; + } + + for (i = 0; i < retry_count; i++) { + msleep(20); + ret = brl_read(cd, cmd_addr, rcv_buf, 2); + if (rcv_buf[0] == status && rcv_buf[1] == 0x80) + return 0; + } + + ts_err("r_sta[0x%x] != status[0x%x]", rcv_buf[0], status); + return -EINVAL; +} + +static int brl_flash_read(struct goodix_ts_core *cd, + unsigned int addr, unsigned char *buf, + unsigned int len) +{ + int i; + int ret; + u8 *tmp_buf; + u32 buffer_addr = cd->ic_info.misc.fw_buffer_addr; + struct goodix_ts_cmd temp_cmd; + uint32_t checksum = 0; + struct flash_head head_info; + int read_len = len; + u8 *p = (u8 *)&head_info.address; + + if ((read_len % 2) != 0) + read_len++; + + tmp_buf = kzalloc(read_len + sizeof(head_info), GFP_KERNEL); + if (!tmp_buf) + return -ENOMEM; + + head_info.address = cpu_to_le32(addr); + head_info.length = cpu_to_le32(read_len); + for (i = 0; i < 8; i += 2) + checksum += p[i] | (p[i + 1] << 8); + head_info.checksum = checksum; + + ret = goodix_flash_cmd(cd, FLASH_CMD_R_START, FLASH_CMD_STATE_READY, 15); + if (ret < 0) { + ts_err("failed enter flash read state"); + goto read_end; + } + + ret = brl_write(cd, buffer_addr, (u8 *)&head_info, sizeof(head_info)); + if (ret < 0) { + ts_err("failed write flash head info"); + goto read_end; + } + + ret = goodix_flash_cmd(cd, FLASH_CMD_RW_FINISH, FLASH_CMD_STATE_OKAY, 50); + if (ret) { + ts_err("faild read flash ready state"); + goto read_end; + } + + ret = brl_read(cd, buffer_addr, tmp_buf, read_len + sizeof(head_info)); + if (ret < 0) { + ts_err("failed read data len %lu", read_len + sizeof(head_info)); + goto read_end; + } + + checksum = 0; + for (i = 0; i < read_len + sizeof(head_info) - 4; i += 2) + checksum += tmp_buf[4 + i] | (tmp_buf[5 + i] << 8); + + if (checksum != le32_to_cpup((__le32 *)tmp_buf)) { + ts_err("read back data checksum error"); + ret = -EINVAL; + goto read_end; + } + + memcpy(buf, tmp_buf + sizeof(head_info), len); + ret = 0; +read_end: + temp_cmd.len = 4; + temp_cmd.cmd = 0x0C; + brl_send_cmd(cd, &temp_cmd); + kfree(tmp_buf); + return ret; +} + +#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; + 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; + + if (len > misc->fw_buffer_max_len) { + ts_err("config len exceed limit %d > %d", + len, misc->fw_buffer_max_len); + 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) + 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_ts_core *cd, const u8 *data) +{ + int i; + struct goodix_ic_info *info = &cd->ic_info; + 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; + struct goodix_ic_info_other *other = &info->other; + + 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_rawdata_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); + + data += sizeof(*misc); + memcpy((u8 *)other, data, sizeof(*other)); + + return 0; +} + +static void goodix_compatible_ic_info(struct goodix_ts_core *cd) +{ + struct goodix_ic_info_v2 *info_v2 = &cd->ic_info_v2; + struct goodix_ic_info *info = &cd->ic_info; + int i; + + info->length = info_v2->length; + info->version.info_customer_id = info_v2->info_customer_id; + info->version.info_version_id = info_v2->info_version_id; + info->version.ic_die_id = info_v2->version.ic_die_id; + info->version.ic_version_id = info_v2->version.ic_version_id; + info->version.config_id = info_v2->version.config_id; + info->version.config_version = info_v2->version.config_version; + info->version.frame_data_customer_id = info_v2->version.frame_data_customer_id; + info->version.frame_data_version_id = info_v2->version.frame_data_version_id; + info->version.touch_data_customer_id = info_v2->version.touch_data_customer_id; + info->version.touch_data_version_id = info_v2->version.touch_data_version_id; + + info->feature.freqhop_feature = info_v2->sample.freqhop_feature; + info->feature.calibration_feature = info_v2->sample.calibration_feature; + info->feature.gesture_feature = info_v2->sample.gesture_feature; + // info->feature.side_touch_feature = 0; + info->feature.stylus_feature = info_v2->sample.stylus_feature; + + info->parm.drv_num = info_v2->sample.drv_num; + info->parm.sen_num = info_v2->sample.sen_num; + info->parm.button_num = info_v2->sample.button_num; + info->parm.force_num = info_v2->sample.force_num; + info->parm.active_scan_rate_num = info_v2->sample.active_scan_rate_num; + for (i = 0; i < info->parm.active_scan_rate_num; i++) + info->parm.active_scan_rate[i] = info_v2->sample.active_scan_rate[i]; + info->parm.mutual_freq_num = info_v2->sample.mutual_freq_num; + for (i = 0; i < info->parm.mutual_freq_num; i++) + info->parm.mutual_freq[i] = info_v2->sample.mutual_freq[i]; + info->parm.self_tx_freq_num = info_v2->sample.self_tx_freq_num; + for (i = 0; i < info->parm.self_tx_freq_num; i++) + info->parm.self_tx_freq[i] = info_v2->sample.self_tx_freq[i]; + info->parm.self_rx_freq_num = info_v2->sample.self_rx_freq_num; + for (i = 0; i < info->parm.self_rx_freq_num; i++) + info->parm.self_rx_freq[i] = info_v2->sample.self_rx_freq[i]; + info->parm.stylus_freq_num = info_v2->sample.stylus_freq_num; + for (i = 0; i < info->parm.stylus_freq_num; i++) + info->parm.stylus_freq[i] = info_v2->sample.stylus_freq[i]; + + info->misc.cmd_addr = info_v2->address.cmd_addr; + info->misc.cmd_max_len = info_v2->address.cmd_max_len; + info->misc.cmd_reply_addr = info_v2->address.cmd_reply_addr; + info->misc.cmd_reply_len = info_v2->address.cmd_reply_len; + info->misc.fw_state_addr = info_v2->address.fw_state_addr; + info->misc.fw_state_len = info_v2->address.fw_state_len; + info->misc.fw_buffer_addr = info_v2->address.fw_buffer_addr; + info->misc.fw_buffer_max_len = info_v2->address.fw_buffer_max_len; + info->misc.frame_data_addr = info_v2->address.frame_data_addr; + info->misc.frame_data_head_len = info_v2->address.frame_data_head_len; + info->misc.fw_attr_len = info_v2->address.fw_attr_len; + info->misc.fw_log_len = info_v2->address.fw_log_len; + info->misc.pack_max_num = info_v2->address.pack_max_num; + info->misc.pack_compress_version = info_v2->address.pack_compress_version; + info->misc.stylus_struct_len = info_v2->address.stylus_struct_len; + info->misc.mutual_struct_len = info_v2->address.mutual_struct_len; + info->misc.self_struct_len = info_v2->address.self_struct_len; + info->misc.noise_struct_len = info_v2->address.noise_struct_len; + info->misc.touch_data_addr = info_v2->address.touch_data_addr; + info->misc.touch_data_head_len = info_v2->address.touch_data_head_len; + info->misc.point_struct_len = info_v2->address.point_struct_len; + info->misc.screen_real_max_x = info_v2->sample.screen_real_max_x; + info->misc.screen_real_max_y = info_v2->sample.screen_real_max_y; + info->misc.mutual_rawdata_addr = info_v2->address.mutual_rawdata_addr; + info->misc.mutual_diffdata_addr = info_v2->address.mutual_diffdata_addr; + info->misc.mutual_refdata_addr = info_v2->address.mutual_refdata_addr; + info->misc.self_rawdata_addr = info_v2->address.self_rawdata_addr; + info->misc.self_diffdata_addr = info_v2->address.self_diffdata_addr; + info->misc.self_refdata_addr = info_v2->address.self_refdata_addr; + info->misc.iq_rawdata_addr = info_v2->address.iq_rawdata_addr; + info->misc.iq_refdata_addr = info_v2->address.iq_refdata_addr; + info->misc.im_rawdata_addr = info_v2->address.im_rawdata_addr; + info->misc.im_rawdata_len = info_v2->address.im_rawdata_len; + info->misc.noise_rawdata_addr = info_v2->address.noise_rawdata_addr; + info->misc.noise_rawdata_len = info_v2->address.noise_rawdata_len; + info->misc.stylus_rawdata_addr = info_v2->address.stylus_rawdata_addr; + info->misc.stylus_rawdata_len = info_v2->address.stylus_rawdata_len; + info->misc.noise_data_addr = info_v2->address.noise_data_addr; + info->misc.esd_addr = info_v2->address.esd_addr; + info->misc.auto_scan_cmd_addr = info_v2->address.auto_scan_cmd_addr; + info->misc.auto_scan_info_addr = info_v2->address.auto_scan_info_addr; + + info->other.normalize_k_version = info_v2->version.normalize_k_version; + info->other.irrigation_data_addr = info_v2->address.irrigation_data_addr; + info->other.algo_debug_data_addr = info_v2->address.algo_debug_data_addr; + info->other.algo_debug_data_len = info_v2->address.algo_debug_data_len; + info->other.update_sync_data_addr = info_v2->address.update_sync_data_addr; + info->other.screen_max_x = info_v2->sample.screen_max_x; + info->other.screen_max_y = info_v2->sample.screen_max_y; +} + +static int convert_ic_info_v2(struct goodix_ts_core *cd, const u8 *data) +{ + struct goodix_ic_info_v2 *info_v2 = &cd->ic_info_v2; + int i; + + info_v2->length = le16_to_cpup((__le16 *)data); + info_v2->info_customer_id = data[2]; + info_v2->info_version_id = data[3]; + + // sub version + data += 4; + memcpy((u8 *)&info_v2->version, data, sizeof(info_v2->version)); + + // sub sample + data += sizeof(info_v2->version); + memcpy((u8 *)&info_v2->sample, data, 16); + + data += 16; + info_v2->sample.active_scan_rate_num = *data++; + if (info_v2->sample.active_scan_rate_num > MAX_SCAN_RATE_NUM) { + ts_err("invalid scan rate num %d > %d", + info_v2->sample.active_scan_rate_num, MAX_SCAN_RATE_NUM); + return -EINVAL; + } + for (i = 0; i < info_v2->sample.active_scan_rate_num; i++) + info_v2->sample.active_scan_rate[i] = le16_to_cpup((__le16 *)(data + i * 2)); + + data += info_v2->sample.active_scan_rate_num * 2; + info_v2->sample.mutual_freq_num = *data++; + if (info_v2->sample.mutual_freq_num > MAX_SCAN_FREQ_NUM) { + ts_err("invalid mntual freq num %d > %d", + info_v2->sample.mutual_freq_num, MAX_SCAN_FREQ_NUM); + return -EINVAL; + } + for (i = 0; i < info_v2->sample.mutual_freq_num; i++) + info_v2->sample.mutual_freq[i] = le16_to_cpup((__le16 *)(data + i * 2)); + + data += info_v2->sample.mutual_freq_num * 2; + info_v2->sample.self_tx_freq_num = *data++; + if (info_v2->sample.self_tx_freq_num > MAX_SCAN_FREQ_NUM) { + ts_err("invalid tx freq num %d > %d", + info_v2->sample.self_tx_freq_num, MAX_SCAN_FREQ_NUM); + return -EINVAL; + } + for (i = 0; i < info_v2->sample.self_tx_freq_num; i++) + info_v2->sample.self_tx_freq[i] = le16_to_cpup((__le16 *)(data + i * 2)); + + data += info_v2->sample.self_tx_freq_num * 2; + info_v2->sample.self_rx_freq_num = *data++; + if (info_v2->sample.self_rx_freq_num > MAX_SCAN_FREQ_NUM) { + ts_err("invalid rx freq num %d > %d", + info_v2->sample.self_rx_freq_num, MAX_SCAN_FREQ_NUM); + return -EINVAL; + } + for (i = 0; i < info_v2->sample.self_rx_freq_num; i++) + info_v2->sample.self_rx_freq[i] = le16_to_cpup((__le16 *)(data + i * 2)); + + data += info_v2->sample.self_rx_freq_num * 2; + info_v2->sample.stylus_freq_num = *data++; + if (info_v2->sample.stylus_freq_num > MAX_FREQ_NUM_STYLUS) { + ts_err("invalid stylus freq num %d > %d", + info_v2->sample.stylus_freq_num, MAX_FREQ_NUM_STYLUS); + return -EINVAL; + } + for (i = 0; i < info_v2->sample.stylus_freq_num; i++) + info_v2->sample.stylus_freq[i] = le16_to_cpup((__le16 *)(data + i * 2)); + + data += info_v2->sample.stylus_freq_num * 2; + info_v2->sample.stylus_tx2_freq_num = *data++; + if (info_v2->sample.stylus_tx2_freq_num > MAX_FREQ_NUM_STYLUS) { + ts_err("invalid stylus tx2 freq num %d > %d", + info_v2->sample.stylus_tx2_freq_num, MAX_FREQ_NUM_STYLUS); + return -EINVAL; + } + for (i = 0; i < info_v2->sample.stylus_tx2_freq_num; i++) + info_v2->sample.stylus_tx2_freq[i] = le16_to_cpup((__le16 *)(data + i * 2)); + + // sub address + data += info_v2->sample.stylus_tx2_freq_num * 2; + memcpy((u8 *)&info_v2->address, data, sizeof(info_v2->address)); + + // sub customer + data += sizeof(info_v2->address); + memcpy((u8 *)&info_v2->customer, data, sizeof(info_v2->customer)); + + goodix_compatible_ic_info(cd); + return 0; +} + +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 info_ver; + 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; + } + + info_ver = afe_data[3]; + if (info_ver < 0x80) + ret = convert_ic_info(cd, afe_data); + else + ret = convert_ic_info_v2(cd, afe_data); + if (ret) { + ts_err("convert ic info encounter error"); + return ret; + } + + /* 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 BYTES_STYLUS_LEN 16 +#define IRQ_EVENT_MAX_SIZE 106 + +#define GOODIX_TOUCH_EVENT 0x80 +#define GOODIX_REQUEST_EVENT 0x40 +#define GOODIX_GESTURE_EVENT 0x20 +#define GOODIX_FP_EVENT 0x08 + +static void goodix_parse_finger(struct goodix_touch_data *touch_data, + u8 *buf, int id) +{ + touch_data->coords[id].status = TS_TOUCH; + touch_data->coords[id].x = le16_to_cpup((__le16 *)(buf + 2)); + touch_data->coords[id].y = le16_to_cpup((__le16 *)(buf + 4)); + touch_data->coords[id].w = le16_to_cpup((__le16 *)(buf + 6)); + touch_data->touch_num += 1; +} + +static unsigned int goodix_pen_btn_code[] = {BTN_STYLUS, BTN_STYLUS2}; +static void goodix_parse_pen(struct goodix_pen_data *pen_data, + u8 *event_head, u8 *buf) +{ + u8 cur_key_map; + int16_t x_angle, y_angle; + int i; + + pen_data->coords.status = TS_TOUCH; + pen_data->coords.x = le16_to_cpup((__le16 *)(buf + 2)); + pen_data->coords.y = le16_to_cpup((__le16 *)(buf + 4)); + pen_data->coords.p = le16_to_cpup((__le16 *)(buf + 6)); + x_angle = le16_to_cpup((__le16 *)(buf + 8)); + y_angle = le16_to_cpup((__le16 *)(buf + 10)); + pen_data->coords.tilt_x = x_angle / 100; + pen_data->coords.tilt_y = y_angle / 100; + + cur_key_map = (event_head[3] & 0x0F) >> 1; + for (i = 0; i < GOODIX_MAX_PEN_KEY; i++) { + if (!(cur_key_map & (1 << i))) + continue; + pen_data->keys[i].status = TS_TOUCH; + } +} + +// TODO: confirm these touch key code. +static unsigned int goodix_touch_btn_code[] = { + KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, + KEY_5, KEY_6, KEY_7, KEY_8, KEY_9 +}; +static void goodix_parse_key(struct goodix_touch_data *touch_data, + u8 *buf) +{ + int i; + u32 key_map = ((buf[3] & 0x03) << 8) | buf[2]; + + for (i = 0; i < GOODIX_MAX_KEY; i++) { + if (!(key_map & (1 << i))) + continue; + touch_data->keys[i].status = TS_TOUCH; + } + ts_debug("touch key:0x%06x", key_map); +} + +static int goodix_touch_handler(struct goodix_ts_core *cd, + struct goodix_ts_event *ts_event, + u8 *buffer) +{ + struct goodix_touch_data *touch_data = &ts_event->touch_data; + struct goodix_pen_data *pen_data = &ts_event->pen_data; + int point_struct_len = cd->ic_info.misc.point_struct_len; + u8 touch_num = 0; + u8 have_key = 0; + u8 *data; + u8 point_type; + int checksum_len = 0; + int ret = 0; + int i; + int tid; + + touch_num = buffer[2] & 0x0F; + have_key = (buffer[3] >> 4) & 0x01; + if (have_key) + touch_num++; + + for (i = 0; i < GOODIX_MAX_KEY; i++) + touch_data->keys[i].code = goodix_touch_btn_code[i]; + for (i = 0; i < GOODIX_MAX_PEN_KEY; i++) + pen_data->keys[i].code = goodix_pen_btn_code[i]; + + if (touch_num > 0) { + data = buffer + IRQ_EVENT_HEAD_LEN; + for (i = 0; i < touch_num; i++) { + point_type = data[0] & 0x0F; + tid = (data[0] >> 4) & 0x0F; + switch (point_type) { + case POINT_TYPE_STYLUS_HOVER: + pen_data->is_hover = true; + goodix_parse_pen(pen_data, buffer, data); + checksum_len += BYTES_STYLUS_LEN; + data += BYTES_STYLUS_LEN; + break; + case POINT_TYPE_STYLUS: + goodix_parse_pen(pen_data, buffer, data); + checksum_len += BYTES_STYLUS_LEN; + data += BYTES_STYLUS_LEN; + break; + case POINT_TYPE_FINGER: + goodix_parse_finger(touch_data, data, tid); + checksum_len += point_struct_len; + data += point_struct_len; + break; + case POINT_TYPE_KEY: + goodix_parse_key(touch_data, data); + checksum_len += BYTES_PER_POINT; + data += BYTES_PER_POINT; + break; + case POINT_TYPE_GLOVE: + break; + default: + ts_debug("not support point type:%d", point_type); + break; + } + } + ret = checksum_cmp(&buffer[IRQ_EVENT_HEAD_LEN], + checksum_len + 2, + CHECKSUM_MODE_U8_LE); + if (ret) { + ts_debug("touch data checksum error"); + return -EINVAL; + } + } + + ts_event->fp_flag = buffer[0] & GOODIX_FP_EVENT; + 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; + u8 pre_buf[IRQ_EVENT_MAX_SIZE]; + u8 event_status; + int ret; + + memset(ts_event, 0, sizeof(*ts_event)); + + ret = hw_ops->read(cd, misc->touch_data_addr, + pre_buf, sizeof(pre_buf)); + if (ret) { + ts_debug("failed get event head data"); + return ret; + } + + if (pre_buf[0] == 0x00) { + ts_debug("invalid touch head"); + return -EINVAL; + } + + /* read done */ + hw_ops->after_event_handler(cd); + + 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) { + ret = goodix_touch_handler(cd, ts_event, pre_buf); + if (ret < 0) + return ret; + ts_event->event_type |= EVENT_TOUCH; + if (cd->board_data.pen_enable) + ts_event->event_type |= EVENT_PEN; + } + + 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); + } + + 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; + static unsigned char frame_buf[GOODIX_MAX_FRAMEDATA_LEN]; + unsigned char *cur_ptr; + unsigned int flag_addr = cd->ic_info.misc.frame_data_addr; + int tx = cd->ic_info.parm.drv_num; + int rx = cd->ic_info.parm.sen_num; + + /* 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; + } + + 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, + tx * rx * 2); + + 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_esd_off(cd); + + 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_esd_on(cd); + 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 || + cd->bus->ic_type == IC_TYPE_NOTTINGHAM) + return brld_get_cap_data(cd, info); + + /* disable irq & close esd */ + brl_irq_enbale(cd, false); + goodix_ts_esd_off(cd); + + /* 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_esd_on(cd); + 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, + .read_flash = brl_flash_read, + .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; +} diff --git a/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_brl_i2c.c b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_brl_i2c.c new file mode 100644 index 000000000000..664ad4192881 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_brl_i2c.c @@ -0,0 +1,242 @@ + /* + * 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 +#include +#include + +#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 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 int goodix_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct goodix_device_resource *dev_res; + int ret = 0; + + ts_info("goodix i2c probe in"); + ret = i2c_check_functionality(client->adapter, + I2C_FUNC_I2C); + if (!ret) + return -EIO; + + dev_res = kzalloc(sizeof(*dev_res), GFP_KERNEL); + if (!dev_res) + return -ENOMEM; + goodix_device_register(dev_res); + + /* get ic type */ + ret = goodix_get_ic_type(client->dev.of_node, &dev_res->bus); + if (ret < 0) + return ret; + + dev_res->bus.bus_type = GOODIX_BUS_TYPE_I2C; + dev_res->bus.dev = &client->dev; + dev_res->bus.read = goodix_i2c_read; + dev_res->bus.write = goodix_i2c_write; + + dev_res->pdev.name = GOODIX_CORE_DRIVER_NAME; + dev_res->pdev.id = dev_res->id; + dev_res->pdev.num_resources = 0; + + /* register platform device, then the goodix_ts_core + * module will probe the touch device. + */ + ret = platform_device_register(&dev_res->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(dev_res); + ts_info("i2c probe out, %d", ret); + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id i2c_matchs[] = { + {.compatible = "goodix,brl-a",}, + {.compatible = "goodix,brl-b",}, + {.compatible = "goodix,brl-d",}, + {.compatible = "goodix,nottingham",}, + {}, +}; +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, + .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); +} diff --git a/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_brl_spi.c b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_brl_spi.c new file mode 100644 index 000000000000..aaf2c8782936 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_brl_spi.c @@ -0,0 +1,277 @@ + /* + * 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 +#include +#include + +#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 + +/** + * 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 int goodix_spi_probe(struct spi_device *spi) +{ + struct goodix_device_resource *dev_res; + int ret = 0; + + ts_info("goodix spi probe in"); + + /* init spi_device */ + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + + ts_info("spi_info: speed[%d] mode[%d] bits_per_word[%d]", + spi->max_speed_hz, spi->mode, spi->bits_per_word); + ret = spi_setup(spi); + if (ret) { + ts_err("failed set spi mode, %d", ret); + return ret; + } + + dev_res = kzalloc(sizeof(*dev_res), GFP_KERNEL); + if (!dev_res) + return -ENOMEM; + goodix_device_register(dev_res); + + /* get ic type */ + ret = goodix_get_ic_type(spi->dev.of_node, &dev_res->bus); + if (ret < 0) + return ret; + + dev_res->bus.bus_type = GOODIX_BUS_TYPE_SPI; + dev_res->bus.dev = &spi->dev; + if (dev_res->bus.ic_type == IC_TYPE_BERLIN_A) + dev_res->bus.read = goodix_spi_read_bra; + else + dev_res->bus.read = goodix_spi_read; + dev_res->bus.write = goodix_spi_write; + + // platform device init + dev_res->pdev.name = GOODIX_CORE_DRIVER_NAME; + dev_res->pdev.id = dev_res->id; + dev_res->pdev.num_resources = 0; + + /* register platform device, then the goodix_ts_core + * module will probe the touch deivce. + */ + ret = platform_device_register(&dev_res->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(dev_res); + ts_info("spi probe out, %d", ret); + return ret; +} + +#ifdef CONFIG_OF +static const struct of_device_id spi_matchs[] = { + {.compatible = "goodix,brl-a",}, + {.compatible = "goodix,brl-b",}, + {.compatible = "goodix,brl-d",}, + {.compatible = "goodix,nottingham",}, + {}, +}; +#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, +}; + +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); +} diff --git a/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_cfg_bin.c b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_cfg_bin.c new file mode 100644 index 000000000000..bf922eb42fb2 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_cfg_bin.c @@ -0,0 +1,348 @@ + /* + * 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(300); + } + if (retry < 0) { + ts_info("can't find config bin[%s]", cfg_name); + 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; + bool match_sensor_id = true; + 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 */ +refind_cfg: + memset(cd->ic_configs, 0, sizeof(cd->ic_configs)); + for (i = 0; i < cfg_bin->head.pkg_num; i++) { + cfg_pkg = &cfg_bin->cfg_pkgs[i]; + if (match_sensor_id && (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].len > 0) { + ts_err("found same type config twice for sensor id %d, skiped", + sensor_id); + continue; + } + 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); + } + + if (cd->ic_configs[CONFIG_TYPE_NORMAL].len <= 0) { + if (match_sensor_id) { + ts_info("no cfg match sensor_id[%d], don't match sensor_id, try again.", sensor_id); + match_sensor_id = false; + goto refind_cfg; + } else { + ts_err("can't find normal config"); + goto err_out; + } + } + + return 0; + +err_out: + 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_info("can't 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); +} + + diff --git a/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_ts_core.c b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_ts_core.c new file mode 100644 index 000000000000..d04bf1352619 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_ts_core.c @@ -0,0 +1,1986 @@ + /* + * 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 +#include +#include +#include "goodix_ts_core.h" + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) +#include +#define INPUT_TYPE_B_PROTOCOL +#endif + +/* goodix fb test */ +// #include "../../../video/fbdev/core/fb_firefly.h" + +#define GOODIX_DEFAULT_CFG_NAME "goodix_cfg_group.cfg" +struct goodix_device_manager goodix_devices; + +static void goodix_device_manager_init(void) +{ + if (goodix_devices.initilized) + return; + goodix_devices.initilized = true; + INIT_LIST_HEAD(&goodix_devices.list); +} + +static void goodix_device_manager_exit(void) +{ + struct goodix_device_resource *res, *next; + + if (!list_empty(&goodix_devices.list)) { + list_for_each_entry_safe(res, next, &goodix_devices.list, list) { + platform_device_unregister(&res->pdev); + kfree(res); + } + } + goodix_devices.initilized = false; +} + +int goodix_device_register(struct goodix_device_resource *device) +{ + if (!device) + return -ENXIO; + + list_add(&device->list, &goodix_devices.list); + device->id = goodix_devices.nums++; + sprintf(device->name, "%s.%d", GOODIX_CORE_DRIVER_NAME, device->id); + ts_info("goodix ts register device %s", device->name); + return 0; +} + +static int goodix_send_ic_config(struct goodix_ts_core *cd, int type); + +/* 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 *cd = dev_get_drvdata(dev); + + if (!buf || count <= 0) + return -EINVAL; + + if (buf[0] != '0') + goodix_ts_esd_on(cd); + else + goodix_ts_esd_off(cd); + 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; +} + +/* show die package site and mcu fabs */ +#define DIE_INFO_START_FLASH_ADDR 0x1F300 +static ssize_t die_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; + u8 temp_buf[21]; + u8 pkg_site; + u8 mcu_fab; + int ret; + + ret = hw_ops->read_flash(cd, DIE_INFO_START_FLASH_ADDR, temp_buf, sizeof(temp_buf)); + if (ret < 0) { + ts_err("read flash failed"); + return 0; + } + + ts_info("die info:%*ph", (int)sizeof(temp_buf), temp_buf); + + pkg_site = temp_buf[1]; + mcu_fab = temp_buf[20]; + ret = snprintf(buf, PAGE_SIZE, "package_id:0x%02X mcu_fab:0x%02X\n", pkg_site, mcu_fab); + + return ret; +} + +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 DEVICE_ATTR(die_info, 0440, + die_info_show, NULL); + +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, + &dev_attr_die_info.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; + int tx; + int rx; + int ret; + int i; + int index; + + if (!m || !v) + return -EIO; + + cd = m->private; + + 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) +{ + return single_open_size(file, rawdata_proc_show, + PDE_DATA(inode), PAGE_SIZE * 10); +} + +#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) +{ + int dev_id = core_data->pdev->id; + char proc_node[32] = {0}; + + sprintf(proc_node, "goodix_ts.%d", dev_id); + core_data->proc_dir_entry = proc_mkdir(proc_node, NULL); + if (!core_data->proc_dir_entry) + return; + + if (proc_create_data("tp_capacitance_data", + 0664, core_data->proc_dir_entry, + &rawdata_proc_fops, core_data) == NULL) { + ts_err("failed to create proc entry"); + } +} + +static void goodix_ts_procfs_exit(struct goodix_ts_core *core_data) +{ + int dev_id = core_data->pdev->id; + char proc_node[32] = {0}; + + sprintf(proc_node, "goodix_ts.%d", dev_id); + + remove_proc_entry("tp_capacitance_data", NULL); + remove_proc_entry(proc_node, NULL); +} + +#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; + } + + 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; + } + + 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; + } + + 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)) + strncpy(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)) + strncpy(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); + strncpy(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); + strncpy(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); + strncpy(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); + strncpy(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 sleep mode flag */ + board_data->sleep_enable = of_property_read_bool(node, + "goodix,sleep-enable"); + + /*get pen-enable switch and pen keys, must after "key map"*/ + board_data->pen_enable = of_property_read_bool(node, + "goodix,pen-enable"); + + ts_info("[DT]x:%d, y:%d, w:%d, p:%d sleep_enable:%d pen_enable:%d", + board_data->panel_max_x, board_data->panel_max_y, + board_data->panel_max_w, board_data->panel_max_p, + board_data->sleep_enable, board_data->pen_enable); + 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, pen_data->is_hover ? 0 : 1); + input_report_key(dev, BTN_TOOL_PEN, 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); + if (pen_data->coords.p == 0) + input_report_abs(dev, ABS_DISTANCE, 1); + else + input_report_abs(dev, ABS_DISTANCE, 0); + 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, BTN_TOOL_PEN, 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) +{ + 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); + 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); + } + } + + for (i = 0; i < GOODIX_MAX_KEY; i++) { + if (touch_data->keys[i].status == TS_TOUCH) + input_report_key(dev, touch_data->keys[i].code, 1); + else + input_report_key(dev, touch_data->keys[i].code, 0); + } + + 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_ts_event *ts_event = &core_data->ts_event; + struct goodix_ts_esd *ts_esd = &core_data->ts_esd; + int ret; + + disable_irq_nosync(core_data->irq); + ts_esd->irq_status = true; + core_data->irq_trig_cnt++; + + /* 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); + } + 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); + if (ts_event->event_type & EVENT_GESTURE) + goodix_ts_report_gesture(core_data, ts_event); + } + + enable_irq(core_data->irq); + 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 = &core_data->board_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_ts_threadirq_func, + 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 = &core_data->board_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; + + if (!cd->power_on) + return 0; + + ts_info("Device power off"); + 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 = &core_data->board_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 = &core_data->board_data; + struct input_dev *input_dev = NULL; + int dev_id = core_data->pdev->id; + 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); + + sprintf(core_data->input_name, "%s.%d", GOODIX_CORE_DRIVER_NAME, dev_id); + + input_dev->name = core_data->input_name; + input_dev->id.bustype = core_data->bus->bus_type; + input_dev->id.product = 0x0100 + dev_id; + input_dev->id.vendor = 0x27C6; + input_dev->id.version = 0x0100; + + 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 - 1, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, ts_bdata->panel_max_y - 1, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, ts_bdata->panel_max_w - 1, 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 = &core_data->board_data; + struct input_dev *pen_dev = NULL; + int dev_id = core_data->pdev->id; + 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); + + sprintf(core_data->input_pen_name, "%s.%d,%s", GOODIX_CORE_DRIVER_NAME, dev_id, "pen"); + pen_dev->name = core_data->input_pen_name; + pen_dev->id.bustype = core_data->bus->bus_type; + pen_dev->id.product = 0x0200 + dev_id; + pen_dev->id.vendor = 0x27C6; + pen_dev->id.version = 0x0100; + + 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 - 1, 0, 0); + input_set_abs_params(pen_dev, ABS_Y, 0, ts_bdata->panel_max_y - 1, 0, 0); + input_set_abs_params(pen_dev, ABS_PRESSURE, 0, + ts_bdata->panel_max_p - 1, 0, 0); + input_set_abs_params(pen_dev, ABS_DISTANCE, 0, 255, 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 + */ +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 + */ +void goodix_ts_esd_off(struct goodix_ts_core *cd) +{ + struct goodix_ts_esd *ts_esd = &cd->ts_esd; + + if (!atomic_read(&ts_esd->esd_on)) + return; + + atomic_set(&ts_esd->esd_on, 0); + cancel_delayed_work_sync(&ts_esd->esd_work); + ts_info("Esd off"); +} + +/** + * 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); + goodix_ts_esd_on(cd); + + return 0; +} + +static void goodix_ts_release_connects(struct goodix_ts_core *core_data) +{ + struct input_dev *input_dev = core_data->input_dev; + struct input_dev *pen_dev = core_data->pen_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); + + if (core_data->board_data.pen_enable) { + mutex_lock(&pen_dev->mutex); + input_report_key(pen_dev, BTN_TOUCH, 0); + input_report_key(pen_dev, BTN_TOOL_PEN, 0); + input_sync(pen_dev); + mutex_unlock(&pen_dev->mutex); + } + + if (core_data->gesture_type) + core_data->hw_ops->after_event_handler(core_data); +} + +/** + * 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_ts_hw_ops *hw_ops = core_data->hw_ops; + + 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); + goodix_ts_esd_off(core_data); + + if (core_data->gesture_type) { + /* enter gesture mode */ + ts_info("enter gesture mode[0x%x]", core_data->gesture_type); + // TODO: send 0 means enable all gesture type + hw_ops->gesture(core_data, 0); + hw_ops->irq_enable(core_data, true); + enable_irq_wake(core_data->irq); + } else { + /* enter sleep mode or power off */ + if (core_data->board_data.sleep_enable) + hw_ops->suspend(core_data); + else + goodix_ts_power_off(core_data); + } + + 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_ts_hw_ops *hw_ops = core_data->hw_ops; + + 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); + + if (core_data->gesture_type) { + disable_irq_wake(core_data->irq); + hw_ops->resume(core_data); + } else { + /* reset device or power on*/ + if (core_data->board_data.sleep_enable) + hw_ops->resume(core_data); + else + goodix_ts_power_on(core_data); + } + + /* enable irq */ + hw_ops->irq_enable(core_data, true); + /* open esd */ + goodix_ts_esd_on(core_data); + ts_info("Resume end"); + return 0; +} + +static void goodix_ts_resume_work(struct work_struct *work) +{ + struct goodix_ts_core *cd = + container_of(work, struct goodix_ts_core, resume_work); + goodix_ts_resume(cd); +} + +#if 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; + int *blank; + + blank = fb_event->data; + if (fb_event && fb_event->data && core_data) { + if (event == FB_EVENT_BLANK) { + if (*blank == FB_BLANK_UNBLANK) { + schedule_work(&core_data->resume_work); + } else if (*blank == FB_BLANK_POWERDOWN) { + cancel_work_sync(&core_data->resume_work); + goodix_ts_suspend(core_data); + } + } + } + + return 0; +} +#elif IS_ENABLED(CONFIG_PM) +/** + * 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); +} +static const struct dev_pm_ops dev_pm_ops = { + .suspend = goodix_ts_pm_suspend, + .resume = goodix_ts_pm_resume, +}; +#endif + +static void goodix_self_check(struct work_struct *work) +{ + struct goodix_ts_core *cd = + container_of(work, struct goodix_ts_core, self_check_work); + u32 fw_state_addr = cd->ic_info.misc.fw_state_addr; + int update_flag = UPDATE_MODE_BLOCK | UPDATE_MODE_SRC_REQUEST | UPDATE_MODE_FORCE; + u8 cur_cycle_cnt = 0; + u8 pre_cycle_cnt = 0; + int err_cnt = 0; + int retry = 5; + + while (retry--) { + cd->hw_ops->read(cd, fw_state_addr, &cur_cycle_cnt, 1); + if (cur_cycle_cnt == pre_cycle_cnt) + err_cnt++; + pre_cycle_cnt = cur_cycle_cnt; + msleep(20); + } + if (err_cnt > 1) { + ts_err("Warning! The firmware maybe running abnormal, need upgrade."); + goodix_do_fw_update(cd, update_flag); + } +} + +int goodix_ts_stage2_init(struct goodix_ts_core *cd) +{ + int ret; + + /* 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; + } + } + /* 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 IS_ENABLED(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 + /* create sysfs files */ + goodix_ts_sysfs_init(cd); + + /* create procfs files */ + goodix_ts_procfs_init(cd); + + /* esd protector */ + goodix_ts_esd_init(cd); + + /* gesture init */ + gesture_module_init(cd); + + /* inspect init */ + inspect_module_init(cd); + + INIT_WORK(&cd->resume_work, goodix_ts_resume_work); + + /* Do self check on first boot */ + INIT_WORK(&cd->self_check_work, goodix_self_check); + schedule_work(&cd->self_check_work); + + 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->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; + 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; + + /* step 1: read version */ + ret = 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; + } + + /* step 2: read ic info */ + ret = hw_ops->get_ic_info(cd, &cd->ic_info); + if (ret < 0) { + ts_err("failed to get ic info, try to upgrade"); + update_flag |= UPDATE_MODE_FORCE; + } + + /* step 3: do upgrade */ + ts_info("update flag: 0x%X", update_flag); + goodix_do_fw_update(cd, update_flag); + + print_ic_info(&cd->ic_info); + + /* 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); + + /* init other resources */ + ret = goodix_ts_stage2_init(cd); + if (ret) { + ts_err("stage2 init failed"); + goto err_out; + } + + cd->init_stage = CORE_INIT_STAGE2; + return 0; +err_out: + cd->init_stage = CORE_INIT_FAIL; + 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; +} + +/* goodix fb test */ +// static void test_suspend(void *data) +// { +// struct goodix_ts_core *cd = data; +// cancel_work_sync(&cd->resume_work); +// goodix_ts_suspend(cd); +// } + +// static void test_resume(void *data) +// { +// struct goodix_ts_core *cd = data; +// schedule_work(&cd->resume_work); +// } + +/** + * goodix_ts_probe - called by kernel when Goodix touch + * platform driver is added. + */ +static int goodix_ts_probe(struct platform_device *pdev) +{ + struct goodix_device_resource *dev_res = + container_of(pdev, struct goodix_device_resource, pdev); + struct goodix_ts_core *core_data; + struct goodix_bus_interface *bus_interface; + int ret; + + ts_info("IN"); + core_data = &dev_res->core_data; + bus_interface = &dev_res->bus; + + if (IS_ENABLED(CONFIG_OF) && bus_interface->dev->of_node) { + /* parse devicetree property */ + ret = goodix_parse_dt(bus_interface->dev->of_node, + &core_data->board_data); + if (ret) { + ts_err("failed parse device info form dts, %d", ret); + return -EINVAL; + } + } else { + ts_err("no valid device tree node found"); + return -ENODEV; + } + + core_data->hw_ops = goodix_get_hw_ops(); + if (!core_data->hw_ops) { + ts_err("hw ops is NULL"); + return -EINVAL; + } + + /* touch core layer is a platform driver */ + core_data->pdev = pdev; + core_data->bus = bus_interface; + platform_set_drvdata(pdev, core_data); + + /* get GPIO resource */ + 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; + } + + /* update modue init */ + ret = goodix_fw_update_init(core_data); + if (ret) { + ts_err("firmware update module init failed"); + goto err_out; + } + + /* debug node init */ + ret = goodix_tools_init(core_data); + if (ret) { + ts_err("debug tools init failed"); + goto err_out; + } + + /* goodix fb test */ + // fb_firefly_register(test_suspend, test_resume, core_data); + + core_data->init_stage = CORE_INIT_STAGE1; + + /* Try start a thread to get config-bin info */ + goodix_start_later_init(core_data); + + mutex_init(&core_data->cmd_lock); + ts_info("goodix_ts_core probe success"); + return 0; + +err_out: + goodix_fw_update_uninit(core_data); + goodix_ts_power_off(core_data); + core_data->init_stage = CORE_INIT_FAIL; + 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_tools_exit(core_data); + goodix_fw_update_uninit(core_data); + + if (core_data->init_stage >= CORE_INIT_STAGE2) { + gesture_module_exit(core_data); + inspect_module_exit(core_data); + hw_ops->irq_enable(core_data, false); + #if IS_ENABLED(CONFIG_FB) + fb_unregister_client(&core_data->fb_notifier); + #endif + if (atomic_read(&ts_esd->esd_on)) + goodix_ts_esd_off(core_data); + + 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; +} + +static const struct platform_device_id ts_core_ids[] = { + {.name = GOODIX_CORE_DRIVER_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 IS_ENABLED(CONFIG_PM) && !IS_ENABLED(CONFIG_FB) + .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; + + ts_info("Core layer init:%s", GOODIX_DRIVER_VERSION); + goodix_device_manager_init(); +#ifdef CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI + ret = goodix_spi_bus_init(); + if (ret < 0) { + ts_err("failed to add spi bus driver"); + return ret; + } +#endif +#ifdef CONFIG_TOUCHSCREEN_GOODIX_BRL_I2C + ret = goodix_i2c_bus_init(); + if (ret < 0) { + ts_err("failed to add i2c bus driver"); + return ret; + } +#endif + 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); +#ifdef CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI + goodix_spi_bus_exit(); +#endif +#ifdef CONFIG_TOUCHSCREEN_GOODIX_BRL_I2C + goodix_i2c_bus_exit(); +#endif + goodix_device_manager_exit(); +} + +late_initcall(goodix_ts_core_init); +module_exit(goodix_ts_core_exit); + +MODULE_DESCRIPTION("Goodix Touchscreen Driver Module"); +MODULE_AUTHOR("Goodix, Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_ts_core.h b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_ts_core.h new file mode 100644 index 000000000000..faafcca88bfc --- /dev/null +++ b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_ts_core.h @@ -0,0 +1,817 @@ +/* + * 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. + */ +#ifndef _GOODIX_TS_CORE_H_ +#define _GOODIX_TS_CORE_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if IS_ENABLED(CONFIG_OF) +#include +#include +#endif +#if IS_ENABLED(CONFIG_FB) +#include +#include +#endif + +#define GOODIX_CORE_DRIVER_NAME "goodix_ts" +#define GOODIX_PEN_DRIVER_NAME "goodix_ts,pen" +#define GOODIX_DRIVER_VERSION "v1.3.13" +#define GOODIX_MAX_TOUCH 10 +#define GOODIX_MAX_KEY 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 2000 +#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" + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0)) +#define PDE_DATA(x) pde_data(x) +#endif + +enum GOODIX_GESTURE_TYP { + GESTURE_C = (1 << 0), + GESTURE_E = (1 << 1), + GESTURE_F = (1 << 2), + GESTURE_O = (1 << 3), + GESTURE_M = (1 << 4), + GESTURE_W = (1 << 5), + GESTURE_DOUBLE_TAP = (1 << 7), + GESTURE_BA = (1 << 8), + GESTURE_AB = (1 << 9), + GESTURE_AA = (1 << 10), + GESTURE_BB = (1 << 11), + GESTURE_SINGLE_TAP = (1 << 12), + GESTURE_FOD_PRESS = (1 << 13) +}; + +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) +}; + +/* MAIN-ID */ +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, + IC_TYPE_NOTTINGHAM +}; + +/* SUB-ID + * sub type of berlinB serial IC. + * for convenience we put the MAIN-ID on the hith bits, + * hith 8 bits is MAIN-ID, low 8 bits is MIN-ID + */ +enum BERLIN_B_SUB_ID { + IC_TYPE_SUB_B2 = (IC_TYPE_BERLIN_B << 8) | 0x2, +}; + + +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, +}; + +#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 flash_head { + uint32_t checksum; + uint32_t address; + uint32_t length; +}; + +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 screen_real_max_x; + u16 screen_real_max_y; + 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_rawdata_len; + u32 noise_rawdata_addr; + u16 noise_rawdata_len; + u32 stylus_rawdata_addr; + u16 stylus_rawdata_len; + u32 noise_data_addr; + u32 esd_addr; + u32 auto_scan_cmd_addr; + u32 auto_scan_info_addr; +}; + +struct goodix_ic_info_other { + u16 normalize_k_version; + u32 irrigation_data_addr; + u32 algo_debug_data_addr; + u16 algo_debug_data_len; + u32 update_sync_data_addr; + u16 screen_max_x; + u16 screen_max_y; +}; + +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; + struct goodix_ic_info_other other; +}; + +// goodix_ic_info V2 +struct goodix_ic_info_sub_version { + u16 length; + u8 ic_die_id; + u8 ic_version_id; + u8 frame_data_customer_id; + u8 frame_data_version_id; + u8 touch_data_customer_id; + u8 touch_data_version_id; + u16 normalize_k_version; + u32 algorithm_version; + u32 short_test_version; + u32 lsp_version; + u32 config_id; + u8 config_version; +}; + +struct goodix_ic_info_sub_sample { + u16 length; + u16 screen_real_max_x; + u16 screen_real_max_y; + u16 screen_max_x; + u16 screen_max_y; + u8 stylus_feature; + union { + u8 freqhop_feature:1; + u8 calibration_feature:1; + u8 gesture_feature:1; + u8 stylus_freqhop_feature:1; + }; + 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]; + u8 stylus_tx2_freq_num; + u16 stylus_tx2_freq[MAX_FREQ_NUM_STYLUS]; +}; + +struct goodix_ic_info_sub_address { + u16 length; + u32 cmd_addr; + u16 cmd_max_len; + u32 cmd_reply_addr; + u16 cmd_reply_len; + u32 fw_state_addr; + u8 fw_state_len; + u8 cmd_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; + u16 im_struct_len; + u32 touch_data_addr; + u16 touch_data_head_len; + u16 point_struct_len; + 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_rawdata_len; + u32 noise_rawdata_addr; + u16 noise_rawdata_len; + u32 stylus_rawdata_addr; + u16 stylus_rawdata_len; + u32 noise_data_addr; + u32 esd_addr; + u32 auto_scan_cmd_addr; + u32 auto_scan_info_addr; + u32 irrigation_data_addr; + u32 algo_debug_data_addr; + u16 algo_debug_data_len; + u32 update_sync_data_addr; +}; + +struct goodix_ic_info_sub_customer { + u16 length; + u32 customer; +}; + +struct goodix_ic_info_v2 { + u16 length; + u8 info_customer_id; + u8 info_version_id; + struct goodix_ic_info_sub_version version; + struct goodix_ic_info_sub_sample sample; + struct goodix_ic_info_sub_address address; + struct goodix_ic_info_sub_customer customer; +}; +#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 + * @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 pen_enable; + bool sleep_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_point_type { + POINT_TYPE_NULL = 0, + POINT_TYPE_STYLUS_HOVER = 1, + POINT_TYPE_FINGER = 2, + POINT_TYPE_STYLUS = 3, + POINT_TYPE_GLOVE = 4, + POINT_TYPE_KEY = 5, +}; + +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; +}; + +struct goodix_ts_key { + int status; + int code; +}; + +/* touch event data */ +struct goodix_touch_data { + int touch_num; + struct goodix_ts_key keys[GOODIX_MAX_KEY]; + struct goodix_ts_coords coords[GOODIX_MAX_TOUCH]; +}; + +struct goodix_pen_data { + bool is_hover; + 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 { + u8 event_type; + u8 fp_flag; /* finger print DOWN flag */ + 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_NONE, + GOODIX_BUS_TYPE_I2C, + GOODIX_BUS_TYPE_SPI, + GOODIX_BUS_TYPE_I3C, +}; + +struct goodix_bus_interface { + int bus_type; + int ic_type; + int sub_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 (*read_flash)(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 goodix_ts_core *ts_core; +}; + +enum goodix_core_init_stage { + CORE_UNINIT, + CORE_INIT_FAIL, + CORE_INIT_STAGE1, + CORE_INIT_STAGE2 +}; + +enum update_status { + UPSTA_NOTWORK = 0, + UPSTA_PREPARING, + UPSTA_UPDATING, + UPSTA_SUCCESS, + UPSTA_FAILED +}; + +struct fw_subsys_info { + u8 type; + u32 size; + u32 flash_addr; + const u8 *data; +}; + +#pragma pack(1) +struct firmware_summary { + u32 size; + u32 checksum; + u8 hw_pid[6]; + u8 hw_vid[3]; + u8 fw_pid[8]; + u8 fw_vid[4]; + u8 subsys_num; + u8 chip_type; + u8 protocol_ver; + u8 bus_type; + u8 flash_protect; + // u8 reserved[8]; + struct fw_subsys_info subsys[47]; +}; +#pragma pack() + +struct firmware_data { + struct firmware_summary fw_summary; + const struct firmware *firmware; +}; + +struct goodix_ic_config { + int len; + u8 data[GOODIX_CFG_MAX_SIZE]; +}; + +struct fw_update_ctrl { + bool initialized; + struct mutex mutex; + char fw_name[GOODIX_MAX_STR_LABLE_LEN]; + u32 cfg_id; + 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 kobject *kobj; +}; + +struct goodix_tools_dev { + struct miscdevice miscdev; + char name[32]; +}; + +struct goodix_ts_core { + int init_stage; + struct goodix_tools_dev tools_dev; + struct fw_update_ctrl update_ctrl; + struct kobject *gesture_kobj; + struct proc_dir_entry *proc_dir_entry; + struct platform_device *pdev; + struct goodix_bus_interface *bus; + struct goodix_fw_version fw_version; + struct goodix_ic_info ic_info; + struct goodix_ic_info_v2 ic_info_v2; + struct goodix_ts_board_data board_data; + struct goodix_ts_hw_ops *hw_ops; + struct input_dev *input_dev; + struct input_dev *pen_dev; + struct mutex cmd_lock; + /* TODO counld we remove this from core data? */ + struct goodix_ts_event ts_event; + + struct work_struct resume_work; + struct work_struct self_check_work; + + /* 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; + u32 gesture_type; + + char input_name[32]; + char input_pen_name[32]; + + 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 goodix_ts_esd ts_esd; + +#if IS_ENABLED(CONFIG_FB) + struct notifier_block fb_notifier; +#endif +}; + +struct goodix_device_resource { + struct list_head list; + int id; + char name[64]; + struct goodix_bus_interface bus; + struct platform_device pdev; + struct goodix_ts_core core_data; +}; + +struct goodix_device_manager { + struct list_head list; + bool initilized; + int nums; +}; + +extern struct goodix_device_manager goodix_devices; +extern int goodix_device_register(struct goodix_device_resource *device); + +/* log macro */ +extern bool debug_log_flag; +#define ts_info(fmt, arg...) \ + pr_info("[GTP-INF][%s] "fmt"\n", __func__, ##arg) +#define ts_err(fmt, arg...) \ + pr_err("[GTP-ERR][%s] "fmt"\n", __func__, ##arg) +#define ts_debug(fmt, arg...) \ + {if (debug_log_flag) \ + pr_info("[GTP-DBG][%s] "fmt"\n", __func__, ##arg);} + +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); +void print_ic_info(struct goodix_ic_info *ic_info); + +int goodix_fw_update_init(struct goodix_ts_core *core_data); +void goodix_fw_update_uninit(struct goodix_ts_core *core_data); +int goodix_do_fw_update(struct goodix_ts_core *core_data, int mode); +int goodix_get_ic_type(struct device_node *node, struct goodix_bus_interface *bus_inf); +int gesture_module_init(struct goodix_ts_core *core_data); +void gesture_module_exit(struct goodix_ts_core *core_data); +int inspect_module_init(struct goodix_ts_core *core_data); +void inspect_module_exit(struct goodix_ts_core *core_data); +int goodix_tools_init(struct goodix_ts_core *core_data); +void goodix_tools_exit(struct goodix_ts_core *core_data); +void goodix_ts_esd_on(struct goodix_ts_core *cd); +void goodix_ts_esd_off(struct goodix_ts_core *cd); + +int goodix_ts_report_gesture(struct goodix_ts_core *cd, struct goodix_ts_event *event); + +#endif diff --git a/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_ts_gesture.c b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_ts_gesture.c new file mode 100644 index 000000000000..d8312aa9c52a --- /dev/null +++ b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_ts_gesture.c @@ -0,0 +1,251 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 + +static ssize_t gsx_double_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct device *device = + container_of(((struct kobject *)dev)->parent, struct device, kobj); + struct goodix_ts_core *cd = dev_get_drvdata(device); + uint32_t type = cd->gesture_type; + + return sprintf(buf, "%s\n", + (type & GESTURE_DOUBLE_TAP) ? "enable" : "disable"); +} + +static ssize_t gsx_double_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct device *device = + container_of(((struct kobject *)dev)->parent, struct device, kobj); + struct goodix_ts_core *cd = dev_get_drvdata(device); + + if (buf[0] == '1' || buf[0] == 1) { + ts_info("enable double tap"); + cd->gesture_type |= GESTURE_DOUBLE_TAP; + } else if (buf[0] == '0' || buf[0] == 0) { + ts_info("disable double tap"); + cd->gesture_type &= ~GESTURE_DOUBLE_TAP; + } else + ts_err("invalid cmd[%d]", buf[0]); + + return count; +} + +static ssize_t gsx_single_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct device *device = + container_of(((struct kobject *)dev)->parent, struct device, kobj); + struct goodix_ts_core *cd = dev_get_drvdata(device); + uint32_t type = cd->gesture_type; + + return sprintf(buf, "%s\n", + (type & GESTURE_SINGLE_TAP) ? "enable" : "disable"); +} + +static ssize_t gsx_single_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct device *device = + container_of(((struct kobject *)dev)->parent, struct device, kobj); + struct goodix_ts_core *cd = dev_get_drvdata(device); + + if (buf[0] == '1' || buf[0] == 1) { + ts_info("enable single tap"); + cd->gesture_type |= GESTURE_SINGLE_TAP; + } else if (buf[0] == '0' || buf[0] == 0) { + ts_info("disable single tap"); + cd->gesture_type &= ~GESTURE_SINGLE_TAP; + } else + ts_err("invalid cmd[%d]", buf[0]); + + return count; +} + +static ssize_t gsx_fod_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct device *device = + container_of(((struct kobject *)dev)->parent, struct device, kobj); + struct goodix_ts_core *cd = dev_get_drvdata(device); + uint32_t type = cd->gesture_type; + + return sprintf(buf, "%s\n", + (type & GESTURE_FOD_PRESS) ? "enable" : "disable"); +} + +static ssize_t gsx_fod_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct device *device = + container_of(((struct kobject *)dev)->parent, struct device, kobj); + struct goodix_ts_core *cd = dev_get_drvdata(device); + + if (buf[0] == '1' || buf[0] == 1) { + ts_info("enable fod"); + cd->gesture_type |= GESTURE_FOD_PRESS; + } else if (buf[0] == '0' || buf[0] == 0) { + ts_info("disable fod"); + cd->gesture_type &= ~GESTURE_FOD_PRESS; + } else + ts_err("invalid cmd[%d]", buf[0]); + + return count; +} + +int goodix_ts_report_gesture(struct goodix_ts_core *cd, struct goodix_ts_event *event) +{ + int fodx, fody, overlay_area; + + switch (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 *)event->gesture_data); + fody = le16_to_cpup((__le16 *)(event->gesture_data + 2)); + overlay_area = 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]", event->gesture_type); + break; + } + + return 0; +} + +static DEVICE_ATTR(double_type, 0664, gsx_double_type_show, gsx_double_type_store); +static DEVICE_ATTR(single_type, 0664, gsx_single_type_show, gsx_single_type_store); +static DEVICE_ATTR(fod_type, 0664, gsx_fod_type_show, gsx_fod_type_store); + +static struct attribute *gesture_attrs[] = { + &dev_attr_double_type.attr, + &dev_attr_single_type.attr, + &dev_attr_fod_type.attr, + NULL, +}; + +const static struct attribute_group gesture_sysfs_group = { + .attrs = gesture_attrs, +}; + +int gesture_module_init(struct goodix_ts_core *core_data) +{ + int ret = 0; + struct kobject *parent = &core_data->pdev->dev.kobj; + + /* gesture sysfs init */ + core_data->gesture_kobj = kobject_create_and_add("gesture", parent); + if (!core_data->gesture_kobj) { + ts_err("failed create gesture sysfs node!"); + goto err_out; + } + + ret = sysfs_create_group(core_data->gesture_kobj, &gesture_sysfs_group); + if (ret) { + ts_err("failed create gesture sysfs files"); + kobject_put(core_data->gesture_kobj); + goto err_out; + } + + ts_info("gesture module init success"); + return 0; + +err_out: + ts_err("gesture module init failed!"); + return ret; +} + +void gesture_module_exit(struct goodix_ts_core *core_data) +{ + ts_info("gesture module exit"); + + sysfs_remove_group(core_data->gesture_kobj, &gesture_sysfs_group); + kobject_put(core_data->gesture_kobj); +} diff --git a/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_ts_inspect.c b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_ts_inspect.c new file mode 100644 index 000000000000..26f0c9f41357 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_ts_inspect.c @@ -0,0 +1,2770 @@ + /* + * 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 +#include +#include +#include +#include +#include +#include + + +/* test config */ +#define TOTAL_FRAME_NUM 16 /* rawdata test frames */ +#define NOISEDATA_TEST_TIMES 1 /* noise test frames */ + +#define DISCARD_FRAMES 3 + +#define GOODIX_TEST_FILE_NAME "goodix_test_limits" +#define MAX_PROC_OUTPUT_SIZE 2 * 1024 * 1024 +#define MAX_DATA_BUFFER 30000 +#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 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 + +/* nottingham */ +#define MAX_DRV_NUM_NOT 17 +#define MAX_SEN_NUM_NOT 35 +#define SHORT_TEST_TIME_REG_NOT 0x1479E +#define SHORT_TEST_STATUS_REG_NOT 0x13400 +#define SHORT_TEST_RESULT_REG_NOT 0x13408 +#define DRV_DRV_SELFCODE_REG_NOT 0x13446 +#define SEN_SEN_SELFCODE_REG_NOT 0x136EE +#define DRV_SEN_SELFCODE_REG_NOT 0x14152 +#define DIFF_CODE_DATA_REG_NOT 0x14734 + + +#define ABS(val) ((val < 0)? -(val) : val) +#define MAX(a, b) ((a > b)? a : b) + +enum GTP_TEST_ITEMS { + GTP_CAP_TEST = 0, + GTP_DELTA_TEST, + GTP_NOISE_TEST, + GTP_SHORT_TEST, + GTP_SELFCAP_TEST, + GTP_SELFNOISE_TEST, + GTP_CUSTOM_INFO_TEST, + GTP_CHIP_KEY_INFO_TEST, + GTP_VERSION_TEST, + MAX_TEST_ITEMS = 20 +}; + +enum GTP_TEST_RESULTS { + GTP_TEST_SOFT_ERR = 0, + GTP_TEST_PANEL_ERR, + GTP_TEST_OK +}; + +enum GTP_MUTUAL_DATA_TYPE { + GTP_MUTUAL_RAW, + GTP_MUTUAL_DIFF, + GTP_SELF_RAW, + GTP_SELF_DIFF +}; + +static int output_offset; +static char *output_buf; + +/* 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, +}; + +/* nottingham drv-sen map */ +static u8 not_drv_map[] = { + 35, 36, 37, 38, 39, 40, 41, 42, 43, + 44, 45, 46, 47, 48, 49, 50, 51 +}; + +static u8 not_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 +}; + +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; + +#define CHIP_KEY_INFO_MAX_LEN 0x40 +#define CHIP_UID_MAX_LEN 16 +typedef struct { + u8 hw_ver; + u8 package_site; + u8 chip_uid[CHIP_UID_MAX_LEN]; + u8 ft_test_result; + u8 substrate_tech; + u8 mcu_fab; + u8 res[40]; + u8 vaild_len; + u16 check_sum; +} chip_attr_info_t; + +//test result +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 params_info_t params_not = { + MAX_DRV_NUM_NOT, + MAX_SEN_NUM_NOT, + not_drv_map, + not_sen_map, + SHORT_TEST_TIME_REG_NOT, + SHORT_TEST_STATUS_REG_NOT, + SHORT_TEST_RESULT_REG_NOT, + DRV_DRV_SELFCODE_REG_NOT, + SEN_SEN_SELFCODE_REG_NOT, + DRV_SEN_SELFCODE_REG_NOT, + DIFF_CODE_DATA_REG_NOT, + 0, + 0, + 0, +}; + +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; + char cur_version[64]; + char chip_pid[8]; + chip_attr_info_t chip_info; + char custom_info_flash[16]; + char custom_info_sram[16]; + + char test_result[MAX_TEST_ITEMS]; +}; + +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 if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D) + return (v1 / v2 - 1) * 70 + 59; + else + return (v1 / v2 - 1) * 55 + 45; +} + +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 if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D) + return 64 * (2 * v2 - 25) * 93 / v1 - 20; + else + return 64 * (2 * v2 - 25) * 76 / v1 - 15; +} + +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 if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D) + return 145000 / v - 15; + else + return 120000 / v - 16; +} + +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->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 proc_append_string(char *format_str, ...) +{ + va_list args; + + va_start(args, format_str); + output_offset += vsprintf(output_buf + output_offset, format_str, args); + va_end(args); +} + +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*)vzalloc(len + 5); + if (buf == NULL){ + ts_err("get space alloc 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); + vfree(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 *)vzalloc(MAX_LINE_LEN); + 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)) { + vfree(row_data); + return -EINVAL; + } + data[j++] = (s32)temp_val; + } + if (i == rows - 1) + break; + goto_next_line(&ptr); //next row + if(!ptr || (0 == strlen(ptr)) || (ptr >= (buf_start + buf_size))) { + ts_info("invalid ptr, return"); + vfree(row_data); + row_data = NULL; + return -EPERM; + } + } + vfree(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 || (0 == strlen(ptr))) { + 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 = ¶ms_bra; + else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_B) + test_params->params_info = ¶ms_brb; + else if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D) + test_params->params_info = ¶ms_brd; + else if (ts_test->ts->bus->ic_type == IC_TYPE_NOTTINGHAM) + test_params->params_info = ¶ms_not; +} + +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 *cd = ts_test->ts; + const struct firmware *firmware = NULL; + struct device *dev = &cd->pdev->dev; + char limit_file[100] = {0}; + u32 tx = test_params->drv_num; + u32 rx = test_params->sen_num; + + sprintf(limit_file, "%s_%d.csv", GOODIX_TEST_FILE_NAME, + cd->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->size <= 0) { + ts_err("request_firmware, limits param length error,len:%zu", + firmware->size); + ret = -EINVAL; + goto exit_free; + } + temp_buf = vzalloc(firmware->size + 1); + if (!temp_buf) { + ts_err("alloc bytes failed."); + ret = -ENOMEM; + goto exit_free; + } + memcpy(temp_buf, firmware->data, firmware->size); + + test_params->test_items[GTP_VERSION_TEST] = true; + test_params->test_items[GTP_CHIP_KEY_INFO_TEST] = true; + test_params->test_items[GTP_CUSTOM_INFO_TEST] = true; + test_params->test_items[GTP_CAP_TEST] = true; + test_params->test_items[GTP_DELTA_TEST] = true; + + /* 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); + ts_test->test_config.len = cd->hw_ops->read_config(cd, ts_test->test_config.data, GOODIX_CFG_MAX_SIZE); + } 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: + vfree(temp_buf); + if (firmware) + 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_esd_off(ts_test->ts); + + /* 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_esd_on(ts_test->ts); + 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_esd_on(ts_test->ts); + /* enable irq */ + ts_test_irq_enable(ts_test, true); +} + +#define SHORT_TEST_RUN_REG 0x10400 +#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; + + ts_info("short test prepare IN"); + ts_test->test_result[GTP_SHORT_TEST] = GTP_TEST_SOFT_ERR; + 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); + 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; + else + ts_info("patch ID dismatch %s != %s", fw_ver.patch_pid, TEST_FW_PID); + } + + 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; + } + + msleep(500); //BerlinB quick check need to delay at least 500ms + + /* 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 */ + if (test_time > 0) + 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_TEST_PANEL_ERR; + else + ts_test->test_result[GTP_SHORT_TEST] = GTP_TEST_OK; +} + +#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"); + + /* switch rawdata mode */ + if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D || + ts_test->ts->bus->ic_type == IC_TYPE_NOTTINGHAM) { + 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; +} + +#define FRAME_DATA_MAX_SIZE 2500 +static int goodix_poll_rawdata(struct goodix_ts_test *ts_test, u8 data_type, u8 *data, u32 data_num) +{ + struct goodix_ts_core *cd = ts_test->ts; + u32 flag_addr = ts_test->ts->ic_info.misc.touch_data_addr; + u32 data_addr; + u8 *frame_buf; + int offset = 0; + int frame_len; + u8 val; + int ret; + int retry; + + if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D || + ts_test->ts->bus->ic_type == IC_TYPE_NOTTINGHAM) + flag_addr = ts_test->ts->ic_info.misc.frame_data_addr; + + if (data_type == GTP_MUTUAL_RAW) { + data_addr = ts_test->test_params.rawdata_addr; + } else if (data_type == GTP_MUTUAL_DIFF) { + data_addr = ts_test->test_params.noisedata_addr; + } else if (data_type == GTP_SELF_RAW) { + data_addr = ts_test->test_params.self_rawdata_addr; + } else { + data_addr = ts_test->test_params.self_noisedata_addr; + } + + val = 0; + ret = ts_test_write(ts_test, flag_addr, &val, 1); + if (ret < 0) + return ret; + 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", val); + return -EAGAIN; + } + + frame_buf = kzalloc(FRAME_DATA_MAX_SIZE, GFP_KERNEL); + if (cd->bus->ic_type == IC_TYPE_BERLIN_D || + cd->bus->ic_type == IC_TYPE_NOTTINGHAM) { + ret = ts_test_read(ts_test, flag_addr, frame_buf, FRAME_DATA_MAX_SIZE); + if (ret < 0) + goto free_exit; + + if (checksum_cmp(frame_buf, cd->ic_info.misc.frame_data_head_len, CHECKSUM_MODE_U8_LE)) { + ts_err("frame head checksum error"); + ret = -EAGAIN; + goto free_exit; + } + + frame_len = le16_to_cpup((__le16 *)&frame_buf[3]); + if (checksum_cmp(frame_buf, frame_len, CHECKSUM_MODE_U16_LE)) { + ts_err("frame body checksum error"); + ret = -EAGAIN; + goto free_exit; + } + + offset += cd->ic_info.misc.frame_data_head_len; + offset += cd->ic_info.misc.fw_attr_len; + offset += cd->ic_info.misc.fw_log_len; + if (data_type == GTP_MUTUAL_RAW || data_type == GTP_MUTUAL_DIFF) { + offset += 8; + } else { + offset += cd->ic_info.misc.mutual_struct_len; + offset += 10; + } + memcpy(data, frame_buf + offset, data_num * 2); + } else { + ret = ts_test_read(ts_test, data_addr, data, data_num * 2); + } + +free_exit: + kfree(frame_buf); + return ret; +} + +static int goodix_cache_rawdata(struct goodix_ts_test *ts_test) +{ + int ret; + int i,j; + u8 val; + 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 flag_addr = ts_test->ts->ic_info.misc.touch_data_addr; + + if (ts_test->ts->bus->ic_type == IC_TYPE_BERLIN_D || + ts_test->ts->bus->ic_type == IC_TYPE_NOTTINGHAM) + flag_addr = ts_test->ts->ic_info.misc.frame_data_addr; + + for (i = 0; i < DISCARD_FRAMES; i++) { + val = 0; + ts_test_write(ts_test, flag_addr, &val, 1); + msleep(20); + } + + for (i = 0; i < TOTAL_FRAME_NUM; i++) { + for (j = 0; j < GOODIX_RETRY_3; j++) { + ret = goodix_poll_rawdata(ts_test, + GTP_MUTUAL_RAW, (u8 *)ts_test->rawdata[i].data, data_size); + if (ret == 0) { + ts_test->rawdata[i].size = data_size; + goodix_rotate_abcd2cbad(drv_num, sen_num, ts_test->rawdata[i].data); + break; + } + } + } + + 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 % 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); + } + temp = max_val * 1000 / raw; + ts_test->accord_arr[i].data[j] = (temp > 32767) ? 32767 : temp; + } + ts_test->accord_arr[i].size = data_size; + } +} + +static int goodix_cache_self_rawdata(struct goodix_ts_test *ts_test) +{ + int i; + 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; + + for (i = 0; i < GOODIX_RETRY_3; i++) { + ret = goodix_poll_rawdata(ts_test, + GTP_SELF_RAW, (u8 *)ts_test->self_rawdata.data, data_size); + if (ret == 0) { + ts_test->self_rawdata.size = data_size; + break; + } + } + + return ret; +} + +static int goodix_cache_noisedata(struct goodix_ts_test *ts_test) +{ + int ret; + int i,j; + int cnt; + u8 val; + 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 flag_addr = ts_test->ts->ic_info.misc.touch_data_addr; + + if (cd->bus->ic_type == IC_TYPE_BERLIN_D || + cd->bus->ic_type == IC_TYPE_NOTTINGHAM) { + 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 (i = 0; i < DISCARD_FRAMES; i++) { + val = 0; + ts_test_write(ts_test, flag_addr, &val, 1); + msleep(20); + } + + for (cnt = 0; cnt < NOISEDATA_TEST_TIMES; cnt++) { + for (j = 0; j < GOODIX_RETRY_3; j++) { + ret = goodix_poll_rawdata(ts_test, + GTP_MUTUAL_DIFF, (u8 *)ts_test->noisedata[cnt].data, data_size); + if (ret == 0) { + 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]); + break; + } + } + } + + return ret; +} + +static int goodix_cache_self_noisedata(struct goodix_ts_test *ts_test) +{ + int ret; + int i,j; + 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; + + for (j = 0; j < GOODIX_RETRY_3; j++) { + ret = goodix_poll_rawdata(ts_test, + GTP_SELF_DIFF, (u8 *)ts_test->self_noisedata.data, data_size); + if (ret == 0) { + 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]); + } + break; + } + } + + 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 * 100 > times * 100 * 9 / 10) { + ts_err("rawdata more than 90%%(%d) fail, test fail", err_cnt); + return -1; + } + if (err_cnt * 100 > times * 100 * 1 / 10) { + ts_info("rawdata more than 10%%(%d) fail, need test again", err_cnt); + return 1; + } + + ts_info("rawdata less than 10%%(%d) fail, test pass", err_cnt); + return 0; +} + +static int goodix_analysis_deltadata(struct goodix_ts_test *ts_test) +{ + int i; + int j; + int ret = 0; + s32 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); + 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; + int retry = 0; + + 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"); + +rawdata_test_start: + /* obtain rawdata */ + ret = goodix_cache_rawdata(ts_test); + if (ret < 0) { + 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_TEST_PANEL_ERR; + } else if (ret == 0) { + ts_test->test_result[GTP_CAP_TEST] = GTP_TEST_OK; + } else if (retry < GOODIX_RETRY_3) { + retry++; + ts_info("rawdata test retry[%d]", retry); + goto rawdata_test_start; + } else { + ts_test->test_result[GTP_CAP_TEST] = GTP_TEST_PANEL_ERR; + } + } + + /* 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_TEST_PANEL_ERR; + else + ts_test->test_result[GTP_DELTA_TEST] = GTP_TEST_OK; + + /* 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_TEST_PANEL_ERR; + else + ts_test->test_result[GTP_SELFCAP_TEST] = GTP_TEST_OK; + } + } + + /* 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 diffdata"); + } 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_TEST_PANEL_ERR; + else + ts_test->test_result[GTP_NOISE_TEST] = GTP_TEST_OK; + } + } + + /* 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_TEST_PANEL_ERR; + else + ts_test->test_result[GTP_SELFNOISE_TEST] = GTP_TEST_OK; + } + } + +exit: + goodix_cap_test_finish(ts_test); +} + +static void goodix_version_test(struct goodix_ts_test *ts_test) +{ + struct goodix_ts_core *cd = ts_test->ts; + u8 rom_pid[8] = {0}; + + memcpy(rom_pid, cd->fw_version.rom_pid, sizeof(cd->fw_version.rom_pid)); + sprintf(ts_test->cur_version, "%s-%02x.%02x.%02x_%s_%02x.%02x.%02x.%02x", + rom_pid, cd->fw_version.rom_vid[0], cd->fw_version.rom_vid[1], + cd->fw_version.rom_vid[2], cd->fw_version.patch_pid, + cd->fw_version.patch_vid[0], cd->fw_version.patch_vid[1], + cd->fw_version.patch_vid[2], cd->fw_version.patch_vid[3]); + + ts_test->test_result[GTP_VERSION_TEST] = GTP_TEST_OK; +} + +#define FT_TEST_PASS_FLAG 0xAA +static void goodix_check_key_info_test(struct goodix_ts_test *ts_test) +{ + int ret; + u32 chip_pid_addr; + u32 chip_info_addr; + u8 chip_info_buf[CHIP_KEY_INFO_MAX_LEN]; + u8 test_result = 0; + u16 chksum; + u16 sum = 0; + int i; + struct goodix_ts_core *cd = ts_test->ts; + + //check chip pid + if (cd->bus->ic_type == IC_TYPE_BERLIN_B) + chip_pid_addr = 0x3F051; + else + chip_pid_addr = 0x1F031; + + for (i = 0; i < 3; i++) { + ret = cd->hw_ops->read_flash(cd, chip_pid_addr, ts_test->chip_pid, sizeof(ts_test->chip_pid)); + if (ret == 0) + break; + msleep(100); + } + if (ret < 0) { + ts_err("read chip pid from flash failed"); + return; + } + + if (memcmp(ts_test->chip_pid, cd->fw_version.patch_pid, 7)) { + test_result |= 0x01; // chip pid check failed + ts_err("chip_pid:%s != patch_pid:%s", ts_test->chip_pid, cd->fw_version.patch_pid); + } + + //read chip load info for berlin + if (cd->bus->ic_type == IC_TYPE_BERLIN_B || + cd->bus->ic_type == IC_TYPE_BERLIN_D || + cd->bus->ic_type == IC_TYPE_NOTTINGHAM) { + if (cd->bus->ic_type == IC_TYPE_BERLIN_B) + chip_info_addr = 0x3F300; + else + chip_info_addr = 0x1F300; + + for (i = 0; i < 3; i++) { + ret = cd->hw_ops->read_flash(cd, chip_info_addr, chip_info_buf, sizeof(chip_info_buf)); + if (ret == 0) + break; + msleep(100); + } + if (ret < 0) { + ts_err("read chip info from flash failed"); + return; + } + chksum = chip_info_buf[CHIP_KEY_INFO_MAX_LEN - 2] + ((u16)chip_info_buf[CHIP_KEY_INFO_MAX_LEN - 1] << 8); + for (i = 0; i < CHIP_KEY_INFO_MAX_LEN - 2; i++) + sum += chip_info_buf[i]; + if ((sum & 0x00FF) != (chksum & 0x00FF)) { + ts_err("chip info checksum error"); + return; + } + + ts_test->chip_info.hw_ver = chip_info_buf[0]; + ts_test->chip_info.package_site = chip_info_buf[1]; + + if (chip_info_buf[2 + CHIP_UID_MAX_LEN] == FT_TEST_PASS_FLAG) { + memcpy(ts_test->chip_info.chip_uid, &chip_info_buf[2], CHIP_UID_MAX_LEN);//UID + ts_test->chip_info.ft_test_result = chip_info_buf[2 + CHIP_UID_MAX_LEN]; + } else if (chip_info_buf[2] == FT_TEST_PASS_FLAG){ + ts_test->chip_info.ft_test_result = chip_info_buf[2]; + memcpy(ts_test->chip_info.chip_uid, &chip_info_buf[3], CHIP_UID_MAX_LEN);//UID + } else { + test_result |= 0x02; + ts_err("chip info test failed"); + } + + ts_test->chip_info.substrate_tech = chip_info_buf[3 + CHIP_UID_MAX_LEN]; + ts_test->chip_info.mcu_fab = chip_info_buf[4 + CHIP_UID_MAX_LEN]; + ts_test->chip_info.vaild_len = chip_info_buf[CHIP_KEY_INFO_MAX_LEN - 3]; + ts_test->chip_info.check_sum = chksum; + } + + if (test_result > 0) + ts_test->test_result[GTP_CHIP_KEY_INFO_TEST] = GTP_TEST_PANEL_ERR; + else + ts_test->test_result[GTP_CHIP_KEY_INFO_TEST] = GTP_TEST_OK; +} + +static void goodix_custom_info_test(struct goodix_ts_test *ts_test) +{ + int ret; + struct goodix_ts_core *cd = ts_test->ts; + u32 custom_info_addr; + u32 custom_info_ram_addr; + u32 custom_info_len = 16; + + if (cd->bus->ic_type == IC_TYPE_BERLIN_A) { + custom_info_addr = 0x3F000; + custom_info_ram_addr = 0x10028; + } else if (cd->bus->ic_type == IC_TYPE_BERLIN_B) { + custom_info_addr = 0x7F000; + custom_info_ram_addr = 0x10030; + } else { + custom_info_addr = 0x1E000; + custom_info_ram_addr = 0x10030; + } + + ret = cd->hw_ops->read_flash(cd, custom_info_addr, ts_test->custom_info_flash, custom_info_len); + if (ret < 0) { + ts_err("read custom info from flash failed"); + return; + } + ret = cd->hw_ops->read(cd, custom_info_ram_addr, ts_test->custom_info_sram, custom_info_len); + if (ret < 0) { + ts_err("read custom info from sram failed"); + return; + } + + ts_test->test_result[GTP_CUSTOM_INFO_TEST] = GTP_TEST_OK; +} + +char *goodix_strncat(char *dest, char *src, size_t dest_size) +{ + size_t dest_len = 0; + + dest_len = strnlen(dest, dest_size); + return strncat(&dest[dest_len], src, dest_size - dest_len - 1); +} + +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 int goodix_save_test_config(struct goodix_ts_test *ts_test) +{ + int ret = 0; + int i; + struct goodix_ic_config *cfg = &ts_test->test_config; + + if (cfg->len <= 0) + return 0; + + proc_append_string("\n"); + for (i = 0; i < cfg->len; i++) + proc_append_string("0x%02X,", cfg->data[i]); + proc_append_string("\n\n"); + return ret; +} + +static int goodix_save_header(struct goodix_ts_test *ts_test) +{ + int i; + bool result = false; + struct goodix_ts_core *ts = ts_test->ts; + + proc_append_string("\n"); + proc_append_string("\n"); + proc_append_string("
\n"); + + /* sava test result */ + for (i = 0; i < MAX_TEST_ITEMS; i++) { + if (ts_test->test_params.test_items[i]) { + if (ts_test->test_result[i] != GTP_TEST_OK) { + result = true; + break; + } + } + } + proc_append_string("%s\n", result ? "NG" : "OK"); + proc_append_string("GT%s\n%d\n", + ts->fw_version.patch_pid, ts->fw_version.sensor_id); + + /* save test config */ + goodix_save_test_config(ts_test); + + proc_append_string("
\n"); + + /* item list */ + proc_append_string("\n"); + + if (ts_test->test_params.test_items[GTP_VERSION_TEST]) { + if (GTP_TEST_OK == ts_test->test_result[GTP_VERSION_TEST]) + proc_append_string("\n"); + else + proc_append_string("\n"); + } + + if (ts_test->test_params.test_items[GTP_CHIP_KEY_INFO_TEST]) { + if (GTP_TEST_OK == ts_test->test_result[GTP_CHIP_KEY_INFO_TEST]) + proc_append_string("\n"); + else + proc_append_string("\n"); + } + + if (ts_test->test_params.test_items[GTP_CUSTOM_INFO_TEST]) { + if (GTP_TEST_OK == ts_test->test_result[GTP_CUSTOM_INFO_TEST]) + proc_append_string("\n"); + else + proc_append_string("\n"); + } + + if (ts_test->test_params.test_items[GTP_CAP_TEST]) { + if (GTP_TEST_OK == ts_test->test_result[GTP_CAP_TEST]) + proc_append_string("\n"); + else + proc_append_string("\n"); + } + + if (ts_test->test_params.test_items[GTP_DELTA_TEST]) { + if (GTP_TEST_OK == ts_test->test_result[GTP_DELTA_TEST]) + proc_append_string("\n"); + else + proc_append_string("\n"); + } + + if (ts_test->test_params.test_items[GTP_NOISE_TEST]) { + if (GTP_TEST_OK == ts_test->test_result[GTP_NOISE_TEST]) + proc_append_string("\n"); + else + proc_append_string("\n"); + } + + if (ts_test->test_params.test_items[GTP_SELFNOISE_TEST]) { + if (GTP_TEST_OK == ts_test->test_result[GTP_SELFNOISE_TEST]) + proc_append_string("\n"); + else + proc_append_string("\n"); + } + + if (ts_test->test_params.test_items[GTP_SELFCAP_TEST]) { + if (GTP_TEST_OK == ts_test->test_result[GTP_SELFCAP_TEST]) + proc_append_string("\n"); + else + proc_append_string("\n"); + } + + if (ts_test->test_params.test_items[GTP_SHORT_TEST]) { + if (GTP_TEST_OK == ts_test->test_result[GTP_SHORT_TEST]) + proc_append_string("\n"); + else + proc_append_string("\n"); + } + + proc_append_string("\n"); + return 0; +} + +static int goodix_save_items(struct goodix_ts_test *ts_test) +{ + int i; + int tx = ts_test->test_params.drv_num; + int rx = ts_test->test_params.sen_num; + int chn1; + int chn2; + int r; + + proc_append_string("\n"); + + if (ts_test->test_params.test_items[GTP_VERSION_TEST]) { + proc_append_string("\n"); + proc_append_string("%s\n", ts_test->cur_version); + proc_append_string("\n"); + } + + if (ts_test->test_params.test_items[GTP_CHIP_KEY_INFO_TEST]) { + proc_append_string("\n"); + if (GTP_TEST_OK == ts_test->test_result[GTP_CHIP_KEY_INFO_TEST]) + proc_append_string("\n"); + else + proc_append_string("\n"); + proc_append_string("\n", + ts_test->chip_pid, ts_test->chip_info.hw_ver, + ts_test->chip_info.package_site, ts_test->chip_info.ft_test_result, + ts_test->chip_info.substrate_tech, ts_test->chip_info.mcu_fab); + proc_append_string(""); + for (i = 0; i < sizeof(ts_test->chip_info.chip_uid); i++) + proc_append_string("%02x", ts_test->chip_info.chip_uid[i]); + proc_append_string("\n"); + proc_append_string("\n"); + proc_append_string("\n"); + } + + if (ts_test->test_params.test_items[GTP_CUSTOM_INFO_TEST]) { + proc_append_string("\n"); + proc_append_string(""); + for (i = 0; i < sizeof(ts_test->custom_info_flash); i++) + proc_append_string("0x%02x,", ts_test->custom_info_flash[i]); + proc_append_string("\n"); + proc_append_string(""); + for (i = 0; i < sizeof(ts_test->custom_info_sram); i++) + proc_append_string("0x%02x,", ts_test->custom_info_sram[i]); + proc_append_string("\n"); + proc_append_string("\n"); + } + + /* save short result */ + if (ts_test->test_params.test_items[GTP_SHORT_TEST]) { + proc_append_string("\n"); + proc_append_string("%d\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) + proc_append_string("\n", r); + else if (chn2 == CHN_GND) + proc_append_string("Chn2=\"GND\" ShortResistor= \"%dKom\"/>\n", r); + else if (chn2 & DRV_CHANNEL_FLAG) + proc_append_string("Chn2=\"Tx%d\" ShortResistor= \"%dKom\"/>\n", + chn2 & 0x7f, r); + else + proc_append_string("Chn2=\"Rx%d\" ShortResistor= \"%dKom\"/>\n", + chn2 & 0x7f, r); + } + proc_append_string("\n"); + } + + /* rawdata max limit */ + proc_append_string("\n"); + proc_append_string("%d\n", TOTAL_FRAME_NUM); + proc_append_string("\n"); + for (i = 0; i < tx * rx; i++) { + proc_append_string("%d,", ts_test->test_params.max_limits[i]); + if ((i + 1) % tx == 0) + proc_append_string("\n"); + } + proc_append_string("\n"); + /* BeyondRawdataUpperLimit */ + proc_append_string("\n"); + for (i = 0; i < tx * rx; i++) { + proc_append_string("%d,", ts_test->open_res.beyond_max_limit_cnt[i]); + if ((i + 1) % tx == 0) + proc_append_string("\n"); + } + proc_append_string("\n"); + + /* rawdata min limit */ + proc_append_string("\n"); + for (i = 0; i < tx * rx; i++) { + proc_append_string("%d,", ts_test->test_params.min_limits[i]); + if ((i + 1) % tx == 0) + proc_append_string("\n"); + } + proc_append_string("\n"); + /* BeyondRawdataLower limit */ + proc_append_string("\n"); + for (i = 0; i < tx * rx; i++) { + proc_append_string("%d,", ts_test->open_res.beyond_min_limit_cnt[i]); + if ((i + 1) % tx == 0) + proc_append_string("\n"); + } + proc_append_string("\n"); + + /* Max Accord limit */ + proc_append_string("\n"); + for (i = 0; i < tx * rx; i++) { + proc_append_string("%d,", ts_test->test_params.deviation_limits[i]); + if ((i + 1) % tx == 0) + proc_append_string("\n"); + } + proc_append_string("\n"); + /* BeyondAccordLimitCnt */ + proc_append_string("\n"); + for (i = 0; i < tx * rx; i++) { + proc_append_string("%d,", ts_test->open_res.beyond_accord_limit_cnt[i]); + if ((i + 1) % tx == 0) + proc_append_string("\n"); + } + proc_append_string("\n"); + proc_append_string("\n"); + + /* save noise limit */ + if (ts_test->test_params.test_items[GTP_NOISE_TEST]) { + proc_append_string("\n"); + proc_append_string("%d\n", + NOISEDATA_TEST_TIMES); + proc_append_string("%d\n", + ts_test->test_params.noise_threshold); + proc_append_string("\n"); + } + + /* save self rawdata limit */ + if (ts_test->test_params.test_items[GTP_SELFCAP_TEST]) { + proc_append_string("\n"); + proc_append_string("1\n"); + proc_append_string("\n"); + for (i = 0; i < tx + rx; i++) { + proc_append_string("%d,", + ts_test->test_params.self_max_limits[i]); + if ((i + 1) % tx == 0) + proc_append_string("\n"); + } + if ((tx + rx) % tx != 0) + proc_append_string("\n"); + proc_append_string("\n"); + proc_append_string("\n"); + for (i = 0; i < tx + rx; i++) { + proc_append_string("%d,", + ts_test->test_params.self_min_limits[i]); + if ((i + 1) % tx == 0) + proc_append_string("\n"); + } + if ((tx + rx) % tx != 0) + proc_append_string("\n"); + proc_append_string("\n"); + proc_append_string("\n"); + } + + /* save selfnoise limit */ + if (ts_test->test_params.test_items[GTP_SELFNOISE_TEST]) { + proc_append_string("\n"); + proc_append_string("1\n"); + proc_append_string("%d\n", + ts_test->test_params.self_noise_threshold); + proc_append_string("\n"); + } + + proc_append_string("\n"); + return 0; +} + +static int goodix_save_rawdata(struct goodix_ts_test *ts_test) +{ + int i; + int j; + s16 stat_result[3]; + int tx = ts_test->test_params.drv_num; + int rx = ts_test->test_params.sen_num; + int len = tx * rx; + + proc_append_string("\n"); + for (i = 0; i < TOTAL_FRAME_NUM; i++) { + goodix_data_cal(ts_test->rawdata[i].data, len, stat_result); + proc_append_string("\n", + i, len, stat_result[1], stat_result[2], stat_result[0]); + for (j = 0; j < len; j++) { + proc_append_string("%d,", ts_test->rawdata[i].data[j]); + if ((j + 1) % tx == 0) + proc_append_string("\n"); + } + proc_append_string("\n"); + goodix_data_cal(ts_test->accord_arr[i].data, len, stat_result); + proc_append_string("\n", + i, len, stat_result[1], stat_result[2], stat_result[0]); + for (j = 0; j < len; j++) { + proc_append_string("%d,", ts_test->accord_arr[i].data[j]); + if ((j + 1) % tx == 0) + proc_append_string("\n"); + } + proc_append_string("\n"); + } + + proc_append_string("\n"); + return 0; +} + +static int goodix_save_noise_data(struct goodix_ts_test *ts_test) +{ + int i; + int j; + s16 stat_result[3]; + int tx = ts_test->test_params.drv_num; + int rx = ts_test->test_params.sen_num; + int len = tx * rx; + + proc_append_string("\n"); + for (i = 0; i < NOISEDATA_TEST_TIMES; i++) { + goodix_data_cal(ts_test->noisedata[i].data, len, stat_result); + proc_append_string("\n", + i, len, stat_result[1], stat_result[2], stat_result[0]); + for (j = 0; j < len; j++) { + proc_append_string("%d,", ts_test->noisedata[i].data[j]); + if ((j + 1) % tx == 0) + proc_append_string("\n"); + } + proc_append_string("\n"); + } + + proc_append_string("\n"); + return 0; +} + +static int goodix_save_self_data(struct goodix_ts_test *ts_test, + s16 *src_data, u8 *title, int len) +{ + int i; + s16 stat_result[3]; + int tx = ts_test->test_params.drv_num; + + proc_append_string("<%s>\n", title); + goodix_data_cal(src_data, len, stat_result); + proc_append_string("\n", + len, stat_result[1], stat_result[2], stat_result[0]); + for (i = 0; i < len; i++) { + proc_append_string("%d,", src_data[i]); + if ((i + 1) % tx == 0) + proc_append_string("\n"); + } + if (len % tx != 0) + proc_append_string("\n"); + proc_append_string("\n"); + proc_append_string("\n", title); + return 0; +} + +static int goodix_save_data(struct goodix_ts_test *ts_test) +{ + int ret; + + proc_append_string("\n"); + + ret = goodix_save_rawdata(ts_test); + + if (ts_test->test_params.test_items[GTP_NOISE_TEST]) { + ret = goodix_save_noise_data(ts_test); + } + + if (ts_test->test_params.test_items[GTP_SELFCAP_TEST]) { + ret = goodix_save_self_data(ts_test, + ts_test->self_rawdata.data, + "selfDataRecord", + ts_test->self_rawdata.size); + } + + if (ts_test->test_params.test_items[GTP_SELFNOISE_TEST]) { + ret = goodix_save_self_data(ts_test, + ts_test->self_noisedata.data, + "selfDiffDataRecord", + ts_test->self_noisedata.size); + } + + proc_append_string("\n"); + return ret; +} + +/* save end tag in csv file */ +static int goodix_save_tail(struct goodix_ts_test *ts_test) +{ + proc_append_string("
\n"); + return 0; +} + +static void goodix_save_result_data(struct goodix_ts_test *ts_test) +{ + int ret; + + /* save header */ + ret = goodix_save_header(ts_test); + if (ret < 0) + return; + + /* save limits */ + ret = goodix_save_items(ts_test); + if (ret < 0) + return; + + /* save data */ + ret = goodix_save_data(ts_test); + if (ret < 0) + return; + + /* save tail */ + ret = goodix_save_tail(ts_test); + if (ret < 0) + return; +} + +static int goodix_do_inspect(struct goodix_ts_core *cd) +{ + int ret; + struct goodix_ts_test *ts_test = NULL; + + if (!cd) { + ts_err("core_data or info is NULL"); + return -ENODEV; + } + + ts_test = vzalloc(sizeof(*ts_test)); + 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"); + goto exit_finish; + } + ts_info("TP test prepare OK"); + + goodix_version_test(ts_test); + goodix_check_key_info_test(ts_test); + goodix_custom_info_test(ts_test); + goodix_capacitance_test(ts_test); + if (ts_test->test_params.test_items[GTP_SHORT_TEST]) + goodix_shortcircut_test(ts_test); + goodix_save_result_data(ts_test); + goodix_tptest_finish(ts_test); + +exit_finish: + vfree(ts_test); + return ret; +} + +static ssize_t auto_test_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct seq_file *seq_file = (struct seq_file *)file->private_data; + struct goodix_ts_core *cd = (struct goodix_ts_core *)seq_file->private; + char tmp_buf[10]; + + if (copy_from_user(tmp_buf, buf, count) != 0) { + ts_err("copy from user failed"); + goto exit; + } + + if (!(tmp_buf[0] == 1 || tmp_buf[0] == '1')) { + ts_err("auto test input parameter error [%d]", tmp_buf[0]); + goto exit; + } + + output_offset = 0; + vfree(output_buf); + output_buf = (char *)vmalloc(MAX_PROC_OUTPUT_SIZE); + + goodix_do_inspect(cd); +exit: + return count; +} + +static void *seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= output_offset) + return NULL; + + return output_buf + *pos; +} + +static int seq_show(struct seq_file *s, void *v) +{ + //seq_printf(s, (u8 *)v); + seq_printf(s, "%s", (u8 *)v); + return 0; +} + +static void *seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + *pos += output_offset; + return NULL; +} + +static void seq_stop(struct seq_file *s, void *v) +{ + if (s->read_pos >= output_offset) { + output_offset = 0; + vfree(output_buf); + } +} + +static const struct seq_operations seq_ops = { + .start = seq_start, + .next = seq_next, + .stop = seq_stop, + .show = seq_show +}; + +static int auto_test_open(struct inode *inode, struct file *file) +{ + struct seq_file *seq_file; + int ret; + + ret = seq_open(file, &seq_ops); + if (ret) + return ret; + + seq_file = (struct seq_file *)file->private_data; + if (seq_file) { + seq_file->private = PDE_DATA(inode); + } + + return 0; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0)) +static const struct proc_ops auto_test_ops = { + .proc_open = auto_test_open, + .proc_read = seq_read, + .proc_write = auto_test_write, + .proc_lseek = seq_lseek, + .proc_release = seq_release, +}; +#else +static const struct file_operations auto_test_ops = { + .open = auto_test_open, + .read = seq_read, + .write = auto_test_write, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif + +int inspect_module_init(struct goodix_ts_core *core_data) +{ + struct proc_dir_entry *proc_entry; + + proc_entry = proc_create_data("auto_test", + 0660, core_data->proc_dir_entry, &auto_test_ops, core_data); + if (!proc_entry) { + ts_err("failed to create proc entry"); + return -ENODEV; + } + + ts_info("inspect module init success"); + return 0; +} + +void inspect_module_exit(struct goodix_ts_core *core_data) +{ + ts_info("inspect module exit"); + remove_proc_entry("auto_test", core_data->proc_dir_entry); +} diff --git a/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_ts_tools.c b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_ts_tools.c new file mode 100644 index 000000000000..bcc320ae946f --- /dev/null +++ b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_ts_tools.c @@ -0,0 +1,437 @@ + /* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#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 + +/* 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]; + struct goodix_ts_core *ts_core = + container_of(dev, struct goodix_ts_core, tools_dev); + 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) + 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(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 = + container_of(dev, struct goodix_ts_core, tools_dev); + 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 = + container_of(dev, struct goodix_ts_core, tools_dev); + const struct goodix_ts_hw_ops *hw_ops = ts_core->hw_ops; + struct goodix_ic_config *temp_cfg = NULL; + + 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); + ts_info("IRQ enabled"); + } else if (arg == 0) { + hw_ops->irq_enable(ts_core, false); + 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_esd_off(ts_core); + else + goodix_ts_esd_on(ts_core); + 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 == NULL) { + 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 struct goodix_ts_core *core_data_locate(int minor) +{ + struct goodix_device_resource *res, *next; + + if (!list_empty(&goodix_devices.list)) { + list_for_each_entry_safe(res, next, &goodix_devices.list, list) { + if (res->core_data.tools_dev.miscdev.minor == minor) + return &res->core_data; + } + } + + return NULL; +} + +static int goodix_tools_open(struct inode *inode, struct file *filp) +{ + struct goodix_ts_core *cd = core_data_locate(iminor(inode)); + + if (!cd) { + ts_err("can't find core data"); + return -ENODEV; + } + + goodix_ts_esd_off(cd); + filp->private_data = &cd->tools_dev; + ts_info("success open tools"); + return 0; +} + +static int goodix_tools_release(struct inode *inode, struct file *filp) +{ + struct goodix_ts_core *cd = core_data_locate(iminor(inode)); + + if (!cd) { + ts_err("can't find core data"); + return -ENODEV; + } + /* when the last close this dev node unregister the module */ + cd->tools_ctrl_sync = false; + goodix_ts_esd_on(cd); + 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 +}; + +/** + * goodix_tools_init - init goodix tools device and register a miscdevice + * + * return: 0 success, else failed + */ +int goodix_tools_init(struct goodix_ts_core *core_data) +{ + int ret; + struct goodix_tools_dev *tools_dev = &core_data->tools_dev; + + sprintf(tools_dev->name, "%s.%d", GOODIX_TOOLS_NAME, core_data->pdev->id); + tools_dev->miscdev.minor = MISC_DYNAMIC_MINOR; + tools_dev->miscdev.name = tools_dev->name; + tools_dev->miscdev.fops = &goodix_tools_fops; + ret = misc_register(&tools_dev->miscdev); + if (ret) + ts_err("Debug tools miscdev register failed"); + else + ts_info("Debug tools miscdev register success"); + + return ret; +} + +void goodix_tools_exit(struct goodix_ts_core *core_data) +{ + struct goodix_tools_dev *tools_dev = &core_data->tools_dev; + + misc_deregister(&tools_dev->miscdev); + ts_info("Debug tools miscdev exit"); +} diff --git a/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_ts_utils.c b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_ts_utils.c new file mode 100644 index 000000000000..de90dfb94bc6 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gt96x_mmi/goodix_ts_utils.c @@ -0,0 +1,280 @@ + /* + * 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) { + if (size < 2) + return 1; + for (i = 0; i < size - 2; i++) + cal_checksum += data[i]; + r_checksum = data[size - 2] + (data[size - 1] << 8); + return (cal_checksum & 0xFFFF) == r_checksum ? 0 : 1; + } + + if (size < 4) + return 1; + for (i = 0; i < size - 4; i += 2) + cal_checksum += data[i] + (data[i + 1] << 8); + r_checksum = data[size - 4] + (data[size - 3] << 8) + + (data[size - 2] << 16) + (data[size - 1] << 24); + return cal_checksum == r_checksum ? 0 : 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 ? "0x00" : "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]); +} + +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; + struct goodix_ic_info_other *other = &ic_info->other; + + 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 * %d, %d, %d", + parm->drv_num, parm->sen_num, + parm->button_num, parm->force_num); + + ts_info("screen_max_x * screen_max_y: %d * %d", + other->screen_max_x, other->screen_max_y); + + 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("frame_data_addr: 0x%04X", + misc->frame_data_addr); + 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); +} + +/* 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, + struct goodix_bus_interface *bus_inf) +{ + const struct property *prop; + char ic_name[128] = {0}; + int i; + + prop = of_find_property(node, "compatible", NULL); + if (!prop || !prop->value || prop->length > sizeof(ic_name)) { + ts_err("invalid compatible property"); + return -EINVAL; + } + + memcpy(ic_name, prop->value, prop->length); + + /* replace string end flag with ';' */ + for (i = 0; i < prop->length - 1; i++) + if (ic_name[i] == 0) + ic_name[i] = ';'; + + ts_info("ic_name %s", ic_name); + + if (strstr(ic_name, "brl-a")) { + ts_info("ic type is brl-a"); + bus_inf->ic_type = IC_TYPE_BERLIN_A; + return 0; + } + + if (strstr(ic_name, "brl-b")) { + ts_info("ic type is brl-b"); + bus_inf->ic_type = IC_TYPE_BERLIN_B; + if (strstr(ic_name, "ga687x")) { + bus_inf->sub_ic_type = IC_TYPE_SUB_B2; + ts_info("sub ic type is brl-b2"); + } + return 0; + } + if (strstr(ic_name, "brl-d")) { + ts_info("ic type is brl-d"); + bus_inf->ic_type = IC_TYPE_BERLIN_D; + return 0; + } + if (strstr(ic_name, "nottingham")) { + ts_info("ic type is nottingham"); + bus_inf->ic_type = IC_TYPE_NOTTINGHAM; + return 0; + } + + ts_err("unsupported ic type %s", ic_name); + return -EINVAL; +} + diff --git a/drivers/input/touchscreen/goodix_gtx8_mmi/Android.mk b/drivers/input/touchscreen/goodix_gtx8_mmi/Android.mk new file mode 100644 index 000000000000..85cc305895f6 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_mmi/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := goodix_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/goodix_gtx8_mmi/Kbuild b/drivers/input/touchscreen/goodix_gtx8_mmi/Kbuild new file mode 100644 index 000000000000..d527679769df --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_mmi/Kbuild @@ -0,0 +1,8 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += \ + -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/goodix_gtx8_mmi + +obj-m := goodix_mmi.o +goodix_mmi-objs = goodix_ts_i2c.o goodix_ts_core.o goodix_ts_test.o goodix_cfg_bin.o goodix_gtx8_update.o goodix_ts_tools.o goodix_ts_gesture.o diff --git a/drivers/input/touchscreen/goodix_gtx8_mmi/Kconfig b/drivers/input/touchscreen/goodix_gtx8_mmi/Kconfig new file mode 100644 index 000000000000..094bc47b0f8d --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_mmi/Kconfig @@ -0,0 +1,47 @@ +# +# Goodix touchscreen driver configuration +# +menuconfig TOUCHSCREEN_GOODIX_GTX8_MMI + bool "Goodix GTx8 touchscreen" + depends on I2C + default y + help + Say Y here if you have a Goodix GTx5xx touchscreen connected + to your system. + + If unsure, say N. + +if TOUCHSCREEN_GOODIX_GTX8_MMI + + +config TOUCHSCREEN_GOODIX_GTX8_MMI_UPDATE + tristate "Goodix GTx8 firmware update module" + default y + help + Say Y here to enable support for doing firmware update. + + If unsure, say N. + + To compile this driver as a module, choose M here. + +config TOUCHSCREEN_GOODIX_GTX8_MMI_GESTURE + tristate "Goodix GTx8 gesture wakeup feature" + default n + help + Say Y here to enable support for gesture wakeup feature.. + + If unsure, say N. + + To compile this driver as a module, choose M here. + +config TOUCHSCREEN_GOODIX_GTX8_MMI_TOOLS + tristate "Goodix touch tools support" + default y + help + Say Y here to enable debug tools. + + If unsure, say N. + + To compile this driver as a module, choose M here. + +endif diff --git a/drivers/input/touchscreen/goodix_gtx8_mmi/Makefile b/drivers/input/touchscreen/goodix_gtx8_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_cfg_bin.c b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_cfg_bin.c new file mode 100644 index 000000000000..54ea91c9964d --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_cfg_bin.c @@ -0,0 +1,688 @@ +#include "goodix_cfg_bin.h" + + +int goodix_i2c_write(struct goodix_ts_device *dev, + unsigned int reg, unsigned char *data, unsigned int len); +int goodix_i2c_read(struct goodix_ts_device *dev, + unsigned int reg, unsigned char *data, unsigned int len); +int goodix_hw_reset(struct goodix_ts_device *dev); +extern struct goodix_module goodix_modules; + +int goodix_start_cfg_bin(struct goodix_ts_core *ts_core) +{ + struct task_struct *cfg_bin_thrd; + /* create and run update thread */ + cfg_bin_thrd = kthread_run(goodix_cfg_bin_proc, + ts_core, "goodix-parse_cfg_bin"); + if (IS_ERR_OR_NULL(cfg_bin_thrd)) { + ts_err("Failed to create update thread:%ld", + PTR_ERR(cfg_bin_thrd)); + return -EFAULT; + } + return 0; +} + +int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin) +{ + u8 checksum; + int i, r; + u16 offset1, offset2; + + if (!cfg_bin->bin_data || + cfg_bin->bin_data_len == 0) { + ts_err("NO cfg_bin data, cfg_bin data length:%d", + cfg_bin->bin_data_len); + r = -EINVAL; + goto exit; + } + + /* 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); + r = -EINVAL; + goto exit; + } + 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 length error,len in cfg_bin:%d, len of firmware:%d", + cfg_bin->head.bin_len, cfg_bin->bin_data_len); + r = -EINVAL; + goto exit; + } + + /*check cfg_bin valid*/ + checksum = 0; + for (i = 5; i < cfg_bin->bin_data_len; i++) + checksum += cfg_bin->bin_data[i]; + + if (checksum != cfg_bin->head.checksum) { + ts_err("cfg_bin checksum ERROR, checksum in cfg_bin:0x%02x, checksum caculate:0x%02x", + cfg_bin->head.checksum, checksum); + r = -EINVAL; + goto exit; + } + + /*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) { + /*ts_err("cfg_pkgs, allocate memory ERROR");*/ + r = -ENOMEM; + goto exit; + } + /*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 * 2] + + (cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * 2 + 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 * 2] + + (cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * 2 + 1] + << 8); + + offset2 = + cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * 2 + 2] + + (cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * 2 + 3] + << 8); + + if (offset2 <= offset1) { + ts_err("offset error,pkg:%d, offset1:%d, offset2:%d", + i, offset1, offset2); + r = -EINVAL; + 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); + /*compatible little edition and big edition*/ + goodix_cfg_pkg_leToCpu(&cfg_bin->cfg_pkgs[i]); + + /*get configuration data*/ + cfg_bin->cfg_pkgs[i].cfg = + &cfg_bin->bin_data[offset1 + TS_PKG_HEAD_LEN]; + } + + /*debug, print pkg information*/ + for (i = 0; i < cfg_bin->head.pkg_num; i++) { + ts_info("---------------------------------------------"); + ts_info("------package:%d------", i + 1); + ts_info("package len:%04x", + cfg_bin->cfg_pkgs[i].cnst_info.pkg_len); + ts_info("package ic_type:%s", + cfg_bin->cfg_pkgs[i].cnst_info.ic_type); + ts_info("package cfg_type:%01x", + cfg_bin->cfg_pkgs[i].cnst_info.cfg_type); + ts_info("package sensor_id:%01x", + cfg_bin->cfg_pkgs[i].cnst_info.sensor_id); + ts_info("package hw_pid:%s", + cfg_bin->cfg_pkgs[i].cnst_info.hw_pid); + ts_info("package hw_vid:%s", + cfg_bin->cfg_pkgs[i].cnst_info.hw_vid); + ts_info("package fw_mask_version:%s", + cfg_bin->cfg_pkgs[i].cnst_info.fw_mask); + ts_info("package fw_patch_version:%s", + cfg_bin->cfg_pkgs[i].cnst_info.fw_patch); + ts_info("package x_res_offset:%02x", + cfg_bin->cfg_pkgs[i].cnst_info.x_res_offset); + ts_info("package y_res_offset:%02x", + cfg_bin->cfg_pkgs[i].cnst_info.y_res_offset); + ts_info("package trigger_offset:%02x", + cfg_bin->cfg_pkgs[i].cnst_info.trigger_offset); + + ts_info(""); + ts_info("send_cfg_flag reg:%02x", + cfg_bin->cfg_pkgs[i].reg_info.cfg_send_flag.addr); + ts_info("version base reg:%02x, len:%d", + cfg_bin->cfg_pkgs[i].reg_info.version_base.addr, + cfg_bin->cfg_pkgs[i].reg_info.version_base.reserved1); + ts_info("pid reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.pid.addr); + ts_info("vid reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.vid.addr); + ts_info("sensor_id reg:%02x", + cfg_bin->cfg_pkgs[i].reg_info.sensor_id.addr); + ts_info("fw_status reg:%02x", + cfg_bin->cfg_pkgs[i].reg_info.fw_status.addr); + ts_info("cfg_addr reg:%02x", + cfg_bin->cfg_pkgs[i].reg_info.cfg_addr.addr); + ts_info("esd reg:%02x", + cfg_bin->cfg_pkgs[i].reg_info.esd.addr); + ts_info("command reg:%02x", + cfg_bin->cfg_pkgs[i].reg_info.command.addr); + ts_info("coor:%02x", + cfg_bin->cfg_pkgs[i].reg_info.coor.addr); + ts_info("gesture:%02x", + cfg_bin->cfg_pkgs[i].reg_info.gesture.addr); + ts_info("fw_request:%02x", + cfg_bin->cfg_pkgs[i].reg_info.fw_request.addr); + ts_info("proximity:%02x", + cfg_bin->cfg_pkgs[i].reg_info.proximity.addr); + + ts_info("--------------------------------------------"); + } + r = 0; +exit: + return r; +} + +int goodix_cfg_bin_proc(void *data) +{ + struct goodix_ts_core *core_data = data; + struct goodix_ts_device *ts_dev = core_data->ts_dev; + int r; + struct goodix_cfg_bin *cfg_bin = kzalloc(sizeof(struct goodix_cfg_bin), + GFP_KERNEL); + if (!cfg_bin) { + /*ts_err("Failed to alloc memory for cfg_bin");*/ + r = -ENOMEM; + goto exit; + } + /*get cfg_bin from file system*/ + r = goodix_read_cfg_bin(ts_dev, cfg_bin); + if (r < 0) { + ts_err("read cfg_bin from /etc/firmware FAILED"); + goto exit; + } + /*parse cfg bin*/ + r = goodix_parse_cfg_bin(cfg_bin); + if (!r) { + ts_info("parse cfg bin SUCCESS"); + } else { + ts_err("parse cfg bin FAILED"); + goto exit; + } + + /*init i2c_set_doze_mode para*/ + ts_dev->doze_mode_set_count = 0; + mutex_init(&ts_dev->doze_mode_lock); + + /*get register address and configuration from cfg bin*/ + r = goodix_get_reg_and_cfg(ts_dev, cfg_bin); + if (!r) { + ts_info("get reg and cfg from cfg_bin SUCCESS"); + } else { + if (r != -EBUS) { + ts_err("get reg and cfg from cfg_bin FAILED, update fw then retry"); + goodix_modules.core_data = core_data; + goodix_modules.core_exit = false; + complete_all(&goodix_modules.core_comp); + goto exit; + } else { + ts_err("get reg and cfg from cfg_bin FAILED, I2C com ERROR"); + goto exit; + } + } + + /*debug*/ + ts_info("@@@@@@@@@"); + ts_info("cfg_send_flag:0x%04x", ts_dev->reg.cfg_send_flag); + ts_info("pid:0x%04x", ts_dev->reg.pid); + ts_info("vid:0x%04x", ts_dev->reg.vid); + ts_info("sensor_id:0x%04x", ts_dev->reg.sensor_id); + ts_info("fw_mask:0x%04x", ts_dev->reg.fw_mask); + ts_info("fw_status:0x%04x", ts_dev->reg.fw_status); + ts_info("cfg_addr:0x%04x", ts_dev->reg.cfg_addr); + ts_info("esd:0x%04x", ts_dev->reg.esd); + ts_info("command:0x%04x", ts_dev->reg.command); + ts_info("coor:0x%04x", ts_dev->reg.coor); + ts_info("gesture:0x%04x", ts_dev->reg.gesture); + ts_info("fw_request:0x%04x", ts_dev->reg.fw_request); + ts_info("proximity:0x%04x", ts_dev->reg.proximity); + ts_info("@@@@@@@@@"); + + /* initialize firmware */ + r = goodix_ts_hw_init(core_data); + if (r < 0) + goto exit; + + /* alloc/config/register input device */ + r = goodix_ts_input_dev_config(core_data); + if (r < 0) + goto exit; + + /* request irq line */ + r = goodix_ts_irq_setup(core_data); + if (r < 0) + goto exit; + + /*set flag, prevent fwupdate module parse cfg_group again*/ + core_data->cfg_group_parsed = true; + + /* inform the external module manager that + * touch core layer is ready now + */ + goodix_modules.core_data = core_data; + goodix_modules.core_exit = false; + /*complete_all(&goodix_modules.core_comp);*/ + +#ifdef CONFIG_DRM + core_data->fb_notifier.notifier_call = goodix_ts_fb_notifier_callback; + if (!msm_drm_register_client(&core_data->fb_notifier)) + ts_info("registered DRM notifier"); + else + ts_err("%s: Unable to register DRM notifier", + __func__); +#elif defined(CONFIG_FB) + core_data->fb_notifier.notifier_call = goodix_ts_fb_notifier_callback; + if (fb_register_client(&core_data->fb_notifier)) + ts_err("Failed to register fb notifier client:%d", r); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + core_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + core_data->early_suspend.resume = goodix_ts_lateresume; + core_data->early_suspend.suspend = goodix_ts_earlysuspend; + register_early_suspend(&core_data->early_suspend); +#endif + + /* esd protector */ + goodix_ts_esd_init(core_data); +#ifdef GTP_CHARGER + goodix_charger_init(ts_dev->board_data); + + if (ts_dev->board_data->charger_detection) + queue_work( + ts_dev->board_data->charger_detection->goodix_charger_notify_wq, + &ts_dev->board_data->charger_detection->charger_notify_work); + + ts_dev->board_data->charger_send_flage_enable = true; +#endif + /* generic notifier callback */ + core_data->ts_notifier.notifier_call = goodix_generic_noti_callback; + goodix_ts_register_notifier(&core_data->ts_notifier); + +exit: + complete_all(&goodix_modules.core_comp); + + /*if (cfg_bin->cfg_pkgs) {*/ + kfree(cfg_bin->cfg_pkgs); + cfg_bin->cfg_pkgs = NULL; + /*}*/ + + /*if (cfg_bin->bin_data) {*/ + kfree(cfg_bin->bin_data); + cfg_bin->bin_data = NULL; + /*}*/ + return r; +} + +int goodix_get_reg_and_cfg(struct goodix_ts_device *ts_dev, + struct goodix_cfg_bin *cfg_bin) +{ + int i; + u16 addr; + u8 read_len; + struct goodix_cfg_package *normal_pkg, *high_sense_pkg; + char temp_sensor_id = -1; + u8 temp_fw_mask[9] = {0x00}; + u8 temp_pid[8] = {0x00}; + int r = -EINVAL; + + normal_pkg = NULL; + high_sense_pkg = NULL; + + 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); + r = -EINVAL; + goto exit; + } + + /*select suitable cfg packages*/ + for (i = 0; i < cfg_bin->head.pkg_num; i++) { + /*get ic type*/ + if (!strncmp(cfg_bin->cfg_pkgs[i].cnst_info.ic_type, "nanjing", + sizeof(cfg_bin->cfg_pkgs[i].cnst_info.ic_type))) + ts_dev->ic_type = IC_TYPE_NANJING; + else if (!strncmp(cfg_bin->cfg_pkgs[i].cnst_info.ic_type, + "normandy", + sizeof(cfg_bin->cfg_pkgs[i].cnst_info.ic_type))) + ts_dev->ic_type = IC_TYPE_NORMANDY; + else + ts_err("get ic type FAILED, unknow ic type from cfg_bin:%s", + cfg_bin->cfg_pkgs[i].cnst_info.ic_type); + + + ts_info("ic_type:%d", ts_dev->ic_type); + /*reset IC*/ + r = goodix_hw_reset(ts_dev); + + /*contrast sensor id*/ + addr = cfg_bin->cfg_pkgs[i].reg_info.sensor_id.addr; + if (!addr) { + ts_info("pkg:%d, sensor_id reg is NULL", i); + continue; + } + + r = goodix_i2c_read(ts_dev, addr, + &temp_sensor_id, 1); + + if (r < 0) { + ts_err( + "read sensor id FAILED,I2C ERROR, pkg:%d, sensor_id reg:0x%02x", + i, addr); + goto exit; + } else { + /*sensor.reserved1 is a mask, if it's not ZERO, use it*/ + if (cfg_bin->cfg_pkgs[i].reg_info.sensor_id.reserved1 + != 0) + temp_sensor_id &= + cfg_bin-> + cfg_pkgs[i].reg_info.sensor_id.reserved1; + + if (temp_sensor_id != + cfg_bin->cfg_pkgs[i].cnst_info.sensor_id) { + ts_err("pkg:%d, sensor id contrast FAILED, reg:0x%02x", + i, addr); + ts_err("sensor_id from i2c:%d, sensor_id of cfg bin:%d", + temp_sensor_id, + cfg_bin->cfg_pkgs[i].cnst_info.sensor_id); + continue; + } + } + + /*contrast fw_mask, if this reg is null, skip this step*/ + addr = cfg_bin->cfg_pkgs[i].reg_info.fw_mask.addr; + if (!addr || !cfg_bin->cfg_pkgs[i].cnst_info.fw_mask[0]) { + ts_err("pkg:%d, fw_mask of cfg bin is NULL, Skip!!", i); + } else { + r = goodix_i2c_read(ts_dev, addr, + temp_fw_mask, sizeof(temp_fw_mask)); + if (r < 0) { + ts_err( + "read fw_mask FAILED, I2C ERROR, pkg: %d, fw_mask reg:0x%02x", + i, addr); + goto exit; + } else if (strncmp(temp_fw_mask, + cfg_bin->cfg_pkgs[i].cnst_info.fw_mask, + sizeof(temp_fw_mask))) { + ts_err("pkg:%d, fw_mask contrast FAILED, reg:0x%02x,", + i, addr); + ts_err("mask from i2c:%s, mask of cfg bin:%s", + temp_fw_mask, + cfg_bin->cfg_pkgs[i].cnst_info.fw_mask); + continue; + } + } + + /*contrast pid*/ + addr = cfg_bin->cfg_pkgs[i].reg_info.pid.addr; + read_len = cfg_bin->cfg_pkgs[i].reg_info.pid.reserved1; + if (!addr) { + ts_err("pkg:%d, pid reg is NULL", i); + continue; + } + if (read_len <= 0 || read_len > 8) { + ts_err("pkg:%d, hw_pid length ERROR, len:%d", + i, read_len); + continue; + } + r = goodix_i2c_read(ts_dev, addr, temp_pid, read_len); + if (r < 0) { + ts_err("read pid FAILED, I2C ERROR, pkg: %d, pid reg:0x%02x", + i, addr); + goto exit; + } else if (strncmp(temp_pid, + cfg_bin->cfg_pkgs[i].cnst_info.hw_pid, + read_len)) { + ts_err("pkg:%d, pid contrast FAILED, reg:0x%02x", + i, addr); + ts_err("pid from i2c:%s, pid of cfg bin:%s", + temp_pid, + cfg_bin->cfg_pkgs[i].cnst_info.hw_pid); + continue; + } + + /*contrast success, cfg_type*/ + if (cfg_bin->cfg_pkgs[i].cnst_info.cfg_type == 0x01) { + ts_info("find normal cfg_pkg SUCCESS"); + r = 0; + normal_pkg = &cfg_bin->cfg_pkgs[i]; + } + if (cfg_bin->cfg_pkgs[i].cnst_info.cfg_type == 0x03) { + ts_info("find high sense cfg_pkg SUCCESS"); + high_sense_pkg = &cfg_bin->cfg_pkgs[i]; + } + } + + /*get register address from normal_pkg*/ + if (!normal_pkg) { + ts_err("ERROR, none suitable normal_pkg exist in cfg_bin"); + /*ts_dev->ic_type = IC_TYPE_NONE;*/ + r = -EINVAL; + goto exit; + } else { + /*get ic type*/ + if (!strncmp(normal_pkg->cnst_info.ic_type, "nanjing", + sizeof(normal_pkg->cnst_info.ic_type))) + ts_dev->ic_type = IC_TYPE_NANJING; + else if (!strncmp(normal_pkg->cnst_info.ic_type, "normandy", + sizeof(normal_pkg->cnst_info.ic_type))) + ts_dev->ic_type = IC_TYPE_NORMANDY; + else + ts_err("get ic type FAILED, unknow ic type from cfg_bin:%s", + normal_pkg->cnst_info.ic_type); + + /*get register info*/ + ts_dev->reg.cfg_send_flag = + normal_pkg->reg_info.cfg_send_flag.addr; + + ts_dev->reg.version_base = + normal_pkg->reg_info.version_base.addr; + ts_dev->reg.version_len = + normal_pkg->reg_info.version_base.reserved1; + + ts_dev->reg.pid = normal_pkg->reg_info.pid.addr; + ts_dev->reg.pid_len = normal_pkg->reg_info.pid.reserved1; + + ts_dev->reg.vid = normal_pkg->reg_info.vid.addr; + ts_dev->reg.vid_len = normal_pkg->reg_info.vid.reserved1; + + ts_dev->reg.sensor_id = normal_pkg->reg_info.sensor_id.addr; + ts_dev->reg.sensor_id_mask = + normal_pkg->reg_info.sensor_id.reserved1; + + ts_dev->reg.fw_mask = normal_pkg->reg_info.fw_mask.addr; + ts_dev->reg.fw_status = normal_pkg->reg_info.fw_status.addr; + ts_dev->reg.cfg_addr = normal_pkg->reg_info.cfg_addr.addr; + ts_dev->reg.esd = normal_pkg->reg_info.esd.addr; + ts_dev->reg.command = normal_pkg->reg_info.command.addr; + ts_dev->reg.coor = normal_pkg->reg_info.coor.addr; + ts_dev->reg.gesture = normal_pkg->reg_info.gesture.addr; + ts_dev->reg.fw_request = normal_pkg->reg_info.fw_request.addr; + ts_dev->reg.proximity = normal_pkg->reg_info.proximity.addr; + } + + /*get configuration from pkgs*/ + if (normal_pkg) { + ts_info("normal cfg is found!"); + if (!ts_dev->normal_cfg) { + ts_dev->normal_cfg = devm_kzalloc(ts_dev->dev, + sizeof(*ts_dev->normal_cfg), GFP_KERNEL); + if (!ts_dev->normal_cfg) { + /*ts_err("Failed to alloc memory for normal cfg");*/ + return -ENOMEM; + } + mutex_init(&ts_dev->normal_cfg->lock); + } + + ts_dev->normal_cfg->length = normal_pkg->pkg_len - + TS_PKG_CONST_INFO_LEN - TS_PKG_REG_INFO_LEN; + memcpy(ts_dev->normal_cfg->data, + normal_pkg->cfg, + ts_dev->normal_cfg->length); + } + + if (high_sense_pkg) { + ts_info("high sense cfg is found!"); + if (!ts_dev->highsense_cfg) { + ts_dev->highsense_cfg = devm_kzalloc(ts_dev->dev, + sizeof(*ts_dev->highsense_cfg), GFP_KERNEL); + if (!ts_dev->highsense_cfg) { + /*ts_err( + *"Failed to alloc memory for high sense cfg"); + */ + return -ENOMEM; + } + mutex_init(&ts_dev->highsense_cfg->lock); + } + + ts_dev->highsense_cfg->length = high_sense_pkg->pkg_len - + TS_PKG_CONST_INFO_LEN - TS_PKG_REG_INFO_LEN; + memcpy(ts_dev->highsense_cfg->data, + high_sense_pkg->cfg, + ts_dev->highsense_cfg->length); + } + +exit: + return r; +} + + +int goodix_read_cfg_bin(struct goodix_ts_device *ts_dev, struct goodix_cfg_bin *cfg_bin) +{ + int r; + const struct firmware *firmware; + char cfg_bin_name[32] = {0x00}; + int i = 0; + + /*get cfg_bin_name*/ + if(ts_dev->board_data->cfg_bin_name) + strlcpy(cfg_bin_name, ts_dev->board_data->cfg_bin_name, + sizeof(cfg_bin_name)); + else + strlcpy(cfg_bin_name, TS_DEFAULT_CFG_BIN, + sizeof(cfg_bin_name)); + + ts_info("cfg_bin_name:%s", cfg_bin_name); + + for (i = 0; i < 2; i++) { + r = request_firmware(&firmware, cfg_bin_name, ts_dev->dev); + if (r < 0) { + ts_err("Cfg_bin image [%s] not available,error:%d, try_times:%d", + cfg_bin_name, r, i + 1); + msleep(GTP_1000_DLY_MS); + } else { + ts_info("Cfg_bin image [%s] is ready, try_times:%d", + cfg_bin_name, i + 1); + break; + } + } + if (i >= 2) { + ts_err("get cfg_bin FAILED"); + goto exit; + } + + if (firmware->size <= 0) { + ts_err("request_firmware, cfg_bin length ERROR,len:%zu", + firmware->size); + r = -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) { + /*ts_err("Allocate memory for cfg_bin->bin_data FAILED");*/ + r = -ENOMEM; + } + memcpy(cfg_bin->bin_data, firmware->data, cfg_bin->bin_data_len); + + r = 0; +exit: + if (firmware) { + release_firmware(firmware); + firmware = NULL; + } + return r; +} + +int goodix_read_cfg_bin_from_dts(struct device_node *node, + struct goodix_cfg_bin *cfg_bin) +{ + unsigned int len = 0; + struct property *prop = NULL; + + prop = of_find_property(node, "goodix_cfg_bin", &len); + if (!prop || !prop->value || len == 0) { + ts_err("Invalid cfg type, size:%u", len); + return -EINVAL; + } + + cfg_bin->bin_data_len = len; + /*allocate memory for cfg_bin->bin_data*/ + cfg_bin->bin_data = kzalloc(cfg_bin->bin_data_len, GFP_KERNEL); + if (!cfg_bin->bin_data) { + /*ts_err("Allocate memory for cfg_bin->bin_data FAILED");*/ + return -ENOMEM; + } + memcpy(cfg_bin->bin_data, prop->value, cfg_bin->bin_data_len); + + return 0; +} + +void goodix_cfg_pkg_leToCpu(struct goodix_cfg_package *pkg) +{ + if (!pkg) { + ts_err("cfg package is NULL"); + return; + } + /*package const_info*/ + pkg->cnst_info.pkg_len = le32_to_cpu(pkg->cnst_info.pkg_len); + pkg->cnst_info.x_res_offset = + le16_to_cpu(pkg->cnst_info.x_res_offset); + pkg->cnst_info.y_res_offset = + le16_to_cpu(pkg->cnst_info.y_res_offset); + pkg->cnst_info.trigger_offset = + le16_to_cpu(pkg->cnst_info.trigger_offset); + + /*package reg_info*/ + pkg->reg_info.cfg_send_flag.addr = + le16_to_cpu(pkg->reg_info.cfg_send_flag.addr); + pkg->reg_info.pid.addr = le16_to_cpu(pkg->reg_info.pid.addr); + pkg->reg_info.vid.addr = le16_to_cpu(pkg->reg_info.vid.addr); + pkg->reg_info.sensor_id.addr = + le16_to_cpu(pkg->reg_info.sensor_id.addr); + pkg->reg_info.fw_status.addr = + le16_to_cpu(pkg->reg_info.fw_status.addr); + pkg->reg_info.cfg_addr.addr = + le16_to_cpu(pkg->reg_info.cfg_addr.addr); + pkg->reg_info.esd.addr = + le16_to_cpu(pkg->reg_info.esd.addr); + pkg->reg_info.command.addr = + le16_to_cpu(pkg->reg_info.command.addr); + pkg->reg_info.coor.addr = + le16_to_cpu(pkg->reg_info.coor.addr); + pkg->reg_info.gesture.addr = + le16_to_cpu(pkg->reg_info.gesture.addr); + pkg->reg_info.fw_request.addr = + le16_to_cpu(pkg->reg_info.fw_request.addr); + pkg->reg_info.proximity.addr = + le16_to_cpu(pkg->reg_info.proximity.addr); +} + +void goodix_release_fb_notifier(struct goodix_ts_core *core_data) +{ +#if defined(CONFIG_DRM) + msm_drm_unregister_client(&core_data->fb_notifier); +#elif defined(CONFIG_FB) + fb_unregister_client(&core_data->fb_notifier); +#endif +} diff --git a/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_cfg_bin.h b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_cfg_bin.h new file mode 100644 index 000000000000..855ded6b1c01 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_cfg_bin.h @@ -0,0 +1,96 @@ +#ifndef _GOODIX_CFG_BIN_H_ +#define _GOODIX_CFG_BIN_H_ + +#include "goodix_ts_core.h" + +#define TS_DEFAULT_CFG_BIN "goodix_cfg_group2.bin" +#define TS_CFG_BIN_HEAD_LEN (sizeof(struct goodix_cfg_bin_head) + 6) +#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) + + +#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[15]; + u8 cfg_type; + u8 sensor_id; + u8 hw_pid[8]; + u8 hw_vid[8]; + u8 fw_mask[9]; + u8 fw_patch[4]; + 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[9]; +}; + +struct goodix_cfg_bin_head { + u32 bin_len; + u8 checksum; + u8 bin_version[4]; + 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; +}; + + +int goodix_cfg_bin_proc(void *data); + +int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin); + +int goodix_get_reg_and_cfg(struct goodix_ts_device *ts_dev, + struct goodix_cfg_bin *cfg_bin); + +int goodix_read_cfg_bin(struct goodix_ts_device *ts_dev, struct goodix_cfg_bin *cfg_bin); + +int goodix_read_cfg_bin_from_dts(struct device_node *node, + struct goodix_cfg_bin *cfg_bin); + +void goodix_cfg_pkg_leToCpu(struct goodix_cfg_package *pkg); + +int goodix_start_cfg_bin(struct goodix_ts_core *ts_core); + +void goodix_release_fb_notifier(struct goodix_ts_core *core_data); + +#endif diff --git a/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_default_fw.h b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_default_fw.h new file mode 100644 index 000000000000..95c768384132 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_default_fw.h @@ -0,0 +1,6 @@ +#ifndef _GOODIX_FIRMWARE_FW_H_ +#define _GOODIX_FIRMWARE_FW_H_ + +u8 goodix_default_fw[] = {0x00}; + +#endif diff --git a/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_gtx8_update.c b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_gtx8_update.c new file mode 100644 index 000000000000..7df507397f93 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_gtx8_update.c @@ -0,0 +1,1722 @@ +/* + * Goodix GTX5 Firmware Update Driver. + * + * Copyright (C) 2015 - 2016 Goodix, Inc. + * Authors: Yulong Cai + * + * 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 +#include "goodix_ts_core.h" +#include "goodix_cfg_bin.h" +#include "goodix_default_fw.h" +/* COMMON PART - START */ +#define TS_DEFAULT_FIRMWARE "goodix-GT9886P-beckham2.bin" + +#define FW_HEADER_SIZE 256 +#define FW_SUBSYS_INFO_SIZE 8 +#define FW_SUBSYS_INFO_OFFSET 32 +#define FW_SUBSYS_MAX_NUM 28 +#define FW_NAME_MAX_LEN 50 + +#define ISP_MAX_BUFFERSIZE (1024 * 4) + +#define HW_REG_CPU_CTRL 0x2180 +#define HW_REG_DSP_MCU_POWER 0x2010 +#define HW_REG_RESET 0x2184 +#define HW_REG_SCRAMBLE 0x2218 +#define HW_REG_BANK_SELECT 0x2048 +#define HW_REG_ACCESS_PATCH0 0x204D +#define HW_REG_EC_SRM_START 0x204F +#define HW_REG_CPU_RUN_FROM 0x4506 /* for nor_L is 0x4006 */ +#define HW_REG_ISP_RUN_FLAG 0x6006 +#define HW_REG_ISP_ADDR 0xC000 +#define HW_REG_ISP_BUFFER 0x6100 +#define HW_REG_SUBSYS_TYPE 0x6020 +#define HW_REG_FLASH_FLAG 0x6022 + +#define CPU_CTRL_PENDING 0x00 +#define CPU_CTRL_RUNNING 0x01 + +#define ISP_STAT_IDLE 0xFF +#define ISP_STAT_READY 0xAA +#define ISP_STAT_WRITING 0xCC +#define ISP_FLASH_ERROR 0xEE +#define ISP_FLASH_SUCCESS 0xDD +#define ISP_CMD_PREPARE 0x55 +#define ISP_CMD_FLASH 0xAA + +/*0: Header update + *1: request firmware update + */ +atomic_t fw_update_mode = ATOMIC_INIT(1); + +static struct goodix_ext_module *goodix_module = NULL; +/** + * fw_subsys_info - subsytem firmware infomation + * @type: sybsystem type + * @size: firmware size + * @flash_addr: flash address + * @data: firmware data + */ +struct fw_subsys_info { + u8 type; + u32 size; + u16 flash_addr; + const u8 *data; +}; + +#pragma pack(1) +/** + * firmware_info + * @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 + * @subsys: sybsystem info + */ +struct firmware_info { + u32 size; + u16 checksum; + u8 hw_pid[6]; + u8 hw_vid[3]; + u8 fw_pid[8]; + u8 fw_vid[4]; + u8 subsys_num; + u8 chip_type; + u8 protocol_ver; + u8 reserved[2]; + struct fw_subsys_info subsys[FW_SUBSYS_MAX_NUM]; +}; + +#pragma pack() + +/** + * firmware_data - firmware data structure + * @fw_info: firmware infomation + * @firmware: firmware data structure + */ +struct firmware_data { + struct firmware_info fw_info; + const struct firmware *firmware; +}; + +enum update_status { + UPSTA_NOTWORK = 0, + UPSTA_PREPARING, + UPSTA_UPDATING, + UPSTA_ABORT, + UPSTA_SUCCESS, + UPSTA_FAILED +}; + +/** + * fw_update_ctrl - sturcture used to control the + * firmware update process + * @status: update status + * @progress: indicate the progress of update + * @allow_reset: control the reset callback + * @allow_irq: control the irq callback + * @allow_suspend: control the suspend callback + * @allow_resume: allow resume callback + * @fw_data: firmware data + * @ts_dev: touch device + * @fw_name: firmware name + * @attr_fwimage: sysfs bin attrs, for storing fw image + * @fw_from_sysfs: whether the firmware image is loadind + * from sysfs + */ +struct fw_update_ctrl { + enum update_status status; + unsigned int progress; + bool force_update; + + bool allow_reset; + bool allow_irq; + bool allow_suspend; + bool allow_resume; + + struct firmware_data fw_data; + struct goodix_ts_device *ts_dev; + struct goodix_ts_core *core_data; + + char fw_name[FW_NAME_MAX_LEN]; + char fw_name_sysfs[FW_NAME_MAX_LEN]; + struct bin_attribute attr_fwimage; + bool fw_from_sysfs; +}; + +/** + * goodix_parse_firmware - parse firmware header infomation + * and subsystem infomation from firmware data buffer + * + * @fw_data: firmware struct, contains firmware header info + * and firmware data. + * return: 0 - OK, < 0 - error + */ +static int goodix_parse_firmware(struct firmware_data *fw_data) +{ + const struct firmware *firmware; + struct firmware_info *fw_info; + unsigned int i, fw_offset, info_offset; + u16 checksum; + int r = 0; + + if (!fw_data || !fw_data->firmware) { + ts_err("Invalid firmware data"); + return -EINVAL; + } + fw_info = &fw_data->fw_info; + + /* copy firmware head info */ + firmware = fw_data->firmware; + if (firmware->size < FW_SUBSYS_INFO_OFFSET) { + ts_err("Invalid firmware size:%zu", firmware->size); + r = -EINVAL; + goto err_size; + } + memcpy(fw_info, firmware->data, FW_SUBSYS_INFO_OFFSET); + + /* check firmware size */ + fw_info->size = be32_to_cpu(fw_info->size); + if (firmware->size != fw_info->size + 6) { + ts_err("Bad firmware, size not match"); + r = -EINVAL; + goto err_size; + } + + /* calculate checksum, note: sum of bytes, but check + * by u16 checksum + */ + for (i = 6, checksum = 0; i < firmware->size; i++) + checksum += firmware->data[i]; + + /* byte order change, and check */ + fw_info->checksum = be16_to_cpu(fw_info->checksum); + if (checksum != fw_info->checksum) { + ts_err("Bad firmware, cheksum error"); + r = -EINVAL; + goto err_size; + } + + if (fw_info->subsys_num > FW_SUBSYS_MAX_NUM) { + ts_err("Bad firmware, invalid subsys num: %d", + fw_info->subsys_num); + r = -EINVAL; + goto err_size; + } + + /* parse subsystem info */ + fw_offset = FW_HEADER_SIZE; + for (i = 0; i < fw_info->subsys_num; i++) { + info_offset = FW_SUBSYS_INFO_OFFSET + + i * FW_SUBSYS_INFO_SIZE; + + fw_info->subsys[i].type = + firmware->data[info_offset]; + fw_info->subsys[i].size = + be32_to_cpup((__be32 *) + &firmware->data[info_offset + 1]); + fw_info->subsys[i].flash_addr = + be16_to_cpup((__be16 *) + &firmware->data[info_offset + 5]); + + if (fw_offset > firmware->size) { + ts_err("Sybsys offset exceed Firmware size"); + goto err_size; + } + + fw_info->subsys[i].data = firmware->data + fw_offset; + fw_offset += fw_info->subsys[i].size; + } + + ts_info("Firmware package protocol: V%u", fw_info->protocol_ver); + ts_info("Fimware PID:GT%s", fw_info->fw_pid); + ts_info("Fimware VID:%02X%02X%02X", fw_info->fw_vid[0], + fw_info->fw_vid[1], fw_info->fw_vid[2]); + ts_info("Firmware chip type:%02X", fw_info->chip_type); + ts_info("Firmware size:%u", fw_info->size); + ts_info("Firmware subsystem num:%u", fw_info->subsys_num); +#ifdef CONFIG_GOODIX_DEBUG + for (i = 0; i < fw_info->subsys_num; i++) { + ts_debug("------------------------------------------"); + ts_debug("Index:%d", i); + ts_debug("Subsystem type:%02X", fw_info->subsys[i].type); + ts_debug("Subsystem size:%u", fw_info->subsys[i].size); + ts_debug("Subsystem flash_addr:%08X", + fw_info->subsys[i].flash_addr); + ts_debug("Subsystem Ptr:%p", fw_info->subsys[i].data); + } + ts_debug("------------------------------------------"); +#endif + +err_size: + return r; +} + +/** + * goodix_check_update - compare the version of firmware running in + * touch device with the version getting from the firmware file. + * @fw_info: firmware infomation to be compared + * return: 0 firmware in the touch device needs to be updated + * < 0 no need to update firmware + */ +static int goodix_check_update(struct goodix_ts_device *dev, + const struct firmware_info *fw_info) +{ + struct goodix_ts_version fw_ver; + int r = 0; + int res = 0; + + /* read version from chip, if we got invalid + * firmware version, maybe fimware in flash is + * incorrect, so we need to update firmware + */ + r = dev->hw_ops->read_version(dev, &fw_ver); + if (r == -EBUS) + return r; + + if (fw_ver.valid) { + if (memcmp(fw_ver.pid, fw_info->fw_pid, dev->reg.pid_len)) { + ts_err("Product ID is not match"); + return -EPERM; + } + + ts_info("fw_ver.vid = 0x%2x 0x%2x", fw_ver.vid[0], fw_ver.vid[1]); + ts_info("fw_info->fw_vid = 0x%2x 0x%2x", fw_info->fw_vid[0], fw_info->fw_vid[1]); + ts_info("dev->reg.vid_len = %d", dev->reg.vid_len); + res = memcmp(fw_ver.vid, fw_info->fw_vid, dev->reg.vid_len); + if (res == 0) { + ts_err("FW version is equal to the IC's"); + return -EPERM; + } else if (res > 0) { + ts_info("Warning: fw version is lower the IC's"); + } + } /* else invalid firmware, update firmware */ + + ts_info("Firmware needs to be updated"); + return 0; +} + +/** + * 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(struct goodix_ts_device *dev, + unsigned int addr, unsigned char *data, unsigned int len) +{ + u8 *cfm, cfm_buf[32]; + int r, i; + + if (len > sizeof(cfm_buf)) { + cfm = kzalloc(len, GFP_KERNEL); + if (!cfm) { + ts_err("Mem alloc failed"); + return -ENOMEM; + } + } else { + cfm = &cfm_buf[0]; + } + + for (i = 0; i < GOODIX_BUS_RETRY_TIMES; i++) { + r = dev->hw_ops->write_trans(dev, addr, data, len); + if (r < 0) + goto exit; + + r = dev->hw_ops->read_trans(dev, addr, cfm, len); + if (r < 0) + goto exit; + + if (memcmp(data, cfm, len)) { + r = -EMEMCMP; + continue; + } else { + r = 0; + break; + } + } + +exit: + if (cfm != &cfm_buf[0]) + kfree(cfm); + return r; +} + +static inline int goodix_reg_write(struct goodix_ts_device *dev, + unsigned int addr, unsigned char *data, unsigned int len) +{ + return dev->hw_ops->write_trans(dev, addr, data, len); +} + +static inline int goodix_reg_read(struct goodix_ts_device *dev, + unsigned int addr, unsigned char *data, unsigned int len) +{ + return dev->hw_ops->read_trans(dev, addr, data, len); +} + +#define NO_MAKE +#ifndef NO_MAKE +#define MAX_MASK_BUF_SIZE (16*1024) +static int goodix_load_mask(struct goodix_ts_device *ts_dev) +{ + const struct firmware *mask_fw; + const u8 *mask_name = "goodix_mask.bin"; + u8 reg_val[10] = {0}; + u32 total_size = 0, data_size = 0, offset = 0; + int r, i; + int index; + + ts_debug("Start load mask"); + r = request_firmware(&mask_fw, mask_name, ts_dev->dev); + if (r < 0) { + ts_err("Firmware image [%s] not available,errno:%d", + mask_name, r); + return r; + } + ts_info("Firmware image [%s] is ready, size = %zu", mask_name, + mask_fw->size); + + /* enable AHB access */ + reg_val[0] = 0x01; + r = goodix_reg_write(ts_dev, 0x2049, reg_val, 1); + if (r) { + ts_err("Failed enbale AHB access"); + goto mask_exit; + } + ts_debug("Success enable AHB access, Set 0x2049 --> 0x01"); + + /* switch to bank4 */ + reg_val[0] = 0x04; + r = goodix_reg_write(ts_dev, 0x2048, reg_val, 1); + if (r) { + ts_err("Failed switch to bank4"); + goto mask_exit; + } + ts_debug("Success switch to bank4, Set 0x2048 -->0x04"); + + total_size = mask_fw->size; + offset = 0; + index = 1; + while (total_size > 0) { + data_size = total_size > MAX_MASK_BUF_SIZE ? + MAX_MASK_BUF_SIZE : total_size; + ts_info("Flash firmware to %08x,size:%u bytes", + 0xC000 + offset, data_size); + + for (i = 0; i < 3; i++) { + r = goodix_reg_write_confirm(ts_dev, 0xC000, + (u8 *)mask_fw->data + offset, data_size); + if (!r) + break; + ts_info("Failed write mask data retry.."); + msleep(GTP_20_DLY_MS); + } + if (r) { + ts_err("Failed send mask"); + goto mask_exit; + } + offset += data_size; + total_size -= data_size; + /* switch to bank5 */ + if (index == 1) { + reg_val[0] = 0x05; + r = goodix_reg_write(ts_dev, 0x2048, reg_val, 1); + if (r) { + ts_err("Failed switch to bank5"); + goto mask_exit; + } + ts_debug("Success switch to bank5, Set 0x2048-->0x05"); + } + index++; + } + /* disable AHB access */ + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, 0x2049, reg_val, 1); + if (r) { + ts_err("Failed disbale AHB access"); + goto mask_exit; + } + ts_debug("Success diable AHB access, Set 0x2049-->0x00"); + ts_info("Success loak mask"); +mask_exit: + release_firmware(mask_fw); + return r; +} +#endif + +/** + * goodix_load_isp - load ISP program to deivce ram + * @dev: pointer to touch device + * @fw_data: firmware data + * return 0 ok, <0 error + */ +static int goodix_load_isp(struct goodix_ts_device *ts_dev, + struct firmware_data *fw_data) +{ + struct fw_subsys_info *fw_isp; + u8 reg_val[8] = {0x00}; + int r; + int i; + + fw_isp = &fw_data->fw_info.subsys[0]; + + ts_info("Loading ISP start"); + /* select bank0 */ + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_BANK_SELECT, + reg_val, 1); + if (r < 0) { + ts_err("Failed to select bank0"); + return r; + } + ts_debug("Success select bank0, Set 0x%x -->0x00", HW_REG_BANK_SELECT); + + /* enable bank0 access */ + reg_val[0] = 0x01; + r = goodix_reg_write(ts_dev, HW_REG_ACCESS_PATCH0, + reg_val, 1); + if (r < 0) { + ts_err("Failed to enable patch0 access"); + return r; + } + ts_debug("Success select bank0, Set 0x%x -->0x01", + HW_REG_ACCESS_PATCH0); + + r = goodix_reg_write_confirm(ts_dev, HW_REG_ISP_ADDR, + (u8 *)fw_isp->data, fw_isp->size); + if (r < 0) { + ts_err("Loading ISP error"); + return r; + } + + ts_debug("Success send ISP data to IC"); + + + /* forbid patch access */ + reg_val[0] = 0x00; + r = goodix_reg_write_confirm(ts_dev, HW_REG_ACCESS_PATCH0, + reg_val, 1); + if (r < 0) { + ts_err("Failed to disable patch0 access"); + return r; + } + ts_debug("Success forbit bank0 accedd, set 0x%x -->0x00", + HW_REG_ACCESS_PATCH0); + + /*clear 0x6006*/ + reg_val[0] = 0x00; + reg_val[1] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_ISP_RUN_FLAG, + reg_val, 2); + if (r < 0) { + ts_err("Failed to clear 0x%x", HW_REG_ISP_RUN_FLAG); + return r; + } + ts_debug("Success clear 0x%x", HW_REG_ISP_RUN_FLAG); + + /* TODO: change address 0xBDE6 set backdoor flag HW_REG_CPU_RUN_FROM */ + memset(reg_val, 0x55, 8); + r = goodix_reg_write(ts_dev, HW_REG_CPU_RUN_FROM, + reg_val, 8); + if (r < 0) { + ts_err("Failed set backdoor flag"); + return r; + } + ts_debug("Success write [8]0x55 to 0x%x", HW_REG_CPU_RUN_FROM); + + /* TODO: change reg_val 0x08---> 0x00 release ss51 */ + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_CPU_CTRL, + reg_val, 1); + if (r < 0) { + ts_err("Failed to run isp"); + return r; + } + ts_debug("Success run isp, set 0x%x-->0x00", HW_REG_CPU_CTRL); + + /* check isp work state */ + for (i = 0; i < 200; i++) { + r = goodix_reg_read(ts_dev, HW_REG_ISP_RUN_FLAG, + reg_val, 2); + if (r < 0 || (reg_val[0] == 0xAA && reg_val[1] == 0xBB)) + break; + usleep_range(GTP_5000_DLY_US, GTP_5100_DLY_US); + } + if (reg_val[0] == 0xAA && reg_val[1] == 0xBB) { + ts_info("ISP working OK"); + return 0; + } + ts_err("ISP not work,0x%x=0x%x, 0x%x=0x%x", + HW_REG_ISP_RUN_FLAG, reg_val[0], + HW_REG_ISP_RUN_FLAG + 1, reg_val[1]); + return -EFAULT; +} + +/** + * 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) +{ + struct goodix_ts_device *ts_dev = fwu_ctrl->ts_dev; + u8 reg_val[4] = { 0x00 }; + u8 temp_buf[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + int retry = 20; + int r; + + ts_dev->hw_ops->write(ts_dev, 0x4506, temp_buf, 8); + + /*reset IC*/ + fwu_ctrl->allow_reset = true; + ts_info("normandy firmware update, reset"); + gpio_direction_output(ts_dev->board_data->reset_gpio, 0); + udelay(GTP_2000_udelay); + gpio_direction_output(ts_dev->board_data->reset_gpio, 1); + usleep_range(GTP_10000_DLY_US, GTP_11000_DLY_US); + fwu_ctrl->allow_reset = false; + + retry = 20; + do { + reg_val[0] = 0x24; + r = goodix_reg_write_confirm(ts_dev, + HW_REG_CPU_CTRL, reg_val, 1); + if (r < 0) { + ts_info("Failed to hold ss51, retry"); + msleep(GTP_20_DLY_MS); + } else { + break; + } + } while (--retry); + if (!retry) { + ts_err("Failed hold ss51,return =%d", r); + return -EINVAL; + } + ts_debug("Success hold ss51"); + + /* enable DSP & MCU power */ + reg_val[0] = 0x00; + r = goodix_reg_write_confirm(ts_dev, HW_REG_DSP_MCU_POWER, reg_val, 1); + if (r < 0) { + ts_err("Failed enable DSP&MCU power"); + return r; + } + ts_debug("Success enabled DSP&MCU power,set 0x%x-->0x00", + HW_REG_DSP_MCU_POWER); + + /* disable watchdog timer */ + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, 0x204B, reg_val, 1); + if (r < 0) { + ts_err("Failed to clear cache"); + return r; + } + ts_debug("Success clear cache"); + + reg_val[0] = 0x95; + r = goodix_reg_write(ts_dev, 0x2318, reg_val, 1); + reg_val[0] = 0x00; + r |= goodix_reg_write(ts_dev, 0x20B0, reg_val, 1); + + reg_val[0] = 0x27; + r |= goodix_reg_write(ts_dev, 0x2318, reg_val, 1); + if (r < 0) { + ts_err("Failed to disable watchdog"); + return r; + } + ts_debug("Success disable watchdog"); + + /* set scramble */ + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_SCRAMBLE, reg_val, 1); + if (r < 0) { + ts_err("Failed to set scramble"); + return r; + } + ts_debug("Succcess set scramble"); + + /* load ISP code and run form isp */ + r = goodix_load_isp(ts_dev, &fwu_ctrl->fw_data); + if (r < 0) + ts_err("Failed lode and run isp"); + + return r; +} + +/** + * goodix_format_fw_packet - formate one flash packet + * @pkt: target firmware packet + * @flash_addr: flash address + * @size: packet size + * @data: packet data + */ +static int goodix_format_fw_packet(u8 *pkt, u32 flash_addr, + u16 len, const u8 *data) +{ + u16 checksum; + + if (!pkt || !data) + return -EINVAL; + + /* + * checksum rule:sum of data in one format is equal to zero + * data format: byte/le16/be16/le32/be32/le64/be64 + */ + pkt[0] = (len >> 8) & 0xff; + pkt[1] = len & 0xff; + pkt[2] = (flash_addr >> 16) & 0xff; + pkt[3] = (flash_addr >> 8) & 0xff; + memcpy(&pkt[4], data, len); + checksum = checksum_be16(pkt, len + 4); + checksum = 0 - checksum; + pkt[len + 4] = (checksum >> 8) & 0xff; + pkt[len + 5] = checksum & 0xff; + return 0; +} + +/** + * goodix_send_fw_packet - send one firmware packet to ISP + * @dev: target touch device + * @pkt: firmware packet + * return ok, <0 error + */ +static int goodix_send_fw_packet(struct goodix_ts_device *dev, u8 type, + u8 *pkt, u32 len) +{ + u8 reg_val[4]; + int r, i; + + if (!pkt) + return -EINVAL; + + r = goodix_reg_write_confirm(dev, HW_REG_ISP_BUFFER, pkt, len); + if (r < 0) { + ts_err("Failed to write firmware packet"); + return r; + } + + reg_val[0] = 0; + reg_val[1] = 0; + /* clear flash flag 0X6022 */ + r = goodix_reg_write_confirm(dev, HW_REG_FLASH_FLAG, reg_val, 2); + if (r < 0) { + ts_err("Faile to clear flash flag"); + return r; + } + + /* write subsystem type 0X8020*/ + reg_val[0] = type; + reg_val[1] = type; + r = goodix_reg_write_confirm(dev, HW_REG_SUBSYS_TYPE, reg_val, 2); + if (r < 0) { + ts_err("Failed write subsystem type to IC"); + return r; + } + + for (i = 0; i < 200; i++) { + r = goodix_reg_read(dev, HW_REG_FLASH_FLAG, reg_val, 2); + if (r < 0) { + ts_err("Failed read flash state"); + return r; + } + + /* flash haven't end */ + if (reg_val[0] == 0xAA && reg_val[1] == 0xAA) { + ts_debug("Flash not ending..."); + usleep_range(GTP_55000_DLY_US, GTP_56000_DLY_US); + continue; + } + if (reg_val[0] == 0xBB && reg_val[1] == 0xBB) { + /* read twice to confirm the result */ + r = goodix_reg_read(dev, HW_REG_FLASH_FLAG, reg_val, 2); + if (!r && reg_val[0] == 0xBB && reg_val[1] == 0xBB) { + ts_info("Flash subsystem ok"); + return 0; + } + } + if (reg_val[0] == 0xCC && reg_val[1] == 0xCC) { + ts_err(" Flash subsystem failed"); + return -EAGAIN; + } + if (reg_val[0] == 0xDD) { + ts_err("Subsystem checksum err"); + return -EAGAIN; + } + + usleep_range(GTP_250_DLY_US, GTP_260_DLY_US); + } + + ts_err("Wait for flash end timeout, 0x6022= %x %x", + reg_val[0], reg_val[1]); + return -EAGAIN; +} + +/** + * 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 infomation + * return: 0 ok, < 0 error + */ +static int goodix_flash_subsystem(struct goodix_ts_device *dev, + struct fw_subsys_info *subsys) +{ + u16 data_size, offset; + u32 total_size; + u32 subsys_base_addr = subsys->flash_addr << 8; + u8 *fw_packet; + int r = 0, i; + + /* + * 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 + 6, 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); + + /* format one firmware packet */ + r = goodix_format_fw_packet(fw_packet, + subsys_base_addr + offset, + data_size, &subsys->data[offset]); + if (r < 0) { + ts_err("Invalid packet params"); + goto exit; + } + + /* send one firmware packet, retry 3 time if send failed */ + for (i = 0; i < 3; i++) { + r = goodix_send_fw_packet(dev, subsys->type, + fw_packet, data_size + 6); + if (!r) + break; + } + if (r) { + ts_err("Failed flash subsystem"); + goto exit; + } + offset += data_size; + total_size -= data_size; + } /* end while */ + +exit: + 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 goodix_ts_device *dev, + struct firmware_data *fw_data) +{ + struct fw_update_ctrl *fw_ctrl; + struct firmware_info *fw_info; + struct fw_subsys_info *fw_x; + int retry = GOODIX_BUS_RETRY_TIMES; + int i, r = 0, fw_num, prog_step; + + /* start from subsystem 1, + * subsystem 0 is the ISP program + */ + fw_ctrl = container_of(fw_data, struct fw_update_ctrl, fw_data); + fw_info = &fw_data->fw_info; + fw_num = fw_info->subsys_num; + + /* we have 80% work here */ + prog_step = 80 / (fw_num - 1); + + for (i = 1; i < fw_num && retry;) { + ts_info("--- Start to flash subsystem[%d] ---", i); + fw_x = &fw_info->subsys[i]; + r = goodix_flash_subsystem(dev, fw_x); + if (r == 0) { + ts_info("--- End flash subsystem[%d]: OK ---", i); + fw_ctrl->progress += prog_step; + 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 goodix_ts_device *ts_dev, + struct fw_update_ctrl *fwu_ctrl) +{ + u8 reg_val[8] = {0}; + int r = 0; + + /* hold ss51 */ + reg_val[0] = 0x24; + r = goodix_reg_write(ts_dev, HW_REG_CPU_CTRL, + reg_val, 1); + if (r < 0) + ts_err("Failed to hold ss51"); + + /* clear back door flag */ + memset(reg_val, 0, sizeof(reg_val)); + r = goodix_reg_write(ts_dev, HW_REG_CPU_RUN_FROM, reg_val, 8); + if (r) { + ts_err("Failed set CPU run from normal firmware"); + return r; + } + + /* release ss51 */ + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_CPU_CTRL, reg_val, 1); + if (r < 0) + ts_err("Failed to run ss51"); + + /*reset*/ + r = ts_dev->hw_ops->reset(ts_dev); + + return r; +} + + +/** + * goodix_fw_update_proc - firmware update process, the entry of + * firmware update flow + * @fwu_ctrl: firmware control + * return: 0 ok, < 0 error + */ +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 r = 0; + + if (fwu_ctrl->status == UPSTA_PREPARING || + fwu_ctrl->status == UPSTA_UPDATING) { + ts_err("Firmware update already in progress"); + return 0; + } + + fwu_ctrl->progress = 0; + fwu_ctrl->status = UPSTA_PREPARING; + + r = goodix_parse_firmware(&fwu_ctrl->fw_data); + if (r < 0) { + fwu_ctrl->status = UPSTA_ABORT; + goto err_parse_fw; + } + + /* TODO: set force update flag*/ + fwu_ctrl->progress = 10; + if (fwu_ctrl->force_update == false) { + r = goodix_check_update(fwu_ctrl->ts_dev, + &fwu_ctrl->fw_data.fw_info); + if (r < 0) { + fwu_ctrl->status = UPSTA_ABORT; + goto err_check_update; + } + } + +start_update: + fwu_ctrl->progress = 20; + fwu_ctrl->status = UPSTA_UPDATING; /* show upgrading status */ + r = goodix_update_prepare(fwu_ctrl); + if ((r == -EBUS || r == -EAGAIN) && --retry0 > 0) { + ts_err("Bus error, retry prepare ISP:%d", + FW_UPDATE_RETRY - retry0); + goto start_update; + } else if (r < 0) { + ts_err("Failed to prepare ISP, exit update:%d", r); + fwu_ctrl->status = UPSTA_FAILED; + goto err_fw_prepare; + } + + /* progress: 20%~100% */ + r = goodix_flash_firmware(fwu_ctrl->ts_dev, &fwu_ctrl->fw_data); + if ((r == -EBUS || r == -ETIMEOUT) && --retry1 > 0) { + /* we will retry[twice] if returns bus error[i2c/spi] + * we will do hardware reset and re-prepare ISP and then retry + * flashing + */ + ts_err("Bus error, retry firmware update:%d", + FW_UPDATE_RETRY - retry1); + goto start_update; + } else if (r < 0) { + ts_err("Fatal error, exit update:%d", r); + fwu_ctrl->status = UPSTA_FAILED; + goto err_fw_flash; + } + + fwu_ctrl->status = UPSTA_SUCCESS; + +err_fw_flash: +err_fw_prepare: + goodix_update_finish(fwu_ctrl->ts_dev, fwu_ctrl); +err_check_update: +err_parse_fw: + if (fwu_ctrl->status == UPSTA_SUCCESS) + ts_info("Firmware update successfully"); + else if (fwu_ctrl->status == UPSTA_FAILED) + ts_err("Firmware update failed"); + + fwu_ctrl->progress = 100; /* 100% */ + + return r; +} +/* COMMON PART - END */ + +static struct goodix_ext_module goodix_fwu_module; + +/** + * 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->ts_dev->dev; + int r; + + ts_info("Request firmware image [%s]", name); + r = request_firmware(&fw_data->firmware, name, dev); + if (r < 0) + ts_err("Firmware image [%s] not available,errno:%d", name, r); + else + ts_info("Firmware image [%s] is ready", name); + return r; +} + +/** + * 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; + } +} + +#ifdef UPDATE_USING_KTHREAD +static int goodix_fw_update_thread(void *data) +#else +static int goodix_fw_update_thread(struct fw_update_ctrl *fwu_ctrl) +#endif +{ +#ifdef UPDATE_USING_KTHREAD + struct fw_update_ctrl *fwu_ctrl = data; +#endif + struct firmware *temp_firmware = NULL; + static DEFINE_MUTEX(fwu_lock); + int r; + + if (!fwu_ctrl) { + ts_err("Invaid thread params"); +#ifdef UPDATE_USING_KTHREAD + goodix_unregister_ext_module(&goodix_fwu_module); +#endif + return 0; + } + + if (!fwu_ctrl->core_data->update_from_sysfs) { + ts_info("Not update from sysfs, firmware won't be updated"); +#ifdef UPDATE_USING_KTHREAD + goodix_unregister_ext_module(&goodix_fwu_module); +#endif + return 0; + } + mutex_lock(&fwu_lock); + if (!fwu_ctrl->fw_from_sysfs) { + if (atomic_read(&fw_update_mode) == 0) { + ts_info("Firmware header update starts"); + temp_firmware = kzalloc(sizeof(struct firmware), + GFP_KERNEL); + if (!temp_firmware) { + goto out; + } + temp_firmware->size = sizeof(goodix_default_fw); + temp_firmware->data = goodix_default_fw; + fwu_ctrl->fw_data.firmware = temp_firmware; + } else if (atomic_read(&fw_update_mode) == 1) { + ts_info("Firmware request update starts"); + r = goodix_request_firmware(&fwu_ctrl->fw_data, + fwu_ctrl->fw_name); + if (r < 0) { + fwu_ctrl->status = UPSTA_ABORT; + fwu_ctrl->progress = 100; + goto out; + } + } + } else { + if (!fwu_ctrl->fw_data.firmware) { + ts_err("Invalid firmware from sysfs"); + fwu_ctrl->status = UPSTA_ABORT; + fwu_ctrl->progress = 100; + goto out; + } + } + + /* DONT allow reset/irq/suspend/resume during update */ + fwu_ctrl->allow_irq = false; + fwu_ctrl->allow_suspend = false; + fwu_ctrl->allow_resume = false; + fwu_ctrl->allow_reset = false; +#ifdef GTP_CHARGER + fwu_ctrl->core_data->ts_dev->board_data->charger_send_flage_enable = false; +#endif + goodix_ts_blocking_notify(NOTIFY_FWUPDATE_START, NULL); + + /* ready to update */ + goodix_fw_update_proc(fwu_ctrl); + + goodix_ts_blocking_notify(NOTIFY_FWUPDATE_END, NULL); + fwu_ctrl->allow_reset = true; + fwu_ctrl->allow_irq = true; + fwu_ctrl->allow_suspend = true; + fwu_ctrl->allow_resume = true; +#ifdef GTP_CHARGER + fwu_ctrl->core_data->ts_dev->board_data->charger_send_flage_enable = true; +#endif + /* clean */ + if (!fwu_ctrl->fw_from_sysfs) { + if (atomic_read(&fw_update_mode) == 0) { + kfree(fwu_ctrl->fw_data.firmware); + fwu_ctrl->fw_data.firmware = NULL; + temp_firmware = NULL; + } else if (atomic_read(&fw_update_mode) == 1) { + goodix_release_firmware(&fwu_ctrl->fw_data); + } + } else { + fwu_ctrl->fw_from_sysfs = false; + vfree(fwu_ctrl->fw_data.firmware); + fwu_ctrl->fw_data.firmware = NULL; + if (atomic_read(&fw_update_mode) == 0) + temp_firmware = NULL; + } + + /*parse cfg_group.bin*/ + if (!fwu_ctrl->core_data->cfg_group_parsed) + goodix_cfg_bin_proc(fwu_ctrl->core_data); + +out: +#ifdef UPDATE_USING_KTHREAD + goodix_unregister_ext_module(&goodix_fwu_module); +#endif + mutex_unlock(&fwu_lock); + atomic_set(&fw_update_mode, 0); + return 0; +} + +/* sysfs attributes */ +static ssize_t goodix_sysfs_update_en_store( + struct goodix_ext_module *module, + const char *buf, size_t count) +{ + int val = 0, r; + struct fw_update_ctrl *fw_ctrl = goodix_module->priv_data; + struct goodix_ts_board_data *ts_bdata = board_data(fw_ctrl->core_data); + + r = kstrtoint(buf, 10, &val); + if (r < 0) + return r; + + if (!r) { + /* find a valid firmware image name */ + if (ts_bdata && ts_bdata->fw_name) + strlcpy(fw_ctrl->fw_name, ts_bdata->fw_name, + sizeof(fw_ctrl->fw_name)); + else + strlcpy(fw_ctrl->fw_name, TS_DEFAULT_FIRMWARE, + sizeof(fw_ctrl->fw_name)); + + fw_ctrl->core_data->update_from_sysfs = true; + atomic_set(&fw_update_mode, 1); +#ifdef UPDATE_USING_KTHREAD + ts_info("Start to register ext module %s", __func__); + if (goodix_register_ext_module(&goodix_fwu_module)) { + return -EIO; + } +#else + goodix_fw_update_thread(fw_ctrl); +#endif + } + + return count; +} + +static ssize_t goodix_sysfs_update_progress_show( + struct goodix_ext_module *module, + char *buf) +{ + struct fw_update_ctrl *fw_ctrl = module->priv_data; + + return scnprintf(buf, PAGE_SIZE, "%d\n", fw_ctrl->progress); +} + +static ssize_t goodix_sysfs_update_result_show( + struct goodix_ext_module *module, + char *buf) +{ + char *result = NULL; + struct fw_update_ctrl *fw_ctrl = module->priv_data; + + ts_info("result show"); + switch (fw_ctrl->status) { + case UPSTA_NOTWORK: + result = "notwork"; + break; + case UPSTA_PREPARING: + result = "preparing"; + break; + case UPSTA_UPDATING: + result = "upgrading"; + break; + case UPSTA_ABORT: + result = "abort"; + break; + case UPSTA_SUCCESS: + result = "success"; + break; + case UPSTA_FAILED: + result = "failed"; + break; + default: + break; + } + + return scnprintf(buf, PAGE_SIZE, "%s\n", result); +} + +static ssize_t goodix_sysfs_update_fwversion_show( + struct goodix_ext_module *module, + char *buf) +{ + struct goodix_ts_version fw_ver; + struct fw_update_ctrl *fw_ctrl = module->priv_data; + int r = 0; + char str[5]; + + /* read version from chip */ + r = fw_ctrl->ts_dev->hw_ops->read_version(fw_ctrl->ts_dev, + &fw_ver); + if (!r) { + memcpy(str, fw_ver.pid, 4); + str[4] = '\0'; + return scnprintf(buf, PAGE_SIZE, + "PID:%s VID:%02x %02x %02x %02x SENSOR_ID:%d\n", + str, fw_ver.vid[0], fw_ver.vid[1], + fw_ver.vid[2], fw_ver.vid[3], fw_ver.sensor_id); + } + return 0; +} + +static ssize_t goodix_sysfs_fwsize_show(struct goodix_ext_module *module, + char *buf) +{ + struct fw_update_ctrl *fw_ctrl = module->priv_data; + int r = -EINVAL; + + if (fw_ctrl && fw_ctrl->fw_data.firmware) + r = snprintf(buf, PAGE_SIZE, "%zu\n", + fw_ctrl->fw_data.firmware->size); + return r; +} + +static ssize_t goodix_sysfs_fwsize_store(struct goodix_ext_module *module, + const char *buf, size_t count) +{ + struct fw_update_ctrl *fw_ctrl = module->priv_data; + struct firmware *fw; + u8 **data; + size_t size = 0; + + if (!fw_ctrl) + return -EINVAL; + + if (sscanf(buf, "%zu", &size) < 0 || !size) { + ts_err("Failed to get fwsize"); + return -EFAULT; + } + + fw = vmalloc(sizeof(*fw) + size); + if (fw == NULL) { + return -ENOMEM; + } + + memset(fw, 0x00, sizeof(*fw) + size); + data = (u8 **)&fw->data; + *data = (u8 *)fw + sizeof(struct firmware); + fw->size = size; + fw_ctrl->fw_data.firmware = fw; + fw_ctrl->fw_from_sysfs = true; + + return count; +} + +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 fw_update_ctrl *fw_ctrl; + struct firmware_data *fw_data; + + fw_ctrl = container_of(attr, struct fw_update_ctrl, + attr_fwimage); + fw_data = &fw_ctrl->fw_data; + + if (!fw_data->firmware) { + ts_err("Need set fw image size first"); + return -ENOMEM; + } + + if (fw_data->firmware->size == 0) { + ts_err("Invalid firmware size"); + return -EINVAL; + } + + if (pos + count > fw_data->firmware->size) + return -EFAULT; + + memcpy((u8 *)&fw_data->firmware->data[pos], buf, count); + fw_ctrl->force_update = true; + + return count; +} + +static ssize_t goodix_sysfs_force_update_store( + struct goodix_ext_module *module, + const char *buf, size_t count) +{ + struct fw_update_ctrl *fw_ctrl = module->priv_data; + int val = 0, r; + + r = kstrtoint(buf, 10, &val); + if (r < 0) + return r; + + if (!r) + fw_ctrl->force_update = true; + else + fw_ctrl->force_update = false; + + return count; +} + +static struct goodix_ext_attribute goodix_fwu_attrs[] = { + __EXTMOD_ATTR(update_en, 0222, NULL, goodix_sysfs_update_en_store), + __EXTMOD_ATTR(progress, 0444, goodix_sysfs_update_progress_show, NULL), + __EXTMOD_ATTR(result, 0444, goodix_sysfs_update_result_show, NULL), + __EXTMOD_ATTR(fwversion, 0444, + goodix_sysfs_update_fwversion_show, NULL), + __EXTMOD_ATTR(fwsize, 0664, goodix_sysfs_fwsize_show, + goodix_sysfs_fwsize_store), + __EXTMOD_ATTR(force_update, 0222, NULL, + goodix_sysfs_force_update_store), +}; + +static int goodix_syfs_init(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + struct fw_update_ctrl *fw_ctrl = module->priv_data; + struct kobj_type *ktype; + int ret = 0, i; + + ktype = goodix_get_default_ktype(); + ret = kobject_init_and_add(&module->kobj, + ktype, + &core_data->pdev->dev.kobj, + "fwupdate"); + if (ret) { + ts_err("Create fwupdate sysfs node error!"); + goto exit_sysfs_init; + } + + for (i = 0; i < ARRAY_SIZE(goodix_fwu_attrs); i++) { + if (sysfs_create_file(&module->kobj, + &goodix_fwu_attrs[i].attr)) { + ts_err("Create sysfs attr file error"); + kobject_put(&module->kobj); + ret = -EINVAL; + goto exit_sysfs_init; + } + } + + 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(&module->kobj, + &fw_ctrl->attr_fwimage); + +exit_sysfs_init: + return ret; +} + +/* Motorola feature sysfs */ +static ssize_t goodix_sysfs_force_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fw_update_ctrl *fw_ctrl = goodix_module->priv_data; + int val = 0, r; + + r = kstrtoint(buf, 10, &val); + if (r < 0) + return r; + + if (!r) + fw_ctrl->force_update = true; + else + fw_ctrl->force_update = false; + + return count; +} + +static ssize_t goodix_sysfs_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct fw_update_ctrl *fw_ctrl = goodix_module->priv_data; + int retval; + + if (count > FW_NAME_MAX_LEN) { + ts_info("FW filename is too long"); + retval = -EINVAL; + goto exit; + } + + if (fw_ctrl->status == UPSTA_PREPARING || + fw_ctrl->status == UPSTA_UPDATING) { + ts_err("Firmware update already in progress"); + retval = -EINVAL; + goto exit; + } + + fw_ctrl->core_data->update_from_sysfs = true; + strlcpy(fw_ctrl->fw_name_sysfs, buf, + count); + + if (fw_ctrl->fw_name_sysfs[0] != 0) { + ts_info("%s: Using specific name from sysfs:%s", + __func__, fw_ctrl->fw_name_sysfs); + strlcpy(fw_ctrl->fw_name, fw_ctrl->fw_name_sysfs, + sizeof(fw_ctrl->fw_name)); + fw_ctrl->fw_name_sysfs[0] = 0; + } + + atomic_set(&fw_update_mode, 1); +#ifdef UPDATE_USING_KTHREAD + ts_info("Start to register ext module %s", __func__); + if (goodix_register_ext_module(&goodix_fwu_module)) { + retval = -EIO; + goto exit; + } +#else + goodix_fw_update_thread(fw_ctrl); +#endif + return count; + +exit: + return retval; +} + +static ssize_t goodix_sysfs_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fw_update_ctrl *fw_ctrl = goodix_module->priv_data; + bool val; + + val = fw_ctrl->core_data->gtp_suspended; + + return scnprintf(buf, PAGE_SIZE, "%d\n", + val == false); +} + +static ssize_t goodix_sysfs_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fw_update_ctrl *fw_ctrl = goodix_module->priv_data; + + return scnprintf(buf, PAGE_SIZE, "%d\n", + (fw_ctrl->status == UPSTA_PREPARING || + fw_ctrl->status == UPSTA_UPDATING) ? 1 : 0); +} + +static ssize_t goodix_sysfs_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fw_update_ctrl *fw_ctrl = goodix_module->priv_data; + struct goodix_ts_core *core_data = fw_ctrl->core_data; + struct goodix_ts_device *ts_dev = core_data->ts_dev; + + return scnprintf(buf, PAGE_SIZE, "%s\n", + ts_dev->name); +} + +/* show buildid module infomation */ +static ssize_t goodix_sysfs_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fw_update_ctrl *fw_ctrl = goodix_module->priv_data; + struct goodix_ts_core *core_data = fw_ctrl->core_data; + struct goodix_ts_device *ts_dev = core_data->ts_dev; + struct goodix_ts_version chip_ver; + int r, cnt = 0; + + if (ts_dev->hw_ops->read_version) { + r = ts_dev->hw_ops->read_version(ts_dev, &chip_ver); + if (r == -EBUS) { + ts_err("read version error with -EBUS"); + return cnt; + } else if (!r && chip_ver.valid) { + cnt = snprintf(buf, PAGE_SIZE, "%02x-%02x\n", + chip_ver.vid[1],chip_ver.vid[3]); + } else { + cnt = snprintf(buf, PAGE_SIZE, "%02x-%02x\n", 0, 0); + } + } + + return cnt; +} + +static struct device_attribute moto_attrs[] = { + __ATTR(forcereflash, S_IWUSR | S_IWGRP, + NULL, + goodix_sysfs_force_reflash_store), + __ATTR(doreflash, S_IWUSR | S_IWGRP, + NULL, + goodix_sysfs_do_reflash_store), + __ATTR(poweron, S_IRUSR | S_IRGRP, + goodix_sysfs_poweron_show, + NULL), + __ATTR(flashprog, S_IRUSR | S_IRGRP, + goodix_sysfs_flashprog_show, + NULL), + __ATTR(productinfo, S_IRUGO, + goodix_sysfs_productinfo_show, + NULL), + __ATTR(buildid, S_IRUGO, + goodix_sysfs_buildid_show, + NULL), +}; + +static int goodix_moto_sysfs_init(struct goodix_ext_module *module) +{ + struct fw_update_ctrl *fw_ctrl = module->priv_data; + struct i2c_client *client = to_i2c_client(fw_ctrl->ts_dev->dev); + int ret = 0, i; + + for (i = 0; i < ARRAY_SIZE(moto_attrs); i++) { + if (sysfs_create_file(&client->dev.kobj, + &moto_attrs[i].attr)) { + ts_err("Create moto sysfs attr file error"); + ret = -EINVAL; + goto exit_sysfs_init; + } + } + +exit_sysfs_init: + return ret; +} + +static int goodix_fw_update_init(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + struct goodix_ts_board_data *ts_bdata = board_data(core_data); +#ifdef UPDATE_USING_KTHREAD + struct task_struct *fwu_thrd; +#endif + struct fw_update_ctrl *fwu_ctrl; + static bool init_sysfs = true; + static bool init_moto_sysfs = true; + + if (!core_data->ts_dev) + return -ENODEV; + + if (!module->priv_data) { + module->priv_data = kzalloc(sizeof(struct fw_update_ctrl), + GFP_KERNEL); + if (!module->priv_data) { + return -ENOMEM; + } + } + + if (goodix_module) { + ts_info("%s: Handle already exists", + __func__); + } + + goodix_module = module; + fwu_ctrl = module->priv_data; + fwu_ctrl->ts_dev = core_data->ts_dev; + fwu_ctrl->allow_reset = true; + fwu_ctrl->allow_irq = true; + fwu_ctrl->allow_suspend = true; + fwu_ctrl->allow_resume = true; + fwu_ctrl->core_data = core_data; + + /* find a valid firmware image name */ + if (ts_bdata && ts_bdata->fw_name) + strlcpy(fwu_ctrl->fw_name, ts_bdata->fw_name, + sizeof(fwu_ctrl->fw_name)); + else + strlcpy(fwu_ctrl->fw_name, TS_DEFAULT_FIRMWARE, + sizeof(fwu_ctrl->fw_name)); + + /* create sysfs interface */ + if (init_sysfs) { + if (!goodix_syfs_init(core_data, module)) + init_sysfs = false; + } + + /* create motorola sysfs interface */ + if (init_moto_sysfs) { + if (!goodix_moto_sysfs_init(module)) + init_moto_sysfs = false; + } + +#ifdef UPDATE_USING_KTHREAD + /* create and run update thread */ + fwu_thrd = kthread_run(goodix_fw_update_thread, + module->priv_data, "goodix-fwu"); + if (IS_ERR_OR_NULL(fwu_thrd)) { + ts_err("Failed to create update thread:%ld", + PTR_ERR(fwu_thrd)); + return -EFAULT; + } +#endif + return 0; +} + +static int goodix_fw_update_exit(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + return 0; +} + +static int goodix_fw_before_suspend(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + struct fw_update_ctrl *fwu_ctrl = module->priv_data; + + return fwu_ctrl->allow_suspend ? + EVT_HANDLED : EVT_CANCEL_SUSPEND; +} + +static int goodix_fw_before_resume(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + struct fw_update_ctrl *fwu_ctrl = module->priv_data; + + return fwu_ctrl->allow_resume ? + EVT_HANDLED : EVT_CANCEL_RESUME; +} + +static int goodix_fw_after_resume(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + return 0; +} + +static int goodix_fw_irq_event(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + struct fw_update_ctrl *fwu_ctrl = module->priv_data; + + return fwu_ctrl->allow_irq ? + EVT_HANDLED : EVT_CANCEL_IRQEVT; +} + +static int goodix_fw_before_reset(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + struct fw_update_ctrl *fwu_ctrl = module->priv_data; + + return fwu_ctrl->allow_reset ? + EVT_HANDLED : EVT_CANCEL_RESET; +} + +static const struct goodix_ext_module_funcs goodix_ext_funcs = { + .init = goodix_fw_update_init, + .exit = goodix_fw_update_exit, + .before_reset = goodix_fw_before_reset, + .after_reset = NULL, + .before_suspend = goodix_fw_before_suspend, + .after_suspend = NULL, + .before_resume = goodix_fw_before_resume, + .after_resume = goodix_fw_after_resume, + .irq_event = goodix_fw_irq_event, +}; + +static struct goodix_ext_module goodix_fwu_module = { + .name = "goodix-fwu", + .funcs = &goodix_ext_funcs, + .priority = EXTMOD_PRIO_FWUPDATE, +}; + +int __init goodix_fwu_module_init(void) +{ + ts_info("fwu_init"); + return goodix_register_ext_module(&goodix_fwu_module); +} + +void __exit goodix_fwu_module_exit(void) +{ + /*return;*/ +} diff --git a/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_ts_core.c b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_ts_core.c new file mode 100644 index 000000000000..e0812b7f7d5b --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_ts_core.c @@ -0,0 +1,2776 @@ + /* + * Goodix Touchscreen Driver + * Core layer of touchdriver architecture. + * + * Copyright (C) 2015 - 2016 Goodix, Inc. + * Authors: Yulong Cai + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "goodix_ts_core.h" +#include +#ifdef GTP_CHARGER +#include +#endif + +#define INPUT_TYPE_B_PROTOCOL +#define GOOIDX_INPUT_PHYS "goodix_ts/input0" +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" + +#define GT_VTG_MIN_UV 3000000 +#define GT_VTG_MAX_UV 3000000 +#define GT_I2C_VTG_MIN_UV 1800000 +#define GT_I2C_VTG_MAX_UV 1800000 + +int goodix_start_cfg_bin(struct goodix_ts_core *ts_core); +void goodix_release_fb_notifier(struct goodix_ts_core *core_data); + +struct goodix_module goodix_modules; +static struct class *touchscreen_class; +static struct device *touchscreen_class_dev; +static struct class *gesture_class; +static struct device *gesture_class_dev; + +/** + * __do_register_ext_module - register external module + * to register into touch core modules structure + */ +static void __do_register_ext_module(struct work_struct *work) +{ + struct goodix_ext_module *module = + container_of(work, struct goodix_ext_module, work); + struct goodix_ext_module *ext_module; + struct list_head *insert_point = &goodix_modules.head; + + ts_info("__do_register_ext_module IN, goodix_modules.core_exit:%d", + goodix_modules.core_exit); + + /* waitting for core layer */ + if (!wait_for_completion_timeout(&goodix_modules.core_comp, 25 * HZ)) { + ts_err("Module [%s] timeout", module->name); + return; + } + + /* driver probe failed */ + if (goodix_modules.core_exit) { + ts_err("Can't register ext_module, core exit"); + return; + } + + ts_info("start register ext_module"); + + /* prority level *must* be set */ + if (module->priority == EXTMOD_PRIO_RESERVED) { + ts_err("Priority of module [%s] needs to be set", + module->name); + return; + } + + mutex_lock(&goodix_modules.mutex); + if (!list_empty(&goodix_modules.head)) { + list_for_each_entry(ext_module, &goodix_modules.head, list) { + if (ext_module == module) { + ts_info("Module [%s] already exists", + module->name); + mutex_unlock(&goodix_modules.mutex); + return; + } + } + + list_for_each_entry(ext_module, &goodix_modules.head, list) { + /* small value of priority have + * higher priority level + */ + if (ext_module->priority >= module->priority) { + insert_point = &ext_module->list; + break; + } + } + /* else module will be inserted + * to goodix_modules->head + */ + } + + 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; + } + } + + list_add(&module->list, insert_point->prev); + goodix_modules.count++; + mutex_unlock(&goodix_modules.mutex); + + ts_info("Module [%s] registered,priority:%u", + module->name, + module->priority); + /*return;*/ +} + +/** + * 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) +{ + if (!module) + return -EINVAL; + + if (!goodix_modules.initilized) { + goodix_modules.initilized = true; + goodix_modules.core_exit = true; + INIT_LIST_HEAD(&goodix_modules.head); + mutex_init(&goodix_modules.mutex); + init_completion(&goodix_modules.core_comp); + } + + msleep(GTP_500_DLY_MS); + ts_info("goodix_register_ext_module IN"); + + INIT_WORK(&module->work, __do_register_ext_module); + schedule_work(&module->work); + + ts_info("goodix_register_ext_module OUT"); + + + return 0; +} +EXPORT_SYMBOL_GPL(goodix_register_ext_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; + 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(ext_module, &goodix_modules.head, list) { + if (ext_module == module) { + found = true; + break; + } + } + } else { + mutex_unlock(&goodix_modules.mutex); + return -EFAULT; + } + + if (!found) { + ts_err("Module [%s] never registed", + module->name); + mutex_unlock(&goodix_modules.mutex); + return -EFAULT; + } + + list_del(&module->list); + mutex_unlock(&goodix_modules.mutex); + + if (module->funcs && module->funcs->exit) + module->funcs->exit(goodix_modules.core_data, module); + goodix_modules.count--; + + ts_info("Module [%s] unregistered", + module->name ? module->name : " "); + return 0; +} +EXPORT_SYMBOL_GPL(goodix_unregister_ext_module); + +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; +} +EXPORT_SYMBOL_GPL(goodix_get_default_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; +} +EXPORT_SYMBOL_GPL(goodix_get_default_kobj); + +/* debug fs */ +struct debugfs_buf { + struct debugfs_blob_wrapper buf; + int pos; + struct dentry *dentry; +} goodix_dbg; + +void goodix_msg_printf(const char *fmt, ...) +{ + va_list args; + int r; + + if (goodix_dbg.pos < goodix_dbg.buf.size) { + va_start(args, fmt); + r = vscnprintf(goodix_dbg.buf.data + goodix_dbg.pos, + goodix_dbg.buf.size - 1, fmt, args); + goodix_dbg.pos += r; + va_end(args); + } +} +EXPORT_SYMBOL_GPL(goodix_msg_printf); + +static int goodix_debugfs_init(void) +{ + struct dentry *r_b; + + goodix_dbg.buf.size = PAGE_SIZE; + goodix_dbg.pos = 0; + goodix_dbg.buf.data = kzalloc(goodix_dbg.buf.size, GFP_KERNEL); + if (goodix_dbg.buf.data == NULL) { + pr_err("Debugfs init failed\n"); + goto exit; + } + r_b = debugfs_create_blob("goodix_ts", 0644, NULL, &goodix_dbg.buf); + if (!r_b) { + pr_err("Debugfs create failed\n"); + return -ENOENT; + } + goodix_dbg.dentry = r_b; + +exit: + return 0; +} + +static void goodix_debugfs_exit(void) +{ + debugfs_remove(goodix_dbg.dentry); + goodix_dbg.dentry = NULL; + pr_info("Debugfs module exit\n"); +} + +/* show external module infomation */ +static ssize_t goodix_ts_extmod_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct goodix_ext_module *module; + size_t offset = 0; + int r; + + mutex_lock(&goodix_modules.mutex); + if (!list_empty(&goodix_modules.head)) { + list_for_each_entry(module, &goodix_modules.head, list) { + r = snprintf(&buf[offset], PAGE_SIZE, + "priority:%u module:%s\n", + module->priority, module->name); + if (r < 0) { + mutex_unlock(&goodix_modules.mutex); + return -EINVAL; + } + offset += r; + } + } + + mutex_unlock(&goodix_modules.mutex); + return offset; +} + +/* show driver infomation */ +static ssize_t goodix_ts_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 goodix_ts_chip_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct goodix_ts_core *core_data = + dev_get_drvdata(dev); + struct goodix_ts_device *ts_dev = core_data->ts_dev; + struct goodix_ts_version chip_ver; + int r, cnt = 0; + + cnt += snprintf(buf, PAGE_SIZE, + "TouchDeviceName:%s\n", ts_dev->name); + if (ts_dev->hw_ops->read_version) { + r = ts_dev->hw_ops->read_version(ts_dev, &chip_ver); + if (!r && chip_ver.valid) { + cnt += snprintf(&buf[cnt], PAGE_SIZE, + "PID:%s\nVID:%02x %02x %02x %02x\nSensorID:%02x\n", + chip_ver.pid, chip_ver.vid[0], + chip_ver.vid[1], chip_ver.vid[2], + chip_ver.vid[3], chip_ver.sensor_id); + } + } + + return cnt; +} + +/* show chip configuration data */ +static ssize_t goodix_ts_config_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct goodix_ts_core *core_data = + dev_get_drvdata(dev); + struct goodix_ts_device *ts_dev = core_data->ts_dev; + struct goodix_ts_config *ncfg = ts_dev->normal_cfg; + u8 *data; + int i, r, offset = 0; + + if (ncfg && ncfg->initialized && ncfg->length < PAGE_SIZE) { + data = kmalloc(ncfg->length, GFP_KERNEL); + if (!data) + return -ENOMEM; + + r = ts_dev->hw_ops->read(ts_dev, ncfg->reg_base, + &data[0], ncfg->length); + if (r < 0) { + kfree(data); + return -EINVAL; + } + + for (i = 0; i < ncfg->length; i++) { + if (i != 0 && i % 20 == 0) + buf[offset++] = '\n'; + offset += snprintf(&buf[offset], PAGE_SIZE - offset, + "%02x ", data[i]); + } + buf[offset++] = '\n'; + buf[offset++] = '\0'; + kfree(data); + return offset; + } + + return -EINVAL; +} + +/* 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_device *ts_dev = core_data->ts_dev; + int en; + + if (kstrtoint(buf, 10, &en) != 0) + return -EINVAL; + + if (en != 1) + return -EINVAL; + + if (ts_dev->hw_ops->reset) + ts_dev->hw_ops->reset(ts_dev); + return count; + +} + +static ssize_t goodix_ts_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_device *ts_dev = core_data->ts_dev; + int ret, i = 0, offset; + char *cfg_buf; + + cfg_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + disable_irq(core_data->irq); + if (ts_dev->hw_ops->read_config) + ret = ts_dev->hw_ops->read_config(ts_dev, cfg_buf, 0); + else + ret = -EINVAL; + enable_irq(core_data->irq); + + snprintf(&buf[0], PAGE_SIZE, "%d ", ret); + +#define LINE_NUM 20 + offset = 0; + if (ret > 0) { + for (i = 0; i < ret; i++) { + if (i != 0 && i % LINE_NUM == 0) + ts_info("%s", &buf[offset - (LINE_NUM * 3)]); + + offset += snprintf(&buf[offset], PAGE_SIZE, + "%02x ", cfg_buf[i]); + } + } + + if (i % LINE_NUM) + ts_info("%s", &buf[offset - (i % LINE_NUM) * 3]); + + kfree(cfg_buf); + return ret; +} + +static int goodix_ts_convert_0x_data(const u8 *buf, + int buf_size, + unsigned char *out_buf, + int *out_buf_len) +{ + int i, m_size = 0; + int temp_index = 0; + + for (i = 0; i < buf_size; i++) { + if (buf[i] == 'x' || buf[i] == 'X') + m_size++; + } + ts_info("***m_size:%d", m_size); + + if (m_size <= 1) { + ts_err("cfg file ERROR, valid data count:%d\n", m_size); + return -EINVAL; + } + *out_buf_len = m_size; + + for (i = 0; i < buf_size; i++) { + if (buf[i] == 'x' || buf[i] == 'X') { + if (temp_index >= m_size) { + ts_err("exchange cfg data error, overflow, temp_index:%d,m_size:%d\n", + temp_index, m_size); + return -EINVAL; + } + if (buf[i + 1] >= '0' && buf[i + 1] <= '9') + out_buf[temp_index] = (buf[i + 1] - '0') << 4; + else if (buf[i + 1] >= 'a' && buf[i + 1] <= 'f') + out_buf[temp_index] = + (buf[i + 1] - 'a' + 10) << 4; + else if (buf[i + 1] >= 'A' && buf[i + 1] <= 'F') + out_buf[temp_index] = + (buf[i + 1] - 'A' + 10) << 4; + + if (buf[i + 2] >= '0' && buf[i + 2] <= '9') + out_buf[temp_index] += (buf[i + 2] - '0'); + else if (buf[i + 2] >= 'a' && buf[i + 2] <= 'f') + out_buf[temp_index] += (buf[i + 2] - 'a' + 10); + else if (buf[i + 2] >= 'A' && buf[i + 2] <= 'F') + out_buf[temp_index] += (buf[i + 2] - 'A' + 10); + + temp_index++; + } + } + return 0; +} + +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_device *ts_dev = core_data->ts_dev; + int en, r; + const struct firmware *cfg_img; + struct goodix_ts_config *config = NULL; + + ts_info("******IN"); + + if (kstrtoint(buf, 10, &en) != 0) + return -EINVAL; + + if (en != 1) + return -EINVAL; + + ts_info("en:%d", en); + + disable_irq(core_data->irq); + + /*request configuration*/ + r = request_firmware(&cfg_img, GOODIX_DEFAULT_CFG_NAME, dev); + if (r < 0) { + ts_err("cfg file [%s] not available,errno:%d", + GOODIX_DEFAULT_CFG_NAME, r); + goto exit; + } else + ts_info("cfg file [%s] is ready", GOODIX_DEFAULT_CFG_NAME); + + config = kzalloc(sizeof(struct goodix_ts_config), GFP_KERNEL); + if (config == NULL) { + goto exit; + } + + /*parse cfg data*/ + if (goodix_ts_convert_0x_data(cfg_img->data, cfg_img->size, + config->data, &config->length)) { + ts_err("convert config data FAILED"); + goto exit; + } + + config->reg_base = ts_dev->reg.cfg_addr; + mutex_init(&config->lock); + config->initialized = true; + + if (ts_dev->hw_ops->send_config) + ts_dev->hw_ops->send_config(ts_dev, config); + +exit: + enable_irq(core_data->irq); + + kfree(config); + + if (cfg_img) { + release_firmware(cfg_img); + cfg_img = NULL; + } + + ts_info("******OUT"); + return count; +} + +/* 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"); + 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); + int en; + + if (kstrtoint(buf, 10, &en) != 0) + return -EINVAL; + + goodix_ts_irq_enable(core_data, en); + return count; +} + +static u16 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) +{ + int ret; + struct goodix_ts_core *core_data = + dev_get_drvdata(dev); + struct goodix_ts_device *ts_dev = core_data->ts_dev; + + if (!rw_addr || !rw_len) { + ts_err("address(0x%x) and length(%d) cann't be null\n", + rw_addr, rw_len); + return -EINVAL; + } + if (rw_flag == 1) { + ret = ts_dev->hw_ops->read(ts_dev, rw_addr, show_buf, rw_len); + if (ret) { + ts_err("failed read addr(%x) length(%d)\n", rw_addr, + rw_len); + return snprintf(buf, PAGE_SIZE, + "failed read addr(%x), len(%d)\n", + rw_addr, rw_len); + } else { + return snprintf(buf, PAGE_SIZE, "0x%x,%d {%*ph}\n", + rw_addr, rw_len, rw_len, show_buf); + } + } + + return snprintf(buf, PAGE_SIZE, "invalied rw_flag %d\n", rw_flag); +} + +static ssize_t goodix_ts_reg_rw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + char *pos = NULL; + char *token = NULL; + long result = 0; + int i; + struct goodix_ts_core *core_data = + dev_get_drvdata(dev); + struct goodix_ts_device *ts_dev = core_data->ts_dev; + + if (!buf || !count) { + ts_err("invalied params\n"); + 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'\n"); + goto err_out; + } + + /* get addr */ + pos = (char *)buf; + pos += 2; + token = strsep(&pos, ":"); + if (!token) { + ts_err("invalied address info\n"); + goto err_out; + } else { + if (kstrtol(token, 16, &result)) { + ts_err("failed get addr info\n"); + goto err_out; + } + rw_addr = (u16)result; + ts_err("rw addr is 0x%x\n", rw_addr); + } + + /* get length */ + token = strsep(&pos, ":"); + if (!token) { + ts_err("invalied length info\n"); + goto err_out; + } else { + if (kstrtol(token, 0, &result)) { + ts_err("failed get length info\n"); + goto err_out; + } + rw_len = (u32)result; + ts_err("rw length info is %d\n", rw_len); + if (rw_len > sizeof(store_buf)) { + //ts_err("data len > %d\n", sizeof(store_buf)); + ts_err("data len > 32\n"); + goto err_out; + } + } + + if (rw_flag == 2) { + for (i = 0; i < rw_len; i++) { + token = strsep(&pos, ":"); + if (!token) { + ts_err("invalied data info\n"); + goto err_out; + } else { + if (kstrtol(token, 16, &result)) { + ts_err("failed get data[%d] info\n", i); + goto err_out; + } + store_buf[i] = (u8)result; + ts_err("get data[%d]=0x%x\n", i, store_buf[i]); + } + } + ret = ts_dev->hw_ops->write(ts_dev, rw_addr, store_buf, rw_len); + if (ret) { + ts_err("failed write addr(%x) data %*ph\n", rw_addr, + rw_len, store_buf); + goto err_out; + } + + ts_err("%s write to addr (%x) with data %*ph\n", + "success", rw_addr, rw_len, store_buf); + } + + return count; +err_out: + snprintf(show_buf, PAGE_SIZE, "%s\n", + "invalied params, format{r/w:4100:length:[41:21:31]}"); + return -EINVAL; +} + +/* get_raw\Diff\Baseline */ +static ssize_t goodix_ts_get_rawDiff_data_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int i, j; + u32 sen_num, drv_num; + int data_size; + int *temp_data = NULL; + int offset = 0; + char *tm_buf = NULL; + struct goodix_ts_core *core_data = + dev_get_drvdata(dev); + int state; + + if (kstrtoint(buf, 10, &state) != 0) + return -EINVAL; + + if (state > 2) + return -EINVAL; + + goodix_get_channel_num(&sen_num, &drv_num); + data_size = sen_num * drv_num; + temp_data = kzalloc(data_size * 4, GFP_KERNEL); + if (!temp_data) { + ts_err("Alloc rawdiff temp_data mem fail."); + return -EINVAL; + } + tm_buf = kzalloc(drv_num * 5, GFP_KERNEL); + if (!tm_buf) + return -EINVAL; + + disable_irq(core_data->irq); + if (goodix_get_rawordiff_data(state, temp_data)) { + kfree(temp_data); + temp_data = NULL; + enable_irq(core_data->irq); + return -EINVAL; + } + enable_irq(core_data->irq); + + switch (state) { + case 0: + ts_info("rawdata: sen_num = %d;drv_num = %d;total = %d", + sen_num, drv_num, (sen_num * drv_num)); + break; + case 1: + ts_info("Diffdata: sen_num = %d;drv_num = %d;total = %d", + sen_num, drv_num, (sen_num * drv_num)); + break; + case 2: + ts_info("Baseline: sen_num = %d;drv_num = %d;total = %d", + sen_num, drv_num, (sen_num * drv_num)); + break; + default: + ts_info("Err CMD !"); + break; + } + for (j = 0; j < sen_num; j++) { + offset = 0; + for (i = 0; i < drv_num; i++) { + offset += snprintf(&tm_buf[offset], PAGE_SIZE, + "%4d ", temp_data[data_size - (i * sen_num + j) - 1]); + } + ts_info("%s", &tm_buf[0]); + } + + offset = snprintf(&tm_buf[0], PAGE_SIZE, "%s", "Check kmsg !"); + + kfree(temp_data); + kfree(tm_buf); + temp_data = NULL; + tm_buf = NULL; + return data_size; +} + +/* get_self_data */ +static ssize_t goodix_ts_get_self_data_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int i; + u32 sen_num, drv_num; + int data_size; + int *temp_data = NULL; + int offset = 0; + char *tm_buf = NULL; + int state; + struct goodix_ts_core *core_data = + dev_get_drvdata(dev); + + if (kstrtoint(buf, 10, &state) != 0) + return -EINVAL; + + if (state > 2) + return -EINVAL; + + goodix_get_channel_num(&sen_num, &drv_num); + data_size = sen_num + drv_num; + temp_data = kzalloc(data_size * 4, GFP_KERNEL); + if (!temp_data) { + ts_err("Alloc rawdiff temp_data mem fail."); + return -EINVAL; + } + tm_buf = kzalloc(sen_num * 6, GFP_KERNEL); + if (!tm_buf) + return -EINVAL; + + state += 3; + disable_irq(core_data->irq); + if (goodix_get_rawordiff_data(state, temp_data)) { + kfree(temp_data); + temp_data = NULL; + enable_irq(core_data->irq); + return -EINVAL; + } + enable_irq(core_data->irq); + + switch (state) { + case 3: + ts_info("self_rawdata: sen_num = %d;drv_num = %d;total = %d", + sen_num, drv_num, (sen_num + drv_num)); + break; + case 4: + ts_info("self_Diffdata: sen_num = %d;drv_num = %d;total = %d", + sen_num, drv_num, (sen_num + drv_num)); + break; + case 5: + ts_info("self_Baseline: sen_num = %d;drv_num = %d;total = %d", + sen_num, drv_num, (sen_num + drv_num)); + break; + default: + ts_info("Err CMD !"); + break; + } + //display drv_num + ts_info("selfdata drv_num,1~%d", drv_num); + offset = 0; + for (i = 0; i < drv_num; i++) { + offset += snprintf(&tm_buf[offset], PAGE_SIZE, + "%5d ", temp_data[i]); + } + ts_info("%s", &tm_buf[0]); + //display sen_num0 + ts_info("selfdata sen_num,1~%d", drv_num); + offset = 0; + for (i = 0; i < drv_num; i++) { + offset += snprintf(&tm_buf[offset], PAGE_SIZE, + "%5d ", temp_data[i + drv_num]); + } + ts_info("%s", &tm_buf[0]); + //display sen_num1 + ts_info("selfdata sen_num,%d~%d", (drv_num + 1), (drv_num + drv_num)); + offset = 0; + for (i = 0; i < drv_num; i++) { + offset += snprintf(&tm_buf[offset], PAGE_SIZE, + "%5d ", temp_data[i + drv_num + drv_num]); + } + ts_info("%s", &tm_buf[0]); + //display sen_num2 + offset = 0; + ts_info("selfdata sen_num,%d~%d", (drv_num + drv_num + 1), sen_num); + for (i = 0; i < (sen_num - drv_num - drv_num); i++) { + offset += snprintf(&tm_buf[offset], PAGE_SIZE, + "%5d ", temp_data[i + drv_num + drv_num + drv_num]); + } + ts_info("%s", &tm_buf[0]); + + kfree(temp_data); + kfree(tm_buf); + temp_data = NULL; + tm_buf = NULL; + return data_size; +} + +/* get_reg */ +static ssize_t goodix_ts_get_reg_data_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct goodix_ts_core *core_data = + dev_get_drvdata(dev); + int state; + + if (kstrtoint(buf, 10, &state) != 0) + return -EINVAL; + + if (state != 1) + return -EINVAL; + + disable_irq(core_data->irq); + ts_info("reg start"); + goodix_nodereg_read(); + ts_info("reg end"); + enable_irq(core_data->irq); + + return state; +} + +static DEVICE_ATTR(extmod_info, 0444, goodix_ts_extmod_show, NULL); +static DEVICE_ATTR(driver_info, 0444, goodix_ts_driver_info_show, NULL); +static DEVICE_ATTR(chip_info, 0444, goodix_ts_chip_info_show, NULL); +static DEVICE_ATTR(config_data, 0444, goodix_ts_config_data_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, 0444, goodix_ts_read_cfg_show, NULL); +static DEVICE_ATTR(irq_info, 0664, + goodix_ts_irq_info_show, goodix_ts_irq_info_store); +static DEVICE_ATTR(reg_rw, 0664, + goodix_ts_reg_rw_show, goodix_ts_reg_rw_store); +static DEVICE_ATTR(get_rawDiff, 0220, NULL, goodix_ts_get_rawDiff_data_store); +static DEVICE_ATTR(get_selfdata, 0220, NULL, goodix_ts_get_self_data_store); +static DEVICE_ATTR(get_regdata, 0220, NULL, goodix_ts_get_reg_data_store); + +static struct attribute *sysfs_attrs[] = { + &dev_attr_extmod_info.attr, + &dev_attr_driver_info.attr, + &dev_attr_chip_info.attr, + &dev_attr_config_data.attr, + &dev_attr_reset.attr, + &dev_attr_send_cfg.attr, + &dev_attr_read_cfg.attr, + &dev_attr_irq_info.attr, + &dev_attr_reg_rw.attr, + &dev_attr_get_rawDiff.attr, + &dev_attr_get_selfdata.attr, + &dev_attr_get_regdata.attr, + NULL, +}; + + +/****/ +static ssize_t path_show(struct device *dev, struct device_attribute *pAttr, char *pBuf) +{ + ssize_t blen; + const char *path; + struct goodix_ts_device *ts_device = + dev_get_drvdata(dev); + struct i2c_client *client = to_i2c_client(ts_device->dev); + + path = kobject_get_path(&client->dev.kobj, GFP_KERNEL); + blen = scnprintf(pBuf, PAGE_SIZE, "%s\n", path ? path : "na"); + kfree(path); + + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ts_info("*** %s() vendor = %s ***", __func__, "goodix"); + return scnprintf(buf, PAGE_SIZE, "goodix"); +} + + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_NULL +}; + +static int goodix_ts_sysfs_class(struct goodix_ts_device *core_data, bool create) +{ + int i, error = 0; + dev_t devno; + s32 ret = 0; + struct device_attribute *attrs = touchscreen_attributes; + + if (create) { + ret = alloc_chrdev_region(&devno, 0, 1, GOODIX_TP_IC_TYPE); + + if (ret) { + ts_err("cant`t allocate chrdev"); + return ret; + } + ts_debug("allocate chrdev ok"); + + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + ts_err("Faild to create touchscreen class!"); + return ret; + } + + touchscreen_class_dev = device_create(touchscreen_class, NULL, + devno, core_data, GOODIX_TP_IC_TYPE); + if (IS_ERR(touchscreen_class_dev)) { + error = PTR_ERR(touchscreen_class_dev); + touchscreen_class_dev = NULL; + ts_err("Faild to create device(touchscreen_class_dev)!"); + return error; + } + + ts_info("Succeed to create device(touchscreen_class_dev)!"); + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + ret = device_create_file(touchscreen_class_dev, &attrs[i]); + if (ret < 0) + goto device_destroy; + } + } else { + if (!touchscreen_class || !touchscreen_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(touchscreen_class_dev, &attrs[i]); + + device_unregister(touchscreen_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(touchscreen_class_dev, &attrs[i]); + /*device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor));*/ + touchscreen_class_dev = NULL; + class_unregister(touchscreen_class); + ts_err("error creating touchscreen class"); + + return -ENODEV; +} + +/* enable/disable gesture feature */ +static ssize_t goodix_gesture_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, + "gesture:%d\n", goodix_modules.core_data->wakeable); +} + +static ssize_t goodix_gesture_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int en; + + ts_err("Touch screen gesture.\n"); + if (kstrtoint(buf, 10, &en) != 0) + return -EINVAL; + + if (en > 0) + goodix_modules.core_data->wakeable = true; + else if (en == 0) + goodix_modules.core_data->wakeable = false; + else + ts_err("Open gesture invalid!"); + + ts_debug("enable gesture= %d", goodix_modules.core_data->wakeable); + return count; +} + +static struct device_attribute gesture_attributes[] = { + __ATTR(gesture, 0664, goodix_gesture_show, goodix_gesture_store), + __ATTR_NULL +}; + +static int goodix_ts_gesture_class(struct goodix_ts_device *core_data, bool create) +{ + int i, error = 0; + dev_t devno; + s32 ret = 0; + struct device_attribute *attrs = gesture_attributes; + + if (create) { + ret = alloc_chrdev_region(&devno, 0, 1, GOODIX_TP_GESTURE); + + if (ret) { + ts_err("cant`t allocate chrdev"); + return ret; + } + ts_debug("allocate chrdev ok"); + + gesture_class = class_create(THIS_MODULE, "touch_gesture"); + if (IS_ERR(gesture_class)) { + error = PTR_ERR(gesture_class); + gesture_class = NULL; + ts_err("Faild to create gesture class!"); + return ret; + } + + gesture_class_dev = device_create(gesture_class, NULL, + devno, core_data, GOODIX_TP_GESTURE); + if (IS_ERR(gesture_class_dev)) { + error = PTR_ERR(gesture_class_dev); + gesture_class_dev = NULL; + ts_err("Faild to create device(gesture_class_dev)!"); + return error; + } + + ts_info("Succeed to create device(gesture_class_dev)!"); + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + ret = device_create_file(gesture_class_dev, &attrs[i]); + if (ret < 0) + goto device_destroy; + } + } else { + if (!gesture_class || !gesture_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(gesture_class_dev, &attrs[i]); + + device_unregister(gesture_class_dev); + class_unregister(gesture_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(gesture_class_dev, &attrs[i]); + gesture_class_dev = NULL; + class_unregister(gesture_class); + ts_err("error creating gesture class"); + + return -ENODEV; +} + +static const struct attribute_group sysfs_group = { + .attrs = sysfs_attrs, +}; + +int goodix_ts_sysfs_init(struct goodix_ts_core *core_data) +{ + int err; + err = goodix_ts_sysfs_class(core_data->ts_dev, true); + if (err) { + ts_err("sys class files creation failed"); + } + err = goodix_ts_gesture_class(core_data->ts_dev, true); + if (err) { + ts_err("gesture class files creation failed"); + } + err = sysfs_create_group(&core_data->pdev->dev.kobj, &sysfs_group); + return err; +} + +static void goodix_ts_sysfs_exit(struct goodix_ts_core *core_data) +{ + int err; + err = goodix_ts_sysfs_class(core_data->ts_dev, false); + if (err) { + ts_err("sys class files delete failed"); + } + err = goodix_ts_gesture_class(core_data->ts_dev, false); + if (err) { + ts_err("sys class files delete failed"); + } + sysfs_remove_group(&core_data->pdev->dev.kobj, &sysfs_group); +} + +/* 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); +} +EXPORT_SYMBOL(goodix_ts_register_notifier); + +/** + * 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); +} +EXPORT_SYMBOL(goodix_ts_unregister_notifier); + +/** + * 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) +{ + return blocking_notifier_call_chain(&ts_notifier_list, + (unsigned long)evt, v); +} +EXPORT_SYMBOL_GPL(goodix_ts_blocking_notify); + + + +/** + * goodix_ts_input_report - report touch event to input subsystem + * + * @dev: input device pointer + * @touch_data: touch data pointer + * return: 0 ok, <0 failed + */ +static int goodix_ts_input_report(struct input_dev *dev, + struct goodix_touch_data *touch_data) +{ + struct goodix_ts_coords *coords = &touch_data->coords[0]; + struct goodix_ts_core *core_data = input_get_drvdata(dev); + struct goodix_ts_board_data *ts_bdata = board_data(core_data); + unsigned int touch_num = touch_data->touch_num; + static u32 pre_fin;/* = 0x0000;*/ + static u8 pre_key;/* = 0x00;*/ + int i, id; + + /*first touch down and last touch up condition*/ + if (touch_num != 0 && pre_fin == 0x0000) { + /*first touch down event*/ + input_report_key(dev, BTN_TOUCH, 1); + } else if (touch_num == 0 && pre_fin != 0x0000) { + /*no finger exist*/ + input_report_key(dev, BTN_TOUCH, 0); + } + + /*report key, include tp's key and pen's key */ + if (unlikely(touch_data->have_key)) { + for (i = 0; i < ts_bdata->panel_max_key; i++) { + input_report_key(dev, ts_bdata->panel_key_map[i], + touch_data->key_value & (1 << i)); + } + pre_key = touch_data->key_value; + } else if (pre_key != 0x00) { + + for (i = 0; i < ts_bdata->panel_max_key; i++) { + if (pre_key & (1 << i)) { + input_report_key(dev, + ts_bdata->panel_key_map[i], 0); + pre_key &= ~(1 << i); + + } + } + } + +#define MAKE +#ifdef MAKE + /*protocol B*/ + + /*report pen*/ + if (touch_num >= 1 && touch_data->pen_down) { + touch_num -= 1; + + input_mt_slot(dev, ts_bdata->panel_max_id * 2); + input_report_abs(dev, ABS_MT_TRACKING_ID, + touch_data->pen_coords[0].id); + input_report_abs(dev, ABS_MT_TOOL_TYPE, MT_TOOL_PEN); + + + input_report_abs(dev, ABS_MT_POSITION_X, + touch_data->pen_coords[0].x); + input_report_abs(dev, ABS_MT_POSITION_Y, + touch_data->pen_coords[0].y); + input_report_abs(dev, ABS_MT_TOUCH_MAJOR, + touch_data->pen_coords[0].w); + input_report_abs(dev, ABS_MT_PRESSURE, + touch_data->pen_coords[0].p); + + pre_fin |= 1 << 20; + } else { + if (pre_fin & (1 << 20)) { + input_mt_slot(dev, ts_bdata->panel_max_id * 2); + input_report_abs(dev, ABS_MT_TRACKING_ID, -1); + pre_fin &= ~(1 << 20); + } + } + + /*report finger*/ + id = coords->id; + for (i = 0; i < ts_bdata->panel_max_id * 2; i++) { + /* this is a valid touch down event */ + if (touch_num && i == id) { + input_mt_slot(dev, id); + input_report_abs(dev, ABS_MT_TRACKING_ID, i); + input_report_abs(dev, ABS_MT_TOOL_TYPE, MT_TOOL_FINGER); + + input_report_abs(dev, ABS_MT_POSITION_X, coords->x); + input_report_abs(dev, ABS_MT_POSITION_Y, coords->y); + input_report_abs(dev, ABS_MT_TOUCH_MAJOR, coords->w); + input_report_abs(dev, ABS_MT_PRESSURE, coords->p); + + pre_fin |= 1 << i; + id = (++coords)->id; + } else { + if (pre_fin & (1 << i)) {/* release touch */ + input_mt_slot(dev, i); + input_report_abs(dev, ABS_MT_TRACKING_ID, -1); + pre_fin &= ~(1 << i); + } + } + } +#endif + +#define NO_MAKE3 +#ifndef NO_MAKE3 + /*report pen use protocl A*/ + if (touch_data->pen_down) { + + input_report_abs(dev, ABS_MT_POSITION_X, + touch_data->pen_coords[0].x); + input_report_abs(dev, ABS_MT_POSITION_Y, + touch_data->pen_coords[0].y); + + input_report_abs(dev, ABS_MT_PRESSURE, + touch_data->pen_coords[0].p); + input_report_abs(dev, ABS_MT_TOUCH_MAJOR, + touch_data->pen_coords[0].w); + + input_report_abs(dev, ABS_MT_TOOL_TYPE, 1); + + input_mt_sync(dev); + } else { + if (pre_fin & (1 << 10) && touch_num == 0) { + + pre_fin &= ~(1 << 10); + } + } + + /* report abs */ + id = coords->id; + for (i = 0; i < ts_bdata->panel_max_id; i++) { + /* this is a valid touch down event */ + if (touch_num && i == id) { + + input_report_abs(dev, ABS_MT_POSITION_X, coords->x); + input_report_abs(dev, ABS_MT_POSITION_Y, coords->y); + input_report_abs(dev, ABS_MT_TOUCH_MAJOR, coords->w); + input_report_abs(dev, ABS_MT_PRESSURE, coords->p); + input_mt_sync(dev); + + pre_fin |= 1 << i; + id = (++coords)->id; + } else { + if (pre_fin & (1 << i)) { + pre_fin &= ~(1 << i); + } + } + } +#endif + + input_sync(dev); + return 0; +} + +/** + * goodix_ts_threadirq_func - Bottom half of interrupt + * This functions is excuted in thread context, + * sleep in this function is permit. + * + * @core_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_device *ts_dev = core_data->ts_dev; + struct goodix_ext_module *ext_module; + struct goodix_ts_event *ts_event = &core_data->ts_event; + int r; + + core_data->irq_trig_cnt++; + /* inform external module */ + list_for_each_entry(ext_module, &goodix_modules.head, list) { + if (!ext_module->funcs->irq_event) + continue; + r = ext_module->funcs->irq_event(core_data, ext_module); + if (r == EVT_CANCEL_IRQEVT) + return IRQ_HANDLED; + } + + /* read touch data from touch device */ + r = ts_dev->hw_ops->event_handler(ts_dev, ts_event); + if (likely(r >= 0)) { + if (ts_event->event_type == EVENT_TOUCH) { + /* report touch */ + goodix_ts_input_report(core_data->input_dev, + &ts_event->event_data.touch_data); + } + } + + return IRQ_HANDLED; +} + +/** + * goodix_ts_init_irq - Requset interrput line from system + * @core_data: pointer to touch core data + * return: 0 ok, <0 failed + */ +int goodix_ts_irq_setup(struct goodix_ts_core *core_data) +{ + const struct goodix_ts_board_data *ts_bdata = + board_data(core_data); + int r; + + if (ts_bdata->irq <= 0) + core_data->irq = gpio_to_irq(ts_bdata->irq_gpio); + else + core_data->irq = ts_bdata->irq; + + ts_info("IRQ:%u,flags:%d", core_data->irq, (int)ts_bdata->irq_flags); + r = devm_request_threaded_irq(&core_data->pdev->dev, + core_data->irq, NULL, + goodix_ts_threadirq_func, + ts_bdata->irq_flags | IRQF_ONESHOT, + GOODIX_CORE_DRIVER_NAME, + core_data); + if (r < 0) + ts_err("Failed to requeset threaded irq:%d", r); + else + atomic_set(&core_data->irq_enabled, 1); + + return r; +} + +/** + * goodix_ts_irq_enable - Enable/Disable a irq + * @core_data: pointer to touch core data + * enable: enable or disable irq + * return: 0 ok, <0 failed + */ +int goodix_ts_irq_enable(struct goodix_ts_core *core_data, + bool enable) +{ + if (enable) { + if (!atomic_cmpxchg(&core_data->irq_enabled, 0, 1)) { + enable_irq(core_data->irq); + ts_debug("Irq enabled"); + if (core_data->wakeable) { + enable_irq_wake(core_data->irq); + core_data->irq_waked = true; + ts_debug("Irq enabled wake"); + } + } + } else { + if (atomic_cmpxchg(&core_data->irq_enabled, 1, 0)) { + disable_irq(core_data->irq); + ts_debug("Irq disabled"); + if (core_data->irq_waked) { + disable_irq_wake(core_data->irq); + core_data->irq_waked = false; + ts_debug("Irq disabled wake"); + } + } + } + + return 0; +} +EXPORT_SYMBOL(goodix_ts_irq_enable); +/** + * 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 device *dev = NULL; + struct goodix_ts_board_data *ts_bdata; + int r = 0; + + ts_info("Power init"); + /* dev:i2c client device or spi slave device*/ + dev = core_data->ts_dev->dev; + ts_bdata = board_data(core_data); + + if (ts_bdata->avdd_name) { + core_data->avdd = devm_regulator_get(dev, + ts_bdata->avdd_name); + if (IS_ERR_OR_NULL(core_data->avdd)) { + r = PTR_ERR(core_data->avdd); + ts_err("Failed to get regulator avdd:%d", r); + core_data->avdd = NULL; + return r; + } + } else { + ts_info("Avdd name is NULL"); + } + + r = regulator_set_load(core_data->avdd, + 50000); + + if (regulator_count_voltages(core_data->avdd) > 0) { + r = regulator_set_voltage(core_data->avdd, GT_VTG_MIN_UV, + GT_VTG_MAX_UV); + if (r) { + ts_err("Regulator set_vtg failed avdd r=%d\n", r); + /* FIXME not fatal error, requested voltage 3V while constraints are 3.08V */ + r = 0; + } + } + + if (ts_bdata->iovdd_name) { + core_data->iovdd = devm_regulator_get(dev, + ts_bdata->iovdd_name); + if (IS_ERR_OR_NULL(core_data->iovdd)) { + r = PTR_ERR(core_data->iovdd); + ts_err("Failed to get regulator iovdd:%d", r); + core_data->iovdd = NULL; + } + } else { + ts_info("iovdd name is NULL"); + } + + if (regulator_count_voltages(core_data->iovdd) > 0) { + r = regulator_set_voltage(core_data->iovdd, GT_I2C_VTG_MIN_UV, + GT_I2C_VTG_MAX_UV); + if (r) { + ts_err("Regulator set_vtg failed iovdd r=%d\n", r); + } + } + return r; +} + +/** + * 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 *core_data) +{ + struct goodix_ts_board_data *ts_bdata = + board_data(core_data); + int r; + + ts_info("Device power on"); + if (core_data->power_on) + return 0; + + if (core_data->avdd) { + r = regulator_enable(core_data->avdd); + if (!r) { + ts_info("regulator enable SUCCESS"); + if (ts_bdata->power_on_delay_us) + usleep_range(ts_bdata->power_on_delay_us, + ts_bdata->power_on_delay_us); + } else { + ts_err("Failed to enable analog power:%d", r); + return r; + } + } + + if (core_data->iovdd) { + r = regulator_enable(core_data->iovdd); + if (!r) { + ts_info("regulator iovdd enable SUCCESS"); + if (ts_bdata->power_on_delay_us) + usleep_range(ts_bdata->power_on_delay_us, + ts_bdata->power_on_delay_us); + } else { + ts_err("Failed to enable iovdd power:%d", r); + return r; + } + } + + core_data->power_on = 1; + return 0; +} + +/** + * 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 *core_data) +{ + struct goodix_ts_board_data *ts_bdata = + board_data(core_data); + int r; + + ts_info("Device power off"); + if (!core_data->power_on) + return 0; + + if (core_data->avdd) { + r = regulator_disable(core_data->avdd); + if (!r) { + ts_info("regulator disable SUCCESS"); + if (ts_bdata->power_off_delay_us) + usleep_range(ts_bdata->power_off_delay_us, + ts_bdata->power_off_delay_us); + } else { + ts_err("Failed to disable analog power:%d", r); + return r; + } + } + + if (core_data->iovdd) { + r = regulator_disable(core_data->iovdd); + if (!r) { + ts_info("regulator iovdd disable SUCCESS"); + if (ts_bdata->power_off_delay_us) + usleep_range(ts_bdata->power_off_delay_us, + ts_bdata->power_off_delay_us); + } else { + ts_err("Failed to disable iovdd power:%d", r); + return r; + } + } + + core_data->power_on = 0; + return 0; +} + +/** + * goodix_ts_power_release - Get regulator for touch device + * @core_data: pointer to touch core data + * return: 0 ok, <0 failed + */ +static int goodix_ts_power_release(struct goodix_ts_core *core_data) +{ + struct device *dev = NULL; + struct goodix_ts_board_data *ts_bdata; + int r = 0; + + ts_info("Power release"); + /* dev:i2c client device or spi slave device*/ + dev = core_data->ts_dev->dev; + ts_bdata = board_data(core_data); + + if (ts_bdata->avdd_name) { + regulator_put(core_data->avdd); + ts_bdata->avdd_name = ""; + ts_info("Avdd power release"); + } else { + ts_info("Avdd name is NULL"); + } + + r = regulator_set_load(core_data->avdd, + 50000); + + if (regulator_count_voltages(core_data->avdd) > 0) { + r = regulator_set_voltage(core_data->avdd, GT_VTG_MIN_UV, + GT_VTG_MAX_UV); + if (r) { + ts_err("Regulator set_vtg failed vdd r=%d\n", r); + } + } + + if (ts_bdata->iovdd_name) { + regulator_put(core_data->iovdd); + ts_bdata->avdd_name = ""; + ts_info("Avdd power release"); + } else { + ts_info("iovdd name is NULL"); + } + + if (regulator_count_voltages(core_data->iovdd) > 0) { + r = regulator_set_voltage(core_data->iovdd, GT_I2C_VTG_MIN_UV, + GT_I2C_VTG_MAX_UV); + if (r) { + ts_err("Regulator set_vtg failed vdd r=%d\n", r); + } + } + return r; +} + +#ifdef CONFIG_PINCTRL +/** + * goodix_ts_pinctrl_init - Get pinctrl handler and pinctrl_state + * @core_data: pointer to touch core data + * return: 0 ok, <0 failed + */ +static int goodix_ts_pinctrl_init(struct goodix_ts_core *core_data) +{ + int r = 0; + + /* get pinctrl handler from of node */ + core_data->pinctrl = devm_pinctrl_get(core_data->ts_dev->dev); + if (IS_ERR_OR_NULL(core_data->pinctrl)) { + ts_err("Failed to get pinctrl handler"); + return PTR_ERR(core_data->pinctrl); + } + + /* active state */ + core_data->pin_sta_active = pinctrl_lookup_state(core_data->pinctrl, + PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(core_data->pin_sta_active)) { + r = PTR_ERR(core_data->pin_sta_active); + ts_err("Failed to get pinctrl state:%s, r:%d", + PINCTRL_STATE_ACTIVE, r); + goto exit_pinctrl_put; + } + + /* suspend state */ + core_data->pin_sta_suspend = pinctrl_lookup_state(core_data->pinctrl, + PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(core_data->pin_sta_suspend)) { + r = PTR_ERR(core_data->pin_sta_suspend); + ts_err("Failed to get pinctrl state:%s, r:%d", + PINCTRL_STATE_SUSPEND, r); + goto exit_pinctrl_put; + } + + return 0; +exit_pinctrl_put: + devm_pinctrl_put(core_data->pinctrl); + core_data->pinctrl = NULL; + return r; +} +#endif + +/** + * goodix_ts_gpio_setup - Request gpio resources from GPIO subsysten + * reset_gpio and irq_gpio number are obtained from goodix_ts_device + * which created in hardware layer driver. e.g.goodix_xx_i2c.c + * A goodix_ts_device should set those two fileds to right value + * before registed to touch core driver. + * @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,reg-en-gpio:%d, reset-gpio:%d, irq-gpio:%d", + ts_bdata->reg_en_gpio, ts_bdata->reset_gpio, ts_bdata->irq_gpio); + /* + * after kenerl3.13, gpio_ api is deprecated, new + * driver should use gpiod_ api. + */ + + if (gpio_is_valid(ts_bdata->reg_en_gpio)) { + r = devm_gpio_request_one(&core_data->pdev->dev, + ts_bdata->reg_en_gpio, + GPIOF_OUT_INIT_HIGH, + "ts_reg_en_gpio"); + if (r < 0) { + ts_err("Failed to request regulator enable gpio, r:%d", r); + } + } + + r = devm_gpio_request_one(&core_data->pdev->dev, + ts_bdata->reset_gpio, + GPIOF_OUT_INIT_HIGH, + "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; + } + + return 0; +} + +/** + * goodix_input_set_params - set input parameters + */ +static void goodix_ts_set_input_params(struct input_dev *input_dev, + struct goodix_ts_board_data *ts_bdata) +{ + int i; + + if (ts_bdata->swap_axis) + swap(ts_bdata->panel_max_x, ts_bdata->panel_max_y); + + input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, + 0, ts_bdata->panel_max_id, 0, 0); + 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); + input_set_abs_params(input_dev, ABS_MT_PRESSURE, + 0, ts_bdata->panel_max_p, 0, 0); + + if (ts_bdata->panel_max_key) { + for (i = 0; i < ts_bdata->panel_max_key; i++) + input_set_capability(input_dev, EV_KEY, + ts_bdata->panel_key_map[i]); + } +} + +/** + * goodix_ts_input_dev_config - Requset and config a input device + * then register it to input sybsystem. + * NOTE that some hardware layer may provide a input device + * (ts_dev->input_dev not NULL). + * @core_data: pointer to touch core data + * return: 0 ok, <0 failed + */ +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 = devm_input_allocate_device(&core_data->pdev->dev); + 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; + + __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(BTN_TOOL_PEN, input_dev->keybit); + +#ifdef INPUT_PROP_DIRECT + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); +#endif + + /* set input parameters */ + goodix_ts_set_input_params(input_dev, ts_bdata); + + /*set ABS_MT_TOOL_TYPE*/ + input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, + 0, 1, 0, 0); + +#ifdef INPUT_TYPE_B_PROTOCOL + input_mt_init_slots(input_dev, + ts_bdata->panel_max_id * 2 + 1, 0); +#endif + + input_set_capability(input_dev, EV_KEY, KEY_POWER); + + r = input_register_device(input_dev); + if (r < 0) { + ts_err("Unable to register input device"); + return r; + } + + return 0; +} + +/** + * goodix_ts_hw_init - Hardware initilize + * poweron - hardware reset - sendconfig + * @core_data: pointer to touch core data + * return: 0 intilize ok, <0 failed + */ +int goodix_ts_hw_init(struct goodix_ts_core *core_data) +{ + const struct goodix_ts_hw_ops *hw_ops = + ts_hw_ops(core_data); + int r; + + /* reset touch device */ + if (hw_ops->reset) { + r = hw_ops->reset(core_data->ts_dev); + if (r < 0) + goto exit; + } + + /* init */ + if (hw_ops->init) { + r = hw_ops->init(core_data->ts_dev); + if (r < 0) + goto exit; + } + +exit: + /* if bus communication error occured then + * exit driver binding, other errors will + * be ignored + */ + if (r != -EBUS) + r = 0; + return r; +} + +/** + * 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 *core = container_of(ts_esd, + struct goodix_ts_core, ts_esd); + const struct goodix_ts_hw_ops *hw_ops = ts_hw_ops(core); + int r = 0; + u8 data = 0xaa; + + if (ts_esd->esd_on == false) + return; + + if (hw_ops->check_hw) + r = hw_ops->check_hw(core->ts_dev); + if (r < 0) { + goodix_ts_power_off(core); + goodix_ts_power_on(core); + if (hw_ops->reset) + hw_ops->reset(core->ts_dev); + + /*init static esd*/ + if (core->ts_dev->ic_type == IC_TYPE_NANJING) { + r = hw_ops->write(core->ts_dev, + 0x8043, &data, 1); + if (r < 0) + ts_err("nanjing esd reset, init static esd FAILED, i2c wirte ERROR"); + } + + /*init dynamic esd*/ + r = hw_ops->write_trans(core->ts_dev, + core->ts_dev->reg.esd, + &data, 1); + if (r < 0) + ts_err("esd reset, init dynamic esd FAILED, i2c write ERROR"); + + } else { + /*init dynamic esd*/ + r = hw_ops->write_trans(core->ts_dev, + core->ts_dev->reg.esd, + &data, 1); + if (r < 0) + ts_err("esd init watch dog FAILED, i2c write ERROR"); + } + + mutex_lock(&ts_esd->esd_mutex); + if (ts_esd->esd_on) + schedule_delayed_work(&ts_esd->esd_work, 2 * HZ); + mutex_unlock(&ts_esd->esd_mutex); +} + +/** + * goodix_ts_esd_on - turn on esd protection + */ +static void goodix_ts_esd_on(struct goodix_ts_core *core) +{ + struct goodix_ts_esd *ts_esd = &core->ts_esd; + + if (core->ts_dev->reg.esd == 0) + return; + + mutex_lock(&ts_esd->esd_mutex); + if (ts_esd->esd_on == false) { + ts_esd->esd_on = true; + schedule_delayed_work(&ts_esd->esd_work, 2 * HZ); + mutex_unlock(&ts_esd->esd_mutex); + ts_info("Esd on"); + return; + } + mutex_unlock(&ts_esd->esd_mutex); +} + +/** + * goodix_ts_esd_off - turn off esd protection + */ +static void goodix_ts_esd_off(struct goodix_ts_core *core) +{ + struct goodix_ts_esd *ts_esd = &core->ts_esd; + + mutex_lock(&ts_esd->esd_mutex); + if (ts_esd->esd_on == true) { + ts_esd->esd_on = false; + cancel_delayed_work(&ts_esd->esd_work); + mutex_unlock(&ts_esd->esd_mutex); + ts_info("Esd off"); + return; + } + mutex_unlock(&ts_esd->esd_mutex); +} + +/** + * 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_END: + case NOTIFY_RESUME: + case NOTIFY_ESD_ON: + goodix_ts_esd_on(ts_esd->ts_core); + break; + } + + return 0; +} + +/** + * goodix_ts_esd_init - initialize esd protection + */ +int goodix_ts_esd_init(struct goodix_ts_core *core) +{ + struct goodix_ts_esd *ts_esd = &core->ts_esd; + struct goodix_ts_device *dev = core->ts_dev; + u8 data = 0xaa; + int r; + + INIT_DELAYED_WORK(&ts_esd->esd_work, goodix_ts_esd_work); + mutex_init(&ts_esd->esd_mutex); + ts_esd->ts_core = core; + ts_esd->esd_on = false; + ts_esd->esd_notifier.notifier_call = goodix_esd_notifier_callback; + goodix_ts_register_notifier(&ts_esd->esd_notifier); + + if (core->ts_dev->board_data->esd_default_on && + dev->hw_ops->check_hw && + dev->reg.esd != 0) { + /*init static esd*/ + if (dev->ic_type == IC_TYPE_NANJING) { + r = dev->hw_ops->write_trans(core->ts_dev, + 0x8043, &data, 1); + if (r < 0) + ts_err("static ESD init ERROR, i2c write failed"); + } + + /*init dynamic esd*/ + r = dev->hw_ops->write_trans(core->ts_dev, + core->ts_dev->reg.esd, + &data, 1); + if (r < 0) + ts_err("dynamic ESD init ERROR, i2c write failed"); + + goodix_ts_esd_on(core); + } + return 0; +} + +/** + * goodix_ts_set_regulator_gpio - Touchscreen regulator gpio control function + * Regulator gpio will control avdd 3.0V + */ +static int goodix_ts_set_regulator_gpio(struct goodix_ts_board_data *ts_bdata, int value) +{ + if (!ts_bdata) { + ts_err("ts_bdata is NULL.\n"); + return -ENODEV; + } + + if (gpio_is_valid(ts_bdata->reg_en_gpio)) { + gpio_set_value(ts_bdata->reg_en_gpio, value); + ts_info("Output reg_en_gpio num:%d to %d, actually: %d", + ts_bdata->reg_en_gpio, value, gpio_get_value(ts_bdata->reg_en_gpio)); + } + return 0; +} +/** + * 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; + struct goodix_ts_device *ts_dev = core_data->ts_dev; + struct goodix_ts_board_data *ts_bdata = + board_data(core_data); + int r; + + ts_info("Suspend start"); + + if (core_data->gtp_suspended) { + ts_info( "Already in suspend state"); + return 0; + } + /* + * 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(ext_module, &goodix_modules.head, list) { + if (!ext_module->funcs->before_suspend) + continue; + + r = ext_module->funcs->before_suspend( + core_data, ext_module); + if (r == EVT_CANCEL_SUSPEND) { + mutex_unlock(&goodix_modules.mutex); + ts_info("Canceled by module:%s", + ext_module->name); + goto out; + } + } + } + mutex_unlock(&goodix_modules.mutex); + + /* disable irq */ + goodix_ts_irq_enable(core_data, false); + + /* let touch ic work in sleep mode */ + if (ts_dev && ts_dev->hw_ops->suspend) + ts_dev->hw_ops->suspend(ts_dev); + atomic_set(&core_data->suspended, 1); + +#ifdef CONFIG_PINCTRL + if (core_data->pinctrl) { + r = pinctrl_select_state(core_data->pinctrl, + core_data->pin_sta_suspend); + if (r < 0) + ts_err("Failed to select active pinstate, r:%d", r); + } +#endif + + /* inform exteranl modules */ + mutex_lock(&goodix_modules.mutex); + if (!list_empty(&goodix_modules.head)) { + list_for_each_entry(ext_module, &goodix_modules.head, list) { + if (!ext_module->funcs->after_suspend) + continue; + + r = ext_module->funcs->after_suspend( + core_data, ext_module); + if (r == EVT_CANCEL_SUSPEND) { + mutex_unlock(&goodix_modules.mutex); + ts_info("Canceled by module:%s", + ext_module->name); + goto out; + } + } + } + mutex_unlock(&goodix_modules.mutex); + + /* power off when suspend */ + goodix_ts_set_regulator_gpio(ts_bdata, 0); + +out: + /* release all the touch IDs */ + core_data->ts_event.event_data.touch_data.touch_num = 0; + goodix_ts_input_report(core_data->input_dev, + &core_data->ts_event.event_data.touch_data); + core_data->gtp_suspended = true; + 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; + struct goodix_ts_device *ts_dev = + core_data->ts_dev; + struct goodix_ts_board_data *ts_bdata = + board_data(core_data); + int r; + + ts_info("Resume start"); + + if (!core_data->gtp_suspended) { + ts_info("Already in awake state\n"); + return 0; + } + + mutex_lock(&goodix_modules.mutex); + if (!list_empty(&goodix_modules.head)) { + list_for_each_entry(ext_module, &goodix_modules.head, list) { + if (!ext_module->funcs->before_resume) + continue; + + r = ext_module->funcs->before_resume( + core_data, ext_module); + if (r == EVT_CANCEL_RESUME) { + mutex_unlock(&goodix_modules.mutex); + ts_info("Canceled by module:%s", + ext_module->name); + goto out; + } + } + } + mutex_unlock(&goodix_modules.mutex); + + /* power on when resume */ + goodix_ts_set_regulator_gpio(ts_bdata, 1); + +#ifdef CONFIG_PINCTRL + if (core_data->pinctrl) { + r = pinctrl_select_state(core_data->pinctrl, + core_data->pin_sta_active); + if (r < 0) + ts_err("Failed to select active pinstate, r:%d", r); + } +#endif + + atomic_set(&core_data->suspended, 0); + /* resume device */ + if (ts_dev && ts_dev->hw_ops->resume) + ts_dev->hw_ops->resume(ts_dev); + + mutex_lock(&goodix_modules.mutex); + if (!list_empty(&goodix_modules.head)) { + list_for_each_entry(ext_module, &goodix_modules.head, list) { + if (!ext_module->funcs->after_resume) + continue; + + r = ext_module->funcs->after_resume( + core_data, ext_module); + if (r == EVT_CANCEL_RESUME) { + mutex_unlock(&goodix_modules.mutex); + ts_info("Canceled by module:%s", + ext_module->name); + goto out; + } + } + } + mutex_unlock(&goodix_modules.mutex); + + goodix_ts_irq_enable(core_data, true); + +out: + /* + * notify resume event, inform the esd protector + * and charger detector to turn on the work + */ + goodix_ts_blocking_notify(NOTIFY_RESUME, NULL); + core_data->gtp_suspended = false; + ts_debug("Resume end"); + return 0; +} + +#if defined(CONFIG_DRM) +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 goodix_ts_board_data *ts_bdata = board_data(core_data); + struct msm_drm_notifier *evdata = data; + + if (!evdata || (evdata->id != ts_bdata->ctrl_dsi)) + return 0; + + ts_info("Notifier's event = %ld", event); + if ((event == MSM_DRM_EARLY_EVENT_BLANK || event == MSM_DRM_EVENT_BLANK) && + evdata && evdata->data && core_data) { + int *blank = evdata->data; + pr_debug("drm notification: event = %lu blank = %d\n", event, *blank); +#ifdef GOODIX_SENSOR_EN + mutex_lock(&core_data->state_mutex); + if (event == MSM_DRM_EARLY_EVENT_BLANK) { + if (*blank != MSM_DRM_BLANK_POWERDOWN) { + mutex_unlock(&core_data->state_mutex); + return 0; + } + //screen off + core_data->screen_state = SCREEN_OFF; + ts_info("TP Suspend"); + goodix_ts_suspend(core_data); + } else if (*blank == MSM_DRM_BLANK_UNBLANK) { + ts_info("TP Resume"); + //screen on + core_data->screen_state = SCREEN_ON; + goodix_ts_resume(core_data); + } + mutex_unlock(&core_data->state_mutex); +#else + if (event == MSM_DRM_EARLY_EVENT_BLANK) { + if (*blank != MSM_DRM_BLANK_POWERDOWN) + return 0; + ts_info("TP Suspend"); + goodix_ts_suspend(core_data); + } else if (*blank == MSM_DRM_BLANK_UNBLANK) { + ts_info("TP Resume"); + goodix_ts_resume(core_data); + } +#endif + } + + return 0; +} +#elif defined(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; + + ts_info("Notifier's event = %ld", event); + 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; + ts_info("blank's event = %ld", *blank); + if (*blank == FB_BLANK_UNBLANK) { + ts_info("TP Resume"); + goodix_ts_resume(core_data); + } else if (*blank == FB_BLANK_POWERDOWN) { + goodix_ts_suspend(core_data); + ts_info("TP Suspend"); + } + } + } + + return 0; +} +#endif + +int goodix_ts_start_suspend(struct goodix_ts_core *core_data) +{ + return goodix_ts_suspend(core_data); +} + +int goodix_ts_start_resume(struct goodix_ts_core *core_data) +{ + return goodix_ts_resume(core_data); +} + +#ifdef CONFIG_HAS_EARLYSUSPEND +/** + * goodix_ts_earlysuspend - Early suspend function + * Called by kernel during system suspend phrase + */ +static void goodix_ts_earlysuspend(struct early_suspend *h) +{ + struct goodix_ts_core *core_data = + container_of(h, struct goodix_ts_core, + early_suspend); +#ifdef GTP_CHARGER + if (core_data->ts_dev->board_data->charger_detection) { + if (core_data->ts_dev->board_data->charger_detection->charger_notif.notifier_call) + power_supply_unreg_notifier(&core_data->charger_detection->charger_notif); + destroy_workqueue(core_data->ts_dev->board_data->charger_detection->goodix_charger_notify_wq); + kfree(core_data->ts_dev->board_data->charger_detection); + } +#endif + goodix_ts_suspend(core_data); +} +/** + * goodix_ts_lateresume - Late resume function + * Called by kernel during system wakeup + */ +static void goodix_ts_lateresume(struct early_suspend *h) +{ + struct goodix_ts_core *core_data = + container_of(h, struct goodix_ts_core, + early_suspend); + + goodix_ts_resume(core_data); +} +#endif + +#ifdef CONFIG_PM +#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. + */ +int goodix_generic_noti_callback(struct notifier_block *self, + unsigned long action, void *data) +{ + struct goodix_ts_core *ts_core = container_of(self, + struct goodix_ts_core, ts_notifier); + const struct goodix_ts_hw_ops *hw_ops = ts_hw_ops(ts_core); + int r; + + switch (action) { + case NOTIFY_FWUPDATE_END: + if (hw_ops->init) { + /* Firmware has been updated, we need to reinit + * the chip, read the sensor ID and send the + * correct config data based on sensor ID. + * The input parameters also needs to be updated. + */ + r = hw_ops->init(ts_core->ts_dev); + if (r < 0) + goto exit; + + goodix_ts_set_input_params(ts_core->input_dev, + ts_core->ts_dev->board_data); + } + break; + } + +exit: + return 0; + +} + +/** + * goodix_ts_probe - called by kernel when a Goodix touch + * platform driver is added. + */ +static int goodix_ts_probe(struct platform_device *pdev) +{ + struct goodix_ts_core *core_data = NULL; + struct goodix_ts_device *ts_device; + struct i2c_client *client = NULL; + int r; + u8 read_val = 0; + + ts_info("goodix_ts_probe IN"); + + ts_device = pdev->dev.platform_data; + if (!ts_device || !ts_device->hw_ops || + !ts_device->board_data) { + ts_err("Invalid touch device"); + + goodix_ts_power_off(core_data); + goodix_ts_power_release(core_data); + r = -ENODEV; + goto error; + } + + core_data = devm_kzalloc(&pdev->dev, sizeof(struct goodix_ts_core), + GFP_KERNEL); + if (!core_data) { + r = -ENOMEM; + goto error; + } + + /* touch core layer is a platform driver */ + core_data->pdev = pdev; + core_data->ts_dev = ts_device; + platform_set_drvdata(pdev, core_data); + core_data->cfg_group_parsed = false; + core_data->update_from_sysfs = false; + core_data->gtp_suspended = false; + core_data->wakeable = false; + core_data->irq_waked = false; +#ifdef GOODIX_SENSOR_EN + mutex_init(&core_data->state_mutex); + //unknown screen state + core_data->screen_state = SCREEN_UNKNOWN; +#endif + client = to_i2c_client(ts_device->dev); + i2c_set_clientdata(client, core_data); + r = goodix_ts_power_init(core_data); + if (r < 0) + goto out; + + r = goodix_ts_power_on(core_data); + if (r < 0) + goto out; + +#ifdef CONFIG_PINCTRL + /* Pinctrl handle is optional. */ + r = goodix_ts_pinctrl_init(core_data); + if (!r && core_data->pinctrl) { + r = pinctrl_select_state(core_data->pinctrl, + core_data->pin_sta_active); + if (r < 0) + ts_err("Failed to select active pinstate, r:%d", r); + } +#endif + + /* get GPIO resource */ + r = goodix_ts_gpio_setup(core_data); + if (r < 0) + goto out; + + /*create sysfs files*/ + goodix_ts_sysfs_init(core_data); + + /*i2c test*/ + r = ts_device->hw_ops->read_trans(ts_device, 0x3100, + &read_val, 1); + if (!r) + ts_info("i2c test SUCCESS"); + else { + ts_err("i2c test FAILED"); + goto out; + } + + /*unified protocl + * start a thread to parse cfg_bin and init IC + */ + r = goodix_start_cfg_bin(core_data); + if (!r) { + ts_info("***start cfg_bin_proc SUCCESS"); + } else { + ts_err("***start cfg_bin_proc FAILED"); + goto out; + } + +out: + ts_info("goodix_ts_probe OUT, r:%d", r); + return r; + +error: + ts_info("release power"); + goodix_ts_power_off(core_data); + goodix_ts_power_release(core_data); + return r; +} + +static int goodix_ts_remove(struct platform_device *pdev) +{ + struct goodix_ts_core *core_data = + platform_get_drvdata(pdev); + + goodix_debugfs_exit(); + goodix_ts_sysfs_exit(core_data); + + goodix_release_fb_notifier(core_data); + + goodix_ts_power_off(core_data); + goodix_ts_power_release(core_data); + return 0; +} + +#ifdef CONFIG_PM +static const struct dev_pm_ops dev_pm_ops = { +#if !defined(CONFIG_FB) && !defined(CONFIG_DRM) && !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_DRIVER_NAME}, + {} +}; +MODULE_DEVICE_TABLE(platform, ts_core_ids); + +static struct platform_driver goodix_ts_driver = { + .driver = { + .name = GOODIX_CORE_DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .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) +{ + ts_info("Core layer init"); + + if (!goodix_modules.initilized) { + goodix_modules.initilized = true; + goodix_modules.core_exit = true; + INIT_LIST_HEAD(&goodix_modules.head); + mutex_init(&goodix_modules.mutex); + init_completion(&goodix_modules.core_comp); + } + + goodix_debugfs_init(); + return platform_driver_register(&goodix_ts_driver); +} + + +static void __exit goodix_ts_core_exit(void) +{ +#ifdef GTP_CHARGER + struct goodix_ts_core *core_data = goodix_modules.core_data; + + core_data->ts_dev->board_data->charger_send_flage_enable = false; + if (core_data->ts_dev->board_data->charger_detection) { + if (core_data->ts_dev->board_data->charger_detection + ->charger_notif.notifier_call) + power_supply_unreg_notifier( + &core_data->ts_dev->board_data->charger_detection->charger_notif); + destroy_workqueue(core_data->ts_dev->board_data-> + charger_detection->goodix_charger_notify_wq); + kfree(core_data->ts_dev->board_data->charger_detection); + } +#endif + ts_info("Core layer exit"); + platform_driver_unregister(&goodix_ts_driver); + /*return;*/ +} + +extern int goodix_i2c_init(void); +extern int goodix_tools_init(void); +extern int goodix_fwu_module_init(void); +extern int goodix_gsx_gesture_init(void); +extern int goodix_i2c_exit(void); +extern int goodix_tools_exit(void); +extern int goodix_fwu_module_exit(void); +extern void goodix_gsx_gesture_exit(void); + +int __init goodix_ts_module_init(void) +{ + int err; + + err = goodix_ts_core_init(); + if (err) + return err; + err = goodix_i2c_init(); + if (err) + return err; + err = goodix_tools_init(); + if (err) + return err; + err = goodix_fwu_module_init(); + if (err) + return err; + err = goodix_gsx_gesture_init(); + if (err) + return err; + return 0; +} + +void __exit goodix_ts_module_exit(void) +{ + goodix_fwu_module_exit(); + goodix_tools_exit(); + goodix_i2c_exit(); + goodix_ts_core_exit(); + goodix_gsx_gesture_exit(); +} + +module_init(goodix_ts_module_init); +module_exit(goodix_ts_module_exit); + +MODULE_DESCRIPTION("Goodix Touchscreen Core Module"); +MODULE_AUTHOR("Goodix, Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_ts_core.h b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_ts_core.h new file mode 100644 index 000000000000..1177e5ce35b1 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_ts_core.h @@ -0,0 +1,811 @@ +/* + * Goodix Touchscreen Driver + * Core layer of touchdriver architecture. + * + * Copyright (C) 2015 - 2016 Goodix, Inc. + * Authors: Yulong Cai + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#include +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#ifdef CONFIG_DRM +#include +#elif defined(CONFIG_FB) +#include +#include +#endif + + +/* macros definition */ +#define GOODIX_CORE_DRIVER_NAME "goodix_ts" +#define GOODIX_DRIVER_VERSION "v1.3.0.1" +#define GOODIX_BUS_RETRY_TIMES 3 +#define GOODIX_MAX_TOUCH 10 +#define GOODIX_MAX_PEN 1 +#define GOODIX_MAX_KEY 3 +#define GOODIX_PEN_MAX_KEY 2 +#define GOODIX_CFG_MAX_SIZE 1024 +#define GOODIX_TP_IC_TYPE "GTx5" +#define GOODIX_TP_GESTURE "touch_gesture" +#define GOODIX_SENSOR_EN +#define REPORT_MAX_COUNT 10000 + +#define GOODIX_DEFAULT_CFG_NAME "goodix_config.cfg" + +#define IC_TYPE_NONE 0 +#define IC_TYPE_NORMANDY 1 +#define IC_TYPE_NANJING 2 + + +/* Sleep time define */ +#define GTP_2000_udelay 2000 +#define GTP_20_DLY_MS 20 +#define GTP_60_DLY_MS 60 +#define GTP_100_DLY_MS 100 +#define GTP_150_DLY_MS 150 +#define GTP_250_DLY_MS 250 +#define GTP_500_DLY_MS 500 +#define GTP_1000_DLY_MS 1000 + +/*#define GTP_10_DLY_MS 10*/ + + +#define GTP_1000_DLY_US 1000 +#define GTP_1100_DLY_US 1100 +#define GTP_5000_DLY_US 5000 +#define GTP_10000_DLY_US 10000 +#define GTP_11000_DLY_US 11000 + +#define GTP_100000_DLY_US 100000 +#define GTP_110000_DLY_US 110000 +#define GTP_10100_DLY_US 10100 +#define GTP_250_DLY_US 250 +#define GTP_260_DLY_US 260 +#define GTP_5100_DLY_US 5100 +#define GTP_5200_DLY_US 5200 +#define GTP_55000_DLY_US 55000 +#define GTP_56000_DLY_US 56000 + +/* + * struct goodix_module - external modules container + * @head: external modules list + * @initilized: whether this struct is initilized + * @mutex: mutex lock + * @count: current number of registered external module + * @wq: workqueue to do register work + * @core_exit: if goodix touch core exit, then no + * registration is allowed. + * @core_data: core_data pointer + */ +struct goodix_module { + struct list_head head; + bool initilized; + struct mutex mutex; + unsigned int count; + struct workqueue_struct *wq; + bool core_exit; + struct completion core_comp; + struct goodix_ts_core *core_data; +}; +extern struct goodix_module goodix_modules; + +#define GTP_CHARGER +#ifdef GTP_CHARGER + +#define USB_DETECT_IN 1 +#define USB_DETECT_OUT 0 +extern int usb_detect_flag; +struct usb_charger_detection { + struct notifier_block charger_notif; + int usb_connected; + struct workqueue_struct *goodix_charger_notify_wq; + struct work_struct charger_notify_work; +}; +#endif + +/* + * struct goodix_ts_board_data - board data + * @avdd_name: name of analoy regulator + * @reset_gpio: reset gpio number + * @irq_gpio: interrupt gpio number + * @irq_flag: irq trigger type + * @power_on_delay_us: power on delay time (us) + * @power_off_delay_us: power off delay time (us) + * @swap_axis: whether swaw x y axis + * @panel_max_id: max supported fingers + * @panel_max_x/y/w/p: resolution and size + * @panel_max_key: max supported keys + * @pannel_key_map: key map + * @debug_reg_version: debug regs addr version + * @fw_name: name of the firmware image + */ +struct goodix_ts_board_data { + const char *avdd_name; + const char *iovdd_name; + unsigned int reg_en_gpio; + unsigned int reset_gpio; + unsigned int irq_gpio; + int irq; + int ctrl_dsi; + unsigned int irq_flags; + + unsigned int power_on_delay_us; + unsigned int power_off_delay_us; + + unsigned int swap_axis; + unsigned int panel_max_id; /*max touch id*/ + unsigned int panel_max_x; + unsigned int panel_max_y; + unsigned int panel_max_w; /*major and minor*/ + unsigned int panel_max_p; /*pressure*/ + unsigned int panel_max_key; + unsigned int panel_key_map[GOODIX_MAX_KEY + GOODIX_PEN_MAX_KEY]; + unsigned int debug_reg_ver; + /*add by lishuai*/ + unsigned int x2x; + unsigned int y2y; + bool pen_enable; + unsigned int tp_key_num; + /*add end*/ + + const char *fw_name; + const char *cfg_bin_name; + bool esd_default_on; +#ifdef GTP_CHARGER + bool charger_detection_enable; + struct usb_charger_detection *charger_detection; + bool charger_send_flage_enable; +#endif +}; + +/* + * struct goodix_ts_config - chip config data + * @initialized: whether intialized + * @name: name of this config + * @lock: mutex + * @reg_base: register base of config data + * @length: bytes of the config + * @delay: delay time after sending config + * @data: config data buffer + */ +struct goodix_ts_config { + bool initialized; + char name[24]; + struct mutex lock; + unsigned int reg_base; + unsigned int length; + unsigned int delay; /*ms*/ + unsigned char data[GOODIX_CFG_MAX_SIZE]; +}; + +/* + * struct goodix_ts_cmd - command package + * @initialized: whether initialized + * @cmd_reg: command register + * @length: command length in bytes + * @cmds: command data + */ +#pragma pack(4) +struct goodix_ts_cmd { + u32 initialized; + u32 cmd_reg; + u32 length; + u8 cmds[3]; + }; +#pragma pack() + +/* interrupt event type */ +enum ts_event_type { + EVENT_INVALID, + EVENT_TOUCH, + EVENT_REQUEST, +}; + +/* requset event type */ +enum ts_request_type { + REQUEST_INVALID, + REQUEST_CONFIG, + REQUEST_BAKREF, + REQUEST_RESET, + REQUEST_MAINCLK, +}; + +/* notifier event */ + +enum ts_notify_event { + NOTIFY_FWUPDATE_START, + NOTIFY_FWUPDATE_END, + NOTIFY_SUSPEND, + NOTIFY_RESUME, + NOTIFY_ESD_OFF, + NOTIFY_ESD_ON, +}; + +#ifdef GOODIX_SENSOR_EN +/* display state */ +enum display_state { + SCREEN_UNKNOWN, + SCREEN_OFF, + SCREEN_ON, +}; +#endif + +/* coordinate package */ +struct goodix_ts_coords { + int id; + unsigned int x, y, w, p; +}; + +/* touch event data */ +struct goodix_touch_data { + /* finger */ + int touch_num; + struct goodix_ts_coords coords[GOODIX_MAX_TOUCH]; + /* key */ + u8 key_value; + bool have_key; + /*pen*/ + struct goodix_ts_coords pen_coords[GOODIX_MAX_PEN]; + bool pen_down; +}; + +/* request event data */ +struct goodix_request_data { + enum ts_request_type request_type; +}; + +/* + * 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; + union { + struct goodix_touch_data touch_data; + struct goodix_request_data request_data; + } event_data; +}; + +/* + * struct goodix_ts_version - firmware version + * @valid: whether these infomation is valid + * @pid: product id string + * @vid: firmware version code + * @cid: customer id code + * @sensor_id: sendor id + */ +struct goodix_ts_version { + bool valid; + char pid[8]; + char vid[8]; + u8 cid; + u8 sensor_id; +}; + +struct goodix_ts_regs { + u16 cfg_send_flag; + + u16 version_base; + u8 version_len; + + u16 pid; + u8 pid_len; + + u16 vid; + u8 vid_len; + + u16 sensor_id; + u8 sensor_id_mask; + + u16 fw_mask; + u16 fw_status; + u16 cfg_addr; + u16 esd; + u16 command; + u16 coor; + u16 gesture; + u16 fw_request; + u16 proximity; +}; + +/* + * struct goodix_ts_device - ts device data + * @name: device name + * @version: reserved + * @bus_type: i2c or spi + * @board_data: board data obtained from dts + * @normal_cfg: normal config data + * @highsense_cfg: high sense config data + * @hw_ops: hardware operations + * @chip_version: firmware version infomation + * @sleep_cmd: sleep commang + * @gesture_cmd: gesture command + * @dev: device pointer,may be a i2c or spi device + * @of_node: device node + */ +struct goodix_ts_device { + char *name; + int version; + int bus_type; + + u8 ic_type; + struct goodix_ts_regs reg; + + int doze_mode_set_count; + struct mutex doze_mode_lock; + + struct goodix_ts_board_data *board_data; + struct goodix_ts_config *normal_cfg; + struct goodix_ts_config *highsense_cfg; + const struct goodix_ts_hw_ops *hw_ops; + + struct goodix_ts_version chip_version; + struct device *dev; +}; + +/* + * struct goodix_ts_hw_ops - hardware opeartions + * @init: hardware initialization + * @reset: hardware reset + * @read: read data from touch device + * @write: write data to touch device + * @send_cmd: send command to touch device + * @send_config: send configuration data + * @read_version: read firmware version + * @event_handler: touch event handler + * @suspend: put touch device into low power mode + * @resume: put touch device into working mode + */ +struct goodix_ts_hw_ops { + + int (*init)(struct goodix_ts_device *dev); + int (*reset)(struct goodix_ts_device *dev); + int (*read)(struct goodix_ts_device *dev, unsigned int addr, + unsigned char *data, unsigned int len); + int (*write)(struct goodix_ts_device *dev, unsigned int addr, + unsigned char *data, unsigned int len); + int (*read_trans)(struct goodix_ts_device *dev, unsigned int addr, + unsigned char *data, unsigned int len); + int (*write_trans)(struct goodix_ts_device *dev, unsigned int addr, + unsigned char *data, unsigned int len); + int (*send_cmd)(struct goodix_ts_device *dev, + struct goodix_ts_cmd *cmd); + int (*send_config)(struct goodix_ts_device *dev, + struct goodix_ts_config *config); + int (*read_config)(struct goodix_ts_device *dev, + u8 *config_data, u32 config_len); + int (*read_version)(struct goodix_ts_device *dev, + struct goodix_ts_version *version); + int (*event_handler)(struct goodix_ts_device *dev, + struct goodix_ts_event *ts_event); + int (*check_hw)(struct goodix_ts_device *dev); + int (*suspend)(struct goodix_ts_device *dev); + int (*resume)(struct goodix_ts_device *dev); +}; + +/* + * struct goodix_ts_esd - esd protector structure + * @esd_work: esd delayed work + * @esd_on: true - turn on esd protection, false - turn + * off esd protection + * @esd_mutex: protect @esd_on flag + */ +struct goodix_ts_esd { + struct delayed_work esd_work; + struct mutex esd_mutex; + struct notifier_block esd_notifier; + struct goodix_ts_core *ts_core; + bool esd_on; +}; + +/* + * struct godix_ts_core - core layer data struct + * @pdev: core layer platform device + * @ts_dev: hardware layer touch device + * @input_dev: input device + * @avdd: analog regulator + * @pinctrl: pinctrl handler + * @pin_sta_active: active/normal pin state + * @pin_sta_suspend: suspend/sleep pin state + * @ts_event: touch event data struct + * @power_on: power on/off flag + * @irq: irq number + * @irq_enabled: irq enabled/disabled flag + * @suspended: suspend/resume flag + * @ts_notifier: generic notifier + * @ts_esd: esd protector structure + * @fb_notifier: framebuffer notifier + * @early_suspend: early suspend + */ +struct goodix_ts_core { + struct platform_device *pdev; + struct goodix_ts_device *ts_dev; + struct input_dev *input_dev; +#ifdef GOODIX_SENSOR_EN + enum display_state screen_state; + struct mutex state_mutex; + struct goodix_sensor_platform_data *sensor_pdata; +#endif + struct regulator *avdd; + struct regulator *iovdd; +#ifdef CONFIG_PINCTRL + struct pinctrl *pinctrl; + struct pinctrl_state *pin_sta_active; + struct pinctrl_state *pin_sta_suspend; +#endif + struct goodix_ts_event ts_event; + int power_on; + int irq; + size_t irq_trig_cnt; + + atomic_t irq_enabled; + atomic_t suspended; + + bool cfg_group_parsed; + bool update_from_sysfs; + bool gtp_suspended; + bool wakeable; + bool irq_waked; + struct notifier_block ts_notifier; + struct goodix_ts_esd ts_esd; + +#ifdef CONFIG_FB + struct notifier_block fb_notifier; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif +}; + +/* 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, +}; + +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); +}; + +#ifdef GOODIX_SENSOR_EN +struct goodix_sensor_platform_data { + struct input_dev *input_sensor_dev; + struct sensors_classdev ps_cdev; + int sensor_opened; + char sensor_data; /* 0 near, 1 far */ + struct goodix_ts_core *data; +}; +#endif + +/* + * 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 *, char *); + ssize_t (*store)(struct goodix_ext_module *, const char *, size_t); +}; + +/* 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) + +/* + * get board data pointer + */ +static inline struct goodix_ts_board_data *board_data( + struct goodix_ts_core *core) +{ + return core->ts_dev->board_data; +} + +/* + * get touch device pointer + */ +static inline struct goodix_ts_device *ts_device( + struct goodix_ts_core *core) +{ + return core->ts_dev; +} + +/* + * get touch hardware operations pointer + */ +static inline const struct goodix_ts_hw_ops *ts_hw_ops( + struct goodix_ts_core *core) +{ + return core->ts_dev->hw_ops; +} + +/* + * checksum helper functions + * checksum can be u8/le16/be16/le32/be32 format + * NOTE: the caller shoule be responsible for the + * legality of @data and @size parameters, so be + * careful when call these functions. + */ +static inline u8 checksum_u8(u8 *data, u32 size) +{ + u8 checksum = 0; + u32 i; + + for (i = 0; i < size; i++) + checksum += data[i]; + return checksum; +} + +static inline u16 checksum_le16(u8 *data, u32 size) +{ + u16 checksum = 0; + u32 i; + + for (i = 0; i < size; i += 2) + checksum += le16_to_cpup((__le16 *)(data + i)); + return checksum; +} + +static inline u16 checksum_be16(u8 *data, u32 size) +{ + u16 checksum = 0; + u32 i; + + for (i = 0; i < size; i += 2) + checksum += be16_to_cpup((__be16 *)(data + i)); + return checksum; +} + +static inline u32 checksum_le32(u8 *data, u32 size) +{ + u32 checksum = 0; + u32 i; + + for (i = 0; i < size; i += 4) + checksum += le32_to_cpup((__le32 *)(data + i)); + return checksum; +} + +static inline u32 checksum_be32(u8 *data, u32 size) +{ + u32 checksum = 0; + u32 i; + + for (i = 0; i < size; i += 4) + checksum += be32_to_cpup((__be32 *)(data + i)); + return checksum; +} + +/* + * define event action + * EVT_xxx macros are used in opeartions callback + * defined in @goodix_ext_module_funcs to control + * the behaviors of event such as suspend/resume/irq_event. + * generally there are two types of behaviors: + * 1. you want the flow of this event be canceled, + * in this condition, you should return EVT_CANCEL_XXX in + * the operations callback. + * e.g. the firmware update module is updating + * the firmware, you want to cancel suspend flow, + * so you need to return EVT_CANCEL_SUSPEND in + * suspend callback function. + * 2. you want the flow of this event continue, in + * this condition, you should return EVT_HANDLED in + * the callback function. + */ +#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 + +/* + * errno define + * Note: + * 1. bus read/write functions defined in hardware + * layer code(e.g. goodix_xxx_i2c.c) *must* return + * -EBUS if failed to transfer data on bus. + */ +#define EBUS 1000 +#define ETIMEOUT 1001 +#define ECHKSUM 1002 +#define EMEMCMP 1003 + +#define CONFIG_GOODIX_DEBUG +/* log macro */ +#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 boot_log(fmt, arg...) \ + g_info(fmt, ##arg) +#ifdef CONFIG_GOODIX_DEBUG +#define ts_debug(fmt, arg...) \ + pr_info("[GTP-DBG][%s:%d] "fmt"\n", __func__, __LINE__, ##arg) +#else +#define ts_debug(fmt, arg...) do {} while (0) +#endif + +/** + * 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); + +/** + * 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); + +/** + * goodix_ts_irq_enable - Enable/Disable a irq + + * @core_data: pointer to touch core data + * enable: enable or disable irq + * return: 0 ok, <0 failed + */ +int goodix_ts_irq_enable(struct goodix_ts_core *core_data, bool enable); + +struct kobj_type *goodix_get_default_ktype(void); + +/** + * 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); + + +/** + * * 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 *core_data); + +/** + * 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 *core_data); + +int goodix_ts_hw_init(struct goodix_ts_core *core_data); + +int goodix_ts_input_dev_config(struct goodix_ts_core *core_data); + +int goodix_ts_irq_setup(struct goodix_ts_core *core_data); + +int goodix_ts_sysfs_init(struct goodix_ts_core *core_data); + +int goodix_ts_esd_init(struct goodix_ts_core *core); + +int goodix_ts_register_notifier(struct notifier_block *nb); + +int goodix_generic_noti_callback(struct notifier_block *self, + unsigned long action, void *data); + +int goodix_ts_fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); + +int goodix_i2c_read(struct goodix_ts_device *dev, unsigned int reg, + unsigned char *data, unsigned int len); +int goodix_i2c_write(struct goodix_ts_device *dev, unsigned int reg, + unsigned char *data, unsigned int len); + +int goodix_get_channel_num(u32 *sen_num, u32 *drv_num); + +int goodix_get_rawordiff_data(int which, int *data); + +int goodix_nodereg_read(void); +#ifdef GTP_CHARGER +int goodix_set_charger_bit(int state); +int goodix_charger_init(struct goodix_ts_board_data *board_data); +#endif +int goodix_ts_start_suspend(struct goodix_ts_core *core_data); +int goodix_ts_start_resume(struct goodix_ts_core *core_data); +extern void goodix_msg_printf(const char *fmt, ...); + +#endif diff --git a/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_ts_gesture.c b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_ts_gesture.c new file mode 100644 index 000000000000..9b1b18def855 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_ts_gesture.c @@ -0,0 +1,660 @@ +/* + * Goodix GTX5 Gesture Dirver + * + * Copyright (C) 2015 - 2016 Goodix, Inc. + * Authors: Wang Yafei + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "goodix_ts_core.h" + +#define GSX_REG_GESTURE_DATA 0x4100 +#define GSX_REG_GESTURE 0x6F68 + +#define GSX_GESTURE_CMD 0x08 + +#define QUERYBIT(longlong, bit) (!!(longlong[bit/8] & (1 << bit%8))) + +#define GSX_KEY_DATA_LEN 37 + +#ifdef GOODIX_SENSOR_EN +static struct wakeup_source gesture_wakelock; +static struct goodix_ts_core *goodix_core_data; +static int gsx_sensor_init(struct goodix_ts_core *data); + +/***************************************************************************** + * Static variables + *****************************************************************************/ +static struct sensors_classdev __maybe_unused sensors_touch_cdev = { + + .name = "dt-gesture", + .vendor = "Goodix", + .version = 1, + .type = 0, //FIXME SENSOR_TYPE_MOTO_DOUBLE_TAP, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = -1, + .max_delay = 0, + /* WAKE_UP & SPECIAL_REPORT */ + .flags = 1 | 6, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#endif +/* + * struct gesture_module - gesture module data + * @registered: module register state + * @sysfs_node_created: sysfs node state + * @gesture_type: store valied gesture type,each bit stand for a gesture + * @gesture_data: gesture data + * @gesture_ts_cmd: gesture command data + */ +struct gesture_module { + atomic_t registered; + unsigned int kobj_initialized; + rwlock_t rwlock; + unsigned char gesture_type[32]; + unsigned char gesture_data[GSX_KEY_DATA_LEN]; + struct goodix_ext_module module; + struct goodix_ts_cmd cmd; +}; + +static struct gesture_module *gsx_gesture; /*allocated in gesture init module*/ + + +/** + * gsx_gesture_type_show - show valid gesture type + * + * @module: pointer to goodix_ext_module struct + * @buf: pointer to output buffer + * Returns >=0 - succeed,< 0 - failed + */ +static ssize_t gsx_gesture_type_show(struct goodix_ext_module *module, + char *buf) +{ + int count = 0, i, ret = 0; + unsigned char *type; + + if (atomic_read(&gsx_gesture->registered) != 1) { + ts_info("Gesture module not register!"); + return -EPERM; + } + type = kzalloc(256, GFP_KERNEL); + if (!type) + return -ENOMEM; + read_lock(&gsx_gesture->rwlock); + for (i = 0; i < 256; i++) { + if (QUERYBIT(gsx_gesture->gesture_type, i)) { + type[count] = i; + count++; + } + } + type[count] = '\0'; + if (count > 0) { + /* TODO 这里使用scnprintf需要确认一下是否有???*/ + ret = scnprintf(buf, PAGE_SIZE, "%s", type); + } + read_unlock(&gsx_gesture->rwlock); + + kfree(type); + return ret; +} + +/** + * gsx_gesture_type_store - set vailed gesture + * + * @module: pointer to goodix_ext_module struct + * @buf: pointer to valid gesture type + * @count: length of buf + * Returns >0 - valid gestures, < 0 - failed + */ +static ssize_t gsx_gesture_type_store(struct goodix_ext_module *module, + const char *buf, size_t count) +{ + int i; + + if (count <= 0 || count > 256 || buf == NULL) { + ts_err("Parameter error"); + return -EINVAL; + } + + write_lock(&gsx_gesture->rwlock); + memset(gsx_gesture->gesture_type, 0, 32); + for (i = 0; i < count; i++) + gsx_gesture->gesture_type[buf[i]/8] |= (0x1 << buf[i]%8); + write_unlock(&gsx_gesture->rwlock); + + return count; +} + +static ssize_t gsx_gesture_enable_show(struct goodix_ext_module *module, + char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", + atomic_read(&gsx_gesture->registered)); +} + +static ssize_t gsx_gesture_enable_store(struct goodix_ext_module *module, + const char *buf, size_t count) +{ + unsigned int tmp; + int ret; + + if (kstrtouint(buf, 0, &tmp) != 1) { + ts_info("Parameter illegal"); + return -EINVAL; + } + ts_debug("Tmp value =%d", tmp); + + if (tmp == 1) { + if (atomic_read(&gsx_gesture->registered)) { + ts_debug("Gesture module has aready registered"); + return count; + } + ret = goodix_register_ext_module(&gsx_gesture->module); + if (!ret) { + ts_info("Gesture module registered!"); + atomic_set(&gsx_gesture->registered, 1); + } else { + atomic_set(&gsx_gesture->registered, 0); + ts_err("Gesture module register failed"); + } + } else if (tmp == 0) { + if (!atomic_read(&gsx_gesture->registered)) { + ts_debug("Gesture module has aready unregistered"); + return count; + } + ts_debug("Start unregistered gesture module"); + ret = goodix_unregister_ext_module(&gsx_gesture->module); + if (!ret) { + atomic_set(&gsx_gesture->registered, 0); + ts_info("Gesture module unregistered success"); + } else { + atomic_set(&gsx_gesture->registered, 1); + ts_info("Gesture module unregistered failed"); + } + } else { + ts_err("Parameter error!"); + return -EINVAL; + } + return count; +} + +static ssize_t gsx_gesture_data_show(struct goodix_ext_module *module, + char *buf) +{ + int count = GSX_KEY_DATA_LEN; + + if (atomic_read(&gsx_gesture->registered) != 1) { + ts_info("Gesture module not register!"); + return -EPERM; + } + if (!buf || !gsx_gesture->gesture_data[0]) { + ts_info("Parameter error!"); + return -EPERM; + } + read_lock(&gsx_gesture->rwlock); + + count = scnprintf(buf, PAGE_SIZE, "Previous gesture type:0x%x\n", + gsx_gesture->gesture_data[2]); + read_unlock(&gsx_gesture->rwlock); + + return count; +} + +const struct goodix_ext_attribute gesture_attrs[] = { + __EXTMOD_ATTR(type, 0666, gsx_gesture_type_show, + gsx_gesture_type_store), + __EXTMOD_ATTR(enable, 0666, gsx_gesture_enable_show, + gsx_gesture_enable_store), + __EXTMOD_ATTR(data, 0444, gsx_gesture_data_show, NULL) +}; + +static int gsx_gesture_init(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + int i, ret; + struct goodix_ts_device *ts_dev = core_data->ts_dev; +#ifdef GOODIX_SENSOR_EN + static bool initialized_sensor; +#endif + if (!core_data || !ts_dev->hw_ops->write || !ts_dev->hw_ops->read) { + ts_err("Register gesture module failed, ts_core unsupported"); + goto exit_gesture_init; + } + + gsx_gesture->cmd.cmd_reg = GSX_REG_GESTURE; + gsx_gesture->cmd.length = 3; + gsx_gesture->cmd.cmds[0] = GSX_GESTURE_CMD; + gsx_gesture->cmd.cmds[1] = 0x0; + gsx_gesture->cmd.cmds[2] = 0 - GSX_GESTURE_CMD; + gsx_gesture->cmd.initialized = 1; + + memset(gsx_gesture->gesture_type, 0, 32); + memset(gsx_gesture->gesture_data, 0xff, GSX_KEY_DATA_LEN); + + ts_debug("Set gesture type manually"); + memset(gsx_gesture->gesture_type, 0xff, 32); + +#ifdef GOODIX_SENSOR_EN + goodix_core_data = core_data; + if (!initialized_sensor) { + wakeup_source_init(&gesture_wakelock, "poll-wake-lock"); + if (!gsx_sensor_init(core_data)) + initialized_sensor = true; + } +#endif + + if (gsx_gesture->kobj_initialized) + goto exit_gesture_init; + + ret = kobject_init_and_add(&module->kobj, goodix_get_default_ktype(), + &core_data->pdev->dev.kobj, "gesture"); + + if (ret) { + ts_err("Create gesture sysfs node error!"); + goto exit_gesture_init; + } + + for (i = 0; i < (ARRAY_SIZE(gesture_attrs) / ARRAY_SIZE(gesture_attrs)); + i++) { + if (sysfs_create_file(&module->kobj, + &gesture_attrs[i].attr)) { + ts_err("Create sysfs attr file error"); + kobject_put(&module->kobj); + goto exit_gesture_init; + } + } + gsx_gesture->kobj_initialized = 1; + +exit_gesture_init: + return 0; +} +static int gsx_gesture_exit(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + atomic_set(&gsx_gesture->registered, 0); + return 0; +} + +#define NO_MAKE_4 +#ifndef NO_MAKE_4 +static void report_gesture_key(struct input_dev *dev, char keycode) +{ + switch (keycode) { + case 0x11: /* click */ + input_report_key(dev, KEY_F7, 1); + input_sync(dev); + input_report_key(dev, KEY_F7, 0); + input_sync(dev); + break; + case 0x22: /* double click */ + input_report_key(dev, KEY_F6, 1); + input_sync(dev); + input_report_key(dev, KEY_F6, 0); + input_sync(dev); + break; + case 0xaa: /* up swip */ + input_report_key(dev, KEY_F2, 1); + input_sync(dev); + input_report_key(dev, KEY_F2, 0); + input_sync(dev); + break; + case 0xbb: /* right swip */ + input_report_key(dev, KEY_F5, 1); + input_sync(dev); + input_report_key(dev, KEY_F5, 0); + input_sync(dev); + break; + case 0xab: /* down swip */ + input_report_key(dev, KEY_F3, 1); + input_sync(dev); + input_report_key(dev, KEY_F3, 0); + input_sync(dev); + break; + case 0xba: /* left swip */ + input_report_key(dev, KEY_F4, 1); + input_sync(dev); + input_report_key(dev, KEY_F4, 0); + input_sync(dev); + break; + default: + break; + } +} +#endif + +/** + * gsx_gesture_ist - Gesture Irq handle + * This functions is excuted when interrupt happended and + * ic in doze mode. + * + * @core_data: 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 *core_data, + struct goodix_ext_module *module) +{ + int ret; + unsigned char clear_reg = 0; +#ifdef GOODIX_SENSOR_EN + static int report_cnt = 0; +#endif + unsigned char checksum = 0, temp_data[GSX_KEY_DATA_LEN]; + struct goodix_ts_device *ts_dev = core_data->ts_dev; + struct goodix_ts_cmd *gesture_cmd = &gsx_gesture->cmd; + + if (atomic_read(&core_data->suspended) == 0) + return EVT_CONTINUE; + + /* get ic gesture state*/ + ret = ts_dev->hw_ops->read_trans(core_data->ts_dev, + GSX_REG_GESTURE_DATA, + temp_data, sizeof(temp_data)); + if (ret < 0 || ((temp_data[0] & 0x20) == 0)) { + ts_debug("Read gesture data failed, ret=%d, temp_data[0]=0x%x", + ret, temp_data[0]); + goto gesture_ist_exit; + } + + checksum = checksum_u8(temp_data, sizeof(temp_data)); + if (checksum != 0) { + ts_err("Gesture data checksum error:0x%x", checksum); + ts_info("Gesture data %*ph", (int)sizeof(temp_data), temp_data); + goto re_send_ges_cmd; + } + + ts_debug("Gesture data:"); + ts_debug("data[0-4]0x%x, 0x%x, 0x%x, 0x%x", temp_data[0], temp_data[1], + temp_data[2], temp_data[3]); + + write_lock(&gsx_gesture->rwlock); + memcpy(gsx_gesture->gesture_data, temp_data, sizeof(temp_data)); + write_unlock(&gsx_gesture->rwlock); + + if (QUERYBIT(gsx_gesture->gesture_type, temp_data[2])) { + /* do resume routine */ + ts_info("Gesture match success, resume IC"); +#ifdef GOODIX_SENSOR_EN + input_report_abs(core_data->sensor_pdata->input_sensor_dev, + ABS_DISTANCE, + ++report_cnt); + ts_info("input report: %d", report_cnt); + if (report_cnt >= REPORT_MAX_COUNT) { + report_cnt = 0; + } + input_sync(core_data->sensor_pdata->input_sensor_dev); + //wake_lock_timeout(&gesture_wakelock, msecs_to_jiffies(5000)); +#else + input_report_key(core_data->input_dev, KEY_POWER, 1); + input_sync(core_data->input_dev); + input_report_key(core_data->input_dev, KEY_POWER, 0); + input_sync(core_data->input_dev); +#endif + goto gesture_ist_exit; + } else { + ts_info("Unsupported gesture:%x", temp_data[2]); + } + +re_send_ges_cmd: + if (ts_dev->hw_ops->send_cmd(core_data->ts_dev, gesture_cmd)) + ts_info("warning: failed re_send gesture cmd\n"); +gesture_ist_exit: + ts_dev->hw_ops->write_trans(core_data->ts_dev, GSX_REG_GESTURE_DATA, + &clear_reg, 1); + return EVT_CANCEL_IRQEVT; +} + +/** + * gsx_gesture_before_suspend - execute gesture suspend routine + * This functions is excuted to set ic into doze mode + * + * @core_data: 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 *core_data, + struct goodix_ext_module *module) +{ + int ret; + const struct goodix_ts_hw_ops *hw_ops = core_data->ts_dev->hw_ops; + struct goodix_ts_cmd *gesture_cmd = &gsx_gesture->cmd; + + if (!gesture_cmd->initialized || hw_ops == NULL) { + ts_err("Uninitialized doze command or hw_ops"); + return 0; + } + + ts_info("Before suspend goodix_core_data->wakeable %d!", + goodix_core_data->wakeable); + if (goodix_core_data->wakeable) { + msleep(GTP_60_DLY_MS); + ret = hw_ops->send_cmd(core_data->ts_dev, gesture_cmd); + if (ret != 0) { + ts_err("Send doze command error"); + return 0; + } + + ts_info("Set IC in doze mode"); + msleep(GTP_20_DLY_MS); + atomic_set(&core_data->suspended, 1); + return EVT_CANCEL_SUSPEND; + } else { + return 0; + } +} + +static int gsx_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + int ret; + struct goodix_ts_cmd *gesture_cmd = &gsx_gesture->cmd; + struct goodix_ts_device *ts_dev = goodix_core_data->ts_dev; + + ts_info("Gesture set enable %d!", enable); + mutex_lock(&goodix_core_data->state_mutex); + if (enable == 1) { + goodix_core_data->wakeable = true; + //If touch still work in normal mode and screen on, skip enter into gesture mode. + if (goodix_core_data->gtp_suspended == false + && goodix_core_data->screen_state == SCREEN_ON) { + ts_info("Touch still in normal mode, skip to set to gesture mode"); + mutex_unlock(&goodix_core_data->state_mutex); + return 0; + } + goodix_ts_start_resume(goodix_core_data); + + msleep(GTP_60_DLY_MS); + + ret = ts_dev->hw_ops->send_cmd(goodix_core_data->ts_dev, + gesture_cmd); + if (ret != 0) { + ts_err("Send doze command error"); + mutex_unlock(&goodix_core_data->state_mutex); + return 0; + } + + msleep(GTP_20_DLY_MS); + +#ifdef GTP_CHARGER + //charger off + if ((ts_dev->board_data->charger_detection_enable) && + (USB_DETECT_IN == ts_dev->board_data->charger_detection->usb_connected)) { + goodix_set_charger_bit(USB_DETECT_OUT); + } + + ts_dev->board_data->charger_send_flage_enable = false; +#endif + + ts_info("Set IC in doze mode"); + atomic_set(&goodix_core_data->suspended, 1); + goodix_core_data->gtp_suspended = true; + + } else if (enable == 0) { + goodix_core_data->wakeable = false; + goodix_ts_start_resume(goodix_core_data); + } else { + ts_info("unknown enable symbol\n"); + } + mutex_unlock(&goodix_core_data->state_mutex); + return 0; +} + +#ifdef GOODIX_SENSOR_EN +static int gsx_sensor_init(struct goodix_ts_core *data) +{ + struct goodix_sensor_platform_data *sensor_pdata; + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + ts_err("Failed to allocate device"); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct goodix_sensor_platform_data), + GFP_KERNEL); + if (!sensor_pdata) { + ts_err("Failed to allocate memory"); + goto free_sensor_pdata; + } + data->sensor_pdata = sensor_pdata; + + __set_bit(EV_ABS, sensor_input_dev->evbit); + __set_bit(EV_SYN, sensor_input_dev->evbit); + input_set_abs_params(sensor_input_dev, ABS_DISTANCE, + 0, REPORT_MAX_COUNT, 0, 0); + sensor_input_dev->name = "double-tap"; + data->sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + ts_err("Unable to register device, err=%d", err); + goto free_sensor_input_dev; + } + + sensor_pdata->ps_cdev = sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = gsx_sensor_set_enable; + sensor_pdata->data = data; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(data->sensor_pdata->input_sensor_dev); +free_sensor_input_dev: + input_free_device(data->sensor_pdata->input_sensor_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, sensor_pdata); + data->sensor_pdata = NULL; +exit: + return 1; +} + +int gsx_sensor_remove(struct goodix_ts_core *data) +{ + sensors_classdev_unregister(&data->sensor_pdata->ps_cdev); + input_unregister_device(data->sensor_pdata->input_sensor_dev); + devm_kfree(&data->sensor_pdata->input_sensor_dev->dev, + data->sensor_pdata); + data->sensor_pdata = NULL; + return 0; +} +#endif + +/** + * gsx_gesture_before_resume - execute gesture resume routine + * This functions is excuted to make ic out doze mode + * + * @core_data: pointer to touch core data + * @module: pointer to goodix_ext_module struct + * return: 0 goon execute, EVT_CANCLED stop execute + * + *static int gsx_gesture_before_resume(struct goodix_ts_core *core_data, + * struct goodix_ext_module *module) + *{ + * struct goodix_ts_device *ts_dev = core_data->ts_dev; + * + * ts_dev->hw_ops->reset(ts_dev); + * return 0; + *} + */ + +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 +}; + +int __init goodix_gsx_gesture_init(void) +{ + /* initialize core_data->ts_dev->gesture_cmd*/ + int result; + + ts_info("gesture module init"); + gsx_gesture = kzalloc(sizeof(struct gesture_module), GFP_KERNEL); + if (!gsx_gesture) + result = -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; + gsx_gesture->kobj_initialized = 0; + atomic_set(&gsx_gesture->registered, 0); + rwlock_init(&gsx_gesture->rwlock); + result = goodix_register_ext_module(&(gsx_gesture->module)); + if (result == 0) + atomic_set(&gsx_gesture->registered, 1); + + return result; +} + +void __exit goodix_gsx_gesture_exit(void) +{ + ts_info("gesture module exit"); +#ifdef GOODIX_SENSOR_EN + wakeup_source_trash(&gesture_wakelock); +#endif + if (gsx_gesture->kobj_initialized) + kobject_put(&gsx_gesture->module.kobj); + kfree(gsx_gesture); + /*return;*/ +} + +MODULE_DESCRIPTION("Goodix gsx Touchscreen Gesture Module"); +MODULE_AUTHOR("Goodix, Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_ts_i2c.c b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_ts_i2c.c new file mode 100644 index 000000000000..b5c7f17b7e8b --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_ts_i2c.c @@ -0,0 +1,2396 @@ +/* + * Goodix GTx5 I2C Dirver + * Hardware interface layer of touchdriver architecture. + * + * Copyright (C) 2015 - 2016 Goodix, Inc. + * Authors: Yulong Cai + * + * 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 +#include +#include +#include +#include +#include +#include +#include "goodix_ts_core.h" +#include "goodix_cfg_bin.h" +#ifdef GTP_CHARGER +#include +#include "goodix_ts_core.h" +#endif +#define TS_DT_COMPATIBLE "goodix,goodix_mmi" +#define TS_DRIVER_NAME "goodix_i2c" +#define I2C_MAX_TRANSFER_SIZE 256 +#define TS_ADDR_LENGTH 2 + +#define TS_REG_COORDS_BASE 0x824E +#define TS_REG_CMD 0x8040 +#define TS_REG_REQUEST 0x8044 +#define TS_REG_VERSION 0x8240 +#define TS_REG_CFG_BASE 0x8050 + +#define CFG_XMAX_OFFSET (0x8052 - 0x8050) +#define CFG_YMAX_OFFSET (0x8054 - 0x8050) + +#define REQUEST_HANDLED 0x00 +#define REQUEST_CONFIG 0x01 +#define REQUEST_BAKREF 0x02 +#define REQUEST_RESET 0x03 +#define REQUEST_MAINCLK 0x04 +#define REQUEST_IDLE 0x05 + +#define COMMAND_SLEEP 0X05 +#define COMMAND_CLOSE_HID 0Xaa +#define COMMAND_START_SEND_CFG 0x80 +#define COMMAND_END_SEND_CFG 0x83 +#define COMMAND_SEND_SMALL_CFG 0x81 + +#define BYTES_PER_COORD 8 +#define TS_MAX_SENSORID 9 +#define TS_CFG_MAX_LEN 1024 +#if TS_CFG_MAX_LEN > GOODIX_CFG_MAX_SIZE +#error GOODIX_CFG_MAX_SIZE too small, please fix. +#endif + +#ifdef GTP_CHARGER +int usb_detect_flag; +#endif +#ifdef 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 r, err; + + r = of_property_read_u32(node, "goodix,panel-max-id", + &board_data->panel_max_id); + if (r) { + err = -ENOENT; + } else { + if (board_data->panel_max_id > GOODIX_MAX_TOUCH) + board_data->panel_max_id = GOODIX_MAX_TOUCH; + } + + r = of_property_read_u32(node, "goodix,panel-max-x", + &board_data->panel_max_x); + if (r) + err = -ENOENT; + + r = of_property_read_u32(node, "goodix,panel-max-y", + &board_data->panel_max_y); + if (r) + err = -ENOENT; + + r = of_property_read_u32(node, "goodix,panel-max-w", + &board_data->panel_max_w); + if (r) + err = -ENOENT; + + r = of_property_read_u32(node, "goodix,panel-max-p", + &board_data->panel_max_p); + if (r) + err = -ENOENT; + + board_data->swap_axis = of_property_read_bool(node, + "goodix,swap-axis"); + + board_data->x2x = of_property_read_bool(node, + "goodix,x2x"); + + board_data->y2y = of_property_read_bool(node, + "goodix,y2y"); + + return 0; +} + +#ifdef GTP_CHARGER +static void goodix_charger_notify_work(struct work_struct *work) +{ + if (work == NULL) { + ts_err("%s: parameter work is null!\n", __func__); + return; + } + + if (usb_detect_flag == USB_DETECT_IN) + goodix_set_charger_bit(USB_DETECT_IN); + else + goodix_set_charger_bit(USB_DETECT_OUT); +} + +static int charger_notifier_callback(struct notifier_block *nb, + unsigned long val, void *v) +{ + int ret = 0; + struct power_supply *psy = NULL; + struct usb_charger_detection *charger_detection = + container_of(nb, struct usb_charger_detection, + charger_notif); + union power_supply_propval prop; + + psy = power_supply_get_by_name("usb"); + + if (!psy) { + return -EINVAL; + ts_err("Couldn't get usbpsy\n"); + } + + if (!strcmp(psy->desc->name, "usb")) { + if (psy && charger_detection && val == + POWER_SUPPLY_PROP_STATUS) { + + ret = power_supply_get_property(psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (ret < 0) + ts_err("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", + ret); + else { + usb_detect_flag = prop.intval; + if (usb_detect_flag != + charger_detection->usb_connected) { + if (usb_detect_flag == USB_DETECT_IN) + charger_detection->usb_connected = + USB_DETECT_IN; + else + charger_detection->usb_connected = + USB_DETECT_OUT; + + queue_work( + charger_detection->goodix_charger_notify_wq, + &charger_detection->charger_notify_work); + } + } + } + } + return ret; +} + +int goodix_charger_init(struct goodix_ts_board_data *board_data) +{ + int r; + + if (board_data->charger_detection_enable) { + board_data->charger_detection = + kzalloc(sizeof(struct usb_charger_detection), GFP_KERNEL); + if (!board_data->charger_detection) + goto err_charger_detection_alloc_failed; + } + + if (board_data->charger_detection) { + struct power_supply *psy = NULL; + union power_supply_propval prop = {0}; + + board_data->charger_detection->usb_connected = 0; + board_data->charger_detection->goodix_charger_notify_wq = + create_singlethread_workqueue("goodix_charger_wq"); + if (!board_data->charger_detection->goodix_charger_notify_wq) { + ts_err("allocate goodix_charger_notify_wq failed\n"); + goto err_charger_notify_wq_failed; + } + + INIT_WORK( + &board_data->charger_detection->charger_notify_work, + goodix_charger_notify_work); + + board_data->charger_detection->charger_notif.notifier_call = charger_notifier_callback; + r = power_supply_reg_notifier( + &board_data->charger_detection->charger_notif); + if (r) { + ts_err("Unable to register charger_notifier:%d\n", r); + goto err_register_charger_notify_failed; + } + + /* if power supply supplier registered brfore TP + * ps_notify_callback will not receive PSY_EVENT_PROP_ADDED + * event, and will cause miss to set TP into charger state. + * So check PS state in probe. + */ + psy = power_supply_get_by_name("usb"); + + if (psy) { + r = power_supply_get_property(psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (r < 0) { + ts_err("Couldn't get POWER_SUPPLY_PROP_ONLINE rc one\n"); + goto err_register_charger_notify_failed; + } else { + usb_detect_flag = prop.intval; + if (usb_detect_flag != + board_data->charger_detection->usb_connected) { + if (usb_detect_flag == USB_DETECT_IN) + board_data->charger_detection->usb_connected + = USB_DETECT_IN; + else { + board_data->charger_detection->usb_connected + = USB_DETECT_OUT; + } + + goodix_set_charger_bit( + board_data->charger_detection->usb_connected); + } + } + } + } + + return 0; + +err_register_charger_notify_failed: + ts_info("usb psy err"); + if (board_data->charger_detection) { + if (board_data->charger_detection->charger_notif.notifier_call) + power_supply_unreg_notifier( + &board_data->charger_detection->charger_notif); + destroy_workqueue( + board_data->charger_detection-> + goodix_charger_notify_wq); + board_data->charger_detection->goodix_charger_notify_wq = NULL; + kfree(board_data->charger_detection); + } +err_charger_detection_alloc_failed: +err_charger_notify_wq_failed: + + return 0; +} +#endif + +/** + * 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) +{ + struct property *prop; + int r; + + if (!board_data) { + ts_err("Invalid board data"); + return -EINVAL; + } + + r = of_get_named_gpio(node, "goodix,reg-en-gpio", 0); + if (r < 0) { + ts_err("failed to get pwr gpio!"); + } + + ts_info("Parse reg-en-gpio[%d] from dt", r); + board_data->reg_en_gpio = r; + + 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("Parse 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("Parse 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; + } + + board_data->cfg_bin_name = kzalloc(32 * sizeof(char), GFP_KERNEL); + r = of_property_read_string(node, "goodix,cfg_bin_name", + &board_data->cfg_bin_name); + if (r) { + ts_err("Unable to read cfg name %d\n", r); + board_data->cfg_bin_name = NULL; + } + + board_data->avdd_name = "vtouch"; + board_data->iovdd_name = "iovtouch"; + r = of_property_read_u32(node, "goodix,power-on-delay-us", + &board_data->power_on_delay_us); + if (!r) { + /* 1000ms is too large, maybe you have pass + * a wrong value + */ + if (board_data->power_on_delay_us > 1000 * 1000) { + ts_err("Power on delay time exceed 1s, please check"); + board_data->power_on_delay_us = 0; + } + } + + r = of_property_read_u32(node, "goodix,power-off-delay-us", + &board_data->power_off_delay_us); + if (!r) { + /* 1000ms is too large, maybe you have pass + * a wrong value + */ + if (board_data->power_off_delay_us > 1000 * 1000) { + ts_err("Power off delay time exceed 1s, please check"); + board_data->power_off_delay_us = 0; + } + } + + /* get xyz resolutions */ + r = goodix_parse_dt_resolution(node, board_data); + if (r < 0) { + ts_err("Failed to parse resolutions:%d", r); + return r; + } + + r = of_property_read_u32(node, "goodix,control-dsi", + &board_data->ctrl_dsi); + if (!r) + ts_info("control DSI %d", board_data->ctrl_dsi); + + /* key map */ + prop = of_find_property(node, "goodix,panel-key-map", NULL); + if (prop && prop->length) { + if (prop->length / sizeof(u32) > GOODIX_MAX_KEY) { + ts_err("Size of panel-key-map is invalid"); + return r; + } + + board_data->panel_max_key = prop->length / sizeof(u32); + board_data->tp_key_num = prop->length / sizeof(u32); + r = of_property_read_u32_array(node, + "goodix,panel-key-map", + &board_data->panel_key_map[0], + board_data->panel_max_key); + if (r) + return r; + } + + /* get debug register address version */ + r = of_property_read_u32(node, "goodix,debug-reg-ver", + &board_data->debug_reg_ver); + if (r) { + board_data->debug_reg_ver = 0; + ts_err("Invalid debug-reg-ver"); + } + + /*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) { + prop = of_find_property(node, "goodix,key-of-pen", NULL); + if (prop && prop->length) { + if (prop->length / sizeof(u32) > GOODIX_PEN_MAX_KEY) { + ts_err("Size of key-of-pen is invalid"); + return r; + } + r = of_property_read_u32_array(node, + "goodix,key-of-pen", + &board_data->panel_key_map + [board_data->panel_max_key], + prop->length / sizeof(u32)); + if (r) + return r; + + board_data->panel_max_key += + (prop->length / sizeof(u32)); + } + } + ts_info("***key:%d, %d, %d, %d, %d", + board_data->panel_key_map[0], + board_data->panel_key_map[1], + board_data->panel_key_map[2], + board_data->panel_key_map[3], + board_data->panel_key_map[4]); + /*add end*/ + + + ts_debug("[DT]id:%d, x:%d, y:%d, w:%d, p:%d", + board_data->panel_max_id, + board_data->panel_max_x, + board_data->panel_max_y, + board_data->panel_max_w, + board_data->panel_max_p); + +#ifdef GTP_CHARGER + if (of_property_read_bool(node, "goodix,usb_charger")) { + ts_info("goodix,usb_charger set"); + board_data->charger_detection_enable = true; + usb_detect_flag = 0; + } else + board_data->charger_detection_enable = false; +#endif + return 0; +} + +/** + * goodix_parse_customize_params - parse sensor independent params + * @dev: pointer to device data + * @board_data: board data + * @sensor_id: sensor ID + * return: 0 - read ok, < 0 - i2c transter error + */ +static int goodix_parse_customize_params(struct goodix_ts_device *dev, + struct goodix_ts_board_data *board_data, + unsigned int sensor_id) +{ + struct device_node *node = dev->dev->of_node; + char of_node_name[24]; + int r; + + if (sensor_id > TS_MAX_SENSORID || node == NULL) { + ts_err("Invalid sensor id"); + return -EINVAL; + } + + /* parse sensor independent parameters */ + snprintf(of_node_name, sizeof(of_node_name), + "sensor%u", sensor_id); + node = of_find_node_by_name(dev->dev->of_node, of_node_name); + if (!node) { + ts_err("Child property[%s] not found", of_node_name); + return -EINVAL; + } + + /* sensor independent resolutions */ + r = goodix_parse_dt_resolution(node, board_data); + return r; +} +#endif + +#ifdef CONFIG_ACPI +static int goodix_parse_acpi(struct acpi_device *dev, + struct goodix_ts_board_data *bdata) +{ + return 0; +} + +static int goodix_parse_acpi_cfg(struct acpi_device *dev, + char *cfg_type, struct goodix_ts_config *config, + unsigned int sensor_id) +{ + return 0; +} +#endif + + +/** + * goodix_i2c_read_trans - read device register through i2c bus + * @dev: pointer to device data + * @addr: register address + * @data: read buffer + * @len: bytes to read + * return: 0 - read ok, < 0 - i2c transter error + */ +int goodix_i2c_read_trans(struct goodix_ts_device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + struct i2c_client *client = to_i2c_client(dev->dev); + unsigned int transfer_length = 0; + unsigned int pos = 0, address = reg; + unsigned char get_buf[32], addr_buf[2]; + int retry, r = 0; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = !I2C_M_RD, + .buf = &addr_buf[0], + .len = TS_ADDR_LENGTH, + }, { + .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 > I2C_MAX_TRANSFER_SIZE + ? I2C_MAX_TRANSFER_SIZE : 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 >> 8) & 0xFF; + msgs[0].buf[1] = 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); + msleep(GTP_20_DLY_MS); + } + if (unlikely(retry == GOODIX_BUS_RETRY_TIMES)) { + ts_err("I2c read failed,dev:%02x,reg:%04x,size:%u", + client->addr, reg, len); + r = -EBUS; + goto read_exit; + } + } + +read_exit: + if (unlikely(len >= sizeof(get_buf))) + kfree(msgs[1].buf); + return r; +} + +/** + * goodix_i2c_write_trans - write device register through i2c bus + * @dev: pointer to device data + * @addr: register address + * @data: write buffer + * @len: bytes to write + * return: 0 - write ok; < 0 - i2c transter error. + */ +int goodix_i2c_write_trans(struct goodix_ts_device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + struct i2c_client *client = to_i2c_client(dev->dev); + unsigned int pos = 0, transfer_length = 0; + unsigned int address = reg; + unsigned char put_buf[32]; + int retry, r = 0; + struct i2c_msg msg = { + .addr = client->addr, + .flags = !I2C_M_RD, + }; + + if (likely(len + TS_ADDR_LENGTH < sizeof(put_buf))) { + /* code optimize,use stack memory*/ + msg.buf = &put_buf[0]; + } else { + msg.buf = kmalloc((len + TS_ADDR_LENGTH) > I2C_MAX_TRANSFER_SIZE + ? I2C_MAX_TRANSFER_SIZE : len + TS_ADDR_LENGTH, + GFP_KERNEL); + if (msg.buf == NULL) { + ts_err("Malloc failed"); + return -ENOMEM; + } + } + + while (pos != len) { + if (unlikely(len - pos > + I2C_MAX_TRANSFER_SIZE - TS_ADDR_LENGTH)) + transfer_length = + I2C_MAX_TRANSFER_SIZE - TS_ADDR_LENGTH; + else + transfer_length = len - pos; + + msg.buf[0] = (unsigned char)((address >> 8) & 0xFF); + msg.buf[1] = (unsigned char)(address & 0xFF); + msg.len = transfer_length + 2; + memcpy(&msg.buf[2], &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_info("I2c write retry[%d]", retry + 1); + msleep(GTP_20_DLY_MS); + } + if (unlikely(retry == GOODIX_BUS_RETRY_TIMES)) { + ts_err("I2c write failed,dev:%02x,reg:%04x,size:%u", + client->addr, reg, len); + r = -EBUS; + goto write_exit; + } + } + +write_exit: + if (likely(len + TS_ADDR_LENGTH >= sizeof(put_buf))) + kfree(msg.buf); + return r; +} + + +/** + * goodix_set_i2c_doze_mode - disable or enable doze mode + * @dev: pointer to device data + * @enable: true/flase + * return: 0 - ok; < 0 - error. + * This func must be used in pairs, when you disable doze + * mode, then you must enable it again. + * Between set_doze_false and set_doze_true, do not reset + * IC! + */ +int goodix_set_i2c_doze_mode(struct goodix_ts_device *dev, int enable) +{ + int result = -EINVAL; + int i, j; + u8 w_data, r_data; + + if (dev->ic_type != IC_TYPE_NORMANDY) + return 0; + + mutex_lock(&dev->doze_mode_lock); + + if (enable) { + if (dev->doze_mode_set_count != 0) + dev->doze_mode_set_count--; + + /*when count equal 0, allow ic enter doze mode*/ + if (dev->doze_mode_set_count == 0) { + w_data = 0xCC; + for (i = 0; i < 3; i++) { + result = goodix_i2c_write_trans(dev, 0x30F0, + &w_data, 1); + if (!result) { + result = 0; + goto exit; + } + usleep_range(GTP_1000_DLY_US, GTP_1100_DLY_US); + } + if (i >= 3) + ts_err("i2c doze mode enable failed, i2c write fail"); + } else { + result = 0; + goto exit; + } + } else { + dev->doze_mode_set_count++; + + if (dev->doze_mode_set_count == 1) { + w_data = 0xAA; + goodix_i2c_write_trans(dev, 0x30F0, &w_data, 1); + usleep_range(GTP_1000_DLY_US, GTP_1100_DLY_US); + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + goodix_i2c_read_trans(dev, 0x3100, + &r_data, 1); + if (r_data == 0xBB) { + result = 0; + goto exit; + } else if (r_data == 0xAA) { + usleep_range(GTP_10000_DLY_US, + GTP_10100_DLY_US); + continue; + } else { + w_data = 0xAA; + goodix_i2c_write_trans(dev, + 0x30F0, &w_data, 1); + usleep_range(GTP_1000_DLY_US, + GTP_1100_DLY_US); + } + } + } + ts_err("doze mode disable FAILED"); + } else { + result = 0; + goto exit; + } + } + +exit: + mutex_unlock(&dev->doze_mode_lock); + return result; +} + +/** + * goodix_i2c_write - write device register through i2c bus + * @dev: pointer to device data + * @addr: register address + * @data: write buffer + * @len: bytes to write + * return: 0 - write ok; < 0 - i2c transter error. + */ +int goodix_i2c_write(struct goodix_ts_device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + int r; + + if (dev->ic_type == IC_TYPE_NORMANDY) { + if (goodix_set_i2c_doze_mode(dev, false) != 0) + ts_err("gtx8 i2c write:0x%04x ERROR, disable doze mode FAILED", + reg); + } + + r = goodix_i2c_write_trans(dev, reg, data, len); + + if (dev->ic_type == IC_TYPE_NORMANDY) { + if (goodix_set_i2c_doze_mode(dev, true) != 0) + ts_err("gtx8 i2c write:0x%04x ERROR, enable doze mode FAILED", + reg); + } + + return r; +} + + +/** + * goodix_i2c_read - read device register through i2c bus + * @dev: pointer to device data + * @addr: register address + * @data: read buffer + * @len: bytes to read + * return: 0 - read ok, < 0 - i2c transter error + */ +int goodix_i2c_read(struct goodix_ts_device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + int r; + + if (dev->ic_type == IC_TYPE_NORMANDY) { + if (goodix_set_i2c_doze_mode(dev, false) != 0) + ts_err("gtx8 i2c read:0x%04x ERROR, disable doze mode FAILED", + reg); + } + + r = goodix_i2c_read_trans(dev, reg, data, len); + + if (dev->ic_type == IC_TYPE_NORMANDY) { + if (goodix_set_i2c_doze_mode(dev, true) != 0) + ts_err("gtx8 i2c read:0x%04x ERROR, enable doze mode FAILED", + reg); + } + + return r; +} + +/** + * goodix_i2c_write_trans_once - + * write device register through i2c bus, no retry + * @dev: pointer to device data + * @addr: register address + * @data: write buffer + * @len: bytes to write + * return: 0 - write ok; < 0 - i2c transter error. + */ +int goodix_i2c_write_trans_once(struct goodix_ts_device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + struct i2c_client *client = to_i2c_client(dev->dev); + unsigned int pos = 0, transfer_length = 0; + unsigned int address = reg; + unsigned char put_buf[32]; + struct i2c_msg msg = { + .addr = client->addr, + .flags = !I2C_M_RD, + }; + + if (likely(len + TS_ADDR_LENGTH < sizeof(put_buf))) { + /* code optimize,use stack memory*/ + msg.buf = &put_buf[0]; + } else { + msg.buf = kmalloc((len + TS_ADDR_LENGTH) > I2C_MAX_TRANSFER_SIZE + ? I2C_MAX_TRANSFER_SIZE : + len + TS_ADDR_LENGTH, + GFP_KERNEL); + if (msg.buf == NULL) { + ts_err("Malloc failed"); + return -ENOMEM; + } + } + + while (pos != len) { + if (unlikely(len - pos > + I2C_MAX_TRANSFER_SIZE - TS_ADDR_LENGTH)) + transfer_length = + I2C_MAX_TRANSFER_SIZE - TS_ADDR_LENGTH; + else + transfer_length = len - pos; + + msg.buf[0] = (unsigned char)((address >> 8) & 0xFF); + msg.buf[1] = (unsigned char)(address & 0xFF); + msg.len = transfer_length + 2; + memcpy(&msg.buf[2], &data[pos], transfer_length); + + i2c_transfer(client->adapter, &msg, 1); + pos += transfer_length; + address += transfer_length; + } + + if (likely(len + TS_ADDR_LENGTH >= sizeof(put_buf))) + kfree(msg.buf); + return 0; +} + +static void goodix_cmds_init(struct goodix_ts_cmd *ts_cmd, + u8 cmds, u8 cmd_data, u32 reg_addr) +{ + if (reg_addr) { + ts_cmd->cmd_reg = reg_addr; + ts_cmd->length = 3; + ts_cmd->cmds[0] = cmds; + ts_cmd->cmds[1] = cmd_data; + ts_cmd->cmds[2] = 0 - cmds - cmd_data; + ts_cmd->initialized = true; + } else { + ts_cmd->initialized = false; + } +} + +/** + * goodix_send_command - seng cmd to firmware + * + * @dev: pointer to device + * @cmd: pointer to command struct which cotain command data + * Returns 0 - succeed,<0 - failed + */ +int goodix_send_command(struct goodix_ts_device *dev, + struct goodix_ts_cmd *cmd) +{ + int ret; + + if (!cmd || !cmd->initialized) + return -EINVAL; + + ret = goodix_i2c_write(dev, cmd->cmd_reg, cmd->cmds, + cmd->length); + + return ret; +} + + +static int goodix_read_version(struct goodix_ts_device *dev, + struct goodix_ts_version *version) +{ + u8 buffer[12]; + u8 temp_buf[256], checksum; + int r; + u8 pid_read_len = dev->reg.pid_len; + u8 vid_read_len = dev->reg.vid_len; + u8 sensor_id_mask = dev->reg.sensor_id_mask; +#define IS_CHAR(c) (((c) >= 'A' && (c) <= 'Z')\ + || ((c) >= 'a' && (c) <= 'z')\ + || ((c) >= '0' && (c) <= '9')) + + if (!version) { + ts_err("pointer of version is NULL"); + return -EINVAL; + } + version->valid = false; + + /*check reg info valid*/ + if (!dev->reg.pid || !dev->reg.sensor_id || !dev->reg.vid) { + ts_err("reg is NULL, pid:0x%04x, vid:0x%04x, sensor_id:0x%04x", + dev->reg.pid, dev->reg.vid, dev->reg.sensor_id); + return -EINVAL; + } + if (!pid_read_len || pid_read_len > 8 || + !vid_read_len || vid_read_len > 8) { + ts_err("pid vid read len ERROR, pid_read_len:%d, vid_read_len:%d", + pid_read_len, vid_read_len); + return -EINVAL; + } + + /*disable doze mode, just valid for normandy + * this func must be used in pairs + */ + goodix_set_i2c_doze_mode(dev, false); + + /*check checksum*/ + if (dev->reg.version_base && dev->reg.version_len < 256) { + r = goodix_i2c_read(dev, dev->reg.version_base, + temp_buf, dev->reg.version_len); + + if (r < 0) { + ts_err("Read version base failed, reg:0x%02x, len:%d", + dev->reg.version_base, dev->reg.version_len); + if (version) + version->valid = false; + goto exit; + } + + checksum = checksum_u8(temp_buf, dev->reg.version_len); + if (checksum) { + ts_err("checksum error:0x%02x, base:0x%02x, len:%d", + checksum, dev->reg.version_base, + dev->reg.version_len); + ts_err("%*ph", + (int)(dev->reg.version_len / 2), temp_buf); + ts_err("%*ph", + (int)(dev->reg.version_len - + dev->reg.version_len / 2), + &temp_buf[dev->reg.version_len / 2]); + + if (version) + version->valid = false; + r = -EINVAL; + goto exit; + } + } + + /*read pid*/ + memset(buffer, 0, sizeof(buffer)); + memset(version->pid, 0, sizeof(version->pid)); + r = goodix_i2c_read(dev, dev->reg.pid, buffer, pid_read_len); + if (r < 0) { + ts_err("Read pid failed"); + if (version) + version->valid = false; + goto exit; + } + memcpy(version->pid, buffer, pid_read_len); + + + /*read vid*/ + memset(buffer, 0, sizeof(buffer)); + memset(version->vid, 0, sizeof(version->vid)); + r = goodix_i2c_read(dev, dev->reg.vid, buffer, vid_read_len); + if (r < 0) { + ts_err("Read vid failed"); + if (version) + version->valid = false; + goto exit; + } + memcpy(version->vid, buffer, vid_read_len); + + /*read sensor_id*/ + memset(buffer, 0, sizeof(buffer)); + r = goodix_i2c_read(dev, dev->reg.sensor_id, buffer, 1); + if (r < 0) { + ts_err("Read sensor_id failed"); + if (version) + version->valid = false; + goto exit; + } + if (sensor_id_mask != 0) { + version->sensor_id = buffer[0] & sensor_id_mask; + ts_info("sensor_id_mask:0x%02x, sensor_id:0x%02x", + sensor_id_mask, version->sensor_id); + } else { + version->sensor_id = buffer[0]; + } + + version->valid = true; + + ts_info("PID:%s,SensorID:%d, VID:%*ph", + version->pid, + version->sensor_id, + (int)sizeof(version->vid), + version->vid); +exit: + /*enable doze mode, just valid for normandy + * this func must be used in pairs + */ + goodix_set_i2c_doze_mode(dev, true); + + return r; +} + +#define NO_MAKE_1 +#ifndef NO_MAKE_1 +static int goodix_read_version(struct goodix_ts_device *dev, + struct goodix_ts_version *version) +{ + u8 buffer[12]; + int r; +#define IS_CHAR(c) (((c) >= 'A' && (c) <= 'Z')\ + || ((c) >= 'a' && (c) <= 'z')\ + || ((c) >= '0' && (c) <= '9')) + + r = goodix_i2c_read(dev, dev->reg.pid,/*TS_REG_VERSION*/ + buffer, sizeof(buffer)); + if (r < 0) { + ts_err("Read chip version failed"); + if (version) + version->valid = false; + return r; + } + + /* if checksum is right and first 4 bytes are + * not invalid value + */ + if (checksum_u8(buffer, sizeof(buffer)) == 0 && + IS_CHAR(buffer[0]) && IS_CHAR(buffer[1]) && + IS_CHAR(buffer[2]) && IS_CHAR(buffer[3])) { + if (version) { + memcpy(&version->pid[0], buffer, 4); + version->pid[4] = '\0'; + version->cid = buffer[4]; + /* vid = main version + minor version */ + version->vid = (buffer[5] << 8) + buffer[6]; + version->sensor_id = buffer[10] & 0x0F; + version->valid = true; + + if (version->cid) + ts_info( + "PID:%s,CID: %c,VID:%04x,SensorID:%u", + version->pid, version->cid + 'A' - 1, + version->vid, version->sensor_id); + else + ts_info("PID:%s,VID:%04x,SensorID:%u", + version->pid, version->vid, + version->sensor_id); + } + } else { + ts_err("Checksum error:%*ph", (int)sizeof(buffer), buffer); + /* mark this version is invalid */ + if (version) + version->valid = false; + r = -EINVAL; + } + + return r; +} +#endif + +static int goodix_send_small_config(struct goodix_ts_device *dev, + struct goodix_ts_config *config) +{ + int r = 0; + int try_times = 0; + u8 buf = 0; + u16 command_reg = dev->reg.command; + u16 cfg_reg = dev->reg.cfg_addr; + struct goodix_ts_cmd ts_cmd; + + /*1. Inquire command_reg until it's free*/ + for (try_times = 0; try_times < 10; try_times++) { + if (!goodix_i2c_read(dev, command_reg, &buf, 1) && buf == 0xff) + break; + usleep_range(GTP_10000_DLY_US, GTP_11000_DLY_US); + } + if (try_times >= 10) { + ts_err("Send small cfg FAILED, before send, reg:0x%04x is not 0xff", + command_reg); + r = -EINVAL; + goto exit; + } + + /*2. write cfg data*/ + if (goodix_i2c_write(dev, cfg_reg, config->data, config->length)) { + ts_err("Send small cfg FAILED, write cfg to fw ERROR"); + r = -EINVAL; + goto exit; + } + + /*3. send 0x81 command*/ + goodix_cmds_init(&ts_cmd, COMMAND_SEND_SMALL_CFG, 0, dev->reg.command); + if (goodix_send_command(dev, &ts_cmd)) { + ts_err("Send large cfg FAILED, send COMMAND_SEND_SMALL_CFG ERROR"); + r = -EINVAL; + goto exit; + } + + r = 0; + ts_info("send small cfg SUCCESS"); + +exit: + return r; +} + +static int goodix_send_large_config(struct goodix_ts_device *dev, + struct goodix_ts_config *config) +{ + int r = 0; + int try_times = 0; + u8 buf = 0; + u16 command_reg = dev->reg.command; + u16 cfg_reg = dev->reg.cfg_addr; + struct goodix_ts_cmd ts_cmd; + + /*1. Inquire command_reg until it's free*/ + for (try_times = 0; try_times < 10; try_times++) { + if (!goodix_i2c_read(dev, command_reg, &buf, 1) && buf == 0xff) + break; + usleep_range(GTP_10000_DLY_US, GTP_11000_DLY_US); + } + if (try_times >= 10) { + ts_err("Send large cfg FAILED, before send, reg:0x%04x is not 0xff", + command_reg); + r = -EINVAL; + goto exit; + } + + /*2. send "start write cfg" command*/ + goodix_cmds_init(&ts_cmd, COMMAND_START_SEND_CFG, 0, dev->reg.command); + if (goodix_send_command(dev, &ts_cmd)) { + ts_err("Send large cfg FAILED, send COMMAND_START_SEND_CFG ERROR"); + r = -EINVAL; + goto exit; + } + + /*3. wait ic set command_reg to 0x82*/ + for (try_times = 0; try_times < 10; try_times++) { + if (!goodix_i2c_read(dev, command_reg, &buf, 1) && buf == 0x82) + break; + usleep_range(GTP_10000_DLY_US, GTP_11000_DLY_US); + } + if (try_times >= 10) { + ts_err("Send large cfg FAILED, reg:0x%04x is not 0x82", + command_reg); + r = -EINVAL; + goto exit; + } + + /*4. write cfg*/ + if (goodix_i2c_write(dev, cfg_reg, config->data, config->length)) { + ts_err("Send large cfg FAILED, write cfg to fw ERROR"); + r = -EINVAL; + goto exit; + } + + /*5. send "end send cfg" command*/ + goodix_cmds_init(&ts_cmd, COMMAND_END_SEND_CFG, 0, dev->reg.command); + if (goodix_send_command(dev, &ts_cmd)) { + ts_err("Send large cfg FAILED, send COMMAND_END_SEND_CFG ERROR"); + r = -EINVAL; + goto exit; + } + + /*6. wait ic set command_reg to 0xff*/ + for (try_times = 0; try_times < 10; try_times++) { + if (!goodix_i2c_read(dev, command_reg, &buf, 1) && buf == 0xff) + break; + usleep_range(GTP_10000_DLY_US, GTP_11000_DLY_US); + } + if (try_times >= 10) { + ts_err("Send large cfg FAILED, after send, reg:0x%04x is not 0xff", + command_reg); + r = -EINVAL; + goto exit; + } + + ts_info("Send large cfg SUCCESS"); + r = 0; + +exit: + return r; +} + +static int goodix_check_cfg_valid(struct goodix_ts_device *dev, + u8 *cfg, u32 length) +{ + int ret; + u8 bag_num; + u8 checksum; + int i, j; + int bag_start = 0; + int bag_end = 0; + + if (!cfg || length < 4) { + ts_err("cfg is INVALID, len:%d", length); + ret = -EINVAL; + goto exit; + } + + if (dev->ic_type == IC_TYPE_NANJING) { + /*check configuration head checksum*/ + checksum = 0; + for (i = 0; i < 3; i++) + checksum += cfg[i]; + + if (checksum != 0) { + ts_err("cfg head checksum ERROR, ic type:nanjing, checksum:0x%02x", + checksum); + ret = -EINVAL; + goto exit; + } + bag_num = cfg[1]; + bag_start = 3; + } else if (dev->ic_type == IC_TYPE_NORMANDY) { + checksum = 0; + for (i = 0; i < 4; i++) + checksum += cfg[i]; + if (checksum != 0) { + ts_err("cfg head checksum ERROR, ic type:normandy, checksum:0x%02x", + checksum); + ret = -EINVAL; + goto exit; + } + bag_num = cfg[2]; + bag_start = 4; + } else { + ts_err("cfg check FAILED, unkonw ic_type"); + ret = -EINVAL; + goto exit; + } + + ts_info("cfg bag_num:%d, cfg length:%d", bag_num, length); + + /*check each bag's checksum*/ + for (j = 0; j < bag_num; j++) { + if (bag_start >= length - 1) { + ts_err("ERROR, overflow!!bag_start:%d, cfg_len:%d", + bag_start, length); + ret = -EINVAL; + goto exit; + } + + bag_end = bag_start + cfg[bag_start + 1] + 3; + if ((j == 0) && (dev->ic_type == IC_TYPE_NANJING)) + /*the first bag of nanjing cfg is different!*/ + bag_end = 336; + + checksum = 0; + if (bag_end > length) { + ts_err("ERROR, overflow!!bag:%d, bag_start:%d, bag_end:%d, cfg length:%d", + j, bag_start, bag_end, length); + ret = -EINVAL; + goto exit; + } + for (i = bag_start; i < bag_end; i++) + checksum += cfg[i]; + if (checksum != 0) { + ts_err("cfg INVALID, bag:%d checksum ERROR:0x%02x", + j, checksum); + ret = -EINVAL; + goto exit; + } + bag_start = bag_end; + } + + ret = 0; + ts_info("configuration check SUCCESS"); + +exit: + return ret; +} + +static int goodix_send_config(struct goodix_ts_device *dev, + struct goodix_ts_config *config) +{ + int r = 0; + + if (!config || !config->data[0]) { + ts_err("Null config data"); + return -EINVAL; + } + + /*check configuration valid*/ + r = goodix_check_cfg_valid(dev, config->data, config->length); + if (r != 0) { + ts_err("cfg check FAILED"); + return -EINVAL; + } + + ts_info("ver:%02xh,size:%d", + config->data[0], + config->length); + + mutex_lock(&config->lock); + + if (dev->ic_type == IC_TYPE_NANJING) + r = goodix_send_large_config(dev, config); + else if (dev->ic_type == IC_TYPE_NORMANDY) { + /*disable doze mode*/ + goodix_set_i2c_doze_mode(dev, false); + + if (config->length > 32) + r = goodix_send_large_config(dev, config); + else + r = goodix_send_small_config(dev, config); + + /*enable doze mode*/ + goodix_set_i2c_doze_mode(dev, true); + } + + if (r != 0) + ts_err("send_cfg FAILED, ic_type:%d, cfg_len:%d", + dev->ic_type, config->length); + + mutex_unlock(&config->lock); + return r; +} + +#define NO_MAKE_2 +#ifndef NO_MAKE_2 +/** + * goodix_send_config - send config data to device. + * @dev: pointer to device + * @config: pointer to config data struct to be send + * @return: 0 - succeed, < 0 - failed + */ +static int goodix_send_config(struct goodix_ts_device *dev, + struct goodix_ts_config *config) +{ + int r = 0; + + if (!config || !config->data) { + ts_err("Null config data"); + return -EINVAL; + } + + ts_info("Send %s,ver:%02xh,size:%d", + config->name, config->data[0], + config->length); + + mutex_lock(&config->lock); + r = goodix_i2c_write(dev, config->reg_base, + config->data, config->length); + if (r) + goto exit; + + /* make sure the firmware accept the config data*/ + if (config->delay) + msleep(config->delay); +exit: + mutex_unlock(&config->lock); + return r; +} +#endif + + +/** + * goodix_close_hidi2c_mode + * Called by touch core module when bootup + * @ts_dev: pointer to touch device + * return: 0 - no error, <0 error + */ +static int goodix_close_hidi2c_mode(struct goodix_ts_device *ts_dev) +{ + int r = 0; + int try_times; + int j; + unsigned char buffer[1]; + unsigned char reg_sta; + struct goodix_ts_cmd ts_cmd; + + for (try_times = 0; try_times < 10; try_times++) { + if (goodix_i2c_read(ts_dev, 0x8040, ®_sta, 1) != 0) + continue; + else if (reg_sta == 0xff) + break; + usleep_range(GTP_10000_DLY_US, GTP_11000_DLY_US); + } + if (try_times >= 10) { + ts_info("goodix_close_hidi2c_mode FAILED, 0x8040 is not equal to 0xff"); + return -EINVAL; + } + + goodix_cmds_init(&ts_cmd, COMMAND_CLOSE_HID, 0, 0x8040); + for (try_times = 0; try_times < 3; try_times++) { + if (ts_cmd.initialized) { + r = goodix_send_command(ts_dev, &ts_cmd); + if (r) + continue; + + usleep_range(GTP_100000_DLY_US, GTP_110000_DLY_US); + + /*read 0x8040, if it's not 0xFF,continue*/ + for (j = 0; j < 3; j++) { + if (goodix_i2c_read(ts_dev, 0x8040, buffer, 1) + != 0) + continue; + else { + if (buffer[0] != 0xFF) { + ts_info("try_times:%d:%d, read 0x8040:0x%02x", + try_times, j, + buffer[0]); + usleep_range(GTP_10000_DLY_US, + GTP_11000_DLY_US); + continue; + } else + goto exit; + } + } + } + } + +exit: + if (try_times >= 3) { + ts_info("close hid_i2c mode FAILED"); + r = -EINVAL; + } else { + ts_info("close hid_i2c mode SUCCESS"); + r = 0; + } + return r; +} + +/* success return config length else return -1 */ +static int _goodix_do_read_config(struct goodix_ts_device *dev, + u32 base_addr, + u8 *buf) +{ + int sub_bags = 0; + int offset = 0; + int subbag_len; + u8 checksum; + int i; + int ret; + + /*disable doze mode*/ + if (dev->ic_type == IC_TYPE_NORMANDY) + goodix_set_i2c_doze_mode(dev, false); + + ret = goodix_i2c_read(dev, base_addr, buf, 4); + if (ret) + goto err_out; + + if (dev->ic_type == IC_TYPE_NANJING) { + offset = 3; + sub_bags = buf[1]; + checksum = checksum_u8(buf, 3); + } else { + offset = 4; + sub_bags = buf[2]; + checksum = checksum_u8(buf, 4); + } + if (checksum) { + ts_err("Config head checksum err:0x%x,data:%*ph", + checksum, 4, buf); + ret = -EINVAL; + goto err_out; + } + + ts_info("config_version:%u, vub_bags:%u", + buf[0], sub_bags); + for (i = 0; i < sub_bags; i++) { + /* read sub head [0]: sub bag num, [1]: sub bag length */ + ret = goodix_i2c_read(dev, base_addr + offset, buf + offset, 2); + if (ret) + goto err_out; + + /* read sub bag data */ + if (dev->ic_type == IC_TYPE_NANJING && i == 0) + subbag_len = buf[offset + 1] + 256; + else + subbag_len = buf[offset + 1]; + + ts_info("sub bag num:%u,sub bag length:%u", + buf[offset], subbag_len); + ret = goodix_i2c_read(dev, base_addr + offset + 2, + buf + offset + 2, + subbag_len + 1); + if (ret) + goto err_out; + checksum = checksum_u8(buf + offset, subbag_len + 3); + if (checksum) { + ts_err("sub bag checksum err:0x%x", checksum); + ret = -EINVAL; + goto err_out; + } + offset += subbag_len + 3; + ts_debug("sub bag %d, data:%*ph", + buf[offset], buf[offset + 1] + 3, + buf + offset); + } + ret = offset; + +err_out: + /*enable doze mode*/ + if (dev->ic_type == IC_TYPE_NORMANDY) + goodix_set_i2c_doze_mode(dev, true); + + return ret; +} + +/* success return config_len, <= 0 failed */ +static int goodix_read_config(struct goodix_ts_device *dev, + u8 *config_data, u32 config_len) +{ + struct goodix_ts_cmd ts_cmd; + u8 cmd_flag; + u32 cmd_reg = dev->reg.command; + int r = 0; + int i; + + if (!config_data || config_len > TS_CFG_MAX_LEN) { + ts_err("Illegal params"); + return -EINVAL; + } + if (!dev->reg.command) { + ts_err("command register ERROR:0x%04x", dev->reg.command); + return -EINVAL; + } + + /*disable doze mode*/ + if (dev->ic_type == IC_TYPE_NORMANDY) + goodix_set_i2c_doze_mode(dev, false); + + /* wait for IC in IDLE state */ + for (i = 0; i < 20; i++) { + cmd_flag = 0; + r = goodix_i2c_read(dev, cmd_reg, &cmd_flag, 1); + if (r < 0 || cmd_flag == 0xFF) + break; + usleep_range(GTP_5000_DLY_US, GTP_5200_DLY_US); + } + if (cmd_flag != 0xFF) { + ts_err("Wait for IC ready IDEL state timeout:addr 0x%x\n", + cmd_reg); + r = -EAGAIN; + goto exit; + } + /* 0x86 read config command */ + goodix_cmds_init(&ts_cmd, 0x86, 0, cmd_reg); + r = goodix_send_command(dev, &ts_cmd); + if (r) { + ts_err("Failed send read config command"); + goto exit; + } + /* wait for config data ready */ + for (i = 0; i < 20; i++) { + cmd_flag = 0; + r = goodix_i2c_read(dev, cmd_reg, &cmd_flag, 1); + if (r < 0 || cmd_flag == 0x85) + break; + usleep_range(GTP_5000_DLY_US, GTP_5200_DLY_US); + } + if (cmd_flag != 0x85) { + ts_err("Wait for config data ready timeout"); + r = -EAGAIN; + goto exit; + } + if (config_len) { + r = goodix_i2c_read(dev, cmd_reg + 16, config_data, config_len); + if (r) + ts_err("Failed read config data"); + else + r = config_len; + } else { + r = _goodix_do_read_config(dev, cmd_reg + 16, config_data); + if (r < 0) + ts_err("Failed read config data"); + } + if (r > 0) + ts_info("success read config, len:%d", r); + /* clear command */ + goodix_cmds_init(&ts_cmd, 0xFF, 0, cmd_reg); + goodix_send_command(dev, &ts_cmd); + + /*enable doze mode*/ + if (dev->ic_type == IC_TYPE_NORMANDY) + goodix_set_i2c_doze_mode(dev, true); + +exit: + return r; +} + +/** + * goodix_hw_init - hardware initialize + * Called by touch core module when bootup + * @ts_dev: pointer to touch device + * return: 0 - no error, <0 error + */ +static int goodix_hw_init(struct goodix_ts_device *ts_dev) +{ + int r; + + BUG_ON(!ts_dev); + + /* goodix_hw_init may be called many times */ + if (!ts_dev->normal_cfg) { + ts_dev->normal_cfg = devm_kzalloc(ts_dev->dev, + sizeof(*ts_dev->normal_cfg), GFP_KERNEL); + if (!ts_dev->normal_cfg) { + return -ENOMEM; + } + mutex_init(&ts_dev->normal_cfg->lock); + } + if (!ts_dev->highsense_cfg) { + ts_dev->highsense_cfg = devm_kzalloc(ts_dev->dev, + sizeof(*ts_dev->highsense_cfg), GFP_KERNEL); + if (!ts_dev->highsense_cfg) { + return -ENOMEM; + } + mutex_init(&ts_dev->highsense_cfg->lock); + } + + /*for Nanjing IC, close HID_I2C mode when driver is probed*/ + if (ts_dev->ic_type == IC_TYPE_NANJING) { + r = goodix_close_hidi2c_mode(ts_dev); + if (r < 0) + ts_info("close hid i2c mode FAILED"); + } + + /* read chip version: PID/VID/sensor ID,etc.*/ + r = goodix_read_version(ts_dev, &ts_dev->chip_version); + if (r < 0) + return r; + + /* devicetree property like resolution(panel_max_xxx) + * may be different between sensors, here we try to parse + * parameters form sensor child node + */ + r = goodix_parse_customize_params(ts_dev, + ts_dev->board_data, + ts_dev->chip_version.sensor_id); + if (r < 0) + ts_info("Cann't find customized parameters"); + + ts_dev->normal_cfg->delay = 500; + /* send normal-cfg to firmware */ + r = goodix_send_config(ts_dev, ts_dev->normal_cfg); + + return r; +} + +/** + * goodix_hw_reset - reset device + * + * @dev: pointer to touch device + * Returns 0 - succeed,<0 - failed + */ +int goodix_hw_reset(struct goodix_ts_device *dev) +{ + u8 data[2] = {0x00}; + int r = 0; + + ts_info("HW reset"); + + if (dev->ic_type == IC_TYPE_NORMANDY) { + ts_info("normandy reset"); + gpio_direction_output(dev->board_data->reset_gpio, 0); + udelay(GTP_2000_udelay); + gpio_direction_output(dev->board_data->reset_gpio, 1); + msleep(GTP_100_DLY_MS); + } else if (dev->ic_type == IC_TYPE_NANJING) { + ts_info("nanjing reset"); + + /*close watch dog*/ + data[0] = 0; + goodix_i2c_write(dev, 0x40b0, data, 1); + usleep_range(GTP_10000_DLY_US, GTP_11000_DLY_US); + + /*soft reset*/ + data[0] = 1; + goodix_i2c_write_trans_once(dev, 0x4180, data, 1); + msleep(GTP_250_DLY_MS); + goodix_close_hidi2c_mode(dev); + + /*clear coor_reg*/ + data[0] = 0; + data[1] = 0; + goodix_i2c_write(dev, 0x824d, data, 2); + } + + + /*init static esd*/ + data[0] = 0xaa; + if (dev->ic_type == IC_TYPE_NANJING) { + r = goodix_i2c_write(dev, + 0x8043, data, 1); + if (r < 0) + ts_err("nanjing reset, init static esd FAILED, i2c wirte ERROR"); + } + + /*init dynamic esd*/ + if (dev->reg.esd) { + r = goodix_i2c_write_trans(dev, + dev->reg.esd, + data, 1); + if (r < 0) + ts_err("IC reset, init dynamic esd FAILED, i2c write ERROR"); + } else + ts_info("reg.esd is NULL, skip dynamic esd init"); + + return 0; +} + +/** + * goodix_request_handler - handle firmware request + * + * @dev: pointer to touch device + * @request_data: requset information + * Returns 0 - succeed,<0 - failed + */ +static int goodix_request_handler(struct goodix_ts_device *dev, + struct goodix_request_data *request_data) +{ + unsigned char buffer[1]; + int r; + + r = goodix_i2c_read_trans(dev, dev->reg.fw_request, + buffer, 1);/*TS_REG_REQUEST*/ + if (r < 0) + return r; + + switch (buffer[0]) { + case REQUEST_CONFIG: + ts_info("HW request config"); + goodix_send_config(dev, dev->normal_cfg); + goto clear_requ; + /*break;*/ + case REQUEST_BAKREF: + ts_info("HW request bakref"); + goto clear_requ; + /*break;*/ + case REQUEST_RESET: + ts_info("HW requset reset"); + goto clear_requ; + /*break;*/ + case REQUEST_MAINCLK: + ts_info("HW request mainclk"); + goto clear_requ; + /*break;*/ + default: + ts_info("Unknown hw request:%d", buffer[0]); + return 0; + } + +clear_requ: + buffer[0] = 0x00; + r = goodix_i2c_write_trans(dev, dev->reg.fw_request, + buffer, 1);/*TS_REG_REQUEST*/ + return r; +} + +/*goodix_swap_coords - swap coord + */ + +static void goodix_swap_coords(struct goodix_ts_device *dev, + struct goodix_ts_coords *coords, + int touch_num) +{ + int i, temp; + struct goodix_ts_board_data *bdata = dev->board_data; + + for (i = 0; i < touch_num; i++) { + if (bdata->swap_axis) { + temp = coords->x; + coords->x = coords->y; + coords->y = temp; + } + if (bdata->x2x) + coords->x = bdata->panel_max_x - coords->x; + if (bdata->y2y) + coords->y = bdata->panel_max_y - coords->y; + coords++; + } +} + +static int goodix_remap_trace_id(struct goodix_ts_device *dev, + u8 *coor_buf, u32 coor_buf_len, int touch_num) +{ + static u8 remap_array[20] = {0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff}; + int i, j; + int offset = 0; + bool need_leave = false; + bool need_add = false; + u8 temp_buf[BYTES_PER_COORD] = {0x00}; + u8 *small; + bool need_swap = false; + int max_touch_num = dev->board_data->panel_max_id; + + if (touch_num > dev->board_data->panel_max_id) { + ts_err("touch num error, trace id no remap:%d", touch_num); + return 0; + } + + if (!coor_buf || coor_buf_len > (2 + BYTES_PER_COORD * max_touch_num)) { + ts_err("touch data buff error, !coor_buf:%d, len:%d", + !coor_buf, coor_buf_len); + return 0; + } + + /*clear and reset remap_array*/ + if (touch_num == 0) { + for (i = 0; i < sizeof(remap_array); i++) + remap_array[i] = 0xff; + return 0; + } + + /*find and add new point*/ + offset = 0; + for (i = 0; i < touch_num; i++) { + need_add = true; + for (j = 0; j < sizeof(remap_array); j++) { + if (coor_buf[offset] == remap_array[j]) { + need_add = false; + break; + } + } + if (need_add == true) { + for (j = 0; j < sizeof(remap_array); j++) { + if (remap_array[j] == 0xff) { + remap_array[j] = coor_buf[offset]; + break; + } + } + } + offset += BYTES_PER_COORD; + } + + /*scan remap_array, find and remove leave point*/ + for (i = 0; i < sizeof(remap_array); i++) { + if (remap_array[i] == 0xff) + continue; + else { + need_leave = true; + offset = 0; + for (j = 0; j < touch_num; j++) { + if (remap_array[i] == coor_buf[offset]) { + need_leave = false; + break; + } + offset += BYTES_PER_COORD; + } + if (need_leave == true) { + remap_array[i] = 0xff; + } + } + } + + /*remap trace id*/ + offset = 0; + for (i = 0; i < touch_num; i++) { + /*do not remap pen's trace ID*/ + if (coor_buf[offset] >= 0x80) { + offset += BYTES_PER_COORD; + continue; + } else { + for (j = 0; j < sizeof(remap_array); j++) { + if (remap_array[j] == coor_buf[offset]) { + coor_buf[offset] = j; + break; + } + } + if (j >= sizeof(remap_array)) { + ts_err("remap ERROR!!trace id:%d", + coor_buf[offset]); + ts_err("remap_array:%*ph", + (int)sizeof(remap_array), remap_array); + } + offset += BYTES_PER_COORD; + } + } + + /*realign coor data by new trace ID*/ + for (i = 0; i < touch_num - 1; i++) { + small = &coor_buf[BYTES_PER_COORD * i]; + need_swap = false; + for (j = i + 1; j < touch_num; j++) { + if (coor_buf[BYTES_PER_COORD * j] < *small) { + need_swap = true; + small = &coor_buf[BYTES_PER_COORD * j]; + } + } + /*swap*/ + if (need_swap) { + memcpy(temp_buf, small, BYTES_PER_COORD); + memcpy(small, + &coor_buf[BYTES_PER_COORD * i], + BYTES_PER_COORD); + memcpy(&coor_buf[BYTES_PER_COORD * i], + temp_buf, + BYTES_PER_COORD); + } + } + + return 0; +} + +/** + * goodix_event_handler - handle firmware event + * + * @dev: pointer to touch device + * @ts_event: pointer to touch event structure + * Returns 0 - succeed,<0 - failed + */ +static int goodix_touch_handler(struct goodix_ts_device *dev, + struct goodix_ts_event *ts_event, + u8 *pre_buf, u32 pre_buf_len) +{ + struct goodix_touch_data *touch_data = &ts_event->event_data.touch_data; + struct goodix_ts_coords *coords = &(touch_data->coords[0]); + int max_touch_num = dev->board_data->panel_max_id; + unsigned char coord_sta; + int touch_num = 0, i, r; + unsigned char chksum = 0; + + static char *buffer; + + if (!buffer) { + buffer = kzalloc(4 + BYTES_PER_COORD * max_touch_num, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + } + + if (!pre_buf || pre_buf_len != (4 + BYTES_PER_COORD)) { + r = -EINVAL; + return r; + } + + /*copy data to buffer*/ + memcpy(buffer, pre_buf, pre_buf_len); + + /* buffer[1]: touch state */ + coord_sta = buffer[1]; + + touch_num = coord_sta & 0x0F; + + if (unlikely(touch_num > max_touch_num)) { + r = -EINVAL; + goto exit_clean_sta; + } else if (unlikely(touch_num > 1)) { + r = goodix_i2c_read_trans(dev, + dev->reg.coor + 4 + + BYTES_PER_COORD,/*TS_REG_COORDS_BASE*/ + &buffer[4 + BYTES_PER_COORD], + (touch_num - 1) * BYTES_PER_COORD); + if (unlikely(r < 0)) + goto exit_clean_sta; + } + + if (dev->ic_type == IC_TYPE_NANJING) { + chksum = checksum_u8(&buffer[1], + touch_num * BYTES_PER_COORD + 3); + } else { + chksum = checksum_u8(&buffer[0], + touch_num * BYTES_PER_COORD + 4); + } + if (unlikely(chksum != 0)) { + ts_err("Checksum error:%X, ic_type:%d", chksum, dev->ic_type); + r = -EINVAL; + goto exit_clean_sta; + } + + touch_data->have_key = false;/*clear variable*/ + touch_data->key_value = 0;/*clear variable*/ + touch_data->have_key = (coord_sta >> 4) & 0x01; + if (touch_data->have_key) { + touch_data->key_value = buffer[touch_num * BYTES_PER_COORD + 2]; + if (dev->board_data->pen_enable) + touch_data->key_value = (touch_data->key_value & 0x0f) | + ((touch_data->key_value & 0xf0) >> + (4 - dev->board_data->tp_key_num)); + } + + /*add end*/ + + /*remap trace id*/ + if (dev->ic_type == IC_TYPE_NANJING) + goodix_remap_trace_id(dev, &buffer[2], + 2 + BYTES_PER_COORD * max_touch_num, + touch_num); + + /*clear buffer*/ + memset(touch_data->coords, 0x00, sizeof(touch_data->coords)); + memset(touch_data->pen_coords, 0x00, sizeof(touch_data->pen_coords)); + + /*"0 ~ touch_num - 2" is finger, + * "touch_num - 1" may be a finger or a pen + */ + /*process "0 ~ touch_num -2"*/ + for (i = 0; i < touch_num - 1; i++) { + coords->id = buffer[i * BYTES_PER_COORD + 2] & 0x0f; + coords->x = buffer[i * BYTES_PER_COORD + 3] | + (buffer[i * BYTES_PER_COORD + 4] << 8); + coords->y = buffer[i * BYTES_PER_COORD + 5] | + (buffer[i * BYTES_PER_COORD + 6] << 8); + coords->p = buffer[i * BYTES_PER_COORD + 7] | + (buffer[i * BYTES_PER_COORD + 8] << 8); + coords->w = coords->p / 16; + + coords++; + } + + /*process "touch_num - 1", it may be a finger or a pen*/ + /*it's a pen*/ + i = touch_num - 1; + if (touch_num >= 1 && buffer[i * BYTES_PER_COORD + 2] >= 0x80) { + if (dev->board_data->pen_enable) {/*pen_enable*/ + touch_data->pen_down = true; + + touch_data->pen_coords[0].id = + dev->board_data->panel_max_id * 2; + touch_data->pen_coords[0].x = + buffer[i * BYTES_PER_COORD + 3] | + (buffer[i * BYTES_PER_COORD + 4] << 8); + touch_data->pen_coords[0].y = + buffer[i * BYTES_PER_COORD + 5] | + (buffer[i * BYTES_PER_COORD + 6] << 8); + touch_data->pen_coords[0].w = 6; + touch_data->pen_coords[0].p = + buffer[i * BYTES_PER_COORD + 7] | + (buffer[i * BYTES_PER_COORD + 8] << 8); + } + } else {/*it's a finger*/ + coords->id = buffer[i * BYTES_PER_COORD + 2] & 0x0f; + coords->x = buffer[i * BYTES_PER_COORD + 3] | + (buffer[i * BYTES_PER_COORD + 4] << 8); + coords->y = buffer[i * BYTES_PER_COORD + 5] | + (buffer[i * BYTES_PER_COORD + 6] << 8); + coords->p = buffer[i * BYTES_PER_COORD + 7] | + (buffer[i * BYTES_PER_COORD + 8] << 8); + coords->w = coords->p / 16; + + if (touch_data->pen_down == true) { + touch_data->pen_down = false; + } + } + + /*swap coord*/ + goodix_swap_coords(dev, &touch_data->coords[0], touch_num); + goodix_swap_coords(dev, &touch_data->pen_coords[0], 1); + + touch_data->touch_num = touch_num; + /* mark this event as touch event */ + ts_event->event_type = EVENT_TOUCH; + r = 0; + +exit_clean_sta: + /* handshake */ + buffer[0] = 0x00; + goodix_i2c_write_trans(dev, dev->reg.coor, + buffer, 1);/*TS_REG_COORDS_BASE*/ + return r; +} + +static int goodix_event_handler(struct goodix_ts_device *dev, + struct goodix_ts_event *ts_event) +{ + unsigned char pre_buf[4 + BYTES_PER_COORD]; + unsigned char event_sta = 0; + int r; + + memset(pre_buf, 0, sizeof(pre_buf)); + + r = goodix_i2c_read_trans(dev, dev->reg.coor, + pre_buf, 4 + BYTES_PER_COORD); + if (unlikely(r < 0)) + return r; + + /* buffer[0]: event state */ + event_sta = pre_buf[0]; + if (likely((event_sta & 0x80) == 0x80)) { + /*handle touch event*/ + goodix_touch_handler(dev, + ts_event, + pre_buf, + 4 + BYTES_PER_COORD); + } else if (unlikely((event_sta & 0x40) == 0x40)) { + /* handle request event */ + ts_event->event_type = EVENT_REQUEST; + goodix_request_handler(dev, + &ts_event->event_data.request_data); + } else if ((event_sta & 0x20) == 0x20) { + /* handle gesture event */ + dev_dbg(dev->dev, "Gesture event\n"); + } else if ((event_sta & 0x10) == 0x10) { + /* handle hotknot event */ + dev_dbg(dev->dev, "Hotknot event\n"); + } else { + dev_dbg(dev->dev, "unknow event type\n"); + r = -EINVAL; + } + + return r; +} + + +/** + * goodix_hw_suspend - Let touch deivce stay in lowpower mode. + * @dev: pointer to goodix touch device + * @return: 0 - succeed, < 0 - failed + */ +static int goodix_hw_suspend(struct goodix_ts_device *dev) +{ + struct goodix_ts_cmd sleep_cmd; + int r = 0; +#ifdef GTP_CHARGER + struct goodix_ts_device *ts_dev = goodix_modules.core_data->ts_dev; + + //is has, charger off + if ((ts_dev->board_data->charger_detection_enable) && + (USB_DETECT_IN == ts_dev->board_data->charger_detection->usb_connected)) { + goodix_set_charger_bit(USB_DETECT_OUT); + } + + ts_dev->board_data->charger_send_flage_enable = false; +#endif + + goodix_cmds_init(&sleep_cmd, COMMAND_SLEEP, 0, dev->reg.command); + if (sleep_cmd.initialized) { + r = goodix_send_command(dev, &sleep_cmd); + if (!r) + ts_info("Chip in sleep mode"); + } else + ts_err("Uninitialized sleep command"); + + return r; +} + +/** + * goodix_hw_resume - Let touch deivce stay in active mode. + * @dev: pointer to goodix touch device + * @return: 0 - succeed, < 0 - failed + */ +static int goodix_hw_resume(struct goodix_ts_device *dev) +{ + int r = 0, i, retry = GOODIX_BUS_RETRY_TIMES; + u8 temp_buf[256], checksum; + u8 data[2] = {0x00}; +#ifdef GTP_CHARGER + struct goodix_ts_device *ts_dev = goodix_modules.core_data->ts_dev; +#endif + + for (; retry > 0; retry--) { + /*resume IC*/ + if (dev->ic_type == IC_TYPE_NORMANDY) + goodix_hw_reset(dev); + else if (dev->ic_type == IC_TYPE_NANJING) { + /*1. read 0x8000 to resume nanjing*/ + goodix_i2c_read(dev, 0x8000, data, 1); + msleep(GTP_150_DLY_MS); + /*2. check resume success or not*/ + for (i = 0; i < 10; i++) { + r = goodix_i2c_read(dev, dev->reg.command, + data, 1); + if (!r && data[0] == 0xff) + break; + msleep(GTP_20_DLY_MS); + } + + if (i >= 10) { + ts_err("resume nanjing failed, after read 0x8000, 0x8040 not equal 0xff"); + continue; + } + /*3. close hid i2c*/ + goodix_close_hidi2c_mode(dev); + + /*4. clear coor*/ + data[0] = 0; + data[1] = 0; + goodix_i2c_write(dev, 0x824d, data, 2); + } +#ifdef GTP_CHARGER + ts_dev->board_data->charger_send_flage_enable = true; + //if has, charger on + if ((ts_dev->board_data->charger_detection_enable) && + (USB_DETECT_IN == ts_dev->board_data->charger_detection->usb_connected)) { + goodix_set_charger_bit(USB_DETECT_IN); + } +#endif + /*read version and check checksum*/ + if (dev->reg.version_base && dev->reg.version_len < 256) { + r = goodix_i2c_read(dev, dev->reg.version_base, + temp_buf, dev->reg.version_len); + if (r < 0) + continue; + + checksum = checksum_u8(temp_buf, dev->reg.version_len); + if (!checksum) { + ts_info("read version SUCCESS"); + break; + } + } + } + return r; +} + +static int goodix_esd_check(struct goodix_ts_device *dev) +{ + int r; + u8 data = 0; + + if (dev->reg.esd == 0) { + ts_err("esd reg is NULL"); + return 0; + } + + /*check dynamic esd*/ + if (dev->ic_type == IC_TYPE_NORMANDY) + r = dev->hw_ops->read_trans(dev, + 0x3103, &data, 1); + else + r = dev->hw_ops->read_trans(dev, + dev->reg.esd, &data, 1); + + if (r < 0 || (data == 0xaa)) { + ts_info("dynamic esd occur, r:%d, data:0x%02x", r, data); + r = -EINVAL; + goto exit; + } + + /*check static esd*/ + if (dev->ic_type == IC_TYPE_NANJING) { + r = dev->hw_ops->read_trans(dev, + 0x8043, &data, 1); + + if (r < 0 || (data != 0xaa)) { + ts_info("static esd occur, r:%d, data:0x%02x", r, data); + r = -EINVAL; + goto exit; + } + } + +exit: + return r; +} + +/* hardware opeation funstions */ +static const struct goodix_ts_hw_ops hw_i2c_ops = { + .init = goodix_hw_init, + .read = goodix_i2c_read, + .write = goodix_i2c_write, + .read_trans = goodix_i2c_read_trans, + .write_trans = goodix_i2c_write_trans, + .reset = goodix_hw_reset, + .event_handler = goodix_event_handler, + .send_config = goodix_send_config, + .read_config = goodix_read_config, + .send_cmd = goodix_send_command, + .read_version = goodix_read_version, + .suspend = goodix_hw_suspend, + .resume = goodix_hw_resume, + .check_hw = goodix_esd_check, +}; + +static struct platform_device *goodix_pdev; + +static void goodix_pdev_release(struct device *dev) +{ + kfree(goodix_pdev); + /*return;*/ +} + +static int goodix_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct goodix_ts_device *ts_device = NULL; + struct goodix_ts_board_data *ts_bdata = NULL; + int r = 0; + + ts_info("goodix_i2c_probe IN"); + + r = i2c_check_functionality(client->adapter, + I2C_FUNC_I2C); + if (!r) + return -EIO; + + /* board data */ + ts_bdata = devm_kzalloc(&client->dev, + sizeof(struct goodix_ts_board_data), GFP_KERNEL); + if (!ts_bdata) + return -ENOMEM; + + if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) { + /* parse devicetree property */ + r = goodix_parse_dt(client->dev.of_node, ts_bdata); + if (r < 0) + return r; + } +#ifdef CONFIG_ACPI + else if (ACPI_COMPANION(&client->dev)) { + r = goodix_parse_acpi(&client->dev, ts_bdata); + if (r < 0) + return r; + } +#endif + else { + /* use platform data */ + devm_kfree(&client->dev, ts_bdata); + ts_bdata = client->dev.platform_data; + } + + if (!ts_bdata) + return -ENODEV; + + ts_device = devm_kzalloc(&client->dev, + sizeof(struct goodix_ts_device), GFP_KERNEL); + if (!ts_device) + return -ENOMEM; + + + ts_device->name = "gtx5"; + ts_device->dev = &client->dev; + ts_device->board_data = ts_bdata; + ts_device->hw_ops = &hw_i2c_ops; + + /* 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 could find this platform dev in + * /sys/devices/platfrom/goodix_ts.0 + * goodix_pdev->dev.parent = &client->dev; + */ + goodix_pdev->dev.platform_data = ts_device; + goodix_pdev->dev.release = goodix_pdev_release; + + /* register platform device, then the goodix_ts_core + * module will probe the touch deivce. + */ + r = platform_device_register(goodix_pdev); + + ts_info("goodix_i2c_probe OUT"); + + return r; +} + +static int goodix_i2c_remove(struct i2c_client *client) +{ + platform_device_unregister(goodix_pdev); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id i2c_matchs[] = { + {.compatible = TS_DT_COMPATIBLE,}, + {}, +}; +MODULE_DEVICE_TABLE(of, i2c_matchs); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id acpi_matchs[] = { + {.id = "PNPxxx"}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, acpi_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), +#ifdef CONFIG_ACPI + .acpi_match_table = acpi_matchs, +#endif + }, + .probe = goodix_i2c_probe, + .remove = goodix_i2c_remove, + .id_table = i2c_id_table, +}; + +int __init goodix_i2c_init(void) +{ + ts_info("GTx5xx HW layer init V1.3.0.1"); + return i2c_add_driver(&goodix_i2c_driver); +} + +void __exit goodix_i2c_exit(void) +{ + i2c_del_driver(&goodix_i2c_driver); + /*return;*/ +} diff --git a/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_ts_test.c b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_ts_test.c new file mode 100644 index 000000000000..d51ba1c00ece --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_ts_test.c @@ -0,0 +1,543 @@ +#include "goodix_ts_core.h" +#ifdef GTP_CHARGER +#include + +#define MOTO_CHARGER_FLAGE_IN 0x06 +#define MOTO_CHARGER_FLAGE_OUT 0x07 +#endif + +#define MOTO_SENSOR_NUM_ADDR 0x5473 +#define MOTO_DRIVER_GROUP_A_NUM_ADDR 0x5477 +#define MOTO_DRIVER_GROUP_B_NUM_ADDR 0x5478 +#define MOTO_READ_COOR_ADDR 0x4100 +#define MOTO_RAW_DATA_BASE_ADDR 0x8FA0 +#define MOTO_DIFF_DATA_BASE_ADDR 0x9D20 +#define MOTO_REF_DATA_BASE_ADDR 0xA980 +#define MOTO_CMD_ADDR 0x6F68 + +static void goodix_cmds_init(struct goodix_ts_cmd *ts_cmd, + u8 cmds, u8 cmd_data, u32 reg_addr) +{ + if (reg_addr) { + ts_cmd->cmd_reg = reg_addr; + ts_cmd->length = 3; + ts_cmd->cmds[0] = cmds; + ts_cmd->cmds[1] = cmd_data; + ts_cmd->cmds[2] = 0 - cmds - cmd_data; + ts_cmd->initialized = true; + } else { + ts_cmd->initialized = false; + } +} + +int goodix_get_channel_num(u32 *sen_num, u32 *drv_num) +{ + int ret = -1; + u8 buf[2] = {0}; + struct goodix_ts_device *ts_dev = goodix_modules.core_data->ts_dev; + + ret = goodix_i2c_read(ts_dev, MOTO_SENSOR_NUM_ADDR, buf, 1); + if (ret) { + ts_err("Read sen_num fail."); + return ret; + } + + *sen_num = buf[0]; + + ret = goodix_i2c_read(ts_dev, MOTO_DRIVER_GROUP_A_NUM_ADDR, buf, 2); + if (ret) { + ts_err("Read drv_num fail."); + return ret; + } + + *drv_num = buf[0] + buf[1]; + ts_info("sen_num : %d, drv_num : %d", *sen_num, *drv_num); + + return 0; +} + +static int goodix_be_normal(void) +{ + int ret = -1; + struct goodix_ts_cmd normal_cmd; + struct goodix_ts_device *ts_dev = goodix_modules.core_data->ts_dev; + + goodix_cmds_init(&normal_cmd, 0x00, 0, ts_dev->reg.command); + ret = ts_dev->hw_ops->send_cmd(ts_dev, &normal_cmd); + if (ret) + ts_err("Send enter normal mode command fail."); + + return ret; +} + +int goodix_get_rawordiff_data(int which, int *data) +{ + int i, wait_cnt, ret = 0; + u32 sen_num = 0, drv_num = 0; + u8 *temp_data = NULL; + s16 *result_data = NULL; + int data_size = 0, data_len = 0; + u8 buf[2] = {0}; + struct goodix_ts_cmd cmd; + struct goodix_ts_device *ts_dev = goodix_modules.core_data->ts_dev; + + if ((which != 0) && (which != 1) && (which != 2) && + (which != 3) && (which != 4) && (which != 5)) { + ts_err("Invalid get rawdiff data params."); + return -EINVAL; + } + + ret = goodix_get_channel_num(&sen_num, &drv_num); + if (ret) { + ts_err("Get channel num fail."); + return -EINVAL; + } + + if (which < 3) + data_size = sen_num * drv_num; + else + data_size = sen_num + drv_num; + + data_len = data_size * sizeof(u16); + if (data == NULL) { + ts_err("User data format error."); + return -EINVAL; + } + + temp_data = kzalloc(data_len, GFP_KERNEL); + if (!temp_data) + return -EINVAL; + + result_data = kzalloc(data_len, GFP_KERNEL); + if (!result_data) { + ret = -EINVAL; + goto free_data; + } + + goodix_cmds_init(&cmd, 0x01, 0, ts_dev->reg.command); + + ret = ts_dev->hw_ops->send_cmd(ts_dev, &cmd); + if (ret) { + ts_err("Send enter rawdiff mode fail."); + ret = -EINVAL; + goto free_data; + } + + usleep_range(18000, 18100); + + /*goodix_ts_irq_enable(goodix_modules.core_data, false);*/ + ret = goodix_i2c_write(ts_dev, MOTO_READ_COOR_ADDR, buf, 2); + if (ret) { + ts_err("Set zero to interrupt status reg fail."); + ret = -EINVAL; + goto be_normal; + } + + for (wait_cnt = 0; wait_cnt < 3; wait_cnt++) { + msleep(150); + ret = goodix_i2c_read(ts_dev, MOTO_READ_COOR_ADDR, buf, 2); + if ((!ret) && (buf[0] & 0x80)) + break; + } + /*goodix_ts_irq_enable(goodix_modules.core_data, true);*/ + + if (wait_cnt < 3) { + switch (which) { + case 0: + ret = goodix_i2c_read(ts_dev, MOTO_RAW_DATA_BASE_ADDR, + temp_data, data_len); + if (ret) { + ts_err("Read rawdata fail."); + ret = -EINVAL; + goto be_normal; + } + break; + case 1: + ret = goodix_i2c_read(ts_dev, MOTO_DIFF_DATA_BASE_ADDR, + temp_data, data_len); + if (ret) { + ts_err("Read diffdata fail."); + ret = -EINVAL; + goto be_normal; + } + break; + case 2: + ret = goodix_i2c_read(ts_dev, MOTO_REF_DATA_BASE_ADDR, + temp_data, data_len); + if (ret) { + ts_err("Read ref data fail."); + ret = -EINVAL; + goto be_normal; + } + break; + case 3: + ret = goodix_i2c_read(ts_dev, 0x4c0c, + temp_data, data_len); + if (ret) { + ts_err("Read ref data fail."); + ret = -EINVAL; + goto be_normal; + } + break; + case 4: + ret = goodix_i2c_read(ts_dev, 0x4ca4, + temp_data, data_len); + if (ret) { + ts_err("Read ref data fail."); + ret = -EINVAL; + goto be_normal; + } + break; + case 5: + ret = goodix_i2c_read(ts_dev, 0x4b74, + temp_data, data_len); + if (ret) { + ts_err("Read ref data fail."); + ret = -EINVAL; + goto be_normal; + } + break; + } + + for (i = 0; i < data_size; i++) { + result_data[i] = (temp_data[2 * i] << 8) | + temp_data[2 * i + 1]; + data[i] = result_data[i]; + } + + buf[0] = buf[1] = 0x00; + ret = goodix_i2c_write(ts_dev, MOTO_READ_COOR_ADDR, buf, 2); + if (ret) { + ts_err("Set zero to interrupt status reg fail."); + ret = -EINVAL; + goto be_normal; + } + } else { + ts_err("Wait for data ready timeout."); + ret = -EINVAL; + goto be_normal; + } + +be_normal: + + goodix_be_normal(); + +free_data: + + kfree(temp_data); + temp_data = NULL; + kfree(result_data); + result_data = NULL; + + return ret; +} + +int goodix_nodereg_read(void) +{ + char tm_buf[50]; + char char_buf[100]; + int ret = 0, i, j, offset = 0; + struct goodix_ts_device *ts_dev = goodix_modules.core_data->ts_dev; + + ret |= goodix_i2c_read(ts_dev, 0x2031, tm_buf, 1); + ts_info("0x2031 = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x20b0, tm_buf, 1); + ts_info("0x20b0 = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x20C0, tm_buf, 2); + ts_info("0x20C0 = 0x%02x%02x", tm_buf[0], tm_buf[1]); + ret |= goodix_i2c_read(ts_dev, 0x21e4, tm_buf, 1); + ts_info("0x21e4 = 0x%02x", tm_buf[0]); + + offset = 0; + for (i = 0; i < 10; i++) { + ret |= goodix_i2c_read(ts_dev, 0x2244, tm_buf, 2); + + offset += snprintf(&char_buf[offset], PAGE_SIZE, + "0x%02x", tm_buf[0]); + offset += snprintf(&char_buf[offset], PAGE_SIZE, + "%02x ", tm_buf[1]); + } + char_buf[offset] = 0; + ts_info("0x2244 = %s", char_buf); + + offset = 0; + for (i = 0; i < 10; i++) { + ret |= goodix_i2c_read(ts_dev, 0x2244, tm_buf, 2); + + offset += snprintf(&char_buf[offset], PAGE_SIZE, + "0x%02x", tm_buf[0]); + offset += snprintf(&char_buf[offset], PAGE_SIZE, + "%02x ", tm_buf[1]); + } + char_buf[offset] = 0; + ts_info("0x2244 = %s", char_buf); + + ret |= goodix_i2c_read(ts_dev, 0x225f, tm_buf, 1); + ts_info("0x225f = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x2276, tm_buf, 1); + ts_info("0x2276 = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x2260, tm_buf, 2); + ts_info("0x2260 = 0x%02x%02x", tm_buf[0], tm_buf[1]); + ret |= goodix_i2c_read(ts_dev, 0x2264, tm_buf, 1); + ts_info("0x2264 = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x2266, tm_buf, 1); + ts_info("0x2266 = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x2268, tm_buf, 1); + ts_info("0x2268 = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x2269, tm_buf, 1); + ts_info("0x2269 = 0x%02x", tm_buf[0]); + + ret |= goodix_i2c_read(ts_dev, 0x228e, tm_buf, 2); + ts_info("0x228e = 0x%02x%02x", tm_buf[0], tm_buf[1]); + + for (i = 0; i < 5; i++) { + ret |= goodix_i2c_read(ts_dev, 0x4040, tm_buf, 1); + ts_info("0x4040 = 0x%02x", tm_buf[0]); + } + for (i = 0; i < 5; i++) { + ret |= goodix_i2c_read(ts_dev, 0x4041, tm_buf, 1); + ts_info("0x4041 = 0x%02x", tm_buf[0]); + } + + for (j = 0; j < 10; j++) { + ret |= goodix_i2c_read(ts_dev, 0x4100, tm_buf, 20); + offset = 0; + for (i = 0; i < 20; i++) { + offset += snprintf(&char_buf[offset], PAGE_SIZE, + "%02x ", tm_buf[i]); + } + char_buf[offset] = 0; + ts_info("0x4100 = %s", char_buf); + } + + ret |= goodix_i2c_read(ts_dev, 0x4535, tm_buf, 8); + offset = 0; + for (i = 0; i < 8; i++) { + offset += snprintf(&char_buf[offset], PAGE_SIZE, + "%02x ", tm_buf[i]); + } + char_buf[offset] = 0; + ts_info("0x4535 = %s", char_buf); + + ret |= goodix_i2c_read(ts_dev, 0x453d, tm_buf, 4); + offset = 0; + for (i = 0; i < 4; i++) { + offset += snprintf(&char_buf[offset], PAGE_SIZE, + "%02x ", tm_buf[i]); + } + char_buf[offset] = 0; + ts_info("0x453d = %s", char_buf); + + ret |= goodix_i2c_read(ts_dev, 0x4541, tm_buf, 1); + ts_info("0x4541 = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x5186, tm_buf, 1); + ts_info("0x5186 = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x518b, tm_buf, 1); + ts_info("0x518b = 0x%02x", tm_buf[0]); + + for (i = 0; i < 5; i++) { + ret |= goodix_i2c_read(ts_dev, 0x5193, tm_buf, 2); + ts_info("0x5193 = 0x%02x%02x", tm_buf[0], tm_buf[1]); + } + for (i = 0; i < 5; i++) { + ret |= goodix_i2c_read(ts_dev, 0x5195, tm_buf, 1); + ts_info("0x5195 = 0x%02x", tm_buf[0]); + } + + ret |= goodix_i2c_read(ts_dev, 0x52cc, tm_buf, 2); + ts_info("0x52cc = 0x%02x%02x", tm_buf[0], tm_buf[1]); + ret |= goodix_i2c_read(ts_dev, 0x52ce, tm_buf, 2); + ts_info("0x52ce = 0x%02x02%x", tm_buf[0], tm_buf[1]); + + ret |= goodix_i2c_read(ts_dev, 0x52de, tm_buf, 8); + offset = 0; + for (i = 0; i < 8; i++) { + offset += snprintf(&char_buf[offset], PAGE_SIZE, + "%02x ", tm_buf[i]); + } + char_buf[offset] = 0; + ts_info("0x52de = %s", char_buf); + + for (i = 0; i < 5; i++) { + ret |= goodix_i2c_read(ts_dev, 0x5436, tm_buf, 2); + ts_info("0x5436 = 0x%02x%02x", tm_buf[0], tm_buf[1]); + } + + ret |= goodix_i2c_read(ts_dev, 0x5639, tm_buf, 2); + ts_info("0x5639 = 0x%02x%02x", tm_buf[0], tm_buf[1]); + ret |= goodix_i2c_read(ts_dev, 0x563b, tm_buf, 1); + ts_info("0x563b = 0x%02x", tm_buf[0]); + + if (ts_dev->board_data->debug_reg_ver == 1) { + for (i = 0; i < 5; i++) { + ret |= goodix_i2c_read(ts_dev, 0x59dd, tm_buf, 2); + ts_info("0x59dd = 0x%02x%02x", tm_buf[0], tm_buf[1]); + } + + ret |= goodix_i2c_read(ts_dev, 0x59e1, tm_buf, 2); + ts_info("0x59e1 = 0x%02x%02x", tm_buf[0], tm_buf[1]); + ret |= goodix_i2c_read(ts_dev, 0x59e7, tm_buf, 2); + ts_info("0x59e7 = 0x%02x%02x", tm_buf[0], tm_buf[1]); + ret |= goodix_i2c_read(ts_dev, 0x59e9, tm_buf, 1); + ts_info("0x59e9 = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x59ea, tm_buf, 1); + ts_info("0x59ea = 0x%02x", tm_buf[0]); + + ret |= goodix_i2c_read(ts_dev, 0x5a72, tm_buf, 12); + offset = 0; + for (i = 0; i < 12; i++) { + offset += snprintf(&char_buf[offset], PAGE_SIZE, + "%02x ", tm_buf[i]); + } + char_buf[offset] = 0; + ts_info("0x5a72 = %s", char_buf); + + ret |= goodix_i2c_read(ts_dev, 0x5b57, tm_buf, 1); + ts_info("0x5b57 = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x5bc4, tm_buf, 1); + ts_info("0x5bc4 = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x5c85, tm_buf, 1); + ts_info("0x5c85 = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x5c99, tm_buf, 1); + ts_info("0x5c99 = 0x%02x", tm_buf[0]); + + ret |= goodix_i2c_read(ts_dev, 0x5e22, tm_buf, 9); + offset = 0; + for (i = 0; i < 9; i++) { + offset += snprintf(&char_buf[offset], PAGE_SIZE, + "%02x ", tm_buf[i]); + } + char_buf[offset] = 0; + ts_info("0x5e22 = %s", char_buf); + + ret |= goodix_i2c_read(ts_dev, 0x5ef9, tm_buf, 1); + ts_info("0x5ef9 = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x5f2c, tm_buf, 2); + ts_info("0x5f2c = 0x%02x%02x", tm_buf[0], tm_buf[1]); + ret |= goodix_i2c_read(ts_dev, 0x5f4b, tm_buf, 1); + ts_info("0x5f4b = 0x%02x", tm_buf[0]); + } else { + for (i = 0; i < 5; i++) { + ret |= goodix_i2c_read(ts_dev, 0x59d5, tm_buf, 2); + ts_info("0x59d5 = 0x%02x%02x", tm_buf[0], tm_buf[1]); + } + + ret |= goodix_i2c_read(ts_dev, 0x59dd, tm_buf, 2); + ts_info("0x59dd = 0x%02x%02x", tm_buf[0], tm_buf[1]); + ret |= goodix_i2c_read(ts_dev, 0x59df, tm_buf, 2); + ts_info("0x59df = 0x%02x%02x", tm_buf[0], tm_buf[1]); + ret |= goodix_i2c_read(ts_dev, 0x59e1, tm_buf, 1); + ts_info("0x59e1 = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x59e2, tm_buf, 1); + ts_info("0x59e2 = 0x%02x", tm_buf[0]); + + ret |= goodix_i2c_read(ts_dev, 0x5a6a, tm_buf, 12); + offset = 0; + for (i = 0; i < 12; i++) { + offset += snprintf(&char_buf[offset], PAGE_SIZE, + "%02x ", tm_buf[i]); + } + char_buf[offset] = 0; + ts_info("0x5a6a = %s", char_buf); + + ret |= goodix_i2c_read(ts_dev, 0x5b4e, tm_buf, 1); + ts_info("0x5b4e = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x5bbc, tm_buf, 1); + ts_info("0x5bbc = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x5c7c, tm_buf, 1); + ts_info("0x5c7c = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x5c90, tm_buf, 1); + ts_info("0x5c90 = 0x%02x", tm_buf[0]); + + ret |= goodix_i2c_read(ts_dev, 0x5e19, tm_buf, 9); + offset = 0; + for (i = 0; i < 9; i++) { + offset += snprintf(&char_buf[offset], PAGE_SIZE, + "%02x ", tm_buf[i]); + } + char_buf[offset] = 0; + ts_info("0x5e19 = %s", char_buf); + + ret |= goodix_i2c_read(ts_dev, 0x5ef0, tm_buf, 1); + ts_info("0x5ef0 = 0x%02x", tm_buf[0]); + ret |= goodix_i2c_read(ts_dev, 0x5f23, tm_buf, 2); + ts_info("0x5f23 = 0x%02x%02x", tm_buf[0], tm_buf[1]); + ret |= goodix_i2c_read(ts_dev, 0x5f42, tm_buf, 1); + ts_info("0x5f42 = 0x%02x", tm_buf[0]); + } + + ret |= goodix_i2c_read(ts_dev, 0x6ea0, tm_buf, 12); + offset = 0; + for (i = 0; i < 12; i++) { + offset += snprintf(&char_buf[offset], PAGE_SIZE, + "%02x ", tm_buf[i]); + } + char_buf[offset] = 0; + ts_info("0x6ea0 = %s", char_buf); + + ret |= goodix_i2c_read(ts_dev, 0x6f6f, tm_buf, 1); + ts_info("0x6f6f = 0x%02x", tm_buf[0]); + + for (i = 0; i < 2; i++) { + ret |= goodix_i2c_read(ts_dev, 0xbed0, tm_buf, 1); + ts_info("0xbed0 = 0x%02x", tm_buf[0]); + } + + if (ret) { + ts_err("Read reg fail."); + return ret; + } + + return ret; +} + +#ifdef GTP_CHARGER +int goodix_set_charger_bit(int state) +{ + int ret = 0; + int count = 3; + u8 read_val = 0; + struct goodix_ts_cmd charger_cmd; + struct goodix_ts_device *ts_dev = goodix_modules.core_data->ts_dev; + + ts_info("send cmd charger ! %d", state); + if (ts_dev == NULL) { + ts_err("i2c no register"); + return -EPERM; + } + if (!ts_dev->board_data->charger_send_flage_enable) { + ts_err("charger send flage disable"); + return -EPERM; + } + + if (state == 1) { + goodix_cmds_init(&charger_cmd, MOTO_CHARGER_FLAGE_IN, 0, + ts_dev->reg.command); + } else if (state == 0) { + goodix_cmds_init(&charger_cmd, MOTO_CHARGER_FLAGE_OUT, 0, + ts_dev->reg.command); + } else { + ts_err("Invalid set charger bit params"); + return -EPERM; + } + + while (count--) { + ret = ts_dev->hw_ops->send_cmd(ts_dev, &charger_cmd); + if (ret) { + ts_err("Send charger command error"); + ret = -1; + } + + usleep_range(GTP_10000_DLY_US, GTP_11000_DLY_US); + if (goodix_i2c_read(ts_dev, MOTO_CMD_ADDR, &read_val, 1)) + ts_err("Read cmd reg fail."); + else + ts_info("Read cmd reg value:%02x.", read_val); + + if (read_val == 0xff) + break; + } + + return ret; +} +#endif diff --git a/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_ts_tools.c b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_ts_tools.c new file mode 100644 index 000000000000..430af33bcb52 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_mmi/goodix_ts_tools.c @@ -0,0 +1,603 @@ +/* + * Goodix GTX5 tools Dirver + * + * Copyright (C) 2015 - 2016 Goodix, Inc. + * Authors: Wang Yafei + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "goodix_ts_core.h" + +#define GOODIX_TOOLS_NAME "gtp_tools" +#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_DRV_VERSION (_IOR(GOODIX_TS_IOC_MAGIC, 9, u8) & NEGLECT_SIZE_MASK) + +#define GOODIX_TS_IOC_MAXNR 10 + +#define IRQ_FALG (0x01 << 2) + +#define I2C_MSG_HEAD_LEN 20 +#define TS_REG_COORDS_BASE 0x4100 + +/* + * struct goodix_tools_data - goodix tools data message used in sync read + * @data: The buffer into which data is written + * @reg_addr: Slave device register start address to start read data + * @length: Number of data bytes in @data being read from slave device + * @filled: When buffer @data be filled will set this flag with 1, outhrwise 0 + * @list_head:Eonnet every goodix_tools_data struct into a list + */ + +struct goodix_tools_data { + u32 reg_addr; + u32 length; + u8 *data; + bool filled; + struct list_head list; +}; + + +/* + * 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 + */ +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 t_count; + struct goodix_ext_module module; +} *goodix_tools_dev; + + +/* read data from i2c asynchronous, + * success return bytes read, else 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]; + struct goodix_ts_device *ts_dev = dev->ts_core->ts_dev; + const struct goodix_ts_hw_ops *hw_ops = ts_dev->hw_ops; + + ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN); + if (ret) { + ret = -EFAULT; + goto err_out; + } + 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); + + databuf = kzalloc(length, GFP_KERNEL); + if (!databuf) { + return -ENOMEM; + } + + if (!hw_ops->read_trans(ts_dev, reg_addr, databuf, length)) { + if (copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, + databuf, length)) { + ret = -EFAULT; + ts_err("Copy_to_user failed"); + } else { + ret = length; + } + } else { + ret = -EBUSY; + ts_err("Read i2c failed"); + } +err_out: + kfree(databuf); + return ret; +} + +/* if success return config data length */ +static int read_config_data(struct goodix_ts_device *ts_dev, 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); + tmp_buf = kzalloc(length, GFP_KERNEL); + if (!tmp_buf) { + ts_err("failed alloc memory"); + return -ENOMEM; + } + if (!reg_addr) { + if (ts_dev->hw_ops->read_config) + ret = ts_dev->hw_ops->read_config(ts_dev, tmp_buf, 0); + else + ret = -EINVAL; + } else { + ret = ts_dev->hw_ops->read_trans(ts_dev, 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; +} + +/* read data from i2c synchronous, + * success return bytes read, else return <= 0 + */ +static int sync_read(struct goodix_tools_dev *dev, void __user *arg) +{ + int ret = 0; + u8 i2c_msg_head[I2C_MSG_HEAD_LEN]; + struct goodix_tools_data tools_data; + + ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN); + if (ret) { + ts_err("Copy data from user failed"); + return -EFAULT; + } + tools_data.reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8) + + (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24); + tools_data.length = i2c_msg_head[4] + (i2c_msg_head[5] << 8) + + (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24); + tools_data.filled = 0; + + tools_data.data = kzalloc(tools_data.length, GFP_KERNEL); + if (!tools_data.data) { + return -ENOMEM; + } + + mutex_lock(&dev->mutex); + list_add_tail(&tools_data.list, &dev->head); + mutex_unlock(&dev->mutex); + /* wait queue will timeout after 1 seconds */ + wait_event_interruptible_timeout(dev->wq, + tools_data.filled == 1, HZ * 3); + + mutex_lock(&dev->mutex); + list_del(&tools_data.list); + mutex_unlock(&dev->mutex); + if (tools_data.filled == 1) { + if (copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, tools_data.data, + tools_data.length)) { + ret = -EFAULT; + ts_err("Copy_to_user failed"); + } else { + ret = tools_data.length; + } + } else { + ret = -EAGAIN; + ts_err("Wait queue timeout"); + } + + kfree(tools_data.data); + 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_device *ts_dev = dev->ts_core->ts_dev; + const struct goodix_ts_hw_ops *hw_ops = ts_dev->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); + + databuf = kzalloc(length, GFP_KERNEL); + if (!databuf) { + 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_trans(ts_dev, 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_ts_config *cfg, void __user *arg) +{ + int ret = 0; + u32 reg_addr, length; + u8 i2c_msg_head[I2C_MSG_HEAD_LEN]; + + cfg->initialized = 0; + mutex_init(&cfg->lock); + 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); + + ret = copy_from_user(cfg->data, (u8 *)arg + I2C_MSG_HEAD_LEN, length); + if (ret) { + ret = -EFAULT; + ts_err("Copy data from user failed"); + goto err_out; + } + cfg->reg_base = reg_addr; + cfg->length = length; + strlcpy(cfg->name, "tools-send-cfg", sizeof(cfg->name)); + cfg->delay = 50; + cfg->initialized = true; + return 0; + +err_out: + return ret; +} +/** + * 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_device *ts_dev; + const struct goodix_ts_hw_ops *hw_ops; + struct goodix_ts_cmd temp_cmd; + struct goodix_ts_config *temp_cfg = NULL; + + if (dev->ts_core == NULL) { + ts_err("Tools module not register"); + return -EINVAL; + } + ts_dev = dev->ts_core->ts_dev; + hw_ops = ts_dev->hw_ops; + + if (_IOC_TYPE(cmd) != GOODIX_TS_IOC_MAGIC) { + ts_err("Bad magic num:%c", _IOC_TYPE(cmd)); + return -ENOTTY; + } + if (_IOC_NR(cmd) > GOODIX_TS_IOC_MAXNR) { + ts_err("Bad cmd num:%d > %d", + _IOC_NR(cmd), GOODIX_TS_IOC_MAXNR); + return -ENOTTY; + } + + switch (cmd & NEGLECT_SIZE_MASK) { + case GTP_IRQ_ENABLE: + if (arg == 1) { + goodix_ts_irq_enable(dev->ts_core, true); + mutex_lock(&dev->mutex); + dev->ops_mode |= IRQ_FALG; + mutex_unlock(&dev->mutex); + ts_info("IRQ enabled"); + } else if (arg == 0) { + goodix_ts_irq_enable(dev->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_dev); + break; + case GTP_SEND_COMMAND: + ret = copy_from_user(&temp_cmd, (void __user *)arg, + sizeof(struct goodix_ts_cmd)); + if (ret) { + ret = -EINVAL; + goto err_out; + } + + ret = hw_ops->send_cmd(ts_dev, &temp_cmd); + if (ret) { + ts_err("Send command failed"); + ret = -EAGAIN; + } + break; + case GTP_SEND_CONFIG: + temp_cfg = kzalloc(sizeof(struct goodix_ts_config), GFP_KERNEL); + if (temp_cfg == NULL) { + 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_dev, temp_cfg); + if (ret) { + ts_err("Failed send config"); + ret = -EAGAIN; + } else { + ts_info("Send config success"); + ret = 0; + } + } + break; + case GTP_READ_CONFIG: + ret = read_config_data(ts_dev, (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: + if (filp->f_flags & O_NONBLOCK) { + ts_err("Goodix tools now worked in sync_bus mode"); + ret = -EAGAIN; + goto err_out; + } + ret = sync_read(dev, (void __user *)arg); + if (ret < 0) + ts_err("Sync data read failed"); + break; + case GTP_ASYNC_WRITE: + ret = async_write(dev, (void __user *)arg); + if (ret < 0) + ts_err("Async data write failed"); + break; + case GTP_DRV_VERSION: + ret = copy_to_user((u8 *)arg, GOODIX_DRIVER_VERSION, + sizeof(GOODIX_DRIVER_VERSION)); + if (ret) + ts_err("failed copy driver version info to user"); + break; + default: + ts_info("Invalid cmd"); + ret = -ENOTTY; + break; + } + +err_out: + if (!temp_cfg) { + kfree(temp_cfg); + temp_cfg = NULL; + } + 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) +{ + filp->private_data = goodix_tools_dev; + ts_info("tools open"); + + return 0; +} + +static int goodix_tools_release(struct inode *inode, struct file *filp) +{ + ts_info("tools closed"); + + return 0; +} + +/** + * goodix_tools_module_irq - goodix tools Irq handle + * This functions is excuted when interrupt happended + * + * @core_data: pointer to touch core data + * @module: pointer to goodix_ext_module struct + * return: EVT_CONTINUE let other module handle this irq + */ +static int goodix_tools_module_irq(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + struct goodix_tools_dev *dev = module->priv_data; + struct goodix_ts_device *ts_dev = dev->ts_core->ts_dev; + const struct goodix_ts_hw_ops *hw_ops = ts_dev->hw_ops; + struct goodix_tools_data *tools_data; + int r = 0; + u8 evt_sta = 0; + + if (!list_empty(&dev->head)) { + r = hw_ops->read_trans(ts_dev, + ts_dev->reg.coor/* TS_REG_COORDS_BASE*/, + &evt_sta, 1); + if (r < 0 || ((evt_sta & 0x80) == 0)) { + ts_err("data not ready:0x%x, read ret =%d", evt_sta, r); + return EVT_CONTINUE; + } + + mutex_lock(&dev->mutex); + list_for_each_entry(tools_data, &dev->head, list) { + if (!hw_ops->read_trans(ts_dev, tools_data->reg_addr, + tools_data->data, tools_data->length)) { + tools_data->filled = 1; + } + } + mutex_unlock(&dev->mutex); + wake_up(&dev->wq); + } + return EVT_CONTINUE; +} + +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 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 = { + .irq_event = goodix_tools_module_irq, + .init = goodix_tools_module_init, +}; + +/** + * goodix_tools_init - init goodix tools device and register a miscdevice + * + * return: 0 success, else failed + */ +int __init goodix_tools_init(void) +{ + int ret; + + goodix_tools_dev = kzalloc(sizeof(struct goodix_tools_dev), GFP_KERNEL); + if (goodix_tools_dev == NULL) { + 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->t_count, 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"); + + ret = goodix_register_ext_module(&goodix_tools_dev->module); + if (ret) + ts_err("failed register to core module"); + + return ret; +} + +void __exit goodix_tools_exit(void) +{ + misc_deregister(&goodix_tools_miscdev); + kfree(goodix_tools_dev); + ts_info("Goodix tools miscdev exit"); +} diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/Android.mk b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/Android.mk new file mode 100644 index 000000000000..0a5d7b52afd2 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/Android.mk @@ -0,0 +1,26 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_MMI=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := goodix_v1430_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := goodix_v1430_update_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := goodix_v1430_ts_tools_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/Kbuild b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/Kbuild new file mode 100644 index 000000000000..81b91826c56d --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/Kbuild @@ -0,0 +1,19 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += \ + -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/goodix_gtx8_v1430_mmi + +obj-m := goodix_v1430_mmi.o +goodix_v1430_mmi-objs = goodix_ts_i2c.o goodix_ts_core.o goodix_cfg_bin.o + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI + goodix_v1430_mmi-objs += goodix_ts_mmi.o +endif + +obj-m += goodix_v1430_update_mmi.o +goodix_v1430_update_mmi-objs = goodix_gtx8_update.o + +obj-m += goodix_v1430_ts_tools_mmi.o +goodix_v1430_ts_tools_mmi-objs = goodix_ts_tools.o diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/Makefile b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_cfg_bin.c b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_cfg_bin.c new file mode 100644 index 000000000000..a2cbdd26c768 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_cfg_bin.c @@ -0,0 +1,599 @@ +#include "goodix_cfg_bin.h" + +extern struct goodix_module goodix_modules; +int goodix_ts_stage2_init(struct goodix_ts_core *core_data); +int goodix_ts_core_release(struct goodix_ts_core *core_data); + +static int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin); +static int goodix_get_reg_and_cfg(struct goodix_ts_device *ts_dev, + struct goodix_cfg_bin *cfg_bin); +static int goodix_read_cfg_bin(struct goodix_ts_device *ts_dev, + struct goodix_cfg_bin *cfg_bin); +static void goodix_cfg_pkg_leToCpu(struct goodix_cfg_package *pkg); + +static int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin) +{ + u8 checksum; + int i, r; + u16 offset1, offset2; + + if (!cfg_bin->bin_data || cfg_bin->bin_data_len == 0) { + ts_err("NO cfg_bin data, cfg_bin data length:%d", + cfg_bin->bin_data_len); + r = -EINVAL; + goto exit; + } + + /* 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); + r = -EINVAL; + goto exit; + } + 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); + r = -EINVAL; + goto exit; + } + + /*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); + r = -EINVAL; + goto exit; + } + + /*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) { + ts_err("cfg_pkgs, allocate memory ERROR"); + r = -ENOMEM; + goto exit; + } + + /*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); + r = -EINVAL; + 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); + /*compatible little edition and big edition*/ + goodix_cfg_pkg_leToCpu(&cfg_bin->cfg_pkgs[i]); + + /*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); + for (i = 0; i < cfg_bin->head.pkg_num; i++) { + ts_debug("---------------------------------------------"); + ts_debug("------package:%d------", i + 1); + ts_debug("package len:%04x", cfg_bin->cfg_pkgs[i].cnst_info.pkg_len); + ts_debug("package ic_type:%s", cfg_bin->cfg_pkgs[i].cnst_info.ic_type); + ts_debug("package cfg_type:%01x", cfg_bin->cfg_pkgs[i].cnst_info.cfg_type); + ts_debug("package sensor_id:%01x", cfg_bin->cfg_pkgs[i].cnst_info.sensor_id); + ts_debug("package hw_pid:%s", cfg_bin->cfg_pkgs[i].cnst_info.hw_pid); + ts_debug("package hw_vid:%s", cfg_bin->cfg_pkgs[i].cnst_info.hw_vid); + ts_debug("package fw_mask_version:%s", cfg_bin->cfg_pkgs[i].cnst_info.fw_mask); + ts_debug("package fw_patch_version:%s", cfg_bin->cfg_pkgs[i].cnst_info.fw_patch); + ts_debug("package x_res_offset:%02x", cfg_bin->cfg_pkgs[i].cnst_info.x_res_offset); + ts_debug("package y_res_offset:%02x", cfg_bin->cfg_pkgs[i].cnst_info.y_res_offset); + ts_debug("package trigger_offset:%02x", cfg_bin->cfg_pkgs[i].cnst_info.trigger_offset); + + ts_debug("reg info"); + ts_debug("send_cfg_flag reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.cfg_send_flag.addr); + ts_debug("version base reg:%02x, len:%d", + cfg_bin->cfg_pkgs[i].reg_info.version_base.addr, + cfg_bin->cfg_pkgs[i].reg_info.version_base.reserved1); + ts_debug("pid reg:%02x:%d", cfg_bin->cfg_pkgs[i].reg_info.pid.addr, + cfg_bin->cfg_pkgs[i].reg_info.pid.reserved1); + ts_debug("vid reg:%02x:%d", cfg_bin->cfg_pkgs[i].reg_info.vid.addr, + cfg_bin->cfg_pkgs[i].reg_info.vid.reserved1); + ts_debug("sensor_id reg:%02x,mask:%x", cfg_bin->cfg_pkgs[i].reg_info.sensor_id.addr, + cfg_bin->cfg_pkgs[i].reg_info.sensor_id.addr); + ts_debug("fw_status reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.fw_status.addr); + ts_debug("cfg_addr reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.cfg_addr.addr); + ts_debug("esd reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.esd.addr); + ts_debug("command reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.command.addr); + ts_debug("coor:%02x", cfg_bin->cfg_pkgs[i].reg_info.coor.addr); + ts_debug("gesture:%02x", cfg_bin->cfg_pkgs[i].reg_info.gesture.addr); + ts_debug("fw_request:%02x", cfg_bin->cfg_pkgs[i].reg_info.fw_request.addr); + ts_debug("proximity:%02x", cfg_bin->cfg_pkgs[i].reg_info.proximity.addr); + + ts_debug("--------------------------------------------"); + } + r = 0; +exit: + return r; +} + +static int goodix_cfg_bin_proc(struct goodix_ts_core *core_data) +{ + int r; + struct goodix_ts_device *ts_dev = core_data->ts_dev; + struct goodix_cfg_bin *cfg_bin = kzalloc(sizeof(struct goodix_cfg_bin), + GFP_KERNEL); + if (!cfg_bin) { + return -ENOMEM; + } + if (!ts_dev) { + ts_err("ts device can't be null"); + return -EINVAL; + } + + /* when start init config bin with error state */ + ts_dev->cfg_bin_state = CFG_BIN_STATE_ERROR; + + /*get cfg_bin from file system*/ + r = goodix_read_cfg_bin(ts_dev, cfg_bin); + if (r < 0) { + ts_err("Failed get valid config bin data"); + goto exit; + } + /*parse cfg bin*/ + r = goodix_parse_cfg_bin(cfg_bin); + if (!r) { + ts_info("parse cfg bin SUCCESS"); + } else { + ts_err("parse cfg bin FAILED"); + goto exit; + } + + /*get register address and configuration from cfg bin*/ + r = goodix_get_reg_and_cfg(ts_dev, cfg_bin); + if (!r) { + ts_info("success get reg and cfg info from cfg bin"); + } else { + ts_err("failed get cfg and reg info, update fw then retry"); + } + + /*debug*/ + ts_info("cfg_send_flag:0x%04x", ts_dev->reg.cfg_send_flag); + ts_info("pid:0x%04x", ts_dev->reg.pid); + ts_info("vid:0x%04x", ts_dev->reg.vid); + ts_info("sensor_id:0x%04x", ts_dev->reg.sensor_id); + ts_info("fw_mask:0x%04x", ts_dev->reg.fw_mask); + ts_info("fw_status:0x%04x", ts_dev->reg.fw_status); + ts_info("cfg_addr:0x%04x", ts_dev->reg.cfg_addr); + ts_info("esd:0x%04x", ts_dev->reg.esd); + ts_info("command:0x%04x", ts_dev->reg.command); + ts_info("coor:0x%04x", ts_dev->reg.coor); + ts_info("gesture:0x%04x", ts_dev->reg.gesture); + ts_info("fw_request:0x%04x", ts_dev->reg.fw_request); + ts_info("proximity:0x%04x", ts_dev->reg.proximity); + +exit: + kfree(cfg_bin->cfg_pkgs); + kfree(cfg_bin->bin_data); + kfree(cfg_bin); + if (r) + goodix_ts_blocking_notify(NOTIFY_CFG_BIN_FAILED, &r); + else + goodix_ts_blocking_notify(NOTIFY_CFG_BIN_SUCCESS, &r); + + ts_info("cfg bin state %d, ret %d", ts_dev->cfg_bin_state, r); + return r; +} + +static int goodix_extract_cfg_pkg(struct goodix_ts_device *ts_dev, + struct goodix_cfg_package *cfg_pkg) +{ + struct goodix_ts_config *ts_cfg; + + if (cfg_pkg->cnst_info.cfg_type == TS_NORMAL_CFG) { + ts_cfg = &(ts_dev->normal_cfg); + } else if (cfg_pkg->cnst_info.cfg_type == TS_HIGH_SENSE_CFG) { + ts_cfg = &(ts_dev->highsense_cfg); + } else { + ts_err("unknown cfg type %d", cfg_pkg->cnst_info.cfg_type); + return -EINVAL; + } + + ts_cfg->length = cfg_pkg->pkg_len - + TS_PKG_CONST_INFO_LEN - TS_PKG_REG_INFO_LEN; + if (ts_cfg->length > sizeof(ts_cfg->data)) { + ts_err("illegal cfg length %d", ts_cfg->length); + return -EINVAL; + } + if (ts_cfg->length) { + ts_info("get config type %d, len %d", + cfg_pkg->cnst_info.cfg_type, ts_cfg->length); + memcpy(ts_cfg->data, cfg_pkg->cfg, ts_cfg->length); + ts_cfg->initialized = TS_CFG_STABLE; + mutex_init(&ts_cfg->lock); + } else { + ts_info("no config data"); + } + + /*get register info*/ + ts_dev->reg.cfg_send_flag = cfg_pkg->reg_info.cfg_send_flag.addr; + ts_dev->reg.version_base = cfg_pkg->reg_info.version_base.addr; + ts_dev->reg.version_len = cfg_pkg->reg_info.version_base.reserved1; + ts_dev->reg.pid = cfg_pkg->reg_info.pid.addr; + ts_dev->reg.pid_len = cfg_pkg->reg_info.pid.reserved1; + ts_dev->reg.vid = cfg_pkg->reg_info.vid.addr; + ts_dev->reg.vid_len = cfg_pkg->reg_info.vid.reserved1; + ts_dev->reg.sensor_id = cfg_pkg->reg_info.sensor_id.addr; + ts_dev->reg.sensor_id_mask = cfg_pkg->reg_info.sensor_id.reserved1; + ts_dev->reg.fw_mask = cfg_pkg->reg_info.fw_mask.addr; + ts_dev->reg.fw_status = cfg_pkg->reg_info.fw_status.addr; + ts_dev->reg.cfg_addr = cfg_pkg->reg_info.cfg_addr.addr; + ts_dev->reg.esd = cfg_pkg->reg_info.esd.addr; + ts_dev->reg.command = cfg_pkg->reg_info.command.addr; + ts_dev->reg.coor = cfg_pkg->reg_info.coor.addr; + ts_dev->reg.gesture = cfg_pkg->reg_info.gesture.addr; + ts_dev->reg.fw_request = cfg_pkg->reg_info.fw_request.addr; + ts_dev->reg.proximity = cfg_pkg->reg_info.proximity.addr; + + return 0; +} + +static int goodix_get_reg_and_cfg(struct goodix_ts_device *ts_dev, + struct goodix_cfg_bin *cfg_bin) +{ + int i; + u16 addr; + u8 read_len; + char temp_sensor_id = -1; + u8 temp_fw_mask[TS_CFG_BLOCK_FW_MASK_LEN] = {0x00}; + u8 temp_pid[TS_CFG_BLOCK_PID_LEN] = {0x00}; + int r = -EINVAL; + + 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; + } + + memset(&ts_dev->normal_cfg, 0, sizeof(ts_dev->normal_cfg)); + memset(&ts_dev->highsense_cfg, 0, sizeof(ts_dev->highsense_cfg)); + + /* find suitable cfg packages */ + for (i = 0; i < cfg_bin->head.pkg_num; i++) { + /*get ic type*/ + if (!strncmp(cfg_bin->cfg_pkgs[i].cnst_info.ic_type, + "normandy", strlen("normandy"))) { + ts_dev->ic_type = IC_TYPE_NORMANDY; + } else if (!strncmp(cfg_bin->cfg_pkgs[i].cnst_info.ic_type, + "yellowstone", strlen("yellowstone"))) { + ts_dev->ic_type = IC_TYPE_YELLOWSTONE; + } else { + ts_err("unknow ic type of cfg_bin:%s", + cfg_bin->cfg_pkgs[i].cnst_info.ic_type); + continue; + } + + ts_info("ic_type:%d", ts_dev->ic_type); + + /* contrast sensor id */ + addr = cfg_bin->cfg_pkgs[i].reg_info.sensor_id.addr; + if (!addr) { + ts_info("pkg:%d, sensor_id reg is NULL", i); + continue; + } + + r = ts_dev->hw_ops->read(ts_dev, addr, &temp_sensor_id, 1); + if (r < 0) { + ts_err("failed get sensor of pkg:%d, reg:0x%02x", i, addr); + goto get_default_pkg; + } + ts_info("sensor id is %d", temp_sensor_id); + /*sensor.reserved1 is a mask, if it's not ZERO, use it*/ + if (cfg_bin->cfg_pkgs[i].reg_info.sensor_id.reserved1 != 0) + temp_sensor_id &= cfg_bin->cfg_pkgs[i].reg_info.sensor_id.reserved1; + + if (temp_sensor_id != cfg_bin->cfg_pkgs[i].cnst_info.sensor_id) { + ts_err("pkg:%d, sensor id contrast FAILED, reg:0x%02x", + i, addr); + ts_err("sensor_id from i2c:%d, sensor_id of cfg bin:%d", + temp_sensor_id, + cfg_bin->cfg_pkgs[i].cnst_info.sensor_id); + continue; + } + + /*contrast fw_mask, if this reg is null, skip this step*/ + addr = cfg_bin->cfg_pkgs[i].reg_info.fw_mask.addr; + if (addr && cfg_bin->cfg_pkgs[i].cnst_info.fw_mask[0]) { + r = ts_dev->hw_ops->read(ts_dev, addr, temp_fw_mask, + sizeof(temp_fw_mask)); + if (r < 0) { + ts_err("failed read fw_mask pkg:%d, reg:0x%02x", + i, addr); + goto get_default_pkg; + } + if (strncmp(temp_fw_mask, cfg_bin->cfg_pkgs[i].cnst_info.fw_mask, + sizeof(temp_fw_mask))) { + ts_err("pkg:%d, fw_mask contrast FAILED, reg:0x%02x,", i, addr); + ts_err("mask from i2c:%s, mask of cfg bin:%s", + temp_fw_mask, + cfg_bin->cfg_pkgs[i].cnst_info.fw_mask); + continue; + } + } + + /*contrast pid*/ + addr = cfg_bin->cfg_pkgs[i].reg_info.pid.addr; + read_len = cfg_bin->cfg_pkgs[i].reg_info.pid.reserved1; + if (!addr) { + ts_err("pkg:%d, pid reg is NULL", i); + continue; + } + if (read_len <= 0 || read_len > TS_CFG_BLOCK_PID_LEN) { + ts_err("pkg:%d, hw_pid length ERROR, len:%d", + i, read_len); + continue; + } + r = ts_dev->hw_ops->read(ts_dev, addr, temp_pid, read_len); + if (r < 0) { + ts_err("failed read pid pkg:%d, pid reg:0x%02x", i, addr); + goto get_default_pkg; + } + if (strncmp(temp_pid, cfg_bin->cfg_pkgs[i].cnst_info.hw_pid, read_len)) { + ts_err("pkg:%d, pid contrast FAILED, reg:0x%02x", i, addr); + ts_err("pid from i2c:%s, pid of cfg bin:%s", + temp_pid, cfg_bin->cfg_pkgs[i].cnst_info.hw_pid); + continue; + } + + ts_info("try get package info: ic type %s, cfg type %d", + cfg_bin->cfg_pkgs[i].cnst_info.ic_type, + cfg_bin->cfg_pkgs[i].cnst_info.cfg_type); + /* currently only support normal and high_sense config */ + if (cfg_bin->cfg_pkgs[i].cnst_info.cfg_type == TS_NORMAL_CFG || + cfg_bin->cfg_pkgs[i].cnst_info.cfg_type == TS_HIGH_SENSE_CFG) { + r = goodix_extract_cfg_pkg(ts_dev, &cfg_bin->cfg_pkgs[i]); + if (!r) { + ts_dev->cfg_bin_state = CFG_BIN_STATE_INITIALIZED; + ts_info("success parse cfg bin"); + } else { + ts_err("failed parse cfg bin"); + break; + } + } + } + +get_default_pkg: + if (ts_dev->cfg_bin_state != CFG_BIN_STATE_INITIALIZED) { + ts_err("no valid normal cfg, use cfg_pkg 0 as default"); + /* Foo code for recover dead IC. + * force set package 0 config type to normal config, this will + * config will use to recover IC. + */ + cfg_bin->cfg_pkgs[0].cnst_info.cfg_type = TS_NORMAL_CFG; + if (goodix_extract_cfg_pkg(ts_dev, &cfg_bin->cfg_pkgs[0])) { + ts_err("failed get valid config for IC recover"); + ts_dev->cfg_bin_state = CFG_BIN_STATE_ERROR; + } else { + ts_dev->cfg_bin_state = CFG_BIN_STATE_TEMP; + ts_info("get temp config data"); + } + + r = -EINVAL; + } + + return r; +} + +static int goodix_read_cfg_bin(struct goodix_ts_device *ts_dev, struct goodix_cfg_bin *cfg_bin) +{ + const struct firmware *firmware = NULL; + char cfg_bin_name[32] = {0x00}; + int i = 0, r; + + /*get cfg_bin_name*/ + if(ts_dev->board_data.cfg_bin_name) + strlcpy(cfg_bin_name, ts_dev->board_data.cfg_bin_name, + sizeof(cfg_bin_name)); + else + strlcpy(cfg_bin_name, TS_DEFAULT_CFG_BIN, + sizeof(cfg_bin_name)); + + ts_info("cfg_bin_name:%s", cfg_bin_name); + + for (i = 0; i < TS_RQST_FW_RETRY_TIMES; i++) { + r = request_firmware(&firmware, cfg_bin_name, ts_dev->dev); + if (r < 0) { + ts_err("failed get cfg bin[%s] error:%d, try_times:%d", + cfg_bin_name, r, i + 1); + msleep(1000); + } else { + ts_info("Cfg_bin image [%s] is ready, try_times:%d", + cfg_bin_name, i + 1); + break; + } + } + if (i >= TS_RQST_FW_RETRY_TIMES) { + ts_err("get cfg_bin FAILED"); + goto exit; + } + + if (firmware->size <= 0) { + ts_err("request_firmware, cfg_bin length ERROR,len:%zu", + firmware->size); + r = -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) + r = -ENOMEM; + + memcpy(cfg_bin->bin_data, firmware->data, cfg_bin->bin_data_len); + + r = 0; +exit: + if (firmware) { + release_firmware(firmware); + firmware = NULL; + } + return r; +} + +static void goodix_cfg_pkg_leToCpu(struct goodix_cfg_package *pkg) +{ + if (!pkg) { + ts_err("cfg package is NULL"); + return; + } + /*package const_info*/ + pkg->cnst_info.pkg_len = le32_to_cpu(pkg->cnst_info.pkg_len); + pkg->cnst_info.x_res_offset = le16_to_cpu(pkg->cnst_info.x_res_offset); + pkg->cnst_info.y_res_offset = le16_to_cpu(pkg->cnst_info.y_res_offset); + pkg->cnst_info.trigger_offset = le16_to_cpu(pkg->cnst_info.trigger_offset); + + /*package reg_info*/ + pkg->reg_info.cfg_send_flag.addr = le16_to_cpu(pkg->reg_info.cfg_send_flag.addr); + pkg->reg_info.pid.addr = le16_to_cpu(pkg->reg_info.pid.addr); + pkg->reg_info.vid.addr = le16_to_cpu(pkg->reg_info.vid.addr); + pkg->reg_info.sensor_id.addr = le16_to_cpu(pkg->reg_info.sensor_id.addr); + pkg->reg_info.fw_status.addr = le16_to_cpu(pkg->reg_info.fw_status.addr); + pkg->reg_info.cfg_addr.addr = le16_to_cpu(pkg->reg_info.cfg_addr.addr); + pkg->reg_info.esd.addr = le16_to_cpu(pkg->reg_info.esd.addr); + pkg->reg_info.command.addr = le16_to_cpu(pkg->reg_info.command.addr); + pkg->reg_info.coor.addr = le16_to_cpu(pkg->reg_info.coor.addr); + pkg->reg_info.gesture.addr = le16_to_cpu(pkg->reg_info.gesture.addr); + pkg->reg_info.fw_request.addr = le16_to_cpu(pkg->reg_info.fw_request.addr); + pkg->reg_info.proximity.addr = le16_to_cpu(pkg->reg_info.proximity.addr); +} + +static int goodix_later_init_thread(void *data) +{ + int ret; + struct goodix_ts_core *ts_core = data; + struct goodix_ts_device *ts_dev; + + if (!data) { + ts_err("ts core data can't be null"); + return -EINVAL; + } + ts_dev = ts_core->ts_dev; + if (!ts_dev) { + ts_err("ts dev data can't be null"); + return -EINVAL; + } + + ret = goodix_cfg_bin_proc(ts_core); + if (ret) + ts_err("parse cfg bin encounter error, %d", ret); + else + ts_info("success get cfg bin"); + + if (ts_dev->cfg_bin_state == CFG_BIN_STATE_ERROR) { + ts_err("parse cfg bin encounter fatal err"); + goto release_core; + } + + if (ts_dev->cfg_bin_state == CFG_BIN_STATE_TEMP) { + ts_err("failed get valid config data, retry after fwupdate"); + if (!ts_core->do_fw_update) { + ts_err("fw update handler not ready."); +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + goto update_fw_later; +#else + goto release_core; +#endif + } + ret = ts_core->do_fw_update(UPDATE_MODE_BLOCK|UPDATE_MODE_FORCE| + UPDATE_MODE_FLASH_CFG| + UPDATE_MODE_SRC_REQUEST); + if (ret) { + ts_err("fw update failed, %d", ret); +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + goto update_fw_later; +#else + goto release_core; +#endif + } + ts_info("fw update success retry parse cfg bin"); + ret = goodix_cfg_bin_proc(ts_core); + if (ret) { + ts_err("failed parse cfg bin after fw update"); + goto release_core; + } +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + goto stage2_init; +#endif + } else { + ts_info("success parse config bin"); + if (ts_core->do_fw_update) { + ret = ts_core->do_fw_update(UPDATE_MODE_BLOCK| + UPDATE_MODE_FLASH_CFG| + UPDATE_MODE_SRC_REQUEST); + if (ret) { + ts_info("fw update failed, %d[ignore]", ret); + ret = 0; + } + } +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + goto stage2_init; +#endif + } +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +update_fw_later: + ts_core->ts_mmi_info.need_reflash = 1; +stage2_init: +#endif + ret = goodix_ts_stage2_init(ts_core); + if (!ret) { + ts_info("stage2 init success"); + return ret; + } + ts_err("stage2 init failed, %d", ret); + +release_core: + goodix_ts_core_release(ts_core); + return ret; +} + +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; +} diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_cfg_bin.h b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_cfg_bin.h new file mode 100644 index 000000000000..c1aa93a82bba --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_cfg_bin.h @@ -0,0 +1,92 @@ +#ifndef _GOODIX_CFG_BIN_H_ +#define _GOODIX_CFG_BIN_H_ + +#include "goodix_ts_core.h" + +#define TS_DEFAULT_CFG_BIN "goodix_cfg_group.bin" +#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; +}; + +#endif diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_default_fw.h b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_default_fw.h new file mode 100644 index 000000000000..1632e6ef3c76 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_default_fw.h @@ -0,0 +1,6 @@ +#ifndef _GOODIX_FIRMWARE_FW_H_ +#define _GOODIX_FIRMWARE_FW_H_ + +u8 goodix_default_fw[] = { 0x00}; + +#endif diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_gtx8_update.c b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_gtx8_update.c new file mode 100644 index 000000000000..97d3037c0ef0 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_gtx8_update.c @@ -0,0 +1,1636 @@ +/* + * Goodix Firmware Update Driver. + * + * 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 "goodix_ts_core.h" +#include "goodix_cfg_bin.h" +#include "goodix_default_fw.h" +/* COMMON PART - START */ +#define TS_DEFAULT_FIRMWARE "goodix_firmware.bin" + +#define FW_HEADER_SIZE 256 +#define FW_SUBSYS_INFO_SIZE 8 +#define FW_SUBSYS_INFO_OFFSET 32 +#define FW_SUBSYS_MAX_NUM 28 + +#define ISP_MAX_BUFFERSIZE (1024 * 4) + +#define HW_REG_CPU_CTRL 0x2180 +#define HW_REG_DSP_MCU_POWER 0x2010 +#define HW_REG_RESET 0x2184 +#define HW_REG_SCRAMBLE 0x2218 +#define HW_REG_BANK_SELECT 0x2048 +#define HW_REG_ACCESS_PATCH0 0x204D +#define HW_REG_EC_SRM_START 0x204F +#define HW_REG_GIO_YS 0x2014 +#define HW_REG_ESD_KEY_EN 0x2318 +#define HW_REG_ESD_KEY_DIS 0x2324 +#define HW_REG_CPU_RUN_FROM 0x4506 /* for nor_L is 0x4006 */ +#define HW_REG_CPU_RUN_FROM_YS 0x4000 +#define HW_REG_ISP_RUN_FLAG 0x6006 +#define HW_REG_ISP_ADDR 0xC000 +#define HW_REG_ISP_BUFFER 0x6100 +#define HW_REG_SUBSYS_TYPE 0x6020 +#define HW_REG_FLASH_FLAG 0x6022 +#define HW_REG_CACHE 0x204B +#define HW_REG_ESD_KEY 0x2318 +#define HW_REG_WTD_TIMER 0x20B0 + +#define FLASH_ADDR_CONFIG_DATA 0x1E000 +#define FLASH_SUBSYS_TYPE_CONFIG 0x03 + +#define CPU_CTRL_PENDING 0x00 +#define CPU_CTRL_RUNNING 0x01 + +#define ISP_STAT_IDLE 0xFF +#define ISP_STAT_READY 0xAA +#define ISP_STAT_WRITING 0xAA +#define ISP_FLASH_SUCCESS 0xBB +#define ISP_FLASH_ERROR 0xCC +#define ISP_FLASH_CHECK_ERROR 0xDD +#define ISP_CMD_PREPARE 0x55 +#define ISP_CMD_FLASH 0xAA + +#define TS_CHECK_ISP_STATE_RETRY_TIMES 200 +#define TS_READ_FLASH_STATE_RETRY_TIMES 200 + +/** + * fw_subsys_info - subsytem firmware infomation + * @type: sybsystem type + * @size: firmware size + * @flash_addr: flash address + * @data: firmware data + */ +struct fw_subsys_info { + u8 type; + u32 size; + u16 flash_addr; + const u8 *data; +}; + +#pragma pack(1) +/** + * firmware_info + * @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 + * @subsys: sybsystem info + */ +struct firmware_info { + u32 size; + u16 checksum; + u8 hw_pid[6]; + u8 hw_vid[3]; + u8 fw_pid[8]; + u8 fw_vid[4]; + u8 subsys_num; + u8 chip_type; + u8 protocol_ver; + u8 reserved[2]; + struct fw_subsys_info subsys[FW_SUBSYS_MAX_NUM]; +}; + +#pragma pack() + +/** + * firmware_data - firmware data structure + * @fw_info: firmware infomation + * @firmware: firmware data structure + */ +struct firmware_data { + struct firmware_info fw_info; + const struct firmware *firmware; +}; + +enum update_status { + UPSTA_NOTWORK = 0, + UPSTA_PREPARING, + UPSTA_UPDATING, + UPSTA_ABORT, + UPSTA_SUCCESS, + UPSTA_FAILED +}; + +/** + * fw_update_ctrl - sturcture 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 + * @allow_reset: control the reset callback + * @allow_irq: control the irq callback + * @allow_suspend: control the suspend callback + * @allow_resume: allow resume callback + * @fw_data: firmware data + * @ts_dev: touch device + * @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 + */ +struct fw_update_ctrl { + struct mutex mutex; + int initialized; + int mode; + enum update_status status; + unsigned int progress; + + bool allow_reset; + bool allow_irq; + bool allow_suspend; + bool allow_resume; + + struct firmware_data fw_data; + struct goodix_ts_device *ts_dev; + struct goodix_ts_core *core_data; + + char fw_name[64]; + struct bin_attribute attr_fwimage; +}; +static struct fw_update_ctrl goodix_fw_update_ctrl; + +static int goodix_do_fw_update(int mode); + +static int goodix_set_fwname(char* fw_name) { + if (fw_name != NULL) { + strlcpy(goodix_fw_update_ctrl.fw_name, fw_name, + sizeof(goodix_fw_update_ctrl.fw_name)); + return 0; + } + else + return -EINVAL; +} +/** + * goodix_parse_firmware - parse firmware header infomation + * and subsystem infomation from firmware data buffer + * + * @fw_data: firmware struct, contains firmware header info + * and firmware data. + * return: 0 - OK, < 0 - error + */ +static int goodix_parse_firmware(struct firmware_data *fw_data) +{ + const struct firmware *firmware; + struct firmware_info *fw_info; + unsigned int i, fw_offset, info_offset; + u16 checksum; + int r = 0; + + if (!fw_data || !fw_data->firmware) { + ts_err("Invalid firmware data"); + return -EINVAL; + } + fw_info = &fw_data->fw_info; + + /* copy firmware head info */ + firmware = fw_data->firmware; + if (firmware->size < FW_SUBSYS_INFO_OFFSET) { + ts_err("Invalid firmware size:%zu", firmware->size); + r = -EINVAL; + goto err_size; + } + memcpy(fw_info, firmware->data, FW_SUBSYS_INFO_OFFSET); + + /* check firmware size */ + fw_info->size = be32_to_cpu(fw_info->size); + if (firmware->size != fw_info->size + 6) { + ts_err("Bad firmware, size not match"); + r = -EINVAL; + goto err_size; + } + + /* calculate checksum, note: sum of bytes, but check + * by u16 checksum + */ + for (i = 6, checksum = 0; i < firmware->size; i++) + checksum += firmware->data[i]; + + /* byte order change, and check */ + fw_info->checksum = be16_to_cpu(fw_info->checksum); + if (checksum != fw_info->checksum) { + ts_err("Bad firmware, cheksum error %x(file) != %x(cal)", + fw_info->checksum, checksum); + r = -EINVAL; + goto err_size; + } + + if (fw_info->subsys_num > FW_SUBSYS_MAX_NUM) { + ts_err("Bad firmware, invalid subsys num: %d", + fw_info->subsys_num); + r = -EINVAL; + goto err_size; + } + + /* parse subsystem info */ + fw_offset = FW_HEADER_SIZE; + for (i = 0; i < fw_info->subsys_num; i++) { + info_offset = FW_SUBSYS_INFO_OFFSET + + i * FW_SUBSYS_INFO_SIZE; + + fw_info->subsys[i].type = firmware->data[info_offset]; + fw_info->subsys[i].size = + be32_to_cpup((__be32 *)&firmware->data[info_offset + 1]); + fw_info->subsys[i].flash_addr = + be16_to_cpup((__be16 *)&firmware->data[info_offset + 5]); + + if (fw_offset > firmware->size) { + ts_err("Sybsys offset exceed Firmware size"); + goto err_size; + } + + fw_info->subsys[i].data = firmware->data + fw_offset; + fw_offset += fw_info->subsys[i].size; + } + + ts_info("Firmware package protocol: V%u", fw_info->protocol_ver); + ts_info("Fimware PID:GT%s", fw_info->fw_pid); + ts_info("Fimware VID:%02X%02X%02X%02x", fw_info->fw_vid[0], + fw_info->fw_vid[1], fw_info->fw_vid[2], fw_info->fw_vid[3]); + ts_info("Firmware chip type:%02X", fw_info->chip_type); + ts_info("Firmware size:%u", fw_info->size); + ts_info("Firmware subsystem num:%u", fw_info->subsys_num); +#ifdef CONFIG_GOODIX_DEBUG + for (i = 0; i < fw_info->subsys_num; i++) { + ts_debug("------------------------------------------"); + ts_debug("Index:%d", i); + ts_debug("Subsystem type:%02X", fw_info->subsys[i].type); + ts_debug("Subsystem size:%u", fw_info->subsys[i].size); + ts_debug("Subsystem flash_addr:%08X", fw_info->subsys[i].flash_addr); + ts_debug("Subsystem Ptr:%p", fw_info->subsys[i].data); + } + ts_debug("------------------------------------------"); +#endif + +err_size: + return r; +} + +/** + * goodix_check_update - compare the version of firmware running in + * touch device with the version getting from the firmware file. + * @fw_info: firmware infomation to be compared + * return: 0 no need do update, + * otherwise need do update + */ +static int goodix_check_update(struct goodix_ts_device *dev, + const struct firmware_info *fw_info) +{ + struct goodix_ts_version fw_ver; + int ret = -EINVAL; + + /* read version from chip, if we got invalid + * firmware version, maybe fimware in flash is + * incorrect, so we need to update firmware + */ + ret = dev->hw_ops->read_version(dev, &fw_ver); + if (ret) { + ts_info("failed get active pid"); + return -EINVAL; + } + + if (fw_ver.valid) { + // should we compare PID before fw update? + // if fw patch demage the PID may unmatch but + // we should de update to recover it. + // TODO skip PID check + /*if (memcmp(fw_ver.pid, fw_info->fw_pid, dev->reg.pid_len)) { + ts_err("Product ID is not match %s != %s", + fw_ver.pid, fw_info->fw_pid); + return -EPERM; + }*/ + + ret = memcmp(fw_ver.vid, fw_info->fw_vid, dev->reg.vid_len); + if (ret == 0) { + ts_info("FW version is equal to the IC's"); + return 0; + } else if (ret > 0) { + ts_info("Warning: fw version is lower the IC's"); + } + } /* else invalid firmware, update firmware */ + + ts_info("Firmware needs to be updated"); + return ret; +} + +/** + * 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(struct goodix_ts_device *dev, + unsigned int addr, unsigned char *data, unsigned int len) +{ + u8 *cfm, cfm_buf[32]; + int r, i; + + if (len > sizeof(cfm_buf)) { + cfm = kzalloc(len, GFP_KERNEL); + if (!cfm) { + ts_err("Mem alloc failed"); + return -ENOMEM; + } + } else { + cfm = &cfm_buf[0]; + } + + for (i = 0; i < GOODIX_BUS_RETRY_TIMES; i++) { + r = dev->hw_ops->write_trans(dev, addr, data, len); + if (r < 0) + goto exit; + + r = dev->hw_ops->read_trans(dev, addr, cfm, len); + if (r < 0) + goto exit; + + if (memcmp(data, cfm, len)) { + ts_info("data[0]:0x%02x, data[1]:0x%02x," + "read cfm[0]:0x%02x, cfm[1]:0x%02x", + data[0], data[1], cfm[0], cfm[1]); + dev->hw_ops->read_trans(dev, 0x6022, cfm, 2); + ts_info("read 0x6022 data[0]:0x%02x, data[1]:0x%02x", + cfm[0], cfm[1]); + r = -EMEMCMP; + continue; + } else { + r = 0; + break; + } + } + +exit: + if (cfm != &cfm_buf[0]) + kfree(cfm); + return r; +} + +static inline int goodix_reg_write(struct goodix_ts_device *dev, + unsigned int addr, unsigned char *data, unsigned int len) +{ + return dev->hw_ops->write_trans(dev, addr, data, len); +} + +static inline int goodix_reg_read(struct goodix_ts_device *dev, + unsigned int addr, unsigned char *data, unsigned int len) +{ + return dev->hw_ops->read_trans(dev, addr, data, len); +} + +/** + * goodix_load_isp - load ISP program to deivce ram + * @dev: pointer to touch device + * @fw_data: firmware data + * return 0 ok, <0 error + */ +static int goodix_load_isp(struct goodix_ts_device *ts_dev, + struct firmware_data *fw_data) +{ + struct fw_subsys_info *fw_isp; + u8 reg_val[8] = {0x00}; + int r; + int i; + + fw_isp = &fw_data->fw_info.subsys[0]; + + ts_info("Loading ISP start"); + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_BANK_SELECT, + reg_val, 1); + if (r < 0) { + ts_err("Failed to select bank0"); + return r; + } + ts_debug("Success select bank0, Set 0x%x -->0x00", HW_REG_BANK_SELECT); + + reg_val[0] = 0x01; + r = goodix_reg_write(ts_dev, HW_REG_ACCESS_PATCH0, + reg_val, 1); + if (r < 0) { + ts_err("Failed to enable patch0 access"); + return r; + } + ts_debug("Success select bank0, Set 0x%x -->0x01", HW_REG_ACCESS_PATCH0); + + r = goodix_reg_write_confirm(ts_dev, HW_REG_ISP_ADDR, + (u8 *)fw_isp->data, fw_isp->size); + if (r < 0) { + ts_err("Loading ISP error"); + return r; + } + + ts_debug("Success send ISP data to IC"); + + reg_val[0] = 0x00; + r = goodix_reg_write_confirm(ts_dev, HW_REG_ACCESS_PATCH0, + reg_val, 1); + if (r < 0) { + ts_err("Failed to disable patch0 access"); + return r; + } + ts_debug("Success forbit bank0 accedd, set 0x%x -->0x00", + HW_REG_ACCESS_PATCH0); + + reg_val[0] = 0x00; + reg_val[1] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_ISP_RUN_FLAG, reg_val, 2); + if (r < 0) { + ts_err("Failed to clear 0x%x", HW_REG_ISP_RUN_FLAG); + return r; + } + ts_debug("Success clear 0x%x", HW_REG_ISP_RUN_FLAG); + + memset(reg_val, 0x55, 8); + if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) + r = goodix_reg_write(ts_dev, HW_REG_CPU_RUN_FROM_YS, + reg_val, 8); + else + r = goodix_reg_write(ts_dev, HW_REG_CPU_RUN_FROM, + reg_val, 8); + if (r < 0) { + ts_err("Failed set backdoor flag"); + return r; + } + ts_info("Success write [8]0x55 to backdoor"); + + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_CPU_CTRL, + reg_val, 1); + if (r < 0) { + ts_err("Failed to run isp"); + return r; + } + ts_debug("Success run isp, set 0x%x-->0x00", HW_REG_CPU_CTRL); + + /* check isp work state */ + for (i = 0; i < TS_CHECK_ISP_STATE_RETRY_TIMES; i++) { + msleep(10); + r = goodix_reg_read(ts_dev, HW_REG_ISP_RUN_FLAG, + reg_val, 2); + if (r < 0 || (reg_val[0] == 0xAA && reg_val[1] == 0xBB)) + break; + } + if (reg_val[0] == 0xAA && reg_val[1] == 0xBB) { + ts_info("ISP working OK"); + return 0; + } + ts_err("ISP not work,0x%x=0x%x, 0x%x=0x%x", + HW_REG_ISP_RUN_FLAG, reg_val[0], + HW_REG_ISP_RUN_FLAG + 1, reg_val[1]); + return -EFAULT; +} + +/** + * 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) +{ + struct goodix_ts_device *ts_dev = fwu_ctrl->ts_dev; + u8 reg_val[4] = { 0x00 }; + u8 temp_buf[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + int retry = 20; + int r; + + if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) + ts_dev->hw_ops->write(ts_dev, HW_REG_CPU_RUN_FROM_YS, + temp_buf, 8); + else + ts_dev->hw_ops->write(ts_dev, HW_REG_CPU_RUN_FROM, + temp_buf, 8); + + /*reset IC*/ + fwu_ctrl->allow_reset = true; + ts_info("firmware update, reset"); + gpio_direction_output(ts_dev->board_data.reset_gpio, 0); + udelay(2000); + gpio_direction_output(ts_dev->board_data.reset_gpio, 1); + usleep_range(10000, 11000); + fwu_ctrl->allow_reset = false; + + retry = 100; + do { + if (ts_dev->ic_type != IC_TYPE_YELLOWSTONE) + break; + reg_val[0] = 0x00; + r = goodix_reg_write_confirm(ts_dev, HW_REG_GIO_YS, + reg_val, 1); + if (r < 0) + ts_info("Failed to remove GIO hold flag, retry %d", retry); + else + break; + } while (--retry); + if (!retry) { + ts_err("Failed to remove GIO flag"); + return -EINVAL; + } + + retry = 20; + for (retry = 0; retry < 20; retry++) { + if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) { + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_ESD_KEY_DIS, + reg_val, 1); + if (r < 0) { + ts_info("failed dis esd key"); + continue; + } + reg_val[0] = 0x95; + r = goodix_reg_write(ts_dev, HW_REG_ESD_KEY_EN, + reg_val, 1); + if (r < 0) { + ts_info("failed open esd key"); + continue; + } + } + reg_val[0] = 0x24; + r = goodix_reg_write_confirm(ts_dev, HW_REG_CPU_CTRL, + reg_val, 1); + if (r < 0) { + ts_info("Failed to hold ss51, retry"); + msleep(20); + } else { + break; + } + } + if (retry >= 20) { + ts_err("Failed hold ss51,return =%d", r); + return -EINVAL; + } + ts_debug("Success hold ss51"); + + /* enable DSP & MCU power */ + reg_val[0] = 0x00; + r = goodix_reg_write_confirm(ts_dev, HW_REG_DSP_MCU_POWER, reg_val, 1); + if (r < 0) { + ts_err("Failed enable DSP&MCU power"); + return r; + } + ts_debug("Success enabled DSP&MCU power,set 0x%x-->0x00", + HW_REG_DSP_MCU_POWER); + + /* disable watchdog timer */ + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_CACHE, reg_val, 1); + if (r < 0) { + ts_err("Failed to clear cache"); + return r; + } + ts_debug("Success clear cache"); + + if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) { + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_WTD_TIMER, reg_val, 1); + } else { + reg_val[0] = 0x95; + r = goodix_reg_write(ts_dev, HW_REG_ESD_KEY, reg_val, 1); + reg_val[0] = 0x00; + r |= goodix_reg_write(ts_dev, HW_REG_WTD_TIMER, reg_val, 1); + + reg_val[0] = 0x27; + r |= goodix_reg_write(ts_dev, HW_REG_ESD_KEY, reg_val, 1); + } + if (r < 0) { + ts_err("Failed to disable watchdog"); + return r; + } + ts_info("Success disable watchdog"); + + /* set scramble */ + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_SCRAMBLE, reg_val, 1); + if (r < 0) { + ts_err("Failed to set scramble"); + return r; + } + ts_debug("Succcess set scramble"); + + /* load ISP code and run form isp */ + r = goodix_load_isp(ts_dev, &fwu_ctrl->fw_data); + if (r < 0) + ts_err("Failed lode and run isp"); + + return r; +} + +/** + * goodix_format_fw_packet - formate one flash packet + * @pkt: target firmware packet + * @flash_addr: flash address + * @size: packet size + * @data: packet data + */ +static int goodix_format_fw_packet(u8 *pkt, u32 flash_addr, + u16 len, const u8 *data) +{ + u16 checksum; + + if (!pkt || !data) + return -EINVAL; + + /* + * checksum rule:sum of data in one format is equal to zero + * data format: byte/le16/be16/le32/be32/le64/be64 + */ + pkt[0] = (len >> 8) & 0xff; + pkt[1] = len & 0xff; + /* u16 >> 16bit seems nosense but really important */ + pkt[2] = (flash_addr >> 16) & 0xff; + pkt[3] = (flash_addr >> 8) & 0xff; + memcpy(&pkt[4], data, len); + checksum = checksum_be16(pkt, len + 4); + checksum = 0 - checksum; + pkt[len + 4] = (checksum >> 8) & 0xff; + pkt[len + 5] = checksum & 0xff; + return 0; +} + +/** + * goodix_send_fw_packet - send one firmware packet to ISP + * @dev: target touch device + * @pkt: firmware packet + * return:0 ok, <0 error + */ +static int goodix_send_fw_packet(struct goodix_ts_device *dev, u8 type, + u8 *pkt, u32 len) +{ + u8 reg_val[4]; + int r, i; + + if (!pkt) + return -EINVAL; + + ts_info("target fw subsys type:0x%x, len %d", type, len); + r = goodix_reg_write_confirm(dev, HW_REG_ISP_BUFFER, pkt, len); + if (r < 0) { + ts_err("Failed to write firmware packet"); + return r; + } + + reg_val[0] = 0; + reg_val[1] = 0; + /* clear flash flag 0X6022 */ + r = goodix_reg_write_confirm(dev, HW_REG_FLASH_FLAG, reg_val, 2); + if (r < 0) { + ts_err("Faile to clear flash flag"); + return r; + } + + /* write subsystem type 0X8020*/ + reg_val[0] = type; + reg_val[1] = type; + r = goodix_reg_write_confirm(dev, HW_REG_SUBSYS_TYPE, reg_val, 2); + if (r < 0) { + ts_err("Failed write subsystem type to IC"); + return r; + } + + for (i = 0; i < TS_READ_FLASH_STATE_RETRY_TIMES; i++) { + r = goodix_reg_read(dev, HW_REG_FLASH_FLAG, reg_val, 2); + if (r < 0) { + ts_err("Failed read flash state"); + return r; + } + + /* flash haven't end */ + if (reg_val[0] == ISP_STAT_WRITING && + reg_val[1] == ISP_STAT_WRITING) { + ts_debug("Flash not ending..."); + usleep_range(55000, 56000); + continue; + } + if (reg_val[0] == ISP_FLASH_SUCCESS && + reg_val[1] == ISP_FLASH_SUCCESS) { + r = goodix_reg_read(dev, HW_REG_FLASH_FLAG, reg_val, 2); + if (!r && reg_val[0] == ISP_FLASH_SUCCESS && + reg_val[1] == ISP_FLASH_SUCCESS) { + ts_info("Flash subsystem ok"); + return 0; + } + } + if (reg_val[0] == ISP_FLASH_ERROR && + reg_val[1] == ISP_FLASH_ERROR) { + ts_err(" Flash subsystem failed"); + return -EAGAIN; + } + if (reg_val[0] == ISP_FLASH_CHECK_ERROR) { + ts_err("Subsystem checksum err"); + return -EAGAIN; + } + + usleep_range(250, 260); + } + + ts_err("Wait for flash end timeout, 0x6022= %x %x", + reg_val[0], reg_val[1]); + return -EAGAIN; +} + +/** + * 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 infomation + * return: 0 ok, < 0 error + */ +static int goodix_flash_subsystem(struct goodix_ts_device *dev, + struct fw_subsys_info *subsys) +{ + u16 data_size, offset; + u32 total_size; + u32 subsys_base_addr = subsys->flash_addr << 8; + u8 *fw_packet; + int r = 0, i; + + /* + * 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 + 6, 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); + + /* format one firmware packet */ + r = goodix_format_fw_packet(fw_packet, subsys_base_addr + offset, + data_size, &subsys->data[offset]); + if (r < 0) { + ts_err("Invalid packet params"); + goto exit; + } + + /* send one firmware packet, retry 3 time if send failed */ + for (i = 0; i < 3; i++) { + r = goodix_send_fw_packet(dev, subsys->type, + fw_packet, data_size + 6); + if (!r) + break; + } + if (r) { + ts_err("Failed flash subsystem"); + goto exit; + } + offset += data_size; + total_size -= data_size; + } /* end while */ + +exit: + 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 goodix_ts_device *dev, + struct firmware_data *fw_data) +{ + struct fw_update_ctrl *fw_ctrl; + struct firmware_info *fw_info; + struct fw_subsys_info *fw_x; + int retry = GOODIX_BUS_RETRY_TIMES; + int i, r = 0, fw_num, prog_step; + u8 *fw_packet = NULL; + u8 *flash_cfg = NULL; + + /* start from subsystem 1, + * subsystem 0 is the ISP program + */ + fw_ctrl = container_of(fw_data, struct fw_update_ctrl, fw_data); + fw_info = &fw_data->fw_info; + fw_num = fw_info->subsys_num; + + /* we have 80% work here */ + prog_step = 80 / (fw_num - 1); + + for (i = 1; i < fw_num && retry;) { + ts_info("--- Start to flash subsystem[%d] ---", i); + fw_x = &fw_info->subsys[i]; + r = goodix_flash_subsystem(dev, fw_x); + if (r == 0) { + ts_info("--- End flash subsystem[%d]: OK ---", i); + fw_ctrl->progress += prog_step; + 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); + break; + } + } + + kfree(fw_packet); + fw_packet = NULL; + kfree(flash_cfg); + flash_cfg = NULL; + 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 goodix_ts_device *ts_dev, + struct fw_update_ctrl *fwu_ctrl) +{ + u8 reg_val[8] = {0}; + int r = 0, i = 0; + + /* hold ss51 */ + reg_val[0] = 0x24; + r = goodix_reg_write(ts_dev, HW_REG_CPU_CTRL, + reg_val, 1); + if (r < 0) + ts_err("Failed to hold ss51"); + + /* clear back door flag */ + memset(reg_val, 0, sizeof(reg_val)); + if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) + r = goodix_reg_write(ts_dev, HW_REG_CPU_RUN_FROM_YS, + reg_val, 8); + else + r = goodix_reg_write(ts_dev, HW_REG_CPU_RUN_FROM, + reg_val, 8); + if (r) { + ts_err("Failed set CPU run from normal firmware"); + return r; + } + + /* release ss51 */ + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_CPU_CTRL, reg_val, 1); + if (r < 0) + ts_err("Failed to run ss51"); + + /*reset*/ + gpio_direction_output(ts_dev->board_data.reset_gpio, 0); + udelay(2000); + gpio_direction_output(ts_dev->board_data.reset_gpio, 1); + msleep(80); + + for (i = 0; ts_dev->ic_type == IC_TYPE_YELLOWSTONE && i < 100; i++) { + reg_val[0] = 0x00; + r = goodix_reg_write_confirm(ts_dev, HW_REG_GIO_YS, reg_val, 1); + if (!r) + break; + ts_info("failed set GIO flag, r %d", r); + } + if (i >= 100) + ts_err("failed set GIO flag, %d", r); + + return r; +} + +static int goodix_flash_config(struct goodix_ts_device *ts_dev) +{ + int ret; + u8 *fw_packet = NULL; + u8 *temp_data = NULL; + + if (!ts_dev || !ts_dev->cfg_bin_state || + !ts_dev->normal_cfg.initialized) { + ts_err("no valid config data for flash"); + return -EINVAL; + } + + temp_data = kzalloc(ISP_MAX_BUFFERSIZE, GFP_KERNEL); + fw_packet = kzalloc(ISP_MAX_BUFFERSIZE + 6, GFP_KERNEL); + if (!temp_data || !fw_packet) { + ts_err("Failed alloc memory"); + ret = -EINVAL; + goto exit; + } + + memset(temp_data, 0xFF, ISP_MAX_BUFFERSIZE); + ts_info("normal config length %d", ts_dev->normal_cfg.length); + memcpy(temp_data, ts_dev->normal_cfg.data, ts_dev->normal_cfg.length); + + /* format one firmware packet */ + ret = goodix_format_fw_packet(fw_packet, FLASH_ADDR_CONFIG_DATA, + ISP_MAX_BUFFERSIZE, temp_data); + if (ret < 0) { + ts_err("Invalid packet params"); + goto exit; + } + ts_debug("fw_pack:%*ph", 10, fw_packet); + ts_info("try flash config"); + ret = goodix_send_fw_packet(ts_dev, FLASH_SUBSYS_TYPE_CONFIG, + fw_packet, ISP_MAX_BUFFERSIZE + 6); + if (ret) + ts_err("failed flash config, ret %d", ret); + else + ts_info("success flash config with isp"); + +exit: + kfree(temp_data); + kfree(fw_packet); + return ret; +} + +/** + * goodix_fw_update_proc - firmware update process, the entry of + * firmware update flow + * @fwu_ctrl: firmware control + * return: 0 ok, < 0 error + */ +static 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 r = 0; + + if (fwu_ctrl->status == UPSTA_PREPARING || + fwu_ctrl->status == UPSTA_UPDATING) { + ts_err("Firmware update already in progress"); + return -EINVAL; + } + + fwu_ctrl->progress = 0; + fwu_ctrl->status = UPSTA_PREPARING; + + r = goodix_parse_firmware(&fwu_ctrl->fw_data); + if (r < 0) { + fwu_ctrl->status = UPSTA_ABORT; + goto err_parse_fw; + } + + fwu_ctrl->progress = 10; + if (!(fwu_ctrl->mode & UPDATE_MODE_FORCE)) { + r = goodix_check_update(fwu_ctrl->ts_dev, + &fwu_ctrl->fw_data.fw_info); + if (!r) { + fwu_ctrl->status = UPSTA_ABORT; + ts_info("fw update skiped"); + goto err_check_update; + } + } else { + ts_info("force update mode"); + } + +start_update: + fwu_ctrl->progress = 20; + fwu_ctrl->status = UPSTA_UPDATING; /* show upgrading status */ + r = goodix_update_prepare(fwu_ctrl); + if ((r == -EBUS || r == -EAGAIN) && --retry0 > 0) { + ts_err("Bus error, retry prepare ISP:%d", + FW_UPDATE_RETRY - retry0); + goto start_update; + } else if (r < 0) { + ts_err("Failed to prepare ISP, exit update:%d", r); + fwu_ctrl->status = UPSTA_FAILED; + goto err_fw_prepare; + } + + if (GOODIX_FLASH_CONFIG_WITH_ISP && + fwu_ctrl->mode & UPDATE_MODE_FLASH_CFG) { + ts_info("need flash config with isp"); + goodix_flash_config(fwu_ctrl->ts_dev); + } + + /* progress: 20%~100% */ + r = goodix_flash_firmware(fwu_ctrl->ts_dev, &fwu_ctrl->fw_data); + if ((r == -EBUS || r == -ETIMEOUT) && --retry1 > 0) { + /* we will retry[twice] if returns bus error[i2c/spi] + * we will do hardware reset and re-prepare ISP and then retry + * flashing + */ + ts_err("Bus error, retry firmware update:%d", + FW_UPDATE_RETRY - retry1); + goto start_update; + } else if (r < 0) { + ts_err("Fatal error, exit update:%d", r); + fwu_ctrl->status = UPSTA_FAILED; + goto err_fw_flash; + } + + fwu_ctrl->status = UPSTA_SUCCESS; + +err_fw_flash: +err_fw_prepare: + goodix_update_finish(fwu_ctrl->ts_dev, fwu_ctrl); +err_check_update: +err_parse_fw: + if (fwu_ctrl->status == UPSTA_SUCCESS) + ts_info("Firmware update successfully"); + else if (fwu_ctrl->status == UPSTA_FAILED) + ts_err("Firmware update failed"); + + fwu_ctrl->progress = 100; /* 100% */ + ts_info("fw update ret %d", r); + return r; +} + +static struct goodix_ext_module goodix_fwu_module; + +/** + * 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->ts_dev->dev; + int r; + + ts_info("Request firmware image [%s]", name); + r = request_firmware(&fw_data->firmware, name, dev); + if (r < 0) + ts_err("Firmware image [%s] not available,errno:%d", name, r); + else + ts_info("Firmware image [%s] is ready", name); + return r; +} + +/** + * 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; + struct firmware *temp_firmware = NULL; + int r = -EINVAL; + + mutex_lock(&fwu_ctrl->mutex); + + if (fwu_ctrl->mode & UPDATE_MODE_SRC_HEAD) { + ts_info("Firmware header update starts"); + temp_firmware = kzalloc(sizeof(struct firmware), GFP_KERNEL); + if (!temp_firmware) { + ts_err("Failed to allocate memory for firmware"); + goto out; + } + temp_firmware->size = sizeof(goodix_default_fw); + temp_firmware->data = goodix_default_fw; + fwu_ctrl->fw_data.firmware = temp_firmware; + } else 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) { + fwu_ctrl->status = UPSTA_ABORT; + fwu_ctrl->progress = 100; + goto out; + } + } else if (fwu_ctrl->mode & UPDATE_MODE_SRC_SYSFS) { + if (!fwu_ctrl->fw_data.firmware) { + ts_err("Invalid firmware from sysfs"); + fwu_ctrl->status = UPSTA_ABORT; + fwu_ctrl->progress = 100; + r = -EINVAL; + goto out; + } + } else { + ts_err("unknown update mode 0x%x", fwu_ctrl->mode); + r = -EINVAL; + goto out; + } + + goodix_register_ext_module(&goodix_fwu_module); + /* DONT allow reset/irq/suspend/resume during update */ + fwu_ctrl->allow_irq = false; + fwu_ctrl->allow_suspend = false; + fwu_ctrl->allow_resume = false; + fwu_ctrl->allow_reset = false; + ts_debug("notify update start"); + goodix_ts_blocking_notify(NOTIFY_FWUPDATE_START, NULL); + + /* ready to update */ + ts_debug("start update proc"); + r = goodix_fw_update_proc(fwu_ctrl); + + fwu_ctrl->allow_reset = true; + fwu_ctrl->allow_irq = true; + fwu_ctrl->allow_suspend = true; + fwu_ctrl->allow_resume = true; + + /* clean */ + if (fwu_ctrl->mode & UPDATE_MODE_SRC_HEAD) { + kfree(fwu_ctrl->fw_data.firmware); + fwu_ctrl->fw_data.firmware = NULL; + temp_firmware = NULL; + } else if (fwu_ctrl->mode & UPDATE_MODE_SRC_REQUEST) { + goodix_release_firmware(&fwu_ctrl->fw_data); + } else if (fwu_ctrl->mode & UPDATE_MODE_SRC_SYSFS) { + vfree(fwu_ctrl->fw_data.firmware); + fwu_ctrl->fw_data.firmware = NULL; + } + goodix_unregister_ext_module(&goodix_fwu_module); +out: + fwu_ctrl->mode = UPDATE_MODE_DEFAULT; + mutex_unlock(&fwu_ctrl->mutex); + + if (r) { + ts_err("fw update failed, %d", r); + goodix_ts_blocking_notify(NOTIFY_FWUPDATE_FAILED, NULL); + } else { + if (fwu_ctrl->status == UPSTA_SUCCESS) { + ts_info("fw update success"); + goodix_ts_blocking_notify(NOTIFY_FWUPDATE_SUCCESS, NULL); + } else if (fwu_ctrl->status == UPSTA_ABORT) { + ts_info("fw update skiped"); + goodix_ts_blocking_notify(NOTIFY_FWUPDATE_SKIP, NULL); + } + } + return r; +} + +/* + * 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 goodix_ext_module *module, + const char *buf, size_t count) +{ + int ret = 0; + int mode = 0; + struct fw_update_ctrl *fw_ctrl = module->priv_data; + + 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(mode); + if (!ret) { + ts_info("success start update work"); + return count; + } + ts_err("failed start fw update work"); + return -EINVAL; +} + +static ssize_t goodix_sysfs_update_progress_show( + struct goodix_ext_module *module, + char *buf) +{ + struct fw_update_ctrl *fw_ctrl = module->priv_data; + return scnprintf(buf, PAGE_SIZE, "%d\n", fw_ctrl->progress); +} + +static ssize_t goodix_sysfs_update_result_show( + struct goodix_ext_module *module, + char *buf) +{ + char *result = NULL; + struct fw_update_ctrl *fw_ctrl = module->priv_data; + + ts_info("result show"); + switch (fw_ctrl->status) { + case UPSTA_NOTWORK: + result = "notwork"; + break; + case UPSTA_PREPARING: + result = "preparing"; + break; + case UPSTA_UPDATING: + result = "upgrading"; + break; + case UPSTA_ABORT: + result = "abort"; + break; + case UPSTA_SUCCESS: + result = "success"; + break; + case UPSTA_FAILED: + result = "failed"; + break; + default: + break; + } + + return scnprintf(buf, PAGE_SIZE, "%s\n", result); +} + +static ssize_t goodix_sysfs_update_fwversion_show( + struct goodix_ext_module *module, + char *buf) +{ + struct goodix_ts_version fw_ver; + struct fw_update_ctrl *fw_ctrl = module->priv_data; + int r = 0; + char str[5]; + + /* read version from chip */ + r = fw_ctrl->ts_dev->hw_ops->read_version(fw_ctrl->ts_dev, + &fw_ver); + if (!r) { + memcpy(str, fw_ver.pid, 4); + str[4] = '\0'; + return scnprintf(buf, PAGE_SIZE, "PID:%s VID:%02x %02x %02x %02x SENSOR_ID:%d\n", + str, fw_ver.vid[0], fw_ver.vid[1], + fw_ver.vid[2], fw_ver.vid[3], fw_ver.sensor_id); + } + return 0; +} + +static ssize_t goodix_sysfs_fwsize_show(struct goodix_ext_module *module, + char *buf) +{ + struct fw_update_ctrl *fw_ctrl = module->priv_data; + int r = -EINVAL; + + if (fw_ctrl && fw_ctrl->fw_data.firmware) + r = snprintf(buf, PAGE_SIZE, "%zu\n", + fw_ctrl->fw_data.firmware->size); + return r; +} + +static ssize_t goodix_sysfs_fwsize_store(struct goodix_ext_module *module, + const char *buf, size_t count) +{ + struct fw_update_ctrl *fw_ctrl = module->priv_data; + struct firmware *fw; + u8 **data; + size_t size = 0; + + if (!fw_ctrl) + return -EINVAL; + + if (sscanf(buf, "%zu", &size) < 0 || !size) { + ts_err("Failed to get fwsize"); + return -EFAULT; + } + + /* use vmalloc to alloc huge memory */ + fw = vmalloc(sizeof(*fw) + size); + if (fw == NULL) { + ts_err("Failed to alloc memory,size:%zu", + size + sizeof(*fw)); + return -ENOMEM; + } + mutex_lock(&fw_ctrl->mutex); + memset(fw, 0x00, sizeof(*fw) + size); + data = (u8 **)&fw->data; + *data = (u8 *)fw + sizeof(struct firmware); + fw->size = size; + fw_ctrl->fw_data.firmware = fw; + fw_ctrl->mode = UPDATE_MODE_SRC_SYSFS; + mutex_unlock(&fw_ctrl->mutex); + return count; +} + +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 fw_update_ctrl *fw_ctrl; + struct firmware_data *fw_data; + + fw_ctrl = container_of(attr, struct fw_update_ctrl, + attr_fwimage); + fw_data = &fw_ctrl->fw_data; + + if (!fw_data->firmware) { + ts_err("Need set fw image size first"); + return -ENOMEM; + } + + if (fw_data->firmware->size == 0) { + ts_err("Invalid firmware size"); + return -EINVAL; + } + + if (pos + count > fw_data->firmware->size) + return -EFAULT; + mutex_lock(&fw_ctrl->mutex); + memcpy((u8 *)&fw_data->firmware->data[pos], buf, count); + mutex_unlock(&fw_ctrl->mutex); + return count; +} + +/* this interface has ben deprecated */ +static ssize_t goodix_sysfs_force_update_store( + struct goodix_ext_module *module, + const char *buf, size_t count) +{ + return count; +} + +static struct goodix_ext_attribute goodix_fwu_attrs[] = { + __EXTMOD_ATTR(update_en, S_IWUGO, NULL, goodix_sysfs_update_en_store), + __EXTMOD_ATTR(progress, S_IRUGO, goodix_sysfs_update_progress_show, NULL), + __EXTMOD_ATTR(result, S_IRUGO, goodix_sysfs_update_result_show, NULL), + __EXTMOD_ATTR(fwversion, S_IRUGO, + goodix_sysfs_update_fwversion_show, NULL), + __EXTMOD_ATTR(fwsize, S_IRUGO | S_IWUGO, goodix_sysfs_fwsize_show, + goodix_sysfs_fwsize_store), + __EXTMOD_ATTR(force_update, S_IWUGO, NULL, + goodix_sysfs_force_update_store), +}; + +static int goodix_fw_sysfs_init(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + struct fw_update_ctrl *fw_ctrl = module->priv_data; + struct kobj_type *ktype; + int ret = 0, i; + + ktype = goodix_get_default_ktype(); + ret = kobject_init_and_add(&module->kobj, + ktype, + &core_data->pdev->dev.kobj, + "fwupdate"); + if (ret) { + ts_err("Create fwupdate sysfs node error!"); + goto exit_sysfs_init; + } + + + ret = 0; + for (i = 0; i < ARRAY_SIZE(goodix_fwu_attrs) && !ret; i++) + ret = sysfs_create_file(&module->kobj, &goodix_fwu_attrs[i].attr); + if (ret) { + ts_err("failed create fwu sysfs files"); + while (--i >= 0) + sysfs_remove_file(&module->kobj, &goodix_fwu_attrs[i].attr); + + kobject_put(&module->kobj); + ret = -EINVAL; + goto exit_sysfs_init; + } + + fw_ctrl->attr_fwimage.attr.name = "fwimage"; + fw_ctrl->attr_fwimage.attr.mode = S_IRUGO | S_IWUGO; + fw_ctrl->attr_fwimage.size = 0; + fw_ctrl->attr_fwimage.write = goodix_sysfs_fwimage_store; + ret = sysfs_create_bin_file(&module->kobj, + &fw_ctrl->attr_fwimage); + +exit_sysfs_init: + return ret; +} + +static void goodix_fw_sysfs_remove(struct goodix_ext_module *module) +{ + struct fw_update_ctrl *fw_ctrl = module->priv_data; + int i; + + sysfs_remove_bin_file(&module->kobj, &fw_ctrl->attr_fwimage); + + for (i = 0; i < ARRAY_SIZE(goodix_fwu_attrs); i++) + sysfs_remove_file(&module->kobj, + &goodix_fwu_attrs[i].attr); + + kobject_put(&module->kobj); +} + +static int goodix_do_fw_update(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; + 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; + } else { + /* 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; + } +} + +static int goodix_fw_update_init(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + int ret = 0; + struct goodix_ts_board_data *ts_bdata = board_data(core_data); + + if (goodix_fw_update_ctrl.initialized) { + ts_info("no need reinit"); + return ret; + } + + if (!core_data || !ts_bdata || !core_data->ts_dev) { + ts_err("core_data && ts_dev cann't be null"); + return -ENODEV; + } + + mutex_lock(&goodix_fw_update_ctrl.mutex); + core_data->do_fw_update = goodix_do_fw_update; + core_data->set_fw_name = goodix_set_fwname; + module->priv_data = &goodix_fw_update_ctrl; + + goodix_fw_update_ctrl.ts_dev = core_data->ts_dev; + goodix_fw_update_ctrl.allow_reset = true; + goodix_fw_update_ctrl.allow_irq = true; + goodix_fw_update_ctrl.allow_suspend = true; + goodix_fw_update_ctrl.allow_resume = true; + goodix_fw_update_ctrl.core_data = core_data; + goodix_fw_update_ctrl.mode = 0; + /* find a valid firmware image name */ + if (ts_bdata && ts_bdata->fw_name) + strlcpy(goodix_fw_update_ctrl.fw_name, ts_bdata->fw_name, + sizeof(goodix_fw_update_ctrl.fw_name)); + else + strlcpy(goodix_fw_update_ctrl.fw_name, TS_DEFAULT_FIRMWARE, + sizeof(goodix_fw_update_ctrl.fw_name)); + + ret = goodix_fw_sysfs_init(core_data, module); + if (ret) { + ts_err("failed create fwupate sysfs node"); + goto err_out; + } + + goodix_fw_update_ctrl.initialized = 1; +err_out: + mutex_unlock(&goodix_fw_update_ctrl.mutex); +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + if (!ret) { + goodix_do_fw_update(UPDATE_MODE_SRC_REQUEST); + if (ret) + ts_err("fw update failed, %d", ret); + } +#endif + + return ret; +} + +static int goodix_fw_update_exit(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + return 0; +} + +static int goodix_fw_before_suspend(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + struct fw_update_ctrl *fwu_ctrl = module->priv_data; + + return fwu_ctrl->allow_suspend ? + EVT_HANDLED : EVT_CANCEL_SUSPEND; +} + +static int goodix_fw_before_resume(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + struct fw_update_ctrl *fwu_ctrl = module->priv_data; + + return fwu_ctrl->allow_resume ? + EVT_HANDLED : EVT_CANCEL_RESUME; +} + +static int goodix_fw_after_resume(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + return 0; +} + +static int goodix_fw_irq_event(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + struct fw_update_ctrl *fwu_ctrl = module->priv_data; + + return fwu_ctrl->allow_irq ? + EVT_HANDLED : EVT_CANCEL_IRQEVT; +} + +static int goodix_fw_before_reset(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + struct fw_update_ctrl *fwu_ctrl = module->priv_data; + + return fwu_ctrl->allow_reset ? + EVT_HANDLED : EVT_CANCEL_RESET; +} + +static const struct goodix_ext_module_funcs goodix_ext_funcs = { + .init = goodix_fw_update_init, + .exit = goodix_fw_update_exit, + .before_reset = goodix_fw_before_reset, + .after_reset = NULL, + .before_suspend = goodix_fw_before_suspend, + .after_suspend = NULL, + .before_resume = goodix_fw_before_resume, + .after_resume = goodix_fw_after_resume, + .irq_event = goodix_fw_irq_event, +}; + +static struct goodix_ext_module goodix_fwu_module = { + .name = "goodix-fwu", + .funcs = &goodix_ext_funcs, + .priority = EXTMOD_PRIO_FWUPDATE, +}; + +static int __init goodix_fwu_module_init(void) +{ + ts_info("goodix_fwupdate_module_ini IN"); + mutex_init(&goodix_fw_update_ctrl.mutex); + return goodix_register_ext_module(&goodix_fwu_module); +} + +static void __exit goodix_fwu_module_exit(void) +{ + mutex_lock(&goodix_fw_update_ctrl.mutex); + goodix_unregister_ext_module(&goodix_fwu_module); + if (goodix_fw_update_ctrl.initialized) { + goodix_fw_sysfs_remove(&goodix_fwu_module); + goodix_fw_update_ctrl.initialized = 0; + } + mutex_lock(&goodix_fw_update_ctrl.mutex); +} + +module_init(goodix_fwu_module_init); +module_exit(goodix_fwu_module_exit); + +MODULE_DESCRIPTION("Goodix FWU Module"); +MODULE_AUTHOR("Goodix, Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_core.c b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_core.c new file mode 100644 index 000000000000..c7369e643d24 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_core.c @@ -0,0 +1,2297 @@ + /* + * Goodix Touchscreen Driver + * Core layer of touchdriver architecture. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_FB +#include +#include +#endif + +#include "goodix_ts_core.h" +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +#include "goodix_ts_mmi.h" +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) +#include +#define INPUT_TYPE_B_PROTOCOL +#endif + +#define GOOIDX_INPUT_PHYS "goodix_ts/input0" +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" + +static int goodix_ts_remove(struct platform_device *pdev); +int goodix_start_later_init(struct goodix_ts_core *ts_core); +void goodix_ts_dev_release(void); + +struct goodix_module goodix_modules; + +/** + * __do_register_ext_module - register external module + * to register into touch core modules structure + */ +static void __do_register_ext_module(struct work_struct *work) +{ + struct goodix_ext_module *module = + container_of(work, struct goodix_ext_module, work); + struct goodix_ext_module *ext_module, *next; + struct list_head *insert_point = &goodix_modules.head; + + ts_info("__do_register_ext_module IN"); + + if (goodix_modules.core_data && + !goodix_modules.core_data->initialized) { + ts_err("core layer has exit"); + return; + } + + if (!goodix_modules.core_data) { + /* waitting for core layer */ + if (!wait_for_completion_timeout(&goodix_modules.core_comp, + 25 * HZ)) { + ts_err("Module [%s] timeout", module->name); + return; + } + } + + /* driver probe failed */ + if (!goodix_modules.core_data || + !goodix_modules.core_data->initialized) { + ts_err("Can't register ext_module core error"); + return; + } + + ts_info("start register ext_module"); + + /* prority level *must* be set */ + if (module->priority == EXTMOD_PRIO_RESERVED) { + ts_err("Priority of module [%s] needs to be set", + module->name); + return; + } + + 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) { + ts_info("Module [%s] already exists", + module->name); + mutex_unlock(&goodix_modules.mutex); + return; + } + } + + list_for_each_entry_safe(ext_module, next, + &goodix_modules.head, list) { + /* small value of priority have + * higher priority level + */ + 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; + } + } + + list_add(&module->list, insert_point->prev); + goodix_modules.count++; + mutex_unlock(&goodix_modules.mutex); + + ts_info("Module [%s] registered,priority:%u", + module->name, + module->priority); +} + +/** + * 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) +{ + if (!module) + return -EINVAL; + + if (!goodix_modules.initilized) { + goodix_modules.initilized = true; + INIT_LIST_HEAD(&goodix_modules.head); + mutex_init(&goodix_modules.mutex); + init_completion(&goodix_modules.core_comp); + } + + ts_info("goodix_register_ext_module IN"); + + INIT_WORK(&module->work, __do_register_ext_module); + schedule_work(&module->work); + + ts_info("goodix_register_ext_module OUT"); + + return 0; +} +EXPORT_SYMBOL_GPL(goodix_register_ext_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 -EFAULT; + } + + if (!found) { + ts_err("Module [%s] never registed", + module->name); + mutex_unlock(&goodix_modules.mutex); + return -EFAULT; + } + + list_del(&module->list); + goodix_modules.count--; + 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; +} +EXPORT_SYMBOL_GPL(goodix_unregister_ext_module); + +static void goodix_remove_all_ext_modules(void) +{ + struct goodix_ext_module *ext_module, *next; + + if (!goodix_modules.initilized || !goodix_modules.core_data) + return; + + mutex_lock(&goodix_modules.mutex); + if (!list_empty(&goodix_modules.head)) { + list_for_each_entry_safe(ext_module, next, + &goodix_modules.head, list) { + list_del(&ext_module->list); + goodix_modules.count--; + if (ext_module->funcs && ext_module->funcs->exit) + ext_module->funcs->exit(goodix_modules.core_data, + ext_module); + } + } + + mutex_unlock(&goodix_modules.mutex); +} + +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; +} +EXPORT_SYMBOL_GPL(goodix_get_default_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; +} +EXPORT_SYMBOL_GPL(goodix_get_default_kobj); + +/* debug fs */ +struct debugfs_buf { + struct debugfs_blob_wrapper buf; + int pos; + struct dentry *dentry; +} goodix_dbg; + +void goodix_msg_printf(const char *fmt, ...) +{ + va_list args; + int r; + + if (!goodix_dbg.dentry) + return; + if (goodix_dbg.pos < goodix_dbg.buf.size) { + va_start(args, fmt); + r = vscnprintf(goodix_dbg.buf.data + goodix_dbg.pos, + goodix_dbg.buf.size - 1, fmt, args); + goodix_dbg.pos += r; + va_end(args); + } +} +EXPORT_SYMBOL_GPL(goodix_msg_printf); + +static int goodix_debugfs_init(void) +{ + struct dentry *r_b; + + goodix_dbg.buf.size = PAGE_SIZE; + goodix_dbg.pos = 0; + goodix_dbg.buf.data = kzalloc(goodix_dbg.buf.size, GFP_KERNEL); + if (goodix_dbg.buf.data == NULL) { + pr_err("Debugfs init failed\n"); + goto exit; + } + r_b = debugfs_create_blob("goodix_ts", 0644, NULL, &goodix_dbg.buf); + if (!r_b) { + pr_err("Debugfs create failed\n"); + return -ENOENT; + } + goodix_dbg.dentry = r_b; + +exit: + return 0; +} + +static void goodix_debugfs_exit(void) +{ + debugfs_remove(goodix_dbg.dentry); + goodix_dbg.dentry = NULL; + pr_info("Debugfs module exit\n"); +} + +/* show external module infomation */ +static ssize_t goodix_ts_extmod_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct goodix_ext_module *module, *next; + size_t offset = 0; + int r; + + mutex_lock(&goodix_modules.mutex); + if (!list_empty(&goodix_modules.head)) { + list_for_each_entry_safe(module, next, + &goodix_modules.head, list) { + r = snprintf(&buf[offset], PAGE_SIZE, + "priority:%u module:%s\n", + module->priority, module->name); + if (r < 0) { + mutex_unlock(&goodix_modules.mutex); + return -EINVAL; + } + offset += r; + } + } + + mutex_unlock(&goodix_modules.mutex); + return offset; +} + +/* show driver infomation */ +static ssize_t goodix_ts_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 goodix_ts_chip_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct goodix_ts_core *core_data = + dev_get_drvdata(dev); + struct goodix_ts_device *ts_dev = core_data->ts_dev; + struct goodix_ts_version chip_ver; + int r, cnt = 0; + + cnt += snprintf(buf, PAGE_SIZE, "TouchDeviceName:%s\n", ts_dev->name); + if (ts_dev->hw_ops->read_version) { + r = ts_dev->hw_ops->read_version(ts_dev, &chip_ver); + if (!r && chip_ver.valid) { + cnt += snprintf(&buf[cnt], PAGE_SIZE, + "PID:%s\nVID:%02x%02x%02x%02x\nSensID:%02x\n", + chip_ver.pid, chip_ver.vid[0], + chip_ver.vid[1], chip_ver.vid[2], + chip_ver.vid[3], chip_ver.sensor_id); + } + } + + 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_device *ts_dev = core_data->ts_dev; + int en; + + if (sscanf(buf, "%d", &en) != 1) + return -EINVAL; + + if (en != 1) + return -EINVAL; + + if (ts_dev->hw_ops->reset) + ts_dev->hw_ops->reset(ts_dev); + return count; + +} + +static ssize_t goodix_ts_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_device *ts_dev = core_data->ts_dev; + int ret, i, offset; + char *cfg_buf; + + cfg_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!cfg_buf) + return -ENOMEM; + + if (ts_dev->hw_ops->read_config) + ret = ts_dev->hw_ops->read_config(ts_dev, cfg_buf); + else + ret = -EINVAL; + + if (ret > 0) { + offset = 0; + for (i = 0; i < ret; i++) { + if (i != 0 && i % 20 == 0) + buf[offset++] = '\n'; + offset += snprintf(&buf[offset], PAGE_SIZE - offset, + "%02x,", cfg_buf[i]); + } + } + 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, + unsigned char *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\n", 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\n", + 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; +} + +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_device *ts_dev = core_data->ts_dev; + int en, r; + const struct firmware *cfg_img; + struct goodix_ts_config *config = NULL; + + if (sscanf(buf, "%d", &en) != 1) + return -EINVAL; + + if (en != 1) + return -EINVAL; + + disable_irq(core_data->irq); + + /*request configuration*/ + r = request_firmware(&cfg_img, GOODIX_DEFAULT_CFG_NAME, dev); + if (r < 0) { + ts_err("cfg file [%s] not available,errno:%d", + GOODIX_DEFAULT_CFG_NAME, r); + goto exit; + } else + ts_info("cfg file [%s] is ready", GOODIX_DEFAULT_CFG_NAME); + + config = kzalloc(sizeof(*config), GFP_KERNEL); + if (config == NULL) + goto exit; + + /*parse cfg data*/ + if (goodix_ts_convert_0x_data(cfg_img->data, cfg_img->size, + config->data, &config->length)) { + ts_err("convert config data FAILED"); + goto exit; + } + + config->reg_base = ts_dev->reg.cfg_addr; + mutex_init(&config->lock); + config->initialized = TS_CFG_STABLE; + + if (ts_dev->hw_ops->send_config) + ts_dev->hw_ops->send_config(ts_dev, config); + +exit: + enable_irq(core_data->irq); + kfree(config); + config = NULL; + if (cfg_img) { + release_firmware(cfg_img); + cfg_img = NULL; + } + + return count; +} + +/* 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"); + 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); + int en; + + if (sscanf(buf, "%d", &en) != 1) + return -EINVAL; + + goodix_ts_irq_enable(core_data, en); + return count; +} + +/*reg read/write */ +static u16 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) +{ + int ret; + struct goodix_ts_core *core_data = dev_get_drvdata(dev); + struct goodix_ts_device *ts_dev = core_data->ts_dev; + + if (!rw_addr || !rw_len) { + ts_err("address(0x%x) and length(%d) cann't be null\n", + 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 = ts_dev->hw_ops->read(ts_dev, rw_addr, show_buf, rw_len); + if (ret) { + ts_err("failed read addr(%x) length(%d)\n", 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_device *ts_dev = core_data->ts_dev; + char *pos = NULL, *token = NULL; + long result = 0; + int ret, i; + + if (!buf || !count) { + ts_err("invalid params\n"); + 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'\n"); + goto err_out; + } + + /* get addr */ + pos = (char *)buf; + pos += 2; + token = strsep(&pos, ":"); + if (!token) { + ts_err("invalid address info\n"); + goto err_out; + } else { + if (kstrtol(token, 16, &result)) { + ts_err("failed get addr info\n"); + goto err_out; + } + rw_addr = (u16)result; + ts_info("rw addr is 0x%x\n", rw_addr); + } + + /* get length */ + token = strsep(&pos, ":"); + if (!token) { + ts_err("invalid length info\n"); + goto err_out; + } else { + if (kstrtol(token, 0, &result)) { + ts_err("failed get length info\n"); + goto err_out; + } + rw_len = (u32)result; + ts_info("rw length info is %d\n", rw_len); + if (rw_len > sizeof(store_buf)) { + ts_err("data len > %lu\n", 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\n"); + goto err_out; + } else { + if (kstrtol(token, 16, &result)) { + ts_err("failed get data[%d] info\n", i); + goto err_out; + } + store_buf[i] = (u8)result; + ts_info("get data[%d]=0x%x\n", i, store_buf[i]); + } + } + ret = ts_dev->hw_ops->write(ts_dev, rw_addr, store_buf, rw_len); + if (ret) { + ts_err("failed write addr(%x) data %*ph\n", rw_addr, + rw_len, store_buf); + goto err_out; + } + + ts_info("%s write to addr (%x) with data %*ph\n", + "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; +} + +static DEVICE_ATTR(extmod_info, S_IRUGO, goodix_ts_extmod_show, NULL); +static DEVICE_ATTR(driver_info, S_IRUGO, goodix_ts_driver_info_show, NULL); +static DEVICE_ATTR(chip_info, S_IRUGO, goodix_ts_chip_info_show, NULL); +static DEVICE_ATTR(reset, S_IWUSR | S_IWGRP, NULL, goodix_ts_reset_store); +static DEVICE_ATTR(send_cfg, S_IWUSR | S_IWGRP, NULL, goodix_ts_send_cfg_store); +static DEVICE_ATTR(read_cfg, S_IRUGO, goodix_ts_read_cfg_show, NULL); +static DEVICE_ATTR(irq_info, S_IRUGO | S_IWUSR | S_IWGRP, + goodix_ts_irq_info_show, goodix_ts_irq_info_store); +static DEVICE_ATTR(reg_rw, S_IRUGO | S_IWUSR | S_IWGRP, + goodix_ts_reg_rw_show, goodix_ts_reg_rw_store); + +static struct attribute *sysfs_attrs[] = { + &dev_attr_extmod_info.attr, + &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_irq_info.attr, + &dev_attr_reg_rw.attr, + NULL, +}; + +static const struct attribute_group sysfs_group = { + .attrs = sysfs_attrs, +}; + + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +/****/ +static ssize_t path_show(struct device *dev, struct device_attribute *pAttr, char *pBuf) +{ + ssize_t blen; + const char *path; + struct goodix_ts_device *ts_device = + dev_get_drvdata(dev); + struct i2c_client *client = to_i2c_client(ts_device->dev); + + path = kobject_get_path(&client->dev.kobj, GFP_KERNEL); + blen = scnprintf(pBuf, PAGE_SIZE, "%s\n", path ? path : "na"); + kfree(path); + + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ts_info("*** %s() vendor = %s ***", __func__, "goodix"); + return scnprintf(buf, PAGE_SIZE, "goodix"); +} + + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_NULL +}; + +static int goodix_ts_sysfs_class(struct goodix_ts_device *core_data, bool create) +{ + int i, error = 0; + dev_t devno; + s32 ret = 0; + struct device_attribute *attrs = touchscreen_attributes; + static struct class *touchscreen_class; + static struct device *touchscreen_class_dev; + + if (create) { + ret = alloc_chrdev_region(&devno, 0, 1, GOODIX_TP_IC_TYPE); + + if (ret) { + ts_err("cant`t allocate chrdev"); + return ret; + } + ts_debug("allocate chrdev ok"); + + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + ts_err("Faild to create touchscreen class!"); + return ret; + } + + touchscreen_class_dev = device_create(touchscreen_class, NULL, + devno, core_data, GOODIX_TP_IC_TYPE); + if (IS_ERR(touchscreen_class_dev)) { + error = PTR_ERR(touchscreen_class_dev); + touchscreen_class_dev = NULL; + ts_err("Faild to create device(touchscreen_class_dev)!"); + return error; + } + + ts_info("Succeed to create device(touchscreen_class_dev)!"); + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + ret = device_create_file(touchscreen_class_dev, &attrs[i]); + if (ret < 0) + goto device_destroy; + } + } else { + if (!touchscreen_class || !touchscreen_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(touchscreen_class_dev, &attrs[i]); + + device_unregister(touchscreen_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(touchscreen_class_dev, &attrs[i]); + /*device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor));*/ + touchscreen_class_dev = NULL; + class_unregister(touchscreen_class); + ts_err("error creating touchscreen class"); + + return -ENODEV; +} +#endif + +static ssize_t goodix_sysfs_config_write(struct file *file, + struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t pos, size_t count) +{ + struct platform_device *pdev = container_of(kobj_to_dev(kobj), + struct platform_device, dev); + struct goodix_ts_core *ts_core = platform_get_drvdata(pdev); + struct goodix_ts_device *ts_dev = ts_core->ts_dev; + struct goodix_ts_config *config = NULL; + int ret; + + if (pos != 0 || count > GOODIX_CFG_MAX_SIZE) { + ts_info("pos(%d) != 0, cfg size %zu", (int)pos, count); + return -EINVAL; + } + + config = kzalloc(sizeof(struct goodix_ts_config), GFP_KERNEL); + if (config == NULL) + return -ENOMEM; + + memcpy(config->data, buf, count); + config->length = count; + config->reg_base = ts_dev->reg.cfg_addr; + mutex_init(&config->lock); + config->initialized = true; + + ret = ts_dev->hw_ops->send_config(ts_dev, config); + if (ret) { + count = -EINVAL; + ts_err("send config failed %d", ret); + } else { + ts_info("send config success"); + } + + kfree(config); + return count; +} + +static ssize_t goodix_sysfs_config_read(struct file *file, + struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t pos, size_t size) +{ + struct platform_device *pdev = container_of(kobj_to_dev(kobj), + struct platform_device, dev); + struct goodix_ts_core *ts_core = platform_get_drvdata(pdev); + struct goodix_ts_device *ts_dev = ts_core->ts_dev; + int ret; + + ts_debug("pos = %d, size = %zu", (int)pos, size); + + if (pos != 0) + return 0; + + if (ts_dev->hw_ops->read_config) + ret = ts_dev->hw_ops->read_config(ts_dev, buf); + else + ret = -EINVAL; + + ts_debug("read config ret %d", ret); + return ret; +} + +static struct bin_attribute goodix_config_bin_attr = { + .attr = { + .name = "config_bin", + .mode = S_IRUGO | S_IWUSR | S_IWGRP, + }, + .size = GOODIX_CFG_MAX_SIZE, + .read = goodix_sysfs_config_read, + .write = goodix_sysfs_config_write, +}; + +static int goodix_ts_sysfs_init(struct goodix_ts_core *core_data) +{ + int ret; + + ret = sysfs_create_bin_file(&core_data->pdev->dev.kobj, + &goodix_config_bin_attr); + if (ret) { + ts_err("failed create config bin attr"); + return ret; + } + + ret = sysfs_create_group(&core_data->pdev->dev.kobj, &sysfs_group); + if (ret) { + ts_err("failed create core sysfs group"); + sysfs_remove_bin_file(&core_data->pdev->dev.kobj, + &goodix_config_bin_attr); + return ret; + } + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + ret = goodix_ts_sysfs_class(core_data->ts_dev, true); + if (ret) { + sysfs_remove_group(&core_data->pdev->dev.kobj, &sysfs_group); + sysfs_remove_bin_file(&core_data->pdev->dev.kobj, + &goodix_config_bin_attr); + ts_err("sys class files creation failed"); + } +#endif + + return ret; +} + +static void goodix_ts_sysfs_exit(struct goodix_ts_core *core_data) +{ +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + goodix_ts_sysfs_class(core_data->ts_dev, false); +#endif + sysfs_remove_bin_file(&core_data->pdev->dev.kobj, + &goodix_config_bin_attr); + sysfs_remove_group(&core_data->pdev->dev.kobj, &sysfs_group); +} + +/* 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); +} +EXPORT_SYMBOL(goodix_ts_register_notifier); + +/** + * 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); +} +EXPORT_SYMBOL(goodix_ts_unregister_notifier); + +/** + * 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; +} +EXPORT_SYMBOL_GPL(goodix_ts_blocking_notify); + +static void goodix_ts_report_pen(struct input_dev *dev, + struct goodix_pen_data *pen_data) +{ + int i; + + if (pen_data->coords.status == TS_TOUCH) { + input_report_key(dev, BTN_TOUCH, 1); + input_report_key(dev, pen_data->coords.tool_type, 1); + } else if (pen_data->coords.status == TS_RELEASE) { + input_report_key(dev, BTN_TOUCH, 0); + input_report_key(dev, pen_data->coords.tool_type, 0); + } + if (pen_data->coords.status) { + 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); + } + /* report pen button */ + for (i = 0; i < GOODIX_MAX_PEN_KEY; i++) { + if (!pen_data->keys[i].status) + continue; + if (pen_data->keys[i].status == TS_TOUCH) + input_report_key(dev, pen_data->keys[i].code, 1); + else if (pen_data->keys[i].status == TS_RELEASE) + input_report_key(dev, pen_data->keys[i].code, 0); + } + input_sync(dev); +} + +static void goodix_ts_report_finger(struct input_dev *dev, + struct goodix_touch_data *touch_data) +{ + unsigned int touch_num = touch_data->touch_num; + static u32 pre_fin; + int i; + + /*first touch down and last touch up condition*/ + if (touch_num && !pre_fin) + input_report_key(dev, BTN_TOUCH, 1); + else if (!touch_num && pre_fin) + input_report_key(dev, BTN_TOUCH, 0); + + pre_fin = touch_num; + + for (i = 0; i < GOODIX_MAX_TOUCH; i++) { + if (!touch_data->coords[i].status) + continue; + if (touch_data->coords[i].status == TS_RELEASE) { + input_mt_slot(dev, i); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, false); + continue; + } + + 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); + } + + /* report panel key */ + for (i = 0; i < GOODIX_MAX_TP_KEY; i++) { + if (!touch_data->keys[i].status) + continue; + if (touch_data->keys[i].status == TS_TOUCH) + input_report_key(dev, touch_data->keys[i].code, 1); + else if (touch_data->keys[i].status == TS_RELEASE) + input_report_key(dev, touch_data->keys[i].code, 0); + } + input_sync(dev); +} + +/** + * goodix_ts_threadirq_func - Bottom half of interrupt + * This functions is excuted in thread context, + * sleep in this function is permit. + * + * @core_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_device *ts_dev = core_data->ts_dev; + struct goodix_ext_module *ext_module, *next; + struct goodix_ts_event *ts_event = &core_data->ts_event; + u8 irq_flag = 0; + int r; + + 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; + r = ext_module->funcs->irq_event(core_data, ext_module); + if (r == EVT_CANCEL_IRQEVT) { + mutex_unlock(&goodix_modules.mutex); + return IRQ_HANDLED; + } + } + mutex_unlock(&goodix_modules.mutex); + + /* read touch data from touch device */ + r = ts_dev->hw_ops->event_handler(ts_dev, ts_event); + if (likely(r >= 0)) { + if (ts_event->event_type == EVENT_TOUCH) { + /* report touch */ + goodix_ts_report_finger(core_data->input_dev, + &ts_event->touch_data); + } + if (ts_dev->board_data.pen_enable && + ts_event->event_type == EVENT_PEN) { + goodix_ts_report_pen(core_data->pen_dev, + &ts_event->pen_data); + } + } + + /* clean irq flag */ + irq_flag = 0; + ts_dev->hw_ops->write_trans(ts_dev, ts_dev->reg.coor, &irq_flag, 1); + + return IRQ_HANDLED; +} + +/** + * goodix_ts_init_irq - Requset interrput line from system + * @core_data: pointer to touch core data + * return: 0 ok, <0 failed + */ +int goodix_ts_irq_setup(struct goodix_ts_core *core_data) +{ + const struct goodix_ts_board_data *ts_bdata = board_data(core_data); + int r; + + /* if ts_bdata-> irq is invalid */ + if (ts_bdata->irq <= 0) + core_data->irq = gpio_to_irq(ts_bdata->irq_gpio); + else + core_data->irq = ts_bdata->irq; + + ts_info("IRQ:%u,flags:%d", core_data->irq, (int)ts_bdata->irq_flags); + r = devm_request_threaded_irq(&core_data->pdev->dev, + core_data->irq, NULL, + goodix_ts_threadirq_func, + ts_bdata->irq_flags | IRQF_ONESHOT, + GOODIX_CORE_DRIVER_NAME, + core_data); + if (r < 0) + ts_err("Failed to requeset threaded irq:%d", r); + else + atomic_set(&core_data->irq_enabled, 1); + + return r; +} + +/** + * goodix_ts_irq_enable - Enable/Disable a irq + * @core_data: pointer to touch core data + * enable: enable or disable irq + * return: 0 ok, <0 failed + */ +int goodix_ts_irq_enable(struct goodix_ts_core *core_data, + bool enable) +{ + if (enable) { + if (!atomic_cmpxchg(&core_data->irq_enabled, 0, 1)) { + enable_irq(core_data->irq); + ts_debug("Irq enabled"); + } + } else { + if (atomic_cmpxchg(&core_data->irq_enabled, 1, 0)) { + disable_irq(core_data->irq); + ts_debug("Irq disabled"); + } + } + + return 0; +} +EXPORT_SYMBOL(goodix_ts_irq_enable); + +/** + * 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; + struct device *dev = NULL; + int r = 0; + + ts_info("Power init"); + /* dev:i2c client device or spi slave device*/ + dev = core_data->ts_dev->dev; + ts_bdata = board_data(core_data); + + if (ts_bdata->avdd_name) { + core_data->avdd = devm_regulator_get(dev, + ts_bdata->avdd_name); + if (IS_ERR_OR_NULL(core_data->avdd)) { + r = PTR_ERR(core_data->avdd); + ts_err("Failed to get regulator avdd:%d", r); + core_data->avdd = NULL; + return r; + } + } else { + ts_info("Avdd name is NULL[skip]"); + } + + if (ts_bdata->iovdd_name) { + core_data->iovdd = devm_regulator_get(dev, + ts_bdata->iovdd_name); + if (IS_ERR_OR_NULL(core_data->iovdd)) { + r = PTR_ERR(core_data->iovdd); + ts_err("Failed to get regulator iovdd:%d", r); + core_data->iovdd = NULL; + } + } else { + ts_info("iovdd name is NULL"); + } + + return r; +} + +/** + * 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 *core_data) +{ + struct goodix_ts_board_data *ts_bdata = board_data(core_data); + int r = 0; + + ts_info("Device power on"); + if (core_data->power_on) + return 0; + + if (core_data->avdd) { + r = regulator_enable(core_data->avdd); + if (!r) { + ts_info("regulator enable SUCCESS"); + if (ts_bdata->power_on_delay_us) + usleep_range(ts_bdata->power_on_delay_us, + ts_bdata->power_on_delay_us); + } else { + ts_err("Failed to enable avdd:%d", r); + goto err_exit; + } + } + + if (core_data->iovdd) { + r = regulator_enable(core_data->iovdd); + if (r) { + ts_err("Failed to enable iovdd:%d", r); + goto err_exit; + } + } + + core_data->power_on = 1; +err_exit: + return r; +} + +/** + * 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 *core_data) +{ + struct goodix_ts_board_data *ts_bdata = board_data(core_data); + int r = 0; + + ts_info("Device power off"); + if (!core_data->power_on) + return 0; + + if (core_data->avdd) { + r = regulator_disable(core_data->avdd); + if (!r) { + ts_info("regulator disable SUCCESS"); + if (ts_bdata->power_off_delay_us) + usleep_range(ts_bdata->power_off_delay_us, + ts_bdata->power_off_delay_us); + } else { + ts_err("Failed to disable avdd:%d", r); + goto err_exit; + } + } + + if (core_data->iovdd) { + r = regulator_disable(core_data->iovdd); + if (r) { + ts_err("Failed to disable iovdd:%d", r); + goto err_exit; + } + } + + core_data->power_on = 0; +err_exit: + return r; +} + +#ifdef CONFIG_PINCTRL +/** + * goodix_ts_pinctrl_init - Get pinctrl handler and pinctrl_state + * @core_data: pointer to touch core data + * return: 0 ok, <0 failed + */ +static int goodix_ts_pinctrl_init(struct goodix_ts_core *core_data) +{ + int r = 0; + + /* get pinctrl handler from of node */ + core_data->pinctrl = devm_pinctrl_get(core_data->ts_dev->dev); + if (IS_ERR_OR_NULL(core_data->pinctrl)) { + ts_info("Failed to get pinctrl handler[need confirm]"); + core_data->pinctrl = NULL; + return -EINVAL; + } + ts_debug("success get pinctrl"); + /* active state */ + core_data->pin_sta_active = pinctrl_lookup_state(core_data->pinctrl, + PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(core_data->pin_sta_active)) { + r = PTR_ERR(core_data->pin_sta_active); + ts_err("Failed to get pinctrl state:%s, r:%d", + PINCTRL_STATE_ACTIVE, r); + core_data->pin_sta_active = NULL; + goto exit_pinctrl_put; + } + ts_debug("success get avtive pinctrl state"); + + /* suspend state */ + core_data->pin_sta_suspend = pinctrl_lookup_state(core_data->pinctrl, + PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(core_data->pin_sta_suspend)) { + r = PTR_ERR(core_data->pin_sta_suspend); + ts_err("Failed to get pinctrl state:%s, r:%d", + PINCTRL_STATE_SUSPEND, r); + core_data->pin_sta_suspend = NULL; + goto exit_pinctrl_put; + } + ts_debug("success get suspend pinctrl state"); + + return 0; +exit_pinctrl_put: + devm_pinctrl_put(core_data->pinctrl); + core_data->pinctrl = NULL; + return r; +} +#endif + +/** + * goodix_ts_gpio_setup - Request gpio resources from GPIO subsysten + * reset_gpio and irq_gpio number are obtained from goodix_ts_device + * which created in hardware layer driver. e.g.goodix_xx_i2c.c + * A goodix_ts_device should set those two fileds to right value + * before registed to touch core driver. + * @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_HIGH, "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; + } + + return 0; +} + +/** + * goodix_input_set_params - set input parameters + */ +static void goodix_ts_set_input_params(struct input_dev *input_dev, + struct goodix_ts_board_data *ts_bdata) +{ + int i; + + if (ts_bdata->swap_axis) + swap(ts_bdata->panel_max_x, ts_bdata->panel_max_y); + + 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); + + if (ts_bdata->panel_max_key) { + for (i = 0; i < ts_bdata->panel_max_key; i++) + input_set_capability(input_dev, EV_KEY, + ts_bdata->panel_key_map[i]); + } +} + +/** + * goodix_ts_input_dev_config - Requset and config a input device + * then register it to input sybsystem. + * NOTE that some hardware layer may provide a input device + * (ts_dev->input_dev not NULL). + * @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; + + __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); + +#ifdef INPUT_PROP_DIRECT + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); +#endif + + /* set input parameters */ + goodix_ts_set_input_params(input_dev, ts_bdata); + +#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); + + 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(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, + GOODIX_PEN_MAX_PRESSURE, 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) +{ + 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) +{ + 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 *core = container_of(ts_esd, + struct goodix_ts_core, ts_esd); + const struct goodix_ts_hw_ops *hw_ops = ts_hw_ops(core); + u8 data = GOODIX_ESD_TICK_WRITE_DATA; + int r = 0; + + if (!atomic_read(&ts_esd->esd_on)) + return; + + if (hw_ops->check_hw) + r = hw_ops->check_hw(core->ts_dev); + if (r < 0) { + goodix_ts_power_off(core); + goodix_ts_power_on(core); + if (hw_ops->reset) + hw_ops->reset(core->ts_dev); + + /*init dynamic esd*/ + r = hw_ops->write_trans(core->ts_dev, core->ts_dev->reg.esd, + &data, 1); + if (r < 0) + ts_err("failed init dynamic esd"); + } else { + /*init dynamic esd*/ + r = hw_ops->write_trans(core->ts_dev, + core->ts_dev->reg.esd, + &data, 1); + if (r < 0) + ts_err("failed init watch dog"); + } + + 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 *core) +{ + struct goodix_ts_esd *ts_esd = &core->ts_esd; + + if (core->ts_dev->reg.esd == 0) + 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 *core) +{ + struct goodix_ts_esd *ts_esd = &core->ts_esd; + int ret; + + 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_FWUPDATE_SKIP: + 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 *core) +{ + struct goodix_ts_esd *ts_esd = &core->ts_esd; + struct goodix_ts_device *dev = core->ts_dev; + u8 data = GOODIX_ESD_TICK_WRITE_DATA; + int r; + + if (!dev->hw_ops->check_hw || !dev->reg.esd) { + 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 = core; + 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); + + /*init dynamic esd*/ + r = dev->hw_ops->write_trans(core->ts_dev, core->ts_dev->reg.esd, + &data, 1); + if (r < 0) + ts_err("failed init dynamic esd[ignore]"); + + goodix_ts_esd_on(core); + + return 0; +} + +static void goodix_ts_release_connects(struct goodix_ts_core *core_data) +{ + struct input_dev *input_dev = core_data->input_dev; + struct input_mt *mt = input_dev->mt; + int i; + + if (mt) { + for (i = 0; i < mt->num_slots; 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); + } +} + +/** + * 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_device *ts_dev = core_data->ts_dev; + int r; + + ts_info("Suspend start"); + + /* + * 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; + + r = ext_module->funcs->before_suspend(core_data, + ext_module); + if (r == EVT_CANCEL_SUSPEND) { + mutex_unlock(&goodix_modules.mutex); + ts_info("Canceled by module:%s", + ext_module->name); + goto out; + } + } + } + mutex_unlock(&goodix_modules.mutex); + + /* disable irq */ + goodix_ts_irq_enable(core_data, false); + + /* let touch ic work in sleep mode */ + if (ts_dev && ts_dev->hw_ops->suspend) + ts_dev->hw_ops->suspend(ts_dev); + atomic_set(&core_data->suspended, 1); + +#ifdef CONFIG_PINCTRL + if (core_data->pinctrl) { + r = pinctrl_select_state(core_data->pinctrl, + core_data->pin_sta_suspend); + if (r < 0) + ts_err("Failed to select active pinstate, r:%d", r); + } +#endif + + /* 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; + + r = ext_module->funcs->after_suspend(core_data, + ext_module); + if (r == 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: + 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_device *ts_dev = + core_data->ts_dev; + int r; + + ts_info("Resume start"); + goodix_ts_release_connects(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->before_resume) + continue; + + r = ext_module->funcs->before_resume(core_data, + ext_module); + if (r == EVT_CANCEL_RESUME) { + mutex_unlock(&goodix_modules.mutex); + ts_info("Canceled by module:%s", + ext_module->name); + goto out; + } + } + } + mutex_unlock(&goodix_modules.mutex); + +#ifdef CONFIG_PINCTRL + if (core_data->pinctrl) { + r = pinctrl_select_state(core_data->pinctrl, + core_data->pin_sta_active); + if (r < 0) + ts_err("Failed to select active pinstate, r:%d", r); + } +#endif + + atomic_set(&core_data->suspended, 0); + /* resume device */ + if (ts_dev && ts_dev->hw_ops->resume) + ts_dev->hw_ops->resume(ts_dev); + + 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; + + r = ext_module->funcs->after_resume(core_data, + ext_module); + if (r == EVT_CANCEL_RESUME) { + mutex_unlock(&goodix_modules.mutex); + ts_info("Canceled by module:%s", + ext_module->name); + goto out; + } + } + } + mutex_unlock(&goodix_modules.mutex); + + goodix_ts_irq_enable(core_data, true); + + /* + * notify resume event, inform the esd protector + * and charger detector to turn on the work + */ + ts_info("try notify resume"); + goodix_ts_blocking_notify(NOTIFY_RESUME, NULL); +out: + ts_debug("Resume end"); + return 0; +} + +#if defined(CONFIG_DRM) +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 goodix_ts_board_data *ts_bdata = board_data(core_data); + struct msm_drm_notifier *evdata = data; + + if (!evdata || (evdata->id != ts_bdata->ctrl_dsi)) + return 0; + + ts_info("Notifier's event = %ld", event); + if ((event == MSM_DRM_EARLY_EVENT_BLANK || event == MSM_DRM_EVENT_BLANK) && + evdata && evdata->data && core_data) { + int *blank = evdata->data; + pr_debug("drm notification: event = %lu blank = %d\n", event, *blank); + if (event == MSM_DRM_EARLY_EVENT_BLANK) { + if (*blank != MSM_DRM_BLANK_POWERDOWN) + return 0; + ts_info("TP Suspend"); + goodix_ts_suspend(core_data); + } else if (*blank == MSM_DRM_BLANK_UNBLANK) { + ts_info("TP Resume"); + goodix_ts_resume(core_data); + } + } + + return 0; +} +#elif defined(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 + +#ifdef CONFIG_HAS_EARLYSUSPEND +/** + * goodix_ts_earlysuspend - Early suspend function + * Called by kernel during system suspend phrase + */ +static void goodix_ts_earlysuspend(struct early_suspend *h) +{ + struct goodix_ts_core *core_data = + container_of(h, struct goodix_ts_core, + early_suspend); + + goodix_ts_suspend(core_data); +} +/** + * goodix_ts_lateresume - Late resume function + * Called by kernel during system wakeup + */ +static void goodix_ts_lateresume(struct early_suspend *h) +{ + struct goodix_ts_core *core_data = + container_of(h, struct goodix_ts_core, + early_suspend); + + goodix_ts_resume(core_data); +} +#endif + +#ifdef CONFIG_PM +#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 *ts_core = container_of(self, + struct goodix_ts_core, ts_notifier); + struct goodix_ts_device *ts_dev = ts_device(ts_core); + const struct goodix_ts_hw_ops *hw_ops = ts_hw_ops(ts_core); + int r; + + ts_info("notify event type 0x%x", (unsigned int)action); + switch (action) { + case NOTIFY_FWUPDATE_SUCCESS: + /* send normal-cfg to firmware */ + r = ts_dev->hw_ops->send_config(ts_dev, &(ts_dev->normal_cfg)); + if (r < 0) + ts_info("failed send normal config[ignore]"); + case NOTIFY_FWUPDATE_SKIP: + case NOTIFY_FWUPDATE_FAILED: + r = hw_ops->read_version(ts_dev, &ts_dev->chip_version); + if (r < 0) + ts_info("failed read fw version info[ignore]"); + break; + default: + break; + } + + return 0; +} + +int goodix_ts_stage2_init(struct goodix_ts_core *core_data) +{ + int r; + struct goodix_ts_device *ts_dev = ts_device(core_data); + + /* send normal-cfg to firmware */ + r = ts_dev->hw_ops->send_config(ts_dev, &(ts_dev->normal_cfg)); + if (r < 0) { + ts_info("failed send normal config[ignore]"); + } + + r = ts_dev->hw_ops->read_version(ts_dev, &ts_dev->chip_version); + if (r < 0) + ts_info("failed read fw version info[ignore]"); + + /* alloc/config/register input device */ + r = goodix_ts_input_dev_config(core_data); + if (r < 0) { + ts_err("failed set input device"); + return r; + } + + if (ts_dev->board_data.pen_enable) { + r = goodix_ts_pen_dev_config(core_data); + if (r < 0) { + ts_err("failed set pen device"); + goto err_finger; + } + } + /* request irq line */ + r = goodix_ts_irq_setup(core_data); + if (r < 0) { + ts_info("failed set irq"); + goto exit; + } + ts_info("success register irq"); + +#ifdef CONFIG_DRM + core_data->fb_notifier.notifier_call = goodix_ts_fb_notifier_callback; + if (!msm_drm_register_client(&core_data->fb_notifier)) + ts_info("registered DRM notifier"); + else + ts_err("%s: Unable to register DRM notifier", + __func__); +#elif defined(CONFIG_FB) + core_data->fb_notifier.notifier_call = goodix_ts_fb_notifier_callback; + if (fb_register_client(&core_data->fb_notifier)) + ts_err("Failed to register fb notifier client:%d", r); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + core_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + core_data->early_suspend.resume = goodix_ts_lateresume; + core_data->early_suspend.suspend = goodix_ts_earlysuspend; + register_early_suspend(&core_data->early_suspend); +#endif + /*create sysfs files*/ + goodix_ts_sysfs_init(core_data); + + /* esd protector */ + goodix_ts_esd_init(core_data); + return 0; +exit: + if (ts_dev->board_data.pen_enable) { + goodix_ts_pen_dev_remove(core_data); + } +err_finger: + goodix_ts_input_dev_remove(core_data); + return r; +} + +/** + * goodix_ts_probe - called by kernel when a Goodix touch + * platform driver is added. + */ +static int goodix_ts_probe(struct platform_device *pdev) +{ + struct goodix_ts_core *core_data = NULL; + struct goodix_ts_device *ts_device; + int r; + + ts_info("goodix_ts_probe IN"); + + ts_device = pdev->dev.platform_data; + if (!ts_device || !ts_device->hw_ops) { + ts_err("Invalid touch device"); + return -ENODEV; + } + + core_data = devm_kzalloc(&pdev->dev, sizeof(struct goodix_ts_core), + GFP_KERNEL); + if (!core_data) { + ts_err("Failed to allocate memory for core data"); + return -ENOMEM; + } + + /* touch core layer is a platform driver */ + core_data->pdev = pdev; + core_data->ts_dev = ts_device; + platform_set_drvdata(pdev, core_data); + + r = goodix_ts_power_init(core_data); + if (r < 0) + goto out; + + r = goodix_ts_power_on(core_data); + if (r < 0) + goto out; + +#ifdef CONFIG_PINCTRL + /* Pinctrl handle is optional. */ + r = goodix_ts_pinctrl_init(core_data); + if (!r && core_data->pinctrl) { + r = pinctrl_select_state(core_data->pinctrl, + core_data->pin_sta_active); + if (r < 0) + ts_err("Failed to select active pinstate, r:%d", r); + } +#endif + + /* get GPIO resource */ + r = goodix_ts_gpio_setup(core_data); + if (r < 0) + goto out; + + /* confirm it's goodix touch dev or not */ + r = ts_device->hw_ops->dev_confirm(ts_device); + if (r) { + ts_err("goodix device confirm failed[skip]"); + goto out; + } + msleep(100); + + /* Try start a thread to get config-bin info */ + r = goodix_start_later_init(core_data); + if (r) { + ts_info("Failed start cfg_bin_proc"); + goto out; + } + + /* generic notifier callback */ + core_data->ts_notifier.notifier_call = goodix_generic_noti_callback; + goodix_ts_register_notifier(&core_data->ts_notifier); + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + ts_info("%s:goodix_ts_mmi_dev_register",__func__); + r = goodix_ts_mmi_dev_register(pdev); + if (r) { + ts_info("Failed register touchscreen mmi."); + goto out; + } +#endif + +out: + if (r) + core_data->initialized = 0; + else { + core_data->initialized = 1; + goodix_modules.core_data = core_data; + } + ts_info("goodix_ts_probe OUT, r:%d", r); + /* wakeup ext module register work */ + complete_all(&goodix_modules.core_comp); + return r; +} + +static int goodix_ts_remove(struct platform_device *pdev) +{ + struct goodix_ts_core *core_data = platform_get_drvdata(pdev); + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + goodix_ts_mmi_dev_unregister(pdev); +#endif + core_data->initialized = 0; + goodix_ts_unregister_notifier(&core_data->ts_notifier); + if (atomic_read(&core_data->ts_esd.esd_on)) + goodix_ts_esd_off(core_data); + goodix_remove_all_ext_modules(); + goodix_ts_power_off(core_data); + goodix_debugfs_exit(); + goodix_ts_sysfs_exit(core_data); + // can't free the memory for tools or gesture module + //kfree(core_data); + return 0; +} + +#ifdef CONFIG_PM +static const struct dev_pm_ops dev_pm_ops = { +#if !defined(CONFIG_FB) && !defined(CONFIG_DRM) && !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_DRIVER_NAME}, + {} +}; +MODULE_DEVICE_TABLE(platform, ts_core_ids); + +static struct platform_driver goodix_ts_driver = { + .driver = { + .name = GOODIX_CORE_DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &dev_pm_ops, +#endif + }, + .probe = goodix_ts_probe, + .remove = goodix_ts_remove, + .id_table = ts_core_ids, +}; + +int goodix_ts_core_init(void) +{ + ts_info("Core layer init"); + if (!goodix_modules.initilized) { + /* this may init by outer modules register event */ + ts_info("init modules struct"); + goodix_modules.initilized = true; + INIT_LIST_HEAD(&goodix_modules.head); + mutex_init(&goodix_modules.mutex); + init_completion(&goodix_modules.core_comp); + } + + goodix_debugfs_init(); + return platform_driver_register(&goodix_ts_driver); +} + +/* uninit module manually */ +int goodix_ts_core_release(struct goodix_ts_core *core_data) +{ + ts_info("goodix core module removed"); + + platform_driver_unregister(&goodix_ts_driver); + goodix_ts_dev_release(); + goodix_modules.core_data = NULL; + return 0; +} diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_core.h b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_core.h new file mode 100644 index 000000000000..e5c5378b3e64 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_core.h @@ -0,0 +1,770 @@ +/* + * Goodix Touchscreen Driver + * Core layer of touchdriver architecture. + * + * Copyright (C) 2019 - 2020 Goodix, Inc. + * Authors: Wang Yafei + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#include +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#ifdef CONFIG_DRM +#include +#elif defined(CONFIG_FB) +#include +#include +#endif + +#define GOODIX_FLASH_CONFIG_WITH_ISP 1 +/* macros definition */ +#define GOODIX_CORE_DRIVER_NAME "goodix_ts" +#define GOODIX_PEN_DRIVER_NAME "goodix_ts,pen" +#define GOODIX_DRIVER_VERSION "v1.4.3.0" +#define GOODIX_BUS_RETRY_TIMES 3 +#define GOODIX_MAX_TOUCH 10 +#define GOODIX_CFG_MAX_SIZE 4096 +#define GOODIX_ESD_TICK_WRITE_DATA 0xAA +#define GOODIX_PID_MAX_LEN 8 +#define GOODIX_VID_MAX_LEN 8 + +#define GOODIX_DEFAULT_CFG_NAME "goodix_config.cfg" +#define GOODIX_TP_IC_TYPE "GTx8" + +#define IC_TYPE_NONE 0 +#define IC_TYPE_NORMANDY 1 +#define IC_TYPE_NANJING 2 +#define IC_TYPE_YELLOWSTONE 3 + +#define GOODIX_TOUCH_EVENT 0x80 +#define GOODIX_REQUEST_EVENT 0x40 +#define GOODIX_GESTURE_EVENT 0x20 +#define GOODIX_HOTKNOT_EVENT 0x10 + +#define GOODIX_PEN_MAX_PRESSURE 4096 +#define GOODIX_MAX_TP_KEY 4 +#define GOODIX_MAX_PEN_KEY 2 +/* + * struct goodix_module - external modules container + * @head: external modules list + * @initilized: whether this struct is initilized + * @mutex: mutex lock + * @count: current number of registered external module + * @wq: workqueue to do register work + * @core_data: core_data pointer + */ +struct goodix_module { + struct list_head head; + bool initilized; + struct mutex mutex; + unsigned int count; + struct workqueue_struct *wq; + struct completion core_comp; + struct goodix_ts_core *core_data; +}; + +/* + * struct goodix_ts_board_data - board data + * @avdd_name: name of analoy regulator + * @reset_gpio: reset gpio number + * @irq_gpio: interrupt gpio number + * @irq_flag: irq trigger type + * @power_on_delay_us: power on delay time (us) + * @power_off_delay_us: power off delay time (us) + * @swap_axis: whether swaw x y axis + * @panel_max_x/y/w/p: resolution and size + * @panel_max_key: max supported keys + * @pannel_key_map: key map + * @fw_name: name of the firmware image + */ +struct goodix_ts_board_data { + const char *avdd_name; + const char *iovdd_name; + unsigned int reset_gpio; + unsigned int irq_gpio; + int irq; + int ctrl_dsi; + unsigned int irq_flags; + + unsigned int power_on_delay_us; + unsigned int power_off_delay_us; + + 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*/ + unsigned int panel_max_key; + unsigned int panel_key_map[GOODIX_MAX_TP_KEY]; + unsigned int x2x; + unsigned int y2y; + bool pen_enable; + unsigned int tp_key_num; + /*add end*/ + + const char *fw_name; + const char *cfg_bin_name; + bool esd_default_on; +}; + +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 */ +}; + +enum goodix_cfg_init_state { + TS_CFG_UNINITALIZED, + TS_CFG_STABLE, + TS_CFG_TEMP, +}; + +/* + * struct goodix_ts_config - chip config data + * @initialized: cfg init state, 0 uninit, + * 1 init with stable config, 2 init with temp config. + * @name: name of this config + * @lock: mutex + * @reg_base: register base of config data + * @length: bytes of the config + * @delay: delay time after sending config + * @data: config data buffer + */ +struct goodix_ts_config { + int initialized; + char name[24]; + struct mutex lock; + unsigned int reg_base; + unsigned int length; + unsigned int delay; /*ms*/ + unsigned char data[GOODIX_CFG_MAX_SIZE]; +}; + +/* + * struct goodix_ts_cmd - command package + * @initialized: whether initialized + * @cmd_reg: command register + * @length: command length in bytes + * @cmds: command data + */ +#pragma pack(4) +struct goodix_ts_cmd { + u32 initialized; + u32 cmd_reg; + u32 length; + u8 cmds[8]; +}; +#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), +}; + +/* requset event type */ +enum ts_request_type { + REQUEST_INVALID, + REQUEST_CONFIG, + REQUEST_BAKREF, + REQUEST_RESET, + REQUEST_MAINCLK, +}; + +/* notifier event */ +enum ts_notify_event { + NOTIFY_FWUPDATE_START, + NOTIFY_FWUPDATE_FAILED, + NOTIFY_FWUPDATE_SUCCESS, + NOTIFY_FWUPDATE_SKIP, + 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; +}; + +struct goodix_ts_key { + int status; + int code; +}; + +/* touch event data */ +struct goodix_touch_data { + int touch_num; + struct goodix_ts_coords coords[GOODIX_MAX_TOUCH]; + struct goodix_ts_key keys[GOODIX_MAX_TP_KEY]; +}; + +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; + struct goodix_touch_data touch_data; + struct goodix_pen_data pen_data; +}; + +/* + * struct goodix_ts_version - firmware version + * @valid: whether these infomation is valid + * @pid: product id string + * @vid: firmware version code + * @cid: customer id code + * @sensor_id: sendor id + */ +struct goodix_ts_version { + bool valid; + char pid[GOODIX_PID_MAX_LEN]; + char vid[GOODIX_VID_MAX_LEN]; + u8 cid; + u8 sensor_id; +}; + +struct goodix_ts_regs { + u16 cfg_send_flag; + + u16 version_base; + u8 version_len; + + u16 pid; + u8 pid_len; + + u16 vid; + u8 vid_len; + + u16 sensor_id; + u8 sensor_id_mask; + + u16 fw_mask; + u16 fw_status; + u16 cfg_addr; + u16 esd; + u16 command; + u16 coor; + u16 gesture; + u16 fw_request; + u16 proximity; +}; + +enum goodix_cfg_bin_state { + CFG_BIN_STATE_UNINIT, /* config bin uninit */ + CFG_BIN_STATE_ERROR, /* config bin encounter fatal error */ + CFG_BIN_STATE_INITIALIZED, /* config bin init success */ + CFG_BIN_STATE_TEMP, /* config bin need reparse */ +}; + +/* + * struct goodix_ts_device - ts device data + * @name: device name + * @version: reserved + * @bus_type: i2c or spi + * @ic_type: normandy or yellowstone + * @cfg_bin_state: see enum goodix_cfg_bin_state + * @fw_uptodate: set to 1 after do fw update + * @board_data: board data obtained from dts + * @normal_cfg: normal config data + * @highsense_cfg: high sense config data + * @hw_ops: hardware operations + * @chip_version: firmware version infomation + * @sleep_cmd: sleep commang + * @gesture_cmd: gesture command + * @dev: device pointer,may be a i2c or spi device + * @of_node: device node + */ +struct goodix_ts_device { + char *name; + int version; + int bus_type; + int ic_type; + int cfg_bin_state; + struct goodix_ts_regs reg; + struct goodix_ts_board_data board_data; + struct goodix_ts_config normal_cfg; + struct goodix_ts_config highsense_cfg; + const struct goodix_ts_hw_ops *hw_ops; + + struct goodix_ts_version chip_version; + struct device *dev; +}; + +/* + * struct goodix_ts_hw_ops - hardware opeartions + * @init: hardware initialization + * @reset: hardware reset + * @read: read data from touch device + * @write: write data to touch device + * @send_cmd: send command to touch device + * @send_config: send configuration data + * @read_version: read firmware version + * @event_handler: touch event handler + * @suspend: put touch device into low power mode + * @resume: put touch device into working mode + */ +struct goodix_ts_hw_ops { + + int (*init)(struct goodix_ts_device *dev); + int (*dev_confirm)(struct goodix_ts_device *ts_dev); + int (*reset)(struct goodix_ts_device *dev); + int (*read)(struct goodix_ts_device *dev, unsigned int addr, + unsigned char *data, unsigned int len); + int (*write)(struct goodix_ts_device *dev, unsigned int addr, + unsigned char *data, unsigned int len); + int (*read_trans)(struct goodix_ts_device *dev, unsigned int addr, + unsigned char *data, unsigned int len); + int (*write_trans)(struct goodix_ts_device *dev, unsigned int addr, + unsigned char *data, unsigned int len); + int (*send_cmd)(struct goodix_ts_device *dev, struct goodix_ts_cmd *cmd); + int (*send_config)(struct goodix_ts_device *dev, + struct goodix_ts_config *config); + int (*read_config)(struct goodix_ts_device *dev, u8 *config_data); + int (*read_version)(struct goodix_ts_device *dev, + struct goodix_ts_version *version); + int (*event_handler)(struct goodix_ts_device *dev, + struct goodix_ts_event *ts_event); + int (*check_hw)(struct goodix_ts_device *dev); + int (*suspend)(struct goodix_ts_device *dev); + int (*resume)(struct goodix_ts_device *dev); +}; + +/* + * 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 { + struct delayed_work esd_work; + struct notifier_block esd_notifier; + struct goodix_ts_core *ts_core; + atomic_t esd_on; +}; + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +struct goodix_ts_mmi { + struct notifier_block mmi_notifier; + int update_status; + int need_reflash; +}; +#endif + +/* + * struct godix_ts_core - core layer data struct + * @initialized: indicate core state, 1 ok, 0 bad + * @pdev: core layer platform device + * @ts_dev: hardware layer touch device + * @input_dev: input device + * @avdd: analog regulator + * @pinctrl: pinctrl handler + * @pin_sta_active: active/normal pin state + * @pin_sta_suspend: suspend/sleep pin state + * @ts_event: touch event data struct + * @power_on: power on/off flag + * @irq: irq number + * @irq_enabled: irq enabled/disabled flag + * @suspended: suspend/resume flag + * @ts_notifier: generic notifier + * @ts_esd: esd protector structure + * @fb_notifier: framebuffer notifier + * @early_suspend: early suspend + */ +struct goodix_ts_core { + int initialized; + struct platform_device *pdev; + struct goodix_ts_device *ts_dev; + struct input_dev *input_dev; + struct input_dev *pen_dev; + + struct regulator *avdd; + struct regulator *iovdd; +#ifdef CONFIG_PINCTRL + struct pinctrl *pinctrl; + struct pinctrl_state *pin_sta_active; + struct pinctrl_state *pin_sta_suspend; +#endif + struct goodix_ts_event ts_event; + int power_on; + int irq; + size_t irq_trig_cnt; + + atomic_t irq_enabled; + atomic_t suspended; + + struct notifier_block ts_notifier; + struct goodix_ts_esd ts_esd; + +#ifdef CONFIG_FB + struct notifier_block fb_notifier; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif + + int (*do_fw_update)(int mode); + + int (*set_fw_name)(char* fw_name); +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + struct goodix_ts_mmi ts_mmi_info; +#endif +}; + +/* 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, +}; + +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 *, char *); + ssize_t (*store)(struct goodix_ext_module *, const char *, size_t); +}; + +/* 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); + +/* + * get board data pointer + */ +static inline struct goodix_ts_board_data *board_data( + struct goodix_ts_core *core) +{ + if (!core || !core->ts_dev) + return NULL; + return &(core->ts_dev->board_data); +} + +/* + * get touch device pointer + */ +static inline struct goodix_ts_device *ts_device( + struct goodix_ts_core *core) +{ + if (!core) + return NULL; + return core->ts_dev; +} + +/* + * get touch hardware operations pointer + */ +static inline const struct goodix_ts_hw_ops *ts_hw_ops( + struct goodix_ts_core *core) +{ + if (!core || !core->ts_dev) + return NULL; + return core->ts_dev->hw_ops; +} + +/* + * checksum helper functions + * checksum can be u8/le16/be16/le32/be32 format + * NOTE: the caller shoule be responsible for the + * legality of @data and @size parameters, so be + * careful when call these functions. + */ +static inline u8 checksum_u8(u8 *data, u32 size) +{ + u8 checksum = 0; + u32 i; + + for (i = 0; i < size; i++) + checksum += data[i]; + return checksum; +} + +/* cal u8 data checksum for yellowston */ +static inline u16 checksum_u8_ys(u8 *data, u32 size) +{ + u16 checksum = 0; + u32 i; + + for (i = 0; i < size - 2; i++) + checksum += data[i]; + return checksum - (data[size - 2] << 8 | data[size - 1]); +} + +static inline u16 checksum_le16(u8 *data, u32 size) +{ + u16 checksum = 0; + u32 i; + + for (i = 0; i < size; i += 2) + checksum += le16_to_cpup((__le16 *)(data + i)); + return checksum; +} + +static inline u16 checksum_be16(u8 *data, u32 size) +{ + u16 checksum = 0; + u32 i; + + for (i = 0; i < size; i += 2) + checksum += be16_to_cpup((__be16 *)(data + i)); + return checksum; +} + +static inline u32 checksum_le32(u8 *data, u32 size) +{ + u32 checksum = 0; + u32 i; + + for (i = 0; i < size; i += 4) + checksum += le32_to_cpup((__le32 *)(data + i)); + return checksum; +} + +static inline u32 checksum_be32(u8 *data, u32 size) +{ + u32 checksum = 0; + u32 i; + + for (i = 0; i < size; i += 4) + checksum += be32_to_cpup((__be32 *)(data + i)); + return checksum; +} + +/* + * define event action + * EVT_xxx macros are used in opeartions callback + * defined in @goodix_ext_module_funcs to control + * the behaviors of event such as suspend/resume/irq_event. + * generally there are two types of behaviors: + * 1. you want the flow of this event be canceled, + * in this condition, you should return EVT_CANCEL_XXX in + * the operations callback. + * e.g. the firmware update module is updating + * the firmware, you want to cancel suspend flow, + * so you need to return EVT_CANCEL_SUSPEND in + * suspend callback function. + * 2. you want the flow of this event continue, in + * this condition, you should return EVT_HANDLED in + * the callback function. + * */ +#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 + +/* + * errno define + * Note: + * 1. bus read/write functions defined in hardware + * layer code(e.g. goodix_xxx_i2c.c) *must* return + * -EBUS if failed to transfer data on bus. + */ +#define EBUS 1000 +#define ETIMEOUT 1001 +#define ECHKSUM 1002 +#define EMEMCMP 1003 + +//#define CONFIG_GOODIX_DEBUG +/* log macro */ +#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 boot_log(fmt, arg...) g_info(fmt, ##arg) +#ifdef CONFIG_GOODIX_DEBUG +#define ts_debug(fmt, arg...) pr_info("[GTP-DBG][%s:%d] "fmt"\n", __func__, __LINE__, ##arg) +#else +#define ts_debug(fmt, arg...) do {} while (0) +#endif + +/** + * 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); + +/** + * 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); + +/** + * goodix_ts_irq_enable - Enable/Disable a irq + + * @core_data: pointer to touch core data + * enable: enable or disable irq + * return: 0 ok, <0 failed + */ +int goodix_ts_irq_enable(struct goodix_ts_core *core_data, bool enable); + +struct kobj_type *goodix_get_default_ktype(void); + +/** + * 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); + + +/** + * * 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 *core_data); + +/** + * 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 *core_data); + +int goodix_ts_irq_setup(struct goodix_ts_core *core_data); + +int goodix_ts_esd_init(struct goodix_ts_core *core); + +int goodix_ts_register_notifier(struct notifier_block *nb); + +int goodix_ts_fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); + +void goodix_msg_printf(const char *fmt, ...); + +#endif diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_i2c.c b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_i2c.c new file mode 100644 index 000000000000..70df218968ff --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_i2c.c @@ -0,0 +1,1975 @@ +/* + * Goodix I2C Module + * Hardware interface layer of touchdriver architecture. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "goodix_ts_core.h" +#include "goodix_cfg_bin.h" + +#define TS_DRIVER_NAME "gtx8" +#define I2C_MAX_TRANSFER_SIZE 256 +#define TS_ADDR_LENGTH 2 +#define TS_DOZE_ENABLE_RETRY_TIMES 3 +#define TS_DOZE_DISABLE_RETRY_TIMES 9 +#define TS_WAIT_CFG_READY_RETRY_TIMES 30 +#define TS_WAIT_CMD_FREE_RETRY_TIMES 10 + +#define TS_REG_COORDS_BASE 0x824E +#define TS_REG_CMD 0x8040 +#define TS_REG_REQUEST 0x8044 +#define TS_REG_VERSION 0x8240 +#define TS_REG_CFG_BASE 0x8050 +#define TS_REG_DOZE_CTRL 0x30F0 +#define TS_REG_DOZE_STAT 0x3100 +#define TS_REG_ESD_TICK_R 0x3103 + +#define CFG_XMAX_OFFSET (0x8052 - 0x8050) +#define CFG_YMAX_OFFSET (0x8054 - 0x8050) + +#define REQUEST_HANDLED 0x00 +#define REQUEST_CONFIG 0x01 +#define REQUEST_BAKREF 0x02 +#define REQUEST_RESET 0x03 +#define REQUEST_RELOADFW 0x05 +#define REQUEST_IDLE 0xff + +#define COMMAND_SLEEP 0x05 +#define COMMAND_CLOSE_HID 0xaa +#define COMMAND_START_SEND_CFG 0x80 +#define COMMAND_END_SEND_CFG 0x83 +#define COMMAND_SEND_SMALL_CFG 0x81 +#define COMMAND_SEND_CFG_PREPARE_OK 0x82 +#define COMMAND_START_READ_CFG 0x86 +#define COMMAND_READ_CFG_PREPARE_OK 0x85 +#define COMMAND_END_SEND_CFG_YS 0x7D + +#define BYTES_PER_COORD 8 +#define TS_MAX_SENSORID 5 +#define TS_CFG_HEAD_LEN 4 +#define TS_CFG_HEAD_LEN_YS 5 +#define TS_CFG_BAG_NUM_INDEX 2 +#define TS_CFG_BAG_START_INDEX 4 + +#define TS_DOZE_DISABLE_DATA 0xAA +#define TS_DOZE_CLOSE_OK_DATA 0xBB +#define TS_DOZE_ENABLE_DATA 0xCC +#define TS_CMD_REG_READY 0xFF +#define TS_CMD_CFG_ERR 0x7E +#define TS_CMD_CFG_OK 0x7F + +enum TS_SEND_CFG_REPLY { + TS_CFG_REPLY_PKGS_ERR = 0x01, + TS_CFG_REPLY_CHKSUM_ERR = 0x02, + TS_CFG_REPLY_DATA_ERR = 0x03, + TS_CFG_REPLY_DATA_EQU = 0x07, +}; + +#define IRQ_HEAD_LEN_YS 8 +#define IRQ_HEAD_LEN_NOR 2 + + +static struct platform_device *goodix_pdev; +int goodix_ts_core_init(void); +#ifdef 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 r, err; + + r = of_property_read_u32(node, "goodix,panel-max-x", + &board_data->panel_max_x); + if (r) + err = -ENOENT; + + r = of_property_read_u32(node, "goodix,panel-max-y", + &board_data->panel_max_y); + if (r) + err = -ENOENT; + + r = of_property_read_u32(node, "goodix,panel-max-w", + &board_data->panel_max_w); + if (r) + err = -ENOENT; + + board_data->swap_axis = of_property_read_bool(node, + "goodix,swap-axis"); + board_data->x2x = of_property_read_bool(node, "goodix,x2x"); + board_data->y2y = of_property_read_bool(node, "goodix,y2y"); + + return 0; +} + +static const char *goodix_parse_dt_text(struct device_node *node, + const char *name) +{ + const char *prop = NULL; + int r; + + r = of_property_read_string(node, name, &prop); + if (r) { + ts_err("%s: error reading property = %d", name, r); + } + return prop; +} + +/** + * 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) +{ + struct property *prop; + int r; + + if (!board_data) { + ts_err("invalid board data"); + return -EINVAL; + } + + 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; + } + + board_data->cfg_bin_name = goodix_parse_dt_text(node, + "goodix,cfg_bin_name"); + board_data->fw_name = goodix_parse_dt_text(node, + "goodix,fw_name"); + board_data->avdd_name = goodix_parse_dt_text(node, + "goodix,avdd-name"); + board_data->iovdd_name = goodix_parse_dt_text(node, + "goodix,iovdd-name"); + + r = of_property_read_u32(node, "goodix,power-on-delay-us", + &board_data->power_on_delay_us); + if (!r) { + /* 1000ms is too large, maybe you have pass a wrong value */ + if (board_data->power_on_delay_us > 1000 * 1000) { + ts_err("Power on delay time exceed 1s, please check"); + board_data->power_on_delay_us = 0; + } + } + + r = of_property_read_u32(node, "goodix,power-off-delay-us", + &board_data->power_off_delay_us); + if (!r) { + /* 1000ms is too large, maybe you have pass */ + if (board_data->power_off_delay_us > 1000 * 1000) { + ts_err("Power off delay time exceed 1s, please check"); + board_data->power_off_delay_us = 0; + } + } + + /* get xyz resolutions */ + r = goodix_parse_dt_resolution(node, board_data); + if (r < 0) { + ts_err("Failed to parse resolutions:%d", r); + return r; + } + + r = of_property_read_u32(node, "goodix,control-dsi", + &board_data->ctrl_dsi); + if (!r) + ts_info("control DSI %d", board_data->ctrl_dsi); + + /* key map */ + prop = of_find_property(node, "goodix,panel-key-map", NULL); + if (prop && prop->length) { + if (prop->length / sizeof(u32) > GOODIX_MAX_TP_KEY) { + ts_err("Size of panel-key-map is invalid"); + return r; + } + + board_data->panel_max_key = prop->length / sizeof(u32); + board_data->tp_key_num = prop->length / sizeof(u32); + r = of_property_read_u32_array(node, + "goodix,panel-key-map", + &board_data->panel_key_map[0], + board_data->panel_max_key); + if (r) { + ts_err("failed get key map, %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_info("***key:%d, %d, %d, %d", + board_data->panel_key_map[0], board_data->panel_key_map[1], + board_data->panel_key_map[2], board_data->panel_key_map[3]); + + 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 + +int goodix_i2c_test(struct goodix_ts_device *dev) +{ +#define TEST_ADDR 0x4100 +#define TEST_LEN 1 + struct i2c_client *client = to_i2c_client(dev->dev); + unsigned char test_buf[TEST_LEN + 1], addr_buf[2]; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = !I2C_M_RD, + .buf = &addr_buf[0], + .len = TS_ADDR_LENGTH, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .buf = &test_buf[0], + .len = TEST_LEN, + } + }; + + msgs[0].buf[0] = (TEST_ADDR >> 8) & 0xFF; + msgs[0].buf[1] = TEST_ADDR & 0xFF; + + if (likely(i2c_transfer(client->adapter, msgs, 2) == 2)) + return 0; + + /* test failed */ + return -EINVAL; +} + +/* confirm current device is goodix or not. + * If confirmed 0 will return. + */ +static int goodix_ts_dev_confirm(struct goodix_ts_device *ts_dev) +{ +#define DEV_CONFIRM_RETRY 3 + int retry; + + for (retry = 0; retry < DEV_CONFIRM_RETRY; retry++) { + gpio_direction_output(ts_dev->board_data.reset_gpio, 0); + udelay(2000); + gpio_direction_output(ts_dev->board_data.reset_gpio, 1); + mdelay(5); + if (!goodix_i2c_test(ts_dev)) { + msleep(95); + return 0; + } + } + return -EINVAL; +} + +/** + * goodix_i2c_read_trans - read device register through i2c bus + * @dev: pointer to device data + * @addr: register address + * @data: read buffer + * @len: bytes to read + * return: 0 - read ok, < 0 - i2c transter error + */ +int goodix_i2c_read_trans(struct goodix_ts_device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + struct i2c_client *client = to_i2c_client(dev->dev); + unsigned int transfer_length = 0; + unsigned int pos = 0, address = reg; + unsigned char get_buf[64], addr_buf[2]; + int retry, r = 0; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = !I2C_M_RD, + .buf = &addr_buf[0], + .len = TS_ADDR_LENGTH, + }, { + .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(I2C_MAX_TRANSFER_SIZE < len + ? I2C_MAX_TRANSFER_SIZE : 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 >> 8) & 0xFF; + msgs[0].buf[1] = 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); + msleep(20); + } + if (unlikely(retry == GOODIX_BUS_RETRY_TIMES)) { + ts_err("I2c read failed,dev:%02x,reg:%04x,size:%u", + client->addr, reg, len); + r = -EBUS; + goto read_exit; + } + } + +read_exit: + if (unlikely(len >= sizeof(get_buf))) + kfree(msgs[1].buf); + return r; +} + +/** + * goodix_i2c_write_trans - write device register through i2c bus + * @dev: pointer to device data + * @addr: register address + * @data: write buffer + * @len: bytes to write + * return: 0 - write ok; < 0 - i2c transter error. + */ +int goodix_i2c_write_trans(struct goodix_ts_device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + struct i2c_client *client = to_i2c_client(dev->dev); + unsigned int pos = 0, transfer_length = 0; + unsigned int address = reg; + unsigned char put_buf[64]; + int retry, r = 0; + struct i2c_msg msg = { + .addr = client->addr, + .flags = !I2C_M_RD, + }; + + if (likely(len + TS_ADDR_LENGTH < sizeof(put_buf))) { + /* code optimize,use stack memory*/ + msg.buf = &put_buf[0]; + } else { + msg.buf = kmalloc(I2C_MAX_TRANSFER_SIZE < len + TS_ADDR_LENGTH + ? I2C_MAX_TRANSFER_SIZE : len + TS_ADDR_LENGTH, + GFP_KERNEL); + if (msg.buf == NULL) + return -ENOMEM; + } + + while (pos != len) { + if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE - TS_ADDR_LENGTH)) + transfer_length = I2C_MAX_TRANSFER_SIZE - TS_ADDR_LENGTH; + else + transfer_length = len - pos; + + msg.buf[0] = (unsigned char)((address >> 8) & 0xFF); + msg.buf[1] = (unsigned char)(address & 0xFF); + msg.len = transfer_length + 2; + memcpy(&msg.buf[2], &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 = -EBUS; + goto write_exit; + } + } + +write_exit: + if (likely(len + TS_ADDR_LENGTH >= sizeof(put_buf))) + kfree(msg.buf); + return r; +} + + +/** + * goodix_set_i2c_doze_mode - disable or enable doze mode + * @dev: pointer to device data + * @enable: true/flase + * return: 0 - ok; < 0 - error. + * This func must be used in pairs, when you disable doze + * mode, then you must enable it again. + * Between set_doze_false and set_doze_true, do not reset + * IC! + */ +static int goodix_set_i2c_doze_mode(struct goodix_ts_device *dev, int enable) +{ + static DEFINE_MUTEX(doze_mode_lock); + static int doze_mode_set_count; + int result = -EINVAL; + int i; + u8 w_data, r_data; + + if (dev->ic_type != IC_TYPE_NORMANDY) + return 0; + + mutex_lock(&doze_mode_lock); + + if (enable) { + if (doze_mode_set_count != 0) + doze_mode_set_count--; + + /*when count equal 0, allow ic enter doze mode*/ + if (doze_mode_set_count == 0) { + w_data = TS_DOZE_ENABLE_DATA; + for (i = 0; i < TS_DOZE_ENABLE_RETRY_TIMES; i++) { + result = goodix_i2c_write_trans(dev, + TS_REG_DOZE_CTRL, &w_data, 1); + if (!result) { + result = 0; + goto exit; + } + usleep_range(1000, 1100); + } + if (i >= TS_DOZE_ENABLE_RETRY_TIMES) + ts_err("i2c doze mode enable failed"); + } else { + /*ts_info("doze count not euqal 0, + * so skip doze mode enable"); + */ + result = 0; + goto exit; + } + } else { + doze_mode_set_count++; + + if (doze_mode_set_count == 1) { + w_data = TS_DOZE_DISABLE_DATA; + goodix_i2c_write_trans(dev, TS_REG_DOZE_CTRL, + &w_data, 1); + usleep_range(1000, 1100); + for (i = 0; i < TS_DOZE_DISABLE_RETRY_TIMES; i++) { + goodix_i2c_read_trans(dev, + TS_REG_DOZE_STAT, &r_data, 1); + if (TS_DOZE_CLOSE_OK_DATA == r_data) { + result = 0; + goto exit; + } else if (0xAA != r_data) { + w_data = TS_DOZE_DISABLE_DATA; + goodix_i2c_write_trans(dev, + TS_REG_DOZE_CTRL, &w_data, 1); + } + usleep_range(10000, 10100); + } + ts_err("doze mode disable FAILED"); + } else { + result = 0; + goto exit; + } + } + +exit: + mutex_unlock(&doze_mode_lock); + return result; +} + +/** + * goodix_i2c_write - write device register through i2c bus + * @dev: pointer to device data + * @addr: register address + * @data: write buffer + * @len: bytes to write + * return: 0 - write ok; < 0 - i2c transter error. + */ +int goodix_i2c_write(struct goodix_ts_device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + int r = -EINVAL; + + if (goodix_set_i2c_doze_mode(dev, false)) { + ts_err(" faild disable doze, i2c write:0x%04x", reg); + goto exit; + } + r = goodix_i2c_write_trans(dev, reg, data, len); + +exit: + if (goodix_set_i2c_doze_mode(dev, true)) + ts_err("failed enable doze write:0x%04x", reg); + + return r; +} + +/** + * goodix_i2c_read - read device register through i2c bus + * @dev: pointer to device data + * @addr: register address + * @data: read buffer + * @len: bytes to read + * return: 0 - read ok, < 0 - i2c transter error + */ +int goodix_i2c_read(struct goodix_ts_device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + int r = -EINVAL; + + if (goodix_set_i2c_doze_mode(dev, false)) { + ts_err("failed disable doze:0x%04x", reg); + goto exit; + } + r = goodix_i2c_read_trans(dev, reg, data, len); + +exit: + if (goodix_set_i2c_doze_mode(dev, true)) + ts_err("failed enable doze :0x%04x", reg); + + return r; +} + +/** + * goodix_i2c_write_trans_once + * write device register through i2c bus, no retry + * @dev: pointer to device data + * @addr: register address + * @data: write buffer + * @len: bytes to write + * return: 0 - write ok; < 0 - i2c transter error. + */ +int goodix_i2c_write_trans_once(struct goodix_ts_device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + struct i2c_client *client = to_i2c_client(dev->dev); + unsigned int pos = 0, transfer_length = 0; + unsigned int address = reg; + unsigned char put_buf[64]; + struct i2c_msg msg = { + .addr = client->addr, + .flags = !I2C_M_RD, + }; + + if (likely(len + TS_ADDR_LENGTH < sizeof(put_buf))) { + /* code optimize,use stack memory*/ + msg.buf = &put_buf[0]; + } else { + msg.buf = kmalloc(I2C_MAX_TRANSFER_SIZE < len + TS_ADDR_LENGTH + ? I2C_MAX_TRANSFER_SIZE : len + TS_ADDR_LENGTH, + GFP_KERNEL); + if (msg.buf == NULL) { + ts_err("Malloc failed"); + return -ENOMEM; + } + } + + while (pos != len) { + if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE - TS_ADDR_LENGTH)) + transfer_length = I2C_MAX_TRANSFER_SIZE - TS_ADDR_LENGTH; + else + transfer_length = len - pos; + + msg.buf[0] = (unsigned char)((address >> 8) & 0xFF); + msg.buf[1] = (unsigned char)(address & 0xFF); + msg.len = transfer_length + 2; + memcpy(&msg.buf[2], &data[pos], transfer_length); + + i2c_transfer(client->adapter, &msg, 1); + pos += transfer_length; + address += transfer_length; + } + + if (likely(len + TS_ADDR_LENGTH >= sizeof(put_buf))) + kfree(msg.buf); + return 0; +} + +static void goodix_cmd_init(struct goodix_ts_device *dev, + struct goodix_ts_cmd *ts_cmd, + u8 cmds, u16 cmd_data, u32 reg_addr) +{ + u16 checksum = 0; + ts_cmd->initialized = false; + if (!reg_addr || !cmds) + return; + + if (dev->ic_type == IC_TYPE_YELLOWSTONE) { + ts_cmd->cmd_reg = reg_addr; + ts_cmd->length = 5; + ts_cmd->cmds[0] = cmds; + ts_cmd->cmds[1] = (cmd_data >> 8) & 0xFF; + ts_cmd->cmds[2] = cmd_data & 0xFF; + checksum = ts_cmd->cmds[0] + ts_cmd->cmds[1] + + ts_cmd->cmds[2]; + ts_cmd->cmds[3] = (checksum >> 8) & 0xFF; + ts_cmd->cmds[4] = checksum & 0xFF; + ts_cmd->initialized = true; + } else if (dev->ic_type == IC_TYPE_NORMANDY) { + ts_cmd->cmd_reg = reg_addr; + ts_cmd->length = 3; + ts_cmd->cmds[0] = cmds; + ts_cmd->cmds[1] = cmd_data & 0xFF; + ts_cmd->cmds[2] = 0 - cmds - cmd_data; + ts_cmd->initialized = true; + } else { + ts_err("unsupported ic type"); + } +} + +/** + * goodix_send_command - seng cmd to firmware + * + * @dev: pointer to device + * @cmd: pointer to command struct which cotain command data + * Returns 0 - succeed,<0 - failed + */ +int goodix_send_command(struct goodix_ts_device *dev, + struct goodix_ts_cmd *cmd) +{ + int ret; + + if (!cmd || !cmd->initialized) + return -EINVAL; + + ret = goodix_i2c_write(dev, cmd->cmd_reg, cmd->cmds, cmd->length); + + return ret; +} + +static int goodix_read_version(struct goodix_ts_device *dev, + struct goodix_ts_version *version) +{ + u8 buffer[GOODIX_PID_MAX_LEN + 1]; + u8 temp_buf[256]; + u16 checksum = 0; + u8 pid_read_len = dev->reg.pid_len; + u8 vid_read_len = dev->reg.vid_len; + u8 sensor_id_mask = dev->reg.sensor_id_mask; + int r; + + if (!version) { + ts_err("pointer of version is NULL"); + return -EINVAL; + } + version->valid = false; + + /*check reg info valid*/ + if (!dev->reg.pid || !dev->reg.sensor_id || !dev->reg.vid) { + ts_err("reg is NULL, pid:0x%04x, vid:0x%04x, sensor_id:0x%04x", + dev->reg.pid, dev->reg.vid, dev->reg.sensor_id); + return -EINVAL; + } + if (!pid_read_len || pid_read_len > GOODIX_PID_MAX_LEN || + !vid_read_len || vid_read_len > GOODIX_VID_MAX_LEN) { + ts_err("invalied pid vid length, pid_len:%d, vid_len:%d", + pid_read_len, vid_read_len); + return -EINVAL; + } + + /*disable doze mode, just valid for normandy + * this func must be used in pairs + */ + if (goodix_set_i2c_doze_mode(dev, false)) { + ts_err("failed disable doze"); + r = -EINVAL; + goto exit; + } + + /*check checksum*/ + if (dev->reg.version_base) { + r = goodix_i2c_read(dev, dev->reg.version_base, + temp_buf, dev->reg.version_len); + if (r < 0) { + ts_err("Read version base failed, reg:0x%02x, len:%d", + dev->reg.version_base, dev->reg.version_len); + if (version) + version->valid = false; + goto exit; + } + + if (dev->ic_type == IC_TYPE_YELLOWSTONE) + checksum = checksum_u8_ys(temp_buf, dev->reg.version_len); + else + checksum = checksum_u8(temp_buf, dev->reg.version_len); + if (checksum) { + ts_err("checksum error:0x%02x, base:0x%02x, len:%d", + checksum, dev->reg.version_base, + dev->reg.version_len); + ts_err("%*ph", (int)(dev->reg.version_len / 2), + temp_buf); + ts_err("%*ph", (int)(dev->reg.version_len - + dev->reg.version_len / 2), + &temp_buf[dev->reg.version_len / 2]); + + if (version) + version->valid = false; + r = -EINVAL; + goto exit; + } + } + + /*read pid*/ + memset(buffer, 0, sizeof(buffer)); + memset(version->pid, 0, sizeof(version->pid)); + r = goodix_i2c_read(dev, dev->reg.pid, buffer, pid_read_len); + if (r < 0) { + ts_err("Read pid failed"); + if (version) + version->valid = false; + goto exit; + } + + /* check pid is digit or not, current we only support digital pid */ + if (!isdigit(buffer[0]) || !isdigit(buffer[1])) { + ts_err("pid not digit: 0x%x,0x%x", buffer[0], buffer[1]); + r = -EINVAL; + goto exit; + } + + memcpy(version->pid, buffer, pid_read_len); + + /*read vid*/ + memset(buffer, 0, sizeof(buffer)); + memset(version->vid, 0, sizeof(version->vid)); + r = goodix_i2c_read(dev, dev->reg.vid, buffer, vid_read_len); + if (r < 0) { + ts_err("Read vid failed"); + if (version) + version->valid = false; + goto exit; + } + memcpy(version->vid, buffer, vid_read_len); + + /*read sensor_id*/ + memset(buffer, 0, sizeof(buffer)); + r = goodix_i2c_read(dev, dev->reg.sensor_id, buffer, 1); + if (r < 0) { + ts_err("Read sensor_id failed"); + if (version) + version->valid = false; + goto exit; + } + if (sensor_id_mask != 0) { + version->sensor_id = buffer[0] & sensor_id_mask; + ts_info("sensor_id_mask:0x%02x, sensor_id:0x%02x", + sensor_id_mask, version->sensor_id); + } else { + version->sensor_id = buffer[0]; + } + + version->valid = true; + + ts_info("PID:%s,SensorID:%d, VID:%*ph", version->pid, + version->sensor_id, (int)sizeof(version->vid), version->vid); +exit: + /*enable doze mode, just valid for normandy + * this func must be used in pairs + */ + goodix_set_i2c_doze_mode(dev, true); + + return r; +} + +static int goodix_wait_cfg_cmd_ready(struct goodix_ts_device *dev, + u8 right_cmd, u8 send_cmd) +{ + int try_times = 0; + u8 cmd_flag = 0; + u8 cmd_buf[3] = {0}; + u16 command_reg = dev->reg.command; + struct goodix_ts_cmd ts_cmd; + + goodix_cmd_init(dev, &ts_cmd, send_cmd, 0, command_reg); + + for (try_times = 0; try_times < TS_WAIT_CFG_READY_RETRY_TIMES; + try_times++) { + if (goodix_i2c_read(dev, command_reg, cmd_buf, 3)) { + ts_err("Read cmd_reg error"); + return -EINVAL; + } + cmd_flag = cmd_buf[0]; + if (cmd_flag == right_cmd) { + return 0; + } else if (cmd_flag != send_cmd) { + ts_err("failed cmd_reg:0x%X, 0x%X, 0x%X", + cmd_buf[0], cmd_buf[1], cmd_buf[2]); + if (goodix_send_command(dev, &ts_cmd)) { + ts_err("Resend cmd 0x%02X FAILED", send_cmd); + return -EINVAL; + } + } + usleep_range(10000, 11000); + } + + return -EINVAL; +} + +static int _do_goodix_send_config(struct goodix_ts_device *dev, + struct goodix_ts_config *config) +{ + int r = 0; + int try_times = 0; + u8 buf[3] = {0}; + u16 command_reg = dev->reg.command; + u16 cfg_reg = dev->reg.cfg_addr; + struct goodix_ts_cmd ts_cmd; + + /*1. Inquire command_reg until it's free*/ + for (try_times = 0; try_times < TS_WAIT_CMD_FREE_RETRY_TIMES; + try_times++) { + if (!goodix_i2c_read(dev, command_reg, buf, 1) && + buf[0] == TS_CMD_REG_READY) + break; + usleep_range(10000, 11000); + } + if (try_times >= TS_WAIT_CMD_FREE_RETRY_TIMES) { + ts_err("failed send cfg, reg:0x%04x is not 0xff", + command_reg); + r = -EINVAL; + goto exit; + } + + /*2. send "start write cfg" command*/ + goodix_cmd_init(dev, &ts_cmd, COMMAND_START_SEND_CFG, + 0, dev->reg.command); + if (goodix_send_command(dev, &ts_cmd)) { + ts_err("failed send cfg, COMMAND_START_SEND_CFG ERROR"); + r = -EINVAL; + goto exit; + } + + /*3. wait ic set command_reg to 0x82*/ + if (goodix_wait_cfg_cmd_ready(dev, COMMAND_SEND_CFG_PREPARE_OK, + COMMAND_START_SEND_CFG)) { + ts_err("failed send cfg, reg:0x%04x is not 0x82", + command_reg); + r = -EINVAL; + goto exit; + } + + /*4. write cfg*/ + if (goodix_i2c_write(dev, cfg_reg, config->data, config->length)) { + ts_err("Send cfg FAILED, write cfg to fw ERROR"); + r = -EINVAL; + goto exit; + } + + /*5. send "end send cfg" command*/ + goodix_cmd_init(dev, &ts_cmd, COMMAND_END_SEND_CFG, + 0, dev->reg.command); + if (goodix_send_command(dev, &ts_cmd)) { + ts_err("failed send cfg, COMMAND_END_SEND_CFG ERROR"); + r = -EINVAL; + goto exit; + } + + if (dev->ic_type == IC_TYPE_YELLOWSTONE) { + /*6. wait 0x7f or 0x7e */ + for (try_times = 0; try_times < TS_WAIT_CMD_FREE_RETRY_TIMES; + try_times++) { + r = goodix_i2c_read(dev, command_reg, buf, 3); + if (!r && (buf[0] == TS_CMD_CFG_ERR || + buf[0] == TS_CMD_CFG_OK)) + break; + usleep_range(10000, 11000); + } + ts_info("send config result: %*ph", 3, buf); + /* set 0x7D to end send config process */ + goodix_cmd_init(dev, &ts_cmd, COMMAND_END_SEND_CFG_YS, + 0, dev->reg.command); + if (goodix_send_command(dev, &ts_cmd)) { + ts_err("failed send cfg end cmd"); + r = -EINVAL; + goto exit; + } + + if (try_times >= TS_WAIT_CMD_FREE_RETRY_TIMES) { + ts_err("failed get result"); + r = -EINVAL; + goto exit; + } + if (buf[0] == TS_CMD_CFG_ERR) { + if (buf[2] != TS_CFG_REPLY_DATA_EQU) + ts_err("failed send cfg"); + else + ts_info("config data equal with flash"); + r = -EINVAL; + goto exit; + } + } else { + /*6. wait ic set command_reg to 0xff*/ + for (try_times = 0; try_times < TS_WAIT_CMD_FREE_RETRY_TIMES; + try_times++) { + if (!goodix_i2c_read(dev, command_reg, buf, 1) && + buf[0] == TS_CMD_REG_READY) + break; + usleep_range(10000, 11000); + } + if (try_times >= TS_WAIT_CMD_FREE_RETRY_TIMES) { + ts_err("failed send cfg, reg:0x%04x is 0x%x not 0xff", + command_reg, buf[0]); + r = -EINVAL; + goto exit; + } + } + + ts_info("Send cfg SUCCESS"); + r = 0; + +exit: + return r; +} + +/*static int goodix_check_cfg_valid(struct goodix_ts_device *dev, u8 *cfg, u32 length) +{ + int ret; + u8 bag_num; + u8 checksum; + int i, j; + int bag_start = 0; + int bag_end = 0; + + if (!cfg || length < TS_CFG_HEAD_LEN) { + ts_err("cfg is INVALID, len:%d", length); + ret = -EINVAL; + goto exit; + } + + checksum = 0; + for (i = 0; i < TS_CFG_HEAD_LEN; i++) + checksum += cfg[i]; + if (checksum != 0) { + ts_err("cfg head checksum ERROR, checksum:0x%02x", + checksum); + ret = -EINVAL; + goto exit; + } + bag_num = cfg[TS_CFG_BAG_NUM_INDEX]; + bag_start = TS_CFG_BAG_START_INDEX; + + ts_info("cfg bag_num:%d, cfg length:%d", bag_num, length); + for (j = 0; j < bag_num; j++) { + if (bag_start >= length - 1) { + ts_err("ERROR, overflow!!bag_start:%d, cfg_len:%d", + bag_start, length); + ret = -EINVAL; + goto exit; + } + + bag_end = bag_start + cfg[bag_start + 1] + 3; + + checksum = 0; + if (bag_end > length) { + ts_err("ERROR, overflow!!bag:%d, bag_start:%d," + "bag_end:%d, cfg length:%d", + j, bag_start, bag_end, length); + ret = -EINVAL; + goto exit; + } + for (i = bag_start; i < bag_end; i++) + checksum += cfg[i]; + if (checksum != 0) { + ts_err("cfg INVALID, bag:%d checksum ERROR:0x%02x", + j, checksum); + ret = -EINVAL; + goto exit; + } + bag_start = bag_end; + } + + ret = 0; + ts_info("configuration check SUCCESS"); + +exit: + return ret; +}*/ + +static int goodix_send_config(struct goodix_ts_device *dev, + struct goodix_ts_config *config) +{ + int r = 0; + + if (!config || !config->initialized) { + ts_err("invalid config data"); + return -EINVAL; + } + + /*check configuration valid*/ + // TODO remove this + // r = goodix_check_cfg_valid(dev, config->data, config->length); + // if (r != 0) { + // ts_err("cfg check FAILED"); + // return -EINVAL; + // } + + ts_info("ver:%02xh,size:%d", config->data[0], config->length); + mutex_lock(&config->lock); + + /*disable doze mode*/ + if (!goodix_set_i2c_doze_mode(dev, false)) { + r = _do_goodix_send_config(dev, config); + } else { + ts_err("failed disable doze[abort]"); + r = -EINVAL; + } + /*enable doze mode*/ + goodix_set_i2c_doze_mode(dev, true); + + mutex_unlock(&config->lock); + return r; +} + +/* success return config length else return -1 */ +static int goodix_read_config_ys(struct goodix_ts_device *dev, + u8 *buf) +{ + u32 cfg_addr = dev->reg.cfg_addr; + int sub_bags = 0; + int offset = 0; + int subbag_len; + u16 checksum; + int i; + int ret; + + ret = goodix_i2c_read(dev, cfg_addr, buf, TS_CFG_HEAD_LEN_YS); + if (ret) + goto err_out; + + offset = TS_CFG_HEAD_LEN_YS; + sub_bags = buf[TS_CFG_BAG_NUM_INDEX]; + checksum = checksum_u8_ys(buf, TS_CFG_HEAD_LEN_YS); + if (checksum) { + ts_err("Config head checksum err:0x%x,data:%*ph", + checksum, TS_CFG_HEAD_LEN_YS, buf); + ret = -EINVAL; + goto err_out; + } + + ts_info("config_version:%u, vub_bags:%u", buf[0], sub_bags); + for (i = 0; i < sub_bags; i++) { + /* read sub head [0]: sub bag num, [1]: sub bag length */ + ret = goodix_i2c_read(dev, cfg_addr + offset, buf + offset, 2); + if (ret) + goto err_out; + + /* read sub bag data */ + subbag_len = buf[offset + 1]; + + ts_debug("sub bag num:%u,sub bag length:%u", + buf[offset], subbag_len); + ret = goodix_i2c_read(dev, cfg_addr + offset + 2, + buf + offset + 2, subbag_len + 2); + if (ret) + goto err_out; + checksum = checksum_u8_ys(buf + offset, subbag_len + 4); + if (checksum) { + ts_err("sub bag checksum err:0x%x", checksum); + ret = -EINVAL; + goto err_out; + } + offset += subbag_len + 4; + ts_debug("sub bag %d, data:%*ph", + buf[offset], buf[offset + 1] + 4, buf + offset); + } + ret = offset; + +err_out: + return ret; +} + +/* success return config length else return -1 */ +static int goodix_read_config_nor(struct goodix_ts_device *dev, + u8 *buf) +{ + u32 cfg_addr = dev->reg.cfg_addr; + int sub_bags = 0; + int offset = 0; + int subbag_len; + u8 checksum; + int i; + int ret; + + /*disable doze mode*/ + if (goodix_set_i2c_doze_mode(dev, false)) { + ts_err("failed disable doze mode[abort]"); + ret = -EINVAL; + goto err_out; + } + + ret = goodix_i2c_read(dev, cfg_addr, buf, TS_CFG_HEAD_LEN); + if (ret) + goto err_out; + + offset = TS_CFG_BAG_START_INDEX; + sub_bags = buf[TS_CFG_BAG_NUM_INDEX]; + checksum = checksum_u8(buf, TS_CFG_HEAD_LEN); + if (checksum) { + ts_err("Config head checksum err:0x%x,data:%*ph", + checksum, TS_CFG_HEAD_LEN, buf); + ret = -EINVAL; + goto err_out; + } + + ts_info("config_version:%u, vub_bags:%u", buf[0], sub_bags); + for (i = 0; i < sub_bags; i++) { + /* read sub head [0]: sub bag num, [1]: sub bag length */ + ret = goodix_i2c_read(dev, cfg_addr + offset, buf + offset, 2); + if (ret) + goto err_out; + + /* read sub bag data */ + subbag_len = buf[offset + 1]; + + ts_debug("sub bag num:%u,sub bag length:%u", + buf[offset], subbag_len); + ret = goodix_i2c_read(dev, cfg_addr + offset + 2, + buf + offset + 2, subbag_len + 1); + if (ret) + goto err_out; + checksum = checksum_u8(buf + offset, subbag_len + 3); + if (checksum) { + ts_err("sub bag checksum err:0x%x", checksum); + ret = -EINVAL; + goto err_out; + } + offset += subbag_len + 3; + ts_debug("sub bag %d, data:%*ph", + buf[offset], buf[offset + 1] + 3, buf + offset); + } + ret = offset; + +err_out: + /*enable doze mode*/ + goodix_set_i2c_doze_mode(dev, true); + + return ret; +} + +/* success return config_len, <= 0 failed */ +static int goodix_read_config(struct goodix_ts_device *dev, + u8 *config_data) +{ + struct goodix_ts_cmd ts_cmd; + u8 cmd_flag; + u32 cmd_reg = dev->reg.command; + int r = 0; + int i; + + if (!config_data) { + ts_err("Illegal params"); + return -EINVAL; + } + if (!dev->reg.command) { + ts_err("command register ERROR:0x%04x", dev->reg.command); + return -EINVAL; + } + + /*disable doze mode*/ + if (goodix_set_i2c_doze_mode(dev, false)) { + ts_err("failed disabled doze[abort]"); + r = -EINVAL; + goto exit; + } + + /* wait for IC in IDLE state */ + for (i = 0; i < TS_WAIT_CMD_FREE_RETRY_TIMES; i++) { + cmd_flag = 0; + r = goodix_i2c_read(dev, cmd_reg, &cmd_flag, 1); + if (r < 0 || cmd_flag == TS_CMD_REG_READY) + break; + usleep_range(10000, 11000); + } + if (cmd_flag != TS_CMD_REG_READY) { + ts_err("Wait for IC ready IDEL state timeout:addr 0x%x\n", + cmd_reg); + r = -EAGAIN; + goto exit; + } + /* 0x86 read config command */ + goodix_cmd_init(dev, &ts_cmd, COMMAND_START_READ_CFG, + 0, cmd_reg); + r = goodix_send_command(dev, &ts_cmd); + if (r) { + ts_err("Failed send read config command"); + goto exit; + } + /* wait for config data ready */ + if (goodix_wait_cfg_cmd_ready(dev, COMMAND_READ_CFG_PREPARE_OK, + COMMAND_START_READ_CFG)) { + ts_err("Wait for config data ready timeout"); + r = -EAGAIN; + goto exit; + } + + if (dev->ic_type == IC_TYPE_YELLOWSTONE) + r = goodix_read_config_ys(dev, config_data); + else + r = goodix_read_config_nor(dev, config_data); + if (r <= 0) + ts_err("Failed read config data"); + + /* clear command */ + goodix_cmd_init(dev, &ts_cmd, TS_CMD_REG_READY, 0, cmd_reg); + goodix_send_command(dev, &ts_cmd); + +exit: + /*enable doze mode*/ + goodix_set_i2c_doze_mode(dev, true); + + return r; +} + +/** + * goodix_hw_reset - reset device + * + * @dev: pointer to touch device + * Returns 0 - succeed,<0 - failed + */ +int goodix_hw_reset(struct goodix_ts_device *dev) +{ + u8 data[2] = {0x00}; + int r = 0; + + ts_info("HW reset"); + + gpio_direction_output(dev->board_data.reset_gpio, 0); + udelay(2000); + gpio_direction_output(dev->board_data.reset_gpio, 1); + msleep(100); + + /*init dynamic esd*/ + if (dev->reg.esd) { + r = goodix_i2c_write_trans(dev, dev->reg.esd, data, 1); + if (r < 0) + ts_err("IC reset, init dynamic esd FAILED"); + } else { + ts_info("reg.esd is NULL, skip dynamic esd init"); + } + + return 0; +} + +/** + * goodix_request_handler - handle firmware request + * + * @dev: pointer to touch device + * @request_data: requset information + * Returns 0 - succeed,<0 - failed + */ +static int goodix_request_handler(struct goodix_ts_device *dev) +{ + struct goodix_ts_core *core_data = platform_get_drvdata(goodix_pdev); + unsigned char buffer[1]; + int r; + + r = goodix_i2c_read_trans(dev, dev->reg.fw_request, buffer, 1); + if (r < 0) + return r; + + switch (buffer[0]) { + case REQUEST_CONFIG: + ts_info("HW request config"); + r = goodix_send_config(dev, &(dev->normal_cfg)); + if (r != 0) + ts_info("request config, send config faild"); + break; + case REQUEST_BAKREF: + ts_info("HW request bakref"); + break; + case REQUEST_RESET: + ts_info("HW requset reset"); + r = goodix_hw_reset(dev); + if (r != 0) + ts_info("request reset, reset faild"); + break; + case REQUEST_RELOADFW: + ts_info("HW request reload fw"); +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + if (core_data && core_data->do_fw_update) { + r = core_data->do_fw_update(UPDATE_MODE_FORCE|UPDATE_MODE_SRC_REQUEST); + if (r) { + ts_err("fw update failed, %d[try later]", r); + core_data->ts_mmi_info.need_reflash = 1; + } + } else { + ts_err("fw update handler not ready [try later]."); + core_data->ts_mmi_info.need_reflash = 1; + } +#else + if (core_data && core_data->do_fw_update) + core_data->do_fw_update(UPDATE_MODE_FORCE|UPDATE_MODE_SRC_REQUEST); + else + ts_err("fw update handler not ready."); +#endif + break; + case REQUEST_IDLE: + ts_info("HW request idle"); + break; + default: + ts_info("Unknown hw request:%d", buffer[0]); + break; + } + + buffer[0] = 0x00; + r = goodix_i2c_write_trans(dev, dev->reg.fw_request, buffer, 1); + return r; +} + +static void goodix_swap_coords(struct goodix_ts_device *dev, + unsigned int *coor_x, unsigned int *coor_y) +{ + unsigned int temp; + struct goodix_ts_board_data *bdata = &dev->board_data; + + if (bdata->swap_axis) { + temp = *coor_x; + *coor_x = *coor_y; + *coor_y = temp; + } + if (bdata->x2x) + *coor_x = bdata->panel_max_x - *coor_x; + if (bdata->y2y) + *coor_y = bdata->panel_max_y - *coor_y; +} + +#define GOODIX_KEY_STATE 0x10 +static void goodix_parse_finger_nor(struct goodix_ts_device *dev, + struct goodix_touch_data *touch_data, unsigned char *buf, int touch_num) +{ + unsigned int id = 0, x = 0, y = 0, w = 0; + static u8 pre_key_map; + u8 cur_key_map = 0; + static u32 pre_finger_map; + u32 cur_finger_map = 0; + u8 *coor_data; + int i; + + coor_data = &buf[IRQ_HEAD_LEN_NOR]; + for (i = 0; i < touch_num; i++) { + id = coor_data[0]; + if(id >= GOODIX_MAX_TOUCH){ + ts_info("invaild finger id =%d", id); + break; + } + x = le16_to_cpup((__be16 *) (coor_data + 1)); + y = le16_to_cpup((__be16 *) (coor_data + 3)); + w = coor_data[5]; + goodix_swap_coords(dev, &x, &y); + 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; + cur_finger_map |= (1 << id); + coor_data += BYTES_PER_COORD; + } + + /* process finger release */ + for (i = 0; i < GOODIX_MAX_TOUCH; i++) { + if (cur_finger_map & (1 << i)) + continue; + if (pre_finger_map & (1 << i)) + touch_data->coords[i].status = TS_RELEASE; + } + pre_finger_map = cur_finger_map; + touch_data->touch_num = touch_num; + + if (buf[1] & GOODIX_KEY_STATE) { + /* have key */ + cur_key_map = buf[touch_num * BYTES_PER_COORD + 2] & 0x0F; + for (i = 0; i < GOODIX_MAX_TP_KEY; i++) { + if (cur_key_map & (1 << i)) { + touch_data->keys[i].status = TS_TOUCH; + touch_data->keys[i].code = + dev->board_data.panel_key_map[i]; + } + } + } + /* process key release */ + for (i = 0; i < GOODIX_MAX_TP_KEY; i++) { + if (cur_key_map & (1 << i) || !(pre_key_map & (1 << i))) + continue; + touch_data->keys[i].status = TS_RELEASE; + touch_data->keys[i].code = dev->board_data.panel_key_map[i]; + } + pre_key_map = cur_key_map; +} + +static void goodix_parse_finger_ys(struct goodix_ts_device *dev, + struct goodix_touch_data *touch_data, unsigned char *buf, int touch_num) +{ + unsigned int id = 0, x = 0, y = 0, w = 0; + static u32 pre_finger_map; + u32 cur_finger_map = 0; + u8 *coor_data; + int i; + + coor_data = &buf[IRQ_HEAD_LEN_YS]; + for (i = 0; i < touch_num; i++) { + id = (coor_data[0] >> 4) & 0x0F; + if(id >= GOODIX_MAX_TOUCH){ + ts_info("invaild finger id =%d", id); + break; + } + x = be16_to_cpup((__be16 *)(coor_data + 2)); + y = be16_to_cpup((__be16 *)(coor_data + 4)); + w = be16_to_cpup((__be16 *)(coor_data + 6)); + goodix_swap_coords(dev, &x, &y); + 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; + cur_finger_map |= (1 << id); + coor_data += BYTES_PER_COORD; + } + + /* process finger release */ + for (i = 0; i < GOODIX_MAX_TOUCH; i++) { + if (cur_finger_map & (1 << i)) + continue; + if (pre_finger_map & (1 << i)) + touch_data->coords[i].status = TS_RELEASE; + } + pre_finger_map = cur_finger_map; + touch_data->touch_num = touch_num; +} + +static unsigned int goodix_pen_btn_code[] = {BTN_STYLUS, BTN_STYLUS2}; +static void goodix_parse_pen_nor(struct goodix_ts_device *dev, + struct goodix_pen_data *pen_data, unsigned char *buf, int touch_num) +{ + unsigned int id = 0; + static u8 pre_key_map; + u8 cur_key_map = 0; + static u32 pre_pen_status; + u32 cur_pen_status = 0; + u8 *coor_data; + int i; + + coor_data = &buf[2]; + for (i = 0; i < touch_num; i++) { + /* search for pen coordinate */ + id = coor_data[0]; + if (id < 0x80) { + coor_data += BYTES_PER_COORD; + continue; + } + pen_data->coords.x = le16_to_cpup((__be16 *)(coor_data + 1)); + pen_data->coords.y = le16_to_cpup((__be16 *)(coor_data + 3)); + pen_data->coords.p = le16_to_cpup((__be16 *)(coor_data + 5)); + goodix_swap_coords(dev, &pen_data->coords.x, + &pen_data->coords.y); + pen_data->coords.status = TS_TOUCH; + pen_data->coords.tool_type = BTN_TOOL_PEN; + cur_pen_status = 1; + /* currently only support one stylus */ + break; + } + if (!cur_pen_status && pre_pen_status) { + pen_data->coords.status = TS_RELEASE; + } + pre_pen_status = cur_pen_status; + + /* process pen button */ + if (buf[1] & GOODIX_KEY_STATE) { + cur_key_map = (buf[touch_num * BYTES_PER_COORD + 2] >> 4) & 0x0F; + for (i = 0; i < GOODIX_MAX_PEN_KEY; i++) { + if (!(cur_key_map & (1 << i))) + continue; + pen_data->keys[i].status = TS_TOUCH; + pen_data->keys[i].code = goodix_pen_btn_code[i]; + } + } + for (i = 0; i < GOODIX_MAX_PEN_KEY; i++) { + if (cur_key_map & (1 << i) || !(pre_key_map & (1 << i))) + continue; + pen_data->keys[i].status = TS_RELEASE; + pen_data->keys[i].code = goodix_pen_btn_code[i]; + } + pre_key_map = cur_key_map; +} + +static void goodix_parse_pen_ys(struct goodix_ts_device *dev, + struct goodix_pen_data *pen_data, unsigned char *buf, int touch_num) +{ + ts_info("unsupported"); +} + +static int goodix_touch_handler_ys(struct goodix_ts_device *dev, + struct goodix_ts_event *ts_event, + u8 *pre_buf, u32 pre_buf_len) +{ + struct goodix_touch_data *touch_data = &ts_event->touch_data; + struct goodix_pen_data *pen_data = &ts_event->pen_data; + static u8 buffer[IRQ_HEAD_LEN_YS + + BYTES_PER_COORD * GOODIX_MAX_TOUCH + 2]; + int touch_num = 0, r = -EINVAL; + u8 point_type = 0; + u16 chksum = 0; + + static u8 pre_finger_num = 0; + static u8 pre_pen_num = 0; + + /* 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 (unlikely(touch_num > GOODIX_MAX_TOUCH)) { + touch_num = -EINVAL; + goto exit_clean_sta; + } + if (unlikely(touch_num > 1)) { + r = goodix_i2c_read_trans(dev, + dev->reg.coor + pre_buf_len, + &buffer[pre_buf_len], + (touch_num - 1) * BYTES_PER_COORD); + if (unlikely(r < 0)) + goto exit_clean_sta; + } + + if (touch_num > 0) { + chksum = checksum_u8_ys(&buffer[IRQ_HEAD_LEN_YS], + touch_num * BYTES_PER_COORD + 2); + if (unlikely(chksum != 0)) { + ts_debug("checksum error:%x", chksum); + r = -EINVAL; + goto exit_clean_sta; + } + } + point_type = buffer[(touch_num - 1) * BYTES_PER_COORD + IRQ_HEAD_LEN_YS]; + if (touch_num >= 1 && (point_type == 3 || point_type == 1)) { + if (pre_finger_num) { + ts_event->event_type = EVENT_TOUCH; + goodix_parse_finger_ys(dev, touch_data, buffer, 0); + pre_finger_num = 0; + } else { + pre_pen_num = 1; + ts_event->event_type = EVENT_PEN; + goodix_parse_pen_ys(dev, pen_data, buffer, touch_num); + } + } else { + if (pre_pen_num) { + ts_event->event_type = EVENT_PEN; + goodix_parse_pen_ys(dev, pen_data, buffer, 0); + pre_pen_num = 0; + } else { + ts_event->event_type = EVENT_TOUCH; + goodix_parse_finger_ys(dev, 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"); + } +exit_clean_sta: + return r; +} + +static int goodix_touch_handler_nor(struct goodix_ts_device *dev, + struct goodix_ts_event *ts_event, + u8 *pre_buf, u32 pre_buf_len) +{ + struct goodix_touch_data *touch_data = &ts_event->touch_data; + struct goodix_pen_data *pen_data = &ts_event->pen_data; + static u8 buffer[IRQ_HEAD_LEN_NOR + + BYTES_PER_COORD * GOODIX_MAX_TOUCH + 2]; + int touch_num = 0, r = -EINVAL; + unsigned char chksum = 0; + + static u8 pre_finger_num = 0; + static u8 pre_pen_num = 0; + + /* 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[1] & 0x0F; + + if (unlikely(touch_num > GOODIX_MAX_TOUCH)) { + touch_num = -EINVAL; + goto exit_clean_sta; + } + if (unlikely(touch_num > 1)) { + r = goodix_i2c_read_trans(dev, + dev->reg.coor + pre_buf_len, + &buffer[pre_buf_len], + (touch_num - 1) * BYTES_PER_COORD); + if (unlikely(r < 0)) + goto exit_clean_sta; + } + + chksum = checksum_u8(&buffer[0], touch_num * BYTES_PER_COORD + 4); + if (unlikely(chksum != 0)) { + ts_debug("checksum error:%X, ic_type:%d", chksum, dev->ic_type); + r = -EINVAL; + goto exit_clean_sta; + } + + if (touch_num >= 1 && + buffer[(touch_num - 1) * BYTES_PER_COORD + 2] >= 0x80) { + if (pre_finger_num) { + ts_event->event_type = EVENT_TOUCH; + goodix_parse_finger_nor(dev, touch_data, buffer, 0); + pre_finger_num = 0; + } else { + pre_pen_num = 1; + ts_event->event_type = EVENT_PEN; + goodix_parse_pen_nor(dev, pen_data, buffer, touch_num); + } + } else { + if (pre_pen_num) { + ts_event->event_type = EVENT_PEN; + goodix_parse_pen_nor(dev, pen_data, buffer, 0); + pre_pen_num = 0; + } else { + ts_event->event_type = EVENT_TOUCH; + goodix_parse_finger_nor(dev, touch_data, + buffer, touch_num); + pre_finger_num = touch_num; + } + } +exit_clean_sta: + return r; +} + +static int goodix_event_handler(struct goodix_ts_device *dev, + struct goodix_ts_event *ts_event) +{ + int pre_read_len; + u8 pre_buf[32]; + u8 event_sta; + int r; + + if (dev->ic_type == IC_TYPE_YELLOWSTONE) + pre_read_len = IRQ_HEAD_LEN_YS + BYTES_PER_COORD + 2; + else + pre_read_len = IRQ_HEAD_LEN_NOR + BYTES_PER_COORD + 2; + r = goodix_i2c_read_trans(dev, dev->reg.coor, + pre_buf, pre_read_len); + if (unlikely(r < 0)) + return r; + + if (dev->ic_type == IC_TYPE_YELLOWSTONE && + checksum_u8_ys(pre_buf, IRQ_HEAD_LEN_YS)) { + ts_debug("irq head checksum error %*ph", + IRQ_HEAD_LEN_YS, pre_buf); + return -EINVAL; + } + /* buffer[0]: event state */ + event_sta = pre_buf[0]; + if (likely((event_sta & GOODIX_TOUCH_EVENT) == GOODIX_TOUCH_EVENT)) { + /* handle touch event */ + if (dev->ic_type == IC_TYPE_YELLOWSTONE) + goodix_touch_handler_ys(dev, ts_event, pre_buf, + pre_read_len); + else + goodix_touch_handler_nor(dev, ts_event, pre_buf, + pre_read_len); + } else if (unlikely((event_sta & GOODIX_REQUEST_EVENT) == + GOODIX_REQUEST_EVENT)) { + /* handle request event */ + ts_event->event_type = EVENT_REQUEST; + goodix_request_handler(dev); + } else if ((event_sta & GOODIX_GESTURE_EVENT) == + GOODIX_GESTURE_EVENT) { + /* handle gesture event */ + ts_debug("Gesture event"); + } else if ((event_sta & GOODIX_HOTKNOT_EVENT) == + GOODIX_HOTKNOT_EVENT) { + /* handle hotknot event */ + ts_debug("Hotknot event"); + } else { + ts_debug("unknow event type:0x%x", event_sta); + r = -EINVAL; + } + + return r; +} + +/** + * goodix_hw_suspend - Let touch deivce stay in lowpower mode. + * @dev: pointer to goodix touch device + * @return: 0 - succeed, < 0 - failed + */ +static int goodix_hw_suspend(struct goodix_ts_device *dev) +{ + struct goodix_ts_cmd sleep_cmd; + int r = 0; + + goodix_cmd_init(dev, &sleep_cmd, COMMAND_SLEEP, + 0, dev->reg.command); + if (sleep_cmd.initialized) { + r = goodix_send_command(dev, &sleep_cmd); + if (!r) + ts_info("Chip in sleep mode"); + } else { + ts_err("Uninitialized sleep command"); + } + return r; +} + +/** + * goodix_hw_resume - Let touch deivce stay in active mode. + * @dev: pointer to goodix touch device + * @return: 0 - succeed, < 0 - failed + */ +static int goodix_hw_resume(struct goodix_ts_device *dev) +{ + goodix_hw_reset(dev); + + return 0; +} + +static int goodix_esd_check(struct goodix_ts_device *dev) +{ + int r; + u8 data = 0; + + if (dev->reg.esd == 0) { + ts_err("esd reg is NULL"); + return 0; + } + + /*check dynamic esd*/ + r = dev->hw_ops->read_trans(dev, TS_REG_ESD_TICK_R, &data, 1); + + if (r < 0 || (data == GOODIX_ESD_TICK_WRITE_DATA)) { + ts_info("dynamic esd occur, r:%d, data:0x%02x", r, data); + r = -EINVAL; + goto exit; + } + +exit: + return r; +} + +/* hardware opeation funstions */ +static const struct goodix_ts_hw_ops hw_i2c_ops = { + .dev_confirm = goodix_ts_dev_confirm, + .read = goodix_i2c_read, + .write = goodix_i2c_write, + .read_trans = goodix_i2c_read_trans, + .write_trans = goodix_i2c_write_trans, + .reset = goodix_hw_reset, + .event_handler = goodix_event_handler, + .send_config = goodix_send_config, + .read_config = goodix_read_config, + .send_cmd = goodix_send_command, + .read_version = goodix_read_version, + .suspend = goodix_hw_suspend, + .resume = goodix_hw_resume, + .check_hw = goodix_esd_check, +}; + +static void goodix_pdev_release(struct device *dev) +{ + ts_info("goodix pdev released"); +} + +static int goodix_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + struct goodix_ts_device *ts_device = NULL; + int r = 0; + + ts_info("goodix_i2c_probe IN"); + + r = i2c_check_functionality(client->adapter, + I2C_FUNC_I2C); + if (!r) + return -EIO; + + /* ts device data */ + ts_device = devm_kzalloc(&client->dev, + sizeof(struct goodix_ts_device), GFP_KERNEL); + if (!ts_device) + return -ENOMEM; + + if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) { + /* parse devicetree property */ + r = goodix_parse_dt(client->dev.of_node, + &ts_device->board_data); + if (r < 0) { + ts_err("failed parse device info form dts, %d", r); + return -EINVAL; + } + } else { + ts_err("no valid device tree node found"); + return -ENODEV; + } + + ts_device->name = "Goodix TouchDevcie"; + ts_device->dev = &client->dev; + ts_device->hw_ops = &hw_i2c_ops; + + /* ts core device */ + goodix_pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL); + if (!goodix_pdev) + return -ENOMEM; + + i2c_set_clientdata(client, goodix_pdev); + + 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/platfrom/goodix_ts.0 + * goodix_pdev->dev.parent = &client->dev; + */ + goodix_pdev->dev.platform_data = ts_device; + goodix_pdev->dev.release = goodix_pdev_release; + + /* register platform device, then the goodix_ts_core + * module will probe the touch deivce. + */ + r = platform_device_register(goodix_pdev); + if (r) { + ts_err("failed register goodix platform device, %d", r); + goto err_pdev; + } + r = goodix_ts_core_init(); + if (r) { + ts_err("failed register platform driver, %d", r); + goto err_pdriver; + } + ts_info("i2c probe out"); + return r; + +err_pdriver: + platform_device_unregister(goodix_pdev); +err_pdev: + kfree(goodix_pdev); + goodix_pdev = NULL; + i2c_set_clientdata(client, NULL); + ts_info("i2c probe out, %d", r); + return r; +} + +static int goodix_i2c_remove(struct i2c_client *client) +{ + if (goodix_pdev) { + platform_device_unregister(goodix_pdev); + kfree(goodix_pdev); + goodix_pdev = NULL; + } + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id i2c_matchs[] = { + {.compatible = "goodix,gt9896",}, + {.compatible = "goodix,gt9886",}, + {.compatible = "goodix,gt9889",}, + {.compatible = "goodix,gt5863",}, + {}, +}; +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, +}; + +/* release manully when prob failed */ +void goodix_ts_dev_release(void) +{ + if (goodix_pdev) { + platform_device_unregister(goodix_pdev); + kfree(goodix_pdev); + goodix_pdev = NULL; + } + i2c_del_driver(&goodix_i2c_driver); +} + +static int __init goodix_i2c_init(void) +{ + ts_info("Goodix driver init"); + return i2c_add_driver(&goodix_i2c_driver); +} + +static void __exit goodix_i2c_exit(void) +{ + i2c_del_driver(&goodix_i2c_driver); + ts_info("Goodix driver exit"); +} + +module_init(goodix_i2c_init); +module_exit(goodix_i2c_exit); + +MODULE_DESCRIPTION("Goodix Touchscreen Hardware Module"); +MODULE_AUTHOR("Goodix, Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_mmi.c b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_mmi.c new file mode 100644 index 000000000000..21bedfde6519 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_mmi.c @@ -0,0 +1,610 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * 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. + */ + +#include +#include "goodix_ts_mmi.h" +#include "goodix_cfg_bin.h" +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) +#include +#define INPUT_TYPE_B_PROTOCOL +#endif + + +extern int goodix_ts_unregister_notifier(struct notifier_block *nb); +extern struct goodix_module goodix_modules; + +static int goodix_ts_mmi_methods_get_vendor(struct device *dev, void *cdata) { + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", "goodix"); +} +static int goodix_ts_mmi_methods_get_productinfo(struct device *dev, void *cdata) { + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_INFO_LEN, "%s", GOODIX_TP_IC_TYPE); +} +static int goodix_ts_mmi_methods_get_build_id(struct device *dev, void *cdata) { + int ret; + struct goodix_ts_version fw_ver; + struct goodix_ts_device *ts_dev; + struct goodix_ts_core *core_data; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + ts_err("Failed to get platform device"); + return -ENODEV; + } + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver data"); + return -ENODEV; + } + ts_dev = core_data->ts_dev; + if (!ts_dev) { + ts_err("Failed to get platform data"); + return -ENODEV; + } + ret = ts_dev->hw_ops->read_version(ts_dev, &fw_ver); + if (!ret && fw_ver.valid) { + ret = scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%02x", fw_ver.vid[0]); + ts_info("get config id:%d ", fw_ver.vid[0]); + return ret; + } else + return -ENODEV; +} +/*return firmware version*/ +static int goodix_ts_mmi_methods_get_config_id(struct device *dev, void *cdata) { + int ret; + struct goodix_ts_version fw_ver; + struct goodix_ts_device *ts_dev; + struct goodix_ts_core *core_data; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + ts_err("Failed to get platform device"); + return -ENODEV; + } + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver data"); + return -ENODEV; + } + ts_dev = core_data->ts_dev; + if (!ts_dev) { + ts_err("Failed to get platform data"); + return -ENODEV; + } + ret = ts_dev->hw_ops->read_version(ts_dev, &fw_ver); + if (!ret && fw_ver.valid) { + ret = scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%02x", fw_ver.vid[3]); + ts_info("get build id:%d ", fw_ver.vid[3]); + return ret; + } else + return -ENODEV; +} +static int goodix_ts_mmi_methods_get_bus_type(struct device *dev, void *idata) { + TO_INT(idata) = TOUCHSCREEN_MMI_BUS_TYPE_I2C; + return 0; +} +static int goodix_ts_mmi_methods_get_irq_status(struct device *dev, void *idata) { + struct goodix_ts_board_data *ts_bdata; + struct goodix_ts_core *core_data; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + ts_err("Failed to get platform device"); + return -ENODEV; + } + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver data"); + return -ENODEV; + } + ts_bdata = board_data(core_data); + if (!ts_bdata) { + ts_err("Failed to get ts board data"); + return -ENODEV; + } + TO_INT(idata) = gpio_get_value(ts_bdata->irq_gpio); + return 0; +} +static int goodix_ts_mmi_methods_get_drv_irq(struct device *dev, void *idata) { + struct goodix_ts_core *core_data; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + ts_err("Failed to get platform device"); + return -ENODEV; + } + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver data"); + return -ENODEV; + } + TO_INT(idata) = atomic_read(&core_data->irq_enabled); + return 0; +} +static int goodix_ts_mmi_methods_get_poweron(struct device *dev, void *idata) { + struct goodix_ts_core *core_data; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + ts_err("Failed to get platform device"); + return -ENODEV; + } + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver data"); + return -ENODEV; + } + TO_INT(idata) = core_data->power_on; + return 0; +} +static int goodix_ts_mmi_methods_get_flashprog(struct device *dev, void *idata) { + struct goodix_ts_core *core_data; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + ts_err("Failed to get platform device"); + return -ENODEV; + } + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver data"); + return -ENODEV; + } + if (core_data->ts_mmi_info.need_reflash) + TO_INT(idata) = 1; + else + TO_INT(idata) = core_data->ts_mmi_info.update_status; + return 0; +} +static int goodix_ts_mmi_methods_drv_irq(struct device *dev, int state) { + struct goodix_ts_core *core_data; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + ts_err("Failed to get platform device"); + return -ENODEV; + } + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver data"); + return -ENODEV; + } + goodix_ts_irq_enable(core_data, !(!state)); + return 0; +} +/* reset chip + * type:can control software reset and hardware reset, + * but GOODIX has no software reset, + * so the type parameter is not used here. + */ +static int goodix_ts_mmi_methods_reset(struct device *dev, int type) { + int ret = -ENODEV; + struct goodix_ts_device *ts_dev; + struct goodix_ts_core *core_data; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + ts_err("Failed to get platform device"); + return -ENODEV; + } + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver data"); + return -ENODEV; + } + ts_dev = core_data->ts_dev; + if (ts_dev->hw_ops->reset) + ret = ts_dev->hw_ops->reset(ts_dev); + return ret; +} +static int goodix_ts_firmware_update(struct device *dev, char *fwname) { + struct goodix_ts_board_data *ts_bdata; + struct goodix_ts_core *core_data; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + ts_err("Failed to get platform device"); + return -ENODEV; + } + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver data"); + return -ENODEV; + } + ts_bdata = board_data(core_data); + if (!ts_bdata) { + ts_err("Failed to get ts board data"); + return -ENODEV; + } + + ts_debug("HW request update fw"); + /* set firmware image name */ + if (core_data->set_fw_name) + core_data->set_fw_name(fwname); + else + ts_err("fw update handler not ready"); + + if (core_data->do_fw_update) + core_data->do_fw_update(UPDATE_MODE_SRC_REQUEST | + UPDATE_MODE_BLOCK | + UPDATE_MODE_FORCE); + else { + ts_err("fw update handler not ready."); + return -EFAULT; + } + core_data->ts_mmi_info.need_reflash = 0; + return 0; +} + +static int goodix_ts_mmi_methods_power(struct device *dev, int on) { + struct goodix_ts_core *core_data; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + ts_err("Failed to get platform device"); + return -ENODEV; + } + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver data"); + return -ENODEV; + } + + if (on == TS_MMI_POWER_ON) + return goodix_ts_power_on(core_data); + else if(on == TS_MMI_POWER_OFF) + return goodix_ts_power_off(core_data); + else { + ts_err("Invalid power parameter %d.\n", on); + return -EINVAL; + } +} + +#ifdef CONFIG_PINCTRL +static int goodix_ts_mmi_methods_pinctrl(struct device *dev, int on) { + struct goodix_ts_core *core_data; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + ts_err("Failed to get platform device"); + return -ENODEV; + } + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver data"); + return -ENODEV; + } + + if (on == TS_MMI_PINCTL_ON) { + if (core_data->pinctrl) + return pinctrl_select_state(core_data->pinctrl, + core_data->pin_sta_active); + } else if(on == TS_MMI_PINCTL_OFF) { + if (core_data->pinctrl) + return pinctrl_select_state(core_data->pinctrl, + core_data->pin_sta_suspend); + } else { + ts_err("Invalid power parameter %d.\n", on); + return -EINVAL; + } + + return 0; +} +#endif + +static int goodix_ts_mmi_panel_state(struct device *dev, + enum ts_mmi_pm_mode from, enum ts_mmi_pm_mode to) +{ + struct goodix_ts_device *ts_dev; + struct goodix_ts_core *core_data; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + ts_err("Failed to get platform device"); + return -ENODEV; + } + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver data"); + return -ENODEV; + } + ts_dev = core_data->ts_dev; + + switch (to) { + case TS_MMI_PM_GESTURE: + case TS_MMI_PM_DEEPSLEEP: + /* let touch ic work in sleep mode */ + if (ts_dev && ts_dev->hw_ops->suspend) + ts_dev->hw_ops->suspend(ts_dev); + atomic_set(&core_data->suspended, 1); + break; + case TS_MMI_PM_ACTIVE: + atomic_set(&core_data->suspended, 0); + /* resume device */ + if (ts_dev && ts_dev->hw_ops->resume) + ts_dev->hw_ops->resume(ts_dev); + break; + default: + ts_err("Invalid power state parameter %d.\n", to); + return -EINVAL; + } + + return 0; +} + +static int goodix_ts_mmi_pre_resume(struct device *dev) { + struct goodix_ext_module *ext_module, *next; + struct input_dev *input_dev; + struct input_mt *mt; + int i; + int r = 0; + struct goodix_ts_core *core_data; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + ts_err("Failed to get platform device"); + return -ENODEV; + } + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver data"); + return -ENODEV; + } + + input_dev = core_data->input_dev; + mt = input_dev->mt; + + if (mt) { + for (i = 0; i < mt->num_slots; 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_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; + + r = ext_module->funcs->before_resume(core_data, + ext_module); + if (r == 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: + return r; +} + +static int goodix_ts_mmi_post_resume(struct device *dev) { + struct goodix_ext_module *ext_module, *next; + int r = 0; + struct goodix_ts_core *core_data; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + ts_err("Failed to get platform device"); + return -ENODEV; + } + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver 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->funcs->after_resume) + continue; + + r = ext_module->funcs->after_resume(core_data, + ext_module); + if (r == EVT_CANCEL_RESUME) { + mutex_unlock(&goodix_modules.mutex); + ts_info("Canceled by module:%s", + ext_module->name); + goto out; + } + } + } + mutex_unlock(&goodix_modules.mutex); + /* + * notify resume event, inform the esd protector + * and charger detector to turn on the work + */ + ts_info("try notify resume"); + goodix_ts_blocking_notify(NOTIFY_RESUME, NULL); + +out: + ts_debug("Resume end"); + return r; +} + +static int goodix_ts_mmi_pre_suspend(struct device *dev) { + struct goodix_ext_module *ext_module, *next; + int r = 0; + struct goodix_ts_core *core_data; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + ts_err("Failed to get platform device"); + return -ENODEV; + } + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver data"); + return -ENODEV; + } + + /* + * 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; + + r = ext_module->funcs->before_suspend(core_data, + ext_module); + if (r == 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: + return r; +} + +static int goodix_ts_mmi_post_suspend(struct device *dev) { + struct goodix_ext_module *ext_module, *next; + int r = 0; + struct goodix_ts_core *core_data; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + ts_err("Failed to get platform device"); + return -ENODEV; + } + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver data"); + return -ENODEV; + } + + /* 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; + + r = ext_module->funcs->after_suspend(core_data, + ext_module); + if (r == 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: + ts_info("Suspend end"); + return r; +} + +static struct ts_mmi_methods goodix_ts_mmi_methods = { + .get_vendor = goodix_ts_mmi_methods_get_vendor, + .get_productinfo = goodix_ts_mmi_methods_get_productinfo, + .get_build_id = goodix_ts_mmi_methods_get_build_id, + .get_config_id = goodix_ts_mmi_methods_get_config_id, + .get_bus_type = goodix_ts_mmi_methods_get_bus_type, + .get_irq_status = goodix_ts_mmi_methods_get_irq_status, + .get_drv_irq = goodix_ts_mmi_methods_get_drv_irq, + .get_poweron = goodix_ts_mmi_methods_get_poweron, + .get_flashprog = goodix_ts_mmi_methods_get_flashprog, + /* SET methods */ + .reset = goodix_ts_mmi_methods_reset, + .drv_irq = goodix_ts_mmi_methods_drv_irq, + .power = goodix_ts_mmi_methods_power, +#ifdef CONFIG_PINCTRL + .pinctrl = goodix_ts_mmi_methods_pinctrl, +#endif + /* Firmware */ + .firmware_update = goodix_ts_firmware_update, + /* PM callback */ + .panel_state = goodix_ts_mmi_panel_state, + .pre_resume = goodix_ts_mmi_pre_resume, + .post_resume = goodix_ts_mmi_post_resume, + .pre_suspend = goodix_ts_mmi_pre_suspend, + .post_suspend = goodix_ts_mmi_post_suspend, +}; + +static int mmi_notifier_callback(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct goodix_ts_mmi *mmi_info = container_of(nb, + struct goodix_ts_mmi, mmi_notifier); + struct goodix_ts_core *core_data = container_of(mmi_info, + struct goodix_ts_core, ts_mmi_info); + switch (action) { + case NOTIFY_FWUPDATE_START: + ts_info("Firmware update start"); + core_data->ts_mmi_info.update_status = 1; + break; + case NOTIFY_FWUPDATE_FAILED: + case NOTIFY_FWUPDATE_SUCCESS: + case NOTIFY_FWUPDATE_SKIP: + core_data->ts_mmi_info.update_status = 0; + break; + default: + ts_err("Firmware state unknown"); + break; + } + return 0; +} + +int goodix_ts_mmi_dev_register(struct platform_device *pdev) { + int ret; + struct goodix_ts_core *core_data; + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver data"); + return -ENODEV; + } + core_data->ts_mmi_info.mmi_notifier.notifier_call = mmi_notifier_callback; + goodix_ts_register_notifier(&core_data->ts_mmi_info.mmi_notifier); + ret = ts_mmi_dev_register(core_data->ts_dev->dev, &goodix_ts_mmi_methods); + if (ret) { + dev_err(&pdev->dev, "Failed to register ts mmi\n"); + return ret; + } + return 0; +} + +void goodix_ts_mmi_dev_unregister(struct platform_device *pdev) { + struct goodix_ts_core *core_data; + core_data = platform_get_drvdata(pdev); + if (!core_data) + ts_err("Failed to get driver data"); + ts_mmi_dev_unregister(&pdev->dev); + goodix_ts_unregister_notifier(&core_data->ts_mmi_info.mmi_notifier); +} diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_mmi.h b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_mmi.h new file mode 100644 index 000000000000..055377a444bd --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_mmi.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * 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. + */ +#ifndef __GOODIX_TS_MMI_H__ +#define __GOODIX_TS_MMI_H__ + +#include "goodix_ts_core.h" + +extern int goodix_ts_mmi_dev_register(struct platform_device *ts_device); +extern void goodix_ts_mmi_dev_unregister(struct platform_device *ts_device); + +#endif diff --git a/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_tools.c b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_tools.c new file mode 100644 index 000000000000..0028dfea4b36 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1430_mmi/goodix_ts_tools.c @@ -0,0 +1,614 @@ +/* + * Goodix Tools Dirver 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "goodix_ts_core.h" + +#define GOODIX_TOOLS_NAME "gtp_tools" +#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_DRV_VERSION (_IOR(GOODIX_TS_IOC_MAGIC, 9, u8) & NEGLECT_SIZE_MASK) + +#define GOODIX_TS_IOC_MAXNR 10 + +#define IRQ_FALG (0x01 << 2) + +#define I2C_MSG_HEAD_LEN 20 +#define TS_REG_COORDS_BASE 0x4100 + +/* + * struct goodix_tools_data - goodix tools data message used in sync read + * @data: The buffer into which data is written + * @reg_addr: Slave device register start address to start read data + * @length: Number of data bytes in @data being read from slave device + * @filled: When buffer @data be filled will set this flag with 1, outhrwise 0 + * @list_head:Eonnet every goodix_tools_data struct into a list + */ + +struct goodix_tools_data { + u32 reg_addr; + u32 length; + u8 *data; + bool filled; + struct list_head list; +}; + + +/* + * 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 + */ +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 t_count; + struct goodix_ext_module module; +} *goodix_tools_dev; + + +/* read data from i2c asynchronous, + * success return bytes read, else 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]; + struct goodix_ts_device *ts_dev = dev->ts_core->ts_dev; + const struct goodix_ts_hw_ops *hw_ops = ts_dev->hw_ops; + + ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN); + if (ret) { + ret = -EFAULT; + goto err_out; + } + 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); + + databuf = kzalloc(length, GFP_KERNEL); + if (!databuf) { + ts_err("Alloc memory failed"); + return -ENOMEM; + } + + if (!hw_ops->read_trans(ts_dev, reg_addr, databuf, length)) { + if (copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, databuf, length)) { + ret = -EFAULT; + ts_err("Copy_to_user failed"); + } else { + ret = length; + } + } else { + ret = -EBUSY; + ts_err("Read i2c failed"); + } +err_out: + kfree(databuf); + return ret; +} + +/* if success return config data length */ +static int read_config_data(struct goodix_ts_device *ts_dev, 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); + 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_dev->hw_ops->read_config) + ret = ts_dev->hw_ops->read_config(ts_dev, tmp_buf); + else + ret = -EINVAL; + } else { + ret = ts_dev->hw_ops->read_trans(ts_dev, 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; +} + +/* read data from i2c synchronous, + * success return bytes read, else return <= 0 + */ +static int sync_read(struct goodix_tools_dev *dev, void __user *arg) +{ + int ret = 0; + u8 i2c_msg_head[I2C_MSG_HEAD_LEN]; + struct goodix_tools_data tools_data; + + ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN); + if (ret) { + ts_err("Copy data from user failed"); + return -EFAULT; + } + tools_data.reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8) + + (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24); + tools_data.length = i2c_msg_head[4] + (i2c_msg_head[5] << 8) + + (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24); + tools_data.filled = 0; + + tools_data.data = kzalloc(tools_data.length, GFP_KERNEL); + if (!tools_data.data) { + ts_err("Alloc memory failed"); + return -ENOMEM; + } + + mutex_lock(&dev->mutex); + list_add_tail(&tools_data.list, &dev->head); + mutex_unlock(&dev->mutex); + /* wait queue will timeout after 1 seconds */ + wait_event_interruptible_timeout(dev->wq, tools_data.filled == 1, HZ * 3); + + mutex_lock(&dev->mutex); + list_del(&tools_data.list); + mutex_unlock(&dev->mutex); + if (tools_data.filled == 1) { + if (copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, tools_data.data, + tools_data.length)) { + ret = -EFAULT; + ts_err("Copy_to_user failed"); + } else { + ret = tools_data.length; + } + } else { + ret = -EAGAIN; + ts_err("Wait queue timeout"); + } + + kfree(tools_data.data); + 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_device *ts_dev = dev->ts_core->ts_dev; + const struct goodix_ts_hw_ops *hw_ops = ts_dev->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); + + 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_trans(ts_dev, 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_ts_config *cfg, void __user *arg) +{ + int ret = 0; + u32 reg_addr, length; + u8 i2c_msg_head[I2C_MSG_HEAD_LEN]; + + cfg->initialized = 0; + mutex_init(&cfg->lock); + 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); + + ret = copy_from_user(cfg->data, (u8 *)arg + I2C_MSG_HEAD_LEN, length); + if (ret) { + ret = -EFAULT; + ts_err("Copy data from user failed"); + goto err_out; + } + cfg->reg_base = reg_addr; + cfg->length = length; + strlcpy(cfg->name, "tools-send-cfg", sizeof(cfg->name)); + cfg->delay = 50; + cfg->initialized = true; + return 0; + +err_out: + return ret; +} +/** + * 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_device *ts_dev; + const struct goodix_ts_hw_ops *hw_ops; + struct goodix_ts_cmd temp_cmd; + struct goodix_ts_config *temp_cfg = NULL; + + if (dev->ts_core == NULL) { + ts_err("Tools module not register"); + return -EINVAL; + } + ts_dev = dev->ts_core->ts_dev; + hw_ops = ts_dev->hw_ops; + + if (_IOC_TYPE(cmd) != GOODIX_TS_IOC_MAGIC) { + ts_err("Bad magic num:%c", _IOC_TYPE(cmd)); + return -ENOTTY; + } + if (_IOC_NR(cmd) > GOODIX_TS_IOC_MAXNR) { + ts_err("Bad cmd num:%d > %d", + _IOC_NR(cmd), GOODIX_TS_IOC_MAXNR); + return -ENOTTY; + } + + switch (cmd & NEGLECT_SIZE_MASK) { + case GTP_IRQ_ENABLE: + if (arg == 1) { + goodix_ts_irq_enable(dev->ts_core, true); + mutex_lock(&dev->mutex); + dev->ops_mode |= IRQ_FALG; + mutex_unlock(&dev->mutex); + ts_info("IRQ enabled"); + } else if (arg == 0) { + goodix_ts_irq_enable(dev->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_dev); + break; + case GTP_SEND_COMMAND: + ret = copy_from_user(&temp_cmd, (void __user *)arg, + sizeof(struct goodix_ts_cmd)); + if (ret) { + ret = -EINVAL; + goto err_out; + } + + ret = hw_ops->send_cmd(ts_dev, &temp_cmd); + if (ret) { + ts_err("Send command failed"); + ret = -EAGAIN; + } + break; + case GTP_SEND_CONFIG: + temp_cfg = kzalloc(sizeof(struct goodix_ts_config), GFP_KERNEL); + if (temp_cfg == NULL) { + 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_dev, temp_cfg); + if (ret) { + ts_err("Failed send config"); + ret = -EAGAIN; + } else { + ts_info("Send config success"); + ret = 0; + } + } + break; + case GTP_READ_CONFIG: + ret = read_config_data(ts_dev, (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: + if (filp->f_flags & O_NONBLOCK) { + ts_err("Goodix tools now worked in sync_bus mode"); + ret = -EAGAIN; + goto err_out; + } + ret = sync_read(dev, (void __user *)arg); + if (ret < 0) + ts_err("Sync data read failed"); + break; + case GTP_ASYNC_WRITE: + ret = async_write(dev, (void __user *)arg); + if (ret < 0) + ts_err("Async data write failed"); + break; + case GTP_DRV_VERSION: + ret = copy_to_user((u8 *)arg, GOODIX_DRIVER_VERSION, + sizeof(GOODIX_DRIVER_VERSION)); + if (ret) + ts_err("failed copy driver version info to user"); + break; + default: + ts_info("Invalid cmd"); + ret = -ENOTTY; + break; + } + +err_out: + if (!temp_cfg) { + kfree(temp_cfg); + temp_cfg = NULL; + } + 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; + + filp->private_data = goodix_tools_dev; + ts_info("tools open"); + /* Only the first time open device need to register module */ + ret = goodix_register_ext_module(&goodix_tools_dev->module); + if (ret) { + ts_info("failed register to core module"); + } + + return ret; +} + +static int goodix_tools_release(struct inode *inode, struct file *filp) +{ + int ret = 0; + /* when the last close this dev node unregister the module */ + ret = goodix_unregister_ext_module(&goodix_tools_dev->module); + return ret; +} + +/** + * goodix_tools_module_irq - goodix tools Irq handle + * This functions is excuted when interrupt happended + * + * @core_data: pointer to touch core data + * @module: pointer to goodix_ext_module struct + * return: EVT_CONTINUE let other module handle this irq + */ +static int goodix_tools_module_irq(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + struct goodix_tools_dev *dev = module->priv_data; + struct goodix_ts_device *ts_dev = dev->ts_core->ts_dev; + const struct goodix_ts_hw_ops *hw_ops = ts_dev->hw_ops; + struct goodix_tools_data *tools_data, *next; + int r = 0; + u8 evt_sta = 0; + + mutex_lock(&dev->mutex); + if (!list_empty(&dev->head)) { + r = hw_ops->read_trans(ts_dev, ts_dev->reg.coor/* TS_REG_COORDS_BASE*/, &evt_sta, 1); + if (r < 0 || ((evt_sta & GOODIX_TOUCH_EVENT) == 0)) { + ts_err("data not ready:0x%x, read ret =%d", evt_sta, r); + mutex_unlock(&dev->mutex); + return EVT_CONTINUE; + } + + list_for_each_entry_safe(tools_data, next, &dev->head, list) { + if (!hw_ops->read_trans(ts_dev, tools_data->reg_addr, + tools_data->data, tools_data->length)) { + tools_data->filled = 1; + } + } + wake_up(&dev->wq); + } + mutex_unlock(&dev->mutex); + return EVT_CONTINUE; +} + +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 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 = { + .irq_event = goodix_tools_module_irq, + .init = goodix_tools_module_init, +}; + +/** + * goodix_tools_init - init goodix tools device and register a miscdevice + * + * return: 0 success, else failed + */ +static int __init 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->t_count, 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"); + + return ret; +} + +static void __exit goodix_tools_exit(void) +{ + misc_deregister(&goodix_tools_miscdev); + kfree(goodix_tools_dev); + ts_info("Goodix tools miscdev exit"); +} + +module_init(goodix_tools_init); +module_exit(goodix_tools_exit); + +MODULE_DESCRIPTION("Goodix tools Module"); +MODULE_AUTHOR("Goodix, Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/Android.mk b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/Android.mk new file mode 100644 index 000000000000..3b57175d4c2f --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/Android.mk @@ -0,0 +1,18 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DRM_PANEL_NOTIFICATIONS),true) + KBUILD_OPTIONS += CONFIG_DRM_PANEL_NOTIFICATIONS=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := goodix_v1510_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/mmi_info.ko +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KBUILD_OPTIONS += CONFIG_INPUT_TOUCHSCREEN_MMI=y + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +endif +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/Kbuild b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/Kbuild new file mode 100644 index 000000000000..d6ec17b1c9a4 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/Kbuild @@ -0,0 +1,18 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m := goodix_v1510_mmi.o +goodix_v1510_mmi-objs = goodix_ts_i2c.o goodix_ts_core.o goodix_ts_test.o goodix_cfg_bin.o goodix_gtx8_update.o goodix_ts_gesture.o goodix_ts_tools.o + +ifneq ($(filter m y,$(CONFIG_DRM_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_NOTIFICATIONS +endif +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI + goodix_v1510_mmi-objs += goodix_ts_mmi.o +endif + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/Kconfig b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/Kconfig new file mode 100644 index 000000000000..094bc47b0f8d --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/Kconfig @@ -0,0 +1,47 @@ +# +# Goodix touchscreen driver configuration +# +menuconfig TOUCHSCREEN_GOODIX_GTX8_MMI + bool "Goodix GTx8 touchscreen" + depends on I2C + default y + help + Say Y here if you have a Goodix GTx5xx touchscreen connected + to your system. + + If unsure, say N. + +if TOUCHSCREEN_GOODIX_GTX8_MMI + + +config TOUCHSCREEN_GOODIX_GTX8_MMI_UPDATE + tristate "Goodix GTx8 firmware update module" + default y + help + Say Y here to enable support for doing firmware update. + + If unsure, say N. + + To compile this driver as a module, choose M here. + +config TOUCHSCREEN_GOODIX_GTX8_MMI_GESTURE + tristate "Goodix GTx8 gesture wakeup feature" + default n + help + Say Y here to enable support for gesture wakeup feature.. + + If unsure, say N. + + To compile this driver as a module, choose M here. + +config TOUCHSCREEN_GOODIX_GTX8_MMI_TOOLS + tristate "Goodix touch tools support" + default y + help + Say Y here to enable debug tools. + + If unsure, say N. + + To compile this driver as a module, choose M here. + +endif diff --git a/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/Makefile b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/Makefile new file mode 100644 index 000000000000..ff20fa5e17dd --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/Makefile @@ -0,0 +1,17 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(M)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(M)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_cfg_bin.c b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_cfg_bin.c new file mode 100644 index 000000000000..d7ee6ab6029e --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_cfg_bin.c @@ -0,0 +1,650 @@ +#include "goodix_cfg_bin.h" + +extern struct goodix_module goodix_modules; +int goodix_ts_stage2_init(struct goodix_ts_core *core_data); + +static int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin); +static int goodix_get_reg_and_cfg(struct goodix_ts_device *ts_dev, + struct goodix_cfg_bin *cfg_bin); +static int goodix_read_cfg_bin(struct device *dev, + struct goodix_cfg_bin *cfg_bin); +static void goodix_cfg_pkg_leToCpu(struct goodix_cfg_package *pkg); + +int goodix_get_cfg_value(struct goodix_ts_core *core_data, + u8 *config, u8 *buf, u8 len, u8 sub_bag_num, u8 offset) +{ + u8 *sub_bag_ptr = NULL; + u8 i = 0; + u8 chksum_len = 1; + u8 head_len = 0; + + if (core_data->ts_dev->ic_type == IC_TYPE_YELLOWSTONE) + chksum_len = 2; + head_len = 3 + chksum_len; + + sub_bag_ptr = &config[head_len]; + for (i = 0; i < config[2]; i++) { + if (sub_bag_ptr[0] == sub_bag_num) + break; + sub_bag_ptr += sub_bag_ptr[1] + 2 + chksum_len; + } + + if (i >= config[2]) { + ts_err("Cann't find the specifiled bag num %d", sub_bag_num); + return -EINVAL; + } + + if (sub_bag_ptr[1]+2+chksum_len < offset + len) { + ts_err("Sub bag len less then you want to read: %d < %d", sub_bag_ptr[1]+3, offset + len); + return -EINVAL; + } + + if (len) + memcpy(buf, sub_bag_ptr + offset, len); + else + memcpy(buf, sub_bag_ptr, sub_bag_ptr[1] + 2 + chksum_len); + return (sub_bag_ptr + offset - config); +} + +/* + * parse driver and sensor num only on screen + */ +#define MAX_DRV_NUM 40 +#define MAX_SEN_NUM 36 +static int goodix_get_channel_num(struct goodix_ts_core *core_data, + u32 *sen_num, u32 *drv_num, u8 *cfg_data) +{ + int ret = 0; + u8 buf[3] = {0}; + u8 *temp_cfg_data = cfg_data; + + if (!sen_num || !drv_num || !temp_cfg_data) { + ts_err("%s: invalid param", __func__); + return -EINVAL; + } + + ret = goodix_get_cfg_value(core_data, temp_cfg_data, buf, 2, 1, 14); + if (ret > 0) { + *drv_num = buf[0] + buf[1]; + } else { + ts_err("Failed read drv_num reg"); + ret = -EINVAL; + goto err_out; + } + + ret = goodix_get_cfg_value(core_data, temp_cfg_data, buf, 1, 1, 10); + if (ret > 0) { + *sen_num = buf[0]; + } else { + ts_err("Failed read sen_num reg"); + ret = -EINVAL; + goto err_out; + } + + ts_info("drv_num:%d,sen_num:%d", *drv_num, *sen_num); + + if (*drv_num > MAX_DRV_NUM || *drv_num <= 0 + || *sen_num > MAX_SEN_NUM || *sen_num <= 0) { + ts_err("invalid sensor or driver num"); + ret = -EINVAL; + } + +err_out: + return ret < 0 ? ret : 0; +} + +static int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin) +{ + u8 checksum; + int i, r; + u16 offset1, offset2; + + if (!cfg_bin->bin_data || cfg_bin->bin_data_len == 0) { + ts_err("NO cfg_bin data, cfg_bin data length:%d", + cfg_bin->bin_data_len); + r = -EINVAL; + goto exit; + } + + /* 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); + r = -EINVAL; + goto exit; + } + 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); + r = -EINVAL; + goto exit; + } + + /*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); + r = -EINVAL; + goto exit; + } + + /*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) { + ts_err("cfg_pkgs, allocate memory ERROR"); + r = -ENOMEM; + goto exit; + } + + /*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); + r = -EINVAL; + 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); + /*compatible little edition and big edition*/ + goodix_cfg_pkg_leToCpu(&cfg_bin->cfg_pkgs[i]); + + /*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); + for (i = 0; i < cfg_bin->head.pkg_num; i++) { + ts_debug("---------------------------------------------"); + ts_debug("------package:%d------", i + 1); + ts_debug("package len:%04x", cfg_bin->cfg_pkgs[i].cnst_info.pkg_len); + ts_debug("package ic_type:%s", cfg_bin->cfg_pkgs[i].cnst_info.ic_type); + ts_debug("package cfg_type:%01x", cfg_bin->cfg_pkgs[i].cnst_info.cfg_type); + ts_debug("package sensor_id:%01x", cfg_bin->cfg_pkgs[i].cnst_info.sensor_id); + ts_debug("package hw_pid:%s", cfg_bin->cfg_pkgs[i].cnst_info.hw_pid); + ts_debug("package hw_vid:%s", cfg_bin->cfg_pkgs[i].cnst_info.hw_vid); + ts_debug("package fw_mask_version:%s", cfg_bin->cfg_pkgs[i].cnst_info.fw_mask); + ts_debug("package fw_patch_version:%s", cfg_bin->cfg_pkgs[i].cnst_info.fw_patch); + ts_debug("package x_res_offset:%02x", cfg_bin->cfg_pkgs[i].cnst_info.x_res_offset); + ts_debug("package y_res_offset:%02x", cfg_bin->cfg_pkgs[i].cnst_info.y_res_offset); + ts_debug("package trigger_offset:%02x", cfg_bin->cfg_pkgs[i].cnst_info.trigger_offset); + + ts_debug("reg info"); + ts_debug("send_cfg_flag reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.cfg_send_flag.addr); + ts_debug("version base reg:%02x, len:%d", + cfg_bin->cfg_pkgs[i].reg_info.version_base.addr, + cfg_bin->cfg_pkgs[i].reg_info.version_base.reserved1); + ts_debug("pid reg:%02x:%d", cfg_bin->cfg_pkgs[i].reg_info.pid.addr, + cfg_bin->cfg_pkgs[i].reg_info.pid.reserved1); + ts_debug("vid reg:%02x:%d", cfg_bin->cfg_pkgs[i].reg_info.vid.addr, + cfg_bin->cfg_pkgs[i].reg_info.vid.reserved1); + ts_debug("sensor_id reg:%02x,mask:%x", cfg_bin->cfg_pkgs[i].reg_info.sensor_id.addr, + cfg_bin->cfg_pkgs[i].reg_info.sensor_id.addr); + ts_debug("fw_status reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.fw_status.addr); + ts_debug("cfg_addr reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.cfg_addr.addr); + ts_debug("esd reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.esd.addr); + ts_debug("command reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.command.addr); + ts_debug("coor:%02x", cfg_bin->cfg_pkgs[i].reg_info.coor.addr); + ts_debug("gesture:%02x", cfg_bin->cfg_pkgs[i].reg_info.gesture.addr); + ts_debug("fw_request:%02x", cfg_bin->cfg_pkgs[i].reg_info.fw_request.addr); + ts_debug("proximity:%02x", cfg_bin->cfg_pkgs[i].reg_info.proximity.addr); + + ts_debug("--------------------------------------------"); + } + r = 0; +exit: + return r; +} + +static int goodix_cfg_bin_proc(struct goodix_ts_core *core_data) +{ + int r; + struct goodix_ts_device *ts_dev = core_data->ts_dev; + struct goodix_cfg_bin *cfg_bin = kzalloc(sizeof(struct goodix_cfg_bin), + GFP_KERNEL); + if (!cfg_bin) { + return -ENOMEM; + } + if (!ts_dev) { + ts_err("ts device can't be null"); + return -EINVAL; + } + + /* when start init config bin with error state */ + ts_dev->cfg_bin_state = CFG_BIN_STATE_ERROR; + + /*get cfg_bin from file system*/ + r = goodix_read_cfg_bin(ts_dev->dev, cfg_bin); + if (r < 0) { + ts_err("Failed get valid config bin data"); + goto exit; + } + /*parse cfg bin*/ + r = goodix_parse_cfg_bin(cfg_bin); + if (!r) { + ts_info("parse cfg bin SUCCESS"); + } else { + ts_err("parse cfg bin FAILED"); + goto exit; + } + + /*get register address and configuration from cfg bin*/ + r = goodix_get_reg_and_cfg(ts_dev, cfg_bin); + if (!r) { + ts_info("success get reg and cfg info from cfg bin"); + } else { + ts_err("failed get cfg and reg info, update fw then retry"); + } + + /* get sen_num/drv_num */ + if (ts_dev->normal_cfg.initialized) { + r = goodix_get_channel_num(core_data, &core_data->sen_num, + &core_data->drv_num, ts_dev->normal_cfg.data); + if (r) + ts_err("failed get channel_num"); + } + + /*debug*/ + ts_info("cfg_send_flag:0x%04x", ts_dev->reg.cfg_send_flag); + ts_info("pid:0x%04x", ts_dev->reg.pid); + ts_info("vid:0x%04x", ts_dev->reg.vid); + ts_info("sensor_id:0x%04x", ts_dev->reg.sensor_id); + ts_info("fw_mask:0x%04x", ts_dev->reg.fw_mask); + ts_info("fw_status:0x%04x", ts_dev->reg.fw_status); + ts_info("cfg_addr:0x%04x", ts_dev->reg.cfg_addr); + ts_info("esd:0x%04x", ts_dev->reg.esd); + ts_info("command:0x%04x", ts_dev->reg.command); + ts_info("coor:0x%04x", ts_dev->reg.coor); + ts_info("gesture:0x%04x", ts_dev->reg.gesture); + ts_info("fw_request:0x%04x", ts_dev->reg.fw_request); + ts_info("proximity:0x%04x", ts_dev->reg.proximity); + +exit: + kfree(cfg_bin->cfg_pkgs); + kfree(cfg_bin->bin_data); + kfree(cfg_bin); + if (r) + goodix_ts_blocking_notify(NOTIFY_CFG_BIN_FAILED, &r); + else + goodix_ts_blocking_notify(NOTIFY_CFG_BIN_SUCCESS, &r); + + ts_info("cfg bin state %d, ret %d", ts_dev->cfg_bin_state, r); + return r; +} + +static int goodix_extract_cfg_pkg(struct goodix_ts_device *ts_dev, + struct goodix_cfg_package *cfg_pkg) +{ + struct goodix_ts_config *ts_cfg; + + if (cfg_pkg->cnst_info.cfg_type == TS_NORMAL_CFG) { + ts_cfg = &(ts_dev->normal_cfg); + } else if (cfg_pkg->cnst_info.cfg_type == TS_HIGH_SENSE_CFG) { + ts_cfg = &(ts_dev->highsense_cfg); + } else { + ts_err("unknown cfg type %d", cfg_pkg->cnst_info.cfg_type); + return -EINVAL; + } + + ts_cfg->length = cfg_pkg->pkg_len - + TS_PKG_CONST_INFO_LEN - TS_PKG_REG_INFO_LEN; + if (ts_cfg->length > sizeof(ts_cfg->data)) { + ts_err("illegal cfg length %d", ts_cfg->length); + return -EINVAL; + } + if (ts_cfg->length) { + ts_info("get config type %d, len %d", + cfg_pkg->cnst_info.cfg_type, ts_cfg->length); + memcpy(ts_cfg->data, cfg_pkg->cfg, ts_cfg->length); + ts_cfg->initialized = TS_CFG_STABLE; + mutex_init(&ts_cfg->lock); + } else { + ts_info("no config data"); + } + + /*get register info*/ + ts_dev->reg.cfg_send_flag = cfg_pkg->reg_info.cfg_send_flag.addr; + ts_dev->reg.version_base = cfg_pkg->reg_info.version_base.addr; + ts_dev->reg.version_len = cfg_pkg->reg_info.version_base.reserved1; + ts_dev->reg.pid = cfg_pkg->reg_info.pid.addr; + ts_dev->reg.pid_len = cfg_pkg->reg_info.pid.reserved1; + ts_dev->reg.vid = cfg_pkg->reg_info.vid.addr; + ts_dev->reg.vid_len = cfg_pkg->reg_info.vid.reserved1; + ts_dev->reg.sensor_id = cfg_pkg->reg_info.sensor_id.addr; + ts_dev->reg.sensor_id_mask = cfg_pkg->reg_info.sensor_id.reserved1; + ts_dev->reg.fw_mask = cfg_pkg->reg_info.fw_mask.addr; + ts_dev->reg.fw_status = cfg_pkg->reg_info.fw_status.addr; + ts_dev->reg.cfg_addr = cfg_pkg->reg_info.cfg_addr.addr; + ts_dev->reg.esd = cfg_pkg->reg_info.esd.addr; + ts_dev->reg.command = cfg_pkg->reg_info.command.addr; + ts_dev->reg.coor = cfg_pkg->reg_info.coor.addr; + ts_dev->reg.gesture = cfg_pkg->reg_info.gesture.addr; + ts_dev->reg.fw_request = cfg_pkg->reg_info.fw_request.addr; + ts_dev->reg.proximity = cfg_pkg->reg_info.proximity.addr; + + return 0; +} + +static int goodix_get_reg_and_cfg(struct goodix_ts_device *ts_dev, + struct goodix_cfg_bin *cfg_bin) +{ + int i; + u16 addr; + u8 read_len; + char temp_sensor_id = -1; + u8 temp_fw_mask[TS_CFG_BLOCK_FW_MASK_LEN] = {0x00}; + u8 temp_pid[TS_CFG_BLOCK_PID_LEN] = {0x00}; + int r = -EINVAL; + + 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; + } + + memset(&ts_dev->normal_cfg, 0, sizeof(ts_dev->normal_cfg)); + memset(&ts_dev->highsense_cfg, 0, sizeof(ts_dev->highsense_cfg)); + + /* find suitable cfg packages */ + for (i = 0; i < cfg_bin->head.pkg_num; i++) { + /*get ic type*/ + if (!strncmp(cfg_bin->cfg_pkgs[i].cnst_info.ic_type, + "normandy", strlen("normandy"))) { + ts_dev->ic_type = IC_TYPE_NORMANDY; + } else if (!strncmp(cfg_bin->cfg_pkgs[i].cnst_info.ic_type, + "yellowstone", strlen("yellowstone"))) { + ts_dev->ic_type = IC_TYPE_YELLOWSTONE; + } else { + ts_err("unknow ic type of cfg_bin:%s", + cfg_bin->cfg_pkgs[i].cnst_info.ic_type); + continue; + } + + ts_info("ic_type:%d", ts_dev->ic_type); + + /* contrast sensor id */ + addr = cfg_bin->cfg_pkgs[i].reg_info.sensor_id.addr; + if (!addr) { + ts_info("pkg:%d, sensor_id reg is NULL", i); + continue; + } + + r = ts_dev->hw_ops->read(ts_dev, addr, &temp_sensor_id, 1); + if (r < 0) { + ts_err("failed get sensor of pkg:%d, reg:0x%02x", i, addr); + goto get_default_pkg; + } + ts_info("sensor id is %d", temp_sensor_id); + /*sensor.reserved1 is a mask, if it's not ZERO, use it*/ + if (cfg_bin->cfg_pkgs[i].reg_info.sensor_id.reserved1 != 0) + temp_sensor_id &= cfg_bin->cfg_pkgs[i].reg_info.sensor_id.reserved1; + + if (temp_sensor_id != cfg_bin->cfg_pkgs[i].cnst_info.sensor_id) { + ts_err("pkg:%d, sensor id contrast FAILED, reg:0x%02x", + i, addr); + ts_err("sensor_id from i2c:%d, sensor_id of cfg bin:%d", + temp_sensor_id, + cfg_bin->cfg_pkgs[i].cnst_info.sensor_id); + continue; + } + + /*contrast fw_mask, if this reg is null, skip this step*/ + addr = cfg_bin->cfg_pkgs[i].reg_info.fw_mask.addr; + if (addr && cfg_bin->cfg_pkgs[i].cnst_info.fw_mask[0]) { + r = ts_dev->hw_ops->read(ts_dev, addr, temp_fw_mask, + sizeof(temp_fw_mask)); + if (r < 0) { + ts_err("failed read fw_mask pkg:%d, reg:0x%02x", + i, addr); + goto get_default_pkg; + } + if (strncmp(temp_fw_mask, cfg_bin->cfg_pkgs[i].cnst_info.fw_mask, + sizeof(temp_fw_mask))) { + ts_err("pkg:%d, fw_mask contrast FAILED, reg:0x%02x,", i, addr); + ts_err("mask from i2c:%s, mask of cfg bin:%s", + temp_fw_mask, + cfg_bin->cfg_pkgs[i].cnst_info.fw_mask); + continue; + } + } + + /*contrast pid*/ + addr = cfg_bin->cfg_pkgs[i].reg_info.pid.addr; + read_len = cfg_bin->cfg_pkgs[i].reg_info.pid.reserved1; + if (!addr) { + ts_err("pkg:%d, pid reg is NULL", i); + continue; + } + if (read_len <= 0 || read_len > TS_CFG_BLOCK_PID_LEN) { + ts_err("pkg:%d, hw_pid length ERROR, len:%d", + i, read_len); + continue; + } + r = ts_dev->hw_ops->read(ts_dev, addr, temp_pid, read_len); + if (r < 0) { + ts_err("failed read pid pkg:%d, pid reg:0x%02x", i, addr); + goto get_default_pkg; + } + if (strncmp(temp_pid, cfg_bin->cfg_pkgs[i].cnst_info.hw_pid, read_len)) { + ts_err("pkg:%d, pid contrast FAILED, reg:0x%02x", i, addr); + ts_err("pid from i2c:%s, pid of cfg bin:%s", + temp_pid, cfg_bin->cfg_pkgs[i].cnst_info.hw_pid); + continue; + } + + ts_info("try get package info: ic type %s, cfg type %d", + cfg_bin->cfg_pkgs[i].cnst_info.ic_type, + cfg_bin->cfg_pkgs[i].cnst_info.cfg_type); + /* currently only support normal and high_sense config */ + if (cfg_bin->cfg_pkgs[i].cnst_info.cfg_type == TS_NORMAL_CFG || + cfg_bin->cfg_pkgs[i].cnst_info.cfg_type == TS_HIGH_SENSE_CFG) { + r = goodix_extract_cfg_pkg(ts_dev, &cfg_bin->cfg_pkgs[i]); + if (!r) { + ts_dev->cfg_bin_state = CFG_BIN_STATE_INITIALIZED; + ts_info("success parse cfg bin"); + } else { + ts_err("failed parse cfg bin"); + break; + } + } + } + +get_default_pkg: + if (ts_dev->cfg_bin_state != CFG_BIN_STATE_INITIALIZED) { + ts_err("no valid normal cfg, use cfg_pkg 0 as default"); + /* Foo code for recover dead IC. + * force set package 0 config type to normal config, this will + * config will use to recover IC. + */ + cfg_bin->cfg_pkgs[0].cnst_info.cfg_type = TS_NORMAL_CFG; + if (goodix_extract_cfg_pkg(ts_dev, &cfg_bin->cfg_pkgs[0])) { + ts_err("failed get valid config for IC recover"); + ts_dev->cfg_bin_state = CFG_BIN_STATE_ERROR; + } else { + ts_dev->cfg_bin_state = CFG_BIN_STATE_TEMP; + ts_info("get temp config data"); + } + + r = -EINVAL; + } + + return r; +} + +static int goodix_read_cfg_bin(struct device *dev, struct goodix_cfg_bin *cfg_bin) +{ + const struct firmware *firmware = NULL; + char cfg_bin_name[32] = {0x00}; + int i = 0, r; + + /*get cfg_bin_name*/ + strlcpy(cfg_bin_name, TS_DEFAULT_CFG_BIN, sizeof(cfg_bin_name)); + + ts_info("cfg_bin_name:%s", cfg_bin_name); + + for (i = 0; i < TS_RQST_FW_RETRY_TIMES; i++) { + r = request_firmware(&firmware, cfg_bin_name, dev); + if (r < 0) { + ts_err("failed get cfg bin[%s] error:%d, try_times:%d", + cfg_bin_name, r, i + 1); + msleep(1000); + } else { + ts_info("Cfg_bin image [%s] is ready, try_times:%d", + cfg_bin_name, i + 1); + break; + } + } + if (i >= TS_RQST_FW_RETRY_TIMES) { + ts_err("get cfg_bin FAILED"); + goto exit; + } + + if (firmware->size <= 0) { + ts_err("request_firmware, cfg_bin length ERROR,len:%zu", + firmware->size); + r = -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) + r = -ENOMEM; + + memcpy(cfg_bin->bin_data, firmware->data, cfg_bin->bin_data_len); + + r = 0; +exit: + if (firmware) { + release_firmware(firmware); + firmware = NULL; + } + return r; +} + +static void goodix_cfg_pkg_leToCpu(struct goodix_cfg_package *pkg) +{ + if (!pkg) { + ts_err("cfg package is NULL"); + return; + } + /*package const_info*/ + pkg->cnst_info.pkg_len = le32_to_cpu(pkg->cnst_info.pkg_len); + pkg->cnst_info.x_res_offset = le16_to_cpu(pkg->cnst_info.x_res_offset); + pkg->cnst_info.y_res_offset = le16_to_cpu(pkg->cnst_info.y_res_offset); + pkg->cnst_info.trigger_offset = le16_to_cpu(pkg->cnst_info.trigger_offset); + + /*package reg_info*/ + pkg->reg_info.cfg_send_flag.addr = le16_to_cpu(pkg->reg_info.cfg_send_flag.addr); + pkg->reg_info.pid.addr = le16_to_cpu(pkg->reg_info.pid.addr); + pkg->reg_info.vid.addr = le16_to_cpu(pkg->reg_info.vid.addr); + pkg->reg_info.sensor_id.addr = le16_to_cpu(pkg->reg_info.sensor_id.addr); + pkg->reg_info.fw_status.addr = le16_to_cpu(pkg->reg_info.fw_status.addr); + pkg->reg_info.cfg_addr.addr = le16_to_cpu(pkg->reg_info.cfg_addr.addr); + pkg->reg_info.esd.addr = le16_to_cpu(pkg->reg_info.esd.addr); + pkg->reg_info.command.addr = le16_to_cpu(pkg->reg_info.command.addr); + pkg->reg_info.coor.addr = le16_to_cpu(pkg->reg_info.coor.addr); + pkg->reg_info.gesture.addr = le16_to_cpu(pkg->reg_info.gesture.addr); + pkg->reg_info.fw_request.addr = le16_to_cpu(pkg->reg_info.fw_request.addr); + pkg->reg_info.proximity.addr = le16_to_cpu(pkg->reg_info.proximity.addr); +} + +static int goodix_later_init_thread(void *data) +{ + int ret; + struct goodix_ts_core *ts_core = data; + struct goodix_ts_device *ts_dev = ts_core->ts_dev; + + ret = goodix_cfg_bin_proc(ts_core); + if (ret) + ts_err("parse cfg bin encounter error, %d", ret); + else + ts_info("success get cfg bin"); + + if (ts_dev->cfg_bin_state == CFG_BIN_STATE_ERROR) { + ts_err("parse cfg bin encounter fatal err"); + goto release_core; + } + + if (ts_dev->cfg_bin_state == CFG_BIN_STATE_TEMP) { + ts_err("failed get valid config data, retry after fwupdate"); + ret = goodix_do_fw_update(UPDATE_MODE_BLOCK|UPDATE_MODE_FORCE| + UPDATE_MODE_FLASH_CFG| + UPDATE_MODE_SRC_REQUEST); + if (ret) { + ts_err("fw update failed, %d", ret); + goto release_core; + } + ts_info("fw update success retry parse cfg bin"); + ret = goodix_cfg_bin_proc(ts_core); + if (ret) { + ts_err("failed parse cfg bin after fw update"); + goto release_core; + } + } else { + ts_info("success parse config bin"); + ret = goodix_do_fw_update(UPDATE_MODE_BLOCK| + UPDATE_MODE_FLASH_CFG| + UPDATE_MODE_SRC_REQUEST); + if (ret) { + ts_info("fw update failed, %d[ignore]", ret); + ret = 0; + } + } + ret = goodix_ts_stage2_init(ts_core); + if (!ret) { + ts_info("stage2 init success"); + ts_core->init_stage = CORE_INIT_STAGE2; + return ret; + } + ts_err("stage2 init failed, %d", ret); + +release_core: + ts_err("stage2 init failed"); + return ret; +} + +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; +} diff --git a/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_cfg_bin.h b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_cfg_bin.h new file mode 100644 index 000000000000..c1aa93a82bba --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_cfg_bin.h @@ -0,0 +1,92 @@ +#ifndef _GOODIX_CFG_BIN_H_ +#define _GOODIX_CFG_BIN_H_ + +#include "goodix_ts_core.h" + +#define TS_DEFAULT_CFG_BIN "goodix_cfg_group.bin" +#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; +}; + +#endif diff --git a/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_default_fw.h b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_default_fw.h new file mode 100644 index 000000000000..1632e6ef3c76 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_default_fw.h @@ -0,0 +1,6 @@ +#ifndef _GOODIX_FIRMWARE_FW_H_ +#define _GOODIX_FIRMWARE_FW_H_ + +u8 goodix_default_fw[] = { 0x00}; + +#endif diff --git a/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_gtx8_update.c b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_gtx8_update.c new file mode 100644 index 000000000000..b258b5d51207 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_gtx8_update.c @@ -0,0 +1,1505 @@ +/* + * Goodix Firmware Update Driver. + * + * 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 "goodix_ts_core.h" +#include "goodix_cfg_bin.h" +#include "goodix_default_fw.h" +/* COMMON PART - START */ +#define TS_DEFAULT_FIRMWARE "goodix_firmware.bin" + +#define FW_HEADER_SIZE 256 +#define FW_SUBSYS_INFO_SIZE 8 +#define FW_SUBSYS_INFO_OFFSET 32 +#define FW_SUBSYS_MAX_NUM 28 + +#define ISP_MAX_BUFFERSIZE (1024 * 4) + +#define HW_REG_CPU_CTRL 0x2180 +#define HW_REG_DSP_MCU_POWER 0x2010 +#define HW_REG_RESET 0x2184 +#define HW_REG_SCRAMBLE 0x2218 +#define HW_REG_BANK_SELECT 0x2048 +#define HW_REG_ACCESS_PATCH0 0x204D +#define HW_REG_EC_SRM_START 0x204F +#define HW_REG_GIO_YS 0x2014 +#define HW_REG_ESD_KEY_EN 0x2318 +#define HW_REG_ESD_KEY_DIS 0x2324 +#define HW_REG_CPU_RUN_FROM 0x4506 /* for nor_L is 0x4006 */ +#define HW_REG_CPU_RUN_FROM_YS 0x4000 +#define HW_REG_ISP_RUN_FLAG 0x6006 +#define HW_REG_ISP_ADDR 0xC000 +#define HW_REG_ISP_BUFFER 0x6100 +#define HW_REG_SUBSYS_TYPE 0x6020 +#define HW_REG_FLASH_FLAG 0x6022 +#define HW_REG_CACHE 0x204B +#define HW_REG_ESD_KEY 0x2318 +#define HW_REG_WTD_TIMER 0x20B0 + +#define FLASH_ADDR_CONFIG_DATA 0x1E000 +#define FLASH_SUBSYS_TYPE_CONFIG 0x03 + +#define CPU_CTRL_PENDING 0x00 +#define CPU_CTRL_RUNNING 0x01 + +#define ISP_STAT_IDLE 0xFF +#define ISP_STAT_READY 0xAA +#define ISP_STAT_WRITING 0xAA +#define ISP_FLASH_SUCCESS 0xBB +#define ISP_FLASH_ERROR 0xCC +#define ISP_FLASH_CHECK_ERROR 0xDD +#define ISP_CMD_PREPARE 0x55 +#define ISP_CMD_FLASH 0xAA + +#define TS_CHECK_ISP_STATE_RETRY_TIMES 200 +#define TS_READ_FLASH_STATE_RETRY_TIMES 200 + +/** + * fw_subsys_info - subsytem firmware infomation + * @type: sybsystem type + * @size: firmware size + * @flash_addr: flash address + * @data: firmware data + */ +struct fw_subsys_info { + u8 type; + u32 size; + u16 flash_addr; + const u8 *data; +}; + +#pragma pack(1) +/** + * firmware_info + * @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 + * @subsys: sybsystem info + */ +struct firmware_info { + u32 size; + u16 checksum; + u8 hw_pid[6]; + u8 hw_vid[3]; + u8 fw_pid[8]; + u8 fw_vid[4]; + u8 subsys_num; + u8 chip_type; + u8 protocol_ver; + u8 reserved[2]; + struct fw_subsys_info subsys[FW_SUBSYS_MAX_NUM]; +}; + +#pragma pack() + +/** + * firmware_data - firmware data structure + * @fw_info: firmware infomation + * @firmware: firmware data structure + */ +struct firmware_data { + struct firmware_info fw_info; + const struct firmware *firmware; +}; + +enum update_status { + UPSTA_NOTWORK = 0, + UPSTA_PREPARING, + UPSTA_UPDATING, + UPSTA_ABORT, + UPSTA_SUCCESS, + UPSTA_FAILED +}; + +/** + * fw_update_ctrl - sturcture 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 + * @ts_dev: touch device + * @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; + int mode; + enum update_status status; + unsigned int progress; + + struct firmware_data fw_data; + struct goodix_ts_device *ts_dev; + struct goodix_ts_core *core_data; + + char fw_name[32]; + struct bin_attribute attr_fwimage; + struct kobject *kobj; +}; +static struct fw_update_ctrl goodix_fw_update_ctrl; +/** + * goodix_parse_firmware - parse firmware header infomation + * and subsystem infomation from firmware data buffer + * + * @fw_data: firmware struct, contains firmware header info + * and firmware data. + * return: 0 - OK, < 0 - error + */ +static int goodix_parse_firmware(struct firmware_data *fw_data) +{ + const struct firmware *firmware; + struct firmware_info *fw_info; + unsigned int i, fw_offset, info_offset; + u16 checksum; + int r = 0; + + if (!fw_data || !fw_data->firmware) { + ts_err("Invalid firmware data"); + return -EINVAL; + } + fw_info = &fw_data->fw_info; + + /* copy firmware head info */ + firmware = fw_data->firmware; + if (firmware->size < FW_SUBSYS_INFO_OFFSET) { + ts_err("Invalid firmware size:%zu", firmware->size); + r = -EINVAL; + goto err_size; + } + memcpy(fw_info, firmware->data, FW_SUBSYS_INFO_OFFSET); + + /* check firmware size */ + fw_info->size = be32_to_cpu(fw_info->size); + if (firmware->size != fw_info->size + 6) { + ts_err("Bad firmware, size not match"); + r = -EINVAL; + goto err_size; + } + + /* calculate checksum, note: sum of bytes, but check + * by u16 checksum + */ + for (i = 6, checksum = 0; i < firmware->size; i++) + checksum += firmware->data[i]; + + /* byte order change, and check */ + fw_info->checksum = be16_to_cpu(fw_info->checksum); + if (checksum != fw_info->checksum) { + ts_err("Bad firmware, cheksum error %x(file) != %x(cal)", + fw_info->checksum, checksum); + r = -EINVAL; + goto err_size; + } + + if (fw_info->subsys_num > FW_SUBSYS_MAX_NUM) { + ts_err("Bad firmware, invalid subsys num: %d", + fw_info->subsys_num); + r = -EINVAL; + goto err_size; + } + + /* parse subsystem info */ + fw_offset = FW_HEADER_SIZE; + for (i = 0; i < fw_info->subsys_num; i++) { + info_offset = FW_SUBSYS_INFO_OFFSET + + i * FW_SUBSYS_INFO_SIZE; + + fw_info->subsys[i].type = firmware->data[info_offset]; + fw_info->subsys[i].size = + be32_to_cpup((__be32 *)&firmware->data[info_offset + 1]); + fw_info->subsys[i].flash_addr = + be16_to_cpup((__be16 *)&firmware->data[info_offset + 5]); + + if (fw_offset > firmware->size) { + ts_err("Sybsys offset exceed Firmware size"); + goto err_size; + } + + fw_info->subsys[i].data = firmware->data + fw_offset; + fw_offset += fw_info->subsys[i].size; + } + + ts_info("Firmware package protocol: V%u", fw_info->protocol_ver); + ts_info("Fimware PID:GT%s", fw_info->fw_pid); + ts_info("Fimware VID:%02X%02X%02X%02x", fw_info->fw_vid[0], + fw_info->fw_vid[1], fw_info->fw_vid[2], fw_info->fw_vid[3]); + ts_info("Firmware chip type:%02X", fw_info->chip_type); + ts_info("Firmware size:%u", fw_info->size); + ts_info("Firmware subsystem num:%u", fw_info->subsys_num); +#ifdef CONFIG_GOODIX_DEBUG + for (i = 0; i < fw_info->subsys_num; i++) { + ts_debug("------------------------------------------"); + ts_debug("Index:%d", i); + ts_debug("Subsystem type:%02X", fw_info->subsys[i].type); + ts_debug("Subsystem size:%u", fw_info->subsys[i].size); + ts_debug("Subsystem flash_addr:%08X", fw_info->subsys[i].flash_addr); + ts_debug("Subsystem Ptr:%p", fw_info->subsys[i].data); + } + ts_debug("------------------------------------------"); +#endif + +err_size: + return r; +} + +/** + * goodix_check_update - compare the version of firmware running in + * touch device with the version getting from the firmware file. + * @fw_info: firmware infomation to be compared + * return: 0 no need do update, + * otherwise need do update + */ +static int goodix_check_update(struct goodix_ts_device *dev, + const struct firmware_info *fw_info) +{ + struct goodix_ts_version fw_ver; + int ret = -EINVAL; + + /* read version from chip, if we got invalid + * firmware version, maybe fimware in flash is + * incorrect, so we need to update firmware + */ + ret = dev->hw_ops->read_version(dev, &fw_ver); + if (ret) { + ts_info("failed get active pid"); + return -EINVAL; + } + + if (fw_ver.valid) { + // should we compare PID before fw update? + // if fw patch demage the PID may unmatch but + // we should de update to recover it. + // TODO skip PID check + /*if (memcmp(fw_ver.pid, fw_info->fw_pid, dev->reg.pid_len)) { + ts_err("Product ID is not match %s != %s", + fw_ver.pid, fw_info->fw_pid); + return -EPERM; + }*/ + + ret = memcmp(fw_ver.vid, fw_info->fw_vid, dev->reg.vid_len); + if (ret == 0) { + ts_info("FW version is equal to the IC's"); + return 0; + } else if (ret > 0) { + ts_info("Warning: fw version is lower the IC's"); + } + } /* else invalid firmware, update firmware */ + + ts_info("Firmware needs to be updated"); + return ret; +} + +/** + * 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(struct goodix_ts_device *dev, + unsigned int addr, unsigned char *data, unsigned int len) +{ + u8 *cfm, cfm_buf[32]; + int r, i; + + if (len > sizeof(cfm_buf)) { + cfm = kzalloc(len, GFP_KERNEL); + if (!cfm) { + ts_err("Mem alloc failed"); + return -ENOMEM; + } + } else { + cfm = &cfm_buf[0]; + } + + for (i = 0; i < GOODIX_BUS_RETRY_TIMES; i++) { + r = dev->hw_ops->write_trans(dev, addr, data, len); + if (r < 0) + goto exit; + + r = dev->hw_ops->read_trans(dev, addr, cfm, len); + if (r < 0) + goto exit; + + if (memcmp(data, cfm, len)) { + ts_info("data[0]:0x%02x, data[1]:0x%02x," + "read cfm[0]:0x%02x, cfm[1]:0x%02x", + data[0], data[1], cfm[0], cfm[1]); + dev->hw_ops->read_trans(dev, 0x6022, cfm, 2); + ts_info("read 0x6022 data[0]:0x%02x, data[1]:0x%02x", + cfm[0], cfm[1]); + r = -EMEMCMP; + continue; + } else { + r = 0; + break; + } + } + +exit: + if (cfm != &cfm_buf[0]) + kfree(cfm); + return r; +} + +static inline int goodix_reg_write(struct goodix_ts_device *dev, + unsigned int addr, unsigned char *data, unsigned int len) +{ + return dev->hw_ops->write_trans(dev, addr, data, len); +} + +static inline int goodix_reg_read(struct goodix_ts_device *dev, + unsigned int addr, unsigned char *data, unsigned int len) +{ + return dev->hw_ops->read_trans(dev, addr, data, len); +} + +/** + * goodix_load_isp - load ISP program to deivce ram + * @dev: pointer to touch device + * @fw_data: firmware data + * return 0 ok, <0 error + */ +static int goodix_load_isp(struct goodix_ts_device *ts_dev, + struct firmware_data *fw_data) +{ + struct fw_subsys_info *fw_isp; + u8 reg_val[8] = {0x00}; + int r; + int i; + + fw_isp = &fw_data->fw_info.subsys[0]; + + ts_info("Loading ISP start"); + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_BANK_SELECT, + reg_val, 1); + if (r < 0) { + ts_err("Failed to select bank0"); + return r; + } + ts_debug("Success select bank0, Set 0x%x -->0x00", HW_REG_BANK_SELECT); + + reg_val[0] = 0x01; + r = goodix_reg_write(ts_dev, HW_REG_ACCESS_PATCH0, + reg_val, 1); + if (r < 0) { + ts_err("Failed to enable patch0 access"); + return r; + } + ts_debug("Success select bank0, Set 0x%x -->0x01", HW_REG_ACCESS_PATCH0); + + r = goodix_reg_write_confirm(ts_dev, HW_REG_ISP_ADDR, + (u8 *)fw_isp->data, fw_isp->size); + if (r < 0) { + ts_err("Loading ISP error"); + return r; + } + + ts_debug("Success send ISP data to IC"); + + reg_val[0] = 0x00; + r = goodix_reg_write_confirm(ts_dev, HW_REG_ACCESS_PATCH0, + reg_val, 1); + if (r < 0) { + ts_err("Failed to disable patch0 access"); + return r; + } + ts_debug("Success forbit bank0 accedd, set 0x%x -->0x00", + HW_REG_ACCESS_PATCH0); + + reg_val[0] = 0x00; + reg_val[1] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_ISP_RUN_FLAG, reg_val, 2); + if (r < 0) { + ts_err("Failed to clear 0x%x", HW_REG_ISP_RUN_FLAG); + return r; + } + ts_debug("Success clear 0x%x", HW_REG_ISP_RUN_FLAG); + + memset(reg_val, 0x55, 8); + if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) + r = goodix_reg_write(ts_dev, HW_REG_CPU_RUN_FROM_YS, + reg_val, 8); + else + r = goodix_reg_write(ts_dev, HW_REG_CPU_RUN_FROM, + reg_val, 8); + if (r < 0) { + ts_err("Failed set HW_REG_CPU_RUN_FROM flag"); + return r; + } + ts_info("Success write [8]0x55 to HW_REG_CPU_RUN_FROM"); + + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_CPU_CTRL, + reg_val, 1); + if (r < 0) { + ts_err("Failed to run isp"); + return r; + } + ts_debug("Success run isp, set 0x%x-->0x00", HW_REG_CPU_CTRL); + + /* check isp work state */ + for (i = 0; i < TS_CHECK_ISP_STATE_RETRY_TIMES; i++) { + msleep(10); + r = goodix_reg_read(ts_dev, HW_REG_ISP_RUN_FLAG, + reg_val, 2); + if (r < 0 || (reg_val[0] == 0xAA && reg_val[1] == 0xBB)) + break; + } + if (reg_val[0] == 0xAA && reg_val[1] == 0xBB) { + ts_info("ISP working OK"); + return 0; + } + ts_err("ISP not work,0x%x=0x%x, 0x%x=0x%x", + HW_REG_ISP_RUN_FLAG, reg_val[0], + HW_REG_ISP_RUN_FLAG + 1, reg_val[1]); + return -EFAULT; +} + +/** + * 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) +{ + struct goodix_ts_device *ts_dev = fwu_ctrl->ts_dev; + u8 reg_val[4] = { 0x00 }; + u8 temp_buf[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + int retry = 20; + int r; + + if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) + ts_dev->hw_ops->write(ts_dev, HW_REG_CPU_RUN_FROM_YS, + temp_buf, 8); + else + ts_dev->hw_ops->write(ts_dev, HW_REG_CPU_RUN_FROM, + temp_buf, 8); + + /*reset IC*/ + ts_info("firmware update, reset"); + gpio_direction_output(ts_dev->board_data.reset_gpio, 0); + udelay(2000); + gpio_direction_output(ts_dev->board_data.reset_gpio, 1); + usleep_range(10000, 11000); + + retry = 100; + do { + if (ts_dev->ic_type != IC_TYPE_YELLOWSTONE) + break; + reg_val[0] = 0x00; + r = goodix_reg_write_confirm(ts_dev, HW_REG_GIO_YS, + reg_val, 1); + if (r < 0) + ts_info("Failed to remove GIO hold flag, retry %d", retry); + else + break; + } while (--retry); + if (!retry) { + ts_err("Failed to remove GIO flag"); + return -EINVAL; + } + + retry = 20; + for (retry = 0; retry < 20; retry++) { + if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) { + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_ESD_KEY_DIS, + reg_val, 1); + if (r < 0) { + ts_info("failed dis esd key"); + continue; + } + reg_val[0] = 0x95; + r = goodix_reg_write(ts_dev, HW_REG_ESD_KEY_EN, + reg_val, 1); + if (r < 0) { + ts_info("failed open esd key"); + continue; + } + } + reg_val[0] = 0x24; + r = goodix_reg_write_confirm(ts_dev, HW_REG_CPU_CTRL, + reg_val, 1); + if (r < 0) { + ts_info("Failed to hold ss51, retry"); + msleep(20); + } else { + break; + } + } + if (retry >= 20) { + ts_err("Failed hold ss51,return =%d", r); + return -EINVAL; + } + ts_debug("Success hold ss51"); + + /* enable DSP & MCU power */ + reg_val[0] = 0x00; + r = goodix_reg_write_confirm(ts_dev, HW_REG_DSP_MCU_POWER, reg_val, 1); + if (r < 0) { + ts_err("Failed enable DSP&MCU power"); + return r; + } + ts_debug("Success enabled DSP&MCU power,set 0x%x-->0x00", + HW_REG_DSP_MCU_POWER); + + /* disable watchdog timer */ + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_CACHE, reg_val, 1); + if (r < 0) { + ts_err("Failed to clear cache"); + return r; + } + ts_debug("Success clear cache"); + + if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) { + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_WTD_TIMER, reg_val, 1); + } else { + reg_val[0] = 0x95; + r = goodix_reg_write(ts_dev, HW_REG_ESD_KEY, reg_val, 1); + reg_val[0] = 0x00; + r |= goodix_reg_write(ts_dev, HW_REG_WTD_TIMER, reg_val, 1); + + reg_val[0] = 0x27; + r |= goodix_reg_write(ts_dev, HW_REG_ESD_KEY, reg_val, 1); + } + if (r < 0) { + ts_err("Failed to disable watchdog"); + return r; + } + ts_info("Success disable watchdog"); + + /* set scramble */ + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_SCRAMBLE, reg_val, 1); + if (r < 0) { + ts_err("Failed to set scramble"); + return r; + } + ts_debug("Succcess set scramble"); + + /* load ISP code and run form isp */ + r = goodix_load_isp(ts_dev, &fwu_ctrl->fw_data); + if (r < 0) + ts_err("Failed lode and run isp"); + + return r; +} + +/** + * goodix_format_fw_packet - formate one flash packet + * @pkt: target firmware packet + * @flash_addr: flash address + * @size: packet size + * @data: packet data + */ +static int goodix_format_fw_packet(u8 *pkt, u32 flash_addr, + u16 len, const u8 *data) +{ + u16 checksum; + + if (!pkt || !data) + return -EINVAL; + + /* + * checksum rule:sum of data in one format is equal to zero + * data format: byte/le16/be16/le32/be32/le64/be64 + */ + pkt[0] = (len >> 8) & 0xff; + pkt[1] = len & 0xff; + /* u16 >> 16bit seems nosense but really important */ + pkt[2] = (flash_addr >> 16) & 0xff; + pkt[3] = (flash_addr >> 8) & 0xff; + memcpy(&pkt[4], data, len); + checksum = checksum_be16(pkt, len + 4); + checksum = 0 - checksum; + pkt[len + 4] = (checksum >> 8) & 0xff; + pkt[len + 5] = checksum & 0xff; + return 0; +} + +/** + * goodix_send_fw_packet - send one firmware packet to ISP + * @dev: target touch device + * @pkt: firmware packet + * return:0 ok, <0 error + */ +static int goodix_send_fw_packet(struct goodix_ts_device *dev, u8 type, + u8 *pkt, u32 len) +{ + u8 reg_val[4]; + int r, i; + + if (!pkt) + return -EINVAL; + + ts_info("target fw subsys type:0x%x, len %d", type, len); + r = goodix_reg_write_confirm(dev, HW_REG_ISP_BUFFER, pkt, len); + if (r < 0) { + ts_err("Failed to write firmware packet"); + return r; + } + + reg_val[0] = 0; + reg_val[1] = 0; + /* clear flash flag 0X6022 */ + r = goodix_reg_write_confirm(dev, HW_REG_FLASH_FLAG, reg_val, 2); + if (r < 0) { + ts_err("Faile to clear flash flag"); + return r; + } + + /* write subsystem type 0X8020*/ + reg_val[0] = type; + reg_val[1] = type; + r = goodix_reg_write_confirm(dev, HW_REG_SUBSYS_TYPE, reg_val, 2); + if (r < 0) { + ts_err("Failed write subsystem type to IC"); + return r; + } + + for (i = 0; i < TS_READ_FLASH_STATE_RETRY_TIMES; i++) { + r = goodix_reg_read(dev, HW_REG_FLASH_FLAG, reg_val, 2); + if (r < 0) { + ts_err("Failed read flash state"); + return r; + } + + /* flash haven't end */ + if (reg_val[0] == ISP_STAT_WRITING && + reg_val[1] == ISP_STAT_WRITING) { + ts_debug("Flash not ending..."); + usleep_range(55000, 56000); + continue; + } + if (reg_val[0] == ISP_FLASH_SUCCESS && + reg_val[1] == ISP_FLASH_SUCCESS) { + r = goodix_reg_read(dev, HW_REG_FLASH_FLAG, reg_val, 2); + if (!r && reg_val[0] == ISP_FLASH_SUCCESS && + reg_val[1] == ISP_FLASH_SUCCESS) { + ts_info("Flash subsystem ok"); + return 0; + } + } + if (reg_val[0] == ISP_FLASH_ERROR && + reg_val[1] == ISP_FLASH_ERROR) { + ts_err(" Flash subsystem failed"); + return -EAGAIN; + } + if (reg_val[0] == ISP_FLASH_CHECK_ERROR) { + ts_err("Subsystem checksum err"); + return -EAGAIN; + } + + usleep_range(250, 260); + } + + ts_err("Wait for flash end timeout, 0x6022= %x %x", + reg_val[0], reg_val[1]); + return -EAGAIN; +} + +/** + * 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 infomation + * return: 0 ok, < 0 error + */ +static int goodix_flash_subsystem(struct goodix_ts_device *dev, + struct fw_subsys_info *subsys) +{ + u16 data_size, offset; + u32 total_size; + u32 subsys_base_addr = subsys->flash_addr << 8; + u8 *fw_packet; + int r = 0, i; + + /* + * 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 + 6, 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); + + /* format one firmware packet */ + r = goodix_format_fw_packet(fw_packet, subsys_base_addr + offset, + data_size, &subsys->data[offset]); + if (r < 0) { + ts_err("Invalid packet params"); + goto exit; + } + + /* send one firmware packet, retry 3 time if send failed */ + for (i = 0; i < 3; i++) { + r = goodix_send_fw_packet(dev, subsys->type, + fw_packet, data_size + 6); + if (!r) + break; + } + if (r) { + ts_err("Failed flash subsystem"); + goto exit; + } + offset += data_size; + total_size -= data_size; + } /* end while */ + +exit: + 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 goodix_ts_device *dev, + struct firmware_data *fw_data) +{ + struct fw_update_ctrl *fw_ctrl; + struct firmware_info *fw_info; + struct fw_subsys_info *fw_x; + int retry = GOODIX_BUS_RETRY_TIMES; + int i, r = 0, fw_num, prog_step; + u8 *fw_packet = NULL; + u8 *flash_cfg = NULL; + + /* start from subsystem 1, + * subsystem 0 is the ISP program + */ + fw_ctrl = container_of(fw_data, struct fw_update_ctrl, fw_data); + fw_info = &fw_data->fw_info; + fw_num = fw_info->subsys_num; + + /* we have 80% work here */ + prog_step = 80 / (fw_num - 1); + + for (i = 1; i < fw_num && retry;) { + ts_info("--- Start to flash subsystem[%d] ---", i); + fw_x = &fw_info->subsys[i]; + r = goodix_flash_subsystem(dev, fw_x); + if (r == 0) { + ts_info("--- End flash subsystem[%d]: OK ---", i); + fw_ctrl->progress += prog_step; + 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); + break; + } + } + + kfree(fw_packet); + fw_packet = NULL; + kfree(flash_cfg); + flash_cfg = NULL; + 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 goodix_ts_device *ts_dev, + struct fw_update_ctrl *fwu_ctrl) +{ + u8 reg_val[8] = {0}; + int r = 0, i = 0; + + /* hold ss51 */ + reg_val[0] = 0x24; + r = goodix_reg_write(ts_dev, HW_REG_CPU_CTRL, + reg_val, 1); + if (r < 0) + ts_err("Failed to hold ss51"); + + /* clear recovery flag */ + memset(reg_val, 0, sizeof(reg_val)); + if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) + r = goodix_reg_write(ts_dev, HW_REG_CPU_RUN_FROM_YS, + reg_val, 8); + else + r = goodix_reg_write(ts_dev, HW_REG_CPU_RUN_FROM, + reg_val, 8); + if (r) { + ts_err("Failed set CPU run from normal firmware"); + return r; + } + + /* release ss51 */ + reg_val[0] = 0x00; + r = goodix_reg_write(ts_dev, HW_REG_CPU_CTRL, reg_val, 1); + if (r < 0) + ts_err("Failed to run ss51"); + + /*reset*/ + gpio_direction_output(ts_dev->board_data.reset_gpio, 0); + udelay(2000); + gpio_direction_output(ts_dev->board_data.reset_gpio, 1); + msleep(80); + + for (i = 0; ts_dev->ic_type == IC_TYPE_YELLOWSTONE && i < 100; i++) { + reg_val[0] = 0x00; + r = goodix_reg_write_confirm(ts_dev, HW_REG_GIO_YS, reg_val, 1); + if (!r) + break; + ts_info("failed set GIO flag, r %d", r); + } + if (i >= 100) + ts_err("failed set GIO flag, %d", r); + + return r; +} + +static int goodix_flash_config(struct goodix_ts_device *ts_dev) +{ + int ret; + u8 *fw_packet = NULL; + u8 *temp_data = NULL; + + if (!ts_dev || !ts_dev->cfg_bin_state || + !ts_dev->normal_cfg.initialized) { + ts_err("no valid config data for flash"); + return -EINVAL; + } + + temp_data = kzalloc(ISP_MAX_BUFFERSIZE, GFP_KERNEL); + fw_packet = kzalloc(ISP_MAX_BUFFERSIZE + 6, GFP_KERNEL); + if (!temp_data || !fw_packet) { + ts_err("Failed alloc memory"); + ret = -EINVAL; + goto exit; + } + + memset(temp_data, 0xFF, ISP_MAX_BUFFERSIZE); + ts_info("normal config length %d", ts_dev->normal_cfg.length); + memcpy(temp_data, ts_dev->normal_cfg.data, ts_dev->normal_cfg.length); + + /* format one firmware packet */ + ret = goodix_format_fw_packet(fw_packet, FLASH_ADDR_CONFIG_DATA, + ISP_MAX_BUFFERSIZE, temp_data); + if (ret < 0) { + ts_err("Invalid packet params"); + goto exit; + } + ts_debug("fw_pack:%*ph", 10, fw_packet); + ts_info("try flash config"); + ret = goodix_send_fw_packet(ts_dev, FLASH_SUBSYS_TYPE_CONFIG, + fw_packet, ISP_MAX_BUFFERSIZE + 6); + if (ret) + ts_err("failed flash config, ret %d", ret); + else + ts_info("success flash config with isp"); + +exit: + kfree(temp_data); + kfree(fw_packet); + return ret; +} + +/** + * goodix_fw_update_proc - firmware update process, the entry of + * firmware update flow + * @fwu_ctrl: firmware control + * return: 0 ok, < 0 error + */ +static 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 r = 0; + + if (fwu_ctrl->status == UPSTA_PREPARING || + fwu_ctrl->status == UPSTA_UPDATING) { + ts_err("Firmware update already in progress"); + return -EINVAL; + } + + fwu_ctrl->progress = 0; + fwu_ctrl->status = UPSTA_PREPARING; + + r = goodix_parse_firmware(&fwu_ctrl->fw_data); + if (r < 0) { + fwu_ctrl->status = UPSTA_ABORT; + goto err_parse_fw; + } + + fwu_ctrl->progress = 10; + if (!(fwu_ctrl->mode & UPDATE_MODE_FORCE)) { + r = goodix_check_update(fwu_ctrl->ts_dev, + &fwu_ctrl->fw_data.fw_info); + if (!r) { + fwu_ctrl->status = UPSTA_ABORT; + ts_info("fw update skiped"); + goto err_check_update; + } + } else { + ts_info("force update mode"); + } + +start_update: + fwu_ctrl->progress = 20; + fwu_ctrl->status = UPSTA_UPDATING; /* show upgrading status */ + r = goodix_update_prepare(fwu_ctrl); + if ((r == -EBUS || r == -EAGAIN) && --retry0 > 0) { + ts_err("Bus error, retry prepare ISP:%d", + FW_UPDATE_RETRY - retry0); + goto start_update; + } else if (r < 0) { + ts_err("Failed to prepare ISP, exit update:%d", r); + fwu_ctrl->status = UPSTA_FAILED; + goto err_fw_prepare; + } + + if (GOODIX_FLASH_CONFIG_WITH_ISP && + fwu_ctrl->mode & UPDATE_MODE_FLASH_CFG) { + ts_info("need flash config with isp"); + goodix_flash_config(fwu_ctrl->ts_dev); + } + + /* progress: 20%~100% */ + r = goodix_flash_firmware(fwu_ctrl->ts_dev, &fwu_ctrl->fw_data); + if ((r == -EBUS || r == -ETIMEOUT) && --retry1 > 0) { + /* we will retry[twice] if returns bus error[i2c/spi] + * we will do hardware reset and re-prepare ISP and then retry + * flashing + */ + ts_err("Bus error, retry firmware update:%d", + FW_UPDATE_RETRY - retry1); + goto start_update; + } else if (r < 0) { + ts_err("Fatal error, exit update:%d", r); + fwu_ctrl->status = UPSTA_FAILED; + goto err_fw_flash; + } + + fwu_ctrl->status = UPSTA_SUCCESS; + +err_fw_flash: +err_fw_prepare: + goodix_update_finish(fwu_ctrl->ts_dev, fwu_ctrl); +err_check_update: +err_parse_fw: + if (fwu_ctrl->status == UPSTA_SUCCESS) + ts_info("Firmware update successfully"); + else if (fwu_ctrl->status == UPSTA_FAILED) + ts_err("Firmware update failed"); + + fwu_ctrl->progress = 100; /* 100% */ + ts_info("fw update ret %d", r); + return r; +} + +/** + * 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->ts_dev->dev; + int r; + + ts_info("Request firmware image [%s]", name); + r = request_firmware(&fw_data->firmware, name, dev); + if (r < 0) + ts_err("Firmware image [%s] not available,errno:%d", name, r); + else + ts_info("Firmware image [%s] is ready", name); + return r; +} + +/** + * 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; + struct firmware *temp_firmware = NULL; + int r = -EINVAL; + + mutex_lock(&fwu_ctrl->mutex); + + if (fwu_ctrl->mode & UPDATE_MODE_SRC_HEAD) { + ts_info("Firmware header update starts"); + temp_firmware = kzalloc(sizeof(struct firmware), GFP_KERNEL); + if (!temp_firmware) { + ts_err("Failed to allocate memory for firmware"); + goto out; + } + temp_firmware->size = sizeof(goodix_default_fw); + temp_firmware->data = goodix_default_fw; + fwu_ctrl->fw_data.firmware = temp_firmware; + } else 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) { + fwu_ctrl->status = UPSTA_ABORT; + fwu_ctrl->progress = 100; + goto out; + } + } else if (fwu_ctrl->mode & UPDATE_MODE_SRC_SYSFS) { + if (!fwu_ctrl->fw_data.firmware) { + ts_err("Invalid firmware from sysfs"); + fwu_ctrl->status = UPSTA_ABORT; + fwu_ctrl->progress = 100; + r = -EINVAL; + goto out; + } + } else { + ts_err("unknown update mode 0x%x", fwu_ctrl->mode); + r = -EINVAL; + goto out; + } + + ts_debug("notify update start"); + goodix_ts_blocking_notify(NOTIFY_FWUPDATE_START, NULL); + + /* ready to update */ + ts_debug("start update proc"); + r = goodix_fw_update_proc(fwu_ctrl); + + /* clean */ + if (fwu_ctrl->mode & UPDATE_MODE_SRC_HEAD) { + kfree(fwu_ctrl->fw_data.firmware); + fwu_ctrl->fw_data.firmware = NULL; + temp_firmware = NULL; + } else if (fwu_ctrl->mode & UPDATE_MODE_SRC_REQUEST) { + goodix_release_firmware(&fwu_ctrl->fw_data); + } else if (fwu_ctrl->mode & UPDATE_MODE_SRC_SYSFS) { + vfree(fwu_ctrl->fw_data.firmware); + fwu_ctrl->fw_data.firmware = NULL; + } +out: + fwu_ctrl->mode = UPDATE_MODE_DEFAULT; + mutex_unlock(&fwu_ctrl->mutex); + + if (r) { + ts_err("fw update failed, %d", r); + goodix_ts_blocking_notify(NOTIFY_FWUPDATE_FAILED, NULL); + } else { + ts_info("fw update success"); + goodix_ts_blocking_notify(NOTIFY_FWUPDATE_SUCCESS, NULL); + } + return r; +} + +/* + * 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 device *dev, struct device_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(mode); + if (!ret) { + ts_info("success start update work"); + return count; + } + ts_err("failed start fw update work"); + return -EINVAL; +} + +static ssize_t goodix_sysfs_update_progress_show( + struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; + return scnprintf(buf, PAGE_SIZE, "%d\n", fw_ctrl->progress); +} + +static ssize_t goodix_sysfs_update_result_show( + struct device *dev, struct device_attribute *attr, + char *buf) +{ + char *result = NULL; + struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; + + ts_info("result show"); + switch (fw_ctrl->status) { + case UPSTA_NOTWORK: + result = "notwork"; + break; + case UPSTA_PREPARING: + result = "preparing"; + break; + case UPSTA_UPDATING: + result = "upgrading"; + break; + case UPSTA_ABORT: + result = "abort"; + break; + case UPSTA_SUCCESS: + result = "success"; + break; + case UPSTA_FAILED: + result = "failed"; + break; + default: + break; + } + + return scnprintf(buf, PAGE_SIZE, "%s\n", result); +} + +static ssize_t goodix_sysfs_update_fwversion_show( + struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct goodix_ts_version fw_ver; + struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; + int r = 0; + char str[5]; + + /* read version from chip */ + r = fw_ctrl->ts_dev->hw_ops->read_version(fw_ctrl->ts_dev, + &fw_ver); + if (!r) { + memcpy(str, fw_ver.pid, 4); + str[4] = '\0'; + return scnprintf(buf, PAGE_SIZE, "PID:%s VID:%02x %02x %02x %02x SENSOR_ID:%d\n", + str, fw_ver.vid[0], fw_ver.vid[1], + fw_ver.vid[2], fw_ver.vid[3], fw_ver.sensor_id); + } + return 0; +} + +static ssize_t goodix_sysfs_fwsize_show( + struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; + int r = -EINVAL; + + if (fw_ctrl && fw_ctrl->fw_data.firmware) + r = snprintf(buf, PAGE_SIZE, "%zu\n", + fw_ctrl->fw_data.firmware->size); + return r; +} + +static ssize_t goodix_sysfs_fwsize_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; + struct firmware *fw; + u8 **data; + size_t size = 0; + + if (!fw_ctrl) + return -EINVAL; + + if (sscanf(buf, "%zu", &size) < 0 || !size) { + ts_err("Failed to get fwsize"); + return -EFAULT; + } + + /* use vmalloc to alloc huge memory */ + fw = vmalloc(sizeof(*fw) + size); + if (fw == NULL) { + ts_err("Failed to alloc memory,size:%zu", + size + sizeof(*fw)); + return -ENOMEM; + } + mutex_lock(&fw_ctrl->mutex); + memset(fw, 0x00, sizeof(*fw) + size); + data = (u8 **)&fw->data; + *data = (u8 *)fw + sizeof(struct firmware); + fw->size = size; + fw_ctrl->fw_data.firmware = fw; + fw_ctrl->mode = UPDATE_MODE_SRC_SYSFS; + mutex_unlock(&fw_ctrl->mutex); + return count; +} + +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 fw_update_ctrl *fw_ctrl = &goodix_fw_update_ctrl; + struct firmware_data *fw_data; + + fw_data = &fw_ctrl->fw_data; + + if (!fw_data->firmware) { + ts_err("Need set fw image size first"); + return -ENOMEM; + } + + if (fw_data->firmware->size == 0) { + ts_err("Invalid firmware size"); + return -EINVAL; + } + + if (pos + count > fw_data->firmware->size) + return -EFAULT; + mutex_lock(&fw_ctrl->mutex); + memcpy((u8 *)&fw_data->firmware->data[pos], buf, count); + mutex_unlock(&fw_ctrl->mutex); + return count; +} + +/* this interface has ben deprecated */ +static ssize_t goodix_sysfs_force_update_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static DEVICE_ATTR(update_en, 0220, NULL, goodix_sysfs_update_en_store); +static DEVICE_ATTR(progress, 0444, goodix_sysfs_update_progress_show, NULL); +static DEVICE_ATTR(result, 0444, goodix_sysfs_update_result_show, NULL); +static DEVICE_ATTR(fwversion, 0444, + goodix_sysfs_update_fwversion_show, NULL); +static DEVICE_ATTR(fwsize, 0664, goodix_sysfs_fwsize_show, + goodix_sysfs_fwsize_store); +static DEVICE_ATTR(force_update, 0220, NULL, + goodix_sysfs_force_update_store); + +static struct attribute *goodix_fwu_attrs[] = { + &dev_attr_update_en.attr, + &dev_attr_progress.attr, + &dev_attr_result.attr, + &dev_attr_fwversion.attr, + &dev_attr_fwsize.attr, + &dev_attr_force_update.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 = S_IRUGO | S_IWUGO; + 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); +} + +int goodix_do_fw_update(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; + 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; + } else { + /* 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 = 0; + struct goodix_ts_board_data *ts_bdata = board_data(core_data); + + if (goodix_fw_update_ctrl.initialized) { + ts_info("no need reinit"); + return ret; + } + + if (!core_data || !ts_bdata || !core_data->ts_dev) { + ts_err("core_data && ts_dev cann't be null"); + return -ENODEV; + } + + mutex_init(&goodix_fw_update_ctrl.mutex); + + mutex_lock(&goodix_fw_update_ctrl.mutex); + + goodix_fw_update_ctrl.ts_dev = core_data->ts_dev; + goodix_fw_update_ctrl.core_data = core_data; + goodix_fw_update_ctrl.mode = 0; + /* find a valid firmware image name */ + if (ts_bdata && ts_bdata->fw_name) + strlcpy(goodix_fw_update_ctrl.fw_name, ts_bdata->fw_name, + sizeof(goodix_fw_update_ctrl.fw_name)); + else + strlcpy(goodix_fw_update_ctrl.fw_name, TS_DEFAULT_FIRMWARE, + 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"); + goto err_out; + } + + goodix_fw_update_ctrl.initialized = 1; +err_out: + mutex_unlock(&goodix_fw_update_ctrl.mutex); + return ret; +} + +void goodix_fw_update_uninit(void) +{ + mutex_lock(&goodix_fw_update_ctrl.mutex); + if (goodix_fw_update_ctrl.initialized) { + goodix_fw_sysfs_remove(); + goodix_fw_update_ctrl.initialized = 0; + } + mutex_unlock(&goodix_fw_update_ctrl.mutex); + mutex_destroy(&goodix_fw_update_ctrl.mutex); +} + diff --git a/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_core.c b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_core.c new file mode 100644 index 000000000000..c1af962b0c43 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_core.c @@ -0,0 +1,2445 @@ + /* + * Goodix Touchscreen Driver + * Core layer of touchdriver architecture. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_FB +#include +#include +#endif + +#include "goodix_ts_core.h" + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38) +#include +#define INPUT_TYPE_B_PROTOCOL +#endif + +#define GOOIDX_INPUT_PHYS "goodix_ts/input0" +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" + +static int goodix_ts_remove(struct platform_device *pdev); +int goodix_start_later_init(struct goodix_ts_core *ts_core); +void goodix_ts_dev_release(void); +int goodix_fw_update_init(struct goodix_ts_core *core_data); +void goodix_fw_update_uninit(void); +extern int goodix_ts_mmi_dev_register(struct platform_device *ts_device); +extern void goodix_ts_mmi_dev_unregister(struct platform_device *ts_device); +extern int __init goodix_gsx_gesture_init(void); +extern void __exit goodix_gsx_gesture_exit(void); +extern int __init goodix_tools_init(void); +extern void __exit goodix_tools_exit(void); + +struct goodix_module goodix_modules; + +#define CORE_MODULE_UNPROBED 0 +#define CORE_MODULE_PROB_SUCCESS 1 +#define CORE_MODULE_PROB_FAILED -1 +#define CORE_MODULE_REMOVED -2 +int core_module_prob_sate = CORE_MODULE_UNPROBED; + +#define GTP_VTG_MIN_UV 2800000 +#define GTP_VTG_MAX_UV 3300000 + +/** + * __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; + + ts_info("__do_register_ext_module IN"); + /* 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"); + if (core_module_prob_sate == CORE_MODULE_PROB_FAILED + || core_module_prob_sate == CORE_MODULE_REMOVED) { + ts_err("core layer state error %d", core_module_prob_sate); + return; + } + + if (core_module_prob_sate == CORE_MODULE_UNPROBED) { + /* waitting for core layer */ + if (!wait_for_completion_timeout(&goodix_modules.core_comp, + 25 * HZ)) { + ts_err("Module [%s] timeout", module->name); + return; + } + } + + /* 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); + init_completion(&goodix_modules.core_comp); +} + +/** + * 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("goodix_register_ext_module IN"); + + goodix_core_module_init(); + INIT_WORK(&module->work, goodix_register_ext_module_work); + schedule_work(&module->work); + + ts_info("goodix_register_ext_module OUT"); + return 0; +} +EXPORT_SYMBOL_GPL(goodix_register_ext_module); + +/** + * 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("goodix_register_ext_module_no_wait 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); +} +EXPORT_SYMBOL_GPL(goodix_register_ext_module_no_wait); + +/* remove all registered ext module + * return 0 on success, otherwise return < 0 + */ +int goodix_unregister_all_module(void) +{ + int ret = 0; + struct goodix_ext_module *ext_module, *next; + + if (!goodix_modules.initilized) + return 0; + + if (!goodix_modules.core_data) + return 0; + + mutex_lock(&goodix_modules.mutex); + if (list_empty(&goodix_modules.head)) { + mutex_unlock(&goodix_modules.mutex); + return 0; + } + + list_for_each_entry_safe(ext_module, next, + &goodix_modules.head, list) { + if (ext_module->funcs && ext_module->funcs->exit) { + ret = ext_module->funcs->exit(goodix_modules.core_data, + ext_module); + if (ret) { + ts_err("failed register ext module, %d:%s", + ret, ext_module->name); + break; + } + } + ts_info("remove module: %s", ext_module->name); + list_del(&ext_module->list); + } + mutex_unlock(&goodix_modules.mutex); + return ret; +} + +/** + * 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 -EFAULT; + } + + if (!found) { + ts_err("Module [%s] never registed", + module->name); + mutex_unlock(&goodix_modules.mutex); + return -EFAULT; + } + + 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; +} +EXPORT_SYMBOL_GPL(goodix_unregister_ext_module); + +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; +} +EXPORT_SYMBOL_GPL(goodix_get_default_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; +} +EXPORT_SYMBOL_GPL(goodix_get_default_kobj); + +/* show external module infomation */ +static ssize_t goodix_ts_extmod_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct goodix_ext_module *module, *next; + size_t offset = 0; + int r; + + mutex_lock(&goodix_modules.mutex); + if (!list_empty(&goodix_modules.head)) { + list_for_each_entry_safe(module, next, + &goodix_modules.head, list) { + r = snprintf(&buf[offset], PAGE_SIZE, + "priority:%u module:%s\n", + module->priority, module->name); + if (r < 0) { + mutex_unlock(&goodix_modules.mutex); + return -EINVAL; + } + offset += r; + } + } + + mutex_unlock(&goodix_modules.mutex); + return offset; +} + +/* show driver infomation */ +static ssize_t goodix_ts_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 goodix_ts_chip_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct goodix_ts_core *core_data = + dev_get_drvdata(dev); + struct goodix_ts_device *ts_dev = core_data->ts_dev; + struct goodix_ts_version chip_ver; + int r, cnt = 0; + + cnt += snprintf(buf, PAGE_SIZE, "TouchDeviceName:%s\n", ts_dev->name); + if (ts_dev->hw_ops->read_version) { + r = ts_dev->hw_ops->read_version(ts_dev, &chip_ver); + if (!r && chip_ver.valid) { + cnt += snprintf(&buf[cnt], PAGE_SIZE, + "PID:%s\nVID:%02x%02x%02x%02x\nSensID:%02x\n", + chip_ver.pid, chip_ver.vid[0], + chip_ver.vid[1], chip_ver.vid[2], + chip_ver.vid[3], chip_ver.sensor_id); + } + } + + 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_device *ts_dev = core_data->ts_dev; + int en; + + if (sscanf(buf, "%d", &en) != 1) + return -EINVAL; + + if (en != 1) + return -EINVAL; + + if (ts_dev->hw_ops->reset) + ts_dev->hw_ops->reset(ts_dev); + return count; + +} + +static ssize_t goodix_ts_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_device *ts_dev = core_data->ts_dev; + int ret, i, offset; + char *cfg_buf; + + cfg_buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!cfg_buf) + return -ENOMEM; + + if (ts_dev->hw_ops->read_config) + ret = ts_dev->hw_ops->read_config(ts_dev, cfg_buf, PAGE_SIZE); + else + ret = -EINVAL; + + if (ret <= 0) { + kfree(cfg_buf); + return -EINVAL; + } + + offset = 0; + for (i = 0; i < ret && offset < PAGE_SIZE - 5; i++) { + if (i != 0 && i % 20 == 0) + buf[offset++] = '\n'; + if (offset + 3 < PAGE_SIZE - 1) { + offset += snprintf(&buf[offset], PAGE_SIZE - offset, + "%02x,", cfg_buf[i]); + } else { + offset += snprintf(&buf[offset], PAGE_SIZE - offset, "EOF"); + break; + } + } + + kfree(cfg_buf); + 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, + unsigned char *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\n", 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\n", + 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; +} + +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_device *ts_dev = core_data->ts_dev; + int en, r; + const struct firmware *cfg_img; + struct goodix_ts_config *config = NULL; + + if (sscanf(buf, "%d", &en) != 1) + return -EINVAL; + + if (en != 1) + return -EINVAL; + + disable_irq(core_data->irq); + + /*request configuration*/ + r = request_firmware(&cfg_img, GOODIX_DEFAULT_CFG_NAME, dev); + if (r < 0) { + ts_err("cfg file [%s] not available,errno:%d", + GOODIX_DEFAULT_CFG_NAME, r); + goto exit; + } else + ts_info("cfg file [%s] is ready", GOODIX_DEFAULT_CFG_NAME); + + config = kzalloc(sizeof(*config), GFP_KERNEL); + if (config == NULL) + goto exit; + + /*parse cfg data*/ + if (goodix_ts_convert_0x_data(cfg_img->data, cfg_img->size, + config->data, &config->length)) { + ts_err("convert config data FAILED"); + goto exit; + } + + config->reg_base = ts_dev->reg.cfg_addr; + mutex_init(&config->lock); + config->initialized = TS_CFG_STABLE; + + if (ts_dev->hw_ops->send_config) + ts_dev->hw_ops->send_config(ts_dev, config); + +exit: + enable_irq(core_data->irq); + kfree(config); + config = NULL; + if (cfg_img) { + release_firmware(cfg_img); + cfg_img = NULL; + } + + return count; +} + +/* 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"); + 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); + int en; + + if (sscanf(buf, "%d", &en) != 1) + return -EINVAL; + + goodix_ts_irq_enable(core_data, en); + return count; +} + +/*reg read/write */ +static u16 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) +{ + int ret; + struct goodix_ts_core *core_data = dev_get_drvdata(dev); + struct goodix_ts_device *ts_dev = core_data->ts_dev; + + if (!rw_addr || !rw_len) { + ts_err("address(0x%x) and length(%d) cann't be null\n", + 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 = ts_dev->hw_ops->read(ts_dev, rw_addr, show_buf, rw_len); + if (ret) { + ts_err("failed read addr(%x) length(%d)\n", 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_device *ts_dev = core_data->ts_dev; + char *pos = NULL, *token = NULL; + long result = 0; + int ret, i; + + if (!buf || !count) { + ts_err("invalid params\n"); + 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'\n"); + goto err_out; + } + + /* get addr */ + pos = (char *)buf; + pos += 2; + token = strsep(&pos, ":"); + if (!token) { + ts_err("invalid address info\n"); + goto err_out; + } else { + if (kstrtol(token, 16, &result)) { + ts_err("failed get addr info\n"); + goto err_out; + } + rw_addr = (u16)result; + ts_info("rw addr is 0x%x\n", rw_addr); + } + + /* get length */ + token = strsep(&pos, ":"); + if (!token) { + ts_err("invalid length info\n"); + goto err_out; + } else { + if (kstrtol(token, 0, &result)) { + ts_err("failed get length info\n"); + goto err_out; + } + rw_len = (u32)result; + ts_info("rw length info is %d\n", rw_len); + if (rw_len > sizeof(store_buf)) { + ts_err("data len > %lu\n", 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\n"); + goto err_out; + } else { + if (kstrtol(token, 16, &result)) { + ts_err("failed get data[%d] info\n", i); + goto err_out; + } + store_buf[i] = (u8)result; + ts_info("get data[%d]=0x%x\n", i, store_buf[i]); + } + } + ret = ts_dev->hw_ops->write(ts_dev, rw_addr, store_buf, rw_len); + if (ret) { + ts_err("failed write addr(%x) data %*ph\n", rw_addr, + rw_len, store_buf); + goto err_out; + } + + ts_info("%s write to addr (%x) with data %*ph\n", + "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; +} + +static DEVICE_ATTR(extmod_info, S_IRUGO, goodix_ts_extmod_show, NULL); +static DEVICE_ATTR(driver_info, S_IRUGO, goodix_ts_driver_info_show, NULL); +static DEVICE_ATTR(chip_info, S_IRUGO, goodix_ts_chip_info_show, NULL); +static DEVICE_ATTR(reset, S_IWUSR | S_IWGRP, NULL, goodix_ts_reset_store); +static DEVICE_ATTR(send_cfg, S_IWUSR | S_IWGRP, NULL, goodix_ts_send_cfg_store); +static DEVICE_ATTR(read_cfg, S_IRUGO, goodix_ts_read_cfg_show, NULL); +static DEVICE_ATTR(irq_info, S_IRUGO | S_IWUSR | S_IWGRP, + goodix_ts_irq_info_show, goodix_ts_irq_info_store); +static DEVICE_ATTR(reg_rw, S_IRUGO | S_IWUSR | S_IWGRP, + goodix_ts_reg_rw_show, goodix_ts_reg_rw_store); + +static struct attribute *sysfs_attrs[] = { + &dev_attr_extmod_info.attr, + &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_irq_info.attr, + &dev_attr_reg_rw.attr, + NULL, +}; + +static const struct attribute_group sysfs_group = { + .attrs = sysfs_attrs, +}; + +static ssize_t goodix_sysfs_config_write(struct file *file, + struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t pos, size_t count) +{ + struct platform_device *pdev = container_of(kobj_to_dev(kobj), + struct platform_device, dev); + struct goodix_ts_core *ts_core = platform_get_drvdata(pdev); + struct goodix_ts_device *ts_dev = ts_core->ts_dev; + struct goodix_ts_config *config = NULL; + int ret; + + if (pos != 0 || count > GOODIX_CFG_MAX_SIZE) { + ts_info("pos(%d) != 0, cfg size %zu", (int)pos, count); + return -EINVAL; + } + + config = kzalloc(sizeof(struct goodix_ts_config), GFP_KERNEL); + if (config == NULL) + return -ENOMEM; + + memcpy(config->data, buf, count); + config->length = count; + config->reg_base = ts_dev->reg.cfg_addr; + mutex_init(&config->lock); + config->initialized = true; + + ret = ts_dev->hw_ops->send_config(ts_dev, config); + if (ret) { + count = -EINVAL; + ts_err("send config failed %d", ret); + } else { + ts_info("send config success"); + } + + kfree(config); + return count; +} + +static ssize_t goodix_sysfs_config_read(struct file *file, + struct kobject *kobj, struct bin_attribute *attr, + char *buf, loff_t pos, size_t size) +{ + struct platform_device *pdev = container_of(kobj_to_dev(kobj), + struct platform_device, dev); + struct goodix_ts_core *ts_core = platform_get_drvdata(pdev); + struct goodix_ts_device *ts_dev = ts_core->ts_dev; + int ret; + + ts_debug("pos = %d, size = %zu", (int)pos, size); + + if (pos != 0) + return 0; + + if (ts_dev->hw_ops->read_config) + ret = ts_dev->hw_ops->read_config(ts_dev, buf, PAGE_SIZE); + else + ret = -EINVAL; + + ts_debug("read config ret %d", ret); + return ret; +} + +static struct bin_attribute goodix_config_bin_attr = { + .attr = { + .name = "config_bin", + .mode = S_IRUGO | S_IWUSR | S_IWGRP, + }, + .size = GOODIX_CFG_MAX_SIZE, + .read = goodix_sysfs_config_read, + .write = goodix_sysfs_config_write, +}; + +static int goodix_ts_sysfs_init(struct goodix_ts_core *core_data) +{ + int ret; + + ret = sysfs_create_bin_file(&core_data->pdev->dev.kobj, + &goodix_config_bin_attr); + if (ret) { + ts_err("failed create config bin attr"); + return ret; + } + + ret = sysfs_create_group(&core_data->pdev->dev.kobj, &sysfs_group); + if (ret) { + ts_err("failed create core sysfs group"); + sysfs_remove_bin_file(&core_data->pdev->dev.kobj, + &goodix_config_bin_attr); + return ret; + } + + return ret; +} + +static void goodix_ts_sysfs_exit(struct goodix_ts_core *core_data) +{ + sysfs_remove_bin_file(&core_data->pdev->dev.kobj, + &goodix_config_bin_attr); + sysfs_remove_group(&core_data->pdev->dev.kobj, &sysfs_group); +} + +/* proc node */ +static ssize_t goodix_lockdown_info_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct goodix_ts_core *cd = PDE_DATA(file_inode(file)); + int cnt; +#define TP_INFO_MAX_LENGTH 50 + u8 temp_buf[TP_INFO_MAX_LENGTH]; + + if (*pos != 0) + return 0; + + cnt = snprintf(temp_buf, TP_INFO_MAX_LENGTH, + "%02d%02d%02d%02d%02d%02d%02d%02d\n", + cd->lockdown_info[0], cd->lockdown_info[1], + cd->lockdown_info[2], cd->lockdown_info[3], + cd->lockdown_info[4], cd->lockdown_info[5], + cd->lockdown_info[6], cd->lockdown_info[7]); + + return simple_read_from_buffer(buf, count, pos, temp_buf, strlen(temp_buf)); +} + +static const struct file_operations goodix_lockdown_info_ops = { + .read = goodix_lockdown_info_read, + .open = simple_open, + .owner = THIS_MODULE, +}; + +static ssize_t goodix_fw_version_info_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct goodix_ts_core *cd = PDE_DATA(file_inode(file)); + struct goodix_ts_version fw_ver; + u8 temp_buf[100] = {0}; + int ret; + char pid[5] = {0}; + + if (*pos != 0) + return 0; + ret = cd->ts_dev->hw_ops->read_version(cd->ts_dev, &fw_ver); + if (ret) + return 0; + memcpy(pid, fw_ver.pid, 4); + snprintf(temp_buf, 100, "[IC]GT%s\n", pid); + + return simple_read_from_buffer(buf, count, pos, temp_buf, strlen(temp_buf)); +} + +static const struct file_operations goodix_fw_version_info_ops = { + .read = goodix_fw_version_info_read, + .open = simple_open, + .owner = THIS_MODULE, +}; + +static ssize_t gtp_selftest_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct goodix_ts_core *cd = PDE_DATA(file_inode(file)); + struct device *dev = &cd->pdev->dev; + struct ts_rawdata_info *info; + int ret; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ts_err("alloc memory failed"); + return 0; + } + + ret = gtx8_get_rawdata(dev, info); + if (ret < 0) { + ts_err("goodix_test failed"); + kfree(info); + return 0; + } + strlcat(info->result, "\n", sizeof(info->result)); + ret = simple_read_from_buffer(buf, count, pos, info->result, strlen(info->result)); + kfree(info); + + return ret; +} + +static const struct file_operations gtp_selftest_ops = { + .read = gtp_selftest_read, + .open = simple_open, + .owner = THIS_MODULE, +}; + +static ssize_t gtp_gesture_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct goodix_ts_core *cd = PDE_DATA(file_inode(file)); + u8 temp_buf[10] = {0}; + + snprintf(temp_buf, 10, "%d\n", cd->gesture_enable); + + return simple_read_from_buffer(buf, count, pos, temp_buf, strlen(temp_buf)); +} + +static ssize_t gtp_gesture_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + struct goodix_ts_core *cd = PDE_DATA(file_inode(file)); + char temp_buf[5] = {0}; + long temp_val; + + if (copy_from_user(temp_buf, buf, count)) { + ts_err("read proc input error"); + return count; + } + + if (kstrtol(temp_buf, 10, &temp_val)) { + ts_err("param is invalid"); + return count; + } + if (temp_val == 1) + cd->gesture_enable = 1; + else if (temp_val == 0) + cd->gesture_enable = 0; + else + ts_err("param must 0 or 1"); + + ts_info("gesture_enable:%d", cd->gesture_enable); + return count; +} + +static const struct file_operations gtp_gesture_ops = { + .read = gtp_gesture_read, + .write = gtp_gesture_write, + .open = simple_open, + .owner = THIS_MODULE, +}; + +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, rx, i; + int index; + int ret; + + if (!m || !v || !cd) { + ts_err("rawdata_proc_show, input null ptr"); + return -EIO; + } + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ts_err("Failed to alloc rawdata info memory"); + return -ENOMEM; + } + + ret = cd->ts_dev->hw_ops->get_capacitance_data(cd->ts_dev, 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_printf(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_printf(m, "\n"); + } + seq_printf(m, "mutual_diffdata:\n"); + index += tx * rx; + for (i = 0; i < tx * rx; i++) { + seq_printf(m, "%4d,", info->buff[index + i]); + if ((i + 1) % tx == 0) + seq_printf(m, "\n"); + } + +exit: + kfree(info); + return ret; +} + +static int goodix_rawdata_info_open(struct inode *inode, struct file *file) +{ + return single_open(file, rawdata_proc_show, PDE_DATA(inode)); +} + +static const struct file_operations goodix_rawdata_info_ops = { + .open = goodix_rawdata_info_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int goodix_ts_procfs_init(struct goodix_ts_core *core_data) +{ + proc_create_data("tp_data_dump", 0666, NULL, &goodix_rawdata_info_ops, core_data); + proc_create_data("tp_lockdown_info", 0666, NULL, &goodix_lockdown_info_ops, core_data); + proc_create_data("tp_info", 0666, NULL, &goodix_fw_version_info_ops, core_data); + proc_create_data("tp_selftest", 0666, NULL, >p_selftest_ops, core_data); + proc_create_data("tp_gesture", 0666, NULL, >p_gesture_ops, core_data); + + return 0; +} + +/* 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); +} +EXPORT_SYMBOL(goodix_ts_register_notifier); + +/** + * 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); +} +EXPORT_SYMBOL(goodix_ts_unregister_notifier); + +/** + * 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; +} +EXPORT_SYMBOL_GPL(goodix_ts_blocking_notify); + +static void goodix_ts_report_pen(struct input_dev *dev, + struct goodix_pen_data *pen_data) +{ + int i; + + if (pen_data->coords.status == TS_TOUCH) { + input_report_key(dev, BTN_TOUCH, 1); + input_report_key(dev, pen_data->coords.tool_type, 1); + } else if (pen_data->coords.status == TS_RELEASE) { + input_report_key(dev, BTN_TOUCH, 0); + input_report_key(dev, pen_data->coords.tool_type, 0); + } + if (pen_data->coords.status) { + 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); + } + /* report pen button */ + for (i = 0; i < GOODIX_MAX_PEN_KEY; i++) { + if (!pen_data->keys[i].status) + continue; + if (pen_data->keys[i].status == TS_TOUCH) + input_report_key(dev, pen_data->keys[i].code, 1); + else if (pen_data->keys[i].status == TS_RELEASE) + input_report_key(dev, pen_data->keys[i].code, 0); + } + input_sync(dev); +} + +static void goodix_ts_report_finger(struct input_dev *dev, + struct goodix_touch_data *touch_data) +{ + unsigned int touch_num = touch_data->touch_num; + static u32 pre_fin; + int i; + + /*first touch down and last touch up condition*/ + if (touch_num && !pre_fin) + input_report_key(dev, BTN_TOUCH, 1); + else if (!touch_num && pre_fin) + input_report_key(dev, BTN_TOUCH, 0); + + pre_fin = touch_num; + + for (i = 0; i < GOODIX_MAX_TOUCH; i++) { + if (!touch_data->coords[i].status) + continue; + if (touch_data->coords[i].status == TS_RELEASE) { + input_mt_slot(dev, i); + input_mt_report_slot_state(dev, MT_TOOL_FINGER, false); + continue; + } + + 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); + } + + /* report panel key */ + for (i = 0; i < GOODIX_MAX_TP_KEY; i++) { + if (!touch_data->keys[i].status) + continue; + if (touch_data->keys[i].status == TS_TOUCH) + input_report_key(dev, touch_data->keys[i].code, 1); + else if (touch_data->keys[i].status == TS_RELEASE) + input_report_key(dev, touch_data->keys[i].code, 0); + } + input_sync(dev); +} + +/** + * 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_device *ts_dev = core_data->ts_dev; + struct goodix_ext_module *ext_module, *next; + struct goodix_ts_event *ts_event = &core_data->ts_event; + u8 irq_flag = 0; + int r; + + 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; + r = ext_module->funcs->irq_event(core_data, ext_module); + if (r == EVT_CANCEL_IRQEVT) { + mutex_unlock(&goodix_modules.mutex); + return IRQ_HANDLED; + } + } + mutex_unlock(&goodix_modules.mutex); + + /* read touch data from touch device */ + r = ts_dev->hw_ops->event_handler(ts_dev, ts_event); + if (likely(r >= 0)) { + if (ts_event->event_type == EVENT_TOUCH) { + /* report touch */ + goodix_ts_report_finger(core_data->input_dev, + &ts_event->touch_data); + } + if (ts_dev->board_data.pen_enable && + ts_event->event_type == EVENT_PEN) { + goodix_ts_report_pen(core_data->pen_dev, + &ts_event->pen_data); + } + } + + /* clean irq flag */ + irq_flag = 0; + ts_dev->hw_ops->write_trans(ts_dev, ts_dev->reg.coor, &irq_flag, 1); + + return IRQ_HANDLED; +} + +/** + * goodix_ts_init_irq - Requset interrput line from system + * @core_data: pointer to touch core data + * return: 0 ok, <0 failed + */ +int goodix_ts_irq_setup(struct goodix_ts_core *core_data) +{ + const struct goodix_ts_board_data *ts_bdata = board_data(core_data); + int r; + + /* if ts_bdata-> irq is invalid */ + if (ts_bdata->irq <= 0) + core_data->irq = gpio_to_irq(ts_bdata->irq_gpio); + else + core_data->irq = ts_bdata->irq; + + ts_info("IRQ:%u,flags:%d", core_data->irq, (int)ts_bdata->irq_flags); + r = devm_request_threaded_irq(&core_data->pdev->dev, + core_data->irq, NULL, + goodix_ts_threadirq_func, + ts_bdata->irq_flags | IRQF_ONESHOT, + GOODIX_CORE_DRIVER_NAME, + core_data); + if (r < 0) + ts_err("Failed to requeset threaded irq:%d", r); + else + atomic_set(&core_data->irq_enabled, 1); + + return r; +} + +/** + * goodix_ts_irq_enable - Enable/Disable a irq + * @core_data: pointer to touch core data + * enable: enable or disable irq + * return: 0 ok, <0 failed + */ +int goodix_ts_irq_enable(struct goodix_ts_core *core_data, + bool enable) +{ + if (enable) { + if (!atomic_cmpxchg(&core_data->irq_enabled, 0, 1)) { + enable_irq(core_data->irq); + ts_debug("Irq enabled"); + } + } else { + if (atomic_cmpxchg(&core_data->irq_enabled, 1, 0)) { + disable_irq(core_data->irq); + ts_debug("Irq disabled"); + } + } + + return 0; +} +EXPORT_SYMBOL(goodix_ts_irq_enable); + +/** + * 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; + struct device *dev = NULL; + int r = 0; + + ts_info("Power init"); + /* dev:i2c client device or spi slave device*/ + dev = core_data->ts_dev->dev; + ts_bdata = board_data(core_data); + + 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)) { + r = PTR_ERR(core_data->avdd); + ts_err("Failed to get regulator avdd:%d", r); + core_data->avdd = NULL; + return r; + } + r = regulator_set_load(core_data->avdd, 50000); + if (r) { + ts_err("set avdd load fail"); + return r; + } + r = regulator_set_voltage(core_data->avdd, GTP_VTG_MIN_UV, GTP_VTG_MAX_UV); + if (r) { + ts_err("set avdd voltage fail"); + return r; + } + } 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)) { + r = PTR_ERR(core_data->iovdd); + ts_err("Failed to get regulator iovdd:%d", r); + core_data->iovdd = NULL; + } + r = regulator_set_voltage(core_data->iovdd, 1800000, 1800000); + if (r) { + ts_err("set iovdd voltage fail"); + return r; + } + } else { + ts_info("iovdd name is NULL"); + } + + return r; +} + +/** + * 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 *core_data) +{ + int r = 0; + + ts_info("Device power on"); + if (core_data->power_on) + return 0; + + if (!core_data->avdd) { + core_data->power_on = 1; + return 0; + } + + if (core_data->avdd) { + r = regulator_enable(core_data->avdd); + if (!r) { + ts_info("regulator enable SUCCESS"); + } else { + ts_err("Failed to enable avdd:%d", r); + goto err_exit; + } + } + + if (core_data->iovdd) { + r = regulator_enable(core_data->iovdd); + if (r) { + ts_err("Failed to enable iovdd:%d", r); + if (core_data->avdd) + regulator_disable(core_data->avdd); + goto err_exit; + } + } + usleep_range(3000, 3100); + core_data->power_on = 1; +err_exit: + return 0; +} + +/** + * 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 *core_data) +{ + int r; + + ts_info("Device power off"); + if (!core_data->power_on) + return 0; + + if (core_data->avdd) { + r = regulator_disable(core_data->avdd); + if (!r) { + ts_info("regulator disable SUCCESS"); + } else { + ts_err("Failed to disable analog power:%d", r); + goto err_exit; + } + } + + if (core_data->iovdd) { + r = regulator_disable(core_data->iovdd); + if (r) { + ts_err("Failed to disable iovdd:%d", r); + goto err_exit; + } + } + + core_data->power_on = 0; +err_exit: + return r; +} + +#ifdef CONFIG_PINCTRL +/** + * goodix_ts_pinctrl_init - Get pinctrl handler and pinctrl_state + * @core_data: pointer to touch core data + * return: 0 ok, <0 failed + */ +static int goodix_ts_pinctrl_init(struct goodix_ts_core *core_data) +{ + int r = 0; + + /* get pinctrl handler from of node */ + core_data->pinctrl = devm_pinctrl_get(core_data->ts_dev->dev); + if (IS_ERR_OR_NULL(core_data->pinctrl)) { + ts_info("Failed to get pinctrl handler[need confirm]"); + core_data->pinctrl = NULL; + return -EINVAL; + } + ts_debug("success get pinctrl"); + /* active state */ + core_data->pin_sta_active = pinctrl_lookup_state(core_data->pinctrl, + PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(core_data->pin_sta_active)) { + r = PTR_ERR(core_data->pin_sta_active); + ts_err("Failed to get pinctrl state:%s, r:%d", + PINCTRL_STATE_ACTIVE, r); + core_data->pin_sta_active = NULL; + goto exit_pinctrl_put; + } + ts_debug("success get avtive pinctrl state"); + + /* suspend state */ + core_data->pin_sta_suspend = pinctrl_lookup_state(core_data->pinctrl, + PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(core_data->pin_sta_suspend)) { + r = PTR_ERR(core_data->pin_sta_suspend); + ts_err("Failed to get pinctrl state:%s, r:%d", + PINCTRL_STATE_SUSPEND, r); + core_data->pin_sta_suspend = NULL; + goto exit_pinctrl_put; + } + ts_debug("success get suspend pinctrl state"); + + return 0; +exit_pinctrl_put: + devm_pinctrl_put(core_data->pinctrl); + core_data->pinctrl = NULL; + return r; +} +#endif + +/** + * goodix_ts_gpio_setup - Request gpio resources from GPIO subsysten + * reset_gpio and irq_gpio number are obtained from goodix_ts_device + * which created in hardware layer driver. e.g.goodix_xx_i2c.c + * A goodix_ts_device should set those two fileds to right value + * before registed to touch core driver. + * @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_HIGH, "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; + } + + return 0; +} + +/** + * goodix_input_set_params - set input parameters + */ +static void goodix_ts_set_input_params(struct input_dev *input_dev, + struct goodix_ts_board_data *ts_bdata) +{ + int i; + + if (ts_bdata->swap_axis) + swap(ts_bdata->panel_max_x, ts_bdata->panel_max_y); + + 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); + + if (ts_bdata->panel_max_key) { + for (i = 0; i < ts_bdata->panel_max_key; i++) + input_set_capability(input_dev, EV_KEY, + ts_bdata->panel_key_map[i]); + } +} + +/** + * goodix_ts_input_dev_config - Requset and config a input device + * then register it to input sybsystem. + * NOTE that some hardware layer may provide a input device + * (ts_dev->input_dev not NULL). + * @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; + + __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); + +#ifdef INPUT_PROP_DIRECT + __set_bit(INPUT_PROP_DIRECT, input_dev->propbit); +#endif + + /* set input parameters */ + goodix_ts_set_input_params(input_dev, ts_bdata); + +#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); + + 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(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, + GOODIX_PEN_MAX_PRESSURE, 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 *core = container_of(ts_esd, + struct goodix_ts_core, ts_esd); + const struct goodix_ts_hw_ops *hw_ops = ts_hw_ops(core); + u8 data = GOODIX_ESD_TICK_WRITE_DATA; + int r = 0; + + if (!atomic_read(&ts_esd->esd_on)) + return; + + if (hw_ops->check_hw) + r = hw_ops->check_hw(core->ts_dev); + if (r < 0) { + goodix_ts_power_off(core); + goodix_ts_power_on(core); + if (hw_ops->reset) + hw_ops->reset(core->ts_dev); + + /*init dynamic esd*/ + r = hw_ops->write_trans(core->ts_dev, core->ts_dev->reg.esd, + &data, 1); + if (r < 0) + ts_err("failed init dynamic esd"); + } else { + /*init dynamic esd*/ + r = hw_ops->write_trans(core->ts_dev, + core->ts_dev->reg.esd, + &data, 1); + if (r < 0) + ts_err("failed init watch dog"); + } + + 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 *core) +{ + struct goodix_ts_esd *ts_esd = &core->ts_esd; + + if (core->ts_dev->reg.esd == 0) + 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 *core) +{ + struct goodix_ts_esd *ts_esd = &core->ts_esd; + int ret; + + 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 *core) +{ + struct goodix_ts_esd *ts_esd = &core->ts_esd; + struct goodix_ts_device *dev = core->ts_dev; + u8 data = GOODIX_ESD_TICK_WRITE_DATA; + int r; + + if (!dev->hw_ops->check_hw || !dev->reg.esd) { + 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 = core; + 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); + + /*init dynamic esd*/ + r = dev->hw_ops->write_trans(core->ts_dev, core->ts_dev->reg.esd, + &data, 1); + if (r < 0) + ts_err("failed init dynamic esd[ignore]"); + + goodix_ts_esd_on(core); + + return 0; +} + +static void goodix_ts_release_connects(struct goodix_ts_core *core_data) +{ + struct input_dev *input_dev = core_data->input_dev; + struct input_mt *mt = input_dev->mt; + int i; + + memset(&core_data->ts_event, 0, sizeof(core_data->ts_event)); + goodix_ts_report_finger(core_data->input_dev, + &core_data->ts_event.touch_data); + + if (mt) { + for (i = 0; i < mt->num_slots; 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); + } +} + +/** + * 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_device *ts_dev = core_data->ts_dev; + int r; + + ts_info("Suspend start"); + + /* + * 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; + + r = ext_module->funcs->before_suspend(core_data, + ext_module); + if (r == EVT_CANCEL_SUSPEND) { + mutex_unlock(&goodix_modules.mutex); + ts_info("Canceled by module:%s", + ext_module->name); + goto out; + } + } + } + mutex_unlock(&goodix_modules.mutex); + + /* disable irq */ + goodix_ts_irq_enable(core_data, false); + + /* let touch ic work in sleep mode */ + if (ts_dev && ts_dev->hw_ops->suspend) + ts_dev->hw_ops->suspend(ts_dev); + atomic_set(&core_data->suspended, 1); + +#ifdef CONFIG_PINCTRL + if (core_data->pinctrl) { + r = pinctrl_select_state(core_data->pinctrl, + core_data->pin_sta_suspend); + if (r < 0) + ts_err("Failed to select active pinstate, r:%d", r); + } +#endif + + /* 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; + + r = ext_module->funcs->after_suspend(core_data, + ext_module); + if (r == 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_device *ts_dev = + core_data->ts_dev; + int r; + + ts_info("Resume start"); + + 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; + + r = ext_module->funcs->before_resume(core_data, + ext_module); + if (r == EVT_CANCEL_RESUME) { + mutex_unlock(&goodix_modules.mutex); + ts_info("Canceled by module:%s", + ext_module->name); + goto out; + } + } + } + mutex_unlock(&goodix_modules.mutex); + +#ifdef CONFIG_PINCTRL + if (core_data->pinctrl) { + r = pinctrl_select_state(core_data->pinctrl, + core_data->pin_sta_active); + if (r < 0) + ts_err("Failed to select active pinstate, r:%d", r); + } +#endif + + atomic_set(&core_data->suspended, 0); + /* resume device */ + if (ts_dev && ts_dev->hw_ops->resume) + ts_dev->hw_ops->resume(ts_dev); + + 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; + + r = ext_module->funcs->after_resume(core_data, + ext_module); + if (r == EVT_CANCEL_RESUME) { + mutex_unlock(&goodix_modules.mutex); + ts_info("Canceled by module:%s", + ext_module->name); + goto out; + } + } + } + mutex_unlock(&goodix_modules.mutex); + + goodix_ts_irq_enable(core_data, true); + + /* + * notify resume event, inform the esd protector + * and charger detector to turn on the work + */ + ts_info("try notify resume"); + goodix_ts_blocking_notify(NOTIFY_RESUME, NULL); +out: + ts_debug("Resume end"); + return 0; +} + +#ifdef 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 + +#ifdef CONFIG_HAS_EARLYSUSPEND +/** + * goodix_ts_earlysuspend - Early suspend function + * Called by kernel during system suspend phrase + */ +static void goodix_ts_earlysuspend(struct early_suspend *h) +{ + struct goodix_ts_core *core_data = + container_of(h, struct goodix_ts_core, + early_suspend); + + goodix_ts_suspend(core_data); +} +/** + * goodix_ts_lateresume - Late resume function + * Called by kernel during system wakeup + */ +static void goodix_ts_lateresume(struct early_suspend *h) +{ + struct goodix_ts_core *core_data = + container_of(h, struct goodix_ts_core, + early_suspend); + + goodix_ts_resume(core_data); +} +#endif + +#ifdef CONFIG_PM +#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 *ts_core = container_of(self, + struct goodix_ts_core, ts_notifier); + struct goodix_ts_device *ts_dev = ts_device(ts_core); + const struct goodix_ts_hw_ops *hw_ops = ts_hw_ops(ts_core); + int r; + + if (ts_core->init_stage < CORE_INIT_STAGE2) + return 0; + + ts_info("notify event type 0x%x", (unsigned int)action); + switch (action) { + case NOTIFY_FWUPDATE_START: + goodix_ts_irq_enable(ts_core, 0); + break; + case NOTIFY_FWUPDATE_SUCCESS: + case NOTIFY_FWUPDATE_FAILED: + r = hw_ops->read_version(ts_dev, &ts_dev->chip_version); + if (r < 0) + ts_info("failed read fw version info[ignore]"); + goodix_ts_irq_enable(ts_core, 1); + break; + default: + break; + } + return 0; +} + +#define TS_LOCKDOWN_REG 0xBDB4 +static int goodix_get_lockdown_info(struct goodix_ts_core *core_data) +{ + int ret = 0; + struct goodix_ts_device *ts_dev = core_data->ts_dev; + + ret = ts_dev->hw_ops->read(ts_dev, TS_LOCKDOWN_REG, + core_data->lockdown_info, GOODIX_LOCKDOWN_SIZE); + if (ret < 0) { + ts_err("get lockdown failed"); + return ret; + } + ts_info("lockdown info:%*ph", GOODIX_LOCKDOWN_SIZE, core_data->lockdown_info); + return 0; +} + +int goodix_ts_stage2_init(struct goodix_ts_core *core_data) +{ + int r; + struct goodix_ts_device *ts_dev = ts_device(core_data); + + /* send normal-cfg to firmware */ + r = ts_dev->hw_ops->send_config(ts_dev, &(ts_dev->normal_cfg)); + if (r < 0) { + ts_info("failed send normal config[ignore]"); + } + + r = ts_dev->hw_ops->read_version(ts_dev, &ts_dev->chip_version); + if (r < 0) + ts_info("failed read fw version info[ignore]"); + + /* get lockdown info */ + goodix_get_lockdown_info(core_data); + + /* alloc/config/register input device */ + r = goodix_ts_input_dev_config(core_data); + if (r < 0) { + ts_err("failed set input device"); + return r; + } + + if (ts_dev->board_data.pen_enable) { + r = goodix_ts_pen_dev_config(core_data); + if (r < 0) { + ts_err("failed set pen device"); + goto err_finger; + } + } + /* request irq line */ + r = goodix_ts_irq_setup(core_data); + if (r < 0) { + ts_info("failed set irq"); + goto exit; + } + ts_info("success register irq"); + +#ifdef CONFIG_FB + core_data->fb_notifier.notifier_call = goodix_ts_fb_notifier_callback; + if (fb_register_client(&core_data->fb_notifier)) + ts_err("Failed to register fb notifier client:%d", r); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + core_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + core_data->early_suspend.resume = goodix_ts_lateresume; + core_data->early_suspend.suspend = goodix_ts_earlysuspend; + register_early_suspend(&core_data->early_suspend); +#endif + /*create sysfs files*/ + goodix_ts_sysfs_init(core_data); + + /* create proc files */ + goodix_ts_procfs_init(core_data); + + /* esd protector */ + if (core_data->ts_dev->board_data.esd_default_on) + goodix_ts_esd_init(core_data); + return 0; +exit: + goodix_ts_pen_dev_remove(core_data); +err_finger: + goodix_ts_input_dev_remove(core_data); + return r; +} + +/** + * 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_ts_device *ts_device; + int r; + + ts_info("goodix_ts_probe IN"); + + ts_device = pdev->dev.platform_data; + if (!ts_device || !ts_device->hw_ops) { + ts_err("Invalid touch device"); + core_module_prob_sate = CORE_MODULE_PROB_FAILED; + return -ENODEV; + } + + core_data = devm_kzalloc(&pdev->dev, sizeof(struct goodix_ts_core), + GFP_KERNEL); + if (!core_data) { + ts_err("Failed to allocate memory for core data"); + core_module_prob_sate = CORE_MODULE_PROB_FAILED; + return -ENOMEM; + } + + goodix_core_module_init(); + /* touch core layer is a platform driver */ + core_data->pdev = pdev; + core_data->ts_dev = ts_device; + platform_set_drvdata(pdev, core_data); + + r = goodix_ts_power_init(core_data); + if (r < 0) + goto out; + + r = goodix_ts_power_on(core_data); + if (r < 0) + goto out; + +#ifdef CONFIG_PINCTRL + /* Pinctrl handle is optional. */ + r = goodix_ts_pinctrl_init(core_data); + if (!r && core_data->pinctrl) { + r = pinctrl_select_state(core_data->pinctrl, + core_data->pin_sta_active); + if (r < 0) + ts_err("Failed to select active pinstate, r:%d", r); + } +#endif + + /* get GPIO resource */ + r = goodix_ts_gpio_setup(core_data); + if (r < 0) + goto gpio_err; + + /* confirm it's goodix touch dev or not */ + r = ts_device->hw_ops->dev_confirm(ts_device); + if (r) { + ts_err("goodix device confirm failed"); + goto gpio_err; + } + + /* generic notifier callback */ + core_data->ts_notifier.notifier_call = goodix_generic_noti_callback; + goodix_ts_register_notifier(&core_data->ts_notifier); + + core_data->initialized = 1; + goodix_modules.core_data = core_data; + core_data->init_stage = CORE_INIT_STAGE1; + core_module_prob_sate = CORE_MODULE_PROB_SUCCESS; + + r = goodix_fw_update_init(core_data); + if (r) { + ts_err("failed init fwupdate module, %d", r); + goto gpio_err; + } + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + ts_info("%s:goodix_ts_mmi_dev_register",__func__); + r = goodix_ts_mmi_dev_register(pdev); + if (r) { + ts_err("Failed register touchscreen mmi."); + goto fw_update_init_err; + } +#endif + + r = goodix_gsx_gesture_init(); + if (r) { + ts_err("Failed goodix_gsx_gesture_init."); + goto ts_mmi_dev_err; + } + + /* Try start a thread to get config-bin info */ + r = goodix_start_later_init(core_data); + if (r) { + ts_err("Failed start cfg_bin_proc, %d", r); + goto later_thread_err; + } + + /* debug node init */ + goodix_tools_init(); + + complete_all(&goodix_modules.core_comp); + ts_info("goodix_ts_core probe success"); + return 0; +later_thread_err: + goodix_gsx_gesture_exit(); +ts_mmi_dev_err: +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + goodix_ts_mmi_dev_unregister(pdev); +fw_update_init_err: +#endif + goodix_fw_update_uninit(); +gpio_err: + goodix_ts_power_off(core_data); +out: + kfree(core_data); + core_data = NULL; + core_module_prob_sate = CORE_MODULE_PROB_FAILED; + ts_err("goodix_ts_core failed, r:%d", r); + /* wakeup ext module register work */ + complete_all(&goodix_modules.core_comp); + return r; +} + +static int goodix_ts_remove(struct platform_device *pdev) +{ + struct goodix_ts_core *core_data = platform_get_drvdata(pdev); + struct goodix_ts_esd *ts_esd = &core_data->ts_esd; + + if (goodix_unregister_all_module()) + return -EBUSY; + + goodix_fw_update_uninit(); + goodix_ts_unregister_notifier(&core_data->ts_notifier); +#ifdef CONFIG_FB + fb_unregister_client(&core_data->fb_notifier); +#endif + core_data->initialized = 0; + 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_ts_input_dev_remove(core_data); + goodix_ts_pen_dev_remove(core_data); + goodix_ts_irq_enable(core_data, false); + + goodix_ts_power_off(core_data); + goodix_ts_sysfs_exit(core_data); + kfree(core_data); + return 0; +} + +#ifdef CONFIG_PM +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_DRIVER_NAME}, + {} +}; +MODULE_DEVICE_TABLE(platform, ts_core_ids); + +static struct platform_driver goodix_ts_driver = { + .driver = { + .name = GOODIX_CORE_DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .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; + + ts_info("Core layer init:%s", GOODIX_DRIVER_VERSION); + ret = goodix_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_bus_exit(); + return; +} +late_initcall(goodix_ts_core_init); +module_exit(goodix_ts_core_exit); + +MODULE_DESCRIPTION("Goodix Touchscreen Core Module"); +MODULE_AUTHOR("Goodix, Inc."); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_core.h b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_core.h new file mode 100644 index 000000000000..a7ae23258d32 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_core.h @@ -0,0 +1,775 @@ +/* + * Goodix Touchscreen Driver + * Core layer of touchdriver architecture. + * + * Copyright (C) 2019 - 2020 Goodix, Inc. + * Authors: Wang Yafei + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#include +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif +#ifdef CONFIG_FB +#include +#include +#endif + +#define GOODIX_FLASH_CONFIG_WITH_ISP 1 +/* macros definition */ +#define GOODIX_CORE_DRIVER_NAME "goodix_ts" +#define GOODIX_PEN_DRIVER_NAME "goodix_ts,pen" +#define GOODIX_DRIVER_VERSION "v1.5.1.0" +#define GOODIX_BUS_RETRY_TIMES 20 +#define GOODIX_MAX_TOUCH 10 +#define GOODIX_CFG_MAX_SIZE 4096 +#define GOODIX_ESD_TICK_WRITE_DATA 0xAA +#define GOODIX_PID_MAX_LEN 8 +#define GOODIX_VID_MAX_LEN 8 + +#define GOODIX_DEFAULT_CFG_NAME "goodix_config.cfg" +#define GOODIX_TP_IC_TYPE "GTx8" + +#define IC_TYPE_NONE 0 +#define IC_TYPE_NORMANDY 1 +#define IC_TYPE_NANJING 2 +#define IC_TYPE_YELLOWSTONE 3 + +#define GOODIX_TOUCH_EVENT 0x80 +#define GOODIX_REQUEST_EVENT 0x40 +#define GOODIX_GESTURE_EVENT 0x20 +#define GOODIX_HOTKNOT_EVENT 0x10 + +#define GOODIX_LOCKDOWN_SIZE 8 + +#define GOODIX_PEN_MAX_PRESSURE 4096 +#define GOODIX_MAX_TP_KEY 4 +#define GOODIX_MAX_PEN_KEY 2 + +#define TS_RAWDATA_BUFF_MAX 3000 +#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 completion core_comp; + 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 + * @panel_max_key: max supported keys + * @pannel_key_map: key map + * @fw_name: name of the firmware image + */ +struct goodix_ts_board_data { + char avdd_name[24]; + char iovdd_name[24]; + unsigned int reset_gpio; + unsigned int irq_gpio; + int irq; + 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*/ + unsigned int panel_max_key; + unsigned int panel_key_map[GOODIX_MAX_TP_KEY]; + unsigned int x2x; + unsigned int y2y; + bool pen_enable; + unsigned int tp_key_num; + /*add end*/ + + const char *fw_name; + const char *cfg_bin_name; + bool esd_default_on; +}; + +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 */ +}; + +enum goodix_cfg_init_state { + TS_CFG_UNINITALIZED, + TS_CFG_STABLE, + TS_CFG_TEMP, +}; + +enum goodix_core_init_stage { + CORE_UNINIT, + CORE_INIT_FAIL, + CORE_INIT_STAGE1, + CORE_INIT_STAGE2 +}; + +/* + * struct goodix_ts_config - chip config data + * @initialized: cfg init state, 0 uninit, + * 1 init with stable config, 2 init with temp config. + * @name: name of this config + * @lock: mutex + * @reg_base: register base of config data + * @length: bytes of the config + * @delay: delay time after sending config + * @data: config data buffer + */ +struct goodix_ts_config { + int initialized; + char name[24]; + struct mutex lock; + unsigned int reg_base; + unsigned int length; + unsigned int delay; /*ms*/ + unsigned char data[GOODIX_CFG_MAX_SIZE]; +}; + +/* + * struct goodix_ts_cmd - command package + * @initialized: whether initialized + * @cmd_reg: command register + * @length: command length in bytes + * @cmds: command data + */ +#pragma pack(4) +struct goodix_ts_cmd { + u32 initialized; + u32 cmd_reg; + u32 length; + u8 cmds[8]; + }; +#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), +}; + +/* 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; +}; + +struct goodix_ts_key { + int status; + int code; +}; + +/* touch event data */ +struct goodix_touch_data { + int touch_num; + struct goodix_ts_coords coords[GOODIX_MAX_TOUCH]; + struct goodix_ts_key keys[GOODIX_MAX_TP_KEY]; +}; + +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; + struct goodix_touch_data touch_data; + struct goodix_pen_data pen_data; +}; + +/* + * struct goodix_ts_version - firmware version + * @valid: whether these infomation is valid + * @pid: product id string + * @vid: firmware version code + * @cid: customer id code + * @sensor_id: sendor id + */ +struct goodix_ts_version { + bool valid; + char pid[GOODIX_PID_MAX_LEN]; + char vid[GOODIX_VID_MAX_LEN]; + u8 cid; + u8 sensor_id; +}; + +struct goodix_ts_regs { + u16 cfg_send_flag; + + u16 version_base; + u8 version_len; + + u16 pid; + u8 pid_len; + + u16 vid; + u8 vid_len; + + u16 sensor_id; + u8 sensor_id_mask; + + u16 fw_mask; + u16 fw_status; + u16 cfg_addr; + u16 esd; + u16 command; + u16 gesture; + u16 coor; + u16 fw_request; + u16 proximity; +}; + +enum goodix_cfg_bin_state { + CFG_BIN_STATE_UNINIT, /* config bin uninit */ + CFG_BIN_STATE_ERROR, /* config bin encounter fatal error */ + CFG_BIN_STATE_INITIALIZED, /* config bin init success */ + CFG_BIN_STATE_TEMP, /* config bin need reparse */ +}; + +/* + * struct goodix_ts_device - ts device data + * @name: device name + * @version: reserved + * @bus_type: i2c or spi + * @ic_type: normandy or yellowstone + * @cfg_bin_state: see enum goodix_cfg_bin_state + * @fw_uptodate: set to 1 after do fw update + * @board_data: board data obtained from dts + * @normal_cfg: normal config data + * @highsense_cfg: high sense config data + * @hw_ops: hardware operations + * @chip_version: firmware version infomation + * @sleep_cmd: sleep commang + * @gesture_cmd: gesture command + * @dev: device pointer,may be a i2c or spi device + * @of_node: device node + */ +struct goodix_ts_device { + char *name; + int version; + int bus_type; + int ic_type; + int cfg_bin_state; + struct goodix_ts_regs reg; + struct goodix_ts_board_data board_data; + struct goodix_ts_config normal_cfg; + struct goodix_ts_config highsense_cfg; + const struct goodix_ts_hw_ops *hw_ops; + + struct goodix_ts_version chip_version; + struct device *dev; +}; + +/* + * struct goodix_ts_hw_ops - hardware opeartions + * @init: hardware initialization + * @reset: hardware reset + * @read: read data from touch device + * @write: write data to touch device + * @send_cmd: send command to touch device + * @send_config: send configuration data + * @read_version: read firmware version + * @event_handler: touch event handler + * @suspend: put touch device into low power mode + * @resume: put touch device into working mode + */ +struct goodix_ts_hw_ops { + int (*init)(struct goodix_ts_device *dev); + int (*dev_confirm)(struct goodix_ts_device *ts_dev); + int (*reset)(struct goodix_ts_device *dev); + int (*read)(struct goodix_ts_device *dev, unsigned int addr, + unsigned char *data, unsigned int len); + int (*write)(struct goodix_ts_device *dev, unsigned int addr, + unsigned char *data, unsigned int len); + int (*read_trans)(struct goodix_ts_device *dev, unsigned int addr, + unsigned char *data, unsigned int len); + int (*write_trans)(struct goodix_ts_device *dev, unsigned int addr, + unsigned char *data, unsigned int len); + int (*send_cmd)(struct goodix_ts_device *dev, struct goodix_ts_cmd *cmd); + int (*send_config)(struct goodix_ts_device *dev, + struct goodix_ts_config *config); + int (*read_config)(struct goodix_ts_device *dev, u8 *config_data, int size); + int (*read_version)(struct goodix_ts_device *dev, + struct goodix_ts_version *version); + int (*event_handler)(struct goodix_ts_device *dev, + struct goodix_ts_event *ts_event); + int (*check_hw)(struct goodix_ts_device *dev); + int (*suspend)(struct goodix_ts_device *dev); + int (*resume)(struct goodix_ts_device *dev); + int (*get_capacitance_data)(struct goodix_ts_device *dev, 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 { + struct delayed_work esd_work; + struct notifier_block esd_notifier; + struct goodix_ts_core *ts_core; + atomic_t esd_on; +}; + +/* + * struct godix_ts_core - core layer data struct + * @initialized: indicate core state, 1 ok, 0 bad + * @pdev: core layer platform device + * @ts_dev: hardware layer touch device + * @input_dev: input device + * @avdd: analog regulator + * @iovdd: analog regulator + * @pinctrl: pinctrl handler + * @pin_sta_active: active/normal pin state + * @pin_sta_suspend: suspend/sleep pin state + * @ts_event: touch event data struct + * @power_on: power on/off flag + * @irq: irq number + * @irq_enabled: irq enabled/disabled flag + * @suspended: suspend/resume flag + * @ts_notifier: generic notifier + * @ts_esd: esd protector structure + * @fb_notifier: framebuffer notifier + * @early_suspend: early suspend + */ +struct goodix_ts_core { + int initialized; + int init_stage; + int gesture_enable; + u8 lockdown_info[GOODIX_LOCKDOWN_SIZE]; + struct platform_device *pdev; + struct goodix_ts_device *ts_dev; + struct input_dev *input_dev; + struct input_dev *pen_dev; + + struct regulator *avdd; + struct regulator *iovdd; +#ifdef CONFIG_PINCTRL + struct pinctrl *pinctrl; + struct pinctrl_state *pin_sta_active; + struct pinctrl_state *pin_sta_suspend; +#endif + struct goodix_ts_event ts_event; + int power_on; + int irq; + size_t irq_trig_cnt; + + atomic_t irq_enabled; + atomic_t suspended; + + u32 drv_num; + u32 sen_num; + + struct notifier_block ts_notifier; + struct goodix_ts_esd ts_esd; + +#ifdef CONFIG_FB + struct notifier_block fb_notifier; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif + /* touchscreen_mmi */ + struct ts_mmi_class_methods *imports; +}; + +/* 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, +}; + +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 *, char *); + ssize_t (*store)(struct goodix_ext_module *, const char *, size_t); +}; + +/* 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); + +/* + * get board data pointer + */ +static inline struct goodix_ts_board_data *board_data( + struct goodix_ts_core *core) +{ + if (!core || !core->ts_dev) + return NULL; + return &(core->ts_dev->board_data); +} + +/* + * get touch device pointer + */ +static inline struct goodix_ts_device *ts_device( + struct goodix_ts_core *core) +{ + if (!core) + return NULL; + return core->ts_dev; +} + +/* + * get touch hardware operations pointer + */ +static inline const struct goodix_ts_hw_ops *ts_hw_ops( + struct goodix_ts_core *core) +{ + if (!core || !core->ts_dev) + return NULL; + return core->ts_dev->hw_ops; +} + +/* + * checksum helper functions + * checksum can be u8/le16/be16/le32/be32 format + * NOTE: the caller shoule be responsible for the + * legality of @data and @size parameters, so be + * careful when call these functions. + */ +static inline u8 checksum_u8(u8 *data, u32 size) +{ + u8 checksum = 0; + u32 i; + + for (i = 0; i < size; i++) + checksum += data[i]; + return checksum; +} + +/* cal u8 data checksum for yellowston */ +static inline u16 checksum_u8_ys(u8 *data, u32 size) +{ + u16 checksum = 0; + u32 i; + + for (i = 0; i < size - 2; i++) + checksum += data[i]; + return checksum - (data[size - 2] << 8 | data[size - 1]); +} + +static inline u16 checksum_le16(u8 *data, u32 size) +{ + u16 checksum = 0; + u32 i; + + for (i = 0; i < size; i += 2) + checksum += le16_to_cpup((__le16 *)(data + i)); + return checksum; +} + +static inline u16 checksum_be16(u8 *data, u32 size) +{ + u16 checksum = 0; + u32 i; + + for (i = 0; i < size; i += 2) + checksum += be16_to_cpup((__be16 *)(data + i)); + return checksum; +} + +static inline u32 checksum_le32(u8 *data, u32 size) +{ + u32 checksum = 0; + u32 i; + + for (i = 0; i < size; i += 4) + checksum += le32_to_cpup((__le32 *)(data + i)); + return checksum; +} + +static inline u32 checksum_be32(u8 *data, u32 size) +{ + u32 checksum = 0; + u32 i; + + for (i = 0; i < size; i += 4) + checksum += be32_to_cpup((__be32 *)(data + i)); + return checksum; +} + +/* + * define event action + * EVT_xxx macros are used in opeartions callback + * defined in @goodix_ext_module_funcs to control + * the behaviors of event such as suspend/resume/irq_event. + * generally there are two types of behaviors: + * 1. you want the flow of this event be canceled, + * in this condition, you should return EVT_CANCEL_XXX in + * the operations callback. + * e.g. the firmware update module is updating + * the firmware, you want to cancel suspend flow, + * so you need to return EVT_CANCEL_SUSPEND in + * suspend callback function. + * 2. you want the flow of this event continue, in + * this condition, you should return EVT_HANDLED in + * the callback function. + * */ +#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 + +/* + * errno define + * Note: + * 1. bus read/write functions defined in hardware + * layer code(e.g. goodix_xxx_i2c.c) *must* return + * -EBUS if failed to transfer data on bus. + */ +#define EBUS 1000 +#define ETIMEOUT 1001 +#define ECHKSUM 1002 +#define EMEMCMP 1003 + +//#define CONFIG_GOODIX_DEBUG +/* log macro */ +#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 boot_log(fmt, arg...) g_info(fmt, ##arg) +#ifdef CONFIG_GOODIX_DEBUG +#define ts_debug(fmt, arg...) pr_info("[GTP-DBG][%s:%d] "fmt"\n", __func__, __LINE__, ##arg) +#else +#define ts_debug(fmt, arg...) do {} while (0) +#endif + +/** + * 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_unregister_all_module(void); + +/** + * goodix_ts_irq_enable - Enable/Disable a irq + + * @core_data: pointer to touch core data + * enable: enable or disable irq + * return: 0 ok, <0 failed + */ +int goodix_ts_irq_enable(struct goodix_ts_core *core_data, bool enable); + +struct kobj_type *goodix_get_default_ktype(void); + +/** + * 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); + + +/** + * * 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 *core_data); + +/** + * 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 *core_data); + +int goodix_ts_irq_setup(struct goodix_ts_core *core_data); + +int goodix_ts_esd_init(struct goodix_ts_core *core); + +int goodix_ts_register_notifier(struct notifier_block *nb); + +int goodix_ts_fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); + +int goodix_bus_init(void); +void goodix_bus_exit(void); + +int goodix_do_fw_update(int mode); +int gtx8_get_rawdata(void *tsdev, struct ts_rawdata_info *info); +void goodix_rotate_abcd2cbad(int tx, int rx, s16 *data); +int goodix_get_cfg_value(struct goodix_ts_core *core_data, + u8 *config, u8 *buf, u8 len, u8 sub_bag_num, u8 offset); + +#endif diff --git a/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_gesture.c b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_gesture.c new file mode 100644 index 000000000000..eb8c70b0dfa5 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_gesture.c @@ -0,0 +1,468 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "goodix_ts_core.h" +#include + +#define GSX_GESTURE_CMD 0x08 + +#define QUERYBIT(longlong, bit) (!!(longlong[bit/8] & (1 << bit%8))) + +#define GSX_MAX_KEY_DATA_LEN 64 +#define GSX_KEY_DATA_LEN 37 +#define GSX_KEY_DATA_LEN_YS 8 +#define GSX_GESTURE_TYPE_LEN 32 + +/* + * struct gesture_module - gesture module data + * @registered: module register state + * @sysfs_node_created: sysfs node state + * @gesture_type: store valid gesture type,each bit stand for a gesture + * @gesture_data: gesture data + * @gesture_ts_cmd: gesture command data + */ +struct gesture_module { + atomic_t registered; + unsigned int kobj_initialized; + rwlock_t rwlock; + unsigned char gesture_type[GSX_GESTURE_TYPE_LEN]; + unsigned char gesture_data[GSX_MAX_KEY_DATA_LEN]; + struct goodix_ext_module module; + struct goodix_ts_cmd cmd; +}; + +static int gsx_enter_gesture_mode(struct goodix_ts_device *ts_dev); +static struct gesture_module *gsx_gesture; /*allocated in gesture init module*/ + +/** + * gsx_gesture_type_show - show valid gesture type + * + * @module: pointer to goodix_ext_module struct + * @buf: pointer to output buffer + * Returns >=0 - succeed,< 0 - failed + */ +static ssize_t gsx_gesture_type_show(struct goodix_ext_module *module, + char *buf) +{ + int count = 0, i, ret = 0; + unsigned char *type; + + if (atomic_read(&gsx_gesture->registered) != 1) { + ts_info("Gesture module not register!"); + return -EPERM; + } + type = kzalloc(256, GFP_KERNEL); + if (!type) + return -ENOMEM; + read_lock(&gsx_gesture->rwlock); + for (i = 0; i < 256; i++) { + if (QUERYBIT(gsx_gesture->gesture_type, i)) { + type[count] = i; + count++; + } + } + type[count] = '\0'; + if (count > 0) + ret = scnprintf(buf, PAGE_SIZE, "%s", type); + read_unlock(&gsx_gesture->rwlock); + + kfree(type); + return ret; +} + +/** + * gsx_gesture_type_store - set vailed gesture + * + * @module: pointer to goodix_ext_module struct + * @buf: pointer to valid gesture type + * @count: length of buf + * Returns >0 - valid gestures, < 0 - failed + */ +static ssize_t gsx_gesture_type_store(struct goodix_ext_module *module, + const char *buf, size_t count) +{ + int i; + + if (count <= 0 || count > 256 || buf == NULL) { + ts_err("Parameter error"); + return -EINVAL; + } + + write_lock(&gsx_gesture->rwlock); + memset(gsx_gesture->gesture_type, 0, GSX_GESTURE_TYPE_LEN); + for (i = 0; i < count; i++) + gsx_gesture->gesture_type[buf[i]/8] |= (0x1 << buf[i]%8); + write_unlock(&gsx_gesture->rwlock); + + return count; +} + +static ssize_t gsx_gesture_enable_show(struct goodix_ext_module *module, + char *buf) +{ + struct goodix_ts_core *cd = module->priv_data; + + return scnprintf(buf, PAGE_SIZE, "%d\n", cd->gesture_enable); +} + +static ssize_t gsx_gesture_enable_store(struct goodix_ext_module *module, + const char *buf, size_t count) +{ + struct goodix_ts_core *cd = module->priv_data; + unsigned int tmp; + + if (sscanf(buf, "%u", &tmp) != 1) { + ts_info("Parameter illegal"); + return -EINVAL; + } + ts_debug("Tmp value =%d", tmp); + + if (tmp == 1) { + cd->gesture_enable = 1; + } else if (tmp == 0) { + cd->gesture_enable = 0; + } else { + ts_err("Parameter error!"); + return -EINVAL; + } + return count; +} + +static ssize_t gsx_gesture_data_show(struct goodix_ext_module *module, + char *buf) +{ + struct goodix_ts_core *cd = module->priv_data; + ssize_t count; + + if (atomic_read(&gsx_gesture->registered) != 1) { + ts_info("Gesture module not register!"); + return -EPERM; + } + if (!buf) { + ts_info("Parameter error!"); + return -EPERM; + } + read_lock(&gsx_gesture->rwlock); + if (cd->ts_dev->ic_type == IC_TYPE_YELLOWSTONE) + count = scnprintf(buf, PAGE_SIZE, "gesture type code:0x%x\n", + gsx_gesture->gesture_data[4]); + else + count = scnprintf(buf, PAGE_SIZE, "gesture type code:0x%x\n", + gsx_gesture->gesture_data[2]); + read_unlock(&gsx_gesture->rwlock); + + return count; +} + +const struct goodix_ext_attribute gesture_attrs[] = { + __EXTMOD_ATTR(type, 0666, gsx_gesture_type_show, + gsx_gesture_type_store), + __EXTMOD_ATTR(enable, 0666, gsx_gesture_enable_show, + gsx_gesture_enable_store), + __EXTMOD_ATTR(data, 0444, gsx_gesture_data_show, NULL) +}; + +static int gsx_enter_gesture_mode(struct goodix_ts_device *ts_dev) +{ + if (!gsx_gesture->cmd.initialized) { + if (!ts_dev->reg.command) { + ts_err("command reg can not be null"); + return -EINVAL; + } + if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) { + gsx_gesture->cmd.cmd_reg = ts_dev->reg.command; + gsx_gesture->cmd.length = 5; + gsx_gesture->cmd.cmds[0] = GSX_GESTURE_CMD; + gsx_gesture->cmd.cmds[1] = 0x0; + gsx_gesture->cmd.cmds[2] = 0x0; + gsx_gesture->cmd.cmds[3] = 0x0; + gsx_gesture->cmd.cmds[4] = GSX_GESTURE_CMD; + gsx_gesture->cmd.initialized = 1; + + } else { + gsx_gesture->cmd.cmd_reg = ts_dev->reg.command; + gsx_gesture->cmd.length = 3; + gsx_gesture->cmd.cmds[0] = GSX_GESTURE_CMD; + gsx_gesture->cmd.cmds[1] = 0x01; + gsx_gesture->cmd.cmds[2] = 0 - gsx_gesture->cmd.cmds[0] - gsx_gesture->cmd.cmds[1]; + gsx_gesture->cmd.initialized = 1; + } + } + + return ts_dev->hw_ops->send_cmd(ts_dev, &gsx_gesture->cmd); +} + +static int gsx_gesture_init(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + int i, ret = -EINVAL; + struct goodix_ts_device *ts_dev = core_data->ts_dev; + + if (!core_data || !ts_dev->hw_ops->write || !ts_dev->hw_ops->read) { + ts_err("Register gesture module failed, ts_core unsupported"); + goto exit_gesture_init; + } + + memset(gsx_gesture->gesture_type, 0, GSX_GESTURE_TYPE_LEN); + memset(gsx_gesture->gesture_data, 0xff, + sizeof(gsx_gesture->gesture_data)); + module->priv_data = core_data; + + ts_debug("Set gesture type manually"); + /* set all bit to 1 to enable all gesture wakeup */ + memset(gsx_gesture->gesture_type, 0xff, GSX_GESTURE_TYPE_LEN); + + if (gsx_gesture->kobj_initialized) { + ret = 0; + goto exit_gesture_init; + } + + ret = kobject_init_and_add(&module->kobj, goodix_get_default_ktype(), + &core_data->pdev->dev.kobj, "gesture"); + + if (ret) { + ts_err("Create gesture sysfs node error!"); + goto exit_gesture_init; + } + + ret = 0; + for (i = 0; i < ARRAY_SIZE(gesture_attrs) && !ret; i++) + ret = sysfs_create_file(&module->kobj, &gesture_attrs[i].attr); + if (ret) { + ts_err("failed create gst sysfs files"); + while (--i >= 0) + sysfs_remove_file(&module->kobj, &gesture_attrs[i].attr); + + kobject_put(&module->kobj); + goto exit_gesture_init; + } + + gsx_gesture->kobj_initialized = 1; + core_data->gesture_enable = 1; + +exit_gesture_init: + return ret; +} + +static int gsx_gesture_exit(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + atomic_set(&gsx_gesture->registered, 0); + return 0; +} + +/** + * gsx_gesture_ist - Gesture Irq handle + * This functions is excuted when interrupt happended and + * ic in doze mode. + * + * @core_data: 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 *core_data, + struct goodix_ext_module *module) +{ + int ret; + int key_data_len = 0; + u8 clear_reg = 0, checksum = 0, gsx_type = 0; + u8 temp_data[GSX_MAX_KEY_DATA_LEN]; + struct goodix_ts_device *ts_dev = core_data->ts_dev; + + if (!core_data->gesture_enable || atomic_read(&core_data->suspended) == 0) + return EVT_CONTINUE; + + if (!ts_dev->reg.gesture) { + ts_err("gesture reg can't be null"); + return EVT_CONTINUE; + } + /* get ic gesture state*/ + if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) + key_data_len = GSX_KEY_DATA_LEN_YS; + else + key_data_len = GSX_KEY_DATA_LEN; + + ret = ts_dev->hw_ops->read_trans(ts_dev, ts_dev->reg.gesture, + temp_data, key_data_len); + if (ret < 0 || ((temp_data[0] & GOODIX_GESTURE_EVENT) == 0)) { + ts_debug("invalid gesture event, ret=%d, temp_data[0]=0x%x", + ret, temp_data[0]); + goto re_send_ges_cmd; + } + + if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) + checksum = checksum_u8_ys(temp_data, key_data_len); + else + checksum = checksum_u8(temp_data, key_data_len); + if (checksum) { + ts_err("Gesture data checksum error:0x%x", checksum); + ts_info("Gesture data %*ph", + (int)sizeof(temp_data), temp_data); + goto re_send_ges_cmd; + } + + ts_debug("Gesture data:"); + ts_debug("data[0-4]0x%x, 0x%x, 0x%x, 0x%x", temp_data[0], temp_data[1], + temp_data[2], temp_data[3]); + + write_lock(&gsx_gesture->rwlock); + memcpy(gsx_gesture->gesture_data, temp_data, key_data_len); + write_unlock(&gsx_gesture->rwlock); + + if (ts_dev->ic_type == IC_TYPE_YELLOWSTONE) + gsx_type = temp_data[4]; + else + gsx_type = temp_data[2]; + + if (gsx_type == 0xCC) { /* double tap */ + /* do resume routine */ + ts_info("Gesture match success, resume IC"); + input_report_key(core_data->input_dev, KEY_POWER, 1); + input_sync(core_data->input_dev); + input_report_key(core_data->input_dev, KEY_POWER, 0); + input_sync(core_data->input_dev); + goto gesture_ist_exit; +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + } else if (gsx_type == 0x4C) { + if (core_data->imports && core_data->imports->report_gesture) { + struct gesture_event_data mmi_event; + + ts_info("invoke imported report gesture function\n"); + mmi_event.evcode = 1; + + /* call class method */ + ret = core_data->imports->report_gesture(&mmi_event); + goto gesture_ist_exit; + } +#endif + } else { + ts_info("Unsupported gesture:%x", temp_data[2]); + } + +re_send_ges_cmd: + if (gsx_enter_gesture_mode(core_data->ts_dev)) + ts_info("warning: failed re_send gesture cmd\n"); +gesture_ist_exit: + ts_dev->hw_ops->write_trans(ts_dev, ts_dev->reg.gesture, + &clear_reg, 1); + return EVT_CANCEL_IRQEVT; +} + +/** + * gsx_gesture_before_suspend - execute gesture suspend routine + * This functions is excuted to set ic into doze mode + * + * @core_data: 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 *core_data, + struct goodix_ext_module *module) +{ + int ret; + const struct goodix_ts_hw_ops *hw_ops = core_data->ts_dev->hw_ops; + + if (!core_data->gesture_enable || !hw_ops) + return EVT_CONTINUE; + + atomic_set(&core_data->suspended, 1); + ret = gsx_enter_gesture_mode(core_data->ts_dev); + if (ret != 0) + ts_err("failed enter gesture mode"); + else + ts_info("Set IC in gesture mode"); + + enable_irq_wake(core_data->irq); + + return EVT_CANCEL_SUSPEND; +} + +int gsx_gesture_before_suspend_mmi(struct goodix_ts_core *core_data) +{ + int ret; + const struct goodix_ts_hw_ops *hw_ops = core_data->ts_dev->hw_ops; + + if (!core_data->gesture_enable || !hw_ops) + return EVT_CONTINUE; + + atomic_set(&core_data->suspended, 1); + ret = gsx_enter_gesture_mode(core_data->ts_dev); + if (ret != 0) + ts_err("failed enter gesture mode"); + else + ts_err("Set IC in gesture mode"); + + return EVT_CANCEL_SUSPEND; +} + +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 +}; + +int __init goodix_gsx_gesture_init(void) +{ + /* initialize core_data->ts_dev->gesture_cmd */ + int result; + ts_info("gesture module init"); + gsx_gesture = kzalloc(sizeof(struct gesture_module), GFP_KERNEL); + if (!gsx_gesture) + result = -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; + gsx_gesture->kobj_initialized = 0; + atomic_set(&gsx_gesture->registered, 0); + rwlock_init(&gsx_gesture->rwlock); + result = goodix_register_ext_module(&(gsx_gesture->module)); + if (result == 0) + atomic_set(&gsx_gesture->registered, 1); + + return result; +} + +void __exit goodix_gsx_gesture_exit(void) +{ + int i, ret; + ts_info("gesture module exit"); + if (atomic_read(&gsx_gesture->registered)) { + ret = goodix_unregister_ext_module(&gsx_gesture->module); + atomic_set(&gsx_gesture->registered, 0); + } + if (gsx_gesture->kobj_initialized) { + 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); +} diff --git a/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_i2c.c b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_i2c.c new file mode 100644 index 000000000000..c8b368456113 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_i2c.c @@ -0,0 +1,2011 @@ +/* + * Goodix I2C Module + * Hardware interface layer of touchdriver architecture. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "goodix_ts_core.h" +#include "goodix_cfg_bin.h" +#include + +#define TS_DRIVER_NAME "gtx8" +#define I2C_MAX_TRANSFER_SIZE 60 +#define TS_ADDR_LENGTH 2 +#define TS_DOZE_ENABLE_RETRY_TIMES 3 +#define TS_DOZE_DISABLE_RETRY_TIMES 9 +#define TS_WAIT_CFG_READY_RETRY_TIMES 30 +#define TS_WAIT_CMD_FREE_RETRY_TIMES 10 + +#define TS_REG_COORDS_BASE 0x824E +#define TS_REG_CMD 0x8040 +#define TS_REG_REQUEST 0x8044 +#define TS_REG_VERSION 0x8240 +#define TS_REG_CFG_BASE 0x8050 +#define TS_REG_DOZE_CTRL 0x30F0 +#define TS_REG_DOZE_STAT 0x3100 +#define TS_REG_ESD_TICK_R 0x3103 + +#define CFG_XMAX_OFFSET (0x8052 - 0x8050) +#define CFG_YMAX_OFFSET (0x8054 - 0x8050) + +#define REQUEST_HANDLED 0x00 +#define REQUEST_CONFIG 0x01 +#define REQUEST_BAKREF 0x02 +#define REQUEST_RESET 0x03 +#define REQUEST_RELOADFW 0x05 +#define REQUEST_IDLE 0xff + +#define COMMAND_SLEEP 0x05 +#define COMMAND_CLOSE_HID 0xaa +#define COMMAND_START_SEND_CFG 0x80 +#define COMMAND_END_SEND_CFG 0x83 +#define COMMAND_SEND_SMALL_CFG 0x81 +#define COMMAND_SEND_CFG_PREPARE_OK 0x82 +#define COMMAND_START_READ_CFG 0x86 +#define COMMAND_READ_CFG_PREPARE_OK 0x85 +#define COMMAND_END_SEND_CFG_YS 0x7D + +#define BYTES_PER_COORD 8 +#define TS_MAX_SENSORID 5 +#define TS_CFG_HEAD_LEN 4 +#define TS_CFG_HEAD_LEN_YS 5 +#define TS_CFG_BAG_NUM_INDEX 2 +#define TS_CFG_BAG_START_INDEX 4 + +#define TS_DOZE_DISABLE_DATA 0xAA +#define TS_DOZE_CLOSE_OK_DATA 0xBB +#define TS_DOZE_ENABLE_DATA 0xCC +#define TS_CMD_REG_READY 0xFF +#define TS_CMD_CFG_ERR 0x7E +#define TS_CMD_CFG_OK 0x7F + +#define POINT_TYPE_STYLUS 0x01 +#define POINT_TYPE_STYLUS_HOVER 0x03 + +enum TS_SEND_CFG_REPLY { + TS_CFG_REPLY_PKGS_ERR = 0x01, + TS_CFG_REPLY_CHKSUM_ERR = 0x02, + TS_CFG_REPLY_DATA_ERR = 0x03, + TS_CFG_REPLY_DATA_EQU = 0x07, +}; + +#define IRQ_HEAD_LEN_YS 8 +#define IRQ_HEAD_LEN_NOR 2 + +#ifdef 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 r, err; + + r = of_property_read_u32(node, "goodix,panel-max-x", + &board_data->panel_max_x); + if (r) + err = -ENOENT; + + r = of_property_read_u32(node, "goodix,panel-max-y", + &board_data->panel_max_y); + if (r) + err = -ENOENT; + + r = of_property_read_u32(node, "goodix,panel-max-w", + &board_data->panel_max_w); + if (r) + err = -ENOENT; + + board_data->swap_axis = of_property_read_bool(node, + "goodix,swap-axis"); + board_data->x2x = of_property_read_bool(node, "goodix,x2x"); + board_data->y2y = of_property_read_bool(node, "goodix,y2y"); + + 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) +{ + struct property *prop; + const char *name_tmp; + int r; + + if (!board_data) { + ts_err("invalid board data"); + return -EINVAL; + } + + 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; + } + + 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 form dt: %s", name_tmp); + if (strlen(name_tmp) < sizeof(board_data->avdd_name)) + strncpy(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 form dt: %s", name_tmp); + if (strlen(name_tmp) < sizeof(board_data->iovdd_name)) + strncpy(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 xyz resolutions */ + r = goodix_parse_dt_resolution(node, board_data); + if (r < 0) { + ts_err("Failed to parse resolutions:%d", r); + return r; + } + + /* key map */ + prop = of_find_property(node, "goodix,panel-key-map", NULL); + if (prop && prop->length) { + if (prop->length / sizeof(u32) > GOODIX_MAX_TP_KEY) { + ts_err("Size of panel-key-map is invalid"); + return r; + } + + board_data->panel_max_key = prop->length / sizeof(u32); + board_data->tp_key_num = prop->length / sizeof(u32); + r = of_property_read_u32_array(node, + "goodix,panel-key-map", + &board_data->panel_key_map[0], + board_data->panel_max_key); + if (r) { + ts_err("failed get key map, %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_info("***key:%d, %d, %d, %d", + board_data->panel_key_map[0], board_data->panel_key_map[1], + board_data->panel_key_map[2], board_data->panel_key_map[3]); + + 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); + + board_data->esd_default_on= of_property_read_bool(node, + "goodix,esd-default-on"); + if (board_data->esd_default_on) + ts_info("goodix esd-default-on enabled"); + return 0; +} +#endif + +int goodix_i2c_test(struct goodix_ts_device *dev) +{ +#define TEST_ADDR 0x4100 +#define TEST_LEN 1 + struct i2c_client *client = to_i2c_client(dev->dev); + unsigned char test_buf[TEST_LEN + 1], addr_buf[2]; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = !I2C_M_RD, + .buf = &addr_buf[0], + .len = TS_ADDR_LENGTH, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .buf = &test_buf[0], + .len = TEST_LEN, + } + }; + + msgs[0].buf[0] = (TEST_ADDR >> 8) & 0xFF; + msgs[0].buf[1] = TEST_ADDR & 0xFF; + + if (likely(i2c_transfer(client->adapter, msgs, 2) == 2)) + return 0; + + /* test failed */ + return -EINVAL; +} + +/* confirm current device is goodix or not. + * If confirmed 0 will return. + */ +static int goodix_ts_dev_confirm(struct goodix_ts_device *ts_dev) +{ +#define DEV_CONFIRM_RETRY 3 + int retry; + + for (retry = 0; retry < DEV_CONFIRM_RETRY; retry++) { + gpio_direction_output(ts_dev->board_data.reset_gpio, 0); + udelay(2000); + gpio_direction_output(ts_dev->board_data.reset_gpio, 1); + mdelay(5); + if (!goodix_i2c_test(ts_dev)) { + msleep(95); + return 0; + } + } + return -EINVAL; +} + +/** + * goodix_i2c_read_trans - read device register through i2c bus + * @dev: pointer to device data + * @addr: register address + * @data: read buffer + * @len: bytes to read + * return: 0 - read ok, < 0 - i2c transter error + */ +int goodix_i2c_read_trans(struct goodix_ts_device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + struct i2c_client *client = to_i2c_client(dev->dev); + unsigned int transfer_length = 0; + unsigned int pos = 0, address = reg; + unsigned char get_buf[64], addr_buf[2]; + int retry, r = 0; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = !I2C_M_RD, + .buf = &addr_buf[0], + .len = TS_ADDR_LENGTH, + }, { + .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(I2C_MAX_TRANSFER_SIZE < len + ? I2C_MAX_TRANSFER_SIZE : 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 >> 8) & 0xFF; + msgs[0].buf[1] = 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); + msleep(20); + } + if (unlikely(retry == GOODIX_BUS_RETRY_TIMES)) { + ts_err("I2c read failed,dev:%02x,reg:%04x,size:%u", + client->addr, reg, len); + r = -EBUS; + goto read_exit; + } + } + +read_exit: + if (unlikely(len >= sizeof(get_buf))) + kfree(msgs[1].buf); + return r; +} + +/** + * goodix_i2c_write_trans - write device register through i2c bus + * @dev: pointer to device data + * @addr: register address + * @data: write buffer + * @len: bytes to write + * return: 0 - write ok; < 0 - i2c transter error. + */ +int goodix_i2c_write_trans(struct goodix_ts_device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + struct i2c_client *client = to_i2c_client(dev->dev); + unsigned int pos = 0, transfer_length = 0; + unsigned int address = reg; + unsigned char put_buf[64]; + int retry, r = 0; + struct i2c_msg msg = { + .addr = client->addr, + .flags = !I2C_M_RD, + }; + + if (likely(len + TS_ADDR_LENGTH < sizeof(put_buf))) { + /* code optimize,use stack memory*/ + msg.buf = &put_buf[0]; + } else { + msg.buf = kmalloc(I2C_MAX_TRANSFER_SIZE < len + TS_ADDR_LENGTH + ? I2C_MAX_TRANSFER_SIZE : len + TS_ADDR_LENGTH, + GFP_KERNEL); + if (msg.buf == NULL) + return -ENOMEM; + } + + while (pos != len) { + if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE - TS_ADDR_LENGTH)) + transfer_length = I2C_MAX_TRANSFER_SIZE - TS_ADDR_LENGTH; + else + transfer_length = len - pos; + + msg.buf[0] = (unsigned char)((address >> 8) & 0xFF); + msg.buf[1] = (unsigned char)(address & 0xFF); + msg.len = transfer_length + 2; + memcpy(&msg.buf[2], &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 = -EBUS; + goto write_exit; + } + } + +write_exit: + if (likely(len + TS_ADDR_LENGTH >= sizeof(put_buf))) + kfree(msg.buf); + return r; +} + + +/** + * goodix_set_i2c_doze_mode - disable or enable doze mode + * @dev: pointer to device data + * @enable: true/flase + * return: 0 - ok; < 0 - error. + * This func must be used in pairs, when you disable doze + * mode, then you must enable it again. + * Between set_doze_false and set_doze_true, do not reset + * IC! + */ +static int goodix_set_i2c_doze_mode(struct goodix_ts_device *dev, int enable) +{ + static DEFINE_MUTEX(doze_mode_lock); + static int doze_mode_set_count; + int result = -EINVAL; + int i; + u8 w_data, r_data; + + if (dev->ic_type != IC_TYPE_NORMANDY) + return 0; + + mutex_lock(&doze_mode_lock); + + if (enable) { + if (doze_mode_set_count != 0) + doze_mode_set_count--; + + /*when count equal 0, allow ic enter doze mode*/ + if (doze_mode_set_count == 0) { + w_data = TS_DOZE_ENABLE_DATA; + for (i = 0; i < TS_DOZE_ENABLE_RETRY_TIMES; i++) { + result = goodix_i2c_write_trans(dev, + TS_REG_DOZE_CTRL, &w_data, 1); + if (!result) { + result = 0; + goto exit; + } + usleep_range(1000, 1100); + } + if (i >= TS_DOZE_ENABLE_RETRY_TIMES) + ts_err("i2c doze mode enable failed"); + } else { + /*ts_info("doze count not euqal 0, + * so skip doze mode enable"); + */ + result = 0; + goto exit; + } + } else { + doze_mode_set_count++; + + if (doze_mode_set_count == 1) { + w_data = TS_DOZE_DISABLE_DATA; + goodix_i2c_write_trans(dev, TS_REG_DOZE_CTRL, + &w_data, 1); + usleep_range(1000, 1100); + for (i = 0; i < TS_DOZE_DISABLE_RETRY_TIMES; i++) { + goodix_i2c_read_trans(dev, + TS_REG_DOZE_STAT, &r_data, 1); + if (TS_DOZE_CLOSE_OK_DATA == r_data) { + result = 0; + goto exit; + } else if (0xAA != r_data) { + w_data = TS_DOZE_DISABLE_DATA; + goodix_i2c_write_trans(dev, + TS_REG_DOZE_CTRL, &w_data, 1); + } + usleep_range(10000, 10100); + } + ts_err("doze mode disable FAILED"); + } else { + result = 0; + goto exit; + } + } + +exit: + mutex_unlock(&doze_mode_lock); + return result; +} + +/** + * goodix_i2c_write - write device register through i2c bus + * @dev: pointer to device data + * @addr: register address + * @data: write buffer + * @len: bytes to write + * return: 0 - write ok; < 0 - i2c transter error. + */ +int goodix_i2c_write(struct goodix_ts_device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + int r = -EINVAL; + + if (goodix_set_i2c_doze_mode(dev, false)) { + ts_err(" faild disable doze, i2c write:0x%04x", reg); + goto exit; + } + r = goodix_i2c_write_trans(dev, reg, data, len); + +exit: + if (goodix_set_i2c_doze_mode(dev, true)) + ts_err("failed enable doze write:0x%04x", reg); + + return r; +} + +/** + * goodix_i2c_read - read device register through i2c bus + * @dev: pointer to device data + * @addr: register address + * @data: read buffer + * @len: bytes to read + * return: 0 - read ok, < 0 - i2c transter error + */ +int goodix_i2c_read(struct goodix_ts_device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + int r = -EINVAL; + + if (goodix_set_i2c_doze_mode(dev, false)) { + ts_err("failed disable doze:0x%04x", reg); + goto exit; + } + r = goodix_i2c_read_trans(dev, reg, data, len); + +exit: + if (goodix_set_i2c_doze_mode(dev, true)) + ts_err("failed enable doze :0x%04x", reg); + + return r; +} + +/** + * goodix_i2c_write_trans_once + * write device register through i2c bus, no retry + * @dev: pointer to device data + * @addr: register address + * @data: write buffer + * @len: bytes to write + * return: 0 - write ok; < 0 - i2c transter error. + */ +int goodix_i2c_write_trans_once(struct goodix_ts_device *dev, unsigned int reg, + unsigned char *data, unsigned int len) +{ + struct i2c_client *client = to_i2c_client(dev->dev); + unsigned int pos = 0, transfer_length = 0; + unsigned int address = reg; + unsigned char put_buf[64]; + struct i2c_msg msg = { + .addr = client->addr, + .flags = !I2C_M_RD, + }; + + if (likely(len + TS_ADDR_LENGTH < sizeof(put_buf))) { + /* code optimize,use stack memory*/ + msg.buf = &put_buf[0]; + } else { + msg.buf = kmalloc(I2C_MAX_TRANSFER_SIZE < len + TS_ADDR_LENGTH + ? I2C_MAX_TRANSFER_SIZE : len + TS_ADDR_LENGTH, + GFP_KERNEL); + if (msg.buf == NULL) { + ts_err("Malloc failed"); + return -ENOMEM; + } + } + + while (pos != len) { + if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE - TS_ADDR_LENGTH)) + transfer_length = I2C_MAX_TRANSFER_SIZE - TS_ADDR_LENGTH; + else + transfer_length = len - pos; + + msg.buf[0] = (unsigned char)((address >> 8) & 0xFF); + msg.buf[1] = (unsigned char)(address & 0xFF); + msg.len = transfer_length + 2; + memcpy(&msg.buf[2], &data[pos], transfer_length); + + i2c_transfer(client->adapter, &msg, 1); + pos += transfer_length; + address += transfer_length; + } + + if (likely(len + TS_ADDR_LENGTH >= sizeof(put_buf))) + kfree(msg.buf); + return 0; +} + +void goodix_cmd_init(struct goodix_ts_device *dev, + struct goodix_ts_cmd *ts_cmd, + u8 cmds, u16 cmd_data, u32 reg_addr) +{ + u16 checksum = 0; + ts_cmd->initialized = false; + if (!reg_addr || !cmds) + return; + + if (dev->ic_type == IC_TYPE_YELLOWSTONE) { + ts_cmd->cmd_reg = reg_addr; + ts_cmd->length = 5; + ts_cmd->cmds[0] = cmds; + ts_cmd->cmds[1] = (cmd_data >> 8) & 0xFF; + ts_cmd->cmds[2] = cmd_data & 0xFF; + checksum = ts_cmd->cmds[0] + ts_cmd->cmds[1] + + ts_cmd->cmds[2]; + ts_cmd->cmds[3] = (checksum >> 8) & 0xFF; + ts_cmd->cmds[4] = checksum & 0xFF; + ts_cmd->initialized = true; + } else if (dev->ic_type == IC_TYPE_NORMANDY) { + ts_cmd->cmd_reg = reg_addr; + ts_cmd->length = 3; + ts_cmd->cmds[0] = cmds; + ts_cmd->cmds[1] = cmd_data & 0xFF; + ts_cmd->cmds[2] = 0 - cmds - cmd_data; + ts_cmd->initialized = true; + } else { + ts_err("unsupported ic type"); + } +} + +/** + * goodix_send_command - seng cmd to firmware + * + * @dev: pointer to device + * @cmd: pointer to command struct which cotain command data + * Returns 0 - succeed,<0 - failed + */ +int goodix_send_command(struct goodix_ts_device *dev, + struct goodix_ts_cmd *cmd) +{ + int ret; + + if (!cmd || !cmd->initialized) + return -EINVAL; + + ret = goodix_i2c_write(dev, cmd->cmd_reg, cmd->cmds, cmd->length); + + return ret; +} + +static int goodix_read_version(struct goodix_ts_device *dev, + struct goodix_ts_version *version) +{ + u8 buffer[GOODIX_PID_MAX_LEN + 1]; + u8 temp_buf[256]; + u16 checksum = 0; + u8 pid_read_len = dev->reg.pid_len; + u8 vid_read_len = dev->reg.vid_len; + u8 sensor_id_mask = dev->reg.sensor_id_mask; + int r; + + if (!version) { + ts_err("pointer of version is NULL"); + return -EINVAL; + } + version->valid = false; + + /*check reg info valid*/ + if (!dev->reg.pid || !dev->reg.sensor_id || !dev->reg.vid) { + ts_err("reg is NULL, pid:0x%04x, vid:0x%04x, sensor_id:0x%04x", + dev->reg.pid, dev->reg.vid, dev->reg.sensor_id); + return -EINVAL; + } + if (!pid_read_len || pid_read_len > GOODIX_PID_MAX_LEN || + !vid_read_len || vid_read_len > GOODIX_VID_MAX_LEN) { + ts_err("invalied pid vid length, pid_len:%d, vid_len:%d", + pid_read_len, vid_read_len); + return -EINVAL; + } + + /*disable doze mode, just valid for normandy + * this func must be used in pairs + */ + if (goodix_set_i2c_doze_mode(dev, false)) { + ts_err("failed disable doze"); + r = -EINVAL; + goto exit; + } + + /*check checksum*/ + if (dev->reg.version_base) { + r = goodix_i2c_read(dev, dev->reg.version_base, + temp_buf, dev->reg.version_len); + if (r < 0) { + ts_err("Read version base failed, reg:0x%02x, len:%d", + dev->reg.version_base, dev->reg.version_len); + if (version) + version->valid = false; + goto exit; + } + + if (dev->ic_type == IC_TYPE_YELLOWSTONE) + checksum = checksum_u8_ys(temp_buf, dev->reg.version_len); + else + checksum = checksum_u8(temp_buf, dev->reg.version_len); + if (checksum) { + ts_err("checksum error:0x%02x, base:0x%02x, len:%d", + checksum, dev->reg.version_base, + dev->reg.version_len); + ts_err("%*ph", (int)(dev->reg.version_len / 2), + temp_buf); + ts_err("%*ph", (int)(dev->reg.version_len - + dev->reg.version_len / 2), + &temp_buf[dev->reg.version_len / 2]); + + if (version) + version->valid = false; + r = -EINVAL; + goto exit; + } + } + + /*read pid*/ + memset(buffer, 0, sizeof(buffer)); + memset(version->pid, 0, sizeof(version->pid)); + r = goodix_i2c_read(dev, dev->reg.pid, buffer, pid_read_len); + if (r < 0) { + ts_err("Read pid failed"); + if (version) + version->valid = false; + goto exit; + } + + /* check pid is digit or not, current we only support digital pid */ + if (!isdigit(buffer[0]) || !isdigit(buffer[1])) { + ts_err("pid not digit: 0x%x,0x%x", buffer[0], buffer[1]); + r = -EINVAL; + goto exit; + } + + memcpy(version->pid, buffer, pid_read_len); + + /*read vid*/ + memset(buffer, 0, sizeof(buffer)); + memset(version->vid, 0, sizeof(version->vid)); + r = goodix_i2c_read(dev, dev->reg.vid, buffer, vid_read_len); + if (r < 0) { + ts_err("Read vid failed"); + if (version) + version->valid = false; + goto exit; + } + memcpy(version->vid, buffer, vid_read_len); + + /*read sensor_id*/ + memset(buffer, 0, sizeof(buffer)); + r = goodix_i2c_read(dev, dev->reg.sensor_id, buffer, 1); + if (r < 0) { + ts_err("Read sensor_id failed"); + if (version) + version->valid = false; + goto exit; + } + if (sensor_id_mask != 0) { + version->sensor_id = buffer[0] & sensor_id_mask; + ts_info("sensor_id_mask:0x%02x, sensor_id:0x%02x", + sensor_id_mask, version->sensor_id); + } else { + version->sensor_id = buffer[0]; + } + + version->valid = true; + + ts_info("PID:%s,SensorID:%d, VID:%*ph", version->pid, + version->sensor_id, (int)sizeof(version->vid), version->vid); +exit: + /*enable doze mode, just valid for normandy + * this func must be used in pairs + */ + goodix_set_i2c_doze_mode(dev, true); + + return r; +} + +static int goodix_wait_cfg_cmd_ready(struct goodix_ts_device *dev, + u8 right_cmd, u8 send_cmd) +{ + int try_times = 0; + u8 cmd_flag = 0; + u8 cmd_buf[3] = {0}; + u16 command_reg = dev->reg.command; + struct goodix_ts_cmd ts_cmd; + + goodix_cmd_init(dev, &ts_cmd, send_cmd, 0, command_reg); + + for (try_times = 0; try_times < TS_WAIT_CFG_READY_RETRY_TIMES; + try_times++) { + if (goodix_i2c_read(dev, command_reg, cmd_buf, 3)) { + ts_err("Read cmd_reg error"); + return -EINVAL; + } + cmd_flag = cmd_buf[0]; + if (cmd_flag == right_cmd) { + return 0; + } else if (cmd_flag != send_cmd) { + ts_err("failed cmd_reg:0x%X, 0x%X, 0x%X", + cmd_buf[0], cmd_buf[1], cmd_buf[2]); + if (goodix_send_command(dev, &ts_cmd)) { + ts_err("Resend cmd 0x%02X FAILED", send_cmd); + return -EINVAL; + } + } + usleep_range(10000, 11000); + } + + return -EINVAL; +} + +static int _do_goodix_send_config(struct goodix_ts_device *dev, + struct goodix_ts_config *config) +{ + int r = 0; + int try_times = 0; + u8 buf[3] = {0}; + u16 command_reg = dev->reg.command; + u16 cfg_reg = dev->reg.cfg_addr; + struct goodix_ts_cmd ts_cmd; + + /*1. Inquire command_reg until it's free*/ + for (try_times = 0; try_times < TS_WAIT_CMD_FREE_RETRY_TIMES; + try_times++) { + if (!goodix_i2c_read(dev, command_reg, buf, 1) && + buf[0] == TS_CMD_REG_READY) + break; + usleep_range(10000, 11000); + } + if (try_times >= TS_WAIT_CMD_FREE_RETRY_TIMES) { + ts_err("failed send cfg, reg:0x%04x is not 0xff", + command_reg); + r = -EINVAL; + goto exit; + } + + /*2. send "start write cfg" command*/ + goodix_cmd_init(dev, &ts_cmd, COMMAND_START_SEND_CFG, + 0, dev->reg.command); + if (goodix_send_command(dev, &ts_cmd)) { + ts_err("failed send cfg, COMMAND_START_SEND_CFG ERROR"); + r = -EINVAL; + goto exit; + } + + /*3. wait ic set command_reg to 0x82*/ + if (goodix_wait_cfg_cmd_ready(dev, COMMAND_SEND_CFG_PREPARE_OK, + COMMAND_START_SEND_CFG)) { + ts_err("failed send cfg, reg:0x%04x is not 0x82", + command_reg); + r = -EINVAL; + goto exit; + } + + /*4. write cfg*/ + if (goodix_i2c_write(dev, cfg_reg, config->data, config->length)) { + ts_err("Send cfg FAILED, write cfg to fw ERROR"); + r = -EINVAL; + goto exit; + } + + /*5. send "end send cfg" command*/ + goodix_cmd_init(dev, &ts_cmd, COMMAND_END_SEND_CFG, + 0, dev->reg.command); + if (goodix_send_command(dev, &ts_cmd)) { + ts_err("failed send cfg, COMMAND_END_SEND_CFG ERROR"); + r = -EINVAL; + goto exit; + } + + if (dev->ic_type == IC_TYPE_YELLOWSTONE) { + /*6. wait 0x7f or 0x7e */ + for (try_times = 0; try_times < TS_WAIT_CMD_FREE_RETRY_TIMES; + try_times++) { + r = goodix_i2c_read(dev, command_reg, buf, 3); + if (!r && (buf[0] == TS_CMD_CFG_ERR || + buf[0] == TS_CMD_CFG_OK)) + break; + usleep_range(10000, 11000); + } + ts_info("send config result: %*ph", 3, buf); + /* set 0x7D to end send config process */ + goodix_cmd_init(dev, &ts_cmd, COMMAND_END_SEND_CFG_YS, + 0, dev->reg.command); + if (goodix_send_command(dev, &ts_cmd)) { + ts_err("failed send cfg end cmd"); + r = -EINVAL; + goto exit; + } + + if (try_times >= TS_WAIT_CMD_FREE_RETRY_TIMES) { + ts_err("failed get result"); + r = -EINVAL; + goto exit; + } + if (buf[0] == TS_CMD_CFG_ERR) { + if (buf[2] == TS_CFG_REPLY_DATA_EQU) { + ts_info("config data equal with flash"); + r = 0; + } else { + ts_err("failed send cfg"); + r = -EINVAL; + } + goto exit; + } + } else { + /*6. wait ic set command_reg to 0xff*/ + for (try_times = 0; try_times < TS_WAIT_CMD_FREE_RETRY_TIMES; + try_times++) { + if (!goodix_i2c_read(dev, command_reg, buf, 1) && + buf[0] == TS_CMD_REG_READY) + break; + usleep_range(10000, 11000); + } + if (try_times >= TS_WAIT_CMD_FREE_RETRY_TIMES) { + ts_err("failed send cfg, reg:0x%04x is 0x%x not 0xff", + command_reg, buf[0]); + r = -EINVAL; + goto exit; + } + } + + ts_info("Send cfg SUCCESS"); + r = 0; + +exit: + return r; +} + +static int goodix_send_config(struct goodix_ts_device *dev, + struct goodix_ts_config *config) +{ + int r = 0; + + if (!config || !config->initialized) { + ts_err("invalid config data"); + return -EINVAL; + } + + ts_info("ver:%02xh,size:%d", config->data[0], config->length); + mutex_lock(&config->lock); + + /*disable doze mode*/ + if (!goodix_set_i2c_doze_mode(dev, false)) { + r = _do_goodix_send_config(dev, config); + } else { + ts_err("failed disable doze[abort]"); + r = -EINVAL; + } + /*enable doze mode*/ + goodix_set_i2c_doze_mode(dev, true); + + mutex_unlock(&config->lock); + return r; +} + +/* success return config length else return -1 */ +static int goodix_read_config_ys(struct goodix_ts_device *dev, + u8 *buf, int size) +{ + u32 cfg_addr = dev->reg.cfg_addr; + int sub_bags = 0; + int offset = 0; + int subbag_len; + u16 checksum; + int i; + int ret; + + ret = goodix_i2c_read(dev, cfg_addr, buf, TS_CFG_HEAD_LEN_YS); + if (ret) + goto err_out; + + offset = TS_CFG_HEAD_LEN_YS; + sub_bags = buf[TS_CFG_BAG_NUM_INDEX]; + checksum = checksum_u8_ys(buf, TS_CFG_HEAD_LEN_YS); + if (checksum) { + ts_err("Config head checksum err:0x%x,data:%*ph", + checksum, TS_CFG_HEAD_LEN_YS, buf); + ret = -EINVAL; + goto err_out; + } + + ts_info("config_version:%u, vub_bags:%u", buf[0], sub_bags); + for (i = 0; i < sub_bags; i++) { + /* read sub head [0]: sub bag num, [1]: sub bag length */ + if (offset + 2 >= size) { + ts_err("config size overflow, %d", offset + 2); + ret = -EINVAL; + goto err_out; + } + ret = goodix_i2c_read(dev, cfg_addr + offset, buf + offset, 2); + if (ret) + goto err_out; + + /* read sub bag data */ + subbag_len = buf[offset + 1]; + + ts_debug("sub bag num:%u,sub bag length:%u", + buf[offset], subbag_len); + if (offset + 2 + subbag_len + 2 > size) { + ts_err("config size overflow, %d", offset + 2); + ret = -EINVAL; + goto err_out; + } + ret = goodix_i2c_read(dev, cfg_addr + offset + 2, + buf + offset + 2, subbag_len + 2); + if (ret) + goto err_out; + checksum = checksum_u8_ys(buf + offset, subbag_len + 4); + if (checksum) { + ts_err("sub bag checksum err:0x%x", checksum); + ret = -EINVAL; + goto err_out; + } + offset += subbag_len + 4; + ts_debug("sub bag %d, data:%*ph", + buf[offset], buf[offset + 1] + 4, buf + offset); + } + ret = offset; + +err_out: + return ret; +} + +/* success return config length else return -1 */ +static int goodix_read_config_nor(struct goodix_ts_device *dev, + u8 *buf, int size) +{ + u32 cfg_addr = dev->reg.cfg_addr; + int sub_bags = 0; + int offset = 0; + int subbag_len; + u8 checksum; + int i; + int ret; + + /*disable doze mode*/ + if (goodix_set_i2c_doze_mode(dev, false)) { + ts_err("failed disable doze mode[abort]"); + ret = -EINVAL; + goto err_out; + } + + ret = goodix_i2c_read(dev, cfg_addr, buf, TS_CFG_HEAD_LEN); + if (ret) + goto err_out; + + offset = TS_CFG_BAG_START_INDEX; + sub_bags = buf[TS_CFG_BAG_NUM_INDEX]; + checksum = checksum_u8(buf, TS_CFG_HEAD_LEN); + if (checksum) { + ts_err("Config head checksum err:0x%x,data:%*ph", + checksum, TS_CFG_HEAD_LEN, buf); + ret = -EINVAL; + goto err_out; + } + + ts_info("config_version:%u, vub_bags:%u", buf[0], sub_bags); + for (i = 0; i < sub_bags; i++) { + /* read sub head [0]: sub bag num, [1]: sub bag length */ + if (offset + 2 >= size) { + ts_err("config size overflow, %d", offset + 2); + ret = -EINVAL; + goto err_out; + } + ret = goodix_i2c_read(dev, cfg_addr + offset, buf + offset, 2); + if (ret) + goto err_out; + + /* read sub bag data */ + subbag_len = buf[offset + 1]; + + ts_debug("sub bag num:%u,sub bag length:%u", + buf[offset], subbag_len); + if (offset + 2 + subbag_len + 1 >= size) { + ts_err("config size overflow, %d", offset + 2 + subbag_len + 1); + ret = -EINVAL; + goto err_out; + } + ret = goodix_i2c_read(dev, cfg_addr + offset + 2, + buf + offset + 2, subbag_len + 1); + if (ret) + goto err_out; + checksum = checksum_u8(buf + offset, subbag_len + 3); + if (checksum) { + ts_err("sub bag checksum err:0x%x", checksum); + ret = -EINVAL; + goto err_out; + } + offset += subbag_len + 3; + ts_debug("sub bag %d, data:%*ph", + buf[offset], buf[offset + 1] + 3, buf + offset); + } + ret = offset; + +err_out: + /*enable doze mode*/ + goodix_set_i2c_doze_mode(dev, true); + + return ret; +} + +/* success return config_len, <= 0 failed */ +static int goodix_read_config(struct goodix_ts_device *dev, + u8 *config_data, int size) +{ + struct goodix_ts_cmd ts_cmd; + u8 cmd_flag; + u32 cmd_reg = dev->reg.command; + int r = 0; + int i; + + if (!config_data) { + ts_err("Illegal params"); + return -EINVAL; + } + if (!dev->reg.command) { + ts_err("command register ERROR:0x%04x", dev->reg.command); + return -EINVAL; + } + + /*disable doze mode*/ + if (goodix_set_i2c_doze_mode(dev, false)) { + ts_err("failed disabled doze[abort]"); + r = -EINVAL; + goto exit; + } + + /* wait for IC in IDLE state */ + for (i = 0; i < TS_WAIT_CMD_FREE_RETRY_TIMES; i++) { + cmd_flag = 0; + r = goodix_i2c_read(dev, cmd_reg, &cmd_flag, 1); + if (r < 0 || cmd_flag == TS_CMD_REG_READY) + break; + usleep_range(10000, 11000); + } + if (cmd_flag != TS_CMD_REG_READY) { + ts_err("Wait for IC ready IDEL state timeout:addr 0x%x\n", + cmd_reg); + r = -EAGAIN; + goto exit; + } + /* 0x86 read config command */ + goodix_cmd_init(dev, &ts_cmd, COMMAND_START_READ_CFG, + 0, cmd_reg); + r = goodix_send_command(dev, &ts_cmd); + if (r) { + ts_err("Failed send read config command"); + goto exit; + } + /* wait for config data ready */ + if (goodix_wait_cfg_cmd_ready(dev, COMMAND_READ_CFG_PREPARE_OK, + COMMAND_START_READ_CFG)) { + ts_err("Wait for config data ready timeout"); + r = -EAGAIN; + goto exit; + } + + if (dev->ic_type == IC_TYPE_YELLOWSTONE) + r = goodix_read_config_ys(dev, config_data, size); + else + r = goodix_read_config_nor(dev, config_data, size); + if (r <= 0) + ts_err("Failed read config data"); + + /* clear command */ + goodix_cmd_init(dev, &ts_cmd, TS_CMD_REG_READY, 0, cmd_reg); + goodix_send_command(dev, &ts_cmd); + +exit: + /*enable doze mode*/ + goodix_set_i2c_doze_mode(dev, true); + + return r; +} + +/** + * goodix_hw_reset - reset device + * + * @dev: pointer to touch device + * Returns 0 - succeed,<0 - failed + */ +int goodix_hw_reset(struct goodix_ts_device *dev) +{ + u8 data[2] = {0x00}; + int r = 0; + + ts_info("HW reset"); + + gpio_direction_output(dev->board_data.reset_gpio, 0); + udelay(2000); + gpio_direction_output(dev->board_data.reset_gpio, 1); + msleep(100); + + /*init dynamic esd*/ + if (dev->reg.esd) { + r = goodix_i2c_write_trans(dev, dev->reg.esd, data, 1); + if (r < 0) + ts_err("IC reset, init dynamic esd FAILED"); + } else { + ts_info("reg.esd is NULL, skip dynamic esd init"); + } + + return 0; +} + +/** + * goodix_request_handler - handle firmware request + * + * @dev: pointer to touch device + * @request_data: requset information + * Returns 0 - succeed,<0 - failed + */ +static int goodix_request_handler(struct goodix_ts_device *dev) +{ + unsigned char buffer[1]; + int r; + + r = goodix_i2c_read_trans(dev, dev->reg.fw_request, buffer, 1); + if (r < 0) + return r; + + switch (buffer[0]) { + case REQUEST_CONFIG: + ts_info("HW request config"); + r = goodix_send_config(dev, &(dev->normal_cfg)); + if (r != 0) + ts_info("request config, send config faild"); + break; + case REQUEST_BAKREF: + ts_info("HW request bakref"); + break; + case REQUEST_RESET: + ts_info("HW requset reset"); + r = goodix_hw_reset(dev); + if (r != 0) + ts_info("request reset, reset faild"); + break; + /*case REQUEST_RELOADFW: + ts_info("HW request reload fw"); + goodix_do_fw_update(UPDATE_MODE_FORCE|UPDATE_MODE_SRC_REQUEST); + break;*/ + case REQUEST_IDLE: + ts_info("HW request idle"); + break; + default: + ts_info("Unknown hw request:%d", buffer[0]); + break; + } + + buffer[0] = 0x00; + r = goodix_i2c_write_trans(dev, dev->reg.fw_request, buffer, 1); + return r; +} + +static void goodix_swap_coords(struct goodix_ts_device *dev, + unsigned int *coor_x, unsigned int *coor_y) +{ + unsigned int temp; + struct goodix_ts_board_data *bdata = &dev->board_data; + + if (bdata->swap_axis) { + temp = *coor_x; + *coor_x = *coor_y; + *coor_y = temp; + } + if (bdata->x2x) + *coor_x = bdata->panel_max_x - *coor_x; + if (bdata->y2y) + *coor_y = bdata->panel_max_y - *coor_y; +} + +#define GOODIX_KEY_STATE 0x10 +static void goodix_parse_finger_nor(struct goodix_ts_device *dev, + struct goodix_touch_data *touch_data, unsigned char *buf, int touch_num) +{ + unsigned int id = 0, x = 0, y = 0, w = 0; + static u8 pre_key_map; + u8 cur_key_map = 0; + static u32 pre_finger_map; + u32 cur_finger_map = 0; + u8 *coor_data; + int i; + + coor_data = &buf[IRQ_HEAD_LEN_NOR]; + for (i = 0; i < touch_num; i++) { + id = coor_data[0]; + if(id >= GOODIX_MAX_TOUCH){ + ts_info("invaild finger id =%d", id); + break; + } + x = le16_to_cpup((__be16 *) (coor_data + 1)); + y = le16_to_cpup((__be16 *) (coor_data + 3)); + w = coor_data[5]; + goodix_swap_coords(dev, &x, &y); + 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; + cur_finger_map |= (1 << id); + coor_data += BYTES_PER_COORD; + } + + /* process finger release */ + for (i = 0; i < GOODIX_MAX_TOUCH; i++) { + if (cur_finger_map & (1 << i)) + continue; + if (pre_finger_map & (1 << i)) + touch_data->coords[i].status = TS_RELEASE; + } + pre_finger_map = cur_finger_map; + touch_data->touch_num = touch_num; + + if (buf[1] & GOODIX_KEY_STATE) { + /* have key */ + cur_key_map = buf[touch_num * BYTES_PER_COORD + 2] & 0x0F; + for (i = 0; i < GOODIX_MAX_TP_KEY; i++) { + if (cur_key_map & (1 << i)) { + touch_data->keys[i].status = TS_TOUCH; + touch_data->keys[i].code = + dev->board_data.panel_key_map[i]; + } + } + } + /* process key release */ + for (i = 0; i < GOODIX_MAX_TP_KEY; i++) { + if (cur_key_map & (1 << i) || !(pre_key_map & (1 << i))) + continue; + touch_data->keys[i].status = TS_RELEASE; + touch_data->keys[i].code = dev->board_data.panel_key_map[i]; + } + pre_key_map = cur_key_map; +} + +static void goodix_parse_finger_ys(struct goodix_ts_device *dev, + struct goodix_touch_data *touch_data, unsigned char *buf, int touch_num) +{ + unsigned int id = 0, x = 0, y = 0, w = 0; + static u32 pre_finger_map; + u32 cur_finger_map = 0; + u8 *coor_data; + int i; + + coor_data = &buf[IRQ_HEAD_LEN_YS]; + for (i = 0; i < touch_num; i++) { + id = (coor_data[0] >> 4) & 0x0F; + if(id >= GOODIX_MAX_TOUCH){ + ts_info("invaild finger id =%d", id); + break; + } + x = be16_to_cpup((__be16 *)(coor_data + 2)); + y = be16_to_cpup((__be16 *)(coor_data + 4)); + w = be16_to_cpup((__be16 *)(coor_data + 6)); + goodix_swap_coords(dev, &x, &y); + 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; + cur_finger_map |= (1 << id); + coor_data += BYTES_PER_COORD; + } + + /* process finger release */ + for (i = 0; i < GOODIX_MAX_TOUCH; i++) { + if (cur_finger_map & (1 << i)) + continue; + if (pre_finger_map & (1 << i)) + touch_data->coords[i].status = TS_RELEASE; + } + pre_finger_map = cur_finger_map; + touch_data->touch_num = touch_num; +} + +static unsigned int goodix_pen_btn_code[] = {BTN_STYLUS, BTN_STYLUS2}; +static void goodix_parse_pen_nor(struct goodix_ts_device *dev, + struct goodix_pen_data *pen_data, unsigned char *buf, int touch_num) +{ + unsigned int id = 0; + static u8 pre_key_map; + u8 cur_key_map = 0; + static u32 pre_pen_status; + u32 cur_pen_status = 0; + u8 *coor_data; + int i; + + coor_data = &buf[2]; + for (i = 0; i < touch_num; i++) { + /* search for pen coordinate */ + id = coor_data[0]; + if (id < 0x80) { + coor_data += BYTES_PER_COORD; + continue; + } + pen_data->coords.x = le16_to_cpup((__be16 *)(coor_data + 1)); + pen_data->coords.y = le16_to_cpup((__be16 *)(coor_data + 3)); + pen_data->coords.p = le16_to_cpup((__be16 *)(coor_data + 5)); + goodix_swap_coords(dev, &pen_data->coords.x, + &pen_data->coords.y); + pen_data->coords.status = TS_TOUCH; + pen_data->coords.tool_type = BTN_TOOL_PEN; + cur_pen_status = 1; + /* currently only support one stylus */ + break; + } + if (!cur_pen_status && pre_pen_status) { + pen_data->coords.status = TS_RELEASE; + } + pre_pen_status = cur_pen_status; + + /* process pen button */ + if (buf[1] & GOODIX_KEY_STATE) { + cur_key_map = (buf[touch_num * BYTES_PER_COORD + 2] >> 4) & 0x0F; + for (i = 0; i < GOODIX_MAX_PEN_KEY; i++) { + if (!(cur_key_map & (1 << i))) + continue; + pen_data->keys[i].status = TS_TOUCH; + pen_data->keys[i].code = goodix_pen_btn_code[i]; + } + } + for (i = 0; i < GOODIX_MAX_PEN_KEY; i++) { + if (cur_key_map & (1 << i) || !(pre_key_map & (1 << i))) + continue; + pen_data->keys[i].status = TS_RELEASE; + pen_data->keys[i].code = goodix_pen_btn_code[i]; + } + pre_key_map = cur_key_map; +} + +static void goodix_parse_pen_ys(struct goodix_ts_device *dev, + struct goodix_pen_data *pen_data, unsigned char *buf, int touch_num) +{ + ts_info("unsupported"); +} + +static int goodix_touch_handler_ys(struct goodix_ts_device *dev, + struct goodix_ts_event *ts_event, + u8 *pre_buf, u32 pre_buf_len) +{ + struct goodix_touch_data *touch_data = &ts_event->touch_data; + struct goodix_pen_data *pen_data = &ts_event->pen_data; + static u8 buffer[IRQ_HEAD_LEN_YS + + BYTES_PER_COORD * GOODIX_MAX_TOUCH + 2]; + int touch_num = 0, r = -EINVAL; + u8 point_type = 0; + u16 chksum = 0; + + static u8 pre_finger_num = 0; + static u8 pre_pen_num = 0; + + /* 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 (unlikely(touch_num > GOODIX_MAX_TOUCH)) { + touch_num = -EINVAL; + goto exit_clean_sta; + } + if (unlikely(touch_num > 1)) { + r = goodix_i2c_read_trans(dev, + dev->reg.coor + pre_buf_len, + &buffer[pre_buf_len], + (touch_num - 1) * BYTES_PER_COORD); + if (unlikely(r < 0)) + goto exit_clean_sta; + } + + if (touch_num > 0) { + chksum = checksum_u8_ys(&buffer[IRQ_HEAD_LEN_YS], + touch_num * BYTES_PER_COORD + 2); + if (unlikely(chksum != 0)) { + ts_debug("checksum error:%x", chksum); + r = -EINVAL; + goto exit_clean_sta; + } + } + if (touch_num > 0) + point_type = buffer[(touch_num - 1) * BYTES_PER_COORD + IRQ_HEAD_LEN_YS] & 0x0F; + if (touch_num >= 1 && (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_ys(dev, touch_data, buffer, 0); + pre_finger_num = 0; + } else { + pre_pen_num = 1; + ts_event->event_type = EVENT_PEN; + goodix_parse_pen_ys(dev, pen_data, buffer, touch_num); + } + } else { + /* finger info */ + if (pre_pen_num) { + ts_event->event_type = EVENT_PEN; + goodix_parse_pen_ys(dev, pen_data, buffer, 0); + pre_pen_num = 0; + } else { + ts_event->event_type = EVENT_TOUCH; + goodix_parse_finger_ys(dev, 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"); + } +exit_clean_sta: + return r; +} + +static int goodix_touch_handler_nor(struct goodix_ts_device *dev, + struct goodix_ts_event *ts_event, + u8 *pre_buf, u32 pre_buf_len) +{ + struct goodix_touch_data *touch_data = &ts_event->touch_data; + struct goodix_pen_data *pen_data = &ts_event->pen_data; + static u8 buffer[IRQ_HEAD_LEN_NOR + + BYTES_PER_COORD * GOODIX_MAX_TOUCH + 2]; + int touch_num = 0, r = -EINVAL; + unsigned char chksum = 0; + + static u8 pre_finger_num = 0; + static u8 pre_pen_num = 0; + + /* 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[1] & 0x0F; + + if (unlikely(touch_num > GOODIX_MAX_TOUCH)) { + touch_num = -EINVAL; + goto exit_clean_sta; + } + if (unlikely(touch_num > 1)) { + r = goodix_i2c_read_trans(dev, + dev->reg.coor + pre_buf_len, + &buffer[pre_buf_len], + (touch_num - 1) * BYTES_PER_COORD); + if (unlikely(r < 0)) + goto exit_clean_sta; + } + + chksum = checksum_u8(&buffer[0], touch_num * BYTES_PER_COORD + 4); + if (unlikely(chksum != 0)) { + ts_debug("checksum error:%X, ic_type:%d", chksum, dev->ic_type); + r = -EINVAL; + goto exit_clean_sta; + } + + if (touch_num >= 1 && + buffer[(touch_num - 1) * BYTES_PER_COORD + 2] >= 0x80) { + if (pre_finger_num) { + ts_event->event_type = EVENT_TOUCH; + goodix_parse_finger_nor(dev, touch_data, buffer, 0); + pre_finger_num = 0; + } else { + pre_pen_num = 1; + ts_event->event_type = EVENT_PEN; + goodix_parse_pen_nor(dev, pen_data, buffer, touch_num); + } + } else { + if (pre_pen_num) { + ts_event->event_type = EVENT_PEN; + goodix_parse_pen_nor(dev, pen_data, buffer, 0); + pre_pen_num = 0; + } else { + ts_event->event_type = EVENT_TOUCH; + goodix_parse_finger_nor(dev, touch_data, + buffer, touch_num); + pre_finger_num = touch_num; + } + } +exit_clean_sta: + return r; +} + +static int goodix_event_handler(struct goodix_ts_device *dev, + struct goodix_ts_event *ts_event) +{ + int pre_read_len; + u8 pre_buf[32]; + u8 event_sta; + int r; + + if (dev->ic_type == IC_TYPE_YELLOWSTONE) + pre_read_len = IRQ_HEAD_LEN_YS + BYTES_PER_COORD + 2; + else + pre_read_len = IRQ_HEAD_LEN_NOR + BYTES_PER_COORD + 2; + r = goodix_i2c_read_trans(dev, dev->reg.coor, + pre_buf, pre_read_len); + if (unlikely(r < 0)) + return r; + + if (dev->ic_type == IC_TYPE_YELLOWSTONE && + checksum_u8_ys(pre_buf, IRQ_HEAD_LEN_YS)) { + ts_debug("irq head checksum error %*ph", + IRQ_HEAD_LEN_YS, pre_buf); + return -EINVAL; + } + /* buffer[0]: event state */ + event_sta = pre_buf[0]; + if (likely((event_sta & GOODIX_TOUCH_EVENT) == GOODIX_TOUCH_EVENT)) { + /* handle touch event */ + if (dev->ic_type == IC_TYPE_YELLOWSTONE) + goodix_touch_handler_ys(dev, ts_event, pre_buf, + pre_read_len); + else + goodix_touch_handler_nor(dev, ts_event, pre_buf, + pre_read_len); + } else if (unlikely((event_sta & GOODIX_REQUEST_EVENT) == + GOODIX_REQUEST_EVENT)) { + /* handle request event */ + ts_event->event_type = EVENT_REQUEST; + goodix_request_handler(dev); + } else if ((event_sta & GOODIX_GESTURE_EVENT) == + GOODIX_GESTURE_EVENT) { + /* handle gesture event */ + ts_debug("Gesture event"); + } else if ((event_sta & GOODIX_HOTKNOT_EVENT) == + GOODIX_HOTKNOT_EVENT) { + /* handle hotknot event */ + ts_debug("Hotknot event"); + } else { + ts_debug("unknow event type:0x%x", event_sta); + r = -EINVAL; + } + + return r; +} + +/** + * goodix_hw_suspend - Let touch deivce stay in lowpower mode. + * @dev: pointer to goodix touch device + * @return: 0 - succeed, < 0 - failed + */ +static int goodix_hw_suspend(struct goodix_ts_device *dev) +{ + struct goodix_ts_cmd sleep_cmd; + int r = 0; + + goodix_cmd_init(dev, &sleep_cmd, COMMAND_SLEEP, + 0, dev->reg.command); + if (sleep_cmd.initialized) { + r = goodix_send_command(dev, &sleep_cmd); + if (!r) + ts_info("Chip in sleep mode"); + } else { + ts_err("Uninitialized sleep command"); + } + return r; +} + +/** + * goodix_hw_resume - Let touch deivce stay in active mode. + * @dev: pointer to goodix touch device + * @return: 0 - succeed, < 0 - failed + */ +static int goodix_hw_resume(struct goodix_ts_device *dev) +{ + goodix_hw_reset(dev); + + return 0; +} + +static int goodix_esd_check(struct goodix_ts_device *dev) +{ + int r; + u8 data = 0; + + if (dev->reg.esd == 0) { + ts_err("esd reg is NULL"); + return 0; + } + + /*check dynamic esd*/ + if (dev->ic_type == IC_TYPE_NORMANDY) + r = dev->hw_ops->read_trans(dev, TS_REG_ESD_TICK_R, &data, 1); + else + r = dev->hw_ops->read_trans(dev, dev->reg.esd, &data, 1); + + if (r < 0 || (data == GOODIX_ESD_TICK_WRITE_DATA)) { + ts_info("dynamic esd occur, r:%d, data:0x%02x", r, data); + r = -EINVAL; + } + + return r; +} + +/* 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 raw/diff data */ +extern struct goodix_module goodix_modules; +static int goodix_capacitance_data(struct goodix_ts_device *dev, + struct ts_rawdata_info *info) +{ + struct goodix_ts_core *cd = goodix_modules.core_data; + int retry = 20; + u16 rawdata_addr = 0x8FA0; + u16 diffdata_addr = 0x9D20; + u32 tx = cd->drv_num; + u32 rx = cd->sen_num; + int size = tx * rx; + u8 irq_flag; + u8 cmds[3]; + int i; + int ret; + + if (!info) { + ts_err("goodix_capacitance_data, input null ptr"); + return -EIO; + } + + /* disable irq & close esd */ + goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); + goodix_ts_irq_enable(cd, false); + usleep_range(10000, 11000); + + /* switch rawdata mode */ + cmds[0] = 0x01; + cmds[1] = 0x00; + cmds[2] = (u8)(0x00 - 0x01); + ret = goodix_i2c_write(dev, dev->reg.command, cmds, sizeof(cmds)); + if (ret < 0) { + ts_err("switch rawdata mode failed"); + goto exit; + } + msleep(30); //wait command finish + + /* clean touch event flag */ + irq_flag = 0; + ret = goodix_i2c_write_trans(dev, dev->reg.coor, &irq_flag, 1); + if (ret < 0) { + ts_err("clean touch event failed, exit!"); + goto exit; + } + + while (retry--) { + usleep_range(5000, 5100); + ret = goodix_i2c_read_trans(dev, dev->reg.coor, &irq_flag, 1); + if (!ret && (irq_flag & 0x80)) + break; + } + if (retry < 0) { + ts_err("rawdata is not ready irq_flag:0x%02x, exit!", irq_flag); + goto exit; + } + + /* obtain rawdata & diff_rawdata */ + info->buff[0] = rx; + info->buff[1] = tx; + info->used_size = 2; + ret = goodix_i2c_read_trans(dev, rawdata_addr, + (u8 *)&info->buff[info->used_size], size * sizeof(u16)); + if (ret < 0) { + ts_err("obtian raw_data failed, exit!"); + goto exit; + } + for (i = 0; i < size; i++) + info->buff[info->used_size + i] = + be16_to_cpu(info->buff[info->used_size + i]); + goodix_rotate_abcd2cbad(tx, rx, &info->buff[info->used_size]); + info->used_size += size; + + ret = goodix_i2c_read_trans(dev, diffdata_addr, + (u8 *)&info->buff[info->used_size], size * sizeof(u16)); + if (ret < 0) { + ts_err("obtian diff_data failed, exit!"); + goto exit; + } + for (i = 0; i < size; i++) + info->buff[info->used_size + i] = + be16_to_cpu(info->buff[info->used_size + i]); + goodix_rotate_abcd2cbad(tx, rx, &info->buff[info->used_size]); + info->used_size += size; + +exit: + /* switch coor mode */ + cmds[0] = 0; + cmds[1] = 0; + cmds[2] = 0; + goodix_i2c_write_trans(dev, dev->reg.command, cmds, sizeof(cmds)); + /* clean touch event flag */ + irq_flag = 0; + goodix_i2c_write_trans(dev, dev->reg.coor, &irq_flag, 1); + msleep(30); //wait command finish + /* enable irq * esd */ + goodix_ts_irq_enable(cd, true); + goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); + return ret; +} + +/* hardware opeation funstions */ +static const struct goodix_ts_hw_ops hw_i2c_ops = { + .dev_confirm = goodix_ts_dev_confirm, + .read = goodix_i2c_read, + .write = goodix_i2c_write, + .read_trans = goodix_i2c_read_trans, + .write_trans = goodix_i2c_write_trans, + .reset = goodix_hw_reset, + .event_handler = goodix_event_handler, + .send_config = goodix_send_config, + .read_config = goodix_read_config, + .send_cmd = goodix_send_command, + .read_version = goodix_read_version, + .suspend = goodix_hw_suspend, + .resume = goodix_hw_resume, + .check_hw = goodix_esd_check, + .get_capacitance_data = goodix_capacitance_data, +}; + +static struct platform_device *goodix_pdev; + +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) +{ + struct goodix_ts_device *ts_device = NULL; + int r = 0; + + ts_info("goodix_i2c_probe IN"); + + if (client->dev.of_node && !mmi_device_is_available(client->dev.of_node)) { + ts_err("mmi: device not supported\n"); + return -ENODEV; + } + + r = i2c_check_functionality(client->adapter, + I2C_FUNC_I2C); + if (!r) + return -EIO; + + /* ts device data */ + ts_device = devm_kzalloc(&client->dev, + sizeof(struct goodix_ts_device), GFP_KERNEL); + if (!ts_device) + return -ENOMEM; + + if (IS_ENABLED(CONFIG_OF) && client->dev.of_node) { + /* parse devicetree property */ + r = goodix_parse_dt(client->dev.of_node, + &ts_device->board_data); + if (r < 0) { + ts_err("failed parse device info form dts, %d", r); + return -EINVAL; + } + } else { + ts_err("no valid device tree node found"); + return -ENODEV; + } + + ts_device->name = "Goodix TouchDevcie"; + ts_device->dev = &client->dev; + ts_device->hw_ops = &hw_i2c_ops; + + /* ts core device */ + goodix_pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL); + if (!goodix_pdev) + return -ENOMEM; + + i2c_set_clientdata(client, goodix_pdev); + + 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/platfrom/goodix_ts.0 + * goodix_pdev->dev.parent = &client->dev; + */ + goodix_pdev->dev.platform_data = ts_device; + goodix_pdev->dev.release = goodix_pdev_release; + + /* register platform device, then the goodix_ts_core + * module will probe the touch deivce. + */ + r = platform_device_register(goodix_pdev); + if (r) { + ts_err("failed register goodix platform device, %d", r); + goto err_pdev; + } + ts_info("i2c probe out"); + return r; + +err_pdev: + kfree(goodix_pdev); + goodix_pdev = NULL; + i2c_set_clientdata(client, NULL); + ts_info("i2c probe out, %d", r); + return r; +} + +static int goodix_i2c_remove(struct i2c_client *client) +{ + platform_device_unregister(goodix_pdev); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id i2c_matchs[] = { + {.compatible = "goodix,gt9885",}, + {.compatible = "goodix,gt9896",}, + {.compatible = "goodix,gt9886",}, + {.compatible = "goodix,gt9889",}, + {.compatible = "goodix,gt8589",}, + {.compatible = "goodix,gt5863",}, + {.compatible = "goodix,gt6861",}, + {.compatible = "goodix,gt6862",}, + {}, +}; +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_bus_init(void) +{ + ts_info("Goodix driver init"); + return i2c_add_driver(&goodix_i2c_driver); +} + +void goodix_bus_exit(void) +{ + i2c_del_driver(&goodix_i2c_driver); + ts_info("Goodix driver exit"); +} diff --git a/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_mmi.c b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_mmi.c new file mode 100644 index 000000000000..8dc42f5f1af6 --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_mmi.c @@ -0,0 +1,329 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * 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. + */ + +#include +#include "goodix_ts_core.h" +#include + +#define GET_GOODIX_DATA(dev) { \ + pdev = dev_get_drvdata(dev); \ + if (!pdev) { \ + ts_err("Failed to get platform device"); \ + return -ENODEV; \ + } \ + core_data = platform_get_drvdata(pdev); \ + if (!core_data) { \ + ts_err("Failed to get driver data"); \ + return -ENODEV; \ + } \ +} + +extern int gsx_gesture_before_suspend_mmi(struct goodix_ts_core *core_data); +extern void goodix_cmd_init(struct goodix_ts_device *dev, + struct goodix_ts_cmd *ts_cmd, + u8 cmds, u16 cmd_data, u32 reg_addr); +extern int goodix_send_command(struct goodix_ts_device *dev, + struct goodix_ts_cmd *cmd); + +static int goodix_ts_mmi_methods_get_vendor(struct device *dev, void *cdata) { + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", "goodix"); +} + +static int goodix_ts_mmi_methods_get_productinfo(struct device *dev, void *cdata) { + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_INFO_LEN, "%s", GOODIX_TP_IC_TYPE); +} + +static int goodix_ts_mmi_methods_get_build_id(struct device *dev, void *cdata) { + int ret; + struct goodix_ts_version fw_ver; + struct goodix_ts_device *ts_dev; + struct goodix_ts_core *core_data; + struct platform_device *pdev; + + GET_GOODIX_DATA(dev); + ts_dev = core_data->ts_dev; + if (!ts_dev) { + ts_err("Failed to get platform data"); + return -ENODEV; + } + ret = ts_dev->hw_ops->read_version(ts_dev, &fw_ver); + if (!ret && fw_ver.valid) { + ret = scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%02x", fw_ver.vid[0]); + ts_info("get config id:%d ", fw_ver.vid[0]); + return ret; + } else + return -ENODEV; +} + +/*return firmware version*/ +static int goodix_ts_mmi_methods_get_config_id(struct device *dev, void *cdata) { + int ret; + struct goodix_ts_version fw_ver; + struct goodix_ts_device *ts_dev; + struct goodix_ts_core *core_data; + struct platform_device *pdev; + + GET_GOODIX_DATA(dev); + ts_dev = core_data->ts_dev; + if (!ts_dev) { + ts_err("Failed to get platform data"); + return -ENODEV; + } + ret = ts_dev->hw_ops->read_version(ts_dev, &fw_ver); + if (!ret && fw_ver.valid) { + ret = scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%02x", fw_ver.vid[3]); + ts_info("get build id:%d ", fw_ver.vid[3]); + return ret; + } else + return -ENODEV; +} + +static int goodix_ts_mmi_methods_get_bus_type(struct device *dev, void *idata) { + TO_INT(idata) = TOUCHSCREEN_MMI_BUS_TYPE_I2C; + return 0; +} + +static int goodix_ts_mmi_methods_get_irq_status(struct device *dev, void *idata) { + struct goodix_ts_board_data *ts_bdata; + struct goodix_ts_core *core_data; + struct platform_device *pdev; + + GET_GOODIX_DATA(dev); + ts_bdata = board_data(core_data); + if (!ts_bdata) { + ts_err("Failed to get ts board data"); + return -ENODEV; + } + TO_INT(idata) = gpio_get_value(ts_bdata->irq_gpio); + return 0; +} + +static int goodix_ts_mmi_methods_get_drv_irq(struct device *dev, void *idata) { + struct goodix_ts_core *core_data; + struct platform_device *pdev; + + GET_GOODIX_DATA(dev); + TO_INT(idata) = atomic_read(&core_data->irq_enabled); + return 0; +} + +static int goodix_ts_mmi_methods_get_poweron(struct device *dev, void *idata) { + struct goodix_ts_core *core_data; + struct platform_device *pdev; + + GET_GOODIX_DATA(dev); + TO_INT(idata) = core_data->power_on; + return 0; +} + +static int goodix_ts_mmi_methods_drv_irq(struct device *dev, int state) { + struct goodix_ts_core *core_data; + struct platform_device *pdev; + + GET_GOODIX_DATA(dev); + goodix_ts_irq_enable(core_data, !(!state)); + return 0; +} + +/* reset chip + * type:can control software reset and hardware reset, + * but GOODIX has no software reset, + * so the type parameter is not used here. + */ +static int goodix_ts_mmi_methods_reset(struct device *dev, int type) { + int ret = -ENODEV; + struct goodix_ts_device *ts_dev; + struct goodix_ts_core *core_data; + struct platform_device *pdev; + + GET_GOODIX_DATA(dev); + ts_dev = core_data->ts_dev; + if (ts_dev->hw_ops->reset) + ret = ts_dev->hw_ops->reset(ts_dev); + return ret; +} + +static int goodix_ts_mmi_methods_power(struct device *dev, int on) { + struct goodix_ts_core *core_data; + struct platform_device *pdev; + + GET_GOODIX_DATA(dev); + + if (on == TS_MMI_POWER_ON) + return goodix_ts_power_on(core_data); + else if(on == TS_MMI_POWER_OFF) + return goodix_ts_power_off(core_data); + else { + ts_err("Invalid power parameter %d.\n", on); + return -EINVAL; + } +} + +#define CHARGER_ENTER_MODE_CMD 0x06 +#define CHARGER_EXIT_MODE_CMD 0x07 +static int goodix_ts_mmi_charger_mode(struct device *dev, int mode) +{ + int ret = 0; + struct goodix_ts_device *ts_dev; + struct platform_device *pdev; + struct goodix_ts_core *core_data; + struct goodix_ts_cmd charge_cmd; + + GET_GOODIX_DATA(dev); + ts_dev = core_data->ts_dev; + + if (mode) + goodix_cmd_init(ts_dev, &charge_cmd, CHARGER_ENTER_MODE_CMD, + 0, ts_dev->reg.command); + else + goodix_cmd_init(ts_dev, &charge_cmd, CHARGER_EXIT_MODE_CMD, + 0, ts_dev->reg.command); + + if (charge_cmd.initialized) { + ret= goodix_send_command(ts_dev, &charge_cmd); + if (ret < 0) + ts_err("Failed to set charger mode\n"); + } else { + ts_err("Uninitialized charge command"); + } + + msleep(16); + ts_info("Success to %s charger mode\n", mode ? "Enable" : "Disable"); + + return ret; +} + +static int goodix_ts_mmi_panel_state(struct device *dev, + enum ts_mmi_pm_mode from, enum ts_mmi_pm_mode to) +{ + struct goodix_ts_device *ts_dev; + struct goodix_ts_core *core_data; + struct platform_device *pdev; + + GET_GOODIX_DATA(dev); + ts_dev = core_data->ts_dev; + + switch (to) { + case TS_MMI_PM_GESTURE: + gsx_gesture_before_suspend_mmi(core_data); + msleep(16); + enable_irq_wake(core_data->irq); + core_data->gesture_enable = true; + break; + case TS_MMI_PM_DEEPSLEEP: + /* enter sleep mode or power off */ + if (ts_dev && ts_dev->hw_ops->suspend) + ts_dev->hw_ops->suspend(ts_dev); + core_data->gesture_enable = false; + break; + case TS_MMI_PM_ACTIVE: + if (ts_dev && ts_dev->hw_ops->resume) + ts_dev->hw_ops->resume(ts_dev); + core_data->gesture_enable = false; + break; + default: + ts_err("Invalid power state parameter %d.\n", to); + return -EINVAL; + } + + return 0; +} + +static int goodix_ts_mmi_pre_resume(struct device *dev) +{ + struct goodix_ts_device *ts_dev; + struct goodix_ts_core *core_data; + struct platform_device *pdev; + + GET_GOODIX_DATA(dev); + ts_dev = core_data->ts_dev; + + atomic_set(&core_data->suspended, 0); + if (core_data->gesture_enable) + disable_irq_wake(core_data->irq); + + return 0; +} + +static int goodix_ts_mmi_post_resume(struct device *dev) +{ + return 0; +} + +static int goodix_ts_mmi_pre_suspend(struct device *dev) +{ + struct goodix_ts_core *core_data; + struct platform_device *pdev; + + GET_GOODIX_DATA(dev); + + ts_info("Suspend start"); + atomic_set(&core_data->suspended, 1); + return 0; +} + + +static int goodix_ts_mmi_post_suspend(struct device *dev) +{ + ts_info("Suspend end"); + return 0; +} + +static struct ts_mmi_methods goodix_ts_mmi_methods = { + .get_vendor = goodix_ts_mmi_methods_get_vendor, + .get_productinfo = goodix_ts_mmi_methods_get_productinfo, + .get_build_id = goodix_ts_mmi_methods_get_build_id, + .get_config_id = goodix_ts_mmi_methods_get_config_id, + .get_bus_type = goodix_ts_mmi_methods_get_bus_type, + .get_irq_status = goodix_ts_mmi_methods_get_irq_status, + .get_drv_irq = goodix_ts_mmi_methods_get_drv_irq, + .get_poweron = goodix_ts_mmi_methods_get_poweron, + /* SET methods */ + .reset = goodix_ts_mmi_methods_reset, + .drv_irq = goodix_ts_mmi_methods_drv_irq, + .power = goodix_ts_mmi_methods_power, + .charger_mode = goodix_ts_mmi_charger_mode, + /* PM callback */ + .panel_state = goodix_ts_mmi_panel_state, + .pre_resume = goodix_ts_mmi_pre_resume, + .post_resume = goodix_ts_mmi_post_resume, + .pre_suspend = goodix_ts_mmi_pre_suspend, + .post_suspend = goodix_ts_mmi_post_suspend, +}; + +int goodix_ts_mmi_dev_register(struct platform_device *pdev) { + int ret; + struct goodix_ts_core *core_data; + core_data = platform_get_drvdata(pdev); + if (!core_data) { + ts_err("Failed to get driver data"); + return -ENODEV; + } + ret = ts_mmi_dev_register(core_data->ts_dev->dev, &goodix_ts_mmi_methods); + if (ret) { + dev_err(&pdev->dev, "Failed to register ts mmi\n"); + return ret; + } + + core_data->imports = &goodix_ts_mmi_methods.exports; + + return 0; +} + +void goodix_ts_mmi_dev_unregister(struct platform_device *pdev) { + struct goodix_ts_core *core_data; + core_data = platform_get_drvdata(pdev); + if (!core_data) + ts_err("Failed to get driver data"); + ts_mmi_dev_unregister(&pdev->dev); +} diff --git a/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_test.c b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_test.c new file mode 100644 index 000000000000..b41e10f45a9f --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_test.c @@ -0,0 +1,3217 @@ +/* + * gtx8_ts_test.c - TP test + * + * Copyright (C) 2015 - 2016 gtx8 Technology Incorporated + * Copyright (C) 2015 - 2016 Yulong Cai + * + * 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 gtx8'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 +#include +#include + +#include +#include +#include +#include "goodix_ts_core.h" + +#include +#include +#include +#include +#include + +#define TEST_CODE_VERSION "tp_test_v1.0.4" +/* test switch */ +#define RAWDATA_TEST_16_FRAMES /* test 16 frames of rawdata */ +#define SHORT_TEST +#define NOISE_DATA_TEST +#define SELFNOISE_DATA_TEST +#define SELF_RAWDATA_TEST +// #define SAVE_IN_CSV + +#define MAX_LINE_LEN 1024*3*5 +#define MAX_DATA_BUFFER 15000 +#define PATH_LEN 250 +#define TOTAL_FRAME_NUM 16 +#define GTX8_RETRY_NUM_3 3 +#define GTX8_CONFIG_REFRESH_DATA 0x01 +#define GTX8_TEST_FILE_NAME "gtx8_test_limits.csv" +#define GTX8_RESULT_SAVE_PATH "/etc/firmware/" +#define STRTOL_LEN 10 +#define STRTOL_HEX_LEN 16 + +#define GTX8_TEST_FILE_NAME_LEN 64 +#define MAX_STR_LEN 32 + +#define FLOAT_AMPLIFIER 1000 +#define MAX_U16_VALUE 65535 +#define RAWDATA_TEST_TIMES 10 + +#define STATISTICS_DATA_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 + +/* error code */ +#define NO_ERR 0 +#define RESULT_ERR -1 +#define RAWDATA_SIZE_LIMIT -2 + +/*param key word in .csv */ +#define CSV_TP_UNIFIED_LIMIT "unified_raw_limit" +#define CSV_TP_NOISE_LIMIT "noise_data_limit" +#define CSV_TP_SPECIAL_RAW_MIN "specail_raw_min" +#define CSV_TP_SPECIAL_RAW_MAX "specail_raw_max" +#define CSV_TP_SPECIAL_RAW_DELTA "special_raw_delta" +#define CSV_TP_SHORT_THRESHOLD "shortciurt_threshold" +#define CSV_TP_SELF_UNIFIED_LIMIT "unified_selfraw_limit" +#define CSV_TP_SPECIAL_SELFRAW_MAX "special_selfraw_max" +#define CSV_TP_SPECIAL_SELFRAW_MIN "special_selfraw_min" +#define CSV_TP_SELFNOISE_LIMIT "noise_selfdata_limit" +#define CSV_TP_TEST_CONFIG "test_config" +#define CSV_TP_NOISE_CONFIG "noise_config" + +/*GTX8 CMD*/ +#define GTX8_CMD_NORMAL 0x00 +#define GTX8_CMD_RAWDATA 0x01 +#define GTX8_CMD_SELFDATA 0x14 +/* Regiter for rawdata test*/ +#define GTP_RAWDATA_ADDR_9886 0x8FA0 +#define GTP_NOISEDATA_ADDR_9886 0x9D20 +#define GTP_BASEDATA_ADDR_9886 0xA980 +#define GTP_SELF_RAWDATA_ADDR_9886 0x4C0C +#define GTP_SELF_NOISEDATA_ADDR_9886 0x4CA4 + +#define GTP_RAWDATA_ADDR_6861 0x9078 +#define GTP_NOISEDATA_ADDR_6861 0x9B92 +#define GTP_BASEDATA_ADDR_6861 0xB0DE + +#define GTP_RAWDATA_ADDR_6862 0x9078 +#define GTP_NOISEDATA_ADDR_6862 0x9B92 +#define GTP_BASEDATA_ADDR_6862 0xB0DE + +#define GTP_IC_INFO_ADDR 0x4014 + +/* short test*/ +#define SHORT_TO_GND_RESISTER(sig) (div_s64(5266285, (sig) & (~0x8000)) - 40 * 100) /* (52662.85/code-40) * 100 */ +#define SHORT_TO_VDD_RESISTER(sig, value) (div_s64(36864 * ((value) - 9) * 100, (((sig) & (~0x8000)) * 7)) - 40 * 100) + +#define DRV_CHANNEL_FLAG 0x80 +#define SHORT_STATUS_REG 0x5095 +#define WATCH_DOG_TIMER_REG 0x20B0 + +#define TXRX_THRESHOLD_REG 0x8408 +#define GNDVDD_THRESHOLD_REG 0x840A +#define ADC_DUMP_NUM_REG 0x840C + +#define GNDAVDD_SHORT_VALUE 16 +#define ADC_DUMP_NUM 200 +#define SHORT_CAL_SIZE(a) (4 + (a) * 2 + 2) + +#define SHORT_TESTEND_REG 0x8400 +#define TEST_RESTLT_REG 0x8401 +#define TX_SHORT_NUM 0x8402 + +#define DIFF_CODE_REG 0xA97A +#define DRV_SELF_CODE_REG 0xA8E0 +#define TX_SHORT_NUM_REG 0x8802 +#define YS_IC_MASK_TYPE_REG 0x2222 + +#define MAX_DRV_NUM 40 +#define MAX_SEN_NUM 36 + +#define MAX_DRV_NUM_9886 40 +#define MAX_SEN_NUM_9886 36 + +#define MAX_DRV_NUM_6861 47 +#define MAX_SEN_NUM_6861 36 + +#define MAX_DRV_NUM_6862 47 +#define MAX_SEN_NUM_6862 29 + +#define MAX_DRV_NUM_9896 20 +#define MAX_SEN_NUM_9896 40 +/* end */ + +#define FORMAT_PATH(path,mdir,name,suffix) do{\ + struct timex txc;\ + struct rtc_time tm;\ + do_gettimeofday(&(txc.time));\ + rtc_time_to_tm(txc.time.tv_sec,&tm);\ + sprintf((char*)path,"%s%s_%04d%02d%02d%02d%02d%02d%s",mdir,name,(tm.tm_year+1900),(tm.tm_mon + 1),tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec,suffix);\ +}while(0) + +static int g_save_result = 0; + +static u8 gt9886_drv_map[] = {46, 48, 49, 47, 45, 50, 56, 52, 51, 53, 55, 54, 59, 64, 57, 60, 62, 58, 65, 63, 61, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}; +static u8 gt9886_sen_map[] = {32, 34, 35, 30, 31, 33, 27, 28, 29, 10, 25, 26, 23, 13, 24, 12, 9, 11, 8, 7, 5, 6, 4, 3, 2, 1, 0, 73, 75, 74, 39, 72, 40, 36, 37, 38}; + +static u8 gt9885_drv_map[] = {45, 47, 49, 48, 46, 50, 53, 52, 51, 54, 56, 55, 59, 58, 57, 60, 61, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}; +static u8 gt9885_sen_map[] = {32, 28, 34, 30, 26, 35, 24, 31, 33, 11, 27, 29, 25, 13, 12, 23, 8, 10, 9, 6, 7, 5, 4, 1, 0, 75, 74, 40, 73, 72, 39, 38, 37, 36,255, 255}; + +static u8 gt6862_drv_map[] = {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, 75, 0, 1, 2, 3, 4, 5, 6}; +static u8 gt6862_sen_map[] = {34, 35, 33, 31, 32, 30, 28, 27, 29, 24, 25, 26, 21, 22, 23, 18, 19, 20, 15, 17, 16, 14, 13, 12, 11, 9, 10, 8, 7}; +static u8 gt6861_drv_map[] = {255, 255, 255, 255, 255, 255, 255, 255, 255, 56, 46, 47, 48, 49, 45, 50, 52, 51, 55, 53, 54, 64, 59, 57, 60, 62, 58, 65, 63, 61, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}; +static u8 gt6861_sen_map[] = {72, 37, 38, 40, 36, 74, 39, 75, 0, 73, 2, 1, 3, 4, 5, 6, 7, 8, 10, 11, 9, 12, 13, 24, 23, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35}; +static u8 gt9896_drv_map[] = {45, 46, 47, 49, 48, 50, 52, 51, 53, 55, 56, 54, 59, 58, 57, 62, 61, 60, 64, 63}; +static u8 gt9896_sen_map[] = {34, 32, 35, 33, 30, 31, 29, 28, 27, 25, 26, 19, 24, 14, 17, 18, 15, 16, 6, 13, 3, 12, 0, 1, 5,75, 4, 2, 44, 74, 41, 42, 43, 38, 40, 36, 39, 37, 66, 65}; + + +/** + * struct ts_test_params - test parameters + * drv_num: touch panel tx(driver) number + * sen_num: touch panel tx(sensor) number + * max_limits: max limits of rawdata + * min_limits: min limits of rawdata + * deviation_limits: channel deviation limits + * short_threshold: short resistance threshold + * r_drv_drv_threshold: resistance threshold between drv and drv + * r_drv_sen_threshold: resistance threshold between drv and sen + * r_sen_sen_threshold: resistance threshold between sen and sen + * r_drv_gnd_threshold: resistance threshold between drv and gnd + * r_sen_gnd_threshold: resistance threshold between sen and gnd + * avdd_value: avdd voltage value + */ +struct ts_test_params { + u16 rawdata_addr; + u16 noisedata_addr; + u16 self_rawdata_addr; + u16 self_noisedata_addr; + + u16 basedata_addr; + u32 max_drv_num; + u32 max_sen_num; + u32 drv_num; + u32 sen_num; + u8 *drv_map; + u8 *sen_map; + + u32 max_limits[MAX_DRV_NUM * MAX_SEN_NUM]; + u32 min_limits[MAX_DRV_NUM * MAX_SEN_NUM]; + u32 deviation_limits[MAX_DRV_NUM * MAX_SEN_NUM]; + u32 self_max_limits[MAX_DRV_NUM + MAX_SEN_NUM]; + u32 self_min_limits[MAX_DRV_NUM + MAX_SEN_NUM]; + + u32 noise_threshold; + u32 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 - rawdata structure + * data: rawdata buffer + * size: rawdata size + */ +struct ts_test_rawdata { + s16 data[MAX_SEN_NUM * MAX_DRV_NUM]; + u32 size; +}; + +struct ts_test_self_rawdata { + s16 data[MAX_DRV_NUM + MAX_SEN_NUM]; + u32 size; +}; + + +/** + * struct gtx8_ts_test - main data structrue + * ts: gtx8 touch screen data + * test_config: test mode config data + * orig_config: original config data + * test_param: test parameters + * rawdata: raw data structure + * test_result: test result string + */ +struct gtx8_ts_test { + //struct gtx8_ts_data *ts; + void *ts; + struct goodix_ts_config test_config; + struct goodix_ts_config orig_config; +#if ((defined NOISE_DATA_TEST) || (defined SELFNOISE_DATA_TEST)) + struct goodix_ts_config noise_config; +#endif + struct goodix_ts_cmd rawdata_cmd; + struct goodix_ts_cmd normal_cmd; +#ifdef SELF_RAWDATA_TEST + struct goodix_ts_cmd selfdata_cmd; +#endif + struct ts_test_params test_params; + struct ts_test_rawdata rawdata; + struct ts_test_rawdata noisedata; + struct ts_test_self_rawdata self_rawdata; + struct ts_test_self_rawdata self_noisedata; + + /*[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]; +}; + +struct short_record { + u32 master; + u32 slave; + u16 short_code; + u8 group1; + u8 group2; +}; + +static int ts_test_reset(struct gtx8_ts_test *ts_test) +{ + struct goodix_ts_core *cd = ts_test->ts; + + return cd->ts_dev->hw_ops->reset(cd->ts_dev); +} + +static int ts_test_read_trans(struct gtx8_ts_test *ts_test, + u32 addr, u8 *data, u32 len) +{ + struct goodix_ts_core *cd = ts_test->ts; + + return cd->ts_dev->hw_ops->read_trans(cd->ts_dev, addr, data, len); +} + +static int ts_test_write(struct gtx8_ts_test *ts_test, + u32 addr, u8 *data, u32 len) +{ + struct goodix_ts_core *cd = ts_test->ts; + + return cd->ts_dev->hw_ops->write(cd->ts_dev, addr, data, len); +} + +static int ts_test_write_trans(struct gtx8_ts_test *ts_test, + u32 addr, u8 *data, u32 len) +{ + struct goodix_ts_core *cd = ts_test->ts; + + return cd->ts_dev->hw_ops->write_trans(cd->ts_dev, addr, data, len); +} + +static int ts_test_send_config(struct gtx8_ts_test *ts_test, + struct goodix_ts_config *config) +{ + struct goodix_ts_core *cd = ts_test->ts; + + return cd->ts_dev->hw_ops->send_config(cd->ts_dev, config); +} + +static void gtx8_data_cal(u16 *data, size_t data_size, u16 *stat_result); + +/*******************************save csv**********************************************/ + +#ifdef SAVE_IN_CSV +static size_t fwrite(const void* buf, size_t size, size_t count, struct file* fp){ + mm_segment_t fs; + loff_t pos; + int len; + fs = get_fs(); + set_fs(KERNEL_DS); + pos = fp->f_pos; + len = vfs_write(fp, buf, size*count, &pos); + fp->f_pos = pos; + set_fs(fs); + return len; +} + +/* save test config in csv file */ +static s32 gtx8_save_test_config(struct gtx8_ts_test *ts_test, struct file *fp) +{ + s32 ret = 0; + u16 i = 0; + s32 bytes = 0; + char *data; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (data == NULL) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += (int) sprintf((char*)&data[bytes], "\n"); + for (i = 0; i < ts_test->test_config.length; i++) { + bytes += (s32) sprintf((char *)&data[bytes], "0x%02x,", ts_test->test_config.data[i]); + if ((i+1)%ts_test->test_params.drv_num == 0) { + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("test config write fail.\n"); + goto save_end; + } + bytes = 0; + } + } + bytes += (int) sprintf((char*)&data[bytes], "\n"); + bytes += (int) sprintf((char*)&data[bytes], "\n"); + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("test config write fail.\n"); + goto save_end; + } + bytes = 0; + +save_end: + if (data != NULL) + kfree(data); + return ret; + +} + +/* save header info in csv file */ +static s32 gtx8_save_header(struct gtx8_ts_test *ts_test, struct file *fp) +{ + s32 ret = 0; + u16 i = 0; + s32 bytes = 0; + int result = 0; + char *data; + struct goodix_ts_core* core_data; + core_data = (struct goodix_ts_core*)ts_test->ts; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (data == NULL) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + + bytes += (s32) sprintf((char *)&data[bytes], "\n"); + bytes += (int) sprintf((char*)&data[bytes], "\n"); + bytes += (int) sprintf((char*)&data[bytes], "
\n"); + // save test result + for (i = 0; i < MAX_TEST_ITEMS; i++) { + if (ts_test->test_result[i] == SYS_SOFTWARE_REASON + ||ts_test->test_result[i] == GTP_PANEL_REASON) { + result = 1; + break; + } + } + if (result) + bytes += (int) sprintf((char*)&data[bytes], "NG\n"); + else + bytes += (int) sprintf((char*)&data[bytes], "OK\n"); + + bytes += (int) sprintf((char*)&data[bytes], "GT%s\n", + core_data->ts_dev->chip_version.pid); + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("header write fail.\n"); + goto save_end; + } + bytes = 0; + //save test config + ret = gtx8_save_test_config(ts_test, fp); + if (ret < 0) + goto save_end; + + bytes += (int) sprintf((char*)&data[bytes], "
\n"); + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("header write fail.\n"); + goto save_end; + } + bytes = 0; + + //item list + bytes += (int) sprintf((char*)&data[bytes], "\n"); + if (ts_test->test_result[GTP_CAP_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_CAP_TEST]) + bytes += (int) sprintf((char*)&data[bytes], + "\n"); + else + bytes += (int) sprintf((char*)&data[bytes], + "\n"); + } + if (ts_test->test_result[GTP_DELTA_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_DELTA_TEST]) + bytes += (int) sprintf((char*)&data[bytes], + "\n"); + else + bytes += (int) sprintf((char*)&data[bytes], + "\n"); + } +#ifdef NOISE_DATA_TEST + if (ts_test->test_result[GTP_NOISE_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_NOISE_TEST]) + bytes += (int) sprintf((char*)&data[bytes], + "\n"); + else + bytes += (int) sprintf((char*)&data[bytes], + "\n"); + } +#endif +#ifdef SELFNOISE_DATA_TEST + if (ts_test->test_result[GTP_SELFNOISE_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_SELFNOISE_TEST]) + bytes += (int) sprintf((char*)&data[bytes], + "\n"); + else + bytes += (int) sprintf((char*)&data[bytes], + "\n"); + } +#endif +#ifdef SELF_RAWDATA_TEST + if (ts_test->test_result[GTP_SELFCAP_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_SELFCAP_TEST]) + bytes += (int) sprintf((char*)&data[bytes], + "\n"); + else + bytes += (int) sprintf((char*)&data[bytes], + "\n"); + } +#endif +#ifdef SHORT_TEST + if (ts_test->test_result[GTP_SHORT_TEST]) { + if (GTP_TEST_PASS == ts_test->test_result[GTP_SHORT_TEST]) + bytes += (int) sprintf((char*)&data[bytes], + "\n"); + else + bytes += (int) sprintf((char*)&data[bytes], + "\n"); + } +#endif + bytes += (int) sprintf((char*)&data[bytes], "\n"); + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("item list write fail.\n"); + goto save_end; + } + bytes = 0; +save_end: + if (data != NULL) + kfree(data); + return ret; + +} + +/* save end tag in csv file */ +static s32 gtx8_save_tail(struct gtx8_ts_test *ts_test, struct file *fp) +{ + s32 ret = 0; + s32 bytes = 0; + char *data; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (data == NULL) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + + bytes += (int) sprintf((char*)&data[bytes], "
\n"); + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("tail write fail.\n"); + goto save_end; + } + bytes = 0; +save_end: + if (data != NULL) + kfree(data); + return ret; + +} + +/* save data limits in csv file */ +static s32 gtx8_save_limits_data(struct gtx8_ts_test *ts_test, struct file *fp, + int *src_data, int len, u8* title) +{ + s32 ret = 0; + u16 i = 0; + s32 bytes = 0; + char *data; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (data == NULL) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += (int) sprintf((char*)&data[bytes], "<%s>\n",title); + for (i = 0; i < len; i++) { + bytes += (s32) sprintf((char *)&data[bytes], "%d,", src_data[i]); + if ((i+1)%ts_test->test_params.drv_num == 0) { + bytes += (s32) sprintf((char *)&data[bytes], "\n"); + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("limit write fail.\n"); + goto save_end; + } + bytes = 0; + } + } + if (i%ts_test->test_params.drv_num != 0) + bytes += (s32) sprintf((char *)&data[bytes], "\n"); + bytes += (s32) sprintf((char *)&data[bytes], "\n",title); + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("limit write fail.\n"); + goto save_end; + } + bytes = 0; + +save_end: + if (data != NULL) + kfree(data); + return ret; + +} + + +static s32 gtx8_save_limits(struct gtx8_ts_test *ts_test, struct file *fp) +{ + s32 ret = 0; + s32 bytes = 0; + char *data; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (data == NULL) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + + bytes += (s32) sprintf((char *)&data[bytes], "\n"); + //rawdata limit + bytes += (int) sprintf((char*)&data[bytes], "\n"); + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("limit write fail.\n"); + goto save_end; + } + bytes = 0; + //save max limit + gtx8_save_limits_data(ts_test, fp, &ts_test->test_params.max_limits[0], + ts_test->rawdata.size, "MaxRawLimit"); + gtx8_save_limits_data(ts_test, fp, &ts_test->test_params.min_limits[0], + ts_test->rawdata.size, "MinRawLimit"); + gtx8_save_limits_data(ts_test, fp, &ts_test->test_params.deviation_limits[0], + ts_test->rawdata.size, "MaxAccordLimit"); + + bytes += (s32) sprintf((char *)&data[bytes], "\n"); + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("Item write fail.\n"); + goto save_end; + } + bytes = 0; + +#ifdef NOISE_DATA_TEST + //save noise limit + bytes += (int) sprintf((char*)&data[bytes], "\n"); + bytes += (int) sprintf((char*)&data[bytes], "%d\n", + ts_test->test_params.noise_threshold); + bytes += (s32) sprintf((char *)&data[bytes], "\n"); + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("Noise limit write fail.\n"); + goto save_end; + } + bytes = 0; +#endif +#ifdef SELF_RAWDATA_TEST + //self rawdata limit + bytes += (int) sprintf((char*)&data[bytes], "\n"); + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("self raw limit write fail.\n"); + goto save_end; + } + bytes = 0; + gtx8_save_limits_data(ts_test, fp, &ts_test->test_params.self_max_limits[0], + ts_test->self_rawdata.size, "MaxRawLimit"); + gtx8_save_limits_data(ts_test, fp, &ts_test->test_params.self_min_limits[0], + ts_test->self_rawdata.size, "MinRawLimit"); + bytes += (s32) sprintf((char *)&data[bytes], "\n"); + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("self Item write fail.\n"); + goto save_end; + } + bytes = 0; +#endif +#ifdef SELFNOISE_DATA_TEST + //self diffdata limit + bytes += (int) sprintf((char*)&data[bytes], "\n"); + bytes += (int) sprintf((char*)&data[bytes], "%d\n", + ts_test->test_params.self_noise_threshold); + bytes += (s32) sprintf((char *)&data[bytes], "\n"); + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("Noise limit write fail.\n"); + goto save_end; + } + bytes = 0; +#endif + bytes += (s32) sprintf((char *)&data[bytes], "\n"); + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("limit write fail.\n"); + goto save_end; + } + bytes = 0; +save_end: + if (data != NULL) + kfree(data); + return ret; + +} + +/* save data in csv file */ +static s32 gtx8_save_sampdata(struct gtx8_ts_test *ts_test, struct file *fp, u16* src_data, + u8 *title, int len) +{ + s32 ret = 0; + u16 i = 0; + s32 bytes = 0; + u16 stat_result[3]; + char *data; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (data == NULL) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += (s32) sprintf((char *)&data[bytes], "<%s>\n",title); + gtx8_data_cal(src_data, len, stat_result); + bytes += (s32) sprintf((char *)&data[bytes], + "\n", + len,stat_result[1],stat_result[2], stat_result[0]); + + for (i = 0; i < len; i++) { + bytes += (s32) sprintf((char *)&data[bytes], "%d,", src_data[i]); + if ((i+1)%ts_test->test_params.drv_num == 0) { + bytes += (s32) sprintf((char *)&data[bytes], "\n"); + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("rawdata write fail.\n"); + goto save_end; + } + bytes = 0; + } + } + if (i%ts_test->test_params.drv_num != 0) + bytes += (s32) sprintf((char *)&data[bytes], "\n"); + + bytes += (s32) sprintf((char *)&data[bytes], "\n"); + bytes += (s32) sprintf((char *)&data[bytes], "\n",title); + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("rawdata write fail.\n"); + goto save_end; + } + bytes = 0; + +save_end: + kfree(data); + return ret; +} + +/* save rawdiff data in csv file */ +static int gtx8_save_rawdiff(struct gtx8_ts_test *ts_test, struct file *fp) +{ + int ret = 0; + s32 bytes = 0; + char *data; + + data = kzalloc(MAX_DATA_BUFFER, GFP_KERNEL); + if (data == NULL) { + ts_err("alloc memory failed for "); + return -ENOMEM; + } + + bytes += (s32) sprintf((char *)&data[bytes], "\n"); + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("rawdata data record lable fail.\n"); + goto save_end; + } + bytes = 0; + + ret = gtx8_save_sampdata(ts_test, fp, &ts_test->rawdata.data[0], + "RawDataRecord", ts_test->rawdata.size); + if (ret < 0) + goto save_end; +#ifdef NOISE_DATA_TEST + ret = gtx8_save_sampdata(ts_test, fp, &ts_test->noisedata.data[0], + "DiffDataRecord", ts_test->noisedata.size); + if (ret < 0) + goto save_end; +#endif +#ifdef SELF_RAWDATA_TEST + ret = gtx8_save_sampdata(ts_test, fp, &ts_test->self_rawdata.data[0], + "selfDataRecord", ts_test->self_rawdata.size); + if (ret < 0) + goto save_end; +#endif +#ifdef SELFNOISE_DATA_TEST + ret = gtx8_save_sampdata(ts_test, fp, &ts_test->self_noisedata.data[0], + "selfDiffDataRecord", ts_test->self_noisedata.size); + if (ret < 0) + goto save_end; +#endif + bytes += (s32) sprintf((char *)&data[bytes], "\n"); + ret = fwrite(data, bytes, 1, fp); + if (ret < 0) { + ts_err("rawdata data record lable fail.\n"); + goto save_end; + } + bytes = 0; +save_end: + kfree(data); + return ret; +} + +/* save result and rawdiff data in csv file */ +static int gtx8_save_result_data(struct gtx8_ts_test *ts_test) +{ + int ret = 0; + char save_path[PATH_LEN]; + struct file *fp = NULL; + + //format result file + FORMAT_PATH(save_path, GTX8_RESULT_SAVE_PATH,"Test_Data",".csv"); + + fp = filp_open(save_path, O_CREAT|O_TRUNC|O_WRONLY ,0666); + if (IS_ERR(fp)) { + ts_err("open file:%s fail,fp:%ld", save_path,PTR_ERR(fp)); + return -1; + } + //save header + ret = gtx8_save_header(ts_test,fp); + if (ret < 0) + goto save_end; + + //save limits + ret =gtx8_save_limits(ts_test, fp); + if (ret < 0) + goto save_end; + + //save data + ret = gtx8_save_rawdiff(ts_test, fp); + if (ret < 0) + goto save_end; + //save tail + ret = gtx8_save_tail(ts_test, fp); + if (ret < 0) + goto save_end; + + ts_info("the test result saves in %s\n", save_path); +save_end: + filp_close(fp, NULL); + return ret; +} +#endif + +/*******************************save result to csv end**********************************************/ + +/*******************************csv_parse**********************************************/ +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 void goto_next_line(char **ptr) +{ + do { + *ptr = *ptr + 1; + } while (**ptr != '\n' && **ptr != '\0'); + if (**ptr == '\0') { + return; + } + *ptr = *ptr + 1; +} + +s32 getrid_space(s8* data, s32 len) +{ + u8* buf = NULL; + s32 i; + u32 count = 0; + + buf = (char*)kzalloc(len+5, GFP_KERNEL); + if (buf == NULL){ + 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); + buf = NULL; + + return count; +} + +static int parse_valid_data(char *buf_start, loff_t buf_size, + char *ptr, int32_t* data, int rows) +{ + int i = 0; + int j = 0; + char *token = NULL; + char *tok_ptr = NULL; + char *row_data = NULL; + + if(!ptr) { + ts_err("%s, ptr is NULL\n", __func__); + return -EPERM; + } + if (!data) { + ts_err("%s, data is NULL\n", __func__); + return -EPERM; + } + + row_data = (char *)kzalloc(MAX_LINE_LEN, GFP_KERNEL); + if(NULL == row_data) { + ts_err("%s: kzalloc %d bytes failed.\n", __func__, MAX_LINE_LEN); + return -ESRCH; + } + + for (i = 0; i < rows; i++) { + // copy this line to row_data buffer + 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 ( token[0] == '0' && (token[1] == 'X' || token[1] == 'x')){ + data[j] = (int32_t)simple_strtoul(token, NULL, STRTOL_HEX_LEN); + j++; + }else{ + data[j] = (int32_t)simple_strtoul(token, NULL, STRTOL_LEN); + j++; + } + } + if (i == rows -1) + break; + goto_next_line(&ptr); //next row + if(!ptr || (0 == strlen(ptr)) || (ptr >= (buf_start + buf_size))) { + ts_info("invalid ptr, return\n"); + kfree(row_data); + row_data = NULL; + return -EPERM; + } + } + kfree(row_data); + row_data = NULL; + return j; +} + +/*parse data from csv + * @return: length of data + */ +int parse_csvfile(struct device *dev, char *target_name, int32_t *data, int rows, int columns) +{ + int ret = 0; + int i = 0; + int32_t read_ret = 0; + char *buf = NULL; + char *ptr = NULL; + const struct firmware *firmware = NULL; + + if(NULL == target_name) { + ts_err("target path pointer is NULL\n"); + ret = -EPERM; + goto exit_free; + } + + ts_info("%s, file name is %s, target is %s.\n", __func__,GTX8_TEST_FILE_NAME,target_name); + for (i = 0; i < 3; i++) { + ret = request_firmware(&firmware, GTX8_TEST_FILE_NAME, dev); + if (ret < 0) { + ts_err("limits file [%s] not available,error:%d, try_times:%d", GTX8_TEST_FILE_NAME, ret, i + 1); + msleep(1000); + } else { + ts_info("limits file [%s] is ready, try_times:%d", GTX8_TEST_FILE_NAME, i + 1); + break; + } + } + if (i >= 3) { + ts_err("get limits file FAILED"); + goto exit_free; + } + + if (firmware->size <= 0) { + ts_err("request_firmware, limits param length ERROR,len:%zu", firmware->size); + ret = -EINVAL; + goto exit_free; + } + + buf = (char *)kzalloc(firmware->size + 1, GFP_KERNEL); + if(NULL == buf) { + ts_err("%s: kzalloc %zu bytes failed.\n", __func__, firmware->size); + ret = -ESRCH; + goto exit_free; + } + memcpy(buf,firmware->data,firmware->size); + read_ret = firmware->size; + if (read_ret > 0) { + buf[firmware->size] = '\0'; + ptr = buf; + ptr = strstr(ptr, target_name); + if (ptr == NULL) { + ts_err("%s: load %s failed 1!\n", __func__,target_name); + ret = -EINTR; + goto exit_free; + } + + // walk thru this line + goto_next_line(&ptr); + if ((NULL == ptr) || (0 == strlen(ptr))) { + ts_err("%s: load %s failed 2!\n", __func__,target_name); + ret = -EIO; + goto exit_free; + } + + //analyze the data + if (data) { + ret = parse_valid_data(buf, firmware->size, ptr, data, rows); + }else{ + ts_err("%s: load %s failed 3!\n", __func__,target_name); + ret = -EINTR; + goto exit_free; + } + } + else { + ts_err("%s: ret=%d,read_ret=%d, buf=%p, firmware->size=%zu\n", __func__, ret, read_ret, buf, firmware->size); + ret = -ENXIO; + goto exit_free; + } +exit_free: + ts_info("%s exit free\n", __func__); + if(buf) { + ts_info("kfree buf\n"); + kfree(buf); + buf = NULL; + } + + if (firmware) { + release_firmware(firmware); + firmware = NULL; + } + return ret; +} +/*******************************csv_parse end**********************************************/ + +/******************************************************************************* +* Function Name : cfg_update_chksum +* Description : update check sum +* Input : u32* config +* Input : u16 cfg_len +* Output : u8* config +* Return : none +*******************************************************************************/ +static void cfg_update_chksum(struct goodix_ts_core *core_data, u8* config, u16 cfg_len) +{ + u16 pack_map_len_arr[255]; + u16 packNum = 0; + u16 pack_len_tmp = 0; + u16 pack_id_tmp = 0; + u16 i = 0,j = 0; + u16 cur_pos = 0; + u8 check_sum = 0; + u16 check_sum_u16 = 0; + u8 chksum_len = 1; + u8 head_len; + + if (core_data->ts_dev->ic_type == IC_TYPE_YELLOWSTONE) + chksum_len = 2; + head_len = 3 + chksum_len; + + if (cfg_len < head_len) return; + /* Head:4 bytes |byte0:version|byte1:config refresh|byte2:package total num|byte3:check sum|*/ + pack_map_len_arr[pack_id_tmp] = head_len; + packNum = config[2]; + for (i = head_len; i < cfg_len;){ + pack_id_tmp++; + pack_len_tmp = config[i + 1] + 2 + chksum_len; + pack_map_len_arr[pack_id_tmp] = pack_len_tmp; + i += pack_len_tmp; + } + + cur_pos = 0; + for (i = 0; i <= pack_id_tmp; i++) + { + check_sum = 0; + check_sum_u16 = 0; + for (j = cur_pos; j < cur_pos + pack_map_len_arr[i] - chksum_len; j++) + { + check_sum += config[j]; + check_sum_u16 += config[j]; + } + if (chksum_len == 1) { + config[cur_pos + pack_map_len_arr[i] - chksum_len] = (u8)(0 - check_sum); + } else if (chksum_len == 2) { + config[cur_pos + pack_map_len_arr[i] - chksum_len] = (check_sum_u16 >> 8) & 0xff; + config[cur_pos + pack_map_len_arr[i] - chksum_len + 1] = check_sum_u16 & 0xff; + + } + cur_pos += pack_map_len_arr[i]; + } +} + +static int disable_hopping(struct gtx8_ts_test *ts_test, struct goodix_ts_config *test_config) +{ + int ret = 0; + u8 value = 0; + u16 offset = 0; + struct goodix_ts_core *core_data; + + core_data = (struct goodix_ts_core*)ts_test->ts; + if (core_data->ts_dev->ic_type == IC_TYPE_YELLOWSTONE) + ret = goodix_get_cfg_value(core_data, test_config->data, &value, 1, 11, 2); /* T11 2 0 0*/ + else + ret = goodix_get_cfg_value(core_data, test_config->data, &value, 1, 10, 2); /* T10 2 0 0*/ + if (ret < 0 ){ + ts_err("Failed parse hopping reg"); + return -EINVAL; + } + offset = ret; + /* disable hopping */ + value = test_config->data[offset]; + value &= 0xfe; + test_config->data[offset] = value; + ts_info("disable_hopping:0x%02x_%d",test_config->data[offset], offset); + cfg_update_chksum(core_data, test_config->data, test_config->length); + return ret; +} + +static int init_test_config(struct gtx8_ts_test *ts_test) { + int ret = 0; + struct goodix_ts_core *core_data; + core_data = (struct goodix_ts_core*)ts_test->ts; + if (ts_test->test_config.length == 0){ + ts_info("switch to orignal config!"); + memmove(ts_test->test_config.data,ts_test->orig_config.data, + ts_test->orig_config.length); + ts_test->test_config.length = ts_test->orig_config.length; + }else{ + ts_test->test_config.data[0] = ts_test->orig_config.data[0]; + ts_test->test_config.data[1] |= 0x01; + ts_info("switch to test config!"); + } + ts_test->test_config.reg_base = core_data->ts_dev->reg.cfg_addr; + mutex_init(&(ts_test->test_config.lock)); + ts_test->test_config.initialized = true; + strcpy(ts_test->test_config.name, "test_config"); + disable_hopping(ts_test, &ts_test->test_config); + return ret; +} + +static int gtx8_init_testlimits(struct gtx8_ts_test *ts_test) +{ + int ret = 0, i = 0; + u32 data_buf[MAX_DRV_NUM] = {0}; + int *tmp_config = NULL; + struct ts_test_params *test_params = NULL; + struct goodix_ts_config *test_config = NULL; + struct goodix_ts_core *core_data; + + test_params = &ts_test->test_params; + test_config = &ts_test->test_config; + core_data = (struct goodix_ts_core*)ts_test->ts; + tmp_config = (int*)kzalloc(GOODIX_CFG_MAX_SIZE*sizeof(int), GFP_KERNEL); + if(NULL == tmp_config) { + ts_err("%s: config kzalloc bytes failed.\n", __func__); + return -ENOMEM; + } + + /* */ + ret = parse_csvfile(core_data->ts_dev->dev, CSV_TP_UNIFIED_LIMIT, data_buf, 1, 3); + if (ret < 0) { + ts_info("%s: Failed get %s\n", __func__, CSV_TP_UNIFIED_LIMIT); + goto INIT_LIMIT_END; + } + /* store data to test_parms */ + for (i = 0; i < MAX_DRV_NUM * MAX_SEN_NUM; i++) { + test_params->max_limits[i] = data_buf[0]; + test_params->min_limits[i] = data_buf[1]; + test_params->deviation_limits[i] = data_buf[2]; + } +#ifdef NOISE_DATA_TEST + ret = parse_csvfile(core_data->ts_dev->dev, CSV_TP_NOISE_LIMIT, data_buf, 1, 1); + if (ret < 0) { + ts_info("%s: Failed get %s\n", __func__, CSV_TP_NOISE_LIMIT); + goto INIT_LIMIT_END; + } + test_params->noise_threshold = data_buf[0]; +#endif +#ifdef SELFNOISE_DATA_TEST + ret = parse_csvfile(core_data->ts_dev->dev, CSV_TP_SELFNOISE_LIMIT, data_buf, 1, 1); + if (ret < 0) { + ts_info("%s: Failed get %s\n", __func__, CSV_TP_SELFNOISE_LIMIT); + goto INIT_LIMIT_END; + } + test_params->self_noise_threshold = data_buf[0]; +#endif + +#ifdef SELF_RAWDATA_TEST + /* */ + ret = parse_csvfile(core_data->ts_dev->dev, CSV_TP_SELF_UNIFIED_LIMIT, data_buf, 1, 2); + if (ret < 0) { + ts_info("%s: Failed get %s\n", __func__, CSV_TP_SELF_UNIFIED_LIMIT); + goto INIT_LIMIT_END; + } + /* store data to test_parms */ + for (i = 0; i < MAX_DRV_NUM + MAX_SEN_NUM; i++) { + test_params->self_max_limits[i] = data_buf[0]; + test_params->self_min_limits[i] = data_buf[1]; + } + + ret = parse_csvfile(core_data->ts_dev->dev, CSV_TP_SPECIAL_SELFRAW_MAX, test_params->self_max_limits, + 1, test_params->drv_num + test_params->sen_num); + if (ret < 0) { + ts_info("%s: Failed get %s\n", __func__, CSV_TP_SPECIAL_SELFRAW_MAX); + ret = 0; + } + + ret = parse_csvfile(core_data->ts_dev->dev, CSV_TP_SPECIAL_SELFRAW_MIN, test_params->self_min_limits, + 1, test_params->drv_num + test_params->sen_num); + if (ret < 0) { + ts_info("%s: Failed get %s\n", __func__, CSV_TP_SPECIAL_SELFRAW_MIN); + ret = 0; + } +#endif +#ifdef SHORT_TEST + /* shortciurt_threshold */ + ret = parse_csvfile(core_data->ts_dev->dev, CSV_TP_SHORT_THRESHOLD, data_buf, 1, 7); + if (ret < 0) { + ts_info("%s: Failed get %s\n", __func__, CSV_TP_SHORT_THRESHOLD); + goto INIT_LIMIT_END; + } + 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]; +#endif + + ret = parse_csvfile(core_data->ts_dev->dev, CSV_TP_SPECIAL_RAW_MAX, test_params->max_limits, + test_params->sen_num, test_params->drv_num); + if (ret < 0) { + ts_info("%s: Failed get %s\n", __func__, CSV_TP_SPECIAL_RAW_MAX); + ret = 0; /* if does not specialed the node value, we will use unified limits setting */ + } + + ret = parse_csvfile(core_data->ts_dev->dev, CSV_TP_SPECIAL_RAW_MIN, test_params->min_limits, + test_params->sen_num, test_params->drv_num); + if (ret < 0) { + ts_info("%s: Failed get %s\n", __func__, CSV_TP_SPECIAL_RAW_MIN); + ret = 0; + } + + ret = parse_csvfile(core_data->ts_dev->dev, CSV_TP_SPECIAL_RAW_DELTA, test_params->deviation_limits, + test_params->sen_num, test_params->drv_num); + if (ret < 0) { + ts_info("%s: Failed get %s\n", __func__, CSV_TP_SPECIAL_RAW_DELTA); + ret = 0; + } + + ret = parse_csvfile(core_data->ts_dev->dev, CSV_TP_TEST_CONFIG, tmp_config, + 1, GOODIX_CFG_MAX_SIZE); + if (ret < 0) { + ts_info("%s: Failed get %s\n", __func__, CSV_TP_TEST_CONFIG); + test_config->length = 0; + ret = 0; + } else { + for (i = 0; i < ret; i++) + test_config->data[i] = tmp_config[i]; + test_config->length = ret; + } + init_test_config(ts_test); + + ret = 0; +INIT_LIMIT_END: + kfree(tmp_config); + tmp_config = NULL; + return ret; +} + +static int get_ic_info(struct goodix_ts_device *dev, + struct ts_test_params *test_params) +{ + int ret = 0; + u8 buf[10]; + int ic_info_len; + u8 *info_data = NULL; + int offset; + u16 info_addr = GTP_IC_INFO_ADDR; + + ret = dev->hw_ops->read_trans(dev, info_addr, buf, 2); + if (ret < 0) { + ts_err("read ic info len failed,ret:%d\n",ret); + return ret; + } + ic_info_len = (buf[0]<<8) + buf[1]; + + info_data = kzalloc(ic_info_len, GFP_KERNEL); + if (info_data == NULL){ + ts_err("%s: Failed to alloc mem for info_data\n", __func__); + return -ENOMEM; + } + ts_info("ic info len:%d\n",ic_info_len); + + ret = dev->hw_ops->read_trans(dev, info_addr, info_data, ic_info_len); + if (ret < 0){ + ts_err("%s:read ic info failed,ret:%d\n", __func__, ret); + goto err_out; + } + + offset = 36; /* drv num */ + test_params->drv_num = info_data[offset]; + offset++; + test_params->sen_num = info_data[offset]; + + offset = 83; + test_params->rawdata_addr = (info_data[offset]<<8) + info_data[offset+1]; + offset += 2; + test_params->noisedata_addr = (info_data[offset]<<8) + info_data[offset+1]; + offset += 2; + test_params->basedata_addr = (info_data[offset]<<8) + info_data[offset+1]; + offset += 4; /* self data addr */ + test_params->self_rawdata_addr = (info_data[offset]<<8) + info_data[offset+1]; + offset += 2; + test_params->self_noisedata_addr = (info_data[offset]<<8) + info_data[offset+1]; + + if (!strncmp(dev->chip_version.pid, "9896", 4)){ + test_params->max_drv_num = MAX_DRV_NUM_9896; + test_params->max_sen_num = MAX_SEN_NUM_9896; + test_params->drv_map = gt9896_drv_map; + test_params->sen_map = gt9896_sen_map; + }else{ + ts_err("unsupport ic type:%s\n", dev->chip_version.pid); + test_params->max_drv_num = MAX_DRV_NUM_9896; + test_params->max_sen_num = MAX_SEN_NUM_9896; + test_params->drv_map = gt9896_drv_map; + test_params->sen_map = gt9896_sen_map; + ret = NO_ERR; + } + +err_out: + if (info_data) + kfree(info_data); + return ret; +} + +static int gtx8_init_params(struct gtx8_ts_test *ts_test) +{ + int ret = 0; + struct goodix_ts_core *core_data; + struct ts_test_params *test_params = &ts_test->test_params; + core_data = (struct goodix_ts_core*)ts_test->ts; + ts_info("product id:%s\n", core_data->ts_dev->chip_version.pid); + + if (!strncmp(core_data->ts_dev->chip_version.pid, "9886", 4)){ + test_params->rawdata_addr = GTP_RAWDATA_ADDR_9886; + test_params->noisedata_addr = GTP_NOISEDATA_ADDR_9886; + test_params->self_rawdata_addr = GTP_SELF_RAWDATA_ADDR_9886; + test_params->self_noisedata_addr = GTP_SELF_NOISEDATA_ADDR_9886; + test_params->basedata_addr = GTP_BASEDATA_ADDR_9886; + test_params->max_drv_num = MAX_DRV_NUM_9886; + test_params->max_sen_num = MAX_SEN_NUM_9886; + test_params->drv_map = gt9886_drv_map; + test_params->sen_map = gt9886_sen_map; + }else if (!strncmp(core_data->ts_dev->chip_version.pid, "9885", 4)){ + test_params->rawdata_addr = GTP_RAWDATA_ADDR_9886; + test_params->noisedata_addr = GTP_NOISEDATA_ADDR_9886; + test_params->self_rawdata_addr = GTP_SELF_RAWDATA_ADDR_9886; + test_params->self_noisedata_addr = GTP_SELF_NOISEDATA_ADDR_9886; + test_params->basedata_addr = GTP_BASEDATA_ADDR_9886; + test_params->max_drv_num = MAX_DRV_NUM_9886; + test_params->max_sen_num = MAX_SEN_NUM_9886; + test_params->drv_map = gt9885_drv_map; + test_params->sen_map = gt9885_sen_map; + }else if (!strncmp(core_data->ts_dev->chip_version.pid, "6861", 4)){ + test_params->rawdata_addr = GTP_RAWDATA_ADDR_6861; + test_params->noisedata_addr = GTP_NOISEDATA_ADDR_6861; + test_params->basedata_addr = GTP_BASEDATA_ADDR_6861; + test_params->max_drv_num = MAX_DRV_NUM_6861; + test_params->max_sen_num = MAX_SEN_NUM_6861; + test_params->drv_map = gt6861_drv_map; + test_params->sen_map = gt6861_sen_map; + }else if (!strncmp(core_data->ts_dev->chip_version.pid, "6862", 4)){ + test_params->rawdata_addr = GTP_RAWDATA_ADDR_6862; + test_params->noisedata_addr = GTP_NOISEDATA_ADDR_6862; + test_params->basedata_addr = GTP_BASEDATA_ADDR_6862; + test_params->max_drv_num = MAX_DRV_NUM_6862; + test_params->max_sen_num = MAX_SEN_NUM_6862; + test_params->drv_map = gt6862_drv_map; + test_params->sen_map = gt6862_sen_map; + }else if (core_data->ts_dev->ic_type == IC_TYPE_YELLOWSTONE) { + ret = get_ic_info(core_data->ts_dev, test_params); + }else{ + ts_err("unsupport ic type:%s\n", core_data->ts_dev->chip_version.pid); + test_params->rawdata_addr = GTP_RAWDATA_ADDR_9886; + test_params->noisedata_addr = GTP_NOISEDATA_ADDR_9886; + test_params->self_rawdata_addr = GTP_SELF_RAWDATA_ADDR_9886; + test_params->self_noisedata_addr = GTP_SELF_NOISEDATA_ADDR_9886; + test_params->basedata_addr = GTP_BASEDATA_ADDR_9886; + test_params->max_drv_num = MAX_DRV_NUM_9886; + test_params->max_sen_num = MAX_SEN_NUM_9886; + test_params->drv_map = gt9886_drv_map; + test_params->sen_map = gt9886_sen_map; + ret = NO_ERR; + //ret = -1; + } + return ret; +} + +static void goodix_cmd_init(struct goodix_ts_device *dev, + struct goodix_ts_cmd *ts_cmd, + u8 cmds, u16 cmd_data, u32 reg_addr) +{ + u16 checksum = 0; + ts_cmd->initialized = false; + if (!reg_addr || !ts_cmd) + return; + + if (dev->ic_type == IC_TYPE_YELLOWSTONE) { + ts_cmd->cmd_reg = reg_addr; + ts_cmd->length = 5; + ts_cmd->cmds[0] = cmds; + ts_cmd->cmds[1] = (cmd_data >> 8) & 0xFF; + ts_cmd->cmds[2] = cmd_data & 0xFF; + checksum = ts_cmd->cmds[0] + ts_cmd->cmds[1] + + ts_cmd->cmds[2]; + ts_cmd->cmds[3] = (checksum >> 8) & 0xFF; + ts_cmd->cmds[4] = checksum & 0xFF; + ts_cmd->initialized = true; + } else if (dev->ic_type == IC_TYPE_NORMANDY) { + ts_cmd->cmd_reg = reg_addr; + ts_cmd->length = 3; + ts_cmd->cmds[0] = cmds; + ts_cmd->cmds[1] = cmd_data & 0xFF; + ts_cmd->cmds[2] = 0 - cmds - cmd_data; + ts_cmd->initialized = true; + } else { + ts_err("unsupported ic type"); + } +} + +/* gtx8_tptest_prepare + * + * preparation before tp test + */ +static int gtx8_tptest_prepare(struct gtx8_ts_test *ts_test) +{ + int ret = 0; + u8 cmds[3]; + struct goodix_ts_core *core_data; + + core_data = (struct goodix_ts_core*)ts_test->ts; + + ts_info("TP test preparation"); + + /* init reg addr and short cal map */ + ret = gtx8_init_params(ts_test); + if (ret) { + ts_err("Failed init register address"); + return ret; + } + + if (core_data->ts_dev->ic_type != IC_TYPE_YELLOWSTONE) { + /* get sensor and driver num currently in use */ + ts_test->test_params.sen_num = core_data->sen_num; + ts_test->test_params.drv_num = core_data->drv_num; + } + /* parse test limits from csv */ + ret = gtx8_init_testlimits(ts_test); + if (ret) { + ts_err("Failed to init testlimits from csv:%d", ret); + return ret; + } + + if (ts_test->test_config.initialized) { + ret = ts_test_send_config(ts_test, &ts_test->test_config); + if (ret) { + ts_err("Failed to send noise test config config:%d", ret); + return ret; + } + ts_info("send test config success"); + } + + /* disable irq & close esd */ + goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL); + goodix_ts_irq_enable(core_data, false); + usleep_range(10000, 11000); + + /* switch rawdata mode */ + cmds[0] = 0x01; + cmds[1] = 0x00; + cmds[2] = 0 - cmds[0] - cmds[1]; + ret = ts_test_write(ts_test, + core_data->ts_dev->reg.command, cmds, sizeof(cmds)); + if (ret < 0) { + ts_err("switch rawdata mode failed"); + goto err; + } + msleep(30); + return 0; + +err: + goodix_ts_irq_enable(core_data, true); + goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); + return ret; +} + +/* gtx8_tptest_finish + * + * finish test + */ +static int gtx8_tptest_finish(struct gtx8_ts_test *ts_test) +{ + int ret = RESULT_ERR; + struct goodix_ts_core *core_data; + core_data = (struct goodix_ts_core*)ts_test->ts; + + ts_info("TP test finish"); + ret = ts_test_reset(ts_test); + if (ret) + ts_err("chip reset failed"); + + /* send normal config */ + ret = ts_test_send_config(ts_test, &core_data->ts_dev->normal_cfg); + if (ret) + ts_err("send normal config failed"); + + goodix_ts_irq_enable(core_data, true); + goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL); + + return ret; +} + +/** + * gtx8_cache_rawdata - cache rawdata + */ +static int gtx8_cache_rawdata(struct gtx8_ts_test *ts_test) +{ + int i = 0; + int ret = -EINVAL; + int retry = 20; + u8 irq_flag; + u32 rawdata_size = 0; + u16 rawdata_addr = 0; + struct goodix_ts_core *core_data; + core_data = (struct goodix_ts_core*)ts_test->ts; + ts_debug("Cache rawdata"); + ts_test->rawdata.size = 0; + rawdata_size = ts_test->test_params.sen_num *ts_test->test_params.drv_num; + + if (rawdata_size > MAX_DRV_NUM * MAX_SEN_NUM || rawdata_size <= 0) { + ts_err("Invalid rawdata size(%u)", rawdata_size); + return ret; + } + + rawdata_addr = ts_test->test_params.rawdata_addr; + + irq_flag = 0; + ts_test_write_trans(ts_test, core_data->ts_dev->reg.coor, &irq_flag, 1); + while (retry--) { + usleep_range(5000, 5100); + ts_test_read_trans(ts_test, core_data->ts_dev->reg.coor, &irq_flag, 1); + if (irq_flag & 0x80) + break; + } + if (retry < 0) { + ts_err("rawdata is not ready irq_flag:0x%02x, exit", irq_flag); + return -EAGAIN; + } + + ret = ts_test_read_trans(ts_test, rawdata_addr, + (u8 *)&ts_test->rawdata.data[0], rawdata_size * sizeof(u16)); + if (ret < 0) { + ts_err("Failed to read rawdata:%d", ret); + return ret; + } + for (i = 0; i < rawdata_size; i++) + ts_test->rawdata.data[i] = be16_to_cpu(ts_test->rawdata.data[i]); + goodix_rotate_abcd2cbad(ts_test->test_params.drv_num, + ts_test->test_params.sen_num, ts_test->rawdata.data); + ts_test->rawdata.size = rawdata_size; + + return ret; +} + +#ifdef SELF_RAWDATA_TEST +/** + * gtx8_cache_selfrawdata - cache selfrawdata + */ +static int gtx8_cache_self_rawdata(struct gtx8_ts_test *ts_test) +{ + int i = 0; + int ret = -EINVAL; + int retry = 20; + u8 irq_flag; + u16 self_rawdata_size =0; + u16 self_rawdata_addr = 0; + struct goodix_ts_core *core_data; + core_data = (struct goodix_ts_core*)ts_test->ts; + ts_debug("Cache selfrawdata"); + ts_test->self_rawdata.size = 0; + self_rawdata_size = ts_test->test_params.sen_num +ts_test->test_params.drv_num; + + if (self_rawdata_size > MAX_DRV_NUM + MAX_SEN_NUM || self_rawdata_size <= 0) { + ts_err("Invalid selfrawdata size(%u)", self_rawdata_size); + return ret; + } + + self_rawdata_addr = ts_test->test_params.self_rawdata_addr; + ts_info("Selfraw address=0x%x", self_rawdata_addr); + + irq_flag = 0; + ts_test_write_trans(ts_test, core_data->ts_dev->reg.coor, &irq_flag, 1); + while (retry--) { + usleep_range(5000, 5100); + ts_test_read_trans(ts_test, core_data->ts_dev->reg.coor, &irq_flag, 1); + if (irq_flag & 0x80) + break; + } + if (retry < 0) { + ts_err("self rawdata is not ready irq_flag:0x%02x, exit", irq_flag); + return -EAGAIN; + } + ret = ts_test_read_trans(ts_test, self_rawdata_addr, + (u8 *)&ts_test->self_rawdata.data[0], self_rawdata_size * sizeof(u16)); + if (ret < 0) { + ts_err("Failed to read self_rawdata:%d", ret); + return ret; + } + + for (i = 0; i < self_rawdata_size; i++) + ts_test->self_rawdata.data[i] = be16_to_cpu(ts_test->self_rawdata.data[i]); + ts_test->self_rawdata.size = self_rawdata_size; + + return ret; +} +#endif + +#if ((defined NOISE_DATA_TEST) || (defined SELFNOISE_DATA_TEST)) +/** + * gtx8_noisetest_prepare- noisetest prepare + */ +static int gtx8_noisetest_prepare(struct gtx8_ts_test *ts_test) +{ + int ret = 0; + struct goodix_ts_core *core_data; + u32 noise_data_size = 0; + u32 self_noise_data_size = 0; + + core_data = (struct goodix_ts_core*)ts_test->ts; + noise_data_size = ts_test->test_params.sen_num * ts_test->test_params.drv_num; + self_noise_data_size = ts_test->test_params.sen_num + ts_test->test_params.drv_num; +#ifdef NOISE_DATA_TEST + if (noise_data_size <= 0 || noise_data_size > MAX_DRV_NUM * MAX_SEN_NUM) { + ts_err("Bad noise_data_size[%d]", noise_data_size); + ts_test->test_result[GTP_NOISE_TEST] = SYS_SOFTWARE_REASON; + return -EINVAL; + } + ts_test->noisedata.size = noise_data_size; +#endif + +#ifdef SELFNOISE_DATA_TEST + if (self_noise_data_size <= 0 || self_noise_data_size > MAX_DRV_NUM + MAX_SEN_NUM) { + ts_err("Bad self_noise_data_size[%d]", self_noise_data_size); + ts_test->test_result[GTP_SELFNOISE_TEST] = SYS_SOFTWARE_REASON; + return -EINVAL; + } + ts_test->self_noisedata.size = self_noise_data_size; +#endif + + return ret; +} + +/** + * gtx8_cache_noisedata- cache noisedata + */ +static int gtx8_cache_noisedata(struct gtx8_ts_test *ts_test) +{ + int ret = 0; + int retry = 20; + u8 irq_flag; + int i; + struct goodix_ts_core *core_data; + u16 noisedata_addr = 0; + u16 self_noisedata_addr = 0; + u32 noise_data_size = 0; + u32 self_noise_data_size = 0; + + core_data = (struct goodix_ts_core*)ts_test->ts; + noisedata_addr = ts_test->test_params.noisedata_addr; + self_noisedata_addr = ts_test->test_params.self_noisedata_addr; + noise_data_size = ts_test->noisedata.size; + self_noise_data_size = ts_test->self_noisedata.size; + + /* clear irq event */ + irq_flag = 0; + ts_test_write_trans(ts_test, core_data->ts_dev->reg.coor, &irq_flag, 1); + while (retry--) { + usleep_range(5000, 5100); + ts_test_read_trans(ts_test, core_data->ts_dev->reg.coor, &irq_flag, 1); + if (irq_flag & 0x80) + break; + } + if (retry < 0) { + ts_err("noisedata is not ready irq_flag:0x%02x, exit", irq_flag); + return -EAGAIN; + } + +#ifdef NOISE_DATA_TEST + /* read noise data */ + ret = ts_test_read_trans(ts_test, noisedata_addr, + (u8 *)&ts_test->noisedata.data[0], noise_data_size * sizeof(u16)); + if (ret < 0) { + ts_err("Failed read noise data"); + ts_test->noisedata.size = 0; + ts_test->test_result[GTP_NOISE_TEST] = SYS_SOFTWARE_REASON; + } + for (i = 0; i < noise_data_size; i++) + ts_test->noisedata.data[i] = be16_to_cpu(ts_test->noisedata.data[i]); + goodix_rotate_abcd2cbad(ts_test->test_params.drv_num, + ts_test->test_params.sen_num, ts_test->noisedata.data); +#endif +#ifdef SELFNOISE_DATA_TEST + /* read self noise data */ + ret = ts_test_read_trans(ts_test, self_noisedata_addr, + (u8 *)&ts_test->self_noisedata.data[0], self_noise_data_size * sizeof(u16)); + if (ret < 0) { + ts_err("Failed read self noise data"); + ts_test->self_noisedata.size = 0; + ts_test->test_result[GTP_SELFNOISE_TEST] = SYS_SOFTWARE_REASON; + } + for (i = 0; i < self_noise_data_size; i++) + ts_test->self_noisedata.data[i] = be16_to_cpu(ts_test->self_noisedata.data[i]); +#endif + return ret; +} + +#ifdef NOISE_DATA_TEST +/** + * gtx8_analyse_noisedata- analyse noisedata + */ +static void gtx8_analyse_noisedata(struct gtx8_ts_test *ts_test) +{ + int i = 0; + u32 find_bad_node = 0; + s16 noise_value = 0; + + + for (i = 0; i < ts_test->noisedata.size; i++) + ts_test->noisedata.data[i] = abs(noise_value); + + for (i = 0; i < ts_test->noisedata.size; i++) { + if (ts_test->noisedata.data[i] > ts_test->test_params.noise_threshold) { + find_bad_node++; + ts_err("noise check failed: niose[%d][%d]:%u, > %u\n", + (u32)div_s64(i, ts_test->test_params.drv_num), + i % ts_test->test_params.drv_num, + ts_test->noisedata.data[i], + ts_test->test_params.noise_threshold); + } + } + if (find_bad_node) { + ts_info("%s:noise test find bad node\n", __func__); + ts_test->test_result[GTP_NOISE_TEST] = GTP_PANEL_REASON; + }else{ + ts_test->test_result[GTP_NOISE_TEST] = GTP_TEST_PASS; + } + + return; +} +#endif + +#ifdef SELFNOISE_DATA_TEST +/** + * gtx8_analyse_self_noisedata- analyse self noisedata + */ +static void gtx8_analyse_self_noisedata(struct gtx8_ts_test *ts_test) +{ + int i = 0; + u32 self_find_bad_node = 0; + s16 self_noise_value = 0; + + for (i = 0; i < ts_test->self_noisedata.size; i++) { + ts_test->self_noisedata.data[i] = abs(self_noise_value); + + if (ts_test->self_noisedata.data[i] > ts_test->test_params.self_noise_threshold) { + self_find_bad_node++; + ts_err("self noise check failed: self_noise[%d]:%u, > %u",i, + ts_test->self_noisedata.data[i], + ts_test->test_params.self_noise_threshold); + } + } + + if (self_find_bad_node) { + ts_info("%s:self_noise test find bad node", __func__); + ts_test->test_result[GTP_SELFNOISE_TEST] = GTP_PANEL_REASON; + }else{ + ts_test->test_result[GTP_SELFNOISE_TEST] = GTP_TEST_PASS; + } + + return; +} +#endif + +/* test noise data */ +static void gtx8_test_noisedata(struct gtx8_ts_test *ts_test) +{ + int ret = 0; + int test_cnt = 0; + struct goodix_ts_core *core_data; + core_data = (struct goodix_ts_core*)ts_test->ts; + + ret = gtx8_noisetest_prepare(ts_test); + if (ret) { + ts_err("Noisetest prepare failed"); + goto soft_err_out; + } + + /* read noisedata and self_noisedata,calculate result */ + for (test_cnt = 0; test_cnt < RAWDATA_TEST_TIMES; test_cnt++) { + ret = gtx8_cache_noisedata(ts_test); + if (ret) { + if (test_cnt == RAWDATA_TEST_TIMES - 1) { + ts_err("Cache noisedata failed"); + goto soft_err_out; + }else{ + continue; + } + } +#ifdef NOISE_DATA_TEST + gtx8_analyse_noisedata(ts_test); +#endif +#ifdef SELFNOISE_DATA_TEST + gtx8_analyse_self_noisedata(ts_test); +#endif + break; + } + + ts_info("Noisedata test end"); + return; + +soft_err_out: +#ifdef NOISE_DATA_TEST + ts_test->noisedata.size = 0; + ts_test->test_result[GTP_NOISE_TEST] = SYS_SOFTWARE_REASON; +#endif +#ifdef SELFNOISE_DATA_TEST + ts_test->self_noisedata.size = 0; + ts_test->test_result[GTP_SELFNOISE_TEST] = SYS_SOFTWARE_REASON; +#endif +} +#endif + + +#ifdef SELF_RAWDATA_TEST +static int gtx8_self_rawcapacitance_test(struct ts_test_self_rawdata *rawdata, + struct ts_test_params *test_params) +{ + int i = 0; + int ret = NO_ERR; + + for (i = 0; i < rawdata->size; i++) { + if (rawdata->data[i] > test_params->self_max_limits[i]) { + ts_err("self_rawdata[%d]:%u >self_max_limit:%u, NG", i, + rawdata->data[i], test_params->self_max_limits[i]); + ret = RESULT_ERR; + } + + if (rawdata->data[i] < test_params->self_min_limits[i]) { + ts_err("self_rawdata[%d]:%u < min_limit:%u, NG", i, + rawdata->data[i], test_params->self_min_limits[i]); + ret = RESULT_ERR; + } + } + + return ret; +} +#endif + +#ifdef RAWDATA_TEST_16_FRAMES +static void gtx8_rawcapacitance_test_spec(struct gtx8_ts_test *ts_test, + u8 *p_beyond_max_cnt, u8 *p_beyond_min_cnt) +{ + int i = 0; + struct ts_test_params *test_params; + struct ts_test_rawdata *rawdata; + test_params = &ts_test->test_params; + rawdata = &ts_test->rawdata; + for (i=0;irawdata.size;i++){ + if (rawdata->data[i] > test_params->max_limits[i]) { + p_beyond_max_cnt[i]++; + } + if (rawdata->data[i] < test_params->min_limits[i]) { + p_beyond_min_cnt[i]++; + } + } +} + +static int gtx8_accord_test_spec(struct gtx8_ts_test *ts_test, u8 *p_beyond_cnt) +{ + int i = 0; + int ret = NO_ERR; + int cols = 0; + u32 max_val = 0; + u32 rawdata_val = 0; + u32 sc_data_num = 0; + u32 up = 0, down = 0, left = 0, right = 0; + struct ts_test_params *test_params; + struct ts_test_rawdata *rawdata; + test_params = &ts_test->test_params; + rawdata = &ts_test->rawdata; + + cols = test_params->drv_num; + sc_data_num = test_params->drv_num * test_params->sen_num; + if (cols <= 0) { + ts_err("%s: parmas invalid", __func__); + return RESULT_ERR; + } + + for (i = 0; i < sc_data_num; i++) { + rawdata_val = rawdata->data[i]; + max_val = 0; + /* calculate deltacpacitance with above node */ + if (i - cols >= 0) { + up = rawdata->data[i - cols]; + up = abs(rawdata_val - up); + if (up > max_val) + max_val = up; + } + + /* calculate deltacpacitance with bellow node */ + if (i + cols < sc_data_num) { + down = rawdata->data[i + cols]; + down = abs(rawdata_val - down); + if (down > max_val) + max_val = down; + } + + /* calculate deltacpacitance with left node */ + if (i % cols) { + left = rawdata->data[i - 1]; + left = abs(rawdata_val - left); + if (left > max_val) + max_val = left; + } + + /* calculate deltacpacitance with right node */ + if ((i + 1) % cols) { + right = rawdata->data[i + 1]; + right = abs(rawdata_val - right); + if (right > max_val) + max_val = right; + } + + /* float to integer */ + if (rawdata_val) { + max_val *= FLOAT_AMPLIFIER; + max_val = (u32)div_s64(max_val, rawdata_val); + if (max_val > test_params->deviation_limits[i]) { + p_beyond_cnt[i]++; + } + } else { + ts_err("Find rawdata=0 when calculate deltacapacitance:[%d][%d]", + (u32)div_s64(i, cols), i % cols); + ret = RESULT_ERR; + } + } + return ret; +} + + +static int gtx8_rawdata_result_analyze(struct gtx8_ts_test *ts_test, u8 *p_beyond_max_cnt, + u8 *p_beyond_min_cnt, u8 *p_beyond_accord_cnt, u8 frame_cnt) +{ + int ret = 0; + int i = 0; + int low_ratio = 1; + int high_ratio = 9; + struct ts_test_params *test_params; + struct ts_test_rawdata *rawdata; + test_params = &ts_test->test_params; + rawdata = &ts_test->rawdata; + ts_test->test_result[GTP_CAP_TEST] = GTP_TEST_PASS; + ts_test->test_result[GTP_DELTA_TEST] = GTP_TEST_PASS; + for (i=0;isize;i++){ + /* analyze max test*/ + if (p_beyond_max_cnt[i]*10/frame_cnt >= high_ratio){ + ts_err("max_cnt:%d, rawdata[%d][%d]:%u > max_limit:%u, NG",p_beyond_max_cnt[i], + (u32)div_s64(i, test_params->drv_num), i % test_params->drv_num, + rawdata->data[i], test_params->max_limits[i]); + ts_test->test_result[GTP_CAP_TEST] = GTP_PANEL_REASON; + }else if (p_beyond_min_cnt[i]*10/frame_cnt > low_ratio){ + ts_test->test_result[GTP_CAP_TEST] = GTP_PANEL_REASON; + ret = -1; + } + /*analyze min test*/ + if (p_beyond_min_cnt[i]*10/frame_cnt >= high_ratio){ + ts_err("min_cnt:%d,rawdata[%d][%d]:%u < min_limit:%u, NG",p_beyond_min_cnt[i], + (u32)div_s64(i, test_params->drv_num), i % test_params->drv_num, + rawdata->data[i], test_params->min_limits[i]); + ts_test->test_result[GTP_CAP_TEST] = GTP_PANEL_REASON; + }else if (p_beyond_min_cnt[i]*10/frame_cnt > low_ratio){ + ts_test->test_result[GTP_CAP_TEST] = GTP_PANEL_REASON; + ret = -1; + } + } + for (i=0;idrv_num * test_params->sen_num;i++){ + + /* analyze accord test*/ + if (p_beyond_accord_cnt[i]*10 >= high_ratio){ + ts_err("beyond accord cnt:%d,rawdata[%d][%d]:%u, deviation_limit:%u, NG", + p_beyond_accord_cnt[i], + (u32)div_s64(i, test_params->drv_num), i % test_params->drv_num, + rawdata->data[i], test_params->deviation_limits[i]); + ts_test->test_result[GTP_DELTA_TEST] = GTP_PANEL_REASON; + }else if (p_beyond_accord_cnt[i]*10 > low_ratio){ + ts_test->test_result[GTP_DELTA_TEST] = GTP_PANEL_REASON; + ret = -1; + } + } + return ret; +} + +/* gtx8_rawdata_test_spec + * test rawdata with 16 frames + */ +static void gtx8_rawdata_test_spec(struct gtx8_ts_test *ts_test) +{ + int ret = NO_ERR; + u16 i = 0; + u8 frame_num = 16; + u16 data_len = 0; + u8* p_beyond_max_limit_cnt = NULL; + u8* p_beyond_min_limit_cnt = NULL; + u8* p_beyond_accord_limit_cnt = NULL; + + data_len = MAX_DRV_NUM*MAX_SEN_NUM; + p_beyond_max_limit_cnt = kzalloc(data_len, GFP_KERNEL); + if (!p_beyond_max_limit_cnt){ + ts_err("%s: Failed to alloc mem", __func__); + goto RAW_TEST_ERR; + } + p_beyond_min_limit_cnt = kzalloc(data_len, GFP_KERNEL); + if (!p_beyond_min_limit_cnt){ + ts_err("%s: Failed to alloc mem", __func__); + goto RAW_TEST_ERR; + } + p_beyond_accord_limit_cnt = kzalloc(data_len, GFP_KERNEL); + if (!p_beyond_accord_limit_cnt){ + ts_err("%s: Failed to alloc mem", __func__); + goto RAW_TEST_ERR; + } + + //init beyond buffer + memset(p_beyond_max_limit_cnt,0,data_len); + memset(p_beyond_min_limit_cnt,0,data_len); + memset(p_beyond_accord_limit_cnt,0,data_len); + ts_info("data len:%d", data_len); + + /* rawdata test */ + for (i = 0; i < TOTAL_FRAME_NUM; i++) { + ret = gtx8_cache_rawdata(ts_test); + if (ret < 0) { + /* Failed read rawdata */ + ts_err("Read rawdata failed"); + goto RAW_TEST_ERR; + } + + gtx8_rawcapacitance_test_spec(ts_test,p_beyond_max_limit_cnt, + p_beyond_min_limit_cnt); + ret = gtx8_accord_test_spec(ts_test,p_beyond_accord_limit_cnt); + if (ret){ + ts_test->test_result[GTP_DELTA_TEST] = GTP_PANEL_REASON; + ts_err("DeltaCap test failed"); + } + if ((i + 1) % frame_num == 0) { + + ret = gtx8_rawdata_result_analyze(ts_test, p_beyond_max_limit_cnt, + p_beyond_min_limit_cnt, p_beyond_accord_limit_cnt,i+1); + if (!ret) { + break; + } + } + } + goto RAW_TEST_END; + +RAW_TEST_ERR: + ts_test->test_result[GTP_CAP_TEST] = SYS_SOFTWARE_REASON; + ts_test->test_result[GTP_DELTA_TEST] = SYS_SOFTWARE_REASON; +RAW_TEST_END: + if(p_beyond_max_limit_cnt){ + kfree(p_beyond_max_limit_cnt); + p_beyond_max_limit_cnt = NULL; + } + if(p_beyond_min_limit_cnt){ + kfree(p_beyond_min_limit_cnt); + p_beyond_min_limit_cnt = NULL; + } + if(p_beyond_accord_limit_cnt){ + kfree(p_beyond_accord_limit_cnt); + p_beyond_accord_limit_cnt = NULL; + } + return; +} + +#else + +static int gtx8_rawcapacitance_test(struct ts_test_rawdata *rawdata, + struct ts_test_params *test_params) +{ + int i = 0; + int ret = NO_ERR; + + for (i = 0; i < rawdata->size; i++) { + if (rawdata->data[i] > test_params->max_limits[i]) { + ts_err("rawdata[%d][%d]:%u > max_limit:%u, NG", + (u32)div_s64(i, test_params->drv_num), i % test_params->drv_num, + rawdata->data[i], test_params->max_limits[i]); + ret = RESULT_ERR; + } + + if (rawdata->data[i] < test_params->min_limits[i]) { + ts_err("rawdata[%d][%d]:%u < min_limit:%u, NG", + (u32)div_s64(i, test_params->drv_num), i % test_params->drv_num, + rawdata->data[i], test_params->min_limits[i]); + ret = RESULT_ERR; + } + } + + return ret; +} + +static int gtx8_deltacapacitance_test(struct ts_test_rawdata *rawdata, + struct ts_test_params *test_params) +{ + int i = 0; + int ret = NO_ERR; + int cols = 0; + u32 max_val = 0; + u32 rawdata_val = 0; + u32 sc_data_num = 0; + u32 up = 0, down = 0, left = 0, right = 0; + + cols = test_params->drv_num; + sc_data_num = test_params->drv_num * test_params->sen_num; + if (cols <= 0) { + ts_err("%s: parmas invalid", __func__); + return RESULT_ERR; + } + + for (i = 0; i < sc_data_num; i++) { + rawdata_val = rawdata->data[i]; + max_val = 0; + /* calculate deltacpacitance with above node */ + if (i - cols >= 0) { + up = rawdata->data[i - cols]; + up = abs(rawdata_val - up); + if (up > max_val) + max_val = up; + } + + /* calculate deltacpacitance with bellow node */ + if (i + cols < sc_data_num) { + down = rawdata->data[i + cols]; + down = abs(rawdata_val - down); + if (down > max_val) + max_val = down; + } + + /* calculate deltacpacitance with left node */ + if (i % cols) { + left = rawdata->data[i - 1]; + left = abs(rawdata_val - left); + if (left > max_val) + max_val = left; + } + + /* calculate deltacpacitance with right node */ + if ((i + 1) % cols) { + right = rawdata->data[i + 1]; + right = abs(rawdata_val - right); + if (right > max_val) + max_val = right; + } + + /* float to integer */ + if (rawdata_val) { + max_val *= FLOAT_AMPLIFIER; + max_val = (u32)div_s64(max_val, rawdata_val); + if (max_val > test_params->deviation_limits[i]) { + ts_err("deviation[%d][%d]:%u > delta_limit:%u, NG", + (u32)div_s64(i, test_params->drv_num;), + i % test_params->drv_num;, max_val, + test_params->deviation_limits[i]); + ret = RESULT_ERR; + } + } else { + ts_err("Find rawdata=0 when calculate deltacapacitance:[%d][%d]", + (u32)div_s64(i, cols), i % cols); + ret = RESULT_ERR; + } + } + + return ret; +} + +/* gtx8_rawdata_test + * test rawdata with one frame + */ +static void gtx8_rawdata_test(struct gtx8_ts_test *ts_test) +{ + int ret = 0; + /* read rawdata and calculate result, statistics fail times */ + ret = gtx8_cache_rawdata(ts_test); + if (ret < 0) { + /* Failed read rawdata */ + ts_err("Read rawdata failed"); + ts_test->test_result[GTP_CAP_TEST] = SYS_SOFTWARE_REASON; + ts_test->test_result[GTP_DELTA_TEST] = SYS_SOFTWARE_REASON; + } else { + ret = gtx8_rawcapacitance_test(&ts_test->rawdata, &ts_test->test_params); + if (!ret) { + ts_test->test_result[GTP_CAP_TEST] = GTP_TEST_PASS; + ts_info("Rawdata test pass"); + } else { + ts_test->test_result[GTP_CAP_TEST] = GTP_PANEL_REASON; + ts_err("RawCap test failed"); + } + + ret = gtx8_deltacapacitance_test(&ts_test->rawdata, &ts_test->test_params); + if (!ret) { + ts_test->test_result[GTP_DELTA_TEST] = GTP_TEST_PASS; + ts_info("DeltaCap test pass"); + } else { + ts_test->test_result[GTP_DELTA_TEST] = GTP_PANEL_REASON; + ts_err("DeltaCap test failed"); + } + } +} +#endif + +static void gtx8_capacitance_test(struct gtx8_ts_test *ts_test) +{ + int ret = 0; + struct goodix_ts_core *core_data; + core_data = (struct goodix_ts_core*)ts_test->ts; + +#ifdef RAWDATA_TEST_16_FRAMES + gtx8_rawdata_test_spec(ts_test); +#else + gtx8_rawdata_test(ts_test); +#endif + +#ifdef SELF_RAWDATA_TEST + /* read selfrawdata and calculate result, statistics fail times */ + ret = gtx8_cache_self_rawdata(ts_test); + if (ret < 0) { + /* Failed read selfrawdata */ + ts_err("Read selfrawdata failed"); + ts_test->test_result[GTP_SELFCAP_TEST] = SYS_SOFTWARE_REASON; + goto capac_test_exit; + } else { + ret = gtx8_self_rawcapacitance_test(&ts_test->self_rawdata, &ts_test->test_params); + if (!ret) { + ts_test->test_result[GTP_SELFCAP_TEST] = GTP_TEST_PASS; + ts_info("selfrawdata test pass"); + } else { + ts_test->test_result[GTP_SELFCAP_TEST] = GTP_PANEL_REASON; + ts_err("selfrawCap test failed"); + } + } +#endif +capac_test_exit: + ts_test_reset(ts_test); + return; +} +#ifdef SHORT_TEST +static void gtx8_shortcircut_test(struct gtx8_ts_test *ts_test); +static void gtx8_check_setting_group(struct gtx8_ts_test *ts_test, struct short_record *r_data); +#endif +static void gtx8_put_test_result(struct ts_rawdata_info *info, struct gtx8_ts_test *ts_test); + +void set_test_flag(int flag) +{ + g_save_result = flag; +} +int gtx8_get_rawdata(void *tsdev,struct ts_rawdata_info *info) +{ + int ret = 0; + struct gtx8_ts_test *gts_test = NULL; + struct device *dev; + struct goodix_ts_core *core_data; + + ts_info("tp test code version:%s", TEST_CODE_VERSION); + + if (!tsdev || !info) { + ts_err("gtx8_ts is NULL"); + return -ENODEV; + } + dev = (struct device *)tsdev; + core_data = dev_get_drvdata(dev); + + gts_test = kzalloc(sizeof(struct gtx8_ts_test), GFP_KERNEL); + if (!gts_test) { + ts_err("Failed to alloc mem"); + return -ENOMEM; + } + gts_test->ts = core_data; + + ret = gtx8_tptest_prepare(gts_test); + if (ret) { + ts_err("Failed parse test peremeters, exit test"); + strncpy(info->result, "0F-software reason", TS_RAWDATA_RESULT_MAX - 1); + goto exit_finish; + } + ts_info("TP test prepare OK"); +#if ((defined NOISE_DATA_TEST) || (defined SELFNOISE_DATA_TEST)) + gtx8_test_noisedata(gts_test); /*3F 7F test*/ +#endif + gtx8_capacitance_test(gts_test); /* 1F 2F 6F test*/ +#ifdef SHORT_TEST + gtx8_shortcircut_test(gts_test); /* 5F test */ +#endif + gtx8_put_test_result(info, gts_test); + gtx8_tptest_finish(gts_test); + +exit_finish: + if (gts_test) { + kfree(gts_test); + gts_test = NULL; + } + return ret; +} + +char *gtx8_strncat(char *dest, char *src, size_t dest_size) +{ + size_t dest_len = 0; + + dest_len = strnlen(dest, dest_size); + strlcat(dest, src, dest_len); + return dest; +} +char *gtx8_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 gtx8_strncat(dest, src_str, dest_size); +} + +static void gtx8_data_cal(u16 *data, size_t data_size, u16 *stat_result) +{ + u16 i = 0; + u16 avg = 0; + u16 min = 0; + u16 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; +} + +/** gtx8_data_statistics + * + * catlculate Avg Min Max value of data + */ +static void gtx8_data_statistics(u16 *data, size_t data_size, char *result, size_t res_size) +{ + u16 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; + } + gtx8_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; +} + +static void gtx8_put_test_result( + struct ts_rawdata_info *info, + struct gtx8_ts_test *ts_test) +{ + int i = 0; + int have_bus_error = 0; + int have_panel_error = 0; + char statistics_data[STATISTICS_DATA_LEN] = {0}; + struct goodix_ts_core *core_data; + core_data = (struct goodix_ts_core*)ts_test->ts; + + /* save rawdata to info->buff */ + info->used_size = 0; + + if (ts_test->rawdata.size) { + info->buff[0] = ts_test->test_params.sen_num; + info->buff[1] = ts_test->test_params.drv_num; + for (i = 0; i < ts_test->rawdata.size; i++) + info->buff[i + 2] = ts_test->rawdata.data[i]; + + info->used_size += ts_test->rawdata.size + 2; + } +#ifdef NOISE_DATA_TEST + /* save noise data to info->buff */ + if (ts_test->noisedata.size) { + for (i = 0; i < ts_test->noisedata.size; i++) + info->buff[info->used_size + i] = ts_test->noisedata.data[i]; + + info->used_size += ts_test->noisedata.size; + } +#endif +#ifdef SELFNOISE_DATA_TEST + /* 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; + } +#endif + +#ifdef SELF_RAWDATA_TEST + /* 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; + } +#endif + + /* 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 = 1; + else if (ts_test->test_result[i] == GTP_PANEL_REASON) + have_panel_error = 1; + } + ts_info("Have bus error:%d", have_bus_error); + + if (have_bus_error) + gtx8_strncat(ts_test->test_info, "0F-", TS_RAWDATA_RESULT_MAX); + else + gtx8_strncat(ts_test->test_info, "0P-", TS_RAWDATA_RESULT_MAX); + + for (i = 1; i < MAX_TEST_ITEMS; i++) { + /* if have tested, show result */ + if (ts_test->test_result[i]) { + if (GTP_TEST_PASS == ts_test->test_result[i]) + gtx8_strncatint(ts_test->test_info, i, "%dP-", TS_RAWDATA_RESULT_MAX); + else + gtx8_strncatint(ts_test->test_info, i, "%dF-", TS_RAWDATA_RESULT_MAX); + } + ts_info("test_result_info [%d]%d", i, ts_test->test_result[i]); + } + + if (ts_test->rawdata.size) { + /* calculate rawdata min avg max vale*/ + gtx8_data_statistics( + &ts_test->rawdata.data[0], + ts_test->rawdata.size, + statistics_data, + STATISTICS_DATA_LEN); + gtx8_strncat(ts_test->test_info, statistics_data, TS_RAWDATA_RESULT_MAX); + } else { + ts_info("NO valiable rawdata"); + gtx8_strncat(ts_test->test_info, "[0,0,0]", TS_RAWDATA_RESULT_MAX); + } +#ifdef NOISE_DATA_TEST + if (ts_test->noisedata.size) { + /* calculate noise data min avg max vale*/ + gtx8_data_statistics( + &ts_test->noisedata.data[0], + ts_test->noisedata.size, + statistics_data, + STATISTICS_DATA_LEN); + gtx8_strncat(ts_test->test_info, statistics_data, TS_RAWDATA_RESULT_MAX); + } else { + ts_info("NO valiable noisedata"); + gtx8_strncat(ts_test->test_info, "[0,0,0]", TS_RAWDATA_RESULT_MAX); + } +#endif + +#ifdef SELF_RAWDATA_TEST + if (ts_test->self_rawdata.size) { + /* calculate self_rawdata min avg max vale*/ + gtx8_data_statistics( + &ts_test->self_rawdata.data[0], + ts_test->self_rawdata.size, + statistics_data, + STATISTICS_DATA_LEN); + gtx8_strncat(ts_test->test_info, statistics_data, TS_RAWDATA_RESULT_MAX); + } else { + ts_info("NO valiable self_rawdata"); + gtx8_strncat(ts_test->test_info, "[0,0,0]", TS_RAWDATA_RESULT_MAX); + } +#endif + +#ifdef SELFNOISE_DATA_TEST + if (ts_test->self_noisedata.size) { + /* calculate self_noisedata min avg max vale*/ + gtx8_data_statistics( + &ts_test->self_noisedata.data[0], + ts_test->self_noisedata.size, + statistics_data, + STATISTICS_DATA_LEN); + gtx8_strncat(ts_test->test_info, statistics_data, TS_RAWDATA_RESULT_MAX); + } else { + ts_info("NO valiable self_noisedata"); + gtx8_strncat(ts_test->test_info, "[0,0,0]", TS_RAWDATA_RESULT_MAX); + } +#endif + + gtx8_strncat(ts_test->test_info, "-GT", TS_RAWDATA_RESULT_MAX); + gtx8_strncat(ts_test->test_info, core_data->ts_dev->chip_version.pid, TS_RAWDATA_RESULT_MAX); + gtx8_strncat(ts_test->test_info, "", TS_RAWDATA_RESULT_MAX); + ts_info("ts_test->test_info:%s", ts_test->test_info); + strncpy(info->result, ts_test->test_info, TS_RAWDATA_RESULT_MAX - 1); + +#ifdef SAVE_IN_CSV + if (g_save_result == 1) + gtx8_save_result_data(ts_test); +#endif + return; +} + +#ifdef SHORT_TEST +/* short test */ +static int gtx8_short_test_prepare(struct gtx8_ts_test *ts_test) +{ + int ret = 0, i = 0, retry = GTX8_RETRY_NUM_3; + u8 data[MAX_DRV_NUM + MAX_SEN_NUM] = {0}; + struct goodix_ts_core *core_data; + struct goodix_ts_cmd short_code_cmd; + core_data = (struct goodix_ts_core*)ts_test->ts; + goodix_cmd_init(core_data->ts_dev, &short_code_cmd, + 0x0b, 0, core_data->ts_dev->reg.command); + + ts_info("Short test prepare+"); + + while (--retry) { + /* switch to shrot test system */ + ret = core_data->ts_dev->hw_ops->send_cmd(core_data->ts_dev, + &short_code_cmd); /*bagan test command*/ + if (ret) { + ts_err("Can not switch to short test system"); + return ret; + } + msleep(50); + + /* check firmware running */ + for (i = 0; i < 20; i++) { + ts_info("Check firmware running.."); + ret = ts_test_read_trans(ts_test, + SHORT_STATUS_REG, &data[0], 1); /* SHORT_STATUS_REG is 0x5095 */ + if (ret) { + ts_err("Check firmware running failed"); + return ret; + } else if (data[0] == 0xaa) { + ts_info("Short firmware is running"); + break; + } + msleep(10); + } + if (i < 20) { + break; + } else { + ts_test_reset(ts_test); + } + } + if (retry <= 0) { + ret = -EINVAL; + ts_err("Switch to short test mode timeout"); + return ret; + } + + if (core_data->ts_dev->ic_type == IC_TYPE_NORMANDY) { + data[0] = 0; + /* turn off watch dog timer */ + ret = ts_test_write_trans(ts_test, WATCH_DOG_TIMER_REG, data, 1); + if (ret < 0) { + ts_err("Failed turn off watch dog timer"); + return ret; + } + } + + ts_info("Firmware in short test mode"); + + data[0] = (ts_test->test_params.short_threshold >> 8) & 0xff; + data[1] = ts_test->test_params.short_threshold & 0xff; + + /* write tx/tx, tx/rx, rx/rx short threshold value to 0x8408 */ + ret = ts_test_write_trans(ts_test, TXRX_THRESHOLD_REG, + data, 2); /* SHORT THRESHOLD_REG 0X8808 */ + if (ret < 0) { + ts_err("Failed write tx/tx, tx/rx, rx/rx short threshold value"); + return ret; + } + data[0] = (GNDAVDD_SHORT_VALUE >> 8) & 0xff; + data[1] = GNDAVDD_SHORT_VALUE & 0xff; + /* write default txrx/gndavdd short threshold value 16 to 0x804A*/ + ret = ts_test_write_trans(ts_test, GNDVDD_THRESHOLD_REG, + data, 2); /* SHORT THRESHOLD_REG 0X8808 */ + if (ret < 0) { + ts_err("Failed write txrx/gndavdd short threshold value"); + return ret; + } + + /* Write ADC dump data num to 0x840c */ + data[0] = (ADC_DUMP_NUM >> 8) & 0xff; + data[1] = ADC_DUMP_NUM & 0xff; + ret = ts_test_write_trans(ts_test, ADC_DUMP_NUM_REG, data, 2); + if (ret < 0) { + ts_err("Failed write ADC dump data number"); + return ret; + } + + /* write 0x01 to 0x5095 start short test */ + data[0] = 0x01; + ret = ts_test_write_trans(ts_test, SHORT_STATUS_REG, + data, 1); /* SHORT_STATUS_REG 0X5095 */ + if (ret) { + ts_err("Failed write running dsp reg"); + return ret; + } + + ts_info("Short test prepare-"); + return 0; +} + +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) + MAX_SEN_NUM; + + for (i = 0; i < test_params->max_sen_num; i++) { + if (test_params->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->max_drv_num; i++) { + if (test_params->drv_map[i] == chn_num) { + res = i; + break; + } + } + if (i >= test_params->max_drv_num) + ts_err("Faild found corrresponding channel num:%d", chn_num); + else + res |= DRV_CHANNEL_FLAG; + + return res; +} + +static int gtx8_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; + u32 chn_id_tmp = 0; + u32 pin_num = 0; + + avdd_value = test_params->avdd_value; + if (adc_signal == 0 || adc_signal == 0x8000) + adc_signal |= 1; + + if ((adc_signal & 0x8000) == 0) /* short to GND */ + r = SHORT_TO_GND_RESISTER(adc_signal); + else /* short to VDD */ + r = SHORT_TO_VDD_RESISTER(adc_signal, avdd_value); + + r = (long)div_s64(r, 100); + r = r > MAX_U16_VALUE ? MAX_U16_VALUE : r; + r = r < 0 ? 0 : r; + + 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 |= DRV_CHANNEL_FLAG; + else + chn_id_tmp -= MAX_DRV_NUM; + + if (r < r_th) { + pin_num = map_die2pin(test_params, chn_id_tmp); + ts_err("%s%d shortcircut to %s,R=%ldK,R_Threshold=%dK", + (pin_num & DRV_CHANNEL_FLAG) ? "DRV" : "SEN", + (pin_num & ~DRV_CHANNEL_FLAG), + (adc_signal & 0x8000) ? "VDD" : "GND", + r, r_th); + + return RESULT_ERR; + } + return NO_ERR; +} + +static u32 gtx8_short_resistance_calc(struct gtx8_ts_test *ts_test, + struct short_record *r_data, u16 self_capdata, u8 flag) +{ + u16 lineDrvNum = 0, lineSenNum = 0; + u8 DieNumber1 = 0, DieNumber2 = 0; + long r = 0; + u8 tmp_data; + int ret; + struct goodix_ts_core *core_data; + core_data = (struct goodix_ts_core*)ts_test->ts; + + lineDrvNum = MAX_DRV_NUM; + lineSenNum = MAX_SEN_NUM; + + if (core_data->ts_dev->ic_type == IC_TYPE_YELLOWSTONE) { + if (r_data->group1 != r_data->group2) { /* different Group */ + ret = ts_test_read_trans(ts_test, + YS_IC_MASK_TYPE_REG, &tmp_data, 1); /* */ + if (ret < 0) { + ts_err("Read IC_MASK_TYPE_ADDR falied"); + return ret; + } + if (tmp_data == 0x12) { + r = div_s64(self_capdata * 77 * FLOAT_AMPLIFIER, r_data->short_code); + r -= (77 * FLOAT_AMPLIFIER); + } else { + r = div_s64(self_capdata * 81 * FLOAT_AMPLIFIER, r_data->short_code); + r -= (81 * FLOAT_AMPLIFIER); + } + } else { + r = div_s64(self_capdata * 64 * FLOAT_AMPLIFIER, r_data->short_code); + r -= (64 * FLOAT_AMPLIFIER); + } + } else { + if (flag == 0) { + if (r_data->group1 != r_data->group2) { /* different Group */ + r = div_s64(self_capdata * 81 * FLOAT_AMPLIFIER, r_data->short_code); + r -= (81 * FLOAT_AMPLIFIER); + } else { + DieNumber1 = ((r_data->master & 0x80) == 0x80) ? + (r_data->master + lineSenNum) : r_data->master; + DieNumber2 = ((r_data->slave & 0x80) == 0x80) ? + (r_data->slave + lineSenNum) : r_data->slave; + DieNumber1 = (DieNumber1 >= DieNumber2) ? + (DieNumber1 - DieNumber2) : (DieNumber2 - DieNumber1); + if ((DieNumber1 > 3) && (r_data->group1 == 0)) { + r = div_s64(self_capdata * 81 * FLOAT_AMPLIFIER, r_data->short_code); + r -= (81 * FLOAT_AMPLIFIER); + } else { + r = div_s64(self_capdata * 64 * FLOAT_AMPLIFIER, r_data->short_code); + r -= (64 * FLOAT_AMPLIFIER); + } + } + } else { + r = div_s64(self_capdata * 81 * FLOAT_AMPLIFIER, r_data->short_code); + r -= (81 * FLOAT_AMPLIFIER); + } + } + + /*if (r < 6553) + r *= 10; + else + r = 65535;*/ + r = (long)div_s64(r, FLOAT_AMPLIFIER); + r = r > MAX_U16_VALUE ? MAX_U16_VALUE : r; + + return r >= 0 ? r : 0; +} + +static int gtx8_shortcircut_analysis(struct gtx8_ts_test *ts_test) +{ + int ret = 0, err = 0; + u32 r_threshold = 0, short_r = 0; + int size = 0, i = 0, j = 0; + u32 master_pin_num, slave_pin_num; + u16 adc_signal = 0, data_addr = 0; + u8 short_flag = 0, *data_buf = NULL, short_status[3] = {0}; + u16 self_capdata[MAX_DRV_NUM + MAX_SEN_NUM] = {0}, short_die_num = 0; + struct short_record temp_short_info; + struct ts_test_params *test_params = &ts_test->test_params; + struct goodix_ts_core *core_data; + core_data = (struct goodix_ts_core*)ts_test->ts; + + ret = ts_test_read_trans(ts_test, TEST_RESTLT_REG, + &short_flag, 1); /* TEST_RESTLT_REG 0x8401 */ + if (ret < 0) { + ts_err("Read TEST_TESULT_REG falied"); + goto shortcircut_analysis_error; + } else if ((short_flag & 0x0F) == 0x00) { + ts_info("No shortcircut"); + return NO_ERR; + } + + data_buf = kzalloc((MAX_DRV_NUM + MAX_SEN_NUM) * 2, GFP_KERNEL); + if (!data_buf) { + ts_err("Failed to alloc memory"); + goto shortcircut_analysis_error; + } + + /* shortcircut to gnd&vdd */ + if (short_flag & 0x08) { + /* read diff code, diff code will be used to calculate + * resistance between channel and GND */ + size = (MAX_DRV_NUM + MAX_SEN_NUM) * 2; + ret = ts_test_read_trans(ts_test, DIFF_CODE_REG, + data_buf, size); /* DIFF_CODE_REG 0xA97A */ + if (ret < 0) { + ts_err("Failed read to-gnd rawdata"); + goto shortcircut_analysis_error; + } + for (i = 0; i < size; i += 2) { + adc_signal = be16_to_cpup((__be16 *)&data_buf[i]); + ret = gtx8_check_resistance_to_gnd(test_params, + adc_signal, i >> 1); /* i >> 1 = i / 2 */ + if (ret) { + ts_err("Resistance to-gnd test failed"); + err |= ret; + } + } + } + + /* read self-capdata+ */ + size = (MAX_DRV_NUM + MAX_SEN_NUM) * 2; + ret = ts_test_read_trans(ts_test, DRV_SELF_CODE_REG, + data_buf, size); /* DRV_SELF_CODE_REG 0xa8e0 */ + if (ret) { + ts_err("Failed read selfcap rawdata"); + goto shortcircut_analysis_error; + } + for (i = 0; i < MAX_DRV_NUM + MAX_SEN_NUM; i++) + self_capdata[i] = be16_to_cpup((__be16 *)&data_buf[i * 2]) & 0x7fff; + /* read self-capdata- */ + + /* read tx tx short number + ** short_status[0]: tr tx + ** short_status[1]: tr rx + ** short_status[2]: rx rx + */ + ret = ts_test_read_trans(ts_test, TX_SHORT_NUM, + &short_status[0], 3); /* TX_SHORT_NUM 0x8402 */ + if (ret) { + ts_err("Failed read tx-to-tx short rawdata"); + goto shortcircut_analysis_error; + } + ts_info("Tx&Tx:%d,Rx&Rx:%d,Tx&Rx:%d", short_status[0], short_status[1], short_status[2]); + + /* drv&drv shortcircut check */ + data_addr = 0x8460; + for (i = 0; i < short_status[0]; i++) { + size = SHORT_CAL_SIZE(MAX_DRV_NUM); /* 4 + MAX_DRV_NUM * 2 + 2; */ + ret = ts_test_read_trans(ts_test, data_addr, data_buf, size); + if (ret) { + ts_err("Failed read drv-to-drv short rawdata"); + goto shortcircut_analysis_error; + } + + r_threshold = test_params->r_drv_drv_threshold; + short_die_num = be16_to_cpup((__be16 *)&data_buf[0]); + if (short_die_num > MAX_DRV_NUM + MAX_SEN_NUM || + short_die_num < MAX_SEN_NUM) { + ts_info("invalid short pad num:%d", short_die_num); + continue; + } + + /* TODO: j start position need recheck */ + short_die_num -= MAX_SEN_NUM; + for (j = short_die_num + 1; j < MAX_DRV_NUM; j++) { + adc_signal = be16_to_cpup((__be16 *)&data_buf[4 + j * 2]); + + if (adc_signal > test_params->short_threshold) { + temp_short_info.master = short_die_num | DRV_CHANNEL_FLAG; + temp_short_info.slave = j | DRV_CHANNEL_FLAG; + temp_short_info.short_code = adc_signal; + gtx8_check_setting_group(ts_test, &temp_short_info); + + if (self_capdata[short_die_num] == 0xffff || + self_capdata[short_die_num] == 0) { + ts_info("invalid self_capdata:0x%x", self_capdata[short_die_num]); + continue; + } + + short_r = gtx8_short_resistance_calc(ts_test, &temp_short_info, + self_capdata[short_die_num], 0); + if (short_r < 0) + goto shortcircut_analysis_error; + + if (short_r < r_threshold) { + master_pin_num = map_die2pin(test_params, temp_short_info.master); + slave_pin_num = map_die2pin(test_params, temp_short_info.slave); + ts_err("Tx/Tx 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_addr += size; + } + + /* sen&sen shortcircut check */ + data_addr = 0x91d0; + for (i = 0; i < short_status[1]; i++) { + size = SHORT_CAL_SIZE(MAX_SEN_NUM); /* 4 + MAX_SEN_NUM * 2 + 2; */ + ret = ts_test_read_trans(ts_test, data_addr, data_buf, size); + if (ret) { + ts_err("Failed read sen-to-sen short rawdata"); + goto shortcircut_analysis_error; + } + + r_threshold = ts_test->test_params.r_sen_sen_threshold; + short_die_num = be16_to_cpup((__be16 *)&data_buf[0]); + if (short_die_num > MAX_SEN_NUM) + continue; + + for (j = short_die_num + 1; j < MAX_SEN_NUM; j++) { + adc_signal = be16_to_cpup((__be16 *)&data_buf[4 + j * 2]); + if (adc_signal > ts_test->test_params.short_threshold) { + temp_short_info.master = short_die_num; + temp_short_info.slave = j; + temp_short_info.short_code = adc_signal; + gtx8_check_setting_group(ts_test, &temp_short_info); + + if (self_capdata[short_die_num + MAX_DRV_NUM] == 0xffff || + self_capdata[short_die_num + MAX_DRV_NUM] == 0) { + ts_info("invalid self_capdata:0x%x", + self_capdata[short_die_num + MAX_DRV_NUM]); + continue; + } + + short_r = gtx8_short_resistance_calc(ts_test, &temp_short_info, + self_capdata[short_die_num + MAX_DRV_NUM], 0); + if (short_r < 0) + goto shortcircut_analysis_error; + if (short_r < r_threshold) { + master_pin_num = map_die2pin(test_params, temp_short_info.master); + slave_pin_num = map_die2pin(test_params, temp_short_info.slave); + ts_err("Rx/Rx 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_addr += size; + } + + /* sen&drv shortcircut check */ + data_addr = 0x9cc8; + for (i = 0; i < short_status[2]; i++) { + size = SHORT_CAL_SIZE(MAX_DRV_NUM); /* size = 4 + MAX_SEN_NUM * 2 + 2; */ + ret = ts_test_read_trans(ts_test, data_addr, data_buf, size); + if (ret) { + ts_err("Failed read sen-to-drv short rawdata"); + goto shortcircut_analysis_error; + } + + r_threshold = ts_test->test_params.r_drv_sen_threshold; + short_die_num = be16_to_cpup((__be16 *)&data_buf[0]); + if (short_die_num > MAX_SEN_NUM) + continue; + + for (j = 0; j < MAX_DRV_NUM; j++) { + adc_signal = be16_to_cpup((__be16 *)&data_buf[4 + j * 2]); + if (adc_signal > ts_test->test_params.short_threshold) { + temp_short_info.master = short_die_num; + temp_short_info.slave = j | DRV_CHANNEL_FLAG; + temp_short_info.short_code = adc_signal; + gtx8_check_setting_group(ts_test, &temp_short_info); + + if (self_capdata[short_die_num + MAX_DRV_NUM] == 0xffff || + self_capdata[short_die_num + MAX_DRV_NUM] == 0) { + ts_info("invalid self_capdata:0x%x", + self_capdata[short_die_num + MAX_DRV_NUM]); + continue; + } + + short_r = gtx8_short_resistance_calc(ts_test, &temp_short_info, + self_capdata[short_die_num + MAX_DRV_NUM], 0); + if (short_r < 0) + goto shortcircut_analysis_error; + if (short_r < r_threshold) { + master_pin_num = map_die2pin(test_params, temp_short_info.master); + slave_pin_num = map_die2pin(test_params, temp_short_info.slave); + ts_err("Rx/Tx 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_addr += size; + } + + if (data_buf) { + kfree(data_buf); + data_buf = NULL; + } + + return err | ret ? -EFAULT : NO_ERR; +shortcircut_analysis_error: + if (data_buf != NULL) { + kfree(data_buf); + data_buf = NULL; + } + return -EINVAL; +} + +static void gtx8_shortcircut_test(struct gtx8_ts_test *ts_test) +{ + int i = 0; + int ret = 0; + u8 data[2] = {0}; + struct goodix_ts_core *core_data; + core_data = (struct goodix_ts_core*)ts_test->ts; + + ts_test->test_result[GTP_SHORT_TEST] = GTP_TEST_PASS; + ret = gtx8_short_test_prepare(ts_test); + if (ret < 0) { + ts_err("Failed enter short test mode"); + ts_test->test_result[GTP_SHORT_TEST] = SYS_SOFTWARE_REASON; + return; + } + + msleep(2000); + + for (i = 0; i < 110; i++) { + msleep(50); + ts_info("waitting for short test end...:retry=%d", i); + ret = ts_test_read_trans(ts_test, + SHORT_TESTEND_REG, data, 1); /* SHORT_TESTEND_REG 0x8400 */ + if (ret) + ts_err("Failed get short test result: retry%d", i); + else if (data[0] == 0x88) /* test ok*/ + break; + } + + if (i < 110) { + ret = gtx8_shortcircut_analysis(ts_test); + if (ret) { + ts_test->test_result[GTP_SHORT_TEST] = GTP_PANEL_REASON; + ts_err("Short test failed"); + } else { + ts_info("Short test success"); + } + } else { + ts_err("Wait short test finish timeout:reg_val=0x%x", data[0]); + ts_test->test_result[GTP_SHORT_TEST] = SYS_SOFTWARE_REASON; + } +} + +static u8 get_chn_group_ys(u8 chn) +{ + u8 group = 0; + u8 setting_num = 7; + + //sen + if ((chn >= 32) && (chn < 36)) // (Setting1) C(bit10) + group = 2 * setting_num + 0; + else if ((chn >= 27) && (chn < 32)) // (Setting2) C(bit10) + group = 2 * setting_num + 1; + else if ((chn >= 20) && (chn < 24)) // (Setting3) A(bit00) + group = 0 * setting_num + 2; + else if (chn == 25 || // (Setting3) C(bit10) + chn == 26 || chn == 19 || + chn == 24 || chn == 14 || + chn == 17 || chn == 18 || + chn == 15 || chn == 16 || + chn == 6 || chn == 13) + group = 2 * setting_num + 2; + else if (chn == 3 || chn == 12 || // (Setting4) C(bit10) + chn == 0 || chn == 1) + group = 2 * setting_num + 3; + else if ((chn >= 7) && (chn < 12)) // (Setting5) A(bit00) + group = 0 * setting_num + 4; + else if (chn == 5 || // (Setting5) C(bit10) + chn == 4 || chn == 2) + group = 2 * setting_num + 4; + //drv + else if ((chn >= 67) && (chn < 72)) // (Setting2) B(bit01) + group = 1 * setting_num + 1; + else if (chn == 63 || // (Setting4) B(bit01) + chn ==64 || chn == 60 + || chn == 61) + group = 1 * setting_num + 3; + else if (chn == 62 || // (Setting5) B(bit01) + chn == 57 || chn == 58 || + chn == 59 || chn == 54 ) + group = 1 * setting_num + 4; + else if (chn == 75 || chn == 44) // (Setting5) C(bit10) + group = 2 * setting_num + 4; + else if (chn == 56 || // (Setting6) B(bit01) + chn == 55 || ((chn >= 47) && (chn < 54))) + group = 1 * setting_num + 5; + else if ( chn == 74 || // (Setting6) C(bit10) + ((chn >= 36) && (chn < 44))) + group = 2 * setting_num + 5; + else if (chn == 73 || chn == 72) // (Setting7) A(bit00) + group = 0 * setting_num + 6; + else if (chn == 46 || chn == 45) // (Setting7) B(bit01) + group = 1 * setting_num + 6; + else if (chn == 66 || chn == 65) // (Setting7) C(bit10) + group = 2 * setting_num + 6; + + return group; +} + + +static u8 get_chn_group_nor(u8 chn) +{ + u8 group = 0; + + if ((chn >= 0) && (chn < 9)) /* pad s0~s8 */ + group = 5; + + else if ((chn >= 9) && (chn < 14)) /* pad s9~s13 */ + group = 4; + + else if ((chn >= 14) && (chn < 18)) /* pad s14~s17 */ + group = 3; + + else if ((chn >= 18) && (chn < 27)) /* pad s18~s26 */ + group = 2; + + else if ((chn >= 27) && (chn < 32)) /* pad s27~s31 */ + group = 1; + + else if ((chn >= 32) && (chn < 36)) /* pad s32~s35 */ + group = 0; + + else if ((chn >= 36) && (chn < 45)) /* pad d0~d8 */ + group = 5; + + else if ((chn >= 45) && (chn < 54)) /* pad d9~d17 */ + group = 2; + + else if ((chn >= 54) && (chn < 59)) /* pad d18~d22 */ + group = 1; + + else if ((chn >= 59) && (chn < 63)) /* pad d23~d26 */ + group = 0; + + else if ((chn >= 63) && (chn < 67)) /* pad d27~d30 */ + group = 3; + + else if ((chn >= 67) && (chn < 72)) /* pad d31~d35 */ + group = 4; + + else if ((chn >= 72) && (chn < 76)) /* pad d36~d39 */ + group = 0; + + return group; +} + +static void gtx8_check_setting_group(struct gtx8_ts_test *ts_test, struct short_record *r_data) +{ + u32 master = 0; + u32 slave = 0; + struct goodix_ts_core *core_data; + core_data = (struct goodix_ts_core*)ts_test->ts; + + if (r_data->master & 0x80) + master = MAX_SEN_NUM; + + if (r_data->slave & 0x80) + slave = MAX_SEN_NUM; + + master += (r_data->master & 0x7f); + slave += (r_data->slave & 0x7f); + + if (core_data->ts_dev->ic_type == IC_TYPE_YELLOWSTONE) { + r_data->group1 = get_chn_group_ys(master); + r_data->group2 = get_chn_group_ys(slave); + } else if (core_data->ts_dev->ic_type == IC_TYPE_NORMANDY) { + r_data->group1 = get_chn_group_nor(master); + r_data->group2 = get_chn_group_nor(slave); + } else { + r_data->group1 = get_chn_group_nor(master); + r_data->group2 = get_chn_group_nor(slave); + } +} +#endif diff --git a/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_tools.c b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_tools.c new file mode 100644 index 000000000000..6c832dc8c36e --- /dev/null +++ b/drivers/input/touchscreen/goodix_gtx8_v1510_mmi/goodix_ts_tools.c @@ -0,0 +1,645 @@ +/* + * Goodix Tools Dirver 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "goodix_ts_core.h" + +#define GOODIX_TOOLS_NAME "gtp_tools" +#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_DRV_VERSION (_IOR(GOODIX_TS_IOC_MAGIC, 9, u8) & NEGLECT_SIZE_MASK) + +#define GOODIX_TS_IOC_MAXNR 10 +#define MAX_BUF_LENGTH (16*1024) +#define IRQ_FALG (0x01 << 2) + +#define I2C_MSG_HEAD_LEN 20 +#define TS_REG_COORDS_BASE 0x4100 + +/* + * struct goodix_tools_data - goodix tools data message used in sync read + * @data: The buffer into which data is written + * @reg_addr: Slave device register start address to start read data + * @length: Number of data bytes in @data being read from slave device + * @filled: When buffer @data be filled will set this flag with 1, outhrwise 0 + * @list_head:Eonnet every goodix_tools_data struct into a list + */ + +struct goodix_tools_data { + u32 reg_addr; + u32 length; + u8 *data; + bool filled; + struct list_head list; +}; + + +/* + * 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]; + struct goodix_ts_device *ts_dev = dev->ts_core->ts_dev; + const struct goodix_ts_hw_ops *hw_ops = ts_dev->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_trans(ts_dev, 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_device *ts_dev, 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_dev->hw_ops->read_config) + ret = ts_dev->hw_ops->read_config(ts_dev, tmp_buf, length); + else + ret = -EINVAL; + } else { + ret = ts_dev->hw_ops->read_trans(ts_dev, 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; +} + +/* read data from i2c synchronous, + * return data length when success, otherwise return < 0 + */ +static int sync_read(struct goodix_tools_dev *dev, void __user *arg) +{ + int ret = 0; + u8 i2c_msg_head[I2C_MSG_HEAD_LEN]; + struct goodix_tools_data tools_data; + + ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN); + if (ret) { + ts_err("Copy data from user failed"); + return -EFAULT; + } + tools_data.reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8) + + (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24); + tools_data.length = i2c_msg_head[4] + (i2c_msg_head[5] << 8) + + (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24); + tools_data.filled = 0; + if (tools_data.length > MAX_BUF_LENGTH) { + ts_err("buffer too long:%d > %d", + tools_data.length, MAX_BUF_LENGTH); + return -EINVAL; + } + tools_data.data = kzalloc(tools_data.length, GFP_KERNEL); + if (!tools_data.data) { + ts_err("Alloc memory failed"); + return -ENOMEM; + } + + mutex_lock(&dev->mutex); + list_add_tail(&tools_data.list, &dev->head); + mutex_unlock(&dev->mutex); + /* wait queue will timeout after 1 seconds */ + wait_event_interruptible_timeout(dev->wq, tools_data.filled == 1, HZ * 3); + + mutex_lock(&dev->mutex); + list_del(&tools_data.list); + mutex_unlock(&dev->mutex); + if (!tools_data.filled) { + ret = -EAGAIN; + ts_err("Wait queue timeout"); + goto out; + } + + ret = copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, + tools_data.data, tools_data.length); + if (ret) { + ret = -EFAULT; + ts_err("Copy_to_user failed"); + goto out; + } + ret = tools_data.length; +out: + kfree(tools_data.data); + 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_device *ts_dev = dev->ts_core->ts_dev; + const struct goodix_ts_hw_ops *hw_ops = ts_dev->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_trans(ts_dev, 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_ts_config *cfg, void __user *arg) +{ + int ret = 0; + u32 reg_addr, length; + u8 i2c_msg_head[I2C_MSG_HEAD_LEN]; + + cfg->initialized = 0; + mutex_init(&cfg->lock); + 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; + } + ret = copy_from_user(cfg->data, (u8 *)arg + I2C_MSG_HEAD_LEN, length); + if (ret) { + ret = -EFAULT; + ts_err("Copy data from user failed"); + goto err_out; + } + cfg->reg_base = reg_addr; + cfg->length = length; + strlcpy(cfg->name, "tools-send-cfg", sizeof(cfg->name)); + cfg->delay = 50; + cfg->initialized = true; + return 0; + +err_out: + return ret; +} + +/** + * 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_device *ts_dev; + const struct goodix_ts_hw_ops *hw_ops; + struct goodix_ts_cmd temp_cmd; + struct goodix_ts_config *temp_cfg = NULL; + + if (dev->ts_core == NULL) { + ts_err("Tools module not register"); + return -EINVAL; + } + ts_dev = dev->ts_core->ts_dev; + hw_ops = ts_dev->hw_ops; + + if (_IOC_TYPE(cmd) != GOODIX_TS_IOC_MAGIC) { + ts_err("Bad magic num:%c", _IOC_TYPE(cmd)); + return -ENOTTY; + } + if (_IOC_NR(cmd) > GOODIX_TS_IOC_MAXNR) { + ts_err("Bad cmd num:%d > %d", + _IOC_NR(cmd), GOODIX_TS_IOC_MAXNR); + return -ENOTTY; + } + + switch (cmd & NEGLECT_SIZE_MASK) { + case GTP_IRQ_ENABLE: + if (arg == 1) { + goodix_ts_irq_enable(dev->ts_core, true); + mutex_lock(&dev->mutex); + dev->ops_mode |= IRQ_FALG; + mutex_unlock(&dev->mutex); + ts_info("IRQ enabled"); + } else if (arg == 0) { + goodix_ts_irq_enable(dev->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_dev); + break; + case GTP_SEND_COMMAND: + ret = copy_from_user(&temp_cmd, (void __user *)arg, + sizeof(struct goodix_ts_cmd)); + if (ret) { + ret = -EINVAL; + goto err_out; + } + + ret = hw_ops->send_cmd(ts_dev, &temp_cmd); + if (ret) { + ts_err("Send command failed"); + ret = -EAGAIN; + } + break; + case GTP_SEND_CONFIG: + temp_cfg = kzalloc(sizeof(struct goodix_ts_config), GFP_KERNEL); + if (temp_cfg == NULL) { + 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_dev, temp_cfg); + if (ret) { + ts_err("Failed send config"); + ret = -EAGAIN; + } else { + ts_info("Send config success"); + ret = 0; + } + } + break; + case GTP_READ_CONFIG: + ret = read_config_data(ts_dev, (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: + if (filp->f_flags & O_NONBLOCK) { + ts_err("Goodix tools now worked in sync_bus mode"); + ret = -EAGAIN; + goto err_out; + } + ret = sync_read(dev, (void __user *)arg); + if (ret < 0) + ts_err("Sync data read failed"); + break; + case GTP_ASYNC_WRITE: + ret = async_write(dev, (void __user *)arg); + if (ret < 0) + ts_err("Async data write failed"); + break; + case GTP_DRV_VERSION: + ret = copy_to_user((u8 *)arg, GOODIX_DRIVER_VERSION, + sizeof(GOODIX_DRIVER_VERSION)); + if (ret) + ts_err("failed copy driver version info to user"); + break; + default: + ts_info("Invalid cmd"); + ret = -ENOTTY; + break; + } + +err_out: + if (!temp_cfg) { + kfree(temp_cfg); + temp_cfg = NULL; + } + 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"); + 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 */ + atomic_set(&goodix_tools_dev->in_use, 0); + ret = goodix_unregister_ext_module(&goodix_tools_dev->module); + return ret; +} + +/** + * goodix_tools_module_irq - goodix tools Irq handle + * This functions is excuted when interrupt happended + * + * @core_data: pointer to touch core data + * @module: pointer to goodix_ext_module struct + * return: EVT_CONTINUE let other module handle this irq + */ +static int goodix_tools_module_irq(struct goodix_ts_core *core_data, + struct goodix_ext_module *module) +{ + struct goodix_tools_dev *dev = module->priv_data; + struct goodix_ts_device *ts_dev = dev->ts_core->ts_dev; + const struct goodix_ts_hw_ops *hw_ops = ts_dev->hw_ops; + struct goodix_tools_data *tools_data, *next; + int r = 0; + u8 evt_sta = 0; + + mutex_lock(&dev->mutex); + if (!list_empty(&dev->head)) { + r = hw_ops->read_trans(ts_dev, ts_dev->reg.coor/* TS_REG_COORDS_BASE*/, &evt_sta, 1); + if (r < 0 || ((evt_sta & GOODIX_TOUCH_EVENT) == 0)) { + ts_err("data not ready:0x%x, read ret =%d", evt_sta, r); + mutex_unlock(&dev->mutex); + return EVT_CONTINUE; + } + + list_for_each_entry_safe(tools_data, next, &dev->head, list) { + if (!hw_ops->read_trans(ts_dev, tools_data->reg_addr, + tools_data->data, tools_data->length)) { + tools_data->filled = 1; + } + } + wake_up(&dev->wq); + } + mutex_unlock(&dev->mutex); + return EVT_CONTINUE; +} + +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 = { + .irq_event = goodix_tools_module_irq, + .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 __init 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"); + + return ret; +} + +void __exit goodix_tools_exit(void) +{ + misc_deregister(&goodix_tools_miscdev); + kfree(goodix_tools_dev); + ts_info("Goodix tools miscdev exit"); +} diff --git a/drivers/input/touchscreen/himax_0flash_mmi/Android.mk b/drivers/input/touchscreen/himax_0flash_mmi/Android.mk new file mode 100644 index 000000000000..70360edc30eb --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/Android.mk @@ -0,0 +1,43 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +HIMAX_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +HIMAX_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +ifeq ($(HIMAX_V2_MMI_0FLASH),true) + KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_HIMAX_0FLASH_MMI_0FLASH=y +endif +ifneq ($(BOARD_USES_TOUCH_PALM),) + KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_HIMAX_0FLASH_MMI_ENABLE_PALM=y +endif + +ifneq ($(BOARD_USES_DOUBLE_TAP),) + KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_0FLASH_MMI_ENABLE_DOUBLE_TAP=y +endif + +ifneq ($(BOARD_USES_HIMAX_MULTI_BIN),) + KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_0FLASH_MMI_ENABLE_MULTI_BIN=y +endif + +ifeq ($(HIMAX_V2_MMI_HX83102D),true) + KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_0FLASH_MMI_IC_NAME_D=hx83102d +endif + +ifeq ($(HIMAX_V2_MMI_HX83112F),true) + KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_0FLASH_MMI_IC_NAME=hx83112f +endif + +ifneq ($(BOARD_USES_PANEL_NOTIFICATIONS),) + KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_PANEL_NOTIFICATIONS=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := himax_0flash_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(HIMAX_MMI_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/drivers/input/touchscreen/himax_0flash_mmi/Kbuild b/drivers/input/touchscreen/himax_0flash_mmi/Kbuild new file mode 100644 index 000000000000..bef5812282e4 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/Kbuild @@ -0,0 +1,59 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_0flash_mmi +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_DEBUG \ + -DCONFIG_TOUCHSCREEN_HIMAX_INSPECT + +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_INCELL +EXTRA_CFLAGS += -DCONFIG_HMX_DB + +ifneq ($(filter hx83112f,$(CONFIG_INPUT_HIMAX_0FLASH_MMI_IC_NAME)),) + EXTRA_CFLAGS += -D__HIMAX_HX83112_MOD__ + EXTRA_CFLAGS += -DHX_FIX_TOUCH_INFO + EXTRA_CFLAGS += -DHX_112F_SET +# Fixed warning: variable length array used [-Wvla] + EXTRA_CFLAGS += -DFIX_WVLA +endif + +ifneq ($(filter hx83112,$(CONFIG_INPUT_HIMAX_0FLASH_MMI_IC_NAME)),) + EXTRA_CFLAGS += -D__HIMAX_HX83112_MOD__ +endif + +ifneq ($(filter hx83102,$(CONFIG_INPUT_HIMAX_0FLASH_MMI_IC_NAME)),) + EXTRA_CFLAGS += -D__HIMAX_HX83102_MOD__ +endif + +ifneq ($(filter hx83102d,$(CONFIG_INPUT_HIMAX_0FLASH_MMI_IC_NAME_D)),) + EXTRA_CFLAGS += -D__HIMAX_HX83102D_MOD__ + EXTRA_CFLAGS += -DHX_CODE_OVERLAY +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_HIMAX_0FLASH_MMI_ENABLE_PALM)),) + EXTRA_CFLAGS += -DHIMAX_PALM_SENSOR_EN +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_HIMAX_0FLASH_MMI_ENABLE_DOUBLE_TAP)),) + EXTRA_CFLAGS += -DHIMAX_V2_SENSOR_EN +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_HIMAX_0FLASH_MMI_ENABLE_MULTI_BIN)),) + EXTRA_CFLAGS += -DHIMAX_V2_MULTI_BIN +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_HIMAX_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DHIMAX_CONFIG_PANEL_NOTIFICATIONS +endif + +obj-m := himax_0flash_mmi.o + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_HIMAX_0FLASH_MMI_0FLASH)),) + himax_0flash_mmi-objs += himax_platform_QCT_SPI.o + EXTRA_CFLAGS += -D__HIMAX_PLATFORM_SPI__ \ + -DHX_ZERO_FLASH +endif + +himax_0flash_mmi-objs += himax_common.o +himax_0flash_mmi-objs += himax_debug.o +himax_0flash_mmi-objs += himax_ic_incell_core.o +himax_0flash_mmi-objs += himax_inspection.o diff --git a/drivers/input/touchscreen/himax_0flash_mmi/Makefile b/drivers/input/touchscreen/himax_0flash_mmi/Makefile new file mode 100755 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/himax_0flash_mmi/himax_common.c b/drivers/input/touchscreen/himax_0flash_mmi/himax_common.c new file mode 100644 index 000000000000..13b20c0b0a0b --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/himax_common.c @@ -0,0 +1,4265 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for common functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +/*#include "himax_common.h"*/ +/*#include "himax_ic_core.h"*/ +#include "himax_inspection.h" +#include "himax_modular_table.h" + +#ifdef HX_SMART_WAKEUP +#define GEST_SUP_NUM 26 +/* Setting cust key define (DF = double finger) */ +/* {Double Tap, Up, Down, Left, Rright, C, Z, M, + * O, S, V, W, e, m, @, (reserve), + * Finger gesture, ^, >, <, f(R), f(L), Up(DF), Down(DF), + * Left(DF), Right(DF)} + */ + uint8_t gest_event[GEST_SUP_NUM] = { + 0x80, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x81, 0x1D, 0x2D, 0x3D, 0x1F, 0x2F, 0x51, 0x52, + 0x53, 0x54}; + +/*gest_event mapping to gest_key_def*/ + uint16_t gest_key_def[GEST_SUP_NUM] = { + KEY_POWER, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275}; + +uint8_t *wake_event_buffer; +#endif + +#ifdef HIMAX_PALM_SENSOR_EN +static struct sensors_classdev __maybe_unused palm_sensors_touch_cdev = { + .name = "palm-gesture", + .vendor = "Himax", + .version = 1, + .type = SENSOR_TYPE_MOTO_TOUCH_PALM, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +static void himax_palm_detect_func(struct himax_ts_data *ts, bool detected); +#endif + +#ifdef HIMAX_V2_SENSOR_EN +static struct sensors_classdev __maybe_unused sensors_touch_cdev = { + .name = "dt-gesture", + .vendor = "Himax", + .version = 1, + .type = SENSOR_TYPE_MOTO_DOUBLE_TAP, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + /* WAKE_UP & SPECIAL_REPORT */ + .flags = 1 | 6, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#define REPORT_MAX_COUNT 10000 +#endif + +#define SUPPORT_FINGER_DATA_CHECKSUM 0x0F +#define TS_WAKE_LOCK_TIMEOUT (5000) +#define FRAME_COUNT 5 + +#ifdef HX_TP_PROC_GUEST_INFO +struct hx_guest_info *g_guest_info_data; +EXPORT_SYMBOL(g_guest_info_data); + +char *g_guest_info_item[] = { + "projectID", + "CGColor", + "BarCode", + "Reserve1", + "Reserve2", + "Reserve3", + "Reserve4", + "Reserve5", + "VCOM", + "Vcom-3Gar", + NULL +}; +#endif + +uint32_t g_hx_chip_inited; + +#if defined(__EMBEDDED_FW__) +struct firmware g_embedded_fw = { + .data = _binary___Himax_firmware_bin_start, +}; +#endif + +#define HIMAX_FILE_NAME_LENGTH 128 + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) +char *i_CTPM_firmware_name; +#else +char *i_CTPM_firmware_name = "Himax_firmware.bin"; +#endif +bool g_auto_update_flag; +#endif +#if defined(HX_AUTO_UPDATE_FW) +unsigned char *i_CTPM_FW; +int i_CTPM_FW_len; +int g_i_FW_VER; +int g_i_CFG_VER; +int g_i_CID_MAJ; /*GUEST ID*/ +int g_i_CID_MIN; /*VER for GUEST*/ +#endif +#ifdef HX_ZERO_FLASH +int g_f_0f_updat; +#endif + +struct himax_ts_data *private_ts; +EXPORT_SYMBOL(private_ts); + +struct himax_ic_data *ic_data; +EXPORT_SYMBOL(ic_data); + +struct himax_report_data *hx_touch_data; +EXPORT_SYMBOL(hx_touch_data); + +struct himax_core_fp g_core_fp; +EXPORT_SYMBOL(g_core_fp); + +struct himax_debug *debug_data; +EXPORT_SYMBOL(debug_data); + +struct proc_dir_entry *himax_touch_proc_dir; +EXPORT_SYMBOL(himax_touch_proc_dir); + +struct himax_chip_detect *g_core_chip_dt; +EXPORT_SYMBOL(g_core_chip_dt); + +int g_mmi_refcnt; +EXPORT_SYMBOL(g_mmi_refcnt); + +#define HIMAX_PROC_TOUCH_FOLDER "android_touch" +/*ts_work about start*/ +struct himax_target_report_data *g_target_report_data; +EXPORT_SYMBOL(g_target_report_data); + +static void himax_report_all_leave_event(struct himax_ts_data *ts); +/*ts_work about end*/ +static int HX_TOUCH_INFO_POINT_CNT; + +struct filename* (*kp_getname_kernel)(const char *filename); +struct file* (*kp_file_open_name)(struct filename *name, int flags, umode_t mode); + +unsigned long FW_VER_MAJ_FLASH_ADDR; +EXPORT_SYMBOL(FW_VER_MAJ_FLASH_ADDR); + +unsigned long FW_VER_MIN_FLASH_ADDR; +EXPORT_SYMBOL(FW_VER_MIN_FLASH_ADDR); + +unsigned long CFG_VER_MAJ_FLASH_ADDR; +EXPORT_SYMBOL(CFG_VER_MAJ_FLASH_ADDR); + +unsigned long CFG_VER_MIN_FLASH_ADDR; +EXPORT_SYMBOL(CFG_VER_MIN_FLASH_ADDR); + +unsigned long CID_VER_MAJ_FLASH_ADDR; +EXPORT_SYMBOL(CID_VER_MAJ_FLASH_ADDR); + +unsigned long CID_VER_MIN_FLASH_ADDR; +EXPORT_SYMBOL(CID_VER_MIN_FLASH_ADDR); +/*unsigned long PANEL_VERSION_ADDR;*/ + +unsigned long FW_VER_MAJ_FLASH_LENG; +EXPORT_SYMBOL(FW_VER_MAJ_FLASH_LENG); + +unsigned long FW_VER_MIN_FLASH_LENG; +EXPORT_SYMBOL(FW_VER_MIN_FLASH_LENG); + +unsigned long CFG_VER_MAJ_FLASH_LENG; +EXPORT_SYMBOL(CFG_VER_MAJ_FLASH_LENG); + +unsigned long CFG_VER_MIN_FLASH_LENG; +EXPORT_SYMBOL(CFG_VER_MIN_FLASH_LENG); + +unsigned long CID_VER_MAJ_FLASH_LENG; +EXPORT_SYMBOL(CID_VER_MAJ_FLASH_LENG); + +unsigned long CID_VER_MIN_FLASH_LENG; +EXPORT_SYMBOL(CID_VER_MIN_FLASH_LENG); +/*unsigned long PANEL_VERSION_LENG;*/ + +unsigned long FW_CFG_VER_FLASH_ADDR; + + +unsigned char IC_CHECKSUM; +EXPORT_SYMBOL(IC_CHECKSUM); + +#ifdef HX_ESD_RECOVERY +u8 HX_ESD_RESET_ACTIVATE; +EXPORT_SYMBOL(HX_ESD_RESET_ACTIVATE); + +int hx_EB_event_flag; +EXPORT_SYMBOL(hx_EB_event_flag); + +int hx_EC_event_flag; +EXPORT_SYMBOL(hx_EC_event_flag); + +int hx_ED_event_flag; +EXPORT_SYMBOL(hx_ED_event_flag); + +int g_zero_event_count; + +#endif + +u8 HX_HW_RESET_ACTIVATE; + +static uint8_t AA_press; +static uint8_t EN_NoiseFilter; +static uint8_t Last_EN_NoiseFilter; + +static int p_point_num = 0xFFFF; +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) +static uint8_t vk_press; +static int tpd_key; +static int tpd_key_old; +#endif +static int probe_fail_flag; +#ifdef HX_USB_DETECT_GLOBAL + bool USB_detect_flag; +#endif + +#if defined(CONFIG_DRM) +int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); +#elif defined(CONFIG_FB) +int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void himax_ts_early_suspend(struct early_suspend *h); +static void himax_ts_late_resume(struct early_suspend *h); +#endif + +#ifdef HIMAX_CONFIG_PANEL_NOTIFICATIONS +int panel_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); +#endif + +#ifdef HX_GESTURE_TRACK + static int gest_pt_cnt; + static int gest_pt_x[GEST_PT_MAX_NUM]; + static int gest_pt_y[GEST_PT_MAX_NUM]; + static int gest_start_x, gest_start_y, gest_end_x, gest_end_y; + static int gest_width, gest_height, gest_mid_x, gest_mid_y; + static int hx_gesture_coor[16]; +#endif + +int g_ts_dbg; +EXPORT_SYMBOL(g_ts_dbg); + +/* File node for Selftest, SMWP and HSEN - Start*/ +#define HIMAX_PROC_SELF_TEST_FILE "self_test" +struct proc_dir_entry *himax_proc_self_test_file; + +#define HIMAX_PROC_SELF_TEST_RAW_FILE "self_test_rawdata" +struct proc_dir_entry *himax_proc_self_raw_file = NULL; + +uint8_t HX_PROC_SEND_FLAG; +EXPORT_SYMBOL(HX_PROC_SEND_FLAG); + +#ifdef HX_SMART_WAKEUP + #define HIMAX_PROC_SMWP_FILE "SMWP" + struct proc_dir_entry *himax_proc_SMWP_file = NULL; + #define HIMAX_PROC_GESTURE_FILE "GESTURE" + struct proc_dir_entry *himax_proc_GESTURE_file = NULL; + uint8_t HX_SMWP_EN; +#ifdef HX_P_SENSOR + #define HIMAX_PROC_PSENSOR_FILE "Psensor" + struct proc_dir_entry *himax_proc_psensor_file = NULL; +#endif +#endif + +#ifdef HX_EDGE_LIMIT + #define HIMAX_PROC_EDGE_LIMIT_FILE "edge_en" + struct proc_dir_entry *himax_proc_edge_limit_file = NULL; +#endif + +#if defined(HX_SMART_WAKEUP) || defined(CONFIG_TOUCHSCREEN_HIMAX_INSPECT) +bool FAKE_POWER_KEY_SEND = true; +#endif + +#ifdef HX_HIGH_SENSE + #define HIMAX_PROC_HSEN_FILE "HSEN" + struct proc_dir_entry *himax_proc_HSEN_file = NULL; +#endif + +#if defined(HX_PALM_REPORT) +static int himax_palm_detect(uint8_t *buf) +{ + struct himax_ts_data *ts = private_ts; + int32_t loop_i; + int base = 0; + int x = 0, y = 0, w = 0; + + loop_i = 0; + base = loop_i * 4; + x = buf[base] << 8 | buf[base + 1]; + y = (buf[base + 2] << 8 | buf[base + 3]); + w = buf[(ts->nFinger_support * 4) + loop_i]; +#ifndef HIMAX_PALM_SENSOR_EN + I(" %s HX_PALM_REPORT_loopi=%d,base=%x,X=%x,Y=%x,W=%x\n", __func__, loop_i, base, x, y, w); + if ((!atomic_read(&ts->suspend_mode)) && (x == 0xFA5A) && (y == 0xFA5A) && (w == 0x00)) + return PALM_REPORT; + else + return NOT_REPORT; +#else + if ((x == 0xFA5A) && (y == 0xFA5A)) { + I("%s: HX_PALM_REPORT loop_i=%d base=%x X=%x, Y=%x, W=%x\n", + __func__, loop_i, base, x, y, w); + return PALM_REPORT; + } else if ((x == 0xFC5C) && (y == 0xFC5C)) { + I("%s: HX_PALM_REPORT loop_i=%d base=%x X=%x, Y=%x, W=%x\n", + __func__, loop_i, base, x, y, w); + return PALM_LEAVE_REPORT; + } else + return NOT_REPORT; +#endif +} +#endif + +static ssize_t himax_self_test_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + int i = 0; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) { + return -EFAULT; + } + + if (hx_self_test_file_name == NULL) { + E("file name is NULL\n"); + hx_self_test_file_name = kzalloc(80, GFP_KERNEL); + snprintf(hx_self_test_file_name, 15, "hx_criteria.csv"); + } + + for (i = 0; i < 80; i++) { + if (buf[i] == ',' || buf[i] == '\n') { + memset(hx_self_test_file_name, 0x0, 80); + memcpy(hx_self_test_file_name, buf, i); + I("%s: Get name from Customer\n", __func__); + break; + } + } + if (i == 80) { + memset(hx_self_test_file_name, 0x0, 80); + snprintf(hx_self_test_file_name, 16, "hx_criteria.csv"); + I("%s: Use default name\n", __func__); + } + I("file name = %s\n", hx_self_test_file_name); + + return len; +} + +static ssize_t himax_self_test_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + int val = 0x00; + size_t ret = 0; + char *temp_buf; + I("%s: enter, %d \n", __func__, __LINE__); + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + himax_int_enable(0);/* disable irq */ + private_ts->in_self_test = 1; + val = g_core_fp.fp_chip_self_test(); +#ifdef HX_ESD_RECOVERY + HX_ESD_RESET_ACTIVATE = 1; +#endif + himax_int_enable(1);/* enable irq */ + + if (val == 0x00) { + ret += snprintf(temp_buf + ret, len - ret, "Self_Test Pass\n"); + } else { + ret += snprintf(temp_buf + ret, len - ret, "Self_Test Fail\n"); + } + + private_ts->in_self_test = 0; + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static struct file_operations himax_proc_self_test_ops = { + .owner = THIS_MODULE, + .read = himax_self_test_read, + .write = himax_self_test_write, +}; + +static void *himax_self_raw_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) { + return NULL; + } + return (void *)((unsigned long) *pos + 1); +} + +static void *himax_self_raw_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_self_raw_seq_stop(struct seq_file *s, void *v) +{ +} + +static int himax_self_raw_seq_read(struct seq_file *s, void *v) +{ + size_t ret = 0; + + if (g_rslt_data != NULL) { + seq_printf(s, "%s", g_rslt_data); + } + return ret; +} +static struct seq_operations himax_self_raw_seq_ops = { + .start = himax_self_raw_seq_start, + .next = himax_self_raw_seq_next, + .stop = himax_self_raw_seq_stop, + .show = himax_self_raw_seq_read, +}; +static int himax_self_raw_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_self_raw_seq_ops); +}; + +static struct file_operations himax_proc_self_raw_ops = { + .owner = THIS_MODULE, + .open = himax_self_raw_proc_open, + .read = seq_read, +}; + +#if 0 +static ssize_t himax_self_test(struct seq_file *s, void *v) +{ + int val = 0x00; + size_t ret = 0; + + I("%s: enter, %d\n", __func__, __LINE__); + + if (private_ts->suspended == 1) { + E("%s: please do self test in normal active mode\n", __func__); + return HX_INIT_FAIL; + } + + himax_int_enable(0);/* disable irq */ + + private_ts->in_self_test = 1; + + val = g_core_fp.fp_chip_self_test(); +/* + *#ifdef HX_ESD_RECOVERY + * HX_ESD_RESET_ACTIVATE = 1; + *#endif + * himax_int_enable(1); //enable irq + */ + if (val == HX_INSPECT_OK) + seq_puts(s, "Self_Test Pass:\n"); + else + seq_puts(s, "Self_Test Fail:\n"); + + /*Print_Self_Test_Info(val,temp_buf);*/ + if ((val & HX_INSPECT_EFILE) == HX_INSPECT_EFILE) { + seq_puts(s, " Get criteria File Fail\n"); + goto END_FUNC; + } else if ((val & HX_INSPECT_MEMALLCTFAIL) == HX_INSPECT_MEMALLCTFAIL) { + seq_puts(s, " Allocate memory Fail\n"); + goto END_FUNC; + } else { + seq_puts(s, " Get criteria File OK\n"); + } + if ((val & HX_INSPECT_EGETRAW) == HX_INSPECT_EGETRAW) { + seq_puts(s, " Get raw data Fail\n"); + goto END_FUNC; + } else { + seq_puts(s, " Get raw data OK\n"); + } +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + if (g_inspt_crtra_flag[IDX_RAWMIN] == 1 && g_inspt_crtra_flag[IDX_RAWMAX] == 1) { + if ((val & HX_INSPECT_ERAW) == HX_INSPECT_ERAW) + seq_puts(s, " Check Raw data Fail\n"); + else + seq_puts(s, " Check Raw data OK\n"); + } + if (g_inspt_crtra_flag[IDX_WT_NOISEMAX] == 1 && g_inspt_crtra_flag[IDX_WT_NOISEMIN] == 1) { + if ((val & HX_INSPECT_WT_ENOISE) == HX_INSPECT_WT_ENOISE) + seq_puts(s, " Check Noise(weight) Fail\n"); + else + seq_puts(s, " Check Noise(weight) OK\n"); + } + if (g_inspt_crtra_flag[IDX_ABS_NOISEMAX] == 1 && g_inspt_crtra_flag[IDX_ABS_NOISEMIN] == 1) { + if ((val & HX_INSPECT_ABS_ENOISE) == HX_INSPECT_ABS_ENOISE) + seq_puts(s, " Check Noise(absolute) Fail\n"); + else + seq_puts(s, " Check Noise(absolute) OK\n"); + } + if (g_inspt_crtra_flag[IDX_OPENMIN] == 1 && g_inspt_crtra_flag[IDX_OPENMAX] == 1) { + if ((val & HX_INSPECT_EOPEN) == HX_INSPECT_EOPEN) + seq_puts(s, " Check Sensor open Fail\n"); + else + seq_puts(s, " Check Sensor open OK\n"); + } + if (g_inspt_crtra_flag[IDX_M_OPENMIN] == 1 && g_inspt_crtra_flag[IDX_M_OPENMAX] == 1) { + if ((val & HX_INSPECT_EMOPEN) == HX_INSPECT_EMOPEN) + seq_puts(s, " Check Sensor micro open Fail\n"); + else + seq_puts(s, " Check Sensor micro open OK\n"); + } + if (g_inspt_crtra_flag[IDX_SORTMIN] == 1 && g_inspt_crtra_flag[IDX_SORTMAX] == 1) { + if ((val & HX_INSPECT_ESHORT) == HX_INSPECT_ESHORT) + seq_puts(s, " Check Sensor short Fail\n"); + else + seq_puts(s, " Check Sensor short OK\n"); + } + if ((g_inspt_crtra_flag[IDX_GAP_HOR_RAWMIN] == 1 && g_inspt_crtra_flag[IDX_GAP_HOR_RAWMAX] == 1) || + (g_inspt_crtra_flag[IDX_GAP_VER_RAWMIN] == 1 && g_inspt_crtra_flag[IDX_GAP_VER_RAWMAX] == 1)) { + if ((val & HX_INSPECT_EGAP_RAW) == HX_INSPECT_EGAP_RAW) + seq_puts(s, " Check Raw Data GAP Fail\n"); + else + seq_puts(s, " Check Raw Data GAP OK\n"); + } + if (g_inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MIN] == 1 && g_inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MAX] == 1) { + if ((val & HX_INSPECT_EACT_IDLE_RAW) == HX_INSPECT_EACT_IDLE_RAW) + seq_puts(s, " Check ACT_IDLE RAW Fail\n"); + else + seq_puts(s, " Check ACT_IDLE RAW OK\n"); + } + if (g_inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MIN] == 1 && g_inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MAX] == 1) { + if ((val & HX_INSPECT_EACT_IDLE_NOISE) == HX_INSPECT_EACT_IDLE_NOISE) + seq_puts(s, " Check ACT_IDLE NOISE Fail\n"); + else + seq_puts(s, " Check ACT_IDLE NOISE OK\n"); + } + if (g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MIN] == 1 && g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MAX] == 1) { + if ((val & HX_INSPECT_ELPWUG_RAW) == HX_INSPECT_ELPWUG_RAW) + seq_puts(s, " Check LPWUG RAW Fail\n"); + else + seq_puts(s, " Check LPWUG RAW OK\n"); + } + if (g_inspt_crtra_flag[IDX_LPWUG_NOISE_MIN] == 1 && g_inspt_crtra_flag[IDX_LPWUG_NOISE_MAX] == 1) { + if ((val & HX_INSPECT_ELPWUG_NOISE) == HX_INSPECT_ELPWUG_NOISE) + seq_puts(s, " Check LPWUG NOISE Fail\n"); + else + seq_puts(s, " Check LPWUG NOISE OK\n"); + } + if (g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MIN] == 1 && g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MAX] == 1) { + if ((val & HX_INSPECT_ELPWUG_IDLE_RAW) == HX_INSPECT_ELPWUG_IDLE_RAW) + seq_puts(s, " Check LPWUG IDLE RAW Fail\n"); + else + seq_puts(s, " Check LPWUG IDLE RAW OK\n"); + } + if (g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MIN] == 1 && g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MAX] == 1) { + if ((val & HX_INSPECT_ELPWUG_IDLE_NOISE) == HX_INSPECT_ELPWUG_IDLE_NOISE) + seq_puts(s, " Check LPWUG IDLE NOISE Fail\n"); + else + seq_puts(s, " Check LPWUG IDLE NOISE OK\n"); + } +#endif + +END_FUNC: + private_ts->in_self_test = 0; + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + if (g_inspt_crtra_flag != NULL) { + kfree(g_inspt_crtra_flag); + g_inspt_crtra_flag = NULL; + } +#endif + +#ifdef HX_ESD_RECOVERY + HX_ESD_RESET_ACTIVATE = 1; +#endif + himax_int_enable(1);/* enable irq */ + + return ret; +} + +static void *himax_self_test_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) + return NULL; + + + return (void *)((unsigned long) *pos + 1); +} + +static void *himax_self_test_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_self_test_seq_stop(struct seq_file *s, void *v) +{ +} + +static ssize_t himax_self_test_write(struct file *filp, const char __user *buff, + size_t len, loff_t *data) +{ + char buf[80]; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + if (buf[0] == 'r') { + chip_test_r_flag = true; + I("%s: Start to read chip test data.\n", __func__); + } else { + chip_test_r_flag = false; + I("%s: Back to do self test.\n", __func__); + } + + return len; +} + +static int himax_self_test_seq_read(struct seq_file *s, void *v) +{ + size_t ret = 0; + + if (chip_test_r_flag) { +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + if (g_rslt_data) + seq_printf(s, "%s", g_rslt_data); + else +#endif + seq_puts(s, "No chip test data.\n"); + } else { + himax_self_test(s, v); + } + + return ret; +} + +static const struct seq_operations himax_self_test_seq_ops = { + .start = himax_self_test_seq_start, + .next = himax_self_test_seq_next, + .stop = himax_self_test_seq_stop, + .show = himax_self_test_seq_read, +}; + +static int himax_self_test_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_self_test_seq_ops); +}; + +static const struct file_operations himax_proc_self_test_ops = { + .owner = THIS_MODULE, + .open = himax_self_test_proc_open, + .read = seq_read, + .write = himax_self_test_write, + .release = seq_release, +}; +#endif + +#ifdef HX_HIGH_SENSE +static ssize_t himax_HSEN_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + size_t count = 0; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kcalloc(len, sizeof(char), GFP_KERNEL); + count = snprintf(temp_buf, PAGE_SIZE, "%d\n", ts->HSEN_enable); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return count; +} + +static ssize_t himax_HSEN_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + if (buf[0] == '0') + ts->HSEN_enable = 0; + else if (buf[0] == '1') + ts->HSEN_enable = 1; + else + return -EINVAL; + + g_core_fp.fp_set_HSEN_enable(ts->HSEN_enable, ts->suspended); + I("%s: HSEN_enable = %d.\n", __func__, ts->HSEN_enable); + return len; +} + +static const struct file_operations himax_proc_HSEN_ops = { + .owner = THIS_MODULE, + .read = himax_HSEN_read, + .write = himax_HSEN_write, +}; +#endif + +#ifdef HX_SMART_WAKEUP +static ssize_t himax_SMWP_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + size_t count = 0; + struct himax_ts_data *ts = private_ts; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kcalloc(len, sizeof(char), GFP_KERNEL); + count = snprintf(temp_buf, PAGE_SIZE, "%d\n", ts->SMWP_enable); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return count; +} + +static ssize_t himax_SMWP_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + if (buf[0] == '0') + ts->SMWP_enable = 0; + else if (buf[0] == '1') + ts->SMWP_enable = 1; + else + return -EINVAL; + + g_core_fp.fp_set_SMWP_enable(ts->SMWP_enable, ts->suspended); + HX_SMWP_EN = ts->SMWP_enable; + I("%s: SMART_WAKEUP_enable = %d.\n", __func__, HX_SMWP_EN); + return len; +} + +static const struct file_operations himax_proc_SMWP_ops = { + .owner = THIS_MODULE, + .read = himax_SMWP_read, + .write = himax_SMWP_write, +}; + +static ssize_t himax_GESTURE_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + int i = 0; + size_t ret = 0; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kcalloc(len, sizeof(char), GFP_KERNEL); + + for (i = 0; i < GEST_SUP_NUM; i++) + ret += snprintf(temp_buf + ret, len - ret, "ges_en[%d]=%d\n", i, ts->gesture_cust_en[i]); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + ret = 0; + } + + return ret; +} + +static ssize_t himax_GESTURE_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + int i = 0; + int j = 0; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + I("himax_GESTURE_store= %s, len = %d\n", buf, (int)len); + + for (i = 0; i < len; i++) { + if (buf[i] == '0' && j < GEST_SUP_NUM) { + ts->gesture_cust_en[j] = 0; + I("gesture en[%d]=%d\n", j, ts->gesture_cust_en[j]); + j++; + } else if (buf[i] == '1' && j < GEST_SUP_NUM) { + ts->gesture_cust_en[j] = 1; + I("gesture en[%d]=%d\n", j, ts->gesture_cust_en[j]); + j++; + } else + I("Not 0/1 or >=GEST_SUP_NUM : buf[%d] = %c\n", i, buf[i]); + } + + return len; +} + +static const struct file_operations himax_proc_Gesture_ops = { + .owner = THIS_MODULE, + .read = himax_GESTURE_read, + .write = himax_GESTURE_write, +}; + +#ifdef HX_P_SENSOR +static ssize_t himax_psensor_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + size_t count = 0; + struct himax_ts_data *ts = private_ts; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kcalloc(len, sizeof(char), GFP_KERNEL); + count = snprintf(temp_buf, PAGE_SIZE, "p-sensor flag = %d\n", ts->psensor_flag); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return count; +} + +static ssize_t himax_psensor_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + if (buf[0] == '0' && ts->SMWP_enable == 1) { + ts->psensor_flag = false; + g_core_fp.fp_black_gest_ctrl(false); + } else if (buf[0] == '1' && ts->SMWP_enable == 1) { + ts->psensor_flag = true; + g_core_fp.fp_black_gest_ctrl(true); + } else if (ts->SMWP_enable == 0) { + I("%s: SMWP is disable, not supprot to ctrl p-sensor.\n", __func__); + } else + return -EINVAL; + + I("%s: psensor_flag = %d.\n", __func__, ts->psensor_flag); + return len; +} + +static const struct file_operations himax_proc_psensor_ops = { + .owner = THIS_MODULE, + .read = himax_psensor_read, + .write = himax_psensor_write, +}; +#endif +#endif + +#ifdef HX_EDGE_LIMIT +static ssize_t himax_edge_limit_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + size_t count = 0; + struct himax_ts_data *ts = private_ts; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + count = snprintf(temp_buf, PAGE_SIZE, "%d\n", ts->edge_limit_enable); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return count; +} + +static ssize_t himax_edge_limit_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) { + return -EFAULT; + } + + if (buf[0] == '0') + ts->edge_limit_enable = 0; + else if (buf[0] == '1') + ts->edge_limit_enable = 1;/*Portrait*/ + else if (buf[0] == '2') + ts->edge_limit_enable = 2;/*Landscape-Right*/ + else if (buf[0] == '3') + ts->edge_limit_enable = 3;/*Landscape-Left*/ + else + return -EINVAL; + + g_core_fp.fp_set_edge_limit_enable(ts->edge_limit_enable, ts->suspended); + I("%s: edge_limit_enable = %d.\n", __func__, ts->edge_limit_enable); + return len; +} + +static struct file_operations himax_proc_edge_limit_ops = { + .owner = THIS_MODULE, + .read = himax_edge_limit_read, + .write = himax_edge_limit_write, +}; +#endif + +int himax_common_proc_init(void) +{ + himax_touch_proc_dir = proc_mkdir(HIMAX_PROC_TOUCH_FOLDER, NULL); + + if (himax_touch_proc_dir == NULL) { + E(" %s: himax_touch_proc_dir file create failed!\n", __func__); + return -ENOMEM; + } +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + if (fp_himax_self_test_init != NULL) + fp_himax_self_test_init(); +#endif + + himax_proc_self_test_file = proc_create(HIMAX_PROC_SELF_TEST_FILE, 0664, himax_touch_proc_dir, &himax_proc_self_test_ops); + if (himax_proc_self_test_file == NULL) { + E(" %s: proc self_test file create failed!\n", __func__); + goto fail_1; + } + + himax_proc_self_raw_file = proc_create(HIMAX_PROC_SELF_TEST_RAW_FILE, 0444, himax_touch_proc_dir, &himax_proc_self_raw_ops); + if (himax_proc_self_raw_file == NULL) { + E(" %s: proc self_test raw file create failed!\n", __func__); + goto fail_1_1; + } + +#ifdef HX_HIGH_SENSE + himax_proc_HSEN_file = proc_create(HIMAX_PROC_HSEN_FILE, 0666, + himax_touch_proc_dir, &himax_proc_HSEN_ops); + + if (himax_proc_HSEN_file == NULL) { + E(" %s: proc HSEN file create failed!\n", __func__); + goto fail_2; + } + +#endif +#ifdef HX_SMART_WAKEUP + himax_proc_SMWP_file = proc_create(HIMAX_PROC_SMWP_FILE, 0666, + himax_touch_proc_dir, &himax_proc_SMWP_ops); + + if (himax_proc_SMWP_file == NULL) { + E(" %s: proc SMWP file create failed!\n", __func__); + goto fail_3; + } + + himax_proc_GESTURE_file = proc_create(HIMAX_PROC_GESTURE_FILE, 0666, + himax_touch_proc_dir, &himax_proc_Gesture_ops); + + if (himax_proc_GESTURE_file == NULL) { + E(" %s: proc GESTURE file create failed!\n", __func__); + goto fail_4; + } +#ifdef HX_P_SENSOR + himax_proc_psensor_file = proc_create(HIMAX_PROC_PSENSOR_FILE, 0666, + himax_touch_proc_dir, &himax_proc_psensor_ops); + + if (himax_proc_psensor_file == NULL) { + E(" %s: proc GESTURE file create failed!\n", __func__); + goto fail_5; + } +#endif +#endif +#ifdef HX_EDGE_LIMIT + himax_proc_edge_limit_file = proc_create(HIMAX_PROC_EDGE_LIMIT_FILE, 0666, + himax_touch_proc_dir, &himax_proc_edge_limit_ops); + + if (himax_proc_edge_limit_file == NULL) { + E(" %s: proc edge limit file create failed!\n", __func__); + goto fail_6; + } +#endif + return 0; +#ifdef HX_EDGE_LIMIT + remove_proc_entry(HIMAX_PROC_EDGE_LIMIT_FILE, himax_touch_proc_dir); +fail_6: +#endif +#ifdef HX_SMART_WAKEUP +#ifdef HX_P_SENSOR + remove_proc_entry(HIMAX_PROC_PSENSOR_FILE, himax_touch_proc_dir); +fail_5: +#endif + remove_proc_entry(HIMAX_PROC_GESTURE_FILE, himax_touch_proc_dir); +fail_4: + remove_proc_entry(HIMAX_PROC_SMWP_FILE, himax_touch_proc_dir); +fail_3: +#endif +#ifdef HX_HIGH_SENSE + remove_proc_entry(HIMAX_PROC_HSEN_FILE, himax_touch_proc_dir); +fail_2: +#endif + remove_proc_entry(HIMAX_PROC_SELF_TEST_RAW_FILE, himax_touch_proc_dir); +fail_1_1: + remove_proc_entry(HIMAX_PROC_SELF_TEST_FILE, himax_touch_proc_dir); +fail_1: + return -ENOMEM; +} + +void himax_common_proc_deinit(void) +{ +remove_proc_entry(HIMAX_PROC_SELF_TEST_FILE, himax_touch_proc_dir); +remove_proc_entry(HIMAX_PROC_SELF_TEST_RAW_FILE, himax_touch_proc_dir); + +#ifdef HX_EDGE_LIMIT + remove_proc_entry(HIMAX_PROC_EDGE_LIMIT_FILE, himax_touch_proc_dir); +#endif +#ifdef HX_HIGH_SENSE + remove_proc_entry(HIMAX_PROC_HSEN_FILE, himax_touch_proc_dir); +#endif +#ifdef HX_SMART_WAKEUP +#ifdef HX_P_SENSOR + remove_proc_entry(HIMAX_PROC_PSENSOR_FILE, himax_touch_proc_dir); +#endif + remove_proc_entry(HIMAX_PROC_GESTURE_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_SMWP_FILE, himax_touch_proc_dir); +#endif + remove_proc_entry(HIMAX_PROC_TOUCH_FOLDER, NULL); +} + +/* File node for SMWP and HSEN - End*/ + +int himax_input_register(struct himax_ts_data *ts) +{ + int ret = 0; +#if defined(HX_SMART_WAKEUP) + int i = 0; +#endif + ret = himax_dev_set(ts); + + if (ret < 0) { + I("%s, input device register fail!\n", __func__); + ret = INPUT_REGISTER_FAIL; + goto input_device_fail; + } + + set_bit(EV_SYN, ts->input_dev->evbit); + set_bit(EV_ABS, ts->input_dev->evbit); + set_bit(EV_KEY, ts->input_dev->evbit); +#if defined(HX_PLATFOME_DEFINE_KEY) + himax_platform_key(); +#else + set_bit(KEY_BACK, ts->input_dev->keybit); + set_bit(KEY_HOME, ts->input_dev->keybit); + set_bit(KEY_MENU, ts->input_dev->keybit); + set_bit(KEY_SEARCH, ts->input_dev->keybit); +#endif +#if defined(HX_SMART_WAKEUP) || defined(HX_PALM_REPORT) + set_bit(KEY_POWER, ts->input_dev->keybit); +#endif +#if defined(HX_SMART_WAKEUP) + for (i = 1; i < GEST_SUP_NUM; i++) + set_bit(gest_key_def[i], ts->input_dev->keybit); + +#endif + set_bit(BTN_TOUCH, ts->input_dev->keybit); + set_bit(KEY_APPSELECT, ts->input_dev->keybit); + set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); +#ifdef HX_PROTOCOL_A + /*ts->input_dev->mtsize = ts->nFinger_support;*/ + input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, 3, 0, 0); +#else + set_bit(MT_TOOL_FINGER, ts->input_dev->keybit); +#if defined(HX_PROTOCOL_B_3PA) + input_mt_init_slots(ts->input_dev, ts->nFinger_support, INPUT_MT_DIRECT); +#else + input_mt_init_slots(ts->input_dev, ts->nFinger_support); +#endif +#endif + I("input_set_abs_params: mix_x %d, max_x %d, min_y %d, max_y %d\n", + ts->pdata->abs_x_min, ts->pdata->abs_x_max, ts->pdata->abs_y_min, ts->pdata->abs_y_max); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, ts->pdata->abs_x_min, ts->pdata->abs_x_max, ts->pdata->abs_x_fuzz, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, ts->pdata->abs_y_min, ts->pdata->abs_y_max, ts->pdata->abs_y_fuzz, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, ts->pdata->abs_pressure_min, ts->pdata->abs_pressure_max, ts->pdata->abs_pressure_fuzz, 0); +#ifndef HX_PROTOCOL_A + input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, ts->pdata->abs_pressure_min, ts->pdata->abs_pressure_max, ts->pdata->abs_pressure_fuzz, 0); + input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, ts->pdata->abs_width_min, ts->pdata->abs_width_max, ts->pdata->abs_pressure_fuzz, 0); +#endif +/* input_set_abs_params(ts->input_dev, ABS_MT_AMPLITUDE, 0, ((ts->pdata->abs_pressure_max << 16) | ts->pdata->abs_width_max), 0, 0); + * input_set_abs_params(ts->input_dev, ABS_MT_POSITION, 0, (BIT(31) | (ts->pdata->abs_x_max << 16) | ts->pdata->abs_y_max), 0, 0); + */ + + if (himax_input_register_device(ts->input_dev) == 0) + ret = NO_ERR; + else + ret = INPUT_REGISTER_FAIL; + +#if defined(HX_PEN_FUNC_EN) + set_bit(EV_SYN, ts->hx_pen_dev->evbit); + set_bit(EV_ABS, ts->hx_pen_dev->evbit); + set_bit(EV_KEY, ts->hx_pen_dev->evbit); + set_bit(BTN_TOUCH, ts->hx_pen_dev->keybit); + set_bit(INPUT_PROP_DIRECT, ts->hx_pen_dev->propbit); + + set_bit(BTN_TOOL_PEN, ts->hx_pen_dev->keybit); + set_bit(BTN_TOOL_RUBBER, ts->hx_pen_dev->keybit); + + input_set_abs_params(ts->hx_pen_dev, ABS_PRESSURE, 0, 4095, 0, 0); + input_set_abs_params(ts->hx_pen_dev, ABS_DISTANCE, 0, 1, 0, 0); + input_set_abs_params(ts->hx_pen_dev, ABS_TILT_X, -60, 60, 0, 0); + input_set_abs_params(ts->hx_pen_dev, ABS_TILT_Y, -60, 60, 0, 0); + /*input_set_capability(ts->hx_pen_dev, EV_SW, SW_PEN_INSERT);*/ + input_set_capability(ts->hx_pen_dev, EV_KEY, BTN_TOUCH); + input_set_capability(ts->hx_pen_dev, EV_KEY, BTN_STYLUS); + + input_set_abs_params(ts->hx_pen_dev, ABS_X, ts->pdata->abs_x_min, ts->pdata->abs_x_max, ts->pdata->abs_x_fuzz, 0); + input_set_abs_params(ts->hx_pen_dev, ABS_Y, ts->pdata->abs_y_min, ts->pdata->abs_y_max, ts->pdata->abs_y_fuzz, 0); + + if (himax_input_register_device(ts->hx_pen_dev) == 0) + ret = NO_ERR; + else + ret = INPUT_REGISTER_FAIL; + +#endif + I("%s, input device registered.\n", __func__); + +input_device_fail: + return ret; +} +EXPORT_SYMBOL(himax_input_register); + +static void calcDataSize(void) +{ + struct himax_ts_data *ts_data = private_ts; + + ts_data->x_channel = ic_data->HX_RX_NUM; + ts_data->y_channel = ic_data->HX_TX_NUM; + ts_data->nFinger_support = ic_data->HX_MAX_PT; + + ts_data->coord_data_size = 4 * ts_data->nFinger_support; + ts_data->area_data_size = ((ts_data->nFinger_support / 4) + (ts_data->nFinger_support % 4 ? 1 : 0)) * 4; + ts_data->coordInfoSize = ts_data->coord_data_size + ts_data->area_data_size + 4; + ts_data->raw_data_frame_size = 128 - ts_data->coord_data_size - ts_data->area_data_size - 4 - 4 - 1; + + if (ts_data->raw_data_frame_size == 0) { + E("%s: could NOT calculate!\n", __func__); + return; + } + + ts_data->raw_data_nframes = ((uint32_t)ts_data->x_channel * ts_data->y_channel + + ts_data->x_channel + ts_data->y_channel) / ts_data->raw_data_frame_size + + (((uint32_t)ts_data->x_channel * ts_data->y_channel + + ts_data->x_channel + ts_data->y_channel) % ts_data->raw_data_frame_size) ? 1 : 0; + I("%s: coord_data_size: %d, area_data_size:%d, raw_data_frame_size:%d, raw_data_nframes:%d\n", __func__, ts_data->coord_data_size, ts_data->area_data_size, ts_data->raw_data_frame_size, ts_data->raw_data_nframes); +} + +static void calculate_point_number(void) +{ + HX_TOUCH_INFO_POINT_CNT = ic_data->HX_MAX_PT * 4; + + if ((ic_data->HX_MAX_PT % 4) == 0) + HX_TOUCH_INFO_POINT_CNT += (ic_data->HX_MAX_PT / 4) * 4; + else + HX_TOUCH_INFO_POINT_CNT += ((ic_data->HX_MAX_PT / 4) + 1) * 4; +} +#if defined(HX_AUTO_UPDATE_FW) +static int himax_auto_update_check(void) +{ + int32_t ret; + int flag_sys_ex_is_same = 0; + + I("%s:Entering!\n", __func__); + if (g_core_fp.fp_fw_ver_bin() == 0) { + flag_sys_ex_is_same = (g_core_fp._diff_overlay_flash() != g_core_fp._diff_overlay_bin()) ? 1 : 0; + I("Now flag_sys_ex_is_same valur=%d\n", flag_sys_ex_is_same); + if (((ic_data->vendor_fw_ver < g_i_FW_VER) || (ic_data->vendor_config_ver < g_i_CFG_VER)) + || (flag_sys_ex_is_same == 1)) { + I("Need to update!\n"); + ret = NO_ERR; + } else { + E("No need to update!\n"); + ret = 1; + } + } else { + E("FW bin fail!\n"); + ret = 1; + } + + return ret; +} + +static int i_get_FW(void) +{ + int ret = 0; + const struct firmware *image = NULL; + + I("file name = %s\n", i_CTPM_firmware_name); + ret = request_firmware(&image, i_CTPM_firmware_name, private_ts->dev); + if (ret < 0) { +#if defined(__EMBEDDED_FW__) + image = &g_embedded_fw; + I("%s: Couldn't find userspace FW, use embedded FW(size:%zu) instead.\n", __func__, g_embedded_fw.size); +#else + E("%s,fail in line%d error code=%d\n", __func__, __LINE__, ret); + return OPEN_FILE_FAIL; +#endif + } + + if (image != NULL) { + i_CTPM_FW_len = image->size; + i_CTPM_FW = kcalloc(i_CTPM_FW_len, sizeof(char), GFP_KERNEL); + memcpy(i_CTPM_FW, image->data, sizeof(char)*i_CTPM_FW_len); + } else { + I("%s: i_CTPM_FW = NULL\n", __func__); + return OPEN_FILE_FAIL; + } + + if (ret >= 0) + release_firmware(image); + ret = NO_ERR; + return ret; +} +static int i_update_FW(void) +{ + int upgrade_times = 0; + uint8_t ret = 0, result = 0; + + himax_int_enable(0); + + +update_retry: + + if (i_CTPM_FW_len == FW_SIZE_32k) + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_32k(i_CTPM_FW, i_CTPM_FW_len, false); + else if (i_CTPM_FW_len == FW_SIZE_60k) + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_60k(i_CTPM_FW, i_CTPM_FW_len, false); + else if (i_CTPM_FW_len == FW_SIZE_64k) + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_64k(i_CTPM_FW, i_CTPM_FW_len, false); + else if (i_CTPM_FW_len == FW_SIZE_124k) + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_124k(i_CTPM_FW, i_CTPM_FW_len, false); + else if (i_CTPM_FW_len == FW_SIZE_128k) + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_128k(i_CTPM_FW, i_CTPM_FW_len, false); + + if (ret == 0) { + upgrade_times++; + E("%s: TP upgrade error, upgrade_times = %d\n", __func__, upgrade_times); + + if (upgrade_times < 3) + goto update_retry; + else + result = -1; + + + } else { + g_core_fp.fp_read_FW_ver(); + g_core_fp.fp_touch_information(); + result = 1;/*upgrade success*/ + I("%s: TP upgrade OK\n", __func__); + } + + kfree(i_CTPM_FW); + i_CTPM_FW = NULL; + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(true, false); +#else + g_core_fp.fp_sense_on(0x00); +#endif + himax_int_enable(1); + return result; +} +#endif + + +static int himax_loadSensorConfig(struct himax_i2c_platform_data *pdata) +{ + I("%s: initialization complete\n", __func__); + return NO_ERR; +} + +#ifdef HX_ESD_RECOVERY +static void himax_esd_hw_reset(void) +{ +#ifdef HX_ZERO_FLASH + int result = 0; +#endif + if (g_ts_dbg != 0) + I("%s: Entering\n", __func__); + + I("START_Himax TP: ESD - Reset\n"); + + if (private_ts->in_self_test == 1) { + I("In self test , not TP: ESD - Reset\n"); + return; + } + + g_core_fp.fp_esd_ic_reset(); +#ifdef HX_ZERO_FLASH + I("It will update fw after esd event in zero flash mode!\n"); + result = g_core_fp.fp_0f_operation_dirly(); + if (result) { + E("Something is wrong! Skip Update with zero flash!\n"); + goto ESCAPE_0F_UPDATE; + } + g_core_fp.fp_reload_disable(0); + g_core_fp.fp_sense_on(0x00); + himax_report_all_leave_event(private_ts); + himax_int_enable(1); +ESCAPE_0F_UPDATE: +#endif + I("END_Himax TP: ESD - Reset\n"); +} +#endif + +#ifdef HX_SMART_WAKEUP +#ifdef HX_GESTURE_TRACK +static void gest_pt_log_coordinate(int rx, int tx) +{ + /*driver report x y with range 0 - 255 , we scale it up to x/y pixel*/ + gest_pt_x[gest_pt_cnt] = rx * (ic_data->HX_X_RES) / 255; + gest_pt_y[gest_pt_cnt] = tx * (ic_data->HX_Y_RES) / 255; +} +#endif +static int himax_wake_event_parse(struct himax_ts_data *ts, int ts_status) +{ + uint8_t *buf = wake_event_buffer; +#ifdef HX_GESTURE_TRACK + int tmp_max_x = 0x00, tmp_min_x = 0xFFFF, tmp_max_y = 0x00, tmp_min_y = 0xFFFF; + int gest_len; +#endif + int i = 0, check_FC = 0, ret; + int j = 0, gesture_pos = 0, gesture_flag = 0; + + if (g_ts_dbg != 0) + I("%s: Entering!, ts_status=%d\n", __func__, ts_status); + + if (buf == NULL) { + ret = -ENOMEM; + goto END; + } + + memcpy(buf, hx_touch_data->hx_event_buf, hx_touch_data->event_size); + + for (i = 0; i < GEST_PTLG_ID_LEN; i++) { + for (j = 0; j < GEST_SUP_NUM; j++) { + if (buf[i] == gest_event[j]) { + gesture_flag = buf[i]; + gesture_pos = j; + break; + } + } + I("0x%2.2X ", buf[i]); + if (buf[i] == gesture_flag) { + check_FC++; + } else { + I("ID START at %x , value = 0x%2X skip the event\n", i, buf[i]); + break; + } + } + + I("Himax gesture_flag= %x\n", gesture_flag); + I("Himax check_FC is %d\n", check_FC); + + if (check_FC != GEST_PTLG_ID_LEN) { + ret = 0; + goto END; + } + + if (buf[GEST_PTLG_ID_LEN] != GEST_PTLG_HDR_ID1 || + buf[GEST_PTLG_ID_LEN + 1] != GEST_PTLG_HDR_ID2) { + ret = 0; + goto END; + } + +#ifdef HX_GESTURE_TRACK + + if (buf[GEST_PTLG_ID_LEN] == GEST_PTLG_HDR_ID1 && + buf[GEST_PTLG_ID_LEN + 1] == GEST_PTLG_HDR_ID2) { + gest_len = buf[GEST_PTLG_ID_LEN + 2]; + I("gest_len = %d\n", gest_len); + i = 0; + gest_pt_cnt = 0; + I("gest doornidate start\n %s", __func__); + + while (i < (gest_len + 1) / 2) { + gest_pt_log_coordinate(buf[GEST_PTLG_ID_LEN + 4 + i * 2], buf[GEST_PTLG_ID_LEN + 4 + i * 2 + 1]); + i++; + I("gest_pt_x[%d]=%d\n", gest_pt_cnt, gest_pt_x[gest_pt_cnt]); + I("gest_pt_y[%d]=%d\n", gest_pt_cnt, gest_pt_y[gest_pt_cnt]); + gest_pt_cnt += 1; + } + + if (gest_pt_cnt) { + for (i = 0; i < gest_pt_cnt; i++) { + if (tmp_max_x < gest_pt_x[i]) + tmp_max_x = gest_pt_x[i]; + if (tmp_min_x > gest_pt_x[i]) + tmp_min_x = gest_pt_x[i]; + if (tmp_max_y < gest_pt_y[i]) + tmp_max_y = gest_pt_y[i]; + if (tmp_min_y > gest_pt_y[i]) + tmp_min_y = gest_pt_y[i]; + } + + I("gest_point x_min= %d, x_max= %d, y_min= %d, y_max= %d\n", tmp_min_x, tmp_max_x, tmp_min_y, tmp_max_y); + gest_start_x = gest_pt_x[0]; + hx_gesture_coor[0] = gest_start_x; + gest_start_y = gest_pt_y[0]; + hx_gesture_coor[1] = gest_start_y; + gest_end_x = gest_pt_x[gest_pt_cnt - 1]; + hx_gesture_coor[2] = gest_end_x; + gest_end_y = gest_pt_y[gest_pt_cnt - 1]; + hx_gesture_coor[3] = gest_end_y; + gest_width = tmp_max_x - tmp_min_x; + hx_gesture_coor[4] = gest_width; + gest_height = tmp_max_y - tmp_min_y; + hx_gesture_coor[5] = gest_height; + gest_mid_x = (tmp_max_x + tmp_min_x) / 2; + hx_gesture_coor[6] = gest_mid_x; + gest_mid_y = (tmp_max_y + tmp_min_y) / 2; + hx_gesture_coor[7] = gest_mid_y; + hx_gesture_coor[8] = gest_mid_x;/*gest_up_x*/ + hx_gesture_coor[9] = gest_mid_y - gest_height / 2; /*gest_up_y*/ + hx_gesture_coor[10] = gest_mid_x;/*gest_down_x*/ + hx_gesture_coor[11] = gest_mid_y + gest_height / 2; /*gest_down_y*/ + hx_gesture_coor[12] = gest_mid_x - gest_width / 2; /*gest_left_x*/ + hx_gesture_coor[13] = gest_mid_y; /*gest_left_y*/ + hx_gesture_coor[14] = gest_mid_x + gest_width / 2; /*gest_right_x*/ + hx_gesture_coor[15] = gest_mid_y; /*gest_right_y*/ + } + } + +#endif + + if (!ts->gesture_cust_en[gesture_pos]) { + I("%s NOT report key [%d] = %d\n", __func__, gesture_pos, gest_key_def[gesture_pos]); + g_target_report_data->SMWP_event_chk = 0; + ret = 0; + } else { + g_target_report_data->SMWP_event_chk = gest_key_def[gesture_pos]; + ret = gesture_pos; + } +END: + return ret; +} + +static void himax_wake_event_report(void) +{ + int KEY_EVENT = g_target_report_data->SMWP_event_chk; +#ifdef HIMAX_V2_SENSOR_EN + static int report_cnt = 0; +#endif + if (g_ts_dbg != 0) + I("%s: Entering!\n", __func__); + + if (KEY_EVENT) { +#ifdef HIMAX_V2_SENSOR_EN + if (private_ts->report_gesture_key) { + input_report_key(private_ts->sensor_pdata->input_sensor_dev, KEY_F1, 1); + input_sync(private_ts->sensor_pdata->input_sensor_dev); + input_report_key(private_ts->sensor_pdata->input_sensor_dev, KEY_F1, 0); + input_sync(private_ts->sensor_pdata->input_sensor_dev); + ++report_cnt; + } else { + input_report_abs(private_ts->sensor_pdata->input_sensor_dev, ABS_DISTANCE, ++report_cnt); + input_sync(private_ts->sensor_pdata->input_sensor_dev); + } + I("input report: %d", report_cnt); + if (report_cnt >= REPORT_MAX_COUNT) + report_cnt = 0; +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&private_ts->tap_gesture_wakelock, msecs_to_jiffies(5000)); +#else + + PM_WAKEUP_EVENT(private_ts->tap_gesture_wakelock, 5000); +#endif +#else + I(" %s SMART WAKEUP KEY event %d press\n", __func__, KEY_EVENT); + input_report_key(private_ts->input_dev, KEY_EVENT, 1); + input_sync(private_ts->input_dev); + I(" %s SMART WAKEUP KEY event %d release\n", __func__, KEY_EVENT); + input_report_key(private_ts->input_dev, KEY_EVENT, 0); + input_sync(private_ts->input_dev); +#endif + FAKE_POWER_KEY_SEND = true; +#ifdef HX_GESTURE_TRACK + I("gest_start_x= %d, gest_start_y= %d, gest_end_x= %d, gest_end_y= %d\n", gest_start_x, gest_start_y, + gest_end_x, gest_end_y); + I("gest_width= %d, gest_height= %d, gest_mid_x= %d, gest_mid_y= %d\n", gest_width, gest_height, + gest_mid_x, gest_mid_y); + I("gest_up_x= %d, gest_up_y= %d, gest_down_x= %d, gest_down_y= %d\n", hx_gesture_coor[8], hx_gesture_coor[9], + hx_gesture_coor[10], hx_gesture_coor[11]); + I("gest_left_x= %d, gest_left_y= %d, gest_right_x= %d, gest_right_y= %d\n", hx_gesture_coor[12], hx_gesture_coor[13], + hx_gesture_coor[14], hx_gesture_coor[15]); +#endif + g_target_report_data->SMWP_event_chk = 0; + } +} + +#endif + +int himax_report_data_init(void) +{ + if (hx_touch_data->hx_coord_buf != NULL) { + kfree(hx_touch_data->hx_coord_buf); + hx_touch_data->hx_coord_buf = NULL; + } + + if (hx_touch_data->hx_rawdata_buf != NULL) { + kfree(hx_touch_data->hx_rawdata_buf); + hx_touch_data->hx_rawdata_buf = NULL; + } + +#if defined(HX_SMART_WAKEUP) + hx_touch_data->event_size = g_core_fp.fp_get_touch_data_size(); + + if (hx_touch_data->hx_event_buf != NULL) { + kfree(hx_touch_data->hx_event_buf); + hx_touch_data->hx_event_buf = NULL; + } + + if (wake_event_buffer != NULL) { + kfree(wake_event_buffer); + wake_event_buffer = NULL; + } + +#endif + hx_touch_data->touch_all_size = g_core_fp.fp_get_touch_data_size(); + hx_touch_data->raw_cnt_max = ic_data->HX_MAX_PT / 4; + hx_touch_data->raw_cnt_rmd = ic_data->HX_MAX_PT % 4; + /* more than 4 fingers */ + if (hx_touch_data->raw_cnt_rmd != 0x00) { + hx_touch_data->rawdata_size = g_core_fp.fp_cal_data_len(hx_touch_data->raw_cnt_rmd, ic_data->HX_MAX_PT, hx_touch_data->raw_cnt_max); + hx_touch_data->touch_info_size = (ic_data->HX_MAX_PT + hx_touch_data->raw_cnt_max + 2) * 4; + } else { /* less than 4 fingers */ + hx_touch_data->rawdata_size = g_core_fp.fp_cal_data_len(hx_touch_data->raw_cnt_rmd, ic_data->HX_MAX_PT, hx_touch_data->raw_cnt_max); + hx_touch_data->touch_info_size = (ic_data->HX_MAX_PT + hx_touch_data->raw_cnt_max + 1) * 4; + } +#if defined(HX_PEN_FUNC_EN) + hx_touch_data->touch_info_size += PEN_INFO_SZ; +#endif + if ((ic_data->HX_TX_NUM * ic_data->HX_RX_NUM + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM) % hx_touch_data->rawdata_size == 0) + hx_touch_data->rawdata_frame_size = (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM) / hx_touch_data->rawdata_size; + else + hx_touch_data->rawdata_frame_size = (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM) / hx_touch_data->rawdata_size + 1; + + I("%s: rawdata_frame_size = %d\n", __func__, hx_touch_data->rawdata_frame_size); + I("%s: ic_data->HX_MAX_PT:%d, hx_raw_cnt_max:%d, hx_raw_cnt_rmd:%d, g_hx_rawdata_size:%d, hx_touch_data->touch_info_size:%d\n", __func__, ic_data->HX_MAX_PT, hx_touch_data->raw_cnt_max, hx_touch_data->raw_cnt_rmd, hx_touch_data->rawdata_size, hx_touch_data->touch_info_size); + hx_touch_data->hx_coord_buf = kzalloc(sizeof(uint8_t) * (hx_touch_data->touch_info_size), GFP_KERNEL); + + if (hx_touch_data->hx_coord_buf == NULL) + goto mem_alloc_fail; + + if (g_target_report_data == NULL) { + g_target_report_data = kzalloc(sizeof(struct himax_target_report_data), GFP_KERNEL); + if (g_target_report_data == NULL) + goto mem_alloc_fail; + g_target_report_data->x = kzalloc(sizeof(int)*(ic_data->HX_MAX_PT), GFP_KERNEL); + if (g_target_report_data->x == NULL) + goto mem_alloc_fail; + g_target_report_data->y = kzalloc(sizeof(int)*(ic_data->HX_MAX_PT), GFP_KERNEL); + if (g_target_report_data->y == NULL) + goto mem_alloc_fail; + g_target_report_data->w = kzalloc(sizeof(int)*(ic_data->HX_MAX_PT), GFP_KERNEL); + if (g_target_report_data->w == NULL) + goto mem_alloc_fail; + g_target_report_data->finger_id = kzalloc(sizeof(int)*(ic_data->HX_MAX_PT), GFP_KERNEL); + if (g_target_report_data->finger_id == NULL) + goto mem_alloc_fail; +#if defined(HX_PEN_FUNC_EN) + g_target_report_data->p_x = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_x == NULL) + goto mem_alloc_fail; + g_target_report_data->p_y = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_y == NULL) + goto mem_alloc_fail; + g_target_report_data->p_w = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_w == NULL) + goto mem_alloc_fail; + g_target_report_data->pen_id = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->pen_id == NULL) + goto mem_alloc_fail; + g_target_report_data->p_hover = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_hover == NULL) + goto mem_alloc_fail; + g_target_report_data->p_tilt_x = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_tilt_x == NULL) + goto mem_alloc_fail; + g_target_report_data->p_btn = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_btn == NULL) + goto mem_alloc_fail; + g_target_report_data->p_btn2 = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_btn2 == NULL) + goto mem_alloc_fail; + g_target_report_data->p_tilt_y = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_tilt_y == NULL) + goto mem_alloc_fail; + g_target_report_data->p_on = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_on == NULL) + goto mem_alloc_fail; + +#endif + } +#ifdef HX_SMART_WAKEUP + g_target_report_data->SMWP_event_chk = 0; + wake_event_buffer = kcalloc(hx_touch_data->event_size, sizeof(uint8_t), GFP_KERNEL); + if (wake_event_buffer == NULL) + goto mem_alloc_fail; +#endif + + hx_touch_data->hx_rawdata_buf = kzalloc(sizeof(uint8_t) * (hx_touch_data->touch_all_size - hx_touch_data->touch_info_size), GFP_KERNEL); + + if (hx_touch_data->hx_rawdata_buf == NULL) + goto mem_alloc_fail; + +#if defined(HX_SMART_WAKEUP) + hx_touch_data->hx_event_buf = kzalloc(sizeof(uint8_t) * (hx_touch_data->event_size), GFP_KERNEL); + + if (hx_touch_data->hx_event_buf == NULL) + goto mem_alloc_fail; + +#endif + return NO_ERR; +mem_alloc_fail: +#if defined(HX_PEN_FUNC_EN) + kfree(g_target_report_data->p_on); + g_target_report_data->p_on = NULL; + kfree(g_target_report_data->p_tilt_y); + g_target_report_data->p_tilt_y = NULL; + kfree(g_target_report_data->p_btn2); + g_target_report_data->p_btn2 = NULL; + kfree(g_target_report_data->p_btn); + g_target_report_data->p_btn = NULL; + kfree(g_target_report_data->p_tilt_x); + g_target_report_data->p_tilt_x = NULL; + kfree(g_target_report_data->p_hover); + g_target_report_data->p_hover = NULL; + kfree(g_target_report_data->pen_id); + g_target_report_data->pen_id = NULL; + kfree(g_target_report_data->p_w); + g_target_report_data->p_w = NULL; + kfree(g_target_report_data->p_y); + g_target_report_data->p_y = NULL; + kfree(g_target_report_data->p_x); + g_target_report_data->p_x = NULL; +#endif + kfree(g_target_report_data->finger_id); + g_target_report_data->finger_id = NULL; + kfree(g_target_report_data->w); + g_target_report_data->w = NULL; + kfree(g_target_report_data->y); + g_target_report_data->y = NULL; + kfree(g_target_report_data->x); + g_target_report_data->x = NULL; + kfree(g_target_report_data); + g_target_report_data = NULL; + +#if defined(HX_SMART_WAKEUP) + kfree(wake_event_buffer); + wake_event_buffer = NULL; + kfree(hx_touch_data->hx_event_buf); + hx_touch_data->hx_event_buf = NULL; +#endif + kfree(hx_touch_data->hx_rawdata_buf); + hx_touch_data->hx_rawdata_buf = NULL; + kfree(hx_touch_data->hx_coord_buf); + hx_touch_data->hx_coord_buf = NULL; + + I("%s: Memory allocate fail!\n", __func__); + return MEM_ALLOC_FAIL; +} +EXPORT_SYMBOL(himax_report_data_init); + +void himax_report_data_deinit(void) +{ +#if defined(HX_PEN_FUNC_EN) + kfree(g_target_report_data->p_on); + g_target_report_data->p_on = NULL; + kfree(g_target_report_data->p_tilt_y); + g_target_report_data->p_tilt_y = NULL; + kfree(g_target_report_data->p_btn2); + g_target_report_data->p_btn2 = NULL; + kfree(g_target_report_data->p_btn); + g_target_report_data->p_btn = NULL; + kfree(g_target_report_data->p_tilt_x); + g_target_report_data->p_tilt_x = NULL; + kfree(g_target_report_data->p_hover); + g_target_report_data->p_hover = NULL; + kfree(g_target_report_data->pen_id); + g_target_report_data->pen_id = NULL; + kfree(g_target_report_data->p_w); + g_target_report_data->p_w = NULL; + kfree(g_target_report_data->p_y); + g_target_report_data->p_y = NULL; + kfree(g_target_report_data->p_x); + g_target_report_data->p_x = NULL; +#endif + kfree(g_target_report_data->finger_id); + g_target_report_data->finger_id = NULL; + kfree(g_target_report_data->w); + g_target_report_data->w = NULL; + kfree(g_target_report_data->y); + g_target_report_data->y = NULL; + kfree(g_target_report_data->x); + g_target_report_data->x = NULL; + kfree(g_target_report_data); + g_target_report_data = NULL; + +#if defined(HX_SMART_WAKEUP) + kfree(wake_event_buffer); + wake_event_buffer = NULL; + kfree(hx_touch_data->hx_event_buf); + hx_touch_data->hx_event_buf = NULL; +#endif + kfree(hx_touch_data->hx_rawdata_buf); + hx_touch_data->hx_rawdata_buf = NULL; + kfree(hx_touch_data->hx_coord_buf); + hx_touch_data->hx_coord_buf = NULL; +} + +/*start ts_work*/ +#if defined(HX_USB_DETECT_GLOBAL) +void himax_cable_detect_func(bool force_renew) +{ + struct himax_ts_data *ts; + + /*u32 connect_status = 0;*/ + uint8_t connect_status = 0; + + connect_status = USB_detect_flag;/* upmu_is_chr_det(); */ + ts = private_ts; + + /* I("Touch: cable status=%d, cable_config=%p, usb_connected=%d\n", connect_status, ts->cable_config, ts->usb_connected); */ + if (ts->cable_config) { + if ((connect_status != ts->usb_connected) || force_renew) { + if (connect_status) { + ts->cable_config[1] = 0x01; + ts->usb_connected = 0x01; + } else { + ts->cable_config[1] = 0x00; + ts->usb_connected = 0x00; + } + + g_core_fp.fp_usb_detect_set(ts->cable_config); + I("%s: Cable status change: 0x%2.2X\n", __func__, ts->usb_connected); + } + + /*else */ + /*I("%s: Cable status is the same as previous one, ignore.\n", __func__); */ + } +} +#endif + +static int himax_ts_work_status(struct himax_ts_data *ts) +{ + /* 1: normal, 2:SMWP */ + int result = HX_REPORT_COORD; + + hx_touch_data->diag_cmd = ts->diag_cmd; + if (hx_touch_data->diag_cmd) + result = HX_REPORT_COORD_RAWDATA; + +#ifdef HX_SMART_WAKEUP + if (atomic_read(&ts->suspend_mode) && (ts->SMWP_enable) && (!hx_touch_data->diag_cmd)) + result = HX_REPORT_SMWP_EVENT; +#endif + /* I("Now Status is %d\n", result); */ + return result; +} + +static int himax_touch_get(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + switch (ts_path) { + /*normal*/ + case HX_REPORT_COORD: + if ((HX_HW_RESET_ACTIVATE) +#ifdef HX_ESD_RECOVERY + || (HX_ESD_RESET_ACTIVATE) +#endif + ) { + if (!g_core_fp.fp_read_event_stack(buf, 128)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + } + } else { + if (!g_core_fp.fp_read_event_stack(buf, hx_touch_data->touch_info_size)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + } + } + break; +#if defined(HX_SMART_WAKEUP) + + /*SMWP*/ + case HX_REPORT_SMWP_EVENT: + PM_WAKEUP_EVENT(ts->ts_SMWP_wake_lock, TS_WAKE_LOCK_TIMEOUT); + msleep(20); + g_core_fp.fp_burst_enable(0); + + if (!g_core_fp.fp_read_event_stack(buf, hx_touch_data->event_size)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + } + break; +#endif + case HX_REPORT_COORD_RAWDATA: + if (!g_core_fp.fp_read_event_stack(buf, 128)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + } + break; + default: + break; + } + + return ts_status; +} + +/* start error_control*/ +static int himax_checksum_cal(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ + uint16_t check_sum_cal = 0; + int32_t i = 0; + int length = 0; + int zero_cnt = 0; + int raw_data_sel = 0; + int ret_val = ts_status; + + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + /* Normal */ + switch (ts_path) { + case HX_REPORT_COORD: + length = hx_touch_data->touch_info_size; + break; +#if defined(HX_SMART_WAKEUP) +/* SMWP */ + case HX_REPORT_SMWP_EVENT: + length = (GEST_PTLG_ID_LEN + GEST_PTLG_HDR_LEN); + break; +#endif + case HX_REPORT_COORD_RAWDATA: + length = hx_touch_data->touch_info_size; + break; + default: + I("%s, Neither Normal Nor SMWP error!\n", __func__); + ret_val = HX_PATH_FAIL; + goto END_FUNCTION; + } + + for (i = 0; i < length; i++) { + check_sum_cal += buf[i]; + if (buf[i] == 0x00) + zero_cnt++; + } + + if (check_sum_cal % 0x100 != 0) { + I("[HIMAX TP MSG] point data_checksum not match : check_sum_cal: 0x%02X\n", check_sum_cal); + ret_val = HX_CHKSUM_FAIL; + } else if (zero_cnt == length) { + if (ts->use_irq) + I("[HIMAX TP MSG] All Zero event\n"); + + ret_val = HX_CHKSUM_FAIL; + } else { + raw_data_sel = buf[HX_TOUCH_INFO_POINT_CNT]>>4 & 0x0F; + /*I("%s:raw_out_sel=%x , hx_touch_data->diag_cmd=%x.\n", __func__, raw_data_sel, hx_touch_data->diag_cmd);*/ + if ((raw_data_sel != 0x0F) && (raw_data_sel != hx_touch_data->diag_cmd)) {/*raw data out not match skip it*/ + /*I("%s:raw data out not match.\n", __func__);*/ + if (!hx_touch_data->diag_cmd) { + g_core_fp.fp_read_event_stack(buf, (128-hx_touch_data->touch_info_size));/*Need to clear event stack here*/ + /*I("%s: size =%d, buf[0]=%x ,buf[1]=%x, buf[2]=%x, buf[3]=%x.\n", __func__,(128-hx_touch_data->touch_info_size), buf[0], buf[1], buf[2], buf[3]);*/ + /*I("%s:also clear event stack.\n", __func__);*/ + } + ret_val = HX_READY_SERVE; + } + } + +END_FUNCTION: + if (g_ts_dbg != 0) + I("%s: END, ret_val=%d!\n", __func__, ret_val); + return ret_val; +} + +#ifdef HX_ESD_RECOVERY +#ifdef HX_ZERO_FLASH +void hx_update_dirly_0f(void) +{ + I("It will update fw after esd event in zero flash mode!\n"); + g_core_fp.fp_0f_operation_dirly(); +} +#endif +static int himax_ts_event_check(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ + int hx_EB_event = 0; + int hx_EC_event = 0; + int hx_ED_event = 0; + int hx_esd_event = 0; + int hx_zero_event = 0; + int shaking_ret = 0; + + int32_t loop_i = 0; + int length = 0; + int ret_val = ts_status; + + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + /* Normal */ + switch (ts_path) { + case HX_REPORT_COORD: + length = hx_touch_data->touch_info_size; + break; +#if defined(HX_SMART_WAKEUP) +/* SMWP */ + case HX_REPORT_SMWP_EVENT: + length = (GEST_PTLG_ID_LEN + GEST_PTLG_HDR_LEN); + break; +#endif + case HX_REPORT_COORD_RAWDATA: + length = hx_touch_data->touch_info_size; + break; + default: + I("%s, Neither Normal Nor SMWP error!\n", __func__); + ret_val = HX_PATH_FAIL; + goto END_FUNCTION; + } + + if (g_ts_dbg != 0) + I("Now Path=%d, Now status=%d, length=%d\n", ts_path, ts_status, length); + + for (loop_i = 0; loop_i < length; loop_i++) { + if (ts_path == HX_REPORT_COORD || ts_path == HX_REPORT_COORD_RAWDATA) { + /* case 1 ESD recovery flow */ + if (buf[loop_i] == 0xEB) { + hx_EB_event++; + } else if (buf[loop_i] == 0xEC) { + hx_EC_event++; + } else if (buf[loop_i] == 0xED) { + hx_ED_event++; + } else if (buf[loop_i] == 0x00) { /* case 2 ESD recovery flow-Disable */ + hx_zero_event++; + } else { + hx_EB_event = 0; + hx_EC_event = 0; + hx_ED_event = 0; + hx_zero_event = 0; + g_zero_event_count = 0; + } + } + } + + if (hx_EB_event == length) { + hx_esd_event = length; + hx_EB_event_flag++; + I("[HIMAX TP MSG]: ESD event checked - ALL 0xEB.\n"); + } else if (hx_EC_event == length) { + hx_esd_event = length; + hx_EC_event_flag++; + I("[HIMAX TP MSG]: ESD event checked - ALL 0xEC.\n"); + } else if (hx_ED_event == length) { + hx_esd_event = length; + hx_ED_event_flag++; + I("[HIMAX TP MSG]: ESD event checked - ALL 0xED.\n"); + } +/*#ifdef HX_ZERO_FLASH + * //This is for previous version(a, b) because HW pull TSIX low continuely after watchdog timeout reset + * else if (hx_zero_event == length) { + * //check zero flash status + * if (g_core_fp.fp_0f_esd_check() < 0) { + * g_zero_event_count = 6; + * I("[HIMAX TP MSG]: ESD event checked - ALL Zero in ZF.\n"); + * } else { + * I("[HIMAX TP MSG]: Status check pass in ZF.\n"); + * } + * } + *#endif + */ + else + hx_esd_event = 0; + + if ((hx_esd_event == length || hx_zero_event == length) + && (HX_HW_RESET_ACTIVATE == 0) + && (HX_ESD_RESET_ACTIVATE == 0) + && (hx_touch_data->diag_cmd == 0) + && (ts->in_self_test == 0)) { + shaking_ret = g_core_fp.fp_ic_esd_recovery(hx_esd_event, hx_zero_event, length); + + if (shaking_ret == HX_ESD_EVENT) { + himax_esd_hw_reset(); + ret_val = HX_ESD_EVENT; + } else if (shaking_ret == HX_ZERO_EVENT_COUNT) { + ret_val = HX_ZERO_EVENT_COUNT; + } else { + I("I2C running. Nothing to be done!\n"); + ret_val = HX_IC_RUNNING; + } + } else if (HX_ESD_RESET_ACTIVATE) { /* drop 1st interrupts after chip reset */ + HX_ESD_RESET_ACTIVATE = 0; + I("[HX_ESD_RESET_ACTIVATE]:%s: Back from reset, ready to serve.\n", __func__); + ret_val = HX_ESD_REC_OK; + } + +END_FUNCTION: + if (g_ts_dbg != 0) + I("%s: END, ret_val=%d!\n", __func__, ret_val); + + return ret_val; +} +#endif + +static int himax_err_ctrl(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ +#ifdef HX_RST_PIN_FUNC + if (HX_HW_RESET_ACTIVATE) { + /* drop 1st interrupts after chip reset */ + HX_HW_RESET_ACTIVATE = 0; + I("[HX_HW_RESET_ACTIVATE]:%s: Back from reset, ready to serve.\n", __func__); + ts_status = HX_RST_OK; + goto END_FUNCTION; + } +#endif + + ts_status = himax_checksum_cal(ts, buf, ts_path, ts_status); + if (ts_status == HX_CHKSUM_FAIL) { + goto CHK_FAIL; + } else { +#ifdef HX_ESD_RECOVERY + /* continuous N times record, not total N times. */ + g_zero_event_count = 0; +#endif + goto END_FUNCTION; + } + +CHK_FAIL: +#ifdef HX_ESD_RECOVERY + ts_status = himax_ts_event_check(ts, buf, ts_path, ts_status); +#endif + + +END_FUNCTION: + if (g_ts_dbg != 0) + I("%s: END, ts_status=%d!\n", __func__, ts_status); + return ts_status; +} +/* end error_control*/ + +/* start distribute_data*/ +static int himax_distribute_touch_data(uint8_t *buf, int ts_path, int ts_status) +{ + uint8_t hx_state_info_pos = hx_touch_data->touch_info_size - 3; + +#if defined(HX_PEN_FUNC_EN) + hx_state_info_pos -= PEN_INFO_SZ; +#endif + + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + if (ts_path == HX_REPORT_COORD) { + memcpy(hx_touch_data->hx_coord_buf, &buf[0], hx_touch_data->touch_info_size); + + if (buf[hx_state_info_pos] != 0xFF && buf[hx_state_info_pos + 1] != 0xFF) + memcpy(hx_touch_data->hx_state_info, &buf[hx_state_info_pos], 2); + else + memset(hx_touch_data->hx_state_info, 0x00, sizeof(hx_touch_data->hx_state_info)); + + if ((HX_HW_RESET_ACTIVATE) +#ifdef HX_ESD_RECOVERY + || (HX_ESD_RESET_ACTIVATE) +#endif + ) { + memcpy(hx_touch_data->hx_rawdata_buf, &buf[hx_touch_data->touch_info_size], hx_touch_data->touch_all_size - hx_touch_data->touch_info_size); + } + } else if (ts_path == HX_REPORT_COORD_RAWDATA) { + memcpy(hx_touch_data->hx_coord_buf, &buf[0], hx_touch_data->touch_info_size); + + if (buf[hx_state_info_pos] != 0xFF && buf[hx_state_info_pos + 1] != 0xFF) + memcpy(hx_touch_data->hx_state_info, &buf[hx_state_info_pos], 2); + else + memset(hx_touch_data->hx_state_info, 0x00, sizeof(hx_touch_data->hx_state_info)); + + memcpy(hx_touch_data->hx_rawdata_buf, &buf[hx_touch_data->touch_info_size], hx_touch_data->touch_all_size - hx_touch_data->touch_info_size); +#if defined(HX_SMART_WAKEUP) + } else if (ts_path == HX_REPORT_SMWP_EVENT) { + memcpy(hx_touch_data->hx_event_buf, buf, hx_touch_data->event_size); +#endif + } else { + E("%s, Fail Path!\n", __func__); + ts_status = HX_PATH_FAIL; + } + + if (g_ts_dbg != 0) + I("%s: End, ts_status=%d!\n", __func__, ts_status); + return ts_status; +} +/* end assign_data*/ + +/* start parse_report_data*/ +int himax_parse_report_points(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + int x = 0, y = 0, w = 0; +#if defined(HX_PEN_FUNC_EN) + uint8_t p_hover = 0, p_btn = 0, p_btn2 = 0; + int8_t p_tilt_x = 0, p_tilt_y = 0; + int p_x = 0, p_y = 0, p_w = 0; + static uint8_t p_p_on; +#endif + int base = 0; + int32_t loop_i = 0; + + if (g_ts_dbg != 0) + I("%s: start!\n", __func__); + +#if defined(HX_PEN_FUNC_EN) + p_p_on = 0; + base = hx_touch_data->touch_info_size - PEN_INFO_SZ; + + p_x = hx_touch_data->hx_coord_buf[base] << 8 | hx_touch_data->hx_coord_buf[base + 1]; + p_y = (hx_touch_data->hx_coord_buf[base + 2] << 8 | hx_touch_data->hx_coord_buf[base + 3]); + p_w = (hx_touch_data->hx_coord_buf[base + 4] << 8 | hx_touch_data->hx_coord_buf[base + 5]); + p_tilt_x = (int8_t)hx_touch_data->hx_coord_buf[base + 6]; + p_hover = hx_touch_data->hx_coord_buf[base + 7]; + p_btn = hx_touch_data->hx_coord_buf[base + 8]; + p_btn2 = hx_touch_data->hx_coord_buf[base + 9]; + p_tilt_y = (int8_t)hx_touch_data->hx_coord_buf[base + 10]; + + if (g_ts_dbg != 0) { + D("%s: p_x=%d, p_y=%d, p_w=%d\n", __func__, p_x, p_y, p_w); + D("%s: p_tilt_x=%d, p_hover=%d\n", __func__, p_tilt_x, p_hover); + D("%s: p_btn=%d, p_btn2=%d\n", __func__, p_btn, p_btn2); + D("%s: p_tilt_y=%d\n", __func__, p_tilt_y); + } + + if (p_x >= 0 && p_x <= ts->pdata->abs_x_max && p_y >= 0 && p_y <= ts->pdata->abs_y_max) { + g_target_report_data->p_x[0] = p_x; + g_target_report_data->p_y[0] = p_y; + g_target_report_data->p_w[0] = p_w; + g_target_report_data->p_hover[0] = p_hover; + g_target_report_data->pen_id[0] = 1; + g_target_report_data->p_btn[0] = p_btn; + g_target_report_data->p_btn2[0] = p_btn2; + g_target_report_data->p_tilt_x[0] = p_tilt_x; + g_target_report_data->p_tilt_y[0] = p_tilt_y; + g_target_report_data->p_on[0] = 1; + ts->hx_point_num++; + } else {/* report coordinates */ + g_target_report_data->p_x[0] = 0; + g_target_report_data->p_y[0] = 0; + g_target_report_data->p_w[0] = 0; + g_target_report_data->p_hover[0] = 0; + g_target_report_data->pen_id[0] = 0; + g_target_report_data->p_btn[0] = 0; + g_target_report_data->p_btn2[0] = 0; + g_target_report_data->p_tilt_x[0] = 0; + g_target_report_data->p_tilt_y[0] = 0; + g_target_report_data->p_on[0] = 0; + } + + if (g_ts_dbg != 0) { + if (p_p_on != g_target_report_data->p_on[0]) { + I("p_on[0] = %d, hx_point_num=%d\n", + g_target_report_data->p_on[0], + ts->hx_point_num); + p_p_on = g_target_report_data->p_on[0]; + } + } +#endif + + ts->old_finger = ts->pre_finger_mask; + if (ts->hx_point_num == 0) { + if (g_ts_dbg != 0) + I("%s: hx_point_num = 0!\n", __func__); + return ts_status; + } + ts->pre_finger_mask = 0; + hx_touch_data->finger_num = hx_touch_data->hx_coord_buf[ts->coordInfoSize - 4] & 0x0F; + hx_touch_data->finger_on = 1; + AA_press = 1; + + g_target_report_data->finger_num = hx_touch_data->finger_num; + g_target_report_data->finger_on = hx_touch_data->finger_on; + g_target_report_data->ig_count = hx_touch_data->hx_coord_buf[ts->coordInfoSize - 5]; + + if (g_ts_dbg != 0) + I("%s:finger_num = 0x%2X, finger_on = %d\n", __func__, g_target_report_data->finger_num, g_target_report_data->finger_on); + + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + base = loop_i * 4; + x = hx_touch_data->hx_coord_buf[base] << 8 | hx_touch_data->hx_coord_buf[base + 1]; + y = (hx_touch_data->hx_coord_buf[base + 2] << 8 | hx_touch_data->hx_coord_buf[base + 3]); + w = hx_touch_data->hx_coord_buf[(ts->nFinger_support * 4) + loop_i]; + + if (g_ts_dbg != 0) + D("%s: now parsing[%d]:x=%d, y=%d, w=%d\n", __func__, loop_i, x, y, w); + + if (x >= 0 && x <= ts->pdata->abs_x_max && y >= 0 && y <= ts->pdata->abs_y_max) { + hx_touch_data->finger_num--; + + g_target_report_data->x[loop_i] = x; + g_target_report_data->y[loop_i] = y; + g_target_report_data->w[loop_i] = w; + g_target_report_data->finger_id[loop_i] = 1; + + /*I("%s: g_target_report_data->x[loop_i]=%d, g_target_report_data->y[loop_i]=%d, g_target_report_data->w[loop_i]=%d",*/ + /*__func__, g_target_report_data->x[loop_i], g_target_report_data->y[loop_i], g_target_report_data->w[loop_i]); */ + + + if (!ts->first_pressed) { + ts->first_pressed = 1; + I("S1@%d, %d\n", x, y); + } + + ts->pre_finger_data[loop_i][0] = x; + ts->pre_finger_data[loop_i][1] = y; + + ts->pre_finger_mask = ts->pre_finger_mask + (1 << loop_i); + } else {/* report coordinates */ + g_target_report_data->x[loop_i] = x; + g_target_report_data->y[loop_i] = y; + g_target_report_data->w[loop_i] = w; + g_target_report_data->finger_id[loop_i] = 0; + + if (loop_i == 0 && ts->first_pressed == 1) { + ts->first_pressed = 2; + I("E1@%d, %d\n", ts->pre_finger_data[0][0], ts->pre_finger_data[0][1]); + } + } + } + + if (g_ts_dbg != 0) { + for (loop_i = 0; loop_i < 10; loop_i++) + D("DBG X=%d Y=%d ID=%d\n", g_target_report_data->x[loop_i], g_target_report_data->y[loop_i], g_target_report_data->finger_id[loop_i]); + + D("DBG finger number %d\n", g_target_report_data->finger_num); + } + + if (g_ts_dbg != 0) + I("%s: end!\n", __func__); + return ts_status; +} + +static int himax_parse_report_data(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + + if (g_ts_dbg != 0) + I("%s: start now_status=%d!\n", __func__, ts_status); + + + EN_NoiseFilter = (hx_touch_data->hx_coord_buf[HX_TOUCH_INFO_POINT_CNT + 2] >> 3); + /* I("EN_NoiseFilter=%d\n", EN_NoiseFilter); */ + EN_NoiseFilter = EN_NoiseFilter & 0x01; + /* I("EN_NoiseFilter2=%d\n", EN_NoiseFilter); */ +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) + tpd_key = (hx_touch_data->hx_coord_buf[HX_TOUCH_INFO_POINT_CNT + 2] >> 4); + + /* All (VK+AA)leave */ + if (tpd_key == 0x0F) + tpd_key = 0x00; + +#endif + p_point_num = ts->hx_point_num; + + if (hx_touch_data->hx_coord_buf[HX_TOUCH_INFO_POINT_CNT] == 0xff) + ts->hx_point_num = 0; + else + ts->hx_point_num = hx_touch_data->hx_coord_buf[HX_TOUCH_INFO_POINT_CNT] & 0x0f; + + + switch (ts_path) { + case HX_REPORT_COORD: + ts_status = himax_parse_report_points(ts, ts_path, ts_status); + break; + case HX_REPORT_COORD_RAWDATA: + /* touch monitor rawdata */ + if (debug_data != NULL) { + if (debug_data->fp_set_diag_cmd(ic_data, hx_touch_data)) + I("%s: raw data_checksum not match\n", __func__); + } else { + E("%s,There is no init set_diag_cmd\n", __func__); + } + ts_status = himax_parse_report_points(ts, ts_path, ts_status); + break; +#ifdef HX_SMART_WAKEUP + case HX_REPORT_SMWP_EVENT: + himax_wake_event_parse(ts, ts_status); + break; +#endif + default: + E("%s:Fail Path!\n", __func__); + ts_status = HX_PATH_FAIL; + break; + } + if (g_ts_dbg != 0) + I("%s: end now_status=%d!\n", __func__, ts_status); + return ts_status; +} + +/* end parse_report_data*/ + +static void himax_report_all_leave_event(struct himax_ts_data *ts) +{ + int loop_i = 0; + + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { +#ifndef HX_PROTOCOL_A + input_mt_slot(ts->input_dev, loop_i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_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); + input_sync(ts->input_dev); +} + +/* start report_data */ +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) +static void himax_key_report_operation(int tp_key_index, struct himax_ts_data *ts) +{ + uint16_t x_position = 0, y_position = 0; + + if (g_ts_dbg != 0) + I("%s: Entering\n", __func__); + + if (tp_key_index != 0x00) { + I("virtual key index =%x\n", tp_key_index); + + if (tp_key_index == 0x01) { + vk_press = 1; + I("back key pressed\n"); + + if (ts->pdata->virtual_key) { + if (ts->button[0].index) { + x_position = (ts->button[0].x_range_min + ts->button[0].x_range_max) / 2; + y_position = (ts->button[0].y_range_min + ts->button[0].y_range_max) / 2; + } + +#ifdef HX_PROTOCOL_A + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 0); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); + input_mt_sync(ts->input_dev); +#else + input_mt_slot(ts->input_dev, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); +#endif + } else { + input_report_key(ts->input_dev, KEY_BACK, 1); + } + } else if (tp_key_index == 0x02) { + vk_press = 1; + I("home key pressed\n"); + + if (ts->pdata->virtual_key) { + if (ts->button[1].index) { + x_position = (ts->button[1].x_range_min + ts->button[1].x_range_max) / 2; + y_position = (ts->button[1].y_range_min + ts->button[1].y_range_max) / 2; + } + +#ifdef HX_PROTOCOL_A + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 0); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); + input_mt_sync(ts->input_dev); +#else + input_mt_slot(ts->input_dev, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); +#endif + } else { + input_report_key(ts->input_dev, KEY_HOME, 1); + } + } else if (tp_key_index == 0x04) { + vk_press = 1; + I("APP_switch key pressed\n"); + + if (ts->pdata->virtual_key) { + if (ts->button[2].index) { + x_position = (ts->button[2].x_range_min + ts->button[2].x_range_max) / 2; + y_position = (ts->button[2].y_range_min + ts->button[2].y_range_max) / 2; + } + +#ifdef HX_PROTOCOL_A + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); + input_mt_sync(ts->input_dev); +#else + input_mt_slot(ts->input_dev, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); +#endif + } else { + input_report_key(ts->input_dev, KEY_APPSELECT, 1); + } + } + input_sync(ts->input_dev); + } else { /*tp_key_index =0x00*/ + I("virtual key released\n"); + vk_press = 0; +#ifndef HX_PROTOCOL_A + input_mt_slot(ts->input_dev, 0); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); +#else + input_mt_sync(ts->input_dev); +#endif + input_report_key(ts->input_dev, KEY_BACK, 0); + input_report_key(ts->input_dev, KEY_HOME, 0); + input_report_key(ts->input_dev, KEY_APPSELECT, 0); +#ifndef HX_PROTOCOL_A + input_sync(ts->input_dev); +#endif + } +} + +void himax_finger_report_key(struct himax_ts_data *ts) +{ + if (ts->hx_point_num != 0) { + /*Touch KEY*/ + if ((tpd_key_old != 0x00) && (tpd_key == 0x00)) { + /* temp_x[0] = 0xFFFF; + * temp_y[0] = 0xFFFF; + * temp_x[1] = 0xFFFF; + * temp_y[1] = 0xFFFF; + */ + hx_touch_data->finger_on = 0; +#ifdef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 0); +#endif + himax_key_report_operation(tpd_key, ts); +#ifndef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 0); +#endif + } + input_sync(ts->input_dev); + } +} + +void himax_finger_leave_key(struct himax_ts_data *ts) +{ + if (tpd_key != 0x00) { + hx_touch_data->finger_on = 1; +#ifdef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif + himax_key_report_operation(tpd_key, ts); +#ifndef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif + } else if ((tpd_key_old != 0x00) && (tpd_key == 0x00)) { + hx_touch_data->finger_on = 0; +#ifdef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 0); +#endif + himax_key_report_operation(tpd_key, ts); +#ifndef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 0); +#endif + } + input_sync(ts->input_dev); +} + +static void himax_report_key(struct himax_ts_data *ts) +{ + if (ts->hx_point_num != 0) { /* Touch KEY */ + himax_finger_report_key(ts); + } else { /* Key */ + himax_finger_leave_key(ts); + } + + tpd_key_old = tpd_key; + Last_EN_NoiseFilter = EN_NoiseFilter; +} +#endif + +/* start report_point*/ +static void himax_finger_report(struct himax_ts_data *ts) +{ + int i = 0; + bool valid = false; + + + if (g_ts_dbg != 0) { + I("%s:start\n", __func__); + I("hx_touch_data->finger_num=%d\n", hx_touch_data->finger_num); + } + for (i = 0; i < ts->nFinger_support; i++) { + if (g_target_report_data->x[i] >= 0 && g_target_report_data->x[i] <= ts->pdata->abs_x_max && g_target_report_data->y[i] >= 0 && g_target_report_data->y[i] <= ts->pdata->abs_y_max) + valid = true; + else + valid = false; + if (g_ts_dbg != 0) + I("valid=%d\n", valid); + if (valid) { + if (g_ts_dbg != 0) + I("g_target_report_data->x[i]=%d, g_target_report_data->y[i]=%d, g_target_report_data->w[i]=%d\n", g_target_report_data->x[i], g_target_report_data->y[i], g_target_report_data->w[i]); +#ifndef HX_PROTOCOL_A + input_mt_slot(ts->input_dev, i); +#else + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, g_target_report_data->w[i]); +#ifndef HX_PROTOCOL_A + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, g_target_report_data->w[i]); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, g_target_report_data->w[i]); +#else + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, i); +#endif + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, g_target_report_data->x[i]); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, g_target_report_data->y[i]); +#ifndef HX_PROTOCOL_A + ts->last_slot = i; + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); +#else + input_mt_sync(ts->input_dev); +#endif + } else { +#ifndef HX_PROTOCOL_A + 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_WIDTH_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 + } + } +#ifndef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif + input_sync(ts->input_dev); + +#if defined(HX_PEN_FUNC_EN) + valid = false; + + if (g_target_report_data->p_x[0] >= 0 && g_target_report_data->p_x[0] <= ts->pdata->abs_x_max + && g_target_report_data->p_y[0] >= 0 && g_target_report_data->p_y[0] <= ts->pdata->abs_y_max + && (g_target_report_data->p_on[0] == 1)) + valid = true; + else + valid = false; + if (g_ts_dbg != 0) + I("pen valid=%d\n", valid); + if (valid) {/*Pen down*/ + if (g_ts_dbg != 0) + I("p_x[i]=%d, p_y[i]=%d, p_w[i]=%d\n", g_target_report_data->p_x[0], g_target_report_data->p_y[0], g_target_report_data->p_w[0]); + + input_report_abs(ts->hx_pen_dev, ABS_X, g_target_report_data->p_x[0]); + input_report_abs(ts->hx_pen_dev, ABS_Y, g_target_report_data->p_y[0]); + input_report_key(ts->hx_pen_dev, BTN_STYLUS, 1); + input_report_abs(ts->hx_pen_dev, ABS_TILT_X, g_target_report_data->p_tilt_x[0]); + input_report_abs(ts->hx_pen_dev, ABS_TILT_Y, g_target_report_data->p_tilt_y[0]); + input_report_key(ts->hx_pen_dev, BTN_TOOL_PEN, 1); + if (g_target_report_data->p_hover[0] == 0) { + input_report_key(ts->hx_pen_dev, BTN_TOUCH, 1); + input_report_abs(ts->hx_pen_dev, ABS_DISTANCE, 0); + input_report_abs(ts->hx_pen_dev, ABS_PRESSURE, g_target_report_data->p_w[0]); + } else { + input_report_key(ts->hx_pen_dev, BTN_TOUCH, 0); + input_report_abs(ts->hx_pen_dev, ABS_DISTANCE, 1); + input_report_abs(ts->hx_pen_dev, ABS_PRESSURE, 0); + } + } else {/*Pen up*/ + input_report_key(ts->hx_pen_dev, BTN_STYLUS, 0); + input_report_key(ts->hx_pen_dev, BTN_TOUCH, 0); + input_report_abs(ts->hx_pen_dev, ABS_PRESSURE, 0); + input_sync(ts->hx_pen_dev); + + input_report_abs(ts->hx_pen_dev, ABS_DISTANCE, 0); + input_report_key(ts->hx_pen_dev, BTN_TOOL_RUBBER, 0); + input_report_key(ts->hx_pen_dev, BTN_TOOL_PEN, 0); + input_report_abs(ts->hx_pen_dev, ABS_PRESSURE, 0); + } + input_sync(ts->hx_pen_dev); + +#endif + + if (g_ts_dbg != 0) + I("%s:end\n", __func__); +} + + +#ifdef HIMAX_PALM_SENSOR_EN +static void himax_palm_detect_func(struct himax_ts_data *ts, bool detected) { + if (detected) { + I("palm detected\n"); + input_report_abs(ts->palm_sensor_pdata->input_sensor_dev, ABS_DISTANCE, 1); + } else { + I("palm leave\n"); + input_report_abs(ts->palm_sensor_pdata->input_sensor_dev, ABS_DISTANCE, 0); + } + input_sync(ts->palm_sensor_pdata->input_sensor_dev); +} +#endif + +static void himax_finger_leave(struct himax_ts_data *ts) +{ +#ifndef HX_PROTOCOL_A + int32_t loop_i = 0; +#endif + + if (g_ts_dbg != 0) + I("%s: start!\n", __func__); + +#ifndef HIMAX_PALM_SENSOR_EN +#if defined(HX_PALM_REPORT) + if (himax_palm_detect(hx_touch_data->hx_coord_buf) == PALM_REPORT) { + I(" %s HX_PALM_REPORT KEY power event press\n", __func__); + input_report_key(ts->input_dev, KEY_POWER, 1); + input_sync(ts->input_dev); + msleep(100); + + I(" %s HX_PALM_REPORT KEY power event release\n", __func__); + input_report_key(ts->input_dev, KEY_POWER, 0); + input_sync(ts->input_dev); + return; + } +#endif +#endif + + hx_touch_data->finger_on = 0; + g_target_report_data->finger_on = 0; + g_target_report_data->finger_num = 0; + AA_press = 0; + +#ifdef HX_PROTOCOL_A + input_mt_sync(ts->input_dev); +#endif +#ifndef HX_PROTOCOL_A + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + input_mt_slot(ts->input_dev, loop_i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_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 + if (ts->pre_finger_mask > 0) + ts->pre_finger_mask = 0; + + if (ts->first_pressed == 1) { + ts->first_pressed = 2; + I("E1@%d, %d\n", ts->pre_finger_data[0][0], ts->pre_finger_data[0][1]); + } + + /*if (ts->debug_log_level & BIT(1)) */ + /*himax_log_touch_event(x, y, w, loop_i, EN_NoiseFilter, HX_FINGER_LEAVE); */ + + input_report_key(ts->input_dev, BTN_TOUCH, 0); + input_sync(ts->input_dev); + +#if defined(HX_PEN_FUNC_EN) + input_report_key(ts->hx_pen_dev, BTN_STYLUS, 0); + input_report_key(ts->hx_pen_dev, BTN_TOUCH, 0); + input_report_abs(ts->hx_pen_dev, ABS_PRESSURE, 0); + input_sync(ts->hx_pen_dev); + + input_report_abs(ts->hx_pen_dev, ABS_DISTANCE, 0); + input_report_abs(ts->hx_pen_dev, ABS_TILT_X, 0); + input_report_abs(ts->hx_pen_dev, ABS_TILT_Y, 0); + input_report_key(ts->hx_pen_dev, BTN_TOOL_RUBBER, 0); + input_report_key(ts->hx_pen_dev, BTN_TOOL_PEN, 0); + input_sync(ts->hx_pen_dev); +#endif + + if (g_ts_dbg != 0) + I("%s: end!\n", __func__); + + +} + +static void himax_report_points(struct himax_ts_data *ts) +{ + if (g_ts_dbg != 0) + I("%s: start!\n", __func__); + +#if defined(HX_PALM_REPORT) && defined(HIMAX_PALM_SENSOR_EN) + if (ts->palm_detection_enabled) { + if (himax_palm_detect(hx_touch_data->hx_coord_buf) == PALM_REPORT) { + del_timer(&ts->palm_release_fimer); + himax_palm_detect_func(ts, true); + return; + } else if (himax_palm_detect(hx_touch_data->hx_coord_buf) == PALM_LEAVE_REPORT) { + mod_timer(&ts->palm_release_fimer, + jiffies + msecs_to_jiffies(ts->palm_release_delay_ms)); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&ts->palm_gesture_wakelock, + ts->palm_release_delay_ms); +#else + PM_WAKEUP_EVENT(ts->palm_gesture_wakelock, + ts->palm_release_delay_ms); +#endif + return; + } else if (ts->suspended) + return; + } +#endif + if (ts->hx_point_num != 0) + himax_finger_report(ts); + else + himax_finger_leave(ts); + + Last_EN_NoiseFilter = EN_NoiseFilter; + + if (g_ts_dbg != 0) + I("%s: end!\n", __func__); +} +/* end report_points*/ + +int himax_report_data(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + if (ts_path == HX_REPORT_COORD || ts_path == HX_REPORT_COORD_RAWDATA) { + /* Touch Point information */ + himax_report_points(ts); + +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) + /*report key(question mark)*/ + if (tpd_key && tpd_key_old) + himax_report_key(ts); + +#endif +#ifdef HX_SMART_WAKEUP + } else if (ts_path == HX_REPORT_SMWP_EVENT) { + himax_wake_event_report(); +#endif + } else { + E("%s:Fail Path!\n", __func__); + ts_status = HX_PATH_FAIL; + } + + if (g_ts_dbg != 0) + I("%s: END, ts_status=%d!\n", __func__, ts_status); + return ts_status; +} +/* end report_data */ + +static int himax_ts_operation(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + uint8_t hw_reset_check[2]; + + memset(ts->xfer_buff, 0x00, 128 * sizeof(uint8_t)); + memset(hw_reset_check, 0x00, sizeof(hw_reset_check)); + + ts_status = himax_touch_get(ts, ts->xfer_buff, ts_path, ts_status); + if (ts_status == HX_TS_GET_DATA_FAIL) + goto END_FUNCTION; + + ts_status = himax_distribute_touch_data(ts->xfer_buff, ts_path, ts_status); + ts_status = himax_err_ctrl(ts, ts->xfer_buff, ts_path, ts_status); + if (ts_status == HX_REPORT_DATA || ts_status == HX_TS_NORMAL_END) + ts_status = himax_parse_report_data(ts, ts_path, ts_status); + else + goto END_FUNCTION; + + + ts_status = himax_report_data(ts, ts_path, ts_status); + + +END_FUNCTION: + return ts_status; +} + +#ifdef HIMAX_PALM_SENSOR_EN +static void himax_palm_sensor_release_timer_handler(unsigned long data) +{ + himax_palm_detect_func(private_ts, false); +} + +static int _himax_palm_detect_sensor_set_enable(unsigned int enable) +{ + I("Gesture set enable %d!", enable); +/* + * If palm detect function is enabled, interrupt will not disable, IC works in + * normal mode. But in case touch event is reported to input subsystem, skip + * touch event when suspend flag is true. So input subsystem will not take + * wakelock because no one report event. + * In this case, we still need read data from IC, so AP can not enter suspend. + */ + if (enable == 1) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock(&private_ts->palm_gesture_read_wakelock); +#else + PM_STAY_AWAKE(private_ts->palm_gesture_read_wakelock); +#endif + g_core_fp.fp_palm_detection_function(1); + private_ts->palm_detection_enabled = true; + } else if (enable == 0) { + if (timer_pending(&private_ts->palm_release_fimer)) { + himax_palm_detect_func(private_ts, false); + del_timer(&private_ts->palm_release_fimer); + } + g_core_fp.fp_palm_detection_function(0); + private_ts->palm_detection_enabled = false; +#ifdef CONFIG_HAS_WAKELOCK + wake_unlock(&private_ts->palm_gesture_read_wakelock); +#else + PM_RELAX(private_ts->palm_gesture_read_wakelock); +#endif + } else + E("unknown enable symbol\n"); + + return 0; +} + +static int himax_palm_detect_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + int ret = 0; + + mutex_lock(&private_ts->suspend_resume_mutex); + if (!private_ts->suspended) + ret = _himax_palm_detect_sensor_set_enable(enable); + else { + I("Gesture lazy set enable %d!", enable); + if (enable == 1) + private_ts->palm_detection_lazy_set = PALM_SENSOR_LAZY_SET_ENABLE; + else if (enable == 0) + private_ts->palm_detection_lazy_set = PALM_SENSOR_LAZY_SET_DISABLE; + else + E("unknown enable symbol\n"); + } + mutex_unlock(&private_ts->suspend_resume_mutex); + himax_palm_detect_func(private_ts, false); + + return ret; +} + +static int himax_palm_detect_sensor_init(struct himax_ts_data *data) +{ + struct himax_sensor_platform_data *sensor_pdata; + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + E("Failed to allocate device"); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct himax_sensor_platform_data), + GFP_KERNEL); + if (!sensor_pdata) { + E("Failed to allocate memory"); + goto free_sensor_pdata; + } + data->palm_sensor_pdata = sensor_pdata; + + __set_bit(EV_ABS, sensor_input_dev->evbit); + __set_bit(EV_SYN, sensor_input_dev->evbit); + input_set_abs_params(sensor_input_dev, ABS_DISTANCE, + 0, 5, 0, 0); + sensor_input_dev->name = "palm_detect"; + data->palm_sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + E("Unable to register device, err=%d", err); + goto free_sensor_input_dev; + } + + sensor_pdata->ps_cdev = palm_sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = himax_palm_detect_sensor_set_enable; + sensor_pdata->data = data; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&data->palm_gesture_wakelock, WAKE_LOCK_SUSPEND, "palm_detect_wl"); +#else + PM_WAKEUP_REGISTER(data->dev, data->palm_gesture_wakelock, "palm_detect_wl") + if (!data->palm_gesture_wakelock) { + E(" failed to allocate palm_detect_wl wakeup source\n"); + goto err_wakeup_source_register_failed; + } +#endif +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&data->palm_gesture_read_wakelock, WAKE_LOCK_SUSPEND, "palm_read_wl"); +#else + PM_WAKEUP_REGISTER(data->dev, data->palm_gesture_read_wakelock, "palm_read_wl"); + if (!data->palm_gesture_read_wakelock) { + E(" failed to allocate palm_read_wl wakeup source\n"); + goto err_wakeup_source_register_failed; + } +#endif + + data->palm_release_fimer.function = himax_palm_sensor_release_timer_handler; + init_timer(&data->palm_release_fimer); + data->palm_release_delay_ms = 850; + + return 0; + +#ifndef CONFIG_HAS_WAKELOCK +err_wakeup_source_register_failed: +#endif +unregister_sensor_input_device: + input_unregister_device(data->palm_sensor_pdata->input_sensor_dev); +free_sensor_input_dev: + input_free_device(data->palm_sensor_pdata->input_sensor_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, sensor_pdata); + data->palm_sensor_pdata= NULL; +exit: + return 1; +} + +int himax_palm_detect_sensor_remove(struct himax_ts_data *data) +{ + sensors_classdev_unregister(&data->palm_sensor_pdata->ps_cdev); + input_unregister_device(data->palm_sensor_pdata->input_sensor_dev); + devm_kfree(&data->palm_sensor_pdata->input_sensor_dev->dev, + data->palm_sensor_pdata); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&data->palm_gesture_wakelock); +#else + PM_WAKEUP_UNREGISTER(data->palm_gesture_wakelock); +#endif +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&data->palm_gesture_read_wakelock); +#else + PM_WAKEUP_UNREGISTER(data->palm_gesture_read_wakelock); +#endif + data->palm_sensor_pdata = NULL; + data->palm_detection_enabled = false; + return 0; +} +#endif + +#ifdef HIMAX_V2_SENSOR_EN +static int himax_tap_detect_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + int ret = 0; + struct himax_ts_data *ts = private_ts; + + if (enable) { + ts->SMWP_enable = 1; + ts->gesture_cust_en[0] = 1; + I("%s,here:%d, gesture_cust_en[0] = %d\n", __func__, __LINE__, ts->gesture_cust_en[0]); + } else { + ts->SMWP_enable = 0; + ts->gesture_cust_en[0] = 0; + I("%s,here:%d, gesture_cust_en[0] = %d\n", __func__, __LINE__, ts->gesture_cust_en[0]); + } + + g_core_fp.fp_set_SMWP_enable(ts->SMWP_enable, ts->suspended); + HX_SMWP_EN = ts->SMWP_enable; + I("%s: SMART_WAKEUP_enable = %d.\n", __func__, HX_SMWP_EN); + + return ret; +} + +static int himax_tap_detect_sensor_init(struct himax_ts_data *data) +{ + struct himax_tap_sensor_platform_data *sensor_pdata; + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + E("Failed to allocate device"); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct himax_tap_sensor_platform_data), + GFP_KERNEL); + if (!sensor_pdata) { + E("Failed to allocate memory"); + goto free_sensor_pdata; + } + data->sensor_pdata = sensor_pdata; + + __set_bit(EV_SYN, sensor_input_dev->evbit); + + if (data->report_gesture_key) { + __set_bit(EV_KEY, sensor_input_dev->evbit); + __set_bit(KEY_F1, sensor_input_dev->keybit); + } else { + __set_bit(EV_ABS, sensor_input_dev->evbit); + input_set_abs_params(sensor_input_dev, ABS_DISTANCE, + 0, REPORT_MAX_COUNT, 0, 0); + } + sensor_input_dev->name = "double-tap"; + data->sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + E("Unable to register device, err=%d", err); + goto free_sensor_input_dev; + } + + sensor_pdata->ps_cdev = sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = himax_tap_detect_sensor_set_enable; + sensor_pdata->data = data; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&data->tap_gesture_wakelock, WAKE_LOCK_SUSPEND, "dt-wake-lock"); +#else + PM_WAKEUP_REGISTER(data->dev, data->tap_gesture_wakelock, "dt-wake-lock"); + if (!data->tap_gesture_wakelock) { + E(" failed to allocate dt-wake-lock wakeup source\n"); + goto err_wakeup_source_register_failed; + } +#endif + + return 0; + +#ifndef CONFIG_HAS_WAKELOCK +err_wakeup_source_register_failed: +#endif +unregister_sensor_input_device: + input_unregister_device(data->sensor_pdata->input_sensor_dev); +free_sensor_input_dev: + input_free_device(data->sensor_pdata->input_sensor_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, sensor_pdata); + data->sensor_pdata= NULL; +exit: + return 1; +} + +int himax_tap_detect_sensor_remove(struct himax_ts_data *data) +{ + sensors_classdev_unregister(&data->sensor_pdata->ps_cdev); + input_unregister_device(data->sensor_pdata->input_sensor_dev); + devm_kfree(&data->sensor_pdata->input_sensor_dev->dev, + data->sensor_pdata); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&data->tap_gesture_wakelock); +#else + PM_WAKEUP_UNREGISTER(data->tap_gesture_wakelock); +#endif + data->sensor_pdata = NULL; + return 0; +} +#endif + +void himax_ts_work(struct himax_ts_data *ts) +{ + + int ts_status = HX_TS_NORMAL_END; + int ts_path = 0; + + if (debug_data != NULL) + debug_data->fp_ts_dbg_func(ts, HX_FINGER_ON); + +#if defined(HX_USB_DETECT_GLOBAL) + himax_cable_detect_func(false); +#endif + + ts_path = himax_ts_work_status(ts); + switch (ts_path) { + case HX_REPORT_COORD: + ts_status = himax_ts_operation(ts, ts_path, ts_status); + break; + case HX_REPORT_SMWP_EVENT: + ts_status = himax_ts_operation(ts, ts_path, ts_status); + break; + case HX_REPORT_COORD_RAWDATA: + ts_status = himax_ts_operation(ts, ts_path, ts_status); + break; + default: + E("%s:Path Fault! value=%d\n", __func__, ts_path); + goto END_FUNCTION; + } + + if (ts_status == HX_TS_GET_DATA_FAIL) + goto GET_TOUCH_FAIL; + else + goto END_FUNCTION; + +GET_TOUCH_FAIL: + I("%s: Now reset the Touch chip.\n", __func__); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, true); +#else + g_core_fp.fp_system_reset(); +#endif +#if defined(HX_ZERO_FLASH) && defined(HX83102D) + if (g_core_fp.fp_0f_reload_to_active) + g_core_fp.fp_0f_reload_to_active(); +#endif +END_FUNCTION: + if (debug_data != NULL) + debug_data->fp_ts_dbg_func(ts, HX_FINGER_LEAVE); + +} +/*end ts_work*/ +enum hrtimer_restart himax_ts_timer_func(struct hrtimer *timer) +{ + struct himax_ts_data *ts; + + + ts = container_of(timer, struct himax_ts_data, timer); + queue_work(ts->himax_wq, &ts->work); + hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +#if defined(HX_USB_DETECT_CALLBACK) +static void himax_cable_tp_status_handler_func(int connect_status) +{ + struct himax_ts_data *ts; + + I("Touch: cable change to %d\n", connect_status); + + ts = private_ts; + + if (ts->cable_config) { + if (!atomic_read(&ts->suspend_mode)) { + if (connect_status != ts->usb_connected) { + if (connect_status) { + ts->cable_config[1] = 0x01; + ts->usb_connected = 0x01; + } else { + ts->cable_config[1] = 0x00; + ts->usb_connected = 0x00; + } + + himax_bus_master_write(ts->cable_config, + sizeof(ts->cable_config), HIMAX_I2C_RETRY_TIMES); + I("%s: Cable status change: 0x%2.2X\n", __func__, ts->cable_config[1]); + } else + I("%s: Cable status is the same as previous one, ignore.\n", __func__); + } else { + if (connect_status) + ts->usb_connected = 0x01; + else + ts->usb_connected = 0x00; + + I("%s: Cable status remembered: 0x%2.2X\n", __func__, ts->usb_connected); + } + } +} + +static struct t_cable_status_notifier himax_cable_status_handler = { + .name = "usb_tp_connected", + .func = himax_cable_tp_status_handler_func, +}; + +#endif + +#ifdef HX_AUTO_UPDATE_FW +static void himax_update_register(struct work_struct *work) +{ + I(" %s in\n", __func__); + + if (i_get_FW() != 0) + return; + + if (g_auto_update_flag == true) { + I("Update FW Directly"); + goto UPDATE_FW; + } + + if (himax_auto_update_check() != 0) { + I("%s:Don't run auto update fw, so free allocated!\n", __func__); + kfree(i_CTPM_FW); + i_CTPM_FW = NULL; + return; + } + +UPDATE_FW: + if (i_update_FW() <= 0) + I("Auto update FW fail!\n"); + else + I("It have Updated\n"); + +} +#endif + +#ifndef HX_ZERO_FLASH +static int hx_chk_flash_sts(void) +{ + int rslt = 0; + uint32_t size = 0; + + I("%s: Entering\n", __func__); + if (g_core_fp._diff_overlay_flash() == 1) + size = FW_SIZE_128k; + else + size = FW_SIZE_64k; + + +#ifdef HX_AUTO_UPDATE_FW + g_auto_update_flag = (!g_core_fp.fp_calculateChecksum(false, size)); + g_auto_update_flag |= g_core_fp.fp_flash_lastdata_check(size); +#else + g_core_fp.fp_calculateChecksum(false, size); + g_core_fp.fp_flash_lastdata_check(size); +#endif + + return rslt; +} +#endif + +#ifdef HIMAX_CONFIG_PANEL_NOTIFICATIONS +static void himax_panel_register(struct work_struct *work) +{ + int ret = 0; + + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work_panel.work); + + I("%s in\n", __func__); + + ts->panel_notif.notifier_call = panel_notifier_callback; + ret = register_panel_notifier(&ts->panel_notif); + + if (ret) + E("Unable to register panel_notifier: %d\n", ret); +} +#endif + +#if defined(CONFIG_DRM) +static void himax_fb_register(struct work_struct *work) +{ + int ret = 0; + + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work_att.work); + + + I("%s in\n", __func__); + ts->fb_notif.notifier_call = drm_notifier_callback; + ret = msm_drm_register_client(&ts->fb_notif); + + if (ret) + E("Unable to register fb_notifier: %d\n", ret); +} +#elif defined(CONFIG_FB) +static void himax_fb_register(struct work_struct *work) +{ + int ret = 0; + + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work_att.work); + + + I("%s in\n", __func__); + ts->fb_notif.notifier_call = fb_notifier_callback; + ret = fb_register_client(&ts->fb_notif); + + if (ret) + E("Unable to register fb_notifier: %d\n", ret); +} +#endif + +#include +#include + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!ts) { + pr_err("cannot get himax_ts_data pointer\n"); + return (ssize_t)0; + } + path = kobject_get_path(&ts->dev->kobj, GFP_KERNEL); + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "himax"); +} + +/* Attribute: vendor (RO) */ +static ssize_t ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + int buildid; + + buildid = (ic_data->vendor_cid_maj_ver << 8 | + ic_data->vendor_cid_min_ver); + return scnprintf(buf, PAGE_SIZE, "%s%s\n%s%04x\n%s%04x\n", + "Product ID: ", ts->chip_name, + "Build ID: ", buildid ? buildid : ts->build_id, + "Config ID: ", ic_data->vendor_touch_cfg_ver ? ic_data->vendor_touch_cfg_ver : ts->config_id); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_RO(ic_ver), + __ATTR_NULL +}; + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +static int himax_sysfs_touchscreen( + struct himax_ts_data *ts, bool create) +{ + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + static struct class *touchscreen_class; + static struct device *ts_class_dev; + static int minor; + + if (create) { + minor = input_get_new_minor(0x90, + 1, false); + if (minor < 0) + minor = input_get_new_minor(TSDEV_MINOR_BASE, + TSDEV_MINOR_MAX, true); + pr_info("assigned minor %d\n", minor); + + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + +#ifdef HX_112F_SET + ts_class_dev = device_create(touchscreen_class, NULL, + MKDEV(INPUT_MAJOR, minor), + ts, "%s", ts->chip_name); +#else + ts_class_dev = device_create(touchscreen_class, NULL, + MKDEV(INPUT_MAJOR, minor), + ts, ts->chip_name); +#endif + if (IS_ERR(ts_class_dev)) { + error = PTR_ERR(ts_class_dev); + ts_class_dev = NULL; + return error; + } + for (i = 0; attrs[i].attr.name != NULL; ++i) { + error = device_create_file(ts_class_dev, &attrs[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + } else { + if (!touchscreen_class || !ts_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(ts_class_dev, &attrs[i]); + + device_unregister(ts_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor)); + ts_class_dev = NULL; + class_unregister(touchscreen_class); + pr_err("error creating touchscreen class\n"); + + return -ENODEV; +} + +#if defined(HX_USB_DETECT_GLOBAL) +static int charger_notifier_callback(struct notifier_block *nb, + unsigned long val, void *v) { + int ret = 0; + struct power_supply *psy = NULL; + struct himax_ts_data *ts = container_of(nb, struct himax_ts_data, charger_notif); + union power_supply_propval prop; + + psy= power_supply_get_by_name("usb"); + if (!psy) { + E("Couldn't get usbpsy\n"); + return -EINVAL; + } + if (!strcmp(psy->desc->name, "usb")) { + if (psy && ts && val == POWER_SUPPLY_PROP_STATUS) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,&prop); + if (ret < 0) { + E("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret); + return ret; + } else { + USB_detect_flag = prop.intval; + //E("usb prop.intval =%d\n", prop.intval); + } + } + } + return 0; +} +#endif + +#if defined(HX_RESUME_SET_FW) +void himax_resume_work_func(struct work_struct *work) +{ + struct himax_ts_data *ts = private_ts; + himax_chip_common_resume(ts); + return; +} +#endif +int himax_chip_common_init(void) +{ + + int i = 0, ret = 0, err = 0, idx = 0; + struct himax_ts_data *ts = private_ts; + struct himax_i2c_platform_data *pdata; + struct himax_chip_entry *entry; + + I("Prepare kernel fp\n"); + kp_getname_kernel = (void *)kallsyms_lookup_name("getname_kernel"); + if (!kp_getname_kernel) { + E("prepare kp_getname_kernel failed!\n"); + /*goto err_dt_platform_data_fail;*/ + } + kp_file_open_name = (void *)kallsyms_lookup_name("file_open_name"); + if (!kp_file_open_name) { + E("prepare kp_file_open_name failed!\n"); + goto err_dt_platform_data_fail; + } +#if defined(__EMBEDDED_FW__) + g_embedded_fw.size = (size_t)_binary___Himax_firmware_bin_end - + (size_t)_binary___Himax_firmware_bin_start; +#endif + ts->xfer_buff = devm_kzalloc(ts->dev, 128 * sizeof(uint8_t), GFP_KERNEL); + if (ts->xfer_buff == NULL) { + err = -ENOMEM; + goto err_dt_platform_data_fail; + } + + I("PDATA START\n"); + pdata = kzalloc(sizeof(struct himax_i2c_platform_data), GFP_KERNEL); + + if (pdata == NULL) { /*Allocate Platform data space*/ + err = -ENOMEM; + goto err_dt_platform_data_fail; + } + + I("ic_data START\n"); + ic_data = kzalloc(sizeof(struct himax_ic_data), GFP_KERNEL); + if (ic_data == NULL) { /*Allocate IC data space*/ + err = -ENOMEM; + goto err_dt_ic_data_fail; + } + + /* allocate report data */ + hx_touch_data = kzalloc(sizeof(struct himax_report_data), GFP_KERNEL); + if (hx_touch_data == NULL) { + err = -ENOMEM; + goto err_alloc_touch_data_failed; + } + + if (himax_parse_dt(ts, pdata) < 0) { + I(" pdata is NULL for DT\n"); + goto err_alloc_dt_pdata_failed; + } + +#ifdef HX_RST_PIN_FUNC + ts->rst_gpio = pdata->gpio_reset; +#endif + if (himax_gpio_power_config(pdata)) { + err = -1; + E("%s: himax_gpio_power_config failed, stop Himax probe\n", __func__); + goto err_himax_gpio_power_config_failed; + } +#ifndef CONFIG_OF + + if (pdata->power) { + ret = pdata->power(1); + + if (ret < 0) { + E("%s: power on failed\n", __func__); + goto err_power_failed; + } + } + +#endif + + i_CTPM_firmware_name = kzalloc(HIMAX_FILE_NAME_LENGTH, GFP_KERNEL); + if (!i_CTPM_firmware_name) { + E("%s: alloc i_CTPM_firmware_name failed\n", __func__); + goto firmware_name_alloc_failed; + } + +#if defined(HX_112F_SET) + snprintf(i_CTPM_firmware_name, HIMAX_FILE_NAME_LENGTH, "%s_Himax_firmware.bin", + pdata->panel_supplier); +#else + if (pdata->panel_supplier) + snprintf(i_CTPM_firmware_name, HIMAX_FILE_NAME_LENGTH, "%s_Himax_firmware.bin", + pdata->panel_supplier); + else + snprintf(i_CTPM_firmware_name, HIMAX_FILE_NAME_LENGTH, "Himax_firmware.bin"); +#endif + + g_hx_chip_inited = 0; + idx = himax_get_ksym_idx(); + if (idx >= 0) { + if (isEmpty(idx) != 0) { + I("%s: no chip registered, please insmod ic.ko!\n", + __func__); + goto error_ic_detect_failed; + } + entry = get_chip_entry_by_index(idx); + + for (i = 0; i < entry->hx_ic_dt_num; i++) { + if (entry->core_chip_dt[i].fp_chip_detect != NULL) { + if (entry->core_chip_dt[i].fp_chip_detect() + == true) { + I("%s: chip detect found! list_num=%d\n" + , __func__, i); + goto found_hx_chip; + } else { + I("%s: num=%d chip detect NOT found! goto Next\n", __func__, i); + continue; + } + } + } + } else { + I("%s: No available chip exist!\n", __func__); + goto error_ic_detect_failed; + } + + if (i == entry->hx_ic_dt_num) { + E("%s: chip detect failed!\n", __func__); + goto error_ic_detect_failed; + } +found_hx_chip: + if (g_core_fp.fp_chip_init != NULL) { + g_core_fp.fp_chip_init(); + } else { + E("%s: function point of chip_init is NULL!\n", __func__); + goto error_ic_detect_failed; + } + + if (pdata->virtual_key) + ts->button = pdata->virtual_key; + +#ifndef HX_ZERO_FLASH + g_core_fp.fp_read_FW_ver(); + hx_chk_flash_sts(); +#endif + +#ifdef HX_AUTO_UPDATE_FW + if (g_auto_update_flag) + goto FW_force_upgrade; +#endif + + +#ifdef HX_AUTO_UPDATE_FW +FW_force_upgrade: + ts->himax_update_wq = create_singlethread_workqueue("HMX_update_reuqest"); + if (!ts->himax_update_wq) { + E(" allocate himax_update_wq failed\n"); + err = -ENOMEM; + goto err_update_wq_failed; + } + INIT_DELAYED_WORK(&ts->work_update, himax_update_register); + queue_delayed_work(ts->himax_update_wq, &ts->work_update, msecs_to_jiffies(2000)); +#endif +#ifdef HX_ZERO_FLASH + g_auto_update_flag = true; + ts->himax_0f_update_wq = create_singlethread_workqueue("HMX_0f_update_reuqest"); + if (!ts->himax_0f_update_wq) { + E(" allocate himax_0f_update_wq failed\n"); + err = -ENOMEM; + goto err_0f_update_wq_failed; + } + INIT_DELAYED_WORK(&ts->work_0f_update, g_core_fp.fp_0f_operation); + queue_delayed_work(ts->himax_0f_update_wq, &ts->work_0f_update, msecs_to_jiffies(2000)); +#endif + +#ifdef HX_RESUME_SET_FW + ts->ts_int_workqueue = create_singlethread_workqueue("himax_ts_resume_wq"); + if (!ts->ts_int_workqueue) { + E("%s: create ts_resume workqueue failed\n", __func__); + goto err_create_ts_resume_wq_failed; + } +#if defined(__HIMAX_HX83102D_MOD__) + INIT_DELAYED_WORK(&ts->ts_int_work, himax_resume_work_func); +#else + INIT_WORK(&ts->ts_int_work, himax_resume_work_func); +#endif +#endif + /*Himax Power On and Load Config*/ + if (himax_loadSensorConfig(pdata)) { + E("%s: Load Sesnsor configuration failed, unload driver.\n", __func__); + goto err_detect_failed; + } + + g_core_fp.fp_power_on_init(); + calculate_point_number(); + +#ifdef CONFIG_OF + ts->power = pdata->power; +#endif + ts->pdata = pdata; + /*calculate the i2c data size*/ + calcDataSize(); + I("%s: calcDataSize complete\n", __func__); +#ifdef CONFIG_OF + ts->pdata->abs_pressure_min = 0; + ts->pdata->abs_pressure_max = 200; + ts->pdata->abs_width_min = 0; + ts->pdata->abs_width_max = 200; + pdata->cable_config[0] = 0xF0; + pdata->cable_config[1] = 0x00; +#endif + ts->suspended = false; + mutex_init(&ts->suspend_resume_mutex); +#if defined(HX_USB_DETECT_CALLBACK) || defined(HX_USB_DETECT_GLOBAL) + ts->usb_connected = 0x00; + ts->cable_config = pdata->cable_config; + ts->charger_notif.notifier_call = charger_notifier_callback; + ret = power_supply_reg_notifier(&ts->charger_notif); + if (ret) { + E("Unable to register charger_notifier: %d\n",ret); + goto err_register_charger_notify_failed; + } +#endif +#ifdef HX_PROTOCOL_A + ts->protocol_type = PROTOCOL_TYPE_A; +#else + ts->protocol_type = PROTOCOL_TYPE_B; +#endif + I("%s: Use Protocol Type %c\n", __func__, + ts->protocol_type == PROTOCOL_TYPE_A ? 'A' : 'B'); + ret = himax_input_register(ts); + + if (ret) { + E("%s: Unable to register %s input device\n", + __func__, ts->input_dev->name); + goto err_input_register_device_failed; + } + + ts->initialized = true; +#if defined(CONFIG_FB) || defined(CONFIG_DRM) + ts->himax_att_wq = create_singlethread_workqueue("HMX_ATT_reuqest"); + + if (!ts->himax_att_wq) { + E(" allocate himax_att_wq failed\n"); + err = -ENOMEM; + goto err_get_intr_bit_failed; + } + + INIT_DELAYED_WORK(&ts->work_att, himax_fb_register); + queue_delayed_work(ts->himax_att_wq, &ts->work_att, msecs_to_jiffies(15000)); +#endif + +#ifdef HIMAX_CONFIG_PANEL_NOTIFICATIONS + ts->himax_panel_wq = create_singlethread_workqueue("HMX_panel_reuqest"); + + if (!ts->himax_panel_wq) { + E(" allocate himax_panel_wq failed\n"); + err = -ENOMEM; + goto err_panel_notifier_reg_failed; + } + + INIT_DELAYED_WORK(&ts->work_panel, himax_panel_register); + queue_delayed_work(ts->himax_panel_wq, &ts->work_panel, msecs_to_jiffies(15000)); +#endif + +#ifdef HX_SMART_WAKEUP + ts->SMWP_enable = 0; + PM_WAKEUP_REGISTER(ts->dev, ts->ts_SMWP_wake_lock, HIMAX_common_NAME); + if (!ts->ts_SMWP_wake_lock) { + E(" failed to allocate %s wakeup source\n", HIMAX_common_NAME); + goto err_wakeup_source_register_failed; + } +#endif +#ifdef HX_HIGH_SENSE + ts->HSEN_enable = 0; +#endif + + /*touch data init*/ + err = himax_report_data_init(); + + if (err) + goto err_report_data_init_failed; + + if (himax_common_proc_init()) { + E(" %s: himax_common proc_init failed!\n", __func__); + goto err_creat_proc_file_failed; + } + himax_sysfs_touchscreen(ts, true); + +#if defined(HX_USB_DETECT_CALLBACK) + + if (ts->cable_config) + cable_detect_register_notifier(&himax_cable_status_handler); + +#endif + +#ifdef HIMAX_PALM_SENSOR_EN + if (himax_palm_detect_sensor_init(ts)) { + E(" %s: himax_palm_detect_sensor_init failed!\n", __func__); + goto err_create_palm_sensor_failed; + } +#endif + +#ifdef HIMAX_V2_SENSOR_EN + if (himax_tap_detect_sensor_init(ts)) { + E(" %s: himax_tap_detect_sensor_init failed!\n", __func__); + goto err_create_tap_sensor_failed; + } +#endif + + himax_ts_register_interrupt(); + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_DEBUG + if (himax_debug_init()) + E(" %s: debug initial failed!\n", __func__); +#endif + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) + + if (g_auto_update_flag) + himax_int_enable(0); + +#endif + g_hx_chip_inited = true; + return 0; + +#ifdef HIMAX_V2_SENSOR_EN +err_create_tap_sensor_failed: +#endif +#ifdef HIMAX_PALM_SENSOR_EN +err_create_palm_sensor_failed: +#endif +err_creat_proc_file_failed: + himax_report_data_deinit(); +err_report_data_init_failed: +#ifdef HX_SMART_WAKEUP + PM_WAKEUP_UNREGISTER(ts->ts_SMWP_wake_lock); +#endif +#ifndef CONFIG_HAS_WAKELOCK +err_wakeup_source_register_failed: +#endif +#ifdef HIMAX_CONFIG_PANEL_NOTIFICATIONS + cancel_delayed_work_sync(&ts->work_panel); + destroy_workqueue(ts->himax_panel_wq); +err_panel_notifier_reg_failed: +#endif +#if defined(CONFIG_FB) || defined(CONFIG_DRM) + cancel_delayed_work_sync(&ts->work_att); + destroy_workqueue(ts->himax_att_wq); +err_get_intr_bit_failed: +#endif +err_input_register_device_failed: + input_free_device(ts->input_dev); +err_detect_failed: +#ifdef HX_RESUME_SET_FW +#if defined(__HIMAX_HX83102D_MOD__) + cancel_delayed_work_sync(&ts->ts_int_work); +#else + cancel_work_sync(&ts->ts_int_work); +#endif + destroy_workqueue(ts->ts_int_workqueue); +err_create_ts_resume_wq_failed: +#endif +#ifdef HX_ZERO_FLASH + cancel_delayed_work_sync(&ts->work_0f_update); + destroy_workqueue(ts->himax_0f_update_wq); +err_0f_update_wq_failed: +#endif +#ifdef HX_AUTO_UPDATE_FW + if (g_auto_update_flag) { + cancel_delayed_work_sync(&ts->work_update); + destroy_workqueue(ts->himax_update_wq); + } +err_update_wq_failed: +#endif +#if defined(HX_USB_DETECT_GLOBAL) +err_register_charger_notify_failed: + if (ts->charger_notif.notifier_call) + power_supply_unreg_notifier(&ts->charger_notif); +#endif +error_ic_detect_failed: + himax_gpio_power_deconfig(pdata); +firmware_name_alloc_failed: +#ifndef CONFIG_OF +err_power_failed: +#endif +err_himax_gpio_power_config_failed: +err_alloc_dt_pdata_failed: + kfree(hx_touch_data); + hx_touch_data = NULL; +err_alloc_touch_data_failed: + kfree(ic_data); + ic_data = NULL; +err_dt_ic_data_fail: + kfree(pdata); + pdata = NULL; +err_dt_platform_data_fail: + probe_fail_flag = 1; + return err; +} + +void himax_chip_common_deinit(void) +{ + struct himax_ts_data *ts = private_ts; + + himax_ts_unregister_interrupt(); + + himax_sysfs_touchscreen(ts, false); + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + himax_inspect_data_clear(); +#endif + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_DEBUG + himax_debug_remove(); +#endif + + himax_common_proc_deinit(); + himax_report_data_deinit(); + +#ifdef HX_SMART_WAKEUP + PM_WAKEUP_UNREGISTER(ts->ts_SMWP_wake_lock); +#endif +#if defined(CONFIG_DRM) + if (msm_drm_unregister_client(&ts->fb_notif)) + E("Error occurred while unregistering fb_notifier.\n"); + cancel_delayed_work_sync(&ts->work_att); + destroy_workqueue(ts->himax_att_wq); +#elif defined(CONFIG_FB) + if (fb_unregister_client(&ts->fb_notif)) + E("Error occurred while unregistering fb_notifier.\n"); + cancel_delayed_work_sync(&ts->work_att); + destroy_workqueue(ts->himax_att_wq); +#endif +#ifdef HIMAX_CONFIG_PANEL_NOTIFICATIONS + if (unregister_panel_notifier(&ts->panel_notif)) + E("Error occurred while unregistering panel_notifier.\n"); + cancel_delayed_work_sync(&ts->work_panel); + destroy_workqueue(ts->himax_panel_wq); +#endif + input_free_device(ts->input_dev); +#ifdef HX_ZERO_FLASH + cancel_delayed_work_sync(&ts->work_0f_update); + destroy_workqueue(ts->himax_0f_update_wq); +#endif +#ifdef HX_AUTO_UPDATE_FW + cancel_delayed_work_sync(&ts->work_update); + destroy_workqueue(ts->himax_update_wq); +#endif + himax_gpio_power_deconfig(ts->pdata); + if (himax_mcu_cmd_struct_free) + himax_mcu_cmd_struct_free(); + + kfree(hx_touch_data); + hx_touch_data = NULL; + kfree(ic_data); + ic_data = NULL; + kfree(ts->pdata->virtual_key); + ts->pdata->virtual_key = NULL; + kfree(ts->pdata); + ts->pdata = NULL; + kfree(ts); + ts = NULL; + probe_fail_flag = 0; + + I("%s: Common section deinited!\n", __func__); +} + +int _himax_chip_common_suspend(struct himax_ts_data *ts) +{ +#if defined(HX_SMART_WAKEUP) && defined(HX_112F_SET) + int retry = 0; + uint8_t tmp_addr[DATA_LEN_4] = {0}; + uint8_t tmp_data[DATA_LEN_4] = {0}; + uint8_t rec_data[DATA_LEN_4] = {0}; +#endif + + if (ts->suspended) { + I("%s: Already suspended. Skipped.\n", __func__); + goto END; + } else { + ts->suspended = true; + I("%s: enter\n", __func__); + } + +#ifdef HIMAX_PALM_SENSOR_EN + if (ts->palm_detection_enabled) { + himax_report_all_leave_event(ts); + atomic_set(&ts->suspend_mode, 1); + I("[himax] %s: palm detect function enable, reject suspend\n", __func__); + goto END; + } +#endif + + if (debug_data != NULL && debug_data->flash_dump_going == true) { + I("[himax] %s: Flash dump is going, reject suspend\n", __func__); + goto END; + } + +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) +#ifndef HX_RESUME_SEND_CMD + g_core_fp.fp_resend_cmd_func(ts->suspended); +#endif +#endif +#ifdef HX_SMART_WAKEUP + + if (ts->SMWP_enable) { + +#ifdef HX_CODE_OVERLAY + if (ts->in_self_test == 0) + g_core_fp.fp_0f_overlay(2, 0); +#endif + +#ifdef HX_112F_SET + I("SMWP Condition!\n"); + himax_int_enable(0); + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + + I("%s:0x7fD0<-0xA55A\n", __func__); + do { + himax_in_parse_assign_cmd(0x10007fd0, tmp_addr, sizeof(tmp_addr)); + himax_in_parse_assign_cmd(0xA55AA55A, tmp_data, sizeof(tmp_data)); + g_core_fp.fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, 0); + usleep_range(1000, 1001); + + g_core_fp.fp_register_read(tmp_addr, DATA_LEN_4, rec_data, 0); + I("%s: Now retry=%d, data=0x%02X%02X%02X%02X\n", __func__, retry, + rec_data[3], rec_data[2], rec_data[1], rec_data[0]); + } while((retry++ < 10) && (rec_data[3] != 0xA5 && rec_data[2] != 0x5A && rec_data[1] != 0xA5 && rec_data[0] != 0x5A )); + +#if defined(HX_ZERO_FLASH) + g_core_fp.fp_0f_reload_to_active(); +#endif + +#if defined(HX_HIGH_SENSE) +#ifndef HX_RESUME_SEND_CMD + g_core_fp.fp_resend_cmd_func(ts->suspended); +#endif +#endif + himax_int_enable(1); +#endif + atomic_set(&ts->suspend_mode, 1); + ts->pre_finger_mask = 0; + FAKE_POWER_KEY_SEND = false; + I("[himax] %s: SMART_WAKEUP enable, reject suspend\n", __func__); + goto END; + } + + himax_int_enable(0); + himax_rst_gpio_set(ts->rst_gpio, 0); + himax_report_all_leave_event(ts); + /*if (g_core_fp.fp_suspend_ic_action != NULL)*/ + /*g_core_fp.fp_suspend_ic_action();*/ + + if (!ts->use_irq) { + int32_t cancel_state; + + cancel_state = cancel_work_sync(&ts->work); + if (cancel_state) + himax_int_enable(1); + } +#endif + + /*ts->first_pressed = 0;*/ + atomic_set(&ts->suspend_mode, 1); + ts->pre_finger_mask = 0; + + if (ts->pdata) + if (ts->pdata->powerOff3V3 && ts->pdata->power) + ts->pdata->power(0); + +END: + if (ts->in_self_test == 1) + ts->suspend_resume_done = 1; + + I("%s: END\n", __func__); + + return 0; +} + +int himax_chip_common_suspend(struct himax_ts_data *ts) +{ + int ret = 0; + mutex_lock(&ts->suspend_resume_mutex); + ret = _himax_chip_common_suspend(ts); + mutex_unlock(&ts->suspend_resume_mutex); + + return ret; +} + +int _himax_chip_common_resume(struct himax_ts_data *ts) +{ +#ifdef HX_112F_SET + int retry = 0; + uint8_t tmp_addr[DATA_LEN_4] = {0}; + uint8_t tmp_data[DATA_LEN_4] = {0}; + uint8_t rec_data[DATA_LEN_4] = {0}; +#endif + +#if defined(HX_ZERO_FLASH) && defined(HX_RESUME_SET_FW) + int result = 0; +#endif + I("%s: enter\n", __func__); + + if (ts->suspended == false) { + I("%s: It had entered resume, skip this step\n", __func__); + goto END; + } else { + ts->suspended = false; + } + +#ifdef HX_ESD_RECOVERY + /* continuous N times record, not total N times. */ + g_zero_event_count = 0; +#endif + + atomic_set(&ts->suspend_mode, 0); + ts->diag_cmd = 0; +#ifdef HIMAX_PALM_SENSOR_EN + if (ts->palm_detection_enabled) { + I("[himax] %s: palm detect function enable, skip resume\n", __func__); + goto CHECK_LAZY_SET_END; + } +#endif + + if (ts->pdata) + if (ts->pdata->powerOff3V3 && ts->pdata->power) + ts->pdata->power(1); + +#ifndef HX_RESUME_SET_FW +#if defined(HX_RST_PIN_FUNC) && defined(HX_RESUME_HW_RESET) + if (g_core_fp.fp_ic_reset != NULL) + g_core_fp.fp_ic_reset(false, false); +#endif +#endif + +#ifdef HX_112F_SET +#ifdef HX_SMART_WAKEUP + if (ts->SMWP_enable) { + I("%s:0x7fD0<-0x00000000\n", __func__); + do { + himax_in_parse_assign_cmd(0x10007fd0, tmp_addr, sizeof(tmp_addr)); + himax_in_parse_assign_cmd(0x00000000, tmp_data, sizeof(tmp_data)); + g_core_fp.fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, 0); + usleep_range(1000, 1001); + + g_core_fp.fp_register_read(tmp_addr, DATA_LEN_4, rec_data, 0); + I("%s: Now retry=%d, data=0x%02X%02X%02X%02X\n", __func__, retry, + rec_data[3], rec_data[2], rec_data[1], rec_data[0]); + } while((retry++ < 10) && (rec_data[3] != 0x00 && rec_data[2] != 0x00 && rec_data[1] != 0x00 && rec_data[0] != 0x00 )); + } +#endif +#endif + +#if defined(HX_ZERO_FLASH) && defined(HX_RESUME_SET_FW) +#ifdef HX_SMART_WAKEUP + if (!ts->SMWP_enable) { +#endif + himax_rst_gpio_set(ts->rst_gpio, 1); + msleep(2); + himax_rst_gpio_set(ts->rst_gpio, 0); + msleep(5); + himax_rst_gpio_set(ts->rst_gpio, 1); + msleep(2); + I("It will update fw after resume in zero flash mode!\n"); + if (g_core_fp.fp_0f_operation_dirly != NULL) { + result = g_core_fp.fp_0f_operation_dirly(); + if (result) { + E("Something is wrong! Skip Update with zero flash!\n"); + goto ESCAPE_0F_UPDATE; + } + } + if (g_core_fp.fp_reload_disable != NULL) + g_core_fp.fp_reload_disable(0); + if (g_core_fp.fp_sense_on != NULL) + g_core_fp.fp_sense_on(0x00); +#ifdef HX_SMART_WAKEUP + } +#endif +#endif +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) + if (g_core_fp.fp_resend_cmd_func != NULL) + g_core_fp.fp_resend_cmd_func(ts->suspended); + +#ifdef HX_CODE_OVERLAY + if (ts->SMWP_enable && ts->in_self_test == 0) { + I("%s: enter overlay mode", __func__); + g_core_fp.fp_0f_overlay(3, 0); + } +#endif +#endif + himax_report_all_leave_event(ts); + + if (g_core_fp.fp_sense_on != NULL) + g_core_fp.fp_resume_ic_action(); + himax_int_enable(1); +#if defined(HX_ZERO_FLASH) && defined(HX_RESUME_SET_FW) +ESCAPE_0F_UPDATE: +#endif + +#ifdef HIMAX_PALM_SENSOR_EN +CHECK_LAZY_SET_END: + if (ts->palm_detection_lazy_set != PALM_SENSOR_LAZY_SET_NONE) { + _himax_palm_detect_sensor_set_enable( + (ts->palm_detection_lazy_set == PALM_SENSOR_LAZY_SET_ENABLE) ? 1 : 0); + I("%s: Palm sensor lazy set done, clear flag.\n", __func__); + ts->palm_detection_lazy_set = PALM_SENSOR_LAZY_SET_NONE; + } +#endif +END: + if (ts->in_self_test == 1) + ts->suspend_resume_done = 1; + + I("%s: END\n", __func__); + return 0; +} + +int himax_chip_common_resume(struct himax_ts_data *ts) +{ + int ret = 0; + mutex_lock(&ts->suspend_resume_mutex); + ret = _himax_chip_common_resume(ts); + mutex_unlock(&ts->suspend_resume_mutex); + + return ret; +} + diff --git a/drivers/input/touchscreen/himax_0flash_mmi/himax_common.h b/drivers/input/touchscreen/himax_0flash_mmi/himax_common.h new file mode 100755 index 000000000000..be7973f76f65 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/himax_common.h @@ -0,0 +1,637 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for common functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef HIMAX_COMMON_H +#define HIMAX_COMMON_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "himax_platform.h" +#include +#ifdef HIMAX_PALM_SENSOR_EN +#include +#define SENSOR_TYPE_MOTO_TOUCH_PALM (SENSOR_TYPE_DEVICE_PRIVATE_BASE + 31) +#endif +#ifdef HIMAX_V2_SENSOR_EN +#include +#endif + +#if defined(CONFIG_DRM) + #include +#elif defined(CONFIG_FB) + #include + #include +#elif defined(CONFIG_HAS_EARLYSUSPEND) + #include +#endif + +#ifdef HIMAX_CONFIG_PANEL_NOTIFICATIONS +#include +#endif + +#ifdef CONFIG_OF + #include +#endif + +#define HIMAX_DRIVER_VER "2.0.0.14_ABCD1234_01" + +#define FLASH_DUMP_FILE "/sdcard/HX_Flash_Dump.bin" + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG) + #define HX_TP_PROC_2T2R + /*#define HX_TP_SELF_TEST_DRIVER*/ /*if enable, selftest works in driver*/ +#endif + +#ifdef HIMAX_PALM_SENSOR_EN +struct himax_sensor_platform_data { + struct input_dev *input_sensor_dev; + struct sensors_classdev ps_cdev; + int sensor_opened; + char sensor_data; /* 0 near, 1 far */ + struct himax_ts_data *data; +}; + +enum palm_sensor_lazy_set { + PALM_SENSOR_LAZY_SET_NONE = 0, + PALM_SENSOR_LAZY_SET_ENABLE, + PALM_SENSOR_LAZY_SET_DISABLE, +}; +#endif + +#ifdef HIMAX_V2_SENSOR_EN +struct himax_tap_sensor_platform_data { + struct input_dev *input_sensor_dev; + struct sensors_classdev ps_cdev; + struct himax_ts_data *data; +}; +#endif + +/*===========Himax Option function=============*/ +#define HX_RST_PIN_FUNC +#define HX_RESUME_SEND_CMD +#define HX_ESD_RECOVERY +#define HX_TP_PROC_GUEST_INFO +/*#define HX_SHARP_ENABLE_PON*/ +/*#define HX_NEW_EVENT_STACK_FORMAT*/ +/*#define HX_AUTO_UPDATE_FW*/ +#ifdef HIMAX_V2_SENSOR_EN +#define HX_SMART_WAKEUP +#else +//#define HX_SMART_WAKEUP +#endif +/*#define HX_GESTURE_TRACK*/ +/*#define HX_HIGH_SENSE*/ +#ifdef HIMAX_PALM_SENSOR_EN +#define HX_PALM_REPORT +#else +/*#define HX_PALM_REPORT*/ +#endif +#define HX_USB_DETECT_GLOBAL +/*#define HX_USB_DETECT_CALLBACK*/ +/*#define HX_PROTOCOL_A*/ /* for MTK special platform.If turning on,it will report to system by using specific format. */ +#define HX_RESUME_HW_RESET +#define HX_PROTOCOL_B_3PA +/*#define HX_FIX_TOUCH_INFO*/ /* if open, you need to change the touch info in the fix_touch_info*/ +/*#define HX_ZERO_FLASH*/ +#define HX_RESUME_SET_FW +/*#if defined(HX_ZERO_FLASH)*/ +/*#define HX_CODE_OVERLAY*/ +/*#define HX_EDGE_LIMIT*/ +/*#endif*/ +/*#define HX_EN_DYNAMIC_NAME*/ /* Support dynamic load fw name ,default is close */ +/*#undef CONFIG_FB*/ /* Enable it if driver go into suspend/resume twice */ +/*#define HX_PEN_FUNC_EN*/ + +/*#define HX_EN_SEL_BUTTON*/ /* Support Self Virtual key ,default is close*/ +/*#define HX_EN_MUT_BUTTON*/ /* Support Mutual Virtual Key ,default is close*/ + +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) + /*#define HX_PLATFOME_DEFINE_KEY*/ /* for specfic platform to set key(button) */ +#endif +#ifdef HX_USB_DETECT_GLOBAL +#include +#endif + +#if defined(HX_SMART_WAKEUP) +/*#define HX_P_SENSOR*/ +#endif +/*=============================================*/ +#define HX_SPI_MTK_MAX_WRITE_SZ (256 - 4) + +#define HX_KEY_MAX_COUNT 4 +#define DEFAULT_RETRY_CNT 3 + +#define HX_85XX_A_SERIES_PWON "HX85xxA" +#define HX_85XX_B_SERIES_PWON "HX85xxB" +#define HX_85XX_C_SERIES_PWON "HX85xxC" +#define HX_85XX_D_SERIES_PWON "HX85xxD" +#define HX_85XX_E_SERIES_PWON "HX85xxE" +#define HX_85XX_ES_SERIES_PWON "HX85xxES" +#define HX_85XX_F_SERIES_PWON "HX85xxF" +#define HX_85XX_H_SERIES_PWON "HX85xxH" +#define HX_83100A_SERIES_PWON "HX83100A" +#define HX_83102A_SERIES_PWON "HX83102A" +#define HX_83102B_SERIES_PWON "HX83102B" +#define HX_83102D_SERIES_PWON "HX83102D" +#define HX_83102E_SERIES_PWON "HX83102E" +#define HX_83103A_SERIES_PWON "HX83103A" +#define HX_83106A_SERIES_PWON "HX83106A" +#define HX_83110A_SERIES_PWON "HX83110A" +#define HX_83110B_SERIES_PWON "HX83110B" +#define HX_83111B_SERIES_PWON "HX83111B" +#define HX_83112A_SERIES_PWON "HX83112A" +#define HX_83112B_SERIES_PWON "HX83112B" +#define HX_83113A_SERIES_PWON "HX83113A" +#define HX_83112D_SERIES_PWON "HX83112D" +#define HX_83112E_SERIES_PWON "HX83112E" +#define HX_83191A_SERIES_PWON "HX83191A" +#define HX_83112F_SERIES_PWON "HX83112f" + +#define HX_TP_BIN_CHECKSUM_SW 1 +#define HX_TP_BIN_CHECKSUM_HW 2 +#define HX_TP_BIN_CHECKSUM_CRC 3 + +#define SHIFTBITS 5 + +#define FW_SIZE_32k 32768 +#define FW_SIZE_60k 61440 +#define FW_SIZE_64k 65536 +#define FW_SIZE_124k 126976 +#define FW_SIZE_128k 131072 + +#define NO_ERR 0 +#define READY_TO_SERVE 1 +#define WORK_OUT 2 +#define I2C_FAIL -1 +#define HX_INIT_FAIL -1 +#define MEM_ALLOC_FAIL -2 +#define CHECKSUM_FAIL -3 +#define GESTURE_DETECT_FAIL -4 +#define INPUT_REGISTER_FAIL -5 +#define FW_NOT_READY -6 +#define LENGTH_FAIL -7 +#define OPEN_FILE_FAIL -8 +#define ERR_WORK_OUT -10 +#define ERR_STS_WRONG -11 +#define ERR_TEST_FAIL -12 +#define HW_CRC_FAIL 1 + +#define HX_FINGER_ON 1 +#define HX_FINGER_LEAVE 2 + +#if defined(HX_PALM_REPORT) +#define PALM_REPORT 1 +#define PALM_LEAVE_REPORT 2 +#define NOT_REPORT -1 +#endif + +#if defined(HX_PEN_FUNC_EN) +#define PEN_INFO_SZ 12 +#endif + +#if defined(__EMBEDDED_FW__) +extern const uint8_t _binary___Himax_firmware_bin_start[]; +extern const uint8_t _binary___Himax_firmware_bin_end[]; +extern struct firmware g_embedded_fw; +#endif + +enum HX_TS_PATH { + HX_REPORT_COORD = 1, + HX_REPORT_SMWP_EVENT, + HX_REPORT_COORD_RAWDATA, +}; + +enum HX_TS_STATUS { + HX_TS_GET_DATA_FAIL = -4, + HX_ESD_EVENT, + HX_CHKSUM_FAIL, + HX_PATH_FAIL, + HX_TS_NORMAL_END = 0, + HX_ESD_REC_OK, + HX_READY_SERVE, + HX_REPORT_DATA, + HX_ESD_WARNING, + HX_IC_RUNNING, + HX_ZERO_EVENT_COUNT, + HX_RST_OK, +}; + +enum cell_type { + CHIP_IS_ON_CELL, + CHIP_IS_IN_CELL +}; +#ifdef HX_FIX_TOUCH_INFO +#ifdef HX_112F_SET +enum fix_touch_info { + FIX_HX_RX_NUM = 36, + FIX_HX_TX_NUM = 16, + FIX_HX_BT_NUM = 0, + FIX_HX_X_RES = 1080, + FIX_HX_Y_RES = 2520, + FIX_HX_MAX_PT = 10, + FIX_HX_XY_REVERSE = false, + FIX_HX_INT_IS_EDGE = true, +#ifdef HX_TP_PROC_2T2R + FIX_HX_RX_NUM_2 = 36, + FIX_HX_TX_NUM_2 = 16, +#endif +}; +#else +enum fix_touch_info { + FIX_HX_RX_NUM = 36, + FIX_HX_TX_NUM = 18, + FIX_HX_BT_NUM = 0, + FIX_HX_X_RES = 1080, + FIX_HX_Y_RES = 2280, + FIX_HX_MAX_PT = 10, + FIX_HX_XY_REVERSE = false, + FIX_HX_INT_IS_EDGE = true, +#ifdef HX_TP_PROC_2T2R + FIX_HX_RX_NUM_2 = 36, + FIX_HX_TX_NUM_2 = 18, +#endif +}; +#endif +#endif + +#ifdef HX_ZERO_FLASH + #define HX_SPI_OPERATION + #define HX_0F_DEBUG +#endif +struct himax_ic_data { + int vendor_fw_ver; + int vendor_config_ver; + int vendor_touch_cfg_ver; + int vendor_display_cfg_ver; + int vendor_cid_maj_ver; + int vendor_cid_min_ver; + int vendor_panel_ver; + int vendor_sensor_id; +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#ifdef HX_EN_DYNAMIC_NAME + int vendor_ic_ver; + int vendor_old_ic_ver; + uint8_t vendor_semifac; +#endif +#endif + uint8_t vendor_cus_info[12]; + uint8_t vendor_proj_info[12]; + uint8_t vendor_ic_id[13]; + int HX_RX_NUM; + int HX_TX_NUM; + int HX_BT_NUM; + int HX_X_RES; + int HX_Y_RES; + int HX_MAX_PT; + bool HX_XY_REVERSE; + bool HX_INT_IS_EDGE; +#ifdef HX_TP_PROC_2T2R + int HX_RX_NUM_2; + int HX_TX_NUM_2; +#endif +}; + +struct himax_virtual_key { + int index; + int keycode; + int x_range_min; + int x_range_max; + int y_range_min; + int y_range_max; +}; + +struct himax_target_report_data { + int *x; + int *y; + int *w; + int *finger_id; + int finger_on; + int finger_num; +#ifdef HX_PLATFORM_DEFINE_KEY + int key_size; + int *key_x; + int *key_y; + int *key_w; +#endif +#ifdef HX_SMART_WAKEUP + int SMWP_event_chk; +#endif +#if defined(HX_PEN_FUNC_EN) + int32_t *p_x; + int32_t *p_y; + int32_t *p_w; + int32_t *pen_id; + uint32_t *p_hover; + int32_t *p_tilt_x; + uint32_t *p_btn; + uint32_t *p_btn2; + int32_t *p_tilt_y; + uint32_t *p_on; +#endif + int ig_count; + +}; + +struct himax_report_data { + int touch_all_size; + int raw_cnt_max; + int raw_cnt_rmd; + int touch_info_size; + uint8_t finger_num; + uint8_t finger_on; + uint8_t *hx_coord_buf; + uint8_t hx_state_info[2]; +#if defined(HX_SMART_WAKEUP) + int event_size; + uint8_t *hx_event_buf; +#endif + + int rawdata_size; + uint8_t diag_cmd; + uint8_t *hx_rawdata_buf; + uint8_t rawdata_frame_size; +}; + +struct himax_ts_data { + bool initialized; + bool suspended; + atomic_t suspend_mode; + struct mutex suspend_resume_mutex; + uint8_t x_channel; + uint8_t y_channel; + uint8_t useScreenRes; + uint8_t diag_cmd; + char chip_name[30]; + uint8_t chip_cell_type; + + uint8_t protocol_type; + uint8_t first_pressed; + uint8_t coord_data_size; + uint8_t area_data_size; + uint8_t coordInfoSize; + uint8_t raw_data_frame_size; + uint8_t raw_data_nframes; + uint8_t nFinger_support; + uint8_t irq_enabled; + uint8_t diag_self[50]; + + uint16_t finger_pressed; + uint16_t last_slot; + uint16_t pre_finger_mask; + uint16_t old_finger; + int hx_point_num; + + + uint32_t debug_log_level; + uint32_t widthFactor; + uint32_t heightFactor; + uint32_t tw_x_min; + uint32_t tw_x_max; + uint32_t tw_y_min; + uint32_t tw_y_max; + uint32_t pl_x_min; + uint32_t pl_x_max; + uint32_t pl_y_min; + uint32_t pl_y_max; + + int rst_gpio; + int use_irq; + int (*power)(int on); + int pre_finger_data[10][2]; + bool vdd_1v8_always_on; + uint32_t report_gesture_key; + uint32_t build_id; + uint32_t config_id; + + struct device *dev; + struct workqueue_struct *himax_wq; + struct work_struct work; + struct input_dev *input_dev; +#if defined(HX_PEN_FUNC_EN) + struct input_dev *hx_pen_dev; +#endif + struct hrtimer timer; + struct i2c_client *client; + struct himax_i2c_platform_data *pdata; + struct himax_virtual_key *button; + struct mutex rw_lock; + +/******* SPI-start *******/ + struct mutex spi_lock; + struct spi_device *spi; + int hx_irq; + uint8_t *xfer_buff; +/******* SPI-end *******/ + + int in_self_test; + int suspend_resume_done; + int bus_speed; + +#if defined(CONFIG_FB) || defined(CONFIG_DRM) + struct notifier_block fb_notif; + struct workqueue_struct *himax_att_wq; + struct delayed_work work_att; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif + +#ifdef HIMAX_CONFIG_PANEL_NOTIFICATIONS + struct notifier_block panel_notif; + struct workqueue_struct *himax_panel_wq; + struct delayed_work work_panel; +#endif + struct workqueue_struct *flash_wq; + struct work_struct flash_work; + +#ifdef HX_AUTO_UPDATE_FW + struct workqueue_struct *himax_update_wq; + struct delayed_work work_update; +#endif + +#ifdef HX_ZERO_FLASH + struct workqueue_struct *himax_0f_update_wq; + struct delayed_work work_0f_update; +#endif +#ifdef HX_RESUME_SET_FW + struct workqueue_struct *ts_int_workqueue; +#if defined(__HIMAX_HX83102D_MOD__) + struct delayed_work ts_int_work; +#else + struct work_struct ts_int_work; +#endif +#endif + + struct workqueue_struct *himax_diag_wq; + struct delayed_work himax_diag_delay_wrok; + +#if defined (HX_SMART_WAKEUP) || defined (HX_CODE_OVERLAY) + uint8_t SMWP_enable; + uint8_t gesture_cust_en[26]; + struct wakeup_source *ts_SMWP_wake_lock; +#ifdef HX_P_SENSOR + bool psensor_flag; +#endif +#endif + +#ifdef HX_EDGE_LIMIT + uint8_t edge_limit_enable; + uint8_t edge_limit_en[8]; +#endif + +#ifdef HX_HIGH_SENSE + uint8_t HSEN_enable; +#endif + +#if defined(HX_USB_DETECT_CALLBACK) || defined(HX_USB_DETECT_GLOBAL) + uint8_t usb_connected; + uint8_t *cable_config; + struct notifier_block charger_notif; +#endif + +#ifdef HX_TP_PROC_GUEST_INFO + struct workqueue_struct *guest_info_wq; + struct work_struct guest_info_work; +#endif + +#ifdef HIMAX_PALM_SENSOR_EN + bool palm_detection_enabled; + enum palm_sensor_lazy_set palm_detection_lazy_set; + struct himax_sensor_platform_data *palm_sensor_pdata; + struct timer_list palm_release_fimer; + unsigned int palm_release_delay_ms; +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock palm_gesture_wakelock; +#else + struct wakeup_source *palm_gesture_wakelock; +#endif +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock palm_gesture_read_wakelock; +#else + struct wakeup_source *palm_gesture_read_wakelock; +#endif +#endif + +#ifdef HIMAX_V2_SENSOR_EN + struct himax_tap_sensor_platform_data *sensor_pdata; +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock tap_gesture_wakelock; +#else + struct wakeup_source *tap_gesture_wakelock; +#endif +#endif + +}; + +struct himax_debug { + bool flash_dump_going; + void (*fp_ts_dbg_func)(struct himax_ts_data *ts, int start); + int (*fp_set_diag_cmd)(struct himax_ic_data *ic_data, struct himax_report_data *hx_touch_data); +}; + +enum input_protocol_type { + PROTOCOL_TYPE_A = 0x00, + PROTOCOL_TYPE_B = 0x01, +}; + +#ifdef HX_HIGH_SENSE + void himax_set_HSEN_func(uint8_t HSEN_enable); +#endif + +#ifdef HX_SMART_WAKEUP +void himax_set_SMWP_func(uint8_t SMWP_enable); + +#define GEST_PTLG_ID_LEN (4) +#define GEST_PTLG_HDR_LEN (4) +#define GEST_PTLG_HDR_ID1 (0xCC) +#define GEST_PTLG_HDR_ID2 (0x44) +#define GEST_PT_MAX_NUM (128) + +extern uint8_t *wake_event_buffer; +#endif + +#ifdef HIMAX_CONFIG_PANEL_NOTIFICATIONS +#define register_panel_notifier panel_register_notifier +#define unregister_panel_notifier panel_unregister_notifier +enum touch_state { + TOUCH_DEEP_SLEEP_STATE = 0, + TOUCH_LOW_POWER_STATE, +}; +#else +#define register_panel_notifier(...) rc +#define unregister_panel_notifier(...) rc +#endif + +extern int irq_enable_count; +extern int g_mmi_refcnt; +extern int *g_inspt_crtra_flag; +extern uint32_t g_hx_chip_inited; +/*void himax_HW_reset(uint8_t loadconfig,uint8_t int_off);*/ + +int himax_chip_common_suspend(struct himax_ts_data *ts); +int himax_chip_common_resume(struct himax_ts_data *ts); + +extern struct filename* (*kp_getname_kernel)(const char *filename); +extern struct file * (*kp_file_open_name)(struct filename *name, int flags, umode_t mode); + +struct himax_core_fp; +extern struct himax_core_fp g_core_fp; +extern struct himax_ts_data *private_ts; +extern struct himax_ic_data *ic_data; +extern struct device *g_device; + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_DEBUG + int himax_debug_init(void); + int himax_debug_remove(void); +#endif + +#if defined(HX_PLATFOME_DEFINE_KEY) + void himax_platform_key(void); +#endif + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + extern char *g_rslt_data; + extern void (*fp_himax_self_test_init)(void); +#endif + +int himax_parse_dt(struct himax_ts_data *ts, struct himax_i2c_platform_data *pdata); +int himax_report_data(struct himax_ts_data *ts, int ts_path, int ts_status); + +int himax_report_data_init(void); + +int himax_dev_set(struct himax_ts_data *ts); +int himax_input_register_device(struct input_dev *input_dev); + +#endif + diff --git a/drivers/input/touchscreen/himax_0flash_mmi/himax_debug.c b/drivers/input/touchscreen/himax_0flash_mmi/himax_debug.c new file mode 100755 index 000000000000..b41724bd3cb2 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/himax_debug.c @@ -0,0 +1,2864 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for debug nodes + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_debug.h" +#include "himax_ic_core.h" + +#ifdef HX_TP_PROC_2T2R + bool Is_2T2R; + int HX_RX_NUM_2; + int HX_TX_NUM_2; +#endif + +uint8_t g_diag_arr_num; + +int g_max_mutual; +int g_min_mutual = 0xFFFF; +int g_max_self; +int g_min_self = 0xFFFF; + +/* moved from debug.h */ + +struct proc_dir_entry *himax_proc_vendor_file; +uint8_t byte_length; +uint8_t register_command[4]; +uint8_t cfg_flag; + +struct proc_dir_entry *himax_proc_stack_file; +struct proc_dir_entry *himax_proc_iir_file; +struct proc_dir_entry *himax_proc_dc_file; +struct proc_dir_entry *himax_proc_bank_file; +bool dsram_flag; + +#ifdef HX_TP_PROC_2T2R +uint32_t *diag_mutual_2; +#endif +int32_t *diag_mutual; +int32_t *diag_mutual_new; +int32_t *diag_mutual_old; +uint8_t diag_max_cnt; +uint8_t hx_state_info[2]; +uint8_t diag_coor[128]; +int32_t diag_self[100]; +int32_t diag_self_new[100]; +int32_t diag_self_old[100]; + +struct proc_dir_entry *himax_proc_debug_file; +bool fw_update_complete; +int handshaking_result; +unsigned char debug_level_cmd; +uint8_t cmd_set[8]; +uint8_t mutual_set_flag; + +struct proc_dir_entry *himax_proc_flash_dump_file; +int Flash_Size = 131072; +uint8_t *flash_buffer; +uint8_t g_flash_cmd; +uint8_t g_flash_progress; +bool g_flash_dump_rst; /*Fail = 0, Pass = 1*/ + +uint32_t **raw_data_array; +uint8_t X_NUM; +uint8_t Y_NUM; +uint8_t sel_type = 0x0D; + +/* Moved from debug.h End */ +char buf_tmp[BUF_SIZE] = {0}; +uint8_t *reg_read_data = NULL; +#define DEBUG_READ_BUF_SIZE 128 + + +#if defined(HX_PEN_FUNC_EN) + struct proc_dir_entry *himax_proc_pen_pos_file; +#endif + +struct timespec timeStart, timeEnd, timeDelta; +int g_switch_mode; +/* + * Segment : Himax PROC Debug Function + */ + +#if defined(HX_PEN_FUNC_EN) +static ssize_t himax_pen_ops_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + int i = 0; + ssize_t ret = 0; + + if (!HX_PROC_SEND_FLAG) { + for (i = 0; i < private_ts->nFinger_support; i++) { + if (g_target_report_data->x[i] >= 0 && g_target_report_data->x[i] <= private_ts->pdata->abs_x_max + && g_target_report_data->y[i] >= 0 && g_target_report_data->y[i] <= private_ts->pdata->abs_y_max + && private_ts->hx_point_num != 0) { + ret += snprintf(buf_tmp + ret, len - ret, + "%d,%d,", g_target_report_data->x[i], g_target_report_data->y[i]); + } else { + ret += snprintf(buf_tmp + ret, len - ret, + "65535,65535,"); + } + } + if (g_target_report_data->p_x[0] >= 0 && g_target_report_data->p_x[0] <= private_ts->pdata->abs_x_max + && g_target_report_data->p_y[0] >= 0 && g_target_report_data->p_y[0] <= private_ts->pdata->abs_y_max + && (g_target_report_data->p_on[0] == 1)) { + ret += snprintf(buf_tmp + ret, len - ret, + "%d,%d,", g_target_report_data->p_x[0], g_target_report_data->p_y[0]); + } else { + ret += snprintf(buf_tmp + ret, len - ret, + "65535,65535,"); + } + + ret += snprintf(buf_tmp + ret, len - ret, + "%d,", g_target_report_data->p_w[0]); + + ret += snprintf(buf_tmp + ret, len - ret, + "%d,", g_target_report_data->p_hover[0]); + + if (g_target_report_data->p_btn[0] != 0xff) { + ret += snprintf(buf_tmp + ret, len - ret, + "%d,", g_target_report_data->p_btn[0]); + } else { + ret += snprintf(buf_tmp + ret, len - ret, + "0,"); + } + if (g_target_report_data->p_btn2[0] != 0xff) { + ret += snprintf(buf_tmp + ret, len - ret, + "%d,", g_target_report_data->p_btn2[0]); + } else { + ret += snprintf(buf_tmp + ret, len - ret, + "0,"); + } + + if (g_target_report_data->p_tilt_x[0] != 0xff) { + ret += snprintf(buf_tmp + ret, len - ret, + "%d,", g_target_report_data->p_tilt_x[0]); + } else { + ret += snprintf(buf_tmp + ret, len - ret, + "0,"); + } + if (g_target_report_data->p_tilt_y[0] != 0xff) { + ret += snprintf(buf_tmp + ret, len - ret, + "%d\n", g_target_report_data->p_tilt_y[0]); + } else { + ret += snprintf(buf_tmp + ret, len - ret, + "0\n"); + } + + if (copy_to_user(buf, buf_tmp, (len > BUF_SIZE) ? BUF_SIZE : len)) + I("%s,here:%d\n", __func__, __LINE__); + + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static const struct file_operations himax_proc_pen_pos_ops = { + .owner = THIS_MODULE, + .read = himax_pen_ops_read, +}; +#endif + +static ssize_t himax_crc_test_read(char *buf, size_t len) +{ + ssize_t ret = 0; + uint8_t result = 0; + uint32_t size = 0; + + g_core_fp.fp_sense_off(true); + msleep(20); + if (g_core_fp._diff_overlay_flash() == 1) + size = FW_SIZE_128k; + else + size = FW_SIZE_64k; + result = g_core_fp.fp_calculateChecksum(false, size); + g_core_fp.fp_sense_on(0x01); + + if (result) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CRC test is Pass!\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CRC test is Fail!\n"); + + return ret; +} + +static ssize_t himax_vendor_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + ssize_t ret = 0; + + if (!HX_PROC_SEND_FLAG) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "FW_VER = 0x%2.2X\n", + ic_data->vendor_fw_ver); + + if (private_ts->chip_cell_type == CHIP_IS_ON_CELL) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CONFIG_VER = 0x%2.2X\n", + ic_data->vendor_config_ver); + } else { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "TOUCH_VER = 0x%2.2X\n", + ic_data->vendor_touch_cfg_ver); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "DISPLAY_VER = 0x%2.2X\n", + ic_data->vendor_display_cfg_ver); + } + + if (ic_data->vendor_cid_maj_ver < 0 && + ic_data->vendor_cid_min_ver < 0) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CID_VER = NULL\n"); + } else { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CID_VER = 0x%2.2X\n", + (ic_data->vendor_cid_maj_ver << 8 | + ic_data->vendor_cid_min_ver)); + } + + if (ic_data->vendor_panel_ver < 0) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "PANEL_VER = NULL\n"); + } else { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "PANEL_VER = 0x%2.2X\n", + ic_data->vendor_panel_ver); + } + if (private_ts->chip_cell_type == CHIP_IS_IN_CELL) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Cusomer = %s\n", ic_data->vendor_cus_info); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Project = %s\n", ic_data->vendor_proj_info); + } +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#ifdef HX_EN_DYNAMIC_NAME + if (ic_data->vendor_semifac == 1) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "SEMI_FAC = UMC\n"); + } else if (ic_data->vendor_semifac == 2) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "SEMI_FAC = PSC\n"); + } else { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "SEMI_FAC = NULL\n"); + } +#endif +#endif + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Himax Touch Driver Version:\n"); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%s\n", + HIMAX_DRIVER_VER); + HX_PROC_SEND_FLAG = 1; + + if (copy_to_user(buf, buf_tmp, (len > BUF_SIZE) ? BUF_SIZE : len)) + I("%s,here:%d\n", __func__, __LINE__); + + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static const struct file_operations himax_proc_vendor_ops = { + .owner = THIS_MODULE, + .read = himax_vendor_read, +}; + +static ssize_t himax_attn_read(char *buf, size_t len) +{ + ssize_t ret = 0; + struct himax_ts_data *ts_data; + + ts_data = private_ts; + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "attn = %x\n", + himax_int_gpio_read(ts_data->pdata->gpio_irq)); + + return ret; +} + +static ssize_t himax_int_en_read(char *buf, size_t len) +{ + struct himax_ts_data *ts = private_ts; + size_t ret = 0; + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%d\n", + ts->irq_enabled); + + return ret; +} + +static ssize_t himax_int_en_write(char *buf, size_t len) +{ + struct himax_ts_data *ts = private_ts; + int value, ret = 0; + + if (len >= 12) { + I("%s: no command exceeds 12 chars.\n", __func__); + return -EFAULT; + } + + if (buf[0] == '0') + value = false; + else if (buf[0] == '1') + value = true; + else + return -EINVAL; + + if (value) { + ret = himax_int_en_set(); + + if (ret == 0) { + ts->irq_enabled = 1; + irq_enable_count = 1; + } + } else { + himax_int_enable(0); + free_irq(ts->hx_irq, ts); + ts->irq_enabled = 0; + } + + return len; +} + +static ssize_t himax_layout_read(char *buf, size_t len) +{ + struct himax_ts_data *ts = private_ts; + size_t ret = 0; + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%d ", + ts->pdata->abs_x_min); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%d ", + ts->pdata->abs_x_max); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%d ", + ts->pdata->abs_y_min); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%d ", + ts->pdata->abs_y_max); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + + return ret; +} + +static ssize_t himax_layout_write(char *buf, size_t len) +{ + struct himax_ts_data *ts = private_ts; + char buf_tmp[5] = {0}; + int i = 0, j = 0, k = 0, ret; + unsigned long value; + int layout[4] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + for (i = 0; i < 20; i++) { + if (buf[i] == ',' || buf[i] == '\n') { + memset(buf_tmp, 0x0, sizeof(buf_tmp)); + + if (i - j <= 5) { + memcpy(buf_tmp, buf + j, i - j); + } else { + I("buffer size is over 5 char\n"); + return len; + } + + j = i + 1; + + if (k < 4) { + ret = kstrtoul(buf_tmp, 10, &value); + layout[k++] = value; + } + } + } + + if (k == 4) { + ts->pdata->abs_x_min = layout[0]; + ts->pdata->abs_x_max = (layout[1] - 1); + ts->pdata->abs_y_min = layout[2]; + ts->pdata->abs_y_max = (layout[3] - 1); + I("%d, %d, %d, %d\n", + ts->pdata->abs_x_min, ts->pdata->abs_x_max, + ts->pdata->abs_y_min, ts->pdata->abs_y_max); + input_unregister_device(ts->input_dev); + himax_input_register(ts); + } else { + I("ERR@%d, %d, %d, %d\n", + ts->pdata->abs_x_min, ts->pdata->abs_x_max, + ts->pdata->abs_y_min, ts->pdata->abs_y_max); + } + + return len; +} + +static ssize_t himax_debug_level_read(char *buf, size_t len) +{ + struct himax_ts_data *ts_data; + size_t ret = 0; + + ts_data = private_ts; + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%d\n", + ts_data->debug_log_level); + + if (copy_to_user(buf, buf_tmp, (len > BUF_SIZE) ? BUF_SIZE : len)) + I("%s,here:%d\n", __func__, __LINE__); + + return ret; +} + +static ssize_t himax_debug_level_write(char *buf, size_t len) +{ + struct himax_ts_data *ts; + int i; + + ts = private_ts; + + if (len >= 12) { + I("%s: no command exceeds 12 chars.\n", __func__); + return -EFAULT; + } + + ts->debug_log_level = 0; + + for (i = 0; i < len; i++) { + if (buf[i] >= '0' && buf[i] <= '9') + ts->debug_log_level |= (buf[i] - '0'); + else if (buf[i] >= 'A' && buf[i] <= 'F') + ts->debug_log_level |= (buf[i] - 'A' + 10); + else if (buf[i] >= 'a' && buf[i] <= 'f') + ts->debug_log_level |= (buf[i] - 'a' + 10); + + if (i != len - 1) + ts->debug_log_level <<= 4; + } + I("Now debug level value=%d\n", ts->debug_log_level); + if (ts->debug_log_level & BIT(4)) { + I("Turn on/Enable Debug Mode for Inspection!\n"); + goto END_FUNC; + } + + if (ts->debug_log_level & BIT(3)) { + if (ts->pdata->screenWidth > 0 && ts->pdata->screenHeight > 0 && + (ts->pdata->abs_x_max - ts->pdata->abs_x_min) > 0 && + (ts->pdata->abs_y_max - ts->pdata->abs_y_min) > 0) { + ts->widthFactor = (ts->pdata->screenWidth << SHIFTBITS) + / (ts->pdata->abs_x_max - ts->pdata->abs_x_min); + ts->heightFactor = + (ts->pdata->screenHeight << SHIFTBITS) / + (ts->pdata->abs_y_max - ts->pdata->abs_y_min); + + if (ts->widthFactor > 0 && ts->heightFactor > 0) { + ts->useScreenRes = 1; + } else { + ts->heightFactor = 0; + ts->widthFactor = 0; + ts->useScreenRes = 0; + } + } else { + I("Enable finger debug with raw position mode!\n"); + } + } else { + ts->useScreenRes = 0; + ts->widthFactor = 0; + ts->heightFactor = 0; + } +END_FUNC: + return len; +} + +static ssize_t himax_proc_register_read(char *buf, size_t len) +{ + int ret = 0; + uint16_t loop_i; + + if(reg_read_data == NULL){ + E("%s reg_read_data is null \n",__func__); + return ret; + } + memset(reg_read_data, 0x00, DEBUG_READ_BUF_SIZE * sizeof(uint8_t)); + + I("himax_register_show: %02X,%02X,%02X,%02X\n", + register_command[3], register_command[2], + register_command[1], register_command[0]); + g_core_fp.fp_register_read(register_command, 128, reg_read_data, + cfg_flag); + ret += snprintf(buf_tmp + ret, len - ret, + "command: %02X,%02X,%02X,%02X\n", + register_command[3], register_command[2], + register_command[1], register_command[0]); + + for (loop_i = 0; loop_i < 128; loop_i++) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "0x%2.2X ", + reg_read_data[loop_i]); + if ((loop_i % 16) == 15) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + + } + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + + return ret; +} + +static ssize_t himax_proc_register_write(char *buf, size_t len) +{ + char buff_tmp[16] = {0}; + uint8_t length = 0; + unsigned long result = 0; + uint8_t loop_i = 0; + uint16_t base = 2; + char *data_str = NULL; + uint8_t w_data[20] = {0}; + uint8_t x_pos[20] = {0}; + uint8_t count = 0; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + memset(register_command, 0x0, sizeof(register_command)); + + I("himax %s\n", buf); + + if ((buf[0] == 'r' || buf[0] == 'w') && buf[1] == ':' && + buf[2] == 'x') { + length = strlen(buf); + + /* I("%s: length = %d.\n", __func__,length); */ + for (loop_i = 0; loop_i < length; loop_i++) { + /* find postion of 'x' */ + if (buf[loop_i] == 'x') { + x_pos[count] = loop_i; + count++; + } + } + + data_str = strrchr(buf, 'x'); + I("%s: %s.\n", __func__, data_str); + length = strlen(data_str + 1); + + switch (buf[0]) { + case 'r': + if (buf[3] == 'F' && buf[4] == 'E' && length == 4) { + length = length - base; + cfg_flag = 1; + memcpy(buff_tmp, data_str + base + 1, length); + } else { + cfg_flag = 0; + memcpy(buff_tmp, data_str + 1, length); + } + byte_length = length / 2; + if (!kstrtoul(buff_tmp, 16, &result)) { + for (loop_i = 0; loop_i < byte_length; loop_i++) + register_command[loop_i] = + (uint8_t)(result >> loop_i * 8); + } + + if (strcmp(HX_85XX_H_SERIES_PWON, + private_ts->chip_name) == 0 && cfg_flag == 0) + cfg_flag = 2; + break; + case 'w': + if (buf[3] == 'F' && buf[4] == 'E') { + cfg_flag = 1; + memcpy(buff_tmp, buf + base + 3, length); + } else { + cfg_flag = 0; + memcpy(buff_tmp, buf + 3, length); + } + + if (count < 3) { + byte_length = length / 2; + + if (!kstrtoul(buff_tmp, 16, &result)) { + /* command */ + for (loop_i = 0; loop_i < byte_length; + loop_i++) { + register_command[loop_i] = + (uint8_t)(result >> loop_i * 8); + } + } + + if (!kstrtoul(data_str + 1, 16, &result)) { + /* data */ + for (loop_i = 0; loop_i < byte_length; + loop_i++) { + w_data[loop_i] = + (uint8_t)(result >> loop_i * 8); + } + } + + g_core_fp.fp_register_write(register_command, + byte_length, w_data, cfg_flag); + } else { + for (loop_i = 0; loop_i < count; loop_i++) { + /* parsing addr after 'x' */ + memset(buff_tmp, 0x0, sizeof(buff_tmp)); + if (cfg_flag != 0 && loop_i != 0) + byte_length = 2; + else + byte_length = x_pos[1] - + x_pos[0] - 2;/* original */ + memcpy(buff_tmp, buf + x_pos[loop_i] + 1, + byte_length); + + /* I("%s: buff_tmp = %s\n", __func__, + * buff_tmp); + */ + if (!kstrtoul(buff_tmp, 16, &result)) { + if (loop_i == 0) { + register_command[loop_i] + = (uint8_t)(result); + /* I("%s: + * register_command + * = %X\n", __func__, + * register_command[0]); + */ + } else { + w_data[loop_i - 1] = + (uint8_t)(result); + /* I("%s: w_data[%d] = + * %2X\n", __func__, + * loop_i - 1, + * w_data[loop_i - 1]); + */ + } + } + } + + byte_length = count - 1; + if (strcmp(HX_85XX_H_SERIES_PWON, + private_ts->chip_name) == 0 && cfg_flag == 0) + cfg_flag = 2; + g_core_fp.fp_register_write(register_command, + byte_length, &w_data[0], cfg_flag); + + } + break; + }; + } + return len; +} + +int32_t *getMutualBuffer(void) +{ + return diag_mutual; +} +int32_t *getMutualNewBuffer(void) +{ + return diag_mutual_new; +} +int32_t *getMutualOldBuffer(void) +{ + return diag_mutual_old; +} +int32_t *getSelfBuffer(void) +{ + return &diag_self[0]; +} +int32_t *getSelfNewBuffer(void) +{ + return &diag_self_new[0]; +} +int32_t *getSelfOldBuffer(void) +{ + return &diag_self_old[0]; +} +void setMutualBuffer(uint8_t x_num, uint8_t y_num) +{ + diag_mutual = kzalloc(x_num * y_num * sizeof(int32_t), GFP_KERNEL); +} +void setMutualNewBuffer(uint8_t x_num, uint8_t y_num) +{ + diag_mutual_new = kzalloc(x_num * y_num * sizeof(int32_t), GFP_KERNEL); +} +void setMutualOldBuffer(uint8_t x_num, uint8_t y_num) +{ + diag_mutual_old = kzalloc(x_num * y_num * sizeof(int32_t), GFP_KERNEL); +} + +#ifdef HX_TP_PROC_2T2R +int32_t *getMutualBuffer_2(void) +{ + return diag_mutual_2; +} +void setMutualBuffer_2(uint8_t x_num_2, uint8_t y_num_2) +{ + diag_mutual_2 = + kzalloc(x_num_2 * y_num_2 * sizeof(int32_t), GFP_KERNEL); +} +#endif + +int himax_set_diag_cmd(struct himax_ic_data *ic_data, + struct himax_report_data *hx_touch_data) +{ + struct himax_ts_data *ts = private_ts; + int32_t *mutual_data; + int32_t *self_data; + int mul_num; + int self_num; + /* int RawDataLen = 0; */ + hx_touch_data->diag_cmd = ts->diag_cmd; + + if (hx_touch_data->diag_cmd >= 1 && hx_touch_data->diag_cmd <= 7) { + /* Check event stack CRC */ + if (!g_core_fp.fp_diag_check_sum(hx_touch_data)) + goto bypass_checksum_failed_packet; + +#ifdef HX_TP_PROC_2T2R + if (Is_2T2R && (hx_touch_data->diag_cmd >= 4 && + hx_touch_data->diag_cmd <= 6)) { + mutual_data = getMutualBuffer_2(); + self_data = getSelfBuffer(); + /* initiallize the block number of mutual and self */ + mul_num = ic_data->HX_RX_NUM_2 * ic_data->HX_TX_NUM_2; +#ifdef HX_EN_SEL_BUTTON + self_num = ic_data->HX_RX_NUM_2 + + ic_data->HX_TX_NUM_2 + ic_data->HX_BT_NUM; +#else + self_num = ic_data->HX_RX_NUM_2 + ic_data->HX_TX_NUM_2; +#endif + } else +#endif + { + mutual_data = getMutualBuffer(); + self_data = getSelfBuffer(); + /* initiallize the block number of mutual and self */ + mul_num = ic_data->HX_RX_NUM * ic_data->HX_TX_NUM; +#ifdef HX_EN_SEL_BUTTON + self_num = ic_data->HX_RX_NUM + + ic_data->HX_TX_NUM + ic_data->HX_BT_NUM; +#else + self_num = ic_data->HX_RX_NUM + ic_data->HX_TX_NUM; +#endif + } + g_core_fp.fp_diag_parse_raw_data(hx_touch_data, mul_num, + self_num, hx_touch_data->diag_cmd, mutual_data, + self_data); + } else if (hx_touch_data->diag_cmd == 8) { + memset(diag_coor, 0x00, sizeof(diag_coor)); + memcpy(&(diag_coor[0]), &hx_touch_data->hx_coord_buf[0], + hx_touch_data->touch_info_size); + } + + /* assign state info data */ + memcpy(&(hx_state_info[0]), &hx_touch_data->hx_state_info[0], 2); + return NO_ERR; +bypass_checksum_failed_packet: + return 1; +} + +/* #if defined(HX_DEBUG_LEVEL) */ +void himax_log_touch_data(int start) +{ + int loop_i = 0; + int print_size = 0; + uint8_t *buf; + + if (start == 1) + return; /* report data when end of ts_work*/ + + if (hx_touch_data->diag_cmd > 0) { + print_size = hx_touch_data->touch_all_size; + buf = kcalloc(print_size, sizeof(uint8_t), GFP_KERNEL); + memcpy(buf, hx_touch_data->hx_coord_buf, + hx_touch_data->touch_info_size); + memcpy(&buf[hx_touch_data->touch_info_size], + hx_touch_data->hx_rawdata_buf, + print_size - hx_touch_data->touch_info_size); + } +#if defined(HX_SMART_WAKEUP) + else if (private_ts->SMWP_enable > 0 && private_ts->suspended) { + print_size = hx_touch_data->event_size; + buf = kcalloc(print_size, sizeof(uint8_t), GFP_KERNEL); + memcpy(buf, hx_touch_data->hx_event_buf, print_size); + } +#endif + else if (hx_touch_data->diag_cmd == 0) { + print_size = hx_touch_data->touch_info_size; + buf = kcalloc(print_size, sizeof(uint8_t), GFP_KERNEL); + memcpy(buf, hx_touch_data->hx_coord_buf, print_size); + } else { + E("%s:cmd fault\n", __func__); + } + + for (loop_i = 0; loop_i < print_size; loop_i += 8) { + if ((loop_i + 7) >= print_size) { + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", + loop_i, buf[loop_i], loop_i + 1, buf[loop_i + 1]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X\n", + loop_i + 2, buf[loop_i + 2], loop_i + 3, buf[loop_i + 3]); + break; + } + + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", + loop_i, buf[loop_i], loop_i + 1, buf[loop_i + 1]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", + loop_i + 2, buf[loop_i + 2], loop_i + 3, buf[loop_i + 3]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", + loop_i + 4, buf[loop_i + 4], loop_i + 5, buf[loop_i + 5]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", + loop_i + 6, buf[loop_i + 6], loop_i + 7, buf[loop_i + 7]); + I("\n"); + } + kfree(buf); +} +void himax_log_touch_event(struct himax_ts_data *ts, int start) +{ + int loop_i = 0; + + if (start == 1) + return; /*report data when end of ts_work*/ + + if (g_target_report_data->finger_on > 0 && + g_target_report_data->finger_num > 0) { + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + if (g_target_report_data->x[loop_i] >= 0 + && g_target_report_data->x[loop_i] + <= ts->pdata->abs_x_max + && g_target_report_data->y[loop_i] >= 0 + && g_target_report_data->y[loop_i] + <= ts->pdata->abs_y_max) { + I("Finger %d=> X:%d, Y:%d W:%d, Z:%d, F:%d,Int_Delay_Counter:%d\n", + loop_i + 1, + g_target_report_data->x[loop_i], + g_target_report_data->y[loop_i], + g_target_report_data->w[loop_i], + g_target_report_data->w[loop_i], + loop_i + 1, + g_target_report_data->ig_count); + + } + } + } else if (g_target_report_data->finger_on == 0 && + g_target_report_data->finger_num == 0) { + I("All Finger leave\n"); + } else { + I("%s : wrong input!\n", __func__); + } +} +void himax_log_touch_int_devation(int touched) +{ + if (touched == HX_FINGER_ON) { + getnstimeofday(&timeStart); + /* I(" Irq start time = %ld.%06ld s\n", + * timeStart.tv_sec, timeStart.tv_nsec/1000); + */ + } else if (touched == HX_FINGER_LEAVE) { + getnstimeofday(&timeEnd); + timeDelta.tv_nsec = + (timeEnd.tv_sec * 1000000000 + timeEnd.tv_nsec) - + (timeStart.tv_sec * 1000000000 + timeStart.tv_nsec); + /* I("Irq finish time = %ld.%06ld s\n", + * timeEnd.tv_sec, timeEnd.tv_nsec/1000); + */ + I("Touch latency = %ld us\n", timeDelta.tv_nsec / 1000); + I("bus_speed = %d kHz\n", private_ts->bus_speed); + if (g_target_report_data->finger_on == 0 && g_target_report_data->finger_num == 0) + I("All Finger leave\n"); + } else { + I("%s : wrong input!\n", __func__); + } +} + +#define RAW_DOWN_STATUS "status: Raw:F:%02d Down, X:%d, Y:%d, W:%d\n" +#define RAW_UP_STATUS "status: Raw:F:%02d Up, X:%d, Y:%d\n" + +void himax_log_touch_event_detail(struct himax_ts_data *ts, int start) +{ + int loop_i = 0; + + if (start == HX_FINGER_LEAVE) { + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + if (((ts->old_finger >> loop_i & 1) == 0) && + ((ts->pre_finger_mask >> loop_i & 1) == 1)) { + if (g_target_report_data->x[loop_i] >= 0 + && g_target_report_data->x[loop_i] + <= ts->pdata->abs_x_max + && g_target_report_data->y[loop_i] >= 0 + && g_target_report_data->y[loop_i] + <= ts->pdata->abs_y_max) { + I(RAW_DOWN_STATUS, + loop_i + 1, + g_target_report_data->x[loop_i], + g_target_report_data->y[loop_i], + g_target_report_data->w[loop_i]); + } + } else if ((((ts->old_finger >> loop_i & 1) == 1) && + ((ts->pre_finger_mask >> loop_i & 1) == 0))) { + I(RAW_UP_STATUS, + loop_i + 1, ts->pre_finger_data[loop_i][0], + ts->pre_finger_data[loop_i][1]); + } else { + /* I("dbg hx_point_num=%d, old_finger=0x%02X," + * " pre_finger_mask=0x%02X\n", + * ts->hx_point_num, ts->old_finger, + * ts->pre_finger_mask); + */ + } + } + } +} + +void himax_ts_dbg_func(struct himax_ts_data *ts, int start) +{ + if (ts->debug_log_level & BIT(0)) { + /* I("debug level 1\n"); */ + himax_log_touch_data(start); + } + if (ts->debug_log_level & BIT(1)) { + /* I("debug level 2\n"); */ + himax_log_touch_event(ts, start); + } + if (ts->debug_log_level & BIT(2)) { + /* I("debug level 4\n"); */ + himax_log_touch_int_devation(start); + } + if (ts->debug_log_level & BIT(3)) { + /* I("debug level 8\n"); */ + himax_log_touch_event_detail(ts, start); + } +} + +static int himax_change_mode(uint8_t str_pw, uint8_t end_pw) +{ + uint8_t data[4] = {0}; + int count = 0; + + /*sense off*/ + g_core_fp.fp_sense_off(true); + /*mode change*/ + data[1] = str_pw; data[0] = str_pw; + if (g_core_fp.fp_assign_sorting_mode != NULL) + g_core_fp.fp_assign_sorting_mode(data); + + /*sense on*/ + g_core_fp.fp_sense_on(1); + /*wait mode change*/ + do { + if (g_core_fp.fp_check_sorting_mode != NULL) + g_core_fp.fp_check_sorting_mode(data); + if ((data[0] == end_pw) && (data[1] == end_pw)) + return 0; + + I("Now retry %d times!\n", count++); + msleep(50); + } while (count < 50); + + return ERR_WORK_OUT; +} + +static ssize_t himax_diag_cmd_write(char *buf, size_t len) +{ + struct himax_ts_data *ts = private_ts; + char *dbg_map_str = "mode:"; + char *str_ptr = NULL; + int str_len = 0; + int rst = 0; + uint8_t str_pw = 0; + uint8_t end_pw = 0; + + switch (len) { + case 1:/*raw out select - diag,X*/ + if (!kstrtoint(buf, 16, &rst)) { + ts->diag_cmd = rst; + I("%s: dsram_flag = %d\n", __func__, dsram_flag); + if (dsram_flag) { + /*Cancal work queue and return to stack*/ + process_type = 0; + dsram_flag = false; + cancel_delayed_work(&ts->himax_diag_delay_wrok); + himax_int_enable(1); + g_core_fp.fp_return_event_stack(); + } + g_core_fp.fp_diag_register_set(ts->diag_cmd, 0); + I("%s: Set raw out select 0x%X.\n", __func__, ts->diag_cmd); + } + if (!ts->diag_cmd) { + if (mode_flag) /*back to normal mode*/ + himax_change_mode(0x00, 0x99); + } + break; + case 2:/*data processing + rawout select - diag,XY*/ + if (!kstrtoint(buf, 16, &rst)) { + process_type = (rst >> 4) & 0xF; + ts->diag_cmd = rst & 0xF; + } + if (process_type <= 0 || ts->diag_cmd <= 0) + break; + else if (process_type > 0 && process_type <= 3) { + if (!dsram_flag) { + /*Start wrok queue*/ + himax_int_enable(0); + g_core_fp.fp_diag_register_set(ts->diag_cmd, process_type); + queue_delayed_work(ts->himax_diag_wq, + &ts->himax_diag_delay_wrok, 2 * HZ / 100); + dsram_flag = true; + I("%s: Start get raw data in DSRAM\n", __func__); + } else { + g_core_fp.fp_diag_register_set(ts->diag_cmd, process_type); + } + } + break; + case 9:/*change mode - mode:XXYY(start PW,end PW)*/ + str_ptr = strnstr(buf, dbg_map_str, len); + if (str_ptr) { + str_len = strlen(dbg_map_str); + if (!kstrtoint(buf + str_len, 16, &rst)) { + str_pw = (rst >> 8) & 0xFF; + end_pw = rst & 0xFF; + if (!himax_change_mode(str_pw, end_pw)) { + mode_flag = 1; + I("%s: change mode 0x%4X. str_pw = %2X, end_pw = %2X\n", __func__, rst, str_pw, end_pw); + } else + I("%s: change mode failed. str_pw = %2X, end_pw = %2X\n", __func__, str_pw, end_pw); + } + } else { + I("%s: Can't find string [%s].\n", __func__, dbg_map_str); + } + break; + default: + I("%s: Length is not correct.\n", __func__); + } + return len; +} + +static ssize_t himax_diag_arrange_write(char *buf, size_t len) +{ + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + g_diag_arr_num = buf[0] - '0'; + I("%s: g_diag_arr_num = %d\n", __func__, g_diag_arr_num); + return len; +} + +void himax_get_mutual_edge(void) +{ + int i = 0; + + for (i = 0; i < (ic_data->HX_RX_NUM * ic_data->HX_TX_NUM); i++) { + if (diag_mutual[i] > g_max_mutual) + g_max_mutual = diag_mutual[i]; + + if (diag_mutual[i] < g_min_mutual) + g_min_mutual = diag_mutual[i]; + } +} + +void himax_get_self_edge(void) +{ + int i = 0; + + for (i = 0; i < (ic_data->HX_RX_NUM + ic_data->HX_TX_NUM); i++) { + if (diag_self[i] > g_max_self) + g_max_self = diag_self[i]; + + if (diag_self[i] < g_min_self) + g_min_self = diag_self[i]; + } +} + +static void print_state_info(struct seq_file *s) +{ + /* seq_printf(s, "State_info_2bytes:%3d, %3d\n", + * _state_info[0],hx_state_info[1]); + */ + +#ifdef HX_NEW_EVENT_STACK_FORMAT + seq_printf(s, "ReCal = %d\t", hx_state_info[0] & 0x03); + seq_printf(s, "Base Line = %d\t", hx_state_info[0] >> 2 & 0x01); + seq_printf(s, "Palm = %d\t", hx_state_info[0] >> 3 & 0x01); + seq_printf(s, "Idle mode = %d\t", hx_state_info[0] >> 4 & 0x01); + seq_printf(s, "Water = %d\n", hx_state_info[0] >> 5 & 0x01); + seq_printf(s, "TX Hop = %d\t", hx_state_info[0] >> 6 & 0x01); + seq_printf(s, "AC mode = %d\t", hx_state_info[0] >> 7 & 0x01); + seq_printf(s, "Glove = %d\t", hx_state_info[1] & 0x01); + seq_printf(s, "Stylus = %d\t", hx_state_info[1] >> 1 & 0x01); + seq_printf(s, "Hovering = %d\t", hx_state_info[1] >> 2 & 0x01); + seq_printf(s, "Proximity = %d\t", hx_state_info[1] >> 3 & 0x01); + seq_printf(s, "KEY = %d\n", hx_state_info[1] >> 4 & 0x0F); +#else + seq_printf(s, "ReCal = %d\t", hx_state_info[0] & 0x01); + seq_printf(s, "Palm = %d\t", hx_state_info[0] >> 1 & 0x01); + seq_printf(s, "AC mode = %d\t", hx_state_info[0] >> 2 & 0x01); + seq_printf(s, "Water = %d\n", hx_state_info[0] >> 3 & 0x01); + seq_printf(s, "Glove = %d\t", hx_state_info[0] >> 4 & 0x01); + seq_printf(s, "TX Hop = %d\t", hx_state_info[0] >> 5 & 0x01); + seq_printf(s, "Base Line = %d\t", hx_state_info[0] >> 6 & 0x01); + seq_printf(s, "OSR Hop = %d\t", hx_state_info[1] >> 3 & 0x01); + seq_printf(s, "KEY = %d\n", hx_state_info[1] >> 4 & 0x0F); +#endif +} + +static void himax_diag_arrange_print(struct seq_file *s, int i, int j, + int transpose) +{ + if (transpose) + seq_printf(s, "%6d", diag_mutual[j + i * ic_data->HX_RX_NUM]); + else + seq_printf(s, "%6d", diag_mutual[i + j * ic_data->HX_RX_NUM]); +} + +/* ready to print second step which is column*/ +static void himax_diag_arrange_inloop(struct seq_file *s, int in_init, + int out_init, bool transpose, int j) +{ + int x_channel = ic_data->HX_RX_NUM; + int y_channel = ic_data->HX_TX_NUM; + int i; + int in_max = 0; + + if (transpose) + in_max = y_channel; + else + in_max = x_channel; + + if (in_init > 0) { /* bit0 = 1 */ + for (i = in_init - 1; i >= 0; i--) + himax_diag_arrange_print(s, i, j, transpose); + + if (transpose) { + if (out_init > 0) + seq_printf(s, " %5d\n", diag_self[j]); + else + seq_printf(s, " %5d\n", + diag_self[x_channel - j - 1]); + } + } else { /* bit0 = 0 */ + for (i = 0; i < in_max; i++) + himax_diag_arrange_print(s, i, j, transpose); + + if (transpose) { + if (out_init > 0) + seq_printf(s, " %5d\n", + diag_self[x_channel - j - 1]); + else + seq_printf(s, " %5d\n", diag_self[j]); + } + } +} + +/* print first step which is row */ +static void himax_diag_arrange_outloop(struct seq_file *s, int transpose, + int out_init, int in_init) +{ + int j; + int x_channel = ic_data->HX_RX_NUM; + int y_channel = ic_data->HX_TX_NUM; + int out_max = 0; + int self_cnt = 0; + + if (transpose) + out_max = x_channel; + else + out_max = y_channel; + + if (out_init > 0) { /* bit1 = 1 */ + self_cnt = 1; + + for (j = out_init - 1; j >= 0; j--) { + seq_printf(s, "%3c%02d%c", '[', j + 1, ']'); + himax_diag_arrange_inloop(s, in_init, out_init, + transpose, j); + + if (!transpose) { + seq_printf(s, " %5d\n", + diag_self[y_channel + x_channel - self_cnt]); + self_cnt++; + } + } + } else { /* bit1 = 0 */ + /* self_cnt = x_channel; */ + for (j = 0; j < out_max; j++) { + seq_printf(s, "%3c%02d%c", '[', j + 1, ']'); + himax_diag_arrange_inloop(s, in_init, out_init, + transpose, j); + + if (!transpose) { + seq_printf(s, " %5d\n", + diag_self[j + x_channel]); + } + } + } +} + +/* determin the output format of diag */ +static void himax_diag_arrange(struct seq_file *s) +{ + int x_channel = ic_data->HX_RX_NUM; + int y_channel = ic_data->HX_TX_NUM; + int bit2, bit1, bit0; + int i; + /* rotate bit */ + bit2 = g_diag_arr_num >> 2; + /* reverse Y */ + bit1 = g_diag_arr_num >> 1 & 0x1; + /* reverse X */ + bit0 = g_diag_arr_num & 0x1; + + if (g_diag_arr_num < 4) { + for (i = 0 ; i <= x_channel; i++) + seq_printf(s, "%3c%02d%c", '[', i, ']'); + + seq_puts(s, "\n"); + himax_diag_arrange_outloop(s, bit2, bit1 * y_channel, + bit0 * x_channel); + seq_printf(s, "%6c", ' '); + + if (bit0 == 1) { + for (i = x_channel - 1; i >= 0; i--) + seq_printf(s, "%6d", diag_self[i]); + } else { + for (i = 0; i < x_channel; i++) + seq_printf(s, "%6d", diag_self[i]); + } + } else { + for (i = 0 ; i <= y_channel; i++) + seq_printf(s, "%3c%02d%c", '[', i, ']'); + + seq_puts(s, "\n"); + himax_diag_arrange_outloop(s, bit2, bit1 * x_channel, + bit0 * y_channel); + seq_printf(s, "%6c", ' '); + + if (bit1 == 1) { + for (i = x_channel + y_channel - 1; i >= x_channel; + i--) + seq_printf(s, "%6d", diag_self[i]); + } else { + for (i = x_channel; i < x_channel + y_channel; i++) + seq_printf(s, "%6d", diag_self[i]); + } + } +} + +static void *himax_diag_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) + return NULL; + + return (void *)((unsigned long) *pos + 1); +} + +static void *himax_diag_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_diag_seq_stop(struct seq_file *s, void *v) +{ + kfree(v); +} + +/* DSRAM thread */ +bool himax_ts_diag_func(void) +{ + int retry = 3; + int i = 0, j = 0; + unsigned int index = 0; + int x_channel = ic_data->HX_RX_NUM; + int y_channel = ic_data->HX_TX_NUM; + int total_size = (y_channel * x_channel + y_channel + x_channel) * 2; + uint8_t *info_data; + int32_t *mutual_data = NULL; + int32_t *mutual_data_new = NULL; + int32_t *mutual_data_old = NULL; + int32_t *self_data = NULL; + int32_t *self_data_new = NULL; + int32_t *self_data_old = NULL; + int32_t new_data; + /* 1:common dsram,2:100 frame Max,3:N-(N-1)frame */ + int dsram_type = process_type; + + info_data = kcalloc(total_size, sizeof(uint8_t), GFP_KERNEL); + if (info_data == NULL) + return false; + + memset(info_data, 0, total_size * sizeof(uint8_t)); + + I("%s: process type=%d!\n", __func__, process_type); + + g_core_fp.fp_burst_enable(1); + + if (dsram_type <= 2) { + mutual_data = getMutualBuffer(); + self_data = getSelfBuffer(); + } else if (dsram_type == 3) { + mutual_data = getMutualBuffer(); + mutual_data_new = getMutualNewBuffer(); + mutual_data_old = getMutualOldBuffer(); + self_data = getSelfBuffer(); + self_data_new = getSelfNewBuffer(); + self_data_old = getSelfOldBuffer(); + } + + do { + if (retry <= 0) { + E("%s Get DSRAM data failed\n", __func__); + kfree(info_data); + return false; + } + if (g_core_fp.fp_get_DSRAM_data(info_data, dsram_flag)) + break; + } while (retry-- > 0); + + index = 0; + + for (i = 0; i < y_channel; i++) { /*mutual data*/ + for (j = 0; j < x_channel; j++) { + new_data = (((int8_t)info_data[index + 1] << 8) | + info_data[index]); + + if (dsram_type <= 1) { + mutual_data[i * x_channel + j] = new_data; + } else if (dsram_type == 2) { /* Keep max data */ + if (mutual_data[i * x_channel + j] < new_data) + mutual_data[i * x_channel + j] = new_data; + } else if (dsram_type == 3) { + /* Cal data for [N]-[N-1] frame */ + mutual_data_new[i * x_channel + j] = new_data; + mutual_data[i * x_channel + j] = + mutual_data_new[i * x_channel + j] - + mutual_data_old[i * x_channel + j]; + } + index += 2; + } + } + + for (i = 0; i < x_channel + y_channel; i++) { /*self data*/ + new_data = (info_data[index + 1] << 8 | info_data[index]); + if (dsram_type <= 1) { + self_data[i] = new_data; + } else if (dsram_type == 2) { /* Keep max data */ + if (self_data[i] < new_data) + self_data[i] = new_data; + } else if (dsram_type == 3) { /* Cal data for [N]-[N-1] frame */ + self_data_new[i] = new_data; + self_data[i] = self_data_new[i] - self_data_old[i]; + } + index += 2; + } + + kfree(info_data); + + if (dsram_type == 3) { + memcpy(mutual_data_old, mutual_data_new, + x_channel * y_channel * sizeof(int32_t)); + /* copy N data to N-1 array */ + memcpy(self_data_old, self_data_new, + (x_channel + y_channel) * sizeof(int32_t)); + /* copy N data to N-1 array */ + } + + diag_max_cnt++; + + if (dsram_type >= 1 && dsram_type <= 3) { + queue_delayed_work(private_ts->himax_diag_wq, + &private_ts->himax_diag_delay_wrok, 1 / 10 * HZ); + } + return true; +} + +static int himax_diag_print(struct seq_file *s, void *v) +{ + int x_num = ic_data->HX_RX_NUM; + int y_num = ic_data->HX_TX_NUM; + size_t ret = 0; + uint16_t mutual_num, self_num, width; + + mutual_num = x_num * y_num; + self_num = x_num + y_num; + /* don't add KEY_COUNT */ + width = x_num; + seq_printf(s, "ChannelStart: %4d, %4d\n\n", x_num, y_num); + + /* start to show out the raw data in adb shell */ + himax_diag_arrange(s); + seq_puts(s, "\n"); +#ifdef HX_EN_SEL_BUTTON + seq_puts(s, "\n"); + for (loop_i = 0; loop_i < ic_data->HX_BT_NUM; loop_i++) + seq_printf(s, "%6d", diag_self[ic_data->HX_RX_NUM + + ic_data->HX_TX_NUM + loop_i]); +#endif + seq_puts(s, "ChannelEnd"); + seq_puts(s, "\n"); + + /* print Mutual/Slef Maximum and Minimum */ + himax_get_mutual_edge(); + himax_get_self_edge(); + seq_printf(s, "Mutual Max:%3d, Min:%3d\n", g_max_mutual, + g_min_mutual); + seq_printf(s, "Self Max:%3d, Min:%3d\n", g_max_self, + g_min_self); + /* recovery status after print*/ + g_max_mutual = 0; + g_min_mutual = 0xFFFF; + g_max_self = 0; + g_min_self = 0xFFFF; + + /*pring state info*/ + print_state_info(s); + + if (s->count >= s->size) + overflow++; + + return ret; +} + +static int himax_diag_stack_read(struct seq_file *s, void *v) +{ + struct himax_ts_data *ts = private_ts; + + if (ts->diag_cmd) + himax_diag_print(s, v); + else + seq_puts(s, "Please set raw out select 'echo diag,rs:X > debug'\n\n"); + + return 0; +} + +static const struct seq_operations himax_diag_stack_ops = { + .start = himax_diag_seq_start, + .next = himax_diag_seq_next, + .stop = himax_diag_seq_stop, + .show = himax_diag_stack_read, +}; + +static int himax_diag_stack_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_diag_stack_ops); +}; + +static const struct file_operations himax_proc_stack_ops = { + .owner = THIS_MODULE, + .open = himax_diag_stack_open, + .read = seq_read, + .release = seq_release, +}; + +static int himax_sram_read(struct seq_file *s, void *v, uint8_t rs) +{ + struct himax_ts_data *ts = private_ts; + int d_type = 0; + + d_type = (!ts->diag_cmd)?rs:ts->diag_cmd; + + if (!overflow) { + if (!process_type) { + himax_int_enable(0); + g_core_fp.fp_diag_register_set(d_type, 0); + + if (!himax_ts_diag_func()) + seq_puts(s, "Get sram data failed."); + else + himax_diag_print(s, v); + + ts->diag_cmd = 0; + g_core_fp.fp_diag_register_set(0, 0); + himax_int_enable(1); + } + } + + if ((process_type <= 3 && ts->diag_cmd && dsram_flag) || overflow) { + himax_diag_print(s, v); + overflow = 0; + } + + return 0; +} + +static int himax_diag_iir_read(struct seq_file *s, void *v) +{ + return himax_sram_read(s, v, 0x09); +} + +static const struct seq_operations himax_diag_iir_ops = { + .start = himax_diag_seq_start, + .next = himax_diag_seq_next, + .stop = himax_diag_seq_stop, + .show = himax_diag_iir_read, +}; + +static int himax_diag_iir_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_diag_iir_ops); +}; + +static const struct file_operations himax_proc_iir_ops = { + .owner = THIS_MODULE, + .open = himax_diag_iir_open, + .read = seq_read, + .release = seq_release, +}; + +static int himax_diag_dc_read(struct seq_file *s, void *v) +{ + return himax_sram_read(s, v, 0x0A); +} + +static const struct seq_operations himax_diag_dc_ops = { + .start = himax_diag_seq_start, + .next = himax_diag_seq_next, + .stop = himax_diag_seq_stop, + .show = himax_diag_dc_read, +}; +static int himax_diag_dc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_diag_dc_ops); +}; + +static const struct file_operations himax_proc_dc_ops = { + .owner = THIS_MODULE, + .open = himax_diag_dc_open, + .read = seq_read, + .release = seq_release, +}; + +static int himax_diag_bank_read(struct seq_file *s, void *v) +{ + return himax_sram_read(s, v, 0x0B); +} + +static const struct seq_operations himax_diag_bank_ops = { + .start = himax_diag_seq_start, + .next = himax_diag_seq_next, + .stop = himax_diag_seq_stop, + .show = himax_diag_bank_read, +}; +static int himax_diag_bank_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_diag_bank_ops); +}; + +static const struct file_operations himax_proc_bank_ops = { + .owner = THIS_MODULE, + .open = himax_diag_bank_open, + .read = seq_read, + .release = seq_release, +}; + +static ssize_t himax_reset_write(char *buf, size_t len) +{ + if (len >= 12) { + I("%s: no command exceeds 12 chars.\n", __func__); + return -EFAULT; + } + +#ifdef HX_RST_PIN_FUNC + if (buf[0] == '1') + g_core_fp.fp_ic_reset(false, false); + else if (buf[0] == '2') + g_core_fp.fp_ic_reset(false, true); + else if (buf[0] == '3') + g_core_fp.fp_ic_reset(true, false); + else if (buf[0] == '4') + g_core_fp.fp_ic_reset(true, true); + /* else if (buf[0] == '5') */ + /* ESD_HW_REST(); */ +#endif +#if defined(HX_ZERO_FLASH) && defined(HX83102D) + if (g_core_fp.fp_0f_reload_to_active) + g_core_fp.fp_0f_reload_to_active(); +#endif + return len; +} + +static ssize_t himax_proc_FW_debug_read(char *buf, size_t len) +{ + ssize_t ret = 0; + uint8_t loop_i = 0; + uint8_t tmp_data[64] = {0}; + + cmd_set[0] = 0x01; + + if (g_core_fp.fp_read_FW_status(cmd_set, tmp_data) == NO_ERR) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "0x%02X%02X%02X%02X :\t", + cmd_set[5], cmd_set[4], cmd_set[3], cmd_set[2]); + + for (loop_i = 0; loop_i < cmd_set[1]; loop_i++) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "%5d\t", tmp_data[loop_i]); + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + } + + cmd_set[0] = 0x02; + + if (g_core_fp.fp_read_FW_status(cmd_set, tmp_data) == NO_ERR) { + for (loop_i = 0; loop_i < cmd_set[1]; + loop_i = loop_i + 2) { + if ((loop_i % 16) == 0) + ret += snprintf(buf_tmp + ret, + sizeof(buf_tmp) - ret, "0x%02X%02X%02X%02X :\t", + cmd_set[5], cmd_set[4], + cmd_set[3] + + (((cmd_set[2] + loop_i) >> 8) & 0xFF), + (cmd_set[2] + loop_i) & 0xFF); + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "%5d\t", tmp_data[loop_i] + + (tmp_data[loop_i + 1] << 8)); + + if ((loop_i % 16) == 14) + ret += snprintf(buf_tmp + ret, + sizeof(buf_tmp) - ret, "\n"); + } + } + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + + return ret; +} + +static ssize_t himax_proc_DD_debug_read(char *buf, size_t len) +{ + ssize_t ret = 0; + uint8_t tmp_data[64] = {0}; + uint8_t loop_i = 0; + + if (mutual_set_flag == 1) { + if (g_core_fp.fp_read_DD_status(cmd_set, tmp_data) == + NO_ERR) { + for (loop_i = 0; loop_i < cmd_set[0]; + loop_i++) { + if ((loop_i % 8) == 0) + ret += snprintf(buf_tmp + ret, + sizeof(buf_tmp) - ret, "0x%02X : ", loop_i); + + ret += snprintf(buf_tmp + ret, + sizeof(buf_tmp) - ret, "0x%02X ", tmp_data[loop_i]); + + if ((loop_i % 8) == 7) + ret += snprintf(buf_tmp + ret, + sizeof(buf_tmp) - ret, "\n"); + } + } else { + ret += snprintf(buf_tmp + ret, + sizeof(buf_tmp) - ret, "Get DD status falied!\n"); + } + } + + ret += snprintf(buf_tmp + ret, len - ret, "\n"); + + return ret; +} + +#define STR_TO_UL_ERR "String to ul is fail in cnt = %d, buf_tmp2 = %s\n" + +static ssize_t himax_proc_DD_debug_write(char *buf, size_t len) +{ + uint8_t i = 0; + uint8_t cnt = 2; + unsigned long result = 0; + char buf_tmp2[4]; + + if (len >= 20) { + I("%s: no command exceeds 20 chars.\n", __func__); + return -EFAULT; + } + + memset(buf_tmp2, 0x0, sizeof(buf_tmp2)); + + if (buf[2] == 'x' && buf[6] == 'x' && buf[10] == 'x') { + mutual_set_flag = 1; + + for (i = 3; i < 12; i = i + 4) { + memcpy(buf_tmp2, buf + i, 2); + + if (!kstrtoul(buf_tmp2, 16, &result)) + cmd_set[cnt] = (uint8_t)result; + else + I(STR_TO_UL_ERR, cnt, buf_tmp2); + + cnt--; + } + + I("cmd_set[2] = %02X, cmd_set[1] = %02X, cmd_set[0] = %02X\n", + cmd_set[2], cmd_set[1], cmd_set[0]); + } else { + mutual_set_flag = 0; + } + + return len; +} + +void setFlashBuffer(void) +{ + flash_buffer = kcalloc(Flash_Size, sizeof(uint8_t), GFP_KERNEL); +} + +void flash_dump_prog_set(uint8_t prog) +{ + g_flash_progress = prog; + if (prog == ONGOING) + debug_data->flash_dump_going = ONGOING; + else + debug_data->flash_dump_going = START; +} + +static int himax_proc_flash_read(struct seq_file *s, void *v) +{ + ssize_t ret = 0; + int i; + uint8_t flash_progress = g_flash_progress; + uint8_t flash_cmd = g_flash_cmd; + bool flash_rst = g_flash_dump_rst; + + I("flash_progress = %d\n", flash_progress); + + if (!flash_rst) { + seq_puts(s, "FlashStart:Fail\n"); + seq_puts(s, "FlashEnd\n"); + return ret; + } + + if (flash_progress == START) + seq_puts(s, "Flash dump - Start\n"); + else if (flash_progress == ONGOING) + seq_puts(s, "Flash dump - On-going\n"); + else if (flash_progress == FINISHED) + seq_puts(s, "Flash dump - Finished\n"); + + /*print flash dump data*/ + if (flash_cmd == 1 && flash_progress == FINISHED) { + seq_puts(s, "Start to print flash dump data\n"); + for (i = 0; i < Flash_Size; i++) { + seq_printf(s, "0x%02X,", flash_buffer[i]); + if (i % 16 == 15) + seq_puts(s, "\n"); + } + } + + seq_puts(s, "FlashEnd\n"); + + return ret; +} + +static ssize_t himax_proc_flash_write(struct file *filp, + const char __user *buff, size_t len, loff_t *data) +{ + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + I("%s: buf = %s\n", __func__, buf); + + if (g_flash_progress == ONGOING) { + E("%s: process is busy , return!\n", __func__); + return len; + } + + if ((buf[1] == '_') && (buf[2] == '3') && (buf[3] == '2')) + Flash_Size = FW_SIZE_32k; + else if ((buf[1] == '_') && (buf[2] == '6')) { + if (buf[3] == '0') + Flash_Size = FW_SIZE_60k; + else if (buf[3] == '4') + Flash_Size = FW_SIZE_64k; + + } else if ((buf[1] == '_') && (buf[2] == '2')) { + if (buf[3] == '4') + Flash_Size = FW_SIZE_124k; + else if (buf[3] == '8') + Flash_Size = FW_SIZE_128k; + } + + /*1 : print flash to window, 2 : dump to sdcard*/ + if (buf[0] == '1') { + /* 1_32,1_60,1_64,1_24,1_28 for flash size: + * 32k,60k,64k,124k,128k + */ + g_flash_cmd = 1; + flash_dump_prog_set(START); + g_flash_dump_rst = true; + queue_work(private_ts->flash_wq, &private_ts->flash_work); + } else if (buf[0] == '2') { + /* 2_32,2_60,2_64,2_24,2_28 for flash size: + * 32k,60k,64k,124k,128k + */ + g_flash_cmd = 2; + flash_dump_prog_set(START); + g_flash_dump_rst = true; + queue_work(private_ts->flash_wq, &private_ts->flash_work); + } + + return len; +} + +static void *himax_flash_dump_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) + return NULL; + + return (void *)((unsigned long) *pos + 1); +} + +static void *himax_flash_dump_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_flash_dump_seq_stop(struct seq_file *s, void *v) +{ +} + +static const struct seq_operations himax_flash_dump_seq_ops = { + .start = himax_flash_dump_seq_start, + .next = himax_flash_dump_seq_next, + .stop = himax_flash_dump_seq_stop, + .show = himax_proc_flash_read, +}; +static int himax_flash_dump_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_flash_dump_seq_ops); +}; + +static const struct file_operations himax_proc_flash_ops = { + .owner = THIS_MODULE, + .open = himax_flash_dump_proc_open, + .read = seq_read, + .write = himax_proc_flash_write, +}; + +void himax_ts_flash_func(void) +{ + uint8_t flash_command = g_flash_cmd; + + himax_int_enable(0); + flash_dump_prog_set(ONGOING); + + /*msleep(100);*/ + I("%s: flash_command = %d enter.\n", __func__, flash_command); + + if (flash_command == 1 || flash_command == 2) { + g_core_fp.fp_flash_dump_func(flash_command, Flash_Size, + flash_buffer); + g_flash_dump_rst = true; + } + + I("Complete~~~~~~~~~~~~~~~~~~~~~~~\n"); + +/* if (flash_command == 2) { + * struct file *fn; + * struct filename *vts_name; + * + * vts_name = kp_getname_kernel(FLASH_DUMP_FILE); + * fn = kp_file_open_name(vts_name, O_CREAT | O_WRONLY, 0); + * + * if (!IS_ERR(fn)) { + * I("%s create file and ready to write\n", __func__); + * fn->f_op->write(fn, flash_buffer, + * Flash_Size * sizeof(uint8_t), &fn->f_pos); + * filp_close(fn, NULL); + * } else { + * E("%s Open file failed!\n", __func__); + * g_flash_dump_rst = false; + * } + * } + */ + himax_int_enable(1); + flash_dump_prog_set(FINISHED); +} + +static ssize_t himax_sense_on_off_write(char *buf, size_t len) +{ + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (buf[0] == '0') { + g_core_fp.fp_sense_off(true); + I("Sense off\n"); + } else if (buf[0] == '1') { + if (buf[1] == 's') { + g_core_fp.fp_sense_on(0x00); + I("Sense on re-map on, run sram\n"); + } else { + g_core_fp.fp_sense_on(0x01); + I("Sense on re-map off, run flash\n"); + } + } else { + I("Do nothing\n"); + } + + return len; +} + +#ifdef HX_ESD_RECOVERY +static ssize_t himax_esd_cnt_read(char *buf, size_t len) +{ + size_t ret = 0; + + I("%s: enter, %d\n", __func__, __LINE__); + + ret += snprintf(buf_tmp + ret, len - ret, + "EB_cnt = %d, EC_cnt = %d, ED_cnt = %d\n", + hx_EB_event_flag, hx_EC_event_flag, hx_ED_event_flag); + + return ret; +} + +static ssize_t himax_esd_cnt_write(char *buf, size_t len) +{ + int i = 0; + + if (len >= 12) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + I("Clear ESD Flag\n"); + + if (buf[i] == '0') { + hx_EB_event_flag = 0; + hx_EC_event_flag = 0; + hx_ED_event_flag = 0; + } + + return len; +} + +#endif + +static int printMat(char *temp_buf, size_t len, int max_size, + uint8_t *guest_str, int loc) +{ + int ret = loc; + int i; + + for (i = 0; i < max_size; i++) { + if ((i % 16) == 0 && i > 0) + ret += snprintf(temp_buf + ret, len - ret, "\n"); + + ret += snprintf(temp_buf + ret, len - ret, "0x%02X\t", + guest_str[i]); + } + return ret; +} + +static int printUnit(char *temp_buf, size_t len, int max_size, char *info_item, + uint8_t *guest_str, int loc) +{ + int ret = loc; + + ret += snprintf(temp_buf + ret, len - ret, "%s:\n", info_item); + ret = printMat(temp_buf, len, max_size, guest_str, ret); + ret += snprintf(temp_buf + ret, len - ret, "\n"); + return ret; +} + +#ifdef HX_TP_PROC_GUEST_INFO +static ssize_t himax_proc_guest_info_read(char *buf, size_t len) +{ + int ret = 0; + int j = 0; + int max_size = 128; + struct hx_guest_info *info = g_guest_info_data; + + I("guest info progress\n"); + + if (g_core_fp.guest_info_get_status()) { + ret += snprintf(buf_tmp + ret, len - ret, + "Not Ready\n"); + goto END_FUNCTION; + } else { + if (info->g_guest_info_type == 1) { + for (j = 0; j < 3; j++) { + ret = printUnit(buf_tmp, len, max_size, + g_guest_info_item[j], + info->g_guest_str[j], ret); + I("str[%d] %s\n", j, + info->g_guest_str[j]); + } + ret = printUnit(buf_tmp, len, max_size, + g_guest_info_item[8], info->g_guest_str[8], + ret); + + I("str[8] %s\n", + info->g_guest_str[8]); + + ret = printUnit(buf_tmp, len, max_size, + g_guest_info_item[9], info->g_guest_str[9], + ret); + + I("str[9] %s\n", + info->g_guest_str[9]); + } else if (info->g_guest_info_type == 0) { + for (j = 0; j < 10; j++) { + if (j == 3) + j = 8; + ret += snprintf(buf_tmp + ret, + len - ret, "%s:\n", + g_guest_info_item[j]); + if (info->g_guest_data_type[j] == 0) { + ret += snprintf(buf_tmp + ret, + len - ret, "%s", + g_guest_info_data->g_guest_str_in_format[j]); + } else { + ret = printMat(buf_tmp, len, + info->g_guest_data_len[j], + g_guest_info_data->g_guest_str_in_format[j], ret); + } + ret += snprintf(buf_tmp + ret, + len - ret, "\n"); + } + } + } + +END_FUNCTION: + return ret; +} + +static ssize_t himax_proc_guest_info_write(char *buf, size_t len) +{ + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + I("%s: buf = %s\n", __func__, buf); + if (buf[0] == 'r') { + I("%s,Test to get", __func__); + queue_work(private_ts->guest_info_wq, + &private_ts->guest_info_work); + } + return len; +} + +#endif + +static ssize_t himax_debug_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + ssize_t ret = 0; + int i = 0; + + if (!HX_PROC_SEND_FLAG) { + I("%s, Enter, len = %d\n", __func__, (int)len); + memset(buf_tmp, 0, sizeof(buf_tmp)); + + if (dbg_cmd_flag) { + memset(buf_tmp, 0, sizeof(buf_tmp)); + if (dbg_func_ptr_r[dbg_cmd_flag]) + ret += dbg_func_ptr_r[dbg_cmd_flag](buf, len); + else + goto END_FUNC_R; + } + + if (debug_level_cmd == 't') { + if (fw_update_complete) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "FW Update Complete "); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "FW Update Fail "); + + } else if (debug_level_cmd == 'h') { + if (handshaking_result == 0) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Handshaking Result = %d (MCU Running)\n", + handshaking_result); + else if (handshaking_result == 1) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Handshaking Result = %d (MCU Stop)\n", + handshaking_result); + else if (handshaking_result == 2) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Handshaking Result = %d (I2C Error)\n", + handshaking_result); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Handshaking Result = error\n"); + + } else if (debug_level_cmd == 'v') { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "FW_VER = 0x%2.2X\n", ic_data->vendor_fw_ver); + + if (private_ts->chip_cell_type == CHIP_IS_ON_CELL) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CONFIG_VER = 0x%2.2X\n", + ic_data->vendor_config_ver); + else { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "TOUCH_VER = 0x%2.2X\n", + ic_data->vendor_touch_cfg_ver); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "DISPLAY_VER = 0x%2.2X\n", + ic_data->vendor_display_cfg_ver); + } + if (ic_data->vendor_cid_maj_ver < 0 && + ic_data->vendor_cid_min_ver < 0) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CID_VER = NULL\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CID_VER = 0x%2.2X\n", + (ic_data->vendor_cid_maj_ver << 8 | + ic_data->vendor_cid_min_ver)); + + if (ic_data->vendor_panel_ver < 0) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "PANEL_VER = NULL\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "PANEL_VER = 0x%2.2X\n", + ic_data->vendor_panel_ver); + if (private_ts->chip_cell_type == CHIP_IS_IN_CELL) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Cusomer = %s\n", ic_data->vendor_cus_info); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Project = %s\n", ic_data->vendor_proj_info); + } +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#ifdef HX_EN_DYNAMIC_NAME + if (ic_data->vendor_semifac == 1) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "SEMI_FAC = UMC\n"); + else if (ic_data->vendor_semifac == 2) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "SEMI_FAC = PSC\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "SEMI_FAC = NULL\n"); +#endif +#endif + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Himax Touch Driver Version:\n"); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "%s\n", HIMAX_DRIVER_VER); + } else if (debug_level_cmd == 'd') { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Himax Touch IC Information :\n"); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "%s\n", private_ts->chip_name); + + switch (IC_CHECKSUM) { + case HX_TP_BIN_CHECKSUM_SW: + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "IC Checksum : SW\n"); + break; + + case HX_TP_BIN_CHECKSUM_HW: + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "IC Checksum : HW\n"); + break; + + case HX_TP_BIN_CHECKSUM_CRC: + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "IC Checksum : CRC\n"); + break; + + default: + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "IC Checksum error.\n"); + } + + if (ic_data->HX_INT_IS_EDGE) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Driver register Interrupt : EDGE TIRGGER\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Driver register Interrupt : LEVEL TRIGGER\n"); + + if (private_ts->protocol_type == PROTOCOL_TYPE_A) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Protocol : TYPE_A\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Protocol : TYPE_B\n"); + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "RX Num : %d\n", ic_data->HX_RX_NUM); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "TX Num : %d\n", ic_data->HX_TX_NUM); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "BT Num : %d\n", ic_data->HX_BT_NUM); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "X Resolution : %d\n", ic_data->HX_X_RES); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Y Resolution : %d\n", ic_data->HX_Y_RES); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Max Point : %d\n", ic_data->HX_MAX_PT); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "XY reverse : %d\n", ic_data->HX_XY_REVERSE); +#ifdef HX_TP_PROC_2T2R + if (Is_2T2R) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "2T2R panel\n"); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "RX Num_2 : %d\n", HX_RX_NUM_2); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "TX Num_2 : %d\n", HX_TX_NUM_2); + } +#endif + } else if (debug_level_cmd == 'i') { + if (g_core_fp.fp_read_i2c_status()) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "I2C communication is bad.\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "I2C communication is good.\n"); + } else if (debug_level_cmd == 'n') { + /* Edgd = 1, Level = 0 */ + if (g_core_fp.fp_read_ic_trigger_type() == 1) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "IC Interrupt type is edge trigger.\n"); + else if (g_core_fp.fp_read_ic_trigger_type() == 0) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "IC Interrupt type is level trigger.\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Unkown IC trigger type.\n"); + + if (ic_data->HX_INT_IS_EDGE) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Driver register Interrupt : EDGE TIRGGER\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Driver register Interrupt : LEVEL TRIGGER\n"); + } else if (debug_level_cmd == 'l') { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "LotID : "); + for (i = 0; i < 13; i++) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "%02X", ic_data->vendor_ic_id[i]); + } + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "\n"); + } + +END_FUNC_R: + if (copy_to_user(buf, buf_tmp, (len > BUF_SIZE) ? BUF_SIZE : len)) + I("%s,here:%d\n", __func__, __LINE__); + + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static ssize_t himax_debug_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + char fileName[128]; + char buf[80] = {0}; + int result = 0; +#ifndef HX_ZERO_FLASH + int fw_type = 0; + const struct firmware *fw = NULL; +#endif + + char *str_ptr = NULL; + int str_len = 0; + int i = 0; + + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + buf[strlen(buf) - 1] = 0;/*remove \n*/ + + while (dbg_cmd_str[i]) { + str_ptr = strnstr(buf, dbg_cmd_str[i], len); + if (str_ptr) { + str_len = strlen(dbg_cmd_str[i]); + dbg_cmd_flag = i + 1; + debug_level_cmd = 0; + I("Cmd is correct :%s, dbg_cmd = %d\n", str_ptr, dbg_cmd_flag); + break; + } + i++; + } + if (!str_ptr) { + I("Cmd is not correct\n"); + dbg_cmd_flag = 0; + } + + if (buf[str_len] == ',') { + dbg_cmd_par = buf + str_len + 1; + if (dbg_func_ptr_w[dbg_cmd_flag]) + dbg_func_ptr_w[dbg_cmd_flag](dbg_cmd_par, len - str_len - 2);/* 2 => '/n' + ','*/ + I("string of paremeter is %s, dbg_cmd_par = %s\n", buf + str_len + 1, dbg_cmd_par); + } else + I("No paremeter of this cmd\n"); + + if (dbg_cmd_flag) + return len; + + if (buf[0] == 'h') { /* handshaking */ + debug_level_cmd = buf[0]; + himax_int_enable(0); + /* 0:Running, 1:Stop, 2:I2C Fail */ + handshaking_result = g_core_fp.fp_hand_shaking(); + himax_int_enable(1); + return len; + } else if (buf[0] == 'v') { /* firmware version */ + himax_int_enable(0); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#endif + debug_level_cmd = buf[0]; + g_core_fp.fp_read_FW_ver(); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(true, false); +#else + g_core_fp.fp_system_reset(); +#endif +#if defined(HX_ZERO_FLASH) && defined(HX83102D) + if (g_core_fp.fp_0f_reload_to_active) + g_core_fp.fp_0f_reload_to_active(); +#endif + himax_int_enable(1); + /* himax_check_chip_version(); */ + return len; + } else if (buf[0] == 'd') { /* ic information */ + debug_level_cmd = buf[0]; + return len; + } else if (buf[0] == 't') { + if (buf[1] == 's' && + buf[2] == 'd' && + buf[3] == 'b' && + buf[4] == 'g' + ) { + if (buf[5] == '1') { + I("Open Ts Debug!\n"); + g_ts_dbg = 1; + } else if (buf[5] == '0') { + I("Close Ts Debug!\n"); + g_ts_dbg = 0; + } else { + E("Parameter fault for ts debug\n"); + } + goto ENDFUCTION; + } + himax_int_enable(0); + debug_level_cmd = buf[0]; + fw_update_complete = false; + memset(fileName, 0, 128); + /* parse the file name */ + snprintf(fileName, len - 2, "%s", &buf[2]); + +#ifdef HX_ZERO_FLASH + I("NOW Running Zero flash update!\n"); + I("%s: upgrade from file(%s) start!\n", __func__, fileName); + result = g_core_fp.fp_0f_op_file_dirly(fileName); + if (result) { + fw_update_complete = false; + I("Zero flash update fail!\n"); + goto ENDFUCTION; + } else { + fw_update_complete = true; + I("Zero flash update complete!\n"); + } + goto firmware_upgrade_done; +#else + I("NOW Running common flow update!\n"); + I("%s: upgrade from file(%s) start!\n", __func__, fileName); + result = request_firmware(&fw, fileName, private_ts->dev); + + if (result < 0) { + I("fail to request_firmware fwpath: %s (ret:%d)\n", + fileName, result); + return result; + } + + I("%s: FW image: %02X, %02X, %02X, %02X\n", __func__, + fw->data[0], fw->data[1], fw->data[2], fw->data[3]); + fw_type = (fw->size) / 1024; + /* start to upgrade */ + himax_int_enable(0); + I("Now FW size is : %dk\n", fw_type); + + switch (fw_type) { + case 32: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_32k( + (unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", + __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", + __func__, __LINE__); + fw_update_complete = true; + } + break; + + case 60: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_60k( + (unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", + __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", + __func__, __LINE__); + fw_update_complete = true; + } + break; + + case 64: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_64k( + (unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", + __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", + __func__, __LINE__); + fw_update_complete = true; + } + break; + + case 124: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_124k( + (unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", + __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", + __func__, __LINE__); + fw_update_complete = true; + } + break; + + case 128: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_128k( + (unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", + __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", + __func__, __LINE__); + fw_update_complete = true; + } + break; + + default: + E("%s: Flash command fail: %d\n", __func__, __LINE__); + fw_update_complete = false; + break; + } + release_firmware(fw); + goto firmware_upgrade_done; +#endif + } else if (buf[0] == 'i' && buf[1] == '2' && buf[2] == 'c') { + /* i2c communication */ + debug_level_cmd = 'i'; + return len; + } else if (buf[0] == 'i' && buf[1] == 'n' && buf[2] == 't') { + /* INT trigger */ + debug_level_cmd = 'n'; + return len; +#ifdef HX_ZERO_FLASH + } else if (buf[0] == 'z') { + result = buf[1] - '0'; + I("check type = %d\n", result); + g_core_fp.fp_0f_operation_check(result); + return len; + } else if (buf[0] == 'p') { + I("NOW debug echo r!\n"); + /* himax_program_sram(); */ + private_ts->himax_0f_update_wq = + create_singlethread_workqueue("HMX_update_0f_reuqest_write"); + + if (!private_ts->himax_0f_update_wq) + E(" allocate syn_update_wq failed\n"); + + INIT_DELAYED_WORK(&private_ts->work_0f_update, + g_core_fp.fp_0f_operation); + queue_delayed_work(private_ts->himax_0f_update_wq, + &private_ts->work_0f_update, msecs_to_jiffies(100)); + return len; + } else if (buf[0] == 'x') { + g_core_fp.fp_system_reset(); +#if defined(HX_ZERO_FLASH) && defined(HX83102D) + if (g_core_fp.fp_0f_reload_to_active) + g_core_fp.fp_0f_reload_to_active(); +#endif + return len; +#endif + } else if (buf[0] == 'l' && buf[1] == 'o' && buf[2] == 't') { + debug_level_cmd = buf[0]; + g_core_fp.fp_sense_off(true); + g_core_fp.fp_ic_id_read(); + g_core_fp.fp_sense_on(0x01); + return len; + } + /* others,do nothing */ + debug_level_cmd = 0; + return len; + +firmware_upgrade_done: + g_core_fp.fp_reload_disable(0); + g_core_fp.fp_read_FW_ver(); + g_core_fp.fp_touch_information(); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(true, false); +#if defined(HX_ZERO_FLASH) && defined(HX83102D) + if (g_core_fp.fp_0f_reload_to_active) + g_core_fp.fp_0f_reload_to_active(); +#endif +#else + g_core_fp.fp_sense_on(0x00); +#endif + + himax_int_enable(1); + /* todo himax_chip->tp_firmware_upgrade_proceed = 0; + * todo himax_chip->suspend_state = 0; + * todo enable_irq(himax_chip->irq); + */ +ENDFUCTION: + return len; +} + +static const struct file_operations himax_proc_debug_ops = { + .owner = THIS_MODULE, + .read = himax_debug_read, + .write = himax_debug_write, +}; + +static void himax_himax_data_init(void) +{ + debug_data->fp_ts_dbg_func = himax_ts_dbg_func; + debug_data->fp_set_diag_cmd = himax_set_diag_cmd; + debug_data->flash_dump_going = false; +} + +static void himax_ts_flash_work_func(struct work_struct *work) +{ + himax_ts_flash_func(); +} +#ifdef HX_TP_PROC_GUEST_INFO +static void himax_ts_guest_info_work_func(struct work_struct *work) +{ + g_core_fp.read_guest_info(); +} +#endif + +static void himax_ts_diag_work_func(struct work_struct *work) +{ + himax_ts_diag_func(); +} + +void dbg_func_ptr_init(void) +{ + /*debug function ptr init*/ + dbg_func_ptr_r[1] = himax_crc_test_read; + dbg_func_ptr_r[2] = himax_proc_FW_debug_read; + dbg_func_ptr_r[3] = himax_attn_read; + dbg_func_ptr_r[4] = himax_layout_read; + dbg_func_ptr_w[4] = himax_layout_write; + dbg_func_ptr_r[5] = himax_proc_DD_debug_read; + dbg_func_ptr_w[5] = himax_proc_DD_debug_write; +#ifdef HX_ESD_RECOVERY + dbg_func_ptr_r[6] = himax_esd_cnt_read; + dbg_func_ptr_w[6] = himax_esd_cnt_write; +#endif + dbg_func_ptr_w[7] = himax_sense_on_off_write; + dbg_func_ptr_r[8] = himax_debug_level_read; + dbg_func_ptr_w[8] = himax_debug_level_write; +#ifdef HX_TP_PROC_GUEST_INFO + dbg_func_ptr_r[9] = himax_proc_guest_info_read; + dbg_func_ptr_w[9] = himax_proc_guest_info_write; +#endif + dbg_func_ptr_r[10] = himax_int_en_read; + dbg_func_ptr_w[10] = himax_int_en_write; + dbg_func_ptr_w[11] = himax_proc_register_write; + dbg_func_ptr_r[11] = himax_proc_register_read; + dbg_func_ptr_w[12] = himax_reset_write; + dbg_func_ptr_w[13] = himax_diag_arrange_write; + dbg_func_ptr_w[14] = himax_diag_cmd_write; +} + +int himax_touch_proc_init(void) +{ + himax_proc_diag_dir = proc_mkdir(HIMAX_PROC_DIAG_FOLDER, himax_touch_proc_dir); + + if (himax_proc_diag_dir == NULL) { + E(" %s: himax_proc_diag_dir file create failed!\n", __func__); + return -ENOMEM; + } + + himax_proc_vendor_file = proc_create(HIMAX_PROC_VENDOR_FILE, 0444, + himax_touch_proc_dir, &himax_proc_vendor_ops); + if (himax_proc_vendor_file == NULL) { + E(" %s: proc vendor file create failed!\n", __func__); + goto fail_1; + } + + himax_proc_stack_file = proc_create(HIMAX_PROC_STACK_FILE, 0444, + himax_proc_diag_dir, &himax_proc_stack_ops); + if (himax_proc_stack_file == NULL) { + E(" %s: proc stack file create failed!\n", __func__); + goto fail_2_1; + } + + himax_proc_iir_file = proc_create(HIMAX_PROC_IIR_FILE, 0444, + himax_proc_diag_dir, &himax_proc_iir_ops); + if (himax_proc_iir_file == NULL) { + E(" %s: proc iir file create failed!\n", __func__); + goto fail_2_2; + } + + himax_proc_dc_file = proc_create(HIMAX_PROC_DC_FILE, 0444, + himax_proc_diag_dir, &himax_proc_dc_ops); + if (himax_proc_dc_file == NULL) { + E(" %s: proc dc file create failed!\n", __func__); + goto fail_2_3; + } + + himax_proc_bank_file = proc_create(HIMAX_PROC_BANK_FILE, 0444, + himax_proc_diag_dir, &himax_proc_bank_ops); + if (himax_proc_bank_file == NULL) { + E(" %s: proc bank file create failed!\n", __func__); + goto fail_2_4; + } + + himax_proc_debug_file = proc_create(HIMAX_PROC_DEBUG_FILE, + 0644, himax_touch_proc_dir, + &himax_proc_debug_ops); + if (himax_proc_debug_file == NULL) { + E(" %s: proc debug file create failed!\n", __func__); + goto fail_3; + } + dbg_func_ptr_init(); + + himax_proc_flash_dump_file = proc_create(HIMAX_PROC_FLASH_DUMP_FILE, + 0644, himax_touch_proc_dir, + &himax_proc_flash_ops); + if (himax_proc_flash_dump_file == NULL) { + E(" %s: proc flash dump file create failed!\n", __func__); + goto fail_4; + } + +#if defined(HX_PEN_FUNC_EN) + himax_proc_pen_pos_file = proc_create(HIMAX_PROC_PEN_POS_FILE, + 0666, + himax_touch_proc_dir, &himax_proc_pen_pos_ops); + if (himax_proc_pen_pos_file == NULL) { + E(" %s: proc CRC test file create failed!\n", __func__); + goto fail_5; + } +#endif + + return 0; +#if defined(HX_PEN_FUNC_EN) + remove_proc_entry(HIMAX_PROC_PEN_POS_FILE, himax_touch_proc_dir); +fail_5: +#endif +remove_proc_entry(HIMAX_PROC_FLASH_DUMP_FILE, himax_touch_proc_dir); +fail_4: remove_proc_entry(HIMAX_PROC_DEBUG_FILE, himax_touch_proc_dir); +fail_3: remove_proc_entry(HIMAX_PROC_BANK_FILE, himax_proc_diag_dir); +fail_2_4: remove_proc_entry(HIMAX_PROC_DC_FILE, himax_proc_diag_dir); +fail_2_3: remove_proc_entry(HIMAX_PROC_IIR_FILE, himax_proc_diag_dir); +fail_2_2: remove_proc_entry(HIMAX_PROC_STACK_FILE, himax_proc_diag_dir); +fail_2_1: remove_proc_entry(HIMAX_PROC_VENDOR_FILE, himax_touch_proc_dir); +fail_1: + return -ENOMEM; +} + +void himax_touch_proc_deinit(void) +{ +#if defined(HX_PEN_FUNC_EN) + remove_proc_entry(HIMAX_PROC_PEN_POS_FILE, himax_touch_proc_dir); +#endif + remove_proc_entry(HIMAX_PROC_FLASH_DUMP_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_DEBUG_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_BANK_FILE, himax_proc_diag_dir); + remove_proc_entry(HIMAX_PROC_DC_FILE, himax_proc_diag_dir); + remove_proc_entry(HIMAX_PROC_IIR_FILE, himax_proc_diag_dir); + remove_proc_entry(HIMAX_PROC_STACK_FILE, himax_proc_diag_dir); + remove_proc_entry(HIMAX_PROC_VENDOR_FILE, himax_touch_proc_dir); +} + +int himax_debug_init(void) +{ + struct himax_ts_data *ts = private_ts; + int err = 0; + + I("%s:Enter\n", __func__); + + if (ts == NULL) { + E("%s: ts struct is NULL\n", __func__); + return -EPROBE_DEFER; + } + + reg_read_data = kzalloc(DEBUG_READ_BUF_SIZE * sizeof(uint8_t), GFP_KERNEL); + if (reg_read_data == NULL) { + err = -ENOMEM; + goto err_alloc_reg_read_data_fail; + } + + debug_data = kzalloc(sizeof(struct himax_debug), GFP_KERNEL); + if (debug_data == NULL) { /*Allocate debug data space*/ + err = -ENOMEM; + goto err_alloc_debug_data_fail; + } + + himax_himax_data_init(); + + ts->flash_wq = create_singlethread_workqueue("himax_flash_wq"); + + if (!ts->flash_wq) { + E("%s: create flash workqueue failed\n", __func__); + err = -ENOMEM; + goto err_create_flash_dump_wq_failed; + } + + INIT_WORK(&ts->flash_work, himax_ts_flash_work_func); + + g_flash_progress = START; + setFlashBuffer(); + +#ifdef HX_TP_PROC_GUEST_INFO + + if (g_guest_info_data == NULL) { + g_guest_info_data = kzalloc(sizeof(struct hx_guest_info), + GFP_KERNEL); + g_guest_info_data->g_guest_info_ongoing = 0; + g_guest_info_data->g_guest_info_type = 0; + } + + ts->guest_info_wq = + create_singlethread_workqueue("himax_guest_info_wq"); + if (!ts->guest_info_wq) { + E("%s: create guest info workqueue failed\n", __func__); + err = -ENOMEM; + goto err_create_guest_info_wq_failed; + } + INIT_WORK(&ts->guest_info_work, himax_ts_guest_info_work_func); +#endif + + ts->himax_diag_wq = create_singlethread_workqueue("himax_diag"); + + if (!ts->himax_diag_wq) { + E("%s: create diag workqueue failed\n", __func__); + err = -ENOMEM; + goto err_create_diag_wq_failed; + } + + INIT_DELAYED_WORK(&ts->himax_diag_delay_wrok, himax_ts_diag_work_func); + + setMutualBuffer(ic_data->HX_RX_NUM, ic_data->HX_TX_NUM); + if (getMutualBuffer() == NULL) { + E("%s: mutual buffer allocate fail failed\n", __func__); + return MEM_ALLOC_FAIL; + } + + setMutualNewBuffer(ic_data->HX_RX_NUM, ic_data->HX_TX_NUM); + if (getMutualNewBuffer() == NULL) { + E("%s: mutual new buffer allocate fail failed\n", __func__); + return MEM_ALLOC_FAIL; + } + + setMutualOldBuffer(ic_data->HX_RX_NUM, ic_data->HX_TX_NUM); + if (getMutualOldBuffer() == NULL) { + E("%s: mutual old buffer allocate fail failed\n", __func__); + return MEM_ALLOC_FAIL; + } + +#ifdef HX_TP_PROC_2T2R + + if (Is_2T2R) { + setMutualBuffer_2(ic_data->HX_RX_NUM_2, ic_data->HX_TX_NUM_2); + + if (getMutualBuffer_2() == NULL) { + E("%s: mutual buffer 2 allocate fail failed\n", + __func__); + return MEM_ALLOC_FAIL; + } + } +#endif + + himax_touch_proc_init(); + + return 0; + + cancel_delayed_work_sync(&ts->himax_diag_delay_wrok); + destroy_workqueue(ts->himax_diag_wq); +err_create_diag_wq_failed: + +#ifdef HX_TP_PROC_GUEST_INFO +err_create_guest_info_wq_failed: + + destroy_workqueue(ts->guest_info_wq); + if (g_guest_info_data != NULL) { + kfree(g_guest_info_data); + g_guest_info_data = NULL; + } +#endif + destroy_workqueue(ts->flash_wq); + +err_create_flash_dump_wq_failed: + kfree(debug_data); + debug_data = NULL; +err_alloc_debug_data_fail: + kfree(reg_read_data); + reg_read_data = NULL; +err_alloc_reg_read_data_fail: + + return err; +} +EXPORT_SYMBOL(himax_debug_init); + +int himax_debug_remove(void) +{ + struct himax_ts_data *ts = private_ts; + + himax_touch_proc_deinit(); + + cancel_delayed_work_sync(&ts->himax_diag_delay_wrok); +#ifdef HX_TP_PROC_GUEST_INFO + destroy_workqueue(ts->guest_info_wq); + if (g_guest_info_data != NULL) + kfree(g_guest_info_data); +#endif + destroy_workqueue(ts->himax_diag_wq); + destroy_workqueue(ts->flash_wq); + + kfree(debug_data); + + return 0; +} +EXPORT_SYMBOL(himax_debug_remove); + diff --git a/drivers/input/touchscreen/himax_0flash_mmi/himax_debug.h b/drivers/input/touchscreen/himax_0flash_mmi/himax_debug.h new file mode 100644 index 000000000000..586923f9e065 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/himax_debug.h @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for debug nodes + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef H_HIMAX_DEBUG +#define H_HIMAX_DEBUG + +#include "himax_platform.h" +#include "himax_common.h" + + +#ifdef HX_ESD_RECOVERY + extern u8 HX_ESD_RESET_ACTIVATE; + extern int hx_EB_event_flag; + extern int hx_EC_event_flag; + extern int hx_ED_event_flag; +#endif + +#define HIMAX_PROC_VENDOR_FILE "vendor" +extern struct proc_dir_entry *himax_proc_vendor_file; + +#define HIMAX_PROC_PEN_POS_FILE "pen_pos" + +int himax_touch_proc_init(void); +void himax_touch_proc_deinit(void); +extern int himax_int_en_set(void); + +extern uint8_t byte_length; +extern uint8_t register_command[4]; +extern uint8_t cfg_flag; + +#define HIMAX_PROC_DIAG_FOLDER "diag" +struct proc_dir_entry *himax_proc_diag_dir; +#define HIMAX_PROC_STACK_FILE "stack" +extern struct proc_dir_entry *himax_proc_stack_file; +#define HIMAX_PROC_IIR_FILE "iir_s" +extern struct proc_dir_entry *himax_proc_iir_file; +#define HIMAX_PROC_DC_FILE "dc_s" +extern struct proc_dir_entry *himax_proc_dc_file; +#define HIMAX_PROC_BANK_FILE "bank_s" +extern struct proc_dir_entry *himax_proc_bank_file; + +#ifdef HX_TP_PROC_2T2R + extern uint32_t *diag_mutual_2; + + int32_t *getMutualBuffer_2(void); + void setMutualBuffer_2(uint8_t x_num, uint8_t y_num); +#endif +extern int32_t *diag_mutual; +extern int32_t *diag_mutual_new; +extern int32_t *diag_mutual_old; +extern uint8_t diag_max_cnt; +extern uint8_t hx_state_info[2]; +extern uint8_t diag_coor[128]; +extern int32_t diag_self[100]; +extern int32_t diag_self_new[100]; +extern int32_t diag_self_old[100]; +int32_t *getMutualBuffer(void); +int32_t *getMutualNewBuffer(void); +int32_t *getMutualOldBuffer(void); +int32_t *getSelfBuffer(void); +int32_t *getSelfNewBuffer(void); +int32_t *getSelfOldBuffer(void); +void setMutualBuffer(uint8_t x_num, uint8_t y_num); +void setMutualNewBuffer(uint8_t x_num, uint8_t y_num); +void setMutualOldBuffer(uint8_t x_num, uint8_t y_num); +uint8_t process_type; +uint8_t mode_flag; +uint8_t overflow; + +#define HIMAX_PROC_DEBUG_FILE "debug" +extern struct proc_dir_entry *himax_proc_debug_file; +extern bool fw_update_complete; +extern int handshaking_result; +extern unsigned char debug_level_cmd; +extern uint8_t cmd_set[8]; +extern uint8_t mutual_set_flag; + +#define HIMAX_PROC_FLASH_DUMP_FILE "flash_dump" +extern struct proc_dir_entry *himax_proc_flash_dump_file; +extern int Flash_Size; +extern uint8_t *flash_buffer; +extern uint8_t g_flash_cmd; +extern uint8_t g_flash_progress; +extern bool g_flash_dump_rst; /*Fail = 0, Pass = 1*/ +void setFlashBuffer(void); + +enum flash_dump_prog { + START, + ONGOING, + FINISHED, +}; + +extern uint32_t **raw_data_array; +extern uint8_t X_NUM4; +extern uint8_t Y_NUM; +extern uint8_t sel_type; + +/* Moved from debug.c */ +extern struct himax_debug *debug_data; +extern unsigned char IC_CHECKSUM; +extern int i2c_error_count; +extern struct proc_dir_entry *himax_touch_proc_dir; + +#ifdef HX_TP_PROC_GUEST_INFO +extern struct hx_guest_info *g_guest_info_data; +extern char *g_guest_info_item[]; +#endif + +extern int himax_input_register(struct himax_ts_data *ts); +#ifdef HX_TP_PROC_2T2R + extern bool Is_2T2R; +#endif + +#ifdef HX_RST_PIN_FUNC + extern void himax_ic_reset(uint8_t loadconfig, uint8_t int_off); +#endif + +#if defined(HX_ZERO_FLASH) +extern char *i_CTPM_firmware_name; +#endif + +extern uint8_t HX_PROC_SEND_FLAG; +extern struct himax_target_report_data *g_target_report_data; +extern struct himax_report_data *hx_touch_data; +extern int g_ts_dbg; + +/* Moved from debug.c end */ +#define BUF_SIZE 1024 +#define CMD_NUM 15 +char *dbg_cmd_str[] = {"crc_test", "fw_debug", "attn", "layout", "dd_debug", + "esd_cnt", "senseonoff", "debug_level", "guest_info", "int_en", + "register", "reset", "diag_arr", "diag", NULL}; +int dbg_cmd_flag; +char *dbg_cmd_par; +ssize_t (*dbg_func_ptr_r[CMD_NUM])(char *buf, size_t len); +ssize_t (*dbg_func_ptr_w[CMD_NUM])(char *buf, size_t len); +#endif diff --git a/drivers/input/touchscreen/himax_0flash_mmi/himax_ic_core.h b/drivers/input/touchscreen/himax_0flash_mmi/himax_ic_core.h new file mode 100644 index 000000000000..b8dc34f16e81 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/himax_ic_core.h @@ -0,0 +1,978 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for ic core functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef __HIMAX_IC_CORE_H__ +#define __HIMAX_IC_CORE_H__ + +#include "himax_platform.h" +#include "himax_common.h" +#include + +#define DATA_LEN_8 8 +#define DATA_LEN_4 4 +#define ADDR_LEN_4 4 +#define FLASH_RW_MAX_LEN 256 +#define FLASH_WRITE_BURST_SZ 8 +#define PROGRAM_SZ 48 +#define MAX_I2C_TRANS_SZ 128 +#define HIMAX_REG_RETRY_TIMES 5 +#define FW_BIN_16K_SZ 0x4000 +#define HIMAX_TOUCH_DATA_SIZE 128 +#define MASK_BIT_0 0x01 +#define MASK_BIT_1 0x02 +#define MASK_BIT_2 0x04 +#define PACKET_MAX_SZ 4096 + +#define FW_SECTOR_PER_BLOCK 8 +#define FW_PAGE_PER_SECTOR 64 +#define FW_PAGE_SEZE 128 +#define HX256B 0x100 +#define HX4K 0x1000 +#define HX_32K_SZ 0x8000 +#define HX_40K_SZ 0xA000 +#define HX_48K_SZ 0xC000 +#define HX1K 0x400 +#define HX64K 0x10000 +#define HX124K 0x1f000 +#define HX4000K 0x1000000 + +#define HX_NORMAL_MODE 1 +#define HX_SORTING_MODE 2 +#define HX_CHANGE_MODE_FAIL (-1) +#define HX_RW_REG_FAIL (-1) +#define HX_DRIVER_MAX_IC_NUM 12 + +#if defined(__HIMAX_HX852xG_MOD__) +#define HX_MOD_KSYM_HX852xG HX_MOD_KSYM_HX852xG +#endif + +#if defined(__HIMAX_HX852xH_MOD__) +#define HX_MOD_KSYM_HX852xH HX_MOD_KSYM_HX852xH +#endif + +#if defined(__HIMAX_HX83103_MOD__) +#define HX_MOD_KSYM_HX83103 HX_MOD_KSYM_HX83103 +#endif + +#if defined(__HIMAX_HX83106_MOD__) +#define HX_MOD_KSYM_HX83106 HX_MOD_KSYM_HX83106 +#endif + +#if defined(__HIMAX_HX83111_MOD__) +#define HX_MOD_KSYM_HX83111 HX_MOD_KSYM_HX83111 +#endif + +#if defined(__HIMAX_HX83112_MOD__) +#define HX_MOD_KSYM_HX83112 HX_MOD_KSYM_HX83112 +#endif + +#if defined(__HIMAX_HX83191_MOD__) +#define HX_MOD_KSYM_HX83191 HX_MOD_KSYM_HX83191 +#endif + +#if defined(__HIMAX_HX83113_MOD__) +#define HX_MOD_KSYM_HX83113 HX_MOD_KSYM_HX83113 +#endif + +#define CORE_INIT +#define CORE_IC +#define CORE_FW +#define CORE_FLASH +#define CORE_SRAM +#define CORE_DRIVER + +#define HX_0F_DEBUG + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INCELL + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) + extern char *i_CTPM_firmware_name; +#endif + +#ifdef HX_ZERO_FLASH + extern int g_f_0f_updat; +#endif + +#ifdef HX_TP_PROC_GUEST_INFO + extern struct hx_guest_info *g_guest_info_data; +#endif + +void himax_mcu_in_cmd_struct_free(void); + +#endif + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_ONCELL + +void himax_mcu_on_cmd_struct_free(void); + +#endif + +#ifdef HX_AUTO_UPDATE_FW +extern int g_i_FW_VER; +extern int g_i_CFG_VER; +extern int g_i_CID_MAJ; +extern int g_i_CID_MIN; +extern unsigned char *i_CTPM_FW; +#endif + +extern unsigned long FW_VER_MAJ_FLASH_ADDR; +extern unsigned long FW_VER_MIN_FLASH_ADDR; +extern unsigned long CFG_VER_MAJ_FLASH_ADDR; +extern unsigned long CFG_VER_MIN_FLASH_ADDR; +extern unsigned long CID_VER_MAJ_FLASH_ADDR; +extern unsigned long CID_VER_MIN_FLASH_ADDR; + +extern unsigned long FW_VER_MAJ_FLASH_LENG; +extern unsigned long FW_VER_MIN_FLASH_LENG; +extern unsigned long CFG_VER_MAJ_FLASH_LENG; +extern unsigned long CFG_VER_MIN_FLASH_LENG; +extern unsigned long CID_VER_MAJ_FLASH_LENG; +extern unsigned long CID_VER_MIN_FLASH_LENG; + +extern unsigned char IC_CHECKSUM; +#ifdef HX_ESD_RECOVERY +extern int g_zero_event_count; +#endif + +#ifdef HX_RST_PIN_FUNC +extern u8 HX_HW_RESET_ACTIVATE; +void himax_rst_gpio_set(int pinnum, uint8_t value); +#endif + +#if defined(HX_USB_DETECT_GLOBAL) +void himax_cable_detect_func(bool force_renew); +#endif + +int himax_report_data_init(void); +extern int i2c_error_count; + +#ifdef HX_ESD_RECOVERY + extern u8 HX_ESD_RESET_ACTIVATE; +#endif + +#ifdef CORE_INIT + int himax_mcu_in_cmd_struct_init(void); + void himax_in_parse_assign_cmd(uint32_t addr, uint8_t *cmd, int len); + void himax_mcu_in_cmd_init(void); + + int himax_mcu_on_cmd_struct_init(void); + void himax_on_parse_assign_cmd(uint32_t addr, uint8_t *cmd, int len); + void himax_mcu_on_cmd_init(void); + + extern void (*himax_mcu_cmd_struct_free)(void); +#endif + +#ifdef HX_TP_PROC_GUEST_INFO +#define HX_GUEST_INFO_SIZE 10 +#define HX_GUEST_INFO_LEN_SIZE 4 +#define HX_GUEST_INFO_ID_SIZE 4 + +struct hx_guest_info { + int g_guest_info_ongoing; /* 0 stop, 1 ongoing */ + uint8_t g_guest_str[10][128]; + uint8_t g_guest_str_in_format[10][128]; + uint8_t g_guest_data_type[10]; + int g_guest_data_len[10]; + int g_guest_info_type; +}; + +#endif + +#if defined(CORE_IC) + #define ic_adr_ahb_addr_byte_0 0x00 + #define ic_adr_ahb_rdata_byte_0 0x08 + #define ic_adr_ahb_access_direction 0x0c + #define ic_adr_conti 0x13 + #define ic_adr_incr4 0x0D + #define ic_adr_i2c_psw_lb 0x31 + #define ic_adr_i2c_psw_ub 0x32 + #define ic_cmd_ahb_access_direction_read 0x00 + #define ic_cmd_conti 0x31 + #define ic_cmd_incr4 0x10 + #define ic_cmd_i2c_psw_lb 0x27 + #define ic_cmd_i2c_psw_ub 0x95 + #define ic_adr_tcon_on_rst 0x80020020 + #define ic_addr_adc_on_rst 0x80020094 + #define ic_adr_psl 0x900000A0 + #define ic_adr_cs_central_state 0x900000A8 + #define ic_cmd_rst 0x00000000 + #define ic_adr_osc_en 0x900880A8 + #define ic_adr_osc_pw 0x900880E0 + + #define on_ic_adr_ahb_addr_byte_0 0x00 + #define on_ic_adr_ahb_rdata_byte_0 0x08 + #define on_ic_adr_ahb_access_direction 0x0c + #define on_ic_adr_conti 0x13 + #define on_ic_adr_incr4 0x0D + #define on_ic_cmd_ahb_access_direction_read 0x00 + #define on_ic_cmd_conti 0x31 + #define on_ic_cmd_incr4 0x10 + #define on_ic_adr_mcu_ctrl 0x82 + #define on_ic_cmd_mcu_on 0x25 + #define on_ic_cmd_mcu_off 0xDA + #define on_ic_adr_sleep_ctrl 0x99 + #define on_ic_cmd_sleep_in 0x80 + #define on_ic_adr_tcon_ctrl 0x80020000 + #define on_ic_cmd_tcon_on 0x00000000 + #define on_ic_adr_wdg_ctrl 0x9000800C + #define on_ic_cmd_wdg_psw 0x0000AC53 + #define on_ic_adr_wdg_cnt_ctrl 0x90008010 + #define on_ic_cmd_wdg_cnt_clr 0x000035CA +#endif + +#if defined(CORE_FW) + #define fw_addr_system_reset 0x90000018 + #define fw_addr_safe_mode_release_pw 0x90000098 + #define fw_addr_ctrl_fw 0x9000005c + #define fw_addr_flag_reset_event 0x900000e4 + #define fw_addr_hsen_enable 0x10007F14 + #define fw_addr_smwp_enable 0x10007F10 + #define fw_usb_detect_addr 0x10007F38 + #define fw_addr_program_reload_from 0x00000000 + #define fw_addr_program_reload_to 0x08000000 + #define fw_addr_program_reload_page_write 0x0000fb00 + #define fw_addr_raw_out_sel 0x800204b4 + #define fw_addr_reload_status 0x80050000 + #define fw_addr_reload_crc32_result 0x80050018 + #define fw_addr_reload_addr_from 0x80050020 + #define fw_addr_reload_addr_cmd_beat 0x80050028 + #define fw_data_system_reset 0x00000055 + #define fw_data_safe_mode_release_pw_active 0x00000053 + #define fw_data_safe_mode_release_pw_reset 0x00000000 + #define fw_data_clear 0x00000000 + #define fw_data_fw_stop 0x000000A5 + #define fw_data_program_reload_start 0x0A3C3000 + #define fw_data_program_reload_compare 0x04663000 + #define fw_data_program_reload_break 0x15E75678 + #define fw_addr_selftest_addr_en 0x10007F18 + #define fw_addr_selftest_result_addr 0x10007f24 + #define fw_data_selftest_request 0x00006AA6 + #define fw_addr_criteria_addr 0x10007f1c + #define fw_data_criteria_aa_top 0x64 + #define fw_data_criteria_aa_bot 0x00 + #define fw_data_criteria_key_top 0x64 + #define fw_data_criteria_key_bot 0x00 + #define fw_data_criteria_avg_top 0x64 + #define fw_data_criteria_avg_bot 0x00 + #define fw_addr_set_frame_addr 0x10007294 + #define fw_data_set_frame 0x0000000A + #define fw_data_selftest_ack_hb 0xa6 + #define fw_data_selftest_ack_lb 0x6a + #define fw_data_selftest_pass 0xaa + #define fw_data_normal_cmd 0x00 + #define fw_data_normal_status 0x99 + #define fw_data_sorting_cmd 0xaa + #define fw_data_sorting_status 0xcc + #define fw_data_idle_dis_pwd 0x17 + #define fw_data_idle_en_pwd 0x1f + #define fw_addr_sorting_mode_en 0x10007f04 + #define fw_addr_max_dc 0x10007fc8 + #define fw_addr_fw_mode_status 0x10007088 + #define fw_addr_icid_addr 0x900000d0 + #define fw_addr_trigger_addr 0x10007088 + #define fw_addr_fw_ver_addr 0x10007004 + #define fw_addr_fw_cfg_addr 0x10007084 + #define fw_addr_fw_vendor_addr 0x10007000 + #define fw_addr_cus_info 0x10007008 + #define fw_addr_proj_info 0x10007014 + #define fw_addr_fw_state_addr 0x900000f8 + #define fw_addr_fw_dbg_msg_addr 0x10007f40 + #define fw_addr_chk_fw_status 0x900000a8 + #define fw_addr_dd_handshak_addr 0x900000fc + #define fw_addr_dd_data_addr 0x10007f80 + #define fw_data_dd_request 0xaa + #define fw_data_dd_ack 0xbb + #define fw_data_rawdata_ready_hb 0xa3 + #define fw_data_rawdata_ready_lb 0x3a + #define fw_addr_ahb_addr 0x11 + #define fw_data_ahb_dis 0x00 + #define fw_data_ahb_en 0x01 + #define fw_addr_event_addr 0x30 + #define fw_func_handshaking_pwd 0xA55AA55A + #define fw_func_handshaking_end 0x77887788 + #define fw_addr_ulpm_33 0x33 + #define fw_addr_ulpm_34 0x34 + #define fw_data_ulpm_11 0x11 + #define fw_data_ulpm_22 0x22 + #define fw_data_ulpm_33 0x33 + #define fw_data_ulpm_aa 0xAA + + #define fw_func_edge_portrait_pwd 0xA55AA55A + #define fw_func_edge_lsp_left_pwd 0xA11AA11A + #define fw_func_edge_lsp_right_pwd 0xA33AA33A + + #define on_fw_addr_smwp_enable 0xA2 + #define on_fw_usb_detect_addr 0xA4 + #define on_fw_addr_program_reload_from 0x00000000 + #define on_fw_addr_raw_out_sel 0x98 + #define on_fw_addr_flash_checksum 0x80000044 + #define on_fw_data_flash_checksum 0x00000491 + #define on_fw_addr_crc_value 0x80000050 + #define on_fw_data_safe_mode_release_pw_active 0x00000053 + #define on_fw_data_safe_mode_release_pw_reset 0x00000000 + #define on_fw_addr_criteria_addr 0x9A + #define on_fw_data_selftest_pass 0xaa + #define on_fw_addr_reK_crtl 0x8000000C + #define on_fw_data_reK_en 0x02 + #define on_fw_data_reK_dis 0xFD + #define on_fw_data_rst_init 0xF0 + #define on_fw_data_dc_set 0x02 + #define on_fw_data_bank_set 0x03 + #define on_fw_addr_selftest_addr_en 0x98 + #define on_fw_addr_selftest_result_addr 0x9B + #define on_fw_data_selftest_request 0x06 + #define on_fw_data_thx_avg_mul_dc_lsb 0x22 + #define on_fw_data_thx_avg_mul_dc_msb 0x0B + #define on_fw_data_thx_mul_dc_up_low_bud 0x64 + #define on_fw_data_thx_avg_slf_dc_lsb 0x14 + #define on_fw_data_thx_avg_slf_dc_msb 0x05 + #define on_fw_data_thx_slf_dc_up_low_bud 0x64 + #define on_fw_data_thx_slf_bank_up 0x40 + #define on_fw_data_thx_slf_bank_low 0x00 + #define on_fw_data_idle_dis_pwd 0x40 + #define on_fw_data_idle_en_pwd 0x00 + #define on_fw_addr_fw_mode_status 0x99 + #define on_fw_addr_icid_addr 0x900000d0 + #define on_fw_addr_trigger_addr 0x10007088 + #define on_fw_addr_fw_ver_start 0x90 + #define on_fw_data_rawdata_ready_hb 0xa3 + #define on_fw_data_rawdata_ready_lb 0x3a + #define on_fw_addr_ahb_addr 0x11 + #define on_fw_data_ahb_dis 0x00 + #define on_fw_data_ahb_en 0x01 + #define on_fw_addr_event_addr 0x30 +#endif + +#if defined(CORE_FLASH) + #define flash_addr_ctrl_base 0x80000000 + #define flash_addr_spi200_trans_fmt (flash_addr_ctrl_base + 0x10) + #define flash_addr_spi200_trans_ctrl (flash_addr_ctrl_base + 0x20) + #define flash_addr_spi200_cmd (flash_addr_ctrl_base + 0x24) + #define flash_addr_spi200_addr (flash_addr_ctrl_base + 0x28) + #define flash_addr_spi200_data (flash_addr_ctrl_base + 0x2c) + #define flash_addr_spi200_bt_num (flash_addr_ctrl_base + 0xe8) + #define flash_data_spi200_trans_fmt 0x00020780 + #define flash_data_spi200_trans_ctrl_1 0x42000003 + #define flash_data_spi200_trans_ctrl_2 0x47000000 + #define flash_data_spi200_trans_ctrl_3 0x67000000 + #define flash_data_spi200_trans_ctrl_4 0x610ff000 + #define flash_data_spi200_trans_ctrl_5 0x694002ff + #define flash_data_spi200_cmd_1 0x00000005 + #define flash_data_spi200_cmd_2 0x00000006 + #define flash_data_spi200_cmd_3 0x000000C7 + #define flash_data_spi200_cmd_4 0x000000D8 + #define flash_data_spi200_cmd_5 0x00000020 + #define flash_data_spi200_cmd_6 0x00000002 + #define flash_data_spi200_cmd_7 0x0000003b + #define flash_data_spi200_addr 0x00000000 + + #define on_flash_addr_ctrl_base 0x80000000 + #define on_flash_addr_ctrl_auto 0x80000004 + #define on_flash_data_main_erase 0x0000A50D + #define on_flash_data_auto 0xA5 + #define on_flash_data_main_read 0x03 + #define on_flash_data_page_write 0x05 + #define on_flash_data_spp_read 0x10 + #define on_flash_data_sfr_read 0x14 + #define on_flash_addr_ahb_ctrl 0x80000020 + #define on_flash_data_ahb_squit 0x00000001 + #define on_flash_addr_unlock_0 0x00000000 + #define on_flash_addr_unlock_4 0x00000004 + #define on_flash_addr_unlock_8 0x00000008 + #define on_flash_addr_unlock_c 0x0000000C + #define on_flash_data_cmd0 0x28178EA0 + #define on_flash_data_cmd1 0x0A0E03FF + #define on_flash_data_cmd2 0x8C203D0C + #define on_flash_data_cmd3 0x00300263 + #define on_flash_data_lock 0x03400000 +#endif + +#if defined(CORE_SRAM) + #define sram_adr_mkey 0x100070E8 + #define sram_adr_rawdata_addr 0x10000000 + #define sram_adr_rawdata_end 0x00000000 + #define sram_passwrd_start 0x5AA5 + #define sram_passwrd_end 0xA55A + + #define on_sram_adr_rawdata_addr 0x080002E0 + #define on_sram_adr_rawdata_end 0x00000000 + #define on_sram_cmd_conti 0x44332211 + #define on_sram_cmd_fin 0x00000000 + #define on_sram_passwrd_start 0x5AA5 + #define on_sram_passwrd_end 0xA55A +#endif + +#if defined(CORE_DRIVER) + #define driver_addr_fw_define_flash_reload 0x10007f00 + #define driver_addr_fw_define_2nd_flash_reload 0x100072c0 + #define driver_data_fw_define_flash_reload_dis 0x0000a55a + #define driver_data_fw_define_flash_reload_en 0x00000000 + #define driver_addr_fw_define_int_is_edge 0x10007088 + #define driver_addr_fw_define_rxnum_txnum_maxpt 0x100070f4 + #define driver_data_fw_define_rxnum_txnum_maxpt_sorting 0x00000008 + #define driver_data_fw_define_rxnum_txnum_maxpt_normal 0x00000014 + #define driver_addr_fw_define_xy_res_enable 0x100070f8 + #define driver_addr_fw_define_x_y_res 0x100070fc + #define driver_data_df_rx 36 + #define driver_data_df_tx 18 + #define driver_data_df_pt 10 + #define driver_data_df_x_res 1080 + #define driver_data_df_y_res 1920 + #define on_driver_addr_fw_define_int_is_edge 0x10007088 + #define on_driver_data_df_rx 28 + #define on_driver_data_df_tx 14 + #define on_driver_data_df_pt 10 + #define on_driver_data_df_x_res 1080 + #define on_driver_data_df_y_res 1920 +#ifndef HX_NEW_EVENT_STACK_FORMAT + #define on_driver_addr_fw_rx_tx_maxpt_num 0x0800001C + #define on_driver_addr_fw_xy_rev_int_edge 0x0800000C + #define on_driver_addr_fw_define_x_y_res 0x08000030 +#else + #define on_driver_addr_fw_rx_tx_maxpt_num 0x08000004 + #define on_driver_addr_fw_maxpt_bt_num 0x0800000C + #define on_driver_addr_fw_xy_rev_int_edge 0x08000110 + #define on_driver_addr_fw_define_x_y_res 0x08000010 +#endif + +#endif + +#if defined(HX_ZERO_FLASH) + #define zf_addr_dis_flash_reload 0x10007f00 + #define zf_data_dis_flash_reload 0x00009AA9 + #define zf_addr_system_reset 0x90000018 + #define zf_data_system_reset 0x00000055 + #define zf_data_sram_start_addr 0x08000000 + #define zf_data_sram_clean 0x10000000 + #define zf_data_cfg_info 0x10007000 + #define zf_data_fw_cfg_1 0x10007084 + #define zf_data_fw_cfg_2 0x10007264 + #define zf_data_fw_cfg_3 0x10007300 + #define zf_data_adc_cfg_1 0x10006A00 + #define zf_data_adc_cfg_2 0x10007B28 + #define zf_data_adc_cfg_3 0x10007AF0 + #define zf_data_map_table 0x10007500 + #define zf_data_mode_switch 0x10007294 + #define zf_addr_sts_chk 0x900000A8 + #define zf_data_activ_sts 0x05 + #define zf_addr_activ_relod 0x90000048 + #define zf_data_activ_in 0xEC + +#if defined(HX_CODE_OVERLAY) +#if defined(HX83102D) + #define ovl_section_num 3 + #define ovl_gesture_request 0x11 + #define ovl_gesture_reply 0x22 + #define ovl_border_request 0x55 + #define ovl_border_reply 0x66 + #define ovl_sorting_request 0x99 + #define ovl_sorting_reply 0xAA + #define ovl_fault 0xFF +#else + #define ovl_isram_addr 0x20008CE0 + #define ovl_handshaking_addr 0x10007FFC + #define ovl_gesture_request 0x11 + #define ovl_gesture_reply 0x22 + #define ovl_border_request 0x55 + #define ovl_border_reply 0x66 +#endif +#endif + +struct zf_info { + uint8_t sram_addr[4]; + int write_size; + uint32_t fw_addr; + uint32_t cfg_addr; +}; +#endif + +#ifdef HIMAX_PALM_SENSOR_EN + #define palm_detection_register 0x10007FF4 + #define palm_detection_register_enable 0xA55AA55A + #define palm_detection_register_disable 0x00000000 +#endif + +struct ic_operation { + uint8_t addr_ahb_addr_byte_0[1]; + uint8_t addr_ahb_rdata_byte_0[1]; + uint8_t addr_ahb_access_direction[1]; + uint8_t addr_conti[1]; + uint8_t addr_incr4[1]; + uint8_t adr_i2c_psw_lb[1]; + uint8_t adr_i2c_psw_ub[1]; + uint8_t data_ahb_access_direction_read[1]; + uint8_t data_conti[1]; + uint8_t data_incr4[1]; + uint8_t data_i2c_psw_lb[1]; + uint8_t data_i2c_psw_ub[1]; + uint8_t addr_tcon_on_rst[4]; + uint8_t addr_adc_on_rst[4]; + uint8_t addr_psl[4]; + uint8_t addr_cs_central_state[4]; + uint8_t data_rst[4]; + uint8_t adr_osc_en[4]; + uint8_t adr_osc_pw[4]; +}; + +struct fw_operation { + uint8_t addr_system_reset[4]; + uint8_t addr_safe_mode_release_pw[4]; + uint8_t addr_ctrl_fw_isr[4]; + uint8_t addr_flag_reset_event[4]; + uint8_t addr_hsen_enable[4]; + uint8_t addr_smwp_enable[4]; +#ifdef HX_EDGE_LIMIT + uint8_t addr_edge_limit_enable[4]; +#endif +#ifdef HIMAX_PALM_SENSOR_EN + uint8_t addr_palm_detection_enable[4]; +#endif + uint8_t addr_program_reload_from[4]; + uint8_t addr_program_reload_to[4]; + uint8_t addr_program_reload_page_write[4]; + uint8_t addr_raw_out_sel[4]; + uint8_t addr_reload_status[4]; + uint8_t addr_reload_crc32_result[4]; + uint8_t addr_reload_addr_from[4]; + uint8_t addr_reload_addr_cmd_beat[4]; + uint8_t addr_selftest_addr_en[4]; + uint8_t addr_criteria_addr[4]; + uint8_t addr_set_frame_addr[4]; + uint8_t addr_selftest_result_addr[4]; + uint8_t addr_sorting_mode_en[4]; + uint8_t addr_max_dc[4]; + uint8_t addr_fw_mode_status[4]; + uint8_t addr_icid_addr[4]; + uint8_t addr_trigger_addr[4]; + uint8_t addr_fw_ver_addr[4]; + uint8_t addr_fw_cfg_addr[4]; + uint8_t addr_fw_vendor_addr[4]; + uint8_t addr_cus_info[4]; + uint8_t addr_proj_info[4]; + uint8_t addr_fw_state_addr[4]; + uint8_t addr_fw_dbg_msg_addr[4]; + uint8_t addr_chk_fw_status[4]; + uint8_t addr_dd_handshak_addr[4]; + uint8_t addr_dd_data_addr[4]; + uint8_t data_system_reset[4]; + uint8_t data_safe_mode_release_pw_active[4]; + uint8_t data_safe_mode_release_pw_reset[4]; + uint8_t data_clear[4]; + uint8_t data_fw_stop[4]; + uint8_t data_program_reload_start[4]; + uint8_t data_program_reload_compare[4]; + uint8_t data_program_reload_break[4]; + uint8_t data_selftest_request[4]; + uint8_t data_criteria_aa_top[1]; + uint8_t data_criteria_aa_bot[1]; + uint8_t data_criteria_key_top[1]; + uint8_t data_criteria_key_bot[1]; + uint8_t data_criteria_avg_top[1]; + uint8_t data_criteria_avg_bot[1]; + uint8_t data_set_frame[4]; + uint8_t data_selftest_ack_hb[1]; + uint8_t data_selftest_ack_lb[1]; + uint8_t data_selftest_pass[1]; + uint8_t data_normal_cmd[1]; + uint8_t data_normal_status[1]; + uint8_t data_sorting_cmd[1]; + uint8_t data_sorting_status[1]; + uint8_t data_dd_request[1]; + uint8_t data_dd_ack[1]; + uint8_t data_idle_dis_pwd[1]; + uint8_t data_idle_en_pwd[1]; + uint8_t data_rawdata_ready_hb[1]; + uint8_t data_rawdata_ready_lb[1]; + uint8_t addr_ahb_addr[1]; + uint8_t data_ahb_dis[1]; + uint8_t data_ahb_en[1]; + uint8_t addr_event_addr[1]; + uint8_t addr_usb_detect[4]; + uint8_t addr_ulpm_33[1]; + uint8_t addr_ulpm_34[1]; + uint8_t data_ulpm_11[1]; + uint8_t data_ulpm_22[1]; + uint8_t data_ulpm_33[1]; + uint8_t data_ulpm_aa[1]; +}; + +struct flash_operation { + uint8_t addr_spi200_trans_fmt[4]; + uint8_t addr_spi200_trans_ctrl[4]; + uint8_t addr_spi200_cmd[4]; + uint8_t addr_spi200_addr[4]; + uint8_t addr_spi200_data[4]; + uint8_t addr_spi200_bt_num[4]; + + uint8_t data_spi200_trans_fmt[4]; + uint8_t data_spi200_trans_ctrl_1[4]; + uint8_t data_spi200_trans_ctrl_2[4]; + uint8_t data_spi200_trans_ctrl_3[4]; + uint8_t data_spi200_trans_ctrl_4[4]; + uint8_t data_spi200_trans_ctrl_5[4]; + uint8_t data_spi200_cmd_1[4]; + uint8_t data_spi200_cmd_2[4]; + uint8_t data_spi200_cmd_3[4]; + uint8_t data_spi200_cmd_4[4]; + uint8_t data_spi200_cmd_5[4]; + uint8_t data_spi200_cmd_6[4]; + uint8_t data_spi200_cmd_7[4]; + uint8_t data_spi200_addr[4]; +}; + +struct sram_operation { + uint8_t addr_mkey[4]; + uint8_t addr_rawdata_addr[4]; + uint8_t addr_rawdata_end[4]; + uint8_t passwrd_start[2]; + uint8_t passwrd_end[2]; +}; + +struct driver_operation { + uint8_t addr_fw_define_flash_reload[4]; + uint8_t addr_fw_define_2nd_flash_reload[4]; + uint8_t addr_fw_define_int_is_edge[4]; + uint8_t addr_fw_define_rxnum_txnum_maxpt[4]; + uint8_t addr_fw_define_xy_res_enable[4]; + uint8_t addr_fw_define_x_y_res[4]; + uint8_t data_df_rx[1]; + uint8_t data_df_tx[1]; + uint8_t data_df_pt[1]; + uint8_t data_df_x_res[2]; + uint8_t data_df_y_res[2]; + uint8_t data_fw_define_flash_reload_dis[4]; + uint8_t data_fw_define_flash_reload_en[4]; + uint8_t data_fw_define_rxnum_txnum_maxpt_sorting[4]; + uint8_t data_fw_define_rxnum_txnum_maxpt_normal[4]; +}; + +struct zf_operation { + uint8_t addr_dis_flash_reload[4]; + uint8_t data_dis_flash_reload[4]; + uint8_t addr_system_reset[4]; + uint8_t data_system_reset[4]; + uint8_t data_sram_start_addr[4]; + uint8_t data_sram_clean[4]; + uint8_t data_cfg_info[4]; + uint8_t data_fw_cfg_1[4]; + uint8_t data_fw_cfg_2[4]; + uint8_t data_fw_cfg_3[4]; + uint8_t data_adc_cfg_1[4]; + uint8_t data_adc_cfg_2[4]; + uint8_t data_adc_cfg_3[4]; + uint8_t data_map_table[4]; + uint8_t data_mode_switch[4]; + uint8_t addr_sts_chk[4]; + uint8_t data_activ_sts[1]; + uint8_t addr_activ_relod[4]; + uint8_t data_activ_in[1]; +}; + +struct himax_core_command_operation { + struct ic_operation *ic_op; + struct fw_operation *fw_op; + struct flash_operation *flash_op; + struct sram_operation *sram_op; + struct driver_operation *driver_op; + struct zf_operation *zf_op; +}; + +struct on_ic_operation { + uint8_t addr_ahb_addr_byte_0[1]; + uint8_t addr_ahb_rdata_byte_0[1]; + uint8_t addr_ahb_access_direction[1]; + uint8_t addr_conti[1]; + uint8_t addr_incr4[1]; + uint8_t adr_mcu_ctrl[1]; + uint8_t data_ahb_access_direction_read[1]; + uint8_t data_conti[1]; + uint8_t data_incr4[1]; + uint8_t cmd_mcu_on[1]; + uint8_t cmd_mcu_off[1]; + uint8_t adr_sleep_ctrl[1]; + uint8_t cmd_sleep_in[1]; + uint8_t adr_tcon_ctrl[4]; + uint8_t cmd_tcon_on[4]; + uint8_t adr_wdg_ctrl[4]; + uint8_t cmd_wdg_psw[4]; + uint8_t adr_wdg_cnt_ctrl[4]; + uint8_t cmd_wdg_cnt_clr[4]; +}; + +struct on_fw_operation { + uint8_t addr_smwp_enable[1]; + uint8_t addr_program_reload_from[4]; + uint8_t addr_raw_out_sel[1]; + uint8_t addr_flash_checksum[4]; + uint8_t data_flash_checksum[4]; + uint8_t addr_crc_value[4]; + uint8_t addr_reload_status[4]; + uint8_t addr_reload_crc32_result[4]; + uint8_t addr_reload_addr_from[4]; + uint8_t addr_reload_addr_cmd_beat[4]; + uint8_t addr_set_frame_addr[4]; + uint8_t addr_fw_mode_status[1]; + uint8_t addr_icid_addr[4]; + uint8_t addr_trigger_addr[4]; + uint8_t addr_fw_ver_start[1]; + uint8_t data_safe_mode_release_pw_active[4]; + uint8_t data_safe_mode_release_pw_reset[4]; + uint8_t data_clear[4]; + uint8_t addr_criteria_addr[1]; + uint8_t data_selftest_pass[1]; + uint8_t addr_reK_crtl[4]; + uint8_t data_reK_en[1]; + uint8_t data_reK_dis[1]; + uint8_t data_rst_init[1]; + uint8_t data_dc_set[1]; + uint8_t data_bank_set[1]; + uint8_t addr_selftest_addr_en[1]; + uint8_t addr_selftest_result_addr[1]; + uint8_t data_selftest_request[1]; + uint8_t data_thx_avg_mul_dc_lsb[1]; + uint8_t data_thx_avg_mul_dc_msb[1]; + uint8_t data_thx_mul_dc_up_low_bud[1]; + uint8_t data_thx_avg_slf_dc_lsb[1]; + uint8_t data_thx_avg_slf_dc_msb[1]; + uint8_t data_thx_slf_dc_up_low_bud[1]; + uint8_t data_thx_slf_bank_up[1]; + uint8_t data_thx_slf_bank_low[1]; + uint8_t data_idle_dis_pwd[1]; + uint8_t data_idle_en_pwd[1]; + uint8_t data_rawdata_ready_hb[1]; + uint8_t data_rawdata_ready_lb[1]; + uint8_t addr_ahb_addr[1]; + uint8_t data_ahb_dis[1]; + uint8_t data_ahb_en[1]; + uint8_t addr_event_addr[1]; + uint8_t addr_usb_detect[1]; +}; + +struct on_flash_operation { + uint8_t addr_ctrl_base[4]; + uint8_t addr_ctrl_auto[4]; + uint8_t data_main_erase[4]; + uint8_t data_auto[1]; + uint8_t data_main_read[1]; + uint8_t data_page_write[1]; + uint8_t data_sfr_read[1]; + uint8_t data_spp_read[1]; + uint8_t addr_ahb_ctrl[4]; + uint8_t data_ahb_squit[4]; + + uint8_t addr_unlock_0[4]; + uint8_t addr_unlock_4[4]; + uint8_t addr_unlock_8[4]; + uint8_t addr_unlock_c[4]; + uint8_t data_cmd0[4]; + uint8_t data_cmd1[4]; + uint8_t data_cmd2[4]; + uint8_t data_cmd3[4]; + uint8_t data_lock[4]; +}; + +struct on_sram_operation { + uint8_t addr_rawdata_addr[4]; + uint8_t addr_rawdata_end[4]; + uint8_t data_conti[4]; + uint8_t data_fin[4]; + uint8_t passwrd_start[2]; + uint8_t passwrd_end[2]; +}; + +struct on_driver_operation { + uint8_t addr_fw_define_int_is_edge[4]; + uint8_t addr_fw_rx_tx_maxpt_num[4]; +#ifdef HX_NEW_EVENT_STACK_FORMAT + uint8_t addr_fw_maxpt_bt_num[4]; +#endif + uint8_t addr_fw_xy_rev_int_edge[4]; + uint8_t addr_fw_define_x_y_res[4]; + uint8_t data_fw_define_rxnum_txnum_maxpt_sorting[4]; + uint8_t data_fw_define_rxnum_txnum_maxpt_normal[4]; + uint8_t data_df_rx[1]; + uint8_t data_df_tx[1]; + uint8_t data_df_pt[1]; + uint8_t data_df_x_res[2]; + uint8_t data_df_y_res[2]; +}; + +struct himax_on_core_command_operation { + struct on_ic_operation *ic_op; + struct on_fw_operation *fw_op; + struct on_flash_operation *flash_op; + struct on_sram_operation *sram_op; + struct on_driver_operation *driver_op; +}; + +struct himax_chip_detect { + bool (*fp_chip_detect)(void); +}; + +struct himax_chip_entry { + struct himax_chip_detect *core_chip_dt; + uint32_t hx_ic_dt_num; +}; + +struct himax_core_fp { +#ifdef CORE_IC + void (*fp_burst_enable)(uint8_t auto_add_4_byte); + int (*fp_register_read)(uint8_t *read_addr, uint32_t read_length, uint8_t *read_data, uint8_t cfg_flag); + /*int (*fp_flash_write_burst)(uint8_t *reg_byte, uint8_t *write_data);*/ + /*void (*fp_flash_write_burst_lenth)(uint8_t *reg_byte, uint8_t *write_data, uint32_t length);*/ + int (*fp_register_write)(uint8_t *write_addr, uint32_t write_length, uint8_t *write_data, uint8_t cfg_flag); + void (*fp_interface_on)(void); + void (*fp_sense_on)(uint8_t FlashMode); + void (*fp_tcon_on)(void); + bool (*fp_watch_dog_off)(void); + bool (*fp_sense_off)(bool check_en); + void (*fp_sleep_in)(void); + bool (*fp_wait_wip)(int Timing); + void (*fp_init_psl)(void); + void (*fp_resume_ic_action)(void); + void (*fp_suspend_ic_action)(void); + void (*fp_power_on_init)(void); + bool (*fp_dd_clk_set)(bool enable); + void (*fp_dd_reg_en)(bool enable); + bool (*fp_dd_reg_write)(uint8_t addr, uint8_t pa_num, int len, uint8_t *data, uint8_t bank); + bool (*fp_dd_reg_read)(uint8_t addr, uint8_t pa_num, int len, uint8_t *data, uint8_t bank); + bool (*fp_ic_id_read)(void); + bool (*fp_slave_tcon_reset)(void); + bool (*fp_slave_adc_reset_slave)(void); + bool (*fp_slave_wdt_off_slave)(void); +#endif + +#ifdef CORE_FW + void (*fp_parse_raw_data)(struct himax_report_data *hx_touch_data, int mul_num, int self_num, uint8_t diag_cmd, int16_t *mutual_data, int16_t *self_data); + void (*fp_system_reset)(void); + int (*fp_Calculate_CRC_with_AP)(unsigned char *FW_content, int CRC_from_FW, int len); + uint32_t (*fp_check_CRC)(uint8_t *start_addr, int reload_length); + void (*fp_set_reload_cmd)(uint8_t *write_data, int idx, uint32_t cmd_from, uint32_t cmd_to, uint32_t cmd_beat); + bool (*fp_program_reload)(void); + void (*fp_set_SMWP_enable)(uint8_t SMWP_enable, bool suspended); +#ifdef HX_EDGE_LIMIT + void (*fp_set_edge_limit_enable)(uint8_t edge_limit_enable, bool suspended); +#endif + void (*fp_set_HSEN_enable)(uint8_t HSEN_enable, bool suspended); + void (*fp_diag_register_set)(uint8_t diag_command, uint8_t storage_type); +#ifdef HX_TP_SELF_TEST_DRIVER + void (*fp_control_reK)(bool enable); +#endif + int (*fp_chip_self_test)(void); + void (*fp_idle_mode)(int disable); + void (*fp_reload_disable)(int disable); + bool (*fp_check_chip_version)(void); + int (*fp_read_ic_trigger_type)(void); + int (*fp_read_i2c_status)(void); + void (*fp_read_FW_ver)(void); + bool (*fp_read_event_stack)(uint8_t *buf, uint8_t length); + void (*fp_return_event_stack)(void); + bool (*fp_calculateChecksum)(bool change_iref, uint32_t size); + int (*fp_read_FW_status)(uint8_t *state_addr, uint8_t *tmp_addr); + void (*fp_irq_switch)(int switch_on); + int (*fp_assign_sorting_mode)(uint8_t *tmp_data); + int (*fp_check_sorting_mode)(uint8_t *tmp_data); + int (*fp_switch_mode)(int mode); + int (*fp_get_max_dc)(void); + uint8_t (*fp_read_DD_status)(uint8_t *cmd_set, uint8_t *tmp_data); + int (*fp_ulpm_in)(void); + int (*fp_black_gest_ctrl)(bool enable); + int (*_diff_overlay_bin)(void); +#endif + +#ifdef CORE_FLASH + void (*fp_chip_erase)(void); + bool (*fp_block_erase)(int start_addr, int length); + bool (*fp_sector_erase)(int start_addr); + void (*fp_flash_programming)(uint8_t *FW_content, int FW_Size); + void (*fp_flash_page_write)(uint8_t *write_addr, int length, uint8_t *write_data); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_32k)(unsigned char *fw, int len, bool change_iref); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_60k)(unsigned char *fw, int len, bool change_iref); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_64k)(unsigned char *fw, int len, bool change_iref); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_124k)(unsigned char *fw, int len, bool change_iref); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_128k)(unsigned char *fw, int len, bool change_iref); + void (*fp_flash_dump_func)(uint8_t local_flash_command, int Flash_Size, uint8_t *flash_buffer); + bool (*fp_flash_lastdata_check)(uint32_t size); + bool (*fp_ahb_squit)(void); + void (*fp_flash_read)(uint8_t *r_data, int start_addr, int length); + bool (*fp_sfr_rw)(uint8_t *addr, int length, uint8_t *data, uint8_t rw_ctrl); + bool (*fp_lock_flash)(void); + bool (*fp_unlock_flash)(void); + void (*fp_init_auto_func)(void); + int (*_diff_overlay_flash)(void); +#endif + +#ifdef CORE_SRAM + void (*fp_sram_write)(uint8_t *FW_content); + bool (*fp_sram_verify)(uint8_t *FW_File, int FW_Size); + bool (*fp_get_DSRAM_data)(uint8_t *info_data, bool DSRAM_Flag); +#endif + +#ifdef CORE_DRIVER + void (*fp_chip_init)(void); +#ifdef HX_AUTO_UPDATE_FW + int (*fp_fw_ver_bin)(void); +#endif + void (*fp_pin_reset)(void); + void (*fp_touch_information)(void); + void (*fp_reload_config)(void); + int (*fp_get_touch_data_size)(void); + void (*fp_usb_detect_set)(uint8_t *cable_config); + int (*fp_hand_shaking)(void); + int (*fp_determin_diag_rawdata)(int diag_command); + int (*fp_determin_diag_storage)(int diag_command); + int (*fp_cal_data_len)(int raw_cnt_rmd, int HX_MAX_PT, int raw_cnt_max); + bool (*fp_diag_check_sum)(struct himax_report_data *hx_touch_data); + void (*fp_diag_parse_raw_data)(struct himax_report_data *hx_touch_data, int mul_num, int self_num, uint8_t diag_cmd, int32_t *mutual_data, int32_t *self_data); + void (*fp_ic_reset)(uint8_t loadconfig, uint8_t int_off); + int (*fp_ic_esd_recovery)(int hx_esd_event, int hx_zero_event, int length); +#if defined(HX_112F_SET) + void (*_esd_ic_dd_reset)(void); +#endif + void (*fp_esd_ic_reset)(void); + void (*fp_resend_cmd_func)(bool suspended); +#ifdef HX_TP_PROC_GUEST_INFO + int (*guest_info_get_status)(void); + int (*read_guest_info)(void); +#endif +#endif +#ifdef HX_ZERO_FLASH + void (*fp_clean_sram_0f)(uint8_t *addr, int write_len, int type); + void (*fp_write_sram_0f)(const struct firmware *fw_entry, uint8_t *addr, int start_index, uint32_t write_len); + int (*fp_write_sram_0f_crc)(const struct firmware *fw_entry, uint8_t *addr, int start_index, uint32_t write_len); + void (*fp_firmware_update_0f)(const struct firmware *fw_entry); + int (*fp_0f_operation_dirly)(void); + int (*fp_0f_op_file_dirly)(char *file_name); + void (*fp_0f_operation)(struct work_struct *work); + int (*fp_0f_esd_check)(void); +#if defined(HX83102D) || defined(HX_112F_SET) + void (*fp_0f_reload_to_active)(void); +#endif +#ifdef HX_0F_DEBUG + void (*fp_read_sram_0f)(const struct firmware *fw_entry, uint8_t *addr, int start_index, int read_len); + void (*fp_read_all_sram)(uint8_t *addr, int read_len); + void (*fp_firmware_read_0f)(const struct firmware *fw_entry, int type); + void (*fp_0f_operation_check)(int type); +#endif +#ifdef HX_CODE_OVERLAY + int (*fp_0f_overlay)(int ovl_type, int mode); +#endif +#ifdef HIMAX_PALM_SENSOR_EN + int (*fp_palm_detection_function)(int mode); +#endif +#endif +}; + +#endif diff --git a/drivers/input/touchscreen/himax_0flash_mmi/himax_ic_incell_core.c b/drivers/input/touchscreen/himax_0flash_mmi/himax_ic_incell_core.c new file mode 100755 index 000000000000..d167d36189d6 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/himax_ic_incell_core.c @@ -0,0 +1,4162 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for incell ic core functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_ic_core.h" + +struct himax_core_command_operation *g_core_cmd_op; +struct ic_operation *pic_op; +EXPORT_SYMBOL(pic_op); + +struct fw_operation *pfw_op; +EXPORT_SYMBOL(pfw_op); + +struct flash_operation *pflash_op; +struct sram_operation *psram_op; +struct driver_operation *pdriver_op; +EXPORT_SYMBOL(pdriver_op); + +#ifdef HX_ZERO_FLASH +struct zf_operation *pzf_op; +EXPORT_SYMBOL(pzf_op); +#if defined(HX_CODE_OVERLAY) && defined(HX83102D) +uint8_t *ovl_idx; +EXPORT_SYMBOL(ovl_idx); +#endif +#endif + +void (*himax_mcu_cmd_struct_free)(void); +static uint8_t *g_internal_buffer; + +#ifdef CORE_IC +/* IC side start*/ +static void himax_mcu_burst_enable(uint8_t auto_add_4_byte) +{ + uint8_t tmp_data[DATA_LEN_4]; + + /*I("%s,Entering\n", __func__);*/ + tmp_data[0] = pic_op->data_conti[0]; + + if (himax_bus_write(pic_op->addr_conti[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + tmp_data[0] = (pic_op->data_incr4[0] | auto_add_4_byte); + + if (himax_bus_write(pic_op->addr_incr4[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } +} + +static int himax_mcu_register_read(uint8_t *read_addr, uint32_t read_length, uint8_t *read_data, uint8_t cfg_flag) +{ + uint8_t tmp_data[DATA_LEN_4]; + int i = 0; + int address = 0; + + /*I("%s,Entering\n",__func__);*/ + + if (cfg_flag == false) { + if (read_length > FLASH_RW_MAX_LEN) { + E("%s: read len over %d!\n", __func__, FLASH_RW_MAX_LEN); + return LENGTH_FAIL; + } + + if (read_length > DATA_LEN_4) + g_core_fp.fp_burst_enable(1); + else + g_core_fp.fp_burst_enable(0); + + address = (read_addr[3] << 24) + (read_addr[2] << 16) + (read_addr[1] << 8) + read_addr[0]; + i = address; + tmp_data[0] = (uint8_t)i; + tmp_data[1] = (uint8_t)(i >> 8); + tmp_data[2] = (uint8_t)(i >> 16); + tmp_data[3] = (uint8_t)(i >> 24); + + if (himax_bus_write(pic_op->addr_ahb_addr_byte_0[0], tmp_data, DATA_LEN_4, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + tmp_data[0] = pic_op->data_ahb_access_direction_read[0]; + + if (himax_bus_write(pic_op->addr_ahb_access_direction[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + if (himax_bus_read(pic_op->addr_ahb_rdata_byte_0[0], read_data, read_length, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + if (read_length > DATA_LEN_4) + g_core_fp.fp_burst_enable(0); + + } else { + if (himax_bus_read(read_addr[0], read_data, read_length, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + } + return NO_ERR; +} + +static int himax_mcu_flash_write_burst_lenth(uint8_t *reg_byte, uint8_t *write_data, uint32_t length) +{ + uint8_t *data_byte; + + if (!g_internal_buffer) { + E("%s: internal buffer not initialized!\n", __func__); + return MEM_ALLOC_FAIL; + } + data_byte = g_internal_buffer; + + memcpy(data_byte, reg_byte, ADDR_LEN_4); /* assign addr 4bytes */ + + memcpy(data_byte + ADDR_LEN_4, write_data, length); /* assign data n bytes */ + + if (himax_bus_write(pic_op->addr_ahb_addr_byte_0[0], data_byte, length + ADDR_LEN_4, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: xfer fail!\n", __func__); + return I2C_FAIL; + } + + return NO_ERR; +} + +static int himax_mcu_register_write(uint8_t *write_addr, uint32_t write_length, uint8_t *write_data, uint8_t cfg_flag) +{ + int address; +#if 1 + uint8_t tmp_addr[4]; + uint8_t *tmp_data; + + int total_read_times = 0; + uint32_t max_bus_size = MAX_I2C_TRANS_SZ; + uint32_t total_size_temp = 0; + int i = 0; +#endif + + /*I("%s,Entering\n", __func__);*/ + if (cfg_flag == 0) { + total_size_temp = write_length; +#ifdef __MTK_SPI__ + if (write_length > (HX_SPI_MTK_MAX_WRITE_SZ - 4)) + max_bus_size = HX_SPI_MTK_MAX_WRITE_SZ - 4; +#else + if (write_length > (PACKET_MAX_SZ - 8)) + max_bus_size = PACKET_MAX_SZ - 8; +#endif + else + max_bus_size = write_length; + + tmp_addr[3] = write_addr[3]; + tmp_addr[2] = write_addr[2]; + tmp_addr[1] = write_addr[1]; + tmp_addr[0] = write_addr[0]; + + if (total_size_temp % max_bus_size == 0) + total_read_times = total_size_temp / max_bus_size; + else + total_read_times = total_size_temp / max_bus_size + 1; + + if (write_length > DATA_LEN_4) + g_core_fp.fp_burst_enable(1); + else + g_core_fp.fp_burst_enable(0); + +#if 1 + for (i = 0; i < (total_read_times); i++) { + /* I("[log]write %d time start!\n", i); + * I("[log]addr[3]=0x%02X, addr[2]=0x%02X, addr[1]=0x%02X, addr[0]=0x%02X!\n", tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + * I("%s, write addr = 0x%02X%02X%02X%02X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + */ + + if (total_size_temp >= max_bus_size) { + tmp_data = write_data+(i * max_bus_size); + if (himax_mcu_flash_write_burst_lenth(tmp_addr, tmp_data, max_bus_size) < 0) { + I("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + total_size_temp = total_size_temp - max_bus_size; + } else { + tmp_data = write_data+(i * max_bus_size); + /* I("last total_size_temp=%d\n", total_size_temp % max_bus_size); */ + if (himax_mcu_flash_write_burst_lenth(tmp_addr, tmp_data, total_size_temp) < 0) { + I("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + } + + /*I("[log]write %d time end!\n", i);*/ + address = ((i+1) * max_bus_size); + tmp_addr[0] = write_addr[0] + (uint8_t) ((address) & 0x00FF); + + if (tmp_addr[0] < write_addr[0]) + tmp_addr[1] = write_addr[1] + (uint8_t) ((address>>8) & 0x00FF) + 1; + else + tmp_addr[1] = write_addr[1] + (uint8_t) ((address>>8) & 0x00FF); + + udelay(100); + } +#else + if (himax_mcu_flash_write_burst_lenth(write_addr, write_data, write_length) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } +#endif + + } else if (cfg_flag == 1) { + if (himax_bus_write(write_addr[0], write_data, write_length, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + } else + E("%s: cfg_flag = %d, value is wrong!\n", __func__, cfg_flag); + + return NO_ERR; +} + +static int himax_write_read_reg(uint8_t *tmp_addr, uint8_t *tmp_data, uint8_t hb, uint8_t lb) +{ + int cnt = 0; + + do { + g_core_fp.fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, 0); + usleep_range(10000, 11000); + g_core_fp.fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + /* I("%s:Now tmp_data[0]=0x%02X,[1]=0x%02X,[2]=0x%02X,[3]=0x%02X\n", + * __func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + */ + } while ((tmp_data[1] != hb && tmp_data[0] != lb) && cnt++ < 100); + + if (cnt == 99) + return HX_RW_REG_FAIL; + + I("Now register 0x%08X : high byte=0x%02X,low byte=0x%02X\n", tmp_addr[3], tmp_data[1], tmp_data[0]); + return NO_ERR; +} + +static void himax_mcu_interface_on(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_data2[DATA_LEN_4]; + int cnt = 0; + + /* Read a dummy register to wake up I2C.*/ + if (himax_bus_read(pic_op->addr_ahb_rdata_byte_0[0], tmp_data, DATA_LEN_4, HIMAX_I2C_RETRY_TIMES) < 0) {/* to knock I2C*/ + E("%s: i2c access fail!\n", __func__); + return; + } + + do { + tmp_data[0] = pic_op->data_conti[0]; + + if (himax_bus_write(pic_op->addr_conti[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + tmp_data[0] = pic_op->data_incr4[0]; + + if (himax_bus_write(pic_op->addr_incr4[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + /*Check cmd*/ + himax_bus_read(pic_op->addr_conti[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES); + himax_bus_read(pic_op->addr_incr4[0], tmp_data2, 1, HIMAX_I2C_RETRY_TIMES); + + if (tmp_data[0] == pic_op->data_conti[0] && tmp_data2[0] == pic_op->data_incr4[0]) + break; + + usleep_range(1000, 1100); + } while (++cnt < 10); + + if (cnt > 0) + I("%s:Polling burst mode: %d times\n", __func__, cnt); +} + +static bool himax_mcu_wait_wip(int Timing) +{ + uint8_t tmp_data[DATA_LEN_4]; + int retry_cnt = 0; + + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_fmt, DATA_LEN_4, pflash_op->data_spi200_trans_fmt, 0); + tmp_data[0] = 0x01; + + do { + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, pflash_op->data_spi200_trans_ctrl_1, 0); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_1, 0); + tmp_data[0] = tmp_data[1] = tmp_data[2] = tmp_data[3] = 0xFF; + g_core_fp.fp_register_read(pflash_op->addr_spi200_data, 4, tmp_data, 0); + + if ((tmp_data[0] & 0x01) == 0x00) + return true; + + retry_cnt++; + + if (tmp_data[0] != 0x00 || tmp_data[1] != 0x00 || tmp_data[2] != 0x00 || tmp_data[3] != 0x00) + I("%s:Wait wip retry_cnt:%d, buffer[0]=%d, buffer[1]=%d, buffer[2]=%d, buffer[3]=%d\n", + __func__, retry_cnt, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + + if (retry_cnt > 100) { + E("%s: Wait wip error!\n", __func__); + return false; + } + + msleep(Timing); + } while ((tmp_data[0] & 0x01) == 0x01); + + return true; +} + +static void himax_mcu_sense_on(uint8_t FlashMode) +{ + uint8_t tmp_data[DATA_LEN_4]; + int retry = 0; + + I("Enter %s\n", __func__); + g_core_fp.fp_interface_on(); + g_core_fp.fp_register_write(pfw_op->addr_ctrl_fw_isr, + sizeof(pfw_op->data_clear), pfw_op->data_clear, 0); + /*msleep(20);*/ + usleep_range(10000, 11000); + if (!FlashMode) { +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + } else { + do { + g_core_fp.fp_register_write(pfw_op->addr_safe_mode_release_pw, + sizeof(pfw_op->data_safe_mode_release_pw_active), pfw_op->data_safe_mode_release_pw_active, 0); + + g_core_fp.fp_register_read(pfw_op->addr_flag_reset_event, DATA_LEN_4, tmp_data, 0); + I("%s:Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + } while ((tmp_data[1] != 0x01 || tmp_data[0] != 0x00) && retry++ < 5); + + if (retry >= 5) { + E("%s: Fail:\n", __func__); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + } else { + I("%s:OK and Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + /* reset code*/ + tmp_data[0] = 0x00; + + if (himax_bus_write(pic_op->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + if (himax_bus_write(pic_op->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + g_core_fp.fp_register_write(pfw_op->addr_safe_mode_release_pw, + sizeof(pfw_op->data_safe_mode_release_pw_reset), pfw_op->data_safe_mode_release_pw_reset, 0); + } + } +} + +static bool himax_mcu_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_data[DATA_LEN_4]; + + do { + tmp_data[0] = pic_op->data_i2c_psw_lb[0]; + + if (himax_bus_write(pic_op->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + tmp_data[0] = pic_op->data_i2c_psw_ub[0]; + + if (himax_bus_write(pic_op->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + g_core_fp.fp_register_read(pic_op->addr_cs_central_state, ADDR_LEN_4, tmp_data, 0); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + g_core_fp.fp_register_write(pic_op->addr_tcon_on_rst, DATA_LEN_4, pic_op->data_rst, 0); + usleep_range(1000, 1100); + tmp_data[3] = pic_op->data_rst[3]; + tmp_data[2] = pic_op->data_rst[2]; + tmp_data[1] = pic_op->data_rst[1]; + tmp_data[0] = pic_op->data_rst[0] | 0x01; + g_core_fp.fp_register_write(pic_op->addr_tcon_on_rst, DATA_LEN_4, tmp_data, 0); + + g_core_fp.fp_register_write(pic_op->addr_adc_on_rst, DATA_LEN_4, pic_op->data_rst, 0); + usleep_range(1000, 1100); + tmp_data[3] = pic_op->data_rst[3]; + tmp_data[2] = pic_op->data_rst[2]; + tmp_data[1] = pic_op->data_rst[1]; + tmp_data[0] = pic_op->data_rst[0] | 0x01; + g_core_fp.fp_register_write(pic_op->addr_adc_on_rst, DATA_LEN_4, tmp_data, 0); + goto TRUE_END; + } else { + /*msleep(10);*/ +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + } + } while (cnt++ < 15); + + return false; +TRUE_END: + return true; +} + +static void himax_mcu_init_psl(void) /*power saving level*/ +{ + g_core_fp.fp_register_write(pic_op->addr_psl, sizeof(pic_op->data_rst), pic_op->data_rst, 0); + I("%s: power saving level reset OK!\n", __func__); +} + +static void himax_mcu_resume_ic_action(void) +{ + /* Nothing to do */ +} + +static void himax_mcu_suspend_ic_action(void) +{ + /* Nothing to do */ +} + +static void himax_mcu_power_on_init(void) +{ + I("%s:\n", __func__); + g_core_fp.fp_touch_information(); + /*RawOut select initial*/ + g_core_fp.fp_register_write(pfw_op->addr_raw_out_sel, sizeof(pfw_op->data_clear), pfw_op->data_clear, 0); + /*DSRAM func initial*/ + g_core_fp.fp_assign_sorting_mode(pfw_op->data_clear); + g_core_fp.fp_sense_on(0x00); +} + +static bool himax_mcu_dd_clk_set(bool enable) +{ + uint8_t data[4] = {0}; + + data[0] = (enable)?1:0; + return (g_core_fp.fp_register_write(pic_op->adr_osc_en, sizeof(pic_op->adr_osc_en), data, 0) == NO_ERR); +} + +static void himax_mcu_dd_reg_en(bool enable) +{ + uint8_t data[4] = {0}; + + g_core_fp.fp_dd_reg_read(0xCB, 8, 1, data, 0); + + if (data[0] != 0x44) { /*need DD Touch PW*/ + data[0] = 0xA5; data[1] = 0x00; + data[2] = 0x00; data[3] = 0x00; + g_core_fp.fp_register_write(pic_op->adr_osc_pw, DATA_LEN_4, data, 0); + data[0] = 0x00; data[1] = 0x55; + data[2] = 0xAA; data[3] = 0x00; + g_core_fp.fp_dd_reg_write(0xEB, 0, 4, data, 0); + } + + data[0] = 0x00; data[1] = 0x83; + data[2] = 0x11; data[3] = 0x2A; + g_core_fp.fp_dd_reg_write(0xB9, 0, 4, data, 0); +} + +#ifdef FIX_WVLA +static bool himax_mcu_dd_reg_write(uint8_t addr, uint8_t pa_num, int len, uint8_t *data, uint8_t bank) +{ + /*Calculate total write length*/ + uint32_t data_len = (((len + pa_num - 1) / 4 - pa_num / 4) + 1) * 4; + uint8_t *w_data = NULL; + uint8_t tmp_addr[4] = {0}; + uint8_t tmp_data[4] = {0}; + bool *chk_data = NULL; + uint32_t chk_idx = 0; + int i = 0; + int ret = -1; + + w_data = kcalloc(data_len, sizeof(uint8_t), GFP_KERNEL); + if (w_data == NULL) { + E("%s, Failed to allocate memory\n", __func__); + } + + chk_data = kcalloc(data_len, sizeof(bool), GFP_KERNEL); + if (chk_data == NULL) { + E("%s, Failed to allocate memory\n", __func__); + } + if (!w_data || !chk_data) + goto alloc_error; + + /*put input data*/ + chk_idx = pa_num % 4; + for (i = 0; i < len; i++) { + w_data[chk_idx] = data[i]; + chk_data[chk_idx++] = true; + } + + /*get original data*/ + chk_idx = (pa_num / 4) * 4; + for (i = 0; i < data_len; i++) { + if (!chk_data[i]) { + g_core_fp.fp_dd_reg_read(addr, (uint8_t)(chk_idx + i), 1, tmp_data, bank); + w_data[i] = tmp_data[0]; + chk_data[i] = true; + } + D("%s w_data[%d] = %2X\n", __func__, i, w_data[i]); + } + + tmp_addr[3] = 0x30; + tmp_addr[2] = addr >> 4; + tmp_addr[1] = (addr << 4) | (bank * 4); + tmp_addr[0] = chk_idx; + D("%s Addr = %02X%02X%02X%02X.\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + ret = g_core_fp.fp_register_write(tmp_addr, data_len, w_data, 0); +alloc_error: + if (chk_data) + kfree(chk_data); + if (w_data) + kfree(w_data); + + return (ret == NO_ERR); +} +#else +static bool himax_mcu_dd_reg_write(uint8_t addr, uint8_t pa_num, int len, uint8_t *data, uint8_t bank) +{ + /*Calculate total write length*/ + uint32_t data_len = (((len + pa_num - 1) / 4 - pa_num / 4) + 1) * 4; + uint8_t w_data[data_len]; + uint8_t tmp_addr[4] = {0}; + uint8_t tmp_data[4] = {0}; + bool chk_data[data_len]; + uint32_t chk_idx = 0; + int i = 0; + + memset(w_data, 0, data_len * sizeof(uint8_t)); + memset(chk_data, 0, data_len * sizeof(bool)); + + /*put input data*/ + chk_idx = pa_num % 4; + for (i = 0; i < len; i++) { + w_data[chk_idx] = data[i]; + chk_data[chk_idx++] = true; + } + + /*get original data*/ + chk_idx = (pa_num / 4) * 4; + for (i = 0; i < data_len; i++) { + if (!chk_data[i]) { + g_core_fp.fp_dd_reg_read(addr, (uint8_t)(chk_idx + i), 1, tmp_data, bank); + w_data[i] = tmp_data[0]; + chk_data[i] = true; + } + D("%s w_data[%d] = %2X\n", __func__, i, w_data[i]); + } + + tmp_addr[3] = 0x30; + tmp_addr[2] = addr >> 4; + tmp_addr[1] = (addr << 4) | (bank * 4); + tmp_addr[0] = chk_idx; + D("%s Addr = %02X%02X%02X%02X.\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + return (g_core_fp.fp_register_write(tmp_addr, data_len, w_data, 0) == NO_ERR); +} +#endif + +static bool himax_mcu_dd_reg_read(uint8_t addr, uint8_t pa_num, int len, uint8_t *data, uint8_t bank) +{ + uint8_t tmp_addr[4] = {0}; + uint8_t tmp_data[4] = {0}; + int i = 0; + + for (i = 0; i < len; i++) { + tmp_addr[3] = 0x30; + tmp_addr[2] = addr >> 4; + tmp_addr[1] = (addr << 4) | (bank * 4); + tmp_addr[0] = pa_num + i; + + if (g_core_fp.fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0)) + goto READ_FAIL; + data[i] = tmp_data[0]; + + D("%s Addr = %02X%02X%02X%02X.data = %2X\n", __func__, + tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0], data[i]); + } + return true; + +READ_FAIL: + E("%s Read DD reg Failed.\n", __func__); + return false; +} + +static bool himax_mcu_ic_id_read(void) +{ + int i = 0; + uint8_t data[4] = {0}; + + g_core_fp.fp_dd_clk_set(true); + g_core_fp.fp_dd_reg_en(true); + + for (i = 0; i < 13; i++) { + data[0] = 0x28 + i; + g_core_fp.fp_dd_reg_write(0xBB, 2, 1, data, 0); + data[0] = 0x80; + g_core_fp.fp_dd_reg_write(0xBB, 4, 1, data, 0); + data[0] = 0x00; + g_core_fp.fp_dd_reg_write(0xBB, 4, 1, data, 0); + g_core_fp.fp_dd_reg_read(0xBB, 5, 1, data, 0); + ic_data->vendor_ic_id[i] = data[0]; + I("ic_data->vendor_ic_id[%d] = %02X\n", i, ic_data->vendor_ic_id[i]); + } + + g_core_fp.fp_dd_clk_set(false); + + return true; +} + +/* IC side end*/ +#endif + +#ifdef CORE_FW +/* FW side start*/ +static void diag_mcu_parse_raw_data(struct himax_report_data *hx_touch_data, int mul_num, int self_num, uint8_t diag_cmd, int32_t *mutual_data, int32_t *self_data) +{ + int RawDataLen_word; + int index = 0; + int temp1, temp2, i; + + if (hx_touch_data->hx_rawdata_buf[0] == pfw_op->data_rawdata_ready_lb[0] + && hx_touch_data->hx_rawdata_buf[1] == pfw_op->data_rawdata_ready_hb[0] + && hx_touch_data->hx_rawdata_buf[2] > 0 + && hx_touch_data->hx_rawdata_buf[3] == diag_cmd) { + RawDataLen_word = hx_touch_data->rawdata_size / 2; + index = (hx_touch_data->hx_rawdata_buf[2] - 1) * RawDataLen_word; + + /* I("Header[%d]: %x, %x, %x, %x, mutual: %d, self: %d\n", index, buf[56], buf[57], buf[58], buf[59], mul_num, self_num); + * I("RawDataLen=%d , RawDataLen_word=%d , hx_touch_info_size=%d\n", RawDataLen, RawDataLen_word, hx_touch_info_size); + */ + for (i = 0; i < RawDataLen_word; i++) { + temp1 = index + i; + + if (temp1 < mul_num) { /*mutual*/ + mutual_data[index + i] = ((int8_t)hx_touch_data->hx_rawdata_buf[i * 2 + 4 + 1]) * 256 + hx_touch_data->hx_rawdata_buf[i * 2 + 4]; + } else { /*self*/ + temp1 = i + index; + temp2 = self_num + mul_num; + + if (temp1 >= temp2) + break; + + self_data[i + index - mul_num] = (((int8_t)hx_touch_data->hx_rawdata_buf[i * 2 + 4 + 1]) << 8) + + hx_touch_data->hx_rawdata_buf[i * 2 + 4]; + } + } + } +} + +static void himax_mcu_system_reset(void) +{ +#ifdef HX_SHARP_ENABLE_PON + g_core_fp.fp_register_write(pfw_op->addr_system_reset, sizeof(pfw_op->data_system_reset), pfw_op->data_system_reset, 0); +#else + uint8_t tmp_data[DATA_LEN_4]; + int retry = 0; + + g_core_fp.fp_interface_on(); + g_core_fp.fp_register_write(pfw_op->addr_ctrl_fw_isr, + sizeof(pfw_op->data_clear), pfw_op->data_clear, 0); + do { + /* reset code*/ + /* =========================================== + * I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + * =========================================== + */ + tmp_data[0] = pic_op->data_i2c_psw_lb[0]; + + if (himax_bus_write(pic_op->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + /* =========================================== + * I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + * =========================================== + */ + tmp_data[0] = pic_op->data_i2c_psw_ub[0]; + + if (himax_bus_write(pic_op->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + /* =========================================== + * I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x00 + * =========================================== + */ + tmp_data[0] = 0x00; + + if (himax_bus_write(pic_op->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + usleep_range(10000, 11000); + + g_core_fp.fp_register_read(pfw_op->addr_flag_reset_event, DATA_LEN_4, tmp_data, 0); + I("%s:Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + } while ((tmp_data[1] != 0x02 || tmp_data[0] != 0x00) && retry++ < 5); +#endif +} + +static int himax_mcu_Calculate_CRC_with_AP(unsigned char *FW_content, int CRC_from_FW, int len) +{ + int i, j, length = 0; + int fw_data; + int fw_data_2; + int CRC = 0xFFFFFFFF; + int PolyNomial = 0x82F63B78; + + length = len / 4; + + for (i = 0; i < length; i++) { + fw_data = FW_content[i * 4]; + + for (j = 1; j < 4; j++) { + fw_data_2 = FW_content[i * 4 + j]; + fw_data += (fw_data_2) << (8 * j); + } + CRC = fw_data ^ CRC; + for (j = 0; j < 32; j++) { + if ((CRC % 2) != 0) + CRC = ((CRC >> 1) & 0x7FFFFFFF) ^ PolyNomial; + else + CRC = (((CRC >> 1) & 0x7FFFFFFF)/*& 0x7FFFFFFF*/); + } + } + + return CRC; +} + +static uint32_t himax_mcu_check_CRC(uint8_t *start_addr, int reload_length) +{ + uint32_t result = 0; + uint8_t tmp_data[DATA_LEN_4]; + int cnt = 0, ret = 0; + int length = reload_length / DATA_LEN_4; + + ret = g_core_fp.fp_register_write(pfw_op->addr_reload_addr_from, DATA_LEN_4, start_addr, 0); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + + tmp_data[3] = 0x00; tmp_data[2] = 0x99; tmp_data[1] = (length >> 8); tmp_data[0] = length; + ret = g_core_fp.fp_register_write(pfw_op->addr_reload_addr_cmd_beat, DATA_LEN_4, tmp_data, 0); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + cnt = 0; + + do { + ret = g_core_fp.fp_register_read(pfw_op->addr_reload_status, DATA_LEN_4, tmp_data, 0); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + + if ((tmp_data[0] & 0x01) != 0x01) { + ret = g_core_fp.fp_register_read(pfw_op->addr_reload_crc32_result, DATA_LEN_4, tmp_data, 0); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + I("%s: tmp_data[3]=%X, tmp_data[2]=%X, tmp_data[1]=%X, tmp_data[0]=%X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1], tmp_data[0]); + result = ((tmp_data[3] << 24) + (tmp_data[2] << 16) + (tmp_data[1] << 8) + tmp_data[0]); + goto END; + } else { + I("Waiting for HW ready!\n"); + usleep_range(1000, 1100); + } + + } while (cnt++ < 100); +END: + return result; +} + +static void himax_mcu_set_reload_cmd(uint8_t *write_data, int idx, uint32_t cmd_from, uint32_t cmd_to, uint32_t cmd_beat) +{ + int index = idx * 12; + int i; + + for (i = 3; i >= 0; i--) { + write_data[index + i] = (cmd_from >> (8 * i)); + write_data[index + 4 + i] = (cmd_to >> (8 * i)); + write_data[index + 8 + i] = (cmd_beat >> (8 * i)); + } +} + +static bool himax_mcu_program_reload(void) +{ + return true; +} + +#ifdef HX_P_SENSOR +static int himax_mcu_ulpm_in(void) +{ + uint8_t tmp_data[4]; + int rtimes = 0; + + I("%s:entering\n", __func__); + + /* 34 -> 11 */ + do { + if (rtimes > 10) { + I("%s:1/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_34[0], pfw_op->data_ulpm_11, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_34[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x34, correct 0x11 = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_11[0]); + + rtimes = 0; + /* 34 -> 11 */ + do { + if (rtimes > 10) { + I("%s:2/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_34[0], pfw_op->data_ulpm_11, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_34[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x34, correct 0x11 = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_11[0]); + + /* 33 -> 33 */ + rtimes = 0; + do { + if (rtimes > 10) { + I("%s:3/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_33[0], pfw_op->data_ulpm_33, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_33[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x33, correct 0x33 = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_33[0]); + + /* 34 -> 22 */ + rtimes = 0; + do { + if (rtimes > 10) { + I("%s:4/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_34[0], pfw_op->data_ulpm_22, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_34[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x34, correct 0x22 = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_22[0]); + + /* 33 -> AA */ + rtimes = 0; + do { + if (rtimes > 10) { + I("%s:5/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_33[0], pfw_op->data_ulpm_aa, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_33[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x33, correct 0xAA = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_aa[0]); + + /* 33 -> 33 */ + rtimes = 0; + do { + if (rtimes > 10) { + I("%s:6/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_33[0], pfw_op->data_ulpm_33, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_33[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x33, correct 0x33 = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_33[0]); + + /* 33 -> AA */ + rtimes = 0; + do { + if (rtimes > 10) { + I("%s:7/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_33[0], pfw_op->data_ulpm_aa, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_33[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x33, correct 0xAA = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_aa[0]); + + I("%s:END\n", __func__); + return true; +} + +static int himax_mcu_black_gest_ctrl(bool enable) +{ + int ret = 0; + + I("%s:enable=%d, ts->is_suspended=%d\n", __func__, enable, private_ts->suspended); + + if (private_ts->suspended) { + if (enable) { +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + I("%s: Please enable TP reset define\n", __func__); +#endif + } else { + g_core_fp.fp_ulpm_in(); + } + } else { + g_core_fp.fp_sense_on(0); + } + return ret; +} +#endif + +static void himax_mcu_set_SMWP_enable(uint8_t SMWP_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t retry_cnt = 0; + + do { + if (SMWP_enable) { + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, back_data, 4); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + } + + g_core_fp.fp_register_read(pfw_op->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + /*I("%s: tmp_data[0]=%d, SMWP_enable=%d, retry_cnt=%d\n", __func__, tmp_data[0],SMWP_enable,retry_cnt);*/ + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} + +#ifdef HX_EDGE_LIMIT +static void himax_mcu_set_edge_limit_enable(uint8_t edge_limit_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t retry_cnt = 0; + + do { + if (edge_limit_enable == 0x01) {/*Portrait*/ + himax_in_parse_assign_cmd(fw_func_edge_portrait_pwd, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_edge_limit_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_func_edge_portrait_pwd, back_data, 4); + } else if (edge_limit_enable == 0x02) {/*Landscape-Right*/ + himax_in_parse_assign_cmd(fw_func_edge_lsp_right_pwd, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_edge_limit_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_func_edge_lsp_right_pwd, back_data, 4); + } else if (edge_limit_enable == 0x03) {/*Landscape-Left*/ + himax_in_parse_assign_cmd(fw_func_edge_lsp_left_pwd, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_edge_limit_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_func_edge_lsp_left_pwd, back_data, 4); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_edge_limit_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + } + + g_core_fp.fp_register_read(pfw_op->addr_edge_limit_enable, DATA_LEN_4, tmp_data, 0); + I("%s: tmp_data[0]=%d, SMWP_enable=%d, retry_cnt=%d \n", __func__, tmp_data[0], edge_limit_enable, retry_cnt); + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} +#endif + +static void himax_mcu_set_HSEN_enable(uint8_t HSEN_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t retry_cnt = 0; + + do { + if (HSEN_enable) { + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, back_data, 4); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + } + + g_core_fp.fp_register_read(pfw_op->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + /*I("%s: tmp_data[0]=%d, HSEN_enable=%d, retry_cnt=%d\n", __func__, tmp_data[0],HSEN_enable,retry_cnt);*/ + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} + +static void himax_mcu_usb_detect_set(uint8_t *cable_config) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t retry_cnt = 0; + + do { + if (cable_config[1] == 0x01) { + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, back_data, 4); + I("%s: USB detect status IN!\n", __func__); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + I("%s: USB detect status OUT!\n", __func__); + } + + g_core_fp.fp_register_read(pfw_op->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + /*I("%s: tmp_data[0]=%d, USB detect=%d, retry_cnt=%d\n", __func__, tmp_data[0],cable_config[1] ,retry_cnt);*/ + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} + +static void himax_mcu_diag_register_set(uint8_t diag_command, uint8_t storage_type) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t cnt = 50; + + if (diag_command > 0 && storage_type % 8 > 0) + tmp_data[0] = diag_command + 0x08; + else + tmp_data[0] = diag_command; + I("diag_command = %d, tmp_data[0] = %X\n", diag_command, tmp_data[0]); + g_core_fp.fp_interface_on(); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; + do { + g_core_fp.fp_register_write(pfw_op->addr_raw_out_sel, DATA_LEN_4, tmp_data, 0); + g_core_fp.fp_register_read(pfw_op->addr_raw_out_sel, DATA_LEN_4, back_data, 0); + I("%s: back_data[3]=0x%02X,back_data[2]=0x%02X,back_data[1]=0x%02X,back_data[0]=0x%02X!\n", + __func__, back_data[3], back_data[2], back_data[1], back_data[0]); + cnt--; + } while (tmp_data[0] != back_data[0] && cnt > 0); +} + +static int himax_mcu_chip_self_test(void) +{ + uint8_t tmp_data[FLASH_WRITE_BURST_SZ]; + uint8_t self_test_info[20]; + int pf_value = 0x00; + uint8_t test_result_id = 0; + int i; + + memset(tmp_data, 0x00, sizeof(tmp_data)); + g_core_fp.fp_interface_on(); + g_core_fp.fp_sense_off(true); + g_core_fp.fp_burst_enable(1); + g_core_fp.fp_register_write(pfw_op->addr_selftest_addr_en, DATA_LEN_4, pfw_op->data_selftest_request, 0); + /*Set criteria 0x10007F1C [0,1]=aa/up,down=, [2-3]=key/up,down, [4-5]=avg/up,down*/ + tmp_data[0] = pfw_op->data_criteria_aa_top[0]; + tmp_data[1] = pfw_op->data_criteria_aa_bot[0]; + tmp_data[2] = pfw_op->data_criteria_key_top[0]; + tmp_data[3] = pfw_op->data_criteria_key_bot[0]; + tmp_data[4] = pfw_op->data_criteria_avg_top[0]; + tmp_data[5] = pfw_op->data_criteria_avg_bot[0]; + tmp_data[6] = 0x00; + tmp_data[7] = 0x00; + g_core_fp.fp_register_write(pfw_op->addr_criteria_addr, FLASH_WRITE_BURST_SZ, tmp_data, 0); + g_core_fp.fp_register_write(pfw_op->addr_set_frame_addr, DATA_LEN_4, pfw_op->data_set_frame, 0); + /*Disable IDLE Mode*/ + g_core_fp.fp_idle_mode(1); + /*Diable Flash Reload*/ + g_core_fp.fp_reload_disable(1); + /*start selftest // leave safe mode*/ + g_core_fp.fp_sense_on(0x01); + + /*Hand shaking*/ + for (i = 0; i < 1000; i++) { + g_core_fp.fp_register_read(pfw_op->addr_selftest_addr_en, 4, tmp_data, 0); + I("%s: tmp_data[0] = 0x%02X,tmp_data[1] = 0x%02X,tmp_data[2] = 0x%02X,tmp_data[3] = 0x%02X, cnt=%d\n", + __func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3], i); + usleep_range(10000, 11000); + + if (tmp_data[1] == pfw_op->data_selftest_ack_hb[0] && tmp_data[0] == pfw_op->data_selftest_ack_lb[0]) { + I("%s Data ready goto moving data\n", __func__); + break; + } + } + + g_core_fp.fp_sense_off(true); + msleep(20); + /* ===================================== + * Read test result ==> bit[2][1][0] = [key][AA][avg] => 0xF = PASS + * ===================================== + */ + g_core_fp.fp_register_read(pfw_op->addr_selftest_result_addr, 20, self_test_info, 0); + test_result_id = self_test_info[0]; + I("%s: check test result, test_result_id=%x, test_result=%x\n", __func__ + , test_result_id, self_test_info[0]); + I("raw top 1 = %d\n", self_test_info[3] * 256 + self_test_info[2]); + I("raw top 2 = %d\n", self_test_info[5] * 256 + self_test_info[4]); + I("raw top 3 = %d\n", self_test_info[7] * 256 + self_test_info[6]); + I("raw last 1 = %d\n", self_test_info[9] * 256 + self_test_info[8]); + I("raw last 2 = %d\n", self_test_info[11] * 256 + self_test_info[10]); + I("raw last 3 = %d\n", self_test_info[13] * 256 + self_test_info[12]); + I("raw key 1 = %d\n", self_test_info[15] * 256 + self_test_info[14]); + I("raw key 2 = %d\n", self_test_info[17] * 256 + self_test_info[16]); + I("raw key 3 = %d\n", self_test_info[19] * 256 + self_test_info[18]); + + if (test_result_id == pfw_op->data_selftest_pass[0]) { + I("[Himax]: self-test pass\n"); + pf_value = 0x0; + } else { + E("[Himax]: self-test fail\n"); + /* E("[Himax]: bank_avg = %d, bank_max = %d,%d,%d, bank_min = %d,%d,%d, key = %d,%d,%d\n", + * tmp_data[1],tmp_data[2],tmp_data[3],tmp_data[4],tmp_data[5],tmp_data[6],tmp_data[7], + * tmp_data[8],tmp_data[9],tmp_data[10]); + */ + pf_value = 0x1; + } + + /*Enable IDLE Mode*/ + g_core_fp.fp_idle_mode(0); +#ifndef HX_ZERO_FLASH + /* Enable Flash Reload //recovery*/ + g_core_fp.fp_reload_disable(0); +#endif + g_core_fp.fp_sense_on(0x00); + msleep(120); + return pf_value; +} + +static void himax_mcu_idle_mode(int disable) +{ + int retry = 20; + uint8_t tmp_data[DATA_LEN_4]; + uint8_t switch_cmd = 0x00; + + I("%s:entering\n", __func__); + + do { + I("%s,now %d times!\n", __func__, retry); + g_core_fp.fp_register_read(pfw_op->addr_fw_mode_status, DATA_LEN_4, tmp_data, 0); + + if (disable) + switch_cmd = pfw_op->data_idle_dis_pwd[0]; + else + switch_cmd = pfw_op->data_idle_en_pwd[0]; + + tmp_data[0] = switch_cmd; + g_core_fp.fp_register_write(pfw_op->addr_fw_mode_status, DATA_LEN_4, tmp_data, 0); + g_core_fp.fp_register_read(pfw_op->addr_fw_mode_status, DATA_LEN_4, tmp_data, 0); + I("%s:After turn ON/OFF IDLE Mode [0] = 0x%02X,[1] = 0x%02X,[2] = 0x%02X,[3] = 0x%02X\n", + __func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + retry--; + usleep_range(10000, 11000); + } while ((tmp_data[0] != switch_cmd) && retry > 0); + + I("%s: setting OK!\n", __func__); +} + +static void himax_mcu_reload_disable(int disable) +{ + I("%s:entering\n", __func__); + + if (disable) { /*reload disable*/ + g_core_fp.fp_register_write(pdriver_op->addr_fw_define_flash_reload, DATA_LEN_4, pdriver_op->data_fw_define_flash_reload_dis, 0); + } else { /*reload enable*/ + g_core_fp.fp_register_write(pdriver_op->addr_fw_define_flash_reload, DATA_LEN_4, pdriver_op->data_fw_define_flash_reload_en, 0); + } + + I("%s: setting OK!\n", __func__); +} + +static bool himax_mcu_check_chip_version(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t ret_data = false; + int i = 0; + + for (i = 0; i < 5; i++) { + g_core_fp.fp_register_read(pfw_op->addr_icid_addr, DATA_LEN_4, tmp_data, 0); + I("%s:Read driver IC ID = %X,%X,%X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1]); + + if ((tmp_data[3] == 0x83) && (tmp_data[2] == 0x10) && (tmp_data[1] == 0x2a)) { + strlcpy(private_ts->chip_name, HX_83102A_SERIES_PWON, 30); + ret_data = true; + goto END; + } else { + ret_data = false; + E("%s:Read driver ID register Fail:\n", __func__); + } + } +END: + return ret_data; +} + +static int himax_mcu_read_ic_trigger_type(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + int trigger_type = false; + + g_core_fp.fp_register_read(pfw_op->addr_trigger_addr, DATA_LEN_4, tmp_data, 0); + + if ((tmp_data[1] & 0x01) == 1) + trigger_type = true; + + return trigger_type; +} + +static int himax_mcu_read_i2c_status(void) +{ + return i2c_error_count; +} + +static void himax_mcu_read_FW_ver(void) +{ + uint8_t data[12]; + uint8_t data_2[DATA_LEN_4]; + int retry = 200; + int reload_status = 0; + + g_core_fp.fp_sense_on(0x00); + + while (reload_status == 0) { + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_flash_reload, DATA_LEN_4, data, 0); + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_2nd_flash_reload, DATA_LEN_4, data_2, 0); + + if ((data[1] == 0x3A && data[0] == 0xA3) + || (data_2[1] == 0x72 && data_2[0] == 0xC0)) { + I("reload OK!\n"); + reload_status = 1; + break; + } else if (retry == 0) { + E("reload 20 times! fail\n"); + E("Maybe NOT have FW in chipset\n"); + E("Maybe Wrong FW in chipset\n"); + ic_data->vendor_panel_ver = 0; + ic_data->vendor_fw_ver = 0; + ic_data->vendor_config_ver = 0; + ic_data->vendor_touch_cfg_ver = 0; + ic_data->vendor_display_cfg_ver = 0; + ic_data->vendor_cid_maj_ver = 0; + ic_data->vendor_cid_min_ver = 0; + goto END; + } else { + retry--; + usleep_range(10000, 11000); + if (retry % 10 == 0) + I("reload fail ,delay 10ms retry=%d\n", retry); + } + } + + I("%s : data[0]=0x%2.2X,data[1]=0x%2.2X,data_2[0]=0x%2.2X,data_2[1]=0x%2.2X\n", __func__, data[0], data[1], data_2[0], data_2[1]); + I("reload_status=%d\n", reload_status); + /* ===================================== + * Read FW version + * ===================================== + */ + g_core_fp.fp_sense_off(true); + g_core_fp.fp_register_read(pfw_op->addr_fw_ver_addr, DATA_LEN_4, data, 0); + ic_data->vendor_panel_ver = data[0]; + ic_data->vendor_fw_ver = data[1] << 8 | data[2]; + I("PANEL_VER : %X\n", ic_data->vendor_panel_ver); + I("FW_VER : %X\n", ic_data->vendor_fw_ver); + g_core_fp.fp_register_read(pfw_op->addr_fw_cfg_addr, DATA_LEN_4, data, 0); + ic_data->vendor_config_ver = data[2] << 8 | data[3]; + /*I("CFG_VER : %X\n",ic_data->vendor_config_ver);*/ + ic_data->vendor_touch_cfg_ver = data[2]; + I("TOUCH_VER : %X\n", ic_data->vendor_touch_cfg_ver); + ic_data->vendor_display_cfg_ver = data[3]; + I("DISPLAY_VER : %X\n", ic_data->vendor_display_cfg_ver); + g_core_fp.fp_register_read(pfw_op->addr_fw_vendor_addr, DATA_LEN_4, data, 0); + ic_data->vendor_cid_maj_ver = data[2]; + ic_data->vendor_cid_min_ver = data[3]; + I("CID_VER : %X\n", (ic_data->vendor_cid_maj_ver << 8 | ic_data->vendor_cid_min_ver)); + g_core_fp.fp_register_read(pfw_op->addr_cus_info, 12, data, 0); + memcpy(ic_data->vendor_cus_info, data, 12); + I("Cusomer ID = %s\n", ic_data->vendor_cus_info); + g_core_fp.fp_register_read(pfw_op->addr_proj_info, 12, data, 0); + memcpy(ic_data->vendor_proj_info, data, 12); + I("Project ID = %s\n", ic_data->vendor_proj_info); + +END: + return; +} + +static bool himax_mcu_read_event_stack(uint8_t *buf, uint8_t length) +{ + uint8_t cmd[DATA_LEN_4]; + struct timespec t_start, t_end, t_delta; + int len = length; + int i2c_speed = 0; + + /* AHB_I2C Burst Read Off */ + cmd[0] = pfw_op->data_ahb_dis[0]; + + if (himax_bus_write(pfw_op->addr_ahb_addr[0], cmd, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return 0; + } + if (private_ts->debug_log_level & BIT(2)) + getnstimeofday(&t_start); + + himax_bus_read(pfw_op->addr_event_addr[0], buf, length, HIMAX_I2C_RETRY_TIMES); + + if (private_ts->debug_log_level & BIT(2)) { + getnstimeofday(&t_end); + t_delta.tv_nsec = (t_end.tv_sec * 1000000000 + t_end.tv_nsec) - (t_start.tv_sec * 1000000000 + t_start.tv_nsec); /*ns*/ + i2c_speed = (len * 9 * 1000000 / (int)t_delta.tv_nsec) * 13 / 10; + private_ts->bus_speed = (int)i2c_speed; + } + + /* AHB_I2C Burst Read On */ + cmd[0] = pfw_op->data_ahb_en[0]; + + if (himax_bus_write(pfw_op->addr_ahb_addr[0], cmd, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return 0; + } + + return 1; +} + +static void himax_mcu_return_event_stack(void) +{ + int retry = 20, i; + uint8_t tmp_data[DATA_LEN_4]; + + I("%s:entering\n", __func__); + + do { + I("now %d times!\n", retry); + + for (i = 0; i < DATA_LEN_4; i++) + tmp_data[i] = psram_op->addr_rawdata_end[i]; + + g_core_fp.fp_register_write(psram_op->addr_rawdata_addr, DATA_LEN_4, tmp_data, 0); + g_core_fp.fp_register_read(psram_op->addr_rawdata_addr, DATA_LEN_4, tmp_data, 0); + retry--; + usleep_range(10000, 11000); + } while ((tmp_data[1] != psram_op->addr_rawdata_end[1] && tmp_data[0] != psram_op->addr_rawdata_end[0]) && retry > 0); + + I("%s: End of setting!\n", __func__); +} + +static bool himax_mcu_calculateChecksum(bool change_iref, uint32_t size) +{ + uint8_t CRC_result = 0, i; + uint8_t tmp_data[DATA_LEN_4]; + + I("%s:Now size=%d\n", __func__, size); + for (i = 0; i < DATA_LEN_4; i++) + tmp_data[i] = psram_op->addr_rawdata_end[i]; + + CRC_result = g_core_fp.fp_check_CRC(tmp_data, size); + msleep(50); + + if (CRC_result != 0) + I("%s: CRC Fail=%d\n", __func__, CRC_result); + + return (CRC_result == 0) ? true : false; +} + +static int himax_mcu_read_FW_status(uint8_t *state_addr, uint8_t *tmp_addr) +{ + uint8_t i; + uint8_t req_size = 0; + uint8_t status_addr[DATA_LEN_4]; + uint8_t cmd_addr[DATA_LEN_4]; + + if (state_addr[0] == 0x01) { + state_addr[1] = 0x04; + + for (i = 0; i < DATA_LEN_4; i++) { + state_addr[i + 2] = pfw_op->addr_fw_dbg_msg_addr[i]; + status_addr[i] = pfw_op->addr_fw_dbg_msg_addr[i]; + } + + req_size = 0x04; + g_core_fp.fp_register_read(status_addr, req_size, tmp_addr, 0); + } else if (state_addr[0] == 0x02) { + state_addr[1] = 0x30; + + for (i = 0; i < DATA_LEN_4; i++) { + state_addr[i + 2] = pfw_op->addr_fw_dbg_msg_addr[i]; + cmd_addr[i] = pfw_op->addr_fw_dbg_msg_addr[i]; + } + + req_size = 0x30; + g_core_fp.fp_register_read(cmd_addr, req_size, tmp_addr, 0); + } + + return NO_ERR; +} + +static void himax_mcu_irq_switch(int switch_on) +{ + if (switch_on) { + if (private_ts->use_irq) + himax_int_enable(switch_on); + else + hrtimer_start(&private_ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + + } else { + if (private_ts->use_irq) + himax_int_enable(switch_on); + else { + hrtimer_cancel(&private_ts->timer); + cancel_work_sync(&private_ts->work); + } + } +} + +static int himax_mcu_assign_sorting_mode(uint8_t *tmp_data) +{ + + I("%s:Now tmp_data[3]=0x%02X,tmp_data[2]=0x%02X,tmp_data[1]=0x%02X,tmp_data[0]=0x%02X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1], tmp_data[0]); + g_core_fp.fp_register_write(pfw_op->addr_sorting_mode_en, DATA_LEN_4, tmp_data, 0); + + return NO_ERR; +} + +static int himax_mcu_check_sorting_mode(uint8_t *tmp_data) +{ + + g_core_fp.fp_register_read(pfw_op->addr_sorting_mode_en, DATA_LEN_4, tmp_data, 0); + I("%s: tmp_data[0]=%x,tmp_data[1]=%x\n", __func__, tmp_data[0], tmp_data[1]); + + return NO_ERR; +} + +static int himax_mcu_switch_mode(int mode) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t mode_wirte_cmd; + uint8_t mode_read_cmd; + int result = -1; + int retry = 200; + + I("%s: Entering\n", __func__); + + if (mode == 0) { /* normal mode */ + mode_wirte_cmd = pfw_op->data_normal_cmd[0]; + mode_read_cmd = pfw_op->data_normal_status[0]; + } else { /* sorting mode */ + mode_wirte_cmd = pfw_op->data_sorting_cmd[0]; + mode_read_cmd = pfw_op->data_sorting_status[0]; + } + + g_core_fp.fp_sense_off(true); + /*g_core_fp.fp_interface_on();*/ + /* clean up FW status */ + g_core_fp.fp_register_write(psram_op->addr_rawdata_addr, DATA_LEN_4, psram_op->addr_rawdata_end, 0); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = mode_wirte_cmd; + tmp_data[0] = mode_wirte_cmd; + g_core_fp.fp_assign_sorting_mode(tmp_data); + g_core_fp.fp_idle_mode(1); + g_core_fp.fp_reload_disable(1); + + /* To stable the sorting*/ + if (mode) { + g_core_fp.fp_register_write(pdriver_op->addr_fw_define_rxnum_txnum_maxpt, DATA_LEN_4, pdriver_op->data_fw_define_rxnum_txnum_maxpt_sorting, 0); + } else { + g_core_fp.fp_register_write(pfw_op->addr_set_frame_addr, DATA_LEN_4, pfw_op->data_set_frame, 0); + g_core_fp.fp_register_write(pdriver_op->addr_fw_define_rxnum_txnum_maxpt, DATA_LEN_4, pdriver_op->data_fw_define_rxnum_txnum_maxpt_normal, 0); + } + + g_core_fp.fp_sense_on(0x01); + + while (retry != 0) { + I("[%d] %s Read\n", retry, __func__); + g_core_fp.fp_check_sorting_mode(tmp_data); + msleep(100); + I("mode_read_cmd(0)=0x%2.2X,mode_read_cmd(1)=0x%2.2X\n", tmp_data[0], tmp_data[1]); + + if (tmp_data[0] == mode_read_cmd && tmp_data[1] == mode_read_cmd) { + I("Read OK!\n"); + result = 0; + break; + } + + g_core_fp.fp_register_read(pfw_op->addr_chk_fw_status, DATA_LEN_4, tmp_data, 0); + + if (tmp_data[0] == 0x00 && tmp_data[1] == 0x00 && tmp_data[2] == 0x00 && tmp_data[3] == 0x00) { + E("%s,: FW Stop!\n", __func__); + break; + } + + retry--; + } + + if (result == 0) { + if (mode == 0) { /*normal mode*/ + return HX_NORMAL_MODE; + } else { /*sorting mode*/ + return HX_SORTING_MODE; + } + } else { /*change mode fail*/ + return HX_CHANGE_MODE_FAIL; + } +} + +static int himax_mcu_get_max_dc(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + int dc_max = 0; + + g_core_fp.fp_register_read(pfw_op->addr_max_dc, DATA_LEN_4, tmp_data, 0); + I("%s: tmp_data[0]=%x,tmp_data[1]=%x\n", __func__, tmp_data[0], tmp_data[1]); + + dc_max = tmp_data[1] << 8 | tmp_data[0]; + I("%s: dc max = %d\n", __func__, dc_max); + return dc_max; +} + +static uint8_t himax_mcu_read_DD_status(uint8_t *cmd_set, uint8_t *tmp_data) +{ + int cnt = 0; + uint8_t req_size = cmd_set[0]; + + cmd_set[3] = pfw_op->data_dd_request[0]; + g_core_fp.fp_register_write(pfw_op->addr_dd_handshak_addr, DATA_LEN_4, cmd_set, 0); + I("%s: cmd_set[0] = 0x%02X,cmd_set[1] = 0x%02X,cmd_set[2] = 0x%02X,cmd_set[3] = 0x%02X\n", + __func__, cmd_set[0], cmd_set[1], cmd_set[2], cmd_set[3]); + + /* Doing hand shaking 0xAA -> 0xBB */ + for (cnt = 0; cnt < 100; cnt++) { + g_core_fp.fp_register_read(pfw_op->addr_dd_handshak_addr, DATA_LEN_4, tmp_data, 0); + usleep_range(10000, 11000); + + if (tmp_data[3] == pfw_op->data_dd_ack[0]) { + I("%s Data ready goto moving data\n", __func__); + goto FINALIZE; + } else { + if (cnt >= 99) { + I("%s Data not ready in FW\n", __func__); + return FW_NOT_READY; + } + } + } +FINALIZE: + g_core_fp.fp_register_read(pfw_op->addr_dd_data_addr, req_size, tmp_data, 0); + return NO_ERR; +} +#ifdef HX_AUTO_UPDATE_FW +static int hx_mcu_diff_overlay_bin(void) +{ + int rslt = 0; + int diff_val = 0; + + diff_val = g_i_FW_VER; + I("%s:Now fw ID is 0x%04X\n", __func__, diff_val); + diff_val = (diff_val >> 12); + I("%s:Now diff value=0x%04X\n", __func__, diff_val); + + if (diff_val == 1) + I("%s:Now size should be 128K!\n", __func__); + else + I("%s:Now size should be 64K!\n", __func__); + rslt = diff_val; + return rslt; +} +#endif + +/* FW side end*/ +#endif + +#ifdef CORE_FLASH +/* FLASH side start*/ +static void himax_mcu_chip_erase(void) +{ + g_core_fp.fp_interface_on(); + + /* Reset power saving level */ + if (g_core_fp.fp_init_psl != NULL) + g_core_fp.fp_init_psl(); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_fmt, DATA_LEN_4, pflash_op->data_spi200_trans_fmt, 0); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, pflash_op->data_spi200_trans_ctrl_2, 0); + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_2, 0); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_3, 0); + msleep(2000); + + if (!g_core_fp.fp_wait_wip(100)) + E("%s: Chip_Erase Fail\n", __func__); + +} + +static bool himax_mcu_block_erase(int start_addr, int length) /*complete not yet*/ +{ + uint32_t page_prog_start = 0; + uint32_t block_size = 0x10000; + + uint8_t tmp_data[4] = {0}; + + g_core_fp.fp_interface_on(); + + g_core_fp.fp_init_psl(); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_fmt, DATA_LEN_4, pflash_op->data_spi200_trans_fmt, 0); + + for (page_prog_start = start_addr; page_prog_start < start_addr + length; page_prog_start = page_prog_start + block_size) { + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, pflash_op->data_spi200_trans_ctrl_2, 0); + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_2, 0); + + tmp_data[3] = (page_prog_start >> 24)&0xFF; + tmp_data[2] = (page_prog_start >> 16)&0xFF; + tmp_data[1] = (page_prog_start >> 8)&0xFF; + tmp_data[0] = page_prog_start&0xFF; + g_core_fp.fp_register_write(pflash_op->addr_spi200_addr, DATA_LEN_4, tmp_data, 0); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, pflash_op->data_spi200_trans_ctrl_3, 0); + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_4, 0); + msleep(1000); + + if (!g_core_fp.fp_wait_wip(100)) { + E("%s:Erase Fail\n", __func__); + return false; + } + } + + I("%s:END\n", __func__); + return true; +} + +static bool himax_mcu_sector_erase(int start_addr) +{ + return true; +} + +static void himax_mcu_flash_programming(uint8_t *FW_content, int FW_Size) +{ + int page_prog_start = 0, i = 0, j = 0, k = 0; + int program_length = PROGRAM_SZ; + uint8_t tmp_data[DATA_LEN_4]; + uint8_t buring_data[FLASH_RW_MAX_LEN]; /* Read for flash data, 128K*/ + /* 4 bytes for padding*/ + g_core_fp.fp_interface_on(); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_fmt, DATA_LEN_4, pflash_op->data_spi200_trans_fmt, 0); + + for (page_prog_start = 0; page_prog_start < FW_Size; page_prog_start += FLASH_RW_MAX_LEN) { + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, pflash_op->data_spi200_trans_ctrl_2, 0); + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_2, 0); + + /*Programmable size = 1 page = 256 bytes, word_number = 256 byte / 4 = 64*/ + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, pflash_op->data_spi200_trans_ctrl_4, 0); + + /* Flash start address 1st : 0x0000_0000*/ + if (page_prog_start < 0x100) { + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = (uint8_t)page_prog_start; + } else if (page_prog_start >= 0x100 && page_prog_start < 0x10000) { + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = (uint8_t)(page_prog_start >> 8); + tmp_data[0] = (uint8_t)page_prog_start; + } else if (page_prog_start >= 0x10000 && page_prog_start < 0x1000000) { + tmp_data[3] = 0x00; + tmp_data[2] = (uint8_t)(page_prog_start >> 16); + tmp_data[1] = (uint8_t)(page_prog_start >> 8); + tmp_data[0] = (uint8_t)page_prog_start; + } + g_core_fp.fp_register_write(pflash_op->addr_spi200_addr, DATA_LEN_4, tmp_data, 0); + + for (i = 0; i < ADDR_LEN_4; i++) + buring_data[i] = pflash_op->addr_spi200_data[i]; + + + for (i = page_prog_start, j = 0; i < 16 + page_prog_start; i++, j++) + buring_data[j + ADDR_LEN_4] = FW_content[i]; + + + if (himax_bus_write(pic_op->addr_ahb_addr_byte_0[0], buring_data, ADDR_LEN_4 + 16, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_6, 0); + + for (j = 0; j < 5; j++) { + for (i = (page_prog_start + 16 + (j * 48)), k = 0; i < (page_prog_start + 16 + (j * 48)) + program_length; i++, k++) + buring_data[k + ADDR_LEN_4] = FW_content[i]; + + if (himax_bus_write(pic_op->addr_ahb_addr_byte_0[0], buring_data, program_length + ADDR_LEN_4, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + } + + if (!g_core_fp.fp_wait_wip(1)) + E("%s:Flash_Programming Fail\n", __func__); + + } +} + +static void himax_mcu_flash_page_write(uint8_t *write_addr, int length, uint8_t *write_data) +{ +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_32k(unsigned char *fw, int len, bool change_iref) +{ + /* Not use */ + return 0; +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_60k(unsigned char *fw, int len, bool change_iref) +{ + /* Not use */ + return 0; +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_64k(unsigned char *fw, int len, bool change_iref) +{ + int burnFW_success = 0; + + if (len != FW_SIZE_64k) { + E("%s: The file size is not 64K bytes\n", __func__); + return false; + } + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + g_core_fp.fp_sense_off(true); + g_core_fp.fp_block_erase(0x00, FW_SIZE_64k); + g_core_fp.fp_flash_programming(fw, FW_SIZE_64k); + + if (g_core_fp.fp_check_CRC(pfw_op->addr_program_reload_from, FW_SIZE_64k) == 0) + burnFW_success = 1; + + /*RawOut select initial*/ + g_core_fp.fp_register_write(pfw_op->addr_raw_out_sel, sizeof(pfw_op->data_clear), pfw_op->data_clear, 0); + /*DSRAM func initial*/ + g_core_fp.fp_assign_sorting_mode(pfw_op->data_clear); + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + /*System reset*/ + g_core_fp.fp_system_reset(); +#endif + return burnFW_success; +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_124k(unsigned char *fw, int len, bool change_iref) +{ + /* Not use */ + return 0; +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_128k(unsigned char *fw, int len, bool change_iref) +{ + int burnFW_success = 0; + + if (len != FW_SIZE_128k) { + E("%s: The file size is not 128K bytes\n", __func__); + return false; + } + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + g_core_fp.fp_sense_off(true); + g_core_fp.fp_block_erase(0x00, FW_SIZE_128k); + g_core_fp.fp_flash_programming(fw, FW_SIZE_128k); + + if (g_core_fp.fp_check_CRC(pfw_op->addr_program_reload_from, FW_SIZE_128k) == 0) + burnFW_success = 1; + + /*RawOut select initial*/ + g_core_fp.fp_register_write(pfw_op->addr_raw_out_sel, sizeof(pfw_op->data_clear), pfw_op->data_clear, 0); + /*DSRAM func initial*/ + g_core_fp.fp_assign_sorting_mode(pfw_op->data_clear); + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + /*System reset*/ + g_core_fp.fp_system_reset(); +#endif + return burnFW_success; +} + +static void himax_mcu_flash_dump_func(uint8_t local_flash_command, int Flash_Size, uint8_t *flash_buffer) +{ + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t buffer[256]; + int page_prog_start = 0; + + g_core_fp.fp_sense_off(true); + g_core_fp.fp_burst_enable(1); + + for (page_prog_start = 0; page_prog_start < Flash_Size; page_prog_start += 128) { + tmp_addr[0] = page_prog_start % 0x100; + tmp_addr[1] = (page_prog_start >> 8) % 0x100; + tmp_addr[2] = (page_prog_start >> 16) % 0x100; + tmp_addr[3] = page_prog_start / 0x1000000; + himax_mcu_register_read(tmp_addr, 128, buffer, 0); + memcpy(&flash_buffer[page_prog_start], buffer, 128); + } + + g_core_fp.fp_burst_enable(0); + g_core_fp.fp_sense_on(0x01); +} + +static bool himax_mcu_flash_lastdata_check(uint32_t size) +{ + uint8_t tmp_addr[4]; + uint32_t start_addr = 0xFF80; /* 64K - 0x80, which is the address of the last 128bytes in 64K, default value*/ + uint32_t temp_addr = 0; + uint32_t flash_page_len = 0x80; + uint8_t flash_tmp_buffer[128]; + + start_addr = size - flash_page_len; /* In order to match other size of fw*/ + I("%s: Now size is %d, the start_addr is 0x%04X\n", __func__, size, start_addr); + for (temp_addr = start_addr; temp_addr < (start_addr + flash_page_len); temp_addr = temp_addr + flash_page_len) { + /*I("temp_addr=%d,tmp_addr[0]=0x%2X, tmp_addr[1]=0x%2X,tmp_addr[2]=0x%2X,tmp_addr[3]=0x%2X\n", temp_addr,tmp_addr[0], tmp_addr[1], tmp_addr[2],tmp_addr[3]);*/ + tmp_addr[0] = temp_addr % 0x100; + tmp_addr[1] = (temp_addr >> 8) % 0x100; + tmp_addr[2] = (temp_addr >> 16) % 0x100; + tmp_addr[3] = temp_addr / 0x1000000; + g_core_fp.fp_register_read(tmp_addr, flash_page_len, &flash_tmp_buffer[0], 0); + } + + if ((!flash_tmp_buffer[flash_page_len-4]) && (!flash_tmp_buffer[flash_page_len-3]) && (!flash_tmp_buffer[flash_page_len-2]) && (!flash_tmp_buffer[flash_page_len-1])) { + I("Fail, Last four Bytes are flash_buffer[FFFC]=0x%2X,flash_buffer[FFFD]=0x%2X,flash_buffer[FFFE]=0x%2X,flash_buffer[FFFF]=0x%2X\n", + flash_tmp_buffer[flash_page_len-4], flash_tmp_buffer[flash_page_len-3], flash_tmp_buffer[flash_page_len-2], flash_tmp_buffer[flash_page_len-1]); + goto FAIL;/*FAIL*/ + } else if ((flash_tmp_buffer[flash_page_len-4] == 0xFF) && (flash_tmp_buffer[flash_page_len-3] == 0xFF) && (flash_tmp_buffer[flash_page_len-2] == 0xFF) && (flash_tmp_buffer[flash_page_len-1] == 0xFF)) { + I("Fail, Last four Bytes are flash_buffer[FFFC]=0x%2X,flash_buffer[FFFD]=0x%2X,flash_buffer[FFFE]=0x%2X,flash_buffer[FFFF]=0x%2X\n", + flash_tmp_buffer[flash_page_len-4], flash_tmp_buffer[flash_page_len-3], flash_tmp_buffer[flash_page_len-2], flash_tmp_buffer[flash_page_len-1]); + goto FAIL; + } else { + I("flash_buffer[FFFC]=0x%2X,flash_buffer[FFFD]=0x%2X,flash_buffer[FFFE]=0x%2X,flash_buffer[FFFF]=0x%2X\n", + flash_tmp_buffer[flash_page_len-4], flash_tmp_buffer[flash_page_len-3], flash_tmp_buffer[flash_page_len-2], flash_tmp_buffer[flash_page_len-1]); + return 0;/*PASS*/ + } + return 0; +FAIL: + return 1; +} +static int hx_mcu_diff_overlay_flash(void) +{ + int rslt = 0; + int diff_val = 0; + + diff_val = (ic_data->vendor_fw_ver); + I("%s:Now fw ID is 0x%04X\n", __func__, diff_val); + diff_val = (diff_val >> 12); + I("%s:Now diff value=0x%04X\n", __func__, diff_val); + + if (diff_val == 1) + I("%s:Now size should be 128K!\n", __func__); + else + I("%s:Now size should be 64K!\n", __func__); + rslt = diff_val; + return rslt; +} + +/* FLASH side end*/ +#endif + +#ifdef CORE_SRAM +/* SRAM side start*/ +static void himax_mcu_sram_write(uint8_t *FW_content) +{ +} + +static bool himax_mcu_sram_verify(uint8_t *FW_File, int FW_Size) +{ + return true; +} + +static bool himax_mcu_get_DSRAM_data(uint8_t *info_data, bool DSRAM_Flag) +{ + int i = 0; + unsigned char tmp_addr[ADDR_LEN_4]; + unsigned char tmp_data[DATA_LEN_4]; + uint8_t max_i2c_size = MAX_I2C_TRANS_SZ; + uint8_t x_num = ic_data->HX_RX_NUM; + uint8_t y_num = ic_data->HX_TX_NUM; + /*int m_key_num = 0;*/ + int total_size = (x_num * y_num + x_num + y_num) * 2 + 4; + int total_size_temp; + int mutual_data_size = x_num * y_num * 2; + int total_read_times = 0; + int address = 0; + uint8_t *temp_info_data; /*max mkey size = 8*/ + uint16_t check_sum_cal = 0; + int fw_run_flag = -1; + + temp_info_data = kcalloc((total_size + 8), sizeof(uint8_t), GFP_KERNEL); + /* 1. Read number of MKey R100070E8H to determin data size */ + /* m_key_num = ic_data->HX_BT_NUM; */ + /* I("%s,m_key_num=%d\n",__func__ ,m_key_num); */ + /* total_size += m_key_num * 2; */ + /* 2. Start DSRAM Rawdata and Wait Data Ready */ + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = psram_op->passwrd_start[1]; + tmp_data[0] = psram_op->passwrd_start[0]; + fw_run_flag = himax_write_read_reg(psram_op->addr_rawdata_addr, tmp_data, psram_op->passwrd_end[1], psram_op->passwrd_end[0]); + + if (fw_run_flag < 0) { + I("%s Data NOT ready => bypass\n", __func__); + goto FAIL; + } + + /* 3. Read RawData */ + total_size_temp = total_size; + I("%s: tmp_data[0] = 0x%02X,tmp_data[1] = 0x%02X,tmp_data[2] = 0x%02X,tmp_data[3] = 0x%02X\n", + __func__, psram_op->addr_rawdata_addr[0], psram_op->addr_rawdata_addr[1], psram_op->addr_rawdata_addr[2], psram_op->addr_rawdata_addr[3]); + tmp_addr[0] = psram_op->addr_rawdata_addr[0]; + tmp_addr[1] = psram_op->addr_rawdata_addr[1]; + tmp_addr[2] = psram_op->addr_rawdata_addr[2]; + tmp_addr[3] = psram_op->addr_rawdata_addr[3]; + + if (total_size % max_i2c_size == 0) + total_read_times = total_size / max_i2c_size; + else + total_read_times = total_size / max_i2c_size + 1; + + for (i = 0; i < total_read_times; i++) { + address = (psram_op->addr_rawdata_addr[3] << 24) + + (psram_op->addr_rawdata_addr[2] << 16) + + (psram_op->addr_rawdata_addr[1] << 8) + + psram_op->addr_rawdata_addr[0] + i * max_i2c_size; + /*I("%s address = %08X\n", __func__, address);*/ + + tmp_addr[3] = (uint8_t)((address >> 24) & 0x00FF); + tmp_addr[2] = (uint8_t)((address >> 16) & 0x00FF); + tmp_addr[1] = (uint8_t)((address >> 8) & 0x00FF); + tmp_addr[0] = (uint8_t)((address) & 0x00FF); + + if (total_size_temp >= max_i2c_size) { + g_core_fp.fp_register_read(tmp_addr, max_i2c_size, &temp_info_data[i * max_i2c_size], 0); + total_size_temp = total_size_temp - max_i2c_size; + } else { + /*I("last total_size_temp=%d\n",total_size_temp);*/ + g_core_fp.fp_register_read(tmp_addr, total_size_temp % max_i2c_size, &temp_info_data[i * max_i2c_size], 0); + } + } + + /* 4. FW stop outputing */ + tmp_data[3] = temp_info_data[3]; tmp_data[2] = temp_info_data[2]; + tmp_data[1] = 0x00; tmp_data[0] = 0x00; + g_core_fp.fp_register_write(psram_op->addr_rawdata_addr, DATA_LEN_4, tmp_data, 0); + + /* 5. Data Checksum Check */ + for (i = 2; i < total_size; i += 2)/* 2:PASSWORD NOT included */ + check_sum_cal += (temp_info_data[i + 1] * 256 + temp_info_data[i]); + + if (check_sum_cal % 0x10000 != 0) { + I("%s check_sum_cal fail=%2X\n", __func__, check_sum_cal); + goto FAIL; + } else { + memcpy(info_data, &temp_info_data[4], mutual_data_size * sizeof(uint8_t)); + /*I("%s checksum PASS\n", __func__);*/ + } + kfree(temp_info_data); + return true; +FAIL: + kfree(temp_info_data); + return false; +} +/* SRAM side end*/ +#endif + +#ifdef CORE_DRIVER + +static void himax_mcu_init_ic(void) +{ + I("%s: use default incell init.\n", __func__); +} + +#ifdef HX_AUTO_UPDATE_FW +static int himax_mcu_fw_ver_bin(void) +{ + I("%s: use default incell address.\n", __func__); + I("%s:Entering!\n", __func__); + if (i_CTPM_FW != NULL) { + I("Catch fw version in bin file!\n"); + g_i_FW_VER = (i_CTPM_FW[FW_VER_MAJ_FLASH_ADDR] << 8) | i_CTPM_FW[FW_VER_MIN_FLASH_ADDR]; + g_i_CFG_VER = (i_CTPM_FW[CFG_VER_MAJ_FLASH_ADDR] << 8) | i_CTPM_FW[CFG_VER_MIN_FLASH_ADDR]; + g_i_CID_MAJ = i_CTPM_FW[CID_VER_MAJ_FLASH_ADDR]; + g_i_CID_MIN = i_CTPM_FW[CID_VER_MIN_FLASH_ADDR]; + } else { + I("FW data is null!\n"); + return 1; + } + return NO_ERR; +} +#endif + + +#ifdef HX_RST_PIN_FUNC +static void himax_mcu_pin_reset(void) +{ + I("%s: Now reset the Touch chip,private_ts->vdd_1v8_always_on = %d \n", + __func__,private_ts->vdd_1v8_always_on); + himax_rst_gpio_set(private_ts->rst_gpio, 0); +#if defined(__HIMAX_HX83102_MOD__) + msleep(5); +#else + if(private_ts->vdd_1v8_always_on) + msleep(10); + else + msleep(20); +#endif + himax_rst_gpio_set(private_ts->rst_gpio, 1); +#ifdef HX_ZERO_FLASH +#if defined(__HIMAX_HX83102_MOD__) + msleep(2); +#else + msleep(10); +#endif +#else + msleep(50); +#endif + +} + +static void himax_mcu_ic_reset(uint8_t loadconfig, uint8_t int_off) +{ + struct himax_ts_data *ts = private_ts; + + HX_HW_RESET_ACTIVATE = 0; + I("%s,status: loadconfig=%d,int_off=%d\n", __func__, loadconfig, int_off); + + if (ts->rst_gpio >= 0) { + if (int_off) + g_core_fp.fp_irq_switch(0); + + g_core_fp.fp_pin_reset(); + + if (loadconfig) + g_core_fp.fp_reload_config(); + + if (int_off) + g_core_fp.fp_irq_switch(1); + + } +} +#endif + +#ifndef HX_FIX_TOUCH_INFO +static uint8_t himax_mcu_tp_info_check(void) +{ + uint8_t rx = pdriver_op->data_df_rx[0]; + uint8_t tx = pdriver_op->data_df_tx[0]; + uint8_t pt = pdriver_op->data_df_pt[0]; + uint16_t x_res = pdriver_op->data_df_x_res[1] << 8 | pdriver_op->data_df_x_res[0]; + uint16_t y_res = pdriver_op->data_df_y_res[1] << 8 | pdriver_op->data_df_y_res[0]; + uint8_t err_cnt = 0; + + if (ic_data->HX_RX_NUM < (rx / 2) || ic_data->HX_RX_NUM > (rx * 3 / 2)) { + ic_data->HX_RX_NUM = rx; + err_cnt |= 0x01; + } + if (ic_data->HX_TX_NUM < (tx / 2) || ic_data->HX_TX_NUM > (tx * 3 / 2)) { + ic_data->HX_TX_NUM = tx; + err_cnt |= 0x02; + } + if (ic_data->HX_MAX_PT < (pt / 2) || ic_data->HX_MAX_PT > (pt * 3 / 2)) { + ic_data->HX_MAX_PT = pt; + err_cnt |= 0x04; + } + if (ic_data->HX_Y_RES < (y_res / 2) || ic_data->HX_Y_RES > (y_res * 3 / 2)) { + ic_data->HX_Y_RES = y_res; + err_cnt |= 0x08; + } + if (ic_data->HX_X_RES < (x_res / 2) || ic_data->HX_X_RES > (x_res * 3 / 2)) { + ic_data->HX_X_RES = x_res; + err_cnt |= 0x10; + } + return err_cnt; +} +#endif + +static void himax_mcu_touch_information(void) +{ +#ifndef HX_FIX_TOUCH_INFO + char data[DATA_LEN_8] = {0}; + uint8_t err_cnt = 0; + + I(" DT-%s:touch_info: HX_RX_NUM = %d, HX_TX_NUM = %d, HX_BT_NUM = %d,HX_X_RES = %d,HX_Y_RES = %d, HX_MAX_PT = %d, HX_XY_REVERSE = %d, HX_INT_IS_EDGE = %d\n", __func__, + ic_data->HX_RX_NUM,ic_data->HX_TX_NUM,ic_data->HX_BT_NUM,ic_data->HX_X_RES,ic_data->HX_Y_RES,ic_data->HX_MAX_PT,ic_data->HX_XY_REVERSE,ic_data->HX_INT_IS_EDGE); + + if ((0 == ic_data->HX_RX_NUM) || (0 == ic_data->HX_TX_NUM) || (0 == ic_data->HX_X_RES) || (0 == ic_data->HX_Y_RES)) { + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_rxnum_txnum_maxpt, DATA_LEN_8, data, 0); + ic_data->HX_RX_NUM = data[2]; + ic_data->HX_TX_NUM = data[3]; + ic_data->HX_MAX_PT = data[4]; + /*I("%s : HX_RX_NUM=%d,ic_data->HX_TX_NUM=%d,ic_data->HX_MAX_PT=%d\n",__func__,ic_data->HX_RX_NUM,ic_data->HX_TX_NUM,ic_data->HX_MAX_PT);*/ + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_xy_res_enable, DATA_LEN_4, data, 0); + + /*I("%s : c_data->HX_XY_REVERSE=0x%2.2X\n",__func__,data[1]);*/ + if ((data[1] & 0x04) == 0x04) + ic_data->HX_XY_REVERSE = true; + else + ic_data->HX_XY_REVERSE = false; + + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_x_y_res, DATA_LEN_4, data, 0); + ic_data->HX_Y_RES = data[0] * 256 + data[1]; + ic_data->HX_X_RES = data[2] * 256 + data[3]; + /*I("%s : ic_data->HX_Y_RES=%d,ic_data->HX_X_RES=%d\n",__func__,ic_data->HX_Y_RES,ic_data->HX_X_RES);*/ + + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_int_is_edge, DATA_LEN_4, data, 0); + /*I("%s : data[0]=0x%2.2X,data[1]=0x%2.2X,data[2]=0x%2.2X,data[3]=0x%2.2X\n",__func__,data[0],data[1],data[2],data[3]);*/ + /*I("data[0] & 0x01 = %d\n",(data[0] & 0x01));*/ + if ((data[1] & 0x01) == 1) + ic_data->HX_INT_IS_EDGE = true; + else + ic_data->HX_INT_IS_EDGE = false; + + /*1. Read number of MKey R100070E8H to determin data size*/ + g_core_fp.fp_register_read(psram_op->addr_mkey, DATA_LEN_4, data, 0); + /* I("%s: tmp_data[0] = 0x%02X,tmp_data[1] = 0x%02X,tmp_data[2] = 0x%02X,tmp_data[3] = 0x%02X\n",*/ + /*__func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]);*/ + ic_data->HX_BT_NUM = data[0] & 0x03; + + err_cnt = himax_mcu_tp_info_check(); + if (err_cnt > 0) + E("TP Info from IC is wrong, err_cnt = 0x%X", err_cnt); + } +#else + ic_data->HX_RX_NUM = FIX_HX_RX_NUM; + ic_data->HX_TX_NUM = FIX_HX_TX_NUM; + ic_data->HX_MAX_PT = FIX_HX_MAX_PT; + ic_data->HX_XY_REVERSE = FIX_HX_XY_REVERSE; + ic_data->HX_Y_RES = FIX_HX_X_RES; + ic_data->HX_X_RES = FIX_HX_Y_RES; + ic_data->HX_INT_IS_EDGE = FIX_HX_INT_IS_EDGE; + +#endif + I("%s:HX_RX_NUM =%d,HX_TX_NUM =%d,HX_MAX_PT=%d\n", __func__, ic_data->HX_RX_NUM, ic_data->HX_TX_NUM, ic_data->HX_MAX_PT); + I("%s:HX_XY_REVERSE =%d,HX_Y_RES =%d,HX_X_RES=%d\n", __func__, ic_data->HX_XY_REVERSE, ic_data->HX_Y_RES, ic_data->HX_X_RES); + I("%s:HX_INT_IS_EDGE =%d\n", __func__, ic_data->HX_INT_IS_EDGE); +} + +static void himax_mcu_reload_config(void) +{ + if (himax_report_data_init()) + E("%s: allocate data fail\n", __func__); + + g_core_fp.fp_sense_on(0x00); +} + +static int himax_mcu_get_touch_data_size(void) +{ + return HIMAX_TOUCH_DATA_SIZE; +} + +static int himax_mcu_hand_shaking(void) +{ + /* 0:Running, 1:Stop, 2:I2C Fail */ + int result = 0; + return result; +} + +static int himax_mcu_determin_diag_rawdata(int diag_command) +{ + return diag_command % 10; +} + +static int himax_mcu_determin_diag_storage(int diag_command) +{ + return diag_command / 10; +} + +static int himax_mcu_cal_data_len(int raw_cnt_rmd, int HX_MAX_PT, int raw_cnt_max) +{ + int RawDataLen; + + if (raw_cnt_rmd != 0x00) + RawDataLen = MAX_I2C_TRANS_SZ - ((HX_MAX_PT + raw_cnt_max + 3) * 4) - 1; + else + RawDataLen = MAX_I2C_TRANS_SZ - ((HX_MAX_PT + raw_cnt_max + 2) * 4) - 1; + + return RawDataLen; +} + +static bool himax_mcu_diag_check_sum(struct himax_report_data *hx_touch_data) +{ + uint16_t check_sum_cal = 0; + int i; + + /* Check 128th byte CRC */ + for (i = 0, check_sum_cal = 0; i < (hx_touch_data->touch_all_size - hx_touch_data->touch_info_size); i += 2) + check_sum_cal += (hx_touch_data->hx_rawdata_buf[i + 1] * FLASH_RW_MAX_LEN + hx_touch_data->hx_rawdata_buf[i]); + + + if (check_sum_cal % HX64K != 0) { + I("%s fail=%2X\n", __func__, check_sum_cal); + return 0; + } + + return 1; +} + +static void himax_mcu_diag_parse_raw_data(struct himax_report_data *hx_touch_data, int mul_num, int self_num, uint8_t diag_cmd, int32_t *mutual_data, int32_t *self_data) +{ + diag_mcu_parse_raw_data(hx_touch_data, mul_num, self_num, diag_cmd, mutual_data, self_data); +} + +#ifdef HX_ESD_RECOVERY +static int himax_mcu_ic_esd_recovery(int hx_esd_event, int hx_zero_event, int length) +{ + int ret_val = NO_ERR; + + if (hx_esd_event == length) { + g_zero_event_count = 0; + ret_val = HX_ESD_EVENT; + } else if (hx_zero_event == length) { + if (g_zero_event_count > 5) { + g_zero_event_count = 0; + I("[HIMAX TP MSG]: ESD event checked - ALL Zero.\n"); + ret_val = HX_ESD_EVENT; + } else { + g_zero_event_count++; + I("[HIMAX TP MSG]: ALL Zero event is %d times.\n", g_zero_event_count); + ret_val = HX_ZERO_EVENT_COUNT; + } + } + + return ret_val; +} + +static void himax_mcu_esd_ic_reset(void) +{ + HX_ESD_RESET_ACTIVATE = 0; +#ifdef HX_RST_PIN_FUNC + himax_mcu_pin_reset(); +#else + himax_mcu_system_reset(); +#endif + I("%s:\n", __func__); +} +#endif +#ifdef HX_TP_PROC_GUEST_INFO +char *g_checksum_str = "check sum fail"; +/* char *g_guest_info_item[] = { + * "projectID", + * "CGColor", + * "BarCode", + * "Reserve1", + * "Reserve2", + * "Reserve3", + * "Reserve4", + * "Reserve5", + * "VCOM", + * "Vcom-3Gar", + * NULL + * }; + */ + +static int himax_guest_info_get_status(void) +{ + return g_guest_info_data->g_guest_info_ongoing; +} +static void himax_guest_info_set_status(int setting) +{ + g_guest_info_data->g_guest_info_ongoing = setting; +} + +static int himax_guest_info_read(uint32_t start_addr, uint8_t *flash_tmp_buffer) +{ + uint32_t temp_addr = 0; + uint8_t tmp_addr[4]; + uint32_t flash_page_len = 0x1000; + /* uint32_t checksum = 0x00; */ + int result = 0; + + + I("Reading guest info in start_addr = 0x%08X !\n", start_addr); + + tmp_addr[0] = start_addr % 0x100; + tmp_addr[1] = (start_addr >> 8) % 0x100; + tmp_addr[2] = (start_addr >> 16) % 0x100; + tmp_addr[3] = start_addr / 0x1000000; + I("Now start addr: tmp_addr[0]=0x%2X,tmp_addr[1]=0x%2X,tmp_addr[2]=0x%2X,tmp_addr[3]=0x%2X\n", tmp_addr[0], tmp_addr[1], tmp_addr[2], tmp_addr[3]); + result = g_core_fp.fp_check_CRC(tmp_addr, flash_page_len); + I("Checksum = 0x%8X\n", result); + if (result != 0) + goto END_FUNC; + + for (temp_addr = start_addr; temp_addr < (start_addr + flash_page_len); temp_addr = temp_addr + 128) { + + /* I("temp_addr=%d,tmp_addr[0]=0x%2X,tmp_addr[1]=0x%2X,tmp_addr[2]=0x%2X,tmp_addr[3]=0x%2X\n",temp_addr,tmp_addr[0],tmp_addr[1],tmp_addr[2],tmp_addr[3]); */ + tmp_addr[0] = temp_addr % 0x100; + tmp_addr[1] = (temp_addr >> 8) % 0x100; + tmp_addr[2] = (temp_addr >> 16) % 0x100; + tmp_addr[3] = temp_addr / 0x1000000; + g_core_fp.fp_register_read(tmp_addr, 128, &flash_tmp_buffer[temp_addr - start_addr], false); + /* memcpy(&flash_tmp_buffer[temp_addr - start_addr],buffer,128);*/ + } + +END_FUNC: + return result; +} + +static int hx_read_guest_info(void) +{ + /* uint8_t tmp_addr[4]; */ + uint32_t panel_color_addr = 0x10000;/*64k*/ + + uint32_t info_len; + uint32_t flash_page_len = 0x1000;/*4k*/ + uint8_t *flash_tmp_buffer = NULL; + /* uint32_t temp_addr = 0; */ + uint8_t temp_str[128]; + int i = 0; + int custom_info_temp = 0; + int checksum = 0; + + himax_guest_info_set_status(1); + + flash_tmp_buffer = kcalloc(HX_GUEST_INFO_SIZE * flash_page_len, sizeof(uint8_t), GFP_KERNEL); + if (flash_tmp_buffer == NULL) { + I("%s: Memory allocate fail!\n", __func__); + return MEM_ALLOC_FAIL; + } + + g_core_fp.fp_sense_off(true); + /* g_core_fp.fp_burst_enable(1); */ + + for (custom_info_temp = 0; custom_info_temp < HX_GUEST_INFO_SIZE; custom_info_temp++) { + checksum = himax_guest_info_read(panel_color_addr+custom_info_temp * flash_page_len, &flash_tmp_buffer[custom_info_temp * flash_page_len]); + if (checksum != 0) { + E("%s:Checksum Fail! g_checksum_str len=%d\n", __func__, (int)strlen(g_checksum_str)); + memcpy(&g_guest_info_data->g_guest_str_in_format[custom_info_temp][0], g_checksum_str, (int)strlen(g_checksum_str)); + memcpy(&g_guest_info_data->g_guest_str[custom_info_temp][0], g_checksum_str, (int)strlen(g_checksum_str)); + continue; + } + + info_len = flash_tmp_buffer[custom_info_temp * flash_page_len] + + (flash_tmp_buffer[custom_info_temp*flash_page_len + 1] << 8) + + (flash_tmp_buffer[custom_info_temp*flash_page_len + 2] << 16) + + (flash_tmp_buffer[custom_info_temp*flash_page_len + 3] << 24); + + I("Now custom_info_temp = %d\n", custom_info_temp); + + I("Now size_buff[0]=0x%02X,[1]=0x%02X,[2]=0x%02X,[3]=0x%02X\n" + , flash_tmp_buffer[custom_info_temp*flash_page_len] + , flash_tmp_buffer[custom_info_temp*flash_page_len + 1] + , flash_tmp_buffer[custom_info_temp*flash_page_len + 2] + , flash_tmp_buffer[custom_info_temp*flash_page_len + 3]); + + I("Now total length=%d\n", info_len); + + g_guest_info_data->g_guest_data_len[custom_info_temp] = info_len; + + I("Now custom_info_id [0]=%d,[1]=%d,[2]=%d,[3]=%d\n" + , flash_tmp_buffer[custom_info_temp*flash_page_len + 4] + , flash_tmp_buffer[custom_info_temp*flash_page_len + 5] + , flash_tmp_buffer[custom_info_temp*flash_page_len + 6] + , flash_tmp_buffer[custom_info_temp*flash_page_len + 7]); + + g_guest_info_data->g_guest_data_type[custom_info_temp] = flash_tmp_buffer[custom_info_temp * flash_page_len + 7]; + + /* if(custom_info_temp < 3) { */ + if (info_len > 128) { + I("%s: info_len=%d\n", __func__, info_len); + info_len = 128; + } + for (i = 0; i < info_len; i++) + temp_str[i] = flash_tmp_buffer[custom_info_temp * flash_page_len + HX_GUEST_INFO_LEN_SIZE + HX_GUEST_INFO_ID_SIZE + i]; + I("g_guest_info_data->g_guest_str_in_format[%d] size = %d\n", custom_info_temp, info_len); + memcpy(&g_guest_info_data->g_guest_str_in_format[custom_info_temp][0], temp_str, info_len); + /*}*/ + + for (i = 0; i < 128; i++) + temp_str[i] = flash_tmp_buffer[custom_info_temp * flash_page_len + i]; + + I("g_guest_info_data->g_guest_str[%d] size = %d\n", custom_info_temp, 128); + memcpy(&g_guest_info_data->g_guest_str[custom_info_temp][0], temp_str, 128); + /*if(custom_info_temp == 0) + *{ + * for( i = 0; i< 256 ; i++) + * { + * if(i % 16 == 0 && i > 0) + * I("\n"); + * I("g_guest_info_data->g_guest_str[%d][%d] = 0x%02X",custom_info_temp,i,g_guest_info_data->g_guest_str[custom_info_temp][i]); + * } + *} + */ + } + /* himax_burst_enable(private_ts->client, 0); */ + + g_core_fp.fp_sense_on(0x01); + + kfree(flash_tmp_buffer); + himax_guest_info_set_status(0); + return NO_ERR; +} +#endif +#endif + +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) +static void himax_mcu_resend_cmd_func(bool suspended) +{ +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) + struct himax_ts_data *ts = private_ts; +#endif +#ifdef HX_SMART_WAKEUP + g_core_fp.fp_set_SMWP_enable(ts->SMWP_enable, suspended); +#endif +#ifdef HX_EDGE_LIMIT + g_core_fp.fp_set_edge_limit_enable(ts->edge_limit_enable, suspended); +#endif +#ifdef HX_HIGH_SENSE + g_core_fp.fp_set_HSEN_enable(ts->HSEN_enable, suspended); +#endif +#ifdef HX_USB_DETECT_GLOBAL + himax_cable_detect_func(true); +#endif +} +#endif + +#ifdef HX_ZERO_FLASH +int G_POWERONOF = 1; +EXPORT_SYMBOL(G_POWERONOF); +void hx_dis_rload_0f(int disable) +{ + /*Diable Flash Reload*/ + g_core_fp.fp_register_write(pzf_op->addr_dis_flash_reload, DATA_LEN_4, pzf_op->data_dis_flash_reload, 0); +} + +void himax_mcu_clean_sram_0f(uint8_t *addr, int write_len, int type) +{ + int total_read_times = 0; + int max_bus_size = MAX_I2C_TRANS_SZ; + int total_size_temp = 0; + int total_size = 0; + int address = 0; + int i = 0; + + uint8_t fix_data = 0x00; + uint8_t tmp_addr[4]; + uint8_t tmp_data[MAX_I2C_TRANS_SZ] = {0}; + + I("%s, Entering\n", __func__); + + total_size = write_len; + +#ifdef __MTK_SPI__ + if (write_len > (HX_SPI_MTK_MAX_WRITE_SZ - 4)) + max_bus_size = (HX_SPI_MTK_MAX_WRITE_SZ - 4); + else + max_bus_size = write_len; +#else + if (total_size > PACKET_MAX_SZ - 8) + max_bus_size = PACKET_MAX_SZ - 8; +#endif + + total_size_temp = write_len; + + g_core_fp.fp_burst_enable(1); + + tmp_addr[3] = addr[3]; + tmp_addr[2] = addr[2]; + tmp_addr[1] = addr[1]; + tmp_addr[0] = addr[0]; + I("%s, write addr tmp_addr[3]=0x%2.2X, tmp_addr[2]=0x%2.2X, tmp_addr[1]=0x%2.2X, tmp_addr[0]=0x%2.2X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + + switch (type) { + case 0: + fix_data = 0x00; + break; + case 1: + fix_data = 0xAA; + break; + case 2: + fix_data = 0xBB; + break; + } + + for (i = 0; i < MAX_I2C_TRANS_SZ; i++) + tmp_data[i] = fix_data; + + I("%s, total size=%d\n", __func__, total_size); + + if (total_size_temp % max_bus_size == 0) + total_read_times = total_size_temp / max_bus_size; + else + total_read_times = total_size_temp / max_bus_size + 1; + + for (i = 0; i < (total_read_times); i++) { + I("[log]write %d time start!\n", i); + if (total_size_temp >= max_bus_size) { + g_core_fp.fp_register_write(tmp_addr, max_bus_size, tmp_data, 0); + total_size_temp = total_size_temp - max_bus_size; + } else { + I("last total_size_temp=%d\n", total_size_temp); + g_core_fp.fp_register_write(tmp_addr, total_size_temp % max_bus_size, tmp_data, 0); + } + address = ((i+1) * max_bus_size); + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF); + tmp_addr[0] = addr[0] + (uint8_t) ((address) & 0x00FF); + + usleep_range(10000, 11000); + } + + I("%s, END\n", __func__); +} + +void himax_mcu_write_sram_0f(const struct firmware *fw_entry, uint8_t *addr, int start_index, uint32_t write_len) +{ + int total_read_times = 0; + int max_bus_size = MAX_I2C_TRANS_SZ; + int total_size_temp = 0; + int address = 0; + int i = 0; + + uint8_t tmp_addr[4]; + uint8_t *tmp_data; + + total_size_temp = write_len; + I("%s, Entering - total write size=%d\n", __func__, total_size_temp); + +#if defined(HX_SPI_OPERATION) + if (write_len > PACKET_MAX_SZ - 8) + max_bus_size = PACKET_MAX_SZ - 8; + else + max_bus_size = write_len; +#else +#ifdef __MTK_SPI__ + if (write_len > (HX_SPI_MTK_MAX_WRITE_SZ - 4)) + max_bus_size = (HX_SPI_MTK_MAX_WRITE_SZ - 4); + else + max_bus_size = write_len; +#endif +#endif + + g_core_fp.fp_burst_enable(1); + + tmp_addr[3] = addr[3]; + tmp_addr[2] = addr[2]; + tmp_addr[1] = addr[1]; + tmp_addr[0] = addr[0]; + I("%s, write addr = 0x%02X%02X%02X%02X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + + /* tmp_data = kcalloc(max_bus_size, sizeof(uint8_t), GFP_KERNEL); + * if (tmp_data == NULL) { + * I("%s: Can't allocate enough buf\n", __func__); + * return; + * } + */ + + /*for(i = 0;i<10;i++) + *{ + * I("[%d] 0x%2.2X", i, tmp_data[i]); + *} + *I("\n"); + */ + if (total_size_temp % max_bus_size == 0) + total_read_times = total_size_temp / max_bus_size; + else + total_read_times = total_size_temp / max_bus_size + 1; + + for (i = 0; i < (total_read_times); i++) { + /*I("[log]write %d time start!\n", i);*/ + /*I("[log]addr[3]=0x%02X, addr[2]=0x%02X, addr[1]=0x%02X, addr[0]=0x%02X!\n", tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]);*/ + + if (total_size_temp >= max_bus_size) { + /*memcpy(tmp_data, &fw_entry->data[start_index+i * max_bus_size], max_bus_size);*/ + tmp_data = (uint8_t *)&fw_entry->data[start_index+i * max_bus_size]; + g_core_fp.fp_register_write(tmp_addr, max_bus_size, tmp_data, 0); + total_size_temp = total_size_temp - max_bus_size; + } else { + /*memcpy(tmp_data, &fw_entry->data[start_index+i * max_bus_size], total_size_temp % max_bus_size);*/ + tmp_data = (uint8_t *)&fw_entry->data[start_index+i * max_bus_size]; + I("last total_size_temp=%d\n", total_size_temp % max_bus_size); + g_core_fp.fp_register_write(tmp_addr, total_size_temp % max_bus_size, tmp_data, 0); + } + + /*I("[log]write %d time end!\n", i);*/ + address = ((i+1) * max_bus_size); + tmp_addr[0] = addr[0] + (uint8_t) ((address) & 0x00FF); + + if (tmp_addr[0] < addr[0]) + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF) + 1; + else + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF); + + udelay(100); + } + I("%s, End\n", __func__); + /* kfree(tmp_data); */ +} + +int himax_sram_write_crc_check(const struct firmware *fw_entry, uint8_t *addr, int strt_idx, uint32_t len) +{ + int retry = 0; + int crc = -1; + + do { + g_core_fp.fp_write_sram_0f(fw_entry, addr, strt_idx, len); + crc = g_core_fp.fp_check_CRC(addr, len); + retry++; + I("%s, HW CRC %s in %d time\n", __func__, (crc == 0)?"OK":"Fail", retry); + } while (crc != 0 && retry < 3); + + return crc; +} +#if defined(HX83102D) +int hx_parse_bin_cfg_data(const struct firmware *fw_entry, struct zf_info *zf_info_arr, uint8_t *FW_buf, uint8_t *sram_min) +{ + int part_num = 0; + int i = 0; + uint8_t buf[16]; + int i_max = 0; + int i_min = 0; + uint32_t dsram_base = 0xFFFFFFFF; + uint32_t dsram_max = 0; + int cfg_sz = 0; + +#if defined(HX_CODE_OVERLAY) + uint8_t j = 0; + int allovlidx = 0; + + if (ovl_idx == NULL) { + ovl_idx = kzalloc(ovl_section_num, GFP_KERNEL); + if (ovl_idx == NULL) { + E("%s, ovl_idx alloc failed!\n", + __func__); + return -ENOMEM; + } + } else { + memset(ovl_idx, 0, ovl_section_num); + } +#endif + /*1. get number of partition*/ + part_num = fw_entry->data[HX64K + 12]; + I("%s, Number of partition is %d\n", __func__, part_num); + if (part_num <= 1) { + E("%s, size of cfg part failed! part_num = %d\n", + __func__, part_num); + return LENGTH_FAIL; + } + + /* i = 0 is fw entity, it need to update by itself*/ + for (i = 1; i < part_num; i++) { + /*3. get all partition*/ + memcpy(buf, &fw_entry->data[i * 0x10 + HX64K], 16); + memcpy(zf_info_arr[i].sram_addr, buf, 4); + zf_info_arr[i].write_size = buf[7] << 24 + | buf[6] << 16 + | buf[5] << 8 + | buf[4]; + zf_info_arr[i].fw_addr = buf[11] << 24 + | buf[10] << 16 + | buf[9] << 8 + | buf[8]; + zf_info_arr[i].cfg_addr = zf_info_arr[i].sram_addr[0]; + zf_info_arr[i].cfg_addr += zf_info_arr[i].sram_addr[1] << 8; + zf_info_arr[i].cfg_addr += zf_info_arr[i].sram_addr[2] << 16; + zf_info_arr[i].cfg_addr += zf_info_arr[i].sram_addr[3] << 24; + +#if defined(HX_CODE_OVERLAY) + /*overlay section*/ + + if ((buf[15] == 0x55 && buf[14] == 0x66) + || (buf[3] == 0x20 && buf[2] == 0x00 + && buf[1] == 0x8C && buf[0] == 0xE0)) { + I("%s: catch overlay section in index %d\n", + __func__, i); + + /* record index of overlay section */ + allovlidx |= 1< zf_info_arr[i].cfg_addr) { + dsram_base = zf_info_arr[i].cfg_addr; + i_min = i; + } + if (dsram_max < zf_info_arr[i].cfg_addr) { + dsram_max = zf_info_arr[i].cfg_addr; + i_max = i; + } + } + for (i = 0; i < ADDR_LEN_4; i++) + sram_min[i] = zf_info_arr[i_min].sram_addr[i]; + + cfg_sz = (dsram_max - dsram_base) + zf_info_arr[i_max].write_size; + if (cfg_sz % 16 != 0) + cfg_sz = cfg_sz + 16 - (cfg_sz % 16); + + I("%s, cfg_sz = %d!, dsram_base = %X, dsram_max = %X\n", __func__, + cfg_sz, dsram_base, dsram_max); + if (cfg_sz <= FW_BIN_16K_SZ) + memset(FW_buf, 0x00, sizeof(uint8_t) * cfg_sz); + else + memset(FW_buf, 0x00, sizeof(uint8_t) * FW_BIN_16K_SZ); + for (i = 1; i < part_num; i++) { + if (zf_info_arr[i].cfg_addr % 4 != 0) + zf_info_arr[i].cfg_addr = zf_info_arr[i].cfg_addr + - (zf_info_arr[i].cfg_addr % 4); + + I("%s,[%d]SRAM addr=%08X,[%d]fw_addr=%08X,[%d]write_size=%d\n", + __func__, + i, zf_info_arr[i].cfg_addr, + i, zf_info_arr[i].fw_addr, + i, zf_info_arr[i].write_size); + +#if defined(HX_CODE_OVERLAY) + /*overlay section*/ + if (allovlidx & (1<data[zf_info_arr[i].fw_addr], + zf_info_arr[i].write_size); + } + /* debug info */ + /* for (i = 1; i < part_num; i++) { + * different = 0; + * I("--cfg_addr = 0x%08X\n",zf_info_arr[i].cfg_addr); + * I("--bin_addr = 0x%08X\n",zf_info_arr[i].fw_addr); + * I("--write size = %d\n",zf_info_arr[i].write_size); + * for ( j = 0; j < zf_info_arr[i].write_size; j++) { + * if(FW_buf[zf_info_arr[i].cfg_addr - dsram_base + j] != + fw_entry->data[zf_info_arr[i].fw_addr + j]) + * different++; + * } + * I("--There are %d different\n", different); + * } + */ + + return cfg_sz; +} +#else +int hx_parse_bin_cfg_data(const struct firmware *fw_entry, struct zf_info *zf_info_arr, uint8_t *FW_buf, uint8_t *sram_min, uint8_t *ovl_sidx) +{ + bool flag_1k_header = false; + int part_num = 0; + int i = 0; + uint8_t buf[16]; + int i_max = 0; + int i_min = 0; + uint32_t dsram_base = 0xFFFFFFFF; + uint32_t dsram_max = 0; + int cfg_sz = 0; + /*int different = 0;*/ + /*int j = 0;*/ + + /*0. check 1k header*/ + if (fw_entry->data[0x00] == 0x00 + && fw_entry->data[0x01] == 0x00 + && fw_entry->data[0x02] == 0x00 + && fw_entry->data[0x03] == 0x00 + && fw_entry->data[0x04] == 0x00 + && fw_entry->data[0x05] == 0x00 + && fw_entry->data[0x06] == 0x00 + && fw_entry->data[0x07] == 0x00 + && fw_entry->data[0x0E] == 0x87) + flag_1k_header = true; + else + flag_1k_header = false; + + /*1. get number of partition*/ + if(flag_1k_header == true) + part_num = fw_entry->data[HX64K + HX1K + 12]; + else + part_num = fw_entry->data[HX64K + 12]; + + //I("%s, Number of partition is %d\n", __func__, part_num); + I("%s, Number of partition is %d, flag_1k_header=%d\n", __func__, part_num, flag_1k_header); + if (part_num <= 1) { + E("%s, size of cfg part failed! part_num = %d\n", __func__, part_num); + return LENGTH_FAIL; + } + /*2. check struct of array*/ + if (zf_info_arr == NULL || FW_buf == NULL) { + E("%s, Struct is NULL, %p,%p\n", __func__, zf_info_arr, FW_buf); + return MEM_ALLOC_FAIL; + } + + /* i = 0 is fw entity, it need to update by itself*/ + for (i = 1; i < part_num; i++) { + /*3. get all partition*/ + if(flag_1k_header == true) + memcpy(buf, &fw_entry->data[i * 0x10 + HX64K + HX1K], 16); + else + memcpy(buf, &fw_entry->data[i * 0x10 + HX64K], 16); + + memcpy(zf_info_arr[i].sram_addr, buf, 4); + zf_info_arr[i].write_size = buf[7] << 24 | buf[6] << 16 | buf[5] << 8 | buf[4]; + zf_info_arr[i].fw_addr = buf[11] << 24 | buf[10] << 16 | buf[9] << 8 | buf[8]; + zf_info_arr[i].cfg_addr = zf_info_arr[i].sram_addr[0]; + zf_info_arr[i].cfg_addr += zf_info_arr[i].sram_addr[1] << 8; + zf_info_arr[i].cfg_addr += zf_info_arr[i].sram_addr[2] << 16; + zf_info_arr[i].cfg_addr += zf_info_arr[i].sram_addr[3] << 24; + +#ifdef HX_CODE_OVERLAY + /*overlay section*/ + if (zf_info_arr[i].cfg_addr == ovl_isram_addr) { + if (*ovl_sidx == 0) + *ovl_sidx = i; + + continue; + } +#endif + + if (dsram_base > zf_info_arr[i].cfg_addr) { + dsram_base = zf_info_arr[i].cfg_addr; + i_min = i; + } + if (dsram_max < zf_info_arr[i].cfg_addr) { + dsram_max = zf_info_arr[i].cfg_addr; + i_max = i; + } + } + for (i = 0; i < ADDR_LEN_4; i++) + sram_min[i] = zf_info_arr[i_min].sram_addr[i]; + cfg_sz = (dsram_max - dsram_base) + zf_info_arr[i_max].write_size; + if (cfg_sz % 16 != 0) + cfg_sz = cfg_sz + 16 - (cfg_sz % 16); + + I("%s, cfg_sz = %d!, dsram_base = %X, dsram_max = %X\n", __func__, cfg_sz, dsram_base, dsram_max); + + memset(FW_buf, 0x00, sizeof(uint8_t) * cfg_sz); + for (i = 1; i < part_num; i++) { + if (zf_info_arr[i].cfg_addr % 4 != 0) + zf_info_arr[i].cfg_addr = zf_info_arr[i].cfg_addr - (zf_info_arr[i].cfg_addr % 4); + + I("%s,[%d] SRAM addr = %08X\n", __func__, i, zf_info_arr[i].cfg_addr); + I("%s,[%d] fw_addr = %08X\n", __func__, i, zf_info_arr[i].fw_addr); + I("%s,[%d] write_size = %d\n", __func__, i, zf_info_arr[i].write_size); + +#ifdef HX_CODE_OVERLAY + /*overlay section*/ + if (zf_info_arr[i].cfg_addr == ovl_isram_addr) + continue; +#endif + + memcpy(&FW_buf[zf_info_arr[i].cfg_addr - dsram_base], &fw_entry->data[zf_info_arr[i].fw_addr], zf_info_arr[i].write_size); + } + /* debug info */ + /* for (i = 1; i < part_num; i++) { + * different = 0; + * I("--cfg_addr = 0x%08X\n",zf_info_arr[i].cfg_addr); + * I("--bin_addr = 0x%08X\n",zf_info_arr[i].fw_addr); + * I("--write size = %d\n",zf_info_arr[i].write_size); + * for ( j = 0; j < zf_info_arr[i].write_size; j++) { + * if(FW_buf[zf_info_arr[i].cfg_addr - dsram_base + j] != fw_entry->data[zf_info_arr[i].fw_addr + j]) + * different++; + * } + * I("--There are %d different\n", different); + * } + */ + + return cfg_sz; +} +#endif + +int himax_zf_part_info(const struct firmware *fw_entry) +{ + bool flag_1k_header = false; + int part_num = fw_entry->data[HX64K + 12]; + int ret = 0; + uint8_t buf[16]; +#ifdef FIX_WVLA + struct zf_info *zf_info_arr; +#else + struct zf_info zf_info_arr[part_num]; +#endif + uint8_t *FW_buf; + uint8_t sram_min[4]; + int cfg_crc_sw = 0; + int cfg_crc_hw = 0; + int cfg_sz = 0; + int retry = 3; + + uint8_t ovl_sidx = 0; +#if defined(HX83102D) + uint8_t ovl_idx_t; +#if defined(HX_SMART_WAKEUP) || defined(HX_CODE_OVERLAY) + uint8_t tmp_addr[4] = {0xFC, 0x7F, 0x00, 0x10}; + uint8_t send_data[4] = {0}; + uint8_t recv_data[4] = {0}; +#endif +#else +#ifdef HX_CODE_OVERLAY +#ifdef HX_SMART_WAKEUP + uint8_t tmp_addr[4] = {0xFC, 0x7F, 0x00, 0x10}; + uint8_t send_data[4] = {0}; + uint8_t recv_data[4] = {0}; +#endif +#endif +#endif + /*0. check 1k header*/ + if (fw_entry->data[0x00] == 0x00 + && fw_entry->data[0x01] == 0x00 + && fw_entry->data[0x02] == 0x00 + && fw_entry->data[0x03] == 0x00 + && fw_entry->data[0x04] == 0x00 + && fw_entry->data[0x05] == 0x00 + && fw_entry->data[0x06] == 0x00 + && fw_entry->data[0x07] == 0x00 + && fw_entry->data[0x0E] == 0x87) + flag_1k_header = true; + else + flag_1k_header = false; + + /*1. get number of partition*/ + if(flag_1k_header == true) + part_num = fw_entry->data[HX64K + HX1K + 12]; + else + part_num = fw_entry->data[HX64K + 12]; + + /*1. get number of partition*/ + /*part_num = fw_entry->data[HX64K + 12];*/ + //I("%s, Number of partition is %d\n", __func__, part_num); + I("%s, Number of partition is %d, flag_1k_header=%d\n", __func__, part_num, flag_1k_header); + if (part_num <= 0) + part_num = 1; + + /*2. initial struct of array*/ +#ifdef FIX_WVLA + zf_info_arr = kcalloc(part_num, sizeof(struct zf_info), GFP_KERNEL); +#endif + if (zf_info_arr == NULL) { + E("%s, Allocate ZF info array failed!\n", __func__); + ret = MEM_ALLOC_FAIL; + goto ALOC_ZF_INFO_ARR_FAIL; + } + memset(zf_info_arr, 0x0, part_num * sizeof(struct zf_info)); + FW_buf = kcalloc(FW_BIN_16K_SZ, sizeof(uint8_t), GFP_KERNEL); + if (FW_buf == NULL) { + E("%s, Allocate FW_buf array failed!\n", __func__); + ret = MEM_ALLOC_FAIL; + goto ALOC_FW_BUF_FAIL; + } + + /*3. Find the data in the bin file */ + /*CFG*/ +#if defined(HX83102D) + cfg_sz = hx_parse_bin_cfg_data(fw_entry, zf_info_arr, FW_buf, sram_min); +#else + cfg_sz = hx_parse_bin_cfg_data(fw_entry, zf_info_arr, FW_buf, sram_min, &ovl_sidx); +#endif + I("Now cfg_sz=%d\n", cfg_sz); + if (cfg_sz < 0) { + ret = LENGTH_FAIL; + goto END_FUNC; + } else { + cfg_crc_sw = g_core_fp.fp_Calculate_CRC_with_AP(FW_buf, 0, cfg_sz); + I("Now cfg_crc_sw=%X\n", cfg_crc_sw); + } + /*Fw Entity*/ + if(flag_1k_header == true) + memcpy(buf, &fw_entry->data[0 * 0x10 + HX64K + HX1K], 16); + else + memcpy(buf, &fw_entry->data[0 * 0x10 + HX64K], 16); + memcpy(zf_info_arr[0].sram_addr, buf, 4); + zf_info_arr[0].write_size = buf[7] << 24 | buf[6] << 16 | buf[5] << 8 | buf[4]; + zf_info_arr[0].fw_addr = buf[11] << 24 | buf[10] << 16 | buf[9] << 8 | buf[8]; + + /* 4. write to sram*/ + if (G_POWERONOF == 1) { + /* FW entity */ + if (himax_sram_write_crc_check(fw_entry, zf_info_arr[0].sram_addr, zf_info_arr[0].fw_addr, zf_info_arr[0].write_size) != 0) + E("%s, HW CRC FAIL\n", __func__); + else + I("%s, HW CRC PASS\n", __func__); + + I("Now sram_min[0]=0x%02X,[1]=0x%02X,[2]=0x%02X,[3]=0x%02X\n", sram_min[0], sram_min[1], sram_min[2], sram_min[3]); + do { + g_core_fp.fp_register_write(sram_min, cfg_sz, FW_buf, 0); + cfg_crc_hw = g_core_fp.fp_check_CRC(sram_min, cfg_sz); + if (cfg_crc_hw != cfg_crc_sw) + E("Config CRC FAIL, HW CRC = %X,SW CRC = %X, retry = %d\n", cfg_crc_hw, cfg_crc_sw, retry); + else + I("Config CRC Pass\n"); + + } while (cfg_crc_hw != cfg_crc_sw && retry-- > 0); + +#ifdef HX_CODE_OVERLAY +#if defined(HX83102D) + /* ovl_idx[0] - sorting */ + /* ovl_idx[1] - gesture */ + /* ovl_idx[2] - border */ + + ovl_idx_t = ovl_idx[0]; + send_data[0] = ovl_sorting_reply; + + if (private_ts->in_self_test == 0) { +#if defined(HX_SMART_WAKEUP) + if (private_ts->suspended && private_ts->SMWP_enable) { + ovl_idx_t = ovl_idx[1]; + send_data[0] = ovl_gesture_reply; + } else { + ovl_idx_t = ovl_idx[2]; + send_data[0] = ovl_border_reply; + } +#else + ovl_idx_t = ovl_idx[2]; + send_data[0] = ovl_border_reply; +#endif +#else + /*do nothing when self testing ??*/ + if (private_ts->in_self_test == 0) { +#ifdef HX_SMART_WAKEUP + /* ovl_sidx - mpap */ + /* ovl_sidx+1 - gesture */ + /* ovl_sidx+2 - border */ + if (private_ts->suspended && private_ts->SMWP_enable) + ovl_sidx = ovl_sidx + 1; + else + ovl_sidx = ovl_sidx + 2; +#else + ovl_sidx = ovl_sidx + 2; +#endif +#endif + + if (himax_sram_write_crc_check(fw_entry, zf_info_arr[ovl_sidx].sram_addr, zf_info_arr[ovl_sidx].fw_addr, zf_info_arr[ovl_sidx].write_size) != 0) + E("%s, Overlay HW CRC FAIL\n", __func__); + else + I("%s, Overlay HW CRC PASS\n", __func__); + +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) + /*write back system config*/ + g_core_fp.fp_resend_cmd_func(private_ts->suspended); +#endif + +#if defined(HX83102D) + } + I("%s: prepare upgrade overlay section = %d\n", + __func__, ovl_idx_t); + + if (zf_info_arr[ovl_idx_t].write_size == 0) { + send_data[0] = ovl_fault; + E("%s, WRONG overlay section, plese check FW!\n", + __func__); + } else { + if (himax_sram_write_crc_check(fw_entry, + zf_info_arr[ovl_idx_t].sram_addr, + zf_info_arr[ovl_idx_t].fw_addr, + zf_info_arr[ovl_idx_t].write_size) != 0) { + send_data[0] = ovl_fault; + E("%s, Overlay HW CRC FAIL\n", __func__); + } else { + I("%s, Overlay HW CRC PASS\n", __func__); + } + } + + retry = 0; + + do { + g_core_fp.fp_register_write(tmp_addr, DATA_LEN_4, send_data, 0); + g_core_fp.fp_register_read(tmp_addr, DATA_LEN_4, recv_data, 0); + retry++; + } while ((send_data[3] != recv_data[3] || send_data[2] != recv_data[2] || send_data[1] != recv_data[1] || send_data[0] != recv_data[0]) && retry < HIMAX_REG_RETRY_TIMES); +#else +#if defined(HX_SMART_WAKEUP) + /* write overlay handshaking password */ + if (private_ts->SMWP_enable) { + if (private_ts->suspended) + send_data[0] = ovl_gesture_reply; + else + send_data[0] = ovl_border_reply; + + retry = 0; + + do { + g_core_fp.fp_register_write(tmp_addr, DATA_LEN_4, send_data, 0); + g_core_fp.fp_register_read(tmp_addr, DATA_LEN_4, recv_data, 0); + retry++; + } while ((send_data[3] != recv_data[3] || send_data[2] != recv_data[2] || send_data[1] != recv_data[1] || send_data[0] != recv_data[0]) && retry < HIMAX_REG_RETRY_TIMES); + } +#endif + } +#endif +#endif + } else { + g_core_fp.fp_clean_sram_0f(zf_info_arr[0].sram_addr, zf_info_arr[0].write_size, 2); + } + +END_FUNC: + kfree(FW_buf); +ALOC_FW_BUF_FAIL: +#ifdef FIX_WVLA + kfree(zf_info_arr); +#endif +ALOC_ZF_INFO_ARR_FAIL: + return ret; +} + +void himax_mcu_firmware_update_0f(const struct firmware *fw_entry) +{ + int ret = 0; + uint8_t tmp_data[DATA_LEN_4] = {0x01, 0x00, 0x00, 0x00}; + + I("%s,Entering - total FW size=%d\n", __func__, (int)fw_entry->size); + + g_core_fp.fp_register_write(pzf_op->addr_system_reset, 4, pzf_op->data_system_reset, 0); + + g_core_fp.fp_sense_off(false); + + if ((int)fw_entry->size > HX64K) { + ret = himax_zf_part_info(fw_entry); + } else { + /* first 48K */ + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_sram_start_addr, 0, HX_48K_SZ) != 0) + E("%s, HW CRC FAIL - Main SRAM 48K\n", __func__); + + /*config info*/ + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_cfg_info, 0xC000, 128) != 0) + E("Config info CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_cfg_info, 128, 2); + } + + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_fw_cfg_1, 0xC0FE, 528) != 0) + E("FW config 1 CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_fw_cfg_1, 528, 1); + } + + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_fw_cfg_3, 0xCA00, 128) != 0) + E("FW config 3 CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_fw_cfg_3, 128, 2); + } + + /*ADC config*/ + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_adc_cfg_1, 0xD640, 1200) != 0) + E("ADC config 1 CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_adc_cfg_1, 1200, 2); + } + + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_adc_cfg_2, 0xD320, 800) != 0) + E("ADC config 2 CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_adc_cfg_2, 800, 2); + } + + /*mapping table*/ + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_map_table, 0xE000, 1536) != 0) + E("Mapping table CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_map_table, 1536, 2); + } + } + + /* set n frame=0*/ + /*if (G_POWERONOF == 1) + * g_core_fp.fp_write_sram_0f(fw_entry, pzf_op->data_mode_switch, 0xC30C, 4); + *else + * g_core_fp.fp_clean_sram_0f(pzf_op->data_mode_switch, 4, 2); + */ + g_core_fp.fp_register_write(pfw_op->addr_set_frame_addr, 4, tmp_data, 0); + + I("%s, End\n", __func__); +} + +int hx_0f_op_file_dirly(char *file_name) +{ + int err = NO_ERR, ret; + const struct firmware *fw_entry = NULL; + + + I("%s, Entering\n", __func__); + I("file name = %s\n", file_name); + ret = request_firmware(&fw_entry, file_name, private_ts->dev); + if (ret < 0) { +#if defined(__EMBEDDED_FW__) + fw_entry = &g_embedded_fw; + I("%s: Couldn't find userspace FW, use embedded FW(size:%zu) instead.\n", __func__, g_embedded_fw.size); +#else + E("%s, fail in line%d error code=%d,file maybe fail\n", __func__, __LINE__, ret); + return ret; +#endif + } + + himax_int_enable(0); + + if (g_f_0f_updat == 1) { + I("%s:[Warning]Other thread is updating now!\n", __func__); + err = -1; + goto END; + } else { + I("%s:Entering Update Flow!\n", __func__); + g_f_0f_updat = 1; + } + + g_core_fp.fp_firmware_update_0f(fw_entry); + if (ret >= 0) + release_firmware(fw_entry); + + g_f_0f_updat = 0; +END: + I("%s, END\n", __func__); + return err; +} + +int himax_mcu_0f_operation_dirly(void) +{ + int err = NO_ERR, ret; + const struct firmware *fw_entry = NULL; + + I("%s, Entering\n", __func__); + I("file name = %s\n", i_CTPM_firmware_name); + ret = request_firmware(&fw_entry, i_CTPM_firmware_name, private_ts->dev); + if (ret < 0) { +#if defined(__EMBEDDED_FW__) + fw_entry = &g_embedded_fw; + I("%s: Couldn't find userspace FW, use embedded FW(size:%zu) instead.\n", __func__, g_embedded_fw.size); +#else + E("%s, fail in line%d error code=%d,file maybe fail\n", __func__, __LINE__, ret); + return ret; +#endif + } + + himax_int_enable(0); + + if (g_f_0f_updat == 1) { + I("%s:[Warning]Other thread is updating now!\n", __func__); + err = -1; + goto END; + } else { + I("%s:Entering Update Flow!\n", __func__); + g_f_0f_updat = 1; + } + + g_core_fp.fp_firmware_update_0f(fw_entry); + if (ret >= 0) + release_firmware(fw_entry); + + g_f_0f_updat = 0; +END: + I("%s, END\n", __func__); + return err; +} + +void himax_mcu_0f_operation(struct work_struct *work) +{ + int err = NO_ERR; + const struct firmware *fw_entry = NULL; + + I("%s, Entering\n", __func__); + I("file name = %s\n", i_CTPM_firmware_name); + err = request_firmware(&fw_entry, i_CTPM_firmware_name, private_ts->dev); + if (err < 0) { +#if defined(__EMBEDDED_FW__) + fw_entry = &g_embedded_fw; + I("%s: Couldn't find userspace FW, use embedded FW(size:%zu) instead.\n", __func__, g_embedded_fw.size); +#else + E("%s, fail in line%d error code=%d,file maybe fail\n", __func__, __LINE__, err); + goto END; +#endif + } + + if (g_f_0f_updat == 1) { + I("%s:[Warning]Other thread is updating now!\n", __func__); + goto END; + } else { + I("%s:Entering Update Flow!\n", __func__); + g_f_0f_updat = 1; + } + + himax_int_enable(0); + + g_core_fp.fp_firmware_update_0f(fw_entry); + if (err >= 0) + release_firmware(fw_entry); + + g_core_fp.fp_reload_disable(0); + g_core_fp.fp_read_FW_ver(); + /*msleep (10);*/ + g_core_fp.fp_touch_information(); + /*msleep (10);*/ + g_core_fp.fp_sense_on(0x00); + /*msleep (10);*/ + I("%s:End\n", __func__); + himax_int_enable(1); + + g_f_0f_updat = 0; +END: + I("%s, END\n", __func__); +} + +static int himax_mcu_0f_esd_check(void) +{ + return NO_ERR; +} + + +#ifdef HX_0F_DEBUG +void himax_mcu_read_sram_0f(const struct firmware *fw_entry, uint8_t *addr, int start_index, int read_len) +{ + int total_read_times = 0; + int max_bus_size = MAX_I2C_TRANS_SZ; + int total_size_temp = 0; + int total_size = 0; + int address = 0; + int i = 0, j = 0; + int not_same = 0; + + uint8_t tmp_addr[4]; + uint8_t *temp_info_data; + int *not_same_buff; + + I("%s, Entering\n", __func__); + + g_core_fp.fp_burst_enable(1); + + total_size = read_len; + + total_size_temp = read_len; + +#if defined(HX_SPI_OPERATION) + if (read_len > 2048) + max_bus_size = 2048; + else + max_bus_size = read_len; +#else + if (read_len > 240) + max_bus_size = 240; + else + max_bus_size = read_len; +#endif + + + temp_info_data = kcalloc(total_size, sizeof(uint8_t), GFP_KERNEL); + not_same_buff = kcalloc(total_size, sizeof(int), GFP_KERNEL); + + + tmp_addr[3] = addr[3]; + tmp_addr[2] = addr[2]; + tmp_addr[1] = addr[1]; + tmp_addr[0] = addr[0]; + I("%s, read addr tmp_addr[3]=0x%2.2X, tmp_addr[2]=0x%2.2X, tmp_addr[1]=0x%2.2X, tmp_addr[0]=0x%2.2X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + + I("%s, total size=%d\n", __func__, total_size); + + g_core_fp.fp_burst_enable(1); + + if (total_size % max_bus_size == 0) + total_read_times = total_size / max_bus_size; + else + total_read_times = total_size / max_bus_size + 1; + + for (i = 0; i < (total_read_times); i++) { + if (total_size_temp >= max_bus_size) { + g_core_fp.fp_register_read(tmp_addr, max_bus_size, &temp_info_data[i*max_bus_size], false); + total_size_temp = total_size_temp - max_bus_size; + } else { + g_core_fp.fp_register_read(tmp_addr, total_size_temp % max_bus_size, &temp_info_data[i*max_bus_size], false); + } + + address = ((i+1) * max_bus_size); + tmp_addr[0] = addr[0] + (uint8_t) ((address) & 0x00FF); + if (tmp_addr[0] < addr[0]) + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF) + 1; + else + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF); + + /*msleep (10);*/ + } + I("%s, READ Start\n", __func__); + I("%s, start_index = %d\n", __func__, start_index); + j = start_index; + for (i = 0; i < read_len; i++, j++) { + if (fw_entry->data[j] != temp_info_data[i]) { + not_same++; + not_same_buff[i] = 1; + } + + I("0x%2.2X, ", temp_info_data[i]); + + if (i > 0 && i%16 == 15) + pr_info("\n"); + + } + I("%s, READ END\n", __func__); + I("%s, Not Same count=%d\n", __func__, not_same); + if (not_same != 0) { + j = start_index; + for (i = 0; i < read_len; i++, j++) { + if (not_same_buff[i] == 1) + I("bin = [%d] 0x%2.2X\n", i, fw_entry->data[j]); + } + for (i = 0; i < read_len; i++, j++) { + if (not_same_buff[i] == 1) + I("sram = [%d] 0x%2.2X\n", i, temp_info_data[i]); + } + } + I("%s, READ END\n", __func__); + I("%s, Not Same count=%d\n", __func__, not_same); + I("%s, END\n", __func__); + + kfree(not_same_buff); + kfree(temp_info_data); +} + +void himax_mcu_read_all_sram(uint8_t *addr, int read_len) +{ + int total_read_times = 0; + int max_bus_size = MAX_I2C_TRANS_SZ; + int total_size_temp = 0; + int total_size = 0; + int address = 0; + int i = 0; + /* struct file *fn; */ + /* struct filename *vts_name; */ + + uint8_t tmp_addr[4]; + uint8_t *temp_info_data; + + I("%s, Entering\n", __func__); + + g_core_fp.fp_burst_enable(1); + + total_size = read_len; + + total_size_temp = read_len; + + temp_info_data = kcalloc(total_size, sizeof(uint8_t), GFP_KERNEL); + + + tmp_addr[3] = addr[3]; + tmp_addr[2] = addr[2]; + tmp_addr[1] = addr[1]; + tmp_addr[0] = addr[0]; + I("%s, read addr tmp_addr[3]=0x%2.2X, tmp_addr[2]=0x%2.2X, tmp_addr[1]=0x%2.2X, tmp_addr[0]=0x%2.2X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + + I("%s, total size=%d\n", __func__, total_size); + + if (total_size % max_bus_size == 0) + total_read_times = total_size / max_bus_size; + else + total_read_times = total_size / max_bus_size + 1; + + for (i = 0; i < (total_read_times); i++) { + if (total_size_temp >= max_bus_size) { + g_core_fp.fp_register_read(tmp_addr, max_bus_size, &temp_info_data[i*max_bus_size], false); + total_size_temp = total_size_temp - max_bus_size; + } else { + g_core_fp.fp_register_read(tmp_addr, total_size_temp % max_bus_size, &temp_info_data[i*max_bus_size], false); + } + + address = ((i+1) * max_bus_size); + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF); + tmp_addr[0] = addr[0] + (uint8_t) ((address) & 0x00FF); + + /*msleep (10);*/ + } + I("%s, NOW addr tmp_addr[3]=0x%2.2X, tmp_addr[2]=0x%2.2X, tmp_addr[1]=0x%2.2X, tmp_addr[0]=0x%2.2X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + /*for(i = 0;i 0 && i%16 == 15) + * printk("\n"); + *} + */ + + /* need modify + * I("Now Write File start!\n"); + * vts_name = kp_getname_kernel("/sdcard/dump_dsram.txt"); + * fn = kp_file_open_name(vts_name, O_CREAT | O_WRONLY, 0); + * if (!IS_ERR (fn)) { + * I("%s create file and ready to write\n", __func__); + * fn->f_op->write (fn, temp_info_data, read_len*sizeof (uint8_t), &fn->f_pos); + * filp_close (fn, NULL); + * } + * I("Now Write File End!\n"); + */ + + I("%s, END\n", __func__); + + kfree(temp_info_data); +} + +void himax_mcu_firmware_read_0f(const struct firmware *fw_entry, int type) +{ + uint8_t tmp_addr[4]; + + I("%s, Entering\n", __func__); + if (type == 0) { /* first 48K */ + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_sram_start_addr, 0, HX_48K_SZ); + g_core_fp.fp_read_all_sram(tmp_addr, 0xC000); + } else { /*last 16k*/ + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_cfg_info, 0xC000, 132); + + /*FW config*/ + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_fw_cfg_1, 0xC0FE, 484); + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_fw_cfg_2, 0xC9DE, 36); + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_fw_cfg_3, 0xCA00, 72); + + /*ADC config*/ + + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_adc_cfg_1, 0xD630, 1188); + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_adc_cfg_2, 0xD318, 792); + + + /*mapping table*/ + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_map_table, 0xE000, 1536); + + /* set n frame=0*/ + g_core_fp.fp_read_sram_0f(fw_entry, pfw_op->addr_set_frame_addr, 0xC30C, 4); + } + + I("%s, END\n", __func__); +} + +void himax_mcu_0f_operation_check(int type) +{ + int err = NO_ERR; + const struct firmware *fw_entry = NULL; + /* char *firmware_name = "himax.bin"; */ + + + I("%s, Entering\n", __func__); + I("file name = %s\n", i_CTPM_firmware_name); + + + err = request_firmware(&fw_entry, i_CTPM_firmware_name, private_ts->dev); + if (err < 0) { +#if defined(__EMBEDDED_FW__) + fw_entry = &g_embedded_fw; + I("%s: Couldn't find userspace FW, use embedded FW(size:%zu) instead.\n", __func__, g_embedded_fw.size); +#else + E("%s, fail in line%d error code=%d\n", __func__, __LINE__, err); + return; +#endif + } + + I("first 4 bytes 0x%2X, 0x%2X, 0x%2X, 0x%2X !\n", fw_entry->data[0], fw_entry->data[1], fw_entry->data[2], fw_entry->data[3]); + I("next 4 bytes 0x%2X, 0x%2X, 0x%2X, 0x%2X !\n", fw_entry->data[4], fw_entry->data[5], fw_entry->data[6], fw_entry->data[7]); + I("and next 4 bytes 0x%2X, 0x%2X, 0x%2X, 0x%2X !\n", fw_entry->data[8], fw_entry->data[9], fw_entry->data[10], fw_entry->data[11]); + + g_core_fp.fp_firmware_read_0f(fw_entry, type); + + if (err >= 0) + release_firmware(fw_entry); + I("%s, END\n", __func__); +} +#endif + +#ifdef HX_CODE_OVERLAY +int himax_mcu_0f_overlay(int ovl_type, int mode) +{ + return NO_ERR; +} +#endif + +#endif + + +#ifdef HIMAX_PALM_SENSOR_EN +int himax_mcu_palm_detection(int mode) +{ + uint8_t data_byte[DATA_LEN_4]; + uint8_t tmp_addr[4]; + I("%s: set palm detection mode to %d", __func__, mode); + if (mode) + himax_in_parse_assign_cmd(palm_detection_register_enable, data_byte, 4); + else + himax_in_parse_assign_cmd(palm_detection_register_disable, data_byte, 4); + + g_core_fp.fp_register_write(pfw_op->addr_palm_detection_enable, DATA_LEN_4, data_byte, 0); + g_core_fp.fp_register_read(pfw_op->addr_palm_detection_enable, DATA_LEN_4, tmp_addr, 0); + I("%s: reg=0x%02X%02X%02X%02X.", __func__, tmp_addr[0],tmp_addr[1],tmp_addr[2],tmp_addr[3]); + + return 0; +} +#endif + +#ifdef CORE_INIT +/* init start */ +static void himax_mcu_fp_init(void) +{ +#ifdef CORE_IC + g_core_fp.fp_burst_enable = himax_mcu_burst_enable; + g_core_fp.fp_register_read = himax_mcu_register_read; + /*g_core_fp.fp_flash_write_burst = himax_mcu_flash_write_burst;*/ + /*g_core_fp.fp_flash_write_burst_lenth = himax_mcu_flash_write_burst_lenth;*/ + g_core_fp.fp_register_write = himax_mcu_register_write; + g_core_fp.fp_interface_on = himax_mcu_interface_on; + g_core_fp.fp_sense_on = himax_mcu_sense_on; + g_core_fp.fp_sense_off = himax_mcu_sense_off; + g_core_fp.fp_wait_wip = himax_mcu_wait_wip; + g_core_fp.fp_init_psl = himax_mcu_init_psl; + g_core_fp.fp_resume_ic_action = himax_mcu_resume_ic_action; + g_core_fp.fp_suspend_ic_action = himax_mcu_suspend_ic_action; + g_core_fp.fp_power_on_init = himax_mcu_power_on_init; + g_core_fp.fp_dd_clk_set = himax_mcu_dd_clk_set; + g_core_fp.fp_dd_reg_en = himax_mcu_dd_reg_en; + g_core_fp.fp_ic_id_read = himax_mcu_ic_id_read; + g_core_fp.fp_dd_reg_write = himax_mcu_dd_reg_write; + g_core_fp.fp_dd_reg_read = himax_mcu_dd_reg_read; +#endif +#ifdef CORE_FW + g_core_fp.fp_system_reset = himax_mcu_system_reset; + g_core_fp.fp_Calculate_CRC_with_AP = himax_mcu_Calculate_CRC_with_AP; + g_core_fp.fp_check_CRC = himax_mcu_check_CRC; + g_core_fp.fp_set_reload_cmd = himax_mcu_set_reload_cmd; + g_core_fp.fp_program_reload = himax_mcu_program_reload; +#ifdef HX_P_SENSOR + g_core_fp.fp_ulpm_in = himax_mcu_ulpm_in; + g_core_fp.fp_black_gest_ctrl = himax_mcu_black_gest_ctrl; +#endif + g_core_fp.fp_set_SMWP_enable = himax_mcu_set_SMWP_enable; +#ifdef HX_EDGE_LIMIT + g_core_fp.fp_set_edge_limit_enable = himax_mcu_set_edge_limit_enable; +#endif + g_core_fp.fp_set_HSEN_enable = himax_mcu_set_HSEN_enable; + g_core_fp.fp_usb_detect_set = himax_mcu_usb_detect_set; + g_core_fp.fp_diag_register_set = himax_mcu_diag_register_set; + g_core_fp.fp_chip_self_test = himax_mcu_chip_self_test; + g_core_fp.fp_idle_mode = himax_mcu_idle_mode; + g_core_fp.fp_reload_disable = himax_mcu_reload_disable; + g_core_fp.fp_check_chip_version = himax_mcu_check_chip_version; + g_core_fp.fp_read_ic_trigger_type = himax_mcu_read_ic_trigger_type; + g_core_fp.fp_read_i2c_status = himax_mcu_read_i2c_status; + g_core_fp.fp_read_FW_ver = himax_mcu_read_FW_ver; + g_core_fp.fp_read_event_stack = himax_mcu_read_event_stack; + g_core_fp.fp_return_event_stack = himax_mcu_return_event_stack; + g_core_fp.fp_calculateChecksum = himax_mcu_calculateChecksum; + g_core_fp.fp_read_FW_status = himax_mcu_read_FW_status; + g_core_fp.fp_irq_switch = himax_mcu_irq_switch; + g_core_fp.fp_assign_sorting_mode = himax_mcu_assign_sorting_mode; + g_core_fp.fp_check_sorting_mode = himax_mcu_check_sorting_mode; + g_core_fp.fp_switch_mode = himax_mcu_switch_mode; + g_core_fp.fp_get_max_dc = himax_mcu_get_max_dc; + g_core_fp.fp_read_DD_status = himax_mcu_read_DD_status; +#if defined(HX_AUTO_UPDATE_FW) + g_core_fp._diff_overlay_bin = hx_mcu_diff_overlay_bin; +#endif +#endif +#ifdef CORE_FLASH + g_core_fp.fp_chip_erase = himax_mcu_chip_erase; + g_core_fp.fp_block_erase = himax_mcu_block_erase; + g_core_fp.fp_sector_erase = himax_mcu_sector_erase; + g_core_fp.fp_flash_programming = himax_mcu_flash_programming; + g_core_fp.fp_flash_page_write = himax_mcu_flash_page_write; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_32k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_32k; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_60k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_60k; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_64k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_64k; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_124k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_124k; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_128k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_128k; + g_core_fp.fp_flash_dump_func = himax_mcu_flash_dump_func; + g_core_fp.fp_flash_lastdata_check = himax_mcu_flash_lastdata_check; + g_core_fp._diff_overlay_flash = hx_mcu_diff_overlay_flash; +#endif +#ifdef CORE_SRAM + g_core_fp.fp_sram_write = himax_mcu_sram_write; + g_core_fp.fp_sram_verify = himax_mcu_sram_verify; + g_core_fp.fp_get_DSRAM_data = himax_mcu_get_DSRAM_data; +#endif +#ifdef CORE_DRIVER + g_core_fp.fp_chip_init = himax_mcu_init_ic; +#ifdef HX_AUTO_UPDATE_FW + g_core_fp.fp_fw_ver_bin = himax_mcu_fw_ver_bin; +#endif +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_pin_reset = himax_mcu_pin_reset; + g_core_fp.fp_ic_reset = himax_mcu_ic_reset; +#endif + g_core_fp.fp_touch_information = himax_mcu_touch_information; + g_core_fp.fp_reload_config = himax_mcu_reload_config; + g_core_fp.fp_get_touch_data_size = himax_mcu_get_touch_data_size; + g_core_fp.fp_hand_shaking = himax_mcu_hand_shaking; + g_core_fp.fp_determin_diag_rawdata = himax_mcu_determin_diag_rawdata; + g_core_fp.fp_determin_diag_storage = himax_mcu_determin_diag_storage; + g_core_fp.fp_cal_data_len = himax_mcu_cal_data_len; + g_core_fp.fp_diag_check_sum = himax_mcu_diag_check_sum; + g_core_fp.fp_diag_parse_raw_data = himax_mcu_diag_parse_raw_data; +#ifdef HX_ESD_RECOVERY + g_core_fp.fp_ic_esd_recovery = himax_mcu_ic_esd_recovery; + g_core_fp.fp_esd_ic_reset = himax_mcu_esd_ic_reset; +#endif +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) + g_core_fp.fp_resend_cmd_func = himax_mcu_resend_cmd_func; +#endif +#if defined(HX_TP_PROC_GUEST_INFO) + g_core_fp.guest_info_get_status = himax_guest_info_get_status; + g_core_fp.read_guest_info = hx_read_guest_info; +#endif +#endif +#ifdef HX_ZERO_FLASH + g_core_fp.fp_reload_disable = hx_dis_rload_0f; + g_core_fp.fp_clean_sram_0f = himax_mcu_clean_sram_0f; + g_core_fp.fp_write_sram_0f = himax_mcu_write_sram_0f; + g_core_fp.fp_write_sram_0f_crc = himax_sram_write_crc_check; + g_core_fp.fp_firmware_update_0f = himax_mcu_firmware_update_0f; + g_core_fp.fp_0f_operation = himax_mcu_0f_operation; + g_core_fp.fp_0f_operation_dirly = himax_mcu_0f_operation_dirly; + g_core_fp.fp_0f_op_file_dirly = hx_0f_op_file_dirly; + g_core_fp.fp_0f_esd_check = himax_mcu_0f_esd_check; +#ifdef HX_0F_DEBUG + g_core_fp.fp_read_sram_0f = himax_mcu_read_sram_0f; + g_core_fp.fp_read_all_sram = himax_mcu_read_all_sram; + g_core_fp.fp_firmware_read_0f = himax_mcu_firmware_read_0f; + g_core_fp.fp_0f_operation_check = himax_mcu_0f_operation_check; +#endif +#ifdef HX_CODE_OVERLAY + g_core_fp.fp_0f_overlay = himax_mcu_0f_overlay; +#endif +#ifdef HIMAX_PALM_SENSOR_EN + g_core_fp.fp_palm_detection_function = himax_mcu_palm_detection; +#endif +#endif +} + +int himax_mcu_in_cmd_struct_init(void) +{ + int err = 0; + + I("%s: Entering!\n", __func__); + himax_mcu_cmd_struct_free = himax_mcu_in_cmd_struct_free; + + g_core_cmd_op = kzalloc(sizeof(struct himax_core_command_operation), GFP_KERNEL); + if (g_core_cmd_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_fail; + } + + g_core_cmd_op->ic_op = kzalloc(sizeof(struct ic_operation), GFP_KERNEL); + if (g_core_cmd_op->ic_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_ic_op_fail; + } + + g_core_cmd_op->fw_op = kzalloc(sizeof(struct fw_operation), GFP_KERNEL); + if (g_core_cmd_op->fw_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_fw_op_fail; + } + + g_core_cmd_op->flash_op = kzalloc(sizeof(struct flash_operation), GFP_KERNEL); + if (g_core_cmd_op->flash_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_flash_op_fail; + } + + g_core_cmd_op->sram_op = kzalloc(sizeof(struct sram_operation), GFP_KERNEL); + if (g_core_cmd_op->sram_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_sram_op_fail; + } + + g_core_cmd_op->driver_op = kzalloc(sizeof(struct driver_operation), GFP_KERNEL); + if (g_core_cmd_op->driver_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_driver_op_fail; + } + + pic_op = g_core_cmd_op->ic_op; + pfw_op = g_core_cmd_op->fw_op; + pflash_op = g_core_cmd_op->flash_op; + psram_op = g_core_cmd_op->sram_op; + pdriver_op = g_core_cmd_op->driver_op; +#ifdef HX_ZERO_FLASH + g_core_cmd_op->zf_op = kzalloc(sizeof(struct zf_operation), GFP_KERNEL); + if (g_core_cmd_op->zf_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_zf_op_fail; + } + pzf_op = g_core_cmd_op->zf_op; + +#endif +#ifndef __MTK_SPI__ + g_internal_buffer = kzalloc(sizeof(uint8_t)*(1024 * 48 + ADDR_LEN_4), GFP_KERNEL); +#else + g_internal_buffer = kzalloc(sizeof(uint8_t)*(HX_SPI_MTK_MAX_WRITE_SZ), GFP_KERNEL); +#endif + if (g_internal_buffer == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_g_internal_buffer_fail; + } + himax_mcu_fp_init(); + + return NO_ERR; + +err_g_core_cmd_op_g_internal_buffer_fail: +#ifdef HX_ZERO_FLASH + kfree(g_core_cmd_op->zf_op); + g_core_cmd_op->zf_op = NULL; +err_g_core_cmd_op_zf_op_fail: +#endif + kfree(g_core_cmd_op->driver_op); + g_core_cmd_op->driver_op = NULL; +err_g_core_cmd_op_driver_op_fail: + kfree(g_core_cmd_op->sram_op); + g_core_cmd_op->sram_op = NULL; +err_g_core_cmd_op_sram_op_fail: + kfree(g_core_cmd_op->flash_op); + g_core_cmd_op->flash_op = NULL; +err_g_core_cmd_op_flash_op_fail: + kfree(g_core_cmd_op->fw_op); + g_core_cmd_op->fw_op = NULL; +err_g_core_cmd_op_fw_op_fail: + kfree(g_core_cmd_op->ic_op); + g_core_cmd_op->ic_op = NULL; +err_g_core_cmd_op_ic_op_fail: + kfree(g_core_cmd_op); + g_core_cmd_op = NULL; +err_g_core_cmd_op_fail: + + return err; +} +EXPORT_SYMBOL(himax_mcu_in_cmd_struct_init); + +void himax_mcu_in_cmd_struct_free(void) +{ + pic_op = NULL; + pfw_op = NULL; + pflash_op = NULL; + psram_op = NULL; + pdriver_op = NULL; + kfree(g_internal_buffer); + g_internal_buffer = NULL; +#ifdef HX_ZERO_FLASH + kfree(g_core_cmd_op->zf_op); + g_core_cmd_op->zf_op = NULL; +#endif + kfree(g_core_cmd_op->driver_op); + g_core_cmd_op->driver_op = NULL; + kfree(g_core_cmd_op->sram_op); + g_core_cmd_op->sram_op = NULL; + kfree(g_core_cmd_op->flash_op); + g_core_cmd_op->flash_op = NULL; + kfree(g_core_cmd_op->fw_op); + g_core_cmd_op->fw_op = NULL; + kfree(g_core_cmd_op->ic_op); + g_core_cmd_op->ic_op = NULL; + kfree(g_core_cmd_op); + g_core_cmd_op = NULL; +#if defined(HX_EN_DYNAMIC_NAME) + if (i_CTPM_firmware_name != NULL) { + kfree(i_CTPM_firmware_name); + i_CTPM_firmware_name = NULL; + } +#endif + + I("%s: release completed\n", __func__); +} + +void himax_in_parse_assign_cmd(uint32_t addr, uint8_t *cmd, int len) +{ + /*I("%s: Entering!\n", __func__);*/ + switch (len) { + case 1: + cmd[0] = addr; + /*I("%s: cmd[0] = 0x%02X\n", __func__, cmd[0]);*/ + break; + + case 2: + cmd[0] = addr % 0x100; + cmd[1] = (addr >> 8) % 0x100; + /*I("%s: cmd[0] = 0x%02X,cmd[1] = 0x%02X\n", __func__, cmd[0], cmd[1]);*/ + break; + + case 4: + cmd[0] = addr % 0x100; + cmd[1] = (addr >> 8) % 0x100; + cmd[2] = (addr >> 16) % 0x100; + cmd[3] = addr / 0x1000000; + /* I("%s: cmd[0] = 0x%02X,cmd[1] = 0x%02X,cmd[2] = 0x%02X,cmd[3] = 0x%02X\n", */ + /* __func__, cmd[0], cmd[1], cmd[2], cmd[3]);*/ + break; + + default: + E("%s: input length fault,len = %d!\n", __func__, len); + } +} +EXPORT_SYMBOL(himax_in_parse_assign_cmd); + +void himax_mcu_in_cmd_init(void) +{ + I("%s: Entering!\n", __func__); + +#ifdef HIMAX_PALM_SENSOR_EN + himax_in_parse_assign_cmd(palm_detection_register, pfw_op->addr_palm_detection_enable, sizeof(pfw_op->addr_palm_detection_enable)); +#endif +#ifdef CORE_IC + himax_in_parse_assign_cmd(ic_adr_ahb_addr_byte_0, pic_op->addr_ahb_addr_byte_0, sizeof(pic_op->addr_ahb_addr_byte_0)); + himax_in_parse_assign_cmd(ic_adr_ahb_rdata_byte_0, pic_op->addr_ahb_rdata_byte_0, sizeof(pic_op->addr_ahb_rdata_byte_0)); + himax_in_parse_assign_cmd(ic_adr_ahb_access_direction, pic_op->addr_ahb_access_direction, sizeof(pic_op->addr_ahb_access_direction)); + himax_in_parse_assign_cmd(ic_adr_conti, pic_op->addr_conti, sizeof(pic_op->addr_conti)); + himax_in_parse_assign_cmd(ic_adr_incr4, pic_op->addr_incr4, sizeof(pic_op->addr_incr4)); + himax_in_parse_assign_cmd(ic_adr_i2c_psw_lb, pic_op->adr_i2c_psw_lb, sizeof(pic_op->adr_i2c_psw_lb)); + himax_in_parse_assign_cmd(ic_adr_i2c_psw_ub, pic_op->adr_i2c_psw_ub, sizeof(pic_op->adr_i2c_psw_ub)); + himax_in_parse_assign_cmd(ic_cmd_ahb_access_direction_read, pic_op->data_ahb_access_direction_read, sizeof(pic_op->data_ahb_access_direction_read)); + himax_in_parse_assign_cmd(ic_cmd_conti, pic_op->data_conti, sizeof(pic_op->data_conti)); + himax_in_parse_assign_cmd(ic_cmd_incr4, pic_op->data_incr4, sizeof(pic_op->data_incr4)); + himax_in_parse_assign_cmd(ic_cmd_i2c_psw_lb, pic_op->data_i2c_psw_lb, sizeof(pic_op->data_i2c_psw_lb)); + himax_in_parse_assign_cmd(ic_cmd_i2c_psw_ub, pic_op->data_i2c_psw_ub, sizeof(pic_op->data_i2c_psw_ub)); + himax_in_parse_assign_cmd(ic_adr_tcon_on_rst, pic_op->addr_tcon_on_rst, sizeof(pic_op->addr_tcon_on_rst)); + himax_in_parse_assign_cmd(ic_addr_adc_on_rst, pic_op->addr_adc_on_rst, sizeof(pic_op->addr_adc_on_rst)); + himax_in_parse_assign_cmd(ic_adr_psl, pic_op->addr_psl, sizeof(pic_op->addr_psl)); + himax_in_parse_assign_cmd(ic_adr_cs_central_state, pic_op->addr_cs_central_state, sizeof(pic_op->addr_cs_central_state)); + himax_in_parse_assign_cmd(ic_cmd_rst, pic_op->data_rst, sizeof(pic_op->data_rst)); + himax_in_parse_assign_cmd(ic_adr_osc_en, pic_op->adr_osc_en, sizeof(pic_op->adr_osc_en)); + himax_in_parse_assign_cmd(ic_adr_osc_pw, pic_op->adr_osc_pw, sizeof(pic_op->adr_osc_pw)); +#endif +#ifdef CORE_FW + himax_in_parse_assign_cmd(fw_addr_system_reset, pfw_op->addr_system_reset, sizeof(pfw_op->addr_system_reset)); + himax_in_parse_assign_cmd(fw_addr_safe_mode_release_pw, pfw_op->addr_safe_mode_release_pw, sizeof(pfw_op->addr_safe_mode_release_pw)); + himax_in_parse_assign_cmd(fw_addr_ctrl_fw, pfw_op->addr_ctrl_fw_isr, sizeof(pfw_op->addr_ctrl_fw_isr)); + himax_in_parse_assign_cmd(fw_addr_flag_reset_event, pfw_op->addr_flag_reset_event, sizeof(pfw_op->addr_flag_reset_event)); + himax_in_parse_assign_cmd(fw_addr_hsen_enable, pfw_op->addr_hsen_enable, sizeof(pfw_op->addr_hsen_enable)); + himax_in_parse_assign_cmd(fw_addr_smwp_enable, pfw_op->addr_smwp_enable, sizeof(pfw_op->addr_smwp_enable)); + himax_in_parse_assign_cmd(fw_addr_program_reload_from, pfw_op->addr_program_reload_from, sizeof(pfw_op->addr_program_reload_from)); + himax_in_parse_assign_cmd(fw_addr_program_reload_to, pfw_op->addr_program_reload_to, sizeof(pfw_op->addr_program_reload_to)); + himax_in_parse_assign_cmd(fw_addr_program_reload_page_write, pfw_op->addr_program_reload_page_write, sizeof(pfw_op->addr_program_reload_page_write)); + himax_in_parse_assign_cmd(fw_addr_raw_out_sel, pfw_op->addr_raw_out_sel, sizeof(pfw_op->addr_raw_out_sel)); + himax_in_parse_assign_cmd(fw_addr_reload_status, pfw_op->addr_reload_status, sizeof(pfw_op->addr_reload_status)); + himax_in_parse_assign_cmd(fw_addr_reload_crc32_result, pfw_op->addr_reload_crc32_result, sizeof(pfw_op->addr_reload_crc32_result)); + himax_in_parse_assign_cmd(fw_addr_reload_addr_from, pfw_op->addr_reload_addr_from, sizeof(pfw_op->addr_reload_addr_from)); + himax_in_parse_assign_cmd(fw_addr_reload_addr_cmd_beat, pfw_op->addr_reload_addr_cmd_beat, sizeof(pfw_op->addr_reload_addr_cmd_beat)); + himax_in_parse_assign_cmd(fw_addr_selftest_addr_en, pfw_op->addr_selftest_addr_en, sizeof(pfw_op->addr_selftest_addr_en)); + himax_in_parse_assign_cmd(fw_addr_criteria_addr, pfw_op->addr_criteria_addr, sizeof(pfw_op->addr_criteria_addr)); + himax_in_parse_assign_cmd(fw_addr_set_frame_addr, pfw_op->addr_set_frame_addr, sizeof(pfw_op->addr_set_frame_addr)); + himax_in_parse_assign_cmd(fw_addr_selftest_result_addr, pfw_op->addr_selftest_result_addr, sizeof(pfw_op->addr_selftest_result_addr)); + himax_in_parse_assign_cmd(fw_addr_sorting_mode_en, pfw_op->addr_sorting_mode_en, sizeof(pfw_op->addr_sorting_mode_en)); + himax_in_parse_assign_cmd(fw_addr_max_dc, pfw_op->addr_max_dc, sizeof(pfw_op->addr_max_dc)); + himax_in_parse_assign_cmd(fw_addr_fw_mode_status, pfw_op->addr_fw_mode_status, sizeof(pfw_op->addr_fw_mode_status)); + himax_in_parse_assign_cmd(fw_addr_icid_addr, pfw_op->addr_icid_addr, sizeof(pfw_op->addr_icid_addr)); + himax_in_parse_assign_cmd(fw_addr_trigger_addr, pfw_op->addr_trigger_addr, sizeof(pfw_op->addr_trigger_addr)); + himax_in_parse_assign_cmd(fw_addr_fw_ver_addr, pfw_op->addr_fw_ver_addr, sizeof(pfw_op->addr_fw_ver_addr)); + himax_in_parse_assign_cmd(fw_addr_fw_cfg_addr, pfw_op->addr_fw_cfg_addr, sizeof(pfw_op->addr_fw_cfg_addr)); + himax_in_parse_assign_cmd(fw_addr_fw_vendor_addr, pfw_op->addr_fw_vendor_addr, sizeof(pfw_op->addr_fw_vendor_addr)); + himax_in_parse_assign_cmd(fw_addr_cus_info, pfw_op->addr_cus_info, sizeof(pfw_op->addr_cus_info)); + himax_in_parse_assign_cmd(fw_addr_proj_info, pfw_op->addr_proj_info, sizeof(pfw_op->addr_proj_info)); + himax_in_parse_assign_cmd(fw_addr_fw_state_addr, pfw_op->addr_fw_state_addr, sizeof(pfw_op->addr_fw_state_addr)); + himax_in_parse_assign_cmd(fw_addr_fw_dbg_msg_addr, pfw_op->addr_fw_dbg_msg_addr, sizeof(pfw_op->addr_fw_dbg_msg_addr)); + himax_in_parse_assign_cmd(fw_addr_chk_fw_status, pfw_op->addr_chk_fw_status, sizeof(pfw_op->addr_chk_fw_status)); + himax_in_parse_assign_cmd(fw_addr_dd_handshak_addr, pfw_op->addr_dd_handshak_addr, sizeof(pfw_op->addr_dd_handshak_addr)); + himax_in_parse_assign_cmd(fw_addr_dd_data_addr, pfw_op->addr_dd_data_addr, sizeof(pfw_op->addr_dd_data_addr)); + himax_in_parse_assign_cmd(fw_data_system_reset, pfw_op->data_system_reset, sizeof(pfw_op->data_system_reset)); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_active, pfw_op->data_safe_mode_release_pw_active, sizeof(pfw_op->data_safe_mode_release_pw_active)); + himax_in_parse_assign_cmd(fw_data_clear, pfw_op->data_clear, sizeof(pfw_op->data_clear)); + himax_in_parse_assign_cmd(fw_data_clear, pfw_op->data_clear, sizeof(pfw_op->data_clear)); + himax_in_parse_assign_cmd(fw_data_fw_stop, pfw_op->data_fw_stop, sizeof(pfw_op->data_fw_stop)); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, pfw_op->data_safe_mode_release_pw_reset, sizeof(pfw_op->data_safe_mode_release_pw_reset)); + himax_in_parse_assign_cmd(fw_data_program_reload_start, pfw_op->data_program_reload_start, sizeof(pfw_op->data_program_reload_start)); + himax_in_parse_assign_cmd(fw_data_program_reload_compare, pfw_op->data_program_reload_compare, sizeof(pfw_op->data_program_reload_compare)); + himax_in_parse_assign_cmd(fw_data_program_reload_break, pfw_op->data_program_reload_break, sizeof(pfw_op->data_program_reload_break)); + himax_in_parse_assign_cmd(fw_data_selftest_request, pfw_op->data_selftest_request, sizeof(pfw_op->data_selftest_request)); + himax_in_parse_assign_cmd(fw_data_criteria_aa_top, pfw_op->data_criteria_aa_top, sizeof(pfw_op->data_criteria_aa_top)); + himax_in_parse_assign_cmd(fw_data_criteria_aa_bot, pfw_op->data_criteria_aa_bot, sizeof(pfw_op->data_criteria_aa_bot)); + himax_in_parse_assign_cmd(fw_data_criteria_key_top, pfw_op->data_criteria_key_top, sizeof(pfw_op->data_criteria_key_top)); + himax_in_parse_assign_cmd(fw_data_criteria_key_bot, pfw_op->data_criteria_key_bot, sizeof(pfw_op->data_criteria_key_bot)); + himax_in_parse_assign_cmd(fw_data_criteria_avg_top, pfw_op->data_criteria_avg_top, sizeof(pfw_op->data_criteria_avg_top)); + himax_in_parse_assign_cmd(fw_data_criteria_avg_bot, pfw_op->data_criteria_avg_bot, sizeof(pfw_op->data_criteria_avg_bot)); + himax_in_parse_assign_cmd(fw_data_set_frame, pfw_op->data_set_frame, sizeof(pfw_op->data_set_frame)); + himax_in_parse_assign_cmd(fw_data_selftest_ack_hb, pfw_op->data_selftest_ack_hb, sizeof(pfw_op->data_selftest_ack_hb)); + himax_in_parse_assign_cmd(fw_data_selftest_ack_lb, pfw_op->data_selftest_ack_lb, sizeof(pfw_op->data_selftest_ack_lb)); + himax_in_parse_assign_cmd(fw_data_selftest_pass, pfw_op->data_selftest_pass, sizeof(pfw_op->data_selftest_pass)); + himax_in_parse_assign_cmd(fw_data_normal_cmd, pfw_op->data_normal_cmd, sizeof(pfw_op->data_normal_cmd)); + himax_in_parse_assign_cmd(fw_data_normal_status, pfw_op->data_normal_status, sizeof(pfw_op->data_normal_status)); + himax_in_parse_assign_cmd(fw_data_sorting_cmd, pfw_op->data_sorting_cmd, sizeof(pfw_op->data_sorting_cmd)); + himax_in_parse_assign_cmd(fw_data_sorting_status, pfw_op->data_sorting_status, sizeof(pfw_op->data_sorting_status)); + himax_in_parse_assign_cmd(fw_data_dd_request, pfw_op->data_dd_request, sizeof(pfw_op->data_dd_request)); + himax_in_parse_assign_cmd(fw_data_dd_ack, pfw_op->data_dd_ack, sizeof(pfw_op->data_dd_ack)); + himax_in_parse_assign_cmd(fw_data_idle_dis_pwd, pfw_op->data_idle_dis_pwd, sizeof(pfw_op->data_idle_dis_pwd)); + himax_in_parse_assign_cmd(fw_data_idle_en_pwd, pfw_op->data_idle_en_pwd, sizeof(pfw_op->data_idle_en_pwd)); + himax_in_parse_assign_cmd(fw_data_rawdata_ready_hb, pfw_op->data_rawdata_ready_hb, sizeof(pfw_op->data_rawdata_ready_hb)); + himax_in_parse_assign_cmd(fw_data_rawdata_ready_lb, pfw_op->data_rawdata_ready_lb, sizeof(pfw_op->data_rawdata_ready_lb)); + himax_in_parse_assign_cmd(fw_addr_ahb_addr, pfw_op->addr_ahb_addr, sizeof(pfw_op->addr_ahb_addr)); + himax_in_parse_assign_cmd(fw_data_ahb_dis, pfw_op->data_ahb_dis, sizeof(pfw_op->data_ahb_dis)); + himax_in_parse_assign_cmd(fw_data_ahb_en, pfw_op->data_ahb_en, sizeof(pfw_op->data_ahb_en)); + himax_in_parse_assign_cmd(fw_addr_event_addr, pfw_op->addr_event_addr, sizeof(pfw_op->addr_event_addr)); + himax_in_parse_assign_cmd(fw_usb_detect_addr, pfw_op->addr_usb_detect, sizeof(pfw_op->addr_usb_detect)); +#ifdef HX_P_SENSOR + himax_in_parse_assign_cmd(fw_addr_ulpm_33, pfw_op->addr_ulpm_33, sizeof(pfw_op->addr_ulpm_33)); + himax_in_parse_assign_cmd(fw_addr_ulpm_34, pfw_op->addr_ulpm_34, sizeof(pfw_op->addr_ulpm_34)); + himax_in_parse_assign_cmd(fw_data_ulpm_11, pfw_op->data_ulpm_11, sizeof(pfw_op->data_ulpm_11)); + himax_in_parse_assign_cmd(fw_data_ulpm_22, pfw_op->data_ulpm_22, sizeof(pfw_op->data_ulpm_22)); + himax_in_parse_assign_cmd(fw_data_ulpm_33, pfw_op->data_ulpm_33, sizeof(pfw_op->data_ulpm_33)); + himax_in_parse_assign_cmd(fw_data_ulpm_aa, pfw_op->data_ulpm_aa, sizeof(pfw_op->data_ulpm_aa)); +#endif +#endif +#ifdef CORE_FLASH + himax_in_parse_assign_cmd(flash_addr_spi200_trans_fmt, pflash_op->addr_spi200_trans_fmt, sizeof(pflash_op->addr_spi200_trans_fmt)); + himax_in_parse_assign_cmd(flash_addr_spi200_trans_ctrl, pflash_op->addr_spi200_trans_ctrl, sizeof(pflash_op->addr_spi200_trans_ctrl)); + himax_in_parse_assign_cmd(flash_addr_spi200_cmd, pflash_op->addr_spi200_cmd, sizeof(pflash_op->addr_spi200_cmd)); + himax_in_parse_assign_cmd(flash_addr_spi200_addr, pflash_op->addr_spi200_addr, sizeof(pflash_op->addr_spi200_addr)); + himax_in_parse_assign_cmd(flash_addr_spi200_data, pflash_op->addr_spi200_data, sizeof(pflash_op->addr_spi200_data)); + himax_in_parse_assign_cmd(flash_addr_spi200_bt_num, pflash_op->addr_spi200_bt_num, sizeof(pflash_op->addr_spi200_bt_num)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_fmt, pflash_op->data_spi200_trans_fmt, sizeof(pflash_op->data_spi200_trans_fmt)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_1, pflash_op->data_spi200_trans_ctrl_1, sizeof(pflash_op->data_spi200_trans_ctrl_1)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_2, pflash_op->data_spi200_trans_ctrl_2, sizeof(pflash_op->data_spi200_trans_ctrl_2)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_3, pflash_op->data_spi200_trans_ctrl_3, sizeof(pflash_op->data_spi200_trans_ctrl_3)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_4, pflash_op->data_spi200_trans_ctrl_4, sizeof(pflash_op->data_spi200_trans_ctrl_4)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_5, pflash_op->data_spi200_trans_ctrl_5, sizeof(pflash_op->data_spi200_trans_ctrl_5)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_1, pflash_op->data_spi200_cmd_1, sizeof(pflash_op->data_spi200_cmd_1)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_2, pflash_op->data_spi200_cmd_2, sizeof(pflash_op->data_spi200_cmd_2)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_3, pflash_op->data_spi200_cmd_3, sizeof(pflash_op->data_spi200_cmd_3)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_4, pflash_op->data_spi200_cmd_4, sizeof(pflash_op->data_spi200_cmd_4)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_5, pflash_op->data_spi200_cmd_5, sizeof(pflash_op->data_spi200_cmd_5)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_6, pflash_op->data_spi200_cmd_6, sizeof(pflash_op->data_spi200_cmd_6)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_7, pflash_op->data_spi200_cmd_7, sizeof(pflash_op->data_spi200_cmd_7)); + himax_in_parse_assign_cmd(flash_data_spi200_addr, pflash_op->data_spi200_addr, sizeof(pflash_op->data_spi200_addr)); +#endif +#ifdef CORE_SRAM + /* sram start*/ + himax_in_parse_assign_cmd(sram_adr_mkey, psram_op->addr_mkey, sizeof(psram_op->addr_mkey)); + himax_in_parse_assign_cmd(sram_adr_rawdata_addr, psram_op->addr_rawdata_addr, sizeof(psram_op->addr_rawdata_addr)); + himax_in_parse_assign_cmd(sram_adr_rawdata_end, psram_op->addr_rawdata_end, sizeof(psram_op->addr_rawdata_end)); + himax_in_parse_assign_cmd(sram_passwrd_start, psram_op->passwrd_start, sizeof(psram_op->passwrd_start)); + himax_in_parse_assign_cmd(sram_passwrd_end, psram_op->passwrd_end, sizeof(psram_op->passwrd_end)); + /* sram end*/ +#endif +#ifdef CORE_DRIVER + himax_in_parse_assign_cmd(driver_addr_fw_define_flash_reload, pdriver_op->addr_fw_define_flash_reload, sizeof(pdriver_op->addr_fw_define_flash_reload)); + himax_in_parse_assign_cmd(driver_addr_fw_define_2nd_flash_reload, pdriver_op->addr_fw_define_2nd_flash_reload, sizeof(pdriver_op->addr_fw_define_2nd_flash_reload)); + himax_in_parse_assign_cmd(driver_addr_fw_define_int_is_edge, pdriver_op->addr_fw_define_int_is_edge, sizeof(pdriver_op->addr_fw_define_int_is_edge)); + himax_in_parse_assign_cmd(driver_addr_fw_define_rxnum_txnum_maxpt, pdriver_op->addr_fw_define_rxnum_txnum_maxpt, sizeof(pdriver_op->addr_fw_define_rxnum_txnum_maxpt)); + himax_in_parse_assign_cmd(driver_addr_fw_define_xy_res_enable, pdriver_op->addr_fw_define_xy_res_enable, sizeof(pdriver_op->addr_fw_define_xy_res_enable)); + himax_in_parse_assign_cmd(driver_addr_fw_define_x_y_res, pdriver_op->addr_fw_define_x_y_res, sizeof(pdriver_op->addr_fw_define_x_y_res)); + himax_in_parse_assign_cmd(driver_data_df_rx, pdriver_op->data_df_rx, sizeof(pdriver_op->data_df_rx)); + himax_in_parse_assign_cmd(driver_data_df_tx, pdriver_op->data_df_tx, sizeof(pdriver_op->data_df_tx)); + himax_in_parse_assign_cmd(driver_data_df_pt, pdriver_op->data_df_pt, sizeof(pdriver_op->data_df_pt)); + himax_in_parse_assign_cmd(driver_data_df_x_res, pdriver_op->data_df_x_res, sizeof(pdriver_op->data_df_x_res)); + himax_in_parse_assign_cmd(driver_data_df_y_res, pdriver_op->data_df_y_res, sizeof(pdriver_op->data_df_y_res)); + himax_in_parse_assign_cmd(driver_data_fw_define_flash_reload_dis, pdriver_op->data_fw_define_flash_reload_dis, sizeof(pdriver_op->data_fw_define_flash_reload_dis)); + himax_in_parse_assign_cmd(driver_data_fw_define_flash_reload_en, pdriver_op->data_fw_define_flash_reload_en, sizeof(pdriver_op->data_fw_define_flash_reload_en)); + himax_in_parse_assign_cmd(driver_data_fw_define_rxnum_txnum_maxpt_sorting, pdriver_op->data_fw_define_rxnum_txnum_maxpt_sorting, sizeof(pdriver_op->data_fw_define_rxnum_txnum_maxpt_sorting)); + himax_in_parse_assign_cmd(driver_data_fw_define_rxnum_txnum_maxpt_normal, pdriver_op->data_fw_define_rxnum_txnum_maxpt_normal, sizeof(pdriver_op->data_fw_define_rxnum_txnum_maxpt_normal)); +#endif +#ifdef HX_ZERO_FLASH + himax_in_parse_assign_cmd(zf_addr_dis_flash_reload, pzf_op->addr_dis_flash_reload, sizeof(pzf_op->addr_dis_flash_reload)); + himax_in_parse_assign_cmd(zf_data_dis_flash_reload, pzf_op->data_dis_flash_reload, sizeof(pzf_op->data_dis_flash_reload)); + himax_in_parse_assign_cmd(zf_addr_system_reset, pzf_op->addr_system_reset, sizeof(pzf_op->addr_system_reset)); + himax_in_parse_assign_cmd(zf_data_system_reset, pzf_op->data_system_reset, sizeof(pzf_op->data_system_reset)); + himax_in_parse_assign_cmd(zf_data_sram_start_addr, pzf_op->data_sram_start_addr, sizeof(pzf_op->data_sram_start_addr)); + himax_in_parse_assign_cmd(zf_data_sram_clean, pzf_op->data_sram_clean, sizeof(pzf_op->data_sram_clean)); + himax_in_parse_assign_cmd(zf_data_cfg_info, pzf_op->data_cfg_info, sizeof(pzf_op->data_cfg_info)); + himax_in_parse_assign_cmd(zf_data_fw_cfg_1, pzf_op->data_fw_cfg_1, sizeof(pzf_op->data_fw_cfg_1)); + himax_in_parse_assign_cmd(zf_data_fw_cfg_2, pzf_op->data_fw_cfg_2, sizeof(pzf_op->data_fw_cfg_2)); + himax_in_parse_assign_cmd(zf_data_fw_cfg_3, pzf_op->data_fw_cfg_3, sizeof(pzf_op->data_fw_cfg_3)); + himax_in_parse_assign_cmd(zf_data_adc_cfg_1, pzf_op->data_adc_cfg_1, sizeof(pzf_op->data_adc_cfg_1)); + himax_in_parse_assign_cmd(zf_data_adc_cfg_2, pzf_op->data_adc_cfg_2, sizeof(pzf_op->data_adc_cfg_2)); + himax_in_parse_assign_cmd(zf_data_adc_cfg_3, pzf_op->data_adc_cfg_3, sizeof(pzf_op->data_adc_cfg_3)); + himax_in_parse_assign_cmd(zf_data_map_table, pzf_op->data_map_table, sizeof(pzf_op->data_map_table)); + /*himax_in_parse_assign_cmd(zf_data_mode_switch, pzf_op->data_mode_switch, sizeof(pzf_op->data_mode_switch));*/ + himax_in_parse_assign_cmd(zf_addr_sts_chk, pzf_op->addr_sts_chk, sizeof(pzf_op->addr_sts_chk)); + himax_in_parse_assign_cmd(zf_data_activ_sts, pzf_op->data_activ_sts, sizeof(pzf_op->data_activ_sts)); + himax_in_parse_assign_cmd(zf_addr_activ_relod, pzf_op->addr_activ_relod, sizeof(pzf_op->addr_activ_relod)); + himax_in_parse_assign_cmd(zf_data_activ_in, pzf_op->data_activ_in, sizeof(pzf_op->data_activ_in)); +#endif +} +EXPORT_SYMBOL(himax_mcu_in_cmd_init); + +/* init end*/ +#endif diff --git a/drivers/input/touchscreen/himax_0flash_mmi/himax_inspection.c b/drivers/input/touchscreen/himax_0flash_mmi/himax_inspection.c new file mode 100644 index 000000000000..1d4564fde03c --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/himax_inspection.c @@ -0,0 +1,2264 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for inspection functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_inspection.h" + +static int g_gap_vertical_partial = 3; +static int *g_gap_vertical_part; +static int g_gap_horizontal_partial = 3; +static int *g_gap_horizontal_part; + +static int g_dc_max; + +static int g_1kind_raw_size; +uint32_t g_rslt_data_len; +int **g_inspection_criteria; +int *g_inspt_crtra_flag; +int HX_CRITERIA_ITEM; +int HX_CRITERIA_SIZE; +char *g_rslt_data; +static char g_file_path[256]; +static char g_rslt_log[256]; +static char g_start_log[256]; +char *hx_self_test_file_name = NULL; + +char *g_himax_inspection_mode[] = { + "HIMAX_INSPECTION_OPEN", + "HIMAX_INSPECTION_MICRO_OPEN", + "HIMAX_INSPECTION_SHORT", + "HIMAX_INSPECTION_RAWDATA", + "HIMAX_INSPECTION_BPN_RAWDATA", + "HIMAX_INSPECTION_WEIGHT_NOISE", + "HIMAX_INSPECTION_ABS_NOISE", + "HIMAX_INSPECTION_SORTING", + "HIMAX_INSPECTION_BACK_NORMAL", + + "HIMAX_INSPECTION_GAPTEST_RAW", + + "HIMAX_INSPECTION_ACT_IDLE_RAWDATA", + "HIMAX_INSPECTION_ACT_IDLE_NOISE", + + "HIMAX_INSPECTION_LPWUG_RAWDATA", + "HIMAX_INSPECTION_LPWUG_NOISE", + "HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA", + "HIMAX_INSPECTION_LPWUG_IDLE_NOISE", + NULL +}; + +/* for criteria */ +char *g_hx_inspt_crtra_name[] = { + "CRITERIA_RAW_MIN", + "CRITERIA_RAW_MAX", + "CRITERIA_RAW_BPN_MIN", + "CRITERIA_RAW_BPN_MAX", + "CRITERIA_SHORT_MIN", + "CRITERIA_SHORT_MAX", + "CRITERIA_OPEN_MIN", + "CRITERIA_OPEN_MAX", + "CRITERIA_MICRO_OPEN_MIN", + "CRITERIA_MICRO_OPEN_MAX", + "CRITERIA_NOISE_WT_MIN", + "CRITERIA_NOISE_WT_MAX", + "CRITERIA_NOISE_ABS_MIN", + "CRITERIA_NOISE_ABS_MAX", + "CRITERIA_SORT_MIN", + "CRITERIA_SORT_MAX", + + "CRITERIA_GAP_RAW_HOR_MIN", + "CRITERIA_GAP_RAW_HOR_MAX", + "CRITERIA_GAP_RAW_VER_MIN", + "CRITERIA_GAP_RAW_VER_MAX", + + "ACT_IDLE_NOISE_MIN", + "ACT_IDLE_NOISE_MAX", + "ACT_IDLE_RAWDATA_MIN", + "ACT_IDLE_RAWDATA_MAX", + + "LPWUG_NOISE_MIN", + "LPWUG_NOISE_MAX", + "LPWUG_RAWDATA_MIN", + "LPWUG_RAWDATA_MAX", + "LPWUG_IDLE_NOISE_MIN", + "LPWUG_IDLE_NOISE_MAX", + "LPWUG_IDLE_RAWDATA_MIN", + "LPWUG_IDLE_RAWDATA_MAX", + NULL +}; + + +void (*fp_himax_self_test_init)(void) = himax_inspection_init; + + +static void himax_press_powerkey(void) +{ + I(" %s POWER KEY event %x press\n", __func__, KEY_POWER); + input_report_key(private_ts->input_dev, KEY_POWER, 1); + input_sync(private_ts->input_dev); + + msleep(100); + + I(" %s POWER KEY event %x release\n", __func__, KEY_POWER); + input_report_key(private_ts->input_dev, KEY_POWER, 0); + input_sync(private_ts->input_dev); +} + + +static uint8_t NOISEMAX; +static uint8_t g_recal_thx; + +static int hx_test_data_pop_out(char *rslt_buf, char *filepath) +{ + struct file *raw_file = NULL; + struct filename *vts_name = NULL; + mm_segment_t fs; + loff_t pos = 0; + int ret_val = NO_ERR; + + I("%s: Entering!\n", __func__); + I("data size=0x%04X\n", (uint32_t)strlen(rslt_buf)); + + if (!kp_getname_kernel) { + E("kp_getname_kernel is NULL, not open file!\n"); + return -EIO; + } + vts_name = kp_getname_kernel(filepath); + + if (raw_file == NULL) + raw_file = kp_file_open_name(vts_name, O_TRUNC|O_CREAT|O_RDWR, + 0660); + + if (IS_ERR(raw_file)) { + E("%s open file failed = %ld\n", __func__, PTR_ERR(raw_file)); + ret_val = -EIO; + goto SAVE_DATA_ERR; + } + + fs = get_fs(); + set_fs(get_ds()); + vfs_write(raw_file, rslt_buf, + g_1kind_raw_size * HX_CRITERIA_ITEM * sizeof(char), &pos); + if (raw_file != NULL) + filp_close(raw_file, NULL); + + set_fs(fs); + +SAVE_DATA_ERR: + I("%s: End!\n", __func__); + return ret_val; +} + +static int hx_test_data_get(uint32_t RAW[], char *start_log, char *result, + int now_item) +{ + uint32_t i; + + ssize_t len = 0; + char *testdata = NULL; + uint32_t SZ_SIZE = g_1kind_raw_size; + + I("%s: Entering, Now type=%s!\n", __func__, + g_himax_inspection_mode[now_item]); + + testdata = kzalloc(sizeof(char) * SZ_SIZE, GFP_KERNEL); + if (testdata == NULL) { + E("%s: Memory allocation falied!\n", __func__); + return MEM_ALLOC_FAIL; + } + + len += snprintf((testdata + len), SZ_SIZE - len, "%s", start_log); + for (i = 0; i < ic_data->HX_TX_NUM*ic_data->HX_RX_NUM; i++) { + if (i > 1 && ((i + 1) % ic_data->HX_RX_NUM) == 0) + len += snprintf((testdata + len), SZ_SIZE - len, + "%5d,\n", RAW[i]); + else + len += snprintf((testdata + len), SZ_SIZE - len, + "%5d,", RAW[i]); + } + len += snprintf((testdata + len), SZ_SIZE - len, "\n%s", result); + + memcpy(&g_rslt_data[g_rslt_data_len], testdata, len); + g_rslt_data_len += len; + I("%s: g_rslt_data_len=%d!\n", __func__, g_rslt_data_len); + + /*memcpy(&g_rslt_data[now_item * SZ_SIZE], testdata, SZ_SIZE);*/ + + /* dbg */ + /* for(i = 0; i < SZ_SIZE; i++) + * { + * I("0x%04X, ", g_rslt_data[i + (now_item * SZ_SIZE)]); + * if(i > 0 && (i % 16 == 15)) + * PI("\n"); + * } + */ + + kfree(testdata); + I("%s: End!\n", __func__); + return NO_ERR; + +} + +static int himax_switch_mode_inspection(int mode) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + + I("%s: Entering\n", __func__); + + /*Stop Handshaking*/ + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; + tmp_addr[0] = 0x00; tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = 0x00; tmp_data[0] = 0x00; + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); + + /*Swtich Mode*/ + switch (mode) { + case HIMAX_INSPECTION_SORTING: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_SORTING_START; + tmp_data[0] = PWD_SORTING_START; + break; + case HIMAX_INSPECTION_OPEN: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_OPEN_START; + tmp_data[0] = PWD_OPEN_START; + break; + case HIMAX_INSPECTION_MICRO_OPEN: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_OPEN_START; + tmp_data[0] = PWD_OPEN_START; + break; + case HIMAX_INSPECTION_SHORT: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_SHORT_START; + tmp_data[0] = PWD_SHORT_START; + break; + + case HIMAX_INSPECTION_GAPTEST_RAW: + case HIMAX_INSPECTION_RAWDATA: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_RAWDATA_START; + tmp_data[0] = PWD_RAWDATA_START; + break; + case HIMAX_INSPECTION_BPN_RAWDATA: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_RAWDATA_START; + tmp_data[0] = PWD_RAWDATA_START; + break; + case HIMAX_INSPECTION_WEIGHT_NOISE: + case HIMAX_INSPECTION_ABS_NOISE: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_NOISE_START; + tmp_data[0] = PWD_NOISE_START; + break; + + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_ACT_IDLE_START; + tmp_data[0] = PWD_ACT_IDLE_START; + break; + + case HIMAX_INSPECTION_LPWUG_RAWDATA: + case HIMAX_INSPECTION_LPWUG_NOISE: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_LPWUG_START; + tmp_data[0] = PWD_LPWUG_START; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_LPWUG_IDLE_START; + tmp_data[0] = PWD_LPWUG_IDLE_START; + break; + + default: + I("%s,Nothing to be done!\n", __func__); + break; + } + + if (g_core_fp.fp_assign_sorting_mode != NULL) + g_core_fp.fp_assign_sorting_mode(tmp_data); + I("%s: End of setting!\n", __func__); + + return 0; + +} + +static int himax_get_rawdata(uint32_t RAW[], uint32_t datalen) +{ + uint8_t *tmp_rawdata; + bool get_raw_rlst; + uint8_t retry = 0; + uint32_t i = 0; + uint32_t j = 0; + uint32_t index = 0; + uint32_t Min_DATA = 0xFFFFFFFF; + uint32_t Max_DATA = 0x00000000; + + /* We use two bytes to combine a value of rawdata.*/ + tmp_rawdata = kzalloc(sizeof(uint8_t) * (datalen * 2), GFP_KERNEL); + if (tmp_rawdata == NULL) { + E("%s: Memory allocation falied!\n", __func__); + return MEM_ALLOC_FAIL; + } + + while (retry < 200) { + get_raw_rlst = g_core_fp.fp_get_DSRAM_data(tmp_rawdata, false); + if (get_raw_rlst) + break; + retry++; + } + + if (retry >= 200) + goto DIRECT_END; + + /* Copy Data*/ + for (i = 0; i < ic_data->HX_TX_NUM*ic_data->HX_RX_NUM; i++) + RAW[i] = tmp_rawdata[(i * 2) + 1] * 256 + tmp_rawdata[(i * 2)]; + /* don't output these logs, reduce factory test time + for (j = 0; j < ic_data->HX_RX_NUM; j++) { + if (j == 0) + PI(" RX%2d", j + 1); + else + PI(" RX%2d", j + 1); + } + PI("\n"); + ******* delete these logs *********************/ + + for (i = 0; i < ic_data->HX_TX_NUM; i++) { + /*PI("TX%2d", i + 1);*/ + for (j = 0; j < ic_data->HX_RX_NUM; j++) { + PD("%5d ", RAW[index]); + if (RAW[index] > Max_DATA) + Max_DATA = RAW[index]; + + if (RAW[index] < Min_DATA) + Min_DATA = RAW[index]; + + index++; + } + /*PI("\n");*/ + } + I("Max = %5d, Min = %5d\n", Max_DATA, Min_DATA); +DIRECT_END: + kfree(tmp_rawdata); + + if (get_raw_rlst) + return HX_INSPECT_OK; + else + return HX_INSPECT_EGETRAW; + +} + +static void himax_switch_data_type(uint8_t checktype) +{ + uint8_t datatype = 0x00; + + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + datatype = DATA_SORTING; + break; + case HIMAX_INSPECTION_OPEN: + datatype = DATA_OPEN; + break; + case HIMAX_INSPECTION_MICRO_OPEN: + datatype = DATA_MICRO_OPEN; + break; + case HIMAX_INSPECTION_SHORT: + datatype = DATA_SHORT; + break; + case HIMAX_INSPECTION_RAWDATA: + datatype = DATA_RAWDATA; + break; + case HIMAX_INSPECTION_BPN_RAWDATA: + datatype = DATA_RAWDATA; + break; + case HIMAX_INSPECTION_WEIGHT_NOISE: + case HIMAX_INSPECTION_ABS_NOISE: + datatype = DATA_NOISE; + break; + case HIMAX_INSPECTION_BACK_NORMAL: + datatype = DATA_BACK_NORMAL; + break; + + case HIMAX_INSPECTION_GAPTEST_RAW: + datatype = DATA_RAWDATA; + break; + + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + datatype = DATA_ACT_IDLE_RAWDATA; + break; + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + datatype = DATA_ACT_IDLE_NOISE; + break; + + case HIMAX_INSPECTION_LPWUG_RAWDATA: + datatype = DATA_LPWUG_RAWDATA; + break; + case HIMAX_INSPECTION_LPWUG_NOISE: + datatype = DATA_LPWUG_NOISE; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + datatype = DATA_LPWUG_IDLE_RAWDATA; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + datatype = DATA_LPWUG_IDLE_NOISE; + break; + + default: + E("Wrong type=%d\n", checktype); + break; + } + g_core_fp.fp_diag_register_set(datatype, 0x00); +} + +static void himax_bank_search_set(uint16_t Nframe, uint8_t checktype) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + + /*skip frame*/ + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x70; tmp_addr[0] = 0xF4; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + + switch (checktype) { + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + tmp_data[0] = BS_ACT_IDLE; + break; + case HIMAX_INSPECTION_LPWUG_RAWDATA: + case HIMAX_INSPECTION_LPWUG_NOISE: + tmp_data[0] = BS_LPWUG; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + tmp_data[0] = BS_LPWUG_dile; + break; + case HIMAX_INSPECTION_RAWDATA: + case HIMAX_INSPECTION_BPN_RAWDATA: + tmp_data[0] = BS_RAWDATA; + break; + case HIMAX_INSPECTION_WEIGHT_NOISE: + case HIMAX_INSPECTION_ABS_NOISE: + tmp_data[0] = BS_NOISE; + break; + default: + tmp_data[0] = BS_OPENSHORT; + break; + } + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); +} + +static void himax_neg_noise_sup(uint8_t *data) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + + /*0x10007FD8 Check support negative value or not */ + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x7F; tmp_addr[0] = 0xD8; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + + if ((tmp_data[3] & 0x04) == 0x04) { + data[2] = 0x0C; data[3] = 0x7F; + } else + I("%s Not support negative noise\n", __func__); +} + +static void himax_set_N_frame(uint16_t Nframe, uint8_t checktype) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + + himax_bank_search_set(Nframe, checktype); + + /*IIR MAX*/ + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; tmp_addr[1] = 0x72; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = (uint8_t)((Nframe & 0xFF00) >> 8); + tmp_data[0] = (uint8_t)(Nframe & 0x00FF); + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); + + if (checktype == HIMAX_INSPECTION_WEIGHT_NOISE || + checktype == HIMAX_INSPECTION_ABS_NOISE) + himax_neg_noise_sup(tmp_data); + + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); +} + +static void himax_get_noise_base(void)/*Normal Threshold*/ +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x70; tmp_addr[0] = 0x8C;/*0x1000708F*/ + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + + NOISEMAX = tmp_data[3]; + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x70; tmp_addr[0] = 0x90;/*0x10007092*/ + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + + g_recal_thx = tmp_data[2]; + I("%s: NOISEMAX=%d, g_recal_thx = %d\n", __func__, + NOISEMAX, g_recal_thx); +} + +static uint16_t himax_get_palm_num(void)/*Palm Number*/ +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + uint16_t palm_num; + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x70; tmp_addr[0] = 0xA8;/*0x100070AB*/ + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + palm_num = tmp_data[3]; + I("%s: palm_num = %d ", __func__, palm_num); + + return palm_num; +} + +static int himax_get_noise_weight_test(void) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + uint16_t weight = 0; + uint16_t value = 0; + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x72; tmp_addr[0] = 0xC8;/*0x100072C8 weighting value*/ + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + if (tmp_data[3] != 0x72 || tmp_data[2] != 0xC8) + return FW_NOT_READY; + + value = (tmp_data[1] << 8) | tmp_data[0]; + I("%s: value = %d, %d, %d ", __func__, value, tmp_data[2], tmp_data[3]); + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x70; tmp_addr[0] = 0x9C;/*0x1000709C weighting threshold*/ + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + weight = tmp_data[0]; + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x70; tmp_addr[0] = 0x94;/*0x10007095 weighting threshold*/ + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + weight = tmp_data[1] * weight; + I("%s: weight = %d ", __func__, weight); + + if (value > weight) + return ERR_TEST_FAIL; + else + return 0; +} + +static uint32_t himax_check_mode(uint8_t checktype) +{ + uint8_t tmp_data[4] = {0}; + uint8_t wait_pwd[2] = {0}; + + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + wait_pwd[0] = PWD_SORTING_END; + wait_pwd[1] = PWD_SORTING_END; + break; + case HIMAX_INSPECTION_OPEN: + wait_pwd[0] = PWD_OPEN_END; + wait_pwd[1] = PWD_OPEN_END; + break; + case HIMAX_INSPECTION_MICRO_OPEN: + wait_pwd[0] = PWD_OPEN_END; + wait_pwd[1] = PWD_OPEN_END; + break; + case HIMAX_INSPECTION_SHORT: + wait_pwd[0] = PWD_SHORT_END; + wait_pwd[1] = PWD_SHORT_END; + break; + case HIMAX_INSPECTION_RAWDATA: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + case HIMAX_INSPECTION_BPN_RAWDATA: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + case HIMAX_INSPECTION_WEIGHT_NOISE: + case HIMAX_INSPECTION_ABS_NOISE: + wait_pwd[0] = PWD_NOISE_END; + wait_pwd[1] = PWD_NOISE_END; + break; + + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + wait_pwd[0] = PWD_ACT_IDLE_END; + wait_pwd[1] = PWD_ACT_IDLE_END; + break; + + case HIMAX_INSPECTION_LPWUG_RAWDATA: + case HIMAX_INSPECTION_LPWUG_NOISE: + wait_pwd[0] = PWD_LPWUG_END; + wait_pwd[1] = PWD_LPWUG_END; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + wait_pwd[0] = PWD_LPWUG_IDLE_END; + wait_pwd[1] = PWD_LPWUG_IDLE_END; + break; + + default: + E("Wrong type=%d\n", checktype); + break; + } + + if (g_core_fp.fp_check_sorting_mode != NULL) + g_core_fp.fp_check_sorting_mode(tmp_data); + + if ((wait_pwd[0] == tmp_data[0]) && (wait_pwd[1] == tmp_data[1])) { + I("Change to mode=%s\n", g_himax_inspection_mode[checktype]); + return 0; + } else { + return 1; + } +} + +#define TEMP_LOG "%s: %s, tmp_data[0]=%x,tmp_data[1]=%x,tmp_data[2]=%x,tmp_data[3]=%x\n" + +static uint32_t himax_wait_sorting_mode(uint8_t checktype) +{ + uint8_t tmp_addr[4] = {0}; + uint8_t tmp_data[4] = {0}; + uint8_t wait_pwd[2] = {0}; + int count = 0; + + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + wait_pwd[0] = PWD_SORTING_END; + wait_pwd[1] = PWD_SORTING_END; + break; + case HIMAX_INSPECTION_OPEN: + wait_pwd[0] = PWD_OPEN_END; + wait_pwd[1] = PWD_OPEN_END; + break; + case HIMAX_INSPECTION_MICRO_OPEN: + wait_pwd[0] = PWD_OPEN_END; + wait_pwd[1] = PWD_OPEN_END; + break; + case HIMAX_INSPECTION_SHORT: + wait_pwd[0] = PWD_SHORT_END; + wait_pwd[1] = PWD_SHORT_END; + break; + case HIMAX_INSPECTION_RAWDATA: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + case HIMAX_INSPECTION_BPN_RAWDATA: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + case HIMAX_INSPECTION_WEIGHT_NOISE: + case HIMAX_INSPECTION_ABS_NOISE: + wait_pwd[0] = PWD_NOISE_END; + wait_pwd[1] = PWD_NOISE_END; + break; + + case HIMAX_INSPECTION_GAPTEST_RAW: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + wait_pwd[0] = PWD_ACT_IDLE_END; + wait_pwd[1] = PWD_ACT_IDLE_END; + break; + + case HIMAX_INSPECTION_LPWUG_RAWDATA: + case HIMAX_INSPECTION_LPWUG_NOISE: + wait_pwd[0] = PWD_LPWUG_END; + wait_pwd[1] = PWD_LPWUG_END; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + wait_pwd[0] = PWD_LPWUG_IDLE_END; + wait_pwd[1] = PWD_LPWUG_IDLE_END; + break; + + default: + I("No Change Mode and now type=%d\n", checktype); + break; + } + + do { + if (g_core_fp.fp_check_sorting_mode != NULL) + g_core_fp.fp_check_sorting_mode(tmp_data); + if ((wait_pwd[0] == tmp_data[0]) && + (wait_pwd[1] == tmp_data[1])) + return 0; + + + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + I(TEMP_LOG, __func__, "0x900000A8", + tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x00; tmp_addr[0] = 0xE4; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + I(TEMP_LOG, __func__, "0x900000E4", + tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x7F; tmp_addr[0] = 0x40; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + I(TEMP_LOG, __func__, "0x10007F40", + tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); +#ifdef HX_112F_SET + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x00; tmp_addr[0] = 0xE8; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + I("%s: 0x900000E8,tmp_data[0]=%x,tmp_data[1]=%x,tmp_data[2]=%x,tmp_data[3]=%x \n", __func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); +#endif + I("Now retry %d times!\n", count++); + msleep(50); + } while (count < 50); + + return 1; +} + +static int hx_turn_on_mp_func(int on) +{ + int rslt = 0; + int retry = 3; + uint8_t tmp_addr[4] = {0}; + uint8_t tmp_data[4] = {0}; + uint8_t tmp_read[4] = {0}; + /* char *tmp_chipname = private_ts->chip_name; */ + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; tmp_addr[1] = 0x73; tmp_addr[0] = 0xEC; + if (on) { + I("%s : Turn on!\n", __func__); + if (strcmp(HX_83102D_SERIES_PWON, private_ts->chip_name) == 0) { + I("%s: need to enter Mp mode!\n", __func__); + tmp_data[3] = 0x00; tmp_data[2] = 0x10; tmp_data[1] = 0x73; tmp_data[0] = 0x80; + do { + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); + usleep_range(10000, 10001); + g_core_fp.fp_register_read(tmp_addr, 4, tmp_read, false); + I("%s: now read[2]=0x%02X, read[1]=0x%02X, read[0]=0x%02X!\n", + __func__, tmp_read[2], tmp_read[1], tmp_read[0]); + retry--; + } while (((retry > 0) && (tmp_read[2] != tmp_data[2] && tmp_read[1] != tmp_data[1] && tmp_read[0] != tmp_data[0]))); + } else { + I("%s:Nothing to be done!\n", __func__); + } + } else { + I("%s : Turn off!\n", __func__); + if (strcmp(HX_83102D_SERIES_PWON, private_ts->chip_name) == 0) { + I("%s: need to enter Mp mode!\n", __func__); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + do { + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); + usleep_range(10000, 10001); + g_core_fp.fp_register_read(tmp_addr, 4, tmp_read, false); + I("%s: now read[2]=0x%02X, read[1]=0x%02X, read[0]=0x%02X!\n", __func__, tmp_read[2], tmp_read[1], tmp_read[0]); + retry--; + } while ((retry > 0) && (tmp_read[2] != tmp_data[2] && tmp_read[1] != tmp_data[1] && tmp_read[0] != tmp_data[0])); + } else { + I("%s Nothing to be done!\n", __func__); + } + } + return rslt; +} + +/* HX_GAP START */ +/* gap test function */ +/* extern int himax_write_to_ic_flash_flow(uint32_t start_addr,uint32_t + * *write_data,uint32_t write_len); + */ + +static int himax_gap_test_vertical_setting(void) +{ + g_gap_vertical_part = kcalloc(g_gap_vertical_partial, + sizeof(int), GFP_KERNEL); + if (g_gap_vertical_part == NULL) { + E("%s: Memory allocation falied!\n", __func__); + return MEM_ALLOC_FAIL; + } + g_gap_vertical_part[0] = 0; + g_gap_vertical_part[1] = 4; + g_gap_vertical_part[2] = 8; + + return NO_ERR; +} + +static void himax_cal_gap_data_vertical(int start, int end_idx, int direct, + uint32_t *org_raw, uint32_t *result_raw) +{ + int i = 0; + int rx_num = ic_data->HX_RX_NUM; + + I("%s:start=%d\n", __func__, start); + I("%s:end_idx=%d\n", __func__, end_idx); + for (i = start; i < (start + rx_num*end_idx); i++) { + if (direct == 0) { /* up - down */ + if (i < start+rx_num) + result_raw[i] = 0; + else + result_raw[i] = org_raw[i-rx_num] - org_raw[i]; + + } else { /* down - up */ + if (i > (start + rx_num*(end_idx-1)-1)) + result_raw[i] = 0; + else + result_raw[i] = org_raw[i+rx_num] - org_raw[i]; + + } + } +} + +static int himax_gap_test_vertical_raw(int test_type, uint32_t *org_raw) +{ + int i_partial = 0; + int tmp_start = 0; + int tmp_end_idx = 0; + uint32_t *result_raw; + int i = 0; + int ret_val = NO_ERR; + + int tx_num = ic_data->HX_TX_NUM; + int rx_num = ic_data->HX_RX_NUM; + + if (himax_gap_test_vertical_setting()) + return MEM_ALLOC_FAIL; + + I("Print vertical ORG RAW\n"); + for (i = 0; i < tx_num*rx_num; i++) { + I("%04d,", org_raw[i]); + if (i > 0 && i%rx_num == (rx_num-1)) + I("\n"); + } + + result_raw = kcalloc(tx_num*rx_num, sizeof(uint32_t), GFP_KERNEL); + if (result_raw == NULL) { + E("%s: Memory allocation falied!\n", __func__); + goto alloc_result_raw_failed; + } + + for (i_partial = 0; i_partial < g_gap_vertical_partial; i_partial++) { + + tmp_start = g_gap_vertical_part[i_partial]*rx_num; + if (i_partial+1 == g_gap_vertical_partial) + tmp_end_idx = tx_num - g_gap_vertical_part[i_partial]; + else + tmp_end_idx = g_gap_vertical_part[i_partial+1] - + g_gap_vertical_part[i_partial]; + + if (i_partial % 2 == 0) + himax_cal_gap_data_vertical(tmp_start, tmp_end_idx, 0, + org_raw, result_raw); + else + himax_cal_gap_data_vertical(tmp_start, tmp_end_idx, 1, + org_raw, result_raw); + + } + + I("Print Vertical New RAW\n"); + for (i = 0; i < tx_num*rx_num; i++) { + I("%04d,", result_raw[i]); + if (i > 0 && i%rx_num == (rx_num-1)) + I("\n"); + } + + for (i = 0; i < tx_num*rx_num; i++) { + if (result_raw[i] < g_inspection_criteria[IDX_GAP_VER_RAWMIN][i] + && + result_raw[i] > g_inspection_criteria[IDX_GAP_VER_RAWMAX][i]) { + ret_val = NO_ERR - i; + break; + } + } + + /* himax_write_to_ic_flash_flow(0x1A000,result_raw,tx_num*rx_num); */ + kfree(result_raw); +alloc_result_raw_failed: + kfree(g_gap_vertical_part); + g_gap_vertical_part = NULL; + + return ret_val; +} + +static int himax_gap_test_horizontal_setting(void) +{ + g_gap_horizontal_part = kcalloc(g_gap_horizontal_partial, + sizeof(int), GFP_KERNEL); + if (g_gap_horizontal_part == NULL) { + E("%s: Memory allocation falied!\n", __func__); + return MEM_ALLOC_FAIL; + } + g_gap_horizontal_part[0] = 0; + g_gap_horizontal_part[1] = 8; + g_gap_horizontal_part[2] = 24; + + return NO_ERR; +} + +static void himax_cal_gap_data_horizontal(int start, int end_idx, int direct, + uint32_t *org_raw, uint32_t *result_raw) +{ + int i = 0; + int j = 0; + int rx_num = ic_data->HX_RX_NUM; + int tx_num = ic_data->HX_TX_NUM; + + I("start=%d\n", start); + I("end_idx=%d\n", end_idx); + for (j = 0; j < tx_num; j++) { + for (i = (start + (j*rx_num)); + i < (start + (j*rx_num) + end_idx); i++) { + /* left - right */ + if (direct == 0) { + if (i == (start + (j*rx_num))) + result_raw[i] = 0; + else + result_raw[i] = + org_raw[i-1] - org_raw[i]; + + } else { /* right - left */ + if (i == ((start + (j*rx_num) + end_idx) - 1)) + result_raw[i] = 0; + else + result_raw[i] = + org_raw[i + 1] - org_raw[i]; + } + } + } +} + +static int himax_gap_test_honrizontal_raw(int test_type, uint32_t *raw) +{ + int rx_num = ic_data->HX_RX_NUM; + int tx_num = ic_data->HX_TX_NUM; + int tmp_start = 0; + int tmp_end_idx = 0; + int i_partial = 0; + uint32_t *result_raw; + int i = 0; + int ret_val = NO_ERR; + + if (himax_gap_test_horizontal_setting()) + return MEM_ALLOC_FAIL; + + result_raw = kcalloc(ic_data->HX_TX_NUM * ic_data->HX_RX_NUM, + sizeof(uint32_t), GFP_KERNEL); + if (result_raw == NULL) { + E("%s: Memory allocation falied!\n", __func__); + goto alloc_result_raw_failed; + } + + I("Print Horizontal ORG RAW\n"); + for (i = 0; i < tx_num*rx_num; i++) { + I("%04d,", raw[i]); + if (i > 0 && i%rx_num == (rx_num-1)) + I("\n"); + } + + for (i_partial = 0; i_partial < g_gap_horizontal_partial; i_partial++) { + tmp_start = g_gap_horizontal_part[i_partial]; + if (i_partial+1 == g_gap_horizontal_partial) + tmp_end_idx = rx_num - g_gap_horizontal_part[i_partial]; + else + tmp_end_idx = g_gap_horizontal_part[i_partial+1] - + g_gap_horizontal_part[i_partial]; + + if (i_partial % 2 == 0) + himax_cal_gap_data_horizontal(tmp_start, tmp_end_idx, + 0, raw, result_raw); + else + himax_cal_gap_data_horizontal(tmp_start, tmp_end_idx, + 1, raw, result_raw); + + } + I("Print Horizontal New RAW\n"); + for (i = 0; i < tx_num*rx_num; i++) { + I("%04d,", result_raw[i]); + if (i > 0 && i%rx_num == (rx_num-1)) + I("\n"); + } + + for (i = 0; i < tx_num*rx_num; i++) { + if (result_raw[i] < g_inspection_criteria[IDX_GAP_HOR_RAWMIN][i] + && + result_raw[i] > g_inspection_criteria[IDX_GAP_HOR_RAWMAX][i]) { + ret_val = NO_ERR - i; + break; + } + } + + /* himax_write_to_ic_flash_flow(0x1A800,result_raw,tx_num*rx_num); */ + kfree(result_raw); +alloc_result_raw_failed: + kfree(g_gap_horizontal_part); + g_gap_horizontal_part = NULL; + + return ret_val; +} +/* HX_GAP END*/ +#define FAIL_IN_INDEX "%s: %s FAIL in index %d\n" +static uint32_t mpTestFunc(uint8_t checktype, uint32_t datalen) +{ + int32_t ret = 0; + uint32_t i/*, j*/; +#ifdef FIX_WVLA + uint32_t *RAW; +#else + uint32_t RAW[datalen]; +#endif + + uint16_t palm_num = 0; + uint16_t noise_count = 0; + int ret_val = -1; +#ifdef FIX_WVLA + RAW = kcalloc(datalen, sizeof(uint32_t), GFP_KERNEL); + if (RAW == NULL) { + E("%s, Failed to allocate memory\n", __func__); + goto alloc_error; + } +#endif + /*uint16_t* pInspectGridData = &gInspectGridData[0];*/ + /*uint16_t* pInspectNoiseData = &gInspectNoiseData[0];*/ + I("Now Check type = %d\n", checktype); + + if (himax_check_mode(checktype)) { + /*himax_check_mode(checktype);*/ + + I("Need Change Mode ,target=%s\n", g_himax_inspection_mode[checktype]); + + g_core_fp.fp_sense_off(true); + hx_turn_on_mp_func(1); + +#ifndef HX_ZERO_FLASH + if (g_core_fp.fp_reload_disable != NULL) + g_core_fp.fp_reload_disable(1); +#endif + + himax_switch_mode_inspection(checktype); + + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_EOPEN; + break; + + case HIMAX_INSPECTION_OPEN: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_EOPEN; + break; + + case HIMAX_INSPECTION_MICRO_OPEN: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_EMOPEN; + break; + + case HIMAX_INSPECTION_SHORT: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_ESHORT; + break; + + case HIMAX_INSPECTION_RAWDATA: + #ifdef HX_112F_SET + himax_set_N_frame(NOISEFRAME, checktype); + #else + himax_set_N_frame(OTHERSFRAME, checktype); + #endif + ret_val = HX_INSPECT_ERAW; + break; + + case HIMAX_INSPECTION_BPN_RAWDATA: + #ifdef HX_112F_SET + himax_set_N_frame(NOISEFRAME, checktype); + #else + himax_set_N_frame(OTHERSFRAME, checktype); + #endif + ret_val = HX_INSPECT_ERAW; + break; + + case HIMAX_INSPECTION_WEIGHT_NOISE: + himax_set_N_frame(NOISEFRAME, checktype); + ret_val = HX_INSPECT_WT_ENOISE; + break; + + case HIMAX_INSPECTION_ABS_NOISE: + himax_set_N_frame(NOISEFRAME, checktype); + ret_val = HX_INSPECT_ABS_ENOISE; + break; + + case HIMAX_INSPECTION_GAPTEST_RAW: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_EGAP_RAW; + break; + + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + himax_set_N_frame(NORMAL_IDLE_RAWDATA_NOISEFRAME, checktype); + ret_val = HX_INSPECT_EACT_IDLE_RAW; + break; + + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + himax_set_N_frame(NORMAL_IDLE_RAWDATA_NOISEFRAME, checktype); + ret_val = HX_INSPECT_EACT_IDLE_NOISE; + break; + + case HIMAX_INSPECTION_LPWUG_RAWDATA: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_ELPWUG_RAW; + break; + + case HIMAX_INSPECTION_LPWUG_NOISE: + himax_set_N_frame(LPWUG_NOISEFRAME, checktype); + ret_val = HX_INSPECT_ELPWUG_NOISE; + break; + + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + himax_set_N_frame(LPWUG_IDLE_RAWDATAFRAME, checktype); + ret_val = HX_INSPECT_ELPWUG_IDLE_RAW; + break; + + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + himax_set_N_frame(LPWUG_IDLE_NOISEFRAME, checktype); + ret_val = HX_INSPECT_ELPWUG_IDLE_NOISE; + break; + + default: + E("UNDEFINED test type %d, exit\n", checktype); + ret_val = 0xFFFFFFFF; + break; + } + +/* + * if (checktype == HIMAX_INSPECTION_WEIGHT_NOISE || + * checktype == HIMAX_INSPECTION_ABS_NOISE) { + * himax_set_N_frame(NOISEFRAME, checktype); + * } else if (checktype == HIMAX_INSPECTION_ACT_IDLE_RAWDATA || + * checktype == HIMAX_INSPECTION_ACT_IDLE_NOISE) { + * I("N frame = %d\n", NORMAL_IDLE_RAWDATA_NOISEFRAME); + * himax_set_N_frame(NORMAL_IDLE_RAWDATA_NOISEFRAME, checktype); + * } else if (checktype == HIMAX_INSPECTION_LPWUG_RAWDATA) { + * I("N frame = %d\n", LPWUG_RAWDATAFRAME); + * himax_set_N_frame(LPWUG_RAWDATAFRAME, checktype); + * } else if (checktype == HIMAX_INSPECTION_LPWUG_NOISE) { + * I("N frame = %d\n", LPWUG_NOISEFRAME); + * himax_set_N_frame(LPWUG_NOISEFRAME, checktype); + * } else if (checktype == HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA) { + * I("N frame = %d\n", LPWUG_IDLE_RAWDATAFRAME); + * himax_set_N_frame(LPWUG_IDLE_RAWDATAFRAME, checktype); + * } else if (checktype == HIMAX_INSPECTION_LPWUG_IDLE_NOISE) { + * I("N frame = %d\n", LPWUG_IDLE_NOISEFRAME); + * himax_set_N_frame(LPWUG_IDLE_NOISEFRAME, checktype); + * } else { + * himax_set_N_frame(OTHERSFRAME, checktype); + * } + */ + + g_core_fp.fp_sense_on(1); + + if (ret_val == 0xFFFFFFFF) + return ret_val; + } + + ret = himax_wait_sorting_mode(checktype); + if (ret) { + E("%s: himax_wait_sorting_mode FAIL\n", __func__); + return ret_val; + } + himax_switch_data_type(checktype); + + ret = himax_get_rawdata(RAW, datalen); + if (ret) { + E("%s: himax_get_rawdata FAIL\n", __func__); + return ret_val | ret; + } + + /*get Max DC from FW*/ + g_dc_max = g_core_fp.fp_get_max_dc(); + + /* back to normal */ + himax_switch_data_type(HIMAX_INSPECTION_BACK_NORMAL); + + I("%s: Init OK, start to test!\n", __func__); + + snprintf(g_start_log, 256 * sizeof(char), "\n%s%s\n", + g_himax_inspection_mode[checktype], ": data as follow!\n"); + + /*Check Data*/ + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] < g_inspection_criteria[IDX_SORTMIN][i]) { + E(FAIL_IN_INDEX, __func__, "sorting mode open test", i); + ret_val = HX_INSPECT_EOPEN; + goto FAIL_END; + } + } + I("%s: sorting mode open test PASS\n", __func__); + break; + + case HIMAX_INSPECTION_OPEN: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_OPENMAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_OPENMIN][i]) { + E(FAIL_IN_INDEX, __func__, "open test", i); + ret_val = HX_INSPECT_EOPEN; + goto FAIL_END; + } + } + I("%s: open test PASS\n", __func__); + break; + + case HIMAX_INSPECTION_MICRO_OPEN: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_M_OPENMAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_M_OPENMIN][i]) { + E(FAIL_IN_INDEX, __func__, "micro open test", i); + ret_val = HX_INSPECT_EMOPEN; + goto FAIL_END; + } + } + I("%s: micro open test PASS\n", __func__); + break; + + case HIMAX_INSPECTION_SHORT: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_SHORTMAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_SHORTMIN][i]) { + E(FAIL_IN_INDEX, __func__, "short test", i); + ret_val = HX_INSPECT_ESHORT; + goto FAIL_END; + } + } + I("%s: short test PASS\n", __func__); + break; + + case HIMAX_INSPECTION_RAWDATA: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + /* I("Now new compare, datalen=%d!\n", + * ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); + */ + if ((int)RAW[i] > g_inspection_criteria[IDX_RAWMAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_RAWMIN][i]) { + E("%s: rawdata test FAIL:RAW[%d]=%d\n", __func__, i, RAW[i]); + I("%s: Now Criteria max=%d,min=%d\n", __func__, + g_inspection_criteria[IDX_RAWMAX][i], + g_inspection_criteria[IDX_RAWMIN][i]); + ret_val = HX_INSPECT_ERAW; + goto FAIL_END; + } + } + I("%s: rawdata test PASS\n", __func__); + break; + + case HIMAX_INSPECTION_BPN_RAWDATA: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + /* I("Now new compare, datalen=%d!\n", + * ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); + */ + RAW[i] = (int)RAW[i] * 100 / g_dc_max; + if ((int)RAW[i] > g_inspection_criteria[IDX_BPN_RAWMAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_BPN_RAWMIN][i]) { + E("%s: rawdata test FAIL:BPN RAW[%d]=%d\n", __func__, i, RAW[i]); + I("%s: Now Criteria max=%d,min=%d\n", __func__, + g_inspection_criteria[IDX_BPN_RAWMAX][i], + g_inspection_criteria[IDX_BPN_RAWMIN][i]); + ret_val = HX_INSPECT_ERAW; + goto FAIL_END; + } + } + I("%s: BPN rawdata test PASS\n", __func__); + break; + + case HIMAX_INSPECTION_WEIGHT_NOISE: + /*I("NOISEMAX=%d\n", NOISEMAX);*/ + noise_count = 0; + himax_get_noise_base(); + palm_num = himax_get_palm_num(); + for (i = 0; i < (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > NOISEMAX) + noise_count++; + } + I("noise_count=%d\n", noise_count); + if (noise_count > palm_num) { + E("%s: noise test FAIL\n", __func__); + ret_val = HX_INSPECT_WT_ENOISE; + goto FAIL_END; + } + snprintf(g_start_log, 256 * sizeof(char), "\n Threshold = %d\n", NOISEMAX); + /*Check weightingt*/ + ret = himax_get_noise_weight_test(); + if (ret < 0) { + I("%s: noise test FAIL %d\n", __func__, ret); + ret_val = HX_INSPECT_WT_ENOISE; + goto FAIL_END; + } + + /*Check negative side noise*/ + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > (g_inspection_criteria[IDX_WT_NOISEMAX][i] * g_recal_thx / 100) + || (int)RAW[i] < (g_inspection_criteria[IDX_WT_NOISEMIN][i] * g_recal_thx / 100)) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_WEIGHT_NOISE", i); + ret_val = HX_INSPECT_WT_ENOISE; + goto FAIL_END; + } + } + I("%s: noise test(weight) PASS\n", __func__); + break; + + case HIMAX_INSPECTION_ABS_NOISE: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_ABS_NOISEMAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_ABS_NOISEMIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_ABS_NOISE", i); + I("%s: Now Criteria max=%d,min=%d\n", __func__, + g_inspection_criteria[IDX_ABS_NOISEMAX][i], + g_inspection_criteria[IDX_ABS_NOISEMIN][i]); + ret_val = HX_INSPECT_ABS_ENOISE; + goto FAIL_END; + } + } + I("%s: noise test(absolute) PASS\n", __func__); + break; + + case HIMAX_INSPECTION_GAPTEST_RAW: + if (himax_gap_test_vertical_raw(HIMAX_INSPECTION_GAPTEST_RAW, RAW) != NO_ERR) { + E("%s: HIMAX_INSPECTION_GAPTEST_RAW FAIL\n", __func__); + ret_val = HX_INSPECT_EGAP_RAW; + goto FAIL_END; + } + if (himax_gap_test_honrizontal_raw(HIMAX_INSPECTION_GAPTEST_RAW, RAW) != NO_ERR) { + E("%s: HIMAX_INSPECTION_GAPTEST_RAW FAIL\n", __func__); + ret_val = HX_INSPECT_EGAP_RAW; + goto FAIL_END; + } + break; + + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_ACT_IDLE_RAWDATA_MAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_ACT_IDLE_RAWDATA_MIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_ACT_IDLE_RAWDATA", i); + ret_val = HX_INSPECT_EACT_IDLE_RAW; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_ACT_IDLE_RAWDATA PASS\n", __func__); + break; + + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_ACT_IDLE_NOISE_MAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_ACT_IDLE_NOISE_MIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_ACT_IDLE_NOISE", i); + ret_val = HX_INSPECT_EACT_IDLE_NOISE; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_ACT_IDLE_NOISE PASS\n", __func__); + break; + + case HIMAX_INSPECTION_LPWUG_RAWDATA: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_LPWUG_RAWDATA_MAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_LPWUG_RAWDATA_MIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_LPWUG_RAWDATA", i); + ret_val = HX_INSPECT_ELPWUG_RAW; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_LPWUG_RAWDATA PASS\n", __func__); + break; + + case HIMAX_INSPECTION_LPWUG_NOISE: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_LPWUG_NOISE_MAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_LPWUG_NOISE_MIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_LPWUG_NOISE", i); + ret_val = HX_INSPECT_ELPWUG_NOISE; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_LPWUG_NOISE PASS\n", __func__); + break; + + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_LPWUG_IDLE_RAWDATA_MAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_LPWUG_IDLE_RAWDATA_MIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA", i); + ret_val = HX_INSPECT_ELPWUG_IDLE_RAW; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA PASS\n", __func__); + break; + + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_LPWUG_IDLE_NOISE_MAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_LPWUG_IDLE_NOISE_MIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_LPWUG_IDLE_NOISE", i); + ret_val = HX_INSPECT_ELPWUG_IDLE_NOISE; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_LPWUG_IDLE_NOISE PASS\n", __func__); + break; + + default: + E("Wrong type=%d\n", checktype); + ret_val = 0xFFFFFFFF; + break; + } + + ret_val = HX_INSPECT_OK; + snprintf(g_rslt_log, 256 * sizeof(char), "\n%s%s\n", + g_himax_inspection_mode[checktype], " Test Pass!\n"); + I("pass write log\n"); + goto END_FUNC; + +FAIL_END: + snprintf(g_rslt_log, 256 * sizeof(char), "\n%s%s\n", + g_himax_inspection_mode[checktype], " Test Fail!\n"); + I("fail write log\n"); +END_FUNC: + hx_test_data_get(RAW, g_start_log, g_rslt_log, checktype); +#ifdef FIX_WVLA +alloc_error: + if (RAW) + kfree(RAW); +#endif + + return ret_val; +} + +/* claculate 10's power function */ +static int himax_power_cal(int pow, int number) +{ + int i = 0; + int result = 1; + + for (i = 0; i < pow; i++) + result *= 10; + result = result * number; + + return result; + +} + +/* String to int */ +static int hiamx_parse_str2int(char *str) +{ + int i = 0; + int temp_cal = 0; + int result = 0; + int str_len = strlen(str); + int negtive_flag = 0; + + for (i = 0; i < strlen(str); i++) { + if (str[i] != '-' && str[i] > '9' && str[i] < '0') { + E("%s: Parsing fail!\n", __func__); + result = -9487; + negtive_flag = 0; + break; + } + if (str[i] == '-') { + negtive_flag = 1; + continue; + } + temp_cal = str[i] - '0'; + result += himax_power_cal(str_len-i-1, temp_cal); + /* str's the lowest char is the number's the highest number + * So we should reverse this number before using the power + * function + * -1: starting number is from 0 ex:10^0 = 1,10^1=10 + */ + } + + if (negtive_flag == 1) + result = 0 - result; + + return result; +} + + +/* Get sub-string from original string by using some charaters + * return size of result + */ +static int himax_saperate_comma(const struct firmware *file_entry, + char **result, int str_size) +{ + int count = 0; + int str_count = 0; /* now string*/ + int char_count = 0; /* now char count in string*/ + + do { + switch (file_entry->data[count]) { + case ASCII_COMMA: + case ACSII_SPACE: + case ASCII_CR: + case ASCII_LF: + count++; + /* If end of line as above condifiton, + * differencing the count of char. + * If char_count != 0 + * it's meaning this string is parsing over . + * The Next char is belong to next string + */ + if (char_count != 0) { + char_count = 0; + str_count++; + } + break; + default: + result[str_count][char_count++] = + file_entry->data[count]; + count++; + break; + } + } while (count < file_entry->size && str_count < str_size); + + return str_count; +} + +static int hx_diff_str(char *str1, char *str2) +{ + int i = 0; + int result = 0; /* zero is all same, non-zero is not same index*/ + int str1_len = strlen(str1); + int str2_len = strlen(str2); + + if (str1_len != str2_len) { + if (private_ts->debug_log_level & BIT(4)) + I("%s:Size different!\n", __func__); + return LENGTH_FAIL; + } + + for (i = 0; i < str1_len; i++) { + if (str1[i] != str2[i]) { + result = i+1; + I("%s: different in %d!\n", __func__, result); + return result; + } + } + + return result; +} + +/* get idx of criteria whe parsing file */ +int hx_find_crtra_id(char *input) +{ + int i = 0; + int result = 0; + + for (i = 0 ; i < HX_CRITERIA_SIZE ; i++) { + if (hx_diff_str(g_hx_inspt_crtra_name[i], input) == 0) { + result = i; + I("find the str=%s,idx=%d\n", + g_hx_inspt_crtra_name[i], i); + break; + } + } + if (i > (HX_CRITERIA_SIZE - 1)) { + E("%s: find Fail!\n", __func__); + return LENGTH_FAIL; + } + + return result; +} + +int hx_print_crtra_after_parsing(void) +{ + int i = 0, j = 0; + int all_mut_len = ic_data->HX_TX_NUM*ic_data->HX_RX_NUM; + + for (i = 0; i < HX_CRITERIA_SIZE; i++) { + I("Now is %s\n", g_hx_inspt_crtra_name[i]); + if (g_inspt_crtra_flag[i] == 1) { + for (j = 0; j < all_mut_len; j++) { + I("%d, ", g_inspection_criteria[i][j]); + if (j % 16 == 15) + PI("\n"); + } + } else { + I("No this Item in this criteria file!\n"); + } + PI("\n"); + } + + return 0; +} + +static int hx_get_crtra_by_name(char **result, int size_of_result_str) +{ + int i = 0; + /* count of criteria type */ + int count_type = 0; + /* count of criteria data */ + int count_data = 0; + int err = HX_INSPECT_OK; + int all_mut_len = ic_data->HX_TX_NUM*ic_data->HX_RX_NUM; + int temp = 0; + + /* get criteria and assign to a global array(2-Dimensional/int) */ + /* basiclly the size of criteria will be + * (crtra_count * (all_mut_len) + crtra_count) + * but we use file size to be the end of counter + */ + for (i = 0; i < size_of_result_str && result[i] != NULL; i++) { + /* It have get one page(all mutual) criteria data! + * And we should skip the string of criteria name! + */ + if (i == 0 || i == + ((i / (all_mut_len))+(i / (all_mut_len) * (all_mut_len)))) { + count_data = 0; + + if (private_ts->debug_log_level & BIT(4)) + I("Now find str=%s ,idx=%d\n", result[i], i); + + /* check the item of criteria is in criteria file + * or not + */ + count_type = hx_find_crtra_id(result[i]); + if (count_type < 0) { + E("1. %s:Name Not match!\n", __func__); + /* E("can recognize[%d]=%s\n", count_type, + * g_hx_inspt_crtra_name[count_type]); + */ + E("get from file[%d]=%s\n", i, result[i]); + E("Please check criteria file again!\n"); + err = HX_INSPECT_EFILE; + goto END_FUNCTION; + } else { + I("Now str=%s, idx=%d\n", + g_hx_inspt_crtra_name[count_type], count_type); + g_inspt_crtra_flag[count_type] = 1; + } + continue; + } + /* change string to int*/ + temp = hiamx_parse_str2int(result[i]); + if (temp != -9487) + g_inspection_criteria[count_type][count_data] = temp; + else { + E("%s: Parsing Fail in %d\n", __func__, i); + E("in range:[%d]=%s\n", count_type, + g_hx_inspt_crtra_name[count_type]); + E("btw, get from file[%d]=%s\n", i, result[i]); + break; + } + /* dbg + * I("[%d]g_inspection_criteria[%d][%d]=%d\n", + * i, count_type, count_data, + * g_inspection_criteria[count_type][count_data]); + */ + count_data++; + + } + + if (private_ts->debug_log_level & BIT(4)) { + /* dbg:print all of criteria from parsing file */ + hx_print_crtra_after_parsing(); + } + + I("Total loop=%d\n", i); +END_FUNCTION: + return err; +} + +static int himax_parse_criteria_file(void) +{ + int err = HX_INSPECT_OK; + const struct firmware *file_entry = NULL; + char **result; + int i = 0; + int crtra_count = HX_CRITERIA_SIZE; + int data_size = 0; /* The maximum of number Data*/ + int all_mut_len = ic_data->HX_TX_NUM*ic_data->HX_RX_NUM; + int str_max_len = 128; + int result_all_len = 0; + int file_size = 0; + int size_of_result_str = 0; + + I("%s,Entering\n", __func__); + if (hx_self_test_file_name == NULL) { + E("file name is NULL\n"); + hx_self_test_file_name = kzalloc(80, GFP_KERNEL); +#if defined(HX_112F_SET) + snprintf(hx_self_test_file_name, 60, "%s_hx_criteria.csv", private_ts->pdata->panel_supplier); +#else + if (private_ts->pdata->panel_supplier) + snprintf(hx_self_test_file_name, 60, "%s_hx_criteria.csv", private_ts->pdata->panel_supplier); + else + snprintf(hx_self_test_file_name, 16, "hx_criteria.csv"); +#endif + I("%s: Use name %s\n", __func__, hx_self_test_file_name); + } + + I("file name = %s\n", hx_self_test_file_name); + /* default path is /system/etc/firmware */ + err = request_firmware(&file_entry, hx_self_test_file_name, private_ts->dev); + if (err < 0) { + E("%s,fail in line%d error code=%d\n", __func__, __LINE__, err); + err = HX_INSPECT_EFILE; + goto END_FUNC_REQ_FAIL; + } + + /* size of criteria include name string */ + data_size = ((all_mut_len) * crtra_count) + crtra_count; + + /* init the array which store original criteria and include + * name string + */ + result = kcalloc(data_size, sizeof(char *), GFP_KERNEL); + if (result != NULL) { + for (i = 0 ; i < data_size; i++) { + result[i] = kcalloc(str_max_len, sizeof(char), GFP_KERNEL); + if (result[i] == NULL) { + E("%s: rst_arr Memory allocation falied!\n", __func__); + err = HX_INSPECT_MEMALLCTFAIL; + goto rst_arr_mem_alloc_failed; + } + } + } else { + E("%s: Memory allocation falied!\n", __func__); + err = HX_INSPECT_MEMALLCTFAIL; + goto rst_mem_alloc_failed; + } + + result_all_len = data_size; + file_size = file_entry->size; + I("Now result_all_len=%d\n", result_all_len); + I("Now file_size=%d\n", file_size); + + /* dbg */ + if (private_ts->debug_log_level & BIT(4)) { + I("first 4 bytes 0x%2X,0x%2X,0x%2X,0x%2X !\n", + file_entry->data[0], file_entry->data[1], + file_entry->data[2], file_entry->data[3]); + } + + /* parse value in to result array(1-Dimensional/String) */ + size_of_result_str = + himax_saperate_comma(file_entry, result, data_size); + + I("%s: now size_of_result_str=%d\n", __func__, size_of_result_str); + + err = hx_get_crtra_by_name(result, size_of_result_str); + if (err != HX_INSPECT_OK) { + E("%s:Load criteria from file fail, go end!\n", __func__); + goto END_FUNC; + } + + +END_FUNC: +rst_arr_mem_alloc_failed: + for (i = 0 ; i < data_size; i++) + if (result[i] != NULL) + kfree(result[i]); +rst_mem_alloc_failed: + kfree(result); + release_firmware(file_entry); +END_FUNC_REQ_FAIL: + I("%s,END\n", __func__); + return err; + /* parsing Criteria end */ +} + + +int hx_get_size_str_arr(char **input) +{ + int i = 0; + int result = 0; + + while (input[i] != NULL) + i++; + + result = i; + if (private_ts->debug_log_level & BIT(4)) + I("There is %d in [0]=%s\n", result, input[0]); + + return result; +} + +static int himax_self_test_data_init(void) +{ + int ret = HX_INSPECT_OK; + int i = 0; + + /* + * 5: one value will not over than 99999, so get this size of string + * 2: get twice size + */ + g_1kind_raw_size = 5 * ic_data->HX_RX_NUM * ic_data->HX_RX_NUM * 2; + + /* get test item and its items of criteria*/ + HX_CRITERIA_ITEM = hx_get_size_str_arr(g_himax_inspection_mode); + HX_CRITERIA_SIZE = hx_get_size_str_arr(g_hx_inspt_crtra_name); + I("There is %d HX_CRITERIA_ITEM and %d HX_CRITERIA_SIZE\n", + HX_CRITERIA_ITEM, HX_CRITERIA_SIZE); + + /* init criteria data*/ + g_inspt_crtra_flag = kzalloc(HX_CRITERIA_SIZE*sizeof(int), GFP_KERNEL); + g_inspection_criteria = kzalloc(sizeof(int *)*HX_CRITERIA_SIZE, GFP_KERNEL); + if (g_inspt_crtra_flag == NULL || g_inspection_criteria == NULL) { + E("%s: %d, Memory allocation falied!\n", __func__, __LINE__); + return MEM_ALLOC_FAIL; + } + + for (i = 0; i < HX_CRITERIA_SIZE; i++) { + g_inspection_criteria[i] = kcalloc( + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM), sizeof(int), GFP_KERNEL); + if (g_inspection_criteria[i] == NULL) { + E("%s: %d, Memory allocation falied!\n", __func__, __LINE__); + return MEM_ALLOC_FAIL; + } + } + + /* parsing criteria from file*/ + ret = himax_parse_criteria_file(); + + if (private_ts->debug_log_level & BIT(4)) { + /* print get criteria string */ + for (i = 0 ; i < HX_CRITERIA_SIZE ; i++) { + if (g_inspt_crtra_flag[i] != 0) + I("%s: [%d]There is String=%s\n", + __func__, i, g_hx_inspt_crtra_name[i]); + } + } + + if (g_rslt_data == NULL) { + g_rslt_data = kcalloc(g_1kind_raw_size * HX_CRITERIA_ITEM, + sizeof(char), GFP_KERNEL); + I("g_rslt_data is NULL"); + } else { + memset(g_rslt_data, 0x00, g_1kind_raw_size * HX_CRITERIA_ITEM * + sizeof(char)); + } + + snprintf(g_file_path, + (int)(strlen(HX_RSLT_OUT_PATH) + strlen(HX_RSLT_OUT_FILE)+1), + "%s%s", HX_RSLT_OUT_PATH, HX_RSLT_OUT_FILE); + + return ret; +} + +static void himax_self_test_data_deinit(void) +{ + int i = 0; + + /*dbg*/ + /* for (i = 0; i < HX_CRITERIA_ITEM; i++) + * I("%s:[%d]%d\n", __func__, i, g_inspection_criteria[i]); + */ + if (g_inspection_criteria != NULL) { + for (i = 0; i < HX_CRITERIA_SIZE; i++) { + if (g_inspection_criteria[i] != NULL) + kfree(g_inspection_criteria[i]); + } + kfree(g_inspection_criteria); + I("Now it have free the g_inspection_criteria!\n"); + } else { + I("No Need to free g_inspection_criteria!\n"); + } + + g_rslt_data_len = 0; + +} + +static int himax_chip_self_test(void) +{ + uint32_t ret = HX_INSPECT_OK; + uint8_t tmp_addr[DATA_LEN_4] = {0x94, 0x72, 0x00, 0x10}; + uint8_t tmp_data[DATA_LEN_4] = {0x01, 0x00, 0x00, 0x00}; + +#if defined(HIMAX_V2_MULTI_BIN)||defined(HX_CODE_OVERLAY) + uint8_t normalfw[32] = "Himax_firmware.bin"; + uint8_t mpapfw[32] = "Himax_mpfw.bin"; +#ifndef HX_112F_SET + if (private_ts->pdata->panel_supplier) +#endif + { + snprintf(normalfw, 32, "%s_Himax_firmware.bin", private_ts->pdata->panel_supplier); + snprintf(mpapfw, 32, "%s_Himax_mpfw.bin", private_ts->pdata->panel_supplier); + } +#endif + + I("%s:IN\n", __func__); + + private_ts->suspend_resume_done = 0; + + ret = himax_self_test_data_init(); + if (ret != HX_INSPECT_OK) { + E("%s: himax_self_test_data_init fail!\n", __func__); + goto END_FUNC; + } + +#if defined(HIMAX_V2_MULTI_BIN)||defined(HX_CODE_OVERLAY) + g_core_fp.fp_0f_op_file_dirly(mpapfw); + g_core_fp.fp_reload_disable(0); + g_core_fp.fp_sense_on(0x00); +#endif + + if (g_inspt_crtra_flag[IDX_OPENMIN] == 1 && + g_inspt_crtra_flag[IDX_OPENMAX] == 1) { + /*1. Open Test*/ + I("[MP_OPEN_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_OPEN, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("1. Open Test: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_OPENMIN], + g_inspt_crtra_flag[IDX_OPENMIN]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_OPENMAX], + g_inspt_crtra_flag[IDX_OPENMAX]); + } + + if (g_inspt_crtra_flag[IDX_M_OPENMIN] == 1 && + g_inspt_crtra_flag[IDX_M_OPENMAX] == 1) { + /*2. Micro-Open Test*/ + I("[MP_MICRO_OPEN_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_MICRO_OPEN, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("2. Micro Open Test: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_M_OPENMIN], + g_inspt_crtra_flag[IDX_M_OPENMIN]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_M_OPENMAX], + g_inspt_crtra_flag[IDX_M_OPENMAX]); + } + + if (g_inspt_crtra_flag[IDX_SHORTMIN] == 1 && + g_inspt_crtra_flag[IDX_SHORTMAX] == 1) { + /*3. Short Test*/ + I("[MP_SHORT_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_SHORT, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("3. Short Test: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_SHORTMIN], + g_inspt_crtra_flag[IDX_SHORTMIN]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_SHORTMAX], + g_inspt_crtra_flag[IDX_SHORTMAX]); + } + + if (g_inspt_crtra_flag[IDX_RAWMIN] == 1 && + g_inspt_crtra_flag[IDX_RAWMAX] == 1) { + /*4. RawData Test*/ + I("==========================================\n"); + I("[MP_RAW_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_RAWDATA, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("%d. %s: End %d\n\n\n", HIMAX_INSPECTION_RAWDATA, + g_himax_inspection_mode[HIMAX_INSPECTION_RAWDATA], ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_RAWMIN], + g_inspt_crtra_flag[IDX_RAWMIN]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_RAWMAX], + g_inspt_crtra_flag[IDX_RAWMAX]); + } + + if (g_inspt_crtra_flag[IDX_BPN_RAWMIN] == 1 && + g_inspt_crtra_flag[IDX_BPN_RAWMAX] == 1) { + /*4. RawData BPN Test*/ + I("==========================================\n"); + I("[MP_BPN_RAW_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_BPN_RAWDATA, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("%d. %s: End %d\n\n\n", HIMAX_INSPECTION_BPN_RAWDATA, + g_himax_inspection_mode[HIMAX_INSPECTION_BPN_RAWDATA], ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_BPN_RAWMIN], + g_inspt_crtra_flag[IDX_BPN_RAWMIN]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_BPN_RAWMAX], + g_inspt_crtra_flag[IDX_BPN_RAWMAX]); + } + + if (g_inspt_crtra_flag[IDX_WT_NOISEMIN] == 1 && + g_inspt_crtra_flag[IDX_WT_NOISEMAX] == 1) { + /*5. Noise Test by Weighting*/ + I("[MP_NOISE_TEST_WEIGHT]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_WEIGHT_NOISE, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("5. Noise Test: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_WT_NOISEMAX], + g_inspt_crtra_flag[IDX_WT_NOISEMAX]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_WT_NOISEMIN], + g_inspt_crtra_flag[IDX_WT_NOISEMIN]); + } + + if (g_inspt_crtra_flag[IDX_ABS_NOISEMIN] == 1 && + g_inspt_crtra_flag[IDX_ABS_NOISEMAX] == 1) { + /*5. Noise Test by per block*/ + I("[MP_NOISE_TEST_ABS]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_ABS_NOISE, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("5. Noise Test: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_ABS_NOISEMAX], + g_inspt_crtra_flag[IDX_ABS_NOISEMAX]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_ABS_NOISEMIN], + g_inspt_crtra_flag[IDX_ABS_NOISEMIN]); + } + + if (g_inspt_crtra_flag[IDX_SORTMIN] == 1 && + g_inspt_crtra_flag[IDX_SORTMAX] == 1) { + /*6. Sorting Test*/ + I("[SORTING TEST]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_SORTING, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("6. SORTING TEST: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_SORTMIN], + g_inspt_crtra_flag[IDX_SORTMIN]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_SORTMAX], + g_inspt_crtra_flag[IDX_SORTMAX]); + } + + if ((g_inspt_crtra_flag[IDX_GAP_HOR_RAWMAX] == 1 && + g_inspt_crtra_flag[IDX_GAP_HOR_RAWMIN] == 1) && + (g_inspt_crtra_flag[IDX_GAP_VER_RAWMAX] == 1 && + g_inspt_crtra_flag[IDX_GAP_VER_RAWMIN] == 1)) { + /*6. GAP Test*/ + I("[MP_GAP_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_GAPTEST_RAW, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("6. MP_GAP_TEST_RAW: End %d\n\n\n", ret); + } else { + I("Now %s : HOR flag=%d\n", + g_hx_inspt_crtra_name[IDX_GAP_HOR_RAWMAX], + g_inspt_crtra_flag[IDX_GAP_HOR_RAWMAX]); + I("Now %s : HOR flag=%d\n", + g_hx_inspt_crtra_name[IDX_GAP_HOR_RAWMIN], + g_inspt_crtra_flag[IDX_GAP_HOR_RAWMIN]); + I("Now %s : VERTICAL flag=%d\n", + g_hx_inspt_crtra_name[IDX_GAP_VER_RAWMAX], + g_inspt_crtra_flag[IDX_GAP_VER_RAWMAX]); + I("Now %s : VERTICAL flag=%d\n", + g_hx_inspt_crtra_name[IDX_GAP_VER_RAWMIN], + g_inspt_crtra_flag[IDX_GAP_VER_RAWMIN]); + } + + if (g_inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MIN] == 1 && + g_inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MAX] == 1) { + /*7. ACT_IDLE RAWDATA*/ + I("[MP_ACT_IDLE_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_ACT_IDLE_RAWDATA, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("7. MP_ACT_IDLE_TEST_RAW: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_ACT_IDLE_RAWDATA_MAX], + g_inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MAX]); + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_ACT_IDLE_RAWDATA_MIN], + g_inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MIN]); + } + + if (g_inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MIN] == 1 && + g_inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MAX] == 1) { + /*8. ACT_IDLE NOISE*/ + I("[MP_ACT_IDLE_TEST_NOISE]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_ACT_IDLE_NOISE, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("8. MP_ACT_IDLE_TEST_NOISE: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_ACT_IDLE_NOISE_MAX], + g_inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MAX]); + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_ACT_IDLE_NOISE_MIN], + g_inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MIN]); + } + + /* check press power key or not for LPWUG test item*/ + if ((g_inspt_crtra_flag[IDX_LPWUG_NOISE_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_NOISE_MIN] == 1) || + (g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MIN] == 1) || + (g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MIN] == 1) || + (g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MIN] == 1) + ) + himax_press_powerkey(); + +/* Workaround delete */ +#if 0 + /* Wait suspend done */ + while (private_ts->suspend_resume_done != 1) + usleep_range(1000, 1001); + + private_ts->suspend_resume_done = 0; +#endif + + if (g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MIN] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MAX] == 1) { + /*9. LPWUG RAWDATA*/ + I("[MP_LPWUG_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_LPWUG_RAWDATA, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("9. MP_LPWUG_TEST_RAW: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_RAWDATA_MIN], + g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MIN]); + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_RAWDATA_MAX], + g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MAX]); + } + + if (g_inspt_crtra_flag[IDX_LPWUG_NOISE_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_NOISE_MIN] == 1) { + /*10. LPWUG NOISE*/ + I("[MP_LPWUG_TEST_NOISE]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_LPWUG_NOISE, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("10. MP_LPWUG_TEST_NOISE: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_NOISE_MAX], + g_inspt_crtra_flag[IDX_LPWUG_NOISE_MAX]); + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_NOISE_MIN], + g_inspt_crtra_flag[IDX_LPWUG_NOISE_MIN]); + } + + if (g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MIN] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MAX] == 1) { + /*11. LPWUG IDLE RAWDATA*/ + I("[MP_LPWUG_IDLE_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("11. MP_LPWUG_IDLE_TEST_RAW: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_IDLE_RAWDATA_MIN], + g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MIN]); + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_IDLE_RAWDATA_MAX], + g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MAX]); + } + + if (g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MIN] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MAX] == 1) { + /*12. LPWUG IDLE RAWDATA*/ + I("[MP_LPWUG_IDLE_TEST_NOISE]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_LPWUG_IDLE_NOISE, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("12. MP_LPWUG_IDLE_TEST_NOISE: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_IDLE_NOISE_MIN], + g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MIN]); + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_IDLE_NOISE_MAX], + g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MAX]); + } + + /* check press power key or not for LPWUG test item*/ + if ((g_inspt_crtra_flag[IDX_LPWUG_NOISE_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_NOISE_MIN] == 1) || + (g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MIN] == 1) || + (g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MIN] == 1) || + (g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MIN] == 1) + ) + himax_press_powerkey(); + + hx_test_data_pop_out(g_rslt_data, g_file_path); + +/* Workaround delete */ +#if 0 + /* Wait resume done */ + while (private_ts->suspend_resume_done != 1) + usleep_range(1000, 1001); +#endif + +#if defined(HIMAX_V2_MULTI_BIN)||defined(HX_CODE_OVERLAY) + private_ts->in_self_test = 0; + g_core_fp.fp_0f_op_file_dirly(normalfw); + hx_turn_on_mp_func(0); + /* set N frame back to default value 1*/ + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); + g_core_fp.fp_reload_disable(0); + g_core_fp.fp_sense_on(0); +#else + g_core_fp.fp_sense_off(true); + hx_turn_on_mp_func(0); + /*himax_set_N_frame(1, HIMAX_INSPECTION_WEIGHT_NOISE);*/ + /* set N frame back to default value 1*/ + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); +#ifndef HX_ZERO_FLASH + if (g_core_fp.fp_reload_disable != NULL) + g_core_fp.fp_reload_disable(0); +#endif + g_core_fp.fp_sense_on(0); +#endif + +END_FUNC: + himax_self_test_data_deinit(); + + + I("running status = %d\n", ret); + + /*if (ret != 0)*/ + /*ret = 1;*/ + + I("%s:OUT\n", __func__); + return ret; +} + +void himax_inspect_data_clear(void) +{ + if (!g_rslt_data) { + kfree(g_rslt_data); + g_rslt_data = NULL; + } +} + +void himax_inspection_init(void) +{ + I("%s: enter, %d\n", __func__, __LINE__); + + g_core_fp.fp_chip_self_test = himax_chip_self_test; +} diff --git a/drivers/input/touchscreen/himax_0flash_mmi/himax_inspection.h b/drivers/input/touchscreen/himax_0flash_mmi/himax_inspection.h new file mode 100644 index 000000000000..a1716f2cc48a --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/himax_inspection.h @@ -0,0 +1,220 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for inspection functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_platform.h" +#include "himax_common.h" +#include "himax_ic_core.h" + +/*#define HX_GAP_TEST*/ +/*#define HX_INSPECT_LPWUG_TEST*/ +/*#define HX_ACT_IDLE_TEST*/ + +#define HX_RSLT_OUT_PATH "/sdcard/" +#define HX_RSLT_OUT_FILE "hx_test_result.txt" +#define PI(x...) pr_info(x) +#define PD(x...) pr_debug(x) + +#ifdef HX_ESD_RECOVERY + extern u8 HX_ESD_RESET_ACTIVATE; +#endif +#ifdef HX_112F_SET +#define BS_RAWDATA 8 +#define BS_NOISE 8 +#else +#define BS_RAWDATA 10 +#define BS_NOISE 10 +#endif +#define BS_OPENSHORT 0 +#define BS_LPWUG 1 +#define BS_LPWUG_dile 1 +#define BS_ACT_IDLE 1 + +/* skip notch & dummy */ +#define SKIP_NOTCH_START 5 +#define SKIP_NOTCH_END 10 +/* TX+SKIP_NOTCH_START */ +#define SKIP_DUMMY_START 23 +/* TX+SKIP_NOTCH_END*/ +#define SKIP_DUMMY_END 28 + +#ifdef HX_112F_SET +#define NOISEFRAME 60 +#else +#define NOISEFRAME (BS_NOISE+1) +#endif + +#define NORMAL_IDLE_RAWDATA_NOISEFRAME 10 +#define LPWUG_RAWDATAFRAME 1 +#define LPWUG_NOISEFRAME 1 +#define LPWUG_IDLE_RAWDATAFRAME 1 +#define LPWUG_IDLE_NOISEFRAME 1 + +#define OTHERSFRAME 2 + +#define UNIFMAX 500 + + +/*Himax MP Password*/ +#define PWD_OPEN_START 0x77 +#define PWD_OPEN_END 0x88 +#define PWD_SHORT_START 0x11 +#define PWD_SHORT_END 0x33 +#define PWD_RAWDATA_START 0x00 +#define PWD_RAWDATA_END 0x99 +#define PWD_NOISE_START 0x00 +#define PWD_NOISE_END 0x99 +#define PWD_SORTING_START 0xAA +#define PWD_SORTING_END 0xCC + + +#define PWD_ACT_IDLE_START 0x22 +#define PWD_ACT_IDLE_END 0x44 + + + +#define PWD_LPWUG_START 0x55 +#define PWD_LPWUG_END 0x66 + +#define PWD_LPWUG_IDLE_START 0x50 +#define PWD_LPWUG_IDLE_END 0x60 + + +/*Himax DataType*/ +#define DATA_SORTING 0x0A +#define DATA_OPEN 0x0B +#define DATA_MICRO_OPEN 0x0C +#define DATA_SHORT 0x0A +#define DATA_RAWDATA 0x0A +#define DATA_NOISE 0x0F +#define DATA_BACK_NORMAL 0x00 +#define DATA_LPWUG_RAWDATA 0x0C +#define DATA_LPWUG_NOISE 0x0F +#define DATA_ACT_IDLE_RAWDATA 0x0A +#define DATA_ACT_IDLE_NOISE 0x0F +#define DATA_LPWUG_IDLE_RAWDATA 0x0A +#define DATA_LPWUG_IDLE_NOISE 0x0F + +/*Himax Data Ready Password*/ +#define Data_PWD0 0xA5 +#define Data_PWD1 0x5A + +/* ASCII format */ +#define ASCII_LF (0x0A) +#define ASCII_CR (0x0D) +#define ASCII_COMMA (0x2C) +#define ASCII_ZERO (0x30) +#define CHAR_EL '\0' +#define CHAR_NL '\n' +#define ACSII_SPACE (0x20) +/* INSOECTION Setting */ +/*extern int HX_CRITERIA_ITEM;*/ +/*extern int HX_CRITERIA_SIZE;*/ +/*extern char *g_file_path;*/ +/*extern char *g_rslt_data;*/ +void himax_inspection_init(void); +extern int *g_inspt_crtra_flag; + +enum THP_INSPECTION_ENUM { + HIMAX_INSPECTION_OPEN, + HIMAX_INSPECTION_MICRO_OPEN, + HIMAX_INSPECTION_SHORT, + HIMAX_INSPECTION_RAWDATA, + HIMAX_INSPECTION_BPN_RAWDATA, + HIMAX_INSPECTION_WEIGHT_NOISE, + HIMAX_INSPECTION_ABS_NOISE, + HIMAX_INSPECTION_SORTING, + HIMAX_INSPECTION_BACK_NORMAL, + + HIMAX_INSPECTION_GAPTEST_RAW, + + HIMAX_INSPECTION_ACT_IDLE_RAWDATA, + HIMAX_INSPECTION_ACT_IDLE_NOISE, + + HIMAX_INSPECTION_LPWUG_RAWDATA, + HIMAX_INSPECTION_LPWUG_NOISE, + HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA, + HIMAX_INSPECTION_LPWUG_IDLE_NOISE, + +}; + + +enum HX_CRITERIA_ENUM { + IDX_RAWMIN = 0, + IDX_RAWMAX, + IDX_BPN_RAWMIN, + IDX_BPN_RAWMAX, + IDX_SHORTMIN, + IDX_SHORTMAX, + IDX_OPENMIN, + IDX_OPENMAX, + IDX_M_OPENMIN, + IDX_M_OPENMAX, + IDX_WT_NOISEMIN, + IDX_WT_NOISEMAX, + IDX_ABS_NOISEMIN, + IDX_ABS_NOISEMAX, + IDX_SORTMIN, + IDX_SORTMAX, + + IDX_GAP_HOR_RAWMAX, + IDX_GAP_HOR_RAWMIN, + IDX_GAP_VER_RAWMAX, + IDX_GAP_VER_RAWMIN, + + IDX_ACT_IDLE_NOISE_MIN, + IDX_ACT_IDLE_NOISE_MAX, + IDX_ACT_IDLE_RAWDATA_MIN, + IDX_ACT_IDLE_RAWDATA_MAX, + + IDX_LPWUG_NOISE_MIN, + IDX_LPWUG_NOISE_MAX, + IDX_LPWUG_RAWDATA_MIN, + IDX_LPWUG_RAWDATA_MAX, + + IDX_LPWUG_IDLE_NOISE_MIN, + IDX_LPWUG_IDLE_NOISE_MAX, + IDX_LPWUG_IDLE_RAWDATA_MIN, + IDX_LPWUG_IDLE_RAWDATA_MAX, + +}; + +/* Error code of Inspection */ +enum HX_INSPECT_ERR_ENUM { + HX_INSPECT_OK = 0x00000000, /* OK */ + HX_INSPECT_EFILE = 0x00000001, /*Criteria file error*/ + HX_INSPECT_ERAW = 0x00000002, /* Raw data error */ + HX_INSPECT_WT_ENOISE = 0x00000004, /* Noise error */ + HX_INSPECT_ABS_ENOISE = 0x00000008, /* Noise error */ + HX_INSPECT_EOPEN = 0x00000010, /* Sensor open error */ + HX_INSPECT_EMOPEN = 0x00000020, /* Sensor micro open error */ + HX_INSPECT_ESHORT = 0x00000040, /* Sensor short error */ + + HX_INSPECT_EGAP_RAW = 0x00000080, /* Raw Data GAP */ + + HX_INSPECT_EACT_IDLE_RAW = 0x00000100, /* ACT_IDLE RAW ERROR */ + HX_INSPECT_EACT_IDLE_NOISE = 0x00000200, /* ACT_IDLE NOISE ERROR */ + + HX_INSPECT_ELPWUG_RAW = 0x00000400, /* LPWUG RAW ERROR */ + HX_INSPECT_ELPWUG_NOISE = 0x00000800, /* LPWUG NOISE ERROR */ + HX_INSPECT_ELPWUG_IDLE_RAW = 0x00001000, /* LPWUG IDLE RAW ERROR */ + HX_INSPECT_ELPWUG_IDLE_NOISE = 0x00002000, /* LPWUG IDLE NOISE ERROR */ + + HX_INSPECT_EGETRAW = 0x00004000, /* Get raw data errors */ + HX_INSPECT_MEMALLCTFAIL = 0x00008000, /* Get raw data errors */ + /*HX_INSPECT_ESPI = , */ /*SPI communication error,no use*/ +}; + +extern void himax_inspect_data_clear(void); +extern char *hx_self_test_file_name; diff --git a/drivers/input/touchscreen/himax_0flash_mmi/himax_modular_table.h b/drivers/input/touchscreen/himax_0flash_mmi/himax_modular_table.h new file mode 100644 index 000000000000..180d823d581f --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/himax_modular_table.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for modularize functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef __HIMAX_MODULAR_TABLE_H__ +#define __HIMAX_MODULAR_TABLE_H__ + +#include "himax_ic_core.h" + +#define TO_STR(VAR) #VAR +#define DECLARE(sym) struct himax_chip_entry sym; \ + EXPORT_SYMBOL(sym) + +static const char * const himax_ksym_lookup[] = { + #if defined(HX_MOD_KSYM_HX852xG) + TO_STR(HX_MOD_KSYM_HX852xG), + #endif + #if defined(HX_MOD_KSYM_HX852xH) + TO_STR(HX_MOD_KSYM_HX852xH), + #endif + #if defined(HX_MOD_KSYM_HX83102) + TO_STR(HX_MOD_KSYM_HX83102), + #endif + #if defined(HX_MOD_KSYM_HX83103) + TO_STR(HX_MOD_KSYM_HX83103), + #endif + #if defined(HX_MOD_KSYM_HX83106) + TO_STR(HX_MOD_KSYM_HX83106), + #endif + #if defined(HX_MOD_KSYM_HX83111) + TO_STR(HX_MOD_KSYM_HX83111), + #endif + #if defined(HX_MOD_KSYM_HX83112) + TO_STR(HX_MOD_KSYM_HX83112), + #endif + #if defined(HX_MOD_KSYM_HX83113) + TO_STR(HX_MOD_KSYM_HX83113), + #endif + #if defined(HX_MOD_KSYM_HX83191) + TO_STR(HX_MOD_KSYM_HX83191), + #endif + NULL +}; + +static struct himax_chip_entry *get_chip_entry_by_index(int32_t idx) +{ + return (void *)kallsyms_lookup_name(himax_ksym_lookup[idx]); +} + +enum modular_table { + MODULE_NOT_FOUND = -1, + MODULE_FOUND, + MODULE_EMPTY, +}; + +/* + * Return 1 when specified entry is empty, 0 when not, -1 when index error + */ +static int32_t isEmpty(int32_t idx) +{ + int32_t size = sizeof(himax_ksym_lookup) / sizeof(char *); + struct himax_chip_entry *entry; + + if (idx < 0 || idx >= size) + return MODULE_NOT_FOUND; + + entry = get_chip_entry_by_index(idx); + if (entry) + return (entry->core_chip_dt == NULL)?MODULE_EMPTY:MODULE_FOUND; + + return MODULE_NOT_FOUND; +} + +/* + * Search for created entry, if not existed, return 1st + * Return index of himax_ksym_lookup + */ +static int32_t himax_get_ksym_idx(void) +{ + int32_t i, first = -1; + int32_t size = sizeof(himax_ksym_lookup) / sizeof(char *); + struct himax_chip_entry *entry; + + I("%s: symtable size: %d\n", __func__, size); + for (i = 0; i < size; i++) { + if (himax_ksym_lookup[i] == NULL) + break; + + I("%s: %s\n", __func__, himax_ksym_lookup[i]); + entry = get_chip_entry_by_index(i); + if (entry) { + if (first < 0) + first = i; + if (isEmpty(i) == 0) + return i; + } + } + if (first >= 0) + return first; + /*incorrect use state, means no ic defined*/ + return MODULE_NOT_FOUND; +} + +#endif diff --git a/drivers/input/touchscreen/himax_0flash_mmi/himax_platform.h b/drivers/input/touchscreen/himax_0flash_mmi/himax_platform.h new file mode 100644 index 000000000000..924b4c8de25f --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/himax_platform.h @@ -0,0 +1,5 @@ +#ifdef __HIMAX_PLATFORM_SPI__ +#include "himax_platform_QCT_SPI.h" +#else +#include "himax_platform_QCT_I2C.h" +#endif diff --git a/drivers/input/touchscreen/himax_0flash_mmi/himax_platform_QCT_I2C.c b/drivers/input/touchscreen/himax_0flash_mmi/himax_platform_QCT_I2C.c new file mode 100644 index 000000000000..316e6914d2f8 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/himax_platform_QCT_I2C.c @@ -0,0 +1,972 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for QCT platform + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_platform.h" +#include "himax_common.h" + +int i2c_error_count; +int irq_enable_count; +EXPORT_SYMBOL(irq_enable_count); + +int himax_dev_set(struct himax_ts_data *ts) +{ + int ret = 0; + + + ts->input_dev = input_allocate_device(); + + if (ts->input_dev == NULL) { + ret = -ENOMEM; + E("%s: Failed to allocate input device-input_dev\n", __func__); + return ret; + } + + ts->input_dev->name = "himax-touchscreen"; +#if defined(HX_PEN_FUNC_EN) + ts->hx_pen_dev = input_allocate_device(); + + if (ts->hx_pen_dev == NULL) { + ret = -ENOMEM; + E("%s: Failed to allocate input device-hx_pen_dev\n", __func__); + return ret; + } + + ts->hx_pen_dev->name = "himax-pen"; +#endif + return ret; +} +int himax_input_register_device(struct input_dev *input_dev) +{ + return input_register_device(input_dev); +} + +#if defined(HX_PLATFOME_DEFINE_KEY) +void himax_platform_key(void) +{ + I("Nothing to be done! Plz cancel it!\n"); +} +#endif + +void himax_vk_parser(struct device_node *dt, + struct himax_i2c_platform_data *pdata) +{ + u32 data = 0; + uint8_t cnt = 0, i = 0; + uint32_t coords[4] = {0}; + struct device_node *node, *pp = NULL; + struct himax_virtual_key *vk; + + + node = of_parse_phandle(dt, "virtualkey", 0); + + if (node == NULL) { + I(" DT-No vk info in DT\n"); + } else { + while ((pp = of_get_next_child(node, pp))) + cnt++; + + if (!cnt) + return; + + vk = kcalloc(cnt, sizeof(struct himax_virtual_key), GFP_KERNEL); + pp = NULL; + + while ((pp = of_get_next_child(node, pp))) { + if (of_property_read_u32(pp, "idx", &data) == 0) + vk[i].index = data; + + if (of_property_read_u32_array(pp, "range", coords, 4) == 0) { + vk[i].x_range_min = coords[0], vk[i].x_range_max = coords[1]; + vk[i].y_range_min = coords[2], vk[i].y_range_max = coords[3]; + } else { + I(" range faile\n"); + } + + i++; + } + + pdata->virtual_key = vk; + + for (i = 0; i < cnt; i++) + I(" vk[%d] idx:%d x_min:%d, y_max:%d\n", i, pdata->virtual_key[i].index, + pdata->virtual_key[i].x_range_min, pdata->virtual_key[i].y_range_max); + } +} + +int himax_parse_dt(struct himax_ts_data *ts, + struct himax_i2c_platform_data *pdata) +{ + int rc, coords_size = 0; + uint32_t coords[4] = {0}; + struct property *prop; + struct device_node *dt = private_ts->client->dev.of_node; + u32 data = 0; + + + prop = of_find_property(dt, "himax,panel-coords", NULL); + + if (prop) { + coords_size = prop->length / sizeof(u32); + + if (coords_size != 4) + D(" %s:Invalid panel coords size %d\n", __func__, coords_size); + } + + if (of_property_read_u32_array(dt, "himax,panel-coords", coords, coords_size) == 0) { + pdata->abs_x_min = coords[0], pdata->abs_x_max = (coords[1] - 1); + pdata->abs_y_min = coords[2], pdata->abs_y_max = (coords[3] - 1); + I(" DT-%s:panel-coords = %d, %d, %d, %d\n", __func__, pdata->abs_x_min, + pdata->abs_x_max, pdata->abs_y_min, pdata->abs_y_max); + } + + prop = of_find_property(dt, "himax,display-coords", NULL); + + if (prop) { + coords_size = prop->length / sizeof(u32); + + if (coords_size != 4) + D(" %s:Invalid display coords size %d\n", __func__, coords_size); + } + + rc = of_property_read_u32_array(dt, "himax,display-coords", coords, coords_size); + + if (rc && (rc != -EINVAL)) { + D(" %s:Fail to read display-coords %d\n", __func__, rc); + return rc; + } + + pdata->screenWidth = coords[1]; + pdata->screenHeight = coords[3]; + I(" DT-%s:display-coords = (%d, %d)\n", __func__, pdata->screenWidth, + pdata->screenHeight); + pdata->gpio_irq = of_get_named_gpio(dt, "himax,irq-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_irq)) + I(" DT:gpio_irq value is not valid\n"); + +#ifdef HX_SHARP_ENABLE_PON + pdata->gpio_reset = 262; +#else + pdata->gpio_reset = of_get_named_gpio(dt, "himax,rst-gpio", 0); +#endif + + if (!gpio_is_valid(pdata->gpio_reset)) + I(" DT:gpio_rst value is not valid\n"); + +#ifdef HX_SHARP_ENABLE_PON + pdata->gpio_pon = of_get_named_gpio(dt, "himax,pon-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_pon)) + I(" DT:gpio_pon value is not valid\n"); + + + I(" DT:pdata->gpio_pon=%d\n", pdata->gpio_pon); + +#endif + + pdata->gpio_3v3_en = of_get_named_gpio(dt, "himax,3v3-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_3v3_en)) + I(" DT:gpio_3v3_en value is not valid\n"); + + I(" DT:gpio_irq=%d, gpio_rst=%d, gpio_3v3_en=%d\n", pdata->gpio_irq, pdata->gpio_reset, pdata->gpio_3v3_en); + + if (of_property_read_u32(dt, "report_type", &data) == 0) { + pdata->protocol_type = data; + I(" DT:protocol_type=%d\n", pdata->protocol_type); + } + + himax_vk_parser(dt, pdata); + return 0; +} +EXPORT_SYMBOL(himax_parse_dt); + +int himax_bus_read(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry) +{ + int retry; + struct i2c_client *client = private_ts->client; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &command, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + } + }; + mutex_lock(&private_ts->rw_lock); + + for (retry = 0; retry < toRetry; retry++) { + if (i2c_transfer(client->adapter, msg, 2) == 2) + break; + + /*msleep(20);*/ + } + + if (retry == toRetry) { + E("%s: i2c_read_block retry over %d\n", + __func__, toRetry); + i2c_error_count = toRetry; + mutex_unlock(&private_ts->rw_lock); + return -EIO; + } + + mutex_unlock(&private_ts->rw_lock); + return 0; +} +EXPORT_SYMBOL(himax_bus_read); + +int himax_bus_write(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry) +{ + int retry/*, loop_i*/; + uint8_t buf[length + 1]; + struct i2c_client *client = private_ts->client; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = length + 1, + .buf = buf, + } + }; + + + mutex_lock(&private_ts->rw_lock); + buf[0] = command; + memcpy(buf + 1, data, length); + + for (retry = 0; retry < toRetry; retry++) { + if (i2c_transfer(client->adapter, msg, 1) == 1) + break; + + /*msleep(20);*/ + } + + if (retry == toRetry) { + E("%s: i2c_write_block retry over %d\n", + __func__, toRetry); + i2c_error_count = toRetry; + mutex_unlock(&private_ts->rw_lock); + return -EIO; + } + + mutex_unlock(&private_ts->rw_lock); + return 0; +} +EXPORT_SYMBOL(himax_bus_write); + +int himax_bus_write_command(uint8_t command, uint8_t toRetry) +{ + return himax_bus_write(command, NULL, 0, toRetry); +} + +int himax_bus_master_write(uint8_t *data, uint32_t length, uint8_t toRetry) +{ + int retry/*, loop_i*/; + uint8_t buf[length]; + struct i2c_client *client = private_ts->client; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = length, + .buf = buf, + } + }; + + + mutex_lock(&private_ts->rw_lock); + memcpy(buf, data, length); + + for (retry = 0; retry < toRetry; retry++) { + if (i2c_transfer(client->adapter, msg, 1) == 1) + break; + + /*msleep(20);*/ + } + + if (retry == toRetry) { + E("%s: i2c_write_block retry over %d\n", + __func__, toRetry); + i2c_error_count = toRetry; + mutex_unlock(&private_ts->rw_lock); + return -EIO; + } + + mutex_unlock(&private_ts->rw_lock); + return 0; +} +EXPORT_SYMBOL(himax_bus_master_write); + +void himax_int_enable(int enable) +{ + int irqnum = 0; + + + irqnum = private_ts->client->irq; + + if (enable == 1 && irq_enable_count == 0) { + enable_irq(irqnum); + irq_enable_count = 1; + private_ts->irq_enabled = 1; + } else if (enable == 0 && irq_enable_count == 1) { + disable_irq_nosync(irqnum); + irq_enable_count = 0; + private_ts->irq_enabled = 0; + } + + I("irq_enable_count = %d\n", irq_enable_count); +} +EXPORT_SYMBOL(himax_int_enable); + +#ifdef HX_RST_PIN_FUNC +void himax_rst_gpio_set(int pinnum, uint8_t value) +{ + gpio_direction_output(pinnum, value); +} +EXPORT_SYMBOL(himax_rst_gpio_set); +#endif + +uint8_t himax_int_gpio_read(int pinnum) +{ + return gpio_get_value(pinnum); +} + +#if defined(CONFIG_HMX_DB) +static int himax_regulator_configure(struct himax_i2c_platform_data *pdata) +{ + int retval; + struct i2c_client *client = private_ts->client; + + + pdata->vcc_dig = regulator_get(&client->dev, "vdd"); + + if (IS_ERR(pdata->vcc_dig)) { + E("%s: Failed to get regulator vdd\n", + __func__); + retval = PTR_ERR(pdata->vcc_dig); + return retval; + } + + pdata->vcc_ana = regulator_get(&client->dev, "avdd"); + + if (IS_ERR(pdata->vcc_ana)) { + E("%s: Failed to get regulator avdd\n", + __func__); + retval = PTR_ERR(pdata->vcc_ana); + regulator_put(pdata->vcc_dig); + return retval; + } + + return 0; +}; + +static void himax_regulator_deinit(struct himax_i2c_platform_data *pdata) +{ + I("%s: entered.\n", __func__); + + if (!IS_ERR(pdata->vcc_ana)) + regulator_put(pdata->vcc_ana); + + if (!IS_ERR(pdata->vcc_dig)) + regulator_put(pdata->vcc_dig); + + I("%s: regulator put, completed.\n", __func__); +}; + +static int himax_power_on(struct himax_i2c_platform_data *pdata, bool on) +{ + int retval; + + if (on) { + retval = regulator_enable(pdata->vcc_dig); + + if (retval) { + E("%s: Failed to enable regulator vdd\n", + __func__); + return retval; + } + + /*msleep(100);*/ + usleep_range(1000, 1001); + retval = regulator_enable(pdata->vcc_ana); + + if (retval) { + E("%s: Failed to enable regulator avdd\n", + __func__); + regulator_disable(pdata->vcc_dig); + return retval; + } + } else { + regulator_disable(pdata->vcc_dig); + regulator_disable(pdata->vcc_ana); + } + + return 0; +} + +int himax_gpio_power_config(struct himax_i2c_platform_data *pdata) +{ + int error; + struct i2c_client *client = private_ts->client; + + + error = himax_regulator_configure(pdata); + + if (error) { + E("Failed to intialize hardware\n"); + goto err_regulator_not_on; + } + +#ifdef HX_RST_PIN_FUNC + + if (gpio_is_valid(pdata->gpio_reset)) { + /* configure touchscreen reset out gpio */ + error = gpio_request(pdata->gpio_reset, "hmx_reset_gpio"); + + if (error) { + E("unable to request gpio [%d]\n", pdata->gpio_reset); + goto err_regulator_on; + } + + error = gpio_direction_output(pdata->gpio_reset, 0); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_req; + } + } + +#endif + error = himax_power_on(pdata, true); + + if (error) { + E("Failed to power on hardware\n"); + goto err_power_on; + } + + if (gpio_is_valid(pdata->gpio_irq)) { + /* configure touchscreen irq gpio */ + error = gpio_request(pdata->gpio_irq, "hmx_gpio_irq"); + + if (error) { + E("unable to request gpio [%d]\n", + pdata->gpio_irq); + goto err_req_irq_gpio; + } + + error = gpio_direction_input(pdata->gpio_irq); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_irq); + goto err_set_gpio_irq; + } + + client->irq = gpio_to_irq(pdata->gpio_irq); + private_ts->hx_irq = client->irq; + } else { + E("irq gpio not provided\n"); + goto err_req_irq_gpio; + } + + /*msleep(20);*/ + usleep_range(2000, 2001); +#ifdef HX_RST_PIN_FUNC + + if (gpio_is_valid(pdata->gpio_reset)) { + error = gpio_direction_output(pdata->gpio_reset, 1); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_set_gpio_irq; + } + } + +#endif + return 0; +err_set_gpio_irq: + + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); + +err_req_irq_gpio: + himax_power_on(pdata, false); +err_power_on: +#ifdef HX_RST_PIN_FUNC +err_gpio_reset_req: + if (gpio_is_valid(pdata->gpio_reset)) + gpio_free(pdata->gpio_reset); + +err_regulator_on: +#endif + himax_regulator_deinit(pdata); +err_regulator_not_on: + return error; +} + +#else +int himax_gpio_power_config(struct himax_i2c_platform_data *pdata) +{ + int error = 0; + struct i2c_client *client = private_ts->client; +#ifdef HX_RST_PIN_FUNC + + if (pdata->gpio_reset >= 0) { + error = gpio_request(pdata->gpio_reset, "himax-reset"); + + if (error < 0) { + E("%s: request reset pin failed\n", __func__); + goto err_gpio_reset_req; + } + + error = gpio_direction_output(pdata->gpio_reset, 0); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_dir; + } + } + +#endif + +#ifdef HX_SHARP_ENABLE_PON + if (gpio_is_valid(pdata->gpio_pon)) { + error = gpio_request(pdata->gpio_pon, "hmx_pon_gpio"); + + if (error) { + E("unable to request scl gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_req; + } + + error = gpio_direction_output(pdata->gpio_pon, 0); + + I("gpio_pon LOW [%d]\n", pdata->gpio_pon); + + if (error) { + E("unable to set direction for pon gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_dir; + } + } +#endif + + + if (pdata->gpio_3v3_en >= 0) { + error = gpio_request(pdata->gpio_3v3_en, "himax-3v3_en"); + + if (error < 0) { + E("%s: request 3v3_en pin failed\n", __func__); + goto err_gpio_3v3_req; + } + + gpio_direction_output(pdata->gpio_3v3_en, 1); + I("3v3_en set 1 get pin = %d\n", gpio_get_value(pdata->gpio_3v3_en)); + } + + if (gpio_is_valid(pdata->gpio_irq)) { + /* configure touchscreen irq gpio */ + error = gpio_request(pdata->gpio_irq, "himax_gpio_irq"); + + if (error) { + E("unable to request gpio [%d]\n", pdata->gpio_irq); + goto err_gpio_irq_req; + } + + error = gpio_direction_input(pdata->gpio_irq); + + if (error) { + E("unable to set direction for gpio [%d]\n", pdata->gpio_irq); + goto err_gpio_irq_set_input; + } + + client->irq = gpio_to_irq(pdata->gpio_irq); + private_ts->hx_irq = client->irq; + } else { + E("irq gpio not provided\n"); + goto err_gpio_irq_req; + } +#ifdef HX_SHARP_ENABLE_PON + msleep(20); +#else + usleep_range(2000, 2001); +#endif + +#ifdef HX_RST_PIN_FUNC + + if (pdata->gpio_reset >= 0) { + error = gpio_direction_output(pdata->gpio_reset, 1); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_set_high; + } + } +#endif + +#ifdef HX_SHARP_ENABLE_PON + msleep(800); + + if (gpio_is_valid(pdata->gpio_pon)) { + + error = gpio_direction_output(pdata->gpio_pon, 1); + + I("gpio_pon HIGH [%d]\n", pdata->gpio_pon); + + if (error) { + E("gpio_pon unable to set direction for gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_set_high; + } + } +#endif + return error; + +#ifdef HX_SHARP_ENABLE_PON +err_gpio_pon_set_high: +#endif +#ifdef HX_RST_PIN_FUNC +err_gpio_reset_set_high: +#endif +err_gpio_irq_set_input: + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); +err_gpio_irq_req: + if (pdata->gpio_3v3_en >= 0) + gpio_free(pdata->gpio_3v3_en); +err_gpio_3v3_req: +#ifdef HX_SHARP_ENABLE_PON +err_gpio_pon_dir: + if (gpio_is_valid(pdata->gpio_pon)) + gpio_free(pdata->gpio_pon); +err_gpio_pon_req: +#endif +#ifdef HX_RST_PIN_FUNC +err_gpio_reset_dir: + if (pdata->gpio_reset >= 0) + gpio_free(pdata->gpio_reset); +err_gpio_reset_req: +#endif + return error; +} + +#endif + +void himax_gpio_power_deconfig(struct himax_i2c_platform_data *pdata) +{ + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); + +#ifdef HX_RST_PIN_FUNC + if (gpio_is_valid(pdata->gpio_reset)) + gpio_free(pdata->gpio_reset); +#endif + +#if defined(CONFIG_HMX_DB) + himax_power_on(pdata, false); + himax_regulator_deinit(pdata); +#else + if (pdata->gpio_3v3_en >= 0) + gpio_free(pdata->gpio_3v3_en); + +#ifdef HX_SHARP_ENABLE_PON + if (gpio_is_valid(pdata->gpio_pon)) + gpio_free(pdata->gpio_pon); +#endif + +#endif +} + +static void himax_ts_isr_func(struct himax_ts_data *ts) +{ + himax_ts_work(ts); +} + +irqreturn_t himax_ts_thread(int irq, void *ptr) +{ + himax_ts_isr_func((struct himax_ts_data *)ptr); + + return IRQ_HANDLED; +} + +static void himax_ts_work_func(struct work_struct *work) +{ + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work); + + + himax_ts_work(ts); +} + +int himax_int_register_trigger(void) +{ + int ret = 0; + struct himax_ts_data *ts = private_ts; + struct i2c_client *client = private_ts->client; + + if (ic_data->HX_INT_IS_EDGE) { + I("%s edge triiger falling\n ", __func__); + ret = request_threaded_irq(client->irq, NULL, himax_ts_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, ts); + } + + else { + I("%s level trigger low\n ", __func__); + ret = request_threaded_irq(client->irq, NULL, himax_ts_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, ts); + } + + return ret; +} + +int himax_int_en_set(void) +{ + int ret = NO_ERR; + + + ret = himax_int_register_trigger(); + return ret; +} + +int himax_ts_register_interrupt(void) +{ + struct himax_ts_data *ts = private_ts; + struct i2c_client *client = private_ts->client; + int ret = 0; + + + ts->irq_enabled = 0; + + /* Work functon */ + if (client->irq && private_ts->hx_irq) {/*INT mode*/ + ts->use_irq = 1; + ret = himax_int_register_trigger(); + + if (ret == 0) { + ts->irq_enabled = 1; + irq_enable_count = 1; + I("%s: irq enabled at gpio: %d\n", __func__, client->irq); +#ifdef HX_SMART_WAKEUP + irq_set_irq_wake(client->irq, 1); +#endif + } else { + ts->use_irq = 0; + E("%s: request_irq failed\n", __func__); + } + } else { + I("%s: client->irq is empty, use polling mode.\n", __func__); + } + + if (!ts->use_irq) {/*if use polling mode need to disable HX_ESD_RECOVERY function*/ + ts->himax_wq = create_singlethread_workqueue("himax_touch"); + INIT_WORK(&ts->work, himax_ts_work_func); + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = himax_ts_timer_func; + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + I("%s: polling mode enabled\n", __func__); + } + + return ret; +} + +int himax_ts_unregister_interrupt(void) +{ + struct himax_ts_data *ts = private_ts; + int ret = 0; + + + I("%s: entered.\n", __func__); + + /* Work functon */ + if (private_ts->hx_irq && ts->use_irq) {/*INT mode*/ +#ifdef HX_SMART_WAKEUP + irq_set_irq_wake(ts->hx_irq, 0); +#endif + free_irq(ts->hx_irq, ts); + I("%s: irq disabled at qpio: %d\n", __func__, private_ts->hx_irq); + } + + if (!ts->use_irq) {/*if use polling mode need to disable HX_ESD_RECOVERY function*/ + hrtimer_cancel(&ts->timer); + cancel_work_sync(&ts->work); + if (ts->himax_wq != NULL) + destroy_workqueue(ts->himax_wq); + I("%s: polling mode destroyed", __func__); + } + + return ret; +} + +static int himax_common_suspend(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + + + I("%s: enter\n", __func__); + himax_chip_common_suspend(ts); + return 0; +} + +static int himax_common_resume(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + + + I("%s: enter\n", __func__); + himax_chip_common_resume(ts); + return 0; +} + +#if defined(CONFIG_FB) +int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct himax_ts_data *ts = + container_of(self, struct himax_ts_data, fb_notif); + + + I(" %s\n", __func__); + + if (evdata && evdata->data && event == FB_EVENT_BLANK && ts && + ts->client) { + blank = evdata->data; + + switch (*blank) { + case FB_BLANK_UNBLANK: + himax_common_resume(&ts->client->dev); + break; + case FB_BLANK_POWERDOWN: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_NORMAL: + himax_common_suspend(&ts->client->dev); + break; + } + } + + return 0; +} +#endif + +int himax_chip_common_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + struct himax_ts_data *ts; + + I("%s:Enter\n", __func__); + + /* Check I2C functionality */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + E("%s: i2c check functionality error\n", __func__); + ret = -ENODEV; + goto err_check_functionality_failed; + } + + ts = kzalloc(sizeof(struct himax_ts_data), GFP_KERNEL); + if (ts == NULL) { + E("%s: allocate himax_ts_data failed\n", __func__); + ret = -ENOMEM; + goto err_alloc_data_failed; + } + + i2c_set_clientdata(client, ts); + ts->client = client; + ts->dev = &client->dev; + mutex_init(&ts->rw_lock); + private_ts = ts; + + ret = himax_chip_common_init(); + +err_alloc_data_failed: +err_check_functionality_failed: + + return ret; +} + +int himax_chip_common_remove(struct i2c_client *client) +{ + if (g_hx_chip_inited) + himax_chip_common_deinit(); + + return 0; +} + +static const struct i2c_device_id himax_common_ts_id[] = { + {HIMAX_common_NAME, 0 }, + {} +}; + +static const struct dev_pm_ops himax_common_pm_ops = { +#if (!defined(CONFIG_FB)) + .suspend = himax_common_suspend, + .resume = himax_common_resume, +#endif +}; + +#ifdef CONFIG_OF +static const struct of_device_id himax_match_table[] = { + {.compatible = "himax,hxcommon" }, + {}, +}; +#else +#define himax_match_table NULL +#endif + +static struct i2c_driver himax_common_driver = { + .id_table = himax_common_ts_id, + .probe = himax_chip_common_probe, + .remove = himax_chip_common_remove, + .driver = { + .name = HIMAX_common_NAME, + .owner = THIS_MODULE, + .of_match_table = himax_match_table, +#ifdef CONFIG_PM + .pm = &himax_common_pm_ops, +#endif + }, +}; + +static int __init himax_common_init(void) +{ + I("Himax common touch panel driver init\n"); + D("Himax check double loading\n"); + if (g_mmi_refcnt++ > 0) { + + I("Himax driver has been loaded! ignoring....\n"); + + return 0; + } + i2c_add_driver(&himax_common_driver); + + return 0; +} + +static void __exit himax_common_exit(void) +{ + i2c_del_driver(&himax_common_driver); +} + +module_init(himax_common_init); +module_exit(himax_common_exit); + +MODULE_DESCRIPTION("Himax_common driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/touchscreen/himax_0flash_mmi/himax_platform_QCT_I2C.h b/drivers/input/touchscreen/himax_0flash_mmi/himax_platform_QCT_I2C.h new file mode 100644 index 000000000000..f45c28f9e9b8 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/himax_platform_QCT_I2C.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for QCT platform + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef HIMAX_PLATFORM_H +#define HIMAX_PLATFORM_H + +#include "himax_common.h" + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_HMX_DB) + #include +#endif + + +#define HIMAX_I2C_RETRY_TIMES 3 + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG) +#define D(x...) pr_debug("[HXTP] " x) +#define I(x...) pr_info("[HXTP] " x) +#define W(x...) pr_warn("[HXTP][WARNING] " x) +#define E(x...) pr_err("[HXTP][ERROR] " x) +#define DIF(x...) \ +do { \ + if (debug_flag) \ + pr_debug("[HXTP][DEBUG] " x) \ + } while (0) +#else + +#define D(x...) +#define I(x...) +#define W(x...) +#define E(x...) +#define DIF(x...) +#endif + +#if defined(CONFIG_HMX_DB) + + /* Analog voltage @2.7 V */ + #define HX_VTG_MIN_UV 2700000 + #define HX_VTG_MAX_UV 3300000 + #define HX_ACTIVE_LOAD_UA 15000 + #define HX_LPM_LOAD_UA 10 + /* Digital voltage @1.8 V */ + #define HX_VTG_DIG_MIN_UV 1800000 + #define HX_VTG_DIG_MAX_UV 1800000 + #define HX_ACTIVE_LOAD_DIG_UA 10000 + #define HX_LPM_LOAD_DIG_UA 10 + #define HX_I2C_VTG_MIN_UV 1800000 + #define HX_I2C_VTG_MAX_UV 1800000 + #define HX_I2C_LOAD_UA 10000 + #define HX_I2C_LPM_LOAD_UA 10 + +#endif + +#define HIMAX_common_NAME "himax_tp" +#define HIMAX_I2C_ADDR 0x48 +#define INPUT_DEV_NAME "himax-touchscreen" + +struct himax_i2c_platform_data { + int abs_x_min; + int abs_x_max; + int abs_x_fuzz; + int abs_y_min; + int abs_y_max; + int abs_y_fuzz; + int abs_pressure_min; + int abs_pressure_max; + int abs_pressure_fuzz; + int abs_width_min; + int abs_width_max; + int screenWidth; + int screenHeight; + uint8_t fw_version; + uint8_t tw_id; + uint8_t powerOff3V3; + uint8_t cable_config[2]; + uint8_t protocol_type; + int gpio_irq; + int gpio_reset; + int gpio_3v3_en; + int gpio_pon; + int (*power)(int on); + void (*reset)(void); + struct himax_virtual_key *virtual_key; + struct kobject *vk_obj; + struct kobj_attribute *vk2Use; + int hx_config_size; + +#if defined(CONFIG_HMX_DB) + bool i2c_pull_up; + bool digital_pwr_regulator; + int reset_gpio; + u32 reset_gpio_flags; + int irq_gpio; + u32 irq_gpio_flags; + struct regulator *vcc_ana; /* For Dragon Board */ + struct regulator *vcc_dig; /* For Dragon Board */ + struct regulator *vcc_i2c; /* For Dragon Board */ + +#endif + +}; + +extern int irq_enable_count; +extern int himax_bus_read(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +extern int himax_bus_write(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +extern int himax_bus_write_command(uint8_t command, uint8_t toRetry); +extern int himax_bus_master_write(uint8_t *data, uint32_t length, uint8_t toRetry); +extern void himax_int_enable(int enable); +extern int himax_ts_register_interrupt(void); +int himax_ts_unregister_interrupt(void); +extern uint8_t himax_int_gpio_read(int pinnum); +extern int himax_gpio_power_config(struct himax_i2c_platform_data *pdata); +void himax_gpio_power_deconfig(struct himax_i2c_platform_data *pdata); + +#if defined(CONFIG_FB) + extern int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#endif + +extern void himax_ts_work(struct himax_ts_data *ts); +extern enum hrtimer_restart himax_ts_timer_func(struct hrtimer *timer); +extern int himax_chip_common_init(void); +extern void himax_chip_common_deinit(void); + +#endif + diff --git a/drivers/input/touchscreen/himax_0flash_mmi/himax_platform_QCT_SPI.c b/drivers/input/touchscreen/himax_0flash_mmi/himax_platform_QCT_SPI.c new file mode 100755 index 000000000000..97c9760ebb8c --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/himax_platform_QCT_SPI.c @@ -0,0 +1,1222 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for QCT platform + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_platform.h" +#include "himax_common.h" + +int i2c_error_count; +int irq_enable_count; +EXPORT_SYMBOL(irq_enable_count); +struct spi_device *spi; + +static uint8_t *gBuffer; + +int himax_dev_set(struct himax_ts_data *ts) +{ + int ret = 0; + + ts->input_dev = input_allocate_device(); + + if (ts->input_dev == NULL) { + ret = -ENOMEM; + E("%s: Failed to allocate input device\n", __func__); + return ret; + } + + ts->input_dev->name = "himax-touchscreen"; + return ret; +} +int himax_input_register_device(struct input_dev *input_dev) +{ + return input_register_device(input_dev); +} + +#if defined(HX_PLATFOME_DEFINE_KEY) +void himax_platform_key(void) +{ + I("Nothing to be done! Plz cancel it!\n"); +} +#endif + +void himax_vk_parser(struct device_node *dt, + struct himax_i2c_platform_data *pdata) +{ + u32 data = 0; + uint8_t cnt = 0, i = 0; + uint32_t coords[4] = {0}; + struct device_node *node, *pp = NULL; + struct himax_virtual_key *vk; + + node = of_parse_phandle(dt, "virtualkey", 0); + + if (node == NULL) { + I(" DT-No vk info in DT\n"); + return; + } + while ((pp = of_get_next_child(node, pp))) + cnt++; + + if (!cnt) + return; + + vk = kcalloc(cnt, sizeof(struct himax_virtual_key), GFP_KERNEL); + if (vk == NULL) { + E("%s, vk init fail!\n", __func__); + return; + } + pp = NULL; + + while ((pp = of_get_next_child(node, pp))) { + if (of_property_read_u32(pp, "idx", &data) == 0) + vk[i].index = data; + + if (of_property_read_u32_array(pp, "range", coords, 4) == 0) { + vk[i].x_range_min = coords[0], vk[i].x_range_max = coords[1]; + vk[i].y_range_min = coords[2], vk[i].y_range_max = coords[3]; + } else { + I(" range faile\n"); + } + + i++; + } + + pdata->virtual_key = vk; + + for (i = 0; i < cnt; i++) + I(" vk[%d] idx:%d x_min:%d, y_max:%d\n", i, pdata->virtual_key[i].index, + pdata->virtual_key[i].x_range_min, pdata->virtual_key[i].y_range_max); + +} + +int himax_parse_dt(struct himax_ts_data *ts, + struct himax_i2c_platform_data *pdata) +{ + int rc, coords_size = 0; + int touch_info_size = 0; + uint32_t coords[4] = {0}; + uint32_t touch_info[8] = {0}; + struct property *prop; + struct device_node *chosen, *dt = ts->dev->of_node; + u32 data = 0; + + prop = of_find_property(dt, "himax,panel-coords", NULL); + + if (prop) { + coords_size = prop->length / sizeof(u32); + + if (coords_size != 4) + D(" %s:Invalid panel coords size %d\n", __func__, coords_size); + } + + if (of_property_read_u32_array(dt, "himax,panel-coords", coords, coords_size) == 0) { + pdata->abs_x_min = coords[0], pdata->abs_x_max = (coords[1] - 1); + pdata->abs_y_min = coords[2], pdata->abs_y_max = (coords[3] - 1); + I(" DT-%s:panel-coords = %d, %d, %d, %d\n", __func__, pdata->abs_x_min, + pdata->abs_x_max, pdata->abs_y_min, pdata->abs_y_max); + } + + prop = of_find_property(dt, "himax,display-coords", NULL); + + if (prop) { + coords_size = prop->length / sizeof(u32); + + if (coords_size != 4) + D(" %s:Invalid display coords size %d\n", __func__, coords_size); + } + + rc = of_property_read_u32_array(dt, "himax,display-coords", coords, coords_size); + + if (rc && (rc != -EINVAL)) { + D(" %s:Fail to read display-coords %d\n", __func__, rc); + return rc; + } + + pdata->screenWidth = coords[1]; + pdata->screenHeight = coords[3]; + I(" DT-%s:display-coords = (%d, %d)\n", __func__, pdata->screenWidth, + pdata->screenHeight); + pdata->gpio_irq = of_get_named_gpio(dt, "himax,irq-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_irq)) + I(" DT:gpio_irq value is not valid\n"); + + prop = of_find_property(dt, "himax,touch_info", NULL); + if (prop) { + touch_info_size = prop->length / sizeof(u32); + if (touch_info_size != 8) { + D(" %s:Invalid touch_info size %d\n", __func__, touch_info_size); + } else { + rc = of_property_read_u32_array(dt, "himax,touch_info", touch_info, touch_info_size); + if (rc && (rc != -EINVAL)) { + D(" %s:Fail to read touch_info %d\n", __func__, rc); + } else { + ic_data->HX_RX_NUM = touch_info[0]; + ic_data->HX_TX_NUM = touch_info[1]; + ic_data->HX_BT_NUM = touch_info[2]; + ic_data->HX_X_RES = touch_info[3]; + ic_data->HX_Y_RES = touch_info[4]; + ic_data->HX_MAX_PT = touch_info[5]; + ic_data->HX_XY_REVERSE = touch_info[6]; + ic_data->HX_INT_IS_EDGE = touch_info[7]; + D(" %s:success to read touch_info from dts \n", __func__); + } + } + } + + I(" DT-%s:touch_info: HX_RX_NUM = %d, HX_TX_NUM = %d, HX_BT_NUM = %d,HX_X_RES = %d,HX_Y_RES = %d, HX_MAX_PT = %d, HX_XY_REVERSE = %d, HX_INT_IS_EDGE = %d\n", __func__, + ic_data->HX_RX_NUM,ic_data->HX_TX_NUM,ic_data->HX_BT_NUM,ic_data->HX_X_RES,ic_data->HX_Y_RES,ic_data->HX_MAX_PT,ic_data->HX_XY_REVERSE,ic_data->HX_INT_IS_EDGE); + + + pdata->gpio_reset = of_get_named_gpio(dt, "himax,rst-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_reset)) + I(" DT:gpio_rst value is not valid\n"); + + pdata->gpio_3v3_en = of_get_named_gpio(dt, "himax,3v3-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_3v3_en)) + I(" DT:gpio_3v3_en value is not valid\n"); + + I(" DT:gpio_irq=%d, gpio_rst=%d, gpio_3v3_en=%d\n", pdata->gpio_irq, pdata->gpio_reset, pdata->gpio_3v3_en); + + if (of_property_read_u32(dt, "report_type", &data) == 0) { + pdata->protocol_type = data; + I(" DT:protocol_type=%d\n", pdata->protocol_type); + } + + if (of_property_read_u32(dt, "himax,def-build-id", &ts->build_id)) { + ts->build_id = 0; + I("himax,build_id undefined.\n"); + } else { + I("himax,build_id=0x%04X\n", ts->build_id); + } + + if (of_property_read_u32(dt, "himax,def-config-id", &ts->config_id)) { + ts->config_id = 0; + I("himax,config_id undefined.\n"); + } else { + I("himax,config_id=0x%04X\n", ts->config_id); + } + + ts->vdd_1v8_always_on = of_property_read_bool(dt, "himax,vdd_1v8_always_on"); + if (ts->vdd_1v8_always_on){ + I(" DT:vdd_1v8_always_on=%d\n", ts->vdd_1v8_always_on); + } + + if (of_property_read_bool(dt, "himax,report_gesture_key")) { + I("novatek,report_gesture_key set"); + ts->report_gesture_key = 1; + } else { + ts->report_gesture_key = 0; + } + + himax_vk_parser(dt, pdata); + + chosen = of_find_node_by_name(NULL, "chosen"); + if (chosen) { + const char *supplier; + char *s, *d; + int rc; + + rc = of_property_read_string(chosen, "mmi,panel_name", + (const char **)&supplier); + if (rc) { + I("%s: cannot read mmi,panel_name %d\n", + __func__, rc); + } else { + I( "%s: mmi,panel_name %s\n", __func__, supplier); + + /* skip dsi_ part */ + s = (char *)supplier; + d = pdata->panel_supplier; + while (*s != '_') *d++ = *s++; + + I("%s: panel-supplier %s\n", __func__, pdata->panel_supplier); + } + of_node_put(chosen); + } + + return 0; +} +EXPORT_SYMBOL(himax_parse_dt); + +static ssize_t himax_spi_sync(struct himax_ts_data *ts, struct spi_message *message) +{ + int status; + + status = spi_sync(ts->spi, message); + + if (status == 0) { + status = message->status; + if (status == 0) + status = message->actual_length; + } + return status; +} + +static int himax_spi_read(uint8_t *command, uint8_t command_len, uint8_t *data, uint32_t length, uint8_t toRetry) +{ + struct spi_message message; + struct spi_transfer xfer[2]; + int retry; + int error; + + spi_message_init(&message); + memset(xfer, 0, sizeof(xfer)); + + xfer[0].tx_buf = command; + xfer[0].len = command_len; + spi_message_add_tail(&xfer[0], &message); + + xfer[1].rx_buf = data; + xfer[1].len = length; + spi_message_add_tail(&xfer[1], &message); + + for (retry = 0; retry < toRetry; retry++) { + error = spi_sync(private_ts->spi, &message); + if (unlikely(error)) + E("SPI read error: %d\n", error); + else + break; + } + + if (retry == toRetry) { + E("%s: SPI read error retry over %d\n", + __func__, toRetry); + return -EIO; + } + + return 0; +} + +static int himax_spi_write(uint8_t *buf, uint32_t length) +{ +/* + struct spi_transfer t = { + .tx_buf = buf, + .len = length, + }; + struct spi_message m; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + return himax_spi_sync(private_ts, &m); +*/ + struct spi_transfer t; + struct spi_message m; + + memset(&t, 0, sizeof(struct spi_transfer)); + t.tx_buf = buf; + t.len = length; + spi_message_init(&m); + spi_message_add_tail(&t, &m); + return himax_spi_sync(private_ts, &m); +} + +int himax_bus_read(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry) +{ + int result = 0; + uint8_t spi_format_buf[3]; + + mutex_lock(&(private_ts->spi_lock)); + spi_format_buf[0] = 0xF3; + spi_format_buf[1] = command; + spi_format_buf[2] = 0x00; + + result = himax_spi_read(&spi_format_buf[0], 3, data, length, toRetry); + mutex_unlock(&(private_ts->spi_lock)); + + return result; +} +EXPORT_SYMBOL(himax_bus_read); + +int himax_bus_write(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry) +{ + uint8_t *spi_format_buf = gBuffer; + int i = 0; + int result = 0; + + memset(spi_format_buf, 0, GBUFFER_SZ * sizeof(uint8_t)); + mutex_lock(&(private_ts->spi_lock)); + spi_format_buf[0] = 0xF2; + spi_format_buf[1] = command; + + for (i = 0; i < length; i++) + spi_format_buf[i + 2] = data[i]; + + result = himax_spi_write(spi_format_buf, length + 2); + mutex_unlock(&(private_ts->spi_lock)); + + return result; +} +EXPORT_SYMBOL(himax_bus_write); + +int himax_bus_write_command(uint8_t command, uint8_t toRetry) +{ + return himax_bus_write(command, NULL, 0, toRetry); +} + +#ifdef FIX_WVLA +int himax_bus_master_write(uint8_t *data, uint32_t length, uint8_t toRetry) +{ + uint8_t *buf = NULL; + struct spi_transfer t = { + .tx_buf = buf, + .len = length, + }; + struct spi_message m; + int result = 0; + + buf = kcalloc(length, sizeof(uint8_t), GFP_KERNEL); + if (buf == NULL) { + E("%s, Failed to allocate memory\n", __func__); + goto alloc_error; + } + + mutex_lock(&(private_ts->spi_lock)); + memcpy(buf, data, length); + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + result = himax_spi_sync(private_ts, &m); + mutex_unlock(&(private_ts->spi_lock)); + +alloc_error: + if (buf) + kfree(buf); + + return result; +} +#else +int himax_bus_master_write(uint8_t *data, uint32_t length, uint8_t toRetry) +{ + uint8_t buf[length]; + + struct spi_transfer t = { + .tx_buf = buf, + .len = length, + }; + struct spi_message m; + int result = 0; + + mutex_lock(&(private_ts->spi_lock)); + memcpy(buf, data, length); + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + result = himax_spi_sync(private_ts, &m); + mutex_unlock(&(private_ts->spi_lock)); + + return result; +} +#endif +EXPORT_SYMBOL(himax_bus_master_write); + +void himax_int_enable(int enable) +{ + int irqnum = 0; + + irqnum = private_ts->hx_irq; + if (enable == 1 && irq_enable_count == 0) { + enable_irq(irqnum); + irq_enable_count = 1; + private_ts->irq_enabled = 1; + } else if (enable == 0 && irq_enable_count == 1) { + disable_irq_nosync(irqnum); + irq_enable_count = 0; + private_ts->irq_enabled = 0; + } + + I("irq_enable_count = %d\n", irq_enable_count); +} +EXPORT_SYMBOL(himax_int_enable); + +#ifdef HX_RST_PIN_FUNC +void himax_rst_gpio_set(int pinnum, uint8_t value) +{ + gpio_direction_output(pinnum, value); +} +EXPORT_SYMBOL(himax_rst_gpio_set); +#endif + +uint8_t himax_int_gpio_read(int pinnum) +{ + return gpio_get_value(pinnum); +} + +#if defined(CONFIG_HMX_DB) +static int himax_regulator_configure(struct himax_i2c_platform_data *pdata) +{ + int retval; + /* struct i2c_client *client = private_ts->client; */ + + pdata->vcc_dig = regulator_get(private_ts->dev, "vdd"); + + if (IS_ERR(pdata->vcc_dig)) { + E("%s: Failed to get regulator vdd\n", + __func__); + retval = PTR_ERR(pdata->vcc_dig); + return retval; + } + + pdata->vcc_ana = regulator_get(private_ts->dev, "avdd"); + + if (IS_ERR(pdata->vcc_ana)) { + E("%s: Failed to get regulator avdd\n", + __func__); + retval = PTR_ERR(pdata->vcc_ana); + regulator_put(pdata->vcc_dig); + return retval; + } + + return 0; +}; + +static void himax_regulator_deinit(struct himax_i2c_platform_data *pdata) +{ + I("%s: entered.\n", __func__); + + if (!IS_ERR(pdata->vcc_ana)) + regulator_put(pdata->vcc_ana); + + if (!IS_ERR(pdata->vcc_dig)) + regulator_put(pdata->vcc_dig); + + I("%s: regulator put, completed.\n", __func__); +}; + +static int himax_power_on(struct himax_i2c_platform_data *pdata, bool on) +{ + int retval; + + if (on) { + retval = regulator_enable(pdata->vcc_dig); + + if (retval) { + E("%s: Failed to enable regulator vdd\n", + __func__); + return retval; + } + + /*msleep(100);*/ + usleep_range(1000, 1001); + retval = regulator_enable(pdata->vcc_ana); + + if (retval) { + E("%s: Failed to enable regulator avdd\n", + __func__); + regulator_disable(pdata->vcc_dig); + return retval; + } + } else { + regulator_disable(pdata->vcc_dig); + regulator_disable(pdata->vcc_ana); + } + + return 0; +} + +int himax_gpio_power_config(struct himax_i2c_platform_data *pdata) +{ + int error; + /* struct i2c_client *client = private_ts->client; */ + + error = himax_regulator_configure(pdata); + + if (error) { + E("Failed to intialize hardware\n"); + goto err_regulator_not_on; + } + +#ifdef HX_RST_PIN_FUNC + + if (gpio_is_valid(pdata->gpio_reset)) { + /* configure touchscreen reset out gpio */ + error = gpio_request(pdata->gpio_reset, "hmx_reset_gpio"); + + if (error) { + E("unable to request gpio [%d]\n", pdata->gpio_reset); + goto err_regulator_on; + } + + error = gpio_direction_output(pdata->gpio_reset, 0); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_req; + } + } + +#endif + error = himax_power_on(pdata, true); + + if (error) { + E("Failed to power on hardware\n"); + goto err_power_on; + } + + if (gpio_is_valid(pdata->gpio_irq)) { + /* configure touchscreen irq gpio */ + error = gpio_request(pdata->gpio_irq, "hmx_gpio_irq"); + + if (error) { + E("unable to request gpio [%d]\n", + pdata->gpio_irq); + goto err_req_irq_gpio; + } + + error = gpio_direction_input(pdata->gpio_irq); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_irq); + goto err_set_gpio_irq; + } + + private_ts->hx_irq = gpio_to_irq(pdata->gpio_irq); + } else { + E("irq gpio not provided\n"); + goto err_req_irq_gpio; + } + + /*msleep(20);*/ + usleep_range(2000, 2001); +#ifdef HX_RST_PIN_FUNC + + if (gpio_is_valid(pdata->gpio_reset)) { + error = gpio_direction_output(pdata->gpio_reset, 1); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_set_gpio_irq; + } + } + +#endif + return 0; +err_set_gpio_irq: + + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); + +err_req_irq_gpio: + himax_power_on(pdata, false); +err_power_on: +#ifdef HX_RST_PIN_FUNC +err_gpio_reset_req: + if (gpio_is_valid(pdata->gpio_reset)) + gpio_free(pdata->gpio_reset); + +err_regulator_on: +#endif + himax_regulator_deinit(pdata); +err_regulator_not_on: + return error; +} + +#else +int himax_gpio_power_config(struct himax_i2c_platform_data *pdata) +{ + int error = 0; + /* struct i2c_client *client = private_ts->client; */ +#ifdef HX_RST_PIN_FUNC + + if (pdata->gpio_reset >= 0) { + error = gpio_request(pdata->gpio_reset, "himax-reset"); + + if (error < 0) { + E("%s: request reset pin failed\n", __func__); + goto err_gpio_reset_req; + } + + error = gpio_direction_output(pdata->gpio_reset, 0); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_dir; + } + } + +#endif + +#ifdef HX_SHARP_ENABLE_PON + if (gpio_is_valid(pdata->gpio_pon)) { + error = gpio_request(pdata->gpio_pon, "hmx_pon_gpio"); + + if (error) { + E("unable to request scl gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_req; + } + + error = gpio_direction_output(pdata->gpio_pon, 0); + + I("gpio_pon LOW [%d]\n", pdata->gpio_pon); + + if (error) { + E("unable to set direction for pon gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_dir; + } + } +#endif + + + if (pdata->gpio_3v3_en >= 0) { + error = gpio_request(pdata->gpio_3v3_en, "himax-3v3_en"); + + if (error < 0) { + E("%s: request 3v3_en pin failed\n", __func__); + goto err_gpio_3v3_req; + } + + gpio_direction_output(pdata->gpio_3v3_en, 1); + I("3v3_en set 1 get pin = %d\n", gpio_get_value(pdata->gpio_3v3_en)); + } + + if (gpio_is_valid(pdata->gpio_irq)) { + /* configure touchscreen irq gpio */ + error = gpio_request(pdata->gpio_irq, "himax_gpio_irq"); + + if (error) { + E("unable to request gpio [%d]\n", pdata->gpio_irq); + goto err_gpio_irq_req; + } + + error = gpio_direction_input(pdata->gpio_irq); + + if (error) { + E("unable to set direction for gpio [%d]\n", pdata->gpio_irq); + goto err_gpio_irq_set_input; + } + + private_ts->hx_irq = gpio_to_irq(pdata->gpio_irq); + } else { + E("irq gpio not provided\n"); + goto err_gpio_irq_req; + } +#ifdef HX_SHARP_ENABLE_PON + msleep(20); +#else + usleep_range(2000, 2001); +#endif + +#ifdef HX_RST_PIN_FUNC + + if (pdata->gpio_reset >= 0) { + error = gpio_direction_output(pdata->gpio_reset, 1); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_set_high; + } + } +#endif + +#ifdef HX_SHARP_ENABLE_PON + msleep(800); + + if (gpio_is_valid(pdata->gpio_pon)) { + + error = gpio_direction_output(pdata->gpio_pon, 1); + + I("gpio_pon HIGH [%d]\n", pdata->gpio_pon); + + if (error) { + E("gpio_pon unable to set direction for gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_set_high; + } + } +#endif + return error; + +#ifdef HX_SHARP_ENABLE_PON +err_gpio_pon_set_high: +#endif +#ifdef HX_RST_PIN_FUNC +err_gpio_reset_set_high: +#endif +err_gpio_irq_set_input: + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); +err_gpio_irq_req: + if (pdata->gpio_3v3_en >= 0) + gpio_free(pdata->gpio_3v3_en); +err_gpio_3v3_req: +#ifdef HX_SHARP_ENABLE_PON +err_gpio_pon_dir: + if (gpio_is_valid(pdata->gpio_pon)) + gpio_free(pdata->gpio_pon); +err_gpio_pon_req: +#endif +#ifdef HX_RST_PIN_FUNC +err_gpio_reset_dir: + if (pdata->gpio_reset >= 0) + gpio_free(pdata->gpio_reset); +err_gpio_reset_req: +#endif + return error; +} + +#endif + +void himax_gpio_power_deconfig(struct himax_i2c_platform_data *pdata) +{ + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); + +#ifdef HX_RST_PIN_FUNC + if (gpio_is_valid(pdata->gpio_reset)) + gpio_free(pdata->gpio_reset); +#endif + +#if defined(CONFIG_HMX_DB) + himax_power_on(pdata, false); + himax_regulator_deinit(pdata); +#else + if (pdata->gpio_3v3_en >= 0) + gpio_free(pdata->gpio_3v3_en); + +#ifdef HX_SHARP_ENABLE_PON + if (gpio_is_valid(pdata->gpio_pon)) + gpio_free(pdata->gpio_pon); +#endif + +#endif +} + +static void himax_ts_isr_func(struct himax_ts_data *ts) +{ + himax_ts_work(ts); +} + +irqreturn_t himax_ts_thread(int irq, void *ptr) +{ + himax_ts_isr_func((struct himax_ts_data *)ptr); + + return IRQ_HANDLED; +} + +static void himax_ts_work_func(struct work_struct *work) +{ + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work); + + himax_ts_work(ts); +} + +int himax_int_register_trigger(void) +{ + int ret = 0; + struct himax_ts_data *ts = private_ts; + + if (ic_data->HX_INT_IS_EDGE) { + I("%s edge triiger falling\n", __func__); + ret = request_threaded_irq(ts->hx_irq, NULL, himax_ts_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, HIMAX_common_NAME, ts); + } else { + I("%s level trigger low\n", __func__); + ret = request_threaded_irq(ts->hx_irq, NULL, himax_ts_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT, HIMAX_common_NAME, ts); + } + + return ret; +} + +int himax_int_en_set(void) +{ + int ret = NO_ERR; + + ret = himax_int_register_trigger(); + return ret; +} + +int himax_ts_register_interrupt(void) +{ + struct himax_ts_data *ts = private_ts; + /* struct i2c_client *client = private_ts->client; */ + int ret = 0; + + ts->irq_enabled = 0; + + /* Work functon */ + if (private_ts->hx_irq) {/*INT mode*/ + ts->use_irq = 1; + ret = himax_int_register_trigger(); + + if (ret == 0) { + ts->irq_enabled = 1; + irq_enable_count = 1; + I("%s: irq enabled at gpio: %d\n", __func__, private_ts->hx_irq); +#ifdef HX_SMART_WAKEUP + irq_set_irq_wake(private_ts->hx_irq, 1); +#endif + } else { + ts->use_irq = 0; + E("%s: request_irq failed\n", __func__); + } + } else { + I("%s: private_ts->hx_irq is empty, use polling mode.\n", __func__); + } + + if (!ts->use_irq) {/*if use polling mode need to disable HX_ESD_RECOVERY function*/ + ts->himax_wq = create_singlethread_workqueue("himax_touch"); + INIT_WORK(&ts->work, himax_ts_work_func); + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = himax_ts_timer_func; + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + I("%s: polling mode enabled\n", __func__); + } + + return ret; +} + +int himax_ts_unregister_interrupt(void) +{ + struct himax_ts_data *ts = private_ts; + int ret = 0; + + I("%s: entered.\n", __func__); + + /* Work functon */ + if (private_ts->hx_irq && ts->use_irq) {/*INT mode*/ +#ifdef HX_SMART_WAKEUP + irq_set_irq_wake(ts->hx_irq, 0); +#endif + free_irq(ts->hx_irq, ts); + I("%s: irq disabled at qpio: %d\n", __func__, private_ts->hx_irq); + } + + if (!ts->use_irq) {/*if use polling mode need to disable HX_ESD_RECOVERY function*/ + hrtimer_cancel(&ts->timer); + cancel_work_sync(&ts->work); + if (ts->himax_wq != NULL) + destroy_workqueue(ts->himax_wq); + I("%s: polling mode destroyed", __func__); + } + + return ret; +} + +static int himax_common_suspend(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + + I("%s: enter\n", __func__); +#ifdef CONFIG_DRM + if (!ts->initialized) + return -ECANCELED; +#endif + himax_chip_common_suspend(ts); + return 0; +} + +#if defined(HX_RESUME_SET_FW) +void himax_resume_thread_func(void) +{ + struct himax_ts_data *ts = private_ts; + I("%s: TP resume from thread\n", __func__); +#if defined(__HIMAX_HX83102D_MOD__) + queue_delayed_work(ts->ts_int_workqueue, + &ts->ts_int_work, msecs_to_jiffies(60)); +#else + queue_work(ts->ts_int_workqueue, &ts->ts_int_work); +#endif + return; +} +#endif +static int himax_common_resume(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + + I("%s: enter\n", __func__); +#ifdef CONFIG_DRM + if (!ts->initialized) { + /* + * wait until device resume for TDDI + * TDDI: Touch and display Driver IC + */ + if (himax_chip_common_init()) + return -ECANCELED; + } +#endif +#if defined(HX_RESUME_SET_FW) + himax_resume_thread_func(); +#else + himax_chip_common_resume(ts); +#endif + return 0; +} + +#if defined(CONFIG_DRM) +int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank; + struct himax_ts_data *ts = + container_of(self, struct himax_ts_data, fb_notif); + + if (!evdata || (evdata->id != 0)) + return 0; + + D("DRM %s\n", __func__); + + if (evdata->data && event == MSM_DRM_EARLY_EVENT_BLANK && ts != NULL && + ts->dev != NULL) { + blank = evdata->data; + switch (*blank) { + case MSM_DRM_BLANK_POWERDOWN: + if (!ts->initialized) + return -ECANCELED; + himax_common_suspend(ts->dev); +#ifdef HIMAX_PALM_SENSOR_EN + if (ts->palm_detection_enabled) { + I("%s: palm detection is enabled", __func__); + return 1; + } +#endif +#ifdef HIMAX_V2_SENSOR_EN + if (ts->SMWP_enable) { + I("%s: tap detection is enabled", __func__); + return 1; + } +#endif + break; +#if defined(__HIMAX_HX83102D_MOD__) + case MSM_DRM_BLANK_UNBLANK: + D("DRM_EARLY_EVENT_BLANK resume.\n"); + himax_common_resume(ts->dev); + return 0; +#endif + } + } + + if (evdata->data && event == MSM_DRM_EVENT_BLANK && ts != NULL && + ts->dev != NULL) { + blank = evdata->data; + switch (*blank) { + case MSM_DRM_BLANK_UNBLANK: + himax_common_resume(ts->dev); + break; + } + } + + return 0; +} +#elif defined(CONFIG_FB) +int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct himax_ts_data *ts = + container_of(self, struct himax_ts_data, fb_notif); + + I(" %s\n", __func__); + + if (evdata && evdata->data && event == FB_EVENT_BLANK && ts != NULL && + ts->dev != NULL) { + blank = evdata->data; + + switch (*blank) { + case FB_BLANK_UNBLANK: + himax_common_resume(ts->dev); + break; + + case FB_BLANK_POWERDOWN: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_NORMAL: + himax_common_suspend(ts->dev); + break; + } + } + + return 0; +} +#endif + +#ifdef HIMAX_CONFIG_PANEL_NOTIFICATIONS +int panel_notifier_callback(struct notifier_block *self, unsigned long event, void *data) +{ + struct himax_ts_data *ts = + container_of(self, struct himax_ts_data, panel_notif); + + I("panel %s\n", __func__); + + switch (event) { + case PANEL_EVENT_PRE_DISPLAY_OFF: + I("event=%lu\n", event); + if (!ts->initialized) + return -ECANCELED; + himax_common_suspend(ts->dev); +#ifdef HIMAX_PALM_SENSOR_EN + if (ts->palm_detection_enabled) { + I("%s: palm detection is enabled", __func__); + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); + } else { + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); + } +#endif +#ifdef HIMAX_V2_SENSOR_EN + if (ts->SMWP_enable) { + I("%s: tap detection is enabled", __func__); + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); + } else { + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); + } +#endif + break; + + case PANEL_EVENT_DISPLAY_ON: + I("event=%lu\n", event); + himax_common_resume(ts->dev); + break; + default: /* use DEV_TS here to avoid unused variable */ + I("panel %s\n", __func__); + break; + } + + return 0; +} +#endif + +int himax_chip_common_probe(struct spi_device *spi) +{ + struct himax_ts_data *ts; + int ret = 0; + + I("Enter %s\n", __func__); + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) { + dev_err(&spi->dev, + "%s: Full duplex not supported by host\n", __func__); + return -EIO; + } + + gBuffer = kzalloc(sizeof(uint8_t) * PACKET_MAX_SZ, GFP_KERNEL); + if (gBuffer == NULL) { + E("%s: allocate gBuffer failed\n", __func__); + ret = -ENOMEM; + goto err_alloc_gbuffer_failed; + } + + ts = kzalloc(sizeof(struct himax_ts_data), GFP_KERNEL); + if (ts == NULL) { + E("%s: allocate himax_ts_data failed\n", __func__); + ret = -ENOMEM; + goto err_alloc_data_failed; + } + + private_ts = ts; + spi->bits_per_word = 8; + spi->mode = SPI_MODE_3; + spi->chip_select = 0; + + ts->spi = spi; + mutex_init(&(ts->spi_lock)); + ts->dev = &spi->dev; + dev_set_drvdata(&spi->dev, ts); + spi_set_drvdata(spi, ts); + + ts->initialized = false; + ret = himax_chip_common_init(); + if (ret < 0) + goto err_alloc_data_failed; + return 0; + +err_alloc_data_failed: + kfree(gBuffer); +err_alloc_gbuffer_failed: + return ret; +} + +int himax_chip_common_remove(struct spi_device *spi) +{ + struct himax_ts_data *ts = spi_get_drvdata(spi); + + if (g_hx_chip_inited) + himax_chip_common_deinit(); + +#if defined(HX_USB_DETECT_GLOBAL) + if (ts->charger_notif.notifier_call) + power_supply_unreg_notifier(&ts->charger_notif); +#endif + ts->spi = NULL; + /* spin_unlock_irq(&ts->spi_lock); */ + spi_set_drvdata(spi, NULL); + kfree(gBuffer); + + I("%s: completed.\n", __func__); + + return 0; +} + +#ifndef HX_112F_SET +static const struct dev_pm_ops himax_common_pm_ops = { +#if (!defined(CONFIG_FB)) && (!defined(CONFIG_DRM)) + .suspend = himax_common_suspend, + .resume = himax_common_resume, +#endif +}; +#endif + +#ifdef CONFIG_OF +static const struct of_device_id himax_match_table[] = { + {.compatible = "himax,hxcommon" }, + {}, +}; +#else +#define himax_match_table NULL +#endif + +static struct spi_driver himax_common_driver = { + .driver = { + .name = HIMAX_common_NAME, + .owner = THIS_MODULE, + .of_match_table = himax_match_table, + }, + .probe = himax_chip_common_probe, + .remove = himax_chip_common_remove, +}; + +static int __init himax_common_init(void) +{ + I("Himax common touch panel driver init\n"); + D("Himax check double loading\n"); + if (g_mmi_refcnt++ > 0) { + I("Himax driver has been loaded! ignoring....\n"); + return 0; + } + spi_register_driver(&himax_common_driver); + + return 0; +} + +static void __exit himax_common_exit(void) +{ + if (spi) { + spi_unregister_device(spi); + spi = NULL; + } + spi_unregister_driver(&himax_common_driver); +} + +module_init(himax_common_init); +module_exit(himax_common_exit); + +MODULE_DESCRIPTION("Himax_common driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/touchscreen/himax_0flash_mmi/himax_platform_QCT_SPI.h b/drivers/input/touchscreen/himax_0flash_mmi/himax_platform_QCT_SPI.h new file mode 100644 index 000000000000..e75db6940abf --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/himax_platform_QCT_SPI.h @@ -0,0 +1,147 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for QCT platform + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef HIMAX_PLATFORM_H +#define HIMAX_PLATFORM_H + +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_HMX_DB) + #include +#endif + +#define HIMAX_I2C_RETRY_TIMES 3 + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG) +#define D(x...) pr_debug("[HXTP] " x) +#define I(x...) pr_info("[HXTP] " x) +#define W(x...) pr_warn("[HXTP][WARNING] " x) +#define E(x...) pr_err("[HXTP][ERROR] " x) +#define DIF(x...) \ +do { \ + if (debug_flag) \ + pr_debug("[HXTP][DEBUG] " x) \ + } while (0) +#else +#define D(x...) +#define I(x...) +#define W(x...) +#define E(x...) +#define DIF(x...) +#endif + +#if defined(CONFIG_HMX_DB) + /* Analog voltage @2.7 V */ + #define HX_VTG_MIN_UV 2700000 + #define HX_VTG_MAX_UV 3300000 + #define HX_ACTIVE_LOAD_UA 15000 + #define HX_LPM_LOAD_UA 10 + /* Digital voltage @1.8 V */ + #define HX_VTG_DIG_MIN_UV 1800000 + #define HX_VTG_DIG_MAX_UV 1800000 + #define HX_ACTIVE_LOAD_DIG_UA 10000 + #define HX_LPM_LOAD_DIG_UA 10 + + #define HX_I2C_VTG_MIN_UV 1800000 + #define HX_I2C_VTG_MAX_UV 1800000 + #define HX_I2C_LOAD_UA 10000 + #define HX_I2C_LPM_LOAD_UA 10 +#endif + +#define HIMAX_common_NAME "himax_tp" +#define HIMAX_I2C_ADDR 0x48 +#define INPUT_DEV_NAME "himax-touchscreen" +#define PACKET_MAX_SZ 4096 +#define GBUFFER_SZ PACKET_MAX_SZ - 2 + +struct himax_i2c_platform_data { + int abs_x_min; + int abs_x_max; + int abs_x_fuzz; + int abs_y_min; + int abs_y_max; + int abs_y_fuzz; + int abs_pressure_min; + int abs_pressure_max; + int abs_pressure_fuzz; + int abs_width_min; + int abs_width_max; + int screenWidth; + int screenHeight; + uint8_t fw_version; + uint8_t tw_id; + uint8_t powerOff3V3; + uint8_t cable_config[2]; + uint8_t protocol_type; + int gpio_irq; + int gpio_reset; + int gpio_3v3_en; + char panel_supplier[16]; + int (*power)(int on); + void (*reset)(void); + struct himax_virtual_key *virtual_key; + struct kobject *vk_obj; + struct kobj_attribute *vk2Use; + + int hx_config_size; +#if defined(CONFIG_HMX_DB) + bool i2c_pull_up; + bool digital_pwr_regulator; + int reset_gpio; + u32 reset_gpio_flags; + int irq_gpio; + u32 irq_gpio_flags; + + struct regulator *vcc_ana; /* For Dragon Board */ + struct regulator *vcc_dig; /* For Dragon Board */ + struct regulator *vcc_i2c; /* For Dragon Board */ +#endif +}; + +extern struct himax_ic_data *ic_data; +extern struct himax_ts_data *private_ts; +int himax_chip_common_init(void); +void himax_chip_common_deinit(void); + +void himax_ts_work(struct himax_ts_data *ts); +enum hrtimer_restart himax_ts_timer_func(struct hrtimer *timer); +extern int irq_enable_count; +extern int himax_bus_read(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +extern int himax_bus_write(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +extern int himax_bus_write_command(uint8_t command, uint8_t toRetry); +extern int himax_bus_master_write(uint8_t *data, uint32_t length, uint8_t toRetry); +extern void himax_int_enable(int enable); +extern int himax_ts_register_interrupt(void); +int himax_ts_unregister_interrupt(void); +extern uint8_t himax_int_gpio_read(int pinnum); + +extern int himax_gpio_power_config(struct himax_i2c_platform_data *pdata); +void himax_gpio_power_deconfig(struct himax_i2c_platform_data *pdata); + +#ifdef HIMAX_CONFIG_PANEL_NOTIFICATIONS +extern int panel_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#endif + +#if defined(CONFIG_DRM) + extern int drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#elif defined(CONFIG_FB) + extern int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#endif + +#endif diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102/Android.mk b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102/Android.mk new file mode 100644 index 000000000000..441126d8e1a5 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102/Android.mk @@ -0,0 +1,19 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +HIMAX_MMI_IC_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +HIMAX_MMI_IC_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +ifeq ($(HIMAX_V2_MMI_HX83102),true) +KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_0FLASH_MMI_IC_NAME=hx83102 +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := himax_0flash_mmi_hx83102.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(HIMAX_MMI_IC_MODULE_PATH) +LOCAL_REQUIRED_MODULES := himax_0flash_mmi.ko +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102/Kbuild b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102/Kbuild new file mode 100644 index 000000000000..eea8242d9089 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102/Kbuild @@ -0,0 +1,18 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_0flash_mmi \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_0flash_mmi/support_ic \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102 +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_DEBUG \ + -DCONFIG_TOUCHSCREEN_HIMAX_INSPECT + +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_INCELL +EXTRA_CFLAGS += -DCONFIG_HMX_DB + +ifneq ($(filter hx83102,$(CONFIG_INPUT_HIMAX_0FLASH_MMI_IC_NAME)),) + EXTRA_CFLAGS += -D__HIMAX_HX83102_MOD__ +endif + +obj-m := himax_0flash_mmi_hx83102.o +himax_0flash_mmi_hx83102-objs += himax_ic_HX83102.o diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102/Makefile b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102/Makefile new file mode 100755 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102/himax_ic_HX83102.c b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102/himax_ic_HX83102.c new file mode 100644 index 000000000000..1c449b40ee31 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102/himax_ic_HX83102.c @@ -0,0 +1,1462 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for HX83102 chipset + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_ic_HX83102.h" +#include "himax_modular.h" + +static void hx83102_chip_init(void) +{ + (*kp_private_ts)->chip_cell_type = CHIP_IS_IN_CELL; + I("%s:IC cell type = %d\n", __func__, (*kp_private_ts)->chip_cell_type); + (*kp_IC_CHECKSUM) = HX_TP_BIN_CHECKSUM_CRC; + /*Himax: Set FW and CFG Flash Address*/ + (*kp_FW_VER_MAJ_FLASH_ADDR) = 49157; /*0x00C005*/ + (*kp_FW_VER_MAJ_FLASH_LENG) = 1; + (*kp_FW_VER_MIN_FLASH_ADDR) = 49158; /*0x00C006*/ + (*kp_FW_VER_MIN_FLASH_LENG) = 1; + (*kp_CFG_VER_MAJ_FLASH_ADDR) = 49408; /*0x00C100*/ + (*kp_CFG_VER_MAJ_FLASH_LENG) = 1; + (*kp_CFG_VER_MIN_FLASH_ADDR) = 49409; /*0x00C101*/ + (*kp_CFG_VER_MIN_FLASH_LENG) = 1; + (*kp_CID_VER_MAJ_FLASH_ADDR) = 49154; /*0x00C002*/ + (*kp_CID_VER_MAJ_FLASH_LENG) = 1; + (*kp_CID_VER_MIN_FLASH_ADDR) = 49155; /*0x00C003*/ + (*kp_CID_VER_MIN_FLASH_LENG) = 1; + /*PANEL_VERSION_ADDR = 49156;*/ /*0x00C004*/ + /*PANEL_VERSION_LENG = 1;*/ +} + +static void hx83102e_chip_init(void) +{ + (*kp_private_ts)->chip_cell_type = CHIP_IS_IN_CELL; + I("%s:IC cell type = %d\n", __func__, (*kp_private_ts)->chip_cell_type); + (*kp_IC_CHECKSUM) = HX_TP_BIN_CHECKSUM_CRC; + /*Himax: Set FW and CFG Flash Address*/ + (*kp_FW_VER_MAJ_FLASH_ADDR) = 57349; /*0x00C005*/ + (*kp_FW_VER_MAJ_FLASH_LENG) = 1; + (*kp_FW_VER_MIN_FLASH_ADDR) = 57350; /*0x00C006*/ + (*kp_FW_VER_MIN_FLASH_LENG) = 1; + (*kp_CFG_VER_MAJ_FLASH_ADDR) = 57600; /*0x00C100*/ + (*kp_CFG_VER_MAJ_FLASH_LENG) = 1; + (*kp_CFG_VER_MIN_FLASH_ADDR) = 57601; /*0x00C101*/ + (*kp_CFG_VER_MIN_FLASH_LENG) = 1; + (*kp_CID_VER_MAJ_FLASH_ADDR) = 57346; /*0x00C002*/ + (*kp_CID_VER_MAJ_FLASH_LENG) = 1; + (*kp_CID_VER_MIN_FLASH_ADDR) = 57347; /*0x00C003*/ + (*kp_CID_VER_MIN_FLASH_LENG) = 1; + /*PANEL_VERSION_ADDR = 49156;*/ /*0x00C004*/ + /*PANEL_VERSION_LENG = 1;*/ +} + +void hx83102_burst_enable(uint8_t auto_add_4_byte) +{ + uint8_t tmp_data[4]; + + tmp_data[0] = 0x31; + if (kp_himax_bus_write(0x13, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + tmp_data[0] = (0x10 | auto_add_4_byte); + if (kp_himax_bus_write(0x0D, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } +} + +int hx83102_flash_write_burst(uint8_t *reg_byte, uint8_t *write_data) +{ + uint8_t data_byte[8]; + int i = 0, j = 0; + + for (i = 0; i < 4; i++) + data_byte[i] = reg_byte[i]; + for (j = 4; j < 8; j++) + data_byte[j] = write_data[j-4]; + + if (kp_himax_bus_write(0x00, data_byte, 8, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + return 0; +} + +static int hx83102_register_read(uint8_t *read_addr, int read_length, uint8_t *read_data) +{ + uint8_t tmp_data[4]; + int i = 0; + int address = 0; + + if (read_length > 256) { + E("%s: read len over 256!\n", __func__); + return LENGTH_FAIL; + } + if (read_length > 4) + hx83102_burst_enable(1); + else + hx83102_burst_enable(0); + + address = (read_addr[3] << 24) + (read_addr[2] << 16) + (read_addr[1] << 8) + read_addr[0]; + i = address; + tmp_data[0] = (uint8_t)i; + tmp_data[1] = (uint8_t)(i >> 8); + tmp_data[2] = (uint8_t)(i >> 16); + tmp_data[3] = (uint8_t)(i >> 24); + if (kp_himax_bus_write(0x00, tmp_data, 4, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + tmp_data[0] = 0x00; + if (kp_himax_bus_write(0x0C, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + if (kp_himax_bus_read(0x08, read_data, read_length, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + if (read_length > 4) + hx83102_burst_enable(0); + + return 0; +} + +#ifdef HX_RST_PIN_FUNC +static void hx83102_pin_reset(void) +{ + I("%s: Now reset the Touch chip.\n", __func__); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 0); + msleep(20); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 1); + msleep(50); +} +#endif + +static bool hx83102_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t tmp_writ[DATA_LEN_4]; + uint8_t tmp_data[DATA_LEN_4]; + + do { + if (cnt == 0 || (tmp_data[0] != 0xA5 && tmp_data[0] != 0x00 && tmp_data[0] != 0x87)) { + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x5C; + tmp_writ[3] = 0x00; tmp_writ[2] = 0x00; tmp_writ[1] = 0x00; tmp_writ[0] = 0xA5; + hx83102_flash_write_burst(tmp_addr, tmp_writ); + } + msleep(20); + + /* check fw status */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, DATA_LEN_4, tmp_data); + + if (tmp_data[0] != 0x05) { + I("%s: Do not need wait FW, Status = 0x%02X!\n", __func__, tmp_data[0]); + break; + } + + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x5C; + hx83102_register_read(tmp_addr, DATA_LEN_4, tmp_data); + I("%s: cnt = %d, data[0] = 0x%02X!\n", __func__, cnt, tmp_data[0]); + } while (tmp_data[0] != 0x87 && (++cnt < 50) && check_en == true); + + cnt = 0; + + + do { + /*=========================================== + *I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + *=========================================== + */ + tmp_data[0] = 0x27; + if (kp_himax_bus_write(0x31, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + *I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + *=========================================== + */ + tmp_data[0] = 0x95; + if (kp_himax_bus_write(0x32, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + *Check enter_save_mode + *====================== + */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, ADDR_LEN_4, tmp_data); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /*===================================== + *Reset TCON + *===================================== + */ + tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + /*===================================== + *Reset ADC + *===================================== + */ + tmp_addr[3] = 0x80; + tmp_addr[2] = 0x02; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + + return true; + } + usleep_range(10000, 10001); +#ifdef HX_RST_PIN_FUNC + hx83102_pin_reset(); +#endif + + } while (cnt++ < 15); + + return false; +} + +static bool hx83102ab_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t tmp_data[DATA_LEN_4]; + + do { + /*=========================================== + *I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + *=========================================== + */ + tmp_data[0] = 0x27; + if (kp_himax_bus_write(0x31, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + *I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + *=========================================== + */ + tmp_data[0] = 0x95; + if (kp_himax_bus_write(0x32, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + *Check enter_save_mode + *====================== + */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, ADDR_LEN_4, tmp_data); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /*===================================== + *Reset TCON + *===================================== + */ + tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + /*===================================== + *Reset ADC + *===================================== + */ + tmp_addr[3] = 0x80; + tmp_addr[2] = 0x02; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + + return true; + } + /*msleep(10);*/ + usleep_range(5000, 5001); +#ifdef HX_RST_PIN_FUNC + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 0); + msleep(20); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 1); + msleep(50); +#endif + + } while (cnt++ < 5); + + return false; +} + +static void hx83102ab_set_SMWP_enable(uint8_t SMWP_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t retry_cnt = 0; + + kp_g_core_fp->fp_sense_off(true); + + do { + if (SMWP_enable) { + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, back_data, 4); + } else { + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + } + + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_smwp_enable, DATA_LEN_4, tmp_data, false); + /*I("%s: tmp_data[0]=%d, SMWP_enable=%d, retry_cnt=%d\n", __func__, tmp_data[0],SMWP_enable,retry_cnt);*/ + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); + + kp_g_core_fp->fp_sense_on(0x00); +} + +static void hx83102ab_set_HSEN_enable(uint8_t HSEN_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + + if (HSEN_enable) { + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + } else { + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + } + + /*I("%s: tmp_data[0]=%d, HSEN_enable=%d, retry_cnt=%d\n", __func__, tmp_data[0],HSEN_enable,retry_cnt);*/ +} + +static void hx83102ab_usb_detect_set(uint8_t *cable_config) +{ + uint8_t tmp_data[DATA_LEN_4]; + + if (cable_config[1] == 0x01) { + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + I("%s: USB detect status IN!\n", __func__); + } else { + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + I("%s: USB detect status OUT!\n", __func__); + } +} + +static uint8_t hx83102ab_read_DD_status(uint8_t *cmd_set, uint8_t *tmp_data) +{ + int cnt = 0; + uint8_t req_size = cmd_set[0]; + + cmd_set[3] = (*kp_pfw_op)->data_dd_request[0]; + + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_dd_handshak_addr, DATA_LEN_4, cmd_set, 0); + I("%s: cmd_set[0] = 0x%02X,cmd_set[1] = 0x%02X,cmd_set[2] = 0x%02X,cmd_set[3] = 0x%02X\n", + __func__, cmd_set[0], cmd_set[1], cmd_set[2], cmd_set[3]); + + /* Doing hand shaking 0xAA -> 0xBB */ + for (cnt = 0; cnt < 100; cnt++) { + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_dd_handshak_addr, DATA_LEN_4, tmp_data, false); + usleep_range(10000, 10001); + + if (tmp_data[3] == (*kp_pfw_op)->data_dd_ack[0]) { + I("%s Data ready goto moving data\n", __func__); + break; + } + if (cnt >= 99) { + I("%s Data not ready in FW\n", __func__); + return FW_NOT_READY; + } + } + + kp_g_core_fp->fp_sense_off(true); + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_dd_data_addr, req_size, tmp_data, false); + kp_g_core_fp->fp_sense_on(0x01); + return NO_ERR; +} + +static int hx83102ab_read_FW_status(uint8_t *state_addr, uint8_t *tmp_addr) +{ + uint8_t i; + uint8_t req_size = 0; + uint8_t status_addr[DATA_LEN_4]; /*0x10007F44*/ + uint8_t cmd_addr[DATA_LEN_4]; /*0x900000F8*/ + + if (state_addr[0] == 0x01) { + state_addr[1] = 0x04; + + for (i = 0; i < DATA_LEN_4; i++) { + /* 0x10007F44 */ + state_addr[i + 2] = (*kp_pfw_op)->addr_fw_dbg_msg_addr[i]; + status_addr[i] = (*kp_pfw_op)->addr_fw_dbg_msg_addr[i]; + } + + req_size = 0x04; + kp_g_core_fp->fp_sense_off(true); + kp_g_core_fp->fp_register_read(status_addr, req_size, tmp_addr, false); + kp_g_core_fp->fp_sense_on(0x01); + } else if (state_addr[0] == 0x02) { + state_addr[1] = 0x30; + + for (i = 0; i < DATA_LEN_4; i++) { + /* 0x900000F8 */ + state_addr[i + 2] = (*kp_pfw_op)->addr_fw_state_addr[i]; + cmd_addr[i] = (*kp_pfw_op)->addr_fw_state_addr[i]; + } + + req_size = 0x30; + kp_g_core_fp->fp_register_read(cmd_addr, req_size, tmp_addr, false); + } + + return NO_ERR; +} + +static void hx83102ab_power_on_init(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + + I("%s:\n", __func__); + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_raw_out_sel, DATA_LEN_4, tmp_data, 0); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_sorting_mode_en, DATA_LEN_4, tmp_data, 0); + kp_g_core_fp->fp_touch_information(); + kp_g_core_fp->fp_sense_on(0x00); +} + +static int hx83102ab_fts_ctpm_fw_upgrade_with_sys_fs_64k(unsigned char *fw, int len, bool change_iref) +{ + int burnFW_success = 0; + uint8_t tmp_data[DATA_LEN_4]; + + if (len != FW_SIZE_64k) { + E("%s: The file size is not 64K bytes\n", __func__); + return false; + } + +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + kp_g_core_fp->fp_sense_off(true); + kp_g_core_fp->fp_chip_erase(); + kp_g_core_fp->fp_flash_programming(fw, FW_SIZE_64k); + + if (kp_g_core_fp->fp_check_CRC((*kp_pfw_op)->addr_program_reload_from, FW_SIZE_64k) == 0) + burnFW_success = 1; + + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_raw_out_sel, DATA_LEN_4, tmp_data, 0); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_sorting_mode_en, DATA_LEN_4, tmp_data, 0); +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + /*System reset*/ + kp_g_core_fp->fp_system_reset(); +#endif + return burnFW_success; +} + +#ifdef HX_ESD_RECOVERY +static void hx83102ab_esd_ic_reset(void) +{ + (*kp_HX_ESD_RESET_ACTIVATE) = 1; +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_pin_reset(); +#else + kp_g_core_fp->fp_system_reset(); +#endif + I("%s:\n", __func__); +} +#if defined(HX_ZERO_FLASH) +static int hx83102d_0f_esd_check(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + int ret = NO_ERR; + + I("Enter %s\n", __func__); + + kp_g_core_fp->fp_register_read((*kp_pzf_op)->addr_sts_chk, DATA_LEN_4, tmp_data, 0); + + if (tmp_data[0] != (*kp_pzf_op)->data_activ_sts[0]) { + ret = ERR_STS_WRONG; + I("%s:status : %8X = %2X\n", __func__, zf_addr_sts_chk, tmp_data[0]); + } + + kp_g_core_fp->fp_register_read((*kp_pzf_op)->addr_activ_relod, DATA_LEN_4, tmp_data, 0); + + if (tmp_data[0] != (*kp_pzf_op)->data_activ_in[0]) { + ret = ERR_STS_WRONG; + I("%s:status : %8X = %2X\n", __func__, zf_addr_activ_relod, tmp_data[0]); + } + + return ret; +} +#endif +#endif + +static bool hx83102d_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t tmp_data[DATA_LEN_4]; + + do { + if (cnt == 0 || (tmp_data[0] != 0xA5 && tmp_data[0] != 0x00 && tmp_data[0] != 0x87)) + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_ctrl_fw_isr, DATA_LEN_4, (*kp_pfw_op)->data_fw_stop, 0); + /*msleep(20);*/ + usleep_range(10000, 10001); + + /* check fw status */ + kp_g_core_fp->fp_register_read((*kp_pic_op)->addr_cs_central_state, ADDR_LEN_4, tmp_data, 0); + + if (tmp_data[0] != 0x05) { + I("%s: Do not need wait FW, Status = 0x%02X!\n", __func__, tmp_data[0]); + break; + } + + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_ctrl_fw_isr, 4, tmp_data, false); + I("%s: cnt = %d, data[0] = 0x%02X!\n", __func__, cnt, tmp_data[0]); + } while (tmp_data[0] != 0x87 && (++cnt < 10) && check_en == true); + + cnt = 0; + + do { + /*=========================================== + *I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + *=========================================== + */ + tmp_data[0] = 0x27; + if (kp_himax_bus_write(0x31, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + *I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + *=========================================== + */ + tmp_data[0] = 0x95; + if (kp_himax_bus_write(0x32, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + *Check enter_save_mode + *====================== + */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, ADDR_LEN_4, tmp_data); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /*===================================== + *Reset TCON + *===================================== + */ + tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + /*===================================== + *Reset ADC + *===================================== + */ + tmp_addr[3] = 0x80; + tmp_addr[2] = 0x02; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + + return true; + } + /*msleep(10);*/ + usleep_range(5000, 5001); +#ifdef HX_RST_PIN_FUNC + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 0); + msleep(20); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 1); + msleep(50); +#endif + + } while (cnt++ < 5); + + return false; +} + +static bool hx83102d_dd_clk_set(bool enable) +{ + uint8_t data[4] = {0}; + + data[0] = (enable)?0xDD:0; + return (kp_g_core_fp->fp_register_write((*kp_pic_op)->adr_osc_en, + sizeof((*kp_pic_op)->adr_osc_en), data, 0) == NO_ERR); +} + +static void hx83102d_dd_reg_en(bool enable) +{ + uint8_t data[4] = {0}; + + data[0] = 0xA5; data[1] = 0x00; + data[2] = 0x00; data[3] = 0x00; + kp_g_core_fp->fp_register_write((*kp_pic_op)->adr_osc_pw, DATA_LEN_4, data, 0); + + data[0] = 0xA5; data[1] = 0x83; + data[2] = 0x10; data[3] = 0x2D; + kp_g_core_fp->fp_dd_reg_write(0xB9, 0, 4, data, 0); +} + +static bool hx83102d_ic_id_read(void) +{ + int i = 0; + uint8_t data[4] = {0}; + + kp_g_core_fp->fp_dd_clk_set(true); + kp_g_core_fp->fp_dd_reg_en(true); + + for (i = 0; i < 13; i++) { + data[0] = 0x2A + i; + kp_g_core_fp->fp_dd_reg_write(0xBB, 3, 1, data, 0); + data[0] = 0x08; + kp_g_core_fp->fp_dd_reg_write(0xBB, 1, 1, data, 0); + kp_g_core_fp->fp_dd_reg_read(0xBB, 6, 1, data, 0); + (*kp_ic_data)->vendor_ic_id[i] = data[0]; + I("ic_data->vendor_ic_id[%d] = %02X\n", i, (*kp_ic_data)->vendor_ic_id[i]); + } + + kp_g_core_fp->fp_dd_clk_set(false); + + return true; +} + +#if defined(HX_ZERO_FLASH) +static void himax_hx83102d_reload_to_active(void) +{ + uint8_t addr[DATA_LEN_4] = {0}; + uint8_t data[DATA_LEN_4] = {0}; + uint8_t retry_cnt = 0; + + addr[3] = 0x90; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x48; + + do { + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0xEC; + kp_g_core_fp->fp_register_write(addr, DATA_LEN_4, data, 0); + usleep_range(1000, 1100); + kp_g_core_fp->fp_register_read(addr, DATA_LEN_4, data, 0); + I("%s: data[1]=%d, data[0]=%d, retry_cnt=%d\n", __func__, data[1], data[0], retry_cnt); + retry_cnt++; + } while ((data[1] != 0x01 || data[0] != 0xEC) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} + +static void himax_hx83102d_resume_ic_action(void) +{ +#ifndef HX_RESUME_HW_RESET + himax_hx83102d_reload_to_active(); +#endif +} + +static void himax_hx83102d_suspend_ic_action(void) +{ +#ifndef HX_RESUME_HW_RESET + himax_hx83102d_reload_to_active(); +#endif +} + +static void himax_hx83102d_sense_on(uint8_t FlashMode) +{ + uint8_t tmp_data[DATA_LEN_4]; + int retry = 0; + + I("Enter %s\n", __func__); + + kp_g_core_fp->fp_interface_on(); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_ctrl_fw_isr, + sizeof((*kp_pfw_op)->data_clear), (*kp_pfw_op)->data_clear, 0); + /*msleep(20);*/ + usleep_range(10000, 10001); + if (!FlashMode) { +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + himax_hx83102d_reload_to_active(); + } else { + himax_hx83102d_reload_to_active(); + do { + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_safe_mode_release_pw, + sizeof((*kp_pfw_op)->data_safe_mode_release_pw_active), (*kp_pfw_op)->data_safe_mode_release_pw_active, 0); + + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_flag_reset_event, DATA_LEN_4, tmp_data, 0); + I("%s:Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + } while ((tmp_data[1] != 0x01 || tmp_data[0] != 0x00) && retry++ < 5); + + if (retry >= 5) { + E("%s: Fail:\n", __func__); +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + himax_hx83102d_reload_to_active(); + } else { + I("%s:OK and Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + /* reset code*/ + tmp_data[0] = 0x00; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_safe_mode_release_pw, + sizeof((*kp_pfw_op)->data_safe_mode_release_pw_reset), (*kp_pfw_op)->data_safe_mode_release_pw_reset, 0); + } + } +} + +static void hx83102ab_firmware_update_0f(const struct firmware *fw_entry) +{ + uint8_t tmp_addr[4]; + + I("%s, Entering\n", __func__); + + kp_g_core_fp->fp_register_write((*kp_pzf_op)->addr_system_reset, 4, (*kp_pzf_op)->data_system_reset, 0); + + kp_g_core_fp->fp_sense_off(false); + + /* first 32K */ + /*bin file start*/ + /*isram*/ + kp_g_core_fp->fp_write_sram_0f(fw_entry, (*kp_pzf_op)->data_sram_start_addr, 0, HX_32K_SZ); + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x73; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_write_sram_0f(fw_entry, tmp_addr, 0x8000, (0x200*4)); + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x68; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_write_sram_0f(fw_entry, tmp_addr, 0x8800, (0x200*4)); + + /*last 16k*/ + /*config info*/ + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x72; + tmp_addr[0] = 0x04; /*0x10007206 <= size 60,from 0x0000C180*/ + kp_g_core_fp->fp_write_sram_0f(fw_entry, tmp_addr, 0xC178, (84)); + + /*FW config*/ + if ((*kp_G_POWERONOF) == 1) + kp_g_core_fp->fp_write_sram_0f(fw_entry, (*kp_pzf_op)->data_fw_cfg_1, 0xBFFE, 388); + else + kp_g_core_fp->fp_clean_sram_0f((*kp_pzf_op)->data_fw_cfg_1, 388, 1); + /*ADC config*/ + if ((*kp_G_POWERONOF) == 1) + kp_g_core_fp->fp_write_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_1, 0xe000, (128 * 4)); + else + kp_g_core_fp->fp_clean_sram_0f((*kp_pzf_op)->data_adc_cfg_1, (128 * 4), 2); + + I("%s, END\n", __func__); +} +#if defined(HX_0F_DEBUG) +static void hx83102ab_firmware_read_0f(const struct firmware *fw_entry, int type) +{ + uint8_t tmp_addr[4]; + + I("%s, Entering\n", __func__); + if (type == 0) { /* first 32K */ + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_sram_start_addr, 0, HX_32K_SZ); + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x68; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_addr, 0x8000, (0x200*4)); + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x73; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_addr, 0x8800, (0x200*4)); + + } else { /*last 16k*/ + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x72; + tmp_addr[0] = 0x04; /*0x10007206 <= size 80,from 0x0000C180*/ + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_addr, 0xC178, (84)); + + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_fw_cfg_1, 0xBFFE, 388); + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_1, 0xE000, (128*4)); + + } + I("%s, END\n", __func__); +} +static void hx83102d_firmware_read_0f(const struct firmware *fw_entry, int type) +{ + uint8_t tmp_data[4]; + + I("%s, Entering\n", __func__); + + switch (type) { + case 0: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_sram_start_addr, 0, HX_40K_SZ); + break; + case 1: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_cfg_info, 0xC000, 132); + break; + case 2: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_fw_cfg_1, 0xC0FE, 484); + break; + case 3: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_1, 0xD000, 768); + break; + case 4: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_2, 0xD300, 1536); + break; + case 5: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_3, 0xE000, 1536); + break; + case 6: + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_bor_prevent_info, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xC9E0, 32); + break; + case 7: + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_notch_info, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xCA00, 128); + break; + case 8: + kp_himax_in_parse_assign_cmd(hx83102d_zf_func_info_en, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xCB00, 12); + break; + case 9: + kp_himax_in_parse_assign_cmd(hx83102d_zf_po_sub_func, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xA000, HX4K); + break; + default: + break; + } + I("%s, END\n", __func__); +} +#endif +#if defined(HX_CODE_OVERLAY) +static int hx83102d_0f_overlay(int ovl_type, int mode) +{ + int ret = 0; + uint8_t count = 0; + uint8_t count2 = 0; + const struct firmware *fwp = NULL; + char fwname[32] = "Himax_firmware.bin"; + uint8_t i, j; + uint8_t part_num = 0; + uint8_t buf[16]; + struct zf_info zf_info_arr[3]; + uint8_t sram_addr[4] = {0xE0, 0x8C, 0x00, 0x20}; + uint8_t handshaking_addr[4] = {0xFC, 0x7F, 0x00, 0x10}; + /*uint8_t mpap_addr[4] = {0xEC, 0x73, 0x00, 0x10};*/ + uint8_t request; + uint8_t reply; + uint32_t offset = 0; + uint32_t size = 0; + uint8_t send_data[4] = {0}; + uint8_t recv_data[4] = {0}; + + ret = request_firmware(&fwp, fwname, private_ts->dev); + if (ret < 0) { + E("%s: request firmware FAIL!!!\n", __func__); + return ret; + } + + /*1. get number of partition*/ + part_num = fwp->data[HX64K + 12]; + if (part_num <= 1) { + E("%s, size of cfg part failed! part_num = %d\n", __func__, part_num); + return LENGTH_FAIL; + } + + j = 0; + for (i = 1; i < part_num; i++) { + /* find overlay section */ + memcpy(buf, &fwp->data[i * 0x10 + HX64K], 16); + + if (j < 3 && buf[3] == sram_addr[3] && buf[2] == sram_addr[2] && buf[1] == sram_addr[1] && buf[0] == sram_addr[0]) { + + zf_info_arr[j].write_size = buf[7] << 24 | buf[6] << 16 | buf[5] << 8 | buf[4]; + zf_info_arr[j].fw_addr = buf[11] << 24 | buf[10] << 16 | buf[9] << 8 | buf[8]; + /*I("j = %d; write_size = %d; fw_addr = %d\n", j, zf_info_arr[j].write_size, zf_info_arr[j].fw_addr);*/ + j++; + } + } + +/* + * if (ovl_type == 1) { + * if (mode == 0) { + * //border overlay when finish self_test + * send_data[3] = 0x00; + * send_data[2] = 0x00; + * send_data[1] = 0x00; + * send_data[0] = 0x00; + * offset = zf_info_arr[2].fw_addr; + * size = zf_info_arr[2].write_size; + * I("%s: self test overlay section 2\n", __func__); + * } else if (mode == 1) { + * //sorting overlay when call self_test + * send_data[3] = 0x00; + * send_data[2] = 0x10; + * send_data[1] = 0x73; + * send_data[0] = 0x80; + * offset = zf_info_arr[0].fw_addr; + * size = zf_info_arr[0].write_size; + * I("%s: self test overlay section 0\n", __func__); + * } + * } + */ + if (ovl_type == 2) { + /*gesture overlay when suspend*/ + request = ovl_gesture_request; + reply = ovl_gesture_reply; + offset = zf_info_arr[1].fw_addr; + size = zf_info_arr[1].write_size; + I("%s: overlay section 1\n", __func__); + } else if (ovl_type == 3) { + /*border overlay when resume*/ + request = ovl_border_request; + reply = ovl_border_reply; + offset = zf_info_arr[2].fw_addr; + size = zf_info_arr[2].write_size; + I("%s: overlay section 2\n", __func__); + } else { + return HX_INIT_FAIL; + } + +/* + * if (ovl_type == 1) { + * + * g_core_fp.fp_sense_off(false); + * + * //g_core_fp.fp_register_write(mpap_addr, DATA_LEN_4, send_data, 0); + * count = 0; + * do { + * g_core_fp.fp_register_write(mpap_addr, DATA_LEN_4, send_data, 0); + * g_core_fp.fp_register_read(mpap_addr, DATA_LEN_4, recv_data, 0); + * } while ((recv_data[0] != send_data[0] || recv_data[1] != send_data[1] || recv_data[2] != send_data[2] || recv_data[3] != send_data[3]) && count++ < 10); + * + * I("%s: set mpap pw %d times\n", __func__, count); + * + * //g_core_fp.fp_write_sram_0f(fwp, sram_addr, offset, size); + * if (kp_g_core_fp->fp_write_sram_0f_crc(fwp, sram_addr, offset, size) != 0) + * E("%s, Overlay HW CRC FAIL\n", __func__); + * + * g_core_fp.fp_reload_disable(0); + * + * g_core_fp.fp_sense_on(0x00); + * + * } else { + */ + + count = 0; + do { + kp_g_core_fp->fp_register_read(handshaking_addr, DATA_LEN_4, recv_data, 0); + } while (recv_data[0] != request && count++ < 10); + + if (count < 10) { + /*g_core_fp.fp_write_sram_0f(fwp, sram_addr, offset, size);*/ + if (kp_g_core_fp->fp_write_sram_0f_crc(fwp, sram_addr, offset, size) != 0) + E("%s, Overlay HW CRC FAIL\n", __func__); + + send_data[3] = 0x00; + send_data[2] = 0x00; + send_data[1] = 0x00; + send_data[0] = reply; + + count2 = 0; + do { + kp_g_core_fp->fp_register_write(handshaking_addr, DATA_LEN_4, send_data, 0); + usleep_range(1000, 1100); + kp_g_core_fp->fp_register_read(handshaking_addr, DATA_LEN_4, recv_data, 0); + } while (recv_data[0] != reply && count2++ < 10); + + if (ovl_type == 3) { +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + } + } + + I("%s: overlay request %d times; reply %d times\n", __func__, count, count2); + + release_firmware(fwp); + + /* rescue mechanism */ + if (count >= 10) { + + kp_g_core_fp->fp_0f_op_file_dirly(fwname); + + kp_g_core_fp->fp_reload_disable(0); + + kp_g_core_fp->fp_sense_on(0x00); + + kp_himax_int_enable(1); + } + + return 0; +} +#endif +#endif + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) +static void hx83102a_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + (*kp_ic_data)->vendor_ic_ver = 0xFFFF; + /* Enable read DD */ + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x10; + tmp_data[3] = 0x2A; + kp_himax_in_parse_assign_cmd(hx83102_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83102_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; + + I("VENDOR_IC_VER : %X\n", ((*kp_ic_data)->vendor_ic_ver)); +} + +static void hx83102b_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + (*kp_ic_data)->vendor_ic_ver = 0xFFFF; + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83102b_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x01; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x10; + tmp_data[3] = 0x2B; + kp_himax_in_parse_assign_cmd(hx83102_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83102_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; + + /* Disable read DD */ + kp_himax_in_parse_assign_cmd(hx83102b_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x00; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + I("VENDOR_IC_VER : %X\n", ((*kp_ic_data)->vendor_ic_ver)); +} + +static void hx83102d_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + (*kp_ic_data)->vendor_ic_ver = 0xFFFF; + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83102d_ic_osc_pw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xA5; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + kp_himax_in_parse_assign_cmd(hx83102d_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xDD; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x10; + tmp_data[3] = 0x2D; + kp_himax_in_parse_assign_cmd(hx83102_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83102_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; + + /* Disable read DD */ + kp_himax_in_parse_assign_cmd(hx83102d_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x00; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + I("VENDOR_IC_VER : %X\n", ((*kp_ic_data)->vendor_ic_ver)); +} + +static void hx83102_dynamic_fw_name(uint8_t ic_name) +{ + char firmware_name[64]; + + if (ic_name == 0x2a) + hx83102a_read_ic_ver(); + else if (ic_name == 0x2b) + hx83102b_read_ic_ver(); + else + hx83102d_read_ic_ver(); + + if ((*kp_i_CTPM_firmware_name) != NULL) { + kfree((*kp_i_CTPM_firmware_name)); + (*kp_i_CTPM_firmware_name) = NULL; + } + memset(firmware_name, 0x00, sizeof(firmware_name)); + + if (((*kp_ic_data)->vendor_ic_ver == 0x00) || ((*kp_ic_data)->vendor_ic_ver == 0x21)) { /* Cut 1 */ + memcpy(firmware_name, "Himax_firmware_1.bin", sizeof(char)*strlen("Himax_firmware_1.bin")); + } else if (((*kp_ic_data)->vendor_ic_ver == 0x01) || ((*kp_ic_data)->vendor_ic_ver == 0x11)) { /* Cut 2 */ + memcpy(firmware_name, "Himax_firmware_2.bin", sizeof(char)*strlen("Himax_firmware_2.bin")); + } else { /* Original */ + memcpy(firmware_name, "Himax_firmware.bin", sizeof(char)*strlen("Himax_firmware.bin")); + } + + (*kp_i_CTPM_firmware_name) = kzalloc((sizeof(char)*(strlen(firmware_name)+1)), GFP_KERNEL); + if ((*kp_i_CTPM_firmware_name) != NULL) + memcpy((*kp_i_CTPM_firmware_name), firmware_name, (sizeof(char)*(strlen(firmware_name)+1))); + I("(*kp_i_CTPM_firmware_name) : %s\n", (*kp_i_CTPM_firmware_name)); +} +#endif +#endif + +static bool hx83102e_read_event_stack(uint8_t *buf, uint8_t length) +{ + struct timespec t_start, t_end, t_delta; + int len = length; + int i2c_speed = 0; + + if ((*kp_private_ts)->debug_log_level & BIT(2)) + getnstimeofday(&t_start); + + kp_himax_bus_read((*kp_pfw_op)->addr_event_addr[0], buf, length, HIMAX_I2C_RETRY_TIMES); + + if ((*kp_private_ts)->debug_log_level & BIT(2)) { + getnstimeofday(&t_end); + t_delta.tv_nsec = (t_end.tv_sec * 1000000000 + t_end.tv_nsec) - (t_start.tv_sec * 1000000000 + t_start.tv_nsec); /*ns*/ + i2c_speed = (len * 9 * 1000000 / (int)t_delta.tv_nsec) * 13 / 10; + (*kp_private_ts)->bus_speed = (int)i2c_speed; + } + return 1; +} + +static void himax_hx83102ab_reg_re_init(uint8_t ic_name) +{ + I("%s:Entering!\n", __func__); + kp_himax_in_parse_assign_cmd(hx83102ab_fw_addr_sorting_mode_en, (*kp_pfw_op)->addr_sorting_mode_en, sizeof((*kp_pfw_op)->addr_sorting_mode_en)); + kp_himax_in_parse_assign_cmd(hx83102ab_fw_addr_selftest_addr_en, (*kp_pfw_op)->addr_selftest_addr_en, sizeof((*kp_pfw_op)->addr_selftest_addr_en)); +#ifdef HX_ZERO_FLASH + kp_himax_in_parse_assign_cmd(hx83102ab_data_adc_cfg_1, (*kp_pzf_op)->data_adc_cfg_1, sizeof((*kp_pzf_op)->data_adc_cfg_1)); +#endif + + if (ic_name == 0x2a) { + kp_himax_in_parse_assign_cmd(hx83102a_data_df_rx, (*kp_pdriver_op)->data_df_rx, sizeof((*kp_pdriver_op)->data_df_rx)); + kp_himax_in_parse_assign_cmd(hx83102a_data_df_tx, (*kp_pdriver_op)->data_df_tx, sizeof((*kp_pdriver_op)->data_df_tx)); + kp_himax_in_parse_assign_cmd(hx83102a_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102a_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + } else { + kp_himax_in_parse_assign_cmd(hx83102b_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102b_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + } +} + +static void himax_hx83102ab_func_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_g_core_fp->fp_chip_init = hx83102_chip_init; + kp_g_core_fp->fp_sense_off = hx83102ab_sense_off; + kp_g_core_fp->fp_set_SMWP_enable = hx83102ab_set_SMWP_enable; + kp_g_core_fp->fp_set_HSEN_enable = hx83102ab_set_HSEN_enable; + kp_g_core_fp->fp_usb_detect_set = hx83102ab_usb_detect_set; + kp_g_core_fp->fp_read_DD_status = hx83102ab_read_DD_status; + kp_g_core_fp->fp_read_FW_status = hx83102ab_read_FW_status; + kp_g_core_fp->fp_power_on_init = hx83102ab_power_on_init; + kp_g_core_fp->fp_fts_ctpm_fw_upgrade_with_sys_fs_64k = hx83102ab_fts_ctpm_fw_upgrade_with_sys_fs_64k; +#ifdef HX_ESD_RECOVERY + kp_g_core_fp->fp_esd_ic_reset = hx83102ab_esd_ic_reset; +#endif +#if defined(HX_ZERO_FLASH) + kp_g_core_fp->fp_firmware_update_0f = hx83102ab_firmware_update_0f; +#if defined(HX_0F_DEBUG) + kp_g_core_fp->fp_firmware_read_0f = hx83102ab_firmware_read_0f; +#endif +#endif + +} + +static void himax_hx83102d_reg_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_himax_in_parse_assign_cmd(hx83102d_fw_addr_raw_out_sel, (*kp_pfw_op)->addr_raw_out_sel, sizeof((*kp_pfw_op)->addr_raw_out_sel)); +#if defined(HX_ZERO_FLASH) + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_sram_start_addr, (*kp_pzf_op)->data_sram_start_addr, sizeof((*kp_pzf_op)->data_sram_start_addr)); + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_adc_cfg_1, (*kp_pzf_op)->data_adc_cfg_1, sizeof((*kp_pzf_op)->data_adc_cfg_1)); + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_adc_cfg_2, (*kp_pzf_op)->data_adc_cfg_2, sizeof((*kp_pzf_op)->data_adc_cfg_2)); + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_adc_cfg_3, (*kp_pzf_op)->data_adc_cfg_3, sizeof((*kp_pzf_op)->data_adc_cfg_3)); +#endif + kp_himax_in_parse_assign_cmd(hx83102d_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102d_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + kp_himax_in_parse_assign_cmd(hx83102d_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + kp_himax_in_parse_assign_cmd(hx83102d_adr_osc_en, (*kp_pic_op)->adr_osc_en, sizeof((*kp_pic_op)->adr_osc_en)); + kp_himax_in_parse_assign_cmd(hx83102d_adr_osc_pw, (*kp_pic_op)->adr_osc_pw, sizeof((*kp_pic_op)->adr_osc_pw)); +} + +static void himax_hx83102d_func_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_g_core_fp->fp_chip_init = hx83102_chip_init; + kp_g_core_fp->fp_sense_off = hx83102d_sense_off; + kp_g_core_fp->fp_ic_id_read = hx83102d_ic_id_read; + kp_g_core_fp->fp_dd_clk_set = hx83102d_dd_clk_set; + kp_g_core_fp->fp_dd_reg_en = hx83102d_dd_reg_en; +#if defined(HX_ZERO_FLASH) + /*kp_g_core_fp->fp_firmware_update_0f = hx83102d_firmware_update_0f;*/ + kp_g_core_fp->fp_resume_ic_action = himax_hx83102d_resume_ic_action; + kp_g_core_fp->fp_suspend_ic_action = himax_hx83102d_suspend_ic_action; + kp_g_core_fp->fp_sense_on = himax_hx83102d_sense_on; +#if defined(HX_0F_DEBUG) + kp_g_core_fp->fp_firmware_read_0f = hx83102d_firmware_read_0f; +#endif +#if defined(HX_CODE_OVERLAY) + kp_g_core_fp->fp_0f_overlay = hx83102d_0f_overlay; +#endif +#ifdef HX_ESD_RECOVERY + kp_g_core_fp->fp_0f_esd_check = hx83102d_0f_esd_check; +#endif +#endif +} + +static void himax_hx83102e_reg_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_himax_in_parse_assign_cmd(hx83102e_fw_addr_raw_out_sel, (*kp_pfw_op)->addr_raw_out_sel, sizeof((*kp_pfw_op)->addr_raw_out_sel)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_rx, (*kp_pdriver_op)->data_df_rx, sizeof((*kp_pdriver_op)->data_df_rx)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_tx, (*kp_pdriver_op)->data_df_tx, sizeof((*kp_pdriver_op)->data_df_tx)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); +} + +static void himax_hx83102e_func_re_init(void) +{ + I("%s:Entering!\n", __func__); + + kp_g_core_fp->fp_chip_init = hx83102e_chip_init; + kp_g_core_fp->fp_sense_off = hx83102d_sense_off; + kp_g_core_fp->fp_read_event_stack = hx83102e_read_event_stack; +} + + +static bool hx83102_chip_detect(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + bool ret_data = false; + int ret = 0; + int i = 0; + + if (himax_ic_setup_external_symbols()) + return false; + +#ifdef HX_RST_PIN_FUNC + hx83102_pin_reset(); +#endif + if (kp_himax_bus_read(0x13, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + if (hx83102_sense_off(false) == false) { + ret_data = false; + E("%s:hx83102_sense_off Fail:\n", __func__); + return ret_data; + } + + for (i = 0; i < 5; i++) { + tmp_addr[3] = 0x90; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0xD0; + if (hx83102_register_read(tmp_addr, DATA_LEN_4, tmp_data) != 0) { + ret_data = false; + E("%s:hx83102_register_read Fail:\n", __func__); + return ret_data; + } + I("%s:Read driver IC ID = %X,%X,%X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1]); /*83,10,2X*/ + + if ((tmp_data[3] == 0x83) && (tmp_data[2] == 0x10) && ((tmp_data[1] == 0x2a) || (tmp_data[1] == 0x2b) || (tmp_data[1] == 0x2d) || (tmp_data[1] == 0x2e))) { + if (tmp_data[1] == 0x2a) { + strlcpy((*kp_private_ts)->chip_name, HX_83102A_SERIES_PWON, 30); + I("%s:detect IC HX83102A successfully\n", __func__); + } else if (tmp_data[1] == 0x2b) { + strlcpy((*kp_private_ts)->chip_name, HX_83102B_SERIES_PWON, 30); + I("%s:detect IC HX83102B successfully\n", __func__); + } else if (tmp_data[1] == 0x2d) { + strlcpy((*kp_private_ts)->chip_name, HX_83102D_SERIES_PWON, 30); + I("%s:detect IC HX83102D successfully\n", __func__); + } else { + strlcpy((*kp_private_ts)->chip_name, HX_83102E_SERIES_PWON, 30); + I("%s:detect IC HX83102E successfully\n", __func__); + } + + ret = kp_himax_mcu_in_cmd_struct_init(); + if (ret < 0) { + ret_data = false; + E("%s:cmd_struct_init Fail:\n", __func__); + return ret_data; + } + + kp_himax_mcu_in_cmd_init(); + if ((tmp_data[1] == 0x2a) || (tmp_data[1] == 0x2b)) { + himax_hx83102ab_reg_re_init(tmp_data[1]); + himax_hx83102ab_func_re_init(); + } else if (tmp_data[1] == 0x2d) { + himax_hx83102d_reg_re_init(); + himax_hx83102d_func_re_init(); + } else {/* 0x2e */ + himax_hx83102e_reg_re_init(); + himax_hx83102e_func_re_init(); + } + ret_data = true; + break; + } + + ret_data = false; + E("%s:Read driver ID register Fail:\n", __func__); + E("Could NOT find Himax Chipset\n"); + E("Please check 1.VCCD,VCCA,VSP,VSN\n"); + E("2. LCM_RST,TP_RST\n"); + E("3. Power On Sequence\n"); + + } + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) + hx83102_dynamic_fw_name(tmp_data[1]); +#endif +#endif + + return ret_data; +} + +DECLARE(HX_MOD_KSYM_HX83102); + +static int himax_hx83102_probe(void) +{ + I("%s:Enter\n", __func__); + himax_add_chip_dt(hx83102_chip_detect); + + return 0; +} + +static int himax_hx83102_remove(void) +{ + free_chip_dt_table(); + return 0; +} + +static int __init himax_hx83102_init(void) +{ + int ret = 0; + + I("%s\n", __func__); + ret = himax_hx83102_probe(); + return 0; +} + +static void __exit himax_hx83102_exit(void) +{ + himax_hx83102_remove(); +} + +module_init(himax_hx83102_init); +module_exit(himax_hx83102_exit); + +MODULE_DESCRIPTION("HIMAX HX83102 touch driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102/himax_ic_HX83102.h b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102/himax_ic_HX83102.h new file mode 100644 index 000000000000..ceab657ecdd0 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102/himax_ic_HX83102.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for HX83102 chipset + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_platform.h" +#include "himax_common.h" +#include "himax_ic_core.h" +#include + +#define hx83102ab_fw_addr_sorting_mode_en 0x100007FC +#define hx83102ab_fw_addr_selftest_addr_en 0x100007F8 +#define hx83102ab_data_adc_cfg_1 0x10007B00 +#define hx83102a_data_df_rx 48 +#define hx83102a_data_df_tx 24 +#define hx83102a_data_df_x_res 2160 +#define hx83102a_data_df_y_res 3840 +#define hx83102b_data_df_x_res 720 +#define hx83102b_data_df_y_res 1280 + +#define hx83102d_fw_addr_raw_out_sel 0x800204f4 +#define hx83102d_zf_data_adc_cfg_1 0x10007B00 +#define hx83102d_zf_data_adc_cfg_2 0x10006A00 +#define hx83102d_zf_data_adc_cfg_3 0x10007500 +#define hx83102d_zf_data_bor_prevent_info 0x10007268 +#define hx83102d_zf_data_notch_info 0x10007300 +#define hx83102d_zf_func_info_en 0x10007FD0 +#define hx83102d_zf_po_sub_func 0x10005A00 +#define hx83102d_zf_data_sram_start_addr 0x20000000 +#define hx83102d_data_df_x_res 720 +#define hx83102d_data_df_y_res 1280 +#define hx83102d_adr_osc_en 0x9000009C +#define hx83102d_adr_osc_pw 0x90000280 + +#define hx83102e_fw_addr_raw_out_sel 0x100072EC +#define hx83102e_data_df_rx 48 +#define hx83102e_data_df_tx 30 +#define hx83102e_data_df_x_res 1200 +#define hx83102e_data_df_y_res 1920 + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#ifdef HX_EN_DYNAMIC_NAME +#define hx83102_ic_b9_en 0x300B9000 +#define hx83102_ic_fw 0x300E8006 + +#define hx83102b_ic_osc_en 0x900880A0 +#define hx83102d_ic_osc_en 0x9000009C +#define hx83102d_ic_osc_pw 0x90000280 +#endif +#endif + +#ifdef HX_ESD_RECOVERY + extern u8 HX_ESD_RESET_ACTIVATE; +#endif + diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102d/Android.mk b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102d/Android.mk new file mode 100644 index 000000000000..57c41e5cce97 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102d/Android.mk @@ -0,0 +1,20 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +HIMAX_MMI_IC_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +HIMAX_MMI_IC_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +ifeq ($(HIMAX_V2_MMI_HX83102D),true) +KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_0FLASH_MMI_IC_NAME_D=hx83102d +endif + + +include $(CLEAR_VARS) +LOCAL_MODULE := himax_0flash_mmi_hx83102d.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(HIMAX_MMI_IC_MODULE_PATH) +LOCAL_REQUIRED_MODULES := himax_0flash_mmi.ko +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102d/Kbuild b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102d/Kbuild new file mode 100644 index 000000000000..f68b432ae4a8 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102d/Kbuild @@ -0,0 +1,20 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_0flash_mmi \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_0flash_mmi/support_ic \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102d +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_DEBUG \ + -DCONFIG_TOUCHSCREEN_HIMAX_INSPECT + +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_INCELL +EXTRA_CFLAGS += -DCONFIG_HMX_DB + +ifneq ($(filter hx83102d,$(CONFIG_INPUT_HIMAX_0FLASH_MMI_IC_NAME_D)),) + EXTRA_CFLAGS += -D__HIMAX_HX83102D_MOD__ + EXTRA_CFLAGS += -DHX_ZERO_FLASH + EXTRA_CFLAGS += -DHX_CODE_OVERLAY +endif + +obj-m := himax_0flash_mmi_hx83102d.o +himax_0flash_mmi_hx83102d-objs += himax_ic_HX83102d.o diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102d/Makefile b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102d/Makefile new file mode 100755 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102d/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102d/himax_ic_HX83102d.c b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102d/himax_ic_HX83102d.c new file mode 100644 index 000000000000..2ca679fbc37c --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102d/himax_ic_HX83102d.c @@ -0,0 +1,1449 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for HX83102 chipset + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_ic_HX83102d.h" +#include "himax_modular.h" + +static void hx83102_chip_init(void) +{ + (*kp_private_ts)->chip_cell_type = CHIP_IS_IN_CELL; + I("%s:IC cell type = %d\n", __func__, (*kp_private_ts)->chip_cell_type); + (*kp_IC_CHECKSUM) = HX_TP_BIN_CHECKSUM_CRC; + /*Himax: Set FW and CFG Flash Address*/ + (*kp_FW_VER_MAJ_FLASH_ADDR) = 49157; /*0x00C005*/ + (*kp_FW_VER_MAJ_FLASH_LENG) = 1; + (*kp_FW_VER_MIN_FLASH_ADDR) = 49158; /*0x00C006*/ + (*kp_FW_VER_MIN_FLASH_LENG) = 1; + (*kp_CFG_VER_MAJ_FLASH_ADDR) = 49408; /*0x00C100*/ + (*kp_CFG_VER_MAJ_FLASH_LENG) = 1; + (*kp_CFG_VER_MIN_FLASH_ADDR) = 49409; /*0x00C101*/ + (*kp_CFG_VER_MIN_FLASH_LENG) = 1; + (*kp_CID_VER_MAJ_FLASH_ADDR) = 49154; /*0x00C002*/ + (*kp_CID_VER_MAJ_FLASH_LENG) = 1; + (*kp_CID_VER_MIN_FLASH_ADDR) = 49155; /*0x00C003*/ + (*kp_CID_VER_MIN_FLASH_LENG) = 1; + /*PANEL_VERSION_ADDR = 49156;*/ /*0x00C004*/ + /*PANEL_VERSION_LENG = 1;*/ +} + +static void hx83102e_chip_init(void) +{ + (*kp_private_ts)->chip_cell_type = CHIP_IS_IN_CELL; + I("%s:IC cell type = %d\n", __func__, (*kp_private_ts)->chip_cell_type); + (*kp_IC_CHECKSUM) = HX_TP_BIN_CHECKSUM_CRC; + /*Himax: Set FW and CFG Flash Address*/ + (*kp_FW_VER_MAJ_FLASH_ADDR) = 59397; /*0x00E805*/ + (*kp_FW_VER_MAJ_FLASH_LENG) = 1; + (*kp_FW_VER_MIN_FLASH_ADDR) = 59398; /*0x00E806*/ + (*kp_FW_VER_MIN_FLASH_LENG) = 1; + (*kp_CFG_VER_MAJ_FLASH_ADDR) = 59648; /*0x00E900*/ + (*kp_CFG_VER_MAJ_FLASH_LENG) = 1; + (*kp_CFG_VER_MIN_FLASH_ADDR) = 59649; /*0x00E901*/ + (*kp_CFG_VER_MIN_FLASH_LENG) = 1; + (*kp_CID_VER_MAJ_FLASH_ADDR) = 59394; /*0x00E802*/ + (*kp_CID_VER_MAJ_FLASH_LENG) = 1; + (*kp_CID_VER_MIN_FLASH_ADDR) = 59395; /*0x00E803*/ + (*kp_CID_VER_MIN_FLASH_LENG) = 1; + /*PANEL_VERSION_ADDR = 49156;*/ /*0x00C004*/ + /*PANEL_VERSION_LENG = 1;*/ +} + +void hx83102_burst_enable(uint8_t auto_add_4_byte) +{ + uint8_t tmp_data[4]; + + tmp_data[0] = 0x31; + if (kp_himax_bus_write(0x13, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + tmp_data[0] = (0x10 | auto_add_4_byte); + if (kp_himax_bus_write(0x0D, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } +} + +int hx83102_flash_write_burst(uint8_t *reg_byte, uint8_t *write_data) +{ + uint8_t data_byte[8]; + int i = 0, j = 0; + + for (i = 0; i < 4; i++) + data_byte[i] = reg_byte[i]; + for (j = 4; j < 8; j++) + data_byte[j] = write_data[j-4]; + + if (kp_himax_bus_write(0x00, data_byte, 8, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + return 0; +} + +static int hx83102_register_read(uint8_t *read_addr, int read_length, uint8_t *read_data) +{ + uint8_t tmp_data[4]; + int i = 0; + int address = 0; + + if (read_length > 256) { + E("%s: read len over 256!\n", __func__); + return LENGTH_FAIL; + } + if (read_length > 4) + hx83102_burst_enable(1); + else + hx83102_burst_enable(0); + + address = (read_addr[3] << 24) + (read_addr[2] << 16) + (read_addr[1] << 8) + read_addr[0]; + i = address; + tmp_data[0] = (uint8_t)i; + tmp_data[1] = (uint8_t)(i >> 8); + tmp_data[2] = (uint8_t)(i >> 16); + tmp_data[3] = (uint8_t)(i >> 24); + if (kp_himax_bus_write(0x00, tmp_data, 4, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + tmp_data[0] = 0x00; + if (kp_himax_bus_write(0x0C, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + if (kp_himax_bus_read(0x08, read_data, read_length, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + if (read_length > 4) + hx83102_burst_enable(0); + + return 0; +} + +#ifdef HX_RST_PIN_FUNC +static void hx83102_pin_reset(void) +{ + I("%s: Now reset the Touch chip.\n", __func__); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 0); + msleep(20); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 1); + msleep(50); +} +#endif + +static bool hx83102_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t tmp_writ[DATA_LEN_4]; + uint8_t tmp_data[DATA_LEN_4]; + + do { + if (cnt == 0 || (tmp_data[0] != 0xA5 && tmp_data[0] != 0x00 && tmp_data[0] != 0x87)) { + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x5C; + tmp_writ[3] = 0x00; tmp_writ[2] = 0x00; tmp_writ[1] = 0x00; tmp_writ[0] = 0xA5; + hx83102_flash_write_burst(tmp_addr, tmp_writ); + } + msleep(20); + + /* check fw status */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, DATA_LEN_4, tmp_data); + + if (tmp_data[0] != 0x05) { + I("%s: Do not need wait FW, Status = 0x%02X!\n", __func__, tmp_data[0]); + break; + } + + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x5C; + hx83102_register_read(tmp_addr, DATA_LEN_4, tmp_data); + I("%s: cnt = %d, data[0] = 0x%02X!\n", __func__, cnt, tmp_data[0]); + } while (tmp_data[0] != 0x87 && (++cnt < 50) && check_en == true); + + cnt = 0; + + + do { + /*=========================================== + *I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + *=========================================== + */ + tmp_data[0] = 0x27; + if (kp_himax_bus_write(0x31, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + *I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + *=========================================== + */ + tmp_data[0] = 0x95; + if (kp_himax_bus_write(0x32, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + *Check enter_save_mode + *====================== + */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, ADDR_LEN_4, tmp_data); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /*===================================== + *Reset TCON + *===================================== + */ + tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + /*===================================== + *Reset ADC + *===================================== + */ + tmp_addr[3] = 0x80; + tmp_addr[2] = 0x02; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + + return true; + } + usleep_range(10000, 10001); +#ifdef HX_RST_PIN_FUNC + hx83102_pin_reset(); +#endif + + } while (cnt++ < 15); + + return false; +} + +static bool hx83102ab_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t tmp_data[DATA_LEN_4]; + + do { + /*=========================================== + *I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + *=========================================== + */ + tmp_data[0] = 0x27; + if (kp_himax_bus_write(0x31, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + *I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + *=========================================== + */ + tmp_data[0] = 0x95; + if (kp_himax_bus_write(0x32, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + *Check enter_save_mode + *====================== + */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, ADDR_LEN_4, tmp_data); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /*===================================== + *Reset TCON + *===================================== + */ + tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + /*===================================== + *Reset ADC + *===================================== + */ + tmp_addr[3] = 0x80; + tmp_addr[2] = 0x02; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + + return true; + } + /*msleep(10);*/ + usleep_range(5000, 5001); +#ifdef HX_RST_PIN_FUNC + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 0); + msleep(20); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 1); + msleep(50); +#endif + + } while (cnt++ < 5); + + return false; +} + +static void hx83102ab_set_SMWP_enable(uint8_t SMWP_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t retry_cnt = 0; + + kp_g_core_fp->fp_sense_off(true); + + do { + if (SMWP_enable) { + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, back_data, 4); + } else { + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + } + + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_smwp_enable, DATA_LEN_4, tmp_data, false); + /*I("%s: tmp_data[0]=%d, SMWP_enable=%d, retry_cnt=%d\n", __func__, tmp_data[0],SMWP_enable,retry_cnt);*/ + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); + + kp_g_core_fp->fp_sense_on(0x00); +} + +static void hx83102ab_set_HSEN_enable(uint8_t HSEN_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + + if (HSEN_enable) { + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + } else { + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + } + + /*I("%s: tmp_data[0]=%d, HSEN_enable=%d, retry_cnt=%d\n", __func__, tmp_data[0],HSEN_enable,retry_cnt);*/ +} + +static void hx83102ab_usb_detect_set(uint8_t *cable_config) +{ + uint8_t tmp_data[DATA_LEN_4]; + + if (cable_config[1] == 0x01) { + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + I("%s: USB detect status IN!\n", __func__); + } else { + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + I("%s: USB detect status OUT!\n", __func__); + } +} + +static uint8_t hx83102ab_read_DD_status(uint8_t *cmd_set, uint8_t *tmp_data) +{ + int cnt = 0; + uint8_t req_size = cmd_set[0]; + + cmd_set[3] = (*kp_pfw_op)->data_dd_request[0]; + + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_dd_handshak_addr, DATA_LEN_4, cmd_set, 0); + I("%s: cmd_set[0] = 0x%02X,cmd_set[1] = 0x%02X,cmd_set[2] = 0x%02X,cmd_set[3] = 0x%02X\n", + __func__, cmd_set[0], cmd_set[1], cmd_set[2], cmd_set[3]); + + /* Doing hand shaking 0xAA -> 0xBB */ + for (cnt = 0; cnt < 100; cnt++) { + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_dd_handshak_addr, DATA_LEN_4, tmp_data, false); + usleep_range(10000, 10001); + + if (tmp_data[3] == (*kp_pfw_op)->data_dd_ack[0]) { + I("%s Data ready goto moving data\n", __func__); + break; + } + if (cnt >= 99) { + I("%s Data not ready in FW\n", __func__); + return FW_NOT_READY; + } + } + + kp_g_core_fp->fp_sense_off(true); + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_dd_data_addr, req_size, tmp_data, false); + kp_g_core_fp->fp_sense_on(0x01); + return NO_ERR; +} + +static int hx83102ab_read_FW_status(uint8_t *state_addr, uint8_t *tmp_addr) +{ + uint8_t i; + uint8_t req_size = 0; + uint8_t status_addr[DATA_LEN_4]; /*0x10007F44*/ + uint8_t cmd_addr[DATA_LEN_4]; /*0x900000F8*/ + + if (state_addr[0] == 0x01) { + state_addr[1] = 0x04; + + for (i = 0; i < DATA_LEN_4; i++) { + /* 0x10007F44 */ + state_addr[i + 2] = (*kp_pfw_op)->addr_fw_dbg_msg_addr[i]; + status_addr[i] = (*kp_pfw_op)->addr_fw_dbg_msg_addr[i]; + } + + req_size = 0x04; + kp_g_core_fp->fp_sense_off(true); + kp_g_core_fp->fp_register_read(status_addr, req_size, tmp_addr, false); + kp_g_core_fp->fp_sense_on(0x01); + } else if (state_addr[0] == 0x02) { + state_addr[1] = 0x30; + + for (i = 0; i < DATA_LEN_4; i++) { + /* 0x900000F8 */ + state_addr[i + 2] = (*kp_pfw_op)->addr_fw_state_addr[i]; + cmd_addr[i] = (*kp_pfw_op)->addr_fw_state_addr[i]; + } + + req_size = 0x30; + kp_g_core_fp->fp_register_read(cmd_addr, req_size, tmp_addr, false); + } + + return NO_ERR; +} + +static void hx83102ab_power_on_init(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + + I("%s:\n", __func__); + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_raw_out_sel, DATA_LEN_4, tmp_data, 0); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_sorting_mode_en, DATA_LEN_4, tmp_data, 0); + kp_g_core_fp->fp_touch_information(); + kp_g_core_fp->fp_sense_on(0x00); +} + +static int hx83102ab_fts_ctpm_fw_upgrade_with_sys_fs_64k(unsigned char *fw, int len, bool change_iref) +{ + int burnFW_success = 0; + uint8_t tmp_data[DATA_LEN_4]; + + if (len != FW_SIZE_64k) { + E("%s: The file size is not 64K bytes\n", __func__); + return false; + } + +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + kp_g_core_fp->fp_sense_off(true); + kp_g_core_fp->fp_chip_erase(); + kp_g_core_fp->fp_flash_programming(fw, FW_SIZE_64k); + + if (kp_g_core_fp->fp_check_CRC((*kp_pfw_op)->addr_program_reload_from, FW_SIZE_64k) == 0) + burnFW_success = 1; + + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_raw_out_sel, DATA_LEN_4, tmp_data, 0); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_sorting_mode_en, DATA_LEN_4, tmp_data, 0); +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + /*System reset*/ + kp_g_core_fp->fp_system_reset(); +#endif + return burnFW_success; +} + +#ifdef HX_ESD_RECOVERY +static void hx83102ab_esd_ic_reset(void) +{ + (*kp_HX_ESD_RESET_ACTIVATE) = 1; +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_pin_reset(); +#else + kp_g_core_fp->fp_system_reset(); +#endif + I("%s:\n", __func__); +} +#if defined(HX_ZERO_FLASH) +static int hx83102d_0f_esd_check(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + int ret = NO_ERR; + + I("Enter %s\n", __func__); + + kp_g_core_fp->fp_register_read((*kp_pzf_op)->addr_sts_chk, DATA_LEN_4, tmp_data, 0); + + if (tmp_data[0] != (*kp_pzf_op)->data_activ_sts[0]) { + ret = ERR_STS_WRONG; + I("%s:status : %8X = %2X\n", __func__, zf_addr_sts_chk, tmp_data[0]); + } + + kp_g_core_fp->fp_register_read((*kp_pzf_op)->addr_activ_relod, DATA_LEN_4, tmp_data, 0); + + if (tmp_data[0] != (*kp_pzf_op)->data_activ_in[0]) { + ret = ERR_STS_WRONG; + I("%s:status : %8X = %2X\n", __func__, zf_addr_activ_relod, tmp_data[0]); + } + + return ret; +} +#endif +#endif + +static bool hx83102d_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t tmp_data[DATA_LEN_4]; + + do { + if (cnt == 0 || (tmp_data[0] != 0xA5 && tmp_data[0] != 0x00 && tmp_data[0] != 0x87)) + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_ctrl_fw_isr, DATA_LEN_4, (*kp_pfw_op)->data_fw_stop, 0); + /*msleep(20);*/ + usleep_range(10000, 10001); + + /* check fw status */ + kp_g_core_fp->fp_register_read((*kp_pic_op)->addr_cs_central_state, ADDR_LEN_4, tmp_data, 0); + + if (tmp_data[0] != 0x05) { + I("%s: Do not need wait FW, Status = 0x%02X!\n", __func__, tmp_data[0]); + break; + } + + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_ctrl_fw_isr, 4, tmp_data, false); + I("%s: cnt = %d, data[0] = 0x%02X!\n", __func__, cnt, tmp_data[0]); + } while (tmp_data[0] != 0x87 && (++cnt < 10) && check_en == true); + + cnt = 0; + + do { + /*=========================================== + *I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + *=========================================== + */ + tmp_data[0] = 0x27; + if (kp_himax_bus_write(0x31, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + *I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + *=========================================== + */ + tmp_data[0] = 0x95; + if (kp_himax_bus_write(0x32, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + *Check enter_save_mode + *====================== + */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, ADDR_LEN_4, tmp_data); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /*===================================== + *Reset TCON + *===================================== + */ + tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + /*===================================== + *Reset ADC + *===================================== + */ + tmp_addr[3] = 0x80; + tmp_addr[2] = 0x02; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + + return true; + } + /*msleep(10);*/ + usleep_range(5000, 5001); +#ifdef HX_RST_PIN_FUNC + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 0); + msleep(20); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 1); + msleep(50); +#endif + + } while (cnt++ < 5); + + return false; +} + +static bool hx83102d_dd_clk_set(bool enable) +{ + uint8_t data[4] = {0}; + + data[0] = (enable)?0xDD:0; + return (kp_g_core_fp->fp_register_write((*kp_pic_op)->adr_osc_en, + sizeof((*kp_pic_op)->adr_osc_en), data, 0) == NO_ERR); +} + +static void hx83102d_dd_reg_en(bool enable) +{ + uint8_t data[4] = {0}; + + data[0] = 0xA5; data[1] = 0x00; + data[2] = 0x00; data[3] = 0x00; + kp_g_core_fp->fp_register_write((*kp_pic_op)->adr_osc_pw, DATA_LEN_4, data, 0); + + data[0] = 0xA5; data[1] = 0x83; + data[2] = 0x10; data[3] = 0x2D; + kp_g_core_fp->fp_dd_reg_write(0xB9, 0, 4, data, 0); +} + +static bool hx83102d_ic_id_read(void) +{ + int i = 0; + uint8_t data[4] = {0}; + + kp_g_core_fp->fp_dd_clk_set(true); + kp_g_core_fp->fp_dd_reg_en(true); + + for (i = 0; i < 13; i++) { + data[0] = 0x2A + i; + kp_g_core_fp->fp_dd_reg_write(0xBB, 3, 1, data, 0); + data[0] = 0x08; + kp_g_core_fp->fp_dd_reg_write(0xBB, 1, 1, data, 0); + kp_g_core_fp->fp_dd_reg_read(0xBB, 6, 1, data, 0); + (*kp_ic_data)->vendor_ic_id[i] = data[0]; + I("ic_data->vendor_ic_id[%d] = %02X\n", i, (*kp_ic_data)->vendor_ic_id[i]); + } + + kp_g_core_fp->fp_dd_clk_set(false); + + return true; +} + +#if defined(HX_ZERO_FLASH) +static void himax_hx83102d_reload_to_active(void) +{ + uint8_t addr[DATA_LEN_4] = {0}; + uint8_t data[DATA_LEN_4] = {0}; + uint8_t retry_cnt = 0; + + addr[3] = 0x90; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x48; + + do { + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0xEC; + kp_g_core_fp->fp_register_write(addr, DATA_LEN_4, data, 0); + usleep_range(1000, 1100); + kp_g_core_fp->fp_register_read(addr, DATA_LEN_4, data, 0); + I("%s: data[1]=%d, data[0]=%d, retry_cnt=%d\n", __func__, data[1], data[0], retry_cnt); + retry_cnt++; + } while ((data[1] != 0x01 || data[0] != 0xEC) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} + +static void himax_hx83102d_resume_ic_action(void) +{ +#ifndef HX_RESUME_HW_RESET + himax_hx83102d_reload_to_active(); +#endif +} + +static void himax_hx83102d_suspend_ic_action(void) +{ +#ifndef HX_RESUME_HW_RESET + himax_hx83102d_reload_to_active(); +#endif +} + +static void himax_hx83102d_sense_on(uint8_t FlashMode) +{ + uint8_t tmp_data[DATA_LEN_4]; + int retry = 0; + + I("Enter %s\n", __func__); + + kp_g_core_fp->fp_interface_on(); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_ctrl_fw_isr, + sizeof((*kp_pfw_op)->data_clear), (*kp_pfw_op)->data_clear, 0); + /*msleep(20);*/ + usleep_range(10000, 10001); + if (!FlashMode) { +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + himax_hx83102d_reload_to_active(); + } else { + himax_hx83102d_reload_to_active(); + do { + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_safe_mode_release_pw, + sizeof((*kp_pfw_op)->data_safe_mode_release_pw_active), (*kp_pfw_op)->data_safe_mode_release_pw_active, 0); + + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_flag_reset_event, DATA_LEN_4, tmp_data, 0); + I("%s:Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + } while ((tmp_data[1] != 0x01 || tmp_data[0] != 0x00) && retry++ < 5); + + if (retry >= 5) { + E("%s: Fail:\n", __func__); +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + himax_hx83102d_reload_to_active(); + } else { + I("%s:OK and Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + /* reset code*/ + tmp_data[0] = 0x00; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_safe_mode_release_pw, + sizeof((*kp_pfw_op)->data_safe_mode_release_pw_reset), (*kp_pfw_op)->data_safe_mode_release_pw_reset, 0); + } + } +} + +static void hx83102ab_firmware_update_0f(const struct firmware *fw_entry) +{ + uint8_t tmp_addr[4]; + + I("%s, Entering\n", __func__); + + kp_g_core_fp->fp_register_write((*kp_pzf_op)->addr_system_reset, 4, (*kp_pzf_op)->data_system_reset, 0); + + kp_g_core_fp->fp_sense_off(false); + + /* first 32K */ + /*bin file start*/ + /*isram*/ + kp_g_core_fp->fp_write_sram_0f(fw_entry, (*kp_pzf_op)->data_sram_start_addr, 0, HX_32K_SZ); + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x73; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_write_sram_0f(fw_entry, tmp_addr, 0x8000, (0x200*4)); + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x68; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_write_sram_0f(fw_entry, tmp_addr, 0x8800, (0x200*4)); + + /*last 16k*/ + /*config info*/ + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x72; + tmp_addr[0] = 0x04; /*0x10007206 <= size 60,from 0x0000C180*/ + kp_g_core_fp->fp_write_sram_0f(fw_entry, tmp_addr, 0xC178, (84)); + + /*FW config*/ + if ((*kp_G_POWERONOF) == 1) + kp_g_core_fp->fp_write_sram_0f(fw_entry, (*kp_pzf_op)->data_fw_cfg_1, 0xBFFE, 388); + else + kp_g_core_fp->fp_clean_sram_0f((*kp_pzf_op)->data_fw_cfg_1, 388, 1); + /*ADC config*/ + if ((*kp_G_POWERONOF) == 1) + kp_g_core_fp->fp_write_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_1, 0xe000, (128 * 4)); + else + kp_g_core_fp->fp_clean_sram_0f((*kp_pzf_op)->data_adc_cfg_1, (128 * 4), 2); + + I("%s, END\n", __func__); +} +#if defined(HX_0F_DEBUG) +static void hx83102ab_firmware_read_0f(const struct firmware *fw_entry, int type) +{ + uint8_t tmp_addr[4]; + + I("%s, Entering\n", __func__); + if (type == 0) { /* first 32K */ + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_sram_start_addr, 0, HX_32K_SZ); + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x68; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_addr, 0x8000, (0x200*4)); + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x73; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_addr, 0x8800, (0x200*4)); + + } else { /*last 16k*/ + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x72; + tmp_addr[0] = 0x04; /*0x10007206 <= size 80,from 0x0000C180*/ + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_addr, 0xC178, (84)); + + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_fw_cfg_1, 0xBFFE, 388); + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_1, 0xE000, (128*4)); + + } + I("%s, END\n", __func__); +} +static void hx83102d_firmware_read_0f(const struct firmware *fw_entry, int type) +{ + uint8_t tmp_data[4]; + + I("%s, Entering\n", __func__); + + switch (type) { + case 0: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_sram_start_addr, 0, HX_40K_SZ); + break; + case 1: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_cfg_info, 0xC000, 132); + break; + case 2: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_fw_cfg_1, 0xC0FE, 484); + break; + case 3: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_1, 0xD000, 768); + break; + case 4: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_2, 0xD300, 1536); + break; + case 5: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_3, 0xE000, 1536); + break; + case 6: + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_bor_prevent_info, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xC9E0, 32); + break; + case 7: + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_notch_info, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xCA00, 128); + break; + case 8: + kp_himax_in_parse_assign_cmd(hx83102d_zf_func_info_en, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xCB00, 12); + break; + case 9: + kp_himax_in_parse_assign_cmd(hx83102d_zf_po_sub_func, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xA000, HX4K); + break; + default: + break; + } + I("%s, END\n", __func__); +} +#endif +#if defined(HX_CODE_OVERLAY) +static int hx83102d_0f_overlay(int ovl_type, int mode) +{ + int ret = 0; + uint8_t count = 0; + uint8_t count2 = 0; + const struct firmware *fwp = NULL; + char fwname[32] = "Himax_firmware.bin"; + uint8_t part_num = 0; + uint8_t buf[16]; + uint8_t handshaking_addr[4] = {0xFC, 0x7F, 0x00, 0x10}; + /*uint8_t mpap_addr[4] = {0xEC, 0x73, 0x00, 0x10};*/ + uint8_t ovl_idx_t; + uint8_t request; + uint8_t reply; + uint8_t sram_addr[4] = {0}; + uint32_t offset = 0; + uint32_t size = 0; + uint8_t send_data[4] = {0}; + uint8_t recv_data[4] = {0}; + + ret = request_firmware(&fwp, fwname, (*kp_private_ts)->dev); + if (ret < 0) { + E("%s: request firmware FAIL!!!\n", __func__); + return ret; + } + + /*1. get number of partition*/ + part_num = fwp->data[HX64K + 12]; + if (part_num <= 1) { + E("%s, size of cfg part failed! part_num = %d\n", + __func__, part_num); + return LENGTH_FAIL; + } + + I("%s: overlay section %d\n", __func__, ovl_type-1); + if (ovl_type == 2) { + request = ovl_gesture_request; + reply = ovl_gesture_reply; + } else if (ovl_type == 3) { + request = ovl_border_request; + reply = ovl_border_reply; + } else { + E("%s: error overlay type %d\n", __func__, ovl_type); + return HX_INIT_FAIL; + } + ovl_idx_t = *((*kp_ovl_idx) + ovl_type - 1); + memcpy(buf, &fwp->data[ovl_idx_t * 0x10 + HX64K], 16); + memcpy(sram_addr, buf, 4); + offset = buf[11]<<24 | buf[10]<<16 | buf[9]<<8 | buf[8]; + size = buf[7]<<24 | buf[6]<<16 | buf[5]<<8 | buf[4]; + +/* + * if (ovl_type == 1) { + * if (mode == 0) { + * //border overlay when finish self_test + * send_data[3] = 0x00; + * send_data[2] = 0x00; + * send_data[1] = 0x00; + * send_data[0] = 0x00; + * offset = zf_info_arr[2].fw_addr; + * size = zf_info_arr[2].write_size; + * I("%s: self test overlay section 2\n", __func__); + * } else if (mode == 1) { + * //sorting overlay when call self_test + * send_data[3] = 0x00; + * send_data[2] = 0x10; + * send_data[1] = 0x73; + * send_data[0] = 0x80; + * offset = zf_info_arr[0].fw_addr; + * size = zf_info_arr[0].write_size; + * I("%s: self test overlay section 0\n", __func__); + * } + * } + */ + +/* + * if (ovl_type == 1) { + * + * g_core_fp.fp_sense_off(false); + * + * //g_core_fp.fp_register_write(mpap_addr, DATA_LEN_4, send_data, 0); + * count = 0; + * do { + * g_core_fp.fp_register_write(mpap_addr, DATA_LEN_4, send_data, 0); + * g_core_fp.fp_register_read(mpap_addr, DATA_LEN_4, recv_data, 0); + * } while ((recv_data[0] != send_data[0] || recv_data[1] != send_data[1] || recv_data[2] != send_data[2] || recv_data[3] != send_data[3]) && count++ < 10); + * + * I("%s: set mpap pw %d times\n", __func__, count); + * + * //g_core_fp.fp_write_sram_0f(fwp, sram_addr, offset, size); + * if (kp_g_core_fp->fp_write_sram_0f_crc(fwp, sram_addr, offset, size) != 0) + * E("%s, Overlay HW CRC FAIL\n", __func__); + * + * g_core_fp.fp_reload_disable(0); + * + * g_core_fp.fp_sense_on(0x00); + * + * } else { + */ + + count = 0; + do { + kp_g_core_fp->fp_register_read(handshaking_addr, DATA_LEN_4, recv_data, 0); + } while (recv_data[0] != request && count++ < 10); + + if (count < 10) { + /*g_core_fp.fp_write_sram_0f(fwp, sram_addr, offset, size);*/ + if (kp_g_core_fp->fp_write_sram_0f_crc(fwp, sram_addr, offset, size) != 0) + E("%s, Overlay HW CRC FAIL\n", __func__); + + send_data[3] = 0x00; + send_data[2] = 0x00; + send_data[1] = 0x00; + send_data[0] = reply; + + count2 = 0; + do { + kp_g_core_fp->fp_register_write(handshaking_addr, DATA_LEN_4, send_data, 0); + usleep_range(1000, 1100); + kp_g_core_fp->fp_register_read(handshaking_addr, DATA_LEN_4, recv_data, 0); + } while (recv_data[0] != reply && count2++ < 10); + + if (ovl_type == 3) { +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + } + } + + I("%s: overlay request %d times; reply %d times\n", __func__, count, count2); + + release_firmware(fwp); + + /* rescue mechanism */ + if (count >= 10) { + + kp_g_core_fp->fp_0f_op_file_dirly(fwname); + + kp_g_core_fp->fp_reload_disable(0); + + kp_g_core_fp->fp_sense_on(0x00); + + kp_himax_int_enable(1); + } + + return 0; +} +#endif +#endif + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) +static void hx83102a_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + (*kp_ic_data)->vendor_ic_ver = 0xFFFF; + /* Enable read DD */ + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x10; + tmp_data[3] = 0x2A; + kp_himax_in_parse_assign_cmd(hx83102_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83102_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; + + I("VENDOR_IC_VER : %X\n", ((*kp_ic_data)->vendor_ic_ver)); +} + +static void hx83102b_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + (*kp_ic_data)->vendor_ic_ver = 0xFFFF; + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83102b_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x01; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x10; + tmp_data[3] = 0x2B; + kp_himax_in_parse_assign_cmd(hx83102_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83102_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; + + /* Disable read DD */ + kp_himax_in_parse_assign_cmd(hx83102b_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x00; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + I("VENDOR_IC_VER : %X\n", ((*kp_ic_data)->vendor_ic_ver)); +} + +static void hx83102d_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + (*kp_ic_data)->vendor_ic_ver = 0xFFFF; + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83102d_ic_osc_pw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xA5; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + kp_himax_in_parse_assign_cmd(hx83102d_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xDD; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x10; + tmp_data[3] = 0x2D; + kp_himax_in_parse_assign_cmd(hx83102_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83102_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; + + /* Disable read DD */ + kp_himax_in_parse_assign_cmd(hx83102d_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x00; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + I("VENDOR_IC_VER : %X\n", ((*kp_ic_data)->vendor_ic_ver)); +} + +static void hx83102_dynamic_fw_name(uint8_t ic_name) +{ + char firmware_name[64]; + + if (ic_name == 0x2a) + hx83102a_read_ic_ver(); + else if (ic_name == 0x2b) + hx83102b_read_ic_ver(); + else + hx83102d_read_ic_ver(); + + if ((*kp_i_CTPM_firmware_name) != NULL) { + kfree((*kp_i_CTPM_firmware_name)); + (*kp_i_CTPM_firmware_name) = NULL; + } + memset(firmware_name, 0x00, sizeof(firmware_name)); + + if (((*kp_ic_data)->vendor_ic_ver == 0x00) || ((*kp_ic_data)->vendor_ic_ver == 0x21)) { /* Cut 1 */ + memcpy(firmware_name, "Himax_firmware_1.bin", sizeof(char)*strlen("Himax_firmware_1.bin")); + } else if (((*kp_ic_data)->vendor_ic_ver == 0x01) || ((*kp_ic_data)->vendor_ic_ver == 0x11)) { /* Cut 2 */ + memcpy(firmware_name, "Himax_firmware_2.bin", sizeof(char)*strlen("Himax_firmware_2.bin")); + } else { /* Original */ + memcpy(firmware_name, "Himax_firmware.bin", sizeof(char)*strlen("Himax_firmware.bin")); + } + + (*kp_i_CTPM_firmware_name) = kzalloc((sizeof(char)*(strlen(firmware_name)+1)), GFP_KERNEL); + if ((*kp_i_CTPM_firmware_name) != NULL) + memcpy((*kp_i_CTPM_firmware_name), firmware_name, (sizeof(char)*(strlen(firmware_name)+1))); + I("(*kp_i_CTPM_firmware_name) : %s\n", (*kp_i_CTPM_firmware_name)); +} +#endif +#endif + +static bool hx83102e_read_event_stack(uint8_t *buf, uint8_t length) +{ + struct timespec t_start, t_end, t_delta; + int len = length; + int i2c_speed = 0; + + if ((*kp_private_ts)->debug_log_level & BIT(2)) + getnstimeofday(&t_start); + + kp_himax_bus_read((*kp_pfw_op)->addr_event_addr[0], buf, length, HIMAX_I2C_RETRY_TIMES); + + if ((*kp_private_ts)->debug_log_level & BIT(2)) { + getnstimeofday(&t_end); + t_delta.tv_nsec = (t_end.tv_sec * 1000000000 + t_end.tv_nsec) - (t_start.tv_sec * 1000000000 + t_start.tv_nsec); /*ns*/ + i2c_speed = (len * 9 * 1000000 / (int)t_delta.tv_nsec) * 13 / 10; + (*kp_private_ts)->bus_speed = (int)i2c_speed; + } + return 1; +} + +static void himax_hx83102ab_reg_re_init(uint8_t ic_name) +{ + I("%s:Entering!\n", __func__); + kp_himax_in_parse_assign_cmd(hx83102ab_fw_addr_sorting_mode_en, (*kp_pfw_op)->addr_sorting_mode_en, sizeof((*kp_pfw_op)->addr_sorting_mode_en)); + kp_himax_in_parse_assign_cmd(hx83102ab_fw_addr_selftest_addr_en, (*kp_pfw_op)->addr_selftest_addr_en, sizeof((*kp_pfw_op)->addr_selftest_addr_en)); +#ifdef HX_ZERO_FLASH + kp_himax_in_parse_assign_cmd(hx83102ab_data_adc_cfg_1, (*kp_pzf_op)->data_adc_cfg_1, sizeof((*kp_pzf_op)->data_adc_cfg_1)); +#endif + + if (ic_name == 0x2a) { + kp_himax_in_parse_assign_cmd(hx83102a_data_df_rx, (*kp_pdriver_op)->data_df_rx, sizeof((*kp_pdriver_op)->data_df_rx)); + kp_himax_in_parse_assign_cmd(hx83102a_data_df_tx, (*kp_pdriver_op)->data_df_tx, sizeof((*kp_pdriver_op)->data_df_tx)); + kp_himax_in_parse_assign_cmd(hx83102a_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102a_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + } else { + kp_himax_in_parse_assign_cmd(hx83102b_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102b_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + } +} + +static void himax_hx83102ab_func_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_g_core_fp->fp_chip_init = hx83102_chip_init; + kp_g_core_fp->fp_sense_off = hx83102ab_sense_off; + kp_g_core_fp->fp_set_SMWP_enable = hx83102ab_set_SMWP_enable; + kp_g_core_fp->fp_set_HSEN_enable = hx83102ab_set_HSEN_enable; + kp_g_core_fp->fp_usb_detect_set = hx83102ab_usb_detect_set; + kp_g_core_fp->fp_read_DD_status = hx83102ab_read_DD_status; + kp_g_core_fp->fp_read_FW_status = hx83102ab_read_FW_status; + kp_g_core_fp->fp_power_on_init = hx83102ab_power_on_init; + kp_g_core_fp->fp_fts_ctpm_fw_upgrade_with_sys_fs_64k = hx83102ab_fts_ctpm_fw_upgrade_with_sys_fs_64k; +#ifdef HX_ESD_RECOVERY + kp_g_core_fp->fp_esd_ic_reset = hx83102ab_esd_ic_reset; +#endif +#if defined(HX_ZERO_FLASH) + kp_g_core_fp->fp_firmware_update_0f = hx83102ab_firmware_update_0f; +#if defined(HX_0F_DEBUG) + kp_g_core_fp->fp_firmware_read_0f = hx83102ab_firmware_read_0f; +#endif +#endif + +} + +static void himax_hx83102d_reg_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_himax_in_parse_assign_cmd(hx83102d_fw_addr_raw_out_sel, (*kp_pfw_op)->addr_raw_out_sel, sizeof((*kp_pfw_op)->addr_raw_out_sel)); +#if defined(HX_ZERO_FLASH) + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_sram_start_addr, (*kp_pzf_op)->data_sram_start_addr, sizeof((*kp_pzf_op)->data_sram_start_addr)); + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_adc_cfg_1, (*kp_pzf_op)->data_adc_cfg_1, sizeof((*kp_pzf_op)->data_adc_cfg_1)); + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_adc_cfg_2, (*kp_pzf_op)->data_adc_cfg_2, sizeof((*kp_pzf_op)->data_adc_cfg_2)); + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_adc_cfg_3, (*kp_pzf_op)->data_adc_cfg_3, sizeof((*kp_pzf_op)->data_adc_cfg_3)); +#endif + kp_himax_in_parse_assign_cmd(hx83102d_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102d_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + kp_himax_in_parse_assign_cmd(hx83102d_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + kp_himax_in_parse_assign_cmd(hx83102d_adr_osc_en, (*kp_pic_op)->adr_osc_en, sizeof((*kp_pic_op)->adr_osc_en)); + kp_himax_in_parse_assign_cmd(hx83102d_adr_osc_pw, (*kp_pic_op)->adr_osc_pw, sizeof((*kp_pic_op)->adr_osc_pw)); +} + +static void himax_hx83102d_func_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_g_core_fp->fp_chip_init = hx83102_chip_init; + kp_g_core_fp->fp_sense_off = hx83102d_sense_off; + kp_g_core_fp->fp_ic_id_read = hx83102d_ic_id_read; + kp_g_core_fp->fp_dd_clk_set = hx83102d_dd_clk_set; + kp_g_core_fp->fp_dd_reg_en = hx83102d_dd_reg_en; +#if defined(HX_ZERO_FLASH) + /*kp_g_core_fp->fp_firmware_update_0f = hx83102d_firmware_update_0f;*/ + kp_g_core_fp->fp_resume_ic_action = himax_hx83102d_resume_ic_action; + kp_g_core_fp->fp_suspend_ic_action = himax_hx83102d_suspend_ic_action; + kp_g_core_fp->fp_sense_on = himax_hx83102d_sense_on; + kp_g_core_fp->fp_0f_reload_to_active = himax_hx83102d_reload_to_active; +#if defined(HX_0F_DEBUG) + kp_g_core_fp->fp_firmware_read_0f = hx83102d_firmware_read_0f; +#endif +#if defined(HX_CODE_OVERLAY) + kp_g_core_fp->fp_0f_overlay = hx83102d_0f_overlay; +#endif +#ifdef HX_ESD_RECOVERY + kp_g_core_fp->fp_0f_esd_check = hx83102d_0f_esd_check; +#endif +#endif +} + +static void himax_hx83102e_reg_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_himax_in_parse_assign_cmd(hx83102e_fw_addr_raw_out_sel, (*kp_pfw_op)->addr_raw_out_sel, sizeof((*kp_pfw_op)->addr_raw_out_sel)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_rx, (*kp_pdriver_op)->data_df_rx, sizeof((*kp_pdriver_op)->data_df_rx)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_tx, (*kp_pdriver_op)->data_df_tx, sizeof((*kp_pdriver_op)->data_df_tx)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); +} + +static void himax_hx83102e_func_re_init(void) +{ + I("%s:Entering!\n", __func__); + + kp_g_core_fp->fp_chip_init = hx83102e_chip_init; + kp_g_core_fp->fp_sense_off = hx83102d_sense_off; + kp_g_core_fp->fp_read_event_stack = hx83102e_read_event_stack; +} + + +static bool hx83102_chip_detect(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + bool ret_data = false; + int ret = 0; + int i = 0; + + if (himax_ic_setup_external_symbols()) + return false; + +#ifdef HX_RST_PIN_FUNC + hx83102_pin_reset(); +#endif + if (kp_himax_bus_read(0x13, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + if (hx83102_sense_off(false) == false) { + ret_data = false; + E("%s:hx83102_sense_off Fail:\n", __func__); + return ret_data; + } + + for (i = 0; i < 5; i++) { + tmp_addr[3] = 0x90; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0xD0; + if (hx83102_register_read(tmp_addr, DATA_LEN_4, tmp_data) != 0) { + ret_data = false; + E("%s:hx83102_register_read Fail:\n", __func__); + return ret_data; + } + I("%s:Read driver IC ID = %X,%X,%X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1]); /*83,10,2X*/ + + if ((tmp_data[3] == 0x83) && (tmp_data[2] == 0x10) && ((tmp_data[1] == 0x2a) || (tmp_data[1] == 0x2b) || (tmp_data[1] == 0x2d) || (tmp_data[1] == 0x2e))) { + if (tmp_data[1] == 0x2a) { + strlcpy((*kp_private_ts)->chip_name, HX_83102A_SERIES_PWON, 30); + I("%s:detect IC HX83102A successfully\n", __func__); + } else if (tmp_data[1] == 0x2b) { + strlcpy((*kp_private_ts)->chip_name, HX_83102B_SERIES_PWON, 30); + I("%s:detect IC HX83102B successfully\n", __func__); + } else if (tmp_data[1] == 0x2d) { + strlcpy((*kp_private_ts)->chip_name, HX_83102D_SERIES_PWON, 30); + I("%s:detect IC HX83102D successfully\n", __func__); + } else { + strlcpy((*kp_private_ts)->chip_name, HX_83102E_SERIES_PWON, 30); + I("%s:detect IC HX83102E successfully\n", __func__); + } + + ret = kp_himax_mcu_in_cmd_struct_init(); + if (ret < 0) { + ret_data = false; + E("%s:cmd_struct_init Fail:\n", __func__); + return ret_data; + } + + kp_himax_mcu_in_cmd_init(); + if ((tmp_data[1] == 0x2a) || (tmp_data[1] == 0x2b)) { + himax_hx83102ab_reg_re_init(tmp_data[1]); + himax_hx83102ab_func_re_init(); + } else if (tmp_data[1] == 0x2d) { + himax_hx83102d_reg_re_init(); + himax_hx83102d_func_re_init(); + } else {/* 0x2e */ + himax_hx83102e_reg_re_init(); + himax_hx83102e_func_re_init(); + } + ret_data = true; + break; + } + + ret_data = false; + E("%s:Read driver ID register Fail:\n", __func__); + E("Could NOT find Himax Chipset\n"); + E("Please check 1.VCCD,VCCA,VSP,VSN\n"); + E("2. LCM_RST,TP_RST\n"); + E("3. Power On Sequence\n"); + + } + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) + hx83102_dynamic_fw_name(tmp_data[1]); +#endif +#endif + + return ret_data; +} + +DECLARE(HX_MOD_KSYM_HX83102); + +static int himax_hx83102_probe(void) +{ + I("%s:Enter\n", __func__); + himax_add_chip_dt(hx83102_chip_detect); + + return 0; +} + +static int himax_hx83102_remove(void) +{ + free_chip_dt_table(); + return 0; +} + +static int __init himax_hx83102_init(void) +{ + int ret = 0; + + I("%s\n", __func__); + ret = himax_hx83102_probe(); + return 0; +} + +static void __exit himax_hx83102_exit(void) +{ + himax_hx83102_remove(); +} + +module_init(himax_hx83102_init); +module_exit(himax_hx83102_exit); + +MODULE_DESCRIPTION("HIMAX HX83102 touch driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102d/himax_ic_HX83102d.h b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102d/himax_ic_HX83102d.h new file mode 100644 index 000000000000..ceab657ecdd0 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83102d/himax_ic_HX83102d.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for HX83102 chipset + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_platform.h" +#include "himax_common.h" +#include "himax_ic_core.h" +#include + +#define hx83102ab_fw_addr_sorting_mode_en 0x100007FC +#define hx83102ab_fw_addr_selftest_addr_en 0x100007F8 +#define hx83102ab_data_adc_cfg_1 0x10007B00 +#define hx83102a_data_df_rx 48 +#define hx83102a_data_df_tx 24 +#define hx83102a_data_df_x_res 2160 +#define hx83102a_data_df_y_res 3840 +#define hx83102b_data_df_x_res 720 +#define hx83102b_data_df_y_res 1280 + +#define hx83102d_fw_addr_raw_out_sel 0x800204f4 +#define hx83102d_zf_data_adc_cfg_1 0x10007B00 +#define hx83102d_zf_data_adc_cfg_2 0x10006A00 +#define hx83102d_zf_data_adc_cfg_3 0x10007500 +#define hx83102d_zf_data_bor_prevent_info 0x10007268 +#define hx83102d_zf_data_notch_info 0x10007300 +#define hx83102d_zf_func_info_en 0x10007FD0 +#define hx83102d_zf_po_sub_func 0x10005A00 +#define hx83102d_zf_data_sram_start_addr 0x20000000 +#define hx83102d_data_df_x_res 720 +#define hx83102d_data_df_y_res 1280 +#define hx83102d_adr_osc_en 0x9000009C +#define hx83102d_adr_osc_pw 0x90000280 + +#define hx83102e_fw_addr_raw_out_sel 0x100072EC +#define hx83102e_data_df_rx 48 +#define hx83102e_data_df_tx 30 +#define hx83102e_data_df_x_res 1200 +#define hx83102e_data_df_y_res 1920 + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#ifdef HX_EN_DYNAMIC_NAME +#define hx83102_ic_b9_en 0x300B9000 +#define hx83102_ic_fw 0x300E8006 + +#define hx83102b_ic_osc_en 0x900880A0 +#define hx83102d_ic_osc_en 0x9000009C +#define hx83102d_ic_osc_pw 0x90000280 +#endif +#endif + +#ifdef HX_ESD_RECOVERY + extern u8 HX_ESD_RESET_ACTIVATE; +#endif + diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112/Android.mk b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112/Android.mk new file mode 100644 index 000000000000..819a98e475e4 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112/Android.mk @@ -0,0 +1,23 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +HIMAX_MMI_IC_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +HIMAX_MMI_IC_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +ifeq ($(HIMAX_V2_MMI_HX83112),true) +KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_0FLASH_MMI_IC_NAME=hx83112 +endif + +ifneq ($(BOARD_USES_TOUCH_PALM),) + KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_HIMAX_0FLASH_MMI_ENABLE_PALM=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := himax_0flash_mmi_hx83112.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(HIMAX_MMI_IC_MODULE_PATH) +LOCAL_REQUIRED_MODULES := himax_0flash_mmi.ko +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112/Kbuild b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112/Kbuild new file mode 100644 index 000000000000..a199508dde20 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112/Kbuild @@ -0,0 +1,22 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_0flash_mmi \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_0flash_mmi/support_ic \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112 +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_DEBUG \ + -DCONFIG_TOUCHSCREEN_HIMAX_INSPECT + +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_INCELL +EXTRA_CFLAGS += -DCONFIG_HMX_DB + +ifneq ($(filter hx83112,$(CONFIG_INPUT_HIMAX_0FLASH_MMI_IC_NAME)),) + EXTRA_CFLAGS += -D__HIMAX_HX83112_MOD__ +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_HIMAX_0FLASH_MMI_ENABLE_PALM)),) + EXTRA_CFLAGS += -DHIMAX_PALM_SENSOR_EN +endif + +obj-m := himax_0flash_mmi_hx83112.o +himax_0flash_mmi_hx83112-objs += himax_ic_HX83112.o diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112/Makefile b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112/Makefile new file mode 100755 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112/himax_ic_HX83112.c b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112/himax_ic_HX83112.c new file mode 100644 index 000000000000..e60056006329 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112/himax_ic_HX83112.c @@ -0,0 +1,427 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for HX83112 chipset + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_ic_HX83112.h" +#include "himax_modular.h" + +static void hx83112_chip_init(void) +{ + (*kp_private_ts)->chip_cell_type = CHIP_IS_IN_CELL; + I("%s:IC cell type = %d\n", __func__, (*kp_private_ts)->chip_cell_type); + (*kp_IC_CHECKSUM) = HX_TP_BIN_CHECKSUM_CRC; + /*Himax: Set FW and CFG Flash Address*/ + (*kp_FW_VER_MAJ_FLASH_ADDR) = 49157; /*0x00C005*/ + (*kp_FW_VER_MAJ_FLASH_LENG) = 1; + (*kp_FW_VER_MIN_FLASH_ADDR) = 49158; /*0x00C006*/ + (*kp_FW_VER_MIN_FLASH_LENG) = 1; + (*kp_CFG_VER_MAJ_FLASH_ADDR) = 49408; /*0x00C100*/ + (*kp_CFG_VER_MAJ_FLASH_LENG) = 1; + (*kp_CFG_VER_MIN_FLASH_ADDR) = 49409; /*0x00C101*/ + (*kp_CFG_VER_MIN_FLASH_LENG) = 1; + (*kp_CID_VER_MAJ_FLASH_ADDR) = 49154; /*0x00C002*/ + (*kp_CID_VER_MAJ_FLASH_LENG) = 1; + (*kp_CID_VER_MIN_FLASH_ADDR) = 49155; /*0x00C003*/ + (*kp_CID_VER_MIN_FLASH_LENG) = 1; +} + +static bool hx83112_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_data[DATA_LEN_4]; + + do { + if (cnt == 0 || (tmp_data[0] != 0xA5 && tmp_data[0] != 0x00 && tmp_data[0] != 0x87)) + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_ctrl_fw_isr, DATA_LEN_4, (*kp_pfw_op)->data_fw_stop, 0); + + /*msleep(20);*/ + usleep_range(10000, 10001); + /* check fw status */ + kp_g_core_fp->fp_register_read((*kp_pic_op)->addr_cs_central_state, ADDR_LEN_4, tmp_data, 0); + + if (tmp_data[0] != 0x05) { + I("%s: Do not need wait FW, Status = 0x%02X!\n", __func__, tmp_data[0]); + break; + } + + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_ctrl_fw_isr, 4, tmp_data, false); + I("%s: cnt = %d, data[0] = 0x%02X!\n", __func__, cnt, tmp_data[0]); + } while (tmp_data[0] != 0x87 && (++cnt < 10) && check_en == true); + + cnt = 0; + + do { + /* =========================================== + * I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + * =========================================== + */ + tmp_data[0] = (*kp_pic_op)->data_i2c_psw_lb[0]; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* =========================================== + * I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + * =========================================== + */ + tmp_data[0] = (*kp_pic_op)->data_i2c_psw_ub[0]; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* =========================================== + * I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x00 + * =========================================== + */ + tmp_data[0] = 0x00; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* =========================================== + * I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + * =========================================== + */ + tmp_data[0] = (*kp_pic_op)->data_i2c_psw_lb[0]; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* =========================================== + * I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + * =========================================== + */ + tmp_data[0] = (*kp_pic_op)->data_i2c_psw_ub[0]; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + * Check enter_save_mode + * ====================== + */ + kp_g_core_fp->fp_register_read((*kp_pic_op)->addr_cs_central_state, ADDR_LEN_4, tmp_data, 0); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /* ===================================== + * Reset TCON + * ===================================== + */ + kp_g_core_fp->fp_register_write((*kp_pic_op)->addr_tcon_on_rst, DATA_LEN_4, (*kp_pic_op)->data_rst, 0); + usleep_range(1000, 1001); + tmp_data[3] = (*kp_pic_op)->data_rst[3]; + tmp_data[2] = (*kp_pic_op)->data_rst[2]; + tmp_data[1] = (*kp_pic_op)->data_rst[1]; + tmp_data[0] = (*kp_pic_op)->data_rst[0] | 0x01; + kp_g_core_fp->fp_register_write((*kp_pic_op)->addr_tcon_on_rst, DATA_LEN_4, tmp_data, 0); + /* ===================================== + * Reset ADC + * ===================================== + */ + kp_g_core_fp->fp_register_write((*kp_pic_op)->addr_adc_on_rst, DATA_LEN_4, (*kp_pic_op)->data_rst, 0); + usleep_range(1000, 1001); + tmp_data[3] = (*kp_pic_op)->data_rst[3]; + tmp_data[2] = (*kp_pic_op)->data_rst[2]; + tmp_data[1] = (*kp_pic_op)->data_rst[1]; + tmp_data[0] = (*kp_pic_op)->data_rst[0] | 0x01; + kp_g_core_fp->fp_register_write((*kp_pic_op)->addr_adc_on_rst, DATA_LEN_4, tmp_data, 0); + goto SUCCEED; + } else { + /*msleep(10);*/ +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + } + } while (cnt++ < 5); + + return false; +SUCCEED: + return true; +} + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) +static void hx83112a_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83112_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x01; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + kp_himax_in_parse_assign_cmd(hx83112_ic_osc_pw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xA5; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x11; + tmp_data[3] = 0x2A; + kp_himax_in_parse_assign_cmd(hx83112_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x55; + tmp_data[2] = 0xAA; + tmp_data[3] = 0x00; + kp_himax_in_parse_assign_cmd(hx83112_ic_eb_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83112_cb_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("CB=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_old_ic_ver = tmp_data[0]; + + kp_himax_in_parse_assign_cmd(hx83112_e8_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; + + /* Disable read DD */ + kp_himax_in_parse_assign_cmd(hx83112_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x00; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); +} + +static void hx83112b_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83112_ic_osc_pw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xA5; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x11; + tmp_data[3] = 0x2B; + kp_himax_in_parse_assign_cmd(hx83112_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83112_cb_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("CB=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_old_ic_ver = tmp_data[0]; + + kp_himax_in_parse_assign_cmd(hx83112_e8_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; +} + +static void hx83112e_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83112_ic_osc_pw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xA5; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x11; + tmp_data[3] = 0x2E; + kp_himax_in_parse_assign_cmd(hx83112_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83112_cb_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("CB=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_old_ic_ver = tmp_data[0]; + + kp_himax_in_parse_assign_cmd(hx83112_e8_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; +} + +static void hx83112_dynamic_fw_name(uint8_t ic_name) +{ + char firmware_name[64]; + + if (ic_name == 0x2a) + hx83112a_read_ic_ver(); + else if (ic_name == 0x2b) + hx83112b_read_ic_ver(); + else + hx83112e_read_ic_ver(); + + if ((*kp_i_CTPM_firmware_name) != NULL) { + kfree((*kp_i_CTPM_firmware_name)); + (*kp_i_CTPM_firmware_name) = NULL; + } + memset(firmware_name, 0x00, sizeof(firmware_name)); + + if (((*kp_ic_data)->vendor_ic_ver == 0x13) || ((*kp_ic_data)->vendor_ic_ver == 0x14) || ((*kp_ic_data)->vendor_ic_ver == 0x15) + || ((*kp_ic_data)->vendor_ic_ver == 0x16) || ((*kp_ic_data)->vendor_ic_ver == 0x23)) { + (*kp_ic_data)->vendor_semifac = 2; + } else if (((*kp_ic_data)->vendor_old_ic_ver == 0x44) || ((*kp_ic_data)->vendor_old_ic_ver == 0x77) + || ((*kp_ic_data)->vendor_ic_ver == 0x03) || ((*kp_ic_data)->vendor_ic_ver == 0x04)) { + (*kp_ic_data)->vendor_semifac = 1; + } else { + (*kp_ic_data)->vendor_semifac = 0; + } + memcpy(firmware_name, "Himax_firmware.bin", sizeof(char)*strlen("Himax_firmware.bin")); + + (*kp_i_CTPM_firmware_name) = kzalloc((sizeof(char)*(strlen(firmware_name)+1)), GFP_KERNEL); + if ((*kp_i_CTPM_firmware_name) != NULL) + memcpy((*kp_i_CTPM_firmware_name), firmware_name, (sizeof(char)*(strlen(firmware_name)+1))); + + I("(*kp_i_CTPM_firmware_name) : %s\n", (*kp_i_CTPM_firmware_name)); +} +#endif +#endif + +static void hx83112_func_re_init(void) +{ + kp_g_core_fp->fp_sense_off = hx83112_sense_off; + kp_g_core_fp->fp_chip_init = hx83112_chip_init; +} + +static void hx83112_reg_re_init(void) +{ +} + +static bool hx83112_chip_detect(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + bool ret_data = false; + int ret = 0; + int i = 0; + + if (himax_ic_setup_external_symbols()) + return false; + + ret = kp_himax_mcu_in_cmd_struct_init(); + if (ret < 0) { + ret_data = false; + E("%s:cmd_struct_init Fail:\n", __func__); + return ret_data; + } + + kp_himax_mcu_in_cmd_init(); + + hx83112_reg_re_init(); + hx83112_func_re_init(); + if (kp_himax_bus_read((*kp_pic_op)->addr_conti[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + if (kp_g_core_fp->fp_sense_off(false) == false) { + ret_data = false; + E("%s:fp_sense_off Fail:\n", __func__); + return ret_data; + } + for (i = 0; i < 5; i++) { + if (kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_icid_addr, DATA_LEN_4, tmp_data, false) != 0) { + ret_data = false; + E("%s:fp_register_read Fail:\n", __func__); + return ret_data; + } + I("%s:Read driver IC ID = %X, %X, %X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1]); + + if ((tmp_data[3] == 0x83) && (tmp_data[2] == 0x11) && ((tmp_data[1] == 0x2a) || (tmp_data[1] == 0x2b) || (tmp_data[1] == 0x2e))) { + if (tmp_data[1] == 0x2a) + strlcpy((*kp_private_ts)->chip_name, HX_83112A_SERIES_PWON, 30); + else if (tmp_data[1] == 0x2b) + strlcpy((*kp_private_ts)->chip_name, HX_83112B_SERIES_PWON, 30); + + I("%s:IC name = %s\n", __func__, (*kp_private_ts)->chip_name); + + I("Himax IC package %x%x%x in\n", tmp_data[3], tmp_data[2], tmp_data[1]); + ret_data = true; + goto FINAL; + } else { + ret_data = false; + E("%s:Read driver ID register Fail:\n", __func__); + E("Could NOT find Himax Chipset\n"); + E("Please check 1.VCCD,VCCA,VSP,VSN\n"); + E("2. LCM_RST,TP_RST\n"); + E("3. Power On Sequence\n"); + } + } +FINAL: +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) + hx83112_dynamic_fw_name(tmp_data[1]); +#endif +#endif + + return ret_data; +} + +DECLARE(HX_MOD_KSYM_HX83112); + +static int himax_hx83112_probe(void) +{ + I("%s:Enter\n", __func__); + himax_add_chip_dt(hx83112_chip_detect); + return 0; +} + +static int himax_hx83112_remove(void) +{ + free_chip_dt_table(); + return 0; +} + +static int __init himax_hx83112_init(void) +{ + int ret = 0; + + I("%s\n", __func__); + ret = himax_hx83112_probe(); + return 0; +} + +static void __exit himax_hx83112_exit(void) +{ + himax_hx83112_remove(); +} + +module_init(himax_hx83112_init); +module_exit(himax_hx83112_exit); + +MODULE_DESCRIPTION("HIMAX HX83112 touch driver"); +MODULE_LICENSE("GPL"); + + diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112/himax_ic_HX83112.h b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112/himax_ic_HX83112.h new file mode 100644 index 000000000000..b8c1b83fa81f --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112/himax_ic_HX83112.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for HX83112 chipset + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_platform.h" +#include "himax_common.h" +#include "himax_ic_core.h" +#include + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#ifdef HX_EN_DYNAMIC_NAME +#define hx83112_ic_b9_en 0x300B9000 +#define hx83112_ic_eb_en 0x300EB000 +#define hx83112_ic_osc_en 0x900880A8 +#define hx83112_ic_osc_pw 0x900880E0 +#define hx83112_e8_ic_fw 0x300E8006 +#define hx83112_cb_ic_fw 0x300CB008 +#endif +#endif diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112f/Android.mk b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112f/Android.mk new file mode 100644 index 000000000000..de6eae1b98d5 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112f/Android.mk @@ -0,0 +1,23 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +HIMAX_MMI_IC_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +HIMAX_MMI_IC_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +ifeq ($(HIMAX_V2_MMI_HX83112F),true) +KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_0FLASH_MMI_IC_NAME=hx83112f +endif + +ifneq ($(BOARD_USES_TOUCH_PALM),) + KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_HIMAX_0FLASH_MMI_ENABLE_PALM=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := himax_0flash_mmi_hx83112f.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(HIMAX_MMI_IC_MODULE_PATH) +LOCAL_REQUIRED_MODULES := himax_0flash_mmi.ko +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112f/Kbuild b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112f/Kbuild new file mode 100644 index 000000000000..b88f7fdf75f0 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112f/Kbuild @@ -0,0 +1,24 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_0flash_mmi \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_0flash_mmi/support_ic \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112f +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_DEBUG \ + -DCONFIG_TOUCHSCREEN_HIMAX_INSPECT + +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_INCELL +EXTRA_CFLAGS += -DCONFIG_HMX_DB + +ifneq ($(filter hx83112f,$(CONFIG_INPUT_HIMAX_0FLASH_MMI_IC_NAME)),) + EXTRA_CFLAGS += -D__HIMAX_HX83112_MOD__ + EXTRA_CFLAGS += -DHX_112F_SET + EXTRA_CFLAGS += -DHX_ZERO_FLASH +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_HIMAX_0FLASH_MMI_ENABLE_PALM)),) + EXTRA_CFLAGS += -DHIMAX_PALM_SENSOR_EN +endif + +obj-m := himax_0flash_mmi_hx83112f.o +himax_0flash_mmi_hx83112f-objs += himax_ic_HX83112f.o diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112f/Makefile b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112f/Makefile new file mode 100755 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112f/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112f/himax_ic_HX83112f.c b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112f/himax_ic_HX83112f.c new file mode 100644 index 000000000000..03e17bd00cc4 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112f/himax_ic_HX83112f.c @@ -0,0 +1,610 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for HX83112 chipset + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_ic_HX83112f.h" +#include "himax_modular.h" + +#define HIMAX_XFER_RETRY_TIMES 10 + +static void hx83112_chip_init(void) +{ + (*kp_private_ts)->chip_cell_type = CHIP_IS_IN_CELL; + I("%s:IC cell type = %d\n", __func__, (*kp_private_ts)->chip_cell_type); + (*kp_IC_CHECKSUM) = HX_TP_BIN_CHECKSUM_CRC; + /*Himax: Set FW and CFG Flash Address*/ + (*kp_FW_VER_MAJ_FLASH_ADDR) = 49157; /*0x00C005*/ + (*kp_FW_VER_MAJ_FLASH_LENG) = 1; + (*kp_FW_VER_MIN_FLASH_ADDR) = 49158; /*0x00C006*/ + (*kp_FW_VER_MIN_FLASH_LENG) = 1; + (*kp_CFG_VER_MAJ_FLASH_ADDR) = 49408; /*0x00C100*/ + (*kp_CFG_VER_MAJ_FLASH_LENG) = 1; + (*kp_CFG_VER_MIN_FLASH_ADDR) = 49409; /*0x00C101*/ + (*kp_CFG_VER_MIN_FLASH_LENG) = 1; + (*kp_CID_VER_MAJ_FLASH_ADDR) = 49154; /*0x00C002*/ + (*kp_CID_VER_MAJ_FLASH_LENG) = 1; + (*kp_CID_VER_MIN_FLASH_ADDR) = 49155; /*0x00C003*/ + (*kp_CID_VER_MIN_FLASH_LENG) = 1; +} + +static bool hx83112_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_data[DATA_LEN_4]; + + do { + if (cnt == 0 || (tmp_data[0] != 0xA5 && tmp_data[0] != 0x00 && tmp_data[0] != 0x87)) + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_ctrl_fw_isr, DATA_LEN_4, (*kp_pfw_op)->data_fw_stop, 0); + + /*msleep(20);*/ + usleep_range(10000, 10001); + /* check fw status */ + kp_g_core_fp->fp_register_read((*kp_pic_op)->addr_cs_central_state, ADDR_LEN_4, tmp_data, 0); + + if (tmp_data[0] != 0x05) { + I("%s: Do not need wait FW, Status = 0x%02X!\n", __func__, tmp_data[0]); + break; + } + + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_ctrl_fw_isr, 4, tmp_data, false); + I("%s: cnt = %d, data[0] = 0x%02X!\n", __func__, cnt, tmp_data[0]); + } while (tmp_data[0] != 0x87 && (++cnt < 10) && check_en == true); + + cnt = 0; + + do { + /* =========================================== + * I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + * =========================================== + */ + tmp_data[0] = (*kp_pic_op)->data_i2c_psw_lb[0]; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* =========================================== + * I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + * =========================================== + */ + tmp_data[0] = (*kp_pic_op)->data_i2c_psw_ub[0]; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* =========================================== + * I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x00 + * =========================================== + */ + tmp_data[0] = 0x00; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* =========================================== + * I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + * =========================================== + */ + tmp_data[0] = (*kp_pic_op)->data_i2c_psw_lb[0]; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* =========================================== + * I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + * =========================================== + */ + tmp_data[0] = (*kp_pic_op)->data_i2c_psw_ub[0]; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + * Check enter_save_mode + * ====================== + */ + kp_g_core_fp->fp_register_read((*kp_pic_op)->addr_cs_central_state, ADDR_LEN_4, tmp_data, 0); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /* ===================================== + * Reset TCON + * ===================================== + */ + kp_g_core_fp->fp_register_write((*kp_pic_op)->addr_tcon_on_rst, DATA_LEN_4, (*kp_pic_op)->data_rst, 0); + usleep_range(1000, 1001); + tmp_data[3] = (*kp_pic_op)->data_rst[3]; + tmp_data[2] = (*kp_pic_op)->data_rst[2]; + tmp_data[1] = (*kp_pic_op)->data_rst[1]; + tmp_data[0] = (*kp_pic_op)->data_rst[0] | 0x01; + kp_g_core_fp->fp_register_write((*kp_pic_op)->addr_tcon_on_rst, DATA_LEN_4, tmp_data, 0); + /* ===================================== + * Reset ADC + * ===================================== + */ + kp_g_core_fp->fp_register_write((*kp_pic_op)->addr_adc_on_rst, DATA_LEN_4, (*kp_pic_op)->data_rst, 0); + usleep_range(1000, 1001); + tmp_data[3] = (*kp_pic_op)->data_rst[3]; + tmp_data[2] = (*kp_pic_op)->data_rst[2]; + tmp_data[1] = (*kp_pic_op)->data_rst[1]; + tmp_data[0] = (*kp_pic_op)->data_rst[0] | 0x01; + kp_g_core_fp->fp_register_write((*kp_pic_op)->addr_adc_on_rst, DATA_LEN_4, tmp_data, 0); + goto SUCCEED; + } else { + /*msleep(10);*/ +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + } + } while (cnt++ < 5); + + return false; +SUCCEED: + return true; +} + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) +static void hx83112a_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83112_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x01; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + kp_himax_in_parse_assign_cmd(hx83112_ic_osc_pw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xA5; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x11; + tmp_data[3] = 0x2A; + kp_himax_in_parse_assign_cmd(hx83112_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x55; + tmp_data[2] = 0xAA; + tmp_data[3] = 0x00; + kp_himax_in_parse_assign_cmd(hx83112_ic_eb_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83112_cb_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("CB=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_old_ic_ver = tmp_data[0]; + + kp_himax_in_parse_assign_cmd(hx83112_e8_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; + + /* Disable read DD */ + kp_himax_in_parse_assign_cmd(hx83112_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x00; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); +} + +static void hx83112b_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83112_ic_osc_pw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xA5; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x11; + tmp_data[3] = 0x2B; + kp_himax_in_parse_assign_cmd(hx83112_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83112_cb_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("CB=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_old_ic_ver = tmp_data[0]; + + kp_himax_in_parse_assign_cmd(hx83112_e8_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; +} + +static void hx83112e_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83112_ic_osc_pw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xA5; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x11; + tmp_data[3] = 0x2E; + kp_himax_in_parse_assign_cmd(hx83112_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83112_cb_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("CB=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_old_ic_ver = tmp_data[0]; + + kp_himax_in_parse_assign_cmd(hx83112_e8_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; +} + +static void hx83112_dynamic_fw_name(uint8_t ic_name) +{ + char firmware_name[64]; + + if (ic_name == 0x2a) + hx83112a_read_ic_ver(); + else if (ic_name == 0x2b) + hx83112b_read_ic_ver(); + else + hx83112e_read_ic_ver(); + + if ((*kp_i_CTPM_firmware_name) != NULL) { + kfree((*kp_i_CTPM_firmware_name)); + (*kp_i_CTPM_firmware_name) = NULL; + } + memset(firmware_name, 0x00, sizeof(firmware_name)); + + if (((*kp_ic_data)->vendor_ic_ver == 0x13) || ((*kp_ic_data)->vendor_ic_ver == 0x14) || ((*kp_ic_data)->vendor_ic_ver == 0x15) + || ((*kp_ic_data)->vendor_ic_ver == 0x16) || ((*kp_ic_data)->vendor_ic_ver == 0x23)) { + (*kp_ic_data)->vendor_semifac = 2; + } else if (((*kp_ic_data)->vendor_old_ic_ver == 0x44) || ((*kp_ic_data)->vendor_old_ic_ver == 0x77) + || ((*kp_ic_data)->vendor_ic_ver == 0x03) || ((*kp_ic_data)->vendor_ic_ver == 0x04)) { + (*kp_ic_data)->vendor_semifac = 1; + } else { + (*kp_ic_data)->vendor_semifac = 0; + } + memcpy(firmware_name, "Himax_firmware.bin", sizeof(char)*strlen("Himax_firmware.bin")); + + (*kp_i_CTPM_firmware_name) = kzalloc((sizeof(char)*(strlen(firmware_name)+1)), GFP_KERNEL); + if ((*kp_i_CTPM_firmware_name) != NULL) + memcpy((*kp_i_CTPM_firmware_name), firmware_name, (sizeof(char)*(strlen(firmware_name)+1))); + + I("(*kp_i_CTPM_firmware_name) : %s\n", (*kp_i_CTPM_firmware_name)); +} +#endif +#endif + +static void hx83112_func_re_init(void) +{ + kp_g_core_fp->fp_sense_off = hx83112_sense_off; + kp_g_core_fp->fp_chip_init = hx83112_chip_init; +} + +#ifdef HX_ESD_RECOVERY +static void himax_hx83112f_esd_dd_ic_reset(void) +{ + uint8_t addr[4] = {0}; + uint8_t data[4] = {0}; + + I("%s: Entering\n", __func__); + kp_himax_in_parse_assign_cmd(0x9000001C, addr, sizeof(addr)); + kp_himax_in_parse_assign_cmd(0x49, data, sizeof(data)); + kp_g_core_fp->fp_register_write(addr, 4, data, 0); + usleep_range(49000, 49000); +} +#endif + +static void himax_hx83112f_reload_to_active(void) +{ + uint8_t addr[DATA_LEN_4] = {0}; + uint8_t data[DATA_LEN_4] = {0}; + uint8_t retry_cnt = 0; + + addr[3] = 0x90; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x48; + + do { + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0xEC; + kp_g_core_fp->fp_register_write(addr, DATA_LEN_4, data, 0); + usleep_range(1000, 1100); + kp_g_core_fp->fp_register_read(addr, DATA_LEN_4, data, 0); + I("%s: data[1]=%d, data[0]=%d, retry_cnt=%d\n", __func__, + data[1], data[0], retry_cnt); + retry_cnt++; + } while ((data[1] != 0x01 + || data[0] != 0xEC) + && retry_cnt < HIMAX_REG_RETRY_TIMES); +} + +static void himax_hx83112f_resume_ic_action(void) +{ + himax_hx83112f_reload_to_active(); +} + +static void himax_hx83112f_suspend_ic_action(void) +{ + himax_hx83112f_reload_to_active(); +} + +static void himax_hx83112f_sense_on(uint8_t FlashMode) +{ + uint8_t tmp_data[DATA_LEN_4]; + int retry = 0; + int ret = 0; + + I("Enter %s\n", __func__); + + kp_g_core_fp->fp_interface_on(); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_ctrl_fw_isr, + sizeof((*kp_pfw_op)->data_clear), (*kp_pfw_op)->data_clear, 0); + /*msleep(20);*/ + usleep_range(10000, 10001); + if (!FlashMode) { +#if defined(HX_RST_PIN_FUNC) + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + himax_hx83112f_reload_to_active(); + } else { + himax_hx83112f_reload_to_active(); + do { + kp_g_core_fp->fp_register_write( + (*kp_pfw_op)->addr_safe_mode_release_pw, + sizeof((*kp_pfw_op)->data_safe_mode_release_pw_active), + (*kp_pfw_op)->data_safe_mode_release_pw_active, + 0); + + kp_g_core_fp->fp_register_read( + (*kp_pfw_op)->addr_flag_reset_event, + DATA_LEN_4, tmp_data, 0); + I("%s:Read status from IC = %X,%X\n", __func__, + tmp_data[0], tmp_data[1]); + } while ((tmp_data[1] != 0x01 + || tmp_data[0] != 0x00) + && retry++ < 5); + + if (retry >= 5) { + E("%s: Fail:\n", __func__); +#if defined(HX_RST_PIN_FUNC) + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + himax_hx83112f_reload_to_active(); + } else { + I("%s:OK and Read status from IC = %X,%X\n", __func__, + tmp_data[0], tmp_data[1]); + /* reset code*/ + tmp_data[0] = 0x00; + + ret = kp_himax_bus_write( + (*kp_pic_op)->data_i2c_psw_lb[0], + tmp_data, 1, HIMAX_XFER_RETRY_TIMES); + if (ret < 0) + E("%s: i2c access fail!\n", __func__); + + ret = kp_himax_bus_write( + (*kp_pic_op)->data_i2c_psw_ub[0], + tmp_data, 1, HIMAX_XFER_RETRY_TIMES); + if (ret < 0) + E("%s: i2c access fail!\n", __func__); + + kp_g_core_fp->fp_register_write( + (*kp_pfw_op)->addr_safe_mode_release_pw, + sizeof((*kp_pfw_op)->data_safe_mode_release_pw_reset), + (*kp_pfw_op)->data_safe_mode_release_pw_reset, + 0); + } + } +} + +/* File node for SMWP and HSEN - End*/ +void himax_parse_assign_cmd(uint32_t addr, uint8_t *cmd, int len) +{ + /*I("%s: Entering!\n", __func__);*/ + + switch (len) { + case 1: + cmd[0] = addr; + /*I("%s: cmd[0] = 0x%02X\n", __func__, cmd[0]);*/ + break; + + case 2: + cmd[0] = addr % 0x100; + cmd[1] = (addr >> 8) % 0x100; + /*I("%s: cmd[0] = 0x%02X,cmd[1] = 0x%02X\n",*/ + /* __func__, cmd[0], cmd[1]);*/ + break; + + case 4: + cmd[0] = addr % 0x100; + cmd[1] = (addr >> 8) % 0x100; + cmd[2] = (addr >> 16) % 0x100; + cmd[3] = addr / 0x1000000; + /* I("%s: cmd[0] = 0x%02X,cmd[1] = 0x%02X,*/ + /*cmd[2] = 0x%02X,cmd[3] = 0x%02X\n", */ + /* __func__, cmd[0], cmd[1], cmd[2], cmd[3]);*/ + break; + + default: + E("%s: input length fault,len = %d!\n", __func__, len); + } +} + +static void hx83112f_reg_re_init(void) +{ + himax_parse_assign_cmd(hx83112f_fw_addr_raw_out_sel, + (*kp_pfw_op)->addr_raw_out_sel, + sizeof((*kp_pfw_op)->addr_raw_out_sel)); +} + +static void hx83112f_func_re_init(void) +{ + kp_g_core_fp->fp_resume_ic_action = himax_hx83112f_resume_ic_action; + kp_g_core_fp->fp_suspend_ic_action = himax_hx83112f_suspend_ic_action; + kp_g_core_fp->fp_sense_on = himax_hx83112f_sense_on; + kp_g_core_fp->fp_0f_reload_to_active = himax_hx83112f_reload_to_active; +#ifdef HX_ESD_RECOVERY + kp_g_core_fp->_esd_ic_dd_reset = himax_hx83112f_esd_dd_ic_reset; +#endif +} + +static void hx83112_reg_re_init(void) +{ +} + +static bool hx83112_chip_detect(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + bool ret_data = false; + int ret = 0; + int i = 0; + + if (himax_ic_setup_external_symbols()) + return false; + + ret = kp_himax_mcu_in_cmd_struct_init(); + if (ret < 0) { + ret_data = false; + E("%s:cmd_struct_init Fail:\n", __func__); + return ret_data; + } + + kp_himax_mcu_in_cmd_init(); + + hx83112_reg_re_init(); + hx83112_func_re_init(); + if (kp_himax_bus_read((*kp_pic_op)->addr_conti[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + if (kp_g_core_fp->fp_sense_off(false) == false) { + ret_data = false; + E("%s:fp_sense_off Fail:\n", __func__); + return ret_data; + } + for (i = 0; i < 5; i++) { + if (kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_icid_addr, DATA_LEN_4, tmp_data, false) != 0) { + ret_data = false; + E("%s:fp_register_read Fail:\n", __func__); + return ret_data; + } + I("%s:Read driver IC ID = %X, %X, %X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1]); + + if ((tmp_data[3] == 0x83) && (tmp_data[2] == 0x11) && ((tmp_data[1] == 0x2a) || (tmp_data[1] == 0x2b) || (tmp_data[1] == 0x2e) || (tmp_data[1] == 0x2f))) { + if (tmp_data[1] == 0x2a) + strlcpy((*kp_private_ts)->chip_name, HX_83112A_SERIES_PWON, 30); + else if (tmp_data[1] == 0x2b) + strlcpy((*kp_private_ts)->chip_name, HX_83112B_SERIES_PWON, 30); + else if (tmp_data[1] == 0x2f) { + strlcpy ((*kp_private_ts)->chip_name, HX_83112F_SERIES_PWON, 30); + hx83112f_reg_re_init(); + hx83112f_func_re_init(); + } + + + I("%s:IC name = %s\n", __func__, (*kp_private_ts)->chip_name); + + I("Himax IC package %x%x%x in\n", tmp_data[3], tmp_data[2], tmp_data[1]); + ret_data = true; + goto FINAL; + } else { + ret_data = false; + E("%s:Read driver ID register Fail:\n", __func__); + E("Could NOT find Himax Chipset\n"); + E("Please check 1.VCCD,VCCA,VSP,VSN\n"); + E("2. LCM_RST,TP_RST\n"); + E("3. Power On Sequence\n"); + } + } +FINAL: +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) + hx83112_dynamic_fw_name(tmp_data[1]); +#endif +#endif + + return ret_data; +} + +DECLARE(HX_MOD_KSYM_HX83112); + +static int himax_hx83112_probe(void) +{ + I("%s:Enter\n", __func__); + himax_add_chip_dt(hx83112_chip_detect); + return 0; +} + +static int himax_hx83112_remove(void) +{ + free_chip_dt_table(); + return 0; +} + +static int __init himax_hx83112_init(void) +{ + int ret = 0; + + I("%s\n", __func__); + ret = himax_hx83112_probe(); + return 0; +} + +static void __exit himax_hx83112_exit(void) +{ + himax_hx83112_remove(); +} + +module_init(himax_hx83112_init); +module_exit(himax_hx83112_exit); + +MODULE_DESCRIPTION("HIMAX HX83112 touch driver"); +MODULE_LICENSE("GPL"); + + diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112f/himax_ic_HX83112f.h b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112f/himax_ic_HX83112f.h new file mode 100644 index 000000000000..34d14691f890 --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_0flash_mmi_hx83112f/himax_ic_HX83112f.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for HX83112 chipset + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_platform.h" +#include "himax_common.h" +#include "himax_ic_core.h" +#include + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#ifdef HX_EN_DYNAMIC_NAME +#define hx83112_ic_b9_en 0x300B9000 +#define hx83112_ic_eb_en 0x300EB000 +#define hx83112_ic_osc_en 0x900880A8 +#define hx83112_ic_osc_pw 0x900880E0 +#define hx83112_e8_ic_fw 0x300E8006 +#define hx83112_cb_ic_fw 0x300CB008 +#endif +#endif +#define hx83112f_fw_addr_raw_out_sel 0x100072ec diff --git a/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_modular.h b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_modular.h new file mode 100644 index 000000000000..3e7a43b9ed4e --- /dev/null +++ b/drivers/input/touchscreen/himax_0flash_mmi/support_ic/himax_modular.h @@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for modularize functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef __HIMAX_MODULAR_H__ +#define __HIMAX_MODULAR_H__ + +#include "himax_modular_table.h" + +static bool (*this_detect_fp)(void); + +static void himax_add_chip_dt(bool (*detect_fp)(void)) +{ + int32_t idx; + struct himax_chip_entry *entry; + + this_detect_fp = detect_fp; + idx = himax_get_ksym_idx(); + if (idx < 0) { + /*TODO: No entry, handle this error*/ + E("%s: no entry exist, please insert ic module first!", __func__); + } else { + entry = (void *)kallsyms_lookup_name(himax_ksym_lookup[idx]); + if (/*!(entry->core_chip_dt)*/isEmpty(idx) == 1) { + entry->core_chip_dt = kcalloc(HX_DRIVER_MAX_IC_NUM, + sizeof(struct himax_chip_detect), GFP_KERNEL); + entry->hx_ic_dt_num = 0; + } + entry->core_chip_dt[entry->hx_ic_dt_num++].fp_chip_detect = detect_fp; + } +} + +static void free_chip_dt_table(void) +{ + int i, j, idx; + struct himax_chip_entry *entry; + + idx = himax_get_ksym_idx(); + if (idx >= 0) { + if (isEmpty(idx) != 0) { + I("%s: no chip registered or entry has been clean up, leave safetly.\n", + __func__); + return; + } + entry = get_chip_entry_by_index(idx); + + for (i = 0; i < entry->hx_ic_dt_num; i++) { + if (entry->core_chip_dt[i].fp_chip_detect == this_detect_fp) { + if (i == (entry->hx_ic_dt_num - 1)) { + entry->core_chip_dt[i].fp_chip_detect = NULL; + entry->hx_ic_dt_num = 0; + } else { + for (j = i; j < entry->hx_ic_dt_num; j++) + entry->core_chip_dt[i].fp_chip_detect = entry->core_chip_dt[j].fp_chip_detect; + + entry->core_chip_dt[j].fp_chip_detect = NULL; + entry->hx_ic_dt_num--; + } + } + } + if (entry->hx_ic_dt_num == 0) { + kfree(entry->core_chip_dt); + entry->core_chip_dt = NULL; + } + } +} + +#define setup_symbol(sym) ({kp_##sym = (void *)kallsyms_lookup_name(#sym); kp_##sym; }) +#define assert_on_symbol(sym) do { \ + if (!setup_symbol(sym)) { \ + E("%s: setup %s failed!\n", __func__, #sym); \ + ret = -1; \ + } \ + } while (0) +#if !defined(__HIMAX_HX852xH_MOD__) && !defined(__HIMAX_HX852xG_MOD__) +static struct fw_operation **kp_pfw_op; +static struct ic_operation **kp_pic_op; +static struct driver_operation **kp_pdriver_op; +#endif +static struct himax_chip_detect **kp_g_core_chip_dt; + +#if defined(HX_ZERO_FLASH) +static struct zf_operation **kp_pzf_op; +static int *kp_G_POWERONOF; +#endif + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) + static char **kp_i_CTPM_firmware_name; +#endif +#endif + +static unsigned char *kp_IC_CHECKSUM; + +#ifdef HX_ESD_RECOVERY +static u8 *kp_HX_ESD_RESET_ACTIVATE; +#endif + +#if defined(HX_ZERO_FLASH) && defined(HX_CODE_OVERLAY) +#if defined(CONFIG_TOUCHSCREEN_HIMAX_INCELL) +extern uint8_t *ovl_idx; +static uint8_t **kp_ovl_idx; +#endif +#endif + +static unsigned long *kp_FW_VER_MAJ_FLASH_ADDR; +static unsigned long *kp_FW_VER_MIN_FLASH_ADDR; +static unsigned long *kp_CFG_VER_MAJ_FLASH_ADDR; +static unsigned long *kp_CFG_VER_MIN_FLASH_ADDR; +static unsigned long *kp_CID_VER_MAJ_FLASH_ADDR; +static unsigned long *kp_CID_VER_MIN_FLASH_ADDR; + +static unsigned long *kp_FW_VER_MAJ_FLASH_LENG; +static unsigned long *kp_FW_VER_MIN_FLASH_LENG; +static unsigned long *kp_CFG_VER_MAJ_FLASH_LENG; +static unsigned long *kp_CFG_VER_MIN_FLASH_LENG; +static unsigned long *kp_CID_VER_MAJ_FLASH_LENG; +static unsigned long *kp_CID_VER_MIN_FLASH_LENG; + +#ifdef HX_AUTO_UPDATE_FW + static int *kp_g_i_FW_VER; + static int *kp_g_i_CFG_VER; + static int *kp_g_i_CID_MAJ; + static int *kp_g_i_CID_MIN; + static unsigned char **kp_i_CTPM_FW; +#endif + +#ifdef HX_TP_PROC_2T2R + static bool *kp_Is_2T2R; +#endif + +#ifdef HX_USB_DETECT_GLOBAL + static void (*kp_himax_cable_detect_func)(bool force_renew); +#endif + +#ifdef HX_RST_PIN_FUNC + static void (*kp_himax_rst_gpio_set)(int pinnum, uint8_t value); +#endif + +static struct himax_ts_data **kp_private_ts; +static struct himax_core_fp *kp_g_core_fp; +static struct himax_ic_data **kp_ic_data; + +#if defined(__HIMAX_HX852xH_MOD__) || defined(__HIMAX_HX852xG_MOD__) +#if defined(__HIMAX_HX852xG_MOD__) +static struct on_driver_operation **kp_on_pdriver_op; +#endif +static void (*kp_himax_mcu_on_cmd_init)(void); +static int (*kp_himax_mcu_on_cmd_struct_init)(void); +static void (*kp_himax_on_parse_assign_cmd)(uint32_t addr, uint8_t *cmd, int len); +#else +static void (*kp_himax_mcu_in_cmd_init)(void); +static int (*kp_himax_mcu_in_cmd_struct_init)(void); +static void (*kp_himax_in_parse_assign_cmd)(uint32_t addr, uint8_t *cmd, int len); +#endif + +static int *kp_irq_enable_count; +static int (*kp_himax_bus_read)(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +static int (*kp_himax_bus_write)(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +static int (*kp_himax_bus_write_command)(uint8_t command, uint8_t toRetry); +static int (*kp_himax_bus_master_write)(uint8_t *data, uint32_t length, uint8_t toRetry); +static void (*kp_himax_int_enable)(int enable); +static int (*kp_himax_ts_register_interrupt)(void); +static uint8_t (*kp_himax_int_gpio_read)(int pinnum); +static int (*kp_himax_gpio_power_config)(struct himax_i2c_platform_data *pdata); + +static int32_t himax_ic_setup_external_symbols(void) +{ + int32_t ret = 0; + +#if !defined(__HIMAX_HX852xH_MOD__) && !defined(__HIMAX_HX852xG_MOD__) + assert_on_symbol(pfw_op); + assert_on_symbol(pic_op); + assert_on_symbol(pdriver_op); +#endif + assert_on_symbol(g_core_chip_dt); + + assert_on_symbol(private_ts); + assert_on_symbol(g_core_fp); + assert_on_symbol(ic_data); + +#if defined(__HIMAX_HX852xH_MOD__) || defined(__HIMAX_HX852xG_MOD__) +#if defined(__HIMAX_HX852xG_MOD__) + assert_on_symbol(on_pdriver_op); +#endif + assert_on_symbol(himax_mcu_on_cmd_init); + assert_on_symbol(himax_mcu_on_cmd_struct_init); + assert_on_symbol(himax_on_parse_assign_cmd); +#else + assert_on_symbol(himax_mcu_in_cmd_init); + assert_on_symbol(himax_mcu_in_cmd_struct_init); + assert_on_symbol(himax_in_parse_assign_cmd); +#endif + + assert_on_symbol(irq_enable_count); + assert_on_symbol(himax_bus_read); + assert_on_symbol(himax_bus_write); + assert_on_symbol(himax_bus_write_command); + assert_on_symbol(himax_bus_master_write); + assert_on_symbol(himax_int_enable); + assert_on_symbol(himax_ts_register_interrupt); + assert_on_symbol(himax_int_gpio_read); + assert_on_symbol(himax_gpio_power_config); + +#if defined(HX_ZERO_FLASH) + assert_on_symbol(pzf_op); + assert_on_symbol(G_POWERONOF); +#endif + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) + assert_on_symbol(i_CTPM_firmware_name); +#endif +#endif + assert_on_symbol(IC_CHECKSUM); + + assert_on_symbol(FW_VER_MAJ_FLASH_ADDR); + assert_on_symbol(FW_VER_MIN_FLASH_ADDR); + assert_on_symbol(CFG_VER_MAJ_FLASH_ADDR); + assert_on_symbol(CFG_VER_MIN_FLASH_ADDR); + assert_on_symbol(CID_VER_MAJ_FLASH_ADDR); + assert_on_symbol(CID_VER_MIN_FLASH_ADDR); + + assert_on_symbol(FW_VER_MAJ_FLASH_LENG); + assert_on_symbol(FW_VER_MIN_FLASH_LENG); + assert_on_symbol(CFG_VER_MAJ_FLASH_LENG); + assert_on_symbol(CFG_VER_MIN_FLASH_LENG); + assert_on_symbol(CID_VER_MAJ_FLASH_LENG); + assert_on_symbol(CID_VER_MIN_FLASH_LENG); + +#ifdef HX_AUTO_UPDATE_FW + assert_on_symbol(g_i_FW_VER); + assert_on_symbol(g_i_CFG_VER); + assert_on_symbol(g_i_CID_MAJ); + assert_on_symbol(g_i_CID_MIN); + assert_on_symbol(i_CTPM_FW); +#endif + +#ifdef HX_TP_PROC_2T2R + assert_on_symbol(Is_2T2R); +#endif + +#ifdef HX_USB_DETECT_GLOBAL + assert_on_symbol(himax_cable_detect_func); +#endif + +#ifdef HX_RST_PIN_FUNC + assert_on_symbol(himax_rst_gpio_set); +#endif + +#ifdef HX_ESD_RECOVERY + assert_on_symbol(HX_ESD_RESET_ACTIVATE); +#endif + +#if defined(HX_ZERO_FLASH) && defined(HX_CODE_OVERLAY) +#if defined(CONFIG_TOUCHSCREEN_HIMAX_INCELL) + assert_on_symbol(ovl_idx); +#endif +#endif + return ret; +} + + +#endif diff --git a/drivers/input/touchscreen/himax_mmi/Android.mk b/drivers/input/touchscreen/himax_mmi/Android.mk new file mode 100644 index 000000000000..da5e1683670d --- /dev/null +++ b/drivers/input/touchscreen/himax_mmi/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +HIMAX_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +HIMAX_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := himax_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(HIMAX_MMI_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/himax_mmi/Kbuild b/drivers/input/touchscreen/himax_mmi/Kbuild new file mode 100644 index 000000000000..4e39b6c99012 --- /dev/null +++ b/drivers/input/touchscreen/himax_mmi/Kbuild @@ -0,0 +1,18 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_mmi +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_DEBUG \ + -DCONFIG_TOUCHSCREEN_HIMAX_INSPECT + +ifeq ($(filter m y, $(CONFIG_TOUCHSCREEN_HIMAX_CHIPSET)),) + obj-m := himax_mmi.o +endif + +himax_mmi-objs += himax_ic_HX83112.o +himax_mmi-objs += himax_ic_HX83102.o +himax_mmi-objs += himax_platform.o +himax_mmi-objs += himax_inspection.o +himax_mmi-objs += himax_ic_incell_core.o +himax_mmi-objs += himax_common.o +himax_mmi-objs += himax_debug.o diff --git a/drivers/input/touchscreen/himax_mmi/Makefile b/drivers/input/touchscreen/himax_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/himax_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/himax_mmi/himax_common.c b/drivers/input/touchscreen/himax_mmi/himax_common.c new file mode 100644 index 000000000000..059db42dc7c2 --- /dev/null +++ b/drivers/input/touchscreen/himax_mmi/himax_common.c @@ -0,0 +1,2978 @@ +/* Himax Android Driver Sample Code for common functions + + Copyright (C) 2018 Himax Corporation. + + 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. + +*/ + +#include "himax_common.h" +#include "himax_ic_core.h" + +#ifdef HX_SMART_WAKEUP +#define GEST_SUP_NUM 26 +/*Setting cust key define (DF = double finger)*/ +/*{Double Tap, Up, Down, Left, Rright, C, Z, M, + O, S, V, W, e, m, @, (reserve), + Finger gesture, ^, >, <, f(R), f(L), Up(DF), Down(DF), + Left(DF), Right(DF)}*/ + + uint8_t gest_event[GEST_SUP_NUM] = { + 0x80, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x81, 0x1D, 0x2D, 0x3D, 0x1F, 0x2F, 0x51, 0x52, + 0x53, 0x54}; + +/*gest_event mapping to gest_key_def*/ + uint16_t gest_key_def[GEST_SUP_NUM] = { + KEY_POWER, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275}; +#endif + +#define SUPPORT_FINGER_DATA_CHECKSUM 0x0F +#define TS_WAKE_LOCK_TIMEOUT (2 * HZ) +#define FRAME_COUNT 5 + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +char *i_CTPM_firmware_name = "Himax_firmware.bin"; +bool g_auto_update_flag = false; +#endif +#if defined(HX_AUTO_UPDATE_FW) +unsigned char *i_CTPM_FW = NULL; +int i_CTPM_FW_len; +int g_i_FW_VER = 0; +int g_i_CFG_VER = 0; +int g_i_CID_MAJ = 0; /*GUEST ID*/ +int g_i_CID_MIN = 0; /*VER for GUEST*/ +#endif +#ifdef HX_ZERO_FLASH +int g_f_0f_updat = 0; +#endif + +struct himax_ts_data *private_ts; +struct himax_ic_data *ic_data; +struct himax_report_data *hx_touch_data; +struct himax_core_fp g_core_fp; +struct himax_debug *debug_data; + +struct proc_dir_entry *himax_touch_proc_dir; +#define HIMAX_PROC_TOUCH_FOLDER "android_touch" +#ifdef CONFIG_TOUCHSCREEN_HIMAX_DEBUG + extern int himax_debug_init(void); + extern int himax_debug_remove(void); +#endif +/*ts_work about start*/ +struct himax_target_report_data *g_target_report_data = NULL; +int himax_report_data(struct himax_ts_data *ts, int ts_path, int ts_status); +static void himax_report_all_leave_event(struct himax_ts_data *ts); +/*ts_work about end*/ +static int HX_TOUCH_INFO_POINT_CNT; + +unsigned long FW_VER_MAJ_FLASH_ADDR; +unsigned long FW_VER_MIN_FLASH_ADDR; +unsigned long CFG_VER_MAJ_FLASH_ADDR; +unsigned long CFG_VER_MIN_FLASH_ADDR; +unsigned long CID_VER_MAJ_FLASH_ADDR; +unsigned long CID_VER_MIN_FLASH_ADDR; +/*unsigned long PANEL_VERSION_ADDR;*/ + +unsigned long FW_VER_MAJ_FLASH_LENG; +unsigned long FW_VER_MIN_FLASH_LENG; +unsigned long CFG_VER_MAJ_FLASH_LENG; +unsigned long CFG_VER_MIN_FLASH_LENG; +unsigned long CID_VER_MAJ_FLASH_LENG; +unsigned long CID_VER_MIN_FLASH_LENG; +/*unsigned long PANEL_VERSION_LENG;*/ + +unsigned long FW_CFG_VER_FLASH_ADDR; + +unsigned char IC_CHECKSUM = 0; + +#ifdef HX_ESD_RECOVERY + u8 HX_ESD_RESET_ACTIVATE = 0; + int hx_EB_event_flag = 0; + int hx_EC_event_flag = 0; + int hx_ED_event_flag = 0; + int g_zero_event_count = 0; +#endif +u8 HX_HW_RESET_ACTIVATE = 0; + +#if defined(HX_PLATFOME_DEFINE_KEY) + extern void himax_platform_key(void); +#endif + +extern int himax_parse_dt(struct himax_ts_data *ts, + struct himax_i2c_platform_data *pdata); + +static uint8_t AA_press = 0x00; +static uint8_t EN_NoiseFilter = 0x00; +static uint8_t Last_EN_NoiseFilter = 0x00; + +static int p_point_num = 0xFFFF; +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) +static uint8_t vk_press = 0x00; +static int tpd_key = 0x00; +static int tpd_key_old = 0x00; +#endif +static int probe_fail_flag; +#ifdef HX_USB_DETECT_GLOBAL + bool USB_detect_flag; +#endif + +#if defined(CONFIG_FB) +int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void himax_ts_early_suspend(struct early_suspend *h); +static void himax_ts_late_resume(struct early_suspend *h); +#endif + +#ifdef HX_GESTURE_TRACK + static int gest_pt_cnt; + static int gest_pt_x[GEST_PT_MAX_NUM]; + static int gest_pt_y[GEST_PT_MAX_NUM]; + static int gest_start_x, gest_start_y, gest_end_x, gest_end_y; + static int gest_width, gest_height, gest_mid_x, gest_mid_y; + static int hx_gesture_coor[16]; +#endif + +int himax_report_data_init(void); + +extern bool hx83102_chip_detect (void); +extern bool hx83112_chip_detect (void); +extern int himax_dev_set(struct himax_ts_data *ts); +extern int himax_input_register_device(struct input_dev *input_dev); +extern char *hx_self_test_file_name; + +int g_ts_dbg = 0; + +/* File node for Selftest, SMWP and HSEN - Start*/ +#define HIMAX_PROC_SELF_TEST_FILE "self_test" +struct proc_dir_entry *himax_proc_self_test_file; + +extern char *g_rslt_data; +#define HIMAX_PROC_SELF_TEST_RAW_FILE "self_test_rawdata" +struct proc_dir_entry *himax_proc_self_raw_file = NULL; + +uint8_t HX_PROC_SEND_FLAG; +#ifdef HX_SMART_WAKEUP + #define HIMAX_PROC_SMWP_FILE "SMWP" + struct proc_dir_entry *himax_proc_SMWP_file = NULL; + #define HIMAX_PROC_GESTURE_FILE "GESTURE" + struct proc_dir_entry *himax_proc_GESTURE_file = NULL; + uint8_t HX_SMWP_EN = 0; +#endif +#ifdef HX_EDGE_LIMIT + #define HIMAX_PROC_EDGE_LIMIT_FILE "edge_en" + struct proc_dir_entry *himax_proc_edge_limit_file = NULL; +#endif + +#if defined(HX_SMART_WAKEUP) || defined(CONFIG_TOUCHSCREEN_HIMAX_INSPECT) +bool FAKE_POWER_KEY_SEND = true; +#endif + +#ifdef HX_HIGH_SENSE + #define HIMAX_PROC_HSEN_FILE "HSEN" + struct proc_dir_entry *himax_proc_HSEN_file = NULL; +#endif + +#if defined(HX_PALM_REPORT) +static int himax_palm_detect(uint8_t *buf) +{ + struct himax_ts_data *ts = private_ts; + int32_t loop_i; + int base = 0; + int x = 0, y = 0, w = 0; + + loop_i = 0; + base = loop_i * 4; + x = buf[base] << 8 | buf[base + 1]; + y = (buf[base + 2] << 8 | buf[base + 3]); + w = buf[(ts->nFinger_support * 4) + loop_i]; + I(" %s HX_PALM_REPORT_loopi=%d,base=%x,X=%x,Y=%x,W=%x \n", __func__, loop_i, base, x, y, w); + if ((!atomic_read(&ts->suspend_mode)) && (x == 0xFA5A) && (y == 0xFA5A) && (w == 0x00)) + return PALM_REPORT; + else + return NOT_REPORT; +} +#endif + +static ssize_t himax_self_test_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + int i = 0; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) { + return -EFAULT; + } + + if (hx_self_test_file_name == NULL) { + E("file name is NULL\n"); + hx_self_test_file_name = kzalloc(80, GFP_KERNEL); + snprintf(hx_self_test_file_name, 15, "hx_criteria.csv"); + } + + for (i = 0; i < 80; i++) { + if (buf[i] == ',' || buf[i] == '\n') { + memset(hx_self_test_file_name, 0x0, 80); + memcpy(hx_self_test_file_name, buf, i); + I("%s: Get name from Customer\n", __func__); + break; + } + } + if (i == 80) { + memset(hx_self_test_file_name, 0x0, 80); + snprintf(hx_self_test_file_name, 16, "hx_criteria.csv"); + I("%s: Use default name\n", __func__); + } + I("file name = %s\n", hx_self_test_file_name); + + return len; +} + +static ssize_t himax_self_test_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + int val = 0x00; + size_t ret = 0; + char *temp_buf; + I("%s: enter, %d \n", __func__, __LINE__); + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + himax_int_enable(0);/* disable irq */ + private_ts->in_self_test = 1; + val = g_core_fp.fp_chip_self_test(); +#ifdef HX_ESD_RECOVERY + HX_ESD_RESET_ACTIVATE = 1; +#endif + himax_int_enable(1);/* enable irq */ + + if (val == 0x00) { + ret += snprintf(temp_buf + ret, len - ret, "Self_Test Pass\n"); + } else { + ret += snprintf(temp_buf + ret, len - ret, "Self_Test Fail\n"); + } + + private_ts->in_self_test = 0; + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static struct file_operations himax_proc_self_test_ops = { + .owner = THIS_MODULE, + .read = himax_self_test_read, + .write = himax_self_test_write, +}; + +static void *himax_self_raw_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) { + return NULL; + } + return (void *)((unsigned long) *pos + 1); +} + +static void *himax_self_raw_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_self_raw_seq_stop(struct seq_file *s, void *v) +{ +} + +static int himax_self_raw_seq_read(struct seq_file *s, void *v) +{ + size_t ret = 0; + + if (g_rslt_data != NULL) { + seq_printf(s, "%s", g_rslt_data); + } + return ret; +} +static struct seq_operations himax_self_raw_seq_ops = { + .start = himax_self_raw_seq_start, + .next = himax_self_raw_seq_next, + .stop = himax_self_raw_seq_stop, + .show = himax_self_raw_seq_read, +}; +static int himax_self_raw_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_self_raw_seq_ops); +}; + +static struct file_operations himax_proc_self_raw_ops = { + .owner = THIS_MODULE, + .open = himax_self_raw_proc_open, + .read = seq_read, +}; + +#ifdef HX_HIGH_SENSE +static ssize_t himax_HSEN_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + size_t count = 0; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + count = snprintf(temp_buf, PAGE_SIZE, "%d\n", ts->HSEN_enable); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return count; +} + +static ssize_t himax_HSEN_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) { + return -EFAULT; + } + + if (buf[0] == '0') + ts->HSEN_enable = 0; + else if (buf[0] == '1') + ts->HSEN_enable = 1; + else + return -EINVAL; + + g_core_fp.fp_set_HSEN_enable(ts->HSEN_enable, ts->suspended); + I("%s: HSEN_enable = %d.\n", __func__, ts->HSEN_enable); + return len; +} + +static struct file_operations himax_proc_HSEN_ops = { + .owner = THIS_MODULE, + .read = himax_HSEN_read, + .write = himax_HSEN_write, +}; +#endif + +#ifdef HX_SMART_WAKEUP +static ssize_t himax_SMWP_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + size_t count = 0; + struct himax_ts_data *ts = private_ts; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + count = snprintf(temp_buf, PAGE_SIZE, "%d\n", ts->SMWP_enable); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return count; +} + +static ssize_t himax_SMWP_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) { + return -EFAULT; + } + + if (buf[0] == '0') + ts->SMWP_enable = 0; + else if (buf[0] == '1') + ts->SMWP_enable = 1; + else + return -EINVAL; + + g_core_fp.fp_set_SMWP_enable(ts->SMWP_enable, ts->suspended); + HX_SMWP_EN = ts->SMWP_enable; + I("%s: SMART_WAKEUP_enable = %d.\n", __func__, HX_SMWP_EN); + return len; +} + +static struct file_operations himax_proc_SMWP_ops = { + .owner = THIS_MODULE, + .read = himax_SMWP_read, + .write = himax_SMWP_write, +}; + +static ssize_t himax_GESTURE_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + int i = 0; + size_t ret = 0; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + + for (i = 0; i < GEST_SUP_NUM; i++) + ret += snprintf(temp_buf + ret, len - ret, "ges_en[%d]=%d \n", i, ts->gesture_cust_en[i]); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + ret = 0; + } + + return ret; +} + +static ssize_t himax_GESTURE_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + int i = 0; + int j = 0; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) { + return -EFAULT; + } + + I("himax_GESTURE_store= %s, len = %d \n", buf, (int)len); + + for (i = 0; i < len; i++) { + if (buf[i] == '0' && j < GEST_SUP_NUM) { + ts->gesture_cust_en[j] = 0; + I("gesture en[%d]=%d \n", j, ts->gesture_cust_en[j]); + j++; + } else if (buf[i] == '1' && j < GEST_SUP_NUM) { + ts->gesture_cust_en[j] = 1; + I("gesture en[%d]=%d \n", j, ts->gesture_cust_en[j]); + j++; + } else + I("Not 0/1 or >=GEST_SUP_NUM : buf[%d] = %c\n", i, buf[i]); + } + + return len; +} + +static struct file_operations himax_proc_Gesture_ops = { + .owner = THIS_MODULE, + .read = himax_GESTURE_read, + .write = himax_GESTURE_write, +}; +#endif + +#ifdef HX_EDGE_LIMIT +static ssize_t himax_edge_limit_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + size_t count = 0; + struct himax_ts_data *ts = private_ts; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + count = snprintf(temp_buf, PAGE_SIZE, "%d\n", ts->edge_limit_enable); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return count; +} + +static ssize_t himax_edge_limit_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) { + return -EFAULT; + } + + if (buf[0] == '0') + ts->edge_limit_enable = 0; + else if (buf[0] == '1') + ts->edge_limit_enable = 1;/*Portrait*/ + else if (buf[0] == '2') + ts->edge_limit_enable = 2;/*Landscape-Right*/ + else if (buf[0] == '3') + ts->edge_limit_enable = 3;/*Landscape-Left*/ + else + return -EINVAL; + + g_core_fp.fp_set_edge_limit_enable(ts->edge_limit_enable, ts->suspended); + I("%s: edge_limit_enable = %d.\n", __func__, ts->edge_limit_enable); + return len; +} + +static struct file_operations himax_proc_edge_limit_ops = { + .owner = THIS_MODULE, + .read = himax_edge_limit_read, + .write = himax_edge_limit_write, +}; +#endif + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT +extern void (*fp_himax_self_test_init)(void); +#endif + +int himax_common_proc_init(void) +{ + himax_touch_proc_dir = proc_mkdir(HIMAX_PROC_TOUCH_FOLDER, NULL); + + if (himax_touch_proc_dir == NULL) { + E(" %s: himax_touch_proc_dir file create failed!\n", __func__); + return -ENOMEM; + } +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + if (fp_himax_self_test_init != NULL) + fp_himax_self_test_init(); +#endif + + himax_proc_self_test_file = proc_create(HIMAX_PROC_SELF_TEST_FILE, (S_IRUGO | S_IWUSR | S_IWGRP), + himax_touch_proc_dir, &himax_proc_self_test_ops); + if (himax_proc_self_test_file == NULL) { + E(" %s: proc self_test file create failed!\n", __func__); + goto fail_1; + } + + himax_proc_self_raw_file = proc_create(HIMAX_PROC_SELF_TEST_RAW_FILE, (S_IRUGO), himax_touch_proc_dir, &himax_proc_self_raw_ops); + if (himax_proc_self_raw_file == NULL) { + E(" %s: proc self_test file create failed!\n", __func__); + goto fail_2; + } + +#ifdef HX_HIGH_SENSE + himax_proc_HSEN_file = proc_create(HIMAX_PROC_HSEN_FILE, (S_IWUSR | S_IRUGO | S_IWUGO), + himax_touch_proc_dir, &himax_proc_HSEN_ops); + + if (himax_proc_HSEN_file == NULL) { + E(" %s: proc HSEN file create failed!\n", __func__); + goto fail_3; + } + +#endif +#ifdef HX_SMART_WAKEUP + himax_proc_SMWP_file = proc_create(HIMAX_PROC_SMWP_FILE, (S_IWUSR | S_IRUGO | S_IWUGO), + himax_touch_proc_dir, &himax_proc_SMWP_ops); + + if (himax_proc_SMWP_file == NULL) { + E(" %s: proc SMWP file create failed!\n", __func__); + goto fail_4; + } + + himax_proc_GESTURE_file = proc_create(HIMAX_PROC_GESTURE_FILE, (S_IWUSR | S_IRUGO | S_IWUGO), + himax_touch_proc_dir, &himax_proc_Gesture_ops); + + if (himax_proc_GESTURE_file == NULL) { + E(" %s: proc GESTURE file create failed!\n", __func__); + goto fail_5; + } +#endif +#ifdef HX_EDGE_LIMIT + himax_proc_edge_limit_file = proc_create(HIMAX_PROC_EDGE_LIMIT_FILE, (S_IWUSR | S_IRUGO | S_IWUGO), + himax_touch_proc_dir, &himax_proc_edge_limit_ops); + + if (himax_proc_edge_limit_file == NULL) { + E(" %s: proc edge limit file create failed!\n", __func__); + goto fail_6; + } +#endif + + return 0; +#ifdef HX_EDGE_LIMIT + remove_proc_entry(HIMAX_PROC_EDGE_LIMIT_FILE, himax_touch_proc_dir); +fail_6: +#endif +#ifdef HX_SMART_WAKEUP + remove_proc_entry(HIMAX_PROC_GESTURE_FILE, himax_touch_proc_dir); +fail_5: + remove_proc_entry(HIMAX_PROC_SMWP_FILE, himax_touch_proc_dir); +fail_4: +#endif +#ifdef HX_HIGH_SENSE + remove_proc_entry(HIMAX_PROC_HSEN_FILE, himax_touch_proc_dir); +fail_3: +#endif + remove_proc_entry(HIMAX_PROC_SELF_TEST_RAW_FILE, himax_touch_proc_dir); +fail_2: + remove_proc_entry(HIMAX_PROC_SELF_TEST_FILE, himax_touch_proc_dir); +fail_1: + return -ENOMEM; +} + +void himax_common_proc_deinit(void) +{ + remove_proc_entry(HIMAX_PROC_SELF_TEST_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_SELF_TEST_RAW_FILE, himax_touch_proc_dir); +#ifdef HX_EDGE_LIMIT + remove_proc_entry(HIMAX_PROC_EDGE_LIMIT_FILE, himax_touch_proc_dir); +#endif +#ifdef HX_SMART_WAKEUP + remove_proc_entry(HIMAX_PROC_GESTURE_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_SMWP_FILE, himax_touch_proc_dir); +#endif +#ifdef HX_HIGH_SENSE + remove_proc_entry(HIMAX_PROC_HSEN_FILE, himax_touch_proc_dir); +#endif +} + +/* File node for SMWP and HSEN - End*/ + +int himax_input_register(struct himax_ts_data *ts) +{ + int ret = 0; +#if defined(HX_SMART_WAKEUP) + int i = 0; +#endif + ret = himax_dev_set(ts); + + if (ret < 0) { + goto input_device_fail; + } + + set_bit(EV_SYN, ts->input_dev->evbit); + set_bit(EV_ABS, ts->input_dev->evbit); + set_bit(EV_KEY, ts->input_dev->evbit); +#if defined(HX_PLATFOME_DEFINE_KEY) + himax_platform_key(); +#else + set_bit(KEY_BACK, ts->input_dev->keybit); + set_bit(KEY_HOME, ts->input_dev->keybit); + set_bit(KEY_MENU, ts->input_dev->keybit); + set_bit(KEY_SEARCH, ts->input_dev->keybit); +#endif +#if defined(HX_SMART_WAKEUP) || defined(HX_PALM_REPORT) + set_bit(KEY_POWER, ts->input_dev->keybit); +#endif +#if defined(HX_SMART_WAKEUP) + for (i = 1; i < GEST_SUP_NUM; i++) { + set_bit(gest_key_def[i], ts->input_dev->keybit); + } +#endif + set_bit(BTN_TOUCH, ts->input_dev->keybit); + set_bit(KEY_APPSELECT, ts->input_dev->keybit); + set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); +#ifdef HX_PROTOCOL_A + /*ts->input_dev->mtsize = ts->nFinger_support;*/ + input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, 3, 0, 0); +#else + set_bit(MT_TOOL_FINGER, ts->input_dev->keybit); +#if defined(HX_PROTOCOL_B_3PA) + input_mt_init_slots(ts->input_dev, ts->nFinger_support, INPUT_MT_DIRECT); +#else + input_mt_init_slots(ts->input_dev, ts->nFinger_support); +#endif +#endif + I("input_set_abs_params: mix_x %d, max_x %d, min_y %d, max_y %d\n", + ts->pdata->abs_x_min, ts->pdata->abs_x_max, ts->pdata->abs_y_min, ts->pdata->abs_y_max); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, ts->pdata->abs_x_min, ts->pdata->abs_x_max, ts->pdata->abs_x_fuzz, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, ts->pdata->abs_y_min, ts->pdata->abs_y_max, ts->pdata->abs_y_fuzz, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, ts->pdata->abs_pressure_min, ts->pdata->abs_pressure_max, ts->pdata->abs_pressure_fuzz, 0); +#ifndef HX_PROTOCOL_A + input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, ts->pdata->abs_pressure_min, ts->pdata->abs_pressure_max, ts->pdata->abs_pressure_fuzz, 0); + input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, ts->pdata->abs_width_min, ts->pdata->abs_width_max, ts->pdata->abs_pressure_fuzz, 0); +#endif +/* input_set_abs_params(ts->input_dev, ABS_MT_AMPLITUDE, 0, ((ts->pdata->abs_pressure_max << 16) | ts->pdata->abs_width_max), 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION, 0, (BIT(31) | (ts->pdata->abs_x_max << 16) | ts->pdata->abs_y_max), 0, 0);*/ + + if (himax_input_register_device(ts->input_dev) == 0) { + return NO_ERR; + } else { + ret = INPUT_REGISTER_FAIL; + } + +input_device_fail: + I("%s, input device register fail!\n", __func__); + return ret; +} + +static void calcDataSize(uint8_t finger_num) +{ + struct himax_ts_data *ts_data = private_ts; + ts_data->coord_data_size = 4 * finger_num; + ts_data->area_data_size = ((finger_num / 4) + (finger_num % 4 ? 1 : 0)) * 4; + ts_data->coordInfoSize = ts_data->coord_data_size + ts_data->area_data_size + 4; + ts_data->raw_data_frame_size = 128 - ts_data->coord_data_size - ts_data->area_data_size - 4 - 4 - 1; + + if (ts_data->raw_data_frame_size == 0) { + E("%s: could NOT calculate! \n", __func__); + return; + } + + ts_data->raw_data_nframes = ((uint32_t)ts_data->x_channel * ts_data->y_channel + + ts_data->x_channel + ts_data->y_channel) / ts_data->raw_data_frame_size + + (((uint32_t)ts_data->x_channel * ts_data->y_channel + + ts_data->x_channel + ts_data->y_channel) % ts_data->raw_data_frame_size) ? 1 : 0; + I("%s: coord_data_size: %d, area_data_size:%d, raw_data_frame_size:%d, raw_data_nframes:%d\n", __func__, ts_data->coord_data_size, ts_data->area_data_size, ts_data->raw_data_frame_size, ts_data->raw_data_nframes); +} + +static void calculate_point_number(void) +{ + HX_TOUCH_INFO_POINT_CNT = ic_data->HX_MAX_PT * 4; + + if ((ic_data->HX_MAX_PT % 4) == 0) { + HX_TOUCH_INFO_POINT_CNT += (ic_data->HX_MAX_PT / 4) * 4; + } else { + HX_TOUCH_INFO_POINT_CNT += ((ic_data->HX_MAX_PT / 4) + 1) * 4; + } +} +#if defined(HX_AUTO_UPDATE_FW) +static int himax_auto_update_check(void) +{ + I("%s:Entering!\n", __func__); + if (g_core_fp.fp_fw_ver_bin() == 0) { + if (((ic_data->vendor_fw_ver < g_i_FW_VER) || (ic_data->vendor_config_ver < g_i_CFG_VER))) { + I("Need to update!\n"); + return NO_ERR; + } else { + E("No need to update!\n"); + return 1; + } + } else { + E("FW bin fail!\n"); + return 1; + } +} +static int i_get_FW(void) +{ + int ret = 0; + const struct firmware *image = NULL; + + I("file name = %s\n", i_CTPM_firmware_name); + ret = request_firmware(&image, i_CTPM_firmware_name, private_ts->dev); + if (ret < 0) { + E("%s,fail in line%d error code=%d\n", __func__, __LINE__, ret); + return OPEN_FILE_FAIL; + } + + if (image != NULL) { + i_CTPM_FW_len = image->size; + i_CTPM_FW = kzalloc(sizeof(char)*i_CTPM_FW_len, GFP_KERNEL); + memcpy(i_CTPM_FW, image->data, sizeof(char)*i_CTPM_FW_len); + } else { + I("%s: i_CTPM_FW = NULL\n", __func__); + return OPEN_FILE_FAIL; + } + + release_firmware(image); + ret = NO_ERR; + return ret; +} +static int i_update_FW(void) +{ + int upgrade_times = 0; + + uint8_t ret = 0, result = 0; + + himax_int_enable(0); + + +update_retry: + + if (i_CTPM_FW_len == FW_SIZE_32k) { + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_32k(i_CTPM_FW, i_CTPM_FW_len, false); + } else if (i_CTPM_FW_len == FW_SIZE_60k) { + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_60k(i_CTPM_FW, i_CTPM_FW_len, false); + } else if (i_CTPM_FW_len == FW_SIZE_64k) { + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_64k(i_CTPM_FW, i_CTPM_FW_len, false); + } else if (i_CTPM_FW_len == FW_SIZE_124k) { + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_124k(i_CTPM_FW, i_CTPM_FW_len, false); + } else if (i_CTPM_FW_len == FW_SIZE_128k) { + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_128k(i_CTPM_FW, i_CTPM_FW_len, false); + } + + if (ret == 0) { + upgrade_times++; + E("%s: TP upgrade error, upgrade_times = %d\n", __func__, upgrade_times); + + if (upgrade_times < 3) { + goto update_retry; + } else { + result = -1; + } /*upgrade fail*/ + } else { + g_core_fp.fp_read_FW_ver(); + g_core_fp.fp_touch_information(); + result = 1;/*upgrade success*/ + I("%s: TP upgrade OK\n", __func__); + } + + kfree(i_CTPM_FW); + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(true, false); +#else + g_core_fp.fp_sense_on(0x00); +#endif + himax_int_enable(1); + return result; +} +#endif + + +static int himax_loadSensorConfig(struct himax_i2c_platform_data *pdata) +{ + I("%s: initialization complete\n", __func__); + return NO_ERR; +} + +#ifdef HX_ESD_RECOVERY +static void himax_esd_hw_reset(void) +{ +#ifdef HX_ZERO_FLASH + int result = 0; +#endif + if (g_ts_dbg != 0) + I("%s: Entering \n", __func__); + + I("START_Himax TP: ESD - Reset\n"); + + if (private_ts->in_self_test == 1) { + I("In self test , not TP: ESD - Reset\n"); + return; + } + + g_core_fp.fp_esd_ic_reset(); +#ifdef HX_ZERO_FLASH + I("It will update fw after esd event in zero flash mode!\n"); + result = g_core_fp.fp_0f_operation_dirly(); + if (result) { + E("Something is wrong! Skip Update with zero flash!\n"); + goto ESCAPE_0F_UPDATE; + } + g_core_fp.fp_reload_disable(0); + g_core_fp.fp_sense_on(0x00); + himax_report_all_leave_event(private_ts); + himax_int_enable(1); +ESCAPE_0F_UPDATE: +#endif + I("END_Himax TP: ESD - Reset\n"); +} +#endif + +#ifdef HX_SMART_WAKEUP +#ifdef HX_GESTURE_TRACK +static void gest_pt_log_coordinate(int rx, int tx) +{ + /*driver report x y with range 0 - 255 , we scale it up to x/y pixel*/ + gest_pt_x[gest_pt_cnt] = rx * (ic_data->HX_X_RES) / 255; + gest_pt_y[gest_pt_cnt] = tx * (ic_data->HX_Y_RES) / 255; +} +#endif +static int himax_wake_event_parse(struct himax_ts_data *ts, int ts_status) +{ + uint8_t *buf; +#ifdef HX_GESTURE_TRACK + int tmp_max_x = 0x00, tmp_min_x = 0xFFFF, tmp_max_y = 0x00, tmp_min_y = 0xFFFF; + int gest_len; +#endif + int i = 0, check_FC = 0; + int j = 0, gesture_pos = 0, gesture_flag = 0; + + if (g_ts_dbg != 0) + I("%s: Entering!, ts_status=%d\n", __func__, ts_status); + + buf = kzalloc(hx_touch_data->event_size * sizeof(uint8_t), GFP_KERNEL); + if (buf == NULL) { + return -ENOMEM; + } + + memcpy(buf, hx_touch_data->hx_event_buf, hx_touch_data->event_size); + + for (i = 0; i < GEST_PTLG_ID_LEN; i++) { + for (j = 0; j < GEST_SUP_NUM; j++) { + if (buf[i] == gest_event[j]) { + gesture_flag = buf[i]; + gesture_pos = j; + break; + } + } + I("0x%2.2X ", buf[i]); + if (buf[i] == gesture_flag) { + check_FC++; + } else { + I("ID START at %x , value = 0x%2X skip the event\n", i, buf[i]); + break; + } + } + + I("Himax gesture_flag= %x\n", gesture_flag); + I("Himax check_FC is %d\n", check_FC); + + if (check_FC != GEST_PTLG_ID_LEN) { + kfree(buf); + return 0; + } + + if (buf[GEST_PTLG_ID_LEN] != GEST_PTLG_HDR_ID1 || + buf[GEST_PTLG_ID_LEN + 1] != GEST_PTLG_HDR_ID2) { + kfree(buf); + return 0; + } + +#ifdef HX_GESTURE_TRACK + + if (buf[GEST_PTLG_ID_LEN] == GEST_PTLG_HDR_ID1 && + buf[GEST_PTLG_ID_LEN + 1] == GEST_PTLG_HDR_ID2) { + gest_len = buf[GEST_PTLG_ID_LEN + 2]; + I("gest_len = %d \n", gest_len); + i = 0; + gest_pt_cnt = 0; + I("gest doornidate start \n %s", __func__); + + while (i < (gest_len + 1) / 2) { + gest_pt_log_coordinate(buf[GEST_PTLG_ID_LEN + 4 + i * 2], buf[GEST_PTLG_ID_LEN + 4 + i * 2 + 1]); + i++; + I("gest_pt_x[%d]=%d \n", gest_pt_cnt, gest_pt_x[gest_pt_cnt]); + I("gest_pt_y[%d]=%d \n", gest_pt_cnt, gest_pt_y[gest_pt_cnt]); + gest_pt_cnt += 1; + } + + if (gest_pt_cnt) { + for (i = 0; i < gest_pt_cnt; i++) { + if (tmp_max_x < gest_pt_x[i]) + tmp_max_x = gest_pt_x[i]; + if (tmp_min_x > gest_pt_x[i]) + tmp_min_x = gest_pt_x[i]; + if (tmp_max_y < gest_pt_y[i]) + tmp_max_y = gest_pt_y[i]; + if (tmp_min_y > gest_pt_y[i]) + tmp_min_y = gest_pt_y[i]; + } + + I("gest_point x_min= %d, x_max= %d, y_min= %d, y_max= %d\n", tmp_min_x, tmp_max_x, tmp_min_y, tmp_max_y); + gest_start_x = gest_pt_x[0]; + hx_gesture_coor[0] = gest_start_x; + gest_start_y = gest_pt_y[0]; + hx_gesture_coor[1] = gest_start_y; + gest_end_x = gest_pt_x[gest_pt_cnt - 1]; + hx_gesture_coor[2] = gest_end_x; + gest_end_y = gest_pt_y[gest_pt_cnt - 1]; + hx_gesture_coor[3] = gest_end_y; + gest_width = tmp_max_x - tmp_min_x; + hx_gesture_coor[4] = gest_width; + gest_height = tmp_max_y - tmp_min_y; + hx_gesture_coor[5] = gest_height; + gest_mid_x = (tmp_max_x + tmp_min_x) / 2; + hx_gesture_coor[6] = gest_mid_x; + gest_mid_y = (tmp_max_y + tmp_min_y) / 2; + hx_gesture_coor[7] = gest_mid_y; + hx_gesture_coor[8] = gest_mid_x;/*gest_up_x*/ + hx_gesture_coor[9] = gest_mid_y - gest_height / 2; /*gest_up_y*/ + hx_gesture_coor[10] = gest_mid_x;/*gest_down_x*/ + hx_gesture_coor[11] = gest_mid_y + gest_height / 2; /*gest_down_y*/ + hx_gesture_coor[12] = gest_mid_x - gest_width / 2; /*gest_left_x*/ + hx_gesture_coor[13] = gest_mid_y; /*gest_left_y*/ + hx_gesture_coor[14] = gest_mid_x + gest_width / 2; /*gest_right_x*/ + hx_gesture_coor[15] = gest_mid_y; /*gest_right_y*/ + } + } + +#endif + + kfree(buf); + + if (!ts->gesture_cust_en[gesture_pos]) { + I("%s NOT report key [%d] = %d \n", __func__, gesture_pos, gest_key_def[gesture_pos]); + g_target_report_data->SMWP_event_chk = 0; + return 0; + } else { + g_target_report_data->SMWP_event_chk = gest_key_def[gesture_pos]; + return gesture_pos; + } +} + +static void himax_wake_event_report(void) +{ + int KEY_EVENT = g_target_report_data->SMWP_event_chk; + + if (g_ts_dbg != 0) + I("%s: Entering! \n", __func__); + + if (KEY_EVENT) { + I(" %s SMART WAKEUP KEY event %d press\n", __func__, KEY_EVENT); + input_report_key(private_ts->input_dev, KEY_EVENT, 1); + input_sync(private_ts->input_dev); + I(" %s SMART WAKEUP KEY event %d release\n", __func__, KEY_EVENT); + input_report_key(private_ts->input_dev, KEY_EVENT, 0); + input_sync(private_ts->input_dev); + FAKE_POWER_KEY_SEND = true; +#ifdef HX_GESTURE_TRACK + I("gest_start_x= %d, gest_start_y= %d, gest_end_x= %d, gest_end_y= %d\n", gest_start_x, gest_start_y, + gest_end_x, gest_end_y); + I("gest_width= %d, gest_height= %d, gest_mid_x= %d, gest_mid_y= %d\n", gest_width, gest_height, + gest_mid_x, gest_mid_y); + I("gest_up_x= %d, gest_up_y= %d, gest_down_x= %d, gest_down_y= %d\n", hx_gesture_coor[8], hx_gesture_coor[9], + hx_gesture_coor[10], hx_gesture_coor[11]); + I("gest_left_x= %d, gest_left_y= %d, gest_right_x= %d, gest_right_y= %d\n", hx_gesture_coor[12], hx_gesture_coor[13], + hx_gesture_coor[14], hx_gesture_coor[15]); +#endif + g_target_report_data->SMWP_event_chk = 0; + } +} + +#endif + +int himax_report_data_init(void) +{ + if (hx_touch_data->hx_coord_buf != NULL) { + kfree(hx_touch_data->hx_coord_buf); + } + + if (hx_touch_data->hx_rawdata_buf != NULL) { + kfree(hx_touch_data->hx_rawdata_buf); + } + +#if defined(HX_SMART_WAKEUP) + hx_touch_data->event_size = g_core_fp.fp_get_touch_data_size(); + + if (hx_touch_data->hx_event_buf != NULL) { + kfree(hx_touch_data->hx_event_buf); + } + +#endif + hx_touch_data->touch_all_size = g_core_fp.fp_get_touch_data_size(); + hx_touch_data->raw_cnt_max = ic_data->HX_MAX_PT / 4; + hx_touch_data->raw_cnt_rmd = ic_data->HX_MAX_PT % 4; + /* more than 4 fingers */ + if (hx_touch_data->raw_cnt_rmd != 0x00) { + hx_touch_data->rawdata_size = g_core_fp.fp_cal_data_len(hx_touch_data->raw_cnt_rmd, ic_data->HX_MAX_PT, hx_touch_data->raw_cnt_max); + hx_touch_data->touch_info_size = (ic_data->HX_MAX_PT + hx_touch_data->raw_cnt_max + 2) * 4; + } else { /* less than 4 fingers */ + hx_touch_data->rawdata_size = g_core_fp.fp_cal_data_len(hx_touch_data->raw_cnt_rmd, ic_data->HX_MAX_PT, hx_touch_data->raw_cnt_max); + hx_touch_data->touch_info_size = (ic_data->HX_MAX_PT + hx_touch_data->raw_cnt_max + 1) * 4; + } + + if ((ic_data->HX_TX_NUM * ic_data->HX_RX_NUM + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM) % hx_touch_data->rawdata_size == 0) { + hx_touch_data->rawdata_frame_size = (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM) / hx_touch_data->rawdata_size; + } else { + hx_touch_data->rawdata_frame_size = (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM) / hx_touch_data->rawdata_size + 1; + } + + I("%s: rawdata_frame_size = %d\n", __func__, hx_touch_data->rawdata_frame_size); + I("%s: ic_data->HX_MAX_PT:%d, hx_raw_cnt_max:%d, hx_raw_cnt_rmd:%d, g_hx_rawdata_size:%d, hx_touch_data->touch_info_size:%d\n", __func__, ic_data->HX_MAX_PT, hx_touch_data->raw_cnt_max, hx_touch_data->raw_cnt_rmd, hx_touch_data->rawdata_size, hx_touch_data->touch_info_size); + hx_touch_data->hx_coord_buf = kzalloc(sizeof(uint8_t) * (hx_touch_data->touch_info_size), GFP_KERNEL); + + if (hx_touch_data->hx_coord_buf == NULL) { + goto mem_alloc_fail; + } + + if (g_target_report_data == NULL) { + g_target_report_data = kzalloc(sizeof(struct himax_target_report_data), GFP_KERNEL); + if (g_target_report_data == NULL) + goto mem_alloc_fail; + g_target_report_data->x = kzalloc(sizeof(int)*(ic_data->HX_MAX_PT), GFP_KERNEL); + if (g_target_report_data->x == NULL) + goto mem_alloc_fail; + g_target_report_data->y = kzalloc(sizeof(int)*(ic_data->HX_MAX_PT), GFP_KERNEL); + if (g_target_report_data->y == NULL) + goto mem_alloc_fail; + g_target_report_data->w = kzalloc(sizeof(int)*(ic_data->HX_MAX_PT), GFP_KERNEL); + if (g_target_report_data->w == NULL) + goto mem_alloc_fail; + g_target_report_data->finger_id = kzalloc(sizeof(int)*(ic_data->HX_MAX_PT), GFP_KERNEL); + if (g_target_report_data->finger_id == NULL) + goto mem_alloc_fail; + } +#ifdef HX_SMART_WAKEUP + g_target_report_data->SMWP_event_chk = 0; +#endif + + hx_touch_data->hx_rawdata_buf = kzalloc(sizeof(uint8_t) * (hx_touch_data->touch_all_size - hx_touch_data->touch_info_size), GFP_KERNEL); + + if (hx_touch_data->hx_rawdata_buf == NULL) { + goto mem_alloc_fail; + } + +#if defined(HX_SMART_WAKEUP) + hx_touch_data->hx_event_buf = kzalloc(sizeof(uint8_t) * (hx_touch_data->event_size), GFP_KERNEL); + + if (hx_touch_data->hx_event_buf == NULL) { + goto mem_alloc_fail; + } + +#endif + return NO_ERR; +mem_alloc_fail: + kfree(g_target_report_data->x); + kfree(g_target_report_data->y); + kfree(g_target_report_data->w); + kfree(g_target_report_data->finger_id); + kfree(g_target_report_data); + g_target_report_data = NULL; + kfree(hx_touch_data->hx_coord_buf); + kfree(hx_touch_data->hx_rawdata_buf); +#if defined(HX_SMART_WAKEUP) + kfree(hx_touch_data->hx_event_buf); +#endif + I("%s: Memory allocate fail!\n", __func__); + return MEM_ALLOC_FAIL; +} + +/*start ts_work*/ +#if defined(HX_USB_DETECT_GLOBAL) +void himax_cable_detect_func(bool force_renew) +{ + struct himax_ts_data *ts; + u32 connect_status = 0; + connect_status = USB_detect_flag;/* upmu_is_chr_det(); */ + ts = private_ts; + + /* I("Touch: cable status=%d, cable_config=%p, usb_connected=%d \n", connect_status, ts->cable_config, ts->usb_connected); */ + if (ts->cable_config) { + if (((!!connect_status) != ts->usb_connected) || force_renew) { + if (!!connect_status) { + ts->cable_config[1] = 0x01; + ts->usb_connected = 0x01; + } else { + ts->cable_config[1] = 0x00; + ts->usb_connected = 0x00; + } + + g_core_fp.fp_usb_detect_set(ts->cable_config); + I("%s: Cable status change: 0x%2.2X\n", __func__, ts->usb_connected); + } + + /* else */ + /* I("%s: Cable status is the same as previous one, ignore.\n", __func__); */ + } +} +#endif + +static int himax_ts_work_status(struct himax_ts_data *ts) +{ + /* 1: normal, 2:SMWP */ + int result = HX_REPORT_COORD; + + hx_touch_data->diag_cmd = ts->diag_cmd; + if (hx_touch_data->diag_cmd) + result = HX_REPORT_COORD_RAWDATA; + +#ifdef HX_SMART_WAKEUP + if (atomic_read(&ts->suspend_mode) && (!FAKE_POWER_KEY_SEND) && (ts->SMWP_enable) && (!hx_touch_data->diag_cmd)) + result = HX_REPORT_SMWP_EVENT; +#endif + /* I("Now Status is %d\n", result); */ + return result; +} + +static int himax_touch_get(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d! \n", __func__, ts_status); + + switch (ts_path) { + /*normal*/ + case HX_REPORT_COORD: + if ((HX_HW_RESET_ACTIVATE) +#ifdef HX_ESD_RECOVERY + || (HX_ESD_RESET_ACTIVATE) +#endif + ) { + if (!g_core_fp.fp_read_event_stack(buf, 128)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + goto END_FUNCTION; + } + break; + } else { + if (!g_core_fp.fp_read_event_stack(buf, hx_touch_data->touch_info_size)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + goto END_FUNCTION; + } + break; + } +#if defined(HX_SMART_WAKEUP) + + /*SMWP*/ + case HX_REPORT_SMWP_EVENT: + g_core_fp.fp_burst_enable(0); + + if (!g_core_fp.fp_read_event_stack(buf, hx_touch_data->event_size)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + goto END_FUNCTION; + } + break; +#endif + case HX_REPORT_COORD_RAWDATA: + if (!g_core_fp.fp_read_event_stack(buf, 128)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + goto END_FUNCTION; + } + break; + default: + break; + } + +END_FUNCTION: + return ts_status; +} + +/* start error_control*/ +static int himax_checksum_cal(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ + uint16_t check_sum_cal = 0; + int32_t i = 0; + int length = 0; + int zero_cnt = 0; + int ret_val = ts_status; + + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d! \n", __func__, ts_status); + + /* Normal */ + switch (ts_path) { + case HX_REPORT_COORD: + length = hx_touch_data->touch_info_size; + break; +#if defined(HX_SMART_WAKEUP) +/* SMWP */ + case HX_REPORT_SMWP_EVENT: + length = (GEST_PTLG_ID_LEN + GEST_PTLG_HDR_LEN); + break; +#endif + case HX_REPORT_COORD_RAWDATA: + length = hx_touch_data->touch_info_size; + break; + default: + I("%s, Neither Normal Nor SMWP error!\n", __func__); + ret_val = HX_PATH_FAIL; + goto END_FUNCTION; + } + + for (i = 0; i < length; i++) { + check_sum_cal += buf[i]; + if (buf[i] == 0x00) + zero_cnt++; + } + + if (check_sum_cal % 0x100 != 0) { + I("[HIMAX TP MSG] checksum fail : check_sum_cal: 0x%02X\n", check_sum_cal); + ret_val = HX_CHKSUM_FAIL; + } else if (zero_cnt == length) { + I("[HIMAX TP MSG] All Zero event\n"); + ret_val = HX_CHKSUM_FAIL; + } + +END_FUNCTION: + if (g_ts_dbg != 0) + I("%s: END, ret_val=%d! \n", __func__, ret_val); + return ret_val; +} + +#ifdef HX_ESD_RECOVERY +#ifdef HX_ZERO_FLASH +void hx_update_dirly_0f(void) +{ + I("It will update fw after esd event in zero flash mode!\n"); + g_core_fp.fp_0f_operation_dirly(); +} +#endif +static int himax_ts_event_check(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ + int hx_EB_event = 0; + int hx_EC_event = 0; + int hx_ED_event = 0; + int hx_esd_event = 0; + int hx_zero_event = 0; + int shaking_ret = 0; + + int32_t loop_i = 0; + int length = 0; + int ret_val = ts_status; + + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d! \n", __func__, ts_status); + + /* Normal */ + switch (ts_path) { + case HX_REPORT_COORD: + length = hx_touch_data->touch_info_size; + break; +#if defined(HX_SMART_WAKEUP) +/* SMWP */ + case HX_REPORT_SMWP_EVENT: + length = (GEST_PTLG_ID_LEN + GEST_PTLG_HDR_LEN); + break; +#endif + case HX_REPORT_COORD_RAWDATA: + length = hx_touch_data->touch_info_size; + break; + default: + I("%s, Neither Normal Nor SMWP error!\n", __func__); + ret_val = HX_PATH_FAIL; + goto END_FUNCTION; + } + + if (g_ts_dbg != 0) + I("Now Path=%d, Now status=%d, length=%d\n", ts_path, ts_status, length); + + for (loop_i = 0; loop_i < length; loop_i++) { + if (ts_path == HX_REPORT_COORD || ts_path == HX_REPORT_COORD_RAWDATA) { + /* case 1 ESD recovery flow */ + if (buf[loop_i] == 0xEB) { + hx_EB_event++; + } else if (buf[loop_i] == 0xEC) { + hx_EC_event++; + } else if (buf[loop_i] == 0xED) { + hx_ED_event++; + } else if (buf[loop_i] == 0x00) { /* case 2 ESD recovery flow-Disable */ + hx_zero_event++; + } else { + hx_EB_event = 0; + hx_EC_event = 0; + hx_ED_event = 0; + hx_zero_event = 0; + g_zero_event_count = 0; + } + } + } + + if (hx_EB_event == length) { + hx_esd_event = length; + hx_EB_event_flag++; + I("[HIMAX TP MSG]: ESD event checked - ALL 0xEB.\n"); + } else if (hx_EC_event == length) { + hx_esd_event = length; + hx_EC_event_flag++; + I("[HIMAX TP MSG]: ESD event checked - ALL 0xEC.\n"); + } else if (hx_ED_event == length) { + hx_esd_event = length; + hx_ED_event_flag++; + I("[HIMAX TP MSG]: ESD event checked - ALL 0xED.\n"); + } +#ifdef HX_ZERO_FLASH + else if (hx_zero_event == length) { + /*check zero flash status*/ + if (g_core_fp.fp_0f_esd_check() < 0) { + g_zero_event_count = 6; + I("[HIMAX TP MSG]: ESD event checked - ALL Zero in ZF.\n"); + } else { + I("[HIMAX TP MSG]: Status check pass in ZF.\n"); + } + } +#endif + else { + hx_esd_event = 0; + } + + if ((hx_esd_event == length || hx_zero_event == length) + && (HX_HW_RESET_ACTIVATE == 0) + && (HX_ESD_RESET_ACTIVATE == 0) + && (hx_touch_data->diag_cmd == 0) + && (ts->in_self_test == 0)) { + shaking_ret = g_core_fp.fp_ic_esd_recovery(hx_esd_event, hx_zero_event, length); + + if (shaking_ret == HX_ESD_EVENT) { + himax_esd_hw_reset(); + ret_val = HX_ESD_EVENT; + } else if (shaking_ret == HX_ZERO_EVENT_COUNT) { + ret_val = HX_ZERO_EVENT_COUNT; + } else { + I("I2C running. Nothing to be done!\n"); + ret_val = HX_IC_RUNNING; + } + } else if (HX_ESD_RESET_ACTIVATE) { /* drop 1st interrupts after chip reset */ + HX_ESD_RESET_ACTIVATE = 0; + I("[HX_ESD_RESET_ACTIVATE]:%s: Back from reset, ready to serve.\n", __func__); + ret_val = HX_ESD_REC_OK; + } + +END_FUNCTION: + if (g_ts_dbg != 0) + I("%s: END, ret_val=%d! \n", __func__, ret_val); + + return ret_val; +} +#endif + +static int himax_err_ctrl(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ +#ifdef HX_RST_PIN_FUNC + if (HX_HW_RESET_ACTIVATE) { + /* drop 1st interrupts after chip reset */ + HX_HW_RESET_ACTIVATE = 0; + I("[HX_HW_RESET_ACTIVATE]:%s: Back from reset, ready to serve.\n", __func__); + ts_status = HX_RST_OK; + goto END_FUNCTION; + } +#endif + + ts_status = himax_checksum_cal(ts, buf, ts_path, ts_status); + if (ts_status == HX_CHKSUM_FAIL) { + goto CHK_FAIL; + } else { +#ifdef HX_ESD_RECOVERY + /* continuous N times revord, not total N times */ + g_zero_event_count = 0; +#endif + goto END_FUNCTION; + } + +CHK_FAIL: +#ifdef HX_ESD_RECOVERY + ts_status = himax_ts_event_check(ts, buf, ts_path, ts_status); +#endif + + +END_FUNCTION: + if (g_ts_dbg != 0) + I("%s: END, ts_status=%d! \n", __func__, ts_status); + return ts_status; +} +/* end error_control*/ + +/* start distribute_data*/ +static int himax_distribute_touch_data(uint8_t *buf, int ts_path, int ts_status) +{ + uint8_t hx_state_info_pos = hx_touch_data->touch_info_size - 3; + + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d! \n", __func__, ts_status); + + if (ts_path == HX_REPORT_COORD) { + memcpy(hx_touch_data->hx_coord_buf, &buf[0], hx_touch_data->touch_info_size); + + if (buf[hx_state_info_pos] != 0xFF && buf[hx_state_info_pos + 1] != 0xFF) { + memcpy(hx_touch_data->hx_state_info, &buf[hx_state_info_pos], 2); + } else { + memset(hx_touch_data->hx_state_info, 0x00, sizeof(hx_touch_data->hx_state_info)); + } + + if ((HX_HW_RESET_ACTIVATE) +#ifdef HX_ESD_RECOVERY + || (HX_ESD_RESET_ACTIVATE) +#endif + ) { + memcpy(hx_touch_data->hx_rawdata_buf, &buf[hx_touch_data->touch_info_size], hx_touch_data->touch_all_size - hx_touch_data->touch_info_size); + } + } else if (ts_path == HX_REPORT_COORD_RAWDATA) { + memcpy(hx_touch_data->hx_coord_buf, &buf[0], hx_touch_data->touch_info_size); + + if (buf[hx_state_info_pos] != 0xFF && buf[hx_state_info_pos + 1] != 0xFF) { + memcpy(hx_touch_data->hx_state_info, &buf[hx_state_info_pos], 2); + } else { + memset(hx_touch_data->hx_state_info, 0x00, sizeof(hx_touch_data->hx_state_info)); + } + + memcpy(hx_touch_data->hx_rawdata_buf, &buf[hx_touch_data->touch_info_size], hx_touch_data->touch_all_size - hx_touch_data->touch_info_size); +#if defined(HX_SMART_WAKEUP) + } else if (ts_path == HX_REPORT_SMWP_EVENT) { + memcpy(hx_touch_data->hx_event_buf, buf, hx_touch_data->event_size); +#endif + } else { + E("%s, Fail Path!\n", __func__); + ts_status = HX_PATH_FAIL; + } + + if (g_ts_dbg != 0) + I("%s: End, ts_status=%d! \n", __func__, ts_status); + return ts_status; +} +/* end assign_data*/ + +/* start parse_report_data*/ +int himax_parse_report_points(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + int x = 0; + int y = 0; + int w = 0; + int base = 0; + int32_t loop_i = 0; + + if (g_ts_dbg != 0) + I("%s: start! \n", __func__); + + + ts->old_finger = ts->pre_finger_mask; + if (ts->hx_point_num == 0) { + D("%s: hx_point_num = 0! \n", __func__); + return ts_status; + } + ts->pre_finger_mask = 0; + hx_touch_data->finger_num = hx_touch_data->hx_coord_buf[ts->coordInfoSize - 4] & 0x0F; + hx_touch_data->finger_on = 1; + AA_press = 1; + + g_target_report_data->finger_num = hx_touch_data->finger_num; + g_target_report_data->finger_on = hx_touch_data->finger_on; + + if (g_ts_dbg != 0) + I("%s:finger_num = 0x%2X, finger_on = %d \n", __func__, g_target_report_data->finger_num, g_target_report_data->finger_on); + + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + base = loop_i * 4; + x = hx_touch_data->hx_coord_buf[base] << 8 | hx_touch_data->hx_coord_buf[base + 1]; + y = (hx_touch_data->hx_coord_buf[base + 2] << 8 | hx_touch_data->hx_coord_buf[base + 3]); + w = hx_touch_data->hx_coord_buf[(ts->nFinger_support * 4) + loop_i]; + + if (g_ts_dbg != 0) + D("%s: now parsing[%d]:x=%d, y=%d, w=%d\n", __func__, loop_i, x, y, w); + + if (x >= 0 && x <= ts->pdata->abs_x_max && y >= 0 && y <= ts->pdata->abs_y_max) { + hx_touch_data->finger_num--; + + g_target_report_data->x[loop_i] = x; + g_target_report_data->y[loop_i] = y; + g_target_report_data->w[loop_i] = w; + g_target_report_data->finger_id[loop_i] = 1; + + /* I("%s: g_target_report_data->x[loop_i]=%d, g_target_report_data->y[loop_i]=%d, g_target_report_data->w[loop_i]=%d", */ + /* __func__, g_target_report_data->x[loop_i], g_target_report_data->y[loop_i], g_target_report_data->w[loop_i]); */ + + + if (!ts->first_pressed) { + ts->first_pressed = 1; + I("S1@%d, %d\n", x, y); + } + + ts->pre_finger_data[loop_i][0] = x; + ts->pre_finger_data[loop_i][1] = y; + + ts->pre_finger_mask = ts->pre_finger_mask + (1 << loop_i); + } else {/* report coordinates */ + g_target_report_data->x[loop_i] = x; + g_target_report_data->y[loop_i] = y; + g_target_report_data->w[loop_i] = w; + g_target_report_data->finger_id[loop_i] = 0; + + if (loop_i == 0 && ts->first_pressed == 1) { + ts->first_pressed = 2; + I("E1@%d, %d\n", ts->pre_finger_data[0][0], ts->pre_finger_data[0][1]); + } + } + } + + if (g_ts_dbg != 0) { + for (loop_i = 0; loop_i < 10; loop_i++) { + D("DBG X=%d Y=%d ID=%d\n", g_target_report_data->x[loop_i], g_target_report_data->y[loop_i], g_target_report_data->finger_id[loop_i]); + } + D("DBG finger number %d\n", g_target_report_data->finger_num); + } + + if (g_ts_dbg != 0) + I("%s: end! \n", __func__); + return ts_status; +} + +static int himax_parse_report_data(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + + if (g_ts_dbg != 0) + I("%s: start now_status=%d! \n", __func__, ts_status); + + + EN_NoiseFilter = (hx_touch_data->hx_coord_buf[HX_TOUCH_INFO_POINT_CNT + 2] >> 3); + /* I("EN_NoiseFilter=%d\n", EN_NoiseFilter); */ + EN_NoiseFilter = EN_NoiseFilter & 0x01; + /* I("EN_NoiseFilter2=%d\n", EN_NoiseFilter); */ +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) + tpd_key = (hx_touch_data->hx_coord_buf[HX_TOUCH_INFO_POINT_CNT + 2] >> 4); + + /* All (VK+AA)leave */ + if (tpd_key == 0x0F) { + tpd_key = 0x00; + } +#endif + p_point_num = ts->hx_point_num; + + if (hx_touch_data->hx_coord_buf[HX_TOUCH_INFO_POINT_CNT] == 0xff) + ts->hx_point_num = 0; + else + ts->hx_point_num = hx_touch_data->hx_coord_buf[HX_TOUCH_INFO_POINT_CNT] & 0x0f; + + switch (ts_path) { + case HX_REPORT_COORD: + ts_status = himax_parse_report_points(ts, ts_path, ts_status); + break; + case HX_REPORT_COORD_RAWDATA: + /* touch monitor rawdata */ + if (debug_data != NULL) { + if (debug_data->fp_set_diag_cmd(ic_data, hx_touch_data)) + I("%s: coordinate dump fail and bypass with checksum err\n", __func__); + } else { + E("%s,There is no init set_diag_cmd\n", __func__); + } + ts_status = himax_parse_report_points(ts, ts_path, ts_status); + break; +#ifdef HX_SMART_WAKEUP + case HX_REPORT_SMWP_EVENT: + himax_wake_event_parse(ts, ts_status); + break; +#endif + default: + E("%s:Fail Path!\n", __func__); + ts_status = HX_PATH_FAIL; + break; + } + if (g_ts_dbg != 0) + I("%s: end now_status=%d! \n", __func__, ts_status); + return ts_status; +} + +/* end parse_report_data*/ + +static void himax_report_all_leave_event(struct himax_ts_data *ts) +{ + int loop_i = 0; + + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { +#ifndef HX_PROTOCOL_A + input_mt_slot(ts->input_dev, loop_i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_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); + input_sync(ts->input_dev); +} + +/* start report_data */ +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) +static void himax_key_report_operation(int tp_key_index, struct himax_ts_data *ts) +{ + uint16_t x_position = 0, y_position = 0; + + if (g_ts_dbg != 0) + I("%s: Entering \n", __func__); + + if (tp_key_index != 0x00) { + I("virtual key index =%x\n", tp_key_index); + + if (tp_key_index == 0x01) { + vk_press = 1; + I("back key pressed\n"); + + if (ts->pdata->virtual_key) { + if (ts->button[0].index) { + x_position = (ts->button[0].x_range_min + ts->button[0].x_range_max) / 2; + y_position = (ts->button[0].y_range_min + ts->button[0].y_range_max) / 2; + } + +#ifdef HX_PROTOCOL_A + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 0); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); + input_mt_sync(ts->input_dev); +#else + input_mt_slot(ts->input_dev, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); +#endif + } else { + input_report_key(ts->input_dev, KEY_BACK, 1); + } + } else if (tp_key_index == 0x02) { + vk_press = 1; + I("home key pressed\n"); + + if (ts->pdata->virtual_key) { + if (ts->button[1].index) { + x_position = (ts->button[1].x_range_min + ts->button[1].x_range_max) / 2; + y_position = (ts->button[1].y_range_min + ts->button[1].y_range_max) / 2; + } + +#ifdef HX_PROTOCOL_A + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 0); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); + input_mt_sync(ts->input_dev); +#else + input_mt_slot(ts->input_dev, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); +#endif + } else { + input_report_key(ts->input_dev, KEY_HOME, 1); + } + } else if (tp_key_index == 0x04) { + vk_press = 1; + I("APP_switch key pressed\n"); + + if (ts->pdata->virtual_key) { + if (ts->button[2].index) { + x_position = (ts->button[2].x_range_min + ts->button[2].x_range_max) / 2; + y_position = (ts->button[2].y_range_min + ts->button[2].y_range_max) / 2; + } + +#ifdef HX_PROTOCOL_A + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); + input_mt_sync(ts->input_dev); +#else + input_mt_slot(ts->input_dev, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); +#endif + } else { + input_report_key(ts->input_dev, KEY_APPSELECT, 1); + } + } + input_sync(ts->input_dev); + } else { /*tp_key_index =0x00*/ + I("virtual key released\n"); + vk_press = 0; +#ifndef HX_PROTOCOL_A + input_mt_slot(ts->input_dev, 0); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); +#else + input_mt_sync(ts->input_dev); +#endif + input_report_key(ts->input_dev, KEY_BACK, 0); + input_report_key(ts->input_dev, KEY_HOME, 0); + input_report_key(ts->input_dev, KEY_APPSELECT, 0); +#ifndef HX_PROTOCOL_A + input_sync(ts->input_dev); +#endif + } +} + +void himax_finger_report_key(struct himax_ts_data *ts) +{ + if (ts->hx_point_num != 0) { + /*Touch KEY*/ + if ((tpd_key_old != 0x00) && (tpd_key == 0x00)) { + /* temp_x[0] = 0xFFFF; + temp_y[0] = 0xFFFF; + temp_x[1] = 0xFFFF; + temp_y[1] = 0xFFFF; */ + hx_touch_data->finger_on = 0; +#ifdef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, hx_touch_data->finger_on); +#endif + himax_key_report_operation(tpd_key, ts); + } + +#ifndef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, hx_touch_data->finger_on); +#endif + input_sync(ts->input_dev); + } +} + +void himax_finger_leave_key(struct himax_ts_data *ts) +{ + if (tpd_key != 0x00) { + hx_touch_data->finger_on = 1; +#ifdef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, hx_touch_data->finger_on); +#endif + himax_key_report_operation(tpd_key, ts); + } else if ((tpd_key_old != 0x00) && (tpd_key == 0x00)) { + hx_touch_data->finger_on = 0; +#ifdef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, hx_touch_data->finger_on); +#endif + himax_key_report_operation(tpd_key, ts); + } + +#ifndef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, hx_touch_data->finger_on); +#endif + input_sync(ts->input_dev); +} + +static void himax_report_key(struct himax_ts_data *ts) +{ + if (ts->hx_point_num != 0) { /* Touch KEY */ + himax_finger_report_key(ts); + } else { /* Key */ + himax_finger_leave_key(ts); + } + + tpd_key_old = tpd_key; + Last_EN_NoiseFilter = EN_NoiseFilter; +} +#endif + +/* start report_point*/ +static void himax_finger_report(struct himax_ts_data *ts) +{ + int i = 0; + bool valid = false; + if (g_ts_dbg != 0) { + I("%s:start\n", __func__); + I("hx_touch_data->finger_num=%d\n", hx_touch_data->finger_num); + } + for (i = 0; i < ts->nFinger_support; i++) { + if (g_target_report_data->x[i] >= 0 && g_target_report_data->x[i] <= ts->pdata->abs_x_max && g_target_report_data->y[i] >= 0 && g_target_report_data->y[i] <= ts->pdata->abs_y_max) + valid = true; + else + valid = false; + if (g_ts_dbg != 0) + I("valid=%d\n", valid); + if (valid) { + if (g_ts_dbg != 0) + I("g_target_report_data->x[i]=%d, g_target_report_data->y[i]=%d, g_target_report_data->w[i]=%d\n", g_target_report_data->x[i], g_target_report_data->y[i], g_target_report_data->w[i]); +#ifndef HX_PROTOCOL_A + input_mt_slot(ts->input_dev, i); +#endif + input_report_key(ts->input_dev, BTN_TOUCH, g_target_report_data->finger_on); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, g_target_report_data->w[i]); +#ifndef HX_PROTOCOL_A + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, g_target_report_data->w[i]); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, g_target_report_data->w[i]); +#else + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, i); +#endif + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, g_target_report_data->x[i]); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, g_target_report_data->y[i]); +#ifndef HX_PROTOCOL_A + ts->last_slot = i; + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); +#else + input_mt_sync(ts->input_dev); +#endif + + } else { + 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_WIDTH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); + } + } + + input_report_key(ts->input_dev, BTN_TOUCH, g_target_report_data->finger_on); + input_sync(ts->input_dev); + + if (g_ts_dbg != 0) + I("%s:end\n", __func__); +} + +static void himax_finger_leave(struct himax_ts_data *ts) +{ + int32_t loop_i = 0; + + if (g_ts_dbg != 0) + I("%s: start! \n", __func__); +#if defined(HX_PALM_REPORT) + if (himax_palm_detect(hx_touch_data->hx_coord_buf) == PALM_REPORT) { + I(" %s HX_PALM_REPORT KEY power event press\n", __func__); + input_report_key(ts->input_dev, KEY_POWER, 1); + input_sync(ts->input_dev); + msleep(100); + + I(" %s HX_PALM_REPORT KEY power event release\n", __func__); + input_report_key(ts->input_dev, KEY_POWER, 0); + input_sync(ts->input_dev); + return; + } +#endif + + hx_touch_data->finger_on = 0; + AA_press = 0; + +#ifdef HX_PROTOCOL_A + input_mt_sync(ts->input_dev); +#endif + + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + if (((ts->pre_finger_mask >> loop_i) & 1) == 1) { + input_mt_slot(ts->input_dev, loop_i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); + } + } + + if (ts->pre_finger_mask > 0) { + ts->pre_finger_mask = 0; + } + + if (ts->first_pressed == 1) { + ts->first_pressed = 2; + I("E1@%d, %d\n", ts->pre_finger_data[0][0], ts->pre_finger_data[0][1]); + } + + /* if (ts->debug_log_level & BIT(1)) */ + /* himax_log_touch_event(x, y, w, loop_i, EN_NoiseFilter, HX_FINGER_LEAVE); */ + + input_report_key(ts->input_dev, BTN_TOUCH, hx_touch_data->finger_on); + input_sync(ts->input_dev); + + if (g_ts_dbg != 0) + I("%s: end! \n", __func__); + return; +} + +static void himax_report_points(struct himax_ts_data *ts) +{ + if (g_ts_dbg != 0) + I("%s: start! \n", __func__); + + if (ts->hx_point_num != 0) { + himax_finger_report(ts); + } else { + himax_finger_leave(ts); + } + + Last_EN_NoiseFilter = EN_NoiseFilter; + + if (g_ts_dbg != 0) + I("%s: end! \n", __func__); +} +/* end report_points*/ + +int himax_report_data(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d! \n", __func__, ts_status); + + if (ts_path == HX_REPORT_COORD || ts_path == HX_REPORT_COORD_RAWDATA) { + /* Touch Point information */ + himax_report_points(ts); + +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) + /*report key(question mark)*/ + if (tpd_key && tpd_key_old) { + himax_report_key(ts); + } +#endif +#ifdef HX_SMART_WAKEUP + } else if (ts_path == HX_REPORT_SMWP_EVENT) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&ts->ts_SMWP_wake_lock, TS_WAKE_LOCK_TIMEOUT); +#else + __pm_wakeup_event(&ts->ts_SMWP_wake_lock, TS_WAKE_LOCK_TIMEOUT); +#endif + himax_wake_event_report(); +#endif + } else { + E("%s:Fail Path!\n", __func__); + ts_status = HX_PATH_FAIL; + } + + if (g_ts_dbg != 0) + I("%s: END, ts_status=%d! \n", __func__, ts_status); + return ts_status; +} +/* end report_data */ + +static int himax_ts_operation(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + uint8_t hw_reset_check[2]; + uint8_t buf[128]; + + memset(buf, 0x00, sizeof(buf)); + memset(hw_reset_check, 0x00, sizeof(hw_reset_check)); + + ts_status = himax_touch_get(ts, buf, ts_path, ts_status); + if (ts_status == HX_TS_GET_DATA_FAIL) + goto END_FUNCTION; + + ts_status = himax_err_ctrl(ts, buf, ts_path, ts_status); + if (ts_status == HX_REPORT_DATA || ts_status == HX_TS_NORMAL_END) { + ts_status = himax_distribute_touch_data(buf, ts_path, ts_status); + ts_status = himax_parse_report_data(ts, ts_path, ts_status); + } else { + goto END_FUNCTION; + } + ts_status = himax_report_data(ts, ts_path, ts_status); + + +END_FUNCTION: + return ts_status; +} + +void himax_ts_work(struct himax_ts_data *ts) +{ + + int ts_status = HX_TS_NORMAL_END; + int ts_path = 0; + + if (debug_data != NULL) + debug_data->fp_ts_dbg_func(ts, HX_FINGER_ON); + +#if defined(HX_USB_DETECT_GLOBAL) + himax_cable_detect_func(false); +#endif + + ts_path = himax_ts_work_status(ts); + switch (ts_path) { + case HX_REPORT_COORD: + ts_status = himax_ts_operation(ts, ts_path, ts_status); + break; + case HX_REPORT_SMWP_EVENT: + ts_status = himax_ts_operation(ts, ts_path, ts_status); + break; + case HX_REPORT_COORD_RAWDATA: + ts_status = himax_ts_operation(ts, ts_path, ts_status); + break; + default: + E("%s:Path Fault! value=%d\n", __func__, ts_path); + goto END_FUNCTION; + break; + } + + if (ts_status == HX_TS_GET_DATA_FAIL) + goto GET_TOUCH_FAIL; + else + goto END_FUNCTION; + +GET_TOUCH_FAIL: + I("%s: Now reset the Touch chip.\n", __func__); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, true); +#endif +END_FUNCTION: + if (debug_data != NULL) + debug_data->fp_ts_dbg_func(ts, HX_FINGER_LEAVE); + + return; +} +/*end ts_work*/ +enum hrtimer_restart himax_ts_timer_func(struct hrtimer *timer) +{ + struct himax_ts_data *ts; + ts = container_of(timer, struct himax_ts_data, timer); + queue_work(ts->himax_wq, &ts->work); + hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +#if defined(HX_USB_DETECT_CALLBACK) +static void himax_cable_tp_status_handler_func(int connect_status) +{ + struct himax_ts_data *ts; + I("Touch: cable change to %d\n", connect_status); + ts = private_ts; + + if (ts->cable_config) { + if (!atomic_read(&ts->suspend_mode)) { + if (connect_status != ts->usb_connected) { + if (connect_status) { + ts->cable_config[1] = 0x01; + ts->usb_connected = 0x01; + } else { + ts->cable_config[1] = 0x00; + ts->usb_connected = 0x00; + } + + himax_bus_master_write(ts->cable_config, + sizeof(ts->cable_config), HIMAX_I2C_RETRY_TIMES); + I("%s: Cable status change: 0x%2.2X\n", __func__, ts->cable_config[1]); + } else { + I("%s: Cable status is the same as previous one, ignore.\n", __func__); + } + } else { + if (connect_status) { + ts->usb_connected = 0x01; + } else { + ts->usb_connected = 0x00; + } + + I("%s: Cable status remembered: 0x%2.2X\n", __func__, ts->usb_connected); + } + } +} + +static struct t_cable_status_notifier himax_cable_status_handler = { + .name = "usb_tp_connected", + .func = himax_cable_tp_status_handler_func, +}; + +#endif + +#ifdef HX_AUTO_UPDATE_FW +static void himax_update_register(struct work_struct *work) +{ + I(" %s in\n", __func__); + + if (i_get_FW() != 0) + return ; + + if (g_auto_update_flag == true) { + I("Update FW Directly"); + goto UPDATE_FW; + } + + if (himax_auto_update_check() != 0) { + return ; + } + +UPDATE_FW: + if (i_update_FW() <= 0) { + I("Auto update FW fail!\n"); + } else { + I("It have Updated\n"); + } +} +#endif + + +#ifdef CONFIG_FB +static void himax_fb_register(struct work_struct *work) +{ + int ret = 0; + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work_att.work); + I(" %s in\n", __func__); + ts->fb_notif.notifier_call = fb_notifier_callback; + ret = fb_register_client(&ts->fb_notif); + + if (ret) { + E(" Unable to register fb_notifier: %d\n", ret); + } +} +#endif + +static const char *himax_ic_type_to_string (void) { + + switch (private_ts->chip_cell_type) { + case CHIP_IS_IN_CELL: + if (IC_HX83102 == ic_data->ic_type_val) { + return "hx83102"; + } else { + return "hx83112a"; + } + default: return "hxxxxxx"; + } +} + +static int himax_fw_updater (struct himax_ts_data *ts, const char *fileName) { + const struct firmware *fw = NULL; + int fw_type = 0; + int result; + + himax_int_enable(0); + + result = request_firmware(&fw, fileName, ts->dev); + if (result < 0) { + I("fail to request_firmware fwpath: %s (ret:%d)\n", fileName, result); + return result; + } + I("%s: FW image: %02X, %02X, %02X, %02X\n", + __func__, fw->data[0], fw->data[1], fw->data[2], fw->data[3]); + + fw_type = (fw->size)/1024; + I("Now FW size is : %dk\n", fw_type); + switch (fw_type) { + case 32: + result = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_32k( + (unsigned char *)fw->data, fw->size, false); + if (result == 0) + E("%s: TP upgrade error, line: %d\n", __func__, __LINE__); + else + I("%s: TP upgrade OK, line: %d\n", __func__, __LINE__); + break; + case 60: + result = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_60k( + (unsigned char *)fw->data, fw->size, false); + if (result == 0) + E("%s: TP upgrade error, line: %d\n", __func__, __LINE__); + else + I("%s: TP upgrade OK, line: %d\n", __func__, __LINE__); + break; + case 64: + result = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_64k( + (unsigned char *)fw->data, fw->size, false); + if (result == 0) + E("%s: TP upgrade error, line: %d\n", __func__, __LINE__); + else + I("%s: TP upgrade OK, line: %d\n", __func__, __LINE__); + break; + case 124: + result = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_124k( + (unsigned char *)fw->data, fw->size, false); + if (result == 0) + E("%s: TP upgrade error, line: %d\n", __func__, __LINE__); + else + I("%s: TP upgrade OK, line: %d\n", __func__, __LINE__); + break; + case 128: + result = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_128k( + (unsigned char *)fw->data, fw->size, false); + if (result == 0) + E("%s: TP upgrade error, line: %d\n", __func__, __LINE__); + else + I("%s: TP upgrade OK, line: %d\n", __func__, __LINE__); + break; + default: + E("%s: Flash command fail: %d\n", __func__, __LINE__); + break; + } + release_firmware(fw); + g_core_fp.fp_read_FW_ver(); + g_core_fp.fp_touch_information(); + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(true, false); +#else + g_core_fp.fp_sense_on(0); +#endif + himax_int_enable(1); + + return (result <= 0) ? -1 : 0; +} + +static ssize_t himax_sysfs_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct himax_ts_data *ts = i2c_get_clientdata(to_i2c_client(dev)); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + ((atomic_read(&ts->suspend_mode) == 1) ? 0 : 1)); +} + +static ssize_t himax_sysfs_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct himax_ts_data *ts = i2c_get_clientdata(to_i2c_client(dev)); + + return scnprintf(buf, PAGE_SIZE, "%d\n", atomic_read(&ts->flash_mode)); +} + +static ssize_t himax_sysfs_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s\n", himax_ic_type_to_string()); +} + +static ssize_t himax_sysfs_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%X-%02X%02X\n", + ic_data->vendor_fw_ver, + ic_data->vendor_touch_cfg_ver, + ic_data->vendor_display_cfg_ver); +} + +static ssize_t himax_sysfs_force_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct himax_ts_data *ts = i2c_get_clientdata(to_i2c_client(dev)); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + ts->force_update = true; + + return count; +} + +static ssize_t himax_sysfs_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + char prefix[HX_FILENAME_SIZE] = "himax"; + char template[HX_FILENAME_SIZE]; + char filename[HX_FILENAME_SIZE]; + struct himax_ts_data *ts = i2c_get_clientdata(to_i2c_client(dev)); + + if (count > HX_FILENAME_SIZE) { + E("%s: FW filename is too long\n", __func__); + retval = -EINVAL; + goto exit; + } + + if (!ts->force_update) { + if (strncmp(buf, prefix, + strnlen(prefix, sizeof(prefix)))) { + E("%s: FW does not belong to Himax\n", __func__); + retval = -EINVAL; + goto exit; + } + + snprintf(template, sizeof(template), "-%s-", + himax_ic_type_to_string()); + if (!strnstr(buf + strnlen(prefix, sizeof(prefix)), template, + count)) { + E("%s: FW does not belong to %s\n", + __func__, + himax_ic_type_to_string()); + retval = -EINVAL; + goto exit; + } + } + + strlcpy(filename, buf, count); + D("%s: FW filename: %s\n", __func__, filename); + + atomic_set(&ts->flash_mode, 1); + retval = himax_fw_updater(ts, filename); + atomic_set(&ts->flash_mode, 0); + if (retval < 0) { + E("%s: Failed to do reflash\n", __func__); + goto exit; + } + + retval = count; + +exit: + ts->force_update = false; + return retval; +} + +static struct device_attribute attrs[] = { + __ATTR(poweron, S_IRUSR | S_IRGRP, + himax_sysfs_poweron_show, + NULL), + __ATTR(productinfo, S_IRUGO, + himax_sysfs_productinfo_show, + NULL), + __ATTR(buildid, S_IRUGO, + himax_sysfs_buildid_show, + NULL), + __ATTR(flashprog, S_IRUSR | S_IRGRP, + himax_sysfs_flashprog_show, + NULL), + __ATTR(doreflash, S_IWUSR | S_IWGRP, + NULL, + himax_sysfs_do_reflash_store), + __ATTR(forcereflash, S_IWUSR | S_IWGRP, + NULL, + himax_sysfs_force_reflash_store), +}; + +#include +#include + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!ts) { + pr_err("cannot get himax_ts_data pointer\n"); + return (ssize_t)0; + } + path = kobject_get_path(&ts->client->dev.kobj, GFP_KERNEL); + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "himax"); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_NULL +}; + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +static int himax_sysfs_touchscreen( + struct himax_ts_data *ts, bool create) +{ + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + static struct class *touchscreen_class; + static struct device *ts_class_dev; + static int minor; + + if (create) { + minor = input_get_new_minor(ts->client->addr, + 1, false); + if (minor < 0) + minor = input_get_new_minor(TSDEV_MINOR_BASE, + TSDEV_MINOR_MAX, true); + pr_info("assigned minor %d\n", minor); + + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + + ts_class_dev = device_create(touchscreen_class, NULL, + MKDEV(INPUT_MAJOR, minor), + ts, himax_ic_type_to_string()); + if (IS_ERR(ts_class_dev)) { + error = PTR_ERR(ts_class_dev); + ts_class_dev = NULL; + return error; + } + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + error = device_create_file(ts_class_dev, &attrs[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + } else { + if (!touchscreen_class || !ts_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(ts_class_dev, &attrs[i]); + + device_unregister(ts_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor)); + ts_class_dev = NULL; + class_unregister(touchscreen_class); + pr_err("error creating touchscreen class\n"); + + return -ENODEV; +} + +int himax_chip_common_init(void) +{ + + int ret = 0, err = 0; + struct himax_ts_data *ts = private_ts; + struct himax_i2c_platform_data *pdata; + int attr_count; + + I("PDATA START\n"); + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + + if (pdata == NULL) { /*Allocate Platform data space*/ + err = -ENOMEM; + goto err_dt_platform_data_fail; + } + + I("ic_data START\n"); + ic_data = kzalloc(sizeof(*ic_data), GFP_KERNEL); + if (ic_data == NULL) { /*Allocate IC data space*/ + err = -ENOMEM; + goto err_dt_ic_data_fail; + } + + /* allocate report data */ + hx_touch_data = kzalloc(sizeof(struct himax_report_data), GFP_KERNEL); + if (hx_touch_data == NULL) { + err = -ENOMEM; + goto err_alloc_touch_data_failed; + } + + if (himax_parse_dt(ts, pdata) < 0) { + I(" pdata is NULL for DT\n"); + goto err_alloc_dt_pdata_failed; + } + +#ifdef HX_RST_PIN_FUNC + ts->rst_gpio = pdata->gpio_reset; +#endif + if (himax_gpio_power_config(pdata) < 0) { + I(" GPIO and power config failed\n"); + goto err_gpio_power_config_failed; + } + +#ifndef CONFIG_OF + + if (pdata->power) { + ret = pdata->power(1); + + if (ret < 0) { + E("%s: power on failed\n", __func__); + goto err_power_failed; + } + } + +#endif + + if (IC_HX83102 == ic_data->ic_type_val) { + I("[%s][%d]:is hx83102_chip_detect\n", __func__, __LINE__); + hx83102_chip_detect(); + } else { + I("[%s][%d]:is hx83112_chip_detect\n", __func__, __LINE__); + hx83112_chip_detect(); + } + + if (g_core_fp.fp_chip_init != NULL) { + g_core_fp.fp_chip_init(); + } else { + E("%s: function point of chip_init is NULL!\n", __func__); + goto error_ic_detect_failed; + } + + if (pdata->virtual_key) { + ts->button = pdata->virtual_key; + } + +#ifdef HX_AUTO_UPDATE_FW + g_auto_update_flag = (!g_core_fp.fp_calculateChecksum(false)); + g_auto_update_flag |= g_core_fp.fp_flash_lastdata_check(); + if (g_auto_update_flag) + goto FW_force_upgrade; +#endif +#ifndef HX_ZERO_FLASH + g_core_fp.fp_read_FW_ver(); +#endif + +#ifdef HX_AUTO_UPDATE_FW +FW_force_upgrade: + ts->himax_update_wq = create_singlethread_workqueue("HMX_update_reuqest"); + if (!ts->himax_update_wq) { + E(" allocate syn_update_wq failed\n"); + err = -ENOMEM; + goto err_update_wq_failed; + } + INIT_DELAYED_WORK(&ts->work_update, himax_update_register); + queue_delayed_work(ts->himax_update_wq, &ts->work_update, msecs_to_jiffies(2000)); +#endif +#ifdef HX_ZERO_FLASH + g_auto_update_flag = true; + ts->himax_0f_update_wq = create_singlethread_workqueue("HMX_0f_update_reuqest"); + INIT_DELAYED_WORK(&ts->work_0f_update, g_core_fp.fp_0f_operation); + queue_delayed_work(ts->himax_0f_update_wq, &ts->work_0f_update, msecs_to_jiffies(2000)); +#endif + + /*Himax Power On and Load Config*/ + if (himax_loadSensorConfig(pdata)) { + E("%s: Load Sesnsor configuration failed, unload driver.\n", __func__); + goto err_detect_failed; + } + + g_core_fp.fp_power_on_init(); + calculate_point_number(); + +#ifdef CONFIG_OF + ts->power = pdata->power; +#endif + ts->pdata = pdata; + ts->x_channel = ic_data->HX_RX_NUM; + ts->y_channel = ic_data->HX_TX_NUM; + ts->nFinger_support = ic_data->HX_MAX_PT; + /*calculate the i2c data size*/ + calcDataSize(ts->nFinger_support); + I("%s: calcDataSize complete\n", __func__); +#ifdef CONFIG_OF + ts->pdata->abs_pressure_min = 0; + ts->pdata->abs_pressure_max = 200; + ts->pdata->abs_width_min = 0; + ts->pdata->abs_width_max = 200; + pdata->cable_config[0] = 0xF0; + pdata->cable_config[1] = 0x00; +#endif + ts->suspended = false; +#if defined(HX_USB_DETECT_CALLBACK) || defined(HX_USB_DETECT_GLOBAL) + ts->usb_connected = 0x00; + ts->cable_config = pdata->cable_config; +#endif +#ifdef HX_PROTOCOL_A + ts->protocol_type = PROTOCOL_TYPE_A; +#else + ts->protocol_type = PROTOCOL_TYPE_B; +#endif + I("%s: Use Protocol Type %c\n", __func__, + ts->protocol_type == PROTOCOL_TYPE_A ? 'A' : 'B'); + ret = himax_input_register(ts); + + if (ret) { + E("%s: Unable to register %s input device\n", + __func__, ts->input_dev->name); + goto err_input_register_device_failed; + } + +#ifdef CONFIG_FB + ts->himax_att_wq = create_singlethread_workqueue("HMX_ATT_reuqest"); + + if (!ts->himax_att_wq) { + E(" allocate syn_att_wq failed\n"); + err = -ENOMEM; + goto err_get_intr_bit_failed; + } + + INIT_DELAYED_WORK(&ts->work_att, himax_fb_register); + queue_delayed_work(ts->himax_att_wq, &ts->work_att, msecs_to_jiffies(15000)); +#endif + +#ifdef HX_SMART_WAKEUP + ts->SMWP_enable = 0; +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&ts->ts_SMWP_wake_lock, WAKE_LOCK_SUSPEND, HIMAX_common_NAME); +#else + wakeup_source_init(&ts->ts_SMWP_wake_lock, HIMAX_common_NAME); +#endif +#endif +#ifdef HX_HIGH_SENSE + ts->HSEN_enable = 0; +#endif + + /*touch data init*/ + err = himax_report_data_init(); + + if (err) { + goto err_report_data_init_failed; + } + + if (himax_common_proc_init()) { + E(" %s: himax_common proc_init failed!\n", __func__); + goto err_creat_proc_file_failed; + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + ret = sysfs_create_file(&ts->client->dev.kobj, + &attrs[attr_count].attr); + if (ret < 0) { + E("%s: Failed to create sysfs attributes\n", __func__); + goto err_sysfs; + } + } + + himax_sysfs_touchscreen(ts, true); + +#if defined(HX_USB_DETECT_CALLBACK) + + if (ts->cable_config) { + cable_detect_register_notifier(&himax_cable_status_handler); + } + +#endif + err = himax_ts_register_interrupt(); + + if (err) { + goto err_register_interrupt_failed; + } + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_DEBUG + if (himax_debug_init()) + E(" %s: debug initial failed!\n", __func__); +#endif + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) + + if (g_auto_update_flag) { + himax_int_enable(0); + } + +#endif + return 0; +err_register_interrupt_failed: + remove_proc_entry(HIMAX_PROC_TOUCH_FOLDER, NULL); +err_sysfs: + attr_count = ARRAY_SIZE(attrs); + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&ts->client->dev.kobj, + &attrs[attr_count].attr); + } +err_creat_proc_file_failed: +err_report_data_init_failed: +#ifdef HX_SMART_WAKEUP +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&ts->ts_SMWP_wake_lock); +#else + wakeup_source_trash(&ts->ts_SMWP_wake_lock); +#endif +#endif +#ifdef CONFIG_FB + cancel_delayed_work_sync(&ts->work_att); + destroy_workqueue(ts->himax_att_wq); +err_get_intr_bit_failed: +#endif +err_input_register_device_failed: + input_free_device(ts->input_dev); +err_detect_failed: +#ifdef HX_AUTO_UPDATE_FW + if (g_auto_update_flag) { + cancel_delayed_work_sync(&ts->work_update); + destroy_workqueue(ts->himax_update_wq); + } +err_update_wq_failed: +#endif +error_ic_detect_failed: + if (gpio_is_valid(pdata->gpio_irq)) { + gpio_free(pdata->gpio_irq); + } +#ifdef HX_RST_PIN_FUNC + if (gpio_is_valid(pdata->gpio_reset)) { + gpio_free(pdata->gpio_reset); + } +#endif +#ifndef CONFIG_OF +err_power_failed: +#endif +err_gpio_power_config_failed: +err_alloc_dt_pdata_failed: + kfree(hx_touch_data); +err_alloc_touch_data_failed: + kfree(ic_data); +err_dt_ic_data_fail: + kfree(pdata); +err_dt_platform_data_fail: + kfree(ts); + probe_fail_flag = 1; + return err; +} + +void himax_chip_common_deinit(void) +{ + struct himax_ts_data *ts = private_ts; + int attr_count = ARRAY_SIZE(attrs); + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_DEBUG + himax_debug_remove(); +#endif + + himax_common_proc_deinit(); + remove_proc_entry(HIMAX_PROC_TOUCH_FOLDER, NULL); + + if (!ts->use_irq) { + hrtimer_cancel(&ts->timer); + destroy_workqueue(ts->himax_wq); + } else { + himax_int_enable(0); + free_irq(private_ts->client->irq, ts); + } + + himax_sysfs_touchscreen(ts, false); + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&ts->client->dev.kobj, + &attrs[attr_count].attr); + } + +#ifdef HX_SMART_WAKEUP +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&ts->ts_SMWP_wake_lock); +#else + wakeup_source_trash(&ts->ts_SMWP_wake_lock); +#endif +#endif +#ifdef CONFIG_FB + if (fb_unregister_client(&ts->fb_notif)) + E("Error occurred while unregistering fb_notifier.\n"); + cancel_delayed_work_sync(&ts->work_att); + destroy_workqueue(ts->himax_att_wq); +#endif + input_free_device(ts->input_dev); +#ifdef HX_ZERO_FLASH + cancel_delayed_work_sync(&ts->work_0f_update); + destroy_workqueue(ts->himax_0f_update_wq); +#endif +#ifdef HX_AUTO_UPDATE_FW + cancel_delayed_work_sync(&ts->work_update); + destroy_workqueue(ts->himax_update_wq); +#endif + if (gpio_is_valid(ts->pdata->gpio_irq)) + gpio_free(ts->pdata->gpio_irq); +#ifdef HX_RST_PIN_FUNC + if (gpio_is_valid(ts->pdata->gpio_reset)) + gpio_free(ts->pdata->gpio_reset); +#endif + + kfree(hx_touch_data); + kfree(ic_data); + kfree(ts->pdata); + kfree(ts); + probe_fail_flag = 0; + + return; +} + +int himax_chip_common_suspend(struct himax_ts_data *ts) +{ + int ret; + + if (ts->suspended) { + I("%s: Already suspended. Skipped. \n", __func__); + return 0; + } else { + ts->suspended = true; + I("%s: enter \n", __func__); + } + + if (debug_data != NULL && debug_data->flash_dump_going == true) { + I("[himax] %s: Flash dump is going, reject suspend\n", __func__); + return 0; + } + +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) +#ifndef HX_RESUME_SEND_CMD + g_core_fp.fp_resend_cmd_func(ts->suspended); +#endif +#endif +#ifdef HX_SMART_WAKEUP + + if (ts->SMWP_enable) { + atomic_set(&ts->suspend_mode, 1); + ts->pre_finger_mask = 0; + FAKE_POWER_KEY_SEND = false; + I("[himax] %s: SMART_WAKEUP enable, reject suspend\n", __func__); + return 0; + } + +#endif + himax_int_enable(0); + himax_report_all_leave_event(ts); + g_core_fp.fp_suspend_ic_action(); + + if (!ts->use_irq) { + ret = cancel_work_sync(&ts->work); + + if (ret) { + himax_int_enable(1); + } + } + + /*ts->first_pressed = 0;*/ + atomic_set(&ts->suspend_mode, 1); + ts->pre_finger_mask = 0; + + if (ts->pdata->powerOff3V3 && ts->pdata->power) { + ts->pdata->power(0); + } + + I("%s: END \n", __func__); + return 0; +} + +int himax_chip_common_resume(struct himax_ts_data *ts) +{ +#if defined(HX_RST_PIN_FUNC) && defined(HX_RESUME_HW_RESET) && defined(HX_ZERO_FLASH) + int result = 0; +#endif + I("%s: enter \n", __func__); + + if (ts->suspended == false) { + I("%s: It had entered resume, skip this step \n", __func__); + return 0; + } else { + ts->suspended = false; + } + +#ifdef HX_ESD_RECOVERY + /* continuous N times record, not total N times. */ + g_zero_event_count = 0; +#endif + + atomic_set(&ts->suspend_mode, 0); + + if (ts->pdata->powerOff3V3 && ts->pdata->power) { + ts->pdata->power(1); + } + +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) + g_core_fp.fp_resend_cmd_func(ts->suspended); +#elif defined(HX_RST_PIN_FUNC) && defined(HX_RESUME_HW_RESET) + g_core_fp.fp_ic_reset(false, false); +#ifdef HX_ZERO_FLASH + I("It will update fw after esd event in zero flash mode!\n"); + result = g_core_fp.fp_0f_operation_dirly(); + if (result) { + E("Something is wrong! Skip Update with zero flash!\n"); + goto ESCAPE_0F_UPDATE; + } + g_core_fp.fp_reload_disable(0); + g_core_fp.fp_sense_on(0x00); +#endif +#endif + himax_report_all_leave_event(ts); + + g_core_fp.fp_resume_ic_action(); + himax_int_enable(1); +#if defined(HX_RST_PIN_FUNC) && defined(HX_RESUME_HW_RESET) && defined(HX_ZERO_FLASH) +ESCAPE_0F_UPDATE: +#endif + I("%s: END \n", __func__); + return 0; +} + diff --git a/drivers/input/touchscreen/himax_mmi/himax_common.h b/drivers/input/touchscreen/himax_mmi/himax_common.h new file mode 100644 index 000000000000..ae34c0cb659f --- /dev/null +++ b/drivers/input/touchscreen/himax_mmi/himax_common.h @@ -0,0 +1,421 @@ +/* Himax Android Driver Sample Code for common functions + + Copyright (C) 2018 Himax Corporation. + + 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. + +*/ + +#ifndef HIMAX_COMMON_H +#define HIMAX_COMMON_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_WAKELOCK +#include +#else +#include +#endif +#include +#include +#include "himax_platform.h" + +#if defined(CONFIG_FB) + #include + #include +#elif defined(CONFIG_HAS_EARLYSUSPEND) + #include +#endif + +#ifdef CONFIG_OF + #include +#endif + +#define HIMAX_DRIVER_VER "1.2.2.50_moto_06" + +#define FLASH_DUMP_FILE "/sdcard/HX_Flash_Dump.bin" + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG) + #define HX_TP_PROC_2T2R + /*#define HX_TP_SELF_TEST_DRIVER*/ /*if enable, selftest works in driver*/ +#endif +/*===========Himax Option function=============*/ +#define HX_RST_PIN_FUNC +#define HX_RESUME_SEND_CMD +#define HX_ESD_RECOVERY +/*#define HX_AUTO_UPDATE_FW*/ +/*#define HX_SMART_WAKEUP*/ +/*#define HX_GESTURE_TRACK*/ +/*#define HX_HIGH_SENSE*/ +/*#define HX_PALM_REPORT*/ +/*#define HX_USB_DETECT_GLOBAL*/ +/*#define HX_USB_DETECT_CALLBACK*/ +/*#define HX_PROTOCOL_A*/ /* for MTK special platform.If turning on,it will report to system by using specific format. */ +/*#define HX_RESUME_HW_RESET*/ +#define HX_PROTOCOL_B_3PA +/*#define HX_FIX_TOUCH_INFO*/ /* if open, you need to change the touch info in the fix_touch_info*/ +/*#define HX_ZERO_FLASH*/ +/*#define CONFIG_CHIP_DTCFG*/ +#define HX_EDGE_LIMIT +/*#undef CONFIG_FB*/ /* Enable it if driver go into suspend/resume twice */ + +/*#define HX_EN_SEL_BUTTON*/ /* Support Self Virtual key ,default is close*/ +/*#define HX_EN_MUT_BUTTON*/ /* Support Mutual Virtual Key ,default is close*/ + +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) + /*#define HX_PLATFOME_DEFINE_KEY*/ /* for specfic platform to set key(button) */ +#endif + +#define HX_KEY_MAX_COUNT 4 +#define DEFAULT_RETRY_CNT 3 + +#define HX_85XX_A_SERIES_PWON "HX85xxA" +#define HX_85XX_B_SERIES_PWON "HX85xxB" +#define HX_85XX_C_SERIES_PWON "HX85xxC" +#define HX_85XX_D_SERIES_PWON "HX85xxD" +#define HX_85XX_E_SERIES_PWON "HX85xxE" +#define HX_85XX_ES_SERIES_PWON "HX85xxES" +#define HX_85XX_F_SERIES_PWON "HX85xxF" +#define HX_85XX_H_SERIES_PWON "HX85xxH" +#define HX_83100A_SERIES_PWON "HX83100A" +#define HX_83102A_SERIES_PWON "HX83102A" +#define HX_83102B_SERIES_PWON "HX83102B" +#define HX_83102D_SERIES_PWON "HX83102D" +#define HX_83103A_SERIES_PWON "HX83103A" +#define HX_83110A_SERIES_PWON "HX83110A" +#define HX_83110B_SERIES_PWON "HX83110B" +#define HX_83111B_SERIES_PWON "HX83111B" +#define HX_83112A_SERIES_PWON "HX83112A" +#define HX_83112B_SERIES_PWON "HX83112B" +#define HX_83112D_SERIES_PWON "HX83112D" +#define HX_83112E_SERIES_PWON "HX83112E" +#define HX_83191A_SERIES_PWON "HX83191A" + +#define HX_TP_BIN_CHECKSUM_SW 1 +#define HX_TP_BIN_CHECKSUM_HW 2 +#define HX_TP_BIN_CHECKSUM_CRC 3 + +#define SHIFTBITS 5 + +#define FW_SIZE_32k 32768 +#define FW_SIZE_60k 61440 +#define FW_SIZE_64k 65536 +#define FW_SIZE_124k 126976 +#define FW_SIZE_128k 131072 + +#define NO_ERR 0 +#define READY_TO_SERVE 1 +#define WORK_OUT 2 +#define I2C_FAIL -1 +#define MEM_ALLOC_FAIL -2 +#define CHECKSUM_FAIL -3 +#define GESTURE_DETECT_FAIL -4 +#define INPUT_REGISTER_FAIL -5 +#define FW_NOT_READY -6 +#define LENGTH_FAIL -7 +#define OPEN_FILE_FAIL -8 +#define ERR_WORK_OUT -10 +#define ERR_STS_WRONG -11 +#define HW_CRC_FAIL 1 + +#define HX_FINGER_ON 1 +#define HX_FINGER_LEAVE 2 + +#define HX_FILENAME_SIZE 80 + +#if defined(HX_PALM_REPORT) +#define PALM_REPORT 1 +#define NOT_REPORT -1 +#endif +#define IC_HX83102 102 + +enum HX_TS_PATH { + HX_REPORT_COORD = 1, + HX_REPORT_SMWP_EVENT, + HX_REPORT_COORD_RAWDATA, +}; + +enum HX_TS_STATUS { + HX_TS_GET_DATA_FAIL = -4, + HX_ESD_EVENT, + HX_CHKSUM_FAIL, + HX_PATH_FAIL, + HX_TS_NORMAL_END = 0, + HX_ESD_REC_OK, + HX_READY_SERVE, + HX_REPORT_DATA, + HX_ESD_WARNING, + HX_IC_RUNNING, + HX_ZERO_EVENT_COUNT, + HX_RST_OK, +}; + +enum cell_type { + CHIP_IS_ON_CELL, + CHIP_IS_IN_CELL +}; +#ifdef HX_FIX_TOUCH_INFO +enum fix_touch_info { + FIX_HX_RX_NUM = 36, + FIX_HX_TX_NUM = 18, + FIX_HX_BT_NUM = 0, + FIX_HX_X_RES = 720, + FIX_HX_Y_RES = 1520, + FIX_HX_MAX_PT = 10, + FIX_HX_XY_REVERSE = true, + FIX_HX_INT_IS_EDGE = true, +#ifdef HX_TP_PROC_2T2R + FIX_HX_RX_NUM_2 = 0, + FIX_HX_TX_NUM_2 = 0, +#endif +}; +#endif + +#ifdef HX_ZERO_FLASH + #define HX_SPI_OPERATION + #define HX_0F_DEBUG +#endif +struct himax_ic_data { + int vendor_fw_ver; + int vendor_config_ver; + int vendor_touch_cfg_ver; + int vendor_display_cfg_ver; + int vendor_cid_maj_ver; + int vendor_cid_min_ver; + int vendor_panel_ver; + int vendor_sensor_id; + int HX_RX_NUM; + int HX_TX_NUM; + int HX_BT_NUM; + int HX_X_RES; + int HX_Y_RES; + int HX_MAX_PT; + bool HX_XY_REVERSE; + bool HX_INT_IS_EDGE; +#ifdef HX_TP_PROC_2T2R + int HX_RX_NUM_2; + int HX_TX_NUM_2; +#endif + int ic_type_val; +}; + +struct himax_virtual_key { + int index; + int keycode; + int x_range_min; + int x_range_max; + int y_range_min; + int y_range_max; +}; + +struct himax_target_report_data { + int *x; + int *y; + int *w; + int *finger_id; + int finger_on; + int finger_num; +#ifdef HX_PLATFORM_DEFINE_KEY + int key_size; + int *key_x; + int *key_y; + int *key_w; +#endif +#ifdef HX_SMART_WAKEUP + int SMWP_event_chk; +#endif +}; + +struct himax_report_data { + int touch_all_size; + int raw_cnt_max; + int raw_cnt_rmd; + int touch_info_size; + uint8_t finger_num; + uint8_t finger_on; + uint8_t *hx_coord_buf; + uint8_t hx_state_info[2]; +#if defined(HX_SMART_WAKEUP) + int event_size; + uint8_t *hx_event_buf; +#endif + + int rawdata_size; + uint8_t diag_cmd; + uint8_t *hx_rawdata_buf; + uint8_t rawdata_frame_size; +}; + +struct himax_ts_data { + bool suspended; + bool force_update; + atomic_t suspend_mode; + atomic_t flash_mode; + uint8_t x_channel; + uint8_t y_channel; + uint8_t useScreenRes; + uint8_t diag_cmd; + char chip_name[30]; + uint8_t chip_cell_type; + + uint8_t protocol_type; + uint8_t first_pressed; + uint8_t coord_data_size; + uint8_t area_data_size; + uint8_t coordInfoSize; + uint8_t raw_data_frame_size; + uint8_t raw_data_nframes; + uint8_t nFinger_support; + uint8_t irq_enabled; + uint8_t diag_self[50]; + + uint16_t finger_pressed; + uint16_t last_slot; + uint16_t pre_finger_mask; + uint16_t old_finger; + int hx_point_num; + + + uint32_t debug_log_level; + uint32_t widthFactor; + uint32_t heightFactor; + uint32_t tw_x_min; + uint32_t tw_x_max; + uint32_t tw_y_min; + uint32_t tw_y_max; + uint32_t pl_x_min; + uint32_t pl_x_max; + uint32_t pl_y_min; + uint32_t pl_y_max; + + int rst_gpio; + int use_irq; + int (*power)(int on); + int pre_finger_data[10][2]; + + struct device *dev; + struct workqueue_struct *himax_wq; + struct work_struct work; + struct input_dev *input_dev; + struct hrtimer timer; + struct i2c_client *client; + struct himax_i2c_platform_data *pdata; + struct himax_virtual_key *button; + struct mutex rw_lock; + +/******* SPI-start *******/ + struct mutex spi_lock; + struct spi_device *spi; + int hx_irq; +/******* SPI-end *******/ + + int in_self_test; + +#if defined(CONFIG_FB) + struct notifier_block fb_notif; + struct workqueue_struct *himax_att_wq; + struct delayed_work work_att; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif + + struct workqueue_struct *flash_wq; + struct work_struct flash_work; + +#ifdef HX_AUTO_UPDATE_FW + struct workqueue_struct *himax_update_wq; + struct delayed_work work_update; +#endif + +#ifdef HX_ZERO_FLASH + struct workqueue_struct *himax_0f_update_wq; + struct delayed_work work_0f_update; +#endif + + struct workqueue_struct *himax_diag_wq; + struct delayed_work himax_diag_delay_wrok; + +#ifdef HX_SMART_WAKEUP + uint8_t SMWP_enable; + uint8_t gesture_cust_en[26]; +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock ts_SMWP_wake_lock; +#else + struct wakeup_source ts_SMWP_wake_lock; +#endif +#endif + +#ifdef HX_EDGE_LIMIT + uint8_t edge_limit_enable; + uint8_t edge_limit_en[8]; +#endif + +#ifdef HX_HIGH_SENSE + uint8_t HSEN_enable; +#endif + +#if defined(HX_USB_DETECT_CALLBACK) || defined(HX_USB_DETECT_GLOBAL) + uint8_t usb_connected; + uint8_t *cable_config; +#endif + + +}; + +struct himax_debug { + bool flash_dump_going; + void (*fp_ts_dbg_func)(struct himax_ts_data *ts, int start); + int (*fp_set_diag_cmd)(struct himax_ic_data *ic_data, struct himax_report_data *hx_touch_data); +}; + +enum input_protocol_type { + PROTOCOL_TYPE_A = 0x00, + PROTOCOL_TYPE_B = 0x01, +}; + +#ifdef HX_HIGH_SENSE + void himax_set_HSEN_func(uint8_t HSEN_enable); +#endif + +#ifdef HX_SMART_WAKEUP +void himax_set_SMWP_func(uint8_t SMWP_enable); + +#define GEST_PTLG_ID_LEN (4) +#define GEST_PTLG_HDR_LEN (4) +#define GEST_PTLG_HDR_ID1 (0xCC) +#define GEST_PTLG_HDR_ID2 (0x44) +#define GEST_PT_MAX_NUM (128) +#endif + +extern int irq_enable_count; + +/*void himax_HW_reset(uint8_t loadconfig,uint8_t int_off);*/ + +extern int himax_chip_common_suspend(struct himax_ts_data *ts); +extern int himax_chip_common_resume(struct himax_ts_data *ts); + +#endif + diff --git a/drivers/input/touchscreen/himax_mmi/himax_debug.c b/drivers/input/touchscreen/himax_mmi/himax_debug.c new file mode 100644 index 000000000000..c7cd300b6841 --- /dev/null +++ b/drivers/input/touchscreen/himax_mmi/himax_debug.c @@ -0,0 +1,2720 @@ +/* Himax Android Driver Sample Code for debug nodes + + Copyright (C) 2018 Himax Corporation. + + 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. + +*/ + +#include "himax_debug.h" +#include "himax_ic_core.h" + +extern struct himax_ic_data *ic_data; +extern struct himax_ts_data *private_ts; +extern struct himax_core_fp g_core_fp; +extern struct himax_debug *debug_data; +extern unsigned char IC_CHECKSUM; +extern int i2c_error_count; +extern struct proc_dir_entry *himax_touch_proc_dir; + +extern int himax_input_register(struct himax_ts_data *ts); +#ifdef HX_TP_PROC_2T2R + extern bool Is_2T2R; +#endif + +#ifdef HX_RST_PIN_FUNC + extern void himax_ic_reset(uint8_t loadconfig, uint8_t int_off); +#endif + +#ifdef HX_TP_PROC_2T2R + bool Is_2T2R = false; + int HX_RX_NUM_2 = 0; + int HX_TX_NUM_2 = 0; +#endif + +#if defined(HX_ZERO_FLASH) +extern char *i_CTPM_firmware_name; +#endif + +uint8_t g_diag_arr_num = 0; + +extern uint8_t HX_PROC_SEND_FLAG; + +int g_max_mutual = 0; +int g_min_mutual = 0xFFFF; +int g_max_self = 0; +int g_min_self = 0xFFFF; + + +struct timespec timeStart, timeEnd, timeDelta; +int g_switch_mode = 0; +/* ============================================================================================================= + + Segment : Himax PROC Debug Function + +============================================================================================================= */ +static ssize_t himax_CRC_test_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + ssize_t ret = 0; + uint8_t result = 0; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + g_core_fp.fp_sense_off(); + msleep(20); + result = g_core_fp.fp_calculateChecksum(false); + g_core_fp.fp_sense_on(0x01); + + if (result) { + ret += snprintf(temp_buf + ret, len - ret, "CRC test is Pass! \n"); + } else { + ret += snprintf(temp_buf + ret, len - ret, "CRC test is Fail! \n"); + } + + if (copy_to_user(buf, temp_buf, len)) { + I("%s,here:%d\n", __func__, __LINE__); + } + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static struct file_operations himax_proc_CRC_test_ops = { + .owner = THIS_MODULE, + .read = himax_CRC_test_read, +}; + +static ssize_t himax_vendor_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + ssize_t ret = 0; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + ret += snprintf(temp_buf + ret, len - ret, "FW_VER = 0x%2.2X \n", ic_data->vendor_fw_ver); + + if (private_ts->chip_cell_type == CHIP_IS_ON_CELL) { + ret += snprintf(temp_buf + ret, len - ret, "CONFIG_VER = 0x%2.2X \n", ic_data->vendor_config_ver); + } else { + ret += snprintf(temp_buf + ret, len - ret, "TOUCH_VER = 0x%2.2X \n", ic_data->vendor_touch_cfg_ver); + ret += snprintf(temp_buf + ret, len - ret, "DISPLAY_VER = 0x%2.2X \n", ic_data->vendor_display_cfg_ver); + } + + if (ic_data->vendor_cid_maj_ver < 0 && ic_data->vendor_cid_min_ver < 0) { + ret += snprintf(temp_buf + ret, len - ret, "CID_VER = NULL\n"); + } else { + ret += snprintf(temp_buf + ret, len - ret, "CID_VER = 0x%2.2X \n", (ic_data->vendor_cid_maj_ver << 8 | ic_data->vendor_cid_min_ver)); + } + + if (ic_data->vendor_panel_ver < 0) { + ret += snprintf(temp_buf + ret, len - ret, "PANEL_VER = NULL\n"); + } else { + ret += snprintf(temp_buf + ret, len - ret, "PANEL_VER = 0x%2.2X \n", ic_data->vendor_panel_ver); + } + + ret += snprintf(temp_buf + ret, len - ret, "\n"); + ret += snprintf(temp_buf + ret, len - ret, "Himax Touch Driver Version:\n"); + ret += snprintf(temp_buf + ret, len - ret, "%s \n", HIMAX_DRIVER_VER); + HX_PROC_SEND_FLAG = 1; + + if (copy_to_user(buf, temp_buf, len)) { + I("%s,here:%d\n", __func__, __LINE__); + } + + kfree(temp_buf); + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static struct file_operations himax_proc_vendor_ops = { + .owner = THIS_MODULE, + .read = himax_vendor_read, +}; + +static ssize_t himax_attn_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + ssize_t ret = 0; + struct himax_ts_data *ts_data; + char *temp_buf; + ts_data = private_ts; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + ret += snprintf(temp_buf + ret, len - ret, "attn = %x\n", himax_int_gpio_read(ts_data->pdata->gpio_irq)); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + + +static struct file_operations himax_proc_attn_ops = { + .owner = THIS_MODULE, + .read = himax_attn_read, +}; + +static ssize_t himax_int_en_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + size_t ret = 0; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + ret += snprintf(temp_buf + ret, len - ret, "%d ", ts->irq_enabled); + ret += snprintf(temp_buf + ret, len - ret, "\n"); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static ssize_t himax_int_en_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + char buf_tmp[12] = {0}; + int value, ret = 0; + + if (len >= 12) { + I("%s: no command exceeds 12 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf_tmp, buff, len)) { + return -EFAULT; + } + + if (buf_tmp[0] == '0') { + value = false; + } else if (buf_tmp[0] == '1') { + value = true; + } else { + return -EINVAL; + } + if (value) { + ret = himax_int_en_set(); + + if (ret == 0) { + ts->irq_enabled = 1; + irq_enable_count = 1; + } + } else { + himax_int_enable(0); + free_irq(ts->client->irq, ts); + ts->irq_enabled = 0; + } + + return len; +} + +static struct file_operations himax_proc_int_en_ops = { + .owner = THIS_MODULE, + .read = himax_int_en_read, + .write = himax_int_en_write, +}; + +static ssize_t himax_layout_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + size_t ret = 0; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + ret += snprintf(temp_buf + ret, len - ret, "%d ", ts->pdata->abs_x_min); + ret += snprintf(temp_buf + ret, len - ret, "%d ", ts->pdata->abs_x_max); + ret += snprintf(temp_buf + ret, len - ret, "%d ", ts->pdata->abs_y_min); + ret += snprintf(temp_buf + ret, len - ret, "%d ", ts->pdata->abs_y_max); + ret += snprintf(temp_buf + ret, len - ret, "\n"); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static ssize_t himax_layout_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + char buf_tmp[5]; + int i = 0, j = 0, k = 0, ret; + unsigned long value; + int layout[4] = {0}; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) { + return -EFAULT; + } + + for (i = 0; i < 20; i++) { + if (buf[i] == ',' || buf[i] == '\n') { + memset(buf_tmp, 0x0, sizeof(buf_tmp)); + + if (i - j <= 5) { + memcpy(buf_tmp, buf + j, i - j); + } else { + I("buffer size is over 5 char\n"); + return len; + } + + j = i + 1; + + if (k < 4) { + ret = kstrtoul(buf_tmp, 10, &value); + layout[k++] = value; + } + } + } + + if (k == 4) { + ts->pdata->abs_x_min = layout[0]; + ts->pdata->abs_x_max = layout[1]; + ts->pdata->abs_y_min = layout[2]; + ts->pdata->abs_y_max = layout[3]; + I("%d, %d, %d, %d\n", + ts->pdata->abs_x_min, ts->pdata->abs_x_max, ts->pdata->abs_y_min, ts->pdata->abs_y_max); + input_unregister_device(ts->input_dev); + himax_input_register(ts); + } else { + I("ERR@%d, %d, %d, %d\n", + ts->pdata->abs_x_min, ts->pdata->abs_x_max, ts->pdata->abs_y_min, ts->pdata->abs_y_max); + } + + return len; +} + +static struct file_operations himax_proc_layout_ops = { + .owner = THIS_MODULE, + .read = himax_layout_read, + .write = himax_layout_write, +}; + +static ssize_t himax_debug_level_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts_data; + size_t ret = 0; + char *temp_buf; + ts_data = private_ts; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + ret += snprintf(temp_buf + ret, len - ret, "%d\n", ts_data->debug_log_level); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static ssize_t himax_debug_level_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts; + char buf_tmp[11]; + int i; + ts = private_ts; + + if (len >= 12) { + I("%s: no command exceeds 12 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf_tmp, buff, len)) { + return -EFAULT; + } + + ts->debug_log_level = 0; + + for (i = 0; i < len - 1; i++) { + if (buf_tmp[i] >= '0' && buf_tmp[i] <= '9') + ts->debug_log_level |= (buf_tmp[i] - '0'); + else if (buf_tmp[i] >= 'A' && buf_tmp[i] <= 'F') + ts->debug_log_level |= (buf_tmp[i] - 'A' + 10); + else if (buf_tmp[i] >= 'a' && buf_tmp[i] <= 'f') + ts->debug_log_level |= (buf_tmp[i] - 'a' + 10); + + if (i != len - 2) + ts->debug_log_level <<= 4; + } + + if (ts->debug_log_level & BIT(3)) { + if (ts->pdata->screenWidth > 0 && ts->pdata->screenHeight > 0 && + (ts->pdata->abs_x_max - ts->pdata->abs_x_min) > 0 && + (ts->pdata->abs_y_max - ts->pdata->abs_y_min) > 0) { + ts->widthFactor = (ts->pdata->screenWidth << SHIFTBITS) / (ts->pdata->abs_x_max - ts->pdata->abs_x_min); + ts->heightFactor = (ts->pdata->screenHeight << SHIFTBITS) / (ts->pdata->abs_y_max - ts->pdata->abs_y_min); + + if (ts->widthFactor > 0 && ts->heightFactor > 0) { + ts->useScreenRes = 1; + } else { + ts->heightFactor = 0; + ts->widthFactor = 0; + ts->useScreenRes = 0; + } + } else { + I("Enable finger debug with raw position mode!\n"); + } + } else { + ts->useScreenRes = 0; + ts->widthFactor = 0; + ts->heightFactor = 0; + } + + return len; +} + +static struct file_operations himax_proc_debug_level_ops = { + .owner = THIS_MODULE, + .read = himax_debug_level_read, + .write = himax_debug_level_write, +}; + +static ssize_t himax_proc_register_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + int ret = 0; + uint16_t loop_i; + uint8_t data[128]; + char *temp_buf; + memset(data, 0x00, sizeof(data)); + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + I("himax_register_show: %02X,%02X,%02X,%02X\n", register_command[3], register_command[2], register_command[1], register_command[0]); + g_core_fp.fp_register_read(register_command, 128, data, cfg_flag); + ret += snprintf(temp_buf + ret, len - ret, "command: %02X,%02X,%02X,%02X\n", register_command[3], register_command[2], register_command[1], register_command[0]); + + for (loop_i = 0; loop_i < 128; loop_i++) { + ret += snprintf(temp_buf + ret, len - ret, "0x%2.2X ", data[loop_i]); + if ((loop_i % 16) == 15) + ret += snprintf(temp_buf + ret, len - ret, "\n"); + } + + ret += snprintf(temp_buf + ret, len - ret, "\n"); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static ssize_t himax_proc_register_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + char buf[80] = {0}; + char buf_tmp[16]; + uint8_t length = 0; + unsigned long result = 0; + uint8_t loop_i = 0; + uint16_t base = 2; + char *data_str = NULL; + uint8_t w_data[20]; + uint8_t x_pos[20]; + uint8_t count = 0; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) { + return -EFAULT; + } + + memset(buf_tmp, 0x0, sizeof(buf_tmp)); + memset(w_data, 0x0, sizeof(w_data)); + memset(x_pos, 0x0, sizeof(x_pos)); + memset(register_command, 0x0, sizeof(register_command)); + + I("himax %s \n", buf); + + if ((buf[0] == 'r' || buf[0] == 'w') && buf[1] == ':' && buf[2] == 'x') { + length = strlen(buf); + + /* I("%s: length = %d.\n", __func__,length); */ + for (loop_i = 0; loop_i < length; loop_i++) { /* find postion of 'x' */ + if (buf[loop_i] == 'x') { + x_pos[count] = loop_i; + count++; + } + } + + data_str = strrchr(buf, 'x'); + I("%s: %s.\n", __func__, data_str); + length = strlen(data_str + 1) - 1; + + if (buf[0] == 'r') { + if (buf[3] == 'F' && buf[4] == 'E' && length == 4) { + length = length - base; + cfg_flag = 1; + memcpy(buf_tmp, data_str + base + 1, length); + } else { + cfg_flag = 0; + memcpy(buf_tmp, data_str + 1, length); + } + + byte_length = length / 2; + + if (!kstrtoul(buf_tmp, 16, &result)) { + for (loop_i = 0 ; loop_i < byte_length ; loop_i++) { + register_command[loop_i] = (uint8_t)(result >> loop_i * 8); + } + } + + if (strcmp(HX_85XX_H_SERIES_PWON, private_ts->chip_name) == 0 && cfg_flag == 0) + cfg_flag = 2; + } else if (buf[0] == 'w') { + if (buf[3] == 'F' && buf[4] == 'E') { + cfg_flag = 1; + memcpy(buf_tmp, buf + base + 3, length); + } else { + cfg_flag = 0; + memcpy(buf_tmp, buf + 3, length); + } + + if (count < 3) { + byte_length = length / 2; + + if (!kstrtoul(buf_tmp, 16, &result)) { /* command */ + for (loop_i = 0 ; loop_i < byte_length ; loop_i++) { + register_command[loop_i] = (uint8_t)(result >> loop_i * 8); + } + } + + if (!kstrtoul(data_str + 1, 16, &result)) { /* data */ + for (loop_i = 0 ; loop_i < byte_length ; loop_i++) { + w_data[loop_i] = (uint8_t)(result >> loop_i * 8); + } + } + + g_core_fp.fp_register_write(register_command, byte_length, w_data, cfg_flag); + } else { + for (loop_i = 0; loop_i < count; loop_i++) { /* parsing addr after 'x' */ + memset(buf_tmp, 0x0, sizeof(buf_tmp)); + if (cfg_flag != 0 && loop_i != 0) + byte_length = 2; + else + byte_length = x_pos[1] - x_pos[0] - 2; /* original */ + + memcpy(buf_tmp, buf + x_pos[loop_i] + 1, byte_length); + + /* I("%s: buf_tmp = %s\n", __func__,buf_tmp); */ + if (!kstrtoul(buf_tmp, 16, &result)) { + if (loop_i == 0) { + register_command[loop_i] = (uint8_t)(result); + /* I("%s: register_command = %X\n", __func__,register_command[0]); */ + } else { + w_data[loop_i - 1] = (uint8_t)(result); + /* I("%s: w_data[%d] = %2X\n", __func__,loop_i - 1,w_data[loop_i - 1]); */ + } + } + } + + byte_length = count - 1; + if (strcmp(HX_85XX_H_SERIES_PWON, private_ts->chip_name) == 0 && cfg_flag == 0) + cfg_flag = 2; + g_core_fp.fp_register_write(register_command, byte_length, &w_data[0], cfg_flag); + } + } else { + return len; + } + } + + return len; +} + +static struct file_operations himax_proc_register_ops = { + .owner = THIS_MODULE, + .read = himax_proc_register_read, + .write = himax_proc_register_write, +}; + +int32_t *getMutualBuffer(void) +{ + return diag_mutual; +} +int32_t *getMutualNewBuffer(void) +{ + return diag_mutual_new; +} +int32_t *getMutualOldBuffer(void) +{ + return diag_mutual_old; +} +int32_t *getSelfBuffer(void) +{ + return &diag_self[0]; +} +int32_t *getSelfNewBuffer(void) +{ + return &diag_self_new[0]; +} +int32_t *getSelfOldBuffer(void) +{ + return &diag_self_old[0]; +} +uint8_t getXChannel(void) +{ + return x_channel; +} +uint8_t getYChannel(void) +{ + return y_channel; +} +void setXChannel(uint8_t x) +{ + x_channel = x; +} +void setYChannel(uint8_t y) +{ + y_channel = y; +} +void setMutualBuffer(void) +{ + diag_mutual = kzalloc(x_channel * y_channel * sizeof(int32_t), GFP_KERNEL); +} +void setMutualNewBuffer(void) +{ + diag_mutual_new = kzalloc(x_channel * y_channel * sizeof(int32_t), GFP_KERNEL); +} +void setMutualOldBuffer(void) +{ + diag_mutual_old = kzalloc(x_channel * y_channel * sizeof(int32_t), GFP_KERNEL); +} + +#ifdef HX_TP_PROC_2T2R +int32_t *getMutualBuffer_2(void) +{ + return diag_mutual_2; +} +uint8_t getXChannel_2(void) +{ + return x_channel_2; +} +uint8_t getYChannel_2(void) +{ + return y_channel_2; +} +void setXChannel_2(uint8_t x) +{ + x_channel_2 = x; +} +void setYChannel_2(uint8_t y) +{ + y_channel_2 = y; +} +void setMutualBuffer_2(void) +{ + diag_mutual_2 = kzalloc(x_channel_2 * y_channel_2 * sizeof(int32_t), GFP_KERNEL); +} +#endif + +int himax_set_diag_cmd(struct himax_ic_data *ic_data, struct himax_report_data *hx_touch_data) +{ + struct himax_ts_data *ts = private_ts; + int32_t *mutual_data; + int32_t *self_data; + int mul_num; + int self_num; + /* int RawDataLen = 0; */ + hx_touch_data->diag_cmd = ts->diag_cmd; + + if (hx_touch_data->diag_cmd >= 1 && hx_touch_data->diag_cmd <= 7) { + /* Check event stack CRC */ + if (!g_core_fp.fp_diag_check_sum(hx_touch_data)) { + goto bypass_checksum_failed_packet; + } + +#ifdef HX_TP_PROC_2T2R + if (Is_2T2R && (hx_touch_data->diag_cmd >= 4 && hx_touch_data->diag_cmd <= 6)) { + mutual_data = getMutualBuffer_2(); + self_data = getSelfBuffer(); + /* initiallize the block number of mutual and self */ + mul_num = getXChannel_2() * getYChannel_2(); +#ifdef HX_EN_SEL_BUTTON + self_num = getXChannel_2() + getYChannel_2() + ic_data->HX_BT_NUM; +#else + self_num = getXChannel_2() + getYChannel_2(); +#endif + } else +#endif + { + mutual_data = getMutualBuffer(); + self_data = getSelfBuffer(); + /* initiallize the block number of mutual and self */ + mul_num = getXChannel() * getYChannel(); +#ifdef HX_EN_SEL_BUTTON + self_num = getXChannel() + getYChannel() + ic_data->HX_BT_NUM; +#else + self_num = getXChannel() + getYChannel(); +#endif + } + g_core_fp.fp_diag_parse_raw_data(hx_touch_data, mul_num, self_num, hx_touch_data->diag_cmd, mutual_data, self_data); + } else if (hx_touch_data->diag_cmd == 8) { + memset(diag_coor, 0x00, sizeof(diag_coor)); + memcpy(&(diag_coor[0]), &hx_touch_data->hx_coord_buf[0], hx_touch_data->touch_info_size); + } + + /* assign state info data */ + memcpy(&(hx_state_info[0]), &hx_touch_data->hx_state_info[0], 2); + return NO_ERR; +bypass_checksum_failed_packet: + return 1; +} + +/* #if defined(HX_DEBUG_LEVEL) */ +extern struct himax_target_report_data *g_target_report_data; +extern struct himax_report_data *hx_touch_data; +void himax_log_touch_data(int start) +{ + int loop_i = 0; + int print_size = 0; + uint8_t *buf; + + if (start == 1) + return; /* report data when end of ts_work*/ + + if (hx_touch_data->diag_cmd == 0) { + print_size = hx_touch_data->touch_info_size; + buf = kzalloc(hx_touch_data->touch_info_size*sizeof(uint8_t), GFP_KERNEL); + memcpy(buf, hx_touch_data->hx_coord_buf, hx_touch_data->touch_info_size); + } else if (hx_touch_data->diag_cmd > 0) { + print_size = hx_touch_data->touch_all_size; + buf = kzalloc(hx_touch_data->touch_info_size*sizeof(uint8_t), GFP_KERNEL); + memcpy(buf, hx_touch_data->hx_coord_buf, hx_touch_data->touch_info_size); + memcpy(&buf[hx_touch_data->touch_info_size], hx_touch_data->hx_rawdata_buf, hx_touch_data->touch_all_size - hx_touch_data->touch_info_size); + } else { + E("%s:cmd fault\n", __func__); + } + + for (loop_i = 0; loop_i < print_size; loop_i += 8) { + if ((loop_i + 7) >= print_size) { + I("%s: over flow\n", __func__); + break; + } + + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", loop_i, buf[loop_i], loop_i + 1, buf[loop_i + 1]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", loop_i + 2, buf[loop_i + 2], loop_i + 3, buf[loop_i + 3]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", loop_i + 4, buf[loop_i + 4], loop_i + 5, buf[loop_i + 5]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", loop_i + 6, buf[loop_i + 6], loop_i + 7, buf[loop_i + 7]); + I("\n"); + } + kfree(buf); +} +void himax_log_touch_event(struct himax_ts_data *ts, int start) +{ + int loop_i = 0; + if (g_target_report_data->finger_on > 0 && g_target_report_data->finger_num > 0) { + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + if (g_target_report_data->x[loop_i] >= 0 && g_target_report_data->x[loop_i] <= ts->pdata->abs_x_max && g_target_report_data->y[loop_i] >= 0 && g_target_report_data->y[loop_i] <= ts->pdata->abs_y_max) { + I("Finger %d=> X:%d, Y:%d W:%d, Z:%d, F:%d\n", loop_i + 1, + g_target_report_data->x[loop_i], + g_target_report_data->y[loop_i], + g_target_report_data->w[loop_i], + g_target_report_data->w[loop_i], + loop_i + 1); + } + } + } else if (g_target_report_data->finger_on == 0 && g_target_report_data->finger_num == 0) { + I("All Finger leave\n"); + } else { + I("%s : wrong input!\n", __func__); + } +} +void himax_log_touch_int_devation(int touched) +{ + if (touched == HX_FINGER_ON) { + getnstimeofday(&timeStart); + /* I(" Irq start time = %ld.%06ld s\n", + timeStart.tv_sec, timeStart.tv_nsec/1000); */ + } else if (touched == HX_FINGER_LEAVE) { + getnstimeofday(&timeEnd); + timeDelta.tv_nsec = (timeEnd.tv_sec * 1000000000 + timeEnd.tv_nsec) - (timeStart.tv_sec * 1000000000 + timeStart.tv_nsec); + /* I("Irq finish time = %ld.%06ld s\n", + timeEnd.tv_sec, timeEnd.tv_nsec/1000);*/ + I("Touch latency = %ld us\n", timeDelta.tv_nsec / 1000); + } else { + I("%s : wrong input!\n", __func__); + } +} +void himax_log_touch_event_detail(struct himax_ts_data *ts, int start) +{ + int loop_i = 0; + + if (start == HX_FINGER_LEAVE) { + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + if (((ts->old_finger >> loop_i & 1) == 0) && ((ts->pre_finger_mask >> loop_i & 1) == 1)) { + if (g_target_report_data->x[loop_i] >= 0 && g_target_report_data->x[loop_i] <= ts->pdata->abs_x_max && g_target_report_data->y[loop_i] >= 0 && g_target_report_data->y[loop_i] <= ts->pdata->abs_y_max) { + I("status: Raw:F:%02d Down, X:%d, Y:%d, W:%d\n", loop_i + 1, g_target_report_data->x[loop_i], g_target_report_data->y[loop_i], g_target_report_data->w[loop_i]); + } + } else if ((((ts->old_finger >> loop_i & 1) == 1) && ((ts->pre_finger_mask >> loop_i & 1) == 0))) { + I("status: Raw:F:%02d Up, X:%d, Y:%d\n", loop_i + 1, ts->pre_finger_data[loop_i][0], ts->pre_finger_data[loop_i][1]); + } else { + /* I("dbg hx_point_num=%d,old_finger=0x%02X,pre_finger_mask=0x%02X\n",ts->hx_point_num,ts->old_finger,ts->pre_finger_mask);*/ + } + } + } +} + +void himax_ts_dbg_func(struct himax_ts_data *ts, int start) +{ + switch (ts->debug_log_level) { + case 1: + himax_log_touch_data(start); + break; + case 2: + himax_log_touch_event(ts, start); + break; + case 4: + himax_log_touch_int_devation(start); + break; + case 8: + himax_log_touch_event_detail(ts, start); + break; + } +} + +/* #endif */ +static ssize_t himax_diag_arrange_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) { + return -EFAULT; + } + + g_diag_arr_num = buf[0] - '0'; + I("%s: g_diag_arr_num = %d \n", __func__, g_diag_arr_num); + return len; +} + +void himax_get_mutual_edge(void) +{ + int i = 0; + + for (i = 0; i < (x_channel * y_channel); i++) { + if (diag_mutual[i] > g_max_mutual) + g_max_mutual = diag_mutual[i]; + + if (diag_mutual[i] < g_min_mutual) + g_min_mutual = diag_mutual[i]; + } +} + +void himax_get_self_edge(void) +{ + int i = 0; + + for (i = 0; i < (x_channel + y_channel); i++) { + if (diag_self[i] > g_max_self) + g_max_self = diag_self[i]; + + if (diag_self[i] < g_min_self) + g_min_self = diag_self[i]; + } +} + +/* print first step which is row */ +static struct file_operations himax_proc_diag_arrange_ops = { + .owner = THIS_MODULE, + .write = himax_diag_arrange_write, +}; +static void print_state_info(struct seq_file *s) +{ + /* seq_printf(s, "State_info_2bytes:%3d, %3d\n",hx_state_info[0],hx_state_info[1]); */ + seq_printf(s, "ReCal = %d\t", hx_state_info[0] & 0x01); + seq_printf(s, "Palm = %d\t", hx_state_info[0] >> 1 & 0x01); + seq_printf(s, "AC mode = %d\t", hx_state_info[0] >> 2 & 0x01); + seq_printf(s, "Water = %d\n", hx_state_info[0] >> 3 & 0x01); + seq_printf(s, "Glove = %d\t", hx_state_info[0] >> 4 & 0x01); + seq_printf(s, "TX Hop = %d\t", hx_state_info[0] >> 5 & 0x01); + seq_printf(s, "Base Line = %d\t", hx_state_info[0] >> 6 & 0x01); + seq_printf(s, "OSR Hop = %d\t", hx_state_info[1] >> 3 & 0x01); + seq_printf(s, "KEY = %d\n", hx_state_info[1] >> 4 & 0x0F); +} + +static void himax_diag_arrange_print(struct seq_file *s, int i, int j, int transpose) +{ + if (transpose) + seq_printf(s, "%6d", diag_mutual[j + i * x_channel]); + else + seq_printf(s, "%6d", diag_mutual[i + j * x_channel]); +} + +/* ready to print second step which is column*/ +static void himax_diag_arrange_inloop(struct seq_file *s, int in_init, int out_init, bool transpose, int j) +{ + int i; + int in_max = 0; + + if (transpose) + in_max = y_channel; + else + in_max = x_channel; + + if (in_init > 0) { /* bit0 = 1 */ + for (i = in_init - 1; i >= 0; i--) { + himax_diag_arrange_print(s, i, j, transpose); + } + + if (transpose) { + if (out_init > 0) + seq_printf(s, " %5d\n", diag_self[j]); + else + seq_printf(s, " %5d\n", diag_self[x_channel - j - 1]); + } + } else { /* bit0 = 0 */ + for (i = 0; i < in_max; i++) { + himax_diag_arrange_print(s, i, j, transpose); + } + + if (transpose) { + if (out_init > 0) + seq_printf(s, " %5d\n", diag_self[x_channel - j - 1]); + else + seq_printf(s, " %5d\n", diag_self[j]); + } + } +} + +/* print first step which is row */ +static void himax_diag_arrange_outloop(struct seq_file *s, int transpose, int out_init, int in_init) +{ + int j; + int out_max = 0; + int self_cnt = 0; + + if (transpose) + out_max = x_channel; + else + out_max = y_channel; + + if (out_init > 0) { /* bit1 = 1 */ + self_cnt = 1; + + for (j = out_init - 1; j >= 0; j--) { + seq_printf(s, "%3c%02d%c", '[', j + 1, ']'); + himax_diag_arrange_inloop(s, in_init, out_init, transpose, j); + + if (!transpose) { + seq_printf(s, " %5d\n", diag_self[y_channel + x_channel - self_cnt]); + self_cnt++; + } + } + } else { /* bit1 = 0 */ + /* self_cnt = x_channel; */ + for (j = 0; j < out_max; j++) { + seq_printf(s, "%3c%02d%c", '[', j + 1, ']'); + himax_diag_arrange_inloop(s, in_init, out_init, transpose, j); + + if (!transpose) { + seq_printf(s, " %5d\n", diag_self[j + x_channel]); + } + } + } +} + +/* determin the output format of diag */ +static void himax_diag_arrange(struct seq_file *s) +{ + int bit2, bit1, bit0; + int i; + /* rotate bit */ + bit2 = g_diag_arr_num >> 2; + /* reverse Y */ + bit1 = g_diag_arr_num >> 1 & 0x1; + /* reverse X */ + bit0 = g_diag_arr_num & 0x1; + + if (g_diag_arr_num < 4) { + for (i = 0 ; i <= x_channel; i++) + seq_printf(s, "%3c%02d%c", '[', i, ']'); + + seq_printf(s, "\n"); + himax_diag_arrange_outloop(s, bit2, bit1 * y_channel, bit0 * x_channel); + seq_printf(s, "%6c", ' '); + + if (bit0 == 1) { + for (i = x_channel - 1; i >= 0; i--) + seq_printf(s, "%6d", diag_self[i]); + } else { + for (i = 0; i < x_channel; i++) + seq_printf(s, "%6d", diag_self[i]); + } + } else { + for (i = 0 ; i <= y_channel; i++) + seq_printf(s, "%3c%02d%c", '[', i, ']'); + + seq_printf(s, "\n"); + himax_diag_arrange_outloop(s, bit2, bit1 * x_channel, bit0 * y_channel); + seq_printf(s, "%6c", ' '); + + if (bit1 == 1) { + for (i = x_channel + y_channel - 1; i >= x_channel; i--) { + seq_printf(s, "%6d", diag_self[i]); + } + } else { + for (i = x_channel; i < x_channel + y_channel; i++) { + seq_printf(s, "%6d", diag_self[i]); + } + } + } +} + +static void *himax_diag_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) { + return NULL; + } + return (void *)((unsigned long) *pos + 1); +} + +static void *himax_diag_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_diag_seq_stop(struct seq_file *s, void *v) +{ +} + +static int himax_diag_seq_read(struct seq_file *s, void *v) +{ + struct himax_ts_data *ts = private_ts; + size_t ret = 0; + uint32_t loop_i; + uint16_t mutual_num, self_num, width; + int dsram_type = 0; + dsram_type = ts->diag_cmd / 10; + +#ifdef HX_TP_PROC_2T2R + if (Is_2T2R && (ts->diag_cmd >= 4 && ts->diag_cmd <= 6)) { + mutual_num = x_channel_2 * y_channel_2; + self_num = x_channel_2 + y_channel_2; /* don't add KEY_COUNT */ + width = x_channel_2; + seq_printf(s, "ChannelStart: %4d, %4d\n\n", x_channel_2, y_channel_2); + } else +#endif + { + mutual_num = x_channel * y_channel; + self_num = x_channel + y_channel; /* don't add KEY_COUNT */ + width = x_channel; + seq_printf(s, "ChannelStart: %4d, %4d\n\n", x_channel, y_channel); + } + + /* start to show out the raw data in adb shell */ + if ((ts->diag_cmd >= 1 && ts->diag_cmd <= 3) || (ts->diag_cmd == 7)) { + himax_diag_arrange(s); + seq_printf(s, "\n"); +#ifdef HX_EN_SEL_BUTTON + seq_printf(s, "\n"); + + for (loop_i = 0; loop_i < ic_data->HX_BT_NUM; loop_i++) + seq_printf(s, "%6d", diag_self[ic_data->HX_RX_NUM + ic_data->HX_TX_NUM + loop_i]); + +#endif + seq_printf(s, "ChannelEnd"); + seq_printf(s, "\n"); +#ifdef HX_TP_PROC_2T2R + } else if (Is_2T2R && ts->diag_cmd >= 4 && ts->diag_cmd <= 6) { + for (loop_i = 0; loop_i < mutual_num; loop_i++) { + seq_printf(s, "%4d", diag_mutual_2[loop_i]); + + if ((loop_i % width) == (width - 1)) + seq_printf(s, " %4d\n", diag_self[width + loop_i / width]); + } + + seq_printf(s, "\n"); + + for (loop_i = 0; loop_i < width; loop_i++) { + seq_printf(s, "%4d", diag_self[loop_i]); + + if (((loop_i) % width) == (width - 1)) + seq_printf(s, "\n"); + } + +#ifdef HX_EN_SEL_BUTTON + seq_printf(s, "\n"); + + for (loop_i = 0; loop_i < ic_data->HX_BT_NUM; loop_i++) + seq_printf(s, "%4d", diag_self[ic_data->HX_RX_NUM_2 + ic_data->HX_TX_NUM_2 + loop_i]); + +#endif + seq_printf(s, "ChannelEnd"); + seq_printf(s, "\n"); +#endif + } else if (ts->diag_cmd == 8) { + for (loop_i = 0; loop_i < 128 ; loop_i++) { + if ((loop_i % 16) == 0) + seq_printf(s, "LineStart:"); + + seq_printf(s, "%4x", diag_coor[loop_i]); + + if ((loop_i % 16) == 15) + seq_printf(s, "\n"); + } + } else if (dsram_type > 0 && dsram_type <= 8) { + himax_diag_arrange(s); + seq_printf(s, "\n ChannelEnd"); + seq_printf(s, "\n"); + } + + if ((ts->diag_cmd >= 1 && ts->diag_cmd <= 7) || dsram_type > 0) { + /* print Mutual/Slef Maximum and Minimum */ + himax_get_mutual_edge(); + himax_get_self_edge(); + seq_printf(s, "Mutual Max:%3d, Min:%3d\n", g_max_mutual, g_min_mutual); + seq_printf(s, "Self Max:%3d, Min:%3d\n", g_max_self, g_min_self); + /* recovery status after print*/ + g_max_mutual = 0; + g_min_mutual = 0xFFFF; + g_max_self = 0; + g_min_self = 0xFFFF; + } + + /*pring state info*/ + print_state_info(s); + return ret; +} +static struct seq_operations himax_diag_seq_ops = { + .start = himax_diag_seq_start, + .next = himax_diag_seq_next, + .stop = himax_diag_seq_stop, + .show = himax_diag_seq_read, +}; +static int himax_diag_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_diag_seq_ops); +}; +bool DSRAM_Flag = false; + +/* DSRAM thread */ +bool himax_ts_diag_func(void) +{ + struct himax_ts_data *ts = private_ts; + int i = 0, j = 0; + unsigned int index = 0; + int total_size = (y_channel * x_channel + y_channel + x_channel) * 2; + uint8_t *info_data; + int32_t *mutual_data; + int32_t *mutual_data_new; + int32_t *mutual_data_old; + int32_t *self_data; + int32_t *self_data_new; + int32_t *self_data_old; + int32_t new_data; + /* 1:common dsram,2:100 frame Max,3:N-(N-1)frame */ + int dsram_type = 0; + char temp_buf[20]; + char write_buf[total_size * 3]; + + mutual_data = NULL; + mutual_data_new = NULL; + mutual_data_old = NULL; + self_data = NULL; + self_data_new = NULL; + self_data_old = NULL; + + info_data = kzalloc(total_size * sizeof(uint8_t), GFP_KERNEL); + if (info_data == NULL) + return false; + + memset(write_buf, '\0', sizeof(write_buf)); + memset(info_data, 0, total_size * sizeof(uint8_t)); + dsram_type = ts->diag_cmd / 10; + I("%s:Entering ts->diag_cmd=%d!\n", __func__, ts->diag_cmd); + + if (dsram_type == 8) { + dsram_type = 1; + I("%s Sorting Mode run sram type1 ! \n", __func__); + } + + g_core_fp.fp_burst_enable(1); + + if (dsram_type == 1 || dsram_type == 2 || dsram_type == 4) { + mutual_data = getMutualBuffer(); + self_data = getSelfBuffer(); + } else if (dsram_type == 3) { + mutual_data = getMutualBuffer(); + mutual_data_new = getMutualNewBuffer(); + mutual_data_old = getMutualOldBuffer(); + self_data = getSelfBuffer(); + self_data_new = getSelfNewBuffer(); + self_data_old = getSelfOldBuffer(); + } + + if (g_core_fp.fp_get_DSRAM_data(info_data, DSRAM_Flag) == false) + return false; + + index = 0; + + for (i = 0; i < y_channel; i++) { /*mutual data*/ + for (j = 0; j < x_channel; j++) { + new_data = (((int8_t)info_data[index + 1] << 8) | info_data[index]); + + if (dsram_type == 1 || dsram_type == 4) { + mutual_data[i * x_channel + j] = new_data; + } else if (dsram_type == 2) { /* Keep max data */ + if (mutual_data[i * x_channel + j] < new_data) + mutual_data[i * x_channel + j] = new_data; + } else if (dsram_type == 3) { /* Cal data for [N]-[N-1] frame */ + mutual_data_new[i * x_channel + j] = new_data; + mutual_data[i * x_channel + j] = mutual_data_new[i * x_channel + j] - mutual_data_old[i * x_channel + j]; + } + index += 2; + } + } + + for (i = 0; i < x_channel + y_channel; i++) { /*self data*/ + new_data = (info_data[index + 1] << 8 | info_data[index]); + if (dsram_type == 1 || dsram_type == 4) { + self_data[i] = new_data; + } else if (dsram_type == 2) { /* Keep max data */ + if (self_data[i] < new_data) + self_data[i] = new_data; + } else if (dsram_type == 3) { /* Cal data for [N]-[N-1] frame */ + self_data_new[i] = new_data; + self_data[i] = self_data_new[i] - self_data_old[i]; + } + index += 2; + } + + kfree(info_data); + + if (dsram_type == 3) { + memcpy(mutual_data_old, mutual_data_new, x_channel * y_channel * sizeof(int32_t)); /* copy N data to N-1 array */ + memcpy(self_data_old, self_data_new, (x_channel + y_channel) * sizeof(int32_t)); /* copy N data to N-1 array */ + } + + diag_max_cnt++; + + if (dsram_type >= 1 && dsram_type <= 3) { + queue_delayed_work(private_ts->himax_diag_wq, &private_ts->himax_diag_delay_wrok, 1 / 10 * HZ); + } else if (dsram_type == 4) { + for (i = 0; i < x_channel * y_channel; i++) { + memset(temp_buf, '\0', sizeof(temp_buf)); + + if (i == (x_channel * y_channel - 1)) { + snprintf(temp_buf, sizeof(temp_buf), "%4d\t", mutual_data[i]); + snprintf(temp_buf, sizeof(temp_buf), "%4d\n", self_data[x_channel + y_channel - 1]); + I("%s :i = %d 3\n", __func__, i); + } else if (i % x_channel == (x_channel - 1)) { + snprintf(temp_buf, sizeof(temp_buf), "%4d\t", mutual_data[i]); + snprintf(temp_buf, sizeof(temp_buf), "%4d\n", self_data[x_channel + (i / x_channel) + 1]); + } else { + snprintf(temp_buf, sizeof(temp_buf), "%4d\t", mutual_data[i]); + } + strlcat(&write_buf[i*strlen(temp_buf)], temp_buf, strlen(temp_buf)); + } + + for (i = 0; i < x_channel; i++) { + memset(temp_buf, '\0', sizeof(temp_buf)); + if (i == x_channel - 1) + snprintf(temp_buf, sizeof(temp_buf), "%4d\n", self_data[i]); + else + snprintf(temp_buf, sizeof(temp_buf), "%4d\t", self_data[i]); + strlcat(&write_buf[(i+x_channel * y_channel)*strlen(temp_buf)], temp_buf, strlen(temp_buf)); + } + + /* save raw data in file */ + if (!IS_ERR(diag_sram_fn)) { + I("%s create file and ready to write\n", __func__); + diag_sram_fn->f_op->write(diag_sram_fn, write_buf, sizeof(write_buf), &diag_sram_fn->f_pos); + write_counter++; + + if (write_counter < write_max_count) { + queue_delayed_work(private_ts->himax_diag_wq, &private_ts->himax_diag_delay_wrok, 1 / 10 * HZ); + } else { + filp_close(diag_sram_fn, NULL); + write_counter = 0; + } + } + } + return true; +} + +static ssize_t himax_diag_write(struct file *filp, const char __user *buff, size_t len, loff_t *data) +{ + struct himax_ts_data *ts = private_ts; + char messages[80] = {0}; + /*struct filename *vts_name;*/ + uint8_t command[2] = {0x00, 0x00}; + uint8_t receive[1]; + /* 0: common , other: dsram*/ + int storage_type = 0; + /* 1:IIR,2:DC,3:Bank,4:IIR2,5:IIR2_N,6:FIR2,7:Baseline,8:dump coord */ + int rawdata_type = 0; + memset(receive, 0x00, sizeof(receive)); + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(messages, buff, len)) { + return -EFAULT; + } + + I("%s:g_switch_mode = %d\n", __func__, g_switch_mode); + + if (messages[1] == 0x0A) { + ts->diag_cmd = messages[0] - '0'; + } else { + ts->diag_cmd = (messages[0] - '0') * 10 + (messages[1] - '0'); + } + + storage_type = g_core_fp.fp_determin_diag_storage(ts->diag_cmd); + rawdata_type = g_core_fp.fp_determin_diag_rawdata(ts->diag_cmd); + + if (ts->diag_cmd > 0 && rawdata_type == 0) { + I("[Himax]ts->diag_cmd=0x%x ,storage_type=%d, rawdata_type=%d! Maybe no support!\n" + , ts->diag_cmd, storage_type, rawdata_type); + ts->diag_cmd = 0x00; + } else { + I("[Himax]ts->diag_cmd=0x%x ,storage_type=%d, rawdata_type=%d\n", ts->diag_cmd, storage_type, rawdata_type); + } + + memset(diag_mutual, 0x00, x_channel * y_channel * sizeof(int32_t)); /* Set data 0 */ + memset(diag_self, 0x00, sizeof(diag_self)); + if (storage_type == 0 && rawdata_type > 0 && rawdata_type < 8) { + I("%s,common\n", __func__); + + if (DSRAM_Flag) { + /* 1. Clear DSRAM flag */ + DSRAM_Flag = false; + /* 2. Stop DSRAM thread */ + cancel_delayed_work(&private_ts->himax_diag_delay_wrok); + /* 3. Enable ISR */ + himax_int_enable(1); + /*(4) FW leave sram and return to event stack*/ + g_core_fp.fp_return_event_stack(); + } + + if (g_switch_mode == 2) { + g_core_fp.fp_idle_mode(0); + g_switch_mode = g_core_fp.fp_switch_mode(0); + } + + if (ts->diag_cmd == 0x04) { +#if defined(HX_TP_PROC_2T2R) + command[0] = ts->diag_cmd; +#else + ts->diag_cmd = 0x00; + command[0] = 0x00; +#endif + } else { + command[0] = ts->diag_cmd; + } + + g_core_fp.fp_diag_register_set(command[0], storage_type); + } else if (storage_type > 0 && storage_type < 8 && rawdata_type > 0 && rawdata_type < 8) { + I("%s,dsram\n", __func__); + diag_max_cnt = 0; + + /* 0. set diag flag */ + if (DSRAM_Flag) { + /* (1) Clear DSRAM flag */ + DSRAM_Flag = false; + /* (2) Stop DSRAM thread */ + cancel_delayed_work(&private_ts->himax_diag_delay_wrok); + /* (3) Enable ISR */ + himax_int_enable(1); + /*(4) FW leave sram and return to event stack*/ + g_core_fp.fp_return_event_stack(); + } + + /* close sorting if turn on*/ + if (g_switch_mode == 2) { + g_core_fp.fp_idle_mode(0); + g_switch_mode = g_core_fp.fp_switch_mode(0); + } + + command[0] = rawdata_type;/* ts->diag_cmd; */ + g_core_fp.fp_diag_register_set(command[0], storage_type); + /* 1. Disable ISR */ + himax_int_enable(0); +#if 0 + /* Open file for save raw data log */ + if (storage_type == 4) { + switch (rawdata_type) { + case 1: + vts_name = getname_kernel(IIR_DUMP_FILE); + diag_sram_fn = file_open_name(vts_name, O_CREAT | O_WRONLY, 0); + break; + + case 2: + vts_name = getname_kernel(DC_DUMP_FILE); + diag_sram_fn = file_open_name(vts_name, O_CREAT | O_WRONLY, 0); + break; + + case 3: + vts_name = getname_kernel(BANK_DUMP_FILE); + diag_sram_fn = file_open_name(vts_name, O_CREAT | O_WRONLY, 0); + break; + + default: + I("%s raw data type is not true. raw data type is %d \n", __func__, rawdata_type); + } + } +#endif + /* 2. Start DSRAM thread */ + queue_delayed_work(private_ts->himax_diag_wq, &private_ts->himax_diag_delay_wrok, 2 * HZ / 100); + I("%s: Start get raw data in DSRAM\n", __func__); + + if (storage_type == 4) + msleep(6000); + + /* 3. Set DSRAM flag */ + DSRAM_Flag = true; + } else if (storage_type == 8) { + I("Soritng mode!\n"); + + if (DSRAM_Flag) { + /* 1. Clear DSRAM flag */ + DSRAM_Flag = false; + /* 2. Stop DSRAM thread */ + cancel_delayed_work(&private_ts->himax_diag_delay_wrok); + /* 3. Enable ISR */ + himax_int_enable(1); + /*(4) FW leave sram and return to event stack*/ + g_core_fp.fp_return_event_stack(); + } + + g_core_fp.fp_idle_mode(1); + g_switch_mode = g_core_fp.fp_switch_mode(1); + + if (g_switch_mode == 2) { + g_core_fp.fp_diag_register_set(command[0], storage_type); + } + + queue_delayed_work(private_ts->himax_diag_wq, &private_ts->himax_diag_delay_wrok, 2 * HZ / 100); + DSRAM_Flag = true; + } else { + /* set diag flag */ + if (DSRAM_Flag) { + I("return and cancel sram thread!\n"); + /* (1) Clear DSRAM flag */ + DSRAM_Flag = false; + /* (2) Stop DSRAM thread */ + cancel_delayed_work(&private_ts->himax_diag_delay_wrok); + /* (3) Enable ISR */ + himax_int_enable(1); + /*(4) FW leave sram and return to event stack*/ + g_core_fp.fp_return_event_stack(); + } + + if (g_switch_mode == 2) { + g_core_fp.fp_idle_mode(0); + g_switch_mode = g_core_fp.fp_switch_mode(0); + } + + if (ts->diag_cmd != 0x00) { + E("[Himax]ts->diag_cmd error!diag_command=0x%x so reset\n", ts->diag_cmd); + command[0] = 0x00; + + if (ts->diag_cmd != 0x08) + ts->diag_cmd = 0x00; + + g_core_fp.fp_diag_register_set(command[0], storage_type); + } else { + command[0] = 0x00; + ts->diag_cmd = 0x00; + g_core_fp.fp_diag_register_set(command[0], storage_type); + I("return to normal ts->diag_cmd=0x%x\n", ts->diag_cmd); + } + } + return len; +} + +static struct file_operations himax_proc_diag_ops = { + .owner = THIS_MODULE, + .open = himax_diag_proc_open, + .read = seq_read, + .write = himax_diag_write, +}; + +static ssize_t himax_reset_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + char buf_tmp[12]; + + if (len >= 12) { + I("%s: no command exceeds 12 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf_tmp, buff, len)) { + return -EFAULT; + } + +#ifdef HX_RST_PIN_FUNC + + if (buf_tmp[0] == '1') { + g_core_fp.fp_ic_reset(false, false); + } else if (buf_tmp[0] == '2') { + g_core_fp.fp_ic_reset(false, true); + } else if (buf_tmp[0] == '3') { + g_core_fp.fp_ic_reset(true, false); + } else if (buf_tmp[0] == '4') { + g_core_fp.fp_ic_reset(true, true); + } + /* else if (buf_tmp[0] == '5') */ + /* ESD_HW_REST(); */ +#endif + return len; +} + +static struct file_operations himax_proc_reset_ops = { + .owner = THIS_MODULE, + .write = himax_reset_write, +}; + +static ssize_t himax_debug_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + size_t ret = 0; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + + if (debug_level_cmd == 't') { + if (fw_update_complete) { + ret += snprintf(temp_buf + ret, len - ret, "FW Update Complete "); + } else { + ret += snprintf(temp_buf + ret, len - ret, "FW Update Fail "); + } + } else if (debug_level_cmd == 'h') { + if (handshaking_result == 0) { + ret += snprintf(temp_buf + ret, len - ret, "Handshaking Result = %d (MCU Running)\n", handshaking_result); + } else if (handshaking_result == 1) { + ret += snprintf(temp_buf + ret, len - ret, "Handshaking Result = %d (MCU Stop)\n", handshaking_result); + } else if (handshaking_result == 2) { + ret += snprintf(temp_buf + ret, len - ret, "Handshaking Result = %d (I2C Error)\n", handshaking_result); + } else { + ret += snprintf(temp_buf + ret, len - ret, "Handshaking Result = error \n"); + } + } else if (debug_level_cmd == 'v') { + ret += snprintf(temp_buf + ret, len - ret, "FW_VER = 0x%2.2X \n", ic_data->vendor_fw_ver); + + if (private_ts->chip_cell_type == CHIP_IS_ON_CELL) { + ret += snprintf(temp_buf + ret, len - ret, "CONFIG_VER = 0x%2.2X \n", ic_data->vendor_config_ver); + } else { + ret += snprintf(temp_buf + ret, len - ret, "TOUCH_VER = 0x%2.2X \n", ic_data->vendor_touch_cfg_ver); + ret += snprintf(temp_buf + ret, len - ret, "DISPLAY_VER = 0x%2.2X \n", ic_data->vendor_display_cfg_ver); + } + if (ic_data->vendor_cid_maj_ver < 0 && ic_data->vendor_cid_min_ver < 0) + ret += snprintf(temp_buf + ret, len - ret, "CID_VER = NULL\n"); + else + ret += snprintf(temp_buf + ret, len - ret, "CID_VER = 0x%2.2X \n", (ic_data->vendor_cid_maj_ver << 8 | ic_data->vendor_cid_min_ver)); + + if (ic_data->vendor_panel_ver < 0) + ret += snprintf(temp_buf + ret, len - ret, "PANEL_VER = NULL\n"); + else + ret += snprintf(temp_buf + ret, len - ret, "PANEL_VER = 0x%2.2X \n", ic_data->vendor_panel_ver); + + ret += snprintf(temp_buf + ret, len - ret, "\n"); + ret += snprintf(temp_buf + ret, len - ret, "Himax Touch Driver Version:\n"); + ret += snprintf(temp_buf + ret, len - ret, "%s \n", HIMAX_DRIVER_VER); + } else if (debug_level_cmd == 'd') { + ret += snprintf(temp_buf + ret, len - ret, "Himax Touch IC Information :\n"); + ret += snprintf(temp_buf + ret, len - ret, "%s \n", private_ts->chip_name); + + switch (IC_CHECKSUM) { + case HX_TP_BIN_CHECKSUM_SW: + ret += snprintf(temp_buf + ret, len - ret, "IC Checksum : SW\n"); + break; + + case HX_TP_BIN_CHECKSUM_HW: + ret += snprintf(temp_buf + ret, len - ret, "IC Checksum : HW\n"); + break; + + case HX_TP_BIN_CHECKSUM_CRC: + ret += snprintf(temp_buf + ret, len - ret, "IC Checksum : CRC\n"); + break; + + default: + ret += snprintf(temp_buf + ret, len - ret, "IC Checksum error.\n"); + } + + if (ic_data->HX_INT_IS_EDGE) { + ret += snprintf(temp_buf + ret, len - ret, "Driver register Interrupt : EDGE TIRGGER\n"); + } else { + ret += snprintf(temp_buf + ret, len - ret, "Driver register Interrupt : LEVEL TRIGGER\n"); + } + if (private_ts->protocol_type == PROTOCOL_TYPE_A) { + ret += snprintf(temp_buf + ret, len - ret, "Protocol : TYPE_A\n"); + } else { + ret += snprintf(temp_buf + ret, len - ret, "Protocol : TYPE_B\n"); + } + + ret += snprintf(temp_buf + ret, len - ret, "RX Num : %d\n", ic_data->HX_RX_NUM); + ret += snprintf(temp_buf + ret, len - ret, "TX Num : %d\n", ic_data->HX_TX_NUM); + ret += snprintf(temp_buf + ret, len - ret, "BT Num : %d\n", ic_data->HX_BT_NUM); + ret += snprintf(temp_buf + ret, len - ret, "X Resolution : %d\n", ic_data->HX_X_RES); + ret += snprintf(temp_buf + ret, len - ret, "Y Resolution : %d\n", ic_data->HX_Y_RES); + ret += snprintf(temp_buf + ret, len - ret, "Max Point : %d\n", ic_data->HX_MAX_PT); + ret += snprintf(temp_buf + ret, len - ret, "XY reverse : %d\n", ic_data->HX_XY_REVERSE); +#ifdef HX_TP_PROC_2T2R + if (Is_2T2R) { + ret += snprintf(temp_buf + ret, len - ret, "2T2R panel\n"); + ret += snprintf(temp_buf + ret, len - ret, "RX Num_2 : %d\n", HX_RX_NUM_2); + ret += snprintf(temp_buf + ret, len - ret, "TX Num_2 : %d\n", HX_TX_NUM_2); + } +#endif + } else if (debug_level_cmd == 'i') { + if (g_core_fp.fp_read_i2c_status()) + ret += snprintf(temp_buf + ret, len - ret, "I2C communication is bad.\n"); + else + ret += snprintf(temp_buf + ret, len - ret, "I2C communication is good.\n"); + } else if (debug_level_cmd == 'n') { + if (g_core_fp.fp_read_ic_trigger_type() == 1) /* Edgd = 1, Level = 0 */ + ret += snprintf(temp_buf + ret, len - ret, "IC Interrupt type is edge trigger.\n"); + else if (g_core_fp.fp_read_ic_trigger_type() == 0) + ret += snprintf(temp_buf + ret, len - ret, "IC Interrupt type is level trigger.\n"); + else + ret += snprintf(temp_buf + ret, len - ret, "Unkown IC trigger type.\n"); + + if (ic_data->HX_INT_IS_EDGE) + ret += snprintf(temp_buf + ret, len - ret, "Driver register Interrupt : EDGE TIRGGER\n"); + else + ret += snprintf(temp_buf + ret, len - ret, "Driver register Interrupt : LEVEL TRIGGER\n"); + } + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +extern int g_ts_dbg; +static ssize_t himax_debug_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + char fileName[128]; + char buf[80] = {0}; + int result = 0; +#ifndef HX_ZERO_FLASH + int fw_type = 0; + const struct firmware *fw = NULL; +#endif + + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) { + return -EFAULT; + } + + if (buf[0] == 'h') { /* handshaking */ + debug_level_cmd = buf[0]; + himax_int_enable(0); + handshaking_result = g_core_fp.fp_hand_shaking(); /* 0:Running, 1:Stop, 2:I2C Fail */ + himax_int_enable(1); + return len; + } else if (buf[0] == 'v') { /* firmware version */ + himax_int_enable(0); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#endif + debug_level_cmd = buf[0]; + g_core_fp.fp_read_FW_ver(); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(true, false); +#else + g_core_fp.fp_system_reset(); +#endif + himax_int_enable(1); + /* himax_check_chip_version(); */ + return len; + } else if (buf[0] == 'd') { /* ic information */ + debug_level_cmd = buf[0]; + return len; + } else if (buf[0] == 't') { + if (buf[1] == 's' && + buf[2] == 'd' && + buf[3] == 'b' && + buf[4] == 'g' + ) { + if (buf[5] == '1') { + I("Open Ts Debug!\n"); + g_ts_dbg = 1; + } else if (buf[5] == '0') { + I("Close Ts Debug!\n"); + g_ts_dbg = 0; + } else { + E("Parameter fault for ts debug\n"); + } + goto ENDFUCTION; + } + himax_int_enable(0); + debug_level_cmd = buf[0]; + fw_update_complete = false; + memset(fileName, 0, 128); + /* parse the file name */ + snprintf(fileName, len - 2, "%s", &buf[2]); + +#ifdef HX_ZERO_FLASH + I("NOW Running Zero flash update!\n"); + I("%s: upgrade from file(%s) start!\n", __func__, fileName); + result = g_core_fp.fp_0f_op_file_dirly(fileName); + if (result) { + fw_update_complete = false; + I("Zero flash update fail!\n"); + goto ENDFUCTION; + } else { + fw_update_complete = true; + I("Zero flash update complete!\n"); + } + goto firmware_upgrade_done; +#else + I("NOW Running common flow update!\n"); + I("%s: upgrade from file(%s) start!\n", __func__, fileName); + result = request_firmware(&fw, fileName, private_ts->dev); + + if (result < 0) { + I("fail to request_firmware fwpath: %s (ret:%d)\n", fileName, result); + return result; + } + + I("%s: FW image: %02X, %02X, %02X, %02X\n", __func__, fw->data[0], fw->data[1], fw->data[2], fw->data[3]); + fw_type = (fw->size) / 1024; + /* start to upgrade */ + himax_int_enable(0); + I("Now FW size is : %dk\n", fw_type); + + switch (fw_type) { + case 32: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_32k((unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", __func__, __LINE__); + fw_update_complete = true; + } + break; + + case 60: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_60k((unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", __func__, __LINE__); + fw_update_complete = true; + } + break; + + case 64: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_64k((unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", __func__, __LINE__); + fw_update_complete = true; + } + break; + + case 124: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_124k((unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", __func__, __LINE__); + fw_update_complete = true; + } + break; + + case 128: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_128k((unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", __func__, __LINE__); + fw_update_complete = true; + } + break; + + default: + E("%s: Flash command fail: %d\n", __func__, __LINE__); + fw_update_complete = false; + break; + } + release_firmware(fw); + goto firmware_upgrade_done; +#endif + } else if (buf[0] == 'i' && buf[1] == '2' && buf[2] == 'c') { /* i2c commutation */ + debug_level_cmd = 'i'; + return len; + } else if (buf[0] == 'i' && buf[1] == 'n' && buf[2] == 't') { /* INT trigger */ + debug_level_cmd = 'n'; + return len; +#ifdef HX_ZERO_FLASH + } else if (buf[0] == 'z') { + result = buf[1] - '0'; + I("check type = %d \n", result); + g_core_fp.fp_0f_operation_check(result); + return len; + } else if (buf[0] == 'p') { + I("NOW debug echo r!\n"); + /* himax_program_sram(); */ + private_ts->himax_0f_update_wq = create_singlethread_workqueue("HMX_update_0f_reuqest_write"); + + if (!private_ts->himax_0f_update_wq) + E(" allocate syn_update_wq failed\n"); + + INIT_DELAYED_WORK(&private_ts->work_0f_update, g_core_fp.fp_0f_operation); + queue_delayed_work(private_ts->himax_0f_update_wq, &private_ts->work_0f_update, msecs_to_jiffies(100)); + return len; + } else if (buf[0] == 'x') { + g_core_fp.fp_system_reset(); + return len; +#endif + } else { /* others,do nothing */ + debug_level_cmd = 0; + return len; + } + + +firmware_upgrade_done: + g_core_fp.fp_reload_disable(0); + g_core_fp.fp_read_FW_ver(); + g_core_fp.fp_touch_information(); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(true, false); +#else + g_core_fp.fp_sense_on(0x00); +#endif + + himax_int_enable(1); +/* todo himax_chip->tp_firmware_upgrade_proceed = 0; + todo himax_chip->suspend_state = 0; + todo enable_irq(himax_chip->irq); */ +ENDFUCTION: + return len; +} + +static struct file_operations himax_proc_debug_ops = { + .owner = THIS_MODULE, + .read = himax_debug_read, + .write = himax_debug_write, +}; + +static ssize_t himax_proc_FW_debug_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + ssize_t ret = 0; + uint8_t loop_i = 0; + uint8_t tmp_data[64]; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + cmd_set[0] = 0x01; + + if (g_core_fp.fp_read_FW_status(cmd_set, tmp_data) == NO_ERR) { + ret += snprintf(temp_buf + ret, len - ret, "0x%02X%02X%02X%02X :\t", cmd_set[5], cmd_set[4], cmd_set[3], cmd_set[2]); + + for (loop_i = 0; loop_i < cmd_set[1]; loop_i++) { + ret += snprintf(temp_buf + ret, len - ret, "%5d\t", tmp_data[loop_i]); + } + + ret += snprintf(temp_buf + ret, len - ret, "\n"); + } + + cmd_set[0] = 0x02; + + if (g_core_fp.fp_read_FW_status(cmd_set, tmp_data) == NO_ERR) { + for (loop_i = 0; loop_i < cmd_set[1]; loop_i = loop_i + 2) { + if ((loop_i % 16) == 0) + ret += snprintf(temp_buf + ret, len - ret, "0x%02X%02X%02X%02X :\t", + cmd_set[5], cmd_set[4], cmd_set[3] + (((cmd_set[2] + loop_i) >> 8) & 0xFF), (cmd_set[2] + loop_i) & 0xFF); + + ret += snprintf(temp_buf + ret, len - ret, "%5d\t", tmp_data[loop_i] + (tmp_data[loop_i + 1] << 8)); + + if ((loop_i % 16) == 14) + ret += snprintf(temp_buf + ret, len - ret, "\n"); + } + } + + ret += snprintf(temp_buf + ret, len - ret, "\n"); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static struct file_operations himax_proc_fw_debug_ops = { + .owner = THIS_MODULE, + .read = himax_proc_FW_debug_read, +}; + +static ssize_t himax_proc_DD_debug_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + ssize_t ret = 0; + uint8_t tmp_data[64]; + uint8_t loop_i = 0; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + + if (mutual_set_flag == 1) { + if (g_core_fp.fp_read_DD_status(cmd_set, tmp_data) == NO_ERR) { + for (loop_i = 0; loop_i < cmd_set[0]; loop_i++) { + if ((loop_i % 8) == 0) + ret += snprintf(temp_buf + ret, len - ret, "0x%02X : ", loop_i); + + ret += snprintf(temp_buf + ret, len - ret, "0x%02X ", tmp_data[loop_i]); + + if ((loop_i % 8) == 7) + ret += snprintf(temp_buf + ret, len - ret, "\n"); + } + } + } + + ret += snprintf(temp_buf + ret, len - ret, "\n"); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static ssize_t himax_proc_DD_debug_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + uint8_t i = 0; + uint8_t cnt = 2; + unsigned long result = 0; + char buf_tmp[20]; + char buf_tmp2[4]; + + if (len >= 20) { + I("%s: no command exceeds 20 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf_tmp, buff, len)) { + return -EFAULT; + } + + memset(buf_tmp2, 0x0, sizeof(buf_tmp2)); + + if (buf_tmp[2] == 'x' && buf_tmp[6] == 'x' && buf_tmp[10] == 'x') { + mutual_set_flag = 1; + + for (i = 3; i < 12; i = i + 4) { + memcpy(buf_tmp2, buf_tmp + i, 2); + + if (!kstrtoul(buf_tmp2, 16, &result)) + cmd_set[cnt] = (uint8_t)result; + else + I("String to oul is fail in cnt = %d, buf_tmp2 = %s\n", cnt, buf_tmp2); + + cnt--; + } + + I("cmd_set[2] = %02X, cmd_set[1] = %02X, cmd_set[0] = %02X\n", cmd_set[2], cmd_set[1], cmd_set[0]); + } else { + mutual_set_flag = 0; + } + + return len; +} + +static struct file_operations himax_proc_dd_debug_ops = { + .owner = THIS_MODULE, + .read = himax_proc_DD_debug_read, + .write = himax_proc_DD_debug_write, +}; + +uint8_t getFlashCommand(void) +{ + return flash_command; +} + +static uint8_t getFlashDumpProgress(void) +{ + return flash_progress; +} + +static uint8_t getFlashDumpComplete(void) +{ + return flash_dump_complete; +} + +static uint8_t getFlashDumpFail(void) +{ + return flash_dump_fail; +} + +uint8_t getSysOperation(void) +{ + return sys_operation; +} + +static uint8_t getFlashReadStep(void) +{ + return flash_read_step; +} + +bool getFlashDumpGoing(void) +{ + return flash_dump_going; +} + +void setFlashBuffer(void) +{ + flash_buffer = kzalloc(Flash_Size * sizeof(uint8_t), GFP_KERNEL); +} + +void setSysOperation(uint8_t operation) +{ + sys_operation = operation; +} + +void setFlashDumpProgress(uint8_t progress) +{ + flash_progress = progress; + /* I("setFlashDumpProgress : progress = %d ,flash_progress = %d \n",progress,flash_progress); */ +} + +void setFlashDumpComplete(uint8_t status) +{ + flash_dump_complete = status; +} + +void setFlashDumpFail(uint8_t fail) +{ + flash_dump_fail = fail; +} + +static void setFlashCommand(uint8_t command) +{ + flash_command = command; +} + +static void setFlashReadStep(uint8_t step) +{ + flash_read_step = step; +} + +void setFlashDumpGoing(bool going) +{ + flash_dump_going = going; + debug_data->flash_dump_going = going; +} + +static ssize_t himax_proc_flash_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + ssize_t ret = 0; + int loop_i; + uint8_t local_flash_read_step = 0; + uint8_t local_flash_complete = 0; + uint8_t local_flash_progress = 0; + uint8_t local_flash_command = 0; + uint8_t local_flash_fail = 0; + char *temp_buf; + local_flash_complete = getFlashDumpComplete(); + local_flash_progress = getFlashDumpProgress(); + local_flash_command = getFlashCommand(); + local_flash_fail = getFlashDumpFail(); + I("flash_progress = %d \n", local_flash_progress); + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + + if (local_flash_fail) { + ret += snprintf(temp_buf + ret, len - ret, "FlashStart:Fail \n"); + ret += snprintf(temp_buf + ret, len - ret, "FlashEnd"); + ret += snprintf(temp_buf + ret, len - ret, "\n"); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + return ret; + } + + if (!local_flash_complete) { + ret += snprintf(temp_buf + ret, len - ret, "FlashStart:Ongoing:0x%2.2x \n", flash_progress); + ret += snprintf(temp_buf + ret, len - ret, "FlashEnd"); + ret += snprintf(temp_buf + ret, len - ret, "\n"); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + return ret; + } + + if (local_flash_command == 1 && local_flash_complete) { + ret += snprintf(temp_buf + ret, len - ret, "FlashStart:Complete \n"); + ret += snprintf(temp_buf + ret, len - ret, "FlashEnd"); + ret += snprintf(temp_buf + ret, len - ret, "\n"); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + return ret; + } + + if (local_flash_command == 3 && local_flash_complete) { + ret += snprintf(temp_buf + ret, len - ret, "FlashStart: \n"); + + for (loop_i = 0; loop_i < 128; loop_i++) { + ret += snprintf(temp_buf + ret, len - ret, "x%2.2x", flash_buffer[loop_i]); + + if ((loop_i % 16) == 15) { + ret += snprintf(temp_buf + ret, len - ret, "\n"); + } + } + + ret += snprintf(temp_buf + ret, len - ret, "FlashEnd"); + ret += snprintf(temp_buf + ret, len - ret, "\n"); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + return ret; + } + + /* flash command == 0 , report the data */ + local_flash_read_step = getFlashReadStep(); + ret += snprintf(temp_buf + ret, len - ret, "FlashStart:%2.2x \n", local_flash_read_step); + + for (loop_i = 0; loop_i < 1024; loop_i++) { + ret += snprintf(temp_buf + ret, len - ret, "x%2.2X", flash_buffer[local_flash_read_step * 1024 + loop_i]); + + if ((loop_i % 16) == 15) { + ret += snprintf(temp_buf + ret, len - ret, "\n"); + } + } + + ret += snprintf(temp_buf + ret, len - ret, "FlashEnd"); + ret += snprintf(temp_buf + ret, len - ret, "\n"); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static ssize_t himax_proc_flash_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + char buf_tmp[6]; + unsigned long result = 0; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) { + return -EFAULT; + } + + memset(buf_tmp, 0x0, sizeof(buf_tmp)); + I("%s: buf = %s\n", __func__, buf); + + if (getSysOperation() == 1) { + E("%s: PROC is busy , return!\n", __func__); + return len; + } + + if (buf[0] == '0') { + setFlashCommand(0); + + if (buf[1] == ':' && buf[2] == 'x') { + memcpy(buf_tmp, buf + 3, 2); + I("%s: read_Step = %s\n", __func__, buf_tmp); + + if (!kstrtoul(buf_tmp, 16, &result)) { + I("%s: read_Step = %lu \n", __func__, result); + setFlashReadStep(result); + } + } + } else if (buf[0] == '1') { /* 1_32,1_60,1_64,1_24,1_28 for flash size 32k,60k,64k,124k,128k */ + setSysOperation(1); + setFlashCommand(1); + setFlashDumpProgress(0); + setFlashDumpComplete(0); + setFlashDumpFail(0); + + if ((buf[1] == '_') && (buf[2] == '3') && (buf[3] == '2')) { + Flash_Size = FW_SIZE_32k; + } else if ((buf[1] == '_') && (buf[2] == '6')) { + if (buf[3] == '0') { + Flash_Size = FW_SIZE_60k; + } else if (buf[3] == '4') { + Flash_Size = FW_SIZE_64k; + } + } else if ((buf[1] == '_') && (buf[2] == '2')) { + if (buf[3] == '4') { + Flash_Size = FW_SIZE_124k; + } else if (buf[3] == '8') { + Flash_Size = FW_SIZE_128k; + } + } + queue_work(private_ts->flash_wq, &private_ts->flash_work); + } else if (buf[0] == '2') { /* 2_32,2_60,2_64,2_24,2_28 for flash size 32k,60k,64k,124k,128k */ + setSysOperation(1); + setFlashCommand(2); + setFlashDumpProgress(0); + setFlashDumpComplete(0); + setFlashDumpFail(0); + + if ((buf[1] == '_') && (buf[2] == '3') && (buf[3] == '2')) { + Flash_Size = FW_SIZE_32k; + } else if ((buf[1] == '_') && (buf[2] == '6')) { + if (buf[3] == '0') { + Flash_Size = FW_SIZE_60k; + } else if (buf[3] == '4') { + Flash_Size = FW_SIZE_64k; + } + } else if ((buf[1] == '_') && (buf[2] == '2')) { + if (buf[3] == '4') { + Flash_Size = FW_SIZE_124k; + } else if (buf[3] == '8') { + Flash_Size = FW_SIZE_128k; + } + } + queue_work(private_ts->flash_wq, &private_ts->flash_work); + } + + return len; +} + +static struct file_operations himax_proc_flash_ops = { + .owner = THIS_MODULE, + .read = himax_proc_flash_read, + .write = himax_proc_flash_write, +}; + +void himax_ts_flash_func(void) +{ + uint8_t local_flash_command = 0; + himax_int_enable(0); + setFlashDumpGoing(true); + /* sector = getFlashDumpSector(); */ + /* page = getFlashDumpPage(); */ + local_flash_command = getFlashCommand(); + msleep(100); + I("%s: local_flash_command = %d enter.\n", __func__, local_flash_command); + + if ((local_flash_command == 1 || local_flash_command == 2) || (local_flash_command == 0x0F)) { + g_core_fp.fp_flash_dump_func(local_flash_command, Flash_Size, flash_buffer); + } + + I("Complete~~~~~~~~~~~~~~~~~~~~~~~\n"); + +#if 0 + if (local_flash_command == 2) { + struct file *fn; + struct filename *vts_name; + vts_name = getname_kernel(FLASH_DUMP_FILE); + fn = file_open_name(vts_name, O_CREAT | O_WRONLY, 0); + + if (!IS_ERR(fn)) { + I("%s create file and ready to write\n", __func__); + fn->f_op->write(fn, flash_buffer, Flash_Size * sizeof(uint8_t), &fn->f_pos); + filp_close(fn, NULL); + } + } +#endif + + himax_int_enable(1); + setFlashDumpGoing(false); + setFlashDumpComplete(1); + setSysOperation(0); + return; + /* Flash_Dump_i2c_transfer_error: + + himax_int_enable(1); + setFlashDumpGoing(false); + setFlashDumpComplete(0); + setFlashDumpFail(1); + setSysOperation(0); + return; + */ +} + + + +static ssize_t himax_sense_on_off_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) { + return -EFAULT; + } + + if (buf[0] == '0') { + g_core_fp.fp_sense_off(); + I("Sense off \n"); + } else if (buf[0] == '1') { + if (buf[1] == 's') { + g_core_fp.fp_sense_on(0x00); + I("Sense on re-map on, run sram \n"); + } else { + g_core_fp.fp_sense_on(0x01); + I("Sense on re-map off, run flash \n"); + } + } else { + I("Do nothing \n"); + } + + return len; +} + +static struct file_operations himax_proc_sense_on_off_ops = { + .owner = THIS_MODULE, + .write = himax_sense_on_off_write, +}; + +#ifdef HX_ESD_RECOVERY +static ssize_t himax_esd_cnt_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + size_t ret = 0; + char *temp_buf; + I("%s: enter, %d \n", __func__, __LINE__); + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + ret += snprintf(temp_buf + ret, len - ret, "EB_cnt = %d, EC_cnt = %d, ED_cnt = %d\n", hx_EB_event_flag, hx_EC_event_flag, hx_ED_event_flag); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static ssize_t himax_esd_cnt_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + int i = 0; + char buf[12] = {0}; + + if (len >= 12) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) { + return -EFAULT; + } + + I("Clear ESD Flag \n"); + + if (buf[i] == '0') { + hx_EB_event_flag = 0; + hx_EC_event_flag = 0; + hx_ED_event_flag = 0; + } + + return len; +} + +static struct file_operations himax_proc_esd_cnt_ops = { + .owner = THIS_MODULE, + .read = himax_esd_cnt_read, + .write = himax_esd_cnt_write, +}; +#endif + +static void himax_himax_data_init(void) +{ + debug_data->fp_ts_dbg_func = himax_ts_dbg_func; + debug_data->fp_set_diag_cmd = himax_set_diag_cmd; + debug_data->flash_dump_going = false; +} + +static void himax_ts_flash_work_func(struct work_struct *work) +{ + himax_ts_flash_func(); +} + +static void himax_ts_diag_work_func(struct work_struct *work) +{ + himax_ts_diag_func(); +} + +int himax_touch_proc_init(void) +{ + himax_proc_debug_level_file = proc_create(HIMAX_PROC_DEBUG_LEVEL_FILE, (S_IWUSR | S_IRUGO), + himax_touch_proc_dir, &himax_proc_debug_level_ops); + if (himax_proc_debug_level_file == NULL) { + E(" %s: proc debug_level file create failed!\n", __func__); + goto fail_1; + } + + himax_proc_vendor_file = proc_create(HIMAX_PROC_VENDOR_FILE, (S_IRUGO), + himax_touch_proc_dir, &himax_proc_vendor_ops); + if (himax_proc_vendor_file == NULL) { + E(" %s: proc vendor file create failed!\n", __func__); + goto fail_2; + } + + himax_proc_attn_file = proc_create(HIMAX_PROC_ATTN_FILE, (S_IRUGO), + himax_touch_proc_dir, &himax_proc_attn_ops); + if (himax_proc_attn_file == NULL) { + E(" %s: proc attn file create failed!\n", __func__); + goto fail_3; + } + + himax_proc_int_en_file = proc_create(HIMAX_PROC_INT_EN_FILE, (S_IWUSR | S_IRUGO), + himax_touch_proc_dir, &himax_proc_int_en_ops); + if (himax_proc_int_en_file == NULL) { + E(" %s: proc int en file create failed!\n", __func__); + goto fail_4; + } + + himax_proc_layout_file = proc_create(HIMAX_PROC_LAYOUT_FILE, (S_IWUSR | S_IRUGO), + himax_touch_proc_dir, &himax_proc_layout_ops); + if (himax_proc_layout_file == NULL) { + E(" %s: proc layout file create failed!\n", __func__); + goto fail_5; + } + + himax_proc_reset_file = proc_create(HIMAX_PROC_RESET_FILE, (S_IWUSR), + himax_touch_proc_dir, &himax_proc_reset_ops); + if (himax_proc_reset_file == NULL) { + E(" %s: proc reset file create failed!\n", __func__); + goto fail_6; + } + + himax_proc_diag_file = proc_create(HIMAX_PROC_DIAG_FILE, (S_IWUSR | S_IRUGO), + himax_touch_proc_dir, &himax_proc_diag_ops); + if (himax_proc_diag_file == NULL) { + E(" %s: proc diag file create failed!\n", __func__); + goto fail_7; + } + + himax_proc_diag_arrange_file = proc_create(HIMAX_PROC_DIAG_ARR_FILE, (S_IWUSR | S_IRUGO), + himax_touch_proc_dir, &himax_proc_diag_arrange_ops); + if (himax_proc_diag_arrange_file == NULL) { + E(" %s: proc diag file create failed!\n", __func__); + goto fail_7_1; + } + + himax_proc_register_file = proc_create(HIMAX_PROC_REGISTER_FILE, (S_IWUSR | S_IRUGO), + himax_touch_proc_dir, &himax_proc_register_ops); + if (himax_proc_register_file == NULL) { + E(" %s: proc register file create failed!\n", __func__); + goto fail_8; + } + + himax_proc_debug_file = proc_create(HIMAX_PROC_DEBUG_FILE, (S_IWUSR | S_IRUGO), + himax_touch_proc_dir, &himax_proc_debug_ops); + if (himax_proc_debug_file == NULL) { + E(" %s: proc debug file create failed!\n", __func__); + goto fail_9; + } + + himax_proc_fw_debug_file = proc_create(HIMAX_PROC_FW_DEBUG_FILE, (S_IWUSR | S_IRUGO), + himax_touch_proc_dir, &himax_proc_fw_debug_ops); + if (himax_proc_fw_debug_file == NULL) { + E(" %s: proc fw debug file create failed!\n", __func__); + goto fail_9_1; + } + + himax_proc_dd_debug_file = proc_create(HIMAX_PROC_DD_DEBUG_FILE, (S_IWUSR | S_IRUGO), + himax_touch_proc_dir, &himax_proc_dd_debug_ops); + if (himax_proc_dd_debug_file == NULL) { + E(" %s: proc DD debug file create failed!\n", __func__); + goto fail_9_2; + } + + himax_proc_flash_dump_file = proc_create(HIMAX_PROC_FLASH_DUMP_FILE, (S_IWUSR | S_IRUGO), + himax_touch_proc_dir, &himax_proc_flash_ops); + if (himax_proc_flash_dump_file == NULL) { + E(" %s: proc flash dump file create failed!\n", __func__); + goto fail_10; + } + + himax_proc_SENSE_ON_OFF_file = proc_create(HIMAX_PROC_SENSE_ON_OFF_FILE, (S_IWUSR | S_IRUGO | S_IWUGO), + himax_touch_proc_dir, &himax_proc_sense_on_off_ops); + if (himax_proc_SENSE_ON_OFF_file == NULL) { + E(" %s: proc SENSE_ON_OFF file create failed!\n", __func__); + goto fail_16; + } + +#ifdef HX_ESD_RECOVERY + himax_proc_ESD_cnt_file = proc_create(HIMAX_PROC_ESD_CNT_FILE, (S_IWUSR | S_IRUGO | S_IWUGO), + himax_touch_proc_dir, &himax_proc_esd_cnt_ops); + + if (himax_proc_ESD_cnt_file == NULL) { + E(" %s: proc ESD cnt file create failed!\n", __func__); + goto fail_17; + } + +#endif + himax_proc_CRC_test_file = proc_create(HIMAX_PROC_CRC_TEST_FILE, (S_IWUSR | S_IRUGO | S_IWUGO), + himax_touch_proc_dir, &himax_proc_CRC_test_ops); + + if (himax_proc_CRC_test_file == NULL) { + E(" %s: proc CRC test file create failed!\n", __func__); + goto fail_18; + } + + return 0 ; +fail_18: +#ifdef HX_ESD_RECOVERY + remove_proc_entry(HIMAX_PROC_ESD_CNT_FILE, himax_touch_proc_dir); +fail_17: +#endif + remove_proc_entry(HIMAX_PROC_SENSE_ON_OFF_FILE, himax_touch_proc_dir); +fail_16: remove_proc_entry(HIMAX_PROC_FLASH_DUMP_FILE, himax_touch_proc_dir); +fail_10: remove_proc_entry(HIMAX_PROC_DEBUG_FILE, himax_touch_proc_dir); +fail_9: remove_proc_entry(HIMAX_PROC_FW_DEBUG_FILE, himax_touch_proc_dir); +fail_9_1: remove_proc_entry(HIMAX_PROC_DD_DEBUG_FILE, himax_touch_proc_dir); +fail_9_2: remove_proc_entry(HIMAX_PROC_REGISTER_FILE, himax_touch_proc_dir); +fail_8: remove_proc_entry(HIMAX_PROC_DIAG_FILE, himax_touch_proc_dir); +fail_7: remove_proc_entry(HIMAX_PROC_DIAG_ARR_FILE, himax_touch_proc_dir); +fail_7_1: remove_proc_entry(HIMAX_PROC_RESET_FILE, himax_touch_proc_dir); +fail_6: remove_proc_entry(HIMAX_PROC_LAYOUT_FILE, himax_touch_proc_dir); +fail_5: remove_proc_entry(HIMAX_PROC_INT_EN_FILE, himax_touch_proc_dir); +fail_4: remove_proc_entry(HIMAX_PROC_ATTN_FILE, himax_touch_proc_dir); +fail_3: remove_proc_entry(HIMAX_PROC_VENDOR_FILE, himax_touch_proc_dir); +fail_2: remove_proc_entry(HIMAX_PROC_DEBUG_LEVEL_FILE, himax_touch_proc_dir); +fail_1: + return -ENOMEM; +} + +void himax_touch_proc_deinit(void) +{ + remove_proc_entry(HIMAX_PROC_CRC_TEST_FILE, himax_touch_proc_dir); +#ifdef HX_ESD_RECOVERY + remove_proc_entry(HIMAX_PROC_ESD_CNT_FILE, himax_touch_proc_dir); +#endif + remove_proc_entry(HIMAX_PROC_SENSE_ON_OFF_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_FLASH_DUMP_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_DEBUG_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_FW_DEBUG_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_DD_DEBUG_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_REGISTER_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_DIAG_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_DIAG_ARR_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_RESET_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_LAYOUT_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_INT_EN_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_ATTN_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_VENDOR_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_DEBUG_LEVEL_FILE, himax_touch_proc_dir); +} + +int himax_debug_init(void) +{ + struct himax_ts_data *ts = private_ts; + int err = 0; + + I("%s:Enter\n", __func__); + + if (ts == NULL) { + E("%s: ts struct is NULL \n", __func__); + return -EPROBE_DEFER; + } + + debug_data = kzalloc(sizeof(*debug_data), GFP_KERNEL); + if (debug_data == NULL) { /*Allocate debug data space*/ + err = -ENOMEM; + goto err_alloc_debug_data_fail; + } + + himax_himax_data_init(); + + ts->flash_wq = create_singlethread_workqueue("himax_flash_wq"); + + if (!ts->flash_wq) { + E("%s: create flash workqueue failed\n", __func__); + err = -ENOMEM; + goto err_create_flash_dump_wq_failed; + } + + INIT_WORK(&ts->flash_work, himax_ts_flash_work_func); + setSysOperation(0); + setFlashBuffer(); + + ts->himax_diag_wq = create_singlethread_workqueue("himax_diag"); + + if (!ts->himax_diag_wq) { + E("%s: create diag workqueue failed\n", __func__); + err = -ENOMEM; + goto err_create_diag_wq_failed; + } + + INIT_DELAYED_WORK(&ts->himax_diag_delay_wrok, himax_ts_diag_work_func); + + setXChannel(ic_data->HX_RX_NUM); /*X channel*/ + setYChannel(ic_data->HX_TX_NUM); /*Y channel*/ + setMutualBuffer(); + setMutualNewBuffer(); + setMutualOldBuffer(); + + if (getMutualBuffer() == NULL) { + E("%s: mutual buffer allocate fail failed\n", __func__); + return MEM_ALLOC_FAIL; + } +#ifdef HX_TP_PROC_2T2R + + if (Is_2T2R) { + setXChannel_2(ic_data->HX_RX_NUM_2); /*X channel*/ + setYChannel_2(ic_data->HX_TX_NUM_2); /*Y channel*/ + setMutualBuffer_2(); + + if (getMutualBuffer_2() == NULL) { + E("%s: mutual buffer 2 allocate fail failed\n", __func__); + return MEM_ALLOC_FAIL; + } + } +#endif + + himax_touch_proc_init(); + + return 0; + + cancel_delayed_work_sync(&ts->himax_diag_delay_wrok); + destroy_workqueue(ts->himax_diag_wq); +err_create_diag_wq_failed: + + destroy_workqueue(ts->flash_wq); +err_create_flash_dump_wq_failed: + +err_alloc_debug_data_fail: + + return err; +} + +int himax_debug_remove(void) +{ + struct himax_ts_data *ts = private_ts; + + himax_touch_proc_deinit(); + + cancel_delayed_work_sync(&ts->himax_diag_delay_wrok); + destroy_workqueue(ts->himax_diag_wq); + destroy_workqueue(ts->flash_wq); + + kfree(debug_data); + + return 0; +} + diff --git a/drivers/input/touchscreen/himax_mmi/himax_debug.h b/drivers/input/touchscreen/himax_mmi/himax_debug.h new file mode 100644 index 000000000000..90d634ec6d9d --- /dev/null +++ b/drivers/input/touchscreen/himax_mmi/himax_debug.h @@ -0,0 +1,154 @@ +/* Himax Android Driver Sample Code for debug nodes + + Copyright (C) 2018 Himax Corporation. + + 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. + +*/ +#ifndef H_HIMAX_DEBUG +#define H_HIMAX_DEBUG + +#include "himax_platform.h" +#include "himax_common.h" + + +#ifdef HX_ESD_RECOVERY + extern u8 HX_ESD_RESET_ACTIVATE; + extern int hx_EB_event_flag; + extern int hx_EC_event_flag; + extern int hx_ED_event_flag; +#endif + +#define HIMAX_PROC_DEBUG_LEVEL_FILE "debug_level" +#define HIMAX_PROC_VENDOR_FILE "vendor" +#define HIMAX_PROC_ATTN_FILE "attn" +#define HIMAX_PROC_INT_EN_FILE "int_en" +#define HIMAX_PROC_LAYOUT_FILE "layout" +#define HIMAX_PROC_CRC_TEST_FILE "CRC_test" + +static struct proc_dir_entry *himax_proc_debug_level_file; +static struct proc_dir_entry *himax_proc_vendor_file; +static struct proc_dir_entry *himax_proc_attn_file; +static struct proc_dir_entry *himax_proc_int_en_file; +static struct proc_dir_entry *himax_proc_layout_file; +static struct proc_dir_entry *himax_proc_CRC_test_file; + +int himax_touch_proc_init(void); +void himax_touch_proc_deinit(void); +extern int himax_int_en_set(void); + +#define HIMAX_PROC_REGISTER_FILE "register" +struct proc_dir_entry *himax_proc_register_file = NULL; +uint8_t byte_length = 0; +uint8_t register_command[4]; +uint8_t cfg_flag = 0; + +#define HIMAX_PROC_DIAG_FILE "diag" +struct proc_dir_entry *himax_proc_diag_file = NULL; +#define HIMAX_PROC_DIAG_ARR_FILE "diag_arr" +struct proc_dir_entry *himax_proc_diag_arrange_file = NULL; +struct file *diag_sram_fn; +uint8_t write_counter = 0; +uint8_t write_max_count = 30; +#define IIR_DUMP_FILE "/sdcard/HX_IIR_Dump.txt" +#define DC_DUMP_FILE "/sdcard/HX_DC_Dump.txt" +#define BANK_DUMP_FILE "/sdcard/HX_BANK_Dump.txt" +#ifdef HX_TP_PROC_2T2R + static uint8_t x_channel_2; + static uint8_t y_channel_2; + static uint32_t *diag_mutual_2; + int32_t *getMutualBuffer_2(void); + uint8_t getXChannel_2(void); + uint8_t getYChannel_2(void); + void setMutualBuffer_2(void); + void setXChannel_2(uint8_t x); + void setYChannel_2(uint8_t y); +#endif +uint8_t x_channel = 0; +uint8_t y_channel = 0; +int32_t *diag_mutual = NULL; +int32_t *diag_mutual_new = NULL; +int32_t *diag_mutual_old = NULL; +uint8_t diag_max_cnt = 0; +uint8_t hx_state_info[2] = {0}; +uint8_t diag_coor[128]; +int32_t diag_self[100] = {0}; +int32_t diag_self_new[100] = {0}; +int32_t diag_self_old[100] = {0}; +int32_t *getMutualBuffer(void); +int32_t *getMutualNewBuffer(void); +int32_t *getMutualOldBuffer(void); +int32_t *getSelfBuffer(void); +int32_t *getSelfNewBuffer(void); +int32_t *getSelfOldBuffer(void); +uint8_t getXChannel(void); +uint8_t getYChannel(void); +void setMutualBuffer(void); +void setMutualNewBuffer(void); +void setMutualOldBuffer(void); +void setXChannel(uint8_t x); +void setYChannel(uint8_t y); + +#define HIMAX_PROC_DEBUG_FILE "debug" +struct proc_dir_entry *himax_proc_debug_file = NULL; +#define HIMAX_PROC_FW_DEBUG_FILE "FW_debug" +struct proc_dir_entry *himax_proc_fw_debug_file = NULL; +#define HIMAX_PROC_DD_DEBUG_FILE "DD_debug" +struct proc_dir_entry *himax_proc_dd_debug_file = NULL; +bool fw_update_complete = false; +int handshaking_result = 0; +unsigned char debug_level_cmd = 0; +uint8_t cmd_set[8]; +uint8_t mutual_set_flag = 0; + +#define HIMAX_PROC_FLASH_DUMP_FILE "flash_dump" +struct proc_dir_entry *himax_proc_flash_dump_file = NULL; +static int Flash_Size = 131072; +static uint8_t *flash_buffer; +static uint8_t flash_command; +static uint8_t flash_read_step; +static uint8_t flash_progress; +static uint8_t flash_dump_complete; +static uint8_t flash_dump_fail; +static uint8_t sys_operation; +static bool flash_dump_going; +static uint8_t getFlashDumpComplete(void); +static uint8_t getFlashDumpFail(void); +static uint8_t getFlashDumpProgress(void); +static uint8_t getFlashReadStep(void); +uint8_t getFlashCommand(void); +uint8_t getSysOperation(void); +static void setFlashCommand(uint8_t command); +static void setFlashReadStep(uint8_t step); +void setFlashBuffer(void); +void setFlashDumpComplete(uint8_t complete); +void setFlashDumpFail(uint8_t fail); +void setFlashDumpProgress(uint8_t progress); +void setSysOperation(uint8_t operation); +void setFlashDumpGoing(bool going); +bool getFlashDumpGoing(void); + + +uint32_t **raw_data_array; +uint8_t X_NUM = 0, Y_NUM = 0; +uint8_t sel_type = 0x0D; + +#define HIMAX_PROC_RESET_FILE "reset" +struct proc_dir_entry *himax_proc_reset_file = NULL; + +#define HIMAX_PROC_SENSE_ON_OFF_FILE "SenseOnOff" +struct proc_dir_entry *himax_proc_SENSE_ON_OFF_file = NULL; + +#ifdef HX_ESD_RECOVERY + #define HIMAX_PROC_ESD_CNT_FILE "ESD_cnt" + struct proc_dir_entry *himax_proc_ESD_cnt_file = NULL; +#endif + +#endif diff --git a/drivers/input/touchscreen/himax_mmi/himax_ic_HX83102.c b/drivers/input/touchscreen/himax_mmi/himax_ic_HX83102.c new file mode 100644 index 000000000000..aa9604ef934e --- /dev/null +++ b/drivers/input/touchscreen/himax_mmi/himax_ic_HX83102.c @@ -0,0 +1,972 @@ +/* Himax Android Driver Sample Code for HX83102 chipset + + Copyright (C) 2018 Himax Corporation. + + 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. + +*/ + +#include "himax_ic_HX83102.h" +extern struct himax_ts_data *private_ts; +extern struct himax_core_fp g_core_fp; +extern struct fw_operation *pfw_op; +#if defined(HX_ZERO_FLASH) +extern struct zf_operation *pzf_op; +extern struct ic_operation *pic_op; +extern int G_POWERONOF; +#endif + + +extern unsigned char IC_CHECKSUM; +extern bool DSRAM_Flag; + +extern unsigned long FW_VER_MAJ_FLASH_ADDR; +extern unsigned long FW_VER_MIN_FLASH_ADDR; +extern unsigned long CFG_VER_MAJ_FLASH_ADDR; +extern unsigned long CFG_VER_MIN_FLASH_ADDR; +extern unsigned long CID_VER_MAJ_FLASH_ADDR; +extern unsigned long CID_VER_MIN_FLASH_ADDR; + +extern unsigned long FW_VER_MAJ_FLASH_LENG; +extern unsigned long FW_VER_MIN_FLASH_LENG; +extern unsigned long CFG_VER_MAJ_FLASH_LENG; +extern unsigned long CFG_VER_MIN_FLASH_LENG; +extern unsigned long CID_VER_MAJ_FLASH_LENG; +extern unsigned long CID_VER_MIN_FLASH_LENG; + +#ifdef HX_AUTO_UPDATE_FW + extern int g_i_FW_VER; + extern int g_i_CFG_VER; + extern int g_i_CID_MAJ; + extern int g_i_CID_MIN; + extern unsigned char *i_CTPM_FW; +#endif + +#ifdef HX_TP_PROC_2T2R + extern bool Is_2T2R; +#endif + +#ifdef HX_USB_DETECT_GLOBAL + extern void himax_cable_detect_func(bool force_renew); +#endif + +#ifdef HX_RST_PIN_FUNC + extern void himax_rst_gpio_set(int pinnum, uint8_t value); +#endif + +static void hx83102_chip_init(void) +{ + private_ts->chip_cell_type = CHIP_IS_IN_CELL; + I("%s:IC cell type = %d\n", __func__, private_ts->chip_cell_type); + IC_CHECKSUM = HX_TP_BIN_CHECKSUM_CRC; + /*Himax: Set FW and CFG Flash Address*/ + FW_VER_MAJ_FLASH_ADDR = 49157; /*0x00C005*/ + FW_VER_MAJ_FLASH_LENG = 1; + FW_VER_MIN_FLASH_ADDR = 49158; /*0x00C006*/ + FW_VER_MIN_FLASH_LENG = 1; + CFG_VER_MAJ_FLASH_ADDR = 49408; /*0x00C100*/ + CFG_VER_MAJ_FLASH_LENG = 1; + CFG_VER_MIN_FLASH_ADDR = 49409; /*0x00C101*/ + CFG_VER_MIN_FLASH_LENG = 1; + CID_VER_MAJ_FLASH_ADDR = 49154; /*0x00C002*/ + CID_VER_MAJ_FLASH_LENG = 1; + CID_VER_MIN_FLASH_ADDR = 49155; /*0x00C003*/ + CID_VER_MIN_FLASH_LENG = 1; + /*PANEL_VERSION_ADDR = 49156;*/ /*0x00C004*/ + /*PANEL_VERSION_LENG = 1;*/ + +} + +void hx83102_burst_enable(uint8_t auto_add_4_byte) +{ + uint8_t tmp_data[4]; + + tmp_data[0] = 0x31; + if (himax_bus_write(0x13, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + tmp_data[0] = (0x10 | auto_add_4_byte); + if (himax_bus_write(0x0D, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } +} + +int hx83102_flash_write_burst(uint8_t *reg_byte, uint8_t *write_data) +{ + uint8_t data_byte[8]; + int i = 0, j = 0; + + for (i = 0; i < 4; i++) { + data_byte[i] = reg_byte[i]; + } + for (j = 4; j < 8; j++) { + data_byte[j] = write_data[j-4]; + } + + if (himax_bus_write(0x00, data_byte, 8, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + return 0; +} + +static int hx83102_register_read(uint8_t *read_addr, int read_length, uint8_t *read_data) +{ + uint8_t tmp_data[4]; + int i = 0; + int address = 0; + + if (read_length > 256) { + E("%s: read len over 256!\n", __func__); + return LENGTH_FAIL; + } + if (read_length > 4) + hx83102_burst_enable(1); + else + hx83102_burst_enable(0); + + address = (read_addr[3] << 24) + (read_addr[2] << 16) + (read_addr[1] << 8) + read_addr[0]; + i = address; + tmp_data[0] = (uint8_t)i; + tmp_data[1] = (uint8_t)(i >> 8); + tmp_data[2] = (uint8_t)(i >> 16); + tmp_data[3] = (uint8_t)(i >> 24); + if (himax_bus_write(0x00, tmp_data, 4, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + tmp_data[0] = 0x00; + if (himax_bus_write(0x0C, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + if (himax_bus_read(0x08, read_data, read_length, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + if (read_length > 4) + hx83102_burst_enable(0); + + return 0; +} + +static bool hx83102_sense_off(void) +{ + uint8_t cnt = 0; + uint8_t tmp_addr[FOUR_BYTE_DATA_SZ]; + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + + do { + /*=========================================== + I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + ===========================================*/ + tmp_data[0] = 0x27; + if (himax_bus_write(0x31, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + ===========================================*/ + tmp_data[0] = 0x95; + if (himax_bus_write(0x32, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + Check enter_save_mode + ======================*/ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, FOUR_BYTE_ADDR_SZ, tmp_data); + I("%s: Check enter_save_mode data[0]=%X \n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /*===================================== + Reset TCON + =====================================*/ + tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + msleep(1); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + /*===================================== + Reset ADC + =====================================*/ + tmp_addr[3] = 0x80; + tmp_addr[2] = 0x02; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + msleep(1); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + + return true; + } else { + msleep(10); +#ifdef HX_RST_PIN_FUNC + himax_rst_gpio_set(private_ts->rst_gpio, 0); + msleep(20); + himax_rst_gpio_set(private_ts->rst_gpio, 1); + msleep(50); +#endif + } + } while (cnt++ < 15); + + return false; +} + +static void hx83102ab_set_SMWP_enable(uint8_t SMWP_enable, bool suspended) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + uint8_t back_data[FOUR_BYTE_DATA_SZ]; + uint8_t retry_cnt = 0; + g_core_fp.fp_sense_off(); + + do { + if (SMWP_enable) { + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + g_core_fp.fp_flash_write_burst(pfw_op->addr_smwp_enable, tmp_data); + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, back_data, 4); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_flash_write_burst(pfw_op->addr_smwp_enable, tmp_data); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + } + + g_core_fp.fp_register_read(pfw_op->addr_smwp_enable, FOUR_BYTE_DATA_SZ, tmp_data, false); + /*I("%s: tmp_data[0]=%d, SMWP_enable=%d, retry_cnt=%d \n", __func__, tmp_data[0],SMWP_enable,retry_cnt);*/ + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); + + g_core_fp.fp_sense_on(0x00); +} + +static void hx83102ab_set_HSEN_enable(uint8_t HSEN_enable, bool suspended) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + + if (HSEN_enable) { + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + g_core_fp.fp_flash_write_burst(pfw_op->addr_hsen_enable, tmp_data); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_flash_write_burst(pfw_op->addr_hsen_enable, tmp_data); + } + + /*I("%s: tmp_data[0]=%d, HSEN_enable=%d, retry_cnt=%d \n", __func__, tmp_data[0],HSEN_enable,retry_cnt);*/ +} + +static void hx83102ab_usb_detect_set(uint8_t *cable_config) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + + if (cable_config[1] == 0x01) { + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + g_core_fp.fp_flash_write_burst(pfw_op->addr_usb_detect, tmp_data); + I("%s: USB detect status IN!\n", __func__); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_flash_write_burst(pfw_op->addr_usb_detect, tmp_data); + I("%s: USB detect status OUT!\n", __func__); + } +} + +static uint8_t hx83102ab_read_DD_status(uint8_t *cmd_set, uint8_t *tmp_data) +{ + int cnt = 0; + uint8_t req_size = cmd_set[0]; + cmd_set[3] = pfw_op->data_dd_request[0]; + g_core_fp.fp_register_write(pfw_op->addr_dd_handshak_addr, FOUR_BYTE_DATA_SZ, cmd_set, 0); + I("%s: cmd_set[0] = 0x%02X,cmd_set[1] = 0x%02X,cmd_set[2] = 0x%02X,cmd_set[3] = 0x%02X\n", + __func__, cmd_set[0], cmd_set[1], cmd_set[2], cmd_set[3]); + + /* Doing hand shaking 0xAA -> 0xBB */ + for (cnt = 0; cnt < 100; cnt++) { + g_core_fp.fp_register_read(pfw_op->addr_dd_handshak_addr, FOUR_BYTE_DATA_SZ, tmp_data, false); + msleep(10); + + if (tmp_data[3] == pfw_op->data_dd_ack[0]) { + I("%s Data ready goto moving data\n", __func__); + break; + } else { + if (cnt >= 99) { + I("%s Data not ready in FW \n", __func__); + return FW_NOT_READY; + } + } + } + + g_core_fp.fp_sense_off(); + g_core_fp.fp_register_read(pfw_op->addr_dd_data_addr, req_size, tmp_data, false); + g_core_fp.fp_sense_on(0x01); + return NO_ERR; +} + +static int hx83102ab_read_FW_status(uint8_t *state_addr, uint8_t *tmp_addr) +{ + uint8_t i; + uint8_t req_size = 0; + uint8_t status_addr[FOUR_BYTE_DATA_SZ]; /*0x10007F44*/ + uint8_t cmd_addr[FOUR_BYTE_DATA_SZ]; /*0x900000F8*/ + + if (state_addr[0] == 0x01) { + state_addr[1] = 0x04; + + for (i = 0; i < FOUR_BYTE_DATA_SZ; i++) { + /* 0x10007F44 */ + state_addr[i + 2] = pfw_op->addr_fw_dbg_msg_addr[i]; + status_addr[i] = pfw_op->addr_fw_dbg_msg_addr[i]; + } + + req_size = 0x04; + g_core_fp.fp_sense_off(); + g_core_fp.fp_register_read(status_addr, req_size, tmp_addr, false); + g_core_fp.fp_sense_on(0x01); + } else if (state_addr[0] == 0x02) { + state_addr[1] = 0x30; + + for (i = 0; i < FOUR_BYTE_DATA_SZ; i++) { + /* 0x900000F8 */ + state_addr[i + 2] = pfw_op->addr_fw_state_addr[i]; + cmd_addr[i] = pfw_op->addr_fw_state_addr[i]; + } + + req_size = 0x30; + g_core_fp.fp_register_read(cmd_addr, req_size, tmp_addr, false); + } + + return NO_ERR; +} + +static void hx83102ab_power_on_init(void) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + I("%s:\n", __func__); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_flash_write_burst(pfw_op->addr_raw_out_sel, tmp_data); + g_core_fp.fp_flash_write_burst(pfw_op->addr_sorting_mode_en, tmp_data); + g_core_fp.fp_touch_information(); + g_core_fp.fp_sense_on(0x00); +} + +static int hx83102ab_fts_ctpm_fw_upgrade_with_sys_fs_64k(unsigned char *fw, int len, bool change_iref) +{ + int burnFW_success = 0; + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + + I("[%s][%d]:enter\n", __func__, __LINE__); + + if (len != FW_SIZE_64k) { + E("%s: The file size is not 64K bytes\n", __func__); + return false; + } + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + g_core_fp.fp_sense_off(); + g_core_fp.fp_chip_erase(); + g_core_fp.fp_flash_programming(fw, FW_SIZE_64k); + + if (g_core_fp.fp_check_CRC(pfw_op->addr_program_reload_from, FW_SIZE_64k) == 0) { + burnFW_success = 1; + } + + I("[%s][%d]\n", __func__, __LINE__); + + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_flash_write_burst(pfw_op->addr_raw_out_sel, tmp_data); + g_core_fp.fp_flash_write_burst(pfw_op->addr_sorting_mode_en, tmp_data); + I("[%s][%d]\n", __func__, __LINE__); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + /*System reset*/ + g_core_fp.fp_system_reset(); +#endif + + I("[%s][%d]\n", __func__, __LINE__); + return burnFW_success; +} + +#ifdef HX_ESD_RECOVERY +static void hx83102ab_esd_ic_reset(void) +{ + HX_ESD_RESET_ACTIVATE = 1; +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_pin_reset(); +#endif + I("%s:\n", __func__); +} +#endif + +#if defined(HX_ZERO_FLASH) +static void himax_hx83102d_reload_to_active(void) +{ + uint8_t addr[FOUR_BYTE_DATA_SZ] = {0}; + uint8_t data[FOUR_BYTE_DATA_SZ] = {0}; + uint8_t retry_cnt = 0; + + addr[3] = 0x90; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x48; + + do { + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0xEC; + g_core_fp.fp_flash_write_burst(addr, data); + + g_core_fp.fp_register_read(addr, FOUR_BYTE_DATA_SZ, data, 0); + I("%s: data[1]=%d, data[0]=%d, retry_cnt=%d \n", __func__, data[1], data[0], retry_cnt); + retry_cnt++; + } while ((data[1] != 0x01 || data[0] != 0xEC) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} + +static void himax_hx83102d_resume_ic_action(void) +{ +#ifndef HX_RESUME_HW_RESET + himax_hx83102d_reload_to_active(); +#endif +} + +static void himax_hx83102d_suspend_ic_action(void) +{ +#ifndef HX_RESUME_HW_RESET + himax_hx83102d_reload_to_active(); +#endif +} + +static void himax_hx83102d_sense_on(uint8_t FlashMode) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + int retry = 0; + I("Enter %s \n", __func__); + g_core_fp.fp_interface_on(); + g_core_fp.fp_register_write(pfw_op->addr_ctrl_fw_isr, + sizeof(pfw_op->data_clear), pfw_op->data_clear, false); + msleep(20); + + if (!FlashMode) { +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + himax_hx83102d_reload_to_active(); + } else { + himax_hx83102d_reload_to_active(); + do { + g_core_fp.fp_register_write(pfw_op->addr_safe_mode_release_pw, + sizeof(pfw_op->data_safe_mode_release_pw_active), pfw_op->data_safe_mode_release_pw_active, false); + + g_core_fp.fp_register_read(pfw_op->addr_flag_reset_event, FOUR_BYTE_DATA_SZ, tmp_data, 0); + I("%s:Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + } while ((tmp_data[1] != 0x01 || tmp_data[0] != 0x00) && retry++ < 5); + + if (retry >= 5) { + E("%s: Fail:\n", __func__); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + himax_hx83102d_reload_to_active(); + } else { + I("%s:OK and Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + /* reset code*/ + tmp_data[0] = 0x00; + + if (himax_bus_write(pic_op->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + } + + if (himax_bus_write(pic_op->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + } + + g_core_fp.fp_register_write(pfw_op->addr_safe_mode_release_pw, + sizeof(pfw_op->data_safe_mode_release_pw_reset), pfw_op->data_safe_mode_release_pw_reset, false); + } + } +} + +static void hx83102d_firmware_update_0f(const struct firmware *fw_entry) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ] = {0}; + int retry = 0; + int crc = -1; + + I("%s, Entering \n", __func__); + + g_core_fp.fp_register_write(pzf_op->addr_system_reset, 4, pzf_op->data_system_reset, false); + + g_core_fp.fp_sense_off(); + + /* first 40K - isram */ + do { + g_core_fp.fp_write_sram_0f (fw_entry, pzf_op->data_sram_start_addr, 0, HX_40K_SZ); + crc = g_core_fp.fp_check_CRC (pzf_op->data_sram_start_addr, HX_40K_SZ); + if (crc == 0) { + I("%s, HW CRC OK in %d time \n", __func__, retry); + break; + } else { + E("%s, HW CRC FAIL in %d time !\n", __func__, retry); + } + retry++; + } while (crc != 0 && retry < 3); + + if (crc != 0) { + E("Last time CRC Fail!\n"); + return; + } + + /* clean + if (G_POWERONOF == 1) { + g_core_fp.fp_clean_sram_0f(pzf_op->data_sram_clean, HX_32K_SZ, 0); + } */ + + /*last 16k*/ + /*config info*/ + if (G_POWERONOF == 1) { + g_core_fp.fp_write_sram_0f(fw_entry, pzf_op->data_cfg_info, 0xC000, 132); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_cfg_info, 132, 2); + } + /*FW config*/ + if (G_POWERONOF == 1) { + g_core_fp.fp_write_sram_0f(fw_entry, pzf_op->data_fw_cfg_1, 0xC0FE, 484); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_fw_cfg_1, 484, 1); + } + /*ADC config*/ + if (G_POWERONOF == 1) { + g_core_fp.fp_write_sram_0f(fw_entry, pzf_op->data_adc_cfg_1, 0xD000, 768); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_adc_cfg_1, 768, 2); + } + + if (G_POWERONOF == 1) { + g_core_fp.fp_write_sram_0f(fw_entry, pzf_op->data_adc_cfg_2, 0xD300, 1536); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_adc_cfg_2, 1536, 2); + } + + if (G_POWERONOF == 1) { + g_core_fp.fp_write_sram_0f(fw_entry, pzf_op->data_adc_cfg_3, 0xE000, 1536); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_adc_cfg_3, 1536, 2); + } + + /*border prevent info*/ + himax_in_parse_assign_cmd(hx83102d_zf_data_bor_prevent_info, tmp_data, 4); + if (G_POWERONOF == 1) { + g_core_fp.fp_write_sram_0f(fw_entry, tmp_data, 0xC9E0, 32); + } else { + g_core_fp.fp_clean_sram_0f(tmp_data, 32, 2); + } + /*notch info*/ + himax_in_parse_assign_cmd(hx83102d_zf_data_notch_info, tmp_data, 4); + if (G_POWERONOF == 1) { + g_core_fp.fp_write_sram_0f(fw_entry, tmp_data, 0xCA00, 128); + } else { + g_core_fp.fp_clean_sram_0f(tmp_data, 128, 2); + } + /*enable func info*/ + himax_in_parse_assign_cmd(hx83102d_zf_func_info_en, tmp_data, 4); + if (G_POWERONOF == 1) { + g_core_fp.fp_write_sram_0f(fw_entry, tmp_data, 0xCB00, 12); + } else { + g_core_fp.fp_clean_sram_0f(tmp_data, 12, 2); + } + /*power on sub func*/ + himax_in_parse_assign_cmd(hx83102d_zf_po_sub_func, tmp_data, 4); + if (G_POWERONOF == 1) { + retry = 0; + do { + g_core_fp.fp_write_sram_0f(fw_entry, tmp_data, 0xA000, HX4K); + crc = g_core_fp.fp_check_CRC (tmp_data, HX4K); + if (crc == 0) { + I("%s, power on sub func CRC OK in %d time \n", __func__, retry); + break; + } else { + E("%s, HW CRC FAIL in %d time !\n", __func__, retry); + I("%s, data[3] = %2X,data[2] = %2X,data[1] = %2X,data[0] = %2X !\n", __func__, + tmp_data[3], tmp_data[2], tmp_data[1], tmp_data[0]); + } + retry++; + } while (crc != 0 && retry < 3); + } else { + g_core_fp.fp_clean_sram_0f(tmp_data, 4096, 2); + } + + I("%s, END \n", __func__); +} + +static void hx83102ab_firmware_update_0f(const struct firmware *fw_entry) +{ + uint8_t tmp_addr[4]; + + I("%s, Entering \n", __func__); + + g_core_fp.fp_register_write(pzf_op->addr_system_reset, 4, pzf_op->data_system_reset, false); + + g_core_fp.fp_sense_off(); + + /* first 32K */ + /*bin file start*/ + /*isram*/ + g_core_fp.fp_write_sram_0f (fw_entry, pzf_op->data_sram_start_addr, 0, HX_32K_SZ); + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x73; + tmp_addr[0] = 0x00; + g_core_fp.fp_write_sram_0f (fw_entry, tmp_addr, 0x8000, (0x200*4)); + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x68; + tmp_addr[0] = 0x00; + g_core_fp.fp_write_sram_0f (fw_entry, tmp_addr, 0x8800, (0x200*4)); + + + /*last 16k*/ + /*config info*/ + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x72; + tmp_addr[0] = 0x04; /*0x10007206 <= size 60,from 0x0000C180*/ + g_core_fp.fp_write_sram_0f (fw_entry, tmp_addr, 0xC178, (84)); + + + + + /*FW config*/ + if (G_POWERONOF == 1) { + g_core_fp.fp_write_sram_0f(fw_entry, pzf_op->data_fw_cfg_1, 0xBFFE, 388); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_fw_cfg_1, 388, 1); + } + /*ADC config*/ + if (G_POWERONOF == 1) { + g_core_fp.fp_write_sram_0f(fw_entry, pzf_op->data_adc_cfg_1, 0xe000, (128*4)); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_adc_cfg_1, (128*4), 2); + } + + I("%s, END \n", __func__); +} +#if defined(HX_0F_DEBUG) +static void hx83102ab_firmware_read_0f(const struct firmware *fw_entry, int type) +{ + uint8_t tmp_addr[4]; + + I("%s, Entering \n", __func__); + if (type == 0) { /* first 32K */ + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_sram_start_addr, 0, HX_32K_SZ); + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x68; + tmp_addr[0] = 0x00; + g_core_fp.fp_read_sram_0f(fw_entry, tmp_addr, 0x8000, (0x200*4)); + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x73; + tmp_addr[0] = 0x00; + g_core_fp.fp_read_sram_0f(fw_entry, tmp_addr, 0x8800, (0x200*4)); + + } else { /*last 16k*/ + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x72; + tmp_addr[0] = 0x04; /*0x10007206 <= size 80,from 0x0000C180*/ + g_core_fp.fp_read_sram_0f(fw_entry, tmp_addr, 0xC178, (84)); + + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_fw_cfg_1, 0xBFFE, 388); + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_adc_cfg_1, 0xE000, (128*4)); + + } + I("%s, END \n", __func__); +} +static void hx83102d_firmware_read_0f(const struct firmware *fw_entry, int type) +{ + uint8_t tmp_data[4]; + + I("%s, Entering \n", __func__); + + switch (type) { + case 0: + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_sram_start_addr, 0, HX_40K_SZ); + break; + case 1: + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_cfg_info, 0xC000, 132); + break; + case 2: + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_fw_cfg_1, 0xC0FE, 484); + break; + case 3: + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_adc_cfg_1, 0xD000, 768); + break; + case 4: + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_adc_cfg_2, 0xD300, 1536); + break; + case 5: + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_adc_cfg_3, 0xE000, 1536); + break; + case 6: + himax_in_parse_assign_cmd(hx83102d_zf_data_bor_prevent_info, tmp_data, 4); + g_core_fp.fp_read_sram_0f(fw_entry, tmp_data, 0xC9E0, 32); + break; + case 7: + himax_in_parse_assign_cmd(hx83102d_zf_data_notch_info, tmp_data, 4); + g_core_fp.fp_read_sram_0f(fw_entry, tmp_data, 0xCA00, 128); + break; + case 8: + himax_in_parse_assign_cmd(hx83102d_zf_func_info_en, tmp_data, 4); + g_core_fp.fp_read_sram_0f(fw_entry, tmp_data, 0xCB00, 12); + break; + case 9: + himax_in_parse_assign_cmd(hx83102d_zf_po_sub_func, tmp_data, 4); + g_core_fp.fp_read_sram_0f(fw_entry, tmp_data, 0xA000, HX4K); + break; + default: + break; + } + I("%s, END \n", __func__); +} +#endif +#endif + +static void himax_hx83102ab_reg_re_init(void) +{ + I("%s:Entering!\n", __func__); + himax_in_parse_assign_cmd(hx83102ab_fw_addr_sorting_mode_en, pfw_op->addr_sorting_mode_en, sizeof(pfw_op->addr_sorting_mode_en)); + himax_in_parse_assign_cmd(hx83102ab_fw_addr_selftest_addr_en, pfw_op->addr_selftest_addr_en, sizeof(pfw_op->addr_selftest_addr_en)); +#ifdef HX_ZERO_FLASH + himax_in_parse_assign_cmd(hx83102ab_data_adc_cfg_1, pzf_op->data_adc_cfg_1, sizeof(pzf_op->data_adc_cfg_1)); +#endif + +} + +static void himax_hx83102ab_func_re_init(void) +{ + I("%s:Entering!\n", __func__); + g_core_fp.fp_chip_init = hx83102_chip_init; + g_core_fp.fp_set_SMWP_enable = hx83102ab_set_SMWP_enable; + g_core_fp.fp_set_HSEN_enable = hx83102ab_set_HSEN_enable; + g_core_fp.fp_usb_detect_set = hx83102ab_usb_detect_set; + g_core_fp.fp_read_DD_status = hx83102ab_read_DD_status; + g_core_fp.fp_read_FW_status = hx83102ab_read_FW_status; + g_core_fp.fp_power_on_init = hx83102ab_power_on_init; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_64k = hx83102ab_fts_ctpm_fw_upgrade_with_sys_fs_64k; +#ifdef HX_ESD_RECOVERY + g_core_fp.fp_esd_ic_reset = hx83102ab_esd_ic_reset; +#endif +#if defined(HX_ZERO_FLASH) + g_core_fp.fp_firmware_update_0f = hx83102ab_firmware_update_0f; +#if defined(HX_0F_DEBUG) + g_core_fp.fp_firmware_read_0f = hx83102ab_firmware_read_0f; +#endif +#endif + +} + +static void himax_hx83102d_reg_re_init(void) +{ + I("%s:Entering!\n", __func__); + himax_in_parse_assign_cmd(hx83102d_fw_addr_raw_out_sel, pfw_op->addr_raw_out_sel, sizeof(pfw_op->addr_raw_out_sel)); +#if defined(HX_ZERO_FLASH) + himax_in_parse_assign_cmd(hx83102d_zf_data_sram_start_addr, pzf_op->data_sram_start_addr, sizeof(pzf_op->data_sram_start_addr)); + himax_in_parse_assign_cmd(hx83102d_zf_data_adc_cfg_1, pzf_op->data_adc_cfg_1, sizeof(pzf_op->data_adc_cfg_1)); + himax_in_parse_assign_cmd(hx83102d_zf_data_adc_cfg_2, pzf_op->data_adc_cfg_2, sizeof(pzf_op->data_adc_cfg_2)); + himax_in_parse_assign_cmd(hx83102d_zf_data_adc_cfg_3, pzf_op->data_adc_cfg_3, sizeof(pzf_op->data_adc_cfg_3)); +#endif +} + +static void himax_hx83102d_func_re_init(void) +{ + I("%s:Entering!\n", __func__); + g_core_fp.fp_chip_init = hx83102_chip_init; +#if defined(HX_ZERO_FLASH) + g_core_fp.fp_firmware_update_0f = hx83102d_firmware_update_0f; + g_core_fp.fp_resume_ic_action = himax_hx83102d_resume_ic_action; + g_core_fp.fp_suspend_ic_action = himax_hx83102d_suspend_ic_action; + g_core_fp.fp_sense_on = himax_hx83102d_sense_on; +#if defined(HX_0F_DEBUG) + g_core_fp.fp_firmware_read_0f = hx83102d_firmware_read_0f; +#endif +#endif + +} + +bool hx83102_chip_detect(void) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + uint8_t tmp_addr[FOUR_BYTE_DATA_SZ]; + bool ret_data = false; + int i = 0; + + I("[%s][%d]:enter\n", __func__, __LINE__); + + msleep(50); + hx83102_sense_off(); + + for (i = 0; i < 5; i++) { + tmp_addr[3] = 0x90; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0xD0; + hx83102_register_read(tmp_addr, FOUR_BYTE_DATA_SZ, tmp_data); + I("%s:Read driver IC ID = %X,%X,%X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1]); /*83,10,2X*/ + + if ((tmp_data[3] == 0x83) && (tmp_data[2] == 0x10) && ((tmp_data[1] == 0x2a) || (tmp_data[1] == 0x2b) || (tmp_data[1] == 0x2d))) { + if (tmp_data[1] == 0x2a) { + strlcpy(private_ts->chip_name, HX_83102A_SERIES_PWON, 30); + I("%s:detect IC HX83102A successfully\n", __func__); + } else if (tmp_data[1] == 0x2b) { + strlcpy(private_ts->chip_name, HX_83102B_SERIES_PWON, 30); + I("%s:detect IC HX83102B successfully\n", __func__); + } else { + strlcpy(private_ts->chip_name, HX_83102D_SERIES_PWON, 30); + I("%s:detect IC HX83102D successfully\n", __func__); + } + himax_mcu_in_cmd_struct_init(); + himax_mcu_in_cmd_init(); + if ((tmp_data[1] == 0x2a) || (tmp_data[1] == 0x2b)) { + himax_hx83102ab_reg_re_init(); + himax_hx83102ab_func_re_init(); + } else { + himax_hx83102d_reg_re_init(); + himax_hx83102d_func_re_init(); + } + ret_data = true; + break; + } else { + ret_data = false; + E("%s:Read driver ID register Fail:\n", __func__); + E("Could NOT find Himax Chipset \n"); + E("Please check 1.VCCD,VCCA,VSP,VSN \n"); + E("2. LCM_RST,TP_RST \n"); + E("3. Power On Sequence \n"); + } + } + + return ret_data; +} + +#if 0 +#ifdef CONFIG_CHIP_DTCFG +static int himax_hx83102_probe(struct platform_device *pdev) +{ + I("%s:Enter\n", __func__); + + g_core_fp.fp_chip_detect = hx83102_chip_detect; + + return 0; +} + +static int himax_hx83102_remove(struct platform_device *pdev) +{ + g_core_fp.fp_chip_detect = NULL; + g_core_fp.fp_chip_init = NULL; + return 0; +} + + +#ifdef CONFIG_OF +static const struct of_device_id himax_hx83102_mttable[] = { + { .compatible = "himax,hx83102"}, + { }, +}; +#else +#define himax_hx83102_mttabl NULL +#endif + +static struct platform_driver himax_hx83102_driver = { + .probe = himax_hx83102_probe, + .remove = himax_hx83102_remove, + .driver = { + .name = "HIMAX_HX83102", + .owner = THIS_MODULE, + .of_match_table = himax_hx83102_mttable, + }, +}; + +static int __init himax_hx83102_init(void) +{ + I("%s\n", __func__); + platform_driver_register(&himax_hx83102_driver); + return 0; +} + +static void __exit himax_hx83102_exit(void) +{ + platform_driver_unregister(&himax_hx83102_driver); +} + +#else +static int himax_hx83102_probe(void) +{ + I("%s:Enter\n", __func__); + + g_core_fp.fp_chip_detect = hx83102_chip_detect; + + return 0; +} + +static int himax_hx83102_remove(void) +{ + g_core_fp.fp_chip_detect = NULL; + g_core_fp.fp_chip_init = NULL; + return 0; +} + +static int __init himax_hx83102_init(void) +{ + int ret = 0; + + I("%s\n", __func__); + ret = himax_hx83102_probe(); + return 0; +} + +static void __exit himax_hx83102_exit(void) +{ + himax_hx83102_remove(); +} +#endif + +module_init(himax_hx83102_init); +module_exit(himax_hx83102_exit); + +MODULE_DESCRIPTION("HIMAX HX83102 touch driver"); +MODULE_LICENSE("GPL"); +#endif diff --git a/drivers/input/touchscreen/himax_mmi/himax_ic_HX83102.h b/drivers/input/touchscreen/himax_mmi/himax_ic_HX83102.h new file mode 100644 index 000000000000..8513c991352d --- /dev/null +++ b/drivers/input/touchscreen/himax_mmi/himax_ic_HX83102.h @@ -0,0 +1,39 @@ +/* Himax Android Driver Sample Code for HX83102 chipset + + Copyright (C) 2018 Himax Corporation. + + 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. + +*/ + +#include "himax_platform.h" +#include "himax_common.h" +#include "himax_ic_core.h" +#include + +#define hx83102ab_fw_addr_sorting_mode_en 0x100007FC +#define hx83102ab_fw_addr_selftest_addr_en 0x100007F8 +#define hx83102ab_data_adc_cfg_1 0x10007B00 + +#define hx83102d_fw_addr_raw_out_sel 0x800204f4 +#define hx83102d_zf_data_adc_cfg_1 0x10007B00 +#define hx83102d_zf_data_adc_cfg_2 0x10006A00 +#define hx83102d_zf_data_adc_cfg_3 0x10007500 +#define hx83102d_zf_data_bor_prevent_info 0x10007268 +#define hx83102d_zf_data_notch_info 0x10007300 +#define hx83102d_zf_func_info_en 0x10007FD0 +#define hx83102d_zf_po_sub_func 0x10005A00 +#define hx83102d_zf_data_sram_start_addr 0x20000000 + + +#ifdef HX_ESD_RECOVERY + extern u8 HX_ESD_RESET_ACTIVATE; +#endif + diff --git a/drivers/input/touchscreen/himax_mmi/himax_ic_HX83112.c b/drivers/input/touchscreen/himax_mmi/himax_ic_HX83112.c new file mode 100644 index 000000000000..bb17eb4886dc --- /dev/null +++ b/drivers/input/touchscreen/himax_mmi/himax_ic_HX83112.c @@ -0,0 +1,316 @@ +/* Himax Android Driver Sample Code for HX83112 chipset + + Copyright (C) 2018 Himax Corporation. + + 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. + +*/ + +#include "himax_ic_HX83112.h" +extern struct himax_ts_data *private_ts; +extern struct himax_core_fp g_core_fp; +extern struct ic_operation *pic_op; +extern struct fw_operation *pfw_op; +#ifdef HX_ZERO_FLASH +extern struct zf_operation *pzf_op; +#endif + +extern int i2c_error_count; +extern unsigned char IC_TYPE; +extern unsigned char IC_CHECKSUM; +extern bool DSRAM_Flag; + +extern unsigned long FW_VER_MAJ_FLASH_ADDR; +extern unsigned long FW_VER_MIN_FLASH_ADDR; +extern unsigned long CFG_VER_MAJ_FLASH_ADDR; +extern unsigned long CFG_VER_MIN_FLASH_ADDR; +extern unsigned long CID_VER_MAJ_FLASH_ADDR; +extern unsigned long CID_VER_MIN_FLASH_ADDR; + +extern unsigned long FW_VER_MAJ_FLASH_LENG; +extern unsigned long FW_VER_MIN_FLASH_LENG; +extern unsigned long CFG_VER_MAJ_FLASH_LENG; +extern unsigned long CFG_VER_MIN_FLASH_LENG; +extern unsigned long CID_VER_MAJ_FLASH_LENG; +extern unsigned long CID_VER_MIN_FLASH_LENG; + +#ifdef HX_AUTO_UPDATE_FW +extern int g_i_FW_VER; +extern int g_i_CFG_VER; +extern int g_i_CID_MAJ; +extern int g_i_CID_MIN; +extern unsigned char *i_CTPM_FW; +#endif + +#ifdef HX_TP_PROC_2T2R + extern bool Is_2T2R; +#endif + +#ifdef HX_USB_DETECT_GLOBAL +extern void himax_cable_detect_func (bool force_renew); +#endif + + +static void hx83112_chip_init (void) +{ + + private_ts->chip_cell_type = CHIP_IS_IN_CELL; + I ("%s:IC cell type = %d\n", __func__, private_ts->chip_cell_type); + IC_CHECKSUM = HX_TP_BIN_CHECKSUM_CRC; + /*Himax: Set FW and CFG Flash Address*/ + FW_VER_MAJ_FLASH_ADDR = 49157; /*0x00C005*/ + FW_VER_MAJ_FLASH_LENG = 1; + FW_VER_MIN_FLASH_ADDR = 49158; /*0x00C006*/ + FW_VER_MIN_FLASH_LENG = 1; + CFG_VER_MAJ_FLASH_ADDR = 49408; /*0x00C100*/ + CFG_VER_MAJ_FLASH_LENG = 1; + CFG_VER_MIN_FLASH_ADDR = 49409; /*0x00C101*/ + CFG_VER_MIN_FLASH_LENG = 1; + CID_VER_MAJ_FLASH_ADDR = 49154; /*0x00C002*/ + CID_VER_MAJ_FLASH_LENG = 1; + CID_VER_MIN_FLASH_ADDR = 49155; /*0x00C003*/ + CID_VER_MIN_FLASH_LENG = 1; +} + +static bool hx83112_sense_off(void) +{ + uint8_t cnt = 0; + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + + do { + /*=========================================== + I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + ===========================================*/ + tmp_data[0] = pic_op->data_i2c_psw_lb[0]; + + if (himax_bus_write(pic_op->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + ===========================================*/ + tmp_data[0] = pic_op->data_i2c_psw_ub[0]; + + if (himax_bus_write(pic_op->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x00 + ===========================================*/ + tmp_data[0] = 0x00; + + if (himax_bus_write(pic_op->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + ===========================================*/ + tmp_data[0] = pic_op->data_i2c_psw_lb[0]; + + if (himax_bus_write(pic_op->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + ===========================================*/ + tmp_data[0] = pic_op->data_i2c_psw_ub[0]; + + if (himax_bus_write(pic_op->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + Check enter_save_mode + ======================*/ + g_core_fp.fp_register_read(pic_op->addr_cs_central_state, FOUR_BYTE_ADDR_SZ, tmp_data, 0); + I("%s: Check enter_save_mode data[0]=%X \n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /*===================================== + Reset TCON + =====================================*/ + g_core_fp.fp_flash_write_burst(pic_op->addr_tcon_on_rst, pic_op->data_rst); + msleep(1); + tmp_data[3] = pic_op->data_rst[3]; + tmp_data[2] = pic_op->data_rst[2]; + tmp_data[1] = pic_op->data_rst[1]; + tmp_data[0] = pic_op->data_rst[0] | 0x01; + g_core_fp.fp_flash_write_burst(pic_op->addr_tcon_on_rst, tmp_data); + /*===================================== + Reset ADC + =====================================*/ + g_core_fp.fp_flash_write_burst(pic_op->addr_adc_on_rst, pic_op->data_rst); + msleep(1); + tmp_data[3] = pic_op->data_rst[3]; + tmp_data[2] = pic_op->data_rst[2]; + tmp_data[1] = pic_op->data_rst[1]; + tmp_data[0] = pic_op->data_rst[0] | 0x01; + g_core_fp.fp_flash_write_burst(pic_op->addr_adc_on_rst, tmp_data); + return true; + } else { + msleep(10); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#endif + } + } while (cnt++ < 15); + + return false; +} + +static void hx83112_func_re_init (void) +{ + g_core_fp.fp_sense_off = hx83112_sense_off; + g_core_fp.fp_chip_init = hx83112_chip_init; +} + +static void hx83112_reg_re_init (void) +{ +} + +bool hx83112_chip_detect (void) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + bool ret_data = false; + int i = 0; + himax_mcu_in_cmd_struct_init(); + himax_mcu_in_cmd_init(); + + hx83112_reg_re_init(); + hx83112_func_re_init(); + + g_core_fp.fp_sense_off(); + + for (i = 0; i < 5; i++) { + g_core_fp.fp_register_read (pfw_op->addr_icid_addr, FOUR_BYTE_DATA_SZ, tmp_data, false); + I("%s:Read driver IC ID = %X, %X, %X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1]); + + if ((tmp_data[3] == 0x83) && (tmp_data[2] == 0x11) && ((tmp_data[1] == 0x2a) || (tmp_data[1] == 0x2b) || (tmp_data[1] == 0x2e))) { + if (tmp_data[1] == 0x2a) { + strlcpy (private_ts->chip_name, HX_83112A_SERIES_PWON, 30); + } else if (tmp_data[1] == 0x2b) { + strlcpy (private_ts->chip_name, HX_83112B_SERIES_PWON, 30); + } + + I("%s:IC name = %s\n", __func__, private_ts->chip_name); + + I("Himax IC package %x%x%x in\n", tmp_data[3], tmp_data[2], tmp_data[1]); + ret_data = true; + break; + } else { + ret_data = false; + E("%s:Read driver ID register Fail:\n", __func__); + E("Could NOT find Himax Chipset \n"); + E("Please check 1.VCCD,VCCA,VSP,VSN \n"); + E("2. LCM_RST,TP_RST \n"); + E("3. Power On Sequence \n"); + } + } + + return ret_data; +} + +#if 0 +#ifdef CONFIG_CHIP_DTCFG +static int himax_hx83112_probe(struct platform_device *pdev) +{ + I("%s:Enter\n", __func__); + g_core_fp.fp_chip_detect = hx83112_chip_detect; + + return 0; +} + +static int himax_hx83112_remove(struct platform_device *pdev) +{ + g_core_fp.fp_chip_detect = NULL; + g_core_fp.fp_chip_init = NULL; + return 0; +} + + +#ifdef CONFIG_OF +static const struct of_device_id himax_hx83112_mttable[] = { + { .compatible = "himax,hx83112"}, + { }, +}; +#else +#define himax_hx83112_mttabl NULL +#endif + +static struct platform_driver himax_hx83112_driver = { + .probe = himax_hx83112_probe, + .remove = himax_hx83112_remove, + .driver = { + .name = "HIMAX_HX83112", + .owner = THIS_MODULE, + .of_match_table = himax_hx83112_mttable, + }, +}; + +static int __init himax_hx83112_init(void) +{ + I("%s\n", __func__); + platform_driver_register(&himax_hx83112_driver); + return 0; +} + +static void __exit himax_hx83112_exit(void) +{ + platform_driver_unregister(&himax_hx83112_driver); +} + +#else +static int himax_hx83112_probe(void) +{ + I("%s:Enter\n", __func__); + + g_core_fp.fp_chip_detect = hx83112_chip_detect; + + return 0; +} + +static int himax_hx83112_remove(void) +{ + g_core_fp.fp_chip_detect = NULL; + g_core_fp.fp_chip_init = NULL; + return 0; +} + +static int __init himax_hx83112_init(void) +{ + int ret = 0; + + I("%s\n", __func__); + ret = himax_hx83112_probe(); + return 0; +} + +static void __exit himax_hx83112_exit(void) +{ + himax_hx83112_remove(); +} +#endif + +module_init(himax_hx83112_init); +module_exit(himax_hx83112_exit); + +MODULE_DESCRIPTION("HIMAX HX83112 touch driver"); +MODULE_LICENSE("GPL"); +#endif + diff --git a/drivers/input/touchscreen/himax_mmi/himax_ic_HX83112.h b/drivers/input/touchscreen/himax_mmi/himax_ic_HX83112.h new file mode 100644 index 000000000000..f861ad55a485 --- /dev/null +++ b/drivers/input/touchscreen/himax_mmi/himax_ic_HX83112.h @@ -0,0 +1,19 @@ +/* Himax Android Driver Sample Code for HX83112 chipset + + Copyright (C) 2018 Himax Corporation. + + 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. + +*/ + +#include "himax_platform.h" +#include "himax_common.h" +#include "himax_ic_core.h" +#include diff --git a/drivers/input/touchscreen/himax_mmi/himax_ic_core.h b/drivers/input/touchscreen/himax_mmi/himax_ic_core.h new file mode 100644 index 000000000000..9dc22158814d --- /dev/null +++ b/drivers/input/touchscreen/himax_mmi/himax_ic_core.h @@ -0,0 +1,724 @@ +#include "himax_platform.h" +#include "himax_common.h" +#include + +#define EIGHT_BYTE_DATA_SZ 8 +#define FOUR_BYTE_DATA_SZ 4 +#define FOUR_BYTE_ADDR_SZ 4 +#define FLASH_RW_MAX_LEN 256 +#define FLASH_WRITE_BURST_SZ 8 +#define PROGRAM_SZ 48 +#define MAX_I2C_TRANS_SZ 128 +#define HIMAX_REG_RETRY_TIMES 5 +#define FW_BIN_16K_SZ 0x4000 +#define HIMAX_TOUCH_DATA_SIZE 128 +#define MASK_BIT_0 0x01 +#define MASK_BIT_1 0x02 +#define MASK_BIT_2 0x04 + +#define FW_SECTOR_PER_BLOCK 8 +#define FW_PAGE_PER_SECTOR 64 +#define FW_PAGE_SEZE 128 +#define HX256B 0x100 +#define HX4K 0x1000 +#define HX_32K_SZ 0x8000 +#define HX_40K_SZ 0xA000 +#define HX_48K_SZ 0xC000 +#define HX64K 0x10000 +#define HX124K 0x1f000 +#define HX4000K 0x1000000 + +#define HX_NORMAL_MODE 1 +#define HX_SORTING_MODE 2 +#define HX_CHANGE_MODE_FAIL (-1) +#define HX_RW_REG_FAIL (-1) + + +#define CORE_INIT +#define CORE_IC +#define CORE_FW +#define CORE_FLASH +#define CORE_SRAM +#define CORE_DRIVER + +#define HX_0F_DEBUG + +#ifdef HX_ESD_RECOVERY + extern u8 HX_ESD_RESET_ACTIVATE; +#endif + +#ifdef CORE_INIT + void himax_mcu_in_cmd_struct_init(void); + /*void himax_mcu_in_cmd_struct_free(void);*/ + void himax_in_parse_assign_cmd(uint32_t addr, uint8_t *cmd, int len); + void himax_mcu_in_cmd_init(void); + + void himax_mcu_on_cmd_struct_init(void); + /*static void himax_mcu_on_cmd_struct_free(void);*/ + void himax_on_parse_assign_cmd(uint32_t addr, uint8_t *cmd, int len); + void himax_mcu_on_cmd_init(void); +#endif + +#if defined(CORE_IC) + #define ic_adr_ahb_addr_byte_0 0x00 + #define ic_adr_ahb_rdata_byte_0 0x08 + #define ic_adr_ahb_access_direction 0x0c + #define ic_adr_conti 0x13 + #define ic_adr_incr4 0x0D + #define ic_adr_i2c_psw_lb 0x31 + #define ic_adr_i2c_psw_ub 0x32 + #define ic_cmd_ahb_access_direction_read 0x00 + #define ic_cmd_conti 0x31 + #define ic_cmd_incr4 0x10 + #define ic_cmd_i2c_psw_lb 0x27 + #define ic_cmd_i2c_psw_ub 0x95 + #define ic_adr_tcon_on_rst 0x80020020 + #define ic_addr_adc_on_rst 0x80020094 + #define ic_adr_psl 0x900000A0 + #define ic_adr_cs_central_state 0x900000A8 + #define ic_cmd_rst 0x00000000 + + #define on_ic_adr_ahb_addr_byte_0 0x00 + #define on_ic_adr_ahb_rdata_byte_0 0x08 + #define on_ic_adr_ahb_access_direction 0x0c + #define on_ic_adr_conti 0x13 + #define on_ic_adr_incr4 0x0D + #define on_ic_cmd_ahb_access_direction_read 0x00 + #define on_ic_cmd_conti 0x31 + #define on_ic_cmd_incr4 0x10 + #define on_ic_adr_mcu_ctrl 0x82 + #define on_ic_cmd_mcu_on 0x25 + #define on_ic_cmd_mcu_off 0xDA + #define on_ic_adr_sleep_ctrl 0x99 + #define on_ic_cmd_sleep_in 0x80 + #define on_ic_adr_tcon_ctrl 0x80020000 + #define on_ic_cmd_tcon_on 0x00000000 + #define on_ic_adr_wdg_ctrl 0x9000800C + #define on_ic_cmd_wdg_psw 0x0000AC53 + #define on_ic_adr_wdg_cnt_ctrl 0x90008010 + #define on_ic_cmd_wdg_cnt_clr 0x000035CA +#endif + +#if defined(CORE_FW) + #define fw_addr_system_reset 0x90000018 + #define fw_addr_safe_mode_release_pw 0x90000098 + #define fw_addr_ctrl_fw 0x9000005c + #define fw_addr_flag_reset_event 0x900000e4 + #define fw_addr_hsen_enable 0x10007F14 + #define fw_addr_smwp_enable 0x10007F10 + #define fw_addr_edge_limit_enable 0x10007F3C + #define fw_usb_detect_addr 0x10007F38 + #define fw_addr_program_reload_from 0x00000000 + #define fw_addr_program_reload_to 0x08000000 + #define fw_addr_program_reload_page_write 0x0000fb00 + #define fw_addr_raw_out_sel 0x800204b4 + #define fw_addr_reload_status 0x80050000 + #define fw_addr_reload_crc32_result 0x80050018 + #define fw_addr_reload_addr_from 0x80050020 + #define fw_addr_reload_addr_cmd_beat 0x80050028 + #define fw_data_system_reset 0x00000055 + #define fw_data_safe_mode_release_pw_active 0x00000053 + #define fw_data_safe_mode_release_pw_reset 0x00000000 + #define fw_data_clear 0x00000000 + #define fw_data_program_reload_start 0x0A3C3000 + #define fw_data_program_reload_compare 0x04663000 + #define fw_data_program_reload_break 0x15E75678 + #define fw_addr_selftest_addr_en 0x10007F18 + #define fw_addr_selftest_result_addr 0x10007f24 + #define fw_data_selftest_request 0x00006AA6 + #define fw_addr_criteria_addr 0x10007f1c + #define fw_data_criteria_aa_top 0x64 + #define fw_data_criteria_aa_bot 0x00 + #define fw_data_criteria_key_top 0x64 + #define fw_data_criteria_key_bot 0x00 + #define fw_data_criteria_avg_top 0x64 + #define fw_data_criteria_avg_bot 0x00 + #define fw_addr_set_frame_addr 0x10007294 + #define fw_data_set_frame 0x0000000A + #define fw_data_selftest_ack_hb 0xa6 + #define fw_data_selftest_ack_lb 0x6a + #define fw_data_selftest_pass 0xaa + #define fw_data_normal_cmd 0x00 + #define fw_data_normal_status 0x99 + #define fw_data_sorting_cmd 0xaa + #define fw_data_sorting_status 0xcc + #define fw_data_idle_dis_pwd 0x17 + #define fw_data_idle_en_pwd 0x1f + #define fw_addr_sorting_mode_en 0x10007f04 + #define fw_addr_fw_mode_status 0x10007088 + #define fw_addr_icid_addr 0x900000d0 + #define fw_addr_trigger_addr 0x10007088 + #define fw_addr_fw_ver_addr 0x10007004 + #define fw_addr_fw_cfg_addr 0x10007084 + #define fw_addr_fw_vendor_addr 0x10007000 + #define fw_addr_fw_state_addr 0x900000f8 + #define fw_addr_fw_dbg_msg_addr 0x10007f44 + #define fw_addr_chk_fw_status 0x900000a8 + #define fw_addr_dd_handshak_addr 0x900000fc + #define fw_addr_dd_data_addr 0x10007f80 + #define fw_data_dd_request 0xaa + #define fw_data_dd_ack 0xbb + #define fw_data_rawdata_ready_hb 0xa3 + #define fw_data_rawdata_ready_lb 0x3a + #define fw_addr_ahb_addr 0x11 + #define fw_data_ahb_dis 0x00 + #define fw_data_ahb_en 0x01 + #define fw_addr_event_addr 0x30 + #define fw_func_handshaking_pwd 0xA55AA55A + #define fw_func_handshaking_end 0x77887788 + + #define fw_func_edge_portrait_pwd 0xA55AA55A + #define fw_func_edge_lsp_left_pwd 0xA11AA11A + #define fw_func_edge_lsp_right_pwd 0xA33AA33A + + #define on_fw_addr_smwp_enable 0xA2 + #define on_fw_usb_detect_addr 0xA4 + #define on_fw_addr_program_reload_from 0x00000000 + #define on_fw_addr_raw_out_sel 0x98 + #define on_fw_addr_flash_checksum 0x80000044 + #define on_fw_data_flash_checksum 0x00000491 + #define on_fw_addr_crc_value 0x80000050 + #define on_fw_data_safe_mode_release_pw_active 0x00000053 + #define on_fw_data_safe_mode_release_pw_reset 0x00000000 + #define on_fw_addr_criteria_addr 0x9A + #define on_fw_data_selftest_pass 0xaa + #define on_fw_addr_reK_crtl 0x8000000C + #define on_fw_data_reK_en 0x02 + #define on_fw_data_reK_dis 0xFD + #define on_fw_data_rst_init 0xF0 + #define on_fw_data_dc_set 0x02 + #define on_fw_data_bank_set 0x03 + #define on_fw_addr_selftest_addr_en 0x98 + #define on_fw_addr_selftest_result_addr 0x9B + #define on_fw_data_selftest_request 0x06 + #define on_fw_data_thx_avg_mul_dc_lsb 0x22 + #define on_fw_data_thx_avg_mul_dc_msb 0x0B + #define on_fw_data_thx_mul_dc_up_low_bud 0x64 + #define on_fw_data_thx_avg_slf_dc_lsb 0x14 + #define on_fw_data_thx_avg_slf_dc_msb 0x05 + #define on_fw_data_thx_slf_dc_up_low_bud 0x64 + #define on_fw_data_thx_slf_bank_up 0x40 + #define on_fw_data_thx_slf_bank_low 0x00 + #define on_fw_data_idle_dis_pwd 0x40 + #define on_fw_data_idle_en_pwd 0x00 + #define on_fw_addr_fw_mode_status 0x99 + #define on_fw_addr_icid_addr 0x900000d0 + #define on_fw_addr_trigger_addr 0x10007088 + #define on_fw_addr_fw_ver_start 0x90 + #define on_fw_data_rawdata_ready_hb 0xa3 + #define on_fw_data_rawdata_ready_lb 0x3a + #define on_fw_addr_ahb_addr 0x11 + #define on_fw_data_ahb_dis 0x00 + #define on_fw_data_ahb_en 0x01 + #define on_fw_addr_event_addr 0x30 +#endif + +#if defined(CORE_FLASH) + #define flash_addr_ctrl_base 0x80000000 + #define flash_addr_spi200_trans_fmt (flash_addr_ctrl_base + 0x10) + #define flash_addr_spi200_trans_ctrl (flash_addr_ctrl_base + 0x20) + #define flash_addr_spi200_cmd (flash_addr_ctrl_base + 0x24) + #define flash_addr_spi200_addr (flash_addr_ctrl_base + 0x28) + #define flash_addr_spi200_data (flash_addr_ctrl_base + 0x2c) + #define flash_addr_spi200_bt_num (flash_addr_ctrl_base + 0xe8) + #define flash_data_spi200_trans_fmt 0x00020780 + #define flash_data_spi200_trans_ctrl_1 0x42000003 + #define flash_data_spi200_trans_ctrl_2 0x47000000 + #define flash_data_spi200_trans_ctrl_3 0x67000000 + #define flash_data_spi200_trans_ctrl_4 0x610ff000 + #define flash_data_spi200_trans_ctrl_5 0x694002ff + #define flash_data_spi200_cmd_1 0x00000005 + #define flash_data_spi200_cmd_2 0x00000006 + #define flash_data_spi200_cmd_3 0x000000C7 + #define flash_data_spi200_cmd_4 0x000000D8 + #define flash_data_spi200_cmd_5 0x00000020 + #define flash_data_spi200_cmd_6 0x00000002 + #define flash_data_spi200_cmd_7 0x0000003b + #define flash_data_spi200_addr 0x00000000 + + #define on_flash_addr_ctrl_base 0x80000000 + #define on_flash_addr_ctrl_auto 0x80000004 + #define on_flash_data_main_erase 0x0000A50D + #define on_flash_data_auto 0xA5 + #define on_flash_data_main_read 0x03 + #define on_flash_data_page_write 0x05 + #define on_flash_data_spp_read 0x10 + #define on_flash_data_sfr_read 0x14 + #define on_flash_addr_ahb_ctrl 0x80000020 + #define on_flash_data_ahb_squit 0x00000001 + #define on_flash_addr_unlock_0 0x00000000 + #define on_flash_addr_unlock_4 0x00000004 + #define on_flash_addr_unlock_8 0x00000008 + #define on_flash_addr_unlock_c 0x0000000C + #define on_flash_data_cmd0 0x28178EA0 + #define on_flash_data_cmd1 0x0A0E03FF + #define on_flash_data_cmd2 0x8C203D0C + #define on_flash_data_cmd3 0x00300263 + #define on_flash_data_lock 0x03400000 +#endif + +#if defined(CORE_SRAM) + #define sram_adr_mkey 0x100070E8 + #define sram_adr_rawdata_addr 0x10000000 + #define sram_adr_rawdata_end 0x00000000 + #define sram_cmd_conti 0x44332211 + #define sram_cmd_fin 0x00000000 + #define sram_passwrd_start 0x5AA5 + #define sram_passwrd_end 0xA55A + + #define on_sram_adr_rawdata_addr 0x080002E0 + #define on_sram_adr_rawdata_end 0x00000000 + #define on_sram_cmd_conti 0x44332211 + #define on_sram_cmd_fin 0x00000000 + #define on_sram_passwrd_start 0x5AA5 + #define on_sram_passwrd_end 0xA55A +#endif + +#if defined(CORE_DRIVER) + #define driver_addr_fw_define_flash_reload 0x10007f00 + #define driver_addr_fw_define_2nd_flash_reload 0x100072c0 + #define driver_data_fw_define_flash_reload_dis 0x0000a55a + #define driver_data_fw_define_flash_reload_en 0x00000000 + #define driver_addr_fw_define_int_is_edge 0x10007089 + #define driver_addr_fw_define_rxnum_txnum_maxpt 0x100070f4 + #define driver_data_fw_define_rxnum_txnum_maxpt_sorting 0x00000008 + #define driver_data_fw_define_rxnum_txnum_maxpt_normal 0x00000014 + #define driver_addr_fw_define_xy_res_enable 0x100070fa + #define driver_addr_fw_define_x_y_res 0x100070fc + + #define on_driver_addr_fw_define_int_is_edge 0x10007089 + #define on_driver_addr_fw_rx_tx_maxpt_num 0x0800001C + #define on_driver_addr_fw_xy_rev_int_edge 0x0800000C + #define on_driver_addr_fw_define_x_y_res 0x08000030 +#endif + +#if defined(HX_ZERO_FLASH) + #define zf_addr_dis_flash_reload 0x10007f00 + #define zf_data_dis_flash_reload 0x00009AA9 + #define zf_addr_system_reset 0x90000018 + #define zf_data_system_reset 0x00000055 + #define zf_data_sram_start_addr 0x08000000 + #define zf_data_sram_clean 0x10000000 + #define zf_data_cfg_info 0x10007000 + #define zf_data_fw_cfg_1 0x10007084 + #define zf_data_fw_cfg_2 0x10007264 + #define zf_data_fw_cfg_3 0x10007300 + #define zf_data_adc_cfg_1 0x10006A00 + #define zf_data_adc_cfg_2 0x10007B28 + #define zf_data_adc_cfg_3 0x10007AF0 + #define zf_data_map_table 0x10007500 + #define zf_data_mode_switch 0x10007294 + #define zf_addr_sts_chk 0x900000A8 + #define zf_data_activ_sts 0x05 + #define zf_addr_activ_relod 0x90000048 + #define zf_data_activ_in 0xEC + +struct zf_info{ + uint8_t sram_addr[4]; + int write_size; + uint32_t fw_addr; +}; +#endif + +struct ic_operation { + uint8_t addr_ahb_addr_byte_0[1]; + uint8_t addr_ahb_rdata_byte_0[1]; + uint8_t addr_ahb_access_direction[1]; + uint8_t addr_conti[1]; + uint8_t addr_incr4[1]; + uint8_t adr_i2c_psw_lb[1]; + uint8_t adr_i2c_psw_ub[1]; + uint8_t data_ahb_access_direction_read[1]; + uint8_t data_conti[1]; + uint8_t data_incr4[1]; + uint8_t data_i2c_psw_lb[1]; + uint8_t data_i2c_psw_ub[1]; + uint8_t addr_tcon_on_rst[4]; + uint8_t addr_adc_on_rst[4]; + uint8_t addr_psl[4]; + uint8_t addr_cs_central_state[4]; + uint8_t data_rst[4]; +}; + +struct fw_operation { + uint8_t addr_system_reset[4]; + uint8_t addr_safe_mode_release_pw[4]; + uint8_t addr_ctrl_fw_isr[4]; + uint8_t addr_flag_reset_event[4]; + uint8_t addr_hsen_enable[4]; + uint8_t addr_smwp_enable[4]; + uint8_t addr_edge_limit_enable[4]; + uint8_t addr_program_reload_from[4]; + uint8_t addr_program_reload_to[4]; + uint8_t addr_program_reload_page_write[4]; + uint8_t addr_raw_out_sel[4]; + uint8_t addr_reload_status[4]; + uint8_t addr_reload_crc32_result[4]; + uint8_t addr_reload_addr_from[4]; + uint8_t addr_reload_addr_cmd_beat[4]; + uint8_t addr_selftest_addr_en[4]; + uint8_t addr_criteria_addr[4]; + uint8_t addr_set_frame_addr[4]; + uint8_t addr_selftest_result_addr[4]; + uint8_t addr_sorting_mode_en[4]; + uint8_t addr_fw_mode_status[4]; + uint8_t addr_icid_addr[4]; + uint8_t addr_trigger_addr[4]; + uint8_t addr_fw_ver_addr[4]; + uint8_t addr_fw_cfg_addr[4]; + uint8_t addr_fw_vendor_addr[4]; + uint8_t addr_fw_state_addr[4]; + uint8_t addr_fw_dbg_msg_addr[4]; + uint8_t addr_chk_fw_status[4]; + uint8_t addr_dd_handshak_addr[4]; + uint8_t addr_dd_data_addr[4]; + uint8_t data_system_reset[4]; + uint8_t data_safe_mode_release_pw_active[4]; + uint8_t data_safe_mode_release_pw_reset[4]; + uint8_t data_clear[4]; + uint8_t data_program_reload_start[4]; + uint8_t data_program_reload_compare[4]; + uint8_t data_program_reload_break[4]; + uint8_t data_selftest_request[4]; + uint8_t data_criteria_aa_top[1]; + uint8_t data_criteria_aa_bot[1]; + uint8_t data_criteria_key_top[1]; + uint8_t data_criteria_key_bot[1]; + uint8_t data_criteria_avg_top[1]; + uint8_t data_criteria_avg_bot[1]; + uint8_t data_set_frame[4]; + uint8_t data_selftest_ack_hb[1]; + uint8_t data_selftest_ack_lb[1]; + uint8_t data_selftest_pass[1]; + uint8_t data_normal_cmd[1]; + uint8_t data_normal_status[1]; + uint8_t data_sorting_cmd[1]; + uint8_t data_sorting_status[1]; + uint8_t data_dd_request[1]; + uint8_t data_dd_ack[1]; + uint8_t data_idle_dis_pwd[1]; + uint8_t data_idle_en_pwd[1]; + uint8_t data_rawdata_ready_hb[1]; + uint8_t data_rawdata_ready_lb[1]; + uint8_t addr_ahb_addr[1]; + uint8_t data_ahb_dis[1]; + uint8_t data_ahb_en[1]; + uint8_t addr_event_addr[1]; + uint8_t addr_usb_detect[4]; +}; + +struct flash_operation { + uint8_t addr_spi200_trans_fmt[4]; + uint8_t addr_spi200_trans_ctrl[4]; + uint8_t addr_spi200_cmd[4]; + uint8_t addr_spi200_addr[4]; + uint8_t addr_spi200_data[4]; + uint8_t addr_spi200_bt_num[4]; + + uint8_t data_spi200_trans_fmt[4]; + uint8_t data_spi200_trans_ctrl_1[4]; + uint8_t data_spi200_trans_ctrl_2[4]; + uint8_t data_spi200_trans_ctrl_3[4]; + uint8_t data_spi200_trans_ctrl_4[4]; + uint8_t data_spi200_trans_ctrl_5[4]; + uint8_t data_spi200_cmd_1[4]; + uint8_t data_spi200_cmd_2[4]; + uint8_t data_spi200_cmd_3[4]; + uint8_t data_spi200_cmd_4[4]; + uint8_t data_spi200_cmd_5[4]; + uint8_t data_spi200_cmd_6[4]; + uint8_t data_spi200_cmd_7[4]; + uint8_t data_spi200_addr[4]; +}; + +struct sram_operation { + uint8_t addr_mkey[4]; + uint8_t addr_rawdata_addr[4]; + uint8_t addr_rawdata_end[4]; + uint8_t data_conti[4]; + uint8_t data_fin[4]; + uint8_t passwrd_start[2]; + uint8_t passwrd_end[2]; +}; + +struct driver_operation { + uint8_t addr_fw_define_flash_reload[4]; + uint8_t addr_fw_define_2nd_flash_reload[4]; + uint8_t addr_fw_define_int_is_edge[4]; + uint8_t addr_fw_define_rxnum_txnum_maxpt[4]; + uint8_t addr_fw_define_xy_res_enable[4]; + uint8_t addr_fw_define_x_y_res[4]; + uint8_t data_fw_define_flash_reload_dis[4]; + uint8_t data_fw_define_flash_reload_en[4]; + uint8_t data_fw_define_rxnum_txnum_maxpt_sorting[4]; + uint8_t data_fw_define_rxnum_txnum_maxpt_normal[4]; +}; + +struct zf_operation { + uint8_t addr_dis_flash_reload[4]; + uint8_t data_dis_flash_reload[4]; + uint8_t addr_system_reset[4]; + uint8_t data_system_reset[4]; + uint8_t data_sram_start_addr[4]; + uint8_t data_sram_clean[4]; + uint8_t data_cfg_info[4]; + uint8_t data_fw_cfg_1[4]; + uint8_t data_fw_cfg_2[4]; + uint8_t data_fw_cfg_3[4]; + uint8_t data_adc_cfg_1[4]; + uint8_t data_adc_cfg_2[4]; + uint8_t data_adc_cfg_3[4]; + uint8_t data_map_table[4]; + uint8_t data_mode_switch[4]; + uint8_t addr_sts_chk[4]; + uint8_t data_activ_sts[1]; + uint8_t addr_activ_relod[4]; + uint8_t data_activ_in[1]; +}; + +struct himax_core_command_operation { + struct ic_operation *ic_op; + struct fw_operation *fw_op; + struct flash_operation *flash_op; + struct sram_operation *sram_op; + struct driver_operation *driver_op; + struct zf_operation *zf_op; +}; + +struct on_ic_operation { + uint8_t addr_ahb_addr_byte_0[1]; + uint8_t addr_ahb_rdata_byte_0[1]; + uint8_t addr_ahb_access_direction[1]; + uint8_t addr_conti[1]; + uint8_t addr_incr4[1]; + uint8_t adr_mcu_ctrl[1]; + uint8_t data_ahb_access_direction_read[1]; + uint8_t data_conti[1]; + uint8_t data_incr4[1]; + uint8_t cmd_mcu_on[1]; + uint8_t cmd_mcu_off[1]; + uint8_t adr_sleep_ctrl[1]; + uint8_t cmd_sleep_in[1]; + uint8_t adr_tcon_ctrl[4]; + uint8_t cmd_tcon_on[4]; + uint8_t adr_wdg_ctrl[4]; + uint8_t cmd_wdg_psw[4]; + uint8_t adr_wdg_cnt_ctrl[4]; + uint8_t cmd_wdg_cnt_clr[4]; +}; + +struct on_fw_operation { + uint8_t addr_smwp_enable[1]; + uint8_t addr_program_reload_from[4]; + uint8_t addr_raw_out_sel[1]; + uint8_t addr_flash_checksum[4]; + uint8_t data_flash_checksum[4]; + uint8_t addr_crc_value[4]; + uint8_t addr_reload_status[4]; + uint8_t addr_reload_crc32_result[4]; + uint8_t addr_reload_addr_from[4]; + uint8_t addr_reload_addr_cmd_beat[4]; + uint8_t addr_set_frame_addr[4]; + uint8_t addr_fw_mode_status[1]; + uint8_t addr_icid_addr[4]; + uint8_t addr_trigger_addr[4]; + uint8_t addr_fw_ver_start[1]; + uint8_t data_safe_mode_release_pw_active[4]; + uint8_t data_safe_mode_release_pw_reset[4]; + uint8_t data_clear[4]; + uint8_t addr_criteria_addr[1]; + uint8_t data_selftest_pass[1]; + uint8_t addr_reK_crtl[4]; + uint8_t data_reK_en[1]; + uint8_t data_reK_dis[1]; + uint8_t data_rst_init[1]; + uint8_t data_dc_set[1]; + uint8_t data_bank_set[1]; + uint8_t addr_selftest_addr_en[1]; + uint8_t addr_selftest_result_addr[1]; + uint8_t data_selftest_request[1]; + uint8_t data_thx_avg_mul_dc_lsb[1]; + uint8_t data_thx_avg_mul_dc_msb[1]; + uint8_t data_thx_mul_dc_up_low_bud[1]; + uint8_t data_thx_avg_slf_dc_lsb[1]; + uint8_t data_thx_avg_slf_dc_msb[1]; + uint8_t data_thx_slf_dc_up_low_bud[1]; + uint8_t data_thx_slf_bank_up[1]; + uint8_t data_thx_slf_bank_low[1]; + uint8_t data_idle_dis_pwd[1]; + uint8_t data_idle_en_pwd[1]; + uint8_t data_rawdata_ready_hb[1]; + uint8_t data_rawdata_ready_lb[1]; + uint8_t addr_ahb_addr[1]; + uint8_t data_ahb_dis[1]; + uint8_t data_ahb_en[1]; + uint8_t addr_event_addr[1]; + uint8_t addr_usb_detect[1]; +}; + +struct on_flash_operation { + uint8_t addr_ctrl_base[4]; + uint8_t addr_ctrl_auto[4]; + uint8_t data_main_erase[4]; + uint8_t data_auto[1]; + uint8_t data_main_read[1]; + uint8_t data_page_write[1]; + uint8_t data_sfr_read[1]; + uint8_t data_spp_read[1]; + uint8_t addr_ahb_ctrl[4]; + uint8_t data_ahb_squit[4]; + + uint8_t addr_unlock_0[4]; + uint8_t addr_unlock_4[4]; + uint8_t addr_unlock_8[4]; + uint8_t addr_unlock_c[4]; + uint8_t data_cmd0[4]; + uint8_t data_cmd1[4]; + uint8_t data_cmd2[4]; + uint8_t data_cmd3[4]; + uint8_t data_lock[4]; +}; + +struct on_sram_operation { + uint8_t addr_rawdata_addr[4]; + uint8_t addr_rawdata_end[4]; + uint8_t data_conti[4]; + uint8_t data_fin[4]; + uint8_t passwrd_start[2]; + uint8_t passwrd_end[2]; +}; + +struct on_driver_operation { + uint8_t addr_fw_define_int_is_edge[4]; + uint8_t addr_fw_rx_tx_maxpt_num[4]; + uint8_t addr_fw_xy_rev_int_edge[4]; + uint8_t addr_fw_define_x_y_res[4]; + uint8_t data_fw_define_rxnum_txnum_maxpt_sorting[4]; + uint8_t data_fw_define_rxnum_txnum_maxpt_normal[4]; +}; + +struct himax_on_core_command_operation { + struct on_ic_operation *ic_op; + struct on_fw_operation *fw_op; + struct on_flash_operation *flash_op; + struct on_sram_operation *sram_op; + struct on_driver_operation *driver_op; +}; + +struct himax_core_fp { +#ifdef CORE_IC + void (*fp_burst_enable)(uint8_t auto_add_4_byte); + int (*fp_register_read)(uint8_t *read_addr, uint32_t read_length, uint8_t *read_data, uint8_t cfg_flag); + int (*fp_flash_write_burst)(uint8_t *reg_byte, uint8_t *write_data); + void (*fp_flash_write_burst_lenth)(uint8_t *reg_byte, uint8_t *write_data, uint32_t length); + void (*fp_register_write)(uint8_t *write_addr, uint32_t write_length, uint8_t *write_data, uint8_t cfg_flag); + void (*fp_interface_on)(void); + void (*fp_sense_on)(uint8_t FlashMode); + void (*fp_tcon_on)(void); + bool (*fp_watch_dog_off)(void); + bool (*fp_sense_off)(void); + void (*fp_sleep_in)(void); + bool (*fp_wait_wip)(int Timing); + void (*fp_init_psl)(void); + void (*fp_resume_ic_action)(void); + void (*fp_suspend_ic_action)(void); + void (*fp_power_on_init)(void); +#endif + +#ifdef CORE_FW + void (*fp_parse_raw_data)(struct himax_report_data *hx_touch_data, int mul_num, int self_num, uint8_t diag_cmd, int16_t *mutual_data, int16_t *self_data); + void (*fp_system_reset)(void); + bool (*fp_Calculate_CRC_with_AP)(unsigned char *FW_content, int CRC_from_FW, int mode); + uint32_t (*fp_check_CRC)(uint8_t *start_addr, int reload_length); + void (*fp_set_reload_cmd)(uint8_t *write_data, int idx, uint32_t cmd_from, uint32_t cmd_to, uint32_t cmd_beat); + bool (*fp_program_reload)(void); + void (*fp_set_SMWP_enable)(uint8_t SMWP_enable, bool suspended); + void (*fp_set_edge_limit_enable)(uint8_t edge_limit_enable, bool suspended); + void (*fp_set_HSEN_enable)(uint8_t HSEN_enable, bool suspended); + void (*fp_diag_register_set)(uint8_t diag_command, uint8_t storage_type); +#ifdef HX_TP_SELF_TEST_DRIVER + void (*fp_control_reK)(bool enable); +#endif + int (*fp_chip_self_test)(void); + void (*fp_idle_mode)(int disable); + void (*fp_reload_disable)(int disable); + bool (*fp_check_chip_version)(void); + int (*fp_read_ic_trigger_type)(void); + int (*fp_read_i2c_status)(void); + void (*fp_read_FW_ver)(void); + bool (*fp_read_event_stack)(uint8_t *buf, uint8_t length); + void (*fp_return_event_stack)(void); + bool (*fp_calculateChecksum)(bool change_iref); + int (*fp_read_FW_status)(uint8_t *state_addr, uint8_t *tmp_addr); + void (*fp_irq_switch)(int switch_on); + int (*fp_assign_sorting_mode)(uint8_t *tmp_data); + int (*fp_check_sorting_mode)(uint8_t *tmp_data); + int (*fp_switch_mode)(int mode); + uint8_t (*fp_read_DD_status)(uint8_t *cmd_set, uint8_t *tmp_data); +#endif + +#ifdef CORE_FLASH + void (*fp_chip_erase)(void); + bool (*fp_block_erase)(int start_addr, int length); + bool (*fp_sector_erase)(int start_addr); + void (*fp_flash_programming)(uint8_t *FW_content, int FW_Size); + void (*fp_flash_page_write)(uint8_t *write_addr, int length, uint8_t *write_data); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_32k)(unsigned char *fw, int len, bool change_iref); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_60k)(unsigned char *fw, int len, bool change_iref); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_64k)(unsigned char *fw, int len, bool change_iref); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_124k)(unsigned char *fw, int len, bool change_iref); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_128k)(unsigned char *fw, int len, bool change_iref); + void (*fp_flash_dump_func)(uint8_t local_flash_command, int Flash_Size, uint8_t *flash_buffer); + bool (*fp_flash_lastdata_check)(void); + bool (*fp_ahb_squit)(void); + void (*fp_flash_read)(uint8_t *r_data, int start_addr, int length); + bool (*fp_sfr_rw)(uint8_t *addr, int length, uint8_t *data, uint8_t rw_ctrl); + bool (*fp_lock_flash)(void); + bool (*fp_unlock_flash)(void); + void (*fp_init_auto_func)(void); +#endif + +#ifdef CORE_SRAM + void (*fp_sram_write)(uint8_t *FW_content); + bool (*fp_sram_verify)(uint8_t *FW_File, int FW_Size); + bool (*fp_get_DSRAM_data)(uint8_t *info_data, bool DSRAM_Flag); +#endif + +#ifdef CORE_DRIVER + bool (*fp_chip_detect)(void); + void (*fp_chip_init)(void); +#ifdef HX_AUTO_UPDATE_FW + int (*fp_fw_ver_bin)(void); +#endif + void (*fp_pin_reset)(void); + void (*fp_touch_information)(void); + void (*fp_reload_config)(void); + int (*fp_get_touch_data_size)(void); + void (*fp_usb_detect_set)(uint8_t *cable_config); + int (*fp_hand_shaking)(void); + int (*fp_determin_diag_rawdata)(int diag_command); + int (*fp_determin_diag_storage)(int diag_command); + int (*fp_cal_data_len)(int raw_cnt_rmd, int HX_MAX_PT, int raw_cnt_max); + bool (*fp_diag_check_sum)(struct himax_report_data *hx_touch_data); + void (*fp_diag_parse_raw_data)(struct himax_report_data *hx_touch_data, int mul_num, int self_num, uint8_t diag_cmd, int32_t *mutual_data, int32_t *self_data); + void (*fp_ic_reset)(uint8_t loadconfig, uint8_t int_off); + int (*fp_ic_esd_recovery)(int hx_esd_event, int hx_zero_event, int length); + void (*fp_esd_ic_reset)(void); + void (*fp_resend_cmd_func)(bool suspended); +#endif +#ifdef HX_ZERO_FLASH + void (*fp_clean_sram_0f)(uint8_t *addr, int write_len, int type); + void (*fp_write_sram_0f)(const struct firmware *fw_entry, uint8_t *addr, int start_index, uint32_t write_len); + void (*fp_firmware_update_0f)(const struct firmware *fw_entry); + int (*fp_0f_operation_dirly)(void); + int (*fp_0f_op_file_dirly)(char *file_name); + void (*fp_0f_operation)(struct work_struct *work); + int (*fp_0f_esd_check)(void); +#ifdef HX_0F_DEBUG + void (*fp_read_sram_0f)(const struct firmware *fw_entry, uint8_t *addr, int start_index, int read_len); + void (*fp_read_all_sram)(uint8_t *addr, int read_len); + void (*fp_firmware_read_0f)(const struct firmware *fw_entry, int type); + void (*fp_0f_operation_check)(int type); +#endif +#endif +}; diff --git a/drivers/input/touchscreen/himax_mmi/himax_ic_incell_core.c b/drivers/input/touchscreen/himax_mmi/himax_ic_incell_core.c new file mode 100644 index 000000000000..5aa20cbd9810 --- /dev/null +++ b/drivers/input/touchscreen/himax_mmi/himax_ic_incell_core.c @@ -0,0 +1,2811 @@ + +#include "himax_ic_core.h" + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) + extern char *i_CTPM_firmware_name; +#endif +#ifdef HX_AUTO_UPDATE_FW + extern int g_i_FW_VER; + extern int g_i_CFG_VER; + extern int g_i_CID_MAJ; + extern int g_i_CID_MIN; + extern unsigned char *i_CTPM_FW; +#endif +#ifdef HX_ZERO_FLASH +extern int g_f_0f_updat; +#endif + +extern unsigned long FW_VER_MAJ_FLASH_ADDR; +extern unsigned long FW_VER_MIN_FLASH_ADDR; +extern unsigned long CFG_VER_MAJ_FLASH_ADDR; +extern unsigned long CFG_VER_MIN_FLASH_ADDR; +extern unsigned long CID_VER_MAJ_FLASH_ADDR; +extern unsigned long CID_VER_MIN_FLASH_ADDR; + +extern unsigned long FW_VER_MAJ_FLASH_LENG; +extern unsigned long FW_VER_MIN_FLASH_LENG; +extern unsigned long CFG_VER_MAJ_FLASH_LENG; +extern unsigned long CFG_VER_MIN_FLASH_LENG; +extern unsigned long CID_VER_MAJ_FLASH_LENG; +extern unsigned long CID_VER_MIN_FLASH_LENG; + +extern struct himax_ic_data *ic_data; +extern struct himax_ts_data *private_ts; +extern unsigned char IC_CHECKSUM; + +#ifdef HX_ESD_RECOVERY +extern int g_zero_event_count; +#endif + +#ifdef HX_RST_PIN_FUNC + extern u8 HX_HW_RESET_ACTIVATE; + extern void himax_rst_gpio_set(int pinnum, uint8_t value); +#endif + +#if defined(HX_USB_DETECT_GLOBAL) +extern void himax_cable_detect_func(bool force_renew); +#endif + +extern int himax_report_data_init(void); +extern int i2c_error_count; + +struct himax_core_command_operation *g_core_cmd_op = NULL; +struct ic_operation *pic_op = NULL; +struct fw_operation *pfw_op = NULL; +struct flash_operation *pflash_op = NULL; +struct sram_operation *psram_op = NULL; +struct driver_operation *pdriver_op = NULL; +#ifdef HX_ZERO_FLASH +struct zf_operation *pzf_op = NULL; +#endif + +extern struct himax_core_fp g_core_fp; + +#ifdef CORE_IC +/* IC side start*/ +static void himax_mcu_burst_enable(uint8_t auto_add_4_byte) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + /*I("%s,Entering \n",__func__);*/ + tmp_data[0] = pic_op->data_conti[0]; + + if (himax_bus_write(pic_op->addr_conti[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + tmp_data[0] = (pic_op->data_incr4[0] | auto_add_4_byte); + + if (himax_bus_write(pic_op->addr_incr4[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } +} + +static int himax_mcu_register_read(uint8_t *read_addr, uint32_t read_length, uint8_t *read_data, uint8_t cfg_flag) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + int i = 0; + int address = 0; + + /*I("%s,Entering \n",__func__);*/ + + if (cfg_flag == false) { + if (read_length > FLASH_RW_MAX_LEN) { + E("%s: read len over %d!\n", __func__, FLASH_RW_MAX_LEN); + return LENGTH_FAIL; + } + + if (read_length > FOUR_BYTE_DATA_SZ) { + g_core_fp.fp_burst_enable(1); + } else { + g_core_fp.fp_burst_enable(0); + } + + address = (read_addr[3] << 24) + (read_addr[2] << 16) + (read_addr[1] << 8) + read_addr[0]; + i = address; + tmp_data[0] = (uint8_t)i; + tmp_data[1] = (uint8_t)(i >> 8); + tmp_data[2] = (uint8_t)(i >> 16); + tmp_data[3] = (uint8_t)(i >> 24); + + if (himax_bus_write(pic_op->addr_ahb_addr_byte_0[0], tmp_data, FOUR_BYTE_DATA_SZ, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + tmp_data[0] = pic_op->data_ahb_access_direction_read[0]; + + if (himax_bus_write(pic_op->addr_ahb_access_direction[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + if (himax_bus_read(pic_op->addr_ahb_rdata_byte_0[0], read_data, read_length, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + if (read_length > FOUR_BYTE_DATA_SZ) { + g_core_fp.fp_burst_enable(0); + } + } else { + if (himax_bus_read(read_addr[0], read_data, read_length, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + } + return NO_ERR; +} + +static int himax_mcu_flash_write_burst(uint8_t *reg_byte, uint8_t *write_data) +{ + uint8_t data_byte[FLASH_WRITE_BURST_SZ]; + int i = 0, j = 0; + int data_byte_sz = sizeof(data_byte); + + for (i = 0; i < FOUR_BYTE_ADDR_SZ; i++) { + data_byte[i] = reg_byte[i]; + } + + for (j = FOUR_BYTE_ADDR_SZ; j < data_byte_sz; j++) { + data_byte[j] = write_data[j - FOUR_BYTE_ADDR_SZ]; + } + + if (himax_bus_write(pic_op->addr_ahb_addr_byte_0[0], data_byte, data_byte_sz, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + return NO_ERR; +} + +static void himax_mcu_flash_write_burst_lenth(uint8_t *reg_byte, uint8_t *write_data, uint32_t length) +{ + uint8_t *data_byte; + int i = 0, j = 0; + + /* if (length + FOUR_BYTE_ADDR_SZ > FLASH_RW_MAX_LEN) { + E("%s: write len over %d!\n", __func__, FLASH_RW_MAX_LEN); + return; + } */ + + data_byte = kzalloc(sizeof(uint8_t)*(length + 4), GFP_KERNEL); + + for (i = 0; i < FOUR_BYTE_ADDR_SZ; i++) { + data_byte[i] = reg_byte[i]; + } + + for (j = FOUR_BYTE_ADDR_SZ; j < length + FOUR_BYTE_ADDR_SZ; j++) { + data_byte[j] = write_data[j - FOUR_BYTE_ADDR_SZ]; + } + + if (himax_bus_write(pic_op->addr_ahb_addr_byte_0[0], data_byte, length + FOUR_BYTE_ADDR_SZ, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + kfree(data_byte); + return; + } + kfree(data_byte); +} + +static void himax_mcu_register_write(uint8_t *write_addr, uint32_t write_length, uint8_t *write_data, uint8_t cfg_flag) +{ + int i, address; + + /*I("%s,Entering \n", __func__);*/ + if (cfg_flag == false) { + address = (write_addr[3] << 24) + (write_addr[2] << 16) + (write_addr[1] << 8) + write_addr[0]; + + for (i = address; i < address + write_length; i++) { + if (write_length > FOUR_BYTE_DATA_SZ) { + g_core_fp.fp_burst_enable(1); + } else { + g_core_fp.fp_burst_enable(0); + } + + g_core_fp.fp_flash_write_burst_lenth(write_addr, write_data, write_length); + } + } else if (cfg_flag == true) { + if (himax_bus_write(write_addr[0], write_data, write_length, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + } else { + E("%s: cfg_flag = %d, value is wrong!\n", __func__, cfg_flag); + return; + } +} + +static int himax_write_read_reg(uint8_t *tmp_addr, uint8_t *tmp_data, uint8_t hb, uint8_t lb) +{ + int cnt = 0; + + do { + g_core_fp.fp_flash_write_burst(tmp_addr, tmp_data); + msleep(10); + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, 0); + /* I("%s:Now tmp_data[0]=0x%02X,[1]=0x%02X,[2]=0x%02X,[3]=0x%02X\n", + __func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]);*/ + } while ((tmp_data[1] != hb && tmp_data[0] != lb) && cnt++ < 100); + + if (cnt == 99) { + return HX_RW_REG_FAIL; + } + + I("Now register 0x%08X : high byte=0x%02X,low byte=0x%02X\n", tmp_addr[3], tmp_data[1], tmp_data[0]); + return NO_ERR; +} + +static void himax_mcu_interface_on(void) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + uint8_t tmp_data2[FOUR_BYTE_DATA_SZ]; + int cnt = 0; + + /* Read a dummy register to wake up I2C.*/ + if (himax_bus_read(pic_op->addr_ahb_rdata_byte_0[0], tmp_data, FOUR_BYTE_DATA_SZ, HIMAX_I2C_RETRY_TIMES) < 0) {/* to knock I2C*/ + E("%s: i2c access fail!\n", __func__); + return; + } + + do { + tmp_data[0] = pic_op->data_conti[0]; + + if (himax_bus_write(pic_op->addr_conti[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + tmp_data[0] = pic_op->data_incr4[0]; + + if (himax_bus_write(pic_op->addr_incr4[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + /*Check cmd*/ + himax_bus_read(pic_op->addr_conti[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES); + himax_bus_read(pic_op->addr_incr4[0], tmp_data2, 1, HIMAX_I2C_RETRY_TIMES); + + if (tmp_data[0] == pic_op->data_conti[0] && tmp_data2[0] == pic_op->data_incr4[0]) { + break; + } + + msleep(1); + } while (++cnt < 10); + + if (cnt > 0) { + I("%s:Polling burst mode: %d times\n", __func__, cnt); + } +} + +static bool himax_mcu_wait_wip(int Timing) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + int retry_cnt = 0; + + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_trans_fmt, pflash_op->data_spi200_trans_fmt); + tmp_data[0] = 0x01; + + do { + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_trans_ctrl, pflash_op->data_spi200_trans_ctrl_1); + + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_cmd, pflash_op->data_spi200_cmd_1); + tmp_data[0] = tmp_data[1] = tmp_data[2] = tmp_data[3] = 0xFF; + g_core_fp.fp_register_read(pflash_op->addr_spi200_data, 4, tmp_data, 0); + + if ((tmp_data[0] & 0x01) == 0x00) { + return true; + } + + retry_cnt++; + + if (tmp_data[0] != 0x00 || tmp_data[1] != 0x00 || tmp_data[2] != 0x00 || tmp_data[3] != 0x00) + I("%s:Wait wip retry_cnt:%d, buffer[0]=%d, buffer[1]=%d, buffer[2]=%d, buffer[3]=%d \n", + __func__, retry_cnt, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + + if (retry_cnt > 100) { + E("%s: Wait wip error!\n", __func__); + return false; + } + + msleep(Timing); + } while ((tmp_data[0] & 0x01) == 0x01); + + return true; +} + +static void himax_mcu_sense_on(uint8_t FlashMode) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + int retry = 0; + I("Enter %s \n", __func__); + g_core_fp.fp_interface_on(); + g_core_fp.fp_register_write(pfw_op->addr_ctrl_fw_isr, + sizeof(pfw_op->data_clear), pfw_op->data_clear, false); + msleep(20); + + if (!FlashMode) { +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + } else { + do { + g_core_fp.fp_register_write(pfw_op->addr_safe_mode_release_pw, + sizeof(pfw_op->data_safe_mode_release_pw_active), pfw_op->data_safe_mode_release_pw_active, false); + + g_core_fp.fp_register_read(pfw_op->addr_flag_reset_event, FOUR_BYTE_DATA_SZ, tmp_data, 0); + I("%s:Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + } while ((tmp_data[1] != 0x01 || tmp_data[0] != 0x00) && retry++ < 5); + + if (retry >= 5) { + E("%s: Fail:\n", __func__); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + } else { + I("%s:OK and Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + /* reset code*/ + tmp_data[0] = 0x00; + + if (himax_bus_write(pic_op->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + } + + if (himax_bus_write(pic_op->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + } + + g_core_fp.fp_register_write(pfw_op->addr_safe_mode_release_pw, + sizeof(pfw_op->data_safe_mode_release_pw_reset), pfw_op->data_safe_mode_release_pw_reset, false); + } + } +} + +static bool himax_mcu_sense_off(void) +{ + uint8_t cnt = 0; + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + + do { + tmp_data[0] = pic_op->data_i2c_psw_lb[0]; + + if (himax_bus_write(pic_op->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + tmp_data[0] = pic_op->data_i2c_psw_ub[0]; + + if (himax_bus_write(pic_op->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + g_core_fp.fp_register_read(pic_op->addr_cs_central_state, FOUR_BYTE_ADDR_SZ, tmp_data, 0); + I("%s: Check enter_save_mode data[0]=%X \n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + g_core_fp.fp_flash_write_burst(pic_op->addr_tcon_on_rst, pic_op->data_rst); + msleep(1); + tmp_data[3] = pic_op->data_rst[3]; + tmp_data[2] = pic_op->data_rst[2]; + tmp_data[1] = pic_op->data_rst[1]; + tmp_data[0] = pic_op->data_rst[0] | 0x01; + g_core_fp.fp_flash_write_burst(pic_op->addr_tcon_on_rst, tmp_data); + + g_core_fp.fp_flash_write_burst(pic_op->addr_adc_on_rst, pic_op->data_rst); + msleep(1); + tmp_data[3] = pic_op->data_rst[3]; + tmp_data[2] = pic_op->data_rst[2]; + tmp_data[1] = pic_op->data_rst[1]; + tmp_data[0] = pic_op->data_rst[0] | 0x01; + g_core_fp.fp_flash_write_burst(pic_op->addr_adc_on_rst, tmp_data); + return true; + } else { + msleep(10); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#endif + } + } while (cnt++ < 15); + + return false; +} + +static void himax_mcu_init_psl(void) /*power saving level*/ +{ + g_core_fp.fp_register_write(pic_op->addr_psl, sizeof(pic_op->data_rst), pic_op->data_rst, false); + I("%s: power saving level reset OK!\n", __func__); +} + +static void himax_mcu_resume_ic_action(void) +{ + /* Nothing to do */ +} + +static void himax_mcu_suspend_ic_action(void) +{ + /* Nothing to do */ +} + +static void himax_mcu_power_on_init(void) +{ + I("%s:\n", __func__); + g_core_fp.fp_touch_information(); + /*RawOut select initial*/ + g_core_fp.fp_register_write(pfw_op->addr_raw_out_sel, sizeof(pfw_op->data_clear), pfw_op->data_clear, false); + /*DSRAM func initial*/ + g_core_fp.fp_assign_sorting_mode(pfw_op->data_clear); + g_core_fp.fp_sense_on(0x00); +} + +/* IC side end*/ +#endif + +#ifdef CORE_FW +/* FW side start*/ +static void diag_mcu_parse_raw_data(struct himax_report_data *hx_touch_data, int mul_num, int self_num, uint8_t diag_cmd, int32_t *mutual_data, int32_t *self_data) +{ + int RawDataLen_word; + int index = 0; + int temp1, temp2, i; + + if (hx_touch_data->hx_rawdata_buf[0] == pfw_op->data_rawdata_ready_lb[0] + && hx_touch_data->hx_rawdata_buf[1] == pfw_op->data_rawdata_ready_hb[0] + && hx_touch_data->hx_rawdata_buf[2] > 0 + && hx_touch_data->hx_rawdata_buf[3] == diag_cmd) { + RawDataLen_word = hx_touch_data->rawdata_size / 2; + index = (hx_touch_data->hx_rawdata_buf[2] - 1) * RawDataLen_word; + + /* I("Header[%d]: %x, %x, %x, %x, mutual: %d, self: %d\n", index, buf[56], buf[57], buf[58], buf[59], mul_num, self_num); + I("RawDataLen=%d , RawDataLen_word=%d , hx_touch_info_size=%d\n", RawDataLen, RawDataLen_word, hx_touch_info_size);*/ + for (i = 0; i < RawDataLen_word; i++) { + temp1 = index + i; + + if (temp1 < mul_num) { /*mutual*/ + mutual_data[index + i] = ((int8_t)hx_touch_data->hx_rawdata_buf[i * 2 + 4 + 1]) * 256 + hx_touch_data->hx_rawdata_buf[i * 2 + 4]; + } else { /*self*/ + temp1 = i + index; + temp2 = self_num + mul_num; + + if (temp1 >= temp2) { + break; + } + + self_data[i + index - mul_num] = (((int8_t)hx_touch_data->hx_rawdata_buf[i * 2 + 4 + 1]) << 8) + + hx_touch_data->hx_rawdata_buf[i * 2 + 4]; + } + } + } +} + +static void himax_mcu_system_reset(void) +{ + g_core_fp.fp_register_write(pfw_op->addr_system_reset, sizeof(pfw_op->data_system_reset), pfw_op->data_system_reset, false); +} + +static bool himax_mcu_Calculate_CRC_with_AP(unsigned char *FW_content, int CRC_from_FW, int mode) +{ + return true; +} + +static uint32_t himax_mcu_check_CRC(uint8_t *start_addr, int reload_length) +{ + uint32_t result = 0; + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + int cnt = 0, ret = 0; + int length = reload_length / FOUR_BYTE_DATA_SZ; + + ret = g_core_fp.fp_flash_write_burst(pfw_op->addr_reload_addr_from, start_addr); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + + tmp_data[3] = 0x00; tmp_data[2] = 0x99; tmp_data[1] = (length >> 8); tmp_data[0] = length; + ret = g_core_fp.fp_flash_write_burst(pfw_op->addr_reload_addr_cmd_beat, tmp_data); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + cnt = 0; + + do { + ret = g_core_fp.fp_register_read(pfw_op->addr_reload_status, FOUR_BYTE_DATA_SZ, tmp_data, 0); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + + if ((tmp_data[0] & 0x01) != 0x01) { + ret = g_core_fp.fp_register_read(pfw_op->addr_reload_crc32_result, FOUR_BYTE_DATA_SZ, tmp_data, 0); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + I("%s: tmp_data[3]=%X, tmp_data[2]=%X, tmp_data[1]=%X, tmp_data[0]=%X \n", __func__, tmp_data[3], tmp_data[2], tmp_data[1], tmp_data[0]); + result = ((tmp_data[3] << 24) + (tmp_data[2] << 16) + (tmp_data[1] << 8) + tmp_data[0]); + break; + } else { + I("Waiting for HW ready!\n"); + msleep(1); + } + + } while (cnt++ < 100); + + return result; +} + +static void himax_mcu_set_reload_cmd(uint8_t *write_data, int idx, uint32_t cmd_from, uint32_t cmd_to, uint32_t cmd_beat) +{ + int index = idx * 12; + int i; + + for (i = 3; i >= 0; i--) { + write_data[index + i] = (cmd_from >> (8 * i)); + write_data[index + 4 + i] = (cmd_to >> (8 * i)); + write_data[index + 8 + i] = (cmd_beat >> (8 * i)); + } +} + +static bool himax_mcu_program_reload(void) +{ + return true; +} + +static void himax_mcu_set_SMWP_enable(uint8_t SMWP_enable, bool suspended) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + uint8_t back_data[FOUR_BYTE_DATA_SZ]; + uint8_t retry_cnt = 0; + + do { + if (SMWP_enable) { + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + g_core_fp.fp_flash_write_burst(pfw_op->addr_smwp_enable, tmp_data); + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, back_data, 4); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_flash_write_burst(pfw_op->addr_smwp_enable, tmp_data); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + } + + g_core_fp.fp_register_read(pfw_op->addr_smwp_enable, FOUR_BYTE_DATA_SZ, tmp_data, 0); + /*I("%s: tmp_data[0]=%d, SMWP_enable=%d, retry_cnt=%d \n", __func__, tmp_data[0],SMWP_enable,retry_cnt);*/ + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} + +static void himax_mcu_set_edge_limit_enable(uint8_t edge_limit_enable, bool suspended) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + uint8_t back_data[FOUR_BYTE_DATA_SZ]; + uint8_t retry_cnt = 0; + + do { + if (edge_limit_enable == 0x01) {/*Portrait*/ + himax_in_parse_assign_cmd(fw_func_edge_portrait_pwd, tmp_data, 4); + g_core_fp.fp_flash_write_burst(pfw_op->addr_edge_limit_enable, tmp_data); + himax_in_parse_assign_cmd(fw_func_edge_portrait_pwd, back_data, 4); + } else if (edge_limit_enable == 0x02) {/*Landscape-Right*/ + himax_in_parse_assign_cmd(fw_func_edge_lsp_right_pwd, tmp_data, 4); + g_core_fp.fp_flash_write_burst(pfw_op->addr_edge_limit_enable, tmp_data); + himax_in_parse_assign_cmd(fw_func_edge_lsp_right_pwd, back_data, 4); + } else if (edge_limit_enable == 0x03) {/*Landscape-Left*/ + himax_in_parse_assign_cmd(fw_func_edge_lsp_left_pwd, tmp_data, 4); + g_core_fp.fp_flash_write_burst(pfw_op->addr_edge_limit_enable, tmp_data); + himax_in_parse_assign_cmd(fw_func_edge_lsp_left_pwd, back_data, 4); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_flash_write_burst(pfw_op->addr_edge_limit_enable, tmp_data); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + } + + g_core_fp.fp_register_read(pfw_op->addr_edge_limit_enable, FOUR_BYTE_DATA_SZ, tmp_data, 0); + I("%s: tmp_data[0]=%d, SMWP_enable=%d, retry_cnt=%d \n", __func__, tmp_data[0], edge_limit_enable, retry_cnt); + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} + +static void himax_mcu_set_HSEN_enable(uint8_t HSEN_enable, bool suspended) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + uint8_t back_data[FOUR_BYTE_DATA_SZ]; + uint8_t retry_cnt = 0; + + do { + if (HSEN_enable) { + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + g_core_fp.fp_flash_write_burst(pfw_op->addr_hsen_enable, tmp_data); + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, back_data, 4); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_flash_write_burst(pfw_op->addr_hsen_enable, tmp_data); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + } + + g_core_fp.fp_register_read(pfw_op->addr_hsen_enable, FOUR_BYTE_DATA_SZ, tmp_data, 0); + /*I("%s: tmp_data[0]=%d, HSEN_enable=%d, retry_cnt=%d \n", __func__, tmp_data[0],HSEN_enable,retry_cnt);*/ + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} + +static void himax_mcu_usb_detect_set(uint8_t *cable_config) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + uint8_t back_data[FOUR_BYTE_DATA_SZ]; + uint8_t retry_cnt = 0; + + do { + if (cable_config[1] == 0x01) { + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + g_core_fp.fp_flash_write_burst(pfw_op->addr_usb_detect, tmp_data); + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, back_data, 4); + I("%s: USB detect status IN!\n", __func__); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_flash_write_burst(pfw_op->addr_usb_detect, tmp_data); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + I("%s: USB detect status OUT!\n", __func__); + } + + g_core_fp.fp_register_read(pfw_op->addr_usb_detect, FOUR_BYTE_DATA_SZ, tmp_data, 0); + /*I("%s: tmp_data[0]=%d, USB detect=%d, retry_cnt=%d \n", __func__, tmp_data[0],cable_config[1] ,retry_cnt);*/ + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} + +static void himax_mcu_diag_register_set(uint8_t diag_command, uint8_t storage_type) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + uint8_t back_data[FOUR_BYTE_DATA_SZ]; + uint8_t cnt = 50; + + if (diag_command > 0 && storage_type % 8 > 0) + tmp_data[0] = diag_command + 0x08; + else + tmp_data[0] = diag_command; + I("diag_command = %d, tmp_data[0] = %X\n", diag_command, tmp_data[0]); + g_core_fp.fp_interface_on(); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; + do { + g_core_fp.fp_flash_write_burst(pfw_op->addr_raw_out_sel, tmp_data); + g_core_fp.fp_register_read(pfw_op->addr_raw_out_sel, FOUR_BYTE_DATA_SZ, back_data, 0); + I("%s: back_data[3]=0x%02X,back_data[2]=0x%02X,back_data[1]=0x%02X,back_data[0]=0x%02X!\n", + __func__, back_data[3], back_data[2], back_data[1], back_data[0]); + cnt--; + } while (tmp_data[0] != back_data[0] && cnt > 0); +} + +static int himax_mcu_chip_self_test(void) +{ + uint8_t tmp_data[FLASH_WRITE_BURST_SZ]; + uint8_t self_test_info[20]; + int pf_value = 0x00; + uint8_t test_result_id = 0; + int i; + memset(tmp_data, 0x00, sizeof(tmp_data)); + g_core_fp.fp_interface_on(); + g_core_fp.fp_sense_off(); + g_core_fp.fp_burst_enable(1); + g_core_fp.fp_flash_write_burst(pfw_op->addr_selftest_addr_en, pfw_op->data_selftest_request); + /*Set criteria 0x10007F1C [0,1]=aa/up,down=, [2-3]=key/up,down, [4-5]=avg/up,down*/ + tmp_data[0] = pfw_op->data_criteria_aa_top[0]; + tmp_data[1] = pfw_op->data_criteria_aa_bot[0]; + tmp_data[2] = pfw_op->data_criteria_key_top[0]; + tmp_data[3] = pfw_op->data_criteria_key_bot[0]; + tmp_data[4] = pfw_op->data_criteria_avg_top[0]; + tmp_data[5] = pfw_op->data_criteria_avg_bot[0]; + tmp_data[6] = 0x00; + tmp_data[7] = 0x00; + g_core_fp.fp_flash_write_burst_lenth(pfw_op->addr_criteria_addr, tmp_data, FLASH_WRITE_BURST_SZ); + g_core_fp.fp_flash_write_burst(pfw_op->addr_set_frame_addr, pfw_op->data_set_frame); + /*Disable IDLE Mode*/ + g_core_fp.fp_idle_mode(1); + /*Diable Flash Reload*/ + g_core_fp.fp_reload_disable(1); + /*start selftest // leave safe mode*/ + g_core_fp.fp_sense_on(0x01); + + /*Hand shaking*/ + for (i = 0; i < 1000; i++) { + g_core_fp.fp_register_read(pfw_op->addr_selftest_addr_en, 4, tmp_data, 0); + I("%s: tmp_data[0] = 0x%02X,tmp_data[1] = 0x%02X,tmp_data[2] = 0x%02X,tmp_data[3] = 0x%02X, cnt=%d\n", + __func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3], i); + msleep(10); + + if (tmp_data[1] == pfw_op->data_selftest_ack_hb[0] && tmp_data[0] == pfw_op->data_selftest_ack_lb[0]) { + I("%s Data ready goto moving data\n", __func__); + break; + } + } + + g_core_fp.fp_sense_off(); + msleep(20); + /*===================================== + Read test result ==> bit[2][1][0] = [key][AA][avg] => 0xF = PASS + =====================================*/ + g_core_fp.fp_register_read(pfw_op->addr_selftest_result_addr, 20, self_test_info, 0); + test_result_id = self_test_info[0]; + I("%s: check test result, test_result_id=%x, test_result=%x\n", __func__ + , test_result_id, self_test_info[0]); + I("raw top 1 = %d\n", self_test_info[3] * 256 + self_test_info[2]); + I("raw top 2 = %d\n", self_test_info[5] * 256 + self_test_info[4]); + I("raw top 3 = %d\n", self_test_info[7] * 256 + self_test_info[6]); + I("raw last 1 = %d\n", self_test_info[9] * 256 + self_test_info[8]); + I("raw last 2 = %d\n", self_test_info[11] * 256 + self_test_info[10]); + I("raw last 3 = %d\n", self_test_info[13] * 256 + self_test_info[12]); + I("raw key 1 = %d\n", self_test_info[15] * 256 + self_test_info[14]); + I("raw key 2 = %d\n", self_test_info[17] * 256 + self_test_info[16]); + I("raw key 3 = %d\n", self_test_info[19] * 256 + self_test_info[18]); + + if (test_result_id == pfw_op->data_selftest_pass[0]) { + I("[Himax]: self-test pass\n"); + pf_value = 0x0; + } else { + E("[Himax]: self-test fail\n"); + /* E("[Himax]: bank_avg = %d, bank_max = %d,%d,%d, bank_min = %d,%d,%d, key = %d,%d,%d\n", + tmp_data[1],tmp_data[2],tmp_data[3],tmp_data[4],tmp_data[5],tmp_data[6],tmp_data[7], + tmp_data[8],tmp_data[9],tmp_data[10]); */ + pf_value = 0x1; + } + + /*Enable IDLE Mode*/ + g_core_fp.fp_idle_mode(0); +#ifndef HX_ZERO_FLASH + /* Enable Flash Reload //recovery*/ + g_core_fp.fp_reload_disable(0); +#endif + g_core_fp.fp_sense_on(0x00); + msleep(120); + return pf_value; +} + +static void himax_mcu_idle_mode(int disable) +{ + int retry = 20; + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + uint8_t switch_cmd = 0x00; + I("%s:entering\n", __func__); + + do { + I("%s,now %d times!\n", __func__, retry); + g_core_fp.fp_register_read(pfw_op->addr_fw_mode_status, FOUR_BYTE_DATA_SZ, tmp_data, 0); + + if (disable) { + switch_cmd = pfw_op->data_idle_dis_pwd[0]; + } else { + switch_cmd = pfw_op->data_idle_en_pwd[0]; + } + + tmp_data[0] = switch_cmd; + g_core_fp.fp_flash_write_burst(pfw_op->addr_fw_mode_status, tmp_data); + g_core_fp.fp_register_read(pfw_op->addr_fw_mode_status, FOUR_BYTE_DATA_SZ, tmp_data, 0); + I("%s:After turn ON/OFF IDLE Mode [0] = 0x%02X,[1] = 0x%02X,[2] = 0x%02X,[3] = 0x%02X\n", + __func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + retry--; + msleep(10); + } while ((tmp_data[0] != switch_cmd) && retry > 0); + + I("%s: setting OK!\n", __func__); +} + +static void himax_mcu_reload_disable(int disable) +{ + I("%s:entering\n", __func__); + + if (disable) { /*reload disable*/ + g_core_fp.fp_flash_write_burst(pdriver_op->addr_fw_define_flash_reload, pdriver_op->data_fw_define_flash_reload_dis); + } else { /*reload enable*/ + g_core_fp.fp_flash_write_burst(pdriver_op->addr_fw_define_flash_reload, pdriver_op->data_fw_define_flash_reload_en); + } + + I("%s: setting OK!\n", __func__); +} + +static bool himax_mcu_check_chip_version(void) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + uint8_t ret_data = false; + int i = 0; + + for (i = 0; i < 5; i++) { + g_core_fp.fp_register_read(pfw_op->addr_icid_addr, FOUR_BYTE_DATA_SZ, tmp_data, 0); + I("%s:Read driver IC ID = %X,%X,%X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1]); + + if ((tmp_data[3] == 0x83) && (tmp_data[2] == 0x10) && (tmp_data[1] == 0x2a)) { + strlcpy(private_ts->chip_name, HX_83102A_SERIES_PWON, 30); + ret_data = true; + break; + } else { + ret_data = false; + E("%s:Read driver ID register Fail:\n", __func__); + } + } + + return ret_data; +} + +static int himax_mcu_read_ic_trigger_type(void) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + int trigger_type = false; + g_core_fp.fp_register_read(pfw_op->addr_trigger_addr, FOUR_BYTE_DATA_SZ, tmp_data, 0); + + if ((tmp_data[1] & 0x01) == 1) { + trigger_type = true; + } + + return trigger_type; +} + +static int himax_mcu_read_i2c_status(void) +{ + return i2c_error_count; +} + +static void himax_mcu_read_FW_ver(void) +{ + uint8_t data[FOUR_BYTE_DATA_SZ]; + uint8_t data_2[FOUR_BYTE_DATA_SZ]; + int retry = 200; + int reload_status = 0; + + g_core_fp.fp_sense_on(0x00); + + while (reload_status == 0) { + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_flash_reload, FOUR_BYTE_DATA_SZ, data, 0); + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_2nd_flash_reload, FOUR_BYTE_DATA_SZ, data_2, 0); + + if ((data[1] == 0x3A && data[0] == 0xA3) + || (data_2[1] == 0x72 && data_2[0] == 0xC0)) { + I("reload OK! \n"); + reload_status = 1; + break; + } else if (retry == 0) { + E("reload 20 times! fail \n"); + E("Maybe NOT have FW in chipset \n"); + E("Maybe Wrong FW in chipset \n"); + ic_data->vendor_panel_ver = 0; + ic_data->vendor_fw_ver = 0; + ic_data->vendor_config_ver = 0; + ic_data->vendor_touch_cfg_ver = 0; + ic_data->vendor_display_cfg_ver = 0; + ic_data->vendor_cid_maj_ver = 0; + ic_data->vendor_cid_min_ver = 0; + return; + } else { + retry--; + msleep(10); + if (retry % 10 == 0) + I("reload fail ,delay 10ms retry=%d\n", retry); + } + } + + I("%s : data[0]=0x%2.2X,data[1]=0x%2.2X,data_2[0]=0x%2.2X,data_2[1]=0x%2.2X\n", __func__, data[0], data[1], data_2[0], data_2[1]); + I("reload_status=%d\n", reload_status); + /*===================================== + Read FW version + =====================================*/ + g_core_fp.fp_sense_off(); + g_core_fp.fp_register_read(pfw_op->addr_fw_ver_addr, FOUR_BYTE_DATA_SZ, data, 0); + ic_data->vendor_panel_ver = data[0]; + ic_data->vendor_fw_ver = data[1] << 8 | data[2]; + I("PANEL_VER : %X \n", ic_data->vendor_panel_ver); + I("FW_VER : %X \n", ic_data->vendor_fw_ver); + g_core_fp.fp_register_read(pfw_op->addr_fw_cfg_addr, FOUR_BYTE_DATA_SZ, data, 0); + ic_data->vendor_config_ver = data[2] << 8 | data[3]; + /*I("CFG_VER : %X \n",ic_data->vendor_config_ver);*/ + ic_data->vendor_touch_cfg_ver = data[2]; + I("TOUCH_VER : %X \n", ic_data->vendor_touch_cfg_ver); + ic_data->vendor_display_cfg_ver = data[3]; + I("DISPLAY_VER : %X \n", ic_data->vendor_display_cfg_ver); + g_core_fp.fp_register_read(pfw_op->addr_fw_vendor_addr, FOUR_BYTE_DATA_SZ, data, 0); + ic_data->vendor_cid_maj_ver = data[2] ; + ic_data->vendor_cid_min_ver = data[3]; + I("CID_VER : %X \n", (ic_data->vendor_cid_maj_ver << 8 | ic_data->vendor_cid_min_ver)); +} + +static bool himax_mcu_read_event_stack(uint8_t *buf, uint8_t length) +{ + uint8_t cmd[FOUR_BYTE_DATA_SZ]; + /* AHB_I2C Burst Read Off */ + cmd[0] = pfw_op->data_ahb_dis[0]; + + if (himax_bus_write(pfw_op->addr_ahb_addr[0], cmd, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return 0; + } + + himax_bus_read(pfw_op->addr_event_addr[0], buf, length, HIMAX_I2C_RETRY_TIMES); + /* AHB_I2C Burst Read On */ + cmd[0] = pfw_op->data_ahb_en[0]; + + if (himax_bus_write(pfw_op->addr_ahb_addr[0], cmd, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return 0; + } + + return 1; +} + +static void himax_mcu_return_event_stack(void) +{ + int retry = 20, i; + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + I("%s:entering\n", __func__); + + do { + I("now %d times!\n", retry); + + for (i = 0; i < FOUR_BYTE_DATA_SZ; i++) { + tmp_data[i] = psram_op->addr_rawdata_end[i]; + } + + g_core_fp.fp_flash_write_burst(psram_op->addr_rawdata_addr, tmp_data); + g_core_fp.fp_register_read(psram_op->addr_rawdata_addr, FOUR_BYTE_DATA_SZ, tmp_data, 0); + retry--; + msleep(10); + } while ((tmp_data[1] != psram_op->addr_rawdata_end[1] && tmp_data[0] != psram_op->addr_rawdata_end[0]) && retry > 0); + + I("%s: End of setting!\n", __func__); +} + +static bool himax_mcu_calculateChecksum(bool change_iref) +{ + uint8_t CRC_result = 0, i; + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + + for (i = 0; i < FOUR_BYTE_DATA_SZ; i++) { + tmp_data[i] = psram_op->addr_rawdata_end[i]; + } + + CRC_result = g_core_fp.fp_check_CRC(tmp_data, FW_SIZE_64k); + msleep(50); + + if (CRC_result != 0) { + I("%s: CRC Fail=%d\n", __func__, CRC_result); + } + return (CRC_result == 0) ? true : false; +} + +static int himax_mcu_read_FW_status(uint8_t *state_addr, uint8_t *tmp_addr) +{ + uint8_t i; + uint8_t req_size = 0; + uint8_t status_addr[FOUR_BYTE_DATA_SZ]; + uint8_t cmd_addr[FOUR_BYTE_DATA_SZ]; + + if (state_addr[0] == 0x01) { + state_addr[1] = 0x04; + + for (i = 0; i < FOUR_BYTE_DATA_SZ; i++) { + state_addr[i + 2] = pfw_op->addr_fw_dbg_msg_addr[i]; + status_addr[i] = pfw_op->addr_fw_dbg_msg_addr[i]; + } + + req_size = 0x04; + g_core_fp.fp_register_read(status_addr, req_size, tmp_addr, 0); + } else if (state_addr[0] == 0x02) { + state_addr[1] = 0x30; + + for (i = 0; i < FOUR_BYTE_DATA_SZ; i++) { + state_addr[i + 2] = pfw_op->addr_fw_dbg_msg_addr[i]; + cmd_addr[i] = pfw_op->addr_fw_dbg_msg_addr[i]; + } + + req_size = 0x30; + g_core_fp.fp_register_read(cmd_addr, req_size, tmp_addr, 0); + } + + return NO_ERR; +} + +static void himax_mcu_irq_switch(int switch_on) +{ + if (switch_on) { + if (private_ts->use_irq) { + himax_int_enable(switch_on); + } else { + hrtimer_start(&private_ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + } + } else { + if (private_ts->use_irq) { + himax_int_enable(switch_on); + } else { + hrtimer_cancel(&private_ts->timer); + cancel_work_sync(&private_ts->work); + } + } +} + +static int himax_mcu_assign_sorting_mode(uint8_t *tmp_data) +{ + + I("%s:Now tmp_data[3]=0x%02X,tmp_data[2]=0x%02X,tmp_data[1]=0x%02X,tmp_data[0]=0x%02X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1], tmp_data[0]); + g_core_fp.fp_flash_write_burst(pfw_op->addr_sorting_mode_en, tmp_data); + + return NO_ERR; +} + +static int himax_mcu_check_sorting_mode(uint8_t *tmp_data) +{ + + g_core_fp.fp_register_read(pfw_op->addr_sorting_mode_en, FOUR_BYTE_DATA_SZ, tmp_data, 0); + I("%s: tmp_data[0]=%x,tmp_data[1]=%x\n", __func__, tmp_data[0], tmp_data[1]); + + return NO_ERR; +} + +static int himax_mcu_switch_mode(int mode) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + uint8_t mode_wirte_cmd; + uint8_t mode_read_cmd; + int result = -1; + int retry = 200; + I("%s: Entering\n", __func__); + + if (mode == 0) { /* normal mode */ + mode_wirte_cmd = pfw_op->data_normal_cmd[0]; + mode_read_cmd = pfw_op->data_normal_status[0]; + } else { /* sorting mode */ + mode_wirte_cmd = pfw_op->data_sorting_cmd[0]; + mode_read_cmd = pfw_op->data_sorting_status[0]; + } + + g_core_fp.fp_sense_off(); + /*g_core_fp.fp_interface_on();*/ + /* clean up FW status */ + g_core_fp.fp_flash_write_burst(psram_op->addr_rawdata_addr, psram_op->addr_rawdata_end); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = mode_wirte_cmd; + tmp_data[0] = mode_wirte_cmd; + g_core_fp.fp_assign_sorting_mode(tmp_data); + g_core_fp.fp_idle_mode(1); + g_core_fp.fp_reload_disable(1); + + /* To stable the sorting*/ + if (mode) { + g_core_fp.fp_flash_write_burst(pdriver_op->addr_fw_define_rxnum_txnum_maxpt, pdriver_op->data_fw_define_rxnum_txnum_maxpt_sorting); + } else { + g_core_fp.fp_flash_write_burst(pfw_op->addr_set_frame_addr, pfw_op->data_set_frame); + g_core_fp.fp_flash_write_burst(pdriver_op->addr_fw_define_rxnum_txnum_maxpt, pdriver_op->data_fw_define_rxnum_txnum_maxpt_normal); + } + + g_core_fp.fp_sense_on(0x01); + + while (retry != 0) { + I("[%d] %s Read\n", retry, __func__); + g_core_fp.fp_check_sorting_mode(tmp_data); + msleep(100); + I("mode_read_cmd(0)=0x%2.2X,mode_read_cmd(1)=0x%2.2X\n", tmp_data[0], tmp_data[1]); + + if (tmp_data[0] == mode_read_cmd && tmp_data[1] == mode_read_cmd) { + I("Read OK!\n"); + result = 0; + break; + } + + g_core_fp.fp_register_read(pfw_op->addr_chk_fw_status, FOUR_BYTE_DATA_SZ, tmp_data, 0); + + if (tmp_data[0] == 0x00 && tmp_data[1] == 0x00 && tmp_data[2] == 0x00 && tmp_data[3] == 0x00) { + E("%s,: FW Stop!\n", __func__); + break; + } + + retry--; + } + + if (result == 0) { + if (mode == 0) { /*normal mode*/ + return HX_NORMAL_MODE; + } else { /*sorting mode*/ + return HX_SORTING_MODE; + } + } else { /*change mode fail*/ + return HX_CHANGE_MODE_FAIL; + } +} + +static uint8_t himax_mcu_read_DD_status(uint8_t *cmd_set, uint8_t *tmp_data) +{ + int cnt = 0; + uint8_t req_size = cmd_set[0]; + cmd_set[3] = pfw_op->data_dd_request[0]; + g_core_fp.fp_register_write(pfw_op->addr_dd_handshak_addr, FOUR_BYTE_DATA_SZ, cmd_set, 0); + I("%s: cmd_set[0] = 0x%02X,cmd_set[1] = 0x%02X,cmd_set[2] = 0x%02X,cmd_set[3] = 0x%02X\n", + __func__, cmd_set[0], cmd_set[1], cmd_set[2], cmd_set[3]); + + /* Doing hand shaking 0xAA -> 0xBB */ + for (cnt = 0; cnt < 100; cnt++) { + g_core_fp.fp_register_read(pfw_op->addr_dd_handshak_addr, FOUR_BYTE_DATA_SZ, tmp_data, 0); + msleep(10); + + if (tmp_data[3] == pfw_op->data_dd_ack[0]) { + I("%s Data ready goto moving data\n", __func__); + break; + } else { + if (cnt >= 99) { + I("%s Data not ready in FW \n", __func__); + return FW_NOT_READY; + } + } + } + + g_core_fp.fp_register_read(pfw_op->addr_dd_data_addr, req_size, tmp_data, 0); + return NO_ERR; +} +/* FW side end*/ +#endif + +#ifdef CORE_FLASH +/* FLASH side start*/ +static void himax_mcu_chip_erase(void) +{ + g_core_fp.fp_interface_on(); + + /* Reset power saving level */ + if (g_core_fp.fp_init_psl != NULL) { + g_core_fp.fp_init_psl(); + } + + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_trans_fmt, pflash_op->data_spi200_trans_fmt); + + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_trans_ctrl, pflash_op->data_spi200_trans_ctrl_2); + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_cmd, pflash_op->data_spi200_cmd_2); + + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_cmd, pflash_op->data_spi200_cmd_3); + msleep(2000); + + if (!g_core_fp.fp_wait_wip(100)) { + E("%s: Chip_Erase Fail\n", __func__); + } +} + +static bool himax_mcu_block_erase(int start_addr, int length) /*complete not yet*/ +{ + uint32_t page_prog_start = 0; + uint32_t block_size = 0x10000; + + uint8_t tmp_data[4] = {0}; + + g_core_fp.fp_interface_on(); + + g_core_fp.fp_init_psl(); + + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_trans_fmt, pflash_op->data_spi200_trans_fmt); + + for (page_prog_start = start_addr; page_prog_start < start_addr + length; page_prog_start = page_prog_start + block_size) { + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_trans_ctrl, pflash_op->data_spi200_trans_ctrl_2); + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_cmd, pflash_op->data_spi200_cmd_2); + + tmp_data[3] = (page_prog_start >> 24)&0xFF; + tmp_data[2] = (page_prog_start >> 16)&0xFF; + tmp_data[1] = (page_prog_start >> 8)&0xFF; + tmp_data[0] = page_prog_start&0xFF; + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_addr, tmp_data); + + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_trans_ctrl, pflash_op->data_spi200_trans_ctrl_3); + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_cmd, pflash_op->data_spi200_cmd_4); + msleep(1000); + + if (!g_core_fp.fp_wait_wip(100)) { + E("%s:Erase Fail\n", __func__); + return false; + } + } + + I("%s:END\n", __func__); + return true; +} + +static bool himax_mcu_sector_erase(int start_addr) +{ + return true; +} + +static void himax_mcu_flash_programming(uint8_t *FW_content, int FW_Size) +{ + int page_prog_start = 0, i = 0, j = 0, k = 0; + int program_length = PROGRAM_SZ; + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + uint8_t buring_data[FLASH_RW_MAX_LEN]; /* Read for flash data, 128K*/ + /* 4 bytes for padding*/ + g_core_fp.fp_interface_on(); + + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_trans_fmt, pflash_op->data_spi200_trans_fmt); + + for (page_prog_start = 0; page_prog_start < FW_Size; page_prog_start += FLASH_RW_MAX_LEN) { + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_trans_ctrl, pflash_op->data_spi200_trans_ctrl_2); + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_cmd, pflash_op->data_spi200_cmd_2); + + /*Programmable size = 1 page = 256 bytes, word_number = 256 byte / 4 = 64*/ + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_trans_ctrl, pflash_op->data_spi200_trans_ctrl_4); + + /* Flash start address 1st : 0x0000_0000*/ + if (page_prog_start < 0x100) { + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = (uint8_t)page_prog_start; + } else if (page_prog_start >= 0x100 && page_prog_start < 0x10000) { + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = (uint8_t)(page_prog_start >> 8); + tmp_data[0] = (uint8_t)page_prog_start; + } else if (page_prog_start >= 0x10000 && page_prog_start < 0x1000000) { + tmp_data[3] = 0x00; + tmp_data[2] = (uint8_t)(page_prog_start >> 16); + tmp_data[1] = (uint8_t)(page_prog_start >> 8); + tmp_data[0] = (uint8_t)page_prog_start; + } + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_addr, tmp_data); + + for (i = 0; i < FOUR_BYTE_ADDR_SZ; i++) { + buring_data[i] = pflash_op->addr_spi200_data[i]; + } + + for (i = page_prog_start, j = 0; i < 16 + page_prog_start; i++, j++) { + buring_data[j + FOUR_BYTE_ADDR_SZ] = FW_content[i]; + } + + if (himax_bus_write(pic_op->addr_ahb_addr_byte_0[0], buring_data, FOUR_BYTE_ADDR_SZ + 16, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + g_core_fp.fp_flash_write_burst(pflash_op->addr_spi200_cmd, pflash_op->data_spi200_cmd_6); + + for (j = 0; j < 5; j++) { + for (i = (page_prog_start + 16 + (j * 48)), k = 0; i < (page_prog_start + 16 + (j * 48)) + program_length; i++, k++) { + buring_data[k + FOUR_BYTE_ADDR_SZ] = FW_content[i]; + } + + if (himax_bus_write(pic_op->addr_ahb_addr_byte_0[0], buring_data, program_length + FOUR_BYTE_ADDR_SZ, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + } + + if (!g_core_fp.fp_wait_wip(1)) { + E("%s:Flash_Programming Fail\n", __func__); + } + } +} + +static void himax_mcu_flash_page_write(uint8_t *write_addr, int length, uint8_t *write_data) +{ +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_32k(unsigned char *fw, int len, bool change_iref) +{ + /* Not use */ + return 0; +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_60k(unsigned char *fw, int len, bool change_iref) +{ + /* Not use */ + return 0; +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_64k(unsigned char *fw, int len, bool change_iref) +{ + int burnFW_success = 0; + + if (len != FW_SIZE_64k) { + E("%s: The file size is not 64K bytes\n", __func__); + return false; + } + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + g_core_fp.fp_sense_off(); + g_core_fp.fp_block_erase(0x00, FW_SIZE_64k); + g_core_fp.fp_flash_programming(fw, FW_SIZE_64k); + + if (g_core_fp.fp_check_CRC(pfw_op->addr_program_reload_from, FW_SIZE_64k) == 0) { + burnFW_success = 1; + } + + /*RawOut select initial*/ + g_core_fp.fp_register_write(pfw_op->addr_raw_out_sel, sizeof(pfw_op->data_clear), pfw_op->data_clear, false); + /*DSRAM func initial*/ + g_core_fp.fp_assign_sorting_mode(pfw_op->data_clear); + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + /*System reset*/ + g_core_fp.fp_system_reset(); +#endif + return burnFW_success; +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_124k(unsigned char *fw, int len, bool change_iref) +{ + /* Not use */ + return 0; +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_128k(unsigned char *fw, int len, bool change_iref) +{ + /* Not use */ + return 0; +} + +static void himax_mcu_flash_dump_func(uint8_t local_flash_command, int Flash_Size, uint8_t *flash_buffer) +{ + uint8_t tmp_addr[FOUR_BYTE_DATA_SZ]; + uint8_t buffer[256]; + int page_prog_start = 0; + g_core_fp.fp_sense_off(); + g_core_fp.fp_burst_enable(1); + + for (page_prog_start = 0; page_prog_start < Flash_Size; page_prog_start += 128) { + tmp_addr[0] = page_prog_start % 0x100; + tmp_addr[1] = (page_prog_start >> 8) % 0x100; + tmp_addr[2] = (page_prog_start >> 16) % 0x100; + tmp_addr[3] = page_prog_start / 0x1000000; + himax_mcu_register_read(tmp_addr, 128, buffer, 0); + memcpy(&flash_buffer[page_prog_start], buffer, 128); + } + + g_core_fp.fp_burst_enable(0); + g_core_fp.fp_sense_on(0x01); +} + +static bool himax_mcu_flash_lastdata_check(void) +{ + uint8_t tmp_addr[4]; + uint32_t start_addr = 0xFF80; + uint32_t temp_addr = 0; + uint32_t flash_page_len = 0x80; + uint8_t flash_tmp_buffer[128]; + + for (temp_addr = start_addr; temp_addr < (start_addr + flash_page_len); temp_addr = temp_addr + flash_page_len) { + /*I("temp_addr=%d,tmp_addr[0]=0x%2X, tmp_addr[1]=0x%2X,tmp_addr[2]=0x%2X,tmp_addr[3]=0x%2X\n", temp_addr,tmp_addr[0], tmp_addr[1], tmp_addr[2],tmp_addr[3]);*/ + tmp_addr[0] = temp_addr % 0x100; + tmp_addr[1] = (temp_addr >> 8) % 0x100; + tmp_addr[2] = (temp_addr >> 16) % 0x100; + tmp_addr[3] = temp_addr / 0x1000000; + g_core_fp.fp_register_read(tmp_addr, flash_page_len, &flash_tmp_buffer[0], 0); + } + + if ((!flash_tmp_buffer[flash_page_len-4]) && (!flash_tmp_buffer[flash_page_len-3]) && (!flash_tmp_buffer[flash_page_len-2]) && (!flash_tmp_buffer[flash_page_len-1])) { + I("Fail, Last four Bytes are " + "flash_buffer[FFFC]=0x%2X,flash_buffer[FFFD]=0x%2X,flash_buffer[FFFE]=0x%2X,flash_buffer[FFFF]=0x%2X\n", + flash_tmp_buffer[flash_page_len-4], flash_tmp_buffer[flash_page_len-3], flash_tmp_buffer[flash_page_len-2], flash_tmp_buffer[flash_page_len-1]); + return 1;/*FAIL*/ + } else if ((flash_tmp_buffer[flash_page_len-4] == 0xFF) && (flash_tmp_buffer[flash_page_len-3] == 0xFF) && (flash_tmp_buffer[flash_page_len-2] == 0xFF) && (flash_tmp_buffer[flash_page_len-1] == 0xFF)) { + I("Fail, Last four Bytes are " + "flash_buffer[FFFC]=0x%2X,flash_buffer[FFFD]=0x%2X,flash_buffer[FFFE]=0x%2X,flash_buffer[FFFF]=0x%2X\n", + flash_tmp_buffer[flash_page_len-4], flash_tmp_buffer[flash_page_len-3], flash_tmp_buffer[flash_page_len-2], flash_tmp_buffer[flash_page_len-1]); + return 1; + } else { + I("flash_buffer[FFFC]=0x%2X,flash_buffer[FFFD]=0x%2X,flash_buffer[FFFE]=0x%2X,flash_buffer[FFFF]=0x%2X\n", + flash_tmp_buffer[flash_page_len-4], flash_tmp_buffer[flash_page_len-3], flash_tmp_buffer[flash_page_len-2], flash_tmp_buffer[flash_page_len-1]); + return 0;/*PASS*/ + } +} +/* FLASH side end*/ +#endif + +#ifdef CORE_SRAM +/* SRAM side start*/ +static void himax_mcu_sram_write(uint8_t *FW_content) +{ +} + +static bool himax_mcu_sram_verify(uint8_t *FW_File, int FW_Size) +{ + return true; +} + +static bool himax_mcu_get_DSRAM_data(uint8_t *info_data, bool DSRAM_Flag) +{ + int i = 0; + unsigned char tmp_addr[FOUR_BYTE_ADDR_SZ]; + unsigned char tmp_data[FOUR_BYTE_DATA_SZ]; + uint8_t max_i2c_size = MAX_I2C_TRANS_SZ; + uint8_t x_num = ic_data->HX_RX_NUM; + uint8_t y_num = ic_data->HX_TX_NUM; + /*int m_key_num = 0;*/ + int total_size = (x_num * y_num + x_num + y_num) * 2 + 4; + int total_size_temp; + int mutual_data_size = x_num * y_num * 2; + int total_read_times = 0; + int address = 0; + uint8_t *temp_info_data; /*max mkey size = 8*/ + uint16_t check_sum_cal = 0; + int fw_run_flag = -1; + + temp_info_data = kzalloc(sizeof(uint8_t) * (total_size + 8), GFP_KERNEL); + /*1. Read number of MKey R100070E8H to determin data size*/ + /*m_key_num = ic_data->HX_BT_NUM; + I("%s,m_key_num=%d\n",__func__ ,m_key_num); + total_size += m_key_num * 2; + 2. Start DSRAM Rawdata and Wait Data Ready */ + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = psram_op->passwrd_start[1]; + tmp_data[0] = psram_op->passwrd_start[0]; + fw_run_flag = himax_write_read_reg(psram_op->addr_rawdata_addr, tmp_data, psram_op->passwrd_end[1], psram_op->passwrd_end[0]); + + if (fw_run_flag < 0) { + I("%s Data NOT ready => bypass \n", __func__); + kfree(temp_info_data); + return false; + } + + /* 3. Read RawData */ + total_size_temp = total_size; + I("%s: tmp_data[0] = 0x%02X,tmp_data[1] = 0x%02X,tmp_data[2] = 0x%02X,tmp_data[3] = 0x%02X\n", + __func__, psram_op->addr_rawdata_addr[0], psram_op->addr_rawdata_addr[1], psram_op->addr_rawdata_addr[2], psram_op->addr_rawdata_addr[3]); + tmp_addr[0] = psram_op->addr_rawdata_addr[0]; + tmp_addr[1] = psram_op->addr_rawdata_addr[1]; + tmp_addr[2] = psram_op->addr_rawdata_addr[2]; + tmp_addr[3] = psram_op->addr_rawdata_addr[3]; + + if (total_size % max_i2c_size == 0) { + total_read_times = total_size / max_i2c_size; + } else { + total_read_times = total_size / max_i2c_size + 1; + } + + for (i = 0; i < total_read_times; i++) { + address = (psram_op->addr_rawdata_addr[3] << 24) + + (psram_op->addr_rawdata_addr[2] << 16) + + (psram_op->addr_rawdata_addr[1] << 8) + + psram_op->addr_rawdata_addr[0] + i * max_i2c_size; + /*I("%s address = %08X \n", __func__, address);*/ + + tmp_addr[3] = (uint8_t)((address >> 24) & 0x00FF); + tmp_addr[2] = (uint8_t)((address >> 16) & 0x00FF); + tmp_addr[1] = (uint8_t)((address >> 8) & 0x00FF); + tmp_addr[0] = (uint8_t)((address) & 0x00FF); + + if (total_size_temp >= max_i2c_size) { + g_core_fp.fp_register_read(tmp_addr, max_i2c_size, &temp_info_data[i * max_i2c_size], 0); + total_size_temp = total_size_temp - max_i2c_size; + } else { + /*I("last total_size_temp=%d\n",total_size_temp);*/ + g_core_fp.fp_register_read(tmp_addr, total_size_temp % max_i2c_size, &temp_info_data[i * max_i2c_size], 0); + } + } + + /* 4. FW stop outputing */ + /*I("DSRAM_Flag=%d\n",DSRAM_Flag);*/ + if (DSRAM_Flag == false || private_ts->diag_cmd == 0) { + /*I("Return to Event Stack!\n");*/ + g_core_fp.fp_flash_write_burst(psram_op->addr_rawdata_addr, psram_op->data_fin); + } else { + /*I("Continue to SRAM!\n");*/ + g_core_fp.fp_flash_write_burst(psram_op->addr_rawdata_addr, psram_op->data_conti); + } + + /* 5. Data Checksum Check */ + for (i = 2; i < total_size; i += 2) { /* 2:PASSWORD NOT included */ + check_sum_cal += (temp_info_data[i + 1] * 256 + temp_info_data[i]); + } + + if (check_sum_cal % 0x10000 != 0) { + I("%s check_sum_cal fail=%2X \n", __func__, check_sum_cal); + kfree(temp_info_data); + return false; + } else { + memcpy(info_data, &temp_info_data[4], mutual_data_size * sizeof(uint8_t)); + /*I("%s checksum PASS \n", __func__);*/ + } + + kfree(temp_info_data); + return true; +} +/* SRAM side end*/ +#endif + +#ifdef CORE_DRIVER +static bool himax_mcu_detect_ic(void) +{ + I("%s: use default incell detect.\n", __func__); + + return 0; +} + + +static void himax_mcu_init_ic(void) +{ + I("%s: use default incell init.\n", __func__); +} + +#ifdef HX_AUTO_UPDATE_FW +static int himax_mcu_fw_ver_bin(void) +{ + I("%s: use default incell address.\n", __func__); + I("%s:Entering!\n", __func__); + if (i_CTPM_FW != NULL) { + I("Catch fw version in bin file!\n"); + g_i_FW_VER = (i_CTPM_FW[FW_VER_MAJ_FLASH_ADDR] << 8) | i_CTPM_FW[FW_VER_MIN_FLASH_ADDR]; + g_i_CFG_VER = (i_CTPM_FW[CFG_VER_MAJ_FLASH_ADDR] << 8) | i_CTPM_FW[CFG_VER_MIN_FLASH_ADDR]; + g_i_CID_MAJ = i_CTPM_FW[CID_VER_MAJ_FLASH_ADDR]; + g_i_CID_MIN = i_CTPM_FW[CID_VER_MIN_FLASH_ADDR]; + } else { + I("FW data is null!\n"); + return 1; + } + return NO_ERR; +} +#endif + + +#ifdef HX_RST_PIN_FUNC +static void himax_mcu_pin_reset(void) +{ + I("%s: Now reset the Touch chip.\n", __func__); + himax_rst_gpio_set(private_ts->rst_gpio, 0); + msleep(20); + himax_rst_gpio_set(private_ts->rst_gpio, 1); + msleep(50); +} + +static void himax_mcu_ic_reset(uint8_t loadconfig, uint8_t int_off) +{ + struct himax_ts_data *ts = private_ts; + HX_HW_RESET_ACTIVATE = 1; + I("%s,status: loadconfig=%d,int_off=%d\n", __func__, loadconfig, int_off); + + if (ts->rst_gpio >= 0) { + if (int_off) { + g_core_fp.fp_irq_switch(0); + } + + g_core_fp.fp_pin_reset(); + + if (loadconfig) { + g_core_fp.fp_reload_config(); + } + + if (int_off) { + g_core_fp.fp_irq_switch(1); + } + } +} +#endif + +static void himax_mcu_touch_information(void) +{ +#ifndef HX_FIX_TOUCH_INFO + char data[EIGHT_BYTE_DATA_SZ] = {0}; + + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_rxnum_txnum_maxpt, EIGHT_BYTE_DATA_SZ, data, 0); + ic_data->HX_RX_NUM = data[2]; + ic_data->HX_TX_NUM = data[3]; + ic_data->HX_MAX_PT = data[4]; + /*I("%s : HX_RX_NUM=%d,ic_data->HX_TX_NUM=%d,ic_data->HX_MAX_PT=%d\n",__func__,ic_data->HX_RX_NUM,ic_data->HX_TX_NUM,ic_data->HX_MAX_PT);*/ + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_xy_res_enable, FOUR_BYTE_DATA_SZ, data, 0); + + /*I("%s : c_data->HX_XY_REVERSE=0x%2.2X\n",__func__,data[1]);*/ + if ((data[1] & 0x04) == 0x04) { + ic_data->HX_XY_REVERSE = true; + } else { + ic_data->HX_XY_REVERSE = false; + } + + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_x_y_res, FOUR_BYTE_DATA_SZ, data, 0); + ic_data->HX_Y_RES = data[0] * 256 + data[1]; + ic_data->HX_X_RES = data[2] * 256 + data[3]; + /*I("%s : ic_data->HX_Y_RES=%d,ic_data->HX_X_RES=%d \n",__func__,ic_data->HX_Y_RES,ic_data->HX_X_RES);*/ + + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_int_is_edge, FOUR_BYTE_DATA_SZ, data, 0); + /*I("%s : data[0]=0x%2.2X,data[1]=0x%2.2X,data[2]=0x%2.2X,data[3]=0x%2.2X\n",__func__,data[0],data[1],data[2],data[3]); + I("data[0] & 0x01 = %d\n",(data[0] & 0x01));*/ + if ((data[1] & 0x01) == 1) { + ic_data->HX_INT_IS_EDGE = true; + } else { + ic_data->HX_INT_IS_EDGE = false; + } + + if (ic_data->HX_RX_NUM > 40) { + ic_data->HX_RX_NUM = 32; + } + + if (ic_data->HX_TX_NUM > 20) { + ic_data->HX_TX_NUM = 18; + } + + if (ic_data->HX_MAX_PT > 10) { + ic_data->HX_MAX_PT = 10; + } + + if (ic_data->HX_Y_RES > 2000) { + ic_data->HX_Y_RES = 1280; + } + + if (ic_data->HX_X_RES > 2000) { + ic_data->HX_X_RES = 720; + } + + /*1. Read number of MKey R100070E8H to determin data size*/ + g_core_fp.fp_register_read(psram_op->addr_mkey, FOUR_BYTE_DATA_SZ, data, 0); + /* I("%s: tmp_data[0] = 0x%02X,tmp_data[1] = 0x%02X,tmp_data[2] = 0x%02X,tmp_data[3] = 0x%02X\n", + __func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]);*/ + ic_data->HX_BT_NUM = data[0] & 0x03; +#else + ic_data->HX_RX_NUM = FIX_HX_RX_NUM; + ic_data->HX_TX_NUM = FIX_HX_TX_NUM; + ic_data->HX_BT_NUM = FIX_HX_BT_NUM; + ic_data->HX_X_RES = FIX_HX_X_RES; + ic_data->HX_Y_RES = FIX_HX_Y_RES; + ic_data->HX_MAX_PT = FIX_HX_MAX_PT; + ic_data->HX_XY_REVERSE = FIX_HX_XY_REVERSE; + ic_data->HX_INT_IS_EDGE = FIX_HX_INT_IS_EDGE; +#endif + I("%s:HX_RX_NUM =%d,HX_TX_NUM =%d,HX_MAX_PT=%d \n", __func__, ic_data->HX_RX_NUM, ic_data->HX_TX_NUM, ic_data->HX_MAX_PT); + I("%s:HX_XY_REVERSE =%d,HX_Y_RES =%d,HX_X_RES=%d \n", __func__, ic_data->HX_XY_REVERSE, ic_data->HX_Y_RES, ic_data->HX_X_RES); + I("%s:HX_INT_IS_EDGE =%d \n", __func__, ic_data->HX_INT_IS_EDGE); +} + +static void himax_mcu_reload_config(void) +{ + if (himax_report_data_init()) { + E("%s: allocate data fail\n", __func__); + } + g_core_fp.fp_sense_on(0x00); +} + +static int himax_mcu_get_touch_data_size(void) +{ + return HIMAX_TOUCH_DATA_SIZE; +} + +static int himax_mcu_hand_shaking(void) +{ + /* 0:Running, 1:Stop, 2:I2C Fail */ + int result = 0; + return result; +} + +static int himax_mcu_determin_diag_rawdata(int diag_command) +{ + return diag_command % 10; +} + +static int himax_mcu_determin_diag_storage(int diag_command) +{ + return diag_command / 10; +} + +static int himax_mcu_cal_data_len(int raw_cnt_rmd, int HX_MAX_PT, int raw_cnt_max) +{ + int RawDataLen; + + if (raw_cnt_rmd != 0x00) { + RawDataLen = MAX_I2C_TRANS_SZ - ((HX_MAX_PT + raw_cnt_max + 3) * 4) - 1; + } else { + RawDataLen = MAX_I2C_TRANS_SZ - ((HX_MAX_PT + raw_cnt_max + 2) * 4) - 1; + } + + return RawDataLen; +} + +static bool himax_mcu_diag_check_sum(struct himax_report_data *hx_touch_data) +{ + uint16_t check_sum_cal = 0; + int i; + + /* Check 128th byte CRC */ + for (i = 0, check_sum_cal = 0; i < (hx_touch_data->touch_all_size - hx_touch_data->touch_info_size); i += 2) { + check_sum_cal += (hx_touch_data->hx_rawdata_buf[i + 1] * FLASH_RW_MAX_LEN + hx_touch_data->hx_rawdata_buf[i]); + } + + if (check_sum_cal % HX64K != 0) { + I("%s fail=%2X \n", __func__, check_sum_cal); + return 0; + } + + return 1; +} + +static void himax_mcu_diag_parse_raw_data(struct himax_report_data *hx_touch_data, int mul_num, int self_num, uint8_t diag_cmd, int32_t *mutual_data, int32_t *self_data) +{ + diag_mcu_parse_raw_data(hx_touch_data, mul_num, self_num, diag_cmd, mutual_data, self_data); +} + +#ifdef HX_ESD_RECOVERY +static int himax_mcu_ic_esd_recovery(int hx_esd_event, int hx_zero_event, int length) +{ + int ret_val = NO_ERR; + + if (g_zero_event_count > 5) { + g_zero_event_count = 0; + I("[HIMAX TP MSG]: ESD event checked - ALL Zero.\n"); + ret_val = HX_ESD_EVENT; + goto END_FUNCTION; + } + + if (hx_esd_event == length) { + g_zero_event_count = 0; + ret_val = HX_ESD_EVENT; + goto END_FUNCTION; + } else if (hx_zero_event == length) { + g_zero_event_count++; + I("[HIMAX TP MSG]: ALL Zero event is %d times.\n", g_zero_event_count); + ret_val = HX_ZERO_EVENT_COUNT; + goto END_FUNCTION; + } + +END_FUNCTION: + return ret_val; +} + +static void himax_mcu_esd_ic_reset(void) +{ + HX_ESD_RESET_ACTIVATE = 1; +#ifdef HX_RST_PIN_FUNC + himax_mcu_pin_reset(); +#endif + I("%s:\n", __func__); +} +#endif +#endif + +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) +static void himax_mcu_resend_cmd_func (bool suspended) +{ +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) + struct himax_ts_data *ts = private_ts; +#endif +#ifdef HX_SMART_WAKEUP + g_core_fp.fp_set_SMWP_enable(ts->SMWP_enable, suspended); +#endif +#ifdef HX_EDGE_LIMIT + g_core_fp.fp_set_edge_limit_enable(ts->edge_limit_enable, suspended); +#endif +#ifdef HX_HIGH_SENSE + g_core_fp.fp_set_HSEN_enable(ts->HSEN_enable, suspended); +#endif +#ifdef HX_USB_DETECT_GLOBAL + himax_cable_detect_func(true); +#endif +} +#endif + +#ifdef HX_ZERO_FLASH +int G_POWERONOF = 1; + +void hx_dis_rload_0f(int disable) +{ + /*Diable Flash Reload*/ + g_core_fp.fp_flash_write_burst (pzf_op->addr_dis_flash_reload, pzf_op->data_dis_flash_reload); +} + +void himax_mcu_clean_sram_0f(uint8_t *addr, int write_len, int type) +{ + int total_read_times = 0; + int max_bus_size = MAX_I2C_TRANS_SZ; + int total_size_temp = 0; + int total_size = 0; + int address = 0; + int i = 0; + + uint8_t fix_data = 0x00; + uint8_t tmp_addr[4]; + uint8_t tmp_data[MAX_I2C_TRANS_SZ] = {0}; + + I("%s, Entering \n", __func__); + + total_size = write_len; + + if (total_size > 4096) { + max_bus_size = 4096; + } + + total_size_temp = write_len; + + g_core_fp.fp_burst_enable (1); + + tmp_addr[3] = addr[3]; + tmp_addr[2] = addr[2]; + tmp_addr[1] = addr[1]; + tmp_addr[0] = addr[0]; + I("%s, write addr tmp_addr[3]=0x%2.2X, tmp_addr[2]=0x%2.2X, tmp_addr[1]=0x%2.2X, tmp_addr[0]=0x%2.2X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + + switch (type) { + case 0: + fix_data = 0x00; + break; + case 1: + fix_data = 0xAA; + break; + case 2: + fix_data = 0xBB; + break; + } + + for (i = 0; i < MAX_I2C_TRANS_SZ; i++) { + tmp_data[i] = fix_data; + } + + I("%s, total size=%d\n", __func__, total_size); + + if (total_size_temp % max_bus_size == 0) { + total_read_times = total_size_temp / max_bus_size; + } else { + total_read_times = total_size_temp / max_bus_size + 1; + } + + for (i = 0; i < (total_read_times); i++) { + I("[log]write %d time start!\n", i); + if (total_size_temp >= max_bus_size) { + g_core_fp.fp_flash_write_burst_lenth (tmp_addr, tmp_data, max_bus_size); + total_size_temp = total_size_temp - max_bus_size; + } else { + I("last total_size_temp=%d\n", total_size_temp); + g_core_fp.fp_flash_write_burst_lenth (tmp_addr, tmp_data, total_size_temp % max_bus_size); + } + address = ((i+1) * max_bus_size); + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF); + tmp_addr[0] = addr[0] + (uint8_t) ((address) & 0x00FF); + + msleep (10); + } + + I("%s, END \n", __func__); +} + +void himax_mcu_write_sram_0f(const struct firmware *fw_entry, uint8_t *addr, int start_index, uint32_t write_len) +{ + int total_read_times = 0; + int max_bus_size = MAX_I2C_TRANS_SZ; + int total_size_temp = 0; + int address = 0; + int i = 0; + + uint8_t tmp_addr[4]; + uint8_t *tmp_data; + + total_size_temp = write_len; + I("%s, Entering - total write size=%d\n", __func__, total_size_temp); + +#if defined(HX_SPI_OPERATION) + if (write_len > 4096) { + max_bus_size = 4096; + } else { + max_bus_size = write_len; + } +#else + if (write_len > 240) { + max_bus_size = 240; + } else { + max_bus_size = write_len; + } +#endif + + g_core_fp.fp_burst_enable (1); + + tmp_addr[3] = addr[3]; + tmp_addr[2] = addr[2]; + tmp_addr[1] = addr[1]; + tmp_addr[0] = addr[0]; + I("%s, write addr = 0x%02X%02X%02X%02X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + + tmp_data = kzalloc (sizeof (uint8_t) * max_bus_size, GFP_KERNEL); + if (tmp_data == NULL) { + I("%s: Can't allocate enough buf \n", __func__); + return; + } + + /* + for(i = 0;i<10;i++) + { + I("[%d] 0x%2.2X", i, tmp_data[i]); + } + I("\n"); + */ + if (total_size_temp % max_bus_size == 0) { + total_read_times = total_size_temp / max_bus_size; + } else { + total_read_times = total_size_temp / max_bus_size + 1; + } + + for (i = 0; i < (total_read_times); i++) { + /*I("[log]write %d time start!\n", i); + I("[log]addr[3]=0x%02X, addr[2]=0x%02X, addr[1]=0x%02X, addr[0]=0x%02X!\n", tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]);*/ + + if (total_size_temp >= max_bus_size) { + memcpy (tmp_data, &fw_entry->data[start_index+i * max_bus_size], max_bus_size); + g_core_fp.fp_flash_write_burst_lenth (tmp_addr, tmp_data, max_bus_size); + total_size_temp = total_size_temp - max_bus_size; + } else { + memcpy (tmp_data, &fw_entry->data[start_index+i * max_bus_size], total_size_temp % max_bus_size); + I("last total_size_temp=%d\n", total_size_temp % max_bus_size); + g_core_fp.fp_flash_write_burst_lenth (tmp_addr, tmp_data, total_size_temp % max_bus_size); + } + + /*I("[log]write %d time end!\n", i);*/ + address = ((i+1) * max_bus_size); + tmp_addr[0] = addr[0] + (uint8_t) ((address) & 0x00FF); + + if (tmp_addr[0] < addr[0]) { + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF) + 1; + } else { + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF); + } + + + udelay (100); + } + I("%s, End \n", __func__); + kfree (tmp_data); +} + +int himax_sram_write_crc_check(const struct firmware *fw_entry, uint8_t *addr, int strt_idx, uint32_t len) +{ + int retry = 0; + int crc = -1; + + do { + g_core_fp.fp_write_sram_0f (fw_entry, addr, strt_idx, len); + crc = g_core_fp.fp_check_CRC (addr, len); + retry++; + I("%s, HW CRC %s in %d time \n", __func__, (crc == 0)?"OK":"Fail", retry); + } while (crc != 0 && retry < 3); + + return crc; +} + +int himax_zf_part_info(const struct firmware *fw_entry) +{ + int part_num = 0; + int ret = 0; + int i = 0; + uint8_t buf[16]; + struct zf_info *zf_info_arr; + + /*1. get number of partition*/ + part_num = fw_entry->data[HX64K + 12]; + I("%s, Number of partition is %d\n", __func__, part_num); + if (part_num <= 0) + part_num = 1; + + /*2. initial struct of array*/ + zf_info_arr = kzalloc(part_num * sizeof(struct zf_info), GFP_KERNEL); + if (zf_info_arr == NULL) { + E("%s, Allocate ZF info array failed!\n", __func__); + return MEM_ALLOC_FAIL; + } + + for (i = 0; i < part_num; i++) { + /*3. get all partition*/ + memcpy(buf, &fw_entry->data[i * 0x10 + HX64K], 16); + memcpy(zf_info_arr[i].sram_addr, buf, 4); + zf_info_arr[i].write_size = buf[5] << 8 | buf[4]; + zf_info_arr[i].fw_addr = buf[9] << 8 | buf[8]; + I("%s,[%d] SRAM addr = %02X%02X%02X%02X!\n", __func__, i, zf_info_arr[i].sram_addr[3], zf_info_arr[i].sram_addr[2], + zf_info_arr[i].sram_addr[1], zf_info_arr[i].sram_addr[0]); + I("%s,[%d] fw_addr = %04X!\n", __func__, i, zf_info_arr[i].fw_addr); + I("%s,[%d] write_size = %d!\n", __func__, i, zf_info_arr[i].write_size); + + /*4. write to sram*/ + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, zf_info_arr[i].sram_addr, zf_info_arr[i].fw_addr, zf_info_arr[i].write_size) != 0) { + E("%s, HW CRC FAIL\n", __func__); + } + } else { + g_core_fp.fp_clean_sram_0f(zf_info_arr[i].sram_addr, zf_info_arr[i].write_size, 2); + } + } + + kfree (zf_info_arr); + + return ret; +} + +void himax_mcu_firmware_update_0f(const struct firmware *fw_entry) +{ + int ret = 0; + + I("%s,Entering - total FW size=%d\n", __func__, (int)fw_entry->size); + + g_core_fp.fp_register_write(pzf_op->addr_system_reset, 4, pzf_op->data_system_reset, false); + + g_core_fp.fp_sense_off(); + + /* first 48K */ + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_sram_start_addr, 0, HX_48K_SZ) != 0) + E("%s, HW CRC FAIL - Main SRAM 48K\n", __func__); + if ((int)fw_entry->size > HX64K) { + ret = himax_zf_part_info(fw_entry); + } else { + /*config info*/ + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_cfg_info, 0xC000, 128) != 0) + E("Config info CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_cfg_info, 128, 2); + } + + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_fw_cfg_1, 0xC100, 528) != 0) + E("FW config 1 CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_fw_cfg_1, 528, 1); + } + + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_fw_cfg_3, 0xCA00, 128) != 0) + E("FW config 3 CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_fw_cfg_3, 128, 2); + } + + /*ADC config*/ + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_adc_cfg_1, 0xD640, 1200) != 0) + E("ADC config 1 CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_adc_cfg_1, 1200, 2); + } + + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_adc_cfg_2, 0xD320, 800) != 0) + E("ADC config 2 CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_adc_cfg_2, 800, 2); + } + + /*mapping table*/ + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_map_table, 0xE000, 1536) != 0) + E("Mapping table CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_map_table, 1536, 2); + } + } + + /* set n frame=0*/ + if (G_POWERONOF == 1) { + g_core_fp.fp_write_sram_0f(fw_entry, pzf_op->data_mode_switch, 0xC30C, 4); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_mode_switch, 4, 2); + } + + /*DSRAM func initial*/ + g_core_fp.fp_assign_sorting_mode(pfw_op->data_clear); + + I("%s, End \n", __func__); +} + +int hx_0f_op_file_dirly(char *file_name) +{ + int err = NO_ERR; + const struct firmware *fw_entry = NULL; + + + I("%s, Entering \n", __func__); + I("file name = %s\n", file_name); + err = request_firmware (&fw_entry, file_name, private_ts->dev); + if (err < 0) { + E("%s, fail in line%d error code=%d,file maybe fail\n", __func__, __LINE__, err); + return err; + } + + himax_int_enable (0); + + if (g_f_0f_updat == 1) { + I("%s:[Warning]Other thread is updating now!\n", __func__); + err = -1; + return err; + } else { + I("%s:Entering Update Flow!\n", __func__); + g_f_0f_updat = 1; + } + + g_core_fp.fp_firmware_update_0f (fw_entry); + release_firmware (fw_entry); + + g_f_0f_updat = 0; + I("%s, END \n", __func__); + return err; +} + +int himax_mcu_0f_operation_dirly(void) +{ + int err = NO_ERR; + const struct firmware *fw_entry = NULL; + + + I("%s, Entering \n", __func__); + I("file name = %s\n", i_CTPM_firmware_name); + err = request_firmware (&fw_entry, i_CTPM_firmware_name, private_ts->dev); + if (err < 0) { + E("%s, fail in line%d error code=%d,file maybe fail\n", __func__, __LINE__, err); + return err; + } + + himax_int_enable (0); + + if (g_f_0f_updat == 1) { + I("%s:[Warning]Other thread is updating now!\n", __func__); + err = -1; + return err; + } else { + I("%s:Entering Update Flow!\n", __func__); + g_f_0f_updat = 1; + } + + g_core_fp.fp_firmware_update_0f (fw_entry); + release_firmware (fw_entry); + + g_f_0f_updat = 0; + I("%s, END \n", __func__); + return err; +} +void himax_mcu_0f_operation(struct work_struct *work) +{ + int err = NO_ERR; + const struct firmware *fw_entry = NULL; + + + I("%s, Entering \n", __func__); + I("file name = %s\n", i_CTPM_firmware_name); + err = request_firmware (&fw_entry, i_CTPM_firmware_name, private_ts->dev); + if (err < 0) { + E("%s, fail in line%d error code=%d,file maybe fail\n", __func__, __LINE__, err); + return ; + } + + if (g_f_0f_updat == 1) { + I("%s:[Warning]Other thread is updating now!\n", __func__); + return ; + } else { + I("%s:Entering Update Flow!\n", __func__); + g_f_0f_updat = 1; + } + + himax_int_enable (0); + + g_core_fp.fp_firmware_update_0f (fw_entry); + release_firmware (fw_entry); + + g_core_fp.fp_reload_disable(0); + msleep (10); + g_core_fp.fp_read_FW_ver(); + g_core_fp.fp_touch_information(); + msleep (10); + g_core_fp.fp_sense_on(0x00); + msleep (10); + I("%s:End \n", __func__); + himax_int_enable (1); + + g_f_0f_updat = 0; + I("%s, END \n", __func__); + return ; +} + +static int himax_mcu_0f_esd_check(void) +{ + uint8_t tmp_data[FOUR_BYTE_DATA_SZ]; + int ret = NO_ERR; + + I("Enter %s\n", __func__); + + g_core_fp.fp_register_read(pzf_op->addr_sts_chk, FOUR_BYTE_DATA_SZ, tmp_data, 0); + + if (tmp_data[0] != pzf_op->data_activ_sts[0]) { + ret = ERR_STS_WRONG; + I("%s:status : %8X = %2X\n", __func__, zf_addr_sts_chk, tmp_data[0]); + } + + g_core_fp.fp_register_read(pzf_op->addr_activ_relod, FOUR_BYTE_DATA_SZ, tmp_data, 0); + + if (tmp_data[0] != pzf_op->data_activ_in[0]) { + ret = ERR_STS_WRONG; + I("%s:status : %8X = %2X\n", __func__, zf_addr_activ_relod, tmp_data[0]); + } + + return ret; +} + + +#ifdef HX_0F_DEBUG +void himax_mcu_read_sram_0f(const struct firmware *fw_entry, uint8_t *addr, int start_index, int read_len) +{ + int total_read_times = 0; + int max_bus_size = MAX_I2C_TRANS_SZ; + int total_size_temp = 0; + int total_size = 0; + int address = 0; + int i = 0, j = 0; + int not_same = 0; + + uint8_t tmp_addr[4]; + uint8_t *temp_info_data; + int *not_same_buff; + + I("%s, Entering \n", __func__); + + g_core_fp.fp_burst_enable (1); + + total_size = read_len; + + total_size_temp = read_len; + +#if defined(HX_SPI_OPERATION) + if (read_len > 2048) { + max_bus_size = 2048; + } else { + max_bus_size = read_len; + } +#else + if (read_len > 240) { + max_bus_size = 240; + } else { + max_bus_size = read_len; + } +#endif + + + temp_info_data = kzalloc (sizeof (uint8_t) * total_size, GFP_KERNEL); + not_same_buff = kzalloc (sizeof (int) * total_size, GFP_KERNEL); + + + tmp_addr[3] = addr[3]; + tmp_addr[2] = addr[2]; + tmp_addr[1] = addr[1]; + tmp_addr[0] = addr[0]; + I("%s, read addr tmp_addr[3]=0x%2.2X, tmp_addr[2]=0x%2.2X, tmp_addr[1]=0x%2.2X, tmp_addr[0]=0x%2.2X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + + I("%s, total size=%d\n", __func__, total_size); + + g_core_fp.fp_burst_enable (1); + + if (total_size % max_bus_size == 0) { + total_read_times = total_size / max_bus_size; + } else { + total_read_times = total_size / max_bus_size + 1; + } + + for (i = 0; i < (total_read_times); i++) { + if (total_size_temp >= max_bus_size) { + g_core_fp.fp_register_read (tmp_addr, max_bus_size, &temp_info_data[i*max_bus_size], false); + total_size_temp = total_size_temp - max_bus_size; + } else { + g_core_fp.fp_register_read (tmp_addr, total_size_temp % max_bus_size, &temp_info_data[i*max_bus_size], false); + } + + address = ((i+1) * max_bus_size); + tmp_addr[0] = addr[0] + (uint8_t) ((address) & 0x00FF); + if (tmp_addr[0] < addr[0]) { + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF) + 1; + } else { + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF); + } + + msleep (10); + } + I("%s, READ Start \n", __func__); + I("%s, start_index = %d \n", __func__, start_index); + j = start_index; + for (i = 0; i < read_len; i++, j++) { + if (fw_entry->data[j] != temp_info_data[i]) { + not_same++; + not_same_buff[i] = 1; + } + + I("0x%2.2X, ", temp_info_data[i]); + + if (i > 0 && i%16 == 15) { + printk ("\n"); + } + } + I("%s, READ END \n", __func__); + I("%s, Not Same count=%d\n", __func__, not_same); + if (not_same != 0) { + j = start_index; + for (i = 0; i < read_len; i++, j++) { + if (not_same_buff[i] == 1) { + I("bin = [%d] 0x%2.2X\n", i, fw_entry->data[j]); + } + } + for (i = 0; i < read_len; i++, j++) { + if (not_same_buff[i] == 1) { + I("sram = [%d] 0x%2.2X \n", i, temp_info_data[i]); + } + } + } + I("%s, READ END \n", __func__); + I("%s, Not Same count=%d\n", __func__, not_same); + I("%s, END \n", __func__); + + kfree (not_same_buff); + kfree (temp_info_data); +} + +void himax_mcu_read_all_sram(uint8_t *addr, int read_len) +{ + int total_read_times = 0; + int max_bus_size = MAX_I2C_TRANS_SZ; + int total_size_temp = 0; + int total_size = 0; + int address = 0; + int i = 0; + /* + struct file *fn; + struct filename *vts_name; + */ + + uint8_t tmp_addr[4]; + uint8_t *temp_info_data; + + I("%s, Entering \n", __func__); + + g_core_fp.fp_burst_enable (1); + + total_size = read_len; + + total_size_temp = read_len; + + temp_info_data = kzalloc (sizeof (uint8_t) * total_size, GFP_KERNEL); + + + tmp_addr[3] = addr[3]; + tmp_addr[2] = addr[2]; + tmp_addr[1] = addr[1]; + tmp_addr[0] = addr[0]; + I("%s, read addr tmp_addr[3]=0x%2.2X, tmp_addr[2]=0x%2.2X, tmp_addr[1]=0x%2.2X, tmp_addr[0]=0x%2.2X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + + I("%s, total size=%d\n", __func__, total_size); + + if (total_size % max_bus_size == 0) { + total_read_times = total_size / max_bus_size; + } else { + total_read_times = total_size / max_bus_size + 1; + } + + for (i = 0; i < (total_read_times); i++) { + if (total_size_temp >= max_bus_size) { + g_core_fp.fp_register_read (tmp_addr, max_bus_size, &temp_info_data[i*max_bus_size], false); + total_size_temp = total_size_temp - max_bus_size; + } else { + g_core_fp.fp_register_read (tmp_addr, total_size_temp % max_bus_size, &temp_info_data[i*max_bus_size], false); + } + + address = ((i+1) * max_bus_size); + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF); + tmp_addr[0] = addr[0] + (uint8_t) ((address) & 0x00FF); + + msleep (10); + } + I("%s, NOW addr tmp_addr[3]=0x%2.2X, tmp_addr[2]=0x%2.2X, tmp_addr[1]=0x%2.2X, tmp_addr[0]=0x%2.2X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + /*for(i = 0;i 0 && i%16 == 15) + printk("\n"); + }*/ + + /* need modify + I("Now Write File start!\n"); + vts_name = getname_kernel("/sdcard/dump_dsram.txt"); + fn = file_open_name(vts_name, O_CREAT | O_WRONLY, 0); + if (!IS_ERR (fn)) { + I("%s create file and ready to write\n", __func__); + fn->f_op->write (fn, temp_info_data, read_len*sizeof (uint8_t), &fn->f_pos); + filp_close (fn, NULL); + } + I("Now Write File End!\n"); + */ + + I("%s, END \n", __func__); + + kfree (temp_info_data); +} + +void himax_mcu_firmware_read_0f(const struct firmware *fw_entry, int type) +{ + uint8_t tmp_addr[4]; + + I("%s, Entering \n", __func__); + if (type == 0) { /* first 48K */ + g_core_fp.fp_read_sram_0f (fw_entry, pzf_op->data_sram_start_addr, 0, HX_48K_SZ); + g_core_fp.fp_read_all_sram (tmp_addr, 0xC000); + } else { /*last 16k*/ + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_cfg_info, 0xC000, 132); + + /*FW config*/ + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_fw_cfg_1, 0xC0FE, 484); + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_fw_cfg_2, 0xC9DE, 36); + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_fw_cfg_3, 0xCA00, 72); + + /*ADC config*/ + + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_adc_cfg_1, 0xD630, 1188); + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_adc_cfg_2, 0xD318, 792); + + + /*mapping table*/ + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_map_table, 0xE000, 1536); + + /* set n frame=0*/ + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_mode_switch, 0xC30C, 4); + } + + I("%s, END \n", __func__); +} + +void himax_mcu_0f_operation_check(int type) +{ + int err = NO_ERR; + const struct firmware *fw_entry = NULL; + /* char *firmware_name = "himax.bin"; */ + + + I("%s, Entering \n", __func__); + I("file name = %s\n", i_CTPM_firmware_name); + + + err = request_firmware(&fw_entry, i_CTPM_firmware_name, private_ts->dev); + if (err < 0) { + E("%s, fail in line%d error code=%d\n", __func__, __LINE__, err); + return ; + } + + I("first 4 bytes 0x%2X, 0x%2X, 0x%2X, 0x%2X !\n", fw_entry->data[0], fw_entry->data[1], fw_entry->data[2], fw_entry->data[3]); + I("next 4 bytes 0x%2X, 0x%2X, 0x%2X, 0x%2X !\n", fw_entry->data[4], fw_entry->data[5], fw_entry->data[6], fw_entry->data[7]); + I("and next 4 bytes 0x%2X, 0x%2X, 0x%2X, 0x%2X !\n", fw_entry->data[8], fw_entry->data[9], fw_entry->data[10], fw_entry->data[11]); + + g_core_fp.fp_firmware_read_0f(fw_entry, type); + + release_firmware(fw_entry); + I("%s, END \n", __func__); + return ; +} +#endif + +#endif + +#ifdef CORE_INIT +/* init start */ +static void himax_mcu_fp_init(void) +{ +#ifdef CORE_IC + g_core_fp.fp_burst_enable = himax_mcu_burst_enable; + g_core_fp.fp_register_read = himax_mcu_register_read; + g_core_fp.fp_flash_write_burst = himax_mcu_flash_write_burst; + g_core_fp.fp_flash_write_burst_lenth = himax_mcu_flash_write_burst_lenth; + g_core_fp.fp_register_write = himax_mcu_register_write; + g_core_fp.fp_interface_on = himax_mcu_interface_on; + g_core_fp.fp_sense_on = himax_mcu_sense_on; + g_core_fp.fp_sense_off = himax_mcu_sense_off; + g_core_fp.fp_wait_wip = himax_mcu_wait_wip; + g_core_fp.fp_init_psl = himax_mcu_init_psl; + g_core_fp.fp_resume_ic_action = himax_mcu_resume_ic_action; + g_core_fp.fp_suspend_ic_action = himax_mcu_suspend_ic_action; + g_core_fp.fp_power_on_init = himax_mcu_power_on_init; +#endif +#ifdef CORE_FW + g_core_fp.fp_system_reset = himax_mcu_system_reset; + g_core_fp.fp_Calculate_CRC_with_AP = himax_mcu_Calculate_CRC_with_AP; + g_core_fp.fp_check_CRC = himax_mcu_check_CRC; + g_core_fp.fp_set_reload_cmd = himax_mcu_set_reload_cmd; + g_core_fp.fp_program_reload = himax_mcu_program_reload; + g_core_fp.fp_set_SMWP_enable = himax_mcu_set_SMWP_enable; + g_core_fp.fp_set_edge_limit_enable = himax_mcu_set_edge_limit_enable; + g_core_fp.fp_set_HSEN_enable = himax_mcu_set_HSEN_enable; + g_core_fp.fp_usb_detect_set = himax_mcu_usb_detect_set; + g_core_fp.fp_diag_register_set = himax_mcu_diag_register_set; + g_core_fp.fp_chip_self_test = himax_mcu_chip_self_test; + g_core_fp.fp_idle_mode = himax_mcu_idle_mode; + g_core_fp.fp_reload_disable = himax_mcu_reload_disable; + g_core_fp.fp_check_chip_version = himax_mcu_check_chip_version; + g_core_fp.fp_read_ic_trigger_type = himax_mcu_read_ic_trigger_type; + g_core_fp.fp_read_i2c_status = himax_mcu_read_i2c_status; + g_core_fp.fp_read_FW_ver = himax_mcu_read_FW_ver; + g_core_fp.fp_read_event_stack = himax_mcu_read_event_stack; + g_core_fp.fp_return_event_stack = himax_mcu_return_event_stack; + g_core_fp.fp_calculateChecksum = himax_mcu_calculateChecksum; + g_core_fp.fp_read_FW_status = himax_mcu_read_FW_status; + g_core_fp.fp_irq_switch = himax_mcu_irq_switch; + g_core_fp.fp_assign_sorting_mode = himax_mcu_assign_sorting_mode; + g_core_fp.fp_check_sorting_mode = himax_mcu_check_sorting_mode; + g_core_fp.fp_switch_mode = himax_mcu_switch_mode; + g_core_fp.fp_read_DD_status = himax_mcu_read_DD_status; +#endif +#ifdef CORE_FLASH + g_core_fp.fp_chip_erase = himax_mcu_chip_erase; + g_core_fp.fp_block_erase = himax_mcu_block_erase; + g_core_fp.fp_sector_erase = himax_mcu_sector_erase; + g_core_fp.fp_flash_programming = himax_mcu_flash_programming; + g_core_fp.fp_flash_page_write = himax_mcu_flash_page_write; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_32k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_32k; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_60k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_60k; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_64k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_64k; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_124k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_124k; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_128k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_128k; + g_core_fp.fp_flash_dump_func = himax_mcu_flash_dump_func; + g_core_fp.fp_flash_lastdata_check = himax_mcu_flash_lastdata_check; +#endif +#ifdef CORE_SRAM + g_core_fp.fp_sram_write = himax_mcu_sram_write; + g_core_fp.fp_sram_verify = himax_mcu_sram_verify; + g_core_fp.fp_get_DSRAM_data = himax_mcu_get_DSRAM_data; +#endif +#ifdef CORE_DRIVER + g_core_fp.fp_chip_detect = himax_mcu_detect_ic; + g_core_fp.fp_chip_init = himax_mcu_init_ic; +#ifdef HX_AUTO_UPDATE_FW + g_core_fp.fp_fw_ver_bin = himax_mcu_fw_ver_bin; +#endif +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_pin_reset = himax_mcu_pin_reset; + g_core_fp.fp_ic_reset = himax_mcu_ic_reset; +#endif + g_core_fp.fp_touch_information = himax_mcu_touch_information; + g_core_fp.fp_reload_config = himax_mcu_reload_config; + g_core_fp.fp_get_touch_data_size = himax_mcu_get_touch_data_size; + g_core_fp.fp_hand_shaking = himax_mcu_hand_shaking; + g_core_fp.fp_determin_diag_rawdata = himax_mcu_determin_diag_rawdata; + g_core_fp.fp_determin_diag_storage = himax_mcu_determin_diag_storage; + g_core_fp.fp_cal_data_len = himax_mcu_cal_data_len; + g_core_fp.fp_diag_check_sum = himax_mcu_diag_check_sum; + g_core_fp.fp_diag_parse_raw_data = himax_mcu_diag_parse_raw_data; +#ifdef HX_ESD_RECOVERY + g_core_fp.fp_ic_esd_recovery = himax_mcu_ic_esd_recovery; + g_core_fp.fp_esd_ic_reset = himax_mcu_esd_ic_reset; +#endif +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) + g_core_fp.fp_resend_cmd_func = himax_mcu_resend_cmd_func; +#endif +#endif +#ifdef HX_ZERO_FLASH + g_core_fp.fp_reload_disable = hx_dis_rload_0f; + g_core_fp.fp_clean_sram_0f = himax_mcu_clean_sram_0f; + g_core_fp.fp_write_sram_0f = himax_mcu_write_sram_0f; + g_core_fp.fp_firmware_update_0f = himax_mcu_firmware_update_0f; + g_core_fp.fp_0f_operation = himax_mcu_0f_operation; + g_core_fp.fp_0f_operation_dirly = himax_mcu_0f_operation_dirly; + g_core_fp.fp_0f_op_file_dirly = hx_0f_op_file_dirly; + g_core_fp.fp_0f_esd_check = himax_mcu_0f_esd_check; +#ifdef HX_0F_DEBUG + g_core_fp.fp_read_sram_0f = himax_mcu_read_sram_0f; + g_core_fp.fp_read_all_sram = himax_mcu_read_all_sram; + g_core_fp.fp_firmware_read_0f = himax_mcu_firmware_read_0f; + g_core_fp.fp_0f_operation_check = himax_mcu_0f_operation_check; +#endif +#endif +} + +void himax_mcu_in_cmd_struct_init(void) +{ + I("%s: Entering!\n", __func__); + g_core_cmd_op = kzalloc(sizeof(struct himax_core_command_operation), GFP_KERNEL); + g_core_cmd_op->ic_op = kzalloc(sizeof(struct ic_operation), GFP_KERNEL); + g_core_cmd_op->fw_op = kzalloc(sizeof(struct fw_operation), GFP_KERNEL); + g_core_cmd_op->flash_op = kzalloc(sizeof(struct flash_operation), GFP_KERNEL); + g_core_cmd_op->sram_op = kzalloc(sizeof(struct sram_operation), GFP_KERNEL); + g_core_cmd_op->driver_op = kzalloc(sizeof(struct driver_operation), GFP_KERNEL); + pic_op = g_core_cmd_op->ic_op; + pfw_op = g_core_cmd_op->fw_op; + pflash_op = g_core_cmd_op->flash_op; + psram_op = g_core_cmd_op->sram_op; + pdriver_op = g_core_cmd_op->driver_op; +#ifdef HX_ZERO_FLASH + g_core_cmd_op->zf_op = kzalloc(sizeof(struct zf_operation), GFP_KERNEL); + pzf_op = g_core_cmd_op->zf_op; +#endif + himax_mcu_fp_init(); +} + +/* +static void himax_mcu_in_cmd_struct_free(void) +{ + pic_op = NULL; + pfw_op = NULL; + pflash_op = NULL; + psram_op = NULL; + pdriver_op = NULL; + kfree(g_core_cmd_op); + kfree(g_core_cmd_op->ic_op); + kfree(g_core_cmd_op->flash_op); + kfree(g_core_cmd_op->sram_op); + kfree(g_core_cmd_op->driver_op); +} +*/ + +void himax_in_parse_assign_cmd(uint32_t addr, uint8_t *cmd, int len) +{ + /*I("%s: Entering!\n", __func__);*/ + switch (len) { + case 1: + cmd[0] = addr; + /*I("%s: cmd[0] = 0x%02X\n", __func__, cmd[0]);*/ + break; + + case 2: + cmd[0] = addr % 0x100; + cmd[1] = (addr >> 8) % 0x100; + /*I("%s: cmd[0] = 0x%02X,cmd[1] = 0x%02X\n", __func__, cmd[0], cmd[1]);*/ + break; + + case 4: + cmd[0] = addr % 0x100; + cmd[1] = (addr >> 8) % 0x100; + cmd[2] = (addr >> 16) % 0x100; + cmd[3] = addr / 0x1000000; + /* I("%s: cmd[0] = 0x%02X,cmd[1] = 0x%02X,cmd[2] = 0x%02X,cmd[3] = 0x%02X\n", + __func__, cmd[0], cmd[1], cmd[2], cmd[3]);*/ + break; + + default: + E("%s: input length fault,len = %d!\n", __func__, len); + } +} + +void himax_mcu_in_cmd_init(void) +{ + I("%s: Entering!\n", __func__); +#ifdef CORE_IC + himax_in_parse_assign_cmd(ic_adr_ahb_addr_byte_0, pic_op->addr_ahb_addr_byte_0, sizeof(pic_op->addr_ahb_addr_byte_0)); + himax_in_parse_assign_cmd(ic_adr_ahb_rdata_byte_0, pic_op->addr_ahb_rdata_byte_0, sizeof(pic_op->addr_ahb_rdata_byte_0)); + himax_in_parse_assign_cmd(ic_adr_ahb_access_direction, pic_op->addr_ahb_access_direction, sizeof(pic_op->addr_ahb_access_direction)); + himax_in_parse_assign_cmd(ic_adr_conti, pic_op->addr_conti, sizeof(pic_op->addr_conti)); + himax_in_parse_assign_cmd(ic_adr_incr4, pic_op->addr_incr4, sizeof(pic_op->addr_incr4)); + himax_in_parse_assign_cmd(ic_adr_i2c_psw_lb, pic_op->adr_i2c_psw_lb, sizeof(pic_op->adr_i2c_psw_lb)); + himax_in_parse_assign_cmd(ic_adr_i2c_psw_ub, pic_op->adr_i2c_psw_ub, sizeof(pic_op->adr_i2c_psw_ub)); + himax_in_parse_assign_cmd(ic_cmd_ahb_access_direction_read, pic_op->data_ahb_access_direction_read, sizeof(pic_op->data_ahb_access_direction_read)); + himax_in_parse_assign_cmd(ic_cmd_conti, pic_op->data_conti, sizeof(pic_op->data_conti)); + himax_in_parse_assign_cmd(ic_cmd_incr4, pic_op->data_incr4, sizeof(pic_op->data_incr4)); + himax_in_parse_assign_cmd(ic_cmd_i2c_psw_lb, pic_op->data_i2c_psw_lb, sizeof(pic_op->data_i2c_psw_lb)); + himax_in_parse_assign_cmd(ic_cmd_i2c_psw_ub, pic_op->data_i2c_psw_ub, sizeof(pic_op->data_i2c_psw_ub)); + himax_in_parse_assign_cmd(ic_adr_tcon_on_rst, pic_op->addr_tcon_on_rst, sizeof(pic_op->addr_tcon_on_rst)); + himax_in_parse_assign_cmd(ic_addr_adc_on_rst, pic_op->addr_adc_on_rst, sizeof(pic_op->addr_adc_on_rst)); + himax_in_parse_assign_cmd(ic_adr_psl, pic_op->addr_psl, sizeof(pic_op->addr_psl)); + himax_in_parse_assign_cmd(ic_adr_cs_central_state, pic_op->addr_cs_central_state, sizeof(pic_op->addr_cs_central_state)); + himax_in_parse_assign_cmd(ic_cmd_rst, pic_op->data_rst, sizeof(pic_op->data_rst)); +#endif +#ifdef CORE_FW + himax_in_parse_assign_cmd(fw_addr_system_reset, pfw_op->addr_system_reset, sizeof(pfw_op->addr_system_reset)); + himax_in_parse_assign_cmd(fw_addr_safe_mode_release_pw, pfw_op->addr_safe_mode_release_pw, sizeof(pfw_op->addr_safe_mode_release_pw)); + himax_in_parse_assign_cmd(fw_addr_ctrl_fw, pfw_op->addr_ctrl_fw_isr, sizeof(pfw_op->addr_ctrl_fw_isr)); + himax_in_parse_assign_cmd(fw_addr_flag_reset_event, pfw_op->addr_flag_reset_event, sizeof(pfw_op->addr_flag_reset_event)); + himax_in_parse_assign_cmd(fw_addr_hsen_enable, pfw_op->addr_hsen_enable, sizeof(pfw_op->addr_hsen_enable)); + himax_in_parse_assign_cmd(fw_addr_smwp_enable, pfw_op->addr_smwp_enable, sizeof(pfw_op->addr_smwp_enable)); + himax_in_parse_assign_cmd(fw_addr_edge_limit_enable, pfw_op->addr_edge_limit_enable, sizeof(pfw_op->addr_edge_limit_enable)); + himax_in_parse_assign_cmd(fw_addr_program_reload_from, pfw_op->addr_program_reload_from, sizeof(pfw_op->addr_program_reload_from)); + himax_in_parse_assign_cmd(fw_addr_program_reload_to, pfw_op->addr_program_reload_to, sizeof(pfw_op->addr_program_reload_to)); + himax_in_parse_assign_cmd(fw_addr_program_reload_page_write, pfw_op->addr_program_reload_page_write, sizeof(pfw_op->addr_program_reload_page_write)); + himax_in_parse_assign_cmd(fw_addr_raw_out_sel, pfw_op->addr_raw_out_sel, sizeof(pfw_op->addr_raw_out_sel)); + himax_in_parse_assign_cmd(fw_addr_reload_status, pfw_op->addr_reload_status, sizeof(pfw_op->addr_reload_status)); + himax_in_parse_assign_cmd(fw_addr_reload_crc32_result, pfw_op->addr_reload_crc32_result, sizeof(pfw_op->addr_reload_crc32_result)); + himax_in_parse_assign_cmd(fw_addr_reload_addr_from, pfw_op->addr_reload_addr_from, sizeof(pfw_op->addr_reload_addr_from)); + himax_in_parse_assign_cmd(fw_addr_reload_addr_cmd_beat, pfw_op->addr_reload_addr_cmd_beat, sizeof(pfw_op->addr_reload_addr_cmd_beat)); + himax_in_parse_assign_cmd(fw_addr_selftest_addr_en, pfw_op->addr_selftest_addr_en, sizeof(pfw_op->addr_selftest_addr_en)); + himax_in_parse_assign_cmd(fw_addr_criteria_addr, pfw_op->addr_criteria_addr, sizeof(pfw_op->addr_criteria_addr)); + himax_in_parse_assign_cmd(fw_addr_set_frame_addr, pfw_op->addr_set_frame_addr, sizeof(pfw_op->addr_set_frame_addr)); + himax_in_parse_assign_cmd(fw_addr_selftest_result_addr, pfw_op->addr_selftest_result_addr, sizeof(pfw_op->addr_selftest_result_addr)); + himax_in_parse_assign_cmd(fw_addr_sorting_mode_en, pfw_op->addr_sorting_mode_en, sizeof(pfw_op->addr_sorting_mode_en)); + himax_in_parse_assign_cmd(fw_addr_fw_mode_status, pfw_op->addr_fw_mode_status, sizeof(pfw_op->addr_fw_mode_status)); + himax_in_parse_assign_cmd(fw_addr_icid_addr, pfw_op->addr_icid_addr, sizeof(pfw_op->addr_icid_addr)); + himax_in_parse_assign_cmd(fw_addr_trigger_addr, pfw_op->addr_trigger_addr, sizeof(pfw_op->addr_trigger_addr)); + himax_in_parse_assign_cmd(fw_addr_fw_ver_addr, pfw_op->addr_fw_ver_addr, sizeof(pfw_op->addr_fw_ver_addr)); + himax_in_parse_assign_cmd(fw_addr_fw_cfg_addr, pfw_op->addr_fw_cfg_addr, sizeof(pfw_op->addr_fw_cfg_addr)); + himax_in_parse_assign_cmd(fw_addr_fw_vendor_addr, pfw_op->addr_fw_vendor_addr, sizeof(pfw_op->addr_fw_vendor_addr)); + himax_in_parse_assign_cmd(fw_addr_fw_state_addr, pfw_op->addr_fw_state_addr, sizeof(pfw_op->addr_fw_state_addr)); + himax_in_parse_assign_cmd(fw_addr_fw_dbg_msg_addr, pfw_op->addr_fw_dbg_msg_addr, sizeof(pfw_op->addr_fw_dbg_msg_addr)); + himax_in_parse_assign_cmd(fw_addr_chk_fw_status, pfw_op->addr_chk_fw_status, sizeof(pfw_op->addr_chk_fw_status)); + himax_in_parse_assign_cmd(fw_addr_dd_handshak_addr, pfw_op->addr_dd_handshak_addr, sizeof(pfw_op->addr_dd_handshak_addr)); + himax_in_parse_assign_cmd(fw_addr_dd_data_addr, pfw_op->addr_dd_data_addr, sizeof(pfw_op->addr_dd_data_addr)); + himax_in_parse_assign_cmd(fw_data_system_reset, pfw_op->data_system_reset, sizeof(pfw_op->data_system_reset)); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_active, pfw_op->data_safe_mode_release_pw_active, sizeof(pfw_op->data_safe_mode_release_pw_active)); + himax_in_parse_assign_cmd(fw_data_clear, pfw_op->data_clear, sizeof(pfw_op->data_clear)); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, pfw_op->data_safe_mode_release_pw_reset, sizeof(pfw_op->data_safe_mode_release_pw_reset)); + himax_in_parse_assign_cmd(fw_data_program_reload_start, pfw_op->data_program_reload_start, sizeof(pfw_op->data_program_reload_start)); + himax_in_parse_assign_cmd(fw_data_program_reload_compare, pfw_op->data_program_reload_compare, sizeof(pfw_op->data_program_reload_compare)); + himax_in_parse_assign_cmd(fw_data_program_reload_break, pfw_op->data_program_reload_break, sizeof(pfw_op->data_program_reload_break)); + himax_in_parse_assign_cmd(fw_data_selftest_request, pfw_op->data_selftest_request, sizeof(pfw_op->data_selftest_request)); + himax_in_parse_assign_cmd(fw_data_criteria_aa_top, pfw_op->data_criteria_aa_top, sizeof(pfw_op->data_criteria_aa_top)); + himax_in_parse_assign_cmd(fw_data_criteria_aa_bot, pfw_op->data_criteria_aa_bot, sizeof(pfw_op->data_criteria_aa_bot)); + himax_in_parse_assign_cmd(fw_data_criteria_key_top, pfw_op->data_criteria_key_top, sizeof(pfw_op->data_criteria_key_top)); + himax_in_parse_assign_cmd(fw_data_criteria_key_bot, pfw_op->data_criteria_key_bot, sizeof(pfw_op->data_criteria_key_bot)); + himax_in_parse_assign_cmd(fw_data_criteria_avg_top, pfw_op->data_criteria_avg_top, sizeof(pfw_op->data_criteria_avg_top)); + himax_in_parse_assign_cmd(fw_data_criteria_avg_bot, pfw_op->data_criteria_avg_bot, sizeof(pfw_op->data_criteria_avg_bot)); + himax_in_parse_assign_cmd(fw_data_set_frame, pfw_op->data_set_frame, sizeof(pfw_op->data_set_frame)); + himax_in_parse_assign_cmd(fw_data_selftest_ack_hb, pfw_op->data_selftest_ack_hb, sizeof(pfw_op->data_selftest_ack_hb)); + himax_in_parse_assign_cmd(fw_data_selftest_ack_lb, pfw_op->data_selftest_ack_lb, sizeof(pfw_op->data_selftest_ack_lb)); + himax_in_parse_assign_cmd(fw_data_selftest_pass, pfw_op->data_selftest_pass, sizeof(pfw_op->data_selftest_pass)); + himax_in_parse_assign_cmd(fw_data_normal_cmd, pfw_op->data_normal_cmd, sizeof(pfw_op->data_normal_cmd)); + himax_in_parse_assign_cmd(fw_data_normal_status, pfw_op->data_normal_status, sizeof(pfw_op->data_normal_status)); + himax_in_parse_assign_cmd(fw_data_sorting_cmd, pfw_op->data_sorting_cmd, sizeof(pfw_op->data_sorting_cmd)); + himax_in_parse_assign_cmd(fw_data_sorting_status, pfw_op->data_sorting_status, sizeof(pfw_op->data_sorting_status)); + himax_in_parse_assign_cmd(fw_data_dd_request, pfw_op->data_dd_request, sizeof(pfw_op->data_dd_request)); + himax_in_parse_assign_cmd(fw_data_dd_ack, pfw_op->data_dd_ack, sizeof(pfw_op->data_dd_ack)); + himax_in_parse_assign_cmd(fw_data_idle_dis_pwd, pfw_op->data_idle_dis_pwd, sizeof(pfw_op->data_idle_dis_pwd)); + himax_in_parse_assign_cmd(fw_data_idle_en_pwd, pfw_op->data_idle_en_pwd, sizeof(pfw_op->data_idle_en_pwd)); + himax_in_parse_assign_cmd(fw_data_rawdata_ready_hb, pfw_op->data_rawdata_ready_hb, sizeof(pfw_op->data_rawdata_ready_hb)); + himax_in_parse_assign_cmd(fw_data_rawdata_ready_lb, pfw_op->data_rawdata_ready_lb, sizeof(pfw_op->data_rawdata_ready_lb)); + himax_in_parse_assign_cmd(fw_addr_ahb_addr, pfw_op->addr_ahb_addr, sizeof(pfw_op->addr_ahb_addr)); + himax_in_parse_assign_cmd(fw_data_ahb_dis, pfw_op->data_ahb_dis, sizeof(pfw_op->data_ahb_dis)); + himax_in_parse_assign_cmd(fw_data_ahb_en, pfw_op->data_ahb_en, sizeof(pfw_op->data_ahb_en)); + himax_in_parse_assign_cmd(fw_addr_event_addr, pfw_op->addr_event_addr, sizeof(pfw_op->addr_event_addr)); + himax_in_parse_assign_cmd(fw_usb_detect_addr, pfw_op->addr_usb_detect, sizeof(pfw_op->addr_usb_detect)); +#endif +#ifdef CORE_FLASH + himax_in_parse_assign_cmd(flash_addr_spi200_trans_fmt, pflash_op->addr_spi200_trans_fmt, sizeof(pflash_op->addr_spi200_trans_fmt)); + himax_in_parse_assign_cmd(flash_addr_spi200_trans_ctrl, pflash_op->addr_spi200_trans_ctrl, sizeof(pflash_op->addr_spi200_trans_ctrl)); + himax_in_parse_assign_cmd(flash_addr_spi200_cmd, pflash_op->addr_spi200_cmd, sizeof(pflash_op->addr_spi200_cmd)); + himax_in_parse_assign_cmd(flash_addr_spi200_addr, pflash_op->addr_spi200_addr, sizeof(pflash_op->addr_spi200_addr)); + himax_in_parse_assign_cmd(flash_addr_spi200_data, pflash_op->addr_spi200_data, sizeof(pflash_op->addr_spi200_data)); + himax_in_parse_assign_cmd(flash_addr_spi200_bt_num, pflash_op->addr_spi200_bt_num, sizeof(pflash_op->addr_spi200_bt_num)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_fmt, pflash_op->data_spi200_trans_fmt, sizeof(pflash_op->data_spi200_trans_fmt)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_1, pflash_op->data_spi200_trans_ctrl_1, sizeof(pflash_op->data_spi200_trans_ctrl_1)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_2, pflash_op->data_spi200_trans_ctrl_2, sizeof(pflash_op->data_spi200_trans_ctrl_2)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_3, pflash_op->data_spi200_trans_ctrl_3, sizeof(pflash_op->data_spi200_trans_ctrl_3)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_4, pflash_op->data_spi200_trans_ctrl_4, sizeof(pflash_op->data_spi200_trans_ctrl_4)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_5, pflash_op->data_spi200_trans_ctrl_5, sizeof(pflash_op->data_spi200_trans_ctrl_5)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_1, pflash_op->data_spi200_cmd_1, sizeof(pflash_op->data_spi200_cmd_1)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_2, pflash_op->data_spi200_cmd_2, sizeof(pflash_op->data_spi200_cmd_2)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_3, pflash_op->data_spi200_cmd_3, sizeof(pflash_op->data_spi200_cmd_3)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_4, pflash_op->data_spi200_cmd_4, sizeof(pflash_op->data_spi200_cmd_4)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_5, pflash_op->data_spi200_cmd_5, sizeof(pflash_op->data_spi200_cmd_5)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_6, pflash_op->data_spi200_cmd_6, sizeof(pflash_op->data_spi200_cmd_6)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_7, pflash_op->data_spi200_cmd_7, sizeof(pflash_op->data_spi200_cmd_7)); + himax_in_parse_assign_cmd(flash_data_spi200_addr, pflash_op->data_spi200_addr, sizeof(pflash_op->data_spi200_addr)); +#endif +#ifdef CORE_SRAM + /* sram start*/ + himax_in_parse_assign_cmd(sram_adr_mkey, psram_op->addr_mkey, sizeof(psram_op->addr_mkey)); + himax_in_parse_assign_cmd(sram_adr_rawdata_addr, psram_op->addr_rawdata_addr, sizeof(psram_op->addr_rawdata_addr)); + himax_in_parse_assign_cmd(sram_adr_rawdata_end, psram_op->addr_rawdata_end, sizeof(psram_op->addr_rawdata_end)); + himax_in_parse_assign_cmd(sram_cmd_conti, psram_op->data_conti, sizeof(psram_op->data_conti)); + himax_in_parse_assign_cmd(sram_cmd_fin, psram_op->data_fin, sizeof(psram_op->data_fin)); + himax_in_parse_assign_cmd(sram_passwrd_start, psram_op->passwrd_start, sizeof(psram_op->passwrd_start)); + himax_in_parse_assign_cmd(sram_passwrd_end, psram_op->passwrd_end, sizeof(psram_op->passwrd_end)); + /* sram end*/ +#endif +#ifdef CORE_DRIVER + himax_in_parse_assign_cmd(driver_addr_fw_define_flash_reload, pdriver_op->addr_fw_define_flash_reload, sizeof(pdriver_op->addr_fw_define_flash_reload)); + himax_in_parse_assign_cmd(driver_addr_fw_define_2nd_flash_reload, pdriver_op->addr_fw_define_2nd_flash_reload, sizeof(pdriver_op->addr_fw_define_2nd_flash_reload)); + himax_in_parse_assign_cmd(driver_addr_fw_define_int_is_edge, pdriver_op->addr_fw_define_int_is_edge, sizeof(pdriver_op->addr_fw_define_int_is_edge)); + himax_in_parse_assign_cmd(driver_addr_fw_define_rxnum_txnum_maxpt, pdriver_op->addr_fw_define_rxnum_txnum_maxpt, sizeof(pdriver_op->addr_fw_define_rxnum_txnum_maxpt)); + himax_in_parse_assign_cmd(driver_addr_fw_define_xy_res_enable, pdriver_op->addr_fw_define_xy_res_enable, sizeof(pdriver_op->addr_fw_define_xy_res_enable)); + himax_in_parse_assign_cmd(driver_addr_fw_define_x_y_res, pdriver_op->addr_fw_define_x_y_res, sizeof(pdriver_op->addr_fw_define_x_y_res)); + himax_in_parse_assign_cmd(driver_data_fw_define_flash_reload_dis, pdriver_op->data_fw_define_flash_reload_dis, sizeof(pdriver_op->data_fw_define_flash_reload_dis)); + himax_in_parse_assign_cmd(driver_data_fw_define_flash_reload_en, pdriver_op->data_fw_define_flash_reload_en, sizeof(pdriver_op->data_fw_define_flash_reload_en)); + himax_in_parse_assign_cmd(driver_data_fw_define_rxnum_txnum_maxpt_sorting, pdriver_op->data_fw_define_rxnum_txnum_maxpt_sorting, sizeof(pdriver_op->data_fw_define_rxnum_txnum_maxpt_sorting)); + himax_in_parse_assign_cmd(driver_data_fw_define_rxnum_txnum_maxpt_normal, pdriver_op->data_fw_define_rxnum_txnum_maxpt_normal, sizeof(pdriver_op->data_fw_define_rxnum_txnum_maxpt_normal)); +#endif +#ifdef HX_ZERO_FLASH + himax_in_parse_assign_cmd(zf_addr_dis_flash_reload, pzf_op->addr_dis_flash_reload, sizeof(pzf_op->addr_dis_flash_reload)); + himax_in_parse_assign_cmd(zf_data_dis_flash_reload, pzf_op->data_dis_flash_reload, sizeof(pzf_op->data_dis_flash_reload)); + himax_in_parse_assign_cmd(zf_addr_system_reset, pzf_op->addr_system_reset, sizeof(pzf_op->addr_system_reset)); + himax_in_parse_assign_cmd(zf_data_system_reset, pzf_op->data_system_reset, sizeof(pzf_op->data_system_reset)); + himax_in_parse_assign_cmd(zf_data_sram_start_addr, pzf_op->data_sram_start_addr, sizeof(pzf_op->data_sram_start_addr)); + himax_in_parse_assign_cmd(zf_data_sram_clean, pzf_op->data_sram_clean, sizeof(pzf_op->data_sram_clean)); + himax_in_parse_assign_cmd(zf_data_cfg_info, pzf_op->data_cfg_info, sizeof(pzf_op->data_cfg_info)); + himax_in_parse_assign_cmd(zf_data_fw_cfg_1, pzf_op->data_fw_cfg_1, sizeof(pzf_op->data_fw_cfg_1)); + himax_in_parse_assign_cmd(zf_data_fw_cfg_2, pzf_op->data_fw_cfg_2, sizeof(pzf_op->data_fw_cfg_2)); + himax_in_parse_assign_cmd(zf_data_fw_cfg_2, pzf_op->data_fw_cfg_3, sizeof(pzf_op->data_fw_cfg_3)); + himax_in_parse_assign_cmd(zf_data_adc_cfg_1, pzf_op->data_adc_cfg_1, sizeof(pzf_op->data_adc_cfg_1)); + himax_in_parse_assign_cmd(zf_data_adc_cfg_2, pzf_op->data_adc_cfg_2, sizeof(pzf_op->data_adc_cfg_2)); + himax_in_parse_assign_cmd(zf_data_adc_cfg_3, pzf_op->data_adc_cfg_3, sizeof(pzf_op->data_adc_cfg_3)); + himax_in_parse_assign_cmd(zf_data_map_table, pzf_op->data_map_table, sizeof(pzf_op->data_map_table)); + himax_in_parse_assign_cmd(zf_data_mode_switch, pzf_op->data_mode_switch, sizeof(pzf_op->data_mode_switch)); + himax_in_parse_assign_cmd(zf_addr_sts_chk, pzf_op->addr_sts_chk, sizeof(pzf_op->addr_sts_chk)); + himax_in_parse_assign_cmd(zf_data_activ_sts, pzf_op->data_activ_sts, sizeof(pzf_op->data_activ_sts)); + himax_in_parse_assign_cmd(zf_addr_activ_relod, pzf_op->addr_activ_relod, sizeof(pzf_op->addr_activ_relod)); + himax_in_parse_assign_cmd(zf_data_activ_in, pzf_op->data_activ_in, sizeof(pzf_op->data_activ_in)); +#endif +} + +/* init end*/ +#endif diff --git a/drivers/input/touchscreen/himax_mmi/himax_inspection.c b/drivers/input/touchscreen/himax_mmi/himax_inspection.c new file mode 100644 index 000000000000..520d6a9b3bd7 --- /dev/null +++ b/drivers/input/touchscreen/himax_mmi/himax_inspection.c @@ -0,0 +1,1258 @@ +#include "himax_inspection.h" + +char *hx_self_test_file_name = NULL; + +extern struct himax_core_fp g_core_fp; +extern struct himax_ts_data *private_ts; +extern struct himax_ic_data *ic_data; + +void himax_inspection_init(void); + +void (*fp_himax_self_test_init)(void) = himax_inspection_init; + +#ifdef HX_ESD_RECOVERY + extern u8 HX_ESD_RESET_ACTIVATE; +#endif + +#ifdef HX_INSPECT_LPWUG_TEST +static void himax_press_powerkey(void) +{ + I(" %s POWER KEY event %x press\n", __func__, KEY_POWER); + input_report_key(private_ts->input_dev, KEY_POWER, 1); + input_sync(private_ts->input_dev); + + I(" %s POWER KEY event %x release\n", __func__, KEY_POWER); + input_report_key(private_ts->input_dev, KEY_POWER, 0); + input_sync(private_ts->input_dev); +} +#endif +static uint8_t NOISEMAX; + +#if 0 +static int hx_test_data_pop_out(char *rslt_buf, char *filepath) +{ + + struct file *raw_file = NULL; + struct filename *vts_name = NULL; + mm_segment_t fs; + loff_t pos = 0; + int ret_val = NO_ERR; + I("%s: Entering!\n", __func__); + I("data size=0x%04X\n", (uint32_t)strlen(rslt_buf)); + vts_name = getname_kernel(filepath); + + raw_file = file_open_name(vts_name, O_TRUNC|O_CREAT|O_RDWR, 0660); + if (IS_ERR(raw_file)) { + E("%s open file failed = %ld\n", __func__, PTR_ERR(raw_file)); + ret_val = -EIO; + goto SAVE_DATA_ERR; + } + + fs = get_fs(); + set_fs(get_ds()); + vfs_write(raw_file, rslt_buf, 0x1000 * HX_CRITERIA_ITEM * sizeof(char), &pos); + if (raw_file != NULL) { + filp_close(raw_file, NULL); + } + set_fs(fs); + +SAVE_DATA_ERR: + I("%s: End!\n", __func__); + return ret_val; +} +#endif + +static int hx_test_data_get(uint32_t RAW[], char *start_log, char *result, int now_item) +{ + uint32_t i; + + ssize_t len = 0; + char *testdata = NULL; + uint32_t SZ_SIZE = 0x1000; + + I("%s: Entering, Now type=%s!\n", __func__, g_himax_inspection_mode[now_item]); + + testdata = kzalloc(sizeof(char) * SZ_SIZE, GFP_KERNEL); + + len += snprintf((testdata + len), SZ_SIZE - len, "%s", start_log); + for (i = 0 ; i < ic_data->HX_TX_NUM * ic_data->HX_RX_NUM ; i++) { + if (i > 1 && ((i + 1) % ic_data->HX_RX_NUM) == 0) { + len += snprintf((testdata + len), SZ_SIZE - len, "%5d,\n", RAW[i]); + } else { + len += snprintf((testdata + len), SZ_SIZE - len, "%5d,", RAW[i]); + } + } + len += snprintf((testdata + len), SZ_SIZE - len, "\n%s", result); + + memcpy(&g_rslt_data[g_rslt_data_len], testdata, len); + g_rslt_data_len += len; + I("%s: g_rslt_data_len=%d!\n", __func__, g_rslt_data_len); + + /*memcpy(&g_rslt_data[now_item * SZ_SIZE], testdata, SZ_SIZE);*/ + /* dbg */ + /*for(i = 0; i < SZ_SIZE; i++) + { + I("0x%04X, ", g_rslt_data[i + (now_item * SZ_SIZE)]); + if (i > 0 && (i % 16 == 15)) + printk("\n"); + }*/ + + kfree(testdata); + I("%s: End!\n", __func__); + return NO_ERR; + +} + +static int himax_switch_mode_inspection(int mode) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + I("%s: Entering\n", __func__); + + /*Stop Handshaking*/ + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x00; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + g_core_fp.fp_flash_write_burst_lenth(tmp_addr, tmp_data, 4); + + /*Swtich Mode*/ + switch (mode) { + case HIMAX_INSPECTION_SORTING: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = PWD_SORTING_START; tmp_data[0] = PWD_SORTING_START; + break; + case HIMAX_INSPECTION_OPEN: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = PWD_OPEN_START; tmp_data[0] = PWD_OPEN_START; + break; + case HIMAX_INSPECTION_MICRO_OPEN: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = PWD_OPEN_START; tmp_data[0] = PWD_OPEN_START; + break; + case HIMAX_INSPECTION_SHORT: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = PWD_SHORT_START; tmp_data[0] = PWD_SHORT_START; + break; +#ifdef HX_GAP_TEST + case HIMAX_INSPECTION_GAPTEST_RAW: +#endif + case HIMAX_INSPECTION_RAWDATA: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = PWD_RAWDATA_START; tmp_data[0] = PWD_RAWDATA_START; + break; + case HIMAX_INSPECTION_NOISE: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = PWD_NOISE_START; tmp_data[0] = PWD_NOISE_START; + break; +#ifdef HX_ACT_IDLE_TEST + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = PWD_ACT_IDLE_START; tmp_data[0] = PWD_ACT_IDLE_START; + break; +#endif +#ifdef HX_INSPECT_LPWUG_TEST + case HIMAX_INSPECTION_LPWUG_RAWDATA: + case HIMAX_INSPECTION_LPWUG_NOISE: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = PWD_LPWUG_START; tmp_data[0] = PWD_LPWUG_START; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = PWD_LPWUG_IDLE_START; tmp_data[0] = PWD_LPWUG_IDLE_START; + break; +#endif + default: + I("%s,Nothing to be done!\n", __func__); + break; + } + + if (g_core_fp.fp_assign_sorting_mode != NULL) + g_core_fp.fp_assign_sorting_mode(tmp_data); + I("%s: End of setting!\n", __func__); + + return 0; + +} + +static int himax_get_rawdata(uint32_t RAW[], uint32_t datalen) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + uint8_t *tmp_rawdata; + uint8_t retry = 0; + uint16_t checksum_cal; + uint32_t i = 0; + + uint8_t max_i2c_size = 128; + int address = 0; + int total_read_times = 0; + int total_size = datalen * 2 + 4; + int total_size_temp; +#if 1 + uint32_t j = 0; + uint32_t index = 0; + uint32_t Min_DATA = 0xFFFFFFFF; + uint32_t Max_DATA = 0x00000000; +#endif + + tmp_rawdata = kzalloc(sizeof(uint8_t) * (datalen * 2), GFP_KERNEL); + + /*1 Set Data Ready PWD*/ + while (retry < 300) { + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x00; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = Data_PWD1; + tmp_data[0] = Data_PWD0; + g_core_fp.fp_flash_write_burst_lenth(tmp_addr, tmp_data, 4); + + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + if ((tmp_data[0] == Data_PWD0 && tmp_data[1] == Data_PWD1) || + (tmp_data[0] == Data_PWD1 && tmp_data[1] == Data_PWD0)) { + break; + } + + retry++; + msleep(1); + } + + if (retry >= 200) { + kfree(tmp_rawdata); + return 1; + } else { + retry = 0; + } + + while (retry < 200) { + if (tmp_data[0] == Data_PWD1 && tmp_data[1] == Data_PWD0) { + break; + } + + retry++; + msleep(1); + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + } + + if (retry >= 200) { + kfree(tmp_rawdata); + return 1; + } else { + retry = 0; + } + + /*2 Read Data from SRAM*/ + while (retry < 10) { + checksum_cal = 0; + total_size_temp = total_size; + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x00; + + if (total_size % max_i2c_size == 0) { + total_read_times = total_size / max_i2c_size; + } else { + total_read_times = total_size / max_i2c_size + 1; + } + + for (i = 0 ; i < (total_read_times) ; i++) { + if (total_size_temp >= max_i2c_size) { + g_core_fp.fp_register_read(tmp_addr, max_i2c_size, &tmp_rawdata[i*max_i2c_size], false); + total_size_temp = total_size_temp - max_i2c_size; + } else { + /*I("last total_size_temp=%d\n", total_size_temp);*/ + g_core_fp.fp_register_read(tmp_addr, total_size_temp % max_i2c_size, &tmp_rawdata[i*max_i2c_size], false); + } + + address = ((i+1)*max_i2c_size); + tmp_addr[1] = (uint8_t)((address>>8)&0x00FF); + tmp_addr[0] = (uint8_t)((address)&0x00FF); + } + + /*3 Check Checksum*/ + for (i = 2 ; i < datalen * 2 + 4 ; i = i + 2) { + checksum_cal += tmp_rawdata[i + 1] * 256 + tmp_rawdata[i]; + } + + if (checksum_cal == 0) { + break; + } + + retry++; + } + + if (checksum_cal != 0) { + E("%s: Get rawdata checksum fail!\n", __func__); + kfree(tmp_rawdata); + return HX_CHKSUM_FAIL; + } + + /*4 Copy Data*/ + for (i = 0 ; i < ic_data->HX_TX_NUM * ic_data->HX_RX_NUM ; i++) { + RAW[i] = tmp_rawdata[(i * 2) + 1 + 4] * 256 + tmp_rawdata[(i * 2) + 4]; + } + +#if 1 + for (j = 0; j < ic_data->HX_RX_NUM; j++) { + if (j == 0) { + printk(" RX%2d", j + 1); + } else { + printk(" RX%2d", j + 1); + } + } + printk("\n"); + + for (i = 0 ; i < ic_data->HX_TX_NUM ; i++) { + printk("TX%2d", i + 1); + for (j = 0 ; j < ic_data->HX_RX_NUM ; j++) { + printk("%5d ", RAW[index]); + if (RAW[index] > Max_DATA) { + Max_DATA = RAW[index]; + } + if (RAW[index] < Min_DATA) { + Min_DATA = RAW[index]; + } + index++; + } + printk("\n"); + } + I("Max = %5d, Min = %5d \n", Max_DATA, Min_DATA); +#endif + + kfree(tmp_rawdata); + return HX_INSPECT_OK; +} + +static void himax_switch_data_type(uint8_t checktype) +{ + uint8_t datatype = 0x00; + + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + datatype = DATA_SORTING; + break; + case HIMAX_INSPECTION_OPEN: + datatype = DATA_OPEN; + break; + case HIMAX_INSPECTION_MICRO_OPEN: + datatype = DATA_MICRO_OPEN; + break; + case HIMAX_INSPECTION_SHORT: + datatype = DATA_SHORT; + break; + case HIMAX_INSPECTION_RAWDATA: + datatype = DATA_RAWDATA; + break; + case HIMAX_INSPECTION_NOISE: + datatype = DATA_NOISE; + break; + case HIMAX_INSPECTION_BACK_NORMAL: + datatype = DATA_BACK_NORMAL; + break; +#ifdef HX_GAP_TEST + case HIMAX_INSPECTION_GAPTEST_RAW: + datatype = DATA_RAWDATA; + break; +#endif +#ifdef HX_ACT_IDLE_TEST + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + datatype = DATA_ACT_IDLE_RAWDATA; + break; + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + datatype = DATA_ACT_IDLE_NOISE; + break; +#endif +#ifdef HX_INSPECT_LPWUG_TEST + case HIMAX_INSPECTION_LPWUG_RAWDATA: + datatype = DATA_LPWUG_RAWDATA; + break; + case HIMAX_INSPECTION_LPWUG_NOISE: + datatype = DATA_LPWUG_NOISE; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + datatype = DATA_LPWUG_IDLE_RAWDATA; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + datatype = DATA_LPWUG_IDLE_NOISE; + break; +#endif + default: + E("Wrong type=%d\n", checktype); + break; + } + g_core_fp.fp_diag_register_set(datatype, 0x00); +} + +static void himax_set_N_frame(uint16_t Nframe, uint8_t checktype) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + + /*IIR MAX*/ + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; tmp_addr[1] = 0x72; tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = (uint8_t)((Nframe & 0xFF00) >> 8); + tmp_data[0] = (uint8_t)(Nframe & 0x00FF); + g_core_fp.fp_flash_write_burst_lenth(tmp_addr, tmp_data, 4); + + /*skip frame*/ + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; tmp_addr[1] = 0x70; tmp_addr[0] = 0xF4; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + + switch (checktype) { +#ifdef HX_ACT_IDLE_TEST + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + tmp_data[0] = BS_ACT_IDLE; + break; +#endif +#ifdef HX_INSPECT_LPWUG_TEST + case HIMAX_INSPECTION_LPWUG_RAWDATA: + case HIMAX_INSPECTION_LPWUG_NOISE: + tmp_data[0] = BS_LPWUG; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + tmp_data[0] = BS_LPWUG_dile; + break; +#endif + case HIMAX_INSPECTION_RAWDATA: + case HIMAX_INSPECTION_NOISE: + tmp_data[0] = BS_RAWDATANOISE; + break; + default: + tmp_data[0] = BS_OPENSHORT; + break; + } + g_core_fp.fp_flash_write_burst_lenth(tmp_addr, tmp_data, 4); +} + +static void himax_get_noise_base(void) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; tmp_addr[1] = 0x70; tmp_addr[0] = 0x8C; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + + g_inspection_criteria[IDX_NOISEMAX][0] = tmp_data[3]; + I("%s: g_inspection_criteria[IDX_NOISEMAX]=%d\n", __func__, g_inspection_criteria[IDX_NOISEMAX][0]); +} + +static uint32_t himax_check_mode(uint8_t checktype) +{ + uint8_t tmp_data[4] = {0}; + uint8_t wait_pwd[2] = {0}; + + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + wait_pwd[0] = PWD_SORTING_END; + wait_pwd[1] = PWD_SORTING_END; + break; + case HIMAX_INSPECTION_OPEN: + wait_pwd[0] = PWD_OPEN_END; + wait_pwd[1] = PWD_OPEN_END; + break; + case HIMAX_INSPECTION_MICRO_OPEN: + wait_pwd[0] = PWD_OPEN_END; + wait_pwd[1] = PWD_OPEN_END; + break; + case HIMAX_INSPECTION_SHORT: + wait_pwd[0] = PWD_SHORT_END; + wait_pwd[1] = PWD_SHORT_END; + break; + case HIMAX_INSPECTION_RAWDATA: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + case HIMAX_INSPECTION_NOISE: + wait_pwd[0] = PWD_NOISE_END; + wait_pwd[1] = PWD_NOISE_END; + break; +#ifdef HX_ACT_IDLE + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + wait_pwd[0] = PWD_ACT_IDLE_END; + wait_pwd[1] = PWD_ACT_IDLE_END; + break; +#endif +#ifdef HX_INSPECT_LPWUG_TEST + case HIMAX_INSPECTION_LPWUG_RAWDATA: + case HIMAX_INSPECTION_LPWUG_NOISE: + wait_pwd[0] = PWD_LPWUG_END; + wait_pwd[1] = PWD_LPWUG_END; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + wait_pwd[0] = PWD_LPWUG_IDLE_END; + wait_pwd[1] = PWD_LPWUG_IDLE_END; + break; +#endif + default: + E("Wrong type=%d\n", checktype); + break; + } + + if (g_core_fp.fp_check_sorting_mode != NULL) + g_core_fp.fp_check_sorting_mode(tmp_data); + + if ((wait_pwd[0] == tmp_data[0]) && (wait_pwd[1] == tmp_data[1])) { + I("Change to mode=%s\n", g_himax_inspection_mode[checktype]); + return 0; + } else { + return 1; + } +} + +static uint32_t himax_wait_sorting_mode(uint8_t checktype) +{ + uint8_t tmp_addr[4] = {0}; + uint8_t tmp_data[4] = {0}; + uint8_t wait_pwd[2] = {0}; + int count = 0; + + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + wait_pwd[0] = PWD_SORTING_END; + wait_pwd[1] = PWD_SORTING_END; + break; + case HIMAX_INSPECTION_OPEN: + wait_pwd[0] = PWD_OPEN_END; + wait_pwd[1] = PWD_OPEN_END; + break; + case HIMAX_INSPECTION_MICRO_OPEN: + wait_pwd[0] = PWD_OPEN_END; + wait_pwd[1] = PWD_OPEN_END; + break; + case HIMAX_INSPECTION_SHORT: + wait_pwd[0] = PWD_SHORT_END; + wait_pwd[1] = PWD_SHORT_END; + break; + case HIMAX_INSPECTION_RAWDATA: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + case HIMAX_INSPECTION_NOISE: + wait_pwd[0] = PWD_NOISE_END; + wait_pwd[1] = PWD_NOISE_END; + break; +#ifdef HX_GAP_TEST + case HIMAX_INSPECTION_GAPTEST_RAW: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; +#endif +#ifdef HX_ACT_IDLE_TEST + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + wait_pwd[0] = PWD_ACT_IDLE_END; + wait_pwd[1] = PWD_ACT_IDLE_END; + break; +#endif +#ifdef HX_INSPECT_LPWUG_TEST + case HIMAX_INSPECTION_LPWUG_RAWDATA: + case HIMAX_INSPECTION_LPWUG_NOISE: + wait_pwd[0] = PWD_LPWUG_END; + wait_pwd[1] = PWD_LPWUG_END; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + wait_pwd[0] = PWD_LPWUG_IDLE_END; + wait_pwd[1] = PWD_LPWUG_IDLE_END; + break; +#endif + default: + I("No Change Mode and now type=%d\n", checktype); + break; + } + + do { + if (g_core_fp.fp_check_sorting_mode != NULL) + g_core_fp.fp_check_sorting_mode(tmp_data); + if ((wait_pwd[0] == tmp_data[0]) && (wait_pwd[1] == tmp_data[1])) { + return 0; + } + + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + I("%s: 0x900000A8, tmp_data[0]=%x,tmp_data[1]=%x,tmp_data[2]=%x,tmp_data[3]=%x \n", __func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xE4; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + I("%s: 0x900000E4, tmp_data[0]=%x,tmp_data[1]=%x,tmp_data[2]=%x,tmp_data[3]=%x \n", __func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; tmp_addr[1] = 0x7F; tmp_addr[0] = 0x40; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + I("%s: 0x10007F40,tmp_data[0]=%x,tmp_data[1]=%x,tmp_data[2]=%x,tmp_data[3]=%x \n", __func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + I("Now retry %d times!\n", count++); + msleep(50); + } while (count < 50); + + return 1; +} +#if 0 +static int himax_check_notch(int index) +{ + if (SKIP_NOTCH_START < 0 && SKIP_NOTCH_END < 0 && SKIP_DUMMY_START < 0 && SKIP_DUMMY_START < 0) { + /* no support notch */ + return 0; + } + if ((index >= SKIP_NOTCH_START) && (index <= SKIP_NOTCH_END)) + return 1; + else if ((index >= SKIP_DUMMY_START) && (index <= SKIP_DUMMY_END)) + return 1; + else + return 0; +} +#endif +static uint32_t mpTestFunc(uint8_t checktype, uint32_t datalen) +{ + uint32_t i/*, j*/, ret = 0; + uint32_t RAW[datalen]; + char *rslt_log; + char *start_log; + int ret_val; + + /*uint16_t* pInspectGridData = &gInspectGridData[0];*/ + /*uint16_t* pInspectNoiseData = &gInspectNoiseData[0];*/ + I("Now Check type = %d\n", checktype); + + if (himax_check_mode(checktype)) { + I("Need Change Mode ,target=%s\n", g_himax_inspection_mode[checktype]); + + g_core_fp.fp_sense_off(); + +#ifndef HX_ZERO_FLASH + if (g_core_fp.fp_reload_disable != NULL) + g_core_fp.fp_reload_disable(1); +#endif + + himax_switch_mode_inspection(checktype); + + if (checktype == HIMAX_INSPECTION_NOISE) { + himax_set_N_frame(NOISEFRAME, checktype); + himax_get_noise_base(); +#ifdef HX_ACT_IDLE_TEST + } else if (checktype == HIMAX_INSPECTION_ACT_IDLE_RAWDATA || checktype == HIMAX_INSPECTION_ACT_IDLE_NOISE) { + I("N frame = %d\n", 10); + himax_set_N_frame(10, checktype); +#endif +#ifdef HX_INSPECT_LPWUG_TEST + } else if (checktype >= HIMAX_INSPECTION_LPWUG_RAWDATA) { + I("N frame = %d\n", 1); + himax_set_N_frame(1, checktype); +#endif + } else { + himax_set_N_frame(2, checktype); + } + + g_core_fp.fp_sense_on(1); + + ret = himax_wait_sorting_mode(checktype); + if (ret) { + E("%s: himax_wait_sorting_mode FAIL\n", __func__); + return ret; + } + } + + himax_switch_data_type(checktype); + + ret = himax_get_rawdata(RAW, datalen); + if (ret) { + E("%s: himax_get_rawdata FAIL\n", __func__); + return ret; + } + + /* back to normal */ + himax_switch_data_type(HIMAX_INSPECTION_BACK_NORMAL); + + I("%s: Init OK, start to test!\n", __func__); + rslt_log = kzalloc(256 * sizeof(char), GFP_KERNEL); + start_log = kzalloc(256 * sizeof(char), GFP_KERNEL); + + snprintf(start_log, 256 * sizeof(char), "\n%s%s\n", g_himax_inspection_mode[checktype], ": data as follow!\n"); + + /*Check Data*/ + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + for (i = 0 ; i < (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) ; i++) { + /*if (himax_check_notch(i)) { + continue; + }*/ + if ((int)RAW[i] < g_inspection_criteria[IDX_SORTMIN][i]) { + E("%s: sorting mode open test FAIL\n", __func__); + ret_val = HX_INSPECT_EOPEN; + goto FAIL_END; + } + } + I("%s: sorting mode open test PASS\n", __func__); + break; + case HIMAX_INSPECTION_OPEN: + for (i = 0 ; i < (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) ; i++) { + /*if (himax_check_notch(i)) { + continue; + }*/ + if ((int)RAW[i] > g_inspection_criteria[IDX_OPENMAX][i] || (int)RAW[i] < g_inspection_criteria[IDX_OPENMIN][i]) { + E("%s: open test FAIL\n", __func__); + ret_val = HX_INSPECT_EOPEN; + goto FAIL_END; + } + } + I("%s: open test PASS\n", __func__); + break; + case HIMAX_INSPECTION_MICRO_OPEN: + for (i = 0 ; i < (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) ; i++) { + /*if (himax_check_notch(i)) { + continue; + }*/ + if ((int)RAW[i] > g_inspection_criteria[IDX_M_OPENMAX][i] || (int)RAW[i] < g_inspection_criteria[IDX_M_OPENMIN][i]) { + E("%s: open test FAIL\n", __func__); + ret_val = HX_INSPECT_EMOPEN; + goto FAIL_END; + } + } + I("%s: open test PASS\n", __func__); + break; + case HIMAX_INSPECTION_SHORT: + for (i = 0 ; i < (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) ; i++) { + /*if (himax_check_notch(i)) { + continue; + }*/ + if ((int)RAW[i] > g_inspection_criteria[IDX_SHORTMAX][i] || (int)RAW[i] < g_inspection_criteria[IDX_SHORTMIN][i]) { + E("%s: short test FAIL\n", __func__); + ret_val = HX_INSPECT_ESHORT; + goto FAIL_END; + } + } + I("%s: short test PASS\n", __func__); + break; + case HIMAX_INSPECTION_RAWDATA: + for (i = 0 ; i < (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) ; i++) { + /* I("Now new compare, datalen=%d!\n",ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); */ + /*if (himax_check_notch(i)) { + continue; + }*/ + if ((int)RAW[i] > g_inspection_criteria[IDX_RAWMAX][i] || (int)RAW[i] < g_inspection_criteria[IDX_RAWMIN][i]) { + E("%s: rawdata test FAIL:RAW[%d]=%d\n", __func__, i, RAW[i]); + I("%s: Now Criteria max=%d,min=%d\n", __func__, g_inspection_criteria[IDX_RAWMAX][i], g_inspection_criteria[IDX_RAWMIN][i]); + ret_val = HX_INSPECT_ERAW; + goto FAIL_END; + } + } + I("%s: rawdata test PASS\n", __func__); + break; + case HIMAX_INSPECTION_NOISE: + I("NOISEMAX=%d\n", NOISEMAX); + for (i = 0 ; i < (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) ; i++) { + /*if (himax_check_notch(i)) { + continue; + }*/ + if ((int)RAW[i] > g_inspection_criteria[IDX_NOISEMAX][i]) { + I("g_inspection_criteria[IDX_NOISEMAX][%d]=%d\n", i, g_inspection_criteria[IDX_NOISEMAX][i]); + I("RAW[%d]=%d\n", i, RAW[i]); + E("%s: noise test FAIL\n", __func__); + ret_val = HX_INSPECT_ENOISE; + goto FAIL_END; + } + } + I("%s: noise test PASS\n", __func__); + break; +#ifdef HX_GAP_TEST + case HIMAX_INSPECTION_GAPTEST_RAW: + if (himax_gap_test_vertical_raw(HIMAX_INSPECTION_GAPTEST_RAW, RAW) != NO_ERR) { + E("%s: HIMAX_INSPECTION_GAPTEST_RAW FAIL\n", __func__); + ret_val = HX_INSPECT_EGAP_RAW; + goto FAIL_END; + } + if (himax_gap_test_honrizontal_raw(HIMAX_INSPECTION_GAPTEST_RAW, RAW) != NO_ERR) { + E("%s: HIMAX_INSPECTION_GAPTEST_RAW FAIL\n", __func__); + ret_val = HX_INSPECT_EGAP_RAW; + goto FAIL_END; + } + break; +#endif +#ifdef HX_ACT_IDLE_TEST + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + for (i = 0 ; i < (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) ; i++) { + /*if (himax_check_notch(i)) { + continue; + }*/ + if ((int)RAW[i] > g_inspection_criteria[IDX_ACT_IDLE_RAWDATA_MAX][i] || (int)RAW[i] < g_inspection_criteria[IDX_ACT_IDLE_RAWDATA_MIN][i]) { + E("%s: HIMAX_INSPECTION_ACT_IDLE_RAWDATA FAIL\n", __func__); + ret_val = HX_INSPECT_EACT_IDLE_RAW; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_ACT_IDLE_RAWDATA PASS\n", __func__); + break; + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + for (i = 0 ; i < (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) ; i++) { + /*if (himax_check_notch(i)) { + continue; + }*/ + if ((int)RAW[i] > g_inspection_criteria[IDX_ACT_IDLE_NOISE_MAX][i] || (int)RAW[i] < g_inspection_criteria[IDX_ACT_IDLE_NOISE_MIN][i]) { + E("%s: HIMAX_INSPECTION_ACT_IDLE_NOISE FAIL\n", __func__); + ret_val = HX_INSPECT_EACT_IDLE_NOISE; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_ACT_IDLE_NOISE PASS\n", __func__); + break; +#endif +#ifdef HX_INSPECT_LPWUG_TEST + case HIMAX_INSPECTION_LPWUG_RAWDATA: + for (i = 0 ; i < (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) ; i++) { + /*if (himax_check_notch(i)) { + continue; + }*/ + if ((int)RAW[i] > g_inspection_criteria[IDX_LPWUG_RAWDATA_MAX][i] || (int)RAW[i] < g_inspection_criteria[IDX_LPWUG_RAWDATA_MIN][i]) { + E("%s: HIMAX_INSPECTION_LPWUG_RAWDATA FAIL\n", __func__); + ret_val = HX_INSPECT_ELPWUG_RAW; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_LPWUG_RAWDATA PASS\n", __func__); + break; + case HIMAX_INSPECTION_LPWUG_NOISE: + for (i = 0 ; i < (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) ; i++) { + /*if (himax_check_notch(i)) { + continue; + }*/ + if ((int)RAW[i] > g_inspection_criteria[IDX_LPWUG_NOISE_MAX][i] || (int)RAW[i] < g_inspection_criteria[IDX_LPWUG_NOISE_MIN][i]) { + E("%s: HIMAX_INSPECTION_LPWUG_NOISE FAIL\n", __func__); + ret_val = HX_INSPECT_ELPWUG_NOISE; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_LPWUG_NOISE PASS\n", __func__); + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + /*if (himax_check_notch(i)) { + continue; + }*/ + if ((int)RAW[i] > g_inspection_criteria[IDX_LPWUG_IDLE_RAWDATA_MAX][i] || (int)RAW[i] < g_inspection_criteria[IDX_LPWUG_IDLE_RAWDATA_MIN][i]) { + E("%s: HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA FAIL\n", __func__); + ret_val = HX_INSPECT_ELPWUG_IDLE_RAW; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA PASS\n", __func__); + break; + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + for (i = 0 ; i < (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) ; i++) { + /*if (himax_check_notch(i)) { + continue; + }*/ + if ((int)RAW[i] > g_inspection_criteria[IDX_LPWUG_IDLE_NOISE_MAX][i] || (int)RAW[i] < g_inspection_criteria[IDX_LPWUG_IDLE_NOISE_MIN][i]) { + E("%s: HIMAX_INSPECTION_LPWUG_IDLE_NOISE FAIL\n", __func__); + ret_val = HX_INSPECT_ELPWUG_IDLE_NOISE; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_LPWUG_IDLE_NOISE PASS\n", __func__); + break; +#endif + default: + E("Wrong type=%d\n", checktype); + break; + } + + ret_val = HX_INSPECT_OK; + snprintf(rslt_log, 256 * sizeof(char), "\n%s%s\n", g_himax_inspection_mode[checktype], " Test Pass!\n"); + I("pass write log\n"); + goto END_FUNC; + +FAIL_END: + snprintf(rslt_log, 256 * sizeof(char), "\n%s%s\n", g_himax_inspection_mode[checktype], " Test Fail!\n"); + I("fail write log\n"); +END_FUNC: + hx_test_data_get(RAW, start_log, rslt_log, checktype); + kfree(rslt_log); + return ret_val; +} + +/* parsing Criteria start */ +static int himax_get_criteria_size(void) +{ + int result = 0; + + result = HX_CRITERIA_SIZE; + + return result; +} + +/* claculate 10's power function */ +static int himax_power_cal(int pow, int number) +{ + int i = 0; + int result = 1; + + for (i = 0 ; i < pow ; i++) + result *= 10; + result = result * number; + + return result; + +} + +/* String to int */ +static int hiamx_parse_str2int(char *str) +{ + int i = 0; + int temp_cal = 0; + int result = 0; + int str_len = strlen(str); + int negtive_flag = 0; + for (i = 0; i < strlen(str) ; i++) { + if (str[i] == '-') { + negtive_flag = 1; + continue; + } + temp_cal = str[i] - '0'; + result += himax_power_cal(str_len-i-1, temp_cal); /* str's the lowest char is the number's the highest number + So we should reverse this number before using the power function + -1: starting number is from 0 ex:10^0 = 1,10^1=10*/ + } + + if (negtive_flag == 1) + result = 0 - result; + + return result; +} + +#if 0 +static int himax_count_comma(const struct firmware *file_entry) +{ + int i = 0; + int result = 0; + for (i = 0; i < file_entry->size; i++) { + if (file_entry->data[i] == ASCII_COMMA) + result++; + } + return result; +} +#endif + +/* Get sub-string from original string by using some charaters */ +static int himax_saperate_comma(const struct firmware *file_entry, char **result, int str_size) +{ + int count = 0; + int str_count = 0; /* now string*/ + int char_count = 0; /* now char count in string*/ + + do { + switch (file_entry->data[count]) { + case ASCII_COMMA: + case ACSII_SPACE: + case ASCII_CR: + case ASCII_LF: + count++; + /* If end of line as above condifiton, differencing the count of char. + If char_count != 0 it's meaning this string is parsing over . + The Next char is belong to next string */ + if (char_count != 0) { + char_count = 0; + str_count++; + } + break; + default: + result[str_count][char_count++] = file_entry->data[count]; + count++; + break; + } + } while (count < file_entry->size && str_count < str_size); + + return 0; +} + +static int hx_diff_str(char *str1, char *str2) +{ + int i = 0; + int err = -1; + int result = 0; /* zero is all same, non-zero is not same index*/ + int str1_len = strlen(str1); + int str2_len = strlen(str2); + if (str1_len != str2_len) { + I("%s:Size different!\n", __func__); + return err; + } + + for (i = 0 ; i < str1_len ; i++) { + if (str1[i] != str2[i]) { + result = i + 1; + I("%s: different in %d!\n", __func__ , result); + return result; + } + } + + return result; +} + +static int hx_get_crtra_by_name(char **result) +{ + int i = 0; + /* count of criteria type */ + int count_type = 0; + /* count of criteria data */ + int count_data = 0; + int err = HX_INSPECT_OK; + int crtra_count = himax_get_criteria_size(); + int all_mut_len = ic_data->HX_TX_NUM * ic_data->HX_RX_NUM; + /* get criteria and assign to a global array(2-Dimensional/int) */ + for (i = 0 ; i < (crtra_count * (all_mut_len) + crtra_count) ; i++) { + /* It have get one page(all mutual) criteria data! + And we should skip the string of criteria name! + */ + if (i == 0 || i == ((i / (all_mut_len)) + (i / (all_mut_len) * (all_mut_len)))) { + count_data = 0; + /* change to next type */ + if (i != 0) + count_type++; + if (hx_diff_str(g_hx_inspt_crtra_name[count_type], result[i]) != 0) { + E("%s:Name Not match!\n", __func__); + E("can recognize[%d]=%s\n", count_type, g_hx_inspt_crtra_name[count_type]); + E("get from file[%d]=%s\n", i, result[i]); + E("Please check criteria file again!\n"); + err = HX_INSPECT_EFILE; + return err; + } + continue; + } + /* change string to int*/ + g_inspection_criteria[count_type][count_data] = hiamx_parse_str2int(result[i]); + /* dbg + I("[%d]g_inspection_criteria[%d][%d]=%d\n", i, count_type, count_data, g_inspection_criteria[count_type][count_data]); + */ + count_data++; + + } + + return err; +} + +static int himax_parse_criteria_file(void) +{ + int err = HX_INSPECT_OK; + const struct firmware *file_entry = NULL; + char **result; + int i = 0; + + int crtra_count = himax_get_criteria_size(); + int data_size = 0; /* The maximum of number Data*/ + int all_mut_len = ic_data->HX_TX_NUM * ic_data->HX_RX_NUM; + int str_max_len = 128; + int result_all_len = 0; + int file_size = 0; + + I("%s,Entering \n", __func__); + if (hx_self_test_file_name == NULL) { + E("file name is NULL\n"); + hx_self_test_file_name = kzalloc(80, GFP_KERNEL); + snprintf(hx_self_test_file_name, 16, "hx_criteria.csv"); + I("%s: Use default name\n", __func__); + } + + I("file name = %s\n", hx_self_test_file_name); + + /* default path is /system/etc/firmware */ + err = request_firmware(&file_entry, hx_self_test_file_name, private_ts->dev); + if (err < 0) { + E("%s,fail in line%d error code=%d\n", __func__, __LINE__, err); + err = HX_INSPECT_EFILE; + goto END_FUNC_REQ_FAIL; + } + + /* size of criteria include name string */ + data_size = ((all_mut_len) * crtra_count) + crtra_count; + + /* init the array which store original criteria and include name string*/ + result = kzalloc(data_size * sizeof(char *), GFP_KERNEL); + for (i = 0 ; i < data_size ; i++) + result[i] = kzalloc(str_max_len * sizeof(char), GFP_KERNEL); + + result_all_len = data_size; + file_size = file_entry->size; + I("Now result_all_len=%d\n", result_all_len); + I("Now file_size=%d\n", file_size); + + /* dbg */ + I("first 4 bytes 0x%2X,0x%2X,0x%2X,0x%2X !\n", file_entry->data[0], file_entry->data[1], file_entry->data[2], file_entry->data[3]); + + /* parse value in to result array(1-Dimensional/String) */ + himax_saperate_comma(file_entry, result, data_size); + + err = hx_get_crtra_by_name(result); + if (err != HX_INSPECT_OK) { + E("%s:Load criteria from file fail, go end!\n", __func__); + goto END_FUNC; + } + + /* for dbg + for (i = 0; i < (((ic_data->HX_TX_NUM*ic_data->HX_RX_NUM)*6)+6); i++) + { + if (i%32 ==0 && i >0) + I("\n"); + I("[%d]%s", i, result[i]); + }*/ +END_FUNC: + for (i = 0 ; i < data_size ; i++) + kfree(result[i]); + kfree(result); + release_firmware(file_entry); +END_FUNC_REQ_FAIL: + I("%s,END \n", __func__); + return err; +} +/* parsing Criteria end */ + +int himax_self_test_data_init(void) +{ + int ret = HX_INSPECT_OK; + int i = 0; + + g_inspection_criteria = kzalloc(sizeof(int *) * HX_CRITERIA_SIZE, GFP_KERNEL); + for (i = 0 ; i < HX_CRITERIA_SIZE ; i++) { + g_inspection_criteria[i] = kzalloc(sizeof(int) * (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM), GFP_KERNEL); + } + ret = himax_parse_criteria_file(); + + g_file_path = kzalloc(256 * sizeof(char), GFP_KERNEL); + /*memset(g_rslt_data, '\0', 0x1000 * HX_CRITERIA_ITEM * sizeof(char));*/ + snprintf(g_file_path, 256 * sizeof(char), "%s%s", HX_RSLT_OUT_PATH, HX_RSLT_OUT_FILE); + + return ret; +} + +static void himax_self_test_data_deinit(void) +{ + int i = 0; + + /*dbg*//* + for (i = 0; i < HX_CRITERIA_ITEM; i++) + I("%s:[%d]%d\n", __func__, i, g_inspection_criteria[i]);*/ + if (g_inspection_criteria != NULL) { + for (i = 0 ; i < HX_CRITERIA_SIZE ; i++) { + kfree(g_inspection_criteria[i]); + } + kfree(g_inspection_criteria); + I("Now it have free the g_inspection_criteria!\n"); + } else { + I("No Need to free g_inspection_criteria!\n"); + } + kfree(g_file_path); +} + +int himax_chip_self_test(void) +{ + uint32_t ret = HX_INSPECT_OK; + I("%s:IN\n", __func__); + + ret = himax_self_test_data_init(); + if (ret != HX_INSPECT_OK) { + E("himax_self_test_data_init fail!\n"); + goto END_FUNC; + } + + /*1. Open Test*/ + I("[MP_OPEN_TEST_RAW]\n"); + ret += mpTestFunc(HIMAX_INSPECTION_OPEN, (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM); + I("1. Open Test: End %d\n\n\n", ret); + + /*2. Micro-Open Test*/ + I("[MP_MICRO_OPEN_TEST_RAW]\n"); + ret += mpTestFunc(HIMAX_INSPECTION_MICRO_OPEN, (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM); + I("2. Micro Open Test: End %d\n\n\n", ret); + + /*3. Short Test*/ + I("[MP_SHORT_TEST_RAW]\n"); + ret += mpTestFunc(HIMAX_INSPECTION_SHORT, (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM); + I("3. Short Test: End %d\n\n\n", ret); + + /*4. RawData Test*/ + I("==========================================\n"); + I("[MP_RAW_TEST_RAW]\n"); + ret += mpTestFunc(HIMAX_INSPECTION_RAWDATA, (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM); + I("%d. %s: End %d\n\n\n", HIMAX_INSPECTION_RAWDATA, g_himax_inspection_mode[HIMAX_INSPECTION_RAWDATA], ret); + + /*5. Noise Test*/ + I("[MP_NOISE_TEST_RAW]\n"); + ret += mpTestFunc(HIMAX_INSPECTION_NOISE, (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM); + I("5. Noise Test: End %d\n\n\n", ret); + +#ifdef HX_GAP_TEST + /*6. GAP Test*/ + I("[MP_GAP_TEST_RAW]\n"); + ret += mpTestFunc(HIMAX_INSPECTION_GAPTEST_RAW, (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM); + I("6. MP_GAP_TEST_RAW: End %d\n\n\n", ret); +#endif + +#ifdef HX_ACT_IDLE_TEST + /*7. ACT_IDLE RAWDATA*/ + I("[MP_ACT_IDLE_TEST_RAW]\n"); + ret += mpTestFunc(HIMAX_INSPECTION_ACT_IDLE_RAWDATA, (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM); + I("7. MP_ACT_IDLE_TEST_RAW: End %d\n\n\n", ret); + + /*8. ACT_IDLE NOISE*/ + I("[MP_ACT_IDLE_TEST_NOISE]\n"); + ret += mpTestFunc(HIMAX_INSPECTION_ACT_IDLE_NOISE, (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM); + I("8. MP_ACT_IDLE_TEST_NOISE: End %d\n\n\n", ret); +#endif +#ifdef HX_INSPECT_LPWUG_TEST + himax_press_powerkey(); + + /*9. LPWUG RAWDATA*/ + I("[MP_LPWUG_TEST_RAW]\n"); + ret += mpTestFunc(HIMAX_INSPECTION_LPWUG_RAWDATA, (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM); + I("9. MP_LPWUG_TEST_RAW: End %d\n\n\n", ret); + + /*10. LPWUG NOISE*/ + I("[MP_LPWUG_TEST_NOISE]\n"); + ret += mpTestFunc(HIMAX_INSPECTION_LPWUG_NOISE, (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM); + I("10. MP_LPWUG_TEST_NOISE: End %d\n\n\n", ret); + + /*11. LPWUG IDLE RAWDATA*/ + I("[MP_LPWUG_IDLE_TEST_RAW]\n"); + ret += mpTestFunc(HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA, (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM); + I("11. MP_LPWUG_IDLE_TEST_RAW: End %d\n\n\n", ret); + + /*12. LPWUG IDLE RAWDATA*/ + I("[MP_LPWUG_IDLE_TEST_NOISE]\n"); + ret += mpTestFunc(HIMAX_INSPECTION_LPWUG_IDLE_NOISE, (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM); + I("12. MP_LPWUG_IDLE_TEST_NOISE: End %d\n\n\n", ret); + + himax_press_powerkey(); +#endif + /*hx_test_data_pop_out(g_rslt_data, g_file_path);*/ + g_core_fp.fp_sense_off(); + himax_set_N_frame(1, HIMAX_INSPECTION_NOISE); +#ifndef HX_ZERO_FLASH + if (g_core_fp.fp_reload_disable != NULL) + g_core_fp.fp_reload_disable(0); +#endif + g_core_fp.fp_sense_on(0); + +END_FUNC: + himax_self_test_data_deinit(); + + + I("thp_afe_inspect_OUT = %d \n", ret); + + if (ret != 0) + ret = 1; + + I("%s:OUT\n", __func__); + return ret; +} + +void himax_inspection_init(void) +{ + I("%s: enter, %d \n", __func__, __LINE__); + HX_CRITERIA_ITEM = 5; +#ifdef HX_GAP_TEST + HX_CRITERIA_ITEM += 1; +#endif +#ifdef HX_INSPECT_LPWUG_TEST + HX_CRITERIA_ITEM += 4; +#endif +#ifdef HX_ACT_IDLE_TEST + HX_CRITERIA_ITEM += 2; +#endif + /* Noise Just one criteria*/ + HX_CRITERIA_SIZE = HX_CRITERIA_ITEM * 2 - 1; + I("There is %d HX_CRITERIA_ITEM and %d HX_CRITERIA_SIZE\n", HX_CRITERIA_ITEM, HX_CRITERIA_SIZE); + + g_rslt_data = kzalloc(0x1000 * HX_CRITERIA_ITEM * sizeof(char), GFP_KERNEL); + + g_core_fp.fp_chip_self_test = himax_chip_self_test; + + return; +} diff --git a/drivers/input/touchscreen/himax_mmi/himax_inspection.h b/drivers/input/touchscreen/himax_mmi/himax_inspection.h new file mode 100644 index 000000000000..d8c318de7d9a --- /dev/null +++ b/drivers/input/touchscreen/himax_mmi/himax_inspection.h @@ -0,0 +1,238 @@ + +#include "himax_platform.h" +#include "himax_common.h" +#include "himax_ic_core.h" + +/*#define HX_GAP_TEST*/ +/*#define HX_INSPECT_LPWUG_TEST*/ +/*#define HX_ACT_IDLE_TEST*/ + +#define HX_RSLT_OUT_PATH "/sdcard/" +#define HX_RSLT_OUT_FILE "hx_test_result.txt" +char *g_file_path; +char *g_rslt_data; +uint32_t g_rslt_data_len = 0; + + +#define BS_RAWDATANOISE 10 +#define BS_OPENSHORT 0 + +#ifdef HX_INSPECT_LPWUG_TEST +#define BS_LPWUG 1 +#define BS_LPWUG_dile 1 +#endif + +#ifdef HX_ACT_IDLE_TEST +#define BS_ACT_IDLE 1 +#endif + +/* skip notch & dummy */ +#define SKIP_NOTCH_START 5 +#define SKIP_NOTCH_END 10 +/* TX+SKIP_NOTCH_START */ +#define SKIP_DUMMY_START 23 +/* TX+SKIP_NOTCH_END*/ +#define SKIP_DUMMY_END 28 + + +#define NOISEFRAME (BS_RAWDATANOISE+1) +#define UNIFMAX 500 + + +/*Himax MP Password*/ +#define PWD_OPEN_START 0x77 +#define PWD_OPEN_END 0x88 +#define PWD_SHORT_START 0x11 +#define PWD_SHORT_END 0x33 +#define PWD_RAWDATA_START 0x00 +#define PWD_RAWDATA_END 0x99 +#define PWD_NOISE_START 0x00 +#define PWD_NOISE_END 0x99 +#define PWD_SORTING_START 0xAA +#define PWD_SORTING_END 0xCC + +#ifdef HX_ACT_IDLE_TEST +#define PWD_ACT_IDLE_START 0x22 +#define PWD_ACT_IDLE_END 0x44 +#endif + +#ifdef HX_INSPECT_LPWUG_TEST +#define PWD_LPWUG_START 0x55 +#define PWD_LPWUG_END 0x66 + +#define PWD_LPWUG_IDLE_START 0x50 +#define PWD_LPWUG_IDLE_END 0x60 +#endif + +/*Himax DataType*/ +#define DATA_SORTING 0x0A +#define DATA_OPEN 0x0B +#define DATA_MICRO_OPEN 0x0C +#define DATA_SHORT 0x0A +#define DATA_RAWDATA 0x0A +#define DATA_NOISE 0x0F +#define DATA_BACK_NORMAL 0x00 +#define DATA_LPWUG_RAWDATA 0x0C +#define DATA_LPWUG_NOISE 0x0F +#define DATA_ACT_IDLE_RAWDATA 0x0A +#define DATA_ACT_IDLE_NOISE 0x0F +#define DATA_LPWUG_IDLE_RAWDATA 0x0A +#define DATA_LPWUG_IDLE_NOISE 0x0F + +/*Himax Data Ready Password*/ +#define Data_PWD0 0xA5 +#define Data_PWD1 0x5A + +/* ASCII format */ +#define ASCII_LF (0x0A) +#define ASCII_CR (0x0D) +#define ASCII_COMMA (0x2C) +#define ASCII_ZERO (0x30) +#define CHAR_EL '\0' +#define CHAR_NL '\n' +#define ACSII_SPACE (0x20) +/* INSOECTION Setting */ +int HX_CRITERIA_ITEM; +int HX_CRITERIA_SIZE; + +typedef enum { + HIMAX_INSPECTION_OPEN, + HIMAX_INSPECTION_MICRO_OPEN, + HIMAX_INSPECTION_SHORT, + HIMAX_INSPECTION_RAWDATA, + HIMAX_INSPECTION_NOISE, + HIMAX_INSPECTION_SORTING, + HIMAX_INSPECTION_BACK_NORMAL, +#ifdef HX_GAP_TEST + HIMAX_INSPECTION_GAPTEST_RAW, +#endif +#ifdef HX_ACT_IDLE_TEST + HIMAX_INSPECTION_ACT_IDLE_RAWDATA, + HIMAX_INSPECTION_ACT_IDLE_NOISE, +#endif +#ifdef HX_INSPECT_LPWUG_TEST + HIMAX_INSPECTION_LPWUG_RAWDATA, + HIMAX_INSPECTION_LPWUG_NOISE, + HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA, + HIMAX_INSPECTION_LPWUG_IDLE_NOISE, +#endif +} THP_INSPECTION_ENUM; + +char *g_himax_inspection_mode[] = { + "HIMAX_INSPECTION_OPEN", + "HIMAX_INSPECTION_MICRO_OPEN", + "HIMAX_INSPECTION_SHORT", + "HIMAX_INSPECTION_RAWDATA", + "HIMAX_INSPECTION_NOISE", + "HIMAX_INSPECTION_SORTING", + "HIMAX_INSPECTION_BACK_NORMAL", +#ifdef HX_GAP_TEST + "HIMAX_INSPECTION_GAPTEST_RAW", +#endif +#ifdef HX_ACT_IDLE_TEST + "HIMAX_INSPECTION_ACT_IDLE_RAWDATA", + "HIMAX_INSPECTION_ACT_IDLE_NOISE", +#endif +#ifdef HX_INSPECT_LPWUG_TEST + "HIMAX_INSPECTION_LPWUG_RAWDATA", + "HIMAX_INSPECTION_LPWUG_NOISE", + "HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA", + "HIMAX_INSPECTION_LPWUG_IDLE_NOISE", +#endif +}; + +/* for criteria */ +int **g_inspection_criteria; +char *g_hx_inspt_crtra_name[] = { + "CRITERIA_RAW_MIN", + "CRITERIA_RAW_MAX", + "CRITERIA_SHORT_MIN", + "CRITERIA_SHORT_MAX", + "CRITERIA_OPEN_MIN", + "CRITERIA_OPEN_MAX", + "CRITERIA_MICRO_OPEN_MIN", + "CRITERIA_MICRO_OPEN_MAX", + "CRITERIA_NOISE", + "CRITERIA_SORT_MIN", + "CRITERIA_SORT_MAX", +#ifdef HX_GAP_TEST + "CRITERIA_GAP_RAW_MIN", + "CRITERIA_GAP_RAW_MAX", +#endif +#ifdef HX_ACT_IDLE_TEST + "ACT_IDLE_NOISE_MIN", + "ACT_IDLE_NOISE_MAX", + "ACT_IDLE_RAWDATA_MIN", + "ACT_IDLE_RAWDATA_MAX", +#endif +#ifdef HX_INSPECT_LPWUG_TEST + "LPWUG_NOISE_MIN", + "LPWUG_NOISE_MAX", + "LPWUG_RAWDATA_MIN", + "LPWUG_RAWDATA_MAX", + "LPWUG_IDLE_NOISE_MIN", + "LPWUG_IDLE_NOISE_MAX", + "LPWUG_IDLE_RAWDATA_MIN", + "LPWUG_IDLE_RAWDATA_MAX" +#endif +}; +typedef enum { + IDX_RAWMIN = 0, + IDX_RAWMAX, + IDX_SHORTMIN, + IDX_SHORTMAX, + IDX_OPENMIN, + IDX_OPENMAX, + IDX_M_OPENMIN, + IDX_M_OPENMAX, + IDX_NOISEMAX, + IDX_SORTMIN, + IDX_SORTMAX, +#ifdef HX_GAP_TEST + IDX_GAP_RAWMAX, + IDX_GAP_RAWMIN, +#endif +#ifdef HX_ACT_IDLE_TEST + IDX_ACT_IDLE_NOISE_MIN, + IDX_ACT_IDLE_NOISE_MAX, + IDX_ACT_IDLE_RAWDATA_MIN, + IDX_ACT_IDLE_RAWDATA_MAX, +#endif +#ifdef HX_INSPECT_LPWUG_TEST + IDX_LPWUG_NOISE_MIN, + IDX_LPWUG_NOISE_MAX, + IDX_LPWUG_RAWDATA_MIN, + IDX_LPWUG_RAWDATA_MAX, + + IDX_LPWUG_IDLE_NOISE_MIN, + IDX_LPWUG_IDLE_NOISE_MAX, + IDX_LPWUG_IDLE_RAWDATA_MIN, + IDX_LPWUG_IDLE_RAWDATA_MAX, +#endif +} HX_CRITERIA_ENUM; + +/* Error code of Inspection */ +typedef enum { + HX_INSPECT_OK = 0, /* OK */ + HX_INSPECT_ESPI = 1 << 1, /* SPI communication error */ + HX_INSPECT_ERAW = 2 << 1, /* Raw data error */ + HX_INSPECT_ENOISE = 3 << 1, /* Noise error */ + HX_INSPECT_EOPEN = 4 << 1, /* Sensor open error */ + HX_INSPECT_EMOPEN = 5 << 1, /* Sensor micro open error */ + HX_INSPECT_ESHORT = 6 << 1, /* Sensor short error */ +#ifdef HX_GAP_TEST + HX_INSPECT_EGAP_RAW = 8 << 1, /* Sensor short error */ +#endif +#ifdef HX_ACT_IDLE_TEST + HX_INSPECT_EACT_IDLE_RAW = 9 << 1, /* ACT_IDLE RAW ERROR */ + HX_INSPECT_EACT_IDLE_NOISE = 10 << 1, /* ACT_IDLE NOISE ERROR */ +#endif +#ifdef HX_INSPECT_LPWUG_TEST + HX_INSPECT_ELPWUG_RAW = 11 << 1, /* LPWUG RAW ERROR */ + HX_INSPECT_ELPWUG_NOISE = 12 << 1, /* LPWUG NOISE ERROR */ + HX_INSPECT_ELPWUG_IDLE_RAW = 13 << 1, /* LPWUG IDLE RAW ERROR */ + HX_INSPECT_ELPWUG_IDLE_NOISE = 14 << 1, /* LPWUG IDLE NOISE ERROR */ +#endif + HX_INSPECT_EFILE = 15 << 1, /*Criteria file error*/ + HX_INSPECT_EOTHER = 16 << 1, /* All other errors */ +} HX_INSPECT_ERR_ENUM; diff --git a/drivers/input/touchscreen/himax_mmi/himax_platform.c b/drivers/input/touchscreen/himax_mmi/himax_platform.c new file mode 100644 index 000000000000..e9fcd5f223b0 --- /dev/null +++ b/drivers/input/touchscreen/himax_mmi/himax_platform.c @@ -0,0 +1,808 @@ +/* Himax Android Driver Sample Code for QCT platform + + Copyright (C) 2018 Himax Corporation. + + 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. + +*/ + +#include "himax_platform.h" +#include "himax_common.h" + +int i2c_error_count = 0; +int irq_enable_count = 0; + +extern struct himax_ic_data *ic_data; +extern struct himax_ts_data *private_ts; +extern void himax_ts_work(struct himax_ts_data *ts); +extern enum hrtimer_restart himax_ts_timer_func(struct hrtimer *timer); + +extern int himax_chip_common_init(void); +extern void himax_chip_common_deinit(void); + +int himax_dev_set(struct himax_ts_data *ts) +{ + int ret = 0; + ts->input_dev = input_allocate_device(); + + if (ts->input_dev == NULL) { + ret = -ENOMEM; + E("%s: Failed to allocate input device\n", __func__); + return ret; + } + + ts->input_dev->name = "himax-touchscreen"; + return ret; +} +int himax_input_register_device(struct input_dev *input_dev) +{ + return input_register_device(input_dev); +} + +#if defined(HX_PLATFOME_DEFINE_KEY) +void himax_platform_key(void) +{ + I("Nothing to be done! Plz cancel it!\n"); +} +#endif + +void himax_vk_parser(struct device_node *dt, + struct himax_i2c_platform_data *pdata) +{ + u32 data = 0; + uint8_t cnt = 0, i = 0; + uint32_t coords[4] = {0}; + struct device_node *node, *pp = NULL; + struct himax_virtual_key *vk; + node = of_parse_phandle(dt, "virtualkey", 0); + + if (node == NULL) { + I(" DT-No vk info in DT\n"); + return; + } else { + while ((pp = of_get_next_child(node, pp))) + cnt++; + + if (!cnt) + return; + + vk = kzalloc(cnt * (sizeof *vk), GFP_KERNEL); + pp = NULL; + + while ((pp = of_get_next_child(node, pp))) { + if (of_property_read_u32(pp, "idx", &data) == 0) + vk[i].index = data; + + if (of_property_read_u32_array(pp, "range", coords, 4) == 0) { + vk[i].x_range_min = coords[0], vk[i].x_range_max = coords[1]; + vk[i].y_range_min = coords[2], vk[i].y_range_max = coords[3]; + } else { + I(" range faile\n"); + } + + i++; + } + + pdata->virtual_key = vk; + + for (i = 0; i < cnt; i++) + I(" vk[%d] idx:%d x_min:%d, y_max:%d\n", i, pdata->virtual_key[i].index, + pdata->virtual_key[i].x_range_min, pdata->virtual_key[i].y_range_max); + } +} + +int himax_parse_dt(struct himax_ts_data *ts, + struct himax_i2c_platform_data *pdata) +{ + int rc, coords_size = 0; + uint32_t coords[4] = {0}; + struct property *prop; + struct device_node *dt = private_ts->client->dev.of_node; + u32 data = 0; + int ic_type_data = 0; + prop = of_find_property(dt, "himax,panel-coords", NULL); + + if (prop) { + coords_size = prop->length / sizeof(u32); + + if (coords_size != 4) + D(" %s:Invalid panel coords size %d\n", __func__, coords_size); + } + + if (of_property_read_u32_array(dt, "himax,panel-coords", coords, coords_size) == 0) { + pdata->abs_x_min = coords[0], pdata->abs_x_max = coords[1]; + pdata->abs_y_min = coords[2], pdata->abs_y_max = coords[3]; + I(" DT-%s:panel-coords = %d, %d, %d, %d\n", __func__, pdata->abs_x_min, + pdata->abs_x_max, pdata->abs_y_min, pdata->abs_y_max); + } + + prop = of_find_property(dt, "himax,display-coords", NULL); + + if (prop) { + coords_size = prop->length / sizeof(u32); + + if (coords_size != 4) + D(" %s:Invalid display coords size %d\n", __func__, coords_size); + } + + rc = of_property_read_u32_array(dt, "himax,display-coords", coords, coords_size); + + if (rc && (rc != -EINVAL)) { + D(" %s:Fail to read display-coords %d\n", __func__, rc); + return rc; + } + + pdata->screenWidth = coords[1]; + pdata->screenHeight = coords[3]; + I(" DT-%s:display-coords = (%d, %d)\n", __func__, pdata->screenWidth, + pdata->screenHeight); + + rc = of_property_read_u32(dt, "himax,ic-type", &ic_type_data); + + if (rc && (rc != -EINVAL)) { + D("[%s][%d]:Fail to read himax,ic-type %d\n", __func__, __LINE__, rc); + ic_data->ic_type_val = 0; + } else { + I("[%s][%d]:Read driver IC type = %d, return %d\n", __func__, __LINE__, ic_type_data, rc); + ic_data->ic_type_val = ic_type_data; + } + + pdata->gpio_irq = of_get_named_gpio(dt, "himax,irq-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_irq)) { + I(" DT:gpio_irq value is not valid\n"); + } + + pdata->gpio_reset = of_get_named_gpio(dt, "himax,rst-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_reset)) { + I(" DT:gpio_rst value is not valid\n"); + } + + pdata->gpio_3v3_en = of_get_named_gpio(dt, "himax,3v3-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_3v3_en)) { + I(" DT:gpio_3v3_en value is not valid\n"); + } + + I(" DT:gpio_irq=%d, gpio_rst=%d, gpio_3v3_en=%d\n", pdata->gpio_irq, pdata->gpio_reset, pdata->gpio_3v3_en); + + if (of_property_read_u32(dt, "report_type", &data) == 0) { + pdata->protocol_type = data; + I(" DT:protocol_type=%d\n", pdata->protocol_type); + } + + himax_vk_parser(dt, pdata); + return 0; +} + +int himax_bus_read(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry) +{ + int retry; + struct i2c_client *client = private_ts->client; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &command, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + } + }; + mutex_lock(&private_ts->rw_lock); + + for (retry = 0; retry < toRetry; retry++) { + if (i2c_transfer(client->adapter, msg, 2) == 2) + break; + + msleep(20); + } + + if (retry == toRetry) { + E("%s: i2c_read_block retry over %d\n", + __func__, toRetry); + i2c_error_count = toRetry; + mutex_unlock(&private_ts->rw_lock); + return -EIO; + } + + mutex_unlock(&private_ts->rw_lock); + return 0; +} + +int himax_bus_write(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry) +{ + int retry/*, loop_i*/; + uint8_t buf[length + 1]; + struct i2c_client *client = private_ts->client; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = length + 1, + .buf = buf, + } + }; + mutex_lock(&private_ts->rw_lock); + buf[0] = command; + memcpy(buf + 1, data, length); + + for (retry = 0; retry < toRetry; retry++) { + if (i2c_transfer(client->adapter, msg, 1) == 1) + break; + + msleep(20); + } + + if (retry == toRetry) { + E("%s: i2c_write_block retry over %d\n", + __func__, toRetry); + i2c_error_count = toRetry; + mutex_unlock(&private_ts->rw_lock); + return -EIO; + } + + mutex_unlock(&private_ts->rw_lock); + return 0; +} + +int himax_bus_write_command(uint8_t command, uint8_t toRetry) +{ + return himax_bus_write(command, NULL, 0, toRetry); +} + +int himax_bus_master_write(uint8_t *data, uint32_t length, uint8_t toRetry) +{ + int retry/*, loop_i*/; + uint8_t buf[length]; + struct i2c_client *client = private_ts->client; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = length, + .buf = buf, + } + }; + mutex_lock(&private_ts->rw_lock); + memcpy(buf, data, length); + + for (retry = 0; retry < toRetry; retry++) { + if (i2c_transfer(client->adapter, msg, 1) == 1) + break; + + msleep(20); + } + + if (retry == toRetry) { + E("%s: i2c_write_block retry over %d\n", + __func__, toRetry); + i2c_error_count = toRetry; + mutex_unlock(&private_ts->rw_lock); + return -EIO; + } + + mutex_unlock(&private_ts->rw_lock); + return 0; +} + +void himax_int_enable(int enable) +{ + int irqnum = 0; + irqnum = private_ts->client->irq; + + if (enable == 1 && irq_enable_count == 0) { + enable_irq(irqnum); + irq_enable_count++; + private_ts->irq_enabled = 1; + } else if (enable == 0 && irq_enable_count == 1) { + disable_irq_nosync(irqnum); + irq_enable_count--; + private_ts->irq_enabled = 0; + } + + I("irq_enable_count = %d\n", irq_enable_count); +} + +#ifdef HX_RST_PIN_FUNC +void himax_rst_gpio_set(int pinnum, uint8_t value) +{ + gpio_direction_output(pinnum, value); +} +#endif + +uint8_t himax_int_gpio_read(int pinnum) +{ + return gpio_get_value(pinnum); +} + +#if defined(CONFIG_HMX_DB) +static int himax_regulator_configure(struct himax_i2c_platform_data *pdata) +{ + int retval; + struct i2c_client *client = private_ts->client; + pdata->vcc_dig = regulator_get(&client->dev, "vdd"); + + if (IS_ERR(pdata->vcc_dig)) { + E("%s: Failed to get regulator vdd\n", + __func__); + retval = PTR_ERR(pdata->vcc_dig); + return retval; + } + + pdata->vcc_ana = regulator_get(&client->dev, "avdd"); + + if (IS_ERR(pdata->vcc_ana)) { + E("%s: Failed to get regulator avdd\n", + __func__); + retval = PTR_ERR(pdata->vcc_ana); + regulator_put(pdata->vcc_ana); + return retval; + } + + return 0; +}; + +static int himax_power_on(struct himax_i2c_platform_data *pdata, bool on) +{ + int retval; + + if (on) { + retval = regulator_enable(pdata->vcc_dig); + + if (retval) { + E("%s: Failed to enable regulator vdd\n", + __func__); + return retval; + } + + msleep(100); + retval = regulator_enable(pdata->vcc_ana); + + if (retval) { + E("%s: Failed to enable regulator avdd\n", + __func__); + regulator_disable(pdata->vcc_dig); + return retval; + } + } else { + regulator_disable(pdata->vcc_dig); + regulator_disable(pdata->vcc_ana); + } + + return 0; +} + +int himax_gpio_power_config(struct himax_i2c_platform_data *pdata) +{ + int error; + struct i2c_client *client = private_ts->client; + error = himax_regulator_configure(pdata); + + if (error) { + E("Failed to intialize hardware\n"); + goto err_regulator_not_on; + } + +#ifdef HX_RST_PIN_FUNC + + if (gpio_is_valid(pdata->gpio_reset)) { + /* configure touchscreen reset out gpio */ + error = gpio_request(pdata->gpio_reset, "hmx_reset_gpio"); + + if (error) { + E("unable to request gpio [%d]\n", + pdata->gpio_reset); + goto err_regulator_on; + } + + error = gpio_direction_output(pdata->gpio_reset, 0); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_req; + } + } + +#endif + error = himax_power_on(pdata, true); + + if (error) { + E("Failed to power on hardware\n"); + goto err_gpio_reset_req; + } + + if (gpio_is_valid(pdata->gpio_irq)) { + /* configure touchscreen irq gpio */ + error = gpio_request(pdata->gpio_irq, "hmx_gpio_irq"); + + if (error) { + E("unable to request gpio [%d]\n", + pdata->gpio_irq); + goto err_power_on; + } + + error = gpio_direction_input(pdata->gpio_irq); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_irq); + goto err_gpio_irq_req; + } + + client->irq = gpio_to_irq(pdata->gpio_irq); + } else { + E("irq gpio not provided\n"); + goto err_power_on; + } + + msleep(20); +#ifdef HX_RST_PIN_FUNC + + if (gpio_is_valid(pdata->gpio_reset)) { + error = gpio_direction_output(pdata->gpio_reset, 1); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_irq_req; + } + } + +#endif + return 0; +err_gpio_irq_req: + + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); + +err_power_on: + himax_power_on(pdata, false); +err_gpio_reset_req: +#ifdef HX_RST_PIN_FUNC + + if (gpio_is_valid(pdata->gpio_reset)) + gpio_free(pdata->gpio_reset); + +err_regulator_on: +#endif +err_regulator_not_on: + return error; +} + +#else +int himax_gpio_power_config(struct himax_i2c_platform_data *pdata) +{ + int error = 0; + struct i2c_client *client = private_ts->client; +#ifdef HX_RST_PIN_FUNC + + if (pdata->gpio_reset >= 0) { + error = gpio_request(pdata->gpio_reset, "himax-reset"); + + if (error < 0) { + E("%s: request reset pin failed\n", __func__); + return error; + } + } + +#endif + + if (pdata->gpio_3v3_en >= 0) { + error = gpio_request(pdata->gpio_3v3_en, "himax-3v3_en"); + + if (error < 0) { + E("%s: request 3v3_en pin failed\n", __func__); + goto err_3v3_en_request; + } + + gpio_direction_output(pdata->gpio_3v3_en, 1); + I("3v3_en pin =%d\n", gpio_get_value(pdata->gpio_3v3_en)); + } + + if (gpio_is_valid(pdata->gpio_irq)) { + /* configure touchscreen irq gpio */ + error = gpio_request(pdata->gpio_irq, "himax_gpio_irq"); + + if (error) { + E("unable to request gpio [%d]\n", pdata->gpio_irq); + goto err_irq_request; + } + + error = gpio_direction_input(pdata->gpio_irq); + + if (error) { + E("unable to set direction for gpio [%d]\n", pdata->gpio_irq); + goto err_irq_set_direction; + } + + client->irq = gpio_to_irq(pdata->gpio_irq); + } else { + E("irq gpio not provided\n"); + goto err_irq_request; + } + +#ifdef HX_RST_PIN_FUNC + + if (pdata->gpio_reset >= 0) { + error = gpio_direction_output(pdata->gpio_reset, 0); + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_rst_set_value; + } + msleep(20); + error = gpio_direction_output(pdata->gpio_reset, 1); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_rst_set_value; + } + } + +#endif + return 0; +#ifdef HX_RST_PIN_FUNC + err_rst_set_value: +#endif + err_irq_set_direction: + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); + err_irq_request: + if (gpio_is_valid(pdata->gpio_3v3_en)) + gpio_free(pdata->gpio_3v3_en); + err_3v3_en_request: + if (gpio_is_valid(pdata->gpio_reset)) + gpio_free(pdata->gpio_reset); + + return error; +} + +#endif + +static void himax_ts_isr_func(struct himax_ts_data *ts) +{ + himax_ts_work(ts); +} + +irqreturn_t himax_ts_thread(int irq, void *ptr) +{ + himax_ts_isr_func((struct himax_ts_data *)ptr); + + return IRQ_HANDLED; +} + +static void himax_ts_work_func(struct work_struct *work) +{ + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work); + himax_ts_work(ts); +} + +int himax_int_register_trigger(void) +{ + int ret = 0; + struct himax_ts_data *ts = private_ts; + struct i2c_client *client = private_ts->client; + + if (ic_data->HX_INT_IS_EDGE) { + I("%s edge triiger falling\n ", __func__); + ret = request_threaded_irq(client->irq, NULL, himax_ts_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, ts); + } + + else { + I("%s level trigger low\n ", __func__); + ret = request_threaded_irq(client->irq, NULL, himax_ts_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, ts); + } + + return ret; +} + +int himax_int_en_set(void) +{ + int ret = NO_ERR; + ret = himax_int_register_trigger(); + return ret; +} + +int himax_ts_register_interrupt(void) +{ + struct himax_ts_data *ts = private_ts; + struct i2c_client *client = private_ts->client; + int ret = 0; + ts->irq_enabled = 0; + + /* Work functon */ + if (client->irq) {/*INT mode*/ + ts->use_irq = 1; + ret = himax_int_register_trigger(); + + if (ret == 0) { + ts->irq_enabled = 1; + irq_enable_count = 1; + I("%s: irq enabled at qpio: %d\n", __func__, client->irq); +#ifdef HX_SMART_WAKEUP + irq_set_irq_wake(client->irq, 1); +#endif + } else { + ts->use_irq = 0; + E("%s: request_irq failed\n", __func__); + } + } else { + I("%s: client->irq is empty, use polling mode.\n", __func__); + } + + if (!ts->use_irq) {/*if use polling mode need to disable HX_ESD_RECOVERY function*/ + ts->himax_wq = create_singlethread_workqueue("himax_touch"); + INIT_WORK(&ts->work, himax_ts_work_func); + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = himax_ts_timer_func; + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + I("%s: polling mode enabled\n", __func__); + } + + return ret; +} + +static int himax_common_suspend(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + I("%s: enter \n", __func__); + himax_chip_common_suspend(ts); + return 0; +} + +static int himax_common_resume(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + I("%s: enter \n", __func__); + himax_chip_common_resume(ts); + return 0; +} + +#if defined(CONFIG_FB) +int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct himax_ts_data *ts = + container_of(self, struct himax_ts_data, fb_notif); + I(" %s\n", __func__); + + if (evdata && evdata->data && ts && ts->client) { + blank = evdata->data; + if (event == FB_EARLY_EVENT_BLANK) { + switch (*blank) { + case FB_BLANK_POWERDOWN: + himax_common_suspend(&ts->client->dev); + break; + } + } else if (event == FB_EVENT_BLANK) { + switch (*blank) { + case FB_BLANK_UNBLANK: + himax_common_resume(&ts->client->dev); + break; + case FB_BLANK_POWERDOWN: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_NORMAL: + himax_common_suspend(&ts->client->dev); + break; + } + } + } + + return 0; +} +#endif + +int himax_chip_common_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + struct himax_ts_data *ts; + + I("%s:Enter \n", __func__); + + /* Check I2C functionality */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + E("%s: i2c check functionality error\n", __func__); + ret = -ENODEV; + goto err_check_functionality_failed; + } + + ts = kzalloc(sizeof(struct himax_ts_data), GFP_KERNEL); + if (ts == NULL) { + E("%s: allocate himax_ts_data failed\n", __func__); + ret = -ENOMEM; + goto err_alloc_data_failed; + } + + i2c_set_clientdata(client, ts); + ts->client = client; + ts->dev = &client->dev; + mutex_init(&ts->rw_lock); + private_ts = ts; + + ret = himax_chip_common_init(); + + err_alloc_data_failed: + err_check_functionality_failed: + + return ret; +} + +int himax_chip_common_remove(struct i2c_client *client) +{ + himax_chip_common_deinit(); + + return 0; +} + +static const struct i2c_device_id himax_common_ts_id[] = { + {HIMAX_common_NAME, 0 }, + {} +}; + +static const struct dev_pm_ops himax_common_pm_ops = { +#if (!defined(CONFIG_FB)) + .suspend = himax_common_suspend, + .resume = himax_common_resume, +#endif +}; + +#ifdef CONFIG_OF +static struct of_device_id himax_match_table[] = { + {.compatible = "himax,hxcommon" }, + {}, +}; +#else +#define himax_match_table NULL +#endif + +static struct i2c_driver himax_common_driver = { + .id_table = himax_common_ts_id, + .probe = himax_chip_common_probe, + .remove = himax_chip_common_remove, + .driver = { + .name = HIMAX_common_NAME, + .owner = THIS_MODULE, + .of_match_table = himax_match_table, +#ifdef CONFIG_PM + .pm = &himax_common_pm_ops, +#endif + }, +}; + +static int __init himax_common_init(void) +{ + I("Himax common touch panel driver init\n"); + i2c_add_driver(&himax_common_driver); + + return 0; +} + +static void __exit himax_common_exit(void) +{ + i2c_del_driver(&himax_common_driver); +} + +module_init(himax_common_init); +module_exit(himax_common_exit); + +MODULE_DESCRIPTION("Himax_common driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/touchscreen/himax_mmi/himax_platform.h b/drivers/input/touchscreen/himax_mmi/himax_platform.h new file mode 100644 index 000000000000..7694a4c167d7 --- /dev/null +++ b/drivers/input/touchscreen/himax_mmi/himax_platform.h @@ -0,0 +1,130 @@ +/* Himax Android Driver Sample Code for QCT platform + + Copyright (C) 2018 Himax Corporation. + + 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. + +*/ + +#ifndef HIMAX_PLATFORM_H +#define HIMAX_PLATFORM_H + +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_HMX_DB) + #include +#endif + +#define HIMAX_I2C_RETRY_TIMES 10 + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG) +#define D(x...) printk("[HXTP] " x) +#define I(x...) printk("[HXTP] " x) +#define W(x...) printk("[HXTP][WARNING] " x) +#define E(x...) printk("[HXTP][ERROR] " x) +#define DIF(x...) \ +do { \ + if (debug_flag) \ + printk("[HXTP][DEBUG] " x) \ + } while (0) +#else +#define D(x...) +#define I(x...) +#define W(x...) +#define E(x...) +#define DIF(x...) +#endif + +#if defined(CONFIG_HMX_DB) + /* Analog voltage @2.7 V */ + #define HX_VTG_MIN_UV 2700000 + #define HX_VTG_MAX_UV 3300000 + #define HX_ACTIVE_LOAD_UA 15000 + #define HX_LPM_LOAD_UA 10 + /* Digital voltage @1.8 V */ + #define HX_VTG_DIG_MIN_UV 1800000 + #define HX_VTG_DIG_MAX_UV 1800000 + #define HX_ACTIVE_LOAD_DIG_UA 10000 + #define HX_LPM_LOAD_DIG_UA 10 + + #define HX_I2C_VTG_MIN_UV 1800000 + #define HX_I2C_VTG_MAX_UV 1800000 + #define HX_I2C_LOAD_UA 10000 + #define HX_I2C_LPM_LOAD_UA 10 +#endif + +#define HIMAX_common_NAME "himax_tp" +#define HIMAX_I2C_ADDR 0x48 +#define INPUT_DEV_NAME "himax-touchscreen" + +struct himax_i2c_platform_data { + int abs_x_min; + int abs_x_max; + int abs_x_fuzz; + int abs_y_min; + int abs_y_max; + int abs_y_fuzz; + int abs_pressure_min; + int abs_pressure_max; + int abs_pressure_fuzz; + int abs_width_min; + int abs_width_max; + int screenWidth; + int screenHeight; + uint8_t fw_version; + uint8_t tw_id; + uint8_t powerOff3V3; + uint8_t cable_config[2]; + uint8_t protocol_type; + int gpio_irq; + int gpio_reset; + int gpio_3v3_en; + int (*power)(int on); + void (*reset)(void); + struct himax_virtual_key *virtual_key; + struct kobject *vk_obj; + struct kobj_attribute *vk2Use; + + int hx_config_size; +#if defined(CONFIG_HMX_DB) + bool i2c_pull_up; + bool digital_pwr_regulator; + int reset_gpio; + u32 reset_gpio_flags; + int irq_gpio; + u32 irq_gpio_flags; + + struct regulator *vcc_ana; /* For Dragon Board */ + struct regulator *vcc_dig; /* For Dragon Board */ + struct regulator *vcc_i2c; /* For Dragon Board */ +#endif +}; + + +extern int irq_enable_count; +extern int himax_bus_read(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +extern int himax_bus_write(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +extern int himax_bus_write_command(uint8_t command, uint8_t toRetry); +extern int himax_bus_master_write(uint8_t *data, uint32_t length, uint8_t toRetry); +extern void himax_int_enable(int enable); +extern int himax_ts_register_interrupt(void); +extern uint8_t himax_int_gpio_read(int pinnum); + +extern int himax_gpio_power_config(struct himax_i2c_platform_data *pdata); + +#if defined(CONFIG_FB) + extern int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#endif + +#endif diff --git a/drivers/input/touchscreen/himax_v2_mmi/Android.mk b/drivers/input/touchscreen/himax_v2_mmi/Android.mk new file mode 100644 index 000000000000..099321fa5cde --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/Android.mk @@ -0,0 +1,35 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +HIMAX_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +HIMAX_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +ifeq ($(HIMAX_V2_MMI_0FLASH),true) + KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_HIMAX_V2_MMI_0FLASH=y +endif +ifneq ($(BOARD_USES_TOUCH_PALM),) + KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_HIMAX_V2_MMI_ENABLE_PALM=y +endif + +ifneq ($(BOARD_USES_DOUBLE_TAP),) + KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_V2_MMI_ENABLE_DOUBLE_TAP=y +endif + +ifneq ($(BOARD_USES_HIMAX_MULTI_BIN),) + KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_V2_MMI_ENABLE_MULTI_BIN=y +endif + +ifeq ($(HIMAX_V2_MMI_HX83102D),true) + KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_V2_MMI_IC_NAME_D=hx83102d +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := himax_v2_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(HIMAX_MMI_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/drivers/input/touchscreen/himax_v2_mmi/Kbuild b/drivers/input/touchscreen/himax_v2_mmi/Kbuild new file mode 100644 index 000000000000..9e764db0d1a4 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/Kbuild @@ -0,0 +1,47 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_v2_mmi +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_DEBUG \ + -DCONFIG_TOUCHSCREEN_HIMAX_INSPECT + +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_INCELL +EXTRA_CFLAGS += -DCONFIG_HMX_DB + +ifneq ($(filter hx83112,$(CONFIG_INPUT_HIMAX_V2_MMI_IC_NAME)),) + EXTRA_CFLAGS += -D__HIMAX_HX83112_MOD__ +endif + +ifneq ($(filter hx83102,$(CONFIG_INPUT_HIMAX_V2_MMI_IC_NAME)),) + EXTRA_CFLAGS += -D__HIMAX_HX83102_MOD__ +endif + +ifneq ($(filter hx83102d,$(CONFIG_INPUT_HIMAX_V2_MMI_IC_NAME_D)),) + EXTRA_CFLAGS += -D__HIMAX_HX83102D_MOD__ + EXTRA_CFLAGS += -DHX_CODE_OVERLAY +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_HIMAX_V2_MMI_ENABLE_PALM)),) + EXTRA_CFLAGS += -DHIMAX_PALM_SENSOR_EN +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_HIMAX_V2_MMI_ENABLE_DOUBLE_TAP)),) + EXTRA_CFLAGS += -DHIMAX_V2_SENSOR_EN +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_HIMAX_V2_MMI_ENABLE_MULTI_BIN)),) + EXTRA_CFLAGS += -DHIMAX_V2_MULTI_BIN +endif + +obj-m := himax_v2_mmi.o + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_HIMAX_V2_MMI_0FLASH)),) + himax_v2_mmi-objs += himax_platform_QCT_SPI.o + EXTRA_CFLAGS += -D__HIMAX_PLATFORM_SPI__ \ + -DHX_ZERO_FLASH +endif + +himax_v2_mmi-objs += himax_common.o +himax_v2_mmi-objs += himax_debug.o +himax_v2_mmi-objs += himax_ic_incell_core.o +himax_v2_mmi-objs += himax_inspection.o diff --git a/drivers/input/touchscreen/himax_v2_mmi/Makefile b/drivers/input/touchscreen/himax_v2_mmi/Makefile new file mode 100755 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/himax_v2_mmi/himax_common.c b/drivers/input/touchscreen/himax_v2_mmi/himax_common.c new file mode 100644 index 000000000000..66573c698c96 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/himax_common.c @@ -0,0 +1,4169 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for common functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +/*#include "himax_common.h"*/ +/*#include "himax_ic_core.h"*/ +#include "himax_inspection.h" +#include "himax_modular_table.h" + +#ifdef HX_SMART_WAKEUP +#define GEST_SUP_NUM 26 +/* Setting cust key define (DF = double finger) */ +/* {Double Tap, Up, Down, Left, Rright, C, Z, M, + * O, S, V, W, e, m, @, (reserve), + * Finger gesture, ^, >, <, f(R), f(L), Up(DF), Down(DF), + * Left(DF), Right(DF)} + */ + uint8_t gest_event[GEST_SUP_NUM] = { + 0x80, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x81, 0x1D, 0x2D, 0x3D, 0x1F, 0x2F, 0x51, 0x52, + 0x53, 0x54}; + +/*gest_event mapping to gest_key_def*/ + uint16_t gest_key_def[GEST_SUP_NUM] = { + KEY_POWER, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275}; + +uint8_t *wake_event_buffer; +#endif + +#ifdef HIMAX_PALM_SENSOR_EN +static struct sensors_classdev __maybe_unused palm_sensors_touch_cdev = { + .name = "palm-gesture", + .vendor = "Himax", + .version = 1, + .type = SENSOR_TYPE_MOTO_TOUCH_PALM, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +static void himax_palm_detect_func(struct himax_ts_data *ts, bool detected); +#endif + +#ifdef HIMAX_V2_SENSOR_EN +static struct sensors_classdev __maybe_unused sensors_touch_cdev = { + .name = "dt-gesture", + .vendor = "Himax", + .version = 1, + .type = SENSOR_TYPE_MOTO_DOUBLE_TAP, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + /* WAKE_UP & SPECIAL_REPORT */ + .flags = 1 | 6, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#define REPORT_MAX_COUNT 10000 +#endif + +#define SUPPORT_FINGER_DATA_CHECKSUM 0x0F +#define TS_WAKE_LOCK_TIMEOUT (5000) +#define FRAME_COUNT 5 + +#ifdef HX_TP_PROC_GUEST_INFO +struct hx_guest_info *g_guest_info_data; +EXPORT_SYMBOL(g_guest_info_data); + +char *g_guest_info_item[] = { + "projectID", + "CGColor", + "BarCode", + "Reserve1", + "Reserve2", + "Reserve3", + "Reserve4", + "Reserve5", + "VCOM", + "Vcom-3Gar", + NULL +}; +#endif + +uint32_t g_hx_chip_inited; + +#if defined(__EMBEDDED_FW__) +struct firmware g_embedded_fw = { + .data = _binary___Himax_firmware_bin_start, +}; +#endif + +#define HIMAX_FILE_NAME_LENGTH 128 + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) +char *i_CTPM_firmware_name; +#else +char *i_CTPM_firmware_name = "Himax_firmware.bin"; +#endif +bool g_auto_update_flag; +#endif +#if defined(HX_AUTO_UPDATE_FW) +unsigned char *i_CTPM_FW; +int i_CTPM_FW_len; +int g_i_FW_VER; +int g_i_CFG_VER; +int g_i_CID_MAJ; /*GUEST ID*/ +int g_i_CID_MIN; /*VER for GUEST*/ +#endif +#ifdef HX_ZERO_FLASH +int g_f_0f_updat; +#endif + +struct himax_ts_data *private_ts; +#if defined(__HIMAX_HX83102D_MOD__) +extern struct zf_operation *pzf_op; +#endif +EXPORT_SYMBOL(private_ts); + +struct himax_ic_data *ic_data; +EXPORT_SYMBOL(ic_data); + +struct himax_report_data *hx_touch_data; +EXPORT_SYMBOL(hx_touch_data); + +struct himax_core_fp g_core_fp; +EXPORT_SYMBOL(g_core_fp); + +struct himax_debug *debug_data; +EXPORT_SYMBOL(debug_data); + +struct proc_dir_entry *himax_touch_proc_dir; +EXPORT_SYMBOL(himax_touch_proc_dir); + +struct himax_chip_detect *g_core_chip_dt; +EXPORT_SYMBOL(g_core_chip_dt); + +int g_mmi_refcnt; +EXPORT_SYMBOL(g_mmi_refcnt); + +#define HIMAX_PROC_TOUCH_FOLDER "android_touch" +/*ts_work about start*/ +struct himax_target_report_data *g_target_report_data; +EXPORT_SYMBOL(g_target_report_data); + +static void himax_report_all_leave_event(struct himax_ts_data *ts); +/*ts_work about end*/ +static int HX_TOUCH_INFO_POINT_CNT; + +struct filename* (*kp_getname_kernel)(const char *filename); +struct file* (*kp_file_open_name)(struct filename *name, int flags, umode_t mode); + +unsigned long FW_VER_MAJ_FLASH_ADDR; +EXPORT_SYMBOL(FW_VER_MAJ_FLASH_ADDR); + +unsigned long FW_VER_MIN_FLASH_ADDR; +EXPORT_SYMBOL(FW_VER_MIN_FLASH_ADDR); + +unsigned long CFG_VER_MAJ_FLASH_ADDR; +EXPORT_SYMBOL(CFG_VER_MAJ_FLASH_ADDR); + +unsigned long CFG_VER_MIN_FLASH_ADDR; +EXPORT_SYMBOL(CFG_VER_MIN_FLASH_ADDR); + +unsigned long CID_VER_MAJ_FLASH_ADDR; +EXPORT_SYMBOL(CID_VER_MAJ_FLASH_ADDR); + +unsigned long CID_VER_MIN_FLASH_ADDR; +EXPORT_SYMBOL(CID_VER_MIN_FLASH_ADDR); +/*unsigned long PANEL_VERSION_ADDR;*/ + +unsigned long FW_VER_MAJ_FLASH_LENG; +EXPORT_SYMBOL(FW_VER_MAJ_FLASH_LENG); + +unsigned long FW_VER_MIN_FLASH_LENG; +EXPORT_SYMBOL(FW_VER_MIN_FLASH_LENG); + +unsigned long CFG_VER_MAJ_FLASH_LENG; +EXPORT_SYMBOL(CFG_VER_MAJ_FLASH_LENG); + +unsigned long CFG_VER_MIN_FLASH_LENG; +EXPORT_SYMBOL(CFG_VER_MIN_FLASH_LENG); + +unsigned long CID_VER_MAJ_FLASH_LENG; +EXPORT_SYMBOL(CID_VER_MAJ_FLASH_LENG); + +unsigned long CID_VER_MIN_FLASH_LENG; +EXPORT_SYMBOL(CID_VER_MIN_FLASH_LENG); +/*unsigned long PANEL_VERSION_LENG;*/ + +unsigned long FW_CFG_VER_FLASH_ADDR; + + +unsigned char IC_CHECKSUM; +EXPORT_SYMBOL(IC_CHECKSUM); + +#ifdef HX_ESD_RECOVERY +u8 HX_ESD_RESET_ACTIVATE; +EXPORT_SYMBOL(HX_ESD_RESET_ACTIVATE); + +int hx_EB_event_flag; +EXPORT_SYMBOL(hx_EB_event_flag); + +int hx_EC_event_flag; +EXPORT_SYMBOL(hx_EC_event_flag); + +int hx_ED_event_flag; +EXPORT_SYMBOL(hx_ED_event_flag); + +int g_zero_event_count; + +#endif + +u8 HX_HW_RESET_ACTIVATE; + +static uint8_t AA_press; +static uint8_t EN_NoiseFilter; +static uint8_t Last_EN_NoiseFilter; + +static int p_point_num = 0xFFFF; +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) +static uint8_t vk_press; +static int tpd_key; +static int tpd_key_old; +#endif +static int probe_fail_flag; +#ifdef HX_USB_DETECT_GLOBAL + bool USB_detect_flag; +#endif + +#if defined(CONFIG_DRM) +int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); +#elif defined(CONFIG_FB) +int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void himax_ts_early_suspend(struct early_suspend *h); +static void himax_ts_late_resume(struct early_suspend *h); +#endif + +#ifdef HX_GESTURE_TRACK + static int gest_pt_cnt; + static int gest_pt_x[GEST_PT_MAX_NUM]; + static int gest_pt_y[GEST_PT_MAX_NUM]; + static int gest_start_x, gest_start_y, gest_end_x, gest_end_y; + static int gest_width, gest_height, gest_mid_x, gest_mid_y; + static int hx_gesture_coor[16]; +#endif + +int g_ts_dbg; +EXPORT_SYMBOL(g_ts_dbg); + +/* File node for Selftest, SMWP and HSEN - Start*/ +#define HIMAX_PROC_SELF_TEST_FILE "self_test" +struct proc_dir_entry *himax_proc_self_test_file; + +#define HIMAX_PROC_SELF_TEST_RAW_FILE "self_test_rawdata" +struct proc_dir_entry *himax_proc_self_raw_file = NULL; + +uint8_t HX_PROC_SEND_FLAG; +EXPORT_SYMBOL(HX_PROC_SEND_FLAG); + +#ifdef HX_SMART_WAKEUP + #define HIMAX_PROC_SMWP_FILE "SMWP" + struct proc_dir_entry *himax_proc_SMWP_file = NULL; + #define HIMAX_PROC_GESTURE_FILE "GESTURE" + struct proc_dir_entry *himax_proc_GESTURE_file = NULL; + uint8_t HX_SMWP_EN; +#ifdef HX_P_SENSOR + #define HIMAX_PROC_PSENSOR_FILE "Psensor" + struct proc_dir_entry *himax_proc_psensor_file = NULL; +#endif +#endif + +#ifdef HX_EDGE_LIMIT + #define HIMAX_PROC_EDGE_LIMIT_FILE "edge_en" + struct proc_dir_entry *himax_proc_edge_limit_file = NULL; +#endif + +#if defined(HX_SMART_WAKEUP) || defined(CONFIG_TOUCHSCREEN_HIMAX_INSPECT) +bool FAKE_POWER_KEY_SEND = true; +#endif + +#ifdef HX_HIGH_SENSE + #define HIMAX_PROC_HSEN_FILE "HSEN" + struct proc_dir_entry *himax_proc_HSEN_file = NULL; +#endif + +#if defined(HX_PALM_REPORT) +static int himax_palm_detect(uint8_t *buf) +{ + struct himax_ts_data *ts = private_ts; + int32_t loop_i; + int base = 0; + int x = 0, y = 0, w = 0; + + loop_i = 0; + base = loop_i * 4; + x = buf[base] << 8 | buf[base + 1]; + y = (buf[base + 2] << 8 | buf[base + 3]); + w = buf[(ts->nFinger_support * 4) + loop_i]; +#ifndef HIMAX_PALM_SENSOR_EN + I(" %s HX_PALM_REPORT_loopi=%d,base=%x,X=%x,Y=%x,W=%x\n", __func__, loop_i, base, x, y, w); + if ((!atomic_read(&ts->suspend_mode)) && (x == 0xFA5A) && (y == 0xFA5A) && (w == 0x00)) + return PALM_REPORT; + else + return NOT_REPORT; +#else + if ((x == 0xFA5A) && (y == 0xFA5A)) { + I("%s: HX_PALM_REPORT loop_i=%d base=%x X=%x, Y=%x, W=%x\n", + __func__, loop_i, base, x, y, w); + return PALM_REPORT; + } else if ((x == 0xFC5C) && (y == 0xFC5C)) { + I("%s: HX_PALM_REPORT loop_i=%d base=%x X=%x, Y=%x, W=%x\n", + __func__, loop_i, base, x, y, w); + return PALM_LEAVE_REPORT; + } else + return NOT_REPORT; +#endif +} +#endif + +static ssize_t himax_self_test_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + int i = 0; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) { + return -EFAULT; + } + + if (hx_self_test_file_name == NULL) { + E("file name is NULL\n"); + hx_self_test_file_name = kzalloc(80, GFP_KERNEL); + snprintf(hx_self_test_file_name, 15, "hx_criteria.csv"); + } + + for (i = 0; i < 80; i++) { + if (buf[i] == ',' || buf[i] == '\n') { + memset(hx_self_test_file_name, 0x0, 80); + memcpy(hx_self_test_file_name, buf, i); + I("%s: Get name from Customer\n", __func__); + break; + } + } + if (i == 80) { + memset(hx_self_test_file_name, 0x0, 80); + snprintf(hx_self_test_file_name, 16, "hx_criteria.csv"); + I("%s: Use default name\n", __func__); + } + I("file name = %s\n", hx_self_test_file_name); + + return len; +} + +static ssize_t himax_self_test_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + int val = 0x00; + size_t ret = 0; + char *temp_buf; + I("%s: enter, %d \n", __func__, __LINE__); + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + himax_int_enable(0);/* disable irq */ + private_ts->in_self_test = 1; + val = g_core_fp.fp_chip_self_test(); +#ifdef HX_ESD_RECOVERY + HX_ESD_RESET_ACTIVATE = 1; +#endif + himax_int_enable(1);/* enable irq */ + + if (val == 0x00) { + ret += snprintf(temp_buf + ret, len - ret, "Self_Test Pass\n"); + } else { + ret += snprintf(temp_buf + ret, len - ret, "Self_Test Fail\n"); + } + + private_ts->in_self_test = 0; + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static struct file_operations himax_proc_self_test_ops = { + .owner = THIS_MODULE, + .read = himax_self_test_read, + .write = himax_self_test_write, +}; + +static void *himax_self_raw_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) { + return NULL; + } + return (void *)((unsigned long) *pos + 1); +} + +static void *himax_self_raw_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_self_raw_seq_stop(struct seq_file *s, void *v) +{ +} + +static int himax_self_raw_seq_read(struct seq_file *s, void *v) +{ + size_t ret = 0; + + if (g_rslt_data != NULL) { + seq_printf(s, "%s", g_rslt_data); + } + return ret; +} +static struct seq_operations himax_self_raw_seq_ops = { + .start = himax_self_raw_seq_start, + .next = himax_self_raw_seq_next, + .stop = himax_self_raw_seq_stop, + .show = himax_self_raw_seq_read, +}; +static int himax_self_raw_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_self_raw_seq_ops); +}; + +static struct file_operations himax_proc_self_raw_ops = { + .owner = THIS_MODULE, + .open = himax_self_raw_proc_open, + .read = seq_read, +}; + +#if 0 +static ssize_t himax_self_test(struct seq_file *s, void *v) +{ + int val = 0x00; + size_t ret = 0; + + I("%s: enter, %d\n", __func__, __LINE__); + + if (private_ts->suspended == 1) { + E("%s: please do self test in normal active mode\n", __func__); + return HX_INIT_FAIL; + } + + himax_int_enable(0);/* disable irq */ + + private_ts->in_self_test = 1; + + val = g_core_fp.fp_chip_self_test(); +/* + *#ifdef HX_ESD_RECOVERY + * HX_ESD_RESET_ACTIVATE = 1; + *#endif + * himax_int_enable(1); //enable irq + */ + if (val == HX_INSPECT_OK) + seq_puts(s, "Self_Test Pass:\n"); + else + seq_puts(s, "Self_Test Fail:\n"); + + /*Print_Self_Test_Info(val,temp_buf);*/ + if ((val & HX_INSPECT_EFILE) == HX_INSPECT_EFILE) { + seq_puts(s, " Get criteria File Fail\n"); + goto END_FUNC; + } else if ((val & HX_INSPECT_MEMALLCTFAIL) == HX_INSPECT_MEMALLCTFAIL) { + seq_puts(s, " Allocate memory Fail\n"); + goto END_FUNC; + } else { + seq_puts(s, " Get criteria File OK\n"); + } + if ((val & HX_INSPECT_EGETRAW) == HX_INSPECT_EGETRAW) { + seq_puts(s, " Get raw data Fail\n"); + goto END_FUNC; + } else { + seq_puts(s, " Get raw data OK\n"); + } +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + if (g_inspt_crtra_flag[IDX_RAWMIN] == 1 && g_inspt_crtra_flag[IDX_RAWMAX] == 1) { + if ((val & HX_INSPECT_ERAW) == HX_INSPECT_ERAW) + seq_puts(s, " Check Raw data Fail\n"); + else + seq_puts(s, " Check Raw data OK\n"); + } + if (g_inspt_crtra_flag[IDX_WT_NOISEMAX] == 1 && g_inspt_crtra_flag[IDX_WT_NOISEMIN] == 1) { + if ((val & HX_INSPECT_WT_ENOISE) == HX_INSPECT_WT_ENOISE) + seq_puts(s, " Check Noise(weight) Fail\n"); + else + seq_puts(s, " Check Noise(weight) OK\n"); + } + if (g_inspt_crtra_flag[IDX_ABS_NOISEMAX] == 1 && g_inspt_crtra_flag[IDX_ABS_NOISEMIN] == 1) { + if ((val & HX_INSPECT_ABS_ENOISE) == HX_INSPECT_ABS_ENOISE) + seq_puts(s, " Check Noise(absolute) Fail\n"); + else + seq_puts(s, " Check Noise(absolute) OK\n"); + } + if (g_inspt_crtra_flag[IDX_OPENMIN] == 1 && g_inspt_crtra_flag[IDX_OPENMAX] == 1) { + if ((val & HX_INSPECT_EOPEN) == HX_INSPECT_EOPEN) + seq_puts(s, " Check Sensor open Fail\n"); + else + seq_puts(s, " Check Sensor open OK\n"); + } + if (g_inspt_crtra_flag[IDX_M_OPENMIN] == 1 && g_inspt_crtra_flag[IDX_M_OPENMAX] == 1) { + if ((val & HX_INSPECT_EMOPEN) == HX_INSPECT_EMOPEN) + seq_puts(s, " Check Sensor micro open Fail\n"); + else + seq_puts(s, " Check Sensor micro open OK\n"); + } + if (g_inspt_crtra_flag[IDX_SORTMIN] == 1 && g_inspt_crtra_flag[IDX_SORTMAX] == 1) { + if ((val & HX_INSPECT_ESHORT) == HX_INSPECT_ESHORT) + seq_puts(s, " Check Sensor short Fail\n"); + else + seq_puts(s, " Check Sensor short OK\n"); + } + if ((g_inspt_crtra_flag[IDX_GAP_HOR_RAWMIN] == 1 && g_inspt_crtra_flag[IDX_GAP_HOR_RAWMAX] == 1) || + (g_inspt_crtra_flag[IDX_GAP_VER_RAWMIN] == 1 && g_inspt_crtra_flag[IDX_GAP_VER_RAWMAX] == 1)) { + if ((val & HX_INSPECT_EGAP_RAW) == HX_INSPECT_EGAP_RAW) + seq_puts(s, " Check Raw Data GAP Fail\n"); + else + seq_puts(s, " Check Raw Data GAP OK\n"); + } + if (g_inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MIN] == 1 && g_inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MAX] == 1) { + if ((val & HX_INSPECT_EACT_IDLE_RAW) == HX_INSPECT_EACT_IDLE_RAW) + seq_puts(s, " Check ACT_IDLE RAW Fail\n"); + else + seq_puts(s, " Check ACT_IDLE RAW OK\n"); + } + if (g_inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MIN] == 1 && g_inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MAX] == 1) { + if ((val & HX_INSPECT_EACT_IDLE_NOISE) == HX_INSPECT_EACT_IDLE_NOISE) + seq_puts(s, " Check ACT_IDLE NOISE Fail\n"); + else + seq_puts(s, " Check ACT_IDLE NOISE OK\n"); + } + if (g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MIN] == 1 && g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MAX] == 1) { + if ((val & HX_INSPECT_ELPWUG_RAW) == HX_INSPECT_ELPWUG_RAW) + seq_puts(s, " Check LPWUG RAW Fail\n"); + else + seq_puts(s, " Check LPWUG RAW OK\n"); + } + if (g_inspt_crtra_flag[IDX_LPWUG_NOISE_MIN] == 1 && g_inspt_crtra_flag[IDX_LPWUG_NOISE_MAX] == 1) { + if ((val & HX_INSPECT_ELPWUG_NOISE) == HX_INSPECT_ELPWUG_NOISE) + seq_puts(s, " Check LPWUG NOISE Fail\n"); + else + seq_puts(s, " Check LPWUG NOISE OK\n"); + } + if (g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MIN] == 1 && g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MAX] == 1) { + if ((val & HX_INSPECT_ELPWUG_IDLE_RAW) == HX_INSPECT_ELPWUG_IDLE_RAW) + seq_puts(s, " Check LPWUG IDLE RAW Fail\n"); + else + seq_puts(s, " Check LPWUG IDLE RAW OK\n"); + } + if (g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MIN] == 1 && g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MAX] == 1) { + if ((val & HX_INSPECT_ELPWUG_IDLE_NOISE) == HX_INSPECT_ELPWUG_IDLE_NOISE) + seq_puts(s, " Check LPWUG IDLE NOISE Fail\n"); + else + seq_puts(s, " Check LPWUG IDLE NOISE OK\n"); + } +#endif + +END_FUNC: + private_ts->in_self_test = 0; + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + if (g_inspt_crtra_flag != NULL) { + kfree(g_inspt_crtra_flag); + g_inspt_crtra_flag = NULL; + } +#endif + +#ifdef HX_ESD_RECOVERY + HX_ESD_RESET_ACTIVATE = 1; +#endif + himax_int_enable(1);/* enable irq */ + + return ret; +} + +static void *himax_self_test_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) + return NULL; + + + return (void *)((unsigned long) *pos + 1); +} + +static void *himax_self_test_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_self_test_seq_stop(struct seq_file *s, void *v) +{ +} + +static ssize_t himax_self_test_write(struct file *filp, const char __user *buff, + size_t len, loff_t *data) +{ + char buf[80]; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + if (buf[0] == 'r') { + chip_test_r_flag = true; + I("%s: Start to read chip test data.\n", __func__); + } else { + chip_test_r_flag = false; + I("%s: Back to do self test.\n", __func__); + } + + return len; +} + +static int himax_self_test_seq_read(struct seq_file *s, void *v) +{ + size_t ret = 0; + + if (chip_test_r_flag) { +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + if (g_rslt_data) + seq_printf(s, "%s", g_rslt_data); + else +#endif + seq_puts(s, "No chip test data.\n"); + } else { + himax_self_test(s, v); + } + + return ret; +} + +static const struct seq_operations himax_self_test_seq_ops = { + .start = himax_self_test_seq_start, + .next = himax_self_test_seq_next, + .stop = himax_self_test_seq_stop, + .show = himax_self_test_seq_read, +}; + +static int himax_self_test_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_self_test_seq_ops); +}; + +static const struct file_operations himax_proc_self_test_ops = { + .owner = THIS_MODULE, + .open = himax_self_test_proc_open, + .read = seq_read, + .write = himax_self_test_write, + .release = seq_release, +}; +#endif + +#ifdef HX_HIGH_SENSE +static ssize_t himax_HSEN_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + size_t count = 0; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kcalloc(len, sizeof(char), GFP_KERNEL); + count = snprintf(temp_buf, PAGE_SIZE, "%d\n", ts->HSEN_enable); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return count; +} + +static ssize_t himax_HSEN_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + if (buf[0] == '0') + ts->HSEN_enable = 0; + else if (buf[0] == '1') + ts->HSEN_enable = 1; + else + return -EINVAL; + + g_core_fp.fp_set_HSEN_enable(ts->HSEN_enable, ts->suspended); + I("%s: HSEN_enable = %d.\n", __func__, ts->HSEN_enable); + return len; +} + +static const struct file_operations himax_proc_HSEN_ops = { + .owner = THIS_MODULE, + .read = himax_HSEN_read, + .write = himax_HSEN_write, +}; +#endif + +#ifdef HX_SMART_WAKEUP +static ssize_t himax_SMWP_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + size_t count = 0; + struct himax_ts_data *ts = private_ts; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kcalloc(len, sizeof(char), GFP_KERNEL); + count = snprintf(temp_buf, PAGE_SIZE, "%d\n", ts->SMWP_enable); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return count; +} + +static ssize_t himax_SMWP_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + if (buf[0] == '0') + ts->SMWP_enable = 0; + else if (buf[0] == '1') + ts->SMWP_enable = 1; + else + return -EINVAL; + + g_core_fp.fp_set_SMWP_enable(ts->SMWP_enable, ts->suspended); + HX_SMWP_EN = ts->SMWP_enable; + I("%s: SMART_WAKEUP_enable = %d.\n", __func__, HX_SMWP_EN); + return len; +} + +static const struct file_operations himax_proc_SMWP_ops = { + .owner = THIS_MODULE, + .read = himax_SMWP_read, + .write = himax_SMWP_write, +}; + +static ssize_t himax_GESTURE_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + int i = 0; + size_t ret = 0; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kcalloc(len, sizeof(char), GFP_KERNEL); + + for (i = 0; i < GEST_SUP_NUM; i++) + ret += snprintf(temp_buf + ret, len - ret, "ges_en[%d]=%d\n", i, ts->gesture_cust_en[i]); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + ret = 0; + } + + return ret; +} + +static ssize_t himax_GESTURE_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + int i = 0; + int j = 0; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + I("himax_GESTURE_store= %s, len = %d\n", buf, (int)len); + + for (i = 0; i < len; i++) { + if (buf[i] == '0' && j < GEST_SUP_NUM) { + ts->gesture_cust_en[j] = 0; + I("gesture en[%d]=%d\n", j, ts->gesture_cust_en[j]); + j++; + } else if (buf[i] == '1' && j < GEST_SUP_NUM) { + ts->gesture_cust_en[j] = 1; + I("gesture en[%d]=%d\n", j, ts->gesture_cust_en[j]); + j++; + } else + I("Not 0/1 or >=GEST_SUP_NUM : buf[%d] = %c\n", i, buf[i]); + } + + return len; +} + +static const struct file_operations himax_proc_Gesture_ops = { + .owner = THIS_MODULE, + .read = himax_GESTURE_read, + .write = himax_GESTURE_write, +}; + +#ifdef HX_P_SENSOR +static ssize_t himax_psensor_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + size_t count = 0; + struct himax_ts_data *ts = private_ts; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kcalloc(len, sizeof(char), GFP_KERNEL); + count = snprintf(temp_buf, PAGE_SIZE, "p-sensor flag = %d\n", ts->psensor_flag); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return count; +} + +static ssize_t himax_psensor_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + if (buf[0] == '0' && ts->SMWP_enable == 1) { + ts->psensor_flag = false; + g_core_fp.fp_black_gest_ctrl(false); + } else if (buf[0] == '1' && ts->SMWP_enable == 1) { + ts->psensor_flag = true; + g_core_fp.fp_black_gest_ctrl(true); + } else if (ts->SMWP_enable == 0) { + I("%s: SMWP is disable, not supprot to ctrl p-sensor.\n", __func__); + } else + return -EINVAL; + + I("%s: psensor_flag = %d.\n", __func__, ts->psensor_flag); + return len; +} + +static const struct file_operations himax_proc_psensor_ops = { + .owner = THIS_MODULE, + .read = himax_psensor_read, + .write = himax_psensor_write, +}; +#endif +#endif + +#ifdef HX_EDGE_LIMIT +static ssize_t himax_edge_limit_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + size_t count = 0; + struct himax_ts_data *ts = private_ts; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + count = snprintf(temp_buf, PAGE_SIZE, "%d\n", ts->edge_limit_enable); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return count; +} + +static ssize_t himax_edge_limit_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) { + return -EFAULT; + } + + if (buf[0] == '0') + ts->edge_limit_enable = 0; + else if (buf[0] == '1') + ts->edge_limit_enable = 1;/*Portrait*/ + else if (buf[0] == '2') + ts->edge_limit_enable = 2;/*Landscape-Right*/ + else if (buf[0] == '3') + ts->edge_limit_enable = 3;/*Landscape-Left*/ + else + return -EINVAL; + + g_core_fp.fp_set_edge_limit_enable(ts->edge_limit_enable, ts->suspended); + I("%s: edge_limit_enable = %d.\n", __func__, ts->edge_limit_enable); + return len; +} + +static struct file_operations himax_proc_edge_limit_ops = { + .owner = THIS_MODULE, + .read = himax_edge_limit_read, + .write = himax_edge_limit_write, +}; +#endif + +int himax_common_proc_init(void) +{ + himax_touch_proc_dir = proc_mkdir(HIMAX_PROC_TOUCH_FOLDER, NULL); + + if (himax_touch_proc_dir == NULL) { + E(" %s: himax_touch_proc_dir file create failed!\n", __func__); + return -ENOMEM; + } +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + if (fp_himax_self_test_init != NULL) + fp_himax_self_test_init(); +#endif + + himax_proc_self_test_file = proc_create(HIMAX_PROC_SELF_TEST_FILE, 0664, himax_touch_proc_dir, &himax_proc_self_test_ops); + if (himax_proc_self_test_file == NULL) { + E(" %s: proc self_test file create failed!\n", __func__); + goto fail_1; + } + + himax_proc_self_raw_file = proc_create(HIMAX_PROC_SELF_TEST_RAW_FILE, 0444, himax_touch_proc_dir, &himax_proc_self_raw_ops); + if (himax_proc_self_raw_file == NULL) { + E(" %s: proc self_test raw file create failed!\n", __func__); + goto fail_1_1; + } + +#ifdef HX_HIGH_SENSE + himax_proc_HSEN_file = proc_create(HIMAX_PROC_HSEN_FILE, 0666, + himax_touch_proc_dir, &himax_proc_HSEN_ops); + + if (himax_proc_HSEN_file == NULL) { + E(" %s: proc HSEN file create failed!\n", __func__); + goto fail_2; + } + +#endif +#ifdef HX_SMART_WAKEUP + himax_proc_SMWP_file = proc_create(HIMAX_PROC_SMWP_FILE, 0666, + himax_touch_proc_dir, &himax_proc_SMWP_ops); + + if (himax_proc_SMWP_file == NULL) { + E(" %s: proc SMWP file create failed!\n", __func__); + goto fail_3; + } + + himax_proc_GESTURE_file = proc_create(HIMAX_PROC_GESTURE_FILE, 0666, + himax_touch_proc_dir, &himax_proc_Gesture_ops); + + if (himax_proc_GESTURE_file == NULL) { + E(" %s: proc GESTURE file create failed!\n", __func__); + goto fail_4; + } +#ifdef HX_P_SENSOR + himax_proc_psensor_file = proc_create(HIMAX_PROC_PSENSOR_FILE, 0666, + himax_touch_proc_dir, &himax_proc_psensor_ops); + + if (himax_proc_psensor_file == NULL) { + E(" %s: proc GESTURE file create failed!\n", __func__); + goto fail_5; + } +#endif +#endif +#ifdef HX_EDGE_LIMIT + himax_proc_edge_limit_file = proc_create(HIMAX_PROC_EDGE_LIMIT_FILE, 0666, + himax_touch_proc_dir, &himax_proc_edge_limit_ops); + + if (himax_proc_edge_limit_file == NULL) { + E(" %s: proc edge limit file create failed!\n", __func__); + goto fail_6; + } +#endif + return 0; +#ifdef HX_EDGE_LIMIT + remove_proc_entry(HIMAX_PROC_EDGE_LIMIT_FILE, himax_touch_proc_dir); +fail_6: +#endif +#ifdef HX_SMART_WAKEUP +#ifdef HX_P_SENSOR + remove_proc_entry(HIMAX_PROC_PSENSOR_FILE, himax_touch_proc_dir); +fail_5: +#endif + remove_proc_entry(HIMAX_PROC_GESTURE_FILE, himax_touch_proc_dir); +fail_4: + remove_proc_entry(HIMAX_PROC_SMWP_FILE, himax_touch_proc_dir); +fail_3: +#endif +#ifdef HX_HIGH_SENSE + remove_proc_entry(HIMAX_PROC_HSEN_FILE, himax_touch_proc_dir); +fail_2: +#endif + remove_proc_entry(HIMAX_PROC_SELF_TEST_RAW_FILE, himax_touch_proc_dir); +fail_1_1: + remove_proc_entry(HIMAX_PROC_SELF_TEST_FILE, himax_touch_proc_dir); +fail_1: + return -ENOMEM; +} + +void himax_common_proc_deinit(void) +{ +remove_proc_entry(HIMAX_PROC_SELF_TEST_FILE, himax_touch_proc_dir); +remove_proc_entry(HIMAX_PROC_SELF_TEST_RAW_FILE, himax_touch_proc_dir); + +#ifdef HX_EDGE_LIMIT + remove_proc_entry(HIMAX_PROC_EDGE_LIMIT_FILE, himax_touch_proc_dir); +#endif +#ifdef HX_HIGH_SENSE + remove_proc_entry(HIMAX_PROC_HSEN_FILE, himax_touch_proc_dir); +#endif +#ifdef HX_SMART_WAKEUP +#ifdef HX_P_SENSOR + remove_proc_entry(HIMAX_PROC_PSENSOR_FILE, himax_touch_proc_dir); +#endif + remove_proc_entry(HIMAX_PROC_GESTURE_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_SMWP_FILE, himax_touch_proc_dir); +#endif + remove_proc_entry(HIMAX_PROC_TOUCH_FOLDER, NULL); +} + +/* File node for SMWP and HSEN - End*/ + +int himax_input_register(struct himax_ts_data *ts) +{ + int ret = 0; +#if defined(HX_SMART_WAKEUP) + int i = 0; +#endif + ret = himax_dev_set(ts); + + if (ret < 0) { + I("%s, input device register fail!\n", __func__); + ret = INPUT_REGISTER_FAIL; + goto input_device_fail; + } + + set_bit(EV_SYN, ts->input_dev->evbit); + set_bit(EV_ABS, ts->input_dev->evbit); + set_bit(EV_KEY, ts->input_dev->evbit); +#if defined(HX_PLATFOME_DEFINE_KEY) + himax_platform_key(); +#else + set_bit(KEY_BACK, ts->input_dev->keybit); + set_bit(KEY_HOME, ts->input_dev->keybit); + set_bit(KEY_MENU, ts->input_dev->keybit); + set_bit(KEY_SEARCH, ts->input_dev->keybit); +#endif +#if defined(HX_SMART_WAKEUP) || defined(HX_PALM_REPORT) + set_bit(KEY_POWER, ts->input_dev->keybit); +#endif +#if defined(HX_SMART_WAKEUP) + for (i = 1; i < GEST_SUP_NUM; i++) + set_bit(gest_key_def[i], ts->input_dev->keybit); + +#endif + set_bit(BTN_TOUCH, ts->input_dev->keybit); + set_bit(KEY_APPSELECT, ts->input_dev->keybit); + set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); +#ifdef HX_PROTOCOL_A + /*ts->input_dev->mtsize = ts->nFinger_support;*/ + input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, 3, 0, 0); +#else + set_bit(MT_TOOL_FINGER, ts->input_dev->keybit); +#if defined(HX_PROTOCOL_B_3PA) + input_mt_init_slots(ts->input_dev, ts->nFinger_support, INPUT_MT_DIRECT); +#else + input_mt_init_slots(ts->input_dev, ts->nFinger_support); +#endif +#endif + I("input_set_abs_params: mix_x %d, max_x %d, min_y %d, max_y %d\n", + ts->pdata->abs_x_min, ts->pdata->abs_x_max, ts->pdata->abs_y_min, ts->pdata->abs_y_max); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, ts->pdata->abs_x_min, ts->pdata->abs_x_max, ts->pdata->abs_x_fuzz, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, ts->pdata->abs_y_min, ts->pdata->abs_y_max, ts->pdata->abs_y_fuzz, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, ts->pdata->abs_pressure_min, ts->pdata->abs_pressure_max, ts->pdata->abs_pressure_fuzz, 0); +#ifndef HX_PROTOCOL_A + input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, ts->pdata->abs_pressure_min, ts->pdata->abs_pressure_max, ts->pdata->abs_pressure_fuzz, 0); + input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, ts->pdata->abs_width_min, ts->pdata->abs_width_max, ts->pdata->abs_pressure_fuzz, 0); +#endif +/* input_set_abs_params(ts->input_dev, ABS_MT_AMPLITUDE, 0, ((ts->pdata->abs_pressure_max << 16) | ts->pdata->abs_width_max), 0, 0); + * input_set_abs_params(ts->input_dev, ABS_MT_POSITION, 0, (BIT(31) | (ts->pdata->abs_x_max << 16) | ts->pdata->abs_y_max), 0, 0); + */ + + if (himax_input_register_device(ts->input_dev) == 0) + ret = NO_ERR; + else + ret = INPUT_REGISTER_FAIL; + +#if defined(HX_PEN_FUNC_EN) + set_bit(EV_SYN, ts->hx_pen_dev->evbit); + set_bit(EV_ABS, ts->hx_pen_dev->evbit); + set_bit(EV_KEY, ts->hx_pen_dev->evbit); + set_bit(BTN_TOUCH, ts->hx_pen_dev->keybit); + set_bit(INPUT_PROP_DIRECT, ts->hx_pen_dev->propbit); + + set_bit(BTN_TOOL_PEN, ts->hx_pen_dev->keybit); + set_bit(BTN_TOOL_RUBBER, ts->hx_pen_dev->keybit); + + input_set_abs_params(ts->hx_pen_dev, ABS_PRESSURE, 0, 4095, 0, 0); + input_set_abs_params(ts->hx_pen_dev, ABS_DISTANCE, 0, 1, 0, 0); + input_set_abs_params(ts->hx_pen_dev, ABS_TILT_X, -60, 60, 0, 0); + input_set_abs_params(ts->hx_pen_dev, ABS_TILT_Y, -60, 60, 0, 0); + /*input_set_capability(ts->hx_pen_dev, EV_SW, SW_PEN_INSERT);*/ + input_set_capability(ts->hx_pen_dev, EV_KEY, BTN_TOUCH); + input_set_capability(ts->hx_pen_dev, EV_KEY, BTN_STYLUS); + + input_set_abs_params(ts->hx_pen_dev, ABS_X, ts->pdata->abs_x_min, ts->pdata->abs_x_max, ts->pdata->abs_x_fuzz, 0); + input_set_abs_params(ts->hx_pen_dev, ABS_Y, ts->pdata->abs_y_min, ts->pdata->abs_y_max, ts->pdata->abs_y_fuzz, 0); + + if (himax_input_register_device(ts->hx_pen_dev) == 0) + ret = NO_ERR; + else + ret = INPUT_REGISTER_FAIL; + +#endif + I("%s, input device registered.\n", __func__); + +input_device_fail: + return ret; +} +EXPORT_SYMBOL(himax_input_register); + +static void calcDataSize(void) +{ + struct himax_ts_data *ts_data = private_ts; + + ts_data->x_channel = ic_data->HX_RX_NUM; + ts_data->y_channel = ic_data->HX_TX_NUM; + ts_data->nFinger_support = ic_data->HX_MAX_PT; + + ts_data->coord_data_size = 4 * ts_data->nFinger_support; + ts_data->area_data_size = ((ts_data->nFinger_support / 4) + (ts_data->nFinger_support % 4 ? 1 : 0)) * 4; + ts_data->coordInfoSize = ts_data->coord_data_size + ts_data->area_data_size + 4; + ts_data->raw_data_frame_size = 128 - ts_data->coord_data_size - ts_data->area_data_size - 4 - 4 - 1; + + if (ts_data->raw_data_frame_size == 0) { + E("%s: could NOT calculate!\n", __func__); + return; + } + + ts_data->raw_data_nframes = ((uint32_t)ts_data->x_channel * ts_data->y_channel + + ts_data->x_channel + ts_data->y_channel) / ts_data->raw_data_frame_size + + (((uint32_t)ts_data->x_channel * ts_data->y_channel + + ts_data->x_channel + ts_data->y_channel) % ts_data->raw_data_frame_size) ? 1 : 0; + I("%s: coord_data_size: %d, area_data_size:%d, raw_data_frame_size:%d, raw_data_nframes:%d\n", __func__, ts_data->coord_data_size, ts_data->area_data_size, ts_data->raw_data_frame_size, ts_data->raw_data_nframes); +} + +static void calculate_point_number(void) +{ + HX_TOUCH_INFO_POINT_CNT = ic_data->HX_MAX_PT * 4; + + if ((ic_data->HX_MAX_PT % 4) == 0) + HX_TOUCH_INFO_POINT_CNT += (ic_data->HX_MAX_PT / 4) * 4; + else + HX_TOUCH_INFO_POINT_CNT += ((ic_data->HX_MAX_PT / 4) + 1) * 4; +} +#if defined(HX_AUTO_UPDATE_FW) +static int himax_auto_update_check(void) +{ + int32_t ret; + int flag_sys_ex_is_same = 0; + + I("%s:Entering!\n", __func__); + if (g_core_fp.fp_fw_ver_bin() == 0) { + flag_sys_ex_is_same = (g_core_fp._diff_overlay_flash() != g_core_fp._diff_overlay_bin()) ? 1 : 0; + I("Now flag_sys_ex_is_same valur=%d\n", flag_sys_ex_is_same); + if (((ic_data->vendor_fw_ver < g_i_FW_VER) || (ic_data->vendor_config_ver < g_i_CFG_VER)) + || (flag_sys_ex_is_same == 1)) { + I("Need to update!\n"); + ret = NO_ERR; + } else { + E("No need to update!\n"); + ret = 1; + } + } else { + E("FW bin fail!\n"); + ret = 1; + } + + return ret; +} + +static int i_get_FW(void) +{ + int ret = 0; + const struct firmware *image = NULL; + + I("file name = %s\n", i_CTPM_firmware_name); + ret = request_firmware(&image, i_CTPM_firmware_name, private_ts->dev); + if (ret < 0) { +#if defined(__EMBEDDED_FW__) + image = &g_embedded_fw; + I("%s: Couldn't find userspace FW, use embedded FW(size:%zu) instead.\n", __func__, g_embedded_fw.size); +#else + E("%s,fail in line%d error code=%d\n", __func__, __LINE__, ret); + return OPEN_FILE_FAIL; +#endif + } + + if (image != NULL) { + i_CTPM_FW_len = image->size; + i_CTPM_FW = kcalloc(i_CTPM_FW_len, sizeof(char), GFP_KERNEL); + memcpy(i_CTPM_FW, image->data, sizeof(char)*i_CTPM_FW_len); + } else { + I("%s: i_CTPM_FW = NULL\n", __func__); + return OPEN_FILE_FAIL; + } + + if (ret >= 0) + release_firmware(image); + ret = NO_ERR; + return ret; +} +static int i_update_FW(void) +{ + int upgrade_times = 0; + uint8_t ret = 0, result = 0; + + himax_int_enable(0); + + +update_retry: + + if (i_CTPM_FW_len == FW_SIZE_32k) + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_32k(i_CTPM_FW, i_CTPM_FW_len, false); + else if (i_CTPM_FW_len == FW_SIZE_60k) + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_60k(i_CTPM_FW, i_CTPM_FW_len, false); + else if (i_CTPM_FW_len == FW_SIZE_64k) + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_64k(i_CTPM_FW, i_CTPM_FW_len, false); + else if (i_CTPM_FW_len == FW_SIZE_124k) + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_124k(i_CTPM_FW, i_CTPM_FW_len, false); + else if (i_CTPM_FW_len == FW_SIZE_128k) + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_128k(i_CTPM_FW, i_CTPM_FW_len, false); + + if (ret == 0) { + upgrade_times++; + E("%s: TP upgrade error, upgrade_times = %d\n", __func__, upgrade_times); + + if (upgrade_times < 3) + goto update_retry; + else + result = -1; + + + } else { + g_core_fp.fp_read_FW_ver(); + g_core_fp.fp_touch_information(); + result = 1;/*upgrade success*/ + I("%s: TP upgrade OK\n", __func__); + } + + kfree(i_CTPM_FW); + i_CTPM_FW = NULL; + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(true, false); +#else + g_core_fp.fp_sense_on(0x00); +#endif + himax_int_enable(1); + return result; +} +#endif + + +static int himax_loadSensorConfig(struct himax_i2c_platform_data *pdata) +{ + I("%s: initialization complete\n", __func__); + return NO_ERR; +} + +#ifdef HX_ESD_RECOVERY +static void himax_esd_hw_reset(void) +{ +#ifdef HX_ZERO_FLASH + int result = 0; +#endif + if (g_ts_dbg != 0) + I("%s: Entering\n", __func__); + + I("START_Himax TP: ESD - Reset\n"); + + if (private_ts->in_self_test == 1) { + I("In self test , not TP: ESD - Reset\n"); + return; + } + + g_core_fp.fp_esd_ic_reset(); +#ifdef HX_ZERO_FLASH + I("It will update fw after esd event in zero flash mode!\n"); + result = g_core_fp.fp_0f_operation_dirly(); + if (result) { + E("Something is wrong! Skip Update with zero flash!\n"); + goto ESCAPE_0F_UPDATE; + } + g_core_fp.fp_reload_disable(0); + g_core_fp.fp_sense_on(0x00); + himax_report_all_leave_event(private_ts); + himax_int_enable(1); +ESCAPE_0F_UPDATE: +#endif + I("END_Himax TP: ESD - Reset\n"); +} +#endif + +#ifdef HX_SMART_WAKEUP +#ifdef HX_GESTURE_TRACK +static void gest_pt_log_coordinate(int rx, int tx) +{ + /*driver report x y with range 0 - 255 , we scale it up to x/y pixel*/ + gest_pt_x[gest_pt_cnt] = rx * (ic_data->HX_X_RES) / 255; + gest_pt_y[gest_pt_cnt] = tx * (ic_data->HX_Y_RES) / 255; +} +#endif +static int himax_wake_event_parse(struct himax_ts_data *ts, int ts_status) +{ + uint8_t *buf = wake_event_buffer; +#ifdef HX_GESTURE_TRACK + int tmp_max_x = 0x00, tmp_min_x = 0xFFFF, tmp_max_y = 0x00, tmp_min_y = 0xFFFF; + int gest_len; +#endif + int i = 0, check_FC = 0, ret; + int j = 0, gesture_pos = 0, gesture_flag = 0; + + if (g_ts_dbg != 0) + I("%s: Entering!, ts_status=%d\n", __func__, ts_status); + + if (buf == NULL) { + ret = -ENOMEM; + goto END; + } + + memcpy(buf, hx_touch_data->hx_event_buf, hx_touch_data->event_size); + + for (i = 0; i < GEST_PTLG_ID_LEN; i++) { + for (j = 0; j < GEST_SUP_NUM; j++) { + if (buf[i] == gest_event[j]) { + gesture_flag = buf[i]; + gesture_pos = j; + break; + } + } + I("0x%2.2X ", buf[i]); + if (buf[i] == gesture_flag) { + check_FC++; + } else { + I("ID START at %x , value = 0x%2X skip the event\n", i, buf[i]); + break; + } + } + + I("Himax gesture_flag= %x\n", gesture_flag); + I("Himax check_FC is %d\n", check_FC); + + if (check_FC != GEST_PTLG_ID_LEN) { + ret = 0; + goto END; + } + + if (buf[GEST_PTLG_ID_LEN] != GEST_PTLG_HDR_ID1 || + buf[GEST_PTLG_ID_LEN + 1] != GEST_PTLG_HDR_ID2) { + ret = 0; + goto END; + } + +#ifdef HX_GESTURE_TRACK + + if (buf[GEST_PTLG_ID_LEN] == GEST_PTLG_HDR_ID1 && + buf[GEST_PTLG_ID_LEN + 1] == GEST_PTLG_HDR_ID2) { + gest_len = buf[GEST_PTLG_ID_LEN + 2]; + I("gest_len = %d\n", gest_len); + i = 0; + gest_pt_cnt = 0; + I("gest doornidate start\n %s", __func__); + + while (i < (gest_len + 1) / 2) { + gest_pt_log_coordinate(buf[GEST_PTLG_ID_LEN + 4 + i * 2], buf[GEST_PTLG_ID_LEN + 4 + i * 2 + 1]); + i++; + I("gest_pt_x[%d]=%d\n", gest_pt_cnt, gest_pt_x[gest_pt_cnt]); + I("gest_pt_y[%d]=%d\n", gest_pt_cnt, gest_pt_y[gest_pt_cnt]); + gest_pt_cnt += 1; + } + + if (gest_pt_cnt) { + for (i = 0; i < gest_pt_cnt; i++) { + if (tmp_max_x < gest_pt_x[i]) + tmp_max_x = gest_pt_x[i]; + if (tmp_min_x > gest_pt_x[i]) + tmp_min_x = gest_pt_x[i]; + if (tmp_max_y < gest_pt_y[i]) + tmp_max_y = gest_pt_y[i]; + if (tmp_min_y > gest_pt_y[i]) + tmp_min_y = gest_pt_y[i]; + } + + I("gest_point x_min= %d, x_max= %d, y_min= %d, y_max= %d\n", tmp_min_x, tmp_max_x, tmp_min_y, tmp_max_y); + gest_start_x = gest_pt_x[0]; + hx_gesture_coor[0] = gest_start_x; + gest_start_y = gest_pt_y[0]; + hx_gesture_coor[1] = gest_start_y; + gest_end_x = gest_pt_x[gest_pt_cnt - 1]; + hx_gesture_coor[2] = gest_end_x; + gest_end_y = gest_pt_y[gest_pt_cnt - 1]; + hx_gesture_coor[3] = gest_end_y; + gest_width = tmp_max_x - tmp_min_x; + hx_gesture_coor[4] = gest_width; + gest_height = tmp_max_y - tmp_min_y; + hx_gesture_coor[5] = gest_height; + gest_mid_x = (tmp_max_x + tmp_min_x) / 2; + hx_gesture_coor[6] = gest_mid_x; + gest_mid_y = (tmp_max_y + tmp_min_y) / 2; + hx_gesture_coor[7] = gest_mid_y; + hx_gesture_coor[8] = gest_mid_x;/*gest_up_x*/ + hx_gesture_coor[9] = gest_mid_y - gest_height / 2; /*gest_up_y*/ + hx_gesture_coor[10] = gest_mid_x;/*gest_down_x*/ + hx_gesture_coor[11] = gest_mid_y + gest_height / 2; /*gest_down_y*/ + hx_gesture_coor[12] = gest_mid_x - gest_width / 2; /*gest_left_x*/ + hx_gesture_coor[13] = gest_mid_y; /*gest_left_y*/ + hx_gesture_coor[14] = gest_mid_x + gest_width / 2; /*gest_right_x*/ + hx_gesture_coor[15] = gest_mid_y; /*gest_right_y*/ + } + } + +#endif + + if (!ts->gesture_cust_en[gesture_pos]) { + I("%s NOT report key [%d] = %d\n", __func__, gesture_pos, gest_key_def[gesture_pos]); + g_target_report_data->SMWP_event_chk = 0; + ret = 0; + } else { + g_target_report_data->SMWP_event_chk = gest_key_def[gesture_pos]; + ret = gesture_pos; + } +END: + return ret; +} + +static void himax_wake_event_report(void) +{ + int KEY_EVENT = g_target_report_data->SMWP_event_chk; +#ifdef HIMAX_V2_SENSOR_EN + static int report_cnt = 0; +#endif + if (g_ts_dbg != 0) + I("%s: Entering!\n", __func__); + + if (KEY_EVENT) { +#ifdef HIMAX_V2_SENSOR_EN + input_report_abs(private_ts->sensor_pdata->input_sensor_dev, ABS_DISTANCE, ++report_cnt); + I("input report: %d", report_cnt); + if (report_cnt >= REPORT_MAX_COUNT) + report_cnt = 0; + input_sync(private_ts->sensor_pdata->input_sensor_dev); +#else + I(" %s SMART WAKEUP KEY event %d press\n", __func__, KEY_EVENT); + input_report_key(private_ts->input_dev, KEY_EVENT, 1); + input_sync(private_ts->input_dev); + I(" %s SMART WAKEUP KEY event %d release\n", __func__, KEY_EVENT); + input_report_key(private_ts->input_dev, KEY_EVENT, 0); + input_sync(private_ts->input_dev); +#endif + FAKE_POWER_KEY_SEND = true; +#ifdef HX_GESTURE_TRACK + I("gest_start_x= %d, gest_start_y= %d, gest_end_x= %d, gest_end_y= %d\n", gest_start_x, gest_start_y, + gest_end_x, gest_end_y); + I("gest_width= %d, gest_height= %d, gest_mid_x= %d, gest_mid_y= %d\n", gest_width, gest_height, + gest_mid_x, gest_mid_y); + I("gest_up_x= %d, gest_up_y= %d, gest_down_x= %d, gest_down_y= %d\n", hx_gesture_coor[8], hx_gesture_coor[9], + hx_gesture_coor[10], hx_gesture_coor[11]); + I("gest_left_x= %d, gest_left_y= %d, gest_right_x= %d, gest_right_y= %d\n", hx_gesture_coor[12], hx_gesture_coor[13], + hx_gesture_coor[14], hx_gesture_coor[15]); +#endif + g_target_report_data->SMWP_event_chk = 0; + } +} + +#endif + +int himax_report_data_init(void) +{ + if (hx_touch_data->hx_coord_buf != NULL) { + kfree(hx_touch_data->hx_coord_buf); + hx_touch_data->hx_coord_buf = NULL; + } + + if (hx_touch_data->hx_rawdata_buf != NULL) { + kfree(hx_touch_data->hx_rawdata_buf); + hx_touch_data->hx_rawdata_buf = NULL; + } + +#if defined(HX_SMART_WAKEUP) + hx_touch_data->event_size = g_core_fp.fp_get_touch_data_size(); + + if (hx_touch_data->hx_event_buf != NULL) { + kfree(hx_touch_data->hx_event_buf); + hx_touch_data->hx_event_buf = NULL; + } + + if (wake_event_buffer != NULL) { + kfree(wake_event_buffer); + wake_event_buffer = NULL; + } + +#endif + hx_touch_data->touch_all_size = g_core_fp.fp_get_touch_data_size(); + hx_touch_data->raw_cnt_max = ic_data->HX_MAX_PT / 4; + hx_touch_data->raw_cnt_rmd = ic_data->HX_MAX_PT % 4; + /* more than 4 fingers */ + if (hx_touch_data->raw_cnt_rmd != 0x00) { + hx_touch_data->rawdata_size = g_core_fp.fp_cal_data_len(hx_touch_data->raw_cnt_rmd, ic_data->HX_MAX_PT, hx_touch_data->raw_cnt_max); + hx_touch_data->touch_info_size = (ic_data->HX_MAX_PT + hx_touch_data->raw_cnt_max + 2) * 4; + } else { /* less than 4 fingers */ + hx_touch_data->rawdata_size = g_core_fp.fp_cal_data_len(hx_touch_data->raw_cnt_rmd, ic_data->HX_MAX_PT, hx_touch_data->raw_cnt_max); + hx_touch_data->touch_info_size = (ic_data->HX_MAX_PT + hx_touch_data->raw_cnt_max + 1) * 4; + } +#if defined(HX_PEN_FUNC_EN) + hx_touch_data->touch_info_size += PEN_INFO_SZ; +#endif + if ((ic_data->HX_TX_NUM * ic_data->HX_RX_NUM + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM) % hx_touch_data->rawdata_size == 0) + hx_touch_data->rawdata_frame_size = (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM) / hx_touch_data->rawdata_size; + else + hx_touch_data->rawdata_frame_size = (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM) / hx_touch_data->rawdata_size + 1; + + I("%s: rawdata_frame_size = %d\n", __func__, hx_touch_data->rawdata_frame_size); + I("%s: ic_data->HX_MAX_PT:%d, hx_raw_cnt_max:%d, hx_raw_cnt_rmd:%d, g_hx_rawdata_size:%d, hx_touch_data->touch_info_size:%d\n", __func__, ic_data->HX_MAX_PT, hx_touch_data->raw_cnt_max, hx_touch_data->raw_cnt_rmd, hx_touch_data->rawdata_size, hx_touch_data->touch_info_size); + hx_touch_data->hx_coord_buf = kzalloc(sizeof(uint8_t) * (hx_touch_data->touch_info_size), GFP_KERNEL); + + if (hx_touch_data->hx_coord_buf == NULL) + goto mem_alloc_fail; + + if (g_target_report_data == NULL) { + g_target_report_data = kzalloc(sizeof(struct himax_target_report_data), GFP_KERNEL); + if (g_target_report_data == NULL) + goto mem_alloc_fail; + g_target_report_data->x = kzalloc(sizeof(int)*(ic_data->HX_MAX_PT), GFP_KERNEL); + if (g_target_report_data->x == NULL) + goto mem_alloc_fail; + g_target_report_data->y = kzalloc(sizeof(int)*(ic_data->HX_MAX_PT), GFP_KERNEL); + if (g_target_report_data->y == NULL) + goto mem_alloc_fail; + g_target_report_data->w = kzalloc(sizeof(int)*(ic_data->HX_MAX_PT), GFP_KERNEL); + if (g_target_report_data->w == NULL) + goto mem_alloc_fail; + g_target_report_data->finger_id = kzalloc(sizeof(int)*(ic_data->HX_MAX_PT), GFP_KERNEL); + if (g_target_report_data->finger_id == NULL) + goto mem_alloc_fail; +#if defined(HX_PEN_FUNC_EN) + g_target_report_data->p_x = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_x == NULL) + goto mem_alloc_fail; + g_target_report_data->p_y = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_y == NULL) + goto mem_alloc_fail; + g_target_report_data->p_w = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_w == NULL) + goto mem_alloc_fail; + g_target_report_data->pen_id = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->pen_id == NULL) + goto mem_alloc_fail; + g_target_report_data->p_hover = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_hover == NULL) + goto mem_alloc_fail; + g_target_report_data->p_tilt_x = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_tilt_x == NULL) + goto mem_alloc_fail; + g_target_report_data->p_btn = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_btn == NULL) + goto mem_alloc_fail; + g_target_report_data->p_btn2 = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_btn2 == NULL) + goto mem_alloc_fail; + g_target_report_data->p_tilt_y = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_tilt_y == NULL) + goto mem_alloc_fail; + g_target_report_data->p_on = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_on == NULL) + goto mem_alloc_fail; + +#endif + } +#ifdef HX_SMART_WAKEUP + g_target_report_data->SMWP_event_chk = 0; + wake_event_buffer = kcalloc(hx_touch_data->event_size, sizeof(uint8_t), GFP_KERNEL); + if (wake_event_buffer == NULL) + goto mem_alloc_fail; +#endif + + hx_touch_data->hx_rawdata_buf = kzalloc(sizeof(uint8_t) * (hx_touch_data->touch_all_size - hx_touch_data->touch_info_size), GFP_KERNEL); + + if (hx_touch_data->hx_rawdata_buf == NULL) + goto mem_alloc_fail; + +#if defined(HX_SMART_WAKEUP) + hx_touch_data->hx_event_buf = kzalloc(sizeof(uint8_t) * (hx_touch_data->event_size), GFP_KERNEL); + + if (hx_touch_data->hx_event_buf == NULL) + goto mem_alloc_fail; + +#endif + return NO_ERR; +mem_alloc_fail: +#if defined(HX_PEN_FUNC_EN) + kfree(g_target_report_data->p_on); + g_target_report_data->p_on = NULL; + kfree(g_target_report_data->p_tilt_y); + g_target_report_data->p_tilt_y = NULL; + kfree(g_target_report_data->p_btn2); + g_target_report_data->p_btn2 = NULL; + kfree(g_target_report_data->p_btn); + g_target_report_data->p_btn = NULL; + kfree(g_target_report_data->p_tilt_x); + g_target_report_data->p_tilt_x = NULL; + kfree(g_target_report_data->p_hover); + g_target_report_data->p_hover = NULL; + kfree(g_target_report_data->pen_id); + g_target_report_data->pen_id = NULL; + kfree(g_target_report_data->p_w); + g_target_report_data->p_w = NULL; + kfree(g_target_report_data->p_y); + g_target_report_data->p_y = NULL; + kfree(g_target_report_data->p_x); + g_target_report_data->p_x = NULL; +#endif + kfree(g_target_report_data->finger_id); + g_target_report_data->finger_id = NULL; + kfree(g_target_report_data->w); + g_target_report_data->w = NULL; + kfree(g_target_report_data->y); + g_target_report_data->y = NULL; + kfree(g_target_report_data->x); + g_target_report_data->x = NULL; + kfree(g_target_report_data); + g_target_report_data = NULL; + +#if defined(HX_SMART_WAKEUP) + kfree(wake_event_buffer); + wake_event_buffer = NULL; + kfree(hx_touch_data->hx_event_buf); + hx_touch_data->hx_event_buf = NULL; +#endif + kfree(hx_touch_data->hx_rawdata_buf); + hx_touch_data->hx_rawdata_buf = NULL; + kfree(hx_touch_data->hx_coord_buf); + hx_touch_data->hx_coord_buf = NULL; + + I("%s: Memory allocate fail!\n", __func__); + return MEM_ALLOC_FAIL; +} +EXPORT_SYMBOL(himax_report_data_init); + +void himax_report_data_deinit(void) +{ +#if defined(HX_PEN_FUNC_EN) + kfree(g_target_report_data->p_on); + g_target_report_data->p_on = NULL; + kfree(g_target_report_data->p_tilt_y); + g_target_report_data->p_tilt_y = NULL; + kfree(g_target_report_data->p_btn2); + g_target_report_data->p_btn2 = NULL; + kfree(g_target_report_data->p_btn); + g_target_report_data->p_btn = NULL; + kfree(g_target_report_data->p_tilt_x); + g_target_report_data->p_tilt_x = NULL; + kfree(g_target_report_data->p_hover); + g_target_report_data->p_hover = NULL; + kfree(g_target_report_data->pen_id); + g_target_report_data->pen_id = NULL; + kfree(g_target_report_data->p_w); + g_target_report_data->p_w = NULL; + kfree(g_target_report_data->p_y); + g_target_report_data->p_y = NULL; + kfree(g_target_report_data->p_x); + g_target_report_data->p_x = NULL; +#endif + kfree(g_target_report_data->finger_id); + g_target_report_data->finger_id = NULL; + kfree(g_target_report_data->w); + g_target_report_data->w = NULL; + kfree(g_target_report_data->y); + g_target_report_data->y = NULL; + kfree(g_target_report_data->x); + g_target_report_data->x = NULL; + kfree(g_target_report_data); + g_target_report_data = NULL; + +#if defined(HX_SMART_WAKEUP) + kfree(wake_event_buffer); + wake_event_buffer = NULL; + kfree(hx_touch_data->hx_event_buf); + hx_touch_data->hx_event_buf = NULL; +#endif + kfree(hx_touch_data->hx_rawdata_buf); + hx_touch_data->hx_rawdata_buf = NULL; + kfree(hx_touch_data->hx_coord_buf); + hx_touch_data->hx_coord_buf = NULL; +} + +/*start ts_work*/ +#if defined(HX_USB_DETECT_GLOBAL) +void himax_cable_detect_func(bool force_renew) +{ + struct himax_ts_data *ts; + + /*u32 connect_status = 0;*/ + uint8_t connect_status = 0; + + connect_status = USB_detect_flag;/* upmu_is_chr_det(); */ + ts = private_ts; + + /* I("Touch: cable status=%d, cable_config=%p, usb_connected=%d\n", connect_status, ts->cable_config, ts->usb_connected); */ + if (ts->cable_config) { + if ((connect_status != ts->usb_connected) || force_renew) { + if (connect_status) { + ts->cable_config[1] = 0x01; + ts->usb_connected = 0x01; + } else { + ts->cable_config[1] = 0x00; + ts->usb_connected = 0x00; + } + + g_core_fp.fp_usb_detect_set(ts->cable_config); + I("%s: Cable status change: 0x%2.2X\n", __func__, ts->usb_connected); + } + + /*else */ + /*I("%s: Cable status is the same as previous one, ignore.\n", __func__); */ + } +} +#endif + +static int himax_ts_work_status(struct himax_ts_data *ts) +{ + /* 1: normal, 2:SMWP */ + int result = HX_REPORT_COORD; + + hx_touch_data->diag_cmd = ts->diag_cmd; + if (hx_touch_data->diag_cmd) + result = HX_REPORT_COORD_RAWDATA; + +#ifdef HX_SMART_WAKEUP + if (atomic_read(&ts->suspend_mode) && (ts->SMWP_enable) && (!hx_touch_data->diag_cmd)) + result = HX_REPORT_SMWP_EVENT; +#endif + /* I("Now Status is %d\n", result); */ + return result; +} + +static int himax_touch_get(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + switch (ts_path) { + /*normal*/ + case HX_REPORT_COORD: + if ((HX_HW_RESET_ACTIVATE) +#ifdef HX_ESD_RECOVERY + || (HX_ESD_RESET_ACTIVATE) +#endif + ) { + if (!g_core_fp.fp_read_event_stack(buf, 128)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + } + } else { + if (!g_core_fp.fp_read_event_stack(buf, hx_touch_data->touch_info_size)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + } + } + break; +#if defined(HX_SMART_WAKEUP) + + /*SMWP*/ + case HX_REPORT_SMWP_EVENT: + __pm_wakeup_event(&ts->ts_SMWP_wake_lock, TS_WAKE_LOCK_TIMEOUT); + msleep(20); + g_core_fp.fp_burst_enable(0); + + if (!g_core_fp.fp_read_event_stack(buf, hx_touch_data->event_size)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + } + break; +#endif + case HX_REPORT_COORD_RAWDATA: + if (!g_core_fp.fp_read_event_stack(buf, 128)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + } + break; + default: + break; + } + + return ts_status; +} + +/* start error_control*/ +static int himax_checksum_cal(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ + uint16_t check_sum_cal = 0; + int32_t i = 0; + int length = 0; + int zero_cnt = 0; + int raw_data_sel = 0; + int ret_val = ts_status; + + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + /* Normal */ + switch (ts_path) { + case HX_REPORT_COORD: + length = hx_touch_data->touch_info_size; + break; +#if defined(HX_SMART_WAKEUP) +/* SMWP */ + case HX_REPORT_SMWP_EVENT: + length = (GEST_PTLG_ID_LEN + GEST_PTLG_HDR_LEN); + break; +#endif + case HX_REPORT_COORD_RAWDATA: + length = hx_touch_data->touch_info_size; + break; + default: + I("%s, Neither Normal Nor SMWP error!\n", __func__); + ret_val = HX_PATH_FAIL; + goto END_FUNCTION; + } + + for (i = 0; i < length; i++) { + check_sum_cal += buf[i]; + if (buf[i] == 0x00) + zero_cnt++; + } + + if (check_sum_cal % 0x100 != 0) { + I("[HIMAX TP MSG] point data_checksum not match : check_sum_cal: 0x%02X\n", check_sum_cal); + ret_val = HX_CHKSUM_FAIL; + } else if (zero_cnt == length) { + if (ts->use_irq) + I("[HIMAX TP MSG] All Zero event\n"); + + ret_val = HX_CHKSUM_FAIL; + } else { + raw_data_sel = buf[HX_TOUCH_INFO_POINT_CNT]>>4 & 0x0F; + /*I("%s:raw_out_sel=%x , hx_touch_data->diag_cmd=%x.\n", __func__, raw_data_sel, hx_touch_data->diag_cmd);*/ + if ((raw_data_sel != 0x0F) && (raw_data_sel != hx_touch_data->diag_cmd)) {/*raw data out not match skip it*/ + /*I("%s:raw data out not match.\n", __func__);*/ + if (!hx_touch_data->diag_cmd) { + g_core_fp.fp_read_event_stack(buf, (128-hx_touch_data->touch_info_size));/*Need to clear event stack here*/ + /*I("%s: size =%d, buf[0]=%x ,buf[1]=%x, buf[2]=%x, buf[3]=%x.\n", __func__,(128-hx_touch_data->touch_info_size), buf[0], buf[1], buf[2], buf[3]);*/ + /*I("%s:also clear event stack.\n", __func__);*/ + } + ret_val = HX_READY_SERVE; + } + } + +END_FUNCTION: + if (g_ts_dbg != 0) + I("%s: END, ret_val=%d!\n", __func__, ret_val); + return ret_val; +} + +#ifdef HX_ESD_RECOVERY +#ifdef HX_ZERO_FLASH +void hx_update_dirly_0f(void) +{ + I("It will update fw after esd event in zero flash mode!\n"); + g_core_fp.fp_0f_operation_dirly(); +} +#endif +static int himax_ts_event_check(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ + int hx_EB_event = 0; + int hx_EC_event = 0; + int hx_ED_event = 0; + int hx_esd_event = 0; + int hx_zero_event = 0; + int shaking_ret = 0; + + int32_t loop_i = 0; + int length = 0; + int ret_val = ts_status; + + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + /* Normal */ + switch (ts_path) { + case HX_REPORT_COORD: + length = hx_touch_data->touch_info_size; + break; +#if defined(HX_SMART_WAKEUP) +/* SMWP */ + case HX_REPORT_SMWP_EVENT: + length = (GEST_PTLG_ID_LEN + GEST_PTLG_HDR_LEN); + break; +#endif + case HX_REPORT_COORD_RAWDATA: + length = hx_touch_data->touch_info_size; + break; + default: + I("%s, Neither Normal Nor SMWP error!\n", __func__); + ret_val = HX_PATH_FAIL; + goto END_FUNCTION; + } + + if (g_ts_dbg != 0) + I("Now Path=%d, Now status=%d, length=%d\n", ts_path, ts_status, length); + + for (loop_i = 0; loop_i < length; loop_i++) { + if (ts_path == HX_REPORT_COORD || ts_path == HX_REPORT_COORD_RAWDATA) { + /* case 1 ESD recovery flow */ + if (buf[loop_i] == 0xEB) { + hx_EB_event++; + } else if (buf[loop_i] == 0xEC) { + hx_EC_event++; + } else if (buf[loop_i] == 0xED) { + hx_ED_event++; + } else if (buf[loop_i] == 0x00) { /* case 2 ESD recovery flow-Disable */ + hx_zero_event++; + } else { + hx_EB_event = 0; + hx_EC_event = 0; + hx_ED_event = 0; + hx_zero_event = 0; + g_zero_event_count = 0; + } + } + } + + if (hx_EB_event == length) { + hx_esd_event = length; + hx_EB_event_flag++; + I("[HIMAX TP MSG]: ESD event checked - ALL 0xEB.\n"); + } else if (hx_EC_event == length) { + hx_esd_event = length; + hx_EC_event_flag++; + I("[HIMAX TP MSG]: ESD event checked - ALL 0xEC.\n"); + } else if (hx_ED_event == length) { + hx_esd_event = length; + hx_ED_event_flag++; + I("[HIMAX TP MSG]: ESD event checked - ALL 0xED.\n"); + } +/*#ifdef HX_ZERO_FLASH + * //This is for previous version(a, b) because HW pull TSIX low continuely after watchdog timeout reset + * else if (hx_zero_event == length) { + * //check zero flash status + * if (g_core_fp.fp_0f_esd_check() < 0) { + * g_zero_event_count = 6; + * I("[HIMAX TP MSG]: ESD event checked - ALL Zero in ZF.\n"); + * } else { + * I("[HIMAX TP MSG]: Status check pass in ZF.\n"); + * } + * } + *#endif + */ + else + hx_esd_event = 0; + + if ((hx_esd_event == length || hx_zero_event == length) + && (HX_HW_RESET_ACTIVATE == 0) + && (HX_ESD_RESET_ACTIVATE == 0) + && (hx_touch_data->diag_cmd == 0) + && (ts->in_self_test == 0)) { + shaking_ret = g_core_fp.fp_ic_esd_recovery(hx_esd_event, hx_zero_event, length); + + if (shaking_ret == HX_ESD_EVENT) { + himax_esd_hw_reset(); + ret_val = HX_ESD_EVENT; + } else if (shaking_ret == HX_ZERO_EVENT_COUNT) { + ret_val = HX_ZERO_EVENT_COUNT; + } else { + I("I2C running. Nothing to be done!\n"); + ret_val = HX_IC_RUNNING; + } + } else if (HX_ESD_RESET_ACTIVATE) { /* drop 1st interrupts after chip reset */ + HX_ESD_RESET_ACTIVATE = 0; + I("[HX_ESD_RESET_ACTIVATE]:%s: Back from reset, ready to serve.\n", __func__); + ret_val = HX_ESD_REC_OK; + } + +END_FUNCTION: + if (g_ts_dbg != 0) + I("%s: END, ret_val=%d!\n", __func__, ret_val); + + return ret_val; +} +#endif + +static int himax_err_ctrl(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ +#ifdef HX_RST_PIN_FUNC + if (HX_HW_RESET_ACTIVATE) { + /* drop 1st interrupts after chip reset */ + HX_HW_RESET_ACTIVATE = 0; + I("[HX_HW_RESET_ACTIVATE]:%s: Back from reset, ready to serve.\n", __func__); + ts_status = HX_RST_OK; + goto END_FUNCTION; + } +#endif + + ts_status = himax_checksum_cal(ts, buf, ts_path, ts_status); + if (ts_status == HX_CHKSUM_FAIL) { + goto CHK_FAIL; + } else { +#ifdef HX_ESD_RECOVERY + /* continuous N times record, not total N times. */ + g_zero_event_count = 0; +#endif + goto END_FUNCTION; + } + +CHK_FAIL: +#ifdef HX_ESD_RECOVERY + ts_status = himax_ts_event_check(ts, buf, ts_path, ts_status); +#endif + + +END_FUNCTION: + if (g_ts_dbg != 0) + I("%s: END, ts_status=%d!\n", __func__, ts_status); + return ts_status; +} +/* end error_control*/ + +/* start distribute_data*/ +static int himax_distribute_touch_data(uint8_t *buf, int ts_path, int ts_status) +{ + uint8_t hx_state_info_pos = hx_touch_data->touch_info_size - 3; + +#if defined(HX_PEN_FUNC_EN) + hx_state_info_pos -= PEN_INFO_SZ; +#endif + + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + if (ts_path == HX_REPORT_COORD) { + memcpy(hx_touch_data->hx_coord_buf, &buf[0], hx_touch_data->touch_info_size); + + if (buf[hx_state_info_pos] != 0xFF && buf[hx_state_info_pos + 1] != 0xFF) + memcpy(hx_touch_data->hx_state_info, &buf[hx_state_info_pos], 2); + else + memset(hx_touch_data->hx_state_info, 0x00, sizeof(hx_touch_data->hx_state_info)); + + if ((HX_HW_RESET_ACTIVATE) +#ifdef HX_ESD_RECOVERY + || (HX_ESD_RESET_ACTIVATE) +#endif + ) { + memcpy(hx_touch_data->hx_rawdata_buf, &buf[hx_touch_data->touch_info_size], hx_touch_data->touch_all_size - hx_touch_data->touch_info_size); + } + } else if (ts_path == HX_REPORT_COORD_RAWDATA) { + memcpy(hx_touch_data->hx_coord_buf, &buf[0], hx_touch_data->touch_info_size); + + if (buf[hx_state_info_pos] != 0xFF && buf[hx_state_info_pos + 1] != 0xFF) + memcpy(hx_touch_data->hx_state_info, &buf[hx_state_info_pos], 2); + else + memset(hx_touch_data->hx_state_info, 0x00, sizeof(hx_touch_data->hx_state_info)); + + memcpy(hx_touch_data->hx_rawdata_buf, &buf[hx_touch_data->touch_info_size], hx_touch_data->touch_all_size - hx_touch_data->touch_info_size); +#if defined(HX_SMART_WAKEUP) + } else if (ts_path == HX_REPORT_SMWP_EVENT) { + memcpy(hx_touch_data->hx_event_buf, buf, hx_touch_data->event_size); +#endif + } else { + E("%s, Fail Path!\n", __func__); + ts_status = HX_PATH_FAIL; + } + + if (g_ts_dbg != 0) + I("%s: End, ts_status=%d!\n", __func__, ts_status); + return ts_status; +} +/* end assign_data*/ + +/* start parse_report_data*/ +int himax_parse_report_points(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + int x = 0, y = 0, w = 0; +#if defined(HX_PEN_FUNC_EN) + uint8_t p_hover = 0, p_btn = 0, p_btn2 = 0; + int8_t p_tilt_x = 0, p_tilt_y = 0; + int p_x = 0, p_y = 0, p_w = 0; + static uint8_t p_p_on; +#endif + int base = 0; + int32_t loop_i = 0; + + if (g_ts_dbg != 0) + I("%s: start!\n", __func__); + +#if defined(HX_PEN_FUNC_EN) + p_p_on = 0; + base = hx_touch_data->touch_info_size - PEN_INFO_SZ; + + p_x = hx_touch_data->hx_coord_buf[base] << 8 | hx_touch_data->hx_coord_buf[base + 1]; + p_y = (hx_touch_data->hx_coord_buf[base + 2] << 8 | hx_touch_data->hx_coord_buf[base + 3]); + p_w = (hx_touch_data->hx_coord_buf[base + 4] << 8 | hx_touch_data->hx_coord_buf[base + 5]); + p_tilt_x = (int8_t)hx_touch_data->hx_coord_buf[base + 6]; + p_hover = hx_touch_data->hx_coord_buf[base + 7]; + p_btn = hx_touch_data->hx_coord_buf[base + 8]; + p_btn2 = hx_touch_data->hx_coord_buf[base + 9]; + p_tilt_y = (int8_t)hx_touch_data->hx_coord_buf[base + 10]; + + if (g_ts_dbg != 0) { + D("%s: p_x=%d, p_y=%d, p_w=%d\n", __func__, p_x, p_y, p_w); + D("%s: p_tilt_x=%d, p_hover=%d\n", __func__, p_tilt_x, p_hover); + D("%s: p_btn=%d, p_btn2=%d\n", __func__, p_btn, p_btn2); + D("%s: p_tilt_y=%d\n", __func__, p_tilt_y); + } + + if (p_x >= 0 && p_x <= ts->pdata->abs_x_max && p_y >= 0 && p_y <= ts->pdata->abs_y_max) { + g_target_report_data->p_x[0] = p_x; + g_target_report_data->p_y[0] = p_y; + g_target_report_data->p_w[0] = p_w; + g_target_report_data->p_hover[0] = p_hover; + g_target_report_data->pen_id[0] = 1; + g_target_report_data->p_btn[0] = p_btn; + g_target_report_data->p_btn2[0] = p_btn2; + g_target_report_data->p_tilt_x[0] = p_tilt_x; + g_target_report_data->p_tilt_y[0] = p_tilt_y; + g_target_report_data->p_on[0] = 1; + ts->hx_point_num++; + } else {/* report coordinates */ + g_target_report_data->p_x[0] = 0; + g_target_report_data->p_y[0] = 0; + g_target_report_data->p_w[0] = 0; + g_target_report_data->p_hover[0] = 0; + g_target_report_data->pen_id[0] = 0; + g_target_report_data->p_btn[0] = 0; + g_target_report_data->p_btn2[0] = 0; + g_target_report_data->p_tilt_x[0] = 0; + g_target_report_data->p_tilt_y[0] = 0; + g_target_report_data->p_on[0] = 0; + } + + if (g_ts_dbg != 0) { + if (p_p_on != g_target_report_data->p_on[0]) { + I("p_on[0] = %d, hx_point_num=%d\n", + g_target_report_data->p_on[0], + ts->hx_point_num); + p_p_on = g_target_report_data->p_on[0]; + } + } +#endif + + ts->old_finger = ts->pre_finger_mask; + if (ts->hx_point_num == 0) { + if (g_ts_dbg != 0) + I("%s: hx_point_num = 0!\n", __func__); + return ts_status; + } + ts->pre_finger_mask = 0; + hx_touch_data->finger_num = hx_touch_data->hx_coord_buf[ts->coordInfoSize - 4] & 0x0F; + hx_touch_data->finger_on = 1; + AA_press = 1; + + g_target_report_data->finger_num = hx_touch_data->finger_num; + g_target_report_data->finger_on = hx_touch_data->finger_on; + g_target_report_data->ig_count = hx_touch_data->hx_coord_buf[ts->coordInfoSize - 5]; + + if (g_ts_dbg != 0) + I("%s:finger_num = 0x%2X, finger_on = %d\n", __func__, g_target_report_data->finger_num, g_target_report_data->finger_on); + + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + base = loop_i * 4; + x = hx_touch_data->hx_coord_buf[base] << 8 | hx_touch_data->hx_coord_buf[base + 1]; + y = (hx_touch_data->hx_coord_buf[base + 2] << 8 | hx_touch_data->hx_coord_buf[base + 3]); + w = hx_touch_data->hx_coord_buf[(ts->nFinger_support * 4) + loop_i]; + + if (g_ts_dbg != 0) + D("%s: now parsing[%d]:x=%d, y=%d, w=%d\n", __func__, loop_i, x, y, w); + + if (x >= 0 && x <= ts->pdata->abs_x_max && y >= 0 && y <= ts->pdata->abs_y_max) { + hx_touch_data->finger_num--; + + g_target_report_data->x[loop_i] = x; + g_target_report_data->y[loop_i] = y; + g_target_report_data->w[loop_i] = w; + g_target_report_data->finger_id[loop_i] = 1; + + /*I("%s: g_target_report_data->x[loop_i]=%d, g_target_report_data->y[loop_i]=%d, g_target_report_data->w[loop_i]=%d",*/ + /*__func__, g_target_report_data->x[loop_i], g_target_report_data->y[loop_i], g_target_report_data->w[loop_i]); */ + + + if (!ts->first_pressed) { + ts->first_pressed = 1; + I("S1@%d, %d\n", x, y); + } + + ts->pre_finger_data[loop_i][0] = x; + ts->pre_finger_data[loop_i][1] = y; + + ts->pre_finger_mask = ts->pre_finger_mask + (1 << loop_i); + } else {/* report coordinates */ + g_target_report_data->x[loop_i] = x; + g_target_report_data->y[loop_i] = y; + g_target_report_data->w[loop_i] = w; + g_target_report_data->finger_id[loop_i] = 0; + + if (loop_i == 0 && ts->first_pressed == 1) { + ts->first_pressed = 2; + I("E1@%d, %d\n", ts->pre_finger_data[0][0], ts->pre_finger_data[0][1]); + } + } + } + + if (g_ts_dbg != 0) { + for (loop_i = 0; loop_i < 10; loop_i++) + D("DBG X=%d Y=%d ID=%d\n", g_target_report_data->x[loop_i], g_target_report_data->y[loop_i], g_target_report_data->finger_id[loop_i]); + + D("DBG finger number %d\n", g_target_report_data->finger_num); + } + + if (g_ts_dbg != 0) + I("%s: end!\n", __func__); + return ts_status; +} + +static int himax_parse_report_data(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + + if (g_ts_dbg != 0) + I("%s: start now_status=%d!\n", __func__, ts_status); + + + EN_NoiseFilter = (hx_touch_data->hx_coord_buf[HX_TOUCH_INFO_POINT_CNT + 2] >> 3); + /* I("EN_NoiseFilter=%d\n", EN_NoiseFilter); */ + EN_NoiseFilter = EN_NoiseFilter & 0x01; + /* I("EN_NoiseFilter2=%d\n", EN_NoiseFilter); */ +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) + tpd_key = (hx_touch_data->hx_coord_buf[HX_TOUCH_INFO_POINT_CNT + 2] >> 4); + + /* All (VK+AA)leave */ + if (tpd_key == 0x0F) + tpd_key = 0x00; + +#endif + p_point_num = ts->hx_point_num; + + if (hx_touch_data->hx_coord_buf[HX_TOUCH_INFO_POINT_CNT] == 0xff) + ts->hx_point_num = 0; + else + ts->hx_point_num = hx_touch_data->hx_coord_buf[HX_TOUCH_INFO_POINT_CNT] & 0x0f; + + + switch (ts_path) { + case HX_REPORT_COORD: + ts_status = himax_parse_report_points(ts, ts_path, ts_status); + break; + case HX_REPORT_COORD_RAWDATA: + /* touch monitor rawdata */ + if (debug_data != NULL) { + if (debug_data->fp_set_diag_cmd(ic_data, hx_touch_data)) + I("%s: raw data_checksum not match\n", __func__); + } else { + E("%s,There is no init set_diag_cmd\n", __func__); + } + ts_status = himax_parse_report_points(ts, ts_path, ts_status); + break; +#ifdef HX_SMART_WAKEUP + case HX_REPORT_SMWP_EVENT: + himax_wake_event_parse(ts, ts_status); + break; +#endif + default: + E("%s:Fail Path!\n", __func__); + ts_status = HX_PATH_FAIL; + break; + } + if (g_ts_dbg != 0) + I("%s: end now_status=%d!\n", __func__, ts_status); + return ts_status; +} + +/* end parse_report_data*/ + +static void himax_report_all_leave_event(struct himax_ts_data *ts) +{ + int loop_i = 0; + + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { +#ifndef HX_PROTOCOL_A + input_mt_slot(ts->input_dev, loop_i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_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); + input_sync(ts->input_dev); +} + +/* start report_data */ +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) +static void himax_key_report_operation(int tp_key_index, struct himax_ts_data *ts) +{ + uint16_t x_position = 0, y_position = 0; + + if (g_ts_dbg != 0) + I("%s: Entering\n", __func__); + + if (tp_key_index != 0x00) { + I("virtual key index =%x\n", tp_key_index); + + if (tp_key_index == 0x01) { + vk_press = 1; + I("back key pressed\n"); + + if (ts->pdata->virtual_key) { + if (ts->button[0].index) { + x_position = (ts->button[0].x_range_min + ts->button[0].x_range_max) / 2; + y_position = (ts->button[0].y_range_min + ts->button[0].y_range_max) / 2; + } + +#ifdef HX_PROTOCOL_A + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 0); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); + input_mt_sync(ts->input_dev); +#else + input_mt_slot(ts->input_dev, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); +#endif + } else { + input_report_key(ts->input_dev, KEY_BACK, 1); + } + } else if (tp_key_index == 0x02) { + vk_press = 1; + I("home key pressed\n"); + + if (ts->pdata->virtual_key) { + if (ts->button[1].index) { + x_position = (ts->button[1].x_range_min + ts->button[1].x_range_max) / 2; + y_position = (ts->button[1].y_range_min + ts->button[1].y_range_max) / 2; + } + +#ifdef HX_PROTOCOL_A + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 0); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); + input_mt_sync(ts->input_dev); +#else + input_mt_slot(ts->input_dev, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); +#endif + } else { + input_report_key(ts->input_dev, KEY_HOME, 1); + } + } else if (tp_key_index == 0x04) { + vk_press = 1; + I("APP_switch key pressed\n"); + + if (ts->pdata->virtual_key) { + if (ts->button[2].index) { + x_position = (ts->button[2].x_range_min + ts->button[2].x_range_max) / 2; + y_position = (ts->button[2].y_range_min + ts->button[2].y_range_max) / 2; + } + +#ifdef HX_PROTOCOL_A + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); + input_mt_sync(ts->input_dev); +#else + input_mt_slot(ts->input_dev, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); +#endif + } else { + input_report_key(ts->input_dev, KEY_APPSELECT, 1); + } + } + input_sync(ts->input_dev); + } else { /*tp_key_index =0x00*/ + I("virtual key released\n"); + vk_press = 0; +#ifndef HX_PROTOCOL_A + input_mt_slot(ts->input_dev, 0); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); +#else + input_mt_sync(ts->input_dev); +#endif + input_report_key(ts->input_dev, KEY_BACK, 0); + input_report_key(ts->input_dev, KEY_HOME, 0); + input_report_key(ts->input_dev, KEY_APPSELECT, 0); +#ifndef HX_PROTOCOL_A + input_sync(ts->input_dev); +#endif + } +} + +void himax_finger_report_key(struct himax_ts_data *ts) +{ + if (ts->hx_point_num != 0) { + /*Touch KEY*/ + if ((tpd_key_old != 0x00) && (tpd_key == 0x00)) { + /* temp_x[0] = 0xFFFF; + * temp_y[0] = 0xFFFF; + * temp_x[1] = 0xFFFF; + * temp_y[1] = 0xFFFF; + */ + hx_touch_data->finger_on = 0; +#ifdef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 0); +#endif + himax_key_report_operation(tpd_key, ts); +#ifndef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 0); +#endif + } + input_sync(ts->input_dev); + } +} + +void himax_finger_leave_key(struct himax_ts_data *ts) +{ + if (tpd_key != 0x00) { + hx_touch_data->finger_on = 1; +#ifdef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif + himax_key_report_operation(tpd_key, ts); +#ifndef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif + } else if ((tpd_key_old != 0x00) && (tpd_key == 0x00)) { + hx_touch_data->finger_on = 0; +#ifdef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 0); +#endif + himax_key_report_operation(tpd_key, ts); +#ifndef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 0); +#endif + } + input_sync(ts->input_dev); +} + +static void himax_report_key(struct himax_ts_data *ts) +{ + if (ts->hx_point_num != 0) { /* Touch KEY */ + himax_finger_report_key(ts); + } else { /* Key */ + himax_finger_leave_key(ts); + } + + tpd_key_old = tpd_key; + Last_EN_NoiseFilter = EN_NoiseFilter; +} +#endif + +/* start report_point*/ +static void himax_finger_report(struct himax_ts_data *ts) +{ + int i = 0; + bool valid = false; + + + if (g_ts_dbg != 0) { + I("%s:start\n", __func__); + I("hx_touch_data->finger_num=%d\n", hx_touch_data->finger_num); + } + for (i = 0; i < ts->nFinger_support; i++) { + if (g_target_report_data->x[i] >= 0 && g_target_report_data->x[i] <= ts->pdata->abs_x_max && g_target_report_data->y[i] >= 0 && g_target_report_data->y[i] <= ts->pdata->abs_y_max) + valid = true; + else + valid = false; + if (g_ts_dbg != 0) + I("valid=%d\n", valid); + if (valid) { + if (g_ts_dbg != 0) + I("g_target_report_data->x[i]=%d, g_target_report_data->y[i]=%d, g_target_report_data->w[i]=%d\n", g_target_report_data->x[i], g_target_report_data->y[i], g_target_report_data->w[i]); +#ifndef HX_PROTOCOL_A + input_mt_slot(ts->input_dev, i); +#else + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, g_target_report_data->w[i]); +#ifndef HX_PROTOCOL_A + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, g_target_report_data->w[i]); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, g_target_report_data->w[i]); +#else + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, i); +#endif + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, g_target_report_data->x[i]); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, g_target_report_data->y[i]); +#ifndef HX_PROTOCOL_A + ts->last_slot = i; + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); +#else + input_mt_sync(ts->input_dev); +#endif + } else { +#ifndef HX_PROTOCOL_A + 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_WIDTH_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 + } + } +#ifndef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif + input_sync(ts->input_dev); + +#if defined(HX_PEN_FUNC_EN) + valid = false; + + if (g_target_report_data->p_x[0] >= 0 && g_target_report_data->p_x[0] <= ts->pdata->abs_x_max + && g_target_report_data->p_y[0] >= 0 && g_target_report_data->p_y[0] <= ts->pdata->abs_y_max + && (g_target_report_data->p_on[0] == 1)) + valid = true; + else + valid = false; + if (g_ts_dbg != 0) + I("pen valid=%d\n", valid); + if (valid) {/*Pen down*/ + if (g_ts_dbg != 0) + I("p_x[i]=%d, p_y[i]=%d, p_w[i]=%d\n", g_target_report_data->p_x[0], g_target_report_data->p_y[0], g_target_report_data->p_w[0]); + + input_report_abs(ts->hx_pen_dev, ABS_X, g_target_report_data->p_x[0]); + input_report_abs(ts->hx_pen_dev, ABS_Y, g_target_report_data->p_y[0]); + input_report_key(ts->hx_pen_dev, BTN_STYLUS, 1); + input_report_abs(ts->hx_pen_dev, ABS_TILT_X, g_target_report_data->p_tilt_x[0]); + input_report_abs(ts->hx_pen_dev, ABS_TILT_Y, g_target_report_data->p_tilt_y[0]); + input_report_key(ts->hx_pen_dev, BTN_TOOL_PEN, 1); + if (g_target_report_data->p_hover[0] == 0) { + input_report_key(ts->hx_pen_dev, BTN_TOUCH, 1); + input_report_abs(ts->hx_pen_dev, ABS_DISTANCE, 0); + input_report_abs(ts->hx_pen_dev, ABS_PRESSURE, g_target_report_data->p_w[0]); + } else { + input_report_key(ts->hx_pen_dev, BTN_TOUCH, 0); + input_report_abs(ts->hx_pen_dev, ABS_DISTANCE, 1); + input_report_abs(ts->hx_pen_dev, ABS_PRESSURE, 0); + } + } else {/*Pen up*/ + input_report_key(ts->hx_pen_dev, BTN_STYLUS, 0); + input_report_key(ts->hx_pen_dev, BTN_TOUCH, 0); + input_report_abs(ts->hx_pen_dev, ABS_PRESSURE, 0); + input_sync(ts->hx_pen_dev); + + input_report_abs(ts->hx_pen_dev, ABS_DISTANCE, 0); + input_report_key(ts->hx_pen_dev, BTN_TOOL_RUBBER, 0); + input_report_key(ts->hx_pen_dev, BTN_TOOL_PEN, 0); + input_report_abs(ts->hx_pen_dev, ABS_PRESSURE, 0); + } + input_sync(ts->hx_pen_dev); + +#endif + + if (g_ts_dbg != 0) + I("%s:end\n", __func__); +} + + +#ifdef HIMAX_PALM_SENSOR_EN +static void himax_palm_detect_func(struct himax_ts_data *ts, bool detected) { + if (detected) { + I("palm detected\n"); + input_report_abs(ts->palm_sensor_pdata->input_sensor_dev, ABS_DISTANCE, 1); + } else { + I("palm leave\n"); + input_report_abs(ts->palm_sensor_pdata->input_sensor_dev, ABS_DISTANCE, 0); + } + input_sync(ts->palm_sensor_pdata->input_sensor_dev); +} +#endif + +static void himax_finger_leave(struct himax_ts_data *ts) +{ +#ifndef HX_PROTOCOL_A + int32_t loop_i = 0; +#endif + + if (g_ts_dbg != 0) + I("%s: start!\n", __func__); + +#ifndef HIMAX_PALM_SENSOR_EN +#if defined(HX_PALM_REPORT) + if (himax_palm_detect(hx_touch_data->hx_coord_buf) == PALM_REPORT) { + I(" %s HX_PALM_REPORT KEY power event press\n", __func__); + input_report_key(ts->input_dev, KEY_POWER, 1); + input_sync(ts->input_dev); + msleep(100); + + I(" %s HX_PALM_REPORT KEY power event release\n", __func__); + input_report_key(ts->input_dev, KEY_POWER, 0); + input_sync(ts->input_dev); + return; + } +#endif +#endif + + hx_touch_data->finger_on = 0; + g_target_report_data->finger_on = 0; + g_target_report_data->finger_num = 0; + AA_press = 0; + +#ifdef HX_PROTOCOL_A + input_mt_sync(ts->input_dev); +#endif +#ifndef HX_PROTOCOL_A + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + input_mt_slot(ts->input_dev, loop_i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_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 + if (ts->pre_finger_mask > 0) + ts->pre_finger_mask = 0; + + if (ts->first_pressed == 1) { + ts->first_pressed = 2; + I("E1@%d, %d\n", ts->pre_finger_data[0][0], ts->pre_finger_data[0][1]); + } + + /*if (ts->debug_log_level & BIT(1)) */ + /*himax_log_touch_event(x, y, w, loop_i, EN_NoiseFilter, HX_FINGER_LEAVE); */ + + input_report_key(ts->input_dev, BTN_TOUCH, 0); + input_sync(ts->input_dev); + +#if defined(HX_PEN_FUNC_EN) + input_report_key(ts->hx_pen_dev, BTN_STYLUS, 0); + input_report_key(ts->hx_pen_dev, BTN_TOUCH, 0); + input_report_abs(ts->hx_pen_dev, ABS_PRESSURE, 0); + input_sync(ts->hx_pen_dev); + + input_report_abs(ts->hx_pen_dev, ABS_DISTANCE, 0); + input_report_abs(ts->hx_pen_dev, ABS_TILT_X, 0); + input_report_abs(ts->hx_pen_dev, ABS_TILT_Y, 0); + input_report_key(ts->hx_pen_dev, BTN_TOOL_RUBBER, 0); + input_report_key(ts->hx_pen_dev, BTN_TOOL_PEN, 0); + input_sync(ts->hx_pen_dev); +#endif + + if (g_ts_dbg != 0) + I("%s: end!\n", __func__); + + +} + +static void himax_report_points(struct himax_ts_data *ts) +{ + if (g_ts_dbg != 0) + I("%s: start!\n", __func__); + +#if defined(HX_PALM_REPORT) && defined(HIMAX_PALM_SENSOR_EN) + if (ts->palm_detection_enabled) { + if (himax_palm_detect(hx_touch_data->hx_coord_buf) == PALM_REPORT) { + del_timer(&ts->palm_release_fimer); + himax_palm_detect_func(ts, true); + return; + } else if (himax_palm_detect(hx_touch_data->hx_coord_buf) == PALM_LEAVE_REPORT) { + mod_timer(&ts->palm_release_fimer, + jiffies + msecs_to_jiffies(ts->palm_release_delay_ms)); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&ts->palm_gesture_wakelock, + ts->palm_release_delay_ms); +#else + __pm_wakeup_event(&ts->palm_gesture_wakelock, + ts->palm_release_delay_ms); +#endif + return; + } else if (ts->suspended) + return; + } +#endif + if (ts->hx_point_num != 0) + himax_finger_report(ts); + else + himax_finger_leave(ts); + + Last_EN_NoiseFilter = EN_NoiseFilter; + + if (g_ts_dbg != 0) + I("%s: end!\n", __func__); +} +/* end report_points*/ + +int himax_report_data(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + if (ts_path == HX_REPORT_COORD || ts_path == HX_REPORT_COORD_RAWDATA) { + /* Touch Point information */ + himax_report_points(ts); + +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) + /*report key(question mark)*/ + if (tpd_key && tpd_key_old) + himax_report_key(ts); + +#endif +#ifdef HX_SMART_WAKEUP + } else if (ts_path == HX_REPORT_SMWP_EVENT) { + himax_wake_event_report(); +#endif + } else { + E("%s:Fail Path!\n", __func__); + ts_status = HX_PATH_FAIL; + } + + if (g_ts_dbg != 0) + I("%s: END, ts_status=%d!\n", __func__, ts_status); + return ts_status; +} +/* end report_data */ + +static int himax_ts_operation(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + uint8_t hw_reset_check[2]; + + memset(ts->xfer_buff, 0x00, 128 * sizeof(uint8_t)); + memset(hw_reset_check, 0x00, sizeof(hw_reset_check)); + + ts_status = himax_touch_get(ts, ts->xfer_buff, ts_path, ts_status); + if (ts_status == HX_TS_GET_DATA_FAIL) + goto END_FUNCTION; + + ts_status = himax_distribute_touch_data(ts->xfer_buff, ts_path, ts_status); + ts_status = himax_err_ctrl(ts, ts->xfer_buff, ts_path, ts_status); + if (ts_status == HX_REPORT_DATA || ts_status == HX_TS_NORMAL_END) + ts_status = himax_parse_report_data(ts, ts_path, ts_status); + else + goto END_FUNCTION; + + + ts_status = himax_report_data(ts, ts_path, ts_status); + + +END_FUNCTION: + return ts_status; +} + +#ifdef HIMAX_PALM_SENSOR_EN +static void himax_palm_sensor_release_timer_handler(unsigned long data) +{ + himax_palm_detect_func(private_ts, false); +} + +static int _himax_palm_detect_sensor_set_enable(unsigned int enable) +{ + I("Gesture set enable %d!", enable); +/* + * If palm detect function is enabled, interrupt will not disable, IC works in + * normal mode. But in case touch event is reported to input subsystem, skip + * touch event when suspend flag is true. So input subsystem will not take + * wakelock because no one report event. + * In this case, we still need read data from IC, so AP can not enter suspend. + */ + if (enable == 1) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock(&private_ts->palm_gesture_read_wakelock); +#else + __pm_stay_awake(&private_ts->palm_gesture_read_wakelock); +#endif + g_core_fp.fp_palm_detection_function(1); + private_ts->palm_detection_enabled = true; + } else if (enable == 0) { + if (timer_pending(&private_ts->palm_release_fimer)) { + himax_palm_detect_func(private_ts, false); + del_timer(&private_ts->palm_release_fimer); + } + g_core_fp.fp_palm_detection_function(0); + private_ts->palm_detection_enabled = false; +#ifdef CONFIG_HAS_WAKELOCK + wake_unlock(&private_ts->palm_gesture_read_wakelock); +#else + __pm_relax(&private_ts->palm_gesture_read_wakelock); +#endif + } else + E("unknown enable symbol\n"); + + return 0; +} + +static int himax_palm_detect_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + int ret = 0; + + mutex_lock(&private_ts->suspend_resume_mutex); + if (!private_ts->suspended) + ret = _himax_palm_detect_sensor_set_enable(enable); + else { + I("Gesture lazy set enable %d!", enable); + if (enable == 1) + private_ts->palm_detection_lazy_set = PALM_SENSOR_LAZY_SET_ENABLE; + else if (enable == 0) + private_ts->palm_detection_lazy_set = PALM_SENSOR_LAZY_SET_DISABLE; + else + E("unknown enable symbol\n"); + } + mutex_unlock(&private_ts->suspend_resume_mutex); + himax_palm_detect_func(private_ts, false); + + return ret; +} + +static int himax_palm_detect_sensor_init(struct himax_ts_data *data) +{ + struct himax_sensor_platform_data *sensor_pdata; + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + E("Failed to allocate device"); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct himax_sensor_platform_data), + GFP_KERNEL); + if (!sensor_pdata) { + E("Failed to allocate memory"); + goto free_sensor_pdata; + } + data->palm_sensor_pdata = sensor_pdata; + + __set_bit(EV_ABS, sensor_input_dev->evbit); + __set_bit(EV_SYN, sensor_input_dev->evbit); + input_set_abs_params(sensor_input_dev, ABS_DISTANCE, + 0, 5, 0, 0); + sensor_input_dev->name = "palm_detect"; + data->palm_sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + E("Unable to register device, err=%d", err); + goto free_sensor_input_dev; + } + + sensor_pdata->ps_cdev = palm_sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = himax_palm_detect_sensor_set_enable; + sensor_pdata->data = data; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&data->palm_gesture_wakelock, WAKE_LOCK_SUSPEND, "palm_detect_wl"); +#else + wakeup_source_init(&data->palm_gesture_wakelock, "palm_detect_wl"); +#endif +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&data->palm_gesture_read_wakelock, WAKE_LOCK_SUSPEND, "palm_read_wl"); +#else + wakeup_source_init(&data->palm_gesture_read_wakelock, "palm_read_wl"); +#endif + + data->palm_release_fimer.function = himax_palm_sensor_release_timer_handler; + init_timer(&data->palm_release_fimer); + data->palm_release_delay_ms = 850; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(data->palm_sensor_pdata->input_sensor_dev); +free_sensor_input_dev: + input_free_device(data->palm_sensor_pdata->input_sensor_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, sensor_pdata); + data->palm_sensor_pdata= NULL; +exit: + return 1; +} + +int himax_palm_detect_sensor_remove(struct himax_ts_data *data) +{ + sensors_classdev_unregister(&data->palm_sensor_pdata->ps_cdev); + input_unregister_device(data->palm_sensor_pdata->input_sensor_dev); + devm_kfree(&data->palm_sensor_pdata->input_sensor_dev->dev, + data->palm_sensor_pdata); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&data->palm_gesture_wakelock); +#else + wakeup_source_trash(&data->palm_gesture_wakelock); +#endif +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&data->palm_gesture_read_wakelock); +#else + wakeup_source_trash(&data->palm_gesture_read_wakelock); +#endif + data->palm_sensor_pdata = NULL; + data->palm_detection_enabled = false; + return 0; +} +#endif + +#ifdef HIMAX_V2_SENSOR_EN +static int himax_tap_detect_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + int ret = 0; + struct himax_ts_data *ts = private_ts; + + if (enable) { + ts->SMWP_enable = 1; + ts->gesture_cust_en[0] = 1; + I("%s,here:%d, gesture_cust_en[0] = %d\n", __func__, __LINE__, ts->gesture_cust_en[0]); + } else { + ts->SMWP_enable = 0; + ts->gesture_cust_en[0] = 0; + I("%s,here:%d, gesture_cust_en[0] = %d\n", __func__, __LINE__, ts->gesture_cust_en[0]); + } + + g_core_fp.fp_set_SMWP_enable(ts->SMWP_enable, ts->suspended); + HX_SMWP_EN = ts->SMWP_enable; + I("%s: SMART_WAKEUP_enable = %d.\n", __func__, HX_SMWP_EN); + + return ret; +} + +static int himax_tap_detect_sensor_init(struct himax_ts_data *data) +{ + struct himax_tap_sensor_platform_data *sensor_pdata; + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + E("Failed to allocate device"); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct himax_tap_sensor_platform_data), + GFP_KERNEL); + if (!sensor_pdata) { + E("Failed to allocate memory"); + goto free_sensor_pdata; + } + data->sensor_pdata = sensor_pdata; + + __set_bit(EV_ABS, sensor_input_dev->evbit); + __set_bit(EV_SYN, sensor_input_dev->evbit); + input_set_abs_params(sensor_input_dev, ABS_DISTANCE, + 0, REPORT_MAX_COUNT, 0, 0); + sensor_input_dev->name = "double-tap"; + data->sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + E("Unable to register device, err=%d", err); + goto free_sensor_input_dev; + } + + sensor_pdata->ps_cdev = sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = himax_tap_detect_sensor_set_enable; + sensor_pdata->data = data; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(data->sensor_pdata->input_sensor_dev); +free_sensor_input_dev: + input_free_device(data->sensor_pdata->input_sensor_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, sensor_pdata); + data->sensor_pdata= NULL; +exit: + return 1; +} + +int himax_tap_detect_sensor_remove(struct himax_ts_data *data) +{ + sensors_classdev_unregister(&data->sensor_pdata->ps_cdev); + input_unregister_device(data->sensor_pdata->input_sensor_dev); + devm_kfree(&data->sensor_pdata->input_sensor_dev->dev, + data->sensor_pdata); + data->sensor_pdata = NULL; + return 0; +} +#endif + +void himax_ts_work(struct himax_ts_data *ts) +{ + + int ts_status = HX_TS_NORMAL_END; + int ts_path = 0; + + if (debug_data != NULL) + debug_data->fp_ts_dbg_func(ts, HX_FINGER_ON); + +#if defined(HX_USB_DETECT_GLOBAL) + himax_cable_detect_func(false); +#endif + + ts_path = himax_ts_work_status(ts); + switch (ts_path) { + case HX_REPORT_COORD: + ts_status = himax_ts_operation(ts, ts_path, ts_status); + break; + case HX_REPORT_SMWP_EVENT: + ts_status = himax_ts_operation(ts, ts_path, ts_status); + break; + case HX_REPORT_COORD_RAWDATA: + ts_status = himax_ts_operation(ts, ts_path, ts_status); + break; + default: + E("%s:Path Fault! value=%d\n", __func__, ts_path); + goto END_FUNCTION; + } + + if (ts_status == HX_TS_GET_DATA_FAIL) + goto GET_TOUCH_FAIL; + else + goto END_FUNCTION; + +GET_TOUCH_FAIL: + I("%s: Now reset the Touch chip.\n", __func__); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, true); +#else + g_core_fp.fp_system_reset(); +#endif +#if defined(HX_ZERO_FLASH) && defined(HX83102D) + if (g_core_fp.fp_0f_reload_to_active) + g_core_fp.fp_0f_reload_to_active(); +#endif +END_FUNCTION: + if (debug_data != NULL) + debug_data->fp_ts_dbg_func(ts, HX_FINGER_LEAVE); + +} +/*end ts_work*/ +enum hrtimer_restart himax_ts_timer_func(struct hrtimer *timer) +{ + struct himax_ts_data *ts; + + + ts = container_of(timer, struct himax_ts_data, timer); + queue_work(ts->himax_wq, &ts->work); + hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +#if defined(HX_USB_DETECT_CALLBACK) +static void himax_cable_tp_status_handler_func(int connect_status) +{ + struct himax_ts_data *ts; + + I("Touch: cable change to %d\n", connect_status); + + ts = private_ts; + + if (ts->cable_config) { + if (!atomic_read(&ts->suspend_mode)) { + if (connect_status != ts->usb_connected) { + if (connect_status) { + ts->cable_config[1] = 0x01; + ts->usb_connected = 0x01; + } else { + ts->cable_config[1] = 0x00; + ts->usb_connected = 0x00; + } + + himax_bus_master_write(ts->cable_config, + sizeof(ts->cable_config), HIMAX_I2C_RETRY_TIMES); + I("%s: Cable status change: 0x%2.2X\n", __func__, ts->cable_config[1]); + } else + I("%s: Cable status is the same as previous one, ignore.\n", __func__); + } else { + if (connect_status) + ts->usb_connected = 0x01; + else + ts->usb_connected = 0x00; + + I("%s: Cable status remembered: 0x%2.2X\n", __func__, ts->usb_connected); + } + } +} + +static struct t_cable_status_notifier himax_cable_status_handler = { + .name = "usb_tp_connected", + .func = himax_cable_tp_status_handler_func, +}; + +#endif + +#ifdef HX_AUTO_UPDATE_FW +static void himax_update_register(struct work_struct *work) +{ + I(" %s in\n", __func__); + + if (i_get_FW() != 0) + return; + + if (g_auto_update_flag == true) { + I("Update FW Directly"); + goto UPDATE_FW; + } + + if (himax_auto_update_check() != 0) { + I("%s:Don't run auto update fw, so free allocated!\n", __func__); + kfree(i_CTPM_FW); + i_CTPM_FW = NULL; + return; + } + +UPDATE_FW: + if (i_update_FW() <= 0) + I("Auto update FW fail!\n"); + else + I("It have Updated\n"); + +} +#endif + +#ifndef HX_ZERO_FLASH +static int hx_chk_flash_sts(void) +{ + int rslt = 0; + uint32_t size = 0; + + I("%s: Entering\n", __func__); + if (g_core_fp._diff_overlay_flash() == 1) + size = FW_SIZE_128k; + else + size = FW_SIZE_64k; + + +#ifdef HX_AUTO_UPDATE_FW + g_auto_update_flag = (!g_core_fp.fp_calculateChecksum(false, size)); + g_auto_update_flag |= g_core_fp.fp_flash_lastdata_check(size); +#else + g_core_fp.fp_calculateChecksum(false, size); + g_core_fp.fp_flash_lastdata_check(size); +#endif + + return rslt; +} +#endif + +#if defined(CONFIG_DRM) +static void himax_fb_register(struct work_struct *work) +{ + int ret = 0; + + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work_att.work); + + + I("%s in\n", __func__); + ts->fb_notif.notifier_call = drm_notifier_callback; + ret = msm_drm_register_client(&ts->fb_notif); + + if (ret) + E("Unable to register fb_notifier: %d\n", ret); +} +#elif defined(CONFIG_FB) +static void himax_fb_register(struct work_struct *work) +{ + int ret = 0; + + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work_att.work); + + + I("%s in\n", __func__); + ts->fb_notif.notifier_call = fb_notifier_callback; + ret = fb_register_client(&ts->fb_notif); + + if (ret) + E("Unable to register fb_notifier: %d\n", ret); +} +#endif +static ssize_t himax_productinfo_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + if (!ts) { + pr_err("cannot get himax_ts_data pointer\n"); + return (ssize_t)0; + } + return scnprintf(buf, PAGE_SIZE, "%s\n", ts->chip_name); +} +static ssize_t buildid_show( + struct device *dev, struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%2.2X\n",ic_data->vendor_fw_ver); +} + +static DEVICE_ATTR(buildid, S_IRUGO, buildid_show, NULL); +static DEVICE_ATTR(productinfo, S_IRUGO, himax_productinfo_show, NULL); + +static struct attribute *himax_attributes[] = { + &dev_attr_buildid.attr, + &dev_attr_productinfo.attr, + NULL +}; + +static struct attribute_group himax_attribute_group = { + .attrs = himax_attributes +}; + +#include +#include + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!ts) { + pr_err("cannot get himax_ts_data pointer\n"); + return (ssize_t)0; + } + path = kobject_get_path(&ts->dev->kobj, GFP_KERNEL); + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "himax"); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_NULL +}; + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +static int himax_sysfs_touchscreen( + struct himax_ts_data *ts, bool create) +{ + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + static struct class *touchscreen_class; + static struct device *ts_class_dev; + static int minor; + + if (create) { + minor = input_get_new_minor(0x90, + 1, false); + if (minor < 0) + minor = input_get_new_minor(TSDEV_MINOR_BASE, + TSDEV_MINOR_MAX, true); + pr_info("assigned minor %d\n", minor); + + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + + ts_class_dev = device_create(touchscreen_class, NULL, + MKDEV(INPUT_MAJOR, minor), + ts, "%s", ts->chip_name); + if (IS_ERR(ts_class_dev)) { + error = PTR_ERR(ts_class_dev); + ts_class_dev = NULL; + return error; + } + for (i = 0; attrs[i].attr.name != NULL; ++i) { + error = device_create_file(ts_class_dev, &attrs[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + } else { + if (!touchscreen_class || !ts_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(ts_class_dev, &attrs[i]); + + device_unregister(ts_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor)); + ts_class_dev = NULL; + class_unregister(touchscreen_class); + pr_err("error creating touchscreen class\n"); + + return -ENODEV; +} + +int himax_create_sysfs(struct himax_ts_data *ts_data) +{ + int ret = 0; + ret = sysfs_create_group(&ts_data->dev->kobj, &himax_attribute_group); + if (ret) { + E("[EX]: sysfs_create_group() failed!!"); + sysfs_remove_group(&ts_data->dev->kobj, &himax_attribute_group); + return -ENOMEM; + } + return ret; +} + +int himax_remove_sysfs(struct himax_ts_data *ts_data) +{ + sysfs_remove_group(&ts_data->dev->kobj, &himax_attribute_group); + return 0; +} + +#if defined(HX_USB_DETECT_GLOBAL) +static int charger_notifier_callback(struct notifier_block *nb, + unsigned long val, void *v) { + int ret = 0; + struct power_supply *psy = NULL; + struct himax_ts_data *ts = container_of(nb, struct himax_ts_data, charger_notif); + union power_supply_propval prop; + + psy= power_supply_get_by_name("usb"); + if (!psy) { + E("Couldn't get usbpsy\n"); + return -EINVAL; + } + if (!strcmp(psy->desc->name, "usb")) { + if (psy && ts && val == POWER_SUPPLY_PROP_STATUS) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,&prop); + if (ret < 0) { + E("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret); + return ret; + } else { + USB_detect_flag = prop.intval; + //E("usb prop.intval =%d\n", prop.intval); + } + } + } + return 0; +} +#endif + +#if defined(__HIMAX_HX83102D_MOD__) +#define TP_CHECK_TIMES 30 +#define TP_CHECK_INTERVAL 10 +void himax_resume_thread_check(void) +{ + uint8_t cycle = 0; + + while (true) { + if(cycle++ > TP_CHECK_TIMES) + break; + + if (atomic_read(&private_ts->resume_thread_is_runing)) + msleep(TP_CHECK_INTERVAL); + else + break; + } + I("cycle:%d",cycle); + return; +} +#endif + +#if defined(HX_RESUME_SET_FW) +void himax_resume_work_func(struct work_struct *work) +{ + struct himax_ts_data *ts = private_ts; +#if defined(__HIMAX_HX83102D_MOD__) + atomic_set(&private_ts->resume_thread_is_runing, 1); +#endif + himax_chip_common_resume(ts); +#if defined(__HIMAX_HX83102D_MOD__) + atomic_set(&private_ts->resume_thread_is_runing, 0); +#endif + return; +} +#endif +int himax_chip_common_init(void) +{ + + int i = 0, ret = 0, err = 0, idx = 0; + struct himax_ts_data *ts = private_ts; + struct himax_i2c_platform_data *pdata; + struct himax_chip_entry *entry; + + I("Prepare kernel fp\n"); + kp_getname_kernel = (void *)kallsyms_lookup_name("getname_kernel"); + if (!kp_getname_kernel) { + E("prepare kp_getname_kernel failed!\n"); + /*goto err_dt_platform_data_fail;*/ + } + kp_file_open_name = (void *)kallsyms_lookup_name("file_open_name"); + if (!kp_file_open_name) { + E("prepare kp_file_open_name failed!\n"); + goto err_dt_platform_data_fail; + } +#if defined(__EMBEDDED_FW__) + g_embedded_fw.size = (size_t)_binary___Himax_firmware_bin_end - + (size_t)_binary___Himax_firmware_bin_start; +#endif + ts->xfer_buff = devm_kzalloc(ts->dev, 128 * sizeof(uint8_t), GFP_KERNEL); + if (ts->xfer_buff == NULL) { + err = -ENOMEM; + goto err_dt_platform_data_fail; + } + + I("PDATA START\n"); + pdata = kzalloc(sizeof(struct himax_i2c_platform_data), GFP_KERNEL); + + if (pdata == NULL) { /*Allocate Platform data space*/ + err = -ENOMEM; + goto err_dt_platform_data_fail; + } + + I("ic_data START\n"); + ic_data = kzalloc(sizeof(struct himax_ic_data), GFP_KERNEL); + if (ic_data == NULL) { /*Allocate IC data space*/ + err = -ENOMEM; + goto err_dt_ic_data_fail; + } + + /* allocate report data */ + hx_touch_data = kzalloc(sizeof(struct himax_report_data), GFP_KERNEL); + if (hx_touch_data == NULL) { + err = -ENOMEM; + goto err_alloc_touch_data_failed; + } + + if (himax_parse_dt(ts, pdata) < 0) { + I(" pdata is NULL for DT\n"); + goto err_alloc_dt_pdata_failed; + } + +#ifdef HX_RST_PIN_FUNC + ts->rst_gpio = pdata->gpio_reset; +#endif + if (himax_gpio_power_config(pdata)) { + err = -1; + E("%s: himax_gpio_power_config failed, stop Himax probe\n", __func__); + goto err_himax_gpio_power_config_failed; + } +#ifndef CONFIG_OF + + if (pdata->power) { + ret = pdata->power(1); + + if (ret < 0) { + E("%s: power on failed\n", __func__); + goto err_power_failed; + } + } + +#endif + + i_CTPM_firmware_name = kzalloc(HIMAX_FILE_NAME_LENGTH, GFP_KERNEL); + if (!i_CTPM_firmware_name) { + E("%s: alloc i_CTPM_firmware_name failed\n", __func__); + goto firmware_name_alloc_failed; + } + if (strlen(pdata->panel_supplier)) + snprintf(i_CTPM_firmware_name, HIMAX_FILE_NAME_LENGTH, "%s_Himax_firmware.bin", + pdata->panel_supplier); + else + snprintf(i_CTPM_firmware_name, HIMAX_FILE_NAME_LENGTH, "Himax_firmware.bin"); + + g_hx_chip_inited = 0; + idx = himax_get_ksym_idx(); + if (idx >= 0) { + if (isEmpty(idx) != 0) { + I("%s: no chip registered, please insmod ic.ko!\n", + __func__); + goto error_ic_detect_failed; + } + entry = get_chip_entry_by_index(idx); + + for (i = 0; i < entry->hx_ic_dt_num; i++) { + if (entry->core_chip_dt[i].fp_chip_detect != NULL) { + if (entry->core_chip_dt[i].fp_chip_detect() + == true) { + I("%s: chip detect found! list_num=%d\n" + , __func__, i); + goto found_hx_chip; + } else { + I("%s: num=%d chip detect NOT found! goto Next\n", __func__, i); + continue; + } + } + } + } else { + I("%s: No available chip exist!\n", __func__); + goto error_ic_detect_failed; + } + + if (i == entry->hx_ic_dt_num) { + E("%s: chip detect failed!\n", __func__); + goto error_ic_detect_failed; + } +found_hx_chip: + if (g_core_fp.fp_chip_init != NULL) { + g_core_fp.fp_chip_init(); + } else { + E("%s: function point of chip_init is NULL!\n", __func__); + goto error_ic_detect_failed; + } + + if (pdata->virtual_key) + ts->button = pdata->virtual_key; + +#ifndef HX_ZERO_FLASH + g_core_fp.fp_read_FW_ver(); + hx_chk_flash_sts(); +#endif + +#ifdef HX_AUTO_UPDATE_FW + if (g_auto_update_flag) + goto FW_force_upgrade; +#endif + + +#ifdef HX_AUTO_UPDATE_FW +FW_force_upgrade: + ts->himax_update_wq = create_singlethread_workqueue("HMX_update_reuqest"); + if (!ts->himax_update_wq) { + E(" allocate himax_update_wq failed\n"); + err = -ENOMEM; + goto err_update_wq_failed; + } + INIT_DELAYED_WORK(&ts->work_update, himax_update_register); + queue_delayed_work(ts->himax_update_wq, &ts->work_update, msecs_to_jiffies(2000)); +#endif +#ifdef HX_ZERO_FLASH + g_auto_update_flag = true; + ts->himax_0f_update_wq = create_singlethread_workqueue("HMX_0f_update_reuqest"); + if (!ts->himax_0f_update_wq) { + E(" allocate himax_0f_update_wq failed\n"); + err = -ENOMEM; + goto err_0f_update_wq_failed; + } + INIT_DELAYED_WORK(&ts->work_0f_update, g_core_fp.fp_0f_operation); + queue_delayed_work(ts->himax_0f_update_wq, &ts->work_0f_update, msecs_to_jiffies(2000)); +#endif + +#ifdef HX_RESUME_SET_FW + ts->ts_int_workqueue = create_singlethread_workqueue("himax_ts_resume_wq"); + if (!ts->ts_int_workqueue) { + E("%s: create ts_resume workqueue failed\n", __func__); + goto err_create_ts_resume_wq_failed; + } +#if defined(__HIMAX_HX83102D_MOD__) + INIT_DELAYED_WORK(&ts->ts_int_work, himax_resume_work_func); +#else + INIT_WORK(&ts->ts_int_work, himax_resume_work_func); +#endif +#endif + /*Himax Power On and Load Config*/ + if (himax_loadSensorConfig(pdata)) { + E("%s: Load Sesnsor configuration failed, unload driver.\n", __func__); + goto err_detect_failed; + } + + g_core_fp.fp_power_on_init(); + calculate_point_number(); + +#ifdef CONFIG_OF + ts->power = pdata->power; +#endif + ts->pdata = pdata; + /*calculate the i2c data size*/ + calcDataSize(); + I("%s: calcDataSize complete\n", __func__); +#ifdef CONFIG_OF + ts->pdata->abs_pressure_min = 0; + ts->pdata->abs_pressure_max = 200; + ts->pdata->abs_width_min = 0; + ts->pdata->abs_width_max = 200; + pdata->cable_config[0] = 0xF0; + pdata->cable_config[1] = 0x00; +#endif + ts->suspended = false; + atomic_set(&ts->resume_update_fail, 0); + atomic_set(&ts->resume_thread_is_runing, 0); + mutex_init(&ts->suspend_resume_mutex); +#if defined(HX_USB_DETECT_CALLBACK) || defined(HX_USB_DETECT_GLOBAL) + ts->usb_connected = 0x00; + ts->cable_config = pdata->cable_config; + ts->charger_notif.notifier_call = charger_notifier_callback; + ret = power_supply_reg_notifier(&ts->charger_notif); + if (ret) { + E("Unable to register charger_notifier: %d\n",ret); + goto err_register_charger_notify_failed; + } +#endif +#ifdef HX_PROTOCOL_A + ts->protocol_type = PROTOCOL_TYPE_A; +#else + ts->protocol_type = PROTOCOL_TYPE_B; +#endif + I("%s: Use Protocol Type %c\n", __func__, + ts->protocol_type == PROTOCOL_TYPE_A ? 'A' : 'B'); + ret = himax_input_register(ts); + + if (ret) { + E("%s: Unable to register %s input device\n", + __func__, ts->input_dev->name); + goto err_input_register_device_failed; + } + + ts->initialized = true; +#if defined(CONFIG_FB) || defined(CONFIG_DRM) + ts->himax_att_wq = create_singlethread_workqueue("HMX_ATT_reuqest"); + + if (!ts->himax_att_wq) { + E(" allocate himax_att_wq failed\n"); + err = -ENOMEM; + goto err_get_intr_bit_failed; + } + + INIT_DELAYED_WORK(&ts->work_att, himax_fb_register); + queue_delayed_work(ts->himax_att_wq, &ts->work_att, msecs_to_jiffies(15000)); +#endif + +#ifdef HX_SMART_WAKEUP + ts->SMWP_enable = 0; + wakeup_source_init(&ts->ts_SMWP_wake_lock, HIMAX_common_NAME); +#endif +#ifdef HX_HIGH_SENSE + ts->HSEN_enable = 0; +#endif + + /*touch data init*/ + err = himax_report_data_init(); + + if (err) + goto err_report_data_init_failed; + + if (himax_common_proc_init()) { + E(" %s: himax_common proc_init failed!\n", __func__); + goto err_creat_proc_file_failed; + } + err = himax_create_sysfs(ts); + if (err) { + E(" %s: create sysfs node fail!\n", __func__); + } + himax_sysfs_touchscreen(ts, true); + +#if defined(HX_USB_DETECT_CALLBACK) + + if (ts->cable_config) + cable_detect_register_notifier(&himax_cable_status_handler); + +#endif + +#ifdef HIMAX_PALM_SENSOR_EN + if (himax_palm_detect_sensor_init(ts)) { + E(" %s: himax_palm_detect_sensor_init failed!\n", __func__); + goto err_create_palm_sensor_failed; + } +#endif + +#ifdef HIMAX_V2_SENSOR_EN + if (himax_tap_detect_sensor_init(ts)) { + E(" %s: himax_tap_detect_sensor_init failed!\n", __func__); + goto err_create_tap_sensor_failed; + } +#endif + + himax_ts_register_interrupt(); + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_DEBUG + if (himax_debug_init()) + E(" %s: debug initial failed!\n", __func__); +#endif + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) + + if (g_auto_update_flag) + himax_int_enable(0); + +#endif + g_hx_chip_inited = true; + return 0; + +#ifdef HIMAX_V2_SENSOR_EN +err_create_tap_sensor_failed: +#endif +#ifdef HIMAX_PALM_SENSOR_EN +err_create_palm_sensor_failed: +#endif +err_creat_proc_file_failed: + himax_report_data_deinit(); +err_report_data_init_failed: +#ifdef HX_SMART_WAKEUP + wakeup_source_trash(&ts->ts_SMWP_wake_lock); +#endif +#if defined(CONFIG_FB) || defined(CONFIG_DRM) + cancel_delayed_work_sync(&ts->work_att); + destroy_workqueue(ts->himax_att_wq); +err_get_intr_bit_failed: +#endif +err_input_register_device_failed: + input_free_device(ts->input_dev); +err_detect_failed: +#ifdef HX_RESUME_SET_FW +#if defined(__HIMAX_HX83102D_MOD__) + cancel_delayed_work_sync(&ts->ts_int_work); +#else + cancel_work_sync(&ts->ts_int_work); +#endif + destroy_workqueue(ts->ts_int_workqueue); +err_create_ts_resume_wq_failed: +#endif +#ifdef HX_ZERO_FLASH + cancel_delayed_work_sync(&ts->work_0f_update); + destroy_workqueue(ts->himax_0f_update_wq); +err_0f_update_wq_failed: +#endif +#ifdef HX_AUTO_UPDATE_FW + if (g_auto_update_flag) { + cancel_delayed_work_sync(&ts->work_update); + destroy_workqueue(ts->himax_update_wq); + } +err_update_wq_failed: +#endif +#if defined(HX_USB_DETECT_GLOBAL) +err_register_charger_notify_failed: + if (ts->charger_notif.notifier_call) + power_supply_unreg_notifier(&ts->charger_notif); +#endif +error_ic_detect_failed: + himax_gpio_power_deconfig(pdata); +firmware_name_alloc_failed: +#ifndef CONFIG_OF +err_power_failed: +#endif +err_himax_gpio_power_config_failed: +err_alloc_dt_pdata_failed: + kfree(hx_touch_data); + hx_touch_data = NULL; +err_alloc_touch_data_failed: + kfree(ic_data); + ic_data = NULL; +err_dt_ic_data_fail: + kfree(pdata); + pdata = NULL; +err_dt_platform_data_fail: + probe_fail_flag = 1; + return err; +} + +void himax_chip_common_deinit(void) +{ + struct himax_ts_data *ts = private_ts; + + himax_ts_unregister_interrupt(); + + himax_remove_sysfs(ts); + himax_sysfs_touchscreen(ts, false); + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + himax_inspect_data_clear(); +#endif + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_DEBUG + himax_debug_remove(); +#endif + + himax_common_proc_deinit(); + himax_report_data_deinit(); + +#ifdef HX_SMART_WAKEUP + wakeup_source_trash(&ts->ts_SMWP_wake_lock); +#endif +#if defined(CONFIG_DRM) + if (msm_drm_unregister_client(&ts->fb_notif)) + E("Error occurred while unregistering fb_notifier.\n"); + cancel_delayed_work_sync(&ts->work_att); + destroy_workqueue(ts->himax_att_wq); +#elif defined(CONFIG_FB) + if (fb_unregister_client(&ts->fb_notif)) + E("Error occurred while unregistering fb_notifier.\n"); + cancel_delayed_work_sync(&ts->work_att); + destroy_workqueue(ts->himax_att_wq); +#endif + input_free_device(ts->input_dev); +#ifdef HX_ZERO_FLASH + cancel_delayed_work_sync(&ts->work_0f_update); + destroy_workqueue(ts->himax_0f_update_wq); +#endif +#ifdef HX_AUTO_UPDATE_FW + cancel_delayed_work_sync(&ts->work_update); + destroy_workqueue(ts->himax_update_wq); +#endif + himax_gpio_power_deconfig(ts->pdata); + if (himax_mcu_cmd_struct_free) + himax_mcu_cmd_struct_free(); + + kfree(hx_touch_data); + hx_touch_data = NULL; + kfree(ic_data); + ic_data = NULL; + kfree(ts->pdata->virtual_key); + ts->pdata->virtual_key = NULL; + kfree(ts->pdata); + ts->pdata = NULL; + kfree(ts); + ts = NULL; + probe_fail_flag = 0; + + I("%s: Common section deinited!\n", __func__); +} + +int _himax_chip_common_suspend(struct himax_ts_data *ts) +{ + if (ts->suspended) { + I("%s: Already suspended. Skipped.\n", __func__); + goto END; + } else { + ts->suspended = true; + I("%s: enter\n", __func__); + } + +#ifdef HIMAX_PALM_SENSOR_EN + if (ts->palm_detection_enabled) { + himax_report_all_leave_event(ts); + atomic_set(&ts->suspend_mode, 1); + I("[himax] %s: palm detect function enable, reject suspend\n", __func__); + goto END; + } +#endif + + if (debug_data != NULL && debug_data->flash_dump_going == true) { + I("[himax] %s: Flash dump is going, reject suspend\n", __func__); + goto END; + } + +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) +#ifndef HX_RESUME_SEND_CMD + g_core_fp.fp_resend_cmd_func(ts->suspended); +#endif +#endif +#ifdef HX_SMART_WAKEUP + + if (ts->SMWP_enable) { + +#ifdef HX_CODE_OVERLAY + if (ts->in_self_test == 0) + g_core_fp.fp_0f_overlay(2, 0); +#endif + + atomic_set(&ts->suspend_mode, 1); + ts->pre_finger_mask = 0; + FAKE_POWER_KEY_SEND = false; + I("[himax] %s: SMART_WAKEUP enable, reject suspend\n", __func__); + goto END; + } + +#endif + himax_int_enable(0); + himax_report_all_leave_event(ts); + /*if (g_core_fp.fp_suspend_ic_action != NULL)*/ + /*g_core_fp.fp_suspend_ic_action();*/ + + if (!ts->use_irq) { + int32_t cancel_state; + + cancel_state = cancel_work_sync(&ts->work); + if (cancel_state) + himax_int_enable(1); + } + + /*ts->first_pressed = 0;*/ + atomic_set(&ts->suspend_mode, 1); + ts->pre_finger_mask = 0; + + if (ts->pdata) + if (ts->pdata->powerOff3V3 && ts->pdata->power) + ts->pdata->power(0); + +END: + if (ts->in_self_test == 1) + ts->suspend_resume_done = 1; + + I("%s: END\n", __func__); + + return 0; +} + +int himax_chip_common_suspend(struct himax_ts_data *ts) +{ + int ret = 0; +#if defined(__HIMAX_HX83102D_MOD__) + himax_resume_thread_check(); +#endif + mutex_lock(&ts->suspend_resume_mutex); + ret = _himax_chip_common_suspend(ts); + mutex_unlock(&ts->suspend_resume_mutex); + + return ret; +} + +int _himax_chip_common_resume(struct himax_ts_data *ts) +{ +#if defined(HX_ZERO_FLASH) && defined(HX_RESUME_SET_FW) + int result = 0; +#endif +#if defined(__HIMAX_HX83102D_MOD__) + uint8_t tmp_data[DATA_LEN_4] = {0}; +#endif + I("%s: enter\n", __func__); + +#if defined(__HIMAX_HX83102D_MOD__) + if (atomic_read(&ts->resume_update_fail)) { + I("%s:Last time update Fail!\n", __func__); + atomic_set(&ts->resume_update_fail, 0); + ts->suspended = false; + goto START_RESUME; + } +#endif + if (ts->suspended == false) { + I("%s: It had entered resume, skip this step\n", __func__); + goto END; + } else { + ts->suspended = false; + } +#if defined(__HIMAX_HX83102D_MOD__) +START_RESUME: +#endif + +#ifdef HX_ESD_RECOVERY + /* continuous N times record, not total N times. */ + g_zero_event_count = 0; +#endif + + atomic_set(&ts->suspend_mode, 0); + ts->diag_cmd = 0; +#ifdef HIMAX_PALM_SENSOR_EN + if (ts->palm_detection_enabled) { + I("[himax] %s: palm detect function enable, skip resume\n", __func__); + goto CHECK_LAZY_SET_END; + } +#endif + + if (ts->pdata) + if (ts->pdata->powerOff3V3 && ts->pdata->power) + ts->pdata->power(1); + +#ifndef HX_RESUME_SET_FW +#if defined(HX_RST_PIN_FUNC) && defined(HX_RESUME_HW_RESET) + if (g_core_fp.fp_ic_reset != NULL) + g_core_fp.fp_ic_reset(false, false); +#endif +#endif + +#if defined(HX_ZERO_FLASH) && defined(HX_RESUME_SET_FW) +#ifdef HX_SMART_WAKEUP + if (!ts->SMWP_enable) { +#endif + I("It will update fw after resume in zero flash mode!\n"); + if (g_core_fp.fp_0f_operation_dirly != NULL) { + result = g_core_fp.fp_0f_operation_dirly(); + if (result) { + E("Something is wrong! Skip Update with zero flash!\n"); +#if defined(__HIMAX_HX83102D_MOD__) + atomic_set(&ts->resume_update_fail, 1); +#endif + goto ESCAPE_0F_UPDATE; + } + } + if (g_core_fp.fp_reload_disable != NULL) + g_core_fp.fp_reload_disable(0); + if (g_core_fp.fp_sense_on != NULL) + g_core_fp.fp_sense_on(0x00); +#if defined(__HIMAX_HX83102D_MOD__) + g_core_fp.fp_register_read(pzf_op->addr_sts_chk, DATA_LEN_4, tmp_data, 0); + if (tmp_data[0] != 0x05) + atomic_set(&ts->resume_update_fail, 1); +#endif +#ifdef HX_SMART_WAKEUP + } +#endif +#endif +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) + if (g_core_fp.fp_resend_cmd_func != NULL) + g_core_fp.fp_resend_cmd_func(ts->suspended); + +#ifdef HX_CODE_OVERLAY + if (ts->SMWP_enable && ts->in_self_test == 0) + g_core_fp.fp_0f_overlay(3, 0); +#endif +#endif + himax_report_all_leave_event(ts); + + if (g_core_fp.fp_sense_on != NULL) + g_core_fp.fp_resume_ic_action(); + himax_int_enable(1); +#if defined(HX_ZERO_FLASH) && defined(HX_RESUME_SET_FW) +ESCAPE_0F_UPDATE: +#endif + +#ifdef HIMAX_PALM_SENSOR_EN +CHECK_LAZY_SET_END: + if (ts->palm_detection_lazy_set != PALM_SENSOR_LAZY_SET_NONE) { + _himax_palm_detect_sensor_set_enable( + (ts->palm_detection_lazy_set == PALM_SENSOR_LAZY_SET_ENABLE) ? 1 : 0); + I("%s: Palm sensor lazy set done, clear flag.\n", __func__); + ts->palm_detection_lazy_set = PALM_SENSOR_LAZY_SET_NONE; + } +#endif +END: + if (ts->in_self_test == 1) + ts->suspend_resume_done = 1; + + I("%s: END\n", __func__); + return 0; +} + +int himax_chip_common_resume(struct himax_ts_data *ts) +{ + int ret = 0; + mutex_lock(&ts->suspend_resume_mutex); + ret = _himax_chip_common_resume(ts); + mutex_unlock(&ts->suspend_resume_mutex); + + return ret; +} + diff --git a/drivers/input/touchscreen/himax_v2_mmi/himax_common.h b/drivers/input/touchscreen/himax_v2_mmi/himax_common.h new file mode 100755 index 000000000000..7ac38db5afcc --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/himax_common.h @@ -0,0 +1,591 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for common functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef HIMAX_COMMON_H +#define HIMAX_COMMON_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "himax_platform.h" +#include +#ifdef HIMAX_PALM_SENSOR_EN +#include +#define SENSOR_TYPE_MOTO_TOUCH_PALM (SENSOR_TYPE_DEVICE_PRIVATE_BASE + 31) +#endif +#ifdef HIMAX_V2_SENSOR_EN +#include +#endif + +#if defined(CONFIG_DRM) + #include +#elif defined(CONFIG_FB) + #include + #include +#elif defined(CONFIG_HAS_EARLYSUSPEND) + #include +#endif + +#ifdef CONFIG_OF + #include +#endif + +#define HIMAX_DRIVER_VER "2.0.0.14_ABCD1234_01" + +#define FLASH_DUMP_FILE "/sdcard/HX_Flash_Dump.bin" + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG) + #define HX_TP_PROC_2T2R + /*#define HX_TP_SELF_TEST_DRIVER*/ /*if enable, selftest works in driver*/ +#endif + +#ifdef HIMAX_PALM_SENSOR_EN +struct himax_sensor_platform_data { + struct input_dev *input_sensor_dev; + struct sensors_classdev ps_cdev; + int sensor_opened; + char sensor_data; /* 0 near, 1 far */ + struct himax_ts_data *data; +}; + +enum palm_sensor_lazy_set { + PALM_SENSOR_LAZY_SET_NONE = 0, + PALM_SENSOR_LAZY_SET_ENABLE, + PALM_SENSOR_LAZY_SET_DISABLE, +}; +#endif + +#ifdef HIMAX_V2_SENSOR_EN +struct himax_tap_sensor_platform_data { + struct input_dev *input_sensor_dev; + struct sensors_classdev ps_cdev; + struct himax_ts_data *data; +}; +#endif + +/*===========Himax Option function=============*/ +#define HX_RST_PIN_FUNC +#define HX_RESUME_SEND_CMD +#define HX_ESD_RECOVERY +#define HX_TP_PROC_GUEST_INFO +/*#define HX_SHARP_ENABLE_PON*/ +/*#define HX_NEW_EVENT_STACK_FORMAT*/ +/*#define HX_AUTO_UPDATE_FW*/ +#ifdef HIMAX_V2_SENSOR_EN +#define HX_SMART_WAKEUP +#else +//#define HX_SMART_WAKEUP +#endif +/*#define HX_GESTURE_TRACK*/ +/*#define HX_HIGH_SENSE*/ +#ifdef HIMAX_PALM_SENSOR_EN +#define HX_PALM_REPORT +#else +/*#define HX_PALM_REPORT*/ +#endif +#define HX_USB_DETECT_GLOBAL +/*#define HX_USB_DETECT_CALLBACK*/ +/*#define HX_PROTOCOL_A*/ /* for MTK special platform.If turning on,it will report to system by using specific format. */ +#define HX_RESUME_HW_RESET +#define HX_PROTOCOL_B_3PA +/*#define HX_FIX_TOUCH_INFO*/ /* if open, you need to change the touch info in the fix_touch_info*/ +/*#define HX_ZERO_FLASH*/ +#define HX_RESUME_SET_FW +/*#if defined(HX_ZERO_FLASH)*/ +/*#define HX_CODE_OVERLAY*/ +/*#define HX_EDGE_LIMIT*/ +/*#endif*/ +/*#define HX_EN_DYNAMIC_NAME*/ /* Support dynamic load fw name ,default is close */ +/*#undef CONFIG_FB*/ /* Enable it if driver go into suspend/resume twice */ +/*#define HX_PEN_FUNC_EN*/ + +/*#define HX_EN_SEL_BUTTON*/ /* Support Self Virtual key ,default is close*/ +/*#define HX_EN_MUT_BUTTON*/ /* Support Mutual Virtual Key ,default is close*/ + +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) + /*#define HX_PLATFOME_DEFINE_KEY*/ /* for specfic platform to set key(button) */ +#endif +#ifdef HX_USB_DETECT_GLOBAL +#include +#endif + +#if defined(HX_SMART_WAKEUP) +/*#define HX_P_SENSOR*/ +#endif +/*=============================================*/ +#define HX_SPI_MTK_MAX_WRITE_SZ (256 - 4) + +#define HX_KEY_MAX_COUNT 4 +#define DEFAULT_RETRY_CNT 3 + +#define HX_85XX_A_SERIES_PWON "HX85xxA" +#define HX_85XX_B_SERIES_PWON "HX85xxB" +#define HX_85XX_C_SERIES_PWON "HX85xxC" +#define HX_85XX_D_SERIES_PWON "HX85xxD" +#define HX_85XX_E_SERIES_PWON "HX85xxE" +#define HX_85XX_ES_SERIES_PWON "HX85xxES" +#define HX_85XX_F_SERIES_PWON "HX85xxF" +#define HX_85XX_H_SERIES_PWON "HX85xxH" +#define HX_83100A_SERIES_PWON "HX83100A" +#define HX_83102A_SERIES_PWON "HX83102A" +#define HX_83102B_SERIES_PWON "HX83102B" +#define HX_83102D_SERIES_PWON "HX83102D" +#define HX_83102E_SERIES_PWON "HX83102E" +#define HX_83103A_SERIES_PWON "HX83103A" +#define HX_83106A_SERIES_PWON "HX83106A" +#define HX_83110A_SERIES_PWON "HX83110A" +#define HX_83110B_SERIES_PWON "HX83110B" +#define HX_83111B_SERIES_PWON "HX83111B" +#define HX_83112A_SERIES_PWON "HX83112A" +#define HX_83112B_SERIES_PWON "HX83112B" +#define HX_83113A_SERIES_PWON "HX83113A" +#define HX_83112D_SERIES_PWON "HX83112D" +#define HX_83112E_SERIES_PWON "HX83112E" +#define HX_83191A_SERIES_PWON "HX83191A" + +#define HX_TP_BIN_CHECKSUM_SW 1 +#define HX_TP_BIN_CHECKSUM_HW 2 +#define HX_TP_BIN_CHECKSUM_CRC 3 + +#define SHIFTBITS 5 + +#define FW_SIZE_32k 32768 +#define FW_SIZE_60k 61440 +#define FW_SIZE_64k 65536 +#define FW_SIZE_124k 126976 +#define FW_SIZE_128k 131072 + +#define NO_ERR 0 +#define READY_TO_SERVE 1 +#define WORK_OUT 2 +#define I2C_FAIL -1 +#define HX_INIT_FAIL -1 +#define MEM_ALLOC_FAIL -2 +#define CHECKSUM_FAIL -3 +#define GESTURE_DETECT_FAIL -4 +#define INPUT_REGISTER_FAIL -5 +#define FW_NOT_READY -6 +#define LENGTH_FAIL -7 +#define OPEN_FILE_FAIL -8 +#define ERR_WORK_OUT -10 +#define ERR_STS_WRONG -11 +#define ERR_TEST_FAIL -12 +#define HW_CRC_FAIL 1 + +#define HX_FINGER_ON 1 +#define HX_FINGER_LEAVE 2 + +#if defined(HX_PALM_REPORT) +#define PALM_REPORT 1 +#define PALM_LEAVE_REPORT 2 +#define NOT_REPORT -1 +#endif + +#if defined(HX_PEN_FUNC_EN) +#define PEN_INFO_SZ 12 +#endif + +#if defined(__EMBEDDED_FW__) +extern const uint8_t _binary___Himax_firmware_bin_start[]; +extern const uint8_t _binary___Himax_firmware_bin_end[]; +extern struct firmware g_embedded_fw; +#endif + +enum HX_TS_PATH { + HX_REPORT_COORD = 1, + HX_REPORT_SMWP_EVENT, + HX_REPORT_COORD_RAWDATA, +}; + +enum HX_TS_STATUS { + HX_TS_GET_DATA_FAIL = -4, + HX_ESD_EVENT, + HX_CHKSUM_FAIL, + HX_PATH_FAIL, + HX_TS_NORMAL_END = 0, + HX_ESD_REC_OK, + HX_READY_SERVE, + HX_REPORT_DATA, + HX_ESD_WARNING, + HX_IC_RUNNING, + HX_ZERO_EVENT_COUNT, + HX_RST_OK, +}; + +enum cell_type { + CHIP_IS_ON_CELL, + CHIP_IS_IN_CELL +}; +#ifdef HX_FIX_TOUCH_INFO +enum fix_touch_info { + FIX_HX_RX_NUM = 36, + FIX_HX_TX_NUM = 18, + FIX_HX_BT_NUM = 0, + FIX_HX_X_RES = 1080, + FIX_HX_Y_RES = 2280, + FIX_HX_MAX_PT = 10, + FIX_HX_XY_REVERSE = false, + FIX_HX_INT_IS_EDGE = true, +#ifdef HX_TP_PROC_2T2R + FIX_HX_RX_NUM_2 = 36, + FIX_HX_TX_NUM_2 = 18, +#endif +}; +#endif + +#ifdef HX_ZERO_FLASH + #define HX_SPI_OPERATION + #define HX_0F_DEBUG +#endif +struct himax_ic_data { + int vendor_fw_ver; + int vendor_config_ver; + int vendor_touch_cfg_ver; + int vendor_display_cfg_ver; + int vendor_cid_maj_ver; + int vendor_cid_min_ver; + int vendor_panel_ver; + int vendor_sensor_id; +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#ifdef HX_EN_DYNAMIC_NAME + int vendor_ic_ver; + int vendor_old_ic_ver; + uint8_t vendor_semifac; +#endif +#endif + uint8_t vendor_cus_info[12]; + uint8_t vendor_proj_info[12]; + uint8_t vendor_ic_id[13]; + int HX_RX_NUM; + int HX_TX_NUM; + int HX_BT_NUM; + int HX_X_RES; + int HX_Y_RES; + int HX_MAX_PT; + bool HX_XY_REVERSE; + bool HX_INT_IS_EDGE; +#ifdef HX_TP_PROC_2T2R + int HX_RX_NUM_2; + int HX_TX_NUM_2; +#endif +}; + +struct himax_virtual_key { + int index; + int keycode; + int x_range_min; + int x_range_max; + int y_range_min; + int y_range_max; +}; + +struct himax_target_report_data { + int *x; + int *y; + int *w; + int *finger_id; + int finger_on; + int finger_num; +#ifdef HX_PLATFORM_DEFINE_KEY + int key_size; + int *key_x; + int *key_y; + int *key_w; +#endif +#ifdef HX_SMART_WAKEUP + int SMWP_event_chk; +#endif +#if defined(HX_PEN_FUNC_EN) + int32_t *p_x; + int32_t *p_y; + int32_t *p_w; + int32_t *pen_id; + uint32_t *p_hover; + int32_t *p_tilt_x; + uint32_t *p_btn; + uint32_t *p_btn2; + int32_t *p_tilt_y; + uint32_t *p_on; +#endif + int ig_count; + +}; + +struct himax_report_data { + int touch_all_size; + int raw_cnt_max; + int raw_cnt_rmd; + int touch_info_size; + uint8_t finger_num; + uint8_t finger_on; + uint8_t *hx_coord_buf; + uint8_t hx_state_info[2]; +#if defined(HX_SMART_WAKEUP) + int event_size; + uint8_t *hx_event_buf; +#endif + + int rawdata_size; + uint8_t diag_cmd; + uint8_t *hx_rawdata_buf; + uint8_t rawdata_frame_size; +}; + +struct himax_ts_data { + bool initialized; + bool suspended; + atomic_t resume_update_fail; + atomic_t resume_thread_is_runing; + atomic_t suspend_mode; + struct mutex suspend_resume_mutex; + uint8_t x_channel; + uint8_t y_channel; + uint8_t useScreenRes; + uint8_t diag_cmd; + char chip_name[30]; + uint8_t chip_cell_type; + + uint8_t protocol_type; + uint8_t first_pressed; + uint8_t coord_data_size; + uint8_t area_data_size; + uint8_t coordInfoSize; + uint8_t raw_data_frame_size; + uint8_t raw_data_nframes; + uint8_t nFinger_support; + uint8_t irq_enabled; + uint8_t diag_self[50]; + + uint16_t finger_pressed; + uint16_t last_slot; + uint16_t pre_finger_mask; + uint16_t old_finger; + int hx_point_num; + + + uint32_t debug_log_level; + uint32_t widthFactor; + uint32_t heightFactor; + uint32_t tw_x_min; + uint32_t tw_x_max; + uint32_t tw_y_min; + uint32_t tw_y_max; + uint32_t pl_x_min; + uint32_t pl_x_max; + uint32_t pl_y_min; + uint32_t pl_y_max; + + int rst_gpio; + int use_irq; + int (*power)(int on); + int pre_finger_data[10][2]; + bool vdd_1v8_always_on; + + struct device *dev; + struct workqueue_struct *himax_wq; + struct work_struct work; + struct input_dev *input_dev; +#if defined(HX_PEN_FUNC_EN) + struct input_dev *hx_pen_dev; +#endif + struct hrtimer timer; + struct i2c_client *client; + struct himax_i2c_platform_data *pdata; + struct himax_virtual_key *button; + struct mutex rw_lock; + +/******* SPI-start *******/ + struct mutex spi_lock; + struct spi_device *spi; + int hx_irq; + uint8_t *xfer_buff; +/******* SPI-end *******/ + + int in_self_test; + int suspend_resume_done; + int bus_speed; + +#if defined(CONFIG_FB) || defined(CONFIG_DRM) + struct notifier_block fb_notif; + struct workqueue_struct *himax_att_wq; + struct delayed_work work_att; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif + + struct workqueue_struct *flash_wq; + struct work_struct flash_work; + +#ifdef HX_AUTO_UPDATE_FW + struct workqueue_struct *himax_update_wq; + struct delayed_work work_update; +#endif + +#ifdef HX_ZERO_FLASH + struct workqueue_struct *himax_0f_update_wq; + struct delayed_work work_0f_update; +#endif +#ifdef HX_RESUME_SET_FW + struct workqueue_struct *ts_int_workqueue; +#if defined(__HIMAX_HX83102D_MOD__) + struct delayed_work ts_int_work; +#else + struct work_struct ts_int_work; +#endif +#endif + + struct workqueue_struct *himax_diag_wq; + struct delayed_work himax_diag_delay_wrok; + +#if defined (HX_SMART_WAKEUP) || defined (HX_CODE_OVERLAY) + uint8_t SMWP_enable; + uint8_t gesture_cust_en[26]; + struct wakeup_source ts_SMWP_wake_lock; +#ifdef HX_P_SENSOR + bool psensor_flag; +#endif +#endif + +#ifdef HX_EDGE_LIMIT + uint8_t edge_limit_enable; + uint8_t edge_limit_en[8]; +#endif + +#ifdef HX_HIGH_SENSE + uint8_t HSEN_enable; +#endif + +#if defined(HX_USB_DETECT_CALLBACK) || defined(HX_USB_DETECT_GLOBAL) + uint8_t usb_connected; + uint8_t *cable_config; + struct notifier_block charger_notif; +#endif + +#ifdef HX_TP_PROC_GUEST_INFO + struct workqueue_struct *guest_info_wq; + struct work_struct guest_info_work; +#endif + +#ifdef HIMAX_PALM_SENSOR_EN + bool palm_detection_enabled; + enum palm_sensor_lazy_set palm_detection_lazy_set; + struct himax_sensor_platform_data *palm_sensor_pdata; + struct timer_list palm_release_fimer; + unsigned int palm_release_delay_ms; +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock palm_gesture_wakelock; +#else + struct wakeup_source palm_gesture_wakelock; +#endif +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock palm_gesture_read_wakelock; +#else + struct wakeup_source palm_gesture_read_wakelock; +#endif +#endif + +#ifdef HIMAX_V2_SENSOR_EN + struct himax_tap_sensor_platform_data *sensor_pdata; +#endif + +}; + +struct himax_debug { + bool flash_dump_going; + void (*fp_ts_dbg_func)(struct himax_ts_data *ts, int start); + int (*fp_set_diag_cmd)(struct himax_ic_data *ic_data, struct himax_report_data *hx_touch_data); +}; + +enum input_protocol_type { + PROTOCOL_TYPE_A = 0x00, + PROTOCOL_TYPE_B = 0x01, +}; + +#ifdef HX_HIGH_SENSE + void himax_set_HSEN_func(uint8_t HSEN_enable); +#endif + +#ifdef HX_SMART_WAKEUP +void himax_set_SMWP_func(uint8_t SMWP_enable); + +#define GEST_PTLG_ID_LEN (4) +#define GEST_PTLG_HDR_LEN (4) +#define GEST_PTLG_HDR_ID1 (0xCC) +#define GEST_PTLG_HDR_ID2 (0x44) +#define GEST_PT_MAX_NUM (128) + +extern uint8_t *wake_event_buffer; +#endif + +extern int irq_enable_count; +extern int g_mmi_refcnt; +extern int *g_inspt_crtra_flag; +extern uint32_t g_hx_chip_inited; +/*void himax_HW_reset(uint8_t loadconfig,uint8_t int_off);*/ + +int himax_chip_common_suspend(struct himax_ts_data *ts); +int himax_chip_common_resume(struct himax_ts_data *ts); + +extern struct filename* (*kp_getname_kernel)(const char *filename); +extern struct file * (*kp_file_open_name)(struct filename *name, int flags, umode_t mode); + +struct himax_core_fp; +extern struct himax_core_fp g_core_fp; +extern struct himax_ts_data *private_ts; +extern struct himax_ic_data *ic_data; +extern struct device *g_device; + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_DEBUG + int himax_debug_init(void); + int himax_debug_remove(void); +#endif + +#if defined(HX_PLATFOME_DEFINE_KEY) + void himax_platform_key(void); +#endif + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + extern char *g_rslt_data; + extern void (*fp_himax_self_test_init)(void); +#endif + +int himax_parse_dt(struct himax_ts_data *ts, struct himax_i2c_platform_data *pdata); +int himax_report_data(struct himax_ts_data *ts, int ts_path, int ts_status); + +int himax_report_data_init(void); + +int himax_dev_set(struct himax_ts_data *ts); +int himax_input_register_device(struct input_dev *input_dev); + +#endif + diff --git a/drivers/input/touchscreen/himax_v2_mmi/himax_debug.c b/drivers/input/touchscreen/himax_v2_mmi/himax_debug.c new file mode 100755 index 000000000000..b41724bd3cb2 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/himax_debug.c @@ -0,0 +1,2864 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for debug nodes + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_debug.h" +#include "himax_ic_core.h" + +#ifdef HX_TP_PROC_2T2R + bool Is_2T2R; + int HX_RX_NUM_2; + int HX_TX_NUM_2; +#endif + +uint8_t g_diag_arr_num; + +int g_max_mutual; +int g_min_mutual = 0xFFFF; +int g_max_self; +int g_min_self = 0xFFFF; + +/* moved from debug.h */ + +struct proc_dir_entry *himax_proc_vendor_file; +uint8_t byte_length; +uint8_t register_command[4]; +uint8_t cfg_flag; + +struct proc_dir_entry *himax_proc_stack_file; +struct proc_dir_entry *himax_proc_iir_file; +struct proc_dir_entry *himax_proc_dc_file; +struct proc_dir_entry *himax_proc_bank_file; +bool dsram_flag; + +#ifdef HX_TP_PROC_2T2R +uint32_t *diag_mutual_2; +#endif +int32_t *diag_mutual; +int32_t *diag_mutual_new; +int32_t *diag_mutual_old; +uint8_t diag_max_cnt; +uint8_t hx_state_info[2]; +uint8_t diag_coor[128]; +int32_t diag_self[100]; +int32_t diag_self_new[100]; +int32_t diag_self_old[100]; + +struct proc_dir_entry *himax_proc_debug_file; +bool fw_update_complete; +int handshaking_result; +unsigned char debug_level_cmd; +uint8_t cmd_set[8]; +uint8_t mutual_set_flag; + +struct proc_dir_entry *himax_proc_flash_dump_file; +int Flash_Size = 131072; +uint8_t *flash_buffer; +uint8_t g_flash_cmd; +uint8_t g_flash_progress; +bool g_flash_dump_rst; /*Fail = 0, Pass = 1*/ + +uint32_t **raw_data_array; +uint8_t X_NUM; +uint8_t Y_NUM; +uint8_t sel_type = 0x0D; + +/* Moved from debug.h End */ +char buf_tmp[BUF_SIZE] = {0}; +uint8_t *reg_read_data = NULL; +#define DEBUG_READ_BUF_SIZE 128 + + +#if defined(HX_PEN_FUNC_EN) + struct proc_dir_entry *himax_proc_pen_pos_file; +#endif + +struct timespec timeStart, timeEnd, timeDelta; +int g_switch_mode; +/* + * Segment : Himax PROC Debug Function + */ + +#if defined(HX_PEN_FUNC_EN) +static ssize_t himax_pen_ops_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + int i = 0; + ssize_t ret = 0; + + if (!HX_PROC_SEND_FLAG) { + for (i = 0; i < private_ts->nFinger_support; i++) { + if (g_target_report_data->x[i] >= 0 && g_target_report_data->x[i] <= private_ts->pdata->abs_x_max + && g_target_report_data->y[i] >= 0 && g_target_report_data->y[i] <= private_ts->pdata->abs_y_max + && private_ts->hx_point_num != 0) { + ret += snprintf(buf_tmp + ret, len - ret, + "%d,%d,", g_target_report_data->x[i], g_target_report_data->y[i]); + } else { + ret += snprintf(buf_tmp + ret, len - ret, + "65535,65535,"); + } + } + if (g_target_report_data->p_x[0] >= 0 && g_target_report_data->p_x[0] <= private_ts->pdata->abs_x_max + && g_target_report_data->p_y[0] >= 0 && g_target_report_data->p_y[0] <= private_ts->pdata->abs_y_max + && (g_target_report_data->p_on[0] == 1)) { + ret += snprintf(buf_tmp + ret, len - ret, + "%d,%d,", g_target_report_data->p_x[0], g_target_report_data->p_y[0]); + } else { + ret += snprintf(buf_tmp + ret, len - ret, + "65535,65535,"); + } + + ret += snprintf(buf_tmp + ret, len - ret, + "%d,", g_target_report_data->p_w[0]); + + ret += snprintf(buf_tmp + ret, len - ret, + "%d,", g_target_report_data->p_hover[0]); + + if (g_target_report_data->p_btn[0] != 0xff) { + ret += snprintf(buf_tmp + ret, len - ret, + "%d,", g_target_report_data->p_btn[0]); + } else { + ret += snprintf(buf_tmp + ret, len - ret, + "0,"); + } + if (g_target_report_data->p_btn2[0] != 0xff) { + ret += snprintf(buf_tmp + ret, len - ret, + "%d,", g_target_report_data->p_btn2[0]); + } else { + ret += snprintf(buf_tmp + ret, len - ret, + "0,"); + } + + if (g_target_report_data->p_tilt_x[0] != 0xff) { + ret += snprintf(buf_tmp + ret, len - ret, + "%d,", g_target_report_data->p_tilt_x[0]); + } else { + ret += snprintf(buf_tmp + ret, len - ret, + "0,"); + } + if (g_target_report_data->p_tilt_y[0] != 0xff) { + ret += snprintf(buf_tmp + ret, len - ret, + "%d\n", g_target_report_data->p_tilt_y[0]); + } else { + ret += snprintf(buf_tmp + ret, len - ret, + "0\n"); + } + + if (copy_to_user(buf, buf_tmp, (len > BUF_SIZE) ? BUF_SIZE : len)) + I("%s,here:%d\n", __func__, __LINE__); + + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static const struct file_operations himax_proc_pen_pos_ops = { + .owner = THIS_MODULE, + .read = himax_pen_ops_read, +}; +#endif + +static ssize_t himax_crc_test_read(char *buf, size_t len) +{ + ssize_t ret = 0; + uint8_t result = 0; + uint32_t size = 0; + + g_core_fp.fp_sense_off(true); + msleep(20); + if (g_core_fp._diff_overlay_flash() == 1) + size = FW_SIZE_128k; + else + size = FW_SIZE_64k; + result = g_core_fp.fp_calculateChecksum(false, size); + g_core_fp.fp_sense_on(0x01); + + if (result) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CRC test is Pass!\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CRC test is Fail!\n"); + + return ret; +} + +static ssize_t himax_vendor_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + ssize_t ret = 0; + + if (!HX_PROC_SEND_FLAG) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "FW_VER = 0x%2.2X\n", + ic_data->vendor_fw_ver); + + if (private_ts->chip_cell_type == CHIP_IS_ON_CELL) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CONFIG_VER = 0x%2.2X\n", + ic_data->vendor_config_ver); + } else { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "TOUCH_VER = 0x%2.2X\n", + ic_data->vendor_touch_cfg_ver); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "DISPLAY_VER = 0x%2.2X\n", + ic_data->vendor_display_cfg_ver); + } + + if (ic_data->vendor_cid_maj_ver < 0 && + ic_data->vendor_cid_min_ver < 0) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CID_VER = NULL\n"); + } else { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CID_VER = 0x%2.2X\n", + (ic_data->vendor_cid_maj_ver << 8 | + ic_data->vendor_cid_min_ver)); + } + + if (ic_data->vendor_panel_ver < 0) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "PANEL_VER = NULL\n"); + } else { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "PANEL_VER = 0x%2.2X\n", + ic_data->vendor_panel_ver); + } + if (private_ts->chip_cell_type == CHIP_IS_IN_CELL) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Cusomer = %s\n", ic_data->vendor_cus_info); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Project = %s\n", ic_data->vendor_proj_info); + } +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#ifdef HX_EN_DYNAMIC_NAME + if (ic_data->vendor_semifac == 1) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "SEMI_FAC = UMC\n"); + } else if (ic_data->vendor_semifac == 2) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "SEMI_FAC = PSC\n"); + } else { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "SEMI_FAC = NULL\n"); + } +#endif +#endif + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Himax Touch Driver Version:\n"); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%s\n", + HIMAX_DRIVER_VER); + HX_PROC_SEND_FLAG = 1; + + if (copy_to_user(buf, buf_tmp, (len > BUF_SIZE) ? BUF_SIZE : len)) + I("%s,here:%d\n", __func__, __LINE__); + + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static const struct file_operations himax_proc_vendor_ops = { + .owner = THIS_MODULE, + .read = himax_vendor_read, +}; + +static ssize_t himax_attn_read(char *buf, size_t len) +{ + ssize_t ret = 0; + struct himax_ts_data *ts_data; + + ts_data = private_ts; + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "attn = %x\n", + himax_int_gpio_read(ts_data->pdata->gpio_irq)); + + return ret; +} + +static ssize_t himax_int_en_read(char *buf, size_t len) +{ + struct himax_ts_data *ts = private_ts; + size_t ret = 0; + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%d\n", + ts->irq_enabled); + + return ret; +} + +static ssize_t himax_int_en_write(char *buf, size_t len) +{ + struct himax_ts_data *ts = private_ts; + int value, ret = 0; + + if (len >= 12) { + I("%s: no command exceeds 12 chars.\n", __func__); + return -EFAULT; + } + + if (buf[0] == '0') + value = false; + else if (buf[0] == '1') + value = true; + else + return -EINVAL; + + if (value) { + ret = himax_int_en_set(); + + if (ret == 0) { + ts->irq_enabled = 1; + irq_enable_count = 1; + } + } else { + himax_int_enable(0); + free_irq(ts->hx_irq, ts); + ts->irq_enabled = 0; + } + + return len; +} + +static ssize_t himax_layout_read(char *buf, size_t len) +{ + struct himax_ts_data *ts = private_ts; + size_t ret = 0; + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%d ", + ts->pdata->abs_x_min); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%d ", + ts->pdata->abs_x_max); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%d ", + ts->pdata->abs_y_min); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%d ", + ts->pdata->abs_y_max); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + + return ret; +} + +static ssize_t himax_layout_write(char *buf, size_t len) +{ + struct himax_ts_data *ts = private_ts; + char buf_tmp[5] = {0}; + int i = 0, j = 0, k = 0, ret; + unsigned long value; + int layout[4] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + for (i = 0; i < 20; i++) { + if (buf[i] == ',' || buf[i] == '\n') { + memset(buf_tmp, 0x0, sizeof(buf_tmp)); + + if (i - j <= 5) { + memcpy(buf_tmp, buf + j, i - j); + } else { + I("buffer size is over 5 char\n"); + return len; + } + + j = i + 1; + + if (k < 4) { + ret = kstrtoul(buf_tmp, 10, &value); + layout[k++] = value; + } + } + } + + if (k == 4) { + ts->pdata->abs_x_min = layout[0]; + ts->pdata->abs_x_max = (layout[1] - 1); + ts->pdata->abs_y_min = layout[2]; + ts->pdata->abs_y_max = (layout[3] - 1); + I("%d, %d, %d, %d\n", + ts->pdata->abs_x_min, ts->pdata->abs_x_max, + ts->pdata->abs_y_min, ts->pdata->abs_y_max); + input_unregister_device(ts->input_dev); + himax_input_register(ts); + } else { + I("ERR@%d, %d, %d, %d\n", + ts->pdata->abs_x_min, ts->pdata->abs_x_max, + ts->pdata->abs_y_min, ts->pdata->abs_y_max); + } + + return len; +} + +static ssize_t himax_debug_level_read(char *buf, size_t len) +{ + struct himax_ts_data *ts_data; + size_t ret = 0; + + ts_data = private_ts; + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%d\n", + ts_data->debug_log_level); + + if (copy_to_user(buf, buf_tmp, (len > BUF_SIZE) ? BUF_SIZE : len)) + I("%s,here:%d\n", __func__, __LINE__); + + return ret; +} + +static ssize_t himax_debug_level_write(char *buf, size_t len) +{ + struct himax_ts_data *ts; + int i; + + ts = private_ts; + + if (len >= 12) { + I("%s: no command exceeds 12 chars.\n", __func__); + return -EFAULT; + } + + ts->debug_log_level = 0; + + for (i = 0; i < len; i++) { + if (buf[i] >= '0' && buf[i] <= '9') + ts->debug_log_level |= (buf[i] - '0'); + else if (buf[i] >= 'A' && buf[i] <= 'F') + ts->debug_log_level |= (buf[i] - 'A' + 10); + else if (buf[i] >= 'a' && buf[i] <= 'f') + ts->debug_log_level |= (buf[i] - 'a' + 10); + + if (i != len - 1) + ts->debug_log_level <<= 4; + } + I("Now debug level value=%d\n", ts->debug_log_level); + if (ts->debug_log_level & BIT(4)) { + I("Turn on/Enable Debug Mode for Inspection!\n"); + goto END_FUNC; + } + + if (ts->debug_log_level & BIT(3)) { + if (ts->pdata->screenWidth > 0 && ts->pdata->screenHeight > 0 && + (ts->pdata->abs_x_max - ts->pdata->abs_x_min) > 0 && + (ts->pdata->abs_y_max - ts->pdata->abs_y_min) > 0) { + ts->widthFactor = (ts->pdata->screenWidth << SHIFTBITS) + / (ts->pdata->abs_x_max - ts->pdata->abs_x_min); + ts->heightFactor = + (ts->pdata->screenHeight << SHIFTBITS) / + (ts->pdata->abs_y_max - ts->pdata->abs_y_min); + + if (ts->widthFactor > 0 && ts->heightFactor > 0) { + ts->useScreenRes = 1; + } else { + ts->heightFactor = 0; + ts->widthFactor = 0; + ts->useScreenRes = 0; + } + } else { + I("Enable finger debug with raw position mode!\n"); + } + } else { + ts->useScreenRes = 0; + ts->widthFactor = 0; + ts->heightFactor = 0; + } +END_FUNC: + return len; +} + +static ssize_t himax_proc_register_read(char *buf, size_t len) +{ + int ret = 0; + uint16_t loop_i; + + if(reg_read_data == NULL){ + E("%s reg_read_data is null \n",__func__); + return ret; + } + memset(reg_read_data, 0x00, DEBUG_READ_BUF_SIZE * sizeof(uint8_t)); + + I("himax_register_show: %02X,%02X,%02X,%02X\n", + register_command[3], register_command[2], + register_command[1], register_command[0]); + g_core_fp.fp_register_read(register_command, 128, reg_read_data, + cfg_flag); + ret += snprintf(buf_tmp + ret, len - ret, + "command: %02X,%02X,%02X,%02X\n", + register_command[3], register_command[2], + register_command[1], register_command[0]); + + for (loop_i = 0; loop_i < 128; loop_i++) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "0x%2.2X ", + reg_read_data[loop_i]); + if ((loop_i % 16) == 15) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + + } + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + + return ret; +} + +static ssize_t himax_proc_register_write(char *buf, size_t len) +{ + char buff_tmp[16] = {0}; + uint8_t length = 0; + unsigned long result = 0; + uint8_t loop_i = 0; + uint16_t base = 2; + char *data_str = NULL; + uint8_t w_data[20] = {0}; + uint8_t x_pos[20] = {0}; + uint8_t count = 0; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + memset(register_command, 0x0, sizeof(register_command)); + + I("himax %s\n", buf); + + if ((buf[0] == 'r' || buf[0] == 'w') && buf[1] == ':' && + buf[2] == 'x') { + length = strlen(buf); + + /* I("%s: length = %d.\n", __func__,length); */ + for (loop_i = 0; loop_i < length; loop_i++) { + /* find postion of 'x' */ + if (buf[loop_i] == 'x') { + x_pos[count] = loop_i; + count++; + } + } + + data_str = strrchr(buf, 'x'); + I("%s: %s.\n", __func__, data_str); + length = strlen(data_str + 1); + + switch (buf[0]) { + case 'r': + if (buf[3] == 'F' && buf[4] == 'E' && length == 4) { + length = length - base; + cfg_flag = 1; + memcpy(buff_tmp, data_str + base + 1, length); + } else { + cfg_flag = 0; + memcpy(buff_tmp, data_str + 1, length); + } + byte_length = length / 2; + if (!kstrtoul(buff_tmp, 16, &result)) { + for (loop_i = 0; loop_i < byte_length; loop_i++) + register_command[loop_i] = + (uint8_t)(result >> loop_i * 8); + } + + if (strcmp(HX_85XX_H_SERIES_PWON, + private_ts->chip_name) == 0 && cfg_flag == 0) + cfg_flag = 2; + break; + case 'w': + if (buf[3] == 'F' && buf[4] == 'E') { + cfg_flag = 1; + memcpy(buff_tmp, buf + base + 3, length); + } else { + cfg_flag = 0; + memcpy(buff_tmp, buf + 3, length); + } + + if (count < 3) { + byte_length = length / 2; + + if (!kstrtoul(buff_tmp, 16, &result)) { + /* command */ + for (loop_i = 0; loop_i < byte_length; + loop_i++) { + register_command[loop_i] = + (uint8_t)(result >> loop_i * 8); + } + } + + if (!kstrtoul(data_str + 1, 16, &result)) { + /* data */ + for (loop_i = 0; loop_i < byte_length; + loop_i++) { + w_data[loop_i] = + (uint8_t)(result >> loop_i * 8); + } + } + + g_core_fp.fp_register_write(register_command, + byte_length, w_data, cfg_flag); + } else { + for (loop_i = 0; loop_i < count; loop_i++) { + /* parsing addr after 'x' */ + memset(buff_tmp, 0x0, sizeof(buff_tmp)); + if (cfg_flag != 0 && loop_i != 0) + byte_length = 2; + else + byte_length = x_pos[1] - + x_pos[0] - 2;/* original */ + memcpy(buff_tmp, buf + x_pos[loop_i] + 1, + byte_length); + + /* I("%s: buff_tmp = %s\n", __func__, + * buff_tmp); + */ + if (!kstrtoul(buff_tmp, 16, &result)) { + if (loop_i == 0) { + register_command[loop_i] + = (uint8_t)(result); + /* I("%s: + * register_command + * = %X\n", __func__, + * register_command[0]); + */ + } else { + w_data[loop_i - 1] = + (uint8_t)(result); + /* I("%s: w_data[%d] = + * %2X\n", __func__, + * loop_i - 1, + * w_data[loop_i - 1]); + */ + } + } + } + + byte_length = count - 1; + if (strcmp(HX_85XX_H_SERIES_PWON, + private_ts->chip_name) == 0 && cfg_flag == 0) + cfg_flag = 2; + g_core_fp.fp_register_write(register_command, + byte_length, &w_data[0], cfg_flag); + + } + break; + }; + } + return len; +} + +int32_t *getMutualBuffer(void) +{ + return diag_mutual; +} +int32_t *getMutualNewBuffer(void) +{ + return diag_mutual_new; +} +int32_t *getMutualOldBuffer(void) +{ + return diag_mutual_old; +} +int32_t *getSelfBuffer(void) +{ + return &diag_self[0]; +} +int32_t *getSelfNewBuffer(void) +{ + return &diag_self_new[0]; +} +int32_t *getSelfOldBuffer(void) +{ + return &diag_self_old[0]; +} +void setMutualBuffer(uint8_t x_num, uint8_t y_num) +{ + diag_mutual = kzalloc(x_num * y_num * sizeof(int32_t), GFP_KERNEL); +} +void setMutualNewBuffer(uint8_t x_num, uint8_t y_num) +{ + diag_mutual_new = kzalloc(x_num * y_num * sizeof(int32_t), GFP_KERNEL); +} +void setMutualOldBuffer(uint8_t x_num, uint8_t y_num) +{ + diag_mutual_old = kzalloc(x_num * y_num * sizeof(int32_t), GFP_KERNEL); +} + +#ifdef HX_TP_PROC_2T2R +int32_t *getMutualBuffer_2(void) +{ + return diag_mutual_2; +} +void setMutualBuffer_2(uint8_t x_num_2, uint8_t y_num_2) +{ + diag_mutual_2 = + kzalloc(x_num_2 * y_num_2 * sizeof(int32_t), GFP_KERNEL); +} +#endif + +int himax_set_diag_cmd(struct himax_ic_data *ic_data, + struct himax_report_data *hx_touch_data) +{ + struct himax_ts_data *ts = private_ts; + int32_t *mutual_data; + int32_t *self_data; + int mul_num; + int self_num; + /* int RawDataLen = 0; */ + hx_touch_data->diag_cmd = ts->diag_cmd; + + if (hx_touch_data->diag_cmd >= 1 && hx_touch_data->diag_cmd <= 7) { + /* Check event stack CRC */ + if (!g_core_fp.fp_diag_check_sum(hx_touch_data)) + goto bypass_checksum_failed_packet; + +#ifdef HX_TP_PROC_2T2R + if (Is_2T2R && (hx_touch_data->diag_cmd >= 4 && + hx_touch_data->diag_cmd <= 6)) { + mutual_data = getMutualBuffer_2(); + self_data = getSelfBuffer(); + /* initiallize the block number of mutual and self */ + mul_num = ic_data->HX_RX_NUM_2 * ic_data->HX_TX_NUM_2; +#ifdef HX_EN_SEL_BUTTON + self_num = ic_data->HX_RX_NUM_2 + + ic_data->HX_TX_NUM_2 + ic_data->HX_BT_NUM; +#else + self_num = ic_data->HX_RX_NUM_2 + ic_data->HX_TX_NUM_2; +#endif + } else +#endif + { + mutual_data = getMutualBuffer(); + self_data = getSelfBuffer(); + /* initiallize the block number of mutual and self */ + mul_num = ic_data->HX_RX_NUM * ic_data->HX_TX_NUM; +#ifdef HX_EN_SEL_BUTTON + self_num = ic_data->HX_RX_NUM + + ic_data->HX_TX_NUM + ic_data->HX_BT_NUM; +#else + self_num = ic_data->HX_RX_NUM + ic_data->HX_TX_NUM; +#endif + } + g_core_fp.fp_diag_parse_raw_data(hx_touch_data, mul_num, + self_num, hx_touch_data->diag_cmd, mutual_data, + self_data); + } else if (hx_touch_data->diag_cmd == 8) { + memset(diag_coor, 0x00, sizeof(diag_coor)); + memcpy(&(diag_coor[0]), &hx_touch_data->hx_coord_buf[0], + hx_touch_data->touch_info_size); + } + + /* assign state info data */ + memcpy(&(hx_state_info[0]), &hx_touch_data->hx_state_info[0], 2); + return NO_ERR; +bypass_checksum_failed_packet: + return 1; +} + +/* #if defined(HX_DEBUG_LEVEL) */ +void himax_log_touch_data(int start) +{ + int loop_i = 0; + int print_size = 0; + uint8_t *buf; + + if (start == 1) + return; /* report data when end of ts_work*/ + + if (hx_touch_data->diag_cmd > 0) { + print_size = hx_touch_data->touch_all_size; + buf = kcalloc(print_size, sizeof(uint8_t), GFP_KERNEL); + memcpy(buf, hx_touch_data->hx_coord_buf, + hx_touch_data->touch_info_size); + memcpy(&buf[hx_touch_data->touch_info_size], + hx_touch_data->hx_rawdata_buf, + print_size - hx_touch_data->touch_info_size); + } +#if defined(HX_SMART_WAKEUP) + else if (private_ts->SMWP_enable > 0 && private_ts->suspended) { + print_size = hx_touch_data->event_size; + buf = kcalloc(print_size, sizeof(uint8_t), GFP_KERNEL); + memcpy(buf, hx_touch_data->hx_event_buf, print_size); + } +#endif + else if (hx_touch_data->diag_cmd == 0) { + print_size = hx_touch_data->touch_info_size; + buf = kcalloc(print_size, sizeof(uint8_t), GFP_KERNEL); + memcpy(buf, hx_touch_data->hx_coord_buf, print_size); + } else { + E("%s:cmd fault\n", __func__); + } + + for (loop_i = 0; loop_i < print_size; loop_i += 8) { + if ((loop_i + 7) >= print_size) { + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", + loop_i, buf[loop_i], loop_i + 1, buf[loop_i + 1]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X\n", + loop_i + 2, buf[loop_i + 2], loop_i + 3, buf[loop_i + 3]); + break; + } + + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", + loop_i, buf[loop_i], loop_i + 1, buf[loop_i + 1]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", + loop_i + 2, buf[loop_i + 2], loop_i + 3, buf[loop_i + 3]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", + loop_i + 4, buf[loop_i + 4], loop_i + 5, buf[loop_i + 5]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", + loop_i + 6, buf[loop_i + 6], loop_i + 7, buf[loop_i + 7]); + I("\n"); + } + kfree(buf); +} +void himax_log_touch_event(struct himax_ts_data *ts, int start) +{ + int loop_i = 0; + + if (start == 1) + return; /*report data when end of ts_work*/ + + if (g_target_report_data->finger_on > 0 && + g_target_report_data->finger_num > 0) { + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + if (g_target_report_data->x[loop_i] >= 0 + && g_target_report_data->x[loop_i] + <= ts->pdata->abs_x_max + && g_target_report_data->y[loop_i] >= 0 + && g_target_report_data->y[loop_i] + <= ts->pdata->abs_y_max) { + I("Finger %d=> X:%d, Y:%d W:%d, Z:%d, F:%d,Int_Delay_Counter:%d\n", + loop_i + 1, + g_target_report_data->x[loop_i], + g_target_report_data->y[loop_i], + g_target_report_data->w[loop_i], + g_target_report_data->w[loop_i], + loop_i + 1, + g_target_report_data->ig_count); + + } + } + } else if (g_target_report_data->finger_on == 0 && + g_target_report_data->finger_num == 0) { + I("All Finger leave\n"); + } else { + I("%s : wrong input!\n", __func__); + } +} +void himax_log_touch_int_devation(int touched) +{ + if (touched == HX_FINGER_ON) { + getnstimeofday(&timeStart); + /* I(" Irq start time = %ld.%06ld s\n", + * timeStart.tv_sec, timeStart.tv_nsec/1000); + */ + } else if (touched == HX_FINGER_LEAVE) { + getnstimeofday(&timeEnd); + timeDelta.tv_nsec = + (timeEnd.tv_sec * 1000000000 + timeEnd.tv_nsec) - + (timeStart.tv_sec * 1000000000 + timeStart.tv_nsec); + /* I("Irq finish time = %ld.%06ld s\n", + * timeEnd.tv_sec, timeEnd.tv_nsec/1000); + */ + I("Touch latency = %ld us\n", timeDelta.tv_nsec / 1000); + I("bus_speed = %d kHz\n", private_ts->bus_speed); + if (g_target_report_data->finger_on == 0 && g_target_report_data->finger_num == 0) + I("All Finger leave\n"); + } else { + I("%s : wrong input!\n", __func__); + } +} + +#define RAW_DOWN_STATUS "status: Raw:F:%02d Down, X:%d, Y:%d, W:%d\n" +#define RAW_UP_STATUS "status: Raw:F:%02d Up, X:%d, Y:%d\n" + +void himax_log_touch_event_detail(struct himax_ts_data *ts, int start) +{ + int loop_i = 0; + + if (start == HX_FINGER_LEAVE) { + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + if (((ts->old_finger >> loop_i & 1) == 0) && + ((ts->pre_finger_mask >> loop_i & 1) == 1)) { + if (g_target_report_data->x[loop_i] >= 0 + && g_target_report_data->x[loop_i] + <= ts->pdata->abs_x_max + && g_target_report_data->y[loop_i] >= 0 + && g_target_report_data->y[loop_i] + <= ts->pdata->abs_y_max) { + I(RAW_DOWN_STATUS, + loop_i + 1, + g_target_report_data->x[loop_i], + g_target_report_data->y[loop_i], + g_target_report_data->w[loop_i]); + } + } else if ((((ts->old_finger >> loop_i & 1) == 1) && + ((ts->pre_finger_mask >> loop_i & 1) == 0))) { + I(RAW_UP_STATUS, + loop_i + 1, ts->pre_finger_data[loop_i][0], + ts->pre_finger_data[loop_i][1]); + } else { + /* I("dbg hx_point_num=%d, old_finger=0x%02X," + * " pre_finger_mask=0x%02X\n", + * ts->hx_point_num, ts->old_finger, + * ts->pre_finger_mask); + */ + } + } + } +} + +void himax_ts_dbg_func(struct himax_ts_data *ts, int start) +{ + if (ts->debug_log_level & BIT(0)) { + /* I("debug level 1\n"); */ + himax_log_touch_data(start); + } + if (ts->debug_log_level & BIT(1)) { + /* I("debug level 2\n"); */ + himax_log_touch_event(ts, start); + } + if (ts->debug_log_level & BIT(2)) { + /* I("debug level 4\n"); */ + himax_log_touch_int_devation(start); + } + if (ts->debug_log_level & BIT(3)) { + /* I("debug level 8\n"); */ + himax_log_touch_event_detail(ts, start); + } +} + +static int himax_change_mode(uint8_t str_pw, uint8_t end_pw) +{ + uint8_t data[4] = {0}; + int count = 0; + + /*sense off*/ + g_core_fp.fp_sense_off(true); + /*mode change*/ + data[1] = str_pw; data[0] = str_pw; + if (g_core_fp.fp_assign_sorting_mode != NULL) + g_core_fp.fp_assign_sorting_mode(data); + + /*sense on*/ + g_core_fp.fp_sense_on(1); + /*wait mode change*/ + do { + if (g_core_fp.fp_check_sorting_mode != NULL) + g_core_fp.fp_check_sorting_mode(data); + if ((data[0] == end_pw) && (data[1] == end_pw)) + return 0; + + I("Now retry %d times!\n", count++); + msleep(50); + } while (count < 50); + + return ERR_WORK_OUT; +} + +static ssize_t himax_diag_cmd_write(char *buf, size_t len) +{ + struct himax_ts_data *ts = private_ts; + char *dbg_map_str = "mode:"; + char *str_ptr = NULL; + int str_len = 0; + int rst = 0; + uint8_t str_pw = 0; + uint8_t end_pw = 0; + + switch (len) { + case 1:/*raw out select - diag,X*/ + if (!kstrtoint(buf, 16, &rst)) { + ts->diag_cmd = rst; + I("%s: dsram_flag = %d\n", __func__, dsram_flag); + if (dsram_flag) { + /*Cancal work queue and return to stack*/ + process_type = 0; + dsram_flag = false; + cancel_delayed_work(&ts->himax_diag_delay_wrok); + himax_int_enable(1); + g_core_fp.fp_return_event_stack(); + } + g_core_fp.fp_diag_register_set(ts->diag_cmd, 0); + I("%s: Set raw out select 0x%X.\n", __func__, ts->diag_cmd); + } + if (!ts->diag_cmd) { + if (mode_flag) /*back to normal mode*/ + himax_change_mode(0x00, 0x99); + } + break; + case 2:/*data processing + rawout select - diag,XY*/ + if (!kstrtoint(buf, 16, &rst)) { + process_type = (rst >> 4) & 0xF; + ts->diag_cmd = rst & 0xF; + } + if (process_type <= 0 || ts->diag_cmd <= 0) + break; + else if (process_type > 0 && process_type <= 3) { + if (!dsram_flag) { + /*Start wrok queue*/ + himax_int_enable(0); + g_core_fp.fp_diag_register_set(ts->diag_cmd, process_type); + queue_delayed_work(ts->himax_diag_wq, + &ts->himax_diag_delay_wrok, 2 * HZ / 100); + dsram_flag = true; + I("%s: Start get raw data in DSRAM\n", __func__); + } else { + g_core_fp.fp_diag_register_set(ts->diag_cmd, process_type); + } + } + break; + case 9:/*change mode - mode:XXYY(start PW,end PW)*/ + str_ptr = strnstr(buf, dbg_map_str, len); + if (str_ptr) { + str_len = strlen(dbg_map_str); + if (!kstrtoint(buf + str_len, 16, &rst)) { + str_pw = (rst >> 8) & 0xFF; + end_pw = rst & 0xFF; + if (!himax_change_mode(str_pw, end_pw)) { + mode_flag = 1; + I("%s: change mode 0x%4X. str_pw = %2X, end_pw = %2X\n", __func__, rst, str_pw, end_pw); + } else + I("%s: change mode failed. str_pw = %2X, end_pw = %2X\n", __func__, str_pw, end_pw); + } + } else { + I("%s: Can't find string [%s].\n", __func__, dbg_map_str); + } + break; + default: + I("%s: Length is not correct.\n", __func__); + } + return len; +} + +static ssize_t himax_diag_arrange_write(char *buf, size_t len) +{ + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + g_diag_arr_num = buf[0] - '0'; + I("%s: g_diag_arr_num = %d\n", __func__, g_diag_arr_num); + return len; +} + +void himax_get_mutual_edge(void) +{ + int i = 0; + + for (i = 0; i < (ic_data->HX_RX_NUM * ic_data->HX_TX_NUM); i++) { + if (diag_mutual[i] > g_max_mutual) + g_max_mutual = diag_mutual[i]; + + if (diag_mutual[i] < g_min_mutual) + g_min_mutual = diag_mutual[i]; + } +} + +void himax_get_self_edge(void) +{ + int i = 0; + + for (i = 0; i < (ic_data->HX_RX_NUM + ic_data->HX_TX_NUM); i++) { + if (diag_self[i] > g_max_self) + g_max_self = diag_self[i]; + + if (diag_self[i] < g_min_self) + g_min_self = diag_self[i]; + } +} + +static void print_state_info(struct seq_file *s) +{ + /* seq_printf(s, "State_info_2bytes:%3d, %3d\n", + * _state_info[0],hx_state_info[1]); + */ + +#ifdef HX_NEW_EVENT_STACK_FORMAT + seq_printf(s, "ReCal = %d\t", hx_state_info[0] & 0x03); + seq_printf(s, "Base Line = %d\t", hx_state_info[0] >> 2 & 0x01); + seq_printf(s, "Palm = %d\t", hx_state_info[0] >> 3 & 0x01); + seq_printf(s, "Idle mode = %d\t", hx_state_info[0] >> 4 & 0x01); + seq_printf(s, "Water = %d\n", hx_state_info[0] >> 5 & 0x01); + seq_printf(s, "TX Hop = %d\t", hx_state_info[0] >> 6 & 0x01); + seq_printf(s, "AC mode = %d\t", hx_state_info[0] >> 7 & 0x01); + seq_printf(s, "Glove = %d\t", hx_state_info[1] & 0x01); + seq_printf(s, "Stylus = %d\t", hx_state_info[1] >> 1 & 0x01); + seq_printf(s, "Hovering = %d\t", hx_state_info[1] >> 2 & 0x01); + seq_printf(s, "Proximity = %d\t", hx_state_info[1] >> 3 & 0x01); + seq_printf(s, "KEY = %d\n", hx_state_info[1] >> 4 & 0x0F); +#else + seq_printf(s, "ReCal = %d\t", hx_state_info[0] & 0x01); + seq_printf(s, "Palm = %d\t", hx_state_info[0] >> 1 & 0x01); + seq_printf(s, "AC mode = %d\t", hx_state_info[0] >> 2 & 0x01); + seq_printf(s, "Water = %d\n", hx_state_info[0] >> 3 & 0x01); + seq_printf(s, "Glove = %d\t", hx_state_info[0] >> 4 & 0x01); + seq_printf(s, "TX Hop = %d\t", hx_state_info[0] >> 5 & 0x01); + seq_printf(s, "Base Line = %d\t", hx_state_info[0] >> 6 & 0x01); + seq_printf(s, "OSR Hop = %d\t", hx_state_info[1] >> 3 & 0x01); + seq_printf(s, "KEY = %d\n", hx_state_info[1] >> 4 & 0x0F); +#endif +} + +static void himax_diag_arrange_print(struct seq_file *s, int i, int j, + int transpose) +{ + if (transpose) + seq_printf(s, "%6d", diag_mutual[j + i * ic_data->HX_RX_NUM]); + else + seq_printf(s, "%6d", diag_mutual[i + j * ic_data->HX_RX_NUM]); +} + +/* ready to print second step which is column*/ +static void himax_diag_arrange_inloop(struct seq_file *s, int in_init, + int out_init, bool transpose, int j) +{ + int x_channel = ic_data->HX_RX_NUM; + int y_channel = ic_data->HX_TX_NUM; + int i; + int in_max = 0; + + if (transpose) + in_max = y_channel; + else + in_max = x_channel; + + if (in_init > 0) { /* bit0 = 1 */ + for (i = in_init - 1; i >= 0; i--) + himax_diag_arrange_print(s, i, j, transpose); + + if (transpose) { + if (out_init > 0) + seq_printf(s, " %5d\n", diag_self[j]); + else + seq_printf(s, " %5d\n", + diag_self[x_channel - j - 1]); + } + } else { /* bit0 = 0 */ + for (i = 0; i < in_max; i++) + himax_diag_arrange_print(s, i, j, transpose); + + if (transpose) { + if (out_init > 0) + seq_printf(s, " %5d\n", + diag_self[x_channel - j - 1]); + else + seq_printf(s, " %5d\n", diag_self[j]); + } + } +} + +/* print first step which is row */ +static void himax_diag_arrange_outloop(struct seq_file *s, int transpose, + int out_init, int in_init) +{ + int j; + int x_channel = ic_data->HX_RX_NUM; + int y_channel = ic_data->HX_TX_NUM; + int out_max = 0; + int self_cnt = 0; + + if (transpose) + out_max = x_channel; + else + out_max = y_channel; + + if (out_init > 0) { /* bit1 = 1 */ + self_cnt = 1; + + for (j = out_init - 1; j >= 0; j--) { + seq_printf(s, "%3c%02d%c", '[', j + 1, ']'); + himax_diag_arrange_inloop(s, in_init, out_init, + transpose, j); + + if (!transpose) { + seq_printf(s, " %5d\n", + diag_self[y_channel + x_channel - self_cnt]); + self_cnt++; + } + } + } else { /* bit1 = 0 */ + /* self_cnt = x_channel; */ + for (j = 0; j < out_max; j++) { + seq_printf(s, "%3c%02d%c", '[', j + 1, ']'); + himax_diag_arrange_inloop(s, in_init, out_init, + transpose, j); + + if (!transpose) { + seq_printf(s, " %5d\n", + diag_self[j + x_channel]); + } + } + } +} + +/* determin the output format of diag */ +static void himax_diag_arrange(struct seq_file *s) +{ + int x_channel = ic_data->HX_RX_NUM; + int y_channel = ic_data->HX_TX_NUM; + int bit2, bit1, bit0; + int i; + /* rotate bit */ + bit2 = g_diag_arr_num >> 2; + /* reverse Y */ + bit1 = g_diag_arr_num >> 1 & 0x1; + /* reverse X */ + bit0 = g_diag_arr_num & 0x1; + + if (g_diag_arr_num < 4) { + for (i = 0 ; i <= x_channel; i++) + seq_printf(s, "%3c%02d%c", '[', i, ']'); + + seq_puts(s, "\n"); + himax_diag_arrange_outloop(s, bit2, bit1 * y_channel, + bit0 * x_channel); + seq_printf(s, "%6c", ' '); + + if (bit0 == 1) { + for (i = x_channel - 1; i >= 0; i--) + seq_printf(s, "%6d", diag_self[i]); + } else { + for (i = 0; i < x_channel; i++) + seq_printf(s, "%6d", diag_self[i]); + } + } else { + for (i = 0 ; i <= y_channel; i++) + seq_printf(s, "%3c%02d%c", '[', i, ']'); + + seq_puts(s, "\n"); + himax_diag_arrange_outloop(s, bit2, bit1 * x_channel, + bit0 * y_channel); + seq_printf(s, "%6c", ' '); + + if (bit1 == 1) { + for (i = x_channel + y_channel - 1; i >= x_channel; + i--) + seq_printf(s, "%6d", diag_self[i]); + } else { + for (i = x_channel; i < x_channel + y_channel; i++) + seq_printf(s, "%6d", diag_self[i]); + } + } +} + +static void *himax_diag_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) + return NULL; + + return (void *)((unsigned long) *pos + 1); +} + +static void *himax_diag_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_diag_seq_stop(struct seq_file *s, void *v) +{ + kfree(v); +} + +/* DSRAM thread */ +bool himax_ts_diag_func(void) +{ + int retry = 3; + int i = 0, j = 0; + unsigned int index = 0; + int x_channel = ic_data->HX_RX_NUM; + int y_channel = ic_data->HX_TX_NUM; + int total_size = (y_channel * x_channel + y_channel + x_channel) * 2; + uint8_t *info_data; + int32_t *mutual_data = NULL; + int32_t *mutual_data_new = NULL; + int32_t *mutual_data_old = NULL; + int32_t *self_data = NULL; + int32_t *self_data_new = NULL; + int32_t *self_data_old = NULL; + int32_t new_data; + /* 1:common dsram,2:100 frame Max,3:N-(N-1)frame */ + int dsram_type = process_type; + + info_data = kcalloc(total_size, sizeof(uint8_t), GFP_KERNEL); + if (info_data == NULL) + return false; + + memset(info_data, 0, total_size * sizeof(uint8_t)); + + I("%s: process type=%d!\n", __func__, process_type); + + g_core_fp.fp_burst_enable(1); + + if (dsram_type <= 2) { + mutual_data = getMutualBuffer(); + self_data = getSelfBuffer(); + } else if (dsram_type == 3) { + mutual_data = getMutualBuffer(); + mutual_data_new = getMutualNewBuffer(); + mutual_data_old = getMutualOldBuffer(); + self_data = getSelfBuffer(); + self_data_new = getSelfNewBuffer(); + self_data_old = getSelfOldBuffer(); + } + + do { + if (retry <= 0) { + E("%s Get DSRAM data failed\n", __func__); + kfree(info_data); + return false; + } + if (g_core_fp.fp_get_DSRAM_data(info_data, dsram_flag)) + break; + } while (retry-- > 0); + + index = 0; + + for (i = 0; i < y_channel; i++) { /*mutual data*/ + for (j = 0; j < x_channel; j++) { + new_data = (((int8_t)info_data[index + 1] << 8) | + info_data[index]); + + if (dsram_type <= 1) { + mutual_data[i * x_channel + j] = new_data; + } else if (dsram_type == 2) { /* Keep max data */ + if (mutual_data[i * x_channel + j] < new_data) + mutual_data[i * x_channel + j] = new_data; + } else if (dsram_type == 3) { + /* Cal data for [N]-[N-1] frame */ + mutual_data_new[i * x_channel + j] = new_data; + mutual_data[i * x_channel + j] = + mutual_data_new[i * x_channel + j] - + mutual_data_old[i * x_channel + j]; + } + index += 2; + } + } + + for (i = 0; i < x_channel + y_channel; i++) { /*self data*/ + new_data = (info_data[index + 1] << 8 | info_data[index]); + if (dsram_type <= 1) { + self_data[i] = new_data; + } else if (dsram_type == 2) { /* Keep max data */ + if (self_data[i] < new_data) + self_data[i] = new_data; + } else if (dsram_type == 3) { /* Cal data for [N]-[N-1] frame */ + self_data_new[i] = new_data; + self_data[i] = self_data_new[i] - self_data_old[i]; + } + index += 2; + } + + kfree(info_data); + + if (dsram_type == 3) { + memcpy(mutual_data_old, mutual_data_new, + x_channel * y_channel * sizeof(int32_t)); + /* copy N data to N-1 array */ + memcpy(self_data_old, self_data_new, + (x_channel + y_channel) * sizeof(int32_t)); + /* copy N data to N-1 array */ + } + + diag_max_cnt++; + + if (dsram_type >= 1 && dsram_type <= 3) { + queue_delayed_work(private_ts->himax_diag_wq, + &private_ts->himax_diag_delay_wrok, 1 / 10 * HZ); + } + return true; +} + +static int himax_diag_print(struct seq_file *s, void *v) +{ + int x_num = ic_data->HX_RX_NUM; + int y_num = ic_data->HX_TX_NUM; + size_t ret = 0; + uint16_t mutual_num, self_num, width; + + mutual_num = x_num * y_num; + self_num = x_num + y_num; + /* don't add KEY_COUNT */ + width = x_num; + seq_printf(s, "ChannelStart: %4d, %4d\n\n", x_num, y_num); + + /* start to show out the raw data in adb shell */ + himax_diag_arrange(s); + seq_puts(s, "\n"); +#ifdef HX_EN_SEL_BUTTON + seq_puts(s, "\n"); + for (loop_i = 0; loop_i < ic_data->HX_BT_NUM; loop_i++) + seq_printf(s, "%6d", diag_self[ic_data->HX_RX_NUM + + ic_data->HX_TX_NUM + loop_i]); +#endif + seq_puts(s, "ChannelEnd"); + seq_puts(s, "\n"); + + /* print Mutual/Slef Maximum and Minimum */ + himax_get_mutual_edge(); + himax_get_self_edge(); + seq_printf(s, "Mutual Max:%3d, Min:%3d\n", g_max_mutual, + g_min_mutual); + seq_printf(s, "Self Max:%3d, Min:%3d\n", g_max_self, + g_min_self); + /* recovery status after print*/ + g_max_mutual = 0; + g_min_mutual = 0xFFFF; + g_max_self = 0; + g_min_self = 0xFFFF; + + /*pring state info*/ + print_state_info(s); + + if (s->count >= s->size) + overflow++; + + return ret; +} + +static int himax_diag_stack_read(struct seq_file *s, void *v) +{ + struct himax_ts_data *ts = private_ts; + + if (ts->diag_cmd) + himax_diag_print(s, v); + else + seq_puts(s, "Please set raw out select 'echo diag,rs:X > debug'\n\n"); + + return 0; +} + +static const struct seq_operations himax_diag_stack_ops = { + .start = himax_diag_seq_start, + .next = himax_diag_seq_next, + .stop = himax_diag_seq_stop, + .show = himax_diag_stack_read, +}; + +static int himax_diag_stack_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_diag_stack_ops); +}; + +static const struct file_operations himax_proc_stack_ops = { + .owner = THIS_MODULE, + .open = himax_diag_stack_open, + .read = seq_read, + .release = seq_release, +}; + +static int himax_sram_read(struct seq_file *s, void *v, uint8_t rs) +{ + struct himax_ts_data *ts = private_ts; + int d_type = 0; + + d_type = (!ts->diag_cmd)?rs:ts->diag_cmd; + + if (!overflow) { + if (!process_type) { + himax_int_enable(0); + g_core_fp.fp_diag_register_set(d_type, 0); + + if (!himax_ts_diag_func()) + seq_puts(s, "Get sram data failed."); + else + himax_diag_print(s, v); + + ts->diag_cmd = 0; + g_core_fp.fp_diag_register_set(0, 0); + himax_int_enable(1); + } + } + + if ((process_type <= 3 && ts->diag_cmd && dsram_flag) || overflow) { + himax_diag_print(s, v); + overflow = 0; + } + + return 0; +} + +static int himax_diag_iir_read(struct seq_file *s, void *v) +{ + return himax_sram_read(s, v, 0x09); +} + +static const struct seq_operations himax_diag_iir_ops = { + .start = himax_diag_seq_start, + .next = himax_diag_seq_next, + .stop = himax_diag_seq_stop, + .show = himax_diag_iir_read, +}; + +static int himax_diag_iir_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_diag_iir_ops); +}; + +static const struct file_operations himax_proc_iir_ops = { + .owner = THIS_MODULE, + .open = himax_diag_iir_open, + .read = seq_read, + .release = seq_release, +}; + +static int himax_diag_dc_read(struct seq_file *s, void *v) +{ + return himax_sram_read(s, v, 0x0A); +} + +static const struct seq_operations himax_diag_dc_ops = { + .start = himax_diag_seq_start, + .next = himax_diag_seq_next, + .stop = himax_diag_seq_stop, + .show = himax_diag_dc_read, +}; +static int himax_diag_dc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_diag_dc_ops); +}; + +static const struct file_operations himax_proc_dc_ops = { + .owner = THIS_MODULE, + .open = himax_diag_dc_open, + .read = seq_read, + .release = seq_release, +}; + +static int himax_diag_bank_read(struct seq_file *s, void *v) +{ + return himax_sram_read(s, v, 0x0B); +} + +static const struct seq_operations himax_diag_bank_ops = { + .start = himax_diag_seq_start, + .next = himax_diag_seq_next, + .stop = himax_diag_seq_stop, + .show = himax_diag_bank_read, +}; +static int himax_diag_bank_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_diag_bank_ops); +}; + +static const struct file_operations himax_proc_bank_ops = { + .owner = THIS_MODULE, + .open = himax_diag_bank_open, + .read = seq_read, + .release = seq_release, +}; + +static ssize_t himax_reset_write(char *buf, size_t len) +{ + if (len >= 12) { + I("%s: no command exceeds 12 chars.\n", __func__); + return -EFAULT; + } + +#ifdef HX_RST_PIN_FUNC + if (buf[0] == '1') + g_core_fp.fp_ic_reset(false, false); + else if (buf[0] == '2') + g_core_fp.fp_ic_reset(false, true); + else if (buf[0] == '3') + g_core_fp.fp_ic_reset(true, false); + else if (buf[0] == '4') + g_core_fp.fp_ic_reset(true, true); + /* else if (buf[0] == '5') */ + /* ESD_HW_REST(); */ +#endif +#if defined(HX_ZERO_FLASH) && defined(HX83102D) + if (g_core_fp.fp_0f_reload_to_active) + g_core_fp.fp_0f_reload_to_active(); +#endif + return len; +} + +static ssize_t himax_proc_FW_debug_read(char *buf, size_t len) +{ + ssize_t ret = 0; + uint8_t loop_i = 0; + uint8_t tmp_data[64] = {0}; + + cmd_set[0] = 0x01; + + if (g_core_fp.fp_read_FW_status(cmd_set, tmp_data) == NO_ERR) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "0x%02X%02X%02X%02X :\t", + cmd_set[5], cmd_set[4], cmd_set[3], cmd_set[2]); + + for (loop_i = 0; loop_i < cmd_set[1]; loop_i++) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "%5d\t", tmp_data[loop_i]); + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + } + + cmd_set[0] = 0x02; + + if (g_core_fp.fp_read_FW_status(cmd_set, tmp_data) == NO_ERR) { + for (loop_i = 0; loop_i < cmd_set[1]; + loop_i = loop_i + 2) { + if ((loop_i % 16) == 0) + ret += snprintf(buf_tmp + ret, + sizeof(buf_tmp) - ret, "0x%02X%02X%02X%02X :\t", + cmd_set[5], cmd_set[4], + cmd_set[3] + + (((cmd_set[2] + loop_i) >> 8) & 0xFF), + (cmd_set[2] + loop_i) & 0xFF); + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "%5d\t", tmp_data[loop_i] + + (tmp_data[loop_i + 1] << 8)); + + if ((loop_i % 16) == 14) + ret += snprintf(buf_tmp + ret, + sizeof(buf_tmp) - ret, "\n"); + } + } + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + + return ret; +} + +static ssize_t himax_proc_DD_debug_read(char *buf, size_t len) +{ + ssize_t ret = 0; + uint8_t tmp_data[64] = {0}; + uint8_t loop_i = 0; + + if (mutual_set_flag == 1) { + if (g_core_fp.fp_read_DD_status(cmd_set, tmp_data) == + NO_ERR) { + for (loop_i = 0; loop_i < cmd_set[0]; + loop_i++) { + if ((loop_i % 8) == 0) + ret += snprintf(buf_tmp + ret, + sizeof(buf_tmp) - ret, "0x%02X : ", loop_i); + + ret += snprintf(buf_tmp + ret, + sizeof(buf_tmp) - ret, "0x%02X ", tmp_data[loop_i]); + + if ((loop_i % 8) == 7) + ret += snprintf(buf_tmp + ret, + sizeof(buf_tmp) - ret, "\n"); + } + } else { + ret += snprintf(buf_tmp + ret, + sizeof(buf_tmp) - ret, "Get DD status falied!\n"); + } + } + + ret += snprintf(buf_tmp + ret, len - ret, "\n"); + + return ret; +} + +#define STR_TO_UL_ERR "String to ul is fail in cnt = %d, buf_tmp2 = %s\n" + +static ssize_t himax_proc_DD_debug_write(char *buf, size_t len) +{ + uint8_t i = 0; + uint8_t cnt = 2; + unsigned long result = 0; + char buf_tmp2[4]; + + if (len >= 20) { + I("%s: no command exceeds 20 chars.\n", __func__); + return -EFAULT; + } + + memset(buf_tmp2, 0x0, sizeof(buf_tmp2)); + + if (buf[2] == 'x' && buf[6] == 'x' && buf[10] == 'x') { + mutual_set_flag = 1; + + for (i = 3; i < 12; i = i + 4) { + memcpy(buf_tmp2, buf + i, 2); + + if (!kstrtoul(buf_tmp2, 16, &result)) + cmd_set[cnt] = (uint8_t)result; + else + I(STR_TO_UL_ERR, cnt, buf_tmp2); + + cnt--; + } + + I("cmd_set[2] = %02X, cmd_set[1] = %02X, cmd_set[0] = %02X\n", + cmd_set[2], cmd_set[1], cmd_set[0]); + } else { + mutual_set_flag = 0; + } + + return len; +} + +void setFlashBuffer(void) +{ + flash_buffer = kcalloc(Flash_Size, sizeof(uint8_t), GFP_KERNEL); +} + +void flash_dump_prog_set(uint8_t prog) +{ + g_flash_progress = prog; + if (prog == ONGOING) + debug_data->flash_dump_going = ONGOING; + else + debug_data->flash_dump_going = START; +} + +static int himax_proc_flash_read(struct seq_file *s, void *v) +{ + ssize_t ret = 0; + int i; + uint8_t flash_progress = g_flash_progress; + uint8_t flash_cmd = g_flash_cmd; + bool flash_rst = g_flash_dump_rst; + + I("flash_progress = %d\n", flash_progress); + + if (!flash_rst) { + seq_puts(s, "FlashStart:Fail\n"); + seq_puts(s, "FlashEnd\n"); + return ret; + } + + if (flash_progress == START) + seq_puts(s, "Flash dump - Start\n"); + else if (flash_progress == ONGOING) + seq_puts(s, "Flash dump - On-going\n"); + else if (flash_progress == FINISHED) + seq_puts(s, "Flash dump - Finished\n"); + + /*print flash dump data*/ + if (flash_cmd == 1 && flash_progress == FINISHED) { + seq_puts(s, "Start to print flash dump data\n"); + for (i = 0; i < Flash_Size; i++) { + seq_printf(s, "0x%02X,", flash_buffer[i]); + if (i % 16 == 15) + seq_puts(s, "\n"); + } + } + + seq_puts(s, "FlashEnd\n"); + + return ret; +} + +static ssize_t himax_proc_flash_write(struct file *filp, + const char __user *buff, size_t len, loff_t *data) +{ + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + I("%s: buf = %s\n", __func__, buf); + + if (g_flash_progress == ONGOING) { + E("%s: process is busy , return!\n", __func__); + return len; + } + + if ((buf[1] == '_') && (buf[2] == '3') && (buf[3] == '2')) + Flash_Size = FW_SIZE_32k; + else if ((buf[1] == '_') && (buf[2] == '6')) { + if (buf[3] == '0') + Flash_Size = FW_SIZE_60k; + else if (buf[3] == '4') + Flash_Size = FW_SIZE_64k; + + } else if ((buf[1] == '_') && (buf[2] == '2')) { + if (buf[3] == '4') + Flash_Size = FW_SIZE_124k; + else if (buf[3] == '8') + Flash_Size = FW_SIZE_128k; + } + + /*1 : print flash to window, 2 : dump to sdcard*/ + if (buf[0] == '1') { + /* 1_32,1_60,1_64,1_24,1_28 for flash size: + * 32k,60k,64k,124k,128k + */ + g_flash_cmd = 1; + flash_dump_prog_set(START); + g_flash_dump_rst = true; + queue_work(private_ts->flash_wq, &private_ts->flash_work); + } else if (buf[0] == '2') { + /* 2_32,2_60,2_64,2_24,2_28 for flash size: + * 32k,60k,64k,124k,128k + */ + g_flash_cmd = 2; + flash_dump_prog_set(START); + g_flash_dump_rst = true; + queue_work(private_ts->flash_wq, &private_ts->flash_work); + } + + return len; +} + +static void *himax_flash_dump_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) + return NULL; + + return (void *)((unsigned long) *pos + 1); +} + +static void *himax_flash_dump_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_flash_dump_seq_stop(struct seq_file *s, void *v) +{ +} + +static const struct seq_operations himax_flash_dump_seq_ops = { + .start = himax_flash_dump_seq_start, + .next = himax_flash_dump_seq_next, + .stop = himax_flash_dump_seq_stop, + .show = himax_proc_flash_read, +}; +static int himax_flash_dump_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_flash_dump_seq_ops); +}; + +static const struct file_operations himax_proc_flash_ops = { + .owner = THIS_MODULE, + .open = himax_flash_dump_proc_open, + .read = seq_read, + .write = himax_proc_flash_write, +}; + +void himax_ts_flash_func(void) +{ + uint8_t flash_command = g_flash_cmd; + + himax_int_enable(0); + flash_dump_prog_set(ONGOING); + + /*msleep(100);*/ + I("%s: flash_command = %d enter.\n", __func__, flash_command); + + if (flash_command == 1 || flash_command == 2) { + g_core_fp.fp_flash_dump_func(flash_command, Flash_Size, + flash_buffer); + g_flash_dump_rst = true; + } + + I("Complete~~~~~~~~~~~~~~~~~~~~~~~\n"); + +/* if (flash_command == 2) { + * struct file *fn; + * struct filename *vts_name; + * + * vts_name = kp_getname_kernel(FLASH_DUMP_FILE); + * fn = kp_file_open_name(vts_name, O_CREAT | O_WRONLY, 0); + * + * if (!IS_ERR(fn)) { + * I("%s create file and ready to write\n", __func__); + * fn->f_op->write(fn, flash_buffer, + * Flash_Size * sizeof(uint8_t), &fn->f_pos); + * filp_close(fn, NULL); + * } else { + * E("%s Open file failed!\n", __func__); + * g_flash_dump_rst = false; + * } + * } + */ + himax_int_enable(1); + flash_dump_prog_set(FINISHED); +} + +static ssize_t himax_sense_on_off_write(char *buf, size_t len) +{ + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (buf[0] == '0') { + g_core_fp.fp_sense_off(true); + I("Sense off\n"); + } else if (buf[0] == '1') { + if (buf[1] == 's') { + g_core_fp.fp_sense_on(0x00); + I("Sense on re-map on, run sram\n"); + } else { + g_core_fp.fp_sense_on(0x01); + I("Sense on re-map off, run flash\n"); + } + } else { + I("Do nothing\n"); + } + + return len; +} + +#ifdef HX_ESD_RECOVERY +static ssize_t himax_esd_cnt_read(char *buf, size_t len) +{ + size_t ret = 0; + + I("%s: enter, %d\n", __func__, __LINE__); + + ret += snprintf(buf_tmp + ret, len - ret, + "EB_cnt = %d, EC_cnt = %d, ED_cnt = %d\n", + hx_EB_event_flag, hx_EC_event_flag, hx_ED_event_flag); + + return ret; +} + +static ssize_t himax_esd_cnt_write(char *buf, size_t len) +{ + int i = 0; + + if (len >= 12) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + I("Clear ESD Flag\n"); + + if (buf[i] == '0') { + hx_EB_event_flag = 0; + hx_EC_event_flag = 0; + hx_ED_event_flag = 0; + } + + return len; +} + +#endif + +static int printMat(char *temp_buf, size_t len, int max_size, + uint8_t *guest_str, int loc) +{ + int ret = loc; + int i; + + for (i = 0; i < max_size; i++) { + if ((i % 16) == 0 && i > 0) + ret += snprintf(temp_buf + ret, len - ret, "\n"); + + ret += snprintf(temp_buf + ret, len - ret, "0x%02X\t", + guest_str[i]); + } + return ret; +} + +static int printUnit(char *temp_buf, size_t len, int max_size, char *info_item, + uint8_t *guest_str, int loc) +{ + int ret = loc; + + ret += snprintf(temp_buf + ret, len - ret, "%s:\n", info_item); + ret = printMat(temp_buf, len, max_size, guest_str, ret); + ret += snprintf(temp_buf + ret, len - ret, "\n"); + return ret; +} + +#ifdef HX_TP_PROC_GUEST_INFO +static ssize_t himax_proc_guest_info_read(char *buf, size_t len) +{ + int ret = 0; + int j = 0; + int max_size = 128; + struct hx_guest_info *info = g_guest_info_data; + + I("guest info progress\n"); + + if (g_core_fp.guest_info_get_status()) { + ret += snprintf(buf_tmp + ret, len - ret, + "Not Ready\n"); + goto END_FUNCTION; + } else { + if (info->g_guest_info_type == 1) { + for (j = 0; j < 3; j++) { + ret = printUnit(buf_tmp, len, max_size, + g_guest_info_item[j], + info->g_guest_str[j], ret); + I("str[%d] %s\n", j, + info->g_guest_str[j]); + } + ret = printUnit(buf_tmp, len, max_size, + g_guest_info_item[8], info->g_guest_str[8], + ret); + + I("str[8] %s\n", + info->g_guest_str[8]); + + ret = printUnit(buf_tmp, len, max_size, + g_guest_info_item[9], info->g_guest_str[9], + ret); + + I("str[9] %s\n", + info->g_guest_str[9]); + } else if (info->g_guest_info_type == 0) { + for (j = 0; j < 10; j++) { + if (j == 3) + j = 8; + ret += snprintf(buf_tmp + ret, + len - ret, "%s:\n", + g_guest_info_item[j]); + if (info->g_guest_data_type[j] == 0) { + ret += snprintf(buf_tmp + ret, + len - ret, "%s", + g_guest_info_data->g_guest_str_in_format[j]); + } else { + ret = printMat(buf_tmp, len, + info->g_guest_data_len[j], + g_guest_info_data->g_guest_str_in_format[j], ret); + } + ret += snprintf(buf_tmp + ret, + len - ret, "\n"); + } + } + } + +END_FUNCTION: + return ret; +} + +static ssize_t himax_proc_guest_info_write(char *buf, size_t len) +{ + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + I("%s: buf = %s\n", __func__, buf); + if (buf[0] == 'r') { + I("%s,Test to get", __func__); + queue_work(private_ts->guest_info_wq, + &private_ts->guest_info_work); + } + return len; +} + +#endif + +static ssize_t himax_debug_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + ssize_t ret = 0; + int i = 0; + + if (!HX_PROC_SEND_FLAG) { + I("%s, Enter, len = %d\n", __func__, (int)len); + memset(buf_tmp, 0, sizeof(buf_tmp)); + + if (dbg_cmd_flag) { + memset(buf_tmp, 0, sizeof(buf_tmp)); + if (dbg_func_ptr_r[dbg_cmd_flag]) + ret += dbg_func_ptr_r[dbg_cmd_flag](buf, len); + else + goto END_FUNC_R; + } + + if (debug_level_cmd == 't') { + if (fw_update_complete) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "FW Update Complete "); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "FW Update Fail "); + + } else if (debug_level_cmd == 'h') { + if (handshaking_result == 0) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Handshaking Result = %d (MCU Running)\n", + handshaking_result); + else if (handshaking_result == 1) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Handshaking Result = %d (MCU Stop)\n", + handshaking_result); + else if (handshaking_result == 2) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Handshaking Result = %d (I2C Error)\n", + handshaking_result); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Handshaking Result = error\n"); + + } else if (debug_level_cmd == 'v') { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "FW_VER = 0x%2.2X\n", ic_data->vendor_fw_ver); + + if (private_ts->chip_cell_type == CHIP_IS_ON_CELL) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CONFIG_VER = 0x%2.2X\n", + ic_data->vendor_config_ver); + else { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "TOUCH_VER = 0x%2.2X\n", + ic_data->vendor_touch_cfg_ver); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "DISPLAY_VER = 0x%2.2X\n", + ic_data->vendor_display_cfg_ver); + } + if (ic_data->vendor_cid_maj_ver < 0 && + ic_data->vendor_cid_min_ver < 0) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CID_VER = NULL\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CID_VER = 0x%2.2X\n", + (ic_data->vendor_cid_maj_ver << 8 | + ic_data->vendor_cid_min_ver)); + + if (ic_data->vendor_panel_ver < 0) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "PANEL_VER = NULL\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "PANEL_VER = 0x%2.2X\n", + ic_data->vendor_panel_ver); + if (private_ts->chip_cell_type == CHIP_IS_IN_CELL) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Cusomer = %s\n", ic_data->vendor_cus_info); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Project = %s\n", ic_data->vendor_proj_info); + } +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#ifdef HX_EN_DYNAMIC_NAME + if (ic_data->vendor_semifac == 1) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "SEMI_FAC = UMC\n"); + else if (ic_data->vendor_semifac == 2) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "SEMI_FAC = PSC\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "SEMI_FAC = NULL\n"); +#endif +#endif + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Himax Touch Driver Version:\n"); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "%s\n", HIMAX_DRIVER_VER); + } else if (debug_level_cmd == 'd') { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Himax Touch IC Information :\n"); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "%s\n", private_ts->chip_name); + + switch (IC_CHECKSUM) { + case HX_TP_BIN_CHECKSUM_SW: + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "IC Checksum : SW\n"); + break; + + case HX_TP_BIN_CHECKSUM_HW: + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "IC Checksum : HW\n"); + break; + + case HX_TP_BIN_CHECKSUM_CRC: + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "IC Checksum : CRC\n"); + break; + + default: + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "IC Checksum error.\n"); + } + + if (ic_data->HX_INT_IS_EDGE) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Driver register Interrupt : EDGE TIRGGER\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Driver register Interrupt : LEVEL TRIGGER\n"); + + if (private_ts->protocol_type == PROTOCOL_TYPE_A) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Protocol : TYPE_A\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Protocol : TYPE_B\n"); + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "RX Num : %d\n", ic_data->HX_RX_NUM); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "TX Num : %d\n", ic_data->HX_TX_NUM); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "BT Num : %d\n", ic_data->HX_BT_NUM); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "X Resolution : %d\n", ic_data->HX_X_RES); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Y Resolution : %d\n", ic_data->HX_Y_RES); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Max Point : %d\n", ic_data->HX_MAX_PT); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "XY reverse : %d\n", ic_data->HX_XY_REVERSE); +#ifdef HX_TP_PROC_2T2R + if (Is_2T2R) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "2T2R panel\n"); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "RX Num_2 : %d\n", HX_RX_NUM_2); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "TX Num_2 : %d\n", HX_TX_NUM_2); + } +#endif + } else if (debug_level_cmd == 'i') { + if (g_core_fp.fp_read_i2c_status()) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "I2C communication is bad.\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "I2C communication is good.\n"); + } else if (debug_level_cmd == 'n') { + /* Edgd = 1, Level = 0 */ + if (g_core_fp.fp_read_ic_trigger_type() == 1) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "IC Interrupt type is edge trigger.\n"); + else if (g_core_fp.fp_read_ic_trigger_type() == 0) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "IC Interrupt type is level trigger.\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Unkown IC trigger type.\n"); + + if (ic_data->HX_INT_IS_EDGE) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Driver register Interrupt : EDGE TIRGGER\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Driver register Interrupt : LEVEL TRIGGER\n"); + } else if (debug_level_cmd == 'l') { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "LotID : "); + for (i = 0; i < 13; i++) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "%02X", ic_data->vendor_ic_id[i]); + } + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "\n"); + } + +END_FUNC_R: + if (copy_to_user(buf, buf_tmp, (len > BUF_SIZE) ? BUF_SIZE : len)) + I("%s,here:%d\n", __func__, __LINE__); + + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static ssize_t himax_debug_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + char fileName[128]; + char buf[80] = {0}; + int result = 0; +#ifndef HX_ZERO_FLASH + int fw_type = 0; + const struct firmware *fw = NULL; +#endif + + char *str_ptr = NULL; + int str_len = 0; + int i = 0; + + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + buf[strlen(buf) - 1] = 0;/*remove \n*/ + + while (dbg_cmd_str[i]) { + str_ptr = strnstr(buf, dbg_cmd_str[i], len); + if (str_ptr) { + str_len = strlen(dbg_cmd_str[i]); + dbg_cmd_flag = i + 1; + debug_level_cmd = 0; + I("Cmd is correct :%s, dbg_cmd = %d\n", str_ptr, dbg_cmd_flag); + break; + } + i++; + } + if (!str_ptr) { + I("Cmd is not correct\n"); + dbg_cmd_flag = 0; + } + + if (buf[str_len] == ',') { + dbg_cmd_par = buf + str_len + 1; + if (dbg_func_ptr_w[dbg_cmd_flag]) + dbg_func_ptr_w[dbg_cmd_flag](dbg_cmd_par, len - str_len - 2);/* 2 => '/n' + ','*/ + I("string of paremeter is %s, dbg_cmd_par = %s\n", buf + str_len + 1, dbg_cmd_par); + } else + I("No paremeter of this cmd\n"); + + if (dbg_cmd_flag) + return len; + + if (buf[0] == 'h') { /* handshaking */ + debug_level_cmd = buf[0]; + himax_int_enable(0); + /* 0:Running, 1:Stop, 2:I2C Fail */ + handshaking_result = g_core_fp.fp_hand_shaking(); + himax_int_enable(1); + return len; + } else if (buf[0] == 'v') { /* firmware version */ + himax_int_enable(0); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#endif + debug_level_cmd = buf[0]; + g_core_fp.fp_read_FW_ver(); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(true, false); +#else + g_core_fp.fp_system_reset(); +#endif +#if defined(HX_ZERO_FLASH) && defined(HX83102D) + if (g_core_fp.fp_0f_reload_to_active) + g_core_fp.fp_0f_reload_to_active(); +#endif + himax_int_enable(1); + /* himax_check_chip_version(); */ + return len; + } else if (buf[0] == 'd') { /* ic information */ + debug_level_cmd = buf[0]; + return len; + } else if (buf[0] == 't') { + if (buf[1] == 's' && + buf[2] == 'd' && + buf[3] == 'b' && + buf[4] == 'g' + ) { + if (buf[5] == '1') { + I("Open Ts Debug!\n"); + g_ts_dbg = 1; + } else if (buf[5] == '0') { + I("Close Ts Debug!\n"); + g_ts_dbg = 0; + } else { + E("Parameter fault for ts debug\n"); + } + goto ENDFUCTION; + } + himax_int_enable(0); + debug_level_cmd = buf[0]; + fw_update_complete = false; + memset(fileName, 0, 128); + /* parse the file name */ + snprintf(fileName, len - 2, "%s", &buf[2]); + +#ifdef HX_ZERO_FLASH + I("NOW Running Zero flash update!\n"); + I("%s: upgrade from file(%s) start!\n", __func__, fileName); + result = g_core_fp.fp_0f_op_file_dirly(fileName); + if (result) { + fw_update_complete = false; + I("Zero flash update fail!\n"); + goto ENDFUCTION; + } else { + fw_update_complete = true; + I("Zero flash update complete!\n"); + } + goto firmware_upgrade_done; +#else + I("NOW Running common flow update!\n"); + I("%s: upgrade from file(%s) start!\n", __func__, fileName); + result = request_firmware(&fw, fileName, private_ts->dev); + + if (result < 0) { + I("fail to request_firmware fwpath: %s (ret:%d)\n", + fileName, result); + return result; + } + + I("%s: FW image: %02X, %02X, %02X, %02X\n", __func__, + fw->data[0], fw->data[1], fw->data[2], fw->data[3]); + fw_type = (fw->size) / 1024; + /* start to upgrade */ + himax_int_enable(0); + I("Now FW size is : %dk\n", fw_type); + + switch (fw_type) { + case 32: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_32k( + (unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", + __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", + __func__, __LINE__); + fw_update_complete = true; + } + break; + + case 60: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_60k( + (unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", + __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", + __func__, __LINE__); + fw_update_complete = true; + } + break; + + case 64: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_64k( + (unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", + __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", + __func__, __LINE__); + fw_update_complete = true; + } + break; + + case 124: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_124k( + (unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", + __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", + __func__, __LINE__); + fw_update_complete = true; + } + break; + + case 128: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_128k( + (unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", + __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", + __func__, __LINE__); + fw_update_complete = true; + } + break; + + default: + E("%s: Flash command fail: %d\n", __func__, __LINE__); + fw_update_complete = false; + break; + } + release_firmware(fw); + goto firmware_upgrade_done; +#endif + } else if (buf[0] == 'i' && buf[1] == '2' && buf[2] == 'c') { + /* i2c communication */ + debug_level_cmd = 'i'; + return len; + } else if (buf[0] == 'i' && buf[1] == 'n' && buf[2] == 't') { + /* INT trigger */ + debug_level_cmd = 'n'; + return len; +#ifdef HX_ZERO_FLASH + } else if (buf[0] == 'z') { + result = buf[1] - '0'; + I("check type = %d\n", result); + g_core_fp.fp_0f_operation_check(result); + return len; + } else if (buf[0] == 'p') { + I("NOW debug echo r!\n"); + /* himax_program_sram(); */ + private_ts->himax_0f_update_wq = + create_singlethread_workqueue("HMX_update_0f_reuqest_write"); + + if (!private_ts->himax_0f_update_wq) + E(" allocate syn_update_wq failed\n"); + + INIT_DELAYED_WORK(&private_ts->work_0f_update, + g_core_fp.fp_0f_operation); + queue_delayed_work(private_ts->himax_0f_update_wq, + &private_ts->work_0f_update, msecs_to_jiffies(100)); + return len; + } else if (buf[0] == 'x') { + g_core_fp.fp_system_reset(); +#if defined(HX_ZERO_FLASH) && defined(HX83102D) + if (g_core_fp.fp_0f_reload_to_active) + g_core_fp.fp_0f_reload_to_active(); +#endif + return len; +#endif + } else if (buf[0] == 'l' && buf[1] == 'o' && buf[2] == 't') { + debug_level_cmd = buf[0]; + g_core_fp.fp_sense_off(true); + g_core_fp.fp_ic_id_read(); + g_core_fp.fp_sense_on(0x01); + return len; + } + /* others,do nothing */ + debug_level_cmd = 0; + return len; + +firmware_upgrade_done: + g_core_fp.fp_reload_disable(0); + g_core_fp.fp_read_FW_ver(); + g_core_fp.fp_touch_information(); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(true, false); +#if defined(HX_ZERO_FLASH) && defined(HX83102D) + if (g_core_fp.fp_0f_reload_to_active) + g_core_fp.fp_0f_reload_to_active(); +#endif +#else + g_core_fp.fp_sense_on(0x00); +#endif + + himax_int_enable(1); + /* todo himax_chip->tp_firmware_upgrade_proceed = 0; + * todo himax_chip->suspend_state = 0; + * todo enable_irq(himax_chip->irq); + */ +ENDFUCTION: + return len; +} + +static const struct file_operations himax_proc_debug_ops = { + .owner = THIS_MODULE, + .read = himax_debug_read, + .write = himax_debug_write, +}; + +static void himax_himax_data_init(void) +{ + debug_data->fp_ts_dbg_func = himax_ts_dbg_func; + debug_data->fp_set_diag_cmd = himax_set_diag_cmd; + debug_data->flash_dump_going = false; +} + +static void himax_ts_flash_work_func(struct work_struct *work) +{ + himax_ts_flash_func(); +} +#ifdef HX_TP_PROC_GUEST_INFO +static void himax_ts_guest_info_work_func(struct work_struct *work) +{ + g_core_fp.read_guest_info(); +} +#endif + +static void himax_ts_diag_work_func(struct work_struct *work) +{ + himax_ts_diag_func(); +} + +void dbg_func_ptr_init(void) +{ + /*debug function ptr init*/ + dbg_func_ptr_r[1] = himax_crc_test_read; + dbg_func_ptr_r[2] = himax_proc_FW_debug_read; + dbg_func_ptr_r[3] = himax_attn_read; + dbg_func_ptr_r[4] = himax_layout_read; + dbg_func_ptr_w[4] = himax_layout_write; + dbg_func_ptr_r[5] = himax_proc_DD_debug_read; + dbg_func_ptr_w[5] = himax_proc_DD_debug_write; +#ifdef HX_ESD_RECOVERY + dbg_func_ptr_r[6] = himax_esd_cnt_read; + dbg_func_ptr_w[6] = himax_esd_cnt_write; +#endif + dbg_func_ptr_w[7] = himax_sense_on_off_write; + dbg_func_ptr_r[8] = himax_debug_level_read; + dbg_func_ptr_w[8] = himax_debug_level_write; +#ifdef HX_TP_PROC_GUEST_INFO + dbg_func_ptr_r[9] = himax_proc_guest_info_read; + dbg_func_ptr_w[9] = himax_proc_guest_info_write; +#endif + dbg_func_ptr_r[10] = himax_int_en_read; + dbg_func_ptr_w[10] = himax_int_en_write; + dbg_func_ptr_w[11] = himax_proc_register_write; + dbg_func_ptr_r[11] = himax_proc_register_read; + dbg_func_ptr_w[12] = himax_reset_write; + dbg_func_ptr_w[13] = himax_diag_arrange_write; + dbg_func_ptr_w[14] = himax_diag_cmd_write; +} + +int himax_touch_proc_init(void) +{ + himax_proc_diag_dir = proc_mkdir(HIMAX_PROC_DIAG_FOLDER, himax_touch_proc_dir); + + if (himax_proc_diag_dir == NULL) { + E(" %s: himax_proc_diag_dir file create failed!\n", __func__); + return -ENOMEM; + } + + himax_proc_vendor_file = proc_create(HIMAX_PROC_VENDOR_FILE, 0444, + himax_touch_proc_dir, &himax_proc_vendor_ops); + if (himax_proc_vendor_file == NULL) { + E(" %s: proc vendor file create failed!\n", __func__); + goto fail_1; + } + + himax_proc_stack_file = proc_create(HIMAX_PROC_STACK_FILE, 0444, + himax_proc_diag_dir, &himax_proc_stack_ops); + if (himax_proc_stack_file == NULL) { + E(" %s: proc stack file create failed!\n", __func__); + goto fail_2_1; + } + + himax_proc_iir_file = proc_create(HIMAX_PROC_IIR_FILE, 0444, + himax_proc_diag_dir, &himax_proc_iir_ops); + if (himax_proc_iir_file == NULL) { + E(" %s: proc iir file create failed!\n", __func__); + goto fail_2_2; + } + + himax_proc_dc_file = proc_create(HIMAX_PROC_DC_FILE, 0444, + himax_proc_diag_dir, &himax_proc_dc_ops); + if (himax_proc_dc_file == NULL) { + E(" %s: proc dc file create failed!\n", __func__); + goto fail_2_3; + } + + himax_proc_bank_file = proc_create(HIMAX_PROC_BANK_FILE, 0444, + himax_proc_diag_dir, &himax_proc_bank_ops); + if (himax_proc_bank_file == NULL) { + E(" %s: proc bank file create failed!\n", __func__); + goto fail_2_4; + } + + himax_proc_debug_file = proc_create(HIMAX_PROC_DEBUG_FILE, + 0644, himax_touch_proc_dir, + &himax_proc_debug_ops); + if (himax_proc_debug_file == NULL) { + E(" %s: proc debug file create failed!\n", __func__); + goto fail_3; + } + dbg_func_ptr_init(); + + himax_proc_flash_dump_file = proc_create(HIMAX_PROC_FLASH_DUMP_FILE, + 0644, himax_touch_proc_dir, + &himax_proc_flash_ops); + if (himax_proc_flash_dump_file == NULL) { + E(" %s: proc flash dump file create failed!\n", __func__); + goto fail_4; + } + +#if defined(HX_PEN_FUNC_EN) + himax_proc_pen_pos_file = proc_create(HIMAX_PROC_PEN_POS_FILE, + 0666, + himax_touch_proc_dir, &himax_proc_pen_pos_ops); + if (himax_proc_pen_pos_file == NULL) { + E(" %s: proc CRC test file create failed!\n", __func__); + goto fail_5; + } +#endif + + return 0; +#if defined(HX_PEN_FUNC_EN) + remove_proc_entry(HIMAX_PROC_PEN_POS_FILE, himax_touch_proc_dir); +fail_5: +#endif +remove_proc_entry(HIMAX_PROC_FLASH_DUMP_FILE, himax_touch_proc_dir); +fail_4: remove_proc_entry(HIMAX_PROC_DEBUG_FILE, himax_touch_proc_dir); +fail_3: remove_proc_entry(HIMAX_PROC_BANK_FILE, himax_proc_diag_dir); +fail_2_4: remove_proc_entry(HIMAX_PROC_DC_FILE, himax_proc_diag_dir); +fail_2_3: remove_proc_entry(HIMAX_PROC_IIR_FILE, himax_proc_diag_dir); +fail_2_2: remove_proc_entry(HIMAX_PROC_STACK_FILE, himax_proc_diag_dir); +fail_2_1: remove_proc_entry(HIMAX_PROC_VENDOR_FILE, himax_touch_proc_dir); +fail_1: + return -ENOMEM; +} + +void himax_touch_proc_deinit(void) +{ +#if defined(HX_PEN_FUNC_EN) + remove_proc_entry(HIMAX_PROC_PEN_POS_FILE, himax_touch_proc_dir); +#endif + remove_proc_entry(HIMAX_PROC_FLASH_DUMP_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_DEBUG_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_BANK_FILE, himax_proc_diag_dir); + remove_proc_entry(HIMAX_PROC_DC_FILE, himax_proc_diag_dir); + remove_proc_entry(HIMAX_PROC_IIR_FILE, himax_proc_diag_dir); + remove_proc_entry(HIMAX_PROC_STACK_FILE, himax_proc_diag_dir); + remove_proc_entry(HIMAX_PROC_VENDOR_FILE, himax_touch_proc_dir); +} + +int himax_debug_init(void) +{ + struct himax_ts_data *ts = private_ts; + int err = 0; + + I("%s:Enter\n", __func__); + + if (ts == NULL) { + E("%s: ts struct is NULL\n", __func__); + return -EPROBE_DEFER; + } + + reg_read_data = kzalloc(DEBUG_READ_BUF_SIZE * sizeof(uint8_t), GFP_KERNEL); + if (reg_read_data == NULL) { + err = -ENOMEM; + goto err_alloc_reg_read_data_fail; + } + + debug_data = kzalloc(sizeof(struct himax_debug), GFP_KERNEL); + if (debug_data == NULL) { /*Allocate debug data space*/ + err = -ENOMEM; + goto err_alloc_debug_data_fail; + } + + himax_himax_data_init(); + + ts->flash_wq = create_singlethread_workqueue("himax_flash_wq"); + + if (!ts->flash_wq) { + E("%s: create flash workqueue failed\n", __func__); + err = -ENOMEM; + goto err_create_flash_dump_wq_failed; + } + + INIT_WORK(&ts->flash_work, himax_ts_flash_work_func); + + g_flash_progress = START; + setFlashBuffer(); + +#ifdef HX_TP_PROC_GUEST_INFO + + if (g_guest_info_data == NULL) { + g_guest_info_data = kzalloc(sizeof(struct hx_guest_info), + GFP_KERNEL); + g_guest_info_data->g_guest_info_ongoing = 0; + g_guest_info_data->g_guest_info_type = 0; + } + + ts->guest_info_wq = + create_singlethread_workqueue("himax_guest_info_wq"); + if (!ts->guest_info_wq) { + E("%s: create guest info workqueue failed\n", __func__); + err = -ENOMEM; + goto err_create_guest_info_wq_failed; + } + INIT_WORK(&ts->guest_info_work, himax_ts_guest_info_work_func); +#endif + + ts->himax_diag_wq = create_singlethread_workqueue("himax_diag"); + + if (!ts->himax_diag_wq) { + E("%s: create diag workqueue failed\n", __func__); + err = -ENOMEM; + goto err_create_diag_wq_failed; + } + + INIT_DELAYED_WORK(&ts->himax_diag_delay_wrok, himax_ts_diag_work_func); + + setMutualBuffer(ic_data->HX_RX_NUM, ic_data->HX_TX_NUM); + if (getMutualBuffer() == NULL) { + E("%s: mutual buffer allocate fail failed\n", __func__); + return MEM_ALLOC_FAIL; + } + + setMutualNewBuffer(ic_data->HX_RX_NUM, ic_data->HX_TX_NUM); + if (getMutualNewBuffer() == NULL) { + E("%s: mutual new buffer allocate fail failed\n", __func__); + return MEM_ALLOC_FAIL; + } + + setMutualOldBuffer(ic_data->HX_RX_NUM, ic_data->HX_TX_NUM); + if (getMutualOldBuffer() == NULL) { + E("%s: mutual old buffer allocate fail failed\n", __func__); + return MEM_ALLOC_FAIL; + } + +#ifdef HX_TP_PROC_2T2R + + if (Is_2T2R) { + setMutualBuffer_2(ic_data->HX_RX_NUM_2, ic_data->HX_TX_NUM_2); + + if (getMutualBuffer_2() == NULL) { + E("%s: mutual buffer 2 allocate fail failed\n", + __func__); + return MEM_ALLOC_FAIL; + } + } +#endif + + himax_touch_proc_init(); + + return 0; + + cancel_delayed_work_sync(&ts->himax_diag_delay_wrok); + destroy_workqueue(ts->himax_diag_wq); +err_create_diag_wq_failed: + +#ifdef HX_TP_PROC_GUEST_INFO +err_create_guest_info_wq_failed: + + destroy_workqueue(ts->guest_info_wq); + if (g_guest_info_data != NULL) { + kfree(g_guest_info_data); + g_guest_info_data = NULL; + } +#endif + destroy_workqueue(ts->flash_wq); + +err_create_flash_dump_wq_failed: + kfree(debug_data); + debug_data = NULL; +err_alloc_debug_data_fail: + kfree(reg_read_data); + reg_read_data = NULL; +err_alloc_reg_read_data_fail: + + return err; +} +EXPORT_SYMBOL(himax_debug_init); + +int himax_debug_remove(void) +{ + struct himax_ts_data *ts = private_ts; + + himax_touch_proc_deinit(); + + cancel_delayed_work_sync(&ts->himax_diag_delay_wrok); +#ifdef HX_TP_PROC_GUEST_INFO + destroy_workqueue(ts->guest_info_wq); + if (g_guest_info_data != NULL) + kfree(g_guest_info_data); +#endif + destroy_workqueue(ts->himax_diag_wq); + destroy_workqueue(ts->flash_wq); + + kfree(debug_data); + + return 0; +} +EXPORT_SYMBOL(himax_debug_remove); + diff --git a/drivers/input/touchscreen/himax_v2_mmi/himax_debug.h b/drivers/input/touchscreen/himax_v2_mmi/himax_debug.h new file mode 100644 index 000000000000..586923f9e065 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/himax_debug.h @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for debug nodes + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef H_HIMAX_DEBUG +#define H_HIMAX_DEBUG + +#include "himax_platform.h" +#include "himax_common.h" + + +#ifdef HX_ESD_RECOVERY + extern u8 HX_ESD_RESET_ACTIVATE; + extern int hx_EB_event_flag; + extern int hx_EC_event_flag; + extern int hx_ED_event_flag; +#endif + +#define HIMAX_PROC_VENDOR_FILE "vendor" +extern struct proc_dir_entry *himax_proc_vendor_file; + +#define HIMAX_PROC_PEN_POS_FILE "pen_pos" + +int himax_touch_proc_init(void); +void himax_touch_proc_deinit(void); +extern int himax_int_en_set(void); + +extern uint8_t byte_length; +extern uint8_t register_command[4]; +extern uint8_t cfg_flag; + +#define HIMAX_PROC_DIAG_FOLDER "diag" +struct proc_dir_entry *himax_proc_diag_dir; +#define HIMAX_PROC_STACK_FILE "stack" +extern struct proc_dir_entry *himax_proc_stack_file; +#define HIMAX_PROC_IIR_FILE "iir_s" +extern struct proc_dir_entry *himax_proc_iir_file; +#define HIMAX_PROC_DC_FILE "dc_s" +extern struct proc_dir_entry *himax_proc_dc_file; +#define HIMAX_PROC_BANK_FILE "bank_s" +extern struct proc_dir_entry *himax_proc_bank_file; + +#ifdef HX_TP_PROC_2T2R + extern uint32_t *diag_mutual_2; + + int32_t *getMutualBuffer_2(void); + void setMutualBuffer_2(uint8_t x_num, uint8_t y_num); +#endif +extern int32_t *diag_mutual; +extern int32_t *diag_mutual_new; +extern int32_t *diag_mutual_old; +extern uint8_t diag_max_cnt; +extern uint8_t hx_state_info[2]; +extern uint8_t diag_coor[128]; +extern int32_t diag_self[100]; +extern int32_t diag_self_new[100]; +extern int32_t diag_self_old[100]; +int32_t *getMutualBuffer(void); +int32_t *getMutualNewBuffer(void); +int32_t *getMutualOldBuffer(void); +int32_t *getSelfBuffer(void); +int32_t *getSelfNewBuffer(void); +int32_t *getSelfOldBuffer(void); +void setMutualBuffer(uint8_t x_num, uint8_t y_num); +void setMutualNewBuffer(uint8_t x_num, uint8_t y_num); +void setMutualOldBuffer(uint8_t x_num, uint8_t y_num); +uint8_t process_type; +uint8_t mode_flag; +uint8_t overflow; + +#define HIMAX_PROC_DEBUG_FILE "debug" +extern struct proc_dir_entry *himax_proc_debug_file; +extern bool fw_update_complete; +extern int handshaking_result; +extern unsigned char debug_level_cmd; +extern uint8_t cmd_set[8]; +extern uint8_t mutual_set_flag; + +#define HIMAX_PROC_FLASH_DUMP_FILE "flash_dump" +extern struct proc_dir_entry *himax_proc_flash_dump_file; +extern int Flash_Size; +extern uint8_t *flash_buffer; +extern uint8_t g_flash_cmd; +extern uint8_t g_flash_progress; +extern bool g_flash_dump_rst; /*Fail = 0, Pass = 1*/ +void setFlashBuffer(void); + +enum flash_dump_prog { + START, + ONGOING, + FINISHED, +}; + +extern uint32_t **raw_data_array; +extern uint8_t X_NUM4; +extern uint8_t Y_NUM; +extern uint8_t sel_type; + +/* Moved from debug.c */ +extern struct himax_debug *debug_data; +extern unsigned char IC_CHECKSUM; +extern int i2c_error_count; +extern struct proc_dir_entry *himax_touch_proc_dir; + +#ifdef HX_TP_PROC_GUEST_INFO +extern struct hx_guest_info *g_guest_info_data; +extern char *g_guest_info_item[]; +#endif + +extern int himax_input_register(struct himax_ts_data *ts); +#ifdef HX_TP_PROC_2T2R + extern bool Is_2T2R; +#endif + +#ifdef HX_RST_PIN_FUNC + extern void himax_ic_reset(uint8_t loadconfig, uint8_t int_off); +#endif + +#if defined(HX_ZERO_FLASH) +extern char *i_CTPM_firmware_name; +#endif + +extern uint8_t HX_PROC_SEND_FLAG; +extern struct himax_target_report_data *g_target_report_data; +extern struct himax_report_data *hx_touch_data; +extern int g_ts_dbg; + +/* Moved from debug.c end */ +#define BUF_SIZE 1024 +#define CMD_NUM 15 +char *dbg_cmd_str[] = {"crc_test", "fw_debug", "attn", "layout", "dd_debug", + "esd_cnt", "senseonoff", "debug_level", "guest_info", "int_en", + "register", "reset", "diag_arr", "diag", NULL}; +int dbg_cmd_flag; +char *dbg_cmd_par; +ssize_t (*dbg_func_ptr_r[CMD_NUM])(char *buf, size_t len); +ssize_t (*dbg_func_ptr_w[CMD_NUM])(char *buf, size_t len); +#endif diff --git a/drivers/input/touchscreen/himax_v2_mmi/himax_ic_core.h b/drivers/input/touchscreen/himax_v2_mmi/himax_ic_core.h new file mode 100644 index 000000000000..f6010d6ac244 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/himax_ic_core.h @@ -0,0 +1,982 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for ic core functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef __HIMAX_IC_CORE_H__ +#define __HIMAX_IC_CORE_H__ + +#include "himax_platform.h" +#include "himax_common.h" +#include + +#define DATA_LEN_8 8 +#define DATA_LEN_4 4 +#define ADDR_LEN_4 4 +#define FLASH_RW_MAX_LEN 256 +#define FLASH_WRITE_BURST_SZ 8 +#define PROGRAM_SZ 48 +#define MAX_I2C_TRANS_SZ 128 +#define HIMAX_REG_RETRY_TIMES 5 +#define FW_BIN_16K_SZ 0x4000 +#define HIMAX_TOUCH_DATA_SIZE 128 +#define MASK_BIT_0 0x01 +#define MASK_BIT_1 0x02 +#define MASK_BIT_2 0x04 + +#define FW_SECTOR_PER_BLOCK 8 +#define FW_PAGE_PER_SECTOR 64 +#define FW_PAGE_SEZE 128 +#define HX256B 0x100 +#define HX4K 0x1000 +#define HX_32K_SZ 0x8000 +#define HX_40K_SZ 0xA000 +#define HX_48K_SZ 0xC000 +#define HX64K 0x10000 +#define HX124K 0x1f000 +#define HX4000K 0x1000000 + +#define HX_NORMAL_MODE 1 +#define HX_SORTING_MODE 2 +#define HX_CHANGE_MODE_FAIL (-1) +#define HX_RW_REG_FAIL (-1) +#define HX_DRIVER_MAX_IC_NUM 12 + +#if defined(__HIMAX_HX852xG_MOD__) +#define HX_MOD_KSYM_HX852xG HX_MOD_KSYM_HX852xG +#endif + +#if defined(__HIMAX_HX852xH_MOD__) +#define HX_MOD_KSYM_HX852xH HX_MOD_KSYM_HX852xH +#endif + +#if defined(__HIMAX_HX83102_MOD__) +#define HX_MOD_KSYM_HX83102 HX_MOD_KSYM_HX83102 +#endif + +#if defined(__HIMAX_HX83102D_MOD__) +#define HX_MOD_KSYM_HX83102 HX_MOD_KSYM_HX83102 +#define HX83102D +#endif + +#if defined(__HIMAX_HX83103_MOD__) +#define HX_MOD_KSYM_HX83103 HX_MOD_KSYM_HX83103 +#endif + +#if defined(__HIMAX_HX83106_MOD__) +#define HX_MOD_KSYM_HX83106 HX_MOD_KSYM_HX83106 +#endif + +#if defined(__HIMAX_HX83111_MOD__) +#define HX_MOD_KSYM_HX83111 HX_MOD_KSYM_HX83111 +#endif + +#if defined(__HIMAX_HX83112_MOD__) +#define HX_MOD_KSYM_HX83112 HX_MOD_KSYM_HX83112 +#endif + +#if defined(__HIMAX_HX83191_MOD__) +#define HX_MOD_KSYM_HX83191 HX_MOD_KSYM_HX83191 +#endif + +#if defined(__HIMAX_HX83113_MOD__) +#define HX_MOD_KSYM_HX83113 HX_MOD_KSYM_HX83113 +#endif + +#define CORE_INIT +#define CORE_IC +#define CORE_FW +#define CORE_FLASH +#define CORE_SRAM +#define CORE_DRIVER + +#define HX_0F_DEBUG + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INCELL + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) + extern char *i_CTPM_firmware_name; +#endif + +#ifdef HX_ZERO_FLASH + extern int g_f_0f_updat; +#endif + +#ifdef HX_TP_PROC_GUEST_INFO + extern struct hx_guest_info *g_guest_info_data; +#endif + +void himax_mcu_in_cmd_struct_free(void); + +#endif + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_ONCELL + +void himax_mcu_on_cmd_struct_free(void); + +#endif + +#ifdef HX_AUTO_UPDATE_FW +extern int g_i_FW_VER; +extern int g_i_CFG_VER; +extern int g_i_CID_MAJ; +extern int g_i_CID_MIN; +extern unsigned char *i_CTPM_FW; +#endif + +extern unsigned long FW_VER_MAJ_FLASH_ADDR; +extern unsigned long FW_VER_MIN_FLASH_ADDR; +extern unsigned long CFG_VER_MAJ_FLASH_ADDR; +extern unsigned long CFG_VER_MIN_FLASH_ADDR; +extern unsigned long CID_VER_MAJ_FLASH_ADDR; +extern unsigned long CID_VER_MIN_FLASH_ADDR; + +extern unsigned long FW_VER_MAJ_FLASH_LENG; +extern unsigned long FW_VER_MIN_FLASH_LENG; +extern unsigned long CFG_VER_MAJ_FLASH_LENG; +extern unsigned long CFG_VER_MIN_FLASH_LENG; +extern unsigned long CID_VER_MAJ_FLASH_LENG; +extern unsigned long CID_VER_MIN_FLASH_LENG; + +extern unsigned char IC_CHECKSUM; +#ifdef HX_ESD_RECOVERY +extern int g_zero_event_count; +#endif + +#ifdef HX_RST_PIN_FUNC +extern u8 HX_HW_RESET_ACTIVATE; +void himax_rst_gpio_set(int pinnum, uint8_t value); +#endif + +#if defined(HX_USB_DETECT_GLOBAL) +void himax_cable_detect_func(bool force_renew); +#endif + +int himax_report_data_init(void); +extern int i2c_error_count; + +#ifdef HX_ESD_RECOVERY + extern u8 HX_ESD_RESET_ACTIVATE; +#endif + +#ifdef CORE_INIT + int himax_mcu_in_cmd_struct_init(void); + void himax_in_parse_assign_cmd(uint32_t addr, uint8_t *cmd, int len); + void himax_mcu_in_cmd_init(void); + + int himax_mcu_on_cmd_struct_init(void); + void himax_on_parse_assign_cmd(uint32_t addr, uint8_t *cmd, int len); + void himax_mcu_on_cmd_init(void); + + extern void (*himax_mcu_cmd_struct_free)(void); +#endif + +#ifdef HX_TP_PROC_GUEST_INFO +#define HX_GUEST_INFO_SIZE 10 +#define HX_GUEST_INFO_LEN_SIZE 4 +#define HX_GUEST_INFO_ID_SIZE 4 + +struct hx_guest_info { + int g_guest_info_ongoing; /* 0 stop, 1 ongoing */ + uint8_t g_guest_str[10][128]; + uint8_t g_guest_str_in_format[10][128]; + uint8_t g_guest_data_type[10]; + int g_guest_data_len[10]; + int g_guest_info_type; +}; + +#endif + +#if defined(CORE_IC) + #define ic_adr_ahb_addr_byte_0 0x00 + #define ic_adr_ahb_rdata_byte_0 0x08 + #define ic_adr_ahb_access_direction 0x0c + #define ic_adr_conti 0x13 + #define ic_adr_incr4 0x0D + #define ic_adr_i2c_psw_lb 0x31 + #define ic_adr_i2c_psw_ub 0x32 + #define ic_cmd_ahb_access_direction_read 0x00 + #define ic_cmd_conti 0x31 + #define ic_cmd_incr4 0x10 + #define ic_cmd_i2c_psw_lb 0x27 + #define ic_cmd_i2c_psw_ub 0x95 + #define ic_adr_tcon_on_rst 0x80020020 + #define ic_addr_adc_on_rst 0x80020094 + #define ic_adr_psl 0x900000A0 + #define ic_adr_cs_central_state 0x900000A8 + #define ic_cmd_rst 0x00000000 + #define ic_adr_osc_en 0x900880A8 + #define ic_adr_osc_pw 0x900880E0 + + #define on_ic_adr_ahb_addr_byte_0 0x00 + #define on_ic_adr_ahb_rdata_byte_0 0x08 + #define on_ic_adr_ahb_access_direction 0x0c + #define on_ic_adr_conti 0x13 + #define on_ic_adr_incr4 0x0D + #define on_ic_cmd_ahb_access_direction_read 0x00 + #define on_ic_cmd_conti 0x31 + #define on_ic_cmd_incr4 0x10 + #define on_ic_adr_mcu_ctrl 0x82 + #define on_ic_cmd_mcu_on 0x25 + #define on_ic_cmd_mcu_off 0xDA + #define on_ic_adr_sleep_ctrl 0x99 + #define on_ic_cmd_sleep_in 0x80 + #define on_ic_adr_tcon_ctrl 0x80020000 + #define on_ic_cmd_tcon_on 0x00000000 + #define on_ic_adr_wdg_ctrl 0x9000800C + #define on_ic_cmd_wdg_psw 0x0000AC53 + #define on_ic_adr_wdg_cnt_ctrl 0x90008010 + #define on_ic_cmd_wdg_cnt_clr 0x000035CA +#endif + +#if defined(CORE_FW) + #define fw_addr_system_reset 0x90000018 + #define fw_addr_safe_mode_release_pw 0x90000098 + #define fw_addr_ctrl_fw 0x9000005c + #define fw_addr_flag_reset_event 0x900000e4 + #define fw_addr_hsen_enable 0x10007F14 + #define fw_addr_smwp_enable 0x10007F10 + #define fw_usb_detect_addr 0x10007F38 + #define fw_addr_program_reload_from 0x00000000 + #define fw_addr_program_reload_to 0x08000000 + #define fw_addr_program_reload_page_write 0x0000fb00 + #define fw_addr_raw_out_sel 0x800204b4 + #define fw_addr_reload_status 0x80050000 + #define fw_addr_reload_crc32_result 0x80050018 + #define fw_addr_reload_addr_from 0x80050020 + #define fw_addr_reload_addr_cmd_beat 0x80050028 + #define fw_data_system_reset 0x00000055 + #define fw_data_safe_mode_release_pw_active 0x00000053 + #define fw_data_safe_mode_release_pw_reset 0x00000000 + #define fw_data_clear 0x00000000 + #define fw_data_fw_stop 0x000000A5 + #define fw_data_program_reload_start 0x0A3C3000 + #define fw_data_program_reload_compare 0x04663000 + #define fw_data_program_reload_break 0x15E75678 + #define fw_addr_selftest_addr_en 0x10007F18 + #define fw_addr_selftest_result_addr 0x10007f24 + #define fw_data_selftest_request 0x00006AA6 + #define fw_addr_criteria_addr 0x10007f1c + #define fw_data_criteria_aa_top 0x64 + #define fw_data_criteria_aa_bot 0x00 + #define fw_data_criteria_key_top 0x64 + #define fw_data_criteria_key_bot 0x00 + #define fw_data_criteria_avg_top 0x64 + #define fw_data_criteria_avg_bot 0x00 + #define fw_addr_set_frame_addr 0x10007294 + #define fw_data_set_frame 0x0000000A + #define fw_data_selftest_ack_hb 0xa6 + #define fw_data_selftest_ack_lb 0x6a + #define fw_data_selftest_pass 0xaa + #define fw_data_normal_cmd 0x00 + #define fw_data_normal_status 0x99 + #define fw_data_sorting_cmd 0xaa + #define fw_data_sorting_status 0xcc + #define fw_data_idle_dis_pwd 0x17 + #define fw_data_idle_en_pwd 0x1f + #define fw_addr_sorting_mode_en 0x10007f04 + #define fw_addr_max_dc 0x10007fc8 + #define fw_addr_fw_mode_status 0x10007088 + #define fw_addr_icid_addr 0x900000d0 + #define fw_addr_trigger_addr 0x10007088 + #define fw_addr_fw_ver_addr 0x10007004 + #define fw_addr_fw_cfg_addr 0x10007084 + #define fw_addr_fw_vendor_addr 0x10007000 + #define fw_addr_cus_info 0x10007008 + #define fw_addr_proj_info 0x10007014 + #define fw_addr_fw_state_addr 0x900000f8 + #define fw_addr_fw_dbg_msg_addr 0x10007f40 + #define fw_addr_chk_fw_status 0x900000a8 + #define fw_addr_dd_handshak_addr 0x900000fc + #define fw_addr_dd_data_addr 0x10007f80 + #define fw_data_dd_request 0xaa + #define fw_data_dd_ack 0xbb + #define fw_data_rawdata_ready_hb 0xa3 + #define fw_data_rawdata_ready_lb 0x3a + #define fw_addr_ahb_addr 0x11 + #define fw_data_ahb_dis 0x00 + #define fw_data_ahb_en 0x01 + #define fw_addr_event_addr 0x30 + #define fw_func_handshaking_pwd 0xA55AA55A + #define fw_func_handshaking_end 0x77887788 + #define fw_addr_ulpm_33 0x33 + #define fw_addr_ulpm_34 0x34 + #define fw_data_ulpm_11 0x11 + #define fw_data_ulpm_22 0x22 + #define fw_data_ulpm_33 0x33 + #define fw_data_ulpm_aa 0xAA + + #define fw_func_edge_portrait_pwd 0xA55AA55A + #define fw_func_edge_lsp_left_pwd 0xA11AA11A + #define fw_func_edge_lsp_right_pwd 0xA33AA33A + + #define on_fw_addr_smwp_enable 0xA2 + #define on_fw_usb_detect_addr 0xA4 + #define on_fw_addr_program_reload_from 0x00000000 + #define on_fw_addr_raw_out_sel 0x98 + #define on_fw_addr_flash_checksum 0x80000044 + #define on_fw_data_flash_checksum 0x00000491 + #define on_fw_addr_crc_value 0x80000050 + #define on_fw_data_safe_mode_release_pw_active 0x00000053 + #define on_fw_data_safe_mode_release_pw_reset 0x00000000 + #define on_fw_addr_criteria_addr 0x9A + #define on_fw_data_selftest_pass 0xaa + #define on_fw_addr_reK_crtl 0x8000000C + #define on_fw_data_reK_en 0x02 + #define on_fw_data_reK_dis 0xFD + #define on_fw_data_rst_init 0xF0 + #define on_fw_data_dc_set 0x02 + #define on_fw_data_bank_set 0x03 + #define on_fw_addr_selftest_addr_en 0x98 + #define on_fw_addr_selftest_result_addr 0x9B + #define on_fw_data_selftest_request 0x06 + #define on_fw_data_thx_avg_mul_dc_lsb 0x22 + #define on_fw_data_thx_avg_mul_dc_msb 0x0B + #define on_fw_data_thx_mul_dc_up_low_bud 0x64 + #define on_fw_data_thx_avg_slf_dc_lsb 0x14 + #define on_fw_data_thx_avg_slf_dc_msb 0x05 + #define on_fw_data_thx_slf_dc_up_low_bud 0x64 + #define on_fw_data_thx_slf_bank_up 0x40 + #define on_fw_data_thx_slf_bank_low 0x00 + #define on_fw_data_idle_dis_pwd 0x40 + #define on_fw_data_idle_en_pwd 0x00 + #define on_fw_addr_fw_mode_status 0x99 + #define on_fw_addr_icid_addr 0x900000d0 + #define on_fw_addr_trigger_addr 0x10007088 + #define on_fw_addr_fw_ver_start 0x90 + #define on_fw_data_rawdata_ready_hb 0xa3 + #define on_fw_data_rawdata_ready_lb 0x3a + #define on_fw_addr_ahb_addr 0x11 + #define on_fw_data_ahb_dis 0x00 + #define on_fw_data_ahb_en 0x01 + #define on_fw_addr_event_addr 0x30 +#endif + +#if defined(CORE_FLASH) + #define flash_addr_ctrl_base 0x80000000 + #define flash_addr_spi200_trans_fmt (flash_addr_ctrl_base + 0x10) + #define flash_addr_spi200_trans_ctrl (flash_addr_ctrl_base + 0x20) + #define flash_addr_spi200_cmd (flash_addr_ctrl_base + 0x24) + #define flash_addr_spi200_addr (flash_addr_ctrl_base + 0x28) + #define flash_addr_spi200_data (flash_addr_ctrl_base + 0x2c) + #define flash_addr_spi200_bt_num (flash_addr_ctrl_base + 0xe8) + #define flash_data_spi200_trans_fmt 0x00020780 + #define flash_data_spi200_trans_ctrl_1 0x42000003 + #define flash_data_spi200_trans_ctrl_2 0x47000000 + #define flash_data_spi200_trans_ctrl_3 0x67000000 + #define flash_data_spi200_trans_ctrl_4 0x610ff000 + #define flash_data_spi200_trans_ctrl_5 0x694002ff + #define flash_data_spi200_cmd_1 0x00000005 + #define flash_data_spi200_cmd_2 0x00000006 + #define flash_data_spi200_cmd_3 0x000000C7 + #define flash_data_spi200_cmd_4 0x000000D8 + #define flash_data_spi200_cmd_5 0x00000020 + #define flash_data_spi200_cmd_6 0x00000002 + #define flash_data_spi200_cmd_7 0x0000003b + #define flash_data_spi200_addr 0x00000000 + + #define on_flash_addr_ctrl_base 0x80000000 + #define on_flash_addr_ctrl_auto 0x80000004 + #define on_flash_data_main_erase 0x0000A50D + #define on_flash_data_auto 0xA5 + #define on_flash_data_main_read 0x03 + #define on_flash_data_page_write 0x05 + #define on_flash_data_spp_read 0x10 + #define on_flash_data_sfr_read 0x14 + #define on_flash_addr_ahb_ctrl 0x80000020 + #define on_flash_data_ahb_squit 0x00000001 + #define on_flash_addr_unlock_0 0x00000000 + #define on_flash_addr_unlock_4 0x00000004 + #define on_flash_addr_unlock_8 0x00000008 + #define on_flash_addr_unlock_c 0x0000000C + #define on_flash_data_cmd0 0x28178EA0 + #define on_flash_data_cmd1 0x0A0E03FF + #define on_flash_data_cmd2 0x8C203D0C + #define on_flash_data_cmd3 0x00300263 + #define on_flash_data_lock 0x03400000 +#endif + +#if defined(CORE_SRAM) + #define sram_adr_mkey 0x100070E8 + #define sram_adr_rawdata_addr 0x10000000 + #define sram_adr_rawdata_end 0x00000000 + #define sram_passwrd_start 0x5AA5 + #define sram_passwrd_end 0xA55A + + #define on_sram_adr_rawdata_addr 0x080002E0 + #define on_sram_adr_rawdata_end 0x00000000 + #define on_sram_cmd_conti 0x44332211 + #define on_sram_cmd_fin 0x00000000 + #define on_sram_passwrd_start 0x5AA5 + #define on_sram_passwrd_end 0xA55A +#endif + +#if defined(CORE_DRIVER) + #define driver_addr_fw_define_flash_reload 0x10007f00 + #define driver_addr_fw_define_2nd_flash_reload 0x100072c0 + #define driver_data_fw_define_flash_reload_dis 0x0000a55a + #define driver_data_fw_define_flash_reload_en 0x00000000 + #define driver_addr_fw_define_int_is_edge 0x10007088 + #define driver_addr_fw_define_rxnum_txnum_maxpt 0x100070f4 + #define driver_data_fw_define_rxnum_txnum_maxpt_sorting 0x00000008 + #define driver_data_fw_define_rxnum_txnum_maxpt_normal 0x00000014 + #define driver_addr_fw_define_xy_res_enable 0x100070f8 + #define driver_addr_fw_define_x_y_res 0x100070fc + #define driver_data_df_rx 36 + #define driver_data_df_tx 18 + #define driver_data_df_pt 10 + #define driver_data_df_x_res 1080 + #define driver_data_df_y_res 1920 + #define on_driver_addr_fw_define_int_is_edge 0x10007088 + #define on_driver_data_df_rx 28 + #define on_driver_data_df_tx 14 + #define on_driver_data_df_pt 10 + #define on_driver_data_df_x_res 1080 + #define on_driver_data_df_y_res 1920 +#ifndef HX_NEW_EVENT_STACK_FORMAT + #define on_driver_addr_fw_rx_tx_maxpt_num 0x0800001C + #define on_driver_addr_fw_xy_rev_int_edge 0x0800000C + #define on_driver_addr_fw_define_x_y_res 0x08000030 +#else + #define on_driver_addr_fw_rx_tx_maxpt_num 0x08000004 + #define on_driver_addr_fw_maxpt_bt_num 0x0800000C + #define on_driver_addr_fw_xy_rev_int_edge 0x08000110 + #define on_driver_addr_fw_define_x_y_res 0x08000010 +#endif + +#endif + +#if defined(HX_ZERO_FLASH) + #define zf_addr_dis_flash_reload 0x10007f00 + #define zf_data_dis_flash_reload 0x00009AA9 + #define zf_addr_system_reset 0x90000018 + #define zf_data_system_reset 0x00000055 + #define zf_data_sram_start_addr 0x08000000 + #define zf_data_sram_clean 0x10000000 + #define zf_data_cfg_info 0x10007000 + #define zf_data_fw_cfg_1 0x10007084 + #define zf_data_fw_cfg_2 0x10007264 + #define zf_data_fw_cfg_3 0x10007300 + #define zf_data_adc_cfg_1 0x10006A00 + #define zf_data_adc_cfg_2 0x10007B28 + #define zf_data_adc_cfg_3 0x10007AF0 + #define zf_data_map_table 0x10007500 + #define zf_data_mode_switch 0x10007294 + #define zf_addr_sts_chk 0x900000A8 + #define zf_data_activ_sts 0x05 + #define zf_addr_activ_relod 0x90000048 + #define zf_data_activ_in 0xEC + +#if defined(HX_CODE_OVERLAY) +#if defined(HX83102D) + #define ovl_section_num 3 + #define ovl_gesture_request 0x11 + #define ovl_gesture_reply 0x22 + #define ovl_border_request 0x55 + #define ovl_border_reply 0x66 + #define ovl_sorting_request 0x99 + #define ovl_sorting_reply 0xAA + #define ovl_fault 0xFF +#else + #define ovl_isram_addr 0x20008CE0 + #define ovl_handshaking_addr 0x10007FFC + #define ovl_gesture_request 0x11 + #define ovl_gesture_reply 0x22 + #define ovl_border_request 0x55 + #define ovl_border_reply 0x66 +#endif +#endif + +struct zf_info { + uint8_t sram_addr[4]; + int write_size; + uint32_t fw_addr; + uint32_t cfg_addr; +}; +#endif + +#ifdef HIMAX_PALM_SENSOR_EN + #define palm_detection_register 0x10007FF4 + #define palm_detection_register_enable 0xA55AA55A + #define palm_detection_register_disable 0x00000000 +#endif + +struct ic_operation { + uint8_t addr_ahb_addr_byte_0[1]; + uint8_t addr_ahb_rdata_byte_0[1]; + uint8_t addr_ahb_access_direction[1]; + uint8_t addr_conti[1]; + uint8_t addr_incr4[1]; + uint8_t adr_i2c_psw_lb[1]; + uint8_t adr_i2c_psw_ub[1]; + uint8_t data_ahb_access_direction_read[1]; + uint8_t data_conti[1]; + uint8_t data_incr4[1]; + uint8_t data_i2c_psw_lb[1]; + uint8_t data_i2c_psw_ub[1]; + uint8_t addr_tcon_on_rst[4]; + uint8_t addr_adc_on_rst[4]; + uint8_t addr_psl[4]; + uint8_t addr_cs_central_state[4]; + uint8_t data_rst[4]; + uint8_t adr_osc_en[4]; + uint8_t adr_osc_pw[4]; +}; + +struct fw_operation { + uint8_t addr_system_reset[4]; + uint8_t addr_safe_mode_release_pw[4]; + uint8_t addr_ctrl_fw_isr[4]; + uint8_t addr_flag_reset_event[4]; + uint8_t addr_hsen_enable[4]; + uint8_t addr_smwp_enable[4]; +#ifdef HX_EDGE_LIMIT + uint8_t addr_edge_limit_enable[4]; +#endif +#ifdef HIMAX_PALM_SENSOR_EN + uint8_t addr_palm_detection_enable[4]; +#endif + uint8_t addr_program_reload_from[4]; + uint8_t addr_program_reload_to[4]; + uint8_t addr_program_reload_page_write[4]; + uint8_t addr_raw_out_sel[4]; + uint8_t addr_reload_status[4]; + uint8_t addr_reload_crc32_result[4]; + uint8_t addr_reload_addr_from[4]; + uint8_t addr_reload_addr_cmd_beat[4]; + uint8_t addr_selftest_addr_en[4]; + uint8_t addr_criteria_addr[4]; + uint8_t addr_set_frame_addr[4]; + uint8_t addr_selftest_result_addr[4]; + uint8_t addr_sorting_mode_en[4]; + uint8_t addr_max_dc[4]; + uint8_t addr_fw_mode_status[4]; + uint8_t addr_icid_addr[4]; + uint8_t addr_trigger_addr[4]; + uint8_t addr_fw_ver_addr[4]; + uint8_t addr_fw_cfg_addr[4]; + uint8_t addr_fw_vendor_addr[4]; + uint8_t addr_cus_info[4]; + uint8_t addr_proj_info[4]; + uint8_t addr_fw_state_addr[4]; + uint8_t addr_fw_dbg_msg_addr[4]; + uint8_t addr_chk_fw_status[4]; + uint8_t addr_dd_handshak_addr[4]; + uint8_t addr_dd_data_addr[4]; + uint8_t data_system_reset[4]; + uint8_t data_safe_mode_release_pw_active[4]; + uint8_t data_safe_mode_release_pw_reset[4]; + uint8_t data_clear[4]; + uint8_t data_fw_stop[4]; + uint8_t data_program_reload_start[4]; + uint8_t data_program_reload_compare[4]; + uint8_t data_program_reload_break[4]; + uint8_t data_selftest_request[4]; + uint8_t data_criteria_aa_top[1]; + uint8_t data_criteria_aa_bot[1]; + uint8_t data_criteria_key_top[1]; + uint8_t data_criteria_key_bot[1]; + uint8_t data_criteria_avg_top[1]; + uint8_t data_criteria_avg_bot[1]; + uint8_t data_set_frame[4]; + uint8_t data_selftest_ack_hb[1]; + uint8_t data_selftest_ack_lb[1]; + uint8_t data_selftest_pass[1]; + uint8_t data_normal_cmd[1]; + uint8_t data_normal_status[1]; + uint8_t data_sorting_cmd[1]; + uint8_t data_sorting_status[1]; + uint8_t data_dd_request[1]; + uint8_t data_dd_ack[1]; + uint8_t data_idle_dis_pwd[1]; + uint8_t data_idle_en_pwd[1]; + uint8_t data_rawdata_ready_hb[1]; + uint8_t data_rawdata_ready_lb[1]; + uint8_t addr_ahb_addr[1]; + uint8_t data_ahb_dis[1]; + uint8_t data_ahb_en[1]; + uint8_t addr_event_addr[1]; + uint8_t addr_usb_detect[4]; + uint8_t addr_ulpm_33[1]; + uint8_t addr_ulpm_34[1]; + uint8_t data_ulpm_11[1]; + uint8_t data_ulpm_22[1]; + uint8_t data_ulpm_33[1]; + uint8_t data_ulpm_aa[1]; +}; + +struct flash_operation { + uint8_t addr_spi200_trans_fmt[4]; + uint8_t addr_spi200_trans_ctrl[4]; + uint8_t addr_spi200_cmd[4]; + uint8_t addr_spi200_addr[4]; + uint8_t addr_spi200_data[4]; + uint8_t addr_spi200_bt_num[4]; + + uint8_t data_spi200_trans_fmt[4]; + uint8_t data_spi200_trans_ctrl_1[4]; + uint8_t data_spi200_trans_ctrl_2[4]; + uint8_t data_spi200_trans_ctrl_3[4]; + uint8_t data_spi200_trans_ctrl_4[4]; + uint8_t data_spi200_trans_ctrl_5[4]; + uint8_t data_spi200_cmd_1[4]; + uint8_t data_spi200_cmd_2[4]; + uint8_t data_spi200_cmd_3[4]; + uint8_t data_spi200_cmd_4[4]; + uint8_t data_spi200_cmd_5[4]; + uint8_t data_spi200_cmd_6[4]; + uint8_t data_spi200_cmd_7[4]; + uint8_t data_spi200_addr[4]; +}; + +struct sram_operation { + uint8_t addr_mkey[4]; + uint8_t addr_rawdata_addr[4]; + uint8_t addr_rawdata_end[4]; + uint8_t passwrd_start[2]; + uint8_t passwrd_end[2]; +}; + +struct driver_operation { + uint8_t addr_fw_define_flash_reload[4]; + uint8_t addr_fw_define_2nd_flash_reload[4]; + uint8_t addr_fw_define_int_is_edge[4]; + uint8_t addr_fw_define_rxnum_txnum_maxpt[4]; + uint8_t addr_fw_define_xy_res_enable[4]; + uint8_t addr_fw_define_x_y_res[4]; + uint8_t data_df_rx[1]; + uint8_t data_df_tx[1]; + uint8_t data_df_pt[1]; + uint8_t data_df_x_res[2]; + uint8_t data_df_y_res[2]; + uint8_t data_fw_define_flash_reload_dis[4]; + uint8_t data_fw_define_flash_reload_en[4]; + uint8_t data_fw_define_rxnum_txnum_maxpt_sorting[4]; + uint8_t data_fw_define_rxnum_txnum_maxpt_normal[4]; +}; + +struct zf_operation { + uint8_t addr_dis_flash_reload[4]; + uint8_t data_dis_flash_reload[4]; + uint8_t addr_system_reset[4]; + uint8_t data_system_reset[4]; + uint8_t data_sram_start_addr[4]; + uint8_t data_sram_clean[4]; + uint8_t data_cfg_info[4]; + uint8_t data_fw_cfg_1[4]; + uint8_t data_fw_cfg_2[4]; + uint8_t data_fw_cfg_3[4]; + uint8_t data_adc_cfg_1[4]; + uint8_t data_adc_cfg_2[4]; + uint8_t data_adc_cfg_3[4]; + uint8_t data_map_table[4]; + uint8_t data_mode_switch[4]; + uint8_t addr_sts_chk[4]; + uint8_t data_activ_sts[1]; + uint8_t addr_activ_relod[4]; + uint8_t data_activ_in[1]; +}; + +struct himax_core_command_operation { + struct ic_operation *ic_op; + struct fw_operation *fw_op; + struct flash_operation *flash_op; + struct sram_operation *sram_op; + struct driver_operation *driver_op; + struct zf_operation *zf_op; +}; + +struct on_ic_operation { + uint8_t addr_ahb_addr_byte_0[1]; + uint8_t addr_ahb_rdata_byte_0[1]; + uint8_t addr_ahb_access_direction[1]; + uint8_t addr_conti[1]; + uint8_t addr_incr4[1]; + uint8_t adr_mcu_ctrl[1]; + uint8_t data_ahb_access_direction_read[1]; + uint8_t data_conti[1]; + uint8_t data_incr4[1]; + uint8_t cmd_mcu_on[1]; + uint8_t cmd_mcu_off[1]; + uint8_t adr_sleep_ctrl[1]; + uint8_t cmd_sleep_in[1]; + uint8_t adr_tcon_ctrl[4]; + uint8_t cmd_tcon_on[4]; + uint8_t adr_wdg_ctrl[4]; + uint8_t cmd_wdg_psw[4]; + uint8_t adr_wdg_cnt_ctrl[4]; + uint8_t cmd_wdg_cnt_clr[4]; +}; + +struct on_fw_operation { + uint8_t addr_smwp_enable[1]; + uint8_t addr_program_reload_from[4]; + uint8_t addr_raw_out_sel[1]; + uint8_t addr_flash_checksum[4]; + uint8_t data_flash_checksum[4]; + uint8_t addr_crc_value[4]; + uint8_t addr_reload_status[4]; + uint8_t addr_reload_crc32_result[4]; + uint8_t addr_reload_addr_from[4]; + uint8_t addr_reload_addr_cmd_beat[4]; + uint8_t addr_set_frame_addr[4]; + uint8_t addr_fw_mode_status[1]; + uint8_t addr_icid_addr[4]; + uint8_t addr_trigger_addr[4]; + uint8_t addr_fw_ver_start[1]; + uint8_t data_safe_mode_release_pw_active[4]; + uint8_t data_safe_mode_release_pw_reset[4]; + uint8_t data_clear[4]; + uint8_t addr_criteria_addr[1]; + uint8_t data_selftest_pass[1]; + uint8_t addr_reK_crtl[4]; + uint8_t data_reK_en[1]; + uint8_t data_reK_dis[1]; + uint8_t data_rst_init[1]; + uint8_t data_dc_set[1]; + uint8_t data_bank_set[1]; + uint8_t addr_selftest_addr_en[1]; + uint8_t addr_selftest_result_addr[1]; + uint8_t data_selftest_request[1]; + uint8_t data_thx_avg_mul_dc_lsb[1]; + uint8_t data_thx_avg_mul_dc_msb[1]; + uint8_t data_thx_mul_dc_up_low_bud[1]; + uint8_t data_thx_avg_slf_dc_lsb[1]; + uint8_t data_thx_avg_slf_dc_msb[1]; + uint8_t data_thx_slf_dc_up_low_bud[1]; + uint8_t data_thx_slf_bank_up[1]; + uint8_t data_thx_slf_bank_low[1]; + uint8_t data_idle_dis_pwd[1]; + uint8_t data_idle_en_pwd[1]; + uint8_t data_rawdata_ready_hb[1]; + uint8_t data_rawdata_ready_lb[1]; + uint8_t addr_ahb_addr[1]; + uint8_t data_ahb_dis[1]; + uint8_t data_ahb_en[1]; + uint8_t addr_event_addr[1]; + uint8_t addr_usb_detect[1]; +}; + +struct on_flash_operation { + uint8_t addr_ctrl_base[4]; + uint8_t addr_ctrl_auto[4]; + uint8_t data_main_erase[4]; + uint8_t data_auto[1]; + uint8_t data_main_read[1]; + uint8_t data_page_write[1]; + uint8_t data_sfr_read[1]; + uint8_t data_spp_read[1]; + uint8_t addr_ahb_ctrl[4]; + uint8_t data_ahb_squit[4]; + + uint8_t addr_unlock_0[4]; + uint8_t addr_unlock_4[4]; + uint8_t addr_unlock_8[4]; + uint8_t addr_unlock_c[4]; + uint8_t data_cmd0[4]; + uint8_t data_cmd1[4]; + uint8_t data_cmd2[4]; + uint8_t data_cmd3[4]; + uint8_t data_lock[4]; +}; + +struct on_sram_operation { + uint8_t addr_rawdata_addr[4]; + uint8_t addr_rawdata_end[4]; + uint8_t data_conti[4]; + uint8_t data_fin[4]; + uint8_t passwrd_start[2]; + uint8_t passwrd_end[2]; +}; + +struct on_driver_operation { + uint8_t addr_fw_define_int_is_edge[4]; + uint8_t addr_fw_rx_tx_maxpt_num[4]; +#ifdef HX_NEW_EVENT_STACK_FORMAT + uint8_t addr_fw_maxpt_bt_num[4]; +#endif + uint8_t addr_fw_xy_rev_int_edge[4]; + uint8_t addr_fw_define_x_y_res[4]; + uint8_t data_fw_define_rxnum_txnum_maxpt_sorting[4]; + uint8_t data_fw_define_rxnum_txnum_maxpt_normal[4]; + uint8_t data_df_rx[1]; + uint8_t data_df_tx[1]; + uint8_t data_df_pt[1]; + uint8_t data_df_x_res[2]; + uint8_t data_df_y_res[2]; +}; + +struct himax_on_core_command_operation { + struct on_ic_operation *ic_op; + struct on_fw_operation *fw_op; + struct on_flash_operation *flash_op; + struct on_sram_operation *sram_op; + struct on_driver_operation *driver_op; +}; + +struct himax_chip_detect { + bool (*fp_chip_detect)(void); +}; + +struct himax_chip_entry { + struct himax_chip_detect *core_chip_dt; + uint32_t hx_ic_dt_num; +}; + +struct himax_core_fp { +#ifdef CORE_IC + void (*fp_burst_enable)(uint8_t auto_add_4_byte); + int (*fp_register_read)(uint8_t *read_addr, uint32_t read_length, uint8_t *read_data, uint8_t cfg_flag); + /*int (*fp_flash_write_burst)(uint8_t *reg_byte, uint8_t *write_data);*/ + /*void (*fp_flash_write_burst_lenth)(uint8_t *reg_byte, uint8_t *write_data, uint32_t length);*/ + int (*fp_register_write)(uint8_t *write_addr, uint32_t write_length, uint8_t *write_data, uint8_t cfg_flag); + void (*fp_interface_on)(void); + void (*fp_sense_on)(uint8_t FlashMode); + void (*fp_tcon_on)(void); + bool (*fp_watch_dog_off)(void); + bool (*fp_sense_off)(bool check_en); + void (*fp_sleep_in)(void); + bool (*fp_wait_wip)(int Timing); + void (*fp_init_psl)(void); + void (*fp_resume_ic_action)(void); + void (*fp_suspend_ic_action)(void); + void (*fp_power_on_init)(void); + bool (*fp_dd_clk_set)(bool enable); + void (*fp_dd_reg_en)(bool enable); + bool (*fp_dd_reg_write)(uint8_t addr, uint8_t pa_num, int len, uint8_t *data, uint8_t bank); + bool (*fp_dd_reg_read)(uint8_t addr, uint8_t pa_num, int len, uint8_t *data, uint8_t bank); + bool (*fp_ic_id_read)(void); + bool (*fp_slave_tcon_reset)(void); + bool (*fp_slave_adc_reset_slave)(void); + bool (*fp_slave_wdt_off_slave)(void); +#endif + +#ifdef CORE_FW + void (*fp_parse_raw_data)(struct himax_report_data *hx_touch_data, int mul_num, int self_num, uint8_t diag_cmd, int16_t *mutual_data, int16_t *self_data); + void (*fp_system_reset)(void); + int (*fp_Calculate_CRC_with_AP)(unsigned char *FW_content, int CRC_from_FW, int len); + uint32_t (*fp_check_CRC)(uint8_t *start_addr, int reload_length); + void (*fp_set_reload_cmd)(uint8_t *write_data, int idx, uint32_t cmd_from, uint32_t cmd_to, uint32_t cmd_beat); + bool (*fp_program_reload)(void); + void (*fp_set_SMWP_enable)(uint8_t SMWP_enable, bool suspended); +#ifdef HX_EDGE_LIMIT + void (*fp_set_edge_limit_enable)(uint8_t edge_limit_enable, bool suspended); +#endif + void (*fp_set_HSEN_enable)(uint8_t HSEN_enable, bool suspended); + void (*fp_diag_register_set)(uint8_t diag_command, uint8_t storage_type); +#ifdef HX_TP_SELF_TEST_DRIVER + void (*fp_control_reK)(bool enable); +#endif + int (*fp_chip_self_test)(void); + void (*fp_idle_mode)(int disable); + void (*fp_reload_disable)(int disable); + bool (*fp_check_chip_version)(void); + int (*fp_read_ic_trigger_type)(void); + int (*fp_read_i2c_status)(void); + void (*fp_read_FW_ver)(void); + bool (*fp_read_event_stack)(uint8_t *buf, uint8_t length); + void (*fp_return_event_stack)(void); + bool (*fp_calculateChecksum)(bool change_iref, uint32_t size); + int (*fp_read_FW_status)(uint8_t *state_addr, uint8_t *tmp_addr); + void (*fp_irq_switch)(int switch_on); + int (*fp_assign_sorting_mode)(uint8_t *tmp_data); + int (*fp_check_sorting_mode)(uint8_t *tmp_data); + int (*fp_switch_mode)(int mode); + int (*fp_get_max_dc)(void); + uint8_t (*fp_read_DD_status)(uint8_t *cmd_set, uint8_t *tmp_data); + int (*fp_ulpm_in)(void); + int (*fp_black_gest_ctrl)(bool enable); + int (*_diff_overlay_bin)(void); +#endif + +#ifdef CORE_FLASH + void (*fp_chip_erase)(void); + bool (*fp_block_erase)(int start_addr, int length); + bool (*fp_sector_erase)(int start_addr); + void (*fp_flash_programming)(uint8_t *FW_content, int FW_Size); + void (*fp_flash_page_write)(uint8_t *write_addr, int length, uint8_t *write_data); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_32k)(unsigned char *fw, int len, bool change_iref); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_60k)(unsigned char *fw, int len, bool change_iref); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_64k)(unsigned char *fw, int len, bool change_iref); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_124k)(unsigned char *fw, int len, bool change_iref); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_128k)(unsigned char *fw, int len, bool change_iref); + void (*fp_flash_dump_func)(uint8_t local_flash_command, int Flash_Size, uint8_t *flash_buffer); + bool (*fp_flash_lastdata_check)(uint32_t size); + bool (*fp_ahb_squit)(void); + void (*fp_flash_read)(uint8_t *r_data, int start_addr, int length); + bool (*fp_sfr_rw)(uint8_t *addr, int length, uint8_t *data, uint8_t rw_ctrl); + bool (*fp_lock_flash)(void); + bool (*fp_unlock_flash)(void); + void (*fp_init_auto_func)(void); + int (*_diff_overlay_flash)(void); +#endif + +#ifdef CORE_SRAM + void (*fp_sram_write)(uint8_t *FW_content); + bool (*fp_sram_verify)(uint8_t *FW_File, int FW_Size); + bool (*fp_get_DSRAM_data)(uint8_t *info_data, bool DSRAM_Flag); +#endif + +#ifdef CORE_DRIVER + void (*fp_chip_init)(void); +#ifdef HX_AUTO_UPDATE_FW + int (*fp_fw_ver_bin)(void); +#endif + void (*fp_pin_reset)(void); + void (*fp_touch_information)(void); + void (*fp_reload_config)(void); + int (*fp_get_touch_data_size)(void); + void (*fp_usb_detect_set)(uint8_t *cable_config); + int (*fp_hand_shaking)(void); + int (*fp_determin_diag_rawdata)(int diag_command); + int (*fp_determin_diag_storage)(int diag_command); + int (*fp_cal_data_len)(int raw_cnt_rmd, int HX_MAX_PT, int raw_cnt_max); + bool (*fp_diag_check_sum)(struct himax_report_data *hx_touch_data); + void (*fp_diag_parse_raw_data)(struct himax_report_data *hx_touch_data, int mul_num, int self_num, uint8_t diag_cmd, int32_t *mutual_data, int32_t *self_data); + void (*fp_ic_reset)(uint8_t loadconfig, uint8_t int_off); + int (*fp_ic_esd_recovery)(int hx_esd_event, int hx_zero_event, int length); + void (*fp_esd_ic_reset)(void); + void (*fp_resend_cmd_func)(bool suspended); +#ifdef HX_TP_PROC_GUEST_INFO + int (*guest_info_get_status)(void); + int (*read_guest_info)(void); +#endif +#endif +#ifdef HX_ZERO_FLASH + void (*fp_clean_sram_0f)(uint8_t *addr, int write_len, int type); + void (*fp_write_sram_0f)(const struct firmware *fw_entry, uint8_t *addr, int start_index, uint32_t write_len); + int (*fp_write_sram_0f_crc)(const struct firmware *fw_entry, uint8_t *addr, int start_index, uint32_t write_len); + void (*fp_firmware_update_0f)(const struct firmware *fw_entry); + int (*fp_0f_operation_dirly)(void); + int (*fp_0f_op_file_dirly)(char *file_name); + void (*fp_0f_operation)(struct work_struct *work); + int (*fp_0f_esd_check)(void); +#if defined(HX83102D) + void (*fp_0f_reload_to_active)(void); +#endif +#ifdef HX_0F_DEBUG + void (*fp_read_sram_0f)(const struct firmware *fw_entry, uint8_t *addr, int start_index, int read_len); + void (*fp_read_all_sram)(uint8_t *addr, int read_len); + void (*fp_firmware_read_0f)(const struct firmware *fw_entry, int type); + void (*fp_0f_operation_check)(int type); +#endif +#ifdef HX_CODE_OVERLAY + int (*fp_0f_overlay)(int ovl_type, int mode); +#endif +#ifdef HIMAX_PALM_SENSOR_EN + int (*fp_palm_detection_function)(int mode); +#endif +#endif +}; + +#endif diff --git a/drivers/input/touchscreen/himax_v2_mmi/himax_ic_incell_core.c b/drivers/input/touchscreen/himax_v2_mmi/himax_ic_incell_core.c new file mode 100755 index 000000000000..cac0a9357232 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/himax_ic_incell_core.c @@ -0,0 +1,4040 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for incell ic core functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_ic_core.h" + +struct himax_core_command_operation *g_core_cmd_op; +struct ic_operation *pic_op; +EXPORT_SYMBOL(pic_op); + +struct fw_operation *pfw_op; +EXPORT_SYMBOL(pfw_op); + +struct flash_operation *pflash_op; +struct sram_operation *psram_op; +struct driver_operation *pdriver_op; +EXPORT_SYMBOL(pdriver_op); + +#ifdef HX_ZERO_FLASH +struct zf_operation *pzf_op; +EXPORT_SYMBOL(pzf_op); +#if defined(HX_CODE_OVERLAY) && defined(HX83102D) +uint8_t *ovl_idx; +EXPORT_SYMBOL(ovl_idx); +#endif +#endif + +void (*himax_mcu_cmd_struct_free)(void); +static uint8_t *g_internal_buffer; + +#ifdef CORE_IC +/* IC side start*/ +static void himax_mcu_burst_enable(uint8_t auto_add_4_byte) +{ + uint8_t tmp_data[DATA_LEN_4]; + + /*I("%s,Entering\n", __func__);*/ + tmp_data[0] = pic_op->data_conti[0]; + + if (himax_bus_write(pic_op->addr_conti[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + tmp_data[0] = (pic_op->data_incr4[0] | auto_add_4_byte); + + if (himax_bus_write(pic_op->addr_incr4[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } +} + +static int himax_mcu_register_read(uint8_t *read_addr, uint32_t read_length, uint8_t *read_data, uint8_t cfg_flag) +{ + uint8_t tmp_data[DATA_LEN_4]; + int i = 0; + int address = 0; + + /*I("%s,Entering\n",__func__);*/ + + if (cfg_flag == false) { + if (read_length > FLASH_RW_MAX_LEN) { + E("%s: read len over %d!\n", __func__, FLASH_RW_MAX_LEN); + return LENGTH_FAIL; + } + + if (read_length > DATA_LEN_4) + g_core_fp.fp_burst_enable(1); + else + g_core_fp.fp_burst_enable(0); + + address = (read_addr[3] << 24) + (read_addr[2] << 16) + (read_addr[1] << 8) + read_addr[0]; + i = address; + tmp_data[0] = (uint8_t)i; + tmp_data[1] = (uint8_t)(i >> 8); + tmp_data[2] = (uint8_t)(i >> 16); + tmp_data[3] = (uint8_t)(i >> 24); + + if (himax_bus_write(pic_op->addr_ahb_addr_byte_0[0], tmp_data, DATA_LEN_4, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + tmp_data[0] = pic_op->data_ahb_access_direction_read[0]; + + if (himax_bus_write(pic_op->addr_ahb_access_direction[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + if (himax_bus_read(pic_op->addr_ahb_rdata_byte_0[0], read_data, read_length, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + if (read_length > DATA_LEN_4) + g_core_fp.fp_burst_enable(0); + + } else { + if (himax_bus_read(read_addr[0], read_data, read_length, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + } + return NO_ERR; +} + +static int himax_mcu_flash_write_burst_lenth(uint8_t *reg_byte, uint8_t *write_data, uint32_t length) +{ + uint8_t *data_byte; + + if (!g_internal_buffer) { + E("%s: internal buffer not initialized!\n", __func__); + return MEM_ALLOC_FAIL; + } + data_byte = g_internal_buffer; + + memcpy(data_byte, reg_byte, ADDR_LEN_4); /* assign addr 4bytes */ + + memcpy(data_byte + ADDR_LEN_4, write_data, length); /* assign data n bytes */ + + if (himax_bus_write(pic_op->addr_ahb_addr_byte_0[0], data_byte, length + ADDR_LEN_4, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: xfer fail!\n", __func__); + return I2C_FAIL; + } + + return NO_ERR; +} + +static int himax_mcu_register_write(uint8_t *write_addr, uint32_t write_length, uint8_t *write_data, uint8_t cfg_flag) +{ + int address; +#if 1 + uint8_t tmp_addr[4]; + uint8_t *tmp_data; + + int total_read_times = 0; + uint32_t max_bus_size = MAX_I2C_TRANS_SZ; + uint32_t total_size_temp = 0; + int i = 0; +#endif + + /*I("%s,Entering\n", __func__);*/ + if (cfg_flag == 0) { + total_size_temp = write_length; +#ifdef __MTK_SPI__ + if (write_length > (HX_SPI_MTK_MAX_WRITE_SZ - 4)) + max_bus_size = HX_SPI_MTK_MAX_WRITE_SZ - 4; +#else + if (write_length > (PACKET_MAX_SZ - 8)) + max_bus_size = PACKET_MAX_SZ - 8; +#endif + else + max_bus_size = write_length; + + tmp_addr[3] = write_addr[3]; + tmp_addr[2] = write_addr[2]; + tmp_addr[1] = write_addr[1]; + tmp_addr[0] = write_addr[0]; + + if (total_size_temp % max_bus_size == 0) + total_read_times = total_size_temp / max_bus_size; + else + total_read_times = total_size_temp / max_bus_size + 1; + + if (write_length > DATA_LEN_4) + g_core_fp.fp_burst_enable(1); + else + g_core_fp.fp_burst_enable(0); + +#if 1 + for (i = 0; i < (total_read_times); i++) { + /* I("[log]write %d time start!\n", i); + * I("[log]addr[3]=0x%02X, addr[2]=0x%02X, addr[1]=0x%02X, addr[0]=0x%02X!\n", tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + * I("%s, write addr = 0x%02X%02X%02X%02X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + */ + + if (total_size_temp >= max_bus_size) { + tmp_data = write_data+(i * max_bus_size); + if (himax_mcu_flash_write_burst_lenth(tmp_addr, tmp_data, max_bus_size) < 0) { + I("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + total_size_temp = total_size_temp - max_bus_size; + } else { + tmp_data = write_data+(i * max_bus_size); + /* I("last total_size_temp=%d\n", total_size_temp % max_bus_size); */ + if (himax_mcu_flash_write_burst_lenth(tmp_addr, tmp_data, total_size_temp) < 0) { + I("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + } + + /*I("[log]write %d time end!\n", i);*/ + address = ((i+1) * max_bus_size); + tmp_addr[0] = write_addr[0] + (uint8_t) ((address) & 0x00FF); + + if (tmp_addr[0] < write_addr[0]) + tmp_addr[1] = write_addr[1] + (uint8_t) ((address>>8) & 0x00FF) + 1; + else + tmp_addr[1] = write_addr[1] + (uint8_t) ((address>>8) & 0x00FF); + + udelay(100); + } +#else + if (himax_mcu_flash_write_burst_lenth(write_addr, write_data, write_length) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } +#endif + + } else if (cfg_flag == 1) { + if (himax_bus_write(write_addr[0], write_data, write_length, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + } else + E("%s: cfg_flag = %d, value is wrong!\n", __func__, cfg_flag); + + return NO_ERR; +} + +static int himax_write_read_reg(uint8_t *tmp_addr, uint8_t *tmp_data, uint8_t hb, uint8_t lb) +{ + int cnt = 0; + + do { + g_core_fp.fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, 0); + usleep_range(10000, 11000); + g_core_fp.fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + /* I("%s:Now tmp_data[0]=0x%02X,[1]=0x%02X,[2]=0x%02X,[3]=0x%02X\n", + * __func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + */ + } while ((tmp_data[1] != hb && tmp_data[0] != lb) && cnt++ < 100); + + if (cnt == 99) + return HX_RW_REG_FAIL; + + I("Now register 0x%08X : high byte=0x%02X,low byte=0x%02X\n", tmp_addr[3], tmp_data[1], tmp_data[0]); + return NO_ERR; +} + +static void himax_mcu_interface_on(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_data2[DATA_LEN_4]; + int cnt = 0; + + /* Read a dummy register to wake up I2C.*/ + if (himax_bus_read(pic_op->addr_ahb_rdata_byte_0[0], tmp_data, DATA_LEN_4, HIMAX_I2C_RETRY_TIMES) < 0) {/* to knock I2C*/ + E("%s: i2c access fail!\n", __func__); + return; + } + + do { + tmp_data[0] = pic_op->data_conti[0]; + + if (himax_bus_write(pic_op->addr_conti[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + tmp_data[0] = pic_op->data_incr4[0]; + + if (himax_bus_write(pic_op->addr_incr4[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + /*Check cmd*/ + himax_bus_read(pic_op->addr_conti[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES); + himax_bus_read(pic_op->addr_incr4[0], tmp_data2, 1, HIMAX_I2C_RETRY_TIMES); + + if (tmp_data[0] == pic_op->data_conti[0] && tmp_data2[0] == pic_op->data_incr4[0]) + break; + + usleep_range(1000, 1100); + } while (++cnt < 10); + + if (cnt > 0) + I("%s:Polling burst mode: %d times\n", __func__, cnt); +} + +static bool himax_mcu_wait_wip(int Timing) +{ + uint8_t tmp_data[DATA_LEN_4]; + int retry_cnt = 0; + + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_fmt, DATA_LEN_4, pflash_op->data_spi200_trans_fmt, 0); + tmp_data[0] = 0x01; + + do { + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, pflash_op->data_spi200_trans_ctrl_1, 0); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_1, 0); + tmp_data[0] = tmp_data[1] = tmp_data[2] = tmp_data[3] = 0xFF; + g_core_fp.fp_register_read(pflash_op->addr_spi200_data, 4, tmp_data, 0); + + if ((tmp_data[0] & 0x01) == 0x00) + return true; + + retry_cnt++; + + if (tmp_data[0] != 0x00 || tmp_data[1] != 0x00 || tmp_data[2] != 0x00 || tmp_data[3] != 0x00) + I("%s:Wait wip retry_cnt:%d, buffer[0]=%d, buffer[1]=%d, buffer[2]=%d, buffer[3]=%d\n", + __func__, retry_cnt, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + + if (retry_cnt > 100) { + E("%s: Wait wip error!\n", __func__); + return false; + } + + msleep(Timing); + } while ((tmp_data[0] & 0x01) == 0x01); + + return true; +} + +static void himax_mcu_sense_on(uint8_t FlashMode) +{ + uint8_t tmp_data[DATA_LEN_4]; + int retry = 0; + + I("Enter %s\n", __func__); + g_core_fp.fp_interface_on(); + g_core_fp.fp_register_write(pfw_op->addr_ctrl_fw_isr, + sizeof(pfw_op->data_clear), pfw_op->data_clear, 0); + /*msleep(20);*/ + usleep_range(10000, 11000); + if (!FlashMode) { +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + } else { + do { + g_core_fp.fp_register_write(pfw_op->addr_safe_mode_release_pw, + sizeof(pfw_op->data_safe_mode_release_pw_active), pfw_op->data_safe_mode_release_pw_active, 0); + + g_core_fp.fp_register_read(pfw_op->addr_flag_reset_event, DATA_LEN_4, tmp_data, 0); + I("%s:Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + } while ((tmp_data[1] != 0x01 || tmp_data[0] != 0x00) && retry++ < 5); + + if (retry >= 5) { + E("%s: Fail:\n", __func__); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + } else { + I("%s:OK and Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + /* reset code*/ + tmp_data[0] = 0x00; + + if (himax_bus_write(pic_op->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + if (himax_bus_write(pic_op->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + g_core_fp.fp_register_write(pfw_op->addr_safe_mode_release_pw, + sizeof(pfw_op->data_safe_mode_release_pw_reset), pfw_op->data_safe_mode_release_pw_reset, 0); + } + } +} + +static bool himax_mcu_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_data[DATA_LEN_4]; + + do { + tmp_data[0] = pic_op->data_i2c_psw_lb[0]; + + if (himax_bus_write(pic_op->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + tmp_data[0] = pic_op->data_i2c_psw_ub[0]; + + if (himax_bus_write(pic_op->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + g_core_fp.fp_register_read(pic_op->addr_cs_central_state, ADDR_LEN_4, tmp_data, 0); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + g_core_fp.fp_register_write(pic_op->addr_tcon_on_rst, DATA_LEN_4, pic_op->data_rst, 0); + usleep_range(1000, 1100); + tmp_data[3] = pic_op->data_rst[3]; + tmp_data[2] = pic_op->data_rst[2]; + tmp_data[1] = pic_op->data_rst[1]; + tmp_data[0] = pic_op->data_rst[0] | 0x01; + g_core_fp.fp_register_write(pic_op->addr_tcon_on_rst, DATA_LEN_4, tmp_data, 0); + + g_core_fp.fp_register_write(pic_op->addr_adc_on_rst, DATA_LEN_4, pic_op->data_rst, 0); + usleep_range(1000, 1100); + tmp_data[3] = pic_op->data_rst[3]; + tmp_data[2] = pic_op->data_rst[2]; + tmp_data[1] = pic_op->data_rst[1]; + tmp_data[0] = pic_op->data_rst[0] | 0x01; + g_core_fp.fp_register_write(pic_op->addr_adc_on_rst, DATA_LEN_4, tmp_data, 0); + goto TRUE_END; + } else { + /*msleep(10);*/ +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + } + } while (cnt++ < 15); + + return false; +TRUE_END: + return true; +} + +static void himax_mcu_init_psl(void) /*power saving level*/ +{ + g_core_fp.fp_register_write(pic_op->addr_psl, sizeof(pic_op->data_rst), pic_op->data_rst, 0); + I("%s: power saving level reset OK!\n", __func__); +} + +static void himax_mcu_resume_ic_action(void) +{ + /* Nothing to do */ +} + +static void himax_mcu_suspend_ic_action(void) +{ + /* Nothing to do */ +} + +static void himax_mcu_power_on_init(void) +{ + I("%s:\n", __func__); + g_core_fp.fp_touch_information(); + /*RawOut select initial*/ + g_core_fp.fp_register_write(pfw_op->addr_raw_out_sel, sizeof(pfw_op->data_clear), pfw_op->data_clear, 0); + /*DSRAM func initial*/ + g_core_fp.fp_assign_sorting_mode(pfw_op->data_clear); + g_core_fp.fp_sense_on(0x00); +} + +static bool himax_mcu_dd_clk_set(bool enable) +{ + uint8_t data[4] = {0}; + + data[0] = (enable)?1:0; + return (g_core_fp.fp_register_write(pic_op->adr_osc_en, sizeof(pic_op->adr_osc_en), data, 0) == NO_ERR); +} + +static void himax_mcu_dd_reg_en(bool enable) +{ + uint8_t data[4] = {0}; + + g_core_fp.fp_dd_reg_read(0xCB, 8, 1, data, 0); + + if (data[0] != 0x44) { /*need DD Touch PW*/ + data[0] = 0xA5; data[1] = 0x00; + data[2] = 0x00; data[3] = 0x00; + g_core_fp.fp_register_write(pic_op->adr_osc_pw, DATA_LEN_4, data, 0); + data[0] = 0x00; data[1] = 0x55; + data[2] = 0xAA; data[3] = 0x00; + g_core_fp.fp_dd_reg_write(0xEB, 0, 4, data, 0); + } + + data[0] = 0x00; data[1] = 0x83; + data[2] = 0x11; data[3] = 0x2A; + g_core_fp.fp_dd_reg_write(0xB9, 0, 4, data, 0); +} + +static bool himax_mcu_dd_reg_write(uint8_t addr, uint8_t pa_num, int len, uint8_t *data, uint8_t bank) +{ + /*Calculate total write length*/ + uint32_t data_len = (((len + pa_num - 1) / 4 - pa_num / 4) + 1) * 4; + uint8_t w_data[data_len]; + uint8_t tmp_addr[4] = {0}; + uint8_t tmp_data[4] = {0}; + bool chk_data[data_len]; + uint32_t chk_idx = 0; + int i = 0; + + memset(w_data, 0, data_len * sizeof(uint8_t)); + memset(chk_data, 0, data_len * sizeof(bool)); + + /*put input data*/ + chk_idx = pa_num % 4; + for (i = 0; i < len; i++) { + w_data[chk_idx] = data[i]; + chk_data[chk_idx++] = true; + } + + /*get original data*/ + chk_idx = (pa_num / 4) * 4; + for (i = 0; i < data_len; i++) { + if (!chk_data[i]) { + g_core_fp.fp_dd_reg_read(addr, (uint8_t)(chk_idx + i), 1, tmp_data, bank); + w_data[i] = tmp_data[0]; + chk_data[i] = true; + } + D("%s w_data[%d] = %2X\n", __func__, i, w_data[i]); + } + + tmp_addr[3] = 0x30; + tmp_addr[2] = addr >> 4; + tmp_addr[1] = (addr << 4) | (bank * 4); + tmp_addr[0] = chk_idx; + D("%s Addr = %02X%02X%02X%02X.\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + return (g_core_fp.fp_register_write(tmp_addr, data_len, w_data, 0) == NO_ERR); +} + +static bool himax_mcu_dd_reg_read(uint8_t addr, uint8_t pa_num, int len, uint8_t *data, uint8_t bank) +{ + uint8_t tmp_addr[4] = {0}; + uint8_t tmp_data[4] = {0}; + int i = 0; + + for (i = 0; i < len; i++) { + tmp_addr[3] = 0x30; + tmp_addr[2] = addr >> 4; + tmp_addr[1] = (addr << 4) | (bank * 4); + tmp_addr[0] = pa_num + i; + + if (g_core_fp.fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0)) + goto READ_FAIL; + data[i] = tmp_data[0]; + + D("%s Addr = %02X%02X%02X%02X.data = %2X\n", __func__, + tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0], data[i]); + } + return true; + +READ_FAIL: + E("%s Read DD reg Failed.\n", __func__); + return false; +} + +static bool himax_mcu_ic_id_read(void) +{ + int i = 0; + uint8_t data[4] = {0}; + + g_core_fp.fp_dd_clk_set(true); + g_core_fp.fp_dd_reg_en(true); + + for (i = 0; i < 13; i++) { + data[0] = 0x28 + i; + g_core_fp.fp_dd_reg_write(0xBB, 2, 1, data, 0); + data[0] = 0x80; + g_core_fp.fp_dd_reg_write(0xBB, 4, 1, data, 0); + data[0] = 0x00; + g_core_fp.fp_dd_reg_write(0xBB, 4, 1, data, 0); + g_core_fp.fp_dd_reg_read(0xBB, 5, 1, data, 0); + ic_data->vendor_ic_id[i] = data[0]; + I("ic_data->vendor_ic_id[%d] = %02X\n", i, ic_data->vendor_ic_id[i]); + } + + g_core_fp.fp_dd_clk_set(false); + + return true; +} + +/* IC side end*/ +#endif + +#ifdef CORE_FW +/* FW side start*/ +static void diag_mcu_parse_raw_data(struct himax_report_data *hx_touch_data, int mul_num, int self_num, uint8_t diag_cmd, int32_t *mutual_data, int32_t *self_data) +{ + int RawDataLen_word; + int index = 0; + int temp1, temp2, i; + + if (hx_touch_data->hx_rawdata_buf[0] == pfw_op->data_rawdata_ready_lb[0] + && hx_touch_data->hx_rawdata_buf[1] == pfw_op->data_rawdata_ready_hb[0] + && hx_touch_data->hx_rawdata_buf[2] > 0 + && hx_touch_data->hx_rawdata_buf[3] == diag_cmd) { + RawDataLen_word = hx_touch_data->rawdata_size / 2; + index = (hx_touch_data->hx_rawdata_buf[2] - 1) * RawDataLen_word; + + /* I("Header[%d]: %x, %x, %x, %x, mutual: %d, self: %d\n", index, buf[56], buf[57], buf[58], buf[59], mul_num, self_num); + * I("RawDataLen=%d , RawDataLen_word=%d , hx_touch_info_size=%d\n", RawDataLen, RawDataLen_word, hx_touch_info_size); + */ + for (i = 0; i < RawDataLen_word; i++) { + temp1 = index + i; + + if (temp1 < mul_num) { /*mutual*/ + mutual_data[index + i] = ((int8_t)hx_touch_data->hx_rawdata_buf[i * 2 + 4 + 1]) * 256 + hx_touch_data->hx_rawdata_buf[i * 2 + 4]; + } else { /*self*/ + temp1 = i + index; + temp2 = self_num + mul_num; + + if (temp1 >= temp2) + break; + + self_data[i + index - mul_num] = (((int8_t)hx_touch_data->hx_rawdata_buf[i * 2 + 4 + 1]) << 8) + + hx_touch_data->hx_rawdata_buf[i * 2 + 4]; + } + } + } +} + +static void himax_mcu_system_reset(void) +{ +#ifdef HX_SHARP_ENABLE_PON + g_core_fp.fp_register_write(pfw_op->addr_system_reset, sizeof(pfw_op->data_system_reset), pfw_op->data_system_reset, 0); +#else + uint8_t tmp_data[DATA_LEN_4]; + int retry = 0; + + g_core_fp.fp_interface_on(); + g_core_fp.fp_register_write(pfw_op->addr_ctrl_fw_isr, + sizeof(pfw_op->data_clear), pfw_op->data_clear, 0); + do { + /* reset code*/ + /* =========================================== + * I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + * =========================================== + */ + tmp_data[0] = pic_op->data_i2c_psw_lb[0]; + + if (himax_bus_write(pic_op->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + /* =========================================== + * I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + * =========================================== + */ + tmp_data[0] = pic_op->data_i2c_psw_ub[0]; + + if (himax_bus_write(pic_op->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + /* =========================================== + * I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x00 + * =========================================== + */ + tmp_data[0] = 0x00; + + if (himax_bus_write(pic_op->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + usleep_range(10000, 11000); + + g_core_fp.fp_register_read(pfw_op->addr_flag_reset_event, DATA_LEN_4, tmp_data, 0); + I("%s:Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + } while ((tmp_data[1] != 0x02 || tmp_data[0] != 0x00) && retry++ < 5); +#endif +} + +static int himax_mcu_Calculate_CRC_with_AP(unsigned char *FW_content, int CRC_from_FW, int len) +{ + int i, j, length = 0; + int fw_data; + int fw_data_2; + int CRC = 0xFFFFFFFF; + int PolyNomial = 0x82F63B78; + + length = len / 4; + + for (i = 0; i < length; i++) { + fw_data = FW_content[i * 4]; + + for (j = 1; j < 4; j++) { + fw_data_2 = FW_content[i * 4 + j]; + fw_data += (fw_data_2) << (8 * j); + } + CRC = fw_data ^ CRC; + for (j = 0; j < 32; j++) { + if ((CRC % 2) != 0) + CRC = ((CRC >> 1) & 0x7FFFFFFF) ^ PolyNomial; + else + CRC = (((CRC >> 1) & 0x7FFFFFFF)/*& 0x7FFFFFFF*/); + } + } + + return CRC; +} + +static uint32_t himax_mcu_check_CRC(uint8_t *start_addr, int reload_length) +{ + uint32_t result = 0; + uint8_t tmp_data[DATA_LEN_4]; + int cnt = 0, ret = 0; + int length = reload_length / DATA_LEN_4; + + ret = g_core_fp.fp_register_write(pfw_op->addr_reload_addr_from, DATA_LEN_4, start_addr, 0); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + + tmp_data[3] = 0x00; tmp_data[2] = 0x99; tmp_data[1] = (length >> 8); tmp_data[0] = length; + ret = g_core_fp.fp_register_write(pfw_op->addr_reload_addr_cmd_beat, DATA_LEN_4, tmp_data, 0); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + cnt = 0; + + do { + ret = g_core_fp.fp_register_read(pfw_op->addr_reload_status, DATA_LEN_4, tmp_data, 0); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + + if ((tmp_data[0] & 0x01) != 0x01) { + ret = g_core_fp.fp_register_read(pfw_op->addr_reload_crc32_result, DATA_LEN_4, tmp_data, 0); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + I("%s: tmp_data[3]=%X, tmp_data[2]=%X, tmp_data[1]=%X, tmp_data[0]=%X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1], tmp_data[0]); + result = ((tmp_data[3] << 24) + (tmp_data[2] << 16) + (tmp_data[1] << 8) + tmp_data[0]); + goto END; + } else { + I("Waiting for HW ready!\n"); + usleep_range(1000, 1100); + } + + } while (cnt++ < 100); +END: + return result; +} + +static void himax_mcu_set_reload_cmd(uint8_t *write_data, int idx, uint32_t cmd_from, uint32_t cmd_to, uint32_t cmd_beat) +{ + int index = idx * 12; + int i; + + for (i = 3; i >= 0; i--) { + write_data[index + i] = (cmd_from >> (8 * i)); + write_data[index + 4 + i] = (cmd_to >> (8 * i)); + write_data[index + 8 + i] = (cmd_beat >> (8 * i)); + } +} + +static bool himax_mcu_program_reload(void) +{ + return true; +} + +#ifdef HX_P_SENSOR +static int himax_mcu_ulpm_in(void) +{ + uint8_t tmp_data[4]; + int rtimes = 0; + + I("%s:entering\n", __func__); + + /* 34 -> 11 */ + do { + if (rtimes > 10) { + I("%s:1/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_34[0], pfw_op->data_ulpm_11, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_34[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x34, correct 0x11 = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_11[0]); + + rtimes = 0; + /* 34 -> 11 */ + do { + if (rtimes > 10) { + I("%s:2/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_34[0], pfw_op->data_ulpm_11, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_34[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x34, correct 0x11 = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_11[0]); + + /* 33 -> 33 */ + rtimes = 0; + do { + if (rtimes > 10) { + I("%s:3/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_33[0], pfw_op->data_ulpm_33, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_33[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x33, correct 0x33 = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_33[0]); + + /* 34 -> 22 */ + rtimes = 0; + do { + if (rtimes > 10) { + I("%s:4/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_34[0], pfw_op->data_ulpm_22, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_34[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x34, correct 0x22 = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_22[0]); + + /* 33 -> AA */ + rtimes = 0; + do { + if (rtimes > 10) { + I("%s:5/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_33[0], pfw_op->data_ulpm_aa, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_33[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x33, correct 0xAA = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_aa[0]); + + /* 33 -> 33 */ + rtimes = 0; + do { + if (rtimes > 10) { + I("%s:6/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_33[0], pfw_op->data_ulpm_33, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_33[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x33, correct 0x33 = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_33[0]); + + /* 33 -> AA */ + rtimes = 0; + do { + if (rtimes > 10) { + I("%s:7/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_33[0], pfw_op->data_ulpm_aa, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_33[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x33, correct 0xAA = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_aa[0]); + + I("%s:END\n", __func__); + return true; +} + +static int himax_mcu_black_gest_ctrl(bool enable) +{ + int ret = 0; + + I("%s:enable=%d, ts->is_suspended=%d\n", __func__, enable, private_ts->suspended); + + if (private_ts->suspended) { + if (enable) { +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + I("%s: Please enable TP reset define\n", __func__); +#endif + } else { + g_core_fp.fp_ulpm_in(); + } + } else { + g_core_fp.fp_sense_on(0); + } + return ret; +} +#endif + +static void himax_mcu_set_SMWP_enable(uint8_t SMWP_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t retry_cnt = 0; + + do { + if (SMWP_enable) { + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, back_data, 4); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + } + + g_core_fp.fp_register_read(pfw_op->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + /*I("%s: tmp_data[0]=%d, SMWP_enable=%d, retry_cnt=%d\n", __func__, tmp_data[0],SMWP_enable,retry_cnt);*/ + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} + +#ifdef HX_EDGE_LIMIT +static void himax_mcu_set_edge_limit_enable(uint8_t edge_limit_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t retry_cnt = 0; + + do { + if (edge_limit_enable == 0x01) {/*Portrait*/ + himax_in_parse_assign_cmd(fw_func_edge_portrait_pwd, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_edge_limit_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_func_edge_portrait_pwd, back_data, 4); + } else if (edge_limit_enable == 0x02) {/*Landscape-Right*/ + himax_in_parse_assign_cmd(fw_func_edge_lsp_right_pwd, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_edge_limit_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_func_edge_lsp_right_pwd, back_data, 4); + } else if (edge_limit_enable == 0x03) {/*Landscape-Left*/ + himax_in_parse_assign_cmd(fw_func_edge_lsp_left_pwd, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_edge_limit_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_func_edge_lsp_left_pwd, back_data, 4); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_edge_limit_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + } + + g_core_fp.fp_register_read(pfw_op->addr_edge_limit_enable, DATA_LEN_4, tmp_data, 0); + I("%s: tmp_data[0]=%d, SMWP_enable=%d, retry_cnt=%d \n", __func__, tmp_data[0], edge_limit_enable, retry_cnt); + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} +#endif + +static void himax_mcu_set_HSEN_enable(uint8_t HSEN_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t retry_cnt = 0; + + do { + if (HSEN_enable) { + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, back_data, 4); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + } + + g_core_fp.fp_register_read(pfw_op->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + /*I("%s: tmp_data[0]=%d, HSEN_enable=%d, retry_cnt=%d\n", __func__, tmp_data[0],HSEN_enable,retry_cnt);*/ + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} + +static void himax_mcu_usb_detect_set(uint8_t *cable_config) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t retry_cnt = 0; + + do { + if (cable_config[1] == 0x01) { + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, back_data, 4); + I("%s: USB detect status IN!\n", __func__); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + I("%s: USB detect status OUT!\n", __func__); + } + + g_core_fp.fp_register_read(pfw_op->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + /*I("%s: tmp_data[0]=%d, USB detect=%d, retry_cnt=%d\n", __func__, tmp_data[0],cable_config[1] ,retry_cnt);*/ + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} + +static void himax_mcu_diag_register_set(uint8_t diag_command, uint8_t storage_type) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t cnt = 50; + + if (diag_command > 0 && storage_type % 8 > 0) + tmp_data[0] = diag_command + 0x08; + else + tmp_data[0] = diag_command; + I("diag_command = %d, tmp_data[0] = %X\n", diag_command, tmp_data[0]); + g_core_fp.fp_interface_on(); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; + do { + g_core_fp.fp_register_write(pfw_op->addr_raw_out_sel, DATA_LEN_4, tmp_data, 0); + g_core_fp.fp_register_read(pfw_op->addr_raw_out_sel, DATA_LEN_4, back_data, 0); + I("%s: back_data[3]=0x%02X,back_data[2]=0x%02X,back_data[1]=0x%02X,back_data[0]=0x%02X!\n", + __func__, back_data[3], back_data[2], back_data[1], back_data[0]); + cnt--; + } while (tmp_data[0] != back_data[0] && cnt > 0); +} + +static int himax_mcu_chip_self_test(void) +{ + uint8_t tmp_data[FLASH_WRITE_BURST_SZ]; + uint8_t self_test_info[20]; + int pf_value = 0x00; + uint8_t test_result_id = 0; + int i; + + memset(tmp_data, 0x00, sizeof(tmp_data)); + g_core_fp.fp_interface_on(); + g_core_fp.fp_sense_off(true); + g_core_fp.fp_burst_enable(1); + g_core_fp.fp_register_write(pfw_op->addr_selftest_addr_en, DATA_LEN_4, pfw_op->data_selftest_request, 0); + /*Set criteria 0x10007F1C [0,1]=aa/up,down=, [2-3]=key/up,down, [4-5]=avg/up,down*/ + tmp_data[0] = pfw_op->data_criteria_aa_top[0]; + tmp_data[1] = pfw_op->data_criteria_aa_bot[0]; + tmp_data[2] = pfw_op->data_criteria_key_top[0]; + tmp_data[3] = pfw_op->data_criteria_key_bot[0]; + tmp_data[4] = pfw_op->data_criteria_avg_top[0]; + tmp_data[5] = pfw_op->data_criteria_avg_bot[0]; + tmp_data[6] = 0x00; + tmp_data[7] = 0x00; + g_core_fp.fp_register_write(pfw_op->addr_criteria_addr, FLASH_WRITE_BURST_SZ, tmp_data, 0); + g_core_fp.fp_register_write(pfw_op->addr_set_frame_addr, DATA_LEN_4, pfw_op->data_set_frame, 0); + /*Disable IDLE Mode*/ + g_core_fp.fp_idle_mode(1); + /*Diable Flash Reload*/ + g_core_fp.fp_reload_disable(1); + /*start selftest // leave safe mode*/ + g_core_fp.fp_sense_on(0x01); + + /*Hand shaking*/ + for (i = 0; i < 1000; i++) { + g_core_fp.fp_register_read(pfw_op->addr_selftest_addr_en, 4, tmp_data, 0); + I("%s: tmp_data[0] = 0x%02X,tmp_data[1] = 0x%02X,tmp_data[2] = 0x%02X,tmp_data[3] = 0x%02X, cnt=%d\n", + __func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3], i); + usleep_range(10000, 11000); + + if (tmp_data[1] == pfw_op->data_selftest_ack_hb[0] && tmp_data[0] == pfw_op->data_selftest_ack_lb[0]) { + I("%s Data ready goto moving data\n", __func__); + break; + } + } + + g_core_fp.fp_sense_off(true); + msleep(20); + /* ===================================== + * Read test result ==> bit[2][1][0] = [key][AA][avg] => 0xF = PASS + * ===================================== + */ + g_core_fp.fp_register_read(pfw_op->addr_selftest_result_addr, 20, self_test_info, 0); + test_result_id = self_test_info[0]; + I("%s: check test result, test_result_id=%x, test_result=%x\n", __func__ + , test_result_id, self_test_info[0]); + I("raw top 1 = %d\n", self_test_info[3] * 256 + self_test_info[2]); + I("raw top 2 = %d\n", self_test_info[5] * 256 + self_test_info[4]); + I("raw top 3 = %d\n", self_test_info[7] * 256 + self_test_info[6]); + I("raw last 1 = %d\n", self_test_info[9] * 256 + self_test_info[8]); + I("raw last 2 = %d\n", self_test_info[11] * 256 + self_test_info[10]); + I("raw last 3 = %d\n", self_test_info[13] * 256 + self_test_info[12]); + I("raw key 1 = %d\n", self_test_info[15] * 256 + self_test_info[14]); + I("raw key 2 = %d\n", self_test_info[17] * 256 + self_test_info[16]); + I("raw key 3 = %d\n", self_test_info[19] * 256 + self_test_info[18]); + + if (test_result_id == pfw_op->data_selftest_pass[0]) { + I("[Himax]: self-test pass\n"); + pf_value = 0x0; + } else { + E("[Himax]: self-test fail\n"); + /* E("[Himax]: bank_avg = %d, bank_max = %d,%d,%d, bank_min = %d,%d,%d, key = %d,%d,%d\n", + * tmp_data[1],tmp_data[2],tmp_data[3],tmp_data[4],tmp_data[5],tmp_data[6],tmp_data[7], + * tmp_data[8],tmp_data[9],tmp_data[10]); + */ + pf_value = 0x1; + } + + /*Enable IDLE Mode*/ + g_core_fp.fp_idle_mode(0); +#ifndef HX_ZERO_FLASH + /* Enable Flash Reload //recovery*/ + g_core_fp.fp_reload_disable(0); +#endif + g_core_fp.fp_sense_on(0x00); + msleep(120); + return pf_value; +} + +static void himax_mcu_idle_mode(int disable) +{ + int retry = 20; + uint8_t tmp_data[DATA_LEN_4]; + uint8_t switch_cmd = 0x00; + + I("%s:entering\n", __func__); + + do { + I("%s,now %d times!\n", __func__, retry); + g_core_fp.fp_register_read(pfw_op->addr_fw_mode_status, DATA_LEN_4, tmp_data, 0); + + if (disable) + switch_cmd = pfw_op->data_idle_dis_pwd[0]; + else + switch_cmd = pfw_op->data_idle_en_pwd[0]; + + tmp_data[0] = switch_cmd; + g_core_fp.fp_register_write(pfw_op->addr_fw_mode_status, DATA_LEN_4, tmp_data, 0); + g_core_fp.fp_register_read(pfw_op->addr_fw_mode_status, DATA_LEN_4, tmp_data, 0); + I("%s:After turn ON/OFF IDLE Mode [0] = 0x%02X,[1] = 0x%02X,[2] = 0x%02X,[3] = 0x%02X\n", + __func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + retry--; + usleep_range(10000, 11000); + } while ((tmp_data[0] != switch_cmd) && retry > 0); + + I("%s: setting OK!\n", __func__); +} + +static void himax_mcu_reload_disable(int disable) +{ + int ret = 0; + I("%s:entering\n", __func__); + + if (disable) { /*reload disable*/ + ret= g_core_fp.fp_register_write(pdriver_op->addr_fw_define_flash_reload, DATA_LEN_4, pdriver_op->data_fw_define_flash_reload_dis, 0); + } else { /*reload enable*/ + ret= g_core_fp.fp_register_write(pdriver_op->addr_fw_define_flash_reload, DATA_LEN_4, pdriver_op->data_fw_define_flash_reload_en, 0); + } + if (ret != 0) + atomic_set(&private_ts->resume_update_fail, 1); + + I("%s: setting OK!\n", __func__); +} + +static bool himax_mcu_check_chip_version(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t ret_data = false; + int i = 0; + + for (i = 0; i < 5; i++) { + g_core_fp.fp_register_read(pfw_op->addr_icid_addr, DATA_LEN_4, tmp_data, 0); + I("%s:Read driver IC ID = %X,%X,%X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1]); + + if ((tmp_data[3] == 0x83) && (tmp_data[2] == 0x10) && (tmp_data[1] == 0x2a)) { + strlcpy(private_ts->chip_name, HX_83102A_SERIES_PWON, 30); + ret_data = true; + goto END; + } else { + ret_data = false; + E("%s:Read driver ID register Fail:\n", __func__); + } + } +END: + return ret_data; +} + +static int himax_mcu_read_ic_trigger_type(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + int trigger_type = false; + + g_core_fp.fp_register_read(pfw_op->addr_trigger_addr, DATA_LEN_4, tmp_data, 0); + + if ((tmp_data[1] & 0x01) == 1) + trigger_type = true; + + return trigger_type; +} + +static int himax_mcu_read_i2c_status(void) +{ + return i2c_error_count; +} + +static void himax_mcu_read_FW_ver(void) +{ + uint8_t data[12]; + uint8_t data_2[DATA_LEN_4]; + int retry = 200; + int reload_status = 0; + + g_core_fp.fp_sense_on(0x00); + + while (reload_status == 0) { + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_flash_reload, DATA_LEN_4, data, 0); + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_2nd_flash_reload, DATA_LEN_4, data_2, 0); + + if ((data[1] == 0x3A && data[0] == 0xA3) + || (data_2[1] == 0x72 && data_2[0] == 0xC0)) { + I("reload OK!\n"); + reload_status = 1; + break; + } else if (retry == 0) { + E("reload 20 times! fail\n"); + E("Maybe NOT have FW in chipset\n"); + E("Maybe Wrong FW in chipset\n"); + ic_data->vendor_panel_ver = 0; + ic_data->vendor_fw_ver = 0; + ic_data->vendor_config_ver = 0; + ic_data->vendor_touch_cfg_ver = 0; + ic_data->vendor_display_cfg_ver = 0; + ic_data->vendor_cid_maj_ver = 0; + ic_data->vendor_cid_min_ver = 0; + goto END; + } else { + retry--; + usleep_range(10000, 11000); + if (retry % 10 == 0) + I("reload fail ,delay 10ms retry=%d\n", retry); + } + } + + I("%s : data[0]=0x%2.2X,data[1]=0x%2.2X,data_2[0]=0x%2.2X,data_2[1]=0x%2.2X\n", __func__, data[0], data[1], data_2[0], data_2[1]); + I("reload_status=%d\n", reload_status); + /* ===================================== + * Read FW version + * ===================================== + */ + g_core_fp.fp_sense_off(true); + g_core_fp.fp_register_read(pfw_op->addr_fw_ver_addr, DATA_LEN_4, data, 0); + ic_data->vendor_panel_ver = data[0]; + ic_data->vendor_fw_ver = data[1] << 8 | data[2]; + I("PANEL_VER : %X\n", ic_data->vendor_panel_ver); + I("FW_VER : %X\n", ic_data->vendor_fw_ver); + g_core_fp.fp_register_read(pfw_op->addr_fw_cfg_addr, DATA_LEN_4, data, 0); + ic_data->vendor_config_ver = data[2] << 8 | data[3]; + /*I("CFG_VER : %X\n",ic_data->vendor_config_ver);*/ + ic_data->vendor_touch_cfg_ver = data[2]; + I("TOUCH_VER : %X\n", ic_data->vendor_touch_cfg_ver); + ic_data->vendor_display_cfg_ver = data[3]; + I("DISPLAY_VER : %X\n", ic_data->vendor_display_cfg_ver); + g_core_fp.fp_register_read(pfw_op->addr_fw_vendor_addr, DATA_LEN_4, data, 0); + ic_data->vendor_cid_maj_ver = data[2]; + ic_data->vendor_cid_min_ver = data[3]; + I("CID_VER : %X\n", (ic_data->vendor_cid_maj_ver << 8 | ic_data->vendor_cid_min_ver)); + g_core_fp.fp_register_read(pfw_op->addr_cus_info, 12, data, 0); + memcpy(ic_data->vendor_cus_info, data, 12); + I("Cusomer ID = %s\n", ic_data->vendor_cus_info); + g_core_fp.fp_register_read(pfw_op->addr_proj_info, 12, data, 0); + memcpy(ic_data->vendor_proj_info, data, 12); + I("Project ID = %s\n", ic_data->vendor_proj_info); + +END: + return; +} + +static bool himax_mcu_read_event_stack(uint8_t *buf, uint8_t length) +{ + uint8_t cmd[DATA_LEN_4]; + struct timespec t_start, t_end, t_delta; + int len = length; + int i2c_speed = 0; + + /* AHB_I2C Burst Read Off */ + cmd[0] = pfw_op->data_ahb_dis[0]; + + if (himax_bus_write(pfw_op->addr_ahb_addr[0], cmd, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return 0; + } + if (private_ts->debug_log_level & BIT(2)) + getnstimeofday(&t_start); + + himax_bus_read(pfw_op->addr_event_addr[0], buf, length, HIMAX_I2C_RETRY_TIMES); + + if (private_ts->debug_log_level & BIT(2)) { + getnstimeofday(&t_end); + t_delta.tv_nsec = (t_end.tv_sec * 1000000000 + t_end.tv_nsec) - (t_start.tv_sec * 1000000000 + t_start.tv_nsec); /*ns*/ + i2c_speed = (len * 9 * 1000000 / (int)t_delta.tv_nsec) * 13 / 10; + private_ts->bus_speed = (int)i2c_speed; + } + + /* AHB_I2C Burst Read On */ + cmd[0] = pfw_op->data_ahb_en[0]; + + if (himax_bus_write(pfw_op->addr_ahb_addr[0], cmd, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return 0; + } + + return 1; +} + +static void himax_mcu_return_event_stack(void) +{ + int retry = 20, i; + uint8_t tmp_data[DATA_LEN_4]; + + I("%s:entering\n", __func__); + + do { + I("now %d times!\n", retry); + + for (i = 0; i < DATA_LEN_4; i++) + tmp_data[i] = psram_op->addr_rawdata_end[i]; + + g_core_fp.fp_register_write(psram_op->addr_rawdata_addr, DATA_LEN_4, tmp_data, 0); + g_core_fp.fp_register_read(psram_op->addr_rawdata_addr, DATA_LEN_4, tmp_data, 0); + retry--; + usleep_range(10000, 11000); + } while ((tmp_data[1] != psram_op->addr_rawdata_end[1] && tmp_data[0] != psram_op->addr_rawdata_end[0]) && retry > 0); + + I("%s: End of setting!\n", __func__); +} + +static bool himax_mcu_calculateChecksum(bool change_iref, uint32_t size) +{ + uint8_t CRC_result = 0, i; + uint8_t tmp_data[DATA_LEN_4]; + + I("%s:Now size=%d\n", __func__, size); + for (i = 0; i < DATA_LEN_4; i++) + tmp_data[i] = psram_op->addr_rawdata_end[i]; + + CRC_result = g_core_fp.fp_check_CRC(tmp_data, size); + msleep(50); + + if (CRC_result != 0) + I("%s: CRC Fail=%d\n", __func__, CRC_result); + + return (CRC_result == 0) ? true : false; +} + +static int himax_mcu_read_FW_status(uint8_t *state_addr, uint8_t *tmp_addr) +{ + uint8_t i; + uint8_t req_size = 0; + uint8_t status_addr[DATA_LEN_4]; + uint8_t cmd_addr[DATA_LEN_4]; + + if (state_addr[0] == 0x01) { + state_addr[1] = 0x04; + + for (i = 0; i < DATA_LEN_4; i++) { + state_addr[i + 2] = pfw_op->addr_fw_dbg_msg_addr[i]; + status_addr[i] = pfw_op->addr_fw_dbg_msg_addr[i]; + } + + req_size = 0x04; + g_core_fp.fp_register_read(status_addr, req_size, tmp_addr, 0); + } else if (state_addr[0] == 0x02) { + state_addr[1] = 0x30; + + for (i = 0; i < DATA_LEN_4; i++) { + state_addr[i + 2] = pfw_op->addr_fw_dbg_msg_addr[i]; + cmd_addr[i] = pfw_op->addr_fw_dbg_msg_addr[i]; + } + + req_size = 0x30; + g_core_fp.fp_register_read(cmd_addr, req_size, tmp_addr, 0); + } + + return NO_ERR; +} + +static void himax_mcu_irq_switch(int switch_on) +{ + if (switch_on) { + if (private_ts->use_irq) + himax_int_enable(switch_on); + else + hrtimer_start(&private_ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + + } else { + if (private_ts->use_irq) + himax_int_enable(switch_on); + else { + hrtimer_cancel(&private_ts->timer); + cancel_work_sync(&private_ts->work); + } + } +} + +static int himax_mcu_assign_sorting_mode(uint8_t *tmp_data) +{ + + I("%s:Now tmp_data[3]=0x%02X,tmp_data[2]=0x%02X,tmp_data[1]=0x%02X,tmp_data[0]=0x%02X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1], tmp_data[0]); + g_core_fp.fp_register_write(pfw_op->addr_sorting_mode_en, DATA_LEN_4, tmp_data, 0); + + return NO_ERR; +} + +static int himax_mcu_check_sorting_mode(uint8_t *tmp_data) +{ + + g_core_fp.fp_register_read(pfw_op->addr_sorting_mode_en, DATA_LEN_4, tmp_data, 0); + I("%s: tmp_data[0]=%x,tmp_data[1]=%x\n", __func__, tmp_data[0], tmp_data[1]); + + return NO_ERR; +} + +static int himax_mcu_switch_mode(int mode) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t mode_wirte_cmd; + uint8_t mode_read_cmd; + int result = -1; + int retry = 200; + + I("%s: Entering\n", __func__); + + if (mode == 0) { /* normal mode */ + mode_wirte_cmd = pfw_op->data_normal_cmd[0]; + mode_read_cmd = pfw_op->data_normal_status[0]; + } else { /* sorting mode */ + mode_wirte_cmd = pfw_op->data_sorting_cmd[0]; + mode_read_cmd = pfw_op->data_sorting_status[0]; + } + + g_core_fp.fp_sense_off(true); + /*g_core_fp.fp_interface_on();*/ + /* clean up FW status */ + g_core_fp.fp_register_write(psram_op->addr_rawdata_addr, DATA_LEN_4, psram_op->addr_rawdata_end, 0); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = mode_wirte_cmd; + tmp_data[0] = mode_wirte_cmd; + g_core_fp.fp_assign_sorting_mode(tmp_data); + g_core_fp.fp_idle_mode(1); + g_core_fp.fp_reload_disable(1); + + /* To stable the sorting*/ + if (mode) { + g_core_fp.fp_register_write(pdriver_op->addr_fw_define_rxnum_txnum_maxpt, DATA_LEN_4, pdriver_op->data_fw_define_rxnum_txnum_maxpt_sorting, 0); + } else { + g_core_fp.fp_register_write(pfw_op->addr_set_frame_addr, DATA_LEN_4, pfw_op->data_set_frame, 0); + g_core_fp.fp_register_write(pdriver_op->addr_fw_define_rxnum_txnum_maxpt, DATA_LEN_4, pdriver_op->data_fw_define_rxnum_txnum_maxpt_normal, 0); + } + + g_core_fp.fp_sense_on(0x01); + + while (retry != 0) { + I("[%d] %s Read\n", retry, __func__); + g_core_fp.fp_check_sorting_mode(tmp_data); + msleep(100); + I("mode_read_cmd(0)=0x%2.2X,mode_read_cmd(1)=0x%2.2X\n", tmp_data[0], tmp_data[1]); + + if (tmp_data[0] == mode_read_cmd && tmp_data[1] == mode_read_cmd) { + I("Read OK!\n"); + result = 0; + break; + } + + g_core_fp.fp_register_read(pfw_op->addr_chk_fw_status, DATA_LEN_4, tmp_data, 0); + + if (tmp_data[0] == 0x00 && tmp_data[1] == 0x00 && tmp_data[2] == 0x00 && tmp_data[3] == 0x00) { + E("%s,: FW Stop!\n", __func__); + break; + } + + retry--; + } + + if (result == 0) { + if (mode == 0) { /*normal mode*/ + return HX_NORMAL_MODE; + } else { /*sorting mode*/ + return HX_SORTING_MODE; + } + } else { /*change mode fail*/ + return HX_CHANGE_MODE_FAIL; + } +} + +static int himax_mcu_get_max_dc(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + int dc_max = 0; + + g_core_fp.fp_register_read(pfw_op->addr_max_dc, DATA_LEN_4, tmp_data, 0); + I("%s: tmp_data[0]=%x,tmp_data[1]=%x\n", __func__, tmp_data[0], tmp_data[1]); + + dc_max = tmp_data[1] << 8 | tmp_data[0]; + I("%s: dc max = %d\n", __func__, dc_max); + return dc_max; +} + +static uint8_t himax_mcu_read_DD_status(uint8_t *cmd_set, uint8_t *tmp_data) +{ + int cnt = 0; + uint8_t req_size = cmd_set[0]; + + cmd_set[3] = pfw_op->data_dd_request[0]; + g_core_fp.fp_register_write(pfw_op->addr_dd_handshak_addr, DATA_LEN_4, cmd_set, 0); + I("%s: cmd_set[0] = 0x%02X,cmd_set[1] = 0x%02X,cmd_set[2] = 0x%02X,cmd_set[3] = 0x%02X\n", + __func__, cmd_set[0], cmd_set[1], cmd_set[2], cmd_set[3]); + + /* Doing hand shaking 0xAA -> 0xBB */ + for (cnt = 0; cnt < 100; cnt++) { + g_core_fp.fp_register_read(pfw_op->addr_dd_handshak_addr, DATA_LEN_4, tmp_data, 0); + usleep_range(10000, 11000); + + if (tmp_data[3] == pfw_op->data_dd_ack[0]) { + I("%s Data ready goto moving data\n", __func__); + goto FINALIZE; + } else { + if (cnt >= 99) { + I("%s Data not ready in FW\n", __func__); + return FW_NOT_READY; + } + } + } +FINALIZE: + g_core_fp.fp_register_read(pfw_op->addr_dd_data_addr, req_size, tmp_data, 0); + return NO_ERR; +} +#ifdef HX_AUTO_UPDATE_FW +static int hx_mcu_diff_overlay_bin(void) +{ + int rslt = 0; + int diff_val = 0; + + diff_val = g_i_FW_VER; + I("%s:Now fw ID is 0x%04X\n", __func__, diff_val); + diff_val = (diff_val >> 12); + I("%s:Now diff value=0x%04X\n", __func__, diff_val); + + if (diff_val == 1) + I("%s:Now size should be 128K!\n", __func__); + else + I("%s:Now size should be 64K!\n", __func__); + rslt = diff_val; + return rslt; +} +#endif + +/* FW side end*/ +#endif + +#ifdef CORE_FLASH +/* FLASH side start*/ +static void himax_mcu_chip_erase(void) +{ + g_core_fp.fp_interface_on(); + + /* Reset power saving level */ + if (g_core_fp.fp_init_psl != NULL) + g_core_fp.fp_init_psl(); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_fmt, DATA_LEN_4, pflash_op->data_spi200_trans_fmt, 0); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, pflash_op->data_spi200_trans_ctrl_2, 0); + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_2, 0); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_3, 0); + msleep(2000); + + if (!g_core_fp.fp_wait_wip(100)) + E("%s: Chip_Erase Fail\n", __func__); + +} + +static bool himax_mcu_block_erase(int start_addr, int length) /*complete not yet*/ +{ + uint32_t page_prog_start = 0; + uint32_t block_size = 0x10000; + + uint8_t tmp_data[4] = {0}; + + g_core_fp.fp_interface_on(); + + g_core_fp.fp_init_psl(); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_fmt, DATA_LEN_4, pflash_op->data_spi200_trans_fmt, 0); + + for (page_prog_start = start_addr; page_prog_start < start_addr + length; page_prog_start = page_prog_start + block_size) { + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, pflash_op->data_spi200_trans_ctrl_2, 0); + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_2, 0); + + tmp_data[3] = (page_prog_start >> 24)&0xFF; + tmp_data[2] = (page_prog_start >> 16)&0xFF; + tmp_data[1] = (page_prog_start >> 8)&0xFF; + tmp_data[0] = page_prog_start&0xFF; + g_core_fp.fp_register_write(pflash_op->addr_spi200_addr, DATA_LEN_4, tmp_data, 0); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, pflash_op->data_spi200_trans_ctrl_3, 0); + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_4, 0); + msleep(1000); + + if (!g_core_fp.fp_wait_wip(100)) { + E("%s:Erase Fail\n", __func__); + return false; + } + } + + I("%s:END\n", __func__); + return true; +} + +static bool himax_mcu_sector_erase(int start_addr) +{ + return true; +} + +static void himax_mcu_flash_programming(uint8_t *FW_content, int FW_Size) +{ + int page_prog_start = 0, i = 0, j = 0, k = 0; + int program_length = PROGRAM_SZ; + uint8_t tmp_data[DATA_LEN_4]; + uint8_t buring_data[FLASH_RW_MAX_LEN]; /* Read for flash data, 128K*/ + /* 4 bytes for padding*/ + g_core_fp.fp_interface_on(); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_fmt, DATA_LEN_4, pflash_op->data_spi200_trans_fmt, 0); + + for (page_prog_start = 0; page_prog_start < FW_Size; page_prog_start += FLASH_RW_MAX_LEN) { + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, pflash_op->data_spi200_trans_ctrl_2, 0); + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_2, 0); + + /*Programmable size = 1 page = 256 bytes, word_number = 256 byte / 4 = 64*/ + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, pflash_op->data_spi200_trans_ctrl_4, 0); + + /* Flash start address 1st : 0x0000_0000*/ + if (page_prog_start < 0x100) { + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = (uint8_t)page_prog_start; + } else if (page_prog_start >= 0x100 && page_prog_start < 0x10000) { + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = (uint8_t)(page_prog_start >> 8); + tmp_data[0] = (uint8_t)page_prog_start; + } else if (page_prog_start >= 0x10000 && page_prog_start < 0x1000000) { + tmp_data[3] = 0x00; + tmp_data[2] = (uint8_t)(page_prog_start >> 16); + tmp_data[1] = (uint8_t)(page_prog_start >> 8); + tmp_data[0] = (uint8_t)page_prog_start; + } + g_core_fp.fp_register_write(pflash_op->addr_spi200_addr, DATA_LEN_4, tmp_data, 0); + + for (i = 0; i < ADDR_LEN_4; i++) + buring_data[i] = pflash_op->addr_spi200_data[i]; + + + for (i = page_prog_start, j = 0; i < 16 + page_prog_start; i++, j++) + buring_data[j + ADDR_LEN_4] = FW_content[i]; + + + if (himax_bus_write(pic_op->addr_ahb_addr_byte_0[0], buring_data, ADDR_LEN_4 + 16, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_6, 0); + + for (j = 0; j < 5; j++) { + for (i = (page_prog_start + 16 + (j * 48)), k = 0; i < (page_prog_start + 16 + (j * 48)) + program_length; i++, k++) + buring_data[k + ADDR_LEN_4] = FW_content[i]; + + if (himax_bus_write(pic_op->addr_ahb_addr_byte_0[0], buring_data, program_length + ADDR_LEN_4, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + } + + if (!g_core_fp.fp_wait_wip(1)) + E("%s:Flash_Programming Fail\n", __func__); + + } +} + +static void himax_mcu_flash_page_write(uint8_t *write_addr, int length, uint8_t *write_data) +{ +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_32k(unsigned char *fw, int len, bool change_iref) +{ + /* Not use */ + return 0; +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_60k(unsigned char *fw, int len, bool change_iref) +{ + /* Not use */ + return 0; +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_64k(unsigned char *fw, int len, bool change_iref) +{ + int burnFW_success = 0; + + if (len != FW_SIZE_64k) { + E("%s: The file size is not 64K bytes\n", __func__); + return false; + } + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + g_core_fp.fp_sense_off(true); + g_core_fp.fp_block_erase(0x00, FW_SIZE_64k); + g_core_fp.fp_flash_programming(fw, FW_SIZE_64k); + + if (g_core_fp.fp_check_CRC(pfw_op->addr_program_reload_from, FW_SIZE_64k) == 0) + burnFW_success = 1; + + /*RawOut select initial*/ + g_core_fp.fp_register_write(pfw_op->addr_raw_out_sel, sizeof(pfw_op->data_clear), pfw_op->data_clear, 0); + /*DSRAM func initial*/ + g_core_fp.fp_assign_sorting_mode(pfw_op->data_clear); + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + /*System reset*/ + g_core_fp.fp_system_reset(); +#endif + return burnFW_success; +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_124k(unsigned char *fw, int len, bool change_iref) +{ + /* Not use */ + return 0; +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_128k(unsigned char *fw, int len, bool change_iref) +{ + int burnFW_success = 0; + + if (len != FW_SIZE_128k) { + E("%s: The file size is not 128K bytes\n", __func__); + return false; + } + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + g_core_fp.fp_sense_off(true); + g_core_fp.fp_block_erase(0x00, FW_SIZE_128k); + g_core_fp.fp_flash_programming(fw, FW_SIZE_128k); + + if (g_core_fp.fp_check_CRC(pfw_op->addr_program_reload_from, FW_SIZE_128k) == 0) + burnFW_success = 1; + + /*RawOut select initial*/ + g_core_fp.fp_register_write(pfw_op->addr_raw_out_sel, sizeof(pfw_op->data_clear), pfw_op->data_clear, 0); + /*DSRAM func initial*/ + g_core_fp.fp_assign_sorting_mode(pfw_op->data_clear); + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + /*System reset*/ + g_core_fp.fp_system_reset(); +#endif + return burnFW_success; +} + +static void himax_mcu_flash_dump_func(uint8_t local_flash_command, int Flash_Size, uint8_t *flash_buffer) +{ + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t buffer[256]; + int page_prog_start = 0; + + g_core_fp.fp_sense_off(true); + g_core_fp.fp_burst_enable(1); + + for (page_prog_start = 0; page_prog_start < Flash_Size; page_prog_start += 128) { + tmp_addr[0] = page_prog_start % 0x100; + tmp_addr[1] = (page_prog_start >> 8) % 0x100; + tmp_addr[2] = (page_prog_start >> 16) % 0x100; + tmp_addr[3] = page_prog_start / 0x1000000; + himax_mcu_register_read(tmp_addr, 128, buffer, 0); + memcpy(&flash_buffer[page_prog_start], buffer, 128); + } + + g_core_fp.fp_burst_enable(0); + g_core_fp.fp_sense_on(0x01); +} + +static bool himax_mcu_flash_lastdata_check(uint32_t size) +{ + uint8_t tmp_addr[4]; + uint32_t start_addr = 0xFF80; /* 64K - 0x80, which is the address of the last 128bytes in 64K, default value*/ + uint32_t temp_addr = 0; + uint32_t flash_page_len = 0x80; + uint8_t flash_tmp_buffer[128]; + + start_addr = size - flash_page_len; /* In order to match other size of fw*/ + I("%s: Now size is %d, the start_addr is 0x%04X\n", __func__, size, start_addr); + for (temp_addr = start_addr; temp_addr < (start_addr + flash_page_len); temp_addr = temp_addr + flash_page_len) { + /*I("temp_addr=%d,tmp_addr[0]=0x%2X, tmp_addr[1]=0x%2X,tmp_addr[2]=0x%2X,tmp_addr[3]=0x%2X\n", temp_addr,tmp_addr[0], tmp_addr[1], tmp_addr[2],tmp_addr[3]);*/ + tmp_addr[0] = temp_addr % 0x100; + tmp_addr[1] = (temp_addr >> 8) % 0x100; + tmp_addr[2] = (temp_addr >> 16) % 0x100; + tmp_addr[3] = temp_addr / 0x1000000; + g_core_fp.fp_register_read(tmp_addr, flash_page_len, &flash_tmp_buffer[0], 0); + } + + if ((!flash_tmp_buffer[flash_page_len-4]) && (!flash_tmp_buffer[flash_page_len-3]) && (!flash_tmp_buffer[flash_page_len-2]) && (!flash_tmp_buffer[flash_page_len-1])) { + I("Fail, Last four Bytes are flash_buffer[FFFC]=0x%2X,flash_buffer[FFFD]=0x%2X,flash_buffer[FFFE]=0x%2X,flash_buffer[FFFF]=0x%2X\n", + flash_tmp_buffer[flash_page_len-4], flash_tmp_buffer[flash_page_len-3], flash_tmp_buffer[flash_page_len-2], flash_tmp_buffer[flash_page_len-1]); + goto FAIL;/*FAIL*/ + } else if ((flash_tmp_buffer[flash_page_len-4] == 0xFF) && (flash_tmp_buffer[flash_page_len-3] == 0xFF) && (flash_tmp_buffer[flash_page_len-2] == 0xFF) && (flash_tmp_buffer[flash_page_len-1] == 0xFF)) { + I("Fail, Last four Bytes are flash_buffer[FFFC]=0x%2X,flash_buffer[FFFD]=0x%2X,flash_buffer[FFFE]=0x%2X,flash_buffer[FFFF]=0x%2X\n", + flash_tmp_buffer[flash_page_len-4], flash_tmp_buffer[flash_page_len-3], flash_tmp_buffer[flash_page_len-2], flash_tmp_buffer[flash_page_len-1]); + goto FAIL; + } else { + I("flash_buffer[FFFC]=0x%2X,flash_buffer[FFFD]=0x%2X,flash_buffer[FFFE]=0x%2X,flash_buffer[FFFF]=0x%2X\n", + flash_tmp_buffer[flash_page_len-4], flash_tmp_buffer[flash_page_len-3], flash_tmp_buffer[flash_page_len-2], flash_tmp_buffer[flash_page_len-1]); + return 0;/*PASS*/ + } + return 0; +FAIL: + return 1; +} +static int hx_mcu_diff_overlay_flash(void) +{ + int rslt = 0; + int diff_val = 0; + + diff_val = (ic_data->vendor_fw_ver); + I("%s:Now fw ID is 0x%04X\n", __func__, diff_val); + diff_val = (diff_val >> 12); + I("%s:Now diff value=0x%04X\n", __func__, diff_val); + + if (diff_val == 1) + I("%s:Now size should be 128K!\n", __func__); + else + I("%s:Now size should be 64K!\n", __func__); + rslt = diff_val; + return rslt; +} + +/* FLASH side end*/ +#endif + +#ifdef CORE_SRAM +/* SRAM side start*/ +static void himax_mcu_sram_write(uint8_t *FW_content) +{ +} + +static bool himax_mcu_sram_verify(uint8_t *FW_File, int FW_Size) +{ + return true; +} + +static bool himax_mcu_get_DSRAM_data(uint8_t *info_data, bool DSRAM_Flag) +{ + int i = 0; + unsigned char tmp_addr[ADDR_LEN_4]; + unsigned char tmp_data[DATA_LEN_4]; + uint8_t max_i2c_size = MAX_I2C_TRANS_SZ; + uint8_t x_num = ic_data->HX_RX_NUM; + uint8_t y_num = ic_data->HX_TX_NUM; + /*int m_key_num = 0;*/ + int total_size = (x_num * y_num + x_num + y_num) * 2 + 4; + int total_size_temp; + int mutual_data_size = x_num * y_num * 2; + int total_read_times = 0; + int address = 0; + uint8_t *temp_info_data; /*max mkey size = 8*/ + uint16_t check_sum_cal = 0; + int fw_run_flag = -1; + + temp_info_data = kcalloc((total_size + 8), sizeof(uint8_t), GFP_KERNEL); + /* 1. Read number of MKey R100070E8H to determin data size */ + /* m_key_num = ic_data->HX_BT_NUM; */ + /* I("%s,m_key_num=%d\n",__func__ ,m_key_num); */ + /* total_size += m_key_num * 2; */ + /* 2. Start DSRAM Rawdata and Wait Data Ready */ + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = psram_op->passwrd_start[1]; + tmp_data[0] = psram_op->passwrd_start[0]; + fw_run_flag = himax_write_read_reg(psram_op->addr_rawdata_addr, tmp_data, psram_op->passwrd_end[1], psram_op->passwrd_end[0]); + + if (fw_run_flag < 0) { + I("%s Data NOT ready => bypass\n", __func__); + goto FAIL; + } + + /* 3. Read RawData */ + total_size_temp = total_size; + I("%s: tmp_data[0] = 0x%02X,tmp_data[1] = 0x%02X,tmp_data[2] = 0x%02X,tmp_data[3] = 0x%02X\n", + __func__, psram_op->addr_rawdata_addr[0], psram_op->addr_rawdata_addr[1], psram_op->addr_rawdata_addr[2], psram_op->addr_rawdata_addr[3]); + tmp_addr[0] = psram_op->addr_rawdata_addr[0]; + tmp_addr[1] = psram_op->addr_rawdata_addr[1]; + tmp_addr[2] = psram_op->addr_rawdata_addr[2]; + tmp_addr[3] = psram_op->addr_rawdata_addr[3]; + + if (total_size % max_i2c_size == 0) + total_read_times = total_size / max_i2c_size; + else + total_read_times = total_size / max_i2c_size + 1; + + for (i = 0; i < total_read_times; i++) { + address = (psram_op->addr_rawdata_addr[3] << 24) + + (psram_op->addr_rawdata_addr[2] << 16) + + (psram_op->addr_rawdata_addr[1] << 8) + + psram_op->addr_rawdata_addr[0] + i * max_i2c_size; + /*I("%s address = %08X\n", __func__, address);*/ + + tmp_addr[3] = (uint8_t)((address >> 24) & 0x00FF); + tmp_addr[2] = (uint8_t)((address >> 16) & 0x00FF); + tmp_addr[1] = (uint8_t)((address >> 8) & 0x00FF); + tmp_addr[0] = (uint8_t)((address) & 0x00FF); + + if (total_size_temp >= max_i2c_size) { + g_core_fp.fp_register_read(tmp_addr, max_i2c_size, &temp_info_data[i * max_i2c_size], 0); + total_size_temp = total_size_temp - max_i2c_size; + } else { + /*I("last total_size_temp=%d\n",total_size_temp);*/ + g_core_fp.fp_register_read(tmp_addr, total_size_temp % max_i2c_size, &temp_info_data[i * max_i2c_size], 0); + } + } + + /* 4. FW stop outputing */ + tmp_data[3] = temp_info_data[3]; tmp_data[2] = temp_info_data[2]; + tmp_data[1] = 0x00; tmp_data[0] = 0x00; + g_core_fp.fp_register_write(psram_op->addr_rawdata_addr, DATA_LEN_4, tmp_data, 0); + + /* 5. Data Checksum Check */ + for (i = 2; i < total_size; i += 2)/* 2:PASSWORD NOT included */ + check_sum_cal += (temp_info_data[i + 1] * 256 + temp_info_data[i]); + + if (check_sum_cal % 0x10000 != 0) { + I("%s check_sum_cal fail=%2X\n", __func__, check_sum_cal); + goto FAIL; + } else { + memcpy(info_data, &temp_info_data[4], mutual_data_size * sizeof(uint8_t)); + /*I("%s checksum PASS\n", __func__);*/ + } + kfree(temp_info_data); + return true; +FAIL: + kfree(temp_info_data); + return false; +} +/* SRAM side end*/ +#endif + +#ifdef CORE_DRIVER + +static void himax_mcu_init_ic(void) +{ + I("%s: use default incell init.\n", __func__); +} + +#ifdef HX_AUTO_UPDATE_FW +static int himax_mcu_fw_ver_bin(void) +{ + I("%s: use default incell address.\n", __func__); + I("%s:Entering!\n", __func__); + if (i_CTPM_FW != NULL) { + I("Catch fw version in bin file!\n"); + g_i_FW_VER = (i_CTPM_FW[FW_VER_MAJ_FLASH_ADDR] << 8) | i_CTPM_FW[FW_VER_MIN_FLASH_ADDR]; + g_i_CFG_VER = (i_CTPM_FW[CFG_VER_MAJ_FLASH_ADDR] << 8) | i_CTPM_FW[CFG_VER_MIN_FLASH_ADDR]; + g_i_CID_MAJ = i_CTPM_FW[CID_VER_MAJ_FLASH_ADDR]; + g_i_CID_MIN = i_CTPM_FW[CID_VER_MIN_FLASH_ADDR]; + } else { + I("FW data is null!\n"); + return 1; + } + return NO_ERR; +} +#endif + + +#ifdef HX_RST_PIN_FUNC +static void himax_mcu_pin_reset(void) +{ + I("%s: Now reset the Touch chip,private_ts->vdd_1v8_always_on = %d \n", + __func__,private_ts->vdd_1v8_always_on); + himax_rst_gpio_set(private_ts->rst_gpio, 0); +#if defined(__HIMAX_HX83102_MOD__) + msleep(5); +#else + if(private_ts->vdd_1v8_always_on) + msleep(10); + else + msleep(20); +#endif + himax_rst_gpio_set(private_ts->rst_gpio, 1); +#ifdef HX_ZERO_FLASH +#if defined(__HIMAX_HX83102_MOD__) + msleep(2); +#else + msleep(10); +#endif +#else + msleep(50); +#endif + +} + +static void himax_mcu_ic_reset(uint8_t loadconfig, uint8_t int_off) +{ + struct himax_ts_data *ts = private_ts; + + HX_HW_RESET_ACTIVATE = 0; + I("%s,status: loadconfig=%d,int_off=%d\n", __func__, loadconfig, int_off); + + if (ts->rst_gpio >= 0) { + if (int_off) + g_core_fp.fp_irq_switch(0); + + g_core_fp.fp_pin_reset(); + + if (loadconfig) + g_core_fp.fp_reload_config(); + + if (int_off) + g_core_fp.fp_irq_switch(1); + + } +} +#endif + +#ifndef HX_FIX_TOUCH_INFO +static uint8_t himax_mcu_tp_info_check(void) +{ + uint8_t rx = pdriver_op->data_df_rx[0]; + uint8_t tx = pdriver_op->data_df_tx[0]; + uint8_t pt = pdriver_op->data_df_pt[0]; + uint16_t x_res = pdriver_op->data_df_x_res[1] << 8 | pdriver_op->data_df_x_res[0]; + uint16_t y_res = pdriver_op->data_df_y_res[1] << 8 | pdriver_op->data_df_y_res[0]; + uint8_t err_cnt = 0; + + if (ic_data->HX_RX_NUM < (rx / 2) || ic_data->HX_RX_NUM > (rx * 3 / 2)) { + ic_data->HX_RX_NUM = rx; + err_cnt |= 0x01; + } + if (ic_data->HX_TX_NUM < (tx / 2) || ic_data->HX_TX_NUM > (tx * 3 / 2)) { + ic_data->HX_TX_NUM = tx; + err_cnt |= 0x02; + } + if (ic_data->HX_MAX_PT < (pt / 2) || ic_data->HX_MAX_PT > (pt * 3 / 2)) { + ic_data->HX_MAX_PT = pt; + err_cnt |= 0x04; + } + if (ic_data->HX_Y_RES < (y_res / 2) || ic_data->HX_Y_RES > (y_res * 3 / 2)) { + ic_data->HX_Y_RES = y_res; + err_cnt |= 0x08; + } + if (ic_data->HX_X_RES < (x_res / 2) || ic_data->HX_X_RES > (x_res * 3 / 2)) { + ic_data->HX_X_RES = x_res; + err_cnt |= 0x10; + } + return err_cnt; +} +#endif + +static void himax_mcu_touch_information(void) +{ +#ifndef HX_FIX_TOUCH_INFO + char data[DATA_LEN_8] = {0}; + uint8_t err_cnt = 0; + + I(" DT-%s:touch_info: HX_RX_NUM = %d, HX_TX_NUM = %d, HX_BT_NUM = %d,HX_X_RES = %d,HX_Y_RES = %d, HX_MAX_PT = %d, HX_XY_REVERSE = %d, HX_INT_IS_EDGE = %d\n", __func__, + ic_data->HX_RX_NUM,ic_data->HX_TX_NUM,ic_data->HX_BT_NUM,ic_data->HX_X_RES,ic_data->HX_Y_RES,ic_data->HX_MAX_PT,ic_data->HX_XY_REVERSE,ic_data->HX_INT_IS_EDGE); + + if ((0 == ic_data->HX_RX_NUM) || (0 == ic_data->HX_TX_NUM) || (0 == ic_data->HX_X_RES) || (0 == ic_data->HX_Y_RES)) { + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_rxnum_txnum_maxpt, DATA_LEN_8, data, 0); + ic_data->HX_RX_NUM = data[2]; + ic_data->HX_TX_NUM = data[3]; + ic_data->HX_MAX_PT = data[4]; + /*I("%s : HX_RX_NUM=%d,ic_data->HX_TX_NUM=%d,ic_data->HX_MAX_PT=%d\n",__func__,ic_data->HX_RX_NUM,ic_data->HX_TX_NUM,ic_data->HX_MAX_PT);*/ + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_xy_res_enable, DATA_LEN_4, data, 0); + + /*I("%s : c_data->HX_XY_REVERSE=0x%2.2X\n",__func__,data[1]);*/ + if ((data[1] & 0x04) == 0x04) + ic_data->HX_XY_REVERSE = true; + else + ic_data->HX_XY_REVERSE = false; + + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_x_y_res, DATA_LEN_4, data, 0); + ic_data->HX_Y_RES = data[0] * 256 + data[1]; + ic_data->HX_X_RES = data[2] * 256 + data[3]; + /*I("%s : ic_data->HX_Y_RES=%d,ic_data->HX_X_RES=%d\n",__func__,ic_data->HX_Y_RES,ic_data->HX_X_RES);*/ + + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_int_is_edge, DATA_LEN_4, data, 0); + /*I("%s : data[0]=0x%2.2X,data[1]=0x%2.2X,data[2]=0x%2.2X,data[3]=0x%2.2X\n",__func__,data[0],data[1],data[2],data[3]);*/ + /*I("data[0] & 0x01 = %d\n",(data[0] & 0x01));*/ + if ((data[1] & 0x01) == 1) + ic_data->HX_INT_IS_EDGE = true; + else + ic_data->HX_INT_IS_EDGE = false; + + /*1. Read number of MKey R100070E8H to determin data size*/ + g_core_fp.fp_register_read(psram_op->addr_mkey, DATA_LEN_4, data, 0); + /* I("%s: tmp_data[0] = 0x%02X,tmp_data[1] = 0x%02X,tmp_data[2] = 0x%02X,tmp_data[3] = 0x%02X\n",*/ + /*__func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]);*/ + ic_data->HX_BT_NUM = data[0] & 0x03; + + err_cnt = himax_mcu_tp_info_check(); + if (err_cnt > 0) + E("TP Info from IC is wrong, err_cnt = 0x%X", err_cnt); + } + +#endif + I("%s:HX_RX_NUM =%d,HX_TX_NUM =%d,HX_MAX_PT=%d\n", __func__, ic_data->HX_RX_NUM, ic_data->HX_TX_NUM, ic_data->HX_MAX_PT); + I("%s:HX_XY_REVERSE =%d,HX_Y_RES =%d,HX_X_RES=%d\n", __func__, ic_data->HX_XY_REVERSE, ic_data->HX_Y_RES, ic_data->HX_X_RES); + I("%s:HX_INT_IS_EDGE =%d\n", __func__, ic_data->HX_INT_IS_EDGE); +} + +static void himax_mcu_reload_config(void) +{ + if (himax_report_data_init()) + E("%s: allocate data fail\n", __func__); + + g_core_fp.fp_sense_on(0x00); +} + +static int himax_mcu_get_touch_data_size(void) +{ + return HIMAX_TOUCH_DATA_SIZE; +} + +static int himax_mcu_hand_shaking(void) +{ + /* 0:Running, 1:Stop, 2:I2C Fail */ + int result = 0; + return result; +} + +static int himax_mcu_determin_diag_rawdata(int diag_command) +{ + return diag_command % 10; +} + +static int himax_mcu_determin_diag_storage(int diag_command) +{ + return diag_command / 10; +} + +static int himax_mcu_cal_data_len(int raw_cnt_rmd, int HX_MAX_PT, int raw_cnt_max) +{ + int RawDataLen; + + if (raw_cnt_rmd != 0x00) + RawDataLen = MAX_I2C_TRANS_SZ - ((HX_MAX_PT + raw_cnt_max + 3) * 4) - 1; + else + RawDataLen = MAX_I2C_TRANS_SZ - ((HX_MAX_PT + raw_cnt_max + 2) * 4) - 1; + + return RawDataLen; +} + +static bool himax_mcu_diag_check_sum(struct himax_report_data *hx_touch_data) +{ + uint16_t check_sum_cal = 0; + int i; + + /* Check 128th byte CRC */ + for (i = 0, check_sum_cal = 0; i < (hx_touch_data->touch_all_size - hx_touch_data->touch_info_size); i += 2) + check_sum_cal += (hx_touch_data->hx_rawdata_buf[i + 1] * FLASH_RW_MAX_LEN + hx_touch_data->hx_rawdata_buf[i]); + + + if (check_sum_cal % HX64K != 0) { + I("%s fail=%2X\n", __func__, check_sum_cal); + return 0; + } + + return 1; +} + +static void himax_mcu_diag_parse_raw_data(struct himax_report_data *hx_touch_data, int mul_num, int self_num, uint8_t diag_cmd, int32_t *mutual_data, int32_t *self_data) +{ + diag_mcu_parse_raw_data(hx_touch_data, mul_num, self_num, diag_cmd, mutual_data, self_data); +} + +#ifdef HX_ESD_RECOVERY +static int himax_mcu_ic_esd_recovery(int hx_esd_event, int hx_zero_event, int length) +{ + int ret_val = NO_ERR; + + if (hx_esd_event == length) { + g_zero_event_count = 0; + ret_val = HX_ESD_EVENT; + } else if (hx_zero_event == length) { + if (g_zero_event_count > 5) { + g_zero_event_count = 0; + I("[HIMAX TP MSG]: ESD event checked - ALL Zero.\n"); + ret_val = HX_ESD_EVENT; + } else { + g_zero_event_count++; + I("[HIMAX TP MSG]: ALL Zero event is %d times.\n", g_zero_event_count); + ret_val = HX_ZERO_EVENT_COUNT; + } + } + + return ret_val; +} + +static void himax_mcu_esd_ic_reset(void) +{ + HX_ESD_RESET_ACTIVATE = 0; +#ifdef HX_RST_PIN_FUNC + himax_mcu_pin_reset(); +#else + himax_mcu_system_reset(); +#endif + I("%s:\n", __func__); +} +#endif +#ifdef HX_TP_PROC_GUEST_INFO +char *g_checksum_str = "check sum fail"; +/* char *g_guest_info_item[] = { + * "projectID", + * "CGColor", + * "BarCode", + * "Reserve1", + * "Reserve2", + * "Reserve3", + * "Reserve4", + * "Reserve5", + * "VCOM", + * "Vcom-3Gar", + * NULL + * }; + */ + +static int himax_guest_info_get_status(void) +{ + return g_guest_info_data->g_guest_info_ongoing; +} +static void himax_guest_info_set_status(int setting) +{ + g_guest_info_data->g_guest_info_ongoing = setting; +} + +static int himax_guest_info_read(uint32_t start_addr, uint8_t *flash_tmp_buffer) +{ + uint32_t temp_addr = 0; + uint8_t tmp_addr[4]; + uint32_t flash_page_len = 0x1000; + /* uint32_t checksum = 0x00; */ + int result = 0; + + + I("Reading guest info in start_addr = 0x%08X !\n", start_addr); + + tmp_addr[0] = start_addr % 0x100; + tmp_addr[1] = (start_addr >> 8) % 0x100; + tmp_addr[2] = (start_addr >> 16) % 0x100; + tmp_addr[3] = start_addr / 0x1000000; + I("Now start addr: tmp_addr[0]=0x%2X,tmp_addr[1]=0x%2X,tmp_addr[2]=0x%2X,tmp_addr[3]=0x%2X\n", tmp_addr[0], tmp_addr[1], tmp_addr[2], tmp_addr[3]); + result = g_core_fp.fp_check_CRC(tmp_addr, flash_page_len); + I("Checksum = 0x%8X\n", result); + if (result != 0) + goto END_FUNC; + + for (temp_addr = start_addr; temp_addr < (start_addr + flash_page_len); temp_addr = temp_addr + 128) { + + /* I("temp_addr=%d,tmp_addr[0]=0x%2X,tmp_addr[1]=0x%2X,tmp_addr[2]=0x%2X,tmp_addr[3]=0x%2X\n",temp_addr,tmp_addr[0],tmp_addr[1],tmp_addr[2],tmp_addr[3]); */ + tmp_addr[0] = temp_addr % 0x100; + tmp_addr[1] = (temp_addr >> 8) % 0x100; + tmp_addr[2] = (temp_addr >> 16) % 0x100; + tmp_addr[3] = temp_addr / 0x1000000; + g_core_fp.fp_register_read(tmp_addr, 128, &flash_tmp_buffer[temp_addr - start_addr], false); + /* memcpy(&flash_tmp_buffer[temp_addr - start_addr],buffer,128);*/ + } + +END_FUNC: + return result; +} + +static int hx_read_guest_info(void) +{ + /* uint8_t tmp_addr[4]; */ + uint32_t panel_color_addr = 0x10000;/*64k*/ + + uint32_t info_len; + uint32_t flash_page_len = 0x1000;/*4k*/ + uint8_t *flash_tmp_buffer = NULL; + /* uint32_t temp_addr = 0; */ + uint8_t temp_str[128]; + int i = 0; + int custom_info_temp = 0; + int checksum = 0; + + himax_guest_info_set_status(1); + + flash_tmp_buffer = kcalloc(HX_GUEST_INFO_SIZE * flash_page_len, sizeof(uint8_t), GFP_KERNEL); + if (flash_tmp_buffer == NULL) { + I("%s: Memory allocate fail!\n", __func__); + return MEM_ALLOC_FAIL; + } + + g_core_fp.fp_sense_off(true); + /* g_core_fp.fp_burst_enable(1); */ + + for (custom_info_temp = 0; custom_info_temp < HX_GUEST_INFO_SIZE; custom_info_temp++) { + checksum = himax_guest_info_read(panel_color_addr+custom_info_temp * flash_page_len, &flash_tmp_buffer[custom_info_temp * flash_page_len]); + if (checksum != 0) { + E("%s:Checksum Fail! g_checksum_str len=%d\n", __func__, (int)strlen(g_checksum_str)); + memcpy(&g_guest_info_data->g_guest_str_in_format[custom_info_temp][0], g_checksum_str, (int)strlen(g_checksum_str)); + memcpy(&g_guest_info_data->g_guest_str[custom_info_temp][0], g_checksum_str, (int)strlen(g_checksum_str)); + continue; + } + + info_len = flash_tmp_buffer[custom_info_temp * flash_page_len] + + (flash_tmp_buffer[custom_info_temp*flash_page_len + 1] << 8) + + (flash_tmp_buffer[custom_info_temp*flash_page_len + 2] << 16) + + (flash_tmp_buffer[custom_info_temp*flash_page_len + 3] << 24); + + I("Now custom_info_temp = %d\n", custom_info_temp); + + I("Now size_buff[0]=0x%02X,[1]=0x%02X,[2]=0x%02X,[3]=0x%02X\n" + , flash_tmp_buffer[custom_info_temp*flash_page_len] + , flash_tmp_buffer[custom_info_temp*flash_page_len + 1] + , flash_tmp_buffer[custom_info_temp*flash_page_len + 2] + , flash_tmp_buffer[custom_info_temp*flash_page_len + 3]); + + I("Now total length=%d\n", info_len); + + g_guest_info_data->g_guest_data_len[custom_info_temp] = info_len; + + I("Now custom_info_id [0]=%d,[1]=%d,[2]=%d,[3]=%d\n" + , flash_tmp_buffer[custom_info_temp*flash_page_len + 4] + , flash_tmp_buffer[custom_info_temp*flash_page_len + 5] + , flash_tmp_buffer[custom_info_temp*flash_page_len + 6] + , flash_tmp_buffer[custom_info_temp*flash_page_len + 7]); + + g_guest_info_data->g_guest_data_type[custom_info_temp] = flash_tmp_buffer[custom_info_temp * flash_page_len + 7]; + + /* if(custom_info_temp < 3) { */ + if (info_len > 128) { + I("%s: info_len=%d\n", __func__, info_len); + info_len = 128; + } + for (i = 0; i < info_len; i++) + temp_str[i] = flash_tmp_buffer[custom_info_temp * flash_page_len + HX_GUEST_INFO_LEN_SIZE + HX_GUEST_INFO_ID_SIZE + i]; + I("g_guest_info_data->g_guest_str_in_format[%d] size = %d\n", custom_info_temp, info_len); + memcpy(&g_guest_info_data->g_guest_str_in_format[custom_info_temp][0], temp_str, info_len); + /*}*/ + + for (i = 0; i < 128; i++) + temp_str[i] = flash_tmp_buffer[custom_info_temp * flash_page_len + i]; + + I("g_guest_info_data->g_guest_str[%d] size = %d\n", custom_info_temp, 128); + memcpy(&g_guest_info_data->g_guest_str[custom_info_temp][0], temp_str, 128); + /*if(custom_info_temp == 0) + *{ + * for( i = 0; i< 256 ; i++) + * { + * if(i % 16 == 0 && i > 0) + * I("\n"); + * I("g_guest_info_data->g_guest_str[%d][%d] = 0x%02X",custom_info_temp,i,g_guest_info_data->g_guest_str[custom_info_temp][i]); + * } + *} + */ + } + /* himax_burst_enable(private_ts->client, 0); */ + + g_core_fp.fp_sense_on(0x01); + + kfree(flash_tmp_buffer); + himax_guest_info_set_status(0); + return NO_ERR; +} +#endif +#endif + +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) +static void himax_mcu_resend_cmd_func(bool suspended) +{ +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) + struct himax_ts_data *ts = private_ts; +#endif +#ifdef HX_SMART_WAKEUP + g_core_fp.fp_set_SMWP_enable(ts->SMWP_enable, suspended); +#endif +#ifdef HX_EDGE_LIMIT + g_core_fp.fp_set_edge_limit_enable(ts->edge_limit_enable, suspended); +#endif +#ifdef HX_HIGH_SENSE + g_core_fp.fp_set_HSEN_enable(ts->HSEN_enable, suspended); +#endif +#ifdef HX_USB_DETECT_GLOBAL + himax_cable_detect_func(true); +#endif +} +#endif + +#ifdef HX_ZERO_FLASH +int G_POWERONOF = 1; +EXPORT_SYMBOL(G_POWERONOF); +void hx_dis_rload_0f(int disable) +{ + /*Diable Flash Reload*/ + g_core_fp.fp_register_write(pzf_op->addr_dis_flash_reload, DATA_LEN_4, pzf_op->data_dis_flash_reload, 0); +} + +void himax_mcu_clean_sram_0f(uint8_t *addr, int write_len, int type) +{ + int total_read_times = 0; + int max_bus_size = MAX_I2C_TRANS_SZ; + int total_size_temp = 0; + int total_size = 0; + int address = 0; + int i = 0; + + uint8_t fix_data = 0x00; + uint8_t tmp_addr[4]; + uint8_t tmp_data[MAX_I2C_TRANS_SZ] = {0}; + + I("%s, Entering\n", __func__); + + total_size = write_len; + +#ifdef __MTK_SPI__ + if (write_len > (HX_SPI_MTK_MAX_WRITE_SZ - 4)) + max_bus_size = (HX_SPI_MTK_MAX_WRITE_SZ - 4); + else + max_bus_size = write_len; +#else + if (total_size > PACKET_MAX_SZ - 8) + max_bus_size = PACKET_MAX_SZ - 8; +#endif + + total_size_temp = write_len; + + g_core_fp.fp_burst_enable(1); + + tmp_addr[3] = addr[3]; + tmp_addr[2] = addr[2]; + tmp_addr[1] = addr[1]; + tmp_addr[0] = addr[0]; + I("%s, write addr tmp_addr[3]=0x%2.2X, tmp_addr[2]=0x%2.2X, tmp_addr[1]=0x%2.2X, tmp_addr[0]=0x%2.2X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + + switch (type) { + case 0: + fix_data = 0x00; + break; + case 1: + fix_data = 0xAA; + break; + case 2: + fix_data = 0xBB; + break; + } + + for (i = 0; i < MAX_I2C_TRANS_SZ; i++) + tmp_data[i] = fix_data; + + I("%s, total size=%d\n", __func__, total_size); + + if (total_size_temp % max_bus_size == 0) + total_read_times = total_size_temp / max_bus_size; + else + total_read_times = total_size_temp / max_bus_size + 1; + + for (i = 0; i < (total_read_times); i++) { + I("[log]write %d time start!\n", i); + if (total_size_temp >= max_bus_size) { + g_core_fp.fp_register_write(tmp_addr, max_bus_size, tmp_data, 0); + total_size_temp = total_size_temp - max_bus_size; + } else { + I("last total_size_temp=%d\n", total_size_temp); + g_core_fp.fp_register_write(tmp_addr, total_size_temp % max_bus_size, tmp_data, 0); + } + address = ((i+1) * max_bus_size); + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF); + tmp_addr[0] = addr[0] + (uint8_t) ((address) & 0x00FF); + + usleep_range(10000, 11000); + } + + I("%s, END\n", __func__); +} + +void himax_mcu_write_sram_0f(const struct firmware *fw_entry, uint8_t *addr, int start_index, uint32_t write_len) +{ + int total_read_times = 0; + int max_bus_size = MAX_I2C_TRANS_SZ; + int total_size_temp = 0; + int address = 0; + int i = 0; + + uint8_t tmp_addr[4]; + uint8_t *tmp_data; + + total_size_temp = write_len; + I("%s, Entering - total write size=%d\n", __func__, total_size_temp); + +#if defined(HX_SPI_OPERATION) + if (write_len > PACKET_MAX_SZ - 8) + max_bus_size = PACKET_MAX_SZ - 8; + else + max_bus_size = write_len; +#else +#ifdef __MTK_SPI__ + if (write_len > (HX_SPI_MTK_MAX_WRITE_SZ - 4)) + max_bus_size = (HX_SPI_MTK_MAX_WRITE_SZ - 4); + else + max_bus_size = write_len; +#endif +#endif + + g_core_fp.fp_burst_enable(1); + + tmp_addr[3] = addr[3]; + tmp_addr[2] = addr[2]; + tmp_addr[1] = addr[1]; + tmp_addr[0] = addr[0]; + I("%s, write addr = 0x%02X%02X%02X%02X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + + /* tmp_data = kcalloc(max_bus_size, sizeof(uint8_t), GFP_KERNEL); + * if (tmp_data == NULL) { + * I("%s: Can't allocate enough buf\n", __func__); + * return; + * } + */ + + /*for(i = 0;i<10;i++) + *{ + * I("[%d] 0x%2.2X", i, tmp_data[i]); + *} + *I("\n"); + */ + if (total_size_temp % max_bus_size == 0) + total_read_times = total_size_temp / max_bus_size; + else + total_read_times = total_size_temp / max_bus_size + 1; + + for (i = 0; i < (total_read_times); i++) { + /*I("[log]write %d time start!\n", i);*/ + /*I("[log]addr[3]=0x%02X, addr[2]=0x%02X, addr[1]=0x%02X, addr[0]=0x%02X!\n", tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]);*/ + + if (total_size_temp >= max_bus_size) { + /*memcpy(tmp_data, &fw_entry->data[start_index+i * max_bus_size], max_bus_size);*/ + tmp_data = (uint8_t *)&fw_entry->data[start_index+i * max_bus_size]; + g_core_fp.fp_register_write(tmp_addr, max_bus_size, tmp_data, 0); + total_size_temp = total_size_temp - max_bus_size; + } else { + /*memcpy(tmp_data, &fw_entry->data[start_index+i * max_bus_size], total_size_temp % max_bus_size);*/ + tmp_data = (uint8_t *)&fw_entry->data[start_index+i * max_bus_size]; + I("last total_size_temp=%d\n", total_size_temp % max_bus_size); + g_core_fp.fp_register_write(tmp_addr, total_size_temp % max_bus_size, tmp_data, 0); + } + + /*I("[log]write %d time end!\n", i);*/ + address = ((i+1) * max_bus_size); + tmp_addr[0] = addr[0] + (uint8_t) ((address) & 0x00FF); + + if (tmp_addr[0] < addr[0]) + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF) + 1; + else + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF); + + udelay(100); + } + I("%s, End\n", __func__); + /* kfree(tmp_data); */ +} + +int himax_sram_write_crc_check(const struct firmware *fw_entry, uint8_t *addr, int strt_idx, uint32_t len) +{ + int retry = 0; + int crc = -1; + + do { + g_core_fp.fp_write_sram_0f(fw_entry, addr, strt_idx, len); + crc = g_core_fp.fp_check_CRC(addr, len); + retry++; + I("%s, HW CRC %s in %d time\n", __func__, (crc == 0)?"OK":"Fail", retry); + } while (crc != 0 && retry < 3); + + return crc; +} +#if defined(HX83102D) +int hx_parse_bin_cfg_data(const struct firmware *fw_entry, struct zf_info *zf_info_arr, uint8_t *FW_buf, uint8_t *sram_min) +{ + int part_num = 0; + int i = 0; + uint8_t buf[16]; + int i_max = 0; + int i_min = 0; + uint32_t dsram_base = 0xFFFFFFFF; + uint32_t dsram_max = 0; + int cfg_sz = 0; +#if defined(HX_CODE_OVERLAY) + uint8_t j = 0; + int allovlidx = 0; + + if (ovl_idx == NULL) { + ovl_idx = kzalloc(ovl_section_num, GFP_KERNEL); + if (ovl_idx == NULL) { + E("%s, ovl_idx alloc failed!\n", + __func__); + return -ENOMEM; + } + } else { + memset(ovl_idx, 0, ovl_section_num); + } +#endif + /*1. get number of partition*/ + part_num = fw_entry->data[HX64K + 12]; + I("%s, Number of partition is %d\n", __func__, part_num); + if (part_num <= 1) { + E("%s, size of cfg part failed! part_num = %d\n", + __func__, part_num); + return LENGTH_FAIL; + } + + /* i = 0 is fw entity, it need to update by itself*/ + for (i = 1; i < part_num; i++) { + /*3. get all partition*/ + memcpy(buf, &fw_entry->data[i * 0x10 + HX64K], 16); + memcpy(zf_info_arr[i].sram_addr, buf, 4); + zf_info_arr[i].write_size = buf[7] << 24 + | buf[6] << 16 + | buf[5] << 8 + | buf[4]; + zf_info_arr[i].fw_addr = buf[11] << 24 + | buf[10] << 16 + | buf[9] << 8 + | buf[8]; + zf_info_arr[i].cfg_addr = zf_info_arr[i].sram_addr[0]; + zf_info_arr[i].cfg_addr += zf_info_arr[i].sram_addr[1] << 8; + zf_info_arr[i].cfg_addr += zf_info_arr[i].sram_addr[2] << 16; + zf_info_arr[i].cfg_addr += zf_info_arr[i].sram_addr[3] << 24; + +#if defined(HX_CODE_OVERLAY) + /*overlay section*/ + + if ((buf[15] == 0x55 && buf[14] == 0x66) + || (buf[3] == 0x20 && buf[2] == 0x00 + && buf[1] == 0x8C && buf[0] == 0xE0)) { + I("%s: catch overlay section in index %d\n", + __func__, i); + + /* record index of overlay section */ + allovlidx |= 1< zf_info_arr[i].cfg_addr) { + dsram_base = zf_info_arr[i].cfg_addr; + i_min = i; + } + if (dsram_max < zf_info_arr[i].cfg_addr) { + dsram_max = zf_info_arr[i].cfg_addr; + i_max = i; + } + } + for (i = 0; i < ADDR_LEN_4; i++) + sram_min[i] = zf_info_arr[i_min].sram_addr[i]; + + cfg_sz = (dsram_max - dsram_base) + zf_info_arr[i_max].write_size; + if (cfg_sz % 16 != 0) + cfg_sz = cfg_sz + 16 - (cfg_sz % 16); + + I("%s, cfg_sz = %d!, dsram_base = %X, dsram_max = %X\n", __func__, + cfg_sz, dsram_base, dsram_max); + if (cfg_sz <= FW_BIN_16K_SZ) + memset(FW_buf, 0x00, sizeof(uint8_t) * cfg_sz); + else + memset(FW_buf, 0x00, sizeof(uint8_t) * FW_BIN_16K_SZ); + for (i = 1; i < part_num; i++) { + if (zf_info_arr[i].cfg_addr % 4 != 0) + zf_info_arr[i].cfg_addr = zf_info_arr[i].cfg_addr + - (zf_info_arr[i].cfg_addr % 4); + + I("%s,[%d]SRAM addr=%08X,[%d]fw_addr=%08X,[%d]write_size=%d\n", + __func__, + i, zf_info_arr[i].cfg_addr, + i, zf_info_arr[i].fw_addr, + i, zf_info_arr[i].write_size); + +#if defined(HX_CODE_OVERLAY) + /*overlay section*/ + if (allovlidx & (1<data[zf_info_arr[i].fw_addr], + zf_info_arr[i].write_size); + } + /* debug info */ + /* for (i = 1; i < part_num; i++) { + * different = 0; + * I("--cfg_addr = 0x%08X\n",zf_info_arr[i].cfg_addr); + * I("--bin_addr = 0x%08X\n",zf_info_arr[i].fw_addr); + * I("--write size = %d\n",zf_info_arr[i].write_size); + * for ( j = 0; j < zf_info_arr[i].write_size; j++) { + * if(FW_buf[zf_info_arr[i].cfg_addr - dsram_base + j] != + fw_entry->data[zf_info_arr[i].fw_addr + j]) + * different++; + * } + * I("--There are %d different\n", different); + * } + */ + + return cfg_sz; +} +#else +int hx_parse_bin_cfg_data(const struct firmware *fw_entry, struct zf_info *zf_info_arr, uint8_t *FW_buf, uint8_t *sram_min, uint8_t *ovl_sidx) +{ + int part_num = 0; + int i = 0; + uint8_t buf[16]; + int i_max = 0; + int i_min = 0; + uint32_t dsram_base = 0xFFFFFFFF; + uint32_t dsram_max = 0; + int cfg_sz = 0; + /*int different = 0;*/ + /*int j = 0;*/ + + /*1. get number of partition*/ + part_num = fw_entry->data[HX64K + 12]; + I("%s, Number of partition is %d\n", __func__, part_num); + if (part_num <= 1) { + E("%s, size of cfg part failed! part_num = %d\n", __func__, part_num); + return LENGTH_FAIL; + } + /*2. check struct of array*/ + if (zf_info_arr == NULL || FW_buf == NULL) { + E("%s, Struct is NULL, %p,%p\n", __func__, zf_info_arr, FW_buf); + return MEM_ALLOC_FAIL; + } + + /* i = 0 is fw entity, it need to update by itself*/ + for (i = 1; i < part_num; i++) { + /*3. get all partition*/ + memcpy(buf, &fw_entry->data[i * 0x10 + HX64K], 16); + memcpy(zf_info_arr[i].sram_addr, buf, 4); + zf_info_arr[i].write_size = buf[7] << 24 | buf[6] << 16 | buf[5] << 8 | buf[4]; + zf_info_arr[i].fw_addr = buf[11] << 24 | buf[10] << 16 | buf[9] << 8 | buf[8]; + zf_info_arr[i].cfg_addr = zf_info_arr[i].sram_addr[0]; + zf_info_arr[i].cfg_addr += zf_info_arr[i].sram_addr[1] << 8; + zf_info_arr[i].cfg_addr += zf_info_arr[i].sram_addr[2] << 16; + zf_info_arr[i].cfg_addr += zf_info_arr[i].sram_addr[3] << 24; + +#ifdef HX_CODE_OVERLAY + /*overlay section*/ + if (zf_info_arr[i].cfg_addr == ovl_isram_addr) { + if (*ovl_sidx == 0) + *ovl_sidx = i; + + continue; + } +#endif + + if (dsram_base > zf_info_arr[i].cfg_addr) { + dsram_base = zf_info_arr[i].cfg_addr; + i_min = i; + } + if (dsram_max < zf_info_arr[i].cfg_addr) { + dsram_max = zf_info_arr[i].cfg_addr; + i_max = i; + } + } + for (i = 0; i < ADDR_LEN_4; i++) + sram_min[i] = zf_info_arr[i_min].sram_addr[i]; + cfg_sz = (dsram_max - dsram_base) + zf_info_arr[i_max].write_size; + if (cfg_sz % 16 != 0) + cfg_sz = cfg_sz + 16 - (cfg_sz % 16); + + I("%s, cfg_sz = %d!, dsram_base = %X, dsram_max = %X\n", __func__, cfg_sz, dsram_base, dsram_max); + + memset(FW_buf, 0x00, sizeof(uint8_t) * cfg_sz); + for (i = 1; i < part_num; i++) { + if (zf_info_arr[i].cfg_addr % 4 != 0) + zf_info_arr[i].cfg_addr = zf_info_arr[i].cfg_addr - (zf_info_arr[i].cfg_addr % 4); + + I("%s,[%d] SRAM addr = %08X\n", __func__, i, zf_info_arr[i].cfg_addr); + I("%s,[%d] fw_addr = %08X\n", __func__, i, zf_info_arr[i].fw_addr); + I("%s,[%d] write_size = %d\n", __func__, i, zf_info_arr[i].write_size); + +#ifdef HX_CODE_OVERLAY + /*overlay section*/ + if (zf_info_arr[i].cfg_addr == ovl_isram_addr) + continue; +#endif + + memcpy(&FW_buf[zf_info_arr[i].cfg_addr - dsram_base], &fw_entry->data[zf_info_arr[i].fw_addr], zf_info_arr[i].write_size); + } + /* debug info */ + /* for (i = 1; i < part_num; i++) { + * different = 0; + * I("--cfg_addr = 0x%08X\n",zf_info_arr[i].cfg_addr); + * I("--bin_addr = 0x%08X\n",zf_info_arr[i].fw_addr); + * I("--write size = %d\n",zf_info_arr[i].write_size); + * for ( j = 0; j < zf_info_arr[i].write_size; j++) { + * if(FW_buf[zf_info_arr[i].cfg_addr - dsram_base + j] != fw_entry->data[zf_info_arr[i].fw_addr + j]) + * different++; + * } + * I("--There are %d different\n", different); + * } + */ + + return cfg_sz; +} +#endif + +int himax_zf_part_info(const struct firmware *fw_entry) +{ + int part_num = fw_entry->data[HX64K + 12]; + int ret = 0; + uint8_t buf[16]; + struct zf_info *zf_info_arr; + uint8_t *FW_buf; + uint8_t sram_min[4]; + int cfg_crc_sw = 0; + int cfg_crc_hw = 0; + int cfg_sz = 0; + int retry = 3; + + uint8_t ovl_sidx = 0; +#if defined(HX83102D) + uint8_t ovl_idx_t; +#if defined(HX_SMART_WAKEUP) || defined(HX_CODE_OVERLAY) + uint8_t tmp_addr[4] = {0xFC, 0x7F, 0x00, 0x10}; + uint8_t send_data[4] = {0}; + uint8_t recv_data[4] = {0}; +#endif +#else +#ifdef HX_CODE_OVERLAY +#ifdef HX_SMART_WAKEUP + uint8_t tmp_addr[4] = {0xFC, 0x7F, 0x00, 0x10}; + uint8_t send_data[4] = {0}; + uint8_t recv_data[4] = {0}; +#endif +#endif +#endif + + /*1. get number of partition*/ + /*part_num = fw_entry->data[HX64K + 12];*/ + I("%s, Number of partition is %d\n", __func__, part_num); + if (part_num <= 0) + part_num = 1; + + /*2. initial struct of array*/ + zf_info_arr = kcalloc(part_num, sizeof(struct zf_info), GFP_KERNEL); + if (zf_info_arr == NULL) { + E("%s, Allocate ZF info array failed!\n", __func__); + ret = MEM_ALLOC_FAIL; + goto ALOC_ZF_INFO_ARR_FAIL; + } + FW_buf = kcalloc(FW_BIN_16K_SZ, sizeof(uint8_t), GFP_KERNEL); + if (FW_buf == NULL) { + E("%s, Allocate FW_buf array failed!\n", __func__); + ret = MEM_ALLOC_FAIL; + goto ALOC_FW_BUF_FAIL; + } + + /*3. Find the data in the bin file */ + /*CFG*/ +#if defined(HX83102D) + cfg_sz = hx_parse_bin_cfg_data(fw_entry, zf_info_arr, FW_buf, sram_min); +#else + cfg_sz = hx_parse_bin_cfg_data(fw_entry, zf_info_arr, FW_buf, sram_min, &ovl_sidx); +#endif + I("Now cfg_sz=%d\n", cfg_sz); + if (cfg_sz < 0) { + ret = LENGTH_FAIL; + goto END_FUNC; + } else { + cfg_crc_sw = g_core_fp.fp_Calculate_CRC_with_AP(FW_buf, 0, cfg_sz); + I("Now cfg_crc_sw=%X\n", cfg_crc_sw); + } + /*Fw Entity*/ + memcpy(buf, &fw_entry->data[0 * 0x10 + HX64K], 16); + memcpy(zf_info_arr[0].sram_addr, buf, 4); + zf_info_arr[0].write_size = buf[7] << 24 | buf[6] << 16 | buf[5] << 8 | buf[4]; + zf_info_arr[0].fw_addr = buf[11] << 24 | buf[10] << 16 | buf[9] << 8 | buf[8]; + + /* 4. write to sram*/ + if (G_POWERONOF == 1) { + /* FW entity */ + if (himax_sram_write_crc_check(fw_entry, zf_info_arr[0].sram_addr, zf_info_arr[0].fw_addr, zf_info_arr[0].write_size) != 0) + E("%s, HW CRC FAIL\n", __func__); + else + I("%s, HW CRC PASS\n", __func__); + + I("Now sram_min[0]=0x%02X,[1]=0x%02X,[2]=0x%02X,[3]=0x%02X\n", sram_min[0], sram_min[1], sram_min[2], sram_min[3]); + do { + g_core_fp.fp_register_write(sram_min, cfg_sz, FW_buf, 0); + cfg_crc_hw = g_core_fp.fp_check_CRC(sram_min, cfg_sz); + if (cfg_crc_hw != cfg_crc_sw) + E("Config CRC FAIL, HW CRC = %X,SW CRC = %X, retry = %d\n", cfg_crc_hw, cfg_crc_sw, retry); + else + I("Config CRC Pass\n"); + + } while (cfg_crc_hw != cfg_crc_sw && retry-- > 0); + +#ifdef HX_CODE_OVERLAY +#if defined(HX83102D) + /* ovl_idx[0] - sorting */ + /* ovl_idx[1] - gesture */ + /* ovl_idx[2] - border */ + + ovl_idx_t = ovl_idx[0]; + send_data[0] = ovl_sorting_reply; + + if (private_ts->in_self_test == 0) { +#if defined(HX_SMART_WAKEUP) + if (private_ts->suspended && private_ts->SMWP_enable) { + ovl_idx_t = ovl_idx[1]; + send_data[0] = ovl_gesture_reply; + } else { + ovl_idx_t = ovl_idx[2]; + send_data[0] = ovl_border_reply; + } +#else + ovl_idx_t = ovl_idx[2]; + send_data[0] = ovl_border_reply; +#endif +#else + /*do nothing when self testing ??*/ + if (private_ts->in_self_test == 0) { +#ifdef HX_SMART_WAKEUP + /* ovl_sidx - mpap */ + /* ovl_sidx+1 - gesture */ + /* ovl_sidx+2 - border */ + if (private_ts->suspended && private_ts->SMWP_enable) + ovl_sidx = ovl_sidx + 1; + else + ovl_sidx = ovl_sidx + 2; +#else + ovl_sidx = ovl_sidx + 2; +#endif +#endif + + if (himax_sram_write_crc_check(fw_entry, zf_info_arr[ovl_sidx].sram_addr, zf_info_arr[ovl_sidx].fw_addr, zf_info_arr[ovl_sidx].write_size) != 0) + E("%s, Overlay HW CRC FAIL\n", __func__); + else + I("%s, Overlay HW CRC PASS\n", __func__); + +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) + /*write back system config*/ + g_core_fp.fp_resend_cmd_func(private_ts->suspended); +#endif + +#if defined(HX83102D) + } + I("%s: prepare upgrade overlay section = %d\n", + __func__, ovl_idx_t); + + if (zf_info_arr[ovl_idx_t].write_size == 0) { + send_data[0] = ovl_fault; + E("%s, WRONG overlay section, plese check FW!\n", + __func__); + } else { + if (himax_sram_write_crc_check(fw_entry, + zf_info_arr[ovl_idx_t].sram_addr, + zf_info_arr[ovl_idx_t].fw_addr, + zf_info_arr[ovl_idx_t].write_size) != 0) { + send_data[0] = ovl_fault; + E("%s, Overlay HW CRC FAIL\n", __func__); + } else { + I("%s, Overlay HW CRC PASS\n", __func__); + } + } + + retry = 0; + + do { + g_core_fp.fp_register_write(tmp_addr, DATA_LEN_4, send_data, 0); + g_core_fp.fp_register_read(tmp_addr, DATA_LEN_4, recv_data, 0); + retry++; + } while ((send_data[3] != recv_data[3] || send_data[2] != recv_data[2] || send_data[1] != recv_data[1] || send_data[0] != recv_data[0]) && retry < HIMAX_REG_RETRY_TIMES); +#else +#if defined(HX_SMART_WAKEUP) + /* write overlay handshaking password */ + if (private_ts->SMWP_enable) { + if (private_ts->suspended) + send_data[0] = ovl_gesture_reply; + else + send_data[0] = ovl_border_reply; + + retry = 0; + + do { + g_core_fp.fp_register_write(tmp_addr, DATA_LEN_4, send_data, 0); + g_core_fp.fp_register_read(tmp_addr, DATA_LEN_4, recv_data, 0); + retry++; + } while ((send_data[3] != recv_data[3] || send_data[2] != recv_data[2] || send_data[1] != recv_data[1] || send_data[0] != recv_data[0]) && retry < HIMAX_REG_RETRY_TIMES); + } +#endif + } +#endif +#endif + } else { + g_core_fp.fp_clean_sram_0f(zf_info_arr[0].sram_addr, zf_info_arr[0].write_size, 2); + } + +END_FUNC: + kfree(FW_buf); +ALOC_FW_BUF_FAIL: + kfree(zf_info_arr); +ALOC_ZF_INFO_ARR_FAIL: + return ret; +} + +void himax_mcu_firmware_update_0f(const struct firmware *fw_entry) +{ + int ret = 0; + uint8_t tmp_data[DATA_LEN_4] = {0x01, 0x00, 0x00, 0x00}; + + I("%s,Entering - total FW size=%d\n", __func__, (int)fw_entry->size); + + g_core_fp.fp_register_write(pzf_op->addr_system_reset, 4, pzf_op->data_system_reset, 0); + + g_core_fp.fp_sense_off(false); + + if ((int)fw_entry->size > HX64K) { + ret = himax_zf_part_info(fw_entry); + } else { + /* first 48K */ + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_sram_start_addr, 0, HX_48K_SZ) != 0) + E("%s, HW CRC FAIL - Main SRAM 48K\n", __func__); + + /*config info*/ + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_cfg_info, 0xC000, 128) != 0) + E("Config info CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_cfg_info, 128, 2); + } + + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_fw_cfg_1, 0xC0FE, 528) != 0) + E("FW config 1 CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_fw_cfg_1, 528, 1); + } + + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_fw_cfg_3, 0xCA00, 128) != 0) + E("FW config 3 CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_fw_cfg_3, 128, 2); + } + + /*ADC config*/ + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_adc_cfg_1, 0xD640, 1200) != 0) + E("ADC config 1 CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_adc_cfg_1, 1200, 2); + } + + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_adc_cfg_2, 0xD320, 800) != 0) + E("ADC config 2 CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_adc_cfg_2, 800, 2); + } + + /*mapping table*/ + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_map_table, 0xE000, 1536) != 0) + E("Mapping table CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_map_table, 1536, 2); + } + } + + /* set n frame=0*/ + /*if (G_POWERONOF == 1) + * g_core_fp.fp_write_sram_0f(fw_entry, pzf_op->data_mode_switch, 0xC30C, 4); + *else + * g_core_fp.fp_clean_sram_0f(pzf_op->data_mode_switch, 4, 2); + */ + g_core_fp.fp_register_write(pfw_op->addr_set_frame_addr, 4, tmp_data, 0); + + I("%s, End\n", __func__); +} + +int hx_0f_op_file_dirly(char *file_name) +{ + int err = NO_ERR, ret; + const struct firmware *fw_entry = NULL; + + + I("%s, Entering\n", __func__); + I("file name = %s\n", file_name); + ret = request_firmware(&fw_entry, file_name, private_ts->dev); + if (ret < 0) { +#if defined(__EMBEDDED_FW__) + fw_entry = &g_embedded_fw; + I("%s: Couldn't find userspace FW, use embedded FW(size:%zu) instead.\n", __func__, g_embedded_fw.size); +#else + E("%s, fail in line%d error code=%d,file maybe fail\n", __func__, __LINE__, ret); + return ret; +#endif + } + + himax_int_enable(0); + + if (g_f_0f_updat == 1) { + I("%s:[Warning]Other thread is updating now!\n", __func__); + err = -1; + goto END; + } else { + I("%s:Entering Update Flow!\n", __func__); + g_f_0f_updat = 1; + } + + g_core_fp.fp_firmware_update_0f(fw_entry); + if (ret >= 0) + release_firmware(fw_entry); + + g_f_0f_updat = 0; +END: + I("%s, END\n", __func__); + return err; +} + +int himax_mcu_0f_operation_dirly(void) +{ + int err = NO_ERR, ret; + const struct firmware *fw_entry = NULL; + + I("%s, Entering\n", __func__); + I("file name = %s\n", i_CTPM_firmware_name); + ret = request_firmware(&fw_entry, i_CTPM_firmware_name, private_ts->dev); + if (ret < 0) { +#if defined(__EMBEDDED_FW__) + fw_entry = &g_embedded_fw; + I("%s: Couldn't find userspace FW, use embedded FW(size:%zu) instead.\n", __func__, g_embedded_fw.size); +#else + E("%s, fail in line%d error code=%d,file maybe fail\n", __func__, __LINE__, ret); + return ret; +#endif + } + + himax_int_enable(0); + + if (g_f_0f_updat == 1) { + I("%s:[Warning]Other thread is updating now!\n", __func__); + err = -1; + goto END; + } else { + I("%s:Entering Update Flow!\n", __func__); + g_f_0f_updat = 1; + } + + g_core_fp.fp_firmware_update_0f(fw_entry); + if (ret >= 0) + release_firmware(fw_entry); + + g_f_0f_updat = 0; +END: + I("%s, END\n", __func__); + return err; +} + +void himax_mcu_0f_operation(struct work_struct *work) +{ + int err = NO_ERR; + const struct firmware *fw_entry = NULL; + + I("%s, Entering\n", __func__); + I("file name = %s\n", i_CTPM_firmware_name); + err = request_firmware(&fw_entry, i_CTPM_firmware_name, private_ts->dev); + if (err < 0) { +#if defined(__EMBEDDED_FW__) + fw_entry = &g_embedded_fw; + I("%s: Couldn't find userspace FW, use embedded FW(size:%zu) instead.\n", __func__, g_embedded_fw.size); +#else + E("%s, fail in line%d error code=%d,file maybe fail\n", __func__, __LINE__, err); + goto END; +#endif + } + + if (g_f_0f_updat == 1) { + I("%s:[Warning]Other thread is updating now!\n", __func__); + goto END; + } else { + I("%s:Entering Update Flow!\n", __func__); + g_f_0f_updat = 1; + } + + himax_int_enable(0); + + g_core_fp.fp_firmware_update_0f(fw_entry); + if (err >= 0) + release_firmware(fw_entry); + + g_core_fp.fp_reload_disable(0); + g_core_fp.fp_read_FW_ver(); + /*msleep (10);*/ + g_core_fp.fp_touch_information(); + /*msleep (10);*/ + g_core_fp.fp_sense_on(0x00); + /*msleep (10);*/ + I("%s:End\n", __func__); + himax_int_enable(1); + + g_f_0f_updat = 0; +END: + I("%s, END\n", __func__); +} + +static int himax_mcu_0f_esd_check(void) +{ + return NO_ERR; +} + + +#ifdef HX_0F_DEBUG +void himax_mcu_read_sram_0f(const struct firmware *fw_entry, uint8_t *addr, int start_index, int read_len) +{ + int total_read_times = 0; + int max_bus_size = MAX_I2C_TRANS_SZ; + int total_size_temp = 0; + int total_size = 0; + int address = 0; + int i = 0, j = 0; + int not_same = 0; + + uint8_t tmp_addr[4]; + uint8_t *temp_info_data; + int *not_same_buff; + + I("%s, Entering\n", __func__); + + g_core_fp.fp_burst_enable(1); + + total_size = read_len; + + total_size_temp = read_len; + +#if defined(HX_SPI_OPERATION) + if (read_len > 2048) + max_bus_size = 2048; + else + max_bus_size = read_len; +#else + if (read_len > 240) + max_bus_size = 240; + else + max_bus_size = read_len; +#endif + + + temp_info_data = kcalloc(total_size, sizeof(uint8_t), GFP_KERNEL); + not_same_buff = kcalloc(total_size, sizeof(int), GFP_KERNEL); + + + tmp_addr[3] = addr[3]; + tmp_addr[2] = addr[2]; + tmp_addr[1] = addr[1]; + tmp_addr[0] = addr[0]; + I("%s, read addr tmp_addr[3]=0x%2.2X, tmp_addr[2]=0x%2.2X, tmp_addr[1]=0x%2.2X, tmp_addr[0]=0x%2.2X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + + I("%s, total size=%d\n", __func__, total_size); + + g_core_fp.fp_burst_enable(1); + + if (total_size % max_bus_size == 0) + total_read_times = total_size / max_bus_size; + else + total_read_times = total_size / max_bus_size + 1; + + for (i = 0; i < (total_read_times); i++) { + if (total_size_temp >= max_bus_size) { + g_core_fp.fp_register_read(tmp_addr, max_bus_size, &temp_info_data[i*max_bus_size], false); + total_size_temp = total_size_temp - max_bus_size; + } else { + g_core_fp.fp_register_read(tmp_addr, total_size_temp % max_bus_size, &temp_info_data[i*max_bus_size], false); + } + + address = ((i+1) * max_bus_size); + tmp_addr[0] = addr[0] + (uint8_t) ((address) & 0x00FF); + if (tmp_addr[0] < addr[0]) + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF) + 1; + else + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF); + + /*msleep (10);*/ + } + I("%s, READ Start\n", __func__); + I("%s, start_index = %d\n", __func__, start_index); + j = start_index; + for (i = 0; i < read_len; i++, j++) { + if (fw_entry->data[j] != temp_info_data[i]) { + not_same++; + not_same_buff[i] = 1; + } + + I("0x%2.2X, ", temp_info_data[i]); + + if (i > 0 && i%16 == 15) + pr_info("\n"); + + } + I("%s, READ END\n", __func__); + I("%s, Not Same count=%d\n", __func__, not_same); + if (not_same != 0) { + j = start_index; + for (i = 0; i < read_len; i++, j++) { + if (not_same_buff[i] == 1) + I("bin = [%d] 0x%2.2X\n", i, fw_entry->data[j]); + } + for (i = 0; i < read_len; i++, j++) { + if (not_same_buff[i] == 1) + I("sram = [%d] 0x%2.2X\n", i, temp_info_data[i]); + } + } + I("%s, READ END\n", __func__); + I("%s, Not Same count=%d\n", __func__, not_same); + I("%s, END\n", __func__); + + kfree(not_same_buff); + kfree(temp_info_data); +} + +void himax_mcu_read_all_sram(uint8_t *addr, int read_len) +{ + int total_read_times = 0; + int max_bus_size = MAX_I2C_TRANS_SZ; + int total_size_temp = 0; + int total_size = 0; + int address = 0; + int i = 0; + /* struct file *fn; */ + /* struct filename *vts_name; */ + + uint8_t tmp_addr[4]; + uint8_t *temp_info_data; + + I("%s, Entering\n", __func__); + + g_core_fp.fp_burst_enable(1); + + total_size = read_len; + + total_size_temp = read_len; + + temp_info_data = kcalloc(total_size, sizeof(uint8_t), GFP_KERNEL); + + + tmp_addr[3] = addr[3]; + tmp_addr[2] = addr[2]; + tmp_addr[1] = addr[1]; + tmp_addr[0] = addr[0]; + I("%s, read addr tmp_addr[3]=0x%2.2X, tmp_addr[2]=0x%2.2X, tmp_addr[1]=0x%2.2X, tmp_addr[0]=0x%2.2X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + + I("%s, total size=%d\n", __func__, total_size); + + if (total_size % max_bus_size == 0) + total_read_times = total_size / max_bus_size; + else + total_read_times = total_size / max_bus_size + 1; + + for (i = 0; i < (total_read_times); i++) { + if (total_size_temp >= max_bus_size) { + g_core_fp.fp_register_read(tmp_addr, max_bus_size, &temp_info_data[i*max_bus_size], false); + total_size_temp = total_size_temp - max_bus_size; + } else { + g_core_fp.fp_register_read(tmp_addr, total_size_temp % max_bus_size, &temp_info_data[i*max_bus_size], false); + } + + address = ((i+1) * max_bus_size); + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF); + tmp_addr[0] = addr[0] + (uint8_t) ((address) & 0x00FF); + + /*msleep (10);*/ + } + I("%s, NOW addr tmp_addr[3]=0x%2.2X, tmp_addr[2]=0x%2.2X, tmp_addr[1]=0x%2.2X, tmp_addr[0]=0x%2.2X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + /*for(i = 0;i 0 && i%16 == 15) + * printk("\n"); + *} + */ + + /* need modify + * I("Now Write File start!\n"); + * vts_name = kp_getname_kernel("/sdcard/dump_dsram.txt"); + * fn = kp_file_open_name(vts_name, O_CREAT | O_WRONLY, 0); + * if (!IS_ERR (fn)) { + * I("%s create file and ready to write\n", __func__); + * fn->f_op->write (fn, temp_info_data, read_len*sizeof (uint8_t), &fn->f_pos); + * filp_close (fn, NULL); + * } + * I("Now Write File End!\n"); + */ + + I("%s, END\n", __func__); + + kfree(temp_info_data); +} + +void himax_mcu_firmware_read_0f(const struct firmware *fw_entry, int type) +{ + uint8_t tmp_addr[4]; + + I("%s, Entering\n", __func__); + if (type == 0) { /* first 48K */ + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_sram_start_addr, 0, HX_48K_SZ); + g_core_fp.fp_read_all_sram(tmp_addr, 0xC000); + } else { /*last 16k*/ + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_cfg_info, 0xC000, 132); + + /*FW config*/ + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_fw_cfg_1, 0xC0FE, 484); + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_fw_cfg_2, 0xC9DE, 36); + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_fw_cfg_3, 0xCA00, 72); + + /*ADC config*/ + + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_adc_cfg_1, 0xD630, 1188); + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_adc_cfg_2, 0xD318, 792); + + + /*mapping table*/ + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_map_table, 0xE000, 1536); + + /* set n frame=0*/ + g_core_fp.fp_read_sram_0f(fw_entry, pfw_op->addr_set_frame_addr, 0xC30C, 4); + } + + I("%s, END\n", __func__); +} + +void himax_mcu_0f_operation_check(int type) +{ + int err = NO_ERR; + const struct firmware *fw_entry = NULL; + /* char *firmware_name = "himax.bin"; */ + + + I("%s, Entering\n", __func__); + I("file name = %s\n", i_CTPM_firmware_name); + + + err = request_firmware(&fw_entry, i_CTPM_firmware_name, private_ts->dev); + if (err < 0) { +#if defined(__EMBEDDED_FW__) + fw_entry = &g_embedded_fw; + I("%s: Couldn't find userspace FW, use embedded FW(size:%zu) instead.\n", __func__, g_embedded_fw.size); +#else + E("%s, fail in line%d error code=%d\n", __func__, __LINE__, err); + return; +#endif + } + + I("first 4 bytes 0x%2X, 0x%2X, 0x%2X, 0x%2X !\n", fw_entry->data[0], fw_entry->data[1], fw_entry->data[2], fw_entry->data[3]); + I("next 4 bytes 0x%2X, 0x%2X, 0x%2X, 0x%2X !\n", fw_entry->data[4], fw_entry->data[5], fw_entry->data[6], fw_entry->data[7]); + I("and next 4 bytes 0x%2X, 0x%2X, 0x%2X, 0x%2X !\n", fw_entry->data[8], fw_entry->data[9], fw_entry->data[10], fw_entry->data[11]); + + g_core_fp.fp_firmware_read_0f(fw_entry, type); + + if (err >= 0) + release_firmware(fw_entry); + I("%s, END\n", __func__); +} +#endif + +#ifdef HX_CODE_OVERLAY +int himax_mcu_0f_overlay(int ovl_type, int mode) +{ + return NO_ERR; +} +#endif + +#endif + + +#ifdef HIMAX_PALM_SENSOR_EN +int himax_mcu_palm_detection(int mode) +{ + uint8_t data_byte[DATA_LEN_4]; + uint8_t tmp_addr[4]; + I("%s: set palm detection mode to %d", __func__, mode); + if (mode) + himax_in_parse_assign_cmd(palm_detection_register_enable, data_byte, 4); + else + himax_in_parse_assign_cmd(palm_detection_register_disable, data_byte, 4); + + g_core_fp.fp_register_write(pfw_op->addr_palm_detection_enable, DATA_LEN_4, data_byte, 0); + g_core_fp.fp_register_read(pfw_op->addr_palm_detection_enable, DATA_LEN_4, tmp_addr, 0); + I("%s: reg=0x%02X%02X%02X%02X.", __func__, tmp_addr[0],tmp_addr[1],tmp_addr[2],tmp_addr[3]); + + return 0; +} +#endif + +#ifdef CORE_INIT +/* init start */ +static void himax_mcu_fp_init(void) +{ +#ifdef CORE_IC + g_core_fp.fp_burst_enable = himax_mcu_burst_enable; + g_core_fp.fp_register_read = himax_mcu_register_read; + /*g_core_fp.fp_flash_write_burst = himax_mcu_flash_write_burst;*/ + /*g_core_fp.fp_flash_write_burst_lenth = himax_mcu_flash_write_burst_lenth;*/ + g_core_fp.fp_register_write = himax_mcu_register_write; + g_core_fp.fp_interface_on = himax_mcu_interface_on; + g_core_fp.fp_sense_on = himax_mcu_sense_on; + g_core_fp.fp_sense_off = himax_mcu_sense_off; + g_core_fp.fp_wait_wip = himax_mcu_wait_wip; + g_core_fp.fp_init_psl = himax_mcu_init_psl; + g_core_fp.fp_resume_ic_action = himax_mcu_resume_ic_action; + g_core_fp.fp_suspend_ic_action = himax_mcu_suspend_ic_action; + g_core_fp.fp_power_on_init = himax_mcu_power_on_init; + g_core_fp.fp_dd_clk_set = himax_mcu_dd_clk_set; + g_core_fp.fp_dd_reg_en = himax_mcu_dd_reg_en; + g_core_fp.fp_ic_id_read = himax_mcu_ic_id_read; + g_core_fp.fp_dd_reg_write = himax_mcu_dd_reg_write; + g_core_fp.fp_dd_reg_read = himax_mcu_dd_reg_read; +#endif +#ifdef CORE_FW + g_core_fp.fp_system_reset = himax_mcu_system_reset; + g_core_fp.fp_Calculate_CRC_with_AP = himax_mcu_Calculate_CRC_with_AP; + g_core_fp.fp_check_CRC = himax_mcu_check_CRC; + g_core_fp.fp_set_reload_cmd = himax_mcu_set_reload_cmd; + g_core_fp.fp_program_reload = himax_mcu_program_reload; +#ifdef HX_P_SENSOR + g_core_fp.fp_ulpm_in = himax_mcu_ulpm_in; + g_core_fp.fp_black_gest_ctrl = himax_mcu_black_gest_ctrl; +#endif + g_core_fp.fp_set_SMWP_enable = himax_mcu_set_SMWP_enable; +#ifdef HX_EDGE_LIMIT + g_core_fp.fp_set_edge_limit_enable = himax_mcu_set_edge_limit_enable; +#endif + g_core_fp.fp_set_HSEN_enable = himax_mcu_set_HSEN_enable; + g_core_fp.fp_usb_detect_set = himax_mcu_usb_detect_set; + g_core_fp.fp_diag_register_set = himax_mcu_diag_register_set; + g_core_fp.fp_chip_self_test = himax_mcu_chip_self_test; + g_core_fp.fp_idle_mode = himax_mcu_idle_mode; + g_core_fp.fp_reload_disable = himax_mcu_reload_disable; + g_core_fp.fp_check_chip_version = himax_mcu_check_chip_version; + g_core_fp.fp_read_ic_trigger_type = himax_mcu_read_ic_trigger_type; + g_core_fp.fp_read_i2c_status = himax_mcu_read_i2c_status; + g_core_fp.fp_read_FW_ver = himax_mcu_read_FW_ver; + g_core_fp.fp_read_event_stack = himax_mcu_read_event_stack; + g_core_fp.fp_return_event_stack = himax_mcu_return_event_stack; + g_core_fp.fp_calculateChecksum = himax_mcu_calculateChecksum; + g_core_fp.fp_read_FW_status = himax_mcu_read_FW_status; + g_core_fp.fp_irq_switch = himax_mcu_irq_switch; + g_core_fp.fp_assign_sorting_mode = himax_mcu_assign_sorting_mode; + g_core_fp.fp_check_sorting_mode = himax_mcu_check_sorting_mode; + g_core_fp.fp_switch_mode = himax_mcu_switch_mode; + g_core_fp.fp_get_max_dc = himax_mcu_get_max_dc; + g_core_fp.fp_read_DD_status = himax_mcu_read_DD_status; +#if defined(HX_AUTO_UPDATE_FW) + g_core_fp._diff_overlay_bin = hx_mcu_diff_overlay_bin; +#endif +#endif +#ifdef CORE_FLASH + g_core_fp.fp_chip_erase = himax_mcu_chip_erase; + g_core_fp.fp_block_erase = himax_mcu_block_erase; + g_core_fp.fp_sector_erase = himax_mcu_sector_erase; + g_core_fp.fp_flash_programming = himax_mcu_flash_programming; + g_core_fp.fp_flash_page_write = himax_mcu_flash_page_write; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_32k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_32k; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_60k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_60k; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_64k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_64k; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_124k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_124k; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_128k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_128k; + g_core_fp.fp_flash_dump_func = himax_mcu_flash_dump_func; + g_core_fp.fp_flash_lastdata_check = himax_mcu_flash_lastdata_check; + g_core_fp._diff_overlay_flash = hx_mcu_diff_overlay_flash; +#endif +#ifdef CORE_SRAM + g_core_fp.fp_sram_write = himax_mcu_sram_write; + g_core_fp.fp_sram_verify = himax_mcu_sram_verify; + g_core_fp.fp_get_DSRAM_data = himax_mcu_get_DSRAM_data; +#endif +#ifdef CORE_DRIVER + g_core_fp.fp_chip_init = himax_mcu_init_ic; +#ifdef HX_AUTO_UPDATE_FW + g_core_fp.fp_fw_ver_bin = himax_mcu_fw_ver_bin; +#endif +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_pin_reset = himax_mcu_pin_reset; + g_core_fp.fp_ic_reset = himax_mcu_ic_reset; +#endif + g_core_fp.fp_touch_information = himax_mcu_touch_information; + g_core_fp.fp_reload_config = himax_mcu_reload_config; + g_core_fp.fp_get_touch_data_size = himax_mcu_get_touch_data_size; + g_core_fp.fp_hand_shaking = himax_mcu_hand_shaking; + g_core_fp.fp_determin_diag_rawdata = himax_mcu_determin_diag_rawdata; + g_core_fp.fp_determin_diag_storage = himax_mcu_determin_diag_storage; + g_core_fp.fp_cal_data_len = himax_mcu_cal_data_len; + g_core_fp.fp_diag_check_sum = himax_mcu_diag_check_sum; + g_core_fp.fp_diag_parse_raw_data = himax_mcu_diag_parse_raw_data; +#ifdef HX_ESD_RECOVERY + g_core_fp.fp_ic_esd_recovery = himax_mcu_ic_esd_recovery; + g_core_fp.fp_esd_ic_reset = himax_mcu_esd_ic_reset; +#endif +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) + g_core_fp.fp_resend_cmd_func = himax_mcu_resend_cmd_func; +#endif +#if defined(HX_TP_PROC_GUEST_INFO) + g_core_fp.guest_info_get_status = himax_guest_info_get_status; + g_core_fp.read_guest_info = hx_read_guest_info; +#endif +#endif +#ifdef HX_ZERO_FLASH + g_core_fp.fp_reload_disable = hx_dis_rload_0f; + g_core_fp.fp_clean_sram_0f = himax_mcu_clean_sram_0f; + g_core_fp.fp_write_sram_0f = himax_mcu_write_sram_0f; + g_core_fp.fp_write_sram_0f_crc = himax_sram_write_crc_check; + g_core_fp.fp_firmware_update_0f = himax_mcu_firmware_update_0f; + g_core_fp.fp_0f_operation = himax_mcu_0f_operation; + g_core_fp.fp_0f_operation_dirly = himax_mcu_0f_operation_dirly; + g_core_fp.fp_0f_op_file_dirly = hx_0f_op_file_dirly; + g_core_fp.fp_0f_esd_check = himax_mcu_0f_esd_check; +#ifdef HX_0F_DEBUG + g_core_fp.fp_read_sram_0f = himax_mcu_read_sram_0f; + g_core_fp.fp_read_all_sram = himax_mcu_read_all_sram; + g_core_fp.fp_firmware_read_0f = himax_mcu_firmware_read_0f; + g_core_fp.fp_0f_operation_check = himax_mcu_0f_operation_check; +#endif +#ifdef HX_CODE_OVERLAY + g_core_fp.fp_0f_overlay = himax_mcu_0f_overlay; +#endif +#ifdef HIMAX_PALM_SENSOR_EN + g_core_fp.fp_palm_detection_function = himax_mcu_palm_detection; +#endif +#endif +} + +int himax_mcu_in_cmd_struct_init(void) +{ + int err = 0; + + I("%s: Entering!\n", __func__); + himax_mcu_cmd_struct_free = himax_mcu_in_cmd_struct_free; + + g_core_cmd_op = kzalloc(sizeof(struct himax_core_command_operation), GFP_KERNEL); + if (g_core_cmd_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_fail; + } + + g_core_cmd_op->ic_op = kzalloc(sizeof(struct ic_operation), GFP_KERNEL); + if (g_core_cmd_op->ic_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_ic_op_fail; + } + + g_core_cmd_op->fw_op = kzalloc(sizeof(struct fw_operation), GFP_KERNEL); + if (g_core_cmd_op->fw_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_fw_op_fail; + } + + g_core_cmd_op->flash_op = kzalloc(sizeof(struct flash_operation), GFP_KERNEL); + if (g_core_cmd_op->flash_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_flash_op_fail; + } + + g_core_cmd_op->sram_op = kzalloc(sizeof(struct sram_operation), GFP_KERNEL); + if (g_core_cmd_op->sram_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_sram_op_fail; + } + + g_core_cmd_op->driver_op = kzalloc(sizeof(struct driver_operation), GFP_KERNEL); + if (g_core_cmd_op->driver_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_driver_op_fail; + } + + pic_op = g_core_cmd_op->ic_op; + pfw_op = g_core_cmd_op->fw_op; + pflash_op = g_core_cmd_op->flash_op; + psram_op = g_core_cmd_op->sram_op; + pdriver_op = g_core_cmd_op->driver_op; +#ifdef HX_ZERO_FLASH + g_core_cmd_op->zf_op = kzalloc(sizeof(struct zf_operation), GFP_KERNEL); + if (g_core_cmd_op->zf_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_zf_op_fail; + } + pzf_op = g_core_cmd_op->zf_op; + +#endif +#ifndef __MTK_SPI__ + g_internal_buffer = kzalloc(sizeof(uint8_t)*(1024 * 48 + ADDR_LEN_4), GFP_KERNEL); +#else + g_internal_buffer = kzalloc(sizeof(uint8_t)*(HX_SPI_MTK_MAX_WRITE_SZ), GFP_KERNEL); +#endif + if (g_internal_buffer == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_g_internal_buffer_fail; + } + himax_mcu_fp_init(); + + return NO_ERR; + +err_g_core_cmd_op_g_internal_buffer_fail: +#ifdef HX_ZERO_FLASH + kfree(g_core_cmd_op->zf_op); + g_core_cmd_op->zf_op = NULL; +err_g_core_cmd_op_zf_op_fail: +#endif + kfree(g_core_cmd_op->driver_op); + g_core_cmd_op->driver_op = NULL; +err_g_core_cmd_op_driver_op_fail: + kfree(g_core_cmd_op->sram_op); + g_core_cmd_op->sram_op = NULL; +err_g_core_cmd_op_sram_op_fail: + kfree(g_core_cmd_op->flash_op); + g_core_cmd_op->flash_op = NULL; +err_g_core_cmd_op_flash_op_fail: + kfree(g_core_cmd_op->fw_op); + g_core_cmd_op->fw_op = NULL; +err_g_core_cmd_op_fw_op_fail: + kfree(g_core_cmd_op->ic_op); + g_core_cmd_op->ic_op = NULL; +err_g_core_cmd_op_ic_op_fail: + kfree(g_core_cmd_op); + g_core_cmd_op = NULL; +err_g_core_cmd_op_fail: + + return err; +} +EXPORT_SYMBOL(himax_mcu_in_cmd_struct_init); + +void himax_mcu_in_cmd_struct_free(void) +{ + pic_op = NULL; + pfw_op = NULL; + pflash_op = NULL; + psram_op = NULL; + pdriver_op = NULL; + kfree(g_internal_buffer); + g_internal_buffer = NULL; +#ifdef HX_ZERO_FLASH + kfree(g_core_cmd_op->zf_op); + g_core_cmd_op->zf_op = NULL; +#endif + kfree(g_core_cmd_op->driver_op); + g_core_cmd_op->driver_op = NULL; + kfree(g_core_cmd_op->sram_op); + g_core_cmd_op->sram_op = NULL; + kfree(g_core_cmd_op->flash_op); + g_core_cmd_op->flash_op = NULL; + kfree(g_core_cmd_op->fw_op); + g_core_cmd_op->fw_op = NULL; + kfree(g_core_cmd_op->ic_op); + g_core_cmd_op->ic_op = NULL; + kfree(g_core_cmd_op); + g_core_cmd_op = NULL; +#if defined(HX_EN_DYNAMIC_NAME) + if (i_CTPM_firmware_name != NULL) { + kfree(i_CTPM_firmware_name); + i_CTPM_firmware_name = NULL; + } +#endif + + I("%s: release completed\n", __func__); +} + +void himax_in_parse_assign_cmd(uint32_t addr, uint8_t *cmd, int len) +{ + /*I("%s: Entering!\n", __func__);*/ + switch (len) { + case 1: + cmd[0] = addr; + /*I("%s: cmd[0] = 0x%02X\n", __func__, cmd[0]);*/ + break; + + case 2: + cmd[0] = addr % 0x100; + cmd[1] = (addr >> 8) % 0x100; + /*I("%s: cmd[0] = 0x%02X,cmd[1] = 0x%02X\n", __func__, cmd[0], cmd[1]);*/ + break; + + case 4: + cmd[0] = addr % 0x100; + cmd[1] = (addr >> 8) % 0x100; + cmd[2] = (addr >> 16) % 0x100; + cmd[3] = addr / 0x1000000; + /* I("%s: cmd[0] = 0x%02X,cmd[1] = 0x%02X,cmd[2] = 0x%02X,cmd[3] = 0x%02X\n", */ + /* __func__, cmd[0], cmd[1], cmd[2], cmd[3]);*/ + break; + + default: + E("%s: input length fault,len = %d!\n", __func__, len); + } +} +EXPORT_SYMBOL(himax_in_parse_assign_cmd); + +void himax_mcu_in_cmd_init(void) +{ + I("%s: Entering!\n", __func__); + +#ifdef HIMAX_PALM_SENSOR_EN + himax_in_parse_assign_cmd(palm_detection_register, pfw_op->addr_palm_detection_enable, sizeof(pfw_op->addr_palm_detection_enable)); +#endif +#ifdef CORE_IC + himax_in_parse_assign_cmd(ic_adr_ahb_addr_byte_0, pic_op->addr_ahb_addr_byte_0, sizeof(pic_op->addr_ahb_addr_byte_0)); + himax_in_parse_assign_cmd(ic_adr_ahb_rdata_byte_0, pic_op->addr_ahb_rdata_byte_0, sizeof(pic_op->addr_ahb_rdata_byte_0)); + himax_in_parse_assign_cmd(ic_adr_ahb_access_direction, pic_op->addr_ahb_access_direction, sizeof(pic_op->addr_ahb_access_direction)); + himax_in_parse_assign_cmd(ic_adr_conti, pic_op->addr_conti, sizeof(pic_op->addr_conti)); + himax_in_parse_assign_cmd(ic_adr_incr4, pic_op->addr_incr4, sizeof(pic_op->addr_incr4)); + himax_in_parse_assign_cmd(ic_adr_i2c_psw_lb, pic_op->adr_i2c_psw_lb, sizeof(pic_op->adr_i2c_psw_lb)); + himax_in_parse_assign_cmd(ic_adr_i2c_psw_ub, pic_op->adr_i2c_psw_ub, sizeof(pic_op->adr_i2c_psw_ub)); + himax_in_parse_assign_cmd(ic_cmd_ahb_access_direction_read, pic_op->data_ahb_access_direction_read, sizeof(pic_op->data_ahb_access_direction_read)); + himax_in_parse_assign_cmd(ic_cmd_conti, pic_op->data_conti, sizeof(pic_op->data_conti)); + himax_in_parse_assign_cmd(ic_cmd_incr4, pic_op->data_incr4, sizeof(pic_op->data_incr4)); + himax_in_parse_assign_cmd(ic_cmd_i2c_psw_lb, pic_op->data_i2c_psw_lb, sizeof(pic_op->data_i2c_psw_lb)); + himax_in_parse_assign_cmd(ic_cmd_i2c_psw_ub, pic_op->data_i2c_psw_ub, sizeof(pic_op->data_i2c_psw_ub)); + himax_in_parse_assign_cmd(ic_adr_tcon_on_rst, pic_op->addr_tcon_on_rst, sizeof(pic_op->addr_tcon_on_rst)); + himax_in_parse_assign_cmd(ic_addr_adc_on_rst, pic_op->addr_adc_on_rst, sizeof(pic_op->addr_adc_on_rst)); + himax_in_parse_assign_cmd(ic_adr_psl, pic_op->addr_psl, sizeof(pic_op->addr_psl)); + himax_in_parse_assign_cmd(ic_adr_cs_central_state, pic_op->addr_cs_central_state, sizeof(pic_op->addr_cs_central_state)); + himax_in_parse_assign_cmd(ic_cmd_rst, pic_op->data_rst, sizeof(pic_op->data_rst)); + himax_in_parse_assign_cmd(ic_adr_osc_en, pic_op->adr_osc_en, sizeof(pic_op->adr_osc_en)); + himax_in_parse_assign_cmd(ic_adr_osc_pw, pic_op->adr_osc_pw, sizeof(pic_op->adr_osc_pw)); +#endif +#ifdef CORE_FW + himax_in_parse_assign_cmd(fw_addr_system_reset, pfw_op->addr_system_reset, sizeof(pfw_op->addr_system_reset)); + himax_in_parse_assign_cmd(fw_addr_safe_mode_release_pw, pfw_op->addr_safe_mode_release_pw, sizeof(pfw_op->addr_safe_mode_release_pw)); + himax_in_parse_assign_cmd(fw_addr_ctrl_fw, pfw_op->addr_ctrl_fw_isr, sizeof(pfw_op->addr_ctrl_fw_isr)); + himax_in_parse_assign_cmd(fw_addr_flag_reset_event, pfw_op->addr_flag_reset_event, sizeof(pfw_op->addr_flag_reset_event)); + himax_in_parse_assign_cmd(fw_addr_hsen_enable, pfw_op->addr_hsen_enable, sizeof(pfw_op->addr_hsen_enable)); + himax_in_parse_assign_cmd(fw_addr_smwp_enable, pfw_op->addr_smwp_enable, sizeof(pfw_op->addr_smwp_enable)); + himax_in_parse_assign_cmd(fw_addr_program_reload_from, pfw_op->addr_program_reload_from, sizeof(pfw_op->addr_program_reload_from)); + himax_in_parse_assign_cmd(fw_addr_program_reload_to, pfw_op->addr_program_reload_to, sizeof(pfw_op->addr_program_reload_to)); + himax_in_parse_assign_cmd(fw_addr_program_reload_page_write, pfw_op->addr_program_reload_page_write, sizeof(pfw_op->addr_program_reload_page_write)); + himax_in_parse_assign_cmd(fw_addr_raw_out_sel, pfw_op->addr_raw_out_sel, sizeof(pfw_op->addr_raw_out_sel)); + himax_in_parse_assign_cmd(fw_addr_reload_status, pfw_op->addr_reload_status, sizeof(pfw_op->addr_reload_status)); + himax_in_parse_assign_cmd(fw_addr_reload_crc32_result, pfw_op->addr_reload_crc32_result, sizeof(pfw_op->addr_reload_crc32_result)); + himax_in_parse_assign_cmd(fw_addr_reload_addr_from, pfw_op->addr_reload_addr_from, sizeof(pfw_op->addr_reload_addr_from)); + himax_in_parse_assign_cmd(fw_addr_reload_addr_cmd_beat, pfw_op->addr_reload_addr_cmd_beat, sizeof(pfw_op->addr_reload_addr_cmd_beat)); + himax_in_parse_assign_cmd(fw_addr_selftest_addr_en, pfw_op->addr_selftest_addr_en, sizeof(pfw_op->addr_selftest_addr_en)); + himax_in_parse_assign_cmd(fw_addr_criteria_addr, pfw_op->addr_criteria_addr, sizeof(pfw_op->addr_criteria_addr)); + himax_in_parse_assign_cmd(fw_addr_set_frame_addr, pfw_op->addr_set_frame_addr, sizeof(pfw_op->addr_set_frame_addr)); + himax_in_parse_assign_cmd(fw_addr_selftest_result_addr, pfw_op->addr_selftest_result_addr, sizeof(pfw_op->addr_selftest_result_addr)); + himax_in_parse_assign_cmd(fw_addr_sorting_mode_en, pfw_op->addr_sorting_mode_en, sizeof(pfw_op->addr_sorting_mode_en)); + himax_in_parse_assign_cmd(fw_addr_max_dc, pfw_op->addr_max_dc, sizeof(pfw_op->addr_max_dc)); + himax_in_parse_assign_cmd(fw_addr_fw_mode_status, pfw_op->addr_fw_mode_status, sizeof(pfw_op->addr_fw_mode_status)); + himax_in_parse_assign_cmd(fw_addr_icid_addr, pfw_op->addr_icid_addr, sizeof(pfw_op->addr_icid_addr)); + himax_in_parse_assign_cmd(fw_addr_trigger_addr, pfw_op->addr_trigger_addr, sizeof(pfw_op->addr_trigger_addr)); + himax_in_parse_assign_cmd(fw_addr_fw_ver_addr, pfw_op->addr_fw_ver_addr, sizeof(pfw_op->addr_fw_ver_addr)); + himax_in_parse_assign_cmd(fw_addr_fw_cfg_addr, pfw_op->addr_fw_cfg_addr, sizeof(pfw_op->addr_fw_cfg_addr)); + himax_in_parse_assign_cmd(fw_addr_fw_vendor_addr, pfw_op->addr_fw_vendor_addr, sizeof(pfw_op->addr_fw_vendor_addr)); + himax_in_parse_assign_cmd(fw_addr_cus_info, pfw_op->addr_cus_info, sizeof(pfw_op->addr_cus_info)); + himax_in_parse_assign_cmd(fw_addr_proj_info, pfw_op->addr_proj_info, sizeof(pfw_op->addr_proj_info)); + himax_in_parse_assign_cmd(fw_addr_fw_state_addr, pfw_op->addr_fw_state_addr, sizeof(pfw_op->addr_fw_state_addr)); + himax_in_parse_assign_cmd(fw_addr_fw_dbg_msg_addr, pfw_op->addr_fw_dbg_msg_addr, sizeof(pfw_op->addr_fw_dbg_msg_addr)); + himax_in_parse_assign_cmd(fw_addr_chk_fw_status, pfw_op->addr_chk_fw_status, sizeof(pfw_op->addr_chk_fw_status)); + himax_in_parse_assign_cmd(fw_addr_dd_handshak_addr, pfw_op->addr_dd_handshak_addr, sizeof(pfw_op->addr_dd_handshak_addr)); + himax_in_parse_assign_cmd(fw_addr_dd_data_addr, pfw_op->addr_dd_data_addr, sizeof(pfw_op->addr_dd_data_addr)); + himax_in_parse_assign_cmd(fw_data_system_reset, pfw_op->data_system_reset, sizeof(pfw_op->data_system_reset)); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_active, pfw_op->data_safe_mode_release_pw_active, sizeof(pfw_op->data_safe_mode_release_pw_active)); + himax_in_parse_assign_cmd(fw_data_clear, pfw_op->data_clear, sizeof(pfw_op->data_clear)); + himax_in_parse_assign_cmd(fw_data_clear, pfw_op->data_clear, sizeof(pfw_op->data_clear)); + himax_in_parse_assign_cmd(fw_data_fw_stop, pfw_op->data_fw_stop, sizeof(pfw_op->data_fw_stop)); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, pfw_op->data_safe_mode_release_pw_reset, sizeof(pfw_op->data_safe_mode_release_pw_reset)); + himax_in_parse_assign_cmd(fw_data_program_reload_start, pfw_op->data_program_reload_start, sizeof(pfw_op->data_program_reload_start)); + himax_in_parse_assign_cmd(fw_data_program_reload_compare, pfw_op->data_program_reload_compare, sizeof(pfw_op->data_program_reload_compare)); + himax_in_parse_assign_cmd(fw_data_program_reload_break, pfw_op->data_program_reload_break, sizeof(pfw_op->data_program_reload_break)); + himax_in_parse_assign_cmd(fw_data_selftest_request, pfw_op->data_selftest_request, sizeof(pfw_op->data_selftest_request)); + himax_in_parse_assign_cmd(fw_data_criteria_aa_top, pfw_op->data_criteria_aa_top, sizeof(pfw_op->data_criteria_aa_top)); + himax_in_parse_assign_cmd(fw_data_criteria_aa_bot, pfw_op->data_criteria_aa_bot, sizeof(pfw_op->data_criteria_aa_bot)); + himax_in_parse_assign_cmd(fw_data_criteria_key_top, pfw_op->data_criteria_key_top, sizeof(pfw_op->data_criteria_key_top)); + himax_in_parse_assign_cmd(fw_data_criteria_key_bot, pfw_op->data_criteria_key_bot, sizeof(pfw_op->data_criteria_key_bot)); + himax_in_parse_assign_cmd(fw_data_criteria_avg_top, pfw_op->data_criteria_avg_top, sizeof(pfw_op->data_criteria_avg_top)); + himax_in_parse_assign_cmd(fw_data_criteria_avg_bot, pfw_op->data_criteria_avg_bot, sizeof(pfw_op->data_criteria_avg_bot)); + himax_in_parse_assign_cmd(fw_data_set_frame, pfw_op->data_set_frame, sizeof(pfw_op->data_set_frame)); + himax_in_parse_assign_cmd(fw_data_selftest_ack_hb, pfw_op->data_selftest_ack_hb, sizeof(pfw_op->data_selftest_ack_hb)); + himax_in_parse_assign_cmd(fw_data_selftest_ack_lb, pfw_op->data_selftest_ack_lb, sizeof(pfw_op->data_selftest_ack_lb)); + himax_in_parse_assign_cmd(fw_data_selftest_pass, pfw_op->data_selftest_pass, sizeof(pfw_op->data_selftest_pass)); + himax_in_parse_assign_cmd(fw_data_normal_cmd, pfw_op->data_normal_cmd, sizeof(pfw_op->data_normal_cmd)); + himax_in_parse_assign_cmd(fw_data_normal_status, pfw_op->data_normal_status, sizeof(pfw_op->data_normal_status)); + himax_in_parse_assign_cmd(fw_data_sorting_cmd, pfw_op->data_sorting_cmd, sizeof(pfw_op->data_sorting_cmd)); + himax_in_parse_assign_cmd(fw_data_sorting_status, pfw_op->data_sorting_status, sizeof(pfw_op->data_sorting_status)); + himax_in_parse_assign_cmd(fw_data_dd_request, pfw_op->data_dd_request, sizeof(pfw_op->data_dd_request)); + himax_in_parse_assign_cmd(fw_data_dd_ack, pfw_op->data_dd_ack, sizeof(pfw_op->data_dd_ack)); + himax_in_parse_assign_cmd(fw_data_idle_dis_pwd, pfw_op->data_idle_dis_pwd, sizeof(pfw_op->data_idle_dis_pwd)); + himax_in_parse_assign_cmd(fw_data_idle_en_pwd, pfw_op->data_idle_en_pwd, sizeof(pfw_op->data_idle_en_pwd)); + himax_in_parse_assign_cmd(fw_data_rawdata_ready_hb, pfw_op->data_rawdata_ready_hb, sizeof(pfw_op->data_rawdata_ready_hb)); + himax_in_parse_assign_cmd(fw_data_rawdata_ready_lb, pfw_op->data_rawdata_ready_lb, sizeof(pfw_op->data_rawdata_ready_lb)); + himax_in_parse_assign_cmd(fw_addr_ahb_addr, pfw_op->addr_ahb_addr, sizeof(pfw_op->addr_ahb_addr)); + himax_in_parse_assign_cmd(fw_data_ahb_dis, pfw_op->data_ahb_dis, sizeof(pfw_op->data_ahb_dis)); + himax_in_parse_assign_cmd(fw_data_ahb_en, pfw_op->data_ahb_en, sizeof(pfw_op->data_ahb_en)); + himax_in_parse_assign_cmd(fw_addr_event_addr, pfw_op->addr_event_addr, sizeof(pfw_op->addr_event_addr)); + himax_in_parse_assign_cmd(fw_usb_detect_addr, pfw_op->addr_usb_detect, sizeof(pfw_op->addr_usb_detect)); +#ifdef HX_P_SENSOR + himax_in_parse_assign_cmd(fw_addr_ulpm_33, pfw_op->addr_ulpm_33, sizeof(pfw_op->addr_ulpm_33)); + himax_in_parse_assign_cmd(fw_addr_ulpm_34, pfw_op->addr_ulpm_34, sizeof(pfw_op->addr_ulpm_34)); + himax_in_parse_assign_cmd(fw_data_ulpm_11, pfw_op->data_ulpm_11, sizeof(pfw_op->data_ulpm_11)); + himax_in_parse_assign_cmd(fw_data_ulpm_22, pfw_op->data_ulpm_22, sizeof(pfw_op->data_ulpm_22)); + himax_in_parse_assign_cmd(fw_data_ulpm_33, pfw_op->data_ulpm_33, sizeof(pfw_op->data_ulpm_33)); + himax_in_parse_assign_cmd(fw_data_ulpm_aa, pfw_op->data_ulpm_aa, sizeof(pfw_op->data_ulpm_aa)); +#endif +#endif +#ifdef CORE_FLASH + himax_in_parse_assign_cmd(flash_addr_spi200_trans_fmt, pflash_op->addr_spi200_trans_fmt, sizeof(pflash_op->addr_spi200_trans_fmt)); + himax_in_parse_assign_cmd(flash_addr_spi200_trans_ctrl, pflash_op->addr_spi200_trans_ctrl, sizeof(pflash_op->addr_spi200_trans_ctrl)); + himax_in_parse_assign_cmd(flash_addr_spi200_cmd, pflash_op->addr_spi200_cmd, sizeof(pflash_op->addr_spi200_cmd)); + himax_in_parse_assign_cmd(flash_addr_spi200_addr, pflash_op->addr_spi200_addr, sizeof(pflash_op->addr_spi200_addr)); + himax_in_parse_assign_cmd(flash_addr_spi200_data, pflash_op->addr_spi200_data, sizeof(pflash_op->addr_spi200_data)); + himax_in_parse_assign_cmd(flash_addr_spi200_bt_num, pflash_op->addr_spi200_bt_num, sizeof(pflash_op->addr_spi200_bt_num)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_fmt, pflash_op->data_spi200_trans_fmt, sizeof(pflash_op->data_spi200_trans_fmt)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_1, pflash_op->data_spi200_trans_ctrl_1, sizeof(pflash_op->data_spi200_trans_ctrl_1)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_2, pflash_op->data_spi200_trans_ctrl_2, sizeof(pflash_op->data_spi200_trans_ctrl_2)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_3, pflash_op->data_spi200_trans_ctrl_3, sizeof(pflash_op->data_spi200_trans_ctrl_3)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_4, pflash_op->data_spi200_trans_ctrl_4, sizeof(pflash_op->data_spi200_trans_ctrl_4)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_5, pflash_op->data_spi200_trans_ctrl_5, sizeof(pflash_op->data_spi200_trans_ctrl_5)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_1, pflash_op->data_spi200_cmd_1, sizeof(pflash_op->data_spi200_cmd_1)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_2, pflash_op->data_spi200_cmd_2, sizeof(pflash_op->data_spi200_cmd_2)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_3, pflash_op->data_spi200_cmd_3, sizeof(pflash_op->data_spi200_cmd_3)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_4, pflash_op->data_spi200_cmd_4, sizeof(pflash_op->data_spi200_cmd_4)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_5, pflash_op->data_spi200_cmd_5, sizeof(pflash_op->data_spi200_cmd_5)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_6, pflash_op->data_spi200_cmd_6, sizeof(pflash_op->data_spi200_cmd_6)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_7, pflash_op->data_spi200_cmd_7, sizeof(pflash_op->data_spi200_cmd_7)); + himax_in_parse_assign_cmd(flash_data_spi200_addr, pflash_op->data_spi200_addr, sizeof(pflash_op->data_spi200_addr)); +#endif +#ifdef CORE_SRAM + /* sram start*/ + himax_in_parse_assign_cmd(sram_adr_mkey, psram_op->addr_mkey, sizeof(psram_op->addr_mkey)); + himax_in_parse_assign_cmd(sram_adr_rawdata_addr, psram_op->addr_rawdata_addr, sizeof(psram_op->addr_rawdata_addr)); + himax_in_parse_assign_cmd(sram_adr_rawdata_end, psram_op->addr_rawdata_end, sizeof(psram_op->addr_rawdata_end)); + himax_in_parse_assign_cmd(sram_passwrd_start, psram_op->passwrd_start, sizeof(psram_op->passwrd_start)); + himax_in_parse_assign_cmd(sram_passwrd_end, psram_op->passwrd_end, sizeof(psram_op->passwrd_end)); + /* sram end*/ +#endif +#ifdef CORE_DRIVER + himax_in_parse_assign_cmd(driver_addr_fw_define_flash_reload, pdriver_op->addr_fw_define_flash_reload, sizeof(pdriver_op->addr_fw_define_flash_reload)); + himax_in_parse_assign_cmd(driver_addr_fw_define_2nd_flash_reload, pdriver_op->addr_fw_define_2nd_flash_reload, sizeof(pdriver_op->addr_fw_define_2nd_flash_reload)); + himax_in_parse_assign_cmd(driver_addr_fw_define_int_is_edge, pdriver_op->addr_fw_define_int_is_edge, sizeof(pdriver_op->addr_fw_define_int_is_edge)); + himax_in_parse_assign_cmd(driver_addr_fw_define_rxnum_txnum_maxpt, pdriver_op->addr_fw_define_rxnum_txnum_maxpt, sizeof(pdriver_op->addr_fw_define_rxnum_txnum_maxpt)); + himax_in_parse_assign_cmd(driver_addr_fw_define_xy_res_enable, pdriver_op->addr_fw_define_xy_res_enable, sizeof(pdriver_op->addr_fw_define_xy_res_enable)); + himax_in_parse_assign_cmd(driver_addr_fw_define_x_y_res, pdriver_op->addr_fw_define_x_y_res, sizeof(pdriver_op->addr_fw_define_x_y_res)); + himax_in_parse_assign_cmd(driver_data_df_rx, pdriver_op->data_df_rx, sizeof(pdriver_op->data_df_rx)); + himax_in_parse_assign_cmd(driver_data_df_tx, pdriver_op->data_df_tx, sizeof(pdriver_op->data_df_tx)); + himax_in_parse_assign_cmd(driver_data_df_pt, pdriver_op->data_df_pt, sizeof(pdriver_op->data_df_pt)); + himax_in_parse_assign_cmd(driver_data_df_x_res, pdriver_op->data_df_x_res, sizeof(pdriver_op->data_df_x_res)); + himax_in_parse_assign_cmd(driver_data_df_y_res, pdriver_op->data_df_y_res, sizeof(pdriver_op->data_df_y_res)); + himax_in_parse_assign_cmd(driver_data_fw_define_flash_reload_dis, pdriver_op->data_fw_define_flash_reload_dis, sizeof(pdriver_op->data_fw_define_flash_reload_dis)); + himax_in_parse_assign_cmd(driver_data_fw_define_flash_reload_en, pdriver_op->data_fw_define_flash_reload_en, sizeof(pdriver_op->data_fw_define_flash_reload_en)); + himax_in_parse_assign_cmd(driver_data_fw_define_rxnum_txnum_maxpt_sorting, pdriver_op->data_fw_define_rxnum_txnum_maxpt_sorting, sizeof(pdriver_op->data_fw_define_rxnum_txnum_maxpt_sorting)); + himax_in_parse_assign_cmd(driver_data_fw_define_rxnum_txnum_maxpt_normal, pdriver_op->data_fw_define_rxnum_txnum_maxpt_normal, sizeof(pdriver_op->data_fw_define_rxnum_txnum_maxpt_normal)); +#endif +#ifdef HX_ZERO_FLASH + himax_in_parse_assign_cmd(zf_addr_dis_flash_reload, pzf_op->addr_dis_flash_reload, sizeof(pzf_op->addr_dis_flash_reload)); + himax_in_parse_assign_cmd(zf_data_dis_flash_reload, pzf_op->data_dis_flash_reload, sizeof(pzf_op->data_dis_flash_reload)); + himax_in_parse_assign_cmd(zf_addr_system_reset, pzf_op->addr_system_reset, sizeof(pzf_op->addr_system_reset)); + himax_in_parse_assign_cmd(zf_data_system_reset, pzf_op->data_system_reset, sizeof(pzf_op->data_system_reset)); + himax_in_parse_assign_cmd(zf_data_sram_start_addr, pzf_op->data_sram_start_addr, sizeof(pzf_op->data_sram_start_addr)); + himax_in_parse_assign_cmd(zf_data_sram_clean, pzf_op->data_sram_clean, sizeof(pzf_op->data_sram_clean)); + himax_in_parse_assign_cmd(zf_data_cfg_info, pzf_op->data_cfg_info, sizeof(pzf_op->data_cfg_info)); + himax_in_parse_assign_cmd(zf_data_fw_cfg_1, pzf_op->data_fw_cfg_1, sizeof(pzf_op->data_fw_cfg_1)); + himax_in_parse_assign_cmd(zf_data_fw_cfg_2, pzf_op->data_fw_cfg_2, sizeof(pzf_op->data_fw_cfg_2)); + himax_in_parse_assign_cmd(zf_data_fw_cfg_3, pzf_op->data_fw_cfg_3, sizeof(pzf_op->data_fw_cfg_3)); + himax_in_parse_assign_cmd(zf_data_adc_cfg_1, pzf_op->data_adc_cfg_1, sizeof(pzf_op->data_adc_cfg_1)); + himax_in_parse_assign_cmd(zf_data_adc_cfg_2, pzf_op->data_adc_cfg_2, sizeof(pzf_op->data_adc_cfg_2)); + himax_in_parse_assign_cmd(zf_data_adc_cfg_3, pzf_op->data_adc_cfg_3, sizeof(pzf_op->data_adc_cfg_3)); + himax_in_parse_assign_cmd(zf_data_map_table, pzf_op->data_map_table, sizeof(pzf_op->data_map_table)); + /*himax_in_parse_assign_cmd(zf_data_mode_switch, pzf_op->data_mode_switch, sizeof(pzf_op->data_mode_switch));*/ + himax_in_parse_assign_cmd(zf_addr_sts_chk, pzf_op->addr_sts_chk, sizeof(pzf_op->addr_sts_chk)); + himax_in_parse_assign_cmd(zf_data_activ_sts, pzf_op->data_activ_sts, sizeof(pzf_op->data_activ_sts)); + himax_in_parse_assign_cmd(zf_addr_activ_relod, pzf_op->addr_activ_relod, sizeof(pzf_op->addr_activ_relod)); + himax_in_parse_assign_cmd(zf_data_activ_in, pzf_op->data_activ_in, sizeof(pzf_op->data_activ_in)); +#endif +} +EXPORT_SYMBOL(himax_mcu_in_cmd_init); + +/* init end*/ +#endif diff --git a/drivers/input/touchscreen/himax_v2_mmi/himax_inspection.c b/drivers/input/touchscreen/himax_v2_mmi/himax_inspection.c new file mode 100644 index 000000000000..78b3d6bb745a --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/himax_inspection.c @@ -0,0 +1,2227 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for inspection functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_inspection.h" + +static int g_gap_vertical_partial = 3; +static int *g_gap_vertical_part; +static int g_gap_horizontal_partial = 3; +static int *g_gap_horizontal_part; + +static int g_dc_max; + +static int g_1kind_raw_size; +uint32_t g_rslt_data_len; +int **g_inspection_criteria; +int *g_inspt_crtra_flag; +int HX_CRITERIA_ITEM; +int HX_CRITERIA_SIZE; +char *g_rslt_data; +static char g_file_path[256]; +static char g_rslt_log[256]; +static char g_start_log[256]; +char *hx_self_test_file_name = NULL; + +char *g_himax_inspection_mode[] = { + "HIMAX_INSPECTION_OPEN", + "HIMAX_INSPECTION_MICRO_OPEN", + "HIMAX_INSPECTION_SHORT", + "HIMAX_INSPECTION_RAWDATA", + "HIMAX_INSPECTION_BPN_RAWDATA", + "HIMAX_INSPECTION_WEIGHT_NOISE", + "HIMAX_INSPECTION_ABS_NOISE", + "HIMAX_INSPECTION_SORTING", + "HIMAX_INSPECTION_BACK_NORMAL", + + "HIMAX_INSPECTION_GAPTEST_RAW", + + "HIMAX_INSPECTION_ACT_IDLE_RAWDATA", + "HIMAX_INSPECTION_ACT_IDLE_NOISE", + + "HIMAX_INSPECTION_LPWUG_RAWDATA", + "HIMAX_INSPECTION_LPWUG_NOISE", + "HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA", + "HIMAX_INSPECTION_LPWUG_IDLE_NOISE", + NULL +}; + +/* for criteria */ +char *g_hx_inspt_crtra_name[] = { + "CRITERIA_RAW_MIN", + "CRITERIA_RAW_MAX", + "CRITERIA_RAW_BPN_MIN", + "CRITERIA_RAW_BPN_MAX", + "CRITERIA_SHORT_MIN", + "CRITERIA_SHORT_MAX", + "CRITERIA_OPEN_MIN", + "CRITERIA_OPEN_MAX", + "CRITERIA_MICRO_OPEN_MIN", + "CRITERIA_MICRO_OPEN_MAX", + "CRITERIA_NOISE_WT_MIN", + "CRITERIA_NOISE_WT_MAX", + "CRITERIA_NOISE_ABS_MIN", + "CRITERIA_NOISE_ABS_MAX", + "CRITERIA_SORT_MIN", + "CRITERIA_SORT_MAX", + + "CRITERIA_GAP_RAW_HOR_MIN", + "CRITERIA_GAP_RAW_HOR_MAX", + "CRITERIA_GAP_RAW_VER_MIN", + "CRITERIA_GAP_RAW_VER_MAX", + + "ACT_IDLE_NOISE_MIN", + "ACT_IDLE_NOISE_MAX", + "ACT_IDLE_RAWDATA_MIN", + "ACT_IDLE_RAWDATA_MAX", + + "LPWUG_NOISE_MIN", + "LPWUG_NOISE_MAX", + "LPWUG_RAWDATA_MIN", + "LPWUG_RAWDATA_MAX", + "LPWUG_IDLE_NOISE_MIN", + "LPWUG_IDLE_NOISE_MAX", + "LPWUG_IDLE_RAWDATA_MIN", + "LPWUG_IDLE_RAWDATA_MAX", + NULL +}; + + +void (*fp_himax_self_test_init)(void) = himax_inspection_init; + + +static void himax_press_powerkey(void) +{ + I(" %s POWER KEY event %x press\n", __func__, KEY_POWER); + input_report_key(private_ts->input_dev, KEY_POWER, 1); + input_sync(private_ts->input_dev); + + msleep(100); + + I(" %s POWER KEY event %x release\n", __func__, KEY_POWER); + input_report_key(private_ts->input_dev, KEY_POWER, 0); + input_sync(private_ts->input_dev); +} + + +static uint8_t NOISEMAX; +static uint8_t g_recal_thx; + +static int hx_test_data_pop_out(char *rslt_buf, char *filepath) +{ + struct file *raw_file = NULL; + struct filename *vts_name = NULL; + mm_segment_t fs; + loff_t pos = 0; + int ret_val = NO_ERR; + + I("%s: Entering!\n", __func__); + I("data size=0x%04X\n", (uint32_t)strlen(rslt_buf)); + + if (!kp_getname_kernel) { + E("kp_getname_kernel is NULL, not open file!\n"); + return -EIO; + } + vts_name = kp_getname_kernel(filepath); + + if (raw_file == NULL) + raw_file = kp_file_open_name(vts_name, O_TRUNC|O_CREAT|O_RDWR, + 0660); + + if (IS_ERR(raw_file)) { + E("%s open file failed = %ld\n", __func__, PTR_ERR(raw_file)); + ret_val = -EIO; + goto SAVE_DATA_ERR; + } + + fs = get_fs(); + set_fs(get_ds()); + vfs_write(raw_file, rslt_buf, + g_1kind_raw_size * HX_CRITERIA_ITEM * sizeof(char), &pos); + if (raw_file != NULL) + filp_close(raw_file, NULL); + + set_fs(fs); + +SAVE_DATA_ERR: + I("%s: End!\n", __func__); + return ret_val; +} + +static int hx_test_data_get(uint32_t RAW[], char *start_log, char *result, + int now_item) +{ + uint32_t i; + + ssize_t len = 0; + char *testdata = NULL; + uint32_t SZ_SIZE = g_1kind_raw_size; + + I("%s: Entering, Now type=%s!\n", __func__, + g_himax_inspection_mode[now_item]); + + testdata = kzalloc(sizeof(char) * SZ_SIZE, GFP_KERNEL); + if (testdata == NULL) { + E("%s: Memory allocation falied!\n", __func__); + return MEM_ALLOC_FAIL; + } + + len += snprintf((testdata + len), SZ_SIZE - len, "%s", start_log); + for (i = 0; i < ic_data->HX_TX_NUM*ic_data->HX_RX_NUM; i++) { + if (i > 1 && ((i + 1) % ic_data->HX_RX_NUM) == 0) + len += snprintf((testdata + len), SZ_SIZE - len, + "%5d,\n", RAW[i]); + else + len += snprintf((testdata + len), SZ_SIZE - len, + "%5d,", RAW[i]); + } + len += snprintf((testdata + len), SZ_SIZE - len, "\n%s", result); + + memcpy(&g_rslt_data[g_rslt_data_len], testdata, len); + g_rslt_data_len += len; + I("%s: g_rslt_data_len=%d!\n", __func__, g_rslt_data_len); + + /*memcpy(&g_rslt_data[now_item * SZ_SIZE], testdata, SZ_SIZE);*/ + + /* dbg */ + /* for(i = 0; i < SZ_SIZE; i++) + * { + * I("0x%04X, ", g_rslt_data[i + (now_item * SZ_SIZE)]); + * if(i > 0 && (i % 16 == 15)) + * PI("\n"); + * } + */ + + kfree(testdata); + I("%s: End!\n", __func__); + return NO_ERR; + +} + +static int himax_switch_mode_inspection(int mode) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + + I("%s: Entering\n", __func__); + + /*Stop Handshaking*/ + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; + tmp_addr[0] = 0x00; tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = 0x00; tmp_data[0] = 0x00; + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); + + /*Swtich Mode*/ + switch (mode) { + case HIMAX_INSPECTION_SORTING: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_SORTING_START; + tmp_data[0] = PWD_SORTING_START; + break; + case HIMAX_INSPECTION_OPEN: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_OPEN_START; + tmp_data[0] = PWD_OPEN_START; + break; + case HIMAX_INSPECTION_MICRO_OPEN: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_OPEN_START; + tmp_data[0] = PWD_OPEN_START; + break; + case HIMAX_INSPECTION_SHORT: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_SHORT_START; + tmp_data[0] = PWD_SHORT_START; + break; + + case HIMAX_INSPECTION_GAPTEST_RAW: + case HIMAX_INSPECTION_RAWDATA: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_RAWDATA_START; + tmp_data[0] = PWD_RAWDATA_START; + break; + case HIMAX_INSPECTION_BPN_RAWDATA: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_RAWDATA_START; + tmp_data[0] = PWD_RAWDATA_START; + break; + case HIMAX_INSPECTION_WEIGHT_NOISE: + case HIMAX_INSPECTION_ABS_NOISE: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_NOISE_START; + tmp_data[0] = PWD_NOISE_START; + break; + + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_ACT_IDLE_START; + tmp_data[0] = PWD_ACT_IDLE_START; + break; + + case HIMAX_INSPECTION_LPWUG_RAWDATA: + case HIMAX_INSPECTION_LPWUG_NOISE: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_LPWUG_START; + tmp_data[0] = PWD_LPWUG_START; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_LPWUG_IDLE_START; + tmp_data[0] = PWD_LPWUG_IDLE_START; + break; + + default: + I("%s,Nothing to be done!\n", __func__); + break; + } + + if (g_core_fp.fp_assign_sorting_mode != NULL) + g_core_fp.fp_assign_sorting_mode(tmp_data); + I("%s: End of setting!\n", __func__); + + return 0; + +} + +static int himax_get_rawdata(uint32_t RAW[], uint32_t datalen) +{ + uint8_t *tmp_rawdata; + bool get_raw_rlst; + uint8_t retry = 0; + uint32_t i = 0; + uint32_t j = 0; + uint32_t index = 0; + uint32_t Min_DATA = 0xFFFFFFFF; + uint32_t Max_DATA = 0x00000000; + + /* We use two bytes to combine a value of rawdata.*/ + tmp_rawdata = kzalloc(sizeof(uint8_t) * (datalen * 2), GFP_KERNEL); + if (tmp_rawdata == NULL) { + E("%s: Memory allocation falied!\n", __func__); + return MEM_ALLOC_FAIL; + } + + while (retry < 200) { + get_raw_rlst = g_core_fp.fp_get_DSRAM_data(tmp_rawdata, false); + if (get_raw_rlst) + break; + retry++; + } + + if (retry >= 200) + goto DIRECT_END; + + /* Copy Data*/ + for (i = 0; i < ic_data->HX_TX_NUM*ic_data->HX_RX_NUM; i++) + RAW[i] = tmp_rawdata[(i * 2) + 1] * 256 + tmp_rawdata[(i * 2)]; + /* don't output these logs, reduce factory test time + for (j = 0; j < ic_data->HX_RX_NUM; j++) { + if (j == 0) + PI(" RX%2d", j + 1); + else + PI(" RX%2d", j + 1); + } + PI("\n"); + ******* delete these logs *********************/ + + for (i = 0; i < ic_data->HX_TX_NUM; i++) { + /*PI("TX%2d", i + 1);*/ + for (j = 0; j < ic_data->HX_RX_NUM; j++) { + PI("%5d ", RAW[index]); + if (RAW[index] > Max_DATA) + Max_DATA = RAW[index]; + + if (RAW[index] < Min_DATA) + Min_DATA = RAW[index]; + + index++; + } + /*PI("\n");*/ + } + I("Max = %5d, Min = %5d\n", Max_DATA, Min_DATA); +DIRECT_END: + kfree(tmp_rawdata); + + if (get_raw_rlst) + return HX_INSPECT_OK; + else + return HX_INSPECT_EGETRAW; + +} + +static void himax_switch_data_type(uint8_t checktype) +{ + uint8_t datatype = 0x00; + + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + datatype = DATA_SORTING; + break; + case HIMAX_INSPECTION_OPEN: + datatype = DATA_OPEN; + break; + case HIMAX_INSPECTION_MICRO_OPEN: + datatype = DATA_MICRO_OPEN; + break; + case HIMAX_INSPECTION_SHORT: + datatype = DATA_SHORT; + break; + case HIMAX_INSPECTION_RAWDATA: + datatype = DATA_RAWDATA; + break; + case HIMAX_INSPECTION_BPN_RAWDATA: + datatype = DATA_RAWDATA; + break; + case HIMAX_INSPECTION_WEIGHT_NOISE: + case HIMAX_INSPECTION_ABS_NOISE: + datatype = DATA_NOISE; + break; + case HIMAX_INSPECTION_BACK_NORMAL: + datatype = DATA_BACK_NORMAL; + break; + + case HIMAX_INSPECTION_GAPTEST_RAW: + datatype = DATA_RAWDATA; + break; + + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + datatype = DATA_ACT_IDLE_RAWDATA; + break; + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + datatype = DATA_ACT_IDLE_NOISE; + break; + + case HIMAX_INSPECTION_LPWUG_RAWDATA: + datatype = DATA_LPWUG_RAWDATA; + break; + case HIMAX_INSPECTION_LPWUG_NOISE: + datatype = DATA_LPWUG_NOISE; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + datatype = DATA_LPWUG_IDLE_RAWDATA; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + datatype = DATA_LPWUG_IDLE_NOISE; + break; + + default: + E("Wrong type=%d\n", checktype); + break; + } + g_core_fp.fp_diag_register_set(datatype, 0x00); +} + +static void himax_bank_search_set(uint16_t Nframe, uint8_t checktype) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + + /*skip frame*/ + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x70; tmp_addr[0] = 0xF4; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + + switch (checktype) { + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + tmp_data[0] = BS_ACT_IDLE; + break; + case HIMAX_INSPECTION_LPWUG_RAWDATA: + case HIMAX_INSPECTION_LPWUG_NOISE: + tmp_data[0] = BS_LPWUG; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + tmp_data[0] = BS_LPWUG_dile; + break; + case HIMAX_INSPECTION_RAWDATA: + case HIMAX_INSPECTION_BPN_RAWDATA: + tmp_data[0] = BS_RAWDATA; + break; + case HIMAX_INSPECTION_WEIGHT_NOISE: + case HIMAX_INSPECTION_ABS_NOISE: + tmp_data[0] = BS_NOISE; + break; + default: + tmp_data[0] = BS_OPENSHORT; + break; + } + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); +} + +static void himax_neg_noise_sup(uint8_t *data) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + + /*0x10007FD8 Check support negative value or not */ + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x7F; tmp_addr[0] = 0xD8; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + + if ((tmp_data[3] & 0x04) == 0x04) { + data[2] = 0x0C; data[3] = 0x7F; + } else + I("%s Not support negative noise\n", __func__); +} + +static void himax_set_N_frame(uint16_t Nframe, uint8_t checktype) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + + himax_bank_search_set(Nframe, checktype); + + /*IIR MAX*/ + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; tmp_addr[1] = 0x72; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = (uint8_t)((Nframe & 0xFF00) >> 8); + tmp_data[0] = (uint8_t)(Nframe & 0x00FF); + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); + + if (checktype == HIMAX_INSPECTION_WEIGHT_NOISE || + checktype == HIMAX_INSPECTION_ABS_NOISE) + himax_neg_noise_sup(tmp_data); + + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); +} + +static void himax_get_noise_base(void)/*Normal Threshold*/ +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x70; tmp_addr[0] = 0x8C;/*0x1000708F*/ + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + + NOISEMAX = tmp_data[3]; + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x70; tmp_addr[0] = 0x90;/*0x10007092*/ + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + + g_recal_thx = tmp_data[2]; + I("%s: NOISEMAX=%d, g_recal_thx = %d\n", __func__, + NOISEMAX, g_recal_thx); +} + +static uint16_t himax_get_palm_num(void)/*Palm Number*/ +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + uint16_t palm_num; + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x70; tmp_addr[0] = 0xA8;/*0x100070AB*/ + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + palm_num = tmp_data[3]; + I("%s: palm_num = %d ", __func__, palm_num); + + return palm_num; +} + +static int himax_get_noise_weight_test(void) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + uint16_t weight = 0; + uint16_t value = 0; + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x72; tmp_addr[0] = 0xC8;/*0x100072C8 weighting value*/ + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + if (tmp_data[3] != 0x72 || tmp_data[2] != 0xC8) + return FW_NOT_READY; + + value = (tmp_data[1] << 8) | tmp_data[0]; + I("%s: value = %d, %d, %d ", __func__, value, tmp_data[2], tmp_data[3]); + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x70; tmp_addr[0] = 0x9C;/*0x1000709C weighting threshold*/ + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + weight = tmp_data[0]; + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x70; tmp_addr[0] = 0x94;/*0x10007095 weighting threshold*/ + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + weight = tmp_data[1] * weight; + I("%s: weight = %d ", __func__, weight); + + if (value > weight) + return ERR_TEST_FAIL; + else + return 0; +} + +static uint32_t himax_check_mode(uint8_t checktype) +{ + uint8_t tmp_data[4] = {0}; + uint8_t wait_pwd[2] = {0}; + + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + wait_pwd[0] = PWD_SORTING_END; + wait_pwd[1] = PWD_SORTING_END; + break; + case HIMAX_INSPECTION_OPEN: + wait_pwd[0] = PWD_OPEN_END; + wait_pwd[1] = PWD_OPEN_END; + break; + case HIMAX_INSPECTION_MICRO_OPEN: + wait_pwd[0] = PWD_OPEN_END; + wait_pwd[1] = PWD_OPEN_END; + break; + case HIMAX_INSPECTION_SHORT: + wait_pwd[0] = PWD_SHORT_END; + wait_pwd[1] = PWD_SHORT_END; + break; + case HIMAX_INSPECTION_RAWDATA: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + case HIMAX_INSPECTION_BPN_RAWDATA: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + case HIMAX_INSPECTION_WEIGHT_NOISE: + case HIMAX_INSPECTION_ABS_NOISE: + wait_pwd[0] = PWD_NOISE_END; + wait_pwd[1] = PWD_NOISE_END; + break; + + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + wait_pwd[0] = PWD_ACT_IDLE_END; + wait_pwd[1] = PWD_ACT_IDLE_END; + break; + + case HIMAX_INSPECTION_LPWUG_RAWDATA: + case HIMAX_INSPECTION_LPWUG_NOISE: + wait_pwd[0] = PWD_LPWUG_END; + wait_pwd[1] = PWD_LPWUG_END; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + wait_pwd[0] = PWD_LPWUG_IDLE_END; + wait_pwd[1] = PWD_LPWUG_IDLE_END; + break; + + default: + E("Wrong type=%d\n", checktype); + break; + } + + if (g_core_fp.fp_check_sorting_mode != NULL) + g_core_fp.fp_check_sorting_mode(tmp_data); + + if ((wait_pwd[0] == tmp_data[0]) && (wait_pwd[1] == tmp_data[1])) { + I("Change to mode=%s\n", g_himax_inspection_mode[checktype]); + return 0; + } else { + return 1; + } +} + +#define TEMP_LOG "%s: %s, tmp_data[0]=%x,tmp_data[1]=%x,tmp_data[2]=%x,tmp_data[3]=%x\n" + +static uint32_t himax_wait_sorting_mode(uint8_t checktype) +{ + uint8_t tmp_addr[4] = {0}; + uint8_t tmp_data[4] = {0}; + uint8_t wait_pwd[2] = {0}; + int count = 0; + + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + wait_pwd[0] = PWD_SORTING_END; + wait_pwd[1] = PWD_SORTING_END; + break; + case HIMAX_INSPECTION_OPEN: + wait_pwd[0] = PWD_OPEN_END; + wait_pwd[1] = PWD_OPEN_END; + break; + case HIMAX_INSPECTION_MICRO_OPEN: + wait_pwd[0] = PWD_OPEN_END; + wait_pwd[1] = PWD_OPEN_END; + break; + case HIMAX_INSPECTION_SHORT: + wait_pwd[0] = PWD_SHORT_END; + wait_pwd[1] = PWD_SHORT_END; + break; + case HIMAX_INSPECTION_RAWDATA: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + case HIMAX_INSPECTION_BPN_RAWDATA: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + case HIMAX_INSPECTION_WEIGHT_NOISE: + case HIMAX_INSPECTION_ABS_NOISE: + wait_pwd[0] = PWD_NOISE_END; + wait_pwd[1] = PWD_NOISE_END; + break; + + case HIMAX_INSPECTION_GAPTEST_RAW: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + wait_pwd[0] = PWD_ACT_IDLE_END; + wait_pwd[1] = PWD_ACT_IDLE_END; + break; + + case HIMAX_INSPECTION_LPWUG_RAWDATA: + case HIMAX_INSPECTION_LPWUG_NOISE: + wait_pwd[0] = PWD_LPWUG_END; + wait_pwd[1] = PWD_LPWUG_END; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + wait_pwd[0] = PWD_LPWUG_IDLE_END; + wait_pwd[1] = PWD_LPWUG_IDLE_END; + break; + + default: + I("No Change Mode and now type=%d\n", checktype); + break; + } + + do { + if (g_core_fp.fp_check_sorting_mode != NULL) + g_core_fp.fp_check_sorting_mode(tmp_data); + if ((wait_pwd[0] == tmp_data[0]) && + (wait_pwd[1] == tmp_data[1])) + return 0; + + + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + I(TEMP_LOG, __func__, "0x900000A8", + tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x00; tmp_addr[0] = 0xE4; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + I(TEMP_LOG, __func__, "0x900000E4", + tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x7F; tmp_addr[0] = 0x40; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + I(TEMP_LOG, __func__, "0x10007F40", + tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + I("Now retry %d times!\n", count++); + msleep(50); + } while (count < 50); + + return 1; +} + +static int hx_turn_on_mp_func(int on) +{ + int rslt = 0; + int retry = 3; + uint8_t tmp_addr[4] = {0}; + uint8_t tmp_data[4] = {0}; + uint8_t tmp_read[4] = {0}; + /* char *tmp_chipname = private_ts->chip_name; */ + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; tmp_addr[1] = 0x73; tmp_addr[0] = 0xEC; + if (on) { + I("%s : Turn on!\n", __func__); + if (strcmp(HX_83102D_SERIES_PWON, private_ts->chip_name) == 0) { + I("%s: need to enter Mp mode!\n", __func__); + tmp_data[3] = 0x00; tmp_data[2] = 0x10; tmp_data[1] = 0x73; tmp_data[0] = 0x80; + do { + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); + usleep_range(10000, 10001); + g_core_fp.fp_register_read(tmp_addr, 4, tmp_read, false); + I("%s: now read[2]=0x%02X, read[1]=0x%02X, read[0]=0x%02X!\n", + __func__, tmp_read[2], tmp_read[1], tmp_read[0]); + retry--; + } while (((retry > 0) && (tmp_read[2] != tmp_data[2] && tmp_read[1] != tmp_data[1] && tmp_read[0] != tmp_data[0]))); + } else { + I("%s:Nothing to be done!\n", __func__); + } + } else { + I("%s : Turn off!\n", __func__); + if (strcmp(HX_83102D_SERIES_PWON, private_ts->chip_name) == 0) { + I("%s: need to enter Mp mode!\n", __func__); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + do { + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); + usleep_range(10000, 10001); + g_core_fp.fp_register_read(tmp_addr, 4, tmp_read, false); + I("%s: now read[2]=0x%02X, read[1]=0x%02X, read[0]=0x%02X!\n", __func__, tmp_read[2], tmp_read[1], tmp_read[0]); + retry--; + } while ((retry > 0) && (tmp_read[2] != tmp_data[2] && tmp_read[1] != tmp_data[1] && tmp_read[0] != tmp_data[0])); + } else { + I("%s Nothing to be done!\n", __func__); + } + } + return rslt; +} + +/* HX_GAP START */ +/* gap test function */ +/* extern int himax_write_to_ic_flash_flow(uint32_t start_addr,uint32_t + * *write_data,uint32_t write_len); + */ + +static int himax_gap_test_vertical_setting(void) +{ + g_gap_vertical_part = kcalloc(g_gap_vertical_partial, + sizeof(int), GFP_KERNEL); + if (g_gap_vertical_part == NULL) { + E("%s: Memory allocation falied!\n", __func__); + return MEM_ALLOC_FAIL; + } + g_gap_vertical_part[0] = 0; + g_gap_vertical_part[1] = 4; + g_gap_vertical_part[2] = 8; + + return NO_ERR; +} + +static void himax_cal_gap_data_vertical(int start, int end_idx, int direct, + uint32_t *org_raw, uint32_t *result_raw) +{ + int i = 0; + int rx_num = ic_data->HX_RX_NUM; + + I("%s:start=%d\n", __func__, start); + I("%s:end_idx=%d\n", __func__, end_idx); + for (i = start; i < (start + rx_num*end_idx); i++) { + if (direct == 0) { /* up - down */ + if (i < start+rx_num) + result_raw[i] = 0; + else + result_raw[i] = org_raw[i-rx_num] - org_raw[i]; + + } else { /* down - up */ + if (i > (start + rx_num*(end_idx-1)-1)) + result_raw[i] = 0; + else + result_raw[i] = org_raw[i+rx_num] - org_raw[i]; + + } + } +} + +static int himax_gap_test_vertical_raw(int test_type, uint32_t *org_raw) +{ + int i_partial = 0; + int tmp_start = 0; + int tmp_end_idx = 0; + uint32_t *result_raw; + int i = 0; + int ret_val = NO_ERR; + + int tx_num = ic_data->HX_TX_NUM; + int rx_num = ic_data->HX_RX_NUM; + + if (himax_gap_test_vertical_setting()) + return MEM_ALLOC_FAIL; + + I("Print vertical ORG RAW\n"); + for (i = 0; i < tx_num*rx_num; i++) { + I("%04d,", org_raw[i]); + if (i > 0 && i%rx_num == (rx_num-1)) + I("\n"); + } + + result_raw = kcalloc(tx_num*rx_num, sizeof(uint32_t), GFP_KERNEL); + if (result_raw == NULL) { + E("%s: Memory allocation falied!\n", __func__); + goto alloc_result_raw_failed; + } + + for (i_partial = 0; i_partial < g_gap_vertical_partial; i_partial++) { + + tmp_start = g_gap_vertical_part[i_partial]*rx_num; + if (i_partial+1 == g_gap_vertical_partial) + tmp_end_idx = tx_num - g_gap_vertical_part[i_partial]; + else + tmp_end_idx = g_gap_vertical_part[i_partial+1] - + g_gap_vertical_part[i_partial]; + + if (i_partial % 2 == 0) + himax_cal_gap_data_vertical(tmp_start, tmp_end_idx, 0, + org_raw, result_raw); + else + himax_cal_gap_data_vertical(tmp_start, tmp_end_idx, 1, + org_raw, result_raw); + + } + + I("Print Vertical New RAW\n"); + for (i = 0; i < tx_num*rx_num; i++) { + I("%04d,", result_raw[i]); + if (i > 0 && i%rx_num == (rx_num-1)) + I("\n"); + } + + for (i = 0; i < tx_num*rx_num; i++) { + if (result_raw[i] < g_inspection_criteria[IDX_GAP_VER_RAWMIN][i] + && + result_raw[i] > g_inspection_criteria[IDX_GAP_VER_RAWMAX][i]) { + ret_val = NO_ERR - i; + break; + } + } + + /* himax_write_to_ic_flash_flow(0x1A000,result_raw,tx_num*rx_num); */ + kfree(result_raw); +alloc_result_raw_failed: + kfree(g_gap_vertical_part); + g_gap_vertical_part = NULL; + + return ret_val; +} + +static int himax_gap_test_horizontal_setting(void) +{ + g_gap_horizontal_part = kcalloc(g_gap_horizontal_partial, + sizeof(int), GFP_KERNEL); + if (g_gap_horizontal_part == NULL) { + E("%s: Memory allocation falied!\n", __func__); + return MEM_ALLOC_FAIL; + } + g_gap_horizontal_part[0] = 0; + g_gap_horizontal_part[1] = 8; + g_gap_horizontal_part[2] = 24; + + return NO_ERR; +} + +static void himax_cal_gap_data_horizontal(int start, int end_idx, int direct, + uint32_t *org_raw, uint32_t *result_raw) +{ + int i = 0; + int j = 0; + int rx_num = ic_data->HX_RX_NUM; + int tx_num = ic_data->HX_TX_NUM; + + I("start=%d\n", start); + I("end_idx=%d\n", end_idx); + for (j = 0; j < tx_num; j++) { + for (i = (start + (j*rx_num)); + i < (start + (j*rx_num) + end_idx); i++) { + /* left - right */ + if (direct == 0) { + if (i == (start + (j*rx_num))) + result_raw[i] = 0; + else + result_raw[i] = + org_raw[i-1] - org_raw[i]; + + } else { /* right - left */ + if (i == ((start + (j*rx_num) + end_idx) - 1)) + result_raw[i] = 0; + else + result_raw[i] = + org_raw[i + 1] - org_raw[i]; + } + } + } +} + +static int himax_gap_test_honrizontal_raw(int test_type, uint32_t *raw) +{ + int rx_num = ic_data->HX_RX_NUM; + int tx_num = ic_data->HX_TX_NUM; + int tmp_start = 0; + int tmp_end_idx = 0; + int i_partial = 0; + uint32_t *result_raw; + int i = 0; + int ret_val = NO_ERR; + + if (himax_gap_test_horizontal_setting()) + return MEM_ALLOC_FAIL; + + result_raw = kcalloc(ic_data->HX_TX_NUM * ic_data->HX_RX_NUM, + sizeof(uint32_t), GFP_KERNEL); + if (result_raw == NULL) { + E("%s: Memory allocation falied!\n", __func__); + goto alloc_result_raw_failed; + } + + I("Print Horizontal ORG RAW\n"); + for (i = 0; i < tx_num*rx_num; i++) { + I("%04d,", raw[i]); + if (i > 0 && i%rx_num == (rx_num-1)) + I("\n"); + } + + for (i_partial = 0; i_partial < g_gap_horizontal_partial; i_partial++) { + tmp_start = g_gap_horizontal_part[i_partial]; + if (i_partial+1 == g_gap_horizontal_partial) + tmp_end_idx = rx_num - g_gap_horizontal_part[i_partial]; + else + tmp_end_idx = g_gap_horizontal_part[i_partial+1] - + g_gap_horizontal_part[i_partial]; + + if (i_partial % 2 == 0) + himax_cal_gap_data_horizontal(tmp_start, tmp_end_idx, + 0, raw, result_raw); + else + himax_cal_gap_data_horizontal(tmp_start, tmp_end_idx, + 1, raw, result_raw); + + } + I("Print Horizontal New RAW\n"); + for (i = 0; i < tx_num*rx_num; i++) { + I("%04d,", result_raw[i]); + if (i > 0 && i%rx_num == (rx_num-1)) + I("\n"); + } + + for (i = 0; i < tx_num*rx_num; i++) { + if (result_raw[i] < g_inspection_criteria[IDX_GAP_HOR_RAWMIN][i] + && + result_raw[i] > g_inspection_criteria[IDX_GAP_HOR_RAWMAX][i]) { + ret_val = NO_ERR - i; + break; + } + } + + /* himax_write_to_ic_flash_flow(0x1A800,result_raw,tx_num*rx_num); */ + kfree(result_raw); +alloc_result_raw_failed: + kfree(g_gap_horizontal_part); + g_gap_horizontal_part = NULL; + + return ret_val; +} +/* HX_GAP END*/ +#define FAIL_IN_INDEX "%s: %s FAIL in index %d\n" +static uint32_t mpTestFunc(uint8_t checktype, uint32_t datalen) +{ + int32_t ret = 0; + uint32_t i/*, j*/; + uint32_t RAW[datalen]; + uint16_t palm_num = 0; + uint16_t noise_count = 0; + int ret_val = -1; + + /*uint16_t* pInspectGridData = &gInspectGridData[0];*/ + /*uint16_t* pInspectNoiseData = &gInspectNoiseData[0];*/ + I("Now Check type = %d\n", checktype); + + if (himax_check_mode(checktype)) { + /*himax_check_mode(checktype);*/ + + I("Need Change Mode ,target=%s\n", g_himax_inspection_mode[checktype]); + + g_core_fp.fp_sense_off(true); + hx_turn_on_mp_func(1); + +#ifndef HX_ZERO_FLASH + if (g_core_fp.fp_reload_disable != NULL) + g_core_fp.fp_reload_disable(1); +#endif + + himax_switch_mode_inspection(checktype); + + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_EOPEN; + break; + + case HIMAX_INSPECTION_OPEN: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_EOPEN; + break; + + case HIMAX_INSPECTION_MICRO_OPEN: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_EMOPEN; + break; + + case HIMAX_INSPECTION_SHORT: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_ESHORT; + break; + + case HIMAX_INSPECTION_RAWDATA: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_ERAW; + break; + + case HIMAX_INSPECTION_BPN_RAWDATA: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_ERAW; + break; + + case HIMAX_INSPECTION_WEIGHT_NOISE: + himax_set_N_frame(NOISEFRAME, checktype); + ret_val = HX_INSPECT_WT_ENOISE; + break; + + case HIMAX_INSPECTION_ABS_NOISE: + himax_set_N_frame(NOISEFRAME, checktype); + ret_val = HX_INSPECT_ABS_ENOISE; + break; + + case HIMAX_INSPECTION_GAPTEST_RAW: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_EGAP_RAW; + break; + + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + himax_set_N_frame(NORMAL_IDLE_RAWDATA_NOISEFRAME, checktype); + ret_val = HX_INSPECT_EACT_IDLE_RAW; + break; + + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + himax_set_N_frame(NORMAL_IDLE_RAWDATA_NOISEFRAME, checktype); + ret_val = HX_INSPECT_EACT_IDLE_NOISE; + break; + + case HIMAX_INSPECTION_LPWUG_RAWDATA: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_ELPWUG_RAW; + break; + + case HIMAX_INSPECTION_LPWUG_NOISE: + himax_set_N_frame(LPWUG_NOISEFRAME, checktype); + ret_val = HX_INSPECT_ELPWUG_NOISE; + break; + + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + himax_set_N_frame(LPWUG_IDLE_RAWDATAFRAME, checktype); + ret_val = HX_INSPECT_ELPWUG_IDLE_RAW; + break; + + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + himax_set_N_frame(LPWUG_IDLE_NOISEFRAME, checktype); + ret_val = HX_INSPECT_ELPWUG_IDLE_NOISE; + break; + + default: + E("UNDEFINED test type %d, exit\n", checktype); + ret_val = 0xFFFFFFFF; + break; + } + +/* + * if (checktype == HIMAX_INSPECTION_WEIGHT_NOISE || + * checktype == HIMAX_INSPECTION_ABS_NOISE) { + * himax_set_N_frame(NOISEFRAME, checktype); + * } else if (checktype == HIMAX_INSPECTION_ACT_IDLE_RAWDATA || + * checktype == HIMAX_INSPECTION_ACT_IDLE_NOISE) { + * I("N frame = %d\n", NORMAL_IDLE_RAWDATA_NOISEFRAME); + * himax_set_N_frame(NORMAL_IDLE_RAWDATA_NOISEFRAME, checktype); + * } else if (checktype == HIMAX_INSPECTION_LPWUG_RAWDATA) { + * I("N frame = %d\n", LPWUG_RAWDATAFRAME); + * himax_set_N_frame(LPWUG_RAWDATAFRAME, checktype); + * } else if (checktype == HIMAX_INSPECTION_LPWUG_NOISE) { + * I("N frame = %d\n", LPWUG_NOISEFRAME); + * himax_set_N_frame(LPWUG_NOISEFRAME, checktype); + * } else if (checktype == HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA) { + * I("N frame = %d\n", LPWUG_IDLE_RAWDATAFRAME); + * himax_set_N_frame(LPWUG_IDLE_RAWDATAFRAME, checktype); + * } else if (checktype == HIMAX_INSPECTION_LPWUG_IDLE_NOISE) { + * I("N frame = %d\n", LPWUG_IDLE_NOISEFRAME); + * himax_set_N_frame(LPWUG_IDLE_NOISEFRAME, checktype); + * } else { + * himax_set_N_frame(OTHERSFRAME, checktype); + * } + */ + + g_core_fp.fp_sense_on(1); + + if (ret_val == 0xFFFFFFFF) + return ret_val; + } + + ret = himax_wait_sorting_mode(checktype); + if (ret) { + E("%s: himax_wait_sorting_mode FAIL\n", __func__); + return ret_val; + } + himax_switch_data_type(checktype); + + ret = himax_get_rawdata(RAW, datalen); + if (ret) { + E("%s: himax_get_rawdata FAIL\n", __func__); + return ret_val | ret; + } + + /*get Max DC from FW*/ + g_dc_max = g_core_fp.fp_get_max_dc(); + + /* back to normal */ + himax_switch_data_type(HIMAX_INSPECTION_BACK_NORMAL); + + I("%s: Init OK, start to test!\n", __func__); + + snprintf(g_start_log, 256 * sizeof(char), "\n%s%s\n", + g_himax_inspection_mode[checktype], ": data as follow!\n"); + + /*Check Data*/ + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] < g_inspection_criteria[IDX_SORTMIN][i]) { + E(FAIL_IN_INDEX, __func__, "sorting mode open test", i); + ret_val = HX_INSPECT_EOPEN; + goto FAIL_END; + } + } + I("%s: sorting mode open test PASS\n", __func__); + break; + + case HIMAX_INSPECTION_OPEN: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_OPENMAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_OPENMIN][i]) { + E(FAIL_IN_INDEX, __func__, "open test", i); + ret_val = HX_INSPECT_EOPEN; + goto FAIL_END; + } + } + I("%s: open test PASS\n", __func__); + break; + + case HIMAX_INSPECTION_MICRO_OPEN: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_M_OPENMAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_M_OPENMIN][i]) { + E(FAIL_IN_INDEX, __func__, "micro open test", i); + ret_val = HX_INSPECT_EMOPEN; + goto FAIL_END; + } + } + I("%s: micro open test PASS\n", __func__); + break; + + case HIMAX_INSPECTION_SHORT: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_SHORTMAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_SHORTMIN][i]) { + E(FAIL_IN_INDEX, __func__, "short test", i); + ret_val = HX_INSPECT_ESHORT; + goto FAIL_END; + } + } + I("%s: short test PASS\n", __func__); + break; + + case HIMAX_INSPECTION_RAWDATA: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + /* I("Now new compare, datalen=%d!\n", + * ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); + */ + if ((int)RAW[i] > g_inspection_criteria[IDX_RAWMAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_RAWMIN][i]) { + E("%s: rawdata test FAIL:RAW[%d]=%d\n", __func__, i, RAW[i]); + I("%s: Now Criteria max=%d,min=%d\n", __func__, + g_inspection_criteria[IDX_RAWMAX][i], + g_inspection_criteria[IDX_RAWMIN][i]); + ret_val = HX_INSPECT_ERAW; + goto FAIL_END; + } + } + I("%s: rawdata test PASS\n", __func__); + break; + + case HIMAX_INSPECTION_BPN_RAWDATA: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + /* I("Now new compare, datalen=%d!\n", + * ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); + */ + RAW[i] = (int)RAW[i] * 100 / g_dc_max; + if ((int)RAW[i] > g_inspection_criteria[IDX_BPN_RAWMAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_BPN_RAWMIN][i]) { + E("%s: rawdata test FAIL:BPN RAW[%d]=%d\n", __func__, i, RAW[i]); + I("%s: Now Criteria max=%d,min=%d\n", __func__, + g_inspection_criteria[IDX_BPN_RAWMAX][i], + g_inspection_criteria[IDX_BPN_RAWMIN][i]); + ret_val = HX_INSPECT_ERAW; + goto FAIL_END; + } + } + I("%s: BPN rawdata test PASS\n", __func__); + break; + + case HIMAX_INSPECTION_WEIGHT_NOISE: + /*I("NOISEMAX=%d\n", NOISEMAX);*/ + noise_count = 0; + himax_get_noise_base(); + palm_num = himax_get_palm_num(); + for (i = 0; i < (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > NOISEMAX) + noise_count++; + } + I("noise_count=%d\n", noise_count); + if (noise_count > palm_num) { + E("%s: noise test FAIL\n", __func__); + ret_val = HX_INSPECT_WT_ENOISE; + goto FAIL_END; + } + snprintf(g_start_log, 256 * sizeof(char), "\n Threshold = %d\n", NOISEMAX); + /*Check weightingt*/ + ret = himax_get_noise_weight_test(); + if (ret < 0) { + I("%s: noise test FAIL %d\n", __func__, ret); + ret_val = HX_INSPECT_WT_ENOISE; + goto FAIL_END; + } + + /*Check negative side noise*/ + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > (g_inspection_criteria[IDX_WT_NOISEMAX][i] * g_recal_thx / 100) + || (int)RAW[i] < (g_inspection_criteria[IDX_WT_NOISEMIN][i] * g_recal_thx / 100)) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_WEIGHT_NOISE", i); + ret_val = HX_INSPECT_WT_ENOISE; + goto FAIL_END; + } + } + I("%s: noise test(weight) PASS\n", __func__); + break; + + case HIMAX_INSPECTION_ABS_NOISE: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_ABS_NOISEMAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_ABS_NOISEMIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_ABS_NOISE", i); + I("%s: Now Criteria max=%d,min=%d\n", __func__, + g_inspection_criteria[IDX_ABS_NOISEMAX][i], + g_inspection_criteria[IDX_ABS_NOISEMIN][i]); + ret_val = HX_INSPECT_ABS_ENOISE; + goto FAIL_END; + } + } + I("%s: noise test(absolute) PASS\n", __func__); + break; + + case HIMAX_INSPECTION_GAPTEST_RAW: + if (himax_gap_test_vertical_raw(HIMAX_INSPECTION_GAPTEST_RAW, RAW) != NO_ERR) { + E("%s: HIMAX_INSPECTION_GAPTEST_RAW FAIL\n", __func__); + ret_val = HX_INSPECT_EGAP_RAW; + goto FAIL_END; + } + if (himax_gap_test_honrizontal_raw(HIMAX_INSPECTION_GAPTEST_RAW, RAW) != NO_ERR) { + E("%s: HIMAX_INSPECTION_GAPTEST_RAW FAIL\n", __func__); + ret_val = HX_INSPECT_EGAP_RAW; + goto FAIL_END; + } + break; + + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_ACT_IDLE_RAWDATA_MAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_ACT_IDLE_RAWDATA_MIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_ACT_IDLE_RAWDATA", i); + ret_val = HX_INSPECT_EACT_IDLE_RAW; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_ACT_IDLE_RAWDATA PASS\n", __func__); + break; + + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_ACT_IDLE_NOISE_MAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_ACT_IDLE_NOISE_MIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_ACT_IDLE_NOISE", i); + ret_val = HX_INSPECT_EACT_IDLE_NOISE; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_ACT_IDLE_NOISE PASS\n", __func__); + break; + + case HIMAX_INSPECTION_LPWUG_RAWDATA: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_LPWUG_RAWDATA_MAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_LPWUG_RAWDATA_MIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_LPWUG_RAWDATA", i); + ret_val = HX_INSPECT_ELPWUG_RAW; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_LPWUG_RAWDATA PASS\n", __func__); + break; + + case HIMAX_INSPECTION_LPWUG_NOISE: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_LPWUG_NOISE_MAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_LPWUG_NOISE_MIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_LPWUG_NOISE", i); + ret_val = HX_INSPECT_ELPWUG_NOISE; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_LPWUG_NOISE PASS\n", __func__); + break; + + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_LPWUG_IDLE_RAWDATA_MAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_LPWUG_IDLE_RAWDATA_MIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA", i); + ret_val = HX_INSPECT_ELPWUG_IDLE_RAW; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA PASS\n", __func__); + break; + + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_LPWUG_IDLE_NOISE_MAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_LPWUG_IDLE_NOISE_MIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_LPWUG_IDLE_NOISE", i); + ret_val = HX_INSPECT_ELPWUG_IDLE_NOISE; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_LPWUG_IDLE_NOISE PASS\n", __func__); + break; + + default: + E("Wrong type=%d\n", checktype); + ret_val = 0xFFFFFFFF; + break; + } + + ret_val = HX_INSPECT_OK; + snprintf(g_rslt_log, 256 * sizeof(char), "\n%s%s\n", + g_himax_inspection_mode[checktype], " Test Pass!\n"); + I("pass write log\n"); + goto END_FUNC; + +FAIL_END: + snprintf(g_rslt_log, 256 * sizeof(char), "\n%s%s\n", + g_himax_inspection_mode[checktype], " Test Fail!\n"); + I("fail write log\n"); +END_FUNC: + hx_test_data_get(RAW, g_start_log, g_rslt_log, checktype); + + return ret_val; +} + +/* claculate 10's power function */ +static int himax_power_cal(int pow, int number) +{ + int i = 0; + int result = 1; + + for (i = 0; i < pow; i++) + result *= 10; + result = result * number; + + return result; + +} + +/* String to int */ +static int hiamx_parse_str2int(char *str) +{ + int i = 0; + int temp_cal = 0; + int result = 0; + int str_len = strlen(str); + int negtive_flag = 0; + + for (i = 0; i < strlen(str); i++) { + if (str[i] != '-' && str[i] > '9' && str[i] < '0') { + E("%s: Parsing fail!\n", __func__); + result = -9487; + negtive_flag = 0; + break; + } + if (str[i] == '-') { + negtive_flag = 1; + continue; + } + temp_cal = str[i] - '0'; + result += himax_power_cal(str_len-i-1, temp_cal); + /* str's the lowest char is the number's the highest number + * So we should reverse this number before using the power + * function + * -1: starting number is from 0 ex:10^0 = 1,10^1=10 + */ + } + + if (negtive_flag == 1) + result = 0 - result; + + return result; +} + + +/* Get sub-string from original string by using some charaters + * return size of result + */ +static int himax_saperate_comma(const struct firmware *file_entry, + char **result, int str_size) +{ + int count = 0; + int str_count = 0; /* now string*/ + int char_count = 0; /* now char count in string*/ + + do { + switch (file_entry->data[count]) { + case ASCII_COMMA: + case ACSII_SPACE: + case ASCII_CR: + case ASCII_LF: + count++; + /* If end of line as above condifiton, + * differencing the count of char. + * If char_count != 0 + * it's meaning this string is parsing over . + * The Next char is belong to next string + */ + if (char_count != 0) { + char_count = 0; + str_count++; + } + break; + default: + result[str_count][char_count++] = + file_entry->data[count]; + count++; + break; + } + } while (count < file_entry->size && str_count < str_size); + + return str_count; +} + +static int hx_diff_str(char *str1, char *str2) +{ + int i = 0; + int result = 0; /* zero is all same, non-zero is not same index*/ + int str1_len = strlen(str1); + int str2_len = strlen(str2); + + if (str1_len != str2_len) { + if (private_ts->debug_log_level & BIT(4)) + I("%s:Size different!\n", __func__); + return LENGTH_FAIL; + } + + for (i = 0; i < str1_len; i++) { + if (str1[i] != str2[i]) { + result = i+1; + I("%s: different in %d!\n", __func__, result); + return result; + } + } + + return result; +} + +/* get idx of criteria whe parsing file */ +int hx_find_crtra_id(char *input) +{ + int i = 0; + int result = 0; + + for (i = 0 ; i < HX_CRITERIA_SIZE ; i++) { + if (hx_diff_str(g_hx_inspt_crtra_name[i], input) == 0) { + result = i; + I("find the str=%s,idx=%d\n", + g_hx_inspt_crtra_name[i], i); + break; + } + } + if (i > (HX_CRITERIA_SIZE - 1)) { + E("%s: find Fail!\n", __func__); + return LENGTH_FAIL; + } + + return result; +} + +int hx_print_crtra_after_parsing(void) +{ + int i = 0, j = 0; + int all_mut_len = ic_data->HX_TX_NUM*ic_data->HX_RX_NUM; + + for (i = 0; i < HX_CRITERIA_SIZE; i++) { + I("Now is %s\n", g_hx_inspt_crtra_name[i]); + if (g_inspt_crtra_flag[i] == 1) { + for (j = 0; j < all_mut_len; j++) { + I("%d, ", g_inspection_criteria[i][j]); + if (j % 16 == 15) + PI("\n"); + } + } else { + I("No this Item in this criteria file!\n"); + } + PI("\n"); + } + + return 0; +} + +static int hx_get_crtra_by_name(char **result, int size_of_result_str) +{ + int i = 0; + /* count of criteria type */ + int count_type = 0; + /* count of criteria data */ + int count_data = 0; + int err = HX_INSPECT_OK; + int all_mut_len = ic_data->HX_TX_NUM*ic_data->HX_RX_NUM; + int temp = 0; + + /* get criteria and assign to a global array(2-Dimensional/int) */ + /* basiclly the size of criteria will be + * (crtra_count * (all_mut_len) + crtra_count) + * but we use file size to be the end of counter + */ + for (i = 0; i < size_of_result_str && result[i] != NULL; i++) { + /* It have get one page(all mutual) criteria data! + * And we should skip the string of criteria name! + */ + if (i == 0 || i == + ((i / (all_mut_len))+(i / (all_mut_len) * (all_mut_len)))) { + count_data = 0; + + if (private_ts->debug_log_level & BIT(4)) + I("Now find str=%s ,idx=%d\n", result[i], i); + + /* check the item of criteria is in criteria file + * or not + */ + count_type = hx_find_crtra_id(result[i]); + if (count_type < 0) { + E("1. %s:Name Not match!\n", __func__); + /* E("can recognize[%d]=%s\n", count_type, + * g_hx_inspt_crtra_name[count_type]); + */ + E("get from file[%d]=%s\n", i, result[i]); + E("Please check criteria file again!\n"); + err = HX_INSPECT_EFILE; + goto END_FUNCTION; + } else { + I("Now str=%s, idx=%d\n", + g_hx_inspt_crtra_name[count_type], count_type); + g_inspt_crtra_flag[count_type] = 1; + } + continue; + } + /* change string to int*/ + temp = hiamx_parse_str2int(result[i]); + if (temp != -9487) + g_inspection_criteria[count_type][count_data] = temp; + else { + E("%s: Parsing Fail in %d\n", __func__, i); + E("in range:[%d]=%s\n", count_type, + g_hx_inspt_crtra_name[count_type]); + E("btw, get from file[%d]=%s\n", i, result[i]); + break; + } + /* dbg + * I("[%d]g_inspection_criteria[%d][%d]=%d\n", + * i, count_type, count_data, + * g_inspection_criteria[count_type][count_data]); + */ + count_data++; + + } + + if (private_ts->debug_log_level & BIT(4)) { + /* dbg:print all of criteria from parsing file */ + hx_print_crtra_after_parsing(); + } + + I("Total loop=%d\n", i); +END_FUNCTION: + return err; +} + +static int himax_parse_criteria_file(void) +{ + int err = HX_INSPECT_OK; + const struct firmware *file_entry = NULL; + char **result; + int i = 0; + int crtra_count = HX_CRITERIA_SIZE; + int data_size = 0; /* The maximum of number Data*/ + int all_mut_len = ic_data->HX_TX_NUM*ic_data->HX_RX_NUM; + int str_max_len = 128; + int result_all_len = 0; + int file_size = 0; + int size_of_result_str = 0; + + I("%s,Entering\n", __func__); + if (hx_self_test_file_name == NULL) { + E("file name is NULL\n"); + hx_self_test_file_name = kzalloc(80, GFP_KERNEL); + if (strlen(private_ts->pdata->panel_supplier)) + snprintf(hx_self_test_file_name, 60, "%s_hx_criteria.csv", private_ts->pdata->panel_supplier); + else + snprintf(hx_self_test_file_name, 16, "hx_criteria.csv"); + I("%s: Use name %s\n", __func__, hx_self_test_file_name); + } + + I("file name = %s\n", hx_self_test_file_name); + /* default path is /system/etc/firmware */ + err = request_firmware(&file_entry, hx_self_test_file_name, private_ts->dev); + if (err < 0) { + E("%s,fail in line%d error code=%d\n", __func__, __LINE__, err); + err = HX_INSPECT_EFILE; + goto END_FUNC_REQ_FAIL; + } + + /* size of criteria include name string */ + data_size = ((all_mut_len) * crtra_count) + crtra_count; + + /* init the array which store original criteria and include + * name string + */ + result = kcalloc(data_size, sizeof(char *), GFP_KERNEL); + if (result != NULL) { + for (i = 0 ; i < data_size; i++) { + result[i] = kcalloc(str_max_len, sizeof(char), GFP_KERNEL); + if (result[i] == NULL) { + E("%s: rst_arr Memory allocation falied!\n", __func__); + err = HX_INSPECT_MEMALLCTFAIL; + goto rst_arr_mem_alloc_failed; + } + } + } else { + E("%s: Memory allocation falied!\n", __func__); + err = HX_INSPECT_MEMALLCTFAIL; + goto rst_mem_alloc_failed; + } + + result_all_len = data_size; + file_size = file_entry->size; + I("Now result_all_len=%d\n", result_all_len); + I("Now file_size=%d\n", file_size); + + /* dbg */ + if (private_ts->debug_log_level & BIT(4)) { + I("first 4 bytes 0x%2X,0x%2X,0x%2X,0x%2X !\n", + file_entry->data[0], file_entry->data[1], + file_entry->data[2], file_entry->data[3]); + } + + /* parse value in to result array(1-Dimensional/String) */ + size_of_result_str = + himax_saperate_comma(file_entry, result, data_size); + + I("%s: now size_of_result_str=%d\n", __func__, size_of_result_str); + + err = hx_get_crtra_by_name(result, size_of_result_str); + if (err != HX_INSPECT_OK) { + E("%s:Load criteria from file fail, go end!\n", __func__); + goto END_FUNC; + } + + +END_FUNC: +rst_arr_mem_alloc_failed: + for (i = 0 ; i < data_size; i++) + if (result[i] != NULL) + kfree(result[i]); +rst_mem_alloc_failed: + kfree(result); + release_firmware(file_entry); +END_FUNC_REQ_FAIL: + I("%s,END\n", __func__); + return err; + /* parsing Criteria end */ +} + + +int hx_get_size_str_arr(char **input) +{ + int i = 0; + int result = 0; + + while (input[i] != NULL) + i++; + + result = i; + if (private_ts->debug_log_level & BIT(4)) + I("There is %d in [0]=%s\n", result, input[0]); + + return result; +} + +static int himax_self_test_data_init(void) +{ + int ret = HX_INSPECT_OK; + int i = 0; + + /* + * 5: one value will not over than 99999, so get this size of string + * 2: get twice size + */ + g_1kind_raw_size = 5 * ic_data->HX_RX_NUM * ic_data->HX_RX_NUM * 2; + + /* get test item and its items of criteria*/ + HX_CRITERIA_ITEM = hx_get_size_str_arr(g_himax_inspection_mode); + HX_CRITERIA_SIZE = hx_get_size_str_arr(g_hx_inspt_crtra_name); + I("There is %d HX_CRITERIA_ITEM and %d HX_CRITERIA_SIZE\n", + HX_CRITERIA_ITEM, HX_CRITERIA_SIZE); + + /* init criteria data*/ + g_inspt_crtra_flag = kzalloc(HX_CRITERIA_SIZE*sizeof(int), GFP_KERNEL); + g_inspection_criteria = kzalloc(sizeof(int *)*HX_CRITERIA_SIZE, GFP_KERNEL); + if (g_inspt_crtra_flag == NULL || g_inspection_criteria == NULL) { + E("%s: %d, Memory allocation falied!\n", __func__, __LINE__); + return MEM_ALLOC_FAIL; + } + + for (i = 0; i < HX_CRITERIA_SIZE; i++) { + g_inspection_criteria[i] = kcalloc( + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM), sizeof(int), GFP_KERNEL); + if (g_inspection_criteria[i] == NULL) { + E("%s: %d, Memory allocation falied!\n", __func__, __LINE__); + return MEM_ALLOC_FAIL; + } + } + + /* parsing criteria from file*/ + ret = himax_parse_criteria_file(); + + if (private_ts->debug_log_level & BIT(4)) { + /* print get criteria string */ + for (i = 0 ; i < HX_CRITERIA_SIZE ; i++) { + if (g_inspt_crtra_flag[i] != 0) + I("%s: [%d]There is String=%s\n", + __func__, i, g_hx_inspt_crtra_name[i]); + } + } + + if (g_rslt_data == NULL) { + g_rslt_data = kcalloc(g_1kind_raw_size * HX_CRITERIA_ITEM, + sizeof(char), GFP_KERNEL); + I("g_rslt_data is NULL"); + } else { + memset(g_rslt_data, 0x00, g_1kind_raw_size * HX_CRITERIA_ITEM * + sizeof(char)); + } + + snprintf(g_file_path, + (int)(strlen(HX_RSLT_OUT_PATH) + strlen(HX_RSLT_OUT_FILE)+1), + "%s%s", HX_RSLT_OUT_PATH, HX_RSLT_OUT_FILE); + + return ret; +} + +static void himax_self_test_data_deinit(void) +{ + int i = 0; + + /*dbg*/ + /* for (i = 0; i < HX_CRITERIA_ITEM; i++) + * I("%s:[%d]%d\n", __func__, i, g_inspection_criteria[i]); + */ + if (g_inspection_criteria != NULL) { + for (i = 0; i < HX_CRITERIA_SIZE; i++) { + if (g_inspection_criteria[i] != NULL) + kfree(g_inspection_criteria[i]); + } + kfree(g_inspection_criteria); + I("Now it have free the g_inspection_criteria!\n"); + } else { + I("No Need to free g_inspection_criteria!\n"); + } + + g_rslt_data_len = 0; + +} + +static int himax_chip_self_test(void) +{ + uint32_t ret = HX_INSPECT_OK; + uint8_t tmp_addr[DATA_LEN_4] = {0x94, 0x72, 0x00, 0x10}; + uint8_t tmp_data[DATA_LEN_4] = {0x01, 0x00, 0x00, 0x00}; + +#if defined(HIMAX_V2_MULTI_BIN)||defined(HX_CODE_OVERLAY) + uint8_t normalfw[32] = "Himax_firmware.bin"; + uint8_t mpapfw[32] = "Himax_mpfw.bin"; + if (strlen(private_ts->pdata->panel_supplier)) { + snprintf(normalfw, 32, "%s_Himax_firmware.bin", private_ts->pdata->panel_supplier); + snprintf(mpapfw, 32, "%s_Himax_mpfw.bin", private_ts->pdata->panel_supplier); + } +#endif + + I("%s:IN\n", __func__); + + private_ts->suspend_resume_done = 0; + + ret = himax_self_test_data_init(); + if (ret != HX_INSPECT_OK) { + E("%s: himax_self_test_data_init fail!\n", __func__); + goto END_FUNC; + } + +#if defined(HIMAX_V2_MULTI_BIN)||defined(HX_CODE_OVERLAY) + g_core_fp.fp_0f_op_file_dirly(mpapfw); + g_core_fp.fp_reload_disable(0); + g_core_fp.fp_sense_on(0x00); +#endif + + if (g_inspt_crtra_flag[IDX_OPENMIN] == 1 && + g_inspt_crtra_flag[IDX_OPENMAX] == 1) { + /*1. Open Test*/ + I("[MP_OPEN_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_OPEN, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("1. Open Test: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_OPENMIN], + g_inspt_crtra_flag[IDX_OPENMIN]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_OPENMAX], + g_inspt_crtra_flag[IDX_OPENMAX]); + } + + if (g_inspt_crtra_flag[IDX_M_OPENMIN] == 1 && + g_inspt_crtra_flag[IDX_M_OPENMAX] == 1) { + /*2. Micro-Open Test*/ + I("[MP_MICRO_OPEN_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_MICRO_OPEN, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("2. Micro Open Test: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_M_OPENMIN], + g_inspt_crtra_flag[IDX_M_OPENMIN]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_M_OPENMAX], + g_inspt_crtra_flag[IDX_M_OPENMAX]); + } + + if (g_inspt_crtra_flag[IDX_SHORTMIN] == 1 && + g_inspt_crtra_flag[IDX_SHORTMAX] == 1) { + /*3. Short Test*/ + I("[MP_SHORT_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_SHORT, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("3. Short Test: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_SHORTMIN], + g_inspt_crtra_flag[IDX_SHORTMIN]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_SHORTMAX], + g_inspt_crtra_flag[IDX_SHORTMAX]); + } + + if (g_inspt_crtra_flag[IDX_RAWMIN] == 1 && + g_inspt_crtra_flag[IDX_RAWMAX] == 1) { + /*4. RawData Test*/ + I("==========================================\n"); + I("[MP_RAW_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_RAWDATA, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("%d. %s: End %d\n\n\n", HIMAX_INSPECTION_RAWDATA, + g_himax_inspection_mode[HIMAX_INSPECTION_RAWDATA], ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_RAWMIN], + g_inspt_crtra_flag[IDX_RAWMIN]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_RAWMAX], + g_inspt_crtra_flag[IDX_RAWMAX]); + } + + if (g_inspt_crtra_flag[IDX_BPN_RAWMIN] == 1 && + g_inspt_crtra_flag[IDX_BPN_RAWMAX] == 1) { + /*4. RawData BPN Test*/ + I("==========================================\n"); + I("[MP_BPN_RAW_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_BPN_RAWDATA, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("%d. %s: End %d\n\n\n", HIMAX_INSPECTION_BPN_RAWDATA, + g_himax_inspection_mode[HIMAX_INSPECTION_BPN_RAWDATA], ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_BPN_RAWMIN], + g_inspt_crtra_flag[IDX_BPN_RAWMIN]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_BPN_RAWMAX], + g_inspt_crtra_flag[IDX_BPN_RAWMAX]); + } + + if (g_inspt_crtra_flag[IDX_WT_NOISEMIN] == 1 && + g_inspt_crtra_flag[IDX_WT_NOISEMAX] == 1) { + /*5. Noise Test by Weighting*/ + I("[MP_NOISE_TEST_WEIGHT]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_WEIGHT_NOISE, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("5. Noise Test: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_WT_NOISEMAX], + g_inspt_crtra_flag[IDX_WT_NOISEMAX]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_WT_NOISEMIN], + g_inspt_crtra_flag[IDX_WT_NOISEMIN]); + } + + if (g_inspt_crtra_flag[IDX_ABS_NOISEMIN] == 1 && + g_inspt_crtra_flag[IDX_ABS_NOISEMAX] == 1) { + /*5. Noise Test by per block*/ + I("[MP_NOISE_TEST_ABS]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_ABS_NOISE, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("5. Noise Test: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_ABS_NOISEMAX], + g_inspt_crtra_flag[IDX_ABS_NOISEMAX]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_ABS_NOISEMIN], + g_inspt_crtra_flag[IDX_ABS_NOISEMIN]); + } + + if (g_inspt_crtra_flag[IDX_SORTMIN] == 1 && + g_inspt_crtra_flag[IDX_SORTMAX] == 1) { + /*6. Sorting Test*/ + I("[SORTING TEST]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_SORTING, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("6. SORTING TEST: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_SORTMIN], + g_inspt_crtra_flag[IDX_SORTMIN]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_SORTMAX], + g_inspt_crtra_flag[IDX_SORTMAX]); + } + + if ((g_inspt_crtra_flag[IDX_GAP_HOR_RAWMAX] == 1 && + g_inspt_crtra_flag[IDX_GAP_HOR_RAWMIN] == 1) && + (g_inspt_crtra_flag[IDX_GAP_VER_RAWMAX] == 1 && + g_inspt_crtra_flag[IDX_GAP_VER_RAWMIN] == 1)) { + /*6. GAP Test*/ + I("[MP_GAP_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_GAPTEST_RAW, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("6. MP_GAP_TEST_RAW: End %d\n\n\n", ret); + } else { + I("Now %s : HOR flag=%d\n", + g_hx_inspt_crtra_name[IDX_GAP_HOR_RAWMAX], + g_inspt_crtra_flag[IDX_GAP_HOR_RAWMAX]); + I("Now %s : HOR flag=%d\n", + g_hx_inspt_crtra_name[IDX_GAP_HOR_RAWMIN], + g_inspt_crtra_flag[IDX_GAP_HOR_RAWMIN]); + I("Now %s : VERTICAL flag=%d\n", + g_hx_inspt_crtra_name[IDX_GAP_VER_RAWMAX], + g_inspt_crtra_flag[IDX_GAP_VER_RAWMAX]); + I("Now %s : VERTICAL flag=%d\n", + g_hx_inspt_crtra_name[IDX_GAP_VER_RAWMIN], + g_inspt_crtra_flag[IDX_GAP_VER_RAWMIN]); + } + + if (g_inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MIN] == 1 && + g_inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MAX] == 1) { + /*7. ACT_IDLE RAWDATA*/ + I("[MP_ACT_IDLE_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_ACT_IDLE_RAWDATA, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("7. MP_ACT_IDLE_TEST_RAW: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_ACT_IDLE_RAWDATA_MAX], + g_inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MAX]); + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_ACT_IDLE_RAWDATA_MIN], + g_inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MIN]); + } + + if (g_inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MIN] == 1 && + g_inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MAX] == 1) { + /*8. ACT_IDLE NOISE*/ + I("[MP_ACT_IDLE_TEST_NOISE]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_ACT_IDLE_NOISE, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("8. MP_ACT_IDLE_TEST_NOISE: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_ACT_IDLE_NOISE_MAX], + g_inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MAX]); + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_ACT_IDLE_NOISE_MIN], + g_inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MIN]); + } + + /* check press power key or not for LPWUG test item*/ + if ((g_inspt_crtra_flag[IDX_LPWUG_NOISE_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_NOISE_MIN] == 1) || + (g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MIN] == 1) || + (g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MIN] == 1) || + (g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MIN] == 1) + ) + himax_press_powerkey(); + +/* Workaround delete */ +#if 0 + /* Wait suspend done */ + while (private_ts->suspend_resume_done != 1) + usleep_range(1000, 1001); + + private_ts->suspend_resume_done = 0; +#endif + + if (g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MIN] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MAX] == 1) { + /*9. LPWUG RAWDATA*/ + I("[MP_LPWUG_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_LPWUG_RAWDATA, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("9. MP_LPWUG_TEST_RAW: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_RAWDATA_MIN], + g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MIN]); + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_RAWDATA_MAX], + g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MAX]); + } + + if (g_inspt_crtra_flag[IDX_LPWUG_NOISE_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_NOISE_MIN] == 1) { + /*10. LPWUG NOISE*/ + I("[MP_LPWUG_TEST_NOISE]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_LPWUG_NOISE, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("10. MP_LPWUG_TEST_NOISE: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_NOISE_MAX], + g_inspt_crtra_flag[IDX_LPWUG_NOISE_MAX]); + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_NOISE_MIN], + g_inspt_crtra_flag[IDX_LPWUG_NOISE_MIN]); + } + + if (g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MIN] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MAX] == 1) { + /*11. LPWUG IDLE RAWDATA*/ + I("[MP_LPWUG_IDLE_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("11. MP_LPWUG_IDLE_TEST_RAW: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_IDLE_RAWDATA_MIN], + g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MIN]); + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_IDLE_RAWDATA_MAX], + g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MAX]); + } + + if (g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MIN] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MAX] == 1) { + /*12. LPWUG IDLE RAWDATA*/ + I("[MP_LPWUG_IDLE_TEST_NOISE]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_LPWUG_IDLE_NOISE, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("12. MP_LPWUG_IDLE_TEST_NOISE: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_IDLE_NOISE_MIN], + g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MIN]); + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_IDLE_NOISE_MAX], + g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MAX]); + } + + /* check press power key or not for LPWUG test item*/ + if ((g_inspt_crtra_flag[IDX_LPWUG_NOISE_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_NOISE_MIN] == 1) || + (g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MIN] == 1) || + (g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MIN] == 1) || + (g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MIN] == 1) + ) + himax_press_powerkey(); + + hx_test_data_pop_out(g_rslt_data, g_file_path); + +/* Workaround delete */ +#if 0 + /* Wait resume done */ + while (private_ts->suspend_resume_done != 1) + usleep_range(1000, 1001); +#endif + +#if defined(HIMAX_V2_MULTI_BIN)||defined(HX_CODE_OVERLAY) + private_ts->in_self_test = 0; + g_core_fp.fp_0f_op_file_dirly(normalfw); + hx_turn_on_mp_func(0); + /* set N frame back to default value 1*/ + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); + g_core_fp.fp_reload_disable(0); + g_core_fp.fp_sense_on(0); +#else + g_core_fp.fp_sense_off(true); + hx_turn_on_mp_func(0); + /*himax_set_N_frame(1, HIMAX_INSPECTION_WEIGHT_NOISE);*/ + /* set N frame back to default value 1*/ + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); +#ifndef HX_ZERO_FLASH + if (g_core_fp.fp_reload_disable != NULL) + g_core_fp.fp_reload_disable(0); +#endif + g_core_fp.fp_sense_on(0); +#endif + +END_FUNC: + himax_self_test_data_deinit(); + + + I("running status = %d\n", ret); + + /*if (ret != 0)*/ + /*ret = 1;*/ + + I("%s:OUT\n", __func__); + return ret; +} + +void himax_inspect_data_clear(void) +{ + if (!g_rslt_data) { + kfree(g_rslt_data); + g_rslt_data = NULL; + } +} + +void himax_inspection_init(void) +{ + I("%s: enter, %d\n", __func__, __LINE__); + + g_core_fp.fp_chip_self_test = himax_chip_self_test; +} diff --git a/drivers/input/touchscreen/himax_v2_mmi/himax_inspection.h b/drivers/input/touchscreen/himax_v2_mmi/himax_inspection.h new file mode 100644 index 000000000000..6b0f5dbfb46d --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/himax_inspection.h @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for inspection functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_platform.h" +#include "himax_common.h" +#include "himax_ic_core.h" + +/*#define HX_GAP_TEST*/ +/*#define HX_INSPECT_LPWUG_TEST*/ +/*#define HX_ACT_IDLE_TEST*/ + +#define HX_RSLT_OUT_PATH "/sdcard/" +#define HX_RSLT_OUT_FILE "hx_test_result.txt" +#define PI(x...) pr_info(x) + +#ifdef HX_ESD_RECOVERY + extern u8 HX_ESD_RESET_ACTIVATE; +#endif + +#define BS_RAWDATA 10 +#define BS_NOISE 10 +#define BS_OPENSHORT 0 +#define BS_LPWUG 1 +#define BS_LPWUG_dile 1 +#define BS_ACT_IDLE 1 + +/* skip notch & dummy */ +#define SKIP_NOTCH_START 5 +#define SKIP_NOTCH_END 10 +/* TX+SKIP_NOTCH_START */ +#define SKIP_DUMMY_START 23 +/* TX+SKIP_NOTCH_END*/ +#define SKIP_DUMMY_END 28 + + +#define NOISEFRAME (BS_NOISE+1) +#define NORMAL_IDLE_RAWDATA_NOISEFRAME 10 +#define LPWUG_RAWDATAFRAME 1 +#define LPWUG_NOISEFRAME 1 +#define LPWUG_IDLE_RAWDATAFRAME 1 +#define LPWUG_IDLE_NOISEFRAME 1 + +#define OTHERSFRAME 2 + +#define UNIFMAX 500 + + +/*Himax MP Password*/ +#define PWD_OPEN_START 0x77 +#define PWD_OPEN_END 0x88 +#define PWD_SHORT_START 0x11 +#define PWD_SHORT_END 0x33 +#define PWD_RAWDATA_START 0x00 +#define PWD_RAWDATA_END 0x99 +#define PWD_NOISE_START 0x00 +#define PWD_NOISE_END 0x99 +#define PWD_SORTING_START 0xAA +#define PWD_SORTING_END 0xCC + + +#define PWD_ACT_IDLE_START 0x22 +#define PWD_ACT_IDLE_END 0x44 + + + +#define PWD_LPWUG_START 0x55 +#define PWD_LPWUG_END 0x66 + +#define PWD_LPWUG_IDLE_START 0x50 +#define PWD_LPWUG_IDLE_END 0x60 + + +/*Himax DataType*/ +#define DATA_SORTING 0x0A +#define DATA_OPEN 0x0B +#define DATA_MICRO_OPEN 0x0C +#define DATA_SHORT 0x0A +#define DATA_RAWDATA 0x0A +#define DATA_NOISE 0x0F +#define DATA_BACK_NORMAL 0x00 +#define DATA_LPWUG_RAWDATA 0x0C +#define DATA_LPWUG_NOISE 0x0F +#define DATA_ACT_IDLE_RAWDATA 0x0A +#define DATA_ACT_IDLE_NOISE 0x0F +#define DATA_LPWUG_IDLE_RAWDATA 0x0A +#define DATA_LPWUG_IDLE_NOISE 0x0F + +/*Himax Data Ready Password*/ +#define Data_PWD0 0xA5 +#define Data_PWD1 0x5A + +/* ASCII format */ +#define ASCII_LF (0x0A) +#define ASCII_CR (0x0D) +#define ASCII_COMMA (0x2C) +#define ASCII_ZERO (0x30) +#define CHAR_EL '\0' +#define CHAR_NL '\n' +#define ACSII_SPACE (0x20) +/* INSOECTION Setting */ +/*extern int HX_CRITERIA_ITEM;*/ +/*extern int HX_CRITERIA_SIZE;*/ +/*extern char *g_file_path;*/ +/*extern char *g_rslt_data;*/ +void himax_inspection_init(void); +extern int *g_inspt_crtra_flag; + +enum THP_INSPECTION_ENUM { + HIMAX_INSPECTION_OPEN, + HIMAX_INSPECTION_MICRO_OPEN, + HIMAX_INSPECTION_SHORT, + HIMAX_INSPECTION_RAWDATA, + HIMAX_INSPECTION_BPN_RAWDATA, + HIMAX_INSPECTION_WEIGHT_NOISE, + HIMAX_INSPECTION_ABS_NOISE, + HIMAX_INSPECTION_SORTING, + HIMAX_INSPECTION_BACK_NORMAL, + + HIMAX_INSPECTION_GAPTEST_RAW, + + HIMAX_INSPECTION_ACT_IDLE_RAWDATA, + HIMAX_INSPECTION_ACT_IDLE_NOISE, + + HIMAX_INSPECTION_LPWUG_RAWDATA, + HIMAX_INSPECTION_LPWUG_NOISE, + HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA, + HIMAX_INSPECTION_LPWUG_IDLE_NOISE, + +}; + + +enum HX_CRITERIA_ENUM { + IDX_RAWMIN = 0, + IDX_RAWMAX, + IDX_BPN_RAWMIN, + IDX_BPN_RAWMAX, + IDX_SHORTMIN, + IDX_SHORTMAX, + IDX_OPENMIN, + IDX_OPENMAX, + IDX_M_OPENMIN, + IDX_M_OPENMAX, + IDX_WT_NOISEMIN, + IDX_WT_NOISEMAX, + IDX_ABS_NOISEMIN, + IDX_ABS_NOISEMAX, + IDX_SORTMIN, + IDX_SORTMAX, + + IDX_GAP_HOR_RAWMAX, + IDX_GAP_HOR_RAWMIN, + IDX_GAP_VER_RAWMAX, + IDX_GAP_VER_RAWMIN, + + IDX_ACT_IDLE_NOISE_MIN, + IDX_ACT_IDLE_NOISE_MAX, + IDX_ACT_IDLE_RAWDATA_MIN, + IDX_ACT_IDLE_RAWDATA_MAX, + + IDX_LPWUG_NOISE_MIN, + IDX_LPWUG_NOISE_MAX, + IDX_LPWUG_RAWDATA_MIN, + IDX_LPWUG_RAWDATA_MAX, + + IDX_LPWUG_IDLE_NOISE_MIN, + IDX_LPWUG_IDLE_NOISE_MAX, + IDX_LPWUG_IDLE_RAWDATA_MIN, + IDX_LPWUG_IDLE_RAWDATA_MAX, + +}; + +/* Error code of Inspection */ +enum HX_INSPECT_ERR_ENUM { + HX_INSPECT_OK = 0x00000000, /* OK */ + HX_INSPECT_EFILE = 0x00000001, /*Criteria file error*/ + HX_INSPECT_ERAW = 0x00000002, /* Raw data error */ + HX_INSPECT_WT_ENOISE = 0x00000004, /* Noise error */ + HX_INSPECT_ABS_ENOISE = 0x00000008, /* Noise error */ + HX_INSPECT_EOPEN = 0x00000010, /* Sensor open error */ + HX_INSPECT_EMOPEN = 0x00000020, /* Sensor micro open error */ + HX_INSPECT_ESHORT = 0x00000040, /* Sensor short error */ + + HX_INSPECT_EGAP_RAW = 0x00000080, /* Raw Data GAP */ + + HX_INSPECT_EACT_IDLE_RAW = 0x00000100, /* ACT_IDLE RAW ERROR */ + HX_INSPECT_EACT_IDLE_NOISE = 0x00000200, /* ACT_IDLE NOISE ERROR */ + + HX_INSPECT_ELPWUG_RAW = 0x00000400, /* LPWUG RAW ERROR */ + HX_INSPECT_ELPWUG_NOISE = 0x00000800, /* LPWUG NOISE ERROR */ + HX_INSPECT_ELPWUG_IDLE_RAW = 0x00001000, /* LPWUG IDLE RAW ERROR */ + HX_INSPECT_ELPWUG_IDLE_NOISE = 0x00002000, /* LPWUG IDLE NOISE ERROR */ + + HX_INSPECT_EGETRAW = 0x00004000, /* Get raw data errors */ + HX_INSPECT_MEMALLCTFAIL = 0x00008000, /* Get raw data errors */ + /*HX_INSPECT_ESPI = , */ /*SPI communication error,no use*/ +}; + +extern void himax_inspect_data_clear(void); +extern char *hx_self_test_file_name; diff --git a/drivers/input/touchscreen/himax_v2_mmi/himax_modular_table.h b/drivers/input/touchscreen/himax_v2_mmi/himax_modular_table.h new file mode 100644 index 000000000000..180d823d581f --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/himax_modular_table.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for modularize functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef __HIMAX_MODULAR_TABLE_H__ +#define __HIMAX_MODULAR_TABLE_H__ + +#include "himax_ic_core.h" + +#define TO_STR(VAR) #VAR +#define DECLARE(sym) struct himax_chip_entry sym; \ + EXPORT_SYMBOL(sym) + +static const char * const himax_ksym_lookup[] = { + #if defined(HX_MOD_KSYM_HX852xG) + TO_STR(HX_MOD_KSYM_HX852xG), + #endif + #if defined(HX_MOD_KSYM_HX852xH) + TO_STR(HX_MOD_KSYM_HX852xH), + #endif + #if defined(HX_MOD_KSYM_HX83102) + TO_STR(HX_MOD_KSYM_HX83102), + #endif + #if defined(HX_MOD_KSYM_HX83103) + TO_STR(HX_MOD_KSYM_HX83103), + #endif + #if defined(HX_MOD_KSYM_HX83106) + TO_STR(HX_MOD_KSYM_HX83106), + #endif + #if defined(HX_MOD_KSYM_HX83111) + TO_STR(HX_MOD_KSYM_HX83111), + #endif + #if defined(HX_MOD_KSYM_HX83112) + TO_STR(HX_MOD_KSYM_HX83112), + #endif + #if defined(HX_MOD_KSYM_HX83113) + TO_STR(HX_MOD_KSYM_HX83113), + #endif + #if defined(HX_MOD_KSYM_HX83191) + TO_STR(HX_MOD_KSYM_HX83191), + #endif + NULL +}; + +static struct himax_chip_entry *get_chip_entry_by_index(int32_t idx) +{ + return (void *)kallsyms_lookup_name(himax_ksym_lookup[idx]); +} + +enum modular_table { + MODULE_NOT_FOUND = -1, + MODULE_FOUND, + MODULE_EMPTY, +}; + +/* + * Return 1 when specified entry is empty, 0 when not, -1 when index error + */ +static int32_t isEmpty(int32_t idx) +{ + int32_t size = sizeof(himax_ksym_lookup) / sizeof(char *); + struct himax_chip_entry *entry; + + if (idx < 0 || idx >= size) + return MODULE_NOT_FOUND; + + entry = get_chip_entry_by_index(idx); + if (entry) + return (entry->core_chip_dt == NULL)?MODULE_EMPTY:MODULE_FOUND; + + return MODULE_NOT_FOUND; +} + +/* + * Search for created entry, if not existed, return 1st + * Return index of himax_ksym_lookup + */ +static int32_t himax_get_ksym_idx(void) +{ + int32_t i, first = -1; + int32_t size = sizeof(himax_ksym_lookup) / sizeof(char *); + struct himax_chip_entry *entry; + + I("%s: symtable size: %d\n", __func__, size); + for (i = 0; i < size; i++) { + if (himax_ksym_lookup[i] == NULL) + break; + + I("%s: %s\n", __func__, himax_ksym_lookup[i]); + entry = get_chip_entry_by_index(i); + if (entry) { + if (first < 0) + first = i; + if (isEmpty(i) == 0) + return i; + } + } + if (first >= 0) + return first; + /*incorrect use state, means no ic defined*/ + return MODULE_NOT_FOUND; +} + +#endif diff --git a/drivers/input/touchscreen/himax_v2_mmi/himax_platform.h b/drivers/input/touchscreen/himax_v2_mmi/himax_platform.h new file mode 100644 index 000000000000..924b4c8de25f --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/himax_platform.h @@ -0,0 +1,5 @@ +#ifdef __HIMAX_PLATFORM_SPI__ +#include "himax_platform_QCT_SPI.h" +#else +#include "himax_platform_QCT_I2C.h" +#endif diff --git a/drivers/input/touchscreen/himax_v2_mmi/himax_platform_QCT_I2C.c b/drivers/input/touchscreen/himax_v2_mmi/himax_platform_QCT_I2C.c new file mode 100644 index 000000000000..316e6914d2f8 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/himax_platform_QCT_I2C.c @@ -0,0 +1,972 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for QCT platform + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_platform.h" +#include "himax_common.h" + +int i2c_error_count; +int irq_enable_count; +EXPORT_SYMBOL(irq_enable_count); + +int himax_dev_set(struct himax_ts_data *ts) +{ + int ret = 0; + + + ts->input_dev = input_allocate_device(); + + if (ts->input_dev == NULL) { + ret = -ENOMEM; + E("%s: Failed to allocate input device-input_dev\n", __func__); + return ret; + } + + ts->input_dev->name = "himax-touchscreen"; +#if defined(HX_PEN_FUNC_EN) + ts->hx_pen_dev = input_allocate_device(); + + if (ts->hx_pen_dev == NULL) { + ret = -ENOMEM; + E("%s: Failed to allocate input device-hx_pen_dev\n", __func__); + return ret; + } + + ts->hx_pen_dev->name = "himax-pen"; +#endif + return ret; +} +int himax_input_register_device(struct input_dev *input_dev) +{ + return input_register_device(input_dev); +} + +#if defined(HX_PLATFOME_DEFINE_KEY) +void himax_platform_key(void) +{ + I("Nothing to be done! Plz cancel it!\n"); +} +#endif + +void himax_vk_parser(struct device_node *dt, + struct himax_i2c_platform_data *pdata) +{ + u32 data = 0; + uint8_t cnt = 0, i = 0; + uint32_t coords[4] = {0}; + struct device_node *node, *pp = NULL; + struct himax_virtual_key *vk; + + + node = of_parse_phandle(dt, "virtualkey", 0); + + if (node == NULL) { + I(" DT-No vk info in DT\n"); + } else { + while ((pp = of_get_next_child(node, pp))) + cnt++; + + if (!cnt) + return; + + vk = kcalloc(cnt, sizeof(struct himax_virtual_key), GFP_KERNEL); + pp = NULL; + + while ((pp = of_get_next_child(node, pp))) { + if (of_property_read_u32(pp, "idx", &data) == 0) + vk[i].index = data; + + if (of_property_read_u32_array(pp, "range", coords, 4) == 0) { + vk[i].x_range_min = coords[0], vk[i].x_range_max = coords[1]; + vk[i].y_range_min = coords[2], vk[i].y_range_max = coords[3]; + } else { + I(" range faile\n"); + } + + i++; + } + + pdata->virtual_key = vk; + + for (i = 0; i < cnt; i++) + I(" vk[%d] idx:%d x_min:%d, y_max:%d\n", i, pdata->virtual_key[i].index, + pdata->virtual_key[i].x_range_min, pdata->virtual_key[i].y_range_max); + } +} + +int himax_parse_dt(struct himax_ts_data *ts, + struct himax_i2c_platform_data *pdata) +{ + int rc, coords_size = 0; + uint32_t coords[4] = {0}; + struct property *prop; + struct device_node *dt = private_ts->client->dev.of_node; + u32 data = 0; + + + prop = of_find_property(dt, "himax,panel-coords", NULL); + + if (prop) { + coords_size = prop->length / sizeof(u32); + + if (coords_size != 4) + D(" %s:Invalid panel coords size %d\n", __func__, coords_size); + } + + if (of_property_read_u32_array(dt, "himax,panel-coords", coords, coords_size) == 0) { + pdata->abs_x_min = coords[0], pdata->abs_x_max = (coords[1] - 1); + pdata->abs_y_min = coords[2], pdata->abs_y_max = (coords[3] - 1); + I(" DT-%s:panel-coords = %d, %d, %d, %d\n", __func__, pdata->abs_x_min, + pdata->abs_x_max, pdata->abs_y_min, pdata->abs_y_max); + } + + prop = of_find_property(dt, "himax,display-coords", NULL); + + if (prop) { + coords_size = prop->length / sizeof(u32); + + if (coords_size != 4) + D(" %s:Invalid display coords size %d\n", __func__, coords_size); + } + + rc = of_property_read_u32_array(dt, "himax,display-coords", coords, coords_size); + + if (rc && (rc != -EINVAL)) { + D(" %s:Fail to read display-coords %d\n", __func__, rc); + return rc; + } + + pdata->screenWidth = coords[1]; + pdata->screenHeight = coords[3]; + I(" DT-%s:display-coords = (%d, %d)\n", __func__, pdata->screenWidth, + pdata->screenHeight); + pdata->gpio_irq = of_get_named_gpio(dt, "himax,irq-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_irq)) + I(" DT:gpio_irq value is not valid\n"); + +#ifdef HX_SHARP_ENABLE_PON + pdata->gpio_reset = 262; +#else + pdata->gpio_reset = of_get_named_gpio(dt, "himax,rst-gpio", 0); +#endif + + if (!gpio_is_valid(pdata->gpio_reset)) + I(" DT:gpio_rst value is not valid\n"); + +#ifdef HX_SHARP_ENABLE_PON + pdata->gpio_pon = of_get_named_gpio(dt, "himax,pon-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_pon)) + I(" DT:gpio_pon value is not valid\n"); + + + I(" DT:pdata->gpio_pon=%d\n", pdata->gpio_pon); + +#endif + + pdata->gpio_3v3_en = of_get_named_gpio(dt, "himax,3v3-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_3v3_en)) + I(" DT:gpio_3v3_en value is not valid\n"); + + I(" DT:gpio_irq=%d, gpio_rst=%d, gpio_3v3_en=%d\n", pdata->gpio_irq, pdata->gpio_reset, pdata->gpio_3v3_en); + + if (of_property_read_u32(dt, "report_type", &data) == 0) { + pdata->protocol_type = data; + I(" DT:protocol_type=%d\n", pdata->protocol_type); + } + + himax_vk_parser(dt, pdata); + return 0; +} +EXPORT_SYMBOL(himax_parse_dt); + +int himax_bus_read(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry) +{ + int retry; + struct i2c_client *client = private_ts->client; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &command, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + } + }; + mutex_lock(&private_ts->rw_lock); + + for (retry = 0; retry < toRetry; retry++) { + if (i2c_transfer(client->adapter, msg, 2) == 2) + break; + + /*msleep(20);*/ + } + + if (retry == toRetry) { + E("%s: i2c_read_block retry over %d\n", + __func__, toRetry); + i2c_error_count = toRetry; + mutex_unlock(&private_ts->rw_lock); + return -EIO; + } + + mutex_unlock(&private_ts->rw_lock); + return 0; +} +EXPORT_SYMBOL(himax_bus_read); + +int himax_bus_write(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry) +{ + int retry/*, loop_i*/; + uint8_t buf[length + 1]; + struct i2c_client *client = private_ts->client; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = length + 1, + .buf = buf, + } + }; + + + mutex_lock(&private_ts->rw_lock); + buf[0] = command; + memcpy(buf + 1, data, length); + + for (retry = 0; retry < toRetry; retry++) { + if (i2c_transfer(client->adapter, msg, 1) == 1) + break; + + /*msleep(20);*/ + } + + if (retry == toRetry) { + E("%s: i2c_write_block retry over %d\n", + __func__, toRetry); + i2c_error_count = toRetry; + mutex_unlock(&private_ts->rw_lock); + return -EIO; + } + + mutex_unlock(&private_ts->rw_lock); + return 0; +} +EXPORT_SYMBOL(himax_bus_write); + +int himax_bus_write_command(uint8_t command, uint8_t toRetry) +{ + return himax_bus_write(command, NULL, 0, toRetry); +} + +int himax_bus_master_write(uint8_t *data, uint32_t length, uint8_t toRetry) +{ + int retry/*, loop_i*/; + uint8_t buf[length]; + struct i2c_client *client = private_ts->client; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = length, + .buf = buf, + } + }; + + + mutex_lock(&private_ts->rw_lock); + memcpy(buf, data, length); + + for (retry = 0; retry < toRetry; retry++) { + if (i2c_transfer(client->adapter, msg, 1) == 1) + break; + + /*msleep(20);*/ + } + + if (retry == toRetry) { + E("%s: i2c_write_block retry over %d\n", + __func__, toRetry); + i2c_error_count = toRetry; + mutex_unlock(&private_ts->rw_lock); + return -EIO; + } + + mutex_unlock(&private_ts->rw_lock); + return 0; +} +EXPORT_SYMBOL(himax_bus_master_write); + +void himax_int_enable(int enable) +{ + int irqnum = 0; + + + irqnum = private_ts->client->irq; + + if (enable == 1 && irq_enable_count == 0) { + enable_irq(irqnum); + irq_enable_count = 1; + private_ts->irq_enabled = 1; + } else if (enable == 0 && irq_enable_count == 1) { + disable_irq_nosync(irqnum); + irq_enable_count = 0; + private_ts->irq_enabled = 0; + } + + I("irq_enable_count = %d\n", irq_enable_count); +} +EXPORT_SYMBOL(himax_int_enable); + +#ifdef HX_RST_PIN_FUNC +void himax_rst_gpio_set(int pinnum, uint8_t value) +{ + gpio_direction_output(pinnum, value); +} +EXPORT_SYMBOL(himax_rst_gpio_set); +#endif + +uint8_t himax_int_gpio_read(int pinnum) +{ + return gpio_get_value(pinnum); +} + +#if defined(CONFIG_HMX_DB) +static int himax_regulator_configure(struct himax_i2c_platform_data *pdata) +{ + int retval; + struct i2c_client *client = private_ts->client; + + + pdata->vcc_dig = regulator_get(&client->dev, "vdd"); + + if (IS_ERR(pdata->vcc_dig)) { + E("%s: Failed to get regulator vdd\n", + __func__); + retval = PTR_ERR(pdata->vcc_dig); + return retval; + } + + pdata->vcc_ana = regulator_get(&client->dev, "avdd"); + + if (IS_ERR(pdata->vcc_ana)) { + E("%s: Failed to get regulator avdd\n", + __func__); + retval = PTR_ERR(pdata->vcc_ana); + regulator_put(pdata->vcc_dig); + return retval; + } + + return 0; +}; + +static void himax_regulator_deinit(struct himax_i2c_platform_data *pdata) +{ + I("%s: entered.\n", __func__); + + if (!IS_ERR(pdata->vcc_ana)) + regulator_put(pdata->vcc_ana); + + if (!IS_ERR(pdata->vcc_dig)) + regulator_put(pdata->vcc_dig); + + I("%s: regulator put, completed.\n", __func__); +}; + +static int himax_power_on(struct himax_i2c_platform_data *pdata, bool on) +{ + int retval; + + if (on) { + retval = regulator_enable(pdata->vcc_dig); + + if (retval) { + E("%s: Failed to enable regulator vdd\n", + __func__); + return retval; + } + + /*msleep(100);*/ + usleep_range(1000, 1001); + retval = regulator_enable(pdata->vcc_ana); + + if (retval) { + E("%s: Failed to enable regulator avdd\n", + __func__); + regulator_disable(pdata->vcc_dig); + return retval; + } + } else { + regulator_disable(pdata->vcc_dig); + regulator_disable(pdata->vcc_ana); + } + + return 0; +} + +int himax_gpio_power_config(struct himax_i2c_platform_data *pdata) +{ + int error; + struct i2c_client *client = private_ts->client; + + + error = himax_regulator_configure(pdata); + + if (error) { + E("Failed to intialize hardware\n"); + goto err_regulator_not_on; + } + +#ifdef HX_RST_PIN_FUNC + + if (gpio_is_valid(pdata->gpio_reset)) { + /* configure touchscreen reset out gpio */ + error = gpio_request(pdata->gpio_reset, "hmx_reset_gpio"); + + if (error) { + E("unable to request gpio [%d]\n", pdata->gpio_reset); + goto err_regulator_on; + } + + error = gpio_direction_output(pdata->gpio_reset, 0); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_req; + } + } + +#endif + error = himax_power_on(pdata, true); + + if (error) { + E("Failed to power on hardware\n"); + goto err_power_on; + } + + if (gpio_is_valid(pdata->gpio_irq)) { + /* configure touchscreen irq gpio */ + error = gpio_request(pdata->gpio_irq, "hmx_gpio_irq"); + + if (error) { + E("unable to request gpio [%d]\n", + pdata->gpio_irq); + goto err_req_irq_gpio; + } + + error = gpio_direction_input(pdata->gpio_irq); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_irq); + goto err_set_gpio_irq; + } + + client->irq = gpio_to_irq(pdata->gpio_irq); + private_ts->hx_irq = client->irq; + } else { + E("irq gpio not provided\n"); + goto err_req_irq_gpio; + } + + /*msleep(20);*/ + usleep_range(2000, 2001); +#ifdef HX_RST_PIN_FUNC + + if (gpio_is_valid(pdata->gpio_reset)) { + error = gpio_direction_output(pdata->gpio_reset, 1); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_set_gpio_irq; + } + } + +#endif + return 0; +err_set_gpio_irq: + + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); + +err_req_irq_gpio: + himax_power_on(pdata, false); +err_power_on: +#ifdef HX_RST_PIN_FUNC +err_gpio_reset_req: + if (gpio_is_valid(pdata->gpio_reset)) + gpio_free(pdata->gpio_reset); + +err_regulator_on: +#endif + himax_regulator_deinit(pdata); +err_regulator_not_on: + return error; +} + +#else +int himax_gpio_power_config(struct himax_i2c_platform_data *pdata) +{ + int error = 0; + struct i2c_client *client = private_ts->client; +#ifdef HX_RST_PIN_FUNC + + if (pdata->gpio_reset >= 0) { + error = gpio_request(pdata->gpio_reset, "himax-reset"); + + if (error < 0) { + E("%s: request reset pin failed\n", __func__); + goto err_gpio_reset_req; + } + + error = gpio_direction_output(pdata->gpio_reset, 0); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_dir; + } + } + +#endif + +#ifdef HX_SHARP_ENABLE_PON + if (gpio_is_valid(pdata->gpio_pon)) { + error = gpio_request(pdata->gpio_pon, "hmx_pon_gpio"); + + if (error) { + E("unable to request scl gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_req; + } + + error = gpio_direction_output(pdata->gpio_pon, 0); + + I("gpio_pon LOW [%d]\n", pdata->gpio_pon); + + if (error) { + E("unable to set direction for pon gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_dir; + } + } +#endif + + + if (pdata->gpio_3v3_en >= 0) { + error = gpio_request(pdata->gpio_3v3_en, "himax-3v3_en"); + + if (error < 0) { + E("%s: request 3v3_en pin failed\n", __func__); + goto err_gpio_3v3_req; + } + + gpio_direction_output(pdata->gpio_3v3_en, 1); + I("3v3_en set 1 get pin = %d\n", gpio_get_value(pdata->gpio_3v3_en)); + } + + if (gpio_is_valid(pdata->gpio_irq)) { + /* configure touchscreen irq gpio */ + error = gpio_request(pdata->gpio_irq, "himax_gpio_irq"); + + if (error) { + E("unable to request gpio [%d]\n", pdata->gpio_irq); + goto err_gpio_irq_req; + } + + error = gpio_direction_input(pdata->gpio_irq); + + if (error) { + E("unable to set direction for gpio [%d]\n", pdata->gpio_irq); + goto err_gpio_irq_set_input; + } + + client->irq = gpio_to_irq(pdata->gpio_irq); + private_ts->hx_irq = client->irq; + } else { + E("irq gpio not provided\n"); + goto err_gpio_irq_req; + } +#ifdef HX_SHARP_ENABLE_PON + msleep(20); +#else + usleep_range(2000, 2001); +#endif + +#ifdef HX_RST_PIN_FUNC + + if (pdata->gpio_reset >= 0) { + error = gpio_direction_output(pdata->gpio_reset, 1); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_set_high; + } + } +#endif + +#ifdef HX_SHARP_ENABLE_PON + msleep(800); + + if (gpio_is_valid(pdata->gpio_pon)) { + + error = gpio_direction_output(pdata->gpio_pon, 1); + + I("gpio_pon HIGH [%d]\n", pdata->gpio_pon); + + if (error) { + E("gpio_pon unable to set direction for gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_set_high; + } + } +#endif + return error; + +#ifdef HX_SHARP_ENABLE_PON +err_gpio_pon_set_high: +#endif +#ifdef HX_RST_PIN_FUNC +err_gpio_reset_set_high: +#endif +err_gpio_irq_set_input: + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); +err_gpio_irq_req: + if (pdata->gpio_3v3_en >= 0) + gpio_free(pdata->gpio_3v3_en); +err_gpio_3v3_req: +#ifdef HX_SHARP_ENABLE_PON +err_gpio_pon_dir: + if (gpio_is_valid(pdata->gpio_pon)) + gpio_free(pdata->gpio_pon); +err_gpio_pon_req: +#endif +#ifdef HX_RST_PIN_FUNC +err_gpio_reset_dir: + if (pdata->gpio_reset >= 0) + gpio_free(pdata->gpio_reset); +err_gpio_reset_req: +#endif + return error; +} + +#endif + +void himax_gpio_power_deconfig(struct himax_i2c_platform_data *pdata) +{ + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); + +#ifdef HX_RST_PIN_FUNC + if (gpio_is_valid(pdata->gpio_reset)) + gpio_free(pdata->gpio_reset); +#endif + +#if defined(CONFIG_HMX_DB) + himax_power_on(pdata, false); + himax_regulator_deinit(pdata); +#else + if (pdata->gpio_3v3_en >= 0) + gpio_free(pdata->gpio_3v3_en); + +#ifdef HX_SHARP_ENABLE_PON + if (gpio_is_valid(pdata->gpio_pon)) + gpio_free(pdata->gpio_pon); +#endif + +#endif +} + +static void himax_ts_isr_func(struct himax_ts_data *ts) +{ + himax_ts_work(ts); +} + +irqreturn_t himax_ts_thread(int irq, void *ptr) +{ + himax_ts_isr_func((struct himax_ts_data *)ptr); + + return IRQ_HANDLED; +} + +static void himax_ts_work_func(struct work_struct *work) +{ + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work); + + + himax_ts_work(ts); +} + +int himax_int_register_trigger(void) +{ + int ret = 0; + struct himax_ts_data *ts = private_ts; + struct i2c_client *client = private_ts->client; + + if (ic_data->HX_INT_IS_EDGE) { + I("%s edge triiger falling\n ", __func__); + ret = request_threaded_irq(client->irq, NULL, himax_ts_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, ts); + } + + else { + I("%s level trigger low\n ", __func__); + ret = request_threaded_irq(client->irq, NULL, himax_ts_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, ts); + } + + return ret; +} + +int himax_int_en_set(void) +{ + int ret = NO_ERR; + + + ret = himax_int_register_trigger(); + return ret; +} + +int himax_ts_register_interrupt(void) +{ + struct himax_ts_data *ts = private_ts; + struct i2c_client *client = private_ts->client; + int ret = 0; + + + ts->irq_enabled = 0; + + /* Work functon */ + if (client->irq && private_ts->hx_irq) {/*INT mode*/ + ts->use_irq = 1; + ret = himax_int_register_trigger(); + + if (ret == 0) { + ts->irq_enabled = 1; + irq_enable_count = 1; + I("%s: irq enabled at gpio: %d\n", __func__, client->irq); +#ifdef HX_SMART_WAKEUP + irq_set_irq_wake(client->irq, 1); +#endif + } else { + ts->use_irq = 0; + E("%s: request_irq failed\n", __func__); + } + } else { + I("%s: client->irq is empty, use polling mode.\n", __func__); + } + + if (!ts->use_irq) {/*if use polling mode need to disable HX_ESD_RECOVERY function*/ + ts->himax_wq = create_singlethread_workqueue("himax_touch"); + INIT_WORK(&ts->work, himax_ts_work_func); + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = himax_ts_timer_func; + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + I("%s: polling mode enabled\n", __func__); + } + + return ret; +} + +int himax_ts_unregister_interrupt(void) +{ + struct himax_ts_data *ts = private_ts; + int ret = 0; + + + I("%s: entered.\n", __func__); + + /* Work functon */ + if (private_ts->hx_irq && ts->use_irq) {/*INT mode*/ +#ifdef HX_SMART_WAKEUP + irq_set_irq_wake(ts->hx_irq, 0); +#endif + free_irq(ts->hx_irq, ts); + I("%s: irq disabled at qpio: %d\n", __func__, private_ts->hx_irq); + } + + if (!ts->use_irq) {/*if use polling mode need to disable HX_ESD_RECOVERY function*/ + hrtimer_cancel(&ts->timer); + cancel_work_sync(&ts->work); + if (ts->himax_wq != NULL) + destroy_workqueue(ts->himax_wq); + I("%s: polling mode destroyed", __func__); + } + + return ret; +} + +static int himax_common_suspend(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + + + I("%s: enter\n", __func__); + himax_chip_common_suspend(ts); + return 0; +} + +static int himax_common_resume(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + + + I("%s: enter\n", __func__); + himax_chip_common_resume(ts); + return 0; +} + +#if defined(CONFIG_FB) +int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct himax_ts_data *ts = + container_of(self, struct himax_ts_data, fb_notif); + + + I(" %s\n", __func__); + + if (evdata && evdata->data && event == FB_EVENT_BLANK && ts && + ts->client) { + blank = evdata->data; + + switch (*blank) { + case FB_BLANK_UNBLANK: + himax_common_resume(&ts->client->dev); + break; + case FB_BLANK_POWERDOWN: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_NORMAL: + himax_common_suspend(&ts->client->dev); + break; + } + } + + return 0; +} +#endif + +int himax_chip_common_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + struct himax_ts_data *ts; + + I("%s:Enter\n", __func__); + + /* Check I2C functionality */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + E("%s: i2c check functionality error\n", __func__); + ret = -ENODEV; + goto err_check_functionality_failed; + } + + ts = kzalloc(sizeof(struct himax_ts_data), GFP_KERNEL); + if (ts == NULL) { + E("%s: allocate himax_ts_data failed\n", __func__); + ret = -ENOMEM; + goto err_alloc_data_failed; + } + + i2c_set_clientdata(client, ts); + ts->client = client; + ts->dev = &client->dev; + mutex_init(&ts->rw_lock); + private_ts = ts; + + ret = himax_chip_common_init(); + +err_alloc_data_failed: +err_check_functionality_failed: + + return ret; +} + +int himax_chip_common_remove(struct i2c_client *client) +{ + if (g_hx_chip_inited) + himax_chip_common_deinit(); + + return 0; +} + +static const struct i2c_device_id himax_common_ts_id[] = { + {HIMAX_common_NAME, 0 }, + {} +}; + +static const struct dev_pm_ops himax_common_pm_ops = { +#if (!defined(CONFIG_FB)) + .suspend = himax_common_suspend, + .resume = himax_common_resume, +#endif +}; + +#ifdef CONFIG_OF +static const struct of_device_id himax_match_table[] = { + {.compatible = "himax,hxcommon" }, + {}, +}; +#else +#define himax_match_table NULL +#endif + +static struct i2c_driver himax_common_driver = { + .id_table = himax_common_ts_id, + .probe = himax_chip_common_probe, + .remove = himax_chip_common_remove, + .driver = { + .name = HIMAX_common_NAME, + .owner = THIS_MODULE, + .of_match_table = himax_match_table, +#ifdef CONFIG_PM + .pm = &himax_common_pm_ops, +#endif + }, +}; + +static int __init himax_common_init(void) +{ + I("Himax common touch panel driver init\n"); + D("Himax check double loading\n"); + if (g_mmi_refcnt++ > 0) { + + I("Himax driver has been loaded! ignoring....\n"); + + return 0; + } + i2c_add_driver(&himax_common_driver); + + return 0; +} + +static void __exit himax_common_exit(void) +{ + i2c_del_driver(&himax_common_driver); +} + +module_init(himax_common_init); +module_exit(himax_common_exit); + +MODULE_DESCRIPTION("Himax_common driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/touchscreen/himax_v2_mmi/himax_platform_QCT_I2C.h b/drivers/input/touchscreen/himax_v2_mmi/himax_platform_QCT_I2C.h new file mode 100644 index 000000000000..f45c28f9e9b8 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/himax_platform_QCT_I2C.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for QCT platform + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef HIMAX_PLATFORM_H +#define HIMAX_PLATFORM_H + +#include "himax_common.h" + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_HMX_DB) + #include +#endif + + +#define HIMAX_I2C_RETRY_TIMES 3 + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG) +#define D(x...) pr_debug("[HXTP] " x) +#define I(x...) pr_info("[HXTP] " x) +#define W(x...) pr_warn("[HXTP][WARNING] " x) +#define E(x...) pr_err("[HXTP][ERROR] " x) +#define DIF(x...) \ +do { \ + if (debug_flag) \ + pr_debug("[HXTP][DEBUG] " x) \ + } while (0) +#else + +#define D(x...) +#define I(x...) +#define W(x...) +#define E(x...) +#define DIF(x...) +#endif + +#if defined(CONFIG_HMX_DB) + + /* Analog voltage @2.7 V */ + #define HX_VTG_MIN_UV 2700000 + #define HX_VTG_MAX_UV 3300000 + #define HX_ACTIVE_LOAD_UA 15000 + #define HX_LPM_LOAD_UA 10 + /* Digital voltage @1.8 V */ + #define HX_VTG_DIG_MIN_UV 1800000 + #define HX_VTG_DIG_MAX_UV 1800000 + #define HX_ACTIVE_LOAD_DIG_UA 10000 + #define HX_LPM_LOAD_DIG_UA 10 + #define HX_I2C_VTG_MIN_UV 1800000 + #define HX_I2C_VTG_MAX_UV 1800000 + #define HX_I2C_LOAD_UA 10000 + #define HX_I2C_LPM_LOAD_UA 10 + +#endif + +#define HIMAX_common_NAME "himax_tp" +#define HIMAX_I2C_ADDR 0x48 +#define INPUT_DEV_NAME "himax-touchscreen" + +struct himax_i2c_platform_data { + int abs_x_min; + int abs_x_max; + int abs_x_fuzz; + int abs_y_min; + int abs_y_max; + int abs_y_fuzz; + int abs_pressure_min; + int abs_pressure_max; + int abs_pressure_fuzz; + int abs_width_min; + int abs_width_max; + int screenWidth; + int screenHeight; + uint8_t fw_version; + uint8_t tw_id; + uint8_t powerOff3V3; + uint8_t cable_config[2]; + uint8_t protocol_type; + int gpio_irq; + int gpio_reset; + int gpio_3v3_en; + int gpio_pon; + int (*power)(int on); + void (*reset)(void); + struct himax_virtual_key *virtual_key; + struct kobject *vk_obj; + struct kobj_attribute *vk2Use; + int hx_config_size; + +#if defined(CONFIG_HMX_DB) + bool i2c_pull_up; + bool digital_pwr_regulator; + int reset_gpio; + u32 reset_gpio_flags; + int irq_gpio; + u32 irq_gpio_flags; + struct regulator *vcc_ana; /* For Dragon Board */ + struct regulator *vcc_dig; /* For Dragon Board */ + struct regulator *vcc_i2c; /* For Dragon Board */ + +#endif + +}; + +extern int irq_enable_count; +extern int himax_bus_read(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +extern int himax_bus_write(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +extern int himax_bus_write_command(uint8_t command, uint8_t toRetry); +extern int himax_bus_master_write(uint8_t *data, uint32_t length, uint8_t toRetry); +extern void himax_int_enable(int enable); +extern int himax_ts_register_interrupt(void); +int himax_ts_unregister_interrupt(void); +extern uint8_t himax_int_gpio_read(int pinnum); +extern int himax_gpio_power_config(struct himax_i2c_platform_data *pdata); +void himax_gpio_power_deconfig(struct himax_i2c_platform_data *pdata); + +#if defined(CONFIG_FB) + extern int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#endif + +extern void himax_ts_work(struct himax_ts_data *ts); +extern enum hrtimer_restart himax_ts_timer_func(struct hrtimer *timer); +extern int himax_chip_common_init(void); +extern void himax_chip_common_deinit(void); + +#endif + diff --git a/drivers/input/touchscreen/himax_v2_mmi/himax_platform_QCT_SPI.c b/drivers/input/touchscreen/himax_v2_mmi/himax_platform_QCT_SPI.c new file mode 100755 index 000000000000..9c216080b309 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/himax_platform_QCT_SPI.c @@ -0,0 +1,1125 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for QCT platform + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_platform.h" +#include "himax_common.h" + +int i2c_error_count; +int irq_enable_count; +EXPORT_SYMBOL(irq_enable_count); +struct spi_device *spi; + +static uint8_t *gBuffer; + +int himax_dev_set(struct himax_ts_data *ts) +{ + int ret = 0; + + ts->input_dev = input_allocate_device(); + + if (ts->input_dev == NULL) { + ret = -ENOMEM; + E("%s: Failed to allocate input device\n", __func__); + return ret; + } + + ts->input_dev->name = "himax-touchscreen"; + return ret; +} +int himax_input_register_device(struct input_dev *input_dev) +{ + return input_register_device(input_dev); +} + +#if defined(HX_PLATFOME_DEFINE_KEY) +void himax_platform_key(void) +{ + I("Nothing to be done! Plz cancel it!\n"); +} +#endif + +void himax_vk_parser(struct device_node *dt, + struct himax_i2c_platform_data *pdata) +{ + u32 data = 0; + uint8_t cnt = 0, i = 0; + uint32_t coords[4] = {0}; + struct device_node *node, *pp = NULL; + struct himax_virtual_key *vk; + + node = of_parse_phandle(dt, "virtualkey", 0); + + if (node == NULL) { + I(" DT-No vk info in DT\n"); + return; + } + while ((pp = of_get_next_child(node, pp))) + cnt++; + + if (!cnt) + return; + + vk = kcalloc(cnt, sizeof(struct himax_virtual_key), GFP_KERNEL); + if (vk == NULL) { + E("%s, vk init fail!\n", __func__); + return; + } + pp = NULL; + + while ((pp = of_get_next_child(node, pp))) { + if (of_property_read_u32(pp, "idx", &data) == 0) + vk[i].index = data; + + if (of_property_read_u32_array(pp, "range", coords, 4) == 0) { + vk[i].x_range_min = coords[0], vk[i].x_range_max = coords[1]; + vk[i].y_range_min = coords[2], vk[i].y_range_max = coords[3]; + } else { + I(" range faile\n"); + } + + i++; + } + + pdata->virtual_key = vk; + + for (i = 0; i < cnt; i++) + I(" vk[%d] idx:%d x_min:%d, y_max:%d\n", i, pdata->virtual_key[i].index, + pdata->virtual_key[i].x_range_min, pdata->virtual_key[i].y_range_max); + +} + +int himax_parse_dt(struct himax_ts_data *ts, + struct himax_i2c_platform_data *pdata) +{ + int rc, coords_size = 0; + int touch_info_size = 0; + uint32_t coords[4] = {0}; + uint32_t touch_info[8] = {0}; + struct property *prop; + struct device_node *chosen, *dt = ts->dev->of_node; + u32 data = 0; + + prop = of_find_property(dt, "himax,panel-coords", NULL); + + if (prop) { + coords_size = prop->length / sizeof(u32); + + if (coords_size != 4) + D(" %s:Invalid panel coords size %d\n", __func__, coords_size); + } + + if (of_property_read_u32_array(dt, "himax,panel-coords", coords, coords_size) == 0) { + pdata->abs_x_min = coords[0], pdata->abs_x_max = (coords[1] - 1); + pdata->abs_y_min = coords[2], pdata->abs_y_max = (coords[3] - 1); + I(" DT-%s:panel-coords = %d, %d, %d, %d\n", __func__, pdata->abs_x_min, + pdata->abs_x_max, pdata->abs_y_min, pdata->abs_y_max); + } + + prop = of_find_property(dt, "himax,display-coords", NULL); + + if (prop) { + coords_size = prop->length / sizeof(u32); + + if (coords_size != 4) + D(" %s:Invalid display coords size %d\n", __func__, coords_size); + } + + rc = of_property_read_u32_array(dt, "himax,display-coords", coords, coords_size); + + if (rc && (rc != -EINVAL)) { + D(" %s:Fail to read display-coords %d\n", __func__, rc); + return rc; + } + + pdata->screenWidth = coords[1]; + pdata->screenHeight = coords[3]; + I(" DT-%s:display-coords = (%d, %d)\n", __func__, pdata->screenWidth, + pdata->screenHeight); + pdata->gpio_irq = of_get_named_gpio(dt, "himax,irq-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_irq)) + I(" DT:gpio_irq value is not valid\n"); + + prop = of_find_property(dt, "himax,touch_info", NULL); + if (prop) { + touch_info_size = prop->length / sizeof(u32); + if (touch_info_size != 8) { + D(" %s:Invalid touch_info size %d\n", __func__, touch_info_size); + } else { + rc = of_property_read_u32_array(dt, "himax,touch_info", touch_info, touch_info_size); + if (rc && (rc != -EINVAL)) { + D(" %s:Fail to read touch_info %d\n", __func__, rc); + } else { + ic_data->HX_RX_NUM = touch_info[0]; + ic_data->HX_TX_NUM = touch_info[1]; + ic_data->HX_BT_NUM = touch_info[2]; + ic_data->HX_X_RES = touch_info[3]; + ic_data->HX_Y_RES = touch_info[4]; + ic_data->HX_MAX_PT = touch_info[5]; + ic_data->HX_XY_REVERSE = touch_info[6]; + ic_data->HX_INT_IS_EDGE = touch_info[7]; + D(" %s:success to read touch_info from dts \n", __func__); + } + } + } + + I(" DT-%s:touch_info: HX_RX_NUM = %d, HX_TX_NUM = %d, HX_BT_NUM = %d,HX_X_RES = %d,HX_Y_RES = %d, HX_MAX_PT = %d, HX_XY_REVERSE = %d, HX_INT_IS_EDGE = %d\n", __func__, + ic_data->HX_RX_NUM,ic_data->HX_TX_NUM,ic_data->HX_BT_NUM,ic_data->HX_X_RES,ic_data->HX_Y_RES,ic_data->HX_MAX_PT,ic_data->HX_XY_REVERSE,ic_data->HX_INT_IS_EDGE); + + + pdata->gpio_reset = of_get_named_gpio(dt, "himax,rst-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_reset)) + I(" DT:gpio_rst value is not valid\n"); + + pdata->gpio_3v3_en = of_get_named_gpio(dt, "himax,3v3-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_3v3_en)) + I(" DT:gpio_3v3_en value is not valid\n"); + + I(" DT:gpio_irq=%d, gpio_rst=%d, gpio_3v3_en=%d\n", pdata->gpio_irq, pdata->gpio_reset, pdata->gpio_3v3_en); + + if (of_property_read_u32(dt, "report_type", &data) == 0) { + pdata->protocol_type = data; + I(" DT:protocol_type=%d\n", pdata->protocol_type); + } + + ts->vdd_1v8_always_on = of_property_read_bool(dt, "himax,vdd_1v8_always_on"); + if (ts->vdd_1v8_always_on){ + I(" DT:vdd_1v8_always_on=%d\n", ts->vdd_1v8_always_on); + } + + himax_vk_parser(dt, pdata); + + memset(pdata->panel_supplier, '\0', 16 * sizeof(char)); + chosen = of_find_node_by_name(NULL, "chosen"); + if (chosen) { + const char *supplier; + char *s, *d; + int rc; + + rc = of_property_read_string(chosen, "mmi,panel_name", + (const char **)&supplier); + if (rc) { + I("%s: cannot read mmi,panel_name %d\n", + __func__, rc); + } else { + I( "%s: mmi,panel_name %s\n", __func__, supplier); + + /* skip dsi_ part */ + s = (char *)supplier; + d = pdata->panel_supplier; + while (*s != '_') *d++ = *s++; + + I("%s: panel-supplier %s\n", __func__, pdata->panel_supplier); + } + of_node_put(chosen); + } + + return 0; +} +EXPORT_SYMBOL(himax_parse_dt); + +static ssize_t himax_spi_sync(struct himax_ts_data *ts, struct spi_message *message) +{ + int status; + + status = spi_sync(ts->spi, message); + + if (status == 0) { + status = message->status; + if (status == 0) + status = message->actual_length; + } + return status; +} + +static int himax_spi_read(uint8_t *command, uint8_t command_len, uint8_t *data, uint32_t length, uint8_t toRetry) +{ + struct spi_message message; + struct spi_transfer xfer[2]; + int retry; + int error; + + spi_message_init(&message); + memset(xfer, 0, sizeof(xfer)); + + xfer[0].tx_buf = command; + xfer[0].len = command_len; + spi_message_add_tail(&xfer[0], &message); + + xfer[1].rx_buf = data; + xfer[1].len = length; + spi_message_add_tail(&xfer[1], &message); + + for (retry = 0; retry < toRetry; retry++) { + error = spi_sync(private_ts->spi, &message); + if (unlikely(error)) + E("SPI read error: %d\n", error); + else + break; + } + + if (retry == toRetry) { + E("%s: SPI read error retry over %d\n", + __func__, toRetry); + return -EIO; + } + + return 0; +} + +static int himax_spi_write(uint8_t *buf, uint32_t length) +{ +/* + struct spi_transfer t = { + .tx_buf = buf, + .len = length, + }; + struct spi_message m; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + return himax_spi_sync(private_ts, &m); +*/ + struct spi_transfer t; + struct spi_message m; + + memset(&t, 0, sizeof(struct spi_transfer)); + t.tx_buf = buf; + t.len = length; + spi_message_init(&m); + spi_message_add_tail(&t, &m); + return himax_spi_sync(private_ts, &m); +} + +int himax_bus_read(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry) +{ + int result = 0; + uint8_t spi_format_buf[3]; + + mutex_lock(&(private_ts->spi_lock)); + spi_format_buf[0] = 0xF3; + spi_format_buf[1] = command; + spi_format_buf[2] = 0x00; + + result = himax_spi_read(&spi_format_buf[0], 3, data, length, toRetry); + mutex_unlock(&(private_ts->spi_lock)); + + return result; +} +EXPORT_SYMBOL(himax_bus_read); + +int himax_bus_write(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry) +{ + uint8_t *spi_format_buf = gBuffer; + int i = 0; + int result = 0; + + memset(spi_format_buf, 0, GBUFFER_SZ * sizeof(uint8_t)); + mutex_lock(&(private_ts->spi_lock)); + spi_format_buf[0] = 0xF2; + spi_format_buf[1] = command; + + for (i = 0; i < length; i++) + spi_format_buf[i + 2] = data[i]; + + result = himax_spi_write(spi_format_buf, length + 2); + mutex_unlock(&(private_ts->spi_lock)); + + return result; +} +EXPORT_SYMBOL(himax_bus_write); + +int himax_bus_write_command(uint8_t command, uint8_t toRetry) +{ + return himax_bus_write(command, NULL, 0, toRetry); +} + +int himax_bus_master_write(uint8_t *data, uint32_t length, uint8_t toRetry) +{ + uint8_t buf[length]; + + struct spi_transfer t = { + .tx_buf = buf, + .len = length, + }; + struct spi_message m; + int result = 0; + + mutex_lock(&(private_ts->spi_lock)); + memcpy(buf, data, length); + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + result = himax_spi_sync(private_ts, &m); + mutex_unlock(&(private_ts->spi_lock)); + + return result; +} +EXPORT_SYMBOL(himax_bus_master_write); + +void himax_int_enable(int enable) +{ + int irqnum = 0; + + irqnum = private_ts->hx_irq; + if (enable == 1 && irq_enable_count == 0) { + enable_irq(irqnum); + irq_enable_count = 1; + private_ts->irq_enabled = 1; + } else if (enable == 0 && irq_enable_count == 1) { + disable_irq_nosync(irqnum); + irq_enable_count = 0; + private_ts->irq_enabled = 0; + } + + I("irq_enable_count = %d\n", irq_enable_count); +} +EXPORT_SYMBOL(himax_int_enable); + +#ifdef HX_RST_PIN_FUNC +void himax_rst_gpio_set(int pinnum, uint8_t value) +{ + gpio_direction_output(pinnum, value); +} +EXPORT_SYMBOL(himax_rst_gpio_set); +#endif + +uint8_t himax_int_gpio_read(int pinnum) +{ + return gpio_get_value(pinnum); +} + +#if defined(CONFIG_HMX_DB) +static int himax_regulator_configure(struct himax_i2c_platform_data *pdata) +{ + int retval; + /* struct i2c_client *client = private_ts->client; */ + + pdata->vcc_dig = regulator_get(private_ts->dev, "vdd"); + + if (IS_ERR(pdata->vcc_dig)) { + E("%s: Failed to get regulator vdd\n", + __func__); + retval = PTR_ERR(pdata->vcc_dig); + return retval; + } + + pdata->vcc_ana = regulator_get(private_ts->dev, "avdd"); + + if (IS_ERR(pdata->vcc_ana)) { + E("%s: Failed to get regulator avdd\n", + __func__); + retval = PTR_ERR(pdata->vcc_ana); + regulator_put(pdata->vcc_dig); + return retval; + } + + return 0; +}; + +static void himax_regulator_deinit(struct himax_i2c_platform_data *pdata) +{ + I("%s: entered.\n", __func__); + + if (!IS_ERR(pdata->vcc_ana)) + regulator_put(pdata->vcc_ana); + + if (!IS_ERR(pdata->vcc_dig)) + regulator_put(pdata->vcc_dig); + + I("%s: regulator put, completed.\n", __func__); +}; + +static int himax_power_on(struct himax_i2c_platform_data *pdata, bool on) +{ + int retval; + + if (on) { + retval = regulator_enable(pdata->vcc_dig); + + if (retval) { + E("%s: Failed to enable regulator vdd\n", + __func__); + return retval; + } + + /*msleep(100);*/ + usleep_range(1000, 1001); + retval = regulator_enable(pdata->vcc_ana); + + if (retval) { + E("%s: Failed to enable regulator avdd\n", + __func__); + regulator_disable(pdata->vcc_dig); + return retval; + } + } else { + regulator_disable(pdata->vcc_dig); + regulator_disable(pdata->vcc_ana); + } + + return 0; +} + +int himax_gpio_power_config(struct himax_i2c_platform_data *pdata) +{ + int error; + /* struct i2c_client *client = private_ts->client; */ + + error = himax_regulator_configure(pdata); + + if (error) { + E("Failed to intialize hardware\n"); + goto err_regulator_not_on; + } + +#ifdef HX_RST_PIN_FUNC + + if (gpio_is_valid(pdata->gpio_reset)) { + /* configure touchscreen reset out gpio */ + error = gpio_request(pdata->gpio_reset, "hmx_reset_gpio"); + + if (error) { + E("unable to request gpio [%d]\n", pdata->gpio_reset); + goto err_regulator_on; + } + + error = gpio_direction_output(pdata->gpio_reset, 0); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_req; + } + } + +#endif + error = himax_power_on(pdata, true); + + if (error) { + E("Failed to power on hardware\n"); + goto err_power_on; + } + + if (gpio_is_valid(pdata->gpio_irq)) { + /* configure touchscreen irq gpio */ + error = gpio_request(pdata->gpio_irq, "hmx_gpio_irq"); + + if (error) { + E("unable to request gpio [%d]\n", + pdata->gpio_irq); + goto err_req_irq_gpio; + } + + error = gpio_direction_input(pdata->gpio_irq); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_irq); + goto err_set_gpio_irq; + } + + private_ts->hx_irq = gpio_to_irq(pdata->gpio_irq); + } else { + E("irq gpio not provided\n"); + goto err_req_irq_gpio; + } + + /*msleep(20);*/ + usleep_range(2000, 2001); +#ifdef HX_RST_PIN_FUNC + + if (gpio_is_valid(pdata->gpio_reset)) { + error = gpio_direction_output(pdata->gpio_reset, 1); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_set_gpio_irq; + } + } + +#endif + return 0; +err_set_gpio_irq: + + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); + +err_req_irq_gpio: + himax_power_on(pdata, false); +err_power_on: +#ifdef HX_RST_PIN_FUNC +err_gpio_reset_req: + if (gpio_is_valid(pdata->gpio_reset)) + gpio_free(pdata->gpio_reset); + +err_regulator_on: +#endif + himax_regulator_deinit(pdata); +err_regulator_not_on: + return error; +} + +#else +int himax_gpio_power_config(struct himax_i2c_platform_data *pdata) +{ + int error = 0; + /* struct i2c_client *client = private_ts->client; */ +#ifdef HX_RST_PIN_FUNC + + if (pdata->gpio_reset >= 0) { + error = gpio_request(pdata->gpio_reset, "himax-reset"); + + if (error < 0) { + E("%s: request reset pin failed\n", __func__); + goto err_gpio_reset_req; + } + + error = gpio_direction_output(pdata->gpio_reset, 0); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_dir; + } + } + +#endif + +#ifdef HX_SHARP_ENABLE_PON + if (gpio_is_valid(pdata->gpio_pon)) { + error = gpio_request(pdata->gpio_pon, "hmx_pon_gpio"); + + if (error) { + E("unable to request scl gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_req; + } + + error = gpio_direction_output(pdata->gpio_pon, 0); + + I("gpio_pon LOW [%d]\n", pdata->gpio_pon); + + if (error) { + E("unable to set direction for pon gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_dir; + } + } +#endif + + + if (pdata->gpio_3v3_en >= 0) { + error = gpio_request(pdata->gpio_3v3_en, "himax-3v3_en"); + + if (error < 0) { + E("%s: request 3v3_en pin failed\n", __func__); + goto err_gpio_3v3_req; + } + + gpio_direction_output(pdata->gpio_3v3_en, 1); + I("3v3_en set 1 get pin = %d\n", gpio_get_value(pdata->gpio_3v3_en)); + } + + if (gpio_is_valid(pdata->gpio_irq)) { + /* configure touchscreen irq gpio */ + error = gpio_request(pdata->gpio_irq, "himax_gpio_irq"); + + if (error) { + E("unable to request gpio [%d]\n", pdata->gpio_irq); + goto err_gpio_irq_req; + } + + error = gpio_direction_input(pdata->gpio_irq); + + if (error) { + E("unable to set direction for gpio [%d]\n", pdata->gpio_irq); + goto err_gpio_irq_set_input; + } + + private_ts->hx_irq = gpio_to_irq(pdata->gpio_irq); + } else { + E("irq gpio not provided\n"); + goto err_gpio_irq_req; + } +#ifdef HX_SHARP_ENABLE_PON + msleep(20); +#else + usleep_range(2000, 2001); +#endif + +#ifdef HX_RST_PIN_FUNC + + if (pdata->gpio_reset >= 0) { + error = gpio_direction_output(pdata->gpio_reset, 1); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_set_high; + } + } +#endif + +#ifdef HX_SHARP_ENABLE_PON + msleep(800); + + if (gpio_is_valid(pdata->gpio_pon)) { + + error = gpio_direction_output(pdata->gpio_pon, 1); + + I("gpio_pon HIGH [%d]\n", pdata->gpio_pon); + + if (error) { + E("gpio_pon unable to set direction for gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_set_high; + } + } +#endif + return error; + +#ifdef HX_SHARP_ENABLE_PON +err_gpio_pon_set_high: +#endif +#ifdef HX_RST_PIN_FUNC +err_gpio_reset_set_high: +#endif +err_gpio_irq_set_input: + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); +err_gpio_irq_req: + if (pdata->gpio_3v3_en >= 0) + gpio_free(pdata->gpio_3v3_en); +err_gpio_3v3_req: +#ifdef HX_SHARP_ENABLE_PON +err_gpio_pon_dir: + if (gpio_is_valid(pdata->gpio_pon)) + gpio_free(pdata->gpio_pon); +err_gpio_pon_req: +#endif +#ifdef HX_RST_PIN_FUNC +err_gpio_reset_dir: + if (pdata->gpio_reset >= 0) + gpio_free(pdata->gpio_reset); +err_gpio_reset_req: +#endif + return error; +} + +#endif + +void himax_gpio_power_deconfig(struct himax_i2c_platform_data *pdata) +{ + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); + +#ifdef HX_RST_PIN_FUNC + if (gpio_is_valid(pdata->gpio_reset)) + gpio_free(pdata->gpio_reset); +#endif + +#if defined(CONFIG_HMX_DB) + himax_power_on(pdata, false); + himax_regulator_deinit(pdata); +#else + if (pdata->gpio_3v3_en >= 0) + gpio_free(pdata->gpio_3v3_en); + +#ifdef HX_SHARP_ENABLE_PON + if (gpio_is_valid(pdata->gpio_pon)) + gpio_free(pdata->gpio_pon); +#endif + +#endif +} + +static void himax_ts_isr_func(struct himax_ts_data *ts) +{ + himax_ts_work(ts); +} + +irqreturn_t himax_ts_thread(int irq, void *ptr) +{ + himax_ts_isr_func((struct himax_ts_data *)ptr); + + return IRQ_HANDLED; +} + +static void himax_ts_work_func(struct work_struct *work) +{ + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work); + + himax_ts_work(ts); +} + +int himax_int_register_trigger(void) +{ + int ret = 0; + struct himax_ts_data *ts = private_ts; + + if (ic_data->HX_INT_IS_EDGE) { + I("%s edge triiger falling\n", __func__); + ret = request_threaded_irq(ts->hx_irq, NULL, himax_ts_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, HIMAX_common_NAME, ts); + } else { + I("%s level trigger low\n", __func__); + ret = request_threaded_irq(ts->hx_irq, NULL, himax_ts_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT, HIMAX_common_NAME, ts); + } + + return ret; +} + +int himax_int_en_set(void) +{ + int ret = NO_ERR; + + ret = himax_int_register_trigger(); + return ret; +} + +int himax_ts_register_interrupt(void) +{ + struct himax_ts_data *ts = private_ts; + /* struct i2c_client *client = private_ts->client; */ + int ret = 0; + + ts->irq_enabled = 0; + + /* Work functon */ + if (private_ts->hx_irq) {/*INT mode*/ + ts->use_irq = 1; + ret = himax_int_register_trigger(); + + if (ret == 0) { + ts->irq_enabled = 1; + irq_enable_count = 1; + I("%s: irq enabled at gpio: %d\n", __func__, private_ts->hx_irq); +#ifdef HX_SMART_WAKEUP + irq_set_irq_wake(private_ts->hx_irq, 1); +#endif + } else { + ts->use_irq = 0; + E("%s: request_irq failed\n", __func__); + } + } else { + I("%s: private_ts->hx_irq is empty, use polling mode.\n", __func__); + } + + if (!ts->use_irq) {/*if use polling mode need to disable HX_ESD_RECOVERY function*/ + ts->himax_wq = create_singlethread_workqueue("himax_touch"); + INIT_WORK(&ts->work, himax_ts_work_func); + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = himax_ts_timer_func; + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + I("%s: polling mode enabled\n", __func__); + } + + return ret; +} + +int himax_ts_unregister_interrupt(void) +{ + struct himax_ts_data *ts = private_ts; + int ret = 0; + + I("%s: entered.\n", __func__); + + /* Work functon */ + if (private_ts->hx_irq && ts->use_irq) {/*INT mode*/ +#ifdef HX_SMART_WAKEUP + irq_set_irq_wake(ts->hx_irq, 0); +#endif + free_irq(ts->hx_irq, ts); + I("%s: irq disabled at qpio: %d\n", __func__, private_ts->hx_irq); + } + + if (!ts->use_irq) {/*if use polling mode need to disable HX_ESD_RECOVERY function*/ + hrtimer_cancel(&ts->timer); + cancel_work_sync(&ts->work); + if (ts->himax_wq != NULL) + destroy_workqueue(ts->himax_wq); + I("%s: polling mode destroyed", __func__); + } + + return ret; +} + +static int himax_common_suspend(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + + I("%s: enter\n", __func__); +#ifdef CONFIG_DRM + if (!ts->initialized) + return -ECANCELED; +#endif + himax_chip_common_suspend(ts); + return 0; +} + +#if defined(HX_RESUME_SET_FW) +void himax_resume_thread_func(void) +{ + struct himax_ts_data *ts = private_ts; + I("%s: TP resume from thread\n", __func__); +#if defined(__HIMAX_HX83102D_MOD__) + queue_delayed_work(ts->ts_int_workqueue, + &ts->ts_int_work, msecs_to_jiffies(60)); +#else + queue_work(ts->ts_int_workqueue, &ts->ts_int_work); +#endif + return; +} +#endif +static int himax_common_resume(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + + I("%s: enter\n", __func__); +#ifdef CONFIG_DRM + if (!ts->initialized) { + /* + * wait until device resume for TDDI + * TDDI: Touch and display Driver IC + */ + if (himax_chip_common_init()) + return -ECANCELED; + } +#endif +#if defined(HX_RESUME_SET_FW) + himax_resume_thread_func(); +#else + himax_chip_common_resume(ts); +#endif + return 0; +} + +#if defined(CONFIG_DRM) +int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank; + struct himax_ts_data *ts = + container_of(self, struct himax_ts_data, fb_notif); + + if (!evdata || (evdata->id != 0)) + return 0; + + D("DRM %s\n", __func__); + + if (evdata->data && event == MSM_DRM_EARLY_EVENT_BLANK && ts != NULL && + ts->dev != NULL) { + blank = evdata->data; + switch (*blank) { + case MSM_DRM_BLANK_POWERDOWN: + if (!ts->initialized) + return -ECANCELED; + himax_common_suspend(ts->dev); +#ifdef HIMAX_PALM_SENSOR_EN + if (ts->palm_detection_enabled) { + I("%s: palm detection is enabled", __func__); + return 1; + } +#endif +#ifdef HIMAX_V2_SENSOR_EN + if (ts->SMWP_enable) { + I("%s: tap detection is enabled", __func__); + return 1; + } +#endif + break; +#if defined(__HIMAX_HX83102D_MOD__) + case MSM_DRM_BLANK_UNBLANK: + D("DRM_EARLY_EVENT_BLANK resume.\n"); + himax_common_resume(ts->dev); + return 0; +#endif + } + } + + /* Avoid HX83102D is called for early event and event, two times */ +#if !defined(__HIMAX_HX83102D_MOD__) + if (evdata->data && event == MSM_DRM_EVENT_BLANK && ts != NULL && + ts->dev != NULL) { + blank = evdata->data; + switch (*blank) { + case MSM_DRM_BLANK_UNBLANK: + himax_common_resume(ts->dev); + break; + } + } +#endif + + return 0; +} +#elif defined(CONFIG_FB) +int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct himax_ts_data *ts = + container_of(self, struct himax_ts_data, fb_notif); + + I(" %s\n", __func__); + + if (evdata && evdata->data && event == FB_EVENT_BLANK && ts != NULL && + ts->dev != NULL) { + blank = evdata->data; + + switch (*blank) { + case FB_BLANK_UNBLANK: + himax_common_resume(ts->dev); + break; + + case FB_BLANK_POWERDOWN: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_NORMAL: + himax_common_suspend(ts->dev); + break; + } + } + + return 0; +} +#endif + +int himax_chip_common_probe(struct spi_device *spi) +{ + struct himax_ts_data *ts; + int ret = 0; + + I("Enter %s\n", __func__); + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) { + dev_err(&spi->dev, + "%s: Full duplex not supported by host\n", __func__); + return -EIO; + } + + gBuffer = kzalloc(sizeof(uint8_t) * PACKET_MAX_SZ, GFP_KERNEL); + if (gBuffer == NULL) { + E("%s: allocate gBuffer failed\n", __func__); + ret = -ENOMEM; + goto err_alloc_gbuffer_failed; + } + + ts = kzalloc(sizeof(struct himax_ts_data), GFP_KERNEL); + if (ts == NULL) { + E("%s: allocate himax_ts_data failed\n", __func__); + ret = -ENOMEM; + goto err_alloc_data_failed; + } + + private_ts = ts; + spi->bits_per_word = 8; + spi->mode = SPI_MODE_3; + spi->chip_select = 0; + + ts->spi = spi; + mutex_init(&(ts->spi_lock)); + ts->dev = &spi->dev; + dev_set_drvdata(&spi->dev, ts); + spi_set_drvdata(spi, ts); + + ts->initialized = false; + ret = himax_chip_common_init(); + if (ret < 0) + goto err_alloc_data_failed; + return 0; + +err_alloc_data_failed: + kfree(gBuffer); +err_alloc_gbuffer_failed: + return ret; +} + +int himax_chip_common_remove(struct spi_device *spi) +{ + struct himax_ts_data *ts = spi_get_drvdata(spi); + + if (g_hx_chip_inited) + himax_chip_common_deinit(); + +#if defined(HX_USB_DETECT_GLOBAL) + if (ts->charger_notif.notifier_call) + power_supply_unreg_notifier(&ts->charger_notif); +#endif + ts->spi = NULL; + /* spin_unlock_irq(&ts->spi_lock); */ + spi_set_drvdata(spi, NULL); + kfree(gBuffer); + + I("%s: completed.\n", __func__); + + return 0; +} + +#if (!defined(CONFIG_FB)) && (!defined(CONFIG_DRM)) +static const struct dev_pm_ops himax_common_pm_ops = { + .suspend = himax_common_suspend, + .resume = himax_common_resume, +}; +#endif + +#ifdef CONFIG_OF +static const struct of_device_id himax_match_table[] = { + {.compatible = "himax,hxcommon" }, + {}, +}; +#else +#define himax_match_table NULL +#endif + +static struct spi_driver himax_common_driver = { + .driver = { + .name = HIMAX_common_NAME, + .owner = THIS_MODULE, + .of_match_table = himax_match_table, + }, + .probe = himax_chip_common_probe, + .remove = himax_chip_common_remove, +}; + +static int __init himax_common_init(void) +{ + I("Himax common touch panel driver init\n"); + D("Himax check double loading\n"); + if (g_mmi_refcnt++ > 0) { + I("Himax driver has been loaded! ignoring....\n"); + return 0; + } + spi_register_driver(&himax_common_driver); + + return 0; +} + +static void __exit himax_common_exit(void) +{ + if (spi) { + spi_unregister_device(spi); + spi = NULL; + } + spi_unregister_driver(&himax_common_driver); +} + +module_init(himax_common_init); +module_exit(himax_common_exit); + +MODULE_DESCRIPTION("Himax_common driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/touchscreen/himax_v2_mmi/himax_platform_QCT_SPI.h b/drivers/input/touchscreen/himax_v2_mmi/himax_platform_QCT_SPI.h new file mode 100644 index 000000000000..35e4b40c086c --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/himax_platform_QCT_SPI.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for QCT platform + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef HIMAX_PLATFORM_H +#define HIMAX_PLATFORM_H + +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_HMX_DB) + #include +#endif + +#define HIMAX_I2C_RETRY_TIMES 3 + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG) +#define D(x...) pr_debug("[HXTP] " x) +#define I(x...) pr_info("[HXTP] " x) +#define W(x...) pr_warn("[HXTP][WARNING] " x) +#define E(x...) pr_err("[HXTP][ERROR] " x) +#define DIF(x...) \ +do { \ + if (debug_flag) \ + pr_debug("[HXTP][DEBUG] " x) \ + } while (0) +#else +#define D(x...) +#define I(x...) +#define W(x...) +#define E(x...) +#define DIF(x...) +#endif + +#if defined(CONFIG_HMX_DB) + /* Analog voltage @2.7 V */ + #define HX_VTG_MIN_UV 2700000 + #define HX_VTG_MAX_UV 3300000 + #define HX_ACTIVE_LOAD_UA 15000 + #define HX_LPM_LOAD_UA 10 + /* Digital voltage @1.8 V */ + #define HX_VTG_DIG_MIN_UV 1800000 + #define HX_VTG_DIG_MAX_UV 1800000 + #define HX_ACTIVE_LOAD_DIG_UA 10000 + #define HX_LPM_LOAD_DIG_UA 10 + + #define HX_I2C_VTG_MIN_UV 1800000 + #define HX_I2C_VTG_MAX_UV 1800000 + #define HX_I2C_LOAD_UA 10000 + #define HX_I2C_LPM_LOAD_UA 10 +#endif + +#define HIMAX_common_NAME "himax_tp" +#define HIMAX_I2C_ADDR 0x48 +#define INPUT_DEV_NAME "himax-touchscreen" +#define PACKET_MAX_SZ 4096 +#define GBUFFER_SZ PACKET_MAX_SZ - 2 + +struct himax_i2c_platform_data { + int abs_x_min; + int abs_x_max; + int abs_x_fuzz; + int abs_y_min; + int abs_y_max; + int abs_y_fuzz; + int abs_pressure_min; + int abs_pressure_max; + int abs_pressure_fuzz; + int abs_width_min; + int abs_width_max; + int screenWidth; + int screenHeight; + uint8_t fw_version; + uint8_t tw_id; + uint8_t powerOff3V3; + uint8_t cable_config[2]; + uint8_t protocol_type; + int gpio_irq; + int gpio_reset; + int gpio_3v3_en; + char panel_supplier[16]; + int (*power)(int on); + void (*reset)(void); + struct himax_virtual_key *virtual_key; + struct kobject *vk_obj; + struct kobj_attribute *vk2Use; + + int hx_config_size; +#if defined(CONFIG_HMX_DB) + bool i2c_pull_up; + bool digital_pwr_regulator; + int reset_gpio; + u32 reset_gpio_flags; + int irq_gpio; + u32 irq_gpio_flags; + + struct regulator *vcc_ana; /* For Dragon Board */ + struct regulator *vcc_dig; /* For Dragon Board */ + struct regulator *vcc_i2c; /* For Dragon Board */ +#endif +}; + +extern struct himax_ic_data *ic_data; +extern struct himax_ts_data *private_ts; +int himax_chip_common_init(void); +void himax_chip_common_deinit(void); + +void himax_ts_work(struct himax_ts_data *ts); +enum hrtimer_restart himax_ts_timer_func(struct hrtimer *timer); +extern int irq_enable_count; +extern int himax_bus_read(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +extern int himax_bus_write(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +extern int himax_bus_write_command(uint8_t command, uint8_t toRetry); +extern int himax_bus_master_write(uint8_t *data, uint32_t length, uint8_t toRetry); +extern void himax_int_enable(int enable); +extern int himax_ts_register_interrupt(void); +int himax_ts_unregister_interrupt(void); +extern uint8_t himax_int_gpio_read(int pinnum); + +extern int himax_gpio_power_config(struct himax_i2c_platform_data *pdata); +void himax_gpio_power_deconfig(struct himax_i2c_platform_data *pdata); + +#if defined(CONFIG_DRM) + extern int drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#elif defined(CONFIG_FB) + extern int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#endif + +#endif diff --git a/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_modular.h b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_modular.h new file mode 100644 index 000000000000..3e7a43b9ed4e --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_modular.h @@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for modularize functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef __HIMAX_MODULAR_H__ +#define __HIMAX_MODULAR_H__ + +#include "himax_modular_table.h" + +static bool (*this_detect_fp)(void); + +static void himax_add_chip_dt(bool (*detect_fp)(void)) +{ + int32_t idx; + struct himax_chip_entry *entry; + + this_detect_fp = detect_fp; + idx = himax_get_ksym_idx(); + if (idx < 0) { + /*TODO: No entry, handle this error*/ + E("%s: no entry exist, please insert ic module first!", __func__); + } else { + entry = (void *)kallsyms_lookup_name(himax_ksym_lookup[idx]); + if (/*!(entry->core_chip_dt)*/isEmpty(idx) == 1) { + entry->core_chip_dt = kcalloc(HX_DRIVER_MAX_IC_NUM, + sizeof(struct himax_chip_detect), GFP_KERNEL); + entry->hx_ic_dt_num = 0; + } + entry->core_chip_dt[entry->hx_ic_dt_num++].fp_chip_detect = detect_fp; + } +} + +static void free_chip_dt_table(void) +{ + int i, j, idx; + struct himax_chip_entry *entry; + + idx = himax_get_ksym_idx(); + if (idx >= 0) { + if (isEmpty(idx) != 0) { + I("%s: no chip registered or entry has been clean up, leave safetly.\n", + __func__); + return; + } + entry = get_chip_entry_by_index(idx); + + for (i = 0; i < entry->hx_ic_dt_num; i++) { + if (entry->core_chip_dt[i].fp_chip_detect == this_detect_fp) { + if (i == (entry->hx_ic_dt_num - 1)) { + entry->core_chip_dt[i].fp_chip_detect = NULL; + entry->hx_ic_dt_num = 0; + } else { + for (j = i; j < entry->hx_ic_dt_num; j++) + entry->core_chip_dt[i].fp_chip_detect = entry->core_chip_dt[j].fp_chip_detect; + + entry->core_chip_dt[j].fp_chip_detect = NULL; + entry->hx_ic_dt_num--; + } + } + } + if (entry->hx_ic_dt_num == 0) { + kfree(entry->core_chip_dt); + entry->core_chip_dt = NULL; + } + } +} + +#define setup_symbol(sym) ({kp_##sym = (void *)kallsyms_lookup_name(#sym); kp_##sym; }) +#define assert_on_symbol(sym) do { \ + if (!setup_symbol(sym)) { \ + E("%s: setup %s failed!\n", __func__, #sym); \ + ret = -1; \ + } \ + } while (0) +#if !defined(__HIMAX_HX852xH_MOD__) && !defined(__HIMAX_HX852xG_MOD__) +static struct fw_operation **kp_pfw_op; +static struct ic_operation **kp_pic_op; +static struct driver_operation **kp_pdriver_op; +#endif +static struct himax_chip_detect **kp_g_core_chip_dt; + +#if defined(HX_ZERO_FLASH) +static struct zf_operation **kp_pzf_op; +static int *kp_G_POWERONOF; +#endif + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) + static char **kp_i_CTPM_firmware_name; +#endif +#endif + +static unsigned char *kp_IC_CHECKSUM; + +#ifdef HX_ESD_RECOVERY +static u8 *kp_HX_ESD_RESET_ACTIVATE; +#endif + +#if defined(HX_ZERO_FLASH) && defined(HX_CODE_OVERLAY) +#if defined(CONFIG_TOUCHSCREEN_HIMAX_INCELL) +extern uint8_t *ovl_idx; +static uint8_t **kp_ovl_idx; +#endif +#endif + +static unsigned long *kp_FW_VER_MAJ_FLASH_ADDR; +static unsigned long *kp_FW_VER_MIN_FLASH_ADDR; +static unsigned long *kp_CFG_VER_MAJ_FLASH_ADDR; +static unsigned long *kp_CFG_VER_MIN_FLASH_ADDR; +static unsigned long *kp_CID_VER_MAJ_FLASH_ADDR; +static unsigned long *kp_CID_VER_MIN_FLASH_ADDR; + +static unsigned long *kp_FW_VER_MAJ_FLASH_LENG; +static unsigned long *kp_FW_VER_MIN_FLASH_LENG; +static unsigned long *kp_CFG_VER_MAJ_FLASH_LENG; +static unsigned long *kp_CFG_VER_MIN_FLASH_LENG; +static unsigned long *kp_CID_VER_MAJ_FLASH_LENG; +static unsigned long *kp_CID_VER_MIN_FLASH_LENG; + +#ifdef HX_AUTO_UPDATE_FW + static int *kp_g_i_FW_VER; + static int *kp_g_i_CFG_VER; + static int *kp_g_i_CID_MAJ; + static int *kp_g_i_CID_MIN; + static unsigned char **kp_i_CTPM_FW; +#endif + +#ifdef HX_TP_PROC_2T2R + static bool *kp_Is_2T2R; +#endif + +#ifdef HX_USB_DETECT_GLOBAL + static void (*kp_himax_cable_detect_func)(bool force_renew); +#endif + +#ifdef HX_RST_PIN_FUNC + static void (*kp_himax_rst_gpio_set)(int pinnum, uint8_t value); +#endif + +static struct himax_ts_data **kp_private_ts; +static struct himax_core_fp *kp_g_core_fp; +static struct himax_ic_data **kp_ic_data; + +#if defined(__HIMAX_HX852xH_MOD__) || defined(__HIMAX_HX852xG_MOD__) +#if defined(__HIMAX_HX852xG_MOD__) +static struct on_driver_operation **kp_on_pdriver_op; +#endif +static void (*kp_himax_mcu_on_cmd_init)(void); +static int (*kp_himax_mcu_on_cmd_struct_init)(void); +static void (*kp_himax_on_parse_assign_cmd)(uint32_t addr, uint8_t *cmd, int len); +#else +static void (*kp_himax_mcu_in_cmd_init)(void); +static int (*kp_himax_mcu_in_cmd_struct_init)(void); +static void (*kp_himax_in_parse_assign_cmd)(uint32_t addr, uint8_t *cmd, int len); +#endif + +static int *kp_irq_enable_count; +static int (*kp_himax_bus_read)(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +static int (*kp_himax_bus_write)(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +static int (*kp_himax_bus_write_command)(uint8_t command, uint8_t toRetry); +static int (*kp_himax_bus_master_write)(uint8_t *data, uint32_t length, uint8_t toRetry); +static void (*kp_himax_int_enable)(int enable); +static int (*kp_himax_ts_register_interrupt)(void); +static uint8_t (*kp_himax_int_gpio_read)(int pinnum); +static int (*kp_himax_gpio_power_config)(struct himax_i2c_platform_data *pdata); + +static int32_t himax_ic_setup_external_symbols(void) +{ + int32_t ret = 0; + +#if !defined(__HIMAX_HX852xH_MOD__) && !defined(__HIMAX_HX852xG_MOD__) + assert_on_symbol(pfw_op); + assert_on_symbol(pic_op); + assert_on_symbol(pdriver_op); +#endif + assert_on_symbol(g_core_chip_dt); + + assert_on_symbol(private_ts); + assert_on_symbol(g_core_fp); + assert_on_symbol(ic_data); + +#if defined(__HIMAX_HX852xH_MOD__) || defined(__HIMAX_HX852xG_MOD__) +#if defined(__HIMAX_HX852xG_MOD__) + assert_on_symbol(on_pdriver_op); +#endif + assert_on_symbol(himax_mcu_on_cmd_init); + assert_on_symbol(himax_mcu_on_cmd_struct_init); + assert_on_symbol(himax_on_parse_assign_cmd); +#else + assert_on_symbol(himax_mcu_in_cmd_init); + assert_on_symbol(himax_mcu_in_cmd_struct_init); + assert_on_symbol(himax_in_parse_assign_cmd); +#endif + + assert_on_symbol(irq_enable_count); + assert_on_symbol(himax_bus_read); + assert_on_symbol(himax_bus_write); + assert_on_symbol(himax_bus_write_command); + assert_on_symbol(himax_bus_master_write); + assert_on_symbol(himax_int_enable); + assert_on_symbol(himax_ts_register_interrupt); + assert_on_symbol(himax_int_gpio_read); + assert_on_symbol(himax_gpio_power_config); + +#if defined(HX_ZERO_FLASH) + assert_on_symbol(pzf_op); + assert_on_symbol(G_POWERONOF); +#endif + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) + assert_on_symbol(i_CTPM_firmware_name); +#endif +#endif + assert_on_symbol(IC_CHECKSUM); + + assert_on_symbol(FW_VER_MAJ_FLASH_ADDR); + assert_on_symbol(FW_VER_MIN_FLASH_ADDR); + assert_on_symbol(CFG_VER_MAJ_FLASH_ADDR); + assert_on_symbol(CFG_VER_MIN_FLASH_ADDR); + assert_on_symbol(CID_VER_MAJ_FLASH_ADDR); + assert_on_symbol(CID_VER_MIN_FLASH_ADDR); + + assert_on_symbol(FW_VER_MAJ_FLASH_LENG); + assert_on_symbol(FW_VER_MIN_FLASH_LENG); + assert_on_symbol(CFG_VER_MAJ_FLASH_LENG); + assert_on_symbol(CFG_VER_MIN_FLASH_LENG); + assert_on_symbol(CID_VER_MAJ_FLASH_LENG); + assert_on_symbol(CID_VER_MIN_FLASH_LENG); + +#ifdef HX_AUTO_UPDATE_FW + assert_on_symbol(g_i_FW_VER); + assert_on_symbol(g_i_CFG_VER); + assert_on_symbol(g_i_CID_MAJ); + assert_on_symbol(g_i_CID_MIN); + assert_on_symbol(i_CTPM_FW); +#endif + +#ifdef HX_TP_PROC_2T2R + assert_on_symbol(Is_2T2R); +#endif + +#ifdef HX_USB_DETECT_GLOBAL + assert_on_symbol(himax_cable_detect_func); +#endif + +#ifdef HX_RST_PIN_FUNC + assert_on_symbol(himax_rst_gpio_set); +#endif + +#ifdef HX_ESD_RECOVERY + assert_on_symbol(HX_ESD_RESET_ACTIVATE); +#endif + +#if defined(HX_ZERO_FLASH) && defined(HX_CODE_OVERLAY) +#if defined(CONFIG_TOUCHSCREEN_HIMAX_INCELL) + assert_on_symbol(ovl_idx); +#endif +#endif + return ret; +} + + +#endif diff --git a/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102/Android.mk b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102/Android.mk new file mode 100644 index 000000000000..474949dc07e8 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102/Android.mk @@ -0,0 +1,17 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +HIMAX_MMI_IC_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +HIMAX_MMI_IC_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_V2_MMI_IC_NAME=hx83102 + +include $(CLEAR_VARS) +LOCAL_MODULE := himax_v2_mmi_hx83102.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(HIMAX_MMI_IC_MODULE_PATH) +LOCAL_REQUIRED_MODULES := himax_v2_mmi.ko +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102/Kbuild b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102/Kbuild new file mode 100644 index 000000000000..a68da6a2f019 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102/Kbuild @@ -0,0 +1,18 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_v2_mmi \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_v2_mmi/support_ic \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102 +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_DEBUG \ + -DCONFIG_TOUCHSCREEN_HIMAX_INSPECT + +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_INCELL +EXTRA_CFLAGS += -DCONFIG_HMX_DB + +ifneq ($(filter hx83102,$(CONFIG_INPUT_HIMAX_V2_MMI_IC_NAME)),) + EXTRA_CFLAGS += -D__HIMAX_HX83102_MOD__ +endif + +obj-m := himax_v2_mmi_hx83102.o +himax_v2_mmi_hx83102-objs += himax_ic_HX83102.o diff --git a/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102/Makefile b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102/Makefile new file mode 100755 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102/himax_ic_HX83102.c b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102/himax_ic_HX83102.c new file mode 100644 index 000000000000..1c449b40ee31 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102/himax_ic_HX83102.c @@ -0,0 +1,1462 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for HX83102 chipset + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_ic_HX83102.h" +#include "himax_modular.h" + +static void hx83102_chip_init(void) +{ + (*kp_private_ts)->chip_cell_type = CHIP_IS_IN_CELL; + I("%s:IC cell type = %d\n", __func__, (*kp_private_ts)->chip_cell_type); + (*kp_IC_CHECKSUM) = HX_TP_BIN_CHECKSUM_CRC; + /*Himax: Set FW and CFG Flash Address*/ + (*kp_FW_VER_MAJ_FLASH_ADDR) = 49157; /*0x00C005*/ + (*kp_FW_VER_MAJ_FLASH_LENG) = 1; + (*kp_FW_VER_MIN_FLASH_ADDR) = 49158; /*0x00C006*/ + (*kp_FW_VER_MIN_FLASH_LENG) = 1; + (*kp_CFG_VER_MAJ_FLASH_ADDR) = 49408; /*0x00C100*/ + (*kp_CFG_VER_MAJ_FLASH_LENG) = 1; + (*kp_CFG_VER_MIN_FLASH_ADDR) = 49409; /*0x00C101*/ + (*kp_CFG_VER_MIN_FLASH_LENG) = 1; + (*kp_CID_VER_MAJ_FLASH_ADDR) = 49154; /*0x00C002*/ + (*kp_CID_VER_MAJ_FLASH_LENG) = 1; + (*kp_CID_VER_MIN_FLASH_ADDR) = 49155; /*0x00C003*/ + (*kp_CID_VER_MIN_FLASH_LENG) = 1; + /*PANEL_VERSION_ADDR = 49156;*/ /*0x00C004*/ + /*PANEL_VERSION_LENG = 1;*/ +} + +static void hx83102e_chip_init(void) +{ + (*kp_private_ts)->chip_cell_type = CHIP_IS_IN_CELL; + I("%s:IC cell type = %d\n", __func__, (*kp_private_ts)->chip_cell_type); + (*kp_IC_CHECKSUM) = HX_TP_BIN_CHECKSUM_CRC; + /*Himax: Set FW and CFG Flash Address*/ + (*kp_FW_VER_MAJ_FLASH_ADDR) = 57349; /*0x00C005*/ + (*kp_FW_VER_MAJ_FLASH_LENG) = 1; + (*kp_FW_VER_MIN_FLASH_ADDR) = 57350; /*0x00C006*/ + (*kp_FW_VER_MIN_FLASH_LENG) = 1; + (*kp_CFG_VER_MAJ_FLASH_ADDR) = 57600; /*0x00C100*/ + (*kp_CFG_VER_MAJ_FLASH_LENG) = 1; + (*kp_CFG_VER_MIN_FLASH_ADDR) = 57601; /*0x00C101*/ + (*kp_CFG_VER_MIN_FLASH_LENG) = 1; + (*kp_CID_VER_MAJ_FLASH_ADDR) = 57346; /*0x00C002*/ + (*kp_CID_VER_MAJ_FLASH_LENG) = 1; + (*kp_CID_VER_MIN_FLASH_ADDR) = 57347; /*0x00C003*/ + (*kp_CID_VER_MIN_FLASH_LENG) = 1; + /*PANEL_VERSION_ADDR = 49156;*/ /*0x00C004*/ + /*PANEL_VERSION_LENG = 1;*/ +} + +void hx83102_burst_enable(uint8_t auto_add_4_byte) +{ + uint8_t tmp_data[4]; + + tmp_data[0] = 0x31; + if (kp_himax_bus_write(0x13, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + tmp_data[0] = (0x10 | auto_add_4_byte); + if (kp_himax_bus_write(0x0D, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } +} + +int hx83102_flash_write_burst(uint8_t *reg_byte, uint8_t *write_data) +{ + uint8_t data_byte[8]; + int i = 0, j = 0; + + for (i = 0; i < 4; i++) + data_byte[i] = reg_byte[i]; + for (j = 4; j < 8; j++) + data_byte[j] = write_data[j-4]; + + if (kp_himax_bus_write(0x00, data_byte, 8, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + return 0; +} + +static int hx83102_register_read(uint8_t *read_addr, int read_length, uint8_t *read_data) +{ + uint8_t tmp_data[4]; + int i = 0; + int address = 0; + + if (read_length > 256) { + E("%s: read len over 256!\n", __func__); + return LENGTH_FAIL; + } + if (read_length > 4) + hx83102_burst_enable(1); + else + hx83102_burst_enable(0); + + address = (read_addr[3] << 24) + (read_addr[2] << 16) + (read_addr[1] << 8) + read_addr[0]; + i = address; + tmp_data[0] = (uint8_t)i; + tmp_data[1] = (uint8_t)(i >> 8); + tmp_data[2] = (uint8_t)(i >> 16); + tmp_data[3] = (uint8_t)(i >> 24); + if (kp_himax_bus_write(0x00, tmp_data, 4, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + tmp_data[0] = 0x00; + if (kp_himax_bus_write(0x0C, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + if (kp_himax_bus_read(0x08, read_data, read_length, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + if (read_length > 4) + hx83102_burst_enable(0); + + return 0; +} + +#ifdef HX_RST_PIN_FUNC +static void hx83102_pin_reset(void) +{ + I("%s: Now reset the Touch chip.\n", __func__); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 0); + msleep(20); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 1); + msleep(50); +} +#endif + +static bool hx83102_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t tmp_writ[DATA_LEN_4]; + uint8_t tmp_data[DATA_LEN_4]; + + do { + if (cnt == 0 || (tmp_data[0] != 0xA5 && tmp_data[0] != 0x00 && tmp_data[0] != 0x87)) { + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x5C; + tmp_writ[3] = 0x00; tmp_writ[2] = 0x00; tmp_writ[1] = 0x00; tmp_writ[0] = 0xA5; + hx83102_flash_write_burst(tmp_addr, tmp_writ); + } + msleep(20); + + /* check fw status */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, DATA_LEN_4, tmp_data); + + if (tmp_data[0] != 0x05) { + I("%s: Do not need wait FW, Status = 0x%02X!\n", __func__, tmp_data[0]); + break; + } + + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x5C; + hx83102_register_read(tmp_addr, DATA_LEN_4, tmp_data); + I("%s: cnt = %d, data[0] = 0x%02X!\n", __func__, cnt, tmp_data[0]); + } while (tmp_data[0] != 0x87 && (++cnt < 50) && check_en == true); + + cnt = 0; + + + do { + /*=========================================== + *I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + *=========================================== + */ + tmp_data[0] = 0x27; + if (kp_himax_bus_write(0x31, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + *I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + *=========================================== + */ + tmp_data[0] = 0x95; + if (kp_himax_bus_write(0x32, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + *Check enter_save_mode + *====================== + */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, ADDR_LEN_4, tmp_data); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /*===================================== + *Reset TCON + *===================================== + */ + tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + /*===================================== + *Reset ADC + *===================================== + */ + tmp_addr[3] = 0x80; + tmp_addr[2] = 0x02; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + + return true; + } + usleep_range(10000, 10001); +#ifdef HX_RST_PIN_FUNC + hx83102_pin_reset(); +#endif + + } while (cnt++ < 15); + + return false; +} + +static bool hx83102ab_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t tmp_data[DATA_LEN_4]; + + do { + /*=========================================== + *I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + *=========================================== + */ + tmp_data[0] = 0x27; + if (kp_himax_bus_write(0x31, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + *I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + *=========================================== + */ + tmp_data[0] = 0x95; + if (kp_himax_bus_write(0x32, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + *Check enter_save_mode + *====================== + */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, ADDR_LEN_4, tmp_data); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /*===================================== + *Reset TCON + *===================================== + */ + tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + /*===================================== + *Reset ADC + *===================================== + */ + tmp_addr[3] = 0x80; + tmp_addr[2] = 0x02; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + + return true; + } + /*msleep(10);*/ + usleep_range(5000, 5001); +#ifdef HX_RST_PIN_FUNC + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 0); + msleep(20); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 1); + msleep(50); +#endif + + } while (cnt++ < 5); + + return false; +} + +static void hx83102ab_set_SMWP_enable(uint8_t SMWP_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t retry_cnt = 0; + + kp_g_core_fp->fp_sense_off(true); + + do { + if (SMWP_enable) { + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, back_data, 4); + } else { + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + } + + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_smwp_enable, DATA_LEN_4, tmp_data, false); + /*I("%s: tmp_data[0]=%d, SMWP_enable=%d, retry_cnt=%d\n", __func__, tmp_data[0],SMWP_enable,retry_cnt);*/ + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); + + kp_g_core_fp->fp_sense_on(0x00); +} + +static void hx83102ab_set_HSEN_enable(uint8_t HSEN_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + + if (HSEN_enable) { + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + } else { + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + } + + /*I("%s: tmp_data[0]=%d, HSEN_enable=%d, retry_cnt=%d\n", __func__, tmp_data[0],HSEN_enable,retry_cnt);*/ +} + +static void hx83102ab_usb_detect_set(uint8_t *cable_config) +{ + uint8_t tmp_data[DATA_LEN_4]; + + if (cable_config[1] == 0x01) { + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + I("%s: USB detect status IN!\n", __func__); + } else { + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + I("%s: USB detect status OUT!\n", __func__); + } +} + +static uint8_t hx83102ab_read_DD_status(uint8_t *cmd_set, uint8_t *tmp_data) +{ + int cnt = 0; + uint8_t req_size = cmd_set[0]; + + cmd_set[3] = (*kp_pfw_op)->data_dd_request[0]; + + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_dd_handshak_addr, DATA_LEN_4, cmd_set, 0); + I("%s: cmd_set[0] = 0x%02X,cmd_set[1] = 0x%02X,cmd_set[2] = 0x%02X,cmd_set[3] = 0x%02X\n", + __func__, cmd_set[0], cmd_set[1], cmd_set[2], cmd_set[3]); + + /* Doing hand shaking 0xAA -> 0xBB */ + for (cnt = 0; cnt < 100; cnt++) { + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_dd_handshak_addr, DATA_LEN_4, tmp_data, false); + usleep_range(10000, 10001); + + if (tmp_data[3] == (*kp_pfw_op)->data_dd_ack[0]) { + I("%s Data ready goto moving data\n", __func__); + break; + } + if (cnt >= 99) { + I("%s Data not ready in FW\n", __func__); + return FW_NOT_READY; + } + } + + kp_g_core_fp->fp_sense_off(true); + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_dd_data_addr, req_size, tmp_data, false); + kp_g_core_fp->fp_sense_on(0x01); + return NO_ERR; +} + +static int hx83102ab_read_FW_status(uint8_t *state_addr, uint8_t *tmp_addr) +{ + uint8_t i; + uint8_t req_size = 0; + uint8_t status_addr[DATA_LEN_4]; /*0x10007F44*/ + uint8_t cmd_addr[DATA_LEN_4]; /*0x900000F8*/ + + if (state_addr[0] == 0x01) { + state_addr[1] = 0x04; + + for (i = 0; i < DATA_LEN_4; i++) { + /* 0x10007F44 */ + state_addr[i + 2] = (*kp_pfw_op)->addr_fw_dbg_msg_addr[i]; + status_addr[i] = (*kp_pfw_op)->addr_fw_dbg_msg_addr[i]; + } + + req_size = 0x04; + kp_g_core_fp->fp_sense_off(true); + kp_g_core_fp->fp_register_read(status_addr, req_size, tmp_addr, false); + kp_g_core_fp->fp_sense_on(0x01); + } else if (state_addr[0] == 0x02) { + state_addr[1] = 0x30; + + for (i = 0; i < DATA_LEN_4; i++) { + /* 0x900000F8 */ + state_addr[i + 2] = (*kp_pfw_op)->addr_fw_state_addr[i]; + cmd_addr[i] = (*kp_pfw_op)->addr_fw_state_addr[i]; + } + + req_size = 0x30; + kp_g_core_fp->fp_register_read(cmd_addr, req_size, tmp_addr, false); + } + + return NO_ERR; +} + +static void hx83102ab_power_on_init(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + + I("%s:\n", __func__); + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_raw_out_sel, DATA_LEN_4, tmp_data, 0); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_sorting_mode_en, DATA_LEN_4, tmp_data, 0); + kp_g_core_fp->fp_touch_information(); + kp_g_core_fp->fp_sense_on(0x00); +} + +static int hx83102ab_fts_ctpm_fw_upgrade_with_sys_fs_64k(unsigned char *fw, int len, bool change_iref) +{ + int burnFW_success = 0; + uint8_t tmp_data[DATA_LEN_4]; + + if (len != FW_SIZE_64k) { + E("%s: The file size is not 64K bytes\n", __func__); + return false; + } + +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + kp_g_core_fp->fp_sense_off(true); + kp_g_core_fp->fp_chip_erase(); + kp_g_core_fp->fp_flash_programming(fw, FW_SIZE_64k); + + if (kp_g_core_fp->fp_check_CRC((*kp_pfw_op)->addr_program_reload_from, FW_SIZE_64k) == 0) + burnFW_success = 1; + + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_raw_out_sel, DATA_LEN_4, tmp_data, 0); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_sorting_mode_en, DATA_LEN_4, tmp_data, 0); +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + /*System reset*/ + kp_g_core_fp->fp_system_reset(); +#endif + return burnFW_success; +} + +#ifdef HX_ESD_RECOVERY +static void hx83102ab_esd_ic_reset(void) +{ + (*kp_HX_ESD_RESET_ACTIVATE) = 1; +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_pin_reset(); +#else + kp_g_core_fp->fp_system_reset(); +#endif + I("%s:\n", __func__); +} +#if defined(HX_ZERO_FLASH) +static int hx83102d_0f_esd_check(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + int ret = NO_ERR; + + I("Enter %s\n", __func__); + + kp_g_core_fp->fp_register_read((*kp_pzf_op)->addr_sts_chk, DATA_LEN_4, tmp_data, 0); + + if (tmp_data[0] != (*kp_pzf_op)->data_activ_sts[0]) { + ret = ERR_STS_WRONG; + I("%s:status : %8X = %2X\n", __func__, zf_addr_sts_chk, tmp_data[0]); + } + + kp_g_core_fp->fp_register_read((*kp_pzf_op)->addr_activ_relod, DATA_LEN_4, tmp_data, 0); + + if (tmp_data[0] != (*kp_pzf_op)->data_activ_in[0]) { + ret = ERR_STS_WRONG; + I("%s:status : %8X = %2X\n", __func__, zf_addr_activ_relod, tmp_data[0]); + } + + return ret; +} +#endif +#endif + +static bool hx83102d_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t tmp_data[DATA_LEN_4]; + + do { + if (cnt == 0 || (tmp_data[0] != 0xA5 && tmp_data[0] != 0x00 && tmp_data[0] != 0x87)) + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_ctrl_fw_isr, DATA_LEN_4, (*kp_pfw_op)->data_fw_stop, 0); + /*msleep(20);*/ + usleep_range(10000, 10001); + + /* check fw status */ + kp_g_core_fp->fp_register_read((*kp_pic_op)->addr_cs_central_state, ADDR_LEN_4, tmp_data, 0); + + if (tmp_data[0] != 0x05) { + I("%s: Do not need wait FW, Status = 0x%02X!\n", __func__, tmp_data[0]); + break; + } + + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_ctrl_fw_isr, 4, tmp_data, false); + I("%s: cnt = %d, data[0] = 0x%02X!\n", __func__, cnt, tmp_data[0]); + } while (tmp_data[0] != 0x87 && (++cnt < 10) && check_en == true); + + cnt = 0; + + do { + /*=========================================== + *I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + *=========================================== + */ + tmp_data[0] = 0x27; + if (kp_himax_bus_write(0x31, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + *I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + *=========================================== + */ + tmp_data[0] = 0x95; + if (kp_himax_bus_write(0x32, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + *Check enter_save_mode + *====================== + */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, ADDR_LEN_4, tmp_data); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /*===================================== + *Reset TCON + *===================================== + */ + tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + /*===================================== + *Reset ADC + *===================================== + */ + tmp_addr[3] = 0x80; + tmp_addr[2] = 0x02; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + + return true; + } + /*msleep(10);*/ + usleep_range(5000, 5001); +#ifdef HX_RST_PIN_FUNC + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 0); + msleep(20); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 1); + msleep(50); +#endif + + } while (cnt++ < 5); + + return false; +} + +static bool hx83102d_dd_clk_set(bool enable) +{ + uint8_t data[4] = {0}; + + data[0] = (enable)?0xDD:0; + return (kp_g_core_fp->fp_register_write((*kp_pic_op)->adr_osc_en, + sizeof((*kp_pic_op)->adr_osc_en), data, 0) == NO_ERR); +} + +static void hx83102d_dd_reg_en(bool enable) +{ + uint8_t data[4] = {0}; + + data[0] = 0xA5; data[1] = 0x00; + data[2] = 0x00; data[3] = 0x00; + kp_g_core_fp->fp_register_write((*kp_pic_op)->adr_osc_pw, DATA_LEN_4, data, 0); + + data[0] = 0xA5; data[1] = 0x83; + data[2] = 0x10; data[3] = 0x2D; + kp_g_core_fp->fp_dd_reg_write(0xB9, 0, 4, data, 0); +} + +static bool hx83102d_ic_id_read(void) +{ + int i = 0; + uint8_t data[4] = {0}; + + kp_g_core_fp->fp_dd_clk_set(true); + kp_g_core_fp->fp_dd_reg_en(true); + + for (i = 0; i < 13; i++) { + data[0] = 0x2A + i; + kp_g_core_fp->fp_dd_reg_write(0xBB, 3, 1, data, 0); + data[0] = 0x08; + kp_g_core_fp->fp_dd_reg_write(0xBB, 1, 1, data, 0); + kp_g_core_fp->fp_dd_reg_read(0xBB, 6, 1, data, 0); + (*kp_ic_data)->vendor_ic_id[i] = data[0]; + I("ic_data->vendor_ic_id[%d] = %02X\n", i, (*kp_ic_data)->vendor_ic_id[i]); + } + + kp_g_core_fp->fp_dd_clk_set(false); + + return true; +} + +#if defined(HX_ZERO_FLASH) +static void himax_hx83102d_reload_to_active(void) +{ + uint8_t addr[DATA_LEN_4] = {0}; + uint8_t data[DATA_LEN_4] = {0}; + uint8_t retry_cnt = 0; + + addr[3] = 0x90; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x48; + + do { + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0xEC; + kp_g_core_fp->fp_register_write(addr, DATA_LEN_4, data, 0); + usleep_range(1000, 1100); + kp_g_core_fp->fp_register_read(addr, DATA_LEN_4, data, 0); + I("%s: data[1]=%d, data[0]=%d, retry_cnt=%d\n", __func__, data[1], data[0], retry_cnt); + retry_cnt++; + } while ((data[1] != 0x01 || data[0] != 0xEC) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} + +static void himax_hx83102d_resume_ic_action(void) +{ +#ifndef HX_RESUME_HW_RESET + himax_hx83102d_reload_to_active(); +#endif +} + +static void himax_hx83102d_suspend_ic_action(void) +{ +#ifndef HX_RESUME_HW_RESET + himax_hx83102d_reload_to_active(); +#endif +} + +static void himax_hx83102d_sense_on(uint8_t FlashMode) +{ + uint8_t tmp_data[DATA_LEN_4]; + int retry = 0; + + I("Enter %s\n", __func__); + + kp_g_core_fp->fp_interface_on(); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_ctrl_fw_isr, + sizeof((*kp_pfw_op)->data_clear), (*kp_pfw_op)->data_clear, 0); + /*msleep(20);*/ + usleep_range(10000, 10001); + if (!FlashMode) { +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + himax_hx83102d_reload_to_active(); + } else { + himax_hx83102d_reload_to_active(); + do { + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_safe_mode_release_pw, + sizeof((*kp_pfw_op)->data_safe_mode_release_pw_active), (*kp_pfw_op)->data_safe_mode_release_pw_active, 0); + + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_flag_reset_event, DATA_LEN_4, tmp_data, 0); + I("%s:Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + } while ((tmp_data[1] != 0x01 || tmp_data[0] != 0x00) && retry++ < 5); + + if (retry >= 5) { + E("%s: Fail:\n", __func__); +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + himax_hx83102d_reload_to_active(); + } else { + I("%s:OK and Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + /* reset code*/ + tmp_data[0] = 0x00; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_safe_mode_release_pw, + sizeof((*kp_pfw_op)->data_safe_mode_release_pw_reset), (*kp_pfw_op)->data_safe_mode_release_pw_reset, 0); + } + } +} + +static void hx83102ab_firmware_update_0f(const struct firmware *fw_entry) +{ + uint8_t tmp_addr[4]; + + I("%s, Entering\n", __func__); + + kp_g_core_fp->fp_register_write((*kp_pzf_op)->addr_system_reset, 4, (*kp_pzf_op)->data_system_reset, 0); + + kp_g_core_fp->fp_sense_off(false); + + /* first 32K */ + /*bin file start*/ + /*isram*/ + kp_g_core_fp->fp_write_sram_0f(fw_entry, (*kp_pzf_op)->data_sram_start_addr, 0, HX_32K_SZ); + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x73; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_write_sram_0f(fw_entry, tmp_addr, 0x8000, (0x200*4)); + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x68; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_write_sram_0f(fw_entry, tmp_addr, 0x8800, (0x200*4)); + + /*last 16k*/ + /*config info*/ + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x72; + tmp_addr[0] = 0x04; /*0x10007206 <= size 60,from 0x0000C180*/ + kp_g_core_fp->fp_write_sram_0f(fw_entry, tmp_addr, 0xC178, (84)); + + /*FW config*/ + if ((*kp_G_POWERONOF) == 1) + kp_g_core_fp->fp_write_sram_0f(fw_entry, (*kp_pzf_op)->data_fw_cfg_1, 0xBFFE, 388); + else + kp_g_core_fp->fp_clean_sram_0f((*kp_pzf_op)->data_fw_cfg_1, 388, 1); + /*ADC config*/ + if ((*kp_G_POWERONOF) == 1) + kp_g_core_fp->fp_write_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_1, 0xe000, (128 * 4)); + else + kp_g_core_fp->fp_clean_sram_0f((*kp_pzf_op)->data_adc_cfg_1, (128 * 4), 2); + + I("%s, END\n", __func__); +} +#if defined(HX_0F_DEBUG) +static void hx83102ab_firmware_read_0f(const struct firmware *fw_entry, int type) +{ + uint8_t tmp_addr[4]; + + I("%s, Entering\n", __func__); + if (type == 0) { /* first 32K */ + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_sram_start_addr, 0, HX_32K_SZ); + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x68; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_addr, 0x8000, (0x200*4)); + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x73; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_addr, 0x8800, (0x200*4)); + + } else { /*last 16k*/ + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x72; + tmp_addr[0] = 0x04; /*0x10007206 <= size 80,from 0x0000C180*/ + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_addr, 0xC178, (84)); + + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_fw_cfg_1, 0xBFFE, 388); + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_1, 0xE000, (128*4)); + + } + I("%s, END\n", __func__); +} +static void hx83102d_firmware_read_0f(const struct firmware *fw_entry, int type) +{ + uint8_t tmp_data[4]; + + I("%s, Entering\n", __func__); + + switch (type) { + case 0: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_sram_start_addr, 0, HX_40K_SZ); + break; + case 1: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_cfg_info, 0xC000, 132); + break; + case 2: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_fw_cfg_1, 0xC0FE, 484); + break; + case 3: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_1, 0xD000, 768); + break; + case 4: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_2, 0xD300, 1536); + break; + case 5: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_3, 0xE000, 1536); + break; + case 6: + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_bor_prevent_info, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xC9E0, 32); + break; + case 7: + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_notch_info, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xCA00, 128); + break; + case 8: + kp_himax_in_parse_assign_cmd(hx83102d_zf_func_info_en, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xCB00, 12); + break; + case 9: + kp_himax_in_parse_assign_cmd(hx83102d_zf_po_sub_func, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xA000, HX4K); + break; + default: + break; + } + I("%s, END\n", __func__); +} +#endif +#if defined(HX_CODE_OVERLAY) +static int hx83102d_0f_overlay(int ovl_type, int mode) +{ + int ret = 0; + uint8_t count = 0; + uint8_t count2 = 0; + const struct firmware *fwp = NULL; + char fwname[32] = "Himax_firmware.bin"; + uint8_t i, j; + uint8_t part_num = 0; + uint8_t buf[16]; + struct zf_info zf_info_arr[3]; + uint8_t sram_addr[4] = {0xE0, 0x8C, 0x00, 0x20}; + uint8_t handshaking_addr[4] = {0xFC, 0x7F, 0x00, 0x10}; + /*uint8_t mpap_addr[4] = {0xEC, 0x73, 0x00, 0x10};*/ + uint8_t request; + uint8_t reply; + uint32_t offset = 0; + uint32_t size = 0; + uint8_t send_data[4] = {0}; + uint8_t recv_data[4] = {0}; + + ret = request_firmware(&fwp, fwname, private_ts->dev); + if (ret < 0) { + E("%s: request firmware FAIL!!!\n", __func__); + return ret; + } + + /*1. get number of partition*/ + part_num = fwp->data[HX64K + 12]; + if (part_num <= 1) { + E("%s, size of cfg part failed! part_num = %d\n", __func__, part_num); + return LENGTH_FAIL; + } + + j = 0; + for (i = 1; i < part_num; i++) { + /* find overlay section */ + memcpy(buf, &fwp->data[i * 0x10 + HX64K], 16); + + if (j < 3 && buf[3] == sram_addr[3] && buf[2] == sram_addr[2] && buf[1] == sram_addr[1] && buf[0] == sram_addr[0]) { + + zf_info_arr[j].write_size = buf[7] << 24 | buf[6] << 16 | buf[5] << 8 | buf[4]; + zf_info_arr[j].fw_addr = buf[11] << 24 | buf[10] << 16 | buf[9] << 8 | buf[8]; + /*I("j = %d; write_size = %d; fw_addr = %d\n", j, zf_info_arr[j].write_size, zf_info_arr[j].fw_addr);*/ + j++; + } + } + +/* + * if (ovl_type == 1) { + * if (mode == 0) { + * //border overlay when finish self_test + * send_data[3] = 0x00; + * send_data[2] = 0x00; + * send_data[1] = 0x00; + * send_data[0] = 0x00; + * offset = zf_info_arr[2].fw_addr; + * size = zf_info_arr[2].write_size; + * I("%s: self test overlay section 2\n", __func__); + * } else if (mode == 1) { + * //sorting overlay when call self_test + * send_data[3] = 0x00; + * send_data[2] = 0x10; + * send_data[1] = 0x73; + * send_data[0] = 0x80; + * offset = zf_info_arr[0].fw_addr; + * size = zf_info_arr[0].write_size; + * I("%s: self test overlay section 0\n", __func__); + * } + * } + */ + if (ovl_type == 2) { + /*gesture overlay when suspend*/ + request = ovl_gesture_request; + reply = ovl_gesture_reply; + offset = zf_info_arr[1].fw_addr; + size = zf_info_arr[1].write_size; + I("%s: overlay section 1\n", __func__); + } else if (ovl_type == 3) { + /*border overlay when resume*/ + request = ovl_border_request; + reply = ovl_border_reply; + offset = zf_info_arr[2].fw_addr; + size = zf_info_arr[2].write_size; + I("%s: overlay section 2\n", __func__); + } else { + return HX_INIT_FAIL; + } + +/* + * if (ovl_type == 1) { + * + * g_core_fp.fp_sense_off(false); + * + * //g_core_fp.fp_register_write(mpap_addr, DATA_LEN_4, send_data, 0); + * count = 0; + * do { + * g_core_fp.fp_register_write(mpap_addr, DATA_LEN_4, send_data, 0); + * g_core_fp.fp_register_read(mpap_addr, DATA_LEN_4, recv_data, 0); + * } while ((recv_data[0] != send_data[0] || recv_data[1] != send_data[1] || recv_data[2] != send_data[2] || recv_data[3] != send_data[3]) && count++ < 10); + * + * I("%s: set mpap pw %d times\n", __func__, count); + * + * //g_core_fp.fp_write_sram_0f(fwp, sram_addr, offset, size); + * if (kp_g_core_fp->fp_write_sram_0f_crc(fwp, sram_addr, offset, size) != 0) + * E("%s, Overlay HW CRC FAIL\n", __func__); + * + * g_core_fp.fp_reload_disable(0); + * + * g_core_fp.fp_sense_on(0x00); + * + * } else { + */ + + count = 0; + do { + kp_g_core_fp->fp_register_read(handshaking_addr, DATA_LEN_4, recv_data, 0); + } while (recv_data[0] != request && count++ < 10); + + if (count < 10) { + /*g_core_fp.fp_write_sram_0f(fwp, sram_addr, offset, size);*/ + if (kp_g_core_fp->fp_write_sram_0f_crc(fwp, sram_addr, offset, size) != 0) + E("%s, Overlay HW CRC FAIL\n", __func__); + + send_data[3] = 0x00; + send_data[2] = 0x00; + send_data[1] = 0x00; + send_data[0] = reply; + + count2 = 0; + do { + kp_g_core_fp->fp_register_write(handshaking_addr, DATA_LEN_4, send_data, 0); + usleep_range(1000, 1100); + kp_g_core_fp->fp_register_read(handshaking_addr, DATA_LEN_4, recv_data, 0); + } while (recv_data[0] != reply && count2++ < 10); + + if (ovl_type == 3) { +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + } + } + + I("%s: overlay request %d times; reply %d times\n", __func__, count, count2); + + release_firmware(fwp); + + /* rescue mechanism */ + if (count >= 10) { + + kp_g_core_fp->fp_0f_op_file_dirly(fwname); + + kp_g_core_fp->fp_reload_disable(0); + + kp_g_core_fp->fp_sense_on(0x00); + + kp_himax_int_enable(1); + } + + return 0; +} +#endif +#endif + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) +static void hx83102a_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + (*kp_ic_data)->vendor_ic_ver = 0xFFFF; + /* Enable read DD */ + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x10; + tmp_data[3] = 0x2A; + kp_himax_in_parse_assign_cmd(hx83102_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83102_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; + + I("VENDOR_IC_VER : %X\n", ((*kp_ic_data)->vendor_ic_ver)); +} + +static void hx83102b_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + (*kp_ic_data)->vendor_ic_ver = 0xFFFF; + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83102b_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x01; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x10; + tmp_data[3] = 0x2B; + kp_himax_in_parse_assign_cmd(hx83102_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83102_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; + + /* Disable read DD */ + kp_himax_in_parse_assign_cmd(hx83102b_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x00; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + I("VENDOR_IC_VER : %X\n", ((*kp_ic_data)->vendor_ic_ver)); +} + +static void hx83102d_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + (*kp_ic_data)->vendor_ic_ver = 0xFFFF; + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83102d_ic_osc_pw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xA5; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + kp_himax_in_parse_assign_cmd(hx83102d_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xDD; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x10; + tmp_data[3] = 0x2D; + kp_himax_in_parse_assign_cmd(hx83102_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83102_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; + + /* Disable read DD */ + kp_himax_in_parse_assign_cmd(hx83102d_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x00; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + I("VENDOR_IC_VER : %X\n", ((*kp_ic_data)->vendor_ic_ver)); +} + +static void hx83102_dynamic_fw_name(uint8_t ic_name) +{ + char firmware_name[64]; + + if (ic_name == 0x2a) + hx83102a_read_ic_ver(); + else if (ic_name == 0x2b) + hx83102b_read_ic_ver(); + else + hx83102d_read_ic_ver(); + + if ((*kp_i_CTPM_firmware_name) != NULL) { + kfree((*kp_i_CTPM_firmware_name)); + (*kp_i_CTPM_firmware_name) = NULL; + } + memset(firmware_name, 0x00, sizeof(firmware_name)); + + if (((*kp_ic_data)->vendor_ic_ver == 0x00) || ((*kp_ic_data)->vendor_ic_ver == 0x21)) { /* Cut 1 */ + memcpy(firmware_name, "Himax_firmware_1.bin", sizeof(char)*strlen("Himax_firmware_1.bin")); + } else if (((*kp_ic_data)->vendor_ic_ver == 0x01) || ((*kp_ic_data)->vendor_ic_ver == 0x11)) { /* Cut 2 */ + memcpy(firmware_name, "Himax_firmware_2.bin", sizeof(char)*strlen("Himax_firmware_2.bin")); + } else { /* Original */ + memcpy(firmware_name, "Himax_firmware.bin", sizeof(char)*strlen("Himax_firmware.bin")); + } + + (*kp_i_CTPM_firmware_name) = kzalloc((sizeof(char)*(strlen(firmware_name)+1)), GFP_KERNEL); + if ((*kp_i_CTPM_firmware_name) != NULL) + memcpy((*kp_i_CTPM_firmware_name), firmware_name, (sizeof(char)*(strlen(firmware_name)+1))); + I("(*kp_i_CTPM_firmware_name) : %s\n", (*kp_i_CTPM_firmware_name)); +} +#endif +#endif + +static bool hx83102e_read_event_stack(uint8_t *buf, uint8_t length) +{ + struct timespec t_start, t_end, t_delta; + int len = length; + int i2c_speed = 0; + + if ((*kp_private_ts)->debug_log_level & BIT(2)) + getnstimeofday(&t_start); + + kp_himax_bus_read((*kp_pfw_op)->addr_event_addr[0], buf, length, HIMAX_I2C_RETRY_TIMES); + + if ((*kp_private_ts)->debug_log_level & BIT(2)) { + getnstimeofday(&t_end); + t_delta.tv_nsec = (t_end.tv_sec * 1000000000 + t_end.tv_nsec) - (t_start.tv_sec * 1000000000 + t_start.tv_nsec); /*ns*/ + i2c_speed = (len * 9 * 1000000 / (int)t_delta.tv_nsec) * 13 / 10; + (*kp_private_ts)->bus_speed = (int)i2c_speed; + } + return 1; +} + +static void himax_hx83102ab_reg_re_init(uint8_t ic_name) +{ + I("%s:Entering!\n", __func__); + kp_himax_in_parse_assign_cmd(hx83102ab_fw_addr_sorting_mode_en, (*kp_pfw_op)->addr_sorting_mode_en, sizeof((*kp_pfw_op)->addr_sorting_mode_en)); + kp_himax_in_parse_assign_cmd(hx83102ab_fw_addr_selftest_addr_en, (*kp_pfw_op)->addr_selftest_addr_en, sizeof((*kp_pfw_op)->addr_selftest_addr_en)); +#ifdef HX_ZERO_FLASH + kp_himax_in_parse_assign_cmd(hx83102ab_data_adc_cfg_1, (*kp_pzf_op)->data_adc_cfg_1, sizeof((*kp_pzf_op)->data_adc_cfg_1)); +#endif + + if (ic_name == 0x2a) { + kp_himax_in_parse_assign_cmd(hx83102a_data_df_rx, (*kp_pdriver_op)->data_df_rx, sizeof((*kp_pdriver_op)->data_df_rx)); + kp_himax_in_parse_assign_cmd(hx83102a_data_df_tx, (*kp_pdriver_op)->data_df_tx, sizeof((*kp_pdriver_op)->data_df_tx)); + kp_himax_in_parse_assign_cmd(hx83102a_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102a_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + } else { + kp_himax_in_parse_assign_cmd(hx83102b_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102b_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + } +} + +static void himax_hx83102ab_func_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_g_core_fp->fp_chip_init = hx83102_chip_init; + kp_g_core_fp->fp_sense_off = hx83102ab_sense_off; + kp_g_core_fp->fp_set_SMWP_enable = hx83102ab_set_SMWP_enable; + kp_g_core_fp->fp_set_HSEN_enable = hx83102ab_set_HSEN_enable; + kp_g_core_fp->fp_usb_detect_set = hx83102ab_usb_detect_set; + kp_g_core_fp->fp_read_DD_status = hx83102ab_read_DD_status; + kp_g_core_fp->fp_read_FW_status = hx83102ab_read_FW_status; + kp_g_core_fp->fp_power_on_init = hx83102ab_power_on_init; + kp_g_core_fp->fp_fts_ctpm_fw_upgrade_with_sys_fs_64k = hx83102ab_fts_ctpm_fw_upgrade_with_sys_fs_64k; +#ifdef HX_ESD_RECOVERY + kp_g_core_fp->fp_esd_ic_reset = hx83102ab_esd_ic_reset; +#endif +#if defined(HX_ZERO_FLASH) + kp_g_core_fp->fp_firmware_update_0f = hx83102ab_firmware_update_0f; +#if defined(HX_0F_DEBUG) + kp_g_core_fp->fp_firmware_read_0f = hx83102ab_firmware_read_0f; +#endif +#endif + +} + +static void himax_hx83102d_reg_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_himax_in_parse_assign_cmd(hx83102d_fw_addr_raw_out_sel, (*kp_pfw_op)->addr_raw_out_sel, sizeof((*kp_pfw_op)->addr_raw_out_sel)); +#if defined(HX_ZERO_FLASH) + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_sram_start_addr, (*kp_pzf_op)->data_sram_start_addr, sizeof((*kp_pzf_op)->data_sram_start_addr)); + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_adc_cfg_1, (*kp_pzf_op)->data_adc_cfg_1, sizeof((*kp_pzf_op)->data_adc_cfg_1)); + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_adc_cfg_2, (*kp_pzf_op)->data_adc_cfg_2, sizeof((*kp_pzf_op)->data_adc_cfg_2)); + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_adc_cfg_3, (*kp_pzf_op)->data_adc_cfg_3, sizeof((*kp_pzf_op)->data_adc_cfg_3)); +#endif + kp_himax_in_parse_assign_cmd(hx83102d_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102d_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + kp_himax_in_parse_assign_cmd(hx83102d_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + kp_himax_in_parse_assign_cmd(hx83102d_adr_osc_en, (*kp_pic_op)->adr_osc_en, sizeof((*kp_pic_op)->adr_osc_en)); + kp_himax_in_parse_assign_cmd(hx83102d_adr_osc_pw, (*kp_pic_op)->adr_osc_pw, sizeof((*kp_pic_op)->adr_osc_pw)); +} + +static void himax_hx83102d_func_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_g_core_fp->fp_chip_init = hx83102_chip_init; + kp_g_core_fp->fp_sense_off = hx83102d_sense_off; + kp_g_core_fp->fp_ic_id_read = hx83102d_ic_id_read; + kp_g_core_fp->fp_dd_clk_set = hx83102d_dd_clk_set; + kp_g_core_fp->fp_dd_reg_en = hx83102d_dd_reg_en; +#if defined(HX_ZERO_FLASH) + /*kp_g_core_fp->fp_firmware_update_0f = hx83102d_firmware_update_0f;*/ + kp_g_core_fp->fp_resume_ic_action = himax_hx83102d_resume_ic_action; + kp_g_core_fp->fp_suspend_ic_action = himax_hx83102d_suspend_ic_action; + kp_g_core_fp->fp_sense_on = himax_hx83102d_sense_on; +#if defined(HX_0F_DEBUG) + kp_g_core_fp->fp_firmware_read_0f = hx83102d_firmware_read_0f; +#endif +#if defined(HX_CODE_OVERLAY) + kp_g_core_fp->fp_0f_overlay = hx83102d_0f_overlay; +#endif +#ifdef HX_ESD_RECOVERY + kp_g_core_fp->fp_0f_esd_check = hx83102d_0f_esd_check; +#endif +#endif +} + +static void himax_hx83102e_reg_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_himax_in_parse_assign_cmd(hx83102e_fw_addr_raw_out_sel, (*kp_pfw_op)->addr_raw_out_sel, sizeof((*kp_pfw_op)->addr_raw_out_sel)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_rx, (*kp_pdriver_op)->data_df_rx, sizeof((*kp_pdriver_op)->data_df_rx)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_tx, (*kp_pdriver_op)->data_df_tx, sizeof((*kp_pdriver_op)->data_df_tx)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); +} + +static void himax_hx83102e_func_re_init(void) +{ + I("%s:Entering!\n", __func__); + + kp_g_core_fp->fp_chip_init = hx83102e_chip_init; + kp_g_core_fp->fp_sense_off = hx83102d_sense_off; + kp_g_core_fp->fp_read_event_stack = hx83102e_read_event_stack; +} + + +static bool hx83102_chip_detect(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + bool ret_data = false; + int ret = 0; + int i = 0; + + if (himax_ic_setup_external_symbols()) + return false; + +#ifdef HX_RST_PIN_FUNC + hx83102_pin_reset(); +#endif + if (kp_himax_bus_read(0x13, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + if (hx83102_sense_off(false) == false) { + ret_data = false; + E("%s:hx83102_sense_off Fail:\n", __func__); + return ret_data; + } + + for (i = 0; i < 5; i++) { + tmp_addr[3] = 0x90; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0xD0; + if (hx83102_register_read(tmp_addr, DATA_LEN_4, tmp_data) != 0) { + ret_data = false; + E("%s:hx83102_register_read Fail:\n", __func__); + return ret_data; + } + I("%s:Read driver IC ID = %X,%X,%X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1]); /*83,10,2X*/ + + if ((tmp_data[3] == 0x83) && (tmp_data[2] == 0x10) && ((tmp_data[1] == 0x2a) || (tmp_data[1] == 0x2b) || (tmp_data[1] == 0x2d) || (tmp_data[1] == 0x2e))) { + if (tmp_data[1] == 0x2a) { + strlcpy((*kp_private_ts)->chip_name, HX_83102A_SERIES_PWON, 30); + I("%s:detect IC HX83102A successfully\n", __func__); + } else if (tmp_data[1] == 0x2b) { + strlcpy((*kp_private_ts)->chip_name, HX_83102B_SERIES_PWON, 30); + I("%s:detect IC HX83102B successfully\n", __func__); + } else if (tmp_data[1] == 0x2d) { + strlcpy((*kp_private_ts)->chip_name, HX_83102D_SERIES_PWON, 30); + I("%s:detect IC HX83102D successfully\n", __func__); + } else { + strlcpy((*kp_private_ts)->chip_name, HX_83102E_SERIES_PWON, 30); + I("%s:detect IC HX83102E successfully\n", __func__); + } + + ret = kp_himax_mcu_in_cmd_struct_init(); + if (ret < 0) { + ret_data = false; + E("%s:cmd_struct_init Fail:\n", __func__); + return ret_data; + } + + kp_himax_mcu_in_cmd_init(); + if ((tmp_data[1] == 0x2a) || (tmp_data[1] == 0x2b)) { + himax_hx83102ab_reg_re_init(tmp_data[1]); + himax_hx83102ab_func_re_init(); + } else if (tmp_data[1] == 0x2d) { + himax_hx83102d_reg_re_init(); + himax_hx83102d_func_re_init(); + } else {/* 0x2e */ + himax_hx83102e_reg_re_init(); + himax_hx83102e_func_re_init(); + } + ret_data = true; + break; + } + + ret_data = false; + E("%s:Read driver ID register Fail:\n", __func__); + E("Could NOT find Himax Chipset\n"); + E("Please check 1.VCCD,VCCA,VSP,VSN\n"); + E("2. LCM_RST,TP_RST\n"); + E("3. Power On Sequence\n"); + + } + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) + hx83102_dynamic_fw_name(tmp_data[1]); +#endif +#endif + + return ret_data; +} + +DECLARE(HX_MOD_KSYM_HX83102); + +static int himax_hx83102_probe(void) +{ + I("%s:Enter\n", __func__); + himax_add_chip_dt(hx83102_chip_detect); + + return 0; +} + +static int himax_hx83102_remove(void) +{ + free_chip_dt_table(); + return 0; +} + +static int __init himax_hx83102_init(void) +{ + int ret = 0; + + I("%s\n", __func__); + ret = himax_hx83102_probe(); + return 0; +} + +static void __exit himax_hx83102_exit(void) +{ + himax_hx83102_remove(); +} + +module_init(himax_hx83102_init); +module_exit(himax_hx83102_exit); + +MODULE_DESCRIPTION("HIMAX HX83102 touch driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102/himax_ic_HX83102.h b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102/himax_ic_HX83102.h new file mode 100644 index 000000000000..ceab657ecdd0 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102/himax_ic_HX83102.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for HX83102 chipset + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_platform.h" +#include "himax_common.h" +#include "himax_ic_core.h" +#include + +#define hx83102ab_fw_addr_sorting_mode_en 0x100007FC +#define hx83102ab_fw_addr_selftest_addr_en 0x100007F8 +#define hx83102ab_data_adc_cfg_1 0x10007B00 +#define hx83102a_data_df_rx 48 +#define hx83102a_data_df_tx 24 +#define hx83102a_data_df_x_res 2160 +#define hx83102a_data_df_y_res 3840 +#define hx83102b_data_df_x_res 720 +#define hx83102b_data_df_y_res 1280 + +#define hx83102d_fw_addr_raw_out_sel 0x800204f4 +#define hx83102d_zf_data_adc_cfg_1 0x10007B00 +#define hx83102d_zf_data_adc_cfg_2 0x10006A00 +#define hx83102d_zf_data_adc_cfg_3 0x10007500 +#define hx83102d_zf_data_bor_prevent_info 0x10007268 +#define hx83102d_zf_data_notch_info 0x10007300 +#define hx83102d_zf_func_info_en 0x10007FD0 +#define hx83102d_zf_po_sub_func 0x10005A00 +#define hx83102d_zf_data_sram_start_addr 0x20000000 +#define hx83102d_data_df_x_res 720 +#define hx83102d_data_df_y_res 1280 +#define hx83102d_adr_osc_en 0x9000009C +#define hx83102d_adr_osc_pw 0x90000280 + +#define hx83102e_fw_addr_raw_out_sel 0x100072EC +#define hx83102e_data_df_rx 48 +#define hx83102e_data_df_tx 30 +#define hx83102e_data_df_x_res 1200 +#define hx83102e_data_df_y_res 1920 + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#ifdef HX_EN_DYNAMIC_NAME +#define hx83102_ic_b9_en 0x300B9000 +#define hx83102_ic_fw 0x300E8006 + +#define hx83102b_ic_osc_en 0x900880A0 +#define hx83102d_ic_osc_en 0x9000009C +#define hx83102d_ic_osc_pw 0x90000280 +#endif +#endif + +#ifdef HX_ESD_RECOVERY + extern u8 HX_ESD_RESET_ACTIVATE; +#endif + diff --git a/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102d/Android.mk b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102d/Android.mk new file mode 100644 index 000000000000..3f4c67ad8ec2 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102d/Android.mk @@ -0,0 +1,19 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +HIMAX_MMI_IC_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +HIMAX_MMI_IC_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +ifeq ($(HIMAX_V2_MMI_HX83102D),true) +KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_V2_MMI_IC_NAME_D=hx83102d +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := himax_v2_mmi_hx83102d.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(HIMAX_MMI_IC_MODULE_PATH) +LOCAL_REQUIRED_MODULES := himax_v2_mmi.ko +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102d/Kbuild b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102d/Kbuild new file mode 100644 index 000000000000..f495e15c50c8 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102d/Kbuild @@ -0,0 +1,20 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_v2_mmi \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_v2_mmi/support_ic \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102d +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_DEBUG \ + -DCONFIG_TOUCHSCREEN_HIMAX_INSPECT + +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_INCELL +EXTRA_CFLAGS += -DCONFIG_HMX_DB + +ifneq ($(filter hx83102d,$(CONFIG_INPUT_HIMAX_V2_MMI_IC_NAME_D)),) + EXTRA_CFLAGS += -D__HIMAX_HX83102D_MOD__ + EXTRA_CFLAGS += -DHX_ZERO_FLASH + EXTRA_CFLAGS += -DHX_CODE_OVERLAY +endif + +obj-m := himax_v2_mmi_hx83102d.o +himax_v2_mmi_hx83102d-objs += himax_ic_HX83102d.o diff --git a/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102d/Makefile b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102d/Makefile new file mode 100755 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102d/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102d/himax_ic_HX83102d.c b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102d/himax_ic_HX83102d.c new file mode 100644 index 000000000000..5f095fdebce3 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102d/himax_ic_HX83102d.c @@ -0,0 +1,1452 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for HX83102 chipset + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_ic_HX83102d.h" +#include "himax_modular.h" + +static void hx83102_chip_init(void) +{ + (*kp_private_ts)->chip_cell_type = CHIP_IS_IN_CELL; + I("%s:IC cell type = %d\n", __func__, (*kp_private_ts)->chip_cell_type); + (*kp_IC_CHECKSUM) = HX_TP_BIN_CHECKSUM_CRC; + /*Himax: Set FW and CFG Flash Address*/ + (*kp_FW_VER_MAJ_FLASH_ADDR) = 49157; /*0x00C005*/ + (*kp_FW_VER_MAJ_FLASH_LENG) = 1; + (*kp_FW_VER_MIN_FLASH_ADDR) = 49158; /*0x00C006*/ + (*kp_FW_VER_MIN_FLASH_LENG) = 1; + (*kp_CFG_VER_MAJ_FLASH_ADDR) = 49408; /*0x00C100*/ + (*kp_CFG_VER_MAJ_FLASH_LENG) = 1; + (*kp_CFG_VER_MIN_FLASH_ADDR) = 49409; /*0x00C101*/ + (*kp_CFG_VER_MIN_FLASH_LENG) = 1; + (*kp_CID_VER_MAJ_FLASH_ADDR) = 49154; /*0x00C002*/ + (*kp_CID_VER_MAJ_FLASH_LENG) = 1; + (*kp_CID_VER_MIN_FLASH_ADDR) = 49155; /*0x00C003*/ + (*kp_CID_VER_MIN_FLASH_LENG) = 1; + /*PANEL_VERSION_ADDR = 49156;*/ /*0x00C004*/ + /*PANEL_VERSION_LENG = 1;*/ +} + +static void hx83102e_chip_init(void) +{ + (*kp_private_ts)->chip_cell_type = CHIP_IS_IN_CELL; + I("%s:IC cell type = %d\n", __func__, (*kp_private_ts)->chip_cell_type); + (*kp_IC_CHECKSUM) = HX_TP_BIN_CHECKSUM_CRC; + /*Himax: Set FW and CFG Flash Address*/ + (*kp_FW_VER_MAJ_FLASH_ADDR) = 59397; /*0x00E805*/ + (*kp_FW_VER_MAJ_FLASH_LENG) = 1; + (*kp_FW_VER_MIN_FLASH_ADDR) = 59398; /*0x00E806*/ + (*kp_FW_VER_MIN_FLASH_LENG) = 1; + (*kp_CFG_VER_MAJ_FLASH_ADDR) = 59648; /*0x00E900*/ + (*kp_CFG_VER_MAJ_FLASH_LENG) = 1; + (*kp_CFG_VER_MIN_FLASH_ADDR) = 59649; /*0x00E901*/ + (*kp_CFG_VER_MIN_FLASH_LENG) = 1; + (*kp_CID_VER_MAJ_FLASH_ADDR) = 59394; /*0x00E802*/ + (*kp_CID_VER_MAJ_FLASH_LENG) = 1; + (*kp_CID_VER_MIN_FLASH_ADDR) = 59395; /*0x00E803*/ + (*kp_CID_VER_MIN_FLASH_LENG) = 1; + /*PANEL_VERSION_ADDR = 49156;*/ /*0x00C004*/ + /*PANEL_VERSION_LENG = 1;*/ +} + +void hx83102_burst_enable(uint8_t auto_add_4_byte) +{ + uint8_t tmp_data[4]; + + tmp_data[0] = 0x31; + if (kp_himax_bus_write(0x13, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + tmp_data[0] = (0x10 | auto_add_4_byte); + if (kp_himax_bus_write(0x0D, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } +} + +int hx83102_flash_write_burst(uint8_t *reg_byte, uint8_t *write_data) +{ + uint8_t data_byte[8]; + int i = 0, j = 0; + + for (i = 0; i < 4; i++) + data_byte[i] = reg_byte[i]; + for (j = 4; j < 8; j++) + data_byte[j] = write_data[j-4]; + + if (kp_himax_bus_write(0x00, data_byte, 8, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + return 0; +} + +static int hx83102_register_read(uint8_t *read_addr, int read_length, uint8_t *read_data) +{ + uint8_t tmp_data[4]; + int i = 0; + int address = 0; + + if (read_length > 256) { + E("%s: read len over 256!\n", __func__); + return LENGTH_FAIL; + } + if (read_length > 4) + hx83102_burst_enable(1); + else + hx83102_burst_enable(0); + + address = (read_addr[3] << 24) + (read_addr[2] << 16) + (read_addr[1] << 8) + read_addr[0]; + i = address; + tmp_data[0] = (uint8_t)i; + tmp_data[1] = (uint8_t)(i >> 8); + tmp_data[2] = (uint8_t)(i >> 16); + tmp_data[3] = (uint8_t)(i >> 24); + if (kp_himax_bus_write(0x00, tmp_data, 4, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + tmp_data[0] = 0x00; + if (kp_himax_bus_write(0x0C, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + if (kp_himax_bus_read(0x08, read_data, read_length, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + if (read_length > 4) + hx83102_burst_enable(0); + + return 0; +} + +#ifdef HX_RST_PIN_FUNC +static void hx83102_pin_reset(void) +{ + I("%s: Now reset the Touch chip.\n", __func__); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 0); + msleep(20); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 1); + msleep(50); +} +#endif + +static bool hx83102_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t tmp_writ[DATA_LEN_4]; + uint8_t tmp_data[DATA_LEN_4]; + + do { + if (cnt == 0 || (tmp_data[0] != 0xA5 && tmp_data[0] != 0x00 && tmp_data[0] != 0x87)) { + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x5C; + tmp_writ[3] = 0x00; tmp_writ[2] = 0x00; tmp_writ[1] = 0x00; tmp_writ[0] = 0xA5; + hx83102_flash_write_burst(tmp_addr, tmp_writ); + } + msleep(20); + + /* check fw status */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, DATA_LEN_4, tmp_data); + + if (tmp_data[0] != 0x05) { + I("%s: Do not need wait FW, Status = 0x%02X!\n", __func__, tmp_data[0]); + break; + } + + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x5C; + hx83102_register_read(tmp_addr, DATA_LEN_4, tmp_data); + I("%s: cnt = %d, data[0] = 0x%02X!\n", __func__, cnt, tmp_data[0]); + } while (tmp_data[0] != 0x87 && (++cnt < 50) && check_en == true); + + cnt = 0; + + + do { + /*=========================================== + *I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + *=========================================== + */ + tmp_data[0] = 0x27; + if (kp_himax_bus_write(0x31, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + *I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + *=========================================== + */ + tmp_data[0] = 0x95; + if (kp_himax_bus_write(0x32, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + *Check enter_save_mode + *====================== + */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, ADDR_LEN_4, tmp_data); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /*===================================== + *Reset TCON + *===================================== + */ + tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + /*===================================== + *Reset ADC + *===================================== + */ + tmp_addr[3] = 0x80; + tmp_addr[2] = 0x02; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + + return true; + } + usleep_range(10000, 10001); +#ifdef HX_RST_PIN_FUNC + hx83102_pin_reset(); +#endif + + } while (cnt++ < 15); + + return false; +} + +static bool hx83102ab_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t tmp_data[DATA_LEN_4]; + + do { + /*=========================================== + *I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + *=========================================== + */ + tmp_data[0] = 0x27; + if (kp_himax_bus_write(0x31, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + *I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + *=========================================== + */ + tmp_data[0] = 0x95; + if (kp_himax_bus_write(0x32, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + *Check enter_save_mode + *====================== + */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, ADDR_LEN_4, tmp_data); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /*===================================== + *Reset TCON + *===================================== + */ + tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + /*===================================== + *Reset ADC + *===================================== + */ + tmp_addr[3] = 0x80; + tmp_addr[2] = 0x02; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + + return true; + } + /*msleep(10);*/ + usleep_range(5000, 5001); +#ifdef HX_RST_PIN_FUNC + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 0); + msleep(20); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 1); + msleep(50); +#endif + + } while (cnt++ < 5); + + return false; +} + +static void hx83102ab_set_SMWP_enable(uint8_t SMWP_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t retry_cnt = 0; + + kp_g_core_fp->fp_sense_off(true); + + do { + if (SMWP_enable) { + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, back_data, 4); + } else { + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + } + + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_smwp_enable, DATA_LEN_4, tmp_data, false); + /*I("%s: tmp_data[0]=%d, SMWP_enable=%d, retry_cnt=%d\n", __func__, tmp_data[0],SMWP_enable,retry_cnt);*/ + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); + + kp_g_core_fp->fp_sense_on(0x00); +} + +static void hx83102ab_set_HSEN_enable(uint8_t HSEN_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + + if (HSEN_enable) { + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + } else { + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + } + + /*I("%s: tmp_data[0]=%d, HSEN_enable=%d, retry_cnt=%d\n", __func__, tmp_data[0],HSEN_enable,retry_cnt);*/ +} + +static void hx83102ab_usb_detect_set(uint8_t *cable_config) +{ + uint8_t tmp_data[DATA_LEN_4]; + + if (cable_config[1] == 0x01) { + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + I("%s: USB detect status IN!\n", __func__); + } else { + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + I("%s: USB detect status OUT!\n", __func__); + } +} + +static uint8_t hx83102ab_read_DD_status(uint8_t *cmd_set, uint8_t *tmp_data) +{ + int cnt = 0; + uint8_t req_size = cmd_set[0]; + + cmd_set[3] = (*kp_pfw_op)->data_dd_request[0]; + + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_dd_handshak_addr, DATA_LEN_4, cmd_set, 0); + I("%s: cmd_set[0] = 0x%02X,cmd_set[1] = 0x%02X,cmd_set[2] = 0x%02X,cmd_set[3] = 0x%02X\n", + __func__, cmd_set[0], cmd_set[1], cmd_set[2], cmd_set[3]); + + /* Doing hand shaking 0xAA -> 0xBB */ + for (cnt = 0; cnt < 100; cnt++) { + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_dd_handshak_addr, DATA_LEN_4, tmp_data, false); + usleep_range(10000, 10001); + + if (tmp_data[3] == (*kp_pfw_op)->data_dd_ack[0]) { + I("%s Data ready goto moving data\n", __func__); + break; + } + if (cnt >= 99) { + I("%s Data not ready in FW\n", __func__); + return FW_NOT_READY; + } + } + + kp_g_core_fp->fp_sense_off(true); + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_dd_data_addr, req_size, tmp_data, false); + kp_g_core_fp->fp_sense_on(0x01); + return NO_ERR; +} + +static int hx83102ab_read_FW_status(uint8_t *state_addr, uint8_t *tmp_addr) +{ + uint8_t i; + uint8_t req_size = 0; + uint8_t status_addr[DATA_LEN_4]; /*0x10007F44*/ + uint8_t cmd_addr[DATA_LEN_4]; /*0x900000F8*/ + + if (state_addr[0] == 0x01) { + state_addr[1] = 0x04; + + for (i = 0; i < DATA_LEN_4; i++) { + /* 0x10007F44 */ + state_addr[i + 2] = (*kp_pfw_op)->addr_fw_dbg_msg_addr[i]; + status_addr[i] = (*kp_pfw_op)->addr_fw_dbg_msg_addr[i]; + } + + req_size = 0x04; + kp_g_core_fp->fp_sense_off(true); + kp_g_core_fp->fp_register_read(status_addr, req_size, tmp_addr, false); + kp_g_core_fp->fp_sense_on(0x01); + } else if (state_addr[0] == 0x02) { + state_addr[1] = 0x30; + + for (i = 0; i < DATA_LEN_4; i++) { + /* 0x900000F8 */ + state_addr[i + 2] = (*kp_pfw_op)->addr_fw_state_addr[i]; + cmd_addr[i] = (*kp_pfw_op)->addr_fw_state_addr[i]; + } + + req_size = 0x30; + kp_g_core_fp->fp_register_read(cmd_addr, req_size, tmp_addr, false); + } + + return NO_ERR; +} + +static void hx83102ab_power_on_init(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + + I("%s:\n", __func__); + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_raw_out_sel, DATA_LEN_4, tmp_data, 0); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_sorting_mode_en, DATA_LEN_4, tmp_data, 0); + kp_g_core_fp->fp_touch_information(); + kp_g_core_fp->fp_sense_on(0x00); +} + +static int hx83102ab_fts_ctpm_fw_upgrade_with_sys_fs_64k(unsigned char *fw, int len, bool change_iref) +{ + int burnFW_success = 0; + uint8_t tmp_data[DATA_LEN_4]; + + if (len != FW_SIZE_64k) { + E("%s: The file size is not 64K bytes\n", __func__); + return false; + } + +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + kp_g_core_fp->fp_sense_off(true); + kp_g_core_fp->fp_chip_erase(); + kp_g_core_fp->fp_flash_programming(fw, FW_SIZE_64k); + + if (kp_g_core_fp->fp_check_CRC((*kp_pfw_op)->addr_program_reload_from, FW_SIZE_64k) == 0) + burnFW_success = 1; + + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_raw_out_sel, DATA_LEN_4, tmp_data, 0); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_sorting_mode_en, DATA_LEN_4, tmp_data, 0); +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + /*System reset*/ + kp_g_core_fp->fp_system_reset(); +#endif + return burnFW_success; +} + +#ifdef HX_ESD_RECOVERY +static void hx83102ab_esd_ic_reset(void) +{ + (*kp_HX_ESD_RESET_ACTIVATE) = 1; +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_pin_reset(); +#else + kp_g_core_fp->fp_system_reset(); +#endif + I("%s:\n", __func__); +} +#if defined(HX_ZERO_FLASH) +static int hx83102d_0f_esd_check(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + int ret = NO_ERR; + + I("Enter %s\n", __func__); + + kp_g_core_fp->fp_register_read((*kp_pzf_op)->addr_sts_chk, DATA_LEN_4, tmp_data, 0); + + if (tmp_data[0] != (*kp_pzf_op)->data_activ_sts[0]) { + ret = ERR_STS_WRONG; + I("%s:status : %8X = %2X\n", __func__, zf_addr_sts_chk, tmp_data[0]); + } + + kp_g_core_fp->fp_register_read((*kp_pzf_op)->addr_activ_relod, DATA_LEN_4, tmp_data, 0); + + if (tmp_data[0] != (*kp_pzf_op)->data_activ_in[0]) { + ret = ERR_STS_WRONG; + I("%s:status : %8X = %2X\n", __func__, zf_addr_activ_relod, tmp_data[0]); + } + + return ret; +} +#endif +#endif + +static bool hx83102d_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t tmp_data[DATA_LEN_4]; + + do { + if (cnt == 0 || (tmp_data[0] != 0xA5 && tmp_data[0] != 0x00 && tmp_data[0] != 0x87)) + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_ctrl_fw_isr, DATA_LEN_4, (*kp_pfw_op)->data_fw_stop, 0); + /*msleep(20);*/ + usleep_range(10000, 10001); + + /* check fw status */ + kp_g_core_fp->fp_register_read((*kp_pic_op)->addr_cs_central_state, ADDR_LEN_4, tmp_data, 0); + + if (tmp_data[0] != 0x05) { + I("%s: Do not need wait FW, Status = 0x%02X!\n", __func__, tmp_data[0]); + break; + } + + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_ctrl_fw_isr, 4, tmp_data, false); + I("%s: cnt = %d, data[0] = 0x%02X!\n", __func__, cnt, tmp_data[0]); + } while (tmp_data[0] != 0x87 && (++cnt < 10) && check_en == true); + + cnt = 0; + + do { + /*=========================================== + *I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + *=========================================== + */ + tmp_data[0] = 0x27; + if (kp_himax_bus_write(0x31, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + *I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + *=========================================== + */ + tmp_data[0] = 0x95; + if (kp_himax_bus_write(0x32, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + *Check enter_save_mode + *====================== + */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, ADDR_LEN_4, tmp_data); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /*===================================== + *Reset TCON + *===================================== + */ + tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + /*===================================== + *Reset ADC + *===================================== + */ + tmp_addr[3] = 0x80; + tmp_addr[2] = 0x02; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + + return true; + } + /*msleep(10);*/ + usleep_range(5000, 5001); +#ifdef HX_RST_PIN_FUNC + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 0); + msleep(20); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 1); + msleep(50); +#endif + + } while (cnt++ < 5); + + return false; +} + +static bool hx83102d_dd_clk_set(bool enable) +{ + uint8_t data[4] = {0}; + + data[0] = (enable)?0xDD:0; + return (kp_g_core_fp->fp_register_write((*kp_pic_op)->adr_osc_en, + sizeof((*kp_pic_op)->adr_osc_en), data, 0) == NO_ERR); +} + +static void hx83102d_dd_reg_en(bool enable) +{ + uint8_t data[4] = {0}; + + data[0] = 0xA5; data[1] = 0x00; + data[2] = 0x00; data[3] = 0x00; + kp_g_core_fp->fp_register_write((*kp_pic_op)->adr_osc_pw, DATA_LEN_4, data, 0); + + data[0] = 0xA5; data[1] = 0x83; + data[2] = 0x10; data[3] = 0x2D; + kp_g_core_fp->fp_dd_reg_write(0xB9, 0, 4, data, 0); +} + +static bool hx83102d_ic_id_read(void) +{ + int i = 0; + uint8_t data[4] = {0}; + + kp_g_core_fp->fp_dd_clk_set(true); + kp_g_core_fp->fp_dd_reg_en(true); + + for (i = 0; i < 13; i++) { + data[0] = 0x2A + i; + kp_g_core_fp->fp_dd_reg_write(0xBB, 3, 1, data, 0); + data[0] = 0x08; + kp_g_core_fp->fp_dd_reg_write(0xBB, 1, 1, data, 0); + kp_g_core_fp->fp_dd_reg_read(0xBB, 6, 1, data, 0); + (*kp_ic_data)->vendor_ic_id[i] = data[0]; + I("ic_data->vendor_ic_id[%d] = %02X\n", i, (*kp_ic_data)->vendor_ic_id[i]); + } + + kp_g_core_fp->fp_dd_clk_set(false); + + return true; +} + +#if defined(HX_ZERO_FLASH) +static void himax_hx83102d_reload_to_active(void) +{ + uint8_t addr[DATA_LEN_4] = {0}; + uint8_t data[DATA_LEN_4] = {0}; + uint8_t retry_cnt = 0; + int ret = 0; + + addr[3] = 0x90; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x48; + + do { + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0xEC; + ret = kp_g_core_fp->fp_register_write(addr, DATA_LEN_4, data, 0); + usleep_range(1000, 1100); + ret = kp_g_core_fp->fp_register_read(addr, DATA_LEN_4, data, 0); + I("%s: data[1]=%d, data[0]=%d, reload_to_active_cnt=%d\n", __func__, data[1], data[0], retry_cnt); + retry_cnt++; + } while ((data[1] != 0x01 || data[0] != 0xEC) && retry_cnt < HIMAX_REG_RETRY_TIMES); + if (ret != 0) + atomic_set(&(*kp_private_ts)->resume_update_fail, 1); +} + +static void himax_hx83102d_resume_ic_action(void) +{ +#ifndef HX_RESUME_HW_RESET + himax_hx83102d_reload_to_active(); +#endif +} + +static void himax_hx83102d_suspend_ic_action(void) +{ +#ifndef HX_RESUME_HW_RESET + himax_hx83102d_reload_to_active(); +#endif +} + +static void himax_hx83102d_sense_on(uint8_t FlashMode) +{ + uint8_t tmp_data[DATA_LEN_4]; + int retry = 0; + + I("Enter %s\n", __func__); + + kp_g_core_fp->fp_interface_on(); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_ctrl_fw_isr, + sizeof((*kp_pfw_op)->data_clear), (*kp_pfw_op)->data_clear, 0); + /*msleep(20);*/ + usleep_range(10000, 10001); + if (!FlashMode) { +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + himax_hx83102d_reload_to_active(); + } else { + himax_hx83102d_reload_to_active(); + do { + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_safe_mode_release_pw, + sizeof((*kp_pfw_op)->data_safe_mode_release_pw_active), (*kp_pfw_op)->data_safe_mode_release_pw_active, 0); + + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_flag_reset_event, DATA_LEN_4, tmp_data, 0); + I("%s:Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + } while ((tmp_data[1] != 0x01 || tmp_data[0] != 0x00) && retry++ < 5); + + if (retry >= 5) { + E("%s: Fail:\n", __func__); +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + himax_hx83102d_reload_to_active(); + } else { + I("%s:OK and Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + /* reset code*/ + tmp_data[0] = 0x00; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_safe_mode_release_pw, + sizeof((*kp_pfw_op)->data_safe_mode_release_pw_reset), (*kp_pfw_op)->data_safe_mode_release_pw_reset, 0); + } + } +} + +static void hx83102ab_firmware_update_0f(const struct firmware *fw_entry) +{ + uint8_t tmp_addr[4]; + + I("%s, Entering\n", __func__); + + kp_g_core_fp->fp_register_write((*kp_pzf_op)->addr_system_reset, 4, (*kp_pzf_op)->data_system_reset, 0); + + kp_g_core_fp->fp_sense_off(false); + + /* first 32K */ + /*bin file start*/ + /*isram*/ + kp_g_core_fp->fp_write_sram_0f(fw_entry, (*kp_pzf_op)->data_sram_start_addr, 0, HX_32K_SZ); + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x73; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_write_sram_0f(fw_entry, tmp_addr, 0x8000, (0x200*4)); + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x68; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_write_sram_0f(fw_entry, tmp_addr, 0x8800, (0x200*4)); + + /*last 16k*/ + /*config info*/ + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x72; + tmp_addr[0] = 0x04; /*0x10007206 <= size 60,from 0x0000C180*/ + kp_g_core_fp->fp_write_sram_0f(fw_entry, tmp_addr, 0xC178, (84)); + + /*FW config*/ + if ((*kp_G_POWERONOF) == 1) + kp_g_core_fp->fp_write_sram_0f(fw_entry, (*kp_pzf_op)->data_fw_cfg_1, 0xBFFE, 388); + else + kp_g_core_fp->fp_clean_sram_0f((*kp_pzf_op)->data_fw_cfg_1, 388, 1); + /*ADC config*/ + if ((*kp_G_POWERONOF) == 1) + kp_g_core_fp->fp_write_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_1, 0xe000, (128 * 4)); + else + kp_g_core_fp->fp_clean_sram_0f((*kp_pzf_op)->data_adc_cfg_1, (128 * 4), 2); + + I("%s, END\n", __func__); +} +#if defined(HX_0F_DEBUG) +static void hx83102ab_firmware_read_0f(const struct firmware *fw_entry, int type) +{ + uint8_t tmp_addr[4]; + + I("%s, Entering\n", __func__); + if (type == 0) { /* first 32K */ + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_sram_start_addr, 0, HX_32K_SZ); + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x68; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_addr, 0x8000, (0x200*4)); + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x73; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_addr, 0x8800, (0x200*4)); + + } else { /*last 16k*/ + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x72; + tmp_addr[0] = 0x04; /*0x10007206 <= size 80,from 0x0000C180*/ + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_addr, 0xC178, (84)); + + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_fw_cfg_1, 0xBFFE, 388); + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_1, 0xE000, (128*4)); + + } + I("%s, END\n", __func__); +} +static void hx83102d_firmware_read_0f(const struct firmware *fw_entry, int type) +{ + uint8_t tmp_data[4]; + + I("%s, Entering\n", __func__); + + switch (type) { + case 0: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_sram_start_addr, 0, HX_40K_SZ); + break; + case 1: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_cfg_info, 0xC000, 132); + break; + case 2: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_fw_cfg_1, 0xC0FE, 484); + break; + case 3: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_1, 0xD000, 768); + break; + case 4: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_2, 0xD300, 1536); + break; + case 5: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_3, 0xE000, 1536); + break; + case 6: + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_bor_prevent_info, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xC9E0, 32); + break; + case 7: + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_notch_info, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xCA00, 128); + break; + case 8: + kp_himax_in_parse_assign_cmd(hx83102d_zf_func_info_en, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xCB00, 12); + break; + case 9: + kp_himax_in_parse_assign_cmd(hx83102d_zf_po_sub_func, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xA000, HX4K); + break; + default: + break; + } + I("%s, END\n", __func__); +} +#endif +#if defined(HX_CODE_OVERLAY) +static int hx83102d_0f_overlay(int ovl_type, int mode) +{ + int ret = 0; + uint8_t count = 0; + uint8_t count2 = 0; + const struct firmware *fwp = NULL; + char fwname[32] = "Himax_firmware.bin"; + uint8_t part_num = 0; + uint8_t buf[16]; + uint8_t handshaking_addr[4] = {0xFC, 0x7F, 0x00, 0x10}; + /*uint8_t mpap_addr[4] = {0xEC, 0x73, 0x00, 0x10};*/ + uint8_t ovl_idx_t; + uint8_t request; + uint8_t reply; + uint8_t sram_addr[4] = {0}; + uint32_t offset = 0; + uint32_t size = 0; + uint8_t send_data[4] = {0}; + uint8_t recv_data[4] = {0}; + + ret = request_firmware(&fwp, fwname, (*kp_private_ts)->dev); + if (ret < 0) { + E("%s: request firmware FAIL!!!\n", __func__); + return ret; + } + + /*1. get number of partition*/ + part_num = fwp->data[HX64K + 12]; + if (part_num <= 1) { + E("%s, size of cfg part failed! part_num = %d\n", + __func__, part_num); + return LENGTH_FAIL; + } + + I("%s: overlay section %d\n", __func__, ovl_type-1); + if (ovl_type == 2) { + request = ovl_gesture_request; + reply = ovl_gesture_reply; + } else if (ovl_type == 3) { + request = ovl_border_request; + reply = ovl_border_reply; + } else { + E("%s: error overlay type %d\n", __func__, ovl_type); + return HX_INIT_FAIL; + } + ovl_idx_t = *((*kp_ovl_idx) + ovl_type - 1); + memcpy(buf, &fwp->data[ovl_idx_t * 0x10 + HX64K], 16); + memcpy(sram_addr, buf, 4); + offset = buf[11]<<24 | buf[10]<<16 | buf[9]<<8 | buf[8]; + size = buf[7]<<24 | buf[6]<<16 | buf[5]<<8 | buf[4]; + +/* + * if (ovl_type == 1) { + * if (mode == 0) { + * //border overlay when finish self_test + * send_data[3] = 0x00; + * send_data[2] = 0x00; + * send_data[1] = 0x00; + * send_data[0] = 0x00; + * offset = zf_info_arr[2].fw_addr; + * size = zf_info_arr[2].write_size; + * I("%s: self test overlay section 2\n", __func__); + * } else if (mode == 1) { + * //sorting overlay when call self_test + * send_data[3] = 0x00; + * send_data[2] = 0x10; + * send_data[1] = 0x73; + * send_data[0] = 0x80; + * offset = zf_info_arr[0].fw_addr; + * size = zf_info_arr[0].write_size; + * I("%s: self test overlay section 0\n", __func__); + * } + * } + */ + +/* + * if (ovl_type == 1) { + * + * g_core_fp.fp_sense_off(false); + * + * //g_core_fp.fp_register_write(mpap_addr, DATA_LEN_4, send_data, 0); + * count = 0; + * do { + * g_core_fp.fp_register_write(mpap_addr, DATA_LEN_4, send_data, 0); + * g_core_fp.fp_register_read(mpap_addr, DATA_LEN_4, recv_data, 0); + * } while ((recv_data[0] != send_data[0] || recv_data[1] != send_data[1] || recv_data[2] != send_data[2] || recv_data[3] != send_data[3]) && count++ < 10); + * + * I("%s: set mpap pw %d times\n", __func__, count); + * + * //g_core_fp.fp_write_sram_0f(fwp, sram_addr, offset, size); + * if (kp_g_core_fp->fp_write_sram_0f_crc(fwp, sram_addr, offset, size) != 0) + * E("%s, Overlay HW CRC FAIL\n", __func__); + * + * g_core_fp.fp_reload_disable(0); + * + * g_core_fp.fp_sense_on(0x00); + * + * } else { + */ + + count = 0; + do { + kp_g_core_fp->fp_register_read(handshaking_addr, DATA_LEN_4, recv_data, 0); + } while (recv_data[0] != request && count++ < 10); + + if (count < 10) { + /*g_core_fp.fp_write_sram_0f(fwp, sram_addr, offset, size);*/ + if (kp_g_core_fp->fp_write_sram_0f_crc(fwp, sram_addr, offset, size) != 0) + E("%s, Overlay HW CRC FAIL\n", __func__); + + send_data[3] = 0x00; + send_data[2] = 0x00; + send_data[1] = 0x00; + send_data[0] = reply; + + count2 = 0; + do { + kp_g_core_fp->fp_register_write(handshaking_addr, DATA_LEN_4, send_data, 0); + usleep_range(1000, 1100); + kp_g_core_fp->fp_register_read(handshaking_addr, DATA_LEN_4, recv_data, 0); + } while (recv_data[0] != reply && count2++ < 10); + + if (ovl_type == 3) { +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + } + } + + I("%s: overlay request %d times; reply %d times\n", __func__, count, count2); + + release_firmware(fwp); + + /* rescue mechanism */ + if (count >= 10) { + + kp_g_core_fp->fp_0f_op_file_dirly(fwname); + + kp_g_core_fp->fp_reload_disable(0); + + kp_g_core_fp->fp_sense_on(0x00); + + kp_himax_int_enable(1); + } + + return 0; +} +#endif +#endif + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) +static void hx83102a_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + (*kp_ic_data)->vendor_ic_ver = 0xFFFF; + /* Enable read DD */ + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x10; + tmp_data[3] = 0x2A; + kp_himax_in_parse_assign_cmd(hx83102_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83102_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; + + I("VENDOR_IC_VER : %X\n", ((*kp_ic_data)->vendor_ic_ver)); +} + +static void hx83102b_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + (*kp_ic_data)->vendor_ic_ver = 0xFFFF; + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83102b_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x01; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x10; + tmp_data[3] = 0x2B; + kp_himax_in_parse_assign_cmd(hx83102_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83102_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; + + /* Disable read DD */ + kp_himax_in_parse_assign_cmd(hx83102b_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x00; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + I("VENDOR_IC_VER : %X\n", ((*kp_ic_data)->vendor_ic_ver)); +} + +static void hx83102d_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + (*kp_ic_data)->vendor_ic_ver = 0xFFFF; + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83102d_ic_osc_pw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xA5; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + kp_himax_in_parse_assign_cmd(hx83102d_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xDD; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x10; + tmp_data[3] = 0x2D; + kp_himax_in_parse_assign_cmd(hx83102_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83102_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; + + /* Disable read DD */ + kp_himax_in_parse_assign_cmd(hx83102d_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x00; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + I("VENDOR_IC_VER : %X\n", ((*kp_ic_data)->vendor_ic_ver)); +} + +static void hx83102_dynamic_fw_name(uint8_t ic_name) +{ + char firmware_name[64]; + + if (ic_name == 0x2a) + hx83102a_read_ic_ver(); + else if (ic_name == 0x2b) + hx83102b_read_ic_ver(); + else + hx83102d_read_ic_ver(); + + if ((*kp_i_CTPM_firmware_name) != NULL) { + kfree((*kp_i_CTPM_firmware_name)); + (*kp_i_CTPM_firmware_name) = NULL; + } + memset(firmware_name, 0x00, sizeof(firmware_name)); + + if (((*kp_ic_data)->vendor_ic_ver == 0x00) || ((*kp_ic_data)->vendor_ic_ver == 0x21)) { /* Cut 1 */ + memcpy(firmware_name, "Himax_firmware_1.bin", sizeof(char)*strlen("Himax_firmware_1.bin")); + } else if (((*kp_ic_data)->vendor_ic_ver == 0x01) || ((*kp_ic_data)->vendor_ic_ver == 0x11)) { /* Cut 2 */ + memcpy(firmware_name, "Himax_firmware_2.bin", sizeof(char)*strlen("Himax_firmware_2.bin")); + } else { /* Original */ + memcpy(firmware_name, "Himax_firmware.bin", sizeof(char)*strlen("Himax_firmware.bin")); + } + + (*kp_i_CTPM_firmware_name) = kzalloc((sizeof(char)*(strlen(firmware_name)+1)), GFP_KERNEL); + if ((*kp_i_CTPM_firmware_name) != NULL) + memcpy((*kp_i_CTPM_firmware_name), firmware_name, (sizeof(char)*(strlen(firmware_name)+1))); + I("(*kp_i_CTPM_firmware_name) : %s\n", (*kp_i_CTPM_firmware_name)); +} +#endif +#endif + +static bool hx83102e_read_event_stack(uint8_t *buf, uint8_t length) +{ + struct timespec t_start, t_end, t_delta; + int len = length; + int i2c_speed = 0; + + if ((*kp_private_ts)->debug_log_level & BIT(2)) + getnstimeofday(&t_start); + + kp_himax_bus_read((*kp_pfw_op)->addr_event_addr[0], buf, length, HIMAX_I2C_RETRY_TIMES); + + if ((*kp_private_ts)->debug_log_level & BIT(2)) { + getnstimeofday(&t_end); + t_delta.tv_nsec = (t_end.tv_sec * 1000000000 + t_end.tv_nsec) - (t_start.tv_sec * 1000000000 + t_start.tv_nsec); /*ns*/ + i2c_speed = (len * 9 * 1000000 / (int)t_delta.tv_nsec) * 13 / 10; + (*kp_private_ts)->bus_speed = (int)i2c_speed; + } + return 1; +} + +static void himax_hx83102ab_reg_re_init(uint8_t ic_name) +{ + I("%s:Entering!\n", __func__); + kp_himax_in_parse_assign_cmd(hx83102ab_fw_addr_sorting_mode_en, (*kp_pfw_op)->addr_sorting_mode_en, sizeof((*kp_pfw_op)->addr_sorting_mode_en)); + kp_himax_in_parse_assign_cmd(hx83102ab_fw_addr_selftest_addr_en, (*kp_pfw_op)->addr_selftest_addr_en, sizeof((*kp_pfw_op)->addr_selftest_addr_en)); +#ifdef HX_ZERO_FLASH + kp_himax_in_parse_assign_cmd(hx83102ab_data_adc_cfg_1, (*kp_pzf_op)->data_adc_cfg_1, sizeof((*kp_pzf_op)->data_adc_cfg_1)); +#endif + + if (ic_name == 0x2a) { + kp_himax_in_parse_assign_cmd(hx83102a_data_df_rx, (*kp_pdriver_op)->data_df_rx, sizeof((*kp_pdriver_op)->data_df_rx)); + kp_himax_in_parse_assign_cmd(hx83102a_data_df_tx, (*kp_pdriver_op)->data_df_tx, sizeof((*kp_pdriver_op)->data_df_tx)); + kp_himax_in_parse_assign_cmd(hx83102a_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102a_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + } else { + kp_himax_in_parse_assign_cmd(hx83102b_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102b_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + } +} + +static void himax_hx83102ab_func_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_g_core_fp->fp_chip_init = hx83102_chip_init; + kp_g_core_fp->fp_sense_off = hx83102ab_sense_off; + kp_g_core_fp->fp_set_SMWP_enable = hx83102ab_set_SMWP_enable; + kp_g_core_fp->fp_set_HSEN_enable = hx83102ab_set_HSEN_enable; + kp_g_core_fp->fp_usb_detect_set = hx83102ab_usb_detect_set; + kp_g_core_fp->fp_read_DD_status = hx83102ab_read_DD_status; + kp_g_core_fp->fp_read_FW_status = hx83102ab_read_FW_status; + kp_g_core_fp->fp_power_on_init = hx83102ab_power_on_init; + kp_g_core_fp->fp_fts_ctpm_fw_upgrade_with_sys_fs_64k = hx83102ab_fts_ctpm_fw_upgrade_with_sys_fs_64k; +#ifdef HX_ESD_RECOVERY + kp_g_core_fp->fp_esd_ic_reset = hx83102ab_esd_ic_reset; +#endif +#if defined(HX_ZERO_FLASH) + kp_g_core_fp->fp_firmware_update_0f = hx83102ab_firmware_update_0f; +#if defined(HX_0F_DEBUG) + kp_g_core_fp->fp_firmware_read_0f = hx83102ab_firmware_read_0f; +#endif +#endif + +} + +static void himax_hx83102d_reg_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_himax_in_parse_assign_cmd(hx83102d_fw_addr_raw_out_sel, (*kp_pfw_op)->addr_raw_out_sel, sizeof((*kp_pfw_op)->addr_raw_out_sel)); +#if defined(HX_ZERO_FLASH) + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_sram_start_addr, (*kp_pzf_op)->data_sram_start_addr, sizeof((*kp_pzf_op)->data_sram_start_addr)); + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_adc_cfg_1, (*kp_pzf_op)->data_adc_cfg_1, sizeof((*kp_pzf_op)->data_adc_cfg_1)); + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_adc_cfg_2, (*kp_pzf_op)->data_adc_cfg_2, sizeof((*kp_pzf_op)->data_adc_cfg_2)); + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_adc_cfg_3, (*kp_pzf_op)->data_adc_cfg_3, sizeof((*kp_pzf_op)->data_adc_cfg_3)); +#endif + kp_himax_in_parse_assign_cmd(hx83102d_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102d_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + kp_himax_in_parse_assign_cmd(hx83102d_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + kp_himax_in_parse_assign_cmd(hx83102d_adr_osc_en, (*kp_pic_op)->adr_osc_en, sizeof((*kp_pic_op)->adr_osc_en)); + kp_himax_in_parse_assign_cmd(hx83102d_adr_osc_pw, (*kp_pic_op)->adr_osc_pw, sizeof((*kp_pic_op)->adr_osc_pw)); +} + +static void himax_hx83102d_func_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_g_core_fp->fp_chip_init = hx83102_chip_init; + kp_g_core_fp->fp_sense_off = hx83102d_sense_off; + kp_g_core_fp->fp_ic_id_read = hx83102d_ic_id_read; + kp_g_core_fp->fp_dd_clk_set = hx83102d_dd_clk_set; + kp_g_core_fp->fp_dd_reg_en = hx83102d_dd_reg_en; +#if defined(HX_ZERO_FLASH) + /*kp_g_core_fp->fp_firmware_update_0f = hx83102d_firmware_update_0f;*/ + kp_g_core_fp->fp_resume_ic_action = himax_hx83102d_resume_ic_action; + kp_g_core_fp->fp_suspend_ic_action = himax_hx83102d_suspend_ic_action; + kp_g_core_fp->fp_sense_on = himax_hx83102d_sense_on; + kp_g_core_fp->fp_0f_reload_to_active = himax_hx83102d_reload_to_active; +#if defined(HX_0F_DEBUG) + kp_g_core_fp->fp_firmware_read_0f = hx83102d_firmware_read_0f; +#endif +#if defined(HX_CODE_OVERLAY) + kp_g_core_fp->fp_0f_overlay = hx83102d_0f_overlay; +#endif +#ifdef HX_ESD_RECOVERY + kp_g_core_fp->fp_0f_esd_check = hx83102d_0f_esd_check; +#endif +#endif +} + +static void himax_hx83102e_reg_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_himax_in_parse_assign_cmd(hx83102e_fw_addr_raw_out_sel, (*kp_pfw_op)->addr_raw_out_sel, sizeof((*kp_pfw_op)->addr_raw_out_sel)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_rx, (*kp_pdriver_op)->data_df_rx, sizeof((*kp_pdriver_op)->data_df_rx)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_tx, (*kp_pdriver_op)->data_df_tx, sizeof((*kp_pdriver_op)->data_df_tx)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); +} + +static void himax_hx83102e_func_re_init(void) +{ + I("%s:Entering!\n", __func__); + + kp_g_core_fp->fp_chip_init = hx83102e_chip_init; + kp_g_core_fp->fp_sense_off = hx83102d_sense_off; + kp_g_core_fp->fp_read_event_stack = hx83102e_read_event_stack; +} + + +static bool hx83102_chip_detect(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + bool ret_data = false; + int ret = 0; + int i = 0; + + if (himax_ic_setup_external_symbols()) + return false; + +#ifdef HX_RST_PIN_FUNC + hx83102_pin_reset(); +#endif + if (kp_himax_bus_read(0x13, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + if (hx83102_sense_off(false) == false) { + ret_data = false; + E("%s:hx83102_sense_off Fail:\n", __func__); + return ret_data; + } + + for (i = 0; i < 5; i++) { + tmp_addr[3] = 0x90; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0xD0; + if (hx83102_register_read(tmp_addr, DATA_LEN_4, tmp_data) != 0) { + ret_data = false; + E("%s:hx83102_register_read Fail:\n", __func__); + return ret_data; + } + I("%s:Read driver IC ID = %X,%X,%X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1]); /*83,10,2X*/ + + if ((tmp_data[3] == 0x83) && (tmp_data[2] == 0x10) && ((tmp_data[1] == 0x2a) || (tmp_data[1] == 0x2b) || (tmp_data[1] == 0x2d) || (tmp_data[1] == 0x2e))) { + if (tmp_data[1] == 0x2a) { + strlcpy((*kp_private_ts)->chip_name, HX_83102A_SERIES_PWON, 30); + I("%s:detect IC HX83102A successfully\n", __func__); + } else if (tmp_data[1] == 0x2b) { + strlcpy((*kp_private_ts)->chip_name, HX_83102B_SERIES_PWON, 30); + I("%s:detect IC HX83102B successfully\n", __func__); + } else if (tmp_data[1] == 0x2d) { + strlcpy((*kp_private_ts)->chip_name, HX_83102D_SERIES_PWON, 30); + I("%s:detect IC HX83102D successfully\n", __func__); + } else { + strlcpy((*kp_private_ts)->chip_name, HX_83102E_SERIES_PWON, 30); + I("%s:detect IC HX83102E successfully\n", __func__); + } + + ret = kp_himax_mcu_in_cmd_struct_init(); + if (ret < 0) { + ret_data = false; + E("%s:cmd_struct_init Fail:\n", __func__); + return ret_data; + } + + kp_himax_mcu_in_cmd_init(); + if ((tmp_data[1] == 0x2a) || (tmp_data[1] == 0x2b)) { + himax_hx83102ab_reg_re_init(tmp_data[1]); + himax_hx83102ab_func_re_init(); + } else if (tmp_data[1] == 0x2d) { + himax_hx83102d_reg_re_init(); + himax_hx83102d_func_re_init(); + } else {/* 0x2e */ + himax_hx83102e_reg_re_init(); + himax_hx83102e_func_re_init(); + } + ret_data = true; + break; + } + + ret_data = false; + E("%s:Read driver ID register Fail:\n", __func__); + E("Could NOT find Himax Chipset\n"); + E("Please check 1.VCCD,VCCA,VSP,VSN\n"); + E("2. LCM_RST,TP_RST\n"); + E("3. Power On Sequence\n"); + + } + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) + hx83102_dynamic_fw_name(tmp_data[1]); +#endif +#endif + + return ret_data; +} + +DECLARE(HX_MOD_KSYM_HX83102); + +static int himax_hx83102_probe(void) +{ + I("%s:Enter\n", __func__); + himax_add_chip_dt(hx83102_chip_detect); + + return 0; +} + +static int himax_hx83102_remove(void) +{ + free_chip_dt_table(); + return 0; +} + +static int __init himax_hx83102_init(void) +{ + int ret = 0; + + I("%s\n", __func__); + ret = himax_hx83102_probe(); + return 0; +} + +static void __exit himax_hx83102_exit(void) +{ + himax_hx83102_remove(); +} + +module_init(himax_hx83102_init); +module_exit(himax_hx83102_exit); + +MODULE_DESCRIPTION("HIMAX HX83102 touch driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102d/himax_ic_HX83102d.h b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102d/himax_ic_HX83102d.h new file mode 100644 index 000000000000..ceab657ecdd0 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83102d/himax_ic_HX83102d.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for HX83102 chipset + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_platform.h" +#include "himax_common.h" +#include "himax_ic_core.h" +#include + +#define hx83102ab_fw_addr_sorting_mode_en 0x100007FC +#define hx83102ab_fw_addr_selftest_addr_en 0x100007F8 +#define hx83102ab_data_adc_cfg_1 0x10007B00 +#define hx83102a_data_df_rx 48 +#define hx83102a_data_df_tx 24 +#define hx83102a_data_df_x_res 2160 +#define hx83102a_data_df_y_res 3840 +#define hx83102b_data_df_x_res 720 +#define hx83102b_data_df_y_res 1280 + +#define hx83102d_fw_addr_raw_out_sel 0x800204f4 +#define hx83102d_zf_data_adc_cfg_1 0x10007B00 +#define hx83102d_zf_data_adc_cfg_2 0x10006A00 +#define hx83102d_zf_data_adc_cfg_3 0x10007500 +#define hx83102d_zf_data_bor_prevent_info 0x10007268 +#define hx83102d_zf_data_notch_info 0x10007300 +#define hx83102d_zf_func_info_en 0x10007FD0 +#define hx83102d_zf_po_sub_func 0x10005A00 +#define hx83102d_zf_data_sram_start_addr 0x20000000 +#define hx83102d_data_df_x_res 720 +#define hx83102d_data_df_y_res 1280 +#define hx83102d_adr_osc_en 0x9000009C +#define hx83102d_adr_osc_pw 0x90000280 + +#define hx83102e_fw_addr_raw_out_sel 0x100072EC +#define hx83102e_data_df_rx 48 +#define hx83102e_data_df_tx 30 +#define hx83102e_data_df_x_res 1200 +#define hx83102e_data_df_y_res 1920 + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#ifdef HX_EN_DYNAMIC_NAME +#define hx83102_ic_b9_en 0x300B9000 +#define hx83102_ic_fw 0x300E8006 + +#define hx83102b_ic_osc_en 0x900880A0 +#define hx83102d_ic_osc_en 0x9000009C +#define hx83102d_ic_osc_pw 0x90000280 +#endif +#endif + +#ifdef HX_ESD_RECOVERY + extern u8 HX_ESD_RESET_ACTIVATE; +#endif + diff --git a/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83112/Android.mk b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83112/Android.mk new file mode 100644 index 000000000000..21012b7ef06c --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83112/Android.mk @@ -0,0 +1,21 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +HIMAX_MMI_IC_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +HIMAX_MMI_IC_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_V2_MMI_IC_NAME=hx83112 + +ifneq ($(BOARD_USES_TOUCH_PALM),) + KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_HIMAX_V2_MMI_ENABLE_PALM=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := himax_v2_mmi_hx83112.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(HIMAX_MMI_IC_MODULE_PATH) +LOCAL_REQUIRED_MODULES := himax_v2_mmi.ko +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83112/Kbuild b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83112/Kbuild new file mode 100644 index 000000000000..52d075da4819 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83112/Kbuild @@ -0,0 +1,22 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_v2_mmi \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_v2_mmi/support_ic \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83112 +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_DEBUG \ + -DCONFIG_TOUCHSCREEN_HIMAX_INSPECT + +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_INCELL +EXTRA_CFLAGS += -DCONFIG_HMX_DB + +ifneq ($(filter hx83112,$(CONFIG_INPUT_HIMAX_V2_MMI_IC_NAME)),) + EXTRA_CFLAGS += -D__HIMAX_HX83112_MOD__ +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_HIMAX_V2_MMI_ENABLE_PALM)),) + EXTRA_CFLAGS += -DHIMAX_PALM_SENSOR_EN +endif + +obj-m := himax_v2_mmi_hx83112.o +himax_v2_mmi_hx83112-objs += himax_ic_HX83112.o diff --git a/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83112/Makefile b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83112/Makefile new file mode 100755 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83112/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83112/himax_ic_HX83112.c b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83112/himax_ic_HX83112.c new file mode 100644 index 000000000000..e60056006329 --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83112/himax_ic_HX83112.c @@ -0,0 +1,427 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for HX83112 chipset + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_ic_HX83112.h" +#include "himax_modular.h" + +static void hx83112_chip_init(void) +{ + (*kp_private_ts)->chip_cell_type = CHIP_IS_IN_CELL; + I("%s:IC cell type = %d\n", __func__, (*kp_private_ts)->chip_cell_type); + (*kp_IC_CHECKSUM) = HX_TP_BIN_CHECKSUM_CRC; + /*Himax: Set FW and CFG Flash Address*/ + (*kp_FW_VER_MAJ_FLASH_ADDR) = 49157; /*0x00C005*/ + (*kp_FW_VER_MAJ_FLASH_LENG) = 1; + (*kp_FW_VER_MIN_FLASH_ADDR) = 49158; /*0x00C006*/ + (*kp_FW_VER_MIN_FLASH_LENG) = 1; + (*kp_CFG_VER_MAJ_FLASH_ADDR) = 49408; /*0x00C100*/ + (*kp_CFG_VER_MAJ_FLASH_LENG) = 1; + (*kp_CFG_VER_MIN_FLASH_ADDR) = 49409; /*0x00C101*/ + (*kp_CFG_VER_MIN_FLASH_LENG) = 1; + (*kp_CID_VER_MAJ_FLASH_ADDR) = 49154; /*0x00C002*/ + (*kp_CID_VER_MAJ_FLASH_LENG) = 1; + (*kp_CID_VER_MIN_FLASH_ADDR) = 49155; /*0x00C003*/ + (*kp_CID_VER_MIN_FLASH_LENG) = 1; +} + +static bool hx83112_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_data[DATA_LEN_4]; + + do { + if (cnt == 0 || (tmp_data[0] != 0xA5 && tmp_data[0] != 0x00 && tmp_data[0] != 0x87)) + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_ctrl_fw_isr, DATA_LEN_4, (*kp_pfw_op)->data_fw_stop, 0); + + /*msleep(20);*/ + usleep_range(10000, 10001); + /* check fw status */ + kp_g_core_fp->fp_register_read((*kp_pic_op)->addr_cs_central_state, ADDR_LEN_4, tmp_data, 0); + + if (tmp_data[0] != 0x05) { + I("%s: Do not need wait FW, Status = 0x%02X!\n", __func__, tmp_data[0]); + break; + } + + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_ctrl_fw_isr, 4, tmp_data, false); + I("%s: cnt = %d, data[0] = 0x%02X!\n", __func__, cnt, tmp_data[0]); + } while (tmp_data[0] != 0x87 && (++cnt < 10) && check_en == true); + + cnt = 0; + + do { + /* =========================================== + * I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + * =========================================== + */ + tmp_data[0] = (*kp_pic_op)->data_i2c_psw_lb[0]; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* =========================================== + * I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + * =========================================== + */ + tmp_data[0] = (*kp_pic_op)->data_i2c_psw_ub[0]; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* =========================================== + * I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x00 + * =========================================== + */ + tmp_data[0] = 0x00; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* =========================================== + * I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + * =========================================== + */ + tmp_data[0] = (*kp_pic_op)->data_i2c_psw_lb[0]; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* =========================================== + * I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + * =========================================== + */ + tmp_data[0] = (*kp_pic_op)->data_i2c_psw_ub[0]; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + * Check enter_save_mode + * ====================== + */ + kp_g_core_fp->fp_register_read((*kp_pic_op)->addr_cs_central_state, ADDR_LEN_4, tmp_data, 0); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /* ===================================== + * Reset TCON + * ===================================== + */ + kp_g_core_fp->fp_register_write((*kp_pic_op)->addr_tcon_on_rst, DATA_LEN_4, (*kp_pic_op)->data_rst, 0); + usleep_range(1000, 1001); + tmp_data[3] = (*kp_pic_op)->data_rst[3]; + tmp_data[2] = (*kp_pic_op)->data_rst[2]; + tmp_data[1] = (*kp_pic_op)->data_rst[1]; + tmp_data[0] = (*kp_pic_op)->data_rst[0] | 0x01; + kp_g_core_fp->fp_register_write((*kp_pic_op)->addr_tcon_on_rst, DATA_LEN_4, tmp_data, 0); + /* ===================================== + * Reset ADC + * ===================================== + */ + kp_g_core_fp->fp_register_write((*kp_pic_op)->addr_adc_on_rst, DATA_LEN_4, (*kp_pic_op)->data_rst, 0); + usleep_range(1000, 1001); + tmp_data[3] = (*kp_pic_op)->data_rst[3]; + tmp_data[2] = (*kp_pic_op)->data_rst[2]; + tmp_data[1] = (*kp_pic_op)->data_rst[1]; + tmp_data[0] = (*kp_pic_op)->data_rst[0] | 0x01; + kp_g_core_fp->fp_register_write((*kp_pic_op)->addr_adc_on_rst, DATA_LEN_4, tmp_data, 0); + goto SUCCEED; + } else { + /*msleep(10);*/ +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + } + } while (cnt++ < 5); + + return false; +SUCCEED: + return true; +} + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) +static void hx83112a_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83112_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x01; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + kp_himax_in_parse_assign_cmd(hx83112_ic_osc_pw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xA5; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x11; + tmp_data[3] = 0x2A; + kp_himax_in_parse_assign_cmd(hx83112_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x55; + tmp_data[2] = 0xAA; + tmp_data[3] = 0x00; + kp_himax_in_parse_assign_cmd(hx83112_ic_eb_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83112_cb_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("CB=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_old_ic_ver = tmp_data[0]; + + kp_himax_in_parse_assign_cmd(hx83112_e8_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; + + /* Disable read DD */ + kp_himax_in_parse_assign_cmd(hx83112_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x00; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); +} + +static void hx83112b_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83112_ic_osc_pw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xA5; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x11; + tmp_data[3] = 0x2B; + kp_himax_in_parse_assign_cmd(hx83112_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83112_cb_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("CB=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_old_ic_ver = tmp_data[0]; + + kp_himax_in_parse_assign_cmd(hx83112_e8_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; +} + +static void hx83112e_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83112_ic_osc_pw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xA5; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x11; + tmp_data[3] = 0x2E; + kp_himax_in_parse_assign_cmd(hx83112_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83112_cb_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("CB=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_old_ic_ver = tmp_data[0]; + + kp_himax_in_parse_assign_cmd(hx83112_e8_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; +} + +static void hx83112_dynamic_fw_name(uint8_t ic_name) +{ + char firmware_name[64]; + + if (ic_name == 0x2a) + hx83112a_read_ic_ver(); + else if (ic_name == 0x2b) + hx83112b_read_ic_ver(); + else + hx83112e_read_ic_ver(); + + if ((*kp_i_CTPM_firmware_name) != NULL) { + kfree((*kp_i_CTPM_firmware_name)); + (*kp_i_CTPM_firmware_name) = NULL; + } + memset(firmware_name, 0x00, sizeof(firmware_name)); + + if (((*kp_ic_data)->vendor_ic_ver == 0x13) || ((*kp_ic_data)->vendor_ic_ver == 0x14) || ((*kp_ic_data)->vendor_ic_ver == 0x15) + || ((*kp_ic_data)->vendor_ic_ver == 0x16) || ((*kp_ic_data)->vendor_ic_ver == 0x23)) { + (*kp_ic_data)->vendor_semifac = 2; + } else if (((*kp_ic_data)->vendor_old_ic_ver == 0x44) || ((*kp_ic_data)->vendor_old_ic_ver == 0x77) + || ((*kp_ic_data)->vendor_ic_ver == 0x03) || ((*kp_ic_data)->vendor_ic_ver == 0x04)) { + (*kp_ic_data)->vendor_semifac = 1; + } else { + (*kp_ic_data)->vendor_semifac = 0; + } + memcpy(firmware_name, "Himax_firmware.bin", sizeof(char)*strlen("Himax_firmware.bin")); + + (*kp_i_CTPM_firmware_name) = kzalloc((sizeof(char)*(strlen(firmware_name)+1)), GFP_KERNEL); + if ((*kp_i_CTPM_firmware_name) != NULL) + memcpy((*kp_i_CTPM_firmware_name), firmware_name, (sizeof(char)*(strlen(firmware_name)+1))); + + I("(*kp_i_CTPM_firmware_name) : %s\n", (*kp_i_CTPM_firmware_name)); +} +#endif +#endif + +static void hx83112_func_re_init(void) +{ + kp_g_core_fp->fp_sense_off = hx83112_sense_off; + kp_g_core_fp->fp_chip_init = hx83112_chip_init; +} + +static void hx83112_reg_re_init(void) +{ +} + +static bool hx83112_chip_detect(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + bool ret_data = false; + int ret = 0; + int i = 0; + + if (himax_ic_setup_external_symbols()) + return false; + + ret = kp_himax_mcu_in_cmd_struct_init(); + if (ret < 0) { + ret_data = false; + E("%s:cmd_struct_init Fail:\n", __func__); + return ret_data; + } + + kp_himax_mcu_in_cmd_init(); + + hx83112_reg_re_init(); + hx83112_func_re_init(); + if (kp_himax_bus_read((*kp_pic_op)->addr_conti[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + if (kp_g_core_fp->fp_sense_off(false) == false) { + ret_data = false; + E("%s:fp_sense_off Fail:\n", __func__); + return ret_data; + } + for (i = 0; i < 5; i++) { + if (kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_icid_addr, DATA_LEN_4, tmp_data, false) != 0) { + ret_data = false; + E("%s:fp_register_read Fail:\n", __func__); + return ret_data; + } + I("%s:Read driver IC ID = %X, %X, %X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1]); + + if ((tmp_data[3] == 0x83) && (tmp_data[2] == 0x11) && ((tmp_data[1] == 0x2a) || (tmp_data[1] == 0x2b) || (tmp_data[1] == 0x2e))) { + if (tmp_data[1] == 0x2a) + strlcpy((*kp_private_ts)->chip_name, HX_83112A_SERIES_PWON, 30); + else if (tmp_data[1] == 0x2b) + strlcpy((*kp_private_ts)->chip_name, HX_83112B_SERIES_PWON, 30); + + I("%s:IC name = %s\n", __func__, (*kp_private_ts)->chip_name); + + I("Himax IC package %x%x%x in\n", tmp_data[3], tmp_data[2], tmp_data[1]); + ret_data = true; + goto FINAL; + } else { + ret_data = false; + E("%s:Read driver ID register Fail:\n", __func__); + E("Could NOT find Himax Chipset\n"); + E("Please check 1.VCCD,VCCA,VSP,VSN\n"); + E("2. LCM_RST,TP_RST\n"); + E("3. Power On Sequence\n"); + } + } +FINAL: +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) + hx83112_dynamic_fw_name(tmp_data[1]); +#endif +#endif + + return ret_data; +} + +DECLARE(HX_MOD_KSYM_HX83112); + +static int himax_hx83112_probe(void) +{ + I("%s:Enter\n", __func__); + himax_add_chip_dt(hx83112_chip_detect); + return 0; +} + +static int himax_hx83112_remove(void) +{ + free_chip_dt_table(); + return 0; +} + +static int __init himax_hx83112_init(void) +{ + int ret = 0; + + I("%s\n", __func__); + ret = himax_hx83112_probe(); + return 0; +} + +static void __exit himax_hx83112_exit(void) +{ + himax_hx83112_remove(); +} + +module_init(himax_hx83112_init); +module_exit(himax_hx83112_exit); + +MODULE_DESCRIPTION("HIMAX HX83112 touch driver"); +MODULE_LICENSE("GPL"); + + diff --git a/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83112/himax_ic_HX83112.h b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83112/himax_ic_HX83112.h new file mode 100644 index 000000000000..b8c1b83fa81f --- /dev/null +++ b/drivers/input/touchscreen/himax_v2_mmi/support_ic/himax_v2_mmi_hx83112/himax_ic_HX83112.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for HX83112 chipset + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_platform.h" +#include "himax_common.h" +#include "himax_ic_core.h" +#include + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#ifdef HX_EN_DYNAMIC_NAME +#define hx83112_ic_b9_en 0x300B9000 +#define hx83112_ic_eb_en 0x300EB000 +#define hx83112_ic_osc_en 0x900880A8 +#define hx83112_ic_osc_pw 0x900880E0 +#define hx83112_e8_ic_fw 0x300E8006 +#define hx83112_cb_ic_fw 0x300CB008 +#endif +#endif diff --git a/drivers/input/touchscreen/himax_v3_mmi/Android.mk b/drivers/input/touchscreen/himax_v3_mmi/Android.mk new file mode 100644 index 000000000000..e058fe1694a1 --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/Android.mk @@ -0,0 +1,35 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +HIMAX_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +HIMAX_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +ifeq ($(HIMAX_V2_MMI_0FLASH),true) + KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_HIMAX_V2_MMI_0FLASH=y +endif +ifneq ($(BOARD_USES_TOUCH_PALM),) + KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_HIMAX_V2_MMI_ENABLE_PALM=y +endif + +ifneq ($(BOARD_USES_DOUBLE_TAP),) + KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_V2_MMI_ENABLE_DOUBLE_TAP=y +endif + +ifneq ($(BOARD_USES_HIMAX_MULTI_BIN),) + KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_V2_MMI_ENABLE_MULTI_BIN=y +endif + +ifeq ($(HIMAX_V2_MMI_HX83102D),true) + KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_V2_MMI_IC_NAME_D=hx83102d +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := himax_v3_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(HIMAX_MMI_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/drivers/input/touchscreen/himax_v3_mmi/Kbuild b/drivers/input/touchscreen/himax_v3_mmi/Kbuild new file mode 100644 index 000000000000..54bb15ef0f2e --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/Kbuild @@ -0,0 +1,47 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_v3_mmi +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_DEBUG \ + -DCONFIG_TOUCHSCREEN_HIMAX_INSPECT + +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_INCELL +EXTRA_CFLAGS += -DCONFIG_HMX_DB + +ifneq ($(filter hx83112,$(CONFIG_INPUT_HIMAX_V2_MMI_IC_NAME)),) + EXTRA_CFLAGS += -D__HIMAX_HX83112_MOD__ +endif + +ifneq ($(filter hx83102,$(CONFIG_INPUT_HIMAX_V2_MMI_IC_NAME)),) + EXTRA_CFLAGS += -D__HIMAX_HX83102_MOD__ +endif + +ifneq ($(filter hx83102d,$(CONFIG_INPUT_HIMAX_V2_MMI_IC_NAME_D)),) + EXTRA_CFLAGS += -D__HIMAX_HX83102D_MOD__ + EXTRA_CFLAGS += -DHX_CODE_OVERLAY +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_HIMAX_V2_MMI_ENABLE_PALM)),) + EXTRA_CFLAGS += -DHIMAX_PALM_SENSOR_EN +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_HIMAX_V2_MMI_ENABLE_DOUBLE_TAP)),) + EXTRA_CFLAGS += -DHIMAX_V2_SENSOR_EN +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_HIMAX_V2_MMI_ENABLE_MULTI_BIN)),) + EXTRA_CFLAGS += -DHIMAX_V2_MULTI_BIN +endif + +obj-m := himax_v3_mmi.o + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_HIMAX_V2_MMI_0FLASH)),) + himax_v3_mmi-objs += himax_platform_QCT_SPI.o + EXTRA_CFLAGS += -D__HIMAX_PLATFORM_SPI__ \ + -DHX_ZERO_FLASH +endif + +himax_v3_mmi-objs += himax_common.o +himax_v3_mmi-objs += himax_debug.o +himax_v3_mmi-objs += himax_ic_incell_core.o +himax_v3_mmi-objs += himax_inspection.o diff --git a/drivers/input/touchscreen/himax_v3_mmi/Makefile b/drivers/input/touchscreen/himax_v3_mmi/Makefile new file mode 100755 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/himax_v3_mmi/himax_common.c b/drivers/input/touchscreen/himax_v3_mmi/himax_common.c new file mode 100755 index 000000000000..d71788611d0d --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/himax_common.c @@ -0,0 +1,4143 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for common functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +/*#include "himax_common.h"*/ +/*#include "himax_ic_core.h"*/ +#include "himax_inspection.h" +#include "himax_modular_table.h" + +#ifdef HX_SMART_WAKEUP +#define GEST_SUP_NUM 26 +/* Setting cust key define (DF = double finger) */ +/* {Double Tap, Up, Down, Left, Rright, C, Z, M, + * O, S, V, W, e, m, @, (reserve), + * Finger gesture, ^, >, <, f(R), f(L), Up(DF), Down(DF), + * Left(DF), Right(DF)} + */ + uint8_t gest_event[GEST_SUP_NUM] = { + 0x80, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x81, 0x1D, 0x2D, 0x3D, 0x1F, 0x2F, 0x51, 0x52, + 0x53, 0x54}; + +/*gest_event mapping to gest_key_def*/ + uint16_t gest_key_def[GEST_SUP_NUM] = { + KEY_POWER, 251, 252, 253, 254, 255, 256, 257, + 258, 259, 260, 261, 262, 263, 264, 265, + 266, 267, 268, 269, 270, 271, 272, 273, + 274, 275}; + +uint8_t *wake_event_buffer; +#endif + +#ifdef HIMAX_PALM_SENSOR_EN +static struct sensors_classdev __maybe_unused palm_sensors_touch_cdev = { + .name = "palm-gesture", + .vendor = "Himax", + .version = 1, + .type = SENSOR_TYPE_MOTO_TOUCH_PALM, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +static void himax_palm_detect_func(struct himax_ts_data *ts, bool detected); +#endif + +#ifdef HIMAX_V2_SENSOR_EN +static struct sensors_classdev __maybe_unused sensors_touch_cdev = { + .name = "dt-gesture", + .vendor = "Himax", + .version = 1, + .type = SENSOR_TYPE_MOTO_DOUBLE_TAP, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + /* WAKE_UP & SPECIAL_REPORT */ + .flags = 1 | 6, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#define REPORT_MAX_COUNT 10000 +#endif + +#define SUPPORT_FINGER_DATA_CHECKSUM 0x0F +#define TS_WAKE_LOCK_TIMEOUT (5000) +#define FRAME_COUNT 5 + +#ifdef HX_TP_PROC_GUEST_INFO +struct hx_guest_info *g_guest_info_data; +EXPORT_SYMBOL(g_guest_info_data); + +char *g_guest_info_item[] = { + "projectID", + "CGColor", + "BarCode", + "Reserve1", + "Reserve2", + "Reserve3", + "Reserve4", + "Reserve5", + "VCOM", + "Vcom-3Gar", + NULL +}; +#endif + +uint32_t g_hx_chip_inited; + +#if defined(__EMBEDDED_FW__) +struct firmware g_embedded_fw = { + .data = _binary___Himax_firmware_bin_start, +}; +#endif + +#define HIMAX_FILE_NAME_LENGTH 128 + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) +char *i_CTPM_firmware_name; +#else +char *i_CTPM_firmware_name = "Himax_firmware.bin"; +#endif +bool g_auto_update_flag; +#endif +#if defined(HX_AUTO_UPDATE_FW) +unsigned char *i_CTPM_FW; +int i_CTPM_FW_len; +int g_i_FW_VER; +int g_i_CFG_VER; +int g_i_CID_MAJ; /*GUEST ID*/ +int g_i_CID_MIN; /*VER for GUEST*/ +#endif +#ifdef HX_ZERO_FLASH +int g_f_0f_updat; +#endif + +struct himax_ts_data *private_ts; +#if defined(__HIMAX_HX83102D_MOD__) +extern struct zf_operation *pzf_op; +#endif +EXPORT_SYMBOL(private_ts); + +struct himax_ic_data *ic_data; +EXPORT_SYMBOL(ic_data); + +struct himax_report_data *hx_touch_data; +EXPORT_SYMBOL(hx_touch_data); + +struct himax_core_fp g_core_fp; +EXPORT_SYMBOL(g_core_fp); + +struct himax_debug *debug_data; +EXPORT_SYMBOL(debug_data); + +struct proc_dir_entry *himax_touch_proc_dir; +EXPORT_SYMBOL(himax_touch_proc_dir); + +struct himax_chip_detect *g_core_chip_dt; +EXPORT_SYMBOL(g_core_chip_dt); + +int g_mmi_refcnt; +EXPORT_SYMBOL(g_mmi_refcnt); + +#define HIMAX_PROC_TOUCH_FOLDER "android_touch" +/*ts_work about start*/ +struct himax_target_report_data *g_target_report_data; +EXPORT_SYMBOL(g_target_report_data); + +static void himax_report_all_leave_event(struct himax_ts_data *ts); +/*ts_work about end*/ +static int HX_TOUCH_INFO_POINT_CNT; + +struct filename* (*kp_getname_kernel)(const char *filename); +void (*kp_putname_kernel)(struct filename *name); +struct file* (*kp_file_open_name)(struct filename *name, int flags, umode_t mode); + +unsigned long FW_VER_MAJ_FLASH_ADDR; +EXPORT_SYMBOL(FW_VER_MAJ_FLASH_ADDR); + +unsigned long FW_VER_MIN_FLASH_ADDR; +EXPORT_SYMBOL(FW_VER_MIN_FLASH_ADDR); + +unsigned long CFG_VER_MAJ_FLASH_ADDR; +EXPORT_SYMBOL(CFG_VER_MAJ_FLASH_ADDR); + +unsigned long CFG_VER_MIN_FLASH_ADDR; +EXPORT_SYMBOL(CFG_VER_MIN_FLASH_ADDR); + +unsigned long CID_VER_MAJ_FLASH_ADDR; +EXPORT_SYMBOL(CID_VER_MAJ_FLASH_ADDR); + +unsigned long CID_VER_MIN_FLASH_ADDR; +EXPORT_SYMBOL(CID_VER_MIN_FLASH_ADDR); +/*unsigned long PANEL_VERSION_ADDR;*/ + +unsigned long FW_VER_MAJ_FLASH_LENG; +EXPORT_SYMBOL(FW_VER_MAJ_FLASH_LENG); + +unsigned long FW_VER_MIN_FLASH_LENG; +EXPORT_SYMBOL(FW_VER_MIN_FLASH_LENG); + +unsigned long CFG_VER_MAJ_FLASH_LENG; +EXPORT_SYMBOL(CFG_VER_MAJ_FLASH_LENG); + +unsigned long CFG_VER_MIN_FLASH_LENG; +EXPORT_SYMBOL(CFG_VER_MIN_FLASH_LENG); + +unsigned long CID_VER_MAJ_FLASH_LENG; +EXPORT_SYMBOL(CID_VER_MAJ_FLASH_LENG); + +unsigned long CID_VER_MIN_FLASH_LENG; +EXPORT_SYMBOL(CID_VER_MIN_FLASH_LENG); +/*unsigned long PANEL_VERSION_LENG;*/ + +unsigned long FW_CFG_VER_FLASH_ADDR; + + +unsigned char IC_CHECKSUM; +EXPORT_SYMBOL(IC_CHECKSUM); + +#ifdef HX_ESD_RECOVERY +u8 HX_ESD_RESET_ACTIVATE; +EXPORT_SYMBOL(HX_ESD_RESET_ACTIVATE); + +int hx_EB_event_flag; +EXPORT_SYMBOL(hx_EB_event_flag); + +int hx_EC_event_flag; +EXPORT_SYMBOL(hx_EC_event_flag); + +int hx_ED_event_flag; +EXPORT_SYMBOL(hx_ED_event_flag); + +int g_zero_event_count; + +#endif + +u8 HX_HW_RESET_ACTIVATE; + +static uint8_t AA_press; +static uint8_t EN_NoiseFilter; +static uint8_t Last_EN_NoiseFilter; + +static int p_point_num = 0xFFFF; +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) +static uint8_t vk_press; +static int tpd_key; +static int tpd_key_old; +#endif +static int probe_fail_flag; +#ifdef HX_USB_DETECT_GLOBAL + bool USB_detect_flag; +#endif + +#if defined(CONFIG_DRM) +int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); +#elif defined(CONFIG_FB) +int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void himax_ts_early_suspend(struct early_suspend *h); +static void himax_ts_late_resume(struct early_suspend *h); +#endif + +#ifdef HX_GESTURE_TRACK + static int gest_pt_cnt; + static int gest_pt_x[GEST_PT_MAX_NUM]; + static int gest_pt_y[GEST_PT_MAX_NUM]; + static int gest_start_x, gest_start_y, gest_end_x, gest_end_y; + static int gest_width, gest_height, gest_mid_x, gest_mid_y; + static int hx_gesture_coor[16]; +#endif + +int g_ts_dbg; +EXPORT_SYMBOL(g_ts_dbg); + +/* File node for Selftest, SMWP and HSEN - Start*/ +#define HIMAX_PROC_SELF_TEST_FILE "self_test" +struct proc_dir_entry *himax_proc_self_test_file; + +#define HIMAX_PROC_SELF_TEST_RAW_FILE "self_test_rawdata" +struct proc_dir_entry *himax_proc_self_raw_file = NULL; + +uint8_t HX_PROC_SEND_FLAG; +EXPORT_SYMBOL(HX_PROC_SEND_FLAG); + +#ifdef HX_SMART_WAKEUP + #define HIMAX_PROC_SMWP_FILE "SMWP" + struct proc_dir_entry *himax_proc_SMWP_file = NULL; + #define HIMAX_PROC_GESTURE_FILE "GESTURE" + struct proc_dir_entry *himax_proc_GESTURE_file = NULL; + uint8_t HX_SMWP_EN; +#ifdef HX_P_SENSOR + #define HIMAX_PROC_PSENSOR_FILE "Psensor" + struct proc_dir_entry *himax_proc_psensor_file = NULL; +#endif +#endif + +#ifdef HX_EDGE_LIMIT + #define HIMAX_PROC_EDGE_LIMIT_FILE "edge_en" + struct proc_dir_entry *himax_proc_edge_limit_file = NULL; +#endif + +#if defined(HX_SMART_WAKEUP) || defined(CONFIG_TOUCHSCREEN_HIMAX_INSPECT) +bool FAKE_POWER_KEY_SEND = true; +#endif + +#ifdef HX_HIGH_SENSE + #define HIMAX_PROC_HSEN_FILE "HSEN" + struct proc_dir_entry *himax_proc_HSEN_file = NULL; +#endif + +#if defined(HX_PALM_REPORT) +static int himax_palm_detect(uint8_t *buf) +{ + struct himax_ts_data *ts = private_ts; + int32_t loop_i; + int base = 0; + int x = 0, y = 0, w = 0; + + loop_i = 0; + base = loop_i * 4; + x = buf[base] << 8 | buf[base + 1]; + y = (buf[base + 2] << 8 | buf[base + 3]); + w = buf[(ts->nFinger_support * 4) + loop_i]; +#ifndef HIMAX_PALM_SENSOR_EN + I(" %s HX_PALM_REPORT_loopi=%d,base=%x,X=%x,Y=%x,W=%x\n", __func__, loop_i, base, x, y, w); + if ((!atomic_read(&ts->suspend_mode)) && (x == 0xFA5A) && (y == 0xFA5A) && (w == 0x00)) + return PALM_REPORT; + else + return NOT_REPORT; +#else + if ((x == 0xFA5A) && (y == 0xFA5A)) { + I("%s: HX_PALM_REPORT loop_i=%d base=%x X=%x, Y=%x, W=%x\n", + __func__, loop_i, base, x, y, w); + return PALM_REPORT; + } else if ((x == 0xFC5C) && (y == 0xFC5C)) { + I("%s: HX_PALM_REPORT loop_i=%d base=%x X=%x, Y=%x, W=%x\n", + __func__, loop_i, base, x, y, w); + return PALM_LEAVE_REPORT; + } else + return NOT_REPORT; +#endif +} +#endif + +static ssize_t himax_self_test_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + int i = 0; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) { + return -EFAULT; + } + + if (hx_self_test_file_name == NULL) { + E("file name is NULL\n"); + hx_self_test_file_name = kzalloc(80, GFP_KERNEL); + snprintf(hx_self_test_file_name, 15, "hx_criteria.csv"); + } + + for (i = 0; i < 80; i++) { + if (buf[i] == ',' || buf[i] == '\n') { + memset(hx_self_test_file_name, 0x0, 80); + memcpy(hx_self_test_file_name, buf, i); + I("%s: Get name from Customer\n", __func__); + break; + } + } + if (i == 80) { + memset(hx_self_test_file_name, 0x0, 80); + snprintf(hx_self_test_file_name, 16, "hx_criteria.csv"); + I("%s: Use default name\n", __func__); + } + I("file name = %s\n", hx_self_test_file_name); + + return len; +} + +static ssize_t himax_self_test_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + int val = 0x00; + size_t ret = 0; + char *temp_buf; + I("%s: enter, %d \n", __func__, __LINE__); + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + himax_int_enable(0);/* disable irq */ + private_ts->in_self_test = 1; + val = g_core_fp.fp_chip_self_test(); +#ifdef HX_ESD_RECOVERY + HX_ESD_RESET_ACTIVATE = 1; +#endif + himax_int_enable(1);/* enable irq */ + + if (val == 0x00) { + ret += snprintf(temp_buf + ret, len - ret, "Self_Test Pass\n"); + } else { + ret += snprintf(temp_buf + ret, len - ret, "Self_Test Fail\n"); + } + + private_ts->in_self_test = 0; + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static struct file_operations himax_proc_self_test_ops = { + .owner = THIS_MODULE, + .read = himax_self_test_read, + .write = himax_self_test_write, +}; + +static void *himax_self_raw_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) { + return NULL; + } + return (void *)((unsigned long) *pos + 1); +} + +static void *himax_self_raw_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_self_raw_seq_stop(struct seq_file *s, void *v) +{ +} + +static int himax_self_raw_seq_read(struct seq_file *s, void *v) +{ + size_t ret = 0; + + if (g_rslt_data != NULL) { + seq_printf(s, "%s", g_rslt_data); + } + return ret; +} +static struct seq_operations himax_self_raw_seq_ops = { + .start = himax_self_raw_seq_start, + .next = himax_self_raw_seq_next, + .stop = himax_self_raw_seq_stop, + .show = himax_self_raw_seq_read, +}; +static int himax_self_raw_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_self_raw_seq_ops); +}; + +static struct file_operations himax_proc_self_raw_ops = { + .owner = THIS_MODULE, + .open = himax_self_raw_proc_open, + .read = seq_read, +}; + +#if 0 +static ssize_t himax_self_test(struct seq_file *s, void *v) +{ + int val = 0x00; + size_t ret = 0; + + I("%s: enter, %d\n", __func__, __LINE__); + + if (private_ts->suspended == 1) { + E("%s: please do self test in normal active mode\n", __func__); + return HX_INIT_FAIL; + } + + himax_int_enable(0);/* disable irq */ + + private_ts->in_self_test = 1; + + val = g_core_fp.fp_chip_self_test(); +/* + *#ifdef HX_ESD_RECOVERY + * HX_ESD_RESET_ACTIVATE = 1; + *#endif + * himax_int_enable(1); //enable irq + */ + if (val == HX_INSPECT_OK) + seq_puts(s, "Self_Test Pass:\n"); + else + seq_puts(s, "Self_Test Fail:\n"); + + /*Print_Self_Test_Info(val,temp_buf);*/ + if ((val & HX_INSPECT_EFILE) == HX_INSPECT_EFILE) { + seq_puts(s, " Get criteria File Fail\n"); + goto END_FUNC; + } else if ((val & HX_INSPECT_MEMALLCTFAIL) == HX_INSPECT_MEMALLCTFAIL) { + seq_puts(s, " Allocate memory Fail\n"); + goto END_FUNC; + } else { + seq_puts(s, " Get criteria File OK\n"); + } + if ((val & HX_INSPECT_EGETRAW) == HX_INSPECT_EGETRAW) { + seq_puts(s, " Get raw data Fail\n"); + goto END_FUNC; + } else { + seq_puts(s, " Get raw data OK\n"); + } +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + if (g_inspt_crtra_flag[IDX_RAWMIN] == 1 && g_inspt_crtra_flag[IDX_RAWMAX] == 1) { + if ((val & HX_INSPECT_ERAW) == HX_INSPECT_ERAW) + seq_puts(s, " Check Raw data Fail\n"); + else + seq_puts(s, " Check Raw data OK\n"); + } + if (g_inspt_crtra_flag[IDX_WT_NOISEMAX] == 1 && g_inspt_crtra_flag[IDX_WT_NOISEMIN] == 1) { + if ((val & HX_INSPECT_WT_ENOISE) == HX_INSPECT_WT_ENOISE) + seq_puts(s, " Check Noise(weight) Fail\n"); + else + seq_puts(s, " Check Noise(weight) OK\n"); + } + if (g_inspt_crtra_flag[IDX_ABS_NOISEMAX] == 1 && g_inspt_crtra_flag[IDX_ABS_NOISEMIN] == 1) { + if ((val & HX_INSPECT_ABS_ENOISE) == HX_INSPECT_ABS_ENOISE) + seq_puts(s, " Check Noise(absolute) Fail\n"); + else + seq_puts(s, " Check Noise(absolute) OK\n"); + } + if (g_inspt_crtra_flag[IDX_OPENMIN] == 1 && g_inspt_crtra_flag[IDX_OPENMAX] == 1) { + if ((val & HX_INSPECT_EOPEN) == HX_INSPECT_EOPEN) + seq_puts(s, " Check Sensor open Fail\n"); + else + seq_puts(s, " Check Sensor open OK\n"); + } + if (g_inspt_crtra_flag[IDX_M_OPENMIN] == 1 && g_inspt_crtra_flag[IDX_M_OPENMAX] == 1) { + if ((val & HX_INSPECT_EMOPEN) == HX_INSPECT_EMOPEN) + seq_puts(s, " Check Sensor micro open Fail\n"); + else + seq_puts(s, " Check Sensor micro open OK\n"); + } + if (g_inspt_crtra_flag[IDX_SORTMIN] == 1 && g_inspt_crtra_flag[IDX_SORTMAX] == 1) { + if ((val & HX_INSPECT_ESHORT) == HX_INSPECT_ESHORT) + seq_puts(s, " Check Sensor short Fail\n"); + else + seq_puts(s, " Check Sensor short OK\n"); + } + if ((g_inspt_crtra_flag[IDX_GAP_HOR_RAWMIN] == 1 && g_inspt_crtra_flag[IDX_GAP_HOR_RAWMAX] == 1) || + (g_inspt_crtra_flag[IDX_GAP_VER_RAWMIN] == 1 && g_inspt_crtra_flag[IDX_GAP_VER_RAWMAX] == 1)) { + if ((val & HX_INSPECT_EGAP_RAW) == HX_INSPECT_EGAP_RAW) + seq_puts(s, " Check Raw Data GAP Fail\n"); + else + seq_puts(s, " Check Raw Data GAP OK\n"); + } + if (g_inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MIN] == 1 && g_inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MAX] == 1) { + if ((val & HX_INSPECT_EACT_IDLE_RAW) == HX_INSPECT_EACT_IDLE_RAW) + seq_puts(s, " Check ACT_IDLE RAW Fail\n"); + else + seq_puts(s, " Check ACT_IDLE RAW OK\n"); + } + if (g_inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MIN] == 1 && g_inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MAX] == 1) { + if ((val & HX_INSPECT_EACT_IDLE_NOISE) == HX_INSPECT_EACT_IDLE_NOISE) + seq_puts(s, " Check ACT_IDLE NOISE Fail\n"); + else + seq_puts(s, " Check ACT_IDLE NOISE OK\n"); + } + if (g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MIN] == 1 && g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MAX] == 1) { + if ((val & HX_INSPECT_ELPWUG_RAW) == HX_INSPECT_ELPWUG_RAW) + seq_puts(s, " Check LPWUG RAW Fail\n"); + else + seq_puts(s, " Check LPWUG RAW OK\n"); + } + if (g_inspt_crtra_flag[IDX_LPWUG_NOISE_MIN] == 1 && g_inspt_crtra_flag[IDX_LPWUG_NOISE_MAX] == 1) { + if ((val & HX_INSPECT_ELPWUG_NOISE) == HX_INSPECT_ELPWUG_NOISE) + seq_puts(s, " Check LPWUG NOISE Fail\n"); + else + seq_puts(s, " Check LPWUG NOISE OK\n"); + } + if (g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MIN] == 1 && g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MAX] == 1) { + if ((val & HX_INSPECT_ELPWUG_IDLE_RAW) == HX_INSPECT_ELPWUG_IDLE_RAW) + seq_puts(s, " Check LPWUG IDLE RAW Fail\n"); + else + seq_puts(s, " Check LPWUG IDLE RAW OK\n"); + } + if (g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MIN] == 1 && g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MAX] == 1) { + if ((val & HX_INSPECT_ELPWUG_IDLE_NOISE) == HX_INSPECT_ELPWUG_IDLE_NOISE) + seq_puts(s, " Check LPWUG IDLE NOISE Fail\n"); + else + seq_puts(s, " Check LPWUG IDLE NOISE OK\n"); + } +#endif + +END_FUNC: + private_ts->in_self_test = 0; + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + if (g_inspt_crtra_flag != NULL) { + kfree(g_inspt_crtra_flag); + g_inspt_crtra_flag = NULL; + } +#endif + +#ifdef HX_ESD_RECOVERY + HX_ESD_RESET_ACTIVATE = 1; +#endif + himax_int_enable(1);/* enable irq */ + + return ret; +} + +static void *himax_self_test_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) + return NULL; + + + return (void *)((unsigned long) *pos + 1); +} + +static void *himax_self_test_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_self_test_seq_stop(struct seq_file *s, void *v) +{ +} + +static ssize_t himax_self_test_write(struct file *filp, const char __user *buff, + size_t len, loff_t *data) +{ + char buf[80]; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + if (buf[0] == 'r') { + chip_test_r_flag = true; + I("%s: Start to read chip test data.\n", __func__); + } else { + chip_test_r_flag = false; + I("%s: Back to do self test.\n", __func__); + } + + return len; +} + +static int himax_self_test_seq_read(struct seq_file *s, void *v) +{ + size_t ret = 0; + + if (chip_test_r_flag) { +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + if (g_rslt_data) + seq_printf(s, "%s", g_rslt_data); + else +#endif + seq_puts(s, "No chip test data.\n"); + } else { + himax_self_test(s, v); + } + + return ret; +} + +static const struct seq_operations himax_self_test_seq_ops = { + .start = himax_self_test_seq_start, + .next = himax_self_test_seq_next, + .stop = himax_self_test_seq_stop, + .show = himax_self_test_seq_read, +}; + +static int himax_self_test_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_self_test_seq_ops); +}; + +static const struct file_operations himax_proc_self_test_ops = { + .owner = THIS_MODULE, + .open = himax_self_test_proc_open, + .read = seq_read, + .write = himax_self_test_write, + .release = seq_release, +}; +#endif + +#ifdef HX_HIGH_SENSE +static ssize_t himax_HSEN_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + size_t count = 0; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kcalloc(len, sizeof(char), GFP_KERNEL); + count = snprintf(temp_buf, PAGE_SIZE, "%d\n", ts->HSEN_enable); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return count; +} + +static ssize_t himax_HSEN_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + if (buf[0] == '0') + ts->HSEN_enable = 0; + else if (buf[0] == '1') + ts->HSEN_enable = 1; + else + return -EINVAL; + + g_core_fp.fp_set_HSEN_enable(ts->HSEN_enable, ts->suspended); + I("%s: HSEN_enable = %d.\n", __func__, ts->HSEN_enable); + return len; +} + +static const struct file_operations himax_proc_HSEN_ops = { + .owner = THIS_MODULE, + .read = himax_HSEN_read, + .write = himax_HSEN_write, +}; +#endif + +#ifdef HX_SMART_WAKEUP +static ssize_t himax_SMWP_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + size_t count = 0; + struct himax_ts_data *ts = private_ts; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kcalloc(len, sizeof(char), GFP_KERNEL); + count = snprintf(temp_buf, PAGE_SIZE, "%d\n", ts->SMWP_enable); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return count; +} + +static ssize_t himax_SMWP_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + if (buf[0] == '0') + ts->SMWP_enable = 0; + else if (buf[0] == '1') + ts->SMWP_enable = 1; + else + return -EINVAL; + + g_core_fp.fp_set_SMWP_enable(ts->SMWP_enable, ts->suspended); + HX_SMWP_EN = ts->SMWP_enable; + I("%s: SMART_WAKEUP_enable = %d.\n", __func__, HX_SMWP_EN); + return len; +} + +static const struct file_operations himax_proc_SMWP_ops = { + .owner = THIS_MODULE, + .read = himax_SMWP_read, + .write = himax_SMWP_write, +}; + +static ssize_t himax_GESTURE_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + int i = 0; + size_t ret = 0; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kcalloc(len, sizeof(char), GFP_KERNEL); + + for (i = 0; i < GEST_SUP_NUM; i++) + ret += snprintf(temp_buf + ret, len - ret, "ges_en[%d]=%d\n", i, ts->gesture_cust_en[i]); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + ret = 0; + } + + return ret; +} + +static ssize_t himax_GESTURE_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + int i = 0; + int j = 0; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + I("himax_GESTURE_store= %s, len = %d\n", buf, (int)len); + + for (i = 0; i < len; i++) { + if (buf[i] == '0' && j < GEST_SUP_NUM) { + ts->gesture_cust_en[j] = 0; + I("gesture en[%d]=%d\n", j, ts->gesture_cust_en[j]); + j++; + } else if (buf[i] == '1' && j < GEST_SUP_NUM) { + ts->gesture_cust_en[j] = 1; + I("gesture en[%d]=%d\n", j, ts->gesture_cust_en[j]); + j++; + } else + I("Not 0/1 or >=GEST_SUP_NUM : buf[%d] = %c\n", i, buf[i]); + } + + return len; +} + +static const struct file_operations himax_proc_Gesture_ops = { + .owner = THIS_MODULE, + .read = himax_GESTURE_read, + .write = himax_GESTURE_write, +}; + +#ifdef HX_P_SENSOR +static ssize_t himax_psensor_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + size_t count = 0; + struct himax_ts_data *ts = private_ts; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kcalloc(len, sizeof(char), GFP_KERNEL); + count = snprintf(temp_buf, PAGE_SIZE, "p-sensor flag = %d\n", ts->psensor_flag); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return count; +} + +static ssize_t himax_psensor_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + if (buf[0] == '0' && ts->SMWP_enable == 1) { + ts->psensor_flag = false; + g_core_fp.fp_black_gest_ctrl(false); + } else if (buf[0] == '1' && ts->SMWP_enable == 1) { + ts->psensor_flag = true; + g_core_fp.fp_black_gest_ctrl(true); + } else if (ts->SMWP_enable == 0) { + I("%s: SMWP is disable, not supprot to ctrl p-sensor.\n", __func__); + } else + return -EINVAL; + + I("%s: psensor_flag = %d.\n", __func__, ts->psensor_flag); + return len; +} + +static const struct file_operations himax_proc_psensor_ops = { + .owner = THIS_MODULE, + .read = himax_psensor_read, + .write = himax_psensor_write, +}; +#endif +#endif + +#ifdef HX_EDGE_LIMIT +static ssize_t himax_edge_limit_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + size_t count = 0; + struct himax_ts_data *ts = private_ts; + char *temp_buf; + + if (!HX_PROC_SEND_FLAG) { + temp_buf = kzalloc(len, GFP_KERNEL); + count = snprintf(temp_buf, PAGE_SIZE, "%d\n", ts->edge_limit_enable); + + if (copy_to_user(buf, temp_buf, len)) + I("%s,here:%d\n", __func__, __LINE__); + + kfree(temp_buf); + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return count; +} + +static ssize_t himax_edge_limit_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + struct himax_ts_data *ts = private_ts; + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) { + return -EFAULT; + } + + if (buf[0] == '0') + ts->edge_limit_enable = 0; + else if (buf[0] == '1') + ts->edge_limit_enable = 1;/*Portrait*/ + else if (buf[0] == '2') + ts->edge_limit_enable = 2;/*Landscape-Right*/ + else if (buf[0] == '3') + ts->edge_limit_enable = 3;/*Landscape-Left*/ + else + return -EINVAL; + + g_core_fp.fp_set_edge_limit_enable(ts->edge_limit_enable, ts->suspended); + I("%s: edge_limit_enable = %d.\n", __func__, ts->edge_limit_enable); + return len; +} + +static struct file_operations himax_proc_edge_limit_ops = { + .owner = THIS_MODULE, + .read = himax_edge_limit_read, + .write = himax_edge_limit_write, +}; +#endif + +int himax_common_proc_init(void) +{ + himax_touch_proc_dir = proc_mkdir(HIMAX_PROC_TOUCH_FOLDER, NULL); + + if (himax_touch_proc_dir == NULL) { + E(" %s: himax_touch_proc_dir file create failed!\n", __func__); + return -ENOMEM; + } +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + if (fp_himax_self_test_init != NULL) + fp_himax_self_test_init(); +#endif + + himax_proc_self_test_file = proc_create(HIMAX_PROC_SELF_TEST_FILE, 0664, himax_touch_proc_dir, &himax_proc_self_test_ops); + if (himax_proc_self_test_file == NULL) { + E(" %s: proc self_test file create failed!\n", __func__); + goto fail_1; + } + + himax_proc_self_raw_file = proc_create(HIMAX_PROC_SELF_TEST_RAW_FILE, 0444, himax_touch_proc_dir, &himax_proc_self_raw_ops); + if (himax_proc_self_raw_file == NULL) { + E(" %s: proc self_test raw file create failed!\n", __func__); + goto fail_1_1; + } + +#ifdef HX_HIGH_SENSE + himax_proc_HSEN_file = proc_create(HIMAX_PROC_HSEN_FILE, 0666, + himax_touch_proc_dir, &himax_proc_HSEN_ops); + + if (himax_proc_HSEN_file == NULL) { + E(" %s: proc HSEN file create failed!\n", __func__); + goto fail_2; + } + +#endif +#ifdef HX_SMART_WAKEUP + himax_proc_SMWP_file = proc_create(HIMAX_PROC_SMWP_FILE, 0666, + himax_touch_proc_dir, &himax_proc_SMWP_ops); + + if (himax_proc_SMWP_file == NULL) { + E(" %s: proc SMWP file create failed!\n", __func__); + goto fail_3; + } + + himax_proc_GESTURE_file = proc_create(HIMAX_PROC_GESTURE_FILE, 0666, + himax_touch_proc_dir, &himax_proc_Gesture_ops); + + if (himax_proc_GESTURE_file == NULL) { + E(" %s: proc GESTURE file create failed!\n", __func__); + goto fail_4; + } +#ifdef HX_P_SENSOR + himax_proc_psensor_file = proc_create(HIMAX_PROC_PSENSOR_FILE, 0666, + himax_touch_proc_dir, &himax_proc_psensor_ops); + + if (himax_proc_psensor_file == NULL) { + E(" %s: proc GESTURE file create failed!\n", __func__); + goto fail_5; + } +#endif +#endif +#ifdef HX_EDGE_LIMIT + himax_proc_edge_limit_file = proc_create(HIMAX_PROC_EDGE_LIMIT_FILE, 0666, + himax_touch_proc_dir, &himax_proc_edge_limit_ops); + + if (himax_proc_edge_limit_file == NULL) { + E(" %s: proc edge limit file create failed!\n", __func__); + goto fail_6; + } +#endif + return 0; +#ifdef HX_EDGE_LIMIT + remove_proc_entry(HIMAX_PROC_EDGE_LIMIT_FILE, himax_touch_proc_dir); +fail_6: +#endif +#ifdef HX_SMART_WAKEUP +#ifdef HX_P_SENSOR + remove_proc_entry(HIMAX_PROC_PSENSOR_FILE, himax_touch_proc_dir); +fail_5: +#endif + remove_proc_entry(HIMAX_PROC_GESTURE_FILE, himax_touch_proc_dir); +fail_4: + remove_proc_entry(HIMAX_PROC_SMWP_FILE, himax_touch_proc_dir); +fail_3: +#endif +#ifdef HX_HIGH_SENSE + remove_proc_entry(HIMAX_PROC_HSEN_FILE, himax_touch_proc_dir); +fail_2: +#endif + remove_proc_entry(HIMAX_PROC_SELF_TEST_RAW_FILE, himax_touch_proc_dir); +fail_1_1: + remove_proc_entry(HIMAX_PROC_SELF_TEST_FILE, himax_touch_proc_dir); +fail_1: + return -ENOMEM; +} + +void himax_common_proc_deinit(void) +{ +remove_proc_entry(HIMAX_PROC_SELF_TEST_FILE, himax_touch_proc_dir); +remove_proc_entry(HIMAX_PROC_SELF_TEST_RAW_FILE, himax_touch_proc_dir); + +#ifdef HX_EDGE_LIMIT + remove_proc_entry(HIMAX_PROC_EDGE_LIMIT_FILE, himax_touch_proc_dir); +#endif +#ifdef HX_HIGH_SENSE + remove_proc_entry(HIMAX_PROC_HSEN_FILE, himax_touch_proc_dir); +#endif +#ifdef HX_SMART_WAKEUP +#ifdef HX_P_SENSOR + remove_proc_entry(HIMAX_PROC_PSENSOR_FILE, himax_touch_proc_dir); +#endif + remove_proc_entry(HIMAX_PROC_GESTURE_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_SMWP_FILE, himax_touch_proc_dir); +#endif + remove_proc_entry(HIMAX_PROC_TOUCH_FOLDER, NULL); +} + +/* File node for SMWP and HSEN - End*/ + +int himax_input_register(struct himax_ts_data *ts) +{ + int ret = 0; +#if defined(HX_SMART_WAKEUP) + int i = 0; +#endif + ret = himax_dev_set(ts); + + if (ret < 0) { + I("%s, input device register fail!\n", __func__); + ret = INPUT_REGISTER_FAIL; + goto input_device_fail; + } + + set_bit(EV_SYN, ts->input_dev->evbit); + set_bit(EV_ABS, ts->input_dev->evbit); + set_bit(EV_KEY, ts->input_dev->evbit); +#if defined(HX_PLATFOME_DEFINE_KEY) + himax_platform_key(); +#else + set_bit(KEY_BACK, ts->input_dev->keybit); + set_bit(KEY_HOME, ts->input_dev->keybit); + set_bit(KEY_MENU, ts->input_dev->keybit); + set_bit(KEY_SEARCH, ts->input_dev->keybit); +#endif +#if defined(HX_SMART_WAKEUP) || defined(HX_PALM_REPORT) + set_bit(KEY_POWER, ts->input_dev->keybit); +#endif +#if defined(HX_SMART_WAKEUP) + for (i = 1; i < GEST_SUP_NUM; i++) + set_bit(gest_key_def[i], ts->input_dev->keybit); + +#endif + set_bit(BTN_TOUCH, ts->input_dev->keybit); + set_bit(KEY_APPSELECT, ts->input_dev->keybit); + set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit); +#ifdef HX_PROTOCOL_A + /*ts->input_dev->mtsize = ts->nFinger_support;*/ + input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0, 3, 0, 0); +#else + set_bit(MT_TOOL_FINGER, ts->input_dev->keybit); +#if defined(HX_PROTOCOL_B_3PA) + input_mt_init_slots(ts->input_dev, ts->nFinger_support, INPUT_MT_DIRECT); +#else + input_mt_init_slots(ts->input_dev, ts->nFinger_support); +#endif +#endif + I("input_set_abs_params: mix_x %d, max_x %d, min_y %d, max_y %d\n", + ts->pdata->abs_x_min, ts->pdata->abs_x_max, ts->pdata->abs_y_min, ts->pdata->abs_y_max); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, ts->pdata->abs_x_min, ts->pdata->abs_x_max, ts->pdata->abs_x_fuzz, 0); + input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, ts->pdata->abs_y_min, ts->pdata->abs_y_max, ts->pdata->abs_y_fuzz, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, ts->pdata->abs_pressure_min, ts->pdata->abs_pressure_max, ts->pdata->abs_pressure_fuzz, 0); +#ifndef HX_PROTOCOL_A + input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, ts->pdata->abs_pressure_min, ts->pdata->abs_pressure_max, ts->pdata->abs_pressure_fuzz, 0); + input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, ts->pdata->abs_width_min, ts->pdata->abs_width_max, ts->pdata->abs_pressure_fuzz, 0); +#endif +/* input_set_abs_params(ts->input_dev, ABS_MT_AMPLITUDE, 0, ((ts->pdata->abs_pressure_max << 16) | ts->pdata->abs_width_max), 0, 0); + * input_set_abs_params(ts->input_dev, ABS_MT_POSITION, 0, (BIT(31) | (ts->pdata->abs_x_max << 16) | ts->pdata->abs_y_max), 0, 0); + */ + + if (himax_input_register_device(ts->input_dev) == 0) + ret = NO_ERR; + else + ret = INPUT_REGISTER_FAIL; + +#if defined(HX_PEN_FUNC_EN) + set_bit(EV_SYN, ts->hx_pen_dev->evbit); + set_bit(EV_ABS, ts->hx_pen_dev->evbit); + set_bit(EV_KEY, ts->hx_pen_dev->evbit); + set_bit(BTN_TOUCH, ts->hx_pen_dev->keybit); + set_bit(INPUT_PROP_DIRECT, ts->hx_pen_dev->propbit); + + set_bit(BTN_TOOL_PEN, ts->hx_pen_dev->keybit); + set_bit(BTN_TOOL_RUBBER, ts->hx_pen_dev->keybit); + + input_set_abs_params(ts->hx_pen_dev, ABS_PRESSURE, 0, 4095, 0, 0); + input_set_abs_params(ts->hx_pen_dev, ABS_DISTANCE, 0, 1, 0, 0); + input_set_abs_params(ts->hx_pen_dev, ABS_TILT_X, -60, 60, 0, 0); + input_set_abs_params(ts->hx_pen_dev, ABS_TILT_Y, -60, 60, 0, 0); + /*input_set_capability(ts->hx_pen_dev, EV_SW, SW_PEN_INSERT);*/ + input_set_capability(ts->hx_pen_dev, EV_KEY, BTN_TOUCH); + input_set_capability(ts->hx_pen_dev, EV_KEY, BTN_STYLUS); + + input_set_abs_params(ts->hx_pen_dev, ABS_X, ts->pdata->abs_x_min, ts->pdata->abs_x_max, ts->pdata->abs_x_fuzz, 0); + input_set_abs_params(ts->hx_pen_dev, ABS_Y, ts->pdata->abs_y_min, ts->pdata->abs_y_max, ts->pdata->abs_y_fuzz, 0); + + if (himax_input_register_device(ts->hx_pen_dev) == 0) + ret = NO_ERR; + else + ret = INPUT_REGISTER_FAIL; + +#endif + I("%s, input device registered.\n", __func__); + +input_device_fail: + return ret; +} +EXPORT_SYMBOL(himax_input_register); + +static void calcDataSize(void) +{ + struct himax_ts_data *ts_data = private_ts; + + ts_data->x_channel = ic_data->HX_RX_NUM; + ts_data->y_channel = ic_data->HX_TX_NUM; + ts_data->nFinger_support = ic_data->HX_MAX_PT; + + ts_data->coord_data_size = 4 * ts_data->nFinger_support; + ts_data->area_data_size = ((ts_data->nFinger_support / 4) + (ts_data->nFinger_support % 4 ? 1 : 0)) * 4; + ts_data->coordInfoSize = ts_data->coord_data_size + ts_data->area_data_size + 4; + ts_data->raw_data_frame_size = 128 - ts_data->coord_data_size - ts_data->area_data_size - 4 - 4 - 1; + + if (ts_data->raw_data_frame_size == 0) { + E("%s: could NOT calculate!\n", __func__); + return; + } + + ts_data->raw_data_nframes = ((uint32_t)ts_data->x_channel * ts_data->y_channel + + ts_data->x_channel + ts_data->y_channel) / ts_data->raw_data_frame_size + + (((uint32_t)ts_data->x_channel * ts_data->y_channel + + ts_data->x_channel + ts_data->y_channel) % ts_data->raw_data_frame_size) ? 1 : 0; + I("%s: coord_data_size: %d, area_data_size:%d, raw_data_frame_size:%d, raw_data_nframes:%d\n", __func__, ts_data->coord_data_size, ts_data->area_data_size, ts_data->raw_data_frame_size, ts_data->raw_data_nframes); +} + +static void calculate_point_number(void) +{ + HX_TOUCH_INFO_POINT_CNT = ic_data->HX_MAX_PT * 4; + + if ((ic_data->HX_MAX_PT % 4) == 0) + HX_TOUCH_INFO_POINT_CNT += (ic_data->HX_MAX_PT / 4) * 4; + else + HX_TOUCH_INFO_POINT_CNT += ((ic_data->HX_MAX_PT / 4) + 1) * 4; +} +#if defined(HX_AUTO_UPDATE_FW) +static int himax_auto_update_check(void) +{ + int32_t ret; + int flag_sys_ex_is_same = 0; + + I("%s:Entering!\n", __func__); + if (g_core_fp.fp_fw_ver_bin() == 0) { + flag_sys_ex_is_same = (g_core_fp._diff_overlay_flash() != g_core_fp._diff_overlay_bin()) ? 1 : 0; + I("Now flag_sys_ex_is_same valur=%d\n", flag_sys_ex_is_same); + if (((ic_data->vendor_fw_ver < g_i_FW_VER) || (ic_data->vendor_config_ver < g_i_CFG_VER)) + || (flag_sys_ex_is_same == 1)) { + I("Need to update!\n"); + ret = NO_ERR; + } else { + E("No need to update!\n"); + ret = 1; + } + } else { + E("FW bin fail!\n"); + ret = 1; + } + + return ret; +} + +static int i_get_FW(void) +{ + int ret = 0; + const struct firmware *image = NULL; + + I("file name = %s\n", i_CTPM_firmware_name); + ret = request_firmware(&image, i_CTPM_firmware_name, private_ts->dev); + if (ret < 0) { +#if defined(__EMBEDDED_FW__) + image = &g_embedded_fw; + I("%s: Couldn't find userspace FW, use embedded FW(size:%zu) instead.\n", __func__, g_embedded_fw.size); +#else + E("%s,fail in line%d error code=%d\n", __func__, __LINE__, ret); + return OPEN_FILE_FAIL; +#endif + } + + if (image != NULL) { + i_CTPM_FW_len = image->size; + i_CTPM_FW = kcalloc(i_CTPM_FW_len, sizeof(char), GFP_KERNEL); + memcpy(i_CTPM_FW, image->data, sizeof(char)*i_CTPM_FW_len); + } else { + I("%s: i_CTPM_FW = NULL\n", __func__); + return OPEN_FILE_FAIL; + } + + if (ret >= 0) + release_firmware(image); + ret = NO_ERR; + return ret; +} +static int i_update_FW(void) +{ + int upgrade_times = 0; + uint8_t ret = 0, result = 0; + + himax_int_enable(0); + + +update_retry: + + if (i_CTPM_FW_len == FW_SIZE_32k) + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_32k(i_CTPM_FW, i_CTPM_FW_len, false); + else if (i_CTPM_FW_len == FW_SIZE_60k) + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_60k(i_CTPM_FW, i_CTPM_FW_len, false); + else if (i_CTPM_FW_len == FW_SIZE_64k) + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_64k(i_CTPM_FW, i_CTPM_FW_len, false); + else if (i_CTPM_FW_len == FW_SIZE_124k) + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_124k(i_CTPM_FW, i_CTPM_FW_len, false); + else if (i_CTPM_FW_len == FW_SIZE_128k) + ret = g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_128k(i_CTPM_FW, i_CTPM_FW_len, false); + + if (ret == 0) { + upgrade_times++; + E("%s: TP upgrade error, upgrade_times = %d\n", __func__, upgrade_times); + + if (upgrade_times < 3) + goto update_retry; + else + result = -1; + + + } else { + g_core_fp.fp_read_FW_ver(); + g_core_fp.fp_touch_information(); + result = 1;/*upgrade success*/ + I("%s: TP upgrade OK\n", __func__); + } + + kfree(i_CTPM_FW); + i_CTPM_FW = NULL; + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(true, false); +#else + g_core_fp.fp_sense_on(0x00); +#endif + himax_int_enable(1); + return result; +} +#endif + + +static int himax_loadSensorConfig(struct himax_i2c_platform_data *pdata) +{ + I("%s: initialization complete\n", __func__); + return NO_ERR; +} + +#ifdef HX_ESD_RECOVERY +static void himax_esd_hw_reset(void) +{ +#ifdef HX_ZERO_FLASH + int result = 0; +#endif + if (g_ts_dbg != 0) + I("%s: Entering\n", __func__); + + I("START_Himax TP: ESD - Reset\n"); + + if (private_ts->in_self_test == 1) { + I("In self test , not TP: ESD - Reset\n"); + return; + } + + g_core_fp.fp_esd_ic_reset(); +#ifdef HX_ZERO_FLASH + I("It will update fw after esd event in zero flash mode!\n"); + result = g_core_fp.fp_0f_operation_dirly(); + if (result) { + E("Something is wrong! Skip Update with zero flash!\n"); + goto ESCAPE_0F_UPDATE; + } + g_core_fp.fp_reload_disable(0); + g_core_fp.fp_sense_on(0x00); + himax_report_all_leave_event(private_ts); + himax_int_enable(1); +ESCAPE_0F_UPDATE: +#endif + I("END_Himax TP: ESD - Reset\n"); +} +#endif + +#ifdef HX_SMART_WAKEUP +#ifdef HX_GESTURE_TRACK +static void gest_pt_log_coordinate(int rx, int tx) +{ + /*driver report x y with range 0 - 255 , we scale it up to x/y pixel*/ + gest_pt_x[gest_pt_cnt] = rx * (ic_data->HX_X_RES) / 255; + gest_pt_y[gest_pt_cnt] = tx * (ic_data->HX_Y_RES) / 255; +} +#endif +static int himax_wake_event_parse(struct himax_ts_data *ts, int ts_status) +{ + uint8_t *buf = wake_event_buffer; +#ifdef HX_GESTURE_TRACK + int tmp_max_x = 0x00, tmp_min_x = 0xFFFF, tmp_max_y = 0x00, tmp_min_y = 0xFFFF; + int gest_len; +#endif + int i = 0, check_FC = 0, ret; + int j = 0, gesture_pos = 0, gesture_flag = 0; + + if (g_ts_dbg != 0) + I("%s: Entering!, ts_status=%d\n", __func__, ts_status); + + if (buf == NULL) { + ret = -ENOMEM; + goto END; + } + + memcpy(buf, hx_touch_data->hx_event_buf, hx_touch_data->event_size); + + for (i = 0; i < GEST_PTLG_ID_LEN; i++) { + for (j = 0; j < GEST_SUP_NUM; j++) { + if (buf[i] == gest_event[j]) { + gesture_flag = buf[i]; + gesture_pos = j; + break; + } + } + I("0x%2.2X ", buf[i]); + if (buf[i] == gesture_flag) { + check_FC++; + } else { + I("ID START at %x , value = 0x%2X skip the event\n", i, buf[i]); + break; + } + } + + I("Himax gesture_flag= %x\n", gesture_flag); + I("Himax check_FC is %d\n", check_FC); + + if (check_FC != GEST_PTLG_ID_LEN) { + ret = 0; + goto END; + } + + if (buf[GEST_PTLG_ID_LEN] != GEST_PTLG_HDR_ID1 || + buf[GEST_PTLG_ID_LEN + 1] != GEST_PTLG_HDR_ID2) { + ret = 0; + goto END; + } + +#ifdef HX_GESTURE_TRACK + + if (buf[GEST_PTLG_ID_LEN] == GEST_PTLG_HDR_ID1 && + buf[GEST_PTLG_ID_LEN + 1] == GEST_PTLG_HDR_ID2) { + gest_len = buf[GEST_PTLG_ID_LEN + 2]; + I("gest_len = %d\n", gest_len); + i = 0; + gest_pt_cnt = 0; + I("gest doornidate start\n %s", __func__); + + while (i < (gest_len + 1) / 2) { + gest_pt_log_coordinate(buf[GEST_PTLG_ID_LEN + 4 + i * 2], buf[GEST_PTLG_ID_LEN + 4 + i * 2 + 1]); + i++; + I("gest_pt_x[%d]=%d\n", gest_pt_cnt, gest_pt_x[gest_pt_cnt]); + I("gest_pt_y[%d]=%d\n", gest_pt_cnt, gest_pt_y[gest_pt_cnt]); + gest_pt_cnt += 1; + } + + if (gest_pt_cnt) { + for (i = 0; i < gest_pt_cnt; i++) { + if (tmp_max_x < gest_pt_x[i]) + tmp_max_x = gest_pt_x[i]; + if (tmp_min_x > gest_pt_x[i]) + tmp_min_x = gest_pt_x[i]; + if (tmp_max_y < gest_pt_y[i]) + tmp_max_y = gest_pt_y[i]; + if (tmp_min_y > gest_pt_y[i]) + tmp_min_y = gest_pt_y[i]; + } + + I("gest_point x_min= %d, x_max= %d, y_min= %d, y_max= %d\n", tmp_min_x, tmp_max_x, tmp_min_y, tmp_max_y); + gest_start_x = gest_pt_x[0]; + hx_gesture_coor[0] = gest_start_x; + gest_start_y = gest_pt_y[0]; + hx_gesture_coor[1] = gest_start_y; + gest_end_x = gest_pt_x[gest_pt_cnt - 1]; + hx_gesture_coor[2] = gest_end_x; + gest_end_y = gest_pt_y[gest_pt_cnt - 1]; + hx_gesture_coor[3] = gest_end_y; + gest_width = tmp_max_x - tmp_min_x; + hx_gesture_coor[4] = gest_width; + gest_height = tmp_max_y - tmp_min_y; + hx_gesture_coor[5] = gest_height; + gest_mid_x = (tmp_max_x + tmp_min_x) / 2; + hx_gesture_coor[6] = gest_mid_x; + gest_mid_y = (tmp_max_y + tmp_min_y) / 2; + hx_gesture_coor[7] = gest_mid_y; + hx_gesture_coor[8] = gest_mid_x;/*gest_up_x*/ + hx_gesture_coor[9] = gest_mid_y - gest_height / 2; /*gest_up_y*/ + hx_gesture_coor[10] = gest_mid_x;/*gest_down_x*/ + hx_gesture_coor[11] = gest_mid_y + gest_height / 2; /*gest_down_y*/ + hx_gesture_coor[12] = gest_mid_x - gest_width / 2; /*gest_left_x*/ + hx_gesture_coor[13] = gest_mid_y; /*gest_left_y*/ + hx_gesture_coor[14] = gest_mid_x + gest_width / 2; /*gest_right_x*/ + hx_gesture_coor[15] = gest_mid_y; /*gest_right_y*/ + } + } + +#endif + + if (!ts->gesture_cust_en[gesture_pos]) { + I("%s NOT report key [%d] = %d\n", __func__, gesture_pos, gest_key_def[gesture_pos]); + g_target_report_data->SMWP_event_chk = 0; + ret = 0; + } else { + g_target_report_data->SMWP_event_chk = gest_key_def[gesture_pos]; + ret = gesture_pos; + } +END: + return ret; +} + +static void himax_wake_event_report(void) +{ + int KEY_EVENT = g_target_report_data->SMWP_event_chk; +#ifdef HIMAX_V2_SENSOR_EN + static int report_cnt = 0; +#endif + if (g_ts_dbg != 0) + I("%s: Entering!\n", __func__); + + if (KEY_EVENT) { +#ifdef HIMAX_V2_SENSOR_EN + input_report_abs(private_ts->sensor_pdata->input_sensor_dev, ABS_DISTANCE, ++report_cnt); + I("input report: %d", report_cnt); + if (report_cnt >= REPORT_MAX_COUNT) + report_cnt = 0; + input_sync(private_ts->sensor_pdata->input_sensor_dev); +#else + I(" %s SMART WAKEUP KEY event %d press\n", __func__, KEY_EVENT); + input_report_key(private_ts->input_dev, KEY_EVENT, 1); + input_sync(private_ts->input_dev); + I(" %s SMART WAKEUP KEY event %d release\n", __func__, KEY_EVENT); + input_report_key(private_ts->input_dev, KEY_EVENT, 0); + input_sync(private_ts->input_dev); +#endif + FAKE_POWER_KEY_SEND = true; +#ifdef HX_GESTURE_TRACK + I("gest_start_x= %d, gest_start_y= %d, gest_end_x= %d, gest_end_y= %d\n", gest_start_x, gest_start_y, + gest_end_x, gest_end_y); + I("gest_width= %d, gest_height= %d, gest_mid_x= %d, gest_mid_y= %d\n", gest_width, gest_height, + gest_mid_x, gest_mid_y); + I("gest_up_x= %d, gest_up_y= %d, gest_down_x= %d, gest_down_y= %d\n", hx_gesture_coor[8], hx_gesture_coor[9], + hx_gesture_coor[10], hx_gesture_coor[11]); + I("gest_left_x= %d, gest_left_y= %d, gest_right_x= %d, gest_right_y= %d\n", hx_gesture_coor[12], hx_gesture_coor[13], + hx_gesture_coor[14], hx_gesture_coor[15]); +#endif + g_target_report_data->SMWP_event_chk = 0; + } +} + +#endif + +int himax_report_data_init(void) +{ + if (hx_touch_data->hx_coord_buf != NULL) { + kfree(hx_touch_data->hx_coord_buf); + hx_touch_data->hx_coord_buf = NULL; + } + + if (hx_touch_data->hx_rawdata_buf != NULL) { + kfree(hx_touch_data->hx_rawdata_buf); + hx_touch_data->hx_rawdata_buf = NULL; + } + +#if defined(HX_SMART_WAKEUP) + hx_touch_data->event_size = g_core_fp.fp_get_touch_data_size(); + + if (hx_touch_data->hx_event_buf != NULL) { + kfree(hx_touch_data->hx_event_buf); + hx_touch_data->hx_event_buf = NULL; + } + + if (wake_event_buffer != NULL) { + kfree(wake_event_buffer); + wake_event_buffer = NULL; + } + +#endif + hx_touch_data->touch_all_size = g_core_fp.fp_get_touch_data_size(); + hx_touch_data->raw_cnt_max = ic_data->HX_MAX_PT / 4; + hx_touch_data->raw_cnt_rmd = ic_data->HX_MAX_PT % 4; + /* more than 4 fingers */ + if (hx_touch_data->raw_cnt_rmd != 0x00) { + hx_touch_data->rawdata_size = g_core_fp.fp_cal_data_len(hx_touch_data->raw_cnt_rmd, ic_data->HX_MAX_PT, hx_touch_data->raw_cnt_max); + hx_touch_data->touch_info_size = (ic_data->HX_MAX_PT + hx_touch_data->raw_cnt_max + 2) * 4; + } else { /* less than 4 fingers */ + hx_touch_data->rawdata_size = g_core_fp.fp_cal_data_len(hx_touch_data->raw_cnt_rmd, ic_data->HX_MAX_PT, hx_touch_data->raw_cnt_max); + hx_touch_data->touch_info_size = (ic_data->HX_MAX_PT + hx_touch_data->raw_cnt_max + 1) * 4; + } +#if defined(HX_PEN_FUNC_EN) + hx_touch_data->touch_info_size += PEN_INFO_SZ; +#endif + if ((ic_data->HX_TX_NUM * ic_data->HX_RX_NUM + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM) % hx_touch_data->rawdata_size == 0) + hx_touch_data->rawdata_frame_size = (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM) / hx_touch_data->rawdata_size; + else + hx_touch_data->rawdata_frame_size = (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM + ic_data->HX_TX_NUM + ic_data->HX_RX_NUM) / hx_touch_data->rawdata_size + 1; + + I("%s: rawdata_frame_size = %d\n", __func__, hx_touch_data->rawdata_frame_size); + I("%s: ic_data->HX_MAX_PT:%d, hx_raw_cnt_max:%d, hx_raw_cnt_rmd:%d, g_hx_rawdata_size:%d, hx_touch_data->touch_info_size:%d\n", __func__, ic_data->HX_MAX_PT, hx_touch_data->raw_cnt_max, hx_touch_data->raw_cnt_rmd, hx_touch_data->rawdata_size, hx_touch_data->touch_info_size); + hx_touch_data->hx_coord_buf = kzalloc(sizeof(uint8_t) * (hx_touch_data->touch_info_size), GFP_KERNEL); + + if (hx_touch_data->hx_coord_buf == NULL) + goto mem_alloc_fail; + + if (g_target_report_data == NULL) { + g_target_report_data = kzalloc(sizeof(struct himax_target_report_data), GFP_KERNEL); + if (g_target_report_data == NULL) + goto mem_alloc_fail; + g_target_report_data->x = kzalloc(sizeof(int)*(ic_data->HX_MAX_PT), GFP_KERNEL); + if (g_target_report_data->x == NULL) + goto mem_alloc_fail; + g_target_report_data->y = kzalloc(sizeof(int)*(ic_data->HX_MAX_PT), GFP_KERNEL); + if (g_target_report_data->y == NULL) + goto mem_alloc_fail; + g_target_report_data->w = kzalloc(sizeof(int)*(ic_data->HX_MAX_PT), GFP_KERNEL); + if (g_target_report_data->w == NULL) + goto mem_alloc_fail; + g_target_report_data->finger_id = kzalloc(sizeof(int)*(ic_data->HX_MAX_PT), GFP_KERNEL); + if (g_target_report_data->finger_id == NULL) + goto mem_alloc_fail; +#if defined(HX_PEN_FUNC_EN) + g_target_report_data->p_x = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_x == NULL) + goto mem_alloc_fail; + g_target_report_data->p_y = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_y == NULL) + goto mem_alloc_fail; + g_target_report_data->p_w = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_w == NULL) + goto mem_alloc_fail; + g_target_report_data->pen_id = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->pen_id == NULL) + goto mem_alloc_fail; + g_target_report_data->p_hover = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_hover == NULL) + goto mem_alloc_fail; + g_target_report_data->p_tilt_x = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_tilt_x == NULL) + goto mem_alloc_fail; + g_target_report_data->p_btn = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_btn == NULL) + goto mem_alloc_fail; + g_target_report_data->p_btn2 = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_btn2 == NULL) + goto mem_alloc_fail; + g_target_report_data->p_tilt_y = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_tilt_y == NULL) + goto mem_alloc_fail; + g_target_report_data->p_on = kzalloc(sizeof(int)*2, GFP_KERNEL); + if (g_target_report_data->p_on == NULL) + goto mem_alloc_fail; + +#endif + } +#ifdef HX_SMART_WAKEUP + g_target_report_data->SMWP_event_chk = 0; + wake_event_buffer = kcalloc(hx_touch_data->event_size, sizeof(uint8_t), GFP_KERNEL); + if (wake_event_buffer == NULL) + goto mem_alloc_fail; +#endif + + hx_touch_data->hx_rawdata_buf = kzalloc(sizeof(uint8_t) * (hx_touch_data->touch_all_size - hx_touch_data->touch_info_size), GFP_KERNEL); + + if (hx_touch_data->hx_rawdata_buf == NULL) + goto mem_alloc_fail; + +#if defined(HX_SMART_WAKEUP) + hx_touch_data->hx_event_buf = kzalloc(sizeof(uint8_t) * (hx_touch_data->event_size), GFP_KERNEL); + + if (hx_touch_data->hx_event_buf == NULL) + goto mem_alloc_fail; + +#endif + return NO_ERR; +mem_alloc_fail: +#if defined(HX_PEN_FUNC_EN) + kfree(g_target_report_data->p_on); + g_target_report_data->p_on = NULL; + kfree(g_target_report_data->p_tilt_y); + g_target_report_data->p_tilt_y = NULL; + kfree(g_target_report_data->p_btn2); + g_target_report_data->p_btn2 = NULL; + kfree(g_target_report_data->p_btn); + g_target_report_data->p_btn = NULL; + kfree(g_target_report_data->p_tilt_x); + g_target_report_data->p_tilt_x = NULL; + kfree(g_target_report_data->p_hover); + g_target_report_data->p_hover = NULL; + kfree(g_target_report_data->pen_id); + g_target_report_data->pen_id = NULL; + kfree(g_target_report_data->p_w); + g_target_report_data->p_w = NULL; + kfree(g_target_report_data->p_y); + g_target_report_data->p_y = NULL; + kfree(g_target_report_data->p_x); + g_target_report_data->p_x = NULL; +#endif + kfree(g_target_report_data->finger_id); + g_target_report_data->finger_id = NULL; + kfree(g_target_report_data->w); + g_target_report_data->w = NULL; + kfree(g_target_report_data->y); + g_target_report_data->y = NULL; + kfree(g_target_report_data->x); + g_target_report_data->x = NULL; + kfree(g_target_report_data); + g_target_report_data = NULL; + +#if defined(HX_SMART_WAKEUP) + kfree(wake_event_buffer); + wake_event_buffer = NULL; + kfree(hx_touch_data->hx_event_buf); + hx_touch_data->hx_event_buf = NULL; +#endif + kfree(hx_touch_data->hx_rawdata_buf); + hx_touch_data->hx_rawdata_buf = NULL; + kfree(hx_touch_data->hx_coord_buf); + hx_touch_data->hx_coord_buf = NULL; + + I("%s: Memory allocate fail!\n", __func__); + return MEM_ALLOC_FAIL; +} +EXPORT_SYMBOL(himax_report_data_init); + +void himax_report_data_deinit(void) +{ +#if defined(HX_PEN_FUNC_EN) + kfree(g_target_report_data->p_on); + g_target_report_data->p_on = NULL; + kfree(g_target_report_data->p_tilt_y); + g_target_report_data->p_tilt_y = NULL; + kfree(g_target_report_data->p_btn2); + g_target_report_data->p_btn2 = NULL; + kfree(g_target_report_data->p_btn); + g_target_report_data->p_btn = NULL; + kfree(g_target_report_data->p_tilt_x); + g_target_report_data->p_tilt_x = NULL; + kfree(g_target_report_data->p_hover); + g_target_report_data->p_hover = NULL; + kfree(g_target_report_data->pen_id); + g_target_report_data->pen_id = NULL; + kfree(g_target_report_data->p_w); + g_target_report_data->p_w = NULL; + kfree(g_target_report_data->p_y); + g_target_report_data->p_y = NULL; + kfree(g_target_report_data->p_x); + g_target_report_data->p_x = NULL; +#endif + kfree(g_target_report_data->finger_id); + g_target_report_data->finger_id = NULL; + kfree(g_target_report_data->w); + g_target_report_data->w = NULL; + kfree(g_target_report_data->y); + g_target_report_data->y = NULL; + kfree(g_target_report_data->x); + g_target_report_data->x = NULL; + kfree(g_target_report_data); + g_target_report_data = NULL; + +#if defined(HX_SMART_WAKEUP) + kfree(wake_event_buffer); + wake_event_buffer = NULL; + kfree(hx_touch_data->hx_event_buf); + hx_touch_data->hx_event_buf = NULL; +#endif + kfree(hx_touch_data->hx_rawdata_buf); + hx_touch_data->hx_rawdata_buf = NULL; + kfree(hx_touch_data->hx_coord_buf); + hx_touch_data->hx_coord_buf = NULL; +} + +/*start ts_work*/ +#if defined(HX_USB_DETECT_GLOBAL) +void himax_cable_detect_func(bool force_renew) +{ + struct himax_ts_data *ts; + + /*u32 connect_status = 0;*/ + uint8_t connect_status = 0; + + connect_status = USB_detect_flag;/* upmu_is_chr_det(); */ + ts = private_ts; + + /* I("Touch: cable status=%d, cable_config=%p, usb_connected=%d\n", connect_status, ts->cable_config, ts->usb_connected); */ + if (ts->cable_config) { + if ((connect_status != ts->usb_connected) || force_renew) { + if (connect_status) { + ts->cable_config[1] = 0x01; + ts->usb_connected = 0x01; + } else { + ts->cable_config[1] = 0x00; + ts->usb_connected = 0x00; + } + + g_core_fp.fp_usb_detect_set(ts->cable_config); + I("%s: Cable status change: 0x%2.2X\n", __func__, ts->usb_connected); + } + + /*else */ + /*I("%s: Cable status is the same as previous one, ignore.\n", __func__); */ + } +} +#endif + +static int himax_ts_work_status(struct himax_ts_data *ts) +{ + /* 1: normal, 2:SMWP */ + int result = HX_REPORT_COORD; + + hx_touch_data->diag_cmd = ts->diag_cmd; + if (hx_touch_data->diag_cmd) + result = HX_REPORT_COORD_RAWDATA; + +#ifdef HX_SMART_WAKEUP + if (atomic_read(&ts->suspend_mode) && (ts->SMWP_enable) && (!hx_touch_data->diag_cmd)) + result = HX_REPORT_SMWP_EVENT; +#endif + /* I("Now Status is %d\n", result); */ + return result; +} + +static int himax_touch_get(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + switch (ts_path) { + /*normal*/ + case HX_REPORT_COORD: + if ((HX_HW_RESET_ACTIVATE) +#ifdef HX_ESD_RECOVERY + || (HX_ESD_RESET_ACTIVATE) +#endif + ) { + if (!g_core_fp.fp_read_event_stack(buf, 128)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + } + } else { + if (!g_core_fp.fp_read_event_stack(buf, hx_touch_data->touch_info_size)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + } + } + break; +#if defined(HX_SMART_WAKEUP) + + /*SMWP*/ + case HX_REPORT_SMWP_EVENT: + __pm_wakeup_event(&ts->ts_SMWP_wake_lock, TS_WAKE_LOCK_TIMEOUT); + msleep(20); + g_core_fp.fp_burst_enable(0); + + if (!g_core_fp.fp_read_event_stack(buf, hx_touch_data->event_size)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + } + break; +#endif + case HX_REPORT_COORD_RAWDATA: + if (!g_core_fp.fp_read_event_stack(buf, 128)) { + E("%s: can't read data from chip!\n", __func__); + ts_status = HX_TS_GET_DATA_FAIL; + } + break; + default: + break; + } + + return ts_status; +} + +/* start error_control*/ +static int himax_checksum_cal(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ + uint16_t check_sum_cal = 0; + int32_t i = 0; + int length = 0; + int zero_cnt = 0; + int raw_data_sel = 0; + int ret_val = ts_status; + + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + /* Normal */ + switch (ts_path) { + case HX_REPORT_COORD: + length = hx_touch_data->touch_info_size; + break; +#if defined(HX_SMART_WAKEUP) +/* SMWP */ + case HX_REPORT_SMWP_EVENT: + length = (GEST_PTLG_ID_LEN + GEST_PTLG_HDR_LEN); + break; +#endif + case HX_REPORT_COORD_RAWDATA: + length = hx_touch_data->touch_info_size; + break; + default: + I("%s, Neither Normal Nor SMWP error!\n", __func__); + ret_val = HX_PATH_FAIL; + goto END_FUNCTION; + } + + for (i = 0; i < length; i++) { + check_sum_cal += buf[i]; + if (buf[i] == 0x00) + zero_cnt++; + } + + if (check_sum_cal % 0x100 != 0) { + I("[HIMAX TP MSG] point data_checksum not match : check_sum_cal: 0x%02X\n", check_sum_cal); + ret_val = HX_CHKSUM_FAIL; + } else if (zero_cnt == length) { + if (ts->use_irq) + I("[HIMAX TP MSG] All Zero event\n"); + + ret_val = HX_CHKSUM_FAIL; + } else { + raw_data_sel = buf[HX_TOUCH_INFO_POINT_CNT]>>4 & 0x0F; + /*I("%s:raw_out_sel=%x , hx_touch_data->diag_cmd=%x.\n", __func__, raw_data_sel, hx_touch_data->diag_cmd);*/ + if ((raw_data_sel != 0x0F) && (raw_data_sel != hx_touch_data->diag_cmd)) {/*raw data out not match skip it*/ + /*I("%s:raw data out not match.\n", __func__);*/ + if (!hx_touch_data->diag_cmd) { + g_core_fp.fp_read_event_stack(buf, (128-hx_touch_data->touch_info_size));/*Need to clear event stack here*/ + /*I("%s: size =%d, buf[0]=%x ,buf[1]=%x, buf[2]=%x, buf[3]=%x.\n", __func__,(128-hx_touch_data->touch_info_size), buf[0], buf[1], buf[2], buf[3]);*/ + /*I("%s:also clear event stack.\n", __func__);*/ + } + ret_val = HX_READY_SERVE; + } + } + +END_FUNCTION: + if (g_ts_dbg != 0) + I("%s: END, ret_val=%d!\n", __func__, ret_val); + return ret_val; +} + +#ifdef HX_ESD_RECOVERY +#ifdef HX_ZERO_FLASH +void hx_update_dirly_0f(void) +{ + I("It will update fw after esd event in zero flash mode!\n"); + g_core_fp.fp_0f_operation_dirly(); +} +#endif +static int himax_ts_event_check(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ + int hx_EB_event = 0; + int hx_EC_event = 0; + int hx_ED_event = 0; + int hx_esd_event = 0; + int hx_zero_event = 0; + int shaking_ret = 0; + + int32_t loop_i = 0; + int length = 0; + int ret_val = ts_status; + + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + /* Normal */ + switch (ts_path) { + case HX_REPORT_COORD: + length = hx_touch_data->touch_info_size; + break; +#if defined(HX_SMART_WAKEUP) +/* SMWP */ + case HX_REPORT_SMWP_EVENT: + length = (GEST_PTLG_ID_LEN + GEST_PTLG_HDR_LEN); + break; +#endif + case HX_REPORT_COORD_RAWDATA: + length = hx_touch_data->touch_info_size; + break; + default: + I("%s, Neither Normal Nor SMWP error!\n", __func__); + ret_val = HX_PATH_FAIL; + goto END_FUNCTION; + } + + if (g_ts_dbg != 0) + I("Now Path=%d, Now status=%d, length=%d\n", ts_path, ts_status, length); + + for (loop_i = 0; loop_i < length; loop_i++) { + if (ts_path == HX_REPORT_COORD || ts_path == HX_REPORT_COORD_RAWDATA) { + /* case 1 ESD recovery flow */ + if (buf[loop_i] == 0xEB) { + hx_EB_event++; + } else if (buf[loop_i] == 0xEC) { + hx_EC_event++; + } else if (buf[loop_i] == 0xED) { + hx_ED_event++; + } else if (buf[loop_i] == 0x00) { /* case 2 ESD recovery flow-Disable */ + hx_zero_event++; + } else { + hx_EB_event = 0; + hx_EC_event = 0; + hx_ED_event = 0; + hx_zero_event = 0; + g_zero_event_count = 0; + } + } + } + + if (hx_EB_event == length) { + hx_esd_event = length; + hx_EB_event_flag++; + I("[HIMAX TP MSG]: ESD event checked - ALL 0xEB.\n"); + } else if (hx_EC_event == length) { + hx_esd_event = length; + hx_EC_event_flag++; + I("[HIMAX TP MSG]: ESD event checked - ALL 0xEC.\n"); + } else if (hx_ED_event == length) { + hx_esd_event = length; + hx_ED_event_flag++; + I("[HIMAX TP MSG]: ESD event checked - ALL 0xED.\n"); + } +/*#ifdef HX_ZERO_FLASH + * //This is for previous version(a, b) because HW pull TSIX low continuely after watchdog timeout reset + * else if (hx_zero_event == length) { + * //check zero flash status + * if (g_core_fp.fp_0f_esd_check() < 0) { + * g_zero_event_count = 6; + * I("[HIMAX TP MSG]: ESD event checked - ALL Zero in ZF.\n"); + * } else { + * I("[HIMAX TP MSG]: Status check pass in ZF.\n"); + * } + * } + *#endif + */ + else + hx_esd_event = 0; + + if ((hx_esd_event == length || hx_zero_event == length) + && (HX_HW_RESET_ACTIVATE == 0) + && (HX_ESD_RESET_ACTIVATE == 0) + && (hx_touch_data->diag_cmd == 0) + && (ts->in_self_test == 0)) { + shaking_ret = g_core_fp.fp_ic_esd_recovery(hx_esd_event, hx_zero_event, length); + + if (shaking_ret == HX_ESD_EVENT) { + himax_esd_hw_reset(); + ret_val = HX_ESD_EVENT; + } else if (shaking_ret == HX_ZERO_EVENT_COUNT) { + ret_val = HX_ZERO_EVENT_COUNT; + } else { + I("I2C running. Nothing to be done!\n"); + ret_val = HX_IC_RUNNING; + } + } else if (HX_ESD_RESET_ACTIVATE) { /* drop 1st interrupts after chip reset */ + HX_ESD_RESET_ACTIVATE = 0; + I("[HX_ESD_RESET_ACTIVATE]:%s: Back from reset, ready to serve.\n", __func__); + ret_val = HX_ESD_REC_OK; + } + +END_FUNCTION: + if (g_ts_dbg != 0) + I("%s: END, ret_val=%d!\n", __func__, ret_val); + + return ret_val; +} +#endif + +static int himax_err_ctrl(struct himax_ts_data *ts, uint8_t *buf, int ts_path, int ts_status) +{ +#ifdef HX_RST_PIN_FUNC + if (HX_HW_RESET_ACTIVATE) { + /* drop 1st interrupts after chip reset */ + HX_HW_RESET_ACTIVATE = 0; + I("[HX_HW_RESET_ACTIVATE]:%s: Back from reset, ready to serve.\n", __func__); + ts_status = HX_RST_OK; + goto END_FUNCTION; + } +#endif + + ts_status = himax_checksum_cal(ts, buf, ts_path, ts_status); + if (ts_status == HX_CHKSUM_FAIL) { + goto CHK_FAIL; + } else { +#ifdef HX_ESD_RECOVERY + /* continuous N times record, not total N times. */ + g_zero_event_count = 0; +#endif + goto END_FUNCTION; + } + +CHK_FAIL: +#ifdef HX_ESD_RECOVERY + ts_status = himax_ts_event_check(ts, buf, ts_path, ts_status); +#endif + + +END_FUNCTION: + if (g_ts_dbg != 0) + I("%s: END, ts_status=%d!\n", __func__, ts_status); + return ts_status; +} +/* end error_control*/ + +/* start distribute_data*/ +static int himax_distribute_touch_data(uint8_t *buf, int ts_path, int ts_status) +{ + uint8_t hx_state_info_pos = hx_touch_data->touch_info_size - 3; + +#if defined(HX_PEN_FUNC_EN) + hx_state_info_pos -= PEN_INFO_SZ; +#endif + + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + if (ts_path == HX_REPORT_COORD) { + memcpy(hx_touch_data->hx_coord_buf, &buf[0], hx_touch_data->touch_info_size); + + if (buf[hx_state_info_pos] != 0xFF && buf[hx_state_info_pos + 1] != 0xFF) + memcpy(hx_touch_data->hx_state_info, &buf[hx_state_info_pos], 2); + else + memset(hx_touch_data->hx_state_info, 0x00, sizeof(hx_touch_data->hx_state_info)); + + if ((HX_HW_RESET_ACTIVATE) +#ifdef HX_ESD_RECOVERY + || (HX_ESD_RESET_ACTIVATE) +#endif + ) { + memcpy(hx_touch_data->hx_rawdata_buf, &buf[hx_touch_data->touch_info_size], hx_touch_data->touch_all_size - hx_touch_data->touch_info_size); + } + } else if (ts_path == HX_REPORT_COORD_RAWDATA) { + memcpy(hx_touch_data->hx_coord_buf, &buf[0], hx_touch_data->touch_info_size); + + if (buf[hx_state_info_pos] != 0xFF && buf[hx_state_info_pos + 1] != 0xFF) + memcpy(hx_touch_data->hx_state_info, &buf[hx_state_info_pos], 2); + else + memset(hx_touch_data->hx_state_info, 0x00, sizeof(hx_touch_data->hx_state_info)); + + memcpy(hx_touch_data->hx_rawdata_buf, &buf[hx_touch_data->touch_info_size], hx_touch_data->touch_all_size - hx_touch_data->touch_info_size); +#if defined(HX_SMART_WAKEUP) + } else if (ts_path == HX_REPORT_SMWP_EVENT) { + memcpy(hx_touch_data->hx_event_buf, buf, hx_touch_data->event_size); +#endif + } else { + E("%s, Fail Path!\n", __func__); + ts_status = HX_PATH_FAIL; + } + + if (g_ts_dbg != 0) + I("%s: End, ts_status=%d!\n", __func__, ts_status); + return ts_status; +} +/* end assign_data*/ + +/* start parse_report_data*/ +int himax_parse_report_points(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + int x = 0, y = 0, w = 0; +#if defined(HX_PEN_FUNC_EN) + uint8_t p_hover = 0, p_btn = 0, p_btn2 = 0; + int8_t p_tilt_x = 0, p_tilt_y = 0; + int p_x = 0, p_y = 0, p_w = 0; + static uint8_t p_p_on; +#endif + int base = 0; + int32_t loop_i = 0; + + if (g_ts_dbg != 0) + I("%s: start!\n", __func__); + +#if defined(HX_PEN_FUNC_EN) + p_p_on = 0; + base = hx_touch_data->touch_info_size - PEN_INFO_SZ; + + p_x = hx_touch_data->hx_coord_buf[base] << 8 | hx_touch_data->hx_coord_buf[base + 1]; + p_y = (hx_touch_data->hx_coord_buf[base + 2] << 8 | hx_touch_data->hx_coord_buf[base + 3]); + p_w = (hx_touch_data->hx_coord_buf[base + 4] << 8 | hx_touch_data->hx_coord_buf[base + 5]); + p_tilt_x = (int8_t)hx_touch_data->hx_coord_buf[base + 6]; + p_hover = hx_touch_data->hx_coord_buf[base + 7]; + p_btn = hx_touch_data->hx_coord_buf[base + 8]; + p_btn2 = hx_touch_data->hx_coord_buf[base + 9]; + p_tilt_y = (int8_t)hx_touch_data->hx_coord_buf[base + 10]; + + if (g_ts_dbg != 0) { + D("%s: p_x=%d, p_y=%d, p_w=%d\n", __func__, p_x, p_y, p_w); + D("%s: p_tilt_x=%d, p_hover=%d\n", __func__, p_tilt_x, p_hover); + D("%s: p_btn=%d, p_btn2=%d\n", __func__, p_btn, p_btn2); + D("%s: p_tilt_y=%d\n", __func__, p_tilt_y); + } + + if (p_x >= 0 && p_x <= ts->pdata->abs_x_max && p_y >= 0 && p_y <= ts->pdata->abs_y_max) { + g_target_report_data->p_x[0] = p_x; + g_target_report_data->p_y[0] = p_y; + g_target_report_data->p_w[0] = p_w; + g_target_report_data->p_hover[0] = p_hover; + g_target_report_data->pen_id[0] = 1; + g_target_report_data->p_btn[0] = p_btn; + g_target_report_data->p_btn2[0] = p_btn2; + g_target_report_data->p_tilt_x[0] = p_tilt_x; + g_target_report_data->p_tilt_y[0] = p_tilt_y; + g_target_report_data->p_on[0] = 1; + ts->hx_point_num++; + } else {/* report coordinates */ + g_target_report_data->p_x[0] = 0; + g_target_report_data->p_y[0] = 0; + g_target_report_data->p_w[0] = 0; + g_target_report_data->p_hover[0] = 0; + g_target_report_data->pen_id[0] = 0; + g_target_report_data->p_btn[0] = 0; + g_target_report_data->p_btn2[0] = 0; + g_target_report_data->p_tilt_x[0] = 0; + g_target_report_data->p_tilt_y[0] = 0; + g_target_report_data->p_on[0] = 0; + } + + if (g_ts_dbg != 0) { + if (p_p_on != g_target_report_data->p_on[0]) { + I("p_on[0] = %d, hx_point_num=%d\n", + g_target_report_data->p_on[0], + ts->hx_point_num); + p_p_on = g_target_report_data->p_on[0]; + } + } +#endif + + ts->old_finger = ts->pre_finger_mask; + if (ts->hx_point_num == 0) { + if (g_ts_dbg != 0) + I("%s: hx_point_num = 0!\n", __func__); + return ts_status; + } + ts->pre_finger_mask = 0; + hx_touch_data->finger_num = hx_touch_data->hx_coord_buf[ts->coordInfoSize - 4] & 0x0F; + hx_touch_data->finger_on = 1; + AA_press = 1; + + g_target_report_data->finger_num = hx_touch_data->finger_num; + g_target_report_data->finger_on = hx_touch_data->finger_on; + g_target_report_data->ig_count = hx_touch_data->hx_coord_buf[ts->coordInfoSize - 5]; + + if (g_ts_dbg != 0) + I("%s:finger_num = 0x%2X, finger_on = %d\n", __func__, g_target_report_data->finger_num, g_target_report_data->finger_on); + + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + base = loop_i * 4; + x = hx_touch_data->hx_coord_buf[base] << 8 | hx_touch_data->hx_coord_buf[base + 1]; + y = (hx_touch_data->hx_coord_buf[base + 2] << 8 | hx_touch_data->hx_coord_buf[base + 3]); + w = hx_touch_data->hx_coord_buf[(ts->nFinger_support * 4) + loop_i]; + + if (g_ts_dbg != 0) + D("%s: now parsing[%d]:x=%d, y=%d, w=%d\n", __func__, loop_i, x, y, w); + + if (x >= 0 && x <= ts->pdata->abs_x_max && y >= 0 && y <= ts->pdata->abs_y_max) { + hx_touch_data->finger_num--; + + g_target_report_data->x[loop_i] = x; + g_target_report_data->y[loop_i] = y; + g_target_report_data->w[loop_i] = w; + g_target_report_data->finger_id[loop_i] = 1; + + /*I("%s: g_target_report_data->x[loop_i]=%d, g_target_report_data->y[loop_i]=%d, g_target_report_data->w[loop_i]=%d",*/ + /*__func__, g_target_report_data->x[loop_i], g_target_report_data->y[loop_i], g_target_report_data->w[loop_i]); */ + + + if (!ts->first_pressed) { + ts->first_pressed = 1; + I("S1@%d, %d\n", x, y); + } + + ts->pre_finger_data[loop_i][0] = x; + ts->pre_finger_data[loop_i][1] = y; + + ts->pre_finger_mask = ts->pre_finger_mask + (1 << loop_i); + } else {/* report coordinates */ + g_target_report_data->x[loop_i] = x; + g_target_report_data->y[loop_i] = y; + g_target_report_data->w[loop_i] = w; + g_target_report_data->finger_id[loop_i] = 0; + + if (loop_i == 0 && ts->first_pressed == 1) { + ts->first_pressed = 2; + I("E1@%d, %d\n", ts->pre_finger_data[0][0], ts->pre_finger_data[0][1]); + } + } + } + + if (g_ts_dbg != 0) { + for (loop_i = 0; loop_i < 10; loop_i++) + D("DBG X=%d Y=%d ID=%d\n", g_target_report_data->x[loop_i], g_target_report_data->y[loop_i], g_target_report_data->finger_id[loop_i]); + + D("DBG finger number %d\n", g_target_report_data->finger_num); + } + + if (g_ts_dbg != 0) + I("%s: end!\n", __func__); + return ts_status; +} + +static int himax_parse_report_data(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + + if (g_ts_dbg != 0) + I("%s: start now_status=%d!\n", __func__, ts_status); + + + EN_NoiseFilter = (hx_touch_data->hx_coord_buf[HX_TOUCH_INFO_POINT_CNT + 2] >> 3); + /* I("EN_NoiseFilter=%d\n", EN_NoiseFilter); */ + EN_NoiseFilter = EN_NoiseFilter & 0x01; + /* I("EN_NoiseFilter2=%d\n", EN_NoiseFilter); */ +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) + tpd_key = (hx_touch_data->hx_coord_buf[HX_TOUCH_INFO_POINT_CNT + 2] >> 4); + + /* All (VK+AA)leave */ + if (tpd_key == 0x0F) + tpd_key = 0x00; + +#endif + p_point_num = ts->hx_point_num; + + if (hx_touch_data->hx_coord_buf[HX_TOUCH_INFO_POINT_CNT] == 0xff) + ts->hx_point_num = 0; + else + ts->hx_point_num = hx_touch_data->hx_coord_buf[HX_TOUCH_INFO_POINT_CNT] & 0x0f; + + + switch (ts_path) { + case HX_REPORT_COORD: + ts_status = himax_parse_report_points(ts, ts_path, ts_status); + break; + case HX_REPORT_COORD_RAWDATA: + /* touch monitor rawdata */ + if (debug_data != NULL) { + if (debug_data->fp_set_diag_cmd(ic_data, hx_touch_data)) + I("%s: raw data_checksum not match\n", __func__); + } else { + E("%s,There is no init set_diag_cmd\n", __func__); + } + ts_status = himax_parse_report_points(ts, ts_path, ts_status); + break; +#ifdef HX_SMART_WAKEUP + case HX_REPORT_SMWP_EVENT: + himax_wake_event_parse(ts, ts_status); + break; +#endif + default: + E("%s:Fail Path!\n", __func__); + ts_status = HX_PATH_FAIL; + break; + } + if (g_ts_dbg != 0) + I("%s: end now_status=%d!\n", __func__, ts_status); + return ts_status; +} + +/* end parse_report_data*/ + +static void himax_report_all_leave_event(struct himax_ts_data *ts) +{ + int loop_i = 0; + + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { +#ifndef HX_PROTOCOL_A + input_mt_slot(ts->input_dev, loop_i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_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); + input_sync(ts->input_dev); +} + +/* start report_data */ +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) +static void himax_key_report_operation(int tp_key_index, struct himax_ts_data *ts) +{ + uint16_t x_position = 0, y_position = 0; + + if (g_ts_dbg != 0) + I("%s: Entering\n", __func__); + + if (tp_key_index != 0x00) { + I("virtual key index =%x\n", tp_key_index); + + if (tp_key_index == 0x01) { + vk_press = 1; + I("back key pressed\n"); + + if (ts->pdata->virtual_key) { + if (ts->button[0].index) { + x_position = (ts->button[0].x_range_min + ts->button[0].x_range_max) / 2; + y_position = (ts->button[0].y_range_min + ts->button[0].y_range_max) / 2; + } + +#ifdef HX_PROTOCOL_A + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 0); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); + input_mt_sync(ts->input_dev); +#else + input_mt_slot(ts->input_dev, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); +#endif + } else { + input_report_key(ts->input_dev, KEY_BACK, 1); + } + } else if (tp_key_index == 0x02) { + vk_press = 1; + I("home key pressed\n"); + + if (ts->pdata->virtual_key) { + if (ts->button[1].index) { + x_position = (ts->button[1].x_range_min + ts->button[1].x_range_max) / 2; + y_position = (ts->button[1].y_range_min + ts->button[1].y_range_max) / 2; + } + +#ifdef HX_PROTOCOL_A + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 0); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); + input_mt_sync(ts->input_dev); +#else + input_mt_slot(ts->input_dev, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); +#endif + } else { + input_report_key(ts->input_dev, KEY_HOME, 1); + } + } else if (tp_key_index == 0x04) { + vk_press = 1; + I("APP_switch key pressed\n"); + + if (ts->pdata->virtual_key) { + if (ts->button[2].index) { + x_position = (ts->button[2].x_range_min + ts->button[2].x_range_max) / 2; + y_position = (ts->button[2].y_range_min + ts->button[2].y_range_max) / 2; + } + +#ifdef HX_PROTOCOL_A + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); + input_mt_sync(ts->input_dev); +#else + input_mt_slot(ts->input_dev, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 100); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 100); + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, x_position); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, y_position); +#endif + } else { + input_report_key(ts->input_dev, KEY_APPSELECT, 1); + } + } + input_sync(ts->input_dev); + } else { /*tp_key_index =0x00*/ + I("virtual key released\n"); + vk_press = 0; +#ifndef HX_PROTOCOL_A + input_mt_slot(ts->input_dev, 0); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); +#else + input_mt_sync(ts->input_dev); +#endif + input_report_key(ts->input_dev, KEY_BACK, 0); + input_report_key(ts->input_dev, KEY_HOME, 0); + input_report_key(ts->input_dev, KEY_APPSELECT, 0); +#ifndef HX_PROTOCOL_A + input_sync(ts->input_dev); +#endif + } +} + +void himax_finger_report_key(struct himax_ts_data *ts) +{ + if (ts->hx_point_num != 0) { + /*Touch KEY*/ + if ((tpd_key_old != 0x00) && (tpd_key == 0x00)) { + /* temp_x[0] = 0xFFFF; + * temp_y[0] = 0xFFFF; + * temp_x[1] = 0xFFFF; + * temp_y[1] = 0xFFFF; + */ + hx_touch_data->finger_on = 0; +#ifdef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 0); +#endif + himax_key_report_operation(tpd_key, ts); +#ifndef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 0); +#endif + } + input_sync(ts->input_dev); + } +} + +void himax_finger_leave_key(struct himax_ts_data *ts) +{ + if (tpd_key != 0x00) { + hx_touch_data->finger_on = 1; +#ifdef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif + himax_key_report_operation(tpd_key, ts); +#ifndef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif + } else if ((tpd_key_old != 0x00) && (tpd_key == 0x00)) { + hx_touch_data->finger_on = 0; +#ifdef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 0); +#endif + himax_key_report_operation(tpd_key, ts); +#ifndef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 0); +#endif + } + input_sync(ts->input_dev); +} + +static void himax_report_key(struct himax_ts_data *ts) +{ + if (ts->hx_point_num != 0) { /* Touch KEY */ + himax_finger_report_key(ts); + } else { /* Key */ + himax_finger_leave_key(ts); + } + + tpd_key_old = tpd_key; + Last_EN_NoiseFilter = EN_NoiseFilter; +} +#endif + +/* start report_point*/ +static void himax_finger_report(struct himax_ts_data *ts) +{ + int i = 0; + bool valid = false; + + + if (g_ts_dbg != 0) { + I("%s:start\n", __func__); + I("hx_touch_data->finger_num=%d\n", hx_touch_data->finger_num); + } + for (i = 0; i < ts->nFinger_support; i++) { + if (g_target_report_data->x[i] >= 0 && g_target_report_data->x[i] <= ts->pdata->abs_x_max && g_target_report_data->y[i] >= 0 && g_target_report_data->y[i] <= ts->pdata->abs_y_max) + valid = true; + else + valid = false; + if (g_ts_dbg != 0) + I("valid=%d\n", valid); + if (valid) { + if (g_ts_dbg != 0) + I("g_target_report_data->x[i]=%d, g_target_report_data->y[i]=%d, g_target_report_data->w[i]=%d\n", g_target_report_data->x[i], g_target_report_data->y[i], g_target_report_data->w[i]); +#ifndef HX_PROTOCOL_A + input_mt_slot(ts->input_dev, i); +#else + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, g_target_report_data->w[i]); +#ifndef HX_PROTOCOL_A + input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, g_target_report_data->w[i]); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, g_target_report_data->w[i]); +#else + input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, i); +#endif + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, g_target_report_data->x[i]); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, g_target_report_data->y[i]); +#ifndef HX_PROTOCOL_A + ts->last_slot = i; + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); +#else + input_mt_sync(ts->input_dev); +#endif + } else { +#ifndef HX_PROTOCOL_A + 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_WIDTH_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 + } + } +#ifndef HX_PROTOCOL_A + input_report_key(ts->input_dev, BTN_TOUCH, 1); +#endif + input_sync(ts->input_dev); + +#if defined(HX_PEN_FUNC_EN) + valid = false; + + if (g_target_report_data->p_x[0] >= 0 && g_target_report_data->p_x[0] <= ts->pdata->abs_x_max + && g_target_report_data->p_y[0] >= 0 && g_target_report_data->p_y[0] <= ts->pdata->abs_y_max + && (g_target_report_data->p_on[0] == 1)) + valid = true; + else + valid = false; + if (g_ts_dbg != 0) + I("pen valid=%d\n", valid); + if (valid) {/*Pen down*/ + if (g_ts_dbg != 0) + I("p_x[i]=%d, p_y[i]=%d, p_w[i]=%d\n", g_target_report_data->p_x[0], g_target_report_data->p_y[0], g_target_report_data->p_w[0]); + + input_report_abs(ts->hx_pen_dev, ABS_X, g_target_report_data->p_x[0]); + input_report_abs(ts->hx_pen_dev, ABS_Y, g_target_report_data->p_y[0]); + input_report_key(ts->hx_pen_dev, BTN_STYLUS, 1); + input_report_abs(ts->hx_pen_dev, ABS_TILT_X, g_target_report_data->p_tilt_x[0]); + input_report_abs(ts->hx_pen_dev, ABS_TILT_Y, g_target_report_data->p_tilt_y[0]); + input_report_key(ts->hx_pen_dev, BTN_TOOL_PEN, 1); + if (g_target_report_data->p_hover[0] == 0) { + input_report_key(ts->hx_pen_dev, BTN_TOUCH, 1); + input_report_abs(ts->hx_pen_dev, ABS_DISTANCE, 0); + input_report_abs(ts->hx_pen_dev, ABS_PRESSURE, g_target_report_data->p_w[0]); + } else { + input_report_key(ts->hx_pen_dev, BTN_TOUCH, 0); + input_report_abs(ts->hx_pen_dev, ABS_DISTANCE, 1); + input_report_abs(ts->hx_pen_dev, ABS_PRESSURE, 0); + } + } else {/*Pen up*/ + input_report_key(ts->hx_pen_dev, BTN_STYLUS, 0); + input_report_key(ts->hx_pen_dev, BTN_TOUCH, 0); + input_report_abs(ts->hx_pen_dev, ABS_PRESSURE, 0); + input_sync(ts->hx_pen_dev); + + input_report_abs(ts->hx_pen_dev, ABS_DISTANCE, 0); + input_report_key(ts->hx_pen_dev, BTN_TOOL_RUBBER, 0); + input_report_key(ts->hx_pen_dev, BTN_TOOL_PEN, 0); + input_report_abs(ts->hx_pen_dev, ABS_PRESSURE, 0); + } + input_sync(ts->hx_pen_dev); + +#endif + + if (g_ts_dbg != 0) + I("%s:end\n", __func__); +} + + +#ifdef HIMAX_PALM_SENSOR_EN +static void himax_palm_detect_func(struct himax_ts_data *ts, bool detected) { + if (detected) { + I("palm detected\n"); + input_report_abs(ts->palm_sensor_pdata->input_sensor_dev, ABS_DISTANCE, 1); + } else { + I("palm leave\n"); + input_report_abs(ts->palm_sensor_pdata->input_sensor_dev, ABS_DISTANCE, 0); + } + input_sync(ts->palm_sensor_pdata->input_sensor_dev); +} +#endif + +static void himax_finger_leave(struct himax_ts_data *ts) +{ +#ifndef HX_PROTOCOL_A + int32_t loop_i = 0; +#endif + + if (g_ts_dbg != 0) + I("%s: start!\n", __func__); + +#ifndef HIMAX_PALM_SENSOR_EN +#if defined(HX_PALM_REPORT) + if (himax_palm_detect(hx_touch_data->hx_coord_buf) == PALM_REPORT) { + I(" %s HX_PALM_REPORT KEY power event press\n", __func__); + input_report_key(ts->input_dev, KEY_POWER, 1); + input_sync(ts->input_dev); + msleep(100); + + I(" %s HX_PALM_REPORT KEY power event release\n", __func__); + input_report_key(ts->input_dev, KEY_POWER, 0); + input_sync(ts->input_dev); + return; + } +#endif +#endif + + hx_touch_data->finger_on = 0; + g_target_report_data->finger_on = 0; + g_target_report_data->finger_num = 0; + AA_press = 0; + +#ifdef HX_PROTOCOL_A + input_mt_sync(ts->input_dev); +#endif +#ifndef HX_PROTOCOL_A + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + input_mt_slot(ts->input_dev, loop_i); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0); + input_report_abs(ts->input_dev, ABS_MT_WIDTH_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 + if (ts->pre_finger_mask > 0) + ts->pre_finger_mask = 0; + + if (ts->first_pressed == 1) { + ts->first_pressed = 2; + I("E1@%d, %d\n", ts->pre_finger_data[0][0], ts->pre_finger_data[0][1]); + } + + /*if (ts->debug_log_level & BIT(1)) */ + /*himax_log_touch_event(x, y, w, loop_i, EN_NoiseFilter, HX_FINGER_LEAVE); */ + + input_report_key(ts->input_dev, BTN_TOUCH, 0); + input_sync(ts->input_dev); + +#if defined(HX_PEN_FUNC_EN) + input_report_key(ts->hx_pen_dev, BTN_STYLUS, 0); + input_report_key(ts->hx_pen_dev, BTN_TOUCH, 0); + input_report_abs(ts->hx_pen_dev, ABS_PRESSURE, 0); + input_sync(ts->hx_pen_dev); + + input_report_abs(ts->hx_pen_dev, ABS_DISTANCE, 0); + input_report_abs(ts->hx_pen_dev, ABS_TILT_X, 0); + input_report_abs(ts->hx_pen_dev, ABS_TILT_Y, 0); + input_report_key(ts->hx_pen_dev, BTN_TOOL_RUBBER, 0); + input_report_key(ts->hx_pen_dev, BTN_TOOL_PEN, 0); + input_sync(ts->hx_pen_dev); +#endif + + if (g_ts_dbg != 0) + I("%s: end!\n", __func__); + + +} + +static void himax_report_points(struct himax_ts_data *ts) +{ + if (g_ts_dbg != 0) + I("%s: start!\n", __func__); + +#if defined(HX_PALM_REPORT) && defined(HIMAX_PALM_SENSOR_EN) + if (ts->palm_detection_enabled) { + if (himax_palm_detect(hx_touch_data->hx_coord_buf) == PALM_REPORT) { + del_timer(&ts->palm_release_fimer); + himax_palm_detect_func(ts, true); + return; + } else if (himax_palm_detect(hx_touch_data->hx_coord_buf) == PALM_LEAVE_REPORT) { + mod_timer(&ts->palm_release_fimer, + jiffies + msecs_to_jiffies(ts->palm_release_delay_ms)); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&ts->palm_gesture_wakelock, + ts->palm_release_delay_ms); +#else + __pm_wakeup_event(&ts->palm_gesture_wakelock, + ts->palm_release_delay_ms); +#endif + return; + } else if (ts->suspended) + return; + } +#endif + if (ts->hx_point_num != 0) + himax_finger_report(ts); + else + himax_finger_leave(ts); + + Last_EN_NoiseFilter = EN_NoiseFilter; + + if (g_ts_dbg != 0) + I("%s: end!\n", __func__); +} +/* end report_points*/ + +int himax_report_data(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + if (g_ts_dbg != 0) + I("%s: Entering, ts_status=%d!\n", __func__, ts_status); + + if (ts_path == HX_REPORT_COORD || ts_path == HX_REPORT_COORD_RAWDATA) { + /* Touch Point information */ + himax_report_points(ts); + +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) + /*report key(question mark)*/ + if (tpd_key && tpd_key_old) + himax_report_key(ts); + +#endif +#ifdef HX_SMART_WAKEUP + } else if (ts_path == HX_REPORT_SMWP_EVENT) { + himax_wake_event_report(); +#endif + } else { + E("%s:Fail Path!\n", __func__); + ts_status = HX_PATH_FAIL; + } + + if (g_ts_dbg != 0) + I("%s: END, ts_status=%d!\n", __func__, ts_status); + return ts_status; +} +/* end report_data */ + +static int himax_ts_operation(struct himax_ts_data *ts, int ts_path, int ts_status) +{ + uint8_t hw_reset_check[2]; + + memset(ts->xfer_buff, 0x00, 128 * sizeof(uint8_t)); + memset(hw_reset_check, 0x00, sizeof(hw_reset_check)); + + ts_status = himax_touch_get(ts, ts->xfer_buff, ts_path, ts_status); + if (ts_status == HX_TS_GET_DATA_FAIL) + goto END_FUNCTION; + + ts_status = himax_distribute_touch_data(ts->xfer_buff, ts_path, ts_status); + ts_status = himax_err_ctrl(ts, ts->xfer_buff, ts_path, ts_status); + if (ts_status == HX_REPORT_DATA || ts_status == HX_TS_NORMAL_END) + ts_status = himax_parse_report_data(ts, ts_path, ts_status); + else + goto END_FUNCTION; + + + ts_status = himax_report_data(ts, ts_path, ts_status); + + +END_FUNCTION: + return ts_status; +} + +#ifdef HIMAX_PALM_SENSOR_EN +static void himax_palm_sensor_release_timer_handler(unsigned long data) +{ + himax_palm_detect_func(private_ts, false); +} + +static int _himax_palm_detect_sensor_set_enable(unsigned int enable) +{ + I("Gesture set enable %d!", enable); +/* + * If palm detect function is enabled, interrupt will not disable, IC works in + * normal mode. But in case touch event is reported to input subsystem, skip + * touch event when suspend flag is true. So input subsystem will not take + * wakelock because no one report event. + * In this case, we still need read data from IC, so AP can not enter suspend. + */ + if (enable == 1) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock(&private_ts->palm_gesture_read_wakelock); +#else + __pm_stay_awake(&private_ts->palm_gesture_read_wakelock); +#endif + g_core_fp.fp_palm_detection_function(1); + private_ts->palm_detection_enabled = true; + } else if (enable == 0) { + if (timer_pending(&private_ts->palm_release_fimer)) { + himax_palm_detect_func(private_ts, false); + del_timer(&private_ts->palm_release_fimer); + } + g_core_fp.fp_palm_detection_function(0); + private_ts->palm_detection_enabled = false; +#ifdef CONFIG_HAS_WAKELOCK + wake_unlock(&private_ts->palm_gesture_read_wakelock); +#else + __pm_relax(&private_ts->palm_gesture_read_wakelock); +#endif + } else + E("unknown enable symbol\n"); + + return 0; +} + +static int himax_palm_detect_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + int ret = 0; + + mutex_lock(&private_ts->suspend_resume_mutex); + if (!private_ts->suspended) + ret = _himax_palm_detect_sensor_set_enable(enable); + else { + I("Gesture lazy set enable %d!", enable); + if (enable == 1) + private_ts->palm_detection_lazy_set = PALM_SENSOR_LAZY_SET_ENABLE; + else if (enable == 0) + private_ts->palm_detection_lazy_set = PALM_SENSOR_LAZY_SET_DISABLE; + else + E("unknown enable symbol\n"); + } + mutex_unlock(&private_ts->suspend_resume_mutex); + himax_palm_detect_func(private_ts, false); + + return ret; +} + +static int himax_palm_detect_sensor_init(struct himax_ts_data *data) +{ + struct himax_sensor_platform_data *sensor_pdata; + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + E("Failed to allocate device"); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct himax_sensor_platform_data), + GFP_KERNEL); + if (!sensor_pdata) { + E("Failed to allocate memory"); + goto free_sensor_pdata; + } + data->palm_sensor_pdata = sensor_pdata; + + __set_bit(EV_ABS, sensor_input_dev->evbit); + __set_bit(EV_SYN, sensor_input_dev->evbit); + input_set_abs_params(sensor_input_dev, ABS_DISTANCE, + 0, 5, 0, 0); + sensor_input_dev->name = "palm_detect"; + data->palm_sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + E("Unable to register device, err=%d", err); + goto free_sensor_input_dev; + } + + sensor_pdata->ps_cdev = palm_sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = himax_palm_detect_sensor_set_enable; + sensor_pdata->data = data; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&data->palm_gesture_wakelock, WAKE_LOCK_SUSPEND, "palm_detect_wl"); +#else + wakeup_source_init(&data->palm_gesture_wakelock, "palm_detect_wl"); +#endif +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&data->palm_gesture_read_wakelock, WAKE_LOCK_SUSPEND, "palm_read_wl"); +#else + wakeup_source_init(&data->palm_gesture_read_wakelock, "palm_read_wl"); +#endif + + data->palm_release_fimer.function = himax_palm_sensor_release_timer_handler; + init_timer(&data->palm_release_fimer); + data->palm_release_delay_ms = 850; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(data->palm_sensor_pdata->input_sensor_dev); +free_sensor_input_dev: + input_free_device(data->palm_sensor_pdata->input_sensor_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, sensor_pdata); + data->palm_sensor_pdata= NULL; +exit: + return 1; +} + +int himax_palm_detect_sensor_remove(struct himax_ts_data *data) +{ + sensors_classdev_unregister(&data->palm_sensor_pdata->ps_cdev); + input_unregister_device(data->palm_sensor_pdata->input_sensor_dev); + devm_kfree(&data->palm_sensor_pdata->input_sensor_dev->dev, + data->palm_sensor_pdata); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&data->palm_gesture_wakelock); +#else + wakeup_source_trash(&data->palm_gesture_wakelock); +#endif +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&data->palm_gesture_read_wakelock); +#else + wakeup_source_trash(&data->palm_gesture_read_wakelock); +#endif + data->palm_sensor_pdata = NULL; + data->palm_detection_enabled = false; + return 0; +} +#endif + +#ifdef HIMAX_V2_SENSOR_EN +static int himax_tap_detect_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + int ret = 0; + struct himax_ts_data *ts = private_ts; + + if (enable) { + ts->SMWP_enable = 1; + ts->gesture_cust_en[0] = 1; + I("%s,here:%d, gesture_cust_en[0] = %d\n", __func__, __LINE__, ts->gesture_cust_en[0]); + } else { + ts->SMWP_enable = 0; + ts->gesture_cust_en[0] = 0; + I("%s,here:%d, gesture_cust_en[0] = %d\n", __func__, __LINE__, ts->gesture_cust_en[0]); + } + + g_core_fp.fp_set_SMWP_enable(ts->SMWP_enable, ts->suspended); + HX_SMWP_EN = ts->SMWP_enable; + I("%s: SMART_WAKEUP_enable = %d.\n", __func__, HX_SMWP_EN); + + return ret; +} + +static int himax_tap_detect_sensor_init(struct himax_ts_data *data) +{ + struct himax_tap_sensor_platform_data *sensor_pdata; + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + E("Failed to allocate device"); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct himax_tap_sensor_platform_data), + GFP_KERNEL); + if (!sensor_pdata) { + E("Failed to allocate memory"); + goto free_sensor_pdata; + } + data->sensor_pdata = sensor_pdata; + + __set_bit(EV_ABS, sensor_input_dev->evbit); + __set_bit(EV_SYN, sensor_input_dev->evbit); + input_set_abs_params(sensor_input_dev, ABS_DISTANCE, + 0, REPORT_MAX_COUNT, 0, 0); + sensor_input_dev->name = "double-tap"; + data->sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + E("Unable to register device, err=%d", err); + goto free_sensor_input_dev; + } + + sensor_pdata->ps_cdev = sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = himax_tap_detect_sensor_set_enable; + sensor_pdata->data = data; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(data->sensor_pdata->input_sensor_dev); +free_sensor_input_dev: + input_free_device(data->sensor_pdata->input_sensor_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, sensor_pdata); + data->sensor_pdata= NULL; +exit: + return 1; +} + +int himax_tap_detect_sensor_remove(struct himax_ts_data *data) +{ + sensors_classdev_unregister(&data->sensor_pdata->ps_cdev); + input_unregister_device(data->sensor_pdata->input_sensor_dev); + devm_kfree(&data->sensor_pdata->input_sensor_dev->dev, + data->sensor_pdata); + data->sensor_pdata = NULL; + return 0; +} +#endif + +void himax_ts_work(struct himax_ts_data *ts) +{ + + int ts_status = HX_TS_NORMAL_END; + int ts_path = 0; + + if (debug_data != NULL) + debug_data->fp_ts_dbg_func(ts, HX_FINGER_ON); + +#if defined(HX_USB_DETECT_GLOBAL) + himax_cable_detect_func(false); +#endif + + ts_path = himax_ts_work_status(ts); + switch (ts_path) { + case HX_REPORT_COORD: + ts_status = himax_ts_operation(ts, ts_path, ts_status); + break; + case HX_REPORT_SMWP_EVENT: + ts_status = himax_ts_operation(ts, ts_path, ts_status); + break; + case HX_REPORT_COORD_RAWDATA: + ts_status = himax_ts_operation(ts, ts_path, ts_status); + break; + default: + E("%s:Path Fault! value=%d\n", __func__, ts_path); + goto END_FUNCTION; + } + + if (ts_status == HX_TS_GET_DATA_FAIL) + goto GET_TOUCH_FAIL; + else + goto END_FUNCTION; + +GET_TOUCH_FAIL: + I("%s: Now reset the Touch chip.\n", __func__); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, true); +#else + g_core_fp.fp_system_reset(); +#endif +#if defined(HX_ZERO_FLASH) && defined(HX83102D) + if (g_core_fp.fp_0f_reload_to_active) + g_core_fp.fp_0f_reload_to_active(); +#endif +END_FUNCTION: + if (debug_data != NULL) + debug_data->fp_ts_dbg_func(ts, HX_FINGER_LEAVE); + +} +/*end ts_work*/ +enum hrtimer_restart himax_ts_timer_func(struct hrtimer *timer) +{ + struct himax_ts_data *ts; + + + ts = container_of(timer, struct himax_ts_data, timer); + queue_work(ts->himax_wq, &ts->work); + hrtimer_start(&ts->timer, ktime_set(0, 12500000), HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +#if defined(HX_USB_DETECT_CALLBACK) +static void himax_cable_tp_status_handler_func(int connect_status) +{ + struct himax_ts_data *ts; + + I("Touch: cable change to %d\n", connect_status); + + ts = private_ts; + + if (ts->cable_config) { + if (!atomic_read(&ts->suspend_mode)) { + if (connect_status != ts->usb_connected) { + if (connect_status) { + ts->cable_config[1] = 0x01; + ts->usb_connected = 0x01; + } else { + ts->cable_config[1] = 0x00; + ts->usb_connected = 0x00; + } + + himax_bus_master_write(ts->cable_config, + sizeof(ts->cable_config), HIMAX_I2C_RETRY_TIMES); + I("%s: Cable status change: 0x%2.2X\n", __func__, ts->cable_config[1]); + } else + I("%s: Cable status is the same as previous one, ignore.\n", __func__); + } else { + if (connect_status) + ts->usb_connected = 0x01; + else + ts->usb_connected = 0x00; + + I("%s: Cable status remembered: 0x%2.2X\n", __func__, ts->usb_connected); + } + } +} + +static struct t_cable_status_notifier himax_cable_status_handler = { + .name = "usb_tp_connected", + .func = himax_cable_tp_status_handler_func, +}; + +#endif + +#ifdef HX_AUTO_UPDATE_FW +static void himax_update_register(struct work_struct *work) +{ + I(" %s in\n", __func__); + + if (i_get_FW() != 0) + return; + + if (g_auto_update_flag == true) { + I("Update FW Directly"); + goto UPDATE_FW; + } + + if (himax_auto_update_check() != 0) { + I("%s:Don't run auto update fw, so free allocated!\n", __func__); + kfree(i_CTPM_FW); + i_CTPM_FW = NULL; + return; + } + +UPDATE_FW: + if (i_update_FW() <= 0) + I("Auto update FW fail!\n"); + else + I("It have Updated\n"); + +} +#endif + +#ifndef HX_ZERO_FLASH +static int hx_chk_flash_sts(void) +{ + int rslt = 0; + uint32_t size = 0; + + I("%s: Entering\n", __func__); + if (g_core_fp._diff_overlay_flash() == 1) + size = FW_SIZE_128k; + else + size = FW_SIZE_64k; + + +#ifdef HX_AUTO_UPDATE_FW + g_auto_update_flag = (!g_core_fp.fp_calculateChecksum(false, size)); + g_auto_update_flag |= g_core_fp.fp_flash_lastdata_check(size); +#else + g_core_fp.fp_calculateChecksum(false, size); + g_core_fp.fp_flash_lastdata_check(size); +#endif + + return rslt; +} +#endif + +#if defined(CONFIG_DRM) +extern struct drm_panel *himax_active_panel; +static void himax_fb_register(struct work_struct *work) +{ + int ret = 0; + + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work_att.work); + + + I("%s in\n", __func__); + ts->fb_notif.notifier_call = drm_notifier_callback; + if (himax_active_panel) + ret = drm_panel_notifier_register(himax_active_panel, &ts->fb_notif); + + if (ret) + E("Unable to register fb_notifier: %d\n", ret); +} +#elif defined(CONFIG_FB) +static void himax_fb_register(struct work_struct *work) +{ + int ret = 0; + + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work_att.work); + + + I("%s in\n", __func__); + ts->fb_notif.notifier_call = fb_notifier_callback; + ret = fb_register_client(&ts->fb_notif); + + if (ret) + E("Unable to register fb_notifier: %d\n", ret); +} +#endif + +#include +#include + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!ts) { + pr_err("cannot get himax_ts_data pointer\n"); + return (ssize_t)0; + } + path = kobject_get_path(&ts->dev->kobj, GFP_KERNEL); + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "himax"); +} + +/* Attribute: vendor (RO) */ +static ssize_t ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + int buildid; + + buildid = (ic_data->vendor_cid_maj_ver << 8 | + ic_data->vendor_cid_min_ver); + return scnprintf(buf, PAGE_SIZE, "%s%s\n%s%04x\n%s%04x\n", + "Product ID: ", ts->chip_name, + "Build ID: ", buildid ? buildid : ts->build_id, + "Config ID: ", ic_data->vendor_touch_cfg_ver ? ic_data->vendor_touch_cfg_ver : ts->config_id); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_RO(ic_ver), + __ATTR_NULL +}; + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +static int himax_sysfs_touchscreen( + struct himax_ts_data *ts, bool create) +{ + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + static struct class *touchscreen_class; + static struct device *ts_class_dev; + static int minor; + + if (create) { + minor = input_get_new_minor(0x90, + 1, false); + if (minor < 0) + minor = input_get_new_minor(TSDEV_MINOR_BASE, + TSDEV_MINOR_MAX, true); + pr_info("assigned minor %d\n", minor); + + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + + ts_class_dev = device_create(touchscreen_class, NULL, + MKDEV(INPUT_MAJOR, minor), + ts, "%s", ts->chip_name); + if (IS_ERR(ts_class_dev)) { + error = PTR_ERR(ts_class_dev); + ts_class_dev = NULL; + return error; + } + for (i = 0; attrs[i].attr.name != NULL; ++i) { + error = device_create_file(ts_class_dev, &attrs[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + } else { + if (!touchscreen_class || !ts_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(ts_class_dev, &attrs[i]); + + device_unregister(ts_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor)); + ts_class_dev = NULL; + class_unregister(touchscreen_class); + pr_err("error creating touchscreen class\n"); + + return -ENODEV; +} + +#if defined(HX_USB_DETECT_GLOBAL) +static int charger_notifier_callback(struct notifier_block *nb, + unsigned long val, void *v) { + int ret = 0; + struct power_supply *psy = NULL; + struct himax_ts_data *ts = container_of(nb, struct himax_ts_data, charger_notif); + union power_supply_propval prop; + + psy= power_supply_get_by_name("usb"); + if (!psy) { + E("Couldn't get usbpsy\n"); + return -EINVAL; + } + if (!strcmp(psy->desc->name, "usb")) { + if (psy && ts && val == POWER_SUPPLY_PROP_STATUS) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,&prop); + if (ret < 0) { + E("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret); + return ret; + } else { + USB_detect_flag = prop.intval; + //E("usb prop.intval =%d\n", prop.intval); + } + } + } + return 0; +} +#endif + +#if defined(__HIMAX_HX83102D_MOD__) +#define TP_CHECK_TIMES 30 +#define TP_CHECK_INTERVAL 10 +void himax_resume_thread_check(void) +{ + uint8_t cycle = 0; + + while (true) { + if(cycle++ > TP_CHECK_TIMES) + break; + + if (atomic_read(&private_ts->resume_thread_is_runing)) + msleep(TP_CHECK_INTERVAL); + else + break; + } + I("cycle:%d",cycle); + return; +} +#endif + +#if defined(HX_RESUME_SET_FW) +void himax_resume_work_func(struct work_struct *work) +{ + struct himax_ts_data *ts = private_ts; +#if defined(__HIMAX_HX83102D_MOD__) + atomic_set(&private_ts->resume_thread_is_runing, 1); +#endif + himax_chip_common_resume(ts); +#if defined(__HIMAX_HX83102D_MOD__) + atomic_set(&private_ts->resume_thread_is_runing, 0); +#endif + return; +} +#endif +int himax_chip_common_init(void) +{ + + int i = 0, ret = 0, err = 0, idx = 0; + struct himax_ts_data *ts = private_ts; + struct himax_i2c_platform_data *pdata; + struct himax_chip_entry *entry; + + I("Prepare kernel fp\n"); + kp_getname_kernel = (void *)kallsyms_lookup_name("getname_kernel"); + if (!kp_getname_kernel) { + E("prepare kp_getname_kernel failed!\n"); + /*goto err_dt_platform_data_fail;*/ + } + kp_putname_kernel = (void *)kallsyms_lookup_name("putname"); + if (!kp_putname_kernel) { + E("prepare kp_putname_kernel failed!\n"); + /*goto err_xfer_buff_fail;*/ + } + kp_file_open_name = (void *)kallsyms_lookup_name("file_open_name"); + if (!kp_file_open_name) { + E("prepare kp_file_open_name failed!\n"); + goto err_dt_platform_data_fail; + } +#if defined(__EMBEDDED_FW__) + g_embedded_fw.size = (size_t)_binary___Himax_firmware_bin_end - + (size_t)_binary___Himax_firmware_bin_start; +#endif + ts->xfer_buff = devm_kzalloc(ts->dev, 128 * sizeof(uint8_t), GFP_KERNEL); + if (ts->xfer_buff == NULL) { + err = -ENOMEM; + goto err_dt_platform_data_fail; + } + + I("PDATA START\n"); + pdata = kzalloc(sizeof(struct himax_i2c_platform_data), GFP_KERNEL); + + if (pdata == NULL) { /*Allocate Platform data space*/ + err = -ENOMEM; + goto err_dt_platform_data_fail; + } + + I("ic_data START\n"); + ic_data = kzalloc(sizeof(struct himax_ic_data), GFP_KERNEL); + if (ic_data == NULL) { /*Allocate IC data space*/ + err = -ENOMEM; + goto err_dt_ic_data_fail; + } + + /* allocate report data */ + hx_touch_data = kzalloc(sizeof(struct himax_report_data), GFP_KERNEL); + if (hx_touch_data == NULL) { + err = -ENOMEM; + goto err_alloc_touch_data_failed; + } + + if (himax_parse_dt(ts, pdata) < 0) { + I(" pdata is NULL for DT\n"); + goto err_alloc_dt_pdata_failed; + } + +#ifdef HX_RST_PIN_FUNC + ts->rst_gpio = pdata->gpio_reset; +#endif + if (himax_gpio_power_config(pdata)) { + err = -1; + E("%s: himax_gpio_power_config failed, stop Himax probe\n", __func__); + goto err_himax_gpio_power_config_failed; + } +#ifndef CONFIG_OF + + if (pdata->power) { + ret = pdata->power(1); + + if (ret < 0) { + E("%s: power on failed\n", __func__); + goto err_power_failed; + } + } + +#endif + + i_CTPM_firmware_name = kzalloc(HIMAX_FILE_NAME_LENGTH, GFP_KERNEL); + if (!i_CTPM_firmware_name) { + E("%s: alloc i_CTPM_firmware_name failed\n", __func__); + goto firmware_name_alloc_failed; + } + if (pdata->panel_supplier[0]) + snprintf(i_CTPM_firmware_name, HIMAX_FILE_NAME_LENGTH, "%s_Himax_firmware.bin", + pdata->panel_supplier); + else + snprintf(i_CTPM_firmware_name, HIMAX_FILE_NAME_LENGTH, "Himax_firmware.bin"); + + g_hx_chip_inited = 0; + idx = himax_get_ksym_idx(); + if (idx >= 0) { + if (isEmpty(idx) != 0) { + I("%s: no chip registered, please insmod ic.ko!\n", + __func__); + goto error_ic_detect_failed; + } + entry = get_chip_entry_by_index(idx); + + for (i = 0; i < entry->hx_ic_dt_num; i++) { + if (entry->core_chip_dt[i].fp_chip_detect != NULL) { + if (entry->core_chip_dt[i].fp_chip_detect() + == true) { + I("%s: chip detect found! list_num=%d\n" + , __func__, i); + goto found_hx_chip; + } else { + I("%s: num=%d chip detect NOT found! goto Next\n", __func__, i); + continue; + } + } + } + } else { + I("%s: No available chip exist!\n", __func__); + goto error_ic_detect_failed; + } + + if (i == entry->hx_ic_dt_num) { + E("%s: chip detect failed!\n", __func__); + goto error_ic_detect_failed; + } +found_hx_chip: + if (g_core_fp.fp_chip_init != NULL) { + g_core_fp.fp_chip_init(); + } else { + E("%s: function point of chip_init is NULL!\n", __func__); + goto error_ic_detect_failed; + } + + if (pdata->virtual_key) + ts->button = pdata->virtual_key; + +#ifndef HX_ZERO_FLASH + g_core_fp.fp_read_FW_ver(); + hx_chk_flash_sts(); +#endif + +#ifdef HX_AUTO_UPDATE_FW + if (g_auto_update_flag) + goto FW_force_upgrade; +#endif + + +#ifdef HX_AUTO_UPDATE_FW +FW_force_upgrade: + ts->himax_update_wq = create_singlethread_workqueue("HMX_update_reuqest"); + if (!ts->himax_update_wq) { + E(" allocate himax_update_wq failed\n"); + err = -ENOMEM; + goto err_update_wq_failed; + } + INIT_DELAYED_WORK(&ts->work_update, himax_update_register); + queue_delayed_work(ts->himax_update_wq, &ts->work_update, msecs_to_jiffies(2000)); +#endif +#ifdef HX_ZERO_FLASH + g_auto_update_flag = true; + ts->himax_0f_update_wq = create_singlethread_workqueue("HMX_0f_update_reuqest"); + if (!ts->himax_0f_update_wq) { + E(" allocate himax_0f_update_wq failed\n"); + err = -ENOMEM; + goto err_0f_update_wq_failed; + } + INIT_DELAYED_WORK(&ts->work_0f_update, g_core_fp.fp_0f_operation); + queue_delayed_work(ts->himax_0f_update_wq, &ts->work_0f_update, msecs_to_jiffies(2000)); +#endif + +#ifdef HX_RESUME_SET_FW + ts->ts_int_workqueue = create_singlethread_workqueue("himax_ts_resume_wq"); + if (!ts->ts_int_workqueue) { + E("%s: create ts_resume workqueue failed\n", __func__); + goto err_create_ts_resume_wq_failed; + } +#if defined(__HIMAX_HX83102D_MOD__) + INIT_DELAYED_WORK(&ts->ts_int_work, himax_resume_work_func); +#else + INIT_WORK(&ts->ts_int_work, himax_resume_work_func); +#endif +#endif + /*Himax Power On and Load Config*/ + if (himax_loadSensorConfig(pdata)) { + E("%s: Load Sesnsor configuration failed, unload driver.\n", __func__); + goto err_detect_failed; + } + + g_core_fp.fp_power_on_init(); + calculate_point_number(); + +#ifdef CONFIG_OF + ts->power = pdata->power; +#endif + ts->pdata = pdata; + /*calculate the i2c data size*/ + calcDataSize(); + I("%s: calcDataSize complete\n", __func__); +#ifdef CONFIG_OF + ts->pdata->abs_pressure_min = 0; + ts->pdata->abs_pressure_max = 200; + ts->pdata->abs_width_min = 0; + ts->pdata->abs_width_max = 200; + pdata->cable_config[0] = 0xF0; + pdata->cable_config[1] = 0x00; +#endif + ts->suspended = false; + atomic_set(&ts->resume_update_fail, 0); + atomic_set(&ts->resume_thread_is_runing, 0); + mutex_init(&ts->suspend_resume_mutex); +#if defined(HX_USB_DETECT_CALLBACK) || defined(HX_USB_DETECT_GLOBAL) + ts->usb_connected = 0x00; + ts->cable_config = pdata->cable_config; + ts->charger_notif.notifier_call = charger_notifier_callback; + ret = power_supply_reg_notifier(&ts->charger_notif); + if (ret) { + E("Unable to register charger_notifier: %d\n",ret); + goto err_register_charger_notify_failed; + } +#endif +#ifdef HX_PROTOCOL_A + ts->protocol_type = PROTOCOL_TYPE_A; +#else + ts->protocol_type = PROTOCOL_TYPE_B; +#endif + I("%s: Use Protocol Type %c\n", __func__, + ts->protocol_type == PROTOCOL_TYPE_A ? 'A' : 'B'); + ret = himax_input_register(ts); + + if (ret) { + E("%s: Unable to register %s input device\n", + __func__, ts->input_dev->name); + goto err_input_register_device_failed; + } + + ts->initialized = true; +#if defined(CONFIG_FB) || defined(CONFIG_DRM) + ts->himax_att_wq = create_singlethread_workqueue("HMX_ATT_reuqest"); + + if (!ts->himax_att_wq) { + E(" allocate himax_att_wq failed\n"); + err = -ENOMEM; + goto err_get_intr_bit_failed; + } + + INIT_DELAYED_WORK(&ts->work_att, himax_fb_register); + queue_delayed_work(ts->himax_att_wq, &ts->work_att, msecs_to_jiffies(2500)); +#endif + +#ifdef HX_SMART_WAKEUP + ts->SMWP_enable = 0; + wakeup_source_init(&ts->ts_SMWP_wake_lock, HIMAX_common_NAME); +#endif +#ifdef HX_HIGH_SENSE + ts->HSEN_enable = 0; +#endif + + /*touch data init*/ + err = himax_report_data_init(); + + if (err) + goto err_report_data_init_failed; + + if (himax_common_proc_init()) { + E(" %s: himax_common proc_init failed!\n", __func__); + goto err_creat_proc_file_failed; + } + himax_sysfs_touchscreen(ts, true); + +#if defined(HX_USB_DETECT_CALLBACK) + + if (ts->cable_config) + cable_detect_register_notifier(&himax_cable_status_handler); + +#endif + +#ifdef HIMAX_PALM_SENSOR_EN + if (himax_palm_detect_sensor_init(ts)) { + E(" %s: himax_palm_detect_sensor_init failed!\n", __func__); + goto err_create_palm_sensor_failed; + } +#endif + +#ifdef HIMAX_V2_SENSOR_EN + if (himax_tap_detect_sensor_init(ts)) { + E(" %s: himax_tap_detect_sensor_init failed!\n", __func__); + goto err_create_tap_sensor_failed; + } +#endif + + himax_ts_register_interrupt(); + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_DEBUG + if (himax_debug_init()) + E(" %s: debug initial failed!\n", __func__); +#endif + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) + + if (g_auto_update_flag) + himax_int_enable(0); + +#endif + g_hx_chip_inited = true; + return 0; + +#ifdef HIMAX_V2_SENSOR_EN +err_create_tap_sensor_failed: +#endif +#ifdef HIMAX_PALM_SENSOR_EN +err_create_palm_sensor_failed: +#endif +err_creat_proc_file_failed: + himax_report_data_deinit(); +err_report_data_init_failed: +#ifdef HX_SMART_WAKEUP + wakeup_source_trash(&ts->ts_SMWP_wake_lock); +#endif +#if defined(CONFIG_FB) || defined(CONFIG_DRM) + cancel_delayed_work_sync(&ts->work_att); + destroy_workqueue(ts->himax_att_wq); +err_get_intr_bit_failed: +#endif +err_input_register_device_failed: + input_free_device(ts->input_dev); +err_detect_failed: +#ifdef HX_RESUME_SET_FW +#if defined(__HIMAX_HX83102D_MOD__) + cancel_delayed_work_sync(&ts->ts_int_work); +#else + cancel_work_sync(&ts->ts_int_work); +#endif + destroy_workqueue(ts->ts_int_workqueue); +err_create_ts_resume_wq_failed: +#endif +#ifdef HX_ZERO_FLASH + cancel_delayed_work_sync(&ts->work_0f_update); + destroy_workqueue(ts->himax_0f_update_wq); +err_0f_update_wq_failed: +#endif +#ifdef HX_AUTO_UPDATE_FW + if (g_auto_update_flag) { + cancel_delayed_work_sync(&ts->work_update); + destroy_workqueue(ts->himax_update_wq); + } +err_update_wq_failed: +#endif +#if defined(HX_USB_DETECT_GLOBAL) +err_register_charger_notify_failed: + if (ts->charger_notif.notifier_call) + power_supply_unreg_notifier(&ts->charger_notif); +#endif +error_ic_detect_failed: + himax_gpio_power_deconfig(pdata); +firmware_name_alloc_failed: +#ifndef CONFIG_OF +err_power_failed: +#endif +err_himax_gpio_power_config_failed: +err_alloc_dt_pdata_failed: + kfree(hx_touch_data); + hx_touch_data = NULL; +err_alloc_touch_data_failed: + kfree(ic_data); + ic_data = NULL; +err_dt_ic_data_fail: + kfree(pdata); + pdata = NULL; +err_dt_platform_data_fail: + probe_fail_flag = 1; + return err; +} + +void himax_chip_common_deinit(void) +{ + struct himax_ts_data *ts = private_ts; + + himax_ts_unregister_interrupt(); + + himax_sysfs_touchscreen(ts, false); + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + himax_inspect_data_clear(); +#endif + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_DEBUG + himax_debug_remove(); +#endif + + himax_common_proc_deinit(); + himax_report_data_deinit(); + +#ifdef HX_SMART_WAKEUP + wakeup_source_trash(&ts->ts_SMWP_wake_lock); +#endif +#if defined(CONFIG_DRM) + if (himax_active_panel) + if (drm_panel_notifier_unregister(himax_active_panel, &ts->fb_notif)) + E("Error occurred while unregistering drm_notifier.\n"); + cancel_delayed_work_sync(&ts->work_att); + destroy_workqueue(ts->himax_att_wq); +#elif defined(CONFIG_FB) + if (fb_unregister_client(&ts->fb_notif)) + E("Error occurred while unregistering fb_notifier.\n"); + cancel_delayed_work_sync(&ts->work_att); + destroy_workqueue(ts->himax_att_wq); +#endif + input_free_device(ts->input_dev); +#ifdef HX_ZERO_FLASH + cancel_delayed_work_sync(&ts->work_0f_update); + destroy_workqueue(ts->himax_0f_update_wq); +#endif +#ifdef HX_AUTO_UPDATE_FW + cancel_delayed_work_sync(&ts->work_update); + destroy_workqueue(ts->himax_update_wq); +#endif + himax_gpio_power_deconfig(ts->pdata); + if (himax_mcu_cmd_struct_free) + himax_mcu_cmd_struct_free(); + + kfree(hx_touch_data); + hx_touch_data = NULL; + kfree(ic_data); + ic_data = NULL; + kfree(ts->pdata->virtual_key); + ts->pdata->virtual_key = NULL; + kfree(ts->pdata); + ts->pdata = NULL; + kfree(ts); + ts = NULL; + probe_fail_flag = 0; + + I("%s: Common section deinited!\n", __func__); +} + +int _himax_chip_common_suspend(struct himax_ts_data *ts) +{ + if (ts->suspended) { + I("%s: Already suspended. Skipped.\n", __func__); + goto END; + } else { + ts->suspended = true; + I("%s: enter\n", __func__); + } + +#ifdef HIMAX_PALM_SENSOR_EN + if (ts->palm_detection_enabled) { + himax_report_all_leave_event(ts); + atomic_set(&ts->suspend_mode, 1); + I("[himax] %s: palm detect function enable, reject suspend\n", __func__); + goto END; + } +#endif + + if (debug_data != NULL && debug_data->flash_dump_going == true) { + I("[himax] %s: Flash dump is going, reject suspend\n", __func__); + goto END; + } + +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) +#ifndef HX_RESUME_SEND_CMD + g_core_fp.fp_resend_cmd_func(ts->suspended); +#endif +#endif +#ifdef HX_SMART_WAKEUP + + if (ts->SMWP_enable) { + +#ifdef HX_CODE_OVERLAY + if (ts->in_self_test == 0) + g_core_fp.fp_0f_overlay(2, 0); +#endif + + atomic_set(&ts->suspend_mode, 1); + ts->pre_finger_mask = 0; + FAKE_POWER_KEY_SEND = false; + I("[himax] %s: SMART_WAKEUP enable, reject suspend\n", __func__); + goto END; + } + +#endif + himax_int_enable(0); + himax_report_all_leave_event(ts); + /*if (g_core_fp.fp_suspend_ic_action != NULL)*/ + /*g_core_fp.fp_suspend_ic_action();*/ + + if (!ts->use_irq) { + int32_t cancel_state; + + cancel_state = cancel_work_sync(&ts->work); + if (cancel_state) + himax_int_enable(1); + } + + /*ts->first_pressed = 0;*/ + atomic_set(&ts->suspend_mode, 1); + ts->pre_finger_mask = 0; + + if (ts->pdata) + if (ts->pdata->powerOff3V3 && ts->pdata->power) + ts->pdata->power(0); + +END: + if (ts->in_self_test == 1) + ts->suspend_resume_done = 1; + + I("%s: END\n", __func__); + + return 0; +} + +int himax_chip_common_suspend(struct himax_ts_data *ts) +{ + int ret = 0; +#if defined(__HIMAX_HX83102D_MOD__) + himax_resume_thread_check(); +#endif + mutex_lock(&ts->suspend_resume_mutex); + ret = _himax_chip_common_suspend(ts); + mutex_unlock(&ts->suspend_resume_mutex); + + return ret; +} + +int _himax_chip_common_resume(struct himax_ts_data *ts) +{ +#if defined(HX_ZERO_FLASH) && defined(HX_RESUME_SET_FW) + int result = 0; +#endif +#if defined(__HIMAX_HX83102D_MOD__) + uint8_t tmp_data[DATA_LEN_4] = {0}; +#endif + I("%s: enter\n", __func__); + +#if defined(__HIMAX_HX83102D_MOD__) + if (atomic_read(&ts->resume_update_fail)) { + I("%s:Last time update Fail!\n", __func__); + atomic_set(&ts->resume_update_fail, 0); + ts->suspended = false; + goto START_RESUME; + } +#endif + if (ts->suspended == false) { + I("%s: It had entered resume, skip this step\n", __func__); + goto END; + } else { + ts->suspended = false; + } +#if defined(__HIMAX_HX83102D_MOD__) +START_RESUME: +#endif + +#ifdef HX_ESD_RECOVERY + /* continuous N times record, not total N times. */ + g_zero_event_count = 0; +#endif + + atomic_set(&ts->suspend_mode, 0); + ts->diag_cmd = 0; +#ifdef HIMAX_PALM_SENSOR_EN + if (ts->palm_detection_enabled) { + I("[himax] %s: palm detect function enable, skip resume\n", __func__); + goto CHECK_LAZY_SET_END; + } +#endif + + if (ts->pdata) + if (ts->pdata->powerOff3V3 && ts->pdata->power) + ts->pdata->power(1); + +#ifndef HX_RESUME_SET_FW +#if defined(HX_RST_PIN_FUNC) && defined(HX_RESUME_HW_RESET) + if (g_core_fp.fp_ic_reset != NULL) + g_core_fp.fp_ic_reset(false, false); +#endif +#endif + +#if defined(HX_ZERO_FLASH) && defined(HX_RESUME_SET_FW) +#ifdef HX_SMART_WAKEUP + if (!ts->SMWP_enable) { +#endif + I("It will update fw after resume in zero flash mode!\n"); + if (g_core_fp.fp_0f_operation_dirly != NULL) { + result = g_core_fp.fp_0f_operation_dirly(); + if (result) { + E("Something is wrong! Skip Update with zero flash!\n"); +#if defined(__HIMAX_HX83102D_MOD__) + atomic_set(&ts->resume_update_fail, 1); +#endif + goto ESCAPE_0F_UPDATE; + } + } + if (g_core_fp.fp_reload_disable != NULL) + g_core_fp.fp_reload_disable(0); + if (g_core_fp.fp_sense_on != NULL) + g_core_fp.fp_sense_on(0x00); +#if defined(__HIMAX_HX83102D_MOD__) + g_core_fp.fp_register_read(pzf_op->addr_sts_chk, DATA_LEN_4, tmp_data, 0); + if (tmp_data[0] != 0x05) + atomic_set(&ts->resume_update_fail, 1); +#endif +#ifdef HX_SMART_WAKEUP + } +#endif +#endif +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) + if (g_core_fp.fp_resend_cmd_func != NULL) + g_core_fp.fp_resend_cmd_func(ts->suspended); + +#ifdef HX_CODE_OVERLAY + if (ts->SMWP_enable && ts->in_self_test == 0) + g_core_fp.fp_0f_overlay(3, 0); +#endif +#endif + himax_report_all_leave_event(ts); + + if (g_core_fp.fp_sense_on != NULL) + g_core_fp.fp_resume_ic_action(); + himax_int_enable(1); +#if defined(HX_ZERO_FLASH) && defined(HX_RESUME_SET_FW) +ESCAPE_0F_UPDATE: +#endif + +#ifdef HIMAX_PALM_SENSOR_EN +CHECK_LAZY_SET_END: + if (ts->palm_detection_lazy_set != PALM_SENSOR_LAZY_SET_NONE) { + _himax_palm_detect_sensor_set_enable( + (ts->palm_detection_lazy_set == PALM_SENSOR_LAZY_SET_ENABLE) ? 1 : 0); + I("%s: Palm sensor lazy set done, clear flag.\n", __func__); + ts->palm_detection_lazy_set = PALM_SENSOR_LAZY_SET_NONE; + } +#endif +END: + if (ts->in_self_test == 1) + ts->suspend_resume_done = 1; + + I("%s: END\n", __func__); + return 0; +} + +int himax_chip_common_resume(struct himax_ts_data *ts) +{ + int ret = 0; + mutex_lock(&ts->suspend_resume_mutex); + ret = _himax_chip_common_resume(ts); + mutex_unlock(&ts->suspend_resume_mutex); + + return ret; +} + diff --git a/drivers/input/touchscreen/himax_v3_mmi/himax_common.h b/drivers/input/touchscreen/himax_v3_mmi/himax_common.h new file mode 100755 index 000000000000..b6474e78ff45 --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/himax_common.h @@ -0,0 +1,594 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for common functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef HIMAX_COMMON_H +#define HIMAX_COMMON_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "himax_platform.h" +#include +#ifdef HIMAX_PALM_SENSOR_EN +#include +#define SENSOR_TYPE_MOTO_TOUCH_PALM (SENSOR_TYPE_DEVICE_PRIVATE_BASE + 31) +#endif +#ifdef HIMAX_V2_SENSOR_EN +#include +#endif + +#if defined(CONFIG_DRM) + #include +#elif defined(CONFIG_FB) + #include + #include +#elif defined(CONFIG_HAS_EARLYSUSPEND) + #include +#endif + +#ifdef CONFIG_OF + #include +#endif + +#define HIMAX_DRIVER_VER "2.0.0.14_ABCD1234_01" + +#define FLASH_DUMP_FILE "/sdcard/HX_Flash_Dump.bin" + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG) + #define HX_TP_PROC_2T2R + /*#define HX_TP_SELF_TEST_DRIVER*/ /*if enable, selftest works in driver*/ +#endif + +#ifdef HIMAX_PALM_SENSOR_EN +struct himax_sensor_platform_data { + struct input_dev *input_sensor_dev; + struct sensors_classdev ps_cdev; + int sensor_opened; + char sensor_data; /* 0 near, 1 far */ + struct himax_ts_data *data; +}; + +enum palm_sensor_lazy_set { + PALM_SENSOR_LAZY_SET_NONE = 0, + PALM_SENSOR_LAZY_SET_ENABLE, + PALM_SENSOR_LAZY_SET_DISABLE, +}; +#endif + +#ifdef HIMAX_V2_SENSOR_EN +struct himax_tap_sensor_platform_data { + struct input_dev *input_sensor_dev; + struct sensors_classdev ps_cdev; + struct himax_ts_data *data; +}; +#endif + +/*===========Himax Option function=============*/ +#define HX_RST_PIN_FUNC +#define HX_RESUME_SEND_CMD +#define HX_ESD_RECOVERY +#define HX_TP_PROC_GUEST_INFO +/*#define HX_SHARP_ENABLE_PON*/ +/*#define HX_NEW_EVENT_STACK_FORMAT*/ +/*#define HX_AUTO_UPDATE_FW*/ +#ifdef HIMAX_V2_SENSOR_EN +#define HX_SMART_WAKEUP +#else +//#define HX_SMART_WAKEUP +#endif +/*#define HX_GESTURE_TRACK*/ +/*#define HX_HIGH_SENSE*/ +#ifdef HIMAX_PALM_SENSOR_EN +#define HX_PALM_REPORT +#else +/*#define HX_PALM_REPORT*/ +#endif +#define HX_USB_DETECT_GLOBAL +/*#define HX_USB_DETECT_CALLBACK*/ +/*#define HX_PROTOCOL_A*/ /* for MTK special platform.If turning on,it will report to system by using specific format. */ +#define HX_RESUME_HW_RESET +#define HX_PROTOCOL_B_3PA +#define HX_FIX_TOUCH_INFO /* if open, you need to change the touch info in the fix_touch_info*/ +/*#define HX_ZERO_FLASH*/ +#define HX_RESUME_SET_FW +/*#if defined(HX_ZERO_FLASH)*/ +/*#define HX_CODE_OVERLAY*/ +/*#define HX_EDGE_LIMIT*/ +/*#endif*/ +/*#define HX_EN_DYNAMIC_NAME*/ /* Support dynamic load fw name ,default is close */ +/*#undef CONFIG_FB*/ /* Enable it if driver go into suspend/resume twice */ +/*#define HX_PEN_FUNC_EN*/ + +/*#define HX_EN_SEL_BUTTON*/ /* Support Self Virtual key ,default is close*/ +/*#define HX_EN_MUT_BUTTON*/ /* Support Mutual Virtual Key ,default is close*/ + +#if defined(HX_EN_SEL_BUTTON) || defined(HX_EN_MUT_BUTTON) + /*#define HX_PLATFOME_DEFINE_KEY*/ /* for specfic platform to set key(button) */ +#endif +#ifdef HX_USB_DETECT_GLOBAL +#include +#endif + +#if defined(HX_SMART_WAKEUP) +/*#define HX_P_SENSOR*/ +#endif +/*=============================================*/ +#define HX_SPI_MTK_MAX_WRITE_SZ (256 - 4) + +#define HX_KEY_MAX_COUNT 4 +#define DEFAULT_RETRY_CNT 3 + +#define HX_85XX_A_SERIES_PWON "HX85xxA" +#define HX_85XX_B_SERIES_PWON "HX85xxB" +#define HX_85XX_C_SERIES_PWON "HX85xxC" +#define HX_85XX_D_SERIES_PWON "HX85xxD" +#define HX_85XX_E_SERIES_PWON "HX85xxE" +#define HX_85XX_ES_SERIES_PWON "HX85xxES" +#define HX_85XX_F_SERIES_PWON "HX85xxF" +#define HX_85XX_H_SERIES_PWON "HX85xxH" +#define HX_83100A_SERIES_PWON "HX83100A" +#define HX_83102A_SERIES_PWON "HX83102A" +#define HX_83102B_SERIES_PWON "HX83102B" +#define HX_83102D_SERIES_PWON "HX83102D" +#define HX_83102E_SERIES_PWON "HX83102E" +#define HX_83103A_SERIES_PWON "HX83103A" +#define HX_83106A_SERIES_PWON "HX83106A" +#define HX_83110A_SERIES_PWON "HX83110A" +#define HX_83110B_SERIES_PWON "HX83110B" +#define HX_83111B_SERIES_PWON "HX83111B" +#define HX_83112A_SERIES_PWON "HX83112A" +#define HX_83112B_SERIES_PWON "HX83112B" +#define HX_83113A_SERIES_PWON "HX83113A" +#define HX_83112D_SERIES_PWON "HX83112D" +#define HX_83112E_SERIES_PWON "HX83112E" +#define HX_83191A_SERIES_PWON "HX83191A" + +#define HX_TP_BIN_CHECKSUM_SW 1 +#define HX_TP_BIN_CHECKSUM_HW 2 +#define HX_TP_BIN_CHECKSUM_CRC 3 + +#define SHIFTBITS 5 + +#define FW_SIZE_32k 32768 +#define FW_SIZE_60k 61440 +#define FW_SIZE_64k 65536 +#define FW_SIZE_124k 126976 +#define FW_SIZE_128k 131072 + +#define NO_ERR 0 +#define READY_TO_SERVE 1 +#define WORK_OUT 2 +#define I2C_FAIL -1 +#define HX_INIT_FAIL -1 +#define MEM_ALLOC_FAIL -2 +#define CHECKSUM_FAIL -3 +#define GESTURE_DETECT_FAIL -4 +#define INPUT_REGISTER_FAIL -5 +#define FW_NOT_READY -6 +#define LENGTH_FAIL -7 +#define OPEN_FILE_FAIL -8 +#define ERR_WORK_OUT -10 +#define ERR_STS_WRONG -11 +#define ERR_TEST_FAIL -12 +#define HW_CRC_FAIL 1 + +#define HX_FINGER_ON 1 +#define HX_FINGER_LEAVE 2 + +#if defined(HX_PALM_REPORT) +#define PALM_REPORT 1 +#define PALM_LEAVE_REPORT 2 +#define NOT_REPORT -1 +#endif + +#if defined(HX_PEN_FUNC_EN) +#define PEN_INFO_SZ 12 +#endif + +#if defined(__EMBEDDED_FW__) +extern const uint8_t _binary___Himax_firmware_bin_start[]; +extern const uint8_t _binary___Himax_firmware_bin_end[]; +extern struct firmware g_embedded_fw; +#endif + +enum HX_TS_PATH { + HX_REPORT_COORD = 1, + HX_REPORT_SMWP_EVENT, + HX_REPORT_COORD_RAWDATA, +}; + +enum HX_TS_STATUS { + HX_TS_GET_DATA_FAIL = -4, + HX_ESD_EVENT, + HX_CHKSUM_FAIL, + HX_PATH_FAIL, + HX_TS_NORMAL_END = 0, + HX_ESD_REC_OK, + HX_READY_SERVE, + HX_REPORT_DATA, + HX_ESD_WARNING, + HX_IC_RUNNING, + HX_ZERO_EVENT_COUNT, + HX_RST_OK, +}; + +enum cell_type { + CHIP_IS_ON_CELL, + CHIP_IS_IN_CELL +}; +#ifdef HX_FIX_TOUCH_INFO +enum fix_touch_info { + FIX_HX_RX_NUM = 32, + FIX_HX_TX_NUM = 18, + FIX_HX_BT_NUM = 0, + FIX_HX_X_RES = 720, + FIX_HX_Y_RES = 1600, + FIX_HX_MAX_PT = 10, + FIX_HX_XY_REVERSE = false, + FIX_HX_INT_IS_EDGE = true, +#ifdef HX_TP_PROC_2T2R + FIX_HX_RX_NUM_2 = 36, + FIX_HX_TX_NUM_2 = 18, +#endif +}; +#endif + +#ifdef HX_ZERO_FLASH + #define HX_SPI_OPERATION + #define HX_0F_DEBUG +#endif +struct himax_ic_data { + int vendor_fw_ver; + int vendor_config_ver; + int vendor_touch_cfg_ver; + int vendor_display_cfg_ver; + int vendor_cid_maj_ver; + int vendor_cid_min_ver; + int vendor_panel_ver; + int vendor_sensor_id; +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#ifdef HX_EN_DYNAMIC_NAME + int vendor_ic_ver; + int vendor_old_ic_ver; + uint8_t vendor_semifac; +#endif +#endif + uint8_t vendor_cus_info[12]; + uint8_t vendor_proj_info[12]; + uint8_t vendor_ic_id[13]; + int HX_RX_NUM; + int HX_TX_NUM; + int HX_BT_NUM; + int HX_X_RES; + int HX_Y_RES; + int HX_MAX_PT; + bool HX_XY_REVERSE; + bool HX_INT_IS_EDGE; +#ifdef HX_TP_PROC_2T2R + int HX_RX_NUM_2; + int HX_TX_NUM_2; +#endif +}; + +struct himax_virtual_key { + int index; + int keycode; + int x_range_min; + int x_range_max; + int y_range_min; + int y_range_max; +}; + +struct himax_target_report_data { + int *x; + int *y; + int *w; + int *finger_id; + int finger_on; + int finger_num; +#ifdef HX_PLATFORM_DEFINE_KEY + int key_size; + int *key_x; + int *key_y; + int *key_w; +#endif +#ifdef HX_SMART_WAKEUP + int SMWP_event_chk; +#endif +#if defined(HX_PEN_FUNC_EN) + int32_t *p_x; + int32_t *p_y; + int32_t *p_w; + int32_t *pen_id; + uint32_t *p_hover; + int32_t *p_tilt_x; + uint32_t *p_btn; + uint32_t *p_btn2; + int32_t *p_tilt_y; + uint32_t *p_on; +#endif + int ig_count; + +}; + +struct himax_report_data { + int touch_all_size; + int raw_cnt_max; + int raw_cnt_rmd; + int touch_info_size; + uint8_t finger_num; + uint8_t finger_on; + uint8_t *hx_coord_buf; + uint8_t hx_state_info[2]; +#if defined(HX_SMART_WAKEUP) + int event_size; + uint8_t *hx_event_buf; +#endif + + int rawdata_size; + uint8_t diag_cmd; + uint8_t *hx_rawdata_buf; + uint8_t rawdata_frame_size; +}; + +struct himax_ts_data { + bool initialized; + bool suspended; + atomic_t resume_update_fail; + atomic_t resume_thread_is_runing; + atomic_t suspend_mode; + struct mutex suspend_resume_mutex; + uint8_t x_channel; + uint8_t y_channel; + uint8_t useScreenRes; + uint8_t diag_cmd; + char chip_name[30]; + uint8_t chip_cell_type; + + uint8_t protocol_type; + uint8_t first_pressed; + uint8_t coord_data_size; + uint8_t area_data_size; + uint8_t coordInfoSize; + uint8_t raw_data_frame_size; + uint8_t raw_data_nframes; + uint8_t nFinger_support; + uint8_t irq_enabled; + uint8_t diag_self[50]; + + uint16_t finger_pressed; + uint16_t last_slot; + uint16_t pre_finger_mask; + uint16_t old_finger; + int hx_point_num; + + + uint32_t debug_log_level; + uint32_t widthFactor; + uint32_t heightFactor; + uint32_t tw_x_min; + uint32_t tw_x_max; + uint32_t tw_y_min; + uint32_t tw_y_max; + uint32_t pl_x_min; + uint32_t pl_x_max; + uint32_t pl_y_min; + uint32_t pl_y_max; + + int rst_gpio; + int use_irq; + int (*power)(int on); + int pre_finger_data[10][2]; + bool vdd_1v8_always_on; + uint32_t build_id; + uint32_t config_id; + + struct device *dev; + struct workqueue_struct *himax_wq; + struct work_struct work; + struct input_dev *input_dev; +#if defined(HX_PEN_FUNC_EN) + struct input_dev *hx_pen_dev; +#endif + struct hrtimer timer; + struct i2c_client *client; + struct himax_i2c_platform_data *pdata; + struct himax_virtual_key *button; + struct mutex rw_lock; + +/******* SPI-start *******/ + struct mutex spi_lock; + struct spi_device *spi; + int hx_irq; + uint8_t *xfer_buff; +/******* SPI-end *******/ + + int in_self_test; + int suspend_resume_done; + int bus_speed; + +#if defined(CONFIG_FB) || defined(CONFIG_DRM) + struct notifier_block fb_notif; + struct workqueue_struct *himax_att_wq; + struct delayed_work work_att; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif + + struct workqueue_struct *flash_wq; + struct work_struct flash_work; + +#ifdef HX_AUTO_UPDATE_FW + struct workqueue_struct *himax_update_wq; + struct delayed_work work_update; +#endif + +#ifdef HX_ZERO_FLASH + struct workqueue_struct *himax_0f_update_wq; + struct delayed_work work_0f_update; +#endif +#ifdef HX_RESUME_SET_FW + struct workqueue_struct *ts_int_workqueue; +#if defined(__HIMAX_HX83102D_MOD__) + struct delayed_work ts_int_work; +#else + struct work_struct ts_int_work; +#endif +#endif + + struct workqueue_struct *himax_diag_wq; + struct delayed_work himax_diag_delay_wrok; + +#if defined (HX_SMART_WAKEUP) || defined (HX_CODE_OVERLAY) + uint8_t SMWP_enable; + uint8_t gesture_cust_en[26]; + struct wakeup_source ts_SMWP_wake_lock; +#ifdef HX_P_SENSOR + bool psensor_flag; +#endif +#endif + +#ifdef HX_EDGE_LIMIT + uint8_t edge_limit_enable; + uint8_t edge_limit_en[8]; +#endif + +#ifdef HX_HIGH_SENSE + uint8_t HSEN_enable; +#endif + +#if defined(HX_USB_DETECT_CALLBACK) || defined(HX_USB_DETECT_GLOBAL) + uint8_t usb_connected; + uint8_t *cable_config; + struct notifier_block charger_notif; +#endif + +#ifdef HX_TP_PROC_GUEST_INFO + struct workqueue_struct *guest_info_wq; + struct work_struct guest_info_work; +#endif + +#ifdef HIMAX_PALM_SENSOR_EN + bool palm_detection_enabled; + enum palm_sensor_lazy_set palm_detection_lazy_set; + struct himax_sensor_platform_data *palm_sensor_pdata; + struct timer_list palm_release_fimer; + unsigned int palm_release_delay_ms; +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock palm_gesture_wakelock; +#else + struct wakeup_source palm_gesture_wakelock; +#endif +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock palm_gesture_read_wakelock; +#else + struct wakeup_source palm_gesture_read_wakelock; +#endif +#endif + +#ifdef HIMAX_V2_SENSOR_EN + struct himax_tap_sensor_platform_data *sensor_pdata; +#endif + +}; + +struct himax_debug { + bool flash_dump_going; + void (*fp_ts_dbg_func)(struct himax_ts_data *ts, int start); + int (*fp_set_diag_cmd)(struct himax_ic_data *ic_data, struct himax_report_data *hx_touch_data); +}; + +enum input_protocol_type { + PROTOCOL_TYPE_A = 0x00, + PROTOCOL_TYPE_B = 0x01, +}; + +#ifdef HX_HIGH_SENSE + void himax_set_HSEN_func(uint8_t HSEN_enable); +#endif + +#ifdef HX_SMART_WAKEUP +void himax_set_SMWP_func(uint8_t SMWP_enable); + +#define GEST_PTLG_ID_LEN (4) +#define GEST_PTLG_HDR_LEN (4) +#define GEST_PTLG_HDR_ID1 (0xCC) +#define GEST_PTLG_HDR_ID2 (0x44) +#define GEST_PT_MAX_NUM (128) + +extern uint8_t *wake_event_buffer; +#endif + +extern int irq_enable_count; +extern int g_mmi_refcnt; +extern int *g_inspt_crtra_flag; +extern uint32_t g_hx_chip_inited; +/*void himax_HW_reset(uint8_t loadconfig,uint8_t int_off);*/ + +int himax_chip_common_suspend(struct himax_ts_data *ts); +int himax_chip_common_resume(struct himax_ts_data *ts); + +extern struct filename* (*kp_getname_kernel)(const char *filename); +extern void (*kp_putname_kernel)(struct filename *name); +extern struct file * (*kp_file_open_name)(struct filename *name, int flags, umode_t mode); + +struct himax_core_fp; +extern struct himax_core_fp g_core_fp; +extern struct himax_ts_data *private_ts; +extern struct himax_ic_data *ic_data; +extern struct device *g_device; + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_DEBUG + int himax_debug_init(void); + int himax_debug_remove(void); +#endif + +#if defined(HX_PLATFOME_DEFINE_KEY) + void himax_platform_key(void); +#endif + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INSPECT + extern char *g_rslt_data; + extern void (*fp_himax_self_test_init)(void); +#endif + +int himax_parse_dt(struct himax_ts_data *ts, struct himax_i2c_platform_data *pdata); +int himax_report_data(struct himax_ts_data *ts, int ts_path, int ts_status); + +int himax_report_data_init(void); + +int himax_dev_set(struct himax_ts_data *ts); +int himax_input_register_device(struct input_dev *input_dev); + +#endif + diff --git a/drivers/input/touchscreen/himax_v3_mmi/himax_debug.c b/drivers/input/touchscreen/himax_v3_mmi/himax_debug.c new file mode 100755 index 000000000000..b41724bd3cb2 --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/himax_debug.c @@ -0,0 +1,2864 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for debug nodes + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_debug.h" +#include "himax_ic_core.h" + +#ifdef HX_TP_PROC_2T2R + bool Is_2T2R; + int HX_RX_NUM_2; + int HX_TX_NUM_2; +#endif + +uint8_t g_diag_arr_num; + +int g_max_mutual; +int g_min_mutual = 0xFFFF; +int g_max_self; +int g_min_self = 0xFFFF; + +/* moved from debug.h */ + +struct proc_dir_entry *himax_proc_vendor_file; +uint8_t byte_length; +uint8_t register_command[4]; +uint8_t cfg_flag; + +struct proc_dir_entry *himax_proc_stack_file; +struct proc_dir_entry *himax_proc_iir_file; +struct proc_dir_entry *himax_proc_dc_file; +struct proc_dir_entry *himax_proc_bank_file; +bool dsram_flag; + +#ifdef HX_TP_PROC_2T2R +uint32_t *diag_mutual_2; +#endif +int32_t *diag_mutual; +int32_t *diag_mutual_new; +int32_t *diag_mutual_old; +uint8_t diag_max_cnt; +uint8_t hx_state_info[2]; +uint8_t diag_coor[128]; +int32_t diag_self[100]; +int32_t diag_self_new[100]; +int32_t diag_self_old[100]; + +struct proc_dir_entry *himax_proc_debug_file; +bool fw_update_complete; +int handshaking_result; +unsigned char debug_level_cmd; +uint8_t cmd_set[8]; +uint8_t mutual_set_flag; + +struct proc_dir_entry *himax_proc_flash_dump_file; +int Flash_Size = 131072; +uint8_t *flash_buffer; +uint8_t g_flash_cmd; +uint8_t g_flash_progress; +bool g_flash_dump_rst; /*Fail = 0, Pass = 1*/ + +uint32_t **raw_data_array; +uint8_t X_NUM; +uint8_t Y_NUM; +uint8_t sel_type = 0x0D; + +/* Moved from debug.h End */ +char buf_tmp[BUF_SIZE] = {0}; +uint8_t *reg_read_data = NULL; +#define DEBUG_READ_BUF_SIZE 128 + + +#if defined(HX_PEN_FUNC_EN) + struct proc_dir_entry *himax_proc_pen_pos_file; +#endif + +struct timespec timeStart, timeEnd, timeDelta; +int g_switch_mode; +/* + * Segment : Himax PROC Debug Function + */ + +#if defined(HX_PEN_FUNC_EN) +static ssize_t himax_pen_ops_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + int i = 0; + ssize_t ret = 0; + + if (!HX_PROC_SEND_FLAG) { + for (i = 0; i < private_ts->nFinger_support; i++) { + if (g_target_report_data->x[i] >= 0 && g_target_report_data->x[i] <= private_ts->pdata->abs_x_max + && g_target_report_data->y[i] >= 0 && g_target_report_data->y[i] <= private_ts->pdata->abs_y_max + && private_ts->hx_point_num != 0) { + ret += snprintf(buf_tmp + ret, len - ret, + "%d,%d,", g_target_report_data->x[i], g_target_report_data->y[i]); + } else { + ret += snprintf(buf_tmp + ret, len - ret, + "65535,65535,"); + } + } + if (g_target_report_data->p_x[0] >= 0 && g_target_report_data->p_x[0] <= private_ts->pdata->abs_x_max + && g_target_report_data->p_y[0] >= 0 && g_target_report_data->p_y[0] <= private_ts->pdata->abs_y_max + && (g_target_report_data->p_on[0] == 1)) { + ret += snprintf(buf_tmp + ret, len - ret, + "%d,%d,", g_target_report_data->p_x[0], g_target_report_data->p_y[0]); + } else { + ret += snprintf(buf_tmp + ret, len - ret, + "65535,65535,"); + } + + ret += snprintf(buf_tmp + ret, len - ret, + "%d,", g_target_report_data->p_w[0]); + + ret += snprintf(buf_tmp + ret, len - ret, + "%d,", g_target_report_data->p_hover[0]); + + if (g_target_report_data->p_btn[0] != 0xff) { + ret += snprintf(buf_tmp + ret, len - ret, + "%d,", g_target_report_data->p_btn[0]); + } else { + ret += snprintf(buf_tmp + ret, len - ret, + "0,"); + } + if (g_target_report_data->p_btn2[0] != 0xff) { + ret += snprintf(buf_tmp + ret, len - ret, + "%d,", g_target_report_data->p_btn2[0]); + } else { + ret += snprintf(buf_tmp + ret, len - ret, + "0,"); + } + + if (g_target_report_data->p_tilt_x[0] != 0xff) { + ret += snprintf(buf_tmp + ret, len - ret, + "%d,", g_target_report_data->p_tilt_x[0]); + } else { + ret += snprintf(buf_tmp + ret, len - ret, + "0,"); + } + if (g_target_report_data->p_tilt_y[0] != 0xff) { + ret += snprintf(buf_tmp + ret, len - ret, + "%d\n", g_target_report_data->p_tilt_y[0]); + } else { + ret += snprintf(buf_tmp + ret, len - ret, + "0\n"); + } + + if (copy_to_user(buf, buf_tmp, (len > BUF_SIZE) ? BUF_SIZE : len)) + I("%s,here:%d\n", __func__, __LINE__); + + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static const struct file_operations himax_proc_pen_pos_ops = { + .owner = THIS_MODULE, + .read = himax_pen_ops_read, +}; +#endif + +static ssize_t himax_crc_test_read(char *buf, size_t len) +{ + ssize_t ret = 0; + uint8_t result = 0; + uint32_t size = 0; + + g_core_fp.fp_sense_off(true); + msleep(20); + if (g_core_fp._diff_overlay_flash() == 1) + size = FW_SIZE_128k; + else + size = FW_SIZE_64k; + result = g_core_fp.fp_calculateChecksum(false, size); + g_core_fp.fp_sense_on(0x01); + + if (result) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CRC test is Pass!\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CRC test is Fail!\n"); + + return ret; +} + +static ssize_t himax_vendor_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + ssize_t ret = 0; + + if (!HX_PROC_SEND_FLAG) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "FW_VER = 0x%2.2X\n", + ic_data->vendor_fw_ver); + + if (private_ts->chip_cell_type == CHIP_IS_ON_CELL) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CONFIG_VER = 0x%2.2X\n", + ic_data->vendor_config_ver); + } else { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "TOUCH_VER = 0x%2.2X\n", + ic_data->vendor_touch_cfg_ver); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "DISPLAY_VER = 0x%2.2X\n", + ic_data->vendor_display_cfg_ver); + } + + if (ic_data->vendor_cid_maj_ver < 0 && + ic_data->vendor_cid_min_ver < 0) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CID_VER = NULL\n"); + } else { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CID_VER = 0x%2.2X\n", + (ic_data->vendor_cid_maj_ver << 8 | + ic_data->vendor_cid_min_ver)); + } + + if (ic_data->vendor_panel_ver < 0) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "PANEL_VER = NULL\n"); + } else { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "PANEL_VER = 0x%2.2X\n", + ic_data->vendor_panel_ver); + } + if (private_ts->chip_cell_type == CHIP_IS_IN_CELL) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Cusomer = %s\n", ic_data->vendor_cus_info); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Project = %s\n", ic_data->vendor_proj_info); + } +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#ifdef HX_EN_DYNAMIC_NAME + if (ic_data->vendor_semifac == 1) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "SEMI_FAC = UMC\n"); + } else if (ic_data->vendor_semifac == 2) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "SEMI_FAC = PSC\n"); + } else { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "SEMI_FAC = NULL\n"); + } +#endif +#endif + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Himax Touch Driver Version:\n"); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%s\n", + HIMAX_DRIVER_VER); + HX_PROC_SEND_FLAG = 1; + + if (copy_to_user(buf, buf_tmp, (len > BUF_SIZE) ? BUF_SIZE : len)) + I("%s,here:%d\n", __func__, __LINE__); + + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static const struct file_operations himax_proc_vendor_ops = { + .owner = THIS_MODULE, + .read = himax_vendor_read, +}; + +static ssize_t himax_attn_read(char *buf, size_t len) +{ + ssize_t ret = 0; + struct himax_ts_data *ts_data; + + ts_data = private_ts; + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "attn = %x\n", + himax_int_gpio_read(ts_data->pdata->gpio_irq)); + + return ret; +} + +static ssize_t himax_int_en_read(char *buf, size_t len) +{ + struct himax_ts_data *ts = private_ts; + size_t ret = 0; + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%d\n", + ts->irq_enabled); + + return ret; +} + +static ssize_t himax_int_en_write(char *buf, size_t len) +{ + struct himax_ts_data *ts = private_ts; + int value, ret = 0; + + if (len >= 12) { + I("%s: no command exceeds 12 chars.\n", __func__); + return -EFAULT; + } + + if (buf[0] == '0') + value = false; + else if (buf[0] == '1') + value = true; + else + return -EINVAL; + + if (value) { + ret = himax_int_en_set(); + + if (ret == 0) { + ts->irq_enabled = 1; + irq_enable_count = 1; + } + } else { + himax_int_enable(0); + free_irq(ts->hx_irq, ts); + ts->irq_enabled = 0; + } + + return len; +} + +static ssize_t himax_layout_read(char *buf, size_t len) +{ + struct himax_ts_data *ts = private_ts; + size_t ret = 0; + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%d ", + ts->pdata->abs_x_min); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%d ", + ts->pdata->abs_x_max); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%d ", + ts->pdata->abs_y_min); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%d ", + ts->pdata->abs_y_max); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + + return ret; +} + +static ssize_t himax_layout_write(char *buf, size_t len) +{ + struct himax_ts_data *ts = private_ts; + char buf_tmp[5] = {0}; + int i = 0, j = 0, k = 0, ret; + unsigned long value; + int layout[4] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + for (i = 0; i < 20; i++) { + if (buf[i] == ',' || buf[i] == '\n') { + memset(buf_tmp, 0x0, sizeof(buf_tmp)); + + if (i - j <= 5) { + memcpy(buf_tmp, buf + j, i - j); + } else { + I("buffer size is over 5 char\n"); + return len; + } + + j = i + 1; + + if (k < 4) { + ret = kstrtoul(buf_tmp, 10, &value); + layout[k++] = value; + } + } + } + + if (k == 4) { + ts->pdata->abs_x_min = layout[0]; + ts->pdata->abs_x_max = (layout[1] - 1); + ts->pdata->abs_y_min = layout[2]; + ts->pdata->abs_y_max = (layout[3] - 1); + I("%d, %d, %d, %d\n", + ts->pdata->abs_x_min, ts->pdata->abs_x_max, + ts->pdata->abs_y_min, ts->pdata->abs_y_max); + input_unregister_device(ts->input_dev); + himax_input_register(ts); + } else { + I("ERR@%d, %d, %d, %d\n", + ts->pdata->abs_x_min, ts->pdata->abs_x_max, + ts->pdata->abs_y_min, ts->pdata->abs_y_max); + } + + return len; +} + +static ssize_t himax_debug_level_read(char *buf, size_t len) +{ + struct himax_ts_data *ts_data; + size_t ret = 0; + + ts_data = private_ts; + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "%d\n", + ts_data->debug_log_level); + + if (copy_to_user(buf, buf_tmp, (len > BUF_SIZE) ? BUF_SIZE : len)) + I("%s,here:%d\n", __func__, __LINE__); + + return ret; +} + +static ssize_t himax_debug_level_write(char *buf, size_t len) +{ + struct himax_ts_data *ts; + int i; + + ts = private_ts; + + if (len >= 12) { + I("%s: no command exceeds 12 chars.\n", __func__); + return -EFAULT; + } + + ts->debug_log_level = 0; + + for (i = 0; i < len; i++) { + if (buf[i] >= '0' && buf[i] <= '9') + ts->debug_log_level |= (buf[i] - '0'); + else if (buf[i] >= 'A' && buf[i] <= 'F') + ts->debug_log_level |= (buf[i] - 'A' + 10); + else if (buf[i] >= 'a' && buf[i] <= 'f') + ts->debug_log_level |= (buf[i] - 'a' + 10); + + if (i != len - 1) + ts->debug_log_level <<= 4; + } + I("Now debug level value=%d\n", ts->debug_log_level); + if (ts->debug_log_level & BIT(4)) { + I("Turn on/Enable Debug Mode for Inspection!\n"); + goto END_FUNC; + } + + if (ts->debug_log_level & BIT(3)) { + if (ts->pdata->screenWidth > 0 && ts->pdata->screenHeight > 0 && + (ts->pdata->abs_x_max - ts->pdata->abs_x_min) > 0 && + (ts->pdata->abs_y_max - ts->pdata->abs_y_min) > 0) { + ts->widthFactor = (ts->pdata->screenWidth << SHIFTBITS) + / (ts->pdata->abs_x_max - ts->pdata->abs_x_min); + ts->heightFactor = + (ts->pdata->screenHeight << SHIFTBITS) / + (ts->pdata->abs_y_max - ts->pdata->abs_y_min); + + if (ts->widthFactor > 0 && ts->heightFactor > 0) { + ts->useScreenRes = 1; + } else { + ts->heightFactor = 0; + ts->widthFactor = 0; + ts->useScreenRes = 0; + } + } else { + I("Enable finger debug with raw position mode!\n"); + } + } else { + ts->useScreenRes = 0; + ts->widthFactor = 0; + ts->heightFactor = 0; + } +END_FUNC: + return len; +} + +static ssize_t himax_proc_register_read(char *buf, size_t len) +{ + int ret = 0; + uint16_t loop_i; + + if(reg_read_data == NULL){ + E("%s reg_read_data is null \n",__func__); + return ret; + } + memset(reg_read_data, 0x00, DEBUG_READ_BUF_SIZE * sizeof(uint8_t)); + + I("himax_register_show: %02X,%02X,%02X,%02X\n", + register_command[3], register_command[2], + register_command[1], register_command[0]); + g_core_fp.fp_register_read(register_command, 128, reg_read_data, + cfg_flag); + ret += snprintf(buf_tmp + ret, len - ret, + "command: %02X,%02X,%02X,%02X\n", + register_command[3], register_command[2], + register_command[1], register_command[0]); + + for (loop_i = 0; loop_i < 128; loop_i++) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "0x%2.2X ", + reg_read_data[loop_i]); + if ((loop_i % 16) == 15) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + + } + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + + return ret; +} + +static ssize_t himax_proc_register_write(char *buf, size_t len) +{ + char buff_tmp[16] = {0}; + uint8_t length = 0; + unsigned long result = 0; + uint8_t loop_i = 0; + uint16_t base = 2; + char *data_str = NULL; + uint8_t w_data[20] = {0}; + uint8_t x_pos[20] = {0}; + uint8_t count = 0; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + memset(register_command, 0x0, sizeof(register_command)); + + I("himax %s\n", buf); + + if ((buf[0] == 'r' || buf[0] == 'w') && buf[1] == ':' && + buf[2] == 'x') { + length = strlen(buf); + + /* I("%s: length = %d.\n", __func__,length); */ + for (loop_i = 0; loop_i < length; loop_i++) { + /* find postion of 'x' */ + if (buf[loop_i] == 'x') { + x_pos[count] = loop_i; + count++; + } + } + + data_str = strrchr(buf, 'x'); + I("%s: %s.\n", __func__, data_str); + length = strlen(data_str + 1); + + switch (buf[0]) { + case 'r': + if (buf[3] == 'F' && buf[4] == 'E' && length == 4) { + length = length - base; + cfg_flag = 1; + memcpy(buff_tmp, data_str + base + 1, length); + } else { + cfg_flag = 0; + memcpy(buff_tmp, data_str + 1, length); + } + byte_length = length / 2; + if (!kstrtoul(buff_tmp, 16, &result)) { + for (loop_i = 0; loop_i < byte_length; loop_i++) + register_command[loop_i] = + (uint8_t)(result >> loop_i * 8); + } + + if (strcmp(HX_85XX_H_SERIES_PWON, + private_ts->chip_name) == 0 && cfg_flag == 0) + cfg_flag = 2; + break; + case 'w': + if (buf[3] == 'F' && buf[4] == 'E') { + cfg_flag = 1; + memcpy(buff_tmp, buf + base + 3, length); + } else { + cfg_flag = 0; + memcpy(buff_tmp, buf + 3, length); + } + + if (count < 3) { + byte_length = length / 2; + + if (!kstrtoul(buff_tmp, 16, &result)) { + /* command */ + for (loop_i = 0; loop_i < byte_length; + loop_i++) { + register_command[loop_i] = + (uint8_t)(result >> loop_i * 8); + } + } + + if (!kstrtoul(data_str + 1, 16, &result)) { + /* data */ + for (loop_i = 0; loop_i < byte_length; + loop_i++) { + w_data[loop_i] = + (uint8_t)(result >> loop_i * 8); + } + } + + g_core_fp.fp_register_write(register_command, + byte_length, w_data, cfg_flag); + } else { + for (loop_i = 0; loop_i < count; loop_i++) { + /* parsing addr after 'x' */ + memset(buff_tmp, 0x0, sizeof(buff_tmp)); + if (cfg_flag != 0 && loop_i != 0) + byte_length = 2; + else + byte_length = x_pos[1] - + x_pos[0] - 2;/* original */ + memcpy(buff_tmp, buf + x_pos[loop_i] + 1, + byte_length); + + /* I("%s: buff_tmp = %s\n", __func__, + * buff_tmp); + */ + if (!kstrtoul(buff_tmp, 16, &result)) { + if (loop_i == 0) { + register_command[loop_i] + = (uint8_t)(result); + /* I("%s: + * register_command + * = %X\n", __func__, + * register_command[0]); + */ + } else { + w_data[loop_i - 1] = + (uint8_t)(result); + /* I("%s: w_data[%d] = + * %2X\n", __func__, + * loop_i - 1, + * w_data[loop_i - 1]); + */ + } + } + } + + byte_length = count - 1; + if (strcmp(HX_85XX_H_SERIES_PWON, + private_ts->chip_name) == 0 && cfg_flag == 0) + cfg_flag = 2; + g_core_fp.fp_register_write(register_command, + byte_length, &w_data[0], cfg_flag); + + } + break; + }; + } + return len; +} + +int32_t *getMutualBuffer(void) +{ + return diag_mutual; +} +int32_t *getMutualNewBuffer(void) +{ + return diag_mutual_new; +} +int32_t *getMutualOldBuffer(void) +{ + return diag_mutual_old; +} +int32_t *getSelfBuffer(void) +{ + return &diag_self[0]; +} +int32_t *getSelfNewBuffer(void) +{ + return &diag_self_new[0]; +} +int32_t *getSelfOldBuffer(void) +{ + return &diag_self_old[0]; +} +void setMutualBuffer(uint8_t x_num, uint8_t y_num) +{ + diag_mutual = kzalloc(x_num * y_num * sizeof(int32_t), GFP_KERNEL); +} +void setMutualNewBuffer(uint8_t x_num, uint8_t y_num) +{ + diag_mutual_new = kzalloc(x_num * y_num * sizeof(int32_t), GFP_KERNEL); +} +void setMutualOldBuffer(uint8_t x_num, uint8_t y_num) +{ + diag_mutual_old = kzalloc(x_num * y_num * sizeof(int32_t), GFP_KERNEL); +} + +#ifdef HX_TP_PROC_2T2R +int32_t *getMutualBuffer_2(void) +{ + return diag_mutual_2; +} +void setMutualBuffer_2(uint8_t x_num_2, uint8_t y_num_2) +{ + diag_mutual_2 = + kzalloc(x_num_2 * y_num_2 * sizeof(int32_t), GFP_KERNEL); +} +#endif + +int himax_set_diag_cmd(struct himax_ic_data *ic_data, + struct himax_report_data *hx_touch_data) +{ + struct himax_ts_data *ts = private_ts; + int32_t *mutual_data; + int32_t *self_data; + int mul_num; + int self_num; + /* int RawDataLen = 0; */ + hx_touch_data->diag_cmd = ts->diag_cmd; + + if (hx_touch_data->diag_cmd >= 1 && hx_touch_data->diag_cmd <= 7) { + /* Check event stack CRC */ + if (!g_core_fp.fp_diag_check_sum(hx_touch_data)) + goto bypass_checksum_failed_packet; + +#ifdef HX_TP_PROC_2T2R + if (Is_2T2R && (hx_touch_data->diag_cmd >= 4 && + hx_touch_data->diag_cmd <= 6)) { + mutual_data = getMutualBuffer_2(); + self_data = getSelfBuffer(); + /* initiallize the block number of mutual and self */ + mul_num = ic_data->HX_RX_NUM_2 * ic_data->HX_TX_NUM_2; +#ifdef HX_EN_SEL_BUTTON + self_num = ic_data->HX_RX_NUM_2 + + ic_data->HX_TX_NUM_2 + ic_data->HX_BT_NUM; +#else + self_num = ic_data->HX_RX_NUM_2 + ic_data->HX_TX_NUM_2; +#endif + } else +#endif + { + mutual_data = getMutualBuffer(); + self_data = getSelfBuffer(); + /* initiallize the block number of mutual and self */ + mul_num = ic_data->HX_RX_NUM * ic_data->HX_TX_NUM; +#ifdef HX_EN_SEL_BUTTON + self_num = ic_data->HX_RX_NUM + + ic_data->HX_TX_NUM + ic_data->HX_BT_NUM; +#else + self_num = ic_data->HX_RX_NUM + ic_data->HX_TX_NUM; +#endif + } + g_core_fp.fp_diag_parse_raw_data(hx_touch_data, mul_num, + self_num, hx_touch_data->diag_cmd, mutual_data, + self_data); + } else if (hx_touch_data->diag_cmd == 8) { + memset(diag_coor, 0x00, sizeof(diag_coor)); + memcpy(&(diag_coor[0]), &hx_touch_data->hx_coord_buf[0], + hx_touch_data->touch_info_size); + } + + /* assign state info data */ + memcpy(&(hx_state_info[0]), &hx_touch_data->hx_state_info[0], 2); + return NO_ERR; +bypass_checksum_failed_packet: + return 1; +} + +/* #if defined(HX_DEBUG_LEVEL) */ +void himax_log_touch_data(int start) +{ + int loop_i = 0; + int print_size = 0; + uint8_t *buf; + + if (start == 1) + return; /* report data when end of ts_work*/ + + if (hx_touch_data->diag_cmd > 0) { + print_size = hx_touch_data->touch_all_size; + buf = kcalloc(print_size, sizeof(uint8_t), GFP_KERNEL); + memcpy(buf, hx_touch_data->hx_coord_buf, + hx_touch_data->touch_info_size); + memcpy(&buf[hx_touch_data->touch_info_size], + hx_touch_data->hx_rawdata_buf, + print_size - hx_touch_data->touch_info_size); + } +#if defined(HX_SMART_WAKEUP) + else if (private_ts->SMWP_enable > 0 && private_ts->suspended) { + print_size = hx_touch_data->event_size; + buf = kcalloc(print_size, sizeof(uint8_t), GFP_KERNEL); + memcpy(buf, hx_touch_data->hx_event_buf, print_size); + } +#endif + else if (hx_touch_data->diag_cmd == 0) { + print_size = hx_touch_data->touch_info_size; + buf = kcalloc(print_size, sizeof(uint8_t), GFP_KERNEL); + memcpy(buf, hx_touch_data->hx_coord_buf, print_size); + } else { + E("%s:cmd fault\n", __func__); + } + + for (loop_i = 0; loop_i < print_size; loop_i += 8) { + if ((loop_i + 7) >= print_size) { + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", + loop_i, buf[loop_i], loop_i + 1, buf[loop_i + 1]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X\n", + loop_i + 2, buf[loop_i + 2], loop_i + 3, buf[loop_i + 3]); + break; + } + + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", + loop_i, buf[loop_i], loop_i + 1, buf[loop_i + 1]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", + loop_i + 2, buf[loop_i + 2], loop_i + 3, buf[loop_i + 3]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", + loop_i + 4, buf[loop_i + 4], loop_i + 5, buf[loop_i + 5]); + I("P %2d = 0x%2.2X P %2d = 0x%2.2X ", + loop_i + 6, buf[loop_i + 6], loop_i + 7, buf[loop_i + 7]); + I("\n"); + } + kfree(buf); +} +void himax_log_touch_event(struct himax_ts_data *ts, int start) +{ + int loop_i = 0; + + if (start == 1) + return; /*report data when end of ts_work*/ + + if (g_target_report_data->finger_on > 0 && + g_target_report_data->finger_num > 0) { + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + if (g_target_report_data->x[loop_i] >= 0 + && g_target_report_data->x[loop_i] + <= ts->pdata->abs_x_max + && g_target_report_data->y[loop_i] >= 0 + && g_target_report_data->y[loop_i] + <= ts->pdata->abs_y_max) { + I("Finger %d=> X:%d, Y:%d W:%d, Z:%d, F:%d,Int_Delay_Counter:%d\n", + loop_i + 1, + g_target_report_data->x[loop_i], + g_target_report_data->y[loop_i], + g_target_report_data->w[loop_i], + g_target_report_data->w[loop_i], + loop_i + 1, + g_target_report_data->ig_count); + + } + } + } else if (g_target_report_data->finger_on == 0 && + g_target_report_data->finger_num == 0) { + I("All Finger leave\n"); + } else { + I("%s : wrong input!\n", __func__); + } +} +void himax_log_touch_int_devation(int touched) +{ + if (touched == HX_FINGER_ON) { + getnstimeofday(&timeStart); + /* I(" Irq start time = %ld.%06ld s\n", + * timeStart.tv_sec, timeStart.tv_nsec/1000); + */ + } else if (touched == HX_FINGER_LEAVE) { + getnstimeofday(&timeEnd); + timeDelta.tv_nsec = + (timeEnd.tv_sec * 1000000000 + timeEnd.tv_nsec) - + (timeStart.tv_sec * 1000000000 + timeStart.tv_nsec); + /* I("Irq finish time = %ld.%06ld s\n", + * timeEnd.tv_sec, timeEnd.tv_nsec/1000); + */ + I("Touch latency = %ld us\n", timeDelta.tv_nsec / 1000); + I("bus_speed = %d kHz\n", private_ts->bus_speed); + if (g_target_report_data->finger_on == 0 && g_target_report_data->finger_num == 0) + I("All Finger leave\n"); + } else { + I("%s : wrong input!\n", __func__); + } +} + +#define RAW_DOWN_STATUS "status: Raw:F:%02d Down, X:%d, Y:%d, W:%d\n" +#define RAW_UP_STATUS "status: Raw:F:%02d Up, X:%d, Y:%d\n" + +void himax_log_touch_event_detail(struct himax_ts_data *ts, int start) +{ + int loop_i = 0; + + if (start == HX_FINGER_LEAVE) { + for (loop_i = 0; loop_i < ts->nFinger_support; loop_i++) { + if (((ts->old_finger >> loop_i & 1) == 0) && + ((ts->pre_finger_mask >> loop_i & 1) == 1)) { + if (g_target_report_data->x[loop_i] >= 0 + && g_target_report_data->x[loop_i] + <= ts->pdata->abs_x_max + && g_target_report_data->y[loop_i] >= 0 + && g_target_report_data->y[loop_i] + <= ts->pdata->abs_y_max) { + I(RAW_DOWN_STATUS, + loop_i + 1, + g_target_report_data->x[loop_i], + g_target_report_data->y[loop_i], + g_target_report_data->w[loop_i]); + } + } else if ((((ts->old_finger >> loop_i & 1) == 1) && + ((ts->pre_finger_mask >> loop_i & 1) == 0))) { + I(RAW_UP_STATUS, + loop_i + 1, ts->pre_finger_data[loop_i][0], + ts->pre_finger_data[loop_i][1]); + } else { + /* I("dbg hx_point_num=%d, old_finger=0x%02X," + * " pre_finger_mask=0x%02X\n", + * ts->hx_point_num, ts->old_finger, + * ts->pre_finger_mask); + */ + } + } + } +} + +void himax_ts_dbg_func(struct himax_ts_data *ts, int start) +{ + if (ts->debug_log_level & BIT(0)) { + /* I("debug level 1\n"); */ + himax_log_touch_data(start); + } + if (ts->debug_log_level & BIT(1)) { + /* I("debug level 2\n"); */ + himax_log_touch_event(ts, start); + } + if (ts->debug_log_level & BIT(2)) { + /* I("debug level 4\n"); */ + himax_log_touch_int_devation(start); + } + if (ts->debug_log_level & BIT(3)) { + /* I("debug level 8\n"); */ + himax_log_touch_event_detail(ts, start); + } +} + +static int himax_change_mode(uint8_t str_pw, uint8_t end_pw) +{ + uint8_t data[4] = {0}; + int count = 0; + + /*sense off*/ + g_core_fp.fp_sense_off(true); + /*mode change*/ + data[1] = str_pw; data[0] = str_pw; + if (g_core_fp.fp_assign_sorting_mode != NULL) + g_core_fp.fp_assign_sorting_mode(data); + + /*sense on*/ + g_core_fp.fp_sense_on(1); + /*wait mode change*/ + do { + if (g_core_fp.fp_check_sorting_mode != NULL) + g_core_fp.fp_check_sorting_mode(data); + if ((data[0] == end_pw) && (data[1] == end_pw)) + return 0; + + I("Now retry %d times!\n", count++); + msleep(50); + } while (count < 50); + + return ERR_WORK_OUT; +} + +static ssize_t himax_diag_cmd_write(char *buf, size_t len) +{ + struct himax_ts_data *ts = private_ts; + char *dbg_map_str = "mode:"; + char *str_ptr = NULL; + int str_len = 0; + int rst = 0; + uint8_t str_pw = 0; + uint8_t end_pw = 0; + + switch (len) { + case 1:/*raw out select - diag,X*/ + if (!kstrtoint(buf, 16, &rst)) { + ts->diag_cmd = rst; + I("%s: dsram_flag = %d\n", __func__, dsram_flag); + if (dsram_flag) { + /*Cancal work queue and return to stack*/ + process_type = 0; + dsram_flag = false; + cancel_delayed_work(&ts->himax_diag_delay_wrok); + himax_int_enable(1); + g_core_fp.fp_return_event_stack(); + } + g_core_fp.fp_diag_register_set(ts->diag_cmd, 0); + I("%s: Set raw out select 0x%X.\n", __func__, ts->diag_cmd); + } + if (!ts->diag_cmd) { + if (mode_flag) /*back to normal mode*/ + himax_change_mode(0x00, 0x99); + } + break; + case 2:/*data processing + rawout select - diag,XY*/ + if (!kstrtoint(buf, 16, &rst)) { + process_type = (rst >> 4) & 0xF; + ts->diag_cmd = rst & 0xF; + } + if (process_type <= 0 || ts->diag_cmd <= 0) + break; + else if (process_type > 0 && process_type <= 3) { + if (!dsram_flag) { + /*Start wrok queue*/ + himax_int_enable(0); + g_core_fp.fp_diag_register_set(ts->diag_cmd, process_type); + queue_delayed_work(ts->himax_diag_wq, + &ts->himax_diag_delay_wrok, 2 * HZ / 100); + dsram_flag = true; + I("%s: Start get raw data in DSRAM\n", __func__); + } else { + g_core_fp.fp_diag_register_set(ts->diag_cmd, process_type); + } + } + break; + case 9:/*change mode - mode:XXYY(start PW,end PW)*/ + str_ptr = strnstr(buf, dbg_map_str, len); + if (str_ptr) { + str_len = strlen(dbg_map_str); + if (!kstrtoint(buf + str_len, 16, &rst)) { + str_pw = (rst >> 8) & 0xFF; + end_pw = rst & 0xFF; + if (!himax_change_mode(str_pw, end_pw)) { + mode_flag = 1; + I("%s: change mode 0x%4X. str_pw = %2X, end_pw = %2X\n", __func__, rst, str_pw, end_pw); + } else + I("%s: change mode failed. str_pw = %2X, end_pw = %2X\n", __func__, str_pw, end_pw); + } + } else { + I("%s: Can't find string [%s].\n", __func__, dbg_map_str); + } + break; + default: + I("%s: Length is not correct.\n", __func__); + } + return len; +} + +static ssize_t himax_diag_arrange_write(char *buf, size_t len) +{ + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + g_diag_arr_num = buf[0] - '0'; + I("%s: g_diag_arr_num = %d\n", __func__, g_diag_arr_num); + return len; +} + +void himax_get_mutual_edge(void) +{ + int i = 0; + + for (i = 0; i < (ic_data->HX_RX_NUM * ic_data->HX_TX_NUM); i++) { + if (diag_mutual[i] > g_max_mutual) + g_max_mutual = diag_mutual[i]; + + if (diag_mutual[i] < g_min_mutual) + g_min_mutual = diag_mutual[i]; + } +} + +void himax_get_self_edge(void) +{ + int i = 0; + + for (i = 0; i < (ic_data->HX_RX_NUM + ic_data->HX_TX_NUM); i++) { + if (diag_self[i] > g_max_self) + g_max_self = diag_self[i]; + + if (diag_self[i] < g_min_self) + g_min_self = diag_self[i]; + } +} + +static void print_state_info(struct seq_file *s) +{ + /* seq_printf(s, "State_info_2bytes:%3d, %3d\n", + * _state_info[0],hx_state_info[1]); + */ + +#ifdef HX_NEW_EVENT_STACK_FORMAT + seq_printf(s, "ReCal = %d\t", hx_state_info[0] & 0x03); + seq_printf(s, "Base Line = %d\t", hx_state_info[0] >> 2 & 0x01); + seq_printf(s, "Palm = %d\t", hx_state_info[0] >> 3 & 0x01); + seq_printf(s, "Idle mode = %d\t", hx_state_info[0] >> 4 & 0x01); + seq_printf(s, "Water = %d\n", hx_state_info[0] >> 5 & 0x01); + seq_printf(s, "TX Hop = %d\t", hx_state_info[0] >> 6 & 0x01); + seq_printf(s, "AC mode = %d\t", hx_state_info[0] >> 7 & 0x01); + seq_printf(s, "Glove = %d\t", hx_state_info[1] & 0x01); + seq_printf(s, "Stylus = %d\t", hx_state_info[1] >> 1 & 0x01); + seq_printf(s, "Hovering = %d\t", hx_state_info[1] >> 2 & 0x01); + seq_printf(s, "Proximity = %d\t", hx_state_info[1] >> 3 & 0x01); + seq_printf(s, "KEY = %d\n", hx_state_info[1] >> 4 & 0x0F); +#else + seq_printf(s, "ReCal = %d\t", hx_state_info[0] & 0x01); + seq_printf(s, "Palm = %d\t", hx_state_info[0] >> 1 & 0x01); + seq_printf(s, "AC mode = %d\t", hx_state_info[0] >> 2 & 0x01); + seq_printf(s, "Water = %d\n", hx_state_info[0] >> 3 & 0x01); + seq_printf(s, "Glove = %d\t", hx_state_info[0] >> 4 & 0x01); + seq_printf(s, "TX Hop = %d\t", hx_state_info[0] >> 5 & 0x01); + seq_printf(s, "Base Line = %d\t", hx_state_info[0] >> 6 & 0x01); + seq_printf(s, "OSR Hop = %d\t", hx_state_info[1] >> 3 & 0x01); + seq_printf(s, "KEY = %d\n", hx_state_info[1] >> 4 & 0x0F); +#endif +} + +static void himax_diag_arrange_print(struct seq_file *s, int i, int j, + int transpose) +{ + if (transpose) + seq_printf(s, "%6d", diag_mutual[j + i * ic_data->HX_RX_NUM]); + else + seq_printf(s, "%6d", diag_mutual[i + j * ic_data->HX_RX_NUM]); +} + +/* ready to print second step which is column*/ +static void himax_diag_arrange_inloop(struct seq_file *s, int in_init, + int out_init, bool transpose, int j) +{ + int x_channel = ic_data->HX_RX_NUM; + int y_channel = ic_data->HX_TX_NUM; + int i; + int in_max = 0; + + if (transpose) + in_max = y_channel; + else + in_max = x_channel; + + if (in_init > 0) { /* bit0 = 1 */ + for (i = in_init - 1; i >= 0; i--) + himax_diag_arrange_print(s, i, j, transpose); + + if (transpose) { + if (out_init > 0) + seq_printf(s, " %5d\n", diag_self[j]); + else + seq_printf(s, " %5d\n", + diag_self[x_channel - j - 1]); + } + } else { /* bit0 = 0 */ + for (i = 0; i < in_max; i++) + himax_diag_arrange_print(s, i, j, transpose); + + if (transpose) { + if (out_init > 0) + seq_printf(s, " %5d\n", + diag_self[x_channel - j - 1]); + else + seq_printf(s, " %5d\n", diag_self[j]); + } + } +} + +/* print first step which is row */ +static void himax_diag_arrange_outloop(struct seq_file *s, int transpose, + int out_init, int in_init) +{ + int j; + int x_channel = ic_data->HX_RX_NUM; + int y_channel = ic_data->HX_TX_NUM; + int out_max = 0; + int self_cnt = 0; + + if (transpose) + out_max = x_channel; + else + out_max = y_channel; + + if (out_init > 0) { /* bit1 = 1 */ + self_cnt = 1; + + for (j = out_init - 1; j >= 0; j--) { + seq_printf(s, "%3c%02d%c", '[', j + 1, ']'); + himax_diag_arrange_inloop(s, in_init, out_init, + transpose, j); + + if (!transpose) { + seq_printf(s, " %5d\n", + diag_self[y_channel + x_channel - self_cnt]); + self_cnt++; + } + } + } else { /* bit1 = 0 */ + /* self_cnt = x_channel; */ + for (j = 0; j < out_max; j++) { + seq_printf(s, "%3c%02d%c", '[', j + 1, ']'); + himax_diag_arrange_inloop(s, in_init, out_init, + transpose, j); + + if (!transpose) { + seq_printf(s, " %5d\n", + diag_self[j + x_channel]); + } + } + } +} + +/* determin the output format of diag */ +static void himax_diag_arrange(struct seq_file *s) +{ + int x_channel = ic_data->HX_RX_NUM; + int y_channel = ic_data->HX_TX_NUM; + int bit2, bit1, bit0; + int i; + /* rotate bit */ + bit2 = g_diag_arr_num >> 2; + /* reverse Y */ + bit1 = g_diag_arr_num >> 1 & 0x1; + /* reverse X */ + bit0 = g_diag_arr_num & 0x1; + + if (g_diag_arr_num < 4) { + for (i = 0 ; i <= x_channel; i++) + seq_printf(s, "%3c%02d%c", '[', i, ']'); + + seq_puts(s, "\n"); + himax_diag_arrange_outloop(s, bit2, bit1 * y_channel, + bit0 * x_channel); + seq_printf(s, "%6c", ' '); + + if (bit0 == 1) { + for (i = x_channel - 1; i >= 0; i--) + seq_printf(s, "%6d", diag_self[i]); + } else { + for (i = 0; i < x_channel; i++) + seq_printf(s, "%6d", diag_self[i]); + } + } else { + for (i = 0 ; i <= y_channel; i++) + seq_printf(s, "%3c%02d%c", '[', i, ']'); + + seq_puts(s, "\n"); + himax_diag_arrange_outloop(s, bit2, bit1 * x_channel, + bit0 * y_channel); + seq_printf(s, "%6c", ' '); + + if (bit1 == 1) { + for (i = x_channel + y_channel - 1; i >= x_channel; + i--) + seq_printf(s, "%6d", diag_self[i]); + } else { + for (i = x_channel; i < x_channel + y_channel; i++) + seq_printf(s, "%6d", diag_self[i]); + } + } +} + +static void *himax_diag_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) + return NULL; + + return (void *)((unsigned long) *pos + 1); +} + +static void *himax_diag_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_diag_seq_stop(struct seq_file *s, void *v) +{ + kfree(v); +} + +/* DSRAM thread */ +bool himax_ts_diag_func(void) +{ + int retry = 3; + int i = 0, j = 0; + unsigned int index = 0; + int x_channel = ic_data->HX_RX_NUM; + int y_channel = ic_data->HX_TX_NUM; + int total_size = (y_channel * x_channel + y_channel + x_channel) * 2; + uint8_t *info_data; + int32_t *mutual_data = NULL; + int32_t *mutual_data_new = NULL; + int32_t *mutual_data_old = NULL; + int32_t *self_data = NULL; + int32_t *self_data_new = NULL; + int32_t *self_data_old = NULL; + int32_t new_data; + /* 1:common dsram,2:100 frame Max,3:N-(N-1)frame */ + int dsram_type = process_type; + + info_data = kcalloc(total_size, sizeof(uint8_t), GFP_KERNEL); + if (info_data == NULL) + return false; + + memset(info_data, 0, total_size * sizeof(uint8_t)); + + I("%s: process type=%d!\n", __func__, process_type); + + g_core_fp.fp_burst_enable(1); + + if (dsram_type <= 2) { + mutual_data = getMutualBuffer(); + self_data = getSelfBuffer(); + } else if (dsram_type == 3) { + mutual_data = getMutualBuffer(); + mutual_data_new = getMutualNewBuffer(); + mutual_data_old = getMutualOldBuffer(); + self_data = getSelfBuffer(); + self_data_new = getSelfNewBuffer(); + self_data_old = getSelfOldBuffer(); + } + + do { + if (retry <= 0) { + E("%s Get DSRAM data failed\n", __func__); + kfree(info_data); + return false; + } + if (g_core_fp.fp_get_DSRAM_data(info_data, dsram_flag)) + break; + } while (retry-- > 0); + + index = 0; + + for (i = 0; i < y_channel; i++) { /*mutual data*/ + for (j = 0; j < x_channel; j++) { + new_data = (((int8_t)info_data[index + 1] << 8) | + info_data[index]); + + if (dsram_type <= 1) { + mutual_data[i * x_channel + j] = new_data; + } else if (dsram_type == 2) { /* Keep max data */ + if (mutual_data[i * x_channel + j] < new_data) + mutual_data[i * x_channel + j] = new_data; + } else if (dsram_type == 3) { + /* Cal data for [N]-[N-1] frame */ + mutual_data_new[i * x_channel + j] = new_data; + mutual_data[i * x_channel + j] = + mutual_data_new[i * x_channel + j] - + mutual_data_old[i * x_channel + j]; + } + index += 2; + } + } + + for (i = 0; i < x_channel + y_channel; i++) { /*self data*/ + new_data = (info_data[index + 1] << 8 | info_data[index]); + if (dsram_type <= 1) { + self_data[i] = new_data; + } else if (dsram_type == 2) { /* Keep max data */ + if (self_data[i] < new_data) + self_data[i] = new_data; + } else if (dsram_type == 3) { /* Cal data for [N]-[N-1] frame */ + self_data_new[i] = new_data; + self_data[i] = self_data_new[i] - self_data_old[i]; + } + index += 2; + } + + kfree(info_data); + + if (dsram_type == 3) { + memcpy(mutual_data_old, mutual_data_new, + x_channel * y_channel * sizeof(int32_t)); + /* copy N data to N-1 array */ + memcpy(self_data_old, self_data_new, + (x_channel + y_channel) * sizeof(int32_t)); + /* copy N data to N-1 array */ + } + + diag_max_cnt++; + + if (dsram_type >= 1 && dsram_type <= 3) { + queue_delayed_work(private_ts->himax_diag_wq, + &private_ts->himax_diag_delay_wrok, 1 / 10 * HZ); + } + return true; +} + +static int himax_diag_print(struct seq_file *s, void *v) +{ + int x_num = ic_data->HX_RX_NUM; + int y_num = ic_data->HX_TX_NUM; + size_t ret = 0; + uint16_t mutual_num, self_num, width; + + mutual_num = x_num * y_num; + self_num = x_num + y_num; + /* don't add KEY_COUNT */ + width = x_num; + seq_printf(s, "ChannelStart: %4d, %4d\n\n", x_num, y_num); + + /* start to show out the raw data in adb shell */ + himax_diag_arrange(s); + seq_puts(s, "\n"); +#ifdef HX_EN_SEL_BUTTON + seq_puts(s, "\n"); + for (loop_i = 0; loop_i < ic_data->HX_BT_NUM; loop_i++) + seq_printf(s, "%6d", diag_self[ic_data->HX_RX_NUM + + ic_data->HX_TX_NUM + loop_i]); +#endif + seq_puts(s, "ChannelEnd"); + seq_puts(s, "\n"); + + /* print Mutual/Slef Maximum and Minimum */ + himax_get_mutual_edge(); + himax_get_self_edge(); + seq_printf(s, "Mutual Max:%3d, Min:%3d\n", g_max_mutual, + g_min_mutual); + seq_printf(s, "Self Max:%3d, Min:%3d\n", g_max_self, + g_min_self); + /* recovery status after print*/ + g_max_mutual = 0; + g_min_mutual = 0xFFFF; + g_max_self = 0; + g_min_self = 0xFFFF; + + /*pring state info*/ + print_state_info(s); + + if (s->count >= s->size) + overflow++; + + return ret; +} + +static int himax_diag_stack_read(struct seq_file *s, void *v) +{ + struct himax_ts_data *ts = private_ts; + + if (ts->diag_cmd) + himax_diag_print(s, v); + else + seq_puts(s, "Please set raw out select 'echo diag,rs:X > debug'\n\n"); + + return 0; +} + +static const struct seq_operations himax_diag_stack_ops = { + .start = himax_diag_seq_start, + .next = himax_diag_seq_next, + .stop = himax_diag_seq_stop, + .show = himax_diag_stack_read, +}; + +static int himax_diag_stack_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_diag_stack_ops); +}; + +static const struct file_operations himax_proc_stack_ops = { + .owner = THIS_MODULE, + .open = himax_diag_stack_open, + .read = seq_read, + .release = seq_release, +}; + +static int himax_sram_read(struct seq_file *s, void *v, uint8_t rs) +{ + struct himax_ts_data *ts = private_ts; + int d_type = 0; + + d_type = (!ts->diag_cmd)?rs:ts->diag_cmd; + + if (!overflow) { + if (!process_type) { + himax_int_enable(0); + g_core_fp.fp_diag_register_set(d_type, 0); + + if (!himax_ts_diag_func()) + seq_puts(s, "Get sram data failed."); + else + himax_diag_print(s, v); + + ts->diag_cmd = 0; + g_core_fp.fp_diag_register_set(0, 0); + himax_int_enable(1); + } + } + + if ((process_type <= 3 && ts->diag_cmd && dsram_flag) || overflow) { + himax_diag_print(s, v); + overflow = 0; + } + + return 0; +} + +static int himax_diag_iir_read(struct seq_file *s, void *v) +{ + return himax_sram_read(s, v, 0x09); +} + +static const struct seq_operations himax_diag_iir_ops = { + .start = himax_diag_seq_start, + .next = himax_diag_seq_next, + .stop = himax_diag_seq_stop, + .show = himax_diag_iir_read, +}; + +static int himax_diag_iir_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_diag_iir_ops); +}; + +static const struct file_operations himax_proc_iir_ops = { + .owner = THIS_MODULE, + .open = himax_diag_iir_open, + .read = seq_read, + .release = seq_release, +}; + +static int himax_diag_dc_read(struct seq_file *s, void *v) +{ + return himax_sram_read(s, v, 0x0A); +} + +static const struct seq_operations himax_diag_dc_ops = { + .start = himax_diag_seq_start, + .next = himax_diag_seq_next, + .stop = himax_diag_seq_stop, + .show = himax_diag_dc_read, +}; +static int himax_diag_dc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_diag_dc_ops); +}; + +static const struct file_operations himax_proc_dc_ops = { + .owner = THIS_MODULE, + .open = himax_diag_dc_open, + .read = seq_read, + .release = seq_release, +}; + +static int himax_diag_bank_read(struct seq_file *s, void *v) +{ + return himax_sram_read(s, v, 0x0B); +} + +static const struct seq_operations himax_diag_bank_ops = { + .start = himax_diag_seq_start, + .next = himax_diag_seq_next, + .stop = himax_diag_seq_stop, + .show = himax_diag_bank_read, +}; +static int himax_diag_bank_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_diag_bank_ops); +}; + +static const struct file_operations himax_proc_bank_ops = { + .owner = THIS_MODULE, + .open = himax_diag_bank_open, + .read = seq_read, + .release = seq_release, +}; + +static ssize_t himax_reset_write(char *buf, size_t len) +{ + if (len >= 12) { + I("%s: no command exceeds 12 chars.\n", __func__); + return -EFAULT; + } + +#ifdef HX_RST_PIN_FUNC + if (buf[0] == '1') + g_core_fp.fp_ic_reset(false, false); + else if (buf[0] == '2') + g_core_fp.fp_ic_reset(false, true); + else if (buf[0] == '3') + g_core_fp.fp_ic_reset(true, false); + else if (buf[0] == '4') + g_core_fp.fp_ic_reset(true, true); + /* else if (buf[0] == '5') */ + /* ESD_HW_REST(); */ +#endif +#if defined(HX_ZERO_FLASH) && defined(HX83102D) + if (g_core_fp.fp_0f_reload_to_active) + g_core_fp.fp_0f_reload_to_active(); +#endif + return len; +} + +static ssize_t himax_proc_FW_debug_read(char *buf, size_t len) +{ + ssize_t ret = 0; + uint8_t loop_i = 0; + uint8_t tmp_data[64] = {0}; + + cmd_set[0] = 0x01; + + if (g_core_fp.fp_read_FW_status(cmd_set, tmp_data) == NO_ERR) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "0x%02X%02X%02X%02X :\t", + cmd_set[5], cmd_set[4], cmd_set[3], cmd_set[2]); + + for (loop_i = 0; loop_i < cmd_set[1]; loop_i++) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "%5d\t", tmp_data[loop_i]); + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + } + + cmd_set[0] = 0x02; + + if (g_core_fp.fp_read_FW_status(cmd_set, tmp_data) == NO_ERR) { + for (loop_i = 0; loop_i < cmd_set[1]; + loop_i = loop_i + 2) { + if ((loop_i % 16) == 0) + ret += snprintf(buf_tmp + ret, + sizeof(buf_tmp) - ret, "0x%02X%02X%02X%02X :\t", + cmd_set[5], cmd_set[4], + cmd_set[3] + + (((cmd_set[2] + loop_i) >> 8) & 0xFF), + (cmd_set[2] + loop_i) & 0xFF); + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "%5d\t", tmp_data[loop_i] + + (tmp_data[loop_i + 1] << 8)); + + if ((loop_i % 16) == 14) + ret += snprintf(buf_tmp + ret, + sizeof(buf_tmp) - ret, "\n"); + } + } + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + + return ret; +} + +static ssize_t himax_proc_DD_debug_read(char *buf, size_t len) +{ + ssize_t ret = 0; + uint8_t tmp_data[64] = {0}; + uint8_t loop_i = 0; + + if (mutual_set_flag == 1) { + if (g_core_fp.fp_read_DD_status(cmd_set, tmp_data) == + NO_ERR) { + for (loop_i = 0; loop_i < cmd_set[0]; + loop_i++) { + if ((loop_i % 8) == 0) + ret += snprintf(buf_tmp + ret, + sizeof(buf_tmp) - ret, "0x%02X : ", loop_i); + + ret += snprintf(buf_tmp + ret, + sizeof(buf_tmp) - ret, "0x%02X ", tmp_data[loop_i]); + + if ((loop_i % 8) == 7) + ret += snprintf(buf_tmp + ret, + sizeof(buf_tmp) - ret, "\n"); + } + } else { + ret += snprintf(buf_tmp + ret, + sizeof(buf_tmp) - ret, "Get DD status falied!\n"); + } + } + + ret += snprintf(buf_tmp + ret, len - ret, "\n"); + + return ret; +} + +#define STR_TO_UL_ERR "String to ul is fail in cnt = %d, buf_tmp2 = %s\n" + +static ssize_t himax_proc_DD_debug_write(char *buf, size_t len) +{ + uint8_t i = 0; + uint8_t cnt = 2; + unsigned long result = 0; + char buf_tmp2[4]; + + if (len >= 20) { + I("%s: no command exceeds 20 chars.\n", __func__); + return -EFAULT; + } + + memset(buf_tmp2, 0x0, sizeof(buf_tmp2)); + + if (buf[2] == 'x' && buf[6] == 'x' && buf[10] == 'x') { + mutual_set_flag = 1; + + for (i = 3; i < 12; i = i + 4) { + memcpy(buf_tmp2, buf + i, 2); + + if (!kstrtoul(buf_tmp2, 16, &result)) + cmd_set[cnt] = (uint8_t)result; + else + I(STR_TO_UL_ERR, cnt, buf_tmp2); + + cnt--; + } + + I("cmd_set[2] = %02X, cmd_set[1] = %02X, cmd_set[0] = %02X\n", + cmd_set[2], cmd_set[1], cmd_set[0]); + } else { + mutual_set_flag = 0; + } + + return len; +} + +void setFlashBuffer(void) +{ + flash_buffer = kcalloc(Flash_Size, sizeof(uint8_t), GFP_KERNEL); +} + +void flash_dump_prog_set(uint8_t prog) +{ + g_flash_progress = prog; + if (prog == ONGOING) + debug_data->flash_dump_going = ONGOING; + else + debug_data->flash_dump_going = START; +} + +static int himax_proc_flash_read(struct seq_file *s, void *v) +{ + ssize_t ret = 0; + int i; + uint8_t flash_progress = g_flash_progress; + uint8_t flash_cmd = g_flash_cmd; + bool flash_rst = g_flash_dump_rst; + + I("flash_progress = %d\n", flash_progress); + + if (!flash_rst) { + seq_puts(s, "FlashStart:Fail\n"); + seq_puts(s, "FlashEnd\n"); + return ret; + } + + if (flash_progress == START) + seq_puts(s, "Flash dump - Start\n"); + else if (flash_progress == ONGOING) + seq_puts(s, "Flash dump - On-going\n"); + else if (flash_progress == FINISHED) + seq_puts(s, "Flash dump - Finished\n"); + + /*print flash dump data*/ + if (flash_cmd == 1 && flash_progress == FINISHED) { + seq_puts(s, "Start to print flash dump data\n"); + for (i = 0; i < Flash_Size; i++) { + seq_printf(s, "0x%02X,", flash_buffer[i]); + if (i % 16 == 15) + seq_puts(s, "\n"); + } + } + + seq_puts(s, "FlashEnd\n"); + + return ret; +} + +static ssize_t himax_proc_flash_write(struct file *filp, + const char __user *buff, size_t len, loff_t *data) +{ + char buf[80] = {0}; + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + I("%s: buf = %s\n", __func__, buf); + + if (g_flash_progress == ONGOING) { + E("%s: process is busy , return!\n", __func__); + return len; + } + + if ((buf[1] == '_') && (buf[2] == '3') && (buf[3] == '2')) + Flash_Size = FW_SIZE_32k; + else if ((buf[1] == '_') && (buf[2] == '6')) { + if (buf[3] == '0') + Flash_Size = FW_SIZE_60k; + else if (buf[3] == '4') + Flash_Size = FW_SIZE_64k; + + } else if ((buf[1] == '_') && (buf[2] == '2')) { + if (buf[3] == '4') + Flash_Size = FW_SIZE_124k; + else if (buf[3] == '8') + Flash_Size = FW_SIZE_128k; + } + + /*1 : print flash to window, 2 : dump to sdcard*/ + if (buf[0] == '1') { + /* 1_32,1_60,1_64,1_24,1_28 for flash size: + * 32k,60k,64k,124k,128k + */ + g_flash_cmd = 1; + flash_dump_prog_set(START); + g_flash_dump_rst = true; + queue_work(private_ts->flash_wq, &private_ts->flash_work); + } else if (buf[0] == '2') { + /* 2_32,2_60,2_64,2_24,2_28 for flash size: + * 32k,60k,64k,124k,128k + */ + g_flash_cmd = 2; + flash_dump_prog_set(START); + g_flash_dump_rst = true; + queue_work(private_ts->flash_wq, &private_ts->flash_work); + } + + return len; +} + +static void *himax_flash_dump_seq_start(struct seq_file *s, loff_t *pos) +{ + if (*pos >= 1) + return NULL; + + return (void *)((unsigned long) *pos + 1); +} + +static void *himax_flash_dump_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + return NULL; +} + +static void himax_flash_dump_seq_stop(struct seq_file *s, void *v) +{ +} + +static const struct seq_operations himax_flash_dump_seq_ops = { + .start = himax_flash_dump_seq_start, + .next = himax_flash_dump_seq_next, + .stop = himax_flash_dump_seq_stop, + .show = himax_proc_flash_read, +}; +static int himax_flash_dump_proc_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &himax_flash_dump_seq_ops); +}; + +static const struct file_operations himax_proc_flash_ops = { + .owner = THIS_MODULE, + .open = himax_flash_dump_proc_open, + .read = seq_read, + .write = himax_proc_flash_write, +}; + +void himax_ts_flash_func(void) +{ + uint8_t flash_command = g_flash_cmd; + + himax_int_enable(0); + flash_dump_prog_set(ONGOING); + + /*msleep(100);*/ + I("%s: flash_command = %d enter.\n", __func__, flash_command); + + if (flash_command == 1 || flash_command == 2) { + g_core_fp.fp_flash_dump_func(flash_command, Flash_Size, + flash_buffer); + g_flash_dump_rst = true; + } + + I("Complete~~~~~~~~~~~~~~~~~~~~~~~\n"); + +/* if (flash_command == 2) { + * struct file *fn; + * struct filename *vts_name; + * + * vts_name = kp_getname_kernel(FLASH_DUMP_FILE); + * fn = kp_file_open_name(vts_name, O_CREAT | O_WRONLY, 0); + * + * if (!IS_ERR(fn)) { + * I("%s create file and ready to write\n", __func__); + * fn->f_op->write(fn, flash_buffer, + * Flash_Size * sizeof(uint8_t), &fn->f_pos); + * filp_close(fn, NULL); + * } else { + * E("%s Open file failed!\n", __func__); + * g_flash_dump_rst = false; + * } + * } + */ + himax_int_enable(1); + flash_dump_prog_set(FINISHED); +} + +static ssize_t himax_sense_on_off_write(char *buf, size_t len) +{ + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (buf[0] == '0') { + g_core_fp.fp_sense_off(true); + I("Sense off\n"); + } else if (buf[0] == '1') { + if (buf[1] == 's') { + g_core_fp.fp_sense_on(0x00); + I("Sense on re-map on, run sram\n"); + } else { + g_core_fp.fp_sense_on(0x01); + I("Sense on re-map off, run flash\n"); + } + } else { + I("Do nothing\n"); + } + + return len; +} + +#ifdef HX_ESD_RECOVERY +static ssize_t himax_esd_cnt_read(char *buf, size_t len) +{ + size_t ret = 0; + + I("%s: enter, %d\n", __func__, __LINE__); + + ret += snprintf(buf_tmp + ret, len - ret, + "EB_cnt = %d, EC_cnt = %d, ED_cnt = %d\n", + hx_EB_event_flag, hx_EC_event_flag, hx_ED_event_flag); + + return ret; +} + +static ssize_t himax_esd_cnt_write(char *buf, size_t len) +{ + int i = 0; + + if (len >= 12) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + I("Clear ESD Flag\n"); + + if (buf[i] == '0') { + hx_EB_event_flag = 0; + hx_EC_event_flag = 0; + hx_ED_event_flag = 0; + } + + return len; +} + +#endif + +static int printMat(char *temp_buf, size_t len, int max_size, + uint8_t *guest_str, int loc) +{ + int ret = loc; + int i; + + for (i = 0; i < max_size; i++) { + if ((i % 16) == 0 && i > 0) + ret += snprintf(temp_buf + ret, len - ret, "\n"); + + ret += snprintf(temp_buf + ret, len - ret, "0x%02X\t", + guest_str[i]); + } + return ret; +} + +static int printUnit(char *temp_buf, size_t len, int max_size, char *info_item, + uint8_t *guest_str, int loc) +{ + int ret = loc; + + ret += snprintf(temp_buf + ret, len - ret, "%s:\n", info_item); + ret = printMat(temp_buf, len, max_size, guest_str, ret); + ret += snprintf(temp_buf + ret, len - ret, "\n"); + return ret; +} + +#ifdef HX_TP_PROC_GUEST_INFO +static ssize_t himax_proc_guest_info_read(char *buf, size_t len) +{ + int ret = 0; + int j = 0; + int max_size = 128; + struct hx_guest_info *info = g_guest_info_data; + + I("guest info progress\n"); + + if (g_core_fp.guest_info_get_status()) { + ret += snprintf(buf_tmp + ret, len - ret, + "Not Ready\n"); + goto END_FUNCTION; + } else { + if (info->g_guest_info_type == 1) { + for (j = 0; j < 3; j++) { + ret = printUnit(buf_tmp, len, max_size, + g_guest_info_item[j], + info->g_guest_str[j], ret); + I("str[%d] %s\n", j, + info->g_guest_str[j]); + } + ret = printUnit(buf_tmp, len, max_size, + g_guest_info_item[8], info->g_guest_str[8], + ret); + + I("str[8] %s\n", + info->g_guest_str[8]); + + ret = printUnit(buf_tmp, len, max_size, + g_guest_info_item[9], info->g_guest_str[9], + ret); + + I("str[9] %s\n", + info->g_guest_str[9]); + } else if (info->g_guest_info_type == 0) { + for (j = 0; j < 10; j++) { + if (j == 3) + j = 8; + ret += snprintf(buf_tmp + ret, + len - ret, "%s:\n", + g_guest_info_item[j]); + if (info->g_guest_data_type[j] == 0) { + ret += snprintf(buf_tmp + ret, + len - ret, "%s", + g_guest_info_data->g_guest_str_in_format[j]); + } else { + ret = printMat(buf_tmp, len, + info->g_guest_data_len[j], + g_guest_info_data->g_guest_str_in_format[j], ret); + } + ret += snprintf(buf_tmp + ret, + len - ret, "\n"); + } + } + } + +END_FUNCTION: + return ret; +} + +static ssize_t himax_proc_guest_info_write(char *buf, size_t len) +{ + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + I("%s: buf = %s\n", __func__, buf); + if (buf[0] == 'r') { + I("%s,Test to get", __func__); + queue_work(private_ts->guest_info_wq, + &private_ts->guest_info_work); + } + return len; +} + +#endif + +static ssize_t himax_debug_read(struct file *file, char *buf, + size_t len, loff_t *pos) +{ + ssize_t ret = 0; + int i = 0; + + if (!HX_PROC_SEND_FLAG) { + I("%s, Enter, len = %d\n", __func__, (int)len); + memset(buf_tmp, 0, sizeof(buf_tmp)); + + if (dbg_cmd_flag) { + memset(buf_tmp, 0, sizeof(buf_tmp)); + if (dbg_func_ptr_r[dbg_cmd_flag]) + ret += dbg_func_ptr_r[dbg_cmd_flag](buf, len); + else + goto END_FUNC_R; + } + + if (debug_level_cmd == 't') { + if (fw_update_complete) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "FW Update Complete "); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "FW Update Fail "); + + } else if (debug_level_cmd == 'h') { + if (handshaking_result == 0) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Handshaking Result = %d (MCU Running)\n", + handshaking_result); + else if (handshaking_result == 1) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Handshaking Result = %d (MCU Stop)\n", + handshaking_result); + else if (handshaking_result == 2) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Handshaking Result = %d (I2C Error)\n", + handshaking_result); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Handshaking Result = error\n"); + + } else if (debug_level_cmd == 'v') { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "FW_VER = 0x%2.2X\n", ic_data->vendor_fw_ver); + + if (private_ts->chip_cell_type == CHIP_IS_ON_CELL) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CONFIG_VER = 0x%2.2X\n", + ic_data->vendor_config_ver); + else { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "TOUCH_VER = 0x%2.2X\n", + ic_data->vendor_touch_cfg_ver); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "DISPLAY_VER = 0x%2.2X\n", + ic_data->vendor_display_cfg_ver); + } + if (ic_data->vendor_cid_maj_ver < 0 && + ic_data->vendor_cid_min_ver < 0) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CID_VER = NULL\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "CID_VER = 0x%2.2X\n", + (ic_data->vendor_cid_maj_ver << 8 | + ic_data->vendor_cid_min_ver)); + + if (ic_data->vendor_panel_ver < 0) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "PANEL_VER = NULL\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "PANEL_VER = 0x%2.2X\n", + ic_data->vendor_panel_ver); + if (private_ts->chip_cell_type == CHIP_IS_IN_CELL) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Cusomer = %s\n", ic_data->vendor_cus_info); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Project = %s\n", ic_data->vendor_proj_info); + } +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#ifdef HX_EN_DYNAMIC_NAME + if (ic_data->vendor_semifac == 1) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "SEMI_FAC = UMC\n"); + else if (ic_data->vendor_semifac == 2) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "SEMI_FAC = PSC\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "SEMI_FAC = NULL\n"); +#endif +#endif + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, "\n"); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Himax Touch Driver Version:\n"); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "%s\n", HIMAX_DRIVER_VER); + } else if (debug_level_cmd == 'd') { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Himax Touch IC Information :\n"); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "%s\n", private_ts->chip_name); + + switch (IC_CHECKSUM) { + case HX_TP_BIN_CHECKSUM_SW: + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "IC Checksum : SW\n"); + break; + + case HX_TP_BIN_CHECKSUM_HW: + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "IC Checksum : HW\n"); + break; + + case HX_TP_BIN_CHECKSUM_CRC: + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "IC Checksum : CRC\n"); + break; + + default: + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "IC Checksum error.\n"); + } + + if (ic_data->HX_INT_IS_EDGE) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Driver register Interrupt : EDGE TIRGGER\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Driver register Interrupt : LEVEL TRIGGER\n"); + + if (private_ts->protocol_type == PROTOCOL_TYPE_A) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Protocol : TYPE_A\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Protocol : TYPE_B\n"); + + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "RX Num : %d\n", ic_data->HX_RX_NUM); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "TX Num : %d\n", ic_data->HX_TX_NUM); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "BT Num : %d\n", ic_data->HX_BT_NUM); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "X Resolution : %d\n", ic_data->HX_X_RES); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Y Resolution : %d\n", ic_data->HX_Y_RES); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Max Point : %d\n", ic_data->HX_MAX_PT); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "XY reverse : %d\n", ic_data->HX_XY_REVERSE); +#ifdef HX_TP_PROC_2T2R + if (Is_2T2R) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "2T2R panel\n"); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "RX Num_2 : %d\n", HX_RX_NUM_2); + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "TX Num_2 : %d\n", HX_TX_NUM_2); + } +#endif + } else if (debug_level_cmd == 'i') { + if (g_core_fp.fp_read_i2c_status()) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "I2C communication is bad.\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "I2C communication is good.\n"); + } else if (debug_level_cmd == 'n') { + /* Edgd = 1, Level = 0 */ + if (g_core_fp.fp_read_ic_trigger_type() == 1) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "IC Interrupt type is edge trigger.\n"); + else if (g_core_fp.fp_read_ic_trigger_type() == 0) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "IC Interrupt type is level trigger.\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Unkown IC trigger type.\n"); + + if (ic_data->HX_INT_IS_EDGE) + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Driver register Interrupt : EDGE TIRGGER\n"); + else + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "Driver register Interrupt : LEVEL TRIGGER\n"); + } else if (debug_level_cmd == 'l') { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "LotID : "); + for (i = 0; i < 13; i++) { + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "%02X", ic_data->vendor_ic_id[i]); + } + ret += snprintf(buf_tmp + ret, sizeof(buf_tmp) - ret, + "\n"); + } + +END_FUNC_R: + if (copy_to_user(buf, buf_tmp, (len > BUF_SIZE) ? BUF_SIZE : len)) + I("%s,here:%d\n", __func__, __LINE__); + + HX_PROC_SEND_FLAG = 1; + } else { + HX_PROC_SEND_FLAG = 0; + } + + return ret; +} + +static ssize_t himax_debug_write(struct file *file, const char *buff, + size_t len, loff_t *pos) +{ + char fileName[128]; + char buf[80] = {0}; + int result = 0; +#ifndef HX_ZERO_FLASH + int fw_type = 0; + const struct firmware *fw = NULL; +#endif + + char *str_ptr = NULL; + int str_len = 0; + int i = 0; + + + if (len >= 80) { + I("%s: no command exceeds 80 chars.\n", __func__); + return -EFAULT; + } + + if (copy_from_user(buf, buff, len)) + return -EFAULT; + + buf[strlen(buf) - 1] = 0;/*remove \n*/ + + while (dbg_cmd_str[i]) { + str_ptr = strnstr(buf, dbg_cmd_str[i], len); + if (str_ptr) { + str_len = strlen(dbg_cmd_str[i]); + dbg_cmd_flag = i + 1; + debug_level_cmd = 0; + I("Cmd is correct :%s, dbg_cmd = %d\n", str_ptr, dbg_cmd_flag); + break; + } + i++; + } + if (!str_ptr) { + I("Cmd is not correct\n"); + dbg_cmd_flag = 0; + } + + if (buf[str_len] == ',') { + dbg_cmd_par = buf + str_len + 1; + if (dbg_func_ptr_w[dbg_cmd_flag]) + dbg_func_ptr_w[dbg_cmd_flag](dbg_cmd_par, len - str_len - 2);/* 2 => '/n' + ','*/ + I("string of paremeter is %s, dbg_cmd_par = %s\n", buf + str_len + 1, dbg_cmd_par); + } else + I("No paremeter of this cmd\n"); + + if (dbg_cmd_flag) + return len; + + if (buf[0] == 'h') { /* handshaking */ + debug_level_cmd = buf[0]; + himax_int_enable(0); + /* 0:Running, 1:Stop, 2:I2C Fail */ + handshaking_result = g_core_fp.fp_hand_shaking(); + himax_int_enable(1); + return len; + } else if (buf[0] == 'v') { /* firmware version */ + himax_int_enable(0); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#endif + debug_level_cmd = buf[0]; + g_core_fp.fp_read_FW_ver(); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(true, false); +#else + g_core_fp.fp_system_reset(); +#endif +#if defined(HX_ZERO_FLASH) && defined(HX83102D) + if (g_core_fp.fp_0f_reload_to_active) + g_core_fp.fp_0f_reload_to_active(); +#endif + himax_int_enable(1); + /* himax_check_chip_version(); */ + return len; + } else if (buf[0] == 'd') { /* ic information */ + debug_level_cmd = buf[0]; + return len; + } else if (buf[0] == 't') { + if (buf[1] == 's' && + buf[2] == 'd' && + buf[3] == 'b' && + buf[4] == 'g' + ) { + if (buf[5] == '1') { + I("Open Ts Debug!\n"); + g_ts_dbg = 1; + } else if (buf[5] == '0') { + I("Close Ts Debug!\n"); + g_ts_dbg = 0; + } else { + E("Parameter fault for ts debug\n"); + } + goto ENDFUCTION; + } + himax_int_enable(0); + debug_level_cmd = buf[0]; + fw_update_complete = false; + memset(fileName, 0, 128); + /* parse the file name */ + snprintf(fileName, len - 2, "%s", &buf[2]); + +#ifdef HX_ZERO_FLASH + I("NOW Running Zero flash update!\n"); + I("%s: upgrade from file(%s) start!\n", __func__, fileName); + result = g_core_fp.fp_0f_op_file_dirly(fileName); + if (result) { + fw_update_complete = false; + I("Zero flash update fail!\n"); + goto ENDFUCTION; + } else { + fw_update_complete = true; + I("Zero flash update complete!\n"); + } + goto firmware_upgrade_done; +#else + I("NOW Running common flow update!\n"); + I("%s: upgrade from file(%s) start!\n", __func__, fileName); + result = request_firmware(&fw, fileName, private_ts->dev); + + if (result < 0) { + I("fail to request_firmware fwpath: %s (ret:%d)\n", + fileName, result); + return result; + } + + I("%s: FW image: %02X, %02X, %02X, %02X\n", __func__, + fw->data[0], fw->data[1], fw->data[2], fw->data[3]); + fw_type = (fw->size) / 1024; + /* start to upgrade */ + himax_int_enable(0); + I("Now FW size is : %dk\n", fw_type); + + switch (fw_type) { + case 32: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_32k( + (unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", + __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", + __func__, __LINE__); + fw_update_complete = true; + } + break; + + case 60: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_60k( + (unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", + __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", + __func__, __LINE__); + fw_update_complete = true; + } + break; + + case 64: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_64k( + (unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", + __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", + __func__, __LINE__); + fw_update_complete = true; + } + break; + + case 124: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_124k( + (unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", + __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", + __func__, __LINE__); + fw_update_complete = true; + } + break; + + case 128: + if (g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_128k( + (unsigned char *)fw->data, fw->size, false) == 0) { + E("%s: TP upgrade error, line: %d\n", + __func__, __LINE__); + fw_update_complete = false; + } else { + I("%s: TP upgrade OK, line: %d\n", + __func__, __LINE__); + fw_update_complete = true; + } + break; + + default: + E("%s: Flash command fail: %d\n", __func__, __LINE__); + fw_update_complete = false; + break; + } + release_firmware(fw); + goto firmware_upgrade_done; +#endif + } else if (buf[0] == 'i' && buf[1] == '2' && buf[2] == 'c') { + /* i2c communication */ + debug_level_cmd = 'i'; + return len; + } else if (buf[0] == 'i' && buf[1] == 'n' && buf[2] == 't') { + /* INT trigger */ + debug_level_cmd = 'n'; + return len; +#ifdef HX_ZERO_FLASH + } else if (buf[0] == 'z') { + result = buf[1] - '0'; + I("check type = %d\n", result); + g_core_fp.fp_0f_operation_check(result); + return len; + } else if (buf[0] == 'p') { + I("NOW debug echo r!\n"); + /* himax_program_sram(); */ + private_ts->himax_0f_update_wq = + create_singlethread_workqueue("HMX_update_0f_reuqest_write"); + + if (!private_ts->himax_0f_update_wq) + E(" allocate syn_update_wq failed\n"); + + INIT_DELAYED_WORK(&private_ts->work_0f_update, + g_core_fp.fp_0f_operation); + queue_delayed_work(private_ts->himax_0f_update_wq, + &private_ts->work_0f_update, msecs_to_jiffies(100)); + return len; + } else if (buf[0] == 'x') { + g_core_fp.fp_system_reset(); +#if defined(HX_ZERO_FLASH) && defined(HX83102D) + if (g_core_fp.fp_0f_reload_to_active) + g_core_fp.fp_0f_reload_to_active(); +#endif + return len; +#endif + } else if (buf[0] == 'l' && buf[1] == 'o' && buf[2] == 't') { + debug_level_cmd = buf[0]; + g_core_fp.fp_sense_off(true); + g_core_fp.fp_ic_id_read(); + g_core_fp.fp_sense_on(0x01); + return len; + } + /* others,do nothing */ + debug_level_cmd = 0; + return len; + +firmware_upgrade_done: + g_core_fp.fp_reload_disable(0); + g_core_fp.fp_read_FW_ver(); + g_core_fp.fp_touch_information(); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(true, false); +#if defined(HX_ZERO_FLASH) && defined(HX83102D) + if (g_core_fp.fp_0f_reload_to_active) + g_core_fp.fp_0f_reload_to_active(); +#endif +#else + g_core_fp.fp_sense_on(0x00); +#endif + + himax_int_enable(1); + /* todo himax_chip->tp_firmware_upgrade_proceed = 0; + * todo himax_chip->suspend_state = 0; + * todo enable_irq(himax_chip->irq); + */ +ENDFUCTION: + return len; +} + +static const struct file_operations himax_proc_debug_ops = { + .owner = THIS_MODULE, + .read = himax_debug_read, + .write = himax_debug_write, +}; + +static void himax_himax_data_init(void) +{ + debug_data->fp_ts_dbg_func = himax_ts_dbg_func; + debug_data->fp_set_diag_cmd = himax_set_diag_cmd; + debug_data->flash_dump_going = false; +} + +static void himax_ts_flash_work_func(struct work_struct *work) +{ + himax_ts_flash_func(); +} +#ifdef HX_TP_PROC_GUEST_INFO +static void himax_ts_guest_info_work_func(struct work_struct *work) +{ + g_core_fp.read_guest_info(); +} +#endif + +static void himax_ts_diag_work_func(struct work_struct *work) +{ + himax_ts_diag_func(); +} + +void dbg_func_ptr_init(void) +{ + /*debug function ptr init*/ + dbg_func_ptr_r[1] = himax_crc_test_read; + dbg_func_ptr_r[2] = himax_proc_FW_debug_read; + dbg_func_ptr_r[3] = himax_attn_read; + dbg_func_ptr_r[4] = himax_layout_read; + dbg_func_ptr_w[4] = himax_layout_write; + dbg_func_ptr_r[5] = himax_proc_DD_debug_read; + dbg_func_ptr_w[5] = himax_proc_DD_debug_write; +#ifdef HX_ESD_RECOVERY + dbg_func_ptr_r[6] = himax_esd_cnt_read; + dbg_func_ptr_w[6] = himax_esd_cnt_write; +#endif + dbg_func_ptr_w[7] = himax_sense_on_off_write; + dbg_func_ptr_r[8] = himax_debug_level_read; + dbg_func_ptr_w[8] = himax_debug_level_write; +#ifdef HX_TP_PROC_GUEST_INFO + dbg_func_ptr_r[9] = himax_proc_guest_info_read; + dbg_func_ptr_w[9] = himax_proc_guest_info_write; +#endif + dbg_func_ptr_r[10] = himax_int_en_read; + dbg_func_ptr_w[10] = himax_int_en_write; + dbg_func_ptr_w[11] = himax_proc_register_write; + dbg_func_ptr_r[11] = himax_proc_register_read; + dbg_func_ptr_w[12] = himax_reset_write; + dbg_func_ptr_w[13] = himax_diag_arrange_write; + dbg_func_ptr_w[14] = himax_diag_cmd_write; +} + +int himax_touch_proc_init(void) +{ + himax_proc_diag_dir = proc_mkdir(HIMAX_PROC_DIAG_FOLDER, himax_touch_proc_dir); + + if (himax_proc_diag_dir == NULL) { + E(" %s: himax_proc_diag_dir file create failed!\n", __func__); + return -ENOMEM; + } + + himax_proc_vendor_file = proc_create(HIMAX_PROC_VENDOR_FILE, 0444, + himax_touch_proc_dir, &himax_proc_vendor_ops); + if (himax_proc_vendor_file == NULL) { + E(" %s: proc vendor file create failed!\n", __func__); + goto fail_1; + } + + himax_proc_stack_file = proc_create(HIMAX_PROC_STACK_FILE, 0444, + himax_proc_diag_dir, &himax_proc_stack_ops); + if (himax_proc_stack_file == NULL) { + E(" %s: proc stack file create failed!\n", __func__); + goto fail_2_1; + } + + himax_proc_iir_file = proc_create(HIMAX_PROC_IIR_FILE, 0444, + himax_proc_diag_dir, &himax_proc_iir_ops); + if (himax_proc_iir_file == NULL) { + E(" %s: proc iir file create failed!\n", __func__); + goto fail_2_2; + } + + himax_proc_dc_file = proc_create(HIMAX_PROC_DC_FILE, 0444, + himax_proc_diag_dir, &himax_proc_dc_ops); + if (himax_proc_dc_file == NULL) { + E(" %s: proc dc file create failed!\n", __func__); + goto fail_2_3; + } + + himax_proc_bank_file = proc_create(HIMAX_PROC_BANK_FILE, 0444, + himax_proc_diag_dir, &himax_proc_bank_ops); + if (himax_proc_bank_file == NULL) { + E(" %s: proc bank file create failed!\n", __func__); + goto fail_2_4; + } + + himax_proc_debug_file = proc_create(HIMAX_PROC_DEBUG_FILE, + 0644, himax_touch_proc_dir, + &himax_proc_debug_ops); + if (himax_proc_debug_file == NULL) { + E(" %s: proc debug file create failed!\n", __func__); + goto fail_3; + } + dbg_func_ptr_init(); + + himax_proc_flash_dump_file = proc_create(HIMAX_PROC_FLASH_DUMP_FILE, + 0644, himax_touch_proc_dir, + &himax_proc_flash_ops); + if (himax_proc_flash_dump_file == NULL) { + E(" %s: proc flash dump file create failed!\n", __func__); + goto fail_4; + } + +#if defined(HX_PEN_FUNC_EN) + himax_proc_pen_pos_file = proc_create(HIMAX_PROC_PEN_POS_FILE, + 0666, + himax_touch_proc_dir, &himax_proc_pen_pos_ops); + if (himax_proc_pen_pos_file == NULL) { + E(" %s: proc CRC test file create failed!\n", __func__); + goto fail_5; + } +#endif + + return 0; +#if defined(HX_PEN_FUNC_EN) + remove_proc_entry(HIMAX_PROC_PEN_POS_FILE, himax_touch_proc_dir); +fail_5: +#endif +remove_proc_entry(HIMAX_PROC_FLASH_DUMP_FILE, himax_touch_proc_dir); +fail_4: remove_proc_entry(HIMAX_PROC_DEBUG_FILE, himax_touch_proc_dir); +fail_3: remove_proc_entry(HIMAX_PROC_BANK_FILE, himax_proc_diag_dir); +fail_2_4: remove_proc_entry(HIMAX_PROC_DC_FILE, himax_proc_diag_dir); +fail_2_3: remove_proc_entry(HIMAX_PROC_IIR_FILE, himax_proc_diag_dir); +fail_2_2: remove_proc_entry(HIMAX_PROC_STACK_FILE, himax_proc_diag_dir); +fail_2_1: remove_proc_entry(HIMAX_PROC_VENDOR_FILE, himax_touch_proc_dir); +fail_1: + return -ENOMEM; +} + +void himax_touch_proc_deinit(void) +{ +#if defined(HX_PEN_FUNC_EN) + remove_proc_entry(HIMAX_PROC_PEN_POS_FILE, himax_touch_proc_dir); +#endif + remove_proc_entry(HIMAX_PROC_FLASH_DUMP_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_DEBUG_FILE, himax_touch_proc_dir); + remove_proc_entry(HIMAX_PROC_BANK_FILE, himax_proc_diag_dir); + remove_proc_entry(HIMAX_PROC_DC_FILE, himax_proc_diag_dir); + remove_proc_entry(HIMAX_PROC_IIR_FILE, himax_proc_diag_dir); + remove_proc_entry(HIMAX_PROC_STACK_FILE, himax_proc_diag_dir); + remove_proc_entry(HIMAX_PROC_VENDOR_FILE, himax_touch_proc_dir); +} + +int himax_debug_init(void) +{ + struct himax_ts_data *ts = private_ts; + int err = 0; + + I("%s:Enter\n", __func__); + + if (ts == NULL) { + E("%s: ts struct is NULL\n", __func__); + return -EPROBE_DEFER; + } + + reg_read_data = kzalloc(DEBUG_READ_BUF_SIZE * sizeof(uint8_t), GFP_KERNEL); + if (reg_read_data == NULL) { + err = -ENOMEM; + goto err_alloc_reg_read_data_fail; + } + + debug_data = kzalloc(sizeof(struct himax_debug), GFP_KERNEL); + if (debug_data == NULL) { /*Allocate debug data space*/ + err = -ENOMEM; + goto err_alloc_debug_data_fail; + } + + himax_himax_data_init(); + + ts->flash_wq = create_singlethread_workqueue("himax_flash_wq"); + + if (!ts->flash_wq) { + E("%s: create flash workqueue failed\n", __func__); + err = -ENOMEM; + goto err_create_flash_dump_wq_failed; + } + + INIT_WORK(&ts->flash_work, himax_ts_flash_work_func); + + g_flash_progress = START; + setFlashBuffer(); + +#ifdef HX_TP_PROC_GUEST_INFO + + if (g_guest_info_data == NULL) { + g_guest_info_data = kzalloc(sizeof(struct hx_guest_info), + GFP_KERNEL); + g_guest_info_data->g_guest_info_ongoing = 0; + g_guest_info_data->g_guest_info_type = 0; + } + + ts->guest_info_wq = + create_singlethread_workqueue("himax_guest_info_wq"); + if (!ts->guest_info_wq) { + E("%s: create guest info workqueue failed\n", __func__); + err = -ENOMEM; + goto err_create_guest_info_wq_failed; + } + INIT_WORK(&ts->guest_info_work, himax_ts_guest_info_work_func); +#endif + + ts->himax_diag_wq = create_singlethread_workqueue("himax_diag"); + + if (!ts->himax_diag_wq) { + E("%s: create diag workqueue failed\n", __func__); + err = -ENOMEM; + goto err_create_diag_wq_failed; + } + + INIT_DELAYED_WORK(&ts->himax_diag_delay_wrok, himax_ts_diag_work_func); + + setMutualBuffer(ic_data->HX_RX_NUM, ic_data->HX_TX_NUM); + if (getMutualBuffer() == NULL) { + E("%s: mutual buffer allocate fail failed\n", __func__); + return MEM_ALLOC_FAIL; + } + + setMutualNewBuffer(ic_data->HX_RX_NUM, ic_data->HX_TX_NUM); + if (getMutualNewBuffer() == NULL) { + E("%s: mutual new buffer allocate fail failed\n", __func__); + return MEM_ALLOC_FAIL; + } + + setMutualOldBuffer(ic_data->HX_RX_NUM, ic_data->HX_TX_NUM); + if (getMutualOldBuffer() == NULL) { + E("%s: mutual old buffer allocate fail failed\n", __func__); + return MEM_ALLOC_FAIL; + } + +#ifdef HX_TP_PROC_2T2R + + if (Is_2T2R) { + setMutualBuffer_2(ic_data->HX_RX_NUM_2, ic_data->HX_TX_NUM_2); + + if (getMutualBuffer_2() == NULL) { + E("%s: mutual buffer 2 allocate fail failed\n", + __func__); + return MEM_ALLOC_FAIL; + } + } +#endif + + himax_touch_proc_init(); + + return 0; + + cancel_delayed_work_sync(&ts->himax_diag_delay_wrok); + destroy_workqueue(ts->himax_diag_wq); +err_create_diag_wq_failed: + +#ifdef HX_TP_PROC_GUEST_INFO +err_create_guest_info_wq_failed: + + destroy_workqueue(ts->guest_info_wq); + if (g_guest_info_data != NULL) { + kfree(g_guest_info_data); + g_guest_info_data = NULL; + } +#endif + destroy_workqueue(ts->flash_wq); + +err_create_flash_dump_wq_failed: + kfree(debug_data); + debug_data = NULL; +err_alloc_debug_data_fail: + kfree(reg_read_data); + reg_read_data = NULL; +err_alloc_reg_read_data_fail: + + return err; +} +EXPORT_SYMBOL(himax_debug_init); + +int himax_debug_remove(void) +{ + struct himax_ts_data *ts = private_ts; + + himax_touch_proc_deinit(); + + cancel_delayed_work_sync(&ts->himax_diag_delay_wrok); +#ifdef HX_TP_PROC_GUEST_INFO + destroy_workqueue(ts->guest_info_wq); + if (g_guest_info_data != NULL) + kfree(g_guest_info_data); +#endif + destroy_workqueue(ts->himax_diag_wq); + destroy_workqueue(ts->flash_wq); + + kfree(debug_data); + + return 0; +} +EXPORT_SYMBOL(himax_debug_remove); + diff --git a/drivers/input/touchscreen/himax_v3_mmi/himax_debug.h b/drivers/input/touchscreen/himax_v3_mmi/himax_debug.h new file mode 100644 index 000000000000..586923f9e065 --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/himax_debug.h @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for debug nodes + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef H_HIMAX_DEBUG +#define H_HIMAX_DEBUG + +#include "himax_platform.h" +#include "himax_common.h" + + +#ifdef HX_ESD_RECOVERY + extern u8 HX_ESD_RESET_ACTIVATE; + extern int hx_EB_event_flag; + extern int hx_EC_event_flag; + extern int hx_ED_event_flag; +#endif + +#define HIMAX_PROC_VENDOR_FILE "vendor" +extern struct proc_dir_entry *himax_proc_vendor_file; + +#define HIMAX_PROC_PEN_POS_FILE "pen_pos" + +int himax_touch_proc_init(void); +void himax_touch_proc_deinit(void); +extern int himax_int_en_set(void); + +extern uint8_t byte_length; +extern uint8_t register_command[4]; +extern uint8_t cfg_flag; + +#define HIMAX_PROC_DIAG_FOLDER "diag" +struct proc_dir_entry *himax_proc_diag_dir; +#define HIMAX_PROC_STACK_FILE "stack" +extern struct proc_dir_entry *himax_proc_stack_file; +#define HIMAX_PROC_IIR_FILE "iir_s" +extern struct proc_dir_entry *himax_proc_iir_file; +#define HIMAX_PROC_DC_FILE "dc_s" +extern struct proc_dir_entry *himax_proc_dc_file; +#define HIMAX_PROC_BANK_FILE "bank_s" +extern struct proc_dir_entry *himax_proc_bank_file; + +#ifdef HX_TP_PROC_2T2R + extern uint32_t *diag_mutual_2; + + int32_t *getMutualBuffer_2(void); + void setMutualBuffer_2(uint8_t x_num, uint8_t y_num); +#endif +extern int32_t *diag_mutual; +extern int32_t *diag_mutual_new; +extern int32_t *diag_mutual_old; +extern uint8_t diag_max_cnt; +extern uint8_t hx_state_info[2]; +extern uint8_t diag_coor[128]; +extern int32_t diag_self[100]; +extern int32_t diag_self_new[100]; +extern int32_t diag_self_old[100]; +int32_t *getMutualBuffer(void); +int32_t *getMutualNewBuffer(void); +int32_t *getMutualOldBuffer(void); +int32_t *getSelfBuffer(void); +int32_t *getSelfNewBuffer(void); +int32_t *getSelfOldBuffer(void); +void setMutualBuffer(uint8_t x_num, uint8_t y_num); +void setMutualNewBuffer(uint8_t x_num, uint8_t y_num); +void setMutualOldBuffer(uint8_t x_num, uint8_t y_num); +uint8_t process_type; +uint8_t mode_flag; +uint8_t overflow; + +#define HIMAX_PROC_DEBUG_FILE "debug" +extern struct proc_dir_entry *himax_proc_debug_file; +extern bool fw_update_complete; +extern int handshaking_result; +extern unsigned char debug_level_cmd; +extern uint8_t cmd_set[8]; +extern uint8_t mutual_set_flag; + +#define HIMAX_PROC_FLASH_DUMP_FILE "flash_dump" +extern struct proc_dir_entry *himax_proc_flash_dump_file; +extern int Flash_Size; +extern uint8_t *flash_buffer; +extern uint8_t g_flash_cmd; +extern uint8_t g_flash_progress; +extern bool g_flash_dump_rst; /*Fail = 0, Pass = 1*/ +void setFlashBuffer(void); + +enum flash_dump_prog { + START, + ONGOING, + FINISHED, +}; + +extern uint32_t **raw_data_array; +extern uint8_t X_NUM4; +extern uint8_t Y_NUM; +extern uint8_t sel_type; + +/* Moved from debug.c */ +extern struct himax_debug *debug_data; +extern unsigned char IC_CHECKSUM; +extern int i2c_error_count; +extern struct proc_dir_entry *himax_touch_proc_dir; + +#ifdef HX_TP_PROC_GUEST_INFO +extern struct hx_guest_info *g_guest_info_data; +extern char *g_guest_info_item[]; +#endif + +extern int himax_input_register(struct himax_ts_data *ts); +#ifdef HX_TP_PROC_2T2R + extern bool Is_2T2R; +#endif + +#ifdef HX_RST_PIN_FUNC + extern void himax_ic_reset(uint8_t loadconfig, uint8_t int_off); +#endif + +#if defined(HX_ZERO_FLASH) +extern char *i_CTPM_firmware_name; +#endif + +extern uint8_t HX_PROC_SEND_FLAG; +extern struct himax_target_report_data *g_target_report_data; +extern struct himax_report_data *hx_touch_data; +extern int g_ts_dbg; + +/* Moved from debug.c end */ +#define BUF_SIZE 1024 +#define CMD_NUM 15 +char *dbg_cmd_str[] = {"crc_test", "fw_debug", "attn", "layout", "dd_debug", + "esd_cnt", "senseonoff", "debug_level", "guest_info", "int_en", + "register", "reset", "diag_arr", "diag", NULL}; +int dbg_cmd_flag; +char *dbg_cmd_par; +ssize_t (*dbg_func_ptr_r[CMD_NUM])(char *buf, size_t len); +ssize_t (*dbg_func_ptr_w[CMD_NUM])(char *buf, size_t len); +#endif diff --git a/drivers/input/touchscreen/himax_v3_mmi/himax_ic_core.h b/drivers/input/touchscreen/himax_v3_mmi/himax_ic_core.h new file mode 100644 index 000000000000..f6010d6ac244 --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/himax_ic_core.h @@ -0,0 +1,982 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for ic core functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef __HIMAX_IC_CORE_H__ +#define __HIMAX_IC_CORE_H__ + +#include "himax_platform.h" +#include "himax_common.h" +#include + +#define DATA_LEN_8 8 +#define DATA_LEN_4 4 +#define ADDR_LEN_4 4 +#define FLASH_RW_MAX_LEN 256 +#define FLASH_WRITE_BURST_SZ 8 +#define PROGRAM_SZ 48 +#define MAX_I2C_TRANS_SZ 128 +#define HIMAX_REG_RETRY_TIMES 5 +#define FW_BIN_16K_SZ 0x4000 +#define HIMAX_TOUCH_DATA_SIZE 128 +#define MASK_BIT_0 0x01 +#define MASK_BIT_1 0x02 +#define MASK_BIT_2 0x04 + +#define FW_SECTOR_PER_BLOCK 8 +#define FW_PAGE_PER_SECTOR 64 +#define FW_PAGE_SEZE 128 +#define HX256B 0x100 +#define HX4K 0x1000 +#define HX_32K_SZ 0x8000 +#define HX_40K_SZ 0xA000 +#define HX_48K_SZ 0xC000 +#define HX64K 0x10000 +#define HX124K 0x1f000 +#define HX4000K 0x1000000 + +#define HX_NORMAL_MODE 1 +#define HX_SORTING_MODE 2 +#define HX_CHANGE_MODE_FAIL (-1) +#define HX_RW_REG_FAIL (-1) +#define HX_DRIVER_MAX_IC_NUM 12 + +#if defined(__HIMAX_HX852xG_MOD__) +#define HX_MOD_KSYM_HX852xG HX_MOD_KSYM_HX852xG +#endif + +#if defined(__HIMAX_HX852xH_MOD__) +#define HX_MOD_KSYM_HX852xH HX_MOD_KSYM_HX852xH +#endif + +#if defined(__HIMAX_HX83102_MOD__) +#define HX_MOD_KSYM_HX83102 HX_MOD_KSYM_HX83102 +#endif + +#if defined(__HIMAX_HX83102D_MOD__) +#define HX_MOD_KSYM_HX83102 HX_MOD_KSYM_HX83102 +#define HX83102D +#endif + +#if defined(__HIMAX_HX83103_MOD__) +#define HX_MOD_KSYM_HX83103 HX_MOD_KSYM_HX83103 +#endif + +#if defined(__HIMAX_HX83106_MOD__) +#define HX_MOD_KSYM_HX83106 HX_MOD_KSYM_HX83106 +#endif + +#if defined(__HIMAX_HX83111_MOD__) +#define HX_MOD_KSYM_HX83111 HX_MOD_KSYM_HX83111 +#endif + +#if defined(__HIMAX_HX83112_MOD__) +#define HX_MOD_KSYM_HX83112 HX_MOD_KSYM_HX83112 +#endif + +#if defined(__HIMAX_HX83191_MOD__) +#define HX_MOD_KSYM_HX83191 HX_MOD_KSYM_HX83191 +#endif + +#if defined(__HIMAX_HX83113_MOD__) +#define HX_MOD_KSYM_HX83113 HX_MOD_KSYM_HX83113 +#endif + +#define CORE_INIT +#define CORE_IC +#define CORE_FW +#define CORE_FLASH +#define CORE_SRAM +#define CORE_DRIVER + +#define HX_0F_DEBUG + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_INCELL + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) + extern char *i_CTPM_firmware_name; +#endif + +#ifdef HX_ZERO_FLASH + extern int g_f_0f_updat; +#endif + +#ifdef HX_TP_PROC_GUEST_INFO + extern struct hx_guest_info *g_guest_info_data; +#endif + +void himax_mcu_in_cmd_struct_free(void); + +#endif + +#ifdef CONFIG_TOUCHSCREEN_HIMAX_ONCELL + +void himax_mcu_on_cmd_struct_free(void); + +#endif + +#ifdef HX_AUTO_UPDATE_FW +extern int g_i_FW_VER; +extern int g_i_CFG_VER; +extern int g_i_CID_MAJ; +extern int g_i_CID_MIN; +extern unsigned char *i_CTPM_FW; +#endif + +extern unsigned long FW_VER_MAJ_FLASH_ADDR; +extern unsigned long FW_VER_MIN_FLASH_ADDR; +extern unsigned long CFG_VER_MAJ_FLASH_ADDR; +extern unsigned long CFG_VER_MIN_FLASH_ADDR; +extern unsigned long CID_VER_MAJ_FLASH_ADDR; +extern unsigned long CID_VER_MIN_FLASH_ADDR; + +extern unsigned long FW_VER_MAJ_FLASH_LENG; +extern unsigned long FW_VER_MIN_FLASH_LENG; +extern unsigned long CFG_VER_MAJ_FLASH_LENG; +extern unsigned long CFG_VER_MIN_FLASH_LENG; +extern unsigned long CID_VER_MAJ_FLASH_LENG; +extern unsigned long CID_VER_MIN_FLASH_LENG; + +extern unsigned char IC_CHECKSUM; +#ifdef HX_ESD_RECOVERY +extern int g_zero_event_count; +#endif + +#ifdef HX_RST_PIN_FUNC +extern u8 HX_HW_RESET_ACTIVATE; +void himax_rst_gpio_set(int pinnum, uint8_t value); +#endif + +#if defined(HX_USB_DETECT_GLOBAL) +void himax_cable_detect_func(bool force_renew); +#endif + +int himax_report_data_init(void); +extern int i2c_error_count; + +#ifdef HX_ESD_RECOVERY + extern u8 HX_ESD_RESET_ACTIVATE; +#endif + +#ifdef CORE_INIT + int himax_mcu_in_cmd_struct_init(void); + void himax_in_parse_assign_cmd(uint32_t addr, uint8_t *cmd, int len); + void himax_mcu_in_cmd_init(void); + + int himax_mcu_on_cmd_struct_init(void); + void himax_on_parse_assign_cmd(uint32_t addr, uint8_t *cmd, int len); + void himax_mcu_on_cmd_init(void); + + extern void (*himax_mcu_cmd_struct_free)(void); +#endif + +#ifdef HX_TP_PROC_GUEST_INFO +#define HX_GUEST_INFO_SIZE 10 +#define HX_GUEST_INFO_LEN_SIZE 4 +#define HX_GUEST_INFO_ID_SIZE 4 + +struct hx_guest_info { + int g_guest_info_ongoing; /* 0 stop, 1 ongoing */ + uint8_t g_guest_str[10][128]; + uint8_t g_guest_str_in_format[10][128]; + uint8_t g_guest_data_type[10]; + int g_guest_data_len[10]; + int g_guest_info_type; +}; + +#endif + +#if defined(CORE_IC) + #define ic_adr_ahb_addr_byte_0 0x00 + #define ic_adr_ahb_rdata_byte_0 0x08 + #define ic_adr_ahb_access_direction 0x0c + #define ic_adr_conti 0x13 + #define ic_adr_incr4 0x0D + #define ic_adr_i2c_psw_lb 0x31 + #define ic_adr_i2c_psw_ub 0x32 + #define ic_cmd_ahb_access_direction_read 0x00 + #define ic_cmd_conti 0x31 + #define ic_cmd_incr4 0x10 + #define ic_cmd_i2c_psw_lb 0x27 + #define ic_cmd_i2c_psw_ub 0x95 + #define ic_adr_tcon_on_rst 0x80020020 + #define ic_addr_adc_on_rst 0x80020094 + #define ic_adr_psl 0x900000A0 + #define ic_adr_cs_central_state 0x900000A8 + #define ic_cmd_rst 0x00000000 + #define ic_adr_osc_en 0x900880A8 + #define ic_adr_osc_pw 0x900880E0 + + #define on_ic_adr_ahb_addr_byte_0 0x00 + #define on_ic_adr_ahb_rdata_byte_0 0x08 + #define on_ic_adr_ahb_access_direction 0x0c + #define on_ic_adr_conti 0x13 + #define on_ic_adr_incr4 0x0D + #define on_ic_cmd_ahb_access_direction_read 0x00 + #define on_ic_cmd_conti 0x31 + #define on_ic_cmd_incr4 0x10 + #define on_ic_adr_mcu_ctrl 0x82 + #define on_ic_cmd_mcu_on 0x25 + #define on_ic_cmd_mcu_off 0xDA + #define on_ic_adr_sleep_ctrl 0x99 + #define on_ic_cmd_sleep_in 0x80 + #define on_ic_adr_tcon_ctrl 0x80020000 + #define on_ic_cmd_tcon_on 0x00000000 + #define on_ic_adr_wdg_ctrl 0x9000800C + #define on_ic_cmd_wdg_psw 0x0000AC53 + #define on_ic_adr_wdg_cnt_ctrl 0x90008010 + #define on_ic_cmd_wdg_cnt_clr 0x000035CA +#endif + +#if defined(CORE_FW) + #define fw_addr_system_reset 0x90000018 + #define fw_addr_safe_mode_release_pw 0x90000098 + #define fw_addr_ctrl_fw 0x9000005c + #define fw_addr_flag_reset_event 0x900000e4 + #define fw_addr_hsen_enable 0x10007F14 + #define fw_addr_smwp_enable 0x10007F10 + #define fw_usb_detect_addr 0x10007F38 + #define fw_addr_program_reload_from 0x00000000 + #define fw_addr_program_reload_to 0x08000000 + #define fw_addr_program_reload_page_write 0x0000fb00 + #define fw_addr_raw_out_sel 0x800204b4 + #define fw_addr_reload_status 0x80050000 + #define fw_addr_reload_crc32_result 0x80050018 + #define fw_addr_reload_addr_from 0x80050020 + #define fw_addr_reload_addr_cmd_beat 0x80050028 + #define fw_data_system_reset 0x00000055 + #define fw_data_safe_mode_release_pw_active 0x00000053 + #define fw_data_safe_mode_release_pw_reset 0x00000000 + #define fw_data_clear 0x00000000 + #define fw_data_fw_stop 0x000000A5 + #define fw_data_program_reload_start 0x0A3C3000 + #define fw_data_program_reload_compare 0x04663000 + #define fw_data_program_reload_break 0x15E75678 + #define fw_addr_selftest_addr_en 0x10007F18 + #define fw_addr_selftest_result_addr 0x10007f24 + #define fw_data_selftest_request 0x00006AA6 + #define fw_addr_criteria_addr 0x10007f1c + #define fw_data_criteria_aa_top 0x64 + #define fw_data_criteria_aa_bot 0x00 + #define fw_data_criteria_key_top 0x64 + #define fw_data_criteria_key_bot 0x00 + #define fw_data_criteria_avg_top 0x64 + #define fw_data_criteria_avg_bot 0x00 + #define fw_addr_set_frame_addr 0x10007294 + #define fw_data_set_frame 0x0000000A + #define fw_data_selftest_ack_hb 0xa6 + #define fw_data_selftest_ack_lb 0x6a + #define fw_data_selftest_pass 0xaa + #define fw_data_normal_cmd 0x00 + #define fw_data_normal_status 0x99 + #define fw_data_sorting_cmd 0xaa + #define fw_data_sorting_status 0xcc + #define fw_data_idle_dis_pwd 0x17 + #define fw_data_idle_en_pwd 0x1f + #define fw_addr_sorting_mode_en 0x10007f04 + #define fw_addr_max_dc 0x10007fc8 + #define fw_addr_fw_mode_status 0x10007088 + #define fw_addr_icid_addr 0x900000d0 + #define fw_addr_trigger_addr 0x10007088 + #define fw_addr_fw_ver_addr 0x10007004 + #define fw_addr_fw_cfg_addr 0x10007084 + #define fw_addr_fw_vendor_addr 0x10007000 + #define fw_addr_cus_info 0x10007008 + #define fw_addr_proj_info 0x10007014 + #define fw_addr_fw_state_addr 0x900000f8 + #define fw_addr_fw_dbg_msg_addr 0x10007f40 + #define fw_addr_chk_fw_status 0x900000a8 + #define fw_addr_dd_handshak_addr 0x900000fc + #define fw_addr_dd_data_addr 0x10007f80 + #define fw_data_dd_request 0xaa + #define fw_data_dd_ack 0xbb + #define fw_data_rawdata_ready_hb 0xa3 + #define fw_data_rawdata_ready_lb 0x3a + #define fw_addr_ahb_addr 0x11 + #define fw_data_ahb_dis 0x00 + #define fw_data_ahb_en 0x01 + #define fw_addr_event_addr 0x30 + #define fw_func_handshaking_pwd 0xA55AA55A + #define fw_func_handshaking_end 0x77887788 + #define fw_addr_ulpm_33 0x33 + #define fw_addr_ulpm_34 0x34 + #define fw_data_ulpm_11 0x11 + #define fw_data_ulpm_22 0x22 + #define fw_data_ulpm_33 0x33 + #define fw_data_ulpm_aa 0xAA + + #define fw_func_edge_portrait_pwd 0xA55AA55A + #define fw_func_edge_lsp_left_pwd 0xA11AA11A + #define fw_func_edge_lsp_right_pwd 0xA33AA33A + + #define on_fw_addr_smwp_enable 0xA2 + #define on_fw_usb_detect_addr 0xA4 + #define on_fw_addr_program_reload_from 0x00000000 + #define on_fw_addr_raw_out_sel 0x98 + #define on_fw_addr_flash_checksum 0x80000044 + #define on_fw_data_flash_checksum 0x00000491 + #define on_fw_addr_crc_value 0x80000050 + #define on_fw_data_safe_mode_release_pw_active 0x00000053 + #define on_fw_data_safe_mode_release_pw_reset 0x00000000 + #define on_fw_addr_criteria_addr 0x9A + #define on_fw_data_selftest_pass 0xaa + #define on_fw_addr_reK_crtl 0x8000000C + #define on_fw_data_reK_en 0x02 + #define on_fw_data_reK_dis 0xFD + #define on_fw_data_rst_init 0xF0 + #define on_fw_data_dc_set 0x02 + #define on_fw_data_bank_set 0x03 + #define on_fw_addr_selftest_addr_en 0x98 + #define on_fw_addr_selftest_result_addr 0x9B + #define on_fw_data_selftest_request 0x06 + #define on_fw_data_thx_avg_mul_dc_lsb 0x22 + #define on_fw_data_thx_avg_mul_dc_msb 0x0B + #define on_fw_data_thx_mul_dc_up_low_bud 0x64 + #define on_fw_data_thx_avg_slf_dc_lsb 0x14 + #define on_fw_data_thx_avg_slf_dc_msb 0x05 + #define on_fw_data_thx_slf_dc_up_low_bud 0x64 + #define on_fw_data_thx_slf_bank_up 0x40 + #define on_fw_data_thx_slf_bank_low 0x00 + #define on_fw_data_idle_dis_pwd 0x40 + #define on_fw_data_idle_en_pwd 0x00 + #define on_fw_addr_fw_mode_status 0x99 + #define on_fw_addr_icid_addr 0x900000d0 + #define on_fw_addr_trigger_addr 0x10007088 + #define on_fw_addr_fw_ver_start 0x90 + #define on_fw_data_rawdata_ready_hb 0xa3 + #define on_fw_data_rawdata_ready_lb 0x3a + #define on_fw_addr_ahb_addr 0x11 + #define on_fw_data_ahb_dis 0x00 + #define on_fw_data_ahb_en 0x01 + #define on_fw_addr_event_addr 0x30 +#endif + +#if defined(CORE_FLASH) + #define flash_addr_ctrl_base 0x80000000 + #define flash_addr_spi200_trans_fmt (flash_addr_ctrl_base + 0x10) + #define flash_addr_spi200_trans_ctrl (flash_addr_ctrl_base + 0x20) + #define flash_addr_spi200_cmd (flash_addr_ctrl_base + 0x24) + #define flash_addr_spi200_addr (flash_addr_ctrl_base + 0x28) + #define flash_addr_spi200_data (flash_addr_ctrl_base + 0x2c) + #define flash_addr_spi200_bt_num (flash_addr_ctrl_base + 0xe8) + #define flash_data_spi200_trans_fmt 0x00020780 + #define flash_data_spi200_trans_ctrl_1 0x42000003 + #define flash_data_spi200_trans_ctrl_2 0x47000000 + #define flash_data_spi200_trans_ctrl_3 0x67000000 + #define flash_data_spi200_trans_ctrl_4 0x610ff000 + #define flash_data_spi200_trans_ctrl_5 0x694002ff + #define flash_data_spi200_cmd_1 0x00000005 + #define flash_data_spi200_cmd_2 0x00000006 + #define flash_data_spi200_cmd_3 0x000000C7 + #define flash_data_spi200_cmd_4 0x000000D8 + #define flash_data_spi200_cmd_5 0x00000020 + #define flash_data_spi200_cmd_6 0x00000002 + #define flash_data_spi200_cmd_7 0x0000003b + #define flash_data_spi200_addr 0x00000000 + + #define on_flash_addr_ctrl_base 0x80000000 + #define on_flash_addr_ctrl_auto 0x80000004 + #define on_flash_data_main_erase 0x0000A50D + #define on_flash_data_auto 0xA5 + #define on_flash_data_main_read 0x03 + #define on_flash_data_page_write 0x05 + #define on_flash_data_spp_read 0x10 + #define on_flash_data_sfr_read 0x14 + #define on_flash_addr_ahb_ctrl 0x80000020 + #define on_flash_data_ahb_squit 0x00000001 + #define on_flash_addr_unlock_0 0x00000000 + #define on_flash_addr_unlock_4 0x00000004 + #define on_flash_addr_unlock_8 0x00000008 + #define on_flash_addr_unlock_c 0x0000000C + #define on_flash_data_cmd0 0x28178EA0 + #define on_flash_data_cmd1 0x0A0E03FF + #define on_flash_data_cmd2 0x8C203D0C + #define on_flash_data_cmd3 0x00300263 + #define on_flash_data_lock 0x03400000 +#endif + +#if defined(CORE_SRAM) + #define sram_adr_mkey 0x100070E8 + #define sram_adr_rawdata_addr 0x10000000 + #define sram_adr_rawdata_end 0x00000000 + #define sram_passwrd_start 0x5AA5 + #define sram_passwrd_end 0xA55A + + #define on_sram_adr_rawdata_addr 0x080002E0 + #define on_sram_adr_rawdata_end 0x00000000 + #define on_sram_cmd_conti 0x44332211 + #define on_sram_cmd_fin 0x00000000 + #define on_sram_passwrd_start 0x5AA5 + #define on_sram_passwrd_end 0xA55A +#endif + +#if defined(CORE_DRIVER) + #define driver_addr_fw_define_flash_reload 0x10007f00 + #define driver_addr_fw_define_2nd_flash_reload 0x100072c0 + #define driver_data_fw_define_flash_reload_dis 0x0000a55a + #define driver_data_fw_define_flash_reload_en 0x00000000 + #define driver_addr_fw_define_int_is_edge 0x10007088 + #define driver_addr_fw_define_rxnum_txnum_maxpt 0x100070f4 + #define driver_data_fw_define_rxnum_txnum_maxpt_sorting 0x00000008 + #define driver_data_fw_define_rxnum_txnum_maxpt_normal 0x00000014 + #define driver_addr_fw_define_xy_res_enable 0x100070f8 + #define driver_addr_fw_define_x_y_res 0x100070fc + #define driver_data_df_rx 36 + #define driver_data_df_tx 18 + #define driver_data_df_pt 10 + #define driver_data_df_x_res 1080 + #define driver_data_df_y_res 1920 + #define on_driver_addr_fw_define_int_is_edge 0x10007088 + #define on_driver_data_df_rx 28 + #define on_driver_data_df_tx 14 + #define on_driver_data_df_pt 10 + #define on_driver_data_df_x_res 1080 + #define on_driver_data_df_y_res 1920 +#ifndef HX_NEW_EVENT_STACK_FORMAT + #define on_driver_addr_fw_rx_tx_maxpt_num 0x0800001C + #define on_driver_addr_fw_xy_rev_int_edge 0x0800000C + #define on_driver_addr_fw_define_x_y_res 0x08000030 +#else + #define on_driver_addr_fw_rx_tx_maxpt_num 0x08000004 + #define on_driver_addr_fw_maxpt_bt_num 0x0800000C + #define on_driver_addr_fw_xy_rev_int_edge 0x08000110 + #define on_driver_addr_fw_define_x_y_res 0x08000010 +#endif + +#endif + +#if defined(HX_ZERO_FLASH) + #define zf_addr_dis_flash_reload 0x10007f00 + #define zf_data_dis_flash_reload 0x00009AA9 + #define zf_addr_system_reset 0x90000018 + #define zf_data_system_reset 0x00000055 + #define zf_data_sram_start_addr 0x08000000 + #define zf_data_sram_clean 0x10000000 + #define zf_data_cfg_info 0x10007000 + #define zf_data_fw_cfg_1 0x10007084 + #define zf_data_fw_cfg_2 0x10007264 + #define zf_data_fw_cfg_3 0x10007300 + #define zf_data_adc_cfg_1 0x10006A00 + #define zf_data_adc_cfg_2 0x10007B28 + #define zf_data_adc_cfg_3 0x10007AF0 + #define zf_data_map_table 0x10007500 + #define zf_data_mode_switch 0x10007294 + #define zf_addr_sts_chk 0x900000A8 + #define zf_data_activ_sts 0x05 + #define zf_addr_activ_relod 0x90000048 + #define zf_data_activ_in 0xEC + +#if defined(HX_CODE_OVERLAY) +#if defined(HX83102D) + #define ovl_section_num 3 + #define ovl_gesture_request 0x11 + #define ovl_gesture_reply 0x22 + #define ovl_border_request 0x55 + #define ovl_border_reply 0x66 + #define ovl_sorting_request 0x99 + #define ovl_sorting_reply 0xAA + #define ovl_fault 0xFF +#else + #define ovl_isram_addr 0x20008CE0 + #define ovl_handshaking_addr 0x10007FFC + #define ovl_gesture_request 0x11 + #define ovl_gesture_reply 0x22 + #define ovl_border_request 0x55 + #define ovl_border_reply 0x66 +#endif +#endif + +struct zf_info { + uint8_t sram_addr[4]; + int write_size; + uint32_t fw_addr; + uint32_t cfg_addr; +}; +#endif + +#ifdef HIMAX_PALM_SENSOR_EN + #define palm_detection_register 0x10007FF4 + #define palm_detection_register_enable 0xA55AA55A + #define palm_detection_register_disable 0x00000000 +#endif + +struct ic_operation { + uint8_t addr_ahb_addr_byte_0[1]; + uint8_t addr_ahb_rdata_byte_0[1]; + uint8_t addr_ahb_access_direction[1]; + uint8_t addr_conti[1]; + uint8_t addr_incr4[1]; + uint8_t adr_i2c_psw_lb[1]; + uint8_t adr_i2c_psw_ub[1]; + uint8_t data_ahb_access_direction_read[1]; + uint8_t data_conti[1]; + uint8_t data_incr4[1]; + uint8_t data_i2c_psw_lb[1]; + uint8_t data_i2c_psw_ub[1]; + uint8_t addr_tcon_on_rst[4]; + uint8_t addr_adc_on_rst[4]; + uint8_t addr_psl[4]; + uint8_t addr_cs_central_state[4]; + uint8_t data_rst[4]; + uint8_t adr_osc_en[4]; + uint8_t adr_osc_pw[4]; +}; + +struct fw_operation { + uint8_t addr_system_reset[4]; + uint8_t addr_safe_mode_release_pw[4]; + uint8_t addr_ctrl_fw_isr[4]; + uint8_t addr_flag_reset_event[4]; + uint8_t addr_hsen_enable[4]; + uint8_t addr_smwp_enable[4]; +#ifdef HX_EDGE_LIMIT + uint8_t addr_edge_limit_enable[4]; +#endif +#ifdef HIMAX_PALM_SENSOR_EN + uint8_t addr_palm_detection_enable[4]; +#endif + uint8_t addr_program_reload_from[4]; + uint8_t addr_program_reload_to[4]; + uint8_t addr_program_reload_page_write[4]; + uint8_t addr_raw_out_sel[4]; + uint8_t addr_reload_status[4]; + uint8_t addr_reload_crc32_result[4]; + uint8_t addr_reload_addr_from[4]; + uint8_t addr_reload_addr_cmd_beat[4]; + uint8_t addr_selftest_addr_en[4]; + uint8_t addr_criteria_addr[4]; + uint8_t addr_set_frame_addr[4]; + uint8_t addr_selftest_result_addr[4]; + uint8_t addr_sorting_mode_en[4]; + uint8_t addr_max_dc[4]; + uint8_t addr_fw_mode_status[4]; + uint8_t addr_icid_addr[4]; + uint8_t addr_trigger_addr[4]; + uint8_t addr_fw_ver_addr[4]; + uint8_t addr_fw_cfg_addr[4]; + uint8_t addr_fw_vendor_addr[4]; + uint8_t addr_cus_info[4]; + uint8_t addr_proj_info[4]; + uint8_t addr_fw_state_addr[4]; + uint8_t addr_fw_dbg_msg_addr[4]; + uint8_t addr_chk_fw_status[4]; + uint8_t addr_dd_handshak_addr[4]; + uint8_t addr_dd_data_addr[4]; + uint8_t data_system_reset[4]; + uint8_t data_safe_mode_release_pw_active[4]; + uint8_t data_safe_mode_release_pw_reset[4]; + uint8_t data_clear[4]; + uint8_t data_fw_stop[4]; + uint8_t data_program_reload_start[4]; + uint8_t data_program_reload_compare[4]; + uint8_t data_program_reload_break[4]; + uint8_t data_selftest_request[4]; + uint8_t data_criteria_aa_top[1]; + uint8_t data_criteria_aa_bot[1]; + uint8_t data_criteria_key_top[1]; + uint8_t data_criteria_key_bot[1]; + uint8_t data_criteria_avg_top[1]; + uint8_t data_criteria_avg_bot[1]; + uint8_t data_set_frame[4]; + uint8_t data_selftest_ack_hb[1]; + uint8_t data_selftest_ack_lb[1]; + uint8_t data_selftest_pass[1]; + uint8_t data_normal_cmd[1]; + uint8_t data_normal_status[1]; + uint8_t data_sorting_cmd[1]; + uint8_t data_sorting_status[1]; + uint8_t data_dd_request[1]; + uint8_t data_dd_ack[1]; + uint8_t data_idle_dis_pwd[1]; + uint8_t data_idle_en_pwd[1]; + uint8_t data_rawdata_ready_hb[1]; + uint8_t data_rawdata_ready_lb[1]; + uint8_t addr_ahb_addr[1]; + uint8_t data_ahb_dis[1]; + uint8_t data_ahb_en[1]; + uint8_t addr_event_addr[1]; + uint8_t addr_usb_detect[4]; + uint8_t addr_ulpm_33[1]; + uint8_t addr_ulpm_34[1]; + uint8_t data_ulpm_11[1]; + uint8_t data_ulpm_22[1]; + uint8_t data_ulpm_33[1]; + uint8_t data_ulpm_aa[1]; +}; + +struct flash_operation { + uint8_t addr_spi200_trans_fmt[4]; + uint8_t addr_spi200_trans_ctrl[4]; + uint8_t addr_spi200_cmd[4]; + uint8_t addr_spi200_addr[4]; + uint8_t addr_spi200_data[4]; + uint8_t addr_spi200_bt_num[4]; + + uint8_t data_spi200_trans_fmt[4]; + uint8_t data_spi200_trans_ctrl_1[4]; + uint8_t data_spi200_trans_ctrl_2[4]; + uint8_t data_spi200_trans_ctrl_3[4]; + uint8_t data_spi200_trans_ctrl_4[4]; + uint8_t data_spi200_trans_ctrl_5[4]; + uint8_t data_spi200_cmd_1[4]; + uint8_t data_spi200_cmd_2[4]; + uint8_t data_spi200_cmd_3[4]; + uint8_t data_spi200_cmd_4[4]; + uint8_t data_spi200_cmd_5[4]; + uint8_t data_spi200_cmd_6[4]; + uint8_t data_spi200_cmd_7[4]; + uint8_t data_spi200_addr[4]; +}; + +struct sram_operation { + uint8_t addr_mkey[4]; + uint8_t addr_rawdata_addr[4]; + uint8_t addr_rawdata_end[4]; + uint8_t passwrd_start[2]; + uint8_t passwrd_end[2]; +}; + +struct driver_operation { + uint8_t addr_fw_define_flash_reload[4]; + uint8_t addr_fw_define_2nd_flash_reload[4]; + uint8_t addr_fw_define_int_is_edge[4]; + uint8_t addr_fw_define_rxnum_txnum_maxpt[4]; + uint8_t addr_fw_define_xy_res_enable[4]; + uint8_t addr_fw_define_x_y_res[4]; + uint8_t data_df_rx[1]; + uint8_t data_df_tx[1]; + uint8_t data_df_pt[1]; + uint8_t data_df_x_res[2]; + uint8_t data_df_y_res[2]; + uint8_t data_fw_define_flash_reload_dis[4]; + uint8_t data_fw_define_flash_reload_en[4]; + uint8_t data_fw_define_rxnum_txnum_maxpt_sorting[4]; + uint8_t data_fw_define_rxnum_txnum_maxpt_normal[4]; +}; + +struct zf_operation { + uint8_t addr_dis_flash_reload[4]; + uint8_t data_dis_flash_reload[4]; + uint8_t addr_system_reset[4]; + uint8_t data_system_reset[4]; + uint8_t data_sram_start_addr[4]; + uint8_t data_sram_clean[4]; + uint8_t data_cfg_info[4]; + uint8_t data_fw_cfg_1[4]; + uint8_t data_fw_cfg_2[4]; + uint8_t data_fw_cfg_3[4]; + uint8_t data_adc_cfg_1[4]; + uint8_t data_adc_cfg_2[4]; + uint8_t data_adc_cfg_3[4]; + uint8_t data_map_table[4]; + uint8_t data_mode_switch[4]; + uint8_t addr_sts_chk[4]; + uint8_t data_activ_sts[1]; + uint8_t addr_activ_relod[4]; + uint8_t data_activ_in[1]; +}; + +struct himax_core_command_operation { + struct ic_operation *ic_op; + struct fw_operation *fw_op; + struct flash_operation *flash_op; + struct sram_operation *sram_op; + struct driver_operation *driver_op; + struct zf_operation *zf_op; +}; + +struct on_ic_operation { + uint8_t addr_ahb_addr_byte_0[1]; + uint8_t addr_ahb_rdata_byte_0[1]; + uint8_t addr_ahb_access_direction[1]; + uint8_t addr_conti[1]; + uint8_t addr_incr4[1]; + uint8_t adr_mcu_ctrl[1]; + uint8_t data_ahb_access_direction_read[1]; + uint8_t data_conti[1]; + uint8_t data_incr4[1]; + uint8_t cmd_mcu_on[1]; + uint8_t cmd_mcu_off[1]; + uint8_t adr_sleep_ctrl[1]; + uint8_t cmd_sleep_in[1]; + uint8_t adr_tcon_ctrl[4]; + uint8_t cmd_tcon_on[4]; + uint8_t adr_wdg_ctrl[4]; + uint8_t cmd_wdg_psw[4]; + uint8_t adr_wdg_cnt_ctrl[4]; + uint8_t cmd_wdg_cnt_clr[4]; +}; + +struct on_fw_operation { + uint8_t addr_smwp_enable[1]; + uint8_t addr_program_reload_from[4]; + uint8_t addr_raw_out_sel[1]; + uint8_t addr_flash_checksum[4]; + uint8_t data_flash_checksum[4]; + uint8_t addr_crc_value[4]; + uint8_t addr_reload_status[4]; + uint8_t addr_reload_crc32_result[4]; + uint8_t addr_reload_addr_from[4]; + uint8_t addr_reload_addr_cmd_beat[4]; + uint8_t addr_set_frame_addr[4]; + uint8_t addr_fw_mode_status[1]; + uint8_t addr_icid_addr[4]; + uint8_t addr_trigger_addr[4]; + uint8_t addr_fw_ver_start[1]; + uint8_t data_safe_mode_release_pw_active[4]; + uint8_t data_safe_mode_release_pw_reset[4]; + uint8_t data_clear[4]; + uint8_t addr_criteria_addr[1]; + uint8_t data_selftest_pass[1]; + uint8_t addr_reK_crtl[4]; + uint8_t data_reK_en[1]; + uint8_t data_reK_dis[1]; + uint8_t data_rst_init[1]; + uint8_t data_dc_set[1]; + uint8_t data_bank_set[1]; + uint8_t addr_selftest_addr_en[1]; + uint8_t addr_selftest_result_addr[1]; + uint8_t data_selftest_request[1]; + uint8_t data_thx_avg_mul_dc_lsb[1]; + uint8_t data_thx_avg_mul_dc_msb[1]; + uint8_t data_thx_mul_dc_up_low_bud[1]; + uint8_t data_thx_avg_slf_dc_lsb[1]; + uint8_t data_thx_avg_slf_dc_msb[1]; + uint8_t data_thx_slf_dc_up_low_bud[1]; + uint8_t data_thx_slf_bank_up[1]; + uint8_t data_thx_slf_bank_low[1]; + uint8_t data_idle_dis_pwd[1]; + uint8_t data_idle_en_pwd[1]; + uint8_t data_rawdata_ready_hb[1]; + uint8_t data_rawdata_ready_lb[1]; + uint8_t addr_ahb_addr[1]; + uint8_t data_ahb_dis[1]; + uint8_t data_ahb_en[1]; + uint8_t addr_event_addr[1]; + uint8_t addr_usb_detect[1]; +}; + +struct on_flash_operation { + uint8_t addr_ctrl_base[4]; + uint8_t addr_ctrl_auto[4]; + uint8_t data_main_erase[4]; + uint8_t data_auto[1]; + uint8_t data_main_read[1]; + uint8_t data_page_write[1]; + uint8_t data_sfr_read[1]; + uint8_t data_spp_read[1]; + uint8_t addr_ahb_ctrl[4]; + uint8_t data_ahb_squit[4]; + + uint8_t addr_unlock_0[4]; + uint8_t addr_unlock_4[4]; + uint8_t addr_unlock_8[4]; + uint8_t addr_unlock_c[4]; + uint8_t data_cmd0[4]; + uint8_t data_cmd1[4]; + uint8_t data_cmd2[4]; + uint8_t data_cmd3[4]; + uint8_t data_lock[4]; +}; + +struct on_sram_operation { + uint8_t addr_rawdata_addr[4]; + uint8_t addr_rawdata_end[4]; + uint8_t data_conti[4]; + uint8_t data_fin[4]; + uint8_t passwrd_start[2]; + uint8_t passwrd_end[2]; +}; + +struct on_driver_operation { + uint8_t addr_fw_define_int_is_edge[4]; + uint8_t addr_fw_rx_tx_maxpt_num[4]; +#ifdef HX_NEW_EVENT_STACK_FORMAT + uint8_t addr_fw_maxpt_bt_num[4]; +#endif + uint8_t addr_fw_xy_rev_int_edge[4]; + uint8_t addr_fw_define_x_y_res[4]; + uint8_t data_fw_define_rxnum_txnum_maxpt_sorting[4]; + uint8_t data_fw_define_rxnum_txnum_maxpt_normal[4]; + uint8_t data_df_rx[1]; + uint8_t data_df_tx[1]; + uint8_t data_df_pt[1]; + uint8_t data_df_x_res[2]; + uint8_t data_df_y_res[2]; +}; + +struct himax_on_core_command_operation { + struct on_ic_operation *ic_op; + struct on_fw_operation *fw_op; + struct on_flash_operation *flash_op; + struct on_sram_operation *sram_op; + struct on_driver_operation *driver_op; +}; + +struct himax_chip_detect { + bool (*fp_chip_detect)(void); +}; + +struct himax_chip_entry { + struct himax_chip_detect *core_chip_dt; + uint32_t hx_ic_dt_num; +}; + +struct himax_core_fp { +#ifdef CORE_IC + void (*fp_burst_enable)(uint8_t auto_add_4_byte); + int (*fp_register_read)(uint8_t *read_addr, uint32_t read_length, uint8_t *read_data, uint8_t cfg_flag); + /*int (*fp_flash_write_burst)(uint8_t *reg_byte, uint8_t *write_data);*/ + /*void (*fp_flash_write_burst_lenth)(uint8_t *reg_byte, uint8_t *write_data, uint32_t length);*/ + int (*fp_register_write)(uint8_t *write_addr, uint32_t write_length, uint8_t *write_data, uint8_t cfg_flag); + void (*fp_interface_on)(void); + void (*fp_sense_on)(uint8_t FlashMode); + void (*fp_tcon_on)(void); + bool (*fp_watch_dog_off)(void); + bool (*fp_sense_off)(bool check_en); + void (*fp_sleep_in)(void); + bool (*fp_wait_wip)(int Timing); + void (*fp_init_psl)(void); + void (*fp_resume_ic_action)(void); + void (*fp_suspend_ic_action)(void); + void (*fp_power_on_init)(void); + bool (*fp_dd_clk_set)(bool enable); + void (*fp_dd_reg_en)(bool enable); + bool (*fp_dd_reg_write)(uint8_t addr, uint8_t pa_num, int len, uint8_t *data, uint8_t bank); + bool (*fp_dd_reg_read)(uint8_t addr, uint8_t pa_num, int len, uint8_t *data, uint8_t bank); + bool (*fp_ic_id_read)(void); + bool (*fp_slave_tcon_reset)(void); + bool (*fp_slave_adc_reset_slave)(void); + bool (*fp_slave_wdt_off_slave)(void); +#endif + +#ifdef CORE_FW + void (*fp_parse_raw_data)(struct himax_report_data *hx_touch_data, int mul_num, int self_num, uint8_t diag_cmd, int16_t *mutual_data, int16_t *self_data); + void (*fp_system_reset)(void); + int (*fp_Calculate_CRC_with_AP)(unsigned char *FW_content, int CRC_from_FW, int len); + uint32_t (*fp_check_CRC)(uint8_t *start_addr, int reload_length); + void (*fp_set_reload_cmd)(uint8_t *write_data, int idx, uint32_t cmd_from, uint32_t cmd_to, uint32_t cmd_beat); + bool (*fp_program_reload)(void); + void (*fp_set_SMWP_enable)(uint8_t SMWP_enable, bool suspended); +#ifdef HX_EDGE_LIMIT + void (*fp_set_edge_limit_enable)(uint8_t edge_limit_enable, bool suspended); +#endif + void (*fp_set_HSEN_enable)(uint8_t HSEN_enable, bool suspended); + void (*fp_diag_register_set)(uint8_t diag_command, uint8_t storage_type); +#ifdef HX_TP_SELF_TEST_DRIVER + void (*fp_control_reK)(bool enable); +#endif + int (*fp_chip_self_test)(void); + void (*fp_idle_mode)(int disable); + void (*fp_reload_disable)(int disable); + bool (*fp_check_chip_version)(void); + int (*fp_read_ic_trigger_type)(void); + int (*fp_read_i2c_status)(void); + void (*fp_read_FW_ver)(void); + bool (*fp_read_event_stack)(uint8_t *buf, uint8_t length); + void (*fp_return_event_stack)(void); + bool (*fp_calculateChecksum)(bool change_iref, uint32_t size); + int (*fp_read_FW_status)(uint8_t *state_addr, uint8_t *tmp_addr); + void (*fp_irq_switch)(int switch_on); + int (*fp_assign_sorting_mode)(uint8_t *tmp_data); + int (*fp_check_sorting_mode)(uint8_t *tmp_data); + int (*fp_switch_mode)(int mode); + int (*fp_get_max_dc)(void); + uint8_t (*fp_read_DD_status)(uint8_t *cmd_set, uint8_t *tmp_data); + int (*fp_ulpm_in)(void); + int (*fp_black_gest_ctrl)(bool enable); + int (*_diff_overlay_bin)(void); +#endif + +#ifdef CORE_FLASH + void (*fp_chip_erase)(void); + bool (*fp_block_erase)(int start_addr, int length); + bool (*fp_sector_erase)(int start_addr); + void (*fp_flash_programming)(uint8_t *FW_content, int FW_Size); + void (*fp_flash_page_write)(uint8_t *write_addr, int length, uint8_t *write_data); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_32k)(unsigned char *fw, int len, bool change_iref); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_60k)(unsigned char *fw, int len, bool change_iref); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_64k)(unsigned char *fw, int len, bool change_iref); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_124k)(unsigned char *fw, int len, bool change_iref); + int (*fp_fts_ctpm_fw_upgrade_with_sys_fs_128k)(unsigned char *fw, int len, bool change_iref); + void (*fp_flash_dump_func)(uint8_t local_flash_command, int Flash_Size, uint8_t *flash_buffer); + bool (*fp_flash_lastdata_check)(uint32_t size); + bool (*fp_ahb_squit)(void); + void (*fp_flash_read)(uint8_t *r_data, int start_addr, int length); + bool (*fp_sfr_rw)(uint8_t *addr, int length, uint8_t *data, uint8_t rw_ctrl); + bool (*fp_lock_flash)(void); + bool (*fp_unlock_flash)(void); + void (*fp_init_auto_func)(void); + int (*_diff_overlay_flash)(void); +#endif + +#ifdef CORE_SRAM + void (*fp_sram_write)(uint8_t *FW_content); + bool (*fp_sram_verify)(uint8_t *FW_File, int FW_Size); + bool (*fp_get_DSRAM_data)(uint8_t *info_data, bool DSRAM_Flag); +#endif + +#ifdef CORE_DRIVER + void (*fp_chip_init)(void); +#ifdef HX_AUTO_UPDATE_FW + int (*fp_fw_ver_bin)(void); +#endif + void (*fp_pin_reset)(void); + void (*fp_touch_information)(void); + void (*fp_reload_config)(void); + int (*fp_get_touch_data_size)(void); + void (*fp_usb_detect_set)(uint8_t *cable_config); + int (*fp_hand_shaking)(void); + int (*fp_determin_diag_rawdata)(int diag_command); + int (*fp_determin_diag_storage)(int diag_command); + int (*fp_cal_data_len)(int raw_cnt_rmd, int HX_MAX_PT, int raw_cnt_max); + bool (*fp_diag_check_sum)(struct himax_report_data *hx_touch_data); + void (*fp_diag_parse_raw_data)(struct himax_report_data *hx_touch_data, int mul_num, int self_num, uint8_t diag_cmd, int32_t *mutual_data, int32_t *self_data); + void (*fp_ic_reset)(uint8_t loadconfig, uint8_t int_off); + int (*fp_ic_esd_recovery)(int hx_esd_event, int hx_zero_event, int length); + void (*fp_esd_ic_reset)(void); + void (*fp_resend_cmd_func)(bool suspended); +#ifdef HX_TP_PROC_GUEST_INFO + int (*guest_info_get_status)(void); + int (*read_guest_info)(void); +#endif +#endif +#ifdef HX_ZERO_FLASH + void (*fp_clean_sram_0f)(uint8_t *addr, int write_len, int type); + void (*fp_write_sram_0f)(const struct firmware *fw_entry, uint8_t *addr, int start_index, uint32_t write_len); + int (*fp_write_sram_0f_crc)(const struct firmware *fw_entry, uint8_t *addr, int start_index, uint32_t write_len); + void (*fp_firmware_update_0f)(const struct firmware *fw_entry); + int (*fp_0f_operation_dirly)(void); + int (*fp_0f_op_file_dirly)(char *file_name); + void (*fp_0f_operation)(struct work_struct *work); + int (*fp_0f_esd_check)(void); +#if defined(HX83102D) + void (*fp_0f_reload_to_active)(void); +#endif +#ifdef HX_0F_DEBUG + void (*fp_read_sram_0f)(const struct firmware *fw_entry, uint8_t *addr, int start_index, int read_len); + void (*fp_read_all_sram)(uint8_t *addr, int read_len); + void (*fp_firmware_read_0f)(const struct firmware *fw_entry, int type); + void (*fp_0f_operation_check)(int type); +#endif +#ifdef HX_CODE_OVERLAY + int (*fp_0f_overlay)(int ovl_type, int mode); +#endif +#ifdef HIMAX_PALM_SENSOR_EN + int (*fp_palm_detection_function)(int mode); +#endif +#endif +}; + +#endif diff --git a/drivers/input/touchscreen/himax_v3_mmi/himax_ic_incell_core.c b/drivers/input/touchscreen/himax_v3_mmi/himax_ic_incell_core.c new file mode 100755 index 000000000000..48b6dd5461b8 --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/himax_ic_incell_core.c @@ -0,0 +1,4080 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for incell ic core functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_ic_core.h" + +struct himax_core_command_operation *g_core_cmd_op; +struct ic_operation *pic_op; +EXPORT_SYMBOL(pic_op); + +struct fw_operation *pfw_op; +EXPORT_SYMBOL(pfw_op); + +struct flash_operation *pflash_op; +struct sram_operation *psram_op; +struct driver_operation *pdriver_op; +EXPORT_SYMBOL(pdriver_op); + +#ifdef HX_ZERO_FLASH +struct zf_operation *pzf_op; +EXPORT_SYMBOL(pzf_op); +#if defined(HX_CODE_OVERLAY) && defined(HX83102D) +uint8_t *ovl_idx; +EXPORT_SYMBOL(ovl_idx); +#endif +#endif + +void (*himax_mcu_cmd_struct_free)(void); +static uint8_t *g_internal_buffer; + +#ifdef CORE_IC +/* IC side start*/ +static void himax_mcu_burst_enable(uint8_t auto_add_4_byte) +{ + uint8_t tmp_data[DATA_LEN_4]; + + /*I("%s,Entering\n", __func__);*/ + tmp_data[0] = pic_op->data_conti[0]; + + if (himax_bus_write(pic_op->addr_conti[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + tmp_data[0] = (pic_op->data_incr4[0] | auto_add_4_byte); + + if (himax_bus_write(pic_op->addr_incr4[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } +} + +static int himax_mcu_register_read(uint8_t *read_addr, uint32_t read_length, uint8_t *read_data, uint8_t cfg_flag) +{ + uint8_t tmp_data[DATA_LEN_4]; + int i = 0; + int address = 0; + + /*I("%s,Entering\n",__func__);*/ + + if (cfg_flag == false) { + if (read_length > FLASH_RW_MAX_LEN) { + E("%s: read len over %d!\n", __func__, FLASH_RW_MAX_LEN); + return LENGTH_FAIL; + } + + if (read_length > DATA_LEN_4) + g_core_fp.fp_burst_enable(1); + else + g_core_fp.fp_burst_enable(0); + + address = (read_addr[3] << 24) + (read_addr[2] << 16) + (read_addr[1] << 8) + read_addr[0]; + i = address; + tmp_data[0] = (uint8_t)i; + tmp_data[1] = (uint8_t)(i >> 8); + tmp_data[2] = (uint8_t)(i >> 16); + tmp_data[3] = (uint8_t)(i >> 24); + + if (himax_bus_write(pic_op->addr_ahb_addr_byte_0[0], tmp_data, DATA_LEN_4, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + tmp_data[0] = pic_op->data_ahb_access_direction_read[0]; + + if (himax_bus_write(pic_op->addr_ahb_access_direction[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + if (himax_bus_read(pic_op->addr_ahb_rdata_byte_0[0], read_data, read_length, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + if (read_length > DATA_LEN_4) + g_core_fp.fp_burst_enable(0); + + } else { + if (himax_bus_read(read_addr[0], read_data, read_length, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + } + return NO_ERR; +} + +static int himax_mcu_flash_write_burst_lenth(uint8_t *reg_byte, uint8_t *write_data, uint32_t length) +{ + uint8_t *data_byte; + + if (!g_internal_buffer) { + E("%s: internal buffer not initialized!\n", __func__); + return MEM_ALLOC_FAIL; + } + data_byte = g_internal_buffer; + + memcpy(data_byte, reg_byte, ADDR_LEN_4); /* assign addr 4bytes */ + + memcpy(data_byte + ADDR_LEN_4, write_data, length); /* assign data n bytes */ + + if (himax_bus_write(pic_op->addr_ahb_addr_byte_0[0], data_byte, length + ADDR_LEN_4, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: xfer fail!\n", __func__); + return I2C_FAIL; + } + + return NO_ERR; +} + +static int himax_mcu_register_write(uint8_t *write_addr, uint32_t write_length, uint8_t *write_data, uint8_t cfg_flag) +{ + int address; +#if 1 + uint8_t tmp_addr[4]; + uint8_t *tmp_data; + + int total_read_times = 0; + uint32_t max_bus_size = MAX_I2C_TRANS_SZ; + uint32_t total_size_temp = 0; + int i = 0; +#endif + + /*I("%s,Entering\n", __func__);*/ + if (cfg_flag == 0) { + total_size_temp = write_length; +#ifdef __MTK_SPI__ + if (write_length > (HX_SPI_MTK_MAX_WRITE_SZ - 4)) + max_bus_size = HX_SPI_MTK_MAX_WRITE_SZ - 4; +#else + if (write_length > (PACKET_MAX_SZ - 8)) + max_bus_size = PACKET_MAX_SZ - 8; +#endif + else + max_bus_size = write_length; + + tmp_addr[3] = write_addr[3]; + tmp_addr[2] = write_addr[2]; + tmp_addr[1] = write_addr[1]; + tmp_addr[0] = write_addr[0]; + + if (total_size_temp % max_bus_size == 0) + total_read_times = total_size_temp / max_bus_size; + else + total_read_times = total_size_temp / max_bus_size + 1; + + if (write_length > DATA_LEN_4) + g_core_fp.fp_burst_enable(1); + else + g_core_fp.fp_burst_enable(0); + +#if 1 + for (i = 0; i < (total_read_times); i++) { + /* I("[log]write %d time start!\n", i); + * I("[log]addr[3]=0x%02X, addr[2]=0x%02X, addr[1]=0x%02X, addr[0]=0x%02X!\n", tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + * I("%s, write addr = 0x%02X%02X%02X%02X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + */ + + if (total_size_temp >= max_bus_size) { + tmp_data = write_data+(i * max_bus_size); + if (himax_mcu_flash_write_burst_lenth(tmp_addr, tmp_data, max_bus_size) < 0) { + I("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + total_size_temp = total_size_temp - max_bus_size; + } else { + tmp_data = write_data+(i * max_bus_size); + /* I("last total_size_temp=%d\n", total_size_temp % max_bus_size); */ + if (himax_mcu_flash_write_burst_lenth(tmp_addr, tmp_data, total_size_temp) < 0) { + I("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + } + + /*I("[log]write %d time end!\n", i);*/ + address = ((i+1) * max_bus_size); + tmp_addr[0] = write_addr[0] + (uint8_t) ((address) & 0x00FF); + + if (tmp_addr[0] < write_addr[0]) + tmp_addr[1] = write_addr[1] + (uint8_t) ((address>>8) & 0x00FF) + 1; + else + tmp_addr[1] = write_addr[1] + (uint8_t) ((address>>8) & 0x00FF); + + udelay(100); + } +#else + if (himax_mcu_flash_write_burst_lenth(write_addr, write_data, write_length) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } +#endif + + } else if (cfg_flag == 1) { + if (himax_bus_write(write_addr[0], write_data, write_length, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + } else + E("%s: cfg_flag = %d, value is wrong!\n", __func__, cfg_flag); + + return NO_ERR; +} + +static int himax_write_read_reg(uint8_t *tmp_addr, uint8_t *tmp_data, uint8_t hb, uint8_t lb) +{ + int cnt = 0; + + do { + g_core_fp.fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, 0); + usleep_range(10000, 11000); + g_core_fp.fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + /* I("%s:Now tmp_data[0]=0x%02X,[1]=0x%02X,[2]=0x%02X,[3]=0x%02X\n", + * __func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + */ + } while ((tmp_data[1] != hb && tmp_data[0] != lb) && cnt++ < 100); + + if (cnt == 99) + return HX_RW_REG_FAIL; + + I("Now register 0x%08X : high byte=0x%02X,low byte=0x%02X\n", tmp_addr[3], tmp_data[1], tmp_data[0]); + return NO_ERR; +} + +static void himax_mcu_interface_on(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_data2[DATA_LEN_4]; + int cnt = 0; + + /* Read a dummy register to wake up I2C.*/ + if (himax_bus_read(pic_op->addr_ahb_rdata_byte_0[0], tmp_data, DATA_LEN_4, HIMAX_I2C_RETRY_TIMES) < 0) {/* to knock I2C*/ + E("%s: i2c access fail!\n", __func__); + return; + } + + do { + tmp_data[0] = pic_op->data_conti[0]; + + if (himax_bus_write(pic_op->addr_conti[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + tmp_data[0] = pic_op->data_incr4[0]; + + if (himax_bus_write(pic_op->addr_incr4[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + /*Check cmd*/ + himax_bus_read(pic_op->addr_conti[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES); + himax_bus_read(pic_op->addr_incr4[0], tmp_data2, 1, HIMAX_I2C_RETRY_TIMES); + + if (tmp_data[0] == pic_op->data_conti[0] && tmp_data2[0] == pic_op->data_incr4[0]) + break; + + usleep_range(1000, 1100); + } while (++cnt < 10); + + if (cnt > 0) + I("%s:Polling burst mode: %d times\n", __func__, cnt); +} + +static bool himax_mcu_wait_wip(int Timing) +{ + uint8_t tmp_data[DATA_LEN_4]; + int retry_cnt = 0; + + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_fmt, DATA_LEN_4, pflash_op->data_spi200_trans_fmt, 0); + tmp_data[0] = 0x01; + + do { + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, pflash_op->data_spi200_trans_ctrl_1, 0); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_1, 0); + tmp_data[0] = tmp_data[1] = tmp_data[2] = tmp_data[3] = 0xFF; + g_core_fp.fp_register_read(pflash_op->addr_spi200_data, 4, tmp_data, 0); + + if ((tmp_data[0] & 0x01) == 0x00) + return true; + + retry_cnt++; + + if (tmp_data[0] != 0x00 || tmp_data[1] != 0x00 || tmp_data[2] != 0x00 || tmp_data[3] != 0x00) + I("%s:Wait wip retry_cnt:%d, buffer[0]=%d, buffer[1]=%d, buffer[2]=%d, buffer[3]=%d\n", + __func__, retry_cnt, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + + if (retry_cnt > 100) { + E("%s: Wait wip error!\n", __func__); + return false; + } + + msleep(Timing); + } while ((tmp_data[0] & 0x01) == 0x01); + + return true; +} + +static void himax_mcu_sense_on(uint8_t FlashMode) +{ + uint8_t tmp_data[DATA_LEN_4]; + int retry = 0; + + I("Enter %s\n", __func__); + g_core_fp.fp_interface_on(); + g_core_fp.fp_register_write(pfw_op->addr_ctrl_fw_isr, + sizeof(pfw_op->data_clear), pfw_op->data_clear, 0); + /*msleep(20);*/ + usleep_range(10000, 11000); + if (!FlashMode) { +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + } else { + do { + g_core_fp.fp_register_write(pfw_op->addr_safe_mode_release_pw, + sizeof(pfw_op->data_safe_mode_release_pw_active), pfw_op->data_safe_mode_release_pw_active, 0); + + g_core_fp.fp_register_read(pfw_op->addr_flag_reset_event, DATA_LEN_4, tmp_data, 0); + I("%s:Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + } while ((tmp_data[1] != 0x01 || tmp_data[0] != 0x00) && retry++ < 5); + + if (retry >= 5) { + E("%s: Fail:\n", __func__); +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + } else { + I("%s:OK and Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + /* reset code*/ + tmp_data[0] = 0x00; + + if (himax_bus_write(pic_op->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + if (himax_bus_write(pic_op->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + g_core_fp.fp_register_write(pfw_op->addr_safe_mode_release_pw, + sizeof(pfw_op->data_safe_mode_release_pw_reset), pfw_op->data_safe_mode_release_pw_reset, 0); + } + } +} + +static bool himax_mcu_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_data[DATA_LEN_4]; + + do { + tmp_data[0] = pic_op->data_i2c_psw_lb[0]; + + if (himax_bus_write(pic_op->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + tmp_data[0] = pic_op->data_i2c_psw_ub[0]; + + if (himax_bus_write(pic_op->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + g_core_fp.fp_register_read(pic_op->addr_cs_central_state, ADDR_LEN_4, tmp_data, 0); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + g_core_fp.fp_register_write(pic_op->addr_tcon_on_rst, DATA_LEN_4, pic_op->data_rst, 0); + usleep_range(1000, 1100); + tmp_data[3] = pic_op->data_rst[3]; + tmp_data[2] = pic_op->data_rst[2]; + tmp_data[1] = pic_op->data_rst[1]; + tmp_data[0] = pic_op->data_rst[0] | 0x01; + g_core_fp.fp_register_write(pic_op->addr_tcon_on_rst, DATA_LEN_4, tmp_data, 0); + + g_core_fp.fp_register_write(pic_op->addr_adc_on_rst, DATA_LEN_4, pic_op->data_rst, 0); + usleep_range(1000, 1100); + tmp_data[3] = pic_op->data_rst[3]; + tmp_data[2] = pic_op->data_rst[2]; + tmp_data[1] = pic_op->data_rst[1]; + tmp_data[0] = pic_op->data_rst[0] | 0x01; + g_core_fp.fp_register_write(pic_op->addr_adc_on_rst, DATA_LEN_4, tmp_data, 0); + goto TRUE_END; + } else { + /*msleep(10);*/ +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + } + } while (cnt++ < 15); + + return false; +TRUE_END: + return true; +} + +static void himax_mcu_init_psl(void) /*power saving level*/ +{ + g_core_fp.fp_register_write(pic_op->addr_psl, sizeof(pic_op->data_rst), pic_op->data_rst, 0); + I("%s: power saving level reset OK!\n", __func__); +} + +static void himax_mcu_resume_ic_action(void) +{ + /* Nothing to do */ +} + +static void himax_mcu_suspend_ic_action(void) +{ + /* Nothing to do */ +} + +static void himax_mcu_power_on_init(void) +{ + I("%s:\n", __func__); + g_core_fp.fp_touch_information(); + /*RawOut select initial*/ + g_core_fp.fp_register_write(pfw_op->addr_raw_out_sel, sizeof(pfw_op->data_clear), pfw_op->data_clear, 0); + /*DSRAM func initial*/ + g_core_fp.fp_assign_sorting_mode(pfw_op->data_clear); + g_core_fp.fp_sense_on(0x00); +} + +static bool himax_mcu_dd_clk_set(bool enable) +{ + uint8_t data[4] = {0}; + + data[0] = (enable)?1:0; + return (g_core_fp.fp_register_write(pic_op->adr_osc_en, sizeof(pic_op->adr_osc_en), data, 0) == NO_ERR); +} + +static void himax_mcu_dd_reg_en(bool enable) +{ + uint8_t data[4] = {0}; + + g_core_fp.fp_dd_reg_read(0xCB, 8, 1, data, 0); + + if (data[0] != 0x44) { /*need DD Touch PW*/ + data[0] = 0xA5; data[1] = 0x00; + data[2] = 0x00; data[3] = 0x00; + g_core_fp.fp_register_write(pic_op->adr_osc_pw, DATA_LEN_4, data, 0); + data[0] = 0x00; data[1] = 0x55; + data[2] = 0xAA; data[3] = 0x00; + g_core_fp.fp_dd_reg_write(0xEB, 0, 4, data, 0); + } + + data[0] = 0x00; data[1] = 0x83; + data[2] = 0x11; data[3] = 0x2A; + g_core_fp.fp_dd_reg_write(0xB9, 0, 4, data, 0); +} + +static bool himax_mcu_dd_reg_write(uint8_t addr, uint8_t pa_num, int len, uint8_t *data, uint8_t bank) +{ + /*Calculate total write length*/ + uint32_t data_len = (((len + pa_num - 1) / 4 - pa_num / 4) + 1) * 4; + uint8_t tmp_addr[4] = {0}; + uint8_t tmp_data[4] = {0}; + uint32_t chk_idx = 0; + int i = 0; + int ret = -1; + + uint8_t *w_data = NULL; + bool *chk_data = NULL; + + w_data = kcalloc(data_len, sizeof(uint8_t), GFP_KERNEL); + if (w_data == NULL) { + E("%s, Failed to allocate memory\n", __func__); + } + + chk_data = kcalloc(data_len, sizeof(bool), GFP_KERNEL); + if (chk_data == NULL) { + E("%s, Failed to allocate memory\n", __func__); + } + if (!w_data || !chk_data) + goto alloc_error; + + /*put input data*/ + chk_idx = pa_num % 4; + for (i = 0; i < len; i++) { + w_data[chk_idx] = data[i]; + chk_data[chk_idx++] = true; + } + + /*get original data*/ + chk_idx = (pa_num / 4) * 4; + for (i = 0; i < data_len; i++) { + if (!chk_data[i]) { + g_core_fp.fp_dd_reg_read(addr, (uint8_t)(chk_idx + i), 1, tmp_data, bank); + w_data[i] = tmp_data[0]; + chk_data[i] = true; + } + D("%s w_data[%d] = %2X\n", __func__, i, w_data[i]); + } + + tmp_addr[3] = 0x30; + tmp_addr[2] = addr >> 4; + tmp_addr[1] = (addr << 4) | (bank * 4); + tmp_addr[0] = chk_idx; + D("%s Addr = %02X%02X%02X%02X.\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + ret = g_core_fp.fp_register_write(tmp_addr, data_len, w_data, 0); +alloc_error: + if (chk_data) + kfree(chk_data); + if (w_data) + kfree(w_data); + return ret; +} + +static bool himax_mcu_dd_reg_read(uint8_t addr, uint8_t pa_num, int len, uint8_t *data, uint8_t bank) +{ + uint8_t tmp_addr[4] = {0}; + uint8_t tmp_data[4] = {0}; + int i = 0; + + for (i = 0; i < len; i++) { + tmp_addr[3] = 0x30; + tmp_addr[2] = addr >> 4; + tmp_addr[1] = (addr << 4) | (bank * 4); + tmp_addr[0] = pa_num + i; + + if (g_core_fp.fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0)) + goto READ_FAIL; + data[i] = tmp_data[0]; + + D("%s Addr = %02X%02X%02X%02X.data = %2X\n", __func__, + tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0], data[i]); + } + return true; + +READ_FAIL: + E("%s Read DD reg Failed.\n", __func__); + return false; +} + +static bool himax_mcu_ic_id_read(void) +{ + int i = 0; + uint8_t data[4] = {0}; + + g_core_fp.fp_dd_clk_set(true); + g_core_fp.fp_dd_reg_en(true); + + for (i = 0; i < 13; i++) { + data[0] = 0x28 + i; + g_core_fp.fp_dd_reg_write(0xBB, 2, 1, data, 0); + data[0] = 0x80; + g_core_fp.fp_dd_reg_write(0xBB, 4, 1, data, 0); + data[0] = 0x00; + g_core_fp.fp_dd_reg_write(0xBB, 4, 1, data, 0); + g_core_fp.fp_dd_reg_read(0xBB, 5, 1, data, 0); + ic_data->vendor_ic_id[i] = data[0]; + I("ic_data->vendor_ic_id[%d] = %02X\n", i, ic_data->vendor_ic_id[i]); + } + + g_core_fp.fp_dd_clk_set(false); + + return true; +} + +/* IC side end*/ +#endif + +#ifdef CORE_FW +/* FW side start*/ +static void diag_mcu_parse_raw_data(struct himax_report_data *hx_touch_data, int mul_num, int self_num, uint8_t diag_cmd, int32_t *mutual_data, int32_t *self_data) +{ + int RawDataLen_word; + int index = 0; + int temp1, temp2, i; + + if (hx_touch_data->hx_rawdata_buf[0] == pfw_op->data_rawdata_ready_lb[0] + && hx_touch_data->hx_rawdata_buf[1] == pfw_op->data_rawdata_ready_hb[0] + && hx_touch_data->hx_rawdata_buf[2] > 0 + && hx_touch_data->hx_rawdata_buf[3] == diag_cmd) { + RawDataLen_word = hx_touch_data->rawdata_size / 2; + index = (hx_touch_data->hx_rawdata_buf[2] - 1) * RawDataLen_word; + + /* I("Header[%d]: %x, %x, %x, %x, mutual: %d, self: %d\n", index, buf[56], buf[57], buf[58], buf[59], mul_num, self_num); + * I("RawDataLen=%d , RawDataLen_word=%d , hx_touch_info_size=%d\n", RawDataLen, RawDataLen_word, hx_touch_info_size); + */ + for (i = 0; i < RawDataLen_word; i++) { + temp1 = index + i; + + if (temp1 < mul_num) { /*mutual*/ + mutual_data[index + i] = ((int8_t)hx_touch_data->hx_rawdata_buf[i * 2 + 4 + 1]) * 256 + hx_touch_data->hx_rawdata_buf[i * 2 + 4]; + } else { /*self*/ + temp1 = i + index; + temp2 = self_num + mul_num; + + if (temp1 >= temp2) + break; + + self_data[i + index - mul_num] = (((int8_t)hx_touch_data->hx_rawdata_buf[i * 2 + 4 + 1]) << 8) + + hx_touch_data->hx_rawdata_buf[i * 2 + 4]; + } + } + } +} + +static void himax_mcu_system_reset(void) +{ +#ifdef HX_SHARP_ENABLE_PON + g_core_fp.fp_register_write(pfw_op->addr_system_reset, sizeof(pfw_op->data_system_reset), pfw_op->data_system_reset, 0); +#else + uint8_t tmp_data[DATA_LEN_4]; + int retry = 0; + + g_core_fp.fp_interface_on(); + g_core_fp.fp_register_write(pfw_op->addr_ctrl_fw_isr, + sizeof(pfw_op->data_clear), pfw_op->data_clear, 0); + do { + /* reset code*/ + /* =========================================== + * I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + * =========================================== + */ + tmp_data[0] = pic_op->data_i2c_psw_lb[0]; + + if (himax_bus_write(pic_op->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + /* =========================================== + * I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + * =========================================== + */ + tmp_data[0] = pic_op->data_i2c_psw_ub[0]; + + if (himax_bus_write(pic_op->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + /* =========================================== + * I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x00 + * =========================================== + */ + tmp_data[0] = 0x00; + + if (himax_bus_write(pic_op->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + usleep_range(10000, 11000); + + g_core_fp.fp_register_read(pfw_op->addr_flag_reset_event, DATA_LEN_4, tmp_data, 0); + I("%s:Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + } while ((tmp_data[1] != 0x02 || tmp_data[0] != 0x00) && retry++ < 5); +#endif +} + +static int himax_mcu_Calculate_CRC_with_AP(unsigned char *FW_content, int CRC_from_FW, int len) +{ + int i, j, length = 0; + int fw_data; + int fw_data_2; + int CRC = 0xFFFFFFFF; + int PolyNomial = 0x82F63B78; + + length = len / 4; + + for (i = 0; i < length; i++) { + fw_data = FW_content[i * 4]; + + for (j = 1; j < 4; j++) { + fw_data_2 = FW_content[i * 4 + j]; + fw_data += (fw_data_2) << (8 * j); + } + CRC = fw_data ^ CRC; + for (j = 0; j < 32; j++) { + if ((CRC % 2) != 0) + CRC = ((CRC >> 1) & 0x7FFFFFFF) ^ PolyNomial; + else + CRC = (((CRC >> 1) & 0x7FFFFFFF)/*& 0x7FFFFFFF*/); + } + } + + return CRC; +} + +static uint32_t himax_mcu_check_CRC(uint8_t *start_addr, int reload_length) +{ + uint32_t result = 0; + uint8_t tmp_data[DATA_LEN_4]; + int cnt = 0, ret = 0; + int length = reload_length / DATA_LEN_4; + + ret = g_core_fp.fp_register_write(pfw_op->addr_reload_addr_from, DATA_LEN_4, start_addr, 0); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + + tmp_data[3] = 0x00; tmp_data[2] = 0x99; tmp_data[1] = (length >> 8); tmp_data[0] = length; + ret = g_core_fp.fp_register_write(pfw_op->addr_reload_addr_cmd_beat, DATA_LEN_4, tmp_data, 0); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + cnt = 0; + + do { + ret = g_core_fp.fp_register_read(pfw_op->addr_reload_status, DATA_LEN_4, tmp_data, 0); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + + if ((tmp_data[0] & 0x01) != 0x01) { + ret = g_core_fp.fp_register_read(pfw_op->addr_reload_crc32_result, DATA_LEN_4, tmp_data, 0); + if (ret < NO_ERR) { + E("%s: i2c access fail!\n", __func__); + return HW_CRC_FAIL; + } + I("%s: tmp_data[3]=%X, tmp_data[2]=%X, tmp_data[1]=%X, tmp_data[0]=%X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1], tmp_data[0]); + result = ((tmp_data[3] << 24) + (tmp_data[2] << 16) + (tmp_data[1] << 8) + tmp_data[0]); + goto END; + } else { + I("Waiting for HW ready!\n"); + usleep_range(1000, 1100); + } + + } while (cnt++ < 100); +END: + return result; +} + +static void himax_mcu_set_reload_cmd(uint8_t *write_data, int idx, uint32_t cmd_from, uint32_t cmd_to, uint32_t cmd_beat) +{ + int index = idx * 12; + int i; + + for (i = 3; i >= 0; i--) { + write_data[index + i] = (cmd_from >> (8 * i)); + write_data[index + 4 + i] = (cmd_to >> (8 * i)); + write_data[index + 8 + i] = (cmd_beat >> (8 * i)); + } +} + +static bool himax_mcu_program_reload(void) +{ + return true; +} + +#ifdef HX_P_SENSOR +static int himax_mcu_ulpm_in(void) +{ + uint8_t tmp_data[4]; + int rtimes = 0; + + I("%s:entering\n", __func__); + + /* 34 -> 11 */ + do { + if (rtimes > 10) { + I("%s:1/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_34[0], pfw_op->data_ulpm_11, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_34[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x34, correct 0x11 = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_11[0]); + + rtimes = 0; + /* 34 -> 11 */ + do { + if (rtimes > 10) { + I("%s:2/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_34[0], pfw_op->data_ulpm_11, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_34[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x34, correct 0x11 = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_11[0]); + + /* 33 -> 33 */ + rtimes = 0; + do { + if (rtimes > 10) { + I("%s:3/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_33[0], pfw_op->data_ulpm_33, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_33[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x33, correct 0x33 = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_33[0]); + + /* 34 -> 22 */ + rtimes = 0; + do { + if (rtimes > 10) { + I("%s:4/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_34[0], pfw_op->data_ulpm_22, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_34[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x34, correct 0x22 = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_22[0]); + + /* 33 -> AA */ + rtimes = 0; + do { + if (rtimes > 10) { + I("%s:5/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_33[0], pfw_op->data_ulpm_aa, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_33[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x33, correct 0xAA = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_aa[0]); + + /* 33 -> 33 */ + rtimes = 0; + do { + if (rtimes > 10) { + I("%s:6/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_33[0], pfw_op->data_ulpm_33, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_33[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x33, correct 0x33 = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_33[0]); + + /* 33 -> AA */ + rtimes = 0; + do { + if (rtimes > 10) { + I("%s:7/7 retry over 10 times!\n", __func__); + return false; + } + if (himax_bus_write(pfw_op->addr_ulpm_33[0], pfw_op->data_ulpm_aa, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi write fail!\n", __func__); + continue; + } + if (himax_bus_read(pfw_op->addr_ulpm_33[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + I("%s: spi read fail!\n", __func__); + continue; + } + + I("%s:retry times %d, addr = 0x33, correct 0xAA = current 0x%2.2X\n", __func__, rtimes, tmp_data[0]); + rtimes++; + } while (tmp_data[0] != pfw_op->data_ulpm_aa[0]); + + I("%s:END\n", __func__); + return true; +} + +static int himax_mcu_black_gest_ctrl(bool enable) +{ + int ret = 0; + + I("%s:enable=%d, ts->is_suspended=%d\n", __func__, enable, private_ts->suspended); + + if (private_ts->suspended) { + if (enable) { +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + I("%s: Please enable TP reset define\n", __func__); +#endif + } else { + g_core_fp.fp_ulpm_in(); + } + } else { + g_core_fp.fp_sense_on(0); + } + return ret; +} +#endif + +static void himax_mcu_set_SMWP_enable(uint8_t SMWP_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t retry_cnt = 0; + + do { + if (SMWP_enable) { + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, back_data, 4); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + } + + g_core_fp.fp_register_read(pfw_op->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + /*I("%s: tmp_data[0]=%d, SMWP_enable=%d, retry_cnt=%d\n", __func__, tmp_data[0],SMWP_enable,retry_cnt);*/ + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} + +#ifdef HX_EDGE_LIMIT +static void himax_mcu_set_edge_limit_enable(uint8_t edge_limit_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t retry_cnt = 0; + + do { + if (edge_limit_enable == 0x01) {/*Portrait*/ + himax_in_parse_assign_cmd(fw_func_edge_portrait_pwd, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_edge_limit_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_func_edge_portrait_pwd, back_data, 4); + } else if (edge_limit_enable == 0x02) {/*Landscape-Right*/ + himax_in_parse_assign_cmd(fw_func_edge_lsp_right_pwd, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_edge_limit_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_func_edge_lsp_right_pwd, back_data, 4); + } else if (edge_limit_enable == 0x03) {/*Landscape-Left*/ + himax_in_parse_assign_cmd(fw_func_edge_lsp_left_pwd, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_edge_limit_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_func_edge_lsp_left_pwd, back_data, 4); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_edge_limit_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + } + + g_core_fp.fp_register_read(pfw_op->addr_edge_limit_enable, DATA_LEN_4, tmp_data, 0); + I("%s: tmp_data[0]=%d, SMWP_enable=%d, retry_cnt=%d \n", __func__, tmp_data[0], edge_limit_enable, retry_cnt); + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} +#endif + +static void himax_mcu_set_HSEN_enable(uint8_t HSEN_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t retry_cnt = 0; + + do { + if (HSEN_enable) { + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, back_data, 4); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + } + + g_core_fp.fp_register_read(pfw_op->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + /*I("%s: tmp_data[0]=%d, HSEN_enable=%d, retry_cnt=%d\n", __func__, tmp_data[0],HSEN_enable,retry_cnt);*/ + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} + +static void himax_mcu_usb_detect_set(uint8_t *cable_config) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t retry_cnt = 0; + + do { + if (cable_config[1] == 0x01) { + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_func_handshaking_pwd, back_data, 4); + I("%s: USB detect status IN!\n", __func__); + } else { + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + g_core_fp.fp_register_write(pfw_op->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + I("%s: USB detect status OUT!\n", __func__); + } + + g_core_fp.fp_register_read(pfw_op->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + /*I("%s: tmp_data[0]=%d, USB detect=%d, retry_cnt=%d\n", __func__, tmp_data[0],cable_config[1] ,retry_cnt);*/ + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); +} + +static void himax_mcu_diag_register_set(uint8_t diag_command, uint8_t storage_type) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t cnt = 50; + + if (diag_command > 0 && storage_type % 8 > 0) + tmp_data[0] = diag_command + 0x08; + else + tmp_data[0] = diag_command; + I("diag_command = %d, tmp_data[0] = %X\n", diag_command, tmp_data[0]); + g_core_fp.fp_interface_on(); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; + do { + g_core_fp.fp_register_write(pfw_op->addr_raw_out_sel, DATA_LEN_4, tmp_data, 0); + g_core_fp.fp_register_read(pfw_op->addr_raw_out_sel, DATA_LEN_4, back_data, 0); + I("%s: back_data[3]=0x%02X,back_data[2]=0x%02X,back_data[1]=0x%02X,back_data[0]=0x%02X!\n", + __func__, back_data[3], back_data[2], back_data[1], back_data[0]); + cnt--; + } while (tmp_data[0] != back_data[0] && cnt > 0); +} + +static int himax_mcu_chip_self_test(void) +{ + uint8_t tmp_data[FLASH_WRITE_BURST_SZ]; + uint8_t self_test_info[20]; + int pf_value = 0x00; + uint8_t test_result_id = 0; + int i; + + memset(tmp_data, 0x00, sizeof(tmp_data)); + g_core_fp.fp_interface_on(); + g_core_fp.fp_sense_off(true); + g_core_fp.fp_burst_enable(1); + g_core_fp.fp_register_write(pfw_op->addr_selftest_addr_en, DATA_LEN_4, pfw_op->data_selftest_request, 0); + /*Set criteria 0x10007F1C [0,1]=aa/up,down=, [2-3]=key/up,down, [4-5]=avg/up,down*/ + tmp_data[0] = pfw_op->data_criteria_aa_top[0]; + tmp_data[1] = pfw_op->data_criteria_aa_bot[0]; + tmp_data[2] = pfw_op->data_criteria_key_top[0]; + tmp_data[3] = pfw_op->data_criteria_key_bot[0]; + tmp_data[4] = pfw_op->data_criteria_avg_top[0]; + tmp_data[5] = pfw_op->data_criteria_avg_bot[0]; + tmp_data[6] = 0x00; + tmp_data[7] = 0x00; + g_core_fp.fp_register_write(pfw_op->addr_criteria_addr, FLASH_WRITE_BURST_SZ, tmp_data, 0); + g_core_fp.fp_register_write(pfw_op->addr_set_frame_addr, DATA_LEN_4, pfw_op->data_set_frame, 0); + /*Disable IDLE Mode*/ + g_core_fp.fp_idle_mode(1); + /*Diable Flash Reload*/ + g_core_fp.fp_reload_disable(1); + /*start selftest // leave safe mode*/ + g_core_fp.fp_sense_on(0x01); + + /*Hand shaking*/ + for (i = 0; i < 1000; i++) { + g_core_fp.fp_register_read(pfw_op->addr_selftest_addr_en, 4, tmp_data, 0); + I("%s: tmp_data[0] = 0x%02X,tmp_data[1] = 0x%02X,tmp_data[2] = 0x%02X,tmp_data[3] = 0x%02X, cnt=%d\n", + __func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3], i); + usleep_range(10000, 11000); + + if (tmp_data[1] == pfw_op->data_selftest_ack_hb[0] && tmp_data[0] == pfw_op->data_selftest_ack_lb[0]) { + I("%s Data ready goto moving data\n", __func__); + break; + } + } + + g_core_fp.fp_sense_off(true); + msleep(20); + /* ===================================== + * Read test result ==> bit[2][1][0] = [key][AA][avg] => 0xF = PASS + * ===================================== + */ + g_core_fp.fp_register_read(pfw_op->addr_selftest_result_addr, 20, self_test_info, 0); + test_result_id = self_test_info[0]; + I("%s: check test result, test_result_id=%x, test_result=%x\n", __func__ + , test_result_id, self_test_info[0]); + I("raw top 1 = %d\n", self_test_info[3] * 256 + self_test_info[2]); + I("raw top 2 = %d\n", self_test_info[5] * 256 + self_test_info[4]); + I("raw top 3 = %d\n", self_test_info[7] * 256 + self_test_info[6]); + I("raw last 1 = %d\n", self_test_info[9] * 256 + self_test_info[8]); + I("raw last 2 = %d\n", self_test_info[11] * 256 + self_test_info[10]); + I("raw last 3 = %d\n", self_test_info[13] * 256 + self_test_info[12]); + I("raw key 1 = %d\n", self_test_info[15] * 256 + self_test_info[14]); + I("raw key 2 = %d\n", self_test_info[17] * 256 + self_test_info[16]); + I("raw key 3 = %d\n", self_test_info[19] * 256 + self_test_info[18]); + + if (test_result_id == pfw_op->data_selftest_pass[0]) { + I("[Himax]: self-test pass\n"); + pf_value = 0x0; + } else { + E("[Himax]: self-test fail\n"); + /* E("[Himax]: bank_avg = %d, bank_max = %d,%d,%d, bank_min = %d,%d,%d, key = %d,%d,%d\n", + * tmp_data[1],tmp_data[2],tmp_data[3],tmp_data[4],tmp_data[5],tmp_data[6],tmp_data[7], + * tmp_data[8],tmp_data[9],tmp_data[10]); + */ + pf_value = 0x1; + } + + /*Enable IDLE Mode*/ + g_core_fp.fp_idle_mode(0); +#ifndef HX_ZERO_FLASH + /* Enable Flash Reload //recovery*/ + g_core_fp.fp_reload_disable(0); +#endif + g_core_fp.fp_sense_on(0x00); + msleep(120); + return pf_value; +} + +static void himax_mcu_idle_mode(int disable) +{ + int retry = 20; + uint8_t tmp_data[DATA_LEN_4]; + uint8_t switch_cmd = 0x00; + + I("%s:entering\n", __func__); + + do { + I("%s,now %d times!\n", __func__, retry); + g_core_fp.fp_register_read(pfw_op->addr_fw_mode_status, DATA_LEN_4, tmp_data, 0); + + if (disable) + switch_cmd = pfw_op->data_idle_dis_pwd[0]; + else + switch_cmd = pfw_op->data_idle_en_pwd[0]; + + tmp_data[0] = switch_cmd; + g_core_fp.fp_register_write(pfw_op->addr_fw_mode_status, DATA_LEN_4, tmp_data, 0); + g_core_fp.fp_register_read(pfw_op->addr_fw_mode_status, DATA_LEN_4, tmp_data, 0); + I("%s:After turn ON/OFF IDLE Mode [0] = 0x%02X,[1] = 0x%02X,[2] = 0x%02X,[3] = 0x%02X\n", + __func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + retry--; + usleep_range(10000, 11000); + } while ((tmp_data[0] != switch_cmd) && retry > 0); + + I("%s: setting OK!\n", __func__); +} + +static void himax_mcu_reload_disable(int disable) +{ + int ret = 0; + I("%s:entering\n", __func__); + + if (disable) { /*reload disable*/ + ret= g_core_fp.fp_register_write(pdriver_op->addr_fw_define_flash_reload, DATA_LEN_4, pdriver_op->data_fw_define_flash_reload_dis, 0); + } else { /*reload enable*/ + ret= g_core_fp.fp_register_write(pdriver_op->addr_fw_define_flash_reload, DATA_LEN_4, pdriver_op->data_fw_define_flash_reload_en, 0); + } + if (ret != 0) + atomic_set(&private_ts->resume_update_fail, 1); + + I("%s: setting OK!\n", __func__); +} + +static bool himax_mcu_check_chip_version(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t ret_data = false; + int i = 0; + + for (i = 0; i < 5; i++) { + g_core_fp.fp_register_read(pfw_op->addr_icid_addr, DATA_LEN_4, tmp_data, 0); + I("%s:Read driver IC ID = %X,%X,%X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1]); + + if ((tmp_data[3] == 0x83) && (tmp_data[2] == 0x10) && (tmp_data[1] == 0x2a)) { + strlcpy(private_ts->chip_name, HX_83102A_SERIES_PWON, 30); + ret_data = true; + goto END; + } else { + ret_data = false; + E("%s:Read driver ID register Fail:\n", __func__); + } + } +END: + return ret_data; +} + +static int himax_mcu_read_ic_trigger_type(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + int trigger_type = false; + + g_core_fp.fp_register_read(pfw_op->addr_trigger_addr, DATA_LEN_4, tmp_data, 0); + + if ((tmp_data[1] & 0x01) == 1) + trigger_type = true; + + return trigger_type; +} + +static int himax_mcu_read_i2c_status(void) +{ + return i2c_error_count; +} + +static void himax_mcu_read_FW_ver(void) +{ + uint8_t data[12]; + uint8_t data_2[DATA_LEN_4]; + int retry = 200; + int reload_status = 0; + + g_core_fp.fp_sense_on(0x00); + + while (reload_status == 0) { + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_flash_reload, DATA_LEN_4, data, 0); + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_2nd_flash_reload, DATA_LEN_4, data_2, 0); + + if ((data[1] == 0x3A && data[0] == 0xA3) + || (data_2[1] == 0x72 && data_2[0] == 0xC0)) { + I("reload OK!\n"); + reload_status = 1; + break; + } else if (retry == 0) { + E("reload 20 times! fail\n"); + E("Maybe NOT have FW in chipset\n"); + E("Maybe Wrong FW in chipset\n"); + ic_data->vendor_panel_ver = 0; + ic_data->vendor_fw_ver = 0; + ic_data->vendor_config_ver = 0; + ic_data->vendor_touch_cfg_ver = 0; + ic_data->vendor_display_cfg_ver = 0; + ic_data->vendor_cid_maj_ver = 0; + ic_data->vendor_cid_min_ver = 0; + goto END; + } else { + retry--; + usleep_range(10000, 11000); + if (retry % 10 == 0) + I("reload fail ,delay 10ms retry=%d\n", retry); + } + } + + I("%s : data[0]=0x%2.2X,data[1]=0x%2.2X,data_2[0]=0x%2.2X,data_2[1]=0x%2.2X\n", __func__, data[0], data[1], data_2[0], data_2[1]); + I("reload_status=%d\n", reload_status); + /* ===================================== + * Read FW version + * ===================================== + */ + g_core_fp.fp_sense_off(true); + g_core_fp.fp_register_read(pfw_op->addr_fw_ver_addr, DATA_LEN_4, data, 0); + ic_data->vendor_panel_ver = data[0]; + ic_data->vendor_fw_ver = data[1] << 8 | data[2]; + I("PANEL_VER : %X\n", ic_data->vendor_panel_ver); + I("FW_VER : %X\n", ic_data->vendor_fw_ver); + g_core_fp.fp_register_read(pfw_op->addr_fw_cfg_addr, DATA_LEN_4, data, 0); + ic_data->vendor_config_ver = data[2] << 8 | data[3]; + /*I("CFG_VER : %X\n",ic_data->vendor_config_ver);*/ + ic_data->vendor_touch_cfg_ver = data[2]; + I("TOUCH_VER : %X\n", ic_data->vendor_touch_cfg_ver); + ic_data->vendor_display_cfg_ver = data[3]; + I("DISPLAY_VER : %X\n", ic_data->vendor_display_cfg_ver); + g_core_fp.fp_register_read(pfw_op->addr_fw_vendor_addr, DATA_LEN_4, data, 0); + ic_data->vendor_cid_maj_ver = data[2]; + ic_data->vendor_cid_min_ver = data[3]; + I("CID_VER : %X\n", (ic_data->vendor_cid_maj_ver << 8 | ic_data->vendor_cid_min_ver)); + g_core_fp.fp_register_read(pfw_op->addr_cus_info, 12, data, 0); + memcpy(ic_data->vendor_cus_info, data, 12); + I("Cusomer ID = %s\n", ic_data->vendor_cus_info); + g_core_fp.fp_register_read(pfw_op->addr_proj_info, 12, data, 0); + memcpy(ic_data->vendor_proj_info, data, 12); + I("Project ID = %s\n", ic_data->vendor_proj_info); + +END: + return; +} + +static bool himax_mcu_read_event_stack(uint8_t *buf, uint8_t length) +{ + uint8_t cmd[DATA_LEN_4]; + struct timespec t_start, t_end, t_delta; + int len = length; + int i2c_speed = 0; + + /* AHB_I2C Burst Read Off */ + cmd[0] = pfw_op->data_ahb_dis[0]; + + if (himax_bus_write(pfw_op->addr_ahb_addr[0], cmd, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return 0; + } + if (private_ts->debug_log_level & BIT(2)) + getnstimeofday(&t_start); + + himax_bus_read(pfw_op->addr_event_addr[0], buf, length, HIMAX_I2C_RETRY_TIMES); + + if (private_ts->debug_log_level & BIT(2)) { + getnstimeofday(&t_end); + t_delta.tv_nsec = (t_end.tv_sec * 1000000000 + t_end.tv_nsec) - (t_start.tv_sec * 1000000000 + t_start.tv_nsec); /*ns*/ + i2c_speed = (len * 9 * 1000000 / (int)t_delta.tv_nsec) * 13 / 10; + private_ts->bus_speed = (int)i2c_speed; + } + + /* AHB_I2C Burst Read On */ + cmd[0] = pfw_op->data_ahb_en[0]; + + if (himax_bus_write(pfw_op->addr_ahb_addr[0], cmd, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return 0; + } + + return 1; +} + +static void himax_mcu_return_event_stack(void) +{ + int retry = 20, i; + uint8_t tmp_data[DATA_LEN_4]; + + I("%s:entering\n", __func__); + + do { + I("now %d times!\n", retry); + + for (i = 0; i < DATA_LEN_4; i++) + tmp_data[i] = psram_op->addr_rawdata_end[i]; + + g_core_fp.fp_register_write(psram_op->addr_rawdata_addr, DATA_LEN_4, tmp_data, 0); + g_core_fp.fp_register_read(psram_op->addr_rawdata_addr, DATA_LEN_4, tmp_data, 0); + retry--; + usleep_range(10000, 11000); + } while ((tmp_data[1] != psram_op->addr_rawdata_end[1] && tmp_data[0] != psram_op->addr_rawdata_end[0]) && retry > 0); + + I("%s: End of setting!\n", __func__); +} + +static bool himax_mcu_calculateChecksum(bool change_iref, uint32_t size) +{ + uint8_t CRC_result = 0, i; + uint8_t tmp_data[DATA_LEN_4]; + + I("%s:Now size=%d\n", __func__, size); + for (i = 0; i < DATA_LEN_4; i++) + tmp_data[i] = psram_op->addr_rawdata_end[i]; + + CRC_result = g_core_fp.fp_check_CRC(tmp_data, size); + msleep(50); + + if (CRC_result != 0) + I("%s: CRC Fail=%d\n", __func__, CRC_result); + + return (CRC_result == 0) ? true : false; +} + +static int himax_mcu_read_FW_status(uint8_t *state_addr, uint8_t *tmp_addr) +{ + uint8_t i; + uint8_t req_size = 0; + uint8_t status_addr[DATA_LEN_4]; + uint8_t cmd_addr[DATA_LEN_4]; + + if (state_addr[0] == 0x01) { + state_addr[1] = 0x04; + + for (i = 0; i < DATA_LEN_4; i++) { + state_addr[i + 2] = pfw_op->addr_fw_dbg_msg_addr[i]; + status_addr[i] = pfw_op->addr_fw_dbg_msg_addr[i]; + } + + req_size = 0x04; + g_core_fp.fp_register_read(status_addr, req_size, tmp_addr, 0); + } else if (state_addr[0] == 0x02) { + state_addr[1] = 0x30; + + for (i = 0; i < DATA_LEN_4; i++) { + state_addr[i + 2] = pfw_op->addr_fw_dbg_msg_addr[i]; + cmd_addr[i] = pfw_op->addr_fw_dbg_msg_addr[i]; + } + + req_size = 0x30; + g_core_fp.fp_register_read(cmd_addr, req_size, tmp_addr, 0); + } + + return NO_ERR; +} + +static void himax_mcu_irq_switch(int switch_on) +{ + if (switch_on) { + if (private_ts->use_irq) + himax_int_enable(switch_on); + else + hrtimer_start(&private_ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + + } else { + if (private_ts->use_irq) + himax_int_enable(switch_on); + else { + hrtimer_cancel(&private_ts->timer); + cancel_work_sync(&private_ts->work); + } + } +} + +static int himax_mcu_assign_sorting_mode(uint8_t *tmp_data) +{ + + I("%s:Now tmp_data[3]=0x%02X,tmp_data[2]=0x%02X,tmp_data[1]=0x%02X,tmp_data[0]=0x%02X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1], tmp_data[0]); + g_core_fp.fp_register_write(pfw_op->addr_sorting_mode_en, DATA_LEN_4, tmp_data, 0); + + return NO_ERR; +} + +static int himax_mcu_check_sorting_mode(uint8_t *tmp_data) +{ + + g_core_fp.fp_register_read(pfw_op->addr_sorting_mode_en, DATA_LEN_4, tmp_data, 0); + I("%s: tmp_data[0]=%x,tmp_data[1]=%x\n", __func__, tmp_data[0], tmp_data[1]); + + return NO_ERR; +} + +static int himax_mcu_switch_mode(int mode) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t mode_wirte_cmd; + uint8_t mode_read_cmd; + int result = -1; + int retry = 200; + + I("%s: Entering\n", __func__); + + if (mode == 0) { /* normal mode */ + mode_wirte_cmd = pfw_op->data_normal_cmd[0]; + mode_read_cmd = pfw_op->data_normal_status[0]; + } else { /* sorting mode */ + mode_wirte_cmd = pfw_op->data_sorting_cmd[0]; + mode_read_cmd = pfw_op->data_sorting_status[0]; + } + + g_core_fp.fp_sense_off(true); + /*g_core_fp.fp_interface_on();*/ + /* clean up FW status */ + g_core_fp.fp_register_write(psram_op->addr_rawdata_addr, DATA_LEN_4, psram_op->addr_rawdata_end, 0); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = mode_wirte_cmd; + tmp_data[0] = mode_wirte_cmd; + g_core_fp.fp_assign_sorting_mode(tmp_data); + g_core_fp.fp_idle_mode(1); + g_core_fp.fp_reload_disable(1); + + /* To stable the sorting*/ + if (mode) { + g_core_fp.fp_register_write(pdriver_op->addr_fw_define_rxnum_txnum_maxpt, DATA_LEN_4, pdriver_op->data_fw_define_rxnum_txnum_maxpt_sorting, 0); + } else { + g_core_fp.fp_register_write(pfw_op->addr_set_frame_addr, DATA_LEN_4, pfw_op->data_set_frame, 0); + g_core_fp.fp_register_write(pdriver_op->addr_fw_define_rxnum_txnum_maxpt, DATA_LEN_4, pdriver_op->data_fw_define_rxnum_txnum_maxpt_normal, 0); + } + + g_core_fp.fp_sense_on(0x01); + + while (retry != 0) { + I("[%d] %s Read\n", retry, __func__); + g_core_fp.fp_check_sorting_mode(tmp_data); + msleep(100); + I("mode_read_cmd(0)=0x%2.2X,mode_read_cmd(1)=0x%2.2X\n", tmp_data[0], tmp_data[1]); + + if (tmp_data[0] == mode_read_cmd && tmp_data[1] == mode_read_cmd) { + I("Read OK!\n"); + result = 0; + break; + } + + g_core_fp.fp_register_read(pfw_op->addr_chk_fw_status, DATA_LEN_4, tmp_data, 0); + + if (tmp_data[0] == 0x00 && tmp_data[1] == 0x00 && tmp_data[2] == 0x00 && tmp_data[3] == 0x00) { + E("%s,: FW Stop!\n", __func__); + break; + } + + retry--; + } + + if (result == 0) { + if (mode == 0) { /*normal mode*/ + return HX_NORMAL_MODE; + } else { /*sorting mode*/ + return HX_SORTING_MODE; + } + } else { /*change mode fail*/ + return HX_CHANGE_MODE_FAIL; + } +} + +static int himax_mcu_get_max_dc(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + int dc_max = 0; + + g_core_fp.fp_register_read(pfw_op->addr_max_dc, DATA_LEN_4, tmp_data, 0); + I("%s: tmp_data[0]=%x,tmp_data[1]=%x\n", __func__, tmp_data[0], tmp_data[1]); + + dc_max = tmp_data[1] << 8 | tmp_data[0]; + I("%s: dc max = %d\n", __func__, dc_max); + return dc_max; +} + +static uint8_t himax_mcu_read_DD_status(uint8_t *cmd_set, uint8_t *tmp_data) +{ + int cnt = 0; + uint8_t req_size = cmd_set[0]; + + cmd_set[3] = pfw_op->data_dd_request[0]; + g_core_fp.fp_register_write(pfw_op->addr_dd_handshak_addr, DATA_LEN_4, cmd_set, 0); + I("%s: cmd_set[0] = 0x%02X,cmd_set[1] = 0x%02X,cmd_set[2] = 0x%02X,cmd_set[3] = 0x%02X\n", + __func__, cmd_set[0], cmd_set[1], cmd_set[2], cmd_set[3]); + + /* Doing hand shaking 0xAA -> 0xBB */ + for (cnt = 0; cnt < 100; cnt++) { + g_core_fp.fp_register_read(pfw_op->addr_dd_handshak_addr, DATA_LEN_4, tmp_data, 0); + usleep_range(10000, 11000); + + if (tmp_data[3] == pfw_op->data_dd_ack[0]) { + I("%s Data ready goto moving data\n", __func__); + goto FINALIZE; + } else { + if (cnt >= 99) { + I("%s Data not ready in FW\n", __func__); + return FW_NOT_READY; + } + } + } +FINALIZE: + g_core_fp.fp_register_read(pfw_op->addr_dd_data_addr, req_size, tmp_data, 0); + return NO_ERR; +} +#ifdef HX_AUTO_UPDATE_FW +static int hx_mcu_diff_overlay_bin(void) +{ + int rslt = 0; + int diff_val = 0; + + diff_val = g_i_FW_VER; + I("%s:Now fw ID is 0x%04X\n", __func__, diff_val); + diff_val = (diff_val >> 12); + I("%s:Now diff value=0x%04X\n", __func__, diff_val); + + if (diff_val == 1) + I("%s:Now size should be 128K!\n", __func__); + else + I("%s:Now size should be 64K!\n", __func__); + rslt = diff_val; + return rslt; +} +#endif + +/* FW side end*/ +#endif + +#ifdef CORE_FLASH +/* FLASH side start*/ +static void himax_mcu_chip_erase(void) +{ + g_core_fp.fp_interface_on(); + + /* Reset power saving level */ + if (g_core_fp.fp_init_psl != NULL) + g_core_fp.fp_init_psl(); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_fmt, DATA_LEN_4, pflash_op->data_spi200_trans_fmt, 0); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, pflash_op->data_spi200_trans_ctrl_2, 0); + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_2, 0); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_3, 0); + msleep(2000); + + if (!g_core_fp.fp_wait_wip(100)) + E("%s: Chip_Erase Fail\n", __func__); + +} + +static bool himax_mcu_block_erase(int start_addr, int length) /*complete not yet*/ +{ + uint32_t page_prog_start = 0; + uint32_t block_size = 0x10000; + + uint8_t tmp_data[4] = {0}; + + g_core_fp.fp_interface_on(); + + g_core_fp.fp_init_psl(); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_fmt, DATA_LEN_4, pflash_op->data_spi200_trans_fmt, 0); + + for (page_prog_start = start_addr; page_prog_start < start_addr + length; page_prog_start = page_prog_start + block_size) { + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, pflash_op->data_spi200_trans_ctrl_2, 0); + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_2, 0); + + tmp_data[3] = (page_prog_start >> 24)&0xFF; + tmp_data[2] = (page_prog_start >> 16)&0xFF; + tmp_data[1] = (page_prog_start >> 8)&0xFF; + tmp_data[0] = page_prog_start&0xFF; + g_core_fp.fp_register_write(pflash_op->addr_spi200_addr, DATA_LEN_4, tmp_data, 0); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, pflash_op->data_spi200_trans_ctrl_3, 0); + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_4, 0); + msleep(1000); + + if (!g_core_fp.fp_wait_wip(100)) { + E("%s:Erase Fail\n", __func__); + return false; + } + } + + I("%s:END\n", __func__); + return true; +} + +static bool himax_mcu_sector_erase(int start_addr) +{ + return true; +} + +static void himax_mcu_flash_programming(uint8_t *FW_content, int FW_Size) +{ + int page_prog_start = 0, i = 0, j = 0, k = 0; + int program_length = PROGRAM_SZ; + uint8_t tmp_data[DATA_LEN_4]; + uint8_t buring_data[FLASH_RW_MAX_LEN]; /* Read for flash data, 128K*/ + /* 4 bytes for padding*/ + g_core_fp.fp_interface_on(); + + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_fmt, DATA_LEN_4, pflash_op->data_spi200_trans_fmt, 0); + + for (page_prog_start = 0; page_prog_start < FW_Size; page_prog_start += FLASH_RW_MAX_LEN) { + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, pflash_op->data_spi200_trans_ctrl_2, 0); + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_2, 0); + + /*Programmable size = 1 page = 256 bytes, word_number = 256 byte / 4 = 64*/ + g_core_fp.fp_register_write(pflash_op->addr_spi200_trans_ctrl, DATA_LEN_4, pflash_op->data_spi200_trans_ctrl_4, 0); + + /* Flash start address 1st : 0x0000_0000*/ + if (page_prog_start < 0x100) { + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = (uint8_t)page_prog_start; + } else if (page_prog_start >= 0x100 && page_prog_start < 0x10000) { + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = (uint8_t)(page_prog_start >> 8); + tmp_data[0] = (uint8_t)page_prog_start; + } else if (page_prog_start >= 0x10000 && page_prog_start < 0x1000000) { + tmp_data[3] = 0x00; + tmp_data[2] = (uint8_t)(page_prog_start >> 16); + tmp_data[1] = (uint8_t)(page_prog_start >> 8); + tmp_data[0] = (uint8_t)page_prog_start; + } + g_core_fp.fp_register_write(pflash_op->addr_spi200_addr, DATA_LEN_4, tmp_data, 0); + + for (i = 0; i < ADDR_LEN_4; i++) + buring_data[i] = pflash_op->addr_spi200_data[i]; + + + for (i = page_prog_start, j = 0; i < 16 + page_prog_start; i++, j++) + buring_data[j + ADDR_LEN_4] = FW_content[i]; + + + if (himax_bus_write(pic_op->addr_ahb_addr_byte_0[0], buring_data, ADDR_LEN_4 + 16, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + g_core_fp.fp_register_write(pflash_op->addr_spi200_cmd, DATA_LEN_4, pflash_op->data_spi200_cmd_6, 0); + + for (j = 0; j < 5; j++) { + for (i = (page_prog_start + 16 + (j * 48)), k = 0; i < (page_prog_start + 16 + (j * 48)) + program_length; i++, k++) + buring_data[k + ADDR_LEN_4] = FW_content[i]; + + if (himax_bus_write(pic_op->addr_ahb_addr_byte_0[0], buring_data, program_length + ADDR_LEN_4, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + } + + if (!g_core_fp.fp_wait_wip(1)) + E("%s:Flash_Programming Fail\n", __func__); + + } +} + +static void himax_mcu_flash_page_write(uint8_t *write_addr, int length, uint8_t *write_data) +{ +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_32k(unsigned char *fw, int len, bool change_iref) +{ + /* Not use */ + return 0; +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_60k(unsigned char *fw, int len, bool change_iref) +{ + /* Not use */ + return 0; +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_64k(unsigned char *fw, int len, bool change_iref) +{ + int burnFW_success = 0; + + if (len != FW_SIZE_64k) { + E("%s: The file size is not 64K bytes\n", __func__); + return false; + } + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + g_core_fp.fp_sense_off(true); + g_core_fp.fp_block_erase(0x00, FW_SIZE_64k); + g_core_fp.fp_flash_programming(fw, FW_SIZE_64k); + + if (g_core_fp.fp_check_CRC(pfw_op->addr_program_reload_from, FW_SIZE_64k) == 0) + burnFW_success = 1; + + /*RawOut select initial*/ + g_core_fp.fp_register_write(pfw_op->addr_raw_out_sel, sizeof(pfw_op->data_clear), pfw_op->data_clear, 0); + /*DSRAM func initial*/ + g_core_fp.fp_assign_sorting_mode(pfw_op->data_clear); + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + /*System reset*/ + g_core_fp.fp_system_reset(); +#endif + return burnFW_success; +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_124k(unsigned char *fw, int len, bool change_iref) +{ + /* Not use */ + return 0; +} + +static int himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_128k(unsigned char *fw, int len, bool change_iref) +{ + int burnFW_success = 0; + + if (len != FW_SIZE_128k) { + E("%s: The file size is not 128K bytes\n", __func__); + return false; + } + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + g_core_fp.fp_system_reset(); +#endif + g_core_fp.fp_sense_off(true); + g_core_fp.fp_block_erase(0x00, FW_SIZE_128k); + g_core_fp.fp_flash_programming(fw, FW_SIZE_128k); + + if (g_core_fp.fp_check_CRC(pfw_op->addr_program_reload_from, FW_SIZE_128k) == 0) + burnFW_success = 1; + + /*RawOut select initial*/ + g_core_fp.fp_register_write(pfw_op->addr_raw_out_sel, sizeof(pfw_op->data_clear), pfw_op->data_clear, 0); + /*DSRAM func initial*/ + g_core_fp.fp_assign_sorting_mode(pfw_op->data_clear); + +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_ic_reset(false, false); +#else + /*System reset*/ + g_core_fp.fp_system_reset(); +#endif + return burnFW_success; +} + +static void himax_mcu_flash_dump_func(uint8_t local_flash_command, int Flash_Size, uint8_t *flash_buffer) +{ + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t buffer[256]; + int page_prog_start = 0; + + g_core_fp.fp_sense_off(true); + g_core_fp.fp_burst_enable(1); + + for (page_prog_start = 0; page_prog_start < Flash_Size; page_prog_start += 128) { + tmp_addr[0] = page_prog_start % 0x100; + tmp_addr[1] = (page_prog_start >> 8) % 0x100; + tmp_addr[2] = (page_prog_start >> 16) % 0x100; + tmp_addr[3] = page_prog_start / 0x1000000; + himax_mcu_register_read(tmp_addr, 128, buffer, 0); + memcpy(&flash_buffer[page_prog_start], buffer, 128); + } + + g_core_fp.fp_burst_enable(0); + g_core_fp.fp_sense_on(0x01); +} + +static bool himax_mcu_flash_lastdata_check(uint32_t size) +{ + uint8_t tmp_addr[4]; + uint32_t start_addr = 0xFF80; /* 64K - 0x80, which is the address of the last 128bytes in 64K, default value*/ + uint32_t temp_addr = 0; + uint32_t flash_page_len = 0x80; + uint8_t flash_tmp_buffer[128]; + + start_addr = size - flash_page_len; /* In order to match other size of fw*/ + I("%s: Now size is %d, the start_addr is 0x%04X\n", __func__, size, start_addr); + for (temp_addr = start_addr; temp_addr < (start_addr + flash_page_len); temp_addr = temp_addr + flash_page_len) { + /*I("temp_addr=%d,tmp_addr[0]=0x%2X, tmp_addr[1]=0x%2X,tmp_addr[2]=0x%2X,tmp_addr[3]=0x%2X\n", temp_addr,tmp_addr[0], tmp_addr[1], tmp_addr[2],tmp_addr[3]);*/ + tmp_addr[0] = temp_addr % 0x100; + tmp_addr[1] = (temp_addr >> 8) % 0x100; + tmp_addr[2] = (temp_addr >> 16) % 0x100; + tmp_addr[3] = temp_addr / 0x1000000; + g_core_fp.fp_register_read(tmp_addr, flash_page_len, &flash_tmp_buffer[0], 0); + } + + if ((!flash_tmp_buffer[flash_page_len-4]) && (!flash_tmp_buffer[flash_page_len-3]) && (!flash_tmp_buffer[flash_page_len-2]) && (!flash_tmp_buffer[flash_page_len-1])) { + I("Fail, Last four Bytes are flash_buffer[FFFC]=0x%2X,flash_buffer[FFFD]=0x%2X,flash_buffer[FFFE]=0x%2X,flash_buffer[FFFF]=0x%2X\n", + flash_tmp_buffer[flash_page_len-4], flash_tmp_buffer[flash_page_len-3], flash_tmp_buffer[flash_page_len-2], flash_tmp_buffer[flash_page_len-1]); + goto FAIL;/*FAIL*/ + } else if ((flash_tmp_buffer[flash_page_len-4] == 0xFF) && (flash_tmp_buffer[flash_page_len-3] == 0xFF) && (flash_tmp_buffer[flash_page_len-2] == 0xFF) && (flash_tmp_buffer[flash_page_len-1] == 0xFF)) { + I("Fail, Last four Bytes are flash_buffer[FFFC]=0x%2X,flash_buffer[FFFD]=0x%2X,flash_buffer[FFFE]=0x%2X,flash_buffer[FFFF]=0x%2X\n", + flash_tmp_buffer[flash_page_len-4], flash_tmp_buffer[flash_page_len-3], flash_tmp_buffer[flash_page_len-2], flash_tmp_buffer[flash_page_len-1]); + goto FAIL; + } else { + I("flash_buffer[FFFC]=0x%2X,flash_buffer[FFFD]=0x%2X,flash_buffer[FFFE]=0x%2X,flash_buffer[FFFF]=0x%2X\n", + flash_tmp_buffer[flash_page_len-4], flash_tmp_buffer[flash_page_len-3], flash_tmp_buffer[flash_page_len-2], flash_tmp_buffer[flash_page_len-1]); + return 0;/*PASS*/ + } + return 0; +FAIL: + return 1; +} +static int hx_mcu_diff_overlay_flash(void) +{ + int rslt = 0; + int diff_val = 0; + + diff_val = (ic_data->vendor_fw_ver); + I("%s:Now fw ID is 0x%04X\n", __func__, diff_val); + diff_val = (diff_val >> 12); + I("%s:Now diff value=0x%04X\n", __func__, diff_val); + + if (diff_val == 1) + I("%s:Now size should be 128K!\n", __func__); + else + I("%s:Now size should be 64K!\n", __func__); + rslt = diff_val; + return rslt; +} + +/* FLASH side end*/ +#endif + +#ifdef CORE_SRAM +/* SRAM side start*/ +static void himax_mcu_sram_write(uint8_t *FW_content) +{ +} + +static bool himax_mcu_sram_verify(uint8_t *FW_File, int FW_Size) +{ + return true; +} + +static bool himax_mcu_get_DSRAM_data(uint8_t *info_data, bool DSRAM_Flag) +{ + int i = 0; + unsigned char tmp_addr[ADDR_LEN_4]; + unsigned char tmp_data[DATA_LEN_4]; + uint8_t max_i2c_size = MAX_I2C_TRANS_SZ; + uint8_t x_num = ic_data->HX_RX_NUM; + uint8_t y_num = ic_data->HX_TX_NUM; + /*int m_key_num = 0;*/ + int total_size = (x_num * y_num + x_num + y_num) * 2 + 4; + int total_size_temp; + int mutual_data_size = x_num * y_num * 2; + int total_read_times = 0; + int address = 0; + uint8_t *temp_info_data; /*max mkey size = 8*/ + uint16_t check_sum_cal = 0; + int fw_run_flag = -1; + + temp_info_data = kcalloc((total_size + 8), sizeof(uint8_t), GFP_KERNEL); + /* 1. Read number of MKey R100070E8H to determin data size */ + /* m_key_num = ic_data->HX_BT_NUM; */ + /* I("%s,m_key_num=%d\n",__func__ ,m_key_num); */ + /* total_size += m_key_num * 2; */ + /* 2. Start DSRAM Rawdata and Wait Data Ready */ + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = psram_op->passwrd_start[1]; + tmp_data[0] = psram_op->passwrd_start[0]; + fw_run_flag = himax_write_read_reg(psram_op->addr_rawdata_addr, tmp_data, psram_op->passwrd_end[1], psram_op->passwrd_end[0]); + + if (fw_run_flag < 0) { + I("%s Data NOT ready => bypass\n", __func__); + goto FAIL; + } + + /* 3. Read RawData */ + total_size_temp = total_size; + I("%s: tmp_data[0] = 0x%02X,tmp_data[1] = 0x%02X,tmp_data[2] = 0x%02X,tmp_data[3] = 0x%02X\n", + __func__, psram_op->addr_rawdata_addr[0], psram_op->addr_rawdata_addr[1], psram_op->addr_rawdata_addr[2], psram_op->addr_rawdata_addr[3]); + tmp_addr[0] = psram_op->addr_rawdata_addr[0]; + tmp_addr[1] = psram_op->addr_rawdata_addr[1]; + tmp_addr[2] = psram_op->addr_rawdata_addr[2]; + tmp_addr[3] = psram_op->addr_rawdata_addr[3]; + + if (total_size % max_i2c_size == 0) + total_read_times = total_size / max_i2c_size; + else + total_read_times = total_size / max_i2c_size + 1; + + for (i = 0; i < total_read_times; i++) { + address = (psram_op->addr_rawdata_addr[3] << 24) + + (psram_op->addr_rawdata_addr[2] << 16) + + (psram_op->addr_rawdata_addr[1] << 8) + + psram_op->addr_rawdata_addr[0] + i * max_i2c_size; + /*I("%s address = %08X\n", __func__, address);*/ + + tmp_addr[3] = (uint8_t)((address >> 24) & 0x00FF); + tmp_addr[2] = (uint8_t)((address >> 16) & 0x00FF); + tmp_addr[1] = (uint8_t)((address >> 8) & 0x00FF); + tmp_addr[0] = (uint8_t)((address) & 0x00FF); + + if (total_size_temp >= max_i2c_size) { + g_core_fp.fp_register_read(tmp_addr, max_i2c_size, &temp_info_data[i * max_i2c_size], 0); + total_size_temp = total_size_temp - max_i2c_size; + } else { + /*I("last total_size_temp=%d\n",total_size_temp);*/ + g_core_fp.fp_register_read(tmp_addr, total_size_temp % max_i2c_size, &temp_info_data[i * max_i2c_size], 0); + } + } + + /* 4. FW stop outputing */ + tmp_data[3] = temp_info_data[3]; tmp_data[2] = temp_info_data[2]; + tmp_data[1] = 0x00; tmp_data[0] = 0x00; + g_core_fp.fp_register_write(psram_op->addr_rawdata_addr, DATA_LEN_4, tmp_data, 0); + + /* 5. Data Checksum Check */ + for (i = 2; i < total_size; i += 2)/* 2:PASSWORD NOT included */ + check_sum_cal += (temp_info_data[i + 1] * 256 + temp_info_data[i]); + + if (check_sum_cal % 0x10000 != 0) { + I("%s check_sum_cal fail=%2X\n", __func__, check_sum_cal); + goto FAIL; + } else { + memcpy(info_data, &temp_info_data[4], mutual_data_size * sizeof(uint8_t)); + /*I("%s checksum PASS\n", __func__);*/ + } + + /* MPAP debug message ********************************************/ + for (i = total_size-16; i < total_size-1; i+=2) { + check_sum_cal = temp_info_data[i+1]<<8 | temp_info_data[i]; + I("mpdmsg %2X\n", check_sum_cal); + } + /*****************************************************************/ + + kfree(temp_info_data); + return true; +FAIL: + kfree(temp_info_data); + return false; +} +/* SRAM side end*/ +#endif + +#ifdef CORE_DRIVER + +static void himax_mcu_init_ic(void) +{ + I("%s: use default incell init.\n", __func__); +} + +#ifdef HX_AUTO_UPDATE_FW +static int himax_mcu_fw_ver_bin(void) +{ + I("%s: use default incell address.\n", __func__); + I("%s:Entering!\n", __func__); + if (i_CTPM_FW != NULL) { + I("Catch fw version in bin file!\n"); + g_i_FW_VER = (i_CTPM_FW[FW_VER_MAJ_FLASH_ADDR] << 8) | i_CTPM_FW[FW_VER_MIN_FLASH_ADDR]; + g_i_CFG_VER = (i_CTPM_FW[CFG_VER_MAJ_FLASH_ADDR] << 8) | i_CTPM_FW[CFG_VER_MIN_FLASH_ADDR]; + g_i_CID_MAJ = i_CTPM_FW[CID_VER_MAJ_FLASH_ADDR]; + g_i_CID_MIN = i_CTPM_FW[CID_VER_MIN_FLASH_ADDR]; + } else { + I("FW data is null!\n"); + return 1; + } + return NO_ERR; +} +#endif + + +#ifdef HX_RST_PIN_FUNC +static void himax_mcu_pin_reset(void) +{ + I("%s: Now reset the Touch chip,private_ts->vdd_1v8_always_on = %d \n", + __func__,private_ts->vdd_1v8_always_on); + himax_rst_gpio_set(private_ts->rst_gpio, 0); +#if defined(__HIMAX_HX83102_MOD__) + msleep(5); +#else + if(private_ts->vdd_1v8_always_on) + msleep(10); + else + msleep(20); +#endif + himax_rst_gpio_set(private_ts->rst_gpio, 1); +#ifdef HX_ZERO_FLASH +#if defined(__HIMAX_HX83102_MOD__) + msleep(2); +#else + msleep(10); +#endif +#else + msleep(50); +#endif + +} + +static void himax_mcu_ic_reset(uint8_t loadconfig, uint8_t int_off) +{ + struct himax_ts_data *ts = private_ts; + + HX_HW_RESET_ACTIVATE = 0; + I("%s,status: loadconfig=%d,int_off=%d\n", __func__, loadconfig, int_off); + + if (ts->rst_gpio >= 0) { + if (int_off) + g_core_fp.fp_irq_switch(0); + + g_core_fp.fp_pin_reset(); + + if (loadconfig) + g_core_fp.fp_reload_config(); + + if (int_off) + g_core_fp.fp_irq_switch(1); + + } +} +#endif + +#ifndef HX_FIX_TOUCH_INFO +static uint8_t himax_mcu_tp_info_check(void) +{ + uint8_t rx = pdriver_op->data_df_rx[0]; + uint8_t tx = pdriver_op->data_df_tx[0]; + uint8_t pt = pdriver_op->data_df_pt[0]; + uint16_t x_res = pdriver_op->data_df_x_res[1] << 8 | pdriver_op->data_df_x_res[0]; + uint16_t y_res = pdriver_op->data_df_y_res[1] << 8 | pdriver_op->data_df_y_res[0]; + uint8_t err_cnt = 0; + + if (ic_data->HX_RX_NUM < (rx / 2) || ic_data->HX_RX_NUM > (rx * 3 / 2)) { + ic_data->HX_RX_NUM = rx; + err_cnt |= 0x01; + } + if (ic_data->HX_TX_NUM < (tx / 2) || ic_data->HX_TX_NUM > (tx * 3 / 2)) { + ic_data->HX_TX_NUM = tx; + err_cnt |= 0x02; + } + if (ic_data->HX_MAX_PT < (pt / 2) || ic_data->HX_MAX_PT > (pt * 3 / 2)) { + ic_data->HX_MAX_PT = pt; + err_cnt |= 0x04; + } + if (ic_data->HX_Y_RES < (y_res / 2) || ic_data->HX_Y_RES > (y_res * 3 / 2)) { + ic_data->HX_Y_RES = y_res; + err_cnt |= 0x08; + } + if (ic_data->HX_X_RES < (x_res / 2) || ic_data->HX_X_RES > (x_res * 3 / 2)) { + ic_data->HX_X_RES = x_res; + err_cnt |= 0x10; + } + return err_cnt; +} +#endif + +static void himax_mcu_touch_information(void) +{ +#ifndef HX_FIX_TOUCH_INFO + char data[DATA_LEN_8] = {0}; + uint8_t err_cnt = 0; + + I(" DT-%s:touch_info: HX_RX_NUM = %d, HX_TX_NUM = %d, HX_BT_NUM = %d,HX_X_RES = %d,HX_Y_RES = %d, HX_MAX_PT = %d, HX_XY_REVERSE = %d, HX_INT_IS_EDGE = %d\n", __func__, + ic_data->HX_RX_NUM,ic_data->HX_TX_NUM,ic_data->HX_BT_NUM,ic_data->HX_X_RES,ic_data->HX_Y_RES,ic_data->HX_MAX_PT,ic_data->HX_XY_REVERSE,ic_data->HX_INT_IS_EDGE); + + if ((0 == ic_data->HX_RX_NUM) || (0 == ic_data->HX_TX_NUM) || (0 == ic_data->HX_X_RES) || (0 == ic_data->HX_Y_RES)) { + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_rxnum_txnum_maxpt, DATA_LEN_8, data, 0); + ic_data->HX_RX_NUM = data[2]; + ic_data->HX_TX_NUM = data[3]; + ic_data->HX_MAX_PT = data[4]; + /*I("%s : HX_RX_NUM=%d,ic_data->HX_TX_NUM=%d,ic_data->HX_MAX_PT=%d\n",__func__,ic_data->HX_RX_NUM,ic_data->HX_TX_NUM,ic_data->HX_MAX_PT);*/ + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_xy_res_enable, DATA_LEN_4, data, 0); + + /*I("%s : c_data->HX_XY_REVERSE=0x%2.2X\n",__func__,data[1]);*/ + if ((data[1] & 0x04) == 0x04) + ic_data->HX_XY_REVERSE = true; + else + ic_data->HX_XY_REVERSE = false; + + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_x_y_res, DATA_LEN_4, data, 0); + ic_data->HX_Y_RES = data[0] * 256 + data[1]; + ic_data->HX_X_RES = data[2] * 256 + data[3]; + /*I("%s : ic_data->HX_Y_RES=%d,ic_data->HX_X_RES=%d\n",__func__,ic_data->HX_Y_RES,ic_data->HX_X_RES);*/ + + g_core_fp.fp_register_read(pdriver_op->addr_fw_define_int_is_edge, DATA_LEN_4, data, 0); + /*I("%s : data[0]=0x%2.2X,data[1]=0x%2.2X,data[2]=0x%2.2X,data[3]=0x%2.2X\n",__func__,data[0],data[1],data[2],data[3]);*/ + /*I("data[0] & 0x01 = %d\n",(data[0] & 0x01));*/ + if ((data[1] & 0x01) == 1) + ic_data->HX_INT_IS_EDGE = true; + else + ic_data->HX_INT_IS_EDGE = false; + + /*1. Read number of MKey R100070E8H to determin data size*/ + g_core_fp.fp_register_read(psram_op->addr_mkey, DATA_LEN_4, data, 0); + /* I("%s: tmp_data[0] = 0x%02X,tmp_data[1] = 0x%02X,tmp_data[2] = 0x%02X,tmp_data[3] = 0x%02X\n",*/ + /*__func__, tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]);*/ + ic_data->HX_BT_NUM = data[0] & 0x03; + + err_cnt = himax_mcu_tp_info_check(); + if (err_cnt > 0) + E("TP Info from IC is wrong, err_cnt = 0x%X", err_cnt); + } +#else + ic_data->HX_RX_NUM = FIX_HX_RX_NUM; + ic_data->HX_TX_NUM = FIX_HX_TX_NUM; + ic_data->HX_BT_NUM = FIX_HX_BT_NUM; + ic_data->HX_X_RES = FIX_HX_X_RES; + ic_data->HX_Y_RES = FIX_HX_Y_RES; + ic_data->HX_MAX_PT = FIX_HX_MAX_PT; + ic_data->HX_XY_REVERSE = FIX_HX_XY_REVERSE; + ic_data->HX_INT_IS_EDGE = FIX_HX_INT_IS_EDGE; + +#endif + I("%s:HX_RX_NUM =%d,HX_TX_NUM =%d,HX_MAX_PT=%d\n", __func__, ic_data->HX_RX_NUM, ic_data->HX_TX_NUM, ic_data->HX_MAX_PT); + I("%s:HX_XY_REVERSE =%d,HX_Y_RES =%d,HX_X_RES=%d\n", __func__, ic_data->HX_XY_REVERSE, ic_data->HX_Y_RES, ic_data->HX_X_RES); + I("%s:HX_INT_IS_EDGE =%d\n", __func__, ic_data->HX_INT_IS_EDGE); +} + +static void himax_mcu_reload_config(void) +{ + if (himax_report_data_init()) + E("%s: allocate data fail\n", __func__); + + g_core_fp.fp_sense_on(0x00); +} + +static int himax_mcu_get_touch_data_size(void) +{ + return HIMAX_TOUCH_DATA_SIZE; +} + +static int himax_mcu_hand_shaking(void) +{ + /* 0:Running, 1:Stop, 2:I2C Fail */ + int result = 0; + return result; +} + +static int himax_mcu_determin_diag_rawdata(int diag_command) +{ + return diag_command % 10; +} + +static int himax_mcu_determin_diag_storage(int diag_command) +{ + return diag_command / 10; +} + +static int himax_mcu_cal_data_len(int raw_cnt_rmd, int HX_MAX_PT, int raw_cnt_max) +{ + int RawDataLen; + + if (raw_cnt_rmd != 0x00) + RawDataLen = MAX_I2C_TRANS_SZ - ((HX_MAX_PT + raw_cnt_max + 3) * 4) - 1; + else + RawDataLen = MAX_I2C_TRANS_SZ - ((HX_MAX_PT + raw_cnt_max + 2) * 4) - 1; + + return RawDataLen; +} + +static bool himax_mcu_diag_check_sum(struct himax_report_data *hx_touch_data) +{ + uint16_t check_sum_cal = 0; + int i; + + /* Check 128th byte CRC */ + for (i = 0, check_sum_cal = 0; i < (hx_touch_data->touch_all_size - hx_touch_data->touch_info_size); i += 2) + check_sum_cal += (hx_touch_data->hx_rawdata_buf[i + 1] * FLASH_RW_MAX_LEN + hx_touch_data->hx_rawdata_buf[i]); + + + if (check_sum_cal % HX64K != 0) { + I("%s fail=%2X\n", __func__, check_sum_cal); + return 0; + } + + return 1; +} + +static void himax_mcu_diag_parse_raw_data(struct himax_report_data *hx_touch_data, int mul_num, int self_num, uint8_t diag_cmd, int32_t *mutual_data, int32_t *self_data) +{ + diag_mcu_parse_raw_data(hx_touch_data, mul_num, self_num, diag_cmd, mutual_data, self_data); +} + +#ifdef HX_ESD_RECOVERY +static int himax_mcu_ic_esd_recovery(int hx_esd_event, int hx_zero_event, int length) +{ + int ret_val = NO_ERR; + + if (hx_esd_event == length) { + g_zero_event_count = 0; + ret_val = HX_ESD_EVENT; + } else if (hx_zero_event == length) { + if (g_zero_event_count > 5) { + g_zero_event_count = 0; + I("[HIMAX TP MSG]: ESD event checked - ALL Zero.\n"); + ret_val = HX_ESD_EVENT; + } else { + g_zero_event_count++; + I("[HIMAX TP MSG]: ALL Zero event is %d times.\n", g_zero_event_count); + ret_val = HX_ZERO_EVENT_COUNT; + } + } + + return ret_val; +} + +static void himax_mcu_esd_ic_reset(void) +{ + HX_ESD_RESET_ACTIVATE = 0; +#ifdef HX_RST_PIN_FUNC + himax_mcu_pin_reset(); +#else + himax_mcu_system_reset(); +#endif + I("%s:\n", __func__); +} +#endif +#ifdef HX_TP_PROC_GUEST_INFO +char *g_checksum_str = "check sum fail"; +/* char *g_guest_info_item[] = { + * "projectID", + * "CGColor", + * "BarCode", + * "Reserve1", + * "Reserve2", + * "Reserve3", + * "Reserve4", + * "Reserve5", + * "VCOM", + * "Vcom-3Gar", + * NULL + * }; + */ + +static int himax_guest_info_get_status(void) +{ + return g_guest_info_data->g_guest_info_ongoing; +} +static void himax_guest_info_set_status(int setting) +{ + g_guest_info_data->g_guest_info_ongoing = setting; +} + +static int himax_guest_info_read(uint32_t start_addr, uint8_t *flash_tmp_buffer) +{ + uint32_t temp_addr = 0; + uint8_t tmp_addr[4]; + uint32_t flash_page_len = 0x1000; + /* uint32_t checksum = 0x00; */ + int result = 0; + + + I("Reading guest info in start_addr = 0x%08X !\n", start_addr); + + tmp_addr[0] = start_addr % 0x100; + tmp_addr[1] = (start_addr >> 8) % 0x100; + tmp_addr[2] = (start_addr >> 16) % 0x100; + tmp_addr[3] = start_addr / 0x1000000; + I("Now start addr: tmp_addr[0]=0x%2X,tmp_addr[1]=0x%2X,tmp_addr[2]=0x%2X,tmp_addr[3]=0x%2X\n", tmp_addr[0], tmp_addr[1], tmp_addr[2], tmp_addr[3]); + result = g_core_fp.fp_check_CRC(tmp_addr, flash_page_len); + I("Checksum = 0x%8X\n", result); + if (result != 0) + goto END_FUNC; + + for (temp_addr = start_addr; temp_addr < (start_addr + flash_page_len); temp_addr = temp_addr + 128) { + + /* I("temp_addr=%d,tmp_addr[0]=0x%2X,tmp_addr[1]=0x%2X,tmp_addr[2]=0x%2X,tmp_addr[3]=0x%2X\n",temp_addr,tmp_addr[0],tmp_addr[1],tmp_addr[2],tmp_addr[3]); */ + tmp_addr[0] = temp_addr % 0x100; + tmp_addr[1] = (temp_addr >> 8) % 0x100; + tmp_addr[2] = (temp_addr >> 16) % 0x100; + tmp_addr[3] = temp_addr / 0x1000000; + g_core_fp.fp_register_read(tmp_addr, 128, &flash_tmp_buffer[temp_addr - start_addr], false); + /* memcpy(&flash_tmp_buffer[temp_addr - start_addr],buffer,128);*/ + } + +END_FUNC: + return result; +} + +static int hx_read_guest_info(void) +{ + /* uint8_t tmp_addr[4]; */ + uint32_t panel_color_addr = 0x10000;/*64k*/ + + uint32_t info_len; + uint32_t flash_page_len = 0x1000;/*4k*/ + uint8_t *flash_tmp_buffer = NULL; + /* uint32_t temp_addr = 0; */ + uint8_t temp_str[128]; + int i = 0; + int custom_info_temp = 0; + int checksum = 0; + + himax_guest_info_set_status(1); + + flash_tmp_buffer = kcalloc(HX_GUEST_INFO_SIZE * flash_page_len, sizeof(uint8_t), GFP_KERNEL); + if (flash_tmp_buffer == NULL) { + I("%s: Memory allocate fail!\n", __func__); + return MEM_ALLOC_FAIL; + } + + g_core_fp.fp_sense_off(true); + /* g_core_fp.fp_burst_enable(1); */ + + for (custom_info_temp = 0; custom_info_temp < HX_GUEST_INFO_SIZE; custom_info_temp++) { + checksum = himax_guest_info_read(panel_color_addr+custom_info_temp * flash_page_len, &flash_tmp_buffer[custom_info_temp * flash_page_len]); + if (checksum != 0) { + E("%s:Checksum Fail! g_checksum_str len=%d\n", __func__, (int)strlen(g_checksum_str)); + memcpy(&g_guest_info_data->g_guest_str_in_format[custom_info_temp][0], g_checksum_str, (int)strlen(g_checksum_str)); + memcpy(&g_guest_info_data->g_guest_str[custom_info_temp][0], g_checksum_str, (int)strlen(g_checksum_str)); + continue; + } + + info_len = flash_tmp_buffer[custom_info_temp * flash_page_len] + + (flash_tmp_buffer[custom_info_temp*flash_page_len + 1] << 8) + + (flash_tmp_buffer[custom_info_temp*flash_page_len + 2] << 16) + + (flash_tmp_buffer[custom_info_temp*flash_page_len + 3] << 24); + + I("Now custom_info_temp = %d\n", custom_info_temp); + + I("Now size_buff[0]=0x%02X,[1]=0x%02X,[2]=0x%02X,[3]=0x%02X\n" + , flash_tmp_buffer[custom_info_temp*flash_page_len] + , flash_tmp_buffer[custom_info_temp*flash_page_len + 1] + , flash_tmp_buffer[custom_info_temp*flash_page_len + 2] + , flash_tmp_buffer[custom_info_temp*flash_page_len + 3]); + + I("Now total length=%d\n", info_len); + + g_guest_info_data->g_guest_data_len[custom_info_temp] = info_len; + + I("Now custom_info_id [0]=%d,[1]=%d,[2]=%d,[3]=%d\n" + , flash_tmp_buffer[custom_info_temp*flash_page_len + 4] + , flash_tmp_buffer[custom_info_temp*flash_page_len + 5] + , flash_tmp_buffer[custom_info_temp*flash_page_len + 6] + , flash_tmp_buffer[custom_info_temp*flash_page_len + 7]); + + g_guest_info_data->g_guest_data_type[custom_info_temp] = flash_tmp_buffer[custom_info_temp * flash_page_len + 7]; + + /* if(custom_info_temp < 3) { */ + if (info_len > 128) { + I("%s: info_len=%d\n", __func__, info_len); + info_len = 128; + } + for (i = 0; i < info_len; i++) + temp_str[i] = flash_tmp_buffer[custom_info_temp * flash_page_len + HX_GUEST_INFO_LEN_SIZE + HX_GUEST_INFO_ID_SIZE + i]; + I("g_guest_info_data->g_guest_str_in_format[%d] size = %d\n", custom_info_temp, info_len); + memcpy(&g_guest_info_data->g_guest_str_in_format[custom_info_temp][0], temp_str, info_len); + /*}*/ + + for (i = 0; i < 128; i++) + temp_str[i] = flash_tmp_buffer[custom_info_temp * flash_page_len + i]; + + I("g_guest_info_data->g_guest_str[%d] size = %d\n", custom_info_temp, 128); + memcpy(&g_guest_info_data->g_guest_str[custom_info_temp][0], temp_str, 128); + /*if(custom_info_temp == 0) + *{ + * for( i = 0; i< 256 ; i++) + * { + * if(i % 16 == 0 && i > 0) + * I("\n"); + * I("g_guest_info_data->g_guest_str[%d][%d] = 0x%02X",custom_info_temp,i,g_guest_info_data->g_guest_str[custom_info_temp][i]); + * } + *} + */ + } + /* himax_burst_enable(private_ts->client, 0); */ + + g_core_fp.fp_sense_on(0x01); + + kfree(flash_tmp_buffer); + himax_guest_info_set_status(0); + return NO_ERR; +} +#endif +#endif + +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) +static void himax_mcu_resend_cmd_func(bool suspended) +{ +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) + struct himax_ts_data *ts = private_ts; +#endif +#ifdef HX_SMART_WAKEUP + g_core_fp.fp_set_SMWP_enable(ts->SMWP_enable, suspended); +#endif +#ifdef HX_EDGE_LIMIT + g_core_fp.fp_set_edge_limit_enable(ts->edge_limit_enable, suspended); +#endif +#ifdef HX_HIGH_SENSE + g_core_fp.fp_set_HSEN_enable(ts->HSEN_enable, suspended); +#endif +#ifdef HX_USB_DETECT_GLOBAL + himax_cable_detect_func(true); +#endif +} +#endif + +#ifdef HX_ZERO_FLASH +int G_POWERONOF = 1; +EXPORT_SYMBOL(G_POWERONOF); +void hx_dis_rload_0f(int disable) +{ + /*Diable Flash Reload*/ + g_core_fp.fp_register_write(pzf_op->addr_dis_flash_reload, DATA_LEN_4, pzf_op->data_dis_flash_reload, 0); +} + +void himax_mcu_clean_sram_0f(uint8_t *addr, int write_len, int type) +{ + int total_read_times = 0; + int max_bus_size = MAX_I2C_TRANS_SZ; + int total_size_temp = 0; + int total_size = 0; + int address = 0; + int i = 0; + + uint8_t fix_data = 0x00; + uint8_t tmp_addr[4]; + uint8_t tmp_data[MAX_I2C_TRANS_SZ] = {0}; + + I("%s, Entering\n", __func__); + + total_size = write_len; + +#ifdef __MTK_SPI__ + if (write_len > (HX_SPI_MTK_MAX_WRITE_SZ - 4)) + max_bus_size = (HX_SPI_MTK_MAX_WRITE_SZ - 4); + else + max_bus_size = write_len; +#else + if (total_size > PACKET_MAX_SZ - 8) + max_bus_size = PACKET_MAX_SZ - 8; +#endif + + total_size_temp = write_len; + + g_core_fp.fp_burst_enable(1); + + tmp_addr[3] = addr[3]; + tmp_addr[2] = addr[2]; + tmp_addr[1] = addr[1]; + tmp_addr[0] = addr[0]; + I("%s, write addr tmp_addr[3]=0x%2.2X, tmp_addr[2]=0x%2.2X, tmp_addr[1]=0x%2.2X, tmp_addr[0]=0x%2.2X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + + switch (type) { + case 0: + fix_data = 0x00; + break; + case 1: + fix_data = 0xAA; + break; + case 2: + fix_data = 0xBB; + break; + } + + for (i = 0; i < MAX_I2C_TRANS_SZ; i++) + tmp_data[i] = fix_data; + + I("%s, total size=%d\n", __func__, total_size); + + if (total_size_temp % max_bus_size == 0) + total_read_times = total_size_temp / max_bus_size; + else + total_read_times = total_size_temp / max_bus_size + 1; + + for (i = 0; i < (total_read_times); i++) { + I("[log]write %d time start!\n", i); + if (total_size_temp >= max_bus_size) { + g_core_fp.fp_register_write(tmp_addr, max_bus_size, tmp_data, 0); + total_size_temp = total_size_temp - max_bus_size; + } else { + I("last total_size_temp=%d\n", total_size_temp); + g_core_fp.fp_register_write(tmp_addr, total_size_temp % max_bus_size, tmp_data, 0); + } + address = ((i+1) * max_bus_size); + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF); + tmp_addr[0] = addr[0] + (uint8_t) ((address) & 0x00FF); + + usleep_range(10000, 11000); + } + + I("%s, END\n", __func__); +} + +void himax_mcu_write_sram_0f(const struct firmware *fw_entry, uint8_t *addr, int start_index, uint32_t write_len) +{ + int total_read_times = 0; + int max_bus_size = MAX_I2C_TRANS_SZ; + int total_size_temp = 0; + int address = 0; + int i = 0; + + uint8_t tmp_addr[4]; + uint8_t *tmp_data; + + total_size_temp = write_len; + I("%s, Entering - total write size=%d\n", __func__, total_size_temp); + +#if defined(HX_SPI_OPERATION) + if (write_len > PACKET_MAX_SZ - 8) + max_bus_size = PACKET_MAX_SZ - 8; + else + max_bus_size = write_len; +#else +#ifdef __MTK_SPI__ + if (write_len > (HX_SPI_MTK_MAX_WRITE_SZ - 4)) + max_bus_size = (HX_SPI_MTK_MAX_WRITE_SZ - 4); + else + max_bus_size = write_len; +#endif +#endif + + g_core_fp.fp_burst_enable(1); + + tmp_addr[3] = addr[3]; + tmp_addr[2] = addr[2]; + tmp_addr[1] = addr[1]; + tmp_addr[0] = addr[0]; + I("%s, write addr = 0x%02X%02X%02X%02X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + + /* tmp_data = kcalloc(max_bus_size, sizeof(uint8_t), GFP_KERNEL); + * if (tmp_data == NULL) { + * I("%s: Can't allocate enough buf\n", __func__); + * return; + * } + */ + + /*for(i = 0;i<10;i++) + *{ + * I("[%d] 0x%2.2X", i, tmp_data[i]); + *} + *I("\n"); + */ + if (total_size_temp % max_bus_size == 0) + total_read_times = total_size_temp / max_bus_size; + else + total_read_times = total_size_temp / max_bus_size + 1; + + for (i = 0; i < (total_read_times); i++) { + /*I("[log]write %d time start!\n", i);*/ + /*I("[log]addr[3]=0x%02X, addr[2]=0x%02X, addr[1]=0x%02X, addr[0]=0x%02X!\n", tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]);*/ + + if (total_size_temp >= max_bus_size) { + /*memcpy(tmp_data, &fw_entry->data[start_index+i * max_bus_size], max_bus_size);*/ + tmp_data = (uint8_t *)&fw_entry->data[start_index+i * max_bus_size]; + g_core_fp.fp_register_write(tmp_addr, max_bus_size, tmp_data, 0); + total_size_temp = total_size_temp - max_bus_size; + } else { + /*memcpy(tmp_data, &fw_entry->data[start_index+i * max_bus_size], total_size_temp % max_bus_size);*/ + tmp_data = (uint8_t *)&fw_entry->data[start_index+i * max_bus_size]; + I("last total_size_temp=%d\n", total_size_temp % max_bus_size); + g_core_fp.fp_register_write(tmp_addr, total_size_temp % max_bus_size, tmp_data, 0); + } + + /*I("[log]write %d time end!\n", i);*/ + address = ((i+1) * max_bus_size); + tmp_addr[0] = addr[0] + (uint8_t) ((address) & 0x00FF); + + if (tmp_addr[0] < addr[0]) + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF) + 1; + else + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF); + + udelay(100); + } + I("%s, End\n", __func__); + /* kfree(tmp_data); */ +} + +int himax_sram_write_crc_check(const struct firmware *fw_entry, uint8_t *addr, int strt_idx, uint32_t len) +{ + int retry = 0; + int crc = -1; + + do { + g_core_fp.fp_write_sram_0f(fw_entry, addr, strt_idx, len); + crc = g_core_fp.fp_check_CRC(addr, len); + retry++; + I("%s, HW CRC %s in %d time\n", __func__, (crc == 0)?"OK":"Fail", retry); + } while (crc != 0 && retry < 3); + + return crc; +} +#if defined(HX83102D) +int hx_parse_bin_cfg_data(const struct firmware *fw_entry, struct zf_info *zf_info_arr, uint8_t *FW_buf, uint8_t *sram_min) +{ + int part_num = 0; + int i = 0; + uint8_t buf[16]; + int i_max = 0; + int i_min = 0; + uint32_t dsram_base = 0xFFFFFFFF; + uint32_t dsram_max = 0; + int cfg_sz = 0; +#if defined(HX_CODE_OVERLAY) + uint8_t j = 0; + int allovlidx = 0; + + if (ovl_idx == NULL) { + ovl_idx = kzalloc(ovl_section_num, GFP_KERNEL); + if (ovl_idx == NULL) { + E("%s, ovl_idx alloc failed!\n", + __func__); + return -ENOMEM; + } + } else { + memset(ovl_idx, 0, ovl_section_num); + } +#endif + /*1. get number of partition*/ + part_num = fw_entry->data[HX64K + 12]; + I("%s, Number of partition is %d\n", __func__, part_num); + if (part_num <= 1) { + E("%s, size of cfg part failed! part_num = %d\n", + __func__, part_num); + return LENGTH_FAIL; + } + + /* i = 0 is fw entity, it need to update by itself*/ + for (i = 1; i < part_num; i++) { + /*3. get all partition*/ + memcpy(buf, &fw_entry->data[i * 0x10 + HX64K], 16); + memcpy(zf_info_arr[i].sram_addr, buf, 4); + zf_info_arr[i].write_size = buf[7] << 24 + | buf[6] << 16 + | buf[5] << 8 + | buf[4]; + zf_info_arr[i].fw_addr = buf[11] << 24 + | buf[10] << 16 + | buf[9] << 8 + | buf[8]; + zf_info_arr[i].cfg_addr = zf_info_arr[i].sram_addr[0]; + zf_info_arr[i].cfg_addr += zf_info_arr[i].sram_addr[1] << 8; + zf_info_arr[i].cfg_addr += zf_info_arr[i].sram_addr[2] << 16; + zf_info_arr[i].cfg_addr += zf_info_arr[i].sram_addr[3] << 24; + +#if defined(HX_CODE_OVERLAY) + /*overlay section*/ + + if ((buf[15] == 0x55 && buf[14] == 0x66) + || (buf[3] == 0x20 && buf[2] == 0x00 + && buf[1] == 0x8C && buf[0] == 0xE0)) { + I("%s: catch overlay section in index %d\n", + __func__, i); + + /* record index of overlay section */ + allovlidx |= 1< zf_info_arr[i].cfg_addr) { + dsram_base = zf_info_arr[i].cfg_addr; + i_min = i; + } + if (dsram_max < zf_info_arr[i].cfg_addr) { + dsram_max = zf_info_arr[i].cfg_addr; + i_max = i; + } + } + for (i = 0; i < ADDR_LEN_4; i++) + sram_min[i] = zf_info_arr[i_min].sram_addr[i]; + + cfg_sz = (dsram_max - dsram_base) + zf_info_arr[i_max].write_size; + if (cfg_sz % 16 != 0) + cfg_sz = cfg_sz + 16 - (cfg_sz % 16); + + I("%s, cfg_sz = %d!, dsram_base = %X, dsram_max = %X\n", __func__, + cfg_sz, dsram_base, dsram_max); + if (cfg_sz <= FW_BIN_16K_SZ) + memset(FW_buf, 0x00, sizeof(uint8_t) * cfg_sz); + else + memset(FW_buf, 0x00, sizeof(uint8_t) * FW_BIN_16K_SZ); + for (i = 1; i < part_num; i++) { + if (zf_info_arr[i].cfg_addr % 4 != 0) + zf_info_arr[i].cfg_addr = zf_info_arr[i].cfg_addr + - (zf_info_arr[i].cfg_addr % 4); + + I("%s,[%d]SRAM addr=%08X,[%d]fw_addr=%08X,[%d]write_size=%d\n", + __func__, + i, zf_info_arr[i].cfg_addr, + i, zf_info_arr[i].fw_addr, + i, zf_info_arr[i].write_size); + +#if defined(HX_CODE_OVERLAY) + /*overlay section*/ + if (allovlidx & (1<data[zf_info_arr[i].fw_addr], + zf_info_arr[i].write_size); + } + /* debug info */ + /* for (i = 1; i < part_num; i++) { + * different = 0; + * I("--cfg_addr = 0x%08X\n",zf_info_arr[i].cfg_addr); + * I("--bin_addr = 0x%08X\n",zf_info_arr[i].fw_addr); + * I("--write size = %d\n",zf_info_arr[i].write_size); + * for ( j = 0; j < zf_info_arr[i].write_size; j++) { + * if(FW_buf[zf_info_arr[i].cfg_addr - dsram_base + j] != + fw_entry->data[zf_info_arr[i].fw_addr + j]) + * different++; + * } + * I("--There are %d different\n", different); + * } + */ + + return cfg_sz; +} +#else +int hx_parse_bin_cfg_data(const struct firmware *fw_entry, struct zf_info *zf_info_arr, uint8_t *FW_buf, uint8_t *sram_min, uint8_t *ovl_sidx) +{ + int part_num = 0; + int i = 0; + uint8_t buf[16]; + int i_max = 0; + int i_min = 0; + uint32_t dsram_base = 0xFFFFFFFF; + uint32_t dsram_max = 0; + int cfg_sz = 0; + /*int different = 0;*/ + /*int j = 0;*/ + + /*1. get number of partition*/ + part_num = fw_entry->data[HX64K + 12]; + I("%s, Number of partition is %d\n", __func__, part_num); + if (part_num <= 1) { + E("%s, size of cfg part failed! part_num = %d\n", __func__, part_num); + return LENGTH_FAIL; + } + /*2. check struct of array*/ + if (zf_info_arr == NULL || FW_buf == NULL) { + E("%s, Struct is NULL, %p,%p\n", __func__, zf_info_arr, FW_buf); + return MEM_ALLOC_FAIL; + } + + /* i = 0 is fw entity, it need to update by itself*/ + for (i = 1; i < part_num; i++) { + /*3. get all partition*/ + memcpy(buf, &fw_entry->data[i * 0x10 + HX64K], 16); + memcpy(zf_info_arr[i].sram_addr, buf, 4); + zf_info_arr[i].write_size = buf[7] << 24 | buf[6] << 16 | buf[5] << 8 | buf[4]; + zf_info_arr[i].fw_addr = buf[11] << 24 | buf[10] << 16 | buf[9] << 8 | buf[8]; + zf_info_arr[i].cfg_addr = zf_info_arr[i].sram_addr[0]; + zf_info_arr[i].cfg_addr += zf_info_arr[i].sram_addr[1] << 8; + zf_info_arr[i].cfg_addr += zf_info_arr[i].sram_addr[2] << 16; + zf_info_arr[i].cfg_addr += zf_info_arr[i].sram_addr[3] << 24; + +#ifdef HX_CODE_OVERLAY + /*overlay section*/ + if (zf_info_arr[i].cfg_addr == ovl_isram_addr) { + if (*ovl_sidx == 0) + *ovl_sidx = i; + + continue; + } +#endif + + if (dsram_base > zf_info_arr[i].cfg_addr) { + dsram_base = zf_info_arr[i].cfg_addr; + i_min = i; + } + if (dsram_max < zf_info_arr[i].cfg_addr) { + dsram_max = zf_info_arr[i].cfg_addr; + i_max = i; + } + } + for (i = 0; i < ADDR_LEN_4; i++) + sram_min[i] = zf_info_arr[i_min].sram_addr[i]; + cfg_sz = (dsram_max - dsram_base) + zf_info_arr[i_max].write_size; + if (cfg_sz % 16 != 0) + cfg_sz = cfg_sz + 16 - (cfg_sz % 16); + + I("%s, cfg_sz = %d!, dsram_base = %X, dsram_max = %X\n", __func__, cfg_sz, dsram_base, dsram_max); + + memset(FW_buf, 0x00, sizeof(uint8_t) * cfg_sz); + for (i = 1; i < part_num; i++) { + if (zf_info_arr[i].cfg_addr % 4 != 0) + zf_info_arr[i].cfg_addr = zf_info_arr[i].cfg_addr - (zf_info_arr[i].cfg_addr % 4); + + I("%s,[%d] SRAM addr = %08X\n", __func__, i, zf_info_arr[i].cfg_addr); + I("%s,[%d] fw_addr = %08X\n", __func__, i, zf_info_arr[i].fw_addr); + I("%s,[%d] write_size = %d\n", __func__, i, zf_info_arr[i].write_size); + +#ifdef HX_CODE_OVERLAY + /*overlay section*/ + if (zf_info_arr[i].cfg_addr == ovl_isram_addr) + continue; +#endif + + memcpy(&FW_buf[zf_info_arr[i].cfg_addr - dsram_base], &fw_entry->data[zf_info_arr[i].fw_addr], zf_info_arr[i].write_size); + } + /* debug info */ + /* for (i = 1; i < part_num; i++) { + * different = 0; + * I("--cfg_addr = 0x%08X\n",zf_info_arr[i].cfg_addr); + * I("--bin_addr = 0x%08X\n",zf_info_arr[i].fw_addr); + * I("--write size = %d\n",zf_info_arr[i].write_size); + * for ( j = 0; j < zf_info_arr[i].write_size; j++) { + * if(FW_buf[zf_info_arr[i].cfg_addr - dsram_base + j] != fw_entry->data[zf_info_arr[i].fw_addr + j]) + * different++; + * } + * I("--There are %d different\n", different); + * } + */ + + return cfg_sz; +} +#endif + +int himax_zf_part_info(const struct firmware *fw_entry) +{ + int part_num = fw_entry->data[HX64K + 12]; + int ret = 0; + uint8_t buf[16]; + struct zf_info *zf_info_arr = NULL; + uint8_t *FW_buf; + uint8_t sram_min[4]; + int cfg_crc_sw = 0; + int cfg_crc_hw = 0; + int cfg_sz = 0; + int retry = 3; + +#if ((defined HX_CODE_OVERLAY) || !(defined HX83102D)) + uint8_t ovl_sidx = 0; +#endif +#if ((defined HX_CODE_OVERLAY) && (defined HX83102D)) + uint8_t ovl_idx_t; +#endif +#if defined(HX83102D) + +#if defined(HX_SMART_WAKEUP) || defined(HX_CODE_OVERLAY) + uint8_t tmp_addr[4] = {0xFC, 0x7F, 0x00, 0x10}; + uint8_t send_data[4] = {0}; + uint8_t recv_data[4] = {0}; +#endif +#else +#ifdef HX_CODE_OVERLAY +#ifdef HX_SMART_WAKEUP + uint8_t tmp_addr[4] = {0xFC, 0x7F, 0x00, 0x10}; + uint8_t send_data[4] = {0}; + uint8_t recv_data[4] = {0}; +#endif +#endif +#endif + + /*1. get number of partition*/ + /*part_num = fw_entry->data[HX64K + 12];*/ + I("%s, Number of partition is %d\n", __func__, part_num); + if (part_num <= 0) + part_num = 1; + + /*2. initial struct of array*/ + zf_info_arr = kcalloc(part_num, sizeof(struct zf_info), GFP_KERNEL); + if (zf_info_arr == NULL) { + E("%s, Allocate ZF info array failed!\n", __func__); + ret = MEM_ALLOC_FAIL; + goto ALOC_ZF_INFO_ARR_FAIL; + } + memset(zf_info_arr, 0x0, part_num * sizeof(struct zf_info)); + FW_buf = kcalloc(FW_BIN_16K_SZ, sizeof(uint8_t), GFP_KERNEL); + if (FW_buf == NULL) { + E("%s, Allocate FW_buf array failed!\n", __func__); + ret = MEM_ALLOC_FAIL; + goto ALOC_FW_BUF_FAIL; + } + + /*3. Find the data in the bin file */ + /*CFG*/ +#if defined(HX83102D) + cfg_sz = hx_parse_bin_cfg_data(fw_entry, zf_info_arr, FW_buf, sram_min); +#else + cfg_sz = hx_parse_bin_cfg_data(fw_entry, zf_info_arr, FW_buf, sram_min, &ovl_sidx); +#endif + I("Now cfg_sz=%d\n", cfg_sz); + if (cfg_sz < 0) { + ret = LENGTH_FAIL; + goto END_FUNC; + } else { + cfg_crc_sw = g_core_fp.fp_Calculate_CRC_with_AP(FW_buf, 0, cfg_sz); + I("Now cfg_crc_sw=%X\n", cfg_crc_sw); + } + /*Fw Entity*/ + memcpy(buf, &fw_entry->data[0 * 0x10 + HX64K], 16); + memcpy(zf_info_arr[0].sram_addr, buf, 4); + zf_info_arr[0].write_size = buf[7] << 24 | buf[6] << 16 | buf[5] << 8 | buf[4]; + zf_info_arr[0].fw_addr = buf[11] << 24 | buf[10] << 16 | buf[9] << 8 | buf[8]; + + /* 4. write to sram*/ + if (G_POWERONOF == 1) { + /* FW entity */ + if (himax_sram_write_crc_check(fw_entry, zf_info_arr[0].sram_addr, zf_info_arr[0].fw_addr, zf_info_arr[0].write_size) != 0) + E("%s, HW CRC FAIL\n", __func__); + else + I("%s, HW CRC PASS\n", __func__); + + I("Now sram_min[0]=0x%02X,[1]=0x%02X,[2]=0x%02X,[3]=0x%02X\n", sram_min[0], sram_min[1], sram_min[2], sram_min[3]); + do { + g_core_fp.fp_register_write(sram_min, cfg_sz, FW_buf, 0); + cfg_crc_hw = g_core_fp.fp_check_CRC(sram_min, cfg_sz); + if (cfg_crc_hw != cfg_crc_sw) + E("Config CRC FAIL, HW CRC = %X,SW CRC = %X, retry = %d\n", cfg_crc_hw, cfg_crc_sw, retry); + else + I("Config CRC Pass\n"); + + } while (cfg_crc_hw != cfg_crc_sw && retry-- > 0); + +#ifdef HX_CODE_OVERLAY +#if defined(HX83102D) + /* ovl_idx[0] - sorting */ + /* ovl_idx[1] - gesture */ + /* ovl_idx[2] - border */ + + ovl_idx_t = ovl_idx[0]; + send_data[0] = ovl_sorting_reply; + + if (private_ts->in_self_test == 0) { +#if defined(HX_SMART_WAKEUP) + if (private_ts->suspended && private_ts->SMWP_enable) { + ovl_idx_t = ovl_idx[1]; + send_data[0] = ovl_gesture_reply; + } else { + ovl_idx_t = ovl_idx[2]; + send_data[0] = ovl_border_reply; + } +#else + ovl_idx_t = ovl_idx[2]; + send_data[0] = ovl_border_reply; +#endif +#else + /*do nothing when self testing ??*/ + if (private_ts->in_self_test == 0) { +#ifdef HX_SMART_WAKEUP + /* ovl_sidx - mpap */ + /* ovl_sidx+1 - gesture */ + /* ovl_sidx+2 - border */ + if (private_ts->suspended && private_ts->SMWP_enable) + ovl_sidx = ovl_sidx + 1; + else + ovl_sidx = ovl_sidx + 2; +#else + ovl_sidx = ovl_sidx + 2; +#endif +#endif + + if (himax_sram_write_crc_check(fw_entry, zf_info_arr[ovl_sidx].sram_addr, zf_info_arr[ovl_sidx].fw_addr, zf_info_arr[ovl_sidx].write_size) != 0) + E("%s, Overlay HW CRC FAIL\n", __func__); + else + I("%s, Overlay HW CRC PASS\n", __func__); + +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) + /*write back system config*/ + g_core_fp.fp_resend_cmd_func(private_ts->suspended); +#endif + +#if defined(HX83102D) + } + I("%s: prepare upgrade overlay section = %d\n", + __func__, ovl_idx_t); + + if (zf_info_arr[ovl_idx_t].write_size == 0) { + send_data[0] = ovl_fault; + E("%s, WRONG overlay section, plese check FW!\n", + __func__); + } else { + if (himax_sram_write_crc_check(fw_entry, + zf_info_arr[ovl_idx_t].sram_addr, + zf_info_arr[ovl_idx_t].fw_addr, + zf_info_arr[ovl_idx_t].write_size) != 0) { + send_data[0] = ovl_fault; + E("%s, Overlay HW CRC FAIL\n", __func__); + } else { + I("%s, Overlay HW CRC PASS\n", __func__); + } + } + + retry = 0; + + do { + g_core_fp.fp_register_write(tmp_addr, DATA_LEN_4, send_data, 0); + g_core_fp.fp_register_read(tmp_addr, DATA_LEN_4, recv_data, 0); + retry++; + } while ((send_data[3] != recv_data[3] || send_data[2] != recv_data[2] || send_data[1] != recv_data[1] || send_data[0] != recv_data[0]) && retry < HIMAX_REG_RETRY_TIMES); +#else +#if defined(HX_SMART_WAKEUP) + /* write overlay handshaking password */ + if (private_ts->SMWP_enable) { + if (private_ts->suspended) + send_data[0] = ovl_gesture_reply; + else + send_data[0] = ovl_border_reply; + + retry = 0; + + do { + g_core_fp.fp_register_write(tmp_addr, DATA_LEN_4, send_data, 0); + g_core_fp.fp_register_read(tmp_addr, DATA_LEN_4, recv_data, 0); + retry++; + } while ((send_data[3] != recv_data[3] || send_data[2] != recv_data[2] || send_data[1] != recv_data[1] || send_data[0] != recv_data[0]) && retry < HIMAX_REG_RETRY_TIMES); + } +#endif + } +#endif +#endif + } else { + g_core_fp.fp_clean_sram_0f(zf_info_arr[0].sram_addr, zf_info_arr[0].write_size, 2); + } + +END_FUNC: + kfree(FW_buf); +ALOC_FW_BUF_FAIL: + kfree(zf_info_arr); +ALOC_ZF_INFO_ARR_FAIL: + return ret; +} + +void himax_mcu_firmware_update_0f(const struct firmware *fw_entry) +{ + int ret = 0; + uint8_t tmp_data[DATA_LEN_4] = {0x01, 0x00, 0x00, 0x00}; + + I("%s,Entering - total FW size=%d\n", __func__, (int)fw_entry->size); + + g_core_fp.fp_register_write(pzf_op->addr_system_reset, 4, pzf_op->data_system_reset, 0); + + g_core_fp.fp_sense_off(false); + + if ((int)fw_entry->size > HX64K) { + ret = himax_zf_part_info(fw_entry); + } else { + /* first 48K */ + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_sram_start_addr, 0, HX_48K_SZ) != 0) + E("%s, HW CRC FAIL - Main SRAM 48K\n", __func__); + + /*config info*/ + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_cfg_info, 0xC000, 128) != 0) + E("Config info CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_cfg_info, 128, 2); + } + + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_fw_cfg_1, 0xC0FE, 528) != 0) + E("FW config 1 CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_fw_cfg_1, 528, 1); + } + + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_fw_cfg_3, 0xCA00, 128) != 0) + E("FW config 3 CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_fw_cfg_3, 128, 2); + } + + /*ADC config*/ + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_adc_cfg_1, 0xD640, 1200) != 0) + E("ADC config 1 CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_adc_cfg_1, 1200, 2); + } + + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_adc_cfg_2, 0xD320, 800) != 0) + E("ADC config 2 CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_adc_cfg_2, 800, 2); + } + + /*mapping table*/ + if (G_POWERONOF == 1) { + if (himax_sram_write_crc_check(fw_entry, pzf_op->data_map_table, 0xE000, 1536) != 0) + E("Mapping table CRC Fail!\n"); + } else { + g_core_fp.fp_clean_sram_0f(pzf_op->data_map_table, 1536, 2); + } + } + + /* set n frame=0*/ + /*if (G_POWERONOF == 1) + * g_core_fp.fp_write_sram_0f(fw_entry, pzf_op->data_mode_switch, 0xC30C, 4); + *else + * g_core_fp.fp_clean_sram_0f(pzf_op->data_mode_switch, 4, 2); + */ + g_core_fp.fp_register_write(pfw_op->addr_set_frame_addr, 4, tmp_data, 0); + + I("%s, End\n", __func__); +} + +int hx_0f_op_file_dirly(char *file_name) +{ + int err = NO_ERR, ret; + const struct firmware *fw_entry = NULL; + + + I("%s, Entering\n", __func__); + I("file name = %s\n", file_name); + ret = request_firmware(&fw_entry, file_name, private_ts->dev); + if (ret < 0) { +#if defined(__EMBEDDED_FW__) + fw_entry = &g_embedded_fw; + I("%s: Couldn't find userspace FW, use embedded FW(size:%zu) instead.\n", __func__, g_embedded_fw.size); +#else + E("%s, fail in line%d error code=%d,file maybe fail\n", __func__, __LINE__, ret); + return ret; +#endif + } + + himax_int_enable(0); + + if (g_f_0f_updat == 1) { + I("%s:[Warning]Other thread is updating now!\n", __func__); + err = -1; + goto END; + } else { + I("%s:Entering Update Flow!\n", __func__); + g_f_0f_updat = 1; + } + + g_core_fp.fp_firmware_update_0f(fw_entry); + if (ret >= 0) + release_firmware(fw_entry); + + g_f_0f_updat = 0; +END: + I("%s, END\n", __func__); + return err; +} + +int himax_mcu_0f_operation_dirly(void) +{ + int err = NO_ERR, ret; + const struct firmware *fw_entry = NULL; + + I("%s, Entering\n", __func__); + I("file name = %s\n", i_CTPM_firmware_name); + ret = request_firmware(&fw_entry, i_CTPM_firmware_name, private_ts->dev); + if (ret < 0) { +#if defined(__EMBEDDED_FW__) + fw_entry = &g_embedded_fw; + I("%s: Couldn't find userspace FW, use embedded FW(size:%zu) instead.\n", __func__, g_embedded_fw.size); +#else + E("%s, fail in line%d error code=%d,file maybe fail\n", __func__, __LINE__, ret); + return ret; +#endif + } + + himax_int_enable(0); + + if (g_f_0f_updat == 1) { + I("%s:[Warning]Other thread is updating now!\n", __func__); + err = -1; + goto END; + } else { + I("%s:Entering Update Flow!\n", __func__); + g_f_0f_updat = 1; + } + + g_core_fp.fp_firmware_update_0f(fw_entry); + if (ret >= 0) + release_firmware(fw_entry); + + g_f_0f_updat = 0; +END: + I("%s, END\n", __func__); + return err; +} + +void himax_mcu_0f_operation(struct work_struct *work) +{ + int err = NO_ERR; + const struct firmware *fw_entry = NULL; + + I("%s, Entering\n", __func__); + I("file name = %s\n", i_CTPM_firmware_name); + err = request_firmware(&fw_entry, i_CTPM_firmware_name, private_ts->dev); + if (err < 0) { +#if defined(__EMBEDDED_FW__) + fw_entry = &g_embedded_fw; + I("%s: Couldn't find userspace FW, use embedded FW(size:%zu) instead.\n", __func__, g_embedded_fw.size); +#else + E("%s, fail in line%d error code=%d,file maybe fail\n", __func__, __LINE__, err); + goto END; +#endif + } + + if (g_f_0f_updat == 1) { + I("%s:[Warning]Other thread is updating now!\n", __func__); + goto END; + } else { + I("%s:Entering Update Flow!\n", __func__); + g_f_0f_updat = 1; + } + + himax_int_enable(0); + + g_core_fp.fp_firmware_update_0f(fw_entry); + if (err >= 0) + release_firmware(fw_entry); + + g_core_fp.fp_reload_disable(0); + g_core_fp.fp_read_FW_ver(); + /*msleep (10);*/ + g_core_fp.fp_touch_information(); + /*msleep (10);*/ + g_core_fp.fp_sense_on(0x00); + /*msleep (10);*/ + I("%s:End\n", __func__); + himax_int_enable(1); + + g_f_0f_updat = 0; +END: + I("%s, END\n", __func__); +} + +static int himax_mcu_0f_esd_check(void) +{ + return NO_ERR; +} + + +#ifdef HX_0F_DEBUG +void himax_mcu_read_sram_0f(const struct firmware *fw_entry, uint8_t *addr, int start_index, int read_len) +{ + int total_read_times = 0; + int max_bus_size = MAX_I2C_TRANS_SZ; + int total_size_temp = 0; + int total_size = 0; + int address = 0; + int i = 0, j = 0; + int not_same = 0; + + uint8_t tmp_addr[4]; + uint8_t *temp_info_data; + int *not_same_buff; + + I("%s, Entering\n", __func__); + + g_core_fp.fp_burst_enable(1); + + total_size = read_len; + + total_size_temp = read_len; + +#if defined(HX_SPI_OPERATION) + if (read_len > 2048) + max_bus_size = 2048; + else + max_bus_size = read_len; +#else + if (read_len > 240) + max_bus_size = 240; + else + max_bus_size = read_len; +#endif + + + temp_info_data = kcalloc(total_size, sizeof(uint8_t), GFP_KERNEL); + not_same_buff = kcalloc(total_size, sizeof(int), GFP_KERNEL); + + + tmp_addr[3] = addr[3]; + tmp_addr[2] = addr[2]; + tmp_addr[1] = addr[1]; + tmp_addr[0] = addr[0]; + I("%s, read addr tmp_addr[3]=0x%2.2X, tmp_addr[2]=0x%2.2X, tmp_addr[1]=0x%2.2X, tmp_addr[0]=0x%2.2X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + + I("%s, total size=%d\n", __func__, total_size); + + g_core_fp.fp_burst_enable(1); + + if (total_size % max_bus_size == 0) + total_read_times = total_size / max_bus_size; + else + total_read_times = total_size / max_bus_size + 1; + + for (i = 0; i < (total_read_times); i++) { + if (total_size_temp >= max_bus_size) { + g_core_fp.fp_register_read(tmp_addr, max_bus_size, &temp_info_data[i*max_bus_size], false); + total_size_temp = total_size_temp - max_bus_size; + } else { + g_core_fp.fp_register_read(tmp_addr, total_size_temp % max_bus_size, &temp_info_data[i*max_bus_size], false); + } + + address = ((i+1) * max_bus_size); + tmp_addr[0] = addr[0] + (uint8_t) ((address) & 0x00FF); + if (tmp_addr[0] < addr[0]) + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF) + 1; + else + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF); + + /*msleep (10);*/ + } + I("%s, READ Start\n", __func__); + I("%s, start_index = %d\n", __func__, start_index); + j = start_index; + for (i = 0; i < read_len; i++, j++) { + if (fw_entry->data[j] != temp_info_data[i]) { + not_same++; + not_same_buff[i] = 1; + } + + I("0x%2.2X, ", temp_info_data[i]); + + if (i > 0 && i%16 == 15) + pr_info("\n"); + + } + I("%s, READ END\n", __func__); + I("%s, Not Same count=%d\n", __func__, not_same); + if (not_same != 0) { + j = start_index; + for (i = 0; i < read_len; i++, j++) { + if (not_same_buff[i] == 1) + I("bin = [%d] 0x%2.2X\n", i, fw_entry->data[j]); + } + for (i = 0; i < read_len; i++, j++) { + if (not_same_buff[i] == 1) + I("sram = [%d] 0x%2.2X\n", i, temp_info_data[i]); + } + } + I("%s, READ END\n", __func__); + I("%s, Not Same count=%d\n", __func__, not_same); + I("%s, END\n", __func__); + + kfree(not_same_buff); + kfree(temp_info_data); +} + +void himax_mcu_read_all_sram(uint8_t *addr, int read_len) +{ + int total_read_times = 0; + int max_bus_size = MAX_I2C_TRANS_SZ; + int total_size_temp = 0; + int total_size = 0; + int address = 0; + int i = 0; + /* struct file *fn; */ + /* struct filename *vts_name; */ + + uint8_t tmp_addr[4]; + uint8_t *temp_info_data; + + I("%s, Entering\n", __func__); + + g_core_fp.fp_burst_enable(1); + + total_size = read_len; + + total_size_temp = read_len; + + temp_info_data = kcalloc(total_size, sizeof(uint8_t), GFP_KERNEL); + + + tmp_addr[3] = addr[3]; + tmp_addr[2] = addr[2]; + tmp_addr[1] = addr[1]; + tmp_addr[0] = addr[0]; + I("%s, read addr tmp_addr[3]=0x%2.2X, tmp_addr[2]=0x%2.2X, tmp_addr[1]=0x%2.2X, tmp_addr[0]=0x%2.2X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + + I("%s, total size=%d\n", __func__, total_size); + + if (total_size % max_bus_size == 0) + total_read_times = total_size / max_bus_size; + else + total_read_times = total_size / max_bus_size + 1; + + for (i = 0; i < (total_read_times); i++) { + if (total_size_temp >= max_bus_size) { + g_core_fp.fp_register_read(tmp_addr, max_bus_size, &temp_info_data[i*max_bus_size], false); + total_size_temp = total_size_temp - max_bus_size; + } else { + g_core_fp.fp_register_read(tmp_addr, total_size_temp % max_bus_size, &temp_info_data[i*max_bus_size], false); + } + + address = ((i+1) * max_bus_size); + tmp_addr[1] = addr[1] + (uint8_t) ((address>>8) & 0x00FF); + tmp_addr[0] = addr[0] + (uint8_t) ((address) & 0x00FF); + + /*msleep (10);*/ + } + I("%s, NOW addr tmp_addr[3]=0x%2.2X, tmp_addr[2]=0x%2.2X, tmp_addr[1]=0x%2.2X, tmp_addr[0]=0x%2.2X\n", __func__, tmp_addr[3], tmp_addr[2], tmp_addr[1], tmp_addr[0]); + /*for(i = 0;i 0 && i%16 == 15) + * printk("\n"); + *} + */ + + /* need modify + * I("Now Write File start!\n"); + * vts_name = kp_getname_kernel("/sdcard/dump_dsram.txt"); + * fn = kp_file_open_name(vts_name, O_CREAT | O_WRONLY, 0); + * if (!IS_ERR (fn)) { + * I("%s create file and ready to write\n", __func__); + * fn->f_op->write (fn, temp_info_data, read_len*sizeof (uint8_t), &fn->f_pos); + * filp_close (fn, NULL); + * } + * I("Now Write File End!\n"); + */ + + I("%s, END\n", __func__); + + kfree(temp_info_data); +} + +void himax_mcu_firmware_read_0f(const struct firmware *fw_entry, int type) +{ + uint8_t tmp_addr[4]; + + I("%s, Entering\n", __func__); + if (type == 0) { /* first 48K */ + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_sram_start_addr, 0, HX_48K_SZ); + g_core_fp.fp_read_all_sram(tmp_addr, 0xC000); + } else { /*last 16k*/ + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_cfg_info, 0xC000, 132); + + /*FW config*/ + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_fw_cfg_1, 0xC0FE, 484); + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_fw_cfg_2, 0xC9DE, 36); + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_fw_cfg_3, 0xCA00, 72); + + /*ADC config*/ + + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_adc_cfg_1, 0xD630, 1188); + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_adc_cfg_2, 0xD318, 792); + + + /*mapping table*/ + g_core_fp.fp_read_sram_0f(fw_entry, pzf_op->data_map_table, 0xE000, 1536); + + /* set n frame=0*/ + g_core_fp.fp_read_sram_0f(fw_entry, pfw_op->addr_set_frame_addr, 0xC30C, 4); + } + + I("%s, END\n", __func__); +} + +void himax_mcu_0f_operation_check(int type) +{ + int err = NO_ERR; + const struct firmware *fw_entry = NULL; + /* char *firmware_name = "himax.bin"; */ + + + I("%s, Entering\n", __func__); + I("file name = %s\n", i_CTPM_firmware_name); + + + err = request_firmware(&fw_entry, i_CTPM_firmware_name, private_ts->dev); + if (err < 0) { +#if defined(__EMBEDDED_FW__) + fw_entry = &g_embedded_fw; + I("%s: Couldn't find userspace FW, use embedded FW(size:%zu) instead.\n", __func__, g_embedded_fw.size); +#else + E("%s, fail in line%d error code=%d\n", __func__, __LINE__, err); + return; +#endif + } + + I("first 4 bytes 0x%2X, 0x%2X, 0x%2X, 0x%2X !\n", fw_entry->data[0], fw_entry->data[1], fw_entry->data[2], fw_entry->data[3]); + I("next 4 bytes 0x%2X, 0x%2X, 0x%2X, 0x%2X !\n", fw_entry->data[4], fw_entry->data[5], fw_entry->data[6], fw_entry->data[7]); + I("and next 4 bytes 0x%2X, 0x%2X, 0x%2X, 0x%2X !\n", fw_entry->data[8], fw_entry->data[9], fw_entry->data[10], fw_entry->data[11]); + + g_core_fp.fp_firmware_read_0f(fw_entry, type); + + if (err >= 0) + release_firmware(fw_entry); + I("%s, END\n", __func__); +} +#endif + +#ifdef HX_CODE_OVERLAY +int himax_mcu_0f_overlay(int ovl_type, int mode) +{ + return NO_ERR; +} +#endif + +#endif + + +#ifdef HIMAX_PALM_SENSOR_EN +int himax_mcu_palm_detection(int mode) +{ + uint8_t data_byte[DATA_LEN_4]; + uint8_t tmp_addr[4]; + I("%s: set palm detection mode to %d", __func__, mode); + if (mode) + himax_in_parse_assign_cmd(palm_detection_register_enable, data_byte, 4); + else + himax_in_parse_assign_cmd(palm_detection_register_disable, data_byte, 4); + + g_core_fp.fp_register_write(pfw_op->addr_palm_detection_enable, DATA_LEN_4, data_byte, 0); + g_core_fp.fp_register_read(pfw_op->addr_palm_detection_enable, DATA_LEN_4, tmp_addr, 0); + I("%s: reg=0x%02X%02X%02X%02X.", __func__, tmp_addr[0],tmp_addr[1],tmp_addr[2],tmp_addr[3]); + + return 0; +} +#endif + +#ifdef CORE_INIT +/* init start */ +static void himax_mcu_fp_init(void) +{ +#ifdef CORE_IC + g_core_fp.fp_burst_enable = himax_mcu_burst_enable; + g_core_fp.fp_register_read = himax_mcu_register_read; + /*g_core_fp.fp_flash_write_burst = himax_mcu_flash_write_burst;*/ + /*g_core_fp.fp_flash_write_burst_lenth = himax_mcu_flash_write_burst_lenth;*/ + g_core_fp.fp_register_write = himax_mcu_register_write; + g_core_fp.fp_interface_on = himax_mcu_interface_on; + g_core_fp.fp_sense_on = himax_mcu_sense_on; + g_core_fp.fp_sense_off = himax_mcu_sense_off; + g_core_fp.fp_wait_wip = himax_mcu_wait_wip; + g_core_fp.fp_init_psl = himax_mcu_init_psl; + g_core_fp.fp_resume_ic_action = himax_mcu_resume_ic_action; + g_core_fp.fp_suspend_ic_action = himax_mcu_suspend_ic_action; + g_core_fp.fp_power_on_init = himax_mcu_power_on_init; + g_core_fp.fp_dd_clk_set = himax_mcu_dd_clk_set; + g_core_fp.fp_dd_reg_en = himax_mcu_dd_reg_en; + g_core_fp.fp_ic_id_read = himax_mcu_ic_id_read; + g_core_fp.fp_dd_reg_write = himax_mcu_dd_reg_write; + g_core_fp.fp_dd_reg_read = himax_mcu_dd_reg_read; +#endif +#ifdef CORE_FW + g_core_fp.fp_system_reset = himax_mcu_system_reset; + g_core_fp.fp_Calculate_CRC_with_AP = himax_mcu_Calculate_CRC_with_AP; + g_core_fp.fp_check_CRC = himax_mcu_check_CRC; + g_core_fp.fp_set_reload_cmd = himax_mcu_set_reload_cmd; + g_core_fp.fp_program_reload = himax_mcu_program_reload; +#ifdef HX_P_SENSOR + g_core_fp.fp_ulpm_in = himax_mcu_ulpm_in; + g_core_fp.fp_black_gest_ctrl = himax_mcu_black_gest_ctrl; +#endif + g_core_fp.fp_set_SMWP_enable = himax_mcu_set_SMWP_enable; +#ifdef HX_EDGE_LIMIT + g_core_fp.fp_set_edge_limit_enable = himax_mcu_set_edge_limit_enable; +#endif + g_core_fp.fp_set_HSEN_enable = himax_mcu_set_HSEN_enable; + g_core_fp.fp_usb_detect_set = himax_mcu_usb_detect_set; + g_core_fp.fp_diag_register_set = himax_mcu_diag_register_set; + g_core_fp.fp_chip_self_test = himax_mcu_chip_self_test; + g_core_fp.fp_idle_mode = himax_mcu_idle_mode; + g_core_fp.fp_reload_disable = himax_mcu_reload_disable; + g_core_fp.fp_check_chip_version = himax_mcu_check_chip_version; + g_core_fp.fp_read_ic_trigger_type = himax_mcu_read_ic_trigger_type; + g_core_fp.fp_read_i2c_status = himax_mcu_read_i2c_status; + g_core_fp.fp_read_FW_ver = himax_mcu_read_FW_ver; + g_core_fp.fp_read_event_stack = himax_mcu_read_event_stack; + g_core_fp.fp_return_event_stack = himax_mcu_return_event_stack; + g_core_fp.fp_calculateChecksum = himax_mcu_calculateChecksum; + g_core_fp.fp_read_FW_status = himax_mcu_read_FW_status; + g_core_fp.fp_irq_switch = himax_mcu_irq_switch; + g_core_fp.fp_assign_sorting_mode = himax_mcu_assign_sorting_mode; + g_core_fp.fp_check_sorting_mode = himax_mcu_check_sorting_mode; + g_core_fp.fp_switch_mode = himax_mcu_switch_mode; + g_core_fp.fp_get_max_dc = himax_mcu_get_max_dc; + g_core_fp.fp_read_DD_status = himax_mcu_read_DD_status; +#if defined(HX_AUTO_UPDATE_FW) + g_core_fp._diff_overlay_bin = hx_mcu_diff_overlay_bin; +#endif +#endif +#ifdef CORE_FLASH + g_core_fp.fp_chip_erase = himax_mcu_chip_erase; + g_core_fp.fp_block_erase = himax_mcu_block_erase; + g_core_fp.fp_sector_erase = himax_mcu_sector_erase; + g_core_fp.fp_flash_programming = himax_mcu_flash_programming; + g_core_fp.fp_flash_page_write = himax_mcu_flash_page_write; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_32k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_32k; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_60k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_60k; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_64k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_64k; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_124k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_124k; + g_core_fp.fp_fts_ctpm_fw_upgrade_with_sys_fs_128k = himax_mcu_fts_ctpm_fw_upgrade_with_sys_fs_128k; + g_core_fp.fp_flash_dump_func = himax_mcu_flash_dump_func; + g_core_fp.fp_flash_lastdata_check = himax_mcu_flash_lastdata_check; + g_core_fp._diff_overlay_flash = hx_mcu_diff_overlay_flash; +#endif +#ifdef CORE_SRAM + g_core_fp.fp_sram_write = himax_mcu_sram_write; + g_core_fp.fp_sram_verify = himax_mcu_sram_verify; + g_core_fp.fp_get_DSRAM_data = himax_mcu_get_DSRAM_data; +#endif +#ifdef CORE_DRIVER + g_core_fp.fp_chip_init = himax_mcu_init_ic; +#ifdef HX_AUTO_UPDATE_FW + g_core_fp.fp_fw_ver_bin = himax_mcu_fw_ver_bin; +#endif +#ifdef HX_RST_PIN_FUNC + g_core_fp.fp_pin_reset = himax_mcu_pin_reset; + g_core_fp.fp_ic_reset = himax_mcu_ic_reset; +#endif + g_core_fp.fp_touch_information = himax_mcu_touch_information; + g_core_fp.fp_reload_config = himax_mcu_reload_config; + g_core_fp.fp_get_touch_data_size = himax_mcu_get_touch_data_size; + g_core_fp.fp_hand_shaking = himax_mcu_hand_shaking; + g_core_fp.fp_determin_diag_rawdata = himax_mcu_determin_diag_rawdata; + g_core_fp.fp_determin_diag_storage = himax_mcu_determin_diag_storage; + g_core_fp.fp_cal_data_len = himax_mcu_cal_data_len; + g_core_fp.fp_diag_check_sum = himax_mcu_diag_check_sum; + g_core_fp.fp_diag_parse_raw_data = himax_mcu_diag_parse_raw_data; +#ifdef HX_ESD_RECOVERY + g_core_fp.fp_ic_esd_recovery = himax_mcu_ic_esd_recovery; + g_core_fp.fp_esd_ic_reset = himax_mcu_esd_ic_reset; +#endif +#if defined(HX_SMART_WAKEUP) || defined(HX_HIGH_SENSE) || defined(HX_USB_DETECT_GLOBAL) + g_core_fp.fp_resend_cmd_func = himax_mcu_resend_cmd_func; +#endif +#if defined(HX_TP_PROC_GUEST_INFO) + g_core_fp.guest_info_get_status = himax_guest_info_get_status; + g_core_fp.read_guest_info = hx_read_guest_info; +#endif +#endif +#ifdef HX_ZERO_FLASH + g_core_fp.fp_reload_disable = hx_dis_rload_0f; + g_core_fp.fp_clean_sram_0f = himax_mcu_clean_sram_0f; + g_core_fp.fp_write_sram_0f = himax_mcu_write_sram_0f; + g_core_fp.fp_write_sram_0f_crc = himax_sram_write_crc_check; + g_core_fp.fp_firmware_update_0f = himax_mcu_firmware_update_0f; + g_core_fp.fp_0f_operation = himax_mcu_0f_operation; + g_core_fp.fp_0f_operation_dirly = himax_mcu_0f_operation_dirly; + g_core_fp.fp_0f_op_file_dirly = hx_0f_op_file_dirly; + g_core_fp.fp_0f_esd_check = himax_mcu_0f_esd_check; +#ifdef HX_0F_DEBUG + g_core_fp.fp_read_sram_0f = himax_mcu_read_sram_0f; + g_core_fp.fp_read_all_sram = himax_mcu_read_all_sram; + g_core_fp.fp_firmware_read_0f = himax_mcu_firmware_read_0f; + g_core_fp.fp_0f_operation_check = himax_mcu_0f_operation_check; +#endif +#ifdef HX_CODE_OVERLAY + g_core_fp.fp_0f_overlay = himax_mcu_0f_overlay; +#endif +#ifdef HIMAX_PALM_SENSOR_EN + g_core_fp.fp_palm_detection_function = himax_mcu_palm_detection; +#endif +#endif +} + +int himax_mcu_in_cmd_struct_init(void) +{ + int err = 0; + + I("%s: Entering!\n", __func__); + himax_mcu_cmd_struct_free = himax_mcu_in_cmd_struct_free; + + g_core_cmd_op = kzalloc(sizeof(struct himax_core_command_operation), GFP_KERNEL); + if (g_core_cmd_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_fail; + } + + g_core_cmd_op->ic_op = kzalloc(sizeof(struct ic_operation), GFP_KERNEL); + if (g_core_cmd_op->ic_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_ic_op_fail; + } + + g_core_cmd_op->fw_op = kzalloc(sizeof(struct fw_operation), GFP_KERNEL); + if (g_core_cmd_op->fw_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_fw_op_fail; + } + + g_core_cmd_op->flash_op = kzalloc(sizeof(struct flash_operation), GFP_KERNEL); + if (g_core_cmd_op->flash_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_flash_op_fail; + } + + g_core_cmd_op->sram_op = kzalloc(sizeof(struct sram_operation), GFP_KERNEL); + if (g_core_cmd_op->sram_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_sram_op_fail; + } + + g_core_cmd_op->driver_op = kzalloc(sizeof(struct driver_operation), GFP_KERNEL); + if (g_core_cmd_op->driver_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_driver_op_fail; + } + + pic_op = g_core_cmd_op->ic_op; + pfw_op = g_core_cmd_op->fw_op; + pflash_op = g_core_cmd_op->flash_op; + psram_op = g_core_cmd_op->sram_op; + pdriver_op = g_core_cmd_op->driver_op; +#ifdef HX_ZERO_FLASH + g_core_cmd_op->zf_op = kzalloc(sizeof(struct zf_operation), GFP_KERNEL); + if (g_core_cmd_op->zf_op == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_zf_op_fail; + } + pzf_op = g_core_cmd_op->zf_op; + +#endif +#ifndef __MTK_SPI__ + g_internal_buffer = kzalloc(sizeof(uint8_t)*(1024 * 48 + ADDR_LEN_4), GFP_KERNEL); +#else + g_internal_buffer = kzalloc(sizeof(uint8_t)*(HX_SPI_MTK_MAX_WRITE_SZ), GFP_KERNEL); +#endif + if (g_internal_buffer == NULL) { + err = -ENOMEM; + goto err_g_core_cmd_op_g_internal_buffer_fail; + } + himax_mcu_fp_init(); + + return NO_ERR; + +err_g_core_cmd_op_g_internal_buffer_fail: +#ifdef HX_ZERO_FLASH + kfree(g_core_cmd_op->zf_op); + g_core_cmd_op->zf_op = NULL; +err_g_core_cmd_op_zf_op_fail: +#endif + kfree(g_core_cmd_op->driver_op); + g_core_cmd_op->driver_op = NULL; +err_g_core_cmd_op_driver_op_fail: + kfree(g_core_cmd_op->sram_op); + g_core_cmd_op->sram_op = NULL; +err_g_core_cmd_op_sram_op_fail: + kfree(g_core_cmd_op->flash_op); + g_core_cmd_op->flash_op = NULL; +err_g_core_cmd_op_flash_op_fail: + kfree(g_core_cmd_op->fw_op); + g_core_cmd_op->fw_op = NULL; +err_g_core_cmd_op_fw_op_fail: + kfree(g_core_cmd_op->ic_op); + g_core_cmd_op->ic_op = NULL; +err_g_core_cmd_op_ic_op_fail: + kfree(g_core_cmd_op); + g_core_cmd_op = NULL; +err_g_core_cmd_op_fail: + + return err; +} +EXPORT_SYMBOL(himax_mcu_in_cmd_struct_init); + +void himax_mcu_in_cmd_struct_free(void) +{ + pic_op = NULL; + pfw_op = NULL; + pflash_op = NULL; + psram_op = NULL; + pdriver_op = NULL; + kfree(g_internal_buffer); + g_internal_buffer = NULL; +#ifdef HX_ZERO_FLASH + kfree(g_core_cmd_op->zf_op); + g_core_cmd_op->zf_op = NULL; +#endif + kfree(g_core_cmd_op->driver_op); + g_core_cmd_op->driver_op = NULL; + kfree(g_core_cmd_op->sram_op); + g_core_cmd_op->sram_op = NULL; + kfree(g_core_cmd_op->flash_op); + g_core_cmd_op->flash_op = NULL; + kfree(g_core_cmd_op->fw_op); + g_core_cmd_op->fw_op = NULL; + kfree(g_core_cmd_op->ic_op); + g_core_cmd_op->ic_op = NULL; + kfree(g_core_cmd_op); + g_core_cmd_op = NULL; +#if defined(HX_EN_DYNAMIC_NAME) + if (i_CTPM_firmware_name != NULL) { + kfree(i_CTPM_firmware_name); + i_CTPM_firmware_name = NULL; + } +#endif + + I("%s: release completed\n", __func__); +} + +void himax_in_parse_assign_cmd(uint32_t addr, uint8_t *cmd, int len) +{ + /*I("%s: Entering!\n", __func__);*/ + switch (len) { + case 1: + cmd[0] = addr; + /*I("%s: cmd[0] = 0x%02X\n", __func__, cmd[0]);*/ + break; + + case 2: + cmd[0] = addr % 0x100; + cmd[1] = (addr >> 8) % 0x100; + /*I("%s: cmd[0] = 0x%02X,cmd[1] = 0x%02X\n", __func__, cmd[0], cmd[1]);*/ + break; + + case 4: + cmd[0] = addr % 0x100; + cmd[1] = (addr >> 8) % 0x100; + cmd[2] = (addr >> 16) % 0x100; + cmd[3] = addr / 0x1000000; + /* I("%s: cmd[0] = 0x%02X,cmd[1] = 0x%02X,cmd[2] = 0x%02X,cmd[3] = 0x%02X\n", */ + /* __func__, cmd[0], cmd[1], cmd[2], cmd[3]);*/ + break; + + default: + E("%s: input length fault,len = %d!\n", __func__, len); + } +} +EXPORT_SYMBOL(himax_in_parse_assign_cmd); + +void himax_mcu_in_cmd_init(void) +{ + I("%s: Entering!\n", __func__); + +#ifdef HIMAX_PALM_SENSOR_EN + himax_in_parse_assign_cmd(palm_detection_register, pfw_op->addr_palm_detection_enable, sizeof(pfw_op->addr_palm_detection_enable)); +#endif +#ifdef CORE_IC + himax_in_parse_assign_cmd(ic_adr_ahb_addr_byte_0, pic_op->addr_ahb_addr_byte_0, sizeof(pic_op->addr_ahb_addr_byte_0)); + himax_in_parse_assign_cmd(ic_adr_ahb_rdata_byte_0, pic_op->addr_ahb_rdata_byte_0, sizeof(pic_op->addr_ahb_rdata_byte_0)); + himax_in_parse_assign_cmd(ic_adr_ahb_access_direction, pic_op->addr_ahb_access_direction, sizeof(pic_op->addr_ahb_access_direction)); + himax_in_parse_assign_cmd(ic_adr_conti, pic_op->addr_conti, sizeof(pic_op->addr_conti)); + himax_in_parse_assign_cmd(ic_adr_incr4, pic_op->addr_incr4, sizeof(pic_op->addr_incr4)); + himax_in_parse_assign_cmd(ic_adr_i2c_psw_lb, pic_op->adr_i2c_psw_lb, sizeof(pic_op->adr_i2c_psw_lb)); + himax_in_parse_assign_cmd(ic_adr_i2c_psw_ub, pic_op->adr_i2c_psw_ub, sizeof(pic_op->adr_i2c_psw_ub)); + himax_in_parse_assign_cmd(ic_cmd_ahb_access_direction_read, pic_op->data_ahb_access_direction_read, sizeof(pic_op->data_ahb_access_direction_read)); + himax_in_parse_assign_cmd(ic_cmd_conti, pic_op->data_conti, sizeof(pic_op->data_conti)); + himax_in_parse_assign_cmd(ic_cmd_incr4, pic_op->data_incr4, sizeof(pic_op->data_incr4)); + himax_in_parse_assign_cmd(ic_cmd_i2c_psw_lb, pic_op->data_i2c_psw_lb, sizeof(pic_op->data_i2c_psw_lb)); + himax_in_parse_assign_cmd(ic_cmd_i2c_psw_ub, pic_op->data_i2c_psw_ub, sizeof(pic_op->data_i2c_psw_ub)); + himax_in_parse_assign_cmd(ic_adr_tcon_on_rst, pic_op->addr_tcon_on_rst, sizeof(pic_op->addr_tcon_on_rst)); + himax_in_parse_assign_cmd(ic_addr_adc_on_rst, pic_op->addr_adc_on_rst, sizeof(pic_op->addr_adc_on_rst)); + himax_in_parse_assign_cmd(ic_adr_psl, pic_op->addr_psl, sizeof(pic_op->addr_psl)); + himax_in_parse_assign_cmd(ic_adr_cs_central_state, pic_op->addr_cs_central_state, sizeof(pic_op->addr_cs_central_state)); + himax_in_parse_assign_cmd(ic_cmd_rst, pic_op->data_rst, sizeof(pic_op->data_rst)); + himax_in_parse_assign_cmd(ic_adr_osc_en, pic_op->adr_osc_en, sizeof(pic_op->adr_osc_en)); + himax_in_parse_assign_cmd(ic_adr_osc_pw, pic_op->adr_osc_pw, sizeof(pic_op->adr_osc_pw)); +#endif +#ifdef CORE_FW + himax_in_parse_assign_cmd(fw_addr_system_reset, pfw_op->addr_system_reset, sizeof(pfw_op->addr_system_reset)); + himax_in_parse_assign_cmd(fw_addr_safe_mode_release_pw, pfw_op->addr_safe_mode_release_pw, sizeof(pfw_op->addr_safe_mode_release_pw)); + himax_in_parse_assign_cmd(fw_addr_ctrl_fw, pfw_op->addr_ctrl_fw_isr, sizeof(pfw_op->addr_ctrl_fw_isr)); + himax_in_parse_assign_cmd(fw_addr_flag_reset_event, pfw_op->addr_flag_reset_event, sizeof(pfw_op->addr_flag_reset_event)); + himax_in_parse_assign_cmd(fw_addr_hsen_enable, pfw_op->addr_hsen_enable, sizeof(pfw_op->addr_hsen_enable)); + himax_in_parse_assign_cmd(fw_addr_smwp_enable, pfw_op->addr_smwp_enable, sizeof(pfw_op->addr_smwp_enable)); + himax_in_parse_assign_cmd(fw_addr_program_reload_from, pfw_op->addr_program_reload_from, sizeof(pfw_op->addr_program_reload_from)); + himax_in_parse_assign_cmd(fw_addr_program_reload_to, pfw_op->addr_program_reload_to, sizeof(pfw_op->addr_program_reload_to)); + himax_in_parse_assign_cmd(fw_addr_program_reload_page_write, pfw_op->addr_program_reload_page_write, sizeof(pfw_op->addr_program_reload_page_write)); + himax_in_parse_assign_cmd(fw_addr_raw_out_sel, pfw_op->addr_raw_out_sel, sizeof(pfw_op->addr_raw_out_sel)); + himax_in_parse_assign_cmd(fw_addr_reload_status, pfw_op->addr_reload_status, sizeof(pfw_op->addr_reload_status)); + himax_in_parse_assign_cmd(fw_addr_reload_crc32_result, pfw_op->addr_reload_crc32_result, sizeof(pfw_op->addr_reload_crc32_result)); + himax_in_parse_assign_cmd(fw_addr_reload_addr_from, pfw_op->addr_reload_addr_from, sizeof(pfw_op->addr_reload_addr_from)); + himax_in_parse_assign_cmd(fw_addr_reload_addr_cmd_beat, pfw_op->addr_reload_addr_cmd_beat, sizeof(pfw_op->addr_reload_addr_cmd_beat)); + himax_in_parse_assign_cmd(fw_addr_selftest_addr_en, pfw_op->addr_selftest_addr_en, sizeof(pfw_op->addr_selftest_addr_en)); + himax_in_parse_assign_cmd(fw_addr_criteria_addr, pfw_op->addr_criteria_addr, sizeof(pfw_op->addr_criteria_addr)); + himax_in_parse_assign_cmd(fw_addr_set_frame_addr, pfw_op->addr_set_frame_addr, sizeof(pfw_op->addr_set_frame_addr)); + himax_in_parse_assign_cmd(fw_addr_selftest_result_addr, pfw_op->addr_selftest_result_addr, sizeof(pfw_op->addr_selftest_result_addr)); + himax_in_parse_assign_cmd(fw_addr_sorting_mode_en, pfw_op->addr_sorting_mode_en, sizeof(pfw_op->addr_sorting_mode_en)); + himax_in_parse_assign_cmd(fw_addr_max_dc, pfw_op->addr_max_dc, sizeof(pfw_op->addr_max_dc)); + himax_in_parse_assign_cmd(fw_addr_fw_mode_status, pfw_op->addr_fw_mode_status, sizeof(pfw_op->addr_fw_mode_status)); + himax_in_parse_assign_cmd(fw_addr_icid_addr, pfw_op->addr_icid_addr, sizeof(pfw_op->addr_icid_addr)); + himax_in_parse_assign_cmd(fw_addr_trigger_addr, pfw_op->addr_trigger_addr, sizeof(pfw_op->addr_trigger_addr)); + himax_in_parse_assign_cmd(fw_addr_fw_ver_addr, pfw_op->addr_fw_ver_addr, sizeof(pfw_op->addr_fw_ver_addr)); + himax_in_parse_assign_cmd(fw_addr_fw_cfg_addr, pfw_op->addr_fw_cfg_addr, sizeof(pfw_op->addr_fw_cfg_addr)); + himax_in_parse_assign_cmd(fw_addr_fw_vendor_addr, pfw_op->addr_fw_vendor_addr, sizeof(pfw_op->addr_fw_vendor_addr)); + himax_in_parse_assign_cmd(fw_addr_cus_info, pfw_op->addr_cus_info, sizeof(pfw_op->addr_cus_info)); + himax_in_parse_assign_cmd(fw_addr_proj_info, pfw_op->addr_proj_info, sizeof(pfw_op->addr_proj_info)); + himax_in_parse_assign_cmd(fw_addr_fw_state_addr, pfw_op->addr_fw_state_addr, sizeof(pfw_op->addr_fw_state_addr)); + himax_in_parse_assign_cmd(fw_addr_fw_dbg_msg_addr, pfw_op->addr_fw_dbg_msg_addr, sizeof(pfw_op->addr_fw_dbg_msg_addr)); + himax_in_parse_assign_cmd(fw_addr_chk_fw_status, pfw_op->addr_chk_fw_status, sizeof(pfw_op->addr_chk_fw_status)); + himax_in_parse_assign_cmd(fw_addr_dd_handshak_addr, pfw_op->addr_dd_handshak_addr, sizeof(pfw_op->addr_dd_handshak_addr)); + himax_in_parse_assign_cmd(fw_addr_dd_data_addr, pfw_op->addr_dd_data_addr, sizeof(pfw_op->addr_dd_data_addr)); + himax_in_parse_assign_cmd(fw_data_system_reset, pfw_op->data_system_reset, sizeof(pfw_op->data_system_reset)); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_active, pfw_op->data_safe_mode_release_pw_active, sizeof(pfw_op->data_safe_mode_release_pw_active)); + himax_in_parse_assign_cmd(fw_data_clear, pfw_op->data_clear, sizeof(pfw_op->data_clear)); + himax_in_parse_assign_cmd(fw_data_clear, pfw_op->data_clear, sizeof(pfw_op->data_clear)); + himax_in_parse_assign_cmd(fw_data_fw_stop, pfw_op->data_fw_stop, sizeof(pfw_op->data_fw_stop)); + himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, pfw_op->data_safe_mode_release_pw_reset, sizeof(pfw_op->data_safe_mode_release_pw_reset)); + himax_in_parse_assign_cmd(fw_data_program_reload_start, pfw_op->data_program_reload_start, sizeof(pfw_op->data_program_reload_start)); + himax_in_parse_assign_cmd(fw_data_program_reload_compare, pfw_op->data_program_reload_compare, sizeof(pfw_op->data_program_reload_compare)); + himax_in_parse_assign_cmd(fw_data_program_reload_break, pfw_op->data_program_reload_break, sizeof(pfw_op->data_program_reload_break)); + himax_in_parse_assign_cmd(fw_data_selftest_request, pfw_op->data_selftest_request, sizeof(pfw_op->data_selftest_request)); + himax_in_parse_assign_cmd(fw_data_criteria_aa_top, pfw_op->data_criteria_aa_top, sizeof(pfw_op->data_criteria_aa_top)); + himax_in_parse_assign_cmd(fw_data_criteria_aa_bot, pfw_op->data_criteria_aa_bot, sizeof(pfw_op->data_criteria_aa_bot)); + himax_in_parse_assign_cmd(fw_data_criteria_key_top, pfw_op->data_criteria_key_top, sizeof(pfw_op->data_criteria_key_top)); + himax_in_parse_assign_cmd(fw_data_criteria_key_bot, pfw_op->data_criteria_key_bot, sizeof(pfw_op->data_criteria_key_bot)); + himax_in_parse_assign_cmd(fw_data_criteria_avg_top, pfw_op->data_criteria_avg_top, sizeof(pfw_op->data_criteria_avg_top)); + himax_in_parse_assign_cmd(fw_data_criteria_avg_bot, pfw_op->data_criteria_avg_bot, sizeof(pfw_op->data_criteria_avg_bot)); + himax_in_parse_assign_cmd(fw_data_set_frame, pfw_op->data_set_frame, sizeof(pfw_op->data_set_frame)); + himax_in_parse_assign_cmd(fw_data_selftest_ack_hb, pfw_op->data_selftest_ack_hb, sizeof(pfw_op->data_selftest_ack_hb)); + himax_in_parse_assign_cmd(fw_data_selftest_ack_lb, pfw_op->data_selftest_ack_lb, sizeof(pfw_op->data_selftest_ack_lb)); + himax_in_parse_assign_cmd(fw_data_selftest_pass, pfw_op->data_selftest_pass, sizeof(pfw_op->data_selftest_pass)); + himax_in_parse_assign_cmd(fw_data_normal_cmd, pfw_op->data_normal_cmd, sizeof(pfw_op->data_normal_cmd)); + himax_in_parse_assign_cmd(fw_data_normal_status, pfw_op->data_normal_status, sizeof(pfw_op->data_normal_status)); + himax_in_parse_assign_cmd(fw_data_sorting_cmd, pfw_op->data_sorting_cmd, sizeof(pfw_op->data_sorting_cmd)); + himax_in_parse_assign_cmd(fw_data_sorting_status, pfw_op->data_sorting_status, sizeof(pfw_op->data_sorting_status)); + himax_in_parse_assign_cmd(fw_data_dd_request, pfw_op->data_dd_request, sizeof(pfw_op->data_dd_request)); + himax_in_parse_assign_cmd(fw_data_dd_ack, pfw_op->data_dd_ack, sizeof(pfw_op->data_dd_ack)); + himax_in_parse_assign_cmd(fw_data_idle_dis_pwd, pfw_op->data_idle_dis_pwd, sizeof(pfw_op->data_idle_dis_pwd)); + himax_in_parse_assign_cmd(fw_data_idle_en_pwd, pfw_op->data_idle_en_pwd, sizeof(pfw_op->data_idle_en_pwd)); + himax_in_parse_assign_cmd(fw_data_rawdata_ready_hb, pfw_op->data_rawdata_ready_hb, sizeof(pfw_op->data_rawdata_ready_hb)); + himax_in_parse_assign_cmd(fw_data_rawdata_ready_lb, pfw_op->data_rawdata_ready_lb, sizeof(pfw_op->data_rawdata_ready_lb)); + himax_in_parse_assign_cmd(fw_addr_ahb_addr, pfw_op->addr_ahb_addr, sizeof(pfw_op->addr_ahb_addr)); + himax_in_parse_assign_cmd(fw_data_ahb_dis, pfw_op->data_ahb_dis, sizeof(pfw_op->data_ahb_dis)); + himax_in_parse_assign_cmd(fw_data_ahb_en, pfw_op->data_ahb_en, sizeof(pfw_op->data_ahb_en)); + himax_in_parse_assign_cmd(fw_addr_event_addr, pfw_op->addr_event_addr, sizeof(pfw_op->addr_event_addr)); + himax_in_parse_assign_cmd(fw_usb_detect_addr, pfw_op->addr_usb_detect, sizeof(pfw_op->addr_usb_detect)); +#ifdef HX_P_SENSOR + himax_in_parse_assign_cmd(fw_addr_ulpm_33, pfw_op->addr_ulpm_33, sizeof(pfw_op->addr_ulpm_33)); + himax_in_parse_assign_cmd(fw_addr_ulpm_34, pfw_op->addr_ulpm_34, sizeof(pfw_op->addr_ulpm_34)); + himax_in_parse_assign_cmd(fw_data_ulpm_11, pfw_op->data_ulpm_11, sizeof(pfw_op->data_ulpm_11)); + himax_in_parse_assign_cmd(fw_data_ulpm_22, pfw_op->data_ulpm_22, sizeof(pfw_op->data_ulpm_22)); + himax_in_parse_assign_cmd(fw_data_ulpm_33, pfw_op->data_ulpm_33, sizeof(pfw_op->data_ulpm_33)); + himax_in_parse_assign_cmd(fw_data_ulpm_aa, pfw_op->data_ulpm_aa, sizeof(pfw_op->data_ulpm_aa)); +#endif +#endif +#ifdef CORE_FLASH + himax_in_parse_assign_cmd(flash_addr_spi200_trans_fmt, pflash_op->addr_spi200_trans_fmt, sizeof(pflash_op->addr_spi200_trans_fmt)); + himax_in_parse_assign_cmd(flash_addr_spi200_trans_ctrl, pflash_op->addr_spi200_trans_ctrl, sizeof(pflash_op->addr_spi200_trans_ctrl)); + himax_in_parse_assign_cmd(flash_addr_spi200_cmd, pflash_op->addr_spi200_cmd, sizeof(pflash_op->addr_spi200_cmd)); + himax_in_parse_assign_cmd(flash_addr_spi200_addr, pflash_op->addr_spi200_addr, sizeof(pflash_op->addr_spi200_addr)); + himax_in_parse_assign_cmd(flash_addr_spi200_data, pflash_op->addr_spi200_data, sizeof(pflash_op->addr_spi200_data)); + himax_in_parse_assign_cmd(flash_addr_spi200_bt_num, pflash_op->addr_spi200_bt_num, sizeof(pflash_op->addr_spi200_bt_num)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_fmt, pflash_op->data_spi200_trans_fmt, sizeof(pflash_op->data_spi200_trans_fmt)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_1, pflash_op->data_spi200_trans_ctrl_1, sizeof(pflash_op->data_spi200_trans_ctrl_1)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_2, pflash_op->data_spi200_trans_ctrl_2, sizeof(pflash_op->data_spi200_trans_ctrl_2)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_3, pflash_op->data_spi200_trans_ctrl_3, sizeof(pflash_op->data_spi200_trans_ctrl_3)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_4, pflash_op->data_spi200_trans_ctrl_4, sizeof(pflash_op->data_spi200_trans_ctrl_4)); + himax_in_parse_assign_cmd(flash_data_spi200_trans_ctrl_5, pflash_op->data_spi200_trans_ctrl_5, sizeof(pflash_op->data_spi200_trans_ctrl_5)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_1, pflash_op->data_spi200_cmd_1, sizeof(pflash_op->data_spi200_cmd_1)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_2, pflash_op->data_spi200_cmd_2, sizeof(pflash_op->data_spi200_cmd_2)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_3, pflash_op->data_spi200_cmd_3, sizeof(pflash_op->data_spi200_cmd_3)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_4, pflash_op->data_spi200_cmd_4, sizeof(pflash_op->data_spi200_cmd_4)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_5, pflash_op->data_spi200_cmd_5, sizeof(pflash_op->data_spi200_cmd_5)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_6, pflash_op->data_spi200_cmd_6, sizeof(pflash_op->data_spi200_cmd_6)); + himax_in_parse_assign_cmd(flash_data_spi200_cmd_7, pflash_op->data_spi200_cmd_7, sizeof(pflash_op->data_spi200_cmd_7)); + himax_in_parse_assign_cmd(flash_data_spi200_addr, pflash_op->data_spi200_addr, sizeof(pflash_op->data_spi200_addr)); +#endif +#ifdef CORE_SRAM + /* sram start*/ + himax_in_parse_assign_cmd(sram_adr_mkey, psram_op->addr_mkey, sizeof(psram_op->addr_mkey)); + himax_in_parse_assign_cmd(sram_adr_rawdata_addr, psram_op->addr_rawdata_addr, sizeof(psram_op->addr_rawdata_addr)); + himax_in_parse_assign_cmd(sram_adr_rawdata_end, psram_op->addr_rawdata_end, sizeof(psram_op->addr_rawdata_end)); + himax_in_parse_assign_cmd(sram_passwrd_start, psram_op->passwrd_start, sizeof(psram_op->passwrd_start)); + himax_in_parse_assign_cmd(sram_passwrd_end, psram_op->passwrd_end, sizeof(psram_op->passwrd_end)); + /* sram end*/ +#endif +#ifdef CORE_DRIVER + himax_in_parse_assign_cmd(driver_addr_fw_define_flash_reload, pdriver_op->addr_fw_define_flash_reload, sizeof(pdriver_op->addr_fw_define_flash_reload)); + himax_in_parse_assign_cmd(driver_addr_fw_define_2nd_flash_reload, pdriver_op->addr_fw_define_2nd_flash_reload, sizeof(pdriver_op->addr_fw_define_2nd_flash_reload)); + himax_in_parse_assign_cmd(driver_addr_fw_define_int_is_edge, pdriver_op->addr_fw_define_int_is_edge, sizeof(pdriver_op->addr_fw_define_int_is_edge)); + himax_in_parse_assign_cmd(driver_addr_fw_define_rxnum_txnum_maxpt, pdriver_op->addr_fw_define_rxnum_txnum_maxpt, sizeof(pdriver_op->addr_fw_define_rxnum_txnum_maxpt)); + himax_in_parse_assign_cmd(driver_addr_fw_define_xy_res_enable, pdriver_op->addr_fw_define_xy_res_enable, sizeof(pdriver_op->addr_fw_define_xy_res_enable)); + himax_in_parse_assign_cmd(driver_addr_fw_define_x_y_res, pdriver_op->addr_fw_define_x_y_res, sizeof(pdriver_op->addr_fw_define_x_y_res)); + himax_in_parse_assign_cmd(driver_data_df_rx, pdriver_op->data_df_rx, sizeof(pdriver_op->data_df_rx)); + himax_in_parse_assign_cmd(driver_data_df_tx, pdriver_op->data_df_tx, sizeof(pdriver_op->data_df_tx)); + himax_in_parse_assign_cmd(driver_data_df_pt, pdriver_op->data_df_pt, sizeof(pdriver_op->data_df_pt)); + himax_in_parse_assign_cmd(driver_data_df_x_res, pdriver_op->data_df_x_res, sizeof(pdriver_op->data_df_x_res)); + himax_in_parse_assign_cmd(driver_data_df_y_res, pdriver_op->data_df_y_res, sizeof(pdriver_op->data_df_y_res)); + himax_in_parse_assign_cmd(driver_data_fw_define_flash_reload_dis, pdriver_op->data_fw_define_flash_reload_dis, sizeof(pdriver_op->data_fw_define_flash_reload_dis)); + himax_in_parse_assign_cmd(driver_data_fw_define_flash_reload_en, pdriver_op->data_fw_define_flash_reload_en, sizeof(pdriver_op->data_fw_define_flash_reload_en)); + himax_in_parse_assign_cmd(driver_data_fw_define_rxnum_txnum_maxpt_sorting, pdriver_op->data_fw_define_rxnum_txnum_maxpt_sorting, sizeof(pdriver_op->data_fw_define_rxnum_txnum_maxpt_sorting)); + himax_in_parse_assign_cmd(driver_data_fw_define_rxnum_txnum_maxpt_normal, pdriver_op->data_fw_define_rxnum_txnum_maxpt_normal, sizeof(pdriver_op->data_fw_define_rxnum_txnum_maxpt_normal)); +#endif +#ifdef HX_ZERO_FLASH + himax_in_parse_assign_cmd(zf_addr_dis_flash_reload, pzf_op->addr_dis_flash_reload, sizeof(pzf_op->addr_dis_flash_reload)); + himax_in_parse_assign_cmd(zf_data_dis_flash_reload, pzf_op->data_dis_flash_reload, sizeof(pzf_op->data_dis_flash_reload)); + himax_in_parse_assign_cmd(zf_addr_system_reset, pzf_op->addr_system_reset, sizeof(pzf_op->addr_system_reset)); + himax_in_parse_assign_cmd(zf_data_system_reset, pzf_op->data_system_reset, sizeof(pzf_op->data_system_reset)); + himax_in_parse_assign_cmd(zf_data_sram_start_addr, pzf_op->data_sram_start_addr, sizeof(pzf_op->data_sram_start_addr)); + himax_in_parse_assign_cmd(zf_data_sram_clean, pzf_op->data_sram_clean, sizeof(pzf_op->data_sram_clean)); + himax_in_parse_assign_cmd(zf_data_cfg_info, pzf_op->data_cfg_info, sizeof(pzf_op->data_cfg_info)); + himax_in_parse_assign_cmd(zf_data_fw_cfg_1, pzf_op->data_fw_cfg_1, sizeof(pzf_op->data_fw_cfg_1)); + himax_in_parse_assign_cmd(zf_data_fw_cfg_2, pzf_op->data_fw_cfg_2, sizeof(pzf_op->data_fw_cfg_2)); + himax_in_parse_assign_cmd(zf_data_fw_cfg_3, pzf_op->data_fw_cfg_3, sizeof(pzf_op->data_fw_cfg_3)); + himax_in_parse_assign_cmd(zf_data_adc_cfg_1, pzf_op->data_adc_cfg_1, sizeof(pzf_op->data_adc_cfg_1)); + himax_in_parse_assign_cmd(zf_data_adc_cfg_2, pzf_op->data_adc_cfg_2, sizeof(pzf_op->data_adc_cfg_2)); + himax_in_parse_assign_cmd(zf_data_adc_cfg_3, pzf_op->data_adc_cfg_3, sizeof(pzf_op->data_adc_cfg_3)); + himax_in_parse_assign_cmd(zf_data_map_table, pzf_op->data_map_table, sizeof(pzf_op->data_map_table)); + /*himax_in_parse_assign_cmd(zf_data_mode_switch, pzf_op->data_mode_switch, sizeof(pzf_op->data_mode_switch));*/ + himax_in_parse_assign_cmd(zf_addr_sts_chk, pzf_op->addr_sts_chk, sizeof(pzf_op->addr_sts_chk)); + himax_in_parse_assign_cmd(zf_data_activ_sts, pzf_op->data_activ_sts, sizeof(pzf_op->data_activ_sts)); + himax_in_parse_assign_cmd(zf_addr_activ_relod, pzf_op->addr_activ_relod, sizeof(pzf_op->addr_activ_relod)); + himax_in_parse_assign_cmd(zf_data_activ_in, pzf_op->data_activ_in, sizeof(pzf_op->data_activ_in)); +#endif +} +EXPORT_SYMBOL(himax_mcu_in_cmd_init); + +/* init end*/ +#endif diff --git a/drivers/input/touchscreen/himax_v3_mmi/himax_inspection.c b/drivers/input/touchscreen/himax_v3_mmi/himax_inspection.c new file mode 100755 index 000000000000..9de797519843 --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/himax_inspection.c @@ -0,0 +1,2239 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for inspection functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_inspection.h" + +static int g_gap_vertical_partial = 3; +static int *g_gap_vertical_part; +static int g_gap_horizontal_partial = 3; +static int *g_gap_horizontal_part; + +static int g_dc_max; + +static int g_1kind_raw_size; +uint32_t g_rslt_data_len; +int **g_inspection_criteria; +int *g_inspt_crtra_flag; +int HX_CRITERIA_ITEM; +int HX_CRITERIA_SIZE; +char *g_rslt_data; +static char g_file_path[256]; +static char g_rslt_log[256]; +static char g_start_log[256]; +char *hx_self_test_file_name = NULL; + +char *g_himax_inspection_mode[] = { + "HIMAX_INSPECTION_OPEN", + "HIMAX_INSPECTION_MICRO_OPEN", + "HIMAX_INSPECTION_SHORT", + "HIMAX_INSPECTION_RAWDATA", + "HIMAX_INSPECTION_BPN_RAWDATA", + "HIMAX_INSPECTION_WEIGHT_NOISE", + "HIMAX_INSPECTION_ABS_NOISE", + "HIMAX_INSPECTION_SORTING", + "HIMAX_INSPECTION_BACK_NORMAL", + + "HIMAX_INSPECTION_GAPTEST_RAW", + + "HIMAX_INSPECTION_ACT_IDLE_RAWDATA", + "HIMAX_INSPECTION_ACT_IDLE_NOISE", + + "HIMAX_INSPECTION_LPWUG_RAWDATA", + "HIMAX_INSPECTION_LPWUG_NOISE", + "HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA", + "HIMAX_INSPECTION_LPWUG_IDLE_NOISE", + NULL +}; + +/* for criteria */ +char *g_hx_inspt_crtra_name[] = { + "CRITERIA_RAW_MIN", + "CRITERIA_RAW_MAX", + "CRITERIA_RAW_BPN_MIN", + "CRITERIA_RAW_BPN_MAX", + "CRITERIA_SHORT_MIN", + "CRITERIA_SHORT_MAX", + "CRITERIA_OPEN_MIN", + "CRITERIA_OPEN_MAX", + "CRITERIA_MICRO_OPEN_MIN", + "CRITERIA_MICRO_OPEN_MAX", + "CRITERIA_NOISE_WT_MIN", + "CRITERIA_NOISE_WT_MAX", + "CRITERIA_NOISE_ABS_MIN", + "CRITERIA_NOISE_ABS_MAX", + "CRITERIA_SORT_MIN", + "CRITERIA_SORT_MAX", + + "CRITERIA_GAP_RAW_HOR_MIN", + "CRITERIA_GAP_RAW_HOR_MAX", + "CRITERIA_GAP_RAW_VER_MIN", + "CRITERIA_GAP_RAW_VER_MAX", + + "ACT_IDLE_NOISE_MIN", + "ACT_IDLE_NOISE_MAX", + "ACT_IDLE_RAWDATA_MIN", + "ACT_IDLE_RAWDATA_MAX", + + "LPWUG_NOISE_MIN", + "LPWUG_NOISE_MAX", + "LPWUG_RAWDATA_MIN", + "LPWUG_RAWDATA_MAX", + "LPWUG_IDLE_NOISE_MIN", + "LPWUG_IDLE_NOISE_MAX", + "LPWUG_IDLE_RAWDATA_MIN", + "LPWUG_IDLE_RAWDATA_MAX", + NULL +}; + + +void (*fp_himax_self_test_init)(void) = himax_inspection_init; + + +static void himax_press_powerkey(void) +{ + I(" %s POWER KEY event %x press\n", __func__, KEY_POWER); + input_report_key(private_ts->input_dev, KEY_POWER, 1); + input_sync(private_ts->input_dev); + + msleep(100); + + I(" %s POWER KEY event %x release\n", __func__, KEY_POWER); + input_report_key(private_ts->input_dev, KEY_POWER, 0); + input_sync(private_ts->input_dev); +} + + +static uint8_t NOISEMAX; +static uint8_t g_recal_thx; + +static int hx_test_data_pop_out(char *rslt_buf, char *filepath) +{ + struct file *raw_file = NULL; + struct filename *vts_name = NULL; + mm_segment_t fs; + loff_t pos = 0; + int ret_val = NO_ERR; + + I("%s: Entering!\n", __func__); + I("data size=0x%04X\n", (uint32_t)strlen(rslt_buf)); + + if (!kp_getname_kernel) { + E("kp_getname_kernel is NULL, not open file!\n"); + return -EIO; + } + vts_name = kp_getname_kernel(filepath); + + if (raw_file == NULL) + raw_file = kp_file_open_name(vts_name, O_TRUNC|O_CREAT|O_RDWR, + 0660); + + if (IS_ERR(raw_file)) { + E("%s open file failed = %ld\n", __func__, PTR_ERR(raw_file)); + ret_val = -EIO; + goto SAVE_DATA_ERR; + } + + fs = get_fs(); + set_fs(get_ds()); + vfs_write(raw_file, rslt_buf, + g_1kind_raw_size * HX_CRITERIA_ITEM * sizeof(char), &pos); + if (raw_file != NULL) + filp_close(raw_file, NULL); + if (!kp_putname_kernel) { + E("kp_putname_kernel is NULL, not open file!\n"); + } else + kp_putname_kernel(vts_name); + + set_fs(fs); + +SAVE_DATA_ERR: + I("%s: End!\n", __func__); + return ret_val; +} + +static int hx_test_data_get(uint32_t RAW[], char *start_log, char *result, + int now_item) +{ + uint32_t i; + + ssize_t len = 0; + char *testdata = NULL; + uint32_t SZ_SIZE = g_1kind_raw_size; + + I("%s: Entering, Now type=%s!\n", __func__, + g_himax_inspection_mode[now_item]); + + testdata = kzalloc(sizeof(char) * SZ_SIZE, GFP_KERNEL); + if (testdata == NULL) { + E("%s: Memory allocation falied!\n", __func__); + return MEM_ALLOC_FAIL; + } + + len += snprintf((testdata + len), SZ_SIZE - len, "%s", start_log); + for (i = 0; i < ic_data->HX_TX_NUM*ic_data->HX_RX_NUM; i++) { + if (i > 1 && ((i + 1) % ic_data->HX_RX_NUM) == 0) + len += snprintf((testdata + len), SZ_SIZE - len, + "%5d,\n", RAW[i]); + else + len += snprintf((testdata + len), SZ_SIZE - len, + "%5d,", RAW[i]); + } + len += snprintf((testdata + len), SZ_SIZE - len, "\n%s", result); + + memcpy(&g_rslt_data[g_rslt_data_len], testdata, len); + g_rslt_data_len += len; + I("%s: g_rslt_data_len=%d!\n", __func__, g_rslt_data_len); + + /*memcpy(&g_rslt_data[now_item * SZ_SIZE], testdata, SZ_SIZE);*/ + + /* dbg */ + /* for(i = 0; i < SZ_SIZE; i++) + * { + * I("0x%04X, ", g_rslt_data[i + (now_item * SZ_SIZE)]); + * if(i > 0 && (i % 16 == 15)) + * PI("\n"); + * } + */ + + kfree(testdata); + I("%s: End!\n", __func__); + return NO_ERR; + +} + +static int himax_switch_mode_inspection(int mode) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + + I("%s: Entering\n", __func__); + + /*Stop Handshaking*/ + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; + tmp_addr[0] = 0x00; tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = 0x00; tmp_data[0] = 0x00; + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); + + /*Swtich Mode*/ + switch (mode) { + case HIMAX_INSPECTION_SORTING: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_SORTING_START; + tmp_data[0] = PWD_SORTING_START; + break; + case HIMAX_INSPECTION_OPEN: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_OPEN_START; + tmp_data[0] = PWD_OPEN_START; + break; + case HIMAX_INSPECTION_MICRO_OPEN: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_OPEN_START; + tmp_data[0] = PWD_OPEN_START; + break; + case HIMAX_INSPECTION_SHORT: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_SHORT_START; + tmp_data[0] = PWD_SHORT_START; + break; + + case HIMAX_INSPECTION_GAPTEST_RAW: + case HIMAX_INSPECTION_RAWDATA: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_RAWDATA_START; + tmp_data[0] = PWD_RAWDATA_START; + break; + case HIMAX_INSPECTION_BPN_RAWDATA: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_RAWDATA_START; + tmp_data[0] = PWD_RAWDATA_START; + break; + case HIMAX_INSPECTION_WEIGHT_NOISE: + case HIMAX_INSPECTION_ABS_NOISE: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_NOISE_START; + tmp_data[0] = PWD_NOISE_START; + break; + + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_ACT_IDLE_START; + tmp_data[0] = PWD_ACT_IDLE_START; + break; + + case HIMAX_INSPECTION_LPWUG_RAWDATA: + case HIMAX_INSPECTION_LPWUG_NOISE: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_LPWUG_START; + tmp_data[0] = PWD_LPWUG_START; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = PWD_LPWUG_IDLE_START; + tmp_data[0] = PWD_LPWUG_IDLE_START; + break; + + default: + I("%s,Nothing to be done!\n", __func__); + break; + } + + if (g_core_fp.fp_assign_sorting_mode != NULL) + g_core_fp.fp_assign_sorting_mode(tmp_data); + I("%s: End of setting!\n", __func__); + + return 0; + +} + +static int himax_get_rawdata(uint32_t RAW[], uint32_t datalen) +{ + uint8_t *tmp_rawdata; + bool get_raw_rlst; + uint8_t retry = 0; + uint32_t i = 0; + uint32_t j = 0; + uint32_t index = 0; + uint32_t Min_DATA = 0xFFFFFFFF; + uint32_t Max_DATA = 0x00000000; + + /* We use two bytes to combine a value of rawdata.*/ + tmp_rawdata = kzalloc(sizeof(uint8_t) * (datalen * 2), GFP_KERNEL); + if (tmp_rawdata == NULL) { + E("%s: Memory allocation falied!\n", __func__); + return MEM_ALLOC_FAIL; + } + + while (retry < 200) { + get_raw_rlst = g_core_fp.fp_get_DSRAM_data(tmp_rawdata, false); + if (get_raw_rlst) + break; + retry++; + } + + if (retry >= 200) + goto DIRECT_END; + + /* Copy Data*/ + for (i = 0; i < ic_data->HX_TX_NUM*ic_data->HX_RX_NUM; i++) + RAW[i] = tmp_rawdata[(i * 2) + 1] * 256 + tmp_rawdata[(i * 2)]; + /* don't output these logs, reduce factory test time + for (j = 0; j < ic_data->HX_RX_NUM; j++) { + if (j == 0) + PI(" RX%2d", j + 1); + else + PI(" RX%2d", j + 1); + } + PI("\n"); + ******* delete these logs *********************/ + + for (i = 0; i < ic_data->HX_TX_NUM; i++) { + /*PI("TX%2d", i + 1);*/ + for (j = 0; j < ic_data->HX_RX_NUM; j++) { + PI("%5d ", RAW[index]); + if (RAW[index] > Max_DATA) + Max_DATA = RAW[index]; + + if (RAW[index] < Min_DATA) + Min_DATA = RAW[index]; + + index++; + } + /*PI("\n");*/ + } + I("Max = %5d, Min = %5d\n", Max_DATA, Min_DATA); +DIRECT_END: + kfree(tmp_rawdata); + + if (get_raw_rlst) + return HX_INSPECT_OK; + else + return HX_INSPECT_EGETRAW; + +} + +static void himax_switch_data_type(uint8_t checktype) +{ + uint8_t datatype = 0x00; + + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + datatype = DATA_SORTING; + break; + case HIMAX_INSPECTION_OPEN: + datatype = DATA_OPEN; + break; + case HIMAX_INSPECTION_MICRO_OPEN: + datatype = DATA_MICRO_OPEN; + break; + case HIMAX_INSPECTION_SHORT: + datatype = DATA_SHORT; + break; + case HIMAX_INSPECTION_RAWDATA: + datatype = DATA_RAWDATA; + break; + case HIMAX_INSPECTION_BPN_RAWDATA: + datatype = DATA_RAWDATA; + break; + case HIMAX_INSPECTION_WEIGHT_NOISE: + case HIMAX_INSPECTION_ABS_NOISE: + datatype = DATA_NOISE; + break; + case HIMAX_INSPECTION_BACK_NORMAL: + datatype = DATA_BACK_NORMAL; + break; + + case HIMAX_INSPECTION_GAPTEST_RAW: + datatype = DATA_RAWDATA; + break; + + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + datatype = DATA_ACT_IDLE_RAWDATA; + break; + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + datatype = DATA_ACT_IDLE_NOISE; + break; + + case HIMAX_INSPECTION_LPWUG_RAWDATA: + datatype = DATA_LPWUG_RAWDATA; + break; + case HIMAX_INSPECTION_LPWUG_NOISE: + datatype = DATA_LPWUG_NOISE; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + datatype = DATA_LPWUG_IDLE_RAWDATA; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + datatype = DATA_LPWUG_IDLE_NOISE; + break; + + default: + E("Wrong type=%d\n", checktype); + break; + } + g_core_fp.fp_diag_register_set(datatype, 0x00); +} + +static void himax_bank_search_set(uint16_t Nframe, uint8_t checktype) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + + /*skip frame*/ + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x70; tmp_addr[0] = 0xF4; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + + switch (checktype) { + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + tmp_data[0] = BS_ACT_IDLE; + break; + case HIMAX_INSPECTION_LPWUG_RAWDATA: + case HIMAX_INSPECTION_LPWUG_NOISE: + tmp_data[0] = BS_LPWUG; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + tmp_data[0] = BS_LPWUG_dile; + break; + case HIMAX_INSPECTION_RAWDATA: + case HIMAX_INSPECTION_BPN_RAWDATA: + tmp_data[0] = BS_RAWDATA; + break; + case HIMAX_INSPECTION_WEIGHT_NOISE: + case HIMAX_INSPECTION_ABS_NOISE: + tmp_data[0] = BS_NOISE; + break; + default: + tmp_data[0] = BS_OPENSHORT; + break; + } + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); +} + +static void himax_neg_noise_sup(uint8_t *data) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + + /*0x10007FD8 Check support negative value or not */ + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x7F; tmp_addr[0] = 0xD8; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + + if ((tmp_data[3] & 0x04) == 0x04) { + data[2] = 0x0C; data[3] = 0x7F; + } else + I("%s Not support negative noise\n", __func__); +} + +static void himax_set_N_frame(uint16_t Nframe, uint8_t checktype) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + + himax_bank_search_set(Nframe, checktype); + + /*IIR MAX*/ + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; tmp_addr[1] = 0x72; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; + tmp_data[1] = (uint8_t)((Nframe & 0xFF00) >> 8); + tmp_data[0] = (uint8_t)(Nframe & 0x00FF); + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); + + if (checktype == HIMAX_INSPECTION_WEIGHT_NOISE || + checktype == HIMAX_INSPECTION_ABS_NOISE) + himax_neg_noise_sup(tmp_data); + + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); +} + +static void himax_get_noise_base(void)/*Normal Threshold*/ +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x70; tmp_addr[0] = 0x8C;/*0x1000708F*/ + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + + NOISEMAX = tmp_data[3]; + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x70; tmp_addr[0] = 0x90;/*0x10007092*/ + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + + g_recal_thx = tmp_data[2]; + I("%s: NOISEMAX=%d, g_recal_thx = %d\n", __func__, + NOISEMAX, g_recal_thx); +} + +static uint16_t himax_get_palm_num(void)/*Palm Number*/ +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + uint16_t palm_num; + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x70; tmp_addr[0] = 0xA8;/*0x100070AB*/ + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + palm_num = tmp_data[3]; + I("%s: palm_num = %d ", __func__, palm_num); + + return palm_num; +} + +static int himax_get_noise_weight_test(void) +{ + uint8_t tmp_addr[4]; + uint8_t tmp_data[4]; + uint16_t weight = 0; + uint16_t value = 0; + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x72; tmp_addr[0] = 0xC8;/*0x100072C8 weighting value*/ + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + if (tmp_data[3] != 0x72 || tmp_data[2] != 0xC8) + return FW_NOT_READY; + + value = (tmp_data[1] << 8) | tmp_data[0]; + I("%s: value = %d, %d, %d ", __func__, value, tmp_data[2], tmp_data[3]); + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x70; tmp_addr[0] = 0x9C;/*0x1000709C weighting threshold*/ + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + weight = tmp_data[0]; + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x70; tmp_addr[0] = 0x94;/*0x10007095 weighting threshold*/ + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + weight = tmp_data[1] * weight; + I("%s: weight = %d ", __func__, weight); + + if (value > weight) + return ERR_TEST_FAIL; + else + return 0; +} + +static uint32_t himax_check_mode(uint8_t checktype) +{ + uint8_t tmp_data[4] = {0}; + uint8_t wait_pwd[2] = {0}; + + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + wait_pwd[0] = PWD_SORTING_END; + wait_pwd[1] = PWD_SORTING_END; + break; + case HIMAX_INSPECTION_OPEN: + wait_pwd[0] = PWD_OPEN_END; + wait_pwd[1] = PWD_OPEN_END; + break; + case HIMAX_INSPECTION_MICRO_OPEN: + wait_pwd[0] = PWD_OPEN_END; + wait_pwd[1] = PWD_OPEN_END; + break; + case HIMAX_INSPECTION_SHORT: + wait_pwd[0] = PWD_SHORT_END; + wait_pwd[1] = PWD_SHORT_END; + break; + case HIMAX_INSPECTION_RAWDATA: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + case HIMAX_INSPECTION_BPN_RAWDATA: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + case HIMAX_INSPECTION_WEIGHT_NOISE: + case HIMAX_INSPECTION_ABS_NOISE: + wait_pwd[0] = PWD_NOISE_END; + wait_pwd[1] = PWD_NOISE_END; + break; + + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + wait_pwd[0] = PWD_ACT_IDLE_END; + wait_pwd[1] = PWD_ACT_IDLE_END; + break; + + case HIMAX_INSPECTION_LPWUG_RAWDATA: + case HIMAX_INSPECTION_LPWUG_NOISE: + wait_pwd[0] = PWD_LPWUG_END; + wait_pwd[1] = PWD_LPWUG_END; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + wait_pwd[0] = PWD_LPWUG_IDLE_END; + wait_pwd[1] = PWD_LPWUG_IDLE_END; + break; + + default: + E("Wrong type=%d\n", checktype); + break; + } + + if (g_core_fp.fp_check_sorting_mode != NULL) + g_core_fp.fp_check_sorting_mode(tmp_data); + + if ((wait_pwd[0] == tmp_data[0]) && (wait_pwd[1] == tmp_data[1])) { + I("Change to mode=%s\n", g_himax_inspection_mode[checktype]); + return 0; + } else { + return 1; + } +} + +#define TEMP_LOG "%s: %s, tmp_data[0]=%x,tmp_data[1]=%x,tmp_data[2]=%x,tmp_data[3]=%x\n" + +static uint32_t himax_wait_sorting_mode(uint8_t checktype) +{ + uint8_t tmp_addr[4] = {0}; + uint8_t tmp_data[4] = {0}; + uint8_t wait_pwd[2] = {0}; + int count = 0; + + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + wait_pwd[0] = PWD_SORTING_END; + wait_pwd[1] = PWD_SORTING_END; + break; + case HIMAX_INSPECTION_OPEN: + wait_pwd[0] = PWD_OPEN_END; + wait_pwd[1] = PWD_OPEN_END; + break; + case HIMAX_INSPECTION_MICRO_OPEN: + wait_pwd[0] = PWD_OPEN_END; + wait_pwd[1] = PWD_OPEN_END; + break; + case HIMAX_INSPECTION_SHORT: + wait_pwd[0] = PWD_SHORT_END; + wait_pwd[1] = PWD_SHORT_END; + break; + case HIMAX_INSPECTION_RAWDATA: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + case HIMAX_INSPECTION_BPN_RAWDATA: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + case HIMAX_INSPECTION_WEIGHT_NOISE: + case HIMAX_INSPECTION_ABS_NOISE: + wait_pwd[0] = PWD_NOISE_END; + wait_pwd[1] = PWD_NOISE_END; + break; + + case HIMAX_INSPECTION_GAPTEST_RAW: + wait_pwd[0] = PWD_RAWDATA_END; + wait_pwd[1] = PWD_RAWDATA_END; + break; + + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + wait_pwd[0] = PWD_ACT_IDLE_END; + wait_pwd[1] = PWD_ACT_IDLE_END; + break; + + case HIMAX_INSPECTION_LPWUG_RAWDATA: + case HIMAX_INSPECTION_LPWUG_NOISE: + wait_pwd[0] = PWD_LPWUG_END; + wait_pwd[1] = PWD_LPWUG_END; + break; + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + wait_pwd[0] = PWD_LPWUG_IDLE_END; + wait_pwd[1] = PWD_LPWUG_IDLE_END; + break; + + default: + I("No Change Mode and now type=%d\n", checktype); + break; + } + + do { + if (g_core_fp.fp_check_sorting_mode != NULL) + g_core_fp.fp_check_sorting_mode(tmp_data); + if ((wait_pwd[0] == tmp_data[0]) && + (wait_pwd[1] == tmp_data[1])) + return 0; + + + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + I(TEMP_LOG, __func__, "0x900000A8", + tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x00; tmp_addr[0] = 0xE4; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + I(TEMP_LOG, __func__, "0x900000E4", + tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; + tmp_addr[1] = 0x7F; tmp_addr[0] = 0x40; + g_core_fp.fp_register_read(tmp_addr, 4, tmp_data, false); + I(TEMP_LOG, __func__, "0x10007F40", + tmp_data[0], tmp_data[1], tmp_data[2], tmp_data[3]); + I("Now retry %d times!\n", count++); + msleep(50); + } while (count < 50); + + return 1; +} + +static int hx_turn_on_mp_func(int on) +{ + int rslt = 0; + int retry = 3; + uint8_t tmp_addr[4] = {0}; + uint8_t tmp_data[4] = {0}; + uint8_t tmp_read[4] = {0}; + /* char *tmp_chipname = private_ts->chip_name; */ + + tmp_addr[3] = 0x10; tmp_addr[2] = 0x00; tmp_addr[1] = 0x73; tmp_addr[0] = 0xEC; + if (on) { + I("%s : Turn on!\n", __func__); + if (strcmp(HX_83102D_SERIES_PWON, private_ts->chip_name) == 0) { + I("%s: need to enter Mp mode!\n", __func__); + tmp_data[3] = 0x00; tmp_data[2] = 0x10; tmp_data[1] = 0x73; tmp_data[0] = 0x80; + do { + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); + usleep_range(10000, 10001); + g_core_fp.fp_register_read(tmp_addr, 4, tmp_read, false); + I("%s: now read[2]=0x%02X, read[1]=0x%02X, read[0]=0x%02X!\n", + __func__, tmp_read[2], tmp_read[1], tmp_read[0]); + retry--; + } while (((retry > 0) && (tmp_read[2] != tmp_data[2] && tmp_read[1] != tmp_data[1] && tmp_read[0] != tmp_data[0]))); + } else { + I("%s:Nothing to be done!\n", __func__); + } + } else { + I("%s : Turn off!\n", __func__); + if (strcmp(HX_83102D_SERIES_PWON, private_ts->chip_name) == 0) { + I("%s: need to enter Mp mode!\n", __func__); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + do { + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); + usleep_range(10000, 10001); + g_core_fp.fp_register_read(tmp_addr, 4, tmp_read, false); + I("%s: now read[2]=0x%02X, read[1]=0x%02X, read[0]=0x%02X!\n", __func__, tmp_read[2], tmp_read[1], tmp_read[0]); + retry--; + } while ((retry > 0) && (tmp_read[2] != tmp_data[2] && tmp_read[1] != tmp_data[1] && tmp_read[0] != tmp_data[0])); + } else { + I("%s Nothing to be done!\n", __func__); + } + } + return rslt; +} + +/* HX_GAP START */ +/* gap test function */ +/* extern int himax_write_to_ic_flash_flow(uint32_t start_addr,uint32_t + * *write_data,uint32_t write_len); + */ + +static int himax_gap_test_vertical_setting(void) +{ + g_gap_vertical_part = kcalloc(g_gap_vertical_partial, + sizeof(int), GFP_KERNEL); + if (g_gap_vertical_part == NULL) { + E("%s: Memory allocation falied!\n", __func__); + return MEM_ALLOC_FAIL; + } + g_gap_vertical_part[0] = 0; + g_gap_vertical_part[1] = 4; + g_gap_vertical_part[2] = 8; + kfree(g_gap_vertical_part); + + return NO_ERR; +} + +static void himax_cal_gap_data_vertical(int start, int end_idx, int direct, + uint32_t *org_raw, uint32_t *result_raw) +{ + int i = 0; + int rx_num = ic_data->HX_RX_NUM; + + I("%s:start=%d\n", __func__, start); + I("%s:end_idx=%d\n", __func__, end_idx); + for (i = start; i < (start + rx_num*end_idx); i++) { + if (direct == 0) { /* up - down */ + if (i < start+rx_num) + result_raw[i] = 0; + else + result_raw[i] = org_raw[i-rx_num] - org_raw[i]; + + } else { /* down - up */ + if (i > (start + rx_num*(end_idx-1)-1)) + result_raw[i] = 0; + else + result_raw[i] = org_raw[i+rx_num] - org_raw[i]; + + } + } +} + +static int himax_gap_test_vertical_raw(int test_type, uint32_t *org_raw) +{ + int i_partial = 0; + int tmp_start = 0; + int tmp_end_idx = 0; + uint32_t *result_raw; + int i = 0; + int ret_val = NO_ERR; + + int tx_num = ic_data->HX_TX_NUM; + int rx_num = ic_data->HX_RX_NUM; + + if (himax_gap_test_vertical_setting()) + return MEM_ALLOC_FAIL; + + I("Print vertical ORG RAW\n"); + for (i = 0; i < tx_num*rx_num; i++) { + I("%04d,", org_raw[i]); + if (i > 0 && i%rx_num == (rx_num-1)) + I("\n"); + } + + result_raw = kcalloc(tx_num*rx_num, sizeof(uint32_t), GFP_KERNEL); + if (result_raw == NULL) { + E("%s: Memory allocation falied!\n", __func__); + goto alloc_result_raw_failed; + } + + for (i_partial = 0; i_partial < g_gap_vertical_partial; i_partial++) { + + tmp_start = g_gap_vertical_part[i_partial]*rx_num; + if (i_partial+1 == g_gap_vertical_partial) + tmp_end_idx = tx_num - g_gap_vertical_part[i_partial]; + else + tmp_end_idx = g_gap_vertical_part[i_partial+1] - + g_gap_vertical_part[i_partial]; + + if (i_partial % 2 == 0) + himax_cal_gap_data_vertical(tmp_start, tmp_end_idx, 0, + org_raw, result_raw); + else + himax_cal_gap_data_vertical(tmp_start, tmp_end_idx, 1, + org_raw, result_raw); + + } + + I("Print Vertical New RAW\n"); + for (i = 0; i < tx_num*rx_num; i++) { + I("%04d,", result_raw[i]); + if (i > 0 && i%rx_num == (rx_num-1)) + I("\n"); + } + + for (i = 0; i < tx_num*rx_num; i++) { + if (result_raw[i] < g_inspection_criteria[IDX_GAP_VER_RAWMIN][i] + && + result_raw[i] > g_inspection_criteria[IDX_GAP_VER_RAWMAX][i]) { + ret_val = NO_ERR - i; + break; + } + } + + /* himax_write_to_ic_flash_flow(0x1A000,result_raw,tx_num*rx_num); */ + kfree(result_raw); +alloc_result_raw_failed: + kfree(g_gap_vertical_part); + g_gap_vertical_part = NULL; + + return ret_val; +} + +static int himax_gap_test_horizontal_setting(void) +{ + g_gap_horizontal_part = kcalloc(g_gap_horizontal_partial, + sizeof(int), GFP_KERNEL); + if (g_gap_horizontal_part == NULL) { + E("%s: Memory allocation falied!\n", __func__); + return MEM_ALLOC_FAIL; + } + g_gap_horizontal_part[0] = 0; + g_gap_horizontal_part[1] = 8; + g_gap_horizontal_part[2] = 24; + + return NO_ERR; +} + +static void himax_cal_gap_data_horizontal(int start, int end_idx, int direct, + uint32_t *org_raw, uint32_t *result_raw) +{ + int i = 0; + int j = 0; + int rx_num = ic_data->HX_RX_NUM; + int tx_num = ic_data->HX_TX_NUM; + + I("start=%d\n", start); + I("end_idx=%d\n", end_idx); + for (j = 0; j < tx_num; j++) { + for (i = (start + (j*rx_num)); + i < (start + (j*rx_num) + end_idx); i++) { + /* left - right */ + if (direct == 0) { + if (i == (start + (j*rx_num))) + result_raw[i] = 0; + else + result_raw[i] = + org_raw[i-1] - org_raw[i]; + + } else { /* right - left */ + if (i == ((start + (j*rx_num) + end_idx) - 1)) + result_raw[i] = 0; + else + result_raw[i] = + org_raw[i + 1] - org_raw[i]; + } + } + } +} + +static int himax_gap_test_honrizontal_raw(int test_type, uint32_t *raw) +{ + int rx_num = ic_data->HX_RX_NUM; + int tx_num = ic_data->HX_TX_NUM; + int tmp_start = 0; + int tmp_end_idx = 0; + int i_partial = 0; + uint32_t *result_raw; + int i = 0; + int ret_val = NO_ERR; + + if (himax_gap_test_horizontal_setting()) + return MEM_ALLOC_FAIL; + + result_raw = kcalloc(ic_data->HX_TX_NUM * ic_data->HX_RX_NUM, + sizeof(uint32_t), GFP_KERNEL); + if (result_raw == NULL) { + E("%s: Memory allocation falied!\n", __func__); + goto alloc_result_raw_failed; + } + + I("Print Horizontal ORG RAW\n"); + for (i = 0; i < tx_num*rx_num; i++) { + I("%04d,", raw[i]); + if (i > 0 && i%rx_num == (rx_num-1)) + I("\n"); + } + + for (i_partial = 0; i_partial < g_gap_horizontal_partial; i_partial++) { + tmp_start = g_gap_horizontal_part[i_partial]; + if (i_partial+1 == g_gap_horizontal_partial) + tmp_end_idx = rx_num - g_gap_horizontal_part[i_partial]; + else + tmp_end_idx = g_gap_horizontal_part[i_partial+1] - + g_gap_horizontal_part[i_partial]; + + if (i_partial % 2 == 0) + himax_cal_gap_data_horizontal(tmp_start, tmp_end_idx, + 0, raw, result_raw); + else + himax_cal_gap_data_horizontal(tmp_start, tmp_end_idx, + 1, raw, result_raw); + + } + I("Print Horizontal New RAW\n"); + for (i = 0; i < tx_num*rx_num; i++) { + I("%04d,", result_raw[i]); + if (i > 0 && i%rx_num == (rx_num-1)) + I("\n"); + } + + for (i = 0; i < tx_num*rx_num; i++) { + if (result_raw[i] < g_inspection_criteria[IDX_GAP_HOR_RAWMIN][i] + && + result_raw[i] > g_inspection_criteria[IDX_GAP_HOR_RAWMAX][i]) { + ret_val = NO_ERR - i; + break; + } + } + + /* himax_write_to_ic_flash_flow(0x1A800,result_raw,tx_num*rx_num); */ + kfree(result_raw); +alloc_result_raw_failed: + kfree(g_gap_horizontal_part); + g_gap_horizontal_part = NULL; + + return ret_val; +} +/* HX_GAP END*/ +#define FAIL_IN_INDEX "%s: %s FAIL in index %d\n" +static uint32_t mpTestFunc(uint8_t checktype, uint32_t datalen) +{ + int32_t ret = 0; + uint32_t i/*, j*/; + uint32_t *RAW = NULL; + uint16_t palm_num = 0; + uint16_t noise_count = 0; + int ret_val = -1; + + /*uint16_t* pInspectGridData = &gInspectGridData[0];*/ + /*uint16_t* pInspectNoiseData = &gInspectNoiseData[0];*/ + I("Now Check type = %d\n", checktype); + + RAW = kcalloc(datalen, sizeof(uint32_t), GFP_KERNEL); + if (RAW == NULL) { + E("%s, Failed to allocate memory\n", __func__); + return HX_INSPECT_MEMALLCTFAIL; + } + + if (himax_check_mode(checktype)) { + /*himax_check_mode(checktype);*/ + + I("Need Change Mode ,target=%s\n", g_himax_inspection_mode[checktype]); + + g_core_fp.fp_sense_off(true); + hx_turn_on_mp_func(1); + +#ifndef HX_ZERO_FLASH + if (g_core_fp.fp_reload_disable != NULL) + g_core_fp.fp_reload_disable(1); +#endif + + himax_switch_mode_inspection(checktype); + + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_EOPEN; + break; + + case HIMAX_INSPECTION_OPEN: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_EOPEN; + break; + + case HIMAX_INSPECTION_MICRO_OPEN: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_EMOPEN; + break; + + case HIMAX_INSPECTION_SHORT: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_ESHORT; + break; + + case HIMAX_INSPECTION_RAWDATA: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_ERAW; + break; + + case HIMAX_INSPECTION_BPN_RAWDATA: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_ERAW; + break; + + case HIMAX_INSPECTION_WEIGHT_NOISE: + himax_set_N_frame(NOISEFRAME, checktype); + ret_val = HX_INSPECT_WT_ENOISE; + break; + + case HIMAX_INSPECTION_ABS_NOISE: + himax_set_N_frame(NOISEFRAME, checktype); + ret_val = HX_INSPECT_ABS_ENOISE; + break; + + case HIMAX_INSPECTION_GAPTEST_RAW: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_EGAP_RAW; + break; + + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + himax_set_N_frame(NORMAL_IDLE_RAWDATA_NOISEFRAME, checktype); + ret_val = HX_INSPECT_EACT_IDLE_RAW; + break; + + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + himax_set_N_frame(NORMAL_IDLE_RAWDATA_NOISEFRAME, checktype); + ret_val = HX_INSPECT_EACT_IDLE_NOISE; + break; + + case HIMAX_INSPECTION_LPWUG_RAWDATA: + himax_set_N_frame(OTHERSFRAME, checktype); + ret_val = HX_INSPECT_ELPWUG_RAW; + break; + + case HIMAX_INSPECTION_LPWUG_NOISE: + himax_set_N_frame(LPWUG_NOISEFRAME, checktype); + ret_val = HX_INSPECT_ELPWUG_NOISE; + break; + + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + himax_set_N_frame(LPWUG_IDLE_RAWDATAFRAME, checktype); + ret_val = HX_INSPECT_ELPWUG_IDLE_RAW; + break; + + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + himax_set_N_frame(LPWUG_IDLE_NOISEFRAME, checktype); + ret_val = HX_INSPECT_ELPWUG_IDLE_NOISE; + break; + + default: + E("UNDEFINED test type %d, exit\n", checktype); + ret_val = 0xFFFFFFFF; + break; + } + +/* + * if (checktype == HIMAX_INSPECTION_WEIGHT_NOISE || + * checktype == HIMAX_INSPECTION_ABS_NOISE) { + * himax_set_N_frame(NOISEFRAME, checktype); + * } else if (checktype == HIMAX_INSPECTION_ACT_IDLE_RAWDATA || + * checktype == HIMAX_INSPECTION_ACT_IDLE_NOISE) { + * I("N frame = %d\n", NORMAL_IDLE_RAWDATA_NOISEFRAME); + * himax_set_N_frame(NORMAL_IDLE_RAWDATA_NOISEFRAME, checktype); + * } else if (checktype == HIMAX_INSPECTION_LPWUG_RAWDATA) { + * I("N frame = %d\n", LPWUG_RAWDATAFRAME); + * himax_set_N_frame(LPWUG_RAWDATAFRAME, checktype); + * } else if (checktype == HIMAX_INSPECTION_LPWUG_NOISE) { + * I("N frame = %d\n", LPWUG_NOISEFRAME); + * himax_set_N_frame(LPWUG_NOISEFRAME, checktype); + * } else if (checktype == HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA) { + * I("N frame = %d\n", LPWUG_IDLE_RAWDATAFRAME); + * himax_set_N_frame(LPWUG_IDLE_RAWDATAFRAME, checktype); + * } else if (checktype == HIMAX_INSPECTION_LPWUG_IDLE_NOISE) { + * I("N frame = %d\n", LPWUG_IDLE_NOISEFRAME); + * himax_set_N_frame(LPWUG_IDLE_NOISEFRAME, checktype); + * } else { + * himax_set_N_frame(OTHERSFRAME, checktype); + * } + */ + + g_core_fp.fp_sense_on(1); + + if (ret_val == 0xFFFFFFFF) + return ret_val; + } + + ret = himax_wait_sorting_mode(checktype); + if (ret) { + E("%s: himax_wait_sorting_mode FAIL\n", __func__); + return ret_val; + } + himax_switch_data_type(checktype); + + ret = himax_get_rawdata(RAW, datalen); + if (ret) { + E("%s: himax_get_rawdata FAIL\n", __func__); + return ret_val | ret; + } + + /*get Max DC from FW*/ + g_dc_max = g_core_fp.fp_get_max_dc(); + + /* back to normal */ + himax_switch_data_type(HIMAX_INSPECTION_BACK_NORMAL); + + I("%s: Init OK, start to test!\n", __func__); + + snprintf(g_start_log, 256 * sizeof(char), "\n%s%s\n", + g_himax_inspection_mode[checktype], ": data as follow!\n"); + + /*Check Data*/ + switch (checktype) { + case HIMAX_INSPECTION_SORTING: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] < g_inspection_criteria[IDX_SORTMIN][i]) { + E(FAIL_IN_INDEX, __func__, "sorting mode open test", i); + ret_val = HX_INSPECT_EOPEN; + goto FAIL_END; + } + } + I("%s: sorting mode open test PASS\n", __func__); + break; + + case HIMAX_INSPECTION_OPEN: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_OPENMAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_OPENMIN][i]) { + E(FAIL_IN_INDEX, __func__, "open test", i); + ret_val = HX_INSPECT_EOPEN; + goto FAIL_END; + } + } + I("%s: open test PASS\n", __func__); + break; + + case HIMAX_INSPECTION_MICRO_OPEN: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_M_OPENMAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_M_OPENMIN][i]) { + E(FAIL_IN_INDEX, __func__, "micro open test", i); + ret_val = HX_INSPECT_EMOPEN; + goto FAIL_END; + } + } + I("%s: micro open test PASS\n", __func__); + break; + + case HIMAX_INSPECTION_SHORT: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_SHORTMAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_SHORTMIN][i]) { + E(FAIL_IN_INDEX, __func__, "short test", i); + ret_val = HX_INSPECT_ESHORT; + goto FAIL_END; + } + } + I("%s: short test PASS\n", __func__); + break; + + case HIMAX_INSPECTION_RAWDATA: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + /* I("Now new compare, datalen=%d!\n", + * ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); + */ + if ((int)RAW[i] > g_inspection_criteria[IDX_RAWMAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_RAWMIN][i]) { + E("%s: rawdata test FAIL:RAW[%d]=%d\n", __func__, i, RAW[i]); + I("%s: Now Criteria max=%d,min=%d\n", __func__, + g_inspection_criteria[IDX_RAWMAX][i], + g_inspection_criteria[IDX_RAWMIN][i]); + ret_val = HX_INSPECT_ERAW; + goto FAIL_END; + } + } + I("%s: rawdata test PASS\n", __func__); + break; + + case HIMAX_INSPECTION_BPN_RAWDATA: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + /* I("Now new compare, datalen=%d!\n", + * ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); + */ + RAW[i] = (int)RAW[i] * 100 / g_dc_max; + if ((int)RAW[i] > g_inspection_criteria[IDX_BPN_RAWMAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_BPN_RAWMIN][i]) { + E("%s: rawdata test FAIL:BPN RAW[%d]=%d\n", __func__, i, RAW[i]); + I("%s: Now Criteria max=%d,min=%d\n", __func__, + g_inspection_criteria[IDX_BPN_RAWMAX][i], + g_inspection_criteria[IDX_BPN_RAWMIN][i]); + ret_val = HX_INSPECT_ERAW; + goto FAIL_END; + } + } + I("%s: BPN rawdata test PASS\n", __func__); + break; + + case HIMAX_INSPECTION_WEIGHT_NOISE: + /*I("NOISEMAX=%d\n", NOISEMAX);*/ + noise_count = 0; + himax_get_noise_base(); + palm_num = himax_get_palm_num(); + for (i = 0; i < (ic_data->HX_TX_NUM * ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > NOISEMAX) + noise_count++; + } + I("noise_count=%d\n", noise_count); + if (noise_count > palm_num) { + E("%s: noise test FAIL\n", __func__); + ret_val = HX_INSPECT_WT_ENOISE; + goto FAIL_END; + } + snprintf(g_start_log, 256 * sizeof(char), "\n Threshold = %d\n", NOISEMAX); + /*Check weightingt*/ + ret = himax_get_noise_weight_test(); + if (ret < 0) { + I("%s: noise test FAIL %d\n", __func__, ret); + ret_val = HX_INSPECT_WT_ENOISE; + goto FAIL_END; + } + + /*Check negative side noise*/ + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > (g_inspection_criteria[IDX_WT_NOISEMAX][i] * g_recal_thx / 100) + || (int)RAW[i] < (g_inspection_criteria[IDX_WT_NOISEMIN][i] * g_recal_thx / 100)) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_WEIGHT_NOISE", i); + ret_val = HX_INSPECT_WT_ENOISE; + goto FAIL_END; + } + } + I("%s: noise test(weight) PASS\n", __func__); + break; + + case HIMAX_INSPECTION_ABS_NOISE: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_ABS_NOISEMAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_ABS_NOISEMIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_ABS_NOISE", i); + I("%s: Now Criteria max=%d,min=%d\n", __func__, + g_inspection_criteria[IDX_ABS_NOISEMAX][i], + g_inspection_criteria[IDX_ABS_NOISEMIN][i]); + ret_val = HX_INSPECT_ABS_ENOISE; + goto FAIL_END; + } + } + I("%s: noise test(absolute) PASS\n", __func__); + break; + + case HIMAX_INSPECTION_GAPTEST_RAW: + if (himax_gap_test_vertical_raw(HIMAX_INSPECTION_GAPTEST_RAW, RAW) != NO_ERR) { + E("%s: HIMAX_INSPECTION_GAPTEST_RAW FAIL\n", __func__); + ret_val = HX_INSPECT_EGAP_RAW; + goto FAIL_END; + } + if (himax_gap_test_honrizontal_raw(HIMAX_INSPECTION_GAPTEST_RAW, RAW) != NO_ERR) { + E("%s: HIMAX_INSPECTION_GAPTEST_RAW FAIL\n", __func__); + ret_val = HX_INSPECT_EGAP_RAW; + goto FAIL_END; + } + break; + + case HIMAX_INSPECTION_ACT_IDLE_RAWDATA: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_ACT_IDLE_RAWDATA_MAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_ACT_IDLE_RAWDATA_MIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_ACT_IDLE_RAWDATA", i); + ret_val = HX_INSPECT_EACT_IDLE_RAW; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_ACT_IDLE_RAWDATA PASS\n", __func__); + break; + + case HIMAX_INSPECTION_ACT_IDLE_NOISE: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_ACT_IDLE_NOISE_MAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_ACT_IDLE_NOISE_MIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_ACT_IDLE_NOISE", i); + ret_val = HX_INSPECT_EACT_IDLE_NOISE; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_ACT_IDLE_NOISE PASS\n", __func__); + break; + + case HIMAX_INSPECTION_LPWUG_RAWDATA: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_LPWUG_RAWDATA_MAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_LPWUG_RAWDATA_MIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_LPWUG_RAWDATA", i); + ret_val = HX_INSPECT_ELPWUG_RAW; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_LPWUG_RAWDATA PASS\n", __func__); + break; + + case HIMAX_INSPECTION_LPWUG_NOISE: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_LPWUG_NOISE_MAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_LPWUG_NOISE_MIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_LPWUG_NOISE", i); + ret_val = HX_INSPECT_ELPWUG_NOISE; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_LPWUG_NOISE PASS\n", __func__); + break; + + case HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_LPWUG_IDLE_RAWDATA_MAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_LPWUG_IDLE_RAWDATA_MIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA", i); + ret_val = HX_INSPECT_ELPWUG_IDLE_RAW; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA PASS\n", __func__); + break; + + case HIMAX_INSPECTION_LPWUG_IDLE_NOISE: + for (i = 0; i < (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM); i++) { + if ((int)RAW[i] > g_inspection_criteria[IDX_LPWUG_IDLE_NOISE_MAX][i] + || (int)RAW[i] < g_inspection_criteria[IDX_LPWUG_IDLE_NOISE_MIN][i]) { + E(FAIL_IN_INDEX, __func__, "HIMAX_INSPECTION_LPWUG_IDLE_NOISE", i); + ret_val = HX_INSPECT_ELPWUG_IDLE_NOISE; + goto FAIL_END; + } + } + I("%s: HIMAX_INSPECTION_LPWUG_IDLE_NOISE PASS\n", __func__); + break; + + default: + E("Wrong type=%d\n", checktype); + ret_val = 0xFFFFFFFF; + break; + } + + ret_val = HX_INSPECT_OK; + snprintf(g_rslt_log, 256 * sizeof(char), "\n%s%s\n", + g_himax_inspection_mode[checktype], " Test Pass!\n"); + I("pass write log\n"); + goto END_FUNC; + +FAIL_END: + snprintf(g_rslt_log, 256 * sizeof(char), "\n%s%s\n", + g_himax_inspection_mode[checktype], " Test Fail!\n"); + I("fail write log\n"); +END_FUNC: + hx_test_data_get(RAW, g_start_log, g_rslt_log, checktype); + kfree(RAW); + + return ret_val; +} + +/* claculate 10's power function */ +static int himax_power_cal(int pow, int number) +{ + int i = 0; + int result = 1; + + for (i = 0; i < pow; i++) + result *= 10; + result = result * number; + + return result; + +} + +/* String to int */ +static int hiamx_parse_str2int(char *str) +{ + int i = 0; + int temp_cal = 0; + int result = 0; + int str_len = strlen(str); + int negtive_flag = 0; + + for (i = 0; i < strlen(str); i++) { + if (str[i] != '-' && str[i] > '9' && str[i] < '0') { + E("%s: Parsing fail!\n", __func__); + result = -9487; + negtive_flag = 0; + break; + } + if (str[i] == '-') { + negtive_flag = 1; + continue; + } + temp_cal = str[i] - '0'; + result += himax_power_cal(str_len-i-1, temp_cal); + /* str's the lowest char is the number's the highest number + * So we should reverse this number before using the power + * function + * -1: starting number is from 0 ex:10^0 = 1,10^1=10 + */ + } + + if (negtive_flag == 1) + result = 0 - result; + + return result; +} + + +/* Get sub-string from original string by using some charaters + * return size of result + */ +static int himax_saperate_comma(const struct firmware *file_entry, + char **result, int str_size) +{ + int count = 0; + int str_count = 0; /* now string*/ + int char_count = 0; /* now char count in string*/ + + do { + switch (file_entry->data[count]) { + case ASCII_COMMA: + case ACSII_SPACE: + case ASCII_CR: + case ASCII_LF: + count++; + /* If end of line as above condifiton, + * differencing the count of char. + * If char_count != 0 + * it's meaning this string is parsing over . + * The Next char is belong to next string + */ + if (char_count != 0) { + char_count = 0; + str_count++; + } + break; + default: + result[str_count][char_count++] = + file_entry->data[count]; + count++; + break; + } + } while (count < file_entry->size && str_count < str_size); + + return str_count; +} + +static int hx_diff_str(char *str1, char *str2) +{ + int i = 0; + int result = 0; /* zero is all same, non-zero is not same index*/ + int str1_len = strlen(str1); + int str2_len = strlen(str2); + + if (str1_len != str2_len) { + if (private_ts->debug_log_level & BIT(4)) + I("%s:Size different!\n", __func__); + return LENGTH_FAIL; + } + + for (i = 0; i < str1_len; i++) { + if (str1[i] != str2[i]) { + result = i+1; + I("%s: different in %d!\n", __func__, result); + return result; + } + } + + return result; +} + +/* get idx of criteria whe parsing file */ +int hx_find_crtra_id(char *input) +{ + int i = 0; + int result = 0; + + for (i = 0 ; i < HX_CRITERIA_SIZE ; i++) { + if (hx_diff_str(g_hx_inspt_crtra_name[i], input) == 0) { + result = i; + I("find the str=%s,idx=%d\n", + g_hx_inspt_crtra_name[i], i); + break; + } + } + if (i > (HX_CRITERIA_SIZE - 1)) { + E("%s: find Fail!\n", __func__); + return LENGTH_FAIL; + } + + return result; +} + +int hx_print_crtra_after_parsing(void) +{ + int i = 0, j = 0; + int all_mut_len = ic_data->HX_TX_NUM*ic_data->HX_RX_NUM; + + for (i = 0; i < HX_CRITERIA_SIZE; i++) { + I("Now is %s\n", g_hx_inspt_crtra_name[i]); + if (g_inspt_crtra_flag[i] == 1) { + for (j = 0; j < all_mut_len; j++) { + I("%d, ", g_inspection_criteria[i][j]); + if (j % 16 == 15) + PI("\n"); + } + } else { + I("No this Item in this criteria file!\n"); + } + PI("\n"); + } + + return 0; +} + +static int hx_get_crtra_by_name(char **result, int size_of_result_str) +{ + int i = 0; + /* count of criteria type */ + int count_type = 0; + /* count of criteria data */ + int count_data = 0; + int err = HX_INSPECT_OK; + int all_mut_len = ic_data->HX_TX_NUM*ic_data->HX_RX_NUM; + int temp = 0; + + /* get criteria and assign to a global array(2-Dimensional/int) */ + /* basiclly the size of criteria will be + * (crtra_count * (all_mut_len) + crtra_count) + * but we use file size to be the end of counter + */ + for (i = 0; i < size_of_result_str && result[i] != NULL; i++) { + /* It have get one page(all mutual) criteria data! + * And we should skip the string of criteria name! + */ + if (i == 0 || i == + ((i / (all_mut_len))+(i / (all_mut_len) * (all_mut_len)))) { + count_data = 0; + + if (private_ts->debug_log_level & BIT(4)) + I("Now find str=%s ,idx=%d\n", result[i], i); + + /* check the item of criteria is in criteria file + * or not + */ + count_type = hx_find_crtra_id(result[i]); + if (count_type < 0) { + E("1. %s:Name Not match!\n", __func__); + /* E("can recognize[%d]=%s\n", count_type, + * g_hx_inspt_crtra_name[count_type]); + */ + E("get from file[%d]=%s\n", i, result[i]); + E("Please check criteria file again!\n"); + err = HX_INSPECT_EFILE; + goto END_FUNCTION; + } else { + I("Now str=%s, idx=%d\n", + g_hx_inspt_crtra_name[count_type], count_type); + g_inspt_crtra_flag[count_type] = 1; + } + continue; + } + /* change string to int*/ + temp = hiamx_parse_str2int(result[i]); + if (temp != -9487) + g_inspection_criteria[count_type][count_data] = temp; + else { + E("%s: Parsing Fail in %d\n", __func__, i); + E("in range:[%d]=%s\n", count_type, + g_hx_inspt_crtra_name[count_type]); + E("btw, get from file[%d]=%s\n", i, result[i]); + break; + } + /* dbg + * I("[%d]g_inspection_criteria[%d][%d]=%d\n", + * i, count_type, count_data, + * g_inspection_criteria[count_type][count_data]); + */ + count_data++; + + } + + if (private_ts->debug_log_level & BIT(4)) { + /* dbg:print all of criteria from parsing file */ + hx_print_crtra_after_parsing(); + } + + I("Total loop=%d\n", i); +END_FUNCTION: + return err; +} + +static int himax_parse_criteria_file(void) +{ + int err = HX_INSPECT_OK; + const struct firmware *file_entry = NULL; + char **result; + int i = 0; + int crtra_count = HX_CRITERIA_SIZE; + int data_size = 0; /* The maximum of number Data*/ + int all_mut_len = ic_data->HX_TX_NUM*ic_data->HX_RX_NUM; + int str_max_len = 128; + int result_all_len = 0; + int file_size = 0; + int size_of_result_str = 0; + + I("%s,Entering\n", __func__); + if (hx_self_test_file_name == NULL) { + E("file name is NULL\n"); + hx_self_test_file_name = kzalloc(80, GFP_KERNEL); + if (private_ts->pdata->panel_supplier[0]) + snprintf(hx_self_test_file_name, 60, "%s_hx_criteria.csv", private_ts->pdata->panel_supplier); + else + snprintf(hx_self_test_file_name, 16, "hx_criteria.csv"); + I("%s: Use name %s\n", __func__, hx_self_test_file_name); + } + + I("file name = %s\n", hx_self_test_file_name); + /* default path is /system/etc/firmware */ + err = request_firmware(&file_entry, hx_self_test_file_name, private_ts->dev); + if (err < 0) { + E("%s,fail in line%d error code=%d\n", __func__, __LINE__, err); + err = HX_INSPECT_EFILE; + goto END_FUNC_REQ_FAIL; + } + + /* size of criteria include name string */ + data_size = ((all_mut_len) * crtra_count) + crtra_count; + + /* init the array which store original criteria and include + * name string + */ + result = kcalloc(data_size, sizeof(char *), GFP_KERNEL); + if (result != NULL) { + for (i = 0 ; i < data_size; i++) { + result[i] = kcalloc(str_max_len, sizeof(char), GFP_KERNEL); + if (result[i] == NULL) { + E("%s: rst_arr Memory allocation falied!\n", __func__); + err = HX_INSPECT_MEMALLCTFAIL; + goto rst_arr_mem_alloc_failed; + } + } + } else { + E("%s: Memory allocation falied!\n", __func__); + err = HX_INSPECT_MEMALLCTFAIL; + goto rst_mem_alloc_failed; + } + + result_all_len = data_size; + file_size = file_entry->size; + I("Now result_all_len=%d\n", result_all_len); + I("Now file_size=%d\n", file_size); + + /* dbg */ + if (private_ts->debug_log_level & BIT(4)) { + I("first 4 bytes 0x%2X,0x%2X,0x%2X,0x%2X !\n", + file_entry->data[0], file_entry->data[1], + file_entry->data[2], file_entry->data[3]); + } + + /* parse value in to result array(1-Dimensional/String) */ + size_of_result_str = + himax_saperate_comma(file_entry, result, data_size); + + I("%s: now size_of_result_str=%d\n", __func__, size_of_result_str); + + err = hx_get_crtra_by_name(result, size_of_result_str); + if (err != HX_INSPECT_OK) { + E("%s:Load criteria from file fail, go end!\n", __func__); + goto END_FUNC; + } + + +END_FUNC: +rst_arr_mem_alloc_failed: + for (i = 0 ; i < data_size; i++) + if (result[i] != NULL) + kfree(result[i]); +rst_mem_alloc_failed: + kfree(result); + release_firmware(file_entry); +END_FUNC_REQ_FAIL: + I("%s,END\n", __func__); + return err; + /* parsing Criteria end */ +} + + +int hx_get_size_str_arr(char **input) +{ + int i = 0; + int result = 0; + + while (input[i] != NULL) + i++; + + result = i; + if (private_ts->debug_log_level & BIT(4)) + I("There is %d in [0]=%s\n", result, input[0]); + + return result; +} + +static int himax_self_test_data_init(void) +{ + int ret = HX_INSPECT_OK; + int i = 0; + + /* + * 5: one value will not over than 99999, so get this size of string + * 2: get twice size + */ + g_1kind_raw_size = 5 * ic_data->HX_RX_NUM * ic_data->HX_RX_NUM * 2; + + /* get test item and its items of criteria*/ + HX_CRITERIA_ITEM = hx_get_size_str_arr(g_himax_inspection_mode); + HX_CRITERIA_SIZE = hx_get_size_str_arr(g_hx_inspt_crtra_name); + I("There is %d HX_CRITERIA_ITEM and %d HX_CRITERIA_SIZE\n", + HX_CRITERIA_ITEM, HX_CRITERIA_SIZE); + + /* init criteria data*/ + g_inspt_crtra_flag = kzalloc(HX_CRITERIA_SIZE*sizeof(int), GFP_KERNEL); + g_inspection_criteria = kzalloc(sizeof(int *)*HX_CRITERIA_SIZE, GFP_KERNEL); + if (g_inspt_crtra_flag == NULL || g_inspection_criteria == NULL) { + E("%s: %d, Memory allocation falied!\n", __func__, __LINE__); + return MEM_ALLOC_FAIL; + } + + for (i = 0; i < HX_CRITERIA_SIZE; i++) { + g_inspection_criteria[i] = kcalloc( + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM), sizeof(int), GFP_KERNEL); + if (g_inspection_criteria[i] == NULL) { + E("%s: %d, Memory allocation falied!\n", __func__, __LINE__); + return MEM_ALLOC_FAIL; + } + } + + /* parsing criteria from file*/ + ret = himax_parse_criteria_file(); + + if (private_ts->debug_log_level & BIT(4)) { + /* print get criteria string */ + for (i = 0 ; i < HX_CRITERIA_SIZE ; i++) { + if (g_inspt_crtra_flag[i] != 0) + I("%s: [%d]There is String=%s\n", + __func__, i, g_hx_inspt_crtra_name[i]); + } + } + + if (g_rslt_data == NULL) { + g_rslt_data = kcalloc(g_1kind_raw_size * HX_CRITERIA_ITEM, + sizeof(char), GFP_KERNEL); + I("g_rslt_data is NULL"); + } else { + memset(g_rslt_data, 0x00, g_1kind_raw_size * HX_CRITERIA_ITEM * + sizeof(char)); + } + + snprintf(g_file_path, + (int)(strlen(HX_RSLT_OUT_PATH) + strlen(HX_RSLT_OUT_FILE)+1), + "%s%s", HX_RSLT_OUT_PATH, HX_RSLT_OUT_FILE); + + return ret; +} + +static void himax_self_test_data_deinit(void) +{ + int i = 0; + + /*dbg*/ + /* for (i = 0; i < HX_CRITERIA_ITEM; i++) + * I("%s:[%d]%d\n", __func__, i, g_inspection_criteria[i]); + */ + if (g_inspection_criteria != NULL) { + for (i = 0; i < HX_CRITERIA_SIZE; i++) { + if (g_inspection_criteria[i] != NULL) + kfree(g_inspection_criteria[i]); + } + kfree(g_inspection_criteria); + I("Now it have free the g_inspection_criteria!\n"); + } else { + I("No Need to free g_inspection_criteria!\n"); + } + + g_rslt_data_len = 0; + +} + +static int himax_chip_self_test(void) +{ + uint32_t ret = HX_INSPECT_OK; + uint8_t tmp_addr[DATA_LEN_4] = {0x94, 0x72, 0x00, 0x10}; + uint8_t tmp_data[DATA_LEN_4] = {0x01, 0x00, 0x00, 0x00}; + +#if defined(HIMAX_V2_MULTI_BIN)||defined(HX_CODE_OVERLAY) + uint8_t normalfw[32] = "Himax_firmware.bin"; + uint8_t mpapfw[32] = "Himax_mpfw.bin"; + if (private_ts->pdata->panel_supplier[0]) { + snprintf(normalfw, 32, "%s_Himax_firmware.bin", private_ts->pdata->panel_supplier); + snprintf(mpapfw, 32, "%s_Himax_mpfw.bin", private_ts->pdata->panel_supplier); + } +#endif + + I("%s:IN\n", __func__); + + private_ts->suspend_resume_done = 0; + + ret = himax_self_test_data_init(); + if (ret != HX_INSPECT_OK) { + E("%s: himax_self_test_data_init fail!\n", __func__); + goto END_FUNC; + } + +#if defined(HIMAX_V2_MULTI_BIN)||defined(HX_CODE_OVERLAY) + g_core_fp.fp_0f_op_file_dirly(mpapfw); + g_core_fp.fp_reload_disable(0); + g_core_fp.fp_sense_on(0x00); +#endif + + if (g_inspt_crtra_flag[IDX_OPENMIN] == 1 && + g_inspt_crtra_flag[IDX_OPENMAX] == 1) { + /*1. Open Test*/ + I("[MP_OPEN_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_OPEN, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("1. Open Test: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_OPENMIN], + g_inspt_crtra_flag[IDX_OPENMIN]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_OPENMAX], + g_inspt_crtra_flag[IDX_OPENMAX]); + } + + if (g_inspt_crtra_flag[IDX_M_OPENMIN] == 1 && + g_inspt_crtra_flag[IDX_M_OPENMAX] == 1) { + /*2. Micro-Open Test*/ + I("[MP_MICRO_OPEN_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_MICRO_OPEN, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("2. Micro Open Test: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_M_OPENMIN], + g_inspt_crtra_flag[IDX_M_OPENMIN]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_M_OPENMAX], + g_inspt_crtra_flag[IDX_M_OPENMAX]); + } + + if (g_inspt_crtra_flag[IDX_SHORTMIN] == 1 && + g_inspt_crtra_flag[IDX_SHORTMAX] == 1) { + /*3. Short Test*/ + I("[MP_SHORT_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_SHORT, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("3. Short Test: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_SHORTMIN], + g_inspt_crtra_flag[IDX_SHORTMIN]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_SHORTMAX], + g_inspt_crtra_flag[IDX_SHORTMAX]); + } + + if (g_inspt_crtra_flag[IDX_RAWMIN] == 1 && + g_inspt_crtra_flag[IDX_RAWMAX] == 1) { + /*4. RawData Test*/ + I("==========================================\n"); + I("[MP_RAW_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_RAWDATA, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("%d. %s: End %d\n\n\n", HIMAX_INSPECTION_RAWDATA, + g_himax_inspection_mode[HIMAX_INSPECTION_RAWDATA], ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_RAWMIN], + g_inspt_crtra_flag[IDX_RAWMIN]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_RAWMAX], + g_inspt_crtra_flag[IDX_RAWMAX]); + } + + if (g_inspt_crtra_flag[IDX_BPN_RAWMIN] == 1 && + g_inspt_crtra_flag[IDX_BPN_RAWMAX] == 1) { + /*4. RawData BPN Test*/ + I("==========================================\n"); + I("[MP_BPN_RAW_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_BPN_RAWDATA, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("%d. %s: End %d\n\n\n", HIMAX_INSPECTION_BPN_RAWDATA, + g_himax_inspection_mode[HIMAX_INSPECTION_BPN_RAWDATA], ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_BPN_RAWMIN], + g_inspt_crtra_flag[IDX_BPN_RAWMIN]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_BPN_RAWMAX], + g_inspt_crtra_flag[IDX_BPN_RAWMAX]); + } + + if (g_inspt_crtra_flag[IDX_WT_NOISEMIN] == 1 && + g_inspt_crtra_flag[IDX_WT_NOISEMAX] == 1) { + /*5. Noise Test by Weighting*/ + I("[MP_NOISE_TEST_WEIGHT]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_WEIGHT_NOISE, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("5. Noise Test: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_WT_NOISEMAX], + g_inspt_crtra_flag[IDX_WT_NOISEMAX]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_WT_NOISEMIN], + g_inspt_crtra_flag[IDX_WT_NOISEMIN]); + } + + if (g_inspt_crtra_flag[IDX_ABS_NOISEMIN] == 1 && + g_inspt_crtra_flag[IDX_ABS_NOISEMAX] == 1) { + /*5. Noise Test by per block*/ + I("[MP_NOISE_TEST_ABS]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_ABS_NOISE, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("5. Noise Test: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_ABS_NOISEMAX], + g_inspt_crtra_flag[IDX_ABS_NOISEMAX]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_ABS_NOISEMIN], + g_inspt_crtra_flag[IDX_ABS_NOISEMIN]); + } + + if (g_inspt_crtra_flag[IDX_SORTMIN] == 1 && + g_inspt_crtra_flag[IDX_SORTMAX] == 1) { + /*6. Sorting Test*/ + I("[SORTING TEST]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_SORTING, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("6. SORTING TEST: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_SORTMIN], + g_inspt_crtra_flag[IDX_SORTMIN]); + I("Now %s :flag=%d\n", g_hx_inspt_crtra_name[IDX_SORTMAX], + g_inspt_crtra_flag[IDX_SORTMAX]); + } + + if ((g_inspt_crtra_flag[IDX_GAP_HOR_RAWMAX] == 1 && + g_inspt_crtra_flag[IDX_GAP_HOR_RAWMIN] == 1) && + (g_inspt_crtra_flag[IDX_GAP_VER_RAWMAX] == 1 && + g_inspt_crtra_flag[IDX_GAP_VER_RAWMIN] == 1)) { + /*6. GAP Test*/ + I("[MP_GAP_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_GAPTEST_RAW, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("6. MP_GAP_TEST_RAW: End %d\n\n\n", ret); + } else { + I("Now %s : HOR flag=%d\n", + g_hx_inspt_crtra_name[IDX_GAP_HOR_RAWMAX], + g_inspt_crtra_flag[IDX_GAP_HOR_RAWMAX]); + I("Now %s : HOR flag=%d\n", + g_hx_inspt_crtra_name[IDX_GAP_HOR_RAWMIN], + g_inspt_crtra_flag[IDX_GAP_HOR_RAWMIN]); + I("Now %s : VERTICAL flag=%d\n", + g_hx_inspt_crtra_name[IDX_GAP_VER_RAWMAX], + g_inspt_crtra_flag[IDX_GAP_VER_RAWMAX]); + I("Now %s : VERTICAL flag=%d\n", + g_hx_inspt_crtra_name[IDX_GAP_VER_RAWMIN], + g_inspt_crtra_flag[IDX_GAP_VER_RAWMIN]); + } + + if (g_inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MIN] == 1 && + g_inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MAX] == 1) { + /*7. ACT_IDLE RAWDATA*/ + I("[MP_ACT_IDLE_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_ACT_IDLE_RAWDATA, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("7. MP_ACT_IDLE_TEST_RAW: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_ACT_IDLE_RAWDATA_MAX], + g_inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MAX]); + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_ACT_IDLE_RAWDATA_MIN], + g_inspt_crtra_flag[IDX_ACT_IDLE_RAWDATA_MIN]); + } + + if (g_inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MIN] == 1 && + g_inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MAX] == 1) { + /*8. ACT_IDLE NOISE*/ + I("[MP_ACT_IDLE_TEST_NOISE]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_ACT_IDLE_NOISE, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("8. MP_ACT_IDLE_TEST_NOISE: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_ACT_IDLE_NOISE_MAX], + g_inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MAX]); + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_ACT_IDLE_NOISE_MIN], + g_inspt_crtra_flag[IDX_ACT_IDLE_NOISE_MIN]); + } + + /* check press power key or not for LPWUG test item*/ + if ((g_inspt_crtra_flag[IDX_LPWUG_NOISE_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_NOISE_MIN] == 1) || + (g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MIN] == 1) || + (g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MIN] == 1) || + (g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MIN] == 1) + ) + himax_press_powerkey(); + +/* Workaround delete */ +#if 0 + /* Wait suspend done */ + while (private_ts->suspend_resume_done != 1) + usleep_range(1000, 1001); + + private_ts->suspend_resume_done = 0; +#endif + + if (g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MIN] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MAX] == 1) { + /*9. LPWUG RAWDATA*/ + I("[MP_LPWUG_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_LPWUG_RAWDATA, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("9. MP_LPWUG_TEST_RAW: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_RAWDATA_MIN], + g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MIN]); + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_RAWDATA_MAX], + g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MAX]); + } + + if (g_inspt_crtra_flag[IDX_LPWUG_NOISE_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_NOISE_MIN] == 1) { + /*10. LPWUG NOISE*/ + I("[MP_LPWUG_TEST_NOISE]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_LPWUG_NOISE, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("10. MP_LPWUG_TEST_NOISE: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_NOISE_MAX], + g_inspt_crtra_flag[IDX_LPWUG_NOISE_MAX]); + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_NOISE_MIN], + g_inspt_crtra_flag[IDX_LPWUG_NOISE_MIN]); + } + + if (g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MIN] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MAX] == 1) { + /*11. LPWUG IDLE RAWDATA*/ + I("[MP_LPWUG_IDLE_TEST_RAW]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("11. MP_LPWUG_IDLE_TEST_RAW: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_IDLE_RAWDATA_MIN], + g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MIN]); + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_IDLE_RAWDATA_MAX], + g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MAX]); + } + + if (g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MIN] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MAX] == 1) { + /*12. LPWUG IDLE RAWDATA*/ + I("[MP_LPWUG_IDLE_TEST_NOISE]\n"); + ret |= mpTestFunc(HIMAX_INSPECTION_LPWUG_IDLE_NOISE, + (ic_data->HX_TX_NUM*ic_data->HX_RX_NUM) + ic_data->HX_TX_NUM + + ic_data->HX_RX_NUM); + I("12. MP_LPWUG_IDLE_TEST_NOISE: End %d\n\n\n", ret); + } else { + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_IDLE_NOISE_MIN], + g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MIN]); + I("Now %s :flag=%d\n", + g_hx_inspt_crtra_name[IDX_LPWUG_IDLE_NOISE_MAX], + g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MAX]); + } + + /* check press power key or not for LPWUG test item*/ + if ((g_inspt_crtra_flag[IDX_LPWUG_NOISE_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_NOISE_MIN] == 1) || + (g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_RAWDATA_MIN] == 1) || + (g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_IDLE_NOISE_MIN] == 1) || + (g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MAX] == 1 && + g_inspt_crtra_flag[IDX_LPWUG_IDLE_RAWDATA_MIN] == 1) + ) + himax_press_powerkey(); + + hx_test_data_pop_out(g_rslt_data, g_file_path); + +/* Workaround delete */ +#if 0 + /* Wait resume done */ + while (private_ts->suspend_resume_done != 1) + usleep_range(1000, 1001); +#endif + +#if defined(HIMAX_V2_MULTI_BIN)||defined(HX_CODE_OVERLAY) + private_ts->in_self_test = 0; + g_core_fp.fp_0f_op_file_dirly(normalfw); + hx_turn_on_mp_func(0); + /* set N frame back to default value 1*/ + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); + g_core_fp.fp_reload_disable(0); + g_core_fp.fp_sense_on(0); +#else + g_core_fp.fp_sense_off(true); + hx_turn_on_mp_func(0); + /*himax_set_N_frame(1, HIMAX_INSPECTION_WEIGHT_NOISE);*/ + /* set N frame back to default value 1*/ + g_core_fp.fp_register_write(tmp_addr, 4, tmp_data, 0); +#ifndef HX_ZERO_FLASH + if (g_core_fp.fp_reload_disable != NULL) + g_core_fp.fp_reload_disable(0); +#endif + g_core_fp.fp_sense_on(0); +#endif + +END_FUNC: + himax_self_test_data_deinit(); + + + I("running status = %d\n", ret); + + /*if (ret != 0)*/ + /*ret = 1;*/ + + I("%s:OUT\n", __func__); + return ret; +} + +void himax_inspect_data_clear(void) +{ + if (!g_rslt_data) { + kfree(g_rslt_data); + g_rslt_data = NULL; + } +} + +void himax_inspection_init(void) +{ + I("%s: enter, %d\n", __func__, __LINE__); + + g_core_fp.fp_chip_self_test = himax_chip_self_test; +} diff --git a/drivers/input/touchscreen/himax_v3_mmi/himax_inspection.h b/drivers/input/touchscreen/himax_v3_mmi/himax_inspection.h new file mode 100644 index 000000000000..6b0f5dbfb46d --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/himax_inspection.h @@ -0,0 +1,211 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for inspection functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_platform.h" +#include "himax_common.h" +#include "himax_ic_core.h" + +/*#define HX_GAP_TEST*/ +/*#define HX_INSPECT_LPWUG_TEST*/ +/*#define HX_ACT_IDLE_TEST*/ + +#define HX_RSLT_OUT_PATH "/sdcard/" +#define HX_RSLT_OUT_FILE "hx_test_result.txt" +#define PI(x...) pr_info(x) + +#ifdef HX_ESD_RECOVERY + extern u8 HX_ESD_RESET_ACTIVATE; +#endif + +#define BS_RAWDATA 10 +#define BS_NOISE 10 +#define BS_OPENSHORT 0 +#define BS_LPWUG 1 +#define BS_LPWUG_dile 1 +#define BS_ACT_IDLE 1 + +/* skip notch & dummy */ +#define SKIP_NOTCH_START 5 +#define SKIP_NOTCH_END 10 +/* TX+SKIP_NOTCH_START */ +#define SKIP_DUMMY_START 23 +/* TX+SKIP_NOTCH_END*/ +#define SKIP_DUMMY_END 28 + + +#define NOISEFRAME (BS_NOISE+1) +#define NORMAL_IDLE_RAWDATA_NOISEFRAME 10 +#define LPWUG_RAWDATAFRAME 1 +#define LPWUG_NOISEFRAME 1 +#define LPWUG_IDLE_RAWDATAFRAME 1 +#define LPWUG_IDLE_NOISEFRAME 1 + +#define OTHERSFRAME 2 + +#define UNIFMAX 500 + + +/*Himax MP Password*/ +#define PWD_OPEN_START 0x77 +#define PWD_OPEN_END 0x88 +#define PWD_SHORT_START 0x11 +#define PWD_SHORT_END 0x33 +#define PWD_RAWDATA_START 0x00 +#define PWD_RAWDATA_END 0x99 +#define PWD_NOISE_START 0x00 +#define PWD_NOISE_END 0x99 +#define PWD_SORTING_START 0xAA +#define PWD_SORTING_END 0xCC + + +#define PWD_ACT_IDLE_START 0x22 +#define PWD_ACT_IDLE_END 0x44 + + + +#define PWD_LPWUG_START 0x55 +#define PWD_LPWUG_END 0x66 + +#define PWD_LPWUG_IDLE_START 0x50 +#define PWD_LPWUG_IDLE_END 0x60 + + +/*Himax DataType*/ +#define DATA_SORTING 0x0A +#define DATA_OPEN 0x0B +#define DATA_MICRO_OPEN 0x0C +#define DATA_SHORT 0x0A +#define DATA_RAWDATA 0x0A +#define DATA_NOISE 0x0F +#define DATA_BACK_NORMAL 0x00 +#define DATA_LPWUG_RAWDATA 0x0C +#define DATA_LPWUG_NOISE 0x0F +#define DATA_ACT_IDLE_RAWDATA 0x0A +#define DATA_ACT_IDLE_NOISE 0x0F +#define DATA_LPWUG_IDLE_RAWDATA 0x0A +#define DATA_LPWUG_IDLE_NOISE 0x0F + +/*Himax Data Ready Password*/ +#define Data_PWD0 0xA5 +#define Data_PWD1 0x5A + +/* ASCII format */ +#define ASCII_LF (0x0A) +#define ASCII_CR (0x0D) +#define ASCII_COMMA (0x2C) +#define ASCII_ZERO (0x30) +#define CHAR_EL '\0' +#define CHAR_NL '\n' +#define ACSII_SPACE (0x20) +/* INSOECTION Setting */ +/*extern int HX_CRITERIA_ITEM;*/ +/*extern int HX_CRITERIA_SIZE;*/ +/*extern char *g_file_path;*/ +/*extern char *g_rslt_data;*/ +void himax_inspection_init(void); +extern int *g_inspt_crtra_flag; + +enum THP_INSPECTION_ENUM { + HIMAX_INSPECTION_OPEN, + HIMAX_INSPECTION_MICRO_OPEN, + HIMAX_INSPECTION_SHORT, + HIMAX_INSPECTION_RAWDATA, + HIMAX_INSPECTION_BPN_RAWDATA, + HIMAX_INSPECTION_WEIGHT_NOISE, + HIMAX_INSPECTION_ABS_NOISE, + HIMAX_INSPECTION_SORTING, + HIMAX_INSPECTION_BACK_NORMAL, + + HIMAX_INSPECTION_GAPTEST_RAW, + + HIMAX_INSPECTION_ACT_IDLE_RAWDATA, + HIMAX_INSPECTION_ACT_IDLE_NOISE, + + HIMAX_INSPECTION_LPWUG_RAWDATA, + HIMAX_INSPECTION_LPWUG_NOISE, + HIMAX_INSPECTION_LPWUG_IDLE_RAWDATA, + HIMAX_INSPECTION_LPWUG_IDLE_NOISE, + +}; + + +enum HX_CRITERIA_ENUM { + IDX_RAWMIN = 0, + IDX_RAWMAX, + IDX_BPN_RAWMIN, + IDX_BPN_RAWMAX, + IDX_SHORTMIN, + IDX_SHORTMAX, + IDX_OPENMIN, + IDX_OPENMAX, + IDX_M_OPENMIN, + IDX_M_OPENMAX, + IDX_WT_NOISEMIN, + IDX_WT_NOISEMAX, + IDX_ABS_NOISEMIN, + IDX_ABS_NOISEMAX, + IDX_SORTMIN, + IDX_SORTMAX, + + IDX_GAP_HOR_RAWMAX, + IDX_GAP_HOR_RAWMIN, + IDX_GAP_VER_RAWMAX, + IDX_GAP_VER_RAWMIN, + + IDX_ACT_IDLE_NOISE_MIN, + IDX_ACT_IDLE_NOISE_MAX, + IDX_ACT_IDLE_RAWDATA_MIN, + IDX_ACT_IDLE_RAWDATA_MAX, + + IDX_LPWUG_NOISE_MIN, + IDX_LPWUG_NOISE_MAX, + IDX_LPWUG_RAWDATA_MIN, + IDX_LPWUG_RAWDATA_MAX, + + IDX_LPWUG_IDLE_NOISE_MIN, + IDX_LPWUG_IDLE_NOISE_MAX, + IDX_LPWUG_IDLE_RAWDATA_MIN, + IDX_LPWUG_IDLE_RAWDATA_MAX, + +}; + +/* Error code of Inspection */ +enum HX_INSPECT_ERR_ENUM { + HX_INSPECT_OK = 0x00000000, /* OK */ + HX_INSPECT_EFILE = 0x00000001, /*Criteria file error*/ + HX_INSPECT_ERAW = 0x00000002, /* Raw data error */ + HX_INSPECT_WT_ENOISE = 0x00000004, /* Noise error */ + HX_INSPECT_ABS_ENOISE = 0x00000008, /* Noise error */ + HX_INSPECT_EOPEN = 0x00000010, /* Sensor open error */ + HX_INSPECT_EMOPEN = 0x00000020, /* Sensor micro open error */ + HX_INSPECT_ESHORT = 0x00000040, /* Sensor short error */ + + HX_INSPECT_EGAP_RAW = 0x00000080, /* Raw Data GAP */ + + HX_INSPECT_EACT_IDLE_RAW = 0x00000100, /* ACT_IDLE RAW ERROR */ + HX_INSPECT_EACT_IDLE_NOISE = 0x00000200, /* ACT_IDLE NOISE ERROR */ + + HX_INSPECT_ELPWUG_RAW = 0x00000400, /* LPWUG RAW ERROR */ + HX_INSPECT_ELPWUG_NOISE = 0x00000800, /* LPWUG NOISE ERROR */ + HX_INSPECT_ELPWUG_IDLE_RAW = 0x00001000, /* LPWUG IDLE RAW ERROR */ + HX_INSPECT_ELPWUG_IDLE_NOISE = 0x00002000, /* LPWUG IDLE NOISE ERROR */ + + HX_INSPECT_EGETRAW = 0x00004000, /* Get raw data errors */ + HX_INSPECT_MEMALLCTFAIL = 0x00008000, /* Get raw data errors */ + /*HX_INSPECT_ESPI = , */ /*SPI communication error,no use*/ +}; + +extern void himax_inspect_data_clear(void); +extern char *hx_self_test_file_name; diff --git a/drivers/input/touchscreen/himax_v3_mmi/himax_modular_table.h b/drivers/input/touchscreen/himax_v3_mmi/himax_modular_table.h new file mode 100644 index 000000000000..180d823d581f --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/himax_modular_table.h @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for modularize functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef __HIMAX_MODULAR_TABLE_H__ +#define __HIMAX_MODULAR_TABLE_H__ + +#include "himax_ic_core.h" + +#define TO_STR(VAR) #VAR +#define DECLARE(sym) struct himax_chip_entry sym; \ + EXPORT_SYMBOL(sym) + +static const char * const himax_ksym_lookup[] = { + #if defined(HX_MOD_KSYM_HX852xG) + TO_STR(HX_MOD_KSYM_HX852xG), + #endif + #if defined(HX_MOD_KSYM_HX852xH) + TO_STR(HX_MOD_KSYM_HX852xH), + #endif + #if defined(HX_MOD_KSYM_HX83102) + TO_STR(HX_MOD_KSYM_HX83102), + #endif + #if defined(HX_MOD_KSYM_HX83103) + TO_STR(HX_MOD_KSYM_HX83103), + #endif + #if defined(HX_MOD_KSYM_HX83106) + TO_STR(HX_MOD_KSYM_HX83106), + #endif + #if defined(HX_MOD_KSYM_HX83111) + TO_STR(HX_MOD_KSYM_HX83111), + #endif + #if defined(HX_MOD_KSYM_HX83112) + TO_STR(HX_MOD_KSYM_HX83112), + #endif + #if defined(HX_MOD_KSYM_HX83113) + TO_STR(HX_MOD_KSYM_HX83113), + #endif + #if defined(HX_MOD_KSYM_HX83191) + TO_STR(HX_MOD_KSYM_HX83191), + #endif + NULL +}; + +static struct himax_chip_entry *get_chip_entry_by_index(int32_t idx) +{ + return (void *)kallsyms_lookup_name(himax_ksym_lookup[idx]); +} + +enum modular_table { + MODULE_NOT_FOUND = -1, + MODULE_FOUND, + MODULE_EMPTY, +}; + +/* + * Return 1 when specified entry is empty, 0 when not, -1 when index error + */ +static int32_t isEmpty(int32_t idx) +{ + int32_t size = sizeof(himax_ksym_lookup) / sizeof(char *); + struct himax_chip_entry *entry; + + if (idx < 0 || idx >= size) + return MODULE_NOT_FOUND; + + entry = get_chip_entry_by_index(idx); + if (entry) + return (entry->core_chip_dt == NULL)?MODULE_EMPTY:MODULE_FOUND; + + return MODULE_NOT_FOUND; +} + +/* + * Search for created entry, if not existed, return 1st + * Return index of himax_ksym_lookup + */ +static int32_t himax_get_ksym_idx(void) +{ + int32_t i, first = -1; + int32_t size = sizeof(himax_ksym_lookup) / sizeof(char *); + struct himax_chip_entry *entry; + + I("%s: symtable size: %d\n", __func__, size); + for (i = 0; i < size; i++) { + if (himax_ksym_lookup[i] == NULL) + break; + + I("%s: %s\n", __func__, himax_ksym_lookup[i]); + entry = get_chip_entry_by_index(i); + if (entry) { + if (first < 0) + first = i; + if (isEmpty(i) == 0) + return i; + } + } + if (first >= 0) + return first; + /*incorrect use state, means no ic defined*/ + return MODULE_NOT_FOUND; +} + +#endif diff --git a/drivers/input/touchscreen/himax_v3_mmi/himax_platform.h b/drivers/input/touchscreen/himax_v3_mmi/himax_platform.h new file mode 100644 index 000000000000..924b4c8de25f --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/himax_platform.h @@ -0,0 +1,5 @@ +#ifdef __HIMAX_PLATFORM_SPI__ +#include "himax_platform_QCT_SPI.h" +#else +#include "himax_platform_QCT_I2C.h" +#endif diff --git a/drivers/input/touchscreen/himax_v3_mmi/himax_platform_QCT_I2C.c b/drivers/input/touchscreen/himax_v3_mmi/himax_platform_QCT_I2C.c new file mode 100644 index 000000000000..316e6914d2f8 --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/himax_platform_QCT_I2C.c @@ -0,0 +1,972 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for QCT platform + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_platform.h" +#include "himax_common.h" + +int i2c_error_count; +int irq_enable_count; +EXPORT_SYMBOL(irq_enable_count); + +int himax_dev_set(struct himax_ts_data *ts) +{ + int ret = 0; + + + ts->input_dev = input_allocate_device(); + + if (ts->input_dev == NULL) { + ret = -ENOMEM; + E("%s: Failed to allocate input device-input_dev\n", __func__); + return ret; + } + + ts->input_dev->name = "himax-touchscreen"; +#if defined(HX_PEN_FUNC_EN) + ts->hx_pen_dev = input_allocate_device(); + + if (ts->hx_pen_dev == NULL) { + ret = -ENOMEM; + E("%s: Failed to allocate input device-hx_pen_dev\n", __func__); + return ret; + } + + ts->hx_pen_dev->name = "himax-pen"; +#endif + return ret; +} +int himax_input_register_device(struct input_dev *input_dev) +{ + return input_register_device(input_dev); +} + +#if defined(HX_PLATFOME_DEFINE_KEY) +void himax_platform_key(void) +{ + I("Nothing to be done! Plz cancel it!\n"); +} +#endif + +void himax_vk_parser(struct device_node *dt, + struct himax_i2c_platform_data *pdata) +{ + u32 data = 0; + uint8_t cnt = 0, i = 0; + uint32_t coords[4] = {0}; + struct device_node *node, *pp = NULL; + struct himax_virtual_key *vk; + + + node = of_parse_phandle(dt, "virtualkey", 0); + + if (node == NULL) { + I(" DT-No vk info in DT\n"); + } else { + while ((pp = of_get_next_child(node, pp))) + cnt++; + + if (!cnt) + return; + + vk = kcalloc(cnt, sizeof(struct himax_virtual_key), GFP_KERNEL); + pp = NULL; + + while ((pp = of_get_next_child(node, pp))) { + if (of_property_read_u32(pp, "idx", &data) == 0) + vk[i].index = data; + + if (of_property_read_u32_array(pp, "range", coords, 4) == 0) { + vk[i].x_range_min = coords[0], vk[i].x_range_max = coords[1]; + vk[i].y_range_min = coords[2], vk[i].y_range_max = coords[3]; + } else { + I(" range faile\n"); + } + + i++; + } + + pdata->virtual_key = vk; + + for (i = 0; i < cnt; i++) + I(" vk[%d] idx:%d x_min:%d, y_max:%d\n", i, pdata->virtual_key[i].index, + pdata->virtual_key[i].x_range_min, pdata->virtual_key[i].y_range_max); + } +} + +int himax_parse_dt(struct himax_ts_data *ts, + struct himax_i2c_platform_data *pdata) +{ + int rc, coords_size = 0; + uint32_t coords[4] = {0}; + struct property *prop; + struct device_node *dt = private_ts->client->dev.of_node; + u32 data = 0; + + + prop = of_find_property(dt, "himax,panel-coords", NULL); + + if (prop) { + coords_size = prop->length / sizeof(u32); + + if (coords_size != 4) + D(" %s:Invalid panel coords size %d\n", __func__, coords_size); + } + + if (of_property_read_u32_array(dt, "himax,panel-coords", coords, coords_size) == 0) { + pdata->abs_x_min = coords[0], pdata->abs_x_max = (coords[1] - 1); + pdata->abs_y_min = coords[2], pdata->abs_y_max = (coords[3] - 1); + I(" DT-%s:panel-coords = %d, %d, %d, %d\n", __func__, pdata->abs_x_min, + pdata->abs_x_max, pdata->abs_y_min, pdata->abs_y_max); + } + + prop = of_find_property(dt, "himax,display-coords", NULL); + + if (prop) { + coords_size = prop->length / sizeof(u32); + + if (coords_size != 4) + D(" %s:Invalid display coords size %d\n", __func__, coords_size); + } + + rc = of_property_read_u32_array(dt, "himax,display-coords", coords, coords_size); + + if (rc && (rc != -EINVAL)) { + D(" %s:Fail to read display-coords %d\n", __func__, rc); + return rc; + } + + pdata->screenWidth = coords[1]; + pdata->screenHeight = coords[3]; + I(" DT-%s:display-coords = (%d, %d)\n", __func__, pdata->screenWidth, + pdata->screenHeight); + pdata->gpio_irq = of_get_named_gpio(dt, "himax,irq-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_irq)) + I(" DT:gpio_irq value is not valid\n"); + +#ifdef HX_SHARP_ENABLE_PON + pdata->gpio_reset = 262; +#else + pdata->gpio_reset = of_get_named_gpio(dt, "himax,rst-gpio", 0); +#endif + + if (!gpio_is_valid(pdata->gpio_reset)) + I(" DT:gpio_rst value is not valid\n"); + +#ifdef HX_SHARP_ENABLE_PON + pdata->gpio_pon = of_get_named_gpio(dt, "himax,pon-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_pon)) + I(" DT:gpio_pon value is not valid\n"); + + + I(" DT:pdata->gpio_pon=%d\n", pdata->gpio_pon); + +#endif + + pdata->gpio_3v3_en = of_get_named_gpio(dt, "himax,3v3-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_3v3_en)) + I(" DT:gpio_3v3_en value is not valid\n"); + + I(" DT:gpio_irq=%d, gpio_rst=%d, gpio_3v3_en=%d\n", pdata->gpio_irq, pdata->gpio_reset, pdata->gpio_3v3_en); + + if (of_property_read_u32(dt, "report_type", &data) == 0) { + pdata->protocol_type = data; + I(" DT:protocol_type=%d\n", pdata->protocol_type); + } + + himax_vk_parser(dt, pdata); + return 0; +} +EXPORT_SYMBOL(himax_parse_dt); + +int himax_bus_read(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry) +{ + int retry; + struct i2c_client *client = private_ts->client; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &command, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + } + }; + mutex_lock(&private_ts->rw_lock); + + for (retry = 0; retry < toRetry; retry++) { + if (i2c_transfer(client->adapter, msg, 2) == 2) + break; + + /*msleep(20);*/ + } + + if (retry == toRetry) { + E("%s: i2c_read_block retry over %d\n", + __func__, toRetry); + i2c_error_count = toRetry; + mutex_unlock(&private_ts->rw_lock); + return -EIO; + } + + mutex_unlock(&private_ts->rw_lock); + return 0; +} +EXPORT_SYMBOL(himax_bus_read); + +int himax_bus_write(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry) +{ + int retry/*, loop_i*/; + uint8_t buf[length + 1]; + struct i2c_client *client = private_ts->client; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = length + 1, + .buf = buf, + } + }; + + + mutex_lock(&private_ts->rw_lock); + buf[0] = command; + memcpy(buf + 1, data, length); + + for (retry = 0; retry < toRetry; retry++) { + if (i2c_transfer(client->adapter, msg, 1) == 1) + break; + + /*msleep(20);*/ + } + + if (retry == toRetry) { + E("%s: i2c_write_block retry over %d\n", + __func__, toRetry); + i2c_error_count = toRetry; + mutex_unlock(&private_ts->rw_lock); + return -EIO; + } + + mutex_unlock(&private_ts->rw_lock); + return 0; +} +EXPORT_SYMBOL(himax_bus_write); + +int himax_bus_write_command(uint8_t command, uint8_t toRetry) +{ + return himax_bus_write(command, NULL, 0, toRetry); +} + +int himax_bus_master_write(uint8_t *data, uint32_t length, uint8_t toRetry) +{ + int retry/*, loop_i*/; + uint8_t buf[length]; + struct i2c_client *client = private_ts->client; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = length, + .buf = buf, + } + }; + + + mutex_lock(&private_ts->rw_lock); + memcpy(buf, data, length); + + for (retry = 0; retry < toRetry; retry++) { + if (i2c_transfer(client->adapter, msg, 1) == 1) + break; + + /*msleep(20);*/ + } + + if (retry == toRetry) { + E("%s: i2c_write_block retry over %d\n", + __func__, toRetry); + i2c_error_count = toRetry; + mutex_unlock(&private_ts->rw_lock); + return -EIO; + } + + mutex_unlock(&private_ts->rw_lock); + return 0; +} +EXPORT_SYMBOL(himax_bus_master_write); + +void himax_int_enable(int enable) +{ + int irqnum = 0; + + + irqnum = private_ts->client->irq; + + if (enable == 1 && irq_enable_count == 0) { + enable_irq(irqnum); + irq_enable_count = 1; + private_ts->irq_enabled = 1; + } else if (enable == 0 && irq_enable_count == 1) { + disable_irq_nosync(irqnum); + irq_enable_count = 0; + private_ts->irq_enabled = 0; + } + + I("irq_enable_count = %d\n", irq_enable_count); +} +EXPORT_SYMBOL(himax_int_enable); + +#ifdef HX_RST_PIN_FUNC +void himax_rst_gpio_set(int pinnum, uint8_t value) +{ + gpio_direction_output(pinnum, value); +} +EXPORT_SYMBOL(himax_rst_gpio_set); +#endif + +uint8_t himax_int_gpio_read(int pinnum) +{ + return gpio_get_value(pinnum); +} + +#if defined(CONFIG_HMX_DB) +static int himax_regulator_configure(struct himax_i2c_platform_data *pdata) +{ + int retval; + struct i2c_client *client = private_ts->client; + + + pdata->vcc_dig = regulator_get(&client->dev, "vdd"); + + if (IS_ERR(pdata->vcc_dig)) { + E("%s: Failed to get regulator vdd\n", + __func__); + retval = PTR_ERR(pdata->vcc_dig); + return retval; + } + + pdata->vcc_ana = regulator_get(&client->dev, "avdd"); + + if (IS_ERR(pdata->vcc_ana)) { + E("%s: Failed to get regulator avdd\n", + __func__); + retval = PTR_ERR(pdata->vcc_ana); + regulator_put(pdata->vcc_dig); + return retval; + } + + return 0; +}; + +static void himax_regulator_deinit(struct himax_i2c_platform_data *pdata) +{ + I("%s: entered.\n", __func__); + + if (!IS_ERR(pdata->vcc_ana)) + regulator_put(pdata->vcc_ana); + + if (!IS_ERR(pdata->vcc_dig)) + regulator_put(pdata->vcc_dig); + + I("%s: regulator put, completed.\n", __func__); +}; + +static int himax_power_on(struct himax_i2c_platform_data *pdata, bool on) +{ + int retval; + + if (on) { + retval = regulator_enable(pdata->vcc_dig); + + if (retval) { + E("%s: Failed to enable regulator vdd\n", + __func__); + return retval; + } + + /*msleep(100);*/ + usleep_range(1000, 1001); + retval = regulator_enable(pdata->vcc_ana); + + if (retval) { + E("%s: Failed to enable regulator avdd\n", + __func__); + regulator_disable(pdata->vcc_dig); + return retval; + } + } else { + regulator_disable(pdata->vcc_dig); + regulator_disable(pdata->vcc_ana); + } + + return 0; +} + +int himax_gpio_power_config(struct himax_i2c_platform_data *pdata) +{ + int error; + struct i2c_client *client = private_ts->client; + + + error = himax_regulator_configure(pdata); + + if (error) { + E("Failed to intialize hardware\n"); + goto err_regulator_not_on; + } + +#ifdef HX_RST_PIN_FUNC + + if (gpio_is_valid(pdata->gpio_reset)) { + /* configure touchscreen reset out gpio */ + error = gpio_request(pdata->gpio_reset, "hmx_reset_gpio"); + + if (error) { + E("unable to request gpio [%d]\n", pdata->gpio_reset); + goto err_regulator_on; + } + + error = gpio_direction_output(pdata->gpio_reset, 0); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_req; + } + } + +#endif + error = himax_power_on(pdata, true); + + if (error) { + E("Failed to power on hardware\n"); + goto err_power_on; + } + + if (gpio_is_valid(pdata->gpio_irq)) { + /* configure touchscreen irq gpio */ + error = gpio_request(pdata->gpio_irq, "hmx_gpio_irq"); + + if (error) { + E("unable to request gpio [%d]\n", + pdata->gpio_irq); + goto err_req_irq_gpio; + } + + error = gpio_direction_input(pdata->gpio_irq); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_irq); + goto err_set_gpio_irq; + } + + client->irq = gpio_to_irq(pdata->gpio_irq); + private_ts->hx_irq = client->irq; + } else { + E("irq gpio not provided\n"); + goto err_req_irq_gpio; + } + + /*msleep(20);*/ + usleep_range(2000, 2001); +#ifdef HX_RST_PIN_FUNC + + if (gpio_is_valid(pdata->gpio_reset)) { + error = gpio_direction_output(pdata->gpio_reset, 1); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_set_gpio_irq; + } + } + +#endif + return 0; +err_set_gpio_irq: + + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); + +err_req_irq_gpio: + himax_power_on(pdata, false); +err_power_on: +#ifdef HX_RST_PIN_FUNC +err_gpio_reset_req: + if (gpio_is_valid(pdata->gpio_reset)) + gpio_free(pdata->gpio_reset); + +err_regulator_on: +#endif + himax_regulator_deinit(pdata); +err_regulator_not_on: + return error; +} + +#else +int himax_gpio_power_config(struct himax_i2c_platform_data *pdata) +{ + int error = 0; + struct i2c_client *client = private_ts->client; +#ifdef HX_RST_PIN_FUNC + + if (pdata->gpio_reset >= 0) { + error = gpio_request(pdata->gpio_reset, "himax-reset"); + + if (error < 0) { + E("%s: request reset pin failed\n", __func__); + goto err_gpio_reset_req; + } + + error = gpio_direction_output(pdata->gpio_reset, 0); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_dir; + } + } + +#endif + +#ifdef HX_SHARP_ENABLE_PON + if (gpio_is_valid(pdata->gpio_pon)) { + error = gpio_request(pdata->gpio_pon, "hmx_pon_gpio"); + + if (error) { + E("unable to request scl gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_req; + } + + error = gpio_direction_output(pdata->gpio_pon, 0); + + I("gpio_pon LOW [%d]\n", pdata->gpio_pon); + + if (error) { + E("unable to set direction for pon gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_dir; + } + } +#endif + + + if (pdata->gpio_3v3_en >= 0) { + error = gpio_request(pdata->gpio_3v3_en, "himax-3v3_en"); + + if (error < 0) { + E("%s: request 3v3_en pin failed\n", __func__); + goto err_gpio_3v3_req; + } + + gpio_direction_output(pdata->gpio_3v3_en, 1); + I("3v3_en set 1 get pin = %d\n", gpio_get_value(pdata->gpio_3v3_en)); + } + + if (gpio_is_valid(pdata->gpio_irq)) { + /* configure touchscreen irq gpio */ + error = gpio_request(pdata->gpio_irq, "himax_gpio_irq"); + + if (error) { + E("unable to request gpio [%d]\n", pdata->gpio_irq); + goto err_gpio_irq_req; + } + + error = gpio_direction_input(pdata->gpio_irq); + + if (error) { + E("unable to set direction for gpio [%d]\n", pdata->gpio_irq); + goto err_gpio_irq_set_input; + } + + client->irq = gpio_to_irq(pdata->gpio_irq); + private_ts->hx_irq = client->irq; + } else { + E("irq gpio not provided\n"); + goto err_gpio_irq_req; + } +#ifdef HX_SHARP_ENABLE_PON + msleep(20); +#else + usleep_range(2000, 2001); +#endif + +#ifdef HX_RST_PIN_FUNC + + if (pdata->gpio_reset >= 0) { + error = gpio_direction_output(pdata->gpio_reset, 1); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_set_high; + } + } +#endif + +#ifdef HX_SHARP_ENABLE_PON + msleep(800); + + if (gpio_is_valid(pdata->gpio_pon)) { + + error = gpio_direction_output(pdata->gpio_pon, 1); + + I("gpio_pon HIGH [%d]\n", pdata->gpio_pon); + + if (error) { + E("gpio_pon unable to set direction for gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_set_high; + } + } +#endif + return error; + +#ifdef HX_SHARP_ENABLE_PON +err_gpio_pon_set_high: +#endif +#ifdef HX_RST_PIN_FUNC +err_gpio_reset_set_high: +#endif +err_gpio_irq_set_input: + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); +err_gpio_irq_req: + if (pdata->gpio_3v3_en >= 0) + gpio_free(pdata->gpio_3v3_en); +err_gpio_3v3_req: +#ifdef HX_SHARP_ENABLE_PON +err_gpio_pon_dir: + if (gpio_is_valid(pdata->gpio_pon)) + gpio_free(pdata->gpio_pon); +err_gpio_pon_req: +#endif +#ifdef HX_RST_PIN_FUNC +err_gpio_reset_dir: + if (pdata->gpio_reset >= 0) + gpio_free(pdata->gpio_reset); +err_gpio_reset_req: +#endif + return error; +} + +#endif + +void himax_gpio_power_deconfig(struct himax_i2c_platform_data *pdata) +{ + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); + +#ifdef HX_RST_PIN_FUNC + if (gpio_is_valid(pdata->gpio_reset)) + gpio_free(pdata->gpio_reset); +#endif + +#if defined(CONFIG_HMX_DB) + himax_power_on(pdata, false); + himax_regulator_deinit(pdata); +#else + if (pdata->gpio_3v3_en >= 0) + gpio_free(pdata->gpio_3v3_en); + +#ifdef HX_SHARP_ENABLE_PON + if (gpio_is_valid(pdata->gpio_pon)) + gpio_free(pdata->gpio_pon); +#endif + +#endif +} + +static void himax_ts_isr_func(struct himax_ts_data *ts) +{ + himax_ts_work(ts); +} + +irqreturn_t himax_ts_thread(int irq, void *ptr) +{ + himax_ts_isr_func((struct himax_ts_data *)ptr); + + return IRQ_HANDLED; +} + +static void himax_ts_work_func(struct work_struct *work) +{ + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work); + + + himax_ts_work(ts); +} + +int himax_int_register_trigger(void) +{ + int ret = 0; + struct himax_ts_data *ts = private_ts; + struct i2c_client *client = private_ts->client; + + if (ic_data->HX_INT_IS_EDGE) { + I("%s edge triiger falling\n ", __func__); + ret = request_threaded_irq(client->irq, NULL, himax_ts_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->name, ts); + } + + else { + I("%s level trigger low\n ", __func__); + ret = request_threaded_irq(client->irq, NULL, himax_ts_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT, client->name, ts); + } + + return ret; +} + +int himax_int_en_set(void) +{ + int ret = NO_ERR; + + + ret = himax_int_register_trigger(); + return ret; +} + +int himax_ts_register_interrupt(void) +{ + struct himax_ts_data *ts = private_ts; + struct i2c_client *client = private_ts->client; + int ret = 0; + + + ts->irq_enabled = 0; + + /* Work functon */ + if (client->irq && private_ts->hx_irq) {/*INT mode*/ + ts->use_irq = 1; + ret = himax_int_register_trigger(); + + if (ret == 0) { + ts->irq_enabled = 1; + irq_enable_count = 1; + I("%s: irq enabled at gpio: %d\n", __func__, client->irq); +#ifdef HX_SMART_WAKEUP + irq_set_irq_wake(client->irq, 1); +#endif + } else { + ts->use_irq = 0; + E("%s: request_irq failed\n", __func__); + } + } else { + I("%s: client->irq is empty, use polling mode.\n", __func__); + } + + if (!ts->use_irq) {/*if use polling mode need to disable HX_ESD_RECOVERY function*/ + ts->himax_wq = create_singlethread_workqueue("himax_touch"); + INIT_WORK(&ts->work, himax_ts_work_func); + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = himax_ts_timer_func; + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + I("%s: polling mode enabled\n", __func__); + } + + return ret; +} + +int himax_ts_unregister_interrupt(void) +{ + struct himax_ts_data *ts = private_ts; + int ret = 0; + + + I("%s: entered.\n", __func__); + + /* Work functon */ + if (private_ts->hx_irq && ts->use_irq) {/*INT mode*/ +#ifdef HX_SMART_WAKEUP + irq_set_irq_wake(ts->hx_irq, 0); +#endif + free_irq(ts->hx_irq, ts); + I("%s: irq disabled at qpio: %d\n", __func__, private_ts->hx_irq); + } + + if (!ts->use_irq) {/*if use polling mode need to disable HX_ESD_RECOVERY function*/ + hrtimer_cancel(&ts->timer); + cancel_work_sync(&ts->work); + if (ts->himax_wq != NULL) + destroy_workqueue(ts->himax_wq); + I("%s: polling mode destroyed", __func__); + } + + return ret; +} + +static int himax_common_suspend(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + + + I("%s: enter\n", __func__); + himax_chip_common_suspend(ts); + return 0; +} + +static int himax_common_resume(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + + + I("%s: enter\n", __func__); + himax_chip_common_resume(ts); + return 0; +} + +#if defined(CONFIG_FB) +int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct himax_ts_data *ts = + container_of(self, struct himax_ts_data, fb_notif); + + + I(" %s\n", __func__); + + if (evdata && evdata->data && event == FB_EVENT_BLANK && ts && + ts->client) { + blank = evdata->data; + + switch (*blank) { + case FB_BLANK_UNBLANK: + himax_common_resume(&ts->client->dev); + break; + case FB_BLANK_POWERDOWN: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_NORMAL: + himax_common_suspend(&ts->client->dev); + break; + } + } + + return 0; +} +#endif + +int himax_chip_common_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + struct himax_ts_data *ts; + + I("%s:Enter\n", __func__); + + /* Check I2C functionality */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + E("%s: i2c check functionality error\n", __func__); + ret = -ENODEV; + goto err_check_functionality_failed; + } + + ts = kzalloc(sizeof(struct himax_ts_data), GFP_KERNEL); + if (ts == NULL) { + E("%s: allocate himax_ts_data failed\n", __func__); + ret = -ENOMEM; + goto err_alloc_data_failed; + } + + i2c_set_clientdata(client, ts); + ts->client = client; + ts->dev = &client->dev; + mutex_init(&ts->rw_lock); + private_ts = ts; + + ret = himax_chip_common_init(); + +err_alloc_data_failed: +err_check_functionality_failed: + + return ret; +} + +int himax_chip_common_remove(struct i2c_client *client) +{ + if (g_hx_chip_inited) + himax_chip_common_deinit(); + + return 0; +} + +static const struct i2c_device_id himax_common_ts_id[] = { + {HIMAX_common_NAME, 0 }, + {} +}; + +static const struct dev_pm_ops himax_common_pm_ops = { +#if (!defined(CONFIG_FB)) + .suspend = himax_common_suspend, + .resume = himax_common_resume, +#endif +}; + +#ifdef CONFIG_OF +static const struct of_device_id himax_match_table[] = { + {.compatible = "himax,hxcommon" }, + {}, +}; +#else +#define himax_match_table NULL +#endif + +static struct i2c_driver himax_common_driver = { + .id_table = himax_common_ts_id, + .probe = himax_chip_common_probe, + .remove = himax_chip_common_remove, + .driver = { + .name = HIMAX_common_NAME, + .owner = THIS_MODULE, + .of_match_table = himax_match_table, +#ifdef CONFIG_PM + .pm = &himax_common_pm_ops, +#endif + }, +}; + +static int __init himax_common_init(void) +{ + I("Himax common touch panel driver init\n"); + D("Himax check double loading\n"); + if (g_mmi_refcnt++ > 0) { + + I("Himax driver has been loaded! ignoring....\n"); + + return 0; + } + i2c_add_driver(&himax_common_driver); + + return 0; +} + +static void __exit himax_common_exit(void) +{ + i2c_del_driver(&himax_common_driver); +} + +module_init(himax_common_init); +module_exit(himax_common_exit); + +MODULE_DESCRIPTION("Himax_common driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/touchscreen/himax_v3_mmi/himax_platform_QCT_I2C.h b/drivers/input/touchscreen/himax_v3_mmi/himax_platform_QCT_I2C.h new file mode 100644 index 000000000000..f45c28f9e9b8 --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/himax_platform_QCT_I2C.h @@ -0,0 +1,144 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for QCT platform + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef HIMAX_PLATFORM_H +#define HIMAX_PLATFORM_H + +#include "himax_common.h" + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_HMX_DB) + #include +#endif + + +#define HIMAX_I2C_RETRY_TIMES 3 + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG) +#define D(x...) pr_debug("[HXTP] " x) +#define I(x...) pr_info("[HXTP] " x) +#define W(x...) pr_warn("[HXTP][WARNING] " x) +#define E(x...) pr_err("[HXTP][ERROR] " x) +#define DIF(x...) \ +do { \ + if (debug_flag) \ + pr_debug("[HXTP][DEBUG] " x) \ + } while (0) +#else + +#define D(x...) +#define I(x...) +#define W(x...) +#define E(x...) +#define DIF(x...) +#endif + +#if defined(CONFIG_HMX_DB) + + /* Analog voltage @2.7 V */ + #define HX_VTG_MIN_UV 2700000 + #define HX_VTG_MAX_UV 3300000 + #define HX_ACTIVE_LOAD_UA 15000 + #define HX_LPM_LOAD_UA 10 + /* Digital voltage @1.8 V */ + #define HX_VTG_DIG_MIN_UV 1800000 + #define HX_VTG_DIG_MAX_UV 1800000 + #define HX_ACTIVE_LOAD_DIG_UA 10000 + #define HX_LPM_LOAD_DIG_UA 10 + #define HX_I2C_VTG_MIN_UV 1800000 + #define HX_I2C_VTG_MAX_UV 1800000 + #define HX_I2C_LOAD_UA 10000 + #define HX_I2C_LPM_LOAD_UA 10 + +#endif + +#define HIMAX_common_NAME "himax_tp" +#define HIMAX_I2C_ADDR 0x48 +#define INPUT_DEV_NAME "himax-touchscreen" + +struct himax_i2c_platform_data { + int abs_x_min; + int abs_x_max; + int abs_x_fuzz; + int abs_y_min; + int abs_y_max; + int abs_y_fuzz; + int abs_pressure_min; + int abs_pressure_max; + int abs_pressure_fuzz; + int abs_width_min; + int abs_width_max; + int screenWidth; + int screenHeight; + uint8_t fw_version; + uint8_t tw_id; + uint8_t powerOff3V3; + uint8_t cable_config[2]; + uint8_t protocol_type; + int gpio_irq; + int gpio_reset; + int gpio_3v3_en; + int gpio_pon; + int (*power)(int on); + void (*reset)(void); + struct himax_virtual_key *virtual_key; + struct kobject *vk_obj; + struct kobj_attribute *vk2Use; + int hx_config_size; + +#if defined(CONFIG_HMX_DB) + bool i2c_pull_up; + bool digital_pwr_regulator; + int reset_gpio; + u32 reset_gpio_flags; + int irq_gpio; + u32 irq_gpio_flags; + struct regulator *vcc_ana; /* For Dragon Board */ + struct regulator *vcc_dig; /* For Dragon Board */ + struct regulator *vcc_i2c; /* For Dragon Board */ + +#endif + +}; + +extern int irq_enable_count; +extern int himax_bus_read(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +extern int himax_bus_write(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +extern int himax_bus_write_command(uint8_t command, uint8_t toRetry); +extern int himax_bus_master_write(uint8_t *data, uint32_t length, uint8_t toRetry); +extern void himax_int_enable(int enable); +extern int himax_ts_register_interrupt(void); +int himax_ts_unregister_interrupt(void); +extern uint8_t himax_int_gpio_read(int pinnum); +extern int himax_gpio_power_config(struct himax_i2c_platform_data *pdata); +void himax_gpio_power_deconfig(struct himax_i2c_platform_data *pdata); + +#if defined(CONFIG_FB) + extern int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#endif + +extern void himax_ts_work(struct himax_ts_data *ts); +extern enum hrtimer_restart himax_ts_timer_func(struct hrtimer *timer); +extern int himax_chip_common_init(void); +extern void himax_chip_common_deinit(void); + +#endif + diff --git a/drivers/input/touchscreen/himax_v3_mmi/himax_platform_QCT_SPI.c b/drivers/input/touchscreen/himax_v3_mmi/himax_platform_QCT_SPI.c new file mode 100755 index 000000000000..7fc97657fd2f --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/himax_platform_QCT_SPI.c @@ -0,0 +1,1177 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for QCT platform + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_platform.h" +#include "himax_common.h" + +int i2c_error_count; +int irq_enable_count; +EXPORT_SYMBOL(irq_enable_count); +struct spi_device *spi; + +static uint8_t *gBuffer; + +int himax_dev_set(struct himax_ts_data *ts) +{ + int ret = 0; + + ts->input_dev = input_allocate_device(); + + if (ts->input_dev == NULL) { + ret = -ENOMEM; + E("%s: Failed to allocate input device\n", __func__); + return ret; + } + + ts->input_dev->name = "himax-touchscreen"; + return ret; +} +int himax_input_register_device(struct input_dev *input_dev) +{ + return input_register_device(input_dev); +} + +#if defined(HX_PLATFOME_DEFINE_KEY) +void himax_platform_key(void) +{ + I("Nothing to be done! Plz cancel it!\n"); +} +#endif + +void himax_vk_parser(struct device_node *dt, + struct himax_i2c_platform_data *pdata) +{ + u32 data = 0; + uint8_t cnt = 0, i = 0; + uint32_t coords[4] = {0}; + struct device_node *node, *pp = NULL; + struct himax_virtual_key *vk; + + node = of_parse_phandle(dt, "virtualkey", 0); + + if (node == NULL) { + I(" DT-No vk info in DT\n"); + return; + } + while ((pp = of_get_next_child(node, pp))) + cnt++; + + if (!cnt) + return; + + vk = kcalloc(cnt, sizeof(struct himax_virtual_key), GFP_KERNEL); + if (vk == NULL) { + E("%s, vk init fail!\n", __func__); + return; + } + pp = NULL; + + while ((pp = of_get_next_child(node, pp))) { + if (of_property_read_u32(pp, "idx", &data) == 0) + vk[i].index = data; + + if (of_property_read_u32_array(pp, "range", coords, 4) == 0) { + vk[i].x_range_min = coords[0], vk[i].x_range_max = coords[1]; + vk[i].y_range_min = coords[2], vk[i].y_range_max = coords[3]; + } else { + I(" range faile\n"); + } + + i++; + } + + pdata->virtual_key = vk; + + for (i = 0; i < cnt; i++) + I(" vk[%d] idx:%d x_min:%d, y_max:%d\n", i, pdata->virtual_key[i].index, + pdata->virtual_key[i].x_range_min, pdata->virtual_key[i].y_range_max); + kfree(vk); +} + +int himax_parse_dt(struct himax_ts_data *ts, + struct himax_i2c_platform_data *pdata) +{ + int rc, coords_size = 0; + int touch_info_size = 0; + uint32_t coords[4] = {0}; + uint32_t touch_info[8] = {0}; + struct property *prop; + struct device_node *chosen, *dt = ts->dev->of_node; + u32 data = 0; + + prop = of_find_property(dt, "himax,panel-coords", NULL); + + if (prop) { + coords_size = prop->length / sizeof(u32); + + if (coords_size != 4) + D(" %s:Invalid panel coords size %d\n", __func__, coords_size); + } + + if (of_property_read_u32_array(dt, "himax,panel-coords", coords, coords_size) == 0) { + pdata->abs_x_min = coords[0], pdata->abs_x_max = (coords[1] - 1); + pdata->abs_y_min = coords[2], pdata->abs_y_max = (coords[3] - 1); + I(" DT-%s:panel-coords = %d, %d, %d, %d\n", __func__, pdata->abs_x_min, + pdata->abs_x_max, pdata->abs_y_min, pdata->abs_y_max); + } + + prop = of_find_property(dt, "himax,display-coords", NULL); + + if (prop) { + coords_size = prop->length / sizeof(u32); + + if (coords_size != 4) + D(" %s:Invalid display coords size %d\n", __func__, coords_size); + } + + rc = of_property_read_u32_array(dt, "himax,display-coords", coords, coords_size); + + if (rc && (rc != -EINVAL)) { + D(" %s:Fail to read display-coords %d\n", __func__, rc); + return rc; + } + + pdata->screenWidth = coords[1]; + pdata->screenHeight = coords[3]; + I(" DT-%s:display-coords = (%d, %d)\n", __func__, pdata->screenWidth, + pdata->screenHeight); + pdata->gpio_irq = of_get_named_gpio(dt, "himax,irq-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_irq)) + I(" DT:gpio_irq value is not valid\n"); + + prop = of_find_property(dt, "himax,touch_info", NULL); + if (prop) { + touch_info_size = prop->length / sizeof(u32); + if (touch_info_size != 8) { + D(" %s:Invalid touch_info size %d\n", __func__, touch_info_size); + } else { + rc = of_property_read_u32_array(dt, "himax,touch_info", touch_info, touch_info_size); + if (rc && (rc != -EINVAL)) { + D(" %s:Fail to read touch_info %d\n", __func__, rc); + } else { + ic_data->HX_RX_NUM = touch_info[0]; + ic_data->HX_TX_NUM = touch_info[1]; + ic_data->HX_BT_NUM = touch_info[2]; + ic_data->HX_X_RES = touch_info[3]; + ic_data->HX_Y_RES = touch_info[4]; + ic_data->HX_MAX_PT = touch_info[5]; + ic_data->HX_XY_REVERSE = touch_info[6]; + ic_data->HX_INT_IS_EDGE = touch_info[7]; + D(" %s:success to read touch_info from dts \n", __func__); + } + } + } + + I(" DT-%s:touch_info: HX_RX_NUM = %d, HX_TX_NUM = %d, HX_BT_NUM = %d,HX_X_RES = %d,HX_Y_RES = %d, HX_MAX_PT = %d, HX_XY_REVERSE = %d, HX_INT_IS_EDGE = %d\n", __func__, + ic_data->HX_RX_NUM,ic_data->HX_TX_NUM,ic_data->HX_BT_NUM,ic_data->HX_X_RES,ic_data->HX_Y_RES,ic_data->HX_MAX_PT,ic_data->HX_XY_REVERSE,ic_data->HX_INT_IS_EDGE); + + + pdata->gpio_reset = of_get_named_gpio(dt, "himax,rst-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_reset)) + I(" DT:gpio_rst value is not valid\n"); + + pdata->gpio_3v3_en = of_get_named_gpio(dt, "himax,3v3-gpio", 0); + + if (!gpio_is_valid(pdata->gpio_3v3_en)) + I(" DT:gpio_3v3_en value is not valid\n"); + + I(" DT:gpio_irq=%d, gpio_rst=%d, gpio_3v3_en=%d\n", pdata->gpio_irq, pdata->gpio_reset, pdata->gpio_3v3_en); + + if (of_property_read_u32(dt, "report_type", &data) == 0) { + pdata->protocol_type = data; + I(" DT:protocol_type=%d\n", pdata->protocol_type); + } + + if (of_property_read_u32(dt, "himax,def-build-id", &ts->build_id)) { + ts->build_id = 0; + I("himax,build_id undefined.\n"); + } else { + I("himax,build_id=0x%04X\n", ts->build_id); + } + + if (of_property_read_u32(dt, "himax,def-config-id", &ts->config_id)) { + ts->config_id = 0; + I("himax,config_id undefined.\n"); + } else { + I("himax,config_id=0x%04X\n", ts->config_id); + } + + ts->vdd_1v8_always_on = of_property_read_bool(dt, "himax,vdd_1v8_always_on"); + if (ts->vdd_1v8_always_on){ + I(" DT:vdd_1v8_always_on=%d\n", ts->vdd_1v8_always_on); + } + + himax_vk_parser(dt, pdata); + + chosen = of_find_node_by_name(NULL, "chosen"); + if (chosen) { + const char *supplier; + char *s, *d; + int rc; + + rc = of_property_read_string(chosen, "mmi,panel_name", + (const char **)&supplier); + if (rc) { + I("%s: cannot read mmi,panel_name %d\n", + __func__, rc); + } else { + I( "%s: mmi,panel_name %s\n", __func__, supplier); + + /* skip dsi_ part */ + s = (char *)supplier; + d = pdata->panel_supplier; + while (*s != '_') *d++ = *s++; + + I("%s: panel-supplier %s\n", __func__, pdata->panel_supplier); + } + of_node_put(chosen); + } + + return 0; +} +EXPORT_SYMBOL(himax_parse_dt); + +static ssize_t himax_spi_sync(struct himax_ts_data *ts, struct spi_message *message) +{ + int status; + + status = spi_sync(ts->spi, message); + + if (status == 0) { + status = message->status; + if (status == 0) + status = message->actual_length; + } + return status; +} + +static int himax_spi_read(uint8_t *command, uint8_t command_len, uint8_t *data, uint32_t length, uint8_t toRetry) +{ + struct spi_message message; + struct spi_transfer xfer[2]; + int retry; + int error; + + spi_message_init(&message); + memset(xfer, 0, sizeof(xfer)); + + xfer[0].tx_buf = command; + xfer[0].len = command_len; + spi_message_add_tail(&xfer[0], &message); + + xfer[1].rx_buf = data; + xfer[1].len = length; + spi_message_add_tail(&xfer[1], &message); + + for (retry = 0; retry < toRetry; retry++) { + error = spi_sync(private_ts->spi, &message); + if (unlikely(error)) + E("SPI read error: %d\n", error); + else + break; + } + + if (retry == toRetry) { + E("%s: SPI read error retry over %d\n", + __func__, toRetry); + return -EIO; + } + + return 0; +} + +static int himax_spi_write(uint8_t *buf, uint32_t length) +{ +/* + struct spi_transfer t = { + .tx_buf = buf, + .len = length, + }; + struct spi_message m; + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + + return himax_spi_sync(private_ts, &m); +*/ + struct spi_transfer t; + struct spi_message m; + + memset(&t, 0, sizeof(struct spi_transfer)); + t.tx_buf = buf; + t.len = length; + spi_message_init(&m); + spi_message_add_tail(&t, &m); + return himax_spi_sync(private_ts, &m); +} + +int himax_bus_read(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry) +{ + int result = 0; + uint8_t spi_format_buf[3]; + + mutex_lock(&(private_ts->spi_lock)); + spi_format_buf[0] = 0xF3; + spi_format_buf[1] = command; + spi_format_buf[2] = 0x00; + + result = himax_spi_read(&spi_format_buf[0], 3, data, length, toRetry); + mutex_unlock(&(private_ts->spi_lock)); + + return result; +} +EXPORT_SYMBOL(himax_bus_read); + +int himax_bus_write(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry) +{ + uint8_t *spi_format_buf = gBuffer; + int i = 0; + int result = 0; + + memset(spi_format_buf, 0, GBUFFER_SZ * sizeof(uint8_t)); + mutex_lock(&(private_ts->spi_lock)); + spi_format_buf[0] = 0xF2; + spi_format_buf[1] = command; + + for (i = 0; i < length; i++) + spi_format_buf[i + 2] = data[i]; + + result = himax_spi_write(spi_format_buf, length + 2); + mutex_unlock(&(private_ts->spi_lock)); + + return result; +} +EXPORT_SYMBOL(himax_bus_write); + +int himax_bus_write_command(uint8_t command, uint8_t toRetry) +{ + return himax_bus_write(command, NULL, 0, toRetry); +} + +int himax_bus_master_write(uint8_t *data, uint32_t length, uint8_t toRetry) +{ + uint8_t *buf = NULL; + + struct spi_transfer t = { + .tx_buf = buf, + .len = length, + }; + struct spi_message m; + int result = 0; + + buf = kcalloc(length, sizeof(uint8_t), GFP_KERNEL); + if (buf == NULL) { + E("%s, Allocate buf failed!\n", __func__); + return -MEM_ALLOC_FAIL; + } + + mutex_lock(&(private_ts->spi_lock)); + memcpy(buf, data, length); + + spi_message_init(&m); + spi_message_add_tail(&t, &m); + result = himax_spi_sync(private_ts, &m); + mutex_unlock(&(private_ts->spi_lock)); + kfree(buf); + + return result; +} +EXPORT_SYMBOL(himax_bus_master_write); + +void himax_int_enable(int enable) +{ + int irqnum = 0; + + irqnum = private_ts->hx_irq; + if (enable == 1 && irq_enable_count == 0) { + enable_irq(irqnum); + irq_enable_count = 1; + private_ts->irq_enabled = 1; + } else if (enable == 0 && irq_enable_count == 1) { + disable_irq_nosync(irqnum); + irq_enable_count = 0; + private_ts->irq_enabled = 0; + } + + I("irq_enable_count = %d\n", irq_enable_count); +} +EXPORT_SYMBOL(himax_int_enable); + +#ifdef HX_RST_PIN_FUNC +void himax_rst_gpio_set(int pinnum, uint8_t value) +{ + gpio_direction_output(pinnum, value); +} +EXPORT_SYMBOL(himax_rst_gpio_set); +#endif + +uint8_t himax_int_gpio_read(int pinnum) +{ + return gpio_get_value(pinnum); +} + +#if defined(CONFIG_HMX_DB) +static int himax_regulator_configure(struct himax_i2c_platform_data *pdata) +{ + int retval; + /* struct i2c_client *client = private_ts->client; */ + + pdata->vcc_dig = regulator_get(private_ts->dev, "vdd"); + + if (IS_ERR(pdata->vcc_dig)) { + E("%s: Failed to get regulator vdd\n", + __func__); + retval = PTR_ERR(pdata->vcc_dig); + return retval; + } + + pdata->vcc_ana = regulator_get(private_ts->dev, "avdd"); + + if (IS_ERR(pdata->vcc_ana)) { + E("%s: Failed to get regulator avdd\n", + __func__); + retval = PTR_ERR(pdata->vcc_ana); + regulator_put(pdata->vcc_dig); + return retval; + } + + return 0; +}; + +static void himax_regulator_deinit(struct himax_i2c_platform_data *pdata) +{ + I("%s: entered.\n", __func__); + + if (!IS_ERR(pdata->vcc_ana)) + regulator_put(pdata->vcc_ana); + + if (!IS_ERR(pdata->vcc_dig)) + regulator_put(pdata->vcc_dig); + + I("%s: regulator put, completed.\n", __func__); +}; + +static int himax_power_on(struct himax_i2c_platform_data *pdata, bool on) +{ + int retval; + + if (on) { + retval = regulator_enable(pdata->vcc_dig); + + if (retval) { + E("%s: Failed to enable regulator vdd\n", + __func__); + return retval; + } + + /*msleep(100);*/ + usleep_range(1000, 1001); + retval = regulator_enable(pdata->vcc_ana); + + if (retval) { + E("%s: Failed to enable regulator avdd\n", + __func__); + regulator_disable(pdata->vcc_dig); + return retval; + } + } else { + regulator_disable(pdata->vcc_dig); + regulator_disable(pdata->vcc_ana); + } + + return 0; +} + +int himax_gpio_power_config(struct himax_i2c_platform_data *pdata) +{ + int error; + /* struct i2c_client *client = private_ts->client; */ + + error = himax_regulator_configure(pdata); + + if (error) { + E("Failed to intialize hardware\n"); + goto err_regulator_not_on; + } + +#ifdef HX_RST_PIN_FUNC + + if (gpio_is_valid(pdata->gpio_reset)) { + /* configure touchscreen reset out gpio */ + error = gpio_request(pdata->gpio_reset, "hmx_reset_gpio"); + + if (error) { + E("unable to request gpio [%d]\n", pdata->gpio_reset); + goto err_regulator_on; + } + + error = gpio_direction_output(pdata->gpio_reset, 0); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_req; + } + } + +#endif + error = himax_power_on(pdata, true); + + if (error) { + E("Failed to power on hardware\n"); + goto err_power_on; + } + + if (gpio_is_valid(pdata->gpio_irq)) { + /* configure touchscreen irq gpio */ + error = gpio_request(pdata->gpio_irq, "hmx_gpio_irq"); + + if (error) { + E("unable to request gpio [%d]\n", + pdata->gpio_irq); + goto err_req_irq_gpio; + } + + error = gpio_direction_input(pdata->gpio_irq); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_irq); + goto err_set_gpio_irq; + } + + private_ts->hx_irq = gpio_to_irq(pdata->gpio_irq); + } else { + E("irq gpio not provided\n"); + goto err_req_irq_gpio; + } + + /*msleep(20);*/ + usleep_range(2000, 2001); +#ifdef HX_RST_PIN_FUNC + + if (gpio_is_valid(pdata->gpio_reset)) { + error = gpio_direction_output(pdata->gpio_reset, 1); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_set_gpio_irq; + } + } + +#endif + return 0; +err_set_gpio_irq: + + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); + +err_req_irq_gpio: + himax_power_on(pdata, false); +err_power_on: +#ifdef HX_RST_PIN_FUNC +err_gpio_reset_req: + if (gpio_is_valid(pdata->gpio_reset)) + gpio_free(pdata->gpio_reset); + +err_regulator_on: +#endif + himax_regulator_deinit(pdata); +err_regulator_not_on: + return error; +} + +#else +int himax_gpio_power_config(struct himax_i2c_platform_data *pdata) +{ + int error = 0; + /* struct i2c_client *client = private_ts->client; */ +#ifdef HX_RST_PIN_FUNC + + if (pdata->gpio_reset >= 0) { + error = gpio_request(pdata->gpio_reset, "himax-reset"); + + if (error < 0) { + E("%s: request reset pin failed\n", __func__); + goto err_gpio_reset_req; + } + + error = gpio_direction_output(pdata->gpio_reset, 0); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_dir; + } + } + +#endif + +#ifdef HX_SHARP_ENABLE_PON + if (gpio_is_valid(pdata->gpio_pon)) { + error = gpio_request(pdata->gpio_pon, "hmx_pon_gpio"); + + if (error) { + E("unable to request scl gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_req; + } + + error = gpio_direction_output(pdata->gpio_pon, 0); + + I("gpio_pon LOW [%d]\n", pdata->gpio_pon); + + if (error) { + E("unable to set direction for pon gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_dir; + } + } +#endif + + + if (pdata->gpio_3v3_en >= 0) { + error = gpio_request(pdata->gpio_3v3_en, "himax-3v3_en"); + + if (error < 0) { + E("%s: request 3v3_en pin failed\n", __func__); + goto err_gpio_3v3_req; + } + + gpio_direction_output(pdata->gpio_3v3_en, 1); + I("3v3_en set 1 get pin = %d\n", gpio_get_value(pdata->gpio_3v3_en)); + } + + if (gpio_is_valid(pdata->gpio_irq)) { + /* configure touchscreen irq gpio */ + error = gpio_request(pdata->gpio_irq, "himax_gpio_irq"); + + if (error) { + E("unable to request gpio [%d]\n", pdata->gpio_irq); + goto err_gpio_irq_req; + } + + error = gpio_direction_input(pdata->gpio_irq); + + if (error) { + E("unable to set direction for gpio [%d]\n", pdata->gpio_irq); + goto err_gpio_irq_set_input; + } + + private_ts->hx_irq = gpio_to_irq(pdata->gpio_irq); + } else { + E("irq gpio not provided\n"); + goto err_gpio_irq_req; + } +#ifdef HX_SHARP_ENABLE_PON + msleep(20); +#else + usleep_range(2000, 2001); +#endif + +#ifdef HX_RST_PIN_FUNC + + if (pdata->gpio_reset >= 0) { + error = gpio_direction_output(pdata->gpio_reset, 1); + + if (error) { + E("unable to set direction for gpio [%d]\n", + pdata->gpio_reset); + goto err_gpio_reset_set_high; + } + } +#endif + +#ifdef HX_SHARP_ENABLE_PON + msleep(800); + + if (gpio_is_valid(pdata->gpio_pon)) { + + error = gpio_direction_output(pdata->gpio_pon, 1); + + I("gpio_pon HIGH [%d]\n", pdata->gpio_pon); + + if (error) { + E("gpio_pon unable to set direction for gpio [%d]\n", pdata->gpio_pon); + goto err_gpio_pon_set_high; + } + } +#endif + return error; + +#ifdef HX_SHARP_ENABLE_PON +err_gpio_pon_set_high: +#endif +#ifdef HX_RST_PIN_FUNC +err_gpio_reset_set_high: +#endif +err_gpio_irq_set_input: + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); +err_gpio_irq_req: + if (pdata->gpio_3v3_en >= 0) + gpio_free(pdata->gpio_3v3_en); +err_gpio_3v3_req: +#ifdef HX_SHARP_ENABLE_PON +err_gpio_pon_dir: + if (gpio_is_valid(pdata->gpio_pon)) + gpio_free(pdata->gpio_pon); +err_gpio_pon_req: +#endif +#ifdef HX_RST_PIN_FUNC +err_gpio_reset_dir: + if (pdata->gpio_reset >= 0) + gpio_free(pdata->gpio_reset); +err_gpio_reset_req: +#endif + return error; +} + +#endif + +void himax_gpio_power_deconfig(struct himax_i2c_platform_data *pdata) +{ + if (gpio_is_valid(pdata->gpio_irq)) + gpio_free(pdata->gpio_irq); + +#ifdef HX_RST_PIN_FUNC + if (gpio_is_valid(pdata->gpio_reset)) + gpio_free(pdata->gpio_reset); +#endif + +#if defined(CONFIG_HMX_DB) + himax_power_on(pdata, false); + himax_regulator_deinit(pdata); +#else + if (pdata->gpio_3v3_en >= 0) + gpio_free(pdata->gpio_3v3_en); + +#ifdef HX_SHARP_ENABLE_PON + if (gpio_is_valid(pdata->gpio_pon)) + gpio_free(pdata->gpio_pon); +#endif + +#endif +} + +static void himax_ts_isr_func(struct himax_ts_data *ts) +{ + himax_ts_work(ts); +} + +irqreturn_t himax_ts_thread(int irq, void *ptr) +{ + himax_ts_isr_func((struct himax_ts_data *)ptr); + + return IRQ_HANDLED; +} + +static void himax_ts_work_func(struct work_struct *work) +{ + struct himax_ts_data *ts = container_of(work, struct himax_ts_data, work); + + himax_ts_work(ts); +} + +int himax_int_register_trigger(void) +{ + int ret = 0; + struct himax_ts_data *ts = private_ts; + + if (ic_data->HX_INT_IS_EDGE) { + I("%s edge triiger falling\n", __func__); + ret = request_threaded_irq(ts->hx_irq, NULL, himax_ts_thread, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, HIMAX_common_NAME, ts); + } else { + I("%s level trigger low\n", __func__); + ret = request_threaded_irq(ts->hx_irq, NULL, himax_ts_thread, IRQF_TRIGGER_LOW | IRQF_ONESHOT, HIMAX_common_NAME, ts); + } + + return ret; +} + +int himax_int_en_set(void) +{ + int ret = NO_ERR; + + ret = himax_int_register_trigger(); + return ret; +} + +int himax_ts_register_interrupt(void) +{ + struct himax_ts_data *ts = private_ts; + /* struct i2c_client *client = private_ts->client; */ + int ret = 0; + + ts->irq_enabled = 0; + + /* Work functon */ + if (private_ts->hx_irq) {/*INT mode*/ + ts->use_irq = 1; + ret = himax_int_register_trigger(); + + if (ret == 0) { + ts->irq_enabled = 1; + irq_enable_count = 1; + I("%s: irq enabled at gpio: %d\n", __func__, private_ts->hx_irq); +#ifdef HX_SMART_WAKEUP + irq_set_irq_wake(private_ts->hx_irq, 1); +#endif + } else { + ts->use_irq = 0; + E("%s: request_irq failed\n", __func__); + } + } else { + I("%s: private_ts->hx_irq is empty, use polling mode.\n", __func__); + } + + if (!ts->use_irq) {/*if use polling mode need to disable HX_ESD_RECOVERY function*/ + ts->himax_wq = create_singlethread_workqueue("himax_touch"); + INIT_WORK(&ts->work, himax_ts_work_func); + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ts->timer.function = himax_ts_timer_func; + hrtimer_start(&ts->timer, ktime_set(1, 0), HRTIMER_MODE_REL); + I("%s: polling mode enabled\n", __func__); + } + + return ret; +} + +int himax_ts_unregister_interrupt(void) +{ + struct himax_ts_data *ts = private_ts; + int ret = 0; + + I("%s: entered.\n", __func__); + + /* Work functon */ + if (private_ts->hx_irq && ts->use_irq) {/*INT mode*/ +#ifdef HX_SMART_WAKEUP + irq_set_irq_wake(ts->hx_irq, 0); +#endif + free_irq(ts->hx_irq, ts); + I("%s: irq disabled at qpio: %d\n", __func__, private_ts->hx_irq); + } + + if (!ts->use_irq) {/*if use polling mode need to disable HX_ESD_RECOVERY function*/ + hrtimer_cancel(&ts->timer); + cancel_work_sync(&ts->work); + if (ts->himax_wq != NULL) + destroy_workqueue(ts->himax_wq); + I("%s: polling mode destroyed", __func__); + } + + return ret; +} + +static int himax_common_suspend(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + + I("%s: enter\n", __func__); +#ifdef CONFIG_DRM + if (!ts->initialized) + return -ECANCELED; +#endif + himax_chip_common_suspend(ts); + return 0; +} + +#if defined(HX_RESUME_SET_FW) +void himax_resume_thread_func(void) +{ + struct himax_ts_data *ts = private_ts; + I("%s: TP resume from thread\n", __func__); +#if defined(__HIMAX_HX83102D_MOD__) + queue_delayed_work(ts->ts_int_workqueue, + &ts->ts_int_work, msecs_to_jiffies(60)); +#else + queue_work(ts->ts_int_workqueue, &ts->ts_int_work); +#endif + return; +} +#endif +static int himax_common_resume(struct device *dev) +{ + struct himax_ts_data *ts = dev_get_drvdata(dev); + + I("%s: enter\n", __func__); +#ifdef CONFIG_DRM + if (!ts->initialized) { + /* + * wait until device resume for TDDI + * TDDI: Touch and display Driver IC + */ + if (himax_chip_common_init()) + return -ECANCELED; + } +#endif +#if defined(HX_RESUME_SET_FW) + himax_resume_thread_func(); +#else + himax_chip_common_resume(ts); +#endif + return 0; +} + +#if defined(CONFIG_DRM) +struct drm_panel *himax_active_panel; +static int 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)) { + himax_active_panel = panel; + return 0; + } + } + if (node) + E("%s: %s not actived\n", __func__, node->name); + return -ENODEV; +} + +int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct drm_panel_notifier *evdata = data; + int *blank; + struct himax_ts_data *ts = + container_of(self, struct himax_ts_data, fb_notif); + + if (!evdata) + return 0; + + D("DRM %s\n", __func__); + + if (evdata->data && event == DRM_PANEL_EARLY_EVENT_BLANK && ts != NULL && + ts->dev != NULL) { + blank = evdata->data; + switch (*blank) { + case DRM_PANEL_BLANK_POWERDOWN: + if (!ts->initialized) + return -ECANCELED; + himax_common_suspend(ts->dev); +#ifdef HIMAX_PALM_SENSOR_EN + if (ts->palm_detection_enabled) { + I("%s: palm detection is enabled", __func__); + return 1; + } +#endif +#ifdef HIMAX_V2_SENSOR_EN + if (ts->SMWP_enable) { + I("%s: tap detection is enabled", __func__); + return 1; + } +#endif + break; +#if defined(__HIMAX_HX83102D_MOD__) + case DRM_PANEL_BLANK_UNBLANK: + D("DRM_EARLY_EVENT_BLANK resume.\n"); + himax_common_resume(ts->dev); + return 0; +#endif + } + } + + /* Avoid HX83102D is called for early event and event, two times */ +#if !defined(__HIMAX_HX83102D_MOD__) + if (evdata->data && event == DRM_PANEL_EVENT_BLANK && ts != NULL && + ts->dev != NULL) { + blank = evdata->data; + switch (*blank) { + case DRM_PANEL_BLANK_UNBLANK: + himax_common_resume(ts->dev); + break; + } + } +#endif + + return 0; +} +#elif defined(CONFIG_FB) +int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct himax_ts_data *ts = + container_of(self, struct himax_ts_data, fb_notif); + + I(" %s\n", __func__); + + if (evdata && evdata->data && event == FB_EVENT_BLANK && ts != NULL && + ts->dev != NULL) { + blank = evdata->data; + + switch (*blank) { + case FB_BLANK_UNBLANK: + himax_common_resume(ts->dev); + break; + + case FB_BLANK_POWERDOWN: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_NORMAL: + himax_common_suspend(ts->dev); + break; + } + } + + return 0; +} +#endif + +int himax_chip_common_probe(struct spi_device *spi) +{ + struct himax_ts_data *ts; + int ret = 0; + struct device_node *dp; + + I("Enter %s\n", __func__); + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) { + dev_err(&spi->dev, + "%s: Full duplex not supported by host\n", __func__); + return -EIO; + } +#ifdef CONFIG_DRM + dp = spi->dev.of_node; + if (check_dt(dp)) { + return -ENODEV; + } +#endif + gBuffer = kzalloc(sizeof(uint8_t) * PACKET_MAX_SZ, GFP_KERNEL); + if (gBuffer == NULL) { + E("%s: allocate gBuffer failed\n", __func__); + ret = -ENOMEM; + goto err_alloc_gbuffer_failed; + } + + ts = kzalloc(sizeof(struct himax_ts_data), GFP_KERNEL); + if (ts == NULL) { + E("%s: allocate himax_ts_data failed\n", __func__); + ret = -ENOMEM; + goto err_alloc_data_failed; + } + + private_ts = ts; + spi->bits_per_word = 8; + spi->mode = SPI_MODE_3; + spi->chip_select = 0; + + ts->spi = spi; + mutex_init(&(ts->spi_lock)); + ts->dev = &spi->dev; + dev_set_drvdata(&spi->dev, ts); + spi_set_drvdata(spi, ts); + + ts->initialized = false; + ret = himax_chip_common_init(); + if (ret < 0) + goto err_alloc_data_failed; + return 0; + +err_alloc_data_failed: + kfree(gBuffer); +err_alloc_gbuffer_failed: + return ret; +} + +int himax_chip_common_remove(struct spi_device *spi) +{ + struct himax_ts_data *ts = spi_get_drvdata(spi); + + if (g_hx_chip_inited) + himax_chip_common_deinit(); + +#if defined(HX_USB_DETECT_GLOBAL) + if (ts->charger_notif.notifier_call) + power_supply_unreg_notifier(&ts->charger_notif); +#endif + ts->spi = NULL; + /* spin_unlock_irq(&ts->spi_lock); */ + spi_set_drvdata(spi, NULL); + kfree(gBuffer); + + I("%s: completed.\n", __func__); + + return 0; +} + +#if (!defined(CONFIG_FB)) && (!defined(CONFIG_DRM)) +static const struct dev_pm_ops himax_common_pm_ops = { + .suspend = himax_common_suspend, + .resume = himax_common_resume, +}; +#endif + +#ifdef CONFIG_OF +static const struct of_device_id himax_match_table[] = { + {.compatible = "himax,hxcommon" }, + {}, +}; +#else +#define himax_match_table NULL +#endif + +static struct spi_driver himax_common_driver = { + .driver = { + .name = HIMAX_common_NAME, + .owner = THIS_MODULE, + .of_match_table = himax_match_table, + }, + .probe = himax_chip_common_probe, + .remove = himax_chip_common_remove, +}; + +static int __init himax_common_init(void) +{ + I("Himax common touch panel driver init\n"); + D("Himax check double loading\n"); + if (g_mmi_refcnt++ > 0) { + I("Himax driver has been loaded! ignoring....\n"); + return 0; + } + spi_register_driver(&himax_common_driver); + + return 0; +} + +static void __exit himax_common_exit(void) +{ + if (spi) { + spi_unregister_device(spi); + spi = NULL; + } + spi_unregister_driver(&himax_common_driver); +} + +module_init(himax_common_init); +module_exit(himax_common_exit); + +MODULE_DESCRIPTION("Himax_common driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/touchscreen/himax_v3_mmi/himax_platform_QCT_SPI.h b/drivers/input/touchscreen/himax_v3_mmi/himax_platform_QCT_SPI.h new file mode 100755 index 000000000000..459b41e7efc1 --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/himax_platform_QCT_SPI.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for QCT platform + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef HIMAX_PLATFORM_H +#define HIMAX_PLATFORM_H + +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_HMX_DB) + #include +#endif + +#define HIMAX_I2C_RETRY_TIMES 3 + +#if defined(CONFIG_TOUCHSCREEN_HIMAX_DEBUG) +#define D(x...) pr_debug("[HXTP] " x) +#define I(x...) pr_info("[HXTP] " x) +#define W(x...) pr_warn("[HXTP][WARNING] " x) +#define E(x...) pr_err("[HXTP][ERROR] " x) +#define DIF(x...) \ +do { \ + if (debug_flag) \ + pr_debug("[HXTP][DEBUG] " x) \ + } while (0) +#else +#define D(x...) +#define I(x...) +#define W(x...) +#define E(x...) +#define DIF(x...) +#endif + +#if defined(CONFIG_HMX_DB) + /* Analog voltage @2.7 V */ +#define HX_VTG_MIN_UV 2700000 +#define HX_VTG_MAX_UV 3300000 +#define HX_ACTIVE_LOAD_UA 15000 +#define HX_LPM_LOAD_UA 10 +/* Digital voltage @1.8 V */ +#define HX_VTG_DIG_MIN_UV 1800000 +#define HX_VTG_DIG_MAX_UV 1800000 +#define HX_ACTIVE_LOAD_DIG_UA 10000 +#define HX_LPM_LOAD_DIG_UA 10 + +#define HX_I2C_VTG_MIN_UV 1800000 +#define HX_I2C_VTG_MAX_UV 1800000 +#define HX_I2C_LOAD_UA 10000 +#define HX_I2C_LPM_LOAD_UA 10 +#endif + +#define HIMAX_common_NAME "himax_tp" +#define HIMAX_I2C_ADDR 0x48 +#define INPUT_DEV_NAME "himax-touchscreen" +#define PACKET_MAX_SZ 4096 +#define GBUFFER_SZ PACKET_MAX_SZ - 2 + +struct himax_i2c_platform_data { + int abs_x_min; + int abs_x_max; + int abs_x_fuzz; + int abs_y_min; + int abs_y_max; + int abs_y_fuzz; + int abs_pressure_min; + int abs_pressure_max; + int abs_pressure_fuzz; + int abs_width_min; + int abs_width_max; + int screenWidth; + int screenHeight; + uint8_t fw_version; + uint8_t tw_id; + uint8_t powerOff3V3; + uint8_t cable_config[2]; + uint8_t protocol_type; + int gpio_irq; + int gpio_reset; + int gpio_3v3_en; + char panel_supplier[16]; + int (*power)(int on); + void (*reset)(void); + struct himax_virtual_key *virtual_key; + struct kobject *vk_obj; + struct kobj_attribute *vk2Use; + + int hx_config_size; +#if defined(CONFIG_HMX_DB) + bool i2c_pull_up; + bool digital_pwr_regulator; + int reset_gpio; + u32 reset_gpio_flags; + int irq_gpio; + u32 irq_gpio_flags; + + struct regulator *vcc_ana; /* For Dragon Board */ + struct regulator *vcc_dig; /* For Dragon Board */ + struct regulator *vcc_i2c; /* For Dragon Board */ +#endif +}; + +extern struct himax_ic_data *ic_data; +extern struct himax_ts_data *private_ts; +int himax_chip_common_init(void); +void himax_chip_common_deinit(void); + +void himax_ts_work(struct himax_ts_data *ts); +enum hrtimer_restart himax_ts_timer_func(struct hrtimer *timer); +extern int irq_enable_count; +extern int himax_bus_read(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +extern int himax_bus_write(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +extern int himax_bus_write_command(uint8_t command, uint8_t toRetry); +extern int himax_bus_master_write(uint8_t *data, uint32_t length, uint8_t toRetry); +extern void himax_int_enable(int enable); +extern int himax_ts_register_interrupt(void); +int himax_ts_unregister_interrupt(void); +extern uint8_t himax_int_gpio_read(int pinnum); + +extern int himax_gpio_power_config(struct himax_i2c_platform_data *pdata); +void himax_gpio_power_deconfig(struct himax_i2c_platform_data *pdata); + +#if defined(CONFIG_DRM) + extern int drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#elif defined(CONFIG_FB) + extern int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#endif + +#endif diff --git a/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_modular.h b/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_modular.h new file mode 100644 index 000000000000..3e7a43b9ed4e --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_modular.h @@ -0,0 +1,275 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for modularize functions + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#ifndef __HIMAX_MODULAR_H__ +#define __HIMAX_MODULAR_H__ + +#include "himax_modular_table.h" + +static bool (*this_detect_fp)(void); + +static void himax_add_chip_dt(bool (*detect_fp)(void)) +{ + int32_t idx; + struct himax_chip_entry *entry; + + this_detect_fp = detect_fp; + idx = himax_get_ksym_idx(); + if (idx < 0) { + /*TODO: No entry, handle this error*/ + E("%s: no entry exist, please insert ic module first!", __func__); + } else { + entry = (void *)kallsyms_lookup_name(himax_ksym_lookup[idx]); + if (/*!(entry->core_chip_dt)*/isEmpty(idx) == 1) { + entry->core_chip_dt = kcalloc(HX_DRIVER_MAX_IC_NUM, + sizeof(struct himax_chip_detect), GFP_KERNEL); + entry->hx_ic_dt_num = 0; + } + entry->core_chip_dt[entry->hx_ic_dt_num++].fp_chip_detect = detect_fp; + } +} + +static void free_chip_dt_table(void) +{ + int i, j, idx; + struct himax_chip_entry *entry; + + idx = himax_get_ksym_idx(); + if (idx >= 0) { + if (isEmpty(idx) != 0) { + I("%s: no chip registered or entry has been clean up, leave safetly.\n", + __func__); + return; + } + entry = get_chip_entry_by_index(idx); + + for (i = 0; i < entry->hx_ic_dt_num; i++) { + if (entry->core_chip_dt[i].fp_chip_detect == this_detect_fp) { + if (i == (entry->hx_ic_dt_num - 1)) { + entry->core_chip_dt[i].fp_chip_detect = NULL; + entry->hx_ic_dt_num = 0; + } else { + for (j = i; j < entry->hx_ic_dt_num; j++) + entry->core_chip_dt[i].fp_chip_detect = entry->core_chip_dt[j].fp_chip_detect; + + entry->core_chip_dt[j].fp_chip_detect = NULL; + entry->hx_ic_dt_num--; + } + } + } + if (entry->hx_ic_dt_num == 0) { + kfree(entry->core_chip_dt); + entry->core_chip_dt = NULL; + } + } +} + +#define setup_symbol(sym) ({kp_##sym = (void *)kallsyms_lookup_name(#sym); kp_##sym; }) +#define assert_on_symbol(sym) do { \ + if (!setup_symbol(sym)) { \ + E("%s: setup %s failed!\n", __func__, #sym); \ + ret = -1; \ + } \ + } while (0) +#if !defined(__HIMAX_HX852xH_MOD__) && !defined(__HIMAX_HX852xG_MOD__) +static struct fw_operation **kp_pfw_op; +static struct ic_operation **kp_pic_op; +static struct driver_operation **kp_pdriver_op; +#endif +static struct himax_chip_detect **kp_g_core_chip_dt; + +#if defined(HX_ZERO_FLASH) +static struct zf_operation **kp_pzf_op; +static int *kp_G_POWERONOF; +#endif + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) + static char **kp_i_CTPM_firmware_name; +#endif +#endif + +static unsigned char *kp_IC_CHECKSUM; + +#ifdef HX_ESD_RECOVERY +static u8 *kp_HX_ESD_RESET_ACTIVATE; +#endif + +#if defined(HX_ZERO_FLASH) && defined(HX_CODE_OVERLAY) +#if defined(CONFIG_TOUCHSCREEN_HIMAX_INCELL) +extern uint8_t *ovl_idx; +static uint8_t **kp_ovl_idx; +#endif +#endif + +static unsigned long *kp_FW_VER_MAJ_FLASH_ADDR; +static unsigned long *kp_FW_VER_MIN_FLASH_ADDR; +static unsigned long *kp_CFG_VER_MAJ_FLASH_ADDR; +static unsigned long *kp_CFG_VER_MIN_FLASH_ADDR; +static unsigned long *kp_CID_VER_MAJ_FLASH_ADDR; +static unsigned long *kp_CID_VER_MIN_FLASH_ADDR; + +static unsigned long *kp_FW_VER_MAJ_FLASH_LENG; +static unsigned long *kp_FW_VER_MIN_FLASH_LENG; +static unsigned long *kp_CFG_VER_MAJ_FLASH_LENG; +static unsigned long *kp_CFG_VER_MIN_FLASH_LENG; +static unsigned long *kp_CID_VER_MAJ_FLASH_LENG; +static unsigned long *kp_CID_VER_MIN_FLASH_LENG; + +#ifdef HX_AUTO_UPDATE_FW + static int *kp_g_i_FW_VER; + static int *kp_g_i_CFG_VER; + static int *kp_g_i_CID_MAJ; + static int *kp_g_i_CID_MIN; + static unsigned char **kp_i_CTPM_FW; +#endif + +#ifdef HX_TP_PROC_2T2R + static bool *kp_Is_2T2R; +#endif + +#ifdef HX_USB_DETECT_GLOBAL + static void (*kp_himax_cable_detect_func)(bool force_renew); +#endif + +#ifdef HX_RST_PIN_FUNC + static void (*kp_himax_rst_gpio_set)(int pinnum, uint8_t value); +#endif + +static struct himax_ts_data **kp_private_ts; +static struct himax_core_fp *kp_g_core_fp; +static struct himax_ic_data **kp_ic_data; + +#if defined(__HIMAX_HX852xH_MOD__) || defined(__HIMAX_HX852xG_MOD__) +#if defined(__HIMAX_HX852xG_MOD__) +static struct on_driver_operation **kp_on_pdriver_op; +#endif +static void (*kp_himax_mcu_on_cmd_init)(void); +static int (*kp_himax_mcu_on_cmd_struct_init)(void); +static void (*kp_himax_on_parse_assign_cmd)(uint32_t addr, uint8_t *cmd, int len); +#else +static void (*kp_himax_mcu_in_cmd_init)(void); +static int (*kp_himax_mcu_in_cmd_struct_init)(void); +static void (*kp_himax_in_parse_assign_cmd)(uint32_t addr, uint8_t *cmd, int len); +#endif + +static int *kp_irq_enable_count; +static int (*kp_himax_bus_read)(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +static int (*kp_himax_bus_write)(uint8_t command, uint8_t *data, uint32_t length, uint8_t toRetry); +static int (*kp_himax_bus_write_command)(uint8_t command, uint8_t toRetry); +static int (*kp_himax_bus_master_write)(uint8_t *data, uint32_t length, uint8_t toRetry); +static void (*kp_himax_int_enable)(int enable); +static int (*kp_himax_ts_register_interrupt)(void); +static uint8_t (*kp_himax_int_gpio_read)(int pinnum); +static int (*kp_himax_gpio_power_config)(struct himax_i2c_platform_data *pdata); + +static int32_t himax_ic_setup_external_symbols(void) +{ + int32_t ret = 0; + +#if !defined(__HIMAX_HX852xH_MOD__) && !defined(__HIMAX_HX852xG_MOD__) + assert_on_symbol(pfw_op); + assert_on_symbol(pic_op); + assert_on_symbol(pdriver_op); +#endif + assert_on_symbol(g_core_chip_dt); + + assert_on_symbol(private_ts); + assert_on_symbol(g_core_fp); + assert_on_symbol(ic_data); + +#if defined(__HIMAX_HX852xH_MOD__) || defined(__HIMAX_HX852xG_MOD__) +#if defined(__HIMAX_HX852xG_MOD__) + assert_on_symbol(on_pdriver_op); +#endif + assert_on_symbol(himax_mcu_on_cmd_init); + assert_on_symbol(himax_mcu_on_cmd_struct_init); + assert_on_symbol(himax_on_parse_assign_cmd); +#else + assert_on_symbol(himax_mcu_in_cmd_init); + assert_on_symbol(himax_mcu_in_cmd_struct_init); + assert_on_symbol(himax_in_parse_assign_cmd); +#endif + + assert_on_symbol(irq_enable_count); + assert_on_symbol(himax_bus_read); + assert_on_symbol(himax_bus_write); + assert_on_symbol(himax_bus_write_command); + assert_on_symbol(himax_bus_master_write); + assert_on_symbol(himax_int_enable); + assert_on_symbol(himax_ts_register_interrupt); + assert_on_symbol(himax_int_gpio_read); + assert_on_symbol(himax_gpio_power_config); + +#if defined(HX_ZERO_FLASH) + assert_on_symbol(pzf_op); + assert_on_symbol(G_POWERONOF); +#endif + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) + assert_on_symbol(i_CTPM_firmware_name); +#endif +#endif + assert_on_symbol(IC_CHECKSUM); + + assert_on_symbol(FW_VER_MAJ_FLASH_ADDR); + assert_on_symbol(FW_VER_MIN_FLASH_ADDR); + assert_on_symbol(CFG_VER_MAJ_FLASH_ADDR); + assert_on_symbol(CFG_VER_MIN_FLASH_ADDR); + assert_on_symbol(CID_VER_MAJ_FLASH_ADDR); + assert_on_symbol(CID_VER_MIN_FLASH_ADDR); + + assert_on_symbol(FW_VER_MAJ_FLASH_LENG); + assert_on_symbol(FW_VER_MIN_FLASH_LENG); + assert_on_symbol(CFG_VER_MAJ_FLASH_LENG); + assert_on_symbol(CFG_VER_MIN_FLASH_LENG); + assert_on_symbol(CID_VER_MAJ_FLASH_LENG); + assert_on_symbol(CID_VER_MIN_FLASH_LENG); + +#ifdef HX_AUTO_UPDATE_FW + assert_on_symbol(g_i_FW_VER); + assert_on_symbol(g_i_CFG_VER); + assert_on_symbol(g_i_CID_MAJ); + assert_on_symbol(g_i_CID_MIN); + assert_on_symbol(i_CTPM_FW); +#endif + +#ifdef HX_TP_PROC_2T2R + assert_on_symbol(Is_2T2R); +#endif + +#ifdef HX_USB_DETECT_GLOBAL + assert_on_symbol(himax_cable_detect_func); +#endif + +#ifdef HX_RST_PIN_FUNC + assert_on_symbol(himax_rst_gpio_set); +#endif + +#ifdef HX_ESD_RECOVERY + assert_on_symbol(HX_ESD_RESET_ACTIVATE); +#endif + +#if defined(HX_ZERO_FLASH) && defined(HX_CODE_OVERLAY) +#if defined(CONFIG_TOUCHSCREEN_HIMAX_INCELL) + assert_on_symbol(ovl_idx); +#endif +#endif + return ret; +} + + +#endif diff --git a/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_v3_mmi_hx83102d/Android.mk b/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_v3_mmi_hx83102d/Android.mk new file mode 100644 index 000000000000..bd817b74490b --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_v3_mmi_hx83102d/Android.mk @@ -0,0 +1,17 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +HIMAX_MMI_IC_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +HIMAX_MMI_IC_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +KERNEL_CFLAGS += CONFIG_INPUT_HIMAX_V2_MMI_IC_NAME_D=hx83102d + +include $(CLEAR_VARS) +LOCAL_MODULE := himax_v3_mmi_hx83102d.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(HIMAX_MMI_IC_MODULE_PATH) +LOCAL_REQUIRED_MODULES := himax_v3_mmi.ko +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_v3_mmi_hx83102d/Kbuild b/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_v3_mmi_hx83102d/Kbuild new file mode 100644 index 000000000000..336702c6e39e --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_v3_mmi_hx83102d/Kbuild @@ -0,0 +1,20 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_v3_mmi \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_v3_mmi/support_ic \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_v3_mmi_hx83102d +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_DEBUG \ + -DCONFIG_TOUCHSCREEN_HIMAX_INSPECT + +EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_HIMAX_INCELL +EXTRA_CFLAGS += -DCONFIG_HMX_DB + +ifneq ($(filter hx83102d,$(CONFIG_INPUT_HIMAX_V2_MMI_IC_NAME_D)),) + EXTRA_CFLAGS += -D__HIMAX_HX83102D_MOD__ + EXTRA_CFLAGS += -DHX_ZERO_FLASH + EXTRA_CFLAGS += -DHX_CODE_OVERLAY +endif + +obj-m := himax_v3_mmi_hx83102d.o +himax_v3_mmi_hx83102d-objs += himax_ic_HX83102d.o diff --git a/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_v3_mmi_hx83102d/Makefile b/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_v3_mmi_hx83102d/Makefile new file mode 100755 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_v3_mmi_hx83102d/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_v3_mmi_hx83102d/himax_ic_HX83102d.c b/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_v3_mmi_hx83102d/himax_ic_HX83102d.c new file mode 100755 index 000000000000..5f095fdebce3 --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_v3_mmi_hx83102d/himax_ic_HX83102d.c @@ -0,0 +1,1452 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for HX83102 chipset + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_ic_HX83102d.h" +#include "himax_modular.h" + +static void hx83102_chip_init(void) +{ + (*kp_private_ts)->chip_cell_type = CHIP_IS_IN_CELL; + I("%s:IC cell type = %d\n", __func__, (*kp_private_ts)->chip_cell_type); + (*kp_IC_CHECKSUM) = HX_TP_BIN_CHECKSUM_CRC; + /*Himax: Set FW and CFG Flash Address*/ + (*kp_FW_VER_MAJ_FLASH_ADDR) = 49157; /*0x00C005*/ + (*kp_FW_VER_MAJ_FLASH_LENG) = 1; + (*kp_FW_VER_MIN_FLASH_ADDR) = 49158; /*0x00C006*/ + (*kp_FW_VER_MIN_FLASH_LENG) = 1; + (*kp_CFG_VER_MAJ_FLASH_ADDR) = 49408; /*0x00C100*/ + (*kp_CFG_VER_MAJ_FLASH_LENG) = 1; + (*kp_CFG_VER_MIN_FLASH_ADDR) = 49409; /*0x00C101*/ + (*kp_CFG_VER_MIN_FLASH_LENG) = 1; + (*kp_CID_VER_MAJ_FLASH_ADDR) = 49154; /*0x00C002*/ + (*kp_CID_VER_MAJ_FLASH_LENG) = 1; + (*kp_CID_VER_MIN_FLASH_ADDR) = 49155; /*0x00C003*/ + (*kp_CID_VER_MIN_FLASH_LENG) = 1; + /*PANEL_VERSION_ADDR = 49156;*/ /*0x00C004*/ + /*PANEL_VERSION_LENG = 1;*/ +} + +static void hx83102e_chip_init(void) +{ + (*kp_private_ts)->chip_cell_type = CHIP_IS_IN_CELL; + I("%s:IC cell type = %d\n", __func__, (*kp_private_ts)->chip_cell_type); + (*kp_IC_CHECKSUM) = HX_TP_BIN_CHECKSUM_CRC; + /*Himax: Set FW and CFG Flash Address*/ + (*kp_FW_VER_MAJ_FLASH_ADDR) = 59397; /*0x00E805*/ + (*kp_FW_VER_MAJ_FLASH_LENG) = 1; + (*kp_FW_VER_MIN_FLASH_ADDR) = 59398; /*0x00E806*/ + (*kp_FW_VER_MIN_FLASH_LENG) = 1; + (*kp_CFG_VER_MAJ_FLASH_ADDR) = 59648; /*0x00E900*/ + (*kp_CFG_VER_MAJ_FLASH_LENG) = 1; + (*kp_CFG_VER_MIN_FLASH_ADDR) = 59649; /*0x00E901*/ + (*kp_CFG_VER_MIN_FLASH_LENG) = 1; + (*kp_CID_VER_MAJ_FLASH_ADDR) = 59394; /*0x00E802*/ + (*kp_CID_VER_MAJ_FLASH_LENG) = 1; + (*kp_CID_VER_MIN_FLASH_ADDR) = 59395; /*0x00E803*/ + (*kp_CID_VER_MIN_FLASH_LENG) = 1; + /*PANEL_VERSION_ADDR = 49156;*/ /*0x00C004*/ + /*PANEL_VERSION_LENG = 1;*/ +} + +void hx83102_burst_enable(uint8_t auto_add_4_byte) +{ + uint8_t tmp_data[4]; + + tmp_data[0] = 0x31; + if (kp_himax_bus_write(0x13, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } + + tmp_data[0] = (0x10 | auto_add_4_byte); + if (kp_himax_bus_write(0x0D, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return; + } +} + +int hx83102_flash_write_burst(uint8_t *reg_byte, uint8_t *write_data) +{ + uint8_t data_byte[8]; + int i = 0, j = 0; + + for (i = 0; i < 4; i++) + data_byte[i] = reg_byte[i]; + for (j = 4; j < 8; j++) + data_byte[j] = write_data[j-4]; + + if (kp_himax_bus_write(0x00, data_byte, 8, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + return 0; +} + +static int hx83102_register_read(uint8_t *read_addr, int read_length, uint8_t *read_data) +{ + uint8_t tmp_data[4]; + int i = 0; + int address = 0; + + if (read_length > 256) { + E("%s: read len over 256!\n", __func__); + return LENGTH_FAIL; + } + if (read_length > 4) + hx83102_burst_enable(1); + else + hx83102_burst_enable(0); + + address = (read_addr[3] << 24) + (read_addr[2] << 16) + (read_addr[1] << 8) + read_addr[0]; + i = address; + tmp_data[0] = (uint8_t)i; + tmp_data[1] = (uint8_t)(i >> 8); + tmp_data[2] = (uint8_t)(i >> 16); + tmp_data[3] = (uint8_t)(i >> 24); + if (kp_himax_bus_write(0x00, tmp_data, 4, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + tmp_data[0] = 0x00; + if (kp_himax_bus_write(0x0C, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + + if (kp_himax_bus_read(0x08, read_data, read_length, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return I2C_FAIL; + } + if (read_length > 4) + hx83102_burst_enable(0); + + return 0; +} + +#ifdef HX_RST_PIN_FUNC +static void hx83102_pin_reset(void) +{ + I("%s: Now reset the Touch chip.\n", __func__); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 0); + msleep(20); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 1); + msleep(50); +} +#endif + +static bool hx83102_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t tmp_writ[DATA_LEN_4]; + uint8_t tmp_data[DATA_LEN_4]; + + do { + if (cnt == 0 || (tmp_data[0] != 0xA5 && tmp_data[0] != 0x00 && tmp_data[0] != 0x87)) { + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x5C; + tmp_writ[3] = 0x00; tmp_writ[2] = 0x00; tmp_writ[1] = 0x00; tmp_writ[0] = 0xA5; + hx83102_flash_write_burst(tmp_addr, tmp_writ); + } + msleep(20); + + /* check fw status */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, DATA_LEN_4, tmp_data); + + if (tmp_data[0] != 0x05) { + I("%s: Do not need wait FW, Status = 0x%02X!\n", __func__, tmp_data[0]); + break; + } + + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0x5C; + hx83102_register_read(tmp_addr, DATA_LEN_4, tmp_data); + I("%s: cnt = %d, data[0] = 0x%02X!\n", __func__, cnt, tmp_data[0]); + } while (tmp_data[0] != 0x87 && (++cnt < 50) && check_en == true); + + cnt = 0; + + + do { + /*=========================================== + *I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + *=========================================== + */ + tmp_data[0] = 0x27; + if (kp_himax_bus_write(0x31, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + *I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + *=========================================== + */ + tmp_data[0] = 0x95; + if (kp_himax_bus_write(0x32, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + *Check enter_save_mode + *====================== + */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, ADDR_LEN_4, tmp_data); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /*===================================== + *Reset TCON + *===================================== + */ + tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + /*===================================== + *Reset ADC + *===================================== + */ + tmp_addr[3] = 0x80; + tmp_addr[2] = 0x02; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + + return true; + } + usleep_range(10000, 10001); +#ifdef HX_RST_PIN_FUNC + hx83102_pin_reset(); +#endif + + } while (cnt++ < 15); + + return false; +} + +static bool hx83102ab_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t tmp_data[DATA_LEN_4]; + + do { + /*=========================================== + *I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + *=========================================== + */ + tmp_data[0] = 0x27; + if (kp_himax_bus_write(0x31, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + *I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + *=========================================== + */ + tmp_data[0] = 0x95; + if (kp_himax_bus_write(0x32, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + *Check enter_save_mode + *====================== + */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, ADDR_LEN_4, tmp_data); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /*===================================== + *Reset TCON + *===================================== + */ + tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + /*===================================== + *Reset ADC + *===================================== + */ + tmp_addr[3] = 0x80; + tmp_addr[2] = 0x02; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + + return true; + } + /*msleep(10);*/ + usleep_range(5000, 5001); +#ifdef HX_RST_PIN_FUNC + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 0); + msleep(20); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 1); + msleep(50); +#endif + + } while (cnt++ < 5); + + return false; +} + +static void hx83102ab_set_SMWP_enable(uint8_t SMWP_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t back_data[DATA_LEN_4]; + uint8_t retry_cnt = 0; + + kp_g_core_fp->fp_sense_off(true); + + do { + if (SMWP_enable) { + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, back_data, 4); + } else { + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_smwp_enable, DATA_LEN_4, tmp_data, 0); + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, back_data, 4); + } + + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_smwp_enable, DATA_LEN_4, tmp_data, false); + /*I("%s: tmp_data[0]=%d, SMWP_enable=%d, retry_cnt=%d\n", __func__, tmp_data[0],SMWP_enable,retry_cnt);*/ + retry_cnt++; + } while ((tmp_data[3] != back_data[3] || tmp_data[2] != back_data[2] || tmp_data[1] != back_data[1] || tmp_data[0] != back_data[0]) && retry_cnt < HIMAX_REG_RETRY_TIMES); + + kp_g_core_fp->fp_sense_on(0x00); +} + +static void hx83102ab_set_HSEN_enable(uint8_t HSEN_enable, bool suspended) +{ + uint8_t tmp_data[DATA_LEN_4]; + + if (HSEN_enable) { + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + } else { + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_hsen_enable, DATA_LEN_4, tmp_data, 0); + } + + /*I("%s: tmp_data[0]=%d, HSEN_enable=%d, retry_cnt=%d\n", __func__, tmp_data[0],HSEN_enable,retry_cnt);*/ +} + +static void hx83102ab_usb_detect_set(uint8_t *cable_config) +{ + uint8_t tmp_data[DATA_LEN_4]; + + if (cable_config[1] == 0x01) { + kp_himax_in_parse_assign_cmd(fw_func_handshaking_pwd, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + I("%s: USB detect status IN!\n", __func__); + } else { + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_usb_detect, DATA_LEN_4, tmp_data, 0); + I("%s: USB detect status OUT!\n", __func__); + } +} + +static uint8_t hx83102ab_read_DD_status(uint8_t *cmd_set, uint8_t *tmp_data) +{ + int cnt = 0; + uint8_t req_size = cmd_set[0]; + + cmd_set[3] = (*kp_pfw_op)->data_dd_request[0]; + + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_dd_handshak_addr, DATA_LEN_4, cmd_set, 0); + I("%s: cmd_set[0] = 0x%02X,cmd_set[1] = 0x%02X,cmd_set[2] = 0x%02X,cmd_set[3] = 0x%02X\n", + __func__, cmd_set[0], cmd_set[1], cmd_set[2], cmd_set[3]); + + /* Doing hand shaking 0xAA -> 0xBB */ + for (cnt = 0; cnt < 100; cnt++) { + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_dd_handshak_addr, DATA_LEN_4, tmp_data, false); + usleep_range(10000, 10001); + + if (tmp_data[3] == (*kp_pfw_op)->data_dd_ack[0]) { + I("%s Data ready goto moving data\n", __func__); + break; + } + if (cnt >= 99) { + I("%s Data not ready in FW\n", __func__); + return FW_NOT_READY; + } + } + + kp_g_core_fp->fp_sense_off(true); + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_dd_data_addr, req_size, tmp_data, false); + kp_g_core_fp->fp_sense_on(0x01); + return NO_ERR; +} + +static int hx83102ab_read_FW_status(uint8_t *state_addr, uint8_t *tmp_addr) +{ + uint8_t i; + uint8_t req_size = 0; + uint8_t status_addr[DATA_LEN_4]; /*0x10007F44*/ + uint8_t cmd_addr[DATA_LEN_4]; /*0x900000F8*/ + + if (state_addr[0] == 0x01) { + state_addr[1] = 0x04; + + for (i = 0; i < DATA_LEN_4; i++) { + /* 0x10007F44 */ + state_addr[i + 2] = (*kp_pfw_op)->addr_fw_dbg_msg_addr[i]; + status_addr[i] = (*kp_pfw_op)->addr_fw_dbg_msg_addr[i]; + } + + req_size = 0x04; + kp_g_core_fp->fp_sense_off(true); + kp_g_core_fp->fp_register_read(status_addr, req_size, tmp_addr, false); + kp_g_core_fp->fp_sense_on(0x01); + } else if (state_addr[0] == 0x02) { + state_addr[1] = 0x30; + + for (i = 0; i < DATA_LEN_4; i++) { + /* 0x900000F8 */ + state_addr[i + 2] = (*kp_pfw_op)->addr_fw_state_addr[i]; + cmd_addr[i] = (*kp_pfw_op)->addr_fw_state_addr[i]; + } + + req_size = 0x30; + kp_g_core_fp->fp_register_read(cmd_addr, req_size, tmp_addr, false); + } + + return NO_ERR; +} + +static void hx83102ab_power_on_init(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + + I("%s:\n", __func__); + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_raw_out_sel, DATA_LEN_4, tmp_data, 0); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_sorting_mode_en, DATA_LEN_4, tmp_data, 0); + kp_g_core_fp->fp_touch_information(); + kp_g_core_fp->fp_sense_on(0x00); +} + +static int hx83102ab_fts_ctpm_fw_upgrade_with_sys_fs_64k(unsigned char *fw, int len, bool change_iref) +{ + int burnFW_success = 0; + uint8_t tmp_data[DATA_LEN_4]; + + if (len != FW_SIZE_64k) { + E("%s: The file size is not 64K bytes\n", __func__); + return false; + } + +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + kp_g_core_fp->fp_sense_off(true); + kp_g_core_fp->fp_chip_erase(); + kp_g_core_fp->fp_flash_programming(fw, FW_SIZE_64k); + + if (kp_g_core_fp->fp_check_CRC((*kp_pfw_op)->addr_program_reload_from, FW_SIZE_64k) == 0) + burnFW_success = 1; + + kp_himax_in_parse_assign_cmd(fw_data_safe_mode_release_pw_reset, tmp_data, 4); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_raw_out_sel, DATA_LEN_4, tmp_data, 0); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_sorting_mode_en, DATA_LEN_4, tmp_data, 0); +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + /*System reset*/ + kp_g_core_fp->fp_system_reset(); +#endif + return burnFW_success; +} + +#ifdef HX_ESD_RECOVERY +static void hx83102ab_esd_ic_reset(void) +{ + (*kp_HX_ESD_RESET_ACTIVATE) = 1; +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_pin_reset(); +#else + kp_g_core_fp->fp_system_reset(); +#endif + I("%s:\n", __func__); +} +#if defined(HX_ZERO_FLASH) +static int hx83102d_0f_esd_check(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + int ret = NO_ERR; + + I("Enter %s\n", __func__); + + kp_g_core_fp->fp_register_read((*kp_pzf_op)->addr_sts_chk, DATA_LEN_4, tmp_data, 0); + + if (tmp_data[0] != (*kp_pzf_op)->data_activ_sts[0]) { + ret = ERR_STS_WRONG; + I("%s:status : %8X = %2X\n", __func__, zf_addr_sts_chk, tmp_data[0]); + } + + kp_g_core_fp->fp_register_read((*kp_pzf_op)->addr_activ_relod, DATA_LEN_4, tmp_data, 0); + + if (tmp_data[0] != (*kp_pzf_op)->data_activ_in[0]) { + ret = ERR_STS_WRONG; + I("%s:status : %8X = %2X\n", __func__, zf_addr_activ_relod, tmp_data[0]); + } + + return ret; +} +#endif +#endif + +static bool hx83102d_sense_off(bool check_en) +{ + uint8_t cnt = 0; + uint8_t tmp_addr[DATA_LEN_4]; + uint8_t tmp_data[DATA_LEN_4]; + + do { + if (cnt == 0 || (tmp_data[0] != 0xA5 && tmp_data[0] != 0x00 && tmp_data[0] != 0x87)) + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_ctrl_fw_isr, DATA_LEN_4, (*kp_pfw_op)->data_fw_stop, 0); + /*msleep(20);*/ + usleep_range(10000, 10001); + + /* check fw status */ + kp_g_core_fp->fp_register_read((*kp_pic_op)->addr_cs_central_state, ADDR_LEN_4, tmp_data, 0); + + if (tmp_data[0] != 0x05) { + I("%s: Do not need wait FW, Status = 0x%02X!\n", __func__, tmp_data[0]); + break; + } + + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_ctrl_fw_isr, 4, tmp_data, false); + I("%s: cnt = %d, data[0] = 0x%02X!\n", __func__, cnt, tmp_data[0]); + } while (tmp_data[0] != 0x87 && (++cnt < 10) && check_en == true); + + cnt = 0; + + do { + /*=========================================== + *I2C_password[7:0] set Enter safe mode : 0x31 ==> 0x27 + *=========================================== + */ + tmp_data[0] = 0x27; + if (kp_himax_bus_write(0x31, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /*=========================================== + *I2C_password[15:8] set Enter safe mode :0x32 ==> 0x95 + *=========================================== + */ + tmp_data[0] = 0x95; + if (kp_himax_bus_write(0x32, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + + /* ====================== + *Check enter_save_mode + *====================== + */ + tmp_addr[3] = 0x90; tmp_addr[2] = 0x00; tmp_addr[1] = 0x00; tmp_addr[0] = 0xA8; + hx83102_register_read(tmp_addr, ADDR_LEN_4, tmp_data); + I("%s: Check enter_save_mode data[0]=%X\n", __func__, tmp_data[0]); + + if (tmp_data[0] == 0x0C) { + /*===================================== + *Reset TCON + *===================================== + */ + tmp_addr[3] = 0x80; tmp_addr[2] = 0x02; tmp_addr[1] = 0x00; tmp_addr[0] = 0x20; + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; tmp_data[2] = 0x00; tmp_data[1] = 0x00; tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + /*===================================== + *Reset ADC + *===================================== + */ + tmp_addr[3] = 0x80; + tmp_addr[2] = 0x02; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0x94; + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x00; + hx83102_flash_write_burst(tmp_addr, tmp_data); + usleep_range(1000, 1001); + tmp_data[3] = 0x00; + tmp_data[2] = 0x00; + tmp_data[1] = 0x00; + tmp_data[0] = 0x01; + hx83102_flash_write_burst(tmp_addr, tmp_data); + + return true; + } + /*msleep(10);*/ + usleep_range(5000, 5001); +#ifdef HX_RST_PIN_FUNC + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 0); + msleep(20); + kp_himax_rst_gpio_set((*kp_private_ts)->rst_gpio, 1); + msleep(50); +#endif + + } while (cnt++ < 5); + + return false; +} + +static bool hx83102d_dd_clk_set(bool enable) +{ + uint8_t data[4] = {0}; + + data[0] = (enable)?0xDD:0; + return (kp_g_core_fp->fp_register_write((*kp_pic_op)->adr_osc_en, + sizeof((*kp_pic_op)->adr_osc_en), data, 0) == NO_ERR); +} + +static void hx83102d_dd_reg_en(bool enable) +{ + uint8_t data[4] = {0}; + + data[0] = 0xA5; data[1] = 0x00; + data[2] = 0x00; data[3] = 0x00; + kp_g_core_fp->fp_register_write((*kp_pic_op)->adr_osc_pw, DATA_LEN_4, data, 0); + + data[0] = 0xA5; data[1] = 0x83; + data[2] = 0x10; data[3] = 0x2D; + kp_g_core_fp->fp_dd_reg_write(0xB9, 0, 4, data, 0); +} + +static bool hx83102d_ic_id_read(void) +{ + int i = 0; + uint8_t data[4] = {0}; + + kp_g_core_fp->fp_dd_clk_set(true); + kp_g_core_fp->fp_dd_reg_en(true); + + for (i = 0; i < 13; i++) { + data[0] = 0x2A + i; + kp_g_core_fp->fp_dd_reg_write(0xBB, 3, 1, data, 0); + data[0] = 0x08; + kp_g_core_fp->fp_dd_reg_write(0xBB, 1, 1, data, 0); + kp_g_core_fp->fp_dd_reg_read(0xBB, 6, 1, data, 0); + (*kp_ic_data)->vendor_ic_id[i] = data[0]; + I("ic_data->vendor_ic_id[%d] = %02X\n", i, (*kp_ic_data)->vendor_ic_id[i]); + } + + kp_g_core_fp->fp_dd_clk_set(false); + + return true; +} + +#if defined(HX_ZERO_FLASH) +static void himax_hx83102d_reload_to_active(void) +{ + uint8_t addr[DATA_LEN_4] = {0}; + uint8_t data[DATA_LEN_4] = {0}; + uint8_t retry_cnt = 0; + int ret = 0; + + addr[3] = 0x90; + addr[2] = 0x00; + addr[1] = 0x00; + addr[0] = 0x48; + + do { + data[3] = 0x00; + data[2] = 0x00; + data[1] = 0x00; + data[0] = 0xEC; + ret = kp_g_core_fp->fp_register_write(addr, DATA_LEN_4, data, 0); + usleep_range(1000, 1100); + ret = kp_g_core_fp->fp_register_read(addr, DATA_LEN_4, data, 0); + I("%s: data[1]=%d, data[0]=%d, reload_to_active_cnt=%d\n", __func__, data[1], data[0], retry_cnt); + retry_cnt++; + } while ((data[1] != 0x01 || data[0] != 0xEC) && retry_cnt < HIMAX_REG_RETRY_TIMES); + if (ret != 0) + atomic_set(&(*kp_private_ts)->resume_update_fail, 1); +} + +static void himax_hx83102d_resume_ic_action(void) +{ +#ifndef HX_RESUME_HW_RESET + himax_hx83102d_reload_to_active(); +#endif +} + +static void himax_hx83102d_suspend_ic_action(void) +{ +#ifndef HX_RESUME_HW_RESET + himax_hx83102d_reload_to_active(); +#endif +} + +static void himax_hx83102d_sense_on(uint8_t FlashMode) +{ + uint8_t tmp_data[DATA_LEN_4]; + int retry = 0; + + I("Enter %s\n", __func__); + + kp_g_core_fp->fp_interface_on(); + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_ctrl_fw_isr, + sizeof((*kp_pfw_op)->data_clear), (*kp_pfw_op)->data_clear, 0); + /*msleep(20);*/ + usleep_range(10000, 10001); + if (!FlashMode) { +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + himax_hx83102d_reload_to_active(); + } else { + himax_hx83102d_reload_to_active(); + do { + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_safe_mode_release_pw, + sizeof((*kp_pfw_op)->data_safe_mode_release_pw_active), (*kp_pfw_op)->data_safe_mode_release_pw_active, 0); + + kp_g_core_fp->fp_register_read((*kp_pfw_op)->addr_flag_reset_event, DATA_LEN_4, tmp_data, 0); + I("%s:Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + } while ((tmp_data[1] != 0x01 || tmp_data[0] != 0x00) && retry++ < 5); + + if (retry >= 5) { + E("%s: Fail:\n", __func__); +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + himax_hx83102d_reload_to_active(); + } else { + I("%s:OK and Read status from IC = %X,%X\n", __func__, tmp_data[0], tmp_data[1]); + /* reset code*/ + tmp_data[0] = 0x00; + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_lb[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + if (kp_himax_bus_write((*kp_pic_op)->adr_i2c_psw_ub[0], tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) + E("%s: i2c access fail!\n", __func__); + + kp_g_core_fp->fp_register_write((*kp_pfw_op)->addr_safe_mode_release_pw, + sizeof((*kp_pfw_op)->data_safe_mode_release_pw_reset), (*kp_pfw_op)->data_safe_mode_release_pw_reset, 0); + } + } +} + +static void hx83102ab_firmware_update_0f(const struct firmware *fw_entry) +{ + uint8_t tmp_addr[4]; + + I("%s, Entering\n", __func__); + + kp_g_core_fp->fp_register_write((*kp_pzf_op)->addr_system_reset, 4, (*kp_pzf_op)->data_system_reset, 0); + + kp_g_core_fp->fp_sense_off(false); + + /* first 32K */ + /*bin file start*/ + /*isram*/ + kp_g_core_fp->fp_write_sram_0f(fw_entry, (*kp_pzf_op)->data_sram_start_addr, 0, HX_32K_SZ); + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x73; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_write_sram_0f(fw_entry, tmp_addr, 0x8000, (0x200*4)); + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x68; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_write_sram_0f(fw_entry, tmp_addr, 0x8800, (0x200*4)); + + /*last 16k*/ + /*config info*/ + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x72; + tmp_addr[0] = 0x04; /*0x10007206 <= size 60,from 0x0000C180*/ + kp_g_core_fp->fp_write_sram_0f(fw_entry, tmp_addr, 0xC178, (84)); + + /*FW config*/ + if ((*kp_G_POWERONOF) == 1) + kp_g_core_fp->fp_write_sram_0f(fw_entry, (*kp_pzf_op)->data_fw_cfg_1, 0xBFFE, 388); + else + kp_g_core_fp->fp_clean_sram_0f((*kp_pzf_op)->data_fw_cfg_1, 388, 1); + /*ADC config*/ + if ((*kp_G_POWERONOF) == 1) + kp_g_core_fp->fp_write_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_1, 0xe000, (128 * 4)); + else + kp_g_core_fp->fp_clean_sram_0f((*kp_pzf_op)->data_adc_cfg_1, (128 * 4), 2); + + I("%s, END\n", __func__); +} +#if defined(HX_0F_DEBUG) +static void hx83102ab_firmware_read_0f(const struct firmware *fw_entry, int type) +{ + uint8_t tmp_addr[4]; + + I("%s, Entering\n", __func__); + if (type == 0) { /* first 32K */ + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_sram_start_addr, 0, HX_32K_SZ); + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x68; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_addr, 0x8000, (0x200*4)); + + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x73; + tmp_addr[0] = 0x00; + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_addr, 0x8800, (0x200*4)); + + } else { /*last 16k*/ + tmp_addr[3] = 0x10; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x72; + tmp_addr[0] = 0x04; /*0x10007206 <= size 80,from 0x0000C180*/ + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_addr, 0xC178, (84)); + + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_fw_cfg_1, 0xBFFE, 388); + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_1, 0xE000, (128*4)); + + } + I("%s, END\n", __func__); +} +static void hx83102d_firmware_read_0f(const struct firmware *fw_entry, int type) +{ + uint8_t tmp_data[4]; + + I("%s, Entering\n", __func__); + + switch (type) { + case 0: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_sram_start_addr, 0, HX_40K_SZ); + break; + case 1: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_cfg_info, 0xC000, 132); + break; + case 2: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_fw_cfg_1, 0xC0FE, 484); + break; + case 3: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_1, 0xD000, 768); + break; + case 4: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_2, 0xD300, 1536); + break; + case 5: + kp_g_core_fp->fp_read_sram_0f(fw_entry, (*kp_pzf_op)->data_adc_cfg_3, 0xE000, 1536); + break; + case 6: + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_bor_prevent_info, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xC9E0, 32); + break; + case 7: + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_notch_info, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xCA00, 128); + break; + case 8: + kp_himax_in_parse_assign_cmd(hx83102d_zf_func_info_en, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xCB00, 12); + break; + case 9: + kp_himax_in_parse_assign_cmd(hx83102d_zf_po_sub_func, tmp_data, 4); + kp_g_core_fp->fp_read_sram_0f(fw_entry, tmp_data, 0xA000, HX4K); + break; + default: + break; + } + I("%s, END\n", __func__); +} +#endif +#if defined(HX_CODE_OVERLAY) +static int hx83102d_0f_overlay(int ovl_type, int mode) +{ + int ret = 0; + uint8_t count = 0; + uint8_t count2 = 0; + const struct firmware *fwp = NULL; + char fwname[32] = "Himax_firmware.bin"; + uint8_t part_num = 0; + uint8_t buf[16]; + uint8_t handshaking_addr[4] = {0xFC, 0x7F, 0x00, 0x10}; + /*uint8_t mpap_addr[4] = {0xEC, 0x73, 0x00, 0x10};*/ + uint8_t ovl_idx_t; + uint8_t request; + uint8_t reply; + uint8_t sram_addr[4] = {0}; + uint32_t offset = 0; + uint32_t size = 0; + uint8_t send_data[4] = {0}; + uint8_t recv_data[4] = {0}; + + ret = request_firmware(&fwp, fwname, (*kp_private_ts)->dev); + if (ret < 0) { + E("%s: request firmware FAIL!!!\n", __func__); + return ret; + } + + /*1. get number of partition*/ + part_num = fwp->data[HX64K + 12]; + if (part_num <= 1) { + E("%s, size of cfg part failed! part_num = %d\n", + __func__, part_num); + return LENGTH_FAIL; + } + + I("%s: overlay section %d\n", __func__, ovl_type-1); + if (ovl_type == 2) { + request = ovl_gesture_request; + reply = ovl_gesture_reply; + } else if (ovl_type == 3) { + request = ovl_border_request; + reply = ovl_border_reply; + } else { + E("%s: error overlay type %d\n", __func__, ovl_type); + return HX_INIT_FAIL; + } + ovl_idx_t = *((*kp_ovl_idx) + ovl_type - 1); + memcpy(buf, &fwp->data[ovl_idx_t * 0x10 + HX64K], 16); + memcpy(sram_addr, buf, 4); + offset = buf[11]<<24 | buf[10]<<16 | buf[9]<<8 | buf[8]; + size = buf[7]<<24 | buf[6]<<16 | buf[5]<<8 | buf[4]; + +/* + * if (ovl_type == 1) { + * if (mode == 0) { + * //border overlay when finish self_test + * send_data[3] = 0x00; + * send_data[2] = 0x00; + * send_data[1] = 0x00; + * send_data[0] = 0x00; + * offset = zf_info_arr[2].fw_addr; + * size = zf_info_arr[2].write_size; + * I("%s: self test overlay section 2\n", __func__); + * } else if (mode == 1) { + * //sorting overlay when call self_test + * send_data[3] = 0x00; + * send_data[2] = 0x10; + * send_data[1] = 0x73; + * send_data[0] = 0x80; + * offset = zf_info_arr[0].fw_addr; + * size = zf_info_arr[0].write_size; + * I("%s: self test overlay section 0\n", __func__); + * } + * } + */ + +/* + * if (ovl_type == 1) { + * + * g_core_fp.fp_sense_off(false); + * + * //g_core_fp.fp_register_write(mpap_addr, DATA_LEN_4, send_data, 0); + * count = 0; + * do { + * g_core_fp.fp_register_write(mpap_addr, DATA_LEN_4, send_data, 0); + * g_core_fp.fp_register_read(mpap_addr, DATA_LEN_4, recv_data, 0); + * } while ((recv_data[0] != send_data[0] || recv_data[1] != send_data[1] || recv_data[2] != send_data[2] || recv_data[3] != send_data[3]) && count++ < 10); + * + * I("%s: set mpap pw %d times\n", __func__, count); + * + * //g_core_fp.fp_write_sram_0f(fwp, sram_addr, offset, size); + * if (kp_g_core_fp->fp_write_sram_0f_crc(fwp, sram_addr, offset, size) != 0) + * E("%s, Overlay HW CRC FAIL\n", __func__); + * + * g_core_fp.fp_reload_disable(0); + * + * g_core_fp.fp_sense_on(0x00); + * + * } else { + */ + + count = 0; + do { + kp_g_core_fp->fp_register_read(handshaking_addr, DATA_LEN_4, recv_data, 0); + } while (recv_data[0] != request && count++ < 10); + + if (count < 10) { + /*g_core_fp.fp_write_sram_0f(fwp, sram_addr, offset, size);*/ + if (kp_g_core_fp->fp_write_sram_0f_crc(fwp, sram_addr, offset, size) != 0) + E("%s, Overlay HW CRC FAIL\n", __func__); + + send_data[3] = 0x00; + send_data[2] = 0x00; + send_data[1] = 0x00; + send_data[0] = reply; + + count2 = 0; + do { + kp_g_core_fp->fp_register_write(handshaking_addr, DATA_LEN_4, send_data, 0); + usleep_range(1000, 1100); + kp_g_core_fp->fp_register_read(handshaking_addr, DATA_LEN_4, recv_data, 0); + } while (recv_data[0] != reply && count2++ < 10); + + if (ovl_type == 3) { +#ifdef HX_RST_PIN_FUNC + kp_g_core_fp->fp_ic_reset(false, false); +#else + kp_g_core_fp->fp_system_reset(); +#endif + } + } + + I("%s: overlay request %d times; reply %d times\n", __func__, count, count2); + + release_firmware(fwp); + + /* rescue mechanism */ + if (count >= 10) { + + kp_g_core_fp->fp_0f_op_file_dirly(fwname); + + kp_g_core_fp->fp_reload_disable(0); + + kp_g_core_fp->fp_sense_on(0x00); + + kp_himax_int_enable(1); + } + + return 0; +} +#endif +#endif + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) +static void hx83102a_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + (*kp_ic_data)->vendor_ic_ver = 0xFFFF; + /* Enable read DD */ + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x10; + tmp_data[3] = 0x2A; + kp_himax_in_parse_assign_cmd(hx83102_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83102_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; + + I("VENDOR_IC_VER : %X\n", ((*kp_ic_data)->vendor_ic_ver)); +} + +static void hx83102b_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + (*kp_ic_data)->vendor_ic_ver = 0xFFFF; + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83102b_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x01; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x10; + tmp_data[3] = 0x2B; + kp_himax_in_parse_assign_cmd(hx83102_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83102_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; + + /* Disable read DD */ + kp_himax_in_parse_assign_cmd(hx83102b_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x00; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + I("VENDOR_IC_VER : %X\n", ((*kp_ic_data)->vendor_ic_ver)); +} + +static void hx83102d_read_ic_ver(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + + (*kp_ic_data)->vendor_ic_ver = 0xFFFF; + /* Enable read DD */ + kp_himax_in_parse_assign_cmd(hx83102d_ic_osc_pw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xA5; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + kp_himax_in_parse_assign_cmd(hx83102d_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0xDD; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + tmp_data[0] = 0x00; + tmp_data[1] = 0x83; + tmp_data[2] = 0x10; + tmp_data[3] = 0x2D; + kp_himax_in_parse_assign_cmd(hx83102_ic_b9_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + /* Read DD data */ + kp_himax_in_parse_assign_cmd(hx83102_ic_fw, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + I("E8=%X\n", tmp_data[0]); + (*kp_ic_data)->vendor_ic_ver = tmp_data[0]; + + /* Disable read DD */ + kp_himax_in_parse_assign_cmd(hx83102d_ic_osc_en, tmp_addr, sizeof(tmp_addr)); + kp_g_core_fp->fp_register_read(tmp_addr, DATA_LEN_4, tmp_data, 0); + tmp_data[0] = 0x00; + kp_g_core_fp->fp_register_write(tmp_addr, DATA_LEN_4, tmp_data, false); + + I("VENDOR_IC_VER : %X\n", ((*kp_ic_data)->vendor_ic_ver)); +} + +static void hx83102_dynamic_fw_name(uint8_t ic_name) +{ + char firmware_name[64]; + + if (ic_name == 0x2a) + hx83102a_read_ic_ver(); + else if (ic_name == 0x2b) + hx83102b_read_ic_ver(); + else + hx83102d_read_ic_ver(); + + if ((*kp_i_CTPM_firmware_name) != NULL) { + kfree((*kp_i_CTPM_firmware_name)); + (*kp_i_CTPM_firmware_name) = NULL; + } + memset(firmware_name, 0x00, sizeof(firmware_name)); + + if (((*kp_ic_data)->vendor_ic_ver == 0x00) || ((*kp_ic_data)->vendor_ic_ver == 0x21)) { /* Cut 1 */ + memcpy(firmware_name, "Himax_firmware_1.bin", sizeof(char)*strlen("Himax_firmware_1.bin")); + } else if (((*kp_ic_data)->vendor_ic_ver == 0x01) || ((*kp_ic_data)->vendor_ic_ver == 0x11)) { /* Cut 2 */ + memcpy(firmware_name, "Himax_firmware_2.bin", sizeof(char)*strlen("Himax_firmware_2.bin")); + } else { /* Original */ + memcpy(firmware_name, "Himax_firmware.bin", sizeof(char)*strlen("Himax_firmware.bin")); + } + + (*kp_i_CTPM_firmware_name) = kzalloc((sizeof(char)*(strlen(firmware_name)+1)), GFP_KERNEL); + if ((*kp_i_CTPM_firmware_name) != NULL) + memcpy((*kp_i_CTPM_firmware_name), firmware_name, (sizeof(char)*(strlen(firmware_name)+1))); + I("(*kp_i_CTPM_firmware_name) : %s\n", (*kp_i_CTPM_firmware_name)); +} +#endif +#endif + +static bool hx83102e_read_event_stack(uint8_t *buf, uint8_t length) +{ + struct timespec t_start, t_end, t_delta; + int len = length; + int i2c_speed = 0; + + if ((*kp_private_ts)->debug_log_level & BIT(2)) + getnstimeofday(&t_start); + + kp_himax_bus_read((*kp_pfw_op)->addr_event_addr[0], buf, length, HIMAX_I2C_RETRY_TIMES); + + if ((*kp_private_ts)->debug_log_level & BIT(2)) { + getnstimeofday(&t_end); + t_delta.tv_nsec = (t_end.tv_sec * 1000000000 + t_end.tv_nsec) - (t_start.tv_sec * 1000000000 + t_start.tv_nsec); /*ns*/ + i2c_speed = (len * 9 * 1000000 / (int)t_delta.tv_nsec) * 13 / 10; + (*kp_private_ts)->bus_speed = (int)i2c_speed; + } + return 1; +} + +static void himax_hx83102ab_reg_re_init(uint8_t ic_name) +{ + I("%s:Entering!\n", __func__); + kp_himax_in_parse_assign_cmd(hx83102ab_fw_addr_sorting_mode_en, (*kp_pfw_op)->addr_sorting_mode_en, sizeof((*kp_pfw_op)->addr_sorting_mode_en)); + kp_himax_in_parse_assign_cmd(hx83102ab_fw_addr_selftest_addr_en, (*kp_pfw_op)->addr_selftest_addr_en, sizeof((*kp_pfw_op)->addr_selftest_addr_en)); +#ifdef HX_ZERO_FLASH + kp_himax_in_parse_assign_cmd(hx83102ab_data_adc_cfg_1, (*kp_pzf_op)->data_adc_cfg_1, sizeof((*kp_pzf_op)->data_adc_cfg_1)); +#endif + + if (ic_name == 0x2a) { + kp_himax_in_parse_assign_cmd(hx83102a_data_df_rx, (*kp_pdriver_op)->data_df_rx, sizeof((*kp_pdriver_op)->data_df_rx)); + kp_himax_in_parse_assign_cmd(hx83102a_data_df_tx, (*kp_pdriver_op)->data_df_tx, sizeof((*kp_pdriver_op)->data_df_tx)); + kp_himax_in_parse_assign_cmd(hx83102a_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102a_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + } else { + kp_himax_in_parse_assign_cmd(hx83102b_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102b_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + } +} + +static void himax_hx83102ab_func_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_g_core_fp->fp_chip_init = hx83102_chip_init; + kp_g_core_fp->fp_sense_off = hx83102ab_sense_off; + kp_g_core_fp->fp_set_SMWP_enable = hx83102ab_set_SMWP_enable; + kp_g_core_fp->fp_set_HSEN_enable = hx83102ab_set_HSEN_enable; + kp_g_core_fp->fp_usb_detect_set = hx83102ab_usb_detect_set; + kp_g_core_fp->fp_read_DD_status = hx83102ab_read_DD_status; + kp_g_core_fp->fp_read_FW_status = hx83102ab_read_FW_status; + kp_g_core_fp->fp_power_on_init = hx83102ab_power_on_init; + kp_g_core_fp->fp_fts_ctpm_fw_upgrade_with_sys_fs_64k = hx83102ab_fts_ctpm_fw_upgrade_with_sys_fs_64k; +#ifdef HX_ESD_RECOVERY + kp_g_core_fp->fp_esd_ic_reset = hx83102ab_esd_ic_reset; +#endif +#if defined(HX_ZERO_FLASH) + kp_g_core_fp->fp_firmware_update_0f = hx83102ab_firmware_update_0f; +#if defined(HX_0F_DEBUG) + kp_g_core_fp->fp_firmware_read_0f = hx83102ab_firmware_read_0f; +#endif +#endif + +} + +static void himax_hx83102d_reg_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_himax_in_parse_assign_cmd(hx83102d_fw_addr_raw_out_sel, (*kp_pfw_op)->addr_raw_out_sel, sizeof((*kp_pfw_op)->addr_raw_out_sel)); +#if defined(HX_ZERO_FLASH) + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_sram_start_addr, (*kp_pzf_op)->data_sram_start_addr, sizeof((*kp_pzf_op)->data_sram_start_addr)); + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_adc_cfg_1, (*kp_pzf_op)->data_adc_cfg_1, sizeof((*kp_pzf_op)->data_adc_cfg_1)); + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_adc_cfg_2, (*kp_pzf_op)->data_adc_cfg_2, sizeof((*kp_pzf_op)->data_adc_cfg_2)); + kp_himax_in_parse_assign_cmd(hx83102d_zf_data_adc_cfg_3, (*kp_pzf_op)->data_adc_cfg_3, sizeof((*kp_pzf_op)->data_adc_cfg_3)); +#endif + kp_himax_in_parse_assign_cmd(hx83102d_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102d_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + kp_himax_in_parse_assign_cmd(hx83102d_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); + kp_himax_in_parse_assign_cmd(hx83102d_adr_osc_en, (*kp_pic_op)->adr_osc_en, sizeof((*kp_pic_op)->adr_osc_en)); + kp_himax_in_parse_assign_cmd(hx83102d_adr_osc_pw, (*kp_pic_op)->adr_osc_pw, sizeof((*kp_pic_op)->adr_osc_pw)); +} + +static void himax_hx83102d_func_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_g_core_fp->fp_chip_init = hx83102_chip_init; + kp_g_core_fp->fp_sense_off = hx83102d_sense_off; + kp_g_core_fp->fp_ic_id_read = hx83102d_ic_id_read; + kp_g_core_fp->fp_dd_clk_set = hx83102d_dd_clk_set; + kp_g_core_fp->fp_dd_reg_en = hx83102d_dd_reg_en; +#if defined(HX_ZERO_FLASH) + /*kp_g_core_fp->fp_firmware_update_0f = hx83102d_firmware_update_0f;*/ + kp_g_core_fp->fp_resume_ic_action = himax_hx83102d_resume_ic_action; + kp_g_core_fp->fp_suspend_ic_action = himax_hx83102d_suspend_ic_action; + kp_g_core_fp->fp_sense_on = himax_hx83102d_sense_on; + kp_g_core_fp->fp_0f_reload_to_active = himax_hx83102d_reload_to_active; +#if defined(HX_0F_DEBUG) + kp_g_core_fp->fp_firmware_read_0f = hx83102d_firmware_read_0f; +#endif +#if defined(HX_CODE_OVERLAY) + kp_g_core_fp->fp_0f_overlay = hx83102d_0f_overlay; +#endif +#ifdef HX_ESD_RECOVERY + kp_g_core_fp->fp_0f_esd_check = hx83102d_0f_esd_check; +#endif +#endif +} + +static void himax_hx83102e_reg_re_init(void) +{ + I("%s:Entering!\n", __func__); + kp_himax_in_parse_assign_cmd(hx83102e_fw_addr_raw_out_sel, (*kp_pfw_op)->addr_raw_out_sel, sizeof((*kp_pfw_op)->addr_raw_out_sel)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_rx, (*kp_pdriver_op)->data_df_rx, sizeof((*kp_pdriver_op)->data_df_rx)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_tx, (*kp_pdriver_op)->data_df_tx, sizeof((*kp_pdriver_op)->data_df_tx)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_x_res, (*kp_pdriver_op)->data_df_x_res, sizeof((*kp_pdriver_op)->data_df_x_res)); + kp_himax_in_parse_assign_cmd(hx83102e_data_df_y_res, (*kp_pdriver_op)->data_df_y_res, sizeof((*kp_pdriver_op)->data_df_y_res)); +} + +static void himax_hx83102e_func_re_init(void) +{ + I("%s:Entering!\n", __func__); + + kp_g_core_fp->fp_chip_init = hx83102e_chip_init; + kp_g_core_fp->fp_sense_off = hx83102d_sense_off; + kp_g_core_fp->fp_read_event_stack = hx83102e_read_event_stack; +} + + +static bool hx83102_chip_detect(void) +{ + uint8_t tmp_data[DATA_LEN_4]; + uint8_t tmp_addr[DATA_LEN_4]; + bool ret_data = false; + int ret = 0; + int i = 0; + + if (himax_ic_setup_external_symbols()) + return false; + +#ifdef HX_RST_PIN_FUNC + hx83102_pin_reset(); +#endif + if (kp_himax_bus_read(0x13, tmp_data, 1, HIMAX_I2C_RETRY_TIMES) < 0) { + E("%s: i2c access fail!\n", __func__); + return false; + } + if (hx83102_sense_off(false) == false) { + ret_data = false; + E("%s:hx83102_sense_off Fail:\n", __func__); + return ret_data; + } + + for (i = 0; i < 5; i++) { + tmp_addr[3] = 0x90; + tmp_addr[2] = 0x00; + tmp_addr[1] = 0x00; + tmp_addr[0] = 0xD0; + if (hx83102_register_read(tmp_addr, DATA_LEN_4, tmp_data) != 0) { + ret_data = false; + E("%s:hx83102_register_read Fail:\n", __func__); + return ret_data; + } + I("%s:Read driver IC ID = %X,%X,%X\n", __func__, tmp_data[3], tmp_data[2], tmp_data[1]); /*83,10,2X*/ + + if ((tmp_data[3] == 0x83) && (tmp_data[2] == 0x10) && ((tmp_data[1] == 0x2a) || (tmp_data[1] == 0x2b) || (tmp_data[1] == 0x2d) || (tmp_data[1] == 0x2e))) { + if (tmp_data[1] == 0x2a) { + strlcpy((*kp_private_ts)->chip_name, HX_83102A_SERIES_PWON, 30); + I("%s:detect IC HX83102A successfully\n", __func__); + } else if (tmp_data[1] == 0x2b) { + strlcpy((*kp_private_ts)->chip_name, HX_83102B_SERIES_PWON, 30); + I("%s:detect IC HX83102B successfully\n", __func__); + } else if (tmp_data[1] == 0x2d) { + strlcpy((*kp_private_ts)->chip_name, HX_83102D_SERIES_PWON, 30); + I("%s:detect IC HX83102D successfully\n", __func__); + } else { + strlcpy((*kp_private_ts)->chip_name, HX_83102E_SERIES_PWON, 30); + I("%s:detect IC HX83102E successfully\n", __func__); + } + + ret = kp_himax_mcu_in_cmd_struct_init(); + if (ret < 0) { + ret_data = false; + E("%s:cmd_struct_init Fail:\n", __func__); + return ret_data; + } + + kp_himax_mcu_in_cmd_init(); + if ((tmp_data[1] == 0x2a) || (tmp_data[1] == 0x2b)) { + himax_hx83102ab_reg_re_init(tmp_data[1]); + himax_hx83102ab_func_re_init(); + } else if (tmp_data[1] == 0x2d) { + himax_hx83102d_reg_re_init(); + himax_hx83102d_func_re_init(); + } else {/* 0x2e */ + himax_hx83102e_reg_re_init(); + himax_hx83102e_func_re_init(); + } + ret_data = true; + break; + } + + ret_data = false; + E("%s:Read driver ID register Fail:\n", __func__); + E("Could NOT find Himax Chipset\n"); + E("Please check 1.VCCD,VCCA,VSP,VSN\n"); + E("2. LCM_RST,TP_RST\n"); + E("3. Power On Sequence\n"); + + } + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#if defined(HX_EN_DYNAMIC_NAME) + hx83102_dynamic_fw_name(tmp_data[1]); +#endif +#endif + + return ret_data; +} + +DECLARE(HX_MOD_KSYM_HX83102); + +static int himax_hx83102_probe(void) +{ + I("%s:Enter\n", __func__); + himax_add_chip_dt(hx83102_chip_detect); + + return 0; +} + +static int himax_hx83102_remove(void) +{ + free_chip_dt_table(); + return 0; +} + +static int __init himax_hx83102_init(void) +{ + int ret = 0; + + I("%s\n", __func__); + ret = himax_hx83102_probe(); + return 0; +} + +static void __exit himax_hx83102_exit(void) +{ + himax_hx83102_remove(); +} + +module_init(himax_hx83102_init); +module_exit(himax_hx83102_exit); + +MODULE_DESCRIPTION("HIMAX HX83102 touch driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_v3_mmi_hx83102d/himax_ic_HX83102d.h b/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_v3_mmi_hx83102d/himax_ic_HX83102d.h new file mode 100755 index 000000000000..ceab657ecdd0 --- /dev/null +++ b/drivers/input/touchscreen/himax_v3_mmi/support_ic/himax_v3_mmi_hx83102d/himax_ic_HX83102d.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Himax Android Driver Sample Code for HX83102 chipset + * + * Copyright (C) 2019 Himax Corporation. + * + * 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. + */ + +#include "himax_platform.h" +#include "himax_common.h" +#include "himax_ic_core.h" +#include + +#define hx83102ab_fw_addr_sorting_mode_en 0x100007FC +#define hx83102ab_fw_addr_selftest_addr_en 0x100007F8 +#define hx83102ab_data_adc_cfg_1 0x10007B00 +#define hx83102a_data_df_rx 48 +#define hx83102a_data_df_tx 24 +#define hx83102a_data_df_x_res 2160 +#define hx83102a_data_df_y_res 3840 +#define hx83102b_data_df_x_res 720 +#define hx83102b_data_df_y_res 1280 + +#define hx83102d_fw_addr_raw_out_sel 0x800204f4 +#define hx83102d_zf_data_adc_cfg_1 0x10007B00 +#define hx83102d_zf_data_adc_cfg_2 0x10006A00 +#define hx83102d_zf_data_adc_cfg_3 0x10007500 +#define hx83102d_zf_data_bor_prevent_info 0x10007268 +#define hx83102d_zf_data_notch_info 0x10007300 +#define hx83102d_zf_func_info_en 0x10007FD0 +#define hx83102d_zf_po_sub_func 0x10005A00 +#define hx83102d_zf_data_sram_start_addr 0x20000000 +#define hx83102d_data_df_x_res 720 +#define hx83102d_data_df_y_res 1280 +#define hx83102d_adr_osc_en 0x9000009C +#define hx83102d_adr_osc_pw 0x90000280 + +#define hx83102e_fw_addr_raw_out_sel 0x100072EC +#define hx83102e_data_df_rx 48 +#define hx83102e_data_df_tx 30 +#define hx83102e_data_df_x_res 1200 +#define hx83102e_data_df_y_res 1920 + +#if defined(HX_AUTO_UPDATE_FW) || defined(HX_ZERO_FLASH) +#ifdef HX_EN_DYNAMIC_NAME +#define hx83102_ic_b9_en 0x300B9000 +#define hx83102_ic_fw 0x300E8006 + +#define hx83102b_ic_osc_en 0x900880A0 +#define hx83102d_ic_osc_en 0x9000009C +#define hx83102d_ic_osc_pw 0x90000280 +#endif +#endif + +#ifdef HX_ESD_RECOVERY + extern u8 HX_ESD_RESET_ACTIVATE; +#endif + diff --git a/drivers/input/touchscreen/ili9882_mmi/Android.mk b/drivers/input/touchscreen/ili9882_mmi/Android.mk new file mode 100644 index 000000000000..80f61d580f2b --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/Android.mk @@ -0,0 +1,93 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifneq ($(BOARD_USES_DOUBLE_TAP),) + KERNEL_CFLAGS += CONFIG_INPUT_ILI_0FLASH_MMI_ENABLE_DOUBLE_TAP=y +endif + +ifneq ($(BOARD_USES_PANEL_NOTIFICATIONS),) + KERNEL_CFLAGS += CONFIG_INPUT_ILI_PANEL_NOTIFICATIONS=y +endif + +ifneq ($(MOTO_PANEL_CHECK_TOUCH_STATE),) + KERNEL_CFLAGS += CONFIG_INPUT_ILI_0FLASH_MMI_NOTIFY_TOUCH_STATE=y +endif + +ifneq ($(MOTO_PANEL_CHECK_TOUCH_STATE_V2),) + KBUILD_OPTIONS += CONFIG_INPUT_ILI_0FLASH_MMI_NOTIFY_TOUCH_STATE=y +endif + +ifneq ($(DL_FW_BY_DISPLAY),) + KERNEL_CFLAGS += CONFIG_ILITEK_RESUME_BY_DDI=y +endif + +ifeq ($(ILITEK_FW_PANEL),true) + KERNEL_CFLAGS += CONFIG_ILITEK_FW_PANEL=y +endif + +ifeq ($(DRM_PANEL_EVENT_NOTIFICATIONS),true) + KBUILD_OPTIONS += CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS=y +endif + +ifeq ($(ILITEK_SUSPEND_PWROFF),true) + KBUILD_OPTIONS += CONFIG_ILITEK_SUSPEND_PWROFF=y +endif + +ifneq ($(BOARD_USES_PEN_NOTIFIER),) + KBUILD_OPTIONS += CONFIG_INPUT_ILITEK_MMI_PEN_NOTIFIER=y +endif + +ifeq ($(ILITEK_CHARGER),true) + KERNEL_CFLAGS += CONFIG_ILITEK_CHARGER=y +endif + +ifeq ($(ILITEK_ESD),true) + KERNEL_CFLAGS += CONFIG_ILITEK_ESD=y +endif + +ifeq ($(ILITEK_GESTURE),true) + KERNEL_CFLAGS += CONFIG_ILITEK_GESTURE=y +endif + +# enable gesture mode by panel config +# not all panels need enable gesture mode for some products +ifeq ($(ILITEK_PANEL_GESTURE),true) + KERNEL_CFLAGS += CONFIG_ILITEK_PANEL_GESTURE=y +endif + +ifeq ($(BOARD_USES_STYLUS_PALM),true) + KBUILD_OPTIONS += CONFIG_ILITEK_PASSIVE_PEN=y +endif + +ifeq ($(TOUCHSCREEN_LAST_TIME),true) + KBUILD_OPTIONS += CONFIG_ILI_TOUCH_LAST_TIME=y +endif + +ifeq ($(GTP_ENABLE_DDA_STYLUS),true) + KBUILD_OPTIONS += CONFIG_GTP_DDA_STYLUS=y +endif + +ifeq ($(BOARD_USES_DOUBLE_TAP_CTRL),true) + KBUILD_OPTIONS += CONFIG_BOARD_USES_DOUBLE_TAP_CTRL=y +endif + +include $(CLEAR_VARS) +ifneq ($(BOARD_USES_DOUBLE_TAP),) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/sensors_class.ko +endif +ifneq ($(BOARD_USES_PEN_NOTIFIER),) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/bu520xx_pen.ko +endif +LOCAL_MODULE := ili9882_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KBUILD_OPTIONS += CONFIG_INPUT_TOUCHSCREEN_MMI=y + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +endif +ifneq ($(MOTO_PANEL_CHECK_TOUCH_STATE_V2),) + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/msm_drm.ko +endif +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +KBUILD_OPTIONS_GKI += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/ili9882_mmi/Kbuild b/drivers/input/touchscreen/ili9882_mmi/Kbuild new file mode 100644 index 000000000000..f3a8915dc471 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/Kbuild @@ -0,0 +1,85 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/ili9882_mmi \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/ili9882_mmi/firmware + +ifeq ($(filter m y, $(CONFIG_TOUCHSCREEN_ILITEK_CHIPSET)),) + obj-m += ili9882_mmi.o +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_ILI_0FLASH_MMI_ENABLE_DOUBLE_TAP)),) + EXTRA_CFLAGS += -DILI_SENSOR_EN + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_ILI_0FLASH_MMI_NOTIFY_TOUCH_STATE)),) + EXTRA_CFLAGS += -DILI_SET_TOUCH_STATE +endif + +ifneq ($(filter m y, $(CONFIG_BOARD_USES_DOUBLE_TAP_CTRL)),) + EXTRA_CFLAGS += -DCONFIG_BOARD_USES_DOUBLE_TAP_CTRL +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_ILI_PANEL_NOTIFICATIONS)),) +EXTRA_CFLAGS += -DILI_CONFIG_PANEL_NOTIFICATIONS +endif +ifneq ($(filter m y,$(CONFIG_ILITEK_RESUME_BY_DDI)),) +EXTRA_CFLAGS += -DILI_CONFIG_RESUME_BY_DDI +endif +ifneq ($(filter m y,$(CONFIG_ILITEK_FW_PANEL)),) +EXTRA_CFLAGS += -DILI_FW_PANEL +endif +ifneq ($(filter m y,$(CONFIG_ILITEK_SUSPEND_PWROFF)),) +EXTRA_CFLAGS += -DILI_SUSPEND_PWROFF +endif +ifneq ($(filter m y,$(CONFIG_ILITEK_CHARGER)),) +EXTRA_CFLAGS += -DILI_CONFIG_CHARGER +endif +ifneq ($(filter m y,$(CONFIG_ILITEK_ESD)),) +EXTRA_CFLAGS += -DILI_CONFIG_ESD +endif +ifneq ($(filter m y,$(CONFIG_ILITEK_GESTURE)),) +EXTRA_CFLAGS += -DILI_CONFIG_GESTURE +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_ILITEK_MMI_PEN_NOTIFIER)),) + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../misc/pen/$(GKI_OBJ_MODULE_DIR)/Module.symvers + EXTRA_CFLAGS += -DILITEK_PEN_NOTIFIER +endif + +ifneq ($(filter m y,$(CONFIG_ILITEK_PANEL_GESTURE)),) +EXTRA_CFLAGS += -DILI_CONFIG_PANEL_GESTURE +endif + +ifneq ($(filter m y,$(CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_EVENT_NOTIFICATIONS +endif + +ifneq ($(filter m y,$(CONFIG_ILITEK_PASSIVE_PEN)),) +EXTRA_CFLAGS += -DILI_PASSIVE_PEN +endif + +ifneq ($(filter m y,$(CONFIG_ILI_TOUCH_LAST_TIME)),) + EXTRA_CFLAGS += -DILI_TOUCH_LAST_TIME +endif + +ili9882_mmi-objs += ili9882.o +ili9882_mmi-objs += ili9882_spi.o +ili9882_mmi-objs += ili9882_qcom.o +ili9882_mmi-objs += ili9882_ic.o +ili9882_mmi-objs += ili9882_touch.o +ili9882_mmi-objs += ili9882_mp.o +ili9882_mmi-objs += ili9882_hostdl.o +ili9882_mmi-objs += ili9882_node.o +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers + EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI + ili9882_mmi-objs += ili_mmi_class.o +endif +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/../../kernel/msm-$(MODULE_KERNEL_VERSION)/Module.symvers + +ifneq ($(filter m y,$(CONFIG_GTP_DDA_STYLUS)),) + EXTRA_CFLAGS += -DCONFIG_MOTO_DDA_PASSIVESTYLUS + ili9882_mmi-objs += moto_ts_dda.o +endif diff --git a/drivers/input/touchscreen/ili9882_mmi/Makefile b/drivers/input/touchscreen/ili9882_mmi/Makefile new file mode 100644 index 000000000000..7f515f8e71c0 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/Makefile @@ -0,0 +1,19 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(M)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(M)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(M)/../../../misc/pen/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(OUT_DIR)/../../vendor/qcom/opensource/display-drivers/msm/Module.symvers diff --git a/drivers/input/touchscreen/ili9882_mmi/firmware/ili9882_fw.h b/drivers/input/touchscreen/ili9882_mmi/firmware/ili9882_fw.h new file mode 100755 index 000000000000..70551398f81c --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/firmware/ili9882_fw.h @@ -0,0 +1,169 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ILI9881X_FW_H +#define __ILI9881X_FW_H + +/* define names and paths for the variety of tp modules */ +#define DEF_INI_NAME_PATH "/sdcard/mp.ini" +#define DEF_FW_FILP_PATH "/sdcard/ILITEK_FW" +#define DEF_INI_REQUEST_PATH "mp.ini" +#define DEF_FW_REQUEST_PATH "ILITEK_FW" +static unsigned char CTPM_FW_DEF[] = { +}; + +#define CSOT_INI_NAME_PATH "/sdcard/mp_csot.ini" +#define CSOT_FW_FILP_PATH "/sdcard/ILITEK_FW_CSOT" +#define CSOT_INI_REQUEST_PATH "mp_csot.ini" +#define CSOT_FW_REQUEST_PATH "ILITEK_FW_CSOT" +static unsigned char CTPM_FW_CSOT[] = { + 0xFF, +}; + +#define AUO_INI_NAME_PATH "/sdcard/mp_auo.ini" +#define AUO_FW_FILP_PATH "/sdcard/ILITEK_FW_AUO" +#define AUO_INI_REQUEST_PATH "mp_auo.ini" +#define AUO_FW_REQUEST_PATH "ILITEK_FW_AUO" +static unsigned char CTPM_FW_AUO[] = { + 0xFF, +}; + +#define BOE_INI_NAME_PATH "/sdcard/mp_boe.ini" +#define BOE_FW_FILP_PATH "/sdcard/ILITEK_FW_BOE" +#define BOE_INI_REQUEST_PATH "mp_boe.ini" +#define BOE_FW_REQUEST_PATH "ILITEK_FW_BOE" +static unsigned char CTPM_FW_BOE[] = { + 0xFF, +}; + +#define INX_INI_NAME_PATH "/sdcard/mp_inx.ini" +#define INX_FW_FILP_PATH "/sdcard/ILITEK_FW_INX" +#define INX_INI_REQUEST_PATH "mp_inx.ini" +#define INX_FW_REQUEST_PATH "ILITEK_FW_INX" +static unsigned char CTPM_FW_INX[] = { + 0xFF, +}; + +#define DJ_INI_NAME_PATH "/sdcard/mp_dj.ini" +#define DJ_FW_FILP_PATH "/sdcard/ILITEK_FW_DJ" +#define DJ_INI_REQUEST_PATH "mp_dj.ini" +#define DJ_FW_REQUEST_PATH "ILITEK_FW_DJ" +static unsigned char CTPM_FW_DJ[] = { + 0xFF, +}; + +#define TXD_INI_NAME_PATH "/sdcard/mp_txd.ini" +#define TXD_FW_FILP_PATH "/sdcard/ILITEK_FW_TXD" +#define TXD_INI_REQUEST_PATH "mp_txd.ini" +#define TXD_FW_REQUEST_PATH "ILITEK_FW_TXD" +static unsigned char CTPM_FW_TXD[] = { + 0xFF, +}; + +#define TXD_9882H_INI_NAME_PATH "/sdcard/mp_txd_9882h.ini" +#define TXD_9882H_FW_FILP_PATH "/sdcard/ILITEK_FW_TXD_9882H" +#define TXD_9882H_INI_REQUEST_PATH "mp_txd_9882h.ini" +#define TXD_9882H_FW_REQUEST_PATH "ILITEK_FW_TXD_9882H" +static unsigned char CTPM_FW_TXD_9882H[] = { + 0xFF, +}; + +#define TXD_9882N_INI_NAME_PATH "/sdcard/mp_txd_9882n.ini" +#define TXD_9882N_FW_FILP_PATH "/sdcard/ILITEK_FW_TXD_9882N" +#define TXD_9882N_INI_REQUEST_PATH "mp_txd_9882n.ini" +#define TXD_9882N_FW_REQUEST_PATH "ILITEK_FW_TXD_9882N" +static unsigned char CTPM_FW_TXD_9882N[] = { + 0xFF, +}; + +#define TXD_7806S_INI_NAME_PATH "/sdcard/mp_txd_7806s.ini" +#define TXD_7806S_FW_FILP_PATH "/sdcard/ILITEK_FW_TXD_7806S" +#define TXD_7806S_INI_REQUEST_PATH "mp_txd_7806s.ini" +#define TXD_7806S_FW_REQUEST_PATH "ILITEK_FW_TXD_7806S" +static unsigned char CTPM_FW_TXD_7806S[] = { + 0xFF, +}; + +#define TM_9882H_INI_NAME_PATH "/sdcard/mp_tm_9882h.ini" +#define TM_9882H_FW_FILP_PATH "/sdcard/ILITEK_FW_TM_9882H" +#define TM_9882H_INI_REQUEST_PATH "mp_tm_9882h.ini" +#define TM_9882H_FW_REQUEST_PATH "ILITEK_FW_TM_9882H" +static unsigned char CTPM_FW_TM_9882N[] = { + 0xFF, +}; + +#define TM_9882N_INI_NAME_PATH "/sdcard/mp_tm_9882n.ini" +#define TM_9882N_FW_FILP_PATH "/sdcard/ILITEK_FW_TM_9882N" +#define TM_9882N_INI_REQUEST_PATH "mp_tm_9882n.ini" +#define TM_9882N_FW_REQUEST_PATH "ILITEK_FW_TM_9882N" +static unsigned char CTPM_FW_TM_9882H[] = { + 0xFF, +}; + +#define TM_7807S_INI_NAME_PATH "/sdcard/mp_tm_7807s.ini" +#define TM_7807S_FW_FILP_PATH "/sdcard/ILITEK_FW_TM_7807S" +#define TM_7807S_INI_REQUEST_PATH "mp_tm_7807s.ini" +#define TM_7807S_FW_REQUEST_PATH "ILITEK_FW_TM_7807S" +static unsigned char CTPM_FW_TM_7807S[] = { + 0xFF, +}; + +#define CSOT_7807S_INI_NAME_PATH "/sdcard/mp_csot_7807s.ini" +#define CSOT_7807S_FW_FILP_PATH "/sdcard/ILITEK_FW_CSOT_7807S" +#define CSOT_7807S_INI_REQUEST_PATH "mp_csot_7807s.ini" +#define CSOT_7807S_FW_REQUEST_PATH "ILITEK_FW_CSOT_7807S" +static unsigned char CTPM_FW_CSOT_7807S[] = { + 0xFF, +}; + +#define TXD_7807S_INI_NAME_PATH "/sdcard/mp_txd_7807s.ini" +#define TXD_7807S_FW_FILP_PATH "/sdcard/ILITEK_FW_TXD_7807S" +#define TXD_7807S_INI_REQUEST_PATH "mp_txd_7807s.ini" +#define TXD_7807S_FW_REQUEST_PATH "ILITEK_FW_TXD_7807S" +static unsigned char CTPM_FW_TXD_7807S[] = { + 0xFF, +}; + +#define TIANMA_9882N_INI_NAME_PATH "/sdcard/mp_tianma_9882n.ini" +#define TIANMA_9882N_FW_FILP_PATH "/sdcard/ILITEK_FW_TIANMA_9882N" +#define TIANMA_9882N_INI_REQUEST_PATH "mp_tianma_9882n.ini" +#define TIANMA_9882N_FW_REQUEST_PATH "ILITEK_FW_TIANMA_9882N" +static unsigned char CTPM_FW_TIANMA_9882N[] = { + 0xFF, +}; + +#define TM_INI_NAME_PATH "/sdcard/mp_tm.ini" +#define TM_FW_FILP_PATH "/sdcard/ILITEK_FW_TM" +#define TM_INI_REQUEST_PATH "mp_tm.ini" +#define TM_FW_REQUEST_PATH "ILITEK_FW_TM.hex" +static unsigned char CTPM_FW_TM[] = { + 0xFF, +}; + +#define HLT_INI_NAME_PATH "/sdcard/mp_hlt.ini" +#define HLT_FW_FILP_PATH "/sdcard/ILITEK_FW_HLT.hex" +#define HLT_INI_REQUEST_PATH "mp_hlt.ini" +#define HLT_FW_REQUEST_PATH "ILITEK_FW_HLT.hex" +static unsigned char CTPM_FW_HLT[] = { +}; + +#endif diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882.c b/drivers/input/touchscreen/ili9882_mmi/ili9882.c new file mode 100644 index 000000000000..a8470dc319e1 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882.c @@ -0,0 +1,1324 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "firmware/ili9882_fw.h" +#include "ili9882.h" +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS +#include "moto_ts_dda.h" +#endif + + +/* Debug level */ +bool debug_en = DEBUG_OUTPUT; +EXPORT_SYMBOL(debug_en); + +static struct workqueue_struct *esd_wq; +static struct workqueue_struct *bat_wq; +static struct delayed_work esd_work; +static struct delayed_work bat_work; + +#if RESUME_BY_DDI +static int ili_pinctrl_select_normal(struct ilitek_ts_data *ts); +static struct workqueue_struct *resume_by_ddi_wq; +static struct work_struct resume_by_ddi_work; + +static void ilitek_resume_by_ddi_work(struct work_struct *work) +{ + mutex_lock(&ilits->touch_mutex); + +#ifdef ILI_SENSOR_EN + if (ilits->wakeable) { + disable_irq_wake(ilits->irq_num); + ilits->gesture_enabled = false; + ilits->wakeable = false; + } +#else + if (ilits->gesture) + disable_irq_wake(ilits->irq_num); +#endif + +#ifdef ILI_SUSPEND_PWROFF + ili_pinctrl_select_normal(ilits); +#endif + + /* Set tp as demo mode and reload code if it's iram. */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + if (ilits->fw_upgrade_mode == UPGRADE_IRAM) + ili_fw_upgrade_handler(NULL); + else + ili_reset_ctrl(ilits->reset); + +#ifndef ILI_SUSPEND_PWROFF + ili_pinctrl_select_normal(ilits); +#endif + + ili_irq_enable(); + ILI_INFO("TP resume end by wq\n"); + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + + mutex_unlock(&ilits->touch_mutex); +} + +void ili_resume_by_ddi(void) +{ + if (!resume_by_ddi_wq) { + ILI_INFO("resume_by_ddi_wq is null\n"); + return; + } + +// mutex_lock(&ilits->touch_mutex); + + ILI_INFO("TP resume start called by ddi\n"); + ilits->tp_suspend = false; + /* + * To match the timing of sleep out, the first of mipi cmd must be sent within 10ms + * after TP reset. We then create a wq doing host download before resume. + */ + atomic_set(&ilits->fw_stat, ENABLE); + ili_reset_ctrl(ilits->reset); + ili_ice_mode_ctrl(ENABLE, OFF); + ilits->ddi_rest_done = true; + mdelay(5); + queue_work(resume_by_ddi_wq, &(resume_by_ddi_work)); + ILI_INFO("TP resume work queued\n"); + +// mutex_unlock(&ilits->touch_mutex); +} +#endif + +int ili_mp_test_handler(char *apk, bool lcm_on) +{ + int ret = 0; + + if (atomic_read(&ilits->fw_stat)) { + ILI_ERR("fw upgrade processing, ignore\n"); + return -EMP_FW_PROC; + } + + atomic_set(&ilits->mp_stat, ENABLE); + + if (ilits->actual_tp_mode != P5_X_FW_TEST_MODE) { + ret = ili_switch_tp_mode(P5_X_FW_TEST_MODE); + if (ret < 0) { + ILI_ERR("Switch MP mode failed\n"); + ret = -EMP_MODE; + goto out; + } + } + + ret = ili_mp_test_main(apk, lcm_on); + +out: + /* + * If there's running mp test with lcm off, we suspose that + * users will soon call resume from suspend. TP mode will be changed + * from MP to AP mode until resume finished. + */ + if (!lcm_on) { + atomic_set(&ilits->mp_stat, DISABLE); + return ret; + } + + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + if (ilits->fw_upgrade_mode == UPGRADE_IRAM) { + if (ili_fw_upgrade_handler(NULL) < 0) + ILI_ERR("FW upgrade failed during mp test\n"); + } else { + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP Reset failed during mp test\n"); + } + + atomic_set(&ilits->mp_stat, DISABLE); + return ret; +} + +int ili_switch_tp_mode(u8 mode) +{ + int ret = 0; + bool ges_dbg = false; + + atomic_set(&ilits->tp_sw_mode, START); + + ilits->actual_tp_mode = mode; + + /* able to see cdc data in gesture mode */ + if (ilits->tp_data_format == DATA_FORMAT_DEBUG && + ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) + ges_dbg = true; + + switch (ilits->actual_tp_mode) { + case P5_X_FW_AP_MODE: + ILI_INFO("Switch to AP mode\n"); + ilits->wait_int_timeout = AP_INT_TIMEOUT; + if (ilits->fw_upgrade_mode == UPGRADE_IRAM) { + if (ili_fw_upgrade_handler(NULL) < 0) + ILI_ERR("FW upgrade failed\n"); + } else { + ret = ili_reset_ctrl(ilits->reset); + } + if (ret < 0) + ILI_ERR("TP Reset failed\n"); + + break; + case P5_X_FW_GESTURE_MODE: + ILI_INFO("Switch to Gesture mode\n"); + ilits->wait_int_timeout = AP_INT_TIMEOUT; + ret = ilits->gesture_move_code(ilits->gesture_mode); + if (ret < 0) + ILI_ERR("Move gesture code failed\n"); + if (ges_dbg) { + ILI_INFO("Enable gesture debug func\n"); + ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEBUG, false, NULL); + } + break; + case P5_X_FW_TEST_MODE: + ILI_INFO("Switch to Test mode\n"); + ilits->wait_int_timeout = MP_INT_TIMEOUT; + ret = ilits->mp_move_code(); + break; + default: + ILI_ERR("Unknown TP mode: %x\n", mode); + ret = -1; + break; + } + + if (ret < 0) + ILI_ERR("Switch TP mode (%d) failed \n", mode); + + ILI_DBG("Actual TP mode = %d\n", ilits->actual_tp_mode); + atomic_set(&ilits->tp_sw_mode, END); + return ret; +} + +int ili_gesture_recovery(void) +{ + int ret = 0; + + atomic_set(&ilits->esd_stat, START); + + ILI_INFO("Doing gesture recovery\n"); + ret = ilits->ges_recover(); + + atomic_set(&ilits->esd_stat, END); + return ret; +} + +void ili_spi_recovery(void) +{ + atomic_set(&ilits->esd_stat, START); + + ILI_INFO("Doing spi recovery\n"); + if (ili_fw_upgrade_handler(NULL) < 0) + ILI_ERR("FW upgrade failed\n"); + + atomic_set(&ilits->esd_stat, END); +} + +int ili_wq_esd_spi_check(void) +{ + int ret = 0; + u8 tx = SPI_WRITE, rx = 0; + + ret = ilits->spi_write_then_read(ilits->spi, &tx, 1, &rx, 1); + ILI_DBG("spi esd check = 0x%x\n", ret); + if (ret == DO_SPI_RECOVER) { + ILI_ERR("ret = 0x%x\n", ret); + return -1; + } + return 0; +} + +int ili_wq_esd_i2c_check(void) +{ + ILI_DBG(""); + return 0; +} + +static void ilitek_tddi_wq_esd_check(struct work_struct *work) +{ + if (mutex_is_locked(&ilits->touch_mutex)) { + ILI_INFO("touch is locked, ignore\n"); + return; + } + mutex_lock(&ilits->touch_mutex); + if (!ilits->tp_suspend && ilits->esd_recover() < 0) { + ILI_ERR("SPI ACK failed, doing spi recovery\n"); + ili_spi_recovery(); + } + mutex_unlock(&ilits->touch_mutex); + complete_all(&ilits->esd_done); + ili_wq_ctrl(WQ_ESD, ENABLE); +} +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) +static int read_power_status(u8 *buf) +{ + struct file *f = NULL; + mm_segment_t old_fs; + ssize_t byte = 0; + + old_fs = get_fs(); + set_fs(get_ds()); + + f = filp_open(POWER_STATUS_PATH, O_RDONLY, 0); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open %s\n", POWER_STATUS_PATH); + return -1; + } + + f->f_op->llseek(f, 0, SEEK_SET); + byte = f->f_op->read(f, buf, 20, &f->f_pos); + + ILI_DBG("Read %d bytes\n", (int)byte); + + set_fs(old_fs); + filp_close(f, NULL); + return 0; +} +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) +static int read_power_status(u8 *buf) +{ + struct file *f = NULL; + ssize_t byte = 0; + loff_t pos = 0; + + f = filp_open(POWER_STATUS_PATH, O_RDONLY, 0); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open %s\n", POWER_STATUS_PATH); + return -1; + } + + kernel_read(f, buf, 20, &pos); + ILI_DBG("Read %d bytes\n", (int)byte); + + filp_close(f, NULL); + return 0; +} +#endif + +static void ilitek_tddi_wq_bat_check(struct work_struct *work) +{ + u8 str[20] = {0}; + static int charge_mode; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) + if (read_power_status(str) < 0) + ILI_ERR("Read power status failed\n"); +#endif + + ILI_DBG("Batter Status: %s\n", str); + + if (strstr(str, "Charging") != NULL || strstr(str, "Full") != NULL + || strstr(str, "Fully charged") != NULL) { + if (charge_mode != 1) { + ILI_DBG("Charging mode\n"); + if (ili_ic_func_ctrl("plug", DISABLE) < 0) // plug in + ILI_ERR("Write plug in failed\n"); + charge_mode = 1; + } + } else { + if (charge_mode != 2) { + ILI_DBG("Not charging mode\n"); + if (ili_ic_func_ctrl("plug", ENABLE) < 0) // plug out + ILI_ERR("Write plug out failed\n"); + charge_mode = 2; + } + } + ili_wq_ctrl(WQ_BAT, ENABLE); +} + +void ili_wq_ctrl(int type, int ctrl) +{ + switch (type) { + case WQ_ESD: + if (ENABLE_WQ_ESD || ilits->wq_ctrl) { + if (!esd_wq) { + ILI_ERR("wq esd is null\n"); + break; + } + ilits->wq_esd_ctrl = ctrl; + if (ctrl == ENABLE) { + ILI_DBG("execute esd check\n"); + if (!queue_delayed_work(esd_wq, &esd_work, msecs_to_jiffies(WQ_ESD_DELAY))) + ILI_DBG("esd check was already on queue\n"); + } else { + cancel_delayed_work_sync(&esd_work); + flush_workqueue(esd_wq); + ILI_DBG("cancel esd wq\n"); + } + } + break; + case WQ_BAT: + if (ENABLE_WQ_BAT || ilits->wq_ctrl) { + if (!bat_wq) { + ILI_ERR("WQ BAT is null\n"); + break; + } + ilits->wq_bat_ctrl = ctrl; + if (ctrl == ENABLE) { + ILI_DBG("execute bat check\n"); + if (!queue_delayed_work(bat_wq, &bat_work, msecs_to_jiffies(WQ_BAT_DELAY))) + ILI_DBG("bat check was already on queue\n"); + } else { + cancel_delayed_work_sync(&bat_work); + flush_workqueue(bat_wq); + ILI_DBG("cancel bat wq\n"); + } + } + break; + default: + ILI_ERR("Unknown WQ type, %d\n", type); + break; + } +} + +static void ilitek_tddi_wq_init(void) +{ + esd_wq = alloc_workqueue("esd_check", WQ_MEM_RECLAIM, 0); + bat_wq = alloc_workqueue("bat_check", WQ_MEM_RECLAIM, 0); + + WARN_ON(!esd_wq); + WARN_ON(!bat_wq); + + INIT_DELAYED_WORK(&esd_work, ilitek_tddi_wq_esd_check); + INIT_DELAYED_WORK(&bat_work, ilitek_tddi_wq_bat_check); + +#if RESUME_BY_DDI + resume_by_ddi_wq = create_singlethread_workqueue("resume_by_ddi_wq"); + WARN_ON(!resume_by_ddi_wq); + INIT_WORK(&resume_by_ddi_work, ilitek_resume_by_ddi_work); +#endif +} +static int ili_pinctrl_init(struct ilitek_ts_data *ts) +{ + int ret = 0; + + ts->pinctrl = devm_pinctrl_get(ts->dev); + if (IS_ERR_OR_NULL(ts->pinctrl)) { + ILI_ERR("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_int_active"); + if (IS_ERR_OR_NULL(ts->pins_active)) { + ILI_ERR("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_int_suspend_high"); + if (IS_ERR_OR_NULL(ts->pins_suspend)) { + ILI_ERR("Pin state[suspend] not found"); + ret = PTR_ERR(ts->pins_suspend); + } + + return 0; +err_pinctrl_lookup: + if (ts->pinctrl) { + devm_pinctrl_put(ts->pinctrl); + } +err_pinctrl_get: + ts->pinctrl = NULL; + ts->pins_suspend = NULL; + ts->pins_active = NULL; + return ret; +} + +static int ili_pinctrl_select_normal(struct ilitek_ts_data *ts) +{ + int ret = 0; + + if (ts->pinctrl && ts->pins_active) { + ret = pinctrl_select_state(ts->pinctrl, ts->pins_active); + if (ret < 0) { + ILI_ERR("Set normal pin state error:%d", ret); + } + } + + return ret; +} + +static int ili_pinctrl_select_suspend(struct ilitek_ts_data *ts) +{ + int ret = 0; + + if (ts->pinctrl && ts->pins_suspend) { + ret = pinctrl_select_state(ts->pinctrl, ts->pins_suspend); + if (ret < 0) { + ILI_ERR("Set suspend pin state error:%d", ret); + } + } + + return ret; +} + +int ili_sleep_handler(int mode) +{ + int ret = 0; + bool sense_stop = true; + + mutex_lock(&ilits->touch_mutex); + atomic_set(&ilits->tp_sleep, START); + + if (atomic_read(&ilits->fw_stat) || + atomic_read(&ilits->mp_stat)) { + ILI_INFO("fw upgrade or mp still running, ignore sleep requst\n"); + atomic_set(&ilits->tp_sleep, END); + mutex_unlock(&ilits->touch_mutex); + return 0; + } + + ili_wq_ctrl(WQ_ESD, DISABLE); + ili_wq_ctrl(WQ_BAT, DISABLE); + ili_irq_disable(); + + ILI_INFO("Sleep Mode = %d\n", mode); + + if (ilits->ss_ctrl) + sense_stop = true; + else if ((ilits->chip->core_ver >= CORE_VER_1430)) + sense_stop = false; + else + sense_stop = true; + + switch (mode) { + case TP_SUSPEND: + ILI_INFO("TP suspend start\n"); + if (sense_stop) { + if (ili_ic_func_ctrl("sense", DISABLE) < 0) + ILI_ERR("Write sense stop cmd failed\n"); + + if (ili_ic_check_busy(50, 20) < 0) + ILI_ERR("Check busy timeout during suspend\n"); + } + +#if defined(ILI_SENSOR_EN) || defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + if (ilits->should_enable_gesture) { + ili_switch_tp_mode(P5_X_FW_GESTURE_MODE); + enable_irq_wake(ilits->irq_num); + ili_irq_enable(); + ilits->wakeable = true; + } else { + if (ili_ic_func_ctrl("sleep", DEEP_SLEEP_IN) < 0) + ILI_ERR("Write sleep in cmd failed\n"); + ilits->wakeable = false; + } +#else + if (ilits->gesture) { + ili_switch_tp_mode(P5_X_FW_GESTURE_MODE); + enable_irq_wake(ilits->irq_num); + ili_irq_enable(); + } else { + ili_pinctrl_select_suspend(ilits); + if (ili_ic_func_ctrl("sleep", SLEEP_IN) < 0) + ILI_ERR("Write sleep in cmd failed\n"); + } +#endif + ILI_INFO("TP suspend end\n"); + ilits->tp_suspend = true; + break; + case TP_DEEP_SLEEP: + ILI_INFO("TP deep suspend start\n"); + if (sense_stop) { + if (ili_ic_func_ctrl("sense", DISABLE) < 0) + ILI_ERR("Write sense stop cmd failed\n"); + + if (ili_ic_check_busy(50, 20) < 0) + ILI_ERR("Check busy timeout during deep suspend\n"); + } +#ifndef ILI_SENSOR_EN + if (ilits->gesture) { + ili_switch_tp_mode(P5_X_FW_GESTURE_MODE); + enable_irq_wake(ilits->irq_num); + ili_irq_enable(); + } else +#endif + { + ili_pinctrl_select_suspend(ilits); + +#ifdef ILI_SUSPEND_PWROFF + gpio_direction_output(ilits->tp_rst, 1); + mdelay(1); + gpio_set_value(ilits->tp_rst, 0); + mdelay(5); +#else + if (ili_ic_func_ctrl("sleep", DEEP_SLEEP_IN) < 0) + ILI_ERR("Write deep sleep in cmd failed\n"); +#endif + + } + ILI_INFO("TP deep suspend end\n"); + ilits->tp_suspend = true; + break; + case TP_RESUME: +#if !RESUME_BY_DDI + ILI_INFO("TP resume start\n"); + ilits->tp_suspend = false; + + if (ilits->gesture) + disable_irq_wake(ilits->irq_num); + else + ili_pinctrl_select_normal(ilits); + /* Set tp as demo mode and reload code if it's iram. */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + if (ilits->fw_upgrade_mode == UPGRADE_IRAM) { + if (ili_fw_upgrade_handler(NULL) < 0) + ILI_ERR("FW upgrade failed during resume\n"); + } else { + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP Reset failed during resume\n"); + ili_ic_func_ctrl_reset(); + } + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + +#ifdef ILI_SENSOR_EN + if (ilits->wakeable) { + disable_irq_wake(ilits->irq_num); + ilits->gesture_enabled = false; + ilits->wakeable = false; + } +#endif + ILI_INFO("TP resume end\n"); +#else + ili_resume_by_ddi(); +#endif + ili_irq_enable(); + break; + default: + ILI_ERR("Unknown sleep mode, %d\n", mode); + ret = -EINVAL; + break; + } + + ili_touch_release_all_point(); + atomic_set(&ilits->tp_sleep, END); + mutex_unlock(&ilits->touch_mutex); + return ret; +} + +int ili_fw_upgrade_handler(void *data) +{ + int ret = 0; + + atomic_set(&ilits->fw_stat, START); + + ilits->fw_update_stat = FW_STAT_INIT; + ret = ili_fw_upgrade(ilits->fw_open); + if (ret != 0) { + ILI_INFO("FW upgrade fail\n"); + ilits->fw_update_stat = FW_UPDATE_FAIL; + } else { + ILI_INFO("FW upgrade pass\n"); +#if CHARGER_NOTIFIER_CALLBACK +#if KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE + /* add_for_charger_start */ + if((ilits->usb_plug_status)&&(ilits->actual_tp_mode !=P5_X_FW_TEST_MODE)) { + ret = ili_ic_func_ctrl("plug", !ilits->usb_plug_status);// plug in + if(ret<0) { + ILI_ERR("Write plug in failed\n"); + } + } + /* add_for_charger_end */ +#endif +#endif + ilits->fw_update_stat = FW_UPDATE_PASS; + } + + if (!ilits->boot) { + ilits->boot = true; + ILI_INFO("Registre touch to input subsystem\n"); + ili_input_register(); + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + } + + atomic_set(&ilits->fw_stat, END); + return ret; +} + +int ili_set_tp_data_len(int format, bool send, u8* data) +{ + u8 cmd[10] = {0}, ctrl = 0, debug_ctrl = 0; + u16 self_key = 2; + int ret = 0, tp_mode = ilits->actual_tp_mode, len = 0; + + switch (format) { + case DATA_FORMAT_DEMO: + if (TOUCH_WIDTH) + len = P5_X_5B_LOW_RESOLUTION_LENGTH + P5_X_CUSTOMER_LENGTH; + else + len = P5_X_DEMO_MODE_PACKET_LEN; + + ctrl = DATA_FORMAT_DEMO_CMD; + break; + case DATA_FORMAT_GESTURE_DEMO: + len = P5_X_DEMO_MODE_PACKET_LEN; + ctrl = DATA_FORMAT_DEMO_CMD; + break; + case DATA_FORMAT_DEBUG: + len = (2 * ilits->xch_num * ilits->ych_num) + (ilits->stx * 2) + (ilits->srx * 2); + len += 2 * self_key + (8 * 2) + 1 + 35; + if (TOUCH_WIDTH) + len += P5_X_CUSTOMER_LENGTH; + + ctrl = DATA_FORMAT_DEBUG_CMD; + break; + + case DATA_FORMAT_GESTURE_DEBUG: + len = (2 * ilits->xch_num * ilits->ych_num) + (ilits->stx * 2) + (ilits->srx * 2); + len += 2 * self_key + (8 * 2) + 1 + 35; + ctrl = DATA_FORMAT_DEBUG_CMD; + break; + case DATA_FORMAT_DEMO_DEBUG_INFO: + /*only suport SPI interface now, so defult use size 1024 buffer*/ + len = P5_X_DEMO_MODE_PACKET_LEN + + P5_X_DEMO_DEBUG_INFO_ID0_LENGTH + P5_X_INFO_HEADER_LENGTH; + ctrl = DATA_FORMAT_DEMO_DEBUG_INFO_CMD; + break; + case DATA_FORMAT_GESTURE_INFO: + len = P5_X_GESTURE_INFO_LENGTH; + ctrl = DATA_FORMAT_GESTURE_INFO_CMD; + break; + case DATA_FORMAT_GESTURE_NORMAL: + len = P5_X_GESTURE_NORMAL_LENGTH; + ctrl = DATA_FORMAT_GESTURE_NORMAL_CMD; + break; + case DATA_FORMAT_GESTURE_SPECIAL_DEMO: + if (ilits->gesture_demo_ctrl == ENABLE) { + if (ilits->gesture_mode == DATA_FORMAT_GESTURE_INFO) + len = P5_X_GESTURE_INFO_LENGTH + P5_X_INFO_HEADER_LENGTH + P5_X_INFO_CHECKSUM_LENGTH; + else + len = P5_X_DEMO_MODE_PACKET_LEN + P5_X_INFO_HEADER_LENGTH + P5_X_INFO_CHECKSUM_LENGTH; + } else { + if (ilits->gesture_mode == DATA_FORMAT_GESTURE_INFO) + len = P5_X_GESTURE_INFO_LENGTH; + else + len = P5_X_GESTURE_NORMAL_LENGTH; + } + ILI_INFO("Gesture demo mode control = %d\n", ilits->gesture_demo_ctrl); + ili_ic_func_ctrl("gesture_demo_en", ilits->gesture_demo_ctrl); + ILI_INFO("knock_en setting\n"); + ili_ic_func_ctrl("knock_en", 0x8); + break; + case DATA_FORMAT_DEBUG_LITE_ROI: + debug_ctrl = DATA_FORMAT_DEBUG_LITE_ROI_CMD; + ctrl = DATA_FORMAT_DEBUG_LITE_CMD; + break; + case DATA_FORMAT_DEBUG_LITE_WINDOW: + debug_ctrl = DATA_FORMAT_DEBUG_LITE_WINDOW_CMD; + ctrl = DATA_FORMAT_DEBUG_LITE_CMD; + break; + case DATA_FORMAT_DEBUG_LITE_AREA: + /*if(cmd == NULL) { + ILI_ERR("DATA_FORMAT_DEBUG_LITE_AREA error cmd\n"); + return -1; + }*/ + debug_ctrl = DATA_FORMAT_DEBUG_LITE_AREA_CMD; + ctrl = DATA_FORMAT_DEBUG_LITE_CMD; + cmd[3] = data[0]; + cmd[4] = data[1]; + cmd[5] = data[2]; + cmd[6] = data[3]; + break; + default: + ILI_ERR("Unknow TP data format\n"); + return -1; + } + + if (ctrl == DATA_FORMAT_DEBUG_LITE_CMD) { + len = P5_X_DEBUG_LITE_LENGTH; + cmd[0] = P5_X_MODE_CONTROL; + cmd[1] = ctrl; + cmd[2] = debug_ctrl; + ret = ilits->wrapper(cmd, 10, NULL, 0, ON, OFF); + + if (ret < 0) { + ILI_ERR("switch to format %d failed\n", format); + ili_switch_tp_mode(P5_X_FW_AP_MODE); + } + + } else if (tp_mode == P5_X_FW_AP_MODE || + format == DATA_FORMAT_GESTURE_DEMO || + format == DATA_FORMAT_GESTURE_DEBUG) { + cmd[0] = P5_X_MODE_CONTROL; + cmd[1] = ctrl; + ret = ilits->wrapper(cmd, 2, NULL, 0, ON, OFF); + + if (ret < 0) { + ILI_ERR("switch to format %d failed\n", format); + ili_switch_tp_mode(P5_X_FW_AP_MODE); + } + } else if (tp_mode == P5_X_FW_GESTURE_MODE) { + + /*set gesture symbol*/ + ili_set_gesture_symbol(); + + if (send) { + ret = ili_ic_func_ctrl("lpwg", ctrl); + if (ret < 0) + ILI_ERR("write gesture mode failed\n"); + } + } + + ilits->tp_data_format = format; + ilits->tp_data_len = len; + ILI_INFO("TP mode = %d, format = %d, len = %d\n", + tp_mode, ilits->tp_data_format, ilits->tp_data_len); + + return ret; +} + +int ili_report_handler(void) +{ + int ret = 0, pid = 0; + u8 checksum = 0, pack_checksum = 0; + u8 *trdata = NULL; + int rlen = 0; + int tmp = debug_en; + + /* Just in case these stats couldn't be blocked in top half context */ + if (!ilits->boot || !ilits->report || atomic_read(&ilits->tp_reset) || + atomic_read(&ilits->fw_stat) || atomic_read(&ilits->tp_sw_mode) || + atomic_read(&ilits->mp_stat) || atomic_read(&ilits->tp_sleep)) { + ILI_INFO("ignore report request\n"); + return -EINVAL; + } + + if (ilits->irq_after_recovery) { + ILI_INFO("ignore int triggered by recovery\n"); + ilits->irq_after_recovery = false; + return -EINVAL; + } + + ili_wq_ctrl(WQ_ESD, DISABLE); + ili_wq_ctrl(WQ_BAT, DISABLE); + + if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) { + PM_STAY_AWAKE(ilits->ws); + + if (ilits->pm_suspend) { + /* Waiting for pm resume completed */ + ret = wait_for_completion_timeout(&ilits->pm_completion, msecs_to_jiffies(700)); + if (!ret) { + ILI_ERR("system(spi) can't finished resuming procedure."); + } + } + } + + rlen = ilits->tp_data_len; + ILI_DBG("Packget length = %d\n", rlen); + + if (!rlen || rlen > TR_BUF_SIZE) { + ILI_ERR("Length of packet is invaild\n"); + goto out; + } + + memset(ilits->tr_buf, 0x0, TR_BUF_SIZE); + + ret = ilits->wrapper(NULL, 0, ilits->tr_buf, rlen, OFF, OFF); + if (ret < 0) { + ILI_ERR("Read report packet failed, ret = %d\n", ret); + if (ret == DO_SPI_RECOVER) { + ili_ic_get_pc_counter(DO_SPI_RECOVER); + if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE && ilits->gesture && !ilits->prox_near) { + ILI_ERR("Gesture failed, doing gesture recovery\n"); + if (ili_gesture_recovery() < 0) + ILI_ERR("Failed to recover gesture\n"); + ilits->irq_after_recovery = true; + } else { + ILI_ERR("SPI ACK failed, doing spi recovery\n"); + ili_spi_recovery(); + ilits->irq_after_recovery = true; + } + } + goto out; + } + + ili_dump_data(ilits->tr_buf, 8, rlen, 0, "finger report"); + + checksum = ili_calc_packet_checksum(ilits->tr_buf, rlen - 1); + pack_checksum = ilits->tr_buf[rlen-1]; + trdata = ilits->tr_buf; + pid = trdata[0]; + ILI_DBG("Packet ID = %x\n", pid); + + if (checksum != pack_checksum && pid != P5_X_I2CUART_PACKET_ID) { + ILI_ERR("Checksum Error (0x%X)! Pack = 0x%X, len = %d\n", checksum, pack_checksum, rlen); + debug_en = DEBUG_ALL; + ili_dump_data(trdata, 8, rlen, 0, "finger report with wrong"); + debug_en = tmp; + ret = -EINVAL; + goto out; + } + + if (pid == P5_X_INFO_HEADER_PACKET_ID) { + trdata = ilits->tr_buf + P5_X_INFO_HEADER_LENGTH; + pid = trdata[0]; + } + + switch (pid) { + case P5_X_DEMO_PACKET_ID: + ili_report_ap_mode(trdata, rlen); + break; + case P5_X_DEBUG_PACKET_ID: + ili_report_debug_mode(trdata, rlen); + break; + case P5_X_DEMO_HIGH_RESOLUTION_PACKET_ID: + ili_report_ap_mode(trdata, rlen); + break; + case P5_X_DEBUG_HIGH_RESOLUTION_PACKET_ID: + ili_report_debug_mode(trdata, rlen); + break; + case P5_X_DEBUG_LITE_PACKET_ID: + ili_report_debug_lite_mode(trdata, rlen); + break; + case P5_X_I2CUART_PACKET_ID: + ili_report_i2cuart_mode(trdata, rlen); + break; + case P5_X_GESTURE_PACKET_ID: + ili_report_gesture_mode(trdata, rlen); + break; + case P5_X_GESTURE_FAIL_ID: + ILI_INFO("gesture fail reason code = 0x%02x", trdata[1]); + break; + case P5_X_DEMO_DEBUG_INFO_PACKET_ID: + ili_demo_debug_info_mode(trdata, rlen); + break; + default: + ILI_ERR("Unknown packet id, %x\n", pid); +#if ( TDDI_INTERFACE == BUS_I2C ) + ili_ic_get_pc_counter(DO_I2C_RECOVER); + if (ilits->fw_latch !=0){ + msleep(100); + ili_ic_func_ctrl_reset(); + ILI_ERR("I2C func_ctrl_reset\n"); + } + if ((ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) && ilits->fw_latch !=0){ + msleep(100); + ili_set_gesture_symbol(); + ILI_ERR("I2C gesture_symbol\n"); + } +#endif + break; + } + +out: + if (ilits->actual_tp_mode != P5_X_FW_GESTURE_MODE) { + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + } + + if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) + PM_RELAX(ilits->ws); + return ret; +} + +int ili_reset_ctrl(int mode) +{ + int ret = 0; + + atomic_set(&ilits->tp_reset, START); + + switch (mode) { + case TP_IC_CODE_RST: + ILI_INFO("TP IC Code RST \n"); + ret = ili_ic_code_reset(); + if (ret < 0) + ILI_ERR("IC Code reset failed\n"); + break; + case TP_IC_WHOLE_RST: + ILI_INFO("TP IC whole RST\n"); + ret = ili_ic_whole_reset(); + if (ret < 0) + ILI_ERR("IC whole reset failed\n"); + break; + case TP_HW_RST_ONLY: + ILI_INFO("TP HW RST\n"); + ili_tp_reset(); + break; + default: + ILI_ERR("Unknown reset mode, %d\n", mode); + ret = -EINVAL; + break; + } + + /* + * Since OTP must be folloing with reset, except for code rest, + * the stat of ice mode should be set as 0. + */ + if (mode != TP_IC_CODE_RST) + atomic_set(&ilits->ice_stat, DISABLE); + + ilits->tp_data_format = DATA_FORMAT_DEMO; + if (TOUCH_WIDTH) + ilits->tp_data_len = P5_X_5B_LOW_RESOLUTION_LENGTH + P5_X_CUSTOMER_LENGTH; + else + ilits->tp_data_len = P5_X_DEMO_MODE_PACKET_LEN; + + ilits->pll_clk_wakeup = true; + atomic_set(&ilits->tp_reset, END); + return ret; +} + +static int ilitek_get_tp_module(void) +{ + /* + * TODO: users should implement this function + * if there are various tp modules been used in projects. + */ + + return ilits->tp_module; +} + +static void ili_update_tp_module_info(void) +{ + int module; + + module = ilitek_get_tp_module(); + + switch (module) { + case MODEL_CSOT: + ilits->md_name = "CSOT"; + ilits->md_fw_filp_path = CSOT_FW_FILP_PATH; + ilits->md_fw_rq_path = CSOT_FW_REQUEST_PATH; + ilits->md_ini_path = CSOT_INI_NAME_PATH; + ilits->md_ini_rq_path = CSOT_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_CSOT; + ilits->md_fw_ili_size = sizeof(CTPM_FW_CSOT); + break; + case MODEL_CSOT_7807S: + ilits->md_name = "CSOT_7807S"; + ilits->md_fw_filp_path = CSOT_7807S_FW_FILP_PATH; + ilits->md_fw_rq_path = CSOT_7807S_FW_REQUEST_PATH; + ilits->md_ini_path = CSOT_7807S_INI_NAME_PATH; + ilits->md_ini_rq_path = CSOT_7807S_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_CSOT_7807S; + ilits->md_fw_ili_size = sizeof(CTPM_FW_CSOT_7807S); + break; + case MODEL_TXD_7807S: + ilits->md_name = "TXD_7807S"; + ilits->md_fw_filp_path = TXD_7807S_FW_FILP_PATH; + ilits->md_fw_rq_path = TXD_7807S_FW_REQUEST_PATH; + ilits->md_ini_path = TXD_7807S_INI_NAME_PATH; + ilits->md_ini_rq_path = TXD_7807S_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_TXD_7807S; + ilits->md_fw_ili_size = sizeof(CTPM_FW_TXD_7807S); + break; + case MODEL_AUO: + ilits->md_name = "AUO"; + ilits->md_fw_filp_path = AUO_FW_FILP_PATH; + ilits->md_fw_rq_path = AUO_FW_REQUEST_PATH; + ilits->md_ini_path = AUO_INI_NAME_PATH; + ilits->md_ini_rq_path = AUO_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_AUO; + ilits->md_fw_ili_size = sizeof(CTPM_FW_AUO); + break; + case MODEL_BOE: + ilits->md_name = "BOE"; + ilits->md_fw_filp_path = BOE_FW_FILP_PATH; + ilits->md_fw_rq_path = BOE_FW_REQUEST_PATH; + ilits->md_ini_path = BOE_INI_NAME_PATH; + ilits->md_ini_rq_path = BOE_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_BOE; + ilits->md_fw_ili_size = sizeof(CTPM_FW_BOE); + break; + case MODEL_INX: + ilits->md_name = "INX"; + ilits->md_fw_filp_path = INX_FW_FILP_PATH; + ilits->md_fw_rq_path = INX_FW_REQUEST_PATH; + ilits->md_ini_path = INX_INI_NAME_PATH; + ilits->md_ini_rq_path = INX_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_INX; + ilits->md_fw_ili_size = sizeof(CTPM_FW_INX); + break; + case MODEL_DJ: + ilits->md_name = "DJ"; + ilits->md_fw_filp_path = DJ_FW_FILP_PATH; + ilits->md_fw_rq_path = DJ_FW_REQUEST_PATH; + ilits->md_ini_path = DJ_INI_NAME_PATH; + ilits->md_ini_rq_path = DJ_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_DJ; + ilits->md_fw_ili_size = sizeof(CTPM_FW_DJ); + break; + case MODEL_TXD: + ilits->md_name = "TXD"; + ilits->md_fw_filp_path = TXD_FW_FILP_PATH; + ilits->md_fw_rq_path = TXD_FW_REQUEST_PATH; + ilits->md_ini_path = TXD_INI_NAME_PATH; + ilits->md_ini_rq_path = TXD_FW_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_TXD; + ilits->md_fw_ili_size = sizeof(CTPM_FW_TXD); + break; + case MODEL_TXD_9882H: + ilits->md_name = "TXD_9882H"; + ilits->md_fw_filp_path = TXD_9882H_FW_FILP_PATH; + ilits->md_fw_rq_path = TXD_9882H_FW_REQUEST_PATH; + ilits->md_ini_path = TXD_9882H_INI_NAME_PATH; + ilits->md_ini_rq_path = TXD_9882H_FW_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_TXD_9882H; + ilits->md_fw_ili_size = sizeof(CTPM_FW_TXD_9882H); + break; + case MODEL_TXD_9882N: + ilits->md_name = "TXD_9882N"; + ilits->md_fw_filp_path = TXD_9882N_FW_FILP_PATH; + ilits->md_fw_rq_path = TXD_9882N_FW_REQUEST_PATH; + ilits->md_ini_path = TXD_9882N_INI_NAME_PATH; + ilits->md_ini_rq_path = TXD_9882N_FW_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_TXD_9882N; + ilits->md_fw_ili_size = sizeof(CTPM_FW_TXD_9882N); + break; + case MODEL_TXD_7806S: + ilits->md_name = "TXD_7806S"; + ilits->md_fw_filp_path = TXD_7806S_FW_FILP_PATH; + ilits->md_fw_rq_path = TXD_7806S_FW_REQUEST_PATH; + ilits->md_ini_path = TXD_7806S_INI_NAME_PATH; + ilits->md_ini_rq_path = TXD_7806S_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_TXD_7806S; + ilits->md_fw_ili_size = sizeof(CTPM_FW_TXD_7806S); + break; + case MODEL_TM: + ilits->md_name = "ILITEK_TDDI_TM"; + ilits->md_fw_filp_path = TM_FW_REQUEST_PATH; + ilits->md_fw_rq_path = TM_FW_REQUEST_PATH; + ilits->md_ini_path = TM_INI_NAME_PATH; + ilits->md_ini_rq_path = TM_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_TM; + ilits->md_fw_ili_size = sizeof(CTPM_FW_TM); + break; + case MODEL_TM_9882N: + ilits->md_name = "TM_9882N"; + ilits->md_fw_filp_path = TM_9882N_FW_FILP_PATH; + ilits->md_fw_rq_path = TM_9882N_FW_REQUEST_PATH; + ilits->md_ini_path = TM_9882N_INI_NAME_PATH; + ilits->md_ini_rq_path = TM_9882N_FW_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_TM_9882N; + ilits->md_fw_ili_size = sizeof(CTPM_FW_TM_9882N); + break; + case MODEL_TM_9882H: + ilits->md_name = "TM_9882H"; + ilits->md_fw_filp_path = TM_9882H_FW_FILP_PATH; + ilits->md_fw_rq_path = TM_9882H_FW_REQUEST_PATH; + ilits->md_ini_path = TM_9882H_INI_NAME_PATH; + ilits->md_ini_rq_path = TM_9882H_FW_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_TM_9882H; + ilits->md_fw_ili_size = sizeof(CTPM_FW_TM_9882H); + break; + case MODEL_TM_7807S: + ilits->md_name = "TM_7807S"; + ilits->md_fw_filp_path = TM_7807S_FW_FILP_PATH; + ilits->md_fw_rq_path = TM_7807S_FW_REQUEST_PATH; + ilits->md_ini_path = TM_7807S_INI_NAME_PATH; + ilits->md_ini_rq_path = TM_7807S_FW_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_TM_7807S; + ilits->md_fw_ili_size = sizeof(CTPM_FW_TM_7807S); + break; + case MODEL_TIANMA_9882N: + ilits->md_name = "TIANMA_9882N"; + ilits->md_fw_filp_path = TIANMA_9882N_FW_FILP_PATH; + ilits->md_fw_rq_path = TIANMA_9882N_FW_REQUEST_PATH; + ilits->md_ini_path = TIANMA_9882N_INI_NAME_PATH; + ilits->md_ini_rq_path = TIANMA_9882N_FW_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_TIANMA_9882N; + ilits->md_fw_ili_size = sizeof(CTPM_FW_TIANMA_9882N); + break; + case MODEL_HLT: + ilits->md_name = "ILITEK_TDDI_HLT"; + ilits->md_fw_filp_path = HLT_FW_REQUEST_PATH; + ilits->md_fw_rq_path = HLT_FW_REQUEST_PATH; + ilits->md_ini_path = HLT_INI_NAME_PATH; + ilits->md_ini_rq_path = HLT_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_HLT; + ilits->md_fw_ili_size = sizeof(CTPM_FW_HLT); + break; + default: + break; + } + + if (module == 0) { + ILI_ERR("Couldn't find any tp modules, applying default settings\n"); + ilits->md_name = "DEF"; + ilits->md_fw_filp_path = DEF_FW_FILP_PATH; + ilits->md_fw_rq_path = DEF_FW_REQUEST_PATH; + ilits->md_ini_path = DEF_INI_NAME_PATH; + ilits->md_ini_rq_path = DEF_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_DEF; + ilits->md_fw_ili_size = sizeof(CTPM_FW_DEF); + } + + ILI_INFO("Found %s module: ini path = %s, fw path = (%s, %s, %d)\n", + ilits->md_name, + ilits->md_ini_path, + ilits->md_fw_filp_path, + ilits->md_fw_rq_path, + ilits->md_fw_ili_size); +} + +int ili_tddi_init(void) +{ +#if (BOOT_FW_UPDATE | HOST_DOWN_LOAD) + struct task_struct *fw_boot_th; +#endif + + ILI_INFO("driver version = %s\n", DRIVER_VERSION); + + mutex_init(&ilits->touch_mutex); + mutex_init(&ilits->debug_mutex); + mutex_init(&ilits->debug_read_mutex); + init_waitqueue_head(&(ilits->inq)); + spin_lock_init(&ilits->irq_spin); + init_completion(&ilits->esd_done); + + //atomic_set(&ilits->irq_stat, DISABLE); + atomic_set(&ilits->ice_stat, DISABLE); + atomic_set(&ilits->tp_reset, END); + atomic_set(&ilits->fw_stat, END); + atomic_set(&ilits->mp_stat, DISABLE); + atomic_set(&ilits->tp_sleep, END); + atomic_set(&ilits->cmd_int_check, DISABLE); + atomic_set(&ilits->esd_stat, END); + + ili_ic_init(); + ilitek_tddi_wq_init(); + ili_pinctrl_init(ilits); + ili_pinctrl_select_normal(ilits); + /* Must do hw reset once in first time for work normally if tp reset is avaliable */ +#if !TDDI_RST_BIND + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP Reset failed during init\n"); +#endif + + ilits->demo_debug_info[0] = ili_demo_debug_info_id0; + ilits->tp_data_format = DATA_FORMAT_DEMO; + ilits->boot = false; + + /* + * This status of ice enable will be reset until process of fw upgrade runs. + * it might cause unknown problems if we disable ice mode without any + * codes inside touch ic. + */ + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Failed to enable ice mode during ili_tddi_init\n"); + + if ( ili_ic_dummy_check() < 0 ){ + ILI_ERR("Not found ilitek chip\n"); + return -ENODEV; + } + + if (ili_ic_get_info() < 0) + ILI_ERR("Chip info is incorrect\n"); + + ili_update_tp_module_info(); + + ili_node_init(); + + ili_fw_read_flash_info(); + +#if (BOOT_FW_UPDATE | HOST_DOWN_LOAD) + fw_boot_th = kthread_run(ili_fw_upgrade_handler, NULL, "ili_fw_boot"); + if (fw_boot_th == (struct task_struct *)ERR_PTR) { + fw_boot_th = NULL; + WARN_ON(!fw_boot_th); + ILI_ERR("Failed to create fw upgrade thread\n"); + } +#else + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Failed to disable ice mode failed during init\n"); + +#if (TDDI_INTERFACE == BUS_I2C) + ilits->info_from_hex = DISABLE; +#endif + + ili_ic_get_core_ver(); + ili_ic_get_protocl_ver(); + ili_ic_get_fw_ver(); + ili_ic_get_tp_info(); + ili_ic_get_panel_info(); + +#if (TDDI_INTERFACE == BUS_I2C) + ilits->info_from_hex = ENABLE; +#endif + + ILI_INFO("Registre touch to input subsystem\n"); + ili_input_register(); + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + ilits->boot = true; +#endif +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS + { + int err; + moto_dda_init("ilitek-ts for Geneva5G"); + err = moto_dda_register_cdevice(); + if (err) + ILI_ERR("Failed register stylus dda device, %d", err); + } +#endif + + PM_WAKEUP_REGISTER(NULL, ilits->ws, "ili_wakelock"); + if (!ilits->ws) { + ILI_ERR("wakeup source request failed\n"); + return -ENOMEM; + } + + return 0; +} + +void ili_dev_remove(void) +{ + ILI_INFO("remove ilitek dev\n"); + + if (!ilits) + return; + + gpio_free(ilits->tp_int); + gpio_free(ilits->tp_rst); + + if (esd_wq != NULL) { + cancel_delayed_work_sync(&esd_work); + flush_workqueue(esd_wq); + destroy_workqueue(esd_wq); + } + if (bat_wq != NULL) { + cancel_delayed_work_sync(&bat_work); + flush_workqueue(bat_wq); + destroy_workqueue(bat_wq); + } + + if (ilits->ws) + PM_WAKEUP_UNREGISTER(ilits->ws); + + kfree(ilits->tr_buf); + kfree(ilits->gcoord); + ili_interface_dev_exit(ilits); + +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS + moto_dda_exit(); +#endif + +} + +int ili_dev_init(struct ilitek_hwif_info *hwif) +{ + ILI_INFO("TP Interface: %s\n", (hwif->bus_type == BUS_I2C) ? "I2C" : "SPI"); + return ili_interface_dev_init(hwif); +} diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882.h b/drivers/input/touchscreen/ili9882_mmi/ili9882.h new file mode 100755 index 000000000000..1d75fcb5c847 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882.h @@ -0,0 +1,1290 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __ILI9882_H +#define __ILI9882_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_COMPAT +#include +#endif +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_OF +#include +#include +#include +#include +#include +#endif + +#ifdef CONFIG_FB +#include +#include +#endif + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + +#ifdef CONFIG_DRM_MSM +#include +#endif + +#ifdef ILI_SENSOR_EN +#include +#endif + +#ifdef CONFIG_DRM +#include +#endif + +#ifdef CONFIG_MTK_SPI +#include "mt_spi.h" +#include "sync_write.h" +#endif +#include +#include + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +#include +#endif + +#ifdef ILITEK_PEN_NOTIFIER +#include +#endif + +#define DRIVER_VERSION "3.0.3.0.200610" + +/* Options */ +#define TDDI_INTERFACE BUS_SPI /* BUS_I2C(0x18) or BUS_SPI(0x1C) */ +#define VDD_VOLTAGE 1800000 +#define VCC_VOLTAGE 1800000 +#define SPI_CLK 9 /* follow by clk list */ +#define SPI_RETRY 5 +#define IRQ_GPIO_NUM 66 +#define TR_BUF_SIZE (2*K) /* Buffer size of touch report */ +#define TR_BUF_LIST_SIZE (1*K) /* Buffer size of touch report for debug data */ +#define SPI_TX_BUF_SIZE 4096 +#define SPI_RX_BUF_SIZE 4096 +#define WQ_ESD_DELAY 4000 +#define WQ_BAT_DELAY 2000 +#define AP_INT_TIMEOUT 600 /*600ms*/ +#define MP_INT_TIMEOUT 5000 /*5s*/ +#define MT_B_TYPE ENABLE +#define TDDI_RST_BIND DISABLE +#ifdef ILI_PASSIVE_PEN +#define MT_PRESSURE ENABLE +#define TOUCH_WIDTH ENABLE +#else +#define MT_PRESSURE DISABLE +#define TOUCH_WIDTH DISABLE +#endif +#ifdef ILI_CONFIG_ESD +#define ENABLE_WQ_ESD ENABLE +#else +#define ENABLE_WQ_ESD DISABLE +#endif +#define ENABLE_WQ_BAT DISABLE +#if defined(ILI_CONFIG_GESTURE) || defined(ILI_CONFIG_PANEL_GESTURE) || defined(ILI_SENSOR_EN) +#define ENABLE_GESTURE ENABLE +#else +#define ENABLE_GESTURE DISABLE +#endif +#define REGULATOR_POWER DISABLE +#define TP_SUSPEND_PRIO ENABLE +#ifdef ILI_CONFIG_RESUME_BY_DDI +#define RESUME_BY_DDI ENABLE +#else +#define RESUME_BY_DDI DISABLE +#endif +#define BOOT_FW_UPDATE ENABLE +#define MP_INT_LEVEL DISABLE +#define PLL_CLK_WAKEUP_TP_RESUME DISABLE +#ifdef ILI_CONFIG_CHARGER +#define CHARGER_NOTIFIER_CALLBACK ENABLE +#else +#define CHARGER_NOTIFIER_CALLBACK DISABLE +#endif + +/*if current interface is spi, must to hostdownload */ +#if (TDDI_INTERFACE == BUS_SPI) +#define HOST_DOWN_LOAD ENABLE +#else +#define HOST_DOWN_LOAD DISABLE +#endif + +/* Plaform compatibility */ +#define CONFIG_PLAT_SPRD DISABLE +#define I2C_DMA_TRANSFER DISABLE +#define SPI_DMA_TRANSFER_SPLIT DISABLE +#define SPRD_SYSFS_SUSPEND_RESUME DISABLE + +/* Path */ +#define DEBUG_DATA_FILE_SIZE (10*K) +#define DEBUG_DATA_FILE_PATH "/sdcard/ILITEK_log.csv" +#define CSV_LCM_ON_PATH "/sdcard" +#define CSV_LCM_OFF_PATH "/sdcard" +#define POWER_STATUS_PATH "/sys/class/power_supply/battery/status" +#define DUMP_FLASH_PATH "/sdcard/flash_dump" +#define DUMP_IRAM_PATH "/sdcard/iram_dump" + +/* Debug messages */ +#define DEBUG_NONE 0 +#define DEBUG_ALL 1 +#define DEBUG_OUTPUT DEBUG_NONE + +#define ILI_INFO(fmt, arg...) \ +({ \ + pr_info("ILITEK: (%s, %d): " fmt, __func__, __LINE__, ##arg); \ +}) \ + +#define ILI_ERR(fmt, arg...) \ +({ \ + pr_err("ILITEK: (%s, %d): " fmt, __func__, __LINE__, ##arg); \ +}) \ + +extern bool debug_en; +#define ILI_DBG(fmt, arg...) \ +do { \ + if (debug_en) \ + pr_info("ILITEK: (%s, %d): " fmt, __func__, __LINE__, ##arg); \ +} while (0) + +#define GET_TS_DATA(dev) { \ + ts_data = dev_get_drvdata(dev); \ + if (!ts_data) { \ + ILI_ERR("Failed to get driver data"); \ + return -ENODEV; \ + } \ +} + +#define ERR_ALLOC_MEM(X) ((IS_ERR(X) || X == NULL) ? 1 : 0) +#define K (1024) +#define M (K * K) +#define ENABLE 1 +#define START 1 +#define ON 1 +#define ILI_WRITE 1 +#define ILI_READ 0 +#define DISABLE 0 +#define END 0 +#define OFF 0 +#define NONE -1 +#define DO_SPI_RECOVER -2 +#define DO_I2C_RECOVER -3 + +enum TP_SPI_CLK_LIST { + TP_SPI_CLK_1M = 1000000, + TP_SPI_CLK_2M = 2000000, + TP_SPI_CLK_3M = 3000000, + TP_SPI_CLK_4M = 4000000, + TP_SPI_CLK_5M = 5000000, + TP_SPI_CLK_6M = 6000000, + TP_SPI_CLK_7M = 7000000, + TP_SPI_CLK_8M = 8000000, + TP_SPI_CLK_9M = 9000000, + TP_SPI_CLK_10M = 10000000, + TP_SPI_CLK_11M = 11000000, + TP_SPI_CLK_12M = 12000000, + TP_SPI_CLK_13M = 13000000, + TP_SPI_CLK_14M = 14000000, + TP_SPI_CLK_15M = 15000000, +}; + +enum TP_PLAT_TYPE { + TP_PLAT_MTK = 0, + TP_PLAT_QCOM +}; + +enum TP_RST_METHOD { + TP_IC_WHOLE_RST = 0, + TP_IC_CODE_RST, + TP_HW_RST_ONLY, +}; + +enum TP_FW_UPGRADE_TYPE { + UPGRADE_FLASH = 0, + UPGRADE_IRAM +}; + +enum TP_FW_UPGRADE_STATUS { + FW_STAT_INIT = 0, + FW_UPDATING = 90, + FW_UPDATE_PASS = 100, + FW_UPDATE_FAIL = -1 +}; + +enum TP_FW_OPEN_METHOD { + REQUEST_FIRMWARE = 0, + FILP_OPEN +}; + +enum TP_SLEEP_STATUS { + TP_SUSPEND = 0, + TP_DEEP_SLEEP = 1, + TP_RESUME = 2 +}; + +enum TP_PROXIMITY_STATUS { + DDI_POWER_OFF = 0, + DDI_POWER_ON = 1, + WAKE_UP_GESTURE_RECOVERY = 2, + WAKE_UP_SWITCH_GESTURE_MODE = 3 +}; + +enum TP_SLEEP_CTRL { + SLEEP_IN = 0x0, + SLEEP_OUT = 0x1, + DEEP_SLEEP_IN = 0x3 +}; + +enum TP_FW_BLOCK_NUM { + AP = 1, + DATA = 2, + TUNING = 3, + GESTURE = 4, + MP = 5, + DDI = 6, + TAG = 7, + PARA_BACKUP = 8, + RESERVE_BLOCK3 = 9, + RESERVE_BLOCK4 = 10, + RESERVE_BLOCK5 = 11, + RESERVE_BLOCK6 = 12, + RESERVE_BLOCK7 = 13, + RESERVE_BLOCK8 = 14, + RESERVE_BLOCK9 = 15, + RESERVE_BLOCK10 = 16, +}; + +enum TP_FW_BLOCK_TAG { + BLOCK_TAG_AF = 0xAF, + BLOCK_TAG_B0 = 0xB0 +}; + +enum TP_WQ_TYPE { + WQ_ESD = 0, + WQ_BAT, +}; + +enum TP_RECORD_DATA { + DISABLE_RECORD = 0, + ENABLE_RECORD, + DATA_RECORD +}; + +enum TP_DATA_FORMAT { + DATA_FORMAT_DEMO = 0, + DATA_FORMAT_DEBUG, + DATA_FORMAT_DEMO_DEBUG_INFO, + DATA_FORMAT_GESTURE_SPECIAL_DEMO, + DATA_FORMAT_GESTURE_INFO, + DATA_FORMAT_GESTURE_NORMAL, + DATA_FORMAT_GESTURE_DEMO, + DATA_FORMAT_GESTURE_DEBUG, + DATA_FORMAT_DEBUG_LITE_ROI, + DATA_FORMAT_DEBUG_LITE_WINDOW, + DATA_FORMAT_DEBUG_LITE_AREA +}; + +enum NODE_MODE_SWITCH { + AP_MODE = 0, + TEST_MODE, + DEBUG_MODE, + DEBUG_LITE_ROI, + DEBUG_LITE_WINDOW, + DEBUG_LITE_AREA +}; + +//make sure define mode in sequence per panel vendor +//some panel need check vendor with model +enum TP_MODEL { + MODEL_DEF = 0, + MODEL_CSOT, + MODEL_CSOT_7807S, + MODEL_CSOT_END, //For csot panel check + MODEL_AUO, + MODEL_BOE, + MODEL_INX, + MODEL_DJ, + MODEL_TXD, + MODEL_TXD_9882H, + MODEL_TXD_9882N, + MODEL_TXD_7806S, + MODEL_TXD_7807S, + MODEL_TXD_END, //For txd panel check + MODEL_TM, + MODEL_TM_9882N, + MODEL_TM_9882H, + MODEL_TM_7807S, + MODEL_TIANMA_9882N, + MODEL_TM_END, //For tianma panel check + MODEL_HLT +}; + +enum TP_ERR_CODE { + EMP_CMD = 100, + EMP_PROTOCOL, + EMP_FILE, + EMP_INI, + EMP_TIMING_INFO, + EMP_INVAL, + EMP_PARSE, + EMP_NOMEM, + EMP_GET_CDC, + EMP_INT, + EMP_CHECK_BUY, + EMP_MODE, + EMP_FW_PROC, + EMP_FORMUL_NULL, + EMP_PARA_NULL, + EFW_CONVERT_FILE, + EFW_ICE_MODE, + EFW_CRC, + EFW_REST, + EFW_ERASE, + EFW_PROGRAM, + EFW_INTERFACE, +}; + +enum TP_IC_TYPE { + ILI_A = 0x0A, + ILI_B, + ILI_C, + ILI_D, + ILI_E, + ILI_F, + ILI_G, + ILI_H, + ILI_I, + ILI_J, + ILI_K, + ILI_L, + ILI_M, + ILI_N, + ILI_O, + ILI_P, + ILI_Q, + ILI_R, + ILI_S, + ILI_T, + ILI_U, + ILI_V, + ILI_W, + ILI_X, + ILI_Y, + ILI_Z, +}; + +struct gesture_symbol { + u8 double_tap :1; + u8 alphabet_line_2_top :1; + u8 alphabet_line_2_bottom :1; + u8 alphabet_line_2_left :1; + u8 alphabet_line_2_right :1; + u8 alphabet_m :1; + u8 alphabet_w :1; + u8 alphabet_c :1; + u8 alphabet_E :1; + u8 alphabet_V :1; + u8 alphabet_O :1; + u8 alphabet_S :1; + u8 alphabet_Z :1; + u8 alphabet_V_down :1; + u8 alphabet_V_left :1; + u8 alphabet_V_right :1; + u8 alphabet_two_line_2_bottom :1; + u8 alphabet_F :1; + u8 alphabet_AT :1; + u8 single_tap :1; + u8 reserve0 :4; +}; + +struct report_info_block { + u8 nReportByPixel :1; + u8 nIsHostDownload :1; + u8 nIsSPIICE :1; + u8 nIsSPISLAVE :1; + u8 nIsI2C :1; + u8 nReserved00 :3; + u8 nReserved01 :8; + u8 nReserved02 :8; + u8 nReserved03 :8; +}; + +#define TDDI_I2C_ADDR 0x41 +#define TDDI_DEV_ID "ILITEK_TDDI" + + /* define the width and heigth of a screen. */ +#define TOUCH_SCREEN_X_MIN 0 +#define TOUCH_SCREEN_Y_MIN 0 +#define TOUCH_SCREEN_X_MAX 720 +#define TOUCH_SCREEN_Y_MAX 1600 +#define MAX_TOUCH_NUM 10 + +/* define the range on panel */ +#define TPD_HEIGHT 2048 +#define TPD_WIDTH 2048 + +/* Firmware upgrade */ +#define CORE_VER_1410 0x01040100 +#define CORE_VER_1420 0x01040200 +#define CORE_VER_1430 0x01040300 +#define CORE_VER_1460 0x01040600 +#define CORE_VER_1470 0x01040700 +#define MAX_HEX_FILE_SIZE (256*K) +#define ILI_FILE_HEADER 256 +#define DLM_START_ADDRESS 0x20610 +#define DLM_HEX_ADDRESS 0x10000 +#define MP_HEX_ADDRESS 0x13000 +#define DDI_RSV_BK_ST_ADDR 0x1E000 +#define DDI_RSV_BK_END_ADDR 0x1FFFF +#define DDI_RSV_BK_SIZE (1*K) +#define RSV_BK_ST_ADDR 0x1E000 +#define RSV_BK_END_ADDR 0x1E3FF +#define FW_VER_ADDR 0xFFE0 +#define FW_BLOCK_INFO_NUM 17 +#define SPI_UPGRADE_LEN 2048 +#define SPI_BUF_SIZE MAX_HEX_FILE_SIZE +#define INFO_HEX_ST_ADDR 0x4F +#define INFO_MP_HEX_ADDR 0x1F + +/* DMA Control Registers */ +#define DMA_BASED_ADDR 0x72000 +#define DMA48_ADDR (DMA_BASED_ADDR + 0xC0) +#define DMA48_reg_dma_ch0_busy_flag DMA48_ADDR +#define DMA48_reserved_0 0xFFFE +#define DMA48_reg_dma_ch0_trigger_sel (BIT(16)|BIT(17)|BIT(18)|BIT(19)) +#define DMA48_reserved_1 (BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define DMA48_reg_dma_ch0_start_set BIT(24) +#define DMA48_reg_dma_ch0_start_clear BIT(25) +#define DMA48_reg_dma_ch0_trigger_src_mask BIT(26) +#define DMA48_reserved_2 BIT(27) + +#define DMA49_ADDR (DMA_BASED_ADDR + 0xC4) +#define DMA49_reg_dma_ch0_src1_addr DMA49_ADDR +#define DMA49_reserved_0 BIT(20) + +#define DMA50_ADDR (DMA_BASED_ADDR + 0xC8) +#define DMA50_reg_dma_ch0_src1_step_inc DMA50_ADDR +#define DMA50_reserved_0 (DMA50_ADDR + 0x01) +#define DMA50_reg_dma_ch0_src1_format (BIT(24)|BIT(25)) +#define DMA50_reserved_1 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)) +#define DMA50_reg_dma_ch0_src1_en BIT(31) + +#define DMA52_ADDR (DMA_BASED_ADDR + 0xD0) +#define DMA52_reg_dma_ch0_src2_step_inc DMA52_ADDR +#define DMA52_reserved_0 (DMA52_ADDR + 0x01) +#define DMA52_reg_dma_ch0_src2_format (BIT(24)|BIT(25)) +#define DMA52_reserved_1 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)) +#define DMA52_reg_dma_ch0_src2_en BIT(31) + +#define DMA53_ADDR (DMA_BASED_ADDR + 0xD4) +#define DMA53_reg_dma_ch0_dest_addr DMA53_ADDR +#define DMA53_reserved_0 BIT(20) + +#define DMA54_ADDR (DMA_BASED_ADDR + 0xD8) +#define DMA54_reg_dma_ch0_dest_step_inc DMA54_ADDR +#define DMA54_reserved_0 (DMA54_ADDR + 0x01) +#define DMA54_reg_dma_ch0_dest_format (BIT(24)|BIT(25)) +#define DMA54_reserved_1 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)) +#define DMA54_reg_dma_ch0_dest_en BIT(31) + +#define DMA55_ADDR (DMA_BASED_ADDR + 0xDC) +#define DMA55_reg_dma_ch0_trafer_counts DMA55_ADDR +#define DMA55_reserved_0 (BIT(17)|BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define DMA55_reg_dma_ch0_trafer_mode (BIT(24)|BIT(25)|BIT(26)|BIT(27)) +#define DMA55_reserved_1 (BIT(28)|BIT(29)|BIT(30)|BIT(31)) + +/* INT Function Registers */ +#define INTR_BASED_ADDR 0x48000 +#define INTR1_ADDR (INTR_BASED_ADDR + 0x4) +#define INTR1_reg_uart_tx_int_flag INTR1_ADDR +#define INTR1_reserved_0 (BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)) +#define INTR1_reg_wdt_alarm_int_flag BIT(8) +#define INTR1_reserved_1 (BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) +#define INTR1_reg_dma_ch1_int_flag BIT(16) +#define INTR1_reg_dma_ch0_int_flag BIT(17) +#define INTR1_reg_dma_frame_done_int_flag BIT(18) +#define INTR1_reg_dma_tdi_done_int_flag BIT(19) +#define INTR1_reserved_2 (BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define INTR1_reg_flash_error_flag BIT(24) +#define INTR1_reg_flash_int_flag BIT(25) +#define INTR1_reserved_3 BIT(26) + +#define INTR2_ADDR (INTR_BASED_ADDR + 0x8) +#define INTR2_td_int_flag_clear INTR2_ADDR +#define INTR2_td_timeout_int_flag_clear BIT(1) +#define INTR2_td_debug_frame_done_int_flag_clear BIT(2) +#define INTR2_td_frame_start_scan_int_flag_clear BIT(3) +#define INTR2_log_int_flag_clear BIT(4) +#define INTR2_d2t_crc_err_int_flag_clear BIT(8) +#define INTR2_d2t_flash_req_int_flag_clear BIT(9) +#define INTR2_d2t_ddi_int_flag_clear BIT(10) +#define INTR2_wr_done_int_flag_clear BIT(16) +#define INTR2_rd_done_int_flag_clear BIT(17) +#define INTR2_tdi_err_int_flag_clear BIT(18) +#define INTR2_d2t_slpout_rise_flag_clear BIT(24) +#define INTR2_d2t_slpout_fall_flag_clear BIT(25) +#define INTR2_d2t_dstby_flag_clear BIT(26) +#define INTR2_ddi_pwr_rdy_flag_clear BIT(27) + +#define INTR32_ADDR (INTR_BASED_ADDR + 0x80) +#define INTR32_reg_ice_sw_int_en INTR32_ADDR +#define INTR32_reg_ice_apb_conflict_int_en BIT(1) +#define INTR32_reg_ice_ilm_conflict_int_en BIT(2) +#define INTR32_reg_ice_dlm_conflict_int_en BIT(3) +#define INTR32_reserved_0 (BIT(4)|BIT(5)|BIT(6)|BIT(7)) +#define INTR32_reg_spi_sr_int_en BIT(8) +#define INTR32_reg_spi_sp_int_en BIT(9) +#define INTR32_reg_spi_trx_int_en BIT(10) +#define INTR32_reg_spi_cmd_int_en BIT(11) +#define INTR32_reg_spi_rw_int_en BIT(12) +#define INTR32_reserved_1 (BIT(13)|BIT(14)|BIT(15)) +#define INTR32_reg_i2c_start_int_en BIT(16) +#define INTR32_reg_i2c_addr_match_int_en BIT(17) +#define INTR32_reg_i2c_cmd_int_en BIT(18) +#define INTR32_reg_i2c_sr_int_en BIT(19) +#define INTR32_reg_i2c_trx_int_en BIT(20) +#define INTR32_reg_i2c_rx_stop_int_en BIT(21) +#define INTR32_reg_i2c_tx_stop_int_en BIT(22) +#define INTR32_reserved_2 BIT(23) +#define INTR32_reg_t0_int_en BIT(24) +#define INTR32_reg_t1_int_en BIT(25) +#define INTR32_reserved_3 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) + +#define INTR33_ADDR (INTR_BASED_ADDR + 0x84) +#define INTR33_reg_uart_tx_int_en INTR33_ADDR +#define INTR33_reserved_0 (BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)) +#define INTR33_reg_wdt_alarm_int_en BIT(8) +#define INTR33_reserved_1 (BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) +#define INTR33_reg_dma_ch1_int_en BIT(16) +#define INTR33_reg_dma_ch0_int_en BIT(17) +#define INTR33_reg_dma_frame_done_int_en BIT(18) +#define INTR33_reg_dma_tdi_done_int_en BIT(19) +#define INTR33_reserved_2 (BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define INTR33_reg_flash_error_en BIT(24) +#define INTR33_reg_flash_int_en BIT(25) +#define INTR33_reserved_3 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) + +/* Flash */ +#define FLASH_BASED_ADDR 0x41000 +#define FLASH0_ADDR (FLASH_BASED_ADDR + 0x0) +#define FLASH0_reg_flash_csb FLASH0_ADDR +#define FLASH0_reserved_0 (BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) +#define FLASH0_reg_preclk_sel (BIT(16)|BIT(17)|BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define FLASH0_reg_rx_dual BIT(24) +#define FLASH0_reg_tx_dual BIT(25) +#define FLASH0_reserved_26 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) +#define FLASH1_ADDR (FLASH_BASED_ADDR + 0x4) +#define FLASH1_reg_flash_key1 FLASH1_ADDR +#define FLASH1_reg_flash_key2 (FLASH1_ADDR + 0x01) +#define FLASH1_reg_flash_key3 (FLASH1_ADDR + 0x02) +#define FLASH1_reserved_0 (FLASH1_ADDR + 0x03) +#define FLASH2_ADDR (FLASH_BASED_ADDR + 0x8) +#define FLASH2_reg_tx_data FLASH2_ADDR +#define FLASH3_ADDR (FLASH_BASED_ADDR + 0xC) +#define FLASH3_reg_rcv_cnt FLASH3_ADDR +#define FLASH4_ADDR (FLASH_BASED_ADDR + 0x10) +#define FLASH4_reg_rcv_data FLASH4_ADDR +#define FLASH4_reg_rcv_dly BIT(8) +#define FLASH4_reg_sutrg_en BIT(9) +#define FLASH4_reserved_1 (BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) +#define FLASH4_reg_rcv_data_valid_state BIT(16) +#define FLASH4_reg_flash_rd_finish_state BIT(17) +#define FLASH4_reserved_2 (BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define FLASH4_reg_flash_dma_trigger_en (BIT(24)|BIT(25)|BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) + +/* Dummy Registers */ +#define WDT_DUMMY_BASED_ADDR 0x5101C +#define WDT7_DUMMY0 WDT_DUMMY_BASED_ADDR +#define WDT8_DUMMY1 (WDT_DUMMY_BASED_ADDR + 0x04) +#define WDT9_DUMMY2 (WDT_DUMMY_BASED_ADDR + 0x08) + +/* The example for the gesture virtual keys */ +#define GESTURE_DOUBLECLICK 0x58 +#define GESTURE_SINGLECLICK 0x57 +#define GESTURE_UP 0x60 +#define GESTURE_DOWN 0x61 +#define GESTURE_LEFT 0x62 +#define GESTURE_RIGHT 0x63 +#define GESTURE_M 0x64 +#define GESTURE_W 0x65 +#define GESTURE_C 0x66 +#define GESTURE_E 0x67 +#define GESTURE_V 0x68 +#define GESTURE_O 0x69 +#define GESTURE_S 0x6A +#define GESTURE_Z 0x6B +#define KEY_GESTURE_POWER KEY_POWER +#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_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 KEY_GESTURE_F KEY_F +#define GESTURE_V_DOWN 0x6C +#define GESTURE_V_LEFT 0x6D +#define GESTURE_V_RIGHT 0x6E +#define GESTURE_TWOLINE_DOWN 0x6F +#define GESTURE_F 0x70 +#define GESTURE_AT 0x71 +#define ESD_GESTURE_PWD 0xF38A94EF +#define SPI_ESD_GESTURE_RUN 0xA67C9DFE +#define I2C_ESD_GESTURE_RUN 0xA67C9DFE +#define SPI_ESD_GESTURE_PWD_ADDR 0x25FF8 +#define I2C_ESD_GESTURE_PWD_ADDR 0x40054 + +#define ESD_GESTURE_CORE146_PWD 0xF38A +#define SPI_ESD_GESTURE_CORE146_RUN 0xA67C +#define I2C_ESD_GESTURE_CORE146_RUN 0xA67C +#define SPI_ESD_GESTURE_CORE146_PWD_ADDR 0x4005C +#define I2C_ESD_GESTURE_CORE146_PWD_ADDR 0x4005C + +#define DOUBLE_TAP ( ON )//BIT0 +#define ALPHABET_LINE_2_TOP ( ON )//BIT1 +#define ALPHABET_LINE_2_BOTTOM ( ON )//BIT2 +#define ALPHABET_LINE_2_LEFT ( ON )//BIT3 +#define ALPHABET_LINE_2_RIGHT ( ON )//BIT4 +#define ALPHABET_M ( ON )//BIT5 +#define ALPHABET_W ( ON )//BIT6 +#define ALPHABET_C ( ON )//BIT7 +#define ALPHABET_E ( ON )//BIT8 +#define ALPHABET_V ( ON )//BIT9 +#define ALPHABET_O ( ON )//BIT10 +#define ALPHABET_S ( ON )//BIT11 +#define ALPHABET_Z ( ON )//BIT12 +#define ALPHABET_V_DOWN ( ON )//BIT13 +#define ALPHABET_V_LEFT ( ON )//BIT14 +#define ALPHABET_V_RIGHT ( ON )//BIT15 +#define ALPHABET_TWO_LINE_2_BOTTOM ( ON )//BIT16 +#define ALPHABET_F ( ON )//BIT17 +#define ALPHABET_AT ( ON )//BIT18 +#define SINGLE_TAP ( ON )//BIT0 + +/* FW data format */ +#define DATA_FORMAT_DEMO_CMD 0x00 +#define DATA_FORMAT_DEBUG_CMD 0x02 +#define DATA_FORMAT_DEMO_DEBUG_INFO_CMD 0x04 +#define DATA_FORMAT_GESTURE_NORMAL_CMD 0x01 +#define DATA_FORMAT_GESTURE_INFO_CMD 0x02 +#define DATA_FORMAT_DEBUG_LITE_CMD 0x05 +#define DATA_FORMAT_DEBUG_LITE_ROI_CMD 0x01 +#define DATA_FORMAT_DEBUG_LITE_WINDOW_CMD 0x02 +#define DATA_FORMAT_DEBUG_LITE_AREA_CMD 0x03 +#define P5_X_DEMO_MODE_PACKET_INFO_LEN 3 +#define P5_X_DEMO_MODE_PACKET_LEN 43 +#define P5_X_INFO_HEADER_LENGTH 3 +#define P5_X_INFO_CHECKSUM_LENGTH 1 +#define P5_X_DEMO_DEBUG_INFO_ID0_LENGTH 14 +#define P5_X_DEBUG_MODE_PACKET_LENGTH 1280 +#define P5_X_TEST_MODE_PACKET_LENGTH 1180 +#define P5_X_GESTURE_NORMAL_LENGTH 8 +#define P5_X_GESTURE_INFO_LENGTH 170 +#define P5_X_DEBUG_LITE_LENGTH 300 +#define P5_X_CORE_VER_THREE_LENGTH 5 +#define P5_X_CORE_VER_FOUR_LENGTH 6 +#define P5_X_5B_LOW_RESOLUTION_LENGTH 62 +#define P5_X_CUSTOMER_LENGTH 50 + + +/* Protocol */ +#define PROTOCOL_VER_500 0x050000 +#define PROTOCOL_VER_510 0x050100 +#define PROTOCOL_VER_520 0x050200 +#define PROTOCOL_VER_530 0x050300 +#define PROTOCOL_VER_540 0x050400 +#define PROTOCOL_VER_550 0x050500 +#define PROTOCOL_VER_560 0x050600 +#define PROTOCOL_VER_570 0x050700 +#define P5_X_READ_DATA_CTRL 0xF6 +#define P5_X_GET_TP_INFORMATION 0x20 +#define P5_X_GET_KEY_INFORMATION 0x27 +#define P5_X_GET_PANEL_INFORMATION 0x29 +#define P5_X_GET_FW_VERSION 0x21 +#define P5_X_GET_PROTOCOL_VERSION 0x22 +#define P5_X_GET_CORE_VERSION 0x23 +#define P5_X_GET_CORE_VERSION_NEW 0x24 +#define P5_X_MODE_CONTROL 0xF0 +#define P5_X_SET_CDC_INIT 0xF1 +#define P5_X_GET_CDC_DATA 0xF2 +#define P5_X_CDC_BUSY_STATE 0xF3 +#define P5_X_MP_TEST_MODE_INFO 0xFE +#define P5_X_I2C_UART 0x40 +#define CMD_GET_FLASH_DATA 0x41 +#define CMD_CTRL_INT_ACTION 0x1B +#define P5_X_FW_UNKNOWN_MODE 0xFF +#define P5_X_FW_AP_MODE 0x00 +#define P5_X_FW_TEST_MODE 0x01 +#define P5_X_FW_GESTURE_MODE 0x0F +#define P5_X_FW_DELTA_DATA_MODE 0x03 +#define P5_X_FW_RAW_DATA_MODE 0x08 +#define P5_X_DEMO_PACKET_ID 0x5A +#define P5_X_DEBUG_PACKET_ID 0xA7 +#define P5_X_DEMO_HIGH_RESOLUTION_PACKET_ID 0x5B +#define P5_X_DEBUG_HIGH_RESOLUTION_PACKET_ID 0xA8 +#define P5_X_DEMO_MODE_AXIS_LEN 50 +#define P5_X_DEMO_MODE_STATE_INFO 16 + +#define P5_X_TEST_PACKET_ID 0xF2 +#define P5_X_GESTURE_PACKET_ID 0xAA +#define P5_X_GESTURE_FAIL_ID 0xAE +#define P5_X_I2CUART_PACKET_ID 0x7A +#define P5_X_DEBUG_LITE_PACKET_ID 0x9A +#define P5_X_SLAVE_MODE_CMD_ID 0x5F +#define P5_X_INFO_HEADER_PACKET_ID 0xB7 +#define P5_X_DEMO_DEBUG_INFO_PACKET_ID 0x5C +#define P5_X_EDGE_PLAM_CTRL_1 0x01 +#define P5_X_EDGE_PLAM_CTRL_2 0x12 +#define SPI_WRITE 0x82 +#define SPI_READ 0x83 +#define SPI_ACK 0xA3 + +/* Chips */ +#define ILI9881_CHIP 0x9881 +#define ILI7807_CHIP 0x7807 +#define ILI9881N_AA 0x98811700 +#define ILI9881O_AA 0x98811800 +#define ILI9882_CHIP 0x9882 +#define TDDI_PID_ADDR 0x4009C +#define TDDI_OTP_ID_ADDR 0x400A0 +#define TDDI_ANA_ID_ADDR 0x400A4 +#define TDDI_PC_COUNTER_ADDR 0x44008 +#define TDDI_PC_LATCH_ADDR 0x51010 +#define TDDI_CHIP_RESET_ADDR 0x40050 +#define RAWDATA_NO_BK_SHIFT 8192 + +#ifdef ILI_SET_TOUCH_STATE +#define MAX_PANEL_IDX 2 +enum touch_panel_id { + TOUCH_PANEL_IDX_PRIMARY = 0, + TOUCH_PANEL_MAX_IDX, +}; +#endif + +#ifdef ILI_SENSOR_EN +/* display state */ +enum display_state { + SCREEN_UNKNOWN, + SCREEN_OFF, + SCREEN_ON, +}; +struct ili_sensor_platform_data { + struct input_dev *input_sensor_dev; + struct sensors_classdev ps_cdev; + int sensor_opened; + char sensor_data; /* 0 near, 1 far */ + struct ilitek_ts_data *data; +}; +#define REPORT_MAX_COUNT 10000 +#endif + +struct ilitek_ts_data { + struct i2c_client *i2c; + struct spi_device *spi; + struct input_dev *input; + struct device *dev; + struct wakeup_source *ws; + + struct ilitek_hwif_info *hwif; + struct ilitek_ic_info *chip; + struct ilitek_protocol_info *protocol; + struct gesture_coordinate *gcoord; + struct regulator *vdd; + struct regulator *vcc; + +#if defined(CONFIG_FB) || defined(CONFIG_DRM) + struct notifier_block notifier_fb; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif +#if CHARGER_NOTIFIER_CALLBACK +#if KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE +/* add_for_charger_start */ + struct notifier_block notifier_charger; + struct workqueue_struct *charger_notify_wq; + struct work_struct update_charger; + int usb_plug_status; +/* add_for_charger_end */ +#endif +#endif + struct mutex touch_mutex; + struct mutex debug_mutex; + struct mutex debug_read_mutex; + spinlock_t irq_spin; + + struct completion pm_completion; + bool pm_suspend; + + /* physical path to the input device in the system hierarchy */ + const char *phys; + + bool boot; + u32 fw_pc; + u32 fw_latch; + + u16 max_x; + u16 max_y; + u16 min_x; + u16 min_y; + u16 panel_wid; + u16 panel_hei; + u8 xch_num; + u8 ych_num; + u8 stx; + u8 srx; + u8 *update_buf; + u8 *tr_buf; + u8 *spi_tx; + u8 *spi_rx; + struct firmware tp_fw; + + int actual_tp_mode; + int tp_data_format; + int tp_data_len; + + int irq_num; + int irq_gpio; + int irq_tirgger_type; + int tp_rst; + int tp_int; + int wait_int_timeout; + + int finger; + int curt_touch[MAX_TOUCH_NUM]; + int prev_touch[MAX_TOUCH_NUM]; + int last_touch; + + int fw_retry; + int fw_update_stat; + int fw_open; + u8 fw_info[75]; + u8 fw_mp_ver[4]; + bool wq_ctrl; + bool wq_esd_ctrl; + bool wq_bat_ctrl; + + bool panel_gesture_enable; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + bool netlink; +#endif + bool report; + bool gesture; + bool mp_retry; + int gesture_mode; + int gesture_demo_ctrl; + struct gesture_symbol ges_sym; + struct report_info_block rib; + int canvas_value; +#ifdef ILITEK_PEN_NOTIFIER + struct notifier_block pen_notif; +#endif + + u16 flash_mid; + u16 flash_devid; + int program_page; + int flash_sector; + + /* Sending report data to users for the debug */ + bool dnp; //debug node open + int dbf; //debug data frame + int odi; //out data index + wait_queue_head_t inq; + struct debug_buf_list *dbl; + int raw_count; + int delta_count; + int bg_count; + + int reset; + int rst_edge_delay; + int fw_upgrade_mode; + int mp_ret_len; + bool wtd_ctrl; + bool force_fw_update; + bool irq_after_recovery; + bool ddi_rest_done; + bool resume_by_ddi; + bool tp_suspend; + bool info_from_hex; + bool prox_near; + bool gesture_load_code; + bool trans_xy; + bool ss_ctrl; + bool node_update; + bool int_pulse; + bool pll_clk_wakeup; + + /* module info */ + int tp_module; + int md_fw_ili_size; + char *md_name; + char *md_fw_filp_path; + char *md_fw_rq_path; + char *md_ini_path; + char *md_ini_rq_path; + u8 *md_fw_ili; + + atomic_t irq_stat; + atomic_t tp_reset; + atomic_t ice_stat; + atomic_t fw_stat; + atomic_t mp_stat; + atomic_t tp_sleep; + atomic_t tp_sw_mode; + atomic_t cmd_int_check; + atomic_t esd_stat; + +#if defined(ILI_SENSOR_EN) || defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + bool wakeable; + bool should_enable_gesture; +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock gesture_wakelock; +#else + struct wakeup_source *gesture_wakelock; +#endif +#endif +#ifdef ILI_SENSOR_EN + bool gesture_enabled; + uint32_t report_gesture_key; + enum display_state screen_state; + struct mutex state_mutex; + struct ili_sensor_platform_data *sensor_pdata; +#endif + + /* Event for driver test */ + struct completion esd_done; + + int (*spi_write_then_read)(struct spi_device *spi, const void *txbuf, + unsigned n_tx, void *rxbuf, unsigned n_rx); + int (*wrapper)(u8* wdata, u32 wlen, u8* rdata, u32 rlen, bool spi_irq, bool i2c_irq); + int (*mp_move_code)(void); + int (*gesture_move_code)(int mode); + int (*esd_recover)(void); + int (*ges_recover)(void); + void (*demo_debug_info[5])(u8 *, size_t); + int (*detect_int_stat)(bool status); + struct pinctrl *pinctrl; + struct pinctrl_state *pins_active; + struct pinctrl_state *pins_suspend; + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + struct ts_mmi_class_methods *imports; +#endif +#ifdef ILI_TOUCH_LAST_TIME + ktime_t last_event_time; +#endif +}; +extern struct ilitek_ts_data *ilits; + +struct debug_buf_list { + bool mark; + unsigned char *data; +}; + +struct ilitek_touch_info { + u16 id; + u16 x; + u16 y; + u16 pressure; + s8 degree; + u16 width_major; + u16 width_minor; +}; + +struct gesture_coordinate { + u16 code; + u8 clockwise; + int type; + struct ilitek_touch_info pos_start; + struct ilitek_touch_info pos_end; + struct ilitek_touch_info pos_1st; + struct ilitek_touch_info pos_2nd; + struct ilitek_touch_info pos_3rd; + struct ilitek_touch_info pos_4th; +}; + +struct ilitek_protocol_info { + u32 ver; + int fw_ver_len; + int pro_ver_len; + int tp_info_len; + int key_info_len; + int panel_info_len; + int core_ver_len; + int func_ctrl_len; + int window_len; + int cdc_len; + int mp_info_len; +}; + +struct ilitek_ic_func_ctrl { + const char *name; + u8 cmd[6]; + int len; + u8 def_cmd; + u8 rec_state; //0:disable, 1: enable, 2: ignore record + u8 rec_cmd; +}; + +struct ilitek_ic_info { + u8 type; + u8 ver; + u16 id; + u32 pid; + u32 pid_addr; + u32 pc_counter_addr; + u32 pc_latch_addr; + u32 reset_addr; + u32 otp_addr; + u32 ana_addr; + u32 otp_id; + u32 ana_id; + u32 fw_ver; + u32 core_ver; + u32 fw_mp_ver; + u32 max_count; + u32 reset_key; + u16 wtd_key; + int no_bk_shift; + bool dma_reset; + int (*dma_crc)(u32 start_addr, u32 block_size); +}; + +struct ilitek_hwif_info { + u8 bus_type; + u8 plat_type; + const char *name; + struct module *owner; + const struct of_device_id *of_match_table; + const struct dev_pm_ops *pm; + int (*plat_probe)(void); + int (*plat_remove)(void); + void *info; +}; + +/* Prototypes for tddi firmware/flash functions */ +extern void ili_flash_dma_write(u32 start, u32 end, u32 len); +extern void ili_flash_clear_dma(void); +extern void ili_fw_read_flash_info(void); +extern u32 ili_fw_read_hw_crc(u32 start, u32 end, u32 *flash_crc); +extern int ili_fw_read_flash(u32 start, u32 end, u8 *data, int len); +extern int ili_fw_dump_iram_data(u32 start, u32 end, bool save); +extern int ili_fw_dump_flash_data(u32 start, u32 end, bool user); +extern int ili_fw_upgrade(int op); + +/* Prototypes for tddi mp test */ +extern int ili_mp_test_main(char *apk, bool lcm_on); + +/* Prototypes for tddi core functions */ +extern int ili_touch_esd_gesture_flash(void); +extern int ili_touch_esd_gesture_iram(void); +extern void ili_set_gesture_symbol(void); +extern int ili_move_gesture_code_flash(int mode); +extern int ili_move_gesture_code_iram(int mode); +extern int ili_move_mp_code_flash(void); +extern int ili_move_mp_code_iram(void); +extern void ili_touch_press(u16 x, u16 y, u16 pressure, u16 id); +extern void ili_touch_press_width(u16 x, u16 y, u16 pressure, u16 id, u16 width_major, u16 width_minor, s8 degree); +extern void ili_touch_release(u16 x, u16 y, u16 id); +extern void ili_touch_release_all_point(void); +extern void ili_report_ap_mode(u8 *buf, int len); +extern void ili_report_debug_mode(u8 *buf, int rlen); +extern void ili_report_debug_lite_mode(u8 *buf, int rlen); +extern void ili_report_gesture_mode(u8 *buf, int rlen); +extern void ili_report_i2cuart_mode(u8 *buf, int rlen); +extern void ili_ic_set_ddi_reg_onepage(u8 page, u8 reg, u8 data); +extern void ili_ic_get_ddi_reg_onepage(u8 page, u8 reg, u8 *data); +extern int ili_ic_whole_reset(void); +extern int ili_ic_code_reset(void); +extern int ili_ic_func_ctrl(const char *name, int ctrl); +extern void ili_ic_func_ctrl_reset(void); +extern int ili_ic_int_trigger_ctrl(bool pulse); +extern void ili_ic_get_pc_counter(int stat); +extern int ili_ic_check_int_level(bool level); +extern int ili_ic_check_int_pulse(bool pulse); +extern int ili_ic_check_busy(int count, int delay); +extern int ili_ic_get_project_id(u8 *pdata, int size); +extern int ili_ic_get_panel_info(void); +extern int ili_ic_get_tp_info(void); +extern int ili_ic_get_core_ver(void); +extern int ili_ic_get_protocl_ver(void); +extern int ili_ic_get_fw_ver(void); +extern int ili_ic_get_info(void); +extern int ili_ic_dummy_check(void); +extern int ili_ice_mode_bit_mask_write(u32 addr, u32 mask, u32 value); +extern int ili_ice_mode_write(u32 addr, u32 data, int len); +extern int ili_ice_mode_read(u32 addr, u32 *data, int len); +extern int ili_ice_mode_ctrl(bool enable, bool mcu); +extern void ili_ic_init(void); +extern void ili_fw_uart_ctrl(u8 ctrl); + +/* Prototypes for tddi events */ +#if RESUME_BY_DDI +extern void ili_resume_by_ddi(void); +#endif +extern int ili_proximity_far(int mode); +extern int ili_proximity_near(int mode); +extern int ili_switch_tp_mode(u8 data); +extern int ili_set_tp_data_len(int format, bool send, u8* data); +extern int ili_fw_upgrade_handler(void *data); +extern int ili_wq_esd_i2c_check(void); +extern int ili_wq_esd_spi_check(void); +extern int ili_gesture_recovery(void); +extern void ili_spi_recovery(void); +extern void ili_wq_ctrl(int type, int ctrl); +extern int ili_mp_test_handler(char *apk, bool lcm_on); +extern int ili_report_handler(void); +extern int ili_sleep_handler(int mode); +extern int ili_reset_ctrl(int mode); +extern int ili_tddi_init(void); +extern int ili_dev_init(struct ilitek_hwif_info *hwif); +extern void ili_dev_remove(void); + +/* Prototypes for i2c/spi interface */ +extern int ili_core_spi_setup(int num); +extern int ili_interface_dev_init(struct ilitek_hwif_info *hwif); +extern void ili_interface_dev_exit(struct ilitek_ts_data *ts); +extern int ili_spi_write_then_read_split(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx); +extern int ili_spi_write_then_read_direct(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx); + +/* Prototypes for platform level */ +extern void ili_plat_regulator_power_on(bool status); +extern void ili_input_register(void); +extern void ili_irq_disable(void); +extern void ili_irq_enable(void); +extern void ili_tp_reset(void); +extern void ili_irq_unregister(void); +extern int ili_irq_register(int type); + +/* Prototypes for miscs */ +extern void ili_node_init(void); +extern void ili_dump_data(void *data, int type, int len, int row_len, const char *name); +extern u8 ili_calc_packet_checksum(u8 *packet, int len); +extern void ili_netlink_reply_msg(void *raw, int size); +extern int ili_katoi(char *str); +extern int ili_str2hex(char *str); + +extern int ili_mp_read_write(u8* wdata, u32 wlen, u8* rdata, u32 rlen); +/* Prototypes for additional functionalities */ +extern void ili_gesture_fail_reason(bool enable); +extern int ili_get_tp_recore_ctrl(int data); +extern int ili_get_tp_recore_data(void); +extern void ili_demo_debug_info_mode(u8 *buf, size_t rlen); +extern void ili_demo_debug_info_id0(u8 *buf, size_t len); + +#ifdef ILI_SET_TOUCH_STATE +int touch_set_state(int state, int panel_idx); +int check_touch_state(int *state, int panel_idx); +#endif + +static inline void ipio_kfree(void **mem) +{ + if (*mem != NULL) { + kfree(*mem); + *mem = NULL; + } +} + +static inline void ipio_vfree(void **mem) +{ + if (*mem != NULL) { + vfree(*mem); + *mem = NULL; + } +} + +static inline void *ipio_memcpy(void *dest, const void *src, int n, int dest_size) +{ + if (n > dest_size) + n = dest_size; + + return memcpy(dest, src, n); +} + +static inline int ipio_strcmp(const char *s1, const char *s2) +{ + return (strlen(s1) != strlen(s2)) ? -1 : strncmp(s1, s2, strlen(s1)); +} + +#endif /* __ILI9882_H */ diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_flash.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_flash.c new file mode 100755 index 000000000000..7b16a36f1dd3 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_flash.c @@ -0,0 +1,1500 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" + +#define UPDATE_PASS 0 +#define UPDATE_FAIL -1 +#define TIMEOUT_SECTOR 500 +#define TIMEOUT_PAGE 3500 +#define TIMEOUT_PROGRAM 10 + +static struct touch_fw_data { + u8 block_number; + u32 start_addr; + u32 end_addr; + u32 new_fw_cb; + int delay_after_upgrade; + bool isCRC; + bool isboot; + bool is80k; + int hex_tag; +} tfd; + +static struct flash_block_info { + char *name; + u32 start; + u32 end; + u32 len; + u32 mem_start; + u32 fix_mem_start; + u8 mode; +} fbi[FW_BLOCK_INFO_NUM]; + +static u8 fw_flash_tmp[K + 7]; +static u8 *pfw; +static u8 *CTPM_FW; + +static u32 HexToDec(char *phex, s32 len) +{ + u32 ret = 0, temp = 0, i; + s32 shift = (len - 1) * 4; + + for (i = 0; i < len; shift -= 4, i++) { + if ((phex[i] >= '0') && (phex[i] <= '9')) + temp = phex[i] - '0'; + else if ((phex[i] >= 'a') && (phex[i] <= 'f')) + temp = (phex[i] - 'a') + 10; + else if ((phex[i] >= 'A') && (phex[i] <= 'F')) + temp = (phex[i] - 'A') + 10; + + ret |= (temp << shift); + } + return ret; +} + +static int CalculateCRC32(u32 start_addr, u32 len, u8 *pfw) +{ + int i = 0, j = 0; + int crc_poly = 0x04C11DB7; + int tmp_crc = 0xFFFFFFFF; + + for (i = start_addr; i < start_addr + len; i++) { + tmp_crc ^= (pfw[i] << 24); + + for (j = 0; j < 8; j++) { + if ((tmp_crc & 0x80000000) != 0) + tmp_crc = tmp_crc << 1 ^ crc_poly; + else + tmp_crc = tmp_crc << 1; + } + } + return tmp_crc; +} + +static int ilitek_tddi_fw_iram_read(u8 *buf, u32 start, int len) +{ + int limit = 4*K; + int addr = 0, loop = 0, tmp_len = len, cnt = 0; + u8 cmd[4] = {0}; + + if (!buf) { + ILI_ERR("buf is null\n"); + return -ENOMEM; + } + + if (len % limit) + loop = (len / limit) + 1; + else + loop = len / limit; + + for (cnt = 0, addr = start; cnt < loop; cnt++, addr += limit) { + if (tmp_len > limit) + tmp_len = limit; + + cmd[0] = 0x25; + cmd[3] = (char)((addr & 0x00FF0000) >> 16); + cmd[2] = (char)((addr & 0x0000FF00) >> 8); + cmd[1] = (char)((addr & 0x000000FF)); + + if (ilits->wrapper(cmd, 4, NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Failed to write iram data\n"); + return -ENODEV; + } + + if (ilits->wrapper(NULL, 0, buf + cnt * limit, tmp_len, OFF, OFF) < 0) { + ILI_ERR("Failed to Read iram data\n"); + return -ENODEV; + } + + tmp_len = len - cnt * limit; + ilits->fw_update_stat = ((len - tmp_len) * 100) / len; + ILI_DBG("Reading iram data .... %d%c", ilits->fw_update_stat, '%'); + } + return 0; +} + +int ili_fw_dump_iram_data(u32 start, u32 end, bool save) +{ + struct file *f = NULL; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + mm_segment_t old_fs; +#endif + loff_t pos = 0; + int i, ret, len; + u8 *fw_buf = NULL; + + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) { + ILI_ERR("Enable ice mode failed\n"); + ret = -1; + goto out; + } + + len = end - start + 1; + + if (len > MAX_HEX_FILE_SIZE) { + ILI_ERR("len is larger than buffer, abort\n"); + ret = -EINVAL; + goto out; + } + + fw_buf = kzalloc(MAX_HEX_FILE_SIZE, GFP_KERNEL); + if (ERR_ALLOC_MEM(fw_buf)) { + ILI_ERR("Failed to allocate update_buf\n"); + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + fw_buf[i] = 0xFF; + + ret = ilitek_tddi_fw_iram_read(fw_buf, start, len); + if (ret < 0) + goto out; + + f = filp_open(DUMP_IRAM_PATH, O_WRONLY | O_CREAT | O_TRUNC, 644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file at %ld.\n", PTR_ERR(f)); + ret = -1; + goto out; + } + + pos = 0; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + old_fs = get_fs(); + set_fs(get_ds()); + set_fs(KERNEL_DS); + vfs_write(f, fw_buf, len, &pos); + set_fs(old_fs); +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) + kernel_write(f, fw_buf, len, &pos); +#endif + filp_close(f, NULL); + ILI_INFO("Save iram data to %s\n", DUMP_IRAM_PATH); + +out: + ili_ice_mode_ctrl(DISABLE, OFF); + ILI_INFO("Dump IRAM %s\n", (ret < 0) ? "FAIL" : "SUCCESS"); + ipio_kfree((void **)&fw_buf); + return ret; +} + +static int ilitek_tddi_flash_poll_busy(int timer) +{ + int ret = UPDATE_PASS, retry = timer; + u8 cmd = 0x5; + u32 temp = 0; + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Pull cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, cmd, 1) < 0) + ILI_ERR("Write 0x5 cmd failed\n"); + + do { + if (ili_ice_mode_write(FLASH2_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + mdelay(1); + + if (ili_ice_mode_read(FLASH4_ADDR, &temp, sizeof(u8)) < 0) + ILI_ERR("Read flash busy error\n"); + + if ((temp & 0x3) == 0) + break; + } while (--retry >= 0); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Pull cs high failed\n"); + + if (retry <= 0) { + ILI_ERR("Flash polling busy timeout ! tmp = %x\n", temp); + ret = UPDATE_FAIL; + } + + return ret; +} + +void ili_flash_clear_dma(void) +{ + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_preclk_sel, (2 << 16)) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_preclk_sel, FLASH0_ADDR); + + if (ili_ice_mode_bit_mask_write(FLASH4_ADDR, FLASH4_reg_flash_dma_trigger_en, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH4_reg_flash_dma_trigger_en, FLASH4_ADDR); + + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_rx_dual, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_rx_dual, FLASH0_ADDR); + + if (ili_ice_mode_write(FLASH3_reg_rcv_cnt, 0x00, 1) < 0) + ILI_ERR("Write 0x0 at %x failed\n", FLASH3_reg_rcv_cnt); + + if (ili_ice_mode_write(FLASH4_reg_rcv_data, 0xFF, 1) < 0) + ILI_ERR("Write 0xFF at %x failed\n", FLASH4_reg_rcv_data); +} + +static int ilitek_tddi_flash_read_int_flag(void) +{ + int retry = 100; + u32 data = 0; + + do { + if (ili_ice_mode_read(INTR1_ADDR & BIT(25), &data, sizeof(u32)) < 0) + ILI_ERR("Read flash int flag error\n"); + + ILI_DBG("int flag = %x\n", data); + if (data) + break; + mdelay(2); + } while (--retry >= 0); + + if (retry <= 0) { + ILI_ERR("Read Flash INT flag timeout !, flag = 0x%x\n", data); + return -1; + } + return 0; +} + +void ili_flash_dma_write(u32 start, u32 end, u32 len) +{ + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_preclk_sel, 1 << 16) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_preclk_sel, FLASH0_ADDR); + + if (ili_ice_mode_write(FLASH0_reg_flash_csb, 0x00, 1) < 0) + ILI_ERR("Pull cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_reg_flash_key1, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, 0x0b, 1) < 0) + ILI_ERR("Write 0x0b at %x failed\n", FLASH2_reg_tx_data); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write 0xb timeout \n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, (start & 0xFF0000) >> 16, 1) < 0) + ILI_ERR("Write %x at %x failed\n", (start & 0xFF0000), FLASH2_reg_tx_data); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write addr1 timeout\n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, (start & 0x00FF00) >> 8, 1) < 0) + ILI_ERR("Write %x at %x failed\n", (start & 0x00FF00) >> 8, FLASH2_reg_tx_data); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write addr2 timeout\n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, (start & 0x0000FF), 1) < 0) + ILI_ERR("Write %x at %x failed\n", (start & 0x0000FF), FLASH2_reg_tx_data); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write addr3 timeout\n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_rx_dual, 0 << 24) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_rx_dual, FLASH0_ADDR); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, 0x00, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write dummy timeout\n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_write(FLASH3_reg_rcv_cnt, len, 4) < 0) + ILI_ERR("Write length failed\n"); +} + +static void ilitek_tddi_flash_write_enable(void) +{ + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Pull CS low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x6, 1) < 0) + ILI_ERR("Write 0x6 failed\n"); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Pull CS high failed\n"); +} + +u32 ili_fw_read_hw_crc(u32 start, u32 end, u32 *flash_crc) +{ + int retry = 100; + u32 busy = 0; + u32 write_len = end; + + if (write_len > ilits->chip->max_count) { + ILI_ERR("The length (%x) written into firmware is greater than max count (%x)\n", + write_len, ilits->chip->max_count); + return -1; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Pull CS low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x3b, 1) < 0) + ILI_ERR("Write 0x3b failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0xFF0000) >> 16, 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0x00FF00) >> 8, 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0x0000FF), 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(0x041003, 0x01, 1) < 0) + ILI_ERR("Write enable Dio_Rx_dual failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + if (ili_ice_mode_write(0x04100C, write_len, 3) < 0) + ILI_ERR("Write Set Receive count failed\n"); + + if (ili_ice_mode_write(0x048007, 0x02, 1) < 0) + ILI_ERR("Write clearing int flag failed\n"); + + if (ili_ice_mode_write(0x041016, 0x00, 1) < 0) + ILI_ERR("Write 0x0 at 0x041016 failed\n"); + + if (ili_ice_mode_write(0x041016, 0x01, 1) < 0) + ILI_ERR("Write Checksum_En failed\n"); + + if (ili_ice_mode_write(FLASH4_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write start to receive failed\n"); + + do { + if (ili_ice_mode_read(0x048007, &busy, sizeof(u8)) < 0) + ILI_ERR("Read busy error\n"); + + ILI_DBG("busy = %x\n", busy); + if (((busy >> 1) & 0x01) == 0x01) + break; + mdelay(2); + } while (--retry >= 0); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write CS high failed\n"); + + if (retry <= 0) { + ILI_ERR("Read HW CRC timeout !, busy = 0x%x\n", busy); + return -1; + } + + if (ili_ice_mode_write(0x041003, 0x0, 1) < 0) + ILI_ERR("Write disable dio_Rx_dual failed\n"); + + if (ili_ice_mode_read(0x04101C, flash_crc, sizeof(u32)) < 0) { + ILI_ERR("Read hw crc error\n"); + return -1; + } + + return 0; +} + +static int ilitek_tddi_fw_read_flash_data(u32 start, u32 end, u8 *data, int len) +{ + u32 i, j, index = 0; + u32 tmp; + + if (end - start > len) { + ILI_ERR("the length (%d) reading crc is over than len(%d)\n", end - start, len); + return -1; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x03, 1) < 0) + ILI_ERR("Write 0x3 failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0xFF0000) >> 16, 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0x00FF00) >> 8, 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0x0000FF), 1) < 0) + ILI_ERR("Write address failed\n"); + + for (i = start, j = 0; i <= end; i++, j++) { + if (ili_ice_mode_write(FLASH2_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + if (ili_ice_mode_read(FLASH4_ADDR, &tmp, sizeof(u8)) < 0) + ILI_ERR("Read flash data error!\n"); + + data[index] = tmp; + index++; + ilits->fw_update_stat = (j * 100) / len; + ILI_DBG("Reading flash data .... %d%c", ilits->fw_update_stat, '%'); + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + + return 0; +} + +int ili_fw_dump_flash_data(u32 start, u32 end, bool user) +{ + struct file *f = NULL; + u8 *buf = NULL; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + mm_segment_t old_fs; +#endif + + loff_t pos = 0; + u32 start_addr, end_addr; + int ret, length; + + f = filp_open(DUMP_FLASH_PATH, O_WRONLY | O_CREAT | O_TRUNC, 644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file at %ld.\n", PTR_ERR(f)); + ret = -1; + goto out; + } + + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) + goto out; + + if (user) { + start_addr = 0x0; + end_addr = 0x1FFFF; + } else { + start_addr = start; + end_addr = end; + } + + length = end_addr - start_addr + 1; + ILI_INFO("len = %d\n", length); + + buf = vmalloc(length * sizeof(u8)); + if (ERR_ALLOC_MEM(buf)) { + ILI_ERR("Failed to allocate buf memory, %ld\n", PTR_ERR(buf)); + filp_close(f, NULL); + ret = -1; + goto out; + } + + ret = ilitek_tddi_fw_read_flash_data(start_addr, end_addr, buf, length); + if (ret < 0) + goto out; + + pos = 0; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + old_fs = get_fs(); + set_fs(get_ds()); + set_fs(KERNEL_DS); + vfs_write(f, buf, length, &pos); + set_fs(old_fs); +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) + kernel_write(f, buf, length, &pos); +#endif + filp_close(f, NULL); + ipio_vfree((void **)&buf); + +out: + ili_ice_mode_ctrl(DISABLE, OFF); + ILI_INFO("Dump flash %s\n", (ret < 0) ? "FAIL" : "SUCCESS"); + return ret; +} + +static void ilitek_tddi_flash_protect(bool enable) +{ + ILI_INFO("%s flash protection\n", enable ? "Enable" : "Disable"); + + ilitek_tddi_flash_write_enable(); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x1, 1) < 0) + ILI_ERR("Write 0x1 failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x0, 1) < 0) + ILI_ERR("Write 0x0 failed\n"); + + switch (ilits->flash_mid) { + case 0xEF: + if (ilits->flash_devid == 0x6012 || ilits->flash_devid == 0x6011) { + if (enable) { + if (ili_ice_mode_write(FLASH2_ADDR, 0x7E, 1) < 0) + ILI_ERR("Write 0x7E at %x failed\n", FLASH2_ADDR); + } else { + if (ili_ice_mode_write(FLASH2_ADDR, 0x0, 1) < 0) + ILI_ERR("Write 0x0 at %x failed\n", FLASH2_ADDR); + } + } + break; + case 0xC8: + if (ilits->flash_devid == 0x6012 || ilits->flash_devid == 0x6013) { + if (enable) { + if (ili_ice_mode_write(FLASH2_ADDR, 0x7A, 1) < 0) + ILI_ERR("Write 0x7A at %x failed\n", FLASH2_ADDR); + } else { + if (ili_ice_mode_write(FLASH2_ADDR, 0x0, 1) < 0) + ILI_ERR("Write 0x0 at %x failed\n", FLASH2_ADDR); + } + + } + break; + default: + ILI_ERR("Can't find flash id(0x%x), ignore protection\n", ilits->flash_mid); + break; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); +} + +static int ilitek_fw_check_ddi_chunk(u8 *pfw) +{ + int ret = 0; + u8 cmd[7] = {0x0}; + u32 start = 0x0, end = 0x0, len = 0x0; + bool hex_info = ilits->info_from_hex; + + if (!fbi[DDI].start || !fbi[DDI].end) + return UPDATE_PASS; + + /* To make sure if flash has data. */ + if (hex_info) + ilits->info_from_hex = DISABLE; + + if (ili_ic_get_fw_ver() < 0) { + ILI_INFO("Failed to get fw ver, reading flash by dma instead\n"); + goto dma; + } + + /* Command for getting ddi info from firmware. */ + cmd[0] = CMD_GET_FLASH_DATA; //cmd + cmd[1] = DDI_RSV_BK_SIZE & 0xFF; //len L byte + cmd[2] = (DDI_RSV_BK_SIZE >> 8) & 0xFF; //len H byte + + /* Read customer reserved block */ + start = DDI_RSV_BK_ST_ADDR; + cmd[3] = (start & 0xFF); //addr L byte + cmd[4] = (start >> 8) & 0xFF ; //addr M byte + cmd[5] = (start >> 16) & 0xFF; //addr H byte + cmd[6] = ili_calc_packet_checksum(cmd, sizeof(cmd) - 1); + ILI_INFO("Read customer reserved block: start addr = 0x%X, len = 0x%X ", start, DDI_RSV_BK_SIZE); + if (ilits->wrapper(cmd, sizeof(cmd), fw_flash_tmp, sizeof(fw_flash_tmp), OFF, ON) < 0) { + ILI_ERR("Failed to read customer reserved block data from fw\n"); + goto dma; + } + ipio_memcpy(&pfw[start], fw_flash_tmp + 6, DDI_RSV_BK_SIZE, MAX_HEX_FILE_SIZE - start); + + /* Read mp data block */ + start = DDI_RSV_BK_END_ADDR - DDI_RSV_BK_SIZE + 1; + cmd[3] = (start & 0xFF); //addr L byte + cmd[4] = (start >> 8) & 0xFF ; //addr M byte + cmd[5] = (start >> 16) & 0xFF; //addr H byte + cmd[6] = ili_calc_packet_checksum(cmd, sizeof(cmd) - 1); + ILI_INFO("Read mp data block: start addr = 0x%X, len = 0x%X ", start, DDI_RSV_BK_SIZE); + if (ilits->wrapper(cmd, sizeof(cmd), fw_flash_tmp, sizeof(fw_flash_tmp), OFF, ON) < 0) { + ILI_ERR("Failed to read mp data block from fw\n"); + goto dma; + } + ipio_memcpy(&pfw[start], fw_flash_tmp + 6, DDI_RSV_BK_SIZE, MAX_HEX_FILE_SIZE- start); + + goto out; + +dma: + ILI_INFO("Obtain block data from dma\n"); + + ili_ice_mode_ctrl(ENABLE, OFF); + + /* Read customer reserved block */ + start = DDI_RSV_BK_ST_ADDR; + end = DDI_RSV_BK_ST_ADDR + DDI_RSV_BK_SIZE - 1; + len = DDI_RSV_BK_SIZE; + if (ilitek_tddi_fw_read_flash_data(start, end, fw_flash_tmp, len) < 0) { + ILI_ERR("Failed to read customer reserved block data from dma\\n"); + ret = UPDATE_FAIL; + goto out; + } + ipio_memcpy(&pfw[start], fw_flash_tmp + 6, DDI_RSV_BK_SIZE, MAX_HEX_FILE_SIZE - start); + + /* Read mp data block */ + start = DDI_RSV_BK_END_ADDR - DDI_RSV_BK_SIZE + 1; + end = DDI_RSV_BK_END_ADDR; + len = DDI_RSV_BK_SIZE; + if (ilitek_tddi_fw_read_flash_data(start, end, fw_flash_tmp, len) < 0) { + ILI_ERR("Failed to read mp data block from dma\n"); + ret = UPDATE_FAIL; + goto out; + } + ipio_memcpy(&pfw[start], fw_flash_tmp + 6, DDI_RSV_BK_SIZE, MAX_HEX_FILE_SIZE - start); + + ili_ice_mode_ctrl(DISABLE, OFF); +out: + /* + * Since there're two blocks combined with DDI chunk togeter, + * we need to extend the addresses where are going to upgrade. + */ + fbi[DDI].start = DDI_RSV_BK_ST_ADDR; + fbi[DDI].end = DDI_RSV_BK_END_ADDR; + tfd.end_addr = DDI_RSV_BK_END_ADDR; + + if (hex_info) + ilits->info_from_hex = ENABLE; + + atomic_set(&ilits->cmd_int_check, DISABLE); + return ret; +} + +static int ilitek_tddi_flash_fw_crc_check(u8 *pfw, bool check_fw_ver) +{ + int i, len = 0, crc_byte_len = 4; + u8 flash_crc[4] = {0}; + u32 start_addr = 0, end_addr = 0; + u32 hw_crc = 0; + u32 flash_crc_cb = 0, hex_crc = 0; + + /* Check Flash and HW CRC */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + start_addr = fbi[i].start; + end_addr = fbi[i].end; + + /* Invaild end address */ + if (end_addr == 0) + continue; + + if (ilitek_tddi_fw_read_flash_data(end_addr - crc_byte_len + 1, end_addr, + flash_crc, sizeof(flash_crc)) < 0) { + ILI_ERR("Read Flash failed\n"); + return UPDATE_FAIL; + } + + flash_crc_cb = flash_crc[0] << 24 | flash_crc[1] << 16 | flash_crc[2] << 8 | flash_crc[3]; + + if (ili_fw_read_hw_crc(start_addr, end_addr - start_addr - crc_byte_len + 1, &hw_crc) < 0) { + ILI_ERR("Read HW CRC failed\n"); + return UPDATE_FAIL; + } + + ILI_INFO("Block = %d, HW CRC = 0x%06x, Flash CRC = 0x%06x\n", i, hw_crc, flash_crc_cb); + + /* Compare Flash CRC with HW CRC */ + if (flash_crc_cb != hw_crc) { + ILI_INFO("HW and Flash CRC not matched\n"); + return UPDATE_FAIL; + } + memset(flash_crc, 0, sizeof(flash_crc)); + } + + if (check_fw_ver) { + /* Check FW version */ + ILI_INFO("New FW ver = 0x%x, Current FW ver = 0x%x\n", tfd.new_fw_cb, ilits->chip->fw_ver); + if (tfd.new_fw_cb != ilits->chip->fw_ver) { + ILI_INFO("FW version not matched\n"); + return UPDATE_FAIL; + } + } + + + /* Check Hex and HW CRC */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].end == 0) + continue; + + start_addr = fbi[i].start; + end_addr = fbi[i].end; + + len = fbi[i].end - fbi[i].start + 1 - 4; + hex_crc = CalculateCRC32(fbi[i].start, len, pfw); + + if (ili_fw_read_hw_crc(start_addr, end_addr - start_addr - crc_byte_len + 1, &hw_crc) < 0) { + ILI_ERR("Read HW CRC failed\n"); + return UPDATE_FAIL; + } + + ILI_INFO("Block = %d, HW CRC = 0x%06x, Hex CRC = 0x%06x\n", i, hw_crc, hex_crc); + if (hex_crc != hw_crc) { + ILI_ERR("Hex and HW CRC not matched\n"); + return UPDATE_FAIL; + } + } + + ILI_INFO("Flash FW is the same as targe file FW\n"); + return UPDATE_PASS; +} + +static int ilitek_tddi_fw_flash_program(u8 *pfw) +{ + u8 buf[512] = {0}; + u32 i = 0, j = 0, addr = 0, k = 0, recv_addr = 0; + int page = ilits->program_page; + bool skip = true; + + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].end == 0) + continue; + + ILI_INFO("Block[%d]: Programing from (0x%x) to (0x%x), tfd.end_addr = 0x%x\n", + i, fbi[i].start, fbi[i].end, tfd.end_addr); + + for (addr = fbi[i].start; addr < fbi[i].end; addr += page) { + buf[0] = 0x25; + buf[3] = 0x04; + buf[2] = 0x10; + buf[1] = 0x08; + + for (k = 0; k < page; k++) { + if (addr + k <= tfd.end_addr) + buf[4 + k] = pfw[addr + k]; + else + buf[4 + k] = 0xFF; + + if (buf[4 + k] != 0xFF) + skip = false; + } + + if (skip) { + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + return UPDATE_FAIL; + } + + ilitek_tddi_flash_write_enable(); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x2, 1) < 0) + ILI_ERR("Write 0x2 failed\n"); + + recv_addr = ((addr & 0xFF0000) >> 16) | (addr & 0x00FF00) | ((addr & 0x0000FF) << 16); + if (ili_ice_mode_write(FLASH2_ADDR, recv_addr, 3) < 0) + ILI_ERR("Write address failed\n"); + + if (ilits->wrapper(buf, page + 4, NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Failed to program data at start_addr = 0x%X, k = 0x%X, addr = 0x%x\n", + addr, k, addr + k); + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + return UPDATE_FAIL; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + + if (ilits->flash_mid == 0xEF) { + mdelay(1); + } else { + if (ilitek_tddi_flash_poll_busy(TIMEOUT_PROGRAM) < 0) + return UPDATE_FAIL; + } + if (ilits->fw_update_stat != ((j * 100) / tfd.end_addr)) { + ilits->fw_update_stat = (j * 100) / tfd.end_addr; + ILI_DBG("Program flahse data .... %d%c", ilits->fw_update_stat, '%'); + } + } + } + return UPDATE_PASS; +} + +static int ilitek_tddi_fw_flash_erase(void) +{ + int ret = 0; + u32 i = 0, addr = 0, recv_addr = 0; + bool bk_erase = false; + + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].end == 0) + continue; + + ILI_INFO("Block[%d]: Erasing from (0x%x) to (0x%x) \n", i, fbi[i].start, fbi[i].end); + + for (addr = fbi[i].start; addr <= fbi[i].end; addr += ilits->flash_sector) { + ilitek_tddi_flash_write_enable(); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (addr == fbi[AP].start) { + /* block erase with 64k bytes. */ + if (ili_ice_mode_write(FLASH2_ADDR, 0xD8, 1) < 0) + ILI_ERR("Write 0xB at %x failed\n", FLASH2_ADDR); + bk_erase = true; + } else { + /* sector erase with 4k bytes. */ + if (ili_ice_mode_write(FLASH2_ADDR, 0x20, 1) < 0) + ILI_ERR("Write 0x20 at %x failed\n", FLASH2_ADDR); + } + + recv_addr = ((addr & 0xFF0000) >> 16) | (addr & 0x00FF00) | ((addr & 0x0000FF) << 16); + if (ili_ice_mode_write(FLASH2_ADDR, recv_addr, 3) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + + /* Waitint for flash setting ready */ + mdelay(1); + + if (addr == fbi[AP].start) + ret = ilitek_tddi_flash_poll_busy(TIMEOUT_PAGE); + else + ret = ilitek_tddi_flash_poll_busy(TIMEOUT_SECTOR); + + if (ret < 0) + return UPDATE_FAIL; + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + + if (i == AP && tfd.is80k && bk_erase) { + addr = 0x10000 - 0x1000; + bk_erase = false; + } + } + } + return UPDATE_PASS; +} + +static int ilitek_fw_flash_upgrade(u8 *pfw) +{ + int ret = UPDATE_PASS; + if (ili_reset_ctrl(ilits->reset) < 0) { + ILI_ERR("TP reset failed during flash progam\n"); + return -EFW_REST; + } + + ilitek_fw_check_ddi_chunk(pfw); + + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) + return -EFW_ICE_MODE; + + ret = ilitek_tddi_flash_fw_crc_check(pfw, true); + if (ret == UPDATE_PASS) { + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) { + ILI_ERR("Disable ice mode failed, call reset instead\n"); + if (ili_reset_ctrl(ilits->reset) < 0) { + ILI_ERR("TP reset failed during flash progam\n"); + return -EFW_REST; + } + return UPDATE_PASS; + } + return UPDATE_PASS; + } else { + ILI_INFO("Flash FW and target file FW are different, do upgrade\n"); + } + + ret = ilitek_tddi_fw_flash_erase(); + if (ret == UPDATE_FAIL) + return -EFW_ERASE; + + ret = ilitek_tddi_fw_flash_program(pfw); + if (ret == UPDATE_FAIL) + return -EFW_PROGRAM; + + ret = ilitek_tddi_flash_fw_crc_check(pfw, false); + if (ret == UPDATE_FAIL) + return -EFW_CRC; + + /* We do have to reset chip in order to move new code from flash to iram. */ + if (ili_reset_ctrl(ilits->reset) < 0) { + ILI_ERR("TP reset failed after flash progam\n"); + ret = -EFW_REST; + } + return ret; +} + +static int ilitek_fw_calc_file_crc(u8 *pfw) +{ + int i; + u32 ex_addr, data_crc, file_crc; + + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].end == 0) + continue; + ex_addr = fbi[i].end; + data_crc = CalculateCRC32(fbi[i].start, fbi[i].len - 4, pfw); + file_crc = pfw[ex_addr - 3] << 24 | pfw[ex_addr - 2] << 16 | pfw[ex_addr - 1] << 8 | pfw[ex_addr]; + ILI_DBG("data crc = %x, file crc = %x\n", data_crc, file_crc); + if (data_crc != file_crc) { + ILI_ERR("Content of fw file is broken. (%d, %x, %x)\n", + i, data_crc, file_crc); + return -1; + } + } + + ILI_INFO("Content of fw file is correct\n"); + return 0; +} + +static void ilitek_tddi_fw_update_block_info(u8 *pfw) +{ + u32 fw_info_addr = 0, fw_mp_ver_addr = 0; + + fbi[AP].name = "AP"; + fbi[DATA].name = "DATA"; + fbi[TUNING].name = "TUNING"; + fbi[MP].name = "MP"; + fbi[GESTURE].name = "GESTURE"; + + /* upgrade mode define */ + fbi[DATA].mode = fbi[AP].mode = fbi[TUNING].mode = AP; + fbi[MP].mode = MP; + fbi[GESTURE].mode = GESTURE; + + if (fbi[AP].end > (64*K)) + tfd.is80k = true; + + /* Copy fw info */ + fw_info_addr = fbi[AP].end - INFO_HEX_ST_ADDR; + ILI_INFO("Parsing hex info start addr = 0x%x\n", fw_info_addr); + ipio_memcpy(ilits->fw_info, (pfw + fw_info_addr), sizeof(ilits->fw_info), sizeof(ilits->fw_info)); + + /* copy fw mp ver */ + fw_mp_ver_addr = fbi[MP].end - INFO_MP_HEX_ADDR; + ILI_INFO("Parsing hex mp ver addr = 0x%x\n", fw_mp_ver_addr); + ipio_memcpy(ilits->fw_mp_ver, pfw + fw_mp_ver_addr, sizeof(ilits->fw_mp_ver), sizeof(ilits->fw_mp_ver)); + + /* copy fw core ver */ + ilits->chip->core_ver = (ilits->fw_info[68] << 24) | (ilits->fw_info[69] << 16) | + (ilits->fw_info[70] << 8) | ilits->fw_info[71]; + ILI_INFO("New FW Core version = %x\n", ilits->chip->core_ver); + + /* Get hex fw vers */ + tfd.new_fw_cb = (ilits->fw_info[48] << 24) | (ilits->fw_info[49] << 16) | + (ilits->fw_info[50] << 8) | ilits->fw_info[51]; + + /* Get hex report info block*/ + ipio_memcpy(&ilits->rib, ilits->fw_info, sizeof(ilits->rib), sizeof(ilits->rib)); + ILI_INFO("report_info_block : nReportByPixel = %d, nIsHostDownload = %d, nIsSPIICE = %d, nIsSPISLAVE = %d\n", + ilits->rib.nReportByPixel, ilits->rib.nIsHostDownload, ilits->rib.nIsSPIICE, ilits->rib.nIsSPISLAVE); + ILI_INFO("report_info_block : nIsI2C = %d, nReserved00 = %d, nReserved01 = %x, nReserved02 = %x, nReserved03 = %x\n", + ilits->rib.nIsI2C, ilits->rib.nReserved00, ilits->rib.nReserved01, ilits->rib.nReserved02, ilits->rib.nReserved03); + + /* Calculate update address */ + ILI_INFO("New FW ver = 0x%x\n", tfd.new_fw_cb); + ILI_INFO("star_addr = 0x%06X, end_addr = 0x%06X, Block Num = %d\n", tfd.start_addr, tfd.end_addr, tfd.block_number); +} + +static int ilitek_tddi_fw_ili_convert(u8 *pfw) +{ + int i, size, blk_num = 0, blk_map = 0, num = 0; + int b0_addr = 0, b0_num = 0; + + if (ERR_ALLOC_MEM(ilits->md_fw_ili)) + return -ENOMEM; + + CTPM_FW = ilits->md_fw_ili; + size = ilits->md_fw_ili_size; + + if (size < ILI_FILE_HEADER || size > MAX_HEX_FILE_SIZE) { + ILI_ERR("size of ILI file is invalid\n"); + return -EINVAL; + } + + /* Check if it's old version of ILI format. */ + if (CTPM_FW[22] == 0xFF && CTPM_FW[23] == 0xFF && + CTPM_FW[24] == 0xFF && CTPM_FW[25] == 0xFF) { + ILI_ERR("Invaild ILI format, abort!\n"); + return -EINVAL; + } + + blk_num = CTPM_FW[131]; + blk_map = (CTPM_FW[129] << 8) | CTPM_FW[130]; + ILI_INFO("Parsing ILI file, block num = %d, block mapping = %x\n", blk_num, blk_map); + + if (blk_num > (FW_BLOCK_INFO_NUM - 1) || !blk_num || !blk_map) { + ILI_ERR("Number of block or block mapping is invalid, abort!\n"); + return -EINVAL; + } + + memset(fbi, 0x0, sizeof(fbi)); + tfd.start_addr = 0; + tfd.end_addr = 0; + tfd.hex_tag = BLOCK_TAG_AF; + + /* Parsing block info */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + /* B0 tag */ + b0_addr = (CTPM_FW[4 + i * 4] << 16) | (CTPM_FW[5 + i * 4] << 8) | (CTPM_FW[6 + i * 4]); + b0_num = CTPM_FW[7 + i * 4]; + if ((b0_num != 0) && (b0_addr != 0x000000)) + fbi[b0_num].fix_mem_start = b0_addr; + + /* AF tag */ + num = i + 1; + if (((blk_map >> i) & 0x01) == 0x01) { + fbi[num].start = (CTPM_FW[132 + i * 6] << 16) | (CTPM_FW[133 + i * 6] << 8) | CTPM_FW[134 + i * 6]; + fbi[num].end = (CTPM_FW[135 + i * 6] << 16) | (CTPM_FW[136 + i * 6] << 8) | CTPM_FW[137 + i * 6]; + + if (fbi[num].fix_mem_start == 0) + fbi[num].fix_mem_start = INT_MAX; + + fbi[num].len = fbi[num].end - fbi[num].start + 1; + ILI_INFO("Block[%d]: start_addr = %x, end = %x, fix_mem_start = 0x%x\n", num, fbi[num].start, + fbi[num].end, fbi[num].fix_mem_start); + } + } + + memcpy(pfw, CTPM_FW + ILI_FILE_HEADER, size - ILI_FILE_HEADER); + + if (ilitek_fw_calc_file_crc(pfw) < 0) + return -1; + + tfd.block_number = blk_num; + tfd.end_addr = size - ILI_FILE_HEADER; + return 0; +} + +static int ilitek_tddi_fw_hex_convert(u8 *phex, int size, u8 *pfw) +{ + int block = 0; + u32 i = 0, j = 0, k = 0, num = 0; + u32 len = 0, addr = 0, type = 0; + u32 start_addr = 0x0, end_addr = 0x0, ex_addr = 0; + u32 offset; + + memset(fbi, 0x0, sizeof(fbi)); + + /* Parsing HEX file */ + for (; i < size;) { + len = HexToDec(&phex[i + 1], 2); + addr = HexToDec(&phex[i + 3], 4); + type = HexToDec(&phex[i + 7], 2); + + if (type == 0x04) { + ex_addr = HexToDec(&phex[i + 9], 4); + } else if (type == 0x02) { + ex_addr = HexToDec(&phex[i + 9], 4); + ex_addr = ex_addr >> 12; + } else if (type == BLOCK_TAG_AF) { + /* insert block info extracted from hex */ + tfd.hex_tag = type; + if (tfd.hex_tag == BLOCK_TAG_AF) + num = HexToDec(&phex[i + 9 + 6 + 6], 2); + else + num = 0xFF; + + if (num > (FW_BLOCK_INFO_NUM - 1)) { + ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n", + num, FW_BLOCK_INFO_NUM - 1); + return -EINVAL; + } + + fbi[num].start = HexToDec(&phex[i + 9], 6); + fbi[num].end = HexToDec(&phex[i + 9 + 6], 6); + fbi[num].fix_mem_start = INT_MAX; + fbi[num].len = fbi[num].end - fbi[num].start + 1; + ILI_INFO("Block[%d]: start_addr = %x, end = %x", num, fbi[num].start, fbi[num].end); + + block++; + } else if (type == BLOCK_TAG_B0 && tfd.hex_tag == BLOCK_TAG_AF) { + num = HexToDec(&phex[i + 9 + 6], 2); + + if (num > (FW_BLOCK_INFO_NUM - 1)) { + ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n", + num, FW_BLOCK_INFO_NUM - 1); + return -EINVAL; + } + + fbi[num].fix_mem_start = HexToDec(&phex[i + 9], 6); + ILI_INFO("Tag 0xB0: change Block[%d] to addr = 0x%x\n", num, fbi[num].fix_mem_start); + } + + addr = addr + (ex_addr << 16); + + if (phex[i + 1 + 2 + 4 + 2 + (len * 2) + 2] == 0x0D) + offset = 2; + else + offset = 1; + + if (addr > MAX_HEX_FILE_SIZE) { + ILI_ERR("Invalid hex format %d\n", addr); + return -1; + } + + if (type == 0x00) { + end_addr = addr + len; + if (addr < start_addr) + start_addr = addr; + /* fill data */ + for (j = 0, k = 0; j < (len * 2); j += 2, k++) + pfw[addr + k] = HexToDec(&phex[i + 9 + j], 2); + } + i += 1 + 2 + 4 + 2 + (len * 2) + 2 + offset; + } + + if (ilitek_fw_calc_file_crc(pfw) < 0) + return -1; + + tfd.start_addr = start_addr; + tfd.end_addr = end_addr; + tfd.block_number = block; + return 0; +} + +static int ilitek_tdd_fw_hex_open(u8 op, u8 *pfw) +{ + int ret =0, fsize = 0; + const struct firmware *fw = NULL; + struct file *f = NULL; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + mm_segment_t old_fs; +#endif + + loff_t pos = 0; + + ILI_INFO("Open file method = %s, path = %s\n", + op ? "FILP_OPEN" : "REQUEST_FIRMWARE", + op ? ilits->md_fw_filp_path : ilits->md_fw_rq_path); + + switch (op) { + case REQUEST_FIRMWARE: + if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) { + ILI_ERR("Request firmware failed, try again\n"); + if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) { + ILI_ERR("Request firmware failed after retry\n"); + ret = -1; + goto out; + } + } + + fsize = fw->size; + ILI_INFO("fsize = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is zero\n"); + release_firmware(fw); + ret = -1; + goto out; + } + + ilits->tp_fw.size = 0; + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n"); + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw after retry\n"); + release_firmware(fw); + ret = -ENOMEM; + goto out; + } + } + + /* Copy fw data got from request_firmware to global */ + ipio_memcpy((u8 *)ilits->tp_fw.data, fw->data, fsize * sizeof(*fw->data), fsize); + ilits->tp_fw.size = fsize; + release_firmware(fw); + break; + case FILP_OPEN: + f = filp_open(ilits->md_fw_filp_path, O_RDONLY, 0644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file at %ld\n", PTR_ERR(f)); + ret = -1; + goto out; + } + + fsize = f->f_inode->i_size; + ILI_INFO("fsize = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is invaild\n"); + filp_close(f, NULL); + ret = -1; + goto out; + } + + ipio_vfree((void **)&(ilits->tp_fw.data)); + ilits->tp_fw.size = 0; + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n"); + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw after retry\n"); + filp_close(f, NULL); + ret = -ENOMEM; + goto out; + } + } + + /* ready to map user's memory to obtain data by reading files */ + pos = 0; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + old_fs = get_fs(); + set_fs(get_ds()); + set_fs(KERNEL_DS); + vfs_read(f, (u8 *)ilits->tp_fw.data, fsize, &pos); + set_fs(old_fs); +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) + kernel_read(f, (u8 *)ilits->tp_fw.data, fsize, &pos); +#endif + + filp_close(f, NULL); + ilits->tp_fw.size = fsize; + break; + default: + ILI_ERR("Unknown open file method, %d\n", op); + break; + } + + if (ERR_ALLOC_MEM(ilits->tp_fw.data) || ilits->tp_fw.size <= 0) { + ILI_ERR("fw data/size is invaild\n"); + ret = -1; + goto out; + } + + /* Convert hex and copy data from tp_fw.data to pfw */ + if (ilitek_tddi_fw_hex_convert((u8 *)ilits->tp_fw.data, ilits->tp_fw.size, pfw) < 0) { + ILI_ERR("Convert hex file failed\n"); + ret = -1; + } + +out: + ipio_vfree((void **)&(ilits->tp_fw.data)); + return ret; +} + +int ili_fw_upgrade(int op) +{ + int i, ret = 0, retry = 3; + + if (!ilits->boot || ilits->force_fw_update || ERR_ALLOC_MEM(pfw)) { + if (ERR_ALLOC_MEM(pfw)) { + ipio_vfree((void **)&pfw); + pfw = vmalloc(MAX_HEX_FILE_SIZE * sizeof(u8)); + if (ERR_ALLOC_MEM(pfw)) { + ILI_ERR("Failed to allocate pfw memory, %ld\n", PTR_ERR(pfw)); + ipio_vfree((void **)&pfw); + ret = -ENOMEM; + goto out; + } + } + + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + pfw[i] = 0xFF; + + if (ilitek_tdd_fw_hex_open(op, pfw) < 0) { + ILI_ERR("Open hex file fail, try upgrade from ILI file\n"); + + /* + * Users might not be aware of a broken hex file when recovering + * fw from ILI file. We should force them to check + * hex files they attempt to update via device node. + */ + if (ilits->node_update) { + ILI_ERR("Ignore update from ILI file\n"); + ipio_vfree((void **)&pfw); + return -EFW_CONVERT_FILE; + } + + if (ilitek_tddi_fw_ili_convert(pfw) < 0) { + ILI_ERR("Convert ILI file error\n"); + ret = -EFW_CONVERT_FILE; + goto out; + } + } + ilitek_tddi_fw_update_block_info(pfw); + + if (ilits->chip->core_ver >= CORE_VER_1470 && ilits->rib.nIsHostDownload == 1) { + ILI_ERR("hex file interface no match error\n"); + ret = -EFW_INTERFACE; + goto out; + } + } + + do { + ret = ilitek_fw_flash_upgrade(pfw); + if (ret == UPDATE_PASS) + break; + + ILI_ERR("Upgrade failed, do retry!\n"); + } while (--retry > 0); + + if (ret != UPDATE_PASS) { + ILI_ERR("Failed to upgrade fw %d times, erasing flash\n", retry); + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed while erasing flash\n"); + if (ilitek_tddi_fw_flash_erase() < 0) + ILI_ERR("Failed to erase flash\n"); + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Disable ice mode failed after erase flash\n"); + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP reset failed after erase flash\n"); + } + +out: + if (ret < 0) + ilits->info_from_hex = DISABLE; + + if (atomic_read(&ilits->ice_stat)) + ili_ice_mode_ctrl(DISABLE, OFF); + + ili_ic_get_core_ver(); + ili_ic_get_protocl_ver(); + ili_ic_get_fw_ver(); + ili_ic_get_tp_info(); + ili_ic_get_panel_info(); + ili_ic_func_ctrl_reset(); + + if (!ilits->info_from_hex) + ilits->info_from_hex = ENABLE; + + ipio_vfree((void **)&pfw); + return ret; +} + +struct flash_table { + u16 mid; + u16 dev_id; + int mem_size; + int program_page; + int sector; +} flash_t[] = { + [0] = {0x00, 0x0000, (256 * K), 256, (4 * K)}, /* Default */ + [1] = {0xEF, 0x6011, (128 * K), 256, (4 * K)}, /* W25Q10EW */ + [2] = {0xEF, 0x6012, (256 * K), 256, (4 * K)}, /* W25Q20EW */ + [3] = {0xC8, 0x6012, (256 * K), 256, (4 * K)}, /* GD25LQ20B */ + [4] = {0xC8, 0x6013, (512 * K), 256, (4 * K)}, /* GD25LQ40 */ + [5] = {0x85, 0x6013, (4 * M), 256, (4 * K)}, + [6] = {0xC2, 0x2812, (256 * K), 256, (4 * K)}, + [7] = {0x1C, 0x3812, (256 * K), 256, (4 * K)}, +}; + +void ili_fw_read_flash_info(void) +{ + int i = 0; + u8 buf[4] = {0}; + u8 cmd = 0x9F; + u32 tmp = 0; + u16 flash_id = 0, flash_mid = 0; + bool ice = atomic_read(&ilits->ice_stat); + + if (!ice) { + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed while reading flash info\n"); + } + + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_rx_dual, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_rx_dual, FLASH0_ADDR); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, cmd, 1) < 0) + ILI_ERR("Write 0x9F failed\n"); + + for (i = 0; i < ARRAY_SIZE(buf); i++) { + if (ili_ice_mode_write(FLASH2_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + if (ili_ice_mode_read(FLASH4_ADDR, &tmp, sizeof(u8)) < 0) + ILI_ERR("Read flash info error\n"); + + buf[i] = tmp; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); /* CS high */ + + flash_mid = buf[0]; + flash_id = buf[1] << 8 | buf[2]; + + for (i = 0; i < ARRAY_SIZE(flash_t); i++) { + if (flash_mid == flash_t[i].mid && flash_id == flash_t[i].dev_id) { + ilits->flash_mid = flash_t[i].mid; + ilits->flash_devid = flash_t[i].dev_id; + ilits->program_page = flash_t[i].program_page; + ilits->flash_sector = flash_t[i].sector; + break; + } + } + + if (i >= ARRAY_SIZE(flash_t)) { + ILI_INFO("Not found flash id in tab, use default\n"); + ilits->flash_mid = flash_t[0].mid; + ilits->flash_devid = flash_t[0].dev_id; + ilits->program_page = flash_t[0].program_page; + ilits->flash_sector = flash_t[0].sector; + } + + ILI_INFO("Flash MID = %x, Flash DEV_ID = %x\n", ilits->flash_mid, ilits->flash_devid); + ILI_INFO("Flash program page = %d\n", ilits->program_page); + ILI_INFO("Flash sector = %d\n", ilits->flash_sector); + + ilitek_tddi_flash_protect(DISABLE); + + if (!ice) { + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Disable ice mode failed while reading flash info\n"); + } +} diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_hostdl.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_hostdl.c new file mode 100755 index 000000000000..99035dd05914 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_hostdl.c @@ -0,0 +1,932 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" + +#define UPDATE_PASS 0 +#define UPDATE_FAIL -1 +#define TIMEOUT_SECTOR 500 +#define TIMEOUT_PAGE 3500 +#define TIMEOUT_PROGRAM 10 + +static struct touch_fw_data { + u8 block_number; + u32 start_addr; + u32 end_addr; + u32 new_fw_cb; + int delay_after_upgrade; + bool isCRC; + bool isboot; + bool is80k; + int hex_tag; +} tfd; + +static struct flash_block_info { + char *name; + u32 start; + u32 end; + u32 len; + u32 mem_start; + u32 fix_mem_start; + u8 mode; +} fbi[FW_BLOCK_INFO_NUM]; + +static u8 *pfw; +static u8 *CTPM_FW; + +static u32 HexToDec(char *phex, s32 len) +{ + u32 ret = 0, temp = 0, i; + s32 shift = (len - 1) * 4; + + for (i = 0; i < len; shift -= 4, i++) { + if ((phex[i] >= '0') && (phex[i] <= '9')) + temp = phex[i] - '0'; + else if ((phex[i] >= 'a') && (phex[i] <= 'f')) + temp = (phex[i] - 'a') + 10; + else if ((phex[i] >= 'A') && (phex[i] <= 'F')) + temp = (phex[i] - 'A') + 10; + + ret |= (temp << shift); + } + return ret; +} + +static int CalculateCRC32(u32 start_addr, u32 len, u8 *pfw) +{ + int i = 0, j = 0; + int crc_poly = 0x04C11DB7; + int tmp_crc = 0xFFFFFFFF; + + for (i = start_addr; i < start_addr + len; i++) { + tmp_crc ^= (pfw[i] << 24); + + for (j = 0; j < 8; j++) { + if ((tmp_crc & 0x80000000) != 0) + tmp_crc = tmp_crc << 1 ^ crc_poly; + else + tmp_crc = tmp_crc << 1; + } + } + return tmp_crc; +} + +static int calc_hw_dma_crc(u32 start_addr, u32 block_size) +{ + int count = 50; + u32 busy = 0; + + /* dma1 src1 address */ + if (ili_ice_mode_write(0x072104, start_addr, 4) < 0) + ILI_ERR("Write dma1 src1 address failed\n"); + /* dma1 src1 format */ + if (ili_ice_mode_write(0x072108, 0x80000001, 4) < 0) + ILI_ERR("Write dma1 src1 format failed\n"); + /* dma1 dest address */ + if (ili_ice_mode_write(0x072114, 0x0002725C, 4) < 0) + ILI_ERR("Write dma1 src1 format failed\n"); + /* dma1 dest format */ + if (ili_ice_mode_write(0x072118, 0x80000000, 4) < 0) + ILI_ERR("Write dma1 dest format failed\n"); + /* Block size*/ + if (ili_ice_mode_write(0x07211C, block_size, 4) < 0) + ILI_ERR("Write block size (%d) failed\n", block_size); + /* crc off */ + if (ili_ice_mode_write(0x041016, 0x00, 1) < 0) + ILI_INFO("Write crc of failed\n"); + /* dma crc */ + if (ili_ice_mode_write(0x041017, 0x03, 1) < 0) + ILI_ERR("Write dma 1 crc failed\n"); + /* Dma1 stop */ + if (ili_ice_mode_write(0x072100, 0x02000000, 4) < 0) + ILI_ERR("Write dma1 stop failed\n"); + /* crc on */ + if (ili_ice_mode_write(0x041016, 0x01, 1) < 0) + ILI_ERR("Write crc on failed\n"); + /* clr int */ + if (ili_ice_mode_write(0x048006, 0x2, 1) < 0) + ILI_ERR("Write clr int failed\n"); + /* Dma1 start */ + if (ili_ice_mode_write(0x072100, 0x01000000, 4) < 0) + ILI_ERR("Write dma1 start failed\n"); + + /* Polling BIT0 */ + while (count > 0) { + mdelay(1); + if (ili_ice_mode_read(0x048006, &busy, sizeof(u8)) < 0) + ILI_ERR("Read busy error\n"); + ILI_DBG("busy = %x\n", busy); + if ((busy & 0x02) == 2) + break; + count--; + } + + if (count <= 0) { + ILI_ERR("BIT0 is busy\n"); + return -1; + } + + if (ili_ice_mode_read(0x04101C, &busy, sizeof(u32)) < 0) { + ILI_ERR("Read dma crc error\n"); + return -1; + } + return busy; +} + +static int ilitek_tddi_fw_iram_read(u8 *buf, u32 start, int len) +{ + int limit = SPI_RX_BUF_SIZE; + int addr = 0, loop = 0, tmp_len = len, cnt = 0; + u8 cmd[4] = {0}; + + if (!buf) { + ILI_ERR("buf is null\n"); + return -ENOMEM; + } + + if (len % limit) + loop = (len / limit) + 1; + else + loop = len / limit; + + for (cnt = 0, addr = start; cnt < loop; cnt++, addr += limit) { + if (tmp_len > limit) + tmp_len = limit; + + cmd[0] = 0x25; + cmd[3] = (char)((addr & 0x00FF0000) >> 16); + cmd[2] = (char)((addr & 0x0000FF00) >> 8); + cmd[1] = (char)((addr & 0x000000FF)); + + if (ilits->wrapper(cmd, 4, NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Failed to write iram data\n"); + return -ENODEV; + } + + if (ilits->wrapper(NULL, 0, buf + cnt * limit, tmp_len, OFF, OFF) < 0) { + ILI_ERR("Failed to Read iram data\n"); + return -ENODEV; + } + + tmp_len = len - cnt * limit; + ilits->fw_update_stat = ((len - tmp_len) * 100) / len; + ILI_INFO("Reading iram data .... %d%c", ilits->fw_update_stat, '%'); + } + return 0; +} + +int ili_fw_dump_iram_data(u32 start, u32 end, bool save) +{ + struct file *f = NULL; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + mm_segment_t old_fs; +#endif + loff_t pos = 0; + int i, ret = 0; + int len, tmp = debug_en; + bool ice = atomic_read(&ilits->ice_stat); + + if (!ice) { + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) { + ILI_ERR("Enable ice mode failed\n"); + return ret; + } + } + + len = end - start + 1; + + if (len > MAX_HEX_FILE_SIZE) { + ILI_ERR("len is larger than buffer, abort\n"); + ret = -EINVAL; + goto out; + } + + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + ilits->update_buf[i] = 0xFF; + + ret = ilitek_tddi_fw_iram_read(ilits->update_buf, start, len); + if (ret < 0) { + ILI_ERR("Read IRAM data failed\n"); + goto out; + } + + if (save) { + f = filp_open(DUMP_IRAM_PATH, O_WRONLY | O_CREAT | O_TRUNC, 644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file at %ld.\n", PTR_ERR(f)); + ret = -ENOMEM; + goto out; + } + + pos = 0; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + old_fs = get_fs(); + set_fs(get_ds()); + set_fs(KERNEL_DS); + vfs_write(f, ilits->update_buf, len, &pos); + set_fs(old_fs); + +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) + kernel_write(f, ilits->update_buf, len, &pos); +#endif + filp_close(f, NULL); + ILI_INFO("Save iram data to %s\n", DUMP_IRAM_PATH); + } else { + debug_en = DEBUG_ALL; + ili_dump_data(ilits->update_buf, 8, len, 0, "IRAM"); + debug_en = tmp; + } + +out: + if (!ice) { + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed after code reset\n"); + } + + ILI_INFO("Dump IRAM %s\n", (ret < 0) ? "FAIL" : "SUCCESS"); + return ret; +} + +static int ilitek_tddi_fw_iram_program(u32 start, u8 *w_buf, u32 w_len, u32 split_len) +{ + int i = 0, j = 0, addr = 0; + u32 end = start + w_len; + bool fix_4_alignment = false; + + if (split_len % 4 > 0) + ILI_ERR("Since split_len must be four-aligned, it must be a multiple of four"); + + if (split_len != 0) { + for (addr = start, i = 0; addr < end; addr += split_len, i += split_len) { + if ((addr + split_len) > end) { + split_len = end - addr; + if (split_len % 4 != 0) + fix_4_alignment = true; + } + + ilits->update_buf[0] = SPI_WRITE; + ilits->update_buf[1] = 0x25; + ilits->update_buf[2] = (char)((addr & 0x000000FF)); + ilits->update_buf[3] = (char)((addr & 0x0000FF00) >> 8); + ilits->update_buf[4] = (char)((addr & 0x00FF0000) >> 16); + + for (j = 0; j < split_len; j++) + ilits->update_buf[5 + j] = w_buf[i + j]; + + if (fix_4_alignment) { + ILI_INFO("org split_len = 0x%X\n", split_len); + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 4, ilits->update_buf[5 + split_len - 4]); + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 3, ilits->update_buf[5 + split_len - 3]); + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 2, ilits->update_buf[5 + split_len - 2]); + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 1, ilits->update_buf[5 + split_len - 1]); + for (j = 0; j < (4 - (split_len % 4)); j++) { + ilits->update_buf[5 + j + split_len] = 0xFF; + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n",j + split_len, ilits->update_buf[5 + j + split_len]); + } + + ILI_INFO("split_len %% 4 = %d\n", split_len % 4); + split_len = split_len + (4 - (split_len % 4)); + ILI_INFO("fix split_len = 0x%X\n", split_len); + } + if (ilits->spi_write_then_read(ilits->spi, ilits->update_buf, split_len + 5, NULL, 0)) { + ILI_ERR("Failed to write data via SPI in host download (%x)\n", split_len + 5); + return -EIO; + } + ilits->fw_update_stat = (i * 100) / w_len; + } + } else { + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + ilits->update_buf[i] = 0xFF; + + ilits->update_buf[0] = SPI_WRITE; + ilits->update_buf[1] = 0x25; + ilits->update_buf[2] = (char)((start & 0x000000FF)); + ilits->update_buf[3] = (char)((start & 0x0000FF00) >> 8); + ilits->update_buf[4] = (char)((start & 0x00FF0000) >> 16); + + memcpy(&ilits->update_buf[5], w_buf, w_len); + if (w_len % 4 != 0) { + ILI_INFO("org w_len = %d\n", w_len); + w_len = w_len + (4 - (w_len % 4)); + ILI_INFO("w_len = %d w_len %% 4 = %d\n", w_len, w_len % 4); + } + /* It must be supported by platforms that have the ability to transfer all data at once. */ + if (ilits->spi_write_then_read(ilits->spi, ilits->update_buf, w_len + 5, NULL, 0) < 0) { + ILI_ERR("Failed to write data via SPI in host download (%x)\n", w_len + 5); + return -EIO; + } + } + return 0; +} + +static int ilitek_tddi_fw_iram_upgrade(u8 *pfw) +{ + int i, ret = UPDATE_PASS; + u32 mode, crc, dma, iram_crc = 0; + u8 *fw_ptr = NULL, crc_temp[4], crc_len = 4; + bool iram_crc_err = false; + + if (!ilits->ddi_rest_done) { + if (ilits->actual_tp_mode != P5_X_FW_GESTURE_MODE) + ili_reset_ctrl(ilits->reset); + + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) + return -EFW_ICE_MODE; + } else { + /* Restore it if the wq of load_fw_ddi has been called. */ + ilits->ddi_rest_done = false; + } + + if (ilits->chip->dma_reset) { + ILI_INFO("operate dma reset in reg after tp reset\n"); + if (ili_ice_mode_write(0x40040, 0x00800000, 4) < 0) + ILI_INFO("Failed to open DMA reset\n"); + if (ili_ice_mode_write(0x40040, 0x00000000, 4) < 0) + ILI_INFO("Failed to close DMA reset\n"); + } + + /* Point to pfw with different addresses for getting its block data. */ + fw_ptr = pfw; + if (ilits->actual_tp_mode == P5_X_FW_TEST_MODE) { + + mode = MP; + } else if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) { + mode = GESTURE; + crc_len = 0; + } else { + mode = AP; + } + + /* Program data to iram acorrding to each block */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].mode == mode && fbi[i].len != 0) { + ILI_DBG("Download %s code from hex 0x%x to IRAM 0x%x, len = 0x%x\n", + fbi[i].name, fbi[i].start, fbi[i].mem_start, fbi[i].len); + +#if SPI_DMA_TRANSFER_SPLIT + if (ilitek_tddi_fw_iram_program(fbi[i].mem_start, (fw_ptr + fbi[i].start), fbi[i].len, SPI_UPGRADE_LEN) < 0) + ILI_ERR("IRAM program failed\n"); +#else + if (ilitek_tddi_fw_iram_program(fbi[i].mem_start, (fw_ptr + fbi[i].start), fbi[i].len, 0) < 0) + ILI_ERR("IRAM program failed\n"); +#endif + crc = CalculateCRC32(fbi[i].start, fbi[i].len - crc_len, fw_ptr); + dma = calc_hw_dma_crc(fbi[i].mem_start, fbi[i].len - crc_len); + + if (mode != GESTURE) { + ilitek_tddi_fw_iram_read(crc_temp, (fbi[i].mem_start + fbi[i].len - crc_len), sizeof(crc_temp)); + iram_crc = crc_temp[0] << 24 | crc_temp[1] << 16 |crc_temp[2] << 8 | crc_temp[3]; + if (iram_crc != dma) + iram_crc_err = true; + } + + ILI_INFO("%s CRC is %s hex(%x) : dma(%x) : iram(%x), calculation len is 0x%x\n", + fbi[i].name,((crc != dma)||(iram_crc_err)) ? "Invalid !" : "Correct !", crc, dma, iram_crc, fbi[i].len - crc_len); + + if ((crc != dma)|| iram_crc_err) { + ILI_ERR("CRC Error! print iram data with first 16 bytes\n"); + ili_fw_dump_iram_data(0x0, 0xF, false); + return -EFW_CRC; + } + } + } + + if (ilits->actual_tp_mode != P5_X_FW_GESTURE_MODE) { + if (ili_reset_ctrl(TP_IC_CODE_RST) < 0) { + ILI_ERR("TP Code reset failed during iram programming\n"); + ret = -EFW_REST; + return ret; + } + } + + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) { + ILI_ERR("Disable ice mode failed after code reset\n"); + ret = -EFW_ICE_MODE; + } + + /* Waiting for fw ready sending first cmd */ + if (!ilits->info_from_hex || (ilits->chip->core_ver < CORE_VER_1410)) + mdelay(100); + else + mdelay(20); + + return ret; +} + +static int ilitek_fw_calc_file_crc(u8 *pfw) +{ + int i; + u32 ex_addr, data_crc, file_crc; + + for (i = 0; i < ARRAY_SIZE(fbi); i++) { + if (fbi[i].end == 0) + continue; + ex_addr = fbi[i].end; + data_crc = CalculateCRC32(fbi[i].start, fbi[i].len - 4, pfw); + file_crc = pfw[ex_addr - 3] << 24 | pfw[ex_addr - 2] << 16 | pfw[ex_addr - 1] << 8 | pfw[ex_addr]; + ILI_DBG("data crc = %x, file crc = %x\n", data_crc, file_crc); + if (data_crc != file_crc) { + ILI_ERR("Content of fw file is broken. (%d, %x, %x)\n", + i, data_crc, file_crc); + return -1; + } + } + + ILI_INFO("Content of fw file is correct\n"); + return 0; +} + +static int ilitek_tddi_fw_update_block_info(u8 *pfw) +{ + u32 ges_area_section = 0, ges_info_addr = 0, ges_fw_start = 0, ges_fw_end = 0; + u32 ap_end = 0, ap_len = 0; + u32 fw_info_addr = 0, fw_mp_ver_addr = 0; + + if (tfd.hex_tag != BLOCK_TAG_AF) { + ILI_ERR("HEX TAG is invalid (0x%X)\n", tfd.hex_tag); + return -EINVAL; + } + + fbi[AP].mem_start = (fbi[AP].fix_mem_start != INT_MAX) ? fbi[AP].fix_mem_start : 0; + fbi[DATA].mem_start = (fbi[DATA].fix_mem_start != INT_MAX) ? fbi[DATA].fix_mem_start : DLM_START_ADDRESS; + fbi[TUNING].mem_start = (fbi[TUNING].fix_mem_start != INT_MAX) ? fbi[TUNING].fix_mem_start : fbi[DATA].mem_start + fbi[DATA].len; + fbi[MP].mem_start = (fbi[MP].fix_mem_start != INT_MAX) ? fbi[MP].fix_mem_start : 0; + fbi[GESTURE].mem_start = (fbi[GESTURE].fix_mem_start != INT_MAX) ? fbi[GESTURE].fix_mem_start : 0; + fbi[TAG].mem_start = (fbi[TAG].fix_mem_start != INT_MAX) ? fbi[TAG].fix_mem_start : 0; + fbi[PARA_BACKUP].mem_start = (fbi[PARA_BACKUP].fix_mem_start != INT_MAX) ? fbi[PARA_BACKUP].fix_mem_start : 0; + fbi[DDI].mem_start = (fbi[DDI].fix_mem_start != INT_MAX) ? fbi[DDI].fix_mem_start : 0; + + /* Parsing gesture info form AP code */ + ges_info_addr = (fbi[AP].end + 1 - 60); + ges_area_section = (pfw[ges_info_addr + 3] << 24) + (pfw[ges_info_addr + 2] << 16) + (pfw[ges_info_addr + 1] << 8) + pfw[ges_info_addr]; + fbi[GESTURE].mem_start = (pfw[ges_info_addr + 7] << 24) + (pfw[ges_info_addr + 6] << 16) + (pfw[ges_info_addr + 5] << 8) + pfw[ges_info_addr + 4]; + ap_end = (pfw[ges_info_addr + 11] << 24) + (pfw[ges_info_addr + 10] << 16) + (pfw[ges_info_addr + 9] << 8) + pfw[ges_info_addr + 8]; + + if (ap_end != fbi[GESTURE].mem_start) + ap_len = ap_end - fbi[GESTURE].mem_start + 1; + + ges_fw_start = (pfw[ges_info_addr + 15] << 24) + (pfw[ges_info_addr + 14] << 16) + (pfw[ges_info_addr + 13] << 8) + pfw[ges_info_addr + 12]; + ges_fw_end = (pfw[ges_info_addr + 19] << 24) + (pfw[ges_info_addr + 18] << 16) + (pfw[ges_info_addr + 17] << 8) + pfw[ges_info_addr + 16]; + + if (ges_fw_end != ges_fw_start) + fbi[GESTURE].len = ges_fw_end - ges_fw_start; + + /* update gesture address */ + fbi[GESTURE].start = ges_fw_start; + + ILI_INFO("==== Gesture loader info ====\n"); + ILI_INFO("gesture move to ap addr => start = 0x%x, ap_end = 0x%x, ap_len = 0x%x\n", fbi[GESTURE].mem_start, ap_end, ap_len); + ILI_INFO("gesture hex addr => start = 0x%x, gesture_end = 0x%x, gesture_len = 0x%x\n", ges_fw_start, ges_fw_end, fbi[GESTURE].len); + ILI_INFO("=============================\n"); + + fbi[AP].name = "AP"; + fbi[DATA].name = "DATA"; + fbi[TUNING].name = "TUNING"; + fbi[MP].name = "MP"; + fbi[GESTURE].name = "GESTURE"; + fbi[TAG].name = "TAG"; + fbi[PARA_BACKUP].name = "PARA_BACKUP"; + fbi[DDI].name = "DDI"; + + /* upgrade mode define */ + fbi[DATA].mode = fbi[AP].mode = fbi[TUNING].mode = AP; + fbi[MP].mode = MP; + fbi[GESTURE].mode = GESTURE; + + if (fbi[AP].end > (64*K)) + tfd.is80k = true; + + /* Copy fw info */ + fw_info_addr = fbi[AP].end - INFO_HEX_ST_ADDR; + ILI_INFO("Parsing hex info start addr = 0x%x\n", fw_info_addr); + ipio_memcpy(ilits->fw_info, (pfw + fw_info_addr), sizeof(ilits->fw_info), sizeof(ilits->fw_info)); + + /* copy fw mp ver */ + fw_mp_ver_addr = fbi[MP].end - INFO_MP_HEX_ADDR; + ILI_INFO("Parsing hex mp ver addr = 0x%x\n", fw_mp_ver_addr); + ipio_memcpy(ilits->fw_mp_ver, pfw + fw_mp_ver_addr, sizeof(ilits->fw_mp_ver), sizeof(ilits->fw_mp_ver)); + + /* copy fw core ver */ + ilits->chip->core_ver = (ilits->fw_info[68] << 24) | (ilits->fw_info[69] << 16) | + (ilits->fw_info[70] << 8) | ilits->fw_info[71]; + ILI_INFO("New FW Core version = %x\n", ilits->chip->core_ver); + + /* Get hex fw vers */ + tfd.new_fw_cb = (ilits->fw_info[48] << 24) | (ilits->fw_info[49] << 16) | + (ilits->fw_info[50] << 8) | ilits->fw_info[51]; + + /* Get hex report info block*/ + ipio_memcpy(&ilits->rib, ilits->fw_info, sizeof(ilits->rib), sizeof(ilits->rib)); + ILI_INFO("report_info_block : nReportByPixel = %d, nIsHostDownload = %d, nIsSPIICE = %d, nIsSPISLAVE = %d\n", + ilits->rib.nReportByPixel, ilits->rib.nIsHostDownload, ilits->rib.nIsSPIICE, ilits->rib.nIsSPISLAVE); + ILI_INFO("report_info_block : nIsI2C = %d, nReserved00 = %d, nReserved01 = %x, nReserved02 = %x, nReserved03 = %x\n", + ilits->rib.nIsI2C, ilits->rib.nReserved00, ilits->rib.nReserved01, ilits->rib.nReserved02, ilits->rib.nReserved03); + + /* Calculate update address */ + ILI_INFO("New FW ver = 0x%x\n", tfd.new_fw_cb); + ILI_INFO("star_addr = 0x%06X, end_addr = 0x%06X, Block Num = %d\n", tfd.start_addr, tfd.end_addr, tfd.block_number); + + return 0; +} + +static int ilitek_tddi_fw_ili_convert(u8 *pfw) +{ + int i, size, blk_num = 0, blk_map = 0, num; + int b0_addr = 0, b0_num = 0; + + if (ERR_ALLOC_MEM(ilits->md_fw_ili)) + return -ENOMEM; + + CTPM_FW = ilits->md_fw_ili; + size = ilits->md_fw_ili_size; + + if (size < ILI_FILE_HEADER || size > MAX_HEX_FILE_SIZE) { + ILI_ERR("size of ILI file is invalid\n"); + return -EINVAL; + } + + /* Check if it's old version of ILI format. */ + if (CTPM_FW[22] == 0xFF && CTPM_FW[23] == 0xFF && + CTPM_FW[24] == 0xFF && CTPM_FW[25] == 0xFF) { + ILI_ERR("Invaild ILI format, abort!\n"); + return -EINVAL; + } + + blk_num = CTPM_FW[131]; + blk_map = (CTPM_FW[129] << 8) | CTPM_FW[130]; + ILI_INFO("Parsing ILI file, block num = %d, block mapping = %x\n", blk_num, blk_map); + + if (blk_num > (FW_BLOCK_INFO_NUM - 1) || !blk_num || !blk_map) { + ILI_ERR("Number of block or block mapping is invalid, abort!\n"); + return -EINVAL; + } + + memset(fbi, 0x0, sizeof(fbi)); + + tfd.start_addr = 0; + tfd.end_addr = 0; + tfd.hex_tag = BLOCK_TAG_AF; + + /* Parsing block info */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + /* B0 tag */ + b0_addr = (CTPM_FW[4 + i * 4] << 16) | (CTPM_FW[5 + i * 4] << 8) | (CTPM_FW[6 + i * 4]); + b0_num = CTPM_FW[7 + i * 4]; + if ((b0_num != 0) && (b0_addr != 0x000000)) + fbi[b0_num].fix_mem_start = b0_addr; + + /* AF tag */ + num = i + 1; + if (((blk_map >> i) & 0x01) == 0x01) { + fbi[num].start = (CTPM_FW[132 + i * 6] << 16) | (CTPM_FW[133 + i * 6] << 8) | CTPM_FW[134 + i * 6]; + fbi[num].end = (CTPM_FW[135 + i * 6] << 16) | (CTPM_FW[136 + i * 6] << 8) | CTPM_FW[137 + i * 6]; + + if (fbi[num].fix_mem_start == 0) + fbi[num].fix_mem_start = INT_MAX; + + fbi[num].len = fbi[num].end - fbi[num].start + 1; + ILI_DBG("Block[%d]: start_addr = %x, end = %x, fix_mem_start = 0x%x\n", num, fbi[num].start, + fbi[num].end, fbi[num].fix_mem_start); + if (num == GESTURE) + ilits->gesture_load_code = true; + } + } + + memcpy(pfw, CTPM_FW + ILI_FILE_HEADER, size - ILI_FILE_HEADER); + + if (ilitek_fw_calc_file_crc(pfw) < 0) + return -1; + + tfd.block_number = blk_num; + tfd.end_addr = size - ILI_FILE_HEADER; + return 0; +} + +static int ilitek_tddi_fw_hex_convert(u8 *phex, int size, u8 *pfw) +{ + int block = 0; + u32 i = 0, j = 0, k = 0, num = 0; + u32 len = 0, addr = 0, type = 0; + u32 start_addr = 0x0, end_addr = 0x0, ex_addr = 0; + u32 offset; + + memset(fbi, 0x0, sizeof(fbi)); + + /* Parsing HEX file */ + for (; i < size;) { + len = HexToDec(&phex[i + 1], 2); + addr = HexToDec(&phex[i + 3], 4); + type = HexToDec(&phex[i + 7], 2); + + if (type == 0x04) { + ex_addr = HexToDec(&phex[i + 9], 4); + } else if (type == 0x02) { + ex_addr = HexToDec(&phex[i + 9], 4); + ex_addr = ex_addr >> 12; + } else if (type == BLOCK_TAG_AF) { + /* insert block info extracted from hex */ + tfd.hex_tag = type; + if (tfd.hex_tag == BLOCK_TAG_AF) + num = HexToDec(&phex[i + 9 + 6 + 6], 2); + else + num = 0xFF; + + if (num > (FW_BLOCK_INFO_NUM - 1)) { + ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n", + num, FW_BLOCK_INFO_NUM - 1); + return -EINVAL; + } + + fbi[num].start = HexToDec(&phex[i + 9], 6); + fbi[num].end = HexToDec(&phex[i + 9 + 6], 6); + fbi[num].fix_mem_start = INT_MAX; + fbi[num].len = fbi[num].end - fbi[num].start + 1; + ILI_DBG("Block[%d]: start_addr = %x, end = %x", num, fbi[num].start, fbi[num].end); + + if (num == GESTURE) + ilits->gesture_load_code = true; + + block++; + } else if (type == BLOCK_TAG_B0 && tfd.hex_tag == BLOCK_TAG_AF) { + num = HexToDec(&phex[i + 9 + 6], 2); + + if (num > (FW_BLOCK_INFO_NUM - 1)) { + ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n", + num, FW_BLOCK_INFO_NUM - 1); + return -EINVAL; + } + + fbi[num].fix_mem_start = HexToDec(&phex[i + 9], 6); + ILI_DBG("Tag 0xB0: change Block[%d] to addr = 0x%x\n", num, fbi[num].fix_mem_start); + } + + addr = addr + (ex_addr << 16); + + if (phex[i + 1 + 2 + 4 + 2 + (len * 2) + 2] == 0x0D) + offset = 2; + else + offset = 1; + + if (addr > MAX_HEX_FILE_SIZE) { + ILI_ERR("Invalid hex format %d\n", addr); + return -1; + } + + if (type == 0x00) { + end_addr = addr + len; + if (addr < start_addr) + start_addr = addr; + /* fill data */ + for (j = 0, k = 0; j < (len * 2); j += 2, k++) + pfw[addr + k] = HexToDec(&phex[i + 9 + j], 2); + } + i += 1 + 2 + 4 + 2 + (len * 2) + 2 + offset; + } + + if (ilitek_fw_calc_file_crc(pfw) < 0) + return -1; + + tfd.start_addr = start_addr; + tfd.end_addr = end_addr; + tfd.block_number = block; + return 0; +} + +static int ilitek_tdd_fw_hex_open(u8 op, u8 *pfw) +{ + int ret =0, fsize = 0; + const struct firmware *fw = NULL; + struct file *f = NULL; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + mm_segment_t old_fs; +#endif + loff_t pos = 0; + + ILI_INFO("Open file method = %s, path = %s\n", + op ? "FILP_OPEN" : "REQUEST_FIRMWARE", + op ? ilits->md_fw_filp_path : ilits->md_fw_rq_path); + + switch (op) { + case REQUEST_FIRMWARE: + if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) { + ILI_ERR("Request firmware failed, try again\n"); + if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) { + ILI_ERR("Request firmware failed after retry\n"); + ret = -1; + goto out; + } + } + + fsize = fw->size; + ILI_INFO("fsize = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is invaild\n"); + release_firmware(fw); + ret = -1; + goto out; + } + + ilits->tp_fw.size = 0; + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n"); + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw after retry\n"); + release_firmware(fw); + ret = -ENOMEM; + goto out; + } + } + + /* Copy fw data got from request_firmware to global */ + ipio_memcpy((u8 *)ilits->tp_fw.data, fw->data, fsize * sizeof(*fw->data), fsize); + ilits->tp_fw.size = fsize; + release_firmware(fw); + break; + case FILP_OPEN: + f = filp_open(ilits->md_fw_filp_path, O_RDONLY, 0644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file, %ld\n", PTR_ERR(f)); + ret = -1; + goto out; + } + + fsize = f->f_inode->i_size; + ILI_INFO("fsize = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is invaild\n"); + filp_close(f, NULL); + ret = -1; + goto out; + } + + ilits->tp_fw.size = 0; + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n"); + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw after retry\n"); + filp_close(f, NULL); + ret = -ENOMEM; + goto out; + } + } + + /* ready to map user's memory to obtain data by reading files */ + pos = 0; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + old_fs = get_fs(); + set_fs(get_ds()); + set_fs(KERNEL_DS); + vfs_read(f, (u8 *)ilits->tp_fw.data, fsize, &pos); + set_fs(old_fs); +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) + kernel_read(f, (u8 *)ilits->tp_fw.data, fsize, &pos); +#endif + filp_close(f, NULL); + ilits->tp_fw.size = fsize; + break; + default: + ILI_ERR("Unknown open file method, %d\n", op); + break; + } + + if (ERR_ALLOC_MEM(ilits->tp_fw.data) || ilits->tp_fw.size <= 0) { + ILI_ERR("fw data/size is invaild\n"); + ret = -1; + goto out; + } + + /* Convert hex and copy data from tp_fw.data to pfw */ + if (ilitek_tddi_fw_hex_convert((u8 *)ilits->tp_fw.data, ilits->tp_fw.size, pfw) < 0) { + ILI_ERR("Convert hex file failed\n"); + ret = -1; + } + +out: + ipio_vfree((void **)&(ilits->tp_fw.data)); + return ret; +} + +int ili_fw_upgrade(int op) +{ + int i, ret = 0, retry = 3; + + if (!ilits->boot || ilits->force_fw_update || ERR_ALLOC_MEM(pfw)) { + ilits->gesture_load_code = false; + + if (ERR_ALLOC_MEM(pfw)) { + ipio_vfree((void **)&pfw); + pfw = vmalloc(MAX_HEX_FILE_SIZE * sizeof(u8)); + if (ERR_ALLOC_MEM(pfw)) { + ILI_ERR("Failed to allocate pfw memory, %ld\n", PTR_ERR(pfw)); + ipio_vfree((void **)&pfw); + ret = -ENOMEM; + goto out; + } + } + + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + pfw[i] = 0xFF; + + if (ilitek_tdd_fw_hex_open(op, pfw) < 0) { + ILI_ERR("Open hex file fail, try upgrade from ILI file\n"); + + /* + * Users might not be aware of a broken hex file when recovering + * fw from ILI file. We should force them to check + * hex files if they attempt to update via device node. + */ + if (ilits->node_update) { + ILI_ERR("Ignore update from ILI file\n"); + ipio_vfree((void **)&pfw); + return -EFW_CONVERT_FILE; + } + + if (ilitek_tddi_fw_ili_convert(pfw) < 0) { + ILI_ERR("Convert ILI file error\n"); + ret = -EFW_CONVERT_FILE; + goto out; + } + } + + if (ilitek_tddi_fw_update_block_info(pfw) < 0) { + ret = -EFW_CONVERT_FILE; + goto out; + } + + if (ilits->chip->core_ver >= CORE_VER_1470 && ilits->rib.nIsHostDownload == 0) { + ILI_ERR("hex file interface no match error\n"); + return -EFW_INTERFACE; + } + } + + do { + ret = ilitek_tddi_fw_iram_upgrade(pfw); + if (ret == UPDATE_PASS) + break; + + ILI_ERR("Upgrade failed, do retry!\n"); + } while (--retry > 0); + + if (ret != UPDATE_PASS) { + ILI_ERR("Failed to upgrade fw %d times, erasing iram\n", retry); + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP reset failed while erasing data\n"); + ilits->xch_num = 0; + ilits->ych_num = 0; + return ret; + } + +out: + ili_ic_get_core_ver(); + ili_ic_get_protocl_ver(); + ili_ic_get_fw_ver(); + ili_ic_get_tp_info(); + ili_ic_get_panel_info(); + ili_ic_func_ctrl_reset(); + return ret; +} + +void ili_fw_read_flash_info(void) +{ + return; +} + +void ili_flash_clear_dma(void) +{ + return; +} + +void ili_flash_dma_write(u32 start, u32 end, u32 len) +{ + return; +} + +int ili_fw_dump_flash_data(u32 start, u32 end, bool user) +{ + return 0; +} diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_i2c.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_i2c.c new file mode 100755 index 000000000000..6f3b01bd4714 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_i2c.c @@ -0,0 +1,470 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" + +struct touch_bus_info { + struct i2c_driver bus_driver; + struct ilitek_hwif_info *hwif; +}; + +struct ilitek_ts_data *ilits; + +#define RW_SYNC 0 +#define R_ONLY 1 +#define W_ONLY 2 + +#if I2C_DMA_TRANSFER +static unsigned char *ilitek_dma_va; +static dma_addr_t ilitek_dma_pa; + +#define DMA_VA_BUFFER 4096 + +static int dma_i2c_alloc(struct i2c_client *client) +{ + if (client != NULL) { + client->dev.coherent_dma_mask = DMA_BIT_MASK(32); + ilitek_dma_va = (u8 *)dmam_alloc_coherent(client->dev, DMA_VA_BUFFER, &ilitek_dma_pa, GFP_KERNEL); + if (ERR_ALLOC_MEM(ilitek_dma_va)) { + ILI_ERR("Allocate DMA I2C Buffer failed\n"); + return -ENOMEM; + } + + memset(ilitek_dma_va, 0, DMA_VA_BUFFER); + client->ext_flag |= I2C_DMA_FLAG; + return 0; + } + + ILI_ERR("client is NULL, allocated dma i2c failed\n"); + return -ENODEV; +} +#endif + +static int core_i2c_write(void *buf, int len) +{ + int ret = 0; + u8 *txbuf = (u8 *)buf; + u8 check_sum = 0; + u8 *mpbuf = NULL; + + struct i2c_msg msgs[] = { + { + .addr = ilits->i2c->addr, + .flags = 0, /* write flag. */ + .len = len, + .buf = txbuf, + }, + }; + +#if I2C_DMA_TRANSFER + if (len > 8) { + msgs[0].addr = (ilits->client->addr & I2C_MASK_FLAG); + msgs[0].ext_flag = (ilits->client->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG); + memcpy(ilitek_dma_va, txbuf, len); + msgs[0].buf = (u8 *)ilitek_dma_pa; + } +#endif + + + /* + * NOTE: If TP driver is doing MP test and commanding 0xF1 to FW, we add a checksum + * to the last index and plus 1 with size. + */ + if (atomic_read(&ilits->mp_stat) && txbuf[0] == P5_X_SET_CDC_INIT) { + check_sum = ili_calc_packet_checksum(txbuf, len); + mpbuf = kcalloc(len + 1, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(mpbuf)) { + ILI_ERR("Failed to allocate mpbuf mem\n"); + return -ENOMEM; + } + ipio_memcpy(mpbuf, txbuf, len, msgs[0].len); + mpbuf[len] = check_sum; + msgs[0].buf = mpbuf; + ili_dump_data(mpbuf, 8, len+1, 0, "mp cdc cmd"); + msgs[0].len = len + 1; + } + + if (i2c_transfer(ilits->i2c->adapter, msgs, 1) != 1) + ret = -1; + + ipio_kfree((void **)&mpbuf); + return ret; +} + +static int core_i2c_read(void *buf, int len) +{ + int ret; + u8 *rxbuf = (u8 *)buf; + + struct i2c_msg msgs[] = { + { + .addr = ilits->i2c->addr, + .flags = I2C_M_RD, /* read flag. */ + .len = len, + .buf = rxbuf, + }, + }; + + +#if I2C_DMA_TRANSFER + if (len > 8) { + msgs[0].addr = (ilits->client->addr & I2C_MASK_FLAG); + msgs[0].ext_flag = (ilits->client->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG); + msgs[0].buf = (u8 *)ilitek_dma_pa; + } else { + msgs[0].buf = rxbuf; + } +#endif + + + ret = i2c_transfer(ilits->i2c->adapter, msgs, 1); + +#if I2C_DMA_TRANSFER + if (len > 8) + memcpy(rxbuf, ilitek_dma_va, len); +#endif + + /* + * If i2c_transfer is ok (must return 1 because only sends one msg), + * return #bytes transferred, else error code. + */ + return (ret == 1) ? len : ret; +} + +static int ilitek_i2c_write(void *buf, int len) +{ + int ret = 0; + + if (len == 0) { + ILI_ERR("i2c write len is invalid\n"); + return -EINVAL; + } + + ret = core_i2c_write(buf, len); + if (ret < 0) { + if (atomic_read(&ilits->tp_reset) == START) { + ret = 0; + goto out; + } + ILI_ERR("i2c write error, ret = %d\n", ret); + } + +out: + return ret; +} + +static int ilitek_i2c_read(void *buf, int len) +{ + int ret = 0; + + if (len == 0) { + ILI_ERR("i2c read len is invalid\n"); + return -EINVAL; + } + + ret = core_i2c_read(buf, len); + if (ret < 0) { + if (atomic_read(&ilits->tp_reset) == START) { + ret = 0; + goto out; + } + ILI_ERR("i2c read error, ret = %d\n", ret); + } + +out: + return ret; +} + +static int ili_i2c_pll_clk_wakeup(void) +{ + int ret = 0; + u8 wakeup = 0xA3; +#if (PLL_CLK_WAKEUP_TP_RESUME == ENABLE) + if (ilits->pll_clk_wakeup == true) { +#else + if ((ilits->pll_clk_wakeup == true) && (ilits->tp_suspend == true)) { +#endif + ret = ilitek_i2c_write(&wakeup, sizeof(wakeup)); + if (ret < 0) + ILI_ERR("i2c write dummy cmd error\n"); + } + return ret; +} + +static int ili_i2c_wrapper(u8 *txbuf, u32 wlen, u8 *rxbuf, u32 rlen, bool spi_irq, bool i2c_irq) +{ + int ret = 0, operate = -1; + + if (wlen > 0) { + if (ERR_ALLOC_MEM(txbuf)) { + ILI_ERR("txbuf is null\n"); + return -ENOMEM; + } + } + + if (rlen > 0) { + if (ERR_ALLOC_MEM(rxbuf)) { + ILI_ERR("rxbuf is null\n"); + return -ENOMEM; + } + } + + if (wlen > 0 && rlen > 0) + operate = RW_SYNC; + else if (wlen > 0 && !rlen) + operate = W_ONLY; + else + operate = R_ONLY; + + if (ilits->int_pulse) + ilits->detect_int_stat = ili_ic_check_int_pulse; + else + ilits->detect_int_stat = ili_ic_check_int_level; + + if (i2c_irq) + atomic_set(&ilits->cmd_int_check, ENABLE); + + switch (operate) { + case RW_SYNC: + ret = ilitek_i2c_write(txbuf, wlen); + if (ret < 0) { + ILI_ERR("i2c-wrapper write error\n"); + break; + } + + if (i2c_irq) { + if (ilits->detect_int_stat(false) < 0) { + ILI_ERR("ERROR! Check INT timeout\n"); + ret = -ETIME; + break; + } + } else { + msleep(1); + } + + ret = ilitek_i2c_read(rxbuf, rlen); + if (ret < 0) { + ILI_ERR("i2c-wrapper read error\n"); + break; + } + break; + case W_ONLY: + ret = ili_i2c_pll_clk_wakeup(); + if (ret < 0) { + ILI_ERR("i2c-wrapper dummy cmd write error\n"); + } + ret = ilitek_i2c_write(txbuf, wlen); + if (ret < 0) { + ILI_ERR("i2c-wrapper write error\n"); + break; + } + break; + case R_ONLY: + if (i2c_irq) { + if (ilits->detect_int_stat(false) < 0) { + ILI_ERR("ERROR! Check INT timeout\n"); + ret = -ETIME; + break; + } + } + + ret = ilitek_i2c_read(rxbuf, rlen); + if (ret < 0) { + ILI_ERR("i2c-wrapper read error\n"); + break; + } + break; + default: + ILI_ERR("Unknown ts-i2c operation\n"); + ret = -EINVAL; + break; + } + + if (i2c_irq) + atomic_set(&ilits->cmd_int_check, DISABLE); + + return ret; +} + +int ili_core_spi_setup(int num) +{ + ILI_ERR("Not support this interface\n"); + return 0; +} + +static int ilitek_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct touch_bus_info *info = + container_of(to_i2c_driver(i2c->dev.driver), + struct touch_bus_info, bus_driver); + + ILI_INFO("ilitek i2c probe\n"); + + if (!i2c) { + ILI_ERR("i2c client is NULL\n"); + return -ENODEV; + } + + if (i2c->addr != TDDI_I2C_ADDR) { + i2c->addr = TDDI_I2C_ADDR; + ILI_INFO("i2c addr doesn't be set up, use default : 0x%x\n", i2c->addr); + } + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + ILI_ERR("i2c functions are not supported!\n"); + return -ENODEV; + } + + ilits = devm_kzalloc(&i2c->dev, sizeof(struct ilitek_ts_data), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits)) { + ILI_ERR("Failed to allocate ts memory, %ld\n", PTR_ERR(ilits)); + return -ENOMEM; + } + + /* Used for receiving touch data only, do not mix up with others. */ + ilits->tr_buf = kzalloc(TR_BUF_SIZE, GFP_ATOMIC); + if (ERR_ALLOC_MEM(ilits->tr_buf)) { + ILI_ERR("failed to allocate touch report buffer\n"); + return -ENOMEM; + } + + ilits->gcoord = kzalloc(sizeof(struct gesture_coordinate), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->gcoord)) { + ILI_ERR("Failed to allocate gresture coordinate buffer\n"); + return -ENOMEM; + } + + ilits->i2c = i2c; + ilits->spi = NULL; + ilits->dev = &i2c->dev; + ilits->hwif = info->hwif; + ilits->phys = "I2C"; + ilits->wrapper = ili_i2c_wrapper; + ilits->detect_int_stat = ili_ic_check_int_pulse; + ilits->int_pulse = true; + ilits->mp_retry = false; + +#if I2C_DMA_TRANSFER + if (dma_i2c_alloc(ilits->i2c) < 0) + ILI_ERR("Failed to alllocate DMA mem %ld\n", PTR_ERR(ilits->i2c)); +#endif + + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + + if (TDDI_RST_BIND) + ilits->reset = TP_IC_WHOLE_RST; + else + ilits->reset = TP_HW_RST_ONLY; + + ilits->rst_edge_delay = 100; + ilits->fw_open = FILP_OPEN; + ilits->fw_upgrade_mode = UPGRADE_FLASH; + ilits->mp_move_code = ili_move_mp_code_flash; + ilits->gesture_move_code = ili_move_gesture_code_flash; + ilits->esd_recover = ili_wq_esd_i2c_check; + ilits->ges_recover = ili_touch_esd_gesture_flash; + ilits->gesture_mode = DATA_FORMAT_GESTURE_INFO; + ilits->gesture_demo_ctrl = DISABLE; + ilits->wtd_ctrl = OFF; + ilits->report = ENABLE; + ilits->netlink = DISABLE; + ilits->dnp = DISABLE; + ilits->irq_tirgger_type = IRQF_TRIGGER_FALLING; + ilits->info_from_hex = DISABLE; + ilits->wait_int_timeout = AP_INT_TIMEOUT; + +#if ENABLE_GESTURE + ilits->gesture = DISABLE; + ilits->ges_sym.double_tap = DOUBLE_TAP; + ilits->ges_sym.alphabet_line_2_top = ALPHABET_LINE_2_TOP; + ilits->ges_sym.alphabet_line_2_bottom = ALPHABET_LINE_2_BOTTOM; + ilits->ges_sym.alphabet_line_2_left = ALPHABET_LINE_2_LEFT; + ilits->ges_sym.alphabet_line_2_right = ALPHABET_LINE_2_RIGHT; + ilits->ges_sym.alphabet_m = ALPHABET_M; + ilits->ges_sym.alphabet_w = ALPHABET_W; + ilits->ges_sym.alphabet_c = ALPHABET_C; + ilits->ges_sym.alphabet_E = ALPHABET_E; + ilits->ges_sym.alphabet_V = ALPHABET_V; + ilits->ges_sym.alphabet_O = ALPHABET_O; + ilits->ges_sym.alphabet_S = ALPHABET_S; + ilits->ges_sym.alphabet_Z = ALPHABET_Z; + ilits->ges_sym.alphabet_V_down = ALPHABET_V_DOWN; + ilits->ges_sym.alphabet_V_left = ALPHABET_V_LEFT; + ilits->ges_sym.alphabet_V_right = ALPHABET_V_RIGHT; + ilits->ges_sym.alphabet_two_line_2_bottom = ALPHABET_TWO_LINE_2_BOTTOM; + ilits->ges_sym.alphabet_F = ALPHABET_F; + ilits->ges_sym.alphabet_AT = ALPHABET_AT; +#endif + + return info->hwif->plat_probe(); +} + +static int ilitek_i2c_remove(struct i2c_client *i2c) +{ + ILI_INFO(); + return 0; +} + +static const struct i2c_device_id tp_i2c_id[] = { + {TDDI_DEV_ID, 0}, + {}, +}; + +int ili_interface_dev_init(struct ilitek_hwif_info *hwif) +{ + struct touch_bus_info *info; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ILI_ERR("faied to allocate i2c_driver\n"); + return -ENOMEM; + } + + if (hwif->bus_type != BUS_I2C) { + ILI_ERR("Not I2C dev\n"); + ipio_kfree((void **)&info); + return -EINVAL; + } + + hwif->info = info; + + info->bus_driver.driver.name = hwif->name; + info->bus_driver.driver.owner = hwif->owner; + info->bus_driver.driver.of_match_table = hwif->of_match_table; + info->bus_driver.driver.pm = hwif->pm; + + info->bus_driver.probe = ilitek_i2c_probe; + info->bus_driver.remove = ilitek_i2c_remove; + info->bus_driver.id_table = tp_i2c_id; + + info->hwif = hwif; + return i2c_add_driver(&info->bus_driver); +} + +void ili_interface_dev_exit(struct ilitek_ts_data *ts) +{ + struct touch_bus_info *info = (struct touch_bus_info *)ilits->hwif->info; + + ILI_INFO("remove i2c dev\n"); + i2c_del_driver(&info->bus_driver); + ipio_kfree((void **)&info); +} diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_ic.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_ic.c new file mode 100755 index 000000000000..ce4d439a96fa --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_ic.c @@ -0,0 +1,1019 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" + +#define PROTOCL_VER_NUM 8 +static struct ilitek_protocol_info protocol_info[PROTOCL_VER_NUM] = { + /* length -> fw, protocol, tp, key, panel, core, func, window, cdc, mp_info */ + [0] = {PROTOCOL_VER_500, 4, 4, 14, 30, 5, 5, 2, 8, 3, 8}, + [1] = {PROTOCOL_VER_510, 4, 3, 14, 30, 5, 5, 3, 8, 3, 8}, + [2] = {PROTOCOL_VER_520, 4, 4, 14, 30, 5, 5, 3, 8, 3, 8}, + [3] = {PROTOCOL_VER_530, 9, 4, 14, 30, 5, 5, 3, 8, 3, 8}, + [4] = {PROTOCOL_VER_540, 9, 4, 14, 30, 5, 5, 3, 8, 15, 8}, + [5] = {PROTOCOL_VER_550, 9, 4, 14, 30, 5, 5, 3, 8, 15, 14}, + [6] = {PROTOCOL_VER_560, 9, 4, 14, 30, 5, 5, 3, 8, 15, 14}, + [7] = {PROTOCOL_VER_570, 9, 4, 14, 30, 5, 5, 3, 8, 15, 14}, +}; + +#define FUNC_CTRL_NUM 18 +static struct ilitek_ic_func_ctrl func_ctrl[FUNC_CTRL_NUM] = { + /* cmd[3] = cmd, func, ctrl */ + // rec_state 0:disable, 1: enable, 2: ignore record + [0] = {"sense", {0x1, 0x1, 0x0}, 3, 0x0, 2, 0xFF}, + [1] = {"sleep", {0x1, 0x2, 0x0}, 3, 0x0, 2, 0xFF}, + [2] = {"glove", {0x1, 0x6, 0x0}, 3, 0x0, 0, 0xFF}, + [3] = {"stylus", {0x1, 0x7, 0x0}, 3, 0x0, 0, 0xFF}, + [4] = {"lpwg", {0x1, 0xA, 0x0}, 3, 0x0, 2, 0xFF}, + [5] = {"proximity", {0x1, 0x10, 0x0}, 3, 0x0, 2, 0xFF}, + [6] = {"plug", {0x1, 0x11, 0x0}, 3, 0x1, 0, 0xFF}, + [7] = {"edge_palm", {0x1, 0x12, 0x0}, 3, 0x1, 0, 0xFF}, + [8] = {"lock_point", {0x1, 0x13, 0x0}, 3, 0x0, 0, 0xFF}, + [9] = {"active", {0x1, 0x14, 0x0}, 3, 0x0, 2, 0xFF}, + [10] = {"idle", {0x1, 0x19, 0x0}, 3, 0x1, 0, 0xFF}, + [11] = {"gesture_demo_en", {0x1, 0x16, 0x0}, 3, 0x0, 0, 0xFF}, + [12] = {"tp_recore", {0x1, 0x18, 0x0}, 3, 0x0, 2, 0xFF}, + [13] = {"knock_en", {0x1, 0xA, 0x8, 0x03, 0x0, 0x0}, 6, 0xFF, 0, 0xFF}, + [14] = {"int_trigger", {0x1, 0x1B, 0x0}, 3, 0x0, 2, 0xFF}, + [15] = {"ear_phone", {0x1, 0x17, 0x0}, 3, 0x0, 0, 0xFF}, + [16] = {"passive_pen", {0x1, 0x27, 0x0}, 3, 0x0, 0, 0xFF}, + [17] = {"canvas", {0x1, 0x28, 0x0}, 3, 0x0, 0, 0xFF}, +}; + +#define CHIP_SUP_NUM 5 +static u32 ic_sup_list[CHIP_SUP_NUM] = { + [0] = ILI9881_CHIP, + [1] = ILI7807_CHIP, + [2] = ILI9881N_AA, + [3] = ILI9881O_AA, + [4] = ILI9882_CHIP +}; + +static int ilitek_tddi_ic_check_support(u32 pid, u16 id) +{ + int i = 0; + + for (i = 0; i < CHIP_SUP_NUM; i++) { + if ((pid == ic_sup_list[i]) || (id == ic_sup_list[i])) + break; + } + + if (i >= CHIP_SUP_NUM) { + ILI_INFO("ERROR, ILITEK CHIP(0x%x) Not found !!\n", pid); + } + + ILI_INFO("ILITEK CHIP ILI9881x found.\n"); + + ilits->chip->pid = pid; + + ilits->chip->reset_key = 0x00019878; + ilits->chip->wtd_key = 0x9881; + + if (((pid & 0xFFFFFF00) == ILI9881N_AA) || ((pid & 0xFFFFFF00) == ILI9881O_AA)){ + ilits->chip->dma_reset = ENABLE; + } else { + ilits->chip->dma_reset = DISABLE; + } + + ilits->chip->no_bk_shift = RAWDATA_NO_BK_SHIFT; + ilits->chip->max_count = 0x1FFFF; + return 0; +} + +int ili_ice_mode_bit_mask_write(u32 addr, u32 mask, u32 value) +{ + int ret = 0; + u32 data = 0; + + if (ili_ice_mode_read(addr, &data, sizeof(u32)) < 0) { + ILI_ERR("Read data error\n"); + return -1; + } + + data &= (~mask); + data |= (value & mask); + + ILI_DBG("mask value data = %x\n", data); + + ret = ili_ice_mode_write(addr, data, sizeof(u32)); + if (ret < 0) + ILI_ERR("Failed to re-write data in ICE mode, ret = %d\n", ret); + + return ret; +} + +int ili_ice_mode_write(u32 addr, u32 data, int len) +{ + int ret = 0, i; + u8 txbuf[64] = {0}; + + if (!atomic_read(&ilits->ice_stat)) { + ILI_ERR("ice mode not enabled\n"); + return -1; + } + + txbuf[0] = 0x25; + txbuf[1] = (char)((addr & 0x000000FF) >> 0); + txbuf[2] = (char)((addr & 0x0000FF00) >> 8); + txbuf[3] = (char)((addr & 0x00FF0000) >> 16); + + for (i = 0; i < len; i++) + txbuf[i + 4] = (char)(data >> (8 * i)); + + ret = ilits->wrapper(txbuf, len + 4, NULL, 0, OFF, OFF); + if (ret < 0) + ILI_ERR("Failed to write data in ice mode, ret = %d\n", ret); + + return ret; +} + +int ili_ice_mode_read(u32 addr, u32 *data, int len) +{ + int ret = 0; + u8 *rxbuf = NULL; + u8 txbuf[4] = {0}; + + if (!atomic_read(&ilits->ice_stat)) { + ILI_ERR("ice mode not enabled\n"); + return -1; + } + + txbuf[0] = 0x25; + txbuf[1] = (char)((addr & 0x000000FF) >> 0); + txbuf[2] = (char)((addr & 0x0000FF00) >> 8); + txbuf[3] = (char)((addr & 0x00FF0000) >> 16); + + ret = ilits->wrapper(txbuf, sizeof(txbuf), NULL, 0, OFF, OFF); + if (ret < 0) + goto out; + + rxbuf = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(rxbuf)) { + ILI_ERR("Failed to allocate rxbuf, %ld\n", PTR_ERR(rxbuf)); + ret = -ENOMEM; + goto out; + } + + ret = ilits->wrapper(NULL, 0, rxbuf, len, OFF, OFF); + if (ret < 0) + goto out; + + if (len == sizeof(u8)) + *data = rxbuf[0]; + else + *data = (rxbuf[0] | rxbuf[1] << 8 | rxbuf[2] << 16 | rxbuf[3] << 24); + +out: + if (ret < 0) + ILI_ERR("Failed to read data in ice mode, ret = %d\n", ret); + + ipio_kfree((void **)&rxbuf); + return ret; +} + +int ili_ice_mode_ctrl(bool enable, bool mcu) +{ + int ret = 0; + u8 cmd_open[4] = {0x25, 0x62, 0x10, 0x18}; + u8 cmd_close[4] = {0x1B, 0x62, 0x10, 0x18}; + + ILI_INFO("%s ICE mode, mcu on = %d\n", (enable ? "Enable" : "Disable"), mcu); + + if (enable) { + if (atomic_read(&ilits->ice_stat)) { + ILI_INFO("ice mode already enabled\n"); + return 0; + } + + if (mcu) + cmd_open[0] = 0x1F; + + atomic_set(&ilits->ice_stat, ENABLE); + + if (ilits->wrapper(cmd_open, sizeof(cmd_open), NULL, 0, OFF, OFF) < 0){ + ILI_ERR("write ice mode cmd error\n"); + atomic_set(&ilits->ice_stat, DISABLE); + } + ilits->pll_clk_wakeup = false; + +#if (TDDI_INTERFACE == BUS_I2C) + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); /* CS high */ +#endif + } else { + if (!atomic_read(&ilits->ice_stat)) { + ILI_INFO("ice mode already disabled\n"); + return 0; + } + + ret = ilits->wrapper(cmd_close, sizeof(cmd_close), NULL, 0, OFF, OFF); + if (ret < 0){ + ILI_ERR("Exit to ICE Mode failed !!\n"); + atomic_set(&ilits->ice_stat, ENABLE); + } else { + atomic_set(&ilits->ice_stat, DISABLE); + ilits->pll_clk_wakeup = true; + } + } + + return ret; +} + +int ili_ic_func_ctrl(const char *name, int ctrl) +{ + int i = 0, ret; + + for (i = 0; i < FUNC_CTRL_NUM; i++) { + if (ipio_strcmp(name, func_ctrl[i].name) == 0) { + if (strlen(name) != strlen(func_ctrl[i].name)) + continue; + break; + } + } + + if (i >= FUNC_CTRL_NUM) { + ILI_ERR("Not found function ctrl, %s\n", name); + ret = -1; + goto out; + } + + if (ilits->protocol->ver == PROTOCOL_VER_500) { + ILI_ERR("Non support function ctrl with protocol v5.0\n"); + ret = -1; + goto out; + } + + if (ilits->protocol->ver >= PROTOCOL_VER_560) { + if (ipio_strcmp(func_ctrl[i].name, "gesture") == 0 || + ipio_strcmp(func_ctrl[i].name, "phone_cover_window") == 0) { + ILI_INFO("Non support %s function ctrl\n", func_ctrl[i].name); + ret = -1; + goto out; + } + } + + func_ctrl[i].cmd[2] = ctrl; + + ILI_INFO("func = %s, len = %d, cmd = 0x%x, 0%x, 0x%x\n", func_ctrl[i].name, func_ctrl[i].len, + func_ctrl[i].cmd[0], func_ctrl[i].cmd[1], func_ctrl[i].cmd[2]); + + ret = ilits->wrapper(func_ctrl[i].cmd, func_ctrl[i].len, NULL, 0, OFF, OFF); + if (ret < 0) + ILI_ERR("Write TP function failed\n"); + + if (func_ctrl[i].rec_state < 2) { + if (ctrl == func_ctrl[i].def_cmd) + func_ctrl[i].rec_state = DISABLE; + else + func_ctrl[i].rec_state = ENABLE; + + func_ctrl[i].rec_cmd = ctrl; + } + + ILI_DBG("record %s func cmd %d, rec_state %d\n", func_ctrl[i].name, func_ctrl[i].rec_cmd, func_ctrl[i].rec_state); +out: + return ret; +} + +void ili_ic_func_ctrl_reset(void) { + int i = 0; + for (i = 0; i < FUNC_CTRL_NUM; i++) { + if (func_ctrl[i].rec_state == ENABLE) { + ILI_DBG("reset func ctrl %s, record status = %d, cmd = %d\n", func_ctrl[i].name, + func_ctrl[i].rec_state, func_ctrl[i].rec_cmd); + if (ili_ic_func_ctrl(func_ctrl[i].name, func_ctrl[i].rec_cmd) < 0) + ILI_ERR("reset ic func ctrl %s failed\n", func_ctrl[i].name); + } + } +} + +int ili_ic_code_reset(void) +{ + int ret; + bool ice = atomic_read(&ilits->ice_stat); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed before code reset\n"); + + ret = ili_ice_mode_write(0x40040, 0xAE, 1); + if (ret < 0) + ILI_ERR("ic code reset failed\n"); + + if (!ice) + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed after code reset\n"); + return ret; +} + +int ili_ic_whole_reset(void) +{ + int ret = 0; + bool ice = atomic_read(&ilits->ice_stat); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed before chip reset\n"); + + ILI_INFO("ic whole reset key = 0x%x, edge_delay = %d\n", + ilits->chip->reset_key, ilits->rst_edge_delay); + + ret = ili_ice_mode_write(ilits->chip->reset_addr, ilits->chip->reset_key, sizeof(u32)); + if (ret < 0) { + ILI_ERR("ic whole reset failed\n"); + goto out; + } + + /* Need accurate power sequence, do not change it to msleep */ + mdelay(ilits->rst_edge_delay); + +out: + if (!ice) + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed after chip reset\n"); + + return ret; +} + +static void ilitek_tddi_ic_wr_pack(int packet) +{ + int retry = 100; + u32 reg_data = 0; + + while (retry--) { + if (ili_ice_mode_read(0x73010, ®_data, sizeof(u8)) < 0) + ILI_ERR("Read 0x73010 error\n"); + + if ((reg_data & 0x02) == 0) { + ILI_INFO("check ok 0x73010 read 0x%X retry = %d\n", reg_data, retry); + break; + } + mdelay(10); + } + + if (retry <= 0) + ILI_INFO("check 0x73010 error read 0x%X\n", reg_data); + + if (ili_ice_mode_write(0x73000, packet, 4) < 0) + ILI_ERR("Write %x at 0x73000\n", packet); +} + +static u32 ilitek_tddi_ic_rd_pack(int packet) +{ + int retry = 100; + u32 reg_data = 0; + + ilitek_tddi_ic_wr_pack(packet); + + while (retry--) { + if (ili_ice_mode_read(0x4800A, ®_data, sizeof(u8)) < 0) + ILI_ERR("Read 0x4800A error\n"); + + if ((reg_data & 0x02) == 0x02) { + ILI_INFO("check ok 0x4800A read 0x%X retry = %d\n", reg_data, retry); + break; + } + mdelay(10); + } + if (retry <= 0) + ILI_INFO("check 0x4800A error read 0x%X\n", reg_data); + + if (ili_ice_mode_write(0x4800A, 0x02, 1) < 0) + ILI_ERR("Write 0x2 at 0x4800A\n"); + + if (ili_ice_mode_read(0x73016, ®_data, sizeof(u8)) < 0) + ILI_ERR("Read 0x73016 error\n"); + + return reg_data; +} + +void ili_ic_set_ddi_reg_onepage(u8 page, u8 reg, u8 data) +{ + u32 setpage = 0x1FFFFF00 | page; + u32 setreg = 0x1F000100 | (reg << 16) | data; + bool ice = atomic_read(&ilits->ice_stat); + + ILI_INFO("setpage = 0x%X setreg = 0x%X\n", setpage, setreg); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed before writing ddi reg\n"); + + /*TDI_WR_KEY*/ + ilitek_tddi_ic_wr_pack(0x1FFF9527); + /*Switch to Page*/ + ilitek_tddi_ic_wr_pack(setpage); + /* Page*/ + ilitek_tddi_ic_wr_pack(setreg); + /*TDI_WR_KEY OFF*/ + ilitek_tddi_ic_wr_pack(0x1FFF9500); + + if (!ice) + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Disable ice mode failed after writing ddi reg\n"); +} + +void ili_ic_get_ddi_reg_onepage(u8 page, u8 reg, u8 *data) +{ + u32 setpage = 0x1FFFFF00 | page; + u32 setreg = 0x2F000100 | (reg << 16); + bool ice = atomic_read(&ilits->ice_stat); + + ILI_INFO("setpage = 0x%X setreg = 0x%X\n", setpage, setreg); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed before reading ddi reg\n"); + + /*TDI_WR_KEY*/ + ilitek_tddi_ic_wr_pack(0x1FFF9527); + /*Set Read Page reg*/ + ilitek_tddi_ic_wr_pack(setpage); + + /*TDI_RD_KEY*/ + ilitek_tddi_ic_wr_pack(0x1FFF9487); + /*( *( __IO uint8 *) (0x4800A) ) =0x2*/ + if (ili_ice_mode_write(0x4800A, 0x02, 1) < 0) + ILI_ERR("Write 0x2 at 0x4800A\n"); + + *data = ilitek_tddi_ic_rd_pack(setreg); + ILI_INFO("check page = 0x%X, reg = 0x%X, read 0x%X\n", page, reg, *data); + + /*TDI_RD_KEY OFF*/ + ilitek_tddi_ic_wr_pack(0x1FFF9400); + /*TDI_WR_KEY OFF*/ + ilitek_tddi_ic_wr_pack(0x1FFF9500); + + if (!ice) + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Disable ice mode failed after reading ddi reg\n"); +} + + +void ili_ic_get_pc_counter(int stat) +{ + bool ice = atomic_read(&ilits->ice_stat); + u32 pc = 0, pc_addr = ilits->chip->pc_counter_addr; + u32 latch = 0, latch_addr = ilits->chip->pc_latch_addr; + int ret = 0; + + ILI_DBG("stat = %d\n", stat); + + if (!ice) { + if (stat == DO_SPI_RECOVER || stat == DO_I2C_RECOVER) + ret = ili_ice_mode_ctrl(ENABLE, OFF); + else + ret = ili_ice_mode_ctrl(ENABLE, ON); + + if (ret < 0) + ILI_ERR("Enable ice mode failed while reading pc counter\n"); + } + + if (ili_ice_mode_read(ilits->chip->pc_counter_addr, &pc, sizeof(u32)) < 0) + ILI_ERR("Read pc conter error\n"); + + if (ili_ice_mode_read(ilits->chip->pc_latch_addr, &latch, sizeof(u32)) < 0) + ILI_ERR("Read pc latch error\n"); + + ilits->fw_pc = pc; + ilits->fw_latch = latch; + ILI_ERR("Read counter (addr: 0x%x) = 0x%x, latch (addr: 0x%x) = 0x%x\n", + pc_addr, ilits->fw_pc, latch_addr, ilits->fw_latch); + + /* Avoid screen abnormal. */ + if (stat == DO_SPI_RECOVER) { + atomic_set(&ilits->ice_stat, DISABLE); + return; + } + + if (!ice) { + if (ili_ice_mode_ctrl(DISABLE, ON) < 0) + ILI_ERR("Disable ice mode failed while reading pc counter\n"); + } +} + +int ili_ic_int_trigger_ctrl(bool pulse) +{ + /* It's supported by fw, and the level will be kept at high until data was already prepared. */ + if (ili_ic_func_ctrl("int_trigger", pulse) < 0) { + ILI_ERR("Write CMD error, set back to <%s> trigger\n", ilits->int_pulse ? "Level" : "Pulse"); + return -1; + } + + ilits->int_pulse = pulse; + ILI_INFO("INT Trigger = %s\n", ilits->int_pulse ? "Level" : "Pulse"); + return 0; +} + +int ili_ic_check_int_level(bool level) +{ + int timer = 3000; + int gpio = IRQ_GPIO_NUM; //ilits->tp_int + + /* + * If callers have a trouble to use the gpio that is passed by vendors, + * please utlises a physical gpio number instead or call a help from them. + */ + + while (--timer > 0) { + if(level) { + if (gpio_get_value(gpio)) { + ILI_DBG("INT high detected.\n"); + return 0; + } + } else { + if (!gpio_get_value(gpio)) { + ILI_DBG("INT low detected.\n");; + return 0; + } + } + mdelay(1); + } + ILI_ERR("Error! INT level no detected.\n"); + return -1; +} + +int ili_ic_check_int_pulse(bool pulse) +{ + if (!wait_event_interruptible_timeout(ilits->inq, !atomic_read(&ilits->cmd_int_check), msecs_to_jiffies(ilits->wait_int_timeout))) { + ILI_ERR("Error! INT pulse no detected. Timeout = %d ms\n", ilits->wait_int_timeout); + atomic_set(&ilits->cmd_int_check, DISABLE); + return -1; + } + ILI_DBG("INT pulse detected.\n"); + return 0; +} + +int ili_ic_check_busy(int count, int delay) +{ + u8 cmd[2] = {0}; + u8 busy = 0, rby = 0; + + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_CDC_BUSY_STATE; + + if (ilits->actual_tp_mode == P5_X_FW_AP_MODE) + rby = 0x41; + else if (ilits->actual_tp_mode == P5_X_FW_TEST_MODE) + rby = 0x51; + else { + ILI_ERR("Unknown TP mode (0x%x)\n", ilits->actual_tp_mode); + return -EINVAL; + } + + ILI_INFO("read byte = %x, delay = %d\n", rby, delay); + + do { + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) + ILI_ERR("Write check busy cmd failed\n"); + + if (ilits->wrapper(&cmd[1], sizeof(u8), &busy, sizeof(u8), ON, OFF) < 0) + ILI_ERR("Read check busy failed\n"); + + ILI_DBG("busy = 0x%x\n", busy); + + if (busy == rby) { + ILI_INFO("Check busy free\n"); + return 0; + } + + mdelay(delay); + } while (--count > 0); + + ILI_ERR("Check busy (0x%x) timeout !\n", busy); + ili_ic_get_pc_counter(0); + return -1; +} + +int ili_ic_get_project_id(u8 *pdata, int size) +{ + int i; + u32 tmp; + bool ice = atomic_read(&ilits->ice_stat); + + if (!pdata) { + ILI_ERR("pdata is null\n"); + return -ENOMEM; + } + + ILI_INFO("Read size = %d\n", size); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed while reading project id\n"); + + if (ili_ice_mode_write(0x041000, 0x0, 1) < 0) + ILI_ERR("Pull cs low failed\n"); + if (ili_ice_mode_write(0x041004, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(0x041008, 0x03, 1) < 0) + ILI_ERR("Write 0x03 at 0x041008\n"); + + if (ili_ice_mode_write(0x041008, (RSV_BK_ST_ADDR & 0xFF0000) >> 16, 1) < 0) + ILI_ERR("Write address failed\n"); + if (ili_ice_mode_write(0x041008, (RSV_BK_ST_ADDR & 0x00FF00) >> 8, 1) < 0) + ILI_ERR("Write address failed\n"); + if (ili_ice_mode_write(0x041008, (RSV_BK_ST_ADDR & 0x0000FF), 1) < 0) + ILI_ERR("Write address failed\n"); + + for (i = 0; i < size; i++) { + if (ili_ice_mode_write(0x041008, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + if (ili_ice_mode_read(0x41010, &tmp, sizeof(u8)) < 0) + ILI_ERR("Read project id error\n"); + pdata[i] = tmp; + ILI_INFO("project_id[%d] = 0x%x\n", i, pdata[i]); + } + + ili_flash_clear_dma(); + + if (ili_ice_mode_write(0x041000, 0x1, 1) < 0) + ILI_ERR("Pull cs high\n"); + + if (!ice) + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Disable ice mode failed while reading project id\n"); + + return 0; +} + +int ili_ic_get_core_ver(void) +{ + int i = 0, ret = 0; + u8 cmd[2] = {0}, buf[10] = {0}; + + ilits->protocol->core_ver_len = P5_X_CORE_VER_FOUR_LENGTH; + + if (ilits->info_from_hex) { + buf[1] = ilits->fw_info[68]; + buf[2] = ilits->fw_info[69]; + buf[3] = ilits->fw_info[70]; + buf[4] = ilits->fw_info[71]; + goto out; + } + + do { + if (i == 0) { + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_CORE_VERSION_NEW; + } else { + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_CORE_VERSION; + ilits->protocol->core_ver_len = P5_X_CORE_VER_THREE_LENGTH; + } + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) + ILI_ERR("Write core ver cmd failed\n"); + + if (ilits->wrapper(&cmd[1], sizeof(u8), buf, ilits->protocol->core_ver_len, ON, OFF) < 0) + ILI_ERR("Write core ver (0x%x) failed\n", cmd[1]); + + ILI_DBG("header = 0x%x\n", buf[0]); + + if (buf[0] == P5_X_GET_CORE_VERSION || + buf[0] == P5_X_GET_CORE_VERSION_NEW) + break; + + } while (++i < 2); + + if (buf[0] == P5_X_GET_CORE_VERSION) + buf[4] = 0; + + if (i >= 2) { + ILI_ERR("Invalid header (0x%x)\n", buf[0]); + ret = -EINVAL; + } + +out: + ILI_INFO("Core version = %d.%d.%d.%d\n", buf[1], buf[2], buf[3], buf[4]); + ilits->chip->core_ver = buf[1] << 24 | buf[2] << 16 | buf[3] << 8 | buf[4]; + return ret; +} + +void ili_fw_uart_ctrl(u8 ctrl) +{ + u8 cmd[4] = {0}; + + if (ctrl > 1) { + ILI_INFO("Unknown cmd, ignore\n"); + return; + } + + ILI_INFO("%s UART mode\n", ctrl ? "Enable" : "Disable"); + + cmd[0] = P5_X_I2C_UART; + cmd[1] = 0x3; + cmd[2] = 0; + cmd[3] = ctrl; + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) { + ILI_INFO("Write fw uart cmd failed\n"); + return; + } +} + +int ili_ic_get_fw_ver(void) +{ + int ret = 0; + u8 cmd[2] = {0}; + u8 buf[10] = {0}; + + if (ilits->info_from_hex) { + buf[1] = ilits->fw_info[48]; + buf[2] = ilits->fw_info[49]; + buf[3] = ilits->fw_info[50]; + buf[4] = ilits->fw_info[51]; + buf[5] = ilits->fw_mp_ver[0]; + buf[6] = ilits->fw_mp_ver[1]; + buf[7] = ilits->fw_mp_ver[2]; + buf[8] = ilits->fw_mp_ver[3]; + goto out; + } + + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_FW_VERSION; + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Write pre cmd failed\n"); + ret = -EINVAL; + goto out; + + } + + if (ilits->wrapper(&cmd[1], sizeof(u8), buf, ilits->protocol->fw_ver_len, ON, OFF)) { + ILI_ERR("Write fw version cmd failed\n"); + ret = -EINVAL; + goto out; + } + + if (buf[0] != P5_X_GET_FW_VERSION) { + ILI_ERR("Invalid firmware ver\n"); + ret = -1; + } + +out: + ILI_INFO("Firmware version = %d.%d.%d.%d\n", buf[1], buf[2], buf[3], buf[4]); + ILI_INFO("Firmware MP version = %d.%d.%d.%d\n", buf[5], buf[6], buf[7], buf[8]); + ilits->chip->fw_ver = buf[1] << 24 | buf[2] << 16 | buf[3] << 8 | buf[4]; + ilits->chip->fw_mp_ver = buf[5] << 24 | buf[6] << 16 | buf[7] << 8 | buf[8]; + return ret; +} + +int ili_ic_get_panel_info(void) +{ + int ret = 0; + u8 cmd = P5_X_GET_PANEL_INFORMATION; + u8 buf[10] = {0}; + u8 len = ilits->protocol->panel_info_len; + + if (ilits->info_from_hex && (ilits->chip->core_ver >= CORE_VER_1410)) { + buf[1] = ilits->fw_info[16]; + buf[2] = ilits->fw_info[17]; + buf[3] = ilits->fw_info[18]; + buf[4] = ilits->fw_info[19]; + ilits->panel_wid = buf[2] << 8 | buf[1]; + ilits->panel_hei = buf[4] << 8 | buf[3]; + ilits->trans_xy = (ilits->chip->core_ver >= CORE_VER_1430 + && (ilits->rib.nReportByPixel > 0)) ? ON : OFF; + goto out; + } + + len = (ilits->chip->core_ver >= CORE_VER_1430) ? 6 : len; + + ret = ilits->wrapper(&cmd, sizeof(cmd), buf, len, ON, OFF); + if (ret < 0) + ILI_ERR("Read panel info error\n"); + + if (buf[0] != cmd) { + ILI_INFO("Invalid panel info, use default resolution\n"); + ilits->panel_wid = TOUCH_SCREEN_X_MAX; + ilits->panel_hei = TOUCH_SCREEN_Y_MAX; + ilits->trans_xy = OFF; + } else { + ilits->panel_wid = buf[1] << 8 | buf[2]; + ilits->panel_hei = buf[3] << 8 | buf[4]; + ilits->trans_xy = (ilits->chip->core_ver >= CORE_VER_1430) ? buf[5] : OFF; + } + +out: + ILI_INFO("Panel info: width = %d, height = %d\n", ilits->panel_wid, ilits->panel_hei); + ILI_INFO("Transfer touch coordinate = %s\n", ilits->trans_xy ? "ON" : "OFF"); + return ret; +} + +int ili_ic_get_tp_info(void) +{ + int ret = 0; + u8 cmd[2] = {0}; + u8 buf[20] = {0}; + + if (ilits->info_from_hex && (ilits->chip->core_ver >= CORE_VER_1410)) { + buf[1] = ilits->fw_info[5]; + buf[2] = ilits->fw_info[7]; + buf[3] = ilits->fw_info[8]; + buf[4] = ilits->fw_info[9]; + buf[5] = ilits->fw_info[10]; + buf[6] = ilits->fw_info[11]; + buf[7] = ilits->fw_info[12]; + buf[8] = ilits->fw_info[14]; + buf[11] = buf[7]; + buf[12] = buf[8]; + goto out; + } + + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_TP_INFORMATION; + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Write tp info pre cmd failed\n"); + ret = -EINVAL; + goto out; + + } + + ret = ilits->wrapper(&cmd[1], sizeof(u8), buf, ilits->protocol->tp_info_len, ON, OFF); + if (ret < 0) { + ILI_ERR("Read tp info error\n"); + goto out; + } + + if (buf[0] != P5_X_GET_TP_INFORMATION) { + ILI_ERR("Invalid tp info\n"); + ret = -1; + goto out; + } + +out: + ilits->min_x = buf[1]; + ilits->min_y = buf[2]; + ilits->max_x = buf[4] << 8 | buf[3]; + ilits->max_y = buf[6] << 8 | buf[5]; + ilits->xch_num = buf[7]; + ilits->ych_num = buf[8]; + ilits->stx = buf[11]; + ilits->srx = buf[12]; + + ILI_INFO("TP Info: min_x = %d, min_y = %d, max_x = %d, max_y = %d\n", ilits->min_x, ilits->min_y, ilits->max_x, ilits->max_y); + ILI_INFO("TP Info: xch = %d, ych = %d, stx = %d, srx = %d\n", ilits->xch_num, ilits->ych_num, ilits->stx, ilits->srx); + return ret; +} + +static void ilitek_tddi_ic_check_protocol_ver(u32 pver) +{ + int i = 0; + + if (ilits->protocol->ver == pver) { + ILI_DBG("same procotol version, do nothing\n"); + return; + } + + for (i = 0; i < PROTOCL_VER_NUM - 1; i++) { + if (protocol_info[i].ver == pver) { + ilits->protocol = &protocol_info[i]; + ILI_INFO("update protocol version = %x\n", ilits->protocol->ver); + return; + } + } + + ILI_ERR("Not found a correct protocol version in list, use newest version\n"); + ilits->protocol = &protocol_info[PROTOCL_VER_NUM - 1]; +} + +int ili_ic_get_protocl_ver(void) +{ + int ret = 0; + u8 cmd[2] = {0}; + u8 buf[10] = {0}; + u32 ver; + + if (ilits->info_from_hex) { + buf[1] = ilits->fw_info[72]; + buf[2] = ilits->fw_info[73]; + buf[3] = ilits->fw_info[74]; + goto out; + } + + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_PROTOCOL_VERSION; + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Write protocol ver pre cmd failed\n"); + ret = -EINVAL; + goto out; + + } + + if (ilits->wrapper(&cmd[1], sizeof(u8), buf, ilits->protocol->pro_ver_len, ON, OFF)) { + ILI_ERR("Read protocol version error\n"); + ret = -EINVAL; + goto out; + } + + if (buf[0] != P5_X_GET_PROTOCOL_VERSION) { + ILI_ERR("Invalid protocol ver\n"); + ret = -1; + goto out; + } + +out: + ver = buf[1] << 16 | buf[2] << 8 | buf[3]; + + ilitek_tddi_ic_check_protocol_ver(ver); + + ILI_INFO("Protocol version = %d.%d.%d\n", ilits->protocol->ver >> 16, + (ilits->protocol->ver >> 8) & 0xFF, ilits->protocol->ver & 0xFF); + return ret; +} + +int ili_ic_get_info(void) +{ + int ret = 0; + + if (!atomic_read(&ilits->ice_stat)) { + ILI_ERR("ice mode doesn't enable\n"); + return -1; + } + + + if (ili_ice_mode_read(ilits->chip->pid_addr, &ilits->chip->pid, sizeof(u32)) < 0) + ILI_ERR("Read chip pid error\n"); + + if (ili_ice_mode_read(ilits->chip->otp_addr, &ilits->chip->otp_id, sizeof(u32)) < 0) + ILI_ERR("Read otp id error\n"); + if (ili_ice_mode_read(ilits->chip->ana_addr, &ilits->chip->ana_id, sizeof(u32)) < 0) + ILI_ERR("Read ana id error\n"); + + ilits->chip->pid = ilits->chip->pid; + ilits->chip->id = ilits->chip->pid >> 16; + ilits->chip->type = (ilits->chip->pid & 0x0000FF00) >> 8; + ilits->chip->ver = ilits->chip->pid & 0xFF; + ilits->chip->otp_id &= 0xFF; + ilits->chip->ana_id &= 0xFF; + + ILI_INFO("CHIP: PID = %x\n",(ilits->chip->pid >> 8)); + + ret = ilitek_tddi_ic_check_support(ilits->chip->pid, ilits->chip->id); + return ret; +} + +int ili_ic_dummy_check(void) +{ + int ret = 0; + u32 wdata = 0xA55A5AA5; + u32 rdata = 0; + + if (!atomic_read(&ilits->ice_stat)) { + ILI_ERR("ice mode doesn't enable\n"); + return -1; + } + + if (ili_ice_mode_write(WDT9_DUMMY2, wdata, sizeof(u32)) < 0) + ILI_ERR("Write dummy error\n"); + + + if (ili_ice_mode_read(WDT9_DUMMY2, &rdata, sizeof(u32)) < 0) + ILI_ERR("Read dummy error\n"); + + if (rdata != wdata){ + ILI_ERR("Dummy check incorrect, rdata = %x wdata = %x \n", rdata, wdata); + return -1; + } + ILI_INFO("Ilitek IC check successe\n"); + + return ret; +} + +static struct ilitek_ic_info chip; + +void ili_ic_init(void) +{ + chip.pid_addr = TDDI_PID_ADDR; + chip.pc_counter_addr = TDDI_PC_COUNTER_ADDR; + chip.pc_latch_addr = TDDI_PC_LATCH_ADDR; + chip.otp_addr = TDDI_OTP_ID_ADDR; + chip.ana_addr = TDDI_ANA_ID_ADDR; + chip.reset_addr = TDDI_CHIP_RESET_ADDR; + + ilits->protocol = &protocol_info[PROTOCL_VER_NUM - 1]; + ilits->chip = &chip; +} diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_mp.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_mp.c new file mode 100755 index 000000000000..69a4f76c9400 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_mp.c @@ -0,0 +1,3847 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" + +#define VALUE 0 +#define RETRY_COUNT 3 +#define INT_CHECK 0 +#define POLL_CHECK 1 + +#define BENCHMARK 1 +#define NODETYPE 1 + +#define TYPE_BENCHMARK 0 +#define TYPE_NO_JUGE 1 +#define TYPE_JUGE 2 + +#define NORMAL_CSV_PASS_NAME "mp_pass" +#define NORMAL_CSV_FAIL_NAME "mp_fail" +#define NORMAL_CSV_WARNING_NAME "mp_warning" + +#define CSV_FILE_SIZE (1 * M) + +#define PARSER_MAX_CFG_BUF (512 * 3) +#define PARSER_MAX_KEY_NUM (600 * 3) +#define PARSER_MAX_KEY_NAME_LEN 100 +#define PARSER_MAX_KEY_VALUE_LEN 2000 +#define MAX_SECTION_NUM 100 +#define BENCHMARK_KEY_NAME "benchmark_data" +#define NODE_TYPE_KEY_NAME "node type" +#define INI_ERR_OUT_OF_LINE -1 + +#define CMD_MUTUAL_DAC 0x1 +#define CMD_MUTUAL_BG 0x2 +#define CMD_MUTUAL_SIGNAL 0x3 +#define CMD_MUTUAL_NO_BK 0x5 +#define CMD_MUTUAL_HAVE_BK 0x8 +#define CMD_MUTUAL_BK_DAC 0x10 +#define CMD_SELF_DAC 0xC +#define CMD_SELF_BG 0xF +#define CMD_SELF_SIGNAL 0xD +#define CMD_SELF_NO_BK 0xE +#define CMD_SELF_HAVE_BK 0xB +#define CMD_SELF_BK_DAC 0x11 +#define CMD_KEY_DAC 0x14 +#define CMD_KEY_BG 0x16 +#define CMD_KEY_NO_BK 0x7 +#define CMD_KEY_HAVE_BK 0x15 +#define CMD_KEY_OPEN 0x12 +#define CMD_KEY_SHORT 0x13 +#define CMD_ST_DAC 0x1A +#define CMD_ST_BG 0x1C +#define CMD_ST_NO_BK 0x17 +#define CMD_ST_HAVE_BK 0x1B +#define CMD_ST_OPEN 0x18 +#define CMD_TX_SHORT 0x19 +#define CMD_RX_SHORT 0x4 +#define CMD_RX_OPEN 0x6 +#define CMD_TX_RX_DELTA 0x1E +#define CMD_CM_DATA 0x9 +#define CMD_CS_DATA 0xA +#define CMD_TRCRQ_PIN 0x20 +#define CMD_RESX2_PIN 0x21 +#define CMD_MUTUAL_INTEGRA_TIME 0x22 +#define CMD_SELF_INTEGRA_TIME 0x23 +#define CMD_KEY_INTERGRA_TIME 0x24 +#define CMD_ST_INTERGRA_TIME 0x25 +#define CMD_PEAK_TO_PEAK 0x1D +#define CMD_GET_TIMING_INFO 0x30 +#define CMD_DOZE_P2P 0x32 +#define CMD_DOZE_RAW 0x33 +#define CMD_PIN_TEST 0x61 + +#define MP_DATA_PASS 0 +#define MP_DATA_FAIL -1 + +#define Mathabs(x) ({ \ + long ret; \ + if (sizeof(x) == sizeof(long)) { \ + long __x = (x); \ + ret = (__x < 0) ? -__x : __x; \ + } else { \ + int __x = (x); \ + ret = (__x < 0) ? -__x : __x; \ + } \ + ret; \ + }) + +#define DUMP(fmt, arg...) \ + do { \ + if (debug_en) \ + pr_cont(fmt, ##arg); \ + } while (0) + +static struct ini_file_data { + char section_name[PARSER_MAX_KEY_NAME_LEN]; + char key_name[PARSER_MAX_KEY_NAME_LEN]; + char key_value[PARSER_MAX_KEY_VALUE_LEN]; + int section_len; + int key_name_len; + int key_val_len; +} *ini_info; + +enum open_test_node_type { + NO_COMPARE = 0x00, /* Not A Area, No Compare */ + AA_Area = 0x01, /* AA Area, Compare using Charge_AA */ + Border_Area = 0x02, /* Border Area, Compare using Charge_Border */ + Notch = 0x04, /* Notch Area, Compare using Charge_Notch */ + Round_Corner = 0x08, /* Round Corner, No Compare */ + Skip_Micro = 0x10 /* Skip_Micro, No Compare */ +}; + +enum mp_test_catalog { + MUTUAL_TEST = 0, + SELF_TEST = 1, + KEY_TEST = 2, + ST_TEST = 3, + TX_RX_DELTA = 4, + UNTOUCH_P2P = 5, + PIXEL = 6, + OPEN_TEST = 7, + PEAK_TO_PEAK_TEST = 8, + SHORT_TEST = 9, + PIN_TEST = 10, +}; + +struct mp_test_P540_open { + s32 *tdf_700; + s32 *tdf_250; + s32 *tdf_200; + s32 *cbk_700; + s32 *cbk_250; + s32 *cbk_200; + s32 *charg_rate; + s32 *full_Open; + s32 *dac; +}; + +struct mp_test_open_c { + s32 *cap_dac; + s32 *cap_raw; + s32 *dcl_cap; +}; + +struct open_test_para { + int tvch; + int tvcl; + int gain; + int cbk_step; + int cint; +} open_para; + +struct shor_test_para { + int tvch; + int tvcl; + int variation; + int rinternal; + int cint; +} short_para; + +static struct core_mp_test_data { + u32 chip_pid; + u16 chip_id; + u8 chip_type; + u8 chip_ver; + u32 fw_ver; + u32 protocol_ver; + u32 core_ver; + char ini_date[128]; + char ini_ver[64]; + int no_bk_shift; + bool retry; + bool m_signal; + bool m_dac; + bool s_signal; + bool s_dac; + bool key_dac; + bool st_dac; + bool p_no_bk; + bool p_has_bk; + bool open_integ; + bool open_cap; + bool isLongV; + bool td_retry; + bool all_pass; + + int cdc_len; + int xch_len; + int ych_len; + int stx_len; + int srx_len; + int key_len; + int st_len; + int frame_len; + int mp_items; + int final_result; + int short_varia; + + u32 overlay_start_addr; + u32 overlay_end_addr; + u32 mp_flash_addr; + u32 mp_size; + u8 dma_trigger_enable; + + /* Tx/Rx threshold & buffer */ + int TxDeltaMax; + int TxDeltaMin; + int RxDeltaMax; + int RxDeltaMin; + s32 *tx_delta_buf; + s32 *rx_delta_buf; + s32 *tx_max_buf; + s32 *tx_min_buf; + s32 *rx_max_buf; + s32 *rx_min_buf; + + int tdf; + int busy_cdc; + bool ctrl_lcm; + bool lost_benchmark; + bool lost_parameter; +} core_mp = {0}; + +struct mp_test_items { + /* The description must be the same as ini's section name */ + char *desp; + char *result; + int catalog; + u8 cmd; + u8 spec_option; + u8 type_option; + bool run; + bool lcm; + int bch_mrk_multi; + int max; + int max_res; + int item_result; + int min; + int min_res; + int frame_count; + int trimmed_mean; + int lowest_percentage; + int highest_percentage; + int v_tdf_1; + int v_tdf_2; + int h_tdf_1; + int h_tdf_2; + int goldenmode; + int max_min_mode; + int bch_mrk_frm_num; + int retry_cnt; + u8 delay_time; + u8 test_int_pin; + u8 int_pulse_test; + s32 *result_buf; + s32 *buf; + s32 *max_buf; + s32 *min_buf; + s32 *bench_mark_max; + s32 *bench_mark_min; + s32 **bch_mrk_max; + s32 **bch_mrk_min; + s32 *node_type; + int (*do_test)(int index); +}; + +#define MP_TEST_ITEM 50 +static struct mp_test_items tItems[MP_TEST_ITEM] = { + {.desp = "baseline data(bg)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_BG, .lcm = ON}, + {.desp = "untouch signal data(bg-raw-4096) - mutual", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_SIGNAL, .lcm = ON}, + {.desp = "manual bk data(mutual)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_BK_DAC, .lcm = ON}, + {.desp = "calibration data(dac) - self", .catalog = SELF_TEST, .cmd = CMD_SELF_DAC, .lcm = ON}, + {.desp = "baselin data(bg,self_tx,self_r)", .catalog = SELF_TEST, .cmd = CMD_SELF_BG, .lcm = ON}, + {.desp = "untouch signal data(bg-raw-4096) - self", .catalog = SELF_TEST, .cmd = CMD_SELF_SIGNAL, .lcm = ON}, + {.desp = "raw data(no bk) - self", .catalog = SELF_TEST, .cmd = CMD_SELF_NO_BK, .lcm = ON}, + {.desp = "raw data(have bk) - self", .catalog = SELF_TEST, .cmd = CMD_SELF_HAVE_BK, .lcm = ON}, + {.desp = "manual bk dac data(self_tx,self_rx)", .catalog = SELF_TEST, .cmd = CMD_SELF_BK_DAC, .lcm = ON}, + {.desp = "calibration data(dac/icon)", .catalog = KEY_TEST, .cmd = CMD_KEY_DAC, .lcm = ON}, + {.desp = "key baseline data", .catalog = KEY_TEST, .cmd = CMD_KEY_BG, .lcm = ON}, + {.desp = "key raw data", .catalog = KEY_TEST, .cmd = CMD_KEY_NO_BK, .lcm = ON}, + {.desp = "key raw bk dac", .catalog = KEY_TEST, .cmd = CMD_KEY_HAVE_BK, .lcm = ON}, + {.desp = "key raw open test", .catalog = KEY_TEST, .cmd = CMD_KEY_OPEN, .lcm = ON}, + {.desp = "key raw short test", .catalog = KEY_TEST, .cmd = CMD_KEY_SHORT, .lcm = ON}, + {.desp = "st calibration data(dac)", .catalog = ST_TEST, .cmd = CMD_ST_DAC, .lcm = ON}, + {.desp = "st baseline data(bg)", .catalog = ST_TEST, .cmd = CMD_ST_BG, .lcm = ON}, + {.desp = "st raw data(no bk)", .catalog = ST_TEST, .cmd = CMD_ST_NO_BK, .lcm = ON}, + {.desp = "st raw(have bk)", .catalog = ST_TEST, .cmd = CMD_ST_HAVE_BK, .lcm = ON}, + {.desp = "st open data", .catalog = ST_TEST, .cmd = CMD_ST_OPEN, .lcm = ON}, + {.desp = "tx short test", .catalog = MUTUAL_TEST, .cmd = CMD_TX_SHORT, .lcm = ON}, + {.desp = "rx open", .catalog = MUTUAL_TEST, .cmd = CMD_RX_OPEN, .lcm = ON}, + {.desp = "untouch cm data", .catalog = MUTUAL_TEST, .cmd = CMD_CM_DATA, .lcm = ON}, + {.desp = "untouch cs data", .catalog = MUTUAL_TEST, .cmd = CMD_CS_DATA, .lcm = ON}, + {.desp = "tx/rx delta", .catalog = TX_RX_DELTA, .cmd = CMD_TX_RX_DELTA, .lcm = ON}, + {.desp = "untouch peak to peak", .catalog = UNTOUCH_P2P, .cmd = CMD_MUTUAL_SIGNAL, .lcm = ON}, + {.desp = "pixel raw (no bk)", .catalog = PIXEL, .cmd = CMD_MUTUAL_NO_BK, .lcm = ON}, + {.desp = "pixel raw (have bk)", .catalog = PIXEL, .cmd = CMD_MUTUAL_HAVE_BK, .lcm = ON}, + {.desp = "noise peak to peak(cut panel)", .catalog = PEAK_TO_PEAK_TEST, .lcm = ON}, + {.desp = "open test(integration)", .catalog = OPEN_TEST, .cmd = CMD_RX_SHORT, .lcm = ON}, + {.desp = "open test(cap)", .catalog = OPEN_TEST, .cmd = CMD_RX_SHORT, .lcm = ON}, + /* Following is the new test items for protocol 5.4.0 above */ + {.desp = "pin test ( int and rst )", .catalog = PIN_TEST, .cmd = CMD_PIN_TEST, .lcm = ON}, + {.desp = "noise peak to peak(with panel)", .catalog = PEAK_TO_PEAK_TEST, .lcm = ON}, + {.desp = "noise peak to peak(ic only)", .catalog = PEAK_TO_PEAK_TEST, .cmd = CMD_PEAK_TO_PEAK, .lcm = ON}, + {.desp = "open test(integration)_sp", .catalog = OPEN_TEST, .lcm = ON}, + {.desp = "raw data(no bk)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_NO_BK, .lcm = ON}, + {.desp = "raw data(have bk)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_HAVE_BK, .lcm = ON}, + {.desp = "calibration data(dac)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_DAC, .lcm = ON}, + {.desp = "short test -ili9881", .catalog = SHORT_TEST, .cmd = CMD_RX_SHORT, .lcm = ON}, + {.desp = "short test", .catalog = SHORT_TEST, .lcm = ON}, + {.desp = "doze raw data", .catalog = MUTUAL_TEST, .lcm = ON}, + {.desp = "doze peak to peak", .catalog = PEAK_TO_PEAK_TEST, .lcm = ON}, + {.desp = "open test_c", .catalog = OPEN_TEST, .lcm = ON}, + {.desp = "touch deltac", .catalog = MUTUAL_TEST, .lcm = ON}, + /* LCM OFF TEST */ + {.desp = "raw data(have bk) (lcm off)", .catalog = MUTUAL_TEST, .lcm = OFF}, + {.desp = "raw data(no bk) (lcm off)", .catalog = MUTUAL_TEST, .lcm = OFF}, + {.desp = "noise peak to peak(with panel) (lcm off)", .catalog = PEAK_TO_PEAK_TEST, .lcm = OFF}, + {.desp = "noise peak to peak(ic only) (lcm off)", .catalog = PEAK_TO_PEAK_TEST, .lcm = OFF}, + {.desp = "raw data_td (lcm off)", .catalog = MUTUAL_TEST, .lcm = OFF}, + {.desp = "peak to peak_td (lcm off)", .catalog = PEAK_TO_PEAK_TEST, .lcm = OFF}, +}; + +static struct run_index { + int count; + int index[MP_TEST_ITEM]; +} ri; + +static s32 *frame_buf; +static s32 **frm_buf; +static s32 *key_buf; +static s32 *frame1_cbk700, *frame1_cbk250, *frame1_cbk200; +static s32 *cap_dac, *cap_raw; +static int g_ini_items = 0; +static char csv_name[128] = {0}; +static char seq_item[MAX_SECTION_NUM][PARSER_MAX_KEY_NAME_LEN] = {{0}}; + +static int isspace_t(int x) +{ + if (x == ' ' || x == '\t' || x == '\n' || + x == '\f' || x == '\b' || x == '\r') + return 1; + else + return 0; +} + +static void dump_benchmark_data(s32 *max_ptr, s32 *min_ptr) +{ + ili_dump_data(max_ptr, 32, core_mp.frame_len, 0, "Dump Benchmark Max : "); + ili_dump_data(min_ptr, 32, core_mp.frame_len, 0, "Dump Benchmark Min : "); +} + +static void dump_node_type_buffer(s32 *node_ptr, u8 *name) +{ + ili_dump_data(node_ptr, 32, core_mp.frame_len, 0, "Dump NodeType : "); +} + +static int parser_get_ini_key_value(char *section, char *key, char *value) +{ + int i = 0; + int ret = -2; + + for (i = 0; i < g_ini_items; i++) { + if (ipio_strcmp(section, ini_info[i].section_name) != 0) + continue; + + if (ipio_strcmp(key, ini_info[i].key_name) == 0) { + ipio_memcpy(value, ini_info[i].key_value, ini_info[i].key_val_len, PARSER_MAX_KEY_VALUE_LEN); + ILI_DBG("(key: %s, value:%s) => (ini key: %s, val: %s)\n", + key, + value, + ini_info[i].key_name, + ini_info[i].key_value); + ret = 0; + break; + } + } + return ret; +} + +static void parser_ini_nodetype(s32 *type_ptr, char *desp, int frame_len) +{ + int i = 0, j = 0, index1 = 0, temp, count = 0; + char str[512] = {0}, record = ','; + + for (i = 0; i < g_ini_items; i++) { + if (!strstr(ini_info[i].section_name, desp) || + ipio_strcmp(ini_info[i].key_name, NODE_TYPE_KEY_NAME) != 0) { + continue; + } + + record = ','; + for (j = 0, index1 = 0; j <= ini_info[i].key_val_len; j++) { + if (ini_info[i].key_value[j] == ';' || j == ini_info[i].key_val_len) { + if (record != '.') { + memset(str, 0, sizeof(str)); + ipio_memcpy(str, &ini_info[i].key_value[index1], (j - index1), sizeof(str)); + temp = ili_katoi(str); + + /* Over boundary, end to calculate. */ + if (count >= frame_len) { + ILI_ERR("count(%d) is larger than frame length, break\n", count); + break; + } + type_ptr[count] = temp; + count++; + } + record = ini_info[i].key_value[j]; + index1 = j + 1; + } + } + } +} + +static void parser_ini_benchmark(s32 *max_ptr, s32 *min_ptr, int8_t type, char *desp, int frame_len, char *bchmrk_name) +{ + int i = 0, j = 0, index1 = 0, temp, count = 0; + char str[512] = {0}, record = ','; + s32 data[4]; + char benchmark_str[256] = {0}; + + /* format complete string from the name of section "_Benchmark_Data". */ + snprintf(benchmark_str,sizeof(benchmark_str), "%s%s%s", desp, "_", bchmrk_name); + ILI_DBG("benchmark_str = %s\n", benchmark_str); + + for (i = 0; i < g_ini_items; i++) { + if ((ipio_strcmp(ini_info[i].section_name, benchmark_str) != 0)) + continue; + record = ','; + for (j = 0, index1 = 0; j <= ini_info[i].key_val_len; j++) { + if (ini_info[i].key_value[j] == ',' || ini_info[i].key_value[j] == ';' || + ini_info[i].key_value[j] == '.' || j == ini_info[i].key_val_len) { + + if (record != '.') { + memset(str, 0, sizeof(str)); + ipio_memcpy(str, &ini_info[i].key_value[index1], (j - index1), sizeof(str)); + temp = ili_katoi(str); + data[(count % 4)] = temp; + + /* Over boundary, end to calculate. */ + if ((count / 4) >= frame_len) { + ILI_ERR("count (%d) is larger than frame length, break\n", (count / 4)); + break; + } + + if ((count % 4) == 3) { + if (data[0] == 1) { + if (type == VALUE) { + max_ptr[count/4] = data[1] + data[2]; + min_ptr[count/4] = data[1] - data[3]; + } else { + max_ptr[count/4] = data[1] + (data[1] * data[2]) / 100; + min_ptr[count/4] = data[1] - (data[1] * data[3]) / 100; + } + } else { + max_ptr[count/4] = INT_MAX; + min_ptr[count/4] = INT_MIN; + } + } + count++; + } + record = ini_info[i].key_value[j]; + index1 = j + 1; + } + } + } +} + +static int parser_get_tdf_value(char *str, int catalog) +{ + u32 i, ans, index = 0, flag = 0, count = 0, size = 0; + char s[10] = {0}; + + if (!str) { + ILI_ERR("String is null\n"); + return -1; + } + + size = strlen(str); + for (i = 0, count = 0; i < size; i++) { + if (str[i] == '.') { + flag = 1; + continue; + } + s[index++] = str[i]; + if (flag) + count++; + } + ans = ili_katoi(s); + + /* Multiply by 100 to shift out of decimal point */ + if (catalog == SHORT_TEST) { + if (count == 0) + ans = ans * 100; + else if (count == 1) + ans = ans * 10; + } + + return ans; +} + +static int parser_get_u8_array(char *key, u8 *buf, u16 base, int len) +{ + char *s = key; + char *pToken; + int ret, conut = 0; + long s_to_long = 0; + + if (strlen(s) == 0 || len <= 0) { + ILI_ERR("Can't find any characters inside buffer\n"); + return -1; + } + + /* + * @base: The number base to use. The maximum supported base is 16. If base is + * given as 0, then the base of the string is automatically detected with the + * conventional semantics - If it begins with 0x the number will be parsed as a + * hexadecimal (case insensitive), if it otherwise begins with 0, it will be + * parsed as an octal number. Otherwise it will be parsed as a decimal. + */ + if (isspace_t((int)(unsigned char)*s) == 0) { + while ((pToken = strsep(&s, ",")) != NULL) { + ret = kstrtol(pToken, base, &s_to_long); + if (ret == 0) + buf[conut] = s_to_long; + else + ILI_INFO("convert string too long, ret = %d\n", ret); + conut++; + + if (conut >= len) + break; + } + } + + return conut; +} + +static int parser_get_int_data(char *section, char *keyname, char *rv, int rv_len) +{ + int len = 0; + char value[512] = { 0 }; + + if (rv == NULL || section == NULL || keyname == NULL) { + ILI_ERR("Parameters are invalid\n"); + return -EINVAL; + } + + /* return a white-space string if get nothing */ + if (parser_get_ini_key_value(section, keyname, value) < 0) { + snprintf(rv, rv_len, "%s", value); + return 0; + } + + len = snprintf(rv, rv_len, "%s", value); + return len; +} + +/* Count the number of each line and assign the content to tmp buffer */ +static int parser_get_ini_phy_line(char *data, char *buffer, int maxlen) +{ + int i = 0; + int j = 0; + int iRetNum = -1; + char ch1 = '\0'; + + for (i = 0, j = 0; i < maxlen; j++) { + ch1 = data[j]; + iRetNum = j + 1; + if (ch1 == '\n' || ch1 == '\r') { /* line end */ + ch1 = data[j + 1]; + if (ch1 == '\n' || ch1 == '\r') + iRetNum++; + break; + } else if (ch1 == 0x00) { + //iRetNum = -1; + break; /* file end */ + } + + buffer[i++] = ch1; + } + + buffer[i] = '\0'; + return iRetNum; +} + +static char *parser_ini_str_trim_r(char *buf) +{ + int len, i; + char *tmp = NULL; + char x[512] = {0}; + char *y = NULL; + char *empty = ""; + + len = strlen(buf); + + if (len < sizeof(x)) { + tmp = x; + goto copy; + } + + y = kzalloc(len, GFP_KERNEL); + if (ERR_ALLOC_MEM(y)) { + ILI_ERR("Failed to allocate tmp buf\n"); + return empty; + } + tmp = y; + +copy: + for (i = 0; i < len; i++) { + if (buf[i] != ' ') + break; + } + + if (i < len) + strncpy(tmp, (buf + i), (len - i)); + + strncpy(buf, tmp, len); + ipio_kfree((void **)&y); + return buf; +} + +static int parser_get_ini_phy_data(char *data, int fsize) +{ + int i, n = 0, ret = 0, banchmark_flag = 0, empty_section, nodetype_flag = 0; + int offset = 0, isEqualSign = 0, scount = 0; + char *ini_buf = NULL, *tmpSectionName = NULL; + char M_CFG_SSL = '['; + char M_CFG_SSR = ']'; +/* char M_CFG_NIS = ':'; */ + char M_CFG_NTS = '#'; + char M_CFG_EQS = '='; + + if (data == NULL) { + ILI_ERR("INI data is NULL\n"); + ret = -EINVAL; + goto out; + } + + ini_buf = kzalloc((PARSER_MAX_CFG_BUF + 1) * sizeof(char), GFP_KERNEL); + if (ERR_ALLOC_MEM(ini_buf)) { + ILI_ERR("Failed to allocate ini_buf memory, %ld\n", PTR_ERR(ini_buf)); + ret = -ENOMEM; + goto out; + } + + tmpSectionName = kzalloc((PARSER_MAX_CFG_BUF + 1) * sizeof(char), GFP_KERNEL); + if (ERR_ALLOC_MEM(tmpSectionName)) { + ILI_ERR("Failed to allocate tmpSectionName memory, %ld\n", PTR_ERR(tmpSectionName)); + ret = -ENOMEM; + goto out; + } + + memset(seq_item, 0, MP_TEST_ITEM * PARSER_MAX_KEY_NAME_LEN * sizeof(char)); + + while (true) { + banchmark_flag = 0; + empty_section = 0; + nodetype_flag = 0; + if (g_ini_items > PARSER_MAX_KEY_NUM) { + ILI_ERR("MAX_KEY_NUM: Out of length\n"); + goto out; + } + + if (offset >= fsize) + goto out;/*over size*/ + + n = parser_get_ini_phy_line(data + offset, ini_buf, PARSER_MAX_CFG_BUF); + if (n < 0) { + ILI_ERR("End of Line\n"); + goto out; + } + + offset += n; + + n = strlen(parser_ini_str_trim_r(ini_buf)); + + if (n == 0 || ini_buf[0] == M_CFG_NTS) + continue; + + /* Get section names */ + if (n > 2 && ((ini_buf[0] == M_CFG_SSL && ini_buf[n - 1] != M_CFG_SSR))) { + ILI_ERR("Bad Section: %s\n", ini_buf); + ret = -EINVAL; + goto out; + } else { + if (ini_buf[0] == M_CFG_SSL) { + ini_info[g_ini_items].section_len = n - 2; + if (ini_info[g_ini_items].section_len > PARSER_MAX_KEY_NAME_LEN) { + ILI_ERR("MAX_KEY_NAME_LEN: Out Of Length\n"); + ret = INI_ERR_OUT_OF_LINE; + goto out; + } + + if (scount > MAX_SECTION_NUM) { + ILI_ERR("seq_item is over than its define (%d), abort\n", scount); + ret = INI_ERR_OUT_OF_LINE; + goto out; + } + + ini_buf[n - 1] = 0x00; + strncpy((char *)tmpSectionName, ini_buf + 1, (PARSER_MAX_CFG_BUF + 1) * sizeof(char)); + banchmark_flag = 0; + nodetype_flag = 0; + strncpy(seq_item[scount], tmpSectionName, PARSER_MAX_KEY_NAME_LEN); + scount++; + ILI_DBG("Section Name: %s, Len: %d, offset = %d\n", seq_item[scount], n - 2, offset); + continue; + } + } + + /* copy section's name without square brackets to its real buffer */ + strncpy(ini_info[g_ini_items].section_name, tmpSectionName, (PARSER_MAX_KEY_NAME_LEN * sizeof(char))); + ini_info[g_ini_items].section_len = strlen(tmpSectionName); + + isEqualSign = 0; + for (i = 0; i < n; i++) { + if (ini_buf[i] == M_CFG_EQS) { + isEqualSign = i; + break; + } + if (ini_buf[i] == M_CFG_SSL || ini_buf[i] == M_CFG_SSR) { + empty_section = 1; + break; + } + } + + if (isEqualSign == 0) { + if (empty_section) + continue; + + if (strstr(ini_info[g_ini_items].section_name, BENCHMARK_KEY_NAME) != NULL) { + isEqualSign = -1; + ini_info[g_ini_items].key_name_len = strlen(ini_info[g_ini_items].section_name); + strncpy(ini_info[g_ini_items].key_name, ini_info[g_ini_items].section_name, (PARSER_MAX_KEY_NAME_LEN * sizeof(char))); + ini_info[g_ini_items].key_val_len = n; + } else if (strstr(ini_info[g_ini_items].section_name, NODE_TYPE_KEY_NAME) != NULL) { + isEqualSign = -1; + ini_info[g_ini_items].key_name_len = strlen(NODE_TYPE_KEY_NAME); + strncpy(ini_info[g_ini_items].key_name, NODE_TYPE_KEY_NAME, (PARSER_MAX_KEY_NAME_LEN * sizeof(char))); + ini_info[g_ini_items].key_val_len = n; + } else { + continue; + } + } else { + ini_info[g_ini_items].key_name_len = isEqualSign; + if (ini_info[g_ini_items].key_name_len > PARSER_MAX_KEY_NAME_LEN) { + /* ret = CFG_ERR_OUT_OF_LEN; */ + ILI_ERR("MAX_KEY_NAME_LEN: Out Of Length\n"); + ret = INI_ERR_OUT_OF_LINE; + goto out; + } + + ipio_memcpy(ini_info[g_ini_items].key_name, ini_buf, + ini_info[g_ini_items].key_name_len, PARSER_MAX_KEY_NAME_LEN); + ini_info[g_ini_items].key_val_len = n - isEqualSign - 1; + } + + if (ini_info[g_ini_items].key_val_len > PARSER_MAX_KEY_VALUE_LEN) { + ILI_ERR("MAX_KEY_VALUE_LEN: Out Of Length\n"); + ret = INI_ERR_OUT_OF_LINE; + goto out; + } + + ipio_memcpy(ini_info[g_ini_items].key_value, + ini_buf + isEqualSign + 1, ini_info[g_ini_items].key_val_len, PARSER_MAX_KEY_VALUE_LEN); + + ILI_DBG("%s = %s\n", ini_info[g_ini_items].key_name, + ini_info[g_ini_items].key_value); + + g_ini_items++; + } +out: + ipio_kfree((void **)&ini_buf); + ipio_kfree((void **)&tmpSectionName); + return ret; +} + +static int ilitek_tddi_mp_ini_parser(const char *path) +{ + int i, ret = 0, fsize = 0; + char *tmp = NULL; + const struct firmware *ini = NULL; + struct file *f = NULL; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + mm_segment_t old_fs; +#endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) + loff_t pos = 0; +#endif + + ILI_INFO("ini file path = %s\n", path); + + f = filp_open(path, O_RDONLY, 644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open ini file at %ld, try to request_firmware\n", PTR_ERR(f)); + f = NULL; + path = ilits->md_ini_rq_path; + ILI_INFO("request path = %s\n", path); + if (request_firmware(&ini, path, ilits->dev) < 0) { + ILI_ERR("Request ini file failed\n"); + return -EINVAL; + } + } + + if (f != NULL) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)) + fsize = f->f_inode->i_size; +#else + struct inode *inode; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)) + inode = f->f_dentry->d_inode; +#else + inode = f->f_path.dentry->d_inode; +#endif + fsize = inode->i_size; +#endif + } else { + fsize = ini->size; + } + + ILI_INFO("ini file size = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is invaild\n"); + ret = -EINVAL; + goto out; + } + + tmp = vmalloc(fsize+1); + if (ERR_ALLOC_MEM(tmp)) { + ILI_ERR("Failed to allocate tmp memory, %ld\n", PTR_ERR(tmp)); + ret = -ENOMEM; + goto out; + } + + if (f != NULL) { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + old_fs = get_fs(); + set_fs(get_ds()); + vfs_read(f, tmp, fsize, &pos); + set_fs(old_fs); +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) + kernel_read(f, tmp, fsize, &pos); +#endif + tmp[fsize] = 0x0; + } else { + memcpy(tmp, ini->data, fsize); + } + + g_ini_items = 0; + + /* Initialise ini strcture */ + for (i = 0; i < PARSER_MAX_KEY_NUM; i++) { + memset(ini_info[i].section_name, 0, PARSER_MAX_KEY_NAME_LEN); + memset(ini_info[i].key_name, 0, PARSER_MAX_KEY_NAME_LEN); + memset(ini_info[i].key_value, 0, PARSER_MAX_KEY_VALUE_LEN); + ini_info[i].section_len = 0; + ini_info[i].key_name_len = 0; + ini_info[i].key_val_len = 0; + } + + /* change all characters to lower case */ + for (i = 0; i < fsize; i++) + tmp[i] = tolower(tmp[i]); + + ret = parser_get_ini_phy_data(tmp, fsize); + if (ret < 0) { + ILI_ERR("Failed to get physical ini data, ret = %d\n", ret); + goto out; + } + + ILI_INFO("Parsed ini file done\n"); +out: + ipio_vfree((void **)&tmp); + + if (f != NULL) + filp_close(f, NULL); + else + release_firmware(ini); + + return ret; +} + +static void run_pixel_test(int index) +{ + int i, x, y; + s32 *p_comb = frame_buf; + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int tmp[4] = { 0 }, max = 0; + int shift = y * core_mp.xch_len; + int centre = p_comb[shift + x]; + + /* + * if its position is in corner, the number of point + * we have to minus is around 2 to 3. + */ + if (y == 0 && x == 0) { + tmp[0] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[1] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y == (core_mp.ych_len - 1) && x == 0) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y == 0 && x == (core_mp.xch_len - 1)) { + tmp[0] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + } else if (y == (core_mp.ych_len - 1) && x == (core_mp.xch_len - 1)) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + } else if (y == 0 && x != 0) { + tmp[0] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[2] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y != 0 && x == 0) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + tmp[2] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + + } else if (y == (core_mp.ych_len - 1) && x != 0) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[2] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y != 0 && x == (core_mp.xch_len - 1)) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[2] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + } else { + /* middle minus four directions */ + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[2] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[3] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } + + max = tmp[0]; + + for (i = 0; i < 4; i++) { + if (tmp[i] > max) + max = tmp[i]; + } + + tItems[index].buf[shift + x] = max; + } + } +} + +static void run_untouch_p2p_test(int index) +{ + int x, y; + s32 *p_comb = frame_buf; + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int shift = y * core_mp.xch_len; + + if (p_comb[shift + x] > tItems[index].max_buf[shift + x]) + tItems[index].max_buf[shift + x] = p_comb[shift + x]; + + if (p_comb[shift + x] < tItems[index].min_buf[shift + x]) + tItems[index].min_buf[shift + x] = p_comb[shift + x]; + + tItems[index].buf[shift + x] = + tItems[index].max_buf[shift + x] - tItems[index].min_buf[shift + x]; + } + } +} + +static int run_open_test(int index) +{ + int i, x, y, k, ret = 0; + int border_x[] = {-1, 0, 1, 1, 1, 0, -1, -1}; + int border_y[] = {-1, -1, -1, 0, 1, 1, 1, 0}; + s32 *p_comb = frame_buf; + + if (ipio_strcmp(tItems[index].desp, "open test(integration)") == 0) { + for (i = 0; i < core_mp.frame_len; i++) + tItems[index].buf[i] = p_comb[i]; + } else if (ipio_strcmp(tItems[index].desp, "open test(cap)") == 0) { + /* + * Each result is getting from a 3 by 3 grid depending on where the centre location is. + * So if the centre is at corner, the number of node grabbed from a grid will be different. + */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int sum = 0, avg = 0, count = 0; + int shift = y * core_mp.xch_len; + int centre = p_comb[shift + x]; + + for (k = 0; k < 8; k++) { + if (((y + border_y[k] >= 0) && (y + border_y[k] < core_mp.ych_len)) && + ((x + border_x[k] >= 0) && (x + border_x[k] < core_mp.xch_len))) { + count++; + sum += p_comb[(y + border_y[k]) * core_mp.xch_len + (x + border_x[k])]; + } + } + + avg = (sum + centre) / (count + 1); /* plus 1 because of centre */ + tItems[index].buf[shift + x] = (centre * 100) / avg; + } + } + } + return ret; +} + +static void run_tx_rx_delta_test(int index) +{ + int x, y; + s32 *p_comb = frame_buf; + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int shift = y * core_mp.xch_len; + + /* Tx Delta */ + if (y != (core_mp.ych_len - 1)) + core_mp.tx_delta_buf[shift + x] = Mathabs(p_comb[shift + x] - p_comb[(shift + 1) + x]); + + /* Rx Delta */ + if (x != (core_mp.xch_len - 1)) + core_mp.rx_delta_buf[shift + x] = Mathabs(p_comb[shift + x] - p_comb[shift + (x + 1)]); + } + } +} + +static char *get_date_time_str(void) +{ + static char time_data_buf[128] = { 0 }; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + struct timespec now_time; + struct rtc_time rtc_now_time; + + getnstimeofday(&now_time); + rtc_time_to_tm(now_time.tv_sec, &rtc_now_time); + snprintf(time_data_buf, sizeof(time_data_buf), "%04d%02d%02d-%02d%02d%02d", + (rtc_now_time.tm_year + 1900), rtc_now_time.tm_mon + 1, + rtc_now_time.tm_mday, rtc_now_time.tm_hour, rtc_now_time.tm_min, + rtc_now_time.tm_sec); +#else + struct tm tm; + time64_to_tm(ktime_get_real_seconds(), 0, &tm); + + snprintf(time_data_buf, sizeof(time_data_buf), "%04d%02d%02d-%02d%02d%02d", + (int)(tm.tm_year + 1900), tm.tm_mon + 1, + tm.tm_mday, tm.tm_hour, tm.tm_min, + tm.tm_sec); +#endif + + return time_data_buf; +} + +static void mp_print_csv_header(char *csv, int *csv_len, int *csv_line, int file_size) +{ + int i, seq, tmp_len = *csv_len, tmp_line = *csv_line; + + /* header must has 19 line*/ + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "==============================================================================\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "ILITek C-TP Utility V%s %x : Driver Sensor Test\n", DRIVER_VERSION, core_mp.chip_pid); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Confidentiality Notice:\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Any information of this tool is confidential and privileged.\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "@ ILI TECHNOLOGY CORP. All Rights Reserved.\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "==============================================================================\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Firmware Version ,0x%x\n", core_mp.fw_ver); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Panel information ,XCH=%d, YCH=%d\n", core_mp.xch_len, core_mp.ych_len); + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "INI Release Version ,%s\n", core_mp.ini_ver); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "INI Release Date ,%s\n", core_mp.ini_date); + tmp_line++; + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Test Item:\n"); + tmp_line++; + + for (seq = 0; seq < ri.count; seq++) { + i = ri.index[seq]; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), " ---%s\n", tItems[i].desp); + tmp_line++; + } + + while (tmp_line < 19) { + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "\n"); + tmp_line++; + } + + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "==============================================================================\n"); + + *csv_len = tmp_len; + *csv_line = tmp_line; +} + +static void mp_print_csv_tail(char *csv, int *csv_len, int file_size) +{ + int i, seq, tmp_len = *csv_len; + + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "==============================================================================\n"); + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Result_Summary \n"); + + for (seq = 0; seq < ri.count; seq++) { + i = ri.index[seq]; + if (tItems[i].item_result == MP_DATA_PASS) + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), " {%s} ,OK\n", tItems[i].desp); + else + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), " {%s} ,NG\n", tItems[i].desp); + } + + *csv_len = tmp_len; +} + +static void mp_print_csv_cdc_cmd(char *csv, int *csv_len, int index, int file_size) +{ + int i, slen = 0, tmp_len = *csv_len, size; + char str[128] = {0}; + char *open_sp_cmd[] = {"open dac", "open raw1", "open raw2", "open raw3"}; + char *open_c_cmd[] = {"open cap1 dac", "open cap1 raw"}; + char *name = tItems[index].desp; + + if (ipio_strcmp(name, "open test(integration)_sp") == 0) { + size = ARRAY_SIZE(open_sp_cmd); + for (i = 0; i < size; i++) { + slen = parser_get_int_data("pv5_4 command", open_sp_cmd[i], str, sizeof(str)); + if (slen < 0) + ILI_ERR("Failed to get CDC command %s from ini\n", open_sp_cmd[i]); + else + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "%s = ,%s\n", open_sp_cmd[i], str); + } + } else if (ipio_strcmp(name, "open test_c") == 0) { + size = ARRAY_SIZE(open_c_cmd); + for (i = 0; i < size; i++) { + slen = parser_get_int_data("pv5_4 command", open_c_cmd[i], str, sizeof(str)); + if (slen < 0) + ILI_ERR("Failed to get CDC command %s from ini\n", open_sp_cmd[i]); + else + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "%s = ,%s\n", open_c_cmd[i], str); + } + } else { + slen = parser_get_int_data("pv5_4 command", name, str, sizeof(str)); + if (slen < 0) + ILI_ERR("Failed to get CDC command %s from ini\n", name); + else + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "CDC command = ,%s\n", str); + + /* Print short parameters */ + if (ipio_strcmp(name, "short test") == 0) + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Variation = ,%d\n", short_para.variation); + + /* Print td second command */ + if (core_mp.td_retry && (ipio_strcmp(name, "peak to peak_td (lcm off)") == 0)) { + name = "peak to peak_td (lcm off)_2"; + parser_get_int_data("pv5_4 command", name, str, sizeof(str)); + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "CDC command 2 = ,%s\n", str); + } + } + *csv_len = tmp_len; +} + +static void mp_compare_cdc_show_result(int index, s32 *tmp, char *csv, + int *csv_len, int type, s32 *max_ts, + s32 *min_ts, const char *desp, int file_zise) +{ + int x, y, tmp_len = *csv_len; + int mp_result = MP_DATA_PASS; + + if (ERR_ALLOC_MEM(tmp)) { + ILI_ERR("The data of test item is null (%p)\n", tmp); + mp_result = -EMP_INVAL; + goto out; + } + + /* print X raw only */ + for (x = 0; x < core_mp.xch_len; x++) { + if (x == 0) { + DUMP("\n %s ", desp); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "\n %s ,", desp); + } + DUMP(" X_%d ,", (x+1)); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " X_%d ,", (x+1)); + } + + DUMP("\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "\n"); + + for (y = 0; y < core_mp.ych_len; y++) { + DUMP(" Y_%d ,", (y+1)); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " Y_%d ,", (y+1)); + + for (x = 0; x < core_mp.xch_len; x++) { + int shift = y * core_mp.xch_len + x; + + /* In Short teset, we only identify if its value is low than min threshold. */ + if (tItems[index].catalog == SHORT_TEST) { + if (tmp[shift] < min_ts[shift]) { + DUMP(" #%7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "#%7d,", tmp[shift]); + mp_result = MP_DATA_FAIL; + } else { + DUMP(" %7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " %7d, ", tmp[shift]); + } + continue; + } + + if ((tmp[shift] <= max_ts[shift] && tmp[shift] >= min_ts[shift]) || (type != TYPE_JUGE)) { + if ((tmp[shift] == INT_MAX || tmp[shift] == INT_MIN) && (type == TYPE_BENCHMARK)) { + DUMP("%s", "BYPASS,"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "BYPASS,"); + } else { + DUMP(" %7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " %7d, ", tmp[shift]); + } + } else { + if (tmp[shift] > max_ts[shift]) { + DUMP(" *%7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "*%7d,", tmp[shift]); + } else { + DUMP(" #%7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "#%7d,", tmp[shift]); + } + mp_result = MP_DATA_FAIL; + } + } + DUMP("\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "\n"); + } + +out: + if (type == TYPE_JUGE) { + if (mp_result == MP_DATA_PASS) { + pr_info("\n Result : PASS\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "Result : PASS\n"); + } else { + pr_info("\n Result : FAIL\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "Result : FAIL\n"); + } + } + *csv_len = tmp_len; +} + +#define ABS(a, b) ((a > b) ? (a - b) : (b - a)) +#define ADDR(x, y) ((y * core_mp.xch_len) + (x)) + +static int compare_charge(s32 *charge_rate, int x, int y, s32 *inNodeType, + int Charge_AA, int Charge_Border, int Charge_Notch) +{ + int OpenThreadhold, tempY, tempX, ret, k; + int sx[8] = {-1, 0, 1, -1, 1, -1, 0, 1}; + int sy[8] = {-1, -1, -1, 0, 0, 1, 1, 1}; + + ret = charge_rate[ADDR(x, y)]; + + /*Setting Threadhold from node type */ + if (charge_rate[ADDR(x, y)] == 0) + return ret; + else if ((inNodeType[ADDR(x, y)] & AA_Area) == AA_Area) + OpenThreadhold = Charge_AA; + else if ((inNodeType[ADDR(x, y)] & Border_Area) == Border_Area) + OpenThreadhold = Charge_Border; + else if ((inNodeType[ADDR(x, y)] & Notch) == Notch) + OpenThreadhold = Charge_Notch; + else + return ret; + + /* compare carge rate with 3*3 node */ + /* by pass => 1.no compare 2.corner 3.Skip_Micro 4.full open fail node */ + for (k = 0; k < 8; k++) { + tempX = x + sx[k]; + tempY = y + sy[k]; + + /*out of range */ + if ((tempX < 0) || (tempX >= core_mp.xch_len) || (tempY < 0) || (tempY >= core_mp.ych_len)) + continue; + + if ((inNodeType[ADDR(tempX, tempY)] == NO_COMPARE) || ((inNodeType[ADDR(tempX, tempY)] & Round_Corner) == Round_Corner) || + ((inNodeType[ADDR(tempX, tempY)] & Skip_Micro) == Skip_Micro) || charge_rate[ADDR(tempX, tempY)] == 0) + continue; + + if ((charge_rate[ADDR(tempX, tempY)] - charge_rate[ADDR(x, y)]) > OpenThreadhold) + return OpenThreadhold; + } + return ret; +} + +static int full_open_rate_compare(s32 *full_open, s32 *cbk, int x, int y, s32 inNodeType, int full_open_rate) +{ + int ret = true; + + if ((inNodeType == NO_COMPARE) || ((inNodeType & Round_Corner) == Round_Corner)) + return true; + + if (full_open[ADDR(x, y)] < (cbk[ADDR(x, y)] * full_open_rate / 100)) + ret = false; + + return ret; +} + +static s32 open_sp_formula(int dac, int raw, int tvch, int tvcl) +{ + s32 ret = 0; + u16 id = core_mp.chip_id; + + if (id == ILI9881_CHIP) { + ret = ((dac * 10000 * 161 / 100) - (16384 / 2 - raw) * 20000 * 7 / 16384 * 36 / 10) / (tvch - tvcl) / 2; + } else { + ret = ((dac * 10000 * 131 / 100) - (16384 / 2 - raw) * 20000 * 7 / 16384 * 36 / 10) / (tvch - tvcl) / 2; + } + return ret; +} + +static s32 open_c_formula(int inCap1DAC, int inCap1Raw, int accuracy) +{ + s32 inCap1Value = 0; + + u16 id = core_mp.chip_id; + u8 type = core_mp.chip_type; + + int inFoutRange = 16384; + int inFoutRange_half = 8192; + int inVadc_range = 36; + int inVbk = 39; + int inCbk_step = open_para.cbk_step;/* from mp.ini */ + int inCint = open_para.cint;/* from mp.ini */ + int inVdrv = open_para.tvch - open_para.tvcl;/* from mp.ini */ + int inGain = open_para.gain;/* from mp.ini */ + int inMagnification = 10; + int inPartDac = 0; + int inPartRaw = 0; + + if ((inCbk_step == 0) || (inCint == 0)) { + if (id == ILI9881_CHIP) { + if ((type == ILI_N) || (type == ILI_O)){ + inCbk_step = 32; + inCint = 70; + } + } else if (id == ILI9882_CHIP) { + if (type == ILI_N) { + inCbk_step = 42; + inCint = 70; + } else if (type == ILI_H) { + inCbk_step = 42; + inCint = 69; + } + } else if (id == ILI7807_CHIP){ + if (type == ILI_Q) { + inCbk_step = 28; + inCint= 70; + } else if (type == ILI_S) { + inCbk_step = 38; + inCint= 66; + inVbk = 42; + inVdrv = inVdrv / 10; + inGain = inGain / 10; + + } else if (type == ILI_V) { + inCbk_step = 28; + inCint= 70; + } + } + } + + inPartDac = (inCap1DAC * inCbk_step * inVbk / 2); + inPartRaw = ((inCap1Raw - inFoutRange_half) * inVadc_range * inCint * 10 / inFoutRange); + + if (accuracy) { + inCap1Value = ((inPartDac + inPartRaw) * 10) / inVdrv / inMagnification / inGain; + } else { + inCap1Value = (inPartDac + inPartRaw) / inVdrv / inMagnification / inGain; + } + + return inCap1Value; +} + +static void allnode_open_cdc_result(int index, int *buf, int *dac, int *raw) +{ + int i; + char *desp = tItems[index].desp; + char str[32] = {0}; + int accuracy = 0; + + if (ipio_strcmp(desp, "open test(integration)_sp") == 0) { + for (i = 0; i < core_mp.frame_len; i++) + buf[i] = open_sp_formula(dac[i], raw[i], open_para.tvch, open_para.tvcl); + } else if (ipio_strcmp(desp, "open test_c") == 0) { + + if (parser_get_int_data(desp, "accuracy", str, sizeof(str)) > 0) + accuracy = ili_katoi(str); + + for (i = 0; i < core_mp.frame_len; i++) + buf[i] = open_c_formula(dac[i], raw[i], accuracy); + } +} + +static int codeToOhm(s32* ohm, s32* Code, u16 *v_tdf, u16 *h_tdf) +{ + u16 id = core_mp.chip_id; + u8 type = core_mp.chip_type; + + int inTVCH = short_para.tvch; + int inTVCL = short_para.tvcl; + int inVariation = short_para.variation; + int inTDF1 = 0; + int inTDF2 = 0; + int inCint = short_para.cint; + int inRinternal = short_para.rinternal; + s32 temp = 0; + int j = 0; + + if (core_mp.isLongV) { + inTDF1 = *v_tdf; + inTDF2 = *(v_tdf + 1); + } else { + inTDF1 = *h_tdf; + inTDF2 = *(h_tdf + 1); + } + + if (inVariation == 0) { + inVariation = 100; + } + + if ((inCint == 0) || (inRinternal == 0)) { + if (id == ILI9881_CHIP) { + if ((type == ILI_N) || (type == ILI_O)){ + inRinternal = 1915; + inCint = 70; + } + } else if (id == ILI9882_CHIP) { + if (type == ILI_N) { + inRinternal = 1354; + inCint = 70; + } else if (type == ILI_H) { + inRinternal = 1354; + inCint = 69; + } + } else if (id == ILI7807_CHIP){ + if (type == ILI_Q) { + inRinternal = 1500; + inCint= 70; + } else if (type == ILI_S) { + inRinternal = 1500; + inCint= 66; + inTVCH = inTVCH/10; + inTVCL = inTVCL/10; + } else if (type == ILI_V) { + inRinternal = 1500; + inCint= 70; + } + } + } + for (j = 0; j < core_mp.frame_len; j++) { + if (Code[j] == 0) { + ILI_ERR("code is invalid\n"); + } else { + temp = ((inTVCH - inTVCL) * inVariation * (inTDF1 - inTDF2) * (1 << 12) / (9 * Code[j] * inCint)) * 1000; + temp = (temp - inRinternal) / 1000; + } + ohm[j] = temp; + } + + /* Unit = M Ohm */ + return temp; + +} + +static int short_test(int index, int frame_index) +{ + u32 pid = core_mp.chip_pid >> 8; + int j = 0, ret = 0; + u16 v_tdf[2] = {0}; + u16 h_tdf[2] = {0}; + char str[32] = {0}; + + v_tdf[0] = tItems[index].v_tdf_1; + v_tdf[1] = tItems[index].v_tdf_2; + h_tdf[0] = tItems[index].h_tdf_1; + h_tdf[1] = tItems[index].h_tdf_2; + + parser_get_int_data("short test", "tvch", str, sizeof(str)); + short_para.tvch = ili_katoi(str); + + parser_get_int_data("short test", "tvcl", str, sizeof(str)); + short_para.tvcl = ili_katoi(str); + + parser_get_int_data("short test", "variation", str, sizeof(str)); + short_para.variation = ili_katoi(str); + + parser_get_int_data("short test", "cint", str, sizeof(str)); + short_para.cint = ili_katoi(str); + + parser_get_int_data("short test", "rinternal", str, sizeof(str)); + short_para.rinternal = ili_katoi(str); + + /* 9881N, 9881O, 7807Q, 7807S not support cbk_step and cint read from mp.ini */ + if ((pid != 0x988117) && (pid != 0x988118) && (pid != 0x78071A) && (pid != 0x78071C)) { + if ((short_para.cint == 0) || (short_para.rinternal == 0)) { + ILI_ERR("Failed to get short parameter"); + core_mp.lost_parameter = true; + return -1; + } + } + + ILI_INFO("TVCH = %d, TVCL = %d, Variation = %d, V_TDF1 = %d, V_TDF2 = %d, H_TDF1 = %d, H_TDF2 = %d, Cint = %d, Rinternal = %d\n" + , short_para.tvch, short_para.tvcl, short_para.variation, tItems[index].v_tdf_1, tItems[index].v_tdf_2, tItems[index].h_tdf_1, tItems[index].h_tdf_2, short_para.cint, short_para.rinternal); + + if (core_mp.protocol_ver >= PROTOCOL_VER_540) { + /* Calculate code to ohm and save to tItems[index].buf */ + ili_dump_data(frame_buf, 10, core_mp.frame_len, core_mp.xch_len, "Short Raw 1"); + codeToOhm(&tItems[index].buf[frame_index * core_mp.frame_len], frame_buf, v_tdf, h_tdf); + ili_dump_data(&tItems[index].buf[frame_index * core_mp.frame_len], 10, core_mp.frame_len, core_mp.xch_len, "Short Ohm 1"); + } else { + for (j = 0; j < core_mp.frame_len; j++) + tItems[index].buf[frame_index * core_mp.frame_len + j] = frame_buf[j]; + } + + return ret; +} + +static int allnode_key_cdc_data(int index) +{ + int i, ret = 0, len = 0; + int inDACp = 0, inDACn = 0; + u8 cmd[3] = {0}; + u8 *ori = NULL; + + len = core_mp.key_len * 2; + + ILI_DBG("Read key's length = %d\n", len); + ILI_DBG("core_mp.key_len = %d\n", core_mp.key_len); + + if (len <= 0) { + ILI_ERR("Length is invalid\n"); + ret = -1; + goto out; + } + + /* CDC init */ + cmd[0] = P5_X_SET_CDC_INIT; + cmd[1] = tItems[index].cmd; + cmd[2] = 0; + + // /* Allocate a buffer for the original */ + ori = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(ori)) { + ILI_ERR("Failed to allocate ori mem (%ld)\n", PTR_ERR(ori)); + ret = -1; + goto out; + } + ret = ilits->wrapper(cmd, sizeof(cmd), ori, len, ON, ON); + if (ret < 0) { + ILI_ERR(" fail to get cdc data\n"); + goto out; + } + ili_dump_data(ori, 8, len, 0, "Key CDC original"); + + if (key_buf == NULL) { + key_buf = kcalloc(core_mp.key_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(key_buf)) { + ILI_ERR("Failed to allocate FrameBuffer mem (%ld)\n", PTR_ERR(key_buf)); + goto out; + } + } else { + memset(key_buf, 0x0, core_mp.key_len); + } + + /* Convert original data to the physical one in each node */ + for (i = 0; i < core_mp.frame_len; i++) { + if (tItems[index].cmd == CMD_KEY_DAC) { + /* DAC - P */ + if (((ori[(2 * i) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACp = 0 - (int)(ori[(2 * i) + 1] & 0x7F); + } else { + inDACp = ori[(2 * i) + 1] & 0x7F; + } + + /* DAC - N */ + if (((ori[(1 + (2 * i)) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACn = 0 - (int)(ori[(1 + (2 * i)) + 1] & 0x7F); + } else { + inDACn = ori[(1 + (2 * i)) + 1] & 0x7F; + } + + key_buf[i] = (inDACp + inDACn) / 2; + } + } + ili_dump_data(key_buf, 32, core_mp.frame_len, core_mp.xch_len, "Key CDC combined data"); + +out: + ipio_kfree((void **)&ori); + return ret; +} + +static int mp_cdc_get_pv5_4_command(u8 *cmd, int len, int index) +{ + int slen = 0; + char str[128] = {0}; + char *key = tItems[index].desp; + + if (core_mp.td_retry && (ipio_strcmp(key, "peak to peak_td (lcm off)") == 0)) + key = "peak to peak_td (lcm off)_2"; + + slen = parser_get_int_data("pv5_4 command", key, str, sizeof(str)); + if (slen < 0) + return -1; + + if (parser_get_u8_array(str, cmd, 16, len) < 0) + return -1; + + return 0; +} + +static int mp_cdc_init_cmd_common(u8 *cmd, int len, int index) +{ + int ret = 0; + + if (core_mp.protocol_ver >= PROTOCOL_VER_540) { + core_mp.cdc_len = 15; + return mp_cdc_get_pv5_4_command(cmd, len, index); + } + + cmd[0] = P5_X_SET_CDC_INIT; + cmd[1] = tItems[index].cmd; + cmd[2] = 0; + + core_mp.cdc_len = 3; + + if (ipio_strcmp(tItems[index].desp, "open test(integration)") == 0) + cmd[2] = 0x2; + if (ipio_strcmp(tItems[index].desp, "open test(cap)") == 0) + cmd[2] = 0x3; + + if (tItems[index].catalog == PEAK_TO_PEAK_TEST) { + cmd[2] = ((tItems[index].frame_count & 0xff00) >> 8); + cmd[3] = tItems[index].frame_count & 0xff; + cmd[4] = 0; + + core_mp.cdc_len = 5; + + if (ipio_strcmp(tItems[index].desp, "noise peak to peak(cut panel)") == 0) + cmd[4] = 0x1; + + ILI_DBG("P2P CMD: %d,%d,%d,%d,%d\n", + cmd[0], cmd[1], cmd[2], cmd[3], cmd[4]); + } + + return ret; +} + +static int allnode_open_cdc_data(int mode, int *buf) +{ + int i = 0, ret = 0, len = 0; + int inDACp = 0, inDACn = 0; + u8 cmd[15] = {0}; + u8 *ori = NULL; + char str[128] = {0}; + char *key[] = {"open dac", "open raw1", "open raw2", "open raw3", + "open cap1 dac", "open cap1 raw"}; + + /* Multipling by 2 is due to the 16 bit in each node */ + len = (core_mp.xch_len * core_mp.ych_len * 2) + 2; + + ILI_DBG("Read X/Y Channel length = %d, mode = %d\n", len, mode); + + if (len <= 2) { + ILI_ERR("Length is invalid\n"); + ret = -EMP_INVAL; + goto out; + } + + /* CDC init. Read command from ini file */ + if (parser_get_int_data("pv5_4 command", key[mode], str, sizeof(str)) < 0) { + ILI_ERR("Failed to parse PV54 command, ret = %d\n", ret); + ret = -EMP_PARSE; + goto out; + } + + parser_get_u8_array(str, cmd, 16, sizeof(cmd)); + + ili_dump_data(cmd, 8, sizeof(cmd), 0, "Open SP command"); + + /* Allocate a buffer for the original */ + ori = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(ori)) { + ILI_ERR("Failed to allocate ori, (%ld)\n", PTR_ERR(ori)); + ret = -EMP_NOMEM; + goto out; + } + + /* Get original frame(cdc) data */ + if (ilits->wrapper(cmd, core_mp.cdc_len, ori, len, ON, ON) < 0) { + ILI_ERR("Failed to get cdc data\n"); + ret = -EMP_GET_CDC; + goto out; + } + + ili_dump_data(ori, 8, len, 0, "Open SP CDC original"); + + /* Convert original data to the physical one in each node */ + for (i = 0; i < core_mp.frame_len; i++) { + if ((mode == 0) || (mode == 4)) { + /* DAC - P */ + if (((ori[(2 * i) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACp = 0 - (int)(ori[(2 * i) + 1] & 0x7F); + } else { + inDACp = ori[(2 * i) + 1] & 0x7F; + } + + /* DAC - N */ + if (((ori[(1 + (2 * i)) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACn = 0 - (int)(ori[(1 + (2 * i)) + 1] & 0x7F); + } else { + inDACn = ori[(1 + (2 * i)) + 1] & 0x7F; + } + + buf[i] = inDACp + inDACn; + } else { + /* H byte + L byte */ + s32 tmp = (ori[(2 * i) + 1] << 8) + ori[(1 + (2 * i)) + 1]; + if ((tmp & 0x8000) == 0x8000) + buf[i] = tmp - 65536; + else + buf[i] = tmp; + + } + } + ili_dump_data(buf, 10, core_mp.frame_len, core_mp.xch_len, "Open SP CDC combined"); +out: + + ipio_kfree((void **)&ori); + return ret; +} + +static int allnode_peak_to_peak_cdc_data(int index) +{ + int i, k, ret = 0, len = 0, rd_frame_num = 1, cmd_len = 0; + u8 cmd[15] = {0}; + u8 *ori = NULL; + + /* Multipling by 2 is due to the 16 bit in each node */ + len = (core_mp.frame_len * 2) + 2; + + ILI_DBG("Read X/Y Channel length = %d\n", len); + + if (len <= 2) { + ILI_ERR("Length is invalid\n"); + ret = -EMP_INVAL; + goto out; + } + + memset(cmd, 0xFF, sizeof(cmd)); + + /* Allocate a buffer for the original */ + ori = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(ori)) { + ILI_ERR("Failed to allocate ori, (%ld)\n", PTR_ERR(ori)); + ret = -EMP_NOMEM; + goto out; + } + + if (tItems[index].bch_mrk_multi) { + rd_frame_num = tItems[index].bch_mrk_frm_num - 1; + } + + if (frm_buf == NULL) { + frm_buf = (s32 **)kzalloc(tItems[index].bch_mrk_frm_num * sizeof(s32 *), GFP_KERNEL); + if (ERR_ALLOC_MEM(frm_buf)) { + ILI_ERR("Failed to allocate frm_buf mem (%ld)\n", PTR_ERR(frm_buf)); + ret = -EMP_NOMEM; + goto out; + } + for (i = 0 ; i < tItems[index].bch_mrk_frm_num ; i++) { + frm_buf[i] = (s32*)kzalloc(core_mp.frame_len * sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frm_buf)) { + ILI_ERR("Failed to allocate frm_buf[%d] mem (%ld)\n", i, PTR_ERR(frm_buf)); + ret = -EMP_NOMEM; + goto out; + } + } + } + + /* CDC init */ + if (mp_cdc_init_cmd_common(cmd, sizeof(cmd), index) < 0) { + ILI_ERR("Failed to get cdc command\n"); + ret = -EMP_CMD; + goto out; + } + + ili_dump_data(cmd, 8, core_mp.cdc_len, 0, "Mutual CDC command"); + + for (k = 0; k < rd_frame_num; k++) { + if (k == 0) + cmd_len = core_mp.cdc_len; + else + cmd_len = 0; + memset(ori, 0, len); + /* Get original frame(cdc) data */ + if (ilits->wrapper(cmd, cmd_len, ori, len, ON, ON) < 0) { + ILI_ERR("Failed to get cdc data\n"); + ret = -EMP_GET_CDC; + goto out; + } + + ili_dump_data(ori, 8, len, 0, "Mutual CDC original"); + + /* Convert original data to the physical one in each node */ + for (i = 0; i < core_mp.frame_len; i++) { + /* H byte + L byte */ + s32 tmp = (ori[(2 * i) + 1] << 8) + ori[(1 + (2 * i)) + 1]; + + if ((tmp & 0x8000) == 0x8000) + frm_buf[k][i] = tmp - 65536; + else + frm_buf[k][i] = tmp; + + // multiple case frame3 = frame1 - frame2 + if (tItems[index].bch_mrk_multi && rd_frame_num == k + 1) + frm_buf[k+1][i] = frm_buf[k-1][i] - frm_buf[k][i]; + } + } + + if (tItems[index].bch_mrk_multi) { + ili_dump_data(frm_buf[0], 32, core_mp.frame_len, core_mp.xch_len, "Mutual CDC combined[0]/frame1"); + ili_dump_data(frm_buf[1], 32, core_mp.frame_len, core_mp.xch_len, "Mutual CDC combined[1]/frame2"); + ili_dump_data(frm_buf[2], 32, core_mp.frame_len, core_mp.xch_len, "Mutual CDC combined[2]/frame3"); + } else { + ili_dump_data(frm_buf[0], 32, core_mp.frame_len, core_mp.xch_len, "Mutual CDC combined[0]/frame1"); + } + +out: + ipio_kfree((void **)&ori); + return ret; +} + +static int allnode_mutual_cdc_data(int index) +{ + int i, ret = 0, len = 0; + int inDACp = 0, inDACn = 0; + u8 cmd[15] = {0}; + u8 *ori = NULL; + + /* Multipling by 2 is due to the 16 bit in each node */ + len = (core_mp.frame_len * 2) + 2; + + ILI_DBG("Read X/Y Channel length = %d\n", len); + + if (len <= 2) { + ILI_ERR("Length is invalid\n"); + ret = -EMP_INVAL; + goto out; + } + + memset(cmd, 0xFF, sizeof(cmd)); + + /* CDC init */ + if (mp_cdc_init_cmd_common(cmd, sizeof(cmd), index) < 0) { + ILI_ERR("Failed to get cdc command\n"); + ret = -EMP_CMD; + goto out; + } + + ili_dump_data(cmd, 8, core_mp.cdc_len, 0, "Mutual CDC command"); + + /* Allocate a buffer for the original */ + ori = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(ori)) { + ILI_ERR("Failed to allocate ori, (%ld)\n", PTR_ERR(ori)); + ret = -EMP_NOMEM; + goto out; + } + + /* Get original frame(cdc) data */ + if (ilits->wrapper(cmd, core_mp.cdc_len, ori, len, ON, ON) < 0) { + ILI_ERR("Failed to get cdc data\n"); + ret = -EMP_GET_CDC; + goto out; + } + + ili_dump_data(ori, 8, len, 0, "Mutual CDC original"); + + if (frame_buf == NULL) { + frame_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frame_buf)) { + ILI_ERR("Failed to allocate FrameBuffer mem (%ld)\n", PTR_ERR(frame_buf)); + ret = -EMP_NOMEM; + goto out; + } + } else { + memset(frame_buf, 0x0, core_mp.frame_len); + } + + /* Convert original data to the physical one in each node */ + for (i = 0; i < core_mp.frame_len; i++) { + if (ipio_strcmp(tItems[index].desp, "calibration data(dac)") == 0) { + /* DAC - P */ + if (((ori[(2 * i) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACp = 0 - (int)(ori[(2 * i) + 1] & 0x7F); + } else { + inDACp = ori[(2 * i) + 1] & 0x7F; + } + + /* DAC - N */ + if (((ori[(1 + (2 * i)) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACn = 0 - (int)(ori[(1 + (2 * i)) + 1] & 0x7F); + } else { + inDACn = ori[(1 + (2 * i)) + 1] & 0x7F; + } + + frame_buf[i] = (inDACp + inDACn) / 2; + } else { + /* H byte + L byte */ + s32 tmp = (ori[(2 * i) + 1] << 8) + ori[(1 + (2 * i)) + 1]; + + if ((tmp & 0x8000) == 0x8000) + frame_buf[i] = tmp - 65536; + else + frame_buf[i] = tmp; + + if (ipio_strcmp(tItems[index].desp, "raw data(no bk)") == 0 || + ipio_strcmp(tItems[index].desp, "raw data(no bk) (lcm off)") == 0) { + frame_buf[i] -= core_mp.no_bk_shift; + } + } + } + + ili_dump_data(frame_buf, 32, core_mp.frame_len, core_mp.xch_len, "Mutual CDC combined"); + +out: + ipio_kfree((void **)&ori); + return ret; +} + +static void compare_MaxMin_result(int index, s32 *data) +{ + int x, y; + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int shift = y * core_mp.xch_len; + + if (tItems[index].catalog == UNTOUCH_P2P) + return; + else if (tItems[index].catalog == TX_RX_DELTA) { + /* Tx max/min comparison */ + if (core_mp.tx_delta_buf[shift + x] < data[shift + x]) + core_mp.tx_max_buf[shift + x] = data[shift + x]; + + if (core_mp.tx_delta_buf[shift + x] > data[shift + x]) + core_mp.tx_min_buf[shift + x] = data[shift + x]; + + /* Rx max/min comparison */ + if (core_mp.rx_delta_buf[shift + x] < data[shift + x]) + core_mp.rx_max_buf[shift + x] = data[shift + x]; + + if (core_mp.rx_delta_buf[shift + x] > data[shift + x]) + core_mp.rx_min_buf[shift + x] = data[shift + x]; + } else { + if (tItems[index].max_buf[shift + x] < data[shift + x]) + tItems[index].max_buf[shift + x] = data[shift + x]; + + if (tItems[index].min_buf[shift + x] > data[shift + x]) + tItems[index].min_buf[shift + x] = data[shift + x]; + } + } + } +} + +static int create_mp_test_frame_buffer(int index, int frame_count) +{ + int i; + + ILI_DBG("Create MP frame buffers (index = %d), count = %d\n", + index, frame_count); + + if (tItems[index].catalog == TX_RX_DELTA) { + if (core_mp.tx_delta_buf == NULL) { + core_mp.tx_delta_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.tx_delta_buf)) { + ILI_ERR("Failed to allocate tx_delta_buf mem\n"); + ipio_kfree((void **)&core_mp.tx_delta_buf); + return -ENOMEM; + } + } + + if (core_mp.rx_delta_buf == NULL) { + core_mp.rx_delta_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.rx_delta_buf)) { + ILI_ERR("Failed to allocate rx_delta_buf mem\n"); + ipio_kfree((void **)&core_mp.rx_delta_buf); + return -ENOMEM; + } + } + + if (core_mp.tx_max_buf == NULL) { + core_mp.tx_max_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.tx_max_buf)) { + ILI_ERR("Failed to allocate tx_max_buf mem\n"); + ipio_kfree((void **)&core_mp.tx_max_buf); + return -ENOMEM; + } + } + + if (core_mp.tx_min_buf == NULL) { + core_mp.tx_min_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.tx_min_buf)) { + ILI_ERR("Failed to allocate tx_min_buf mem\n"); + ipio_kfree((void **)&core_mp.tx_min_buf); + return -ENOMEM; + } + } + + if (core_mp.rx_max_buf == NULL) { + core_mp.rx_max_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.rx_max_buf)) { + ILI_ERR("Failed to allocate rx_max_buf mem\n"); + ipio_kfree((void **)&core_mp.rx_max_buf); + return -ENOMEM; + } + } + + if (core_mp.rx_min_buf == NULL) { + core_mp.rx_min_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.rx_min_buf)) { + ILI_ERR("Failed to allocate rx_min_buf mem\n"); + ipio_kfree((void **)&core_mp.rx_min_buf); + return -ENOMEM; + } + } + } else { + if (tItems[index].buf == NULL) { + tItems[index].buf = vmalloc(frame_count * core_mp.frame_len * sizeof(s32)); + if (ERR_ALLOC_MEM(tItems[index].buf)) { + ILI_ERR("Failed to allocate buf mem\n"); + ipio_kfree((void **)&tItems[index].buf); + return -ENOMEM; + } + } + + if (tItems[index].result_buf == NULL) { + tItems[index].result_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].result_buf)) { + ILI_ERR("Failed to allocate result_buf mem\n"); + ipio_kfree((void **)&tItems[index].result_buf); + return -ENOMEM; + } + } + + if (tItems[index].max_buf == NULL) { + tItems[index].max_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].max_buf)) { + ILI_ERR("Failed to allocate max_buf mem\n"); + ipio_kfree((void **)&tItems[index].max_buf); + return -ENOMEM; + } + } + + if (tItems[index].min_buf == NULL) { + tItems[index].min_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].min_buf)) { + ILI_ERR("Failed to allocate min_buf mem\n"); + ipio_kfree((void **)&tItems[index].min_buf); + return -ENOMEM; + } + } + + if (tItems[index].spec_option == BENCHMARK) { + if (tItems[index].bch_mrk_max == NULL) { + tItems[index].bch_mrk_max = (s32 **)kzalloc(tItems[index].bch_mrk_frm_num * sizeof(s32 *), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bch_mrk_max)) { + ILI_ERR("Failed to allocate bch_mrk_max mem\n"); + ipio_kfree((void **)&tItems[index].bch_mrk_max); + return -ENOMEM; + } + + for (i = 0; i < tItems[index].bch_mrk_frm_num; i++) { + tItems[index].bch_mrk_max[i] = (s32 *)kzalloc(core_mp.frame_len * sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bch_mrk_max[i])) { + ILI_ERR("Failed to allocate bch_mrk_max[%d] mem\n", i); + ipio_kfree((void **)&tItems[index].bch_mrk_max[i]); + return -ENOMEM; + } + } + } + + if (tItems[index].bch_mrk_min == NULL) { + tItems[index].bch_mrk_min = (s32 **)kzalloc(tItems[index].bch_mrk_frm_num * sizeof(s32 *), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bch_mrk_min)) { + ILI_ERR("Failed to allocate bch_mrk_min mem\n"); + ipio_kfree((void **)&tItems[index].bch_mrk_min); + return -ENOMEM; + } + for (i = 0; i < tItems[index].bch_mrk_frm_num; i++) { + tItems[index].bch_mrk_min[i] = (s32 *)kzalloc(core_mp.frame_len * sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bch_mrk_min[i])) { + ILI_ERR("Failed to allocate bch_mrk_min[%d] mem\n", i); + ipio_kfree((void **)&tItems[index].bch_mrk_min[i]); + return -ENOMEM; + } + } + } + + if (tItems[index].bench_mark_max == NULL) { + tItems[index].bench_mark_max = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bench_mark_max)) { + ILI_ERR("Failed to allocate bench_mark_max mem\n"); + ipio_kfree((void **)&tItems[index].bench_mark_max); + return -ENOMEM; + } + } + if (tItems[index].bench_mark_min == NULL) { + tItems[index].bench_mark_min = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bench_mark_min)) { + ILI_ERR("Failed to allocate bench_mark_min mem\n"); + ipio_kfree((void **)&tItems[index].bench_mark_min); + return -ENOMEM; + } + } + } + } + return 0; +} + +static int pin_test(int index) +{ + int ret = 0; + u8 cmd[5] = {0}; + + ILI_INFO("PIN test start"); + ILI_INFO("test_int_pin = 0x%x\n", tItems[index].test_int_pin); + ILI_INFO("int_pulse_test = 0x%x\n", tItems[index].int_pulse_test); + ILI_INFO("delay_time = 0x%x\n", tItems[index].delay_time); + + if (tItems[index].test_int_pin == ENABLE) { + /* test HIGH level*/ + cmd[0] = tItems[index].cmd; + cmd[1] = 0x1; + ret = ilits->wrapper(cmd, 2, NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write command failed\n"); + ret = -EMP_CMD; + goto out; + } + + ret = ilits->detect_int_stat(false); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + + /* test LOW level*/ + cmd[1] = 0x0; + ret = ilits->wrapper(cmd, 2, NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write command failed\n"); + ret = -EMP_CMD; + goto out; + } + + ret = ilits->detect_int_stat(false); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + } + + if (tItems[index].int_pulse_test == ENABLE) { + + ILI_INFO("MP IRQ Rising Trigger Test\n"); + cmd[1] = 0x2; + cmd[2] = tItems[index].delay_time; + + ili_irq_unregister(); + ret = ili_irq_register(IRQF_TRIGGER_RISING); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + + atomic_set(&ilits->cmd_int_check, ENABLE); + ret = ilits->wrapper(cmd, 3, NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write command failed\n"); + ret = -EMP_CMD; + goto out; + } + + ret = ilits->detect_int_stat(false); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + + ILI_INFO("MP IRQ Falling Trigger Test\n"); + ili_irq_unregister(); + ret = ili_irq_register(IRQF_TRIGGER_FALLING); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + + atomic_set(&ilits->cmd_int_check, ENABLE); + + ret = ilits->wrapper(cmd, 3, NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write command failed\n"); + ret = -EMP_CMD; + goto out; + } + + ret = ilits->detect_int_stat(false); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + } + + tItems[index].item_result = MP_DATA_PASS; + +out: + if (ret < 0) + tItems[index].item_result = MP_DATA_FAIL; + + ILI_INFO("Change to defualt IRQ trigger type\n"); + ili_irq_unregister(); + ili_irq_register(ilits->irq_tirgger_type); + atomic_set(&ilits->cmd_int_check, DISABLE); + + return ret; +} + +static int mutual_test(int index) +{ + int i = 0, j = 0, x = 0, y = 0, ret = 0, get_frame_cont = 1; + + ILI_DBG("index = %d, desp = %s, Frame Count = %d\n", + index, tItems[index].desp, tItems[index].frame_count); + + /* + * We assume that users who are calling the test forget to config frame count + * as 1, so we just help them to set it up. + */ + if (tItems[index].frame_count <= 0) { + ILI_ERR("Frame count is zero, which is at least set as 1\n"); + tItems[index].frame_count = 1; + } + + ret = create_mp_test_frame_buffer(index, tItems[index].frame_count); + if (ret < 0) { + ret = -EMP_NOMEM; + goto out; + } + + /* Init Max/Min buffer */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + if (tItems[i].catalog == TX_RX_DELTA) { + core_mp.tx_max_buf[y * core_mp.xch_len + x] = INT_MIN; + core_mp.rx_max_buf[y * core_mp.xch_len + x] = INT_MIN; + core_mp.tx_min_buf[y * core_mp.xch_len + x] = INT_MAX; + core_mp.rx_min_buf[y * core_mp.xch_len + x] = INT_MAX; + } else { + tItems[index].max_buf[y * core_mp.xch_len + x] = INT_MIN; + tItems[index].min_buf[y * core_mp.xch_len + x] = INT_MAX; + } + } + } + + if (tItems[index].catalog != PEAK_TO_PEAK_TEST) + get_frame_cont = tItems[index].frame_count; + + if (tItems[index].spec_option == BENCHMARK) { + parser_ini_benchmark(tItems[index].bench_mark_max, tItems[index].bench_mark_min, + tItems[index].type_option, tItems[index].desp, core_mp.frame_len, BENCHMARK_KEY_NAME); + dump_benchmark_data(tItems[index].bench_mark_max, tItems[index].bench_mark_min); + } + + for (i = 0; i < get_frame_cont; i++) { + ret = allnode_mutual_cdc_data(index); + if (ret < 0) { + ILI_ERR("Failed to initialise CDC data, %d\n", ret); + goto out; + } + switch (tItems[index].catalog) { + case PIXEL: + run_pixel_test(index); + break; + case UNTOUCH_P2P: + run_untouch_p2p_test(index); + break; + case OPEN_TEST: + run_open_test(index); + break; + case TX_RX_DELTA: + run_tx_rx_delta_test(index); + break; + case SHORT_TEST: + ret = short_test(index, i); + if (ret < 0) { + ret = -EMP_PARA_NULL; + goto out; + } + break; + default: + for (j = 0; j < core_mp.frame_len; j++) + tItems[index].buf[i * core_mp.frame_len + j] = frame_buf[j]; + break; + } + compare_MaxMin_result(index, &tItems[index].buf[i * core_mp.frame_len]); + } + +out: + return ret; +} + +static int peak_to_peak_test(int index) +{ + int i = 0, j = 0, x = 0, y = 0, ret = 0; + char benchmark_str[128] = {0}; + + ILI_DBG("index = %d, desp = %s bch_mrk_frm_num = %d\n" + , index, tItems[index].desp, tItems[index].bch_mrk_frm_num); + + ret = create_mp_test_frame_buffer(index, tItems[index].bch_mrk_frm_num); + if (ret < 0) { + ret = -EMP_NOMEM; + goto out; + } + + if (tItems[index].spec_option == BENCHMARK) { + for ( i = 0 ; i < tItems[index].bch_mrk_frm_num ; i++ ) { + if (tItems[index].bch_mrk_multi) + snprintf(benchmark_str, sizeof(benchmark_str), "%s%d", BENCHMARK_KEY_NAME, i+1); + else + snprintf(benchmark_str, sizeof(benchmark_str), "%s", BENCHMARK_KEY_NAME); + parser_ini_benchmark(tItems[index].bch_mrk_max[i], tItems[index].bch_mrk_min[i], + tItems[index].type_option, tItems[index].desp, core_mp.frame_len, benchmark_str); + + dump_benchmark_data(tItems[index].bch_mrk_max[i], tItems[index].bch_mrk_min[i]); + } + } + + /* Init Max/Min buffer */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].max_buf[y * core_mp.xch_len + x] = INT_MIN; + tItems[index].min_buf[y * core_mp.xch_len + x] = INT_MAX; + } + } + + ret = allnode_peak_to_peak_cdc_data(index); + if (ret < 0) { + ILI_ERR("Failed to initialise CDC data, %d\n", ret); + goto out; + } + for (i = 0; i < tItems[index].bch_mrk_frm_num; i++){ + for (j = 0; j < core_mp.frame_len; j++) + tItems[index].buf[i * core_mp.frame_len + j] = frm_buf[i][j]; + + compare_MaxMin_result(index, &tItems[index].buf[i * core_mp.frame_len]); + } +out: + return ret; +} + + +static int open_test_sp(int index) +{ + struct mp_test_P540_open *open; + int i = 0, x = 0, y = 0, ret = 0, addr = 0; + int Charge_AA = 0, Charge_Border = 0, Charge_Notch = 0, full_open_rate = 0; + char str[512] = {0}; + + ILI_DBG("index = %d, desp = %s, Frame Count = %d\n", + index, tItems[index].desp, tItems[index].frame_count); + + /* + * We assume that users who are calling the test forget to config frame count + * as 1, so we just help them to set it up. + */ + if (tItems[index].frame_count <= 0) { + ILI_ERR("Frame count is zero, which is at least set as 1\n"); + tItems[index].frame_count = 1; + } + + open = kzalloc(tItems[index].frame_count * sizeof(struct mp_test_P540_open), GFP_KERNEL); + if (ERR_ALLOC_MEM(open)) { + ILI_ERR("Failed to allocate open mem (%ld)\n", PTR_ERR(open)); + ret = -EMP_NOMEM; + goto out; + } + + ret = create_mp_test_frame_buffer(index, tItems[index].frame_count); + if (ret < 0) { + ret = -EMP_NOMEM; + goto out; + } + + if (frame1_cbk700 == NULL) { + frame1_cbk700 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frame1_cbk700)) { + ILI_ERR("Failed to allocate frame1_cbk700 buffer\n"); + return -EMP_NOMEM; + } + } else { + memset(frame1_cbk700, 0x0, core_mp.frame_len); + } + + if (frame1_cbk250 == NULL) { + frame1_cbk250 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frame1_cbk250)) { + ILI_ERR("Failed to allocate frame1_cbk250 buffer\n"); + ipio_kfree((void **)&frame1_cbk700); + return -EMP_NOMEM; + } + } else { + memset(frame1_cbk250, 0x0, core_mp.frame_len); + } + + if (frame1_cbk200 == NULL) { + frame1_cbk200 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frame1_cbk200)) { + ILI_ERR("Failed to allocate cbk buffer\n"); + ipio_kfree((void **)&frame1_cbk700); + ipio_kfree((void **)&frame1_cbk250); + return -EMP_NOMEM; + } + } else { + memset(frame1_cbk200, 0x0, core_mp.frame_len); + } + + tItems[index].node_type = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].node_type)) { + ILI_ERR("Failed to allocate node_type FRAME buffer\n"); + return -EMP_NOMEM; + } + + /* Init Max/Min buffer */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].max_buf[y * core_mp.xch_len + x] = INT_MIN; + tItems[index].min_buf[y * core_mp.xch_len + x] = INT_MAX; + } + } + + if (tItems[index].spec_option == BENCHMARK) { + parser_ini_benchmark(tItems[index].bench_mark_max, tItems[index].bench_mark_min, + tItems[index].type_option, tItems[index].desp, core_mp.frame_len, BENCHMARK_KEY_NAME); + dump_benchmark_data(tItems[index].bench_mark_max, tItems[index].bench_mark_min); + } + + parser_ini_nodetype(tItems[index].node_type, NODE_TYPE_KEY_NAME, core_mp.frame_len); + dump_node_type_buffer(tItems[index].node_type, "node type"); + + parser_get_int_data(tItems[index].desp, "charge_aa", str, sizeof(str)); + Charge_AA = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "charge_border", str, sizeof(str)); + Charge_Border = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "charge_notch", str, sizeof(str)); + Charge_Notch = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "full open", str, sizeof(str)); + full_open_rate = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "tvch", str, sizeof(str)); + open_para.tvch = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "tvcl", str, sizeof(str)); + open_para.tvcl = ili_katoi(str); + + ILI_DBG("AA = %d, Border = %d, Notch = %d, full_open_rate = %d, tvch = %d, tvcl = %d\n", + Charge_AA, Charge_Border, Charge_Notch, + full_open_rate, open_para.tvch, open_para.tvcl); + + for (i = 0; i < tItems[index].frame_count; i++) { + open[i].tdf_700 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].tdf_250 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].tdf_200 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].cbk_700 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].cbk_250 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].cbk_200 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].charg_rate = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].full_Open = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].dac = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + } + + for (i = 0; i < tItems[index].frame_count; i++) { + ret = allnode_open_cdc_data(0, open[i].dac); + if (ret < 0) { + ILI_ERR("Failed to get Open SP DAC data, %d\n", ret); + goto out; + } + ret = allnode_open_cdc_data(1, open[i].tdf_700); + if (ret < 0) { + ILI_ERR("Failed to get Open SP Raw1 data, %d\n", ret); + goto out; + } + ret = allnode_open_cdc_data(2, open[i].tdf_250); + if (ret < 0) { + ILI_ERR("Failed to get Open SP Raw2 data, %d\n", ret); + goto out; + } + ret = allnode_open_cdc_data(3, open[i].tdf_200); + if (ret < 0) { + ILI_ERR("Failed to get Open SP Raw3 data, %d\n", ret); + goto out; + } + allnode_open_cdc_result(index, open[i].cbk_700, open[i].dac, open[i].tdf_700); + allnode_open_cdc_result(index, open[i].cbk_250, open[i].dac, open[i].tdf_250); + allnode_open_cdc_result(index, open[i].cbk_200, open[i].dac, open[i].tdf_200); + + addr = 0; + + /* record fist frame for debug */ + if (i == 0) { + ipio_memcpy(frame1_cbk700, open[i].cbk_700, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + ipio_memcpy(frame1_cbk250, open[i].cbk_250, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + ipio_memcpy(frame1_cbk200, open[i].cbk_200, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + } + + ili_dump_data(open[i].cbk_700, 10, core_mp.frame_len, core_mp.xch_len, "cbk 700"); + ili_dump_data(open[i].cbk_250, 10, core_mp.frame_len, core_mp.xch_len, "cbk 250"); + ili_dump_data(open[i].cbk_200, 10, core_mp.frame_len, core_mp.xch_len, "cbk 200"); + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + open[i].charg_rate[addr] = open[i].cbk_250[addr] * 100 / open[i].cbk_700[addr]; + open[i].full_Open[addr] = open[i].cbk_700[addr] - open[i].cbk_200[addr]; + addr++; + } + } + + ili_dump_data(open[i].charg_rate, 10, core_mp.frame_len, core_mp.xch_len, "origin charge rate"); + ili_dump_data(open[i].full_Open, 10, core_mp.frame_len, core_mp.xch_len, "origin full open"); + + addr = 0; + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + if (full_open_rate_compare(open[i].full_Open, open[i].cbk_700, x, y, tItems[index].node_type[addr], full_open_rate) == false) { + tItems[index].buf[(i * core_mp.frame_len) + addr] = 0; + open[i].charg_rate[addr] = 0; + } + addr++; + } + } + + ili_dump_data(&tItems[index].buf[(i * core_mp.frame_len)], 10, core_mp.frame_len, core_mp.xch_len, "after full_open_rate_compare"); + + addr = 0; + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].buf[(i * core_mp.frame_len) + addr] = compare_charge(open[i].charg_rate, x, y, tItems[index].node_type, Charge_AA, Charge_Border, Charge_Notch); + addr++; + } + } + + ili_dump_data(&tItems[index].buf[(i * core_mp.frame_len)], 10, core_mp.frame_len, core_mp.xch_len, "after compare charge rate"); + + compare_MaxMin_result(index, &tItems[index].buf[(i * core_mp.frame_len)]); + } + +out: + ipio_kfree((void **)&tItems[index].node_type); + + if (open != NULL) { + for (i = 0; i < tItems[index].frame_count; i++) { + ipio_kfree((void **)&open[i].tdf_700); + ipio_kfree((void **)&open[i].tdf_250); + ipio_kfree((void **)&open[i].tdf_200); + ipio_kfree((void **)&open[i].cbk_700); + ipio_kfree((void **)&open[i].cbk_250); + ipio_kfree((void **)&open[i].cbk_200); + ipio_kfree((void **)&open[i].charg_rate); + ipio_kfree((void **)&open[i].full_Open); + ipio_kfree((void **)&open[i].dac); + } + kfree(open); + open = NULL; + } + + return ret; +} + +static int open_test_cap(int index) +{ + u32 pid = core_mp.chip_pid >> 8; + struct mp_test_open_c *open; + int i = 0, x = 0, y = 0, ret = 0, addr = 0; + char str[512] = {0}; + + ILI_DBG("index = %d, desp = %s, Frame Count = %d\n", + index, tItems[index].desp, tItems[index].frame_count); + + if (tItems[index].frame_count <= 0) { + ILI_ERR("Frame count is zero, which is at least set as 1\n"); + tItems[index].frame_count = 1; + } + + open = kzalloc(tItems[index].frame_count * sizeof(struct mp_test_open_c), GFP_KERNEL); + if (ERR_ALLOC_MEM(open)) { + ILI_ERR("Failed to allocate open mem (%ld)\n", PTR_ERR(open)); + ret = -EMP_NOMEM; + goto out; + } + + if (create_mp_test_frame_buffer(index, tItems[index].frame_count) < 0) { + ret = -EMP_NOMEM; + goto out; + } + + if (cap_dac == NULL) { + cap_dac = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(cap_dac)) { + ILI_ERR("Failed to allocate cap_dac buffer\n"); + return -EMP_NOMEM; + } + } else { + memset(cap_dac, 0x0, core_mp.frame_len); + } + + if (cap_raw == NULL) { + cap_raw = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(cap_raw)) { + ILI_ERR("Failed to allocate cap_raw buffer\n"); + ipio_kfree((void **)&cap_dac); + return -EMP_NOMEM; + } + } else { + memset(cap_raw, 0x0, core_mp.frame_len); + } + + /* Init Max/Min buffer */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].max_buf[y * core_mp.xch_len + x] = INT_MIN; + tItems[index].min_buf[y * core_mp.xch_len + x] = INT_MAX; + } + } + + if (tItems[index].spec_option == BENCHMARK) { + parser_ini_benchmark(tItems[index].bench_mark_max, tItems[index].bench_mark_min, + tItems[index].type_option, tItems[index].desp, core_mp.frame_len, BENCHMARK_KEY_NAME); + dump_benchmark_data(tItems[index].bench_mark_max, tItems[index].bench_mark_min); + } + + parser_get_int_data(tItems[index].desp, "gain", str, sizeof(str)); + open_para.gain = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "tvch", str, sizeof(str)); + open_para.tvch = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "tvcl", str, sizeof(str)); + open_para.tvcl = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "cbk_step", str, sizeof(str)); + open_para.cbk_step = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "cint", str, sizeof(str)); + open_para.cint = ili_katoi(str); + + /* 9881N, 9881O, 7807Q, 7807S not support cbk_step and cint read from mp.ini */ + if ((pid != 0x988117) && (pid != 0x988118) && (pid != 0x78071A) && (pid != 0x78071C)) { + if ((open_para.cbk_step == 0) || (open_para.cint == 0)) { + ILI_ERR("Failed to get open parameter"); + ret = -EMP_PARA_NULL; + core_mp.lost_parameter = true; + goto out; + } + } + + ILI_INFO("gain = %d, tvch = %d, tvcl = %d, cbk_step = %d, cint = %d\n", open_para.gain, open_para.tvch, open_para.tvcl, open_para.cbk_step, open_para.cint ); + for (i = 0; i < tItems[index].frame_count; i++) { + open[i].cap_dac = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].cap_raw = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].dcl_cap = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + } + + for (i = 0; i < tItems[index].frame_count; i++) { + ret = allnode_open_cdc_data(4, open[i].cap_dac); + if (ret < 0) { + ILI_ERR("Failed to get Open CAP DAC data, %d\n", ret); + goto out; + } + ret = allnode_open_cdc_data(5, open[i].cap_raw); + if (ret < 0) { + ILI_ERR("Failed to get Open CAP RAW data, %d\n", ret); + goto out; + } + + allnode_open_cdc_result(index, open[i].dcl_cap, open[i].cap_dac, open[i].cap_raw); + + /* record fist frame for debug */ + if (i == 0) { + ipio_memcpy(cap_dac, open[i].cap_dac, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + ipio_memcpy(cap_raw, open[i].cap_raw, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + } + + ili_dump_data(open[i].dcl_cap, 10, core_mp.frame_len, core_mp.xch_len, "DCL_Cap"); + + addr = 0; + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].buf[(i * core_mp.frame_len) + addr] = open[i].dcl_cap[addr]; + addr++; + } + } + compare_MaxMin_result(index, &tItems[index].buf[i * core_mp.frame_len]); + } + +out: + if (open != NULL) { + for (i = 0; i < tItems[index].frame_count; i++) { + ipio_kfree((void **)&open[i].cap_dac); + ipio_kfree((void **)&open[i].cap_raw); + ipio_kfree((void **)&open[i].dcl_cap); + } + kfree(open); + open = NULL; + } + + return ret; +} + +static int key_test(int index) +{ + int i, j = 0, ret = 0; + + ILI_DBG("Item = %s, Frame Count = %d\n", + tItems[index].desp, tItems[index].frame_count); + + if (tItems[index].frame_count == 0) { + ILI_ERR("Frame count is zero, which at least sets as 1\n"); + ret = -EINVAL; + goto out; + } + + ret = create_mp_test_frame_buffer(index, tItems[index].frame_count); + if (ret < 0) + goto out; + + for (i = 0; i < tItems[index].frame_count; i++) { + ret = allnode_key_cdc_data(index); + if (ret < 0) { + ILI_ERR("Failed to initialise CDC data, %d\n", ret); + goto out; + } + + for (j = 0; j < core_mp.key_len; j++) + tItems[index].buf[j] = key_buf[j]; + } + + compare_MaxMin_result(index, tItems[index].buf); + +out: + return ret; +} + +static int self_test(int index) +{ + ILI_ERR("TDDI has no self to be tested currently\n"); + return -1; +} + +static int st_test(int index) +{ + ILI_ERR("ST Test is not supported by the driver\n"); + return -1; +} + +static int mp_get_timing_info(void) +{ + int slen = 0; + char str[256] = {0}; + u8 info[64] = {0}; + char *key = "timing_info_raw"; + + core_mp.isLongV = 0; + + slen = parser_get_int_data("pv5_4 command", key, str, sizeof(str)); + if (slen < 0) + return -1; + + if (parser_get_u8_array(str, info, 16, slen) < 0) + return -1; + + core_mp.isLongV = info[6]; + + ILI_INFO("DDI Mode = %s\n", (core_mp.isLongV ? "Long V" : "Long H")); + + return 0; +} + +static int mp_test_data_sort_average(s32 *oringin_data, int index, s32 *avg_result) +{ + int i, j, k, x, y, len = 5, size; + s32 u32temp; + int u32up_frame, u32down_frame; + s32 *u32sum_raw_data = NULL; + s32 *u32data_buff = NULL; + int ret = 0; + + if (tItems[index].frame_count <= 1) { + ret = 0; + goto out; + } + + if (ERR_ALLOC_MEM(oringin_data)) { + ILI_ERR("Input wrong address\n"); + ret = -ENOMEM; + goto out; + } + + u32data_buff = kcalloc(core_mp.frame_len * tItems[index].frame_count, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(u32data_buff)) { + ILI_ERR("Failed to allocate u32data_buff FRAME buffer\n"); + ret = -ENOMEM; + goto out; + } + + u32sum_raw_data = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(u32data_buff)) { + ILI_ERR("Failed to allocate u32sum_raw_data FRAME buffer\n"); + ret = -ENOMEM; + goto out; + } + + size = core_mp.frame_len * tItems[index].frame_count; + for (i = 0; i < size; i++) { + u32data_buff[i] = oringin_data[i]; + } + + u32up_frame = tItems[index].frame_count * tItems[index].highest_percentage / 100; + u32down_frame = tItems[index].frame_count * tItems[index].lowest_percentage / 100; + ILI_DBG("Up=%d, Down=%d -%s\n", u32up_frame, u32down_frame, tItems[index].desp); + + if (debug_en) { + pr_cont("\n[Show Original frist%d and last%d node data]\n", len, len); + for (i = 0; i < core_mp.frame_len; i++) { + for (j = 0 ; j < tItems[index].frame_count ; j++) { + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("%d,", u32data_buff[j * core_mp.frame_len + i]); + } + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("\n"); + } + } + + for (i = 0; i < core_mp.frame_len; i++) { + for (j = 0; j < tItems[index].frame_count-1; j++) { + for (k = 0; k < (tItems[index].frame_count-1-j); k++) { + x = i+k*core_mp.frame_len; + y = i+(k+1)*core_mp.frame_len; + if (*(u32data_buff+x) > *(u32data_buff+y)) { + u32temp = *(u32data_buff+x); + *(u32data_buff+x) = *(u32data_buff+y); + *(u32data_buff+y) = u32temp; + } + } + } + } + + if (debug_en) { + pr_cont("\n[After sorting frist%d and last%d node data]\n", len, len); + for (i = 0; i < core_mp.frame_len; i++) { + for (j = u32down_frame; j < tItems[index].frame_count - u32up_frame; j++) { + if ((i < len) || (i >= (core_mp.frame_len - len))) + pr_cont("%d,", u32data_buff[i + j * core_mp.frame_len]); + } + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("\n"); + } + } + + for (i = 0 ; i < core_mp.frame_len ; i++) { + u32sum_raw_data[i] = 0; + for (j = u32down_frame; j < tItems[index].frame_count - u32up_frame; j++) + u32sum_raw_data[i] += u32data_buff[i + j * core_mp.frame_len]; + + avg_result[i] = u32sum_raw_data[i] / (tItems[index].frame_count - u32down_frame - u32up_frame); + } + + if (debug_en) { + pr_cont("\n[Average result frist%d and last%d node data]\n", len, len); + for (i = 0; i < core_mp.frame_len; i++) { + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("%d,", avg_result[i]); + } + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("\n"); + } + +out: + ipio_kfree((void **)&u32data_buff); + ipio_kfree((void **)&u32sum_raw_data); + return ret; +} + +static void mp_compare_cdc_result(int index, s32 *tmp, s32 *max_ts, s32 *min_ts, int *result) +{ + int i; + + if (ERR_ALLOC_MEM(tmp)) { + ILI_ERR("The data of test item is null (%p)\n", tmp); + *result = MP_DATA_FAIL; + return; + } + + if (tItems[index].catalog == SHORT_TEST) { + for (i = 0; i < core_mp.frame_len; i++) { + if (tmp[i] < min_ts[i]) { + *result = MP_DATA_FAIL; + return; + } + } + } else { + for (i = 0; i < core_mp.frame_len; i++) { + if (tmp[i] > max_ts[i] || tmp[i] < min_ts[i]) { + ILI_DBG("Fail No.%d: max=%d, val=%d, min=%d\n", i, max_ts[i], tmp[i], min_ts[i]); + *result = MP_DATA_FAIL; + return; + } + } + } +} + +static int mp_compare_test_result(int index) +{ + int i, test_result = MP_DATA_PASS; + s32 *max_threshold = NULL, *min_threshold = NULL; + + if (tItems[index].catalog == PIN_TEST) + return tItems[index].item_result; + + max_threshold = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(max_threshold)) { + ILI_ERR("Failed to allocate threshold FRAME buffer\n"); + test_result = MP_DATA_FAIL; + goto out; + } + + min_threshold = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(min_threshold)) { + ILI_ERR("Failed to allocate threshold FRAME buffer\n"); + test_result = MP_DATA_FAIL; + goto out; + } + + /* Show test result as below */ + if (tItems[index].catalog == TX_RX_DELTA) { + if (ERR_ALLOC_MEM(core_mp.rx_delta_buf) || ERR_ALLOC_MEM(core_mp.tx_delta_buf)) { + ILI_ERR("This test item (%s) has no data inside its buffer\n", tItems[index].desp); + test_result = MP_DATA_FAIL; + goto out; + } + + for (i = 0; i < core_mp.frame_len; i++) { + max_threshold[i] = core_mp.TxDeltaMax; + min_threshold[i] = core_mp.TxDeltaMin; + } + mp_compare_cdc_result(index, core_mp.tx_max_buf, max_threshold, min_threshold, &test_result); + mp_compare_cdc_result(index, core_mp.tx_min_buf, max_threshold, min_threshold, &test_result); + + for (i = 0; i < core_mp.frame_len; i++) { + max_threshold[i] = core_mp.RxDeltaMax; + min_threshold[i] = core_mp.RxDeltaMin; + } + + mp_compare_cdc_result(index, core_mp.rx_max_buf, max_threshold, min_threshold, &test_result); + mp_compare_cdc_result(index, core_mp.rx_min_buf, max_threshold, min_threshold, &test_result); + } else { + if (ERR_ALLOC_MEM(tItems[index].buf) || ERR_ALLOC_MEM(tItems[index].max_buf) || + ERR_ALLOC_MEM(tItems[index].min_buf) || ERR_ALLOC_MEM(tItems[index].result_buf)) { + ILI_ERR("This test item (%s) has no data inside its buffer\n", tItems[index].desp); + test_result = MP_DATA_FAIL; + goto out; + } + + if (tItems[index].spec_option == BENCHMARK) { + if (tItems[index].catalog == PEAK_TO_PEAK_TEST) { + for (i = 0; i < core_mp.frame_len; i++) { + max_threshold[i] = tItems[index].bch_mrk_max[0][i]; + min_threshold[i] = tItems[index].bch_mrk_min[0][i]; + } + } else { + for (i = 0; i < core_mp.frame_len; i++) { + max_threshold[i] = tItems[index].bench_mark_max[i]; + min_threshold[i] = tItems[index].bench_mark_min[i]; + } + } + } else { + for (i = 0; i < core_mp.frame_len; i++) { + max_threshold[i] = tItems[index].max; + min_threshold[i] = tItems[index].min; + } + } + + /* general result */ + if (tItems[index].trimmed_mean && tItems[index].catalog != PEAK_TO_PEAK_TEST) { + mp_test_data_sort_average(tItems[index].buf, index, tItems[index].result_buf); + mp_compare_cdc_result(index, tItems[index].result_buf, max_threshold, min_threshold, &test_result); + } else { + if (tItems[index].bch_mrk_multi) { + for (i = 0; i < tItems[index].bch_mrk_frm_num; i++) { + mp_compare_cdc_result(index, frm_buf[i], tItems[index].bch_mrk_max[i], tItems[index].bch_mrk_min[i], &test_result); + } + } else { + mp_compare_cdc_result(index, tItems[index].max_buf, max_threshold, min_threshold, &test_result); + mp_compare_cdc_result(index, tItems[index].min_buf, max_threshold, min_threshold, &test_result); + } + } + } + +out: + ipio_kfree((void **)&max_threshold); + ipio_kfree((void **)&min_threshold); + if (frm_buf != NULL) { + for (i = 0; i < tItems[index].bch_mrk_frm_num; i++) + if (frm_buf[i] != NULL) { + kfree(frm_buf[i]); + frm_buf[i] = NULL; + } + kfree(frm_buf); + frm_buf = NULL; + } + if ( core_mp.all_pass == true ){ + test_result = MP_DATA_PASS; + } + tItems[index].item_result = test_result; + return test_result; +} + +static void mp_do_retry(int index, int count) +{ + if (count == 0) { + ILI_INFO("Finish retry action\n"); + return; + } + + ILI_INFO("retry = %d, item = %s\n", count, tItems[index].desp); + + tItems[index].do_test(index); + + if (mp_compare_test_result(index) < 0) + return mp_do_retry(index, count - 1); +} + +static int mp_show_result(bool lcm_on) +{ + int ret = MP_DATA_PASS, seq; + int i, x, y, j, csv_len = 0, pass_item_count = 0, line_count = 0, get_frame_cont = 1; + s32 *max_threshold = NULL, *min_threshold = NULL; + char *csv = NULL; + char *ret_pass_name = NULL, *ret_fail_name = NULL; + struct file *f = NULL; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + mm_segment_t fs; +#endif + loff_t pos; + + csv = vmalloc(CSV_FILE_SIZE); + if (ERR_ALLOC_MEM(csv)) { + ILI_ERR("Failed to allocate CSV mem\n"); + ret = -EMP_NOMEM; + goto fail_open; + } + + max_threshold = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + min_threshold = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(max_threshold) || ERR_ALLOC_MEM(min_threshold)) { + ILI_ERR("Failed to allocate threshold FRAME buffer\n"); + ret = -EMP_NOMEM; + goto fail_open; + } + + if (parser_get_ini_key_value("pv5_4 command", "date", core_mp.ini_date) < 0) + ipio_memcpy(core_mp.ini_date, "Unknown", strlen("Unknown"), strlen("Unknown")); + if (parser_get_ini_key_value("pv5_4 command", "version", core_mp.ini_ver) < 0) + ipio_memcpy(core_mp.ini_ver, "Unknown", strlen("Unknown"), strlen("Unknown")); + + mp_print_csv_header(csv, &csv_len, &line_count, CSV_FILE_SIZE); + + for (seq = 0; seq < ri.count; seq++) { + i = ri.index[seq]; + get_frame_cont = 1; + + if (tItems[i].item_result == MP_DATA_PASS) { + pr_info("\n[%s],OK \n\n", tItems[i].desp); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "\n[%s],OK\n\n", tItems[i].desp); + } else { + pr_info("\n[%s],NG \n\n", tItems[i].desp); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "\n[%s],NG\n\n", tItems[i].desp); + } + + if (tItems[i].catalog == PIN_TEST) { + pr_info("Test INT Pin = %d\n", tItems[i].test_int_pin); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Test INT Pin = %d\n", tItems[i].test_int_pin); + pr_info("Pulse Test = %d\n", tItems[i].int_pulse_test); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Pulse Test = %d\n", tItems[i].int_pulse_test); + pr_info("Delay Time = %d\n", tItems[i].delay_time); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Delay Time = %d\n", tItems[i].delay_time); + continue; + } + + mp_print_csv_cdc_cmd(csv, &csv_len, i, CSV_FILE_SIZE); + + pr_info("Frame count = %d\n", tItems[i].frame_count); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Frame count = %d\n", tItems[i].frame_count); + + if (tItems[i].trimmed_mean && tItems[i].catalog != PEAK_TO_PEAK_TEST) { + pr_info("lowest percentage = %d\n", tItems[i].lowest_percentage); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "lowest percentage = %d\n", tItems[i].lowest_percentage); + + pr_info("highest percentage = %d\n", tItems[i].highest_percentage); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "highest percentage = %d\n", tItems[i].highest_percentage); + } + + /* Show result of benchmark max and min */ + if (tItems[i].spec_option == BENCHMARK) { + if (tItems[i].catalog == PEAK_TO_PEAK_TEST) { + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = tItems[i].bch_mrk_max[0][j]; + min_threshold[j] = tItems[i].bch_mrk_min[0][j]; + } + + for (j = 0; j < tItems[i].bch_mrk_frm_num; j++) { + char bench_name[128] = {0}; + snprintf(bench_name, (CSV_FILE_SIZE - csv_len), "Max_Bench%d", (j+1)); + mp_compare_cdc_show_result(i, tItems[i].bch_mrk_max[j], csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, bench_name, CSV_FILE_SIZE); + snprintf(bench_name, (CSV_FILE_SIZE - csv_len), "Min_Bench%d", (j+1)); + mp_compare_cdc_show_result(i, tItems[i].bch_mrk_min[j], csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, bench_name, CSV_FILE_SIZE); + } + } else { + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = tItems[i].bench_mark_max[j]; + min_threshold[j] = tItems[i].bench_mark_min[j]; + } + mp_compare_cdc_show_result(i, tItems[i].bench_mark_max, csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, "Max_Bench", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, tItems[i].bench_mark_min, csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, "Min_Bench", CSV_FILE_SIZE); + } + + } else { + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = tItems[i].max; + min_threshold[j] = tItems[i].min; + } + + pr_info("Max = %d\n", tItems[i].max); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Max = %d\n", tItems[i].max); + + pr_info("Min = %d\n", tItems[i].min); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Min = %d\n", tItems[i].min); + } + + if (ipio_strcmp(tItems[i].desp, "open test(integration)_sp") == 0) { + mp_compare_cdc_show_result(i, frame1_cbk700, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "frame1 cbk700", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, frame1_cbk250, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "frame1 cbk250", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, frame1_cbk200, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "frame1 cbk200", CSV_FILE_SIZE); + } + + if (ipio_strcmp(tItems[i].desp, "open test_c") == 0) { + mp_compare_cdc_show_result(i, cap_dac, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "CAP_DAC", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, cap_raw, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "CAP_RAW", CSV_FILE_SIZE); + } + + if (tItems[i].catalog == TX_RX_DELTA) { + if (ERR_ALLOC_MEM(core_mp.rx_delta_buf) || ERR_ALLOC_MEM(core_mp.tx_delta_buf)) { + ILI_ERR("This test item (%s) has no data inside its buffer\n", tItems[i].desp); + continue; + } + } else { + if (ERR_ALLOC_MEM(tItems[i].buf) || ERR_ALLOC_MEM(tItems[i].max_buf) || + ERR_ALLOC_MEM(tItems[i].min_buf)) { + ILI_ERR("This test item (%s) has no data inside its buffer\n", tItems[i].desp); + continue; + } + } + + /* Show test result as below */ + if (tItems[i].catalog == KEY_TEST) { + for (x = 0; x < core_mp.key_len; x++) { + DUMP("KEY_%02d ", x); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "KEY_%02d,", x); + } + + DUMP("\n"); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "\n"); + + for (y = 0; y < core_mp.key_len; y++) { + DUMP(" %3d ", tItems[i].buf[y]); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), " %3d, ", tItems[i].buf[y]); + } + + DUMP("\n"); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "\n"); + } else if (tItems[i].catalog == TX_RX_DELTA) { + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = core_mp.TxDeltaMax; + min_threshold[j] = core_mp.TxDeltaMin; + } + mp_compare_cdc_show_result(i, core_mp.tx_max_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "TX Max Hold", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, core_mp.tx_min_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "TX Min Hold", CSV_FILE_SIZE); + + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = core_mp.RxDeltaMax; + min_threshold[j] = core_mp.RxDeltaMin; + } + mp_compare_cdc_show_result(i, core_mp.rx_max_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "RX Max Hold", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, core_mp.rx_min_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "RX Min Hold", CSV_FILE_SIZE); + } else { + /* general result */ + if (tItems[i].trimmed_mean && tItems[i].catalog != PEAK_TO_PEAK_TEST) { + mp_compare_cdc_show_result(i, tItems[i].result_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "Mean result", CSV_FILE_SIZE); + } else { + if (!tItems[i].bch_mrk_multi) { + mp_compare_cdc_show_result(i, tItems[i].max_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "Max Hold", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, tItems[i].min_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "Min Hold", CSV_FILE_SIZE); + } + + } + if (tItems[i].catalog != PEAK_TO_PEAK_TEST) + get_frame_cont = tItems[i].frame_count; + + if (tItems[i].bch_mrk_multi) + get_frame_cont = tItems[i].bch_mrk_frm_num; + + /* result of each frame */ + for (j = 0; j < get_frame_cont; j++) { + char frame_name[128] = {0}; + snprintf(frame_name, (CSV_FILE_SIZE - csv_len), "Frame %d", (j+1)); + mp_compare_cdc_show_result(i, &tItems[i].buf[(j*core_mp.frame_len)], csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, frame_name, CSV_FILE_SIZE); + } + } + } + + memset(csv_name, 0, 128 * sizeof(char)); + + mp_print_csv_tail(csv, &csv_len, CSV_FILE_SIZE); + + for (i = 0; i < MP_TEST_ITEM; i++) { + if (tItems[i].run) { + if (tItems[i].item_result < 0) { + pass_item_count = 0; + break; + } + pass_item_count++; + } + } + + /* define csv file name */ + ret_pass_name = NORMAL_CSV_PASS_NAME; + ret_fail_name = NORMAL_CSV_FAIL_NAME; + + if (pass_item_count == 0) { + core_mp.final_result = MP_DATA_FAIL; + ret = MP_DATA_FAIL; + + if(core_mp.lost_parameter){ + ret_fail_name = NULL; + ret_fail_name = NORMAL_CSV_WARNING_NAME; + ILI_ERR("WARNING! OPEN or SHORT parameter not found in ini file!!\n"); + } + + if (lcm_on) + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/%s_%s.csv", CSV_LCM_ON_PATH, get_date_time_str(), ret_fail_name); + else + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/%s_%s.csv", CSV_LCM_OFF_PATH, get_date_time_str(), ret_fail_name); + } else { + core_mp.final_result = MP_DATA_PASS; + ret = MP_DATA_PASS; + + if (core_mp.lost_benchmark) { + ret_pass_name = NULL; + ret_pass_name = NORMAL_CSV_WARNING_NAME; + ILI_ERR("WARNING! Golden and SPEC in ini file aren't matched!!\n"); + } + + if (lcm_on) + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/%s_%s.csv", CSV_LCM_ON_PATH, get_date_time_str(), ret_pass_name); + else + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/%s_%s.csv", CSV_LCM_OFF_PATH, get_date_time_str(), ret_pass_name); + } + + ILI_INFO("Open CSV : %s\n", csv_name); + + if (f == NULL) + f = filp_open(csv_name, O_WRONLY | O_CREAT | O_TRUNC, 644); + + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open CSV file"); + ret = -EMP_NOMEM; + goto fail_open; + } + + ILI_INFO("Open CSV succeed, its length = %d\n ", csv_len); + + if (csv_len >= CSV_FILE_SIZE) { + ILI_ERR("The length saved to CSV is too long !\n"); + ret = -EMP_INVAL; + goto fail_open; + } + + pos = 0; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + fs = get_fs(); + set_fs(KERNEL_DS); + vfs_write(f, csv, csv_len, &pos); + set_fs(fs); +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) + kernel_write(f, csv, csv_len, &pos); +#endif + filp_close(f, NULL); + + ILI_INFO("Writing Data into CSV succeed\n"); + +fail_open: + ipio_vfree((void **)&csv); + ipio_kfree((void **)&max_threshold); + ipio_kfree((void **)&min_threshold); + return ret; +} + +static void ilitek_tddi_mp_init_item(void) +{ + int i = 0; + + memset(&core_mp, 0, sizeof(core_mp)); + memset(&open_para, 0, sizeof(open_para)); + memset(&short_para, 0, sizeof(short_para)); + + core_mp.chip_pid = ilits->chip->pid; + core_mp.chip_id = ilits->chip->id; + core_mp.chip_type = ilits->chip->type; + core_mp.chip_ver = ilits->chip->ver; + core_mp.fw_ver = ilits->chip->fw_ver; + core_mp.protocol_ver = ilits->protocol->ver; + core_mp.core_ver = ilits->chip->core_ver; + core_mp.cdc_len = ilits->protocol->cdc_len; + core_mp.no_bk_shift = ilits->chip->no_bk_shift; + core_mp.xch_len = ilits->xch_num; + core_mp.ych_len = ilits->ych_num; + core_mp.frame_len = core_mp.xch_len * core_mp.ych_len; + core_mp.stx_len = 0; + core_mp.srx_len = 0; + core_mp.key_len = 0; + core_mp.st_len = 0; + core_mp.tdf = 240; + core_mp.busy_cdc = INT_CHECK; + core_mp.retry = ilits->mp_retry; + core_mp.td_retry = false; + core_mp.final_result = MP_DATA_FAIL; + core_mp.lost_benchmark = false; + core_mp.lost_parameter = false; + core_mp.all_pass = false; + + ILI_INFO("============== TP & Panel info ================\n"); + ILI_INFO("Driver version = %s\n", DRIVER_VERSION); + ILI_INFO("TP Module = %s\n", ilits->md_name); + ILI_INFO("CHIP = 0x%x\n", core_mp.chip_pid); + ILI_INFO("Firmware version = %x\n", core_mp.fw_ver); + ILI_INFO("Protocol version = %x\n", core_mp.protocol_ver); + ILI_INFO("Core version = %x\n", core_mp.core_ver); + ILI_INFO("Read CDC Length = %d\n", core_mp.cdc_len); + ILI_INFO("X length = %d, Y length = %d\n", core_mp.xch_len, core_mp.ych_len); + ILI_INFO("Frame length = %d\n", core_mp.frame_len); + ILI_INFO("Check busy method = %s\n", (core_mp.busy_cdc ? "Polling" : "Interrupt")); + ILI_INFO("===============================================\n"); + + for (i = 0; i < MP_TEST_ITEM; i++) { + tItems[i].spec_option = 0; + tItems[i].type_option = 0; + tItems[i].run = false; + tItems[i].max = 0; + tItems[i].max_res = MP_DATA_FAIL; + tItems[i].item_result = MP_DATA_PASS; + tItems[i].min = 0; + tItems[i].min_res = MP_DATA_FAIL; + tItems[i].frame_count = 0; + tItems[i].trimmed_mean = 0; + tItems[i].lowest_percentage = 0; + tItems[i].highest_percentage = 0; + tItems[i].v_tdf_1 = 0; + tItems[i].v_tdf_2 = 0; + tItems[i].h_tdf_1 = 0; + tItems[i].h_tdf_2 = 0; + tItems[i].max_min_mode = 0; + tItems[i].bch_mrk_multi = false; + tItems[i].bch_mrk_frm_num = 1; + tItems[i].goldenmode = 0; + tItems[i].retry_cnt = RETRY_COUNT; + tItems[i].result_buf = NULL; + tItems[i].buf = NULL; + tItems[i].max_buf = NULL; + tItems[i].min_buf = NULL; + tItems[i].bench_mark_max = NULL; + tItems[i].bench_mark_min = NULL; + tItems[i].bch_mrk_max = NULL; + tItems[i].bch_mrk_min = NULL; + tItems[i].node_type = NULL; + tItems[i].delay_time = 0; + tItems[i].test_int_pin = 0; + tItems[i].int_pulse_test = 0; + + if (tItems[i].catalog == MUTUAL_TEST) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == TX_RX_DELTA) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == UNTOUCH_P2P) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == PIXEL) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == OPEN_TEST) { + if (ipio_strcmp(tItems[i].desp, "open test(integration)_sp") == 0) + tItems[i].do_test = open_test_sp; + else if (ipio_strcmp(tItems[i].desp, "open test_c") == 0) + tItems[i].do_test = open_test_cap; + else + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == KEY_TEST) { + tItems[i].do_test = key_test; + } else if (tItems[i].catalog == SELF_TEST) { + tItems[i].do_test = self_test; + } else if (tItems[i].catalog == ST_TEST) { + tItems[i].do_test = st_test; + } else if (tItems[i].catalog == PEAK_TO_PEAK_TEST) { + tItems[i].do_test = peak_to_peak_test; + } else if (tItems[i].catalog == SHORT_TEST) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == PIN_TEST) { + tItems[i].do_test = pin_test; + } + + tItems[i].result = kmalloc(16, GFP_KERNEL); + snprintf(tItems[i].result, 16, "%s", "FAIL"); + } +} + +static void mp_p2p_td_retry_after_ra_fail(int p2p_td) +{ + int i; + + for (i = 0; i < MP_TEST_ITEM; i++) { + if (ipio_strcmp(tItems[i].desp, "noise peak to peak(with panel) (lcm off)") == 0) + break; + } + + if (i >= MP_TEST_ITEM) + return; + + ILI_DBG("i = %d, p2p_noise_ret = %d, p2p_noise_run = %d\n", + i, tItems[i].item_result, tItems[i].run); + + if (tItems[i].item_result == MP_DATA_PASS && tItems[i].run == 1) + tItems[p2p_td].do_test(p2p_td); +} + +static void mp_test_run(int index) +{ + int i = index, j; + char str[512] = {0}; + + + /* Get parameters from ini */ + parser_get_int_data(tItems[i].desp, "spec option", str, sizeof(str)); + tItems[i].spec_option = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "type option", str, sizeof(str)); + tItems[i].type_option = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "frame count", str, sizeof(str)); + tItems[i].frame_count = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "trimmed mean", str, sizeof(str)); + tItems[i].trimmed_mean = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "lowest percentage", str, sizeof(str)); + tItems[i].lowest_percentage = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "highest percentage", str, sizeof(str)); + tItems[i].highest_percentage = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "goldenmode", str, sizeof(str)); + tItems[i].goldenmode = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "max min mode", str, sizeof(str)); + tItems[i].max_min_mode = ili_katoi(str); + + parser_get_int_data(tItems[i].desp, "retry count", str, sizeof(str)); + tItems[i].retry_cnt = ili_katoi(str); + + if (tItems[i].goldenmode && (tItems[i].spec_option != tItems[i].goldenmode)) + core_mp.lost_benchmark = true; + + /* Get pin test delay time */ + if (tItems[i].catalog == PIN_TEST) { + parser_get_int_data(tItems[i].desp, "test int pin", str, sizeof(str)); + tItems[i].test_int_pin = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "int pulse test", str, sizeof(str)); + tItems[i].int_pulse_test = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "delay time", str, sizeof(str)); + tItems[i].delay_time = ili_katoi(str); + } + + /* Get TDF value from ini */ + if (tItems[i].catalog == SHORT_TEST) { + parser_get_int_data(tItems[i].desp, "v_tdf_1", str, sizeof(str)); + tItems[i].v_tdf_1 = parser_get_tdf_value(str, tItems[i].catalog); + parser_get_int_data(tItems[i].desp, "v_tdf_2", str, sizeof(str)); + tItems[i].v_tdf_2 = parser_get_tdf_value(str, tItems[i].catalog); + parser_get_int_data(tItems[i].desp, "h_tdf_1", str, sizeof(str)); + tItems[i].h_tdf_1 = parser_get_tdf_value(str, tItems[i].catalog); + parser_get_int_data(tItems[i].desp, "h_tdf_2", str, sizeof(str)); + tItems[i].h_tdf_2 = parser_get_tdf_value(str, tItems[i].catalog); + } else { + parser_get_int_data(tItems[i].desp, "v_tdf", str, sizeof(str)); + tItems[i].v_tdf_1 = parser_get_tdf_value(str, tItems[i].catalog); + parser_get_int_data(tItems[i].desp, "h_tdf", str, sizeof(str)); + tItems[i].h_tdf_1 = parser_get_tdf_value(str, tItems[i].catalog); + } + + /* Get threshold from ini structure in parser */ + if (ipio_strcmp(tItems[i].desp, "tx/rx delta") == 0) { + parser_get_int_data(tItems[i].desp, "tx max", str, sizeof(str)); + core_mp.TxDeltaMax = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "tx min", str, sizeof(str)); + core_mp.TxDeltaMin = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "rx max", str, sizeof(str)); + core_mp.RxDeltaMax = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "rx min", str, sizeof(str)); + core_mp.RxDeltaMin = ili_katoi(str); + ILI_DBG("%s: Tx Max = %d, Tx Min = %d, Rx Max = %d, Rx Min = %d\n", + tItems[i].desp, core_mp.TxDeltaMax, core_mp.TxDeltaMin, + core_mp.RxDeltaMax, core_mp.RxDeltaMin); + } else { + parser_get_int_data(tItems[i].desp, "max", str, sizeof(str)); + tItems[i].max = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "min", str, sizeof(str)); + tItems[i].min = ili_katoi(str); + } + + + /* Get pin test delay time */ + if (tItems[i].catalog == PEAK_TO_PEAK_TEST) { + if (tItems[i].max_min_mode == 1 && tItems[i].spec_option == 1) { + tItems[i].bch_mrk_multi = true; + for (j = 1; j < 11; j++) { + char tmp[8] = {0}; + /* format complete string from the name of section "_Benchmark_Data". */ + snprintf(str, sizeof(str), "%s%s%s%d", tItems[index].desp, "_", BENCHMARK_KEY_NAME, j); + if (parser_get_int_data(str, str, tmp, sizeof(tmp)) <= 0) + break; + + tItems[i].bch_mrk_frm_num = j; + } + ILI_DBG("set bch_mrk_frm_num = %d\n", tItems[i].bch_mrk_frm_num); + } + } + + ILI_DBG("%s: run = %d, max = %d, min = %d, frame_count = %d\n", tItems[i].desp, + tItems[i].run, tItems[i].max, tItems[i].min, tItems[i].frame_count); + + ILI_DBG("v_tdf_1 = %d, v_tdf_2 = %d, h_tdf_1 = %d, h_tdf_2 = %d", tItems[i].v_tdf_1, + tItems[i].v_tdf_2, tItems[i].h_tdf_1, tItems[i].h_tdf_2); + + ILI_INFO("Run MP Test Item : %s\n", tItems[i].desp); + tItems[i].do_test(i); + + mp_compare_test_result(i); + + /* P2P TD retry after RA sample failed. */ + if (ipio_strcmp(tItems[i].desp, "peak to peak_td (lcm off)") == 0 && + tItems[i].item_result == MP_DATA_FAIL) { + parser_get_int_data(tItems[i].desp, "recheck ptop lcm off", str, sizeof(str)); + ILI_INFO("Peak to Peak TD retry = %d\n", ili_katoi(str)); + core_mp.td_retry = ili_katoi(str); + if (core_mp.td_retry) + mp_p2p_td_retry_after_ra_fail(i); + } + + if (core_mp.retry && tItems[i].item_result == MP_DATA_FAIL) { + ILI_INFO("MP failed, doing retry %d times\n", tItems[i].retry_cnt); + mp_do_retry(i, tItems[i].retry_cnt); + } + +} + +static void mp_test_free(void) +{ + int i, j; + + ILI_INFO("Free all allocated mem for MP\n"); + + core_mp.final_result = MP_DATA_FAIL; + core_mp.td_retry = false; + + for (i = 0; i < MP_TEST_ITEM; i++) { + tItems[i].run = false; + tItems[i].max_res = MP_DATA_FAIL; + tItems[i].min_res = MP_DATA_FAIL; + tItems[i].item_result = MP_DATA_PASS; + + if (tItems[i].catalog == TX_RX_DELTA) { + ipio_kfree((void **)&core_mp.rx_delta_buf); + ipio_kfree((void **)&core_mp.tx_delta_buf); + ipio_kfree((void **)&core_mp.tx_max_buf); + ipio_kfree((void **)&core_mp.tx_min_buf); + ipio_kfree((void **)&core_mp.rx_max_buf); + ipio_kfree((void **)&core_mp.rx_min_buf); + } else { + if (tItems[i].spec_option == BENCHMARK) { + ipio_kfree((void **)&tItems[i].bench_mark_max); + ipio_kfree((void **)&tItems[i].bench_mark_min); + if (tItems[i].bch_mrk_max != NULL) { + for (j = 0; j < tItems[i].bch_mrk_frm_num; j++) + if (tItems[i].bch_mrk_max[j] != NULL) { + kfree(tItems[i].bch_mrk_max[j]); + tItems[i].bch_mrk_max[j] = NULL; + } + kfree(tItems[i].bch_mrk_max); + tItems[i].bch_mrk_max = NULL; + } + if (tItems[i].bch_mrk_min != NULL) { + for (j = 0; j < tItems[i].bch_mrk_frm_num; j++) + if (tItems[i].bch_mrk_min[j] != NULL) { + kfree(tItems[i].bch_mrk_min[j]); + tItems[i].bch_mrk_min[j] = NULL; + } + kfree(tItems[i].bch_mrk_min); + tItems[i].bch_mrk_min = NULL; + } + } + ipio_kfree((void **)&tItems[i].node_type); + ipio_kfree((void **)&tItems[i].result); + ipio_kfree((void **)&tItems[i].result_buf); + ipio_kfree((void **)&tItems[i].max_buf); + ipio_kfree((void **)&tItems[i].min_buf); + ipio_vfree((void **)&tItems[i].buf); + } + } + + ipio_kfree((void **)&frame1_cbk700); + ipio_kfree((void **)&frame1_cbk250); + ipio_kfree((void **)&frame1_cbk200); + ipio_kfree((void **)&frame_buf); + ipio_kfree((void **)&key_buf); + if (frm_buf != NULL) { + for (j = 0; j < 3; j++) + if (frm_buf[j] != NULL) { + kfree(frm_buf[j]); + frm_buf[j] = NULL; + } + kfree(frm_buf); + frm_buf = NULL; + } +} + +/* The method to copy results to user depends on what APK needs */ +static void mp_copy_ret_to_apk(char *buf) +{ + int i, seq, len = 2; + + if (!buf) { + ILI_ERR("apk buffer is null\n"); + return; + } + + len += snprintf(buf + len, PAGE_SIZE - len, "CSV path: %s\n\n", csv_name); + for (seq = 0; seq < ri.count; seq++) { + i = ri.index[seq]; + if (tItems[i].item_result == MP_DATA_FAIL) { + ILI_ERR("[%s] = FAIL\n", tItems[i].desp); + len += snprintf(buf + len, PAGE_SIZE - len, "[%s] = FAIL\n", tItems[i].desp); + } else { + ILI_INFO("[%s] = PASS\n", tItems[i].desp); + len += snprintf(buf + len, PAGE_SIZE - len, "[%s] = PASS\n", tItems[i].desp); + } + } + ilits->mp_ret_len = len; +} + +static int mp_sort_item(bool lcm_on) +{ + int i, j; + char str[128] = {0}; + + ri.count = 0; + memset(ri.index, 0x0, MP_TEST_ITEM); + + for (i = 0; i < MAX_SECTION_NUM; i++) { + for (j = 0; j < MP_TEST_ITEM; j++) { + if (ipio_strcmp(seq_item[i], tItems[j].desp) != 0) + continue; + + parser_get_int_data(tItems[j].desp, "enable", str, sizeof(str)); + tItems[j].run = ili_katoi(str); + if (tItems[j].run != 1 || tItems[j].lcm != lcm_on) + continue; + + if (ri.count > MP_TEST_ITEM) { + ILI_ERR("Test item(%d) is invaild, abort\n", ri.count); + return -EINVAL; + } + + ri.index[ri.count] = j; + ri.count++; + } + } + return 0; +} + +static void ilitek_tddi_mp_all_pass_check(void) +{ + int ret = 0, retry = 5; + u8 cmd[1] = {0xFD}; + u8 temp[3] = {0}; + + ilits->wait_int_timeout = 20; + do { + ret = ilits->wrapper(cmd, 1, temp, 3, ON, OFF); + if (ret < 0) { + ILI_ERR("Write MP All Pass command failed\n"); + core_mp.all_pass = false; + } else { + if (temp[0] == 0xFD) + break; + } + } while (--retry >= 0); + ilits->wait_int_timeout = MP_INT_TIMEOUT; + if (retry <= 0) { + ILI_ERR("MP All Pass command failed, normal mode\n"); + core_mp.all_pass = false; + return; + } + + if ( temp[2] == ili_calc_packet_checksum(temp, sizeof(temp) - 1) && + temp[0] == 0xFD && temp[1] == 0x02) { //0xFD:back door command header, 0x02:Normal mode + core_mp.all_pass = true; + ILI_ERR("MP mode check: Back door mode, MP All Pass\n"); + } else { + core_mp.all_pass = false; //0xFD:back door command header, 0x01:Normal mode + ILI_ERR("MP mode check: Normal mode \n"); + } + + +} + +int ili_mp_test_main(char *apk, bool lcm_on) +{ + int i, ret = 0; + char str[128] = {0}, ver[128] = {0}; + + if (ilits->xch_num <= 0 || ilits->ych_num <= 0) { + ILI_ERR("Invalid frame length (%d, %d)\n", ilits->xch_num, ilits->ych_num); + ret = -EMP_INVAL; + goto out; + } + + if (ini_info == NULL ) { + ini_info = (struct ini_file_data *)vmalloc(sizeof(struct ini_file_data) * PARSER_MAX_KEY_NUM); + if (ERR_ALLOC_MEM(ini_info)) { + ILI_ERR("Failed to malloc ini_info\n"); + ret = -EMP_NOMEM; + goto out; + } + } + + ilitek_tddi_mp_init_item(); + + ilitek_tddi_mp_all_pass_check(); + + + ret = ilitek_tddi_mp_ini_parser(ilits->md_ini_path); + if (ret < 0) { + ILI_ERR("Failed to parsing INI file\n"); + ret = -EMP_INI; + goto out; + } + + /* Compare both protocol version of ini and firmware */ + parser_get_ini_key_value("pv5_4 command", "protocol", str); + snprintf(ver, sizeof(ver), "0x%s", str); + if ((ili_str2hex(ver)) != (core_mp.protocol_ver >> 8)) { + ILI_ERR("ERROR! MP Protocol version is invaild, 0x%x\n", ili_str2hex(ver)); + ret = -EMP_PROTOCOL; + goto out; + } + + /* Read timing info from ini file */ + if (mp_get_timing_info() < 0) { + ILI_ERR("Failed to get timing info from ini\n"); + ret = -EMP_TIMING_INFO; + goto out; + } + +#if MP_INT_LEVEL + if (ili_ic_int_trigger_ctrl(true) < 0) { + ILI_ERR("Failed to set INT as Level trigger"); + ret = -EMP_CMD; + goto out; + } +#endif + + /* Sort test item by ini file */ + if (mp_sort_item(lcm_on) < 0) { + ILI_ERR("Failed to sort test item\n"); + ret = -EMP_INI; + goto out; + } + + for (i = 0; i < ri.count; i++) + mp_test_run(ri.index[i]); + + ret = mp_show_result(lcm_on); + + mp_copy_ret_to_apk(apk); + +out: + mp_test_free(); + +#if MP_INT_LEVEL + if (ili_ic_int_trigger_ctrl(false) < 0) { + ILI_ERR("Failed to set INT back to pluse trigger"); + ret = -EMP_CMD; + } +#endif + + ipio_vfree((void **)&ini_info); + return ret; +}; diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_mtk.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_mtk.c new file mode 100755 index 000000000000..35823d8f1972 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_mtk.c @@ -0,0 +1,459 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" +#include "tpd.h" + +#define DTS_INT_GPIO "touch,irq-gpio" +#define DTS_RESET_GPIO "touch,reset-gpio" +#define DTS_OF_NAME "mediatek,cap_touch" +#define MTK_RST_GPIO GTP_RST_PORT +#define MTK_INT_GPIO GTP_INT_PORT + +extern struct tpd_device *tpd; + +void ili_tp_reset(void) +{ + ILI_INFO("edge delay = %d\n", ilits->rst_edge_delay); + + /* Need accurate power sequence, do not change it to msleep */ + tpd_gpio_output(ilits->tp_rst, 1); + mdelay(1); + tpd_gpio_output(ilits->tp_rst, 0); + mdelay(5); + tpd_gpio_output(ilits->tp_rst, 1); + mdelay(ilits->rst_edge_delay); +} + +void ili_input_register(void) +{ + int i; + + ilits->input = tpd->dev; + + if (tpd_dts_data.use_tpd_button) { + for (i = 0; i < tpd_dts_data.tpd_key_num; i++) + input_set_capability(ilits->input, EV_KEY, tpd_dts_data.tpd_key_local[i]); + } + + /* set the supported event type for input device */ + set_bit(EV_ABS, ilits->input->evbit); + set_bit(EV_SYN, ilits->input->evbit); + set_bit(EV_KEY, ilits->input->evbit); + set_bit(BTN_TOUCH, ilits->input->keybit); + set_bit(BTN_TOOL_FINGER, ilits->input->keybit); + set_bit(INPUT_PROP_DIRECT, ilits->input->propbit); + + if (MT_PRESSURE) + input_set_abs_params(ilits->input, ABS_MT_PRESSURE, 0, 255, 0, 0); + + if (MT_B_TYPE) { +#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE + input_mt_init_slots(ilits->input, MAX_TOUCH_NUM, INPUT_MT_DIRECT); +#else + input_mt_init_slots(ilits->input, MAX_TOUCH_NUM); +#endif /* LINUX_VERSION_CODE */ + } else { + input_set_abs_params(ilits->input, ABS_MT_TRACKING_ID, 0, MAX_TOUCH_NUM, 0, 0); + } + + /* Gesture keys register */ + input_set_capability(ilits->input, EV_KEY, KEY_POWER); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_UP); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_DOWN); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_LEFT); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_RIGHT); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_O); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_E); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_M); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_W); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_S); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_V); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_Z); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_C); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_F); + + __set_bit(KEY_GESTURE_POWER, ilits->input->keybit); + __set_bit(KEY_GESTURE_UP, ilits->input->keybit); + __set_bit(KEY_GESTURE_DOWN, ilits->input->keybit); + __set_bit(KEY_GESTURE_LEFT, ilits->input->keybit); + __set_bit(KEY_GESTURE_RIGHT, ilits->input->keybit); + __set_bit(KEY_GESTURE_O, ilits->input->keybit); + __set_bit(KEY_GESTURE_E, ilits->input->keybit); + __set_bit(KEY_GESTURE_M, ilits->input->keybit); + __set_bit(KEY_GESTURE_W, ilits->input->keybit); + __set_bit(KEY_GESTURE_S, ilits->input->keybit); + __set_bit(KEY_GESTURE_V, ilits->input->keybit); + __set_bit(KEY_GESTURE_Z, ilits->input->keybit); + __set_bit(KEY_GESTURE_C, ilits->input->keybit); + __set_bit(KEY_GESTURE_F, ilits->input->keybit); +} + +#if REGULATOR_POWER +void ili_plat_regulator_power_on(bool status) +{ + ILI_INFO("%s\n", status ? "POWER ON" : "POWER OFF"); + + if (status) { + if (ilits->vdd) { + if (regulator_enable(ilits->vdd) < 0) + ILI_ERR("regulator_enable VDD fail\n"); + } + if (ilits->vcc) { + if (regulator_enable(ilits->vcc) < 0) + ILI_ERR("regulator_enable VCC fail\n"); + } + } else { + if (ilits->vdd) { + if (regulator_disable(ilits->vdd) < 0) + ILI_ERR("regulator_enable VDD fail\n"); + } + if (ilits->vcc) { + if (regulator_disable(ilits->vcc) < 0) + ILI_ERR("regulator_enable VCC fail\n"); + } + } + atomic_set(&ilits->ice_stat, DISABLE); + mdelay(5); +} + +static void ilitek_plat_regulator_power_init(void) +{ + const char *vdd_name = "vdd"; + const char *vcc_name = "vcc"; + + ilits->vdd = regulator_get(tpd->tpd_dev, vdd_name); + if (ERR_ALLOC_MEM(ilits->vdd)) { + ILI_ERR("regulator_get VDD fail\n"); + ilits->vdd = NULL; + } + + tpd->reg = ilits->vdd; + + if (regulator_set_voltage(ilits->vdd, VDD_VOLTAGE, VDD_VOLTAGE) < 0) + ILI_ERR("Failed to set VDD %d\n", VDD_VOLTAGE); + + ilits->vcc = regulator_get(ilits->dev, vcc_name); + if (ERR_ALLOC_MEM(ilits->vcc)) { + ILI_ERR("regulator_get VCC fail.\n"); + ilits->vcc = NULL; + } + if (regulator_set_voltage(ilits->vcc, VCC_VOLTAGE, VCC_VOLTAGE) < 0) + ILI_ERR("Failed to set VCC %d\n", VCC_VOLTAGE); + + ili_plat_regulator_power_on(true); +} +#endif + +static int ilitek_plat_gpio_register(void) +{ + int ret = 0; + + ilits->tp_int = MTK_INT_GPIO; + ilits->tp_rst = MTK_RST_GPIO; + + ILI_INFO("TP INT: %d\n", ilits->tp_int); + ILI_INFO("TP RESET: %d\n", ilits->tp_rst); + + if (!gpio_is_valid(ilits->tp_int)) { + ILI_ERR("Invalid INT gpio: %d\n", ilits->tp_int); + return -EBADR; + } + + if (!gpio_is_valid(ilits->tp_rst)) { + ILI_ERR("Invalid RESET gpio: %d\n", ilits->tp_rst); + return -EBADR; + } + + gpio_direction_input(ilits->tp_int); + return ret; +} + +void ili_irq_disable(void) +{ + unsigned long flag; + + spin_lock_irqsave(&ilits->irq_spin, flag); + + if (atomic_read(&ilits->irq_stat) == DISABLE) + goto out; + + if (!ilits->irq_num) { + ILI_ERR("gpio_to_irq (%d) is incorrect\n", ilits->irq_num); + goto out; + } + + disable_irq_nosync(ilits->irq_num); + atomic_set(&ilits->irq_stat, DISABLE); + ILI_DBG("Disable irq success\n"); + +out: + spin_unlock_irqrestore(&ilits->irq_spin, flag); +} + +void ili_irq_enable(void) +{ + unsigned long flag; + + spin_lock_irqsave(&ilits->irq_spin, flag); + + if (atomic_read(&ilits->irq_stat) == ENABLE) + goto out; + + if (!ilits->irq_num) { + ILI_ERR("gpio_to_irq (%d) is incorrect\n", ilits->irq_num); + goto out; + } + + enable_irq(ilits->irq_num); + atomic_set(&ilits->irq_stat, ENABLE); + ILI_DBG("Enable irq success\n"); + +out: + spin_unlock_irqrestore(&ilits->irq_spin, flag); +} + +static irqreturn_t ilitek_plat_isr_top_half(int irq, void *dev_id) +{ + if (irq != ilits->irq_num) { + ILI_ERR("Incorrect irq number (%d)\n", irq); + return IRQ_NONE; + } + + if (atomic_read(&ilits->cmd_int_check) == ENABLE) { + atomic_set(&ilits->cmd_int_check, DISABLE); + ILI_DBG("CMD INT detected, ignore\n"); + wake_up(&(ilits->inq)); + return IRQ_HANDLED; + } + + if (ilits->prox_near) { + ILI_INFO("Proximity event, ignore interrupt!\n"); + return IRQ_HANDLED; + } + + ILI_DBG("report: %d, rst: %d, fw: %d, switch: %d, mp: %d, sleep: %d, esd: %d\n", + ilits->report, + atomic_read(&ilits->tp_reset), + atomic_read(&ilits->fw_stat), + atomic_read(&ilits->tp_sw_mode), + atomic_read(&ilits->mp_stat), + atomic_read(&ilits->tp_sleep), + atomic_read(&ilits->esd_stat)); + + if (!ilits->report || atomic_read(&ilits->tp_reset) || + atomic_read(&ilits->fw_stat) || atomic_read(&ilits->tp_sw_mode) || + atomic_read(&ilits->mp_stat) || atomic_read(&ilits->tp_sleep) || + atomic_read(&ilits->esd_stat)) { + ILI_DBG("ignore interrupt !\n"); + return IRQ_HANDLED; + } + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t ilitek_plat_isr_bottom_half(int irq, void *dev_id) +{ + if (mutex_is_locked(&ilits->touch_mutex)) { + ILI_DBG("touch is locked, ignore\n"); + return IRQ_HANDLED; + } + mutex_lock(&ilits->touch_mutex); + ili_report_handler(); + mutex_unlock(&ilits->touch_mutex); + return IRQ_HANDLED; +} + +void ili_irq_unregister(void) +{ + devm_free_irq(ilits->dev, ilits->irq_num, NULL); +} + +int ili_irq_register(int type) +{ + int ret = 0; + static bool get_irq_pin; + struct device_node *node; + + atomic_set(&ilits->irq_stat, DISABLE); + + if (get_irq_pin == false) { + node = of_find_matching_node(NULL, touch_of_match); + if (node) + ilits->irq_num = irq_of_parse_and_map(node, 0); + + ILI_INFO("ilits->irq_num = %d\n", ilits->irq_num); + get_irq_pin = true; + } + + ret = devm_request_threaded_irq(ilits->dev, ilits->irq_num, + ilitek_plat_isr_top_half, + ilitek_plat_isr_bottom_half, + type | IRQF_ONESHOT, "ilitek", NULL); + + if (type == IRQF_TRIGGER_FALLING) + ILI_INFO("IRQ TYPE = IRQF_TRIGGER_FALLING\n"); + if (type == IRQF_TRIGGER_RISING) + ILI_INFO("IRQ TYPE = IRQF_TRIGGER_RISING\n"); + + if (ret != 0) + ILI_ERR("Failed to register irq handler, irq = %d, ret = %d\n", ilits->irq_num, ret); + + atomic_set(&ilits->irq_stat, ENABLE); + + return ret; +} + +static void tpd_resume(struct device *h) +{ + if (ili_sleep_handler(TP_RESUME) < 0) + ILI_ERR("TP resume failed\n"); +} + +static void tpd_suspend(struct device *h) +{ + if (ili_sleep_handler(TP_SUSPEND) < 0) + ILI_ERR("TP suspend failed\n"); +} + +static int ilitek_tp_pm_suspend(struct device *dev) +{ + ILI_INFO("CALL BACK TP PM SUSPEND"); + ilits->pm_suspend = true; + reinit_completion(&ilits->pm_completion); + return 0; +} + +static int ilitek_tp_pm_resume(struct device *dev) +{ + ILI_INFO("CALL BACK TP PM RESUME"); + ilits->pm_suspend = false; + complete(&ilits->pm_completion); + return 0; +} + +static int ilitek_plat_probe(void) +{ + ILI_INFO("platform probe\n"); + +#if REGULATOR_POWER + ilitek_plat_regulator_power_init(); +#endif + + if (ilitek_plat_gpio_register() < 0) + ILI_ERR("Register gpio failed\n"); + + ili_irq_register(ilits->irq_tirgger_type); + + if (ili_tddi_init() < 0) { + ILI_ERR("ILITEK Driver probe failed\n"); + ili_irq_unregister(); + return -ENODEV; + } + tpd_load_status = 1; + ilits->pm_suspend = false; + init_completion(&ilits->pm_completion); + + ILI_INFO("ILITEK Driver loaded successfully!"); + return 0; +} + +static int ilitek_plat_remove(void) +{ + ILI_INFO("remove plat dev\n"); + ili_dev_remove(); + return 0; +} + +static const struct dev_pm_ops tp_pm_ops = { + .suspend = ilitek_tp_pm_suspend, + .resume = ilitek_tp_pm_resume, +}; + +static const struct of_device_id tp_match_table[] = { + {.compatible = DTS_OF_NAME}, + {}, +}; + +static struct ilitek_hwif_info hwif = { + .bus_type = TDDI_INTERFACE, + .plat_type = TP_PLAT_MTK, + .owner = THIS_MODULE, + .name = TDDI_DEV_ID, + .of_match_table = of_match_ptr(tp_match_table), + .plat_probe = ilitek_plat_probe, + .plat_remove = ilitek_plat_remove, + .pm = &tp_pm_ops, +}; + +static int tpd_local_init(void) +{ + ILI_INFO("TPD init device driver\n"); + + if (ili_dev_init(&hwif) < 0) { + ILI_ERR("Failed to register i2c/spi bus driver\n"); + return -ENODEV; + } + if (tpd_load_status == 0) { + ILI_ERR("Add error touch panel driver\n"); + return -1; + } + if (tpd_dts_data.use_tpd_button) { + tpd_button_setting(tpd_dts_data.tpd_key_num, tpd_dts_data.tpd_key_local, + tpd_dts_data.tpd_key_dim_local); + } + tpd_type_cap = 1; + return 0; +} + +static struct tpd_driver_t tpd_device_driver = { + .tpd_device_name = TDDI_DEV_ID, + .tpd_local_init = tpd_local_init, + .suspend = tpd_suspend, + .resume = tpd_resume, +}; + +static int __init ilitek_plat_dev_init(void) +{ + int ret = 0; + + ILI_INFO("ILITEK TP driver init for MTK\n"); + tpd_get_dts_info(); + ret = tpd_driver_add(&tpd_device_driver); + if (ret < 0) { + ILI_ERR("ILITEK add TP driver failed\n"); + tpd_driver_remove(&tpd_device_driver); + return -ENODEV; + } + return 0; +} + +static void __exit ilitek_plat_dev_exit(void) +{ + ILI_INFO("ilitek driver has been removed\n"); + tpd_driver_remove(&tpd_device_driver); +} + +module_init(ilitek_plat_dev_init); +module_exit(ilitek_plat_dev_exit); +MODULE_AUTHOR("ILITEK"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_node.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_node.c new file mode 100755 index 000000000000..c0221aa37609 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_node.c @@ -0,0 +1,2840 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" + +#define USER_STR_BUFF PAGE_SIZE +#define IOCTL_I2C_BUFF PAGE_SIZE +#define ILITEK_IOCTL_MAGIC 100 +#define ILITEK_IOCTL_MAXNR 27 + +#define ILITEK_IOCTL_I2C_WRITE_DATA _IOWR(ILITEK_IOCTL_MAGIC, 0, u8*) +#define ILITEK_IOCTL_I2C_SET_WRITE_LENGTH _IOWR(ILITEK_IOCTL_MAGIC, 1, int) +#define ILITEK_IOCTL_I2C_READ_DATA _IOWR(ILITEK_IOCTL_MAGIC, 2, u8*) +#define ILITEK_IOCTL_I2C_SET_READ_LENGTH _IOWR(ILITEK_IOCTL_MAGIC, 3, int) + +#define ILITEK_IOCTL_TP_HW_RESET _IOWR(ILITEK_IOCTL_MAGIC, 4, int) +#define ILITEK_IOCTL_TP_POWER_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 5, int) +#define ILITEK_IOCTL_TP_REPORT_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 6, int) +#define ILITEK_IOCTL_TP_IRQ_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 7, int) + +#define ILITEK_IOCTL_TP_DEBUG_LEVEL _IOWR(ILITEK_IOCTL_MAGIC, 8, int) +#define ILITEK_IOCTL_TP_FUNC_MODE _IOWR(ILITEK_IOCTL_MAGIC, 9, int) + +#define ILITEK_IOCTL_TP_FW_VER _IOWR(ILITEK_IOCTL_MAGIC, 10, u8*) +#define ILITEK_IOCTL_TP_PL_VER _IOWR(ILITEK_IOCTL_MAGIC, 11, u8*) +#define ILITEK_IOCTL_TP_CORE_VER _IOWR(ILITEK_IOCTL_MAGIC, 12, u8*) +#define ILITEK_IOCTL_TP_DRV_VER _IOWR(ILITEK_IOCTL_MAGIC, 13, u8*) +#define ILITEK_IOCTL_TP_CHIP_ID _IOWR(ILITEK_IOCTL_MAGIC, 14, u32*) + +#define ILITEK_IOCTL_TP_NETLINK_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 15, int*) +#define ILITEK_IOCTL_TP_NETLINK_STATUS _IOWR(ILITEK_IOCTL_MAGIC, 16, int*) + +#define ILITEK_IOCTL_TP_MODE_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 17, u8*) +#define ILITEK_IOCTL_TP_MODE_STATUS _IOWR(ILITEK_IOCTL_MAGIC, 18, int*) +#define ILITEK_IOCTL_ICE_MODE_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 19, int) + +#define ILITEK_IOCTL_TP_INTERFACE_TYPE _IOWR(ILITEK_IOCTL_MAGIC, 20, u8*) +#define ILITEK_IOCTL_TP_DUMP_FLASH _IOWR(ILITEK_IOCTL_MAGIC, 21, int) +#define ILITEK_IOCTL_TP_FW_UART_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 22, u8*) +#define ILITEK_IOCTL_TP_PANEL_INFO _IOWR(ILITEK_IOCTL_MAGIC, 23, u32*) +#define ILITEK_IOCTL_TP_INFO _IOWR(ILITEK_IOCTL_MAGIC, 24, u32*) +#define ILITEK_IOCTL_WRAPPER_RW _IOWR(ILITEK_IOCTL_MAGIC, 25, u8*) +#define ILITEK_IOCTL_DDI_WRITE _IOWR(ILITEK_IOCTL_MAGIC, 26, u8*) +#define ILITEK_IOCTL_DDI_READ _IOWR(ILITEK_IOCTL_MAGIC, 27, u8*) + +#ifdef CONFIG_COMPAT +#define ILITEK_COMPAT_IOCTL_I2C_WRITE_DATA _IOWR(ILITEK_IOCTL_MAGIC, 0, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_I2C_SET_WRITE_LENGTH _IOWR(ILITEK_IOCTL_MAGIC, 1, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_I2C_READ_DATA _IOWR(ILITEK_IOCTL_MAGIC, 2, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_I2C_SET_READ_LENGTH _IOWR(ILITEK_IOCTL_MAGIC, 3, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_HW_RESET _IOWR(ILITEK_IOCTL_MAGIC, 4, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_POWER_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 5, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_REPORT_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 6, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_IRQ_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 7, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_DEBUG_LEVEL _IOWR(ILITEK_IOCTL_MAGIC, 8, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_FUNC_MODE _IOWR(ILITEK_IOCTL_MAGIC, 9, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_FW_VER _IOWR(ILITEK_IOCTL_MAGIC, 10, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_PL_VER _IOWR(ILITEK_IOCTL_MAGIC, 11, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_CORE_VER _IOWR(ILITEK_IOCTL_MAGIC, 12, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_DRV_VER _IOWR(ILITEK_IOCTL_MAGIC, 13, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_CHIP_ID _IOWR(ILITEK_IOCTL_MAGIC, 14, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_NETLINK_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 15, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_NETLINK_STATUS _IOWR(ILITEK_IOCTL_MAGIC, 16, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_MODE_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 17, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_MODE_STATUS _IOWR(ILITEK_IOCTL_MAGIC, 18, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_ICE_MODE_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 19, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_INTERFACE_TYPE _IOWR(ILITEK_IOCTL_MAGIC, 20, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_DUMP_FLASH _IOWR(ILITEK_IOCTL_MAGIC, 21, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_FW_UART_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 22, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_PANEL_INFO _IOWR(ILITEK_IOCTL_MAGIC, 23, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_INFO _IOWR(ILITEK_IOCTL_MAGIC, 24, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_WRAPPER_RW _IOWR(ILITEK_IOCTL_MAGIC, 25, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_DDI_WRITE _IOWR(ILITEK_IOCTL_MAGIC, 26, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_DDI_READ _IOWR(ILITEK_IOCTL_MAGIC, 27, compat_uptr_t) +#endif + +struct record_state { + u8 touch_palm_state_e : 2; + u8 app_an_statu_e : 3; + u8 app_sys_check_bg_abnormal : 1; + u8 g_b_wrong_bg : 1; +}; + +static unsigned char g_user_buf[USER_STR_BUFF] = {0}; +#define ILI_SPI_NAME "ilitek" +#define ILI_SPI_NAME_TM "ilitek_tm" +#define ILI_SPI_NAME_CSOT "ilitek_csot" +#define ILI_SPI_NAME_TXD "ilitek_txd" +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +static struct class *touchscreen_class; +static struct device *touchscreen_class_dev; +#endif + +int ili_str2hex(char *str) +{ + int strlen, result, intermed, intermedtop; + char *s = str; + + while (*s != 0x0) { + s++; + } + + strlen = (int)(s - str); + s = str; + if (*s != 0x30) { + return -1; + } + + s++; + + if (*s != 0x78 && *s != 0x58) { + return -1; + } + s++; + + strlen = strlen - 3; + result = 0; + while (*s != 0x0) { + intermed = *s & 0x0f; + intermedtop = *s & 0xf0; + if (intermedtop == 0x60 || intermedtop == 0x40) { + intermed += 0x09; + } + intermed = intermed << (strlen << 2); + result = result | intermed; + strlen -= 1; + s++; + } + return result; +} + +int ili_katoi(char *str) +{ + int result = 0; + unsigned int digit; + int sign; + + if (*str == '-') { + sign = 1; + str += 1; + } else { + sign = 0; + if (*str == '+') { + str += 1; + } + } + + for (;; str += 1) { + digit = *str - '0'; + if (digit > 9) + break; + result = (10 * result) + digit; + } + + if (sign) { + return -result; + } + return result; +} + +struct file_buffer { + char *ptr; + char fname[128]; + int32_t wlen; + int32_t flen; + int32_t max_size; +}; + +static int file_write(struct file_buffer *file, bool new_open) +{ + struct file *f = NULL; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + mm_segment_t fs; +#endif + loff_t pos; + + if (file->ptr == NULL) { + ILI_ERR("str is invaild\n"); + return -1; + } + + /*if (file->fname == NULL) { + ILI_ERR("file name is invaild\n"); + return -1; + }*/ + + if (file->flen >= file->max_size) { + ILI_ERR("The length saved to file is too long !\n"); + return -1; + } + + if (new_open) + f = filp_open(file->fname, O_WRONLY | O_CREAT | O_TRUNC, 644); + else + f = filp_open(file->fname, O_WRONLY | O_CREAT | O_APPEND, 644); + + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open %s file\n", file->fname); + return -1; + } + pos = 0; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + fs = get_fs(); + set_fs(KERNEL_DS); + vfs_write(f, file->ptr, file->flen, &pos); + set_fs(fs); +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) + kernel_write(f, file->ptr, file->wlen, &pos); +#endif + filp_close(f, NULL); + return 0; +} + +static int ilitek_debug_node_buff_control(bool open) +{ + int i, ret; + + ilits->dnp = open; + + ILI_INFO("Debug buf ctrl = %s\n", ilits->dnp ? "Enabled" : "Disabled"); + + if (open) { + ilits->dbf = 0; + ilits->odi = 0; + ipio_kfree((void **)&ilits->dbl); + ilits->dbl = kzalloc(TR_BUF_LIST_SIZE * sizeof(*ilits->dbl), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->dbl)) { + ILI_ERR("Failed to allocate ilits->dbl mem, %ld\n", PTR_ERR(ilits->dbl)); + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < TR_BUF_LIST_SIZE; i++) { + ilits->dbl[i].mark = false; + ipio_kfree((void **)&ilits->dbl[i].data); + ilits->dbl[i].data = kzalloc(TR_BUF_SIZE * sizeof(unsigned char), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->dbl[i].data)) { + ILI_ERR("Failed to allocate dbl[%d] mem, %ld\n", i, PTR_ERR(ilits->dbl[i].data)); + ret = -ENOMEM; + goto out; + } + } + return 0; + } + +out: + for (i = 0; i < TR_BUF_LIST_SIZE; i++) { + ilits->dbl[i].mark = false; + ipio_kfree((void **)&ilits->dbl[i].data); + } + ipio_kfree((void **)&ilits->dbl); + return ret; +} + +static int debug_mode_get_data(struct file_buffer *file, u8 type, u32 frame_count) +{ + int ret; + u8 cmd[2] = { 0 }, row, col; + s16 temp; + unsigned char *ptr; + int j; + u16 write_index = 0; + + ilitek_debug_node_buff_control(DISABLE); + + ilits->dbf = 0; + row = ilits->ych_num; + col = ilits->xch_num; + mutex_lock(&ilits->touch_mutex); + + cmd[0] = 0xFA; + cmd[1] = type; + ret = ilits->wrapper(cmd, 2, NULL, 0, ON, OFF); + + if (ilitek_debug_node_buff_control(ENABLE) < 0) { + ILI_ERR("Failed to allocate debug buf\n"); + ret = -ENOMEM; + goto out; + } + + mutex_unlock(&ilits->touch_mutex); + + if (ret < 0) { + ILI_ERR("Write 0xFA,0x%x failed\n", type); + goto out; + } + + while (write_index < frame_count) { + file->wlen = 0; + ILI_INFO("frame = %d,index = %d,count = %d\n", write_index, ilits->odi, ilits->dbf); + if (!wait_event_interruptible_timeout(ilits->inq, ilits->dbl[ilits->odi].mark, msecs_to_jiffies(3000))) { + ILI_ERR("debug mode get data timeout!\n"); + goto out; + } + + mutex_lock(&ilits->touch_mutex); + + memset(file->ptr, 0, file->max_size); + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "\n\nFrame%d,", write_index); + for (j = 0; j < col; j++) + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "[X%d] ,", j); + ptr = &ilits->dbl[ilits->odi].data[35]; + for (j = 0; j < row * col; j++, ptr += 2) { + temp = (*ptr << 8) + *(ptr + 1); + if (j % col == 0) + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "\n[Y%d] ,", (j / col)); + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "%d, ", temp); + } + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "\n[X] ,"); + for (j = 0; j < row + col; j++, ptr += 2) { + temp = (*ptr << 8) + *(ptr + 1); + if (j == col) + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "\n[Y] ,"); + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "%d, ", temp); + } + file_write(file, false); + write_index++; + + mutex_unlock(&ilits->touch_mutex); + + ilits->dbl[ilits->odi].mark = false; + ilits->odi = ((ilits->odi + 1) % TR_BUF_LIST_SIZE); + } +out: + ilitek_debug_node_buff_control(DISABLE); + return ret; +} + +static int dev_mkdir(char *name, umode_t mode) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + int err = -1; + return err; +#else +#if 0 + struct dentry *dentry; + struct path path; + int err; + dentry = kern_path_create(AT_FDCWD, name, &path, LOOKUP_DIRECTORY); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + err = vfs_mkdir(path.dentry->d_inode, dentry, mode); + done_path_create(&path, dentry); + return err; +#else + int err; + mm_segment_t fs; + + ILI_INFO("mkdir: %s\n", name); + fs = get_fs(); + set_fs(KERNEL_DS); + err = 0;//ksys_mkdir(name, mode); + set_fs(fs); + + return err; +#endif +#endif //#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +} + +static ssize_t ilitek_proc_get_delta_data_read(struct file *pFile, char __user *buf, size_t size, loff_t *pos) +{ + s16 *delta = NULL; + int row = 0, col = 0, index = 0; + int ret, i, x, y; + int read_length = 0, len = 0; + u8 cmd[2] = {0}; + u8 *data = NULL; + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + ili_wq_ctrl(WQ_ESD, DISABLE); + ili_wq_ctrl(WQ_BAT, DISABLE); + mutex_lock(&ilits->touch_mutex); + + row = ilits->ych_num; + col = ilits->xch_num; + read_length = 4 + 2 * row * col + 1 ; + + ILI_INFO("read length = %d\n", read_length); + + data = kcalloc(read_length + 1, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(data)) { + ILI_ERR("Failed to allocate data mem\n"); + goto out; + } + + delta = kcalloc(P5_X_DEBUG_MODE_PACKET_LENGTH, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(delta)) { + ILI_ERR("Failed to allocate delta mem\n"); + goto out; + } + + cmd[0] = 0xB7; + cmd[1] = 0x1; //get delta + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Failed to write 0xB7,0x1 command, %d\n", ret); + goto out; + } + + msleep(120); + + /* read debug packet header */ + ret = ilits->wrapper(NULL, 0, data, read_length, OFF, OFF); + if (ret < 0) { + ILI_ERR("Read debug packet header failed, %d\n", ret); + goto out; + } + + cmd[1] = 0x03; //switch to normal mode + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, ON, OFF); + if (ret < 0) { + ILI_ERR("Failed to write 0xB7,0x3 command, %d\n", ret); + goto out; + } + + for (i = 4, index = 0; i < row * col * 2 + 4; i += 2, index++) + delta[index] = (data[i] << 8) + data[i + 1]; + + len = snprintf(g_user_buf + size, PAGE_SIZE - len, "======== Deltadata ========\n"); + ILI_INFO("======== Deltadata ========\n"); + + len += snprintf(g_user_buf + len, PAGE_SIZE - len, + "Header 0x%x ,Type %d, Length %d\n", data[0], data[1], (data[2] << 8) | data[3]); + ILI_INFO("Header 0x%x ,Type %d, Length %d\n", data[0], data[1], (data[2] << 8) | data[3]); + + // print delta data + for (y = 0; y < row; y++) { + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "[%2d] ", (y+1)); + ILI_INFO("[%2d] ", (y+1)); + + for (x = 0; x < col; x++) { + int shift = y * col + x; + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "%5d", delta[shift]); + printk(KERN_CONT "%5d", delta[shift]); + } + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "\n"); + printk(KERN_CONT "\n"); + } + + if (copy_to_user(buf, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + +out: + mutex_unlock(&ilits->touch_mutex); + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + ipio_kfree((void **)&data); + ipio_kfree((void **)&delta); + return len; +} + +static ssize_t ilitek_proc_fw_get_raw_data_read(struct file *pFile, char __user *buf, size_t size, loff_t *pos) +{ + s16 *rawdata = NULL; + int row = 0, col = 0, index = 0; + int ret, i, x, y; + int read_length = 0, len = 0; + u8 cmd[2] = {0}; + u8 *data = NULL; + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + ili_wq_ctrl(WQ_ESD, DISABLE); + ili_wq_ctrl(WQ_BAT, DISABLE); + mutex_lock(&ilits->touch_mutex); + + row = ilits->ych_num; + col = ilits->xch_num; + read_length = 4 + 2 * row * col + 1 ; + + ILI_INFO("read length = %d\n", read_length); + + data = kcalloc(read_length + 1, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(data)) { + ILI_ERR("Failed to allocate data mem\n"); + goto out; + } + + rawdata = kcalloc(P5_X_DEBUG_MODE_PACKET_LENGTH, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(rawdata)) { + ILI_ERR("Failed to allocate rawdata mem\n"); + goto out; + } + + cmd[0] = 0xB7; + cmd[1] = 0x2; //get rawdata + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Failed to write 0xB7,0x2 command, %d\n", ret); + goto out; + } + + msleep(120); + + /* read debug packet header */ + ret = ilits->wrapper(NULL, 0, data, read_length, OFF, OFF); + if (ret < 0) { + ILI_ERR("Read debug packet header failed, %d\n", ret); + goto out; + } + + cmd[1] = 0x03; //switch to normal mode + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, ON, OFF); + if (ret < 0) { + ILI_ERR("Failed to write 0xB7,0x3 command, %d\n", ret); + goto out; + } + + for (i = 4, index = 0; i < row * col * 2 + 4; i += 2, index++) + rawdata[index] = (data[i] << 8) + data[i + 1]; + + len = snprintf(g_user_buf, PAGE_SIZE, "======== RawData ========\n"); + ILI_INFO("======== RawData ========\n"); + + len += snprintf(g_user_buf + len, PAGE_SIZE - len, + "Header 0x%x ,Type %d, Length %d\n", data[0], data[1], (data[2] << 8) | data[3]); + ILI_INFO("Header 0x%x ,Type %d, Length %d\n", data[0], data[1], (data[2] << 8) | data[3]); + + // print raw data + for (y = 0; y < row; y++) { + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "[%2d] ", (y+1)); + ILI_INFO("[%2d] ", (y+1)); + + for (x = 0; x < col; x++) { + int shift = y * col + x; + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "%5d", rawdata[shift]); + printk(KERN_CONT "%5d", rawdata[shift]); + } + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "\n"); + printk(KERN_CONT "\n"); + } + + if (copy_to_user(buf, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + +out: + mutex_unlock(&ilits->touch_mutex); + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + ipio_kfree((void **)&data); + ipio_kfree((void **)&rawdata); + return len; +} + +static ssize_t ilitek_proc_fw_pc_counter_read(struct file *pFile, char __user *buf, size_t size, loff_t *pos) +{ + int len = 0; + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + ili_ic_get_pc_counter(0); + len = snprintf(g_user_buf, PAGE_SIZE, "pc = 0x%x, latch = 0x%x\n", ilits->fw_pc, ilits->fw_latch); + + if (copy_to_user(buf, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + return len; +} + +static u32 rw_reg[5] = {0}; +static ssize_t ilitek_proc_rw_tp_reg_read(struct file *pFile, char __user *buf, size_t size, loff_t *pos) +{ + int ret = 0, len = 0; + bool mcu_on = 0, read = 0; + u32 type, addr, read_data, write_data, write_len, stop_mcu; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + + if (*pos != 0) + return 0; + + stop_mcu = rw_reg[0]; + type = rw_reg[1]; + addr = rw_reg[2]; + write_data = rw_reg[3]; + write_len = rw_reg[4]; + + ILI_INFO("stop_mcu = %d\n", rw_reg[0]); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + mutex_lock(&ilits->touch_mutex); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + if (stop_mcu == mcu_on) + ret = ili_ice_mode_ctrl(ENABLE, ON); + else + ret = ili_ice_mode_ctrl(ENABLE, OFF); + + if (ret < 0) { + ILI_ERR("Failed to enter ICE mode, ret = %d\n", ret); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to enter ICE mode"); + } + + if (type == read) { + if (ili_ice_mode_read(addr, &read_data, sizeof(u32)) < 0) { + ILI_ERR("Read data error\n"); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Read data error"); + } + + ILI_INFO("[READ]:addr = 0x%06x, read = 0x%08x\n", addr, read_data); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "READ:addr = 0x%06x, read = 0x%08x\n", addr, read_data); + } else { + if (ili_ice_mode_write(addr, write_data, write_len) < 0) { + ILI_ERR("Write data error\n"); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Write data error"); + } + + ILI_INFO("[WRITE]:addr = 0x%06x, write = 0x%08x, len = %d byte\n", addr, write_data, write_len); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "WRITE:addr = 0x%06x, write = 0x%08x, len =%d byte\n", addr, write_data, write_len); + } + + if (stop_mcu == mcu_on) + ret = ili_ice_mode_ctrl(DISABLE, ON); + else + ret = ili_ice_mode_ctrl(DISABLE, OFF); + + if (ret < 0) { + ILI_ERR("Failed to disable ICE mode, ret = %d\n", ret); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to disable ICE mode"); + } + + if (copy_to_user((char *)buf, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_proc_rw_tp_reg_write(struct file *filp, const char *buff, size_t size, loff_t *pos) +{ + char *token = NULL, *cur = NULL; + char cmd[256] = { 0 }; + u32 count = 0; + + if ((size - 1) > sizeof(cmd)) { + ILI_ERR("ERROR! input length is larger than local buffer\n"); + return -1; + } + + mutex_lock(&ilits->touch_mutex); + + if (buff != NULL) { + if (copy_from_user(cmd, buff, size - 1)) { + ILI_INFO("Failed to copy data from user space\n"); + size = -1; + goto out; + } + } + token = cur = cmd; + while ((token = strsep(&cur, ",")) != NULL) { + rw_reg[count] = ili_str2hex(token); + ILI_INFO("rw_reg[%d] = 0x%x\n", count, rw_reg[count]); + count++; + } + +out: + mutex_unlock(&ilits->touch_mutex); + return size; +} +static ssize_t ilitek_proc_passive_pen_write(struct file *filp, const char *buff, size_t size, loff_t *pos) +{ + char cmd[256] = { 0 }; + int glove_value =0; + + if ((size - 1) > sizeof(cmd)) { + ILI_ERR("ERROR! input length is larger than local buffer\n"); + return -1; + } + + mutex_lock(&ilits->touch_mutex); + + if (buff != NULL) { + if (copy_from_user(cmd, buff, size - 1)) { + ILI_INFO("Failed to copy data from user space\n"); + size = -1; + goto out; + } + } + sscanf(cmd, "%x", &glove_value); + ILI_INFO("glove status:%x\n",glove_value); + if (!glove_value) { + if (ili_ic_func_ctrl("passive_pen", DISABLE) < 0) + ILI_ERR("Write glove disable fail\n"); + } + else { + if (ili_ic_func_ctrl("passive_pen", ENABLE) < 0) + ILI_ERR("Write glove enable fail\n"); + } + +out: + mutex_unlock(&ilits->touch_mutex); + return size; +} + +static ssize_t ilitek_proc_canvas_write(struct file *filp, const char *buff, size_t size, loff_t *pos) +{ + char cmd[256] = { 0 }; + int stylus_value =0; + + if ((size - 1) > sizeof(cmd)) { + ILI_ERR("ERROR! input length is larger than local buffer\n"); + return -1; + } + + mutex_lock(&ilits->touch_mutex); + + if (buff != NULL) { + if (copy_from_user(cmd, buff, size - 1)) { + ILI_INFO("Failed to copy data from user space\n"); + size = -1; + goto out; + } + } + sscanf(cmd, "%x", &stylus_value); + ILI_INFO("stylus status:%x\n",stylus_value); + if (!stylus_value) { + if (ili_ic_func_ctrl("canvas", DISABLE) < 0) + ILI_ERR("Write stylus disable fail\n"); + } + else { + if (ili_ic_func_ctrl("canvas", ENABLE) < 0) + ILI_ERR("Write stylus enable fail\n"); + } + +out: + mutex_unlock(&ilits->touch_mutex); + return size; +} +static ssize_t ilitek_proc_debug_switch_read(struct file *pFile, char __user *buff, size_t size, loff_t *pos) +{ + bool open; + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + mutex_lock(&ilits->debug_read_mutex); + + open = !ilits->dnp; + + ilitek_debug_node_buff_control(open); + + size = snprintf(g_user_buf, USER_STR_BUFF * sizeof(unsigned char), "dnp : %s\n", ilits->dnp ? "Enabled" : "Disabled"); + *pos = size; + + if (copy_to_user(buff, g_user_buf, size)) + ILI_ERR("Failed to copy data to user space\n"); + + mutex_unlock(&ilits->debug_read_mutex); + return size; +} + +static ssize_t ilitek_proc_debug_message_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + unsigned long p = *pos; + int i = 0; + int send_data_len = 0; + int ret = 0; + int data_count = 0; + int one_data_bytes = 0; + int need_read_data_len = 0; + int type = 0; + unsigned char *tmpbuf = NULL; + unsigned char tmpbufback[128] = {0}; + + if (filp->f_flags & O_NONBLOCK) { + return -EAGAIN; + } + + if (!ilits->dnp) { + ILI_ERR("Debug flag isn't enabled (%d)\n", ilits->dnp); + return -EAGAIN; + } + + mutex_lock(&ilits->debug_read_mutex); + ILI_DBG("f_count= %d, index = %d, mark = %d\n", ilits->dbf, ilits->odi, ilits->dbl[ilits->odi].mark); + if (!wait_event_interruptible_timeout(ilits->inq, ilits->dbl[ilits->odi].mark, msecs_to_jiffies(3000))) { + ILI_ERR("WARNING ! there's no data received.\n"); + mutex_unlock(&ilits->debug_read_mutex); + *pos = send_data_len; + return send_data_len; + } + mutex_lock(&ilits->debug_mutex); + + tmpbuf = vmalloc(4096); /* buf size if even */ + if (ERR_ALLOC_MEM(tmpbuf)) { + ILI_ERR("buffer vmalloc error\n"); + send_data_len += snprintf(tmpbufback + send_data_len, sizeof(tmpbufback), "buffer vmalloc error\n"); + ret = copy_to_user(buff, tmpbufback, send_data_len); /*ilits->dbl[0] */ + goto out; + } + + if (ilits->dbl[ilits->odi].mark) { + if (ilits->dbl[ilits->odi].data[0] == P5_X_DEMO_PACKET_ID) { + need_read_data_len = 43; + } else if (ilits->dbl[ilits->odi].data[0] == P5_X_DEMO_HIGH_RESOLUTION_PACKET_ID) { + need_read_data_len = P5_X_5B_LOW_RESOLUTION_LENGTH; + need_read_data_len += P5_X_CUSTOMER_LENGTH; + } else if (ilits->dbl[ilits->odi].data[0] == P5_X_I2CUART_PACKET_ID) { + type = ilits->dbl[ilits->odi].data[3] & 0x0F; + + data_count = ilits->dbl[ilits->odi].data[1] * ilits->dbl[ilits->odi].data[2]; + + if (type == 0 || type == 1 || type == 6) { + one_data_bytes = 1; + } else if (type == 2 || type == 3) { + one_data_bytes = 2; + } else if (type == 4 || type == 5) { + one_data_bytes = 4; + } + need_read_data_len = data_count * one_data_bytes + 1 + 5; + } else if (ilits->dbl[ilits->odi].data[0] == P5_X_DEBUG_PACKET_ID || ilits->dbl[ilits->odi].data[0] == P5_X_DEBUG_LITE_PACKET_ID) { + send_data_len = 0; /* ilits->dbl[0][1] - 2; */ + need_read_data_len = TR_BUF_SIZE - 8; + } else if (ilits->dbl[ilits->odi].data[0] == P5_X_I2CUART_PACKET_ID) { + need_read_data_len = P5_X_DEMO_MODE_PACKET_LEN+P5_X_DEMO_MODE_AXIS_LEN+P5_X_DEMO_MODE_STATE_INFO; + } else if (ilits->dbl[ilits->odi].data[0] == P5_X_DEBUG_HIGH_RESOLUTION_PACKET_ID) { + send_data_len = 0; /* ilits->dbl[0][1] - 2; */ + need_read_data_len = TR_BUF_SIZE - 8; + } + + for (i = 0; i < need_read_data_len; i++) { + send_data_len += snprintf(tmpbuf + send_data_len, sizeof(tmpbufback), "%02X", ilits->dbl[ilits->odi].data[i]); + if (send_data_len >= 4096) { + ILI_ERR("send_data_len = %d set 4096 i = %d\n", send_data_len, i); + send_data_len = 4096; + break; + } + } + + send_data_len += snprintf(tmpbuf + send_data_len, sizeof(tmpbufback), "\n\n"); + + if (p == 5 || size == 4096 || size == 2048) { + ilits->dbl[ilits->odi].mark = false; + ilits->odi = ((ilits->odi + 1) % TR_BUF_LIST_SIZE); + } + } + + /* Preparing to send debug data to user */ + if (size == 4096) + ret = copy_to_user(buff, tmpbuf, send_data_len); + else + ret = copy_to_user(buff, tmpbuf + p, send_data_len - p); + + /* ILI_ERR("send_data_len = %d\n", send_data_len); */ + if (send_data_len <= 0 || send_data_len > 4096) { + ILI_ERR("send_data_len = %d set 4096\n", send_data_len); + send_data_len = 4096; + } + + if (ret) { + ILI_ERR("copy_to_user err\n"); + ret = -EFAULT; + } else { + *pos += send_data_len; + ret = send_data_len; + ILI_DBG("Read %d bytes(s) from %ld\n", send_data_len, p); + } + +out: + mutex_unlock(&ilits->debug_mutex); + mutex_unlock(&ilits->debug_read_mutex); + ipio_vfree((void **)&tmpbuf); + return send_data_len; +} + +static ssize_t ilitek_proc_get_debug_mode_data_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + int ret; + struct file_buffer csv; + + if (*pos != 0) + return 0; + + /* initialize file */ + memset(csv.fname, 0, sizeof(csv.fname)); + snprintf(csv.fname, sizeof(csv.fname), "%s", DEBUG_DATA_FILE_PATH); + csv.flen = 0; + csv.wlen = 0; + csv.max_size = DEBUG_DATA_FILE_SIZE; + + csv.ptr = vmalloc(csv.max_size); + if (ERR_ALLOC_MEM(csv.ptr)) { + ILI_ERR("Failed to allocate CSV mem\n"); + goto out; + } + + /* save data to csv */ + ILI_INFO("Get Raw data %d frame\n", ilits->raw_count); + ILI_INFO("Get Delta data %d frame\n", ilits->delta_count); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "Get Raw data %d frame\n", ilits->raw_count); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "Get Delta data %d frame\n", ilits->delta_count); + + file_write(&csv, true); + + /* change to debug mode */ + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG, false, NULL) < 0) { + ILI_ERR("Failed to set tp data length\n"); + goto out; + } + + /* get raw data */ + csv.wlen = 0; + memset(csv.ptr, 0, csv.max_size); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "\n\n=======Raw data======="); + file_write(&csv, false); + + ret = debug_mode_get_data(&csv, P5_X_FW_RAW_DATA_MODE, ilits->raw_count); + if (ret < 0) + goto out; + + /* get delta data */ + csv.wlen = 0; + memset(csv.ptr, 0, csv.max_size); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "\n\n=======Delta data======="); + file_write(&csv, false); + + ret = debug_mode_get_data(&csv, P5_X_FW_DELTA_DATA_MODE, ilits->delta_count); + if (ret < 0) + goto out; + + /* change to demo mode */ + if (ili_set_tp_data_len(DATA_FORMAT_DEMO, false, NULL) < 0) + ILI_ERR("Failed to set tp data length\n"); + +out: + ipio_vfree((void **)&csv.ptr); + return 0; +} + +static ssize_t ilitek_proc_get_debug_mode_data_write(struct file *filp, const char *buff, size_t size, loff_t *pos) +{ + char *token = NULL, *cur = NULL; + char cmd[256] = {0}; + u8 temp[256] = {0}, count = 0; + + if ((size - 1) > sizeof(cmd)) { + ILI_ERR("ERROR! input length is larger than local buffer\n"); + return -1; + } + + if (buff != NULL) { + if (copy_from_user(cmd, buff, size - 1)) { + ILI_INFO("Failed to copy data from user space\n"); + return -1; + } + } + + ILI_INFO("size = %d, cmd = %s\n", (int)size, cmd); + token = cur = cmd; + while ((token = strsep(&cur, ",")) != NULL) { + temp[count] = ili_str2hex(token); + ILI_INFO("temp[%d] = %d\n", count, temp[count]); + count++; + } + + ilits->raw_count = ((temp[0] << 8) | temp[1]); + ilits->delta_count = ((temp[2] << 8) | temp[3]); + ilits->bg_count = ((temp[4] << 8) | temp[5]); + + ILI_INFO("Raw_count = %d, Delta_count = %d, BG_count = %d\n", ilits->raw_count, ilits->delta_count, ilits->bg_count); + return size; +} + +static ssize_t ilitek_node_mp_lcm_on_test_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + int ret = 0, len = 2; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + + if (*pos != 0) + return 0; + + ILI_INFO("Run MP test with LCM on\n"); + + mutex_lock(&ilits->touch_mutex); + + /* Create the directory for mp_test result */ + if ((dev_mkdir(CSV_LCM_ON_PATH, S_IRUGO | S_IWUSR)) != 0) + ILI_ERR("Failed to create directory for mp_test\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + ilits->mp_ret_len = 0; + + ret = ili_mp_test_handler(g_user_buf, ON); + ILI_INFO("MP TEST %s, Error code = %d\n", (ret < 0) ? "FAIL" : "PASS", ret); + + g_user_buf[0] = 3; + g_user_buf[1] = (ret < 0) ? -ret : ret; + len += ilits->mp_ret_len; + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "MP TEST %s\n", (ret < 0) ? "FAIL" : "PASS"); + if (g_user_buf[1] == EMP_MODE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to switch MP mode, abort!"); + } else if (g_user_buf[1] == EMP_FW_PROC) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "FW still upgrading, abort!"); + } else if (g_user_buf[1] == EMP_FORMUL_NULL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "MP formula is null, abort!"); + } else if (g_user_buf[1] == EMP_INI) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Not found ini file, abort!"); + } else if (g_user_buf[1] == EMP_NOMEM) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to allocated memory, abort!"); + } else if (g_user_buf[1] == EMP_PROTOCOL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Protocol version isn't matched, abort!"); + } else if (g_user_buf[1] == EMP_TIMING_INFO) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to get timing info, abort!"); + } else if (g_user_buf[1] == EMP_PARA_NULL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to get mp parameter, abort!"); + } + + if (copy_to_user((char *)buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_node_mp_lcm_off_test_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + int ret = 0, len = 2; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + + if (*pos != 0) + return 0; + + ILI_INFO("Run MP test with LCM off\n"); + + mutex_lock(&ilits->touch_mutex); + + /* Create the directory for mp_test result */ + if ((dev_mkdir(CSV_LCM_OFF_PATH, S_IRUGO | S_IWUSR)) != 0) + ILI_ERR("Failed to create directory for mp_test\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + ilits->mp_ret_len = 0; + + ret = ili_mp_test_handler(g_user_buf, OFF); + ILI_INFO("MP TEST %s, Error code = %d\n", (ret < 0) ? "FAIL" : "PASS", ret); + + g_user_buf[0] = 3; + g_user_buf[1] = (ret < 0) ? -ret : ret; + len += ilits->mp_ret_len; + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "MP TEST %s\n", (ret < 0) ? "FAIL" : "PASS"); + if (g_user_buf[1] == EMP_MODE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to switch MP mode, abort!"); + } else if (g_user_buf[1] == EMP_FW_PROC) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "FW still upgrading, abort!"); + } else if (g_user_buf[1] == EMP_FORMUL_NULL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "MP formula is null, abort!"); + } else if (g_user_buf[1] == EMP_INI) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Not found ini file, abort!"); + } else if (g_user_buf[1] == EMP_NOMEM) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to allocated memory, abort!"); + } else if (g_user_buf[1] == EMP_PROTOCOL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Protocol version isn't matched, abort!"); + } else if (g_user_buf[1] == EMP_TIMING_INFO) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to get timing info, abort!"); + } else if (g_user_buf[1] == EMP_PARA_NULL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to get mp parameter, abort!"); + } + + if (copy_to_user((char *)buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_node_ver_info_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + u32 len = 0; + + if (*pos != 0) + return 0; + + mutex_lock(&ilits->touch_mutex); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "CHIP ID = %x\n", ilits->chip->id); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "FW version = %d.%d.%d.%d\n", + ilits->chip->fw_ver >> 24, (ilits->chip->fw_ver >> 16) & 0xFF, + (ilits->chip->fw_ver >> 8) & 0xFF, ilits->chip->fw_ver & 0xFF); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "FW MP version = %d.%d.%d.%d\n", + ilits->chip->fw_mp_ver >> 24, (ilits->chip->fw_mp_ver >> 16) & 0xFF, + (ilits->chip->fw_mp_ver >> 8) & 0xFF, ilits->chip->fw_mp_ver & 0xFF); + + if (ilits->protocol->core_ver_len == P5_X_CORE_VER_THREE_LENGTH) + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "Core version = %d.%d.%d\n", + (ilits->chip->core_ver >> 24) & 0xFF, (ilits->chip->core_ver >> 16) & 0xFF, + (ilits->chip->core_ver >> 8) & 0xFF); + else + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "Core version = %d.%d.%d.%d\n", + (ilits->chip->core_ver >> 24) & 0xFF, (ilits->chip->core_ver >> 16) & 0xFF, + (ilits->chip->core_ver >> 8) & 0xFF, ilits->chip->core_ver & 0xFF); + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "Protocol version = %d.%d.%d\n", + ilits->protocol->ver >> 16, (ilits->protocol->ver >> 8) & 0xFF, + ilits->protocol->ver & 0xFF); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "Driver version = %s\n", DRIVER_VERSION); + + if (copy_to_user((char *)buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_node_change_list_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + u32 len = 0; + + if (*pos != 0) + return 0; + + mutex_lock(&ilits->touch_mutex); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "============= Change list ==============\n"); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "[Drive version] = %s\n", DRIVER_VERSION); + // len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "[Patch] 202001-0001\n"); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "========================================\n"); + + if (copy_to_user((char *)buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_proc_fw_process_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + u32 len = 0; + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + len = snprintf(g_user_buf, USER_STR_BUFF * sizeof(unsigned char), "%02d\n", ilits->fw_update_stat); + + ILI_INFO("update status = %d\n", ilits->fw_update_stat); + + if (copy_to_user((char *)buff, &ilits->fw_update_stat, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos = len; + return len; +} + +static ssize_t ilitek_node_fw_upgrade_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + int ret = 0, len = 2; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + + if (*pos != 0) + return 0; + + ILI_INFO("Prepar to upgarde firmware\n"); + + mutex_lock(&ilits->touch_mutex); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + ilits->force_fw_update = ENABLE; + ilits->node_update = true; + + ret = ili_fw_upgrade_handler(NULL); + + ilits->node_update = false; + ilits->force_fw_update = DISABLE; + + g_user_buf[0] = 0x0; + g_user_buf[1] = (ret < 0) ? -ret : ret; + + if (g_user_buf[1] == 0) + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Upgrade firmware = PASS"); + else + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Upgrade firmware = FAIL"); + + /* Reason for fail */ + if (g_user_buf[1] == EFW_CONVERT_FILE) { + g_user_buf[0] = 0xFF; + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to convert hex/ili file, abort!"); + } else if (g_user_buf[1] == ENOMEM) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to allocate memory, abort!"); + } else if (g_user_buf[1] == EFW_ICE_MODE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to operate ice mode, abort!"); + } else if (g_user_buf[1] == EFW_CRC) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "CRC not matched, abort!"); + } else if (g_user_buf[1] == EFW_REST) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to do reset, abort!"); + } else if (g_user_buf[1] == EFW_ERASE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to erase flash, abort!"); + } else if (g_user_buf[1] == EFW_PROGRAM) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to program flash, abort!"); + } else if (g_user_buf[1] == EFW_INTERFACE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to hex file interface no match, abort!"); + } + + if (copy_to_user((u32 *) buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_proc_debug_level_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + debug_en = !debug_en; + + ILI_INFO(" %s debug level = %x\n", debug_en ? "Enable" : "Disable", debug_en); + + size = snprintf(g_user_buf, USER_STR_BUFF * sizeof(unsigned char), "debug level : %s\n", debug_en ? "Enable" : "Disable"); + + *pos += size; + + if (copy_to_user((u32 *) buff, g_user_buf, size)) + ILI_ERR("Failed to copy data to user space\n"); + + return size; +} + +int ili_get_tp_recore_ctrl(int data) +{ + int ret = 0; + + switch((int)data) { + + case ENABLE_RECORD: + ILI_INFO("recore enable"); + ret = ili_ic_func_ctrl("tp_recore", 1); + mdelay(200); + break; + case DATA_RECORD: + mdelay(50); + ILI_INFO("Get data"); + ret = ili_ic_func_ctrl("tp_recore", 2); + if (ret < 0) { + ILI_ERR("cmd fail\n"); + goto out; + } + + ret = ili_get_tp_recore_data(); + if (ret < 0) + ILI_ERR("get data fail\n"); + + ILI_INFO("recore reset"); + ret = ili_ic_func_ctrl("tp_recore", 3); + if (ret < 0) { + ILI_ERR("cmd fail\n"); + goto out; + } + break; + case DISABLE_RECORD: + ILI_INFO("recore disable"); + ret = ili_ic_func_ctrl("tp_recore", 0); + break; + + } +out: + return ret; + +} + +int ili_get_tp_recore_data(void) +{ + u8 buf[8] = {0}, record_case = 0; + s8 index; + u16 *raw = NULL, *raw_ptr = NULL, frame_len = 0; + u32 base_addr = 0x20000, addr, len, *ptr, i, fcnt; + struct record_state record_stat; + bool ice = atomic_read(&ilits->ice_stat); + + if (ilits->wrapper(NULL, 0, buf, sizeof(buf), OFF, OFF) < 0) { + ILI_ERR("Get info fail\n"); + return -1; + } + + addr = ((buf[0] << 8) | buf[1]) + base_addr; + len = ((buf[2] << 8) | buf[3]); + index = buf[4]; + fcnt = buf[5]; + record_case = buf[6]; + ipio_memcpy(&record_stat, &buf[7], 1, 1); + ILI_INFO("addr = 0x%x, len = %d, lndex = 0x%d, fram num = %d, record_case = 0x%x\n", + addr, len, index, fcnt, record_case); + ili_dump_data(buf, 8, sizeof(buf), 0, "all record bytes"); + + raw = kcalloc(len, sizeof(u8), GFP_ATOMIC); + if (ERR_ALLOC_MEM(raw)) { + ILI_ERR("Failed to allocate packet memory, %ld\n", PTR_ERR(raw)); + return -1; + } + ptr = (u32*)raw; + + if (!ice) + ili_ice_mode_ctrl(ENABLE, ON); + + buf[0] = 0x25; + buf[3] = (char)((addr & 0x00FF0000) >> 16); + buf[2] = (char)((addr & 0x0000FF00) >> 8); + buf[1] = (char)((addr & 0x000000FF)); + + if (ilits->wrapper(buf, 4, NULL, 0, ON, OFF)) { + ILI_ERR("Failed to write iram data\n"); + return -ENODEV; + } + + if (ilits->wrapper(NULL, 0, (u8 *)raw, len, ON, OFF)) { + ILI_ERR("Failed to Read iram data\n"); + return -ENODEV; + } + + frame_len = (len / (fcnt * 2)); + for (i = 0; i < fcnt; i ++) { + raw_ptr = raw + (index * frame_len); + + ili_dump_data(raw_ptr, 16, frame_len, ilits->xch_num, "recore_data"); + index--; + if(index < 0) + index = fcnt - 1; + } + + if (!ice) + ili_ice_mode_ctrl(DISABLE, ON); + + if (record_case == 2) { + ILI_INFO("tp_palm_stat = %d\n", record_stat.touch_palm_state_e); + ILI_INFO("app_an_stat = %d\n", record_stat.app_an_statu_e); + ILI_INFO("app_check_abnor = %d\n", record_stat.app_sys_check_bg_abnormal); + ILI_INFO("wrong_bg = %d\n", record_stat.g_b_wrong_bg); + } + + ipio_kfree((void **)&raw); + + return 0; +} + +void ili_gesture_fail_reason(bool enable) +{ + + u8 cmd[24] = {0}; + + /* set symbol */ + if (ili_ic_func_ctrl("knock_en", 0x8) < 0) + ILI_ERR("set symbol failed"); + + /* enable gesture fail reason */ + cmd[0] = 0x01; + cmd[1] = 0x0A; + cmd[2] = 0x10; + if (enable) + cmd[3] = 0x01; + else + cmd[3] = 0x00; + cmd[4] = 0xFF; + cmd[5] = 0xFF; + if ((ilits->wrapper(cmd, 6, NULL, 0, ON, OFF)) < 0) + ILI_ERR("enable gesture fail reason failed"); + + /* set gesture parameters */ + cmd[0] = 0x01; + cmd[1] = 0x0A; + cmd[2] = 0x12; + cmd[3] = 0x01; + memset(cmd + 4, 0xFF, 20); + if ((ilits->wrapper(cmd, 24, NULL, 0, ON, OFF)) < 0) + ILI_ERR("set gesture parameters failed"); + + /* get gesture parameters */ + cmd[0] = 0x01; + cmd[1] = 0x0A; + cmd[2] = 0x11; + cmd[3] = 0x01; + if ((ilits->wrapper(cmd, 4, NULL, 0, ON, OFF)) < 0) + ILI_ERR("get gesture parameters failed"); + +} + +int ili_tp_data_mode_ctrl(u8* cmd) +{ + int ret = 0; + switch (cmd[0]) { + case AP_MODE: + if (ilits->tp_suspend) { + if (ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEMO, false, NULL) < 0) { + ILI_ERR("Failed to set demo len from gesture mode\n"); + ret = -ENOTTY; + } + } else { + if (ilits->actual_tp_mode == P5_X_FW_TEST_MODE) { + if (ili_switch_tp_mode(P5_X_FW_AP_MODE) < 0) { + ILI_ERR("Failed to switch demo mode\n"); + ret = -ENOTTY; + } + } else { + if (ili_set_tp_data_len(DATA_FORMAT_DEMO, false, NULL) < 0) { + ILI_ERR("Failed to switch demo mode\n"); + ret = -ENOTTY; + } + } + } + break; + case TEST_MODE: + if (ili_switch_tp_mode(P5_X_FW_TEST_MODE) < 0) { + ILI_ERR("Failed to switch test mode\n"); + ret = -ENOTTY; + } + break; + case DEBUG_MODE: + if (!ilits->tp_suspend && (ilits->actual_tp_mode != P5_X_FW_AP_MODE)) { + if (ili_switch_tp_mode(P5_X_FW_AP_MODE) < 0) { + ILI_ERR("Failed to switch demo mode\n"); + ret = -ENOTTY; + break; + } + } + if (ilits->tp_suspend) { + if (ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEBUG, false, NULL) < 0) { + ILI_ERR("Failed to set debug len from gesture mode\n"); + ret = -ENOTTY; + } + } else { + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG, false, NULL) < 0) { + ILI_ERR("Failed to set debug len\n"); + ret = -ENOTTY; + } + } + break; + case DEBUG_LITE_ROI: + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG_LITE_ROI, false, NULL) < 0) { + ILI_ERR("Failed to set debug lite roi from gesture mode\n"); + ret = -ENOTTY; + } + break; + case DEBUG_LITE_WINDOW: + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG_LITE_WINDOW, false, NULL) < 0) { + ILI_ERR("Failed to set debug lite window from gesture mode\n"); + ret = -ENOTTY; + } + break; + case DEBUG_LITE_AREA: + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG_LITE_AREA, false, &cmd[1]) < 0) { + ILI_ERR("Failed to set debug lite area from gesture mode\n"); + ret = -ENOTTY; + } + break; + default: + ILI_ERR("Unknown TP mode ctrl\n"); + ret = -ENOTTY; + break; + } + + return ret; +} + +static ssize_t ilitek_node_ioctl_write(struct file *filp, const char *buff, size_t size, loff_t *pos) +{ + int i, ret = 0, count = 0; + char cmd[512] = {0}; + char *token = NULL, *cur = NULL; + u8 temp[256] = {0}; + u32 *data = NULL; + + if ((size - 1) > sizeof(cmd)) { + ILI_ERR("ERROR! input length is larger than local buffer\n"); + return -1; + } + + mutex_lock(&ilits->touch_mutex); + + if (buff != NULL) { + if (copy_from_user(cmd, buff, size - 1)) { + ILI_INFO("Failed to copy data from user space\n"); + return -1; + } + } + + ILI_INFO("size = %d, cmd = %s\n", (int)size, cmd); + + token = cur = cmd; + + data = kcalloc(512, sizeof(u32), GFP_KERNEL); + if (ERR_ALLOC_MEM(data)) { + ILI_ERR("Failed to allocate data mem\n"); + size = -1; + goto out; + } + + while ((token = strsep(&cur, ",")) != NULL) { + data[count] = ili_str2hex(token); + ILI_INFO("data[%d] = %x\n", count, data[count]); + count++; + } + + ILI_INFO("cmd = %s\n", cmd); + + if (strncmp(cmd, "hwreset", strlen(cmd)) == 0) { + ili_reset_ctrl(TP_HW_RST_ONLY); + } else if (strcmp(cmd, "rawdatarecore") == 0) { + if (data[1] == ENABLE_RECORD) + ili_get_tp_recore_ctrl(ENABLE_RECORD); + else if (data[1] == DATA_RECORD) + ili_get_tp_recore_ctrl(DATA_RECORD); + else if (data[1] == DISABLE_RECORD) + ili_get_tp_recore_ctrl(DISABLE_RECORD); + } else if (strcmp(cmd, "switchdemodebuginfomode") == 0) { + ili_set_tp_data_len(DATA_FORMAT_DEMO_DEBUG_INFO, false, NULL); + } else if (strcmp(cmd, "gesturedemoen") == 0) { + if (data[1] == DISABLE) + ilits->gesture_demo_ctrl = DISABLE; + else + ilits->gesture_demo_ctrl = ENABLE; + ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEMO, false, NULL); + } else if (strcmp(cmd, "gesturefailrsn") == 0) { + if (data[1] == DISABLE) + ili_gesture_fail_reason(DISABLE); + else + ili_gesture_fail_reason(ENABLE); + ILI_INFO("%s gesture fail reason\n", data[1] ? "ENABLE" : "DISABLE"); + } else if (strncmp(cmd, "icwholereset", strlen(cmd)) == 0) { + ili_ice_mode_ctrl(ENABLE, OFF); + ili_reset_ctrl(TP_IC_WHOLE_RST); + } else if (strncmp(cmd, "iccodereset", strlen(cmd)) == 0) { + ili_ice_mode_ctrl(ENABLE, OFF); + ili_reset_ctrl(TP_IC_CODE_RST); + ili_ice_mode_ctrl(DISABLE, OFF); + } else if (strncmp(cmd, "enableirq", strlen(cmd)) == 0) { + ILI_INFO("Enable IRQ\n"); + ili_irq_enable(); + } else if (strncmp(cmd, "disableirq", strlen(cmd)) == 0) { + ILI_INFO("Disable IRQ\n"); + ili_irq_disable(); + } else if (strncmp(cmd, "infofromhex", strlen(cmd)) == 0) { + ilits->info_from_hex = data[1]; + ILI_INFO("info from hex = %d\n", data[1]); + } else if (strncmp(cmd, "getinfo", strlen(cmd)) == 0) { + ili_ice_mode_ctrl(ENABLE, OFF); + ili_ic_get_info(); + ili_ice_mode_ctrl(DISABLE, OFF); + ili_ic_get_protocl_ver(); + ili_ic_get_fw_ver(); + ili_ic_get_core_ver(); + ili_ic_get_tp_info(); + ili_ic_get_panel_info(); + ILI_INFO("Driver version = %s\n", DRIVER_VERSION); + ILI_INFO("TP module = %s\n", ilits->md_name); + } else if (strncmp(cmd, "enableicemode", strlen(cmd)) == 0) { + if (data[1] == ON) + ili_ice_mode_ctrl(ENABLE, ON); + else + ili_ice_mode_ctrl(ENABLE, OFF); + } else if (strncmp(cmd, "wqctrl", strlen(cmd)) == 0) { + ilits->wq_ctrl = !ilits->wq_ctrl; + ILI_INFO("wq_ctrl flag= %d\n", ilits->wq_ctrl); + } else if (strncmp(cmd, "disableicemode", strlen(cmd)) == 0) { + ili_ice_mode_ctrl(DISABLE, OFF); + } else if (strncmp(cmd, "enablewqesd", strlen(cmd)) == 0) { + ili_wq_ctrl(WQ_ESD, ENABLE); + } else if (strncmp(cmd, "enablewqbat", strlen(cmd)) == 0) { + ili_wq_ctrl(WQ_BAT, ENABLE); + } else if (strncmp(cmd, "disablewqesd", strlen(cmd)) == 0) { + ili_wq_ctrl(WQ_ESD, DISABLE); + } else if (strncmp(cmd, "disablewqbat", strlen(cmd)) == 0) { + ili_wq_ctrl(WQ_BAT, DISABLE); + } else if (strncmp(cmd, "gesture", strlen(cmd)) == 0) { + ilits->gesture = !ilits->gesture; + ILI_INFO("gesture = %d\n", ilits->gesture); + } else if (strncmp(cmd, "esdgesture", strlen(cmd)) == 0) { + if (ili_gesture_recovery() < 0) { + ILI_ERR("Gesture recovery failed\n"); + size = -1; + } + } else if (strncmp(cmd, "esdspi", strlen(cmd)) == 0) { + ili_spi_recovery(); + } else if (strncmp(cmd, "sleepin", strlen(cmd)) == 0) { + ili_ic_func_ctrl("sleep", SLEEP_IN); + } else if (strncmp(cmd, "deepsleepin", strlen(cmd)) == 0) { + ili_ic_func_ctrl("sleep", DEEP_SLEEP_IN); + } else if (strncmp(cmd, "iceflag", strlen(cmd)) == 0) { + if (data[1] == ENABLE) + atomic_set(&ilits->ice_stat, ENABLE); + else + atomic_set(&ilits->ice_stat, DISABLE); + ILI_INFO("ice mode flag = %d\n", atomic_read(&ilits->ice_stat)); + } else if (strncmp(cmd, "gesturenormal", strlen(cmd)) == 0) { + ilits->gesture_mode = DATA_FORMAT_GESTURE_NORMAL; + ILI_INFO("gesture mode = %d\n", ilits->gesture_mode); + } else if (strncmp(cmd, "gestureinfo", strlen(cmd)) == 0) { + ilits->gesture_mode = DATA_FORMAT_GESTURE_INFO; + ILI_INFO("gesture mode = %d\n", ilits->gesture_mode); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + } else if (strncmp(cmd, "netlink", strlen(cmd)) == 0) { + ilits->netlink = !ilits->netlink; + ILI_INFO("netlink flag= %d\n", ilits->netlink); +#endif + } else if (strncmp(cmd, "switchtestmode", strlen(cmd)) == 0) { + ili_switch_tp_mode(P5_X_FW_TEST_MODE); + } else if (strncmp(cmd, "switchdebugmode", strlen(cmd)) == 0) { + ili_set_tp_data_len(DATA_FORMAT_DEBUG, false, NULL); + } else if (strncmp(cmd, "switchdemomode", strlen(cmd)) == 0) { + ili_set_tp_data_len(DATA_FORMAT_DEMO, false, NULL); + } else if (strncmp(cmd, "switchgesturedebugmode", strlen(cmd)) == 0) { + ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEBUG, false, NULL); + } else if (strncmp(cmd, "dbgflag", strlen(cmd)) == 0) { + ilits->dnp = !ilits->dnp; + ILI_INFO("debug flag message = %d\n", ilits->dnp); + } else if (strncmp(cmd, "spiclk", strlen(cmd)) == 0) { + ILI_INFO("spi clk num = %d\n", data[1]); + ili_core_spi_setup(data[1]); + } else if (strncmp(cmd, "ss", strlen(cmd)) == 0) { + ILI_INFO("sense_stop = %d\n", data[1]); + ilits->ss_ctrl = data[1]; + } else if (strncmp(cmd, "iow", strlen(cmd)) == 0) { + int w_len = 0; + w_len = data[1]; + ILI_INFO("w_len = %d\n", w_len); + for (i = 0; i < w_len; i++) { + temp[i] = data[2 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + + ilits->wrapper(temp, w_len, NULL, 0, ON, OFF); + } else if (strncmp(cmd, "ior", strlen(cmd)) == 0) { + int r_len = 0; + r_len = data[1]; + ILI_INFO("r_len = %d\n", r_len); + ilits->wrapper(NULL, 0, temp, r_len, ON, OFF); + for (i = 0; i < r_len; i++) + ILI_INFO("read[%d] = %x\n", i, temp[i]); + } else if (strncmp(cmd, "iowr", strlen(cmd)) == 0) { + int delay = 0; + int w_len = 0, r_len = 0; + w_len = data[1]; + r_len = data[2]; + delay = data[3]; + ILI_INFO("w_len = %d, r_len = %d, delay = %d\n", w_len, r_len, delay); + + for (i = 0; i < w_len; i++) { + temp[i] = data[4 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + ret = ilits->wrapper(temp, w_len, NULL, 0, ON, OFF); + memset(temp, 0, sizeof(temp)); + mdelay(delay); + ilits->wrapper(NULL, 0, temp, r_len, ON, OFF); + for (i = 0; i < r_len; i++) + ILI_INFO("read[%d] = %x\n", i, temp[i]); + } else if (strcmp(cmd, "iowrint") == 0) { + int delay = 0; + int w_len = 0, r_len = 0; + w_len = data[1]; + r_len = data[2]; + delay = data[3]; + ILI_INFO("w_len = %d, r_len = %d, delay = %d\n", w_len, r_len, delay); + for (i = 0; i < w_len; i++) { + temp[i] = data[4 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + ret = ilits->wrapper(temp, w_len, temp, r_len, ON, ON); + for (i = 0; i < r_len; i++) + ILI_INFO("read[%d] = %x\n", i, temp[i]); + } else if (strncmp(cmd, "getddiregdata", strlen(cmd)) == 0) { + u8 ddi_data = 0; + ILI_INFO("Get ddi reg one page: page = %x, reg = %x\n", data[1], data[2]); + ili_ic_get_ddi_reg_onepage(data[1], data[2], &ddi_data); + ILI_INFO("ddi_data = %x\n", ddi_data); + } else if (strncmp(cmd, "setddiregdata", strlen(cmd)) == 0) { + ILI_INFO("Set ddi reg one page: page = %x, reg = %x, data = %x\n", data[1], data[2], data[3]); + ili_ic_set_ddi_reg_onepage(data[1], data[2], data[3]); + } else if (strncmp(cmd, "dumpflashdata", strlen(cmd)) == 0) { + ilits->fw_update_stat = FW_STAT_INIT; + ILI_INFO("Start = 0x%x, End = 0x%x, Dump Hex path = %s\n", data[1], data[2], DUMP_FLASH_PATH); + ret = ili_fw_dump_flash_data(data[1], data[2], false); + ilits->fw_update_stat = (ret < 0) ? FW_UPDATE_FAIL : FW_UPDATE_PASS; + ILI_INFO("ilits->fw_update_stat = %d\n", ilits->fw_update_stat); + } else if (strncmp(cmd, "dumpiramdata", strlen(cmd)) == 0) { + ILI_INFO("Start = 0x%x, End = 0x%x, Dump IRAM path = %s\n", data[1], data[2], DUMP_IRAM_PATH); + ilits->fw_update_stat = FW_STAT_INIT; + ret = ili_fw_dump_iram_data(data[1], data[2], true); + ilits->fw_update_stat = (ret < 0) ? FW_UPDATE_FAIL : FW_UPDATE_PASS; + ILI_INFO("ilits->fw_update_stat = %d\n", ilits->fw_update_stat); + } else if (strncmp(cmd, "edge_palm_ctrl", strlen(cmd)) == 0) { + ili_ic_func_ctrl("edge_palm", data[1]); + } else if (strncmp(cmd, "uart_mode_ctrl", strlen(cmd)) == 0) { + ili_fw_uart_ctrl(data[1]); + } else if (strncmp(cmd, "flashesdgesture", strlen(cmd)) == 0) { + ili_touch_esd_gesture_flash(); + } else if (strncmp(cmd, "intact", strlen(cmd)) == 0) { + ILI_INFO("Set INT trigger = %x\n", data[1]); + ili_ic_int_trigger_ctrl(data[1]); + } else if (strncmp(cmd, "spiw", strlen(cmd)) == 0) { + int wlen; + wlen = data[1]; + temp[0] = 0x82; + for (i = 0; i < wlen; i++) { + temp[i] = data[2 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + ilits->spi_write_then_read(ilits->spi, temp, wlen, NULL, 0); + } else if (strncmp(cmd, "spir", strlen(cmd)) == 0) { + int rlen; + u8 *rbuf = NULL; + rlen = data[1]; + rbuf = kzalloc(rlen, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(rbuf)) { + ILI_ERR("Failed to allocate dma_rxbuf, %ld\n", PTR_ERR(rbuf)); + kfree(rbuf); + goto out; + } + temp[0] = 0x83; + ilits->spi_write_then_read(ilits->spi, temp, 1, rbuf, rlen); + for (i = 0; i < rlen; i++) + ILI_INFO("read[%d] = %x\n", i, rbuf[i]); + kfree(rbuf); + } else if (strncmp(cmd, "spirw", strlen(cmd)) == 0) { + int wlen, rlen; + u8 *rbuf = NULL; + wlen = data[1]; + rlen = data[2]; + for (i = 0; i < wlen; i++) { + temp[i] = data[3 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + if (rlen != 0) { + rbuf = kzalloc(rlen, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(rbuf)) { + ILI_ERR("Failed to allocate dma_rxbuf, %ld\n", PTR_ERR(rbuf)); + kfree(rbuf); + goto out; + } + } + ilits->spi_write_then_read(ilits->spi, temp, wlen, rbuf, rlen); + if (rlen != 0) { + for (i = 0; i < rlen; i++) + ILI_INFO("read[%d] = %x\n", i, rbuf[i]); + } + kfree(rbuf); + } else if (strncmp(cmd, "ges_sym", strlen(cmd)) == 0) { + struct gesture_symbol *ptr_sym = &ilits->ges_sym; + u8 *ptr; + ptr = (u8*) ptr_sym; + ptr[0] = data[1]; + ptr[1] = data[2]; + ptr[2] = data[3]; + ili_dump_data(ptr, 8, 3, 0, "Gesture symbol"); + } else if (strncmp(cmd, "mode", strlen(cmd)) == 0) { + temp[0] = data[1]; + temp[1] = data[2]; + temp[2] = data[3]; + temp[3] = data[4]; + temp[4] = data[5]; + ret = ili_tp_data_mode_ctrl(temp); + } else if (strncmp(cmd, "switchmpretry", strlen(cmd)) == 0) { + ilits->mp_retry = !ilits->mp_retry; + ILI_INFO("switchmpretry = %d\n", ilits->mp_retry); + } else { + ILI_ERR("Unknown command\n"); + size = -1; + } +out: + ipio_kfree((void **)&data); + mutex_unlock(&ilits->touch_mutex); + return size; +} + +#ifdef CONFIG_COMPAT +static long ilitek_node_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + long ret = 0; + + if (!filp->f_op || !filp->f_op->unlocked_ioctl) { + ILI_ERR("There's no unlocked_ioctl defined in file\n"); + return -ENOTTY; + } + + ILI_DBG("cmd = %d\n", _IOC_NR(cmd)); + + switch (cmd) { + case ILITEK_COMPAT_IOCTL_I2C_WRITE_DATA: + ILI_DBG("compat_ioctl: convert i2c/spi write\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_I2C_WRITE_DATA, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_I2C_READ_DATA: + ILI_DBG("compat_ioctl: convert i2c/spi read\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_I2C_READ_DATA, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_I2C_SET_WRITE_LENGTH: + ILI_DBG("compat_ioctl: convert set write length\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_I2C_SET_WRITE_LENGTH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_I2C_SET_READ_LENGTH: + ILI_DBG("compat_ioctl: convert set read length\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_I2C_SET_READ_LENGTH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_HW_RESET: + ILI_DBG("compat_ioctl: convert hw reset\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_HW_RESET, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_POWER_SWITCH: + ILI_DBG("compat_ioctl: convert power switch\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_POWER_SWITCH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_REPORT_SWITCH: + ILI_DBG("compat_ioctl: convert report switch\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_REPORT_SWITCH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_IRQ_SWITCH: + ILI_DBG("compat_ioctl: convert irq switch\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_IRQ_SWITCH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_DEBUG_LEVEL: + ILI_DBG("compat_ioctl: convert debug level\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_DEBUG_LEVEL, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_FUNC_MODE: + ILI_DBG("compat_ioctl: convert format mode\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_FUNC_MODE, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_FW_VER: + ILI_DBG("compat_ioctl: convert set read length\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_FW_VER, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_PL_VER: + ILI_DBG("compat_ioctl: convert fw version\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_PL_VER, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_CORE_VER: + ILI_DBG("compat_ioctl: convert core version\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_CORE_VER, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_DRV_VER: + ILI_DBG("compat_ioctl: convert driver version\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_DRV_VER, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_CHIP_ID: + ILI_DBG("compat_ioctl: convert chip id\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_CHIP_ID, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_NETLINK_CTRL: + ILI_DBG("compat_ioctl: convert netlink ctrl\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_NETLINK_CTRL, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_NETLINK_STATUS: + ILI_DBG("compat_ioctl: convert netlink status\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_NETLINK_STATUS, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_MODE_CTRL: + ILI_DBG("compat_ioctl: convert tp mode ctrl\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_MODE_CTRL, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_MODE_STATUS: + ILI_DBG("compat_ioctl: convert tp mode status\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_MODE_STATUS, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_ICE_MODE_SWITCH: + ILI_DBG("compat_ioctl: convert tp mode switch\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_ICE_MODE_SWITCH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_INTERFACE_TYPE: + ILI_DBG("compat_ioctl: convert interface type\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_INTERFACE_TYPE, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_DUMP_FLASH: + ILI_DBG("compat_ioctl: convert dump flash\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_DUMP_FLASH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_FW_UART_CTRL: + ILI_DBG("compat_ioctl: convert fw uart\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_FW_UART_CTRL, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_PANEL_INFO: + ILI_DBG("compat_ioctl: convert resolution\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_PANEL_INFO, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_INFO: + ILI_DBG("compat_ioctl: convert tp info\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_INFO, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_WRAPPER_RW: + ILI_DBG("compat_ioctl: convert wrapper\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_WRAPPER_RW, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_DDI_WRITE: + ILI_DBG("compat_ioctl: convert ddi write\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_DDI_WRITE, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_DDI_READ: + ILI_DBG("compat_ioctl: convert ddi read\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_DDI_READ, (unsigned long)compat_ptr(arg)); + return ret; + default: + ILI_ERR("no ioctl cmd, return ilitek_node_ioctl\n"); + return -ENOIOCTLCMD; + } +} +#endif + +static long ilitek_node_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = 0, length = 0; + u8 *szBuf = NULL, if_to_user = 0; + static u16 i2c_rw_length; + u32 id_to_user[64] = {0}; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + u8 *wrap_rbuf = NULL, wrap_int = 0; + u16 wrap_wlen = 0, wrap_rlen = 0; + bool spi_irq, i2c_irq; + + if (_IOC_TYPE(cmd) != ILITEK_IOCTL_MAGIC) { + ILI_ERR("The Magic number doesn't match\n"); + return -ENOTTY; + } + + if (_IOC_NR(cmd) > ILITEK_IOCTL_MAXNR) { + ILI_ERR("The number of ioctl doesn't match\n"); + return -ENOTTY; + } + + ILI_DBG("cmd = %d\n", _IOC_NR(cmd)); + + mutex_lock(&ilits->touch_mutex); + + szBuf = kcalloc(IOCTL_I2C_BUFF, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(szBuf)) { + ILI_ERR("Failed to allocate mem\n"); + ret = -ENOMEM; + goto out; + } + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + switch (cmd) { + case ILITEK_IOCTL_I2C_WRITE_DATA: + ILI_DBG("ioctl: write len = %d\n", i2c_rw_length); + + if (i2c_rw_length > IOCTL_I2C_BUFF) { + ILI_ERR("ERROR! write len is largn than ioctl buf (%d, %ld)\n", + i2c_rw_length, IOCTL_I2C_BUFF); + ret = -ENOTTY; + break; + } + + if (copy_from_user(szBuf, (u8 *) arg, i2c_rw_length)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + ret = ilits->wrapper(szBuf, i2c_rw_length, NULL, 0, OFF, OFF); + if (ret < 0) + ILI_ERR("Failed to write data\n"); + break; + case ILITEK_IOCTL_I2C_READ_DATA: + ILI_DBG("ioctl: read len = %d\n", i2c_rw_length); + + if (i2c_rw_length > IOCTL_I2C_BUFF) { + ILI_ERR("ERROR! read len is largn than ioctl buf (%d, %ld)\n", + i2c_rw_length, IOCTL_I2C_BUFF); + ret = -ENOTTY; + break; + } + + ret = ilits->wrapper(NULL, 0, szBuf, i2c_rw_length, OFF, OFF); + if (ret < 0) { + ILI_ERR("Failed to read data\n"); + break; + } + + if (copy_to_user((u8 *) arg, szBuf, i2c_rw_length)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_I2C_SET_WRITE_LENGTH: + case ILITEK_IOCTL_I2C_SET_READ_LENGTH: + i2c_rw_length = arg; + break; + case ILITEK_IOCTL_TP_HW_RESET: + ILI_DBG("ioctl: hw reset\n"); + ili_reset_ctrl(ilits->reset); + break; + case ILITEK_IOCTL_TP_POWER_SWITCH: + ILI_INFO("Not implemented yet\n"); + break; + case ILITEK_IOCTL_TP_REPORT_SWITCH: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + ILI_DBG("ioctl: report switch = %d\n", szBuf[0]); + if (szBuf[0]) { + ilits->report = ENABLE; + ILI_DBG("report is enabled\n"); + } else { + ilits->report = DISABLE; + ILI_DBG("report is disabled\n"); + } + break; + case ILITEK_IOCTL_TP_IRQ_SWITCH: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: irq switch = %d\n", szBuf[0]); + if (szBuf[0]) + ili_irq_enable(); + else + ili_irq_disable(); + break; + case ILITEK_IOCTL_TP_DEBUG_LEVEL: + if (copy_from_user(szBuf, (u8 *) arg, sizeof(u32))) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + debug_en = !debug_en; + ILI_DBG("debug_en = %d", debug_en); + break; + case ILITEK_IOCTL_TP_FUNC_MODE: + if (copy_from_user(szBuf, (u8 *) arg, 3)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + ILI_DBG("ioctl: set func mode = %x,%x,%x\n", szBuf[0], szBuf[1], szBuf[2]); + ret = ilits->wrapper(szBuf, 3, NULL, 0, ON, OFF); + break; + case ILITEK_IOCTL_TP_FW_VER: + ILI_DBG("ioctl: get fw and mp version\n"); + szBuf[7] = ilits->chip->fw_mp_ver & 0xFF; + szBuf[6] = (ilits->chip->fw_mp_ver >> 8) & 0xFF; + szBuf[5] = (ilits->chip->fw_mp_ver >> 16) & 0xFF; + szBuf[4] = ilits->chip->fw_mp_ver >> 24; + szBuf[3] = ilits->chip->fw_ver & 0xFF; + szBuf[2] = (ilits->chip->fw_ver >> 8) & 0xFF; + szBuf[1] = (ilits->chip->fw_ver >> 16) & 0xFF; + szBuf[0] = ilits->chip->fw_ver >> 24; + ILI_DBG("Firmware version = %d.%d.%d.%d\n", szBuf[0], szBuf[1], szBuf[2], szBuf[3]); + ILI_DBG("Firmware MP version = %d.%d.%d.%d\n", szBuf[4], szBuf[5], szBuf[6], szBuf[7]); + + if (copy_to_user((u8 *) arg, szBuf, 8)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_PL_VER: + ILI_DBG("ioctl: get protocl version\n"); + szBuf[2] = ilits->protocol->ver & 0xFF; + szBuf[1] = (ilits->protocol->ver >> 8) & 0xFF; + szBuf[0] = ilits->protocol->ver >> 16; + ILI_DBG("Protocol version = %d.%d.%d\n", szBuf[0], szBuf[1], szBuf[2]); + + if (copy_to_user((u8 *) arg, szBuf, 3)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_CORE_VER: + ILI_DBG("ioctl: get core version\n"); + if (ilits->protocol->core_ver_len == P5_X_CORE_VER_THREE_LENGTH) + szBuf[3] = 0xFF; + else + szBuf[3] = ilits->chip->core_ver & 0xFF; + szBuf[2] = (ilits->chip->core_ver >> 8) & 0xFF; + szBuf[1] = (ilits->chip->core_ver >> 16) & 0xFF; + szBuf[0] = ilits->chip->core_ver >> 24; + ILI_DBG("Core version = %d.%d.%d.%d, length = %d\n", + szBuf[0], szBuf[1], szBuf[2], szBuf[3], ilits->protocol->core_ver_len); + + if (copy_to_user((u8 *) arg, szBuf, 4)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_DRV_VER: + ILI_DBG("ioctl: get driver version\n"); + length = snprintf(szBuf, USER_STR_BUFF * sizeof(unsigned char), "%s", DRIVER_VERSION); + + if (copy_to_user((u8 *) arg, szBuf, length)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_CHIP_ID: + ILI_DBG("ioctl: get chip id\n"); + id_to_user[0] = ilits->chip->pid; + id_to_user[1] = ilits->chip->otp_id; + id_to_user[2] = ilits->chip->ana_id; + + if (copy_to_user((u32 *) arg, id_to_user, sizeof(u32) * 3)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + case ILITEK_IOCTL_TP_NETLINK_CTRL: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: netlink ctrl = %d\n", szBuf[0]); + if (szBuf[0]) { + ilits->netlink = ENABLE; + ILI_DBG("ioctl: Netlink is enabled\n"); + } else { + ilits->netlink = DISABLE; + ILI_DBG("ioctl: Netlink is disabled\n"); + } + break; + case ILITEK_IOCTL_TP_NETLINK_STATUS: + ILI_DBG("ioctl: get netlink stat = %d\n", ilits->netlink); + if (copy_to_user((int *)arg, &ilits->netlink, sizeof(int))) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; +#endif + case ILITEK_IOCTL_TP_MODE_CTRL: + if (copy_from_user(szBuf, (u8 *) arg, 10)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: switch fw format = %d\n", szBuf[0]); + ret = ili_tp_data_mode_ctrl(szBuf); + + break; + case ILITEK_IOCTL_TP_MODE_STATUS: + ILI_DBG("ioctl: current firmware mode = %d", ilits->actual_tp_mode); + if (copy_to_user((int *)arg, &ilits->actual_tp_mode, sizeof(int))) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + /* It works for host downloado only */ + case ILITEK_IOCTL_ICE_MODE_SWITCH: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + ILI_DBG("ioctl: switch ice mode = %d, mcu on = %d\n", szBuf[0], szBuf[1]); + + if (ili_ice_mode_ctrl(szBuf[0] ? ENABLE : DISABLE, + szBuf[1] ? ENABLE : DISABLE) < 0) + ILI_ERR("ioctl: set ice mode failed\n"); + + break; + case ILITEK_IOCTL_TP_INTERFACE_TYPE: + if_to_user = ilits->hwif->bus_type; + if (copy_to_user((u8 *) arg, &if_to_user, sizeof(if_to_user))) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_DUMP_FLASH: + ILI_DBG("ioctl: dump flash data\n"); + ret = ili_fw_dump_flash_data(0, 0, true); + if (ret < 0) { + ILI_ERR("ioctl: Failed to dump flash data\n"); + } + break; + case ILITEK_IOCTL_TP_FW_UART_CTRL: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: fw UART = %d\n", szBuf[0]); + + ili_fw_uart_ctrl(szBuf[0]); + + if_to_user = szBuf[0]; + + if (copy_to_user((u8 *) arg, &if_to_user, sizeof(if_to_user))) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_PANEL_INFO: + ILI_DBG("ioctl: get panel resolution\n"); + id_to_user[0] = ilits->panel_wid; + id_to_user[1] = ilits->panel_hei; + id_to_user[2] = (ilits->trans_xy) ? 0x1 : 0x0; + + if (copy_to_user((u32 *) arg, id_to_user, sizeof(u32) * 3)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_INFO: + ILI_DBG("ioctl: get tp info\n"); + id_to_user[0] = ilits->min_x; + id_to_user[1] = ilits->min_y; + id_to_user[2] = ilits->max_x; + id_to_user[3] = ilits->max_y; + id_to_user[4] = ilits->xch_num; + id_to_user[5] = ilits->ych_num; + id_to_user[6] = ilits->stx; + id_to_user[7] = ilits->srx; + + if (copy_to_user((u32 *) arg, id_to_user, sizeof(u32) * 8)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_WRAPPER_RW: + ILI_DBG("ioctl: wrapper rw\n"); + + if (i2c_rw_length > IOCTL_I2C_BUFF || i2c_rw_length == 0) { + ILI_ERR("ERROR! i2c_rw_length is is invalid\n"); + ret = -ENOTTY; + break; + } + + if (copy_from_user(szBuf, (u8 *) arg, i2c_rw_length)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + wrap_int = szBuf[0]; + wrap_rlen = (szBuf[1] << 8) | szBuf[2]; + wrap_wlen = (szBuf[3] << 8) | szBuf[4]; + + ILI_DBG("wrap_int = %d\n", wrap_int); + ILI_DBG("wrap_rlen = %d\n", wrap_rlen); + ILI_DBG("wrap_wlen = %d\n", wrap_wlen); + + if (wrap_wlen > IOCTL_I2C_BUFF || wrap_rlen > IOCTL_I2C_BUFF) { + ILI_ERR("ERROR! R/W len is largn than ioctl buf\n"); + ret = -ENOTTY; + break; + } + + if (wrap_rlen > 0) { + wrap_rbuf = kcalloc(IOCTL_I2C_BUFF, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(wrap_rbuf)) { + ILI_ERR("Failed to allocate mem\n"); + ret = -ENOMEM; + break; + } + } + + if (wrap_int == 1) { + i2c_irq = ON; + spi_irq = ON; + } else if (wrap_int == 2) { + i2c_irq = OFF; + spi_irq = OFF; + } else { + i2c_irq= OFF; + spi_irq = (wrap_rlen > 0 ? ON : OFF); + } + + ILI_DBG("i2c_irq = %d, spi_irq = %d\n", i2c_irq, spi_irq); + + ilits->wrapper(szBuf + 5, wrap_wlen, wrap_rbuf, wrap_rlen, spi_irq, i2c_irq); + + ili_dump_data(szBuf + 5, 8, wrap_wlen, 16, "wrap_wbuf:"); + ili_dump_data(wrap_rbuf, 8, wrap_rlen, 16, "wrap_rbuf:"); + if (copy_to_user((u8 *) arg, wrap_rbuf, wrap_rlen)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_DDI_WRITE: + if (copy_from_user(szBuf, (u8 *) arg, 3)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: page = %x, reg = %x, data = %x\n", szBuf[0], szBuf[1], szBuf[2]); + ili_ic_set_ddi_reg_onepage(szBuf[0], szBuf[1], szBuf[2]); + break; + case ILITEK_IOCTL_DDI_READ: + if (copy_from_user(szBuf, (u8 *) arg, 2)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: page = %x, reg = %x\n", szBuf[0], szBuf[1]); + ili_ic_get_ddi_reg_onepage(szBuf[0], szBuf[1], &szBuf[2]); + ILI_DBG("ioctl: data = %x\n", szBuf[2]); + if (copy_to_user((u8 *) arg, szBuf + 2, 1)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + default: + ret = -ENOTTY; + break; + } + + ipio_kfree((void **)&szBuf); + ipio_kfree((void **)&wrap_rbuf); + +out: + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + mutex_unlock(&ilits->touch_mutex); + return ret; +} + +static struct proc_dir_entry *proc_dir_ilitek; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +typedef struct { + char *name; + struct proc_dir_entry *node; + struct proc_ops *fops; + bool isCreated; +} proc_node; +#else +typedef struct { + char *name; + struct proc_dir_entry *node; + struct file_operations *fops; + bool isCreated; +} proc_node; +#endif +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_mp_lcm_on_test_fops = { + .proc_read = ilitek_node_mp_lcm_on_test_read, +}; +#else +static struct file_operations proc_mp_lcm_on_test_fops = { + .read = ilitek_node_mp_lcm_on_test_read, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_mp_lcm_off_test_fops = { + .proc_read = ilitek_node_mp_lcm_off_test_read, +}; +#else +static struct file_operations proc_mp_lcm_off_test_fops = { + .read = ilitek_node_mp_lcm_off_test_read, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_ver_info_fops = { + .proc_read = ilitek_node_ver_info_read, +}; +#else +static struct file_operations proc_ver_info_fops = { + .read = ilitek_node_ver_info_read, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_change_list_fops = { + .proc_read = ilitek_node_change_list_read, +}; +#else +static struct file_operations proc_change_list_fops = { + .read = ilitek_node_change_list_read, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_debug_message_fops = { + .proc_read = ilitek_proc_debug_message_read, +}; +#else +static struct file_operations proc_debug_message_fops = { + .read = ilitek_proc_debug_message_read, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_debug_message_switch_fops = { + .proc_read = ilitek_proc_debug_switch_read, +}; +#else +static struct file_operations proc_debug_message_switch_fops = { + .read = ilitek_proc_debug_switch_read, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_ioctl_fops = { + .proc_ioctl = ilitek_node_ioctl, +#ifdef CONFIG_COMPAT + .proc_compat_ioctl = ilitek_node_compat_ioctl, +#endif + .proc_write = ilitek_node_ioctl_write, +}; +#else +static struct file_operations proc_ioctl_fops = { + .unlocked_ioctl = ilitek_node_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ilitek_node_compat_ioctl, +#endif + .write = ilitek_node_ioctl_write, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_fw_upgrade_fops = { + .proc_read = ilitek_node_fw_upgrade_read, +}; +#else +static struct file_operations proc_fw_upgrade_fops = { + .read = ilitek_node_fw_upgrade_read, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_fw_process_fops = { + .proc_read = ilitek_proc_fw_process_read, +}; +#else +static struct file_operations proc_fw_process_fops = { + .read = ilitek_proc_fw_process_read, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_get_delta_data_fops = { + .proc_read = ilitek_proc_get_delta_data_read, +}; +#else +static struct file_operations proc_get_delta_data_fops = { + .read = ilitek_proc_get_delta_data_read, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_get_raw_data_fops = { + .proc_read = ilitek_proc_fw_get_raw_data_read, +}; +#else +static struct file_operations proc_get_raw_data_fops = { + .read = ilitek_proc_fw_get_raw_data_read, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_rw_tp_reg_fops = { + .proc_read = ilitek_proc_rw_tp_reg_read, + .proc_write = ilitek_proc_rw_tp_reg_write, +}; +#else +static struct file_operations proc_rw_tp_reg_fops = { + .read = ilitek_proc_rw_tp_reg_read, + .write = ilitek_proc_rw_tp_reg_write, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_passive_pen_ctrl_fops = { + .proc_write = ilitek_proc_passive_pen_write, +}; +static struct proc_ops proc_canvas_ctrl_fops = { + .proc_write = ilitek_proc_canvas_write, +}; +#else +static struct file_operations proc_passive_pen_ctrl_fops = { + .write = ilitek_proc_passive_pen_write, +}; +static struct file_operations proc_canvas_ctrl_fops = { + .write = ilitek_proc_canvas_write, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_fw_pc_counter_fops = { + .proc_read = ilitek_proc_fw_pc_counter_read, +}; +#else +static struct file_operations proc_fw_pc_counter_fops = { + .read = ilitek_proc_fw_pc_counter_read, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_get_debug_mode_data_fops = { + .proc_read = ilitek_proc_get_debug_mode_data_read, + .proc_write = ilitek_proc_get_debug_mode_data_write, +}; +#else +static struct file_operations proc_get_debug_mode_data_fops = { + .read = ilitek_proc_get_debug_mode_data_read, + .write = ilitek_proc_get_debug_mode_data_write, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_debug_level_fops = { + .proc_read = ilitek_proc_debug_level_read, +}; +#else +static struct file_operations proc_debug_level_fops = { + .read = ilitek_proc_debug_level_read, +}; +#endif + +proc_node iliproc[] = { + {"ioctl", NULL, &proc_ioctl_fops, false}, + {"fw_process", NULL, &proc_fw_process_fops, false}, + {"fw_upgrade", NULL, &proc_fw_upgrade_fops, false}, + {"debug_level", NULL, &proc_debug_level_fops, false}, + {"mp_lcm_on_test", NULL, &proc_mp_lcm_on_test_fops, false}, + {"mp_lcm_off_test", NULL, &proc_mp_lcm_off_test_fops, false}, + {"debug_message", NULL, &proc_debug_message_fops, false}, + {"debug_message_switch", NULL, &proc_debug_message_switch_fops, false}, + {"fw_pc_counter", NULL, &proc_fw_pc_counter_fops, false}, + {"show_delta_data", NULL, &proc_get_delta_data_fops, false}, + {"show_raw_data", NULL, &proc_get_raw_data_fops, false}, + {"get_debug_mode_data", NULL, &proc_get_debug_mode_data_fops, false}, + {"rw_tp_reg", NULL, &proc_rw_tp_reg_fops, false}, + {"ver_info", NULL, &proc_ver_info_fops, false}, + {"change_list", NULL, &proc_change_list_fops, false}, + {"passive_pen_ctrl", NULL, &proc_passive_pen_ctrl_fops, false}, + {"canvas_ctrl", NULL, &proc_canvas_ctrl_fops, false}, + +}; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) +#define NETLINK_USER 21 +static struct sock *netlink_skb; +static struct nlmsghdr *netlink_head; +static struct sk_buff *skb_out; +static int netlink_pid; + +void ili_netlink_reply_msg(void *raw, int size) +{ + int ret; + int msg_size = size; + u8 *data = (u8 *) raw; + + ILI_INFO("The size of data being sent to user = %d\n", msg_size); + ILI_INFO("pid = %d\n", netlink_pid); + ILI_INFO("Netlink is enable = %d\n", ilits->netlink); + + if (ilits->netlink) { + skb_out = nlmsg_new(msg_size, 0); + + if (!skb_out) { + ILI_ERR("Failed to allocate new skb\n"); + return; + } + + netlink_head = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0); + NETLINK_CB(skb_out).dst_group = 0; /* not in mcast group */ + + /* strncpy(NLMSG_DATA(netlink_head), data, msg_size); */ + ipio_memcpy(nlmsg_data(netlink_head), data, msg_size, size); + + ret = nlmsg_unicast(netlink_skb, skb_out, netlink_pid); + if (ret < 0) + ILI_ERR("Failed to send data back to user\n"); + } +} + +static void netlink_recv_msg(struct sk_buff *skb) +{ + netlink_pid = 0; + + ILI_INFO("Netlink = %d\n", ilits->netlink); + + netlink_head = (struct nlmsghdr *)skb->data; + + ILI_INFO("Received a request from client: %s, %d\n", + (char *)NLMSG_DATA(netlink_head), (int)strlen((char *)NLMSG_DATA(netlink_head))); + + /* pid of sending process */ + netlink_pid = netlink_head->nlmsg_pid; + + ILI_INFO("the pid of sending process = %d\n", netlink_pid); + + /* TODO: may do something if there's not receiving msg from user. */ + if (netlink_pid != 0) { + ILI_ERR("The channel of Netlink has been established successfully !\n"); + ilits->netlink = ENABLE; + } else { + ILI_ERR("Failed to establish the channel between kernel and user space\n"); + ilits->netlink = DISABLE; + } +} + +static int netlink_init(void) +{ + int ret = 0; + +#if KERNEL_VERSION(3, 4, 0) > LINUX_VERSION_CODE + netlink_skb = netlink_kernel_create(&init_net, NETLINK_USER, netlink_recv_msg, NULL, THIS_MODULE); +#else + struct netlink_kernel_cfg cfg = { + .input = netlink_recv_msg, + }; + + netlink_skb = netlink_kernel_create(&init_net, NETLINK_USER, &cfg); +#endif + + ILI_INFO("Initialise Netlink and create its socket\n"); + + if (!netlink_skb) { + ILI_ERR("Failed to create nelink socket\n"); + ret = -EFAULT; + } + return ret; +} +#endif + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +static ssize_t path_show(struct device *pDevice, struct device_attribute *pAttr, char *pBuf) +{ + ssize_t blen; + const char *path; + + path = kobject_get_path(&ilits->spi->dev.kobj, GFP_KERNEL); + blen = scnprintf(pBuf, PAGE_SIZE, "%s\n", path ? path : "na"); + kfree(path); + + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ILI_INFO("*** %s() vendor = %s ***\n", __func__, "ilitek"); + return scnprintf(buf, PAGE_SIZE, "ilitek"); +} + +/* Attribute: ic_ver (RO) */ +static ssize_t ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s%s\n%s%d%d%d%d\n%s%04x\n", + "Product ID: ", ilits->md_name, + "Build ID: ", ilits->chip->fw_ver >> 24, (ilits->chip->fw_ver >> 16) & 0xFF, + (ilits->chip->fw_ver >> 8) & 0xFF, ilits->chip->fw_ver & 0xFF, + "Config ID: ", ilits->chip->core_ver); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_RO(ic_ver), + __ATTR_NULL +}; + +int ilitek_sys_init(void) +{ + int i; + s32 ret = 0; + dev_t devno; + struct device_attribute *attrs = touchscreen_attributes; + + ret = alloc_chrdev_region(&devno, 0, 1, ILI_SPI_NAME); + if (ret) { + ILI_ERR ("can't allocate chrdev\n"); + return ret; + } else { + + /* set sysfs for firmware */ + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + ret = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + ILI_ERR("Failed to create touchscreen class!\n"); + return ret; + } + + if((ilits->tp_module >= MODEL_TM) && (ilits->tp_module < MODEL_TM_END)) + touchscreen_class_dev = device_create(touchscreen_class, NULL, devno, NULL, ILI_SPI_NAME_TM); + else if((ilits->tp_module >= MODEL_CSOT) && (ilits->tp_module < MODEL_CSOT_END)) + touchscreen_class_dev = device_create(touchscreen_class, NULL, devno, NULL, ILI_SPI_NAME_CSOT); + else if((ilits->tp_module >= MODEL_TXD) && (ilits->tp_module < MODEL_TXD_END)) + touchscreen_class_dev = device_create(touchscreen_class, NULL, devno, NULL, ILI_SPI_NAME_TXD); + else + touchscreen_class_dev = device_create(touchscreen_class, NULL, devno, NULL, ILI_SPI_NAME); + + pr_info(" touchscreen_class_dev = %p \n", touchscreen_class_dev); + if (IS_ERR(touchscreen_class_dev)) { + ret = PTR_ERR(touchscreen_class_dev); + touchscreen_class_dev = NULL; + ILI_ERR("Failed to create device(touchscreen_class_dev)!\n"); + return ret; + } + ILI_INFO("Succeed to create device(touchscreen_class_dev)!\n"); + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + ret = device_create_file(touchscreen_class_dev, &attrs[i]); + if (ret < 0) + goto device_destroy; + } + } + + return ret; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(touchscreen_class_dev, &attrs[i]); + + touchscreen_class_dev = NULL; + class_unregister(touchscreen_class); + ILI_ERR("error creating touchscreen class\n"); + + return -ENODEV; +} +#endif + +void ili_node_init(void) +{ + int i = 0, ret = 0; + + proc_dir_ilitek = proc_mkdir("ilitek", NULL); + + for (; i < ARRAY_SIZE(iliproc); i++) { + iliproc[i].node = proc_create(iliproc[i].name, 0644, proc_dir_ilitek, iliproc[i].fops); + + if (iliproc[i].node == NULL) { + iliproc[i].isCreated = false; + ILI_ERR("Failed to create %s under /proc\n", iliproc[i].name); + ret = -ENODEV; + } else { + iliproc[i].isCreated = true; + ILI_INFO("Succeed to create %s under /proc\n", iliproc[i].name); + } + } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + netlink_init(); +#endif +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + ilitek_sys_init(); +#endif +} diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_qcom.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_qcom.c new file mode 100755 index 000000000000..3d7caa5676ef --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_qcom.c @@ -0,0 +1,1015 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" + +#define DTS_INT_GPIO "touch,irq-gpio" +#define DTS_RESET_GPIO "touch,reset-gpio" +#define DTS_OF_NAME "tchip,ilitek" +enum touch_state { + TOUCH_DEEP_SLEEP_STATE = 0, + TOUCH_LOW_POWER_STATE, +}; + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +extern int ili_mmi_init(struct ilitek_ts_data *ts_data, bool enable); +#endif + +#ifdef ILI_SENSOR_EN +static struct sensors_classdev __maybe_unused sensors_touch_cdev = { + + .name = "dt-gesture", + .vendor = "ilitek", + .version = 1, + .type = SENSOR_TYPE_MOTO_DOUBLE_TAP, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + /* WAKE_UP & SPECIAL_REPORT */ + .flags = 1 | 6, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#endif + + +void ili_tp_reset(void) +{ + ILI_INFO("edge delay = %d\n", ilits->rst_edge_delay); + + /* Need accurate power sequence, do not change it to msleep */ + gpio_direction_output(ilits->tp_rst, 1); + mdelay(1); + gpio_set_value(ilits->tp_rst, 0); + mdelay(5); + gpio_set_value(ilits->tp_rst, 1); + mdelay(ilits->rst_edge_delay); +} + +void ili_input_register(void) +{ + ILI_INFO(); + + ilits->input = input_allocate_device(); + if (ERR_ALLOC_MEM(ilits->input)) { + ILI_ERR("Failed to allocate touch input device\n"); + input_free_device(ilits->input); + return; + } + + ilits->input->name = ilits->md_name; + ilits->input->phys = ilits->phys; + ilits->input->dev.parent = ilits->dev; + ilits->input->id.bustype = ilits->hwif->bus_type; + + /* set the supported event type for input device */ + set_bit(EV_ABS, ilits->input->evbit); + set_bit(EV_SYN, ilits->input->evbit); + set_bit(EV_KEY, ilits->input->evbit); + set_bit(BTN_TOUCH, ilits->input->keybit); + set_bit(BTN_TOOL_FINGER, ilits->input->keybit); + set_bit(INPUT_PROP_DIRECT, ilits->input->propbit); + + input_set_abs_params(ilits->input, ABS_MT_POSITION_X, TOUCH_SCREEN_X_MIN, ilits->panel_wid - 1, 0, 0); + input_set_abs_params(ilits->input, ABS_MT_POSITION_Y, TOUCH_SCREEN_Y_MIN, ilits->panel_hei - 1, 0, 0); + if (!TOUCH_WIDTH) { + input_set_abs_params(ilits->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(ilits->input, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); + } else { + input_set_abs_params(ilits->input, ABS_MT_TOUCH_MAJOR, 0, ilits->panel_hei - 1, 0, 0); + input_set_abs_params(ilits->input, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(ilits->input, ABS_MT_TOUCH_MINOR, 0, ilits->panel_wid - 1, 0, 0); + input_set_abs_params(ilits->input, ABS_MT_ORIENTATION, -90, 90, 0, 0); + } + + if (MT_PRESSURE) + input_set_abs_params(ilits->input, ABS_MT_PRESSURE, 0, 1000, 0, 0); + + if (MT_B_TYPE) { +#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE + input_mt_init_slots(ilits->input, MAX_TOUCH_NUM, INPUT_MT_DIRECT); +#else + input_mt_init_slots(ilits->input, MAX_TOUCH_NUM); +#endif /* LINUX_VERSION_CODE */ + } else { + input_set_abs_params(ilits->input, ABS_MT_TRACKING_ID, 0, MAX_TOUCH_NUM, 0, 0); + } + + /* Gesture keys register */ + input_set_capability(ilits->input, EV_KEY, KEY_POWER); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_UP); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_DOWN); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_LEFT); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_RIGHT); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_O); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_E); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_M); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_W); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_S); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_V); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_Z); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_C); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_F); + + __set_bit(KEY_GESTURE_POWER, ilits->input->keybit); + __set_bit(KEY_GESTURE_UP, ilits->input->keybit); + __set_bit(KEY_GESTURE_DOWN, ilits->input->keybit); + __set_bit(KEY_GESTURE_LEFT, ilits->input->keybit); + __set_bit(KEY_GESTURE_RIGHT, ilits->input->keybit); + __set_bit(KEY_GESTURE_O, ilits->input->keybit); + __set_bit(KEY_GESTURE_E, ilits->input->keybit); + __set_bit(KEY_GESTURE_M, ilits->input->keybit); + __set_bit(KEY_GESTURE_W, ilits->input->keybit); + __set_bit(KEY_GESTURE_S, ilits->input->keybit); + __set_bit(KEY_GESTURE_V, ilits->input->keybit); + __set_bit(KEY_GESTURE_Z, ilits->input->keybit); + __set_bit(KEY_GESTURE_C, ilits->input->keybit); + __set_bit(KEY_GESTURE_F, ilits->input->keybit); + + /* register the input device to input sub-system */ + if (input_register_device(ilits->input) < 0) { + ILI_ERR("Failed to register touch input device\n"); + input_unregister_device(ilits->input); + input_free_device(ilits->input); + } +} + +#if REGULATOR_POWER +void ili_plat_regulator_power_on(bool status) +{ + ILI_INFO("%s\n", status ? "POWER ON" : "POWER OFF"); + + if (status) { + if (ilits->vdd) { + if (regulator_enable(ilits->vdd) < 0) + ILI_ERR("regulator_enable VDD fail\n"); + } + if (ilits->vcc) { + if (regulator_enable(ilits->vcc) < 0) + ILI_ERR("regulator_enable VCC fail\n"); + } + } else { + if (ilits->vdd) { + if (regulator_disable(ilits->vdd) < 0) + ILI_ERR("regulator_enable VDD fail\n"); + } + if (ilits->vcc) { + if (regulator_disable(ilits->vcc) < 0) + ILI_ERR("regulator_enable VCC fail\n"); + } + } + atomic_set(&ilits->ice_stat, DISABLE); + mdelay(5); +} + +static void ilitek_plat_regulator_power_init(void) +{ + const char *vdd_name = "vdd"; + const char *vcc_name = "vcc"; + + ilits->vdd = regulator_get(ilits->dev, vdd_name); + if (ERR_ALLOC_MEM(ilits->vdd)) { + ILI_ERR("regulator_get VDD fail\n"); + ilits->vdd = NULL; + } + if (regulator_set_voltage(ilits->vdd, VDD_VOLTAGE, VDD_VOLTAGE) < 0) + ILI_ERR("Failed to set VDD %d\n", VDD_VOLTAGE); + + ilits->vcc = regulator_get(ilits->dev, vcc_name); + if (ERR_ALLOC_MEM(ilits->vcc)) { + ILI_ERR("regulator_get VCC fail.\n"); + ilits->vcc = NULL; + } + if (regulator_set_voltage(ilits->vcc, VCC_VOLTAGE, VCC_VOLTAGE) < 0) + ILI_ERR("Failed to set VCC %d\n", VCC_VOLTAGE); + + ili_plat_regulator_power_on(true); +} +#endif + +static int ilitek_plat_gpio_register(void) +{ + int ret = 0; + u32 flag; + struct device_node *dev_node = ilits->dev->of_node; + + ilits->tp_int = of_get_named_gpio_flags(dev_node, DTS_INT_GPIO, 0, &flag); + ilits->tp_rst = of_get_named_gpio_flags(dev_node, DTS_RESET_GPIO, 0, &flag); + + ILI_INFO("TP INT: %d\n", ilits->tp_int); + ILI_INFO("TP RESET: %d\n", ilits->tp_rst); + + if (!gpio_is_valid(ilits->tp_int)) { + ILI_ERR("Invalid INT gpio: %d\n", ilits->tp_int); + return -EBADR; + } + + if (!gpio_is_valid(ilits->tp_rst)) { + ILI_ERR("Invalid RESET gpio: %d\n", ilits->tp_rst); + return -EBADR; + } + + ret = gpio_request(ilits->tp_int, "TP_INT"); + if (ret < 0) { + ILI_ERR("Request IRQ GPIO failed, ret = %d\n", ret); + gpio_free(ilits->tp_int); + ret = gpio_request(ilits->tp_int, "TP_INT"); + if (ret < 0) { + ILI_ERR("Retrying request INT GPIO still failed , ret = %d\n", ret); + goto out; + } + } + + ret = gpio_request(ilits->tp_rst, "TP_RESET"); + if (ret < 0) { + ILI_ERR("Request RESET GPIO failed, ret = %d\n", ret); + gpio_free(ilits->tp_rst); + ret = gpio_request(ilits->tp_rst, "TP_RESET"); + if (ret < 0) { + ILI_ERR("Retrying request RESET GPIO still failed , ret = %d\n", ret); + goto out; + } + } + +out: + gpio_direction_input(ilits->tp_int); + return ret; +} + +void ili_irq_disable(void) +{ + unsigned long flag; + + spin_lock_irqsave(&ilits->irq_spin, flag); + + if (atomic_read(&ilits->irq_stat) == DISABLE) + goto out; + + if (!ilits->irq_num) { + ILI_ERR("gpio_to_irq (%d) is incorrect\n", ilits->irq_num); + goto out; + } + + disable_irq_nosync(ilits->irq_num); + atomic_set(&ilits->irq_stat, DISABLE); + ILI_DBG("Disable irq success\n"); + +out: + spin_unlock_irqrestore(&ilits->irq_spin, flag); +} + +void ili_irq_enable(void) +{ + unsigned long flag; + + spin_lock_irqsave(&ilits->irq_spin, flag); + + if (atomic_read(&ilits->irq_stat) == ENABLE) + goto out; + + if (!ilits->irq_num) { + ILI_ERR("gpio_to_irq (%d) is incorrect\n", ilits->irq_num); + goto out; + } + + enable_irq(ilits->irq_num); + atomic_set(&ilits->irq_stat, ENABLE); + ILI_DBG("Enable irq success\n"); + +out: + spin_unlock_irqrestore(&ilits->irq_spin, flag); +} + +static irqreturn_t ilitek_plat_isr_top_half(int irq, void *dev_id) +{ + if (irq != ilits->irq_num) { + ILI_ERR("Incorrect irq number (%d)\n", irq); + return IRQ_NONE; + } + + if (atomic_read(&ilits->cmd_int_check) == ENABLE) { + atomic_set(&ilits->cmd_int_check, DISABLE); + ILI_DBG("CMD INT detected, ignore\n"); + wake_up(&(ilits->inq)); + return IRQ_HANDLED; + } + + if (ilits->prox_near) { + ILI_INFO("Proximity event, ignore interrupt!\n"); + return IRQ_HANDLED; + } + + ILI_DBG("report: %d, rst: %d, fw: %d, switch: %d, mp: %d, sleep: %d, esd: %d\n", + ilits->report, + atomic_read(&ilits->tp_reset), + atomic_read(&ilits->fw_stat), + atomic_read(&ilits->tp_sw_mode), + atomic_read(&ilits->mp_stat), + atomic_read(&ilits->tp_sleep), + atomic_read(&ilits->esd_stat)); + + if (!ilits->report || atomic_read(&ilits->tp_reset) || + atomic_read(&ilits->fw_stat) || atomic_read(&ilits->tp_sw_mode) || + atomic_read(&ilits->mp_stat) || atomic_read(&ilits->tp_sleep) || + atomic_read(&ilits->esd_stat)) { + ILI_DBG("ignore interrupt !\n"); + return IRQ_HANDLED; + } + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t ilitek_plat_isr_bottom_half(int irq, void *dev_id) +{ + if (mutex_is_locked(&ilits->touch_mutex)) { + ILI_DBG("touch is locked, ignore\n"); + return IRQ_HANDLED; + } + mutex_lock(&ilits->touch_mutex); + ili_report_handler(); + mutex_unlock(&ilits->touch_mutex); + return IRQ_HANDLED; +} + +void ili_irq_unregister(void) +{ + devm_free_irq(ilits->dev, ilits->irq_num, NULL); +} + +int ili_irq_register(int type) +{ + int ret = 0; + static bool get_irq_pin; + + atomic_set(&ilits->irq_stat, DISABLE); + + if (get_irq_pin == false) { + ilits->irq_num = gpio_to_irq(ilits->tp_int); + get_irq_pin = true; + } + + ILI_INFO("ilits->irq_num = %d\n", ilits->irq_num); + + ret = devm_request_threaded_irq(ilits->dev, ilits->irq_num, + ilitek_plat_isr_top_half, + ilitek_plat_isr_bottom_half, + type | IRQF_ONESHOT, "ilitek", NULL); + + if (type == IRQF_TRIGGER_FALLING) + ILI_INFO("IRQ TYPE = IRQF_TRIGGER_FALLING\n"); + if (type == IRQF_TRIGGER_RISING) + ILI_INFO("IRQ TYPE = IRQF_TRIGGER_RISING\n"); + + if (ret != 0) + ILI_ERR("Failed to register irq handler, irq = %d, ret = %d\n", ilits->irq_num, ret); + + atomic_set(&ilits->irq_stat, ENABLE); + + return ret; +} + +#if SPRD_SYSFS_SUSPEND_RESUME +static ssize_t ts_suspend_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", ilits->tp_suspend ? "true" : "false"); +} + +static ssize_t ts_suspend_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + if ((buf[0] == '1') && !ilits->tp_suspend) + ili_sleep_handler(TP_DEEP_SLEEP); + else if ((buf[0] == '0') && ilits->tp_suspend) + ili_sleep_handler(TP_RESUME); + + return count; +} +static DEVICE_ATTR_RW(ts_suspend); + +static struct attribute *ilitek_dev_suspend_atts[] = { + &dev_attr_ts_suspend.attr, + NULL +}; + +static const struct attribute_group ilitek_dev_suspend_atts_group = { + .attrs = ilitek_dev_suspend_atts, +}; + +static const struct attribute_group *ilitek_dev_attr_groups[] = { + &ilitek_dev_suspend_atts_group, + NULL +}; + +int ili_sysfs_add_device(struct device *dev) { + int ret = 0, i; + + for (i = 0; ilitek_dev_attr_groups[i]; i++) { + ret = sysfs_create_group(&dev->kobj, ilitek_dev_attr_groups[i]); + if (ret) { + while (--i >= 0) { + sysfs_remove_group(&dev->kobj, ilitek_dev_attr_groups[i]); + } + break; + } + } + + return ret; +} + +int ili_sysfs_remove_device(struct device *dev) { + int i; + + sysfs_remove_link(NULL, "touchscreen"); + for (i = 0; ilitek_dev_attr_groups[i]; i++) { + sysfs_remove_group(&dev->kobj, ilitek_dev_attr_groups[i]); + } + + return 0; +} +#else + +#if (defined(CONFIG_DRM) && (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0))) +static int ilitek_plat_notifier_fb(struct notifier_block *self, unsigned long event, void *data) +{ + int *blank; + struct drm_panel_notifier *evdata = data; +#if defined(ILI_SENSOR_EN) && (defined(ILI_SET_TOUCH_STATE) || defined(ILI_CONFIG_PANEL_NOTIFICATIONS)) + struct ilitek_ts_data *ts = + container_of(self, struct ilitek_ts_data, notifier_fb); +#endif + + ILI_INFO("Notifier's event = %ld\n", event); + + if (!(evdata && evdata->data)) + return 0; + + blank = evdata->data; + + switch (*blank) { + case DRM_PANEL_BLANK_POWERDOWN: +/* + if (TP_SUSPEND_PRIO) { + if (event != MSM_DRM_EARLY_EVENT_BLANK) + return NOTIFY_DONE; + } else { + if (event != MSM_DRM_EVENT_BLANK) + return NOTIFY_DONE; + } +*/ + if (event == DRM_PANEL_EARLY_EVENT_BLANK) + { +#if ENABLE_GESTURE +#ifdef ILI_CONFIG_PANEL_GESTURE + if (ilits->panel_gesture_enable) { + if (ili_sleep_handler(TP_SUSPEND) < 0) + ILI_ERR("TP suspend failed!\n"); + else + ILI_INFO("TP suspend!\n"); + } else { + //enter deep sleep when panel not config gesture + if (ili_sleep_handler(TP_DEEP_SLEEP) < 0) + ILI_ERR("TP deep sleep in failed!\n"); + else + ILI_INFO("TP in deep sleep!\n"); + } +#elif defined(ILI_SENSOR_EN) && (defined(ILI_SET_TOUCH_STATE) || defined(ILI_CONFIG_PANEL_NOTIFICATIONS)) + if (ts->should_enable_gesture) { + ILI_INFO("double tap gesture suspend\n"); + if (ili_sleep_handler(TP_SUSPEND) < 0) + ILI_ERR("TP suspend failed\n"); + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); + } else { + if (ili_sleep_handler(TP_DEEP_SLEEP) < 0) + ILI_ERR("TP deep sleep in failed\n"); + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); + } +#else + if (ilits->tp_module == MODEL_TM) { + if (ili_sleep_handler(TP_DEEP_SLEEP) < 0) + ILI_ERR("TP deep sleep in failed\n"); + } else { + if (ili_sleep_handler(TP_SUSPEND) < 0) + ILI_ERR("TP suspend failed\n"); + } +#endif +#else + //enter deep sleep when not enable gesture + if (ili_sleep_handler(TP_DEEP_SLEEP) < 0) + { + ILI_ERR("TP deep sleep in failed\n"); + } else { + ILI_INFO("TP in deep sleep\n"); + } +#endif + +#if defined(ILI_CONFIG_GESTURE) || defined(ILI_CONFIG_PANEL_GESTURE) + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); +#endif + } + break; + + case DRM_PANEL_BLANK_UNBLANK: + if (event == DRM_PANEL_EVENT_BLANK) + { + if (ili_sleep_handler(TP_RESUME) < 0) + ILI_ERR("TP resume failed\n"); + + } + break; + default: + ILI_ERR("Unknown event, blank = %d\n", *blank); + break; + } + return NOTIFY_OK; +} +#else + +#if defined(CONFIG_FB) || defined(CONFIG_DRM_MSM) +static int ilitek_plat_notifier_fb(struct notifier_block *self, unsigned long event, void *data) +{ + int *blank; + struct fb_event *evdata = data; + + ILI_INFO("Notifier's event = %ld\n", event); + + /* + * FB_EVENT_BLANK(0x09): A hardware display blank change occurred. + * FB_EARLY_EVENT_BLANK(0x10): A hardware display blank early change occurred. + */ + if (evdata && evdata->data) { + blank = evdata->data; + switch (*blank) { +#ifdef CONFIG_DRM_MSM + case MSM_DRM_BLANK_POWERDOWN: +#else + case FB_BLANK_POWERDOWN: +#endif +#if CONFIG_PLAT_SPRD + case DRM_MODE_DPMS_OFF: +#endif /* CONFIG_PLAT_SPRD */ + if (TP_SUSPEND_PRIO) { +#ifdef CONFIG_DRM_MSM + if (event != MSM_DRM_EARLY_EVENT_BLANK) +#else + if (event != FB_EARLY_EVENT_BLANK) +#endif + return NOTIFY_DONE; + } else { +#ifdef CONFIG_DRM_MSM + if (event != MSM_DRM_EVENT_BLANK) +#else + if (event != FB_EVENT_BLANK) +#endif + return NOTIFY_DONE; + } + if (ili_sleep_handler(TP_SUSPEND) < 0) + ILI_ERR("TP suspend failed\n"); + break; +#ifdef CONFIG_DRM_MSM + case MSM_DRM_BLANK_UNBLANK: + case MSM_DRM_BLANK_NORMAL: +#else + case FB_BLANK_UNBLANK: + case FB_BLANK_NORMAL: +#endif + +#if CONFIG_PLAT_SPRD + case DRM_MODE_DPMS_ON: +#endif /* CONFIG_PLAT_SPRD */ + +#ifdef CONFIG_DRM_MSM + if (event == MSM_DRM_EVENT_BLANK) +#else + if (event == FB_EVENT_BLANK) +#endif + { + if (ili_sleep_handler(TP_RESUME) < 0) + ILI_ERR("TP resume failed\n"); + + } + break; + default: + ILI_ERR("Unknown event, blank = %d\n", *blank); + break; + } + } + return NOTIFY_OK; +} +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void ilitek_plat_early_suspend(struct early_suspend *h) +{ + if (ili_sleep_handler(TP_SUSPEND) < 0) + ILI_ERR("TP suspend failed\n"); +} + +static void ilitek_plat_late_resume(struct early_suspend *h) +{ + if (ili_sleep_handler(TP_RESUME) < 0) + ILI_ERR("TP resume failed\n"); +} +#endif +#endif + +#ifdef ILI_SENSOR_EN +static int ili_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + ILI_INFO("Gesture set enable %d!", enable); + mutex_lock(&ilits->state_mutex); + if (enable == 1) { + ilits->should_enable_gesture = true; + } else if (enable == 0) { + ilits->should_enable_gesture = false; + } else { + ILI_INFO("unknown enable symbol\n"); + } + mutex_unlock(&ilits->state_mutex); + return 0; +} + +static int ili_sensor_init(struct ilitek_ts_data *data) +{ + struct ili_sensor_platform_data *sensor_pdata; + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + ILI_ERR("Failed to allocate device"); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct ili_sensor_platform_data), + GFP_KERNEL); + if (!sensor_pdata) { + ILI_ERR("Failed to allocate memory"); + goto free_sensor_pdata; + } + data->sensor_pdata = sensor_pdata; + + if (data->report_gesture_key) { + __set_bit(EV_KEY, sensor_input_dev->evbit); + __set_bit(KEY_F1, sensor_input_dev->keybit); + } else { + __set_bit(EV_ABS, sensor_input_dev->evbit); + input_set_abs_params(sensor_input_dev, ABS_DISTANCE, + 0, REPORT_MAX_COUNT, 0, 0); + } + __set_bit(EV_SYN, sensor_input_dev->evbit); + + sensor_input_dev->name = "double-tap"; + data->sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + ILI_ERR("Unable to register device, err=%d", err); + goto free_sensor_input_dev; + } + + sensor_pdata->ps_cdev = sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = ili_sensor_set_enable; + sensor_pdata->data = data; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(data->sensor_pdata->input_sensor_dev); +free_sensor_input_dev: + input_free_device(data->sensor_pdata->input_sensor_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, sensor_pdata); + data->sensor_pdata = NULL; +exit: + return 1; +} + +int ili_sensor_remove(struct ilitek_ts_data *data) +{ + sensors_classdev_unregister(&data->sensor_pdata->ps_cdev); + input_unregister_device(data->sensor_pdata->input_sensor_dev); + devm_kfree(&data->sensor_pdata->input_sensor_dev->dev, + data->sensor_pdata); + data->sensor_pdata = NULL; + data->wakeable = false; + data->should_enable_gesture = false; + return 0; +} +#endif + +static void ilitek_plat_sleep_init(void) +{ + ILI_INFO("Init notifier_fb struct\n"); +#if (defined(CONFIG_DRM) && (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0))) + ilits->notifier_fb.notifier_call = ilitek_plat_notifier_fb; +{ + int ret = 0; + extern struct drm_panel *ili_active_panel; + if (ili_active_panel) { + ret = drm_panel_notifier_register(ili_active_panel, &ilits->notifier_fb); + if(ret) { + ILI_ERR("register drm_notifier failed. ret=%d\n", ret); + } + } +} +#else +#if defined(CONFIG_FB) || defined(CONFIG_DRM_MSM) +#if defined(CONFIG_DRM_MSM) + if (msm_drm_register_client(&ilits->notifier_fb)) { + ILI_ERR("msm_drm_register_client Unable to register fb_notifier\n"); + } +#else +#if CONFIG_PLAT_SPRD + if (adf_register_client(&ilits->notifier_fb)) + ILI_ERR("Unable to register notifier_fb\n"); +#else + if (fb_register_client(&ilits->notifier_fb)) + ILI_ERR("Unable to register notifier_fb\n"); +#endif /* CONFIG_PLAT_SPRD */ +#endif /* CONFIG_DRM_MSM */ + +#elif defined(CONFIG_HAS_EARLYSUSPEND) + ILI_INFO("Init eqarly_suspend struct\n"); + ilits->early_suspend.suspend = ilitek_plat_early_suspend; + ilits->early_suspend.resume = ilitek_plat_late_resume; + ilits->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + register_early_suspend(&ilits->early_suspend); +#endif +#endif +} +#endif +#if CHARGER_NOTIFIER_CALLBACK +#if KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE +/* add_for_charger_start */ +static int ilitek_charger_notifier_callback(struct notifier_block *nb, + unsigned long val, void *v) { + int ret = 0; + struct power_supply *psy = NULL; + union power_supply_propval prop; + + + if(ilits->fw_update_stat != 100) + return 0; + + psy= power_supply_get_by_name("usb"); + if (!psy) { + ILI_ERR("Couldn't get usbpsy\n"); + return -EINVAL; + } + if (!strcmp(psy->desc->name, "usb")) { + if (psy && val == POWER_SUPPLY_PROP_STATUS) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,&prop); + if (ret < 0) { + ILI_ERR("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret); + return ret; + } else { + if(ilits->usb_plug_status == 2) + ilits->usb_plug_status = prop.intval; + if(ilits->usb_plug_status != prop.intval) { + ILI_INFO("usb prop.intval =%d\n", prop.intval); + ilits->usb_plug_status = prop.intval; + if(!ilits->tp_suspend && (ilits->charger_notify_wq != NULL)) + queue_work(ilits->charger_notify_wq,&ilits->update_charger); + } + } + } + } + return 0; +} +static void ilitek_update_charger(struct work_struct *work) +{ + int ret = 0; + mutex_lock(&ilits->touch_mutex); + ret = ili_ic_func_ctrl("plug", !ilits->usb_plug_status);// plug in + if(ret<0) { + ILI_ERR("Write plug in failed\n"); + } + mutex_unlock(&ilits->touch_mutex); +} +void ilitek_plat_charger_init(void) +{ + int ret = 0; + struct power_supply *psy = NULL; + union power_supply_propval prop; + + ilits->usb_plug_status = 2; + ilits->charger_notify_wq = create_singlethread_workqueue("ili_charger_wq"); + if (!ilits->charger_notify_wq) { + ILI_ERR("allocate ili_charger_notify_wq failed\n"); + return; + } + INIT_WORK(&ilits->update_charger, ilitek_update_charger); + ilits->notifier_charger.notifier_call = ilitek_charger_notifier_callback; + ret = power_supply_reg_notifier(&ilits->notifier_charger); + if (ret < 0) + ILI_ERR("power_supply_reg_notifier failed\n"); + + /* if power supply supplier registered brfore TP + * ps_notify_callback will not receive PSY_EVENT_PROP_ADDED + * event, and will cause miss to set TP into charger state. + * So check PS state in probe. + */ + psy = power_supply_get_by_name("usb"); + if (psy) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &prop); + if (ret < 0) { + ILI_ERR("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret); + } else { + ilits->usb_plug_status = prop.intval; + ILI_INFO("boot check usb_plug_status = %d\n", prop.intval); + } + } +} +/* add_for_charger_end */ +#endif +#endif + +#ifdef ILITEK_PEN_NOTIFIER +#define ENABLE_PASSIVE_PEN_MODE_CMD 0x01 +#define DISABLE_PASSIVE_PEN_MODE_CMD 0x00 +static int pen_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + ILI_INFO("Received event(%lu) for pen detection\n", event); + + mutex_lock(&ilits->touch_mutex); + if (event == PEN_DETECTION_PULL) + ili_ic_func_ctrl("passive_pen", ENABLE_PASSIVE_PEN_MODE_CMD); + else if (event == PEN_DETECTION_INSERT) + ili_ic_func_ctrl("passive_pen", DISABLE_PASSIVE_PEN_MODE_CMD); + mutex_unlock(&ilits->touch_mutex); + + return 0; +} +#endif + +static int ilitek_plat_probe(void) +{ +#ifdef ILI_SENSOR_EN + static bool initialized_sensor; +#endif + ILI_INFO("platform probe\n"); + +#if REGULATOR_POWER + ilitek_plat_regulator_power_init(); +#endif + + if (ilitek_plat_gpio_register() < 0) + ILI_ERR("Register gpio failed\n"); + + ili_irq_register(ilits->irq_tirgger_type); + + if (ili_tddi_init() < 0) { + ILI_ERR("ILITEK Driver probe failed\n"); + ili_irq_unregister(); + return -ENODEV; + } +#if SPRD_SYSFS_SUSPEND_RESUME + ili_sysfs_add_device(ilits->dev); + if (sysfs_create_link(NULL, &ilits->dev->kobj, "touchscreen") < 0) + ILI_INFO("Failed to create link!\n"); +#else + ilitek_plat_sleep_init(); +#endif + ilits->pm_suspend = false; + init_completion(&ilits->pm_completion); +#if CHARGER_NOTIFIER_CALLBACK +#if KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE + /* add_for_charger_start */ + ilitek_plat_charger_init(); + /* add_for_charger_end */ +#endif +#endif + +#ifdef ILI_SENSOR_EN + mutex_init(&ilits->state_mutex); + //unknown screen state + ilits->screen_state = SCREEN_UNKNOWN; + if (!initialized_sensor) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&(ilits->gesture_wakelock), WAKE_LOCK_SUSPEND, "dt-wake-lock"); +#else + PM_WAKEUP_REGISTER(ilits->dev, ilits->gesture_wakelock, "dt-wake-lock"); + if (!ilits->gesture_wakelock) { + ILI_ERR("ILITEK Driver failed to load. wakeup_source_init failed."); + return -ENOMEM; + } +#endif + if (!ili_sensor_init(ilits)) + initialized_sensor = true; + } +#endif + +#ifdef ILITEK_PEN_NOTIFIER + ilits->pen_notif.notifier_call = pen_notifier_callback; + pen_detection_register_client(&ilits->pen_notif); +#endif + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + ili_mmi_init(ilits, true); +#endif + + ILI_INFO("ILITEK Driver loaded successfully!"); + return 0; +} + +static int ilitek_tp_pm_suspend(struct device *dev) +{ + ILI_INFO("CALL BACK TP PM SUSPEND"); + ilits->pm_suspend = true; + reinit_completion(&ilits->pm_completion); + return 0; +} + +static int ilitek_tp_pm_resume(struct device *dev) +{ + ILI_INFO("CALL BACK TP PM RESUME"); + ilits->pm_suspend = false; + complete(&ilits->pm_completion); + return 0; +} + +static int ilitek_plat_remove(void) +{ + ILI_INFO("remove plat dev\n"); +#if SPRD_SYSFS_SUSPEND_RESUME + ili_sysfs_remove_device(ilits->dev); +#endif + ili_dev_remove(); + return 0; +} + +static const struct dev_pm_ops tp_pm_ops = { + .suspend = ilitek_tp_pm_suspend, + .resume = ilitek_tp_pm_resume, +}; + +static const struct of_device_id tp_match_table[] = { + {.compatible = DTS_OF_NAME}, + {}, +}; + +static struct ilitek_hwif_info hwif = { + .bus_type = TDDI_INTERFACE, + .plat_type = TP_PLAT_QCOM, + .owner = THIS_MODULE, + .name = TDDI_DEV_ID, + .of_match_table = of_match_ptr(tp_match_table), + .plat_probe = ilitek_plat_probe, + .plat_remove = ilitek_plat_remove, + .pm = &tp_pm_ops, +}; + +static int __init ilitek_plat_dev_init(void) +{ + ILI_INFO("ILITEK TP driver init for QCOM\n"); + if (ili_dev_init(&hwif) < 0) { + ILI_ERR("Failed to register i2c/spi bus driver\n"); + return -ENODEV; + } + return 0; +} + +static void __exit ilitek_plat_dev_exit(void) +{ + ILI_INFO("remove plat dev\n"); + ili_dev_remove(); +} + +//module_init(ilitek_plat_dev_init); +late_initcall(ilitek_plat_dev_init); +module_exit(ilitek_plat_dev_exit); +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +MODULE_AUTHOR("ILITEK"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_spi.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_spi.c new file mode 100755 index 000000000000..cf437f7045a4 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_spi.c @@ -0,0 +1,841 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +#include +#endif +#ifdef CONFIG_DRM +struct drm_panel *ili_active_panel; +#ifdef ILI_FW_PANEL +static const char *active_panel_name = NULL; +#endif +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +static int check_dt(struct device_node *np); +#endif +#endif + +struct touch_bus_info { + struct spi_driver bus_driver; + struct ilitek_hwif_info *hwif; +}; + +struct ilitek_ts_data *ilits; + +#if SPI_DMA_TRANSFER_SPLIT +#define DMA_TRANSFER_MAX_CHUNK 64 // number of chunks to be transferred. +#define DMA_TRANSFER_MAX_LEN 4096 // length of a chunk. + +int ili_spi_write_then_read_split(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx) +{ + int status = -1, duplex_len = 0; + int xfercnt = 0, xferlen = 0, xferloop = 0; + int offset = 0; + u8 cmd = 0; + struct spi_message message; + struct spi_transfer *xfer; + + xfer = kzalloc(DMA_TRANSFER_MAX_CHUNK * sizeof(struct spi_transfer), GFP_KERNEL); + + if (n_rx > SPI_RX_BUF_SIZE) { + ILI_ERR("Rx length is greater than spi local buf, abort\n"); + status = -ENOMEM; + goto out; + } + + spi_message_init(&message); + memset(ilits->spi_tx, 0x0, SPI_TX_BUF_SIZE); + memset(ilits->spi_rx, 0x0, SPI_RX_BUF_SIZE); + + if ((n_tx > 0) && (n_rx > 0)) + cmd = SPI_READ; + else + cmd = SPI_WRITE; + + switch (cmd) { + case SPI_WRITE: + if (n_tx % DMA_TRANSFER_MAX_LEN) + xferloop = (n_tx / DMA_TRANSFER_MAX_LEN) + 1; + else + xferloop = n_tx / DMA_TRANSFER_MAX_LEN; + + xferlen = n_tx; + memcpy(ilits->spi_tx, (u8 *)txbuf, xferlen); + + for (xfercnt = 0; xfercnt < xferloop; xfercnt++) { + if (xferlen > DMA_TRANSFER_MAX_LEN) + xferlen = DMA_TRANSFER_MAX_LEN; + + xfer[xfercnt].len = xferlen; + xfer[xfercnt].tx_buf = ilits->spi_tx + xfercnt * DMA_TRANSFER_MAX_LEN; + spi_message_add_tail(&xfer[xfercnt], &message); + xferlen = n_tx - (xfercnt+1) * DMA_TRANSFER_MAX_LEN; + } + status = spi_sync(spi, &message); + break; + case SPI_READ: + if (n_tx > DMA_TRANSFER_MAX_LEN) { + ILI_ERR("Tx length must be lower than dma length (%d).\n", DMA_TRANSFER_MAX_LEN); + status = -EINVAL; + break; + } + + if (!atomic_read(&ilits->ice_stat)) + offset = 2; + + memcpy(ilits->spi_tx, txbuf, n_tx); + duplex_len = n_tx + n_rx + offset; + + if (duplex_len % DMA_TRANSFER_MAX_LEN) + xferloop = (duplex_len / DMA_TRANSFER_MAX_LEN) + 1; + else + xferloop = duplex_len / DMA_TRANSFER_MAX_LEN; + + xferlen = duplex_len; + for (xfercnt = 0; xfercnt < xferloop; xfercnt++) { + if (xferlen > DMA_TRANSFER_MAX_LEN) + xferlen = DMA_TRANSFER_MAX_LEN; + + xfer[xfercnt].len = xferlen; + xfer[xfercnt].tx_buf = ilits->spi_tx; + xfer[xfercnt].rx_buf = ilits->spi_rx + xfercnt * DMA_TRANSFER_MAX_LEN; + spi_message_add_tail(&xfer[xfercnt], &message); + xferlen = duplex_len - (xfercnt + 1) * DMA_TRANSFER_MAX_LEN; + } + + status = spi_sync(spi, &message); + if (status == 0) { + if (ilits->spi_rx[1] != SPI_ACK && !atomic_read(&ilits->ice_stat)) { + status = DO_SPI_RECOVER; + ILI_ERR("Do spi recovery: rxbuf[1] = 0x%x, ice = %d\n", ilits->spi_rx[1], atomic_read(&ilits->ice_stat)); + break; + } + + memcpy((u8 *)rxbuf, ilits->spi_rx + offset + 1, n_rx); + } else { + ILI_ERR("spi read fail, status = %d\n", status); + } + break; + default: + ILI_INFO("Unknown command 0x%x\n", cmd); + break; + } + +out: + ipio_kfree((void **)&xfer); + return status; +} +#else +int ili_spi_write_then_read_direct(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx) +{ + int status = -1, duplex_len = 0; + int offset = 0; + u8 cmd; + struct spi_message message; + struct spi_transfer xfer; + + if (n_rx > SPI_RX_BUF_SIZE) { + ILI_ERR("Rx length is greater than spi local buf, abort\n"); + status = -ENOMEM; + goto out; + } + + spi_message_init(&message); + memset(&xfer, 0, sizeof(xfer)); + + if ((n_tx > 0) && (n_rx > 0)) + cmd = SPI_READ; + else + cmd = SPI_WRITE; + + switch (cmd) { + case SPI_WRITE: + xfer.len = n_tx; + xfer.tx_buf = txbuf; + spi_message_add_tail(&xfer, &message); + status = spi_sync(spi, &message); + break; + case SPI_READ: + if (!atomic_read(&ilits->ice_stat)) + offset = 2; + + duplex_len = n_tx + n_rx + offset; + if ((duplex_len > SPI_TX_BUF_SIZE) || + (duplex_len > SPI_RX_BUF_SIZE)) { + ILI_ERR("duplex_len is over than dma buf, abort\n"); + status = -ENOMEM; + break; + } + + memset(ilits->spi_tx, 0x0, SPI_TX_BUF_SIZE); + memset(ilits->spi_rx, 0x0, SPI_RX_BUF_SIZE); + + xfer.len = duplex_len; + memcpy(ilits->spi_tx, txbuf, n_tx); + xfer.tx_buf = ilits->spi_tx; + xfer.rx_buf = ilits->spi_rx; + + spi_message_add_tail(&xfer, &message); + status = spi_sync(spi, &message); + if (status == 0) { + if (ilits->spi_rx[1] != SPI_ACK && !atomic_read(&ilits->ice_stat)) { + status = DO_SPI_RECOVER; + ILI_ERR("Do spi recovery: rxbuf[1] = 0x%x, ice = %d\n", ilits->spi_rx[1], atomic_read(&ilits->ice_stat)); + break; + } + + memcpy((u8 *)rxbuf, ilits->spi_rx + offset + 1, n_rx); + } else { + ILI_ERR("spi read fail, status = %d\n", status); + } + break; + default: + ILI_INFO("Unknown command 0x%x\n", cmd); + break; + } + +out: + return status; +} +#endif + +static int ili_spi_mp_pre_cmd(u8 cdc) +{ + u8 pre[5] = {0}; + + if (!atomic_read(&ilits->mp_stat) || cdc != P5_X_SET_CDC_INIT || + ilits->chip->core_ver >= CORE_VER_1430) + return 0; + + ILI_DBG("mp test with pre commands\n"); + + pre[0] = SPI_WRITE; + pre[1] = 0x0;// dummy byte + pre[2] = 0x2;// Write len byte + pre[3] = P5_X_READ_DATA_CTRL; + pre[4] = P5_X_GET_CDC_DATA; + if (ilits->spi_write_then_read(ilits->spi, pre, 5, NULL, 0) < 0) { + ILI_ERR("Failed to write pre commands\n"); + return -1; + } + + pre[0] = SPI_WRITE; + pre[1] = 0x0;// dummy byte + pre[2] = 0x1;// Write len byte + pre[3] = P5_X_GET_CDC_DATA; + if (ilits->spi_write_then_read(ilits->spi, pre, 4, NULL, 0) < 0) { + ILI_ERR("Failed to write pre commands\n"); + return -1; + } + return 0; +} + +static int ili_spi_pll_clk_wakeup(void) +{ + int index = 0; + u8 wdata[32] = {0}; + u8 wakeup[9] = {0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3}; + u32 wlen = sizeof(wakeup); + + wdata[0] = SPI_WRITE; + wdata[1] = wlen >> 8; + wdata[2] = wlen & 0xff; + index = 3; + + ipio_memcpy(&wdata[index], wakeup, wlen, wlen); + wlen += index; + + ILI_INFO("Write dummy to wake up spi pll clk\n"); + if (ilits->spi_write_then_read(ilits->spi, wdata, wlen, NULL, 0) < 0) { + ILI_INFO("spi slave write error\n"); + return -1; + } + mdelay(1); + return 0; +} + +static int ili_spi_wrapper(u8 *txbuf, u32 wlen, u8 *rxbuf, u32 rlen, bool spi_irq, bool i2c_irq) +{ + int ret = 0; + int mode = 0, index = 0; + u8 wdata[128] = {0}; + u8 checksum = 0; + bool ice = atomic_read(&ilits->ice_stat); + + if (wlen > 0) { + if (!txbuf) { + ILI_ERR("txbuf is null\n"); + return -ENOMEM; + } + + /* 3 bytes data consist of length and header */ + if ((wlen + 3) > sizeof(wdata)) { + ILI_ERR("WARNING! wlen(%d) > wdata(%d), using wdata length to transfer\n", wlen, (int)sizeof(wdata)); + wlen = sizeof(wdata) - 3; + } + } + + if (rlen > 0) { + if (!rxbuf) { + ILI_ERR("rxbuf is null\n"); + return -ENOMEM; + } + } + + if (rlen > 0 && !wlen) + mode = SPI_READ; + else + mode = SPI_WRITE; + + if (ilits->int_pulse) + ilits->detect_int_stat = ili_ic_check_int_pulse; + else + ilits->detect_int_stat = ili_ic_check_int_level; + + if (spi_irq) + atomic_set(&ilits->cmd_int_check, ENABLE); + + switch (mode) { + case SPI_WRITE: +#if ( PLL_CLK_WAKEUP_TP_RESUME == ENABLE ) + if (ilits->pll_clk_wakeup == true) { +#else + if ((ilits->pll_clk_wakeup == true) && (ilits->tp_suspend == true)) { +#endif + ret = ili_spi_pll_clk_wakeup(); + if (ret < 0) { + ILI_ERR("Wakeup pll clk error\n"); + break; + } + } + if (ice) { + wdata[0] = SPI_WRITE; + index = 1; + } else { + wdata[0] = SPI_WRITE; + wdata[1] = wlen >> 8; + wdata[2] = wlen & 0xff; + index = 3; + } + + + ipio_memcpy(&wdata[index], txbuf, wlen, wlen); + wlen += index; + + /* + * NOTE: If TP driver is doing MP test and commanding 0xF1 to FW, we add a checksum + * to the last index and plus 1 with size. + */ + if (atomic_read(&ilits->mp_stat) && wdata[index] == P5_X_SET_CDC_INIT) { + checksum = ili_calc_packet_checksum(&wdata[index], wlen - index); + wdata[wlen] = checksum; + wlen++; + wdata[1] = (wlen - index) >> 8; + wdata[2] = (wlen - index) & 0xff; + ili_dump_data(wdata, 8, wlen, 0, "mp cdc cmd with checksum"); + } + + ret = ilits->spi_write_then_read(ilits->spi, wdata, wlen, txbuf, 0); + + if (!ice) { + ILI_INFO("send cmd delay 1ms\n"); + mdelay(1); + } + + if (ret < 0) { + ILI_INFO("spi-wrapper write error\n"); + break; + } + + /* Won't break if it needs to read data following with writing. */ + if (!rlen) + break; + case SPI_READ: + if (!ice && spi_irq) { + /* Check INT triggered by FW when sending cmds. */ + if (ilits->detect_int_stat(false) < 0) { + ILI_ERR("ERROR! Check INT timeout\n"); + ret = -ETIME; + break; + } + } + + ret = ili_spi_mp_pre_cmd(wdata[3]); + if (ret < 0) + ILI_ERR("spi-wrapper mp pre cmd error\n"); + + wdata[0] = SPI_READ; + + ret = ilits->spi_write_then_read(ilits->spi, wdata, 1, rxbuf, rlen); + if (ret < 0) + ILI_ERR("spi-wrapper read error\n"); + + break; + default: + ILI_ERR("Unknown spi mode (%d)\n", mode); + ret = -EINVAL; + break; + } + + if (spi_irq) + atomic_set(&ilits->cmd_int_check, DISABLE); + + return ret; +} + +int ili_core_spi_setup(int num) +{ + u32 freq[] = { + TP_SPI_CLK_1M, + TP_SPI_CLK_2M, + TP_SPI_CLK_3M, + TP_SPI_CLK_4M, + TP_SPI_CLK_5M, + TP_SPI_CLK_6M, + TP_SPI_CLK_7M, + TP_SPI_CLK_8M, + TP_SPI_CLK_9M, + TP_SPI_CLK_10M, + TP_SPI_CLK_11M, + TP_SPI_CLK_12M, + TP_SPI_CLK_13M, + TP_SPI_CLK_14M, + TP_SPI_CLK_15M + }; + + if (num > sizeof(freq)) { + ILI_ERR("Invaild clk freq, set default clk freq\n"); + num = 7; + } + + ILI_INFO("spi clock = %d\n", freq[num]); + + ilits->spi->mode = SPI_MODE_0; + ilits->spi->bits_per_word = 8; + ilits->spi->max_speed_hz = freq[num]; + ilits->spi->chip_select = 0; + + if (spi_setup(ilits->spi) < 0) { + ILI_ERR("Failed to setup spi device\n"); + return -ENODEV; + } + + ILI_INFO("name = %s, bus_num = %d,cs = %d, mode = %d, speed = %d\n", + ilits->spi->modalias, + ilits->spi->master->bus_num, + ilits->spi->chip_select, + ilits->spi->mode, + ilits->spi->max_speed_hz); + return 0; +} + +#ifdef ILI_FW_PANEL +static int ili_parse_tp_module() +{ + int tp_module = 0; + if(active_panel_name) { + if (strstr(active_panel_name, "txd")) { + if (strstr(active_panel_name, "ili9882h")) { + tp_module = MODEL_TXD_9882H; + } else if (strstr(active_panel_name, "ili9882n")) { + tp_module = MODEL_TXD_9882N; + } else if (strstr(active_panel_name, "ili7806s")) { + tp_module = MODEL_TXD_7806S; + } + } else if (strstr(active_panel_name, "tm")) { // || strstr(active_panel_name, "tianma")) { + if (strstr(active_panel_name, "ili9882n")) { + tp_module = MODEL_TM_9882N; + } else if (strstr(active_panel_name, "ili9882h")) { + tp_module = MODEL_TM_9882H; + } else if (strstr(active_panel_name, "ili7807s")) { + tp_module = MODEL_TM_7807S; + } + } else if (strstr(active_panel_name, "tianma") && strstr(active_panel_name, "ili9882n")) { + tp_module = MODEL_TIANMA_9882N; + } else if (strstr(active_panel_name, "csot")) { + if (strstr(active_panel_name, "ili7807s")) { + tp_module = MODEL_CSOT_7807S; + } + } else if (strstr(active_panel_name, "txd")) { + if (strstr(active_panel_name, "ili7807s")) { + tp_module = MODEL_TXD_7807S; + } + } + } + ILI_INFO("ili_parse_tp_module=%d\n", tp_module); + return tp_module; +} +#endif //ILI_FW_PANEL + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#ifdef CONFIG_DRM +static int check_dt(struct device_node *np) +{ + int i; + int count; + int ret = -ENODEV; + bool dts_using_dummy = false; +#ifdef ILI_FW_PANEL + static int retry = 0; +#endif + 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); + ILI_INFO("node->name %s !\n", node->name); + if(strstr(node->name, "dummy")) { + dts_using_dummy = true; + } + of_node_put(node); +#ifdef ILI_FW_PANEL + if (!IS_ERR(panel)) { + ili_active_panel = panel; + active_panel_name = node->name; + ILI_INFO("%s: actived\n", active_panel_name); + ret = 0; + } + } + + if(dts_using_dummy && ret) { + ILI_INFO("Retry times %d.\n", retry); + ret = -EPROBE_DEFER; + if(retry++ > 5) + ret = -ENODEV; + } + + if(active_panel_name != NULL) { + if(strstr(active_panel_name, "dummy")) { + ILI_INFO("Using dummy panel! Return!\n"); + ret = -ENODEV; + } + } +#else + if (!IS_ERR(panel)) { + ili_active_panel = panel; + pr_err("%s: ili9882h HLT actived\n", __func__); + return MODEL_HLT; + } else { + node = of_parse_phandle(np, "panel2", i); + panel = of_drm_find_panel(node); + of_node_put(node); + if (!IS_ERR(panel)) { + ili_active_panel = panel; + pr_err("%s: ili9882n TM actived\n", __func__); + return MODEL_TM; + } + } + } +#endif //ILI_FW_PANEL + + if (node) + pr_err("%s: %s not actived\n", __func__, node->name); + return ret; +} +#endif +#endif + +#ifdef CONFIG_DRM +static int parse_dt(struct device_node *np) +{ + int32_t ret = 0; + +#ifdef ILI_SENSOR_EN + if (of_property_read_bool(np, "ilitek,report_gesture_key")) { + ILI_INFO("ilitek,report_gesture_key set"); + ilits->report_gesture_key = 1; + } else { + ilits->report_gesture_key = 0; + } +#endif + +#ifdef ILI_CONFIG_PANEL_GESTURE + //parse gesture by panel config + if (active_panel_name) { + const char *panel_gesture; + int panel_num = of_property_count_strings(np, "ilitek,gesture_panel"); + if (panel_num > 0) { + int i; + ILI_INFO("%s: get ilitek,gesture_panel count=%d", __func__, panel_num); + for (i = 0; i < panel_num; i++) { + ret = of_property_read_string_index(np, "ilitek,gesture_panel", i, &panel_gesture); + if (ret < 0) { + ILI_INFO("%s: cannot parse gesture_panel, ret=%d\n", __func__, ret); + break; + } else if (panel_gesture && strstr(active_panel_name, panel_gesture)) { + ILI_INFO("%s: panel %s gesture enabled", __func__, panel_gesture); + ilits->panel_gesture_enable = true; + break; + } + } + } + } +#endif + + return ret; +} +#endif + +static int ilitek_spi_probe(struct spi_device *spi) +{ + struct touch_bus_info *info = + container_of(to_spi_driver(spi->dev.driver), + struct touch_bus_info, bus_driver); + int tp_module = 0; +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#ifdef ILI_FW_PANEL + int ret; +#endif +#endif + ILI_INFO("ilitek spi probe\n"); + + if (!spi) { + ILI_ERR("spi device is NULL\n"); + return -ENODEV; + } + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#ifdef CONFIG_DRM +{ + struct device_node *dp = spi->dev.of_node; + +#ifdef ILI_FW_PANEL + ret = check_dt(dp); + if (ret) { + ILI_INFO("panel error\n"); + return ret; + } +#else + if ((tp_module = check_dt(dp)) < 0) { + ILI_ERR("%s: %s not actived\n", __func__, dp->name); + return -ENODEV; + } +#endif +} +#endif +#endif +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + if (spi->dev.of_node && !mmi_device_is_available(spi->dev.of_node)) { + ILI_ERR("mmi: device not supported\n"); + return -ENODEV; + } +#endif + ilits = devm_kzalloc(&spi->dev, sizeof(struct ilitek_ts_data), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits)) { + ILI_ERR("Failed to allocate ts memory, %ld\n", PTR_ERR(ilits)); + return -ENOMEM; + } + + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) { + ILI_ERR("Full duplex not supported by master\n"); + return -EIO; + } + + ilits->update_buf = kzalloc(MAX_HEX_FILE_SIZE, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(ilits->update_buf)) { + ILI_ERR("fw kzalloc error\n"); + return -ENOMEM; + } + + /* Used for receiving touch data only, do not mix up with others. */ + ilits->tr_buf = kzalloc(TR_BUF_SIZE, GFP_ATOMIC); + if (ERR_ALLOC_MEM(ilits->tr_buf)) { + ILI_ERR("failed to allocate touch report buffer\n"); + return -ENOMEM; + } + + ilits->spi_tx = kzalloc(SPI_TX_BUF_SIZE, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(ilits->spi_tx)) { + ILI_ERR("Failed to allocate spi tx buffer\n"); + return -ENOMEM; + } + + ilits->spi_rx = kzalloc(SPI_RX_BUF_SIZE, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(ilits->spi_rx)) { + ILI_ERR("Failed to allocate spi rx buffer\n"); + return -ENOMEM; + } + + ilits->gcoord = kzalloc(sizeof(struct gesture_coordinate), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->gcoord)) { + ILI_ERR("Failed to allocate gresture coordinate buffer\n"); + return -ENOMEM; + } + + ilits->spi = spi; + spi_set_drvdata(spi, ilits); + +//---parse tp module--- +#ifdef ILI_FW_PANEL + tp_module = ili_parse_tp_module(); +#endif + + ilits->i2c = NULL; + ilits->spi = spi; + ilits->dev = &spi->dev; + ilits->hwif = info->hwif; + ilits->phys = "SPI"; + ilits->wrapper = ili_spi_wrapper; + ilits->detect_int_stat = ili_ic_check_int_pulse; + ilits->int_pulse = true; + ilits->mp_retry = false; + ilits->tp_module = tp_module; + +#if SPI_DMA_TRANSFER_SPLIT + ilits->spi_write_then_read = ili_spi_write_then_read_split; +#else + ilits->spi_write_then_read = ili_spi_write_then_read_direct; +#endif + + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + ilits->tp_data_format = DATA_FORMAT_DEMO; + ilits->tp_data_len = P5_X_DEMO_MODE_PACKET_LEN; + + if (TDDI_RST_BIND) + ilits->reset = TP_IC_WHOLE_RST; + else + ilits->reset = TP_HW_RST_ONLY; + + ilits->rst_edge_delay = 10; + ilits->fw_open = REQUEST_FIRMWARE; + ilits->fw_upgrade_mode = UPGRADE_IRAM; + ilits->mp_move_code = ili_move_mp_code_iram; + ilits->gesture_move_code = ili_move_gesture_code_iram; + ilits->esd_recover = ili_wq_esd_spi_check; + ilits->ges_recover = ili_touch_esd_gesture_iram; + ilits->gesture_mode = DATA_FORMAT_GESTURE_INFO; + ilits->gesture_demo_ctrl = DISABLE; + ilits->wtd_ctrl = OFF; + ilits->report = ENABLE; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + ilits->netlink = DISABLE; +#endif + ilits->dnp = DISABLE; + ilits->irq_tirgger_type = IRQF_TRIGGER_FALLING; + ilits->info_from_hex = ENABLE; + ilits->wait_int_timeout = AP_INT_TIMEOUT; + + //---parse dts--- +#ifdef CONFIG_DRM + parse_dt(spi->dev.of_node); +#endif + +#if ENABLE_GESTURE +#ifdef ILI_CONFIG_PANEL_GESTURE + if (ilits->panel_gesture_enable) +#elif defined(ILI_CONFIG_GESTURE) + if (MODEL_HLT == tp_module) +#endif + { + ilits->gesture = ENABLE; + ilits->ges_sym.double_tap = DOUBLE_TAP; + ilits->ges_sym.alphabet_line_2_top = ALPHABET_LINE_2_TOP; + ilits->ges_sym.alphabet_line_2_bottom = ALPHABET_LINE_2_BOTTOM; + ilits->ges_sym.alphabet_line_2_left = ALPHABET_LINE_2_LEFT; + ilits->ges_sym.alphabet_line_2_right = ALPHABET_LINE_2_RIGHT; + ilits->ges_sym.alphabet_m = ALPHABET_M; + ilits->ges_sym.alphabet_w = ALPHABET_W; + ilits->ges_sym.alphabet_c = ALPHABET_C; + ilits->ges_sym.alphabet_E = ALPHABET_E; + ilits->ges_sym.alphabet_V = ALPHABET_V; + ilits->ges_sym.alphabet_O = ALPHABET_O; + ilits->ges_sym.alphabet_S = ALPHABET_S; + ilits->ges_sym.alphabet_Z = ALPHABET_Z; + ilits->ges_sym.alphabet_V_down = ALPHABET_V_DOWN; + ilits->ges_sym.alphabet_V_left = ALPHABET_V_LEFT; + ilits->ges_sym.alphabet_V_right = ALPHABET_V_RIGHT; + ilits->ges_sym.alphabet_two_line_2_bottom = ALPHABET_TWO_LINE_2_BOTTOM; + ilits->ges_sym.alphabet_F = ALPHABET_F; + ilits->ges_sym.alphabet_AT = ALPHABET_AT; + + ILI_INFO("ilitek: gesture eanble:%d\n", ilits->gesture); + } +#endif + + if (ili_core_spi_setup(SPI_CLK) < 0) + return -EINVAL; + + return info->hwif->plat_probe(); +} + +static int ilitek_spi_remove(struct spi_device *spi) +{ + ILI_INFO(); + return 0; +} + +static struct spi_device_id tp_spi_id[] = { + {TDDI_DEV_ID, 0}, + {}, +}; + +int ili_interface_dev_init(struct ilitek_hwif_info *hwif) +{ + struct touch_bus_info *info; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ILI_ERR("faied to allocate spi_driver\n"); + return -ENOMEM; + } + + if (hwif->bus_type != BUS_SPI) { + ILI_ERR("Not SPI dev\n"); + ipio_kfree((void **)&info); + return -EINVAL; + } + + hwif->info = info; + + info->bus_driver.driver.name = hwif->name; + info->bus_driver.driver.owner = hwif->owner; + info->bus_driver.driver.of_match_table = hwif->of_match_table; + info->bus_driver.driver.pm = hwif->pm; + + info->bus_driver.probe = ilitek_spi_probe; + info->bus_driver.remove = ilitek_spi_remove; + info->bus_driver.id_table = tp_spi_id; + + info->hwif = hwif; + return spi_register_driver(&info->bus_driver); +} + +void ili_interface_dev_exit(struct ilitek_ts_data *ts) +{ + struct touch_bus_info *info = (struct touch_bus_info *)ilits->hwif->info; + + ILI_INFO("remove spi dev\n"); + kfree(ilits->update_buf); + kfree(ilits->spi_tx); + kfree(ilits->spi_rx); + spi_unregister_driver(&info->bus_driver); + ipio_kfree((void **)&info); +} diff --git a/drivers/input/touchscreen/ili9882_mmi/ili9882_touch.c b/drivers/input/touchscreen/ili9882_mmi/ili9882_touch.c new file mode 100755 index 000000000000..3fb931d77697 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili9882_touch.c @@ -0,0 +1,1491 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ili9882.h" + +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS +#include "moto_ts_dda.h" +#endif + + +/*gesture info mode*/ +struct ili_demo_debug_info_id0 { + u32 id : 8; + u32 sys_powr_state_e : 3; + u32 sys_state_e : 3; + u32 tp_state_e : 2; + + u32 touch_palm_state : 2; + u32 app_an_statu_e : 3; + u32 app_sys_bg_err : 1; + u32 g_b_wrong_bg : 1; + u32 reserved0 : 1; + + u32 normal_mode : 1; + u32 charger_mode : 1; + u32 glove_mode : 1; + u32 stylus_mode : 1; + u32 multi_mode : 1; + u32 noise_mode : 1; + u32 palm_plus_mode : 1; + u32 floating_mode : 1; + + u32 algo_pt_status0 : 3; + u32 algo_pt_status1 : 3; + u32 algo_pt_status2 : 3; + u32 algo_pt_status3 : 3; + u32 algo_pt_status4 : 3; + u32 algo_pt_status5 : 3; + u32 algo_pt_status6 : 3; + u32 algo_pt_status7 : 3; + u32 algo_pt_status8 : 3; + u32 algo_pt_status9 : 3; + u32 reserved2 : 2; + + u32 hopping_flg : 1; + u32 hopping_index : 5; + u32 frequency_h : 2; + u32 frequency_l : 8; + u32 reserved3 : 8; + u32 reserved4 : 8; + +}; + +void ili_dump_data(void *data, int type, int len, int row_len, const char *name) +{ + int i, row = 31; + u8 *p8 = NULL; + s32 *p32 = NULL; + s16 *p16 = NULL; + + if (!debug_en) + return; + + if (row_len > 0) + row = row_len; + + if (data == NULL) { + ILI_ERR("The data going to dump is NULL\n"); + return; + } + + pr_cont("\n\n"); + pr_cont("ILITEK: Dump %s data\n", name); + pr_cont("ILITEK: "); + + if (type == 8) + p8 = (u8 *) data; + if (type == 32 || type == 10) + p32 = (s32 *) data; + if (type == 16) + p16 = (s16 *) data; + + for (i = 0; i < len; i++) { + if (type == 8) + pr_cont(" %4x ", p8[i]); + else if (type == 32) + pr_cont(" %4x ", p32[i]); + else if (type == 10) + pr_cont(" %4d ", p32[i]); + else if (type == 16) + pr_cont(" %4d ", p16[i]); + + if ((i % row) == row - 1) { + pr_cont("\n"); + pr_cont("ILITEK: "); + } + } + pr_cont("\n\n"); +} + +static void dma_clear_reg_setting(void) +{ + /* 1. interrupt t0/t1 enable flag */ + if (ili_ice_mode_bit_mask_write(INTR32_ADDR, INTR32_reg_t0_int_en | INTR32_reg_t1_int_en, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR32_reg_t0_int_en | INTR32_reg_t1_int_en, INTR32_ADDR); + + /* 2. clear tdi_err_int_flag */ + if (ili_ice_mode_bit_mask_write(INTR2_ADDR, INTR2_tdi_err_int_flag_clear, (1 << 18)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR2_tdi_err_int_flag_clear, INTR2_ADDR); + + /* 3. clear dma channel 0 src1 info */ + if (ili_ice_mode_write(DMA49_reg_dma_ch0_src1_addr, 0x00000000, 4) < 0) + ILI_ERR("Write 0x00000000 at %x failed\n", DMA49_reg_dma_ch0_src1_addr); + if (ili_ice_mode_write(DMA50_reg_dma_ch0_src1_step_inc, 0x00, 1) < 0) + ILI_ERR("Write 0x0 at %x failed\n", DMA50_reg_dma_ch0_src1_step_inc); + if (ili_ice_mode_bit_mask_write(DMA50_ADDR, DMA50_reg_dma_ch0_src1_format | DMA50_reg_dma_ch0_src1_en, BIT(31)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA50_reg_dma_ch0_src1_format | DMA50_reg_dma_ch0_src1_en, DMA50_ADDR); + + /* 4. clear dma channel 0 trigger select */ + if (ili_ice_mode_bit_mask_write(DMA48_ADDR, DMA48_reg_dma_ch0_trigger_sel, (0 << 16)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA48_reg_dma_ch0_trigger_sel, DMA48_ADDR); + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + /* 5. clear dma flash setting */ + ili_flash_clear_dma(); +} + +static void dma_trigger_reg_setting(u32 reg_dest_addr, u32 flash_start_addr, u32 copy_size) +{ + int retry = 30; + u32 stat = 0; + + /* 1. set dma channel 0 clear */ + if (ili_ice_mode_bit_mask_write(DMA48_ADDR, DMA48_reg_dma_ch0_start_clear, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA48_reg_dma_ch0_start_clear, DMA48_ADDR); + + /* 2. set dma channel 0 src1 info */ + if (ili_ice_mode_write(DMA49_reg_dma_ch0_src1_addr, 0x00041010, 4) < 0) + ILI_ERR("Write 0x00041010 at %x failed\n", DMA49_reg_dma_ch0_src1_addr); + if (ili_ice_mode_write(DMA50_reg_dma_ch0_src1_step_inc, 0x00, 1) < 0) + ILI_ERR("Write 0x00 at %x failed\n", DMA50_reg_dma_ch0_src1_step_inc); + if (ili_ice_mode_bit_mask_write(DMA50_ADDR, DMA50_reg_dma_ch0_src1_format | DMA50_reg_dma_ch0_src1_en, BIT(31)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA50_reg_dma_ch0_src1_format | DMA50_reg_dma_ch0_src1_en, DMA50_ADDR); + + /* 3. set dma channel 0 src2 info */ + if (ili_ice_mode_bit_mask_write(DMA52_ADDR, DMA52_reg_dma_ch0_src2_en, (0 << 31)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA52_reg_dma_ch0_src2_en, DMA52_ADDR); + + /* 4. set dma channel 0 dest info */ + if (ili_ice_mode_write(DMA53_reg_dma_ch0_dest_addr, reg_dest_addr, 3) < 0) + ILI_ERR("Write %x at %x failed\n", reg_dest_addr, DMA53_reg_dma_ch0_dest_addr); + if (ili_ice_mode_write(DMA54_reg_dma_ch0_dest_step_inc, 0x01, 1) < 0) + ILI_ERR("Write 0x01 at %x failed\n", DMA54_reg_dma_ch0_dest_step_inc); + + if (ili_ice_mode_write(DMA54_ADDR, DMA54_reg_dma_ch0_dest_format | DMA54_reg_dma_ch0_dest_en, BIT(31)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA54_reg_dma_ch0_dest_format | DMA54_reg_dma_ch0_dest_en, DMA54_ADDR); + + /* 5. set dma channel 0 trafer info */ + if (ili_ice_mode_write(DMA55_reg_dma_ch0_trafer_counts, copy_size, 4) < 0) + ILI_ERR("Write %x at %x failed\n", copy_size, DMA55_reg_dma_ch0_trafer_counts); + if (ili_ice_mode_bit_mask_write(DMA55_ADDR, DMA55_reg_dma_ch0_trafer_mode, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA55_reg_dma_ch0_trafer_mode, DMA55_ADDR); + + /* 6. set dma channel 0 int info */ + if (ili_ice_mode_bit_mask_write(INTR33_ADDR, INTR33_reg_dma_ch0_int_en, (1 << 17)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR33_reg_dma_ch0_int_en, INTR33_ADDR); + + /* 7. set dma channel 0 trigger select */ + if (ili_ice_mode_bit_mask_write(DMA48_ADDR, DMA48_reg_dma_ch0_trigger_sel, (1 << 16)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA48_reg_dma_ch0_trigger_sel, DMA48_ADDR); + + /* 8. set dma flash setting */ + ili_flash_dma_write(flash_start_addr, (flash_start_addr+copy_size), copy_size); + + /* 9. clear flash and dma ch0 int flag */ + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_dma_ch1_int_flag | INTR1_reg_flash_int_flag, BIT(16) | BIT(25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_dma_ch1_int_flag | INTR1_reg_flash_int_flag, INTR1_ADDR); + if (ili_ice_mode_bit_mask_write(0x041013, BIT(0), 1) < 0) //patch + ILI_ERR("Write %lu at %x failed\n", BIT(0), 0x041013); + + /* DMA Trigger */ + if (ili_ice_mode_write(FLASH4_reg_rcv_data, 0xFF, 1) < 0) + ILI_ERR("Trigger DMA failed\n"); + + /* waiting for fw reload code completed. */ + while (retry > 0) { + if (ili_ice_mode_read(INTR1_ADDR, &stat, sizeof(u32)) < 0) { + ILI_ERR("Read 0x%x error\n", INTR1_ADDR); + retry--; + continue; + } + + ILI_DBG("fw dma stat = %x\n", stat); + + if ((stat & BIT(16)) == BIT(16)) + break; + + retry--; + usleep_range(1000, 1000); + } + + if (retry <= 0) + ILI_ERR("DMA fail: Regsiter = 0x%x Flash = 0x%x, Size = %d\n", + reg_dest_addr, flash_start_addr, copy_size); + + /* CS High */ + if (ili_ice_mode_write(FLASH0_reg_flash_csb, 0x1, 1) < 0) + ILI_ERR("Pull CS High failed\n"); + /* waiting for CS status done */ + mdelay(10); +} + +int ili_move_mp_code_flash(void) +{ + int ret = 0; + u32 mp_text_size = 0, mp_andes_init_size = 0; + u32 mp_flash_addr, mp_size, overlay_start_addr, overlay_end_addr; + bool dma_trigger_enable = 0; + u8 cmd[2] = {0}; + u8 data[16] = {0}; + + cmd[0] = P5_X_MP_TEST_MODE_INFO; + ret = ilits->wrapper(cmd, sizeof(u8), data, ilits->protocol->mp_info_len, ON, OFF); + ili_dump_data(data, 8, ilits->protocol->mp_info_len, 0, "MP overlay info"); + if (ret < 0) { + ILI_ERR("Failed to write info cmd\n"); + goto out; + } + + dma_trigger_enable = 0; + + mp_flash_addr = data[3] + (data[2] << 8) + (data[1] << 16); + mp_size = data[6] + (data[5] << 8) + (data[4] << 16); + overlay_start_addr = data[9] + (data[8] << 8) + (data[7] << 16); + overlay_end_addr = data[12] + (data[11] << 8) + (data[10] << 16); + + if (overlay_start_addr != 0x0 && overlay_end_addr != 0x0 + && data[0] == P5_X_MP_TEST_MODE_INFO) + dma_trigger_enable = 1; + + ILI_INFO("MP info Overlay: Enable = %d, addr = 0x%x ~ 0x%x, flash addr = 0x%x, mp size = 0x%x\n", + dma_trigger_enable, overlay_start_addr, + overlay_end_addr, mp_flash_addr, mp_size); + + cmd[0] = P5_X_MODE_CONTROL; + cmd[1] = P5_X_FW_TEST_MODE; + ret = ilits->wrapper(cmd, 2, NULL, 0, ON, OFF); + if (ret < 0) + goto out; + + /* Check if ic is ready switching test mode from demo mode */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + ret = ili_ic_check_busy(50, 50); /* Set busy as 0x41 */ + if (ret < 0) + goto out; + + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) + goto out; + + if (dma_trigger_enable) { + mp_andes_init_size = overlay_start_addr; + mp_text_size = (mp_size - overlay_end_addr) + 1; + ILI_INFO("MP andes init size = %d , MP text size = %d\n", mp_andes_init_size, mp_text_size); + + dma_clear_reg_setting(); + + ILI_INFO("[Move ANDES.INIT to DRAM]\n"); + dma_trigger_reg_setting(0, mp_flash_addr, mp_andes_init_size); /* DMA ANDES.INIT */ + + dma_clear_reg_setting(); + + ILI_INFO("[Move MP.TEXT to DRAM]\n"); + dma_trigger_reg_setting(overlay_end_addr, (mp_flash_addr + overlay_start_addr), mp_text_size); + + dma_clear_reg_setting(); + } else { + /* DMA Trigger */ + if (ili_ice_mode_write(FLASH4_reg_rcv_data, 0xFF, 1) < 0) + ILI_ERR("Trigger DMA failed\n"); + /* waiting for fw reload code completed. */ + mdelay(30); + + /* CS High */ + if (ili_ice_mode_write(FLASH0_reg_flash_csb, 0x1, 1) < 0) + ILI_ERR("Pull CS High failed\n"); + /* waiting for CS status done */ + mdelay(10); + } + + if (ili_reset_ctrl(TP_IC_CODE_RST) < 0) + ILI_ERR("IC Code reset failed during moving mp code\n"); + + ret = ili_ice_mode_ctrl(DISABLE, OFF); + if (ret < 0) + goto out; + + /* Check if ic is already in test mode */ + ilits->actual_tp_mode = P5_X_FW_TEST_MODE; /* set busy as 0x51 */ + ret = ili_ic_check_busy(300, 50); + if (ret < 0) + ILI_ERR("Check cdc timeout failed after moved mp code\n"); + +out: + return ret; +} + +int ili_move_mp_code_iram(void) +{ + ILI_INFO("Download MP code to iram\n"); + return ili_fw_upgrade_handler(NULL); +} + +int ili_proximity_near(int mode) +{ + int ret = 0; + + ilits->prox_near = true; + + switch (mode) { + case DDI_POWER_ON: + /* + * If the power of VSP and VSN keeps alive when proximity near event + * occures, TP can just go to sleep in. + */ + ret = ili_ic_func_ctrl("sleep", SLEEP_IN); + if (ret < 0) + ILI_ERR("Write sleep in cmd failed\n"); + break; + case DDI_POWER_OFF: + ILI_INFO("DDI POWER OFF, do nothing\n"); + break; + default: + ILI_ERR("Unknown mode (%d)\n", mode); + ret = -EINVAL; + break; + } + return ret; +} + +int ili_proximity_far(int mode) +{ + int ret = 0; + u8 cmd[2] = {0}; + + if (!ilits->prox_near) { + ILI_INFO("No proximity near event, break\n"); + return 0; + } + + switch (mode) { + case WAKE_UP_GESTURE_RECOVERY: + /* + * If the power of VSP and VSN has been shut down previsouly, + * TP should go through gesture recovery to get back. + */ + ili_gesture_recovery(); + break; + case WAKE_UP_SWITCH_GESTURE_MODE: + /* + * If the power of VSP and VSN keeps alive in the event of proximity near, + * TP can be just recovered by switching gesture mode to get back. + */ + cmd[0] = 0xF6; + cmd[1] = 0x0A; + + ILI_INFO("write prepare gesture command 0xF6 0x0A\n"); + ret = ilits->wrapper(cmd, 2, NULL, 0, ON, OFF); + if (ret < 0) { + ILI_INFO("write prepare gesture command error\n"); + break; + } + + ret = ili_switch_tp_mode(P5_X_FW_GESTURE_MODE); + if (ret < 0) + ILI_ERR("Switch to gesture mode failed during proximity far\n"); + break; + default: + ILI_ERR("Unknown mode (%d)\n", mode); + ret = -EINVAL; + break; + } + + ilits->prox_near = false; + + return ret; +} + +int ili_move_gesture_code_flash(int mode) +{ + int ret = 0; + + /* + * NOTE: If functions need to be added during suspend, + * they must be called before gesture cmd reaches to FW. + */ + + ILI_INFO("Gesture mode = %d\n", mode); + ret = ili_set_tp_data_len(mode, true, NULL); + + return ret; +} + +void ili_set_gesture_symbol(void) +{ + u8 cmd[7] = {0}; + struct gesture_symbol *ptr_sym = &ilits->ges_sym; + u8 *ptr; + + ptr = (u8*) ptr_sym; + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = 0x01; + cmd[2] = 0x0A; + cmd[3] = 0x08; + cmd[4] = ptr[0]; + cmd[5] = ptr[1]; + cmd[6] = ptr[2]; + + ili_dump_data(cmd, 8, sizeof(cmd), 0, "Gesture symbol"); + + if (ilits->wrapper(cmd, 2, NULL, 0, ON, OFF) < 0) { + ILI_ERR("Write pre cmd failed\n"); + return; + + } + + if (ilits->wrapper(&cmd[1], (sizeof(cmd) - 1), NULL, 0, ON, OFF)) { + ILI_ERR("Write gesture symbol fail\n"); + return; + } + + ILI_DBG(" double_tap = %d\n", ilits->ges_sym.double_tap); + ILI_DBG(" alphabet_line_2_top = %d\n", ilits->ges_sym.alphabet_line_2_top); + ILI_DBG(" alphabet_line_2_bottom = %d\n", ilits->ges_sym.alphabet_line_2_bottom); + ILI_DBG(" alphabet_line_2_left = %d\n", ilits->ges_sym.alphabet_line_2_left); + ILI_DBG(" alphabet_line_2_right = %d\n", ilits->ges_sym.alphabet_line_2_right); + ILI_DBG(" alphabet_w = %d\n", ilits->ges_sym.alphabet_w); + ILI_DBG(" alphabet_c = %d\n", ilits->ges_sym.alphabet_c); + ILI_DBG(" alphabet_E = %d\n", ilits->ges_sym.alphabet_E); + ILI_DBG(" alphabet_V = %d\n", ilits->ges_sym.alphabet_V); + ILI_DBG(" alphabet_O = %d\n", ilits->ges_sym.alphabet_O); + ILI_DBG(" alphabet_S = %d\n", ilits->ges_sym.alphabet_S); + ILI_DBG(" alphabet_Z = %d\n", ilits->ges_sym.alphabet_Z); + ILI_DBG(" alphabet_V_down = %d\n", ilits->ges_sym.alphabet_V_down); + ILI_DBG(" alphabet_V_left = %d\n", ilits->ges_sym.alphabet_V_left); + ILI_DBG(" alphabet_V_right = %d\n", ilits->ges_sym.alphabet_V_right); + ILI_DBG(" alphabet_two_line_2_bottom= %d\n", ilits->ges_sym.alphabet_two_line_2_bottom); + ILI_DBG(" alphabet_F= %d\n", ilits->ges_sym.alphabet_F); + ILI_DBG(" alphabet_AT= %d\n", ilits->ges_sym.alphabet_AT); + +} + +int ili_move_gesture_code_iram(int mode) +{ + int i, ret = 0, timeout = 100; + u8 cmd[2] = {0}; + u8 cmd_write[3] = {0x01,0x0A,0x05}; + + /* + * NOTE: If functions need to be added during suspend, + * they must be called before gesture cmd reaches to FW. + */ + + ILI_INFO("Gesture code loaded by %s\n", ilits->gesture_load_code ? "driver" : "firmware" ); + + if (!ilits->gesture_load_code) { + ret = ili_set_tp_data_len(mode, true, NULL); + goto out; + } + + /*pre-command for ili_ic_func_ctrl("lpwg", 0x3)*/ + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = 0x1; + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write 0xF6,0x1 failed\n"); + goto out; + } + + ret = ili_ic_func_ctrl("lpwg", 0x3); + if (ret < 0) { + ILI_ERR("write gesture flag failed\n"); + goto out; + } + + ret = ili_set_tp_data_len(mode, true, NULL); + if (ret < 0) { + ILI_ERR("Failed to set tp data length\n"); + goto out; + } + + ili_irq_enable(); + /* Prepare Check Ready */ + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = 0xA; + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write 0xF6,0xA failed\n"); + goto out; + } + + for (i = 0; i < timeout; i++) { + /* Check ready for load code */ + ret = ilits->wrapper(cmd_write, sizeof(cmd_write), cmd, sizeof(u8), ON, OFF); + ILI_DBG("gesture ready byte = 0x%x\n", cmd[0]); + + if (cmd[0] == 0x91) { + ILI_INFO("Ready to load gesture code\n"); + break; + } + } + mdelay(2); + ili_irq_disable(); + + if (i >= timeout) { + ILI_ERR("Gesture is not ready (0x%x), try to run its recovery\n", cmd[0]); + return ili_gesture_recovery(); + } + + ret = ili_fw_upgrade_handler(NULL); + if (ret < 0) { + ILI_ERR("FW upgrade failed during moving code\n"); + goto out; + } + + /* Resume gesture loader */ + ret = ili_ic_func_ctrl("lpwg", 0x6); + if (ret < 0) { + ILI_ERR("write resume loader error"); + goto out; + } + +out: + return ret; +} + +u8 ili_calc_packet_checksum(u8 *packet, int len) +{ + int i; + s32 sum = 0; + + for (i = 0; i < len; i++) + sum += packet[i]; + + return (u8) ((-sum) & 0xFF); +} + +int ili_touch_esd_gesture_flash(void) +{ + int ret = 0, retry = 100; + u32 answer = 0; + int ges_pwd_addr = I2C_ESD_GESTURE_CORE146_PWD_ADDR; + int ges_pwd = ESD_GESTURE_CORE146_PWD; + int ges_run = I2C_ESD_GESTURE_CORE146_RUN; + int pwd_len = 2; + + if (ilits->chip->core_ver < CORE_VER_1460) { + ges_pwd_addr = I2C_ESD_GESTURE_PWD_ADDR; + ges_pwd = ESD_GESTURE_PWD; + ges_run = I2C_ESD_GESTURE_RUN; + pwd_len = 4; + } + + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) { + ILI_ERR("Enable ice mode failed during gesture recovery\n"); + return ret; + } + + ILI_INFO("ESD Gesture PWD Addr = 0x%X, PWD = 0x%X, GES_RUN = 0%X, core_ver = 0x%X\n", + ges_pwd_addr, ges_pwd, ges_run, ilits->chip->core_ver); + + /* write a special password to inform FW go back into gesture mode */ + ret = ili_ice_mode_write(ges_pwd_addr, ges_pwd, pwd_len); + if (ret < 0) { + ILI_ERR("write password failed\n"); + goto out; + } + + /* HW reset gives effect to FW receives password successed */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + ret = ili_reset_ctrl(ilits->reset); + if (ret < 0) { + ILI_ERR("TP Reset failed during gesture recovery\n"); + goto out; + } + + ret = ili_ice_mode_ctrl(ENABLE, ON); + if (ret < 0) { + ILI_ERR("Enable ice mode failed during gesture recovery\n"); + goto out; + } + + /* polling another specific register to see if gesutre is enabled properly */ + do { + ret = ili_ice_mode_read(ges_pwd_addr, &answer, pwd_len); + if (ret < 0) { + ILI_ERR("Read gesture answer error\n"); + goto out; + } + + if (answer != ges_run) + ILI_INFO("ret = 0x%X, answer = 0x%X\n", answer, ges_run); + + mdelay(2); + } while (answer != ges_run && --retry > 0); + + if (retry <= 0) { + ILI_ERR("Enter gesture failed\n"); + ret = -1; + goto out; + } + + ILI_INFO("0x%X Enter gesture successfully\n", answer); + +out: + if (ili_ice_mode_ctrl(DISABLE, ON) < 0) { + ILI_ERR("Disable ice mode failed during gesture recovery\n"); + } + + if (ret >= 0) { + ilits->actual_tp_mode = P5_X_FW_GESTURE_MODE; + ili_set_tp_data_len(ilits->gesture_mode, false, NULL); + } + + return ret; +} + +int ili_touch_esd_gesture_iram(void) +{ + int ret = 0, retry = 100; + u32 answer = 0; + int ges_pwd_addr = SPI_ESD_GESTURE_CORE146_PWD_ADDR; + int ges_pwd = ESD_GESTURE_CORE146_PWD; + int ges_run = SPI_ESD_GESTURE_CORE146_RUN; + int pwd_len = 2; + + if (!ilits->gesture_load_code) + ges_run = I2C_ESD_GESTURE_CORE146_RUN; + + if (ilits->chip->core_ver < CORE_VER_1460) { + if (ilits->chip->core_ver >= CORE_VER_1420) + ges_pwd_addr = I2C_ESD_GESTURE_PWD_ADDR; + else + ges_pwd_addr = SPI_ESD_GESTURE_PWD_ADDR; + ges_pwd = ESD_GESTURE_PWD; + ges_run = SPI_ESD_GESTURE_RUN; + pwd_len = 4; + } + + ILI_INFO("ESD Gesture PWD Addr = 0x%X, PWD = 0x%X, GES_RUN = 0%X, core_ver = 0x%X\n", + ges_pwd_addr, ges_pwd, ges_run, ilits->chip->core_ver); + + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) { + ILI_ERR("Enable ice mode failed during gesture recovery\n"); + goto fail; + } + + /* write a special password to inform FW go back into gesture mode */ + ret = ili_ice_mode_write(ges_pwd_addr, ges_pwd, pwd_len); + if (ret < 0) { + ILI_ERR("write password failed\n"); + goto fail; + } + + /* Host download gives effect to FW receives password successed */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + ret = ili_fw_upgrade_handler(NULL); + if (ret < 0) { + ILI_ERR("FW upgrade failed during gesture recovery\n"); + goto fail; + } + + /* Wait for fw running code finished. */ + if (ilits->info_from_hex || (ilits->chip->core_ver >= CORE_VER_1410)) + msleep(50); + + + ret = ili_ice_mode_ctrl(ENABLE, ON); + if (ret < 0) { + ILI_ERR("Enable ice mode failed during gesture recovery\n"); + goto fail; + } + + /* polling another specific register to see if gesutre is enabled properly */ + do { + ret = ili_ice_mode_read(ges_pwd_addr, &answer, pwd_len); + if (ret < 0) { + ILI_ERR("Read gesture answer error\n"); + goto fail; + } + + if (answer != ges_run) + ILI_INFO("ret = 0x%X, answer = 0x%X\n", answer, ges_run); + + mdelay(2); + } while (answer != ges_run && --retry > 0); + + if (retry <= 0) { + ILI_ERR("Enter gesture failed\n"); + ret = -1; + goto fail; + } + + ILI_INFO("Enter gesture successfully\n"); + + ret = ili_ice_mode_ctrl(DISABLE, ON); + if (ret < 0) { + ILI_ERR("Disable ice mode failed during gesture recovery\n"); + goto fail; + } + + ILI_INFO("Gesture code loaded by %s\n", ilits->gesture_load_code ? "driver" : "firmware"); + + if (!ilits->gesture_load_code) { + ilits->actual_tp_mode = P5_X_FW_GESTURE_MODE; + ili_set_tp_data_len(ilits->gesture_mode, false, NULL); + goto out; + } + + /* Load gesture code */ + ilits->actual_tp_mode = P5_X_FW_GESTURE_MODE; + ili_set_tp_data_len(ilits->gesture_mode, false, NULL); + ret = ili_fw_upgrade_handler(NULL); + if (ret < 0) { + ILI_ERR("Failed to load code during gesture recovery\n"); + goto fail; + } + + /* Resume gesture loader */ + ret = ili_ic_func_ctrl("lpwg", 0x6); + if (ret < 0) { + ILI_ERR("write resume loader error"); + goto fail; + } + +out: + return ret; + +fail: + ilits->actual_tp_mode = P5_X_FW_GESTURE_MODE; + ili_ice_mode_ctrl(DISABLE, ON); + return ret; +} + +void ili_demo_debug_info_id0(u8 *buf, size_t len) +{ + struct ili_demo_debug_info_id0 id0; + + ipio_memcpy(&id0, buf, sizeof(id0), len); + ILI_INFO("id0 len = %d,strucy len = %d", (int)len, (int)sizeof(id0)); + + ILI_INFO("id = %d\n", id0.id); + ILI_INFO("app_sys_powr_state_e = %d\n", id0.sys_powr_state_e); + ILI_INFO("app_sys_state_e = %d\n", id0.sys_state_e); + ILI_INFO("tp_state_e = %d\n", id0.tp_state_e); + + ILI_INFO("touch_palm_state_e = %d\n", id0.touch_palm_state); + ILI_INFO("app_an_statu_e = %d\n", id0.app_an_statu_e); + ILI_INFO("app_sys_bg_err = %d\n", id0.app_sys_bg_err); + ILI_INFO("g_b_wrong_bg = %d\n", id0.g_b_wrong_bg); + ILI_INFO("reserved0 = %d\n", id0.reserved0); + + ILI_INFO("normal_mode = %d\n", id0.normal_mode); + ILI_INFO("charger_mode = %d\n", id0.charger_mode); + ILI_INFO("glove_mode = %d\n", id0.glove_mode); + ILI_INFO("stylus_mode = %d\n", id0.stylus_mode); + ILI_INFO("multi_mode = %d\n", id0.multi_mode); + ILI_INFO("noise_mode = %d\n", id0.noise_mode); + ILI_INFO("palm_plus_mode = %d\n", id0.palm_plus_mode); + ILI_INFO("floating_mode = %d\n", id0.floating_mode); + + ILI_INFO("algo_pt_status0 = %d\n", id0.algo_pt_status0); + ILI_INFO("algo_pt_status1 = %d\n", id0.algo_pt_status1); + ILI_INFO("algo_pt_status2 = %d\n", id0.algo_pt_status2); + ILI_INFO("algo_pt_status3 = %d\n", id0.algo_pt_status3); + ILI_INFO("algo_pt_status4 = %d\n", id0.algo_pt_status4); + ILI_INFO("algo_pt_status5 = %d\n", id0.algo_pt_status5); + ILI_INFO("algo_pt_status6 = %d\n", id0.algo_pt_status6); + ILI_INFO("algo_pt_status7 = %d\n", id0.algo_pt_status7); + ILI_INFO("algo_pt_status8 = %d\n", id0.algo_pt_status8); + ILI_INFO("algo_pt_status9 = %d\n", id0.algo_pt_status9); + ILI_INFO("reserved2 = %d\n", id0.reserved2); + + ILI_INFO("hopping_flg = %d\n", id0.hopping_flg); + ILI_INFO("hopping_index = %d\n", id0.hopping_index); + ILI_INFO("frequency = %d\n", (id0.frequency_h << 8 | id0.frequency_l)); + ILI_INFO("reserved3 = %d\n", id0.reserved3); + ILI_INFO("reserved4 = %d\n", id0.reserved4); + +} + +void ili_demo_debug_info_mode(u8 *buf, size_t len) +{ + u8 *info_ptr; + u8 info_id, info_len; + + ili_report_ap_mode(buf, P5_X_DEMO_MODE_PACKET_LEN); + info_ptr = buf + P5_X_DEMO_MODE_PACKET_LEN; + info_len = info_ptr[0]; + info_id = info_ptr[1]; + + ILI_INFO("info len = %d ,id = %d\n", info_len, info_id); + + ilits->demo_debug_info[info_id](&info_ptr[1] , info_len); +} + +static void ilitek_tddi_touch_send_debug_data(u8 *buf, int len) +{ + int index; + + mutex_lock(&ilits->debug_mutex); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + if (!ilits->netlink && !ilits->dnp) + goto out; + + /* Send data to netlink */ + if (ilits->netlink) { + ili_netlink_reply_msg(buf, len); + goto out; + } +#else + if (!ilits->dnp) + goto out; +#endif + + /* Sending data to apk via the node of debug_message node */ + if (ilits->dnp) { + index = ilits->dbf; + if (!ilits->dbl[ilits->dbf].mark) { + ilits->dbf = ((ilits->dbf + 1) % TR_BUF_LIST_SIZE); + } else { + if (ilits->dbf == 0) + index = TR_BUF_LIST_SIZE - 1; + else + index = ilits->dbf - 1; + } + if (ilits->dbl[index].data == NULL) { + ILI_ERR("BUFFER %d error\n", index); + goto out; + } + ipio_memcpy(ilits->dbl[index].data, buf, len, 2048); + ilits->dbl[index].mark = true; + wake_up(&(ilits->inq)); + goto out; + } + +out: + mutex_unlock(&ilits->debug_mutex); +} + +void ili_touch_press_width(u16 x, u16 y, u16 pressure, u16 id, u16 width_major, u16 width_minor, s8 degree) +{ + ILI_DBG("Touch Press: id = %d, x = %d, y = %d, p = %d, width_major = %d, width_minor = %d, degree = %d\n", + id, x, y, pressure, width_major, width_minor, degree); + + if (MT_B_TYPE) { + input_mt_slot(ilits->input, id); + input_mt_report_slot_state(ilits->input, MT_TOOL_FINGER, true); + input_report_abs(ilits->input, ABS_MT_POSITION_X, x); + input_report_abs(ilits->input, ABS_MT_POSITION_Y, y); + input_report_abs(ilits->input, ABS_MT_TOUCH_MAJOR, width_major); + input_report_abs(ilits->input, ABS_MT_TOUCH_MINOR, width_minor); + input_report_abs(ilits->input, ABS_MT_ORIENTATION, degree); + + if (MT_PRESSURE) + input_report_abs(ilits->input, ABS_MT_PRESSURE, pressure); + } else { + input_report_key(ilits->input, BTN_TOUCH, 1); + input_report_abs(ilits->input, ABS_MT_TRACKING_ID, id); + input_report_abs(ilits->input, ABS_MT_TOUCH_MAJOR, 1); + input_report_abs(ilits->input, ABS_MT_WIDTH_MAJOR, 1); + input_report_abs(ilits->input, ABS_MT_POSITION_X, x); + input_report_abs(ilits->input, ABS_MT_POSITION_Y, y); + if (MT_PRESSURE) + input_report_abs(ilits->input, ABS_MT_PRESSURE, pressure); + + input_mt_sync(ilits->input); + } +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS + { + struct dda_finger_coords finger_data; + finger_data.x = x; + finger_data.y = y; + finger_data.p = pressure; + finger_data.minor = width_minor; + finger_data.major = width_major; + moto_dda_process_finger_press(id, &finger_data); + } +#endif +} + +void ili_touch_press(u16 x, u16 y, u16 pressure, u16 id) +{ + ILI_DBG("Touch Press: id = %d, x = %d, y = %d, p = %d\n", id, x, y, pressure); + + if (MT_B_TYPE) { + input_mt_slot(ilits->input, id); + input_mt_report_slot_state(ilits->input, MT_TOOL_FINGER, true); + input_report_abs(ilits->input, ABS_MT_POSITION_X, x); + input_report_abs(ilits->input, ABS_MT_POSITION_Y, y); + if (MT_PRESSURE) + input_report_abs(ilits->input, ABS_MT_PRESSURE, pressure); + } else { + input_report_key(ilits->input, BTN_TOUCH, 1); + input_report_abs(ilits->input, ABS_MT_TRACKING_ID, id); + input_report_abs(ilits->input, ABS_MT_TOUCH_MAJOR, 1); + input_report_abs(ilits->input, ABS_MT_WIDTH_MAJOR, 1); + input_report_abs(ilits->input, ABS_MT_POSITION_X, x); + input_report_abs(ilits->input, ABS_MT_POSITION_Y, y); + if (MT_PRESSURE) + input_report_abs(ilits->input, ABS_MT_PRESSURE, pressure); + + input_mt_sync(ilits->input); + } +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS + { + struct dda_finger_coords finger_data; + finger_data.x = x; + finger_data.y = y; + finger_data.p = pressure; + finger_data.minor = 1; + finger_data.major = 1; + moto_dda_process_finger_press(id, &finger_data); + } +#endif +} + +void ili_touch_release(u16 x, u16 y, u16 id) +{ + ILI_DBG("Touch Release: id = %d, x = %d, y = %d\n", id, x, y); + + if (MT_B_TYPE) { + input_mt_slot(ilits->input, id); + input_mt_report_slot_state(ilits->input, MT_TOOL_FINGER, false); + } else { + input_report_key(ilits->input, BTN_TOUCH, 0); + input_mt_sync(ilits->input); + } +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS + moto_dda_process_finger_release(id); +#endif +} + +void ili_touch_release_all_point(void) +{ + int i; + + if (MT_B_TYPE) { + for (i = 0 ; i < MAX_TOUCH_NUM; i++) + ili_touch_release(0, 0, i); + + input_report_key(ilits->input, BTN_TOUCH, 0); + input_report_key(ilits->input, BTN_TOOL_FINGER, 0); + } else { + ili_touch_release(0, 0, 0); + } + input_sync(ilits->input); +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS + for (i = 0 ; i < MAX_TOUCH_NUM; i++){ + moto_dda_process_finger_release(i); + } +#endif +} + +static struct ilitek_touch_info touch_info[MAX_TOUCH_NUM]; +#ifdef ILI_TOUCH_LAST_TIME +static bool time_flag = 1; +#endif + +void ili_report_ap_mode(u8 *buf, int len) +{ + int i = 0; + u32 xop = 0, yop = 0; + int index = 0; + + if (TOUCH_WIDTH && ilits->tp_data_format == DATA_FORMAT_DEMO) { + index = 45; + } + + + memset(touch_info, 0x0, sizeof(touch_info)); + + ilits->finger = 0; + + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (TOUCH_WIDTH) { + if ((buf[(4 * i) + 1 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF) + && (buf[(4 * i) + 2 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF) + && (buf[(4 * i) + 3 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF)) { + if (MT_B_TYPE) + ilits->curt_touch[i] = 0; + continue; + } + + xop = (((buf[(4 * i) + 1 + P5_X_DEMO_MODE_PACKET_INFO_LEN] & 0xF0) << 4) | (buf[(4 * i) + 2 + P5_X_DEMO_MODE_PACKET_INFO_LEN])); + yop = (((buf[(4 * i) + 1 + P5_X_DEMO_MODE_PACKET_INFO_LEN] & 0x0F) << 8) | (buf[(4 * i) + 3 + P5_X_DEMO_MODE_PACKET_INFO_LEN])); + } else { + if ((buf[(4 * i) + 1] == 0xFF) && (buf[(4 * i) + 2] == 0xFF) + && (buf[(4 * i) + 3] == 0xFF)) { + if (MT_B_TYPE) + ilits->curt_touch[i] = 0; + continue; + } + + xop = (((buf[(4 * i) + 1] & 0xF0) << 4) | (buf[(4 * i) + 2])); + yop = (((buf[(4 * i) + 1] & 0x0F) << 8) | (buf[(4 * i) + 3])); + } + if (ilits->trans_xy) { + touch_info[ilits->finger].x = xop; + touch_info[ilits->finger].y = yop; + } else { + touch_info[ilits->finger].x = xop * ilits->panel_wid / TPD_WIDTH; + touch_info[ilits->finger].y = yop * ilits->panel_hei / TPD_HEIGHT; + } + + touch_info[ilits->finger].id = i; + + if (MT_PRESSURE) { + if (TOUCH_WIDTH) { + touch_info[ilits->finger].pressure =(buf[(4 * i) + 4 + P5_X_DEMO_MODE_PACKET_INFO_LEN])<<2; + } else { + touch_info[ilits->finger].pressure = buf[(4 * i) + 4]; + } + } else { + touch_info[ilits->finger].pressure = 1; + } + + if (TOUCH_WIDTH && ilits->tp_data_format == DATA_FORMAT_DEMO) { + touch_info[ilits->finger].degree = buf[(5 * i) + index]; + touch_info[ilits->finger].width_major = (((buf[(5 * i) + 1 + index]) << 8) | (buf[(5 * i) + 2 + index])); + touch_info[ilits->finger].width_minor = (((buf[(5 * i) + 3 + index]) << 8) | (buf[(5 * i) + 4 + index])); + } + + + ILI_DBG("original x = %d, y = %d\n", xop, yop); + ilits->finger++; + if (MT_B_TYPE) + ilits->curt_touch[i] = 1; + } + + ILI_DBG("figner number = %d, LastTouch = %d\n", ilits->finger, ilits->last_touch); + + if (ilits->finger) { +#ifdef ILI_TOUCH_LAST_TIME + if (time_flag) { + ilits->last_event_time = ktime_get_boottime(); + time_flag = 0; + ILI_DBG("set last_event_time\n"); + } +#endif + if (MT_B_TYPE) { + for (i = 0; i < ilits->finger; i++) { + input_report_key(ilits->input, BTN_TOUCH, 1); + if (TOUCH_WIDTH && ilits->tp_data_format == DATA_FORMAT_DEMO) { + ili_touch_press_width(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id, + touch_info[i].width_major, touch_info[i].width_minor, touch_info[i].degree); + } else { + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + } + input_report_key(ilits->input, BTN_TOOL_FINGER, 1); + } + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) + ili_touch_release(0, 0, i); + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + } else { + for (i = 0; i < ilits->finger; i++) + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + } + input_sync(ilits->input); + ilits->last_touch = ilits->finger; + } else { + if (ilits->last_touch) { +#ifdef ILI_TOUCH_LAST_TIME + time_flag = 1; + ILI_DBG("last touch, reset time_flag\n"); +#endif + if (MT_B_TYPE) { + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) + ili_touch_release(0, 0, i); + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + input_report_key(ilits->input, BTN_TOUCH, 0); + input_report_key(ilits->input, BTN_TOOL_FINGER, 0); + } else { + ili_touch_release(0, 0, 0); + } + input_sync(ilits->input); + ilits->last_touch = 0; + } + } + ilitek_tddi_touch_send_debug_data(buf, len); +} + +void ili_debug_mode_report_point(u8 *buf, int len) +{ + int i = 0; + u32 xop = 0, yop = 0; + static u8 p[MAX_TOUCH_NUM]; + int index = 0; + + if (TOUCH_WIDTH && ilits->tp_data_format == DATA_FORMAT_DEBUG) { + index = 35 + (2 * ilits->xch_num * ilits->ych_num) + (ilits->stx * 2) + (ilits->srx * 2) + (2 * 2) - 5; + } + + memset(touch_info, 0x0, sizeof(touch_info)); + + ilits->finger = 0; + + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if ((buf[(3 * i)] == 0xFF) && (buf[(3 * i) + 1] == 0xFF) + && (buf[(3 * i) + 2] == 0xFF)) { + if (MT_B_TYPE) + ilits->curt_touch[i] = 0; + continue; + } + + xop = (((buf[(3 * i)] & 0xF0) << 4) | (buf[(3 * i) + 1])); + yop = (((buf[(3 * i)] & 0x0F) << 8) | (buf[(3 * i) + 2])); + + if (ilits->trans_xy) { + touch_info[ilits->finger].x = xop; + touch_info[ilits->finger].y = yop; + } else { + touch_info[ilits->finger].x = xop * ilits->panel_wid / TPD_WIDTH; + touch_info[ilits->finger].y = yop * ilits->panel_hei / TPD_HEIGHT; + } + + touch_info[ilits->finger].id = i; + + if (MT_PRESSURE) { + /* + * Since there's no pressure data in debug mode, we make fake values + * for android system if pressure needs to be reported. + */ + if (p[ilits->finger] == 1) + touch_info[ilits->finger].pressure = p[ilits->finger] = 2; + else + touch_info[ilits->finger].pressure = p[ilits->finger] = 1; + } else { + touch_info[ilits->finger].pressure = 1; + } + + if (TOUCH_WIDTH && ilits->tp_data_format == DATA_FORMAT_DEBUG) { + touch_info[ilits->finger].degree = buf[(5 * i) + index]; + touch_info[ilits->finger].width_major = (((buf[(5 * i) + 1 + index]) << 8) | (buf[(5 * i) + 2 + index])); + touch_info[ilits->finger].width_minor = (((buf[(5 * i) + 3 + index]) << 8) | (buf[(5 * i) + 4 + index])); + } + + ILI_DBG("original x = %d, y = %d\n", xop, yop); + ilits->finger++; + if (MT_B_TYPE) + ilits->curt_touch[i] = 1; + } + + ILI_DBG("figner number = %d, LastTouch = %d\n", ilits->finger, ilits->last_touch); + + if (ilits->finger) { + if (MT_B_TYPE) { + for (i = 0; i < ilits->finger; i++) { + input_report_key(ilits->input, BTN_TOUCH, 1); + if (TOUCH_WIDTH && ilits->tp_data_format == DATA_FORMAT_DEBUG) { + ili_touch_press_width(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id, + touch_info[i].width_major, touch_info[i].width_minor, touch_info[i].degree); + } else { + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + } + input_report_key(ilits->input, BTN_TOOL_FINGER, 1); + } + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) + ili_touch_release(0, 0, i); + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + } else { + for (i = 0; i < ilits->finger; i++) + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + } + input_sync(ilits->input); + ilits->last_touch = ilits->finger; + } else { + if (ilits->last_touch) { + if (MT_B_TYPE) { + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) + ili_touch_release(0, 0, i); + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + input_report_key(ilits->input, BTN_TOUCH, 0); + input_report_key(ilits->input, BTN_TOOL_FINGER, 0); + } else { + ili_touch_release(0, 0, 0); + } + input_sync(ilits->input); + ilits->last_touch = 0; + } + } +} + +void ili_report_debug_mode(u8 *buf, int len) +{ + ili_debug_mode_report_point(buf + 5, len); + + ilitek_tddi_touch_send_debug_data(buf, len); +} + +void ili_report_debug_lite_mode(u8 *buf, int len) +{ + ili_debug_mode_report_point(buf + 4, len); + + ilitek_tddi_touch_send_debug_data(buf, len); +} + +void ili_report_gesture_mode(u8 *buf, int len) +{ + int lu_x = 0, lu_y = 0, rd_x = 0, rd_y = 0, score = 0; + u8 ges[P5_X_GESTURE_INFO_LENGTH] = {0}; + struct gesture_coordinate *gc = ilits->gcoord; +#if !defined(ILI_SENSOR_EN) && !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + struct input_dev *input = ilits->input; +#endif + bool transfer = ilits->trans_xy; +#ifdef ILI_SENSOR_EN + static int report_cnt = 0; +#endif + ipio_memcpy(ges, buf, len, P5_X_GESTURE_INFO_LENGTH); + + memset(gc, 0x0, sizeof(struct gesture_coordinate)); + + gc->code = ges[1]; + score = ges[36]; + ILI_INFO("gesture code = 0x%x, score = %d\n", gc->code, score); + + /* Parsing gesture coordinate */ + gc->pos_start.x = ((ges[4] & 0xF0) << 4) | ges[5]; + gc->pos_start.y = ((ges[4] & 0x0F) << 8) | ges[6]; + gc->pos_end.x = ((ges[7] & 0xF0) << 4) | ges[8]; + gc->pos_end.y = ((ges[7] & 0x0F) << 8) | ges[9]; + gc->pos_1st.x = ((ges[16] & 0xF0) << 4) | ges[17]; + gc->pos_1st.y = ((ges[16] & 0x0F) << 8) | ges[18]; + gc->pos_2nd.x = ((ges[19] & 0xF0) << 4) | ges[20]; + gc->pos_2nd.y = ((ges[19] & 0x0F) << 8) | ges[21]; + gc->pos_3rd.x = ((ges[22] & 0xF0) << 4) | ges[23]; + gc->pos_3rd.y = ((ges[22] & 0x0F) << 8) | ges[24]; + gc->pos_4th.x = ((ges[25] & 0xF0) << 4) | ges[26]; + gc->pos_4th.y = ((ges[25] & 0x0F) << 8) | ges[27]; + + switch (gc->code) { + case GESTURE_SINGLECLICK: + case GESTURE_DOUBLECLICK: + ILI_INFO("Click key event 0x%x\n", gc->code); +#ifdef ILI_SENSOR_EN + if (!(ilits->wakeable && ilits->should_enable_gesture)) { + ILI_INFO("Gesture got but wakeable not set. Skip this gesture."); + return; + } + if (ilits->report_gesture_key) { + input_report_key(ilits->sensor_pdata->input_sensor_dev, KEY_F1, 1); + input_sync(ilits->sensor_pdata->input_sensor_dev); + input_report_key(ilits->sensor_pdata->input_sensor_dev, KEY_F1, 0); + input_sync(ilits->sensor_pdata->input_sensor_dev); + ++report_cnt; + } else { + input_report_abs(ilits->sensor_pdata->input_sensor_dev, + ABS_DISTANCE, + ++report_cnt); + input_sync(ilits->sensor_pdata->input_sensor_dev); + } + ILI_INFO("input report: %d", report_cnt); + if (report_cnt >= REPORT_MAX_COUNT) { + report_cnt = 0; + } +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&(ilits->gesture_wakelock), msecs_to_jiffies(5000)); +#else + PM_WAKEUP_EVENT(ilits->gesture_wakelock, 5000); +#endif +#elif CONFIG_INPUT_TOUCHSCREEN_MMI + if (ilits->imports && ilits->imports->report_gesture) { + struct gesture_event_data event; + int ret = 0; + + if(gc->code == GESTURE_SINGLECLICK) { + event.evcode = 1; + } else if(gc->code == GESTURE_DOUBLECLICK) { + event.evcode =4; + } + /* call class method */ + ret = ilits->imports->report_gesture(&event); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&(ilits->gesture_wakelock), msecs_to_jiffies(5000)); +#else + PM_WAKEUP_EVENT(ilits->gesture_wakelock, 5000); +#endif + } +#else + input_report_key(input, KEY_GESTURE_POWER, 1); + input_sync(input); + input_report_key(input, KEY_GESTURE_POWER, 0); + input_sync(input); +#endif + gc->type = GESTURE_DOUBLECLICK; + gc->clockwise = 1; + gc->pos_end.x = gc->pos_start.x; + gc->pos_end.y = gc->pos_start.y; + break; + case GESTURE_LEFT: + gc->type = GESTURE_LEFT; + gc->clockwise = 1; + break; + case GESTURE_RIGHT: + gc->type = GESTURE_RIGHT; + gc->clockwise = 1; + break; + case GESTURE_UP: + gc->type = GESTURE_UP; + gc->clockwise = 1; + break; + case GESTURE_DOWN: + gc->type = GESTURE_DOWN; + gc->clockwise = 1; + break; + case GESTURE_O: + gc->type = GESTURE_O; + gc->clockwise = (ges[34] > 1) ? 0 : ges[34]; + + lu_x = (((ges[28] & 0xF0) << 4) | (ges[29])); + lu_y = (((ges[28] & 0x0F) << 8) | (ges[30])); + rd_x = (((ges[31] & 0xF0) << 4) | (ges[32])); + rd_y = (((ges[31] & 0x0F) << 8) | (ges[33])); + + gc->pos_1st.x = ((rd_x + lu_x) / 2); + gc->pos_1st.y = lu_y; + gc->pos_2nd.x = lu_x; + gc->pos_2nd.y = ((rd_y + lu_y) / 2); + gc->pos_3rd.x = ((rd_x + lu_x) / 2); + gc->pos_3rd.y = rd_y; + gc->pos_4th.x = rd_x; + gc->pos_4th.y = ((rd_y + lu_y) / 2); + break; + case GESTURE_W: + gc->type = GESTURE_W; + gc->clockwise = 1; + break; + case GESTURE_M: + gc->type = GESTURE_M; + gc->clockwise = 1; + break; + case GESTURE_V: + gc->type = GESTURE_V; + gc->clockwise = 1; + break; + case GESTURE_C: + gc->type = GESTURE_C; + gc->clockwise = 1; + break; + case GESTURE_E: + gc->type = GESTURE_E; + gc->clockwise = 1; + break; + case GESTURE_S: + gc->type = GESTURE_S; + gc->clockwise = 1; + break; + case GESTURE_Z: + gc->type = GESTURE_Z; + gc->clockwise = 1; + break; + case GESTURE_TWOLINE_DOWN: + gc->type = GESTURE_TWOLINE_DOWN; + gc->clockwise = 1; + gc->pos_1st.x = (((ges[10] & 0xF0) << 4) | (ges[11])); + gc->pos_1st.y = (((ges[10] & 0x0F) << 8) | (ges[12])); + gc->pos_2nd.x = (((ges[13] & 0xF0) << 4) | (ges[14])); + gc->pos_2nd.y = (((ges[13] & 0x0F) << 8) | (ges[15])); + break; + default: + ILI_ERR("Unknown gesture code\n"); + break; + } + + if (!transfer) { + gc->pos_start.x = gc->pos_start.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_start.y = gc->pos_start.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_end.x = gc->pos_end.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_end.y = gc->pos_end.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_1st.x = gc->pos_1st.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_1st.y = gc->pos_1st.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_2nd.x = gc->pos_2nd.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_2nd.y = gc->pos_2nd.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_3rd.x = gc->pos_3rd.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_3rd.y = gc->pos_3rd.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_4th.x = gc->pos_4th.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_4th.y = gc->pos_4th.y * ilits->panel_hei / TPD_HEIGHT; + } + + ILI_INFO("Transfer = %d, Type = %d, clockwise = %d\n", transfer, gc->type, gc->clockwise); + ILI_INFO("Gesture Points: (%d, %d)(%d, %d)(%d, %d)(%d, %d)(%d, %d)(%d, %d)\n", + gc->pos_start.x, gc->pos_start.y, + gc->pos_end.x, gc->pos_end.y, + gc->pos_1st.x, gc->pos_1st.y, + gc->pos_2nd.x, gc->pos_2nd.y, + gc->pos_3rd.x, gc->pos_3rd.y, + gc->pos_4th.x, gc->pos_4th.y); + + ilitek_tddi_touch_send_debug_data(buf, len); +} + +void ili_report_i2cuart_mode(u8 *buf, int len) +{ + int type = buf[3] & 0x0F; + int need_read_len = 0, one_data_bytes = 0; + int actual_len = len - 5; + int uart_len; + u8 *uart_buf = NULL, *total_buf = NULL; + + ILI_DBG("data[3] = %x, type = %x, actual_len = %d\n", + buf[3], type, actual_len); + + need_read_len = buf[1] * buf[2]; + + if (type == 0 || type == 1 || type == 6) { + one_data_bytes = 1; + } else if (type == 2 || type == 3) { + one_data_bytes = 2; + } else if (type == 4 || type == 5) { + one_data_bytes = 4; + } + + need_read_len = need_read_len * one_data_bytes + 1; + ILI_DBG("need_read_len = %d one_data_bytes = %d\n", need_read_len, one_data_bytes); + + if (need_read_len <= actual_len) { + ilitek_tddi_touch_send_debug_data(buf, len); + goto out; + } + + uart_len = need_read_len - actual_len; + ILI_DBG("uart len = %d\n", uart_len); + + uart_buf = kcalloc(uart_len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(uart_buf)) { + ILI_ERR("Failed to allocate uart_buf memory %ld\n", PTR_ERR(uart_buf)); + goto out; + } + + if (ilits->wrapper(NULL, 0, uart_buf, uart_len, OFF, OFF) < 0) { + ILI_ERR("i2cuart read data failed\n"); + goto out; + } + + total_buf = kcalloc(len + uart_len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(total_buf)) { + ILI_ERR("Failed to allocate total_buf memory %ld\n", PTR_ERR(total_buf)); + goto out; + } + + memcpy(total_buf, buf, len); + memcpy(total_buf + len, uart_buf, uart_len); + ilitek_tddi_touch_send_debug_data(total_buf, len + uart_len); + +out: + ipio_kfree((void **)&uart_buf); + ipio_kfree((void **)&total_buf); + return; +} diff --git a/drivers/input/touchscreen/ili9882_mmi/ili_mmi_class.c b/drivers/input/touchscreen/ili9882_mmi/ili_mmi_class.c new file mode 100644 index 000000000000..9288941996f5 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/ili_mmi_class.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include +#include "ili9882.h" + +static int ili_mmi_methods_get_vendor(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), 16, "%s", "ilitek"); +} + +static int ili_mmi_methods_get_productinfo(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), 16, "%s", "6666"); +} + +static int ili_mmi_methods_get_build_id(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), 16, "%04x", 0x8888); +} + +/* Read fw_version, date_Y, date_M, date_D */ +static int ili_mmi_methods_get_config_id(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), 16, "%02d%02d%02d%02x", + 22, 9, 8, 1); +} + +static int ili_mmi_methods_get_bus_type(struct device *dev, void *idata) +{ + return 0; +} + +static int ili_mmi_methods_get_irq_status(struct device *dev, void *idata) +{ + return 0; +} + +static int ili_mmi_methods_get_drv_irq(struct device *dev, void *idata) +{ + return 0; +} + +static int ili_mmi_methods_get_poweron(struct device *dev, void *idata) +{ + return 0; +} + +static int ili_mmi_methods_get_flashprog(struct device *dev, void *idata) +{ + return 0; +} + +static int ili_mmi_methods_drv_irq(struct device *dev, int state) +{ + return 0; +} + +static int ili_mmi_charger_mode(struct device *dev, int mode) +{ + int ret = 0; + mutex_lock(&ilits->touch_mutex); + ret = ili_ic_func_ctrl("plug", !mode); + if(ret<0) { + ILI_ERR("Write plug in failed\n"); + } + ILI_INFO("%s: %d\n", __func__, !mode); + mutex_unlock(&ilits->touch_mutex); + + return 0; +} + +#define MAX_ATTRS_ENTRIES 10 +#define ADD_ATTR(name) { \ + if (idx < MAX_ATTRS_ENTRIES) { \ + dev_info(dev, "%s: [%d] adding %p\n", __func__, idx, &dev_attr_##name.attr); \ + ext_attributes[idx] = &dev_attr_##name.attr; \ + idx++; \ + } else { \ + dev_err(dev, "%s: cannot add attribute '%s'\n", __func__, #name); \ + } \ +} + +static struct attribute *ext_attributes[MAX_ATTRS_ENTRIES]; +static struct attribute_group ext_attr_group = { + .attrs = ext_attributes, +}; + +static ssize_t ili_palm_settings_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d", ilits->canvas_value); +} + +static ssize_t ili_palm_settings_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) + +{ + int value; + int err = 0; + + mutex_lock(&ilits->touch_mutex); + if (count > 2) + return -EINVAL; + + if (sscanf(buf, "%d", &value) != 1) + return -EINVAL; + + err = count; + + if (ili_ic_func_ctrl("canvas", value) < 0) + ILI_ERR("Write canvas enable fail\n"); + else + ilits->canvas_value = value; + ILI_INFO("%s: %d\n", __func__, ilits->canvas_value); + mutex_unlock(&ilits->touch_mutex); + + return err; +} + +#ifdef ILI_TOUCH_LAST_TIME +static ssize_t ili_mmi_timestamp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ktime_t last_ktime; + struct timespec64 last_ts; + + mutex_lock(&ilits->touch_mutex); + last_ktime = ilits->last_event_time; + ilits->last_event_time = 0; + mutex_unlock(&ilits->touch_mutex); + + last_ts = ktime_to_timespec64(last_ktime); + + return scnprintf(buf, PAGE_SIZE, "%lld.%ld\n", last_ts.tv_sec, last_ts.tv_nsec); +} +static DEVICE_ATTR(timestamp, S_IRUGO, ili_mmi_timestamp_show, NULL); +#endif + +static DEVICE_ATTR(palm_settings, S_IRUGO | S_IWUSR | S_IWGRP, ili_palm_settings_show, ili_palm_settings_store); + +static int ili_mmi_extend_attribute_group(struct device *dev, struct attribute_group **group) +{ + int idx = 0; + + ADD_ATTR(palm_settings); +#ifdef ILI_TOUCH_LAST_TIME + ADD_ATTR(timestamp); +#endif + + if (idx) { + ext_attributes[idx] = NULL; + *group = &ext_attr_group; + } else + *group = NULL; + + return 0; +} + + +static int ili_mmi_methods_reset(struct device *dev, int type) +{ + return 0; +} + +static int ili_mmi_firmware_update(struct device *dev, char *fwname) +{ + return 0; +} + +static int ili_mmi_panel_state(struct device *dev, + enum ts_mmi_pm_mode from, enum ts_mmi_pm_mode to) +{ + struct ilitek_ts_data __maybe_unused *ts_data; +#if defined(CONFIG_BOARD_USES_DOUBLE_TAP_CTRL) + unsigned char gesture_type = 0; +#endif + GET_TS_DATA(dev); + + ILI_INFO("%s: panel state change: %d->%d\n", __func__, from, to); + ILI_INFO("panel state change: %d->%d\n", from, to); + switch (to) { + case TS_MMI_PM_GESTURE: +#ifdef ILI_SET_TOUCH_STATE + ilits->should_enable_gesture = true; + touch_set_state(TS_MMI_PM_GESTURE, TOUCH_PANEL_IDX_PRIMARY); +#else + ilits->should_enable_gesture = false; +#endif + +#if defined(CONFIG_BOARD_USES_DOUBLE_TAP_CTRL) + if (ts_data->imports && ts_data->imports->get_gesture_type) { + ts_data->imports->get_gesture_type(ts_data->dev, &gesture_type); + } + + if (gesture_type & TS_MMI_GESTURE_SINGLE) { + ilits->ges_sym.single_tap = SINGLE_TAP; + ILI_INFO("enable single gesture mode\n"); + } + + if (gesture_type & TS_MMI_GESTURE_DOUBLE) { + ilits->ges_sym.double_tap = DOUBLE_TAP; + ILI_INFO("enable double gesture mode\n"); + } + + /*---write command to enter "wakeup gesture mode"---*/ + ili_sleep_handler(TP_SUSPEND); + ilits->ges_sym.single_tap = OFF; + ilits->ges_sym.double_tap = OFF; +#endif + break; + + case TS_MMI_PM_DEEPSLEEP: + ilits->should_enable_gesture = false; +#ifdef ILI_SET_TOUCH_STATE + touch_set_state(TS_MMI_PM_DEEPSLEEP, TOUCH_PANEL_IDX_PRIMARY); +#endif + ili_sleep_handler(TP_DEEP_SLEEP); + break; + + case TS_MMI_PM_ACTIVE: + break; + default: + ILI_INFO("panel mode %d is invalid.\n", to); + return -EINVAL; + } + return 0; +} + +int32_t ili_mmi_post_suspend(struct device *dev) +{ + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen driver resume function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int ili_mmi_pre_resume(struct device *dev) +{ + return 0; +} + +static int ili_mmi_post_resume(struct device *dev) +{ + ili_sleep_handler(TP_RESUME); + return 0; +} + +static struct ts_mmi_methods ili_mmi_methods = { + .get_vendor = ili_mmi_methods_get_vendor, + .get_productinfo = ili_mmi_methods_get_productinfo, + .get_build_id = ili_mmi_methods_get_build_id, + .get_config_id = ili_mmi_methods_get_config_id, + .get_bus_type = ili_mmi_methods_get_bus_type, + .get_irq_status = ili_mmi_methods_get_irq_status, + .get_drv_irq = ili_mmi_methods_get_drv_irq, + .get_flashprog = ili_mmi_methods_get_flashprog, + .get_poweron = ili_mmi_methods_get_poweron, + /* SET methods */ + .reset = ili_mmi_methods_reset, + .drv_irq = ili_mmi_methods_drv_irq, + .charger_mode = ili_mmi_charger_mode, + /* Firmware */ + .firmware_update = ili_mmi_firmware_update, + .extend_attribute_group = ili_mmi_extend_attribute_group, + /* PM callback */ + .panel_state = ili_mmi_panel_state, + .pre_resume = ili_mmi_pre_resume, + .post_resume = ili_mmi_post_resume, + .post_suspend = ili_mmi_post_suspend, + +}; + +int ili_mmi_init(struct ilitek_ts_data *ts_data, bool enable) { + int ret = 0; + + if (enable) { + ret = ts_mmi_dev_register(&ts_data->spi->dev, &ili_mmi_methods); + if (ret) + ILI_INFO("Failed to register ts mmi\n"); + /* initialize class imported methods */ + ts_data->imports = &ili_mmi_methods.exports; + } else + ts_mmi_dev_unregister(&ts_data->spi->dev); + + return ret; +} diff --git a/drivers/input/touchscreen/ili9882_mmi/moto_ts_dda.c b/drivers/input/touchscreen/ili9882_mmi/moto_ts_dda.c new file mode 100644 index 000000000000..e11a44bf3eb2 --- /dev/null +++ b/drivers/input/touchscreen/ili9882_mmi/moto_ts_dda.c @@ -0,0 +1,582 @@ +#include +#include +#include +#include +#include +#include "moto_ts_dda.h" + +//cdev registration +#define DRIVER_NAME "moto_dda_penraw_driver" +#define DEVICE_NAME "moto_penraw" +#define MINOR_NUMBER_START ((const unsigned int)0) /* the minor number starts at */ +#define NUMBER_MINOR_NUMBER ((const unsigned int)1) /* the number of minor numbers */ +#define DDA_MAX_BUFFER 32 +#define MAX_IO_CONTROL_REPORT 16 +#define DDA_TOUCH_ID_MAX 10 +#define DDA_DEVICE_NAME_MAXLEN 32 + +static bool debug_log_flag = false; +#define DDA_INFO(fmt, args...) pr_info("[MOTO_DDA_INFO] %s %d: " fmt, __func__, __LINE__, ##args) +#define DDA_ERR(fmt, args...) pr_err("[MOTO_DDA_ERR] %s %d: " fmt, __func__, __LINE__, ##args) +#define DDA_DBG(fmt, args...) {if(debug_log_flag)pr_debug("[MOTO_DDA_DBG] %s %d: " fmt, __func__, __LINE__, ##args);} + + +struct dda_io_device_name { + char device_name[DDA_DEVICE_NAME_MAXLEN]; +}; + +#ifdef MOTO_DDA_PASSIVE_STYLUS +enum { + DDA_FINGER_RELEASE = 0, + DDA_FINGER_ENTER = 1, + DDA_FINGER_MOVE = 2 +}; + +struct dda_finger_info { + unsigned char frame_no; + unsigned char reserve[3]; + struct dda_finger_coords coords; +}; + +struct dda_io_finger_reports { + unsigned char report_num; + unsigned char reserve[3]; + struct dda_finger_info finger_info[MAX_IO_CONTROL_REPORT]; +}; + +struct dda_finger_report_buffer{ + unsigned char finger_report_num; + unsigned char frame_no; + unsigned char buffer_wp; + unsigned char reserve[1]; + unsigned char last_status; + struct dda_finger_info finger_report_buffer[DDA_MAX_BUFFER]; +}; +#endif //MOTO_DDA_PASSIVE_STYLUS + + +#ifdef MOTO_DDA_ACTIVE_STYLUS +/* for DDA goodix_penraw */ +enum{ + DATA_TYPE_RAW = 0 +}; + +struct dda_pen_info { + unsigned char frame_no; + unsigned char data_type; + unsigned char reserve[2]; + struct dda_pen_coords coords; +}; + +struct dda_io_pen_reports { + unsigned char report_num; + unsigned char reserve[3]; + struct dda_pen_info pen_info[MAX_IO_CONTROL_REPORT]; +}; + +struct dda_pen_report_buffer{ + unsigned char pen_report_num; + unsigned char frame_no; + unsigned char buffer_wp; + unsigned char reserve[1]; + struct dda_pen_info pen_report_buffer[DDA_MAX_BUFFER]; +}; +#endif //MOTO_DDA_ACTIVE_STYLUS + +struct dda_cdev_control{ + unsigned int major_number; /* the major number of the device */ + struct cdev penraw_char_dev; /* character device */ + struct class* penraw_char_dev_class; /* class object */ +}; + + +//DDA cdev +static struct dda_cdev_control dda_cdev_ctrl; + +#ifdef MOTO_DDA_PASSIVE_STYLUS +//DDA raw finger report buffer +static struct dda_finger_report_buffer dda_finger_report_buf[DDA_TOUCH_ID_MAX]; +static int dda_finger_id_assign_table[DDA_TOUCH_ID_MAX]; +static int dda_finger_id_last_assign_table[DDA_TOUCH_ID_MAX]; +#endif //MOTO_DDA_PASSIVE_STYLUS + +#ifdef MOTO_DDA_ACTIVE_STYLUS +//DDA raw pen report buffer +static struct dda_pen_report_buffer dda_pen_report_buf; +#endif //MOTO_DDA_ACTIVE_STYLUS + +static spinlock_t lock; +static int open_count = 0; + +static char dda_device_name[DDA_DEVICE_NAME_MAXLEN]; + + +//prototype of private functions +#ifdef MOTO_DDA_PASSIVE_STYLUS +static int get_finger_report(int id, unsigned long arg); +#endif //MOTO_DDA_PASSIVE_STYLUS +#ifdef MOTO_DDA_ACTIVE_STYLUS +static int get_pen_report(unsigned long arg); +#endif //MOTO_DDA_ACTIVE_STYLUS + +static int get_device_name(unsigned long arg); + +// Linux 2.0/2.2 +static int penraw_open(struct inode * inode, struct file * file) +{ + spin_lock(&lock); + if (open_count) { + spin_unlock(&lock); + return -EBUSY; + } + open_count++; + spin_unlock(&lock); + + return 0; +} + +// Linux 2.1: int type +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 1, 0) +static int penraw_close(struct inode * inode, struct file * file) +{ + spin_lock(&lock); + open_count--; + spin_unlock(&lock); + + return 0; +} +#else +static void penraw_close(struct inode * inode, struct file * file) +{ + spinlock(&lock); + open_count--; + spin_unlock(&lock); +} +#endif + +#define PENRAW_IOC_TYPE 'P' +#ifdef MOTO_DDA_ACTIVE_STYLUS +#define PENRAW_GET_VALUES _IOR(PENRAW_IOC_TYPE, 0, struct dda_io_pen_reports) + +static struct dda_io_pen_reports pen_reports; // return report +#endif //MOTO_DDA_ACTIVE_STYLUS + +#ifdef MOTO_DDA_PASSIVE_STYLUS +#define PENRAW_GET_VALUES_ID0 _IOR(PENRAW_IOC_TYPE, 1, struct dda_io_finger_reports) +#define PENRAW_GET_VALUES_ID1 _IOR(PENRAW_IOC_TYPE, 2, struct dda_io_finger_reports) +#define PENRAW_GET_VALUES_ID2 _IOR(PENRAW_IOC_TYPE, 3, struct dda_io_finger_reports) +#define PENRAW_GET_VALUES_ID3 _IOR(PENRAW_IOC_TYPE, 4, struct dda_io_finger_reports) +#define PENRAW_GET_VALUES_ID4 _IOR(PENRAW_IOC_TYPE, 5, struct dda_io_finger_reports) +#define PENRAW_GET_VALUES_ID5 _IOR(PENRAW_IOC_TYPE, 6, struct dda_io_finger_reports) +#define PENRAW_GET_VALUES_ID6 _IOR(PENRAW_IOC_TYPE, 7, struct dda_io_finger_reports) +#define PENRAW_GET_VALUES_ID7 _IOR(PENRAW_IOC_TYPE, 8, struct dda_io_finger_reports) +#define PENRAW_GET_VALUES_ID8 _IOR(PENRAW_IOC_TYPE, 9, struct dda_io_finger_reports) +#define PENRAW_GET_VALUES_ID9 _IOR(PENRAW_IOC_TYPE, 10, struct dda_io_finger_reports) + +static struct dda_io_finger_reports finger_reports; // return report +#endif //MOTO_DDA_PASSIVE_STYLUS + +#define PENRAW_GET_DEVICE_NAME _IOR(PENRAW_IOC_TYPE, 11, struct dda_io_device_name) + +static long penraw_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret; + + switch (cmd) { +#ifdef MOTO_DDA_ACTIVE_STYLUS + case PENRAW_GET_VALUES: + ret = get_pen_report(arg); + break; +#endif +#ifdef MOTO_DDA_PASSIVE_STYLUS + case PENRAW_GET_VALUES_ID0: + ret = get_finger_report(0,arg); + break; + case PENRAW_GET_VALUES_ID1: + ret = get_finger_report(1,arg); + break; + case PENRAW_GET_VALUES_ID2: + ret = get_finger_report(2,arg); + break; + case PENRAW_GET_VALUES_ID3: + ret = get_finger_report(3,arg); + break; + case PENRAW_GET_VALUES_ID4: + ret = get_finger_report(4,arg); + break; + case PENRAW_GET_VALUES_ID5: + ret = get_finger_report(5,arg); + break; + case PENRAW_GET_VALUES_ID6: + ret = get_finger_report(6,arg); + break; + case PENRAW_GET_VALUES_ID7: + ret = get_finger_report(7,arg); + break; + case PENRAW_GET_VALUES_ID8: + ret = get_finger_report(8,arg); + break; + case PENRAW_GET_VALUES_ID9: + ret = get_finger_report(9,arg); + break; +#endif //MOTO_DDA_PASSIVE_STYLUS + case PENRAW_GET_DEVICE_NAME: + ret = get_device_name(arg); + break; + default: + DDA_ERR("unsupported command %d", cmd); + return -EFAULT; + } + return ret; +} + +#ifdef MOTO_DDA_PASSIVE_STYLUS +static int get_finger_report(int id, unsigned long arg){ + unsigned long flags; + struct dda_finger_info *pfinger_info; + unsigned char cnt; + unsigned char finger_buffer_rp; + unsigned char wp; + unsigned char num; + int touch_id; + //enter to critical session + local_irq_save(flags); + touch_id = dda_finger_id_assign_table[id]; + if(-1 == touch_id){ + touch_id = dda_finger_id_last_assign_table[id]; + if(-1 == touch_id){ + DDA_ERR("PassiveStylusDDA touch_id is not assignd for %d",id); + touch_id = id; + } + } + + wp = dda_finger_report_buf[touch_id].buffer_wp; + num = dda_finger_report_buf[touch_id].finger_report_num; + if(MAX_IO_CONTROL_REPORT <= num) { + finger_buffer_rp = (unsigned char)((wp + (DDA_MAX_BUFFER - + MAX_IO_CONTROL_REPORT)) % DDA_MAX_BUFFER); + } else { + num = MAX_IO_CONTROL_REPORT; //for safety implementation + finger_buffer_rp = 0; + } + memset(&finger_reports, 0, sizeof(finger_reports)); + finger_reports.report_num = num; + pfinger_info = (struct dda_finger_info *)&finger_reports.finger_info[0]; + for(cnt = 0; cnt < num; cnt++) { + memcpy(pfinger_info, &dda_finger_report_buf[touch_id].finger_report_buffer[finger_buffer_rp], + sizeof(struct dda_finger_info)); + pfinger_info++; + finger_buffer_rp++; + if(DDA_MAX_BUFFER == finger_buffer_rp) { + finger_buffer_rp = 0; + } + } + //leave from critical session + local_irq_restore(flags); + + + if (copy_to_user((void __user *)arg, &finger_reports, sizeof(finger_reports))) { + return -EFAULT; + } + return 0; +} +#endif //MOTO_DDA_PASSIVE_STYLUS + +#ifdef MOTO_DDA_ACTIVE_STYLUS +static int get_pen_report(unsigned long arg){ + unsigned long flags; + struct dda_pen_info *ppen_info; + unsigned char cnt; + unsigned char pen_buffer_rp; + unsigned char wp; + unsigned char num; + + local_irq_save(flags); + wp = dda_pen_report_buf.buffer_wp; + num = dda_pen_report_buf.pen_report_num; + local_irq_restore(flags); + if(MAX_IO_CONTROL_REPORT <= num) { + pen_buffer_rp = (unsigned char)((wp + (DDA_MAX_BUFFER - + MAX_IO_CONTROL_REPORT)) % DDA_MAX_BUFFER); + } else { + pen_buffer_rp = 0; + } + memset(&pen_reports, 0, sizeof(pen_reports)); + pen_reports.report_num = num; + ppen_info = (struct dda_pen_info *)&pen_reports.pen_info[0]; + for(cnt = 0; cnt < num; cnt++) { + memcpy(ppen_info, &dda_pen_report_buf.pen_report_buffer[pen_buffer_rp], + sizeof(struct dda_pen_info)); + ppen_info++; + pen_buffer_rp++; + if(DDA_MAX_BUFFER == pen_buffer_rp) { + pen_buffer_rp = 0; + } + } + if (copy_to_user((void __user *)arg, &pen_reports, sizeof(pen_reports))) { + return -EFAULT; + } + return 0; +} +#endif //MOTO_DDA_ACTIVE_STYLUS + +static int get_device_name(unsigned long arg){ + if (copy_to_user((void __user *)arg, &dda_device_name, sizeof(dda_device_name))) { + return -EFAULT; + } + return 0; +} + +static struct file_operations penraw_fops = { + .owner = THIS_MODULE, + .open = penraw_open, + .release = penraw_close, + .unlocked_ioctl = penraw_ioctl, +}; + +/* functions for DDA Features */ +#ifdef MOTO_DDA_PASSIVE_STYLUS +void moto_dda_process_finger_press(uint8_t touch_id, struct dda_finger_coords *finger_data) { + struct dda_finger_info *pfinger_info; + int i; + unsigned char id_is_found; + + if(touch_id >= DDA_TOUCH_ID_MAX){ + return; + } + + // ioctl-DAA Buffering finger raw data + pfinger_info = &dda_finger_report_buf[touch_id].finger_report_buffer[0]; + pfinger_info += dda_finger_report_buf[touch_id].buffer_wp; + memset(pfinger_info, 0, sizeof(struct dda_finger_info)); + pfinger_info->coords.x = finger_data->x; + pfinger_info->coords.y = finger_data->y; + pfinger_info->coords.p = finger_data->p; + pfinger_info->coords.minor = finger_data->minor; + pfinger_info->coords.major = finger_data->major; + pfinger_info->frame_no = dda_finger_report_buf[touch_id].frame_no; + DDA_DBG("P:[%d] %d %d %d (%d)\n",touch_id, finger_data->x, finger_data->y, finger_data->p, dda_finger_report_buf[touch_id].frame_no); + dda_finger_report_buf[touch_id].frame_no++; + if(DDA_FINGER_RELEASE == dda_finger_report_buf[touch_id].last_status){ + pfinger_info->coords.status = DDA_FINGER_ENTER; + //check registerd iDs to avoid duplicated registration. + id_is_found = 0; + for(i=0;icoords.status = DDA_FINGER_MOVE; + } + dda_finger_report_buf[touch_id].last_status = pfinger_info->coords.status; + if(MAX_IO_CONTROL_REPORT > dda_finger_report_buf[touch_id].finger_report_num) { + // Max count: MAX_IO_CONTROL_REPORT + dda_finger_report_buf[touch_id].finger_report_num++; + } + dda_finger_report_buf[touch_id].buffer_wp++; + if(DDA_MAX_BUFFER == dda_finger_report_buf[touch_id].buffer_wp) { + dda_finger_report_buf[touch_id].buffer_wp = 0; + } +} + +void moto_dda_process_finger_release(uint8_t touch_id) { + struct dda_finger_info *pfinger_info; + int i; + + if(touch_id >= DDA_TOUCH_ID_MAX){ + return; + } + + // ioctl-DAA Buffering finger raw data + pfinger_info = &dda_finger_report_buf[touch_id].finger_report_buffer[0]; + pfinger_info += dda_finger_report_buf[touch_id].buffer_wp; + memset(pfinger_info, 0, sizeof(struct dda_finger_info)); + pfinger_info->coords.x = 0xFFFFFFFF; + pfinger_info->coords.y = 0xFFFFFFFF; + pfinger_info->coords.p = 0; + pfinger_info->coords.minor = 0; + pfinger_info->coords.major = 0; + pfinger_info->frame_no = dda_finger_report_buf[touch_id].frame_no; + DDA_DBG("R:[%d] (%d)\n",touch_id, dda_finger_report_buf[touch_id].frame_no); + dda_finger_report_buf[touch_id].frame_no++; + pfinger_info->coords.status = DDA_FINGER_RELEASE; + dda_finger_report_buf[touch_id].last_status = pfinger_info->coords.status; + for(i=0;i dda_finger_report_buf[touch_id].finger_report_num) { + // Max count: MAX_IO_CONTROL_REPORT + dda_finger_report_buf[touch_id].finger_report_num++; + } + dda_finger_report_buf[touch_id].buffer_wp++; + if(DDA_MAX_BUFFER == dda_finger_report_buf[touch_id].buffer_wp) { + dda_finger_report_buf[touch_id].buffer_wp = 0; + } + +} +#endif //MOTO_DDA_PASSIVE_STYLUS + +#ifdef MOTO_DDA_ACTIVE_STYLUS +void moto_dda_process_pen_report(struct dda_pen_coords *pen_data) { + struct dda_pen_info *ppen_info; + + if (pen_data->status) { + // ioctl-DAA Buffering pen raw data + ppen_info = &dda_pen_report_buf.pen_report_buffer[0]; + ppen_info += dda_pen_report_buf.buffer_wp; + memset(ppen_info, 0, sizeof(struct dda_pen_info)); + ppen_info->coords.status = (signed char)pen_data->status; + ppen_info->coords.tool_type = (signed char)pen_data->tool_type; + ppen_info->coords.tilt_x = pen_data->tilt_x; + ppen_info->coords.tilt_y = pen_data->tilt_y; + ppen_info->coords.x = pen_data->x; + ppen_info->coords.y = pen_data->y; + ppen_info->coords.p = pen_data->p; + ppen_info->frame_no = dda_pen_report_buf.frame_no; + ppen_info->data_type = DATA_TYPE_RAW; + DDA_DBG("S:[%d] %d %d %d (%d)\n", pen_data->status, (int)pen_data->x, (int)pen_data->y, (int)pen_data->p, dda_pen_report_buf.frame_no); + dda_pen_report_buf.frame_no++; + if(MAX_IO_CONTROL_REPORT > dda_pen_report_buf.pen_report_num) { + // Max count: MAX_IO_CONTROL_REPORT + dda_pen_report_buf.pen_report_num++; + } + dda_pen_report_buf.buffer_wp++; + if(DDA_MAX_BUFFER == dda_pen_report_buf.buffer_wp) { + dda_pen_report_buf.buffer_wp = 0; + } + } +} +#endif //MOTO_DDA_ACTIVE_STYLUS + + +void moto_dda_init(char *device_name) { +#ifdef MOTO_DDA_PASSIVE_STYLUS + int cnt; +#endif //MOTO_DDA_PASSIVE_STYLUS + + memset(dda_device_name,0x00,sizeof(dda_device_name)); + strncpy(dda_device_name,device_name,sizeof(dda_device_name)-1); + +#ifdef MOTO_DDA_PASSIVE_STYLUS + for(cnt =0;cnt + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ILI9881X_FW_H +#define __ILI9881X_FW_H + +/* define names and paths for the variety of tp modules */ +#define DEF_INI_NAME_PATH "/sdcard/mp.ini" +#define DEF_FW_FILP_PATH "/sdcard/ILITEK_FW" +#define DEF_INI_REQUEST_PATH "mp.ini" +#define DEF_FW_REQUEST_PATH "ilitek_fw.bin" +static unsigned char CTPM_FW_DEF[] = { +}; + +#define CSOT_INI_NAME_PATH "/sdcard/mp_csot.ini" +#define CSOT_FW_FILP_PATH "/sdcard/ILITEK_FW_CSOT" +#define CSOT_INI_REQUEST_PATH "mp_csot.ini" +#define CSOT_FW_REQUEST_PATH "ILITEK_FW_CSOT" +static unsigned char CTPM_FW_CSOT[] = { + 0xFF, +}; + +#define AUO_INI_NAME_PATH "/sdcard/mp_auo.ini" +#define AUO_FW_FILP_PATH "/sdcard/ILITEK_FW_AUO" +#define AUO_INI_REQUEST_PATH "mp_auo.ini" +#define AUO_FW_REQUEST_PATH "ILITEK_FW_AUO" +static unsigned char CTPM_FW_AUO[] = { + 0xFF, +}; + +#define BOE_INI_NAME_PATH "/sdcard/mp_boe.ini" +#define BOE_FW_FILP_PATH "/sdcard/ILITEK_FW_BOE" +#define BOE_INI_REQUEST_PATH "mp_boe.ini" +#define BOE_FW_REQUEST_PATH "ILITEK_FW_BOE" +static unsigned char CTPM_FW_BOE[] = { + 0xFF, +}; + +#define INX_INI_NAME_PATH "/sdcard/mp_inx.ini" +#define INX_FW_FILP_PATH "/sdcard/ILITEK_FW_INX" +#define INX_INI_REQUEST_PATH "mp_inx.ini" +#define INX_FW_REQUEST_PATH "ILITEK_FW_INX" +static unsigned char CTPM_FW_INX[] = { + 0xFF, +}; + +#define DJ_INI_NAME_PATH "/sdcard/mp_dj.ini" +#define DJ_FW_FILP_PATH "/sdcard/ILITEK_FW_DJ" +#define DJ_INI_REQUEST_PATH "mp_dj.ini" +#define DJ_FW_REQUEST_PATH "ILITEK_FW_DJ" +static unsigned char CTPM_FW_DJ[] = { + 0xFF, +}; + +#define TXD_INI_NAME_PATH "/sdcard/mp_txd.ini" +#define TXD_FW_FILP_PATH "/sdcard/ILITEK_FW_TXD" +#define TXD_INI_REQUEST_PATH "mp_txd.ini" +#define TXD_FW_REQUEST_PATH "ILITEK_FW_TXD" +static unsigned char CTPM_FW_TXD[] = { + 0xFF, +}; + +#define TM_INI_NAME_PATH "/sdcard/mp_tm.ini" +#define TM_FW_FILP_PATH "/sdcard/ILITEK_FW_TM" +#define TM_INI_REQUEST_PATH "mp_tm.ini" +#define TM_FW_REQUEST_PATH "ILITEK_FW_TM" +static unsigned char CTPM_FW_TM[] = { + 0xFF, +}; + +#endif diff --git a/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek.c b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek.c new file mode 100644 index 000000000000..82856f6f1196 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek.c @@ -0,0 +1,1090 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "firmware/ilitek_fw.h" +#include "ilitek.h" + +/* Debug level */ +bool debug_en = DEBUG_OUTPUT; +EXPORT_SYMBOL(debug_en); + +static struct workqueue_struct *esd_wq; +static struct workqueue_struct *bat_wq; +static struct delayed_work esd_work; +static struct delayed_work bat_work; + +#if RESUME_BY_DDI +static struct workqueue_struct *resume_by_ddi_wq; +static struct work_struct resume_by_ddi_work; + +static void ilitek_resume_by_ddi_work(struct work_struct *work) +{ + mutex_lock(&ilits->touch_mutex); + + if (ilits->gesture) + disable_irq_wake(ilits->irq_num); + + /* Set tp as demo mode and reload code if it's iram. */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + if (ilits->fw_upgrade_mode == UPGRADE_IRAM) + ili_fw_upgrade_handler(NULL); + else + ili_reset_ctrl(ilits->reset); + + ili_irq_enable(); + ILI_INFO("TP resume end by wq\n"); + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + ilits->tp_suspend = false; + mutex_unlock(&ilits->touch_mutex); +} + +void ili_resume_by_ddi(void) +{ + if (!resume_by_ddi_wq) { + ILI_INFO("resume_by_ddi_wq is null\n"); + return; + } + + mutex_lock(&ilits->touch_mutex); + + ILI_INFO("TP resume start called by ddi\n"); + + /* + * To match the timing of sleep out, the first of mipi cmd must be sent within 10ms + * after TP reset. We then create a wq doing host download before resume. + */ + atomic_set(&ilits->fw_stat, ENABLE); + ili_reset_ctrl(ilits->reset); + ili_ice_mode_ctrl(ENABLE, OFF); + ilits->ddi_rest_done = true; + mdelay(5); + queue_work(resume_by_ddi_wq, &(resume_by_ddi_work)); + + mutex_unlock(&ilits->touch_mutex); +} +#endif + +int ili_mp_test_handler(char *apk, bool lcm_on) +{ + int ret = 0; + + if (atomic_read(&ilits->fw_stat)) { + ILI_ERR("fw upgrade processing, ignore\n"); + return -EMP_FW_PROC; + } + + atomic_set(&ilits->mp_stat, ENABLE); + + if (ilits->actual_tp_mode != P5_X_FW_TEST_MODE) { + ret = ili_switch_tp_mode(P5_X_FW_TEST_MODE); + if (ret < 0) { + ILI_ERR("Switch MP mode failed\n"); + ret = -EMP_MODE; + goto out; + } + } + + ret = ili_mp_test_main(apk, lcm_on); + +out: + /* + * If there's running mp test with lcm off, we suspose that + * users will soon call resume from suspend. TP mode will be changed + * from MP to AP mode until resume finished. + */ + if (!lcm_on) { + atomic_set(&ilits->mp_stat, DISABLE); + return ret; + } + + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + if (ilits->fw_upgrade_mode == UPGRADE_IRAM) { + if (ili_fw_upgrade_handler(NULL) < 0) + ILI_ERR("FW upgrade failed during mp test\n"); + } else { + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP Reset failed during mp test\n"); + } + + atomic_set(&ilits->mp_stat, DISABLE); + return ret; +} + +int ili_switch_tp_mode(u8 mode) +{ + int ret = 0; + bool ges_dbg = false; + + atomic_set(&ilits->tp_sw_mode, START); + + ilits->actual_tp_mode = mode; + + /* able to see cdc data in gesture mode */ + if (ilits->tp_data_format == DATA_FORMAT_DEBUG && + ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) + ges_dbg = true; + + switch (ilits->actual_tp_mode) { + case P5_X_FW_AP_MODE: + ILI_INFO("Switch to AP mode\n"); + ilits->wait_int_timeout = AP_INT_TIMEOUT; + if (ilits->fw_upgrade_mode == UPGRADE_IRAM) { + if (ili_fw_upgrade_handler(NULL) < 0) + ILI_ERR("FW upgrade failed\n"); + } else { + ret = ili_reset_ctrl(ilits->reset); + } + if (ret < 0) + ILI_ERR("TP Reset failed\n"); + + break; + case P5_X_FW_GESTURE_MODE: + ILI_INFO("Switch to Gesture mode\n"); + ilits->wait_int_timeout = AP_INT_TIMEOUT; + ret = ilits->gesture_move_code(ilits->gesture_mode); + if (ret < 0) + ILI_ERR("Move gesture code failed\n"); + if (ges_dbg) { + ILI_INFO("Enable gesture debug func\n"); + ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEBUG, false, NULL); + } + break; + case P5_X_FW_TEST_MODE: + ILI_INFO("Switch to Test mode\n"); + ilits->wait_int_timeout = MP_INT_TIMEOUT; + ret = ilits->mp_move_code(); + break; + default: + ILI_ERR("Unknown TP mode: %x\n", mode); + ret = -1; + break; + } + + if (ret < 0) + ILI_ERR("Switch TP mode (%d) failed \n", mode); + + ILI_DBG("Actual TP mode = %d\n", ilits->actual_tp_mode); + atomic_set(&ilits->tp_sw_mode, END); + return ret; +} + +int ili_gesture_recovery(void) +{ + int ret = 0; + bool lock = mutex_is_locked(&ilits->touch_mutex); + + // atomic_set(&ilits->esd_stat, START); + + if (!lock) + mutex_lock(&ilits->touch_mutex); + + ILI_INFO("Doing gesture recovery\n"); + ret = ilits->ges_recover(); + + if (!lock) + mutex_unlock(&ilits->touch_mutex); + + // atomic_set(&ilits->esd_stat, END); + return ret; +} + +void ili_spi_recovery(void) +{ + atomic_set(&ilits->esd_stat, START); + + ILI_INFO("Doing spi recovery\n"); + if (ili_fw_upgrade_handler(NULL) < 0) + ILI_ERR("FW upgrade failed\n"); + + atomic_set(&ilits->esd_stat, END); +} + +int ili_wq_esd_spi_check(void) +{ + int ret = 0; + u8 tx = SPI_WRITE, rx = 0; + + ret = ilits->spi_write_then_read(ilits->spi, &tx, 1, &rx, 1); + ILI_DBG("spi esd check = 0x%x\n", ret); + if (ret == DO_SPI_RECOVER) { + ILI_ERR("ret = 0x%x\n", ret); + return -1; + } + return 0; +} + +int ili_wq_esd_i2c_check(void) +{ + ILI_DBG(""); + return 0; +} + +static void ilitek_tddi_wq_esd_check(struct work_struct *work) +{ + mutex_lock(&ilits->touch_mutex); + if (ilits->esd_recover() < 0) { + ILI_ERR("SPI ACK failed, doing spi recovery\n"); + ili_spi_recovery(); + } + mutex_unlock(&ilits->touch_mutex); + complete_all(&ilits->esd_done); + ili_wq_ctrl(WQ_ESD, ENABLE); +} + +static int read_power_status(u8 *buf) +{ + struct file *f = NULL; + mm_segment_t old_fs; + ssize_t byte = 0; + + old_fs = get_fs(); + set_fs(get_ds()); + + f = filp_open(POWER_STATUS_PATH, O_RDONLY, 0); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open %s\n", POWER_STATUS_PATH); + return -1; + } + + f->f_op->llseek(f, 0, SEEK_SET); + byte = f->f_op->read(f, buf, 20, &f->f_pos); + + ILI_DBG("Read %d bytes\n", (int)byte); + + set_fs(old_fs); + filp_close(f, NULL); + return 0; +} + +static void ilitek_tddi_wq_bat_check(struct work_struct *work) +{ + u8 str[20] = {0}; + static int charge_mode; + + if (read_power_status(str) < 0) + ILI_ERR("Read power status failed\n"); + + ILI_DBG("Batter Status: %s\n", str); + + if (strstr(str, "Charging") != NULL || strstr(str, "Full") != NULL + || strstr(str, "Fully charged") != NULL) { + if (charge_mode != 1) { + ILI_DBG("Charging mode\n"); + if (ili_ic_func_ctrl("plug", DISABLE) < 0) // plug in + ILI_ERR("Write plug in failed\n"); + charge_mode = 1; + } + } else { + if (charge_mode != 2) { + ILI_DBG("Not charging mode\n"); + if (ili_ic_func_ctrl("plug", ENABLE) < 0) // plug out + ILI_ERR("Write plug out failed\n"); + charge_mode = 2; + } + } + ili_wq_ctrl(WQ_BAT, ENABLE); +} + +void ili_wq_ctrl(int type, int ctrl) +{ + switch (type) { + case WQ_ESD: + if (ENABLE_WQ_ESD || ilits->wq_ctrl) { + if (!esd_wq) { + ILI_ERR("wq esd is null\n"); + break; + } + ilits->wq_esd_ctrl = ctrl; + if (ctrl == ENABLE) { + ILI_DBG("execute esd check\n"); + if (!queue_delayed_work(esd_wq, &esd_work, msecs_to_jiffies(WQ_ESD_DELAY))) + ILI_DBG("esd check was already on queue\n"); + } else { + cancel_delayed_work_sync(&esd_work); + flush_workqueue(esd_wq); + ILI_DBG("cancel esd wq\n"); + } + } + break; + case WQ_BAT: + if (ENABLE_WQ_BAT || ilits->wq_ctrl) { + if (!bat_wq) { + ILI_ERR("WQ BAT is null\n"); + break; + } + ilits->wq_bat_ctrl = ctrl; + if (ctrl == ENABLE) { + ILI_DBG("execute bat check\n"); + if (!queue_delayed_work(bat_wq, &bat_work, msecs_to_jiffies(WQ_BAT_DELAY))) + ILI_DBG("bat check was already on queue\n"); + } else { + cancel_delayed_work_sync(&bat_work); + flush_workqueue(bat_wq); + ILI_DBG("cancel bat wq\n"); + } + } + break; + default: + ILI_ERR("Unknown WQ type, %d\n", type); + break; + } +} + +static void ilitek_tddi_wq_init(void) +{ + esd_wq = alloc_workqueue("esd_check", WQ_MEM_RECLAIM, 0); + bat_wq = alloc_workqueue("bat_check", WQ_MEM_RECLAIM, 0); + + WARN_ON(!esd_wq); + WARN_ON(!bat_wq); + + INIT_DELAYED_WORK(&esd_work, ilitek_tddi_wq_esd_check); + INIT_DELAYED_WORK(&bat_work, ilitek_tddi_wq_bat_check); + +#if RESUME_BY_DDI + resume_by_ddi_wq = create_singlethread_workqueue("resume_by_ddi_wq"); + WARN_ON(!resume_by_ddi_wq); + INIT_WORK(&resume_by_ddi_work, ilitek_resume_by_ddi_work); +#endif +} + +int ili_sleep_handler(int mode) +{ + int ret = 0; + bool sense_stop = true; + + mutex_lock(&ilits->touch_mutex); + atomic_set(&ilits->tp_sleep, START); + + if (atomic_read(&ilits->fw_stat) || + atomic_read(&ilits->mp_stat)) { + ILI_INFO("fw upgrade or mp still running, ignore sleep requst\n"); + atomic_set(&ilits->tp_sleep, END); + mutex_unlock(&ilits->touch_mutex); + return 0; + } + + ili_wq_ctrl(WQ_ESD, DISABLE); + ili_wq_ctrl(WQ_BAT, DISABLE); + ili_irq_disable(); + + ILI_INFO("Sleep Mode = %d\n", mode); + + if (ilits->ss_ctrl) + sense_stop = true; + else if ((ilits->chip->core_ver >= CORE_VER_1430)) + sense_stop = false; + else + sense_stop = true; + + switch (mode) { + case TP_SUSPEND: + ILI_INFO("TP suspend start\n"); + if (sense_stop) { + if (ili_ic_func_ctrl("sense", DISABLE) < 0) + ILI_ERR("Write sense stop cmd failed\n"); + + if (ili_ic_check_busy(50, 20) < 0) + ILI_ERR("Check busy timeout during suspend\n"); + } + +#ifdef ILI_SENSOR_EN + if (ilits->should_enable_gesture) { + ili_switch_tp_mode(P5_X_FW_GESTURE_MODE); + enable_irq_wake(ilits->irq_num); + ili_irq_enable(); + ilits->wakeable = true; + } else { + if (ili_ic_func_ctrl("sleep", DEEP_SLEEP_IN) < 0) + ILI_ERR("Write sleep in cmd failed\n"); + ilits->wakeable = false; + } +#else + if (ilits->gesture) { + ili_switch_tp_mode(P5_X_FW_GESTURE_MODE); + enable_irq_wake(ilits->irq_num); + ili_irq_enable(); + } else { + if (ili_ic_func_ctrl("sleep", DEEP_SLEEP_IN) < 0) + ILI_ERR("Write sleep in cmd failed\n"); + } +#endif + ILI_INFO("TP suspend end\n"); + ilits->tp_suspend = true; + break; + case TP_DEEP_SLEEP: + ILI_INFO("TP deep suspend start\n"); + if (sense_stop) { + if (ili_ic_func_ctrl("sense", DISABLE) < 0) + ILI_ERR("Write sense stop cmd failed\n"); + + if (ili_ic_check_busy(50, 20) < 0) + ILI_ERR("Check busy timeout during deep suspend\n"); + } + + if (ilits->gesture) { + ili_switch_tp_mode(P5_X_FW_GESTURE_MODE); + enable_irq_wake(ilits->irq_num); + ili_irq_enable(); + } else { + if (ili_ic_func_ctrl("sleep", DEEP_SLEEP_IN) < 0) + ILI_ERR("Write deep sleep in cmd failed\n"); + } + ILI_INFO("TP deep suspend end\n"); + ilits->tp_suspend = true; + break; + case TP_RESUME: +#if !RESUME_BY_DDI + ILI_INFO("TP resume start\n"); + + if (ilits->gesture) + disable_irq_wake(ilits->irq_num); + + /* Set tp as demo mode and reload code if it's iram. */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + if (ilits->fw_upgrade_mode == UPGRADE_IRAM) { + if (ili_fw_upgrade_handler(NULL) < 0) + ILI_ERR("FW upgrade failed during resume\n"); + } else { + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP Reset failed during resume\n"); + } + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + ilits->tp_suspend = false; +#ifdef ILI_SENSOR_EN + if (ilits->wakeable) { + disable_irq_wake(ilits->irq_num); + ilits->gesture_enabled = false; + ilits->wakeable = false; + } +#endif + ILI_INFO("TP resume end\n"); +#endif + ili_irq_enable(); + break; + default: + ILI_ERR("Unknown sleep mode, %d\n", mode); + ret = -EINVAL; + break; + } + + ili_touch_release_all_point(); + atomic_set(&ilits->tp_sleep, END); + mutex_unlock(&ilits->touch_mutex); + return ret; +} + +int ili_fw_upgrade_handler(void *data) +{ + int ret = 0; + + atomic_set(&ilits->fw_stat, START); + + ilits->fw_update_stat = FW_STAT_INIT; + ret = ili_fw_upgrade(ilits->fw_open); + if (ret != 0) { + ILI_INFO("FW upgrade fail\n"); + ilits->fw_update_stat = FW_UPDATE_FAIL; + } else { + ILI_INFO("FW upgrade pass\n"); + ilits->fw_update_stat = FW_UPDATE_PASS; + } + + if (!ilits->boot) { + ilits->boot = true; + ILI_INFO("Registre touch to input subsystem\n"); + ili_input_register(); + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + } + + atomic_set(&ilits->fw_stat, END); + if(ilits->charger_detection_enable && ilits->boot && (ilits->charger_notify_wq != NULL)) + queue_work(ilits->charger_notify_wq, &ilits->charger_notify_work); + return ret; +} + +int ili_set_tp_data_len(int format, bool send, u8* data) +{ + u8 cmd[10] = {0}, ctrl = 0, debug_ctrl = 0; + u16 self_key = 2; + int ret = 0, tp_mode = ilits->actual_tp_mode, len = 0; + + switch (format) { + case DATA_FORMAT_DEMO: + case DATA_FORMAT_GESTURE_DEMO: + len = P5_X_DEMO_MODE_PACKET_LEN; + ctrl = DATA_FORMAT_DEMO_CMD; + break; + case DATA_FORMAT_DEBUG: + case DATA_FORMAT_GESTURE_DEBUG: + len = (2 * ilits->xch_num * ilits->ych_num) + (ilits->stx * 2) + (ilits->srx * 2); + len += 2 * self_key + (8 * 2) + 1 + 35; + ctrl = DATA_FORMAT_DEBUG_CMD; + break; + case DATA_FORMAT_DEMO_DEBUG_INFO: + /*only suport SPI interface now, so defult use size 1024 buffer*/ + len = P5_X_DEMO_MODE_PACKET_LEN + + P5_X_DEMO_DEBUG_INFO_ID0_LENGTH + P5_X_INFO_HEADER_LENGTH; + ctrl = DATA_FORMAT_DEMO_DEBUG_INFO_CMD; + break; + case DATA_FORMAT_GESTURE_INFO: + len = P5_X_GESTURE_INFO_LENGTH; + ctrl = DATA_FORMAT_GESTURE_INFO_CMD; + break; + case DATA_FORMAT_GESTURE_NORMAL: + len = P5_X_GESTURE_NORMAL_LENGTH; + ctrl = DATA_FORMAT_GESTURE_NORMAL_CMD; + break; + case DATA_FORMAT_GESTURE_SPECIAL_DEMO: + if (ilits->gesture_demo_ctrl == ENABLE) { + if (ilits->gesture_mode == DATA_FORMAT_GESTURE_INFO) + len = P5_X_GESTURE_INFO_LENGTH + P5_X_INFO_HEADER_LENGTH + P5_X_INFO_CHECKSUM_LENGTH; + else + len = P5_X_DEMO_MODE_PACKET_LEN + P5_X_INFO_HEADER_LENGTH + P5_X_INFO_CHECKSUM_LENGTH; + } else { + if (ilits->gesture_mode == DATA_FORMAT_GESTURE_INFO) + len = P5_X_GESTURE_INFO_LENGTH; + else + len = P5_X_GESTURE_NORMAL_LENGTH; + } + ILI_INFO("Gesture demo mode control = %d\n", ilits->gesture_demo_ctrl); + ili_ic_func_ctrl("gesture_demo_en", ilits->gesture_demo_ctrl); + ILI_INFO("knock_en setting\n"); + ili_ic_func_ctrl("knock_en", 0x8); + break; + case DATA_FORMAT_DEBUG_LITE_ROI: + debug_ctrl = DATA_FORMAT_DEBUG_LITE_ROI_CMD; + ctrl = DATA_FORMAT_DEBUG_LITE_CMD; + break; + case DATA_FORMAT_DEBUG_LITE_WINDOW: + debug_ctrl = DATA_FORMAT_DEBUG_LITE_WINDOW_CMD; + ctrl = DATA_FORMAT_DEBUG_LITE_CMD; + break; + case DATA_FORMAT_DEBUG_LITE_AREA: + /* Compiler complains that this comparison is always false. + if(cmd == NULL) { + ILI_ERR("DATA_FORMAT_DEBUG_LITE_AREA error cmd\n"); + return -1; + }*/ + debug_ctrl = DATA_FORMAT_DEBUG_LITE_AREA_CMD; + ctrl = DATA_FORMAT_DEBUG_LITE_CMD; + cmd[3] = data[0]; + cmd[4] = data[1]; + cmd[5] = data[2]; + cmd[6] = data[3]; + break; + default: + ILI_ERR("Unknow TP data format\n"); + return -1; + } + + if (ctrl == DATA_FORMAT_DEBUG_LITE_CMD) { + len = P5_X_DEBUG_LITE_LENGTH; + cmd[0] = P5_X_MODE_CONTROL; + cmd[1] = ctrl; + cmd[2] = debug_ctrl; + ret = ilits->wrapper(cmd, 10, NULL, 0, ON, OFF); + + if (ret < 0) { + ILI_ERR("switch to format %d failed\n", format); + ili_switch_tp_mode(P5_X_FW_AP_MODE); + } + + } else if (tp_mode == P5_X_FW_AP_MODE || + format == DATA_FORMAT_GESTURE_DEMO || + format == DATA_FORMAT_GESTURE_DEBUG) { + cmd[0] = P5_X_MODE_CONTROL; + cmd[1] = ctrl; + ret = ilits->wrapper(cmd, 2, NULL, 0, ON, OFF); + + if (ret < 0) { + ILI_ERR("switch to format %d failed\n", format); + ili_switch_tp_mode(P5_X_FW_AP_MODE); + } + } else if (tp_mode == P5_X_FW_GESTURE_MODE) { + + /*set gesture symbol*/ + ili_set_gesture_symbol(); + + if (send) { + ret = ili_ic_func_ctrl("lpwg", ctrl); + if (ret < 0) + ILI_ERR("write gesture mode failed\n"); + } + } + + ilits->tp_data_format = format; + ilits->tp_data_len = len; + ILI_INFO("TP mode = %d, format = %d, len = %d\n", + tp_mode, ilits->tp_data_format, ilits->tp_data_len); + + return ret; +} + +int ili_report_handler(void) +{ + int ret = 0, pid = 0; + u8 checksum = 0, pack_checksum = 0; + u8 *trdata = NULL; + int rlen = 0; + int tmp = debug_en; + + /* Just in case these stats couldn't be blocked in top half context */ + if (!ilits->report || atomic_read(&ilits->tp_reset) || + atomic_read(&ilits->fw_stat) || atomic_read(&ilits->tp_sw_mode) || + atomic_read(&ilits->mp_stat) || atomic_read(&ilits->tp_sleep)) { + ILI_INFO("ignore report request\n"); + return -EINVAL; + } + + if (ilits->irq_after_recovery) { + ILI_INFO("ignore int triggered by recovery\n"); + ilits->irq_after_recovery = false; + return -EINVAL; + } + + ili_wq_ctrl(WQ_ESD, DISABLE); + ili_wq_ctrl(WQ_BAT, DISABLE); + + if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) { + PM_STAY_AWAKE(ilits->ws); + + if (ilits->pm_suspend) { + /* Waiting for pm resume completed */ + ret = wait_for_completion_timeout(&ilits->pm_completion, msecs_to_jiffies(700)); + if (!ret) { + ILI_ERR("system(spi) can't finished resuming procedure."); + } + } + } + + rlen = ilits->tp_data_len; + ILI_DBG("Packget length = %d\n", rlen); + + if (!rlen || rlen > TR_BUF_SIZE) { + ILI_ERR("Length of packet is invaild\n"); + goto out; + } + + memset(ilits->tr_buf, 0x0, TR_BUF_SIZE); + + ret = ilits->wrapper(NULL, 0, ilits->tr_buf, rlen, OFF, OFF); + if (ret < 0) { + ILI_ERR("Read report packet failed, ret = %d\n", ret); + if (ret == DO_SPI_RECOVER) { + ili_ic_get_pc_counter(DO_SPI_RECOVER); + if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE && ilits->gesture && !ilits->prox_near) { + ILI_ERR("Gesture failed, doing gesture recovery\n"); + if (ili_gesture_recovery() < 0) + ILI_ERR("Failed to recover gesture\n"); + ilits->irq_after_recovery = true; + } else { + ILI_ERR("SPI ACK failed, doing spi recovery\n"); + ili_spi_recovery(); + ilits->irq_after_recovery = true; + } + } + goto out; + } + + ili_dump_data(ilits->tr_buf, 8, rlen, 0, "finger report"); + + checksum = ili_calc_packet_checksum(ilits->tr_buf, rlen - 1); + pack_checksum = ilits->tr_buf[rlen-1]; + trdata = ilits->tr_buf; + pid = trdata[0]; + ILI_DBG("Packet ID = %x\n", pid); + + if (checksum != pack_checksum && pid != P5_X_I2CUART_PACKET_ID) { + ILI_ERR("Checksum Error (0x%X)! Pack = 0x%X, len = %d\n", checksum, pack_checksum, rlen); + debug_en = DEBUG_ALL; + ili_dump_data(trdata, 8, rlen, 0, "finger report with wrong"); + debug_en = tmp; + ret = -EINVAL; + goto out; + } + + if (pid == P5_X_INFO_HEADER_PACKET_ID) { + trdata = ilits->tr_buf + P5_X_INFO_HEADER_LENGTH; + pid = trdata[0]; + } + + switch (pid) { + case P5_X_DEMO_PACKET_ID: + ili_report_ap_mode(trdata, rlen); + break; + case P5_X_DEBUG_PACKET_ID: + ili_report_debug_mode(trdata, rlen); + break; + case P5_X_DEBUG_LITE_PACKET_ID: + ili_report_debug_lite_mode(trdata, rlen); + break; + case P5_X_I2CUART_PACKET_ID: + ili_report_i2cuart_mode(trdata, rlen); + break; + case P5_X_GESTURE_PACKET_ID: + ili_report_gesture_mode(trdata, rlen); + break; + case P5_X_GESTURE_FAIL_ID: + ILI_INFO("gesture fail reason code = 0x%02x", trdata[1]); + break; + case P5_X_DEMO_DEBUG_INFO_PACKET_ID: + ili_demo_debug_info_mode(trdata, rlen); + break; + default: + ILI_ERR("Unknown packet id, %x\n", pid); + break; + } + +out: + if (ilits->actual_tp_mode != P5_X_FW_GESTURE_MODE) { + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + } + + if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) + PM_RELAX(ilits->ws); + return ret; +} + +int ili_reset_ctrl(int mode) +{ + int ret = 0; + + atomic_set(&ilits->tp_reset, START); + + switch (mode) { + case TP_IC_CODE_RST: + ILI_INFO("TP IC Code RST \n"); + ret = ili_ic_code_reset(); + if (ret < 0) + ILI_ERR("IC Code reset failed\n"); + break; + case TP_IC_WHOLE_RST: + ILI_INFO("TP IC whole RST\n"); + ret = ili_ic_whole_reset(); + if (ret < 0) + ILI_ERR("IC whole reset failed\n"); + break; + case TP_HW_RST_ONLY: + ILI_INFO("TP HW RST\n"); + ili_tp_reset(); + break; + default: + ILI_ERR("Unknown reset mode, %d\n", mode); + ret = -EINVAL; + break; + } + + /* + * Since OTP must be folloing with reset, except for code rest, + * the stat of ice mode should be set as 0. + */ + if (mode != TP_IC_CODE_RST) + atomic_set(&ilits->ice_stat, DISABLE); + + ilits->tp_data_format = DATA_FORMAT_DEMO; + ilits->tp_data_len = P5_X_DEMO_MODE_PACKET_LEN; + ilits->pll_clk_wakeup = true; + atomic_set(&ilits->tp_reset, END); + return ret; +} + +static int ilitek_get_tp_module(void) +{ + /* + * TODO: users should implement this function + * if there are various tp modules been used in projects. + */ + + return 0; +} + +static void ili_update_tp_module_info(void) +{ + int module; + + module = ilitek_get_tp_module(); + + switch (module) { + case MODEL_CSOT: + ilits->md_name = "CSOT"; + ilits->md_fw_filp_path = CSOT_FW_FILP_PATH; + ilits->md_fw_rq_path = CSOT_FW_REQUEST_PATH; + ilits->md_ini_path = CSOT_INI_NAME_PATH; + ilits->md_ini_rq_path = CSOT_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_CSOT; + ilits->md_fw_ili_size = sizeof(CTPM_FW_CSOT); + break; + case MODEL_AUO: + ilits->md_name = "AUO"; + ilits->md_fw_filp_path = AUO_FW_FILP_PATH; + ilits->md_fw_rq_path = AUO_FW_REQUEST_PATH; + ilits->md_ini_path = AUO_INI_NAME_PATH; + ilits->md_ini_rq_path = AUO_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_AUO; + ilits->md_fw_ili_size = sizeof(CTPM_FW_AUO); + break; + case MODEL_BOE: + ilits->md_name = "BOE"; + ilits->md_fw_filp_path = BOE_FW_FILP_PATH; + ilits->md_fw_rq_path = BOE_FW_REQUEST_PATH; + ilits->md_ini_path = BOE_INI_NAME_PATH; + ilits->md_ini_rq_path = BOE_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_BOE; + ilits->md_fw_ili_size = sizeof(CTPM_FW_BOE); + break; + case MODEL_INX: + ilits->md_name = "INX"; + ilits->md_fw_filp_path = INX_FW_FILP_PATH; + ilits->md_fw_rq_path = INX_FW_REQUEST_PATH; + ilits->md_ini_path = INX_INI_NAME_PATH; + ilits->md_ini_rq_path = INX_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_INX; + ilits->md_fw_ili_size = sizeof(CTPM_FW_INX); + break; + case MODEL_DJ: + ilits->md_name = "DJ"; + ilits->md_fw_filp_path = DJ_FW_FILP_PATH; + ilits->md_fw_rq_path = DJ_FW_REQUEST_PATH; + ilits->md_ini_path = DJ_INI_NAME_PATH; + ilits->md_ini_rq_path = DJ_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_DJ; + ilits->md_fw_ili_size = sizeof(CTPM_FW_DJ); + break; + case MODEL_TXD: + ilits->md_name = "TXD"; + ilits->md_fw_filp_path = TXD_FW_FILP_PATH; + ilits->md_fw_rq_path = TXD_FW_REQUEST_PATH; + ilits->md_ini_path = TXD_INI_NAME_PATH; + ilits->md_ini_rq_path = TXD_FW_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_TXD; + ilits->md_fw_ili_size = sizeof(CTPM_FW_TXD); + break; + case MODEL_TM: + ilits->md_name = "TM"; + ilits->md_fw_filp_path = TM_FW_REQUEST_PATH; + ilits->md_fw_rq_path = TM_FW_REQUEST_PATH; + ilits->md_ini_path = TM_INI_NAME_PATH; + ilits->md_ini_rq_path = TM_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_TM; + ilits->md_fw_ili_size = sizeof(CTPM_FW_TM); + break; + default: + break; + } + + if (module == 0 || ilits->md_fw_ili_size < ILI_FILE_HEADER) { + ILI_ERR("Couldn't find any tp modules, applying default settings\n"); + ilits->md_name = "DEF"; + ilits->md_fw_filp_path = DEF_FW_FILP_PATH; + ilits->md_fw_rq_path = DEF_FW_REQUEST_PATH; + ilits->md_ini_path = DEF_INI_NAME_PATH; + ilits->md_ini_rq_path = DEF_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_DEF; + ilits->md_fw_ili_size = sizeof(CTPM_FW_DEF); + } + + ILI_INFO("Found %s module: ini path = %s, fw path = (%s, %s, %d)\n", + ilits->md_name, + ilits->md_ini_path, + ilits->md_fw_filp_path, + ilits->md_fw_rq_path, + ilits->md_fw_ili_size); + + ilits->tp_module = module; +} + +int ili_tddi_init(void) +{ +#if BOOT_FW_UPDATE + struct task_struct *fw_boot_th; +#endif + + ILI_INFO("driver version = %s\n", DRIVER_VERSION); + + mutex_init(&ilits->touch_mutex); + mutex_init(&ilits->debug_mutex); + mutex_init(&ilits->debug_read_mutex); + init_waitqueue_head(&(ilits->inq)); + spin_lock_init(&ilits->irq_spin); + init_completion(&ilits->esd_done); + + atomic_set(&ilits->irq_stat, DISABLE); + atomic_set(&ilits->ice_stat, DISABLE); + atomic_set(&ilits->tp_reset, END); + atomic_set(&ilits->fw_stat, END); + atomic_set(&ilits->mp_stat, DISABLE); + atomic_set(&ilits->tp_sleep, END); + atomic_set(&ilits->cmd_int_check, DISABLE); + atomic_set(&ilits->esd_stat, END); + + ili_ic_init(); + ilitek_tddi_wq_init(); + + /* Must do hw reset once in first time for work normally if tp reset is avaliable */ +#if !TDDI_RST_BIND + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP Reset failed during init\n"); +#endif + + ilits->demo_debug_info[0] = ili_demo_debug_info_id0; + ilits->tp_data_format = DATA_FORMAT_DEMO; + ilits->boot = false; + + /* + * This status of ice enable will be reset until process of fw upgrade runs. + * it might cause unknown problems if we disable ice mode without any + * codes inside touch ic. + */ + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Failed to enable ice mode during ili_tddi_init\n"); + + if ( ili_ic_dummy_check() < 0 ){ + ILI_ERR("Not found ilitek chip\n"); + return -ENODEV; + } + + if (ili_ic_get_info() < 0) + ILI_ERR("Chip info is incorrect\n"); + + ili_update_tp_module_info(); + + ili_node_init(); + + ili_fw_read_flash_info(); + +#if BOOT_FW_UPDATE + fw_boot_th = kthread_run(ili_fw_upgrade_handler, NULL, "ili_fw_boot"); + if (fw_boot_th == (struct task_struct *)ERR_PTR) { + fw_boot_th = NULL; + WARN_ON(!fw_boot_th); + ILI_ERR("Failed to create fw upgrade thread\n"); + } +#else + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Failed to disable ice mode failed during init\n"); + +#if (TDDI_INTERFACE == BUS_I2C) + ilits->info_from_hex = DISABLE; +#endif + + ili_ic_get_core_ver(); + ili_ic_get_protocl_ver(); + ili_ic_get_fw_ver(); + ili_ic_get_tp_info(); + ili_ic_get_panel_info(); + +#if (TDDI_INTERFACE == BUS_I2C) + ilits->info_from_hex = ENABLE; +#endif + + ILI_INFO("Registre touch to input subsystem\n"); + ili_input_register(); + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + ilits->boot = true; +#endif + + PM_WAKEUP_REGISTER(NULL, ilits->ws, "ili_wakelock"); + if (!ilits->ws) { + ILI_ERR("wakeup source request failed\n"); + return -ENOMEM; + } + + return 0; +} + +void ili_dev_remove(void) +{ + ILI_INFO("remove ilitek dev\n"); + + if (!ilits) + return; + + gpio_free(ilits->tp_int); + gpio_free(ilits->tp_rst); + + if (esd_wq != NULL) { + cancel_delayed_work_sync(&esd_work); + flush_workqueue(esd_wq); + destroy_workqueue(esd_wq); + } + if (bat_wq != NULL) { + cancel_delayed_work_sync(&bat_work); + flush_workqueue(bat_wq); + destroy_workqueue(bat_wq); + } + + if (ilits->ws) + PM_WAKEUP_UNREGISTER(ilits->ws); + + if (ilits->charger_notif.notifier_call) + power_supply_unreg_notifier(&ilits->charger_notif); + if (ilits->charger_notify_wq) + destroy_workqueue(ilits->charger_notify_wq); + + kfree(ilits->tr_buf); + kfree(ilits->gcoord); + ili_interface_dev_exit(ilits); +} + +void ilitek_spi_shutdown(struct spi_device *spi) +{ + ILI_INFO("Shutdown driver...\n"); + + ili_irq_disable(); + +#ifdef ILI_CONFIG_PANEL_NOTIFICATIONS + ilitek_panel_notifier_unregister(); +#endif + + if (esd_wq != NULL) { + cancel_delayed_work_sync(&esd_work); + flush_workqueue(esd_wq); + destroy_workqueue(esd_wq); + } + if (bat_wq != NULL) { + cancel_delayed_work_sync(&bat_work); + flush_workqueue(bat_wq); + destroy_workqueue(bat_wq); + } + + if (ilits->ws) + PM_WAKEUP_UNREGISTER(ilits->ws); + + ilits->gesture = DISABLE; + + if(gpio_is_valid(ilits->tp_rst)) { + gpio_direction_output(ilits->tp_rst, 0); + mdelay(1); + gpio_set_value(ilits->tp_rst, 0); + } +} + +int ili_dev_init(struct ilitek_hwif_info *hwif) +{ + ILI_INFO("TP Interface: %s\n", (hwif->bus_type == BUS_I2C) ? "I2C" : "SPI"); + return ili_interface_dev_init(hwif); +} diff --git a/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek.h b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek.h new file mode 100644 index 000000000000..d800098d9f20 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek.h @@ -0,0 +1,1174 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __ILI9881X_H +#define __ILI9881X_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_COMPAT +#include +#endif +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_OF +#include +#include +#include +#include +#include +#endif + +#if defined(CONFIG_FB) +#include +#include +#endif +#if defined(CONFIG_DRM) + #if defined(CONFIG_DRM_PANEL) + #include + #endif + #if defined(CONFIG_DRM_MSM) + #include + #endif +#endif/* CONFIG_DRM */ +#if defined(CONFIG_HAS_EARLYSUSPEND) +#include +#endif + +#ifdef CONFIG_DRM_MSM +#include +#endif + +#ifdef ILI_SENSOR_EN +#include +#endif +#ifdef ILI_CONFIG_PANEL_NOTIFICATIONS +#include +#endif +#include + +#define DRIVER_VERSION "3.0.2.0.200512" + +/* Options */ +#define TDDI_INTERFACE BUS_SPI /* BUS_I2C(0x18) or BUS_SPI(0x1C) */ +#define VDD_VOLTAGE 1800000 +#define VCC_VOLTAGE 1800000 +#define SPI_CLK 9 /* follow by clk list */ +#define SPI_RETRY 5 +#define IRQ_GPIO_NUM 66 +#define TR_BUF_SIZE (2*K) /* Buffer size of touch report */ +#define TR_BUF_LIST_SIZE (1*K) /* Buffer size of touch report for debug data */ +#define SPI_TX_BUF_SIZE 4096 +#define SPI_RX_BUF_SIZE 4096 +#define WQ_ESD_DELAY 4000 +#define WQ_BAT_DELAY 2000 +#define AP_INT_TIMEOUT 600 /*600ms*/ +#define MP_INT_TIMEOUT 5000 /*5s*/ +#define MT_B_TYPE ENABLE +#define TDDI_RST_BIND DISABLE +#define MT_PRESSURE DISABLE +#define ENABLE_WQ_ESD DISABLE +#define ENABLE_WQ_BAT DISABLE +#ifdef ILI_SENSOR_EN +#define ENABLE_GESTURE ENABLE +#else +#define ENABLE_GESTURE DISABLE +#endif +#define REGULATOR_POWER DISABLE +#define TP_SUSPEND_PRIO ENABLE +#ifdef ILI_CONFIG_RESUME_BY_DDI +#define RESUME_BY_DDI ENABLE +#else +#define RESUME_BY_DDI DISABLE +#endif +#define BOOT_FW_UPDATE ENABLE +#define MP_INT_LEVEL DISABLE +#define Pll_Clk_Wakeup_TP_Resume ENABLE + +/* Plaform compatibility */ +#define CONFIG_PLAT_SPRD DISABLE +#define I2C_DMA_TRANSFER DISABLE +#define SPI_DMA_TRANSFER_SPLIT ENABLE + +/* Path */ +#define DEBUG_DATA_FILE_SIZE (10*K) +#define DEBUG_DATA_FILE_PATH "/sdcard/ILITEK_log.csv" +#define CSV_LCM_ON_PATH "/sdcard/ilitek_mp_lcm_on_log" +#define CSV_LCM_OFF_PATH "/sdcard/ilitek_mp_lcm_off_log" +#define POWER_STATUS_PATH "/sys/class/power_supply/battery/status" +#define DUMP_FLASH_PATH "/sdcard/flash_dump" +#define DUMP_IRAM_PATH "/sdcard/iram_dump" + +/* Debug messages */ +#define DEBUG_NONE 0 +#define DEBUG_ALL 1 +#define DEBUG_OUTPUT DEBUG_NONE + +#define ILI_INFO(fmt, arg...) \ +({ \ + pr_info("ILITEK: (%s, %d): " fmt, __func__, __LINE__, ##arg); \ +}) \ + +#define ILI_ERR(fmt, arg...) \ +({ \ + pr_err("ILITEK: (%s, %d): " fmt, __func__, __LINE__, ##arg); \ +}) \ + +extern bool debug_en; +#define ILI_DBG(fmt, arg...) \ +do { \ + if (debug_en) \ + pr_info("ILITEK: (%s, %d): " fmt, __func__, __LINE__, ##arg); \ +} while (0) + +#define ERR_ALLOC_MEM(X) ((IS_ERR(X) || X == NULL) ? 1 : 0) +#define K (1024) +#define M (K * K) +#define ENABLE 1 +#define START 1 +#define ON 1 +#define ILI_WRITE 1 +#define ILI_READ 0 +#define DISABLE 0 +#define END 0 +#define OFF 0 +#define NONE -1 +#define DO_SPI_RECOVER -2 + +enum TP_SPI_CLK_LIST { + TP_SPI_CLK_1M = 1000000, + TP_SPI_CLK_2M = 2000000, + TP_SPI_CLK_3M = 3000000, + TP_SPI_CLK_4M = 4000000, + TP_SPI_CLK_5M = 5000000, + TP_SPI_CLK_6M = 6000000, + TP_SPI_CLK_7M = 7000000, + TP_SPI_CLK_8M = 8000000, + TP_SPI_CLK_9M = 9000000, + TP_SPI_CLK_10M = 10000000, + TP_SPI_CLK_11M = 11000000, + TP_SPI_CLK_12M = 12000000, + TP_SPI_CLK_13M = 13000000, + TP_SPI_CLK_14M = 14000000, + TP_SPI_CLK_15M = 15000000, +}; + +enum TP_PLAT_TYPE { + TP_PLAT_QCOM = 1, +}; + +enum TP_RST_METHOD { + TP_IC_WHOLE_RST = 0, + TP_IC_CODE_RST, + TP_HW_RST_ONLY, +}; + +enum TP_FW_UPGRADE_TYPE { + UPGRADE_FLASH = 0, + UPGRADE_IRAM +}; + +enum TP_FW_UPGRADE_STATUS { + FW_STAT_INIT = 0, + FW_UPDATING = 90, + FW_UPDATE_PASS = 100, + FW_UPDATE_FAIL = -1 +}; + +enum TP_FW_OPEN_METHOD { + REQUEST_FIRMWARE = 0, + FILP_OPEN +}; + +enum TP_SLEEP_STATUS { + TP_SUSPEND = 0, + TP_DEEP_SLEEP = 1, + TP_RESUME = 2 +}; + +enum TP_PROXIMITY_STATUS { + DDI_POWER_OFF = 0, + DDI_POWER_ON = 1, + WAKE_UP_GESTURE_RECOVERY = 2, + WAKE_UP_SWITCH_GESTURE_MODE = 3 +}; + +enum TP_SLEEP_CTRL { + SLEEP_IN = 0x0, + SLEEP_OUT = 0x1, + DEEP_SLEEP_IN = 0x3 +}; + +enum TP_FW_BLOCK_NUM { + AP = 1, + DATA = 2, + TUNING = 3, + GESTURE = 4, + MP = 5, + DDI = 6, + TAG = 7, + PARA_BACKUP = 8, + RESERVE_BLOCK3 = 9, + RESERVE_BLOCK4 = 10, + RESERVE_BLOCK5 = 11, + RESERVE_BLOCK6 = 12, + RESERVE_BLOCK7 = 13, + RESERVE_BLOCK8 = 14, + RESERVE_BLOCK9 = 15, + RESERVE_BLOCK10 = 16, +}; + +enum TP_FW_BLOCK_TAG { + BLOCK_TAG_AF = 0xAF, + BLOCK_TAG_B0 = 0xB0 +}; + +enum TP_WQ_TYPE { + WQ_ESD = 0, + WQ_BAT, +}; + +enum TP_RECORD_DATA { + DISABLE_RECORD = 0, + ENABLE_RECORD, + DATA_RECORD +}; + +enum TP_DATA_FORMAT { + DATA_FORMAT_DEMO = 0, + DATA_FORMAT_DEBUG, + DATA_FORMAT_DEMO_DEBUG_INFO, + DATA_FORMAT_GESTURE_SPECIAL_DEMO, + DATA_FORMAT_GESTURE_INFO, + DATA_FORMAT_GESTURE_NORMAL, + DATA_FORMAT_GESTURE_DEMO, + DATA_FORMAT_GESTURE_DEBUG, + DATA_FORMAT_DEBUG_LITE_ROI, + DATA_FORMAT_DEBUG_LITE_WINDOW, + DATA_FORMAT_DEBUG_LITE_AREA +}; + +enum NODE_MODE_SWITCH { + AP_MODE = 0, + TEST_MODE, + DEBUG_MODE, + DEBUG_LITE_ROI, + DEBUG_LITE_WINDOW, + DEBUG_LITE_AREA +}; + +enum TP_MODEL { + MODEL_DEF = 0, + MODEL_CSOT, + MODEL_AUO, + MODEL_BOE, + MODEL_INX, + MODEL_DJ, + MODEL_TXD, + MODEL_TM +}; + +enum TP_ERR_CODE { + EMP_CMD = 100, + EMP_PROTOCOL, + EMP_FILE, + EMP_INI, + EMP_TIMING_INFO, + EMP_INVAL, + EMP_PARSE, + EMP_NOMEM, + EMP_GET_CDC, + EMP_INT, + EMP_CHECK_BUY, + EMP_MODE, + EMP_FW_PROC, + EMP_FORMUL_NULL, + EFW_CONVERT_FILE, + EFW_ICE_MODE, + EFW_CRC, + EFW_REST, + EFW_ERASE, + EFW_PROGRAM, +}; + +enum TP_IC_TYPE { + ILI_A = 0x0A, + ILI_B, + ILI_C, + ILI_D, + ILI_E, + ILI_F, + ILI_G, + ILI_H, + ILI_I, + ILI_J, + ILI_K, + ILI_L, + ILI_M, + ILI_N, + ILI_O, + ILI_P, + ILI_Q, + ILI_R, + ILI_S, + ILI_T, + ILI_U, + ILI_V, + ILI_W, + ILI_X, + ILI_Y, + ILI_Z, +}; + +struct gesture_symbol { + u8 double_tap :1; + u8 alphabet_line_2_top :1; + u8 alphabet_line_2_bottom :1; + u8 alphabet_line_2_left :1; + u8 alphabet_line_2_right :1; + u8 alphabet_m :1; + u8 alphabet_w :1; + u8 alphabet_c :1; + u8 alphabet_E :1; + u8 alphabet_V :1; + u8 alphabet_O :1; + u8 alphabet_S :1; + u8 alphabet_Z :1; + u8 alphabet_V_down :1; + u8 alphabet_V_left :1; + u8 alphabet_V_right :1; + u8 alphabet_two_line_2_bottom :1; + u8 alphabet_F :1; + u8 alphabet_AT :1; + u8 reserve0 :5; +}; + +#define TDDI_I2C_ADDR 0x41 +#define TDDI_DEV_ID "ILITEK_TDDI" + + /* define the width and heigth of a screen. */ +#define TOUCH_SCREEN_X_MIN 0 +#define TOUCH_SCREEN_Y_MIN 0 +#define TOUCH_SCREEN_X_MAX 720 +#define TOUCH_SCREEN_Y_MAX 1440 +#define MAX_TOUCH_NUM 10 + +/* define the range on panel */ +#define TPD_HEIGHT 2048 +#define TPD_WIDTH 2048 + +/* Firmware upgrade */ +#define CORE_VER_1410 0x01040100 +#define CORE_VER_1420 0x01040200 +#define CORE_VER_1430 0x01040300 +#define CORE_VER_1460 0x01040600 +#define MAX_HEX_FILE_SIZE (160*K) +#define ILI_FILE_HEADER 256 +#define DLM_START_ADDRESS 0x20610 +#define DLM_HEX_ADDRESS 0x10000 +#define MP_HEX_ADDRESS 0x13000 +#define DDI_RSV_BK_ST_ADDR 0x1E000 +#define DDI_RSV_BK_END_ADDR 0x1FFFF +#define DDI_RSV_BK_SIZE (1*K) +#define RSV_BK_ST_ADDR 0x1E000 +#define RSV_BK_END_ADDR 0x1E3FF +#define FW_VER_ADDR 0xFFE0 +#define FW_BLOCK_INFO_NUM 17 +#define SPI_UPGRADE_LEN 2048 +#define SPI_BUF_SIZE MAX_HEX_FILE_SIZE +#define INFO_HEX_ST_ADDR 0x4F +#define INFO_MP_HEX_ADDR 0x1F + +/* DMA Control Registers */ +#define DMA_BASED_ADDR 0x72000 +#define DMA48_ADDR (DMA_BASED_ADDR + 0xC0) +#define DMA48_reg_dma_ch0_busy_flag DMA48_ADDR +#define DMA48_reserved_0 0xFFFE +#define DMA48_reg_dma_ch0_trigger_sel (BIT(16)|BIT(17)|BIT(18)|BIT(19)) +#define DMA48_reserved_1 (BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define DMA48_reg_dma_ch0_start_set BIT(24) +#define DMA48_reg_dma_ch0_start_clear BIT(25) +#define DMA48_reg_dma_ch0_trigger_src_mask BIT(26) +#define DMA48_reserved_2 BIT(27) + +#define DMA49_ADDR (DMA_BASED_ADDR + 0xC4) +#define DMA49_reg_dma_ch0_src1_addr DMA49_ADDR +#define DMA49_reserved_0 BIT(20) + +#define DMA50_ADDR (DMA_BASED_ADDR + 0xC8) +#define DMA50_reg_dma_ch0_src1_step_inc DMA50_ADDR +#define DMA50_reserved_0 (DMA50_ADDR + 0x01) +#define DMA50_reg_dma_ch0_src1_format (BIT(24)|BIT(25)) +#define DMA50_reserved_1 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)) +#define DMA50_reg_dma_ch0_src1_en BIT(31) + +#define DMA52_ADDR (DMA_BASED_ADDR + 0xD0) +#define DMA52_reg_dma_ch0_src2_step_inc DMA52_ADDR +#define DMA52_reserved_0 (DMA52_ADDR + 0x01) +#define DMA52_reg_dma_ch0_src2_format (BIT(24)|BIT(25)) +#define DMA52_reserved_1 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)) +#define DMA52_reg_dma_ch0_src2_en BIT(31) + +#define DMA53_ADDR (DMA_BASED_ADDR + 0xD4) +#define DMA53_reg_dma_ch0_dest_addr DMA53_ADDR +#define DMA53_reserved_0 BIT(20) + +#define DMA54_ADDR (DMA_BASED_ADDR + 0xD8) +#define DMA54_reg_dma_ch0_dest_step_inc DMA54_ADDR +#define DMA54_reserved_0 (DMA54_ADDR + 0x01) +#define DMA54_reg_dma_ch0_dest_format (BIT(24)|BIT(25)) +#define DMA54_reserved_1 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)) +#define DMA54_reg_dma_ch0_dest_en BIT(31) + +#define DMA55_ADDR (DMA_BASED_ADDR + 0xDC) +#define DMA55_reg_dma_ch0_trafer_counts DMA55_ADDR +#define DMA55_reserved_0 (BIT(17)|BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define DMA55_reg_dma_ch0_trafer_mode (BIT(24)|BIT(25)|BIT(26)|BIT(27)) +#define DMA55_reserved_1 (BIT(28)|BIT(29)|BIT(30)|BIT(31)) + +/* INT Function Registers */ +#define INTR_BASED_ADDR 0x48000 +#define INTR1_ADDR (INTR_BASED_ADDR + 0x4) +#define INTR1_reg_uart_tx_int_flag INTR1_ADDR +#define INTR1_reserved_0 (BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)) +#define INTR1_reg_wdt_alarm_int_flag BIT(8) +#define INTR1_reserved_1 (BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) +#define INTR1_reg_dma_ch1_int_flag BIT(16) +#define INTR1_reg_dma_ch0_int_flag BIT(17) +#define INTR1_reg_dma_frame_done_int_flag BIT(18) +#define INTR1_reg_dma_tdi_done_int_flag BIT(19) +#define INTR1_reserved_2 (BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define INTR1_reg_flash_error_flag BIT(24) +#define INTR1_reg_flash_int_flag BIT(25) +#define INTR1_reserved_3 BIT(26) + +#define INTR2_ADDR (INTR_BASED_ADDR + 0x8) +#define INTR2_td_int_flag_clear INTR2_ADDR +#define INTR2_td_timeout_int_flag_clear BIT(1) +#define INTR2_td_debug_frame_done_int_flag_clear BIT(2) +#define INTR2_td_frame_start_scan_int_flag_clear BIT(3) +#define INTR2_log_int_flag_clear BIT(4) +#define INTR2_d2t_crc_err_int_flag_clear BIT(8) +#define INTR2_d2t_flash_req_int_flag_clear BIT(9) +#define INTR2_d2t_ddi_int_flag_clear BIT(10) +#define INTR2_wr_done_int_flag_clear BIT(16) +#define INTR2_rd_done_int_flag_clear BIT(17) +#define INTR2_tdi_err_int_flag_clear BIT(18) +#define INTR2_d2t_slpout_rise_flag_clear BIT(24) +#define INTR2_d2t_slpout_fall_flag_clear BIT(25) +#define INTR2_d2t_dstby_flag_clear BIT(26) +#define INTR2_ddi_pwr_rdy_flag_clear BIT(27) + +#define INTR32_ADDR (INTR_BASED_ADDR + 0x80) +#define INTR32_reg_ice_sw_int_en INTR32_ADDR +#define INTR32_reg_ice_apb_conflict_int_en BIT(1) +#define INTR32_reg_ice_ilm_conflict_int_en BIT(2) +#define INTR32_reg_ice_dlm_conflict_int_en BIT(3) +#define INTR32_reserved_0 (BIT(4)|BIT(5)|BIT(6)|BIT(7)) +#define INTR32_reg_spi_sr_int_en BIT(8) +#define INTR32_reg_spi_sp_int_en BIT(9) +#define INTR32_reg_spi_trx_int_en BIT(10) +#define INTR32_reg_spi_cmd_int_en BIT(11) +#define INTR32_reg_spi_rw_int_en BIT(12) +#define INTR32_reserved_1 (BIT(13)|BIT(14)|BIT(15)) +#define INTR32_reg_i2c_start_int_en BIT(16) +#define INTR32_reg_i2c_addr_match_int_en BIT(17) +#define INTR32_reg_i2c_cmd_int_en BIT(18) +#define INTR32_reg_i2c_sr_int_en BIT(19) +#define INTR32_reg_i2c_trx_int_en BIT(20) +#define INTR32_reg_i2c_rx_stop_int_en BIT(21) +#define INTR32_reg_i2c_tx_stop_int_en BIT(22) +#define INTR32_reserved_2 BIT(23) +#define INTR32_reg_t0_int_en BIT(24) +#define INTR32_reg_t1_int_en BIT(25) +#define INTR32_reserved_3 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) + +#define INTR33_ADDR (INTR_BASED_ADDR + 0x84) +#define INTR33_reg_uart_tx_int_en INTR33_ADDR +#define INTR33_reserved_0 (BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)) +#define INTR33_reg_wdt_alarm_int_en BIT(8) +#define INTR33_reserved_1 (BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) +#define INTR33_reg_dma_ch1_int_en BIT(16) +#define INTR33_reg_dma_ch0_int_en BIT(17) +#define INTR33_reg_dma_frame_done_int_en BIT(18) +#define INTR33_reg_dma_tdi_done_int_en BIT(19) +#define INTR33_reserved_2 (BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define INTR33_reg_flash_error_en BIT(24) +#define INTR33_reg_flash_int_en BIT(25) +#define INTR33_reserved_3 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) + +/* Flash */ +#define FLASH_BASED_ADDR 0x41000 +#define FLASH0_ADDR (FLASH_BASED_ADDR + 0x0) +#define FLASH0_reg_flash_csb FLASH0_ADDR +#define FLASH0_reserved_0 (BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) +#define FLASH0_reg_preclk_sel (BIT(16)|BIT(17)|BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define FLASH0_reg_rx_dual BIT(24) +#define FLASH0_reg_tx_dual BIT(25) +#define FLASH0_reserved_26 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) +#define FLASH1_ADDR (FLASH_BASED_ADDR + 0x4) +#define FLASH1_reg_flash_key1 FLASH1_ADDR +#define FLASH1_reg_flash_key2 (FLASH1_ADDR + 0x01) +#define FLASH1_reg_flash_key3 (FLASH1_ADDR + 0x02) +#define FLASH1_reserved_0 (FLASH1_ADDR + 0x03) +#define FLASH2_ADDR (FLASH_BASED_ADDR + 0x8) +#define FLASH2_reg_tx_data FLASH2_ADDR +#define FLASH3_ADDR (FLASH_BASED_ADDR + 0xC) +#define FLASH3_reg_rcv_cnt FLASH3_ADDR +#define FLASH4_ADDR (FLASH_BASED_ADDR + 0x10) +#define FLASH4_reg_rcv_data FLASH4_ADDR +#define FLASH4_reg_rcv_dly BIT(8) +#define FLASH4_reg_sutrg_en BIT(9) +#define FLASH4_reserved_1 (BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) +#define FLASH4_reg_rcv_data_valid_state BIT(16) +#define FLASH4_reg_flash_rd_finish_state BIT(17) +#define FLASH4_reserved_2 (BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define FLASH4_reg_flash_dma_trigger_en (BIT(24)|BIT(25)|BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) + +/* Dummy Registers */ +#define WDT_DUMMY_BASED_ADDR 0x5101C +#define WDT7_DUMMY0 WDT_DUMMY_BASED_ADDR +#define WDT8_DUMMY1 (WDT_DUMMY_BASED_ADDR + 0x04) +#define WDT9_DUMMY2 (WDT_DUMMY_BASED_ADDR + 0x08) + +/* The example for the gesture virtual keys */ +#define GESTURE_DOUBLECLICK 0x58 +#define GESTURE_UP 0x60 +#define GESTURE_DOWN 0x61 +#define GESTURE_LEFT 0x62 +#define GESTURE_RIGHT 0x63 +#define GESTURE_M 0x64 +#define GESTURE_W 0x65 +#define GESTURE_C 0x66 +#define GESTURE_E 0x67 +#define GESTURE_V 0x68 +#define GESTURE_O 0x69 +#define GESTURE_S 0x6A +#define GESTURE_Z 0x6B +#define KEY_GESTURE_POWER KEY_POWER +#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_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 KEY_GESTURE_F KEY_F +#define GESTURE_V_DOWN 0x6C +#define GESTURE_V_LEFT 0x6D +#define GESTURE_V_RIGHT 0x6E +#define GESTURE_TWOLINE_DOWN 0x6F +#define GESTURE_F 0x70 +#define GESTURE_AT 0x71 +#define ESD_GESTURE_PWD 0xF38A94EF +#define SPI_ESD_GESTURE_RUN 0xA67C9DFE +#define I2C_ESD_GESTURE_RUN 0xA67C9DFE +#define SPI_ESD_GESTURE_PWD_ADDR 0x25FF8 +#define I2C_ESD_GESTURE_PWD_ADDR 0x40054 + +#define ESD_GESTURE_CORE146_PWD 0xF38A +#define SPI_ESD_GESTURE_CORE146_RUN 0xA67C +#define I2C_ESD_GESTURE_CORE146_RUN 0xA67C +#define SPI_ESD_GESTURE_CORE146_PWD_ADDR 0x4005C +#define I2C_ESD_GESTURE_CORE146_PWD_ADDR 0x4005C + +#define DOUBLE_TAP ( ON )//BIT0 +#define ALPHABET_LINE_2_TOP ( ON )//BIT1 +#define ALPHABET_LINE_2_BOTTOM ( ON )//BIT2 +#define ALPHABET_LINE_2_LEFT ( ON )//BIT3 +#define ALPHABET_LINE_2_RIGHT ( ON )//BIT4 +#define ALPHABET_M ( ON )//BIT5 +#define ALPHABET_W ( ON )//BIT6 +#define ALPHABET_C ( ON )//BIT7 +#define ALPHABET_E ( ON )//BIT8 +#define ALPHABET_V ( ON )//BIT9 +#define ALPHABET_O ( ON )//BIT10 +#define ALPHABET_S ( ON )//BIT11 +#define ALPHABET_Z ( ON )//BIT12 +#define ALPHABET_V_DOWN ( ON )//BIT13 +#define ALPHABET_V_LEFT ( ON )//BIT14 +#define ALPHABET_V_RIGHT ( ON )//BIT15 +#define ALPHABET_TWO_LINE_2_BOTTOM ( ON )//BIT16 +#define ALPHABET_F ( ON )//BIT17 +#define ALPHABET_AT ( ON )//BIT18 + +/* FW data format */ +#define DATA_FORMAT_DEMO_CMD 0x00 +#define DATA_FORMAT_DEBUG_CMD 0x02 +#define DATA_FORMAT_DEMO_DEBUG_INFO_CMD 0x04 +#define DATA_FORMAT_GESTURE_NORMAL_CMD 0x01 +#define DATA_FORMAT_GESTURE_INFO_CMD 0x02 +#define DATA_FORMAT_DEBUG_LITE_CMD 0x05 +#define DATA_FORMAT_DEBUG_LITE_ROI_CMD 0x01 +#define DATA_FORMAT_DEBUG_LITE_WINDOW_CMD 0x02 +#define DATA_FORMAT_DEBUG_LITE_AREA_CMD 0x03 +#define P5_X_DEMO_MODE_PACKET_LEN 43 +#define P5_X_INFO_HEADER_LENGTH 3 +#define P5_X_INFO_CHECKSUM_LENGTH 1 +#define P5_X_DEMO_DEBUG_INFO_ID0_LENGTH 14 +#define P5_X_DEBUG_MODE_PACKET_LENGTH 1280 +#define P5_X_TEST_MODE_PACKET_LENGTH 1180 +#define P5_X_GESTURE_NORMAL_LENGTH 8 +#define P5_X_GESTURE_INFO_LENGTH 170 +#define P5_X_DEBUG_LITE_LENGTH 300 +#define P5_X_CORE_VER_THREE_LENGTH 5 +#define P5_X_CORE_VER_FOUR_LENGTH 6 + + +/* Protocol */ +#define PROTOCOL_VER_500 0x050000 +#define PROTOCOL_VER_510 0x050100 +#define PROTOCOL_VER_520 0x050200 +#define PROTOCOL_VER_530 0x050300 +#define PROTOCOL_VER_540 0x050400 +#define PROTOCOL_VER_550 0x050500 +#define PROTOCOL_VER_560 0x050600 +#define PROTOCOL_VER_570 0x050700 +#define P5_X_READ_DATA_CTRL 0xF6 +#define P5_X_GET_TP_INFORMATION 0x20 +#define P5_X_GET_KEY_INFORMATION 0x27 +#define P5_X_GET_PANEL_INFORMATION 0x29 +#define P5_X_GET_FW_VERSION 0x21 +#define P5_X_GET_PROTOCOL_VERSION 0x22 +#define P5_X_GET_CORE_VERSION 0x23 +#define P5_X_GET_CORE_VERSION_NEW 0x24 +#define P5_X_MODE_CONTROL 0xF0 +#define P5_X_SET_CDC_INIT 0xF1 +#define P5_X_GET_CDC_DATA 0xF2 +#define P5_X_CDC_BUSY_STATE 0xF3 +#define P5_X_MP_TEST_MODE_INFO 0xFE +#define P5_X_I2C_UART 0x40 +#define CMD_GET_FLASH_DATA 0x41 +#define CMD_CTRL_INT_ACTION 0x1B +#define P5_X_FW_UNKNOWN_MODE 0xFF +#define P5_X_FW_AP_MODE 0x00 +#define P5_X_FW_TEST_MODE 0x01 +#define P5_X_FW_GESTURE_MODE 0x0F +#define P5_X_FW_DELTA_DATA_MODE 0x03 +#define P5_X_FW_RAW_DATA_MODE 0x08 +#define P5_X_DEMO_PACKET_ID 0x5A +#define P5_X_DEBUG_PACKET_ID 0xA7 +#define P5_X_TEST_PACKET_ID 0xF2 +#define P5_X_GESTURE_PACKET_ID 0xAA +#define P5_X_GESTURE_FAIL_ID 0xAE +#define P5_X_I2CUART_PACKET_ID 0x7A +#define P5_X_DEBUG_LITE_PACKET_ID 0x9A +#define P5_X_SLAVE_MODE_CMD_ID 0x5F +#define P5_X_INFO_HEADER_PACKET_ID 0xB7 +#define P5_X_DEMO_DEBUG_INFO_PACKET_ID 0x5C +#define P5_X_EDGE_PLAM_CTRL_1 0x01 +#define P5_X_EDGE_PLAM_CTRL_2 0x12 +#define SPI_WRITE 0x82 +#define SPI_READ 0x83 +#define SPI_ACK 0xA3 + +/* Chips */ +#define ILI9881_CHIP 0x9881 +#define ILI7807_CHIP 0x7807 +#define ILI9881N_AA 0x98811700 +#define ILI9881O_AA 0x98811800 +#define ILI9882_CHIP 0x9882 +#define TDDI_PID_ADDR 0x4009C +#define TDDI_OTP_ID_ADDR 0x400A0 +#define TDDI_ANA_ID_ADDR 0x400A4 +#define TDDI_PC_COUNTER_ADDR 0x44008 +#define TDDI_PC_LATCH_ADDR 0x51010 +#define TDDI_CHIP_RESET_ADDR 0x40050 +#define RAWDATA_NO_BK_SHIFT 8192 + +#ifdef ILI_SENSOR_EN +/* display state */ +enum display_state { + SCREEN_UNKNOWN, + SCREEN_OFF, + SCREEN_ON, +}; +struct ili_sensor_platform_data { + struct input_dev *input_sensor_dev; + struct sensors_classdev ps_cdev; + int sensor_opened; + char sensor_data; /* 0 near, 1 far */ + struct ilitek_ts_data *data; +}; +#define REPORT_MAX_COUNT 10000 +#endif + +struct ilitek_ts_data { + struct i2c_client *i2c; + struct spi_device *spi; + struct input_dev *input; + struct device *dev; + struct wakeup_source *ws; + + struct ilitek_hwif_info *hwif; + struct ilitek_ic_info *chip; + struct ilitek_protocol_info *protocol; + struct gesture_coordinate *gcoord; + struct regulator *vdd; + struct regulator *vcc; + + struct notifier_block charger_notif; + struct workqueue_struct *charger_notify_wq; + struct work_struct charger_notify_work; + int usb_plug_status; + +#ifdef CONFIG_FB + struct notifier_block notifier_fb; +#else + struct early_suspend early_suspend; +#endif + +#ifdef ILI_CONFIG_PANEL_NOTIFICATIONS + struct notifier_block panel_notif; +#endif + + struct mutex touch_mutex; + struct mutex debug_mutex; + struct mutex debug_read_mutex; + spinlock_t irq_spin; + + struct completion pm_completion; + bool pm_suspend; + + /* physical path to the input device in the system hierarchy */ + const char *phys; + + bool boot; + u32 fw_pc; + u32 fw_latch; + + u16 max_x; + u16 max_y; + u16 min_x; + u16 min_y; + u16 panel_wid; + u16 panel_hei; + u8 xch_num; + u8 ych_num; + u8 stx; + u8 srx; + u8 *update_buf; + u8 *tr_buf; + u8 *spi_tx; + u8 *spi_rx; + struct firmware tp_fw; + + int actual_tp_mode; + int tp_data_format; + int tp_data_len; + + int irq_num; + int irq_gpio; + int irq_tirgger_type; + bool charger_detection_enable; + int tp_rst; + int tp_int; + int wait_int_timeout; + + int finger; + int curt_touch[MAX_TOUCH_NUM]; + int prev_touch[MAX_TOUCH_NUM]; + int last_touch; + + int fw_retry; + int fw_update_stat; + int fw_open; + u8 fw_info[75]; + u8 fw_mp_ver[4]; + bool wq_ctrl; + bool wq_esd_ctrl; + bool wq_bat_ctrl; + + bool netlink; + bool report; + bool gesture; + bool mp_retry; + int gesture_mode; + int gesture_demo_ctrl; + struct gesture_symbol ges_sym; + + u16 flash_mid; + u16 flash_devid; + int program_page; + int flash_sector; + + /* Sending report data to users for the debug */ + bool dnp; //debug node open + int dbf; //debug data frame + int odi; //out data index + wait_queue_head_t inq; + struct debug_buf_list *dbl; + int raw_count; + int delta_count; + int bg_count; + + int reset; + int rst_edge_delay; + int fw_upgrade_mode; + int mp_ret_len; + bool wtd_ctrl; + bool force_fw_update; + bool irq_after_recovery; + bool ddi_rest_done; + bool resume_by_ddi; + bool tp_suspend; + bool info_from_hex; + bool prox_near; + bool gesture_load_code; + bool trans_xy; + bool ss_ctrl; + bool node_update; + bool int_pulse; + bool pll_clk_wakeup; + + /* module info */ + int tp_module; + int md_fw_ili_size; + char *md_name; + char *md_fw_filp_path; + char *md_fw_rq_path; + char *md_ini_path; + char *md_ini_rq_path; + u8 *md_fw_ili; + + atomic_t irq_stat; + atomic_t tp_reset; + atomic_t ice_stat; + atomic_t fw_stat; + atomic_t mp_stat; + atomic_t tp_sleep; + atomic_t tp_sw_mode; + atomic_t cmd_int_check; + atomic_t esd_stat; + +#ifdef ILI_SENSOR_EN + bool wakeable; + bool should_enable_gesture; + bool gesture_enabled; + uint32_t report_gesture_key; + enum display_state screen_state; + struct mutex state_mutex; + struct ili_sensor_platform_data *sensor_pdata; +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock gesture_wakelock; +#else + struct wakeup_source *gesture_wakelock; +#endif +#endif + + /* Event for driver test */ + struct completion esd_done; + + int (*spi_write_then_read)(struct spi_device *spi, const void *txbuf, + unsigned n_tx, void *rxbuf, unsigned n_rx); + int (*wrapper)(u8* wdata, u32 wlen, u8* rdata, u32 rlen, bool spi_irq, bool i2c_irq); + int (*mp_move_code)(void); + int (*gesture_move_code)(int mode); + int (*esd_recover)(void); + int (*ges_recover)(void); + void (*demo_debug_info[5])(u8 *, size_t); + int (*detect_int_stat)(bool status); +}; +extern struct ilitek_ts_data *ilits; + +struct debug_buf_list { + bool mark; + unsigned char *data; +}; + +struct ilitek_touch_info { + u16 id; + u16 x; + u16 y; + u16 pressure; +}; + +struct gesture_coordinate { + u16 code; + u8 clockwise; + int type; + struct ilitek_touch_info pos_start; + struct ilitek_touch_info pos_end; + struct ilitek_touch_info pos_1st; + struct ilitek_touch_info pos_2nd; + struct ilitek_touch_info pos_3rd; + struct ilitek_touch_info pos_4th; +}; + +struct ilitek_protocol_info { + u32 ver; + int fw_ver_len; + int pro_ver_len; + int tp_info_len; + int key_info_len; + int panel_info_len; + int core_ver_len; + int func_ctrl_len; + int window_len; + int cdc_len; + int mp_info_len; +}; + +struct ilitek_ic_func_ctrl { + const char *name; + u8 cmd[6]; + int len; + u8 rec_state; //0:disable, 1: enable, 2: ignore record + u8 rec_cmd; +}; + +struct ilitek_ic_info { + u8 type; + u8 ver; + u16 id; + u32 pid; + u32 pid_addr; + u32 pc_counter_addr; + u32 pc_latch_addr; + u32 reset_addr; + u32 otp_addr; + u32 ana_addr; + u32 otp_id; + u32 ana_id; + u32 fw_ver; + u32 core_ver; + u32 fw_mp_ver; + u32 max_count; + u32 reset_key; + u16 wtd_key; + int no_bk_shift; + bool dma_reset; + int (*dma_crc)(u32 start_addr, u32 block_size); +}; + +struct ilitek_hwif_info { + u8 bus_type; + u8 plat_type; + const char *name; + struct module *owner; + const struct of_device_id *of_match_table; + const struct dev_pm_ops *pm; + int (*plat_probe)(void); + int (*plat_remove)(void); + void *info; +}; + +/* Prototypes for tddi firmware/flash functions */ +extern void ili_flash_dma_write(u32 start, u32 end, u32 len); +extern void ili_flash_clear_dma(void); +extern void ili_fw_read_flash_info(void); +extern u32 ili_fw_read_hw_crc(u32 start, u32 end); +extern int ili_fw_read_flash(u32 start, u32 end, u8 *data, int len); +extern int ili_fw_dump_iram_data(u32 start, u32 end, bool save); +extern int ili_fw_dump_flash_data(u32 start, u32 end, bool user); +extern int ili_fw_upgrade(int op); + +/* Prototypes for tddi mp test */ +extern int ili_mp_test_main(char *apk, bool lcm_on); + +/* Prototypes for tddi core functions */ +extern int ili_touch_esd_gesture_flash(void); +extern int ili_touch_esd_gesture_iram(void); +extern void ili_set_gesture_symbol(void); +extern int ili_move_gesture_code_flash(int mode); +extern int ili_move_gesture_code_iram(int mode); +extern int ili_move_mp_code_flash(void); +extern int ili_move_mp_code_iram(void); +extern void ili_touch_press(u16 x, u16 y, u16 pressure, u16 id); +extern void ili_touch_release(u16 x, u16 y, u16 id); +extern void ili_touch_release_all_point(void); +extern void ili_report_ap_mode(u8 *buf, int len); +extern void ili_report_debug_mode(u8 *buf, int rlen); +extern void ili_report_debug_lite_mode(u8 *buf, int rlen); +extern void ili_report_gesture_mode(u8 *buf, int rlen); +extern void ili_report_i2cuart_mode(u8 *buf, int rlen); +extern void ili_ic_set_ddi_reg_onepage(u8 page, u8 reg, u8 data); +extern void ili_ic_get_ddi_reg_onepage(u8 page, u8 reg, u8 *data); +extern int ili_ic_whole_reset(void); +extern int ili_ic_code_reset(void); +extern int ili_ic_func_ctrl(const char *name, int ctrl); +extern void ili_ic_func_ctrl_reset(void); +extern int ili_ic_int_trigger_ctrl(bool pulse); +extern void ili_ic_get_pc_counter(int stat); +extern int ili_ic_check_int_level(bool level); +extern int ili_ic_check_int_pulse(bool pulse); +extern int ili_ic_check_busy(int count, int delay); +extern int ili_ic_get_project_id(u8 *pdata, int size); +extern int ili_ic_get_panel_info(void); +extern int ili_ic_get_tp_info(void); +extern int ili_ic_get_core_ver(void); +extern int ili_ic_get_protocl_ver(void); +extern int ili_ic_get_fw_ver(void); +extern int ili_ic_get_info(void); +extern int ili_ic_dummy_check(void); +extern int ili_ice_mode_bit_mask_write(u32 addr, u32 mask, u32 value); +extern int ili_ice_mode_write(u32 addr, u32 data, int len); +extern int ili_ice_mode_read(u32 addr, u32 *data, int len); +extern int ili_ice_mode_ctrl(bool enable, bool mcu); +extern void ili_ic_init(void); +extern void ili_fw_uart_ctrl(u8 ctrl); + +/* Prototypes for tddi events */ +#if RESUME_BY_DDI +extern void ili_resume_by_ddi(void); +#endif +extern int ili_proximity_far(int mode); +extern int ili_proximity_near(int mode); +extern int ili_switch_tp_mode(u8 data); +extern int ili_set_tp_data_len(int format, bool send, u8* data); +extern int ili_fw_upgrade_handler(void *data); +extern int ili_wq_esd_i2c_check(void); +extern int ili_wq_esd_spi_check(void); +extern int ili_gesture_recovery(void); +extern void ili_spi_recovery(void); +extern void ili_wq_ctrl(int type, int ctrl); +extern int ili_mp_test_handler(char *apk, bool lcm_on); +extern int ili_report_handler(void); +extern int ili_sleep_handler(int mode); +extern int ili_reset_ctrl(int mode); +extern int ili_tddi_init(void); +extern int ili_dev_init(struct ilitek_hwif_info *hwif); +extern void ili_dev_remove(void); + +/* Prototypes for i2c/spi interface */ +extern int ili_core_spi_setup(int num); +extern int ili_interface_dev_init(struct ilitek_hwif_info *hwif); +extern void ili_interface_dev_exit(struct ilitek_ts_data *ts); +extern int ili_spi_write_then_read_split(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx); +extern int ili_spi_write_then_read_direct(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx); + +/* Prototypes for platform level */ +extern void ili_plat_regulator_power_on(bool status); +extern void ili_input_register(void); +extern void ili_irq_disable(void); +extern void ili_irq_enable(void); +extern void ili_tp_reset(void); +extern void ili_irq_unregister(void); +extern int ili_irq_register(int type); + +/* Prototypes for miscs */ +extern void ili_node_init(void); +extern void ili_dump_data(void *data, int type, int len, int row_len, const char *name); +extern u8 ili_calc_packet_checksum(u8 *packet, int len); +extern void ili_netlink_reply_msg(void *raw, int size); +extern int ili_katoi(char *str); +extern int ili_str2hex(char *str); + +extern int ili_mp_read_write(u8* wdata, u32 wlen, u8* rdata, u32 rlen); +/* Prototypes for additional functionalities */ +extern void ili_gesture_fail_reason(bool enable); +extern int ili_get_tp_recore_ctrl(int data); +extern int ili_get_tp_recore_data(void); +extern void ili_demo_debug_info_mode(u8 *buf, size_t rlen); +extern void ili_demo_debug_info_id0(u8 *buf, size_t len); +#ifdef ILI_CONFIG_PANEL_NOTIFICATIONS +extern void ilitek_panel_notifier_unregister(void); +#endif +extern void ilitek_spi_shutdown(struct spi_device *spi); + +static inline void ipio_kfree(void **mem) +{ + if (*mem != NULL) { + kfree(*mem); + *mem = NULL; + } +} + +static inline void ipio_vfree(void **mem) +{ + if (*mem != NULL) { + vfree(*mem); + *mem = NULL; + } +} + +static inline void *ipio_memcpy(void *dest, const void *src, int n, int dest_size) +{ + if (n > dest_size) + n = dest_size; + + return memcpy(dest, src, n); +} + +static inline int ipio_strcmp(const char *s1, const char *s2) +{ + return (strlen(s1) != strlen(s2)) ? -1 : strncmp(s1, s2, strlen(s1)); +} + +#endif /* __ILI9881X_H */ diff --git a/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_flash.c b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_flash.c new file mode 100644 index 000000000000..d64eaa8ad6a5 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_flash.c @@ -0,0 +1,1451 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek.h" + +#define UPDATE_PASS 0 +#define UPDATE_FAIL -1 +#define TIMEOUT_SECTOR 500 +#define TIMEOUT_PAGE 3500 +#define TIMEOUT_PROGRAM 10 + +static struct touch_fw_data { + u8 block_number; + u32 start_addr; + u32 end_addr; + u32 new_fw_cb; + int delay_after_upgrade; + bool isCRC; + bool isboot; + bool is80k; + int hex_tag; +} tfd; + +static struct flash_block_info { + char *name; + u32 start; + u32 end; + u32 len; + u32 mem_start; + u32 fix_mem_start; + u8 mode; +} fbi[FW_BLOCK_INFO_NUM]; + +static u8 fw_flash_tmp[K + 7]; +static u8 *pfw; +static u8 *CTPM_FW; + +static u32 HexToDec(char *phex, s32 len) +{ + u32 ret = 0, temp = 0, i; + s32 shift = (len - 1) * 4; + + for (i = 0; i < len; shift -= 4, i++) { + if ((phex[i] >= '0') && (phex[i] <= '9')) + temp = phex[i] - '0'; + else if ((phex[i] >= 'a') && (phex[i] <= 'f')) + temp = (phex[i] - 'a') + 10; + else if ((phex[i] >= 'A') && (phex[i] <= 'F')) + temp = (phex[i] - 'A') + 10; + else + return -1; + + ret |= (temp << shift); + } + return ret; +} + +static int CalculateCRC32(u32 start_addr, u32 len, u8 *pfw) +{ + int i = 0, j = 0; + int crc_poly = 0x04C11DB7; + int tmp_crc = 0xFFFFFFFF; + + for (i = start_addr; i < start_addr + len; i++) { + tmp_crc ^= (pfw[i] << 24); + + for (j = 0; j < 8; j++) { + if ((tmp_crc & 0x80000000) != 0) + tmp_crc = tmp_crc << 1 ^ crc_poly; + else + tmp_crc = tmp_crc << 1; + } + } + return tmp_crc; +} + +static int ilitek_tddi_fw_iram_read(u8 *buf, u32 start, int len) +{ + int limit = 4*K; + int addr = 0, loop = 0, tmp_len = len, cnt = 0; + u8 cmd[4] = {0}; + + if (!buf) { + ILI_ERR("buf is null\n"); + return -ENOMEM; + } + + if (len % limit) + loop = (len / limit) + 1; + else + loop = len / limit; + + for (cnt = 0, addr = start; cnt < loop; cnt++, addr += limit) { + if (tmp_len > limit) + tmp_len = limit; + + cmd[0] = 0x25; + cmd[3] = (char)((addr & 0x00FF0000) >> 16); + cmd[2] = (char)((addr & 0x0000FF00) >> 8); + cmd[1] = (char)((addr & 0x000000FF)); + + if (ilits->wrapper(cmd, 4, NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Failed to write iram data\n"); + return -ENODEV; + } + + if (ilits->wrapper(NULL, 0, buf + cnt * limit, tmp_len, OFF, OFF) < 0) { + ILI_ERR("Failed to Read iram data\n"); + return -ENODEV; + } + + tmp_len = len - cnt * limit; + ilits->fw_update_stat = ((len - tmp_len) * 100) / len; + ILI_DBG("Reading iram data .... %d%c", ilits->fw_update_stat, '%'); + } + return 0; +} + +int ili_fw_dump_iram_data(u32 start, u32 end, bool save) +{ + struct file *f = NULL; + mm_segment_t old_fs; + loff_t pos = 0; + int i, ret, len; + u8 *fw_buf = NULL; + + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) { + ILI_ERR("Enable ice mode failed\n"); + ret = -1; + goto out; + } + + len = end - start + 1; + + if (len > MAX_HEX_FILE_SIZE) { + ILI_ERR("len is larger than buffer, abort\n"); + ret = -EINVAL; + goto out; + } + + fw_buf = kzalloc(MAX_HEX_FILE_SIZE, GFP_KERNEL); + if (ERR_ALLOC_MEM(fw_buf)) { + ILI_ERR("Failed to allocate update_buf\n"); + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + fw_buf[i] = 0xFF; + + ret = ilitek_tddi_fw_iram_read(fw_buf, start, len); + if (ret < 0) + goto out; + + f = filp_open(DUMP_IRAM_PATH, O_WRONLY | O_CREAT | O_TRUNC, 644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file at %ld.\n", PTR_ERR(f)); + ret = -1; + goto out; + } + + old_fs = get_fs(); + set_fs(get_ds()); + set_fs(KERNEL_DS); + pos = 0; + vfs_write(f, fw_buf, len, &pos); + set_fs(old_fs); + filp_close(f, NULL); + ILI_INFO("Save iram data to %s\n", DUMP_IRAM_PATH); + +out: + ili_ice_mode_ctrl(DISABLE, OFF); + ILI_INFO("Dump IRAM %s\n", (ret < 0) ? "FAIL" : "SUCCESS"); + ipio_kfree((void **)&fw_buf); + return ret; +} + +static int ilitek_tddi_flash_poll_busy(int timer) +{ + int ret = UPDATE_PASS, retry = timer; + u8 cmd = 0x5; + u32 temp = 0; + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Pull cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, cmd, 1) < 0) + ILI_ERR("Write 0x5 cmd failed\n"); + + do { + if (ili_ice_mode_write(FLASH2_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + mdelay(1); + + if (ili_ice_mode_read(FLASH4_ADDR, &temp, sizeof(u8)) < 0) + ILI_ERR("Read flash busy error\n"); + + if ((temp & 0x3) == 0) + break; + } while (--retry >= 0); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Pull cs high failed\n"); + + if (retry <= 0) { + ILI_ERR("Flash polling busy timeout ! tmp = %x\n", temp); + ret = UPDATE_FAIL; + } + + return ret; +} + +void ili_flash_clear_dma(void) +{ + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_preclk_sel, (2 << 16)) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_preclk_sel, FLASH0_ADDR); + + if (ili_ice_mode_bit_mask_write(FLASH4_ADDR, FLASH4_reg_flash_dma_trigger_en, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH4_reg_flash_dma_trigger_en, FLASH4_ADDR); + + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_rx_dual, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_rx_dual, FLASH0_ADDR); + + if (ili_ice_mode_write(FLASH3_reg_rcv_cnt, 0x00, 1) < 0) + ILI_ERR("Write 0x0 at %x failed\n", FLASH3_reg_rcv_cnt); + + if (ili_ice_mode_write(FLASH4_reg_rcv_data, 0xFF, 1) < 0) + ILI_ERR("Write 0xFF at %x failed\n", FLASH4_reg_rcv_data); +} + +static int ilitek_tddi_flash_read_int_flag(void) +{ + int retry = 500; + u32 data = 0; + + do { + if (ili_ice_mode_read(INTR1_ADDR & BIT(25), &data, sizeof(u32)) < 0) + ILI_ERR("Read flash int flag error\n"); + + ILI_DBG("int flag = %x\n", data); + if (data) + break; + } while (--retry >= 0); + + if (retry <= 0) { + ILI_ERR("Read Flash INT flag timeout !, flag = 0x%x\n", data); + return -1; + } + return 0; +} + +void ili_flash_dma_write(u32 start, u32 end, u32 len) +{ + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_preclk_sel, 1 << 16) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_preclk_sel, FLASH0_ADDR); + + if (ili_ice_mode_write(FLASH0_reg_flash_csb, 0x00, 1) < 0) + ILI_ERR("Pull cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_reg_flash_key1, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, 0x0b, 1) < 0) + ILI_ERR("Write 0x0b at %x failed\n", FLASH2_reg_tx_data); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write 0xb timeout \n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, (start & 0xFF0000) >> 16, 1) < 0) + ILI_ERR("Write %x at %x failed\n", (start & 0xFF0000), FLASH2_reg_tx_data); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write addr1 timeout\n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, (start & 0x00FF00) >> 8, 1) < 0) + ILI_ERR("Write %x at %x failed\n", (start & 0x00FF00) >> 8, FLASH2_reg_tx_data); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write addr2 timeout\n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, (start & 0x0000FF), 1) < 0) + ILI_ERR("Write %x at %x failed\n", (start & 0x0000FF), FLASH2_reg_tx_data); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write addr3 timeout\n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_rx_dual, 0 << 24) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_rx_dual, FLASH0_ADDR); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, 0x00, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write dummy timeout\n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_write(FLASH3_reg_rcv_cnt, len, 4) < 0) + ILI_ERR("Write length failed\n"); +} + +static void ilitek_tddi_flash_write_enable(void) +{ + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Pull CS low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x6, 1) < 0) + ILI_ERR("Write 0x6 failed\n"); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Pull CS high failed\n"); +} + +u32 ili_fw_read_hw_crc(u32 start, u32 end) +{ + int retry = 500; + u32 busy = 0; + u32 write_len = end; + u32 flash_crc = 0; + + if (write_len > ilits->chip->max_count) { + ILI_ERR("The length (%x) written into firmware is greater than max count (%x)\n", + write_len, ilits->chip->max_count); + return -1; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Pull CS low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x3b, 1) < 0) + ILI_ERR("Write 0x3b failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0xFF0000) >> 16, 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0x00FF00) >> 8, 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0x0000FF), 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(0x041003, 0x01, 1) < 0) + ILI_ERR("Write enable Dio_Rx_dual failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + if (ili_ice_mode_write(0x04100C, write_len, 3) < 0) + ILI_ERR("Write Set Receive count failed\n"); + + if (ili_ice_mode_write(0x048007, 0x02, 1) < 0) + ILI_ERR("Write clearing int flag failed\n"); + + if (ili_ice_mode_write(0x041016, 0x00, 1) < 0) + ILI_ERR("Write 0x0 at 0x041016 failed\n"); + + if (ili_ice_mode_write(0x041016, 0x01, 1) < 0) + ILI_ERR("Write Checksum_En failed\n"); + + if (ili_ice_mode_write(FLASH4_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write start to receive failed\n"); + + do { + if (ili_ice_mode_read(0x048007, &busy, sizeof(u8)) < 0) + ILI_ERR("Read busy error\n"); + + ILI_DBG("busy = %x\n", busy); + if (((busy >> 1) & 0x01) == 0x01) + break; + } while (--retry >= 0); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write CS high failed\n"); + + if (retry <= 0) { + ILI_ERR("Read HW CRC timeout !, busy = 0x%x\n", busy); + return -1; + } + + if (ili_ice_mode_write(0x041003, 0x0, 1) < 0) + ILI_ERR("Write disable dio_Rx_dual failed\n"); + + if (ili_ice_mode_read(0x04101C, &flash_crc, sizeof(u32)) < 0) { + ILI_ERR("Read hw crc error\n"); + return -1; + } + + return flash_crc; +} + +static int ilitek_tddi_fw_read_flash_data(u32 start, u32 end, u8 *data, int len) +{ + u32 i, j, index = 0; + u32 tmp; + + if (end - start > len) { + ILI_ERR("the length (%d) reading crc is over than len(%d)\n", end - start, len); + return -1; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x03, 1) < 0) + ILI_ERR("Write 0x3 failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0xFF0000) >> 16, 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0x00FF00) >> 8, 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0x0000FF), 1) < 0) + ILI_ERR("Write address failed\n"); + + for (i = start, j = 0; i <= end; i++, j++) { + if (ili_ice_mode_write(FLASH2_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + if (ili_ice_mode_read(FLASH4_ADDR, &tmp, sizeof(u8)) < 0) + ILI_ERR("Read flash data error!\n"); + + data[index] = tmp; + index++; + ilits->fw_update_stat = (j * 100) / len; + ILI_DBG("Reading flash data .... %d%c", ilits->fw_update_stat, '%'); + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + + return 0; +} + +int ili_fw_dump_flash_data(u32 start, u32 end, bool user) +{ + struct file *f = NULL; + u8 *buf = NULL; + mm_segment_t old_fs; + loff_t pos = 0; + u32 start_addr, end_addr; + int ret, length; + + f = filp_open(DUMP_FLASH_PATH, O_WRONLY | O_CREAT | O_TRUNC, 644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file at %ld.\n", PTR_ERR(f)); + ret = -1; + goto out; + } + + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) + goto out; + + if (user) { + start_addr = 0x0; + end_addr = 0x1FFFF; + } else { + start_addr = start; + end_addr = end; + } + + length = end_addr - start_addr + 1; + ILI_INFO("len = %d\n", length); + + buf = vmalloc(length * sizeof(u8)); + if (ERR_ALLOC_MEM(buf)) { + ILI_ERR("Failed to allocate buf memory, %ld\n", PTR_ERR(buf)); + filp_close(f, NULL); + ret = -1; + goto out; + } + + ret = ilitek_tddi_fw_read_flash_data(start_addr, end_addr, buf, length); + if (ret < 0) + goto out; + + old_fs = get_fs(); + set_fs(get_ds()); + set_fs(KERNEL_DS); + pos = 0; + vfs_write(f, buf, length, &pos); + set_fs(old_fs); + filp_close(f, NULL); + ipio_vfree((void **)&buf); + +out: + ili_ice_mode_ctrl(DISABLE, OFF); + ILI_INFO("Dump flash %s\n", (ret < 0) ? "FAIL" : "SUCCESS"); + return ret; +} + +static void ilitek_tddi_flash_protect(bool enable) +{ + ILI_INFO("%s flash protection\n", enable ? "Enable" : "Disable"); + + ilitek_tddi_flash_write_enable(); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x1, 1) < 0) + ILI_ERR("Write 0x1 failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x0, 1) < 0) + ILI_ERR("Write 0x0 failed\n"); + + switch (ilits->flash_mid) { + case 0xEF: + if (ilits->flash_devid == 0x6012 || ilits->flash_devid == 0x6011) { + if (enable) { + if (ili_ice_mode_write(FLASH2_ADDR, 0x7E, 1) < 0) + ILI_ERR("Write 0x7E at %x failed\n", FLASH2_ADDR); + } else { + if (ili_ice_mode_write(FLASH2_ADDR, 0x0, 1) < 0) + ILI_ERR("Write 0x0 at %x failed\n", FLASH2_ADDR); + } + } + break; + case 0xC8: + if (ilits->flash_devid == 0x6012 || ilits->flash_devid == 0x6013) { + if (enable) { + if (ili_ice_mode_write(FLASH2_ADDR, 0x7A, 1) < 0) + ILI_ERR("Write 0x7A at %x failed\n", FLASH2_ADDR); + } else { + if (ili_ice_mode_write(FLASH2_ADDR, 0x0, 1) < 0) + ILI_ERR("Write 0x0 at %x failed\n", FLASH2_ADDR); + } + + } + break; + default: + ILI_ERR("Can't find flash id(0x%x), ignore protection\n", ilits->flash_mid); + break; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); +} + +static int ilitek_fw_check_ddi_chunk(u8 *pfw) +{ + int ret = 0; + u8 cmd[7] = {0x0}; + u32 start = 0x0, end = 0x0, len = 0x0; + bool hex_info = ilits->info_from_hex; + + if (!fbi[DDI].start || !fbi[DDI].end) + return UPDATE_PASS; + + /* To make sure if flash has data. */ + if (hex_info) + ilits->info_from_hex = DISABLE; + + if (ili_ic_get_fw_ver() < 0) { + ILI_INFO("Failed to get fw ver, reading flash by dma instead\n"); + goto dma; + } + + /* Command for getting ddi info from firmware. */ + cmd[0] = CMD_GET_FLASH_DATA; //cmd + cmd[1] = DDI_RSV_BK_SIZE & 0xFF; //len L byte + cmd[2] = (DDI_RSV_BK_SIZE >> 8) & 0xFF; //len H byte + + /* Read customer reserved block */ + start = DDI_RSV_BK_ST_ADDR; + cmd[3] = (start & 0xFF); //addr L byte + cmd[4] = (start >> 8) & 0xFF ; //addr M byte + cmd[5] = (start >> 16) & 0xFF; //addr H byte + cmd[6] = ili_calc_packet_checksum(cmd, sizeof(cmd) - 1); + ILI_INFO("Read customer reserved block: start addr = 0x%X, len = 0x%X ", start, DDI_RSV_BK_SIZE); + if (ilits->wrapper(cmd, sizeof(cmd), fw_flash_tmp, sizeof(fw_flash_tmp), OFF, ON) < 0) { + ILI_ERR("Failed to read customer reserved block data from fw\n"); + goto dma; + } + ipio_memcpy(&pfw[start], fw_flash_tmp + 6, DDI_RSV_BK_SIZE, MAX_HEX_FILE_SIZE - start); + + /* Read mp data block */ + start = DDI_RSV_BK_END_ADDR - DDI_RSV_BK_SIZE + 1; + cmd[3] = (start & 0xFF); //addr L byte + cmd[4] = (start >> 8) & 0xFF ; //addr M byte + cmd[5] = (start >> 16) & 0xFF; //addr H byte + cmd[6] = ili_calc_packet_checksum(cmd, sizeof(cmd) - 1); + ILI_INFO("Read mp data block: start addr = 0x%X, len = 0x%X ", start, DDI_RSV_BK_SIZE); + if (ilits->wrapper(cmd, sizeof(cmd), fw_flash_tmp, sizeof(fw_flash_tmp), OFF, ON) < 0) { + ILI_ERR("Failed to read mp data block from fw\n"); + goto dma; + } + ipio_memcpy(&pfw[start], fw_flash_tmp + 6, DDI_RSV_BK_SIZE, MAX_HEX_FILE_SIZE- start); + + goto out; + +dma: + ILI_INFO("Obtain block data from dma\n"); + + ili_ice_mode_ctrl(ENABLE, OFF); + + /* Read customer reserved block */ + start = DDI_RSV_BK_ST_ADDR; + end = DDI_RSV_BK_ST_ADDR + DDI_RSV_BK_SIZE - 1; + len = DDI_RSV_BK_SIZE; + if (ilitek_tddi_fw_read_flash_data(start, end, fw_flash_tmp, len) < 0) { + ILI_ERR("Failed to read customer reserved block data from dma\\n"); + ret = UPDATE_FAIL; + goto out; + } + ipio_memcpy(&pfw[start], fw_flash_tmp + 6, DDI_RSV_BK_SIZE, MAX_HEX_FILE_SIZE - start); + + /* Read mp data block */ + start = DDI_RSV_BK_END_ADDR - DDI_RSV_BK_SIZE + 1; + end = DDI_RSV_BK_END_ADDR; + len = DDI_RSV_BK_SIZE; + if (ilitek_tddi_fw_read_flash_data(start, end, fw_flash_tmp, len) < 0) { + ILI_ERR("Failed to read mp data block from dma\n"); + ret = UPDATE_FAIL; + goto out; + } + ipio_memcpy(&pfw[start], fw_flash_tmp + 6, DDI_RSV_BK_SIZE, MAX_HEX_FILE_SIZE - start); + + ili_ice_mode_ctrl(DISABLE, OFF); +out: + /* + * Since there're two blocks combined with DDI chunk togeter, + * we need to extend the addresses where are going to upgrade. + */ + fbi[DDI].start = DDI_RSV_BK_ST_ADDR; + fbi[DDI].end = DDI_RSV_BK_END_ADDR; + tfd.end_addr = DDI_RSV_BK_END_ADDR; + + if (hex_info) + ilits->info_from_hex = ENABLE; + + atomic_set(&ilits->cmd_int_check, DISABLE); + return ret; +} + +static int ilitek_tddi_flash_fw_crc_check(u8 *pfw, bool check_fw_ver) +{ + int i, len = 0, crc_byte_len = 4; + u8 flash_crc[4] = {0}; + u32 start_addr = 0, end_addr = 0; + u32 hw_crc[FW_BLOCK_INFO_NUM]; + u32 flash_crc_cb = 0, hex_crc = 0; + + /* Check Flash and HW CRC */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + start_addr = fbi[i].start; + end_addr = fbi[i].end; + + /* Invaild end address */ + if (end_addr == 0) + continue; + + if (ilitek_tddi_fw_read_flash_data(end_addr - crc_byte_len + 1, end_addr, + flash_crc, sizeof(flash_crc)) < 0) { + ILI_ERR("Read Flash failed\n"); + return UPDATE_FAIL; + } + + flash_crc_cb = flash_crc[0] << 24 | flash_crc[1] << 16 | flash_crc[2] << 8 | flash_crc[3]; + + hw_crc[i] = ili_fw_read_hw_crc(start_addr, end_addr - start_addr - crc_byte_len + 1); + + ILI_INFO("Block = %d, HW CRC = 0x%06x, Flash CRC = 0x%06x\n", i, hw_crc[i], flash_crc_cb); + + /* Compare Flash CRC with HW CRC */ + if (flash_crc_cb != hw_crc[i]) { + ILI_INFO("HW and Flash CRC not matched\n"); + return UPDATE_FAIL; + } + memset(flash_crc, 0, sizeof(flash_crc)); + } + + if (check_fw_ver) { + /* Check FW version */ + ILI_INFO("New FW ver = 0x%x, Current FW ver = 0x%x\n", tfd.new_fw_cb, ilits->chip->fw_ver); + if (tfd.new_fw_cb != ilits->chip->fw_ver) { + ILI_INFO("FW version not matched\n"); + return UPDATE_FAIL; + } + } + + + /* Check Hex and HW CRC */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].end == 0) + continue; + + len = fbi[i].end - fbi[i].start + 1 - 4; + hex_crc = CalculateCRC32(fbi[i].start, len, pfw); + ILI_INFO("Block = %d, HW CRC = 0x%06x, Hex CRC = 0x%06x\n", i, hw_crc[i], hex_crc); + if (hex_crc != hw_crc[i]) { + ILI_ERR("Hex and HW CRC not matched\n"); + return UPDATE_FAIL; + } + } + + ILI_INFO("Flash FW is the same as targe file FW\n"); + return UPDATE_PASS; +} + +static int ilitek_tddi_fw_flash_program(u8 *pfw) +{ + u8 buf[512] = {0}; + u32 i = 0, j = 0, addr = 0, k = 0, recv_addr = 0; + int page = ilits->program_page; + bool skip = true; + + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].end == 0) + continue; + + ILI_INFO("Block[%d]: Programing from (0x%x) to (0x%x), tfd.end_addr = 0x%x\n", + i, fbi[i].start, fbi[i].end, tfd.end_addr); + + for (addr = fbi[i].start; addr < fbi[i].end; addr += page) { + buf[0] = 0x25; + buf[3] = 0x04; + buf[2] = 0x10; + buf[1] = 0x08; + + for (k = 0; k < page; k++) { + if (addr + k <= tfd.end_addr) + buf[4 + k] = pfw[addr + k]; + else + buf[4 + k] = 0xFF; + + if (buf[4 + k] != 0xFF) + skip = false; + } + + if (skip) { + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + return UPDATE_FAIL; + } + + ilitek_tddi_flash_write_enable(); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x2, 1) < 0) + ILI_ERR("Write 0x2 failed\n"); + + recv_addr = ((addr & 0xFF0000) >> 16) | (addr & 0x00FF00) | ((addr & 0x0000FF) << 16); + if (ili_ice_mode_write(FLASH2_ADDR, recv_addr, 3) < 0) + ILI_ERR("Write address failed\n"); + + if (ilits->wrapper(buf, page + 4, NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Failed to program data at start_addr = 0x%X, k = 0x%X, addr = 0x%x\n", + addr, k, addr + k); + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + return UPDATE_FAIL; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + + if (ilits->flash_mid == 0xEF) { + mdelay(1); + } else { + if (ilitek_tddi_flash_poll_busy(TIMEOUT_PROGRAM) < 0) + return UPDATE_FAIL; + } + if (ilits->fw_update_stat != ((j * 100) / tfd.end_addr)) { + ilits->fw_update_stat = (j * 100) / tfd.end_addr; + ILI_DBG("Program flahse data .... %d%c", ilits->fw_update_stat, '%'); + } + } + } + return UPDATE_PASS; +} + +static int ilitek_tddi_fw_flash_erase(void) +{ + int ret = 0; + u32 i = 0, addr = 0, recv_addr = 0; + bool bk_erase = false; + + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].end == 0) + continue; + + ILI_INFO("Block[%d]: Erasing from (0x%x) to (0x%x) \n", i, fbi[i].start, fbi[i].end); + + for (addr = fbi[i].start; addr <= fbi[i].end; addr += ilits->flash_sector) { + ilitek_tddi_flash_write_enable(); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (addr == fbi[AP].start) { + /* block erase with 64k bytes. */ + if (ili_ice_mode_write(FLASH2_ADDR, 0xD8, 1) < 0) + ILI_ERR("Write 0xB at %x failed\n", FLASH2_ADDR); + bk_erase = true; + } else { + /* sector erase with 4k bytes. */ + if (ili_ice_mode_write(FLASH2_ADDR, 0x20, 1) < 0) + ILI_ERR("Write 0x20 at %x failed\n", FLASH2_ADDR); + } + + recv_addr = ((addr & 0xFF0000) >> 16) | (addr & 0x00FF00) | ((addr & 0x0000FF) << 16); + if (ili_ice_mode_write(FLASH2_ADDR, recv_addr, 3) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + + /* Waitint for flash setting ready */ + mdelay(1); + + if (addr == fbi[AP].start) + ret = ilitek_tddi_flash_poll_busy(TIMEOUT_PAGE); + else + ret = ilitek_tddi_flash_poll_busy(TIMEOUT_SECTOR); + + if (ret < 0) + return UPDATE_FAIL; + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + + if (i == AP && tfd.is80k && bk_erase) { + addr = 0x10000 - 0x1000; + bk_erase = false; + } + } + } + return UPDATE_PASS; +} + +static int ilitek_fw_flash_upgrade(u8 *pfw) +{ + int ret = UPDATE_PASS; + if (ili_reset_ctrl(ilits->reset) < 0) { + ILI_ERR("TP reset failed during flash progam\n"); + return -EFW_REST; + } + + ilitek_fw_check_ddi_chunk(pfw); + + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) + return -EFW_ICE_MODE; + + ret = ilitek_tddi_flash_fw_crc_check(pfw, true); + if (ret == UPDATE_PASS) { + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) { + ILI_ERR("Disable ice mode failed, call reset instead\n"); + if (ili_reset_ctrl(ilits->reset) < 0) { + ILI_ERR("TP reset failed during flash progam\n"); + return -EFW_REST; + } + return UPDATE_PASS; + } + return UPDATE_PASS; + } else { + ILI_INFO("Flash FW and target file FW are different, do upgrade\n"); + } + + ret = ilitek_tddi_fw_flash_erase(); + if (ret == UPDATE_FAIL) + return -EFW_ERASE; + + ret = ilitek_tddi_fw_flash_program(pfw); + if (ret == UPDATE_FAIL) + return -EFW_PROGRAM; + + ret = ilitek_tddi_flash_fw_crc_check(pfw, false); + if (ret == UPDATE_FAIL) + return -EFW_CRC; + + /* We do have to reset chip in order to move new code from flash to iram. */ + if (ili_reset_ctrl(ilits->reset) < 0) { + ILI_ERR("TP reset failed after flash progam\n"); + ret = -EFW_REST; + } + return ret; +} + +static int ilitek_fw_calc_file_crc(u8 *pfw) +{ + int i; + u32 ex_addr, data_crc, file_crc; + + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].end == 0) + continue; + ex_addr = fbi[i].end; + data_crc = CalculateCRC32(fbi[i].start, fbi[i].len - 4, pfw); + file_crc = pfw[ex_addr - 3] << 24 | pfw[ex_addr - 2] << 16 | pfw[ex_addr - 1] << 8 | pfw[ex_addr]; + ILI_DBG("data crc = %x, file crc = %x\n", data_crc, file_crc); + if (data_crc != file_crc) { + ILI_ERR("Content of fw file is broken. (%d, %x, %x)\n", + i, data_crc, file_crc); + return -1; + } + } + + ILI_INFO("Content of fw file is correct\n"); + return 0; +} + +static void ilitek_tddi_fw_update_block_info(u8 *pfw) +{ + u32 fw_info_addr = 0, fw_mp_ver_addr = 0; + + fbi[AP].name = "AP"; + fbi[DATA].name = "DATA"; + fbi[TUNING].name = "TUNING"; + fbi[MP].name = "MP"; + fbi[GESTURE].name = "GESTURE"; + + /* upgrade mode define */ + fbi[DATA].mode = fbi[AP].mode = fbi[TUNING].mode = AP; + fbi[MP].mode = MP; + fbi[GESTURE].mode = GESTURE; + + if (fbi[AP].end > (64*K)) + tfd.is80k = true; + + /* Copy fw info */ + fw_info_addr = fbi[AP].end - INFO_HEX_ST_ADDR; + ILI_INFO("Parsing hex info start addr = 0x%x\n", fw_info_addr); + ipio_memcpy(ilits->fw_info, (pfw + fw_info_addr), sizeof(ilits->fw_info), sizeof(ilits->fw_info)); + + /* copy fw mp ver */ + fw_mp_ver_addr = fbi[MP].end - INFO_MP_HEX_ADDR; + ILI_INFO("Parsing hex mp ver addr = 0x%x\n", fw_mp_ver_addr); + ipio_memcpy(ilits->fw_mp_ver, pfw + fw_mp_ver_addr, sizeof(ilits->fw_mp_ver), sizeof(ilits->fw_mp_ver)); + + /* copy fw core ver */ + ilits->chip->core_ver = (ilits->fw_info[68] << 24) | (ilits->fw_info[69] << 16) | + (ilits->fw_info[70] << 8) | ilits->fw_info[71]; + ILI_INFO("New FW Core version = %x\n", ilits->chip->core_ver); + + /* Get hex fw vers */ + tfd.new_fw_cb = (ilits->fw_info[48] << 24) | (ilits->fw_info[49] << 16) | + (ilits->fw_info[50] << 8) | ilits->fw_info[51]; + + /* Calculate update address */ + ILI_INFO("New FW ver = 0x%x\n", tfd.new_fw_cb); + ILI_INFO("star_addr = 0x%06X, end_addr = 0x%06X, Block Num = %d\n", tfd.start_addr, tfd.end_addr, tfd.block_number); +} + +static int ilitek_tddi_fw_ili_convert(u8 *pfw) +{ + int i, size, blk_num = 0, blk_map = 0, num = 0; + int b0_addr = 0, b0_num = 0; + + if (ERR_ALLOC_MEM(ilits->md_fw_ili)) + return -ENOMEM; + + CTPM_FW = ilits->md_fw_ili; + size = ilits->md_fw_ili_size; + + if (size < ILI_FILE_HEADER || size > MAX_HEX_FILE_SIZE) { + ILI_ERR("size of ILI file is invalid\n"); + return -EINVAL; + } + + /* Check if it's old version of ILI format. */ + if (CTPM_FW[22] == 0xFF && CTPM_FW[23] == 0xFF && + CTPM_FW[24] == 0xFF && CTPM_FW[25] == 0xFF) { + ILI_ERR("Invaild ILI format, abort!\n"); + return -EINVAL; + } + + blk_num = CTPM_FW[131]; + blk_map = (CTPM_FW[129] << 8) | CTPM_FW[130]; + ILI_INFO("Parsing ILI file, block num = %d, block mapping = %x\n", blk_num, blk_map); + + if (blk_num > (FW_BLOCK_INFO_NUM - 1) || !blk_num || !blk_map) { + ILI_ERR("Number of block or block mapping is invalid, abort!\n"); + return -EINVAL; + } + + memset(fbi, 0x0, sizeof(fbi)); + tfd.start_addr = 0; + tfd.end_addr = 0; + tfd.hex_tag = BLOCK_TAG_AF; + + /* Parsing block info */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + /* B0 tag */ + b0_addr = (CTPM_FW[4 + i * 4] << 16) | (CTPM_FW[5 + i * 4] << 8) | (CTPM_FW[6 + i * 4]); + b0_num = CTPM_FW[7 + i * 4]; + if ((b0_num != 0) && (b0_addr != 0x000000)) + fbi[b0_num].fix_mem_start = b0_addr; + + /* AF tag */ + num = i + 1; + if (((blk_map >> i) & 0x01) == 0x01) { + fbi[num].start = (CTPM_FW[132 + i * 6] << 16) | (CTPM_FW[133 + i * 6] << 8) | CTPM_FW[134 + i * 6]; + fbi[num].end = (CTPM_FW[135 + i * 6] << 16) | (CTPM_FW[136 + i * 6] << 8) | CTPM_FW[137 + i * 6]; + + if (fbi[num].fix_mem_start == 0) + fbi[num].fix_mem_start = INT_MAX; + + fbi[num].len = fbi[num].end - fbi[num].start + 1; + ILI_INFO("Block[%d]: start_addr = %x, end = %x, fix_mem_start = 0x%x\n", num, fbi[num].start, + fbi[num].end, fbi[num].fix_mem_start); + } + } + + memcpy(pfw, CTPM_FW + ILI_FILE_HEADER, size - ILI_FILE_HEADER); + + if (ilitek_fw_calc_file_crc(pfw) < 0) + return -1; + + tfd.block_number = blk_num; + tfd.end_addr = size - ILI_FILE_HEADER; + return 0; +} + +static int ilitek_tddi_fw_hex_convert(u8 *phex, int size, u8 *pfw) +{ + int block = 0; + u32 i = 0, j = 0, k = 0, num = 0; + u32 len = 0, addr = 0, type = 0; + u32 start_addr = 0x0, end_addr = 0x0, ex_addr = 0; + u32 offset; + + memset(fbi, 0x0, sizeof(fbi)); + + /* Parsing HEX file */ + for (; i < size;) { + len = HexToDec(&phex[i + 1], 2); + addr = HexToDec(&phex[i + 3], 4); + type = HexToDec(&phex[i + 7], 2); + + if (type == 0x04) { + ex_addr = HexToDec(&phex[i + 9], 4); + } else if (type == 0x02) { + ex_addr = HexToDec(&phex[i + 9], 4); + ex_addr = ex_addr >> 12; + } else if (type == BLOCK_TAG_AF) { + /* insert block info extracted from hex */ + tfd.hex_tag = type; + if (tfd.hex_tag == BLOCK_TAG_AF) + num = HexToDec(&phex[i + 9 + 6 + 6], 2); + else + num = 0xFF; + + if (num > (FW_BLOCK_INFO_NUM - 1)) { + ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n", + num, FW_BLOCK_INFO_NUM - 1); + return -EINVAL; + } + + fbi[num].start = HexToDec(&phex[i + 9], 6); + fbi[num].end = HexToDec(&phex[i + 9 + 6], 6); + fbi[num].fix_mem_start = INT_MAX; + fbi[num].len = fbi[num].end - fbi[num].start + 1; + ILI_INFO("Block[%d]: start_addr = %x, end = %x", num, fbi[num].start, fbi[num].end); + + block++; + } else if (type == BLOCK_TAG_B0 && tfd.hex_tag == BLOCK_TAG_AF) { + num = HexToDec(&phex[i + 9 + 6], 2); + + if (num > (FW_BLOCK_INFO_NUM - 1)) { + ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n", + num, FW_BLOCK_INFO_NUM - 1); + return -EINVAL; + } + + fbi[num].fix_mem_start = HexToDec(&phex[i + 9], 6); + ILI_INFO("Tag 0xB0: change Block[%d] to addr = 0x%x\n", num, fbi[num].fix_mem_start); + } + + addr = addr + (ex_addr << 16); + + if (phex[i + 1 + 2 + 4 + 2 + (len * 2) + 2] == 0x0D) + offset = 2; + else + offset = 1; + + if (addr > MAX_HEX_FILE_SIZE) { + ILI_ERR("Invalid hex format %d\n", addr); + return -1; + } + + if (type == 0x00) { + end_addr = addr + len; + if (addr < start_addr) + start_addr = addr; + /* fill data */ + for (j = 0, k = 0; j < (len * 2); j += 2, k++) + pfw[addr + k] = HexToDec(&phex[i + 9 + j], 2); + } + i += 1 + 2 + 4 + 2 + (len * 2) + 2 + offset; + } + + if (ilitek_fw_calc_file_crc(pfw) < 0) + return -1; + + tfd.start_addr = start_addr; + tfd.end_addr = end_addr; + tfd.block_number = block; + return 0; +} + +static int ilitek_tdd_fw_hex_open(u8 op, u8 *pfw) +{ + int ret =0, fsize = 0; + const struct firmware *fw = NULL; + struct file *f = NULL; + mm_segment_t old_fs; + loff_t pos = 0; + + ILI_INFO("Open file method = %s, path = %s\n", + op ? "FILP_OPEN" : "REQUEST_FIRMWARE", + op ? ilits->md_fw_filp_path : ilits->md_fw_rq_path); + + switch (op) { + case REQUEST_FIRMWARE: + if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) { + ILI_ERR("Request firmware failed, try again\n"); + if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) { + ILI_ERR("Request firmware failed after retry\n"); + ret = -1; + goto out; + } + } + + fsize = fw->size; + ILI_INFO("fsize = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is zero\n"); + release_firmware(fw); + ret = -1; + goto out; + } + + ilits->tp_fw.size = 0; + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n"); + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw after retry\n"); + release_firmware(fw); + ret = -ENOMEM; + goto out; + } + } + + /* Copy fw data got from request_firmware to global */ + ipio_memcpy((u8 *)ilits->tp_fw.data, fw->data, fsize * sizeof(*fw->data), fsize); + ilits->tp_fw.size = fsize; + release_firmware(fw); + break; + case FILP_OPEN: + f = filp_open(ilits->md_fw_filp_path, O_RDONLY, 0644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file at %ld\n", PTR_ERR(f)); + ret = -1; + goto out; + } + + fsize = f->f_inode->i_size; + ILI_INFO("fsize = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is invaild\n"); + filp_close(f, NULL); + ret = -1; + goto out; + } + + ipio_vfree((void **)&(ilits->tp_fw.data)); + ilits->tp_fw.size = 0; + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n"); + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw after retry\n"); + filp_close(f, NULL); + ret = -ENOMEM; + goto out; + } + } + + /* ready to map user's memory to obtain data by reading files */ + old_fs = get_fs(); + set_fs(get_ds()); + set_fs(KERNEL_DS); + pos = 0; + vfs_read(f, (u8 *)ilits->tp_fw.data, fsize, &pos); + set_fs(old_fs); + filp_close(f, NULL); + ilits->tp_fw.size = fsize; + break; + default: + ILI_ERR("Unknown open file method, %d\n", op); + break; + } + + if (ERR_ALLOC_MEM(ilits->tp_fw.data) || ilits->tp_fw.size <= 0) { + ILI_ERR("fw data/size is invaild\n"); + ret = -1; + goto out; + } + + /* Convert hex and copy data from tp_fw.data to pfw */ + if (ilitek_tddi_fw_hex_convert((u8 *)ilits->tp_fw.data, ilits->tp_fw.size, pfw) < 0) { + ILI_ERR("Convert hex file failed\n"); + ret = -1; + } + +out: + ipio_vfree((void **)&(ilits->tp_fw.data)); + return ret; +} + +int ili_fw_upgrade(int op) +{ + int i, ret = 0, retry = 3; + + if (!ilits->boot || ilits->force_fw_update || ERR_ALLOC_MEM(pfw)) { + if (ERR_ALLOC_MEM(pfw)) { + ipio_vfree((void **)&pfw); + pfw = vmalloc(MAX_HEX_FILE_SIZE * sizeof(u8)); + if (ERR_ALLOC_MEM(pfw)) { + ILI_ERR("Failed to allocate pfw memory, %ld\n", PTR_ERR(pfw)); + ipio_vfree((void **)&pfw); + ret = -ENOMEM; + goto out; + } + } + + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + pfw[i] = 0xFF; + + if (ilitek_tdd_fw_hex_open(op, pfw) < 0) { + ILI_ERR("Open hex file fail, try upgrade from ILI file\n"); + + /* + * Users might not be aware of a broken hex file when recovering + * fw from ILI file. We should force them to check + * hex files they attempt to update via device node. + */ + if (ilits->node_update) { + ILI_ERR("Ignore update from ILI file\n"); + ipio_vfree((void **)&pfw); + return -EFW_CONVERT_FILE; + } + + if (ilitek_tddi_fw_ili_convert(pfw) < 0) { + ILI_ERR("Convert ILI file error\n"); + ret = -EFW_CONVERT_FILE; + goto out; + } + } + ilitek_tddi_fw_update_block_info(pfw); + } + + do { + ret = ilitek_fw_flash_upgrade(pfw); + if (ret == UPDATE_PASS) + break; + + ILI_ERR("Upgrade failed, do retry!\n"); + } while (--retry > 0); + + if (ret != UPDATE_PASS) { + ILI_ERR("Failed to upgrade fw %d times, erasing flash\n", retry); + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed while erasing flash\n"); + if (ilitek_tddi_fw_flash_erase() < 0) + ILI_ERR("Failed to erase flash\n"); + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Disable ice mode failed after erase flash\n"); + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP reset failed after erase flash\n"); + } + +out: + if (ret < 0) + ilits->info_from_hex = DISABLE; + + if (atomic_read(&ilits->ice_stat)) + ili_ice_mode_ctrl(DISABLE, OFF); + + ili_ic_get_core_ver(); + ili_ic_get_protocl_ver(); + ili_ic_get_fw_ver(); + ili_ic_get_tp_info(); + ili_ic_get_panel_info(); + + if (!ilits->info_from_hex) + ilits->info_from_hex = ENABLE; + + return ret; +} + +struct flash_table { + u16 mid; + u16 dev_id; + int mem_size; + int program_page; + int sector; +} flash_t[] = { + [0] = {0x00, 0x0000, (256 * K), 256, (4 * K)}, /* Default */ + [1] = {0xEF, 0x6011, (128 * K), 256, (4 * K)}, /* W25Q10EW */ + [2] = {0xEF, 0x6012, (256 * K), 256, (4 * K)}, /* W25Q20EW */ + [3] = {0xC8, 0x6012, (256 * K), 256, (4 * K)}, /* GD25LQ20B */ + [4] = {0xC8, 0x6013, (512 * K), 256, (4 * K)}, /* GD25LQ40 */ + [5] = {0x85, 0x6013, (4 * M), 256, (4 * K)}, + [6] = {0xC2, 0x2812, (256 * K), 256, (4 * K)}, + [7] = {0x1C, 0x3812, (256 * K), 256, (4 * K)}, +}; + +void ili_fw_read_flash_info(void) +{ + int i = 0; + u8 buf[4] = {0}; + u8 cmd = 0x9F; + u32 tmp = 0; + u16 flash_id = 0, flash_mid = 0; + bool ice = atomic_read(&ilits->ice_stat); + + if (!ice) { + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed while reading flash info\n"); + } + + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_rx_dual, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_rx_dual, FLASH0_ADDR); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, cmd, 1) < 0) + ILI_ERR("Write 0x9F failed\n"); + + for (i = 0; i < ARRAY_SIZE(buf); i++) { + if (ili_ice_mode_write(FLASH2_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + if (ili_ice_mode_read(FLASH4_ADDR, &tmp, sizeof(u8)) < 0) + ILI_ERR("Read flash info error\n"); + + buf[i] = tmp; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); /* CS high */ + + flash_mid = buf[0]; + flash_id = buf[1] << 8 | buf[2]; + + for (i = 0; i < ARRAY_SIZE(flash_t); i++) { + if (flash_mid == flash_t[i].mid && flash_id == flash_t[i].dev_id) { + ilits->flash_mid = flash_t[i].mid; + ilits->flash_devid = flash_t[i].dev_id; + ilits->program_page = flash_t[i].program_page; + ilits->flash_sector = flash_t[i].sector; + break; + } + } + + if (i >= ARRAY_SIZE(flash_t)) { + ILI_INFO("Not found flash id in tab, use default\n"); + ilits->flash_mid = flash_t[0].mid; + ilits->flash_devid = flash_t[0].dev_id; + ilits->program_page = flash_t[0].program_page; + ilits->flash_sector = flash_t[0].sector; + } + + ILI_INFO("Flash MID = %x, Flash DEV_ID = %x\n", ilits->flash_mid, ilits->flash_devid); + ILI_INFO("Flash program page = %d\n", ilits->program_page); + ILI_INFO("Flash sector = %d\n", ilits->flash_sector); + + ilitek_tddi_flash_protect(DISABLE); + + if (!ice) { + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Disable ice mode failed while reading flash info\n"); + } +} diff --git a/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_hostdl.c b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_hostdl.c new file mode 100644 index 000000000000..b5add0ef33c3 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_hostdl.c @@ -0,0 +1,817 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek.h" + +#define UPDATE_PASS 0 +#define UPDATE_FAIL -1 +#define TIMEOUT_SECTOR 500 +#define TIMEOUT_PAGE 3500 +#define TIMEOUT_PROGRAM 10 + +static struct touch_fw_data { + u8 block_number; + u32 start_addr; + u32 end_addr; + u32 new_fw_cb; + int delay_after_upgrade; + bool isCRC; + bool isboot; + bool is80k; + int hex_tag; +} tfd; + +static struct flash_block_info { + char *name; + u32 start; + u32 end; + u32 len; + u32 mem_start; + u32 fix_mem_start; + u8 mode; +} fbi[FW_BLOCK_INFO_NUM]; + +static u8 *pfw; + +static u32 HexToDec(char *phex, s32 len) +{ + u32 ret = 0, temp = 0, i; + s32 shift = (len - 1) * 4; + + for (i = 0; i < len; shift -= 4, i++) { + if ((phex[i] >= '0') && (phex[i] <= '9')) + temp = phex[i] - '0'; + else if ((phex[i] >= 'a') && (phex[i] <= 'f')) + temp = (phex[i] - 'a') + 10; + else if ((phex[i] >= 'A') && (phex[i] <= 'F')) + temp = (phex[i] - 'A') + 10; + else + return -1; + + ret |= (temp << shift); + } + return ret; +} + +static int CalculateCRC32(u32 start_addr, u32 len, u8 *pfw) +{ + int i = 0, j = 0; + int crc_poly = 0x04C11DB7; + int tmp_crc = 0xFFFFFFFF; + + for (i = start_addr; i < start_addr + len; i++) { + tmp_crc ^= (pfw[i] << 24); + + for (j = 0; j < 8; j++) { + if ((tmp_crc & 0x80000000) != 0) + tmp_crc = tmp_crc << 1 ^ crc_poly; + else + tmp_crc = tmp_crc << 1; + } + } + return tmp_crc; +} + +static int calc_hw_dma_crc(u32 start_addr, u32 block_size) +{ + int count = 50; + u32 busy = 0; + + /* dma1 src1 address */ + if (ili_ice_mode_write(0x072104, start_addr, 4) < 0) + ILI_ERR("Write dma1 src1 address failed\n"); + /* dma1 src1 format */ + if (ili_ice_mode_write(0x072108, 0x80000001, 4) < 0) + ILI_ERR("Write dma1 src1 format failed\n"); + /* dma1 dest address */ + if (ili_ice_mode_write(0x072114, 0x0002725C, 4) < 0) + ILI_ERR("Write dma1 src1 format failed\n"); + /* dma1 dest format */ + if (ili_ice_mode_write(0x072118, 0x80000000, 4) < 0) + ILI_ERR("Write dma1 dest format failed\n"); + /* Block size*/ + if (ili_ice_mode_write(0x07211C, block_size, 4) < 0) + ILI_ERR("Write block size (%d) failed\n", block_size); + /* crc off */ + if (ili_ice_mode_write(0x041016, 0x00, 1) < 0) + ILI_INFO("Write crc of failed\n"); + /* dma crc */ + if (ili_ice_mode_write(0x041017, 0x03, 1) < 0) + ILI_ERR("Write dma 1 crc failed\n"); + /* Dma1 stop */ + if (ili_ice_mode_write(0x072100, 0x02000000, 4) < 0) + ILI_ERR("Write dma1 stop failed\n"); + /* crc on */ + if (ili_ice_mode_write(0x041016, 0x01, 1) < 0) + ILI_ERR("Write crc on failed\n"); + /* clr int */ + if (ili_ice_mode_write(0x048006, 0x2, 1) < 0) + ILI_ERR("Write clr int failed\n"); + /* Dma1 start */ + if (ili_ice_mode_write(0x072100, 0x01000000, 4) < 0) + ILI_ERR("Write dma1 start failed\n"); + + /* Polling BIT0 */ + while (count > 0) { + mdelay(1); + if (ili_ice_mode_read(0x048006, &busy, sizeof(u8)) < 0) + ILI_ERR("Read busy error\n"); + ILI_DBG("busy = %x\n", busy); + if ((busy & 0x02) == 2) + break; + count--; + } + + if (count <= 0) { + ILI_ERR("BIT0 is busy\n"); + return -1; + } + + if (ili_ice_mode_read(0x04101C, &busy, sizeof(u32)) < 0) { + ILI_ERR("Read dma crc error\n"); + return -1; + } + return busy; +} + +static int ilitek_tddi_fw_iram_read(u8 *buf, u32 start, int len) +{ + int limit = SPI_RX_BUF_SIZE; + int addr = 0, loop = 0, tmp_len = len, cnt = 0; + u8 cmd[4] = {0}; + + if (!buf) { + ILI_ERR("buf is null\n"); + return -ENOMEM; + } + + if (len % limit) + loop = (len / limit) + 1; + else + loop = len / limit; + + for (cnt = 0, addr = start; cnt < loop; cnt++, addr += limit) { + if (tmp_len > limit) + tmp_len = limit; + + cmd[0] = 0x25; + cmd[3] = (char)((addr & 0x00FF0000) >> 16); + cmd[2] = (char)((addr & 0x0000FF00) >> 8); + cmd[1] = (char)((addr & 0x000000FF)); + + if (ilits->wrapper(cmd, 4, NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Failed to write iram data\n"); + return -ENODEV; + } + + if (ilits->wrapper(NULL, 0, buf + cnt * limit, tmp_len, OFF, OFF) < 0) { + ILI_ERR("Failed to Read iram data\n"); + return -ENODEV; + } + + tmp_len = len - cnt * limit; + ilits->fw_update_stat = ((len - tmp_len) * 100) / len; + ILI_INFO("Reading iram data .... %d%c", ilits->fw_update_stat, '%'); + } + return 0; +} + +int ili_fw_dump_iram_data(u32 start, u32 end, bool save) +{ + struct file *f = NULL; + mm_segment_t old_fs; + loff_t pos = 0; + int i, ret = 0; + int len, tmp = debug_en; + bool ice = atomic_read(&ilits->ice_stat); + + if (!ice) { + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) { + ILI_ERR("Enable ice mode failed\n"); + return ret; + } + } + + len = end - start + 1; + + if (len > MAX_HEX_FILE_SIZE) { + ILI_ERR("len is larger than buffer, abort\n"); + ret = -EINVAL; + goto out; + } + + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + ilits->update_buf[i] = 0xFF; + + ret = ilitek_tddi_fw_iram_read(ilits->update_buf, start, len); + if (ret < 0) { + ILI_ERR("Read IRAM data failed\n"); + goto out; + } + + if (save) { + f = filp_open(DUMP_IRAM_PATH, O_WRONLY | O_CREAT | O_TRUNC, 644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file at %ld.\n", PTR_ERR(f)); + ret = -ENOMEM; + goto out; + } + + old_fs = get_fs(); + set_fs(get_ds()); + set_fs(KERNEL_DS); + pos = 0; + vfs_write(f, ilits->update_buf, len, &pos); + set_fs(old_fs); + filp_close(f, NULL); + ILI_INFO("Save iram data to %s\n", DUMP_IRAM_PATH); + } else { + debug_en = DEBUG_ALL; + ili_dump_data(ilits->update_buf, 8, len, 0, "IRAM"); + debug_en = tmp; + } + +out: + if (!ice) { + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed after code reset\n"); + } + + ILI_INFO("Dump IRAM %s\n", (ret < 0) ? "FAIL" : "SUCCESS"); + return ret; +} + +static int ilitek_tddi_fw_iram_program(u32 start, u8 *w_buf, u32 w_len, u32 split_len) +{ + int i = 0, j = 0, addr = 0; + u32 end = start + w_len; + bool fix_4_alignment = false; + + if (split_len % 4 > 0) + ILI_ERR("Since split_len must be four-aligned, it must be a multiple of four"); + + if (split_len != 0) { + for (addr = start, i = 0; addr < end; addr += split_len, i += split_len) { + if ((addr + split_len) > end) { + split_len = end - addr; + if (split_len % 4 != 0) + fix_4_alignment = true; + } + + ilits->update_buf[0] = SPI_WRITE; + ilits->update_buf[1] = 0x25; + ilits->update_buf[2] = (char)((addr & 0x000000FF)); + ilits->update_buf[3] = (char)((addr & 0x0000FF00) >> 8); + ilits->update_buf[4] = (char)((addr & 0x00FF0000) >> 16); + + for (j = 0; j < split_len; j++) + ilits->update_buf[5 + j] = w_buf[i + j]; + + if (fix_4_alignment) { + ILI_INFO("org split_len = 0x%X\n", split_len); + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 4, ilits->update_buf[5 + split_len - 4]); + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 3, ilits->update_buf[5 + split_len - 3]); + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 2, ilits->update_buf[5 + split_len - 2]); + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 1, ilits->update_buf[5 + split_len - 1]); + for (j = 0; j < (4 - (split_len % 4)); j++) { + ilits->update_buf[5 + j + split_len] = 0xFF; + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n",j + split_len, ilits->update_buf[5 + j + split_len]); + } + + ILI_INFO("split_len %% 4 = %d\n", split_len % 4); + split_len = split_len + (4 - (split_len % 4)); + ILI_INFO("fix split_len = 0x%X\n", split_len); + } + if (ilits->spi_write_then_read(ilits->spi, ilits->update_buf, split_len + 5, NULL, 0)) { + ILI_ERR("Failed to write data via SPI in host download (%x)\n", split_len + 5); + return -EIO; + } + ilits->fw_update_stat = (i * 100) / w_len; + } + } else { + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + ilits->update_buf[i] = 0xFF; + + ilits->update_buf[0] = SPI_WRITE; + ilits->update_buf[1] = 0x25; + ilits->update_buf[2] = (char)((start & 0x000000FF)); + ilits->update_buf[3] = (char)((start & 0x0000FF00) >> 8); + ilits->update_buf[4] = (char)((start & 0x00FF0000) >> 16); + + memcpy(&ilits->update_buf[5], w_buf, w_len); + if (w_len % 4 != 0) { + ILI_INFO("org w_len = %d\n", w_len); + w_len = w_len + (4 - (w_len % 4)); + ILI_INFO("w_len = %d w_len %% 4 = %d\n", w_len, w_len % 4); + } + /* It must be supported by platforms that have the ability to transfer all data at once. */ + if (ilits->spi_write_then_read(ilits->spi, ilits->update_buf, w_len + 5, NULL, 0) < 0) { + ILI_ERR("Failed to write data via SPI in host download (%x)\n", w_len + 5); + return -EIO; + } + } + return 0; +} + +static int ilitek_tddi_fw_iram_upgrade(u8 *pfw) +{ + int i, ret = UPDATE_PASS; + u32 mode, crc, dma, iram_crc = 0; + u8 *fw_ptr = NULL, crc_temp[4], crc_len = 4; + bool iram_crc_err = false; + + if (!ilits->ddi_rest_done) { + if (ilits->actual_tp_mode != P5_X_FW_GESTURE_MODE) + ili_reset_ctrl(ilits->reset); + + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) + return -EFW_ICE_MODE; + } else { + /* Restore it if the wq of load_fw_ddi has been called. */ + ilits->ddi_rest_done = false; + } + + if (ilits->chip->dma_reset) { + ILI_INFO("operate dma reset in reg after tp reset\n"); + if (ili_ice_mode_write(0x40040, 0x00800000, 4) < 0) + ILI_INFO("Failed to open DMA reset\n"); + if (ili_ice_mode_write(0x40040, 0x00000000, 4) < 0) + ILI_INFO("Failed to close DMA reset\n"); + } + + /* Point to pfw with different addresses for getting its block data. */ + fw_ptr = pfw; + if (ilits->actual_tp_mode == P5_X_FW_TEST_MODE) { + + mode = MP; + } else if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) { + mode = GESTURE; + crc_len = 0; + } else { + mode = AP; + } + + /* Program data to iram acorrding to each block */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].mode == mode && fbi[i].len != 0) { + ILI_DBG("Download %s code from hex 0x%x to IRAM 0x%x, len = 0x%x\n", + fbi[i].name, fbi[i].start, fbi[i].mem_start, fbi[i].len); + +#if SPI_DMA_TRANSFER_SPLIT + if (ilitek_tddi_fw_iram_program(fbi[i].mem_start, (fw_ptr + fbi[i].start), fbi[i].len, SPI_UPGRADE_LEN) < 0) + ILI_ERR("IRAM program failed\n"); +#else + if (ilitek_tddi_fw_iram_program(fbi[i].mem_start, (fw_ptr + fbi[i].start), fbi[i].len, 0) < 0) + ILI_ERR("IRAM program failed\n"); +#endif + crc = CalculateCRC32(fbi[i].start, fbi[i].len - crc_len, fw_ptr); + dma = calc_hw_dma_crc(fbi[i].mem_start, fbi[i].len - crc_len); + + if (mode != GESTURE) { + ilitek_tddi_fw_iram_read(crc_temp, (fbi[i].mem_start + fbi[i].len - crc_len), sizeof(crc_temp)); + iram_crc = crc_temp[0] << 24 | crc_temp[1] << 16 |crc_temp[2] << 8 | crc_temp[3]; + if (iram_crc != dma) + iram_crc_err = true; + } + + ILI_INFO("%s CRC is %s hex(%x) : dma(%x) : iram(%x), calculation len is 0x%x\n", + fbi[i].name,((crc != dma)||(iram_crc_err)) ? "Invalid !" : "Correct !", crc, dma, iram_crc, fbi[i].len - crc_len); + + if ((crc != dma)|| iram_crc_err) { + ILI_ERR("CRC Error! print iram data with first 16 bytes\n"); + ili_fw_dump_iram_data(0x0, 0xF, false); + return -EFW_CRC; + } + } + } + + if (ilits->actual_tp_mode != P5_X_FW_GESTURE_MODE) { + if (ili_reset_ctrl(TP_IC_CODE_RST) < 0) { + ILI_ERR("TP Code reset failed during iram programming\n"); + ret = -EFW_REST; + return ret; + } + } + + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) { + ILI_ERR("Disable ice mode failed after code reset\n"); + ret = -EFW_ICE_MODE; + } + + /* Waiting for fw ready sending first cmd */ + if (!ilits->info_from_hex || (ilits->chip->core_ver < CORE_VER_1410)) + mdelay(100); + else + mdelay(20); + + return ret; +} + +static int ilitek_fw_calc_file_crc(u8 *pfw) +{ + int i; + u32 ex_addr, data_crc, file_crc; + + for (i = 0; i < ARRAY_SIZE(fbi); i++) { + if (fbi[i].end == 0) + continue; + ex_addr = fbi[i].end; + data_crc = CalculateCRC32(fbi[i].start, fbi[i].len - 4, pfw); + file_crc = pfw[ex_addr - 3] << 24 | pfw[ex_addr - 2] << 16 | pfw[ex_addr - 1] << 8 | pfw[ex_addr]; + ILI_DBG("data crc = %x, file crc = %x\n", data_crc, file_crc); + if (data_crc != file_crc) { + ILI_ERR("Content of fw file is broken. (%d, %x, %x)\n", + i, data_crc, file_crc); + return -1; + } + } + + ILI_INFO("Content of fw file is correct\n"); + return 0; +} + +static int ilitek_tddi_fw_update_block_info(u8 *pfw) +{ + u32 ges_area_section = 0, ges_info_addr = 0, ges_fw_start = 0, ges_fw_end = 0; + u32 ap_end = 0, ap_len = 0; + u32 fw_info_addr = 0, fw_mp_ver_addr = 0; + + if (tfd.hex_tag != BLOCK_TAG_AF) { + ILI_ERR("HEX TAG is invalid (0x%X)\n", tfd.hex_tag); + return -EINVAL; + } + + fbi[AP].mem_start = (fbi[AP].fix_mem_start != INT_MAX) ? fbi[AP].fix_mem_start : 0; + fbi[DATA].mem_start = (fbi[DATA].fix_mem_start != INT_MAX) ? fbi[DATA].fix_mem_start : DLM_START_ADDRESS; + fbi[TUNING].mem_start = (fbi[TUNING].fix_mem_start != INT_MAX) ? fbi[TUNING].fix_mem_start : fbi[DATA].mem_start + fbi[DATA].len; + fbi[MP].mem_start = (fbi[MP].fix_mem_start != INT_MAX) ? fbi[MP].fix_mem_start : 0; + fbi[GESTURE].mem_start = (fbi[GESTURE].fix_mem_start != INT_MAX) ? fbi[GESTURE].fix_mem_start : 0; + fbi[TAG].mem_start = (fbi[TAG].fix_mem_start != INT_MAX) ? fbi[TAG].fix_mem_start : 0; + fbi[PARA_BACKUP].mem_start = (fbi[PARA_BACKUP].fix_mem_start != INT_MAX) ? fbi[PARA_BACKUP].fix_mem_start : 0; + fbi[DDI].mem_start = (fbi[DDI].fix_mem_start != INT_MAX) ? fbi[DDI].fix_mem_start : 0; + + /* Parsing gesture info form AP code */ + ges_info_addr = (fbi[AP].end + 1 - 60); + ges_area_section = (pfw[ges_info_addr + 3] << 24) + (pfw[ges_info_addr + 2] << 16) + (pfw[ges_info_addr + 1] << 8) + pfw[ges_info_addr]; + fbi[GESTURE].mem_start = (pfw[ges_info_addr + 7] << 24) + (pfw[ges_info_addr + 6] << 16) + (pfw[ges_info_addr + 5] << 8) + pfw[ges_info_addr + 4]; + ap_end = (pfw[ges_info_addr + 11] << 24) + (pfw[ges_info_addr + 10] << 16) + (pfw[ges_info_addr + 9] << 8) + pfw[ges_info_addr + 8]; + + if (ap_end != fbi[GESTURE].mem_start) + ap_len = ap_end - fbi[GESTURE].mem_start + 1; + + ges_fw_start = (pfw[ges_info_addr + 15] << 24) + (pfw[ges_info_addr + 14] << 16) + (pfw[ges_info_addr + 13] << 8) + pfw[ges_info_addr + 12]; + ges_fw_end = (pfw[ges_info_addr + 19] << 24) + (pfw[ges_info_addr + 18] << 16) + (pfw[ges_info_addr + 17] << 8) + pfw[ges_info_addr + 16]; + + if (ges_fw_end != ges_fw_start) + fbi[GESTURE].len = ges_fw_end - ges_fw_start; + + /* update gesture address */ + fbi[GESTURE].start = ges_fw_start; + + ILI_INFO("==== Gesture loader info ====\n"); + ILI_INFO("gesture move to ap addr => start = 0x%x, ap_end = 0x%x, ap_len = 0x%x\n", fbi[GESTURE].mem_start, ap_end, ap_len); + ILI_INFO("gesture hex addr => start = 0x%x, gesture_end = 0x%x, gesture_len = 0x%x\n", ges_fw_start, ges_fw_end, fbi[GESTURE].len); + ILI_INFO("=============================\n"); + + fbi[AP].name = "AP"; + fbi[DATA].name = "DATA"; + fbi[TUNING].name = "TUNING"; + fbi[MP].name = "MP"; + fbi[GESTURE].name = "GESTURE"; + fbi[TAG].name = "TAG"; + fbi[PARA_BACKUP].name = "PARA_BACKUP"; + fbi[DDI].name = "DDI"; + + /* upgrade mode define */ + fbi[DATA].mode = fbi[AP].mode = fbi[TUNING].mode = AP; + fbi[MP].mode = MP; + fbi[GESTURE].mode = GESTURE; + + if (fbi[AP].end > (64*K)) + tfd.is80k = true; + + /* Copy fw info */ + fw_info_addr = fbi[AP].end - INFO_HEX_ST_ADDR; + ILI_INFO("Parsing hex info start addr = 0x%x\n", fw_info_addr); + ipio_memcpy(ilits->fw_info, (pfw + fw_info_addr), sizeof(ilits->fw_info), sizeof(ilits->fw_info)); + + /* copy fw mp ver */ + fw_mp_ver_addr = fbi[MP].end - INFO_MP_HEX_ADDR; + ILI_INFO("Parsing hex mp ver addr = 0x%x\n", fw_mp_ver_addr); + ipio_memcpy(ilits->fw_mp_ver, pfw + fw_mp_ver_addr, sizeof(ilits->fw_mp_ver), sizeof(ilits->fw_mp_ver)); + + /* copy fw core ver */ + ilits->chip->core_ver = (ilits->fw_info[68] << 24) | (ilits->fw_info[69] << 16) | + (ilits->fw_info[70] << 8) | ilits->fw_info[71]; + ILI_INFO("New FW Core version = %x\n", ilits->chip->core_ver); + + /* Get hex fw vers */ + tfd.new_fw_cb = (ilits->fw_info[48] << 24) | (ilits->fw_info[49] << 16) | + (ilits->fw_info[50] << 8) | ilits->fw_info[51]; + + /* Calculate update address */ + ILI_INFO("New FW ver = 0x%x\n", tfd.new_fw_cb); + ILI_INFO("star_addr = 0x%06X, end_addr = 0x%06X, Block Num = %d\n", tfd.start_addr, tfd.end_addr, tfd.block_number); + + return 0; +} + +static int ilitek_tddi_fw_hex_convert(u8 *phex, int size, u8 *pfw) +{ + int block = 0; + u32 i = 0, j = 0, k = 0, num = 0; + u32 len = 0, addr = 0, type = 0; + u32 start_addr = 0x0, end_addr = 0x0, ex_addr = 0; + u32 offset; + + memset(fbi, 0x0, sizeof(fbi)); + + /* Parsing HEX file */ + for (; i < size;) { + len = HexToDec(&phex[i + 1], 2); + addr = HexToDec(&phex[i + 3], 4); + type = HexToDec(&phex[i + 7], 2); + + if (type == 0x04) { + ex_addr = HexToDec(&phex[i + 9], 4); + } else if (type == 0x02) { + ex_addr = HexToDec(&phex[i + 9], 4); + ex_addr = ex_addr >> 12; + } else if (type == BLOCK_TAG_AF) { + /* insert block info extracted from hex */ + tfd.hex_tag = type; + if (tfd.hex_tag == BLOCK_TAG_AF) + num = HexToDec(&phex[i + 9 + 6 + 6], 2); + else + num = 0xFF; + + if (num > (FW_BLOCK_INFO_NUM - 1)) { + ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n", + num, FW_BLOCK_INFO_NUM - 1); + return -EINVAL; + } + + fbi[num].start = HexToDec(&phex[i + 9], 6); + fbi[num].end = HexToDec(&phex[i + 9 + 6], 6); + fbi[num].fix_mem_start = INT_MAX; + fbi[num].len = fbi[num].end - fbi[num].start + 1; + ILI_DBG("Block[%d]: start_addr = %x, end = %x", num, fbi[num].start, fbi[num].end); + + if (num == GESTURE) + ilits->gesture_load_code = true; + + block++; + } else if (type == BLOCK_TAG_B0 && tfd.hex_tag == BLOCK_TAG_AF) { + num = HexToDec(&phex[i + 9 + 6], 2); + + if (num > (FW_BLOCK_INFO_NUM - 1)) { + ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n", + num, FW_BLOCK_INFO_NUM - 1); + return -EINVAL; + } + + fbi[num].fix_mem_start = HexToDec(&phex[i + 9], 6); + ILI_DBG("Tag 0xB0: change Block[%d] to addr = 0x%x\n", num, fbi[num].fix_mem_start); + } + + addr = addr + (ex_addr << 16); + + if (phex[i + 1 + 2 + 4 + 2 + (len * 2) + 2] == 0x0D) + offset = 2; + else + offset = 1; + + if (addr > MAX_HEX_FILE_SIZE) { + ILI_ERR("Invalid hex format %d\n", addr); + return -1; + } + + if (type == 0x00) { + end_addr = addr + len; + if (addr < start_addr) + start_addr = addr; + /* fill data */ + for (j = 0, k = 0; j < (len * 2); j += 2, k++) + pfw[addr + k] = HexToDec(&phex[i + 9 + j], 2); + } + i += 1 + 2 + 4 + 2 + (len * 2) + 2 + offset; + } + + if (ilitek_fw_calc_file_crc(pfw) < 0) + return -1; + + tfd.start_addr = start_addr; + tfd.end_addr = end_addr; + tfd.block_number = block; + return 0; +} + +static int ilitek_tdd_fw_hex_open(u8 op, u8 *pfw) +{ + int ret =0, fsize = 0; + const struct firmware *fw = NULL; + struct file *f = NULL; + mm_segment_t old_fs; + loff_t pos = 0; + + ILI_INFO("Open file method = %s, path = %s\n", + op ? "FILP_OPEN" : "REQUEST_FIRMWARE", + op ? ilits->md_fw_filp_path : ilits->md_fw_rq_path); + + switch (op) { + case REQUEST_FIRMWARE: + if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) { + ILI_ERR("Request firmware failed, try again\n"); + if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) { + ILI_ERR("Request firmware failed after retry\n"); + ret = -1; + goto out; + } + } + + fsize = fw->size; + ILI_INFO("fsize = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is invaild\n"); + release_firmware(fw); + ret = -1; + goto out; + } + + ilits->tp_fw.size = 0; + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n"); + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw after retry\n"); + release_firmware(fw); + ret = -ENOMEM; + goto out; + } + } + + /* Copy fw data got from request_firmware to global */ + ipio_memcpy((u8 *)ilits->tp_fw.data, fw->data, fsize * sizeof(*fw->data), fsize); + ilits->tp_fw.size = fsize; + release_firmware(fw); + break; + case FILP_OPEN: + f = filp_open(ilits->md_fw_filp_path, O_RDONLY, 0644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file, %ld\n", PTR_ERR(f)); + ret = -1; + goto out; + } + + fsize = f->f_inode->i_size; + ILI_INFO("fsize = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is invaild\n"); + filp_close(f, NULL); + ret = -1; + goto out; + } + + ilits->tp_fw.size = 0; + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n"); + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw after retry\n"); + filp_close(f, NULL); + ret = -ENOMEM; + goto out; + } + } + + /* ready to map user's memory to obtain data by reading files */ + old_fs = get_fs(); + set_fs(get_ds()); + set_fs(KERNEL_DS); + pos = 0; + vfs_read(f, (u8 *)ilits->tp_fw.data, fsize, &pos); + set_fs(old_fs); + filp_close(f, NULL); + ilits->tp_fw.size = fsize; + break; + default: + ILI_ERR("Unknown open file method, %d\n", op); + break; + } + + if (ERR_ALLOC_MEM(ilits->tp_fw.data) || ilits->tp_fw.size <= 0) { + ILI_ERR("fw data/size is invaild\n"); + ret = -1; + goto out; + } + + /* Convert hex and copy data from tp_fw.data to pfw */ + if (ilitek_tddi_fw_hex_convert((u8 *)ilits->tp_fw.data, ilits->tp_fw.size, pfw) < 0) { + ILI_ERR("Convert hex file failed\n"); + ret = -1; + } + +out: + ipio_vfree((void **)&(ilits->tp_fw.data)); + return ret; +} + +int ili_fw_upgrade(int op) +{ + int i, ret = 0, retry = 3; + + if (!ilits->boot || ilits->force_fw_update || ERR_ALLOC_MEM(pfw)) { + ilits->gesture_load_code = false; + + if (ERR_ALLOC_MEM(pfw)) { + ipio_vfree((void **)&pfw); + pfw = vmalloc(MAX_HEX_FILE_SIZE * sizeof(u8)); + if (ERR_ALLOC_MEM(pfw)) { + ILI_ERR("Failed to allocate pfw memory, %ld\n", PTR_ERR(pfw)); + ipio_vfree((void **)&pfw); + ret = -ENOMEM; + goto out; + } + } + + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + pfw[i] = 0xFF; + + if (ilitek_tdd_fw_hex_open(op, pfw) < 0) { + ILI_ERR("Open firmware file fail \n"); + } + if (ilitek_tddi_fw_update_block_info(pfw) < 0) { + ret = -EFW_CONVERT_FILE; + goto out; + } + } + + do { + ret = ilitek_tddi_fw_iram_upgrade(pfw); + if (ret == UPDATE_PASS) + break; + + ILI_ERR("Upgrade failed, do retry!\n"); + } while (--retry > 0); + + if (ret != UPDATE_PASS) { + ILI_ERR("Failed to upgrade fw %d times, erasing iram\n", retry); + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP reset failed while erasing data\n"); + ilits->xch_num = 0; + ilits->ych_num = 0; + return ret; + } + +out: + ili_ic_get_core_ver(); + ili_ic_get_protocl_ver(); + ili_ic_get_fw_ver(); + ili_ic_get_tp_info(); + ili_ic_get_panel_info(); + ili_ic_func_ctrl_reset(); + return ret; +} + +void ili_fw_read_flash_info(void) +{ + return; +} + +void ili_flash_clear_dma(void) +{ + return; +} + +void ili_flash_dma_write(u32 start, u32 end, u32 len) +{ + return; +} + +int ili_fw_dump_flash_data(u32 start, u32 end, bool user) +{ + return 0; +} diff --git a/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_i2c.c b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_i2c.c new file mode 100644 index 000000000000..2d5c2880cc9f --- /dev/null +++ b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_i2c.c @@ -0,0 +1,448 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek.h" + +struct touch_bus_info { + struct i2c_driver bus_driver; + struct ilitek_hwif_info *hwif; +}; + +struct ilitek_ts_data *ilits; + +#define RW_SYNC 0 +#define R_ONLY 1 +#define W_ONLY 2 + +#if I2C_DMA_TRANSFER +static unsigned char *ilitek_dma_va; +static dma_addr_t ilitek_dma_pa; + +#define DMA_VA_BUFFER 4096 + +static int dma_i2c_alloc(struct i2c_client *client) +{ + if (client != NULL) { + client->dev.coherent_dma_mask = DMA_BIT_MASK(32); + ilitek_dma_va = (u8 *)dmam_alloc_coherent(client->dev, DMA_VA_BUFFER, &ilitek_dma_pa, GFP_KERNEL); + if (ERR_ALLOC_MEM(ilitek_dma_va)) { + ILI_ERR("Allocate DMA I2C Buffer failed\n"); + return -ENOMEM; + } + + memset(ilitek_dma_va, 0, DMA_VA_BUFFER); + client->ext_flag |= I2C_DMA_FLAG; + return 0; + } + + ILI_ERR("client is NULL, allocated dma i2c failed\n"); + return -ENODEV; +} +#endif + +static int core_i2c_write(void *buf, int len) +{ + int ret = 0; + u8 *txbuf = (u8 *)buf; + u8 check_sum = 0; + u8 *mpbuf = NULL; + + struct i2c_msg msgs[] = { + { + .addr = ilits->i2c->addr, + .flags = 0, /* write flag. */ + .len = len, + .buf = txbuf, + }, + }; + +#if I2C_DMA_TRANSFER + if (len > 8) { + msgs[0].addr = (ilits->client->addr & I2C_MASK_FLAG); + msgs[0].ext_flag = (ilits->client->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG); + memcpy(ilitek_dma_va, txbuf, len); + msgs[0].buf = (u8 *)ilitek_dma_pa; + } +#endif + + /* + * NOTE: If TP driver is doing MP test and commanding 0xF1 to FW, we add a checksum + * to the last index and plus 1 with size. + */ + if (atomic_read(&ilits->mp_stat) && txbuf[0] == P5_X_SET_CDC_INIT) { + check_sum = ili_calc_packet_checksum(txbuf, len); + mpbuf = kcalloc(len + 1, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(mpbuf)) { + ILI_ERR("Failed to allocate mpbuf mem\n"); + return -ENOMEM; + } + ipio_memcpy(mpbuf, txbuf, len, msgs[0].len); + mpbuf[len] = check_sum; + msgs[0].buf = mpbuf; + ili_dump_data(mpbuf, 8, len+1, 0, "mp cdc cmd"); + msgs[0].len = len + 1; + } + + if (i2c_transfer(ilits->i2c->adapter, msgs, 1) != 1) + ret = -1; + + ipio_kfree((void **)&mpbuf); + return ret; +} + +static int core_i2c_read(void *buf, int len) +{ + int ret; + u8 *rxbuf = (u8 *)buf; + + struct i2c_msg msgs[] = { + { + .addr = ilits->i2c->addr, + .flags = I2C_M_RD, /* read flag. */ + .len = len, + .buf = rxbuf, + }, + }; + + +#if I2C_DMA_TRANSFER + if (len > 8) { + msgs[0].addr = (ilits->client->addr & I2C_MASK_FLAG); + msgs[0].ext_flag = (ilits->client->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG); + msgs[0].buf = (u8 *)ilitek_dma_pa; + } else { + msgs[0].buf = rxbuf; + } +#endif + + ret = i2c_transfer(ilits->i2c->adapter, msgs, 1); + +#if I2C_DMA_TRANSFER + if (len > 8) + memcpy(rxbuf, ilitek_dma_va, len); +#endif + + /* + * If i2c_transfer is ok (must return 1 because only sends one msg), + * return #bytes transferred, else error code. + */ + return (ret == 1) ? len : ret; +} + +static int ilitek_i2c_write(void *buf, int len) +{ + int ret = 0; + + if (len == 0) { + ILI_ERR("i2c write len is invalid\n"); + return -EINVAL; + } + + ret = core_i2c_write(buf, len); + if (ret < 0) { + if (atomic_read(&ilits->tp_reset) == START) { + ret = 0; + goto out; + } + ILI_ERR("i2c write error, ret = %d\n", ret); + } + +out: + return ret; +} + +static int ilitek_i2c_read(void *buf, int len) +{ + int ret = 0; + + if (len == 0) { + ILI_ERR("i2c read len is invalid\n"); + return -EINVAL; + } + + ret = core_i2c_read(buf, len); + if (ret < 0) { + if (atomic_read(&ilits->tp_reset) == START) { + ret = 0; + goto out; + } + ILI_ERR("i2c read error, ret = %d\n", ret); + } + +out: + return ret; +} + +static int ili_i2c_wrapper(u8 *txbuf, u32 wlen, u8 *rxbuf, u32 rlen, bool spi_irq, bool i2c_irq) +{ + int ret = 0, operate = -1; + + if (wlen > 0) { + if (ERR_ALLOC_MEM(txbuf)) { + ILI_ERR("txbuf is null\n"); + return -ENOMEM; + } + } + + if (rlen > 0) { + if (ERR_ALLOC_MEM(rxbuf)) { + ILI_ERR("rxbuf is null\n"); + return -ENOMEM; + } + } + + if (wlen > 0 && rlen > 0) + operate = RW_SYNC; + else if (wlen > 0 && !rlen) + operate = W_ONLY; + else + operate = R_ONLY; + + if (ilits->int_pulse) + ilits->detect_int_stat = ili_ic_check_int_pulse; + else + ilits->detect_int_stat = ili_ic_check_int_level; + + if (i2c_irq) + atomic_set(&ilits->cmd_int_check, ENABLE); + + switch (operate) { + case RW_SYNC: + ret = ilitek_i2c_write(txbuf, wlen); + if (ret < 0) { + ILI_ERR("i2c-wrapper write error\n"); + break; + } + + if (i2c_irq) { + if (ilits->detect_int_stat(false) < 0) { + ILI_ERR("ERROR! Check INT timeout\n"); + ret = -ETIME; + break; + } + } else { + msleep(1); + } + + ret = ilitek_i2c_read(rxbuf, rlen); + if (ret < 0) { + ILI_ERR("i2c-wrapper read error\n"); + break; + } + break; + case W_ONLY: + ret = ilitek_i2c_write(txbuf, wlen); + if (ret < 0) { + ILI_ERR("i2c-wrapper write error\n"); + break; + } + break; + case R_ONLY: + if (i2c_irq) { + if (ilits->detect_int_stat(false) < 0) { + ILI_ERR("ERROR! Check INT timeout\n"); + ret = -ETIME; + break; + } + } + + ret = ilitek_i2c_read(rxbuf, rlen); + if (ret < 0) { + ILI_ERR("i2c-wrapper read error\n"); + break; + } + break; + default: + ILI_ERR("Unknown ts-i2c operation\n"); + ret = -EINVAL; + break; + } + + if (i2c_irq) + atomic_set(&ilits->cmd_int_check, DISABLE); + + return ret; +} + +int ili_core_spi_setup(int num) +{ + ILI_ERR("Not support this interface\n"); + return 0; +} + +static int ilitek_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct touch_bus_info *info = + container_of(to_i2c_driver(i2c->dev.driver), + struct touch_bus_info, bus_driver); + + ILI_INFO("ilitek i2c probe\n"); + + if (!i2c) { + ILI_ERR("i2c client is NULL\n"); + return -ENODEV; + } + + if (i2c->addr != TDDI_I2C_ADDR) { + i2c->addr = TDDI_I2C_ADDR; + ILI_INFO("i2c addr doesn't be set up, use default : 0x%x\n", i2c->addr); + } + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + ILI_ERR("i2c functions are not supported!\n"); + return -ENODEV; + } + + ilits = devm_kzalloc(&i2c->dev, sizeof(struct ilitek_ts_data), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits)) { + ILI_ERR("Failed to allocate ts memory, %ld\n", PTR_ERR(ilits)); + return -ENOMEM; + } + + /* Used for receiving touch data only, do not mix up with others. */ + ilits->tr_buf = kzalloc(TR_BUF_SIZE, GFP_ATOMIC); + if (ERR_ALLOC_MEM(ilits->tr_buf)) { + ILI_ERR("failed to allocate touch report buffer\n"); + return -ENOMEM; + } + + ilits->gcoord = kzalloc(sizeof(struct gesture_coordinate), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->gcoord)) { + ILI_ERR("Failed to allocate gresture coordinate buffer\n"); + return -ENOMEM; + } + + ilits->i2c = i2c; + ilits->spi = NULL; + ilits->dev = &i2c->dev; + ilits->hwif = info->hwif; + ilits->phys = "I2C"; + ilits->wrapper = ili_i2c_wrapper; + ilits->detect_int_stat = ili_ic_check_int_pulse; + ilits->int_pulse = true; + ilits->mp_retry = false; + +#if I2C_DMA_TRANSFER + if (dma_i2c_alloc(ilits->i2c) < 0) + ILI_ERR("Failed to alllocate DMA mem %ld\n", PTR_ERR(ilits->i2c)); +#endif + + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + + if (TDDI_RST_BIND) + ilits->reset = TP_IC_WHOLE_RST; + else + ilits->reset = TP_HW_RST_ONLY; + + ilits->rst_edge_delay = 100; + ilits->fw_open = FILP_OPEN; + ilits->fw_upgrade_mode = UPGRADE_FLASH; + ilits->mp_move_code = ili_move_mp_code_flash; + ilits->gesture_move_code = ili_move_gesture_code_flash; + ilits->esd_recover = ili_wq_esd_i2c_check; + ilits->ges_recover = ili_touch_esd_gesture_flash; + ilits->gesture_mode = DATA_FORMAT_GESTURE_INFO; + ilits->gesture_demo_ctrl = DISABLE; + ilits->wtd_ctrl = OFF; + ilits->report = ENABLE; + ilits->netlink = DISABLE; + ilits->dnp = DISABLE; + ilits->irq_tirgger_type = IRQF_TRIGGER_FALLING; + ilits->info_from_hex = DISABLE; + ilits->wait_int_timeout = AP_INT_TIMEOUT; + +#if ENABLE_GESTURE + ilits->gesture = DISABLE; + ilits->ges_sym.double_tap = DOUBLE_TAP; + ilits->ges_sym.alphabet_line_2_top = ALPHABET_LINE_2_TOP; + ilits->ges_sym.alphabet_line_2_bottom = ALPHABET_LINE_2_BOTTOM; + ilits->ges_sym.alphabet_line_2_left = ALPHABET_LINE_2_LEFT; + ilits->ges_sym.alphabet_line_2_right = ALPHABET_LINE_2_RIGHT; + ilits->ges_sym.alphabet_m = ALPHABET_M; + ilits->ges_sym.alphabet_w = ALPHABET_W; + ilits->ges_sym.alphabet_c = ALPHABET_C; + ilits->ges_sym.alphabet_E = ALPHABET_E; + ilits->ges_sym.alphabet_V = ALPHABET_V; + ilits->ges_sym.alphabet_O = ALPHABET_O; + ilits->ges_sym.alphabet_S = ALPHABET_S; + ilits->ges_sym.alphabet_Z = ALPHABET_Z; + ilits->ges_sym.alphabet_V_down = ALPHABET_V_DOWN; + ilits->ges_sym.alphabet_V_left = ALPHABET_V_LEFT; + ilits->ges_sym.alphabet_V_right = ALPHABET_V_RIGHT; + ilits->ges_sym.alphabet_two_line_2_bottom = ALPHABET_TWO_LINE_2_BOTTOM; + ilits->ges_sym.alphabet_F = ALPHABET_F; + ilits->ges_sym.alphabet_AT = ALPHABET_AT; +#endif + + return info->hwif->plat_probe(); +} + +static int ilitek_i2c_remove(struct i2c_client *i2c) +{ + ILI_INFO(); + return 0; +} + +static const struct i2c_device_id tp_i2c_id[] = { + {TDDI_DEV_ID, 0}, + {}, +}; + +int ili_interface_dev_init(struct ilitek_hwif_info *hwif) +{ + struct touch_bus_info *info; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ILI_ERR("faied to allocate i2c_driver\n"); + return -ENOMEM; + } + + if (hwif->bus_type != BUS_I2C) { + ILI_ERR("Not I2C dev\n"); + ipio_kfree((void **)&info); + return -EINVAL; + } + + hwif->info = info; + + info->bus_driver.driver.name = hwif->name; + info->bus_driver.driver.owner = hwif->owner; + info->bus_driver.driver.of_match_table = hwif->of_match_table; + info->bus_driver.driver.pm = hwif->pm; + + info->bus_driver.probe = ilitek_i2c_probe; + info->bus_driver.remove = ilitek_i2c_remove; + info->bus_driver.id_table = tp_i2c_id; + + info->hwif = hwif; + return i2c_add_driver(&info->bus_driver); +} + +void ili_interface_dev_exit(struct ilitek_ts_data *ts) +{ + struct touch_bus_info *info = (struct touch_bus_info *)ilits->hwif->info; + + ILI_INFO("remove i2c dev\n"); + i2c_del_driver(&info->bus_driver); + ipio_kfree((void **)&info); +} diff --git a/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_ic.c b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_ic.c new file mode 100644 index 000000000000..06bec0b6964a --- /dev/null +++ b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_ic.c @@ -0,0 +1,1011 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek.h" + +#define PROTOCL_VER_NUM 8 +static struct ilitek_protocol_info protocol_info[PROTOCL_VER_NUM] = { + /* length -> fw, protocol, tp, key, panel, core, func, window, cdc, mp_info */ + [0] = {PROTOCOL_VER_500, 4, 4, 14, 30, 5, 5, 2, 8, 3, 8}, + [1] = {PROTOCOL_VER_510, 4, 3, 14, 30, 5, 5, 3, 8, 3, 8}, + [2] = {PROTOCOL_VER_520, 4, 4, 14, 30, 5, 5, 3, 8, 3, 8}, + [3] = {PROTOCOL_VER_530, 9, 4, 14, 30, 5, 5, 3, 8, 3, 8}, + [4] = {PROTOCOL_VER_540, 9, 4, 14, 30, 5, 5, 3, 8, 15, 8}, + [5] = {PROTOCOL_VER_550, 9, 4, 14, 30, 5, 5, 3, 8, 15, 14}, + [6] = {PROTOCOL_VER_560, 9, 4, 14, 30, 5, 5, 3, 8, 15, 14}, + [7] = {PROTOCOL_VER_570, 9, 4, 14, 30, 5, 5, 3, 8, 15, 14}, +}; + +#define FUNC_CTRL_NUM 21 +static struct ilitek_ic_func_ctrl func_ctrl[FUNC_CTRL_NUM] = { + /* cmd[3] = cmd, func, ctrl */ + // rec_state 0:disable, 1: enable, 2: ignore record + [0] = {"sense", {0x1, 0x1, 0x0}, 3, 2, 0xFF}, + [1] = {"sleep", {0x1, 0x2, 0x0}, 3, 2, 0xFF}, + [2] = {"glove", {0x1, 0x6, 0x0}, 3, 2, 0xFF}, + [3] = {"stylus", {0x1, 0x7, 0x0}, 3, 2, 0xFF}, + [4] = {"tp_scan_mode", {0x1, 0x8, 0x0}, 3, 2, 0xFF}, + [5] = {"lpwg", {0x1, 0xA, 0x0}, 3, 2, 0xFF}, + [6] = {"gesture", {0x1, 0xB, 0x3F}, 3, 2, 0xFF}, + [7] = {"phone_cover", {0x1, 0xC, 0x0}, 3, 2, 0xFF}, + [8] = {"finger_sense", {0x1, 0xF, 0x0}, 3, 2, 0xFF}, + [9] = {"phone_cover_window", {0xE, 0x0, 0x0}, 3, 2, 0xFF}, + [10] = {"proximity", {0x1, 0x10, 0x0}, 3, 2, 0xFF}, + [11] = {"plug", {0x1, 0x11, 0x0}, 3, 0, 0xFF}, + [12] = {"edge_palm", {0x1, 0x12, 0x0}, 3, 0, 0xFF}, + [13] = {"lock_point", {0x1, 0x13, 0x0}, 3, 0, 0xFF}, + [14] = {"active", {0x1, 0x14, 0x0}, 3, 2, 0xFF}, + [15] = {"idle", {0x1, 0x19, 0x0}, 3, 2, 0xFF}, + [16] = {"gesture_demo_en", {0x1, 0x16, 0x0}, 3, 2, 0xFF}, + [17] = {"tp_recore", {0x1, 0x18, 0x0}, 3, 2, 0xFF}, + [18] = {"knock_en", {0x1, 0xA, 0x8, 0x03, 0x0, 0x0}, 6, 2, 0xFF}, + [19] = {"int_trigger", {0x1, 0x1B, 0x0}, 3, 2, 0xFF}, + [20] = {"ear_phone", {0x1, 0x17, 0x0}, 3, 0, 0xFF}, +}; + +#define CHIP_SUP_NUM 5 +static u32 ic_sup_list[CHIP_SUP_NUM] = { + [0] = ILI9881_CHIP, + [1] = ILI7807_CHIP, + [2] = ILI9881N_AA, + [3] = ILI9881O_AA, + [4] = ILI9882_CHIP +}; + +static int ilitek_tddi_ic_check_support(u32 pid, u16 id) +{ + int i = 0; + + for (i = 0; i < CHIP_SUP_NUM; i++) { + if ((pid == ic_sup_list[i]) || (id == ic_sup_list[i])) + break; + } + + if (i >= CHIP_SUP_NUM) { + ILI_INFO("ERROR, ILITEK CHIP(0x%x) Not found !!\n", pid); + } + + ILI_INFO("ILITEK CHIP ILI9881x found.\n"); + + ilits->chip->pid = pid; + + ilits->chip->reset_key = 0x00019878; + ilits->chip->wtd_key = 0x9881; + + if (((pid & 0xFFFFFF00) == ILI9881N_AA) || ((pid & 0xFFFFFF00) == ILI9881O_AA)){ + ilits->chip->dma_reset = ENABLE; + } else { + ilits->chip->dma_reset = DISABLE; + } + + ilits->chip->no_bk_shift = RAWDATA_NO_BK_SHIFT; + ilits->chip->max_count = 0x1FFFF; + return 0; +} + +int ili_ice_mode_bit_mask_write(u32 addr, u32 mask, u32 value) +{ + int ret = 0; + u32 data = 0; + + if (ili_ice_mode_read(addr, &data, sizeof(u32)) < 0) { + ILI_ERR("Read data error\n"); + return -1; + } + + data &= (~mask); + data |= (value & mask); + + ILI_DBG("mask value data = %x\n", data); + + ret = ili_ice_mode_write(addr, data, sizeof(u32)); + if (ret < 0) + ILI_ERR("Failed to re-write data in ICE mode, ret = %d\n", ret); + + return ret; +} + +int ili_ice_mode_write(u32 addr, u32 data, int len) +{ + int ret = 0, i; + u8 txbuf[64] = {0}; + + if (!atomic_read(&ilits->ice_stat)) { + ILI_ERR("ice mode not enabled\n"); + return -1; + } + + txbuf[0] = 0x25; + txbuf[1] = (char)((addr & 0x000000FF) >> 0); + txbuf[2] = (char)((addr & 0x0000FF00) >> 8); + txbuf[3] = (char)((addr & 0x00FF0000) >> 16); + + for (i = 0; i < len; i++) + txbuf[i + 4] = (char)(data >> (8 * i)); + + ret = ilits->wrapper(txbuf, len + 4, NULL, 0, OFF, OFF); + if (ret < 0) + ILI_ERR("Failed to write data in ice mode, ret = %d\n", ret); + + return ret; +} + +int ili_ice_mode_read(u32 addr, u32 *data, int len) +{ + int ret = 0; + u8 *rxbuf = NULL; + u8 txbuf[4] = {0}; + + if (!atomic_read(&ilits->ice_stat)) { + ILI_ERR("ice mode not enabled\n"); + return -1; + } + + txbuf[0] = 0x25; + txbuf[1] = (char)((addr & 0x000000FF) >> 0); + txbuf[2] = (char)((addr & 0x0000FF00) >> 8); + txbuf[3] = (char)((addr & 0x00FF0000) >> 16); + + ret = ilits->wrapper(txbuf, sizeof(txbuf), NULL, 0, OFF, OFF); + if (ret < 0) + goto out; + + rxbuf = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(rxbuf)) { + ILI_ERR("Failed to allocate rxbuf, %ld\n", PTR_ERR(rxbuf)); + ret = -ENOMEM; + goto out; + } + + ret = ilits->wrapper(NULL, 0, rxbuf, len, OFF, OFF); + if (ret < 0) + goto out; + + if (len == sizeof(u8)) + *data = rxbuf[0]; + else + *data = (rxbuf[0] | rxbuf[1] << 8 | rxbuf[2] << 16 | rxbuf[3] << 24); + +out: + if (ret < 0) + ILI_ERR("Failed to read data in ice mode, ret = %d\n", ret); + + ipio_kfree((void **)&rxbuf); + return ret; +} + +int ili_ice_mode_ctrl(bool enable, bool mcu) +{ + int ret = 0; + u8 cmd_open[4] = {0x25, 0x62, 0x10, 0x18}; + u8 cmd_close[4] = {0x1B, 0x62, 0x10, 0x18}; + + ILI_INFO("%s ICE mode, mcu on = %d\n", (enable ? "Enable" : "Disable"), mcu); + + if (enable) { + if (atomic_read(&ilits->ice_stat)) { + ILI_INFO("ice mode already enabled\n"); + return 0; + } + + if (mcu) + cmd_open[0] = 0x1F; + + atomic_set(&ilits->ice_stat, ENABLE); + + if (ilits->wrapper(cmd_open, sizeof(cmd_open), NULL, 0, OFF, OFF) < 0) + ILI_ERR("write ice mode cmd error\n"); + ilits->pll_clk_wakeup = false; + +#if (TDDI_INTERFACE == BUS_I2C) + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); /* CS high */ +#endif + } else { + if (!atomic_read(&ilits->ice_stat)) { + ILI_INFO("ice mode already disabled\n"); + return 0; + } + + ret = ilits->wrapper(cmd_close, sizeof(cmd_close), NULL, 0, OFF, OFF); + if (ret < 0) + ILI_ERR("Exit to ICE Mode failed !!\n"); + ilits->pll_clk_wakeup = true; + atomic_set(&ilits->ice_stat, DISABLE); + } + + return ret; +} + +int ili_ic_func_ctrl(const char *name, int ctrl) +{ + int i = 0, ret; + + for (i = 0; i < FUNC_CTRL_NUM; i++) { + if (ipio_strcmp(name, func_ctrl[i].name) == 0) { + if (strlen(name) != strlen(func_ctrl[i].name)) + continue; + break; + } + } + + if (i >= FUNC_CTRL_NUM) { + ILI_ERR("Not found function ctrl, %s\n", name); + ret = -1; + goto out; + } + + if (ilits->protocol->ver == PROTOCOL_VER_500) { + ILI_ERR("Non support function ctrl with protocol v5.0\n"); + ret = -1; + goto out; + } + + if (ilits->protocol->ver >= PROTOCOL_VER_560) { + if (ipio_strcmp(func_ctrl[i].name, "gesture") == 0 || + ipio_strcmp(func_ctrl[i].name, "phone_cover_window") == 0) { + ILI_INFO("Non support %s function ctrl\n", func_ctrl[i].name); + ret = -1; + goto out; + } + } + + func_ctrl[i].cmd[2] = ctrl; + + ILI_INFO("func = %s, len = %d, cmd = 0x%x, 0%x, 0x%x\n", func_ctrl[i].name, func_ctrl[i].len, + func_ctrl[i].cmd[0], func_ctrl[i].cmd[1], func_ctrl[i].cmd[2]); + + ret = ilits->wrapper(func_ctrl[i].cmd, func_ctrl[i].len, NULL, 0, OFF, OFF); + if (ret < 0) + ILI_ERR("Write TP function failed\n"); + + if (func_ctrl[i].rec_state < 2) { + func_ctrl[i].rec_state = ENABLE; + func_ctrl[i].rec_cmd = ctrl; + } + + ILI_DBG("record %s func cmd %d \n", func_ctrl[i].name, func_ctrl[i].rec_cmd); +out: + return ret; +} + +void ili_ic_func_ctrl_reset(void) { + int i = 0; + for (i = 0; i < FUNC_CTRL_NUM; i++) { + if (func_ctrl[i].rec_state == ENABLE) { + ILI_DBG("reset func ctrl %s, record status = %d, cmd = %d\n", func_ctrl[i].name, + func_ctrl[i].rec_state, func_ctrl[i].rec_cmd); + if (ili_ic_func_ctrl(func_ctrl[i].name, func_ctrl[i].rec_cmd) < 0) + ILI_ERR("reset ic func ctrl %s failed\n", func_ctrl[i].name); + } + } +} + +int ili_ic_code_reset(void) +{ + int ret; + bool ice = atomic_read(&ilits->ice_stat); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed before code reset\n"); + + ret = ili_ice_mode_write(0x40040, 0xAE, 1); + if (ret < 0) + ILI_ERR("ic code reset failed\n"); + + if (!ice) + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed after code reset\n"); + return ret; +} + +int ili_ic_whole_reset(void) +{ + int ret = 0; + bool ice = atomic_read(&ilits->ice_stat); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed before chip reset\n"); + + ILI_INFO("ic whole reset key = 0x%x, edge_delay = %d\n", + ilits->chip->reset_key, ilits->rst_edge_delay); + + ret = ili_ice_mode_write(ilits->chip->reset_addr, ilits->chip->reset_key, sizeof(u32)); + if (ret < 0) { + ILI_ERR("ic whole reset failed\n"); + goto out; + } + + /* Need accurate power sequence, do not change it to msleep */ + mdelay(ilits->rst_edge_delay); + +out: + if (!ice) + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed after chip reset\n"); + + return ret; +} + +static void ilitek_tddi_ic_wr_pack(int packet) +{ + int retry = 100; + u32 reg_data = 0; + + while (retry--) { + if (ili_ice_mode_read(0x73010, ®_data, sizeof(u8)) < 0) + ILI_ERR("Read 0x73010 error\n"); + + if ((reg_data & 0x02) == 0) { + ILI_INFO("check ok 0x73010 read 0x%X retry = %d\n", reg_data, retry); + break; + } + mdelay(10); + } + + if (retry <= 0) + ILI_INFO("check 0x73010 error read 0x%X\n", reg_data); + + if (ili_ice_mode_write(0x73000, packet, 4) < 0) + ILI_ERR("Write %x at 0x73000\n", packet); +} + +static u32 ilitek_tddi_ic_rd_pack(int packet) +{ + int retry = 100; + u32 reg_data = 0; + + ilitek_tddi_ic_wr_pack(packet); + + while (retry--) { + if (ili_ice_mode_read(0x4800A, ®_data, sizeof(u8)) < 0) + ILI_ERR("Read 0x4800A error\n"); + + if ((reg_data & 0x02) == 0x02) { + ILI_INFO("check ok 0x4800A read 0x%X retry = %d\n", reg_data, retry); + break; + } + mdelay(10); + } + if (retry <= 0) + ILI_INFO("check 0x4800A error read 0x%X\n", reg_data); + + if (ili_ice_mode_write(0x4800A, 0x02, 1) < 0) + ILI_ERR("Write 0x2 at 0x4800A\n"); + + if (ili_ice_mode_read(0x73016, ®_data, sizeof(u8)) < 0) + ILI_ERR("Read 0x73016 error\n"); + + return reg_data; +} + +void ili_ic_set_ddi_reg_onepage(u8 page, u8 reg, u8 data) +{ + u32 setpage = 0x1FFFFF00 | page; + u32 setreg = 0x1F000100 | (reg << 16) | data; + bool ice = atomic_read(&ilits->ice_stat); + + ILI_INFO("setpage = 0x%X setreg = 0x%X\n", setpage, setreg); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed before writing ddi reg\n"); + + /*TDI_WR_KEY*/ + ilitek_tddi_ic_wr_pack(0x1FFF9527); + /*Switch to Page*/ + ilitek_tddi_ic_wr_pack(setpage); + /* Page*/ + ilitek_tddi_ic_wr_pack(setreg); + /*TDI_WR_KEY OFF*/ + ilitek_tddi_ic_wr_pack(0x1FFF9500); + + if (!ice) + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Disable ice mode failed after writing ddi reg\n"); +} + +void ili_ic_get_ddi_reg_onepage(u8 page, u8 reg, u8 *data) +{ + u32 setpage = 0x1FFFFF00 | page; + u32 setreg = 0x2F000100 | (reg << 16); + bool ice = atomic_read(&ilits->ice_stat); + + ILI_INFO("setpage = 0x%X setreg = 0x%X\n", setpage, setreg); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed before reading ddi reg\n"); + + /*TDI_WR_KEY*/ + ilitek_tddi_ic_wr_pack(0x1FFF9527); + /*Set Read Page reg*/ + ilitek_tddi_ic_wr_pack(setpage); + + /*TDI_RD_KEY*/ + ilitek_tddi_ic_wr_pack(0x1FFF9487); + /*( *( __IO uint8 *) (0x4800A) ) =0x2*/ + if (ili_ice_mode_write(0x4800A, 0x02, 1) < 0) + ILI_ERR("Write 0x2 at 0x4800A\n"); + + *data = ilitek_tddi_ic_rd_pack(setreg); + ILI_INFO("check page = 0x%X, reg = 0x%X, read 0x%X\n", page, reg, *data); + + /*TDI_RD_KEY OFF*/ + ilitek_tddi_ic_wr_pack(0x1FFF9400); + /*TDI_WR_KEY OFF*/ + ilitek_tddi_ic_wr_pack(0x1FFF9500); + + if (!ice) + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Disable ice mode failed after reading ddi reg\n"); +} + + +void ili_ic_get_pc_counter(int stat) +{ + bool ice = atomic_read(&ilits->ice_stat); + u32 pc = 0, pc_addr = ilits->chip->pc_counter_addr; + u32 latch = 0, latch_addr = ilits->chip->pc_latch_addr; + + ILI_DBG("stat = %d\n", stat); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, ON) < 0) + ILI_ERR("Enable ice mode failed while reading pc counter\n"); + + if (ili_ice_mode_read(ilits->chip->pc_counter_addr, &pc, sizeof(u32)) < 0) + ILI_ERR("Read pc conter error\n"); + + if (ili_ice_mode_read(ilits->chip->pc_latch_addr, &latch, sizeof(u32)) < 0) + ILI_ERR("Read pc latch error\n"); + + ilits->fw_pc = pc; + ilits->fw_latch = latch; + ILI_ERR("Read counter (addr: 0x%x) = 0x%x, latch (addr: 0x%x) = 0x%x\n", + pc_addr, ilits->fw_pc, latch_addr, ilits->fw_latch); + + /* Avoid screen abnormal. */ + if (stat == DO_SPI_RECOVER) { + atomic_set(&ilits->ice_stat, DISABLE); + return; + } + + if (!ice) { + if (ili_ice_mode_ctrl(DISABLE, ON) < 0) + ILI_ERR("Disable ice mode failed while reading pc counter\n"); + } +} + +int ili_ic_int_trigger_ctrl(bool pulse) +{ + /* It's supported by fw, and the level will be kept at high until data was already prepared. */ + if (ili_ic_func_ctrl("int_trigger", pulse) < 0) { + ILI_ERR("Write CMD error, set back to <%s> trigger\n", ilits->int_pulse ? "Level" : "Pulse"); + return -1; + } + + ilits->int_pulse = pulse; + ILI_INFO("INT Trigger = %s\n", ilits->int_pulse ? "Level" : "Pulse"); + return 0; +} + +int ili_ic_check_int_level(bool level) +{ + int timer = 3000; + int gpio = IRQ_GPIO_NUM; //ilits->tp_int + + /* + * If callers have a trouble to use the gpio that is passed by vendors, + * please utlises a physical gpio number instead or call a help from them. + */ + + while (--timer > 0) { + if(level) { + if (gpio_get_value(gpio)) { + ILI_DBG("INT high detected.\n"); + return 0; + } + } else { + if (!gpio_get_value(gpio)) { + ILI_DBG("INT low detected.\n");; + return 0; + } + } + mdelay(1); + } + ILI_ERR("Error! INT level no detected.\n"); + return -1; +} + +int ili_ic_check_int_pulse(bool pulse) +{ + if (!wait_event_interruptible_timeout(ilits->inq, !atomic_read(&ilits->cmd_int_check), msecs_to_jiffies(ilits->wait_int_timeout))) { + ILI_ERR("Error! INT pulse no detected. Timeout = %d ms\n", ilits->wait_int_timeout); + atomic_set(&ilits->cmd_int_check, DISABLE); + return -1; + } + ILI_DBG("INT pulse detected.\n"); + return 0; +} + +int ili_ic_check_busy(int count, int delay) +{ + u8 cmd[2] = {0}; + u8 busy = 0, rby = 0; + + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_CDC_BUSY_STATE; + + if (ilits->actual_tp_mode == P5_X_FW_AP_MODE) + rby = 0x41; + else if (ilits->actual_tp_mode == P5_X_FW_TEST_MODE) + rby = 0x51; + else { + ILI_ERR("Unknown TP mode (0x%x)\n", ilits->actual_tp_mode); + return -EINVAL; + } + + ILI_INFO("read byte = %x, delay = %d\n", rby, delay); + + do { + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) + ILI_ERR("Write check busy cmd failed\n"); + + if (ilits->wrapper(&cmd[1], sizeof(u8), &busy, sizeof(u8), ON, OFF) < 0) + ILI_ERR("Read check busy failed\n"); + + ILI_DBG("busy = 0x%x\n", busy); + + if (busy == rby) { + ILI_INFO("Check busy free\n"); + return 0; + } + + mdelay(delay); + } while (--count > 0); + + ILI_ERR("Check busy (0x%x) timeout !\n", busy); + ili_ic_get_pc_counter(0); + return -1; +} + +int ili_ic_get_project_id(u8 *pdata, int size) +{ + int i; + u32 tmp; + bool ice = atomic_read(&ilits->ice_stat); + + if (!pdata) { + ILI_ERR("pdata is null\n"); + return -ENOMEM; + } + + ILI_INFO("Read size = %d\n", size); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed while reading project id\n"); + + if (ili_ice_mode_write(0x041000, 0x0, 1) < 0) + ILI_ERR("Pull cs low failed\n"); + if (ili_ice_mode_write(0x041004, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(0x041008, 0x03, 1) < 0) + ILI_ERR("Write 0x03 at 0x041008\n"); + + if (ili_ice_mode_write(0x041008, (RSV_BK_ST_ADDR & 0xFF0000) >> 16, 1) < 0) + ILI_ERR("Write address failed\n"); + if (ili_ice_mode_write(0x041008, (RSV_BK_ST_ADDR & 0x00FF00) >> 8, 1) < 0) + ILI_ERR("Write address failed\n"); + if (ili_ice_mode_write(0x041008, (RSV_BK_ST_ADDR & 0x0000FF), 1) < 0) + ILI_ERR("Write address failed\n"); + + for (i = 0; i < size; i++) { + if (ili_ice_mode_write(0x041008, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + if (ili_ice_mode_read(0x41010, &tmp, sizeof(u8)) < 0) + ILI_ERR("Read project id error\n"); + pdata[i] = tmp; + ILI_INFO("project_id[%d] = 0x%x\n", i, pdata[i]); + } + + ili_flash_clear_dma(); + + if (ili_ice_mode_write(0x041000, 0x1, 1) < 0) + ILI_ERR("Pull cs high\n"); + + if (!ice) + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Disable ice mode failed while reading project id\n"); + + return 0; +} + +int ili_ic_get_core_ver(void) +{ + int i = 0, ret = 0; + u8 cmd[2] = {0}, buf[10] = {0}; + + ilits->protocol->core_ver_len = P5_X_CORE_VER_FOUR_LENGTH; + + if (ilits->info_from_hex) { + buf[1] = ilits->fw_info[68]; + buf[2] = ilits->fw_info[69]; + buf[3] = ilits->fw_info[70]; + buf[4] = ilits->fw_info[71]; + goto out; + } + + do { + if (i == 0) { + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_CORE_VERSION_NEW; + } else { + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_CORE_VERSION; + ilits->protocol->core_ver_len = P5_X_CORE_VER_THREE_LENGTH; + } + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) + ILI_ERR("Write core ver cmd failed\n"); + + if (ilits->wrapper(&cmd[1], sizeof(u8), buf, ilits->protocol->core_ver_len, ON, OFF) < 0) + ILI_ERR("Write core ver (0x%x) failed\n", cmd[1]); + + ILI_DBG("header = 0x%x\n", buf[0]); + + if (buf[0] == P5_X_GET_CORE_VERSION || + buf[0] == P5_X_GET_CORE_VERSION_NEW) + break; + + } while (++i < 2); + + if (buf[0] == P5_X_GET_CORE_VERSION) + buf[4] = 0; + + if (i >= 2) { + ILI_ERR("Invalid header (0x%x)\n", buf[0]); + ret = -EINVAL; + } + +out: + ILI_INFO("Core version = %d.%d.%d.%d\n", buf[1], buf[2], buf[3], buf[4]); + ilits->chip->core_ver = buf[1] << 24 | buf[2] << 16 | buf[3] << 8 | buf[4]; + return ret; +} + +void ili_fw_uart_ctrl(u8 ctrl) +{ + u8 cmd[4] = {0}; + + if (ctrl > 1) { + ILI_INFO("Unknown cmd, ignore\n"); + return; + } + + ILI_INFO("%s UART mode\n", ctrl ? "Enable" : "Disable"); + + cmd[0] = P5_X_I2C_UART; + cmd[1] = 0x3; + cmd[2] = 0; + cmd[3] = ctrl; + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) { + ILI_INFO("Write fw uart cmd failed\n"); + return; + } +} + +int ili_ic_get_fw_ver(void) +{ + int ret = 0; + u8 cmd[2] = {0}; + u8 buf[10] = {0}; + + if (ilits->info_from_hex) { + buf[1] = ilits->fw_info[48]; + buf[2] = ilits->fw_info[49]; + buf[3] = ilits->fw_info[50]; + buf[4] = ilits->fw_info[51]; + buf[5] = ilits->fw_mp_ver[0]; + buf[6] = ilits->fw_mp_ver[1]; + buf[7] = ilits->fw_mp_ver[2]; + buf[8] = ilits->fw_mp_ver[3]; + goto out; + } + + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_FW_VERSION; + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Write pre cmd failed\n"); + ret = -EINVAL; + goto out; + + } + + if (ilits->wrapper(&cmd[1], sizeof(u8), buf, ilits->protocol->fw_ver_len, ON, OFF)) { + ILI_ERR("Write fw version cmd failed\n"); + ret = -EINVAL; + goto out; + } + + if (buf[0] != P5_X_GET_FW_VERSION) { + ILI_ERR("Invalid firmware ver\n"); + ret = -1; + } + +out: + ILI_INFO("Firmware version = %d.%d.%d.%d\n", buf[1], buf[2], buf[3], buf[4]); + ILI_INFO("Firmware MP version = %d.%d.%d.%d\n", buf[5], buf[6], buf[7], buf[8]); + ilits->chip->fw_ver = buf[1] << 24 | buf[2] << 16 | buf[3] << 8 | buf[4]; + ilits->chip->fw_mp_ver = buf[5] << 24 | buf[6] << 16 | buf[7] << 8 | buf[8]; + return ret; +} + +int ili_ic_get_panel_info(void) +{ + int ret = 0; + u8 cmd = P5_X_GET_PANEL_INFORMATION; + u8 buf[10] = {0}; + u8 len = ilits->protocol->panel_info_len; + + if (ilits->info_from_hex && (ilits->chip->core_ver >= CORE_VER_1410)) { + buf[1] = ilits->fw_info[16]; + buf[2] = ilits->fw_info[17]; + buf[3] = ilits->fw_info[18]; + buf[4] = ilits->fw_info[19]; + ilits->panel_wid = buf[2] << 8 | buf[1]; + ilits->panel_hei = buf[4] << 8 | buf[3]; + ilits->trans_xy = (ilits->chip->core_ver >= CORE_VER_1430) ? ilits->fw_info[0] : OFF; + goto out; + } + + len = (ilits->chip->core_ver >= CORE_VER_1430) ? 6 : len; + + ret = ilits->wrapper(&cmd, sizeof(cmd), buf, len, ON, OFF); + if (ret < 0) + ILI_ERR("Read panel info error\n"); + + if (buf[0] != cmd) { + ILI_INFO("Invalid panel info, use default resolution\n"); + ilits->panel_wid = TOUCH_SCREEN_X_MAX; + ilits->panel_hei = TOUCH_SCREEN_Y_MAX; + ilits->trans_xy = OFF; + } else { + ilits->panel_wid = buf[1] << 8 | buf[2]; + ilits->panel_hei = buf[3] << 8 | buf[4]; + ilits->trans_xy = (ilits->chip->core_ver >= CORE_VER_1430) ? buf[5] : OFF; + } + +out: + ILI_INFO("Panel info: width = %d, height = %d\n", ilits->panel_wid, ilits->panel_hei); + ILI_INFO("Transfer touch coordinate = %s\n", ilits->trans_xy ? "ON" : "OFF"); + return ret; +} + +int ili_ic_get_tp_info(void) +{ + int ret = 0; + u8 cmd[2] = {0}; + u8 buf[20] = {0}; + + if (ilits->info_from_hex && (ilits->chip->core_ver >= CORE_VER_1410)) { + buf[1] = ilits->fw_info[5]; + buf[2] = ilits->fw_info[7]; + buf[3] = ilits->fw_info[8]; + buf[4] = ilits->fw_info[9]; + buf[5] = ilits->fw_info[10]; + buf[6] = ilits->fw_info[11]; + buf[7] = ilits->fw_info[12]; + buf[8] = ilits->fw_info[14]; + buf[11] = buf[7]; + buf[12] = buf[8]; + goto out; + } + + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_TP_INFORMATION; + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Write tp info pre cmd failed\n"); + ret = -EINVAL; + goto out; + + } + + ret = ilits->wrapper(&cmd[1], sizeof(u8), buf, ilits->protocol->tp_info_len, ON, OFF); + if (ret < 0) { + ILI_ERR("Read tp info error\n"); + goto out; + } + + if (buf[0] != P5_X_GET_TP_INFORMATION) { + ILI_ERR("Invalid tp info\n"); + ret = -1; + goto out; + } + +out: + ilits->min_x = buf[1]; + ilits->min_y = buf[2]; + ilits->max_x = buf[4] << 8 | buf[3]; + ilits->max_y = buf[6] << 8 | buf[5]; + ilits->xch_num = buf[7]; + ilits->ych_num = buf[8]; + ilits->stx = buf[11]; + ilits->srx = buf[12]; + + ILI_INFO("TP Info: min_x = %d, min_y = %d, max_x = %d, max_y = %d\n", ilits->min_x, ilits->min_y, ilits->max_x, ilits->max_y); + ILI_INFO("TP Info: xch = %d, ych = %d, stx = %d, srx = %d\n", ilits->xch_num, ilits->ych_num, ilits->stx, ilits->srx); + return ret; +} + +static void ilitek_tddi_ic_check_protocol_ver(u32 pver) +{ + int i = 0; + + if (ilits->protocol->ver == pver) { + ILI_DBG("same procotol version, do nothing\n"); + return; + } + + for (i = 0; i < PROTOCL_VER_NUM - 1; i++) { + if (protocol_info[i].ver == pver) { + ilits->protocol = &protocol_info[i]; + ILI_INFO("update protocol version = %x\n", ilits->protocol->ver); + return; + } + } + + ILI_ERR("Not found a correct protocol version in list, use newest version\n"); + ilits->protocol = &protocol_info[PROTOCL_VER_NUM - 1]; +} + +int ili_ic_get_protocl_ver(void) +{ + int ret = 0; + u8 cmd[2] = {0}; + u8 buf[10] = {0}; + u32 ver; + + if (ilits->info_from_hex) { + buf[1] = ilits->fw_info[72]; + buf[2] = ilits->fw_info[73]; + buf[3] = ilits->fw_info[74]; + goto out; + } + + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_PROTOCOL_VERSION; + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Write protocol ver pre cmd failed\n"); + ret = -EINVAL; + goto out; + + } + + if (ilits->wrapper(&cmd[1], sizeof(u8), buf, ilits->protocol->pro_ver_len, ON, OFF)) { + ILI_ERR("Read protocol version error\n"); + ret = -EINVAL; + goto out; + } + + if (buf[0] != P5_X_GET_PROTOCOL_VERSION) { + ILI_ERR("Invalid protocol ver\n"); + ret = -1; + goto out; + } + +out: + ver = buf[1] << 16 | buf[2] << 8 | buf[3]; + + ilitek_tddi_ic_check_protocol_ver(ver); + + ILI_INFO("Protocol version = %d.%d.%d\n", ilits->protocol->ver >> 16, + (ilits->protocol->ver >> 8) & 0xFF, ilits->protocol->ver & 0xFF); + return ret; +} + +int ili_ic_get_info(void) +{ + int ret = 0; + + if (!atomic_read(&ilits->ice_stat)) { + ILI_ERR("ice mode doesn't enable\n"); + return -1; + } + + + if (ili_ice_mode_read(ilits->chip->pid_addr, &ilits->chip->pid, sizeof(u32)) < 0) + ILI_ERR("Read chip pid error\n"); + + if (ili_ice_mode_read(ilits->chip->otp_addr, &ilits->chip->otp_id, sizeof(u32)) < 0) + ILI_ERR("Read otp id error\n"); + if (ili_ice_mode_read(ilits->chip->ana_addr, &ilits->chip->ana_id, sizeof(u32)) < 0) + ILI_ERR("Read ana id error\n"); + + ilits->chip->pid = ilits->chip->pid; + ilits->chip->id = ilits->chip->pid >> 16; + ilits->chip->type = (ilits->chip->pid & 0x0000FF00) >> 8; + ilits->chip->ver = ilits->chip->pid & 0xFF; + ilits->chip->otp_id &= 0xFF; + ilits->chip->ana_id &= 0xFF; + + ILI_INFO("CHIP: PID = %x, ID = %x, TYPE = %x, VER = %x, OTP = %x, ANA = %x\n", + ilits->chip->pid, + ilits->chip->id, + ilits->chip->type, + ilits->chip->ver, + ilits->chip->otp_id, + ilits->chip->ana_id); + + ret = ilitek_tddi_ic_check_support(ilits->chip->pid, ilits->chip->id); + return ret; +} + +int ili_ic_dummy_check(void) +{ + int ret = 0; + u32 wdata = 0xA55A5AA5; + u32 rdata = 0; + + if (!atomic_read(&ilits->ice_stat)) { + ILI_ERR("ice mode doesn't enable\n"); + return -1; + } + + if (ili_ice_mode_write(WDT9_DUMMY2, wdata, sizeof(u32)) < 0) + ILI_ERR("Write dummy error\n"); + + + if (ili_ice_mode_read(WDT9_DUMMY2, &rdata, sizeof(u32)) < 0) + ILI_ERR("Read dummy error\n"); + + if (rdata != wdata){ + ILI_ERR("Dummy check incorrect, rdata = %x wdata = %x \n", rdata, wdata); + return -1; + } + ILI_INFO("Ilitek IC check successe\n"); + + return ret; +} + +static struct ilitek_ic_info chip; + +void ili_ic_init(void) +{ + chip.pid_addr = TDDI_PID_ADDR; + chip.pc_counter_addr = TDDI_PC_COUNTER_ADDR; + chip.pc_latch_addr = TDDI_PC_LATCH_ADDR; + chip.otp_addr = TDDI_OTP_ID_ADDR; + chip.ana_addr = TDDI_ANA_ID_ADDR; + chip.reset_addr = TDDI_CHIP_RESET_ADDR; + + ilits->protocol = &protocol_info[PROTOCL_VER_NUM - 1]; + ilits->chip = &chip; +} diff --git a/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_mp.c b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_mp.c new file mode 100644 index 000000000000..e0140bcd1093 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_mp.c @@ -0,0 +1,3338 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek.h" + +#define VALUE 0 +#define RETRY_COUNT 3 +#define INT_CHECK 0 +#define POLL_CHECK 1 + +#define BENCHMARK 1 +#define NODETYPE 1 + +#define TYPE_BENCHMARK 0 +#define TYPE_NO_JUGE 1 +#define TYPE_JUGE 2 + +#define NORMAL_CSV_PASS_NAME "mp_pass" +#define NORMAL_CSV_FAIL_NAME "mp_fail" +#define NORMAL_CSV_WARNING_NAME "mp_warning" + +#define CSV_FILE_SIZE (1 * M) + +#define PARSER_MAX_CFG_BUF (512 * 3) +#define PARSER_MAX_KEY_NUM (600 * 3) +#define PARSER_MAX_KEY_NAME_LEN 100 +#define PARSER_MAX_KEY_VALUE_LEN 2000 +#define MAX_SECTION_NUM 100 +#define BENCHMARK_KEY_NAME "benchmark_data" +#define NODE_TYPE_KEY_NAME "node type" +#define INI_ERR_OUT_OF_LINE -1 + +#define CMD_MUTUAL_DAC 0x1 +#define CMD_MUTUAL_BG 0x2 +#define CMD_MUTUAL_SIGNAL 0x3 +#define CMD_MUTUAL_NO_BK 0x5 +#define CMD_MUTUAL_HAVE_BK 0x8 +#define CMD_MUTUAL_BK_DAC 0x10 +#define CMD_SELF_DAC 0xC +#define CMD_SELF_BG 0xF +#define CMD_SELF_SIGNAL 0xD +#define CMD_SELF_NO_BK 0xE +#define CMD_SELF_HAVE_BK 0xB +#define CMD_SELF_BK_DAC 0x11 +#define CMD_KEY_DAC 0x14 +#define CMD_KEY_BG 0x16 +#define CMD_KEY_NO_BK 0x7 +#define CMD_KEY_HAVE_BK 0x15 +#define CMD_KEY_OPEN 0x12 +#define CMD_KEY_SHORT 0x13 +#define CMD_ST_DAC 0x1A +#define CMD_ST_BG 0x1C +#define CMD_ST_NO_BK 0x17 +#define CMD_ST_HAVE_BK 0x1B +#define CMD_ST_OPEN 0x18 +#define CMD_TX_SHORT 0x19 +#define CMD_RX_SHORT 0x4 +#define CMD_RX_OPEN 0x6 +#define CMD_TX_RX_DELTA 0x1E +#define CMD_CM_DATA 0x9 +#define CMD_CS_DATA 0xA +#define CMD_TRCRQ_PIN 0x20 +#define CMD_RESX2_PIN 0x21 +#define CMD_MUTUAL_INTEGRA_TIME 0x22 +#define CMD_SELF_INTEGRA_TIME 0x23 +#define CMD_KEY_INTERGRA_TIME 0x24 +#define CMD_ST_INTERGRA_TIME 0x25 +#define CMD_PEAK_TO_PEAK 0x1D +#define CMD_GET_TIMING_INFO 0x30 +#define CMD_DOZE_P2P 0x32 +#define CMD_DOZE_RAW 0x33 +#define CMD_PIN_TEST 0x61 + +#define MP_DATA_PASS 0 +#define MP_DATA_FAIL -1 + +#define Mathabs(x) ({ \ + long ret; \ + if (sizeof(x) == sizeof(long)) { \ + long __x = (x); \ + ret = (__x < 0) ? -__x : __x; \ + } else { \ + int __x = (x); \ + ret = (__x < 0) ? -__x : __x; \ + } \ + ret; \ + }) + +#define DUMP(fmt, arg...) \ + do { \ + if (debug_en) \ + pr_cont(fmt, ##arg); \ + } while (0) + +static struct ini_file_data { + char section_name[PARSER_MAX_KEY_NAME_LEN]; + char key_name[PARSER_MAX_KEY_NAME_LEN]; + char key_value[PARSER_MAX_KEY_VALUE_LEN]; + int section_len; + int key_name_len; + int key_val_len; +} *ini_info; + +enum open_test_node_type { + NO_COMPARE = 0x00, /* Not A Area, No Compare */ + AA_Area = 0x01, /* AA Area, Compare using Charge_AA */ + Border_Area = 0x02, /* Border Area, Compare using Charge_Border */ + Notch = 0x04, /* Notch Area, Compare using Charge_Notch */ + Round_Corner = 0x08, /* Round Corner, No Compare */ + Skip_Micro = 0x10 /* Skip_Micro, No Compare */ +}; + +enum mp_test_catalog { + MUTUAL_TEST = 0, + SELF_TEST = 1, + KEY_TEST = 2, + ST_TEST = 3, + TX_RX_DELTA = 4, + UNTOUCH_P2P = 5, + PIXEL = 6, + OPEN_TEST = 7, + PEAK_TO_PEAK_TEST = 8, + SHORT_TEST = 9, + PIN_TEST = 10, +}; + +struct mp_test_P540_open { + s32 *tdf_700; + s32 *tdf_250; + s32 *tdf_200; + s32 *cbk_700; + s32 *cbk_250; + s32 *cbk_200; + s32 *charg_rate; + s32 *full_Open; + s32 *dac; +}; + +struct mp_test_open_c { + s32 *cap_dac; + s32 *cap_raw; + s32 *dcl_cap; +}; + +static struct open_test_spec { + int tvch; + int tvcl; + int gain; +} open_spec; + +static struct core_mp_test_data { + u32 chip_pid; + u16 chip_id; + u8 chip_type; + u8 chip_ver; + u32 fw_ver; + u32 protocol_ver; + u32 core_ver; + char ini_date[128]; + char ini_ver[64]; + int no_bk_shift; + bool retry; + bool m_signal; + bool m_dac; + bool s_signal; + bool s_dac; + bool key_dac; + bool st_dac; + bool p_no_bk; + bool p_has_bk; + bool open_integ; + bool open_cap; + bool isLongV; + bool td_retry; + + int cdc_len; + int xch_len; + int ych_len; + int stx_len; + int srx_len; + int key_len; + int st_len; + int frame_len; + int mp_items; + int final_result; + int short_varia; + + u32 overlay_start_addr; + u32 overlay_end_addr; + u32 mp_flash_addr; + u32 mp_size; + u8 dma_trigger_enable; + + /* Tx/Rx threshold & buffer */ + int TxDeltaMax; + int TxDeltaMin; + int RxDeltaMax; + int RxDeltaMin; + s32 *tx_delta_buf; + s32 *rx_delta_buf; + s32 *tx_max_buf; + s32 *tx_min_buf; + s32 *rx_max_buf; + s32 *rx_min_buf; + + int tdf; + int busy_cdc; + bool ctrl_lcm; + bool lost_benchmark; +} core_mp = {0}; + +struct mp_test_items { + /* The description must be the same as ini's section name */ + char *desp; + char *result; + int catalog; + u8 cmd; + u8 spec_option; + u8 type_option; + bool run; + bool lcm; + int max; + int max_res; + int item_result; + int min; + int min_res; + int frame_count; + int trimmed_mean; + int lowest_percentage; + int highest_percentage; + int v_tdf_1; + int v_tdf_2; + int h_tdf_1; + int h_tdf_2; + int goldenmode; + u8 delay_time; + u8 test_int_pin; + u8 int_pulse_test; + s32 *result_buf; + s32 *buf; + s32 *max_buf; + s32 *min_buf; + s32 *bench_mark_max; + s32 *bench_mark_min; + s32 *node_type; + int (*do_test)(int index); +}; + +#define MP_TEST_ITEM 50 +static struct mp_test_items tItems[MP_TEST_ITEM] = { + {.desp = "baseline data(bg)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_BG, .lcm = ON}, + {.desp = "untouch signal data(bg-raw-4096) - mutual", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_SIGNAL, .lcm = ON}, + {.desp = "manual bk data(mutual)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_BK_DAC, .lcm = ON}, + {.desp = "calibration data(dac) - self", .catalog = SELF_TEST, .cmd = CMD_SELF_DAC, .lcm = ON}, + {.desp = "baselin data(bg,self_tx,self_r)", .catalog = SELF_TEST, .cmd = CMD_SELF_BG, .lcm = ON}, + {.desp = "untouch signal data(bg-raw-4096) - self", .catalog = SELF_TEST, .cmd = CMD_SELF_SIGNAL, .lcm = ON}, + {.desp = "raw data(no bk) - self", .catalog = SELF_TEST, .cmd = CMD_SELF_NO_BK, .lcm = ON}, + {.desp = "raw data(have bk) - self", .catalog = SELF_TEST, .cmd = CMD_SELF_HAVE_BK, .lcm = ON}, + {.desp = "manual bk dac data(self_tx,self_rx)", .catalog = SELF_TEST, .cmd = CMD_SELF_BK_DAC, .lcm = ON}, + {.desp = "calibration data(dac/icon)", .catalog = KEY_TEST, .cmd = CMD_KEY_DAC, .lcm = ON}, + {.desp = "key baseline data", .catalog = KEY_TEST, .cmd = CMD_KEY_BG, .lcm = ON}, + {.desp = "key raw data", .catalog = KEY_TEST, .cmd = CMD_KEY_NO_BK, .lcm = ON}, + {.desp = "key raw bk dac", .catalog = KEY_TEST, .cmd = CMD_KEY_HAVE_BK, .lcm = ON}, + {.desp = "key raw open test", .catalog = KEY_TEST, .cmd = CMD_KEY_OPEN, .lcm = ON}, + {.desp = "key raw short test", .catalog = KEY_TEST, .cmd = CMD_KEY_SHORT, .lcm = ON}, + {.desp = "st calibration data(dac)", .catalog = ST_TEST, .cmd = CMD_ST_DAC, .lcm = ON}, + {.desp = "st baseline data(bg)", .catalog = ST_TEST, .cmd = CMD_ST_BG, .lcm = ON}, + {.desp = "st raw data(no bk)", .catalog = ST_TEST, .cmd = CMD_ST_NO_BK, .lcm = ON}, + {.desp = "st raw(have bk)", .catalog = ST_TEST, .cmd = CMD_ST_HAVE_BK, .lcm = ON}, + {.desp = "st open data", .catalog = ST_TEST, .cmd = CMD_ST_OPEN, .lcm = ON}, + {.desp = "tx short test", .catalog = MUTUAL_TEST, .cmd = CMD_TX_SHORT, .lcm = ON}, + {.desp = "rx open", .catalog = MUTUAL_TEST, .cmd = CMD_RX_OPEN, .lcm = ON}, + {.desp = "untouch cm data", .catalog = MUTUAL_TEST, .cmd = CMD_CM_DATA, .lcm = ON}, + {.desp = "untouch cs data", .catalog = MUTUAL_TEST, .cmd = CMD_CS_DATA, .lcm = ON}, + {.desp = "tx/rx delta", .catalog = TX_RX_DELTA, .cmd = CMD_TX_RX_DELTA, .lcm = ON}, + {.desp = "untouch peak to peak", .catalog = UNTOUCH_P2P, .cmd = CMD_MUTUAL_SIGNAL, .lcm = ON}, + {.desp = "pixel raw (no bk)", .catalog = PIXEL, .cmd = CMD_MUTUAL_NO_BK, .lcm = ON}, + {.desp = "pixel raw (have bk)", .catalog = PIXEL, .cmd = CMD_MUTUAL_HAVE_BK, .lcm = ON}, + {.desp = "noise peak to peak(cut panel)", .catalog = PEAK_TO_PEAK_TEST, .lcm = ON}, + {.desp = "open test(integration)", .catalog = OPEN_TEST, .cmd = CMD_RX_SHORT, .lcm = ON}, + {.desp = "open test(cap)", .catalog = OPEN_TEST, .cmd = CMD_RX_SHORT, .lcm = ON}, + /* Following is the new test items for protocol 5.4.0 above */ + {.desp = "pin test ( int and rst )", .catalog = PIN_TEST, .cmd = CMD_PIN_TEST, .lcm = ON}, + {.desp = "noise peak to peak(with panel)", .catalog = PEAK_TO_PEAK_TEST, .lcm = ON}, + {.desp = "noise peak to peak(ic only)", .catalog = PEAK_TO_PEAK_TEST, .cmd = CMD_PEAK_TO_PEAK, .lcm = ON}, + {.desp = "open test(integration)_sp", .catalog = OPEN_TEST, .lcm = ON}, + {.desp = "raw data(no bk)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_NO_BK, .lcm = ON}, + {.desp = "raw data(have bk)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_HAVE_BK, .lcm = ON}, + {.desp = "calibration data(dac)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_DAC, .lcm = ON}, + {.desp = "short test -ili9881", .catalog = SHORT_TEST, .cmd = CMD_RX_SHORT, .lcm = ON}, + {.desp = "short test", .catalog = SHORT_TEST, .lcm = ON}, + {.desp = "doze raw data", .catalog = MUTUAL_TEST, .lcm = ON}, + {.desp = "doze peak to peak", .catalog = PEAK_TO_PEAK_TEST, .lcm = ON}, + {.desp = "open test_c", .catalog = OPEN_TEST, .lcm = ON}, + {.desp = "touch deltac", .catalog = MUTUAL_TEST, .lcm = ON}, + /* LCM OFF TEST */ + {.desp = "raw data(have bk) (lcm off)", .catalog = MUTUAL_TEST, .lcm = OFF}, + {.desp = "raw data(no bk) (lcm off)", .catalog = MUTUAL_TEST, .lcm = OFF}, + {.desp = "noise peak to peak(with panel) (lcm off)", .catalog = PEAK_TO_PEAK_TEST, .lcm = OFF}, + {.desp = "noise peak to peak(ic only) (lcm off)", .catalog = PEAK_TO_PEAK_TEST, .lcm = OFF}, + {.desp = "raw data_td (lcm off)", .catalog = MUTUAL_TEST, .lcm = OFF}, + {.desp = "peak to peak_td (lcm off)", .catalog = PEAK_TO_PEAK_TEST, .lcm = OFF}, +}; + +static struct run_index { + int count; + int index[MP_TEST_ITEM]; +} ri; + +static s32 *frame_buf; +static s32 *key_buf; +static s32 *frame1_cbk700, *frame1_cbk250, *frame1_cbk200; +static s32 *cap_dac, *cap_raw; +static int g_ini_items = 0; +static char csv_name[128] = {0}; +static char seq_item[MAX_SECTION_NUM][PARSER_MAX_KEY_NAME_LEN] = {{0}}; + +static int isspace_t(int x) +{ + if (x == ' ' || x == '\t' || x == '\n' || + x == '\f' || x == '\b' || x == '\r') + return 1; + else + return 0; +} + +static void dump_benchmark_data(s32 *max_ptr, s32 *min_ptr) +{ + int i; + + if (debug_en) { + ILI_INFO("Dump Benchmark Max\n"); + + for (i = 0; i < core_mp.frame_len; i++) { + pr_cont("%d, ", max_ptr[i]); + if (i % core_mp.xch_len == core_mp.xch_len - 1) + pr_cont("\n"); + } + + pr_cont("Dump Denchmark Min\n"); + + for (i = 0; i < core_mp.frame_len; i++) { + pr_cont("%d, ", min_ptr[i]); + if (i % core_mp.xch_len == core_mp.xch_len - 1) + pr_cont("\n"); + } + } +} + +static void dump_node_type_buffer(s32 *node_ptr, u8 *name) +{ + int i; + + if (debug_en) { + ILI_INFO("Dump NodeType\n"); + for (i = 0; i < core_mp.frame_len; i++) { + pr_cont("%d, ", node_ptr[i]); + if (i % core_mp.xch_len == core_mp.xch_len-1) + pr_cont("\n"); + } + } +} + +static int parser_get_ini_key_value(char *section, char *key, char *value) +{ + int i = 0; + int ret = -2; + + for (i = 0; i < g_ini_items; i++) { + if (ipio_strcmp(section, ini_info[i].section_name) != 0) + continue; + + if (ipio_strcmp(key, ini_info[i].key_name) == 0) { + ipio_memcpy(value, ini_info[i].key_value, ini_info[i].key_val_len, PARSER_MAX_KEY_VALUE_LEN); + ILI_DBG("(key: %s, value:%s) => (ini key: %s, val: %s)\n", + key, + value, + ini_info[i].key_name, + ini_info[i].key_value); + ret = 0; + break; + } + } + return ret; +} + +static void parser_ini_nodetype(s32 *type_ptr, char *desp, int frame_len) +{ + int i = 0, j = 0, index1 = 0, temp, count = 0; + char str[512] = {0}, record = ','; + + for (i = 0; i < g_ini_items; i++) { + if (!strstr(ini_info[i].section_name, desp) || + ipio_strcmp(ini_info[i].key_name, NODE_TYPE_KEY_NAME) != 0) { + continue; + } + + record = ','; + for (j = 0, index1 = 0; j <= ini_info[i].key_val_len; j++) { + if (ini_info[i].key_value[j] == ';' || j == ini_info[i].key_val_len) { + if (record != '.') { + memset(str, 0, sizeof(str)); + ipio_memcpy(str, &ini_info[i].key_value[index1], (j - index1), sizeof(str)); + temp = ili_katoi(str); + + /* Over boundary, end to calculate. */ + if (count >= frame_len) { + ILI_ERR("count(%d) is larger than frame length, break\n", count); + break; + } + type_ptr[count] = temp; + count++; + } + record = ini_info[i].key_value[j]; + index1 = j + 1; + } + } + } +} + +static void parser_ini_benchmark(s32 *max_ptr, s32 *min_ptr, int8_t type, char *desp, int frame_len) +{ + int i = 0, j = 0, index1 = 0, temp, count = 0; + char str[512] = {0}, record = ','; + s32 data[4]; + char benchmark_str[256] = {0}; + + /* format complete string from the name of section "_Benchmark_Data". */ + snprintf(benchmark_str,sizeof(benchmark_str), "%s%s%s", desp, "_", BENCHMARK_KEY_NAME); + + for (i = 0; i < g_ini_items; i++) { + if ((ipio_strcmp(ini_info[i].section_name, benchmark_str) != 0) || + ipio_strcmp(ini_info[i].key_name, BENCHMARK_KEY_NAME) != 0) + continue; + record = ','; + for (j = 0, index1 = 0; j <= ini_info[i].key_val_len; j++) { + if (ini_info[i].key_value[j] == ',' || ini_info[i].key_value[j] == ';' || + ini_info[i].key_value[j] == '.' || j == ini_info[i].key_val_len) { + + if (record != '.') { + memset(str, 0, sizeof(str)); + ipio_memcpy(str, &ini_info[i].key_value[index1], (j - index1), sizeof(str)); + temp = ili_katoi(str); + data[(count % 4)] = temp; + + /* Over boundary, end to calculate. */ + if ((count / 4) >= frame_len) { + ILI_ERR("count (%d) is larger than frame length, break\n", (count / 4)); + break; + } + + if ((count % 4) == 3) { + if (data[0] == 1) { + if (type == VALUE) { + max_ptr[count/4] = data[1] + data[2]; + min_ptr[count/4] = data[1] - data[3]; + } else { + max_ptr[count/4] = data[1] + (data[1] * data[2]) / 100; + min_ptr[count/4] = data[1] - (data[1] * data[3]) / 100; + } + } else { + max_ptr[count/4] = INT_MAX; + min_ptr[count/4] = INT_MIN; + } + } + count++; + } + record = ini_info[i].key_value[j]; + index1 = j + 1; + } + } + } +} + +static int parser_get_tdf_value(char *str, int catalog) +{ + u32 i, ans, index = 0, flag = 0, count = 0, size = 0; + char s[10] = {0}; + + if (!str) { + ILI_ERR("String is null\n"); + return -1; + } + + size = strlen(str); + for (i = 0, count = 0; i < size; i++) { + if (str[i] == '.') { + flag = 1; + continue; + } + s[index++] = str[i]; + if (flag) + count++; + } + ans = ili_katoi(s); + + /* Multiply by 100 to shift out of decimal point */ + if (catalog == SHORT_TEST) { + if (count == 0) + ans = ans * 100; + else if (count == 1) + ans = ans * 10; + } + + return ans; +} + +static int parser_get_u8_array(char *key, u8 *buf, u16 base, int len) +{ + char *s = key; + char *pToken; + int ret, conut = 0; + long s_to_long = 0; + + if (strlen(s) == 0 || len <= 0) { + ILI_ERR("Can't find any characters inside buffer\n"); + return -1; + } + + /* + * @base: The number base to use. The maximum supported base is 16. If base is + * given as 0, then the base of the string is automatically detected with the + * conventional semantics - If it begins with 0x the number will be parsed as a + * hexadecimal (case insensitive), if it otherwise begins with 0, it will be + * parsed as an octal number. Otherwise it will be parsed as a decimal. + */ + if (isspace_t((int)(unsigned char)*s) == 0) { + while ((pToken = strsep(&s, ",")) != NULL) { + ret = kstrtol(pToken, base, &s_to_long); + if (ret == 0) + buf[conut] = s_to_long; + else + ILI_INFO("convert string too long, ret = %d\n", ret); + conut++; + + if (conut >= len) + break; + } + } + + return conut; +} + +static int parser_get_int_data(char *section, char *keyname, char *rv, int rv_len) +{ + int len = 0; + char value[512] = { 0 }; + + if (rv == NULL || section == NULL || keyname == NULL) { + ILI_ERR("Parameters are invalid\n"); + return -EINVAL; + } + + /* return a white-space string if get nothing */ + if (parser_get_ini_key_value(section, keyname, value) < 0) { + snprintf(rv, rv_len, "%s", value); + return 0; + } + + len = snprintf(rv, rv_len, "%s", value); + return len; +} + +/* Count the number of each line and assign the content to tmp buffer */ +static int parser_get_ini_phy_line(char *data, char *buffer, int maxlen) +{ + int i = 0; + int j = 0; + int iRetNum = -1; + char ch1 = '\0'; + + for (i = 0, j = 0; i < maxlen; j++) { + ch1 = data[j]; + iRetNum = j + 1; + if (ch1 == '\n' || ch1 == '\r') { /* line end */ + ch1 = data[j + 1]; + if (ch1 == '\n' || ch1 == '\r') + iRetNum++; + break; + } else if (ch1 == 0x00) { + //iRetNum = -1; + break; /* file end */ + } + + buffer[i++] = ch1; + } + + buffer[i] = '\0'; + return iRetNum; +} + +static char *parser_ini_str_trim_r(char *buf) +{ + int len, i; + char *tmp = NULL; + char x[512] = {0}; + char *y = NULL; + char *empty = ""; + + len = strlen(buf); + + if (len < sizeof(x)) { + tmp = x; + goto copy; + } + + y = kzalloc(len, GFP_KERNEL); + if (ERR_ALLOC_MEM(y)) { + ILI_ERR("Failed to allocate tmp buf\n"); + return empty; + } + tmp = y; + +copy: + for (i = 0; i < len; i++) { + if (buf[i] != ' ') + break; + } + + if (i < len) + strncpy(tmp, (buf + i), (len - i)); + + strncpy(buf, tmp, len); + ipio_kfree((void **)&y); + return buf; +} + +static int parser_get_ini_phy_data(char *data, int fsize) +{ + int i, n = 0, ret = 0, banchmark_flag = 0, empty_section, nodetype_flag = 0; + int offset = 0, isEqualSign = 0, scount = 0; + char *ini_buf = NULL, *tmpSectionName = NULL; + char M_CFG_SSL = '['; + char M_CFG_SSR = ']'; +/* char M_CFG_NIS = ':'; */ + char M_CFG_NTS = '#'; + char M_CFG_EQS = '='; + + if (data == NULL) { + ILI_ERR("INI data is NULL\n"); + ret = -EINVAL; + goto out; + } + + ini_buf = kzalloc((PARSER_MAX_CFG_BUF + 1) * sizeof(char), GFP_KERNEL); + if (ERR_ALLOC_MEM(ini_buf)) { + ILI_ERR("Failed to allocate ini_buf memory, %ld\n", PTR_ERR(ini_buf)); + ret = -ENOMEM; + goto out; + } + + tmpSectionName = kzalloc((PARSER_MAX_CFG_BUF + 1) * sizeof(char), GFP_KERNEL); + if (ERR_ALLOC_MEM(tmpSectionName)) { + ILI_ERR("Failed to allocate tmpSectionName memory, %ld\n", PTR_ERR(tmpSectionName)); + ret = -ENOMEM; + goto out; + } + + memset(seq_item, 0, MP_TEST_ITEM * PARSER_MAX_KEY_NAME_LEN * sizeof(char)); + + while (true) { + banchmark_flag = 0; + empty_section = 0; + nodetype_flag = 0; + if (g_ini_items > PARSER_MAX_KEY_NUM) { + ILI_ERR("MAX_KEY_NUM: Out of length\n"); + goto out; + } + + if (offset >= fsize) + goto out;/*over size*/ + + n = parser_get_ini_phy_line(data + offset, ini_buf, PARSER_MAX_CFG_BUF); + if (n < 0) { + ILI_ERR("End of Line\n"); + goto out; + } + + offset += n; + + n = strlen(parser_ini_str_trim_r(ini_buf)); + + if (n == 0 || ini_buf[0] == M_CFG_NTS) + continue; + + /* Get section names */ + if (n > 2 && ((ini_buf[0] == M_CFG_SSL && ini_buf[n - 1] != M_CFG_SSR))) { + ILI_ERR("Bad Section: %s\n", ini_buf); + ret = -EINVAL; + goto out; + } else { + if (ini_buf[0] == M_CFG_SSL) { + ini_info[g_ini_items].section_len = n - 2; + if (ini_info[g_ini_items].section_len > PARSER_MAX_KEY_NAME_LEN) { + ILI_ERR("MAX_KEY_NAME_LEN: Out Of Length\n"); + ret = INI_ERR_OUT_OF_LINE; + goto out; + } + + if (scount > MAX_SECTION_NUM) { + ILI_ERR("seq_item is over than its define (%d), abort\n", scount); + ret = INI_ERR_OUT_OF_LINE; + goto out; + } + + ini_buf[n - 1] = 0x00; + strncpy((char *)tmpSectionName, ini_buf + 1, (PARSER_MAX_CFG_BUF + 1) * sizeof(char)); + banchmark_flag = 0; + nodetype_flag = 0; + strncpy(seq_item[scount], tmpSectionName, PARSER_MAX_KEY_NAME_LEN); + scount++; + ILI_DBG("Section Name: %s, Len: %d, offset = %d\n", seq_item[scount], n - 2, offset); + continue; + } + } + + /* copy section's name without square brackets to its real buffer */ + strncpy(ini_info[g_ini_items].section_name, tmpSectionName, (PARSER_MAX_KEY_NAME_LEN * sizeof(char))); + ini_info[g_ini_items].section_len = strlen(tmpSectionName); + + isEqualSign = 0; + for (i = 0; i < n; i++) { + if (ini_buf[i] == M_CFG_EQS) { + isEqualSign = i; + break; + } + if (ini_buf[i] == M_CFG_SSL || ini_buf[i] == M_CFG_SSR) { + empty_section = 1; + break; + } + } + + if (isEqualSign == 0) { + if (empty_section) + continue; + + if (strstr(ini_info[g_ini_items].section_name, BENCHMARK_KEY_NAME) != NULL) { + banchmark_flag = 1; + isEqualSign = -1; + } else if (strstr(ini_info[g_ini_items].section_name, NODE_TYPE_KEY_NAME) != NULL) { + nodetype_flag = 1; + isEqualSign = -1; + } else { + continue; + } + } + + if (banchmark_flag) { + ini_info[g_ini_items].key_name_len = strlen(BENCHMARK_KEY_NAME); + strncpy(ini_info[g_ini_items].key_name, BENCHMARK_KEY_NAME, (PARSER_MAX_KEY_NAME_LEN * sizeof(char))); + ini_info[g_ini_items].key_val_len = n; + } else if (nodetype_flag) { + ini_info[g_ini_items].key_name_len = strlen(NODE_TYPE_KEY_NAME); + strncpy(ini_info[g_ini_items].key_name, NODE_TYPE_KEY_NAME, (PARSER_MAX_KEY_NAME_LEN * sizeof(char))); + ini_info[g_ini_items].key_val_len = n; + } else{ + ini_info[g_ini_items].key_name_len = isEqualSign; + if (ini_info[g_ini_items].key_name_len > PARSER_MAX_KEY_NAME_LEN) { + /* ret = CFG_ERR_OUT_OF_LEN; */ + ILI_ERR("MAX_KEY_NAME_LEN: Out Of Length\n"); + ret = INI_ERR_OUT_OF_LINE; + goto out; + } + + ipio_memcpy(ini_info[g_ini_items].key_name, ini_buf, + ini_info[g_ini_items].key_name_len, PARSER_MAX_KEY_NAME_LEN); + ini_info[g_ini_items].key_val_len = n - isEqualSign - 1; + } + + if (ini_info[g_ini_items].key_val_len > PARSER_MAX_KEY_VALUE_LEN) { + ILI_ERR("MAX_KEY_VALUE_LEN: Out Of Length\n"); + ret = INI_ERR_OUT_OF_LINE; + goto out; + } + + ipio_memcpy(ini_info[g_ini_items].key_value, + ini_buf + isEqualSign + 1, ini_info[g_ini_items].key_val_len, PARSER_MAX_KEY_VALUE_LEN); + + ILI_DBG("%s = %s\n", ini_info[g_ini_items].key_name, + ini_info[g_ini_items].key_value); + + g_ini_items++; + } +out: + ipio_kfree((void **)&ini_buf); + ipio_kfree((void **)&tmpSectionName); + return ret; +} + +static int ilitek_tddi_mp_ini_parser(const char *path) +{ + int i, ret = 0, fsize = 0; + char *tmp = NULL; + const struct firmware *ini = NULL; + struct file *f = NULL; + struct inode *inode; + mm_segment_t old_fs; + loff_t pos = 0; + + ILI_INFO("ini file path = %s\n", path); + + f = filp_open(path, O_RDONLY, 644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open ini file at %ld, try to request_firmware\n", PTR_ERR(f)); + f = NULL; + path = ilits->md_ini_rq_path; + ILI_INFO("request path = %s\n", path); + if (request_firmware(&ini, path, ilits->dev) < 0) { + ILI_ERR("Request ini file failed\n"); + return -EINVAL; + } + } + + if (f != NULL) { +#if KERNEL_VERSION(3, 18, 0) >= LINUX_VERSION_CODE + inode = f->f_dentry->d_inode; +#else + inode = f->f_path.dentry->d_inode; +#endif + fsize = inode->i_size; + } else { + fsize = ini->size; + } + + ILI_INFO("ini file size = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is invaild\n"); + ret = -EINVAL; + goto out; + } + + tmp = vmalloc(fsize+1); + if (ERR_ALLOC_MEM(tmp)) { + ILI_ERR("Failed to allocate tmp memory, %ld\n", PTR_ERR(tmp)); + ret = -ENOMEM; + goto out; + } + + if (f != NULL) { + old_fs = get_fs(); + set_fs(get_ds()); + vfs_read(f, tmp, fsize, &pos); + set_fs(old_fs); + tmp[fsize] = 0x0; + } else { + memcpy(tmp, ini->data, fsize); + } + + g_ini_items = 0; + + /* Initialise ini strcture */ + for (i = 0; i < PARSER_MAX_KEY_NUM; i++) { + memset(ini_info[i].section_name, 0, PARSER_MAX_KEY_NAME_LEN); + memset(ini_info[i].key_name, 0, PARSER_MAX_KEY_NAME_LEN); + memset(ini_info[i].key_value, 0, PARSER_MAX_KEY_VALUE_LEN); + ini_info[i].section_len = 0; + ini_info[i].key_name_len = 0; + ini_info[i].key_val_len = 0; + } + + /* change all characters to lower case */ + for (i = 0; i < fsize; i++) + tmp[i] = tolower(tmp[i]); + + ret = parser_get_ini_phy_data(tmp, fsize); + if (ret < 0) { + ILI_ERR("Failed to get physical ini data, ret = %d\n", ret); + goto out; + } + + ILI_INFO("Parsed ini file done\n"); +out: + ipio_vfree((void **)&tmp); + + if (f != NULL) + filp_close(f, NULL); + else + release_firmware(ini); + + return ret; +} + +static void run_pixel_test(int index) +{ + int i, x, y; + s32 *p_comb = frame_buf; + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int tmp[4] = { 0 }, max = 0; + int shift = y * core_mp.xch_len; + int centre = p_comb[shift + x]; + + /* + * if its position is in corner, the number of point + * we have to minus is around 2 to 3. + */ + if (y == 0 && x == 0) { + tmp[0] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[1] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y == (core_mp.ych_len - 1) && x == 0) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y == 0 && x == (core_mp.xch_len - 1)) { + tmp[0] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + } else if (y == (core_mp.ych_len - 1) && x == (core_mp.xch_len - 1)) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + } else if (y == 0 && x != 0) { + tmp[0] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[2] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y != 0 && x == 0) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + tmp[2] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + + } else if (y == (core_mp.ych_len - 1) && x != 0) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[2] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y != 0 && x == (core_mp.xch_len - 1)) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[2] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + } else { + /* middle minus four directions */ + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[2] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[3] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } + + max = tmp[0]; + + for (i = 0; i < 4; i++) { + if (tmp[i] > max) + max = tmp[i]; + } + + tItems[index].buf[shift + x] = max; + } + } +} + +static void run_untouch_p2p_test(int index) +{ + int x, y; + s32 *p_comb = frame_buf; + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int shift = y * core_mp.xch_len; + + if (p_comb[shift + x] > tItems[index].max_buf[shift + x]) + tItems[index].max_buf[shift + x] = p_comb[shift + x]; + + if (p_comb[shift + x] < tItems[index].min_buf[shift + x]) + tItems[index].min_buf[shift + x] = p_comb[shift + x]; + + tItems[index].buf[shift + x] = + tItems[index].max_buf[shift + x] - tItems[index].min_buf[shift + x]; + } + } +} + +static int run_open_test(int index) +{ + int i, x, y, k, ret = 0; + int border_x[] = {-1, 0, 1, 1, 1, 0, -1, -1}; + int border_y[] = {-1, -1, -1, 0, 1, 1, 1, 0}; + s32 *p_comb = frame_buf; + + if (ipio_strcmp(tItems[index].desp, "open test(integration)") == 0) { + for (i = 0; i < core_mp.frame_len; i++) + tItems[index].buf[i] = p_comb[i]; + } else if (ipio_strcmp(tItems[index].desp, "open test(cap)") == 0) { + /* + * Each result is getting from a 3 by 3 grid depending on where the centre location is. + * So if the centre is at corner, the number of node grabbed from a grid will be different. + */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int sum = 0, avg = 0, count = 0; + int shift = y * core_mp.xch_len; + int centre = p_comb[shift + x]; + + for (k = 0; k < 8; k++) { + if (((y + border_y[k] >= 0) && (y + border_y[k] < core_mp.ych_len)) && + ((x + border_x[k] >= 0) && (x + border_x[k] < core_mp.xch_len))) { + count++; + sum += p_comb[(y + border_y[k]) * core_mp.xch_len + (x + border_x[k])]; + } + } + + avg = (sum + centre) / (count + 1); /* plus 1 because of centre */ + tItems[index].buf[shift + x] = (centre * 100) / avg; + } + } + } + return ret; +} + +static void run_tx_rx_delta_test(int index) +{ + int x, y; + s32 *p_comb = frame_buf; + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int shift = y * core_mp.xch_len; + + /* Tx Delta */ + if (y != (core_mp.ych_len - 1)) + core_mp.tx_delta_buf[shift + x] = Mathabs(p_comb[shift + x] - p_comb[(shift + 1) + x]); + + /* Rx Delta */ + if (x != (core_mp.xch_len - 1)) + core_mp.rx_delta_buf[shift + x] = Mathabs(p_comb[shift + x] - p_comb[shift + (x + 1)]); + } + } +} + +static char *get_date_time_str(void) +{ + struct timespec now_time; + struct rtc_time rtc_now_time; + static char time_data_buf[128] = { 0 }; + + getnstimeofday(&now_time); + rtc_time_to_tm(now_time.tv_sec, &rtc_now_time); + snprintf(time_data_buf, sizeof(time_data_buf), "%04d%02d%02d-%02d%02d%02d", + (rtc_now_time.tm_year + 1900), rtc_now_time.tm_mon + 1, + rtc_now_time.tm_mday, rtc_now_time.tm_hour, rtc_now_time.tm_min, + rtc_now_time.tm_sec); + + return time_data_buf; +} + +static void mp_print_csv_header(char *csv, int *csv_len, int *csv_line, int file_size) +{ + int i, seq, tmp_len = *csv_len, tmp_line = *csv_line; + + /* header must has 19 line*/ + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "==============================================================================\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "ILITek C-TP Utility V%s %x : Driver Sensor Test\n", DRIVER_VERSION, core_mp.chip_pid); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Confidentiality Notice:\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Any information of this tool is confidential and privileged.\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "@ ILI TECHNOLOGY CORP. All Rights Reserved.\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "==============================================================================\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Firmware Version ,0x%x\n", core_mp.fw_ver); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Panel information ,XCH=%d, YCH=%d\n", core_mp.xch_len, core_mp.ych_len); + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "INI Release Version ,%s\n", core_mp.ini_ver); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "INI Release Date ,%s\n", core_mp.ini_date); + tmp_line++; + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Test Item:\n"); + tmp_line++; + + for (seq = 0; seq < ri.count; seq++) { + i = ri.index[seq]; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), " ---%s\n", tItems[i].desp); + tmp_line++; + } + + while (tmp_line < 19) { + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "\n"); + tmp_line++; + } + + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "==============================================================================\n"); + + *csv_len = tmp_len; + *csv_line = tmp_line; +} + +static void mp_print_csv_tail(char *csv, int *csv_len, int file_size) +{ + int i, seq, tmp_len = *csv_len; + + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "==============================================================================\n"); + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Result_Summary \n"); + + for (seq = 0; seq < ri.count; seq++) { + i = ri.index[seq]; + if (tItems[i].item_result == MP_DATA_PASS) + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), " {%s} ,OK\n", tItems[i].desp); + else + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), " {%s} ,NG\n", tItems[i].desp); + } + + *csv_len = tmp_len; +} + +static void mp_print_csv_cdc_cmd(char *csv, int *csv_len, int index, int file_size) +{ + int i, slen = 0, tmp_len = *csv_len, size; + char str[128] = {0}; + char *open_sp_cmd[] = {"open dac", "open raw1", "open raw2", "open raw3"}; + char *open_c_cmd[] = {"open cap1 dac", "open cap1 raw"}; + char *name = tItems[index].desp; + + if (ipio_strcmp(name, "open test(integration)_sp") == 0) { + size = ARRAY_SIZE(open_sp_cmd); + for (i = 0; i < size; i++) { + slen = parser_get_int_data("pv5_4 command", open_sp_cmd[i], str, sizeof(str)); + if (slen < 0) + ILI_ERR("Failed to get CDC command %s from ini\n", open_sp_cmd[i]); + else + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "%s = ,%s\n", open_sp_cmd[i], str); + } + } else if (ipio_strcmp(name, "open test_c") == 0) { + size = ARRAY_SIZE(open_c_cmd); + for (i = 0; i < size; i++) { + slen = parser_get_int_data("pv5_4 command", open_c_cmd[i], str, sizeof(str)); + if (slen < 0) + ILI_ERR("Failed to get CDC command %s from ini\n", open_sp_cmd[i]); + else + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "%s = ,%s\n", open_c_cmd[i], str); + } + } else { + slen = parser_get_int_data("pv5_4 command", name, str, sizeof(str)); + if (slen < 0) + ILI_ERR("Failed to get CDC command %s from ini\n", name); + else + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "CDC command = ,%s\n", str); + + /* Print short parameters */ + if (ipio_strcmp(name, "short test") == 0) + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Variation = ,%d\n", core_mp.short_varia); + + /* Print td second command */ + if (core_mp.td_retry && (ipio_strcmp(name, "peak to peak_td (lcm off)") == 0)) { + name = "peak to peak_td (lcm off)_2"; + parser_get_int_data("pv5_4 command", name, str, sizeof(str)); + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "CDC command 2 = ,%s\n", str); + } + } + *csv_len = tmp_len; +} + +static void mp_compare_cdc_show_result(int index, s32 *tmp, char *csv, + int *csv_len, int type, s32 *max_ts, + s32 *min_ts, const char *desp, int file_zise) +{ + int x, y, tmp_len = *csv_len; + int mp_result = MP_DATA_PASS; + + if (ERR_ALLOC_MEM(tmp)) { + ILI_ERR("The data of test item is null (%p)\n", tmp); + mp_result = -EMP_INVAL; + goto out; + } + + /* print X raw only */ + for (x = 0; x < core_mp.xch_len; x++) { + if (x == 0) { + DUMP("\n %s ", desp); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "\n %s ,", desp); + } + DUMP(" X_%d ,", (x+1)); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " X_%d ,", (x+1)); + } + + DUMP("\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "\n"); + + for (y = 0; y < core_mp.ych_len; y++) { + DUMP(" Y_%d ,", (y+1)); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " Y_%d ,", (y+1)); + + for (x = 0; x < core_mp.xch_len; x++) { + int shift = y * core_mp.xch_len + x; + + /* In Short teset, we only identify if its value is low than min threshold. */ + if (tItems[index].catalog == SHORT_TEST) { + if (tmp[shift] < min_ts[shift]) { + DUMP(" #%7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "#%7d,", tmp[shift]); + mp_result = MP_DATA_FAIL; + } else { + DUMP(" %7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " %7d, ", tmp[shift]); + } + continue; + } + + if ((tmp[shift] <= max_ts[shift] && tmp[shift] >= min_ts[shift]) || (type != TYPE_JUGE)) { + if ((tmp[shift] == INT_MAX || tmp[shift] == INT_MIN) && (type == TYPE_BENCHMARK)) { + DUMP("%s", "BYPASS,"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "BYPASS,"); + } else { + DUMP(" %7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " %7d, ", tmp[shift]); + } + } else { + if (tmp[shift] > max_ts[shift]) { + DUMP(" *%7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "*%7d,", tmp[shift]); + } else { + DUMP(" #%7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "#%7d,", tmp[shift]); + } + mp_result = MP_DATA_FAIL; + } + } + DUMP("\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "\n"); + } + +out: + if (type == TYPE_JUGE) { + if (mp_result == MP_DATA_PASS) { + pr_info("\n Result : PASS\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "Result : PASS\n"); + } else { + pr_info("\n Result : FAIL\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "Result : FAIL\n"); + } + } + *csv_len = tmp_len; +} + +#define ABS(a, b) ((a > b) ? (a - b) : (b - a)) +#define ADDR(x, y) ((y * core_mp.xch_len) + (x)) + +static int compare_charge(s32 *charge_rate, int x, int y, s32 *inNodeType, + int Charge_AA, int Charge_Border, int Charge_Notch) +{ + int OpenThreadhold, tempY, tempX, ret, k; + int sx[8] = {-1, 0, 1, -1, 1, -1, 0, 1}; + int sy[8] = {-1, -1, -1, 0, 0, 1, 1, 1}; + + ret = charge_rate[ADDR(x, y)]; + + /*Setting Threadhold from node type */ + if (charge_rate[ADDR(x, y)] == 0) + return ret; + else if ((inNodeType[ADDR(x, y)] & AA_Area) == AA_Area) + OpenThreadhold = Charge_AA; + else if ((inNodeType[ADDR(x, y)] & Border_Area) == Border_Area) + OpenThreadhold = Charge_Border; + else if ((inNodeType[ADDR(x, y)] & Notch) == Notch) + OpenThreadhold = Charge_Notch; + else + return ret; + + /* compare carge rate with 3*3 node */ + /* by pass => 1.no compare 2.corner 3.Skip_Micro 4.full open fail node */ + for (k = 0; k < 8; k++) { + tempX = x + sx[k]; + tempY = y + sy[k]; + + /*out of range */ + if ((tempX < 0) || (tempX >= core_mp.xch_len) || (tempY < 0) || (tempY >= core_mp.ych_len)) + continue; + + if ((inNodeType[ADDR(tempX, tempY)] == NO_COMPARE) || ((inNodeType[ADDR(tempX, tempY)] & Round_Corner) == Round_Corner) || + ((inNodeType[ADDR(tempX, tempY)] & Skip_Micro) == Skip_Micro) || charge_rate[ADDR(tempX, tempY)] == 0) + continue; + + if ((charge_rate[ADDR(tempX, tempY)] - charge_rate[ADDR(x, y)]) > OpenThreadhold) + return OpenThreadhold; + } + return ret; +} + +static int full_open_rate_compare(s32 *full_open, s32 *cbk, int x, int y, s32 inNodeType, int full_open_rate) +{ + int ret = true; + + if ((inNodeType == NO_COMPARE) || ((inNodeType & Round_Corner) == Round_Corner)) + return true; + + if (full_open[ADDR(x, y)] < (cbk[ADDR(x, y)] * full_open_rate / 100)) + ret = false; + + return ret; +} + +static s32 open_sp_formula(int dac, int raw, int tvch, int tvcl) +{ + s32 ret = 0; + u16 id = core_mp.chip_id; + + if (id == ILI9881_CHIP) { + ret = ((dac * 10000 * 161 / 100) - (16384 / 2 - raw) * 20000 * 7 / 16384 * 36 / 10) / (tvch - tvcl) / 2; + } else { + ret = ((dac * 10000 * 131 / 100) - (16384 / 2 - raw) * 20000 * 7 / 16384 * 36 / 10) / (tvch - tvcl) / 2; + } + return ret; +} + +static s32 open_c_formula(int dac, int raw, int tvch, int gain) +{ + s32 ret = 0; + u16 id = core_mp.chip_id, type = core_mp.chip_type; + int accuracy = 0; + char *section = "open test_c"; + char str[32] = {0}; + + if (id == ILI9881_CHIP) { + ret = (dac * 3206 * 39 / 2 + (raw - 8192) * 9 * 7 * 125 * 5 / 256) / tvch / 1000 / gain; + return ret; + } + + parser_get_int_data(section, "accuracy", str, sizeof(str)); + accuracy = ili_katoi(str); + + if (type == ILI_Q) { + if (accuracy) + ret = ((dac * 275 * 39 / 2 * 10) + ((raw - 8192) * 9 * (7 * 100) * 25 / 1024)) / tvch / 100 / gain; + else + ret = ((dac * 275 * 39 / 2) + ((raw - 8192) * 36 * (7 * 100) * 10 * 1 / 16384)) / tvch / 100 / gain; + } + + return ret; +} + +static void allnode_open_cdc_result(int index, int *buf, int *dac, int *raw) +{ + int i; + char *desp = tItems[index].desp; + + if (ipio_strcmp(desp, "open test(integration)_sp") == 0) { + for (i = 0; i < core_mp.frame_len; i++) + buf[i] = open_sp_formula(dac[i], raw[i], open_spec.tvch, open_spec.tvcl); + } else if (ipio_strcmp(desp, "open test_c") == 0) { + for (i = 0; i < core_mp.frame_len; i++) + buf[i] = open_c_formula(dac[i], raw[i], open_spec.tvch - open_spec.tvcl, open_spec.gain); + } +} + +static int codeToOhm(s32 Code, u16 *v_tdf, u16 *h_tdf) +{ + int douTDF1 = 0; + int douTDF2 = 0; + int douTVCH = 24; + int douTVCL = 8; + int douCint = 7; + int douVariation = 100; + int douRinternal = 1915; + u16 id = core_mp.chip_id, type = core_mp.chip_type; + s32 temp = 0; + char str[128] = {0}; + + if (id == ILI7807_CHIP && type == ILI_Q) + douRinternal = 1500; + + core_mp.short_varia = douVariation; + + parser_get_int_data("short test", "tvch", str, sizeof(str)); + if (ili_katoi(str) != 0) + douTVCH = ili_katoi(str); + + parser_get_int_data("short test", "tvcl", str, sizeof(str)); + if (ili_katoi(str) != 0) + douTVCL = ili_katoi(str); + + ILI_DBG("TVCH = %d, TVCL = %d\n", douTVCH, douTVCL); + + if (core_mp.isLongV) { + douTDF1 = *v_tdf; + douTDF2 = *(v_tdf + 1); + } else { + douTDF1 = *h_tdf; + douTDF2 = *(h_tdf + 1); + } + + if (Code == 0) { + ILI_ERR("code is invalid\n"); + } else { + temp = ((douTVCH - douTVCL) * douVariation * (douTDF1 - douTDF2) * (1 << 12) / (9 * Code * douCint)) * 100; + temp = (temp - douRinternal) / 1000; + } + /* Unit = M Ohm */ + return temp; +} + +static int short_test(int index, int frame_index) +{ + int j = 0, ret = 0; + u16 v_tdf[2] = {0}; + u16 h_tdf[2] = {0}; + + v_tdf[0] = tItems[index].v_tdf_1; + v_tdf[1] = tItems[index].v_tdf_2; + h_tdf[0] = tItems[index].h_tdf_1; + h_tdf[1] = tItems[index].h_tdf_2; + + if (core_mp.protocol_ver >= PROTOCOL_VER_540) { + /* Calculate code to ohm and save to tItems[index].buf */ + for (j = 0; j < core_mp.frame_len; j++) + tItems[index].buf[frame_index * core_mp.frame_len + j] = codeToOhm(frame_buf[j], v_tdf, h_tdf); + } else { + for (j = 0; j < core_mp.frame_len; j++) + tItems[index].buf[frame_index * core_mp.frame_len + j] = frame_buf[j]; + } + + return ret; +} + +static int allnode_key_cdc_data(int index) +{ + int i, ret = 0, len = 0; + int inDACp = 0, inDACn = 0; + u8 cmd[3] = {0}; + u8 *ori = NULL; + + len = core_mp.key_len * 2; + + ILI_DBG("Read key's length = %d\n", len); + ILI_DBG("core_mp.key_len = %d\n", core_mp.key_len); + + if (len <= 0) { + ILI_ERR("Length is invalid\n"); + ret = -1; + goto out; + } + + /* CDC init */ + cmd[0] = P5_X_SET_CDC_INIT; + cmd[1] = tItems[index].cmd; + cmd[2] = 0; + + // /* Allocate a buffer for the original */ + ori = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(ori)) { + ILI_ERR("Failed to allocate ori mem (%ld)\n", PTR_ERR(ori)); + ret = -1; + goto out; + } + ret = ilits->wrapper(cmd, sizeof(cmd), ori, len, ON, ON); + if (ret < 0) { + ILI_ERR(" fail to get cdc data\n"); + goto out; + } + ili_dump_data(ori, 8, len, 0, "Key CDC original"); + + if (key_buf == NULL) { + key_buf = kcalloc(core_mp.key_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(key_buf)) { + ILI_ERR("Failed to allocate FrameBuffer mem (%ld)\n", PTR_ERR(key_buf)); + goto out; + } + } else { + memset(key_buf, 0x0, core_mp.key_len); + } + + /* Convert original data to the physical one in each node */ + for (i = 0; i < core_mp.frame_len; i++) { + if (tItems[index].cmd == CMD_KEY_DAC) { + /* DAC - P */ + if (((ori[(2 * i) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACp = 0 - (int)(ori[(2 * i) + 1] & 0x7F); + } else { + inDACp = ori[(2 * i) + 1] & 0x7F; + } + + /* DAC - N */ + if (((ori[(1 + (2 * i)) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACn = 0 - (int)(ori[(1 + (2 * i)) + 1] & 0x7F); + } else { + inDACn = ori[(1 + (2 * i)) + 1] & 0x7F; + } + + key_buf[i] = (inDACp + inDACn) / 2; + } + } + ili_dump_data(key_buf, 32, core_mp.frame_len, core_mp.xch_len, "Key CDC combined data"); + +out: + ipio_kfree((void **)&ori); + return ret; +} + +static int mp_cdc_get_pv5_4_command(u8 *cmd, int len, int index) +{ + int slen = 0; + char str[128] = {0}; + char *key = tItems[index].desp; + + if (core_mp.td_retry && (ipio_strcmp(key, "peak to peak_td (lcm off)") == 0)) + key = "peak to peak_td (lcm off)_2"; + + slen = parser_get_int_data("pv5_4 command", key, str, sizeof(str)); + if (slen < 0) + return -1; + + if (parser_get_u8_array(str, cmd, 16, len) < 0) + return -1; + + return 0; +} + +static int mp_cdc_init_cmd_common(u8 *cmd, int len, int index) +{ + int ret = 0; + + if (core_mp.protocol_ver >= PROTOCOL_VER_540) { + core_mp.cdc_len = 15; + return mp_cdc_get_pv5_4_command(cmd, len, index); + } + + cmd[0] = P5_X_SET_CDC_INIT; + cmd[1] = tItems[index].cmd; + cmd[2] = 0; + + core_mp.cdc_len = 3; + + if (ipio_strcmp(tItems[index].desp, "open test(integration)") == 0) + cmd[2] = 0x2; + if (ipio_strcmp(tItems[index].desp, "open test(cap)") == 0) + cmd[2] = 0x3; + + if (tItems[index].catalog == PEAK_TO_PEAK_TEST) { + cmd[2] = ((tItems[index].frame_count & 0xff00) >> 8); + cmd[3] = tItems[index].frame_count & 0xff; + cmd[4] = 0; + + core_mp.cdc_len = 5; + + if (ipio_strcmp(tItems[index].desp, "noise peak to peak(cut panel)") == 0) + cmd[4] = 0x1; + + ILI_DBG("P2P CMD: %d,%d,%d,%d,%d\n", + cmd[0], cmd[1], cmd[2], cmd[3], cmd[4]); + } + + return ret; +} + +static int allnode_open_cdc_data(int mode, int *buf) +{ + int i = 0, ret = 0, len = 0; + int inDACp = 0, inDACn = 0; + u8 cmd[15] = {0}; + u8 *ori = NULL; + char str[128] = {0}; + char *key[] = {"open dac", "open raw1", "open raw2", "open raw3", + "open cap1 dac", "open cap1 raw"}; + + /* Multipling by 2 is due to the 16 bit in each node */ + len = (core_mp.xch_len * core_mp.ych_len * 2) + 2; + + ILI_DBG("Read X/Y Channel length = %d, mode = %d\n", len, mode); + + if (len <= 2) { + ILI_ERR("Length is invalid\n"); + ret = -EMP_INVAL; + goto out; + } + + /* CDC init. Read command from ini file */ + if (parser_get_int_data("pv5_4 command", key[mode], str, sizeof(str)) < 0) { + ILI_ERR("Failed to parse PV54 command, ret = %d\n", ret); + ret = -EMP_PARSE; + goto out; + } + + parser_get_u8_array(str, cmd, 16, sizeof(cmd)); + + ili_dump_data(cmd, 8, sizeof(cmd), 0, "Open SP command"); + + /* Allocate a buffer for the original */ + ori = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(ori)) { + ILI_ERR("Failed to allocate ori, (%ld)\n", PTR_ERR(ori)); + ret = -EMP_NOMEM; + goto out; + } + + /* Get original frame(cdc) data */ + if (ilits->wrapper(cmd, core_mp.cdc_len, ori, len, ON, ON) < 0) { + ILI_ERR("Failed to get cdc data\n"); + ret = -EMP_GET_CDC; + goto out; + } + + ili_dump_data(ori, 8, len, 0, "Open SP CDC original"); + + /* Convert original data to the physical one in each node */ + for (i = 0; i < core_mp.frame_len; i++) { + if ((mode == 0) || (mode == 4)) { + /* DAC - P */ + if (((ori[(2 * i) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACp = 0 - (int)(ori[(2 * i) + 1] & 0x7F); + } else { + inDACp = ori[(2 * i) + 1] & 0x7F; + } + + /* DAC - N */ + if (((ori[(1 + (2 * i)) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACn = 0 - (int)(ori[(1 + (2 * i)) + 1] & 0x7F); + } else { + inDACn = ori[(1 + (2 * i)) + 1] & 0x7F; + } + + buf[i] = inDACp + inDACn; + } else { + /* H byte + L byte */ + s32 tmp = (ori[(2 * i) + 1] << 8) + ori[(1 + (2 * i)) + 1]; + if ((tmp & 0x8000) == 0x8000) + buf[i] = tmp - 65536; + else + buf[i] = tmp; + + } + } + ili_dump_data(buf, 10, core_mp.frame_len, core_mp.xch_len, "Open SP CDC combined"); +out: + + ipio_kfree((void **)&ori); + return ret; +} + +static int allnode_mutual_cdc_data(int index) +{ + int i, ret = 0, len = 0; + int inDACp = 0, inDACn = 0; + u8 cmd[15] = {0}; + u8 *ori = NULL; + + /* Multipling by 2 is due to the 16 bit in each node */ + len = (core_mp.frame_len * 2) + 2; + + ILI_DBG("Read X/Y Channel length = %d\n", len); + + if (len <= 2) { + ILI_ERR("Length is invalid\n"); + ret = -EMP_INVAL; + goto out; + } + + memset(cmd, 0xFF, sizeof(cmd)); + + /* CDC init */ + if (mp_cdc_init_cmd_common(cmd, sizeof(cmd), index) < 0) { + ILI_ERR("Failed to get cdc command\n"); + ret = -EMP_CMD; + goto out; + } + + ili_dump_data(cmd, 8, core_mp.cdc_len, 0, "Mutual CDC command"); + + /* Allocate a buffer for the original */ + ori = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(ori)) { + ILI_ERR("Failed to allocate ori, (%ld)\n", PTR_ERR(ori)); + ret = -EMP_NOMEM; + goto out; + } + + /* Get original frame(cdc) data */ + if (ilits->wrapper(cmd, core_mp.cdc_len, ori, len, ON, ON) < 0) { + ILI_ERR("Failed to get cdc data\n"); + ret = -EMP_GET_CDC; + goto out; + } + + ili_dump_data(ori, 8, len, 0, "Mutual CDC original"); + + if (frame_buf == NULL) { + frame_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frame_buf)) { + ILI_ERR("Failed to allocate FrameBuffer mem (%ld)\n", PTR_ERR(frame_buf)); + ret = -EMP_NOMEM; + goto out; + } + } else { + memset(frame_buf, 0x0, core_mp.frame_len); + } + + /* Convert original data to the physical one in each node */ + for (i = 0; i < core_mp.frame_len; i++) { + if (ipio_strcmp(tItems[index].desp, "calibration data(dac)") == 0) { + /* DAC - P */ + if (((ori[(2 * i) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACp = 0 - (int)(ori[(2 * i) + 1] & 0x7F); + } else { + inDACp = ori[(2 * i) + 1] & 0x7F; + } + + /* DAC - N */ + if (((ori[(1 + (2 * i)) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACn = 0 - (int)(ori[(1 + (2 * i)) + 1] & 0x7F); + } else { + inDACn = ori[(1 + (2 * i)) + 1] & 0x7F; + } + + frame_buf[i] = (inDACp + inDACn) / 2; + } else { + /* H byte + L byte */ + s32 tmp = (ori[(2 * i) + 1] << 8) + ori[(1 + (2 * i)) + 1]; + + if ((tmp & 0x8000) == 0x8000) + frame_buf[i] = tmp - 65536; + else + frame_buf[i] = tmp; + + if (ipio_strcmp(tItems[index].desp, "raw data(no bk)") == 0 || + ipio_strcmp(tItems[index].desp, "raw data(no bk) (lcm off)") == 0) { + frame_buf[i] -= core_mp.no_bk_shift; + } + } + } + + ili_dump_data(frame_buf, 32, core_mp.frame_len, core_mp.xch_len, "Mutual CDC combined"); + +out: + ipio_kfree((void **)&ori); + return ret; +} + +static void compare_MaxMin_result(int index, s32 *data) +{ + int x, y; + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int shift = y * core_mp.xch_len; + + if (tItems[index].catalog == UNTOUCH_P2P) + return; + else if (tItems[index].catalog == TX_RX_DELTA) { + /* Tx max/min comparison */ + if (core_mp.tx_delta_buf[shift + x] < data[shift + x]) + core_mp.tx_max_buf[shift + x] = data[shift + x]; + + if (core_mp.tx_delta_buf[shift + x] > data[shift + x]) + core_mp.tx_min_buf[shift + x] = data[shift + x]; + + /* Rx max/min comparison */ + if (core_mp.rx_delta_buf[shift + x] < data[shift + x]) + core_mp.rx_max_buf[shift + x] = data[shift + x]; + + if (core_mp.rx_delta_buf[shift + x] > data[shift + x]) + core_mp.rx_min_buf[shift + x] = data[shift + x]; + } else { + if (tItems[index].max_buf[shift + x] < data[shift + x]) + tItems[index].max_buf[shift + x] = data[shift + x]; + + if (tItems[index].min_buf[shift + x] > data[shift + x]) + tItems[index].min_buf[shift + x] = data[shift + x]; + } + } + } +} + +static int create_mp_test_frame_buffer(int index, int frame_count) +{ + ILI_DBG("Create MP frame buffers (index = %d), count = %d\n", + index, frame_count); + + if (tItems[index].catalog == TX_RX_DELTA) { + if (core_mp.tx_delta_buf == NULL) { + core_mp.tx_delta_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.tx_delta_buf)) { + ILI_ERR("Failed to allocate tx_delta_buf mem\n"); + ipio_kfree((void **)&core_mp.tx_delta_buf); + return -ENOMEM; + } + } + + if (core_mp.rx_delta_buf == NULL) { + core_mp.rx_delta_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.rx_delta_buf)) { + ILI_ERR("Failed to allocate rx_delta_buf mem\n"); + ipio_kfree((void **)&core_mp.rx_delta_buf); + return -ENOMEM; + } + } + + if (core_mp.tx_max_buf == NULL) { + core_mp.tx_max_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.tx_max_buf)) { + ILI_ERR("Failed to allocate tx_max_buf mem\n"); + ipio_kfree((void **)&core_mp.tx_max_buf); + return -ENOMEM; + } + } + + if (core_mp.tx_min_buf == NULL) { + core_mp.tx_min_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.tx_min_buf)) { + ILI_ERR("Failed to allocate tx_min_buf mem\n"); + ipio_kfree((void **)&core_mp.tx_min_buf); + return -ENOMEM; + } + } + + if (core_mp.rx_max_buf == NULL) { + core_mp.rx_max_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.rx_max_buf)) { + ILI_ERR("Failed to allocate rx_max_buf mem\n"); + ipio_kfree((void **)&core_mp.rx_max_buf); + return -ENOMEM; + } + } + + if (core_mp.rx_min_buf == NULL) { + core_mp.rx_min_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.rx_min_buf)) { + ILI_ERR("Failed to allocate rx_min_buf mem\n"); + ipio_kfree((void **)&core_mp.rx_min_buf); + return -ENOMEM; + } + } + } else { + if (tItems[index].buf == NULL) { + tItems[index].buf = vmalloc(frame_count * core_mp.frame_len * sizeof(s32)); + if (ERR_ALLOC_MEM(tItems[index].buf)) { + ILI_ERR("Failed to allocate buf mem\n"); + ipio_kfree((void **)&tItems[index].buf); + return -ENOMEM; + } + } + + if (tItems[index].result_buf == NULL) { + tItems[index].result_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].result_buf)) { + ILI_ERR("Failed to allocate result_buf mem\n"); + ipio_kfree((void **)&tItems[index].result_buf); + return -ENOMEM; + } + } + + if (tItems[index].max_buf == NULL) { + tItems[index].max_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].max_buf)) { + ILI_ERR("Failed to allocate max_buf mem\n"); + ipio_kfree((void **)&tItems[index].max_buf); + return -ENOMEM; + } + } + + if (tItems[index].min_buf == NULL) { + tItems[index].min_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].min_buf)) { + ILI_ERR("Failed to allocate min_buf mem\n"); + ipio_kfree((void **)&tItems[index].min_buf); + return -ENOMEM; + } + } + + if (tItems[index].spec_option == BENCHMARK) { + if (tItems[index].bench_mark_max == NULL) { + tItems[index].bench_mark_max = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bench_mark_max)) { + ILI_ERR("Failed to allocate bench_mark_max mem\n"); + ipio_kfree((void **)&tItems[index].bench_mark_max); + return -ENOMEM; + } + } + if (tItems[index].bench_mark_min == NULL) { + tItems[index].bench_mark_min = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bench_mark_min)) { + ILI_ERR("Failed to allocate bench_mark_min mem\n"); + ipio_kfree((void **)&tItems[index].bench_mark_min); + return -ENOMEM; + } + } + } + } + return 0; +} + +static int pin_test(int index) +{ + int ret = 0; + u8 cmd[5] = {0}; + + ILI_INFO("PIN test start"); + ILI_INFO("test_int_pin = 0x%x\n", tItems[index].test_int_pin); + ILI_INFO("int_pulse_test = 0x%x\n", tItems[index].int_pulse_test); + ILI_INFO("delay_time = 0x%x\n", tItems[index].delay_time); + + if (tItems[index].test_int_pin == ENABLE) { + /* test HIGH level*/ + cmd[0] = tItems[index].cmd; + cmd[1] = 0x1; + ret = ilits->wrapper(cmd, 2, NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write command failed\n"); + ret = -EMP_CMD; + goto out; + } + + ret = ilits->detect_int_stat(false); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + + /* test LOW level*/ + cmd[1] = 0x0; + ret = ilits->wrapper(cmd, 2, NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write command failed\n"); + ret = -EMP_CMD; + goto out; + } + + ret = ilits->detect_int_stat(false); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + } + + if (tItems[index].int_pulse_test == ENABLE) { + + ILI_INFO("MP IRQ Rising Trigger Test\n"); + cmd[1] = 0x2; + cmd[2] = tItems[index].delay_time; + + ili_irq_unregister(); + ret = ili_irq_register(IRQF_TRIGGER_RISING); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + + atomic_set(&ilits->cmd_int_check, ENABLE); + ret = ilits->wrapper(cmd, 3, NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write command failed\n"); + ret = -EMP_CMD; + goto out; + } + + ret = ilits->detect_int_stat(false); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + + ILI_INFO("MP IRQ Falling Trigger Test\n"); + ili_irq_unregister(); + ret = ili_irq_register(IRQF_TRIGGER_FALLING); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + + atomic_set(&ilits->cmd_int_check, ENABLE); + + ret = ilits->wrapper(cmd, 3, NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write command failed\n"); + ret = -EMP_CMD; + goto out; + } + + ret = ilits->detect_int_stat(false); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + } + + tItems[index].item_result = MP_DATA_PASS; + +out: + if (ret < 0) + tItems[index].item_result = MP_DATA_FAIL; + + ILI_INFO("Change to defualt IRQ trigger type\n"); + ili_irq_unregister(); + ili_irq_register(ilits->irq_tirgger_type); + atomic_set(&ilits->cmd_int_check, DISABLE); + + return ret; +} + +static int mutual_test(int index) +{ + int i = 0, j = 0, x = 0, y = 0, ret = 0, get_frame_cont = 1; + + ILI_DBG("index = %d, desp = %s, Frame Count = %d\n", + index, tItems[index].desp, tItems[index].frame_count); + + /* + * We assume that users who are calling the test forget to config frame count + * as 1, so we just help them to set it up. + */ + if (tItems[index].frame_count <= 0) { + ILI_ERR("Frame count is zero, which is at least set as 1\n"); + tItems[index].frame_count = 1; + } + + ret = create_mp_test_frame_buffer(index, tItems[index].frame_count); + if (ret < 0) { + ret = -EMP_NOMEM; + goto out; + } + + /* Init Max/Min buffer */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + if (tItems[i].catalog == TX_RX_DELTA) { + core_mp.tx_max_buf[y * core_mp.xch_len + x] = INT_MIN; + core_mp.rx_max_buf[y * core_mp.xch_len + x] = INT_MIN; + core_mp.tx_min_buf[y * core_mp.xch_len + x] = INT_MAX; + core_mp.rx_min_buf[y * core_mp.xch_len + x] = INT_MAX; + } else { + tItems[index].max_buf[y * core_mp.xch_len + x] = INT_MIN; + tItems[index].min_buf[y * core_mp.xch_len + x] = INT_MAX; + } + } + } + + if (tItems[index].catalog != PEAK_TO_PEAK_TEST) + get_frame_cont = tItems[index].frame_count; + + if (tItems[index].spec_option == BENCHMARK) { + parser_ini_benchmark(tItems[index].bench_mark_max, tItems[index].bench_mark_min, + tItems[index].type_option, tItems[index].desp, core_mp.frame_len); + dump_benchmark_data(tItems[index].bench_mark_max, tItems[index].bench_mark_min); + } + + for (i = 0; i < get_frame_cont; i++) { + ret = allnode_mutual_cdc_data(index); + if (ret < 0) { + ILI_ERR("Failed to initialise CDC data, %d\n", ret); + goto out; + } + switch (tItems[index].catalog) { + case PIXEL: + run_pixel_test(index); + break; + case UNTOUCH_P2P: + run_untouch_p2p_test(index); + break; + case OPEN_TEST: + run_open_test(index); + break; + case TX_RX_DELTA: + run_tx_rx_delta_test(index); + break; + case SHORT_TEST: + short_test(index, i); + break; + default: + for (j = 0; j < core_mp.frame_len; j++) + tItems[index].buf[i * core_mp.frame_len + j] = frame_buf[j]; + break; + } + compare_MaxMin_result(index, &tItems[index].buf[i * core_mp.frame_len]); + } + +out: + return ret; +} + +static int open_test_sp(int index) +{ + struct mp_test_P540_open open[2]; + int i = 0, x = 0, y = 0, ret = 0, addr = 0; + int Charge_AA = 0, Charge_Border = 0, Charge_Notch = 0, full_open_rate = 0; + char str[512] = {0}; + + ILI_DBG("index = %d, desp = %s, Frame Count = %d\n", + index, tItems[index].desp, tItems[index].frame_count); + + /* + * We assume that users who are calling the test forget to config frame count + * as 1, so we just help them to set it up. + */ + if ((tItems[index].frame_count <= 0) || (tItems[index].frame_count > 2)){ + ILI_ERR("Frame count is zero, which is at least set as 1\n"); + tItems[index].frame_count = 1; + } + + ret = create_mp_test_frame_buffer(index, tItems[index].frame_count); + if (ret < 0) { + ret = -EMP_NOMEM; + goto out; + } + + if (frame1_cbk700 == NULL) { + frame1_cbk700 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frame1_cbk700)) { + ILI_ERR("Failed to allocate frame1_cbk700 buffer\n"); + return -EMP_NOMEM; + } + } else { + memset(frame1_cbk700, 0x0, core_mp.frame_len); + } + + if (frame1_cbk250 == NULL) { + frame1_cbk250 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frame1_cbk250)) { + ILI_ERR("Failed to allocate frame1_cbk250 buffer\n"); + ipio_kfree((void **)&frame1_cbk700); + return -EMP_NOMEM; + } + } else { + memset(frame1_cbk250, 0x0, core_mp.frame_len); + } + + if (frame1_cbk200 == NULL) { + frame1_cbk200 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frame1_cbk200)) { + ILI_ERR("Failed to allocate cbk buffer\n"); + ipio_kfree((void **)&frame1_cbk700); + ipio_kfree((void **)&frame1_cbk250); + return -EMP_NOMEM; + } + } else { + memset(frame1_cbk200, 0x0, core_mp.frame_len); + } + + tItems[index].node_type = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].node_type)) { + ILI_ERR("Failed to allocate node_type FRAME buffer\n"); + return -EMP_NOMEM; + } + + /* Init Max/Min buffer */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].max_buf[y * core_mp.xch_len + x] = INT_MIN; + tItems[index].min_buf[y * core_mp.xch_len + x] = INT_MAX; + } + } + + if (tItems[index].spec_option == BENCHMARK) { + parser_ini_benchmark(tItems[index].bench_mark_max, tItems[index].bench_mark_min, + tItems[index].type_option, tItems[index].desp, core_mp.frame_len); + dump_benchmark_data(tItems[index].bench_mark_max, tItems[index].bench_mark_min); + } + + parser_ini_nodetype(tItems[index].node_type, NODE_TYPE_KEY_NAME, core_mp.frame_len); + dump_node_type_buffer(tItems[index].node_type, "node type"); + + parser_get_int_data(tItems[index].desp, "charge_aa", str, sizeof(str)); + Charge_AA = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "charge_border", str, sizeof(str)); + Charge_Border = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "charge_notch", str, sizeof(str)); + Charge_Notch = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "full open", str, sizeof(str)); + full_open_rate = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "tvch", str, sizeof(str)); + open_spec.tvch = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "tvcl", str, sizeof(str)); + open_spec.tvcl = ili_katoi(str); + + ILI_DBG("AA = %d, Border = %d, Notch = %d, full_open_rate = %d, tvch = %d, tvcl = %d\n", + Charge_AA, Charge_Border, Charge_Notch, + full_open_rate, open_spec.tvch, open_spec.tvcl); + + for (i = 0; i < tItems[index].frame_count; i++) { + open[i].tdf_700 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].tdf_250 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].tdf_200 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].cbk_700 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].cbk_250 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].cbk_200 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].charg_rate = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].full_Open = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].dac = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + } + + for (i = 0; i < tItems[index].frame_count; i++) { + ret = allnode_open_cdc_data(0, open[i].dac); + if (ret < 0) { + ILI_ERR("Failed to get Open SP DAC data, %d\n", ret); + goto out; + } + ret = allnode_open_cdc_data(1, open[i].tdf_700); + if (ret < 0) { + ILI_ERR("Failed to get Open SP Raw1 data, %d\n", ret); + goto out; + } + ret = allnode_open_cdc_data(2, open[i].tdf_250); + if (ret < 0) { + ILI_ERR("Failed to get Open SP Raw2 data, %d\n", ret); + goto out; + } + ret = allnode_open_cdc_data(3, open[i].tdf_200); + if (ret < 0) { + ILI_ERR("Failed to get Open SP Raw3 data, %d\n", ret); + goto out; + } + allnode_open_cdc_result(index, open[i].cbk_700, open[i].dac, open[i].tdf_700); + allnode_open_cdc_result(index, open[i].cbk_250, open[i].dac, open[i].tdf_250); + allnode_open_cdc_result(index, open[i].cbk_200, open[i].dac, open[i].tdf_200); + + addr = 0; + + /* record fist frame for debug */ + if (i == 0) { + ipio_memcpy(frame1_cbk700, open[i].cbk_700, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + ipio_memcpy(frame1_cbk250, open[i].cbk_250, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + ipio_memcpy(frame1_cbk200, open[i].cbk_200, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + } + + ili_dump_data(open[i].cbk_700, 10, core_mp.frame_len, core_mp.xch_len, "cbk 700"); + ili_dump_data(open[i].cbk_250, 10, core_mp.frame_len, core_mp.xch_len, "cbk 250"); + ili_dump_data(open[i].cbk_200, 10, core_mp.frame_len, core_mp.xch_len, "cbk 200"); + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + open[i].charg_rate[addr] = open[i].cbk_250[addr] * 100 / open[i].cbk_700[addr]; + open[i].full_Open[addr] = open[i].cbk_700[addr] - open[i].cbk_200[addr]; + addr++; + } + } + + ili_dump_data(open[i].charg_rate, 10, core_mp.frame_len, core_mp.xch_len, "origin charge rate"); + ili_dump_data(open[i].full_Open, 10, core_mp.frame_len, core_mp.xch_len, "origin full open"); + + addr = 0; + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + if (full_open_rate_compare(open[i].full_Open, open[i].cbk_700, x, y, tItems[index].node_type[addr], full_open_rate) == false) { + tItems[index].buf[(i * core_mp.frame_len) + addr] = 0; + open[i].charg_rate[addr] = 0; + } + addr++; + } + } + + ili_dump_data(&tItems[index].buf[(i * core_mp.frame_len)], 10, core_mp.frame_len, core_mp.xch_len, "after full_open_rate_compare"); + + addr = 0; + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].buf[(i * core_mp.frame_len) + addr] = compare_charge(open[i].charg_rate, x, y, tItems[index].node_type, Charge_AA, Charge_Border, Charge_Notch); + addr++; + } + } + + ili_dump_data(&tItems[index].buf[(i * core_mp.frame_len)], 10, core_mp.frame_len, core_mp.xch_len, "after compare charge rate"); + + compare_MaxMin_result(index, &tItems[index].buf[(i * core_mp.frame_len)]); + } + +out: + ipio_kfree((void **)&tItems[index].node_type); + + for (i = 0; i < tItems[index].frame_count; i++) { + ipio_kfree((void **)&open[i].tdf_700); + ipio_kfree((void **)&open[i].tdf_250); + ipio_kfree((void **)&open[i].tdf_200); + ipio_kfree((void **)&open[i].cbk_700); + ipio_kfree((void **)&open[i].cbk_250); + ipio_kfree((void **)&open[i].cbk_200); + ipio_kfree((void **)&open[i].charg_rate); + ipio_kfree((void **)&open[i].full_Open); + ipio_kfree((void **)&open[i].dac); + } + return ret; +} + +static int open_test_cap(int index) +{ + struct mp_test_open_c open[2]; + int i = 0, x = 0, y = 0, ret = 0, addr = 0; + char str[512] = {0}; + + ILI_DBG("index = %d, desp = %s, Frame Count = %d\n", + index, tItems[index].desp, tItems[index].frame_count); + + if ((tItems[index].frame_count <= 0) || (tItems[index].frame_count > 2)){ + ILI_ERR("Frame count is zero, which is at least set as 1\n"); + tItems[index].frame_count = 1; + } + + if (create_mp_test_frame_buffer(index, tItems[index].frame_count) < 0) { + ret = -EMP_NOMEM; + goto out; + } + + if (cap_dac == NULL) { + cap_dac = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(cap_dac)) { + ILI_ERR("Failed to allocate cap_dac buffer\n"); + return -EMP_NOMEM; + } + } else { + memset(cap_dac, 0x0, core_mp.frame_len); + } + + if (cap_raw == NULL) { + cap_raw = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(cap_raw)) { + ILI_ERR("Failed to allocate cap_raw buffer\n"); + ipio_kfree((void **)&cap_dac); + return -EMP_NOMEM; + } + } else { + memset(cap_raw, 0x0, core_mp.frame_len); + } + + /* Init Max/Min buffer */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].max_buf[y * core_mp.xch_len + x] = INT_MIN; + tItems[index].min_buf[y * core_mp.xch_len + x] = INT_MAX; + } + } + + if (tItems[index].spec_option == BENCHMARK) { + parser_ini_benchmark(tItems[index].bench_mark_max, tItems[index].bench_mark_min, + tItems[index].type_option, tItems[index].desp, core_mp.frame_len); + dump_benchmark_data(tItems[index].bench_mark_max, tItems[index].bench_mark_min); + } + + parser_get_int_data(tItems[index].desp, "gain", str, sizeof(str)); + open_spec.gain = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "tvch", str, sizeof(str)); + open_spec.tvch = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "tvcl", str, sizeof(str)); + open_spec.tvcl = ili_katoi(str); + + ILI_DBG("gain = %d, tvch = %d, tvcl = %d\n", open_spec.gain, open_spec.tvch, open_spec.tvcl); + + for (i = 0; i < tItems[index].frame_count; i++) { + open[i].cap_dac = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].cap_raw = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].dcl_cap = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + } + + for (i = 0; i < tItems[index].frame_count; i++) { + ret = allnode_open_cdc_data(4, open[i].cap_dac); + if (ret < 0) { + ILI_ERR("Failed to get Open CAP DAC data, %d\n", ret); + goto out; + } + ret = allnode_open_cdc_data(5, open[i].cap_raw); + if (ret < 0) { + ILI_ERR("Failed to get Open CAP RAW data, %d\n", ret); + goto out; + } + + allnode_open_cdc_result(index, open[i].dcl_cap, open[i].cap_dac, open[i].cap_raw); + + /* record fist frame for debug */ + if (i == 0) { + ipio_memcpy(cap_dac, open[i].cap_dac, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + ipio_memcpy(cap_raw, open[i].cap_raw, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + } + + ili_dump_data(open[i].dcl_cap, 10, core_mp.frame_len, core_mp.xch_len, "DCL_Cap"); + + addr = 0; + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].buf[(i * core_mp.frame_len) + addr] = open[i].dcl_cap[addr]; + addr++; + } + } + compare_MaxMin_result(index, &tItems[index].buf[i * core_mp.frame_len]); + } + +out: + + for (i = 0; i < tItems[index].frame_count; i++) { + ipio_kfree((void **)&open[i].cap_dac); + ipio_kfree((void **)&open[i].cap_raw); + ipio_kfree((void **)&open[i].dcl_cap); + } + return ret; +} + +static int key_test(int index) +{ + int i, j = 0, ret = 0; + + ILI_DBG("Item = %s, Frame Count = %d\n", + tItems[index].desp, tItems[index].frame_count); + + if (tItems[index].frame_count == 0) { + ILI_ERR("Frame count is zero, which at least sets as 1\n"); + ret = -EINVAL; + goto out; + } + + ret = create_mp_test_frame_buffer(index, tItems[index].frame_count); + if (ret < 0) + goto out; + + for (i = 0; i < tItems[index].frame_count; i++) { + ret = allnode_key_cdc_data(index); + if (ret < 0) { + ILI_ERR("Failed to initialise CDC data, %d\n", ret); + goto out; + } + + for (j = 0; j < core_mp.key_len; j++) + tItems[index].buf[j] = key_buf[j]; + } + + compare_MaxMin_result(index, tItems[index].buf); + +out: + return ret; +} + +static int self_test(int index) +{ + ILI_ERR("TDDI has no self to be tested currently\n"); + return -1; +} + +static int st_test(int index) +{ + ILI_ERR("ST Test is not supported by the driver\n"); + return -1; +} + +static int mp_get_timing_info(void) +{ + int slen = 0; + char str[256] = {0}; + u8 info[64] = {0}; + char *key = "timing_info_raw"; + + core_mp.isLongV = 0; + + slen = parser_get_int_data("pv5_4 command", key, str, sizeof(str)); + if (slen < 0) + return -1; + + if (parser_get_u8_array(str, info, 16, slen) < 0) + return -1; + + core_mp.isLongV = info[6]; + + ILI_INFO("DDI Mode = %s\n", (core_mp.isLongV ? "Long V" : "Long H")); + + return 0; +} + +static int mp_test_data_sort_average(s32 *oringin_data, int index, s32 *avg_result) +{ + int i, j, k, x, y, len = 5, size; + s32 u32temp; + int u32up_frame, u32down_frame; + s32 *u32sum_raw_data; + s32 *u32data_buff; + + if (tItems[index].frame_count <= 1) + return 0; + + + if (ERR_ALLOC_MEM(oringin_data)) { + ILI_ERR("Input wrong address\n"); + return -ENOMEM; + } + + u32data_buff = kcalloc(core_mp.frame_len * tItems[index].frame_count, sizeof(s32), GFP_KERNEL); + u32sum_raw_data = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(u32sum_raw_data) || (ERR_ALLOC_MEM(u32data_buff))) { + ILI_ERR("Failed to allocate u32sum_raw_data FRAME buffer\n"); + return -ENOMEM; + } + + size = core_mp.frame_len * tItems[index].frame_count; + for (i = 0; i < size; i++) { + u32data_buff[i] = oringin_data[i]; + } + + u32up_frame = tItems[index].frame_count * tItems[index].highest_percentage / 100; + u32down_frame = tItems[index].frame_count * tItems[index].lowest_percentage / 100; + ILI_DBG("Up=%d, Down=%d -%s\n", u32up_frame, u32down_frame, tItems[index].desp); + + if (debug_en) { + pr_cont("\n[Show Original frist%d and last%d node data]\n", len, len); + for (i = 0; i < core_mp.frame_len; i++) { + for (j = 0 ; j < tItems[index].frame_count ; j++) { + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("%d,", u32data_buff[j * core_mp.frame_len + i]); + } + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("\n"); + } + } + + for (i = 0; i < core_mp.frame_len; i++) { + for (j = 0; j < tItems[index].frame_count-1; j++) { + for (k = 0; k < (tItems[index].frame_count-1-j); k++) { + x = i+k*core_mp.frame_len; + y = i+(k+1)*core_mp.frame_len; + if (*(u32data_buff+x) > *(u32data_buff+y)) { + u32temp = *(u32data_buff+x); + *(u32data_buff+x) = *(u32data_buff+y); + *(u32data_buff+y) = u32temp; + } + } + } + } + + if (debug_en) { + pr_cont("\n[After sorting frist%d and last%d node data]\n", len, len); + for (i = 0; i < core_mp.frame_len; i++) { + for (j = u32down_frame; j < tItems[index].frame_count - u32up_frame; j++) { + if ((i < len) || (i >= (core_mp.frame_len - len))) + pr_cont("%d,", u32data_buff[i + j * core_mp.frame_len]); + } + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("\n"); + } + } + + for (i = 0 ; i < core_mp.frame_len ; i++) { + u32sum_raw_data[i] = 0; + for (j = u32down_frame; j < tItems[index].frame_count - u32up_frame; j++) + u32sum_raw_data[i] += u32data_buff[i + j * core_mp.frame_len]; + + avg_result[i] = u32sum_raw_data[i] / (tItems[index].frame_count - u32down_frame - u32up_frame); + } + + if (debug_en) { + pr_cont("\n[Average result frist%d and last%d node data]\n", len, len); + for (i = 0; i < core_mp.frame_len; i++) { + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("%d,", avg_result[i]); + } + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("\n"); + } + + ipio_kfree((void **)&u32data_buff); + ipio_kfree((void **)&u32sum_raw_data); + return 0; +} + +static void mp_compare_cdc_result(int index, s32 *tmp, s32 *max_ts, s32 *min_ts, int *result) +{ + int i; + + if (ERR_ALLOC_MEM(tmp)) { + ILI_ERR("The data of test item is null (%p)\n", tmp); + *result = MP_DATA_FAIL; + return; + } + + if (tItems[index].catalog == SHORT_TEST) { + for (i = 0; i < core_mp.frame_len; i++) { + if (tmp[i] < min_ts[i]) { + *result = MP_DATA_FAIL; + return; + } + } + } else { + for (i = 0; i < core_mp.frame_len; i++) { + if (tmp[i] > max_ts[i] || tmp[i] < min_ts[i]) { + *result = MP_DATA_FAIL; + return; + } + } + } +} + +static int mp_compare_test_result(int index) +{ + int i, ret = 0, test_result = MP_DATA_PASS; + s32 *max_threshold = NULL, *min_threshold = NULL; + + if (tItems[index].catalog == PIN_TEST) + return tItems[index].item_result; + + max_threshold = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(max_threshold)) { + ILI_ERR("Failed to allocate threshold FRAME buffer\n"); + test_result = MP_DATA_FAIL; + ret = -EMP_NOMEM; + goto out; + } + + min_threshold = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(min_threshold)) { + ILI_ERR("Failed to allocate threshold FRAME buffer\n"); + test_result = MP_DATA_FAIL; + ret = -EMP_NOMEM; + goto out; + } + + /* Show test result as below */ + if (tItems[index].catalog == TX_RX_DELTA) { + if (ERR_ALLOC_MEM(core_mp.rx_delta_buf) || ERR_ALLOC_MEM(core_mp.tx_delta_buf)) { + ILI_ERR("This test item (%s) has no data inside its buffer\n", tItems[index].desp); + test_result = MP_DATA_FAIL; + ret = -EMP_NOMEM; + goto out; + } + + for (i = 0; i < core_mp.frame_len; i++) { + max_threshold[i] = core_mp.TxDeltaMax; + min_threshold[i] = core_mp.TxDeltaMin; + } + mp_compare_cdc_result(index, core_mp.tx_max_buf, max_threshold, min_threshold, &test_result); + mp_compare_cdc_result(index, core_mp.tx_min_buf, max_threshold, min_threshold, &test_result); + + for (i = 0; i < core_mp.frame_len; i++) { + max_threshold[i] = core_mp.RxDeltaMax; + min_threshold[i] = core_mp.RxDeltaMin; + } + + mp_compare_cdc_result(index, core_mp.rx_max_buf, max_threshold, min_threshold, &test_result); + mp_compare_cdc_result(index, core_mp.rx_min_buf, max_threshold, min_threshold, &test_result); + } else { + if (ERR_ALLOC_MEM(tItems[index].buf) || ERR_ALLOC_MEM(tItems[index].max_buf) || + ERR_ALLOC_MEM(tItems[index].min_buf) || ERR_ALLOC_MEM(tItems[index].result_buf)) { + ILI_ERR("This test item (%s) has no data inside its buffer\n", tItems[index].desp); + test_result = MP_DATA_FAIL; + goto out; + } + + if (tItems[index].spec_option == BENCHMARK) { + for (i = 0; i < core_mp.frame_len; i++) { + max_threshold[i] = tItems[index].bench_mark_max[i]; + min_threshold[i] = tItems[index].bench_mark_min[i]; + } + } else { + for (i = 0; i < core_mp.frame_len; i++) { + max_threshold[i] = tItems[index].max; + min_threshold[i] = tItems[index].min; + } + } + + /* general result */ + if (tItems[index].trimmed_mean && tItems[index].catalog != PEAK_TO_PEAK_TEST) { + mp_test_data_sort_average(tItems[index].buf, index, tItems[index].result_buf); + mp_compare_cdc_result(index, tItems[index].result_buf, max_threshold, min_threshold, &test_result); + } else { + mp_compare_cdc_result(index, tItems[index].max_buf, max_threshold, min_threshold, &test_result); + mp_compare_cdc_result(index, tItems[index].min_buf, max_threshold, min_threshold, &test_result); + } + } + +out: + ipio_kfree((void **)&max_threshold); + ipio_kfree((void **)&min_threshold); + tItems[index].item_result = test_result; + return ret; +} + +static void mp_do_retry(int index, int count) +{ + if (count == 0) { + ILI_INFO("Finish retry action\n"); + return; + } + + ILI_INFO("retry = %d, item = %s\n", count, tItems[index].desp); + + tItems[index].do_test(index); + + if (mp_compare_test_result(index) < 0) + return mp_do_retry(index, count - 1); +} + +static int mp_show_result(bool lcm_on) +{ + int ret = MP_DATA_PASS, seq; + int i, x, y, j, csv_len = 0, pass_item_count = 0, line_count = 0, get_frame_cont = 1; + s32 *max_threshold = NULL, *min_threshold = NULL; + char *csv = NULL; + char *ret_pass_name = NULL, *ret_fail_name = NULL; + struct file *f = NULL; + mm_segment_t fs; + loff_t pos; + + csv = vmalloc(CSV_FILE_SIZE); + if (ERR_ALLOC_MEM(csv)) { + ILI_ERR("Failed to allocate CSV mem\n"); + ret = -EMP_NOMEM; + goto fail_open; + } + + max_threshold = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + min_threshold = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(max_threshold) || ERR_ALLOC_MEM(min_threshold)) { + ILI_ERR("Failed to allocate threshold FRAME buffer\n"); + ret = -EMP_NOMEM; + goto fail_open; + } + + if (parser_get_ini_key_value("pv5_4 command", "date", core_mp.ini_date) < 0) + ipio_memcpy(core_mp.ini_date, "Unknown", strlen("Unknown"), strlen("Unknown")); + if (parser_get_ini_key_value("pv5_4 command", "version", core_mp.ini_ver) < 0) + ipio_memcpy(core_mp.ini_ver, "Unknown", strlen("Unknown"), strlen("Unknown")); + + mp_print_csv_header(csv, &csv_len, &line_count, CSV_FILE_SIZE); + + for (seq = 0; seq < ri.count; seq++) { + i = ri.index[seq]; + get_frame_cont = 1; + + if (tItems[i].item_result == MP_DATA_PASS) { + pr_info("\n[%s],OK \n", tItems[i].desp); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "\n[%s],OK\n", tItems[i].desp); + } else { + pr_info("\n[%s],NG \n", tItems[i].desp); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "\n[%s],NG\n", tItems[i].desp); + } + + if (tItems[i].catalog == PIN_TEST) { + pr_info("Test INT Pin = %d\n", tItems[i].test_int_pin); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Test INT Pin = %d\n", tItems[i].test_int_pin); + pr_info("Pulse Test = %d\n", tItems[i].int_pulse_test); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Pulse Test = %d\n", tItems[i].int_pulse_test); + pr_info("Delay Time = %d\n", tItems[i].delay_time); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Delay Time = %d\n", tItems[i].delay_time); + continue; + } + + mp_print_csv_cdc_cmd(csv, &csv_len, i, CSV_FILE_SIZE); + + pr_info("Frame count = %d\n", tItems[i].frame_count); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Frame count = %d\n", tItems[i].frame_count); + + if (tItems[i].trimmed_mean && tItems[i].catalog != PEAK_TO_PEAK_TEST) { + pr_info("lowest percentage = %d\n", tItems[i].lowest_percentage); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "lowest percentage = %d\n", tItems[i].lowest_percentage); + + pr_info("highest percentage = %d\n", tItems[i].highest_percentage); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "highest percentage = %d\n", tItems[i].highest_percentage); + } + + /* Show result of benchmark max and min */ + if (tItems[i].spec_option == BENCHMARK) { + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = tItems[i].bench_mark_max[j]; + min_threshold[j] = tItems[i].bench_mark_min[j]; + } + mp_compare_cdc_show_result(i, tItems[i].bench_mark_max, csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, "Max_Bench", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, tItems[i].bench_mark_min, csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, "Min_Bench", CSV_FILE_SIZE); + } else { + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = tItems[i].max; + min_threshold[j] = tItems[i].min; + } + + pr_info("Max = %d\n", tItems[i].max); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Max = %d\n", tItems[i].max); + + pr_info("Min = %d\n", tItems[i].min); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Min = %d\n", tItems[i].min); + } + + if (ipio_strcmp(tItems[i].desp, "open test(integration)_sp") == 0) { + mp_compare_cdc_show_result(i, frame1_cbk700, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "frame1 cbk700", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, frame1_cbk250, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "frame1 cbk250", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, frame1_cbk200, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "frame1 cbk200", CSV_FILE_SIZE); + } + + if (ipio_strcmp(tItems[i].desp, "open test_c") == 0) { + mp_compare_cdc_show_result(i, cap_dac, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "CAP_DAC", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, cap_raw, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "CAP_RAW", CSV_FILE_SIZE); + } + + if (tItems[i].catalog == TX_RX_DELTA) { + if (ERR_ALLOC_MEM(core_mp.rx_delta_buf) || ERR_ALLOC_MEM(core_mp.tx_delta_buf)) { + ILI_ERR("This test item (%s) has no data inside its buffer\n", tItems[i].desp); + continue; + } + } else { + if (ERR_ALLOC_MEM(tItems[i].buf) || ERR_ALLOC_MEM(tItems[i].max_buf) || + ERR_ALLOC_MEM(tItems[i].min_buf)) { + ILI_ERR("This test item (%s) has no data inside its buffer\n", tItems[i].desp); + continue; + } + } + + /* Show test result as below */ + if (tItems[i].catalog == KEY_TEST) { + for (x = 0; x < core_mp.key_len; x++) { + DUMP("KEY_%02d ", x); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "KEY_%02d,", x); + } + + DUMP("\n"); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "\n"); + + for (y = 0; y < core_mp.key_len; y++) { + DUMP(" %3d ", tItems[i].buf[y]); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), " %3d, ", tItems[i].buf[y]); + } + + DUMP("\n"); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "\n"); + } else if (tItems[i].catalog == TX_RX_DELTA) { + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = core_mp.TxDeltaMax; + min_threshold[j] = core_mp.TxDeltaMin; + } + mp_compare_cdc_show_result(i, core_mp.tx_max_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "TX Max Hold", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, core_mp.tx_min_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "TX Min Hold", CSV_FILE_SIZE); + + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = core_mp.RxDeltaMax; + min_threshold[j] = core_mp.RxDeltaMin; + } + mp_compare_cdc_show_result(i, core_mp.rx_max_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "RX Max Hold", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, core_mp.rx_min_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "RX Min Hold", CSV_FILE_SIZE); + } else { + /* general result */ + if (tItems[i].trimmed_mean && tItems[i].catalog != PEAK_TO_PEAK_TEST) { + mp_compare_cdc_show_result(i, tItems[i].result_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "Mean result", CSV_FILE_SIZE); + } else { + mp_compare_cdc_show_result(i, tItems[i].max_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "Max Hold", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, tItems[i].min_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "Min Hold", CSV_FILE_SIZE); + } + if (tItems[i].catalog != PEAK_TO_PEAK_TEST) + get_frame_cont = tItems[i].frame_count; + + /* result of each frame */ + for (j = 0; j < get_frame_cont; j++) { + char frame_name[128] = {0}; + snprintf(frame_name, (CSV_FILE_SIZE - csv_len), "Frame %d", (j+1)); + mp_compare_cdc_show_result(i, &tItems[i].buf[(j*core_mp.frame_len)], csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, frame_name, CSV_FILE_SIZE); + } + } + } + + memset(csv_name, 0, 128 * sizeof(char)); + + mp_print_csv_tail(csv, &csv_len, CSV_FILE_SIZE); + + for (i = 0; i < MP_TEST_ITEM; i++) { + if (tItems[i].run) { + if (tItems[i].item_result < 0) { + pass_item_count = 0; + break; + } + pass_item_count++; + } + } + + /* define csv file name */ + ret_pass_name = NORMAL_CSV_PASS_NAME; + ret_fail_name = NORMAL_CSV_FAIL_NAME; + + if (pass_item_count == 0) { + core_mp.final_result = MP_DATA_FAIL; + ret = MP_DATA_FAIL; + if (lcm_on) + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/%s_%s.csv", CSV_LCM_ON_PATH, get_date_time_str(), ret_fail_name); + else + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/%s_%s.csv", CSV_LCM_OFF_PATH, get_date_time_str(), ret_fail_name); + } else { + core_mp.final_result = MP_DATA_PASS; + ret = MP_DATA_PASS; + + if (core_mp.lost_benchmark) { + ret_pass_name = NULL; + ret_pass_name = NORMAL_CSV_WARNING_NAME; + ILI_ERR("WARNING! Golden and SPEC in ini file aren't matched!!\n"); + } + + if (lcm_on) + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/%s_%s.csv", CSV_LCM_ON_PATH, get_date_time_str(), ret_pass_name); + else + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/%s_%s.csv", CSV_LCM_OFF_PATH, get_date_time_str(), ret_pass_name); + } + + ILI_INFO("Open CSV : %s\n", csv_name); + + if (f == NULL) + f = filp_open(csv_name, O_WRONLY | O_CREAT | O_TRUNC, 644); + + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open CSV file"); + ret = -EMP_NOMEM; + goto fail_open; + } + + ILI_INFO("Open CSV succeed, its length = %d\n ", csv_len); + + if (csv_len >= CSV_FILE_SIZE) { + ILI_ERR("The length saved to CSV is too long !\n"); + ret = -EMP_INVAL; + goto fail_open; + } + + fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + vfs_write(f, csv, csv_len, &pos); + set_fs(fs); + filp_close(f, NULL); + + ILI_INFO("Writing Data into CSV succeed\n"); + +fail_open: + ipio_vfree((void **)&csv); + ipio_kfree((void **)&max_threshold); + ipio_kfree((void **)&min_threshold); + return ret; +} + +static void ilitek_tddi_mp_init_item(void) +{ + int i = 0; + + memset(&core_mp, 0, sizeof(core_mp)); + memset(&open_spec, 0, sizeof(open_spec)); + + core_mp.chip_pid = ilits->chip->pid; + core_mp.chip_id = ilits->chip->id; + core_mp.chip_type = ilits->chip->type; + core_mp.chip_ver = ilits->chip->ver; + core_mp.fw_ver = ilits->chip->fw_ver; + core_mp.protocol_ver = ilits->protocol->ver; + core_mp.core_ver = ilits->chip->core_ver; + core_mp.cdc_len = ilits->protocol->cdc_len; + core_mp.no_bk_shift = ilits->chip->no_bk_shift; + core_mp.xch_len = ilits->xch_num; + core_mp.ych_len = ilits->ych_num; + core_mp.frame_len = core_mp.xch_len * core_mp.ych_len; + core_mp.stx_len = 0; + core_mp.srx_len = 0; + core_mp.key_len = 0; + core_mp.st_len = 0; + core_mp.tdf = 240; + core_mp.busy_cdc = INT_CHECK; + core_mp.retry = false; + core_mp.td_retry = false; + core_mp.final_result = MP_DATA_FAIL; + core_mp.lost_benchmark = false; + + ILI_INFO("============== TP & Panel info ================\n"); + ILI_INFO("Driver version = %s\n", DRIVER_VERSION); + ILI_INFO("TP Module = %s\n", ilits->md_name); + ILI_INFO("CHIP = 0x%x\n", core_mp.chip_pid); + ILI_INFO("Firmware version = %x\n", core_mp.fw_ver); + ILI_INFO("Protocol version = %x\n", core_mp.protocol_ver); + ILI_INFO("Core version = %x\n", core_mp.core_ver); + ILI_INFO("Read CDC Length = %d\n", core_mp.cdc_len); + ILI_INFO("X length = %d, Y length = %d\n", core_mp.xch_len, core_mp.ych_len); + ILI_INFO("Frame length = %d\n", core_mp.frame_len); + ILI_INFO("Check busy method = %s\n", (core_mp.busy_cdc ? "Polling" : "Interrupt")); + ILI_INFO("===============================================\n"); + + for (i = 0; i < MP_TEST_ITEM; i++) { + tItems[i].spec_option = 0; + tItems[i].type_option = 0; + tItems[i].run = false; + tItems[i].max = 0; + tItems[i].max_res = MP_DATA_FAIL; + tItems[i].item_result = MP_DATA_PASS; + tItems[i].min = 0; + tItems[i].min_res = MP_DATA_FAIL; + tItems[i].frame_count = 0; + tItems[i].trimmed_mean = 0; + tItems[i].lowest_percentage = 0; + tItems[i].highest_percentage = 0; + tItems[i].v_tdf_1 = 0; + tItems[i].v_tdf_2 = 0; + tItems[i].h_tdf_1 = 0; + tItems[i].h_tdf_2 = 0; + tItems[i].goldenmode = 0; + tItems[i].result_buf = NULL; + tItems[i].buf = NULL; + tItems[i].max_buf = NULL; + tItems[i].min_buf = NULL; + tItems[i].bench_mark_max = NULL; + tItems[i].bench_mark_min = NULL; + tItems[i].node_type = NULL; + tItems[i].delay_time = 0; + tItems[i].test_int_pin = 0; + tItems[i].int_pulse_test = 0; + + if (tItems[i].catalog == MUTUAL_TEST) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == TX_RX_DELTA) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == UNTOUCH_P2P) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == PIXEL) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == OPEN_TEST) { + if (ipio_strcmp(tItems[i].desp, "open test(integration)_sp") == 0) + tItems[i].do_test = open_test_sp; + else if (ipio_strcmp(tItems[i].desp, "open test_c") == 0) + tItems[i].do_test = open_test_cap; + else + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == KEY_TEST) { + tItems[i].do_test = key_test; + } else if (tItems[i].catalog == SELF_TEST) { + tItems[i].do_test = self_test; + } else if (tItems[i].catalog == ST_TEST) { + tItems[i].do_test = st_test; + } else if (tItems[i].catalog == PEAK_TO_PEAK_TEST) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == SHORT_TEST) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == PIN_TEST) { + tItems[i].do_test = pin_test; + } + + tItems[i].result = kmalloc(16, GFP_KERNEL); + snprintf(tItems[i].result, 16, "%s", "FAIL"); + } +} + +static void mp_p2p_td_retry_after_ra_fail(int p2p_td) +{ + int i; + + for (i = 0; i < MP_TEST_ITEM; i++) { + if (ipio_strcmp(tItems[i].desp, "noise peak to peak(with panel) (lcm off)") == 0) + break; + } + + if (i >= MP_TEST_ITEM) + return; + + ILI_DBG("i = %d, p2p_noise_ret = %d, p2p_noise_run = %d\n", + i, tItems[i].item_result, tItems[i].run); + + if (tItems[i].item_result == MP_DATA_PASS && tItems[i].run == 1) + tItems[p2p_td].do_test(p2p_td); +} + +static void mp_test_run(int index) +{ + int i = index; + char str[512] = {0}; + + + /* Get parameters from ini */ + parser_get_int_data(tItems[i].desp, "spec option", str, sizeof(str)); + tItems[i].spec_option = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "type option", str, sizeof(str)); + tItems[i].type_option = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "frame count", str, sizeof(str)); + tItems[i].frame_count = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "trimmed mean", str, sizeof(str)); + tItems[i].trimmed_mean = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "lowest percentage", str, sizeof(str)); + tItems[i].lowest_percentage = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "highest percentage", str, sizeof(str)); + tItems[i].highest_percentage = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "goldenmode", str, sizeof(str)); + tItems[i].goldenmode = ili_katoi(str); + + if (tItems[i].goldenmode && (tItems[i].spec_option != tItems[i].goldenmode)) + core_mp.lost_benchmark = true; + + /* Get pin test delay time */ + if (tItems[i].catalog == PIN_TEST) { + parser_get_int_data(tItems[i].desp, "test int pin", str, sizeof(str)); + tItems[i].test_int_pin = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "int pulse test", str, sizeof(str)); + tItems[i].int_pulse_test = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "delay time", str, sizeof(str)); + tItems[i].delay_time = ili_katoi(str); + } + + /* Get TDF value from ini */ + if (tItems[i].catalog == SHORT_TEST) { + parser_get_int_data(tItems[i].desp, "v_tdf_1", str, sizeof(str)); + tItems[i].v_tdf_1 = parser_get_tdf_value(str, tItems[i].catalog); + parser_get_int_data(tItems[i].desp, "v_tdf_2", str, sizeof(str)); + tItems[i].v_tdf_2 = parser_get_tdf_value(str, tItems[i].catalog); + parser_get_int_data(tItems[i].desp, "h_tdf_1", str, sizeof(str)); + tItems[i].h_tdf_1 = parser_get_tdf_value(str, tItems[i].catalog); + parser_get_int_data(tItems[i].desp, "h_tdf_2", str, sizeof(str)); + tItems[i].h_tdf_2 = parser_get_tdf_value(str, tItems[i].catalog); + } else { + parser_get_int_data(tItems[i].desp, "v_tdf", str, sizeof(str)); + tItems[i].v_tdf_1 = parser_get_tdf_value(str, tItems[i].catalog); + parser_get_int_data(tItems[i].desp, "h_tdf", str, sizeof(str)); + tItems[i].h_tdf_1 = parser_get_tdf_value(str, tItems[i].catalog); + } + + /* Get threshold from ini structure in parser */ + if (ipio_strcmp(tItems[i].desp, "tx/rx delta") == 0) { + parser_get_int_data(tItems[i].desp, "tx max", str, sizeof(str)); + core_mp.TxDeltaMax = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "tx min", str, sizeof(str)); + core_mp.TxDeltaMin = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "rx max", str, sizeof(str)); + core_mp.RxDeltaMax = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "rx min", str, sizeof(str)); + core_mp.RxDeltaMin = ili_katoi(str); + ILI_DBG("%s: Tx Max = %d, Tx Min = %d, Rx Max = %d, Rx Min = %d\n", + tItems[i].desp, core_mp.TxDeltaMax, core_mp.TxDeltaMin, + core_mp.RxDeltaMax, core_mp.RxDeltaMin); + } else { + parser_get_int_data(tItems[i].desp, "max", str, sizeof(str)); + tItems[i].max = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "min", str, sizeof(str)); + tItems[i].min = ili_katoi(str); + } + + ILI_DBG("%s: run = %d, max = %d, min = %d, frame_count = %d\n", tItems[i].desp, + tItems[i].run, tItems[i].max, tItems[i].min, tItems[i].frame_count); + + ILI_DBG("v_tdf_1 = %d, v_tdf_2 = %d, h_tdf_1 = %d, h_tdf_2 = %d", tItems[i].v_tdf_1, + tItems[i].v_tdf_2, tItems[i].h_tdf_1, tItems[i].h_tdf_2); + + ILI_INFO("Run MP Test Item : %s\n", tItems[i].desp); + tItems[i].do_test(i); + + mp_compare_test_result(i); + + /* P2P TD retry after RA sample failed. */ + if (ipio_strcmp(tItems[i].desp, "peak to peak_td (lcm off)") == 0 && + tItems[i].item_result == MP_DATA_FAIL) { + parser_get_int_data(tItems[i].desp, "recheck ptop lcm off", str, sizeof(str)); + ILI_INFO("Peak to Peak TD retry = %d\n", ili_katoi(str)); + core_mp.td_retry = ili_katoi(str); + if (core_mp.td_retry) + mp_p2p_td_retry_after_ra_fail(i); + } + + if (core_mp.retry && tItems[i].item_result == MP_DATA_FAIL) { + ILI_INFO("MP failed, doing retry\n"); + mp_do_retry(i, RETRY_COUNT); + } + +} + +static void mp_test_free(void) +{ + int i; + + ILI_INFO("Free all allocated mem for MP\n"); + + core_mp.final_result = MP_DATA_FAIL; + core_mp.td_retry = false; + + for (i = 0; i < MP_TEST_ITEM; i++) { + tItems[i].run = false; + tItems[i].max_res = MP_DATA_FAIL; + tItems[i].min_res = MP_DATA_FAIL; + tItems[i].item_result = MP_DATA_PASS; + + if (tItems[i].catalog == TX_RX_DELTA) { + ipio_kfree((void **)&core_mp.rx_delta_buf); + ipio_kfree((void **)&core_mp.tx_delta_buf); + ipio_kfree((void **)&core_mp.tx_max_buf); + ipio_kfree((void **)&core_mp.tx_min_buf); + ipio_kfree((void **)&core_mp.rx_max_buf); + ipio_kfree((void **)&core_mp.rx_min_buf); + } else { + if (tItems[i].spec_option == BENCHMARK) { + ipio_kfree((void **)&tItems[i].bench_mark_max); + ipio_kfree((void **)&tItems[i].bench_mark_min); + } + ipio_kfree((void **)&tItems[i].node_type); + ipio_kfree((void **)&tItems[i].result); + ipio_kfree((void **)&tItems[i].result_buf); + ipio_kfree((void **)&tItems[i].max_buf); + ipio_kfree((void **)&tItems[i].min_buf); + ipio_vfree((void **)&tItems[i].buf); + } + } + + ipio_kfree((void **)&frame1_cbk700); + ipio_kfree((void **)&frame1_cbk250); + ipio_kfree((void **)&frame1_cbk200); + ipio_kfree((void **)&frame_buf); + ipio_kfree((void **)&key_buf); +} + +/* The method to copy results to user depends on what APK needs */ +static void mp_copy_ret_to_apk(char *buf) +{ + int i, seq, len = 2; + + if (!buf) { + ILI_ERR("apk buffer is null\n"); + return; + } + + len += snprintf(buf + len, PAGE_SIZE - len, "CSV path: %s\n\n", csv_name); + for (seq = 0; seq < ri.count; seq++) { + i = ri.index[seq]; + if (tItems[i].item_result == MP_DATA_FAIL) { + ILI_ERR("[%s] = FAIL\n", tItems[i].desp); + len += snprintf(buf + len, PAGE_SIZE - len, "[%s] = FAIL\n", tItems[i].desp); + } else { + ILI_INFO("[%s] = PASS\n", tItems[i].desp); + len += snprintf(buf + len, PAGE_SIZE - len, "[%s] = PASS\n", tItems[i].desp); + } + } + ilits->mp_ret_len = len; +} + +static int mp_sort_item(bool lcm_on) +{ + int i, j; + char str[128] = {0}; + + ri.count = 0; + memset(ri.index, 0x0, MP_TEST_ITEM); + + for (i = 0; i < MAX_SECTION_NUM; i++) { + for (j = 0; j < MP_TEST_ITEM; j++) { + if (ipio_strcmp(seq_item[i], tItems[j].desp) != 0) + continue; + + parser_get_int_data(tItems[j].desp, "enable", str, sizeof(str)); + tItems[j].run = ili_katoi(str); + if (tItems[j].run != 1 || tItems[j].lcm != lcm_on) + continue; + + if (ri.count > MP_TEST_ITEM) { + ILI_ERR("Test item(%d) is invaild, abort\n", ri.count); + return -EINVAL; + } + + ri.index[ri.count] = j; + ri.count++; + } + } + return 0; +} + +int ili_mp_test_main(char *apk, bool lcm_on) +{ + int i, ret = 0; + char str[128] = {0}, ver[128] = {0}; + + if (ilits->xch_num <= 0 || ilits->ych_num <= 0) { + ILI_ERR("Invalid frame length (%d, %d)\n", ilits->xch_num, ilits->ych_num); + ret = -EMP_INVAL; + goto out; + } + + ini_info = (struct ini_file_data *)vmalloc(sizeof(struct ini_file_data) * PARSER_MAX_KEY_NUM); + if (ERR_ALLOC_MEM(ini_info)) { + ILI_ERR("Failed to malloc ini_info\n"); + ret = -EMP_NOMEM; + goto out; + } + + ilitek_tddi_mp_init_item(); + + ret = ilitek_tddi_mp_ini_parser(ilits->md_ini_path); + if (ret < 0) { + ILI_ERR("Failed to parsing INI file\n"); + ret = -EMP_INI; + goto out; + } + + /* Compare both protocol version of ini and firmware */ + parser_get_ini_key_value("pv5_4 command", "protocol", str); + snprintf(ver, sizeof(ver), "0x%s", str); + if ((ili_str2hex(ver)) != (core_mp.protocol_ver >> 8)) { + ILI_ERR("ERROR! MP Protocol version is invaild, 0x%x\n", ili_str2hex(ver)); + ret = -EMP_PROTOCOL; + goto out; + } + + /* Read timing info from ini file */ + if (mp_get_timing_info() < 0) { + ILI_ERR("Failed to get timing info from ini\n"); + ret = -EMP_TIMING_INFO; + goto out; + } + +#if MP_INT_LEVEL + if (ili_ic_int_trigger_ctrl(true) < 0) { + ILI_ERR("Failed to set INT as Level trigger"); + ret = -EMP_CMD; + goto out; + } +#endif + + /* Sort test item by ini file */ + if (mp_sort_item(lcm_on) < 0) { + ILI_ERR("Failed to sort test item\n"); + ret = -EMP_INI; + goto out; + } + + for (i = 0; i < ri.count; i++) + mp_test_run(ri.index[i]); + + ret = mp_show_result(lcm_on); + + mp_copy_ret_to_apk(apk); + +out: + mp_test_free(); + +#if MP_INT_LEVEL + if (ili_ic_int_trigger_ctrl(false) < 0) { + ILI_ERR("Failed to set INT back to pluse trigger"); + ret = -EMP_CMD; + } +#endif + + ipio_vfree((void **)&ini_info); + return ret; +}; diff --git a/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_node.c b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_node.c new file mode 100644 index 000000000000..a607df34e076 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_node.c @@ -0,0 +1,2553 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek.h" + +#define USER_STR_BUFF PAGE_SIZE +#define IOCTL_I2C_BUFF PAGE_SIZE +#define ILITEK_IOCTL_MAGIC 100 +#define ILITEK_IOCTL_MAXNR 27 + +#define ILITEK_IOCTL_I2C_WRITE_DATA _IOWR(ILITEK_IOCTL_MAGIC, 0, u8*) +#define ILITEK_IOCTL_I2C_SET_WRITE_LENGTH _IOWR(ILITEK_IOCTL_MAGIC, 1, int) +#define ILITEK_IOCTL_I2C_READ_DATA _IOWR(ILITEK_IOCTL_MAGIC, 2, u8*) +#define ILITEK_IOCTL_I2C_SET_READ_LENGTH _IOWR(ILITEK_IOCTL_MAGIC, 3, int) + +#define ILITEK_IOCTL_TP_HW_RESET _IOWR(ILITEK_IOCTL_MAGIC, 4, int) +#define ILITEK_IOCTL_TP_POWER_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 5, int) +#define ILITEK_IOCTL_TP_REPORT_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 6, int) +#define ILITEK_IOCTL_TP_IRQ_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 7, int) + +#define ILITEK_IOCTL_TP_DEBUG_LEVEL _IOWR(ILITEK_IOCTL_MAGIC, 8, int) +#define ILITEK_IOCTL_TP_FUNC_MODE _IOWR(ILITEK_IOCTL_MAGIC, 9, int) + +#define ILITEK_IOCTL_TP_FW_VER _IOWR(ILITEK_IOCTL_MAGIC, 10, u8*) +#define ILITEK_IOCTL_TP_PL_VER _IOWR(ILITEK_IOCTL_MAGIC, 11, u8*) +#define ILITEK_IOCTL_TP_CORE_VER _IOWR(ILITEK_IOCTL_MAGIC, 12, u8*) +#define ILITEK_IOCTL_TP_DRV_VER _IOWR(ILITEK_IOCTL_MAGIC, 13, u8*) +#define ILITEK_IOCTL_TP_CHIP_ID _IOWR(ILITEK_IOCTL_MAGIC, 14, u32*) + +#define ILITEK_IOCTL_TP_NETLINK_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 15, int*) +#define ILITEK_IOCTL_TP_NETLINK_STATUS _IOWR(ILITEK_IOCTL_MAGIC, 16, int*) + +#define ILITEK_IOCTL_TP_MODE_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 17, u8*) +#define ILITEK_IOCTL_TP_MODE_STATUS _IOWR(ILITEK_IOCTL_MAGIC, 18, int*) +#define ILITEK_IOCTL_ICE_MODE_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 19, int) + +#define ILITEK_IOCTL_TP_INTERFACE_TYPE _IOWR(ILITEK_IOCTL_MAGIC, 20, u8*) +#define ILITEK_IOCTL_TP_DUMP_FLASH _IOWR(ILITEK_IOCTL_MAGIC, 21, int) +#define ILITEK_IOCTL_TP_FW_UART_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 22, u8*) +#define ILITEK_IOCTL_TP_PANEL_INFO _IOWR(ILITEK_IOCTL_MAGIC, 23, u32*) +#define ILITEK_IOCTL_TP_INFO _IOWR(ILITEK_IOCTL_MAGIC, 24, u32*) +#define ILITEK_IOCTL_WRAPPER_RW _IOWR(ILITEK_IOCTL_MAGIC, 25, u8*) +#define ILITEK_IOCTL_DDI_WRITE _IOWR(ILITEK_IOCTL_MAGIC, 26, u8*) +#define ILITEK_IOCTL_DDI_READ _IOWR(ILITEK_IOCTL_MAGIC, 27, u8*) + +#ifdef CONFIG_COMPAT +#define ILITEK_COMPAT_IOCTL_I2C_WRITE_DATA _IOWR(ILITEK_IOCTL_MAGIC, 0, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_I2C_SET_WRITE_LENGTH _IOWR(ILITEK_IOCTL_MAGIC, 1, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_I2C_READ_DATA _IOWR(ILITEK_IOCTL_MAGIC, 2, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_I2C_SET_READ_LENGTH _IOWR(ILITEK_IOCTL_MAGIC, 3, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_HW_RESET _IOWR(ILITEK_IOCTL_MAGIC, 4, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_POWER_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 5, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_REPORT_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 6, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_IRQ_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 7, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_DEBUG_LEVEL _IOWR(ILITEK_IOCTL_MAGIC, 8, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_FUNC_MODE _IOWR(ILITEK_IOCTL_MAGIC, 9, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_FW_VER _IOWR(ILITEK_IOCTL_MAGIC, 10, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_PL_VER _IOWR(ILITEK_IOCTL_MAGIC, 11, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_CORE_VER _IOWR(ILITEK_IOCTL_MAGIC, 12, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_DRV_VER _IOWR(ILITEK_IOCTL_MAGIC, 13, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_CHIP_ID _IOWR(ILITEK_IOCTL_MAGIC, 14, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_NETLINK_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 15, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_NETLINK_STATUS _IOWR(ILITEK_IOCTL_MAGIC, 16, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_MODE_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 17, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_MODE_STATUS _IOWR(ILITEK_IOCTL_MAGIC, 18, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_ICE_MODE_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 19, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_INTERFACE_TYPE _IOWR(ILITEK_IOCTL_MAGIC, 20, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_DUMP_FLASH _IOWR(ILITEK_IOCTL_MAGIC, 21, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_FW_UART_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 22, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_PANEL_INFO _IOWR(ILITEK_IOCTL_MAGIC, 23, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_INFO _IOWR(ILITEK_IOCTL_MAGIC, 24, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_WRAPPER_RW _IOWR(ILITEK_IOCTL_MAGIC, 25, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_DDI_WRITE _IOWR(ILITEK_IOCTL_MAGIC, 26, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_DDI_READ _IOWR(ILITEK_IOCTL_MAGIC, 27, compat_uptr_t) +#endif + +struct record_state { + u8 touch_palm_state_e : 2; + u8 app_an_statu_e : 3; + u8 app_sys_check_bg_abnormal : 1; + u8 g_b_wrong_bg : 1; +}; + +static unsigned char g_user_buf[USER_STR_BUFF] = {0}; +#define ILI_SPI_NAME "ilitek" +static struct class *touchscreen_class; +static struct device *touchscreen_class_dev; + +int ili_str2hex(char *str) +{ + int strlen, result, intermed, intermedtop; + char *s = str; + + while (*s != 0x0) { + s++; + } + + strlen = (int)(s - str); + s = str; + if (*s != 0x30) { + return -1; + } + + s++; + + if (*s != 0x78 && *s != 0x58) { + return -1; + } + s++; + + strlen = strlen - 3; + result = 0; + while (*s != 0x0) { + intermed = *s & 0x0f; + intermedtop = *s & 0xf0; + if (intermedtop == 0x60 || intermedtop == 0x40) { + intermed += 0x09; + } + intermed = intermed << (strlen << 2); + result = result | intermed; + strlen -= 1; + s++; + } + return result; +} + +int ili_katoi(char *str) +{ + int result = 0; + unsigned int digit; + int sign; + + if (*str == '-') { + sign = 1; + str += 1; + } else { + sign = 0; + if (*str == '+') { + str += 1; + } + } + + for (;; str += 1) { + digit = *str - '0'; + if (digit > 9) + break; + result = (10 * result) + digit; + } + + if (sign) { + return -result; + } + return result; +} + +struct file_buffer { + char *ptr; + char fname[128]; + int32_t wlen; + int32_t flen; + int32_t max_size; +}; + +static int file_write(struct file_buffer *file, bool new_open) +{ + struct file *f = NULL; + mm_segment_t fs; + loff_t pos; + + if (file->ptr == NULL) { + ILI_ERR("str is invaild\n"); + return -1; + } + + /* Compiler complains that this comparison is always false. + if (file->fname == NULL) { + ILI_ERR("file name is invaild\n"); + return -1; + }*/ + + if (file->flen >= file->max_size) { + ILI_ERR("The length saved to file is too long !\n"); + return -1; + } + + if (new_open) + f = filp_open(file->fname, O_WRONLY | O_CREAT | O_TRUNC, 644); + else + f = filp_open(file->fname, O_WRONLY | O_CREAT | O_APPEND, 644); + + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open %s file\n", file->fname); + return -1; + } + + fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + vfs_write(f, file->ptr, file->flen, &pos); + set_fs(fs); + filp_close(f, NULL); + return 0; +} + +static int ilitek_debug_node_buff_control(bool open) +{ + int i, ret; + + ilits->dnp = open; + + ILI_INFO("Debug buf ctrl = %s\n", ilits->dnp ? "Enabled" : "Disabled"); + + if (open) { + ilits->dbf = 0; + ilits->odi = 0; + ipio_kfree((void **)&ilits->dbl); + ilits->dbl = kzalloc(TR_BUF_LIST_SIZE * sizeof(*ilits->dbl), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->dbl)) { + ILI_ERR("Failed to allocate ilits->dbl mem, %ld\n", PTR_ERR(ilits->dbl)); + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < TR_BUF_LIST_SIZE; i++) { + ilits->dbl[i].mark = false; + ipio_kfree((void **)&ilits->dbl[i].data); + ilits->dbl[i].data = kzalloc(TR_BUF_SIZE * sizeof(unsigned char), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->dbl[i].data)) { + ILI_ERR("Failed to allocate dbl[%d] mem, %ld\n", i, PTR_ERR(ilits->dbl[i].data)); + ret = -ENOMEM; + goto out; + } + } + return 0; + } + +out: + for (i = 0; i < TR_BUF_LIST_SIZE; i++) { + ilits->dbl[i].mark = false; + ipio_kfree((void **)&ilits->dbl[i].data); + } + ipio_kfree((void **)&ilits->dbl); + return ret; +} + +static int debug_mode_get_data(struct file_buffer *file, u8 type, u32 frame_count) +{ + int ret; + u8 cmd[2] = { 0 }, row, col; + s16 temp; + unsigned char *ptr; + int j; + u16 write_index = 0; + + ilitek_debug_node_buff_control(DISABLE); + + ilits->dbf = 0; + row = ilits->ych_num; + col = ilits->xch_num; + mutex_lock(&ilits->touch_mutex); + + cmd[0] = 0xFA; + cmd[1] = type; + ret = ilits->wrapper(cmd, 2, NULL, 0, ON, OFF); + + if (ilitek_debug_node_buff_control(ENABLE) < 0) { + ILI_ERR("Failed to allocate debug buf\n"); + ret = -ENOMEM; + goto out; + } + + mutex_unlock(&ilits->touch_mutex); + + if (ret < 0) { + ILI_ERR("Write 0xFA,0x%x failed\n", type); + goto out; + } + + while (write_index < frame_count) { + file->wlen = 0; + ILI_INFO("frame = %d,index = %d,count = %d\n", write_index, ilits->odi, ilits->dbf); + if (!wait_event_interruptible_timeout(ilits->inq, ilits->dbl[ilits->odi].mark, msecs_to_jiffies(3000))) { + ILI_ERR("debug mode get data timeout!\n"); + goto out; + } + + mutex_lock(&ilits->touch_mutex); + + memset(file->ptr, 0, file->max_size); + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "\n\nFrame%d,", write_index); + for (j = 0; j < col; j++) + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "[X%d] ,", j); + ptr = &ilits->dbl[ilits->odi].data[35]; + for (j = 0; j < row * col; j++, ptr += 2) { + temp = (*ptr << 8) + *(ptr + 1); + if (j % col == 0) + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "\n[Y%d] ,", (j / col)); + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "%d, ", temp); + } + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "\n[X] ,"); + for (j = 0; j < row + col; j++, ptr += 2) { + temp = (*ptr << 8) + *(ptr + 1); + if (j == col) + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "\n[Y] ,"); + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "%d, ", temp); + } + file_write(file, false); + write_index++; + + mutex_unlock(&ilits->touch_mutex); + + ilits->dbl[ilits->odi].mark = false; + ilits->odi = ((ilits->odi + 1) % TR_BUF_LIST_SIZE); + } +out: + ilitek_debug_node_buff_control(DISABLE); + return ret; +} + +static ssize_t ilitek_proc_get_delta_data_read(struct file *pFile, char __user *buf, size_t size, loff_t *pos) +{ + s16 *delta = NULL; + int row = 0, col = 0, index = 0; + int ret, i, x, y; + int read_length = 0, len = 0; + u8 cmd[2] = {0}; + u8 *data = NULL; + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + ili_wq_ctrl(WQ_ESD, DISABLE); + ili_wq_ctrl(WQ_BAT, DISABLE); + mutex_lock(&ilits->touch_mutex); + + row = ilits->ych_num; + col = ilits->xch_num; + read_length = 4 + 2 * row * col + 1 ; + + ILI_INFO("read length = %d\n", read_length); + + data = kcalloc(read_length + 1, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(data)) { + ILI_ERR("Failed to allocate data mem\n"); + goto out; + } + + delta = kcalloc(P5_X_DEBUG_MODE_PACKET_LENGTH, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(delta)) { + ILI_ERR("Failed to allocate delta mem\n"); + goto out; + } + + cmd[0] = 0xB7; + cmd[1] = 0x1; //get delta + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Failed to write 0xB7,0x1 command, %d\n", ret); + goto out; + } + + msleep(120); + + /* read debug packet header */ + ret = ilits->wrapper(NULL, 0, data, read_length, OFF, OFF); + if (ret < 0) { + ILI_ERR("Read debug packet header failed, %d\n", ret); + goto out; + } + + cmd[1] = 0x03; //switch to normal mode + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, ON, OFF); + if (ret < 0) { + ILI_ERR("Failed to write 0xB7,0x3 command, %d\n", ret); + goto out; + } + + for (i = 4, index = 0; i < row * col * 2 + 4; i += 2, index++) + delta[index] = (data[i] << 8) + data[i + 1]; + + len = snprintf(g_user_buf + size, PAGE_SIZE - len, "======== Deltadata ========\n"); + ILI_INFO("======== Deltadata ========\n"); + + len += snprintf(g_user_buf + len, PAGE_SIZE - len, + "Header 0x%x ,Type %d, Length %d\n", data[0], data[1], (data[2] << 8) | data[3]); + ILI_INFO("Header 0x%x ,Type %d, Length %d\n", data[0], data[1], (data[2] << 8) | data[3]); + + // print delta data + for (y = 0; y < row; y++) { + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "[%2d] ", (y+1)); + ILI_INFO("[%2d] ", (y+1)); + + for (x = 0; x < col; x++) { + int shift = y * col + x; + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "%5d", delta[shift]); + printk(KERN_CONT "%5d", delta[shift]); + } + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "\n"); + printk(KERN_CONT "\n"); + } + + if (copy_to_user(buf, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + +out: + mutex_unlock(&ilits->touch_mutex); + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + ipio_kfree((void **)&data); + ipio_kfree((void **)&delta); + return len; +} + +static ssize_t ilitek_proc_fw_get_raw_data_read(struct file *pFile, char __user *buf, size_t size, loff_t *pos) +{ + s16 *rawdata = NULL; + int row = 0, col = 0, index = 0; + int ret, i, x, y; + int read_length = 0, len = 0; + u8 cmd[2] = {0}; + u8 *data = NULL; + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + ili_wq_ctrl(WQ_ESD, DISABLE); + ili_wq_ctrl(WQ_BAT, DISABLE); + mutex_lock(&ilits->touch_mutex); + + row = ilits->ych_num; + col = ilits->xch_num; + read_length = 4 + 2 * row * col + 1 ; + + ILI_INFO("read length = %d\n", read_length); + + data = kcalloc(read_length + 1, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(data)) { + ILI_ERR("Failed to allocate data mem\n"); + goto out; + } + + rawdata = kcalloc(P5_X_DEBUG_MODE_PACKET_LENGTH, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(rawdata)) { + ILI_ERR("Failed to allocate rawdata mem\n"); + goto out; + } + + cmd[0] = 0xB7; + cmd[1] = 0x2; //get rawdata + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Failed to write 0xB7,0x2 command, %d\n", ret); + goto out; + } + + msleep(120); + + /* read debug packet header */ + ret = ilits->wrapper(NULL, 0, data, read_length, OFF, OFF); + if (ret < 0) { + ILI_ERR("Read debug packet header failed, %d\n", ret); + goto out; + } + + cmd[1] = 0x03; //switch to normal mode + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, ON, OFF); + if (ret < 0) { + ILI_ERR("Failed to write 0xB7,0x3 command, %d\n", ret); + goto out; + } + + for (i = 4, index = 0; i < row * col * 2 + 4; i += 2, index++) + rawdata[index] = (data[i] << 8) + data[i + 1]; + + len = snprintf(g_user_buf, PAGE_SIZE, "======== RawData ========\n"); + ILI_INFO("======== RawData ========\n"); + + len += snprintf(g_user_buf + len, PAGE_SIZE - len, + "Header 0x%x ,Type %d, Length %d\n", data[0], data[1], (data[2] << 8) | data[3]); + ILI_INFO("Header 0x%x ,Type %d, Length %d\n", data[0], data[1], (data[2] << 8) | data[3]); + + // print raw data + for (y = 0; y < row; y++) { + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "[%2d] ", (y+1)); + ILI_INFO("[%2d] ", (y+1)); + + for (x = 0; x < col; x++) { + int shift = y * col + x; + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "%5d", rawdata[shift]); + printk(KERN_CONT "%5d", rawdata[shift]); + } + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "\n"); + printk(KERN_CONT "\n"); + } + + if (copy_to_user(buf, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + +out: + mutex_unlock(&ilits->touch_mutex); + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + ipio_kfree((void **)&data); + ipio_kfree((void **)&rawdata); + return len; +} + +static ssize_t ilitek_proc_fw_pc_counter_read(struct file *pFile, char __user *buf, size_t size, loff_t *pos) +{ + int len = 0; + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + ili_ic_get_pc_counter(0); + len = snprintf(g_user_buf, PAGE_SIZE, "pc = 0x%x, latch = 0x%x\n", ilits->fw_pc, ilits->fw_latch); + + if (copy_to_user(buf, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + return len; +} + +static u32 rw_reg[5] = {0}; +static ssize_t ilitek_proc_rw_tp_reg_read(struct file *pFile, char __user *buf, size_t size, loff_t *pos) +{ + int ret = 0, len = 0; + bool mcu_on = 0, read = 0; + u32 type, addr, read_data, write_data, write_len, stop_mcu; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + + if (*pos != 0) + return 0; + + stop_mcu = rw_reg[0]; + type = rw_reg[1]; + addr = rw_reg[2]; + write_data = rw_reg[3]; + write_len = rw_reg[4]; + + ILI_INFO("stop_mcu = %d\n", rw_reg[0]); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + mutex_lock(&ilits->touch_mutex); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + if (stop_mcu == mcu_on) + ret = ili_ice_mode_ctrl(ENABLE, ON); + else + ret = ili_ice_mode_ctrl(ENABLE, OFF); + + if (ret < 0) { + ILI_ERR("Failed to enter ICE mode, ret = %d\n", ret); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to enter ICE mode"); + } + + if (type == read) { + if (ili_ice_mode_read(addr, &read_data, sizeof(u32)) < 0) { + ILI_ERR("Read data error\n"); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Read data error"); + } + + ILI_INFO("[READ]:addr = 0x%06x, read = 0x%08x\n", addr, read_data); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "READ:addr = 0x%06x, read = 0x%08x\n", addr, read_data); + } else { + if (ili_ice_mode_write(addr, write_data, write_len) < 0) { + ILI_ERR("Write data error\n"); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Write data error"); + } + + ILI_INFO("[WRITE]:addr = 0x%06x, write = 0x%08x, len = %d byte\n", addr, write_data, write_len); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "WRITE:addr = 0x%06x, write = 0x%08x, len =%d byte\n", addr, write_data, write_len); + } + + if (stop_mcu == mcu_on) + ret = ili_ice_mode_ctrl(DISABLE, ON); + else + ret = ili_ice_mode_ctrl(DISABLE, OFF); + + if (ret < 0) { + ILI_ERR("Failed to disable ICE mode, ret = %d\n", ret); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to disable ICE mode"); + } + + if (copy_to_user((char *)buf, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_proc_rw_tp_reg_write(struct file *filp, const char *buff, size_t size, loff_t *pos) +{ + char *token = NULL, *cur = NULL; + char cmd[256] = { 0 }; + u32 count = 0; + + if ((size - 1) > sizeof(cmd)) { + ILI_ERR("ERROR! input length is larger than local buffer\n"); + return -1; + } + + mutex_lock(&ilits->touch_mutex); + + if (buff != NULL) { + if (copy_from_user(cmd, buff, size - 1)) { + ILI_INFO("Failed to copy data from user space\n"); + size = -1; + goto out; + } + } + token = cur = cmd; + while ((token = strsep(&cur, ",")) != NULL) { + rw_reg[count] = ili_str2hex(token); + ILI_INFO("rw_reg[%d] = 0x%x\n", count, rw_reg[count]); + count++; + } + +out: + mutex_unlock(&ilits->touch_mutex); + return size; +} + +static ssize_t ilitek_proc_debug_switch_read(struct file *pFile, char __user *buff, size_t size, loff_t *pos) +{ + bool open; + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + mutex_lock(&ilits->debug_mutex); + + open = !ilits->dnp; + + ilitek_debug_node_buff_control(open); + + size = snprintf(g_user_buf, USER_STR_BUFF * sizeof(unsigned char), "dnp : %s\n", ilits->dnp ? "Enabled" : "Disabled"); + *pos = size; + + if (copy_to_user(buff, g_user_buf, size)) + ILI_ERR("Failed to copy data to user space\n"); + + mutex_unlock(&ilits->debug_mutex); + return size; +} + +static ssize_t ilitek_proc_debug_message_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + unsigned long p = *pos; + int i = 0; + int send_data_len = 0; + int ret = 0; + int data_count = 0; + int one_data_bytes = 0; + int need_read_data_len = 0; + int type = 0; + unsigned char *tmpbuf = NULL; + unsigned char tmpbufback[128] = {0}; + + if (filp->f_flags & O_NONBLOCK) { + return -EAGAIN; + } + + if (!ilits->dnp) { + ILI_ERR("Debug flag isn't enabled (%d)\n", ilits->dnp); + return -EAGAIN; + } + + mutex_lock(&ilits->debug_read_mutex); + ILI_DBG("f_count= %d, index = %d, mark = %d\n", ilits->dbf, ilits->odi, ilits->dbl[ilits->odi].mark); + if (!wait_event_interruptible_timeout(ilits->inq, ilits->dbl[ilits->odi].mark, msecs_to_jiffies(3000))) { + ILI_ERR("WARNING ! there's no data received.\n"); + mutex_unlock(&ilits->debug_read_mutex); + *pos = send_data_len; + return send_data_len; + } + mutex_lock(&ilits->debug_mutex); + + tmpbuf = vmalloc(4096); /* buf size if even */ + if (ERR_ALLOC_MEM(tmpbuf)) { + ILI_ERR("buffer vmalloc error\n"); + send_data_len += snprintf(tmpbufback + send_data_len, sizeof(tmpbufback), "buffer vmalloc error\n"); + ret = copy_to_user(buff, tmpbufback, send_data_len); /*ilits->dbl[0] */ + goto out; + } + + if (ilits->dbl[ilits->odi].mark) { + if (ilits->dbl[ilits->odi].data[0] == P5_X_DEMO_PACKET_ID) { + need_read_data_len = 43; + } else if (ilits->dbl[ilits->odi].data[0] == P5_X_I2CUART_PACKET_ID) { + type = ilits->dbl[ilits->odi].data[3] & 0x0F; + + data_count = ilits->dbl[ilits->odi].data[1] * ilits->dbl[ilits->odi].data[2]; + + if (type == 0 || type == 1 || type == 6) { + one_data_bytes = 1; + } else if (type == 2 || type == 3) { + one_data_bytes = 2; + } else if (type == 4 || type == 5) { + one_data_bytes = 4; + } + need_read_data_len = data_count * one_data_bytes + 1 + 5; + } else if (ilits->dbl[ilits->odi].data[0] == P5_X_DEBUG_PACKET_ID || ilits->dbl[ilits->odi].data[0] == P5_X_DEBUG_LITE_PACKET_ID) { + send_data_len = 0; /* ilits->dbl[0][1] - 2; */ + need_read_data_len = TR_BUF_SIZE - 8; + } + + for (i = 0; i < need_read_data_len; i++) { + send_data_len += snprintf(tmpbuf + send_data_len, sizeof(tmpbufback), "%02X", ilits->dbl[ilits->odi].data[i]); + if (send_data_len >= 4096) { + ILI_ERR("send_data_len = %d set 4096 i = %d\n", send_data_len, i); + send_data_len = 4096; + break; + } + } + + send_data_len += snprintf(tmpbuf + send_data_len, sizeof(tmpbufback), "\n\n"); + + if (p == 5 || size == 4096 || size == 2048) { + ilits->dbl[ilits->odi].mark = false; + ilits->odi = ((ilits->odi + 1) % TR_BUF_LIST_SIZE); + } + } + + /* Preparing to send debug data to user */ + if (size == 4096) + ret = copy_to_user(buff, tmpbuf, send_data_len); + else + ret = copy_to_user(buff, tmpbuf + p, send_data_len - p); + + /* ILI_ERR("send_data_len = %d\n", send_data_len); */ + if (send_data_len <= 0 || send_data_len > 4096) { + ILI_ERR("send_data_len = %d set 4096\n", send_data_len); + send_data_len = 4096; + } + + if (ret) { + ILI_ERR("copy_to_user err\n"); + ret = -EFAULT; + } else { + *pos += send_data_len; + ret = send_data_len; + ILI_DBG("Read %d bytes(s) from %ld\n", send_data_len, p); + } + +out: + mutex_unlock(&ilits->debug_mutex); + mutex_unlock(&ilits->debug_read_mutex); + ipio_vfree((void **)&tmpbuf); + return send_data_len; +} + +static ssize_t ilitek_proc_get_debug_mode_data_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + int ret; + struct file_buffer csv; + + if (*pos != 0) + return 0; + + /* initialize file */ + memset(csv.fname, 0, sizeof(csv.fname)); + snprintf(csv.fname, sizeof(csv.fname), "%s", DEBUG_DATA_FILE_PATH); + csv.flen = 0; + csv.wlen = 0; + csv.max_size = DEBUG_DATA_FILE_SIZE; + + csv.ptr = vmalloc(csv.max_size); + if (ERR_ALLOC_MEM(csv.ptr)) { + ILI_ERR("Failed to allocate CSV mem\n"); + goto out; + } + + /* save data to csv */ + ILI_INFO("Get Raw data %d frame\n", ilits->raw_count); + ILI_INFO("Get Delta data %d frame\n", ilits->delta_count); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "Get Raw data %d frame\n", ilits->raw_count); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "Get Delta data %d frame\n", ilits->delta_count); + + file_write(&csv, true); + + /* change to debug mode */ + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG, false, NULL) < 0) { + ILI_ERR("Failed to set tp data length\n"); + goto out; + } + + /* get raw data */ + csv.wlen = 0; + memset(csv.ptr, 0, csv.max_size); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "\n\n=======Raw data======="); + file_write(&csv, false); + + ret = debug_mode_get_data(&csv, P5_X_FW_RAW_DATA_MODE, ilits->raw_count); + if (ret < 0) + goto out; + + /* get delta data */ + csv.wlen = 0; + memset(csv.ptr, 0, csv.max_size); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "\n\n=======Delta data======="); + file_write(&csv, false); + + ret = debug_mode_get_data(&csv, P5_X_FW_DELTA_DATA_MODE, ilits->delta_count); + if (ret < 0) + goto out; + + /* change to demo mode */ + if (ili_set_tp_data_len(DATA_FORMAT_DEMO, false, NULL) < 0) + ILI_ERR("Failed to set tp data length\n"); + +out: + ipio_vfree((void **)&csv.ptr); + return 0; +} + +static ssize_t ilitek_proc_get_debug_mode_data_write(struct file *filp, const char *buff, size_t size, loff_t *pos) +{ + char *token = NULL, *cur = NULL; + char cmd[256] = {0}; + u8 temp[256] = {0}, count = 0; + + if ((size - 1) > sizeof(cmd)) { + ILI_ERR("ERROR! input length is larger than local buffer\n"); + return -1; + } + + if (buff != NULL) { + if (copy_from_user(cmd, buff, size - 1)) { + ILI_INFO("Failed to copy data from user space\n"); + return -1; + } + } + + ILI_INFO("size = %d, cmd = %s\n", (int)size, cmd); + token = cur = cmd; + while ((token = strsep(&cur, ",")) != NULL) { + temp[count] = ili_str2hex(token); + ILI_INFO("temp[%d] = %d\n", count, temp[count]); + count++; + } + + ilits->raw_count = ((temp[0] << 8) | temp[1]); + ilits->delta_count = ((temp[2] << 8) | temp[3]); + ilits->bg_count = ((temp[4] << 8) | temp[5]); + + ILI_INFO("Raw_count = %d, Delta_count = %d, BG_count = %d\n", ilits->raw_count, ilits->delta_count, ilits->bg_count); + return size; +} + +static ssize_t ilitek_node_mp_lcm_on_test_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + int ret = 0, len = 2; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + + if (*pos != 0) + return 0; + + ILI_INFO("Run MP test with LCM on\n"); + + mutex_lock(&ilits->touch_mutex); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + ilits->mp_ret_len = 0; + + ret = ili_mp_test_handler(g_user_buf, ON); + ILI_INFO("MP TEST %s, Error code = %d\n", (ret < 0) ? "FAIL" : "PASS", ret); + + g_user_buf[0] = 3; + g_user_buf[1] = (ret < 0) ? -ret : ret; + len += ilits->mp_ret_len; + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "MP TEST %s\n", (ret < 0) ? "FAIL" : "PASS"); + if (g_user_buf[1] == EMP_MODE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to switch MP mode, abort!"); + } else if (g_user_buf[1] == EMP_FW_PROC) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "FW still upgrading, abort!"); + } else if (g_user_buf[1] == EMP_FORMUL_NULL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "MP formula is null, abort!"); + } else if (g_user_buf[1] == EMP_INI) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Not found ini file, abort!"); + } else if (g_user_buf[1] == EMP_NOMEM) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to allocated memory, abort!"); + } else if (g_user_buf[1] == EMP_PROTOCOL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Protocol version isn't matched, abort!"); + } else if (g_user_buf[1] == EMP_TIMING_INFO) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to get timing info, abort!"); + } + + if (copy_to_user((char *)buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_node_mp_lcm_off_test_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + int ret = 0, len = 2; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + + if (*pos != 0) + return 0; + + ILI_INFO("Run MP test with LCM off\n"); + + mutex_lock(&ilits->touch_mutex); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + ilits->mp_ret_len = 0; + + ret = ili_mp_test_handler(g_user_buf, OFF); + ILI_INFO("MP TEST %s, Error code = %d\n", (ret < 0) ? "FAIL" : "PASS", ret); + + g_user_buf[0] = 3; + g_user_buf[1] = (ret < 0) ? -ret : ret; + len += ilits->mp_ret_len; + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "MP TEST %s\n", (ret < 0) ? "FAIL" : "PASS"); + if (g_user_buf[1] == EMP_MODE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to switch MP mode, abort!"); + } else if (g_user_buf[1] == EMP_FW_PROC) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "FW still upgrading, abort!"); + } else if (g_user_buf[1] == EMP_FORMUL_NULL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "MP formula is null, abort!"); + } else if (g_user_buf[1] == EMP_INI) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Not found ini file, abort!"); + } else if (g_user_buf[1] == EMP_NOMEM) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to allocated memory, abort!"); + } else if (g_user_buf[1] == EMP_PROTOCOL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Protocol version isn't matched, abort!"); + } else if (g_user_buf[1] == EMP_TIMING_INFO) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to get timing info, abort!"); + } + + if (copy_to_user((char *)buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_node_ver_info_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + u32 len = 0; + + if (*pos != 0) + return 0; + + mutex_lock(&ilits->touch_mutex); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "CHIP ID = %x\n", ilits->chip->id); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "FW version = %d.%d.%d.%d\n", + ilits->chip->fw_ver >> 24, (ilits->chip->fw_ver >> 16) & 0xFF, + (ilits->chip->fw_ver >> 8) & 0xFF, ilits->chip->fw_ver & 0xFF); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "FW MP version = %d.%d.%d.%d\n", + ilits->chip->fw_mp_ver >> 24, (ilits->chip->fw_mp_ver >> 16) & 0xFF, + (ilits->chip->fw_mp_ver >> 8) & 0xFF, ilits->chip->fw_mp_ver & 0xFF); + + if (ilits->protocol->core_ver_len == P5_X_CORE_VER_THREE_LENGTH) + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "Core version = %d.%d.%d\n", + (ilits->chip->core_ver >> 24) & 0xFF, (ilits->chip->core_ver >> 16) & 0xFF, + (ilits->chip->core_ver >> 8) & 0xFF); + else + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "Core version = %d.%d.%d.%d\n", + (ilits->chip->core_ver >> 24) & 0xFF, (ilits->chip->core_ver >> 16) & 0xFF, + (ilits->chip->core_ver >> 8) & 0xFF, ilits->chip->core_ver & 0xFF); + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "Protocol version = %d.%d.%d\n", + ilits->protocol->ver >> 16, (ilits->protocol->ver >> 8) & 0xFF, + ilits->protocol->ver & 0xFF); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "Driver version = %s\n", DRIVER_VERSION); + + if (copy_to_user((char *)buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_node_change_list_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + u32 len = 0; + + if (*pos != 0) + return 0; + + mutex_lock(&ilits->touch_mutex); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "============= Change list ==============\n"); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "[Drive version] = %s\n", DRIVER_VERSION); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "[Patch] 20200707-crc issue\n"); + // len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "[Patch] 202001-0001\n"); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "========================================\n"); + + if (copy_to_user((char *)buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_proc_fw_process_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + u32 len = 0; + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + len = snprintf(g_user_buf, USER_STR_BUFF * sizeof(unsigned char), "%02d\n", ilits->fw_update_stat); + + ILI_INFO("update status = %d\n", ilits->fw_update_stat); + + if (copy_to_user((char *)buff, &ilits->fw_update_stat, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos = len; + return len; +} + +static ssize_t ilitek_node_fw_upgrade_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + int ret = 0, len = 2; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + + if (*pos != 0) + return 0; + + ILI_INFO("Prepar to upgarde firmware\n"); + + mutex_lock(&ilits->touch_mutex); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + ilits->force_fw_update = ENABLE; + ilits->node_update = true; + + ret = ili_fw_upgrade_handler(NULL); + + ilits->node_update = false; + ilits->force_fw_update = DISABLE; + + g_user_buf[0] = 0x0; + g_user_buf[1] = (ret < 0) ? -ret : ret; + + if (g_user_buf[1] == 0) + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Upgrade firmware = PASS"); + else + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Upgrade firmware = FAIL"); + + /* Reason for fail */ + if (g_user_buf[1] == EFW_CONVERT_FILE) { + g_user_buf[0] = 0xFF; + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to convert hex/ili file, abort!"); + } else if (g_user_buf[1] == ENOMEM) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to allocate memory, abort!"); + } else if (g_user_buf[1] == EFW_ICE_MODE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to operate ice mode, abort!"); + } else if (g_user_buf[1] == EFW_CRC) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "CRC not matched, abort!"); + } else if (g_user_buf[1] == EFW_REST) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to do reset, abort!"); + } else if (g_user_buf[1] == EFW_ERASE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to erase flash, abort!"); + } else if (g_user_buf[1] == EFW_PROGRAM) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to program flash, abort!"); + } + + if (copy_to_user((u32 *) buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_proc_debug_level_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + debug_en = !debug_en; + + ILI_INFO(" %s debug level = %x\n", debug_en ? "Enable" : "Disable", debug_en); + + size = snprintf(g_user_buf, USER_STR_BUFF * sizeof(unsigned char), "debug level : %s\n", debug_en ? "Enable" : "Disable"); + + *pos += size; + + if (copy_to_user((u32 *) buff, g_user_buf, size)) + ILI_ERR("Failed to copy data to user space\n"); + + return size; +} + +int ili_get_tp_recore_ctrl(int data) +{ + int ret = 0; + + switch((int)data) { + + case ENABLE_RECORD: + ILI_INFO("recore enable"); + ret = ili_ic_func_ctrl("tp_recore", 1); + mdelay(200); + break; + case DATA_RECORD: + mdelay(50); + ILI_INFO("Get data"); + ret = ili_ic_func_ctrl("tp_recore", 2); + if (ret < 0) { + ILI_ERR("cmd fail\n"); + goto out; + } + + ret = ili_get_tp_recore_data(); + if (ret < 0) + ILI_ERR("get data fail\n"); + + ILI_INFO("recore reset"); + ret = ili_ic_func_ctrl("tp_recore", 3); + if (ret < 0) { + ILI_ERR("cmd fail\n"); + goto out; + } + break; + case DISABLE_RECORD: + ILI_INFO("recore disable"); + ret = ili_ic_func_ctrl("tp_recore", 0); + break; + + } +out: + return ret; + +} + +int ili_get_tp_recore_data(void) +{ + u8 buf[8] = {0}, record_case = 0; + s8 index; + u16 *raw = NULL, *raw_ptr = NULL, frame_len = 0; + u32 base_addr = 0x20000, addr, len, *ptr, i, fcnt; + struct record_state record_stat; + bool ice = atomic_read(&ilits->ice_stat); + + if (ilits->wrapper(NULL, 0, buf, sizeof(buf), OFF, OFF) < 0) { + ILI_ERR("Get info fail\n"); + return -1; + } + + addr = ((buf[0] << 8) | buf[1]) + base_addr; + len = ((buf[2] << 8) | buf[3]); + index = buf[4]; + fcnt = buf[5]; + record_case = buf[6]; + ipio_memcpy(&record_stat, &buf[7], 1, 1); + ILI_INFO("addr = 0x%x, len = %d, lndex = 0x%d, fram num = %d, record_case = 0x%x\n", + addr, len, index, fcnt, record_case); + ili_dump_data(buf, 8, sizeof(buf), 0, "all record bytes"); + + raw = kcalloc(len, sizeof(u8), GFP_ATOMIC); + if (ERR_ALLOC_MEM(raw)) { + ILI_ERR("Failed to allocate packet memory, %ld\n", PTR_ERR(raw)); + return -1; + } + ptr = (u32*)raw; + + if (!ice) + ili_ice_mode_ctrl(ENABLE, ON); + + buf[0] = 0x25; + buf[3] = (char)((addr & 0x00FF0000) >> 16); + buf[2] = (char)((addr & 0x0000FF00) >> 8); + buf[1] = (char)((addr & 0x000000FF)); + + if (ilits->wrapper(buf, 4, NULL, 0, ON, OFF)) { + ILI_ERR("Failed to write iram data\n"); + return -ENODEV; + } + + if (ilits->wrapper(NULL, 0, (u8 *)raw, len, ON, OFF)) { + ILI_ERR("Failed to Read iram data\n"); + return -ENODEV; + } + + frame_len = (len / (fcnt * 2)); + for (i = 0; i < fcnt; i ++) { + raw_ptr = raw + (index * frame_len); + + ili_dump_data(raw_ptr, 16, frame_len, ilits->xch_num, "recore_data"); + index--; + if(index < 0) + index = fcnt - 1; + } + + if (!ice) + ili_ice_mode_ctrl(DISABLE, ON); + + if (record_case == 2) { + ILI_INFO("tp_palm_stat = %d\n", record_stat.touch_palm_state_e); + ILI_INFO("app_an_stat = %d\n", record_stat.app_an_statu_e); + ILI_INFO("app_check_abnor = %d\n", record_stat.app_sys_check_bg_abnormal); + ILI_INFO("wrong_bg = %d\n", record_stat.g_b_wrong_bg); + } + + ipio_kfree((void **)&raw); + + return 0; +} + +void ili_gesture_fail_reason(bool enable) +{ + + u8 cmd[24] = {0}; + + /* set symbol */ + if (ili_ic_func_ctrl("knock_en", 0x8) < 0) + ILI_ERR("set symbol failed"); + + /* enable gesture fail reason */ + cmd[0] = 0x01; + cmd[1] = 0x0A; + cmd[2] = 0x10; + if (enable) + cmd[3] = 0x01; + else + cmd[3] = 0x00; + cmd[4] = 0xFF; + cmd[5] = 0xFF; + if ((ilits->wrapper(cmd, 6, NULL, 0, ON, OFF)) < 0) + ILI_ERR("enable gesture fail reason failed"); + + /* set gesture parameters */ + cmd[0] = 0x01; + cmd[1] = 0x0A; + cmd[2] = 0x12; + cmd[3] = 0x01; + memset(cmd + 4, 0xFF, 20); + if ((ilits->wrapper(cmd, 24, NULL, 0, ON, OFF)) < 0) + ILI_ERR("set gesture parameters failed"); + + /* get gesture parameters */ + cmd[0] = 0x01; + cmd[1] = 0x0A; + cmd[2] = 0x11; + cmd[3] = 0x01; + if ((ilits->wrapper(cmd, 4, NULL, 0, ON, OFF)) < 0) + ILI_ERR("get gesture parameters failed"); + +} + +int ili_tp_data_mode_ctrl(u8* cmd) +{ + int ret = 0; + switch (cmd[0]) { + case AP_MODE: + if (ilits->tp_suspend) { + if (ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEMO, false, NULL) < 0) { + ILI_ERR("Failed to set demo len from gesture mode\n"); + ret = -ENOTTY; + } + } else { + if (ili_switch_tp_mode(P5_X_FW_AP_MODE) < 0) { + ILI_ERR("Failed to switch demo mode\n"); + ret = -ENOTTY; + } + } + break; + case TEST_MODE: + if (ili_switch_tp_mode(P5_X_FW_TEST_MODE) < 0) { + ILI_ERR("Failed to switch test mode\n"); + ret = -ENOTTY; + } + break; + case DEBUG_MODE: + if (!ilits->tp_suspend && (ilits->actual_tp_mode != P5_X_FW_AP_MODE)) { + if (ili_switch_tp_mode(P5_X_FW_AP_MODE) < 0) { + ILI_ERR("Failed to switch demo mode\n"); + ret = -ENOTTY; + break; + } + } + if (ilits->tp_suspend) { + if (ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEBUG, false, NULL) < 0) { + ILI_ERR("Failed to set debug len from gesture mode\n"); + ret = -ENOTTY; + } + } else { + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG, false, NULL) < 0) { + ILI_ERR("Failed to set debug len\n"); + ret = -ENOTTY; + } + } + break; + case DEBUG_LITE_ROI: + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG_LITE_ROI, false, NULL) < 0) { + ILI_ERR("Failed to set debug lite roi from gesture mode\n"); + ret = -ENOTTY; + } + break; + case DEBUG_LITE_WINDOW: + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG_LITE_WINDOW, false, NULL) < 0) { + ILI_ERR("Failed to set debug lite window from gesture mode\n"); + ret = -ENOTTY; + } + break; + case DEBUG_LITE_AREA: + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG_LITE_AREA, false, &cmd[1]) < 0) { + ILI_ERR("Failed to set debug lite area from gesture mode\n"); + ret = -ENOTTY; + } + break; + default: + ILI_ERR("Unknown TP mode ctrl\n"); + ret = -ENOTTY; + break; + } + + return ret; +} + +static ssize_t ilitek_node_ioctl_write(struct file *filp, const char *buff, size_t size, loff_t *pos) +{ + int i, ret = 0, count = 0; + char cmd[512] = {0}; + char *token = NULL, *cur = NULL; + u8 temp[256] = {0}; + u32 *data = NULL; + + if ((size - 1) > sizeof(cmd)) { + ILI_ERR("ERROR! input length is larger than local buffer\n"); + return -1; + } + + mutex_lock(&ilits->touch_mutex); + + if (buff != NULL) { + if (copy_from_user(cmd, buff, size - 1)) { + ILI_INFO("Failed to copy data from user space\n"); + return -1; + } + } + + ILI_INFO("size = %d, cmd = %s\n", (int)size, cmd); + + token = cur = cmd; + + data = kcalloc(512, sizeof(u32), GFP_KERNEL); + + while ((token = strsep(&cur, ",")) != NULL) { + data[count] = ili_str2hex(token); + ILI_INFO("data[%d] = %x\n", count, data[count]); + count++; + } + + ILI_INFO("cmd = %s\n", cmd); + + if (strncmp(cmd, "hwreset", strlen(cmd)) == 0) { + ili_reset_ctrl(TP_HW_RST_ONLY); + } else if (strcmp(cmd, "rawdatarecore") == 0) { + if (data[1] == ENABLE_RECORD) + ili_get_tp_recore_ctrl(ENABLE_RECORD); + else if (data[1] == DATA_RECORD) + ili_get_tp_recore_ctrl(DATA_RECORD); + else if (data[1] == DISABLE_RECORD) + ili_get_tp_recore_ctrl(DISABLE_RECORD); + } else if (strcmp(cmd, "switchdemodebuginfomode") == 0) { + ili_set_tp_data_len(DATA_FORMAT_DEMO_DEBUG_INFO, false, NULL); + } else if (strcmp(cmd, "gesturedemoen") == 0) { + if (data[1] == DISABLE) + ilits->gesture_demo_ctrl = DISABLE; + else + ilits->gesture_demo_ctrl = ENABLE; + ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEMO, false, NULL); + } else if (strcmp(cmd, "gesturefailrsn") == 0) { + if (data[1] == DISABLE) + ili_gesture_fail_reason(DISABLE); + else + ili_gesture_fail_reason(ENABLE); + ILI_INFO("%s gesture fail reason\n", data[1] ? "ENABLE" : "DISABLE"); + } else if (strncmp(cmd, "icwholereset", strlen(cmd)) == 0) { + ili_ice_mode_ctrl(ENABLE, OFF); + ili_reset_ctrl(TP_IC_WHOLE_RST); + } else if (strncmp(cmd, "iccodereset", strlen(cmd)) == 0) { + ili_ice_mode_ctrl(ENABLE, OFF); + ili_reset_ctrl(TP_IC_CODE_RST); + ili_ice_mode_ctrl(DISABLE, OFF); + } else if (strncmp(cmd, "enableirq", strlen(cmd)) == 0) { + ILI_INFO("Enable IRQ\n"); + ili_irq_enable(); + } else if (strncmp(cmd, "disableirq", strlen(cmd)) == 0) { + ILI_INFO("Disable IRQ\n"); + ili_irq_disable(); + } else if (strncmp(cmd, "infofromhex", strlen(cmd)) == 0) { + ilits->info_from_hex = data[1]; + ILI_INFO("info from hex = %d\n", data[1]); + } else if (strncmp(cmd, "getinfo", strlen(cmd)) == 0) { + ili_ice_mode_ctrl(ENABLE, OFF); + ili_ic_get_info(); + ili_ice_mode_ctrl(DISABLE, OFF); + ili_ic_get_protocl_ver(); + ili_ic_get_fw_ver(); + ili_ic_get_core_ver(); + ili_ic_get_tp_info(); + ili_ic_get_panel_info(); + ILI_INFO("Driver version = %s\n", DRIVER_VERSION); + ILI_INFO("TP module = %s\n", ilits->md_name); + } else if (strncmp(cmd, "enableicemode", strlen(cmd)) == 0) { + if (data[1] == ON) + ili_ice_mode_ctrl(ENABLE, ON); + else + ili_ice_mode_ctrl(ENABLE, OFF); + } else if (strncmp(cmd, "wqctrl", strlen(cmd)) == 0) { + ilits->wq_ctrl = !ilits->wq_ctrl; + ILI_INFO("wq_ctrl flag= %d\n", ilits->wq_ctrl); + } else if (strncmp(cmd, "disableicemode", strlen(cmd)) == 0) { + ili_ice_mode_ctrl(DISABLE, OFF); + } else if (strncmp(cmd, "enablewqesd", strlen(cmd)) == 0) { + ili_wq_ctrl(WQ_ESD, ENABLE); + } else if (strncmp(cmd, "enablewqbat", strlen(cmd)) == 0) { + ili_wq_ctrl(WQ_BAT, ENABLE); + } else if (strncmp(cmd, "disablewqesd", strlen(cmd)) == 0) { + ili_wq_ctrl(WQ_ESD, DISABLE); + } else if (strncmp(cmd, "disablewqbat", strlen(cmd)) == 0) { + ili_wq_ctrl(WQ_BAT, DISABLE); + } else if (strncmp(cmd, "gesture", strlen(cmd)) == 0) { + ilits->gesture = !ilits->gesture; + ILI_INFO("gesture = %d\n", ilits->gesture); + } else if (strncmp(cmd, "esdgesture", strlen(cmd)) == 0) { + if (ili_gesture_recovery() < 0) { + ILI_ERR("Gesture recovery failed\n"); + size = -1; + } + } else if (strncmp(cmd, "esdspi", strlen(cmd)) == 0) { + ili_spi_recovery(); + } else if (strncmp(cmd, "sleepin", strlen(cmd)) == 0) { + ili_ic_func_ctrl("sleep", SLEEP_IN); + } else if (strncmp(cmd, "deepsleepin", strlen(cmd)) == 0) { + ili_ic_func_ctrl("sleep", DEEP_SLEEP_IN); + } else if (strncmp(cmd, "iceflag", strlen(cmd)) == 0) { + if (data[1] == ENABLE) + atomic_set(&ilits->ice_stat, ENABLE); + else + atomic_set(&ilits->ice_stat, DISABLE); + ILI_INFO("ice mode flag = %d\n", atomic_read(&ilits->ice_stat)); + } else if (strncmp(cmd, "gesturenormal", strlen(cmd)) == 0) { + ilits->gesture_mode = DATA_FORMAT_GESTURE_NORMAL; + ILI_INFO("gesture mode = %d\n", ilits->gesture_mode); + } else if (strncmp(cmd, "gestureinfo", strlen(cmd)) == 0) { + ilits->gesture_mode = DATA_FORMAT_GESTURE_INFO; + ILI_INFO("gesture mode = %d\n", ilits->gesture_mode); + } else if (strncmp(cmd, "netlink", strlen(cmd)) == 0) { + ilits->netlink = !ilits->netlink; + ILI_INFO("netlink flag= %d\n", ilits->netlink); + } else if (strncmp(cmd, "switchtestmode", strlen(cmd)) == 0) { + ili_switch_tp_mode(P5_X_FW_TEST_MODE); + } else if (strncmp(cmd, "switchdebugmode", strlen(cmd)) == 0) { + ili_set_tp_data_len(DATA_FORMAT_DEBUG, false, NULL); + } else if (strncmp(cmd, "switchdemomode", strlen(cmd)) == 0) { + ili_set_tp_data_len(DATA_FORMAT_DEMO, false, NULL); + } else if (strncmp(cmd, "switchgesturedebugmode", strlen(cmd)) == 0) { + ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEBUG, false, NULL); + } else if (strncmp(cmd, "dbgflag", strlen(cmd)) == 0) { + ilits->dnp = !ilits->dnp; + ILI_INFO("debug flag message = %d\n", ilits->dnp); + } else if (strncmp(cmd, "spiclk", strlen(cmd)) == 0) { + ILI_INFO("spi clk num = %d\n", data[1]); + ili_core_spi_setup(data[1]); + } else if (strncmp(cmd, "ss", strlen(cmd)) == 0) { + ILI_INFO("sense_stop = %d\n", data[1]); + ilits->ss_ctrl = data[1]; + } else if (strncmp(cmd, "iow", strlen(cmd)) == 0) { + int w_len = 0; + w_len = data[1]; + ILI_INFO("w_len = %d\n", w_len); + for (i = 0; i < w_len; i++) { + temp[i] = data[2 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + + ilits->wrapper(temp, w_len, NULL, 0, ON, OFF); + } else if (strncmp(cmd, "ior", strlen(cmd)) == 0) { + int r_len = 0; + r_len = data[1]; + ILI_INFO("r_len = %d\n", r_len); + ilits->wrapper(NULL, 0, temp, r_len, ON, OFF); + for (i = 0; i < r_len; i++) + ILI_INFO("read[%d] = %x\n", i, temp[i]); + } else if (strncmp(cmd, "iowr", strlen(cmd)) == 0) { + int delay = 0; + int w_len = 0, r_len = 0; + w_len = data[1]; + r_len = data[2]; + delay = data[3]; + ILI_INFO("w_len = %d, r_len = %d, delay = %d\n", w_len, r_len, delay); + + for (i = 0; i < w_len; i++) { + temp[i] = data[4 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + ret = ilits->wrapper(temp, w_len, NULL, 0, ON, OFF); + memset(temp, 0, sizeof(temp)); + mdelay(delay); + ilits->wrapper(NULL, 0, temp, r_len, ON, OFF); + for (i = 0; i < r_len; i++) + ILI_INFO("read[%d] = %x\n", i, temp[i]); + } else if (strcmp(cmd, "iowrint") == 0) { + int delay = 0; + int w_len = 0, r_len = 0; + w_len = data[1]; + r_len = data[2]; + delay = data[3]; + ILI_INFO("w_len = %d, r_len = %d, delay = %d\n", w_len, r_len, delay); + for (i = 0; i < w_len; i++) { + temp[i] = data[4 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + ret = ilits->wrapper(temp, w_len, temp, r_len, ON, ON); + for (i = 0; i < r_len; i++) + ILI_INFO("read[%d] = %x\n", i, temp[i]); + } else if (strncmp(cmd, "getddiregdata", strlen(cmd)) == 0) { + u8 ddi_data = 0; + ILI_INFO("Get ddi reg one page: page = %x, reg = %x\n", data[1], data[2]); + ili_ic_get_ddi_reg_onepage(data[1], data[2], &ddi_data); + ILI_INFO("ddi_data = %x\n", ddi_data); + } else if (strncmp(cmd, "setddiregdata", strlen(cmd)) == 0) { + ILI_INFO("Set ddi reg one page: page = %x, reg = %x, data = %x\n", data[1], data[2], data[3]); + ili_ic_set_ddi_reg_onepage(data[1], data[2], data[3]); + } else if (strncmp(cmd, "dumpflashdata", strlen(cmd)) == 0) { + ilits->fw_update_stat = FW_STAT_INIT; + ILI_INFO("Start = 0x%x, End = 0x%x, Dump Hex path = %s\n", data[1], data[2], DUMP_FLASH_PATH); + ret = ili_fw_dump_flash_data(data[1], data[2], false); + ilits->fw_update_stat = (ret < 0) ? FW_UPDATE_FAIL : FW_UPDATE_PASS; + ILI_INFO("ilits->fw_update_stat = %d\n", ilits->fw_update_stat); + } else if (strncmp(cmd, "dumpiramdata", strlen(cmd)) == 0) { + ILI_INFO("Start = 0x%x, End = 0x%x, Dump IRAM path = %s\n", data[1], data[2], DUMP_IRAM_PATH); + ilits->fw_update_stat = FW_STAT_INIT; + ret = ili_fw_dump_iram_data(data[1], data[2], true); + ilits->fw_update_stat = (ret < 0) ? FW_UPDATE_FAIL : FW_UPDATE_PASS; + ILI_INFO("ilits->fw_update_stat = %d\n", ilits->fw_update_stat); + } else if (strncmp(cmd, "edge_palm_ctrl", strlen(cmd)) == 0) { + ili_ic_func_ctrl("edge_palm", data[1]); + } else if (strncmp(cmd, "uart_mode_ctrl", strlen(cmd)) == 0) { + ili_fw_uart_ctrl(data[1]); + } else if (strncmp(cmd, "flashesdgesture", strlen(cmd)) == 0) { + ili_touch_esd_gesture_flash(); + } else if (strncmp(cmd, "intact", strlen(cmd)) == 0) { + ILI_INFO("Set INT trigger = %x\n", data[1]); + ili_ic_int_trigger_ctrl(data[1]); + } else if (strncmp(cmd, "spiw", strlen(cmd)) == 0) { + int wlen; + wlen = data[1]; + temp[0] = 0x82; + for (i = 0; i < wlen; i++) { + temp[i] = data[2 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + ilits->spi_write_then_read(ilits->spi, temp, wlen, NULL, 0); + } else if (strncmp(cmd, "spir", strlen(cmd)) == 0) { + int rlen; + u8 *rbuf = NULL; + rlen = data[1]; + rbuf = kzalloc(rlen, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(rbuf)) { + ILI_ERR("Failed to allocate dma_rxbuf, %ld\n", PTR_ERR(rbuf)); + kfree(rbuf); + goto out; + } + temp[0] = 0x83; + ilits->spi_write_then_read(ilits->spi, temp, 1, rbuf, rlen); + for (i = 0; i < rlen; i++) + ILI_INFO("read[%d] = %x\n", i, rbuf[i]); + kfree(rbuf); + } else if (strncmp(cmd, "spirw", strlen(cmd)) == 0) { + int wlen, rlen; + u8 *rbuf = NULL; + wlen = data[1]; + rlen = data[2]; + for (i = 0; i < wlen; i++) { + temp[i] = data[3 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + if (rlen != 0) { + rbuf = kzalloc(rlen, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(rbuf)) { + ILI_ERR("Failed to allocate dma_rxbuf, %ld\n", PTR_ERR(rbuf)); + kfree(rbuf); + goto out; + } + } + ilits->spi_write_then_read(ilits->spi, temp, wlen, rbuf, rlen); + if (rlen != 0) { + for (i = 0; i < rlen; i++) + ILI_INFO("read[%d] = %x\n", i, rbuf[i]); + } + kfree(rbuf); + } else if (strncmp(cmd, "ges_sym", strlen(cmd)) == 0) { + struct gesture_symbol *ptr_sym = &ilits->ges_sym; + u8 *ptr; + ptr = (u8*) ptr_sym; + ptr[0] = data[1]; + ptr[1] = data[2]; + ptr[2] = data[3]; + ili_dump_data(ptr, 8, 3, 0, "Gesture symbol"); + } else if (strncmp(cmd, "mode", strlen(cmd)) == 0) { + temp[0] = data[1]; + temp[1] = data[2]; + temp[2] = data[3]; + temp[3] = data[4]; + temp[4] = data[5]; + ret = ili_tp_data_mode_ctrl(temp); + } else if (strncmp(cmd, "switchmpretry", strlen(cmd)) == 0) { + ilits->mp_retry = !ilits->mp_retry; + ILI_INFO("switchmpretry = %d\n", ilits->mp_retry); + } else { + ILI_ERR("Unknown command\n"); + size = -1; + } +out: + ipio_kfree((void **)&data); + mutex_unlock(&ilits->touch_mutex); + return size; +} + +#ifdef CONFIG_COMPAT +static long ilitek_node_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + long ret = 0; + + if (!filp->f_op || !filp->f_op->unlocked_ioctl) { + ILI_ERR("There's no unlocked_ioctl defined in file\n"); + return -ENOTTY; + } + + ILI_DBG("cmd = %d\n", _IOC_NR(cmd)); + + switch (cmd) { + case ILITEK_COMPAT_IOCTL_I2C_WRITE_DATA: + ILI_DBG("compat_ioctl: convert i2c/spi write\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_I2C_WRITE_DATA, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_I2C_READ_DATA: + ILI_DBG("compat_ioctl: convert i2c/spi read\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_I2C_READ_DATA, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_I2C_SET_WRITE_LENGTH: + ILI_DBG("compat_ioctl: convert set write length\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_I2C_SET_WRITE_LENGTH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_I2C_SET_READ_LENGTH: + ILI_DBG("compat_ioctl: convert set read length\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_I2C_SET_READ_LENGTH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_HW_RESET: + ILI_DBG("compat_ioctl: convert hw reset\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_HW_RESET, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_POWER_SWITCH: + ILI_DBG("compat_ioctl: convert power switch\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_POWER_SWITCH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_REPORT_SWITCH: + ILI_DBG("compat_ioctl: convert report switch\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_REPORT_SWITCH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_IRQ_SWITCH: + ILI_DBG("compat_ioctl: convert irq switch\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_IRQ_SWITCH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_DEBUG_LEVEL: + ILI_DBG("compat_ioctl: convert debug level\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_DEBUG_LEVEL, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_FUNC_MODE: + ILI_DBG("compat_ioctl: convert format mode\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_FUNC_MODE, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_FW_VER: + ILI_DBG("compat_ioctl: convert set read length\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_FW_VER, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_PL_VER: + ILI_DBG("compat_ioctl: convert fw version\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_PL_VER, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_CORE_VER: + ILI_DBG("compat_ioctl: convert core version\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_CORE_VER, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_DRV_VER: + ILI_DBG("compat_ioctl: convert driver version\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_DRV_VER, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_CHIP_ID: + ILI_DBG("compat_ioctl: convert chip id\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_CHIP_ID, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_NETLINK_CTRL: + ILI_DBG("compat_ioctl: convert netlink ctrl\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_NETLINK_CTRL, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_NETLINK_STATUS: + ILI_DBG("compat_ioctl: convert netlink status\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_NETLINK_STATUS, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_MODE_CTRL: + ILI_DBG("compat_ioctl: convert tp mode ctrl\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_MODE_CTRL, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_MODE_STATUS: + ILI_DBG("compat_ioctl: convert tp mode status\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_MODE_STATUS, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_ICE_MODE_SWITCH: + ILI_DBG("compat_ioctl: convert tp mode switch\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_ICE_MODE_SWITCH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_INTERFACE_TYPE: + ILI_DBG("compat_ioctl: convert interface type\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_INTERFACE_TYPE, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_DUMP_FLASH: + ILI_DBG("compat_ioctl: convert dump flash\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_DUMP_FLASH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_FW_UART_CTRL: + ILI_DBG("compat_ioctl: convert fw uart\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_FW_UART_CTRL, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_PANEL_INFO: + ILI_DBG("compat_ioctl: convert resolution\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_COMPAT_IOCTL_TP_PANEL_INFO, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_INFO: + ILI_DBG("compat_ioctl: convert tp info\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_COMPAT_IOCTL_TP_INFO, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_WRAPPER_RW: + ILI_DBG("compat_ioctl: convert wrapper\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_COMPAT_IOCTL_WRAPPER_RW, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_DDI_WRITE: + ILI_DBG("compat_ioctl: convert ddi write\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_COMPAT_IOCTL_DDI_WRITE, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_DDI_READ: + ILI_DBG("compat_ioctl: convert ddi read\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_COMPAT_IOCTL_DDI_READ, (unsigned long)compat_ptr(arg)); + return ret; + default: + ILI_ERR("no ioctl cmd, return ilitek_node_ioctl\n"); + return -ENOIOCTLCMD; + } +} +#endif + +static long ilitek_node_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = 0, length = 0; + u8 *szBuf = NULL, if_to_user = 0; + static u16 i2c_rw_length; + u32 id_to_user[64] = {0}; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + u8 *wrap_rbuf = NULL, wrap_int = 0; + u16 wrap_wlen = 0, wrap_rlen = 0; + bool spi_irq, i2c_irq; + + if (_IOC_TYPE(cmd) != ILITEK_IOCTL_MAGIC) { + ILI_ERR("The Magic number doesn't match\n"); + return -ENOTTY; + } + + if (_IOC_NR(cmd) > ILITEK_IOCTL_MAXNR) { + ILI_ERR("The number of ioctl doesn't match\n"); + return -ENOTTY; + } + + ILI_DBG("cmd = %d\n", _IOC_NR(cmd)); + + mutex_lock(&ilits->touch_mutex); + + szBuf = kcalloc(IOCTL_I2C_BUFF, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(szBuf)) { + ILI_ERR("Failed to allocate mem\n"); + ret = -ENOMEM; + goto out; + } + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + switch (cmd) { + case ILITEK_IOCTL_I2C_WRITE_DATA: + ILI_DBG("ioctl: write len = %d\n", i2c_rw_length); + + if (i2c_rw_length > IOCTL_I2C_BUFF) { + ILI_ERR("ERROR! write len is largn than ioctl buf (%d, %ld)\n", + i2c_rw_length, IOCTL_I2C_BUFF); + ret = -ENOTTY; + break; + } + + if (copy_from_user(szBuf, (u8 *) arg, i2c_rw_length)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + ret = ilits->wrapper(szBuf, i2c_rw_length, NULL, 0, OFF, OFF); + if (ret < 0) + ILI_ERR("Failed to write data\n"); + break; + case ILITEK_IOCTL_I2C_READ_DATA: + ILI_DBG("ioctl: read len = %d\n", i2c_rw_length); + + if (i2c_rw_length > IOCTL_I2C_BUFF) { + ILI_ERR("ERROR! read len is largn than ioctl buf (%d, %ld)\n", + i2c_rw_length, IOCTL_I2C_BUFF); + ret = -ENOTTY; + break; + } + + ret = ilits->wrapper(NULL, 0, szBuf, i2c_rw_length, OFF, OFF); + if (ret < 0) { + ILI_ERR("Failed to read data\n"); + break; + } + + if (copy_to_user((u8 *) arg, szBuf, i2c_rw_length)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_I2C_SET_WRITE_LENGTH: + case ILITEK_IOCTL_I2C_SET_READ_LENGTH: + i2c_rw_length = arg; + break; + case ILITEK_IOCTL_TP_HW_RESET: + ILI_DBG("ioctl: hw reset\n"); + ili_reset_ctrl(ilits->reset); + break; + case ILITEK_IOCTL_TP_POWER_SWITCH: + ILI_INFO("Not implemented yet\n"); + break; + case ILITEK_IOCTL_TP_REPORT_SWITCH: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + ILI_DBG("ioctl: report switch = %d\n", szBuf[0]); + if (szBuf[0]) { + ilits->report = ENABLE; + ILI_DBG("report is enabled\n"); + } else { + ilits->report = DISABLE; + ILI_DBG("report is disabled\n"); + } + break; + case ILITEK_IOCTL_TP_IRQ_SWITCH: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: irq switch = %d\n", szBuf[0]); + if (szBuf[0]) + ili_irq_enable(); + else + ili_irq_disable(); + break; + case ILITEK_IOCTL_TP_DEBUG_LEVEL: + if (copy_from_user(szBuf, (u8 *) arg, sizeof(u32))) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + debug_en = !debug_en; + ILI_DBG("debug_en = %d", debug_en); + break; + case ILITEK_IOCTL_TP_FUNC_MODE: + if (copy_from_user(szBuf, (u8 *) arg, 3)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + ILI_DBG("ioctl: set func mode = %x,%x,%x\n", szBuf[0], szBuf[1], szBuf[2]); + ret = ilits->wrapper(szBuf, 3, NULL, 0, ON, OFF); + break; + case ILITEK_IOCTL_TP_FW_VER: + ILI_DBG("ioctl: get fw and mp version\n"); + szBuf[7] = ilits->chip->fw_mp_ver & 0xFF; + szBuf[6] = (ilits->chip->fw_mp_ver >> 8) & 0xFF; + szBuf[5] = (ilits->chip->fw_mp_ver >> 16) & 0xFF; + szBuf[4] = ilits->chip->fw_mp_ver >> 24; + szBuf[3] = ilits->chip->fw_ver & 0xFF; + szBuf[2] = (ilits->chip->fw_ver >> 8) & 0xFF; + szBuf[1] = (ilits->chip->fw_ver >> 16) & 0xFF; + szBuf[0] = ilits->chip->fw_ver >> 24; + ILI_DBG("Firmware version = %d.%d.%d.%d\n", szBuf[0], szBuf[1], szBuf[2], szBuf[3]); + ILI_DBG("Firmware MP version = %d.%d.%d.%d\n", szBuf[4], szBuf[5], szBuf[6], szBuf[7]); + + if (copy_to_user((u8 *) arg, szBuf, 8)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_PL_VER: + ILI_DBG("ioctl: get protocl version\n"); + szBuf[2] = ilits->protocol->ver & 0xFF; + szBuf[1] = (ilits->protocol->ver >> 8) & 0xFF; + szBuf[0] = ilits->protocol->ver >> 16; + ILI_DBG("Protocol version = %d.%d.%d\n", szBuf[0], szBuf[1], szBuf[2]); + + if (copy_to_user((u8 *) arg, szBuf, 3)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_CORE_VER: + ILI_DBG("ioctl: get core version\n"); + if (ilits->protocol->core_ver_len == P5_X_CORE_VER_THREE_LENGTH) + szBuf[3] = 0xFF; + else + szBuf[3] = ilits->chip->core_ver & 0xFF; + szBuf[2] = (ilits->chip->core_ver >> 8) & 0xFF; + szBuf[1] = (ilits->chip->core_ver >> 16) & 0xFF; + szBuf[0] = ilits->chip->core_ver >> 24; + ILI_DBG("Core version = %d.%d.%d.%d, length = %d\n", + szBuf[0], szBuf[1], szBuf[2], szBuf[3], ilits->protocol->core_ver_len); + + if (copy_to_user((u8 *) arg, szBuf, 4)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_DRV_VER: + ILI_DBG("ioctl: get driver version\n"); + length = snprintf(szBuf, USER_STR_BUFF * sizeof(unsigned char), "%s", DRIVER_VERSION); + + if (copy_to_user((u8 *) arg, szBuf, length)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_CHIP_ID: + ILI_DBG("ioctl: get chip id\n"); + id_to_user[0] = ilits->chip->pid; + id_to_user[1] = ilits->chip->otp_id; + id_to_user[2] = ilits->chip->ana_id; + + if (copy_to_user((u32 *) arg, id_to_user, sizeof(u32) * 3)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_NETLINK_CTRL: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: netlink ctrl = %d\n", szBuf[0]); + if (szBuf[0]) { + ilits->netlink = ENABLE; + ILI_DBG("ioctl: Netlink is enabled\n"); + } else { + ilits->netlink = DISABLE; + ILI_DBG("ioctl: Netlink is disabled\n"); + } + break; + case ILITEK_IOCTL_TP_NETLINK_STATUS: + ILI_DBG("ioctl: get netlink stat = %d\n", ilits->netlink); + if (copy_to_user((int *)arg, &ilits->netlink, sizeof(int))) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_MODE_CTRL: + if (copy_from_user(szBuf, (u8 *) arg, 10)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: switch fw format = %d\n", szBuf[0]); + ret = ili_tp_data_mode_ctrl(szBuf); + + break; + case ILITEK_IOCTL_TP_MODE_STATUS: + ILI_DBG("ioctl: current firmware mode = %d", ilits->actual_tp_mode); + if (copy_to_user((int *)arg, &ilits->actual_tp_mode, sizeof(int))) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + /* It works for host downloado only */ + case ILITEK_IOCTL_ICE_MODE_SWITCH: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + ILI_DBG("ioctl: switch ice mode = %d, mcu on = %d\n", szBuf[0], szBuf[1]); + + if (ili_ice_mode_ctrl(szBuf[0] ? ENABLE : DISABLE, + szBuf[1] ? ENABLE : DISABLE) < 0) + ILI_ERR("ioctl: set ice mode failed\n"); + + break; + case ILITEK_IOCTL_TP_INTERFACE_TYPE: + if_to_user = ilits->hwif->bus_type; + if (copy_to_user((u8 *) arg, &if_to_user, sizeof(if_to_user))) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_DUMP_FLASH: + ILI_DBG("ioctl: dump flash data\n"); + ret = ili_fw_dump_flash_data(0, 0, true); + if (ret < 0) { + ILI_ERR("ioctl: Failed to dump flash data\n"); + } + break; + case ILITEK_IOCTL_TP_FW_UART_CTRL: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: fw UART = %d\n", szBuf[0]); + + ili_fw_uart_ctrl(szBuf[0]); + + if_to_user = szBuf[0]; + + if (copy_to_user((u8 *) arg, &if_to_user, sizeof(if_to_user))) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_PANEL_INFO: + ILI_DBG("ioctl: get panel resolution\n"); + id_to_user[0] = ilits->panel_wid; + id_to_user[1] = ilits->panel_hei; + + if (copy_to_user((u32 *) arg, id_to_user, sizeof(u32) * 2)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_INFO: + ILI_DBG("ioctl: get tp info\n"); + id_to_user[0] = ilits->min_x; + id_to_user[1] = ilits->min_y; + id_to_user[2] = ilits->max_x; + id_to_user[3] = ilits->max_y; + id_to_user[4] = ilits->xch_num; + id_to_user[5] = ilits->ych_num; + id_to_user[6] = ilits->stx; + id_to_user[7] = ilits->srx; + + if (copy_to_user((u32 *) arg, id_to_user, sizeof(u32) * 8)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_WRAPPER_RW: + ILI_DBG("ioctl: wrapper rw\n"); + + if (i2c_rw_length > IOCTL_I2C_BUFF || i2c_rw_length == 0) { + ILI_ERR("ERROR! i2c_rw_length is is invalid\n"); + ret = -ENOTTY; + break; + } + + if (copy_from_user(szBuf, (u8 *) arg, i2c_rw_length)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + wrap_int = szBuf[0]; + wrap_rlen = (szBuf[1] << 8) | szBuf[2]; + wrap_wlen = (szBuf[3] << 8) | szBuf[4]; + + ILI_DBG("wrap_int = %d\n", wrap_int); + ILI_DBG("wrap_rlen = %d\n", wrap_rlen); + ILI_DBG("wrap_wlen = %d\n", wrap_wlen); + + if (wrap_wlen > IOCTL_I2C_BUFF || wrap_rlen > IOCTL_I2C_BUFF) { + ILI_ERR("ERROR! R/W len is largn than ioctl buf\n"); + ret = -ENOTTY; + break; + } + + if (wrap_rlen > 0) { + wrap_rbuf = kcalloc(IOCTL_I2C_BUFF, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(wrap_rbuf)) { + ILI_ERR("Failed to allocate mem\n"); + ret = -ENOMEM; + break; + } + } + + if (wrap_int == 1) { + i2c_irq = ON; + spi_irq = ON; + } else if (wrap_int == 2) { + i2c_irq = OFF; + spi_irq = OFF; + } else { + i2c_irq= OFF; + spi_irq = (wrap_rlen > 0 ? ON : OFF); + } + + ILI_DBG("i2c_irq = %d, spi_irq = %d\n", i2c_irq, spi_irq); + + ilits->wrapper(szBuf + 5, wrap_wlen, wrap_rbuf, wrap_rlen, spi_irq, i2c_irq); + + ili_dump_data(szBuf + 5, 8, wrap_wlen, 16, "wrap_wbuf:"); + ili_dump_data(wrap_rbuf, 8, wrap_rlen, 16, "wrap_rbuf:"); + if (copy_to_user((u8 *) arg, wrap_rbuf, wrap_rlen)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_DDI_WRITE: + if (copy_from_user(szBuf, (u8 *) arg, 3)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: page = %x, reg = %x, data = %x\n", szBuf[0], szBuf[1], szBuf[2]); + ili_ic_set_ddi_reg_onepage(szBuf[0], szBuf[1], szBuf[2]); + break; + case ILITEK_IOCTL_DDI_READ: + if (copy_from_user(szBuf, (u8 *) arg, 2)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: page = %x, reg = %x\n", szBuf[0], szBuf[1]); + ili_ic_get_ddi_reg_onepage(szBuf[0], szBuf[1], &szBuf[2]); + ILI_DBG("ioctl: data = %x\n", szBuf[2]); + if (copy_to_user((u8 *) arg, szBuf + 2, 1)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + default: + ret = -ENOTTY; + break; + } + + ipio_kfree((void **)&szBuf); + ipio_kfree((void **)&wrap_rbuf); + +out: + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + mutex_unlock(&ilits->touch_mutex); + return ret; +} + +static struct proc_dir_entry *proc_dir_ilitek; + +typedef struct { + char *name; + struct proc_dir_entry *node; + struct file_operations *fops; + bool isCreated; +} proc_node; + +static struct file_operations proc_mp_lcm_on_test_fops = { + .read = ilitek_node_mp_lcm_on_test_read, +}; + +static struct file_operations proc_mp_lcm_off_test_fops = { + .read = ilitek_node_mp_lcm_off_test_read, +}; + +static struct file_operations proc_ver_info_fops = { + .read = ilitek_node_ver_info_read, +}; + +static struct file_operations proc_change_list_fops = { + .read = ilitek_node_change_list_read, +}; + +static struct file_operations proc_debug_message_fops = { + .read = ilitek_proc_debug_message_read, +}; + +static struct file_operations proc_debug_message_switch_fops = { + .read = ilitek_proc_debug_switch_read, +}; + +static struct file_operations proc_ioctl_fops = { + .unlocked_ioctl = ilitek_node_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ilitek_node_compat_ioctl, +#endif + .write = ilitek_node_ioctl_write, +}; + +static struct file_operations proc_fw_upgrade_fops = { + .read = ilitek_node_fw_upgrade_read, +}; + +static struct file_operations proc_fw_process_fops = { + .read = ilitek_proc_fw_process_read, +}; + +static struct file_operations proc_get_delta_data_fops = { + .read = ilitek_proc_get_delta_data_read, +}; + +static struct file_operations proc_get_raw_data_fops = { + .read = ilitek_proc_fw_get_raw_data_read, +}; + +static struct file_operations proc_rw_tp_reg_fops = { + .read = ilitek_proc_rw_tp_reg_read, + .write = ilitek_proc_rw_tp_reg_write, +}; + +static struct file_operations proc_fw_pc_counter_fops = { + .read = ilitek_proc_fw_pc_counter_read, +}; + +static struct file_operations proc_get_debug_mode_data_fops = { + .read = ilitek_proc_get_debug_mode_data_read, + .write = ilitek_proc_get_debug_mode_data_write, +}; + +static struct file_operations proc_debug_level_fops = { + .read = ilitek_proc_debug_level_read, +}; + +proc_node iliproc[] = { + {"ioctl", NULL, &proc_ioctl_fops, false}, + {"fw_process", NULL, &proc_fw_process_fops, false}, + {"fw_upgrade", NULL, &proc_fw_upgrade_fops, false}, + {"debug_level", NULL, &proc_debug_level_fops, false}, + {"mp_lcm_on_test", NULL, &proc_mp_lcm_on_test_fops, false}, + {"mp_lcm_off_test", NULL, &proc_mp_lcm_off_test_fops, false}, + {"debug_message", NULL, &proc_debug_message_fops, false}, + {"debug_message_switch", NULL, &proc_debug_message_switch_fops, false}, + {"fw_pc_counter", NULL, &proc_fw_pc_counter_fops, false}, + {"show_delta_data", NULL, &proc_get_delta_data_fops, false}, + {"show_raw_data", NULL, &proc_get_raw_data_fops, false}, + {"get_debug_mode_data", NULL, &proc_get_debug_mode_data_fops, false}, + {"rw_tp_reg", NULL, &proc_rw_tp_reg_fops, false}, + {"ver_info", NULL, &proc_ver_info_fops, false}, + {"change_list", NULL, &proc_change_list_fops, false}, +}; + +#define NETLINK_USER 21 +static struct sock *netlink_skb; +static struct nlmsghdr *netlink_head; +static struct sk_buff *skb_out; +static int netlink_pid; + +void ili_netlink_reply_msg(void *raw, int size) +{ + int ret; + int msg_size = size; + u8 *data = (u8 *) raw; + + ILI_INFO("The size of data being sent to user = %d\n", msg_size); + ILI_INFO("pid = %d\n", netlink_pid); + ILI_INFO("Netlink is enable = %d\n", ilits->netlink); + + if (ilits->netlink) { + skb_out = nlmsg_new(msg_size, 0); + + if (!skb_out) { + ILI_ERR("Failed to allocate new skb\n"); + return; + } + + netlink_head = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0); + NETLINK_CB(skb_out).dst_group = 0; /* not in mcast group */ + + /* strncpy(NLMSG_DATA(netlink_head), data, msg_size); */ + ipio_memcpy(nlmsg_data(netlink_head), data, msg_size, size); + + ret = nlmsg_unicast(netlink_skb, skb_out, netlink_pid); + if (ret < 0) + ILI_ERR("Failed to send data back to user\n"); + } +} + +static void netlink_recv_msg(struct sk_buff *skb) +{ + netlink_pid = 0; + + ILI_INFO("Netlink = %d\n", ilits->netlink); + + netlink_head = (struct nlmsghdr *)skb->data; + + ILI_INFO("Received a request from client: %s, %d\n", + (char *)NLMSG_DATA(netlink_head), (int)strlen((char *)NLMSG_DATA(netlink_head))); + + /* pid of sending process */ + netlink_pid = netlink_head->nlmsg_pid; + + ILI_INFO("the pid of sending process = %d\n", netlink_pid); + + /* TODO: may do something if there's not receiving msg from user. */ + if (netlink_pid != 0) { + ILI_ERR("The channel of Netlink has been established successfully !\n"); + ilits->netlink = ENABLE; + } else { + ILI_ERR("Failed to establish the channel between kernel and user space\n"); + ilits->netlink = DISABLE; + } +} + +static int netlink_init(void) +{ + int ret = 0; + +#if KERNEL_VERSION(3, 4, 0) > LINUX_VERSION_CODE + netlink_skb = netlink_kernel_create(&init_net, NETLINK_USER, netlink_recv_msg, NULL, THIS_MODULE); +#else + struct netlink_kernel_cfg cfg = { + .input = netlink_recv_msg, + }; + + netlink_skb = netlink_kernel_create(&init_net, NETLINK_USER, &cfg); +#endif + + ILI_INFO("Initialise Netlink and create its socket\n"); + + if (!netlink_skb) { + ILI_ERR("Failed to create nelink socket\n"); + ret = -EFAULT; + } + return ret; +} + +static ssize_t path_show(struct device *pDevice, struct device_attribute *pAttr, char *pBuf) +{ + ssize_t blen; + const char *path; + + path = kobject_get_path(&ilits->spi->dev.kobj, GFP_KERNEL); + blen = scnprintf(pBuf, PAGE_SIZE, "%s\n", path ? path : "na"); + kfree(path); + + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ILI_INFO("*** %s() vendor = %s ***\n", __func__, "ilitek"); + return scnprintf(buf, PAGE_SIZE, "ilitek"); +} + +static ssize_t ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s%s\n%s%04x\n%s%04x\n", + "Product ID: ", ILI_SPI_NAME, + "Build ID: ", ilits->chip->fw_ver, + "Config ID: ", ilits->chip->pid); +} + + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_RO(ic_ver), + __ATTR_NULL +}; + +int ilitek_sys_init(void) +{ + int i; + s32 ret = 0; + dev_t devno; + struct device_attribute *attrs = touchscreen_attributes; + + ret = alloc_chrdev_region(&devno, 0, 1, ILI_SPI_NAME); + if (ret) { + ILI_ERR ("can't allocate chrdev\n"); + return ret; + } else { + + /* set sysfs for firmware */ + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + ret = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + ILI_ERR("Failed to create touchscreen class!\n"); + return ret; + } + + touchscreen_class_dev = device_create(touchscreen_class, NULL, devno, NULL, ILI_SPI_NAME); + pr_info(" touchscreen_class_dev = %p \n", touchscreen_class_dev); + if (IS_ERR(touchscreen_class_dev)) { + ret = PTR_ERR(touchscreen_class_dev); + touchscreen_class_dev = NULL; + ILI_ERR("Failed to create device(touchscreen_class_dev)!\n"); + return ret; + } + ILI_INFO("Succeed to create device(touchscreen_class_dev)!\n"); + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + ret = device_create_file(touchscreen_class_dev, &attrs[i]); + if (ret < 0) + goto device_destroy; + } + } + + return ret; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(touchscreen_class_dev, &attrs[i]); + + touchscreen_class_dev = NULL; + class_unregister(touchscreen_class); + ILI_ERR("error creating touchscreen class\n"); + + return -ENODEV; +} + +void ili_node_init(void) +{ + int i = 0, ret = 0; + + proc_dir_ilitek = proc_mkdir("ilitek", NULL); + + for (; i < ARRAY_SIZE(iliproc); i++) { + iliproc[i].node = proc_create(iliproc[i].name, 0644, proc_dir_ilitek, iliproc[i].fops); + + if (iliproc[i].node == NULL) { + iliproc[i].isCreated = false; + ILI_ERR("Failed to create %s under /proc\n", iliproc[i].name); + ret = -ENODEV; + } else { + iliproc[i].isCreated = true; + ILI_INFO("Succeed to create %s under /proc\n", iliproc[i].name); + } + } + netlink_init(); + ilitek_sys_init(); +} diff --git a/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_qcom.c b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_qcom.c new file mode 100644 index 000000000000..0983fdc52619 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_qcom.c @@ -0,0 +1,1071 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek.h" + +#ifdef ILI_CONFIG_PANEL_NOTIFICATIONS +#define register_panel_notifier panel_register_notifier +#define unregister_panel_notifier panel_unregister_notifier +enum touch_state { + TOUCH_DEEP_SLEEP_STATE = 0, + TOUCH_LOW_POWER_STATE, +}; +#else +#define register_panel_notifier(...) rc +#define unregister_panel_notifier(...) rc +#endif + +#ifdef ILI_SENSOR_EN +static struct sensors_classdev __maybe_unused sensors_touch_cdev = { + + .name = "dt-gesture", + .vendor = "ilitek", + .version = 1, + .type = SENSOR_TYPE_MOTO_DOUBLE_TAP, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + /* WAKE_UP & SPECIAL_REPORT */ + .flags = 1 | 6, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#endif + +#ifdef ILI_CONFIG_PANEL_NOTIFICATIONS +static int ili_panel_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#endif + +#define DTS_INT_GPIO "touch,irq-gpio" +#define DTS_RESET_GPIO "touch,reset-gpio" +#define DTS_OF_NAME "tchip,ilitek" + +void ili_tp_reset(void) +{ + ILI_INFO("edge delay = %d\n", ilits->rst_edge_delay); + + /* Need accurate power sequence, do not change it to msleep */ + gpio_direction_output(ilits->tp_rst, 1); + mdelay(1); + gpio_set_value(ilits->tp_rst, 0); + mdelay(5); + gpio_set_value(ilits->tp_rst, 1); + mdelay(ilits->rst_edge_delay); +} + +void ili_input_register(void) +{ + ILI_INFO(); + + ilits->input = input_allocate_device(); + if (ERR_ALLOC_MEM(ilits->input)) { + ILI_ERR("Failed to allocate touch input device\n"); + input_free_device(ilits->input); + return; + } + + ilits->input->name = ilits->hwif->name; + ilits->input->phys = ilits->phys; + ilits->input->dev.parent = ilits->dev; + ilits->input->id.bustype = ilits->hwif->bus_type; + + /* set the supported event type for input device */ + set_bit(EV_ABS, ilits->input->evbit); + set_bit(EV_SYN, ilits->input->evbit); + set_bit(EV_KEY, ilits->input->evbit); + set_bit(BTN_TOUCH, ilits->input->keybit); + set_bit(BTN_TOOL_FINGER, ilits->input->keybit); + set_bit(INPUT_PROP_DIRECT, ilits->input->propbit); + + input_set_abs_params(ilits->input, ABS_MT_POSITION_X, TOUCH_SCREEN_X_MIN, ilits->panel_wid - 1, 0, 0); + input_set_abs_params(ilits->input, ABS_MT_POSITION_Y, TOUCH_SCREEN_Y_MIN, ilits->panel_hei - 1, 0, 0); + input_set_abs_params(ilits->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(ilits->input, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); + + if (MT_PRESSURE) + input_set_abs_params(ilits->input, ABS_MT_PRESSURE, 0, 255, 0, 0); + + if (MT_B_TYPE) { +#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE + input_mt_init_slots(ilits->input, MAX_TOUCH_NUM, INPUT_MT_DIRECT); +#else + input_mt_init_slots(ilits->input, MAX_TOUCH_NUM); +#endif /* LINUX_VERSION_CODE */ + } else { + input_set_abs_params(ilits->input, ABS_MT_TRACKING_ID, 0, MAX_TOUCH_NUM, 0, 0); + } + + /* Gesture keys register */ + input_set_capability(ilits->input, EV_KEY, KEY_POWER); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_UP); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_DOWN); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_LEFT); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_RIGHT); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_O); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_E); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_M); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_W); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_S); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_V); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_Z); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_C); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_F); + + __set_bit(KEY_GESTURE_POWER, ilits->input->keybit); + __set_bit(KEY_GESTURE_UP, ilits->input->keybit); + __set_bit(KEY_GESTURE_DOWN, ilits->input->keybit); + __set_bit(KEY_GESTURE_LEFT, ilits->input->keybit); + __set_bit(KEY_GESTURE_RIGHT, ilits->input->keybit); + __set_bit(KEY_GESTURE_O, ilits->input->keybit); + __set_bit(KEY_GESTURE_E, ilits->input->keybit); + __set_bit(KEY_GESTURE_M, ilits->input->keybit); + __set_bit(KEY_GESTURE_W, ilits->input->keybit); + __set_bit(KEY_GESTURE_S, ilits->input->keybit); + __set_bit(KEY_GESTURE_V, ilits->input->keybit); + __set_bit(KEY_GESTURE_Z, ilits->input->keybit); + __set_bit(KEY_GESTURE_C, ilits->input->keybit); + __set_bit(KEY_GESTURE_F, ilits->input->keybit); + + /* register the input device to input sub-system */ + if (input_register_device(ilits->input) < 0) { + ILI_ERR("Failed to register touch input device\n"); + input_unregister_device(ilits->input); + input_free_device(ilits->input); + } +} + +#if REGULATOR_POWER +void ili_plat_regulator_power_on(bool status) +{ + ILI_INFO("%s\n", status ? "POWER ON" : "POWER OFF"); + + if (status) { + if (ilits->vdd) { + if (regulator_enable(ilits->vdd) < 0) + ILI_ERR("regulator_enable VDD fail\n"); + } + if (ilits->vcc) { + if (regulator_enable(ilits->vcc) < 0) + ILI_ERR("regulator_enable VCC fail\n"); + } + } else { + if (ilits->vdd) { + if (regulator_disable(ilits->vdd) < 0) + ILI_ERR("regulator_enable VDD fail\n"); + } + if (ilits->vcc) { + if (regulator_disable(ilits->vcc) < 0) + ILI_ERR("regulator_enable VCC fail\n"); + } + } + atomic_set(&ilits->ice_stat, DISABLE); + mdelay(5); +} + +static void ilitek_plat_regulator_power_init(void) +{ + const char *vdd_name = "vdd"; + const char *vcc_name = "vcc"; + + ilits->vdd = regulator_get(ilits->dev, vdd_name); + if (ERR_ALLOC_MEM(ilits->vdd)) { + ILI_ERR("regulator_get VDD fail\n"); + ilits->vdd = NULL; + } + if (regulator_set_voltage(ilits->vdd, VDD_VOLTAGE, VDD_VOLTAGE) < 0) + ILI_ERR("Failed to set VDD %d\n", VDD_VOLTAGE); + + ilits->vcc = regulator_get(ilits->dev, vcc_name); + if (ERR_ALLOC_MEM(ilits->vcc)) { + ILI_ERR("regulator_get VCC fail.\n"); + ilits->vcc = NULL; + } + if (regulator_set_voltage(ilits->vcc, VCC_VOLTAGE, VCC_VOLTAGE) < 0) + ILI_ERR("Failed to set VCC %d\n", VCC_VOLTAGE); + + ili_plat_regulator_power_on(true); +} +#endif + +void ili_irq_disable(void) +{ + unsigned long flag; + + spin_lock_irqsave(&ilits->irq_spin, flag); + + if (atomic_read(&ilits->irq_stat) == DISABLE) + goto out; + + if (!ilits->irq_num) { + ILI_ERR("gpio_to_irq (%d) is incorrect\n", ilits->irq_num); + goto out; + } + + disable_irq_nosync(ilits->irq_num); + atomic_set(&ilits->irq_stat, DISABLE); + ILI_DBG("Disable irq success\n"); + +out: + spin_unlock_irqrestore(&ilits->irq_spin, flag); +} + +void ili_irq_enable(void) +{ + unsigned long flag; + + spin_lock_irqsave(&ilits->irq_spin, flag); + + if (atomic_read(&ilits->irq_stat) == ENABLE) + goto out; + + if (!ilits->irq_num) { + ILI_ERR("gpio_to_irq (%d) is incorrect\n", ilits->irq_num); + goto out; + } + + enable_irq(ilits->irq_num); + atomic_set(&ilits->irq_stat, ENABLE); + ILI_DBG("Enable irq success\n"); + +out: + spin_unlock_irqrestore(&ilits->irq_spin, flag); +} + +static irqreturn_t ilitek_plat_isr_top_half(int irq, void *dev_id) +{ + if (irq != ilits->irq_num) { + ILI_ERR("Incorrect irq number (%d)\n", irq); + return IRQ_NONE; + } + + if (atomic_read(&ilits->cmd_int_check) == ENABLE) { + atomic_set(&ilits->cmd_int_check, DISABLE); + ILI_DBG("CMD INT detected, ignore\n"); + wake_up(&(ilits->inq)); + return IRQ_HANDLED; + } + + if (ilits->prox_near) { + ILI_INFO("Proximity event, ignore interrupt!\n"); + return IRQ_HANDLED; + } + + ILI_DBG("report: %d, rst: %d, fw: %d, switch: %d, mp: %d, sleep: %d, esd: %d\n", + ilits->report, + atomic_read(&ilits->tp_reset), + atomic_read(&ilits->fw_stat), + atomic_read(&ilits->tp_sw_mode), + atomic_read(&ilits->mp_stat), + atomic_read(&ilits->tp_sleep), + atomic_read(&ilits->esd_stat)); + + if (!ilits->report || atomic_read(&ilits->tp_reset) || + atomic_read(&ilits->fw_stat) || atomic_read(&ilits->tp_sw_mode) || + atomic_read(&ilits->mp_stat) || atomic_read(&ilits->tp_sleep) || + atomic_read(&ilits->esd_stat)) { + ILI_DBG("ignore interrupt !\n"); + return IRQ_HANDLED; + } + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t ilitek_plat_isr_bottom_half(int irq, void *dev_id) +{ + if (mutex_is_locked(&ilits->touch_mutex)) { + ILI_DBG("touch is locked, ignore\n"); + return IRQ_HANDLED; + } + mutex_lock(&ilits->touch_mutex); + ili_report_handler(); + mutex_unlock(&ilits->touch_mutex); + return IRQ_HANDLED; +} + +void ili_irq_unregister(void) +{ + devm_free_irq(ilits->dev, ilits->irq_num, NULL); +} + +int ili_irq_register(int type) +{ + int ret = 0; + static bool get_irq_pin; + + atomic_set(&ilits->irq_stat, DISABLE); + + if (get_irq_pin == false) { + ilits->irq_num = gpio_to_irq(ilits->tp_int); + get_irq_pin = true; + } + + ILI_INFO("ilits->irq_num = %d\n", ilits->irq_num); + + ret = devm_request_threaded_irq(ilits->dev, ilits->irq_num, + ilitek_plat_isr_top_half, + ilitek_plat_isr_bottom_half, + type | IRQF_ONESHOT, "ilitek", NULL); + + if (type == IRQF_TRIGGER_FALLING) + ILI_INFO("IRQ TYPE = IRQF_TRIGGER_FALLING\n"); + if (type == IRQF_TRIGGER_RISING) + ILI_INFO("IRQ TYPE = IRQF_TRIGGER_RISING\n"); + + if (ret != 0) + ILI_ERR("Failed to register irq handler, irq = %d, ret = %d\n", ilits->irq_num, ret); + + atomic_set(&ilits->irq_stat, ENABLE); + + return ret; +} + +#if defined(CONFIG_FB) +static int ilitek_plat_notifier_fb(struct notifier_block *self, unsigned long event, void *data) +{ + int *blank; + struct fb_event *evdata = data; + + ILI_INFO("Notifier's event = %ld\n", event); + + /* + * FB_EVENT_BLANK(0x09): A hardware display blank change occurred. + * FB_EARLY_EVENT_BLANK(0x10): A hardware display blank early change occurred. + */ + if (evdata && evdata->data) { + blank = evdata->data; + switch (*blank) { +#ifdef CONFIG_DRM_MSM + case MSM_DRM_BLANK_POWERDOWN: +#else + case FB_BLANK_POWERDOWN: +#endif +#if CONFIG_PLAT_SPRD + case DRM_MODE_DPMS_OFF: +#endif /* CONFIG_PLAT_SPRD */ + if (TP_SUSPEND_PRIO) { +#ifdef CONFIG_DRM_MSM + if (event != MSM_DRM_EARLY_EVENT_BLANK) +#else + if (event != FB_EARLY_EVENT_BLANK) +#endif + return NOTIFY_DONE; + } else { +#ifdef CONFIG_DRM_MSM + if (event != MSM_DRM_EVENT_BLANK) +#else + if (event != FB_EVENT_BLANK) +#endif + return NOTIFY_DONE; + } + if (ili_sleep_handler(TP_SUSPEND) < 0) + ILI_ERR("TP suspend failed\n"); + break; +#ifdef CONFIG_DRM_MSM + case MSM_DRM_BLANK_UNBLANK: + case MSM_DRM_BLANK_NORMAL: +#else + case FB_BLANK_UNBLANK: + case FB_BLANK_NORMAL: +#endif + +#if CONFIG_PLAT_SPRD + case DRM_MODE_DPMS_ON: +#endif /* CONFIG_PLAT_SPRD */ + +#ifdef CONFIG_DRM_MSM + if (event == MSM_DRM_EVENT_BLANK) +#else + if (event == FB_EVENT_BLANK) +#endif + { + if (ili_sleep_handler(TP_RESUME) < 0) + ILI_ERR("TP resume failed\n"); + + } + break; + default: + ILI_ERR("Unknown event, blank = %d\n", *blank); + break; + } + } + return NOTIFY_OK; +} +#endif +#if defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) +static struct drm_panel *active_panel; + +int ili_drm_check_dt(struct device_node *np) +{ + int i = 0; + int count = 0; + struct device_node *node = NULL; + struct drm_panel *panel = NULL; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) { + ILI_ERR("find drm_panel count(%d) fail", count); + return 0; + } + + ILI_INFO("find drm_panel count(%d) ", count); + for (i = 0; i < count; i++) { + node = of_parse_phandle(np, "panel", i); + ILI_INFO("node%p", node); + panel = of_drm_find_panel(node); + ILI_INFO("panel%p ", panel); + + of_node_put(node); + if (!IS_ERR(panel)) { + ILI_INFO("find drm_panel successfully"); + active_panel = panel; + return 0; + } + } + + ILI_ERR("no find drm_panel"); + return -ENODEV; +} +int ili_check_default_tp(struct device_node *dt, const char *prop) +{ + const char *active_tp; + const char *compatible; + char *start; + int ret; + + ret = of_property_read_string(dt->parent, prop, &active_tp); + if (ret) { + ILI_ERR(" %s:fail to read %s %d\n", __func__, prop, ret); + return -ENODEV; + } + ILI_INFO("active_tp %s", active_tp); + ret = of_property_read_string(dt, "compatible", &compatible); + if (ret < 0) { + ILI_ERR(" %s:fail to read %s %d\n", __func__, "compatible", ret); + return -ENODEV; + } + ILI_INFO("compatible %s", compatible); + start = strnstr(active_tp, compatible, strlen(active_tp)); + if (start == NULL) { + ILI_ERR(" %s:no match compatible, %s, %s\n", + __func__, compatible, active_tp); + ret = -ENODEV; + } + + return ret; +} + +#ifndef ILI_CONFIG_PANEL_NOTIFICATIONS +static int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct drm_panel_notifier *evdata = data; + int *blank = NULL; + + if (!evdata) { + ILI_ERR("evdata is null"); + return 0; + } + + if (!((event == DRM_PANEL_EARLY_EVENT_BLANK ) + || (event == DRM_PANEL_EVENT_BLANK))) { + ILI_INFO("event(%lu) do not need process\n", event); + return 0; + } + + blank = evdata->data; + ILI_INFO("DRM event:%lu,blank:%d", event, *blank); + switch (*blank) { + case DRM_PANEL_BLANK_UNBLANK: + if (DRM_PANEL_EARLY_EVENT_BLANK == event) { + ILI_INFO("resume: event = %lu, not care\n", event); + } else if (DRM_PANEL_EVENT_BLANK == event) { + ILI_INFO("suspend: event = %lu, TP_RESUME\n", event); + if (ili_sleep_handler(TP_RESUME) < 0) + ILI_ERR("TP resume failed\n"); + } + break; + case DRM_PANEL_BLANK_POWERDOWN: + if (DRM_PANEL_EARLY_EVENT_BLANK == event) { + ILI_INFO("suspend: event = %lu, TP_SUSPEND\n", event); + if (ili_sleep_handler(TP_SUSPEND) < 0) + ILI_ERR("TP suspend failed\n"); + } else if (DRM_PANEL_EVENT_BLANK == event) { + ILI_INFO("suspend: event = %lu, not care\n", event); + } + break; + default: + ILI_INFO("DRM BLANK(%d) do not need process\n", *blank); + break; + } + + return 0; +} +#endif +#endif +#if defined(CONFIG_DRM_MSM) +static int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank = NULL; + if (!evdata) { + ILI_ERR("evdata is null"); + return 0; + } + + if (!((event == MSM_DRM_EARLY_EVENT_BLANK ) + || (event == MSM_DRM_EVENT_BLANK))) { + ILI_INFO("event(%lu) do not need process\n", event); + return 0; + } + + blank = evdata->data; + ILI_INFO("DRM event:%lu,blank:%d", event, *blank); + switch (*blank) { + case MSM_DRM_BLANK_UNBLANK: + if (MSM_DRM_EARLY_EVENT_BLANK == event) { + ILI_INFO("resume: event = %lu, not care\n", event); + } else if (MSM_DRM_EVENT_BLANK == event) { + if (ili_sleep_handler(TP_RESUME) < 0) + ILI_ERR("TP resume failed\n"); + } + break; + case MSM_DRM_BLANK_POWERDOWN: + if (MSM_DRM_EARLY_EVENT_BLANK == event) { + if (ili_sleep_handler(TP_SUSPEND) < 0) + ILI_ERR("TP suspend failed\n"); + } else if (MSM_DRM_EVENT_BLANK == event) { + ILI_INFO("suspend: event = %lu, not care\n", event); + } + break; + default: + ILI_INFO("DRM BLANK(%d) do not need process\n", *blank); + break; + } + + return 0; +} +#endif +#endif +#if defined(CONFIG_HAS_EARLYSUSPEND) +static void ilitek_plat_early_suspend(struct early_suspend *h) +{ + if (ili_sleep_handler(TP_SUSPEND) < 0) + ILI_ERR("TP suspend failed\n"); +} + +static void ilitek_plat_late_resume(struct early_suspend *h) +{ + if (ili_sleep_handler(TP_RESUME) < 0) + ILI_ERR("TP resume failed\n"); +} +#endif + +#ifdef ILI_SENSOR_EN +static int ili_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + ILI_INFO("Gesture set enable %d!", enable); + mutex_lock(&ilits->state_mutex); + if (enable == 1) { + ilits->should_enable_gesture = true; + } else if (enable == 0) { + ilits->should_enable_gesture = false; + } else { + ILI_INFO("unknown enable symbol\n"); + } + mutex_unlock(&ilits->state_mutex); + return 0; +} + +static int ili_sensor_init(struct ilitek_ts_data *data) +{ + struct ili_sensor_platform_data *sensor_pdata; + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + ILI_ERR("Failed to allocate device"); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct ili_sensor_platform_data), + GFP_KERNEL); + if (!sensor_pdata) { + ILI_ERR("Failed to allocate memory"); + goto free_sensor_pdata; + } + data->sensor_pdata = sensor_pdata; + + if (data->report_gesture_key) { + __set_bit(EV_KEY, sensor_input_dev->evbit); + __set_bit(KEY_F1, sensor_input_dev->keybit); + } else { + __set_bit(EV_ABS, sensor_input_dev->evbit); + input_set_abs_params(sensor_input_dev, ABS_DISTANCE, + 0, REPORT_MAX_COUNT, 0, 0); + } + __set_bit(EV_SYN, sensor_input_dev->evbit); + + sensor_input_dev->name = "double-tap"; + data->sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + ILI_ERR("Unable to register device, err=%d", err); + goto free_sensor_input_dev; + } + + sensor_pdata->ps_cdev = sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = ili_sensor_set_enable; + sensor_pdata->data = data; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(data->sensor_pdata->input_sensor_dev); +free_sensor_input_dev: + input_free_device(data->sensor_pdata->input_sensor_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, sensor_pdata); + data->sensor_pdata = NULL; +exit: + return 1; +} + +int ili_sensor_remove(struct ilitek_ts_data *data) +{ + sensors_classdev_unregister(&data->sensor_pdata->ps_cdev); + input_unregister_device(data->sensor_pdata->input_sensor_dev); + devm_kfree(&data->sensor_pdata->input_sensor_dev->dev, + data->sensor_pdata); + data->sensor_pdata = NULL; + data->wakeable = false; + data->should_enable_gesture = false; + return 0; +} +#endif + +#ifdef ILI_CONFIG_PANEL_NOTIFICATIONS +static int ili_panel_notifier_callback(struct notifier_block *self, unsigned long event, void *data) +{ +#ifdef ILI_SENSOR_EN + struct ilitek_ts_data *ts = + container_of(self, struct ilitek_ts_data, panel_notif); +#endif + + switch (event) { + case PANEL_EVENT_PRE_DISPLAY_OFF: + ILI_INFO("event=%lu\n", event); + if (ili_sleep_handler(TP_SUSPEND) < 0) + ILI_ERR("TP suspend failed\n"); +#ifdef ILI_SENSOR_EN + if (ts->should_enable_gesture) { + ILI_INFO("double tap gesture suspend\n"); + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); + } else { + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); + } +#endif + break; + + case PANEL_EVENT_DISPLAY_ON: + ILI_INFO("event=%lu\n", event); + if (ili_sleep_handler(TP_RESUME) < 0) + ILI_ERR("TP resume failed\n"); + break; + case PANEL_EVENT_DISPLAY_ON_PREPARE: + ILI_INFO("event=%lu\n", event); +#if RESUME_BY_DDI + ili_resume_by_ddi(); +#endif + break; + default: /* use DEV_TS here to avoid unused variable */ + ILI_INFO("%s: function not implemented event %lu\n", __func__, event); + break; + } + + return 0; +} + +void ilitek_panel_notifier_unregister(void) +{ + if (register_panel_notifier(&ilits->panel_notif)) { + ILI_ERR("unregister panel_notifier failed\n"); + } +} +#endif + +static void ilitek_plat_sleep_init(void) +{ + int ret; +#if defined(CONFIG_FB) + ILI_INFO("Init notifier_fb struct\n"); + ilits->notifier_fb.notifier_call = ilitek_plat_notifier_fb; + #if CONFIG_PLAT_SPRD + if (adf_register_client(&ilits->notifier_fb)) + ILI_ERR("Unable to register notifier_fb\n"); + #else + if (fb_register_client(&ilits->notifier_fb)) + ILI_ERR("Unable to register notifier_fb\n"); + #endif /* CONFIG_PLAT_SPRD */ +#endif +#ifdef ILI_CONFIG_PANEL_NOTIFICATIONS + ilits->panel_notif.notifier_call = ili_panel_notifier_callback; + ret = register_panel_notifier(&ilits->panel_notif); + if(ret) { + ILI_ERR("register panel_notifier failed. ret=%d\n", ret); + } +#else +#if defined(CONFIG_DRM) + ilits->notifier_fb.notifier_call = drm_notifier_callback; + #if defined(CONFIG_DRM_PANEL) + if (active_panel) { + ret = drm_panel_notifier_register(active_panel,&ilits->notifier_fb); + if (ret) + ILI_ERR("[DRM]drm_panel_notifier_register fail: %d\n", ret); + } + #endif + #if defined(CONFIG_DRM_MSM) + ret = msm_drm_register_client(&ilits->notifier_fb); + if (ret) { + ILI_ERR("[DRM]Unable to register fb_notifier: %d\n", ret); + } + #endif +#endif/* CONFIG_DRM */ +#endif +#if defined(CONFIG_HAS_EARLYSUSPEND) + ILI_ERR("Init eqarly_suspend struct\n"); + ilits->early_suspend.suspend = ilitek_plat_early_suspend; + ilits->early_suspend.resume = ilitek_plat_late_resume; + ilits->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + register_early_suspend(&ilits->early_suspend); +#endif +} + +static int ilitek_plat_gpio_register(void) +{ + int ret = 0; + u32 flag; + struct device_node *dev_node = ilits->dev->of_node; +#if defined(CONFIG_DRM) +#if defined(CONFIG_DRM_PANEL) + int err; + ret = ili_drm_check_dt(dev_node); + if (ret) { + ILI_ERR("parse drm-panel fail"); + if(!ili_check_default_tp(dev_node,"tchip,ilitek")) + { + ILI_ERR("EPROBE_DEFER"); + err=-EPROBE_DEFER; + }else{ + ILI_ERR("ENODEV"); + err= -ENODEV; + } + return err; + } +#endif +#endif +#ifdef ILI_SENSOR_EN + if (of_property_read_bool(dev_node, "ilitek,report_gesture_key")) { + ILI_INFO("ilitek,report_gesture_key set"); + ilits->report_gesture_key = 1; + } else { + ilits->report_gesture_key = 0; + } +#endif + ilits->charger_detection_enable = of_property_read_bool(dev_node, "ilitek,usb_charger"); + if (!ilits->charger_detection_enable) { + ILI_ERR("error reading ilitek,usb_charger. ilits->charger_detection_enable = %d\n", + ilits->charger_detection_enable); + } else { + ILI_INFO("ilitek,usb_charger = %d\n", ilits->charger_detection_enable); + } + + ilits->tp_int = of_get_named_gpio_flags(dev_node, DTS_INT_GPIO, 0, &flag); + ilits->tp_rst = of_get_named_gpio_flags(dev_node, DTS_RESET_GPIO, 0, &flag); + + ILI_INFO("TP INT: %d\n", ilits->tp_int); + ILI_INFO("TP RESET: %d\n", ilits->tp_rst); + + if (!gpio_is_valid(ilits->tp_int)) { + ILI_ERR("Invalid INT gpio: %d\n", ilits->tp_int); + return -EBADR; + } + + if (!gpio_is_valid(ilits->tp_rst)) { + ILI_ERR("Invalid RESET gpio: %d\n", ilits->tp_rst); + return -EBADR; + } + + ret = gpio_request(ilits->tp_int, "TP_INT"); + if (ret < 0) { + ILI_ERR("Request IRQ GPIO failed, ret = %d\n", ret); + gpio_free(ilits->tp_int); + ret = gpio_request(ilits->tp_int, "TP_INT"); + if (ret < 0) { + ILI_ERR("Retrying request INT GPIO still failed , ret = %d\n", ret); + goto out; + } + } + + ret = gpio_request(ilits->tp_rst, "TP_RESET"); + if (ret < 0) { + ILI_ERR("Request RESET GPIO failed, ret = %d\n", ret); + gpio_free(ilits->tp_rst); + ret = gpio_request(ilits->tp_rst, "TP_RESET"); + if (ret < 0) { + ILI_ERR("Retrying request RESET GPIO still failed , ret = %d\n", ret); + goto out; + } + } + +out: + gpio_direction_input(ilits->tp_int); + return ret; +} + +static int ilitek_charger_notifier_callback(struct notifier_block *nb, + unsigned long val, void *v) +{ + int ret = 0; + struct power_supply *psy = NULL; + union power_supply_propval prop; + + if(ilits->fw_update_stat != FW_UPDATE_PASS) + return 0; + + psy= power_supply_get_by_name("usb"); + if (!psy) { + ILI_ERR("Couldn't get usbpsy\n"); + return -EINVAL; + } + if (!strcmp(psy->desc->name, "usb")) { + if (psy && val == POWER_SUPPLY_PROP_STATUS) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &prop); + if (ret < 0) { + ILI_ERR("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret); + return ret; + } else { + if(prop.intval != ilits->usb_plug_status) { + ILI_INFO("usb_plug_status =%d\n", prop.intval); + ilits->usb_plug_status = prop.intval; + if(ilits->charger_detection_enable && !ilits->tp_suspend && + (ilits->charger_notify_wq != NULL)) + queue_work(ilits->charger_notify_wq, &ilits->charger_notify_work); + } + } + } + } + return 0; +} + +static void ilitek_charger_notify_work(struct work_struct *work) +{ + int ret = 0; + + if (NULL == work) { + ILI_ERR("%s: parameter work are null!\n", __func__); + return; + } + ILI_INFO("enter ilitek_charger_notify_work\n"); + + mutex_lock(&ilits->touch_mutex); + ret = ili_ic_func_ctrl("plug", !ilits->usb_plug_status);// plug in + if(ret<0) { + ILI_ERR("Write plug in failed\n"); + } + mutex_unlock(&ilits->touch_mutex); +} + +void ilitek_plat_charger_init(void) +{ + int ret = 0; + struct power_supply *psy = NULL; + union power_supply_propval prop; + + ilits->charger_notify_wq = create_singlethread_workqueue("ili_charger_wq"); + if (!ilits->charger_notify_wq) { + ILI_ERR("allocate charger_notify_wq failed\n"); + return; + } + INIT_WORK(&ilits->charger_notify_work, ilitek_charger_notify_work); + ilits->charger_notif.notifier_call = ilitek_charger_notifier_callback; + ret = power_supply_reg_notifier(&ilits->charger_notif); + if (ret < 0) + ILI_ERR("power_supply_reg_notifier failed\n"); + + /* if power supply supplier registered brfore TP + * ps_notify_callback will not receive PSY_EVENT_PROP_ADDED + * event, and will cause miss to set TP into charger state. + * So check PS state in probe. + */ + psy = power_supply_get_by_name("usb"); + if (psy) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &prop); + if (ret < 0) { + ILI_ERR("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret); + } else { + ilits->usb_plug_status = prop.intval; + ILI_INFO("boot check usb_plug_status = %d\n", prop.intval); + } + } +} + +static int ilitek_plat_probe(void) +{ +#ifdef ILI_SENSOR_EN + static bool initialized_sensor; +#endif + ILI_INFO("platform probe\n"); + +#if REGULATOR_POWER + ilitek_plat_regulator_power_init(); +#endif + + if (ilitek_plat_gpio_register() < 0) + ILI_ERR("Register gpio failed\n"); + + if (ilits->charger_detection_enable) + ilitek_plat_charger_init(); + + if (ili_tddi_init() < 0) { + ILI_ERR("ILITEK Driver probe failed\n"); + if(gpio_is_valid(ilits->tp_int)) + gpio_free(ilits->tp_int); + if(gpio_is_valid(ilits->tp_rst)) + gpio_free(ilits->tp_rst); + if (ilits->charger_notif.notifier_call) + power_supply_unreg_notifier(&ilits->charger_notif); + if (ilits->charger_notify_wq) + destroy_workqueue(ilits->charger_notify_wq); + return -ENODEV; + } + + ili_irq_register(ilits->irq_tirgger_type); + ilitek_plat_sleep_init(); + ilits->pm_suspend = false; + init_completion(&ilits->pm_completion); +#ifdef ILI_SENSOR_EN + mutex_init(&ilits->state_mutex); + //unknown screen state + ilits->screen_state = SCREEN_UNKNOWN; + if (!initialized_sensor) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&(ilits->gesture_wakelock), WAKE_LOCK_SUSPEND, "dt-wake-lock"); +#else + PM_WAKEUP_REGISTER(ilits->dev, ilits->gesture_wakelock, "dt-wake-lock"); + if (!ilits->gesture_wakelock) { + ILI_ERR("ILITEK Driver failed to load. wakeup_source_init failed."); + return -ENOMEM; + } +#endif + if (!ili_sensor_init(ilits)) + initialized_sensor = true; + } +#endif + ILI_INFO("ILITEK Driver loaded successfully!"); + return 0; +} + +static int ilitek_tp_pm_suspend(struct device *dev) +{ + ILI_INFO("CALL BACK TP PM SUSPEND"); + ilits->pm_suspend = true; + reinit_completion(&ilits->pm_completion); + return 0; +} + +static int ilitek_tp_pm_resume(struct device *dev) +{ + ILI_INFO("CALL BACK TP PM RESUME"); + ilits->pm_suspend = false; + complete(&ilits->pm_completion); + return 0; +} + +static int ilitek_plat_remove(void) +{ + ILI_INFO("remove plat dev\n"); +#ifndef CONFIG_HAS_WAKELOCK + if(ilits->gesture_wakelock) + PM_WAKEUP_UNREGISTER(ilits->gesture_wakelock); +#endif + ili_dev_remove(); + return 0; +} + +static const struct dev_pm_ops tp_pm_ops = { + .suspend = ilitek_tp_pm_suspend, + .resume = ilitek_tp_pm_resume, +}; + +static const struct of_device_id tp_match_table[] = { + {.compatible = DTS_OF_NAME}, + {}, +}; + +static struct ilitek_hwif_info hwif = { + .bus_type = TDDI_INTERFACE, + .plat_type = TP_PLAT_QCOM, + .owner = THIS_MODULE, + .name = TDDI_DEV_ID, + .of_match_table = of_match_ptr(tp_match_table), + .plat_probe = ilitek_plat_probe, + .plat_remove = ilitek_plat_remove, + .pm = &tp_pm_ops, +}; + +static int __init ilitek_plat_dev_init(void) +{ + ILI_INFO("ILITEK TP driver init for QCOM\n"); + if (ili_dev_init(&hwif) < 0) { + ILI_ERR("Failed to register i2c/spi bus driver\n"); + return -ENODEV; + } + return 0; +} + +static void __exit ilitek_plat_dev_exit(void) +{ + ILI_INFO("remove plat dev\n"); + ili_dev_remove(); +} + +module_init(ilitek_plat_dev_init); +module_exit(ilitek_plat_dev_exit); +MODULE_AUTHOR("ILITEK"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_spi.c b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_spi.c new file mode 100644 index 000000000000..9962d6659bb0 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_spi.c @@ -0,0 +1,615 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek.h" + +struct touch_bus_info { + struct spi_driver bus_driver; + struct ilitek_hwif_info *hwif; +}; + +struct ilitek_ts_data *ilits; + +#if SPI_DMA_TRANSFER_SPLIT +#define DMA_TRANSFER_MAX_CHUNK 64 // number of chunks to be transferred. +#define DMA_TRANSFER_MAX_LEN 4096 // length of a chunk. + +int ili_spi_write_then_read_split(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx) +{ + int status = -1, duplex_len = 0; + int xfercnt = 0, xferlen = 0, xferloop = 0; + int offset = 0; + u8 cmd = 0; + struct spi_message message; + struct spi_transfer *xfer; + + xfer = kzalloc(DMA_TRANSFER_MAX_CHUNK * sizeof(struct spi_transfer), GFP_KERNEL); + + if (n_rx > SPI_RX_BUF_SIZE) { + ILI_ERR("Rx length is greater than spi local buf, abort\n"); + status = -ENOMEM; + goto out; + } + + spi_message_init(&message); + memset(ilits->spi_tx, 0x0, SPI_TX_BUF_SIZE); + memset(ilits->spi_rx, 0x0, SPI_RX_BUF_SIZE); + + if ((n_tx > 0) && (n_rx > 0)) + cmd = SPI_READ; + else + cmd = SPI_WRITE; + + switch (cmd) { + case SPI_WRITE: + if (n_tx % DMA_TRANSFER_MAX_LEN) + xferloop = (n_tx / DMA_TRANSFER_MAX_LEN) + 1; + else + xferloop = n_tx / DMA_TRANSFER_MAX_LEN; + + xferlen = n_tx; + memcpy(ilits->spi_tx, (u8 *)txbuf, xferlen); + + for (xfercnt = 0; xfercnt < xferloop; xfercnt++) { + if (xferlen > DMA_TRANSFER_MAX_LEN) + xferlen = DMA_TRANSFER_MAX_LEN; + + xfer[xfercnt].len = xferlen; + xfer[xfercnt].tx_buf = ilits->spi_tx + xfercnt * DMA_TRANSFER_MAX_LEN; + spi_message_add_tail(&xfer[xfercnt], &message); + xferlen = n_tx - (xfercnt+1) * DMA_TRANSFER_MAX_LEN; + } + status = spi_sync(spi, &message); + break; + case SPI_READ: + if (n_tx > DMA_TRANSFER_MAX_LEN) { + ILI_ERR("Tx length must be lower than dma length (%d).\n", DMA_TRANSFER_MAX_LEN); + status = -EINVAL; + break; + } + + if (!atomic_read(&ilits->ice_stat)) + offset = 2; + + memcpy(ilits->spi_tx, txbuf, n_tx); + duplex_len = n_tx + n_rx + offset; + + if (duplex_len % DMA_TRANSFER_MAX_LEN) + xferloop = (duplex_len / DMA_TRANSFER_MAX_LEN) + 1; + else + xferloop = duplex_len / DMA_TRANSFER_MAX_LEN; + + xferlen = duplex_len; + for (xfercnt = 0; xfercnt < xferloop; xfercnt++) { + if (xferlen > DMA_TRANSFER_MAX_LEN) + xferlen = DMA_TRANSFER_MAX_LEN; + + xfer[xfercnt].len = xferlen; + xfer[xfercnt].tx_buf = ilits->spi_tx; + xfer[xfercnt].rx_buf = ilits->spi_rx + xfercnt * DMA_TRANSFER_MAX_LEN; + spi_message_add_tail(&xfer[xfercnt], &message); + xferlen = duplex_len - (xfercnt + 1) * DMA_TRANSFER_MAX_LEN; + } + + status = spi_sync(spi, &message); + if (status == 0) { + if (ilits->spi_rx[1] != SPI_ACK && !atomic_read(&ilits->ice_stat)) { + status = DO_SPI_RECOVER; + ILI_ERR("Do spi recovery: rxbuf[1] = 0x%x, ice = %d\n", ilits->spi_rx[1], atomic_read(&ilits->ice_stat)); + break; + } + + memcpy((u8 *)rxbuf, ilits->spi_rx + offset + 1, n_rx); + } else { + ILI_ERR("spi read fail, status = %d\n", status); + } + break; + default: + ILI_INFO("Unknown command 0x%x\n", cmd); + break; + } + +out: + ipio_kfree((void **)&xfer); + return status; +} +#else +int ili_spi_write_then_read_direct(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx) +{ + int status = -1, duplex_len = 0; + int offset = 0; + u8 cmd; + struct spi_message message; + struct spi_transfer xfer; + + if (n_rx > SPI_RX_BUF_SIZE) { + ILI_ERR("Rx length is greater than spi local buf, abort\n"); + status = -ENOMEM; + goto out; + } + + spi_message_init(&message); + memset(&xfer, 0, sizeof(xfer)); + + if ((n_tx > 0) && (n_rx > 0)) + cmd = SPI_READ; + else + cmd = SPI_WRITE; + + switch (cmd) { + case SPI_WRITE: + xfer.len = n_tx; + xfer.tx_buf = txbuf; + spi_message_add_tail(&xfer, &message); + status = spi_sync(spi, &message); + break; + case SPI_READ: + if (!atomic_read(&ilits->ice_stat)) + offset = 2; + + duplex_len = n_tx + n_rx + offset; + if ((duplex_len > SPI_TX_BUF_SIZE) || + (duplex_len > SPI_RX_BUF_SIZE)) { + ILI_ERR("duplex_len is over than dma buf, abort\n"); + status = -ENOMEM; + break; + } + + memset(ilits->spi_tx, 0x0, SPI_TX_BUF_SIZE); + memset(ilits->spi_rx, 0x0, SPI_RX_BUF_SIZE); + + xfer.len = duplex_len; + memcpy(ilits->spi_tx, txbuf, n_tx); + xfer.tx_buf = ilits->spi_tx; + xfer.rx_buf = ilits->spi_rx; + + spi_message_add_tail(&xfer, &message); + status = spi_sync(spi, &message); + if (status == 0) { + if (ilits->spi_rx[1] != SPI_ACK && !atomic_read(&ilits->ice_stat)) { + status = DO_SPI_RECOVER; + ILI_ERR("Do spi recovery: rxbuf[1] = 0x%x, ice = %d\n", ilits->spi_rx[1], atomic_read(&ilits->ice_stat)); + break; + } + + memcpy((u8 *)rxbuf, ilits->spi_rx + offset + 1, n_rx); + } else { + ILI_ERR("spi read fail, status = %d\n", status); + } + break; + default: + ILI_INFO("Unknown command 0x%x\n", cmd); + break; + } + +out: + return status; +} +#endif + +static int ili_spi_mp_pre_cmd(u8 cdc) +{ + u8 pre[5] = {0}; + + if (!atomic_read(&ilits->mp_stat) || cdc != P5_X_SET_CDC_INIT || + ilits->chip->core_ver >= CORE_VER_1430) + return 0; + + ILI_DBG("mp test with pre commands\n"); + + pre[0] = SPI_WRITE; + pre[1] = 0x0;// len low byte + pre[2] = 0x2;// len high byte + pre[3] = P5_X_READ_DATA_CTRL; + pre[4] = P5_X_GET_CDC_DATA; + if (ilits->spi_write_then_read(ilits->spi, pre, 5, NULL, 0) < 0) { + ILI_ERR("Failed to write pre commands\n"); + return -1; + } + + pre[0] = SPI_WRITE; + pre[1] = 0x0;// len low byte + pre[2] = 0x1;// len high byte + pre[3] = P5_X_GET_CDC_DATA; + if (ilits->spi_write_then_read(ilits->spi, pre, 4, NULL, 0) < 0) { + ILI_ERR("Failed to write pre commands\n"); + return -1; + } + return 0; +} + +static int ili_spi_pll_clk_wakeup(void) +{ + int index = 0; + u8 wdata[32] = {0}; + u8 wakeup[9] = {0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3}; + u32 wlen = sizeof(wakeup); + + wdata[0] = SPI_WRITE; + wdata[1] = wlen >> 8; + wdata[2] = wlen & 0xff; + index = 3; + + ipio_memcpy(&wdata[index], wakeup, wlen, wlen); + wlen += index; + + ILI_INFO("Write dummy to wake up spi pll clk\n"); + if (ilits->spi_write_then_read(ilits->spi, wdata, wlen, NULL, 0) < 0) { + ILI_INFO("spi slave write error\n"); + return -1; + } + + return 0; +} + +static int ili_spi_wrapper(u8 *txbuf, u32 wlen, u8 *rxbuf, u32 rlen, bool spi_irq, bool i2c_irq) +{ + int ret = 0; + int mode = 0, index = 0; + u8 wdata[32] = {0}; + u8 checksum = 0; + bool ice = atomic_read(&ilits->ice_stat); + + if (wlen > 0) { + if (!txbuf) { + ILI_ERR("txbuf is null\n"); + return -ENOMEM; + } + + /* 3 bytes data consist of length and header */ + if ((wlen + 3) > sizeof(wdata)) { + ILI_ERR("WARNING! wlen(%d) > wdata(%d), using wdata length to transfer\n", wlen, (int)sizeof(wdata)); + wlen = sizeof(wdata) - 3; + } + } + + if (rlen > 0) { + if (!rxbuf) { + ILI_ERR("rxbuf is null\n"); + return -ENOMEM; + } + } + + if (rlen > 0 && !wlen) + mode = SPI_READ; + else + mode = SPI_WRITE; + + if (ilits->int_pulse) + ilits->detect_int_stat = ili_ic_check_int_pulse; + else + ilits->detect_int_stat = ili_ic_check_int_level; + + if (spi_irq) + atomic_set(&ilits->cmd_int_check, ENABLE); + + switch (mode) { + case SPI_WRITE: +#if ( Pll_Clk_Wakeup_TP_Resume == ENABLE ) + if (ilits->pll_clk_wakeup == true) +#else + if ((ilits->pll_clk_wakeup == true) && (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE)) +#endif + { + ret = ili_spi_pll_clk_wakeup(); + if (ret < 0) { + ILI_ERR("Wakeup pll clk error\n"); + break; + } + } + if (ice) { + wdata[0] = SPI_WRITE; + index = 1; + } else { + wdata[0] = SPI_WRITE; + wdata[1] = wlen >> 8; + wdata[2] = wlen & 0xff; + index = 3; + } + + ipio_memcpy(&wdata[index], txbuf, wlen, wlen); + wlen += index; + + /* + * NOTE: If TP driver is doing MP test and commanding 0xF1 to FW, we add a checksum + * to the last index and plus 1 with size. + */ + if (atomic_read(&ilits->mp_stat) && wdata[index] == P5_X_SET_CDC_INIT) { + checksum = ili_calc_packet_checksum(&wdata[index], wlen - index); + wdata[wlen] = checksum; + wlen++; + wdata[1] = (wlen - index) >> 8; + wdata[2] = (wlen - index) & 0xff; + ili_dump_data(wdata, 8, wlen, 0, "mp cdc cmd with checksum"); + } + + ret = ilits->spi_write_then_read(ilits->spi, wdata, wlen, txbuf, 0); + if (ret < 0) { + ILI_INFO("spi-wrapper write error\n"); + break; + } + + /* Won't break if it needs to read data following with writing. */ + if (!rlen) + break; + case SPI_READ: + if (!ice && spi_irq) { + /* Check INT triggered by FW when sending cmds. */ + if (ilits->detect_int_stat(false) < 0) { + ILI_ERR("ERROR! Check INT timeout\n"); + ret = -ETIME; + break; + } + } + + ret = ili_spi_mp_pre_cmd(wdata[3]); + if (ret < 0) + ILI_ERR("spi-wrapper mp pre cmd error\n"); + + wdata[0] = SPI_READ; + + ret = ilits->spi_write_then_read(ilits->spi, wdata, 1, rxbuf, rlen); + if (ret < 0) + ILI_ERR("spi-wrapper read error\n"); + + break; + default: + ILI_ERR("Unknown spi mode (%d)\n", mode); + ret = -EINVAL; + break; + } + + if (spi_irq) + atomic_set(&ilits->cmd_int_check, DISABLE); + + return ret; +} + +int ili_core_spi_setup(int num) +{ + u32 freq[] = { + TP_SPI_CLK_1M, + TP_SPI_CLK_2M, + TP_SPI_CLK_3M, + TP_SPI_CLK_4M, + TP_SPI_CLK_5M, + TP_SPI_CLK_6M, + TP_SPI_CLK_7M, + TP_SPI_CLK_8M, + TP_SPI_CLK_9M, + TP_SPI_CLK_10M, + TP_SPI_CLK_11M, + TP_SPI_CLK_12M, + TP_SPI_CLK_13M, + TP_SPI_CLK_14M, + TP_SPI_CLK_15M + }; + + if (num > sizeof(freq)) { + ILI_ERR("Invaild clk freq, set default clk freq\n"); + num = 7; + } + + ILI_INFO("spi clock = %d\n", freq[num]); + + ilits->spi->mode = SPI_MODE_0; + ilits->spi->bits_per_word = 8; + ilits->spi->max_speed_hz = freq[num]; + + if (spi_setup(ilits->spi) < 0) { + ILI_ERR("Failed to setup spi device\n"); + return -ENODEV; + } + + ILI_INFO("name = %s, bus_num = %d,cs = %d, mode = %d, speed = %d\n", + ilits->spi->modalias, + ilits->spi->master->bus_num, + ilits->spi->chip_select, + ilits->spi->mode, + ilits->spi->max_speed_hz); + return 0; +} + +static int ilitek_spi_probe(struct spi_device *spi) +{ + struct touch_bus_info *info = + container_of(to_spi_driver(spi->dev.driver), + struct touch_bus_info, bus_driver); + + ILI_INFO("ilitek spi probe\n"); + + if (!spi) { + ILI_ERR("spi device is NULL\n"); + return -ENODEV; + } + + ilits = devm_kzalloc(&spi->dev, sizeof(struct ilitek_ts_data), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits)) { + ILI_ERR("Failed to allocate ts memory, %ld\n", PTR_ERR(ilits)); + return -ENOMEM; + } + + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) { + ILI_ERR("Full duplex not supported by master\n"); + return -EIO; + } + + ilits->update_buf = kzalloc(MAX_HEX_FILE_SIZE, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(ilits->update_buf)) { + ILI_ERR("fw kzalloc error\n"); + return -ENOMEM; + } + + /* Used for receiving touch data only, do not mix up with others. */ + ilits->tr_buf = kzalloc(TR_BUF_SIZE, GFP_ATOMIC); + if (ERR_ALLOC_MEM(ilits->tr_buf)) { + ILI_ERR("failed to allocate touch report buffer\n"); + return -ENOMEM; + } + + ilits->spi_tx = kzalloc(SPI_TX_BUF_SIZE, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(ilits->spi_tx)) { + ILI_ERR("Failed to allocate spi tx buffer\n"); + return -ENOMEM; + } + + ilits->spi_rx = kzalloc(SPI_RX_BUF_SIZE, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(ilits->spi_rx)) { + ILI_ERR("Failed to allocate spi rx buffer\n"); + return -ENOMEM; + } + + ilits->gcoord = kzalloc(sizeof(struct gesture_coordinate), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->gcoord)) { + ILI_ERR("Failed to allocate gresture coordinate buffer\n"); + return -ENOMEM; + } + + ilits->i2c = NULL; + ilits->spi = spi; + ilits->dev = &spi->dev; + ilits->hwif = info->hwif; + ilits->phys = "SPI"; + ilits->wrapper = ili_spi_wrapper; + ilits->detect_int_stat = ili_ic_check_int_pulse; + ilits->int_pulse = true; + ilits->mp_retry = false; + +#if SPI_DMA_TRANSFER_SPLIT + ilits->spi_write_then_read = ili_spi_write_then_read_split; +#else + ilits->spi_write_then_read = ili_spi_write_then_read_direct; +#endif + + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + ilits->tp_data_format = DATA_FORMAT_DEMO; + ilits->tp_data_len = P5_X_DEMO_MODE_PACKET_LEN; + + if (TDDI_RST_BIND) + ilits->reset = TP_IC_WHOLE_RST; + else + ilits->reset = TP_HW_RST_ONLY; + + ilits->rst_edge_delay = 10; + ilits->fw_open = REQUEST_FIRMWARE; + ilits->fw_upgrade_mode = UPGRADE_IRAM; + ilits->mp_move_code = ili_move_mp_code_iram; + ilits->gesture_move_code = ili_move_gesture_code_iram; + ilits->esd_recover = ili_wq_esd_spi_check; + ilits->ges_recover = ili_touch_esd_gesture_iram; + ilits->gesture_mode = DATA_FORMAT_GESTURE_INFO; + ilits->gesture_demo_ctrl = DISABLE; + ilits->wtd_ctrl = OFF; + ilits->report = ENABLE; + ilits->netlink = DISABLE; + ilits->dnp = DISABLE; + ilits->irq_tirgger_type = IRQF_TRIGGER_FALLING; + ilits->info_from_hex = ENABLE; + ilits->wait_int_timeout = AP_INT_TIMEOUT; + +#if ENABLE_GESTURE + ilits->gesture = DISABLE; + ilits->ges_sym.double_tap = DOUBLE_TAP; + ilits->ges_sym.alphabet_line_2_top = ALPHABET_LINE_2_TOP; + ilits->ges_sym.alphabet_line_2_bottom = ALPHABET_LINE_2_BOTTOM; + ilits->ges_sym.alphabet_line_2_left = ALPHABET_LINE_2_LEFT; + ilits->ges_sym.alphabet_line_2_right = ALPHABET_LINE_2_RIGHT; + ilits->ges_sym.alphabet_m = ALPHABET_M; + ilits->ges_sym.alphabet_w = ALPHABET_W; + ilits->ges_sym.alphabet_c = ALPHABET_C; + ilits->ges_sym.alphabet_E = ALPHABET_E; + ilits->ges_sym.alphabet_V = ALPHABET_V; + ilits->ges_sym.alphabet_O = ALPHABET_O; + ilits->ges_sym.alphabet_S = ALPHABET_S; + ilits->ges_sym.alphabet_Z = ALPHABET_Z; + ilits->ges_sym.alphabet_V_down = ALPHABET_V_DOWN; + ilits->ges_sym.alphabet_V_left = ALPHABET_V_LEFT; + ilits->ges_sym.alphabet_V_right = ALPHABET_V_RIGHT; + ilits->ges_sym.alphabet_two_line_2_bottom = ALPHABET_TWO_LINE_2_BOTTOM; + ilits->ges_sym.alphabet_F = ALPHABET_F; + ilits->ges_sym.alphabet_AT = ALPHABET_AT; +#endif + + if (ili_core_spi_setup(SPI_CLK) < 0) + return -EINVAL; + + return info->hwif->plat_probe(); +} + +static int ilitek_spi_remove(struct spi_device *spi) +{ + ILI_INFO(); + return 0; +} + +static struct spi_device_id tp_spi_id[] = { + {TDDI_DEV_ID, 0}, + {}, +}; + +int ili_interface_dev_init(struct ilitek_hwif_info *hwif) +{ + struct touch_bus_info *info; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ILI_ERR("faied to allocate spi_driver\n"); + return -ENOMEM; + } + + if (hwif->bus_type != BUS_SPI) { + ILI_ERR("Not SPI dev\n"); + ipio_kfree((void **)&info); + return -EINVAL; + } + + hwif->info = info; + + info->bus_driver.driver.name = hwif->name; + info->bus_driver.driver.owner = hwif->owner; + info->bus_driver.driver.of_match_table = hwif->of_match_table; + info->bus_driver.driver.pm = hwif->pm; + + info->bus_driver.probe = ilitek_spi_probe; + info->bus_driver.remove = ilitek_spi_remove; + info->bus_driver.shutdown = ilitek_spi_shutdown; + info->bus_driver.id_table = tp_spi_id; + + info->hwif = hwif; + return spi_register_driver(&info->bus_driver); +} + +void ili_interface_dev_exit(struct ilitek_ts_data *ts) +{ + struct touch_bus_info *info = (struct touch_bus_info *)ilits->hwif->info; + + ILI_INFO("remove spi dev\n"); + kfree(ilits->update_buf); + kfree(ilits->spi_tx); + kfree(ilits->spi_rx); + spi_unregister_driver(&info->bus_driver); + ipio_kfree((void **)&info); +} diff --git a/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_touch.c b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_touch.c new file mode 100644 index 000000000000..be725e4dcfd6 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_0flash_mmi/ilitek_touch.c @@ -0,0 +1,1334 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek.h" + +/*gesture info mode*/ +struct ili_demo_debug_info_id0 { + u32 id : 8; + u32 sys_powr_state_e : 3; + u32 sys_state_e : 3; + u32 tp_state_e : 2; + + u32 touch_palm_state : 2; + u32 app_an_statu_e : 3; + u32 app_sys_bg_err : 1; + u32 g_b_wrong_bg : 1; + u32 reserved0 : 1; + + u32 normal_mode : 1; + u32 charger_mode : 1; + u32 glove_mode : 1; + u32 stylus_mode : 1; + u32 multi_mode : 1; + u32 noise_mode : 1; + u32 palm_plus_mode : 1; + u32 floating_mode : 1; + + u32 algo_pt_status0 : 3; + u32 algo_pt_status1 : 3; + u32 algo_pt_status2 : 3; + u32 algo_pt_status3 : 3; + u32 algo_pt_status4 : 3; + u32 algo_pt_status5 : 3; + u32 algo_pt_status6 : 3; + u32 algo_pt_status7 : 3; + u32 algo_pt_status8 : 3; + u32 algo_pt_status9 : 3; + u32 reserved2 : 2; + + u32 hopping_flg : 1; + u32 hopping_index : 5; + u32 frequency_h : 2; + u32 frequency_l : 8; + u32 reserved3 : 8; + u32 reserved4 : 8; + +}; + +void ili_dump_data(void *data, int type, int len, int row_len, const char *name) +{ + int i, row = 31; + u8 *p8 = NULL; + s32 *p32 = NULL; + s16 *p16 = NULL; + + if (!debug_en) + return; + + if (row_len > 0) + row = row_len; + + if (data == NULL) { + ILI_ERR("The data going to dump is NULL\n"); + return; + } + + pr_cont("\n\n"); + pr_cont("ILITEK: Dump %s data\n", name); + pr_cont("ILITEK: "); + + if (type == 8) + p8 = (u8 *) data; + if (type == 32 || type == 10) + p32 = (s32 *) data; + if (type == 16) + p16 = (s16 *) data; + + for (i = 0; i < len; i++) { + if (type == 8) + pr_cont(" %4x ", p8[i]); + else if (type == 32) + pr_cont(" %4x ", p32[i]); + else if (type == 10) + pr_cont(" %4d ", p32[i]); + else if (type == 16) + pr_cont(" %4d ", p16[i]); + + if ((i % row) == row - 1) { + pr_cont("\n"); + pr_cont("ILITEK: "); + } + } + pr_cont("\n\n"); +} + +static void dma_clear_reg_setting(void) +{ + /* 1. interrupt t0/t1 enable flag */ + if (ili_ice_mode_bit_mask_write(INTR32_ADDR, INTR32_reg_t0_int_en | INTR32_reg_t1_int_en, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR32_reg_t0_int_en | INTR32_reg_t1_int_en, INTR32_ADDR); + + /* 2. clear tdi_err_int_flag */ + if (ili_ice_mode_bit_mask_write(INTR2_ADDR, INTR2_tdi_err_int_flag_clear, (1 << 18)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR2_tdi_err_int_flag_clear, INTR2_ADDR); + + /* 3. clear dma channel 0 src1 info */ + if (ili_ice_mode_write(DMA49_reg_dma_ch0_src1_addr, 0x00000000, 4) < 0) + ILI_ERR("Write 0x00000000 at %x failed\n", DMA49_reg_dma_ch0_src1_addr); + if (ili_ice_mode_write(DMA50_reg_dma_ch0_src1_step_inc, 0x00, 1) < 0) + ILI_ERR("Write 0x0 at %x failed\n", DMA50_reg_dma_ch0_src1_step_inc); + if (ili_ice_mode_bit_mask_write(DMA50_ADDR, DMA50_reg_dma_ch0_src1_format | DMA50_reg_dma_ch0_src1_en, BIT(31)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA50_reg_dma_ch0_src1_format | DMA50_reg_dma_ch0_src1_en, DMA50_ADDR); + + /* 4. clear dma channel 0 trigger select */ + if (ili_ice_mode_bit_mask_write(DMA48_ADDR, DMA48_reg_dma_ch0_trigger_sel, (0 << 16)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA48_reg_dma_ch0_trigger_sel, DMA48_ADDR); + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + /* 5. clear dma flash setting */ + ili_flash_clear_dma(); +} + +static void dma_trigger_reg_setting(u32 reg_dest_addr, u32 flash_start_addr, u32 copy_size) +{ + int retry = 30; + u32 stat = 0; + + /* 1. set dma channel 0 clear */ + if (ili_ice_mode_bit_mask_write(DMA48_ADDR, DMA48_reg_dma_ch0_start_clear, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA48_reg_dma_ch0_start_clear, DMA48_ADDR); + + /* 2. set dma channel 0 src1 info */ + if (ili_ice_mode_write(DMA49_reg_dma_ch0_src1_addr, 0x00041010, 4) < 0) + ILI_ERR("Write 0x00041010 at %x failed\n", DMA49_reg_dma_ch0_src1_addr); + if (ili_ice_mode_write(DMA50_reg_dma_ch0_src1_step_inc, 0x00, 1) < 0) + ILI_ERR("Write 0x00 at %x failed\n", DMA50_reg_dma_ch0_src1_step_inc); + if (ili_ice_mode_bit_mask_write(DMA50_ADDR, DMA50_reg_dma_ch0_src1_format | DMA50_reg_dma_ch0_src1_en, BIT(31)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA50_reg_dma_ch0_src1_format | DMA50_reg_dma_ch0_src1_en, DMA50_ADDR); + + /* 3. set dma channel 0 src2 info */ + if (ili_ice_mode_bit_mask_write(DMA52_ADDR, DMA52_reg_dma_ch0_src2_en, (0 << 31)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA52_reg_dma_ch0_src2_en, DMA52_ADDR); + + /* 4. set dma channel 0 dest info */ + if (ili_ice_mode_write(DMA53_reg_dma_ch0_dest_addr, reg_dest_addr, 3) < 0) + ILI_ERR("Write %x at %x failed\n", reg_dest_addr, DMA53_reg_dma_ch0_dest_addr); + if (ili_ice_mode_write(DMA54_reg_dma_ch0_dest_step_inc, 0x01, 1) < 0) + ILI_ERR("Write 0x01 at %x failed\n", DMA54_reg_dma_ch0_dest_step_inc); + + if (ili_ice_mode_write(DMA54_ADDR, DMA54_reg_dma_ch0_dest_format | DMA54_reg_dma_ch0_dest_en, BIT(31)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA54_reg_dma_ch0_dest_format | DMA54_reg_dma_ch0_dest_en, DMA54_ADDR); + + /* 5. set dma channel 0 trafer info */ + if (ili_ice_mode_write(DMA55_reg_dma_ch0_trafer_counts, copy_size, 4) < 0) + ILI_ERR("Write %x at %x failed\n", copy_size, DMA55_reg_dma_ch0_trafer_counts); + if (ili_ice_mode_bit_mask_write(DMA55_ADDR, DMA55_reg_dma_ch0_trafer_mode, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA55_reg_dma_ch0_trafer_mode, DMA55_ADDR); + + /* 6. set dma channel 0 int info */ + if (ili_ice_mode_bit_mask_write(INTR33_ADDR, INTR33_reg_dma_ch0_int_en, (1 << 17)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR33_reg_dma_ch0_int_en, INTR33_ADDR); + + /* 7. set dma channel 0 trigger select */ + if (ili_ice_mode_bit_mask_write(DMA48_ADDR, DMA48_reg_dma_ch0_trigger_sel, (1 << 16)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA48_reg_dma_ch0_trigger_sel, DMA48_ADDR); + + /* 8. set dma flash setting */ + ili_flash_dma_write(flash_start_addr, (flash_start_addr+copy_size), copy_size); + + /* 9. clear flash and dma ch0 int flag */ + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_dma_ch1_int_flag | INTR1_reg_flash_int_flag, BIT(16) | BIT(25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_dma_ch1_int_flag | INTR1_reg_flash_int_flag, INTR1_ADDR); + if (ili_ice_mode_bit_mask_write(0x041013, BIT(0), 1) < 0) //patch + ILI_ERR("Write %lu at %x failed\n", BIT(0), 0x041013); + + /* DMA Trigger */ + if (ili_ice_mode_write(FLASH4_reg_rcv_data, 0xFF, 1) < 0) + ILI_ERR("Trigger DMA failed\n"); + + /* waiting for fw reload code completed. */ + while (retry > 0) { + if (ili_ice_mode_read(INTR1_ADDR, &stat, sizeof(u32)) < 0) { + ILI_ERR("Read 0x%x error\n", INTR1_ADDR); + retry--; + continue; + } + + ILI_DBG("fw dma stat = %x\n", stat); + + if ((stat & BIT(16)) == BIT(16)) + break; + + retry--; + usleep_range(1000, 1000); + } + + if (retry <= 0) + ILI_ERR("DMA fail: Regsiter = 0x%x Flash = 0x%x, Size = %d\n", + reg_dest_addr, flash_start_addr, copy_size); + + /* CS High */ + if (ili_ice_mode_write(FLASH0_reg_flash_csb, 0x1, 1) < 0) + ILI_ERR("Pull CS High failed\n"); + /* waiting for CS status done */ + mdelay(10); +} + +int ili_move_mp_code_flash(void) +{ + int ret = 0; + u32 mp_text_size = 0, mp_andes_init_size = 0; + u32 mp_flash_addr, mp_size, overlay_start_addr, overlay_end_addr; + bool dma_trigger_enable = 0; + u8 cmd[2] = {0}; + u8 data[16] = {0}; + + cmd[0] = P5_X_MP_TEST_MODE_INFO; + ret = ilits->wrapper(cmd, sizeof(u8), data, ilits->protocol->mp_info_len, ON, OFF); + ili_dump_data(data, 8, ilits->protocol->mp_info_len, 0, "MP overlay info"); + if (ret < 0) { + ILI_ERR("Failed to write info cmd\n"); + goto out; + } + + dma_trigger_enable = 0; + + mp_flash_addr = data[3] + (data[2] << 8) + (data[1] << 16); + mp_size = data[6] + (data[5] << 8) + (data[4] << 16); + overlay_start_addr = data[9] + (data[8] << 8) + (data[7] << 16); + overlay_end_addr = data[12] + (data[11] << 8) + (data[10] << 16); + + if (overlay_start_addr != 0x0 && overlay_end_addr != 0x0 + && data[0] == P5_X_MP_TEST_MODE_INFO) + dma_trigger_enable = 1; + + ILI_INFO("MP info Overlay: Enable = %d, addr = 0x%x ~ 0x%x, flash addr = 0x%x, mp size = 0x%x\n", + dma_trigger_enable, overlay_start_addr, + overlay_end_addr, mp_flash_addr, mp_size); + + cmd[0] = P5_X_MODE_CONTROL; + cmd[1] = P5_X_FW_TEST_MODE; + ret = ilits->wrapper(cmd, 2, NULL, 0, ON, OFF); + if (ret < 0) + goto out; + + /* Check if ic is ready switching test mode from demo mode */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + ret = ili_ic_check_busy(50, 50); /* Set busy as 0x41 */ + if (ret < 0) + goto out; + + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) + goto out; + + if (dma_trigger_enable) { + mp_andes_init_size = overlay_start_addr; + mp_text_size = (mp_size - overlay_end_addr) + 1; + ILI_INFO("MP andes init size = %d , MP text size = %d\n", mp_andes_init_size, mp_text_size); + + dma_clear_reg_setting(); + + ILI_INFO("[Move ANDES.INIT to DRAM]\n"); + dma_trigger_reg_setting(0, mp_flash_addr, mp_andes_init_size); /* DMA ANDES.INIT */ + + dma_clear_reg_setting(); + + ILI_INFO("[Move MP.TEXT to DRAM]\n"); + dma_trigger_reg_setting(overlay_end_addr, (mp_flash_addr + overlay_start_addr), mp_text_size); + + dma_clear_reg_setting(); + } else { + /* DMA Trigger */ + if (ili_ice_mode_write(FLASH4_reg_rcv_data, 0xFF, 1) < 0) + ILI_ERR("Trigger DMA failed\n"); + /* waiting for fw reload code completed. */ + mdelay(30); + + /* CS High */ + if (ili_ice_mode_write(FLASH0_reg_flash_csb, 0x1, 1) < 0) + ILI_ERR("Pull CS High failed\n"); + /* waiting for CS status done */ + mdelay(10); + } + + if (ili_reset_ctrl(TP_IC_CODE_RST) < 0) + ILI_ERR("IC Code reset failed during moving mp code\n"); + + ret = ili_ice_mode_ctrl(DISABLE, OFF); + if (ret < 0) + goto out; + + /* Check if ic is already in test mode */ + ilits->actual_tp_mode = P5_X_FW_TEST_MODE; /* set busy as 0x51 */ + ret = ili_ic_check_busy(300, 50); + if (ret < 0) + ILI_ERR("Check cdc timeout failed after moved mp code\n"); + +out: + return ret; +} + +int ili_move_mp_code_iram(void) +{ + ILI_INFO("Download MP code to iram\n"); + return ili_fw_upgrade_handler(NULL); +} + +int ili_proximity_near(int mode) +{ + int ret = 0; + + ilits->prox_near = true; + + switch (mode) { + case DDI_POWER_ON: + /* + * If the power of VSP and VSN keeps alive when proximity near event + * occures, TP can just go to sleep in. + */ + ret = ili_ic_func_ctrl("sleep", SLEEP_IN); + if (ret < 0) + ILI_ERR("Write sleep in cmd failed\n"); + break; + case DDI_POWER_OFF: + ILI_INFO("DDI POWER OFF, do nothing\n"); + break; + default: + ILI_ERR("Unknown mode (%d)\n", mode); + ret = -EINVAL; + break; + } + return ret; +} + +int ili_proximity_far(int mode) +{ + int ret = 0; + u8 cmd[2] = {0}; + + if (!ilits->prox_near) { + ILI_INFO("No proximity near event, break\n"); + return 0; + } + + switch (mode) { + case WAKE_UP_GESTURE_RECOVERY: + /* + * If the power of VSP and VSN has been shut down previsouly, + * TP should go through gesture recovery to get back. + */ + ili_gesture_recovery(); + break; + case WAKE_UP_SWITCH_GESTURE_MODE: + /* + * If the power of VSP and VSN keeps alive in the event of proximity near, + * TP can be just recovered by switching gesture mode to get back. + */ + cmd[0] = 0xF6; + cmd[1] = 0x0A; + + ILI_INFO("write prepare gesture command 0xF6 0x0A\n"); + ret = ilits->wrapper(cmd, 2, NULL, 0, ON, OFF); + if (ret < 0) { + ILI_INFO("write prepare gesture command error\n"); + break; + } + + ret = ili_switch_tp_mode(P5_X_FW_GESTURE_MODE); + if (ret < 0) + ILI_ERR("Switch to gesture mode failed during proximity far\n"); + break; + default: + ILI_ERR("Unknown mode (%d)\n", mode); + ret = -EINVAL; + break; + } + + ilits->prox_near = false; + + return ret; +} + +int ili_move_gesture_code_flash(int mode) +{ + int ret = 0; + + /* + * NOTE: If functions need to be added during suspend, + * they must be called before gesture cmd reaches to FW. + */ + + ILI_INFO("Gesture mode = %d\n", mode); + ret = ili_set_tp_data_len(mode, true, NULL); + + return ret; +} + +void ili_set_gesture_symbol(void) +{ + u8 cmd[7] = {0}; + struct gesture_symbol *ptr_sym = &ilits->ges_sym; + u8 *ptr; + + ptr = (u8*) ptr_sym; + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = 0x01; + cmd[2] = 0x0A; + cmd[3] = 0x08; + cmd[4] = ptr[0]; + cmd[5] = ptr[1]; + cmd[6] = ptr[2]; + + ili_dump_data(cmd, 8, sizeof(cmd), 0, "Gesture symbol"); + + if (ilits->wrapper(cmd, 2, NULL, 0, ON, OFF) < 0) { + ILI_ERR("Write pre cmd failed\n"); + return; + + } + + if (ilits->wrapper(&cmd[1], (sizeof(cmd) - 1), NULL, 0, ON, OFF)) { + ILI_ERR("Write gesture symbol fail\n"); + return; + } + + ILI_DBG(" double_tap = %d\n", ilits->ges_sym.double_tap); + ILI_DBG(" alphabet_line_2_top = %d\n", ilits->ges_sym.alphabet_line_2_top); + ILI_DBG(" alphabet_line_2_bottom = %d\n", ilits->ges_sym.alphabet_line_2_bottom); + ILI_DBG(" alphabet_line_2_left = %d\n", ilits->ges_sym.alphabet_line_2_left); + ILI_DBG(" alphabet_line_2_right = %d\n", ilits->ges_sym.alphabet_line_2_right); + ILI_DBG(" alphabet_w = %d\n", ilits->ges_sym.alphabet_w); + ILI_DBG(" alphabet_c = %d\n", ilits->ges_sym.alphabet_c); + ILI_DBG(" alphabet_E = %d\n", ilits->ges_sym.alphabet_E); + ILI_DBG(" alphabet_V = %d\n", ilits->ges_sym.alphabet_V); + ILI_DBG(" alphabet_O = %d\n", ilits->ges_sym.alphabet_O); + ILI_DBG(" alphabet_S = %d\n", ilits->ges_sym.alphabet_S); + ILI_DBG(" alphabet_Z = %d\n", ilits->ges_sym.alphabet_Z); + ILI_DBG(" alphabet_V_down = %d\n", ilits->ges_sym.alphabet_V_down); + ILI_DBG(" alphabet_V_left = %d\n", ilits->ges_sym.alphabet_V_left); + ILI_DBG(" alphabet_V_right = %d\n", ilits->ges_sym.alphabet_V_right); + ILI_DBG(" alphabet_two_line_2_bottom= %d\n", ilits->ges_sym.alphabet_two_line_2_bottom); + ILI_DBG(" alphabet_F= %d\n", ilits->ges_sym.alphabet_F); + ILI_DBG(" alphabet_AT= %d\n", ilits->ges_sym.alphabet_AT); + +} + +int ili_move_gesture_code_iram(int mode) +{ + int i, ret = 0, timeout = 10; + u8 cmd[2] = {0}; + u8 cmd_write[3] = {0x01,0x0A,0x05}; + + /* + * NOTE: If functions need to be added during suspend, + * they must be called before gesture cmd reaches to FW. + */ + + ILI_INFO("Gesture code loaded by %s\n", ilits->gesture_load_code ? "driver" : "firmware" ); + + if (!ilits->gesture_load_code) { + ret = ili_set_tp_data_len(mode, true, NULL); + goto out; + } + + /*pre-command for ili_ic_func_ctrl("lpwg", 0x3)*/ + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = 0x1; + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write 0xF6,0x1 failed\n"); + goto out; + } + + ret = ili_ic_func_ctrl("lpwg", 0x3); + if (ret < 0) { + ILI_ERR("write gesture flag failed\n"); + goto out; + } + + ret = ili_set_tp_data_len(mode, true, NULL); + if (ret < 0) { + ILI_ERR("Failed to set tp data length\n"); + goto out; + } + +// for (i = 0; i < timeout; i++) { + /* Prepare Check Ready */ + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = 0xA; + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write 0xF6,0xA failed\n"); + goto out; + } + + for (i = 0; i < timeout; i++) { + /* Check ready for load code */ + ret = ilits->wrapper(cmd_write, sizeof(cmd_write), cmd, sizeof(u8), ON, OFF); + ILI_DBG("gesture ready byte = 0x%x\n", cmd[0]); + + if (cmd[0] == 0x91) { + ILI_INFO("Ready to load gesture code\n"); + break; + } + } + + if (i >= timeout) { + ILI_ERR("Gesture is not ready (0x%x), try to run its recovery\n", cmd[0]); + return ili_gesture_recovery(); + } + + ret = ili_fw_upgrade_handler(NULL); + if (ret < 0) { + ILI_ERR("FW upgrade failed during moving code\n"); + goto out; + } + + /* Resume gesture loader */ + ret = ili_ic_func_ctrl("lpwg", 0x6); + if (ret < 0) { + ILI_ERR("write resume loader error"); + goto out; + } + +out: + return ret; +} + +u8 ili_calc_packet_checksum(u8 *packet, int len) +{ + int i; + s32 sum = 0; + + for (i = 0; i < len; i++) + sum += packet[i]; + + return (u8) ((-sum) & 0xFF); +} + +int ili_touch_esd_gesture_flash(void) +{ + int ret = 0, retry = 100; + u32 answer = 0; + int ges_pwd_addr = I2C_ESD_GESTURE_CORE146_PWD_ADDR; + int ges_pwd = ESD_GESTURE_CORE146_PWD; + int ges_run = I2C_ESD_GESTURE_CORE146_RUN; + int pwd_len = 2; + + if (ilits->chip->core_ver < CORE_VER_1460) { + ges_pwd_addr = I2C_ESD_GESTURE_PWD_ADDR; + ges_pwd = ESD_GESTURE_PWD; + ges_run = I2C_ESD_GESTURE_RUN; + pwd_len = 4; + } + + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) { + ILI_ERR("Enable ice mode failed during gesture recovery\n"); + return ret; + } + + ILI_INFO("ESD Gesture PWD Addr = 0x%X, PWD = 0x%X, GES_RUN = 0%X, core_ver = 0x%X\n", + ges_pwd_addr, ges_pwd, ges_run, ilits->chip->core_ver); + + /* write a special password to inform FW go back into gesture mode */ + ret = ili_ice_mode_write(ges_pwd_addr, ges_pwd, pwd_len); + if (ret < 0) { + ILI_ERR("write password failed\n"); + goto out; + } + + /* HW reset gives effect to FW receives password successed */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + ret = ili_reset_ctrl(ilits->reset); + if (ret < 0) { + ILI_ERR("TP Reset failed during gesture recovery\n"); + goto out; + } + + ret = ili_ice_mode_ctrl(ENABLE, ON); + if (ret < 0) { + ILI_ERR("Enable ice mode failed during gesture recovery\n"); + goto out; + } + + /* polling another specific register to see if gesutre is enabled properly */ + do { + ret = ili_ice_mode_read(ges_pwd_addr, &answer, pwd_len); + if (ret < 0) { + ILI_ERR("Read gesture answer error\n"); + goto out; + } + + if (answer != ges_run) + ILI_INFO("ret = 0x%X, answer = 0x%X\n", answer, ges_run); + + mdelay(1); + } while (answer != ges_run && --retry > 0); + + if (retry <= 0) { + ILI_ERR("Enter gesture failed\n"); + ret = -1; + goto out; + } + + ILI_INFO("0x%X Enter gesture successfully\n", answer); + +out: + if (ili_ice_mode_ctrl(DISABLE, ON) < 0) { + ILI_ERR("Disable ice mode failed during gesture recovery\n"); + } + + if (ret >= 0) { + ilits->actual_tp_mode = P5_X_FW_GESTURE_MODE; + ili_set_tp_data_len(ilits->gesture_mode, false, NULL); + } + + return ret; +} + +int ili_touch_esd_gesture_iram(void) +{ + int ret = 0, retry = 100; + u32 answer = 0; + int ges_pwd_addr = SPI_ESD_GESTURE_CORE146_PWD_ADDR; + int ges_pwd = ESD_GESTURE_CORE146_PWD; + int ges_run = SPI_ESD_GESTURE_CORE146_RUN; + int pwd_len = 2; + + if (ilits->chip->core_ver < CORE_VER_1460) { + if (ilits->chip->core_ver >= CORE_VER_1420) + ges_pwd_addr = I2C_ESD_GESTURE_PWD_ADDR; + else + ges_pwd_addr = SPI_ESD_GESTURE_PWD_ADDR; + ges_pwd = ESD_GESTURE_PWD; + ges_run = SPI_ESD_GESTURE_RUN; + pwd_len = 4; + } + + ILI_INFO("ESD Gesture PWD Addr = 0x%X, PWD = 0x%X, GES_RUN = 0%X, core_ver = 0x%X\n", + ges_pwd_addr, ges_pwd, ges_run, ilits->chip->core_ver); + + ret = ili_ice_mode_ctrl(ENABLE, OFF); + if (ret < 0) { + ILI_ERR("Enable ice mode failed during gesture recovery\n"); + goto fail; + } + + /* write a special password to inform FW go back into gesture mode */ + ret = ili_ice_mode_write(ges_pwd_addr, ges_pwd, pwd_len); + if (ret < 0) { + ILI_ERR("write password failed\n"); + goto fail; + } + + /* Host download gives effect to FW receives password successed */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + ret = ili_fw_upgrade_handler(NULL); + if (ret < 0) { + ILI_ERR("FW upgrade failed during gesture recovery\n"); + goto fail; + } + + /* Wait for fw running code finished. */ + if (ilits->info_from_hex || (ilits->chip->core_ver >= CORE_VER_1410)) + msleep(50); + + + ret = ili_ice_mode_ctrl(ENABLE, ON); + if (ret < 0) { + ILI_ERR("Enable ice mode failed during gesture recovery\n"); + goto fail; + } + + /* polling another specific register to see if gesutre is enabled properly */ + do { + ret = ili_ice_mode_read(ges_pwd_addr, &answer, pwd_len); + if (ret < 0) { + ILI_ERR("Read gesture answer error\n"); + goto fail; + } + + if (answer != ges_run) + ILI_INFO("ret = 0x%X, answer = 0x%X\n", answer, ges_run); + + mdelay(1); + } while (answer != ges_run && --retry > 0); + + if (retry <= 0) { + ILI_ERR("Enter gesture failed\n"); + ret = -1; + goto fail; + } + + ILI_INFO("Enter gesture successfully\n"); + + ret = ili_ice_mode_ctrl(DISABLE, ON); + if (ret < 0) { + ILI_ERR("Disable ice mode failed during gesture recovery\n"); + goto fail; + } + + ILI_INFO("Gesture code loaded by %s\n", ilits->gesture_load_code ? "driver" : "firmware"); + + if (!ilits->gesture_load_code) { + ilits->actual_tp_mode = P5_X_FW_GESTURE_MODE; + ili_set_tp_data_len(ilits->gesture_mode, false, NULL); + goto out; + } + + /* Load gesture code */ + ilits->actual_tp_mode = P5_X_FW_GESTURE_MODE; + ili_set_tp_data_len(ilits->gesture_mode, false, NULL); + ret = ili_fw_upgrade_handler(NULL); + if (ret < 0) { + ILI_ERR("Failed to load code during gesture recovery\n"); + goto fail; + } + + /* Resume gesture loader */ + ret = ili_ic_func_ctrl("lpwg", 0x6); + if (ret < 0) { + ILI_ERR("write resume loader error"); + goto fail; + } + +out: + return ret; + +fail: + ili_reset_ctrl(TP_HW_RST_ONLY); + ilits->actual_tp_mode = P5_X_FW_GESTURE_MODE; + return ret; +} + +void ili_demo_debug_info_id0(u8 *buf, size_t len) +{ + struct ili_demo_debug_info_id0 id0; + + ipio_memcpy(&id0, buf, sizeof(id0), len); + ILI_INFO("id0 len = %d,strucy len = %d", (int)len, (int)sizeof(id0)); + + ILI_INFO("id = %d\n", id0.id); + ILI_INFO("app_sys_powr_state_e = %d\n", id0.sys_powr_state_e); + ILI_INFO("app_sys_state_e = %d\n", id0.sys_state_e); + ILI_INFO("tp_state_e = %d\n", id0.tp_state_e); + + ILI_INFO("touch_palm_state_e = %d\n", id0.touch_palm_state); + ILI_INFO("app_an_statu_e = %d\n", id0.app_an_statu_e); + ILI_INFO("app_sys_bg_err = %d\n", id0.app_sys_bg_err); + ILI_INFO("g_b_wrong_bg = %d\n", id0.g_b_wrong_bg); + ILI_INFO("reserved0 = %d\n", id0.reserved0); + + ILI_INFO("normal_mode = %d\n", id0.normal_mode); + ILI_INFO("charger_mode = %d\n", id0.charger_mode); + ILI_INFO("glove_mode = %d\n", id0.glove_mode); + ILI_INFO("stylus_mode = %d\n", id0.stylus_mode); + ILI_INFO("multi_mode = %d\n", id0.multi_mode); + ILI_INFO("noise_mode = %d\n", id0.noise_mode); + ILI_INFO("palm_plus_mode = %d\n", id0.palm_plus_mode); + ILI_INFO("floating_mode = %d\n", id0.floating_mode); + + ILI_INFO("algo_pt_status0 = %d\n", id0.algo_pt_status0); + ILI_INFO("algo_pt_status1 = %d\n", id0.algo_pt_status1); + ILI_INFO("algo_pt_status2 = %d\n", id0.algo_pt_status2); + ILI_INFO("algo_pt_status3 = %d\n", id0.algo_pt_status3); + ILI_INFO("algo_pt_status4 = %d\n", id0.algo_pt_status4); + ILI_INFO("algo_pt_status5 = %d\n", id0.algo_pt_status5); + ILI_INFO("algo_pt_status6 = %d\n", id0.algo_pt_status6); + ILI_INFO("algo_pt_status7 = %d\n", id0.algo_pt_status7); + ILI_INFO("algo_pt_status8 = %d\n", id0.algo_pt_status8); + ILI_INFO("algo_pt_status9 = %d\n", id0.algo_pt_status9); + ILI_INFO("reserved2 = %d\n", id0.reserved2); + + ILI_INFO("hopping_flg = %d\n", id0.hopping_flg); + ILI_INFO("hopping_index = %d\n", id0.hopping_index); + ILI_INFO("frequency = %d\n", (id0.frequency_h << 8 | id0.frequency_l)); + ILI_INFO("reserved3 = %d\n", id0.reserved3); + ILI_INFO("reserved4 = %d\n", id0.reserved4); + +} + +void ili_demo_debug_info_mode(u8 *buf, size_t len) +{ + u8 *info_ptr; + u8 info_id, info_len; + + ili_report_ap_mode(buf, P5_X_DEMO_MODE_PACKET_LEN); + info_ptr = buf + P5_X_DEMO_MODE_PACKET_LEN; + info_len = info_ptr[0]; + info_id = info_ptr[1]; + + ILI_INFO("info len = %d ,id = %d\n", info_len, info_id); + + ilits->demo_debug_info[info_id](&info_ptr[1] , info_len); +} + +static void ilitek_tddi_touch_send_debug_data(u8 *buf, int len) +{ + int index; + + mutex_lock(&ilits->debug_mutex); + + if (!ilits->netlink && !ilits->dnp) + goto out; + + /* Send data to netlink */ + if (ilits->netlink) { + ili_netlink_reply_msg(buf, len); + goto out; + } + + /* Sending data to apk via the node of debug_message node */ + if (ilits->dnp) { + index = ilits->dbf; + if (!ilits->dbl[ilits->dbf].mark) { + ilits->dbf = ((ilits->dbf + 1) % TR_BUF_LIST_SIZE); + } else { + if (ilits->dbf == 0) + index = TR_BUF_LIST_SIZE - 1; + else + index = ilits->dbf - 1; + } + if (ilits->dbl[index].data == NULL) { + ILI_ERR("BUFFER %d error\n", index); + goto out; + } + ipio_memcpy(ilits->dbl[index].data, buf, len, 2048); + ilits->dbl[index].mark = true; + wake_up(&(ilits->inq)); + goto out; + } + +out: + mutex_unlock(&ilits->debug_mutex); +} + +void ili_touch_press(u16 x, u16 y, u16 pressure, u16 id) +{ + ILI_DBG("Touch Press: id = %d, x = %d, y = %d, p = %d\n", id, x, y, pressure); + + if (MT_B_TYPE) { + input_mt_slot(ilits->input, id); + input_mt_report_slot_state(ilits->input, MT_TOOL_FINGER, true); + input_report_abs(ilits->input, ABS_MT_POSITION_X, x); + input_report_abs(ilits->input, ABS_MT_POSITION_Y, y); + if (MT_PRESSURE) + input_report_abs(ilits->input, ABS_MT_PRESSURE, pressure); + } else { + input_report_key(ilits->input, BTN_TOUCH, 1); + input_report_abs(ilits->input, ABS_MT_TRACKING_ID, id); + input_report_abs(ilits->input, ABS_MT_TOUCH_MAJOR, 1); + input_report_abs(ilits->input, ABS_MT_WIDTH_MAJOR, 1); + input_report_abs(ilits->input, ABS_MT_POSITION_X, x); + input_report_abs(ilits->input, ABS_MT_POSITION_Y, y); + if (MT_PRESSURE) + input_report_abs(ilits->input, ABS_MT_PRESSURE, pressure); + + input_mt_sync(ilits->input); + } +} + +void ili_touch_release(u16 x, u16 y, u16 id) +{ + ILI_DBG("Touch Release: id = %d, x = %d, y = %d\n", id, x, y); + + if (MT_B_TYPE) { + input_mt_slot(ilits->input, id); + input_mt_report_slot_state(ilits->input, MT_TOOL_FINGER, false); + } else { + input_report_key(ilits->input, BTN_TOUCH, 0); + input_mt_sync(ilits->input); + } +} + +void ili_touch_release_all_point(void) +{ + int i; + + if (MT_B_TYPE) { + for (i = 0 ; i < MAX_TOUCH_NUM; i++) + ili_touch_release(0, 0, i); + + input_report_key(ilits->input, BTN_TOUCH, 0); + input_report_key(ilits->input, BTN_TOOL_FINGER, 0); + } else { + ili_touch_release(0, 0, 0); + } + input_sync(ilits->input); +} + +static struct ilitek_touch_info touch_info[MAX_TOUCH_NUM]; + +void ili_report_ap_mode(u8 *buf, int len) +{ + int i = 0; + u32 xop = 0, yop = 0; + + memset(touch_info, 0x0, sizeof(touch_info)); + + ilits->finger = 0; + + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if ((buf[(4 * i) + 1] == 0xFF) && (buf[(4 * i) + 2] == 0xFF) + && (buf[(4 * i) + 3] == 0xFF)) { + if (MT_B_TYPE) + ilits->curt_touch[i] = 0; + continue; + } + + xop = (((buf[(4 * i) + 1] & 0xF0) << 4) | (buf[(4 * i) + 2])); + yop = (((buf[(4 * i) + 1] & 0x0F) << 8) | (buf[(4 * i) + 3])); + + if (ilits->trans_xy) { + touch_info[ilits->finger].x = xop; + touch_info[ilits->finger].y = yop; + } else { + touch_info[ilits->finger].x = xop * ilits->panel_wid / TPD_WIDTH; + touch_info[ilits->finger].y = yop * ilits->panel_hei / TPD_HEIGHT; + } + + touch_info[ilits->finger].id = i; + + if (MT_PRESSURE) + touch_info[ilits->finger].pressure = buf[(4 * i) + 4]; + else + touch_info[ilits->finger].pressure = 1; + + ILI_DBG("original x = %d, y = %d\n", xop, yop); + ilits->finger++; + if (MT_B_TYPE) + ilits->curt_touch[i] = 1; + } + + ILI_DBG("figner number = %d, LastTouch = %d\n", ilits->finger, ilits->last_touch); + + if (ilits->finger) { + if (MT_B_TYPE) { + for (i = 0; i < ilits->finger; i++) { + input_report_key(ilits->input, BTN_TOUCH, 1); + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + input_report_key(ilits->input, BTN_TOOL_FINGER, 1); + } + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) + ili_touch_release(0, 0, i); + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + } else { + for (i = 0; i < ilits->finger; i++) + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + } + input_sync(ilits->input); + ilits->last_touch = ilits->finger; + } else { + if (ilits->last_touch) { + if (MT_B_TYPE) { + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) + ili_touch_release(0, 0, i); + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + input_report_key(ilits->input, BTN_TOUCH, 0); + input_report_key(ilits->input, BTN_TOOL_FINGER, 0); + } else { + ili_touch_release(0, 0, 0); + } + input_sync(ilits->input); + ilits->last_touch = 0; + } + } + ilitek_tddi_touch_send_debug_data(buf, len); +} + +void ili_debug_mode_report_point(u8 *buf, int len) +{ + int i = 0; + u32 xop = 0, yop = 0; + static u8 p[MAX_TOUCH_NUM]; + + memset(touch_info, 0x0, sizeof(touch_info)); + + ilits->finger = 0; + + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if ((buf[(3 * i)] == 0xFF) && (buf[(3 * i) + 1] == 0xFF) + && (buf[(3 * i) + 2] == 0xFF)) { + if (MT_B_TYPE) + ilits->curt_touch[i] = 0; + continue; + } + + xop = (((buf[(3 * i)] & 0xF0) << 4) | (buf[(3 * i) + 1])); + yop = (((buf[(3 * i)] & 0x0F) << 8) | (buf[(3 * i) + 2])); + + if (ilits->trans_xy) { + touch_info[ilits->finger].x = xop; + touch_info[ilits->finger].y = yop; + } else { + touch_info[ilits->finger].x = xop * ilits->panel_wid / TPD_WIDTH; + touch_info[ilits->finger].y = yop * ilits->panel_hei / TPD_HEIGHT; + } + + touch_info[ilits->finger].id = i; + + if (MT_PRESSURE) { + /* + * Since there's no pressure data in debug mode, we make fake values + * for android system if pressure needs to be reported. + */ + if (p[ilits->finger] == 1) + touch_info[ilits->finger].pressure = p[ilits->finger] = 2; + else + touch_info[ilits->finger].pressure = p[ilits->finger] = 1; + } else { + touch_info[ilits->finger].pressure = 1; + } + + ILI_DBG("original x = %d, y = %d\n", xop, yop); + ilits->finger++; + if (MT_B_TYPE) + ilits->curt_touch[i] = 1; + } + + ILI_DBG("figner number = %d, LastTouch = %d\n", ilits->finger, ilits->last_touch); + + if (ilits->finger) { + if (MT_B_TYPE) { + for (i = 0; i < ilits->finger; i++) { + input_report_key(ilits->input, BTN_TOUCH, 1); + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + input_report_key(ilits->input, BTN_TOOL_FINGER, 1); + } + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) + ili_touch_release(0, 0, i); + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + } else { + for (i = 0; i < ilits->finger; i++) + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + } + input_sync(ilits->input); + ilits->last_touch = ilits->finger; + } else { + if (ilits->last_touch) { + if (MT_B_TYPE) { + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) + ili_touch_release(0, 0, i); + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + input_report_key(ilits->input, BTN_TOUCH, 0); + input_report_key(ilits->input, BTN_TOOL_FINGER, 0); + } else { + ili_touch_release(0, 0, 0); + } + input_sync(ilits->input); + ilits->last_touch = 0; + } + } +} + +void ili_report_debug_mode(u8 *buf, int len) +{ + ili_debug_mode_report_point(buf + 5, len); + + ilitek_tddi_touch_send_debug_data(buf, len); +} + +void ili_report_debug_lite_mode(u8 *buf, int len) +{ + ili_debug_mode_report_point(buf + 4, len); + + ilitek_tddi_touch_send_debug_data(buf, len); +} + +void ili_report_gesture_mode(u8 *buf, int len) +{ + int i, lu_x = 0, lu_y = 0, rd_x = 0, rd_y = 0, score = 0; + u8 ges[P5_X_GESTURE_INFO_LENGTH] = {0}; + struct gesture_coordinate *gc = ilits->gcoord; +#ifndef ILI_SENSOR_EN + struct input_dev *input = ilits->input; +#endif + bool transfer = ilits->trans_xy; +#ifdef ILI_SENSOR_EN + static int report_cnt = 0; +#endif + + for (i = 0; i < len; i++) + ges[i] = buf[i]; + + memset(gc, 0x0, sizeof(struct gesture_coordinate)); + + gc->code = ges[1]; + score = ges[36]; + ILI_INFO("gesture code = 0x%x, score = %d\n", gc->code, score); + + /* Parsing gesture coordinate */ + gc->pos_start.x = ((ges[4] & 0xF0) << 4) | ges[5]; + gc->pos_start.y = ((ges[4] & 0x0F) << 8) | ges[6]; + gc->pos_end.x = ((ges[7] & 0xF0) << 4) | ges[8]; + gc->pos_end.y = ((ges[7] & 0x0F) << 8) | ges[9]; + gc->pos_1st.x = ((ges[16] & 0xF0) << 4) | ges[17]; + gc->pos_1st.y = ((ges[16] & 0x0F) << 8) | ges[18]; + gc->pos_2nd.x = ((ges[19] & 0xF0) << 4) | ges[20]; + gc->pos_2nd.y = ((ges[19] & 0x0F) << 8) | ges[21]; + gc->pos_3rd.x = ((ges[22] & 0xF0) << 4) | ges[23]; + gc->pos_3rd.y = ((ges[22] & 0x0F) << 8) | ges[24]; + gc->pos_4th.x = ((ges[25] & 0xF0) << 4) | ges[26]; + gc->pos_4th.y = ((ges[25] & 0x0F) << 8) | ges[27]; + + switch (gc->code) { + case GESTURE_DOUBLECLICK: + ILI_INFO("Double Click key event\n"); +#ifdef ILI_SENSOR_EN + if (!(ilits->wakeable && ilits->should_enable_gesture)) { + ILI_INFO("Gesture got but wakeable not set. Skip this gesture."); + return; + } + if (ilits->report_gesture_key) { + input_report_key(ilits->sensor_pdata->input_sensor_dev, KEY_F1, 1); + input_sync(ilits->sensor_pdata->input_sensor_dev); + input_report_key(ilits->sensor_pdata->input_sensor_dev, KEY_F1, 0); + input_sync(ilits->sensor_pdata->input_sensor_dev); + ++report_cnt; + } else { + input_report_abs(ilits->sensor_pdata->input_sensor_dev, + ABS_DISTANCE, + ++report_cnt); + input_sync(ilits->sensor_pdata->input_sensor_dev); + } + ILI_INFO("input report: %d", report_cnt); + if (report_cnt >= REPORT_MAX_COUNT) { + report_cnt = 0; + } +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&(ilits->gesture_wakelock), msecs_to_jiffies(5000)); +#else + PM_WAKEUP_EVENT(ilits->gesture_wakelock, 5000); +#endif +#else + input_report_key(input, KEY_GESTURE_POWER, 1); + input_sync(input); + input_report_key(input, KEY_GESTURE_POWER, 0); + input_sync(input); +#endif + gc->type = GESTURE_DOUBLECLICK; + gc->clockwise = 1; + gc->pos_end.x = gc->pos_start.x; + gc->pos_end.y = gc->pos_start.y; + break; + case GESTURE_LEFT: + gc->type = GESTURE_LEFT; + gc->clockwise = 1; + break; + case GESTURE_RIGHT: + gc->type = GESTURE_RIGHT; + gc->clockwise = 1; + break; + case GESTURE_UP: + gc->type = GESTURE_UP; + gc->clockwise = 1; + break; + case GESTURE_DOWN: + gc->type = GESTURE_DOWN; + gc->clockwise = 1; + break; + case GESTURE_O: + gc->type = GESTURE_O; + gc->clockwise = (ges[34] > 1) ? 0 : ges[34]; + + lu_x = (((ges[28] & 0xF0) << 4) | (ges[29])); + lu_y = (((ges[28] & 0x0F) << 8) | (ges[30])); + rd_x = (((ges[31] & 0xF0) << 4) | (ges[32])); + rd_y = (((ges[31] & 0x0F) << 8) | (ges[33])); + + gc->pos_1st.x = ((rd_x + lu_x) / 2); + gc->pos_1st.y = lu_y; + gc->pos_2nd.x = lu_x; + gc->pos_2nd.y = ((rd_y + lu_y) / 2); + gc->pos_3rd.x = ((rd_x + lu_x) / 2); + gc->pos_3rd.y = rd_y; + gc->pos_4th.x = rd_x; + gc->pos_4th.y = ((rd_y + lu_y) / 2); + break; + case GESTURE_W: + gc->type = GESTURE_W; + gc->clockwise = 1; + break; + case GESTURE_M: + gc->type = GESTURE_M; + gc->clockwise = 1; + break; + case GESTURE_V: + gc->type = GESTURE_V; + gc->clockwise = 1; + break; + case GESTURE_C: + gc->type = GESTURE_C; + gc->clockwise = 1; + break; + case GESTURE_E: + gc->type = GESTURE_E; + gc->clockwise = 1; + break; + case GESTURE_S: + gc->type = GESTURE_S; + gc->clockwise = 1; + break; + case GESTURE_Z: + gc->type = GESTURE_Z; + gc->clockwise = 1; + break; + case GESTURE_TWOLINE_DOWN: + gc->type = GESTURE_TWOLINE_DOWN; + gc->clockwise = 1; + gc->pos_1st.x = (((ges[10] & 0xF0) << 4) | (ges[11])); + gc->pos_1st.y = (((ges[10] & 0x0F) << 8) | (ges[12])); + gc->pos_2nd.x = (((ges[13] & 0xF0) << 4) | (ges[14])); + gc->pos_2nd.y = (((ges[13] & 0x0F) << 8) | (ges[15])); + break; + default: + ILI_ERR("Unknown gesture code\n"); + break; + } + + if (!transfer) { + gc->pos_start.x = gc->pos_start.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_start.y = gc->pos_start.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_end.x = gc->pos_end.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_end.y = gc->pos_end.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_1st.x = gc->pos_1st.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_1st.y = gc->pos_1st.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_2nd.x = gc->pos_2nd.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_2nd.y = gc->pos_2nd.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_3rd.x = gc->pos_3rd.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_3rd.y = gc->pos_3rd.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_4th.x = gc->pos_4th.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_4th.y = gc->pos_4th.y * ilits->panel_hei / TPD_HEIGHT; + } + + ILI_INFO("Transfer = %d, Type = %d, clockwise = %d\n", transfer, gc->type, gc->clockwise); + ILI_INFO("Gesture Points: (%d, %d)(%d, %d)(%d, %d)(%d, %d)(%d, %d)(%d, %d)\n", + gc->pos_start.x, gc->pos_start.y, + gc->pos_end.x, gc->pos_end.y, + gc->pos_1st.x, gc->pos_1st.y, + gc->pos_2nd.x, gc->pos_2nd.y, + gc->pos_3rd.x, gc->pos_3rd.y, + gc->pos_4th.x, gc->pos_4th.y); + + ilitek_tddi_touch_send_debug_data(buf, len); +} + +void ili_report_i2cuart_mode(u8 *buf, int len) +{ + int type = buf[3] & 0x0F; + int need_read_len = 0, one_data_bytes = 0; + int actual_len = len - 5; + int uart_len; + u8 *uart_buf = NULL, *total_buf = NULL; + + ILI_DBG("data[3] = %x, type = %x, actual_len = %d\n", + buf[3], type, actual_len); + + need_read_len = buf[1] * buf[2]; + + if (type == 0 || type == 1 || type == 6) { + one_data_bytes = 1; + } else if (type == 2 || type == 3) { + one_data_bytes = 2; + } else if (type == 4 || type == 5) { + one_data_bytes = 4; + } + + need_read_len = need_read_len * one_data_bytes + 1; + ILI_DBG("need_read_len = %d one_data_bytes = %d\n", need_read_len, one_data_bytes); + + if (need_read_len <= actual_len) { + ilitek_tddi_touch_send_debug_data(buf, len); + goto out; + } + + uart_len = need_read_len - actual_len; + ILI_DBG("uart len = %d\n", uart_len); + + uart_buf = kcalloc(uart_len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(uart_buf)) { + ILI_ERR("Failed to allocate uart_buf memory %ld\n", PTR_ERR(uart_buf)); + goto out; + } + + if (ilits->wrapper(NULL, 0, uart_buf, uart_len, OFF, OFF) < 0) { + ILI_ERR("i2cuart read data failed\n"); + goto out; + } + + total_buf = kcalloc(len + uart_len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(total_buf)) { + ILI_ERR("Failed to allocate total_buf memory %ld\n", PTR_ERR(total_buf)); + goto out; + } + + memcpy(total_buf, buf, len); + memcpy(total_buf + len, uart_buf, uart_len); + ilitek_tddi_touch_send_debug_data(total_buf, len + uart_len); + +out: + ipio_kfree((void **)&uart_buf); + ipio_kfree((void **)&total_buf); + return; +} diff --git a/drivers/input/touchscreen/ilitek_mmi/Android.mk b/drivers/input/touchscreen/ilitek_mmi/Android.mk new file mode 100644 index 000000000000..c7f78ffc759c --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := ilitek_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/ilitek_mmi/Kbuild b/drivers/input/touchscreen/ilitek_mmi/Kbuild new file mode 100644 index 000000000000..d20949dbf8a8 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/Kbuild @@ -0,0 +1,20 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/ilitek_mmi + +ifeq ($(filter m y, $(CONFIG_TOUCHSCREEN_ILITEK_CHIPSET)),) + obj-m += ilitek_mmi.o +endif + +ilitek_mmi-objs += platform.o userspace.o +ilitek_mmi-objs += core/config.o \ + core/finger_report.o \ + core/firmware.o \ + core/flash.o \ + core/i2c.o \ + core/spi.o \ + core/mp_test.o \ + core/protocol.o \ + core/parser.o \ + core/gesture.o diff --git a/drivers/input/touchscreen/ilitek_mmi/Makefile b/drivers/input/touchscreen/ilitek_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/ilitek_mmi/common.h b/drivers/input/touchscreen/ilitek_mmi/common.h new file mode 100644 index 000000000000..f3ec43e8193a --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/common.h @@ -0,0 +1,262 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __COMMON_H +#define __COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_OF +#include +#include +#include +#include +#include +#endif + +#ifdef CONFIG_FB +#include +#include +#else +#include +#endif + +/* + * Relative Driver with Touch IC + */ + +/* An Touch IC currently supported by driver */ +#define CHIP_TYPE_ILI7807 0x7807 +#define CHIP_TYPE_ILI9881 0x9881 + +#define CHIP_ID_ERR (-100) + +/* A interface currently supported by driver */ +#define I2C_INTERFACE 1 +#define SPI_INTERFACE 2 +#define INTERFACE I2C_INTERFACE + +/* Driver version */ +#define DRIVER_VERSION "1.0.3.5" + +/* Driver core type */ +#define CORE_TYPE_B 0x00 +#define CORE_TYPE_E 0x03 + +/* Protocol version */ +#define PROTOCOL_MAJOR 0x5 +#define PROTOCOL_MID 0x4 +#define PROTOCOL_MINOR 0x0 + +/* Debug messages */ +#ifdef BIT +#undef BIT +#endif +#define BIT(x) (1 << (x)) + +enum { + DEBUG_NONE = 0, + DEBUG_IRQ = BIT(0), + DEBUG_FINGER_REPORT = BIT(1), + DEBUG_FIRMWARE = BIT(2), + DEBUG_CONFIG = BIT(3), + DEBUG_I2C = BIT(4), + DEBUG_BATTERY = BIT(5), + DEBUG_MP_TEST = BIT(6), + DEBUG_IOCTL = BIT(7), + DEBUG_NETLINK = BIT(8), + DEBUG_PARSER = BIT(9), + DEBUG_GESTURE = BIT(10), + DEBUG_BOOT = BIT(11), + DEBUG_ALL = ~0, +}; + +#define ipio_info(fmt, arg...) \ + pr_info("ILITEK: (%s, %d): " fmt, __func__, __LINE__, ##arg) + +#define ipio_err(fmt, arg...) \ + pr_err("ILITEK: (%s, %d): " fmt, __func__, __LINE__, ##arg) + +#define ipio_debug(level, fmt, arg...) \ + do { \ + if (level & ipio_debug_level) \ + pr_info("ILITEK: (%s, %d): " fmt, __func__, \ + __LINE__, ##arg); \ + } while (0) + +/* Distributed to all core functions */ +extern uint32_t ipio_debug_level; +extern uint32_t ipio_chip_list[2]; + +/* Macros */ +#define CHECK_EQUAL(X, Y) ((X == Y) ? 0 : -1) +#define ERR_ALLOC_MEM(X) ((IS_ERR(X) || X == NULL) ? 1 : 0) +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define USEC 1 +#define MSEC (USEC * 1000) + +/* The size of firmware upgrade */ +#define MAX_HEX_FILE_SIZE (160*1024) +#define MAX_FLASH_FIRMWARE_SIZE (256*1024) +#define MAX_IRAM_FIRMWARE_SIZE (60*1024) +#define ILI_FILE_HEADER 64 +#define MAX_AP_FIRMWARE_SIZE (64*1024) +#define MAX_DLM_FIRMWARE_SIZE (8*1024) +#define MAX_MP_FIRMWARE_SIZE (64*1024) +#define MAX_GESTURE_FIRMWARE_SIZE (8*1024) +#define DLM_START_ADDRESS 0x20610 +#define DLM_HEX_ADDRESS 0x10000 +#define MP_HEX_ADDRESS 0x13000 +#define SPI_UPGRADE_LEN 2048 +#define FW_BLOCK_INFO_NUM 6 +#define UPDATE_RETRY_COUNT 3 + +/* ILI7807 Series */ +enum ili7881_types { + ILI7807_TYPE_F_AA = 0x0000, + ILI7807_TYPE_F_AB = 0x0001, + ILI7807_TYPE_H = 0x1100 +}; + +#define ILI7807_SLAVE_ADDR 0x41 +#define ILI7807_ICE_MODE_ADDR 0x181062 +#define ILI7807_PID_ADDR 0x4009C +#define ILI7808_WDT_ADDR 0x5100C + +/* ILI9881 Series */ +enum ili9881_types { + ILI9881_TYPE_F = 0x0F, + ILI9881_TYPE_H = 0x11 +}; +/*Error code*/ +#define SUCCESS 0 +#define ERROR (-1) +#define UPDATE_FAIL (-1) +#define CHECK_RECOVER -2 + +#define ILI9881_SLAVE_ADDR 0x41 +#define ILI9881_ICE_MODE_ADDR 0x181062 +#define ILI9881_PID_ADDR 0x4009C +#define ILI9881_WDT_ADDR 0x5100C +#define ILI9881_PC_COUNTER_ADDR 0x44008 + +/* + * Other settings + */ +#define CSV_PATH "/sdcard" +#define INI_NAME_PATH "mp.ini" +#define UPDATE_FW_PATH "/mnt/sdcard/ILITEK_FW" +#define ILITEK_FW_FILENAME "ILITEK_FW.bin" +#define POWER_STATUS_PATH "/sys/class/power_supply/battery/status" +#define CHECK_BATTERY_TIME 2000 +#define VDD_VOLTAGE 1800000 +#define VDD_I2C_VOLTAGE 1800000 + + +/* It's only for spi interface used to download data to iram */ +#if (INTERFACE == SPI_INTERFACE) +#define HOST_DOWNLOAD +#endif + +/* Linux multiple touch protocol, either B type or A type. */ +//#define MT_B_TYPE + +/* Enable the support of regulator power. */ +/* #define REGULATOR_POWER_ON */ + +/* Either an interrupt event handled by kthread or work queue. */ +#define USE_KTHREAD + +/* Enable DMA with I2C. */ +/* #define I2C_DMA */ + +/* Split the length written to or read from IC via I2C. */ +/* #define I2C_SEGMENT */ + +/* Be able to upgrade fw at boot stage */ +/* #define BOOT_FW_UPGRADE */ + +/* Check battery's status in order to avoid some effects from charge. */ +/* #define BATTERY_CHECK */ + +/*suspend TP power off*/ +//#define SUSPEND_TP_POWER_OFF + +static inline void ipio_kfree(void **mem) +{ + if (*mem != NULL) { + kfree(*mem); + *mem = NULL; + } +} + +extern int katoi(char *string); +extern int str2hex(char *str); + +#endif /* __COMMON_H */ diff --git a/drivers/input/touchscreen/ilitek_mmi/core/FW_TDDI_9881H.ili b/drivers/input/touchscreen/ilitek_mmi/core/FW_TDDI_9881H.ili new file mode 100644 index 000000000000..0292a0eb9688 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/FW_TDDI_9881H.ili @@ -0,0 +1,7170 @@ +0x00,0x00,0x00,0x01,0xBF,0xFF,0x95,0xE9,0x29,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x01,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x01,0xC0,0x00, +0x00,0x03,0x00,0x00,0x00,0x00,0xFF,0xFF,0x01,0x00,0x00,0x01,0x1F,0xFF,0x01,0x30,0x00,0x01,0xBF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x48,0x00,0x74,0xFE,0x48,0x00,0x73,0x3E,0x48,0x00,0x73,0x5C,0x48,0x00,0x73,0x7A, +0x48,0x00,0x73,0x98,0x48,0x00,0x73,0xB6,0x48,0x00,0x73,0xD4,0x48,0x00,0x73,0xF2, +0x48,0x00,0x74,0x10,0x48,0x00,0x74,0x66,0x48,0x00,0x74,0x68,0x48,0x00,0x74,0x6A, +0x48,0x00,0x74,0x6C,0x48,0x00,0x74,0x6E,0x48,0x00,0x74,0x70,0x48,0x00,0x74,0x72, +0x48,0x00,0x74,0x74,0x48,0x00,0x74,0x76,0x48,0x00,0x74,0x78,0x48,0x00,0x74,0x7A, +0x48,0x00,0x74,0x7C,0x48,0x00,0x74,0x7E,0x48,0x00,0x74,0x80,0x48,0x00,0x74,0x82, +0x48,0x00,0x74,0x84,0x48,0x00,0x74,0x86,0x48,0x00,0x74,0x88,0x48,0x00,0x74,0x8A, +0x48,0x00,0x74,0x8C,0x48,0x00,0x74,0x8E,0x48,0x00,0x74,0x90,0x48,0x00,0x74,0x92, +0x48,0x00,0x74,0x94,0x48,0x00,0x74,0x96,0x48,0x00,0x74,0x98,0x48,0x00,0x74,0x9A, +0x48,0x00,0x74,0x9C,0x48,0x00,0x74,0x9F,0x48,0x00,0x74,0xA2,0x48,0x00,0x74,0xA5, +0x48,0x00,0x74,0xA8,0xDA,0xEA,0x00,0x00,0xDC,0xEA,0x00,0x00,0xF0,0xEA,0x00,0x00, +0x02,0xEB,0x00,0x00,0x10,0xEB,0x00,0x00,0x28,0xEB,0x00,0x00,0x36,0xEB,0x00,0x00, +0x6E,0xEB,0x00,0x00,0x76,0xEB,0x00,0x00,0x8C,0xEB,0x00,0x00,0xB0,0xEB,0x00,0x00, +0xFA,0xEB,0x00,0x00,0xFC,0xEB,0x00,0x00,0x04,0xEC,0x00,0x00,0x0C,0xEC,0x00,0x00, +0x14,0xEC,0x00,0x00,0x4E,0xEC,0x00,0x00,0xCE,0xEC,0x00,0x00,0x62,0xED,0x00,0x00, +0x78,0xED,0x00,0x00,0xA0,0xEE,0x00,0x00,0xB8,0xEF,0x00,0x00,0x24,0xF0,0x00,0x00, +0x26,0xF0,0x00,0x00,0x28,0xF0,0x00,0x00,0x2A,0xF0,0x00,0x00,0x2C,0xF0,0x00,0x00, +0x2E,0xF0,0x00,0x00,0x30,0xF0,0x00,0x00,0x32,0xF0,0x00,0x00,0x34,0xF0,0x00,0x00, +0x36,0xF0,0x00,0x00,0xE6,0x28,0xE8,0x4C,0x96,0x06,0x46,0x20,0x00,0x81,0x44,0xF0, +0x01,0x3C,0x38,0x17,0x84,0x00,0x40,0xF0,0xBC,0x00,0xDD,0x0F,0x08,0x16,0x24,0x32, +0x40,0x4E,0x5C,0x6A,0x40,0x00,0x40,0x08,0x04,0x31,0x00,0x29,0x44,0x1E,0xFF,0xFF, +0xD5,0x33,0x40,0x00,0x44,0x08,0x04,0x31,0x00,0x29,0x44,0x1D,0xFF,0xFF,0xD5,0x2C, +0x40,0x00,0x48,0x08,0x04,0x31,0x00,0x29,0x44,0x1B,0xFF,0xFF,0xD5,0x25,0x40,0x00, +0x4C,0x08,0x04,0x31,0x00,0x29,0x46,0x1F,0xFF,0x7F,0xD5,0x1C,0x40,0x00,0x50,0x08, +0x04,0x31,0x00,0x29,0x46,0x1F,0xFE,0xFF,0xD5,0x15,0x40,0x00,0x54,0x08,0x04,0x31, +0x00,0x29,0x46,0x1F,0xFD,0xFF,0xD5,0x0E,0x40,0x00,0x58,0x08,0x04,0x31,0x00,0x29, +0x46,0x1F,0xFB,0xFF,0xD5,0x07,0x40,0x00,0x5C,0x08,0x04,0x31,0x00,0x29,0x46,0x1F, +0xF7,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x29,0xDD,0x9E, +0x44,0x24,0x80,0x90,0x96,0x06,0xB4,0x62,0x40,0x10,0x20,0x08,0x44,0x0F,0xFE,0xFF, +0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x46,0x10,0x00,0x82,0xA0,0x0A,0x58,0x00, +0x00,0x01,0xA8,0x0A,0xDD,0x9E,0x46,0x20,0x00,0x82,0x96,0x06,0xA0,0xD5,0x40,0x10, +0x40,0x08,0x44,0x0E,0xFF,0xFF,0xFE,0x1E,0xFE,0x0F,0xA8,0x15,0xDD,0x9E,0x46,0x00, +0x00,0x82,0xA0,0x05,0x92,0x10,0x96,0x06,0xDD,0x9E,0xFC,0x40,0x46,0x60,0x00,0x81, +0x96,0x2F,0x40,0x90,0x20,0x08,0x04,0x03,0x00,0x2A,0x44,0x7F,0xC0,0xFF,0xFE,0x3E, +0x40,0x00,0x24,0x04,0x14,0x03,0x00,0x2A,0x96,0x6F,0x40,0x90,0xC0,0x08,0x46,0x0F, +0xFC,0x0F,0x04,0x13,0x00,0x2A,0x50,0x00,0x0F,0xFF,0xFE,0x46,0x40,0x10,0xA4,0x04, +0x14,0x13,0x00,0x2A,0x96,0xAF,0x04,0x93,0x00,0x2A,0x40,0x11,0x60,0x08,0x46,0x2C, +0x0F,0xFF,0x50,0x21,0x0F,0xFF,0x40,0x24,0x88,0x02,0xFE,0x8F,0x14,0x23,0x00,0x2A, +0x04,0x13,0x00,0x2B,0x96,0xEF,0x92,0x26,0x94,0x4E,0xFE,0x5F,0x14,0x13,0x00,0x2B, +0x97,0x2F,0x40,0x12,0x20,0x08,0x04,0x43,0x00,0x2B,0x97,0x6F,0xFF,0x3E,0xFF,0x0F, +0x14,0x43,0x00,0x2B,0x04,0x13,0x00,0x2B,0x40,0x52,0xC0,0x08,0xFE,0x0E,0xFE,0x2F, +0x14,0x03,0x00,0x2B,0x49,0x00,0x04,0x82,0x00,0x0F,0x80,0x20,0x54,0x10,0x00,0x3F, +0x04,0x03,0x03,0x98,0x92,0x06,0x94,0x06,0xFE,0x0F,0x14,0x03,0x03,0x98,0x00,0x0F, +0x80,0x24,0x04,0x23,0x03,0x99,0x96,0x2F,0x94,0x46,0x44,0x0F,0xF0,0x3F,0xFE,0x16, +0xFE,0x0F,0x14,0x03,0x03,0x99,0x49,0x00,0x04,0x62,0xFC,0xC0,0x46,0x30,0x00,0x82, +0x44,0x41,0x00,0x00,0xB4,0xA3,0x54,0x10,0x87,0xFF,0xFF,0x2F,0xB6,0x83,0xB4,0x83, +0x94,0x92,0x92,0x8B,0x40,0x42,0x2C,0x08,0xFF,0x0F,0xB6,0x83,0x88,0x40,0x4C,0x01, +0x00,0x05,0xA0,0x59,0xAA,0x41,0xD5,0xFC,0xB4,0x23,0x44,0x0E,0xFF,0xFF,0xFE,0x0E, +0xB6,0x03,0xDD,0x9E,0x46,0x30,0x00,0x82,0x44,0x41,0x00,0x00,0xB4,0xA3,0x54,0x10, +0x87,0xFF,0xFF,0x2F,0xB6,0x83,0xB4,0x83,0x94,0x92,0x92,0x8B,0x40,0x42,0x2C,0x08, +0xFF,0x0F,0xB6,0x83,0x88,0x40,0x80,0x23,0x4C,0x01,0x00,0x05,0xA3,0x01,0xA9,0x09, +0xD5,0xFC,0xB4,0x23,0x44,0x0E,0xFF,0xFF,0xFE,0x0E,0xB6,0x03,0xDD,0x9E,0xFC,0x00, +0x46,0x50,0x00,0x80,0x46,0x3F,0xFF,0x00,0x9D,0x99,0x9D,0x2E,0x94,0xA9,0x88,0x40, +0x38,0x11,0x18,0x00,0x38,0x21,0x0C,0x00,0x94,0x4C,0xFE,0x57,0x96,0x48,0x18,0x12, +0x80,0x01,0xDC,0xF5,0xFC,0x80,0xFC,0x00,0x46,0x10,0x00,0x80,0x46,0x2F,0xFE,0xFF, +0x50,0x50,0x80,0x08,0x50,0x61,0x0F,0xF1,0x50,0x41,0x0F,0xF0,0x50,0x30,0x80,0x0E, +0x94,0xA9,0x88,0x40,0x38,0x11,0x18,0x00,0x38,0x21,0x10,0x00,0x94,0x4C,0xFE,0x57, +0x96,0x48,0x18,0x12,0x80,0x01,0xDB,0xF5,0xFC,0x80,0x46,0x20,0x00,0x80,0x84,0x7E, +0x50,0x51,0x01,0xC4,0xB4,0x85,0x96,0x06,0xFE,0xE6,0xFE,0x1F,0xB6,0x05,0x96,0x57, +0xB4,0x65,0x40,0x00,0xA0,0x08,0x44,0x1F,0xF8,0xFF,0xFE,0x5E,0xFE,0x47,0xB6,0x25, +0xDD,0x9E,0x46,0x20,0x00,0x81,0x40,0x00,0x40,0x08,0x04,0x11,0x00,0x11,0x96,0x49, +0xFE,0x0F,0x14,0x01,0x00,0x11,0xDD,0x9E,0x46,0x20,0x00,0x81,0x40,0x00,0x40,0x08, +0x04,0x11,0x00,0x10,0x96,0x49,0xFE,0x0F,0x14,0x01,0x00,0x10,0xDD,0x9E,0x46,0x20, +0x00,0x81,0x04,0x11,0x00,0x10,0x92,0x30,0x40,0x10,0xC0,0x08,0xFE,0x0F,0x14,0x01, +0x00,0x10,0xDD,0x9E,0x46,0x20,0x00,0x81,0x04,0x11,0x00,0x11,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x0F,0x14,0x01,0x00,0x11,0xDD,0x9E,0x46,0x20,0x00,0x81,0x84,0x30, +0x04,0x31,0x00,0x12,0x96,0x1F,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E, +0x46,0x20,0x00,0x80,0x84,0x30,0x04,0x31,0x00,0x70,0x96,0x1F,0xFE,0x5E,0xFE,0x0F, +0x14,0x01,0x00,0x70,0xDD,0x9E,0x46,0x20,0x00,0x81,0x84,0x3E,0x04,0x31,0x00,0x13, +0x96,0x06,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x13,0xDD,0x9E,0x46,0x20,0x00,0x81, +0x96,0x06,0x04,0x31,0x00,0x12,0x40,0x10,0x20,0x08,0x44,0x0F,0xFE,0xFF,0xFE,0x1E, +0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E,0x46,0x20,0x00,0x81,0x96,0x0F,0x40,0x10, +0x48,0x08,0x04,0x31,0x00,0x12,0x46,0x0F,0xFF,0x3F,0x50,0x00,0x0F,0xFF,0xFE,0x1E, +0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E,0x46,0x00,0x00,0x81,0x04,0x00,0x00,0x12, +0x92,0x12,0x96,0x0F,0xDD,0x9E,0x44,0x14,0x80,0x10,0xB4,0x01,0x92,0x0D,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x11,0xFA,0x50,0xAE,0x80,0xB4,0x01,0x92,0x0E,0x96,0x06, +0xC0,0x06,0x44,0x04,0x80,0x11,0x44,0x10,0x00,0x40,0xAE,0x40,0x44,0x14,0x80,0x10, +0xB4,0x01,0x92,0x0F,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x11,0x44,0x2F,0xFF,0x80, +0xAE,0x80,0xB4,0x01,0x92,0x09,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x11,0x84,0x22, +0xAE,0x40,0x44,0x14,0x80,0x10,0xB4,0x01,0x92,0x0C,0x96,0x06,0xC0,0x05,0x44,0x04, +0x80,0x11,0xFA,0x40,0xAE,0x80,0xB4,0x01,0x92,0x17,0x96,0x06,0xC0,0x06,0x44,0x04, +0x80,0x12,0x44,0x1F,0xFF,0x80,0xAE,0x40,0xDD,0x9E,0x00,0x00,0x46,0x10,0x00,0x81, +0x04,0x30,0x80,0x39,0xC0,0x0B,0x5A,0x00,0x01,0x0D,0x5A,0x00,0x02,0x0E,0x84,0x40, +0x5A,0x08,0x03,0x0D,0x46,0x23,0xF3,0xC0,0xD5,0x09,0x46,0x23,0x00,0x00,0xD5,0x06, +0x46,0x23,0x51,0x40,0xD5,0x03,0x46,0x23,0xA2,0x80,0x46,0x1C,0x0C,0x3F,0x50,0x10, +0x8F,0xFF,0x40,0x01,0x84,0x02,0x46,0x10,0x00,0x81,0xFE,0x17,0x14,0x00,0x80,0x39, +0xDD,0x9E,0x46,0x20,0x00,0x81,0x96,0x0F,0x40,0x10,0x58,0x08,0x04,0x31,0x00,0x39, +0x46,0x0F,0xF3,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x39, +0xDD,0x9E,0x46,0x10,0x00,0x81,0x04,0x30,0x80,0x39,0xC0,0x11,0x5A,0x00,0x01,0x09, +0x5A,0x00,0x02,0x0A,0x5A,0x08,0x03,0x0B,0x44,0x03,0xFF,0xC0,0xD5,0x08,0x44,0x01, +0x55,0x40,0xD5,0x05,0x44,0x02,0xAA,0x80,0xD5,0x02,0x84,0x00,0x44,0x23,0xFF,0xC0, +0x44,0x1C,0x00,0x3F,0xFE,0x5E,0xFE,0x16,0xFE,0x0F,0x46,0x10,0x00,0x81,0x14,0x00, +0x80,0x39,0xDD,0x9E,0x46,0x20,0x00,0x81,0x96,0x0F,0x04,0x31,0x00,0x39,0x40,0x10, +0x28,0x08,0x44,0x0F,0xF3,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x39,0xDD,0x9E, +0x46,0x20,0x00,0x81,0x46,0x31,0x55,0x10,0x50,0x31,0x84,0x40,0x50,0x51,0x00,0xE4, +0xB4,0x25,0xA8,0xE9,0xA1,0x2A,0x84,0x78,0xFE,0xE6,0xE6,0x05,0xA8,0xEA,0xE8,0x3A, +0x44,0xF0,0x06,0x10,0x38,0x07,0x80,0x00,0x40,0xF0,0x3C,0x00,0x4A,0x00,0x3C,0x00, +0x06,0x1C,0x28,0x3E,0x54,0x00,0x46,0x03,0xA3,0x2A,0x92,0x26,0x50,0x00,0x03,0x00, +0x94,0x4E,0x14,0x01,0x00,0x38,0x58,0x10,0x80,0x3A,0xD5,0x22,0x84,0x00,0x92,0x26, +0x14,0x01,0x00,0x38,0x94,0x4E,0xD5,0x1C,0x46,0x05,0x55,0x55,0x92,0x26,0x50,0x00, +0x05,0x55,0x94,0x4E,0x14,0x01,0x00,0x38,0x58,0x10,0x80,0x15,0xD5,0x11,0x46,0x0A, +0xAA,0xAA,0x92,0x26,0x50,0x00,0x0A,0xAA,0x94,0x4E,0x14,0x01,0x00,0x38,0x58,0x10, +0x80,0x2A,0xD5,0x06,0x84,0x1F,0x14,0x01,0x00,0x38,0x58,0x10,0x80,0x3F,0x14,0x11, +0x00,0x39,0xDD,0x9E,0xDD,0x9E,0x44,0x24,0x50,0x00,0x96,0x06,0xB4,0x62,0x94,0x42, +0x84,0x1B,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x04,0x50,0x28,0xB4,0x00, +0x92,0x04,0x96,0x06,0xDD,0x9E,0x92,0x00,0x46,0x20,0x00,0x83,0x84,0x3C,0x04,0x31, +0x00,0x10,0x96,0x0F,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x10,0xDD,0x9E,0x46,0x20, +0x00,0x83,0x96,0x06,0x04,0x31,0x00,0x10,0x40,0x10,0x20,0x08,0x44,0x0F,0xFE,0xFF, +0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x10,0xDD,0x9E,0xFC,0x40,0x46,0x20,0x00,0x83, +0x44,0x5F,0xFE,0xFF,0x83,0x82,0xBB,0x18,0x46,0x40,0x0F,0x03,0xFE,0xEE,0xBB,0x98, +0xBB,0x18,0x50,0x42,0x0C,0x0F,0x92,0x68,0x40,0x31,0xA0,0x08,0x58,0x31,0x80,0x46, +0xBB,0x98,0xBF,0x39,0xBE,0x3A,0x04,0x91,0x00,0x3B,0xFF,0xE6,0xFF,0xA6,0x40,0x44, +0x90,0x02,0x04,0x90,0x80,0x02,0xBB,0x3C,0xB5,0x41,0x89,0x24,0xA1,0x0B,0x96,0xDF, +0x88,0xEA,0x04,0xA0,0x80,0x01,0x98,0x5C,0xBB,0x18,0x88,0xCA,0x58,0x31,0x81,0x00, +0xBB,0x98,0xBB,0x18,0x92,0x68,0x40,0x31,0xA0,0x08,0x58,0x31,0x80,0x46,0xBB,0x98, +0xBF,0xB9,0xBE,0xBA,0x14,0x91,0x00,0x3B,0x96,0xC9,0xB9,0x3C,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x5F,0xB9,0xBC,0xB4,0x20,0xB9,0xB9,0xA0,0x41,0xB9,0xBA,0xA0,0x42, +0xB9,0xBB,0xA4,0x46,0xB8,0x3C,0x92,0x10,0x40,0x00,0x40,0x08,0xFE,0x0F,0xB8,0xBC, +0xB8,0x18,0xFF,0x46,0xBD,0x98,0xFC,0xC0,0xFC,0x20,0x46,0x20,0x00,0x83,0x44,0x3F, +0xFE,0xFF,0x83,0x82,0xBC,0x18,0x94,0x4A,0xFE,0xE6,0xBB,0x98,0xBB,0x18,0x50,0x51, +0x00,0xE4,0x92,0x68,0x40,0x31,0xA0,0x08,0x58,0x31,0x80,0x48,0xBB,0x98,0xBB,0x18, +0x50,0x61,0x00,0xE8,0x58,0x31,0x81,0x00,0xBB,0x98,0x50,0x71,0x00,0xEC,0x88,0x20, +0x50,0x21,0x00,0xF0,0x46,0x30,0x00,0x83,0x4C,0x00,0x80,0x12,0xA2,0xC1,0xB6,0x65, +0x83,0x80,0xBB,0x3F,0x02,0x40,0x01,0x7E,0xB6,0x66,0xBB,0x7F,0xB6,0x67,0xB4,0x62, +0x92,0x70,0x40,0x31,0xC0,0x08,0xFE,0xE7,0xB6,0x62,0xD5,0xED,0x83,0x83,0xB9,0x18, +0x44,0x0F,0xFE,0xFF,0xFE,0x0E,0xB8,0x98,0xFC,0xA0,0xFC,0x20,0x46,0x10,0x00,0x83, +0x44,0x2F,0xFE,0xFF,0x83,0x81,0xBB,0x18,0x46,0x40,0xF0,0x3C,0xFE,0x9E,0xBA,0x98, +0xBA,0x18,0x50,0x32,0x00,0xF0,0x92,0x48,0x40,0x21,0x20,0x08,0x58,0x21,0x00,0x46, +0xBA,0x98,0xBE,0x39,0xB4,0x80,0xBD,0x3A,0xFF,0x9E,0x88,0xC4,0xA1,0x01,0xBF,0x3B, +0xFF,0x5E,0x88,0xA4,0xA1,0x02,0xBA,0x3C,0xFE,0xFE,0x88,0x83,0xA0,0xC3,0x54,0x21, +0x00,0xF0,0xBE,0xB9,0x98,0x13,0xBD,0xBA,0xBC,0xBB,0x96,0x81,0xB8,0x3C,0x92,0x10, +0x40,0x00,0x40,0x08,0xFE,0x17,0xB8,0xBC,0xFC,0xA0,0x44,0x14,0x80,0x90,0x84,0x5E, +0xB4,0x61,0x96,0x06,0xFE,0x9E,0xFE,0x87,0xB6,0x41,0xB4,0x81,0x44,0x2F,0xFF,0xEF, +0x94,0xC4,0xFE,0xA6,0xFE,0x9F,0xB6,0x41,0xB4,0x61,0x44,0x2F,0xFB,0xFF,0xFE,0x9E, +0x40,0x00,0x28,0x08,0xFE,0x17,0xB6,0x01,0x46,0x10,0x00,0x80,0x84,0x18,0x50,0x30, +0x82,0x6C,0xB4,0x43,0xFE,0x16,0x58,0x00,0x00,0x06,0xB6,0x03,0xB4,0x43,0x44,0x0F, +0xF0,0xFF,0xFE,0x16,0x58,0x00,0x08,0x00,0xB6,0x03,0xDD,0x9E,0x52,0x00,0x00,0x01, +0x46,0x20,0x00,0x80,0x96,0x06,0xC9,0x08,0x40,0x10,0x20,0x08,0x04,0x31,0x00,0x79, +0x44,0x0F,0xFE,0xFF,0xD5,0x07,0x40,0x10,0x40,0x08,0x04,0x31,0x00,0x79,0x44,0x0E, +0xFF,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x79,0xDD,0x9E,0x46,0x10,0x00,0x82, +0x5A,0x08,0x01,0x0B,0x50,0x30,0x80,0x20,0xB4,0x03,0x58,0x00,0x00,0x02,0xB6,0x03, +0xB4,0x43,0x84,0x1E,0xD5,0x04,0x04,0x20,0x80,0x08,0x84,0x1D,0xFE,0x16,0x14,0x00, +0x80,0x08,0xDD,0x9E,0x46,0x30,0x00,0x83,0x54,0x10,0x00,0x01,0x50,0x51,0x83,0xB8, +0xB4,0x85,0x40,0x20,0xA0,0x08,0x44,0x1F,0xFE,0xFF,0xFE,0x66,0xFE,0x57,0xB6,0x25, +0xB4,0x25,0xE6,0x01,0x44,0x0F,0xFD,0xFF,0x40,0xF7,0xA4,0x08,0xFE,0x0E,0x40,0x00, +0x3C,0x04,0xB6,0x05,0xDD,0x9E,0x46,0x30,0x00,0x83,0x54,0x10,0x00,0x01,0x50,0x51, +0x83,0xB8,0xB4,0x85,0x40,0x20,0xA8,0x08,0x44,0x1F,0xFB,0xFF,0xFE,0x66,0xFE,0x57, +0xB6,0x25,0xB4,0x25,0xE6,0x01,0x44,0x0F,0xF7,0xFF,0x40,0xF7,0xAC,0x08,0xFE,0x0E, +0x40,0x00,0x3C,0x04,0xB6,0x05,0xDD,0x9E,0x44,0x27,0x20,0x68,0x96,0x01,0xB4,0x22, +0x92,0x30,0x40,0x10,0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x70,0xB4,0x22, +0x92,0x30,0x40,0x10,0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x78,0xB4,0x22, +0x92,0x30,0x40,0x10,0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x80,0xB4,0x22, +0x92,0x30,0x40,0x10,0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x88,0xB4,0x22, +0x92,0x30,0x40,0x10,0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x90,0xB4,0x22, +0x92,0x30,0x40,0x10,0xC0,0x08,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0xC8,0x0A,0x44,0x17, +0x20,0x00,0x46,0x0F,0xEF,0xFF,0xB4,0x41,0x50,0x00,0x0F,0xFF,0xFE,0x16,0xD5,0x09, +0x5A,0x08,0x01,0x09,0x44,0x17,0x20,0x00,0x46,0x00,0x10,0x00,0xB4,0x41,0xFE,0x17, +0xB6,0x01,0xDD,0x9E,0x44,0x27,0x20,0x00,0x84,0x3E,0xB4,0x62,0x96,0x06,0xFE,0x5E, +0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x27,0x20,0x04,0x84,0x3E,0xB4,0x62,0x96,0x06, +0xFE,0x5E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x27,0x20,0x00,0x96,0x06,0xB4,0x62, +0x40,0x10,0x40,0x08,0x44,0x0E,0xFF,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E, +0xC9,0x06,0x44,0x27,0x10,0x00,0xB4,0x22,0xFE,0x0F,0xD5,0x08,0x5A,0x18,0x01,0x08, +0x44,0x27,0x10,0x00,0xFE,0x03,0xB4,0x22,0xFE,0x0E,0xB6,0x02,0xDD,0x9E,0x5A,0x18, +0x01,0x07,0x44,0x27,0x10,0x04,0xB4,0x22,0xFE,0x0F,0xD5,0x07,0xC9,0x07,0x44,0x27, +0x10,0x04,0xFE,0x03,0xB4,0x22,0xFE,0x0E,0xB6,0x02,0xDD,0x9E,0x5A,0x18,0x01,0x07, +0x44,0x27,0x10,0x08,0xB4,0x22,0xFE,0x0F,0xD5,0x07,0xC9,0x07,0x44,0x27,0x10,0x08, +0xFE,0x03,0xB4,0x22,0xFE,0x0E,0xB6,0x02,0xDD,0x9E,0x44,0x16,0x00,0x00,0x5A,0x08, +0x01,0x06,0xB4,0x01,0x58,0x00,0x00,0x01,0xD5,0x04,0xB4,0x41,0x84,0x1E,0xFE,0x16, +0xB6,0x01,0xDD,0x9E,0x44,0x16,0x00,0x00,0xB4,0x41,0x5A,0x08,0x01,0x06,0x44,0x01, +0x00,0x00,0xFE,0x17,0xD5,0x04,0x44,0x0E,0xFF,0xFF,0xFE,0x16,0xB6,0x01,0xDD,0x9E, +0x44,0x06,0x00,0x00,0xB4,0x00,0x92,0x1C,0x96,0x06,0xDD,0x9E,0x44,0x16,0x00,0x30, +0x5A,0x08,0x01,0x06,0xB4,0x01,0x58,0x00,0x01,0x00,0xD5,0x05,0xB4,0x41,0x44,0x0F, +0xFE,0xFF,0xFE,0x16,0xB6,0x01,0xDD,0x9E,0x44,0x36,0x38,0x14,0x40,0x00,0x20,0x08, +0x40,0x20,0x20,0x09,0xB4,0x03,0x96,0x6F,0x92,0x18,0x40,0x00,0x60,0x08,0xFE,0x17, +0x44,0x26,0x38,0x08,0xB6,0x03,0xB4,0x62,0x40,0x00,0xE0,0x08,0x46,0x1C,0x0F,0xFF, +0x50,0x10,0x8F,0xFF,0xFE,0x5E,0xFE,0x47,0xB6,0x22,0x44,0x16,0x38,0x04,0x44,0x01, +0x00,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x03, +0x96,0x06,0xC0,0xFD,0x44,0x04,0x80,0x0C,0x84,0x28,0xAE,0x40,0xDD,0x9E,0x44,0x36, +0x34,0x14,0x40,0x00,0x20,0x08,0x40,0x20,0x20,0x09,0xB4,0x03,0x96,0x6F,0x92,0x18, +0x40,0x00,0x60,0x08,0xFE,0x17,0x44,0x26,0x34,0x08,0xB6,0x03,0xB4,0x62,0x40,0x00, +0xE0,0x08,0x46,0x1C,0x0F,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x5E,0xFE,0x47,0xB6,0x22, +0x44,0x16,0x34,0x04,0x44,0x01,0x00,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0x44,0x14, +0x80,0x0C,0xB4,0x01,0x92,0x13,0x96,0x06,0xC0,0xFD,0x44,0x04,0x80,0x0E,0x84,0x28, +0xAE,0x40,0xDD,0x9E,0xFC,0x01,0xB6,0x3F,0x80,0x22,0xF3,0x81,0x49,0xFF,0xFF,0xA6, +0xB4,0x1F,0xF1,0x01,0x49,0xFF,0xFF,0xCD,0xFC,0x81,0x44,0x16,0x3C,0x04,0xB4,0x01, +0x58,0x00,0x00,0x01,0xB6,0x01,0xDD,0x9E,0x44,0x26,0x38,0x04,0xB4,0x22,0x44,0x06, +0x38,0x04,0x92,0x28,0x96,0x46,0xC9,0xFB,0xB4,0x40,0x84,0x3E,0xFE,0x56,0xB6,0x20, +0x80,0x20,0xB4,0x01,0x92,0x08,0x96,0x06,0xC8,0xFD,0x44,0x26,0x34,0x04,0xB4,0x22, +0x44,0x06,0x34,0x04,0x92,0x28,0x96,0x46,0xC9,0xFB,0xB4,0x40,0x84,0x3E,0xFE,0x56, +0xB6,0x20,0x80,0x20,0xB4,0x01,0x92,0x08,0x96,0x06,0xC8,0xFD,0xDD,0x9E,0xFC,0x00, +0x46,0x60,0x00,0x83,0x83,0x86,0xB8,0x27,0x58,0x00,0x00,0x01,0xB8,0xA7,0xB8,0x27, +0x58,0x00,0x01,0x00,0xB8,0xA7,0x84,0x01,0x49,0x00,0x08,0xA4,0xB9,0x27,0x84,0x1E, +0xFE,0x0E,0xB8,0xA7,0xB9,0x27,0x44,0x0F,0xFE,0xFF,0xFE,0x0E,0xB8,0xA7,0xFC,0x80, +0xFC,0x40,0x46,0x60,0x00,0x83,0x84,0xFE,0x83,0x86,0x04,0x93,0x00,0x14,0x96,0x06, +0x40,0x74,0x9C,0x02,0xFE,0x3F,0xB8,0x94,0x96,0x5F,0xBF,0x14,0x40,0x00,0xE0,0x08, +0x46,0x1F,0x0F,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x7E,0xFE,0x47,0xB9,0x94,0x97,0x2F, +0xB9,0x14,0x40,0x02,0x44,0x08,0x46,0x4F,0xF8,0x1F,0x50,0x42,0x0F,0xFF,0xFF,0x0E, +0xFF,0x07,0xBC,0x94,0x97,0x6F,0x40,0x02,0xAC,0x08,0xBD,0x14,0x44,0x1E,0x07,0xFF, +0xFF,0x4E,0xFF,0x47,0xBD,0x94,0xB8,0x15,0x54,0x21,0x03,0xFF,0x92,0x0A,0x40,0x00, +0x28,0x08,0xFE,0x17,0xB8,0x95,0x96,0xF7,0x40,0x01,0xC0,0x08,0xB9,0x15,0x46,0x3F, +0xF8,0x0F,0x50,0x31,0x8F,0xFF,0xFE,0xCE,0xFE,0xC7,0xBB,0x95,0xFC,0xC0,0x46,0x00, +0x00,0x83,0x04,0x00,0x00,0x16,0x96,0x01,0xDD,0x9E,0xFC,0x00,0x46,0x40,0x00,0x83, +0x84,0x61,0x83,0x84,0xBE,0x13,0x84,0xBE,0xFE,0x1E,0xFF,0x76,0xFE,0x2F,0xB8,0x93, +0xFE,0x5E,0x40,0x30,0xA0,0x08,0xB9,0x60,0x44,0x0F,0xFE,0xFF,0xFE,0x46,0xFE,0x5F, +0xB9,0xE0,0x96,0xA7,0x40,0x01,0x20,0x08,0xBA,0x13,0x44,0x1F,0xE0,0xFF,0xFE,0x8E, +0xFE,0x87,0xBA,0x93,0xFC,0x80,0x46,0x30,0x00,0x83,0x96,0x8F,0x50,0x41,0x81,0x84, +0xB6,0x04,0xA8,0x61,0xA0,0x62,0x84,0x1C,0xFE,0x0E,0xFE,0x87,0xA8,0xA2,0xDD,0x9E, +0x46,0x20,0x00,0x83,0x96,0x27,0x04,0x11,0x00,0x60,0x92,0x25,0x94,0x4D,0xFE,0x47, +0x14,0x11,0x00,0x60,0xDD,0x9E,0x46,0x20,0x00,0x83,0x84,0x3E,0x04,0x31,0x00,0x12, +0x96,0x06,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E,0xB4,0x40,0x46,0x10, +0x00,0x80,0x14,0x20,0x80,0x48,0xA0,0x01,0x14,0x00,0x80,0x4A,0xDD,0x9E,0xFC,0x40, +0x46,0x20,0x00,0x83,0x50,0x61,0x00,0xE4,0x83,0x82,0xBB,0x18,0x50,0x71,0x00,0xE8, +0x58,0x31,0x81,0x00,0xBB,0x98,0xBB,0x18,0x50,0x91,0x00,0xEC,0x92,0x68,0x40,0x31, +0xA0,0x08,0x58,0x31,0x80,0x88,0xBB,0x98,0x8C,0x02,0x84,0xA0,0x50,0x21,0x00,0xF0, +0x46,0x30,0x00,0x83,0xD1,0x22,0x22,0x30,0x00,0x00,0x22,0x40,0x7F,0xFF,0x40,0x31, +0xC0,0x08,0x88,0x64,0xB6,0x66,0x22,0x30,0x00,0x02,0x22,0x40,0x00,0x01,0x40,0x31, +0xC0,0x08,0x88,0x64,0xB6,0x67,0x22,0x30,0x00,0x04,0x22,0x40,0x00,0x03,0x40,0x31, +0xC0,0x08,0x88,0x64,0xB6,0x69,0xB4,0x62,0x8C,0xA1,0x92,0x70,0x40,0x31,0xC0,0x08, +0xB6,0x62,0x97,0x68,0x8C,0x0C,0xD5,0xDD,0x83,0x83,0xB9,0x18,0x44,0x0F,0xFE,0xFF, +0xFE,0x0E,0xB8,0x98,0xFC,0xC0,0x46,0x20,0x00,0x83,0x96,0x0F,0x04,0x31,0x00,0x12, +0x40,0x10,0x40,0x08,0x44,0x0C,0xFF,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x12, +0xDD,0x9E,0xFC,0x00,0x84,0x40,0x44,0x64,0x00,0xC0,0x38,0x30,0x88,0x00,0x38,0x40, +0x09,0x01,0x99,0x56,0x40,0x31,0xB0,0x08,0x95,0x69,0xFE,0xE7,0x96,0xD9,0x8C,0x41, +0xAC,0xE8,0x5A,0x28,0x14,0xF4,0xFC,0x80,0x46,0x20,0x00,0x83,0x96,0x06,0x04,0x31, +0x00,0x26,0x94,0x41,0x84,0x1D,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x26,0xDD,0x9E, +0x46,0x20,0x00,0x81,0x96,0x06,0xA0,0xD5,0x40,0x10,0x20,0x08,0x44,0x0F,0xFE,0xFF, +0xFE,0x1E,0xFE,0x0F,0xA8,0x15,0xDD,0x9E,0xC9,0x08,0x46,0x20,0x00,0x91,0x84,0x3E, +0xB4,0x62,0x96,0x06,0xFE,0x5E,0xD5,0x0A,0x5A,0x18,0x01,0x0B,0x46,0x20,0x00,0x91, +0x96,0x06,0xB4,0x62,0x94,0x41,0x84,0x1D,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E, +0xFC,0x00,0x44,0x00,0x00,0xC8,0x49,0x00,0x07,0x75,0x44,0x14,0x50,0x0C,0x46,0x60, +0x00,0x81,0xB4,0x01,0x58,0x00,0x00,0x04,0xB6,0x01,0xFA,0x18,0x49,0x00,0x07,0x73, +0x83,0x86,0xB8,0x1C,0x58,0x00,0x01,0x00,0xB8,0x9C,0x84,0x01,0x49,0x00,0x07,0x6B, +0xB9,0x1C,0x44,0x01,0x00,0x00,0xFE,0x0F,0xB8,0x9C,0x44,0x00,0x01,0xF4,0x49,0x00, +0x07,0x59,0xB9,0x1C,0x46,0x00,0x10,0x00,0xFE,0x0F,0xB8,0x9C,0x44,0x00,0x01,0xF4, +0x49,0x00,0x07,0x50,0xFC,0x80,0x00,0x00,0x46,0x10,0x00,0x80,0x50,0x50,0x80,0x80, +0x50,0x10,0x80,0xB0,0xA2,0x81,0xAA,0xA9,0xD9,0xFE,0xDD,0x9E,0x46,0x10,0x00,0x80, +0x50,0x50,0x80,0xB0,0x50,0x10,0x80,0xC4,0xA2,0x81,0xAA,0xA9,0xD9,0xFE,0xDD,0x9E, +0xB4,0x40,0x46,0x10,0x00,0x80,0x14,0x20,0x80,0x47,0xA0,0x01,0x14,0x00,0x80,0x49, +0xDD,0x9E,0x46,0x10,0x00,0x80,0x50,0x50,0x81,0x40,0x50,0x30,0x81,0x4C,0xA6,0x41, +0xA6,0x80,0x94,0x4C,0xFE,0x57,0x96,0x48,0xAE,0x68,0x00,0x10,0x00,0x0D,0x00,0x20, +0x00,0x0C,0x94,0x4C,0xFE,0x57,0x96,0x48,0x10,0x12,0x80,0x0C,0x8C,0xA1,0x8C,0x02, +0xDB,0xEF,0xDD,0x9E,0x46,0x10,0x00,0x80,0x50,0x50,0x80,0x40,0x50,0x30,0x80,0x46, +0xA6,0x41,0xA6,0x80,0x94,0x4C,0xFE,0x57,0x96,0x48,0xAE,0x68,0x00,0x10,0x00,0x0D, +0x00,0x20,0x00,0x0C,0x94,0x4C,0xFE,0x57,0x96,0x48,0x10,0x12,0x80,0x0C,0x8C,0xA1, +0x8C,0x02,0xDB,0xEF,0xDD,0x9E,0xA6,0x81,0x80,0xA0,0x94,0x94,0x08,0x12,0x80,0x05, +0xFE,0x8F,0x46,0x10,0x00,0x80,0x96,0x90,0x10,0x20,0x80,0x48,0xA6,0xA9,0xA6,0xC5, +0x94,0x94,0xFE,0x9F,0x96,0x90,0x10,0x20,0x80,0x54,0xA6,0x83,0xA6,0xC2,0x94,0x94, +0xFE,0x9F,0x96,0x90,0x10,0x20,0x80,0x49,0xA6,0xAB,0xA6,0xEA,0x94,0x94,0xFE,0x9F, +0x96,0x90,0x10,0x20,0x80,0x55,0xA6,0x84,0x84,0x0F,0xFE,0x86,0x40,0x31,0x40,0x08, +0x46,0x4F,0xFF,0x0F,0x04,0x20,0x80,0x12,0x50,0x42,0x0F,0xFF,0xFE,0xA6,0xFE,0x9F, +0x14,0x20,0x80,0x12,0xA6,0xAC,0xFE,0x16,0x40,0x20,0x40,0x08,0x04,0x00,0x80,0x15, +0xFE,0x26,0xFE,0x17,0x14,0x00,0x80,0x15,0xDD,0x9E,0x46,0x10,0x00,0x80,0x50,0x50, +0x80,0x58,0x50,0x30,0x80,0x5E,0xA6,0x41,0xA6,0x80,0x94,0x4C,0xFE,0x57,0x96,0x48, +0xAE,0x68,0x00,0x10,0x00,0x0D,0x00,0x20,0x00,0x0C,0x94,0x4C,0xFE,0x57,0x96,0x48, +0x10,0x12,0x80,0x0C,0x8C,0xA1,0x8C,0x02,0xDB,0xEF,0xDD,0x9E,0xA6,0x81,0x80,0xA0, +0x94,0x94,0x08,0x12,0x80,0x05,0xFE,0x8F,0x46,0x10,0x00,0x80,0x96,0x90,0x10,0x20, +0x80,0x60,0xA6,0xA9,0xA6,0xC5,0x94,0x94,0xFE,0x9F,0x96,0x90,0x10,0x20,0x80,0x6C, +0xA6,0x83,0xA6,0xC2,0x94,0x94,0xFE,0x9F,0x96,0x90,0x10,0x20,0x80,0x61,0xA6,0xAB, +0xA6,0xEA,0x94,0x94,0xFE,0x9F,0x96,0x90,0x10,0x20,0x80,0x6D,0xA6,0x84,0x84,0x0F, +0xFE,0x86,0x40,0x31,0x40,0x08,0x46,0x4F,0xFF,0x0F,0x04,0x20,0x80,0x18,0x50,0x42, +0x0F,0xFF,0xFE,0xA6,0xFE,0x9F,0x14,0x20,0x80,0x18,0xA6,0xAC,0xFE,0x16,0x40,0x20, +0x40,0x08,0x04,0x00,0x80,0x1B,0xFE,0x26,0xFE,0x17,0x14,0x00,0x80,0x1B,0xDD,0x9E, +0xE6,0x06,0xE8,0x57,0xFC,0x20,0x46,0x70,0x00,0x81,0x44,0xF0,0x10,0x78,0x38,0x07, +0x80,0x00,0x40,0xF0,0x3C,0x00,0xDD,0x0F,0x06,0x16,0x34,0x44,0x62,0x76,0x04,0x03, +0x80,0x20,0x54,0x10,0x87,0xFF,0x92,0x0B,0x40,0x00,0x2C,0x08,0xD5,0x0C,0x54,0x10, +0x87,0xFF,0x40,0x00,0xC0,0x08,0x04,0x23,0x80,0x20,0x46,0x1F,0x80,0x0F,0x50,0x10, +0x8F,0xFF,0xFE,0x56,0xFE,0x47,0x14,0x13,0x80,0x20,0xFC,0xA0,0x04,0x03,0x80,0x21, +0x54,0x10,0x87,0xFF,0x92,0x0B,0x40,0x00,0x2C,0x08,0xD5,0x0C,0x54,0x10,0x87,0xFF, +0x40,0x00,0xC0,0x08,0x04,0x23,0x80,0x21,0x46,0x1F,0x80,0x0F,0x50,0x10,0x8F,0xFF, +0xFE,0x56,0xFE,0x47,0x14,0x13,0x80,0x21,0xFC,0xA0,0x83,0x87,0xB8,0x22,0x54,0x10, +0x87,0xFF,0x92,0x0B,0x40,0x00,0x2C,0x08,0xFE,0x47,0xB9,0xA2,0xFC,0xA0,0x80,0xC1, +0x49,0xFF,0xFD,0x54,0x54,0x13,0x03,0xFF,0x95,0x8E,0x04,0x13,0x83,0x98,0x44,0x0F, +0x00,0x3F,0xFE,0x46,0xFE,0x77,0x14,0x13,0x83,0x98,0x49,0xFF,0xFD,0x40,0xFC,0xA0, +0xDD,0x9E,0x46,0x20,0x00,0x83,0x96,0x06,0x40,0x10,0x60,0x08,0x04,0x31,0x00,0xC0, +0x46,0x0F,0xEF,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0xC0, +0xDD,0x9E,0x46,0x20,0x00,0x83,0x96,0x06,0x40,0x10,0x64,0x08,0x04,0x31,0x00,0xC0, +0x46,0x0F,0xDF,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0xC0, +0xDD,0x9E,0x46,0x20,0x00,0x83,0x96,0x0F,0x04,0x31,0x00,0x0F,0x40,0x10,0x20,0x08, +0x44,0x0F,0xFC,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x0F,0xDD,0x9E,0x46,0x20, +0x00,0x83,0x84,0x3E,0x04,0x31,0x00,0x0F,0x96,0x06,0xFE,0x5E,0xFE,0x0F,0x14,0x01, +0x00,0x0F,0xDD,0x9E,0x46,0x20,0x00,0x83,0x96,0x06,0x04,0x31,0x00,0x0F,0x40,0x10, +0x28,0x08,0x44,0x0F,0xFB,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x0F,0xDD,0x9E, +0x46,0x10,0x00,0x83,0x44,0x01,0x00,0x00,0x04,0x20,0x80,0x0E,0xFE,0x17,0x14,0x00, +0x80,0x0E,0xDD,0x9E,0x46,0x20,0x00,0x83,0x46,0x3F,0x7F,0xFF,0x50,0x51,0x00,0x38, +0xB4,0x85,0x50,0x31,0x8F,0xFF,0xFE,0xE6,0xB6,0x65,0xB4,0x85,0x46,0x3E,0xFF,0xFF, +0x50,0x31,0x8F,0xFF,0xFE,0xE6,0xB6,0x65,0x5A,0x08,0x01,0x04,0x84,0x1F,0xD5,0x02, +0x84,0x00,0x14,0x01,0x00,0x0B,0x14,0x01,0x00,0x0C,0x04,0x01,0x00,0x33,0x5A,0x18, +0x01,0x0B,0x58,0x00,0x0F,0xFF,0x14,0x01,0x00,0x33,0x04,0x01,0x00,0x34,0x58,0x00, +0x0F,0xFF,0xD5,0x0B,0x92,0x0C,0x40,0x00,0x30,0x08,0x14,0x01,0x00,0x33,0x04,0x01, +0x00,0x34,0x92,0x0C,0x40,0x00,0x30,0x08,0x14,0x01,0x00,0x34,0xDD,0x9E,0x46,0x10, +0x00,0x83,0x96,0x06,0x50,0x50,0x80,0x38,0xB4,0x85,0x44,0x2F,0xFE,0xFF,0x40,0x30, +0x20,0x08,0xFE,0xA6,0xFE,0x9F,0xB6,0x45,0xB4,0x65,0x44,0x2F,0xFD,0xFF,0x40,0x00, +0x24,0x08,0xFE,0x9E,0xFE,0x17,0xB6,0x05,0xDD,0x9E,0x46,0x20,0x00,0x81,0x50,0x51, +0x00,0x90,0x50,0x31,0x00,0x9A,0x08,0x20,0x00,0x01,0x88,0x41,0x96,0x90,0x18,0x22, +0x80,0x01,0xDB,0xFA,0xDD,0x9E,0xE6,0x25,0xE8,0x33,0x96,0x1F,0x46,0x20,0x00,0x81, +0x44,0xF0,0x12,0x80,0x38,0x17,0x84,0x00,0x40,0xF0,0xBC,0x00,0x4A,0x00,0x3C,0x00, +0x06,0x0E,0x1A,0x28,0x36,0x00,0x04,0x31,0x00,0x23,0x84,0x30,0xD5,0x1D,0x94,0x04, +0x04,0x31,0x00,0x23,0x44,0x1F,0xFF,0x0F,0xD5,0x17,0x40,0x00,0x20,0x08,0x04,0x31, +0x00,0x23,0x44,0x1F,0xF0,0xFF,0xD5,0x10,0x40,0x00,0x30,0x08,0x04,0x31,0x00,0x23, +0x44,0x1F,0x0F,0xFF,0xD5,0x09,0x46,0x1F,0xFF,0x0F,0x40,0x00,0x40,0x08,0x04,0x31, +0x00,0x23,0x50,0x10,0x8F,0xFF,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x23,0xDD,0x9E, +0x46,0x00,0x00,0x81,0x84,0x20,0x04,0x00,0x00,0x12,0x92,0x12,0x96,0x0F,0x40,0x00, +0x80,0x06,0xDD,0x9E,0x46,0x20,0x00,0x81,0x54,0x00,0x03,0xFF,0x40,0x10,0x40,0x08, +0x04,0x31,0x00,0x49,0x46,0x0F,0xC0,0x0F,0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F, +0x14,0x01,0x00,0x49,0xDD,0x9E,0xE6,0x06,0xE8,0x52,0xFC,0x20,0x46,0x70,0x00,0x81, +0x54,0x60,0x83,0xFF,0x44,0xF0,0x13,0x24,0x38,0x07,0x80,0x00,0x40,0xF0,0x3C,0x00, +0x4A,0x00,0x3C,0x00,0x06,0x2E,0x46,0x56,0x66,0x16,0x83,0x87,0xB9,0x30,0x92,0x2A, +0x40,0x10,0xA8,0x08,0xFF,0x8F,0xBE,0xB0,0xFC,0xA0,0x83,0x87,0xB8,0x31,0x46,0x2F, +0xC0,0x0F,0x50,0x21,0x0F,0xFF,0x40,0x63,0x40,0x08,0xFE,0x16,0xFE,0x37,0xB8,0xB1, +0xFC,0xA0,0x83,0x87,0xB8,0x30,0x46,0x2F,0xC0,0x0F,0x50,0x21,0x0F,0xFF,0x40,0x63, +0x40,0x08,0xFE,0x16,0xFE,0x37,0xB8,0xB0,0xFC,0xA0,0x83,0x87,0xB9,0x31,0x92,0x2A, +0x40,0x10,0xA8,0x08,0xFF,0x8F,0xBE,0xB1,0xFC,0xA0,0x83,0x87,0xB9,0x32,0x92,0x2A, +0x40,0x10,0xA8,0x08,0xFF,0x8F,0xBE,0xB2,0xFC,0xA0,0x49,0xFF,0xFC,0x07,0x40,0x13, +0x40,0x08,0x04,0x03,0x83,0x98,0x46,0x6F,0xC0,0x0F,0x50,0x63,0x0F,0xFF,0xFF,0x86, +0xFF,0x8F,0x14,0x63,0x83,0x98,0x49,0xFF,0xFB,0xF2,0xFC,0xA0,0xDD,0x9E,0x54,0x20, +0x80,0x03,0xE6,0x08,0x54,0x10,0x80,0x0C,0x90,0x22,0x4E,0xF2,0x00,0x93,0x96,0x4F, +0x46,0x30,0x00,0x81,0x96,0x8F,0x44,0xF0,0x13,0xD4,0x38,0x07,0x80,0x00,0x40,0xF0, +0x3C,0x00,0xDD,0x0F,0x08,0x24,0x40,0x5E,0x7C,0x9C,0xBC,0xE8,0x50,0x51,0x80,0x9C, +0xB4,0x85,0x84,0x1C,0xFE,0x26,0xFE,0x47,0xB6,0x25,0xB4,0x25,0x44,0x0C,0xFF,0xFF, +0x40,0x21,0x40,0x08,0xFE,0x0E,0xD5,0x72,0x50,0x51,0x80,0x9C,0xB4,0x85,0x84,0x13, +0xFE,0x26,0x94,0x4A,0xFE,0x47,0xB6,0x25,0x40,0x21,0x48,0x08,0xB4,0x05,0x46,0x1F, +0xFF,0x3F,0xD5,0x4E,0x50,0x51,0x80,0x9C,0xB4,0x85,0x44,0x0F,0xFF,0xCF,0xFE,0x26, +0x94,0x4C,0xFE,0x47,0xB6,0x25,0x40,0x21,0x50,0x08,0xB4,0x05,0x46,0x1F,0xFC,0xFF, +0xD5,0x3F,0x50,0x51,0x80,0x9C,0xB4,0x85,0x44,0x0F,0xFF,0x3F,0xFE,0x26,0x94,0x4E, +0xFE,0x47,0xB6,0x25,0x40,0x21,0x58,0x08,0xB4,0x05,0x46,0x1F,0xF3,0xFF,0xD5,0x30, +0x50,0x51,0x80,0x9C,0xB4,0x85,0x44,0x0F,0xFC,0xFF,0xFE,0x26,0x40,0x10,0xA0,0x08, +0xFE,0x47,0xB6,0x25,0x40,0x21,0x60,0x08,0xB4,0x05,0x46,0x1F,0xCF,0xFF,0xD5,0x20, +0x50,0x51,0x80,0x9C,0xB4,0x85,0x44,0x0F,0xF3,0xFF,0xFE,0x26,0x40,0x10,0xA8,0x08, +0xFE,0x47,0xB6,0x25,0x40,0x21,0x68,0x08,0xB4,0x05,0x46,0x1F,0x3F,0xFF,0xD5,0x10, +0x50,0x51,0x80,0x9C,0xB4,0x85,0x44,0x0F,0xCF,0xFF,0xFE,0x26,0x40,0x10,0xB0,0x08, +0xFE,0x47,0xB6,0x25,0x40,0x21,0x70,0x08,0xB4,0x05,0x46,0x1C,0xFF,0xFF,0x50,0x10, +0x8F,0xFF,0xFE,0x0E,0xFE,0x17,0x14,0x01,0x80,0x27,0xDD,0x9E,0x50,0x51,0x80,0x9C, +0xB4,0x85,0x44,0x0F,0x3F,0xFF,0xFE,0x26,0x40,0x10,0xB8,0x08,0xFE,0x47,0xB6,0x25, +0xB4,0x05,0x40,0x21,0x78,0x08,0x94,0x02,0x92,0x02,0xFE,0x87,0x14,0x21,0x80,0x27, +0xDD,0x9E,0xDD,0x9E,0xFC,0x40,0x80,0xE0,0x44,0x04,0x34,0x38,0x5A,0x78,0x01,0x2B, +0x80,0xC1,0xB4,0x20,0x44,0x9F,0xE0,0xFF,0x58,0x10,0x80,0x02,0xB6,0x20,0x80,0x27, +0x49,0xFF,0xFB,0x0F,0x44,0x04,0x34,0x50,0x97,0xA7,0xB4,0x20,0x40,0x63,0x20,0x08, +0x40,0x10,0xA4,0x02,0xFE,0x77,0xB6,0x20,0x80,0x27,0x49,0xFF,0xFB,0x02,0x44,0x04, +0x38,0x3C,0xB4,0x20,0x58,0x10,0x80,0x02,0xB6,0x20,0x80,0x27,0x49,0xFF,0xFA,0xCE, +0x44,0x04,0x38,0x58,0xB4,0x20,0x40,0x10,0xA4,0x02,0xFF,0x8F,0xB6,0xC0,0x80,0x27, +0xD5,0x0E,0xB4,0x20,0x84,0xDD,0xFE,0x76,0xB6,0x20,0x84,0x21,0x49,0xFF,0xFA,0xE9, +0x44,0x04,0x38,0x3C,0xB4,0x20,0xFF,0x8E,0xB6,0xC0,0x84,0x21,0x49,0xFF,0xFA,0xB6, +0xFC,0xC0,0x92,0x00,0xC8,0x04,0x44,0x07,0x20,0x0C,0xD5,0x03,0x44,0x07,0x20,0x10, +0xB4,0x00,0x40,0x00,0x30,0x08,0x92,0x0C,0xDD,0x9E,0xFC,0x00,0x80,0xC1,0x49,0xFF, +0xFF,0xF3,0x84,0x40,0x80,0x22,0xE2,0x46,0xE8,0x05,0x38,0x10,0x0A,0x0A,0x8C,0x41, +0xD5,0xFB,0xFC,0x80,0x44,0x27,0x20,0x1C,0x54,0x00,0x0F,0xFF,0xB4,0x22,0x92,0x2C, +0x40,0x10,0xB0,0x08,0xFE,0x47,0xB6,0x22,0xDD,0x9E,0x44,0x27,0x20,0x34,0x54,0x00, +0x0F,0xFF,0xB4,0x22,0x92,0x2C,0x40,0x10,0xB0,0x08,0xFE,0x47,0xB6,0x22,0xDD,0x9E, +0x44,0x27,0x20,0x38,0x54,0x00,0x0F,0xFF,0xB4,0x22,0x92,0x2C,0x40,0x10,0xB0,0x08, +0xFE,0x47,0xB6,0x22,0xDD,0x9E,0x44,0x27,0x20,0x54,0x84,0x30,0xB4,0x62,0x96,0x1F, +0xFE,0x5E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x27,0x20,0x54,0x96,0x1F,0xB4,0x62, +0x40,0x10,0x20,0x08,0x44,0x0F,0xF0,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E, +0x44,0x27,0x20,0x58,0x96,0x1F,0x40,0x10,0x40,0x08,0xB4,0x62,0x46,0x0F,0xFF,0x0F, +0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x04,0x00,0x9C, +0xA6,0x00,0xDD,0x9E,0x46,0x10,0x00,0x81,0x04,0x00,0x80,0x6E,0x58,0x00,0x00,0x01, +0x14,0x00,0x80,0x6E,0xDD,0x9E,0xFC,0x00,0x44,0x04,0x2C,0x1C,0x84,0x41,0xB4,0x20, +0x80,0x62,0x58,0x10,0x81,0x00,0xB6,0x20,0xB4,0x20,0x58,0x10,0x80,0x80,0xB6,0x20, +0x44,0x04,0x28,0x1C,0x44,0x14,0x24,0x1C,0x49,0xFF,0xFA,0x8E,0xFC,0x80,0x46,0x20, +0x00,0x80,0x84,0x30,0x50,0x41,0x01,0xD0,0xB4,0x64,0x96,0x1F,0xFE,0x5E,0xFE,0x47, +0xB6,0x24,0xB4,0x64,0x44,0x1F,0xFF,0x0F,0x94,0x04,0xFE,0x5E,0xFE,0x0F,0xB6,0x04, +0xDD,0x9E,0x00,0x00,0x44,0x24,0x80,0x88,0x84,0x3E,0xB4,0x62,0x96,0x06,0xFE,0x5E, +0xFE,0x47,0xB6,0x22,0xB4,0x62,0x84,0x3D,0x94,0x01,0xFE,0x5E,0xFE,0x0F,0xB6,0x02, +0xDD,0x9E,0xFC,0x00,0x44,0x64,0x00,0x18,0x84,0x21,0xAE,0x70,0xA6,0x32,0x44,0x20, +0x98,0x81,0x58,0x00,0x00,0x01,0xAE,0x32,0xA6,0x32,0x58,0x00,0x00,0x02,0xAE,0x32, +0xA6,0x32,0x58,0x00,0x00,0x04,0xAE,0x32,0xA6,0x33,0x58,0x00,0x00,0x02,0xAE,0x33, +0xA6,0x33,0x58,0x00,0x00,0x04,0xAE,0x33,0xA6,0x33,0x58,0x00,0x00,0x08,0xAE,0x33, +0xA6,0x33,0x58,0x00,0x00,0x10,0xAE,0x33,0x44,0x04,0x00,0x10,0xB6,0x40,0xAE,0x71, +0xA6,0x32,0x58,0x00,0x00,0x10,0xAE,0x32,0xA6,0x32,0x58,0x00,0x00,0x20,0xAE,0x32, +0xA6,0x72,0x44,0x0F,0xFF,0x80,0xFE,0x0F,0xAE,0x32,0xA6,0x33,0x54,0x00,0x00,0xFE, +0xAE,0x33,0xA6,0x33,0x58,0x00,0x00,0x20,0xAE,0x33,0xA6,0x33,0x58,0x00,0x00,0x40, +0xAE,0x33,0x49,0x00,0x64,0xF0,0xE6,0x03,0x44,0x14,0x50,0x10,0xA6,0x32,0xE9,0x10, +0x54,0x00,0x00,0xFB,0xAE,0x32,0xB4,0x01,0x92,0x08,0x40,0x00,0x20,0x08,0xB6,0x01, +0x44,0x14,0x70,0x00,0x44,0x0E,0xFF,0xFF,0xB4,0x41,0xFE,0x16,0xD5,0x11,0x58,0x00, +0x00,0x04,0xAE,0x32,0xB4,0x01,0x92,0x08,0x40,0x00,0x20,0x08,0x58,0x00,0x00,0x98, +0xB6,0x01,0x44,0x14,0x70,0x00,0x44,0x01,0x00,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01, +0xFC,0x80,0x44,0x37,0x20,0x44,0x46,0x0F,0xC0,0x0F,0xB4,0x23,0x50,0x00,0x0F,0xFF, +0xFE,0x46,0x46,0x20,0x24,0x00,0xFE,0x57,0xB6,0x23,0x44,0x37,0x20,0x48,0x44,0x23, +0x00,0x00,0xB4,0x23,0xFE,0x46,0xFE,0x57,0x44,0x27,0x20,0x4C,0xB6,0x23,0xB4,0x22, +0x46,0x30,0x0C,0x00,0x92,0x2C,0x40,0x10,0xB0,0x08,0x58,0x10,0x82,0x52,0xB6,0x22, +0xB4,0x22,0xFE,0x46,0xFE,0x5F,0xB6,0x22,0x44,0x27,0x20,0x50,0x46,0x30,0x00,0x90, +0xB4,0x22,0x92,0x2C,0x40,0x10,0xB0,0x08,0x58,0x10,0x82,0x40,0xB6,0x22,0xB4,0x22, +0xFE,0x0E,0xB6,0x02,0x44,0x27,0x20,0x04,0x46,0x0F,0xFF,0x0F,0xB4,0x22,0x50,0x00, +0x0F,0xFF,0xFE,0x46,0xFE,0x5F,0xB6,0x22,0xB4,0x62,0x46,0x1F,0xF0,0xFF,0x50,0x10, +0x8F,0xFF,0xFE,0x5E,0x46,0x30,0x03,0x00,0xFE,0x5F,0xB6,0x22,0x46,0x20,0x00,0x80, +0x44,0x1F,0xF0,0xFF,0x50,0x41,0x01,0xC0,0xB4,0x64,0xFE,0x5E,0x58,0x10,0x88,0x00, +0xB6,0x24,0xB4,0x24,0xFE,0x0E,0x44,0x12,0x00,0x00,0xFE,0x0F,0xB6,0x04,0xDD,0x9E, +0x46,0x20,0x00,0x91,0x40,0x00,0x40,0x08,0xA0,0x56,0x96,0x49,0xFE,0x0F,0xA8,0x16, +0xDD,0x9E,0xE6,0x04,0xE8,0x28,0x46,0x10,0x00,0x91,0x44,0xF0,0x18,0x48,0x38,0x07, +0x80,0x00,0x40,0xF0,0x3C,0x00,0xDD,0x0F,0x04,0x12,0x20,0x2E,0xA0,0x0A,0x92,0x10, +0x40,0x00,0x40,0x08,0x58,0x00,0x00,0x0A,0xD5,0x15,0xA0,0x0A,0x92,0x10,0x40,0x00, +0x40,0x08,0x58,0x00,0x00,0x46,0xD5,0x0E,0xA0,0x0A,0x92,0x10,0x40,0x00,0x40,0x08, +0x58,0x00,0x00,0x96,0xD5,0x07,0xA0,0x0A,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00, +0x01,0xF4,0xA8,0x0A,0xDD,0x9E,0x46,0x2F,0xFC,0x0F,0x46,0x10,0x00,0x91,0x50,0x21, +0x0F,0xFF,0x5A,0x08,0x01,0x08,0x04,0x00,0x80,0x0E,0xFE,0x86,0x44,0x02,0x00,0x00, +0xD5,0x06,0x04,0x00,0x80,0x0E,0xFE,0x86,0x44,0x01,0x00,0x00,0xFE,0x87,0x14,0x20, +0x80,0x0E,0xA0,0x09,0x92,0x06,0x94,0x06,0x58,0x00,0x00,0x01,0xA8,0x09,0xDD,0x9E, +0xFC,0x00,0x46,0x60,0x00,0x91,0x84,0x01,0xA8,0x31,0x44,0x00,0x01,0xF4,0xA8,0x32, +0x83,0x86,0xB9,0x0A,0x46,0x0F,0xC0,0x0F,0x50,0x00,0x0C,0x00,0xFE,0x46,0x58,0x10, +0x80,0x01,0xB9,0x8A,0xB9,0x0B,0xFE,0x0E,0x58,0x00,0x02,0x40,0xB8,0x8B,0xB9,0x0C, +0x44,0x0F,0xC0,0xC0,0xFE,0x0E,0x58,0x00,0x20,0x01,0xB8,0x8C,0xB9,0x0D,0x46,0x0E, +0x0E,0x0E,0x50,0x00,0x00,0xE0,0xFE,0x0E,0x46,0x10,0x60,0x11,0x50,0x10,0x82,0x01, +0xFE,0x0F,0xB8,0x8D,0xB9,0x0E,0x46,0x0F,0xFC,0x0C,0x50,0x00,0x00,0xF3,0xFE,0x0E, +0x44,0x11,0x01,0x0C,0xFE,0x0F,0xB8,0x8E,0x49,0xFF,0xFE,0x7A,0xE6,0x03,0xA0,0x36, +0x92,0x10,0x40,0x00,0x40,0x08,0xE9,0x1F,0x58,0x00,0x00,0x64,0xA8,0x36,0x83,0x86, +0xB8,0x08,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00,0x00,0x64,0xB8,0x88,0xA0,0x35, +0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00,0x00,0x64,0xA8,0x35,0xA0,0x37,0x92,0x10, +0x40,0x00,0x40,0x08,0x58,0x00,0x00,0x64,0xA8,0x37,0xA0,0x76,0x46,0x00,0x06,0x40, +0x96,0x49,0xD5,0x1E,0x58,0x00,0x46,0x00,0xA8,0x36,0x83,0x86,0xB8,0x08,0x92,0x10, +0x40,0x00,0x40,0x08,0x58,0x00,0x46,0x00,0xB8,0x88,0xA0,0x35,0x92,0x10,0x40,0x00, +0x40,0x08,0x58,0x00,0x46,0x00,0xA8,0x35,0xA0,0x37,0x92,0x10,0x40,0x00,0x40,0x08, +0x58,0x00,0x46,0x00,0xA8,0x37,0xA0,0x76,0x46,0x04,0x60,0x00,0x96,0x49,0xFE,0x47, +0xA8,0x76,0x83,0x86,0xB9,0x08,0x96,0x49,0xFE,0x47,0xB9,0x88,0xA0,0x75,0x96,0x49, +0xFE,0x47,0xA8,0x75,0xA0,0x77,0x96,0x49,0xFE,0x0F,0xA8,0x37,0x46,0x00,0x00,0x91, +0x83,0x80,0xB9,0x12,0x58,0x10,0xBF,0x3F,0xB9,0x92,0xBA,0x13,0x44,0x1E,0xE0,0x80, +0xFE,0x56,0x44,0x21,0x00,0x48,0xFE,0x57,0xB9,0x93,0xFC,0x80,0x46,0x00,0x00,0x91, +0x50,0x30,0x00,0x44,0xB4,0x23,0x58,0x10,0x80,0x01,0xB6,0x23,0xB4,0x43,0x84,0x3E, +0xFE,0x56,0xB6,0x23,0xDD,0x9E,0x92,0x00,0x44,0x17,0x30,0x10,0xB4,0x01,0x96,0x06, +0xC0,0xFE,0x44,0x17,0x30,0x10,0xB4,0x01,0x92,0x03,0x96,0x06,0x5A,0x00,0x01,0xFD, +0xDD,0x9E,0x84,0xA0,0xD0,0x05,0x40,0x00,0x00,0x09,0x8C,0xA1,0xD5,0xFC,0xDD,0x9E, +0x44,0x27,0x30,0x10,0xB4,0x22,0x92,0x21,0x96,0x46,0x5A,0x10,0x01,0xFD,0x44,0x17, +0x30,0x00,0xB6,0x01,0xDD,0x9E,0xFC,0x00,0x44,0x04,0x70,0x18,0x84,0x3B,0xB4,0x40, +0xFE,0x56,0xB6,0x20,0x40,0x00,0x00,0x09,0x40,0x00,0x00,0x09,0xB4,0x20,0x46,0x61, +0xFF,0xF9,0x58,0x10,0x80,0x02,0xB6,0x20,0x44,0x04,0x80,0x0A,0x84,0x24,0xAE,0x40, +0x44,0x04,0x80,0x88,0x44,0x14,0x00,0x00,0xB4,0x40,0xFE,0x57,0xB6,0x20,0xB4,0x40, +0x44,0x12,0x00,0x00,0xFE,0x57,0xB6,0x20,0x44,0x17,0x30,0x08,0xB4,0x01,0x92,0x05, +0x94,0x05,0x58,0x00,0x00,0x01,0xB6,0x01,0x50,0x03,0x05,0x27,0x49,0xFF,0xFF,0xCA, +0x50,0x03,0x04,0x87,0x46,0x61,0xFF,0xFF,0x49,0xFF,0xFF,0xC4,0x50,0x03,0x03,0xCC, +0x49,0xFF,0xFF,0xC0,0x80,0x06,0x49,0xFF,0xFF,0xBD,0x50,0x03,0x0F,0x06,0x49,0xFF, +0xFF,0xB9,0x49,0xFF,0xFF,0xA3,0x46,0x01,0xF0,0x40,0x50,0x00,0x01,0x50,0x49,0xFF, +0xFF,0xB1,0x49,0xFF,0xFF,0x9B,0x46,0x01,0xF0,0x90,0x50,0x00,0x01,0x08,0x49,0xFF, +0xFF,0xA9,0x49,0xFF,0xFF,0x93,0xFC,0x80,0xC9,0x08,0x44,0x24,0x00,0x0C,0x96,0x06, +0xA6,0x50,0x54,0x10,0x80,0xFE,0xD5,0x0A,0x5A,0x18,0x01,0x0B,0x44,0x24,0x00,0x0C, +0x96,0x06,0xA6,0x50,0x94,0x01,0x54,0x10,0x80,0xFD,0xFE,0x0F,0xAE,0x10,0xDD,0x9E, +0xC9,0x04,0x44,0x15,0x00,0x00,0xD5,0x05,0x5A,0x18,0x01,0x05,0x44,0x15,0x00,0x10, +0xAE,0x0A,0xDD,0x9E,0xC9,0x0A,0x44,0x24,0x80,0x80,0x96,0x06,0x40,0x10,0x60,0x08, +0xB4,0x62,0x46,0x0F,0xEF,0xFF,0xD5,0x0B,0x5A,0x18,0x01,0x0F,0x44,0x24,0x80,0x80, +0x96,0x06,0x40,0x10,0x64,0x08,0xB4,0x62,0x46,0x0F,0xDF,0xFF,0x50,0x00,0x0F,0xFF, +0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0xC9,0x04,0x44,0x15,0x00,0x04,0xD5,0x05, +0x5A,0x18,0x01,0x05,0x44,0x15,0x00,0x14,0xAC,0x08,0xDD,0x9E,0xC8,0x08,0x44,0x14, +0x80,0x03,0x84,0x01,0xAE,0x08,0x44,0x15,0x00,0x08,0xD5,0x09,0x5A,0x08,0x01,0x09, +0x44,0x14,0x80,0x03,0x84,0x42,0xAE,0x88,0x44,0x15,0x00,0x18,0xAE,0x09,0xDD,0x9E, +0xC8,0x08,0x44,0x04,0x80,0x03,0x84,0x21,0xAE,0x40,0x44,0x15,0x00,0x08,0xD5,0x09, +0x5A,0x08,0x01,0x0C,0x44,0x04,0x80,0x03,0x84,0x22,0xAE,0x40,0x44,0x15,0x00,0x18, +0xA6,0x08,0x58,0x00,0x00,0x02,0xAE,0x08,0xDD,0x9E,0xC9,0x04,0x44,0x15,0x00,0x00, +0xD5,0x05,0x5A,0x18,0x01,0x05,0x44,0x15,0x00,0x10,0xAE,0x08,0xDD,0x9E,0x84,0x21, +0xC0,0x04,0x5A,0x08,0x01,0x06,0x84,0x22,0x44,0x04,0x80,0x03,0xAE,0x40,0xDD,0x9E, +0x44,0x04,0x80,0x00,0xA6,0x03,0x92,0x01,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x03, +0x84,0x22,0xAE,0x40,0xDD,0x9E,0x46,0x20,0x00,0x81,0x84,0x3E,0xA0,0xD5,0x96,0x06, +0xFE,0x5E,0xFE,0x0F,0xA8,0x15,0xDD,0x9E,0x46,0x00,0x00,0x81,0xA0,0x05,0x96,0x06, +0xDD,0x9E,0x44,0x24,0x50,0x14,0x84,0x3E,0xB4,0x62,0x96,0x06,0xFE,0x5E,0xFE,0x0F, +0xB6,0x02,0xDD,0x9E,0x44,0x25,0x10,0x04,0xB4,0x22,0x92,0x30,0x40,0x10,0xC0,0x08, +0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x15,0x10,0x0C,0xB4,0x01,0x58,0x00,0x01,0x00, +0xB6,0x01,0xDD,0x9E,0x44,0x15,0x10,0x0C,0xB4,0x01,0x92,0x08,0x40,0x00,0x20,0x08, +0x58,0x00,0x00,0x01,0xB6,0x01,0x44,0x15,0x10,0x18,0xB4,0x01,0x96,0x00,0x5A,0x00, +0x5A,0xFE,0xDD,0x9E,0xFC,0x00,0x44,0x00,0x01,0x2C,0x49,0x00,0x00,0x6B,0x44,0x05, +0x10,0x0C,0x44,0x1F,0xFF,0x81,0xAE,0x40,0x44,0x1F,0xFF,0x98,0xAE,0x40,0x44,0x15, +0x10,0x18,0xB4,0x01,0x96,0x00,0x5A,0x00,0xA5,0xFE,0x44,0x15,0x10,0x0C,0xB4,0x01, +0x92,0x08,0x40,0x00,0x20,0x08,0xB6,0x01,0xFC,0x80,0xC0,0x04,0x5A,0x00,0x01,0x1F, +0xDD,0x9E,0x44,0x14,0x80,0x82,0x44,0x22,0x00,0x00,0xAE,0x08,0x44,0x14,0x80,0x00, +0x44,0x04,0x80,0x80,0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x44,0x24,0x00,0x00, +0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x46,0x20,0x01,0x00,0xB6,0x41,0xB4,0x60, +0xFE,0x9F,0xB6,0x40,0x46,0x20,0x00,0x80,0xD5,0x23,0x44,0x04,0x80,0x82,0x84,0x20, +0xAE,0x40,0x44,0x14,0x80,0x00,0x44,0x04,0x80,0x80,0x44,0x21,0x00,0x00,0xB6,0x41, +0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x44,0x22,0x00,0x00,0xB6,0x41,0xB4,0x60,0xFE,0x9F, +0xB6,0x40,0x44,0x24,0x00,0x00,0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x46,0x20, +0x00,0x80,0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x46,0x20,0x01,0x00,0xB6,0x41, +0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x46,0x20,0x02,0x00,0xB6,0x41,0xB4,0x60,0xFE,0x9F, +0xB6,0x40,0x46,0x20,0x04,0x00,0xB6,0x41,0xB4,0x20,0xFE,0x8F,0xB6,0x40,0xDD,0x9E, +0x84,0x2E,0xFE,0x0C,0x8E,0x01,0x5A,0x07,0xFF,0x05,0x40,0x00,0x00,0x09,0xD5,0xFB, +0xDD,0x9E,0x44,0x10,0x36,0xB0,0xFE,0x0C,0x8E,0x01,0x5A,0x07,0xFF,0x05,0x40,0x00, +0x00,0x09,0xD5,0xFB,0xDD,0x9E,0xFF,0xFF,0xFC,0x20,0x44,0x62,0x40,0x0C,0x44,0x72, +0x10,0x80,0x80,0x06,0x49,0x00,0x27,0xDF,0xFD,0x03,0x49,0x00,0x27,0xF8,0xFD,0x03, +0x50,0x23,0xFF,0xE8,0x50,0x33,0xFF,0xF4,0x00,0x43,0x7F,0xF8,0x49,0x00,0x29,0x47, +0xFC,0xA0,0xFC,0x00,0x84,0x20,0x44,0x20,0x08,0xC4,0x44,0x02,0x40,0x0C,0x49,0x00, +0x69,0x7F,0x49,0x00,0x2F,0xDB,0xFC,0x80,0xFC,0x00,0x49,0x00,0x59,0x1C,0x50,0x30, +0x04,0x80,0x3C,0x44,0x0A,0xB1,0x84,0x20,0x2A,0x20,0x00,0x01,0xE0,0x82,0xE8,0x05, +0x84,0x00,0x3E,0x07,0xED,0x20,0xD5,0x22,0x5E,0xF1,0x7F,0x38,0xE8,0x0B,0x8C,0x21, +0x5E,0xF1,0x7E,0x70,0x96,0x49,0xE8,0x06,0xE6,0x27,0x80,0x41,0xE8,0x02,0x84,0x47, +0x96,0x51,0x4C,0x01,0xFF,0xEB,0xE6,0x27,0xE9,0x08,0x84,0x01,0x3E,0x07,0xED,0x21, +0x2E,0x07,0xED,0x20,0x8C,0x01,0xD5,0x04,0x84,0x00,0x3E,0x07,0xED,0x21,0x3E,0x07, +0xED,0x20,0x2E,0x07,0xED,0x20,0xE6,0x02,0xE8,0x03,0x84,0x00,0xD5,0x07,0x49,0x00, +0x4A,0xB8,0xC8,0xFC,0x3E,0x07,0xED,0x20,0x84,0x01,0xFC,0x80,0xFC,0x00,0x49,0x00, +0x4A,0xB0,0xC8,0x04,0x49,0xFF,0xFF,0xC2,0xFC,0x80,0x84,0x00,0xFC,0x80,0xFC,0x40, +0x00,0x7F,0x80,0x24,0x02,0x8F,0x80,0x10,0xB4,0xC0,0x5A,0x70,0x01,0x07,0xA5,0x0C, +0xA4,0xCA,0x02,0x80,0x80,0x05,0xA5,0x4B,0xE0,0xC4,0xE8,0x2A,0xE0,0x66,0x81,0x46, +0x40,0x92,0x0C,0x01,0x40,0xA1,0xBC,0x1A,0x80,0xA9,0x4E,0x94,0x00,0x04,0x52,0x54, +0x80,0x00,0x40,0x21,0xA8,0x01,0x4E,0x24,0x00,0x03,0xFE,0x92,0x22,0x70,0x80,0x00, +0x40,0x35,0x0C,0x01,0x9A,0x67,0xFE,0xCC,0x40,0x31,0xA4,0x76,0x88,0x67,0xE0,0x67, +0xE9,0x05,0xE0,0x83,0x40,0x41,0xBC,0x1A,0x80,0xE4,0x9A,0x6A,0xFF,0x94,0x42,0x23, +0x84,0x24,0x40,0x63,0x14,0xD6,0x40,0x21,0x14,0x56,0x88,0xC2,0xD5,0x31,0x40,0x91, +0x20,0x01,0xE1,0x26,0xE8,0x30,0x9A,0xD5,0xE0,0xC3,0x80,0x86,0x40,0x74,0x14,0x01, +0x40,0x41,0xBC,0x1A,0x4E,0x74,0x00,0x03,0xFF,0xFA,0x8A,0x64,0x4E,0x34,0x00,0x04, +0x9A,0xEA,0x88,0x64,0x22,0xA0,0x80,0x01,0x9A,0x54,0x8A,0x25,0x40,0x44,0x28,0x01, +0xFE,0x64,0x8A,0xA8,0x40,0x50,0x94,0xB6,0x8A,0xAA,0x88,0xA2,0xE0,0xA9,0xE9,0x06, +0x8A,0x4A,0xE0,0x45,0x40,0x22,0xBC,0x1A,0x81,0x22,0x9A,0x7B,0x42,0x43,0x0C,0x24, +0x42,0x34,0x84,0x24,0x40,0x42,0x1C,0x96,0x40,0x31,0x9C,0x76,0x99,0xA3,0xB6,0xC0, +0x84,0x01,0xFC,0xC0,0x84,0x00,0xFC,0xC0,0x3C,0x03,0xF6,0x92,0x96,0x06,0x4E,0x02, +0x00,0xC4,0xFC,0x45,0x84,0xE0,0x44,0x92,0x40,0x0C,0x80,0xC7,0xF7,0x83,0xF7,0x84, +0x83,0x87,0x00,0x04,0x80,0xAD,0x9E,0x41,0xE6,0x22,0xE9,0x05,0x5A,0x00,0x04,0x04, +0x48,0x00,0x00,0xAB,0xB0,0x49,0x80,0x09,0x84,0x40,0x49,0x00,0x00,0xDE,0x44,0x00, +0x00,0xCC,0x44,0x12,0x40,0xCC,0x42,0x13,0x80,0x73,0xA0,0x09,0x40,0x10,0x30,0x09, +0x54,0x10,0x80,0x2F,0xE6,0x22,0xE8,0x24,0x40,0x00,0x20,0x08,0x90,0x1A,0xC8,0x20, +0x3C,0x17,0xF6,0x02,0x84,0x0D,0x84,0xCA,0xFE,0x44,0x40,0x10,0x98,0x36,0x41,0xC0, +0x80,0x13,0x3C,0x17,0xF6,0x04,0x85,0x01,0xFE,0x44,0x40,0x10,0x98,0x36,0x96,0x49, +0xF1,0x83,0x3C,0x17,0xF6,0x03,0xFE,0x44,0x40,0x10,0x98,0x36,0x96,0x49,0xF1,0x84, +0x3C,0x17,0xF6,0x05,0xFE,0x0C,0x40,0x60,0x18,0xD6,0x97,0xB1,0xD5,0x02,0x85,0x00, +0xF0,0x09,0x44,0x12,0x10,0x68,0x80,0x7C,0x22,0x00,0x00,0x04,0xF4,0x03,0xF0,0x87, +0xB6,0xDF,0x3C,0xA3,0x66,0x3C,0x14,0x8F,0x80,0x01,0x80,0x4A,0xB0,0x07,0xF5,0x04, +0x49,0xFF,0xFF,0x37,0xF0,0x85,0xF1,0x07,0x3C,0x07,0xF6,0x00,0xF2,0x09,0xE0,0x20, +0xE9,0x09,0x3C,0x07,0xF6,0x01,0x40,0x05,0x00,0x01,0xE0,0x20,0x40,0x00,0xBC,0x1B, +0x96,0x03,0xAC,0x14,0x5A,0x88,0x01,0x1F,0x3C,0x17,0xF6,0x08,0x84,0x0B,0x84,0xCA, +0xFE,0x44,0x40,0x10,0x98,0x36,0x41,0xC0,0x80,0x13,0x3C,0x17,0xF6,0x0A,0xFE,0x44, +0x40,0x10,0x98,0x36,0x96,0x49,0xF1,0x83,0x3C,0x17,0xF6,0x09,0xFE,0x44,0x40,0x10, +0x98,0x36,0x96,0x49,0xF1,0x84,0x3C,0x17,0xF6,0x0B,0xFE,0x0C,0x40,0x60,0x18,0xD6, +0x97,0xB1,0x22,0x01,0x00,0x05,0x44,0x12,0x10,0x74,0xF0,0x88,0x14,0x8F,0x80,0x01, +0x3C,0xA3,0x66,0x3D,0xB6,0xDF,0x80,0x4A,0xB0,0x08,0x80,0x7C,0xF4,0x03,0xF5,0x04, +0x49,0xFF,0xFE,0xF7,0x81,0x00,0xF2,0x08,0x3C,0x07,0xF6,0x06,0xF1,0x09,0xE0,0x40, +0xE9,0x09,0x3C,0x07,0xF6,0x07,0x40,0x05,0x00,0x01,0xE0,0x40,0x40,0x01,0x3C,0x1B, +0x96,0x03,0xAC,0x0D,0x80,0x09,0x49,0x00,0x00,0x34,0xF0,0x05,0xC8,0x03,0x4E,0x82, +0x00,0x0C,0x44,0x00,0x00,0xCC,0x44,0x12,0x40,0xD4,0x42,0x13,0x80,0x73,0xB4,0x01, +0x58,0x00,0x00,0x20,0xB6,0x01,0x8C,0xE1,0x50,0x94,0x80,0xCC,0x5A,0x70,0x0B,0x04, +0x48,0xFF,0xFF,0x49,0xFC,0xC5,0xDD,0x9E,0x04,0x30,0x00,0x32,0x96,0xD7,0xE4,0x65, +0xE8,0x15,0xFC,0x00,0xFA,0x4C,0x80,0xC0,0x42,0x01,0x88,0x73,0x49,0x00,0x67,0xD2, +0x04,0x23,0x00,0x32,0x84,0x18,0x9C,0x51,0x96,0x57,0xE6,0x25,0xFE,0x16,0xE8,0x02, +0xFE,0x0F,0x14,0x03,0x00,0x32,0x84,0x01,0xFC,0x80,0x84,0x00,0xDD,0x9E,0x04,0x20, +0x00,0x32,0x96,0x97,0xE4,0x45,0xE8,0x0E,0xFC,0x00,0x9E,0xD1,0x96,0xD8,0x5A,0x38, +0xFF,0x03,0x9C,0xD4,0xFA,0x4C,0x42,0x01,0x88,0x73,0x49,0x00,0x67,0xB3,0x84,0x01, +0xFC,0x80,0x84,0x00,0xDD,0x9E,0xE6,0x45,0xE8,0x13,0x04,0x30,0x00,0x32,0x96,0xD7, +0x9A,0x9A,0x96,0x90,0x9E,0xD1,0x96,0xD8,0x97,0x1A,0x4E,0x44,0x00,0x04,0x8C,0x44, +0x96,0xD0,0xFA,0x4C,0x42,0x01,0x88,0x73,0xB6,0x01,0x84,0x01,0xDD,0x9E,0x84,0x00, +0xDD,0x9E,0xFC,0x40,0xFB,0x44,0x80,0xE0,0x44,0x80,0x00,0x64,0x52,0x05,0x00,0x14, +0x96,0x00,0x84,0xC0,0x5C,0x90,0x00,0x14,0x49,0x00,0x5F,0xE5,0xE6,0x03,0xE9,0x06, +0x38,0x03,0x99,0x01,0x94,0x01,0x38,0x03,0x99,0x09,0x4E,0x92,0x00,0x0B,0x38,0x03, +0x99,0x11,0x42,0x15,0x00,0x24,0x40,0x10,0xA0,0x36,0x88,0x01,0x38,0x03,0x99,0x09, +0x8C,0xC1,0x5A,0x68,0x12,0xEB,0x8F,0x41,0x50,0x73,0x80,0x24,0x5A,0xAF,0xF4,0xE0, +0xFC,0xC0,0xFC,0x40,0xFF,0x92,0x81,0x40,0x80,0xE1,0x80,0x01,0x81,0x22,0x84,0x20, +0x44,0x20,0x00,0x40,0x49,0x00,0x67,0x8C,0x97,0xB3,0x84,0x80,0x84,0x49,0xFA,0x22, +0x83,0x8A,0x42,0x82,0x08,0x24,0x43,0xC2,0x04,0x73,0x84,0x00,0x38,0x3E,0x01,0x11, +0x97,0x40,0xE0,0x69,0xE9,0x06,0x96,0x2A,0x4E,0x05,0x00,0x20,0xFA,0x61,0xD5,0x12, +0xE0,0xC3,0xE8,0xFA,0x8C,0x01,0x5A,0x08,0x12,0xF3,0xD5,0x17,0x40,0xF1,0xA0,0x00, +0x39,0xC5,0x3D,0x11,0x40,0xFE,0x24,0x07,0xE8,0x08,0xE0,0xDC,0xE8,0x06,0x8E,0x61, +0x96,0xD8,0xE0,0x60,0xE8,0xF4,0xD5,0x03,0x96,0xDA,0xD5,0x02,0x84,0x7F,0x98,0x3C, +0x8C,0x61,0x38,0x53,0x90,0x08,0xAE,0xC1,0x8C,0x82,0x5A,0x48,0x40,0xD3,0xFC,0xC0, +0xFC,0x42,0x84,0xA0,0x80,0xE5,0x81,0x25,0xA7,0x89,0x55,0xC2,0x80,0xFF,0xF6,0x82, +0xFA,0xC2,0xFF,0xAC,0xF6,0x83,0x2E,0x67,0xED,0x2E,0xA7,0x08,0x2F,0xE7,0xED,0x2F, +0xF6,0x81,0x2E,0x87,0xED,0x2D,0x2E,0xA7,0xED,0x2C,0xF6,0x02,0xE2,0x86,0xE8,0x13, +0xF6,0x03,0x88,0xC4,0x38,0x60,0x19,0x11,0xE1,0x26,0xE8,0x05,0x15,0xCF,0x80,0x01, +0x83,0xC4,0x81,0x26,0xE0,0xC7,0xE8,0x04,0x81,0x5C,0x81,0x04,0x80,0xE6,0x8C,0x81, +0x97,0x20,0xD5,0xEC,0x00,0x4F,0x80,0x04,0x8C,0xA1,0x3F,0xE7,0xED,0x2F,0x3E,0x47, +0xED,0x2E,0x3E,0x87,0xED,0x2D,0x3E,0xA7,0xED,0x2C,0x8C,0x22,0x5A,0x58,0x20,0xCE, +0x12,0x91,0x00,0x00,0xAD,0xD8,0xFC,0xC2,0xFC,0x41,0x80,0xC1,0x85,0x00,0xFA,0x2F, +0x80,0xE0,0x81,0x22,0x80,0x02,0x44,0x20,0x02,0x40,0xF3,0x81,0x81,0x44,0x3C,0x8F, +0xFB,0x4A,0x49,0x00,0x67,0x0D,0x80,0x26,0x80,0x68,0x80,0x48,0x80,0x08,0x84,0x80, +0xA7,0x88,0xE2,0x86,0xE8,0x19,0x99,0x63,0x3C,0x6D,0xFB,0x4A,0x38,0x53,0x95,0x11, +0x8C,0x81,0x88,0xA6,0x3C,0x5F,0xFB,0x4A,0x97,0x20,0xD5,0xF3,0x41,0xE3,0x0C,0x00, +0x38,0x43,0xF9,0x11,0x88,0xA4,0x3C,0x5F,0xFB,0x4A,0xF5,0x01,0x89,0x04,0xE0,0xA4, +0xE9,0x09,0x8C,0xC1,0x97,0xB0,0xA7,0x09,0x3C,0x5D,0xFB,0x4A,0xE2,0xC4,0xE9,0xEF, +0xD5,0x0F,0x8C,0x41,0xE1,0x44,0x96,0x93,0xE8,0x04,0x44,0x40,0x00,0xDF,0xD5,0x03, +0x44,0x40,0x00,0x5F,0x8C,0x01,0x38,0x44,0xF8,0x08,0x96,0x01,0xD5,0xEB,0xE6,0x92, +0xE8,0x08,0x99,0xA3,0x8C,0x81,0x38,0x63,0x99,0x11,0x97,0x20,0x88,0xA6,0xD5,0xF8, +0x8C,0x72,0x3C,0x5F,0xFB,0x4A,0x8C,0x22,0x5A,0x3A,0x40,0xC3,0x3C,0x8F,0xFB,0xB9, +0x3C,0x2B,0xF7,0x74,0xFC,0xC1,0xFC,0x48,0xFA,0x94,0x50,0x50,0x00,0x24,0x84,0x20, +0x80,0x61,0x44,0x60,0x7F,0xFF,0x42,0x20,0x90,0x24,0x38,0x20,0x08,0x11,0x38,0x2F, +0x85,0x09,0xE0,0x46,0x40,0x61,0x3C,0x1B,0x8C,0x21,0xE0,0x62,0x40,0x31,0x3C,0x1B, +0x5A,0x18,0x20,0xF3,0x3C,0x14,0x0A,0xD3,0xE0,0x23,0xE9,0x05,0x8C,0x02,0x4C,0x02, +0xFF,0xE8,0xFC,0xC8,0x84,0x60,0x80,0x43,0x80,0xE3,0x38,0x9F,0x8D,0x11,0x40,0xA4, +0x98,0x01,0xE1,0x41,0xE8,0x04,0x8C,0x41,0x88,0xE9,0x96,0x90,0x8C,0x61,0x5A,0x38, +0x20,0xF6,0xCA,0x02,0x84,0x41,0x40,0x11,0x04,0x09,0x88,0x27,0x40,0x10,0x88,0x36, +0x5E,0xF0,0x80,0xC9,0xE9,0x04,0x44,0x10,0x00,0xC8,0xD5,0x04,0x4E,0x14,0x00,0x03, +0x84,0x20,0x40,0x60,0x84,0x0A,0x97,0xB1,0x84,0x60,0x3C,0x74,0x0A,0xD3,0x38,0x2F, +0x8D,0x11,0x88,0xE1,0xE0,0xE2,0x42,0x71,0x90,0x24,0xE8,0x03,0x8A,0x46,0xD5,0x02, +0x8A,0x41,0x8C,0x61,0x38,0x20,0x1C,0x09,0x5A,0x38,0x20,0xF1,0xD5,0xC8,0xFC,0x4C, +0x80,0xC0,0x3C,0x00,0x0A,0xB8,0xB0,0x53,0xF0,0x8D,0x3C,0x00,0x0A,0xB9,0x44,0x72, +0x3D,0x44,0xF0,0x8E,0x3C,0x00,0x0A,0xBA,0xF0,0x8F,0x3C,0x00,0x0A,0xBB,0xF0,0x90, +0x44,0x00,0xF0,0xF0,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24,0xA4,0x00,0xAC,0x08, +0x49,0x00,0x55,0xF9,0xF0,0x8A,0x84,0x20,0x44,0x02,0x3D,0x44,0x44,0x20,0x02,0x40, +0x49,0x00,0x66,0x4E,0x84,0x00,0x3C,0x0B,0xF6,0x56,0x44,0x00,0x7F,0xFF,0x3C,0x0B, +0xF6,0x52,0x84,0x20,0xF0,0x0A,0x95,0x09,0x88,0x80,0x99,0x4F,0x84,0x00,0x3C,0x33, +0xF6,0x56,0x38,0x22,0x01,0x11,0x40,0xF1,0x80,0x11,0xE0,0x4F,0x40,0x31,0x3C,0x1A, +0x3C,0x3B,0xF6,0x56,0x3C,0x33,0xF6,0x52,0x40,0x91,0x80,0x11,0xE1,0x22,0x40,0x31, +0x3C,0x1A,0x5E,0xF1,0x00,0xFB,0x3C,0x3B,0xF6,0x52,0xE9,0x07,0x38,0x22,0x80,0x00, +0x58,0x21,0x00,0x40,0x38,0x22,0x80,0x08,0x8C,0x01,0x5A,0x08,0x12,0xE2,0x8C,0x32, +0x5A,0x1A,0x40,0xDA,0x85,0x20,0x44,0x82,0x11,0x97,0x44,0xA2,0x3D,0x44,0xFA,0x02, +0x42,0xA4,0x80,0x73,0x84,0xE0,0x38,0x05,0x1C,0x00,0x96,0x36,0xC0,0x3C,0x84,0x08, +0xF0,0x82,0x50,0x0F,0x80,0x4A,0xF0,0x84,0x84,0x01,0x84,0x60,0xF0,0x85,0x44,0x02, +0x11,0x96,0xF0,0x87,0xB6,0x7F,0xF3,0x81,0xF3,0x83,0x14,0x8F,0x80,0x06,0xF3,0x88, +0xF3,0x89,0x96,0x3B,0x40,0x14,0x80,0x11,0x44,0x20,0x00,0x40,0x44,0x42,0x3D,0x44, +0xF5,0x0A,0x49,0x00,0x2A,0xA2,0xA6,0x30,0x8C,0x01,0x96,0x00,0xE6,0x03,0xAE,0x30, +0xE8,0x12,0x44,0x12,0x11,0x68,0x8E,0x01,0x2E,0x27,0xED,0x2F,0x38,0x20,0x81,0x08, +0xA6,0x30,0x8E,0x01,0x94,0x01,0x88,0x01,0x2E,0x17,0xED,0x2E,0xAE,0x41,0xA6,0x30, +0x3C,0x0B,0xF6,0xA0,0x02,0x0F,0x80,0x25,0x5C,0xF0,0x00,0x29,0xE9,0x04,0x84,0x01, +0x3E,0x07,0xED,0x38,0x8C,0xE1,0x5A,0x78,0x12,0xC0,0x8D,0x21,0x5A,0x98,0x20,0xB7, +0x3C,0xF7,0xF6,0x56,0x5E,0xF7,0x80,0xC8,0xE8,0x04,0x84,0x00,0x3E,0x07,0xED,0x38, +0x2E,0x07,0xED,0x38,0x5A,0x08,0x01,0x04,0x48,0x00,0x00,0x8C,0xA6,0x30,0xE6,0x03, +0x4E,0xF2,0x00,0x88,0x4E,0x02,0x00,0x87,0x22,0x1F,0x80,0x1A,0x3C,0x03,0xF6,0xA0, +0xF1,0x8D,0xF2,0x0D,0x22,0x1F,0x80,0x1C,0xF0,0x91,0x8A,0x22,0x3C,0x0D,0xFB,0x3C, +0x23,0xEF,0x80,0x1E,0x80,0xE1,0x22,0x1F,0x80,0x20,0xF0,0x8B,0x3C,0x0D,0xFB,0x3B, +0x8A,0x3E,0xF0,0x8C,0x3C,0x8D,0xFB,0x39,0x3C,0x9D,0xFB,0x3E,0x84,0x00,0xF1,0x90, +0xF1,0x11,0xE2,0x01,0xE8,0x5A,0x44,0x12,0x11,0x68,0x44,0x22,0x11,0x68,0x38,0x10, +0x81,0x10,0xF1,0x8E,0x94,0x41,0x88,0x22,0x84,0x40,0x20,0x10,0x80,0x01,0x80,0x62, +0xF1,0x8F,0x81,0x42,0x80,0x22,0xB1,0x13,0x38,0x52,0x08,0x10,0xF4,0x0E,0x88,0xA4, +0xB1,0x13,0x88,0x82,0x40,0xF2,0x80,0x13,0x5C,0xF7,0x80,0x12,0x20,0x42,0x00,0x01, +0xE8,0x25,0xF6,0x0F,0x88,0x86,0x40,0xF2,0x00,0x13,0x5C,0xF7,0x80,0x20,0xE8,0x1E, +0x81,0xE5,0xFA,0xC2,0x42,0xF2,0x18,0x73,0xF6,0x0A,0x38,0xF3,0x3D,0x11,0xF6,0x0D, +0x42,0x63,0x94,0x73,0x80,0xA6,0xFA,0xC1,0x40,0x52,0x98,0xB6,0x97,0x6B,0xF6,0x10, +0x42,0x17,0x94,0x73,0x80,0xBE,0x42,0x53,0x10,0x73,0x80,0x85,0xFA,0xAF,0x40,0x42, +0x14,0x96,0x97,0x23,0x88,0x6F,0x42,0xA7,0x90,0x73,0x8C,0x42,0x5A,0x28,0x12,0xCD, +0x40,0x21,0x84,0x0A,0x88,0x22,0xC8,0x07,0x89,0x42,0x40,0x80,0x8D,0x16,0x40,0x95, +0x0D,0x36,0xD5,0x08,0x40,0x10,0x8C,0x36,0x89,0x42,0xF1,0x8B,0x40,0x15,0x0C,0x36, +0xF1,0x8C,0x8C,0x01,0x96,0x00,0xD5,0xA5,0xF0,0x0B,0x3C,0x0F,0xFB,0x3C,0xF0,0x0C, +0x3C,0x0F,0xFB,0x3B,0x3C,0x8F,0xFB,0x39,0x3C,0x9F,0xFB,0x3E,0x84,0x01,0xD5,0x02, +0x84,0x02,0xFC,0xCC,0xFC,0x00,0x3C,0x17,0xF6,0x5A,0x5E,0xF0,0x81,0xCA,0xE9,0x0B, +0x3C,0x03,0x66,0x3C,0x50,0xF0,0x7F,0x57,0xE0,0x2F,0xE8,0x05,0x2E,0x07,0xED,0x3E, +0x5A,0x08,0x01,0x06,0x84,0x00,0x3E,0x07,0xED,0x3E,0xD5,0x40,0x2E,0x07,0xED,0x42, +0xC0,0x04,0x5A,0x00,0x01,0x15,0xD5,0x3C,0x3C,0x27,0xEF,0x33,0x3C,0x17,0xEF,0x32, +0x88,0x22,0x90,0x21,0x3C,0x1F,0xFB,0x4C,0x3C,0x27,0xEF,0x35,0x3C,0x17,0xEF,0x34, +0x88,0x22,0x90,0x21,0x3C,0x1F,0xFB,0x4D,0x84,0x21,0xD5,0x21,0x3C,0x13,0xF6,0xA0, +0xE6,0x22,0xE9,0x03,0x84,0x00,0xD5,0x22,0x3C,0x17,0xEF,0x32,0x3C,0x37,0xEF,0x33, +0x3C,0x27,0xEF,0x35,0x88,0x61,0x3C,0x17,0xEF,0x34,0x3C,0x4D,0xFB,0x4C,0x88,0x41, +0x3C,0x1D,0xFB,0x4D,0x90,0x41,0x90,0x61,0x8A,0x22,0x8A,0x83,0xFE,0x4C,0x42,0x12, +0x10,0x73,0x44,0x44,0x20,0x3F,0xE0,0x81,0xE9,0x05,0x84,0x20,0x3E,0x17,0xED,0x42, +0xD5,0x08,0x3C,0x3F,0xFB,0x4C,0x3C,0x2F,0xFB,0x4D,0x3E,0x07,0xED,0x42,0x84,0x00, +0xFC,0x80,0xFC,0x00,0x84,0xC0,0x49,0x00,0x02,0xBE,0x84,0x03,0x84,0x20,0x3E,0x67, +0xED,0x43,0x49,0x00,0x5F,0x8B,0x84,0x04,0x84,0x20,0x49,0x00,0x5F,0x87,0x84,0x08, +0x84,0x20,0x49,0x00,0x5F,0x83,0x84,0x09,0x84,0x20,0x49,0x00,0x5F,0x7F,0x84,0x0A, +0x84,0x20,0x49,0x00,0x5F,0x7B,0x3E,0x67,0xED,0x42,0xFC,0x80,0xFC,0x00,0x49,0x00, +0x54,0x62,0x50,0x30,0x04,0x80,0x84,0x20,0x84,0x40,0x38,0x40,0x09,0x11,0x8C,0x41, +0x88,0x24,0x5A,0x28,0x12,0xFC,0x50,0x00,0x00,0x24,0x4C,0x01,0xFF,0xF7,0x44,0xF0, +0x02,0x40,0x40,0x10,0xBC,0x36,0x3C,0xF7,0xF6,0x56,0x8A,0x2F,0x50,0xF0,0x80,0x4F, +0x5C,0xF7,0x80,0x9F,0xE8,0x09,0x3C,0x0D,0xFB,0x37,0x58,0x00,0x00,0x40,0x3C,0x0F, +0xFB,0x37,0x84,0x01,0xFC,0x80,0x84,0x00,0xFC,0x80,0x00,0x00,0xFC,0x22,0x2E,0x67, +0xED,0x43,0x80,0xE0,0x84,0x00,0x10,0x0F,0x80,0x0F,0xCE,0x0C,0x84,0x04,0x49,0x00, +0x5F,0x60,0x5A,0x08,0x01,0x08,0x84,0x04,0x80,0x26,0x49,0x00,0x5F,0x3F,0x3E,0x67, +0xED,0x42,0x50,0x0F,0x80,0x0F,0x49,0xFF,0xFE,0x14,0x80,0xC0,0x2E,0x07,0xED,0x43, +0xE6,0x05,0x4E,0xF2,0x00,0xC8,0x44,0xF0,0x27,0xB4,0x38,0x07,0x80,0x00,0x40,0xF0, +0x3C,0x00,0xDD,0x0F,0x06,0x94,0xE0,0xC6,0xD8,0x00,0x49,0x00,0x02,0x5C,0x5A,0x68, +0x01,0x37,0x49,0x00,0x02,0xCC,0x44,0x70,0x03,0xE8,0x44,0x30,0x07,0xFF,0x44,0x20, +0x37,0xC1,0x80,0x83,0x80,0xA6,0x44,0x02,0x00,0x08,0x44,0x10,0x1A,0x69,0xB6,0xFF, +0x49,0x00,0x02,0xC5,0x84,0x08,0x44,0x10,0x0B,0xB8,0x49,0x00,0x5F,0x01,0x84,0x09, +0x80,0x27,0x49,0x00,0x5E,0xFD,0x84,0x0A,0x44,0x10,0x13,0x88,0x49,0x00,0x5E,0xF8, +0x84,0x08,0x80,0x26,0x49,0x00,0x5F,0x02,0x84,0x09,0x80,0x26,0x49,0x00,0x5E,0xFE, +0x84,0x0A,0x80,0x26,0x49,0x00,0x5E,0xFA,0x3C,0x07,0xF6,0x72,0x3C,0x17,0xF6,0x7C, +0x49,0x00,0x02,0xC6,0x3E,0x67,0xED,0x43,0x48,0x00,0x00,0x85,0x5A,0x60,0x02,0x04, +0x48,0x00,0x00,0x81,0x84,0x0A,0x44,0x10,0x13,0x88,0x49,0x00,0x5E,0xD9,0x84,0x0A, +0x84,0x21,0x49,0x00,0x5E,0xE3,0xD5,0x0F,0x84,0x0A,0x49,0x00,0x5E,0xFA,0x5A,0x08, +0x01,0x04,0x84,0x04,0xD5,0x09,0xCE,0x05,0x84,0x02,0x3E,0x07,0xED,0x43,0xD5,0x6B, +0x5A,0x68,0x02,0x06,0x84,0x03,0x3E,0x07,0xED,0x43,0xD5,0x64,0x3C,0x07,0xF6,0x72, +0x3C,0x17,0xF6,0x7C,0x49,0x00,0x02,0x9C,0xD5,0x5D,0x84,0x0A,0x49,0x00,0x5E,0xE1, +0x5A,0x00,0x01,0xE9,0xCE,0x57,0x49,0xFF,0xFF,0x2E,0xD5,0x54,0x49,0xFF,0xFF,0x48, +0xC0,0xFA,0xD5,0xFA,0x49,0x00,0x02,0x78,0xC0,0x27,0x84,0x09,0x49,0x00,0x5E,0xD1, +0x80,0xC0,0xC0,0x03,0x84,0xC0,0xD5,0x33,0x49,0xFF,0xFE,0xC6,0x5A,0x08,0x01,0x05, +0xAF,0xB8,0x80,0xC0,0xD5,0x2C,0x84,0x04,0x44,0x10,0x00,0x96,0x49,0x00,0x5E,0x98, +0x84,0x04,0x84,0x21,0x49,0x00,0x5E,0xA2,0x84,0x09,0x80,0x26,0x49,0x00,0x5E,0x9E, +0x84,0x08,0x80,0x26,0x49,0x00,0x5E,0x9A,0x84,0x0A,0x80,0x26,0x49,0x00,0x5E,0x96, +0x3E,0x67,0xED,0x43,0xD5,0x28,0x84,0x08,0x49,0x00,0x5E,0xAB,0xC8,0xDC,0x80,0x07, +0x49,0x00,0x03,0xF9,0x8E,0x01,0x5C,0x60,0x00,0x01,0x2E,0x07,0xED,0x3B,0xC0,0x07, +0x80,0x07,0x49,0x00,0x01,0x9D,0xC0,0xCF,0x84,0x0E,0xAE,0x38,0x80,0x07,0x49,0x00, +0x08,0x43,0x44,0x12,0x3A,0x6C,0xFA,0x58,0x44,0x02,0x3A,0x10,0x49,0x00,0x01,0xEC, +0x3E,0x07,0xEC,0x9B,0x49,0x00,0x02,0x15,0x3E,0x07,0xEC,0x99,0x49,0xFF,0xFE,0xDB, +0xD5,0x02,0x84,0xC0,0x80,0x06,0xFC,0xA2,0xFC,0x00,0x84,0x40,0x44,0x52,0x11,0xB4, +0x44,0x40,0x00,0x40,0x2E,0x37,0xED,0x6C,0xE2,0x43,0xE8,0x0D,0x38,0x32,0x88,0x00, +0x88,0x61,0x50,0x31,0x80,0x40,0x40,0x31,0x90,0xD6,0x38,0x60,0x08,0x08,0x8C,0x41, +0x96,0x90,0xD5,0xF1,0xFC,0x80,0x2E,0x17,0xED,0x6C,0x44,0x22,0x11,0xB4,0x38,0x01, +0x04,0x08,0x9C,0x09,0x96,0x00,0x5C,0xF0,0x00,0x27,0x3E,0x07,0xED,0x6C,0xE8,0x0E, +0x44,0x12,0x12,0xD4,0x3C,0x23,0xF6,0x5A,0x38,0x20,0x81,0x09,0x44,0x12,0x12,0x84, +0x3C,0x23,0xF6,0x67,0x38,0x20,0x81,0x09,0xDD,0x9E,0x44,0x00,0x7F,0xFF,0x3C,0x0B, +0xF7,0x5D,0x3C,0x0B,0xF7,0x35,0xDD,0x9E,0xFC,0x00,0x44,0x50,0xF1,0x08,0x84,0x60, +0x96,0x98,0x0A,0x62,0x80,0x01,0x5A,0x20,0x10,0x07,0xE2,0x26,0xA5,0x28,0xE9,0x19, +0x81,0xE4,0xD5,0x04,0xE2,0x26,0xE9,0x1B,0x85,0xFF,0xE2,0x2F,0xE8,0x12,0x5A,0x08, +0x01,0x06,0x52,0x01,0x80,0x40,0x96,0x2F,0xFC,0x80,0x5A,0x08,0x02,0x05,0x50,0x01, +0x00,0x20,0xD5,0x05,0x5A,0x08,0x03,0x0A,0x52,0x01,0x00,0x20,0x96,0x00,0xFC,0x80, +0x8C,0x61,0x5A,0x38,0x11,0xDF,0xD5,0x03,0x80,0x02,0xFC,0x80,0x44,0x00,0x00,0xFF, +0xFC,0x80,0x3C,0x57,0xEF,0x35,0x3C,0x27,0xEF,0x34,0xD2,0x08,0x44,0x10,0x07,0xD0, +0xFE,0x44,0x9A,0x2A,0x40,0x00,0x80,0x16,0xDD,0x9E,0x84,0x00,0xDD,0x9E,0xC9,0x0D, +0x3C,0x57,0xEF,0x33,0x3C,0x37,0xEF,0x32,0xD3,0x0E,0x44,0x20,0x07,0xD0,0xFE,0x84, +0x9A,0x2B,0x40,0x01,0x00,0x16,0xDD,0x9E,0x5A,0x18,0x01,0x08,0xFC,0x00,0x49,0xFF, +0xFF,0xE2,0xFC,0x80,0x80,0x01,0xDD,0x9E,0x84,0x00,0xDD,0x9E,0x94,0x4E,0xE6,0x69, +0x88,0x41,0x84,0x20,0xE8,0x0C,0xCB,0x04,0x44,0x10,0x00,0x64,0xD5,0x08,0x8C,0x61, +0x44,0x10,0xF1,0x04,0x90,0x61,0x88,0x61,0x00,0x11,0xFF,0xFF,0x38,0x10,0x09,0x09, +0xDD,0x9E,0xFC,0x40,0xEE,0xB0,0xF1,0x83,0x94,0x41,0xE0,0x22,0xF0,0x82,0xE9,0x07, +0x40,0x10,0x88,0x36,0xC1,0x05,0x8C,0x21,0x96,0x48,0xD5,0x02,0x84,0x21,0x42,0xF1, +0x04,0x24,0x5E,0xF7,0x80,0x41,0xE9,0x03,0x8E,0x21,0x96,0x48,0x42,0x00,0x88,0x24, +0x96,0x00,0xF0,0x81,0xF0,0x02,0x52,0x90,0x00,0x02,0xF0,0x01,0x88,0x09,0x54,0x90, +0x00,0xFF,0x84,0x00,0x80,0xA0,0xD2,0x11,0xB1,0x84,0x84,0x80,0x99,0xF0,0x97,0xA0, +0xE2,0xC1,0xE8,0x07,0x38,0x61,0x94,0x00,0x38,0x63,0x90,0x08,0x8C,0x81,0xD5,0xF8, +0x8C,0xA1,0x97,0x68,0x88,0x01,0xD5,0xF0,0x84,0xC0,0xF0,0x02,0x4C,0x60,0x00,0x79, +0xB0,0x14,0x83,0x80,0x80,0xE0,0x85,0x00,0x54,0xA4,0x00,0xFF,0xE3,0x49,0xE8,0x62, +0xF0,0x03,0xCE,0x11,0xA6,0xC0,0xB0,0x04,0x50,0x31,0x80,0x40,0x38,0x00,0x20,0x00, +0x8A,0x60,0x44,0x00,0x00,0x40,0x40,0x01,0x80,0x76,0x96,0xD8,0x5C,0xF1,0x80,0x21, +0xE8,0x15,0xD5,0x18,0x38,0x30,0x18,0x00,0xB0,0x44,0x40,0x04,0x18,0x00,0x88,0x01, +0x50,0x31,0x80,0x40,0x00,0x00,0x7F,0xFF,0x8A,0x60,0x44,0x00,0x00,0x40,0x40,0x01, +0x80,0x76,0x96,0xD8,0x5C,0xF1,0x80,0x21,0xE9,0x12,0x52,0x31,0x80,0x40,0x96,0xD8, +0xCE,0x0E,0xB0,0x14,0x84,0x20,0x80,0x4A,0x49,0xFF,0xFF,0x82,0x4E,0xA2,0x00,0x30, +0xA4,0x78,0x02,0x03,0xFF,0xFF,0x88,0x01,0xAC,0x38,0xD5,0x29,0xB0,0x14,0x5A,0x68, +0x01,0x0D,0x80,0x26,0x80,0x4A,0x49,0xFF,0xFF,0x73,0x4E,0xA2,0x00,0x21,0x02,0x03, +0x80,0x3F,0x02,0x13,0xFF,0xFF,0xD5,0x12,0x84,0x21,0x80,0x4A,0x49,0xFF,0xFF,0x68, +0x4E,0xA3,0x00,0x0A,0x02,0x1F,0x80,0x68,0x02,0x0F,0x80,0x28,0x88,0x01,0x12,0x0F, +0x80,0x68,0xD5,0x0D,0x02,0x03,0x80,0x3F,0xA4,0x78,0xE2,0x20,0x40,0x10,0x3C,0x1B, +0x02,0x03,0x80,0x40,0x81,0xE1,0x88,0x0F,0x12,0x03,0x80,0x40,0x8D,0x01,0x8C,0xE2, +0xD5,0x9C,0x84,0xA0,0xC6,0x0A,0x4C,0x54,0x80,0x09,0x02,0x0E,0x00,0x40,0x8C,0xA1, +0x1A,0x0E,0x00,0x01,0x97,0x68,0xD5,0xF8,0x8C,0xC1,0x97,0xB0,0xD5,0x87,0x5A,0x68, +0x01,0x04,0x8F,0x21,0xD5,0x03,0x50,0x94,0x80,0x3F,0xB0,0x14,0xF1,0x01,0x38,0x00, +0x25,0x01,0x40,0x00,0x04,0x16,0x96,0x00,0xED,0x50,0xFC,0xC0,0xFC,0x00,0x4E,0x17, +0x00,0x0E,0x4E,0x05,0x00,0x07,0x44,0x20,0x03,0xE8,0xFE,0x84,0x84,0x01,0xD5,0x0D, +0x44,0x2F,0xFC,0x18,0xFE,0x84,0x84,0x04,0xD5,0x08,0xC1,0x11,0x4E,0x05,0x00,0x0B, +0x44,0x2F,0xFC,0x18,0xFE,0x84,0x84,0x02,0x40,0x11,0x04,0x36,0x49,0xFF,0xFE,0xC6, +0xFC,0x80,0x44,0x20,0x03,0xE8,0xFE,0x84,0x84,0x03,0xD5,0xF7,0xC0,0x07,0x4E,0x06, +0x00,0x04,0xFA,0x00,0xFC,0x80,0x44,0x00,0x00,0x30,0xFC,0x80,0x80,0x20,0x2E,0x07, +0xED,0x3A,0xC8,0x0E,0x2E,0x27,0xED,0x39,0xCA,0x0C,0x2E,0x07,0xED,0x3D,0xC0,0x09, +0x2E,0x07,0xED,0x3C,0xC0,0x06,0xA6,0x08,0x5C,0x00,0x00,0x03,0xDD,0x9E,0x84,0x00, +0xDD,0x9E,0x84,0x00,0x3E,0x07,0xED,0x3B,0x3E,0x07,0xED,0x3A,0x3E,0x07,0xED,0x39, +0xDD,0x9E,0xFC,0x00,0x84,0x00,0x3E,0x07,0xEC,0x9B,0x3E,0x07,0xEC,0x99,0x84,0x00, +0xFA,0xD8,0x3C,0x0B,0xF6,0x5B,0x3C,0x0B,0xF6,0x61,0x3C,0x0B,0xF6,0x62,0x3C,0x0B, +0xF6,0x5C,0x3C,0x0B,0xF6,0x58,0x3C,0x0B,0xF6,0x68,0x3C,0x0B,0xF6,0x5E,0x3C,0x0B, +0xF6,0x60,0x3C,0x0B,0xF6,0x5D,0x3C,0x0B,0xF6,0x65,0x3C,0x0B,0xF6,0x5F,0x3C,0x0B, +0xF6,0x66,0x3C,0x0B,0xF6,0x63,0x3C,0x0B,0xF6,0x59,0x84,0x1F,0x80,0x46,0x3E,0x07, +0xEC,0x9A,0x84,0x20,0x44,0x02,0x3A,0x10,0x49,0x00,0x61,0xEA,0x80,0x46,0x84,0x20, +0x84,0xC4,0x44,0x02,0x3A,0x6C,0x49,0x00,0x61,0xE3,0x80,0x46,0x84,0x20,0x44,0x02, +0x11,0x6C,0x49,0x00,0x61,0xDD,0x84,0x20,0x80,0x46,0x44,0x02,0x11,0x74,0x49,0x00, +0x61,0xD7,0xFC,0x80,0xFC,0x40,0x5C,0xF1,0x00,0x29,0xE8,0x03,0x80,0xA2,0xD5,0x02, +0xFA,0xB8,0x84,0x60,0x44,0x92,0x12,0x34,0x44,0xA2,0x11,0xE4,0x80,0xC3,0xE0,0x65, +0xE8,0x12,0x95,0x19,0x40,0x72,0x24,0x00,0x88,0x8A,0x22,0x83,0x80,0x00,0x38,0x80, +0x0D,0x09,0x22,0x82,0x00,0x00,0x38,0x80,0x8D,0x09,0x8C,0x61,0xAD,0xB8,0xAD,0xA0, +0x96,0xD8,0xD5,0xEE,0x2E,0x17,0xED,0x70,0x5C,0xF0,0x80,0x29,0x80,0x01,0xE9,0x02, +0xFA,0x18,0x96,0x40,0xE2,0x41,0x40,0x01,0x3C,0x1B,0x96,0x00,0xFC,0xC0,0x84,0x20, +0x2E,0x07,0xED,0x71,0x3E,0x17,0xED,0x71,0xDD,0x9E,0x84,0x00,0x3C,0x0B,0xF7,0x5E, +0x3C,0x0B,0xF6,0xBD,0x3E,0x07,0xEC,0x98,0xDD,0x9E,0x3C,0x0F,0xFB,0x45,0x44,0x02, +0x3A,0x60,0xAC,0x40,0x44,0x10,0x03,0xE8,0xAC,0x81,0xAC,0xC3,0xAD,0x04,0xAF,0x44, +0xAC,0x45,0xDD,0x9E,0x3C,0xF7,0xEF,0x32,0x3C,0x07,0xEF,0x33,0x8A,0x0F,0x5E,0xF0, +0x03,0x20,0xE8,0x0B,0x3C,0x07,0xEF,0x35,0x3C,0x17,0xEF,0x34,0x8A,0x01,0x5E,0xF0, +0x03,0x20,0x54,0x07,0x80,0xFF,0xDD,0x9E,0x84,0x00,0xDD,0x9E,0xFC,0x41,0x3C,0x23, +0xF6,0xA0,0x84,0x61,0x3E,0x37,0xED,0x72,0x5A,0x28,0x02,0x6C,0x3C,0x4D,0xFB,0x3D, +0x3C,0x5D,0xFB,0x3B,0x3C,0x7D,0xFB,0x3A,0x3C,0x6D,0xFB,0x3C,0x3C,0x23,0xF7,0x5E, +0x3E,0x37,0xED,0x3B,0x9A,0xCC,0x9B,0x2C,0x40,0x90,0x1C,0x01,0xFE,0xDC,0x9B,0xF7, +0xFF,0x24,0x8C,0x41,0x42,0x34,0xA4,0x73,0x42,0x43,0x9C,0x73,0x96,0x91,0xE0,0x83, +0x3C,0x2B,0xF7,0x5E,0xE8,0x07,0x3C,0x0F,0xFB,0x3C,0x3C,0x1F,0xFB,0x3B,0x96,0x33, +0x96,0x6B,0x3C,0x4D,0xFB,0x3C,0x3C,0x3D,0xFB,0x3B,0x5A,0x28,0x01,0x1D,0x3C,0x23, +0xF6,0xBD,0x8E,0x41,0x4E,0x24,0x00,0x03,0xFE,0x92,0xE4,0x46,0xE9,0x04,0x84,0x41, +0x3E,0x27,0xED,0x39,0x9A,0x84,0x3C,0x2F,0xFB,0x3F,0x9A,0x8B,0x3C,0x2F,0xFB,0x38, +0x84,0x41,0x3E,0x27,0xED,0x3D,0x3E,0x27,0xED,0x3C,0x3C,0x4B,0xF6,0x5E,0x3C,0x3B, +0xF6,0x60,0xD5,0x23,0x3C,0x6D,0xFB,0x3F,0x9B,0x44,0x8A,0xA6,0x44,0x20,0x03,0xE8, +0x4E,0x54,0x00,0x04,0x9B,0x60,0x88,0xA6,0xE0,0x45,0xE8,0x04,0x84,0x40,0x3E,0x27, +0xED,0x3D,0x3C,0x6D,0xFB,0x38,0x9B,0x4B,0x8A,0xA6,0x44,0x20,0x03,0xE8,0x4E,0x54, +0x00,0x04,0x9B,0x59,0x88,0xA6,0xE0,0x45,0xE8,0x04,0x84,0x40,0x3E,0x27,0xED,0x3C, +0x3C,0x4B,0xF6,0x5D,0x3C,0x3B,0xF6,0x65,0x84,0x40,0x3C,0x2B,0xF6,0xA4,0xD5,0x19, +0x2E,0x37,0xED,0x3B,0x5A,0x38,0x01,0x13,0x5A,0x28,0x01,0x11,0x3C,0x03,0xF6,0xA4, +0x8C,0x01,0x96,0x01,0xE6,0x06,0x3C,0x0B,0xF6,0xA4,0xE9,0x03,0x3E,0x27,0xED,0x3A, +0x84,0x00,0x3C,0x0B,0xF7,0x5E,0x48,0x00,0x01,0x15,0x84,0x40,0x3C,0x2B,0xF7,0x5E, +0x52,0x30,0x1A,0x69,0x3C,0x0F,0xFB,0x3A,0x3C,0x03,0xF6,0xBD,0x52,0x20,0xB7,0xC1, +0x8C,0x01,0x96,0xDB,0x96,0x93,0x96,0x01,0x3C,0x1F,0xFB,0x3D,0x3C,0x3B,0xF6,0x5A, +0x3C,0x2B,0xF6,0x67,0x3C,0x0B,0xF6,0xBD,0x5A,0x08,0x01,0x46,0x84,0x20,0x3C,0x3B, +0xEF,0x32,0x3C,0x3B,0xEF,0x33,0x3C,0x2B,0xEF,0x34,0x3C,0x2B,0xEF,0x35,0x3C,0x3B, +0xEF,0x36,0x3C,0x2B,0xEF,0x37,0x3C,0x1F,0xF7,0x9D,0x3C,0x1F,0xF7,0x9E,0x3E,0x17, +0xED,0x6C,0x3E,0x17,0xED,0x73,0x3E,0x17,0xEC,0x98,0x3C,0x3B,0xF6,0x64,0x3C,0x2B, +0xF6,0x57,0x3C,0x3B,0xF6,0xBC,0x3C,0x2B,0xF6,0xBB,0x44,0x72,0x12,0x34,0x80,0x01, +0x44,0x62,0x11,0xE4,0x44,0x52,0x12,0xD4,0x44,0x42,0x12,0x84,0x38,0x03,0x85,0x09, +0x38,0x03,0x05,0x09,0x38,0x02,0x85,0x09,0x38,0x02,0x05,0x09,0x8C,0x21,0x5A,0x18, +0x28,0xF7,0x84,0x01,0x3C,0x3B,0xF6,0xE6,0x3C,0x2B,0xF6,0xBE,0x3C,0x3B,0xF6,0x62, +0x3C,0x2B,0xF6,0x5C,0x3C,0x3B,0xF7,0x36,0x3C,0x2B,0xF7,0x0E,0x3E,0x07,0xED,0x70, +0x48,0x00,0x00,0xB8,0x2E,0x77,0xED,0x6C,0x3C,0x3B,0xF6,0x58,0xE6,0xFE,0x3C,0x2B, +0xF6,0x68,0x4E,0xF2,0x00,0xA3,0x3C,0xA7,0xF6,0x57,0x3C,0x97,0xF6,0x64,0x40,0x01, +0x28,0x01,0x40,0x11,0xA4,0x01,0x42,0xF0,0x00,0x24,0x42,0xF0,0x84,0x73,0x44,0x41, +0xDE,0x84,0xE0,0x8F,0x4E,0xF2,0x00,0x9E,0x2E,0x47,0xED,0x70,0x5C,0xF2,0x00,0x27, +0xE8,0x0A,0x44,0x52,0x12,0x34,0x38,0x32,0x91,0x09,0x44,0x52,0x11,0xE4,0x38,0x22, +0x91,0x09,0xD5,0x07,0x44,0x50,0x7F,0xFF,0x3C,0x5B,0xF7,0x0D,0x3C,0x5B,0xF6,0xE5, +0x3C,0xF7,0xEF,0x32,0x8C,0x81,0x97,0xA0,0xE0,0x6F,0x3E,0x67,0xED,0x70,0xE8,0x03, +0x3C,0x3B,0xEF,0x32,0x3C,0x47,0xEF,0x33,0xE0,0x83,0xE8,0x03,0x3C,0x3B,0xEF,0x33, +0x3C,0xF7,0xEF,0x34,0xE0,0x4F,0xE8,0x03,0x3C,0x2B,0xEF,0x34,0x3C,0x37,0xEF,0x35, +0xE0,0x62,0xE8,0x03,0x3C,0x2B,0xEF,0x35,0x80,0x41,0x4E,0x14,0x00,0x03,0xFE,0x8A, +0x3C,0x3D,0xF7,0x9D,0x88,0x43,0x3C,0x2F,0xF7,0x9D,0x80,0x40,0x4E,0x04,0x00,0x03, +0xFE,0x82,0x3C,0x3D,0xF7,0x9E,0x88,0x43,0x3C,0x2F,0xF7,0x9E,0x49,0xFF,0xFD,0xE0, +0x81,0x00,0x3E,0x07,0xED,0x75,0x5A,0x60,0x02,0x3E,0xE6,0xC3,0xE9,0x3D,0x2E,0x67, +0xED,0x74,0x44,0xF0,0x00,0x40,0x50,0x63,0x00,0x40,0x8A,0xC0,0x40,0xF3,0x3C,0xD6, +0x96,0xF0,0xE6,0x64,0xE9,0x12,0x96,0x30,0x5E,0xF0,0x00,0x3D,0xE8,0x0E,0x3C,0x27, +0xF6,0xBB,0x3C,0x07,0xF6,0xBC,0x8A,0x4A,0x8A,0x09,0xFE,0x94,0x42,0x20,0x00,0x73, +0x44,0x01,0xDE,0x84,0xE0,0x02,0xE9,0x02,0xCF,0x1F,0x3C,0x07,0xF6,0xBB,0x3C,0x17, +0xF6,0xBC,0x40,0x05,0x00,0x01,0x40,0x14,0x84,0x01,0xF3,0x81,0x49,0xFF,0xFD,0xB0, +0x49,0xFF,0xFC,0x6B,0xF3,0x01,0xE6,0x68,0xE9,0x05,0x97,0xB0,0x5E,0xF3,0x00,0x39, +0xE9,0x1A,0x3C,0x03,0xF6,0x64,0x3C,0x0B,0xF6,0xBC,0x3C,0x03,0xF6,0x57,0x3C,0x0B, +0xF6,0xBB,0x3E,0x87,0xED,0x74,0x3C,0x03,0xF6,0x5A,0x3C,0x0B,0xF6,0x64,0x3C,0x03, +0xF6,0x67,0x3C,0x0B,0xF6,0x57,0xD5,0x0D,0x5A,0x78,0x1E,0x0C,0x84,0x01,0x3E,0x07, +0xEC,0x98,0xD5,0x07,0x2E,0x07,0xED,0x73,0x8C,0x01,0x3E,0x07,0xED,0x73,0xD5,0xE2, +0xFC,0xC1,0xFC,0x4F,0x51,0xCF,0x80,0x98,0x2E,0x27,0xED,0x6C,0x14,0x0E,0x7F,0xDB, +0x3C,0x17,0xF6,0x64,0x3C,0x07,0xF6,0x57,0xE6,0x5F,0x3C,0x1B,0xEF,0x38,0x3C,0x0B, +0xEF,0x39,0xE8,0x0B,0x3C,0x37,0xF6,0xBB,0x3C,0x27,0xF6,0xBC,0x8A,0x03,0x8A,0x22, +0x49,0xFF,0xFD,0x6E,0x49,0xFF,0xFC,0x29,0x3C,0x0D,0xF7,0x9D,0x3C,0x6D,0xF7,0x9E, +0x84,0x20,0x3C,0x0F,0xF7,0x9F,0x3C,0x6F,0xF7,0xA0,0x49,0xFF,0xFC,0x7A,0x3C,0x0F, +0xF7,0x9D,0x80,0x06,0x49,0xFF,0xFC,0x67,0x3C,0x0F,0xF7,0x9E,0x3C,0xF7,0xEF,0x32, +0x3C,0x07,0xEF,0x33,0x3C,0x14,0x0B,0x01,0x8A,0x0F,0xE0,0x01,0xE8,0x08,0x3C,0xF7, +0xEF,0x34,0x3C,0x07,0xEF,0x35,0x8A,0x0F,0xE0,0x01,0xE9,0x09,0x2E,0x07,0xEC,0x98, +0x5A,0x00,0x01,0x06,0x2E,0x07,0xED,0x6C,0xE6,0x02,0xE8,0x0C,0x84,0x00,0x3E,0x07, +0xEC,0x98,0x84,0x00,0x14,0x0E,0x7F,0xEF,0x84,0x00,0x14,0x0E,0x7F,0xE4,0x48,0x00, +0x03,0xC0,0x3C,0x0D,0xFB,0x45,0x15,0xFE,0x7F,0xDC,0x85,0x40,0xB4,0x00,0xA4,0x00, +0x8C,0x07,0x92,0x03,0x94,0x03,0x41,0xFF,0x80,0x01,0x15,0xFE,0x7F,0xE3,0x41,0xFF, +0x80,0x01,0x84,0x00,0x14,0x0E,0x7F,0xEF,0x84,0x00,0x14,0x0E,0x7F,0xE4,0x84,0x00, +0x15,0xFE,0x7F,0xEA,0x14,0x0E,0x7F,0xE1,0x3C,0x0D,0xFB,0x45,0xB4,0x20,0xA4,0x48, +0xE3,0x41,0x4E,0xF2,0x03,0x98,0x84,0x2C,0x42,0x15,0x04,0x24,0x14,0x1E,0x7F,0xED, +0xA0,0x41,0x04,0x0E,0x7F,0xED,0x50,0x2E,0x7F,0x80,0x88,0x20,0x00,0x80,0x80,0x05, +0x50,0x04,0x7F,0xFF,0x96,0x00,0x14,0x0E,0x7F,0xE0,0xB4,0x02,0xE6,0x04,0xE8,0x1D, +0xA6,0x8C,0x2E,0x07,0xED,0x6C,0xB4,0x61,0xC2,0x06,0x44,0x12,0x11,0xB4,0x49,0xFF, +0xFC,0x3A,0x80,0x40,0x04,0x0E,0x7F,0xE3,0x38,0x20,0x28,0x08,0x3C,0x0D,0xFB,0x45, +0x2E,0x27,0xED,0x6C,0xA0,0x41,0x04,0x0E,0x7F,0xED,0x88,0x20,0xA6,0x0C,0xB4,0x21, +0xC2,0x6D,0x44,0x32,0x11,0xB4,0xD5,0x67,0x5A,0x88,0x0A,0x2D,0x84,0xE0,0x81,0x27, +0x80,0xC7,0x50,0x0E,0x7F,0xC0,0x96,0x72,0x49,0xFF,0xFB,0x80,0x3C,0x1D,0xFB,0x45, +0x04,0x2E,0x7F,0xED,0x2E,0x07,0xED,0x6C,0xA0,0x49,0x88,0x22,0xA6,0x8C,0xB4,0x61, +0xC2,0x0A,0x50,0x1E,0x7F,0xC0,0x49,0xFF,0xFC,0x0E,0xE3,0x20,0x40,0x90,0x3C,0x1B, +0x40,0x73,0x3C,0x1B,0x8C,0xC4,0x97,0xB0,0x5A,0x68,0x40,0xE5,0x04,0x0E,0x7F,0xE3, +0x5C,0xF4,0x80,0x58,0x38,0x90,0x28,0x08,0xE9,0x46,0x50,0x0E,0x7F,0xC0,0x96,0x7A, +0xD5,0x2B,0x85,0x20,0x80,0xE9,0x84,0xDB,0x50,0x0E,0x7F,0xC0,0x80,0x26,0x49,0xFF, +0xFB,0x55,0x3C,0x1D,0xFB,0x45,0x04,0x2E,0x7F,0xED,0x2E,0x07,0xED,0x6C,0xA0,0x49, +0x88,0x22,0xA6,0x8C,0xB4,0x61,0xC2,0x0A,0x50,0x1E,0x7F,0xC0,0x49,0xFF,0xFB,0xE3, +0xE2,0xE0,0x40,0x70,0x3C,0x1B,0x40,0x93,0x3C,0x1B,0x8C,0xC1,0x97,0xB2,0x5A,0x68, +0x06,0xE5,0x04,0x0E,0x7F,0xE3,0x5C,0xF3,0x80,0x58,0x38,0x70,0x28,0x08,0xE9,0x1B, +0x50,0x0E,0x7F,0xC0,0x80,0x29,0x49,0xFF,0xFB,0x31,0x3C,0x0D,0xFB,0x45,0x2E,0x27, +0xED,0x6C,0xA0,0x41,0x04,0x0E,0x7F,0xED,0x88,0x20,0xA6,0x0C,0xB4,0x21,0xC2,0x06, +0x50,0x3E,0x7F,0xC0,0x49,0xFF,0xFB,0xBF,0x80,0x40,0x04,0x0E,0x7F,0xEA,0x38,0x20, +0x28,0x08,0xD5,0x06,0x04,0x1E,0x7F,0xEA,0x84,0x00,0x38,0x00,0xA8,0x08,0x04,0x0E, +0x7F,0xE3,0x38,0x00,0x28,0x00,0x5C,0xF0,0x00,0x57,0x14,0x0E,0x7F,0xDF,0x4E,0xF3, +0x02,0xE4,0x04,0x0E,0x7F,0xEA,0x38,0x00,0x28,0x00,0x5C,0xF0,0x00,0x52,0x14,0x0E, +0x7F,0xDE,0x4E,0xF3,0x02,0xDA,0x3C,0x07,0xEF,0x36,0x3C,0x97,0xEF,0x37,0x14,0x0E, +0x7F,0xE9,0x3C,0x07,0xEF,0x38,0x3C,0x67,0xEF,0x39,0x14,0x0E,0x7F,0xEC,0x3C,0x07, +0xEF,0x33,0x14,0x0E,0x7F,0xE8,0x3C,0x07,0xEF,0x32,0x14,0x0E,0x7F,0xDD,0x3C,0x07, +0xEF,0x35,0x14,0x0E,0x7F,0xEB,0x3C,0x07,0xEF,0x34,0x14,0x0E,0x7F,0xEE,0x5A,0x88, +0x07,0x0D,0x40,0x03,0x24,0x01,0x4E,0x04,0x00,0x03,0xFE,0x02,0x49,0xFF,0xFB,0x43, +0x5E,0xF0,0x00,0xC8,0x48,0x00,0x00,0x8E,0x04,0x1E,0x7F,0xEC,0x04,0x0E,0x7F,0xE9, +0x8A,0x01,0x50,0x1E,0x7F,0x88,0x14,0x0E,0x7F,0xE2,0xB4,0x01,0x50,0x1E,0x7F,0x94, +0x14,0x0E,0x7F,0xE5,0x4E,0x04,0x00,0x06,0x04,0x0E,0x7F,0xE2,0xFE,0x02,0xB6,0x01, +0x40,0x74,0x98,0x01,0x4E,0x74,0x00,0x03,0xFF,0xFA,0x04,0x1E,0x7F,0xDD,0x04,0x0E, +0x7F,0xE8,0x8A,0x01,0x14,0x0E,0x7F,0xE7,0x04,0x1E,0x7F,0xEE,0x04,0x0E,0x7F,0xEB, +0x8A,0x01,0x14,0x0E,0x7F,0xE6,0x5A,0x88,0x08,0x69,0x04,0x0E,0x7F,0xEB,0x8A,0x09, +0x49,0xFF,0xFB,0x11,0x5E,0xF0,0x06,0x73,0xE9,0x17,0x04,0x1E,0x7F,0xEC,0x04,0x0E, +0x7F,0xE8,0x8A,0x01,0x84,0x20,0x49,0xFF,0xFB,0x14,0x5E,0xF0,0x00,0xC9,0xE9,0x0C, +0x2E,0x07,0xED,0x6C,0x44,0x12,0x11,0xB4,0x88,0x01,0x00,0x00,0x7F,0xFF,0x5C,0xF0, +0x00,0x30,0x4E,0xF2,0x02,0x6A,0x50,0x3E,0x7F,0x94,0xB4,0x23,0x44,0x00,0x00,0x64, +0xFE,0x4C,0x42,0x13,0x9C,0x73,0xFE,0x44,0xA0,0x1A,0xA0,0x99,0xFE,0x04,0x42,0x01, +0x08,0x73,0x95,0xC3,0xE2,0x27,0xE8,0x0A,0x04,0x0E,0x7F,0xEE,0x9A,0x30,0x49,0xFF, +0xFA,0xE2,0x5E,0xF0,0x00,0xC8,0x4E,0xF3,0x02,0x50,0x04,0x1E,0x7F,0xE9,0x04,0x0E, +0x7F,0xE8,0x8A,0x01,0x84,0x20,0x49,0xFF,0xFA,0xE4,0x5E,0xF0,0x02,0x58,0x4E,0xF3, +0x02,0x44,0x2E,0x07,0xED,0x4C,0x8E,0x08,0xE6,0x08,0x4E,0xF2,0x01,0xEF,0x04,0x0E, +0x7F,0xEB,0x9A,0x30,0x4E,0x04,0x00,0x03,0xFE,0x02,0x49,0xFF,0xFA,0xC4,0x5E,0xF0, +0x03,0xE8,0x4E,0xF2,0x01,0xE3,0x04,0x0E,0x7F,0xEC,0x04,0x1E,0x7F,0xE8,0x8A,0x01, +0x4E,0x04,0x00,0x03,0xFE,0x02,0x84,0x20,0x49,0xFF,0xFA,0xC3,0x5E,0xF0,0x01,0xF4, +0x4E,0xF3,0x02,0x23,0x48,0x00,0x01,0xD2,0x5A,0x80,0x0A,0x04,0x48,0x00,0x00,0xDB, +0x04,0x0E,0x7F,0xEC,0x04,0x1E,0x7F,0xE9,0x9A,0xC1,0x80,0x03,0x4E,0x34,0x00,0x03, +0xFE,0x1A,0x84,0x20,0x14,0x3E,0x7F,0xDA,0x49,0xFF,0xFA,0xAB,0x5E,0xF0,0x05,0x79, +0x14,0x0E,0x7F,0xE0,0x04,0x3E,0x7F,0xDA,0xE8,0x0F,0x40,0x03,0x24,0x01,0x4E,0x04, +0x00,0x03,0xFE,0x02,0x14,0x3E,0x7F,0xDA,0x49,0xFF,0xFA,0x8D,0x5E,0xF0,0x05,0x79, +0x04,0x3E,0x7F,0xDA,0xE9,0x2B,0x04,0x0E,0x7F,0xEB,0x04,0x1E,0x7F,0xEE,0x84,0x4A, +0x88,0x20,0x84,0x02,0x40,0x10,0x80,0x36,0x96,0x09,0x04,0x1E,0x7F,0xE6,0xFE,0x8C, +0xFA,0x2E,0x40,0x21,0x04,0x56,0x96,0x51,0x9A,0x81,0xE0,0x49,0xE8,0x04,0x99,0x01, +0xE1,0x24,0xE9,0x06,0xE0,0x46,0xE8,0x12,0x88,0x20,0xE0,0xC1,0xE8,0x0F,0x04,0x0E, +0x7F,0xE9,0x04,0x1E,0x7F,0xEC,0xE0,0x01,0xE8,0x09,0x80,0x03,0x84,0x20,0x49,0xFF, +0xFA,0x70,0x5E,0xF0,0x01,0x91,0x4E,0xF2,0x01,0xD0,0x50,0x2E,0x7F,0x94,0xB4,0x02, +0x44,0x10,0x00,0x64,0xFE,0x04,0x42,0x03,0x9C,0x73,0xFE,0x0C,0xB6,0x02,0xA0,0x12, +0xA0,0x51,0xFE,0x04,0x42,0x00,0x84,0x73,0xB4,0x22,0x80,0xE0,0x94,0x03,0xE2,0x01, +0xE8,0x1A,0x04,0x0E,0x7F,0xEE,0x9A,0x30,0x49,0xFF,0xFA,0x45,0x5E,0xF0,0x00,0xC8, +0xE8,0x12,0x2E,0x07,0xED,0x4D,0x8E,0x10,0xE6,0x1F,0xE9,0x0D,0x04,0x1E,0x7F,0xDD, +0x04,0x0E,0x7F,0xEC,0x8A,0x01,0x84,0x20,0x49,0xFF,0xFA,0x43,0x5E,0xF0,0x01,0x5F, +0x4E,0xF2,0x01,0xA3,0x2E,0x07,0xED,0x4C,0x50,0xF0,0x7F,0xF8,0x5C,0xF7,0x80,0x31, +0x14,0x0E,0x7F,0xEC,0xE9,0x19,0x04,0x0E,0x7F,0xEB,0x8A,0x09,0x49,0xFF,0xFA,0x23, +0x5E,0xF0,0x06,0x40,0xE8,0x11,0x04,0x0E,0x7F,0xEE,0x40,0x04,0x80,0x01,0x49,0xFF, +0xFA,0x1A,0x5E,0xF0,0x06,0x40,0xE8,0x08,0x84,0x06,0x04,0x1E,0x7F,0xE5,0xFE,0x3C, +0xE2,0x01,0x4E,0xF3,0x01,0x82,0x84,0x0C,0xFF,0xC4,0x04,0x0E,0x7F,0xE5,0xE2,0xE0, +0xE8,0x15,0x2E,0x07,0xED,0x4D,0xE6,0x10,0xE8,0x11,0x04,0x0E,0x7F,0xE2,0x84,0x20, +0x49,0xFF,0xFA,0x0F,0x5E,0xF0,0x00,0x65,0xE9,0x09,0x40,0x03,0x24,0x01,0x49,0xFF, +0xF9,0xFA,0x5E,0xF0,0x00,0x65,0x4E,0xF2,0x01,0x68,0x04,0x0E,0x7F,0xEC,0x50,0x00, +0x7F,0xDB,0xE6,0x12,0xE8,0x12,0x2E,0x07,0xED,0x6C,0x44,0x12,0x11,0xB4,0x88,0x01, +0x00,0x00,0x7F,0xFF,0x50,0x00,0x7F,0xDB,0xE6,0x12,0xE8,0x07,0x04,0x0E,0x7F,0xE0, +0x5E,0xF0,0x03,0xE9,0x4E,0xF2,0x01,0x51,0x04,0x1E,0x7F,0xE9,0x04,0x0E,0x7F,0xE8, +0x8A,0x01,0x84,0x20,0x49,0xFF,0xF9,0xE5,0x44,0x10,0x05,0xDC,0xE0,0x20,0x48,0x00, +0x00,0xF6,0x50,0x04,0x7F,0xFD,0xE6,0x02,0xE8,0x21,0x3C,0x10,0x0A,0xFD,0x44,0x00, +0x01,0x2C,0xFE,0x0C,0x3C,0x10,0x0A,0xFC,0x40,0x00,0x04,0x16,0x04,0x1E,0x7F,0xE6, +0xE0,0x01,0x4E,0xF3,0x01,0x32,0x3C,0x0D,0xF6,0xE7,0x3C,0x10,0x0A,0xFF,0x44,0xF0, +0x00,0x64,0xA6,0x00,0xFE,0x0C,0x40,0xF0,0x3D,0xF6,0x3C,0x0D,0xF7,0x9F,0xE0,0x0F, +0x4E,0xF3,0x01,0x23,0x3C,0x0D,0xF7,0x9D,0xD5,0x5D,0x04,0x0E,0x7F,0xE0,0xE6,0x02, +0xE8,0x5C,0x2E,0x17,0xED,0x6C,0x2E,0x07,0xED,0x4C,0x5A,0x10,0x01,0x10,0x2E,0x37, +0xED,0x4D,0x9A,0x83,0x4E,0x24,0x00,0x03,0xFE,0x92,0x88,0x03,0x5E,0xF1,0x00,0x20, +0x90,0x01,0xE9,0x03,0x50,0x00,0x00,0x20,0x96,0x2F,0x84,0x60,0x50,0x00,0x00,0x40, +0x44,0x52,0x11,0xB4,0x44,0x40,0x00,0x40,0x96,0x98,0xE2,0x41,0xE8,0x12,0x38,0x22, +0x8C,0x00,0x9A,0x82,0x40,0x61,0x10,0x56,0x96,0x90,0x5C,0xF1,0x00,0x21,0xE9,0x04, +0x52,0x21,0x00,0x40,0x96,0x90,0xE6,0x45,0x8C,0x61,0xE9,0xEF,0x48,0x00,0x00,0xED, +0x04,0x1E,0x7F,0xE7,0x4E,0x14,0x00,0x03,0xFE,0x4A,0x04,0x7E,0x7F,0xE6,0x4E,0x74, +0x00,0x03,0xFF,0xFA,0x40,0x03,0x88,0x0A,0x50,0x00,0x01,0x2C,0xE0,0x01,0x4E,0xF3, +0x00,0xDC,0x3C,0x0D,0xF6,0xE6,0x44,0xF0,0x00,0x64,0xA6,0x40,0x3C,0x00,0x0B,0x00, +0xFE,0x0C,0x3C,0x10,0x0A,0xFD,0x40,0x00,0x3C,0x17,0xFE,0x0C,0x3C,0x10,0x0A,0xFC, +0x40,0xF0,0x05,0xF6,0x3C,0x0D,0xF7,0xA0,0xE0,0x0F,0x4E,0xF3,0x00,0xC6,0x3C,0x0D, +0xF7,0x9E,0x5E,0xF0,0x08,0x35,0xD5,0x68,0x5A,0x88,0x09,0x22,0x2E,0x0F,0xED,0x73, +0xE4,0x04,0x4E,0xF2,0x00,0xBA,0x04,0x0E,0x7F,0xEC,0x04,0x1E,0x7F,0xE9,0x8A,0x01, +0x5E,0xF0,0x00,0x64,0x4E,0xF3,0x00,0xB1,0x2E,0x07,0xED,0x6C,0xE6,0x06,0xE9,0x5D, +0x2E,0x17,0xED,0x4D,0x8E,0x30,0xE6,0x3F,0xE8,0x58,0x44,0x12,0x11,0xB4,0x88,0x01, +0x00,0x00,0x7F,0xFE,0x8E,0x10,0xE6,0x1F,0x48,0xFF,0xFE,0x7C,0x5A,0x88,0x05,0x10, +0x04,0x0E,0x7F,0xEE,0x9A,0x30,0x49,0xFF,0xF9,0x26,0x5E,0xF0,0x05,0x15,0xE9,0x45, +0x2E,0x07,0xED,0x4C,0x8E,0x08,0xE6,0x0F,0x48,0xFF,0xFE,0x6C,0x5A,0x88,0x06,0x3E, +0x04,0x0E,0x7F,0xEB,0x8A,0x09,0x49,0xFF,0xF9,0x16,0x5E,0xF0,0x04,0xE3,0xE9,0x0F, +0x04,0x0E,0x7F,0xEE,0x9A,0x30,0x49,0xFF,0xF9,0x0E,0x5E,0xF0,0x01,0xF4,0xE8,0x07, +0x2E,0x07,0xED,0x4C,0x50,0x00,0x7F,0xD8,0xE6,0x0F,0xE9,0x76,0x04,0x0E,0x7F,0xEB, +0x8A,0x06,0x49,0xFF,0xF9,0x00,0x5E,0xF0,0x03,0xE9,0xE9,0x1F,0x04,0x0E,0x7F,0xEE, +0x40,0x04,0x80,0x01,0x49,0xFF,0xF8,0xF7,0x5E,0xF0,0x07,0x08,0xE8,0x16,0x2E,0x07, +0xED,0x6C,0x44,0x12,0x11,0xB4,0x88,0x01,0x00,0x10,0x7F,0xFF,0xE6,0x28,0xE8,0x06, +0x00,0x00,0x7F,0xFE,0xE6,0x08,0xE8,0x58,0xD5,0x08,0xE6,0x37,0xE9,0x55,0x00,0x00, +0x7F,0xFE,0xE6,0x17,0x48,0xFF,0xFE,0x2E,0x85,0xE0,0x3C,0x1D,0xFB,0x45,0x04,0x0E, +0x7F,0xED,0xA1,0x89,0x88,0xC0,0xA6,0x76,0x5A,0x18,0x01,0x34,0x3C,0x3D,0xF7,0x9D, +0xC3,0x30,0x3C,0x4D,0xF7,0x9E,0xC4,0x2D,0xA7,0xF7,0x44,0x13,0x34,0x50,0xFF,0xCC, +0x00,0x53,0x00,0x09,0x00,0x13,0x00,0x08,0x00,0x63,0x00,0x0A,0x44,0x20,0x07,0xD0, +0xFE,0x54,0xFE,0xB4,0x5A,0x88,0x0A,0x0B,0x5A,0xF8,0x01,0x09,0x84,0x09,0xFE,0x44, +0xFE,0x84,0x40,0x10,0xA0,0x37,0x40,0x21,0x20,0x57,0x44,0x60,0x07,0xD0,0x42,0xF1, +0x98,0x24,0xE2,0xEF,0xE9,0x21,0xFA,0x04,0xFE,0xC4,0xE2,0x61,0xE9,0x1D,0x44,0x13, +0x34,0x50,0xFF,0xA4,0xFF,0x4C,0xE2,0xA6,0xE9,0x17,0xFE,0x24,0xE2,0x02,0xE9,0x14, +0x50,0x2E,0x7F,0x78,0xB4,0x22,0xA0,0x11,0x88,0x01,0xA0,0x53,0x90,0x01,0xE0,0x20, +0xE8,0x0B,0x96,0x00,0x50,0x1E,0x7F,0x84,0x14,0x0E,0x7F,0xE1,0xB4,0x01,0x14,0xAE, +0x7F,0xE4,0x14,0x0E,0x7F,0xEF,0x50,0x05,0x00,0x01,0x54,0xA0,0x00,0xFF,0x48,0xFF, +0xFC,0x65,0x00,0x0E,0x7F,0xBC,0x05,0xFE,0x7F,0xDC,0x3E,0x07,0xED,0x71,0x49,0xFF, +0xFA,0x2E,0x04,0x2E,0x7F,0xE4,0x84,0x2C,0x44,0x02,0x00,0x14,0x42,0x01,0x04,0x73, +0xB4,0xA0,0x44,0x02,0x02,0x70,0xD8,0x03,0x84,0x01,0xD5,0x07,0x44,0x02,0x02,0x60, +0xD8,0x03,0x84,0x00,0xD5,0x02,0x84,0x02,0x3E,0x07,0xEC,0x9A,0x04,0x0E,0x7F,0xEF, +0xC8,0x03,0x84,0x02,0xD5,0x11,0x3C,0x0D,0xFB,0x45,0x04,0x2E,0x7F,0xE4,0x84,0x2C, +0xA0,0x01,0x42,0x01,0x04,0x73,0x04,0x1E,0x7F,0xDB,0xA6,0x05,0xAE,0x08,0x8E,0x07, +0xE6,0x02,0x84,0x01,0xE9,0xEF,0x51,0xFE,0x7F,0xE0,0xFC,0xC0,0xFC,0x21,0x44,0x50, +0xF0,0xE0,0x80,0xE2,0x80,0xC3,0xA4,0xA8,0xA4,0xE9,0x22,0x10,0x80,0x00,0x22,0x00, +0x00,0x00,0x9A,0x59,0x9A,0x10,0xB6,0x1F,0xF1,0x81,0xA5,0x2C,0x3A,0x0F,0x84,0x00, +0xA5,0x6D,0x49,0x00,0x25,0x22,0x3A,0x0F,0x84,0x20,0xB4,0x1F,0xAC,0x38,0xF0,0x01, +0xAC,0x30,0xFC,0xA1,0xFC,0x40,0x3C,0x13,0xEF,0x32,0x81,0x20,0x3C,0x03,0xEF,0x33, +0x44,0x72,0x12,0x34,0x8A,0x01,0x3C,0x0B,0xF6,0x5B,0x3C,0x13,0xEF,0x34,0x3C,0x03, +0xEF,0x35,0x44,0x62,0x11,0xE4,0x8A,0x01,0x3C,0x0B,0xF6,0x61,0x44,0x12,0x11,0x2A, +0x44,0x02,0x11,0x1E,0xFD,0x10,0x49,0xFF,0xFF,0xCB,0x80,0x07,0x80,0x26,0x44,0x22, +0x11,0x2C,0x44,0x32,0x11,0x20,0x49,0xFF,0xFF,0xC3,0x2E,0x27,0xED,0x70,0x44,0x32, +0x11,0x38,0x8E,0x41,0x94,0x51,0x98,0x39,0x44,0x22,0x11,0x18,0x88,0x26,0x49,0xFF, +0xFF,0xB7,0x00,0x04,0x80,0x00,0x5A,0x08,0x0E,0x28,0x3C,0x13,0xF6,0x5E,0x3C,0x03, +0x66,0x3C,0x3C,0x23,0xF6,0x60,0x9A,0x41,0x3C,0x1B,0xF6,0x5E,0x3C,0x13,0x66,0x3D, +0x9A,0x8A,0x3C,0x2B,0xF6,0x60,0x3C,0x23,0xF6,0x5D,0x8A,0x02,0x3C,0x0B,0xF6,0x5D, +0x3C,0x03,0xF6,0x65,0x8A,0x20,0x3C,0x1B,0xF6,0x65,0x44,0x02,0x11,0x24,0x44,0x12, +0x11,0x28,0xFD,0x10,0x49,0xFF,0xFF,0x94,0x44,0x02,0x11,0x22,0x44,0x12,0x11,0x32, +0xFD,0x10,0x49,0xFF,0xFF,0x8D,0x85,0x40,0x45,0xC2,0x3A,0x10,0x44,0x82,0x3A,0x6C, +0x40,0x05,0x70,0x00,0x40,0x15,0x20,0x00,0xFD,0x10,0x8D,0x42,0x49,0xFF,0xFF,0x80, +0x5A,0xA8,0x50,0xF8,0x00,0x04,0x80,0x00,0x9E,0x45,0x54,0x90,0x80,0xFF,0xE7,0x22, +0xE9,0x07,0x5A,0x00,0x09,0x06,0x50,0x10,0x7F,0xF5,0xE6,0x23,0xE8,0x75,0x3C,0x27, +0xF6,0xBF,0x3C,0x17,0xF6,0xBE,0x3C,0x33,0xEF,0x34,0xE0,0x41,0x3C,0x23,0xEF,0x35, +0xE8,0x03,0x9A,0x9A,0xD5,0x02,0x8A,0x43,0x3C,0x47,0xF6,0xE7,0x3C,0x37,0xF6,0xE6, +0x96,0x93,0xE0,0x83,0x3C,0x53,0xEF,0x32,0x3C,0x43,0xEF,0x33,0xE8,0x03,0x8A,0xA4, +0xD5,0x02,0x9B,0x65,0xE7,0x22,0x97,0x6B,0x2E,0x47,0xED,0x70,0xE8,0x2B,0x8E,0x81, +0x96,0xD9,0x38,0x03,0x91,0x01,0x84,0xA4,0x8A,0x03,0x96,0x03,0x40,0x50,0x14,0xB6, +0x97,0x69,0x99,0xEB,0x3C,0x7B,0xF6,0x82,0x84,0xE2,0x40,0x00,0x1C,0x16,0x88,0x03, +0x3C,0x0B,0xF6,0x83,0x84,0x03,0x42,0x32,0x80,0x73,0x38,0x03,0x11,0x11,0x88,0x41, +0x88,0x20,0x96,0x93,0x90,0x21,0x3C,0x3B,0xF6,0x84,0x3C,0x2B,0xF6,0x86,0x3C,0x1B, +0xF6,0x87,0x3C,0x2B,0xF6,0x88,0x84,0xC0,0x44,0x92,0x11,0x6C,0x44,0x72,0x11,0x74, +0xD5,0x21,0x54,0x00,0x00,0xFD,0x5A,0x08,0x09,0x10,0x8E,0x81,0x88,0x41,0x38,0x03, +0x91,0x11,0x84,0x82,0x8A,0x03,0x40,0x00,0x10,0x16,0x88,0x60,0x3C,0x3B,0xF6,0x82, +0x3C,0x2B,0xF6,0x86,0xD5,0x2C,0x8E,0x81,0x84,0x02,0x38,0x23,0x11,0x11,0x88,0x65, +0x8A,0x41,0x40,0x21,0x00,0x56,0x88,0x22,0x3C,0x3B,0xF6,0x82,0x3C,0x1B,0xF6,0x86, +0xD5,0x1E,0x40,0x03,0x24,0x00,0x98,0x77,0xFD,0x10,0x8C,0xC2,0x49,0xFF,0xFF,0x00, +0x5A,0x68,0x06,0xF9,0xFC,0xC0,0x5A,0x08,0x0A,0x1A,0x44,0x02,0x02,0xCC,0x9C,0x46, +0x44,0x22,0x11,0x26,0x44,0x32,0x11,0x34,0x49,0xFF,0xFE,0xF2,0x44,0x02,0x02,0xCE, +0x9C,0x42,0x44,0x22,0x11,0x2E,0x44,0x32,0x11,0x1A,0xD5,0x06,0x44,0x02,0x11,0x6C, +0x44,0x12,0x11,0x74,0xFD,0x10,0x49,0xFF,0xFE,0xE3,0xFC,0xC0,0x46,0x27,0xFF,0xFF, +0x84,0x20,0x44,0x52,0x3B,0xC4,0x50,0x21,0x0F,0xFF,0x44,0x32,0x3B,0x98,0x38,0x22, +0x86,0x0A,0x38,0x21,0x86,0x0A,0x8C,0x21,0x44,0x42,0x3B,0xC4,0x5A,0x18,0x0B,0xF9, +0xFC,0x40,0x2E,0x37,0xDE,0x84,0x84,0xA0,0x40,0x91,0x88,0x08,0x81,0x44,0xD3,0x14, +0x40,0x70,0x24,0x00,0x80,0x40,0x4C,0x23,0x80,0x0B,0x38,0x62,0x16,0x02,0xA2,0x51, +0xE0,0xC1,0x40,0x13,0x3C,0x1B,0x38,0x15,0x16,0x0A,0xD5,0xF6,0x8C,0xA1,0x97,0x68, +0x50,0x00,0x00,0x2C,0xD5,0xED,0xFC,0xC0,0x2E,0x27,0xDE,0x84,0x46,0x18,0x00,0x00, +0x84,0x00,0x44,0x42,0x3B,0x6C,0x44,0x52,0x3B,0x40,0xE0,0x02,0xE8,0x0B,0x38,0x32, +0x02,0x02,0xCB,0x06,0x38,0x32,0x82,0x02,0xE0,0x23,0x40,0x11,0xBC,0x1B,0x8C,0x01, +0xD5,0xF5,0x84,0x00,0x44,0x52,0x3B,0x14,0x44,0x42,0x3B,0xC4,0xE0,0x02,0xE8,0x0B, +0x38,0x32,0x82,0x02,0xC3,0x06,0x38,0x32,0x02,0x02,0x8A,0x61,0x38,0x32,0x02,0x0A, +0x8C,0x01,0xD5,0xF5,0x84,0x00,0x44,0x52,0x3B,0x6C,0x44,0x42,0x3B,0x98,0xE0,0x02, +0xE8,0x0B,0x38,0x32,0x82,0x02,0xC3,0x06,0x38,0x32,0x02,0x02,0x88,0x61,0x38,0x32, +0x02,0x0A,0x8C,0x01,0xD5,0xF5,0x84,0x00,0x44,0x52,0x3B,0x6C,0x44,0x42,0x3B,0x40, +0xE0,0x02,0xE8,0x0B,0x38,0x32,0x82,0x02,0xCB,0x06,0x38,0x32,0x02,0x02,0x8A,0x61, +0x38,0x32,0x02,0x0A,0x8C,0x01,0xD5,0xF5,0xDD,0x9E,0xFC,0x40,0x44,0x32,0x3B,0x14, +0x84,0x81,0x38,0x41,0x82,0x0A,0x44,0x32,0x3B,0xF0,0x2E,0x57,0xDE,0x84,0x38,0x11, +0x82,0x0A,0xFA,0x3C,0x42,0x20,0x04,0x73,0x44,0x62,0x3B,0xC4,0x84,0x20,0x44,0x72, +0x3B,0x98,0x44,0x42,0x3B,0x40,0x44,0x92,0x3A,0xBC,0x96,0xC8,0xE2,0x65,0xE8,0x13, +0x38,0xA3,0x02,0x02,0x38,0x33,0x86,0x02,0x88,0x6A,0x38,0xA1,0x06,0x02,0x8A,0x6A, +0x38,0xA2,0x06,0x02,0xE1,0x43,0xE8,0x05,0x38,0x32,0x06,0x0A,0x38,0x04,0x86,0x0A, +0x8C,0x21,0xD5,0xEC,0xFC,0xC0,0xFC,0x48,0x44,0xA2,0x3C,0x1C,0xB6,0x1F,0x2E,0x97, +0xDE,0x84,0x2E,0x57,0xEC,0x9C,0x4C,0x54,0x80,0xE0,0x84,0x20,0xFA,0x5C,0x44,0x02, +0x3B,0x14,0x49,0x00,0x59,0xCD,0x84,0x20,0xFA,0x5C,0x44,0x02,0x3B,0x6C,0x49,0x00, +0x59,0xC7,0x44,0x02,0x3B,0xF0,0x84,0x3F,0xFA,0x5C,0x49,0x00,0x59,0xC1,0x84,0xE0, +0xE0,0xE9,0xE8,0x15,0x38,0x05,0x1E,0x02,0x5A,0x0F,0xFF,0x10,0x44,0x02,0x3B,0xF0, +0x84,0x3E,0x38,0x10,0x1E,0x0A,0x44,0x02,0x3B,0x14,0x84,0x21,0xF7,0x85,0x38,0x10, +0x1E,0x0A,0x80,0xC9,0x80,0x47,0xD5,0x06,0x8C,0xE1,0xD5,0xEB,0x84,0x20,0x80,0xC9, +0x80,0x41,0xFA,0x1C,0xB4,0x7F,0x42,0x31,0x00,0x73,0x84,0xA0,0x45,0xE2,0x3B,0x40, +0x44,0x42,0x3B,0xC4,0x44,0x82,0x3B,0x98,0x44,0xF2,0x3A,0xBC,0x4C,0x54,0x80,0x10, +0x39,0xC2,0x0A,0x02,0x38,0x04,0x16,0x02,0x88,0x1C,0x39,0xC1,0x96,0x02,0x38,0x27, +0x96,0x0A,0x8A,0x1C,0x38,0x0F,0x16,0x0A,0x8C,0xA1,0xD5,0xF1,0x85,0x20,0x44,0x82, +0x3B,0x6C,0xE1,0x21,0xE8,0x39,0xB0,0x05,0x05,0xCF,0x80,0x00,0x38,0x70,0x26,0x02, +0xFA,0x1C,0x43,0xC3,0x80,0x73,0x84,0xC0,0x44,0x42,0x3B,0xC4,0x44,0x52,0x3B,0x98, +0x2E,0x07,0xDE,0x84,0xE0,0xC0,0x4E,0xF2,0x00,0x7D,0x38,0x22,0x1E,0x02,0x38,0x02, +0x9A,0x02,0x94,0xF2,0x88,0x02,0x38,0x2E,0x1A,0x02,0x4C,0x20,0x40,0x1C,0x38,0x04, +0x1A,0x02,0xC8,0x18,0x44,0x02,0x3A,0xE8,0xF5,0x83,0x38,0x00,0x0C,0x02,0xF4,0x82, +0x5A,0x07,0xFF,0x13,0x84,0x41,0x38,0x24,0x0C,0x0A,0x88,0x41,0xF2,0x81,0xB0,0x85, +0x38,0x01,0x06,0x0A,0x80,0x27,0xB4,0x5F,0x49,0xFF,0xFF,0x49,0xF1,0x01,0xF5,0x03, +0xF4,0x02,0x8C,0xC1,0xD5,0xD6,0x2E,0x07,0xDE,0x84,0xE0,0xC0,0xE9,0x37,0x49,0xFF, +0xFE,0xF5,0x84,0x20,0x80,0xC1,0x45,0xC2,0x3B,0x40,0x2E,0x07,0xDE,0x84,0xE0,0xC0, +0xE8,0x2A,0x38,0x04,0x1A,0x02,0x94,0xB2,0xC8,0x24,0x38,0x0E,0x1A,0x02,0xC8,0x21, +0x44,0x02,0x3A,0xE8,0x38,0x00,0x08,0x02,0x5A,0x0F,0xFF,0x07,0x44,0x02,0x3A,0xBC, +0x38,0x70,0x1A,0x02,0xD5,0x1B,0x84,0x61,0x38,0x34,0x08,0x0A,0x44,0x32,0x3B,0x14, +0x38,0x31,0x82,0x02,0xCB,0x0E,0xB0,0xC5,0x50,0x90,0x80,0x01,0x38,0x01,0x86,0x0A, +0x44,0x12,0x3A,0xBC,0x38,0x10,0x88,0x02,0xB4,0x5F,0x49,0xFF,0xFF,0x10,0x80,0x29, +0x8C,0xC1,0xD5,0xD4,0x85,0x20,0x48,0xFF,0xFF,0x8E,0x2E,0x07,0xDE,0x84,0xE0,0xC0, +0xE8,0x1B,0x2E,0x07,0xEC,0x9C,0x44,0x12,0x3B,0xF0,0x8C,0x01,0x3E,0x07,0xEC,0x9C, +0x44,0x02,0x3A,0xE8,0x5A,0x7F,0xFE,0x04,0x48,0xFF,0xFF,0x2B,0x38,0x25,0x1E,0x02, +0x38,0x70,0x1A,0x0A,0x38,0x65,0x1E,0x0A,0x38,0x70,0x9E,0x02,0x80,0xC2,0xD5,0xF3, +0x8D,0x21,0x48,0xFF,0xFF,0x70,0xFC,0xC8,0xFC,0x01,0xF0,0x81,0x84,0x00,0x80,0xC1, +0x3E,0x07,0xEC,0x9C,0x84,0x3F,0xFA,0x5C,0x44,0x02,0x3C,0x1C,0x49,0x00,0x58,0xE8, +0x84,0x3F,0xFA,0x5C,0x44,0x02,0x3A,0xE8,0x49,0x00,0x58,0xE2,0xF0,0x01,0x3E,0x67, +0xDE,0x84,0x49,0xFF,0xFE,0x5D,0xF0,0x01,0x49,0xFF,0xFE,0xFF,0xFC,0x81,0x00,0x00, +0x3A,0x6F,0xAA,0xBC,0xEF,0xEC,0x9E,0x11,0xF0,0x81,0x83,0x81,0x84,0x80,0x85,0x40, +0x4E,0x07,0x00,0x36,0xF0,0x01,0x84,0xC0,0x40,0x80,0x10,0x01,0x4E,0x86,0x00,0x08, +0xD5,0x27,0x8C,0xC1,0x97,0xB0,0xE0,0xC8,0x4E,0xF2,0x00,0x23,0x40,0x93,0x08,0x08, +0x50,0x34,0x80,0x04,0x38,0x0E,0x1A,0x11,0x38,0x1E,0x0C,0x11,0x89,0x3C,0xE0,0x01, +0x40,0x7E,0x0C,0x00,0xE8,0xEF,0x84,0x44,0x80,0x29,0xB0,0x03,0x49,0x00,0x58,0x8A, +0x8C,0xC1,0x80,0x27,0x84,0x44,0x80,0x09,0x49,0x00,0x58,0x84,0x97,0xB0,0x80,0x07, +0xB0,0x43,0x84,0x44,0x49,0x00,0x58,0x7E,0x40,0x03,0x20,0x07,0xC8,0xE0,0x8D,0x41, +0xF0,0x01,0x54,0xA5,0x00,0xFF,0xE1,0x40,0x80,0x8A,0xE9,0xCD,0xEC,0x14,0x3A,0x6F, +0xAA,0x84,0xDD,0x9E,0xC3,0x66,0x3A,0x6F,0xAA,0xBC,0x8E,0x61,0xA7,0x83,0x00,0x90, +0x00,0x02,0x96,0xD8,0x94,0xD9,0x2E,0x76,0xCC,0x7E,0x8C,0x63,0x9D,0x51,0xEF,0xFC, +0x2E,0x06,0xCC,0x7F,0x88,0x43,0xFB,0x4F,0x87,0x80,0x85,0x00,0x00,0x32,0xFF,0xFF, +0x20,0xF2,0x80,0x00,0x88,0x66,0x96,0xDA,0x4E,0x35,0x00,0x24,0x41,0xE1,0x9C,0x07, +0x4F,0xE2,0x00,0x20,0x40,0xF4,0xBC,0x00,0x40,0xF7,0x80,0x10,0x4E,0xF5,0x00,0x1A, +0x41,0xE7,0x80,0x07,0x4F,0xE2,0x00,0x16,0x83,0xC1,0x43,0xE3,0xBC,0x73,0x38,0x3F, +0x0C,0x00,0x54,0xF1,0x80,0x20,0xE8,0x0D,0x54,0xF1,0x80,0x3F,0x5A,0xF0,0x3F,0x24, +0x54,0xF1,0x80,0x1F,0x5A,0xF0,0x1F,0x06,0x4E,0x83,0x00,0x18,0x81,0x43,0x85,0x01, +0x8C,0xA2,0xDA,0xD5,0xE7,0x02,0xE9,0x1E,0xA6,0x60,0x44,0x00,0x00,0x3F,0xE6,0x39, +0xE8,0x08,0x94,0x8A,0x88,0x44,0x8C,0x21,0xAF,0x95,0x10,0x91,0x00,0x04,0xAE,0x60, +0xEC,0x04,0x3A,0x6F,0xAA,0x84,0xDD,0x9E,0x4C,0x35,0x3F,0xEC,0x8D,0x01,0x54,0x84, +0x00,0xFF,0xD5,0xE9,0x51,0xCE,0x00,0x01,0x8C,0xA2,0x55,0xCE,0x00,0xFF,0xDA,0xB7, +0xD5,0xE2,0x80,0x0A,0x4E,0x83,0xFF,0xEE,0xFA,0x0F,0x4F,0xC2,0xFF,0xEB,0xD5,0xDD, +0xFA,0x0F,0xDD,0x9E,0x3A,0x6F,0xAA,0xBC,0xEF,0xD4,0x44,0x00,0xF1,0x2C,0x80,0xC2, +0xF2,0x81,0x81,0x43,0xF4,0x84,0xF5,0x82,0x3A,0x20,0x14,0x00,0x22,0x0F,0x80,0x24, +0xF0,0x85,0x00,0x0F,0x80,0x4C,0xF0,0x83,0xB0,0x06,0x3A,0x20,0x14,0x20,0x4E,0xA2, +0x00,0x23,0x81,0x21,0x2E,0x86,0xCC,0x7E,0xA6,0x72,0xA6,0x33,0x80,0x69,0x42,0x30, +0xA0,0x73,0x38,0x01,0x80,0x00,0x80,0x26,0x96,0x2E,0x8C,0x27,0x84,0xC0,0xC8,0x0E, +0x48,0x00,0x00,0x20,0x00,0x20,0x7F,0xFF,0x80,0x69,0xA6,0x00,0x42,0x31,0x20,0x73, +0x38,0x01,0x80,0x00,0x8C,0x24,0x96,0x2E,0xC0,0x14,0x8C,0xC1,0x97,0xB0,0x80,0x01, +0x4C,0x65,0x7F,0xF2,0xF0,0x04,0xEC,0x2C,0x3A,0x6F,0xAA,0x84,0xDD,0x9E,0xA6,0xBA, +0xA6,0x7B,0x80,0x69,0x42,0x31,0x20,0x73,0x38,0x01,0x84,0x08,0x8C,0xC1,0x97,0xB0, +0xE2,0xCA,0xE8,0xF1,0xF0,0x01,0x41,0xC3,0x08,0x08,0x40,0x70,0x70,0x00,0x80,0x07, +0x80,0x29,0xB0,0x86,0xF3,0x03,0xF4,0x02,0x49,0xFF,0xFF,0x46,0x5A,0x08,0x1F,0xE9, +0xF0,0x04,0xE6,0x0B,0xE8,0xEC,0xF0,0x01,0xF3,0x05,0xF2,0x04,0x38,0x00,0x70,0x11, +0x58,0x11,0x00,0x20,0xE0,0x60,0x8C,0x41,0xE8,0xE2,0xA6,0xFA,0xA6,0x3B,0x80,0x89, +0x42,0x41,0xA0,0x73,0x96,0x90,0xF2,0x84,0x38,0x12,0x00,0x08,0xD5,0xD8,0x92,0x00, +0x3A,0x6F,0xAA,0xBC,0x51,0xFF,0xFB,0x64,0xF0,0x8D,0x84,0x00,0xF1,0x8B,0xF2,0x9D, +0x10,0x0F,0x80,0x9D,0x84,0x20,0x44,0x20,0x01,0xB8,0x50,0x0F,0x82,0xE0,0xF3,0x98, +0x49,0x00,0x57,0xAE,0x84,0x20,0x44,0x20,0x00,0x66,0x44,0x02,0x3C,0x48,0x49,0x00, +0x57,0xA7,0x84,0x20,0x84,0x4B,0x44,0x02,0x13,0x28,0x49,0x00,0x57,0xA1,0x84,0x00, +0x84,0x3F,0x3C,0x0F,0xFB,0x46,0x3E,0x07,0xEC,0x9E,0x3E,0x17,0xEC,0x9D,0x3C,0x0B, +0xF6,0x69,0x3C,0x0B,0xF8,0x69,0x44,0x12,0x3F,0xC4,0xF0,0x0D,0x44,0x22,0x3D,0x44, +0xF3,0x0B,0xF4,0x18,0x49,0xFF,0xF0,0x72,0xF0,0x92,0x3C,0x07,0xF8,0x5F,0x5E,0xF0, +0x01,0x2C,0xE9,0x05,0x90,0x01,0xF0,0x99,0x48,0x00,0x00,0x05,0x44,0x00,0x00,0x96, +0xF0,0x99,0xF0,0x12,0x4E,0x02,0x01,0x9B,0x84,0x00,0xF0,0x8B,0xF0,0x9A,0x84,0x08, +0xF0,0x8E,0x84,0x00,0xF0,0x8F,0xF0,0x8C,0xF0,0x0B,0xF2,0x0B,0x94,0x41,0x44,0x02, +0x3F,0xC4,0xF3,0x0B,0x38,0x20,0x09,0x00,0x88,0x01,0xF2,0x91,0xF0,0xA2,0xFA,0x42, +0xF0,0x0C,0xFE,0x9C,0xF2,0xA4,0xF0,0x90,0xF0,0x22,0xF1,0x11,0xA6,0x01,0xE2,0x20, +0x4E,0xF2,0x01,0x71,0xF1,0x11,0x44,0x02,0x3D,0x44,0x88,0x01,0xF1,0x24,0x38,0x00, +0x04,0x00,0x96,0x36,0x4E,0x02,0x01,0x5F,0x50,0x0F,0x80,0x9D,0xF0,0x81,0xF0,0x0E, +0x84,0xC0,0xF0,0x82,0x84,0x01,0xF0,0x83,0x50,0x0F,0x80,0x9E,0xF0,0x84,0x50,0x7F, +0x81,0xF0,0xB0,0x28,0xF0,0x89,0x22,0x1F,0x80,0x16,0xB6,0xFF,0xF6,0x85,0xF6,0x86, +0xF6,0x87,0xF6,0x88,0xF0,0x11,0x44,0x20,0x00,0x40,0xF3,0x0F,0x44,0x42,0x3D,0x44, +0x04,0x5F,0x80,0x0D,0x49,0x00,0x1B,0xE1,0x2E,0x17,0xEC,0x9E,0x8C,0x21,0x3E,0x17, +0xEC,0x9E,0xF1,0x1D,0xE0,0x01,0x4E,0xF3,0x01,0x36,0xF0,0x0D,0x50,0x13,0x80,0x00, +0x00,0x2F,0x80,0x9D,0x49,0xFF,0xFE,0x4E,0x02,0x0F,0x80,0x4F,0x5C,0xF0,0x00,0x29, +0x4E,0xF2,0x00,0x0A,0x00,0x0F,0x80,0x9D,0x9E,0x42,0xE6,0x2D,0x4E,0xF3,0x00,0x2F, +0x48,0x00,0x03,0x55,0x00,0x1F,0x80,0x9D,0x80,0x06,0x96,0xB0,0xE2,0x41,0xE8,0x0B, +0x38,0xF3,0x9A,0x11,0xF2,0x19,0xE0,0x4F,0xE8,0x03,0x8C,0x01,0x96,0x01,0x8C,0xC1, +0x48,0xFF,0xFF,0xF5,0x3C,0x13,0xF8,0x69,0xE2,0x20,0xE8,0xE5,0x5C,0xF0,0x00,0x3D, +0x3C,0x0B,0xF8,0x69,0x80,0x20,0xE9,0x03,0x44,0x10,0x00,0x3C,0x96,0x49,0x84,0x00, +0x50,0x2F,0x81,0x78,0x50,0x3F,0x81,0xF0,0xE2,0x01,0xE8,0xD5,0x38,0x41,0x82,0x01, +0x38,0x41,0x01,0x09,0x8C,0x01,0x96,0x00,0xD5,0xF8,0xE6,0x06,0x80,0x20,0xE9,0x03, +0x44,0x10,0x00,0x05,0x55,0xE0,0x80,0xFF,0x50,0x0F,0x7F,0xFF,0x84,0x20,0x80,0x61, +0x80,0x41,0xF0,0x93,0xF0,0x13,0xE0,0x40,0x4E,0xF2,0x03,0x19,0x9C,0x11,0x96,0x00, +0xF0,0x8E,0x94,0x92,0x50,0x0F,0x81,0xF0,0x88,0x40,0xF6,0x0E,0xE2,0xDE,0xE8,0x4D, +0x50,0x0F,0x81,0xF0,0x95,0x32,0x88,0x80,0xA7,0xD3,0xA6,0x23,0x00,0x91,0x00,0x02, +0x9A,0x38,0x96,0x02,0xA7,0x62,0x81,0x00,0x4E,0x04,0x00,0x04,0x52,0x80,0x00,0x00, +0x5A,0x88,0x01,0x39,0x40,0x54,0x94,0x01,0x97,0x6A,0x81,0x05,0x4E,0x54,0x00,0x04, +0x52,0x82,0x80,0x00,0x5A,0x88,0x01,0x2F,0xA5,0x20,0x02,0x81,0x00,0x00,0x40,0xF2, +0x00,0x11,0x40,0xA4,0x00,0x11,0xE1,0x4F,0x40,0x44,0x3C,0x1A,0x97,0x23,0xC4,0x1B, +0x9A,0x38,0xFA,0x22,0x42,0x04,0x84,0x73,0xF1,0x0D,0x40,0x54,0x94,0x01,0x38,0x30, +0x81,0x11,0x44,0x00,0x00,0x64,0xFE,0xC4,0xFA,0x02,0x42,0x72,0x80,0x73,0x38,0x10, +0x9D,0x11,0x44,0x00,0x00,0x64,0xFE,0x44,0x40,0x31,0x90,0x76,0x40,0x10,0x90,0x36, +0x96,0xDB,0x96,0x4B,0xE0,0x61,0x80,0x03,0x40,0x00,0xBC,0x1B,0x5E,0xF0,0x00,0x3C, +0xE9,0x06,0x8C,0xC1,0x97,0xB0,0xD5,0xB3,0xF2,0x0E,0xD5,0xA5,0x00,0x0F,0x81,0xF2, +0xE6,0x02,0x4E,0xF3,0x02,0xBC,0x84,0x04,0xF0,0x8E,0xF6,0x0C,0x44,0x70,0x00,0x00, +0x50,0x9F,0x81,0xF0,0x00,0x0F,0x80,0x9D,0xE2,0xE0,0xE8,0x07,0x38,0xF4,0x9E,0x11, +0xF0,0x18,0xE0,0x0F,0x4E,0xF3,0x00,0xED,0xF0,0x1D,0x80,0x86,0xB6,0x1F,0xF0,0x0E, +0x00,0x3F,0x80,0x9D,0xF0,0x81,0x44,0x12,0x3D,0x44,0x50,0x2F,0x81,0xF0,0x44,0x52, +0x3C,0x48,0xF0,0x0D,0x49,0xFF,0xFE,0x30,0xF0,0x8C,0x00,0x0F,0x80,0x9D,0xB1,0xEF, +0xF0,0xA1,0x44,0x00,0xF1,0x3C,0x84,0xC0,0x3A,0x20,0x14,0x00,0x3A,0x23,0x94,0x20, +0x84,0x21,0x84,0x4B,0xB0,0x2C,0xF6,0xA9,0xF6,0xAA,0xF6,0xAB,0x49,0x00,0x56,0x48, +0x80,0x26,0xFA,0x5C,0xB0,0x3D,0x49,0x00,0x56,0x43,0x80,0x26,0x44,0x20,0x00,0x2C, +0x50,0x0F,0x81,0x20,0x49,0x00,0x56,0x3C,0x50,0x9F,0x80,0xCC,0x80,0x26,0xFA,0x5C, +0x50,0x0F,0x81,0x4C,0x49,0x00,0x56,0x34,0x80,0x09,0x80,0x26,0x44,0x20,0x00,0x28, +0x49,0x00,0x56,0x2E,0x50,0xAF,0x81,0xF0,0x2E,0x06,0xCC,0x7E,0xF6,0x94,0xF0,0x93, +0x14,0x9F,0x80,0x25,0x2E,0x06,0xCC,0x7F,0xF7,0xA0,0xF0,0xA3,0xF5,0x14,0xF1,0x21, +0x4C,0x50,0xC0,0xD4,0x04,0xAF,0x80,0x36,0x00,0x9F,0x80,0xEA,0x00,0x7F,0x80,0xE9, +0x00,0x3F,0x80,0xCF,0x00,0x4F,0x80,0xCE,0x00,0x6F,0x80,0xCD,0x00,0x5F,0x80,0xCC, +0xF0,0x10,0x50,0x8F,0x81,0x4C,0xF1,0x0C,0xE2,0x01,0x4E,0xF3,0x01,0xD7,0xF0,0x10, +0x10,0x6F,0x80,0xCD,0xE2,0x01,0x10,0x5F,0x80,0xCC,0x10,0x4F,0x80,0xCE,0x10,0x3F, +0x80,0xCF,0x14,0xAF,0x80,0x36,0x10,0x9F,0x80,0xEA,0x10,0x7F,0x80,0xE9,0x4E,0xF3, +0x02,0x09,0xF0,0x11,0x8C,0x01,0x96,0x00,0xF0,0x91,0xF0,0x0C,0xF0,0x90,0x48,0xFF, +0xFE,0x8D,0xF0,0x1A,0x5A,0x00,0x01,0x0D,0xF0,0x0B,0x8C,0x01,0x14,0x0F,0x80,0x0B, +0x5A,0x00,0x20,0x04,0x48,0xFF,0xFE,0x72,0xD5,0x03,0xF0,0x12,0xF0,0x8C,0xF1,0x12, +0x50,0x0F,0x81,0x78,0x49,0x00,0x08,0xDE,0x3C,0x0F,0xFB,0x46,0x04,0x0F,0x80,0x12, +0x49,0x00,0x08,0x8A,0x3C,0x1D,0xFB,0x46,0x84,0x4B,0xFE,0x0F,0x3C,0x0F,0xFB,0x46, +0x44,0x10,0x00,0xFF,0x44,0x02,0x3C,0xB0,0x84,0xE0,0x49,0x00,0x55,0xC9,0x80,0xC7, +0x50,0x0F,0x82,0xE0,0xFB,0x38,0x45,0xE2,0x13,0x48,0xF2,0x0C,0x96,0x78,0xE2,0x22, +0xE8,0x38,0x00,0x10,0x00,0x1E,0xC1,0x30,0x44,0x12,0x13,0x28,0x38,0xF0,0x9C,0x00, +0xE9,0x2B,0x42,0xA3,0x24,0x24,0x40,0x1F,0x28,0x00,0x8C,0x2C,0x81,0x01,0x80,0x20, +0x89,0x5E,0x3A,0x20,0x94,0x04,0x3A,0x24,0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x24, +0x14,0x24,0x3A,0x20,0x8C,0x00,0x3A,0x24,0x0C,0x20,0x9C,0x71,0x50,0x25,0x00,0x20, +0x80,0x7E,0x42,0x30,0xA4,0x73,0xAF,0xD5,0x84,0x81,0x44,0x22,0x3C,0xB0,0xAF,0x1C, +0x10,0xF5,0x00,0x20,0x84,0x7F,0xE4,0xCA,0x38,0x61,0x1C,0x08,0x10,0x35,0x00,0x2B, +0x84,0xCA,0xE8,0x02,0x97,0x88,0x8C,0xE1,0x50,0x00,0x00,0x28,0x48,0xFF,0xFF,0xC7, +0x3E,0x67,0xEE,0xE0,0x51,0xFF,0x84,0x9C,0x3A,0x6F,0xAA,0x84,0xDD,0x9E,0x94,0x7A, +0x88,0x29,0xFA,0x42,0xA6,0x0B,0xA6,0x4A,0x80,0x60,0x42,0x30,0x88,0x73,0x80,0x43, +0x44,0x32,0x3D,0x44,0x38,0x21,0x88,0x10,0x4E,0x24,0x00,0x20,0x50,0x2F,0x80,0x9D, +0xF2,0x81,0xF2,0x0E,0x58,0x33,0x00,0x20,0xF2,0x82,0x50,0x4F,0x80,0x9E,0x84,0x40, +0x8C,0xC1,0xF2,0x83,0xF4,0x84,0xF2,0x85,0xF2,0x86,0xF2,0x87,0xF2,0x88,0xF2,0x89, +0xB7,0x3F,0x44,0x20,0x00,0x80,0x96,0xD8,0x44,0x42,0x3D,0x44,0xF5,0x0D,0x97,0xB0, +0x49,0x00,0x1A,0x03,0xE6,0xCB,0xE8,0x05,0x8C,0xE1,0x97,0xF8,0x48,0xFF,0xFE,0xDC, +0x84,0x01,0xF0,0x9A,0x48,0xFF,0xFE,0xE2,0x00,0x75,0x00,0x03,0xF1,0x13,0x00,0x25, +0x00,0x02,0x80,0x07,0x42,0x01,0x04,0x73,0xF0,0x9B,0xF1,0x1B,0x44,0x02,0x3D,0x44, +0x38,0x10,0x04,0x00,0x54,0x00,0x80,0x20,0x4E,0x02,0x01,0x01,0x5A,0x18,0x3F,0x04, +0x48,0x00,0x00,0xBC,0x96,0x67,0xE6,0x2B,0x4E,0xF2,0x00,0xF9,0x94,0x0A,0x50,0x9F, +0x80,0xF4,0x40,0x34,0x80,0x00,0x50,0x5F,0x81,0x20,0x50,0x4F,0x81,0x4C,0xF3,0x95, +0x98,0xE8,0x88,0x04,0xF3,0x96,0xF0,0x97,0x50,0x3F,0x82,0xE0,0xFA,0x18,0x42,0x30, +0x80,0x73,0x80,0x03,0x50,0x3F,0x80,0xB0,0x38,0x31,0x84,0x00,0x5A,0x30,0x01,0x04, +0x48,0x00,0x00,0x87,0x84,0x60,0x14,0x9F,0x80,0x1E,0x80,0xC3,0x81,0x25,0xF4,0x9F, +0xF4,0x20,0x04,0x8F,0x80,0x20,0x38,0x52,0x0D,0x00,0x95,0x19,0x88,0xA7,0x97,0x68, +0x88,0x88,0x41,0xE2,0x80,0x10,0x20,0x42,0x00,0x01,0x4F,0xE5,0x00,0x4C,0x04,0x8F, +0x80,0x13,0x40,0xFF,0x20,0x07,0xE8,0x46,0x88,0x82,0x97,0x20,0xF4,0x9C,0x20,0x8F, +0x80,0x70,0x4E,0x85,0x00,0x40,0xF4,0x23,0xE1,0x04,0xE8,0x3C,0xF4,0x13,0x8C,0xC1, +0x43,0xE4,0x10,0x73,0xF4,0x0D,0x04,0x80,0x00,0x04,0x97,0xB0,0x38,0xF2,0x79,0x11, +0x44,0x42,0x3D,0x44,0x89,0x0F,0x14,0x80,0x00,0x04,0x38,0x82,0x78,0x00,0x5A,0x88, +0x1F,0x2A,0x4E,0xF7,0x00,0x07,0x04,0x80,0x00,0x03,0x89,0x0F,0x14,0x80,0x00,0x03, +0x50,0xF7,0xFF,0xFB,0x4E,0xF7,0x00,0x1F,0xF4,0x1E,0x38,0x82,0x06,0x02,0x89,0x0F, +0x38,0x82,0x06,0x0A,0x44,0x42,0x3D,0x20,0x38,0x82,0x15,0x01,0x38,0x54,0x86,0x02, +0x42,0x57,0xA0,0x73,0x38,0x54,0x86,0x0A,0xF5,0x1C,0x44,0x42,0x3F,0x84,0x38,0x52, +0x15,0x01,0xF4,0x1F,0x38,0x42,0x06,0x02,0x42,0x47,0x94,0x73,0xF5,0x1F,0x38,0x42, +0x86,0x0A,0x8C,0x61,0xF5,0x0E,0x97,0x18,0xE2,0x85,0xE9,0xA3,0x22,0x35,0x00,0x00, +0xA1,0x04,0xFF,0x9C,0x9B,0x74,0x44,0x30,0x03,0xE8,0xFE,0xEC,0x40,0x61,0x98,0xD6, +0xAE,0x82,0xAE,0x83,0x10,0x20,0x00,0x1C,0xF3,0x1B,0xF2,0x0D,0x12,0x60,0x00,0x12, +0xAF,0xC0,0xAF,0xC1,0x10,0x70,0x00,0x1B,0x38,0x21,0x0D,0x11,0xB0,0xEC,0x88,0x82, +0xA9,0x04,0x84,0x80,0x12,0x20,0x00,0x0B,0x38,0x41,0x84,0x08,0xD5,0x25,0xA6,0x43, +0xE2,0x22,0xE8,0x03,0xAE,0x83,0xD5,0x05,0xA6,0x42,0xE2,0x41,0xE8,0x02,0xAE,0x82, +0x00,0x15,0x00,0x03,0xA6,0x81,0xE2,0x41,0xE8,0x03,0xAE,0x41,0xD5,0x0D,0xA6,0x80, +0xE2,0x22,0xE8,0x0A,0xAE,0x40,0xD5,0x08,0xB0,0x6B,0xF1,0x97,0xB0,0x6A,0xF1,0x96, +0xB0,0x69,0xF0,0x25,0xF1,0x95,0xF2,0x13,0x00,0x15,0x00,0x02,0x42,0x70,0x88,0x73, +0xF2,0x0D,0x38,0x21,0x1D,0x11,0xA0,0x43,0x88,0x22,0xA8,0x43,0xF1,0x18,0xE0,0x22, +0xE8,0x06,0x00,0x10,0x00,0x1D,0x8C,0x21,0x10,0x10,0x00,0x1D,0x8E,0x45,0x4E,0x27, +0x00,0x26,0xF1,0x15,0xF3,0x15,0xB4,0x21,0x88,0x22,0xB6,0x23,0x00,0x15,0x00,0x03, +0x44,0x32,0x3D,0x20,0x38,0x31,0x85,0x01,0xF1,0x16,0xB4,0x21,0x42,0x11,0x0C,0x73, +0xF3,0x16,0xB6,0x23,0x00,0x15,0x00,0x02,0x44,0x32,0x3F,0x84,0x38,0x31,0x85,0x01, +0xF1,0x17,0xB4,0x21,0x42,0x11,0x0C,0x73,0xF2,0x17,0xB6,0x22,0x00,0x10,0x00,0x1E, +0x5A,0x10,0xFF,0x05,0x8C,0x21,0x10,0x10,0x00,0x1E,0xF6,0x14,0x8D,0x44,0x8C,0xC1, +0x96,0x30,0xF0,0x94,0x48,0xFF,0xFE,0x14,0xB0,0x7D,0x38,0xF0,0x82,0x02,0x4E,0xF7, +0x00,0x19,0x50,0x2F,0x81,0x20,0xFA,0x38,0x51,0xEF,0x82,0xE0,0x38,0x21,0x02,0x02, +0x43,0xE0,0x04,0x73,0x40,0x17,0x84,0x0A,0x88,0x41,0x40,0x21,0x3C,0x56,0x14,0x2F, +0x00,0x01,0x38,0x24,0x02,0x02,0x88,0x22,0x40,0x10,0xBC,0x36,0x14,0x1F,0x00,0x02, +0xFA,0x58,0x50,0x1F,0x82,0xE0,0x42,0x10,0x08,0x73,0xA0,0x8B,0x01,0xE0,0x80,0x03, +0x89,0x42,0x00,0x20,0x80,0x1E,0xE2,0x7E,0x88,0x49,0x54,0x91,0x00,0xFF,0x00,0x20, +0x80,0x1D,0x88,0xE2,0x97,0xF8,0xE9,0x06,0xA6,0x8A,0x83,0xC3,0xE2,0x44,0x40,0x41, +0x3C,0x1B,0xA6,0x89,0xE2,0xC2,0xE9,0x06,0xA6,0x48,0x80,0x46,0xE2,0x25,0x40,0x50, +0xBC,0x1B,0x8C,0x01,0x96,0x00,0x80,0x7E,0x50,0x61,0x00,0x00,0x48,0xFF,0xFD,0xE5, +0x00,0x0F,0x80,0x9D,0x3C,0x13,0xF6,0x69,0xE2,0x20,0xE8,0x07,0x3C,0x0B,0xF6,0x69, +0x00,0x0F,0x80,0x3C,0x3E,0x07,0xEC,0x9D,0xFA,0x38,0xF2,0x10,0x50,0x0F,0x82,0xE0, +0x42,0x01,0x04,0x73,0x00,0x2F,0x80,0x3C,0x10,0x20,0x00,0x22,0xF0,0x10,0xF2,0x0C, +0x8C,0x01,0x97,0x40,0xF5,0x90,0xDA,0xF2,0xF0,0x0F,0xE6,0x0B,0x4E,0xF2,0xFD,0xF1, +0x8C,0x01,0x96,0x00,0x14,0x0F,0x80,0x0F,0x5A,0x08,0x0B,0x04,0x48,0xFF,0xFD,0xE9, +0xE6,0x4B,0x4E,0xF3,0xFD,0xD0,0x48,0xFF,0xFD,0xE4,0x84,0x08,0xF0,0x8E,0x48,0xFF, +0xFD,0x46,0x92,0x00,0x3B,0xFF,0xFC,0xBC,0xEF,0xFC,0xFA,0x40,0x5A,0x08,0x04,0x0A, +0x44,0x12,0x10,0x30,0x44,0x02,0x39,0xC8,0x49,0x00,0x53,0x8C,0x48,0x00,0x00,0x08, +0x44,0x02,0x39,0xC8,0x44,0x12,0x10,0x20,0x49,0x00,0x53,0x84,0xEC,0x04,0x3B,0xFF, +0xFC,0x84,0xDD,0x9E,0x3A,0x6F,0xA8,0xBC,0x3C,0x6D,0xFB,0x37,0xEF,0xF8,0x54,0x63, +0x04,0x00,0xF0,0x81,0xC6,0x06,0x3C,0x6D,0xFB,0x37,0x97,0x9E,0xC6,0x02,0x84,0xC2, +0x3C,0x74,0x0A,0xB0,0x3C,0xA4,0x0A,0xB1,0x3C,0x94,0x0A,0xB2,0x49,0x00,0x44,0xCA, +0x3C,0x37,0xF8,0x5F,0x84,0x44,0x40,0x21,0x88,0x56,0x5A,0x08,0x01,0x15,0x5E,0xF1, +0x01,0xC2,0xE8,0x03,0x44,0x20,0x01,0xC2,0xFA,0x9D,0x44,0xF0,0x00,0x64,0xFE,0xE4, +0x40,0x31,0xBC,0x76,0x5E,0xF1,0x82,0x58,0x96,0x93,0xE8,0x2B,0x44,0x30,0x02,0x58, +0x48,0x00,0x00,0x28,0x5A,0x68,0x02,0x18,0x5E,0xF1,0x00,0xFA,0xE8,0x03,0x44,0x20, +0x00,0xFA,0xFA,0x38,0x44,0xF0,0x00,0x64,0xFE,0xCC,0x40,0x31,0xBC,0x76,0x5E,0xF1, +0x81,0x2C,0x96,0x93,0xE8,0x03,0x44,0x30,0x01,0x2C,0x96,0xDB,0x44,0x70,0x00,0x64, +0x48,0x00,0x00,0x11,0x44,0x40,0x00,0x46,0x42,0x01,0x90,0x24,0x44,0x30,0x00,0x64, +0xE0,0x4A,0x40,0x30,0x0C,0x76,0x40,0x25,0x3C,0x1B,0xE0,0x69,0x40,0x34,0xBC,0x1B, +0x96,0xDB,0xF0,0x01,0xEC,0x08,0x80,0x27,0x3A,0x6F,0xA8,0x84,0x48,0xFF,0xFB,0x8A, +0xFC,0x40,0xEE,0x98,0x44,0x62,0x40,0x0C,0x87,0x80,0x80,0xE6,0x81,0x3C,0x44,0x82, +0x15,0x0C,0x84,0x02,0x10,0x03,0x80,0xAE,0x02,0x03,0x80,0x54,0x54,0xAE,0x00,0xFF, +0xC0,0x38,0x00,0x03,0x80,0xAD,0x5A,0x08,0x02,0x2A,0xB0,0x41,0x84,0x40,0x80,0x07, +0x49,0xFF,0xEB,0x5B,0xF0,0x01,0x40,0x14,0x94,0x08,0xB0,0x82,0x88,0x22,0x22,0x20, +0x00,0x04,0x22,0x33,0x80,0x46,0xA8,0x89,0x22,0x20,0x00,0x05,0xA8,0x8A,0x50,0x20, +0x80,0x08,0xA8,0xD1,0x80,0x41,0x22,0x33,0x80,0x47,0x18,0xA1,0x00,0x10,0xA8,0xCC, +0x04,0x33,0x80,0x29,0xB4,0x00,0xA8,0xD1,0x04,0x23,0x80,0x28,0xA8,0x0F,0x50,0x04, +0x80,0x01,0xA8,0x8E,0x54,0x90,0x00,0xFF,0xD5,0x0C,0x5A,0x00,0x04,0xD8,0x5A,0x08, +0x03,0x09,0x80,0x0A,0x49,0x00,0x19,0x58,0x38,0x14,0x70,0x00,0xE2,0x20,0xE8,0xCE, +0x51,0xCE,0x00,0x01,0x50,0x73,0x80,0xCC,0x5B,0xC8,0x0B,0xBD,0x2E,0x07,0xEE,0xE0, +0xC0,0x41,0x80,0x49,0x44,0x02,0x13,0x48,0xB0,0x42,0x84,0xE0,0x49,0x00,0x0D,0x19, +0x81,0x27,0x02,0x03,0x00,0x54,0xC0,0x28,0x00,0x03,0x00,0xAD,0x5A,0x08,0x01,0x25, +0xB0,0x41,0x84,0x40,0x80,0x06,0x49,0xFF,0xEB,0x10,0xF1,0x01,0x94,0x3D,0xB0,0x82, +0x88,0x02,0x22,0x20,0x80,0x04,0x22,0x33,0x00,0x46,0xA8,0x81,0x22,0x20,0x80,0x05, +0x8C,0xE1,0xA8,0x82,0x50,0x20,0x00,0x08,0xA8,0xD1,0x80,0x40,0x22,0x33,0x00,0x47, +0x18,0x91,0x00,0x10,0xA8,0xC4,0x83,0x86,0xBB,0x29,0xB4,0x21,0xA8,0xD1,0xBA,0x28, +0xA8,0x47,0xA8,0x86,0x97,0xF8,0x50,0x04,0x80,0x01,0x54,0x90,0x00,0xFF,0x50,0x63, +0x00,0xCC,0x5A,0x98,0x0B,0xD0,0x44,0x02,0x13,0x48,0xB0,0x42,0x80,0x47,0x49,0x00, +0x0C,0xE0,0xED,0x68,0xFC,0xC0,0xFC,0x46,0xE6,0x2C,0x81,0x01,0x4E,0xF2,0x00,0xA2, +0x81,0x20,0x80,0xE2,0x5A,0x28,0x01,0x6A,0xFA,0xB8,0x42,0x20,0x14,0x24,0x44,0x42, +0x13,0x48,0x98,0x22,0x51,0xC0,0x00,0x20,0xA0,0xC6,0x10,0x10,0x00,0x2B,0xF3,0x85, +0x00,0x30,0x00,0x2A,0x50,0x10,0x00,0x10,0x10,0x3F,0x80,0x28,0x50,0xA0,0x00,0x18, +0x22,0x30,0x00,0x08,0x44,0x60,0x00,0xCC,0x02,0x00,0x00,0x11,0x22,0x10,0x80,0x02, +0x42,0x64,0x18,0x24,0x12,0x3F,0x80,0x0E,0x12,0x3F,0x80,0x0C,0x12,0x0F,0x80,0x15, +0x44,0x32,0x40,0x04,0x50,0x03,0x00,0x08,0x12,0x1F,0x80,0x0F,0x12,0x1F,0x80,0x0D, +0x88,0x03,0xB0,0x45,0xF5,0x83,0xF2,0x82,0xF4,0x81,0xB6,0x7F,0x49,0xFF,0xEA,0x76, +0xB4,0x7F,0x00,0x0E,0x00,0x07,0x50,0x11,0x80,0xC8,0x88,0x26,0x54,0xF0,0x00,0x0F, +0x41,0xE7,0xB0,0x08,0x02,0xF0,0x80,0x02,0x92,0x04,0x54,0xF7,0x8F,0xFF,0x40,0xF7, +0xF8,0x04,0x12,0xF0,0x80,0x02,0x00,0xFE,0x00,0x08,0x96,0x0F,0x40,0xF7,0x88,0x08, +0x40,0x00,0x3C,0x04,0xAE,0x0E,0x98,0x1E,0x50,0x00,0x00,0xC0,0x02,0x15,0x00,0x05, +0xF4,0x01,0xAC,0x41,0x00,0x1E,0x00,0x04,0xF5,0x03,0xAE,0x44,0x80,0x44,0x50,0x14, +0x80,0x01,0x42,0x20,0x94,0x73,0xA6,0x56,0xF2,0x02,0xAE,0x45,0x88,0x44,0x88,0xC3, +0xA0,0x13,0x14,0x03,0x00,0x27,0xD5,0x12,0x5A,0x28,0x02,0x34,0x44,0x00,0x00,0xCC, +0x44,0x62,0x40,0x0C,0x42,0x60,0x80,0x73,0x80,0x06,0xB0,0x44,0x84,0x40,0x49,0xFF, +0xEA,0x64,0x80,0x06,0xF1,0x04,0x49,0xFF,0xEA,0x31,0x44,0x32,0x40,0x04,0x44,0x00, +0x00,0xCC,0x80,0x23,0x42,0x14,0x00,0x73,0x02,0x00,0x80,0x58,0xC8,0x0C,0xFA,0x58, +0x44,0x02,0x13,0x48,0x42,0x04,0x88,0x73,0xA0,0x84,0xA0,0x05,0x12,0x20,0x80,0x64, +0x12,0x00,0x80,0x65,0x44,0x00,0x00,0xCC,0x42,0x34,0x00,0x73,0x02,0x51,0x80,0x58, +0x44,0x10,0xFF,0xFF,0x10,0x71,0x80,0xB6,0xD1,0x04,0x8C,0xA1,0x12,0x51,0x80,0x58, +0xFC,0xC6,0xFC,0x01,0x49,0x00,0x41,0xAF,0xF0,0x81,0x49,0xFF,0xEA,0x4C,0xF0,0x01, +0x44,0x12,0x3F,0xC4,0x3C,0x24,0x0A,0xD3,0x49,0xFF,0xEA,0x6D,0xF0,0x01,0x44,0x12, +0x3F,0xC4,0x44,0x22,0x15,0x26,0x44,0x32,0x15,0x24,0x49,0xFF,0xEA,0xA3,0x3C,0x13, +0xF6,0x93,0x3C,0xF7,0xF8,0x5F,0x3C,0x24,0x0A,0xD3,0x84,0x1E,0xFE,0x0E,0xE0,0x4F, +0x3C,0x0B,0xF6,0x93,0xE8,0x05,0x58,0x00,0x00,0x01,0x3C,0x0B,0xF6,0x93,0xFC,0x81, +0xFC,0x40,0x84,0x20,0x84,0x48,0x44,0x02,0x13,0x40,0x49,0x00,0x51,0xE1,0x84,0x00, +0x3C,0x0B,0xF6,0x93,0x84,0x01,0x49,0x00,0x17,0xEE,0x84,0x03,0x49,0x00,0x17,0xFE, +0x84,0x20,0x44,0x20,0x08,0xCC,0x44,0x02,0x40,0x04,0x49,0x00,0x51,0xD1,0x49,0x00, +0x18,0x2D,0x49,0x00,0x33,0xAD,0x5A,0x00,0x01,0x05,0x44,0x00,0x00,0x5A,0xD5,0x03, +0x44,0x00,0x00,0x7D,0x3C,0x08,0x0A,0xD3,0x84,0x00,0x3E,0x07,0xEE,0xD4,0x84,0x01, +0x49,0xFF,0xFE,0x02,0x44,0x00,0x00,0xBB,0x3C,0x08,0x0A,0xB8,0x44,0x00,0x19,0xAE, +0x3C,0x08,0x0A,0xB9,0x44,0x00,0x00,0xE1,0x3C,0x08,0x0A,0xBA,0x44,0x00,0x36,0xE0, +0x3C,0x08,0x0A,0xBB,0x49,0x00,0x04,0x44,0x84,0x01,0x49,0x00,0x04,0x4C,0x84,0x04, +0x3E,0x00,0x15,0x80,0x84,0x05,0x3E,0x00,0x15,0x82,0x44,0x00,0x00,0x40,0x3E,0x00, +0x15,0x81,0x84,0x0A,0x3E,0x00,0x15,0x83,0x3C,0x70,0x0A,0xB8,0x84,0x01,0x3E,0x00, +0x15,0x84,0x3C,0x50,0x0A,0xBA,0x3C,0x04,0x0A,0xB9,0x97,0x3B,0x84,0x60,0x2E,0x96, +0xCC,0x7E,0x40,0xA0,0x10,0x01,0x96,0x6B,0x3C,0x64,0x0A,0xBB,0x80,0x43,0x44,0x82, +0x3D,0x20,0x8A,0x80,0x45,0xE0,0x00,0x11,0x96,0x10,0xE2,0x09,0x88,0x6A,0xE8,0x09, +0x98,0x23,0x40,0x00,0x78,0x16,0x88,0x07,0x38,0x04,0x09,0x09,0x8C,0x41,0xD5,0xF5, +0x84,0x60,0x9B,0xF1,0x2E,0x46,0xCC,0x7F,0x8A,0x26,0x80,0x43,0x44,0x92,0x3F,0x84, +0xFA,0xCF,0x96,0x10,0xE2,0x04,0x88,0x67,0xE8,0x09,0x98,0x0B,0x40,0x00,0x18,0x16, +0x88,0x05,0x38,0x04,0x89,0x09,0x8C,0x41,0xD5,0xF5,0x3C,0x1D,0xFB,0x37,0x84,0x01, +0x3C,0x0B,0xF6,0x92,0x44,0x0F,0xFF,0x7F,0xFE,0x0E,0x3C,0x0F,0xFB,0x37,0x3C,0x0D, +0xFB,0x37,0x58,0x00,0x00,0x04,0x3C,0x0F,0xFB,0x37,0x84,0x00,0x3C,0x0B,0xF6,0x91, +0xFC,0xC0,0x84,0x00,0x3E,0x07,0xEE,0xD4,0xDD,0x9E,0xFC,0x00,0x2E,0x07,0xDE,0x85, +0x96,0x16,0xC0,0x03,0x84,0x04,0xD5,0x02,0x84,0x01,0x49,0xFF,0xFD,0x85,0xFC,0x80, +0xFC,0x00,0x84,0x01,0x49,0x00,0x03,0xDF,0xFC,0x80,0x84,0x00,0x3E,0x07,0xDE,0x85, +0x3C,0x0D,0xFB,0x37,0x96,0x1E,0xC0,0x04,0x84,0x04,0x3E,0x07,0xDE,0x85,0x2E,0x07, +0xDE,0x85,0xC8,0x04,0x84,0x01,0x3E,0x07,0xDE,0x85,0xDD,0x9E,0xFC,0x00,0x2E,0x07, +0xDE,0x85,0x5A,0x08,0x04,0x05,0x3C,0x03,0xF5,0xE5,0xD5,0x03,0x3C,0x03,0xF5,0xDD, +0x3C,0x08,0x0A,0xB1,0x49,0xFF,0xFF,0xE3,0x2E,0x07,0xDE,0x85,0x2E,0x57,0xEE,0xD4, +0xD0,0x07,0x3E,0x07,0xEE,0xD4,0x49,0xFF,0xFF,0xCA,0x49,0xFF,0xFF,0xD3,0xFC,0x80, +0xFC,0x00,0x49,0x00,0x40,0xB6,0x84,0x20,0x44,0x32,0x20,0xE8,0x38,0x21,0x85,0x11, +0x40,0x21,0x20,0x08,0x38,0x20,0x06,0x0A,0x8C,0x21,0x5A,0x1A,0x40,0xF9,0xFC,0x80, +0xFC,0x43,0x84,0x00,0x3C,0x13,0xF6,0x93,0x3E,0x07,0xEE,0xE0,0x44,0x0F,0xFF,0xBF, +0xFE,0x0E,0x3C,0x0B,0xF6,0x93,0x49,0x00,0x32,0x76,0x5A,0x00,0x01,0x06,0x3C,0x0C, +0x05,0x45,0x5A,0x08,0x01,0x0A,0x3C,0x03,0xF6,0x91,0x58,0x00,0x00,0x01,0x3C,0x0B, +0xF6,0x91,0x49,0x00,0x32,0x6B,0x3C,0x23,0xF6,0x91,0x54,0x01,0x00,0x01,0xC0,0x06, +0x3C,0x13,0xF7,0x68,0x8C,0x21,0x3C,0x1B,0xF7,0x68,0x3C,0x03,0xF7,0x68,0xE6,0x03, +0xE9,0x08,0x84,0x1E,0xFE,0x16,0x3C,0x0B,0xF6,0x91,0x84,0x00,0x3C,0x0B,0xF7,0x68, +0x3C,0xF7,0xF8,0x5F,0x5E,0xF7,0x80,0x5A,0xE8,0x0B,0x3C,0x03,0xF8,0x5E,0x50,0x00, +0x00,0x59,0x96,0x01,0x5C,0xF0,0x00,0xB3,0xE8,0x03,0x49,0x00,0x32,0x35,0x49,0x00, +0x40,0x62,0x80,0xC0,0x49,0xFF,0xE9,0xF9,0x49,0xFF,0xFF,0x92,0x80,0x06,0x49,0xFF, +0xFD,0x13,0x3C,0x0D,0xFB,0x46,0x5A,0x08,0x01,0x0C,0x49,0x00,0x32,0x22,0xC8,0x08, +0x49,0x00,0x32,0x26,0xC8,0x05,0x49,0xFF,0xE7,0x26,0x48,0x00,0x02,0x95,0x49,0xFF, +0xFD,0x61,0x44,0x02,0x13,0x54,0x2E,0x17,0xEE,0xE0,0x49,0x00,0x0D,0xF1,0x44,0x02, +0x3C,0x48,0xB6,0x1F,0x44,0x02,0x3D,0x44,0xF0,0x81,0x3C,0x10,0x0A,0xB3,0x3C,0x00, +0x0A,0xB1,0x3C,0x20,0x0A,0xB5,0x3C,0x30,0x0A,0xB7,0x3C,0x90,0x0A,0xB0,0x3C,0x70, +0x0A,0xB2,0x3C,0x50,0x0A,0xB4,0x3C,0x40,0x0A,0xB6,0x40,0x00,0x40,0x08,0x40,0x10, +0xC0,0x08,0x40,0x21,0x40,0x08,0x40,0x31,0xC0,0x08,0x40,0x00,0x24,0x04,0xFE,0x7F, +0xFE,0xAF,0xFE,0xE7,0xF6,0x82,0x44,0x42,0x40,0x0C,0x44,0x52,0x13,0x48,0x49,0x00, +0x05,0x0F,0x44,0x72,0x40,0x04,0x80,0x06,0x44,0x12,0x13,0x54,0x2E,0x27,0xEE,0xE0, +0x3C,0x34,0x0A,0xB0,0x49,0x00,0x0E,0x2E,0x81,0x27,0x84,0xC0,0x02,0x04,0x80,0x58, +0xC0,0x08,0x00,0x04,0x80,0xCF,0x80,0x26,0x00,0x24,0x80,0xB6,0x49,0xFF,0xFD,0xAD, +0x8C,0xC1,0x97,0xB0,0x50,0x94,0x80,0xCC,0x5A,0x68,0x0B,0xF2,0x84,0xC0,0x44,0x92, +0x13,0x48,0x44,0x82,0x40,0xD4,0x2E,0x07,0xEE,0xE0,0xE2,0xC0,0xE8,0x18,0xFA,0x18, +0x80,0x29,0x42,0x13,0x00,0x73,0x00,0x10,0x80,0x2B,0x50,0xA3,0x00,0x01,0x5A,0x18, +0xFF,0x23,0x80,0x29,0x42,0x15,0x00,0x73,0xA6,0x8C,0x5A,0x28,0x01,0x1D,0xF2,0x84, +0x49,0x00,0x16,0xA0,0xE6,0x0B,0x80,0x20,0xF2,0x04,0xE9,0x09,0x49,0x00,0x09,0x5E, +0x3C,0x0D,0xFB,0x37,0x96,0x1E,0xC0,0x12,0x84,0x04,0xD5,0x11,0x44,0x00,0x00,0xCC, +0x80,0x68,0x42,0x30,0x80,0x73,0xB4,0x03,0x58,0x00,0x00,0x08,0xB6,0x03,0x80,0x06, +0x49,0xFF,0xFD,0x73,0x54,0x65,0x00,0xFF,0xD5,0xCF,0x84,0x03,0x49,0x00,0x16,0x46, +0x3C,0x0C,0x05,0x45,0xC8,0x08,0x85,0x20,0x44,0x72,0x40,0x0C,0x80,0xC9,0x44,0xA2, +0x15,0x0C,0xD5,0x2A,0x84,0xC0,0x00,0x03,0x80,0xB6,0x5A,0x08,0x01,0x0E,0x00,0x03, +0x80,0xB5,0xC8,0x0A,0x22,0xF3,0x80,0x61,0x5E,0xF7,0x80,0xC9,0xE9,0x05,0x80,0x06, +0x84,0x22,0x49,0x00,0x16,0x39,0x8C,0xC1,0x97,0xB0,0x50,0x73,0x80,0xCC,0x5A,0x68, +0x0B,0xEC,0xD5,0xE2,0x00,0x23,0x80,0xAE,0x5A,0x20,0x01,0x28,0x00,0x03,0x80,0xAD, +0x5A,0x08,0x02,0x04,0x48,0x00,0x00,0xC5,0x8D,0x21,0x50,0x73,0x80,0xCC,0x5A,0x98, +0x0B,0x04,0x48,0x00,0x00,0xC9,0x54,0x04,0x80,0xFF,0xF0,0x84,0x00,0x03,0x80,0xAD, +0xE6,0x05,0xE8,0xED,0x44,0xF0,0x50,0xE4,0x38,0x07,0x82,0x02,0x40,0xF0,0x3C,0x00, +0x4A,0x00,0x3C,0x00,0xC0,0xFF,0xFF,0xFF,0x40,0x00,0x00,0x00,0x96,0x00,0x00,0x00, +0xDA,0x00,0x00,0x00,0x16,0x01,0x00,0x00,0x80,0x22,0xF0,0x04,0xF2,0x85,0x49,0x00, +0x16,0x0C,0xF0,0x04,0x49,0x00,0x16,0x20,0x81,0x00,0xF0,0x04,0x49,0x00,0x15,0xE9, +0x8C,0xC1,0xE2,0x08,0x97,0xB0,0xF2,0x05,0xF0,0x04,0xE9,0x5D,0x80,0x22,0x49,0x00, +0x15,0xF3,0xD5,0xC5,0x00,0x13,0x80,0xAE,0x5A,0x18,0x02,0x0B,0xF0,0x04,0x84,0x20, +0x49,0x00,0x15,0xF3,0xF0,0x04,0x84,0x21,0x49,0x00,0x15,0xEF,0xD5,0x6E,0x5A,0x18, +0x01,0xB7,0xF0,0x04,0x49,0x00,0x15,0xE9,0xF0,0x04,0x49,0x00,0x15,0xFD,0x81,0x00, +0xF0,0x04,0x49,0x00,0x15,0xC6,0x8C,0xC1,0xE2,0x08,0x97,0xB0,0xE8,0xA8,0x2E,0x00, +0x1B,0x9C,0xE6,0x03,0xE8,0x37,0x02,0x03,0x80,0x54,0xE6,0x08,0xE8,0x33,0x22,0xF3, +0x80,0x5D,0x5E,0xF7,0x82,0x27,0xE9,0x2E,0xD5,0x29,0xF0,0x04,0x84,0x20,0x49,0x00, +0x15,0xCC,0x00,0x03,0x80,0xAE,0x5A,0x08,0x02,0x14,0x84,0x21,0xF0,0x04,0x49,0x00, +0x15,0xC4,0xF0,0x04,0x49,0x00,0x15,0xD8,0x81,0x00,0xF0,0x04,0x49,0x00,0x15,0xAF, +0xE2,0x08,0xF0,0x04,0xE9,0x3B,0x84,0x23,0x49,0x00,0x15,0xAE,0xD5,0x45,0x8C,0xC1, +0xF0,0x04,0x84,0x20,0x97,0xB0,0x49,0x00,0x15,0xB0,0x48,0xFF,0xFF,0x79,0x00,0x03, +0x80,0xAE,0x5A,0x08,0x01,0x0B,0x8C,0xC1,0x97,0xB0,0xF0,0x04,0x84,0x20,0x49,0x00, +0x15,0xA4,0xF0,0x04,0x84,0x22,0xD5,0xA4,0x5A,0x00,0x02,0x04,0x48,0xFF,0xFF,0x68, +0x84,0x21,0xF0,0x04,0x49,0x00,0x15,0x99,0xF0,0x04,0x49,0x00,0x15,0xAD,0x81,0x00, +0xF0,0x04,0x49,0x00,0x15,0x84,0xE2,0x08,0xD5,0x0F,0x00,0x03,0x80,0xAE,0x5A,0x08, +0x02,0x18,0x84,0x21,0xF0,0x04,0x49,0x00,0x15,0x88,0xF0,0x04,0x49,0x00,0x15,0x9C, +0x38,0x15,0x24,0x00,0xE2,0x20,0xE8,0x10,0xF0,0x04,0x49,0x00,0x15,0xB8,0x80,0x07, +0x84,0x20,0x44,0x20,0x00,0xCC,0x49,0x00,0x4F,0x3B,0x48,0xFF,0xFF,0x41,0xF0,0x04, +0x84,0x20,0x49,0x00,0x15,0x72,0x8C,0xC1,0x97,0xB0,0x48,0xFF,0xFF,0x39,0x02,0x53, +0x80,0x55,0x44,0x00,0xFF,0xFF,0x4C,0x50,0x3F,0x39,0x8C,0xA1,0x12,0x53,0x80,0x55, +0x48,0xFF,0xFF,0x34,0x3C,0x13,0xF6,0x93,0x84,0x1D,0xFE,0x0E,0xC6,0x03,0x58,0x00, +0x00,0x02,0x3C,0x0B,0xF6,0x93,0x2E,0x00,0x1B,0x9F,0x3E,0x00,0x1B,0xA0,0x2E,0x00, +0x1B,0x9E,0x3E,0x00,0x1B,0x9F,0x2E,0x00,0x1B,0x9D,0x3E,0x00,0x1B,0x9E,0x2E,0x00, +0x1B,0x9C,0x3E,0x00,0x1B,0x9D,0x84,0x00,0x3C,0x0E,0x05,0x45,0x3C,0x03,0xF6,0x93, +0x3E,0x60,0x1B,0x9C,0x54,0x10,0x00,0x02,0xC1,0x03,0x84,0x02,0xD5,0x23,0x2E,0x17, +0xEE,0xD8,0xE6,0x24,0xE8,0x2F,0x44,0xF0,0x52,0xB4,0x38,0x17,0x84,0x00,0x40,0xF0, +0xBC,0x00,0xDD,0x0F,0x04,0x14,0x34,0x28,0x96,0x06,0xC0,0x24,0x84,0x01,0x3E,0x07, +0xEE,0xD8,0x2E,0x07,0xEE,0xDC,0xD5,0x07,0x96,0x06,0xC0,0x0B,0x2E,0x07,0xEE,0xDC, +0x5A,0x00,0xFF,0x19,0x8C,0x01,0x3E,0x07,0xEE,0xDC,0xD5,0x14,0x96,0x06,0xC8,0x0B, +0x84,0x00,0x3E,0x07,0xEE,0xD8,0xD5,0x0E,0x84,0x03,0x3E,0x07,0xEE,0xD8,0x2E,0x07, +0xEE,0xDD,0xD5,0x05,0x2E,0x07,0xEE,0xDD,0x5A,0x00,0xFF,0x05,0x8C,0x01,0x3E,0x07, +0xEE,0xDD,0x2E,0x07,0xEE,0xD8,0x5A,0x08,0x01,0x06,0x84,0x00,0x3E,0x07,0xEE,0xDD, +0xD5,0x06,0x84,0x20,0x3E,0x17,0xEE,0xDC,0x5A,0x08,0x03,0xF9,0x49,0xFF,0xE5,0xF6, +0x49,0xFF,0xE5,0x24,0x2E,0x07,0xEE,0xD8,0x5A,0x00,0x01,0x19,0xC0,0x07,0x5A,0x08, +0x03,0x1B,0x2E,0x07,0xEE,0xDD,0xE6,0x04,0xD5,0x15,0x49,0x00,0x30,0x91,0x5A,0x00, +0x01,0x06,0x49,0x00,0x30,0x1E,0x5A,0x08,0x01,0x05,0x84,0x00,0x84,0x21,0xD5,0x03, +0x84,0x00,0x80,0x20,0x49,0x00,0x25,0x2B,0xD5,0x06,0x2E,0x07,0xEE,0xDC,0x5C,0xF0, +0x00,0xC9,0xE8,0xF4,0x49,0x00,0x3F,0xC6,0x5A,0x08,0x01,0x04,0x84,0x00,0xD5,0x17, +0x2E,0x07,0xEE,0xCF,0xE6,0x05,0x4E,0xF2,0x00,0x7F,0x44,0xF0,0x53,0x88,0x38,0x07, +0x80,0x00,0x40,0xF0,0x3C,0x00,0xDD,0x0F,0x06,0x1A,0x52,0x6E,0xCC,0x00,0x3C,0x03, +0xF6,0x93,0x96,0x06,0xC8,0x70,0x3C,0x0B,0xF7,0x69,0x84,0x01,0x3E,0x07,0xEE,0xCF, +0xD5,0x6A,0x3C,0x03,0xF6,0x93,0x96,0x06,0xC8,0xE2,0x3C,0x03,0xF7,0x69,0x44,0x10, +0xFF,0xFB,0xE0,0x20,0xE9,0x04,0x8C,0x01,0x96,0x01,0xD5,0x03,0x44,0x00,0xFF,0xFD, +0x5C,0xF0,0x00,0xC9,0x3C,0x0B,0xF7,0x69,0xE9,0x56,0x2E,0x07,0xF9,0x14,0xC0,0x53, +0x84,0x00,0x3E,0x07,0xEE,0xCE,0x84,0x02,0xD5,0xE2,0x3C,0x03,0xF6,0x93,0x96,0x06, +0xC8,0xC6,0x2E,0x07,0xEE,0xCE,0x8C,0x01,0x96,0x00,0xE6,0x03,0x3E,0x07,0xEE,0xCE, +0x84,0x03,0xE8,0xD5,0xD5,0x40,0x44,0x02,0x20,0xE8,0x49,0x00,0x25,0x65,0x49,0x00, +0x3E,0x20,0x84,0x00,0x49,0x00,0x40,0xB7,0x84,0x00,0x49,0x00,0x2A,0x8A,0x84,0x00, +0x49,0x00,0x3D,0xFB,0x84,0x00,0x49,0x00,0x3D,0x43,0x49,0x00,0x3F,0x75,0xF0,0x84, +0x49,0x00,0x3F,0x75,0xF0,0x85,0xF1,0x04,0x84,0x00,0xF2,0x05,0x49,0x00,0x3F,0xEB, +0x46,0x00,0x42,0x10,0x50,0x00,0x08,0x20,0x80,0x20,0x84,0x40,0x49,0xFF,0xDC,0x4D, +0x84,0x01,0x49,0x00,0x40,0x98,0x84,0x04,0x3E,0x07,0xEE,0xCF,0x84,0x00,0x3E,0x07, +0xEE,0xCE,0xD5,0x11,0x3C,0x13,0xF6,0x93,0x96,0x46,0xC9,0x89,0x2E,0x07,0xEE,0xCE, +0x8C,0x01,0x96,0x00,0xE6,0x03,0x3E,0x07,0xEE,0xCE,0xE9,0x05,0x3E,0x17,0xEE,0xCF, +0x49,0xFF,0xFD,0x08,0xFC,0xC3,0x92,0x00,0x84,0xA0,0x80,0x25,0x44,0x22,0x40,0x04, +0x44,0x00,0x08,0xC4,0x98,0xEA,0x00,0x31,0x80,0xB5,0xE6,0x62,0xE9,0x03,0x8C,0x21, +0x96,0x48,0x50,0x52,0x80,0xCC,0xD8,0xF7,0xC1,0x09,0x84,0x01,0x3E,0x07,0xEE,0xCD, +0x84,0x00,0x3E,0x07,0xEE,0xCC,0x84,0x01,0xDD,0x9E,0x2E,0x07,0xEE,0xCD,0x5A,0x08, +0x01,0x10,0x2E,0x27,0xEE,0xCC,0x8C,0x41,0x96,0x90,0xE6,0x43,0xE8,0x04,0x3E,0x27, +0xEE,0xCC,0xDD,0x9E,0x3E,0x17,0xEE,0xCC,0x3E,0x17,0xEE,0xCD,0xDD,0x9E,0x2E,0x07, +0xF1,0x6C,0x3E,0x17,0xEE,0xCD,0x5A,0x08,0x01,0x03,0xDD,0x9E,0x80,0x01,0xDD,0x9E, +0x4E,0x05,0x00,0x22,0xE4,0x02,0xE9,0x20,0x40,0x50,0x08,0x0A,0x84,0x22,0xCD,0x05, +0x8E,0x22,0x96,0x4A,0x84,0x40,0xD5,0x07,0xD0,0xFC,0x8C,0x22,0x96,0x4A,0x40,0x50, +0x04,0x0E,0xD5,0xF6,0x4E,0x15,0x00,0x0E,0x94,0x91,0x9C,0xD1,0x40,0x50,0x04,0x0E, +0x42,0xF1,0x8C,0x24,0xE0,0xAF,0x8E,0x22,0x40,0x21,0xBC,0x1A,0x96,0x4A,0xD5,0xF3, +0x80,0x02,0xDD,0x9E,0x84,0x00,0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xFF,0xDB,0xFC,0x80, +0x9A,0xCB,0x8A,0x02,0xFE,0xDC,0x42,0x30,0x00,0x73,0x44,0x00,0x00,0x64,0x40,0x01, +0x80,0x16,0xDD,0x9E,0xFE,0x5C,0x42,0x10,0x08,0x73,0x80,0x01,0xDD,0x9E,0xB4,0x20, +0x84,0x61,0x4E,0x14,0x00,0x03,0x84,0x7F,0x4E,0x14,0x00,0x03,0xFE,0x4A,0x84,0x40, +0x40,0x40,0x88,0x0E,0xC4,0x04,0x8C,0x41,0x96,0x90,0xD5,0xFB,0x52,0x21,0x00,0x1F, +0x96,0x90,0x40,0x10,0x88,0x0C,0xFE,0x5C,0xB6,0x20,0x80,0x02,0xDD,0x9E,0x84,0x60, +0x80,0x43,0xE2,0x61,0xE8,0x07,0x38,0x40,0x0D,0x11,0x8C,0x61,0x88,0x44,0x96,0xD8, +0xD5,0xF9,0x4E,0x26,0x00,0x03,0x84,0x41,0x40,0x21,0x04,0x56,0x84,0x80,0x96,0x93, +0x80,0x64,0xE2,0x81,0xE8,0x09,0x38,0x50,0x11,0x11,0x8C,0x81,0x8A,0xA2,0x42,0x32, +0x94,0x73,0x97,0x20,0xD5,0xF7,0xFC,0x00,0x80,0x03,0x4E,0x36,0x00,0x03,0x84,0x01, +0x40,0x00,0x04,0x16,0x49,0xFF,0xFF,0xB2,0x96,0x03,0xFC,0x80,0x84,0x00,0x84,0x20, +0x3E,0x07,0xF0,0xD0,0x3C,0x1F,0xFC,0x33,0x3E,0x17,0xEE,0xD9,0x3E,0x07,0xF0,0xCB, +0xDD,0x9E,0xFC,0x00,0x5A,0x08,0x04,0x07,0x44,0x02,0x39,0xE0,0x44,0x12,0x10,0x58, +0xD5,0x05,0x44,0x02,0x39,0xE0,0x44,0x12,0x10,0x50,0x84,0x48,0x49,0x00,0x4D,0x2A, +0xFC,0x80,0x00,0x00,0xFC,0x00,0x2E,0x17,0xF0,0xC9,0xE6,0x24,0xE8,0x43,0x44,0xF0, +0x56,0x1C,0x38,0x17,0x84,0x00,0x40,0xF0,0xBC,0x00,0xDD,0x0F,0x04,0x3A,0x50,0x70, +0x3C,0xF7,0xF8,0x5F,0x5E,0xF7,0x80,0x46,0xE8,0x35,0x5C,0xF0,0x00,0x97,0xE9,0x03, +0x84,0x01,0xD5,0x2E,0xC0,0x2F,0x3C,0x0D,0xFB,0x4A,0xFA,0x22,0x40,0x00,0x04,0x16, +0x44,0x10,0x00,0x64,0xFE,0x0C,0xFA,0x30,0x40,0x00,0x04,0x16,0x96,0x03,0x5E,0xF0, +0x01,0xF5,0xE8,0xEF,0xD5,0x1F,0x3C,0x17,0xF8,0x5F,0x3C,0xF4,0x0A,0xD3,0xE0,0x2F, +0xE8,0x19,0x84,0x02,0x3E,0x07,0xF0,0xC9,0x84,0x00,0xD5,0x0C,0x3C,0x17,0xF8,0x5F, +0x3C,0xF4,0x0A,0xD3,0xE0,0x2F,0xE8,0xDD,0x2E,0x07,0xF0,0xC8,0xE6,0x05,0xE8,0x05, +0x8C,0x01,0x3E,0x07,0xF0,0xC8,0xD5,0x06,0x84,0x03,0xD5,0x02,0x84,0x00,0x3E,0x07, +0xF0,0xC9,0x2E,0x07,0xF0,0xC9,0x84,0x20,0x40,0x00,0x80,0x06,0xFC,0x80,0x92,0x00, +0xFC,0x01,0x80,0xC1,0x84,0x20,0x3E,0x17,0xF0,0xCA,0x44,0x10,0x00,0xFF,0x3C,0x1B, +0xF6,0x6A,0x2E,0x10,0x15,0x7C,0x85,0xE3,0x40,0x10,0xBC,0x37,0xE2,0x26,0xF0,0x81, +0xE8,0x31,0x3C,0x13,0xF8,0x69,0xE6,0x2B,0xE8,0x03,0x84,0x00,0xD5,0x27,0x5C,0xF0, +0x80,0x29,0xE9,0x06,0xFA,0x2E,0x49,0xFF,0xFF,0x54,0x3C,0x0B,0xF6,0x6A,0x3C,0x03, +0xF8,0x69,0x2E,0x10,0x15,0x7C,0xE2,0x01,0xE9,0xF1,0x92,0x01,0x5C,0xF0,0x00,0x33, +0x80,0x20,0xE9,0x03,0x44,0x10,0x00,0x32,0xF0,0x01,0x54,0x10,0xFF,0xFF,0x49,0xFF, +0xFF,0x40,0x5E,0xF0,0x00,0x5F,0xE9,0x07,0x3C,0xF7,0xF8,0x62,0x84,0x21,0x5E,0xF7, +0x80,0x5F,0xE8,0x02,0x84,0x22,0x3E,0x17,0xF0,0xCA,0x3C,0x0B,0xF8,0x62,0x84,0x01, +0xD5,0x02,0x84,0x00,0x3C,0x1D,0xFC,0x33,0xC9,0x0A,0x5A,0x08,0x01,0x13,0x3C,0x13, +0xF8,0x69,0x2E,0x20,0x15,0x7C,0xE2,0x41,0xE8,0x0C,0xD5,0x09,0x5A,0x00,0x01,0x0A, +0x3C,0xF7,0xF8,0x5F,0x5E,0xF7,0x80,0x96,0xE8,0x04,0x84,0x00,0x3C,0x0F,0xFC,0x33, +0x2E,0x17,0xEE,0xD9,0x3C,0x0D,0xFC,0x33,0xE6,0x24,0xE8,0x40,0x44,0xF0,0x57,0x6C, +0x38,0x17,0x84,0x00,0x40,0xF0,0xBC,0x00,0x4A,0x00,0x3C,0x00,0x04,0x28,0x42,0x68, +0x5A,0x08,0x01,0x0E,0x2E,0x27,0xF0,0xC6,0x84,0x20,0x3E,0x17,0xF0,0xC6,0x5A,0x20, +0xFF,0x2E,0x3E,0x07,0xEE,0xD9,0x3E,0x07,0xF0,0xCB,0xD5,0x28,0x84,0x00,0x3E,0x07, +0xF0,0xC6,0xD5,0x24,0x2E,0x17,0xF0,0xCB,0x2E,0x20,0x15,0x80,0xE2,0x22,0xE8,0x05, +0x8C,0x21,0x3E,0x17,0xF0,0xCB,0xD5,0x1A,0x84,0x22,0xC0,0x0E,0xD5,0x17,0xC8,0x11, +0x2E,0x27,0xF0,0xD0,0x2E,0x10,0x15,0x82,0x8C,0x41,0x96,0x90,0xE2,0x22,0x3E,0x27, +0xF0,0xD0,0xE8,0x0C,0x84,0x23,0x3E,0x17,0xEE,0xD9,0x3E,0x07,0xF0,0xD0,0xD5,0x06, +0x84,0x01,0xD5,0x02,0x84,0x00,0x3E,0x07,0xEE,0xD9,0x2E,0x17,0xF0,0xCA,0x2E,0x07, +0xF0,0xC7,0x5A,0x18,0x01,0x04,0x8C,0x04,0xD5,0x0D,0x5A,0x18,0x02,0x09,0x3C,0x13, +0xF8,0x69,0x5C,0xF0,0x80,0x38,0xE9,0x03,0x8E,0x01,0xD5,0x04,0xE6,0xC4,0xE9,0xFD, +0x8C,0x01,0x3E,0x07,0xF0,0xC7,0x2E,0x07,0xF0,0xC7,0x80,0x20,0x96,0x02,0x5E,0xF0, +0x00,0x3D,0xE9,0x04,0x44,0x10,0x00,0x3C,0xD5,0x05,0x96,0x0A,0x4E,0x04,0x00,0x03, +0x84,0x20,0x96,0x8A,0x5E,0xF1,0x00,0x29,0x3E,0x17,0xF0,0xC7,0xE9,0x05,0x49,0xFF, +0xFE,0xCF,0x84,0x00,0xD5,0x09,0x2E,0x07,0xEE,0xD9,0xC0,0x06,0xC2,0x04,0x8E,0x21, +0x3E,0x17,0xF0,0xC7,0x84,0x01,0xFC,0x81,0xA7,0x08,0xA6,0xC0,0xE2,0x64,0x40,0x32, +0x3C,0x1A,0xAE,0xD0,0xA7,0x01,0xA6,0xC9,0xE2,0x83,0x40,0x32,0x3C,0x1A,0xAE,0xD1, +0xA7,0x0A,0xA6,0xC2,0xA6,0x4B,0xA6,0x03,0xE2,0x64,0x40,0x32,0x3C,0x1A,0xE2,0x01, +0x40,0x10,0x3C,0x1A,0xAE,0xD2,0xAE,0x53,0xDD,0x9E,0xFC,0x48,0x50,0xAF,0x80,0x14, +0x80,0xE1,0x81,0x00,0x84,0x20,0x81,0x22,0x80,0x0A,0xFA,0x5C,0x83,0x85,0xF3,0x83, +0x80,0xC4,0x49,0x00,0x4C,0x05,0xA6,0x7B,0x2F,0xE6,0xCC,0x7E,0xA6,0x3A,0x80,0x41, +0x42,0x20,0x78,0x73,0x38,0x54,0x09,0x01,0x8A,0xBC,0x97,0x6B,0x4E,0x52,0x00,0x8F, +0x44,0x22,0x3D,0x20,0x80,0x89,0x38,0x11,0x05,0x01,0x44,0x30,0x00,0xFF,0xF1,0x82, +0x44,0x12,0x3F,0x84,0x45,0xC0,0xFF,0xFF,0x38,0x80,0x81,0x01,0x84,0x00,0xF0,0x81, +0x80,0x0A,0x40,0x22,0x24,0x01,0xF1,0x03,0x96,0x90,0xE2,0x41,0xE8,0x2D,0x08,0x32, +0x00,0x01,0x81,0x46,0xFA,0x38,0x42,0xA1,0x84,0x73,0x00,0xF5,0x00,0x20,0x5A,0xF8, +0x01,0xF2,0x38,0x20,0x0E,0x02,0xCA,0xEE,0x04,0x25,0x00,0x01,0x04,0xA5,0x00,0x02, +0xF1,0x02,0x40,0xA4,0x28,0x01,0x9A,0x8A,0x42,0xA5,0x28,0x24,0x42,0xA1,0x08,0x73, +0x40,0x25,0x0C,0x09,0xC2,0x08,0x40,0xFE,0x08,0x06,0x81,0x42,0xE8,0x05,0x44,0xA0, +0xFF,0xFF,0xD5,0x02,0x81,0x4F,0xF1,0x01,0x40,0xAE,0x29,0x57,0x88,0x2A,0x38,0xA0, +0x0E,0x0A,0xF1,0x81,0xD5,0xCF,0x84,0x40,0x9C,0x34,0x80,0x82,0xB0,0x45,0x54,0x91, +0x00,0xFF,0x38,0xF0,0x8A,0x02,0xE8,0x28,0xF1,0x01,0x42,0x37,0x94,0x24,0x04,0xA0, +0x00,0x02,0x40,0xF1,0x85,0xF7,0xB4,0x60,0xF1,0x02,0x42,0x35,0x0C,0x24,0x41,0xC5, +0x3C,0x00,0x42,0x37,0x84,0x73,0x40,0x31,0xF0,0x77,0xB6,0x60,0xA0,0xC1,0xB0,0x45, +0x42,0x35,0x0C,0x24,0x42,0x37,0xA0,0x73,0x40,0x31,0xF0,0x77,0xA8,0xC1,0x38,0x30, +0x8A,0x02,0xF1,0x01,0xFE,0xEC,0x40,0x31,0x84,0x77,0x89,0x43,0x8C,0x81,0x97,0x20, +0x14,0xA0,0x00,0x02,0x80,0x69,0x8C,0x41,0x50,0x00,0x00,0x28,0x5A,0x28,0x0B,0xD0, +0x5A,0x48,0x01,0x15,0xFA,0x18,0x42,0x61,0x80,0x73,0x00,0x03,0x00,0x1E,0xF2,0x18, +0x8C,0x01,0x10,0x03,0x00,0x1E,0xA6,0x3B,0xA6,0x7A,0x88,0x02,0x42,0x00,0xF8,0x73, +0x00,0x13,0x00,0x19,0x58,0x10,0x80,0x20,0xAE,0x40,0xFC,0xC8,0xFC,0x40,0x51,0xFF, +0xFD,0x48,0xB1,0xD2,0xF4,0x89,0x3A,0x03,0x8C,0x20,0x02,0x0F,0x80,0x28,0x44,0x10, +0xF1,0x4C,0xF0,0x84,0xB0,0x1D,0x83,0x85,0x3A,0x20,0x94,0x00,0x3A,0x20,0x14,0x20, +0x44,0x10,0x00,0xFF,0x84,0x4B,0xB0,0x1A,0x02,0x6F,0x80,0x29,0x49,0x00,0x4B,0x50, +0xB0,0x36,0x84,0x20,0x44,0x20,0x01,0xE0,0x49,0x00,0x4B,0x4A,0x00,0x03,0x80,0x0C, +0xF0,0x83,0xF0,0x04,0xE2,0xC0,0x40,0x60,0x3C,0x1B,0x3C,0x03,0xF6,0x93,0xF6,0x87, +0x96,0x36,0xC0,0x0B,0xF1,0x03,0x00,0x0F,0x80,0x0C,0x5C,0xF0,0x80,0x55,0xE8,0x03, +0x44,0x00,0x00,0x55,0x96,0x00,0xF0,0x83,0x04,0x2F,0x80,0xB6,0x2E,0x36,0xCC,0x7E, +0x51,0xEF,0x80,0x60,0x2E,0x06,0xCC,0x7F,0xF0,0x88,0xA6,0x10,0x94,0x02,0x88,0x02, +0xF0,0x8B,0x84,0x00,0xF0,0x82,0xF0,0x0B,0x4C,0x20,0x00,0xAE,0x84,0x80,0xA7,0x55, +0x00,0x91,0x00,0x04,0x80,0x04,0xB1,0xDD,0x44,0xAF,0xFF,0xDF,0x38,0x12,0x1C,0x00, +0x99,0xBC,0x88,0x25,0x96,0x48,0xE2,0x23,0x20,0x63,0x00,0x01,0xE8,0x25,0x88,0xC9, +0x04,0x8F,0x80,0x08,0x97,0xB0,0xE2,0xC8,0xE8,0x1F,0x04,0x8F,0x80,0xB7,0x88,0x28, +0x42,0x13,0x0C,0x73,0xA7,0x88,0x40,0x13,0x28,0x02,0x5A,0x10,0x1F,0x16,0x97,0xA7, +0x84,0x20,0x54,0x80,0x80,0xFF,0x4C,0x80,0x40,0x07,0x38,0x6F,0x00,0x08,0x8C,0x01, +0x96,0x00,0xD5,0x0A,0x38,0x8F,0x04,0x00,0x4C,0x83,0x00,0x07,0x8C,0x21,0x54,0xF0, +0x80,0xFF,0xE2,0x0F,0xE8,0xEF,0x8C,0x82,0x5A,0x48,0x10,0xD2,0x42,0x54,0x8C,0x73, +0x04,0x1F,0x80,0xB8,0x95,0x69,0x88,0x25,0xF1,0x8A,0x84,0xC0,0x4C,0x60,0x00,0x69, +0x9D,0xF1,0x96,0x78,0xF1,0x85,0x80,0xA1,0xD0,0x61,0x38,0x1F,0x18,0x00,0x44,0x42, +0x3C,0xB0,0x44,0x72,0x3C,0xB0,0x38,0xF2,0x04,0x00,0x38,0x4F,0x14,0x00,0x38,0x83, +0x90,0x00,0x5A,0xF0,0xFF,0x51,0x5A,0x80,0xFF,0x4F,0xF7,0x0A,0x81,0x3C,0x22,0xA3, +0x80,0x00,0x44,0x70,0x00,0x64,0x42,0xA5,0x1C,0x24,0xFA,0xF8,0x42,0x97,0x9C,0x73, +0x50,0x94,0x80,0x20,0x00,0xF4,0x80,0x07,0x00,0x94,0x80,0x08,0x04,0x7F,0x80,0xB8, +0x42,0xF4,0x8C,0x73,0x38,0x93,0xBD,0x11,0x40,0x75,0x24,0xF7,0xF7,0x86,0xFB,0x38, +0x80,0xFC,0x42,0x74,0x24,0x73,0x50,0x83,0x80,0x20,0x00,0xF4,0x00,0x07,0x00,0x84, +0x00,0x08,0x04,0x7F,0x80,0xB8,0x42,0xF4,0x0C,0x73,0x38,0x83,0xBD,0x11,0xF7,0x06, +0x40,0xA5,0x21,0x57,0xE3,0x47,0xE8,0x04,0x00,0x9F,0x80,0x18,0xD5,0x03,0x54,0x95, +0x00,0xFF,0xF7,0x02,0xE6,0xF4,0xE8,0x17,0x04,0xAF,0x80,0x02,0x85,0x03,0xB1,0xE7, +0x42,0x75,0x20,0x73,0xE2,0x24,0x81,0x04,0x40,0x80,0xBC,0x1B,0xE2,0x81,0x40,0x12, +0x3C,0x1A,0xAE,0x79,0xF1,0x02,0x10,0x83,0x80,0x00,0x8C,0x21,0x96,0x48,0x10,0x93, +0x80,0x02,0xF1,0x82,0x8C,0xA1,0x97,0x68,0xD5,0xA0,0xF6,0x05,0xD5,0x98,0x8C,0x44, +0x48,0xFF,0xFF,0x53,0x3C,0x03,0xF6,0x69,0xE6,0x1A,0x84,0x00,0xE9,0x16,0x80,0x3C, +0x84,0x00,0x08,0x20,0x80,0x2E,0x2E,0x37,0xEC,0x9D,0x80,0xA0,0xD2,0x0B,0xA7,0x08, +0x4C,0x41,0xC0,0x04,0x8C,0x01,0x96,0x00,0x8C,0xA1,0x97,0x68,0x50,0x10,0x80,0x28, +0xD5,0xF6,0x84,0x21,0x40,0x00,0x80,0x06,0xF0,0x85,0x46,0x00,0x08,0x95,0x84,0xC0, +0x50,0x00,0x04,0x3F,0x81,0x26,0xF0,0x8B,0x00,0x0E,0x00,0x00,0xE3,0x20,0x4E,0xF2, +0x01,0x0F,0xB0,0x1A,0xFB,0x58,0x38,0x90,0x24,0x08,0x50,0x04,0x80,0x01,0x96,0x00, +0xF0,0x88,0x80,0xE0,0x42,0x04,0xA8,0x24,0xF0,0x8A,0x88,0x1C,0xF0,0x86,0x00,0x0E, +0x00,0x00,0xE2,0xE0,0x4E,0xF2,0x00,0xF8,0xF0,0x06,0x00,0x10,0x00,0x2B,0x5A,0x10, +0xFF,0x27,0x80,0x1C,0x42,0x03,0xA8,0x73,0x00,0x30,0x00,0x2B,0x44,0x02,0x40,0x04, +0x5A,0x30,0xFF,0x14,0x44,0x20,0x00,0xCC,0x80,0x80,0x42,0x40,0x88,0x73,0x00,0x42, +0x00,0xB5,0x5A,0x48,0x02,0x0B,0x80,0x80,0x42,0x41,0x88,0x73,0x00,0x22,0x00,0xB5, +0x5A,0x28,0x02,0x04,0x48,0x00,0x00,0xD4,0x44,0x20,0x00,0xCC,0x42,0x00,0x88,0x73, +0x00,0x00,0x00,0xB4,0x5A,0x08,0x01,0x04,0x48,0x00,0x00,0xCA,0x80,0x1C,0x42,0x03, +0xA8,0x73,0x00,0x00,0x00,0x2B,0x5A,0x00,0xFF,0x0E,0x44,0x30,0x00,0xCC,0x44,0x22, +0x40,0x04,0x42,0x20,0x0C,0x73,0x00,0x21,0x00,0xB4,0x5A,0x28,0x01,0x04,0x48,0x00, +0x00,0xB7,0xE6,0xD4,0x4E,0xF2,0x00,0xB4,0xF2,0x06,0x80,0x7C,0x42,0x33,0xA8,0x73, +0xA0,0x94,0xA0,0xDC,0xFA,0x98,0x9A,0xD3,0x4E,0x34,0x00,0x03,0xFE,0xDA,0x80,0x5C, +0x80,0xBC,0x42,0x24,0x90,0x73,0x42,0x53,0x90,0x73,0xA0,0x95,0xA1,0x2D,0x8A,0x44, +0x4E,0x24,0x00,0x03,0xFE,0x92,0xFE,0x94,0x42,0x21,0x8C,0x73,0xF3,0x0B,0xE0,0x62, +0xE8,0x17,0xF3,0x05,0x5A,0x30,0x01,0x04,0x48,0x00,0x00,0x92,0x50,0x34,0x80,0x01, +0x80,0x9C,0x42,0x41,0xA8,0x73,0x2E,0x57,0xEC,0x9D,0xA6,0xE6,0x4C,0x51,0xC0,0x88, +0x9C,0xF9,0x80,0x9C,0x42,0x41,0xA8,0x73,0xA6,0xE6,0x4C,0x32,0xC0,0x81,0xF5,0x0A, +0xFA,0x68,0xB1,0x36,0x42,0x43,0x0C,0x73,0x88,0xBC,0x8C,0xAC,0xB6,0xA4,0x80,0xBC, +0x42,0x53,0xA8,0x73,0x8C,0xAC,0x50,0x32,0x00,0x10,0xA9,0x61,0xA8,0xA2,0x10,0x92, +0x00,0x10,0xF4,0x06,0xAF,0xD9,0xB0,0xA7,0x84,0xA0,0x51,0xE2,0x00,0x20,0xF4,0x02, +0xD4,0x19,0x20,0x8F,0x00,0x05,0xA7,0x10,0x4C,0x82,0x40,0x11,0x80,0x9C,0x42,0x43, +0xA8,0x73,0x20,0x82,0x00,0x25,0xA7,0x11,0x4C,0x82,0x40,0x09,0xA7,0x1B,0x00,0x81, +0x00,0x02,0xE3,0x04,0x40,0x44,0x3C,0x1A,0xAF,0x1B,0x8C,0xA1,0x97,0x68,0x8C,0x43, +0xD5,0xE7,0xFA,0x48,0x5A,0x10,0xFF,0x1F,0x5A,0x00,0xFF,0x19,0x44,0x32,0x40,0x04, +0x44,0x50,0x00,0xCC,0x80,0x83,0x42,0x40,0x94,0x73,0x42,0x30,0x14,0x73,0x00,0x82, +0x00,0xB5,0x00,0x51,0x80,0xB5,0xE2,0xA8,0xE9,0x09,0xE3,0x05,0xE9,0x0D,0x02,0x42, +0x00,0x58,0x02,0x31,0x80,0x58,0xE2,0x83,0xE9,0x07,0xB0,0x36,0x42,0x03,0x08,0x73, +0xD5,0x0D,0x5A,0x00,0xFF,0x08,0xB0,0x76,0x42,0x13,0x08,0x73,0x10,0x00,0x80,0x12, +0xD5,0x07,0xB0,0x36,0x42,0x03,0x08,0x73,0x84,0x3F,0x10,0x10,0x00,0x12,0xFA,0x08, +0xB0,0x76,0x42,0x13,0x00,0x73,0x50,0x30,0x80,0x10,0xA6,0x1A,0x81,0x01,0xF3,0x8C, +0x5A,0x00,0xFF,0x14,0x44,0x10,0x00,0xCC,0xF2,0x09,0x42,0x20,0x04,0x73,0x80,0x02, +0xB0,0x57,0x84,0x40,0x49,0xFF,0xE1,0x81,0xF0,0x17,0xF3,0x0C,0xB4,0x20,0x14,0x14, +0x00,0x03,0x00,0x00,0x00,0x14,0xAE,0x1C,0x8C,0xC1,0x97,0xB0,0x8C,0xE1,0x97,0xF8, +0x48,0xFF,0xFF,0x07,0x04,0x9F,0x80,0x08,0x48,0xFF,0xFE,0xF0,0xB0,0x36,0xF0,0x82, +0x85,0x20,0x4C,0x93,0x00,0x37,0x50,0x14,0x80,0x01,0x54,0x90,0x80,0xFF,0x81,0x49, +0x4C,0xA3,0x00,0x2E,0xFA,0x48,0xB0,0x76,0x42,0x15,0x08,0x73,0xA0,0xC2,0xA0,0x8A, +0xE0,0x43,0xE8,0x20,0xB1,0xE1,0x83,0xC0,0x81,0xE7,0x3A,0x2F,0x14,0x04,0x3A,0x27, +0x94,0x24,0x3A,0x2F,0x0C,0x00,0x81,0x01,0x3A,0x27,0x8C,0x20,0x80,0xE0,0x3A,0x24, +0x14,0x04,0x3A,0x23,0x94,0x24,0x3A,0x24,0x0C,0x00,0x3A,0x2F,0x0C,0x20,0xB0,0xA1, +0x80,0xE2,0x3A,0x23,0x94,0x04,0x3A,0x20,0x94,0x24,0x3A,0x27,0x8C,0x00,0x3A,0x24, +0x0C,0x20,0x50,0x15,0x00,0x01,0x54,0xA0,0x80,0xFF,0xD5,0xD3,0x8C,0x18,0xD5,0xCA, +0xF0,0x04,0x44,0x82,0x40,0x04,0xFE,0x04,0xF0,0x84,0xF0,0x07,0xF1,0x04,0xFE,0x04, +0xF0,0x86,0x44,0x00,0x00,0x64,0xFE,0x0C,0xF0,0x8D,0xB0,0x76,0xFA,0x08,0x42,0x14, +0x80,0x73,0xF1,0x8C,0xF5,0x02,0xF1,0x0C,0xD1,0x45,0xF0,0x02,0xB0,0x5A,0x00,0x00, +0x00,0x11,0x38,0x50,0x80,0x00,0xF0,0x02,0x00,0x00,0x00,0x10,0x38,0x20,0x80,0x00, +0xD2,0x35,0xF0,0x02,0x00,0x00,0x00,0x12,0x5A,0x00,0xFF,0x25,0x44,0x10,0x00,0xCC, +0xF3,0x09,0x42,0x30,0x04,0x73,0xF0,0x02,0x50,0x11,0x80,0x94,0x00,0x31,0x80,0x94, +0xB4,0x00,0xA7,0x01,0xE2,0x83,0xE9,0x16,0xA7,0x09,0xA7,0x80,0xE2,0x86,0xE9,0x12, +0xA7,0x8A,0xA7,0xC3,0xE2,0xE6,0xE9,0x0E,0xA7,0xCB,0xA6,0x42,0xE2,0xE1,0xE9,0x0A, +0xF1,0x02,0xA0,0x49,0x00,0x90,0x80,0x01,0xE3,0x23,0xE9,0x04,0xA6,0xC8,0xE2,0x83, +0xE8,0x6B,0xF0,0x05,0x5A,0x00,0x01,0x04,0x48,0x00,0x01,0xD9,0xF0,0x02,0x00,0x00, +0x00,0x13,0x5C,0xF0,0x00,0x47,0x4E,0xF2,0x00,0xB6,0xF0,0x02,0x8C,0x18,0xF0,0x82, +0xD5,0xBA,0x84,0xC0,0x44,0x72,0x3C,0xB0,0x04,0x0F,0x80,0xB6,0xA6,0x00,0xE2,0xC0, +0x4E,0xF2,0x01,0xC9,0x04,0x0F,0x80,0xB6,0x94,0x72,0x88,0x01,0xB1,0x5D,0xA6,0x85, +0xA6,0x04,0xF2,0x82,0xF0,0x83,0x84,0x40,0x2E,0x96,0xCC,0x7E,0x80,0x62,0x2E,0x06, +0xCC,0x7F,0x45,0xEF,0xFF,0xDF,0xF0,0x84,0x50,0x8F,0x80,0x60,0x50,0xAF,0x80,0x68, +0xF4,0x02,0x38,0x01,0x14,0x00,0x88,0x04,0x96,0x00,0x99,0x2A,0xE2,0x09,0x20,0x42, +0x00,0x01,0xE8,0x1E,0x04,0xFF,0x80,0x03,0x88,0x8F,0x04,0xFF,0x80,0x04,0x97,0x20, +0xE2,0x8F,0xE8,0x16,0x04,0xFF,0x80,0xB7,0x88,0x0F,0x42,0x02,0x24,0x73,0xA6,0x00, +0x40,0x40,0x78,0x02,0x5A,0x40,0x1F,0x0D,0x96,0x27,0x38,0x03,0x80,0x00,0x5A,0x00, +0xFF,0x08,0x38,0x05,0x00,0x00,0x38,0x04,0x0C,0x08,0x8C,0x61,0x96,0xD8,0x8C,0x42, +0x5A,0x28,0x10,0xD8,0x50,0x5F,0x82,0xD8,0xB4,0x05,0xB0,0x98,0x88,0x20,0xA0,0x29, +0x8C,0x22,0xB6,0x1F,0x50,0x4E,0x00,0x0C,0xA0,0x2A,0x84,0xAA,0x8C,0xC1,0x49,0xFF, +0xFC,0x46,0x97,0xB0,0xD5,0xAA,0xA6,0xCB,0xE2,0x66,0xE9,0x94,0xA6,0xCA,0xE2,0xE3, +0xE9,0x91,0x00,0x40,0x00,0x22,0x00,0x30,0x80,0x22,0x4C,0x41,0xFF,0x8C,0xA1,0x03, +0xA0,0xCB,0x44,0x70,0x00,0x7D,0x88,0x64,0xF4,0x02,0xA1,0xA3,0x44,0x40,0x00,0x64, +0xFF,0xF4,0x40,0xF3,0x91,0xF6,0xE0,0x6F,0xE8,0x08,0x44,0x70,0x00,0x4B,0xFF,0xBC, +0x40,0x43,0x10,0x96,0xE0,0x83,0xE9,0x13,0x00,0x30,0x00,0x1E,0x00,0x00,0x80,0x1E, +0x44,0xF0,0x00,0x64,0x98,0x58,0xF0,0x02,0x00,0x30,0x00,0x14,0x44,0x00,0x00,0x96, +0xFE,0x1C,0x40,0x00,0x3C,0x16,0xE0,0x01,0x4E,0xF3,0xFF,0x65,0xF3,0x06,0x84,0x21, +0x3C,0x03,0xF6,0x93,0xF4,0x02,0x96,0x36,0xA1,0x22,0xC0,0x06,0xF0,0x0D,0xE0,0x80, +0x4E,0xF2,0xFF,0x65,0xD5,0x0F,0x44,0x00,0x00,0x64,0xFE,0x1C,0xE0,0x80,0x5A,0x18, +0x01,0x09,0xE8,0x08,0xF0,0x02,0x00,0x1F,0x80,0x55,0x00,0x00,0x00,0x13,0xE2,0x20, +0xE9,0x13,0xF0,0x02,0xF1,0x03,0x00,0x00,0x00,0x13,0xE2,0x20,0xE9,0x0D,0x48,0xFF, +0xFF,0x4E,0xFA,0x38,0x80,0x1C,0x42,0x01,0x04,0x73,0x00,0x30,0x00,0x2B,0x5A,0x30, +0xFF,0x04,0x48,0x00,0x00,0xF9,0xE2,0xA2,0x80,0xC2,0x40,0x62,0xBC,0x1B,0xE2,0x45, +0x40,0x22,0xBC,0x1B,0xFA,0x18,0x80,0x3C,0x42,0x11,0x00,0x73,0x50,0x10,0x80,0x20, +0x00,0x30,0x80,0x0B,0x81,0x42,0x5A,0x38,0xFF,0x04,0x48,0x00,0x00,0xE1,0x80,0x5C, +0x42,0x23,0x00,0x73,0x50,0x21,0x00,0x20,0x00,0x51,0x00,0x0B,0x80,0x86,0x5A,0x50, +0xFF,0x32,0x44,0x00,0x00,0xCC,0x80,0xE8,0x42,0x71,0x80,0x73,0x50,0x33,0x80,0xB0, +0x00,0x91,0x80,0x05,0xFE,0x2C,0x88,0x08,0x5A,0x98,0x01,0x19,0x50,0x50,0x00,0xB0, +0x00,0x92,0x80,0x05,0x5A,0x98,0x01,0x0B,0x02,0x00,0x00,0x58,0x02,0x73,0x80,0x58, +0xE2,0xE0,0x84,0x02,0xE9,0x04,0xAE,0x2E,0xD5,0x12,0x84,0x02,0xAE,0x1E,0x84,0x1F, +0x80,0xCA,0x10,0x00,0x80,0x0B,0x81,0x44,0xD5,0x0D,0x50,0x00,0x00,0xB0,0xA6,0x45, +0x5A,0x10,0x01,0x04,0x48,0xFF,0xFE,0xFB,0x84,0x22,0xAE,0x46,0x84,0x1F,0x10,0x01, +0x00,0x0B,0xFA,0x38,0x9C,0x31,0x80,0x5C,0x42,0x20,0x04,0x73,0x80,0x02,0x84,0x40, +0xAE,0x84,0x42,0x03,0x04,0x24,0x40,0x4E,0x00,0x00,0x50,0x92,0x00,0x20,0xF0,0x87, +0x00,0x04,0x80,0x07,0xF0,0x8E,0x00,0x04,0x80,0x08,0xF0,0x8F,0x42,0x05,0x04,0x24, +0xF0,0x88,0x88,0x1C,0x50,0x70,0x00,0x20,0xA6,0x7F,0x50,0x20,0x00,0x08,0xF1,0x90, +0x00,0x13,0x80,0x08,0x04,0xF0,0x00,0x06,0xF1,0x91,0x50,0x12,0x00,0x10,0xA0,0xD2, +0x05,0xE0,0x80,0x02,0xF2,0x8B,0xA0,0xA4,0x42,0x37,0x8C,0x24,0x42,0x3F,0x08,0x73, +0xF2,0x0B,0xF1,0x8A,0x40,0x1F,0x3C,0x00,0x40,0x31,0x84,0x76,0xA8,0xD2,0xF2,0x0A, +0x50,0x50,0x00,0x10,0xA0,0xD1,0xA0,0xA9,0x42,0x27,0x88,0x24,0x42,0x2F,0x0C,0x73, +0x40,0x21,0x04,0x56,0xA8,0xA9,0x00,0x33,0x80,0x0A,0x00,0x24,0x80,0x0A,0xA8,0x46, +0x88,0x43,0x10,0x23,0x80,0x0A,0x00,0x33,0x80,0x09,0x00,0x24,0x80,0x09,0x88,0x43, +0x10,0x23,0x80,0x09,0x02,0x32,0x00,0x11,0x02,0x20,0x00,0x11,0x97,0x5B,0x97,0x13, +0xE0,0xA4,0x40,0x21,0xBC,0x1A,0x12,0x20,0x00,0x11,0xF0,0x08,0x40,0x1E,0x00,0x00, +0xF0,0x07,0x8C,0x2C,0x88,0x1C,0x8C,0x0C,0x80,0x41,0x49,0xFF,0xFB,0x17,0x00,0x04, +0x80,0x0B,0x5A,0x00,0xFF,0x05,0x10,0x03,0x80,0x0B,0xD5,0x05,0x00,0x03,0x80,0x0B, +0x10,0x04,0x80,0x0B,0xF2,0x0F,0x2E,0x06,0xCC,0x7E,0xF1,0x0E,0xF3,0x11,0x42,0x11, +0x00,0x73,0xF2,0x10,0x42,0x21,0x80,0x73,0x80,0x02,0x04,0x2F,0x80,0xB8,0x38,0xF1, +0x05,0x11,0x38,0x01,0x01,0x11,0xE0,0x0F,0xE8,0x12,0xFA,0x18,0x80,0x3C,0x42,0x13, +0x00,0x73,0x50,0x10,0x80,0x20,0x80,0x7C,0x42,0x35,0x00,0x73,0xA6,0x8F,0x50,0x01, +0x80,0x20,0x00,0x10,0x80,0x08,0xAE,0x87,0x10,0x10,0x00,0x08,0x00,0x2E,0x00,0x00, +0xB0,0x1A,0xB0,0x5A,0x9A,0x41,0x96,0x48,0xE2,0x22,0x4E,0xF2,0xFE,0x58,0x08,0x10, +0x00,0x01,0x4C,0x60,0xFF,0xF8,0x10,0xA0,0x7F,0xFF,0xD5,0xF4,0x81,0x46,0x80,0xC2, +0x48,0xFF,0xFF,0x59,0x44,0x00,0x00,0xCC,0x80,0x88,0x42,0x41,0x80,0x73,0x02,0x32, +0x00,0x59,0x4E,0x32,0xFF,0x02,0x80,0x7C,0x42,0x32,0x84,0x73,0x00,0x11,0x80,0x2B, +0x5A,0x18,0xFF,0x04,0x48,0xFF,0xFE,0xF9,0x80,0x68,0x42,0x30,0x80,0x73,0x02,0x01, +0x80,0x59,0x4E,0x02,0xFE,0xF2,0x48,0xFF,0xFE,0x32,0xF3,0x04,0x84,0x20,0x48,0xFF, +0xFE,0xC1,0x51,0xFF,0x82,0xB8,0xFC,0xC0,0xFC,0x45,0x3C,0x03,0xF6,0x91,0x87,0x81, +0x96,0x06,0xC0,0x03,0x45,0xC0,0x00,0x14,0x46,0xA0,0x02,0x25,0x50,0x75,0x05,0x10, +0x42,0x7E,0x1C,0x24,0x44,0x00,0x00,0x64,0x46,0x90,0x08,0x95,0x40,0x73,0x80,0xF6, +0x44,0x62,0x40,0x0C,0x85,0x00,0x50,0x94,0x84,0x40,0x02,0x03,0x00,0x54,0x4E,0x02, +0x00,0xE0,0xB0,0x45,0x84,0x40,0x80,0x06,0x49,0xFF,0xDE,0xE7,0xB0,0x46,0x84,0x41, +0x80,0x06,0x49,0xFF,0xDE,0xE2,0xB0,0x47,0x84,0x42,0x80,0x06,0x49,0xFF,0xDE,0xDD, +0xB0,0x48,0x84,0x43,0x80,0x06,0x49,0xFF,0xDE,0xD8,0xB0,0x49,0x80,0x06,0x84,0x44, +0x49,0xFF,0xDE,0xD3,0xF1,0x05,0x23,0xE0,0x80,0x04,0x22,0x00,0x80,0x05,0x13,0xE0, +0x80,0x08,0x12,0x00,0x80,0x09,0x02,0x43,0x00,0x54,0x84,0x24,0xE6,0x85,0xE8,0x03, +0x9E,0x61,0x96,0x49,0xF1,0x81,0x44,0x10,0x00,0xCC,0x42,0xF4,0x04,0x24,0x44,0x12, +0x40,0xD4,0x40,0x50,0xBC,0x00,0x44,0x32,0x40,0xD4,0xB4,0x25,0x40,0x20,0x98,0x09, +0x96,0x86,0xCA,0x18,0x50,0x21,0xFF,0x30,0x40,0xF1,0x3C,0x00,0x22,0x27,0x80,0x64, +0x22,0xF7,0x80,0x65,0x40,0x2F,0x08,0x01,0x8A,0x0F,0xFE,0x04,0x42,0x01,0x08,0x73, +0x46,0x20,0x00,0x9C,0x50,0x21,0x04,0x00,0xE0,0x40,0xE8,0x04,0x58,0x10,0x80,0x40, +0xB6,0x25,0x44,0x10,0x00,0x33,0x84,0x00,0x42,0x14,0x04,0x24,0x80,0x40,0x45,0xE2, +0x40,0x04,0xF1,0x83,0xF1,0x01,0xE2,0x41,0xE8,0x31,0xB0,0x45,0xB1,0x45,0x38,0x10, +0x8A,0x02,0xF1,0x82,0x9C,0x51,0x38,0x52,0x86,0x02,0xF1,0x02,0x22,0xF0,0x80,0x04, +0x22,0x12,0x80,0x04,0x22,0x52,0x80,0x05,0x40,0x17,0x84,0x01,0x04,0xFF,0x80,0x02, +0x22,0xF7,0x80,0x05,0x40,0x57,0x94,0x01,0xE6,0x42,0xE8,0x0F,0x04,0xFF,0x80,0x03, +0x40,0xF7,0x88,0x00,0x50,0xF7,0x80,0x24,0x40,0xF7,0x88,0x08,0x40,0xFF,0x3C,0x00, +0x12,0x17,0x80,0x02,0x12,0x57,0x80,0x03,0xFF,0x6C,0x42,0x50,0x84,0x73,0xE0,0x05, +0x8C,0x41,0x40,0x02,0xBC,0x1B,0x96,0x90,0xD5,0xCE,0xE6,0x82,0xE9,0x1B,0x46,0x10, +0x02,0x25,0x94,0x81,0x50,0x10,0x85,0x0F,0xE0,0x22,0xE8,0x06,0xE1,0x22,0x80,0x29, +0x40,0x11,0x3C,0x1A,0xD5,0x03,0x50,0x15,0x05,0x10,0x42,0x1E,0x04,0x24,0x14,0x13, +0x00,0x29,0x44,0x10,0x00,0xCC,0x42,0x34,0x04,0x73,0xB4,0x23,0x92,0x26,0x96,0x46, +0xC9,0x0F,0x3C,0x03,0xF6,0x91,0x96,0x06,0xC0,0x06,0x14,0x73,0x00,0x29,0x14,0x73, +0x00,0x28,0xD5,0x2E,0x44,0x00,0x57,0xE4,0x14,0x03,0x00,0x29,0xD5,0x27,0x50,0x15, +0x05,0x10,0x46,0x30,0x02,0x25,0x88,0x41,0x50,0x31,0x85,0x0F,0xE0,0x62,0xE8,0x05, +0xE1,0x22,0x80,0x22,0x40,0x14,0xBC,0x1B,0x44,0xF0,0x00,0x64,0x42,0x1E,0x04,0x24, +0x40,0x10,0xBC,0x36,0x14,0x13,0x00,0x29,0x46,0x10,0x02,0x25,0x94,0x02,0x50,0x10, +0x85,0x0F,0xE0,0x20,0xE8,0x05,0xE1,0x20,0x40,0x04,0xBC,0x1B,0xD5,0x03,0x50,0x05, +0x05,0x10,0x44,0xF0,0x00,0x64,0x40,0x00,0x3C,0x16,0x14,0x03,0x00,0x28,0x8D,0x01, +0x50,0x63,0x00,0xCC,0x5A,0x80,0x0B,0x04,0x48,0xFF,0xFF,0x19,0xFC,0xC5,0xFC,0x40, +0x51,0xFF,0xFD,0xF0,0x81,0x00,0xA6,0x00,0xF1,0x83,0x4E,0x02,0x01,0x69,0x4E,0x22, +0x01,0x67,0xE6,0x0C,0x80,0x20,0xE9,0x02,0x84,0x2B,0x96,0x08,0xE6,0x4C,0xF0,0x82, +0x80,0x02,0xE9,0x02,0x84,0x0B,0x96,0x00,0xB6,0x1F,0x46,0x30,0x50,0x00,0x84,0x00, +0xB0,0x8B,0x84,0x20,0x88,0x40,0x38,0x31,0x06,0x0A,0x8C,0x21,0x5A,0x18,0x0B,0xFD, +0x50,0x00,0x00,0x2C,0x5A,0x09,0xE4,0xF6,0x3C,0x03,0xF6,0x91,0x96,0x06,0x56,0x00, +0x00,0x01,0xF0,0x86,0x49,0x00,0x27,0x24,0xC0,0x04,0x3C,0x03,0xF6,0x91,0x96,0x06, +0xF0,0x84,0xB0,0x0B,0xF0,0x81,0xF0,0x03,0xF1,0x03,0x50,0x60,0x00,0x18,0xB4,0x1F, +0x84,0xE0,0x8E,0x01,0x96,0x00,0x94,0x05,0x50,0x00,0x00,0x38,0x88,0x01,0xF0,0x85, +0x50,0xA4,0x00,0x10,0x87,0x80,0x84,0x00,0xF0,0x8A,0x00,0x05,0x00,0x1B,0x5A,0x00, +0xFF,0x04,0x48,0x00,0x00,0xC2,0x50,0x03,0x7F,0xEC,0xB4,0xA0,0xA0,0x82,0xA1,0x01, +0xA0,0xC3,0x04,0x15,0x00,0x01,0x88,0x45,0x88,0x64,0xB4,0x0A,0x49,0xFF,0xF7,0xCA, +0x04,0x15,0x00,0x02,0xA0,0xB1,0xE0,0x22,0x80,0x62,0x40,0x30,0xBC,0x1A,0xE0,0x41, +0x40,0x20,0xBC,0x1A,0xF1,0x04,0x94,0xDD,0x40,0x91,0x89,0x36,0x5A,0x18,0x01,0x33, +0x00,0x14,0x00,0x00,0x5A,0x18,0x01,0x2F,0x00,0x23,0x7F,0xE8,0x44,0x10,0x00,0xCC, +0xFE,0x54,0x44,0x22,0x40,0x04,0x88,0x41,0x00,0x21,0x00,0xB5,0x5A,0x28,0x02,0x23, +0x2E,0x20,0x1B,0x9C,0x5A,0x28,0x01,0x1F,0x44,0x22,0x40,0xCC,0x88,0x22,0x00,0x35, +0x7F,0xFC,0xA0,0x89,0x40,0x11,0x38,0x08,0x90,0x3A,0x96,0x48,0x40,0x21,0x20,0x08, +0xE2,0x23,0x90,0x5A,0xE9,0x0F,0x00,0x35,0x7F,0xFD,0xE2,0x61,0xE9,0x0B,0x00,0x15, +0x7F,0xFE,0x96,0x90,0xE2,0x41,0xE9,0x06,0x00,0x15,0x7F,0xFF,0xE2,0x22,0x4E,0xF2, +0x00,0xCF,0x80,0x80,0x46,0x00,0x50,0x00,0xE0,0x04,0xE8,0x02,0x80,0x80,0xF0,0x06, +0xC0,0x0A,0x5E,0xF4,0x80,0x20,0xE9,0x07,0x5E,0xF4,0x80,0x41,0xE9,0x05,0x44,0x90, +0x00,0x40,0xD5,0x02,0xFB,0x30,0x04,0x03,0x7F,0xFF,0xE0,0x04,0xE8,0x58,0xB4,0x26, +0xE0,0x01,0xE8,0x5A,0xB4,0x6A,0x50,0x73,0x7F,0xEC,0xB4,0x47,0x04,0x55,0x00,0x01, +0x9A,0x9A,0xA0,0x7B,0xA0,0xF9,0xA0,0x3A,0x9A,0xEB,0x42,0x50,0x84,0x24,0x42,0x50, +0x00,0x73,0x42,0x71,0x8C,0x24,0xF4,0x88,0xF5,0x87,0x42,0x71,0x08,0x73,0x49,0xFF, +0xF7,0x63,0xF5,0x07,0xF4,0x08,0x4E,0x04,0x00,0x03,0xFE,0x02,0x44,0x10,0x7F,0xFF, +0xE0,0x20,0xE8,0x08,0xE0,0xA7,0x90,0x01,0xE8,0x03,0x90,0xE2,0xD5,0xFA,0x90,0xA2, +0xD5,0xF8,0xC7,0x22,0xC5,0x20,0xFE,0x04,0xF0,0x8A,0xB0,0x0A,0xF5,0x89,0xF4,0x88, +0x49,0xFF,0xF7,0x4F,0xF0,0x87,0xF0,0x0A,0x40,0x70,0x1C,0xF6,0xB0,0x0A,0xF7,0x8A, +0x49,0xFF,0xF7,0x47,0xF1,0x07,0xF5,0x09,0x88,0x01,0x97,0xC0,0xF0,0x0A,0xF4,0x08, +0x40,0x50,0x14,0xB6,0xE6,0xFF,0xE9,0x05,0x8E,0xE1,0x90,0xA1,0x97,0xF8,0xD5,0xFB, +0xF5,0x8A,0xD5,0x02,0x80,0xE5,0x84,0x01,0x40,0x70,0x1C,0x0C,0x90,0xE7,0xF0,0x0A, +0x95,0xFE,0xE0,0xE0,0xE8,0x09,0xB4,0x06,0xE0,0x04,0xE9,0x06,0x42,0x42,0x24,0x24, +0x40,0x72,0x14,0x0A,0xD5,0x03,0x46,0x70,0x50,0x00,0xF0,0x01,0xF1,0x02,0x50,0xA5, +0x00,0x28,0x38,0x70,0x72,0x0A,0x51,0xCE,0x00,0x01,0x54,0x0E,0x00,0xFF,0xE2,0x01, +0x4E,0xF3,0xFF,0x2B,0xF0,0x01,0x50,0x63,0x00,0x20,0x50,0x00,0x00,0x2C,0xF0,0x81, +0xF0,0x05,0x4C,0x60,0x7F,0x1F,0xB4,0x1F,0xF1,0x02,0xF2,0x02,0xE2,0x01,0xB4,0x3F, +0xB1,0x8B,0x40,0x11,0x3C,0x1B,0x80,0x06,0x49,0xFF,0xEB,0xB8,0x44,0x70,0x00,0xCC, +0x44,0x22,0x3C,0x1C,0x84,0x20,0x84,0x0B,0x46,0x30,0x50,0x00,0xFA,0x98,0x44,0x52, +0x40,0x04,0x85,0x41,0x87,0x9F,0x0C,0x91,0x00,0x01,0x81,0xE9,0x42,0xF0,0x80,0x73, +0x38,0xF3,0x3E,0x02,0x4C,0xF1,0xC0,0x05,0x15,0xC1,0x7F,0xFF,0xD5,0x18,0x4E,0x95, +0x00,0x17,0x05,0xEF,0x80,0x03,0x40,0xF0,0x94,0x08,0x38,0xFF,0x3C,0x00,0x83,0xC8, +0x43,0xE4,0x90,0x73,0x81,0x25,0x42,0x97,0x9C,0x73,0x10,0xFF,0x00,0x2B,0x81,0xE9, +0x10,0xA4,0x80,0xB6,0x04,0x91,0x7F,0xFF,0x10,0x97,0x80,0xCF,0x8C,0x21,0x04,0xFF, +0x80,0x00,0x54,0x90,0x80,0xFF,0xE3,0x2F,0xE9,0xD7,0xD5,0x09,0x40,0x10,0x24,0x36, +0xB4,0x46,0x94,0x4D,0xE0,0x22,0x4E,0xF2,0xFF,0x2E,0xD5,0xA0,0x51,0xFF,0x82,0x10, +0xFC,0xC0,0xFC,0x40,0x84,0x81,0x40,0x42,0x04,0x0C,0xE4,0x02,0x40,0x20,0x04,0x0C, +0x95,0xA1,0x9D,0xE5,0x50,0x92,0x7F,0xFB,0xE9,0x23,0x84,0x60,0x90,0x01,0x8C,0x61, +0x5A,0x08,0x01,0xFE,0x40,0x21,0x0C,0x0E,0x40,0x01,0x84,0x0C,0x85,0x00,0xE0,0x49, +0xE9,0x03,0xE0,0xE2,0xE8,0x16,0x80,0x68,0xE0,0x46,0xE8,0x06,0xFE,0x94,0x40,0x21, +0x04,0x0E,0x8C,0x61,0xD5,0xFA,0x81,0x03,0x81,0x44,0x84,0xA0,0xD3,0x04,0x91,0x41, +0x8C,0xA1,0xD5,0xFD,0xE4,0xAA,0x88,0x0A,0x90,0x41,0xE9,0xEA,0xD5,0x02,0x84,0x00, +0xFC,0xC0,0xFC,0x4B,0xF0,0x85,0x44,0x00,0xF1,0x5C,0xF1,0x82,0xB0,0x4C,0x81,0x42, +0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24,0xA4,0x00,0xAC,0x08,0x4E,0xA3,0x00,0x1C, +0x3C,0x04,0x0A,0xB9,0x3C,0x14,0x0A,0xB8,0x9A,0x41,0x2E,0x06,0xCC,0x7E,0x8E,0x01, +0x96,0x02,0x40,0x00,0x80,0x16,0x96,0x03,0x44,0x62,0x13,0x48,0xF2,0x02,0xF0,0x81, +0x80,0x26,0xFA,0x18,0x42,0x11,0x00,0x73,0xF1,0x83,0x2E,0x06,0xCC,0x7F,0x8E,0x01, +0xF0,0x87,0xD5,0x0E,0x3C,0x04,0x0A,0xBB,0x3C,0x14,0x0A,0xBA,0x9A,0x41,0x2E,0x06, +0xCC,0x7F,0xD5,0xE6,0x00,0x03,0x00,0x2C,0xC8,0x08,0x50,0x63,0x00,0x28,0xF0,0x03, +0x4C,0x60,0x7F,0xFA,0x48,0x00,0x01,0x4B,0x04,0x83,0x00,0x06,0x4E,0x87,0xFF,0xF7, +0x4E,0xA3,0x00,0x09,0x00,0x53,0x00,0x27,0xC5,0xF1,0x2E,0x06,0xCC,0x7E,0x8E,0x01, +0xD5,0x05,0x00,0x53,0x00,0x28,0xC5,0xEA,0xF0,0x07,0xD0,0xE8,0x00,0x93,0x00,0x27, +0x00,0x73,0x00,0x28,0x87,0x82,0x84,0x20,0x84,0x46,0xB0,0x08,0x49,0x00,0x43,0xE0, +0xB0,0xD1,0xFA,0x42,0x80,0x03,0x84,0x20,0xF3,0x86,0x49,0x00,0x43,0xD9,0x84,0x40, +0x2F,0xE6,0xCC,0x7E,0x80,0xA2,0x2E,0x06,0xCC,0x7F,0xF0,0x84,0xB0,0x0C,0x38,0x30, +0x08,0x00,0x88,0x02,0x40,0x11,0xA4,0x00,0x96,0x4A,0x20,0x00,0x00,0x01,0x4E,0x15, +0x00,0x21,0x96,0x00,0x99,0x07,0x97,0x22,0x4E,0x45,0x00,0x1C,0xE0,0x3E,0xE8,0x19, +0x04,0xFF,0x80,0x04,0xE0,0x8F,0xE8,0x15,0x42,0x12,0x78,0x73,0xF4,0x05,0x38,0x12, +0x05,0x11,0x4E,0x15,0x00,0x04,0x88,0xA1,0xD5,0x02,0x84,0x20,0x8C,0x01,0x8C,0x61, +0x96,0xD8,0x96,0x00,0x84,0x83,0x42,0x01,0x90,0x73,0xF3,0x06,0x38,0x11,0x81,0x09, +0x8C,0x42,0x5A,0x28,0x12,0xD5,0xB0,0x91,0x84,0x20,0x84,0x00,0x38,0x41,0x01,0x01, +0xB0,0xC8,0x4E,0xA3,0x00,0x09,0x38,0x31,0x85,0x01,0x88,0x64,0xB1,0x08,0x38,0x32, +0x05,0x09,0xD5,0x07,0x38,0x31,0x81,0x01,0x88,0x64,0xB1,0x08,0x38,0x32,0x01,0x09, +0x8C,0x01,0x5A,0x08,0x03,0xED,0x8C,0x21,0x8C,0x46,0x5A,0x18,0x03,0xE8,0xF0,0x02, +0x5A,0x08,0x01,0x21,0x22,0x0F,0x80,0x11,0x22,0xFF,0x80,0x10,0xE0,0x0F,0xE8,0x08, +0x4E,0xA3,0x00,0x05,0x50,0x04,0xFF,0xFF,0xD5,0x0B,0x8E,0xE1,0xD5,0x0D,0x22,0xFF, +0x80,0x12,0xE0,0x0F,0xE8,0x0A,0x4E,0xA3,0x00,0x07,0x50,0x04,0x80,0x01,0x54,0x90, +0x00,0xFF,0xD5,0x03,0x8C,0xE1,0x97,0xF8,0x5B,0xC0,0x01,0x05,0x87,0x81,0x48,0xFF, +0xFF,0x84,0x44,0x00,0x00,0x64,0x44,0x10,0x00,0x55,0xFE,0x2C,0x42,0x14,0x04,0x24, +0xE0,0x01,0x4E,0xF3,0xFF,0x5C,0xE0,0xA8,0xE8,0x08,0x40,0x80,0x21,0x16,0x51,0xC4, +0x7F,0xAB,0x52,0x84,0x00,0x64,0xD5,0x03,0x85,0x00,0x87,0x81,0x22,0x1F,0x80,0x10, +0x22,0x0F,0x80,0x11,0xE0,0x01,0xE8,0x03,0x12,0x1F,0x80,0x11,0x22,0x0F,0x80,0x12, +0x22,0x2F,0x80,0x11,0xE0,0x40,0xE8,0x03,0x12,0x0F,0x80,0x11,0x88,0x01,0x22,0x2F, +0x80,0x11,0x44,0x10,0x00,0x64,0xFE,0x0C,0x44,0x10,0x00,0x82,0xFE,0x54,0xE0,0x20, +0xE9,0x1B,0x44,0x10,0x00,0x5A,0xFE,0x54,0xE0,0x01,0xE9,0x18,0x84,0x7E,0x94,0x51, +0xFE,0x9C,0x40,0x10,0x04,0x36,0x40,0x00,0x08,0x16,0x50,0x10,0xFF,0xD3,0x50,0x00, +0x00,0x41,0x42,0x30,0x20,0x24,0x42,0xFE,0x04,0x24,0xE0,0x6F,0x40,0x80,0xBC,0x1B, +0x41,0xC0,0x3C,0x1B,0xD5,0x03,0x85,0x01,0x87,0x80,0x84,0x40,0xB0,0xCA,0xB0,0x08, +0x38,0x00,0x09,0x11,0x4E,0x06,0x00,0x06,0xB0,0x48,0x84,0x01,0x38,0x00,0x89,0x09, +0xB0,0x08,0x84,0x28,0x38,0x00,0x09,0x11,0xF3,0x86,0xF2,0x84,0x49,0xFF,0xFE,0xA3, +0xF2,0x04,0xF3,0x06,0x38,0x01,0x89,0x09,0x8C,0x41,0x5A,0x28,0x03,0xEA,0xA4,0x19, +0x22,0x11,0x80,0x02,0x22,0x41,0x80,0x00,0x94,0x01,0x8A,0x01,0x8A,0x04,0x96,0x03, +0x40,0x2E,0x20,0x00,0x5A,0xA8,0x01,0x09,0x44,0x32,0x3C,0xBC,0x38,0x31,0x9D,0x11, +0xF3,0x81,0xC8,0x03,0xD5,0x23,0xC0,0x08,0xF3,0x01,0x8A,0x24,0xFE,0x5C,0x94,0x01, +0x40,0x00,0x80,0x16,0x96,0x03,0x4E,0xA3,0x00,0x1A,0x44,0x12,0x3D,0x20,0x38,0x10, +0xA5,0x11,0x88,0x01,0xA0,0x74,0x42,0x14,0x04,0x24,0x42,0x10,0x70,0x73,0x40,0x20, +0x88,0x56,0x4E,0x25,0x00,0x08,0x3C,0x03,0x66,0x3C,0xE0,0x02,0x40,0x20,0x3C,0x1B, +0xD5,0x02,0x80,0x4A,0xA8,0xB4,0x48,0xFF,0xFE,0xCA,0x44,0x12,0x3F,0x84,0x38,0x10, +0x9D,0x11,0x88,0x01,0xA0,0x75,0x42,0x14,0x04,0x24,0x42,0x10,0x70,0x73,0x40,0x20, +0x88,0x56,0x4E,0x25,0x00,0x08,0x3C,0x03,0x66,0x3D,0xE0,0x02,0x40,0x20,0x3C,0x1B, +0xD5,0x02,0x84,0x40,0xA8,0xB5,0x48,0xFF,0xFE,0xB2,0xFC,0xCB,0xFC,0x41,0x80,0xE0, +0x84,0x48,0x80,0xC1,0x80,0x1F,0x84,0x20,0x49,0x00,0x42,0xAA,0x84,0x60,0x3C,0x9D, +0xFB,0x06,0x2E,0x4F,0xF0,0xDE,0x50,0x03,0x80,0x0C,0x80,0xA3,0x80,0x43,0xD6,0x18, +0x00,0x10,0x00,0x14,0x5A,0x18,0x01,0x10,0xE6,0x42,0xB4,0xE0,0xE8,0x03,0x38,0x7F, +0x8A,0x0A,0x00,0x10,0x00,0x12,0x8C,0x41,0x88,0x61,0xE1,0x27,0x96,0x90,0x96,0xD9, +0xE8,0x02,0x84,0x80,0x8C,0xA1,0x97,0x68,0x50,0x00,0x00,0x28,0xD5,0xE9,0x5A,0x28, +0x02,0x14,0xE6,0x79,0xE8,0x11,0xF1,0x01,0xB4,0x1F,0x80,0xA1,0xE0,0x01,0x40,0x50, +0x3C,0x1A,0xE0,0x20,0x40,0x00,0xBC,0x1B,0x94,0x01,0xE0,0xA0,0xE8,0x05,0x8C,0x81, +0x3E,0x47,0xF0,0xDE,0xD5,0x11,0x8E,0x81,0xE6,0x79,0x3E,0x47,0xF0,0xDE,0xE9,0x0C, +0x3C,0x03,0xF8,0x6E,0xE6,0x19,0xE9,0x08,0x84,0x01,0x3C,0x0F,0xFC,0x36,0x84,0x00, +0x3E,0x07,0xF0,0xDE,0xD5,0x07,0x2E,0x07,0xF0,0xD4,0xC8,0x04,0xCA,0x03,0x3C,0x2F, +0xFC,0x36,0x2E,0x17,0xF0,0xDE,0x3E,0x27,0xF0,0xD4,0x80,0x01,0x96,0x4A,0xE4,0x2D, +0x3C,0x3B,0xF8,0x6E,0xE9,0x03,0x84,0x0C,0xD5,0x05,0x96,0x42,0x4E,0x14,0x00,0x03, +0x84,0x00,0x96,0x42,0xE4,0x23,0x3E,0x07,0xF0,0xDE,0xE9,0x0A,0x3C,0x0D,0xFC,0x36, +0xC8,0x07,0x3C,0x03,0xF6,0x93,0x58,0x00,0x00,0x40,0x3C,0x0B,0xF6,0x93,0xFC,0xC1, +0xFC,0x01,0x44,0x62,0x13,0x48,0x84,0x40,0xA6,0x70,0xF0,0x81,0x49,0xFF,0xFE,0x03, +0xF0,0x01,0xA6,0x70,0x84,0x41,0x49,0xFF,0xFD,0xFE,0xFC,0x81,0xFC,0x00,0xB4,0x40, +0x80,0xA2,0x4E,0x24,0x00,0x03,0xFF,0x52,0x84,0xC0,0x50,0x43,0x00,0x0F,0x40,0x12, +0x90,0x0E,0xC1,0x03,0x8C,0xC1,0xD5,0xFA,0xA0,0x41,0x80,0x61,0x4E,0x14,0x00,0x03, +0xFE,0xCA,0x40,0x31,0x90,0x0E,0xCB,0xF7,0x40,0x21,0x18,0x0E,0x40,0x10,0x98,0x0E, +0xB6,0x40,0xA8,0x41,0x42,0x00,0x84,0x24,0x42,0x01,0x08,0x73,0x49,0xFF,0xF4,0x56, +0x40,0x00,0x18,0x0C,0xFC,0x80,0xFC,0x41,0x80,0xC0,0x81,0x23,0x22,0x00,0x80,0x08, +0x22,0x31,0x00,0x08,0x8A,0x03,0xB6,0x1F,0x22,0x00,0x80,0x09,0x22,0x11,0x00,0x09, +0x8A,0x01,0xF0,0x81,0x80,0x1F,0x49,0xFF,0xFF,0xCB,0x80,0xE0,0x80,0x06,0x49,0xFF, +0xFF,0xC7,0x94,0x79,0xE0,0x20,0xE8,0x3C,0x84,0xA0,0x80,0x25,0x80,0x45,0xFA,0x71, +0x4C,0x54,0x80,0x09,0x40,0x13,0x94,0x1A,0xFE,0x5C,0x90,0x25,0x88,0x41,0xE0,0x02, +0xE8,0x04,0xFA,0x30,0x84,0x40,0xD5,0x04,0x8C,0xA1,0x97,0x68,0xD5,0xF2,0x50,0x30, +0xFF,0xE0,0x96,0xD8,0xE2,0x69,0xE8,0x04,0x88,0x41,0x8C,0x21,0xD5,0xF9,0xE2,0xA9, +0xE9,0x0C,0xB4,0x26,0xA0,0x31,0x94,0x4D,0x94,0x05,0x40,0x10,0x88,0x36,0x40,0x00, +0x08,0x16,0xB6,0x26,0xA8,0x31,0xD5,0x12,0x5A,0x50,0x01,0x13,0xB4,0x26,0xFA,0x51, +0xFE,0x7C,0x40,0x10,0x80,0x36,0xFE,0x54,0x90,0x25,0xB6,0x26,0xA0,0x71,0xFF,0xCC, +0x40,0x03,0x80,0x16,0xFE,0x84,0x90,0x45,0xA8,0xB1,0x84,0x01,0xFC,0xC1,0x84,0x00, +0xFC,0xC1,0xFC,0x21,0x80,0xC0,0x50,0x70,0x08,0xC4,0x00,0x03,0x00,0xAD,0xC0,0x11, +0xB0,0x41,0x84,0x40,0x80,0x06,0x49,0xFF,0xD9,0xD8,0xF0,0x01,0x22,0x20,0x00,0x04, +0x22,0x10,0x00,0x05,0xAC,0x86,0xAC,0x47,0x12,0x20,0x00,0x08,0x12,0x10,0x00,0x09, +0x50,0x63,0x00,0xCC,0x4C,0x63,0xFF,0xEB,0xFC,0xA1,0xFC,0x46,0xFA,0x44,0x80,0xC0, +0x00,0xA0,0x80,0x0C,0x00,0x80,0x80,0x0D,0xB0,0x02,0x84,0x20,0xB1,0xC7,0x49,0x00, +0x41,0x8F,0x80,0x07,0x84,0x20,0xFA,0x44,0x49,0x00,0x41,0x8A,0x51,0xC3,0x08,0xC4, +0x00,0x03,0x00,0xAD,0x5A,0x08,0x02,0x6A,0x85,0x24,0x80,0x06,0xB0,0x41,0x80,0x49, +0x49,0xFF,0xD9,0xAB,0x4E,0xA2,0x00,0x0D,0x5A,0xA8,0x01,0x15,0xF0,0x01,0xB0,0x82, +0x22,0x10,0x00,0x06,0x38,0x11,0x26,0x0A,0x22,0x00,0x00,0x07,0xD5,0x09,0xF0,0x01, +0xB0,0x82,0x22,0x10,0x00,0x04,0x38,0x11,0x26,0x0A,0x22,0x00,0x00,0x05,0x38,0x03, +0xA6,0x0A,0x4E,0x92,0x00,0x07,0x50,0x04,0xFF,0xFF,0x54,0x90,0x00,0xFF,0xD5,0xDE, +0xF1,0x01,0x5A,0x80,0x01,0x05,0x5A,0x80,0x02,0x08,0xD5,0x39,0x02,0x03,0x00,0x55, +0xE6,0x02,0xE8,0x13,0xD5,0x34,0x02,0x03,0x00,0x55,0x5A,0x08,0x02,0x19,0x02,0x03, +0x00,0x54,0xE6,0x02,0xE9,0x2C,0x04,0x03,0x00,0x32,0x54,0x00,0x01,0x00,0xC0,0x05, +0xF0,0x03,0xAC,0x0E,0xA0,0x39,0xD5,0x22,0xF2,0x02,0xF0,0x03,0x88,0x02,0x90,0x01, +0xAC,0x0E,0xB4,0x47,0xA0,0x39,0x88,0x02,0x90,0x01,0xD5,0x18,0xE6,0x03,0xE9,0x17, +0x02,0x03,0x00,0x54,0xE6,0x03,0xE9,0x13,0xF2,0x02,0xF0,0x04,0x88,0x02,0x40,0x20, +0x08,0x0A,0xF0,0x03,0x90,0x01,0x88,0x02,0xAC,0x0E,0xB4,0x47,0xA0,0x3A,0x88,0x02, +0x40,0x20,0x08,0x0A,0xA0,0x39,0x90,0x01,0x88,0x02,0xAC,0x0F,0xA4,0x0E,0x12,0x00, +0x80,0x08,0xA4,0x0F,0x12,0x00,0x80,0x09,0x50,0x63,0x00,0xCC,0x4C,0x6E,0x7F,0x92, +0xFC,0xC6,0xFC,0x40,0xB4,0xE0,0xA1,0x42,0xA1,0x09,0xB4,0xC1,0x04,0x90,0x00,0x01, +0xA0,0x0A,0x42,0x33,0x90,0x24,0x42,0x12,0x90,0x24,0x42,0x10,0x24,0x75,0x42,0x33, +0x24,0x75,0xFE,0x3C,0x42,0x03,0x14,0x75,0xCB,0x05,0xC9,0x0C,0x5C,0x00,0x00,0x01, +0xFC,0xC0,0x40,0x00,0x0C,0x16,0x40,0x10,0x8C,0x36,0xA8,0x11,0xB6,0x22,0x84,0x02, +0xFC,0xC0,0x80,0x03,0xFC,0xC0,0x88,0x41,0xA1,0x01,0x40,0x31,0x04,0x0A,0x80,0xA3, +0x42,0x50,0x90,0x73,0x40,0x42,0x88,0x96,0xB4,0xA0,0xA9,0x01,0x42,0x30,0x94,0x73, +0x40,0x31,0x88,0x76,0xB6,0x60,0xDD,0x9E,0xFC,0x43,0xB1,0xC2,0xB5,0x00,0x3A,0x23, +0x94,0x20,0x00,0x4F,0x80,0x38,0x04,0x90,0x00,0x01,0x4E,0x42,0x00,0x8E,0x4E,0x85, +0x00,0x03,0x80,0x43,0x4E,0x95,0x00,0x04,0xA1,0xFB,0xD5,0x02,0xA1,0xFA,0x5A,0x48, +0x03,0x26,0x80,0x28,0x4E,0x84,0x00,0x04,0x52,0x14,0x00,0x00,0xE0,0x41,0xE9,0x04, +0x84,0x20,0xB6,0x20,0xD5,0x08,0x84,0x21,0x4E,0x84,0x00,0x03,0x84,0x3F,0x42,0x81, +0x04,0x75,0xB7,0x00,0x80,0x29,0x4E,0x94,0x00,0x04,0x52,0x14,0x80,0x00,0xE0,0xE1, +0xE9,0x04,0x84,0x20,0xA8,0x41,0xFC,0xC3,0x84,0x21,0x4E,0x94,0x00,0x03,0x84,0x3F, +0x42,0x93,0x84,0x75,0x14,0x90,0x00,0x01,0xFC,0xC3,0x81,0x40,0x5A,0x40,0x02,0x04, +0x80,0xC1,0xD5,0x38,0x42,0x53,0xA0,0x24,0x42,0x41,0x24,0x24,0x84,0xC0,0xFE,0xEA, +0x52,0xF2,0x00,0x00,0x51,0xC3,0x00,0x0F,0x80,0x25,0x4E,0x54,0x00,0x03,0x80,0x23, +0x40,0x10,0xF0,0x0E,0xC1,0x04,0x8C,0xC1,0x97,0xB0,0xD5,0xF5,0x80,0x04,0x4E,0x44, +0x00,0x03,0x80,0x0F,0x40,0x00,0x70,0x0E,0xC8,0xF7,0x40,0x42,0x18,0x0E,0x40,0x52, +0x98,0x0E,0x42,0x02,0x10,0x24,0x42,0x02,0x94,0x73,0xF2,0x81,0x49,0xFF,0xF2,0xC6, +0x40,0x60,0x18,0x0C,0xF2,0x01,0xC6,0x30,0x42,0x04,0xA4,0x24,0x42,0x04,0x20,0x73, +0xFF,0xD4,0x49,0xFF,0xF2,0xBB,0xFE,0x3C,0x40,0x60,0x18,0xD6,0x4E,0x64,0x00,0x03, +0xFF,0xB2,0x80,0x0A,0x49,0xFF,0xFE,0x3C,0x84,0x65,0x42,0x13,0x0C,0x24,0xE0,0x20, +0xE9,0x1B,0xE0,0xC0,0xE9,0x06,0x84,0x00,0xB6,0x0A,0x14,0x05,0x00,0x01,0xFC,0xC3, +0x42,0x24,0x18,0x24,0x42,0x64,0x98,0x24,0x40,0x21,0x00,0x56,0xB4,0x8A,0x40,0x63, +0x00,0xD6,0x80,0x2A,0xA0,0x09,0x9A,0xA2,0x9B,0x86,0xFE,0x9C,0xFE,0xF4,0x90,0x42, +0x90,0x62,0xB6,0x4A,0xA8,0xC9,0xFC,0xC3,0x84,0x4A,0xFE,0x54,0xE0,0x20,0xE8,0x04, +0x8E,0x0A,0x96,0x01,0xDD,0x9E,0x96,0x09,0xDD,0x9E,0xFC,0x56,0xF2,0x86,0x44,0x20, +0xF1,0x70,0x81,0x21,0xB0,0x68,0xF3,0x87,0xF4,0x8F,0x3A,0x21,0x14,0x00,0x3A,0x20, +0x94,0x20,0xF4,0x06,0x84,0x60,0x22,0x42,0x00,0x00,0x3C,0x23,0x66,0x3C,0xF4,0x8B, +0xF4,0x06,0x80,0xC0,0xA5,0x21,0x3C,0x13,0x66,0x3D,0x9B,0x14,0x97,0x23,0xF4,0x8C, +0xF4,0x07,0x44,0x00,0x00,0x64,0xF0,0x83,0x22,0x42,0x00,0x00,0xF0,0x8A,0xF4,0x8D, +0xF4,0x07,0xF0,0x89,0xF3,0x9F,0xA5,0x21,0xF3,0xA0,0x9B,0x0C,0x97,0x23,0xF4,0x8E, +0x40,0x41,0x04,0x09,0xF4,0x93,0x40,0x40,0x84,0x09,0xF4,0x94,0xF3,0x82,0xF0,0x88, +0x80,0x02,0x92,0x03,0xF0,0x95,0x80,0x01,0x92,0x03,0xF3,0x90,0xF2,0x84,0xF1,0x85, +0xF0,0x92,0x00,0x0F,0x80,0x08,0xF0,0x91,0x00,0x03,0x00,0xAD,0x4E,0x02,0x04,0x76, +0xB0,0x5A,0x84,0x40,0x80,0x06,0x49,0xFF,0xD8,0x28,0xB0,0x5B,0x84,0x41,0x80,0x06, +0x49,0xFF,0xD8,0x23,0xB0,0x5C,0x84,0x42,0x80,0x06,0x49,0xFF,0xD8,0x1E,0xB0,0x5D, +0x84,0x43,0x80,0x06,0x49,0xFF,0xD8,0x19,0x80,0x06,0xB0,0x5E,0x84,0x44,0x49,0xFF, +0xD8,0x14,0x02,0x03,0x00,0x55,0xE6,0x02,0x4E,0xF2,0x00,0xE4,0x02,0x03,0x00,0x54, +0x5A,0x00,0x02,0x04,0x48,0x00,0x00,0xDE,0x44,0x20,0xF1,0x80,0xB0,0x23,0x3A,0x21, +0x0C,0x00,0x3A,0x20,0x0C,0x20,0xF4,0x1A,0x3C,0x73,0x66,0x3C,0x22,0x52,0x00,0x06, +0x3C,0xA3,0x66,0x3D,0xF0,0x06,0xF1,0x1B,0x22,0x00,0x00,0x00,0x4C,0x02,0x80,0xCA, +0xF0,0x06,0x22,0x00,0x00,0x01,0x9A,0x38,0x4C,0x50,0x00,0xC4,0xF0,0x07,0x22,0x52, +0x00,0x07,0x22,0x00,0x00,0x00,0x4C,0x02,0x80,0xBD,0xF0,0x07,0x22,0x00,0x00,0x01, +0x40,0x05,0x00,0x01,0x4C,0x50,0x00,0xB6,0x22,0x02,0x00,0x04,0x40,0x33,0x88,0x09, +0x9A,0xB8,0x22,0x42,0x00,0x05,0x4E,0x24,0x00,0x03,0xFE,0x92,0x96,0x93,0xE0,0x62, +0xE8,0x10,0x40,0x25,0x10,0x01,0x40,0x55,0x08,0x09,0x4E,0x24,0x00,0x03,0xFE,0x92, +0x96,0x93,0xE0,0xA2,0xE8,0x06,0xE0,0x60,0xE8,0x04,0xE0,0xA4,0x4E,0xF3,0x00,0x9A, +0x22,0x50,0x80,0x04,0x22,0x30,0x80,0x05,0x9A,0x85,0x8A,0x83,0x84,0x03,0xFF,0x04, +0x84,0x02,0x40,0x82,0x01,0x16,0xF2,0xA1,0x14,0x8F,0x80,0x22,0xCA,0x03,0x4E,0x82, +0x00,0x89,0xB0,0x21,0xF3,0x99,0xF2,0x98,0xF5,0x97,0xF1,0x96,0x49,0xFF,0xFD,0x50, +0x5E,0xF0,0x00,0x32,0x4E,0xF3,0x00,0x7E,0xF5,0x17,0xF2,0x18,0xF3,0x19,0x9A,0x2A, +0x40,0x81,0xA0,0x01,0xF1,0x16,0x4E,0x07,0x00,0x08,0xE0,0x07,0xE8,0x05,0x4E,0x87, +0x00,0x04,0xE1,0x0A,0xE9,0x6E,0x22,0x50,0x80,0x06,0xF4,0x21,0x22,0x10,0x80,0x07, +0xF2,0x22,0xFE,0x64,0xFE,0x92,0x42,0x11,0x14,0x73,0xF2,0xA5,0xF4,0xA6,0xF1,0xA7, +0x4E,0x06,0x00,0x06,0xB0,0x25,0x44,0x12,0x03,0x28,0xD5,0x07,0xE0,0x07,0x84,0x00, +0xE9,0x07,0xB0,0x25,0x44,0x12,0x03,0x1C,0xB0,0xA3,0x49,0xFF,0xFE,0x3C,0xF1,0x23, +0x4E,0x15,0x00,0x09,0xE0,0xE1,0xE9,0x06,0xF2,0x24,0x4E,0x25,0x00,0x04,0xE1,0x42, +0xE8,0x46,0x4E,0x86,0x00,0x06,0xB0,0x25,0x44,0x12,0x03,0x10,0xD5,0x06,0xE1,0x0A, +0xE9,0x07,0xB0,0x25,0x44,0x12,0x03,0x04,0xB0,0xA3,0x49,0xFF,0xFE,0x24,0x5A,0x08, +0x02,0x39,0xF1,0x23,0x4E,0x15,0x00,0x06,0xE0,0xE1,0x40,0x70,0xBC,0x1A,0xD5,0x02, +0x84,0xE0,0xF0,0x24,0x3C,0x7F,0xF7,0xA5,0x4E,0x05,0x00,0x06,0xE0,0x0A,0x40,0xA0, +0x3C,0x1B,0xD5,0x02,0x85,0x40,0x3C,0xAF,0xF7,0xA6,0x84,0x01,0x12,0x03,0x00,0x55, +0x50,0x73,0x00,0xC8,0xB4,0x07,0x84,0x22,0x58,0x00,0x01,0x00,0xB6,0x07,0xF0,0x11, +0x49,0x00,0x05,0x4A,0xF0,0x11,0x84,0x20,0x49,0x00,0x05,0x4F,0xF0,0x1A,0x3C,0x17, +0xEF,0x4C,0x3C,0x27,0xEF,0x4A,0x12,0x10,0x00,0x09,0x12,0x20,0x00,0x08,0xAC,0x86, +0xAC,0x47,0xB4,0x27,0x84,0x17,0xFE,0x0E,0x48,0x00,0x03,0x65,0x5A,0x00,0x02,0xCE, +0x02,0x73,0x00,0x55,0x5A,0x78,0x01,0x32,0x04,0x03,0x00,0x32,0x96,0x1E,0x4E,0x02, +0x03,0x43,0xF0,0x0F,0x5A,0x00,0x01,0x04,0x48,0x00,0x03,0x3E,0xF1,0x1A,0x3C,0x2D, +0xF7,0xA4,0x3C,0x3D,0xF7,0xA3,0x22,0x00,0x80,0x07,0x22,0x40,0x80,0x06,0x8A,0x02, +0x8A,0x83,0xFE,0x04,0x42,0x02,0x10,0x73,0x5E,0xF0,0x27,0x10,0x4E,0xF2,0x03,0x2C, +0x5E,0xF0,0x19,0x00,0xE8,0x04,0x12,0x30,0x80,0x08,0xD5,0x0B,0x22,0x00,0x80,0x08, +0x88,0x03,0x90,0x01,0x12,0x00,0x80,0x08,0x22,0x00,0x80,0x09,0x88,0x40,0x90,0x41, +0x12,0x20,0x80,0x09,0x48,0x00,0x03,0x34,0xE6,0xE2,0x4E,0xF3,0x03,0x2E,0x00,0x03, +0x00,0x97,0x5A,0x00,0x1F,0x0F,0x00,0x03,0x00,0x96,0xC0,0x0B,0x00,0x03,0x00,0x94, +0xC0,0x08,0x00,0x03,0x00,0x95,0x5A,0x00,0x11,0x05,0x84,0x01,0x10,0x03,0x00,0xAF, +0x04,0x03,0x00,0x31,0x40,0x00,0x50,0x08,0x92,0x1C,0x8C,0x0F,0x40,0x00,0x70,0x08, +0x90,0x1C,0x80,0x20,0x4E,0x04,0x00,0x03,0x84,0x20,0x96,0x5F,0x40,0x00,0xA0,0x08, +0x02,0x13,0x00,0x62,0x44,0x2F,0xF0,0xFF,0xFE,0x56,0xFE,0x47,0x12,0x13,0x00,0x62, +0x04,0xAF,0x80,0x1A,0xB4,0x29,0xB4,0x0A,0xE0,0x20,0xE8,0x08,0x02,0x03,0x00,0x62, +0xFE,0x86,0x58,0x21,0x08,0x00,0x12,0x23,0x00,0x62,0x04,0x13,0x00,0x31,0x04,0x03, +0x00,0x32,0x54,0x10,0x8F,0x00,0xC9,0x05,0x44,0x1F,0xFF,0xEF,0xFE,0x0E,0xD5,0x03, +0x58,0x00,0x00,0x10,0x14,0x03,0x00,0x32,0x04,0x03,0x00,0x32,0x54,0x10,0x00,0x08, +0xC1,0x10,0x00,0x13,0x00,0xAD,0x5A,0x18,0x02,0x0D,0x54,0x10,0x00,0x20,0x02,0x03, +0x00,0x5C,0xC1,0x04,0x00,0x14,0x80,0x09,0xD5,0x71,0x00,0x14,0x80,0x08,0xD5,0x6E, +0x5C,0xF3,0x80,0x33,0xE9,0x2E,0x96,0x26,0xC0,0x2C,0xF0,0x0F,0x5A,0x08,0x01,0x2A, +0x3C,0x0D,0xFC,0x38,0x84,0x27,0xFE,0x0C,0xB4,0x2A,0x90,0x03,0xE0,0x20,0xE9,0x05, +0x00,0x03,0x00,0xAD,0x5A,0x08,0x03,0x1E,0xF2,0x1C,0xF0,0x1B,0x22,0xF1,0x00,0x09, +0x22,0x30,0x00,0x08,0x22,0x00,0x00,0x09,0x22,0x11,0x00,0x08,0x8A,0x0F,0x9A,0x59, +0xFE,0x04,0x42,0x00,0x84,0x73,0x44,0x10,0x9C,0x3F,0xE0,0x20,0xE9,0x0A,0x02,0x14, +0x80,0x02,0x84,0x0A,0xFE,0x0C,0x12,0x03,0x00,0x5C,0x84,0x01,0xF0,0x90,0xD5,0x42, +0x00,0x03,0x00,0xAD,0x5A,0x08,0x03,0x21,0x02,0x03,0x00,0x5C,0x00,0x14,0x80,0x0B, +0x49,0xFF,0xFD,0xEC,0xE6,0xE6,0x12,0x03,0x00,0x5C,0xE8,0x34,0x22,0x13,0x00,0x60, +0x22,0x05,0x00,0x04,0x8A,0x01,0xF0,0x9F,0x22,0x13,0x00,0x61,0x22,0x05,0x00,0x05, +0x8A,0x01,0xF0,0xA0,0xB0,0x1F,0x49,0xFF,0xFB,0xF3,0x5E,0xF0,0x03,0xE8,0xE8,0x22, +0x44,0x00,0x03,0xE8,0xD5,0x1D,0xF0,0x1B,0xF1,0x0B,0x22,0x50,0x00,0x08,0xD1,0x09, +0xF1,0x0C,0xD1,0x07,0x22,0x50,0x00,0x09,0xF0,0x0D,0xD0,0x03,0xF0,0x0E,0xD8,0x0A, +0x00,0x14,0x80,0x0A,0x84,0x03,0xFE,0x44,0x90,0x21,0x02,0x03,0x00,0x5C,0x96,0x48, +0xD5,0x05,0x02,0x03,0x00,0x5C,0x00,0x14,0x80,0x0A,0x49,0xFF,0xFD,0xB7,0x12,0x03, +0x00,0x5C,0xF0,0x10,0x02,0xA3,0x00,0x5C,0xC8,0x5B,0x00,0x03,0x00,0xAD,0x5A,0x00, +0x03,0x58,0xF3,0x1B,0xF0,0x0C,0xF1,0x0B,0x22,0x21,0x80,0x08,0xF4,0x0D,0x8A,0x02, +0x9A,0x51,0xE0,0x20,0x40,0x10,0x3C,0x1A,0x22,0x31,0x80,0x09,0xF0,0x0E,0x9B,0x1C, +0x8A,0x03,0xE0,0x80,0x40,0x02,0x3C,0x1B,0x50,0x41,0x76,0x70,0x97,0x21,0x5C,0xF2, +0x07,0x4A,0x14,0xAF,0x80,0x09,0x96,0x4B,0x96,0x03,0xE8,0x0A,0x80,0x8A,0x95,0x22, +0x50,0x42,0x01,0xEF,0xE0,0x83,0xE9,0x04,0x50,0x01,0xFE,0x11,0x96,0x03,0x40,0x45, +0x08,0x08,0xE0,0x24,0xE8,0x0F,0xF5,0x13,0x90,0x22,0xE0,0x45,0x96,0x4B,0xE8,0x05, +0xF1,0x88,0x14,0xAF,0x80,0x03,0xD5,0x04,0xF1,0x83,0x14,0xAF,0x80,0x08,0x84,0xE2, +0xD5,0x06,0x14,0xAF,0x80,0x03,0x14,0xAF,0x80,0x08,0x84,0xE1,0xE0,0x04,0xE8,0x0D, +0xF1,0x14,0x90,0x02,0xE0,0x61,0xE8,0x04,0x96,0x03,0xF0,0x8A,0xD5,0x04,0x14,0xAF, +0x80,0x0A,0xF0,0x89,0x84,0xE2,0xD5,0x03,0x14,0xAF,0x80,0x0A,0xF0,0x0A,0xC0,0x0A, +0xF0,0x09,0xC0,0x08,0xF0,0x08,0xC0,0x06,0xF0,0x03,0xC8,0x05,0xD5,0x03,0x84,0xE1, +0xD5,0x02,0x84,0xE3,0x04,0x03,0x00,0x32,0x54,0x10,0x00,0x80,0xF0,0x1B,0xC9,0x0E, +0xF2,0x1A,0x22,0x30,0x00,0x08,0x22,0x00,0x00,0x09,0x22,0x11,0x00,0x06,0x8A,0x23, +0xF1,0x9F,0x22,0x11,0x00,0x07,0x9A,0x08,0xD5,0x0C,0x22,0x20,0x00,0x08,0x04,0x13, +0x00,0x26,0x8A,0x22,0xF1,0x9F,0x22,0x10,0x00,0x09,0x04,0x03,0x00,0x27,0x8A,0x01, +0xF0,0xA0,0x00,0x03,0x00,0xAD,0x5A,0x08,0x02,0x09,0x04,0x13,0x00,0x32,0x44,0x0F, +0xFF,0x7F,0xFE,0x0E,0x14,0x03,0x00,0x32,0x04,0x03,0x00,0x32,0x96,0x3E,0x4E,0x03, +0x01,0x32,0x44,0x20,0xF1,0x80,0xB0,0x23,0x00,0x53,0x00,0xAE,0x3A,0x21,0x0C,0x00, +0x04,0x8F,0x80,0x1B,0xF1,0x1E,0x02,0x43,0x00,0x54,0x3A,0x20,0x0C,0x20,0x5A,0x58, +0x01,0x04,0x48,0x00,0x00,0xD9,0xE6,0x85,0x4E,0xF3,0x00,0xD6,0xF0,0x06,0x23,0xE4, +0x00,0x08,0x22,0x50,0x00,0x00,0x4C,0x5F,0x00,0xCF,0x22,0x00,0x00,0x01,0xF2,0x04, +0x9A,0x10,0x4D,0xE0,0x00,0xC9,0xF0,0x07,0x22,0x24,0x00,0x09,0x22,0x50,0x00,0x00, +0x4C,0x51,0x00,0xC2,0x22,0x00,0x00,0x01,0xF3,0x05,0x9A,0x18,0x4C,0x20,0x00,0xBC, +0x02,0x0F,0x80,0x2A,0x22,0x44,0x00,0x04,0xF0,0x91,0xF0,0x04,0x22,0x34,0x00,0x05, +0x8A,0x04,0x4E,0x04,0x00,0x03,0xFE,0x02,0xF5,0x11,0x96,0x03,0xE0,0xA0,0xE8,0x10, +0xF0,0x05,0x8A,0x03,0x4E,0x04,0x00,0x03,0xFE,0x02,0xF5,0x12,0x96,0x03,0xE0,0xA0, +0xE8,0x07,0xF0,0x11,0xE0,0x04,0xE8,0x04,0xE0,0xA3,0x4E,0xF3,0x00,0x9D,0xF0,0x1F, +0xC8,0x04,0xF0,0x20,0x4E,0x02,0x00,0x98,0x22,0x00,0x80,0x04,0x5A,0x0F,0xFF,0x04, +0x48,0x00,0x00,0x92,0x22,0x50,0x80,0x05,0x5A,0x5F,0xFF,0x04,0x48,0x00,0x00,0x8C, +0x22,0xF0,0x80,0x09,0x22,0x10,0x80,0x08,0x4D,0xE0,0xC0,0x04,0x4C,0x27,0x80,0x84, +0x9A,0x60,0x9A,0x9D,0x84,0x05,0xFE,0x44,0x85,0xE4,0xFE,0x14,0x40,0x10,0xBC,0x36, +0x40,0x20,0x3C,0x56,0xF1,0xA1,0xF2,0xA2,0xC9,0x02,0xC2,0x75,0xB0,0x21,0xF2,0x98, +0xF1,0x97,0xF3,0x96,0xF4,0x91,0x49,0xFF,0xFA,0xC3,0x5E,0xF0,0x00,0x32,0xE9,0x6B, +0xF4,0x11,0xF1,0x17,0xF3,0x16,0xF2,0x18,0x88,0x24,0x88,0x62,0x4E,0x17,0x00,0x0A, +0xF0,0x04,0xE0,0x20,0xE8,0x06,0x4E,0x37,0x00,0x05,0xF0,0x05,0xE0,0x60,0xE9,0x5B, +0xF4,0x1F,0x22,0x04,0x00,0x09,0xF2,0x20,0x22,0x54,0x00,0x08,0xFE,0x92,0xFE,0x24, +0x42,0x01,0x14,0x73,0xF2,0xA5,0xF4,0xA6,0xF0,0xA7,0x4E,0x16,0x00,0x07,0xF3,0x91, +0xB0,0x25,0x44,0x12,0x03,0x28,0xD5,0x09,0xF0,0x04,0xE0,0x20,0x84,0x00,0xE9,0x09, +0xF3,0x91,0xB0,0x25,0x44,0x12,0x03,0x1C,0xB0,0xA3,0x49,0xFF,0xFB,0xAC,0xF3,0x11, +0xF1,0x23,0x4E,0x15,0x00,0x0B,0xF2,0x04,0xE0,0x41,0xE9,0x07,0xF2,0x24,0x4E,0x25, +0x00,0x05,0xF4,0x05,0xE0,0x82,0xE8,0x2D,0x4E,0x36,0x00,0x06,0xB0,0x25,0x44,0x12, +0x03,0x10,0xD5,0x07,0xF1,0x05,0xE0,0x61,0xE9,0x07,0xB0,0x25,0x44,0x12,0x03,0x04, +0xB0,0xA3,0x49,0xFF,0xFB,0x90,0x5A,0x08,0x02,0x1F,0xF1,0x23,0x4E,0x15,0x00,0x07, +0xF0,0x04,0xE0,0x01,0x40,0x10,0x3C,0x1B,0xD5,0x02,0x84,0x20,0xF0,0x24,0x4E,0x05, +0x00,0x07,0xF2,0x05,0xE0,0x02,0x40,0x01,0x3C,0x1A,0xD5,0x02,0x84,0x00,0x22,0x24, +0x00,0x08,0x22,0xF4,0x00,0x09,0x8A,0x22,0x8A,0x0F,0xF1,0x9F,0xF0,0xA0,0xD5,0x4A, +0x5A,0x00,0x02,0xE8,0xF0,0x08,0xB0,0xA8,0xF0,0xA8,0xF0,0x03,0xB6,0xFF,0xF0,0xA9, +0xF0,0x0A,0xF6,0x81,0xF0,0xAA,0xF0,0x09,0x80,0x2A,0xF0,0xAB,0x3A,0x21,0x14,0x00, +0xB0,0x1F,0x49,0xFF,0xFB,0x93,0x00,0x03,0x00,0xAE,0x5A,0x08,0x01,0x0C,0x5A,0x70, +0x03,0x0A,0xB0,0x1F,0x00,0x14,0x80,0x06,0x00,0x24,0x80,0x07,0x49,0xFF,0xFB,0x75, +0xD5,0x60,0x00,0x03,0x00,0xAD,0x5A,0x08,0x03,0x5D,0x04,0x03,0x00,0x32,0x44,0x50, +0xF0,0x00,0xFF,0x46,0x44,0x00,0x10,0x00,0xD8,0x0E,0xF1,0x1B,0xF0,0x1F,0x22,0x20, +0x80,0x08,0x22,0x10,0x80,0x09,0x88,0x02,0x14,0x03,0x00,0x26,0xF0,0x20,0x88,0x01, +0x14,0x03,0x00,0x27,0xF3,0x02,0x44,0x72,0x15,0x0C,0xB0,0x1F,0xF1,0x1B,0xF2,0x1C, +0x38,0x33,0x8C,0x00,0x49,0xFF,0xFA,0x39,0xC8,0x3C,0xF0,0x02,0x38,0x03,0x80,0x00, +0xD5,0x27,0x04,0x03,0x00,0x32,0x54,0x10,0x00,0x80,0xC9,0x12,0xF2,0x1B,0xF1,0x1F, +0x58,0x00,0x00,0x80,0x22,0x31,0x00,0x08,0x22,0x21,0x00,0x09,0x88,0x23,0x14,0x13, +0x00,0x26,0xF1,0x20,0x14,0x03,0x00,0x32,0x88,0x22,0x14,0x13,0x00,0x27,0xF3,0x02, +0x44,0x02,0x15,0x0C,0xF1,0x1B,0x38,0x30,0x0C,0x00,0x00,0x03,0x00,0xAD,0xF2,0x1C, +0x5A,0x00,0x03,0x14,0xF1,0x02,0x44,0x02,0x15,0x0C,0x38,0x00,0x04,0x00,0x8C,0x01, +0x96,0x1F,0x04,0x23,0x00,0x32,0x40,0x10,0x30,0x08,0x44,0x0F,0x0F,0xFF,0xFE,0x16, +0xFE,0x0F,0x14,0x03,0x00,0x32,0xD5,0x05,0xB0,0x1F,0x49,0xFF,0xF9,0xFE,0xC0,0xEB, +0xF3,0x1F,0xF1,0x1A,0xF0,0x1B,0xCB,0x03,0xF2,0x20,0xC2,0x0B,0x02,0x20,0x00,0x08, +0x02,0x00,0x00,0x09,0x88,0x43,0x12,0x20,0x80,0x08,0xF2,0x20,0x88,0x02,0xD5,0x07, +0x22,0x20,0x00,0x08,0x22,0x00,0x00,0x09,0x12,0x20,0x80,0x08,0x12,0x00,0x80,0x09, +0x04,0x03,0x00,0x32,0x96,0x1E,0xC0,0x19,0xF0,0x1F,0xC8,0x03,0xF0,0x20,0xC0,0x0C, +0x04,0x13,0x00,0x32,0x84,0x17,0xFE,0x0E,0x14,0x03,0x00,0x32,0x44,0x0F,0xFF,0x9C, +0x3C,0x0F,0xF7,0xA3,0xD5,0x08,0xF0,0x1A,0x22,0x10,0x00,0x08,0x3C,0x1F,0xF7,0xA3, +0x22,0x00,0x00,0x09,0x3C,0x0F,0xF7,0xA4,0xF0,0x1A,0xF2,0x0B,0x22,0x10,0x00,0x08, +0xE0,0x22,0xE9,0x07,0xF2,0x0C,0xE0,0x22,0x40,0x20,0xBC,0x1B,0x80,0x22,0xD5,0x02, +0xF1,0x0B,0x12,0x10,0x00,0x08,0xF2,0x0D,0x22,0x10,0x00,0x09,0xE0,0x22,0xE9,0x07, +0xF2,0x0E,0xE0,0x22,0x40,0x20,0xBC,0x1B,0x80,0x22,0xD5,0x02,0xF1,0x0D,0x12,0x10, +0x00,0x09,0xD5,0x1A,0x00,0x03,0x00,0xAD,0x5A,0x08,0x03,0x17,0x00,0x03,0x00,0xAE, +0x5A,0x00,0x01,0x13,0xF1,0x02,0x44,0x02,0x15,0x0C,0x04,0x23,0x00,0x32,0x38,0x00, +0x04,0x00,0x8C,0x01,0x96,0x1F,0x40,0x10,0x30,0x08,0x44,0x0F,0x0F,0xFF,0xFE,0x16, +0xFE,0x0F,0x14,0x03,0x00,0x32,0xF0,0x0F,0x5A,0x08,0x01,0x0D,0x02,0x03,0x00,0x54, +0xE6,0x02,0xE9,0x08,0xF0,0x1A,0xB4,0x20,0x3C,0x0D,0xFC,0x38,0x88,0x01,0x90,0x01, +0xD5,0x02,0x84,0x00,0x3C,0x0F,0xFC,0x38,0xF0,0x02,0x50,0x63,0x00,0xCC,0x8C,0x01, +0xF0,0x82,0x5A,0x00,0x0B,0x04,0x48,0xFF,0xFB,0x7E,0xFC,0xD6,0xA7,0x60,0x5C,0xF2, +0x80,0x3C,0xE8,0x0F,0x95,0x6A,0x88,0xA3,0xAE,0x2B,0xA6,0x20,0x94,0x02,0x88,0x03, +0xAE,0x42,0xA6,0x20,0x38,0x21,0x82,0x09,0xA6,0x20,0x8C,0x01,0xAE,0x20,0xDD,0x9E, +0xFC,0x40,0x84,0xC0,0x44,0x70,0x7F,0xFF,0x80,0x86,0x38,0x91,0x9A,0x11,0x54,0xA3, +0x00,0xFF,0xE0,0xE9,0x8C,0xC1,0x40,0x74,0xBC,0x1A,0x54,0x93,0x00,0xFF,0x40,0x45, +0x3C,0x1A,0xE3,0x25,0xE9,0xF3,0xE0,0xE2,0xE8,0x06,0x95,0x22,0x88,0x64,0xAE,0x1B, +0xAE,0x5A,0xAC,0x98,0xFC,0xC0,0xFC,0x5A,0x81,0x22,0x00,0x2F,0x80,0xF8,0x12,0x1F, +0x80,0x19,0xF2,0x89,0x00,0x2F,0x80,0xFC,0xF1,0x40,0xF2,0x85,0x00,0x2F,0x81,0x04, +0x12,0x0F,0x80,0x18,0xF2,0x87,0x84,0x00,0x2E,0x26,0xCC,0x7E,0xAC,0x08,0xF2,0x81, +0xF1,0x05,0x2E,0x26,0xCC,0x7F,0xF3,0x86,0xB6,0x9F,0xF5,0x83,0xF2,0x88,0x5A,0x18, +0x01,0x04,0xF1,0x3D,0xAE,0x08,0x85,0x01,0x87,0x80,0x4E,0x82,0x01,0x42,0x50,0x04, +0x7F,0xFF,0xB0,0x4C,0xB0,0x8C,0x38,0x60,0x82,0x11,0x94,0x42,0x88,0x22,0x54,0x80, +0x00,0xFF,0x22,0x70,0x80,0x01,0xF0,0x01,0xFE,0x3C,0xF0,0x82,0xF1,0x02,0xB4,0x1F, +0x88,0x01,0x38,0x00,0x18,0x00,0x40,0x50,0x24,0x02,0x4C,0x54,0xFF,0xE8,0x40,0x14, +0xA4,0x05,0x81,0x46,0xF1,0x8A,0x40,0x50,0x24,0x02,0x4C,0x54,0x80,0x11,0x50,0x25, +0x7F,0xFF,0x96,0x10,0xF0,0x84,0x8E,0xC1,0xB4,0x1F,0xF1,0x02,0x97,0xB3,0x88,0x06, +0x38,0x00,0x04,0x00,0x40,0x14,0xA4,0x05,0xF1,0x8A,0xD5,0x73,0xF1,0x01,0xE1,0x41, +0xE8,0xEF,0xF1,0x0A,0xFE,0x0E,0xF1,0x02,0x54,0x00,0x00,0xE0,0x40,0x55,0x04,0x00, +0xF1,0x06,0xFE,0x0F,0xB4,0x3F,0x38,0x00,0x94,0x08,0xF0,0x40,0xF1,0x40,0xA4,0x00, +0x8C,0x01,0xAC,0x08,0xF0,0x05,0x5A,0x08,0x01,0x14,0xF0,0x03,0x80,0x27,0xF3,0x3C, +0x38,0x00,0x15,0x11,0xF4,0x3D,0xF0,0x84,0xF2,0x04,0x80,0x0A,0xF5,0x8B,0x49,0xFF, +0xFF,0x5F,0xF0,0x04,0xF5,0x0B,0xE0,0x1C,0x40,0x0E,0x3C,0x1B,0x83,0x80,0xF0,0x07, +0x5A,0x08,0x01,0x0E,0xF0,0x03,0x38,0x00,0x15,0x11,0x40,0xFE,0x00,0x07,0xE8,0x07, +0xF1,0x42,0x83,0x80,0x10,0xA0,0x80,0x00,0xF1,0x43,0xAF,0xC8,0x50,0x05,0x00,0x01, +0x40,0xA0,0x00,0x11,0xB4,0x1F,0xF1,0x02,0x88,0x0A,0x38,0x00,0x04,0x00,0xD5,0xAC, +0x4E,0x65,0x00,0x34,0xF1,0x0A,0xFE,0x0E,0xF1,0x02,0x54,0x00,0x00,0xE0,0x40,0xA3, +0x04,0x00,0xF1,0x06,0xFE,0x0F,0xB4,0x3F,0x38,0x00,0xA8,0x08,0xF0,0x40,0xF1,0x40, +0xA4,0x00,0x8C,0x01,0xAC,0x08,0xF0,0x05,0x5A,0x08,0x01,0x12,0xF0,0x03,0x80,0x27, +0xF3,0x3C,0x38,0x50,0x29,0x11,0xF4,0x3D,0x80,0x45,0x80,0x06,0xF5,0x8B,0x49,0xFF, +0xFF,0x1F,0xF5,0x0B,0xE0,0xBC,0x40,0x5E,0x3C,0x1B,0x83,0x85,0xF0,0x07,0x5A,0x00, +0x01,0x15,0x8E,0xC1,0xB4,0x1F,0x97,0xB3,0xF1,0x02,0x88,0x06,0x38,0x00,0x04,0x00, +0x40,0x50,0x24,0x02,0x4C,0x54,0xBF,0xCE,0xF0,0x09,0x8C,0xC1,0x97,0xB0,0x5A,0x00, +0x08,0x11,0x84,0x40,0x80,0x02,0xD5,0x15,0xF0,0x03,0x38,0x00,0x29,0x11,0x40,0xFE, +0x00,0x07,0xE8,0xE8,0xF1,0x42,0x83,0x80,0xAF,0x88,0xF1,0x43,0xAF,0xC8,0xD5,0xE2, +0xF0,0x01,0x9E,0x81,0xF0,0x04,0x40,0x20,0x08,0x07,0x84,0x00,0x40,0x00,0x18,0x06, +0xF1,0x08,0x8E,0x21,0xE0,0xE1,0xE8,0x40,0x97,0xF9,0x8A,0xC0,0x9C,0xF9,0xF0,0x01, +0x96,0xDB,0x42,0x51,0x80,0x24,0xF0,0x04,0x80,0x26,0x40,0xA0,0x08,0x00,0xB4,0x1F, +0x41,0xE0,0x14,0x00,0xE1,0x41,0xE9,0x2C,0x80,0x01,0x84,0x80,0xB4,0x3F,0x88,0x20, +0x38,0x10,0x94,0x00,0x40,0x14,0x84,0x02,0x4C,0x14,0x80,0x11,0x80,0x20,0xC4,0x19, +0xB1,0x0C,0x8E,0x01,0x38,0x02,0x22,0x09,0x40,0x04,0x08,0x08,0x88,0x04,0xAC,0xC1, +0x50,0x04,0x00,0x01,0x54,0x80,0x00,0xFF,0xD5,0x0C,0xF1,0x01,0xE0,0x01,0xE8,0xEF, +0x8C,0x01,0x96,0x03,0x84,0x81,0xD5,0xE3,0xE1,0x41,0xE9,0xDD,0x8C,0x21,0x96,0x4B, +0x38,0x0F,0x04,0x00,0x40,0x04,0x80,0x02,0x4C,0x04,0xFF,0xF8,0xD5,0xD4,0xE4,0x62, +0xE8,0x07,0x48,0xFF,0xFE,0xFC,0xE4,0xE2,0x4E,0xF3,0xFE,0xF9,0x8A,0xC0,0xF0,0x01, +0x8E,0xE1,0x97,0xFB,0x42,0x33,0x80,0x24,0xF0,0x04,0x88,0x02,0x80,0x40,0xB4,0x1F, +0x99,0x03,0xE0,0x46,0x4E,0xF3,0xFE,0xEB,0x80,0x06,0x84,0x20,0xB4,0xBF,0x88,0xA0, +0x38,0x52,0x8C,0x00,0x40,0x54,0x94,0x02,0x4C,0x54,0x80,0x11,0x80,0xC0,0xC1,0x19, +0xB0,0x4C,0x8E,0x01,0x38,0x00,0xA2,0x09,0x40,0x04,0x08,0x08,0x88,0x01,0xAD,0xC1, +0x50,0x04,0x00,0x01,0x54,0x80,0x00,0xFF,0xD5,0x0C,0xF5,0x01,0xE0,0x05,0xE8,0xEF, +0x8C,0x01,0x96,0x03,0x84,0x21,0xD5,0xE3,0xE0,0x46,0xE9,0xDC,0x8C,0xC1,0x97,0xB3, +0x38,0x52,0x18,0x00,0x40,0x54,0x94,0x02,0x4C,0x54,0xFF,0xF8,0xD5,0xD3,0x80,0x1C, +0xFC,0xDA,0x84,0x20,0x44,0x22,0x15,0x18,0x9D,0x02,0xE6,0x22,0x80,0x60,0x40,0x32, +0x3C,0x1A,0x38,0x31,0x04,0x08,0x8C,0x21,0x5A,0x18,0x0B,0xF9,0xDD,0x9E,0x44,0x12, +0x15,0x18,0x38,0x00,0x80,0x00,0xDD,0x9E,0x84,0x20,0x44,0x22,0x15,0x0C,0x38,0x01, +0x04,0x08,0x8C,0x21,0x5A,0x18,0x0B,0xFD,0xDD,0x9E,0x44,0x12,0x15,0x0C,0x38,0x00, +0x80,0x00,0xDD,0x9E,0x44,0x30,0x00,0xCC,0x44,0x22,0x40,0x04,0x42,0x20,0x0C,0x73, +0x10,0x11,0x00,0xB5,0xDD,0x9E,0x44,0x22,0x40,0xD4,0x44,0x30,0x00,0xCC,0x42,0x20, +0x0C,0x73,0x44,0x0F,0x0F,0xFF,0xC1,0x0B,0xB4,0x62,0x40,0x11,0xB0,0x09,0x8C,0x21, +0x96,0x5F,0x40,0x10,0xB0,0x08,0xFE,0x1E,0xFE,0x0F,0xD5,0x03,0xB4,0x22,0xFE,0x0E, +0xB6,0x02,0xDD,0x9E,0x44,0x20,0x00,0xCC,0x44,0x12,0x40,0xD4,0x42,0x10,0x08,0x73, +0xB4,0x01,0x92,0x0C,0x96,0x1F,0xDD,0x9E,0x84,0x00,0x3C,0x0B,0xF8,0x72,0xDD,0x9E, +0x3C,0x23,0xF8,0x72,0x84,0x20,0x40,0x31,0x04,0x0E,0x96,0xC6,0x96,0x08,0xC3,0x07, +0x8C,0x21,0x5A,0x18,0x0B,0xFA,0x44,0x00,0x00,0xFF,0xDD,0x9E,0x84,0x61,0x40,0x11, +0x84,0x0C,0xFE,0x8F,0x3C,0x2B,0xF8,0x72,0xDD,0x9E,0x84,0x21,0x40,0x00,0x80,0x0C, +0x3C,0x13,0xF8,0x72,0xFE,0x03,0xFE,0x0E,0x3C,0x0B,0xF8,0x72,0xDD,0x9E,0xFC,0x00, +0x49,0x00,0x2A,0xAC,0xC8,0x05,0x84,0x01,0x3E,0x07,0xDE,0xCC,0xFC,0x80,0x84,0x02, +0x3E,0x07,0xDE,0xCC,0x49,0x00,0x15,0x36,0xFC,0x80,0xFC,0x00,0x49,0x00,0x14,0xD3, +0x84,0x01,0x49,0x00,0x0C,0xD6,0x49,0x00,0x0C,0xC3,0xFC,0x80,0xFC,0x07,0x84,0x00, +0x3E,0x07,0xF0,0xEE,0x3C,0x03,0xF6,0x93,0x96,0x0E,0x96,0x01,0xC0,0x02,0x84,0x01, +0x3E,0x07,0xF0,0xE6,0x84,0x06,0x49,0x00,0x34,0x2C,0x5A,0x08,0x01,0x08,0x84,0x06, +0x84,0x20,0x49,0x00,0x34,0x0B,0x49,0x00,0x1A,0xCE,0x2E,0x07,0xDE,0xCC,0x8E,0x01, +0xE6,0x05,0x4E,0xF2,0x01,0x08,0x44,0xF0,0x7E,0x14,0x38,0x07,0x81,0x01,0x40,0xF0, +0x3C,0x00,0xDD,0x0F,0x0A,0x00,0xD8,0x00,0xFE,0x01,0x36,0x01,0x86,0x01,0x49,0x00, +0x17,0xFB,0x5A,0x08,0x01,0x18,0x84,0x01,0x49,0x00,0x2A,0x77,0x84,0x06,0x49,0x00, +0x13,0xC0,0x84,0x00,0x49,0x00,0x2B,0x9F,0x84,0x01,0x49,0x00,0x2A,0x62,0x84,0x01, +0x49,0x00,0x28,0xE3,0x49,0x00,0x1B,0x0C,0xC8,0x0A,0x84,0x01,0x49,0x00,0x28,0x28, +0xD5,0x06,0x49,0x00,0x14,0x9A,0x5A,0x08,0x01,0x2D,0xD5,0xE6,0x84,0x01,0x49,0x00, +0x14,0x86,0x84,0x01,0x49,0x00,0x15,0x5D,0x49,0x00,0x14,0x96,0x49,0x00,0x2A,0x4C, +0xB6,0x1F,0x49,0x00,0x2A,0x4C,0xF0,0x81,0xB4,0x3F,0x84,0x01,0xF2,0x01,0x49,0x00, +0x2A,0xC2,0x2E,0x67,0xDE,0xCC,0x49,0x00,0x14,0x6E,0x8E,0xC3,0xE6,0xC2,0xE8,0x06, +0x49,0x00,0x14,0xC8,0x84,0x00,0x3C,0x0B,0xF8,0x75,0x84,0x02,0x3E,0x07,0xDE,0xCC, +0x84,0x00,0x3C,0x0B,0xF8,0x74,0x84,0x01,0x49,0x00,0x2B,0x65,0x48,0x00,0x00,0xB3, +0x49,0x00,0x2A,0x30,0x5A,0x00,0x01,0x04,0x48,0x00,0x00,0xAD,0x2E,0x07,0xF0,0xE6, +0x5A,0x08,0x01,0x06,0x3C,0x03,0xF8,0x74,0x8C,0x01,0xD5,0x02,0x84,0x00,0x3C,0x0B, +0xF8,0x74,0x3C,0x03,0xF8,0x74,0xE6,0x0A,0x4E,0xF3,0x00,0x9D,0x84,0x00,0x49,0x00, +0x2A,0x1C,0x84,0x00,0x3C,0x0B,0xF8,0x74,0x48,0x00,0x00,0x95,0x49,0x00,0x17,0x94, +0x5A,0x08,0x01,0x0C,0x84,0x00,0x3C,0x0B,0xF8,0x75,0x84,0x01,0x49,0x00,0x14,0xFE, +0x84,0x04,0x3E,0x07,0xDE,0xCC,0xD5,0x48,0x49,0x00,0x14,0x3F,0x5A,0x00,0x01,0xF4, +0x2E,0x07,0xF0,0xE6,0x5A,0x08,0x01,0x0D,0x3C,0x03,0xF8,0x75,0x5C,0xF0,0x00,0x64, +0xE8,0x04,0x8C,0x01,0x96,0x01,0xD5,0x05,0x44,0x00,0x00,0x64,0xD5,0x02,0x84,0x00, +0x3C,0x0B,0xF8,0x75,0x3C,0x03,0xF8,0x75,0x5C,0xF0,0x00,0x64,0xE9,0x6B,0x49,0x00, +0x14,0x37,0x49,0x00,0x14,0x10,0x84,0x05,0xD5,0x63,0x84,0x01,0x49,0x00,0x18,0xDB, +0x80,0xC0,0x5A,0x08,0x01,0x22,0x49,0x00,0x1A,0x22,0x49,0x00,0x1A,0x0E,0x49,0x00, +0x09,0xCC,0x49,0x00,0x14,0x22,0x84,0x00,0x49,0x00,0x27,0xB0,0x80,0x06,0x49,0x00, +0x14,0xD8,0x2E,0x67,0xDE,0xCC,0x49,0x00,0x13,0xF6,0x8E,0xC3,0xE6,0xC2,0xE8,0x06, +0x49,0x00,0x14,0x50,0x84,0x00,0x3C,0x0B,0xF8,0x75,0x84,0x02,0x3E,0x07,0xDE,0xCC, +0x3E,0x07,0xF0,0xEE,0xD5,0x3F,0x84,0x01,0xD5,0xFC,0x49,0x00,0x18,0x90,0x5A,0x08, +0x01,0x29,0x84,0x00,0x49,0x00,0x29,0xAD,0x84,0x00,0x49,0x00,0x28,0x2E,0x49,0x00, +0x1A,0x57,0xC8,0x03,0x49,0x00,0x27,0x74,0x84,0x00,0x49,0x00,0x13,0xD8,0x84,0x00, +0x49,0x00,0x14,0xAF,0x49,0x00,0x13,0xE8,0x49,0x00,0x29,0x9E,0xB6,0x1F,0x49,0x00, +0x29,0x9E,0xF0,0x81,0xB4,0x3F,0x84,0x00,0xF2,0x01,0x49,0x00,0x2A,0x14,0x49,0x00, +0x13,0xC2,0x84,0x00,0x3C,0x0B,0xF8,0x76,0x3C,0x0B,0xF8,0x74,0x84,0x01,0xD5,0x10, +0x49,0x00,0x14,0x0B,0x2E,0x67,0xDE,0xCC,0x49,0x00,0x13,0xB5,0x8E,0xC3,0xE6,0xC2, +0xE8,0x06,0x49,0x00,0x14,0x0F,0x84,0x00,0x3C,0x0B,0xF8,0x75,0x84,0x02,0x3E,0x07, +0xDE,0xCC,0x2E,0x07,0xDE,0xCC,0x8E,0x03,0xE6,0x02,0x3C,0x0D,0xF7,0xE5,0xE8,0x0B, +0xF0,0x82,0xF0,0x83,0xF0,0x84,0xF0,0x85,0xF0,0x86,0xF0,0x87,0xF0,0x88,0xF0,0x89, +0xF0,0x8A,0xD5,0x1C,0x44,0x12,0x00,0x01,0xF1,0x82,0x44,0x11,0x00,0x02,0xF1,0x83, +0x44,0x10,0x80,0x04,0xF1,0x84,0x44,0x10,0x40,0x08,0xF1,0x85,0x44,0x10,0x20,0x10, +0xF1,0x86,0x44,0x10,0x10,0x20,0xF1,0x87,0x44,0x10,0x08,0x40,0xF1,0x88,0x44,0x10, +0x04,0x80,0xF1,0x89,0x44,0x10,0x03,0x00,0xF1,0x8A,0xF0,0x8B,0xF0,0x8C,0xF0,0x8D, +0xB0,0x02,0x49,0xFF,0xC7,0x1B,0x2E,0x07,0xF0,0xEE,0xFC,0x87,0xDD,0x9E,0xFC,0x21, +0xFA,0x04,0x10,0x0F,0x80,0x07,0x50,0x0F,0x80,0x07,0x49,0xFF,0xD3,0x71,0x80,0xC0, +0x5A,0x08,0x01,0x27,0x84,0x00,0x49,0x00,0x1F,0x03,0x00,0x0F,0x80,0x07,0x49,0x00, +0x01,0xF3,0x84,0x03,0x44,0x10,0x01,0xF4,0x49,0x00,0x32,0xA2,0x84,0x03,0x80,0x26, +0x49,0x00,0x32,0xAC,0x49,0x00,0x1E,0xF0,0x80,0xC0,0xC8,0x0E,0x84,0x03,0x49,0x00, +0x32,0xC0,0x80,0xE0,0x5A,0x08,0x01,0xF8,0x84,0x03,0x80,0x26,0x49,0x00,0x32,0x9E, +0x80,0x07,0x49,0x00,0x1E,0xE5,0x84,0x03,0x84,0x20,0x49,0x00,0x32,0x97,0xFC,0xA1, +0xFC,0x00,0x3C,0x63,0xF6,0x93,0x97,0x86,0xCE,0x0A,0x49,0x00,0x19,0xB9,0x80,0x20, +0x80,0x06,0x5A,0x10,0x01,0x03,0x80,0x26,0x49,0x00,0x0E,0x59,0xFC,0x80,0x44,0x24, +0x10,0x14,0xA6,0x10,0x44,0x14,0x10,0x14,0x96,0x06,0xC0,0xFC,0xA6,0x08,0x54,0x00, +0x00,0xFE,0xAE,0x08,0xDD,0x9E,0xFC,0x20,0x80,0xC1,0x44,0x14,0x10,0x14,0x80,0xE0, +0xA6,0x08,0x54,0x00,0x00,0xFE,0xAE,0x08,0x84,0x00,0x49,0x00,0x08,0xA6,0x49,0x00, +0x08,0x70,0x40,0x00,0x00,0x09,0x88,0xE6,0x4C,0x63,0x80,0x07,0x08,0x03,0x00,0x01, +0x49,0x00,0x08,0x75,0xD5,0xFA,0x84,0x01,0x49,0x00,0x08,0x97,0xFC,0xA0,0xFC,0x01, +0x84,0x06,0x10,0x0F,0x80,0x04,0xB0,0x41,0x84,0x01,0x49,0xFF,0xFF,0xDE,0xFC,0x81, +0xFC,0x00,0x44,0x14,0x10,0x14,0xA6,0x08,0x54,0x00,0x00,0xFE,0xAE,0x08,0x84,0x00, +0x49,0x00,0x08,0x83,0x49,0x00,0x08,0x4D,0x40,0x00,0x00,0x09,0x84,0x05,0x49,0x00, +0x08,0x56,0x44,0x00,0x00,0xFF,0x49,0x00,0x08,0x52,0x44,0x04,0x10,0x10,0xA7,0x80, +0x84,0x01,0x97,0xB0,0x49,0x00,0x08,0x71,0x80,0x06,0xFC,0x80,0xFC,0x40,0x44,0x14, +0x10,0x14,0x80,0xC0,0xA6,0x08,0x54,0x00,0x00,0xFE,0xAE,0x08,0x44,0x04,0x10,0x00, +0x84,0x20,0xAE,0x40,0x44,0x04,0x10,0x04,0x44,0x10,0x00,0x55,0xAE,0x40,0x44,0x1F, +0xFF,0xAA,0xAE,0x41,0x44,0x10,0x00,0x66,0xAE,0x42,0x40,0x00,0x00,0x09,0x44,0x74, +0x10,0x08,0x44,0x00,0x00,0x9F,0xB6,0x07,0x49,0xFF,0xFF,0x93,0x50,0x93,0x00,0x03, +0x44,0x80,0x00,0xFF,0x44,0xA4,0x10,0x10,0xB7,0x07,0x49,0xFF,0xFF,0x8A,0x00,0x05, +0x00,0x00,0x18,0x03,0x00,0x01,0x4C,0x64,0xFF,0xF9,0x44,0x04,0x10,0x00,0x84,0x21, +0xAE,0x40,0xFC,0xC0,0xFC,0x00,0x80,0xC0,0x49,0xFF,0xFF,0xA3,0x84,0x00,0x49,0x00, +0x08,0x34,0x49,0x00,0x07,0xFE,0xFA,0x10,0x49,0x00,0x08,0x09,0x5C,0xF3,0x00,0x40, +0x80,0x26,0xE9,0x03,0x44,0x10,0x00,0x3F,0x96,0x08,0x40,0x00,0x30,0x08,0x49,0x00, +0x08,0x0C,0x84,0x01,0x49,0x00,0x08,0x21,0x40,0x00,0x00,0x09,0x40,0x00,0x00,0x09, +0x40,0x00,0x00,0x09,0x49,0xFF,0xFF,0x8E,0x96,0x06,0xC0,0x05,0x84,0x0A,0x49,0xFF, +0xCD,0x79,0xD5,0xF9,0xFC,0x80,0xFC,0x43,0x83,0x80,0x84,0x00,0xF0,0x85,0xB0,0x05, +0x81,0x01,0x81,0x42,0x49,0xFF,0xFF,0x9C,0xF0,0x05,0x44,0x5E,0xFF,0xFF,0xFF,0x46, +0x46,0x00,0x03,0x02,0x50,0x00,0x05,0xC2,0xD0,0x04,0x44,0x90,0x01,0x00,0xD5,0x02, +0xFB,0x30,0x84,0xC0,0x44,0x74,0x10,0x08,0x49,0xFF,0xFF,0x63,0x44,0x04,0x10,0x00, +0x84,0x20,0xAE,0x40,0x44,0x04,0x10,0x04,0x44,0x10,0x00,0x55,0xAE,0x40,0x44,0x1F, +0xFF,0xAA,0xAE,0x41,0x44,0x10,0x00,0x66,0xAE,0x42,0x40,0x03,0x20,0x00,0x40,0x20, +0x40,0x09,0x40,0x10,0x20,0x09,0x84,0x62,0x96,0x00,0x96,0x48,0x96,0x97,0xF0,0x81, +0xF1,0x82,0xF2,0x83,0xB6,0x67,0x49,0xFF,0xFF,0x1C,0xF2,0x03,0xB6,0x47,0x49,0xFF, +0xFF,0x18,0xF1,0x02,0xB6,0x27,0x49,0xFF,0xFF,0x14,0xF0,0x01,0xB6,0x07,0x49,0xFF, +0xFF,0x10,0x40,0x00,0x00,0x09,0x38,0x0E,0x18,0x00,0x8C,0xC1,0xB6,0x07,0x97,0xB1, +0x49,0xFF,0xFF,0x07,0x40,0x03,0x20,0x00,0x40,0x00,0x24,0x37,0xC1,0x03,0x4C,0x65, +0x7F,0xF4,0x44,0x04,0x10,0x00,0x84,0x21,0xAE,0x40,0x40,0x00,0x00,0x09,0x40,0x00, +0x00,0x09,0x49,0xFF,0xFF,0x27,0x96,0x06,0xC0,0x05,0x84,0x02,0x49,0xFF,0xCD,0x12, +0xD5,0xF9,0x4C,0x65,0x7F,0xB3,0xFC,0xC3,0x40,0x00,0x60,0x08,0x46,0x20,0x4C,0x11, +0xFE,0x0D,0x50,0x21,0x0D,0xB7,0x84,0x28,0x94,0xC1,0x4E,0x04,0x00,0x05,0x40,0x01, +0x88,0x03,0xD5,0x02,0x80,0x03,0x8E,0x21,0x96,0x48,0xC9,0xF7,0xDD,0x9E,0xFC,0x41, +0x84,0x1F,0xF0,0x81,0x84,0xC0,0x44,0x92,0x10,0x00,0x44,0x70,0x0F,0xFC,0x5C,0xF3, +0x01,0x00,0xE8,0x04,0x38,0x04,0x98,0x00,0xD5,0x03,0x44,0x00,0x00,0xFF,0xF1,0x01, +0x49,0xFF,0xFF,0xDC,0x8C,0xC1,0xF0,0x81,0x4C,0x63,0xFF,0xF3,0xF0,0x01,0xFC,0xC1, +0x2E,0x07,0xDE,0xCD,0x5A,0x00,0x01,0x14,0xFC,0x00,0x2E,0x07,0xDE,0xCD,0x5A,0x08, +0x02,0x05,0x84,0x03,0x3E,0x07,0xDE,0xCD,0x2E,0x07,0xDE,0xCD,0x5A,0x08,0x04,0x05, +0x84,0x01,0x3E,0x07,0xDE,0xCD,0x49,0xFF,0xE5,0x2E,0xFC,0x80,0xDD,0x9E,0xFC,0x00, +0x49,0x00,0x1C,0xE5,0x80,0xC0,0xC8,0x06,0x49,0xFF,0xE8,0x60,0x40,0x03,0x00,0x06, +0xFC,0x80,0x84,0x01,0xFC,0x80,0xFC,0x42,0x3A,0x0F,0x84,0x20,0x80,0xE0,0x2E,0x07, +0xEB,0xA9,0x80,0xC1,0xC0,0x02,0x9B,0xD7,0x2E,0x07,0xEB,0xAA,0xC0,0x02,0x9B,0x9E, +0x2E,0x07,0xEB,0xAB,0xC0,0x0B,0x40,0x93,0x00,0x13,0x96,0x11,0x96,0x61,0x80,0xC7, +0x80,0x85,0x80,0x43,0x80,0xE9,0x80,0xA1,0x80,0x60,0x42,0x03,0x90,0x24,0x42,0x13, +0x14,0x24,0x40,0x00,0x08,0x17,0x40,0x10,0x8C,0x37,0xFC,0xC2,0xFC,0x42,0x44,0x62, +0x15,0x58,0x44,0x92,0x40,0x0C,0x84,0xEA,0x3C,0xA3,0x66,0x3C,0x84,0x00,0x10,0x03, +0x00,0x08,0x00,0x04,0x80,0xAD,0x8E,0x02,0xE6,0x02,0xE8,0x26,0xB0,0x41,0x84,0x40, +0x80,0x09,0x49,0xFF,0xCE,0x62,0xF0,0x01,0x22,0x20,0x00,0x08,0xAC,0xB2,0x22,0x10, +0x00,0x09,0xB4,0x00,0xF2,0x82,0xB6,0x06,0x84,0x01,0x10,0x03,0x00,0x08,0xB0,0x02, +0xF1,0x83,0xAC,0x73,0x80,0x4A,0x3A,0x00,0x04,0x00,0x3C,0x33,0x66,0x3D,0x3C,0x43, +0x66,0x40,0x3C,0x53,0x66,0x41,0x49,0xFF,0xFF,0xB0,0xF0,0x82,0xF0,0x02,0xF1,0x83, +0xAC,0x32,0xF0,0x03,0xAC,0x33,0x8E,0xE1,0x97,0xF9,0x8C,0xCC,0x50,0x94,0x80,0xCC, +0xCF,0xCE,0xFC,0xC2,0xFC,0x00,0x2E,0x17,0xF2,0xE6,0xC1,0x08,0x49,0x00,0x22,0x14, +0x49,0x00,0x22,0x1C,0x49,0x00,0x1C,0x50,0xD5,0x07,0x49,0x00,0x22,0x0D,0x49,0x00, +0x22,0x2D,0x49,0x00,0x1C,0x4E,0x49,0x00,0x1C,0x52,0xFC,0x80,0xFC,0x00,0x84,0x00, +0x49,0x00,0x1C,0x6B,0x84,0x00,0x44,0x12,0x15,0x58,0x49,0x00,0x1C,0x87,0x84,0x00, +0x84,0x27,0x49,0x00,0x1C,0xE5,0xFC,0x80,0xFC,0x00,0xC0,0x0B,0x5A,0x08,0x01,0x05, +0x49,0x00,0x25,0x89,0xD5,0x06,0x84,0xC0,0x5A,0x08,0x02,0x05,0x49,0x00,0x25,0x86, +0x80,0xC0,0x84,0x02,0x49,0x00,0x1C,0x51,0x84,0x02,0x44,0x12,0x15,0x58,0x49,0x00, +0x1C,0x6D,0xC6,0x04,0x80,0x06,0x49,0x00,0x1C,0xA6,0x49,0x00,0x25,0x83,0x49,0x00, +0x1C,0xB4,0x84,0x02,0x84,0x27,0x49,0x00,0x1C,0xC3,0xFC,0x80,0xFC,0x01,0xF0,0x81, +0x49,0x00,0x1C,0x35,0x80,0xC0,0xC8,0x05,0xF0,0x01,0x49,0xFF,0xFF,0xC9,0xD5,0x08, +0x5A,0x00,0x01,0x0C,0x49,0x00,0x1C,0x2E,0xF1,0x01,0x49,0xFF,0xFF,0xCF,0x80,0x06, +0x49,0x00,0x1B,0xF9,0x49,0x00,0x1C,0x0B,0xFC,0x81,0xFC,0x00,0x49,0x00,0x17,0x90, +0x5A,0x08,0x01,0x06,0x84,0x00,0x44,0x10,0x13,0x88,0xD5,0x04,0x84,0x00,0x44,0x10, +0x75,0x30,0x49,0x00,0x30,0x4D,0x84,0x00,0x84,0x21,0x49,0x00,0x30,0x57,0x84,0x00, +0x49,0x00,0x17,0x84,0xFC,0x80,0xFC,0x00,0x49,0x00,0x2B,0xA5,0x49,0x00,0x2B,0xBC, +0x49,0x00,0x2F,0xC8,0x49,0x00,0x30,0x07,0x49,0x00,0x2C,0xCC,0x49,0x00,0x2C,0xE1, +0x49,0x00,0x2D,0x14,0x49,0x00,0x10,0x18,0x49,0x00,0x2D,0xE2,0x49,0x00,0x25,0xCB, +0x49,0x00,0x1B,0x8F,0x84,0x00,0x3E,0x07,0xDE,0xCF,0xFC,0x80,0x2E,0x07,0xF1,0x72, +0xDD,0x9E,0xFC,0x00,0x49,0x00,0x12,0x4E,0x49,0xFF,0xFC,0x12,0x3E,0x07,0xF1,0x71, +0x49,0xFF,0xFD,0x66,0x49,0xFF,0xFC,0x03,0xFC,0x80,0xFC,0x00,0x84,0x00,0x3E,0x07, +0xF1,0x6F,0x49,0x00,0x17,0x4D,0xC8,0x0A,0x49,0xFF,0xE4,0x6C,0x2E,0x07,0xEE,0xE0, +0xC0,0x0E,0x84,0x01,0x3E,0x07,0xF1,0x6F,0xFC,0x80,0x49,0xFF,0xFD,0x52,0x49,0xFF, +0xFD,0x81,0x3C,0xF7,0xF8,0x5F,0x5E,0xF7,0x81,0x5E,0xE8,0xF4,0xFC,0x80,0xFC,0x00, +0x49,0xFF,0xFE,0xDF,0x80,0xC0,0x5A,0x08,0x01,0x06,0x49,0xFF,0xFF,0x09,0x3E,0x67, +0xF1,0x70,0xFC,0x80,0x2E,0x07,0xF1,0x71,0x5A,0x08,0x01,0x06,0x84,0x22,0x3E,0x17, +0xF1,0x76,0xDD,0x9E,0x5A,0x08,0x02,0x07,0x84,0x01,0x3E,0x07,0xF1,0x76,0x84,0x01, +0xDD,0x9E,0x84,0x00,0xDD,0x9E,0xFC,0x00,0x49,0x00,0x17,0x1A,0xC8,0x03,0x49,0x00, +0x17,0x44,0x49,0x00,0x10,0x04,0x49,0x00,0x0D,0x32,0x49,0x00,0x27,0x06,0x49,0x00, +0x26,0x5D,0x49,0x00,0x10,0x94,0x49,0x00,0x24,0xFC,0x49,0x00,0x2A,0xDE,0x49,0x00, +0x26,0x55,0x49,0x00,0x11,0x66,0x49,0x00,0x17,0x03,0xC8,0x03,0x49,0x00,0x27,0xFB, +0x49,0xFF,0xFB,0x9F,0x49,0x00,0x29,0xF0,0x49,0xFF,0xE3,0x34,0x49,0xFF,0xFF,0x67, +0x49,0x00,0x04,0x6C,0x49,0x00,0x16,0xF4,0x80,0xC0,0x5A,0x08,0x01,0x0A,0x44,0x00, +0x02,0x58,0x80,0x26,0x49,0xFF,0xCA,0x6A,0x80,0x06,0x49,0xFF,0xCA,0x71,0x84,0x01, +0x3E,0x07,0xF1,0x76,0xFC,0x80,0xFC,0x00,0x84,0x00,0x49,0x00,0x27,0x6C,0x84,0x00, +0x49,0x00,0x04,0x55,0x49,0x00,0x24,0xC3,0x49,0x00,0x20,0xF5,0xFC,0x80,0xFC,0x00, +0x49,0x00,0x25,0xD8,0x49,0x00,0x25,0xD7,0xC0,0x08,0x49,0x00,0x24,0xB8,0x49,0x00, +0x24,0x81,0x84,0x02,0x3E,0x07,0xF1,0x76,0xFC,0x80,0xFC,0x01,0x2E,0x07,0xF1,0x73, +0x5A,0x00,0x01,0x2F,0xC0,0x05,0x5A,0x00,0x02,0x38,0x48,0x00,0x00,0x90,0x49,0x00, +0x24,0xB0,0x84,0x00,0x49,0x00,0x27,0x47,0x49,0x00,0x16,0xBA,0x80,0xC0,0xC8,0x1C, +0x49,0x00,0x26,0x04,0xC0,0x15,0x80,0x06,0x49,0x00,0x11,0x13,0x80,0x06,0x49,0x00, +0x24,0x84,0x80,0x06,0x49,0x00,0x23,0xCC,0x49,0x00,0x25,0xFE,0xB6,0x1F,0x49,0x00, +0x25,0xFE,0xF0,0x81,0xB4,0x3F,0x80,0x06,0xF2,0x01,0x49,0x00,0x26,0x74,0x84,0x02, +0x84,0x20,0x49,0x00,0x11,0xF3,0x84,0x01,0x49,0x00,0x27,0x25,0xD5,0x64,0x49,0x00, +0x25,0x99,0x49,0x00,0x25,0x98,0xC0,0x62,0x49,0x00,0x24,0x79,0x84,0x00,0x49,0x00, +0x0C,0x14,0x84,0x02,0xD5,0x59,0x49,0x00,0x25,0xAF,0xC0,0x58,0x2E,0x07,0xF1,0x77, +0xC8,0x0C,0x44,0x14,0x50,0x28,0xB4,0x01,0x92,0x03,0x96,0x06,0x5A,0x00,0x01,0x06, +0xB4,0x01,0x92,0x01,0x96,0x06,0xC8,0x05,0x2E,0x07,0xF1,0x72,0x5A,0x00,0x02,0x47, +0x3C,0x0D,0xFE,0x44,0x5A,0x00,0x01,0x43,0x49,0x00,0x24,0x59,0x84,0x01,0x49,0x00, +0x0B,0xF4,0x5A,0x08,0x01,0x37,0x49,0x00,0x16,0x6B,0xC8,0x2B,0x44,0x02,0x20,0xE8, +0x49,0x00,0x0B,0x9A,0x49,0xFF,0xE3,0x76,0x49,0x00,0x25,0xB0,0x80,0xC0,0xC8,0x1D, +0x49,0x00,0x10,0xBF,0x80,0x06,0x49,0x00,0x24,0x30,0x80,0x06,0x49,0x00,0x23,0x78, +0x49,0x00,0x25,0xAA,0xB6,0x1F,0x49,0x00,0x25,0xAA,0xF0,0x81,0xB4,0x3F,0x80,0x06, +0xF2,0x01,0x49,0x00,0x26,0x20,0x46,0x00,0x42,0x10,0x50,0x00,0x08,0x20,0x80,0x20, +0x80,0x46,0x49,0xFF,0xC2,0x82,0xD5,0x05,0x49,0x00,0x25,0x90,0x49,0x00,0x12,0x0C, +0x49,0x00,0x12,0x59,0x84,0x00,0x49,0x00,0x23,0x71,0x84,0x01,0x3E,0x07,0xF1,0x76, +0x49,0x00,0x24,0x31,0x84,0x01,0x3E,0x07,0xF1,0x73,0xFC,0x81,0xFC,0x00,0x84,0x01, +0x84,0x20,0x49,0x00,0x2F,0x03,0x84,0x01,0x49,0x00,0x1B,0x4A,0x84,0x00,0x3E,0x07, +0xF1,0x70,0xFC,0x80,0xFC,0x01,0x2E,0x17,0xF1,0x6A,0x5A,0x10,0x01,0x1E,0xC1,0x04, +0x5A,0x10,0x02,0x42,0xD5,0x7A,0x3C,0x07,0xF6,0x56,0xE4,0x1F,0xE8,0x0E,0x3C,0xF7, +0xF6,0x52,0x5E,0xF7,0xFF,0xE2,0xE9,0x09,0x3C,0xF7,0xF6,0x6C,0x3C,0x07,0xF6,0x6B, +0x8A,0x0F,0xE4,0x1F,0x84,0x02,0xE9,0x02,0x84,0x01,0x3E,0x07,0xF1,0x6A,0x84,0x00, +0x3E,0x07,0xF1,0x69,0xD5,0x62,0x3C,0x17,0xF6,0x56,0xE4,0x3F,0xE8,0x0D,0x3C,0xF7, +0xF6,0x52,0x5E,0xF7,0xFF,0xE2,0xE9,0x08,0x3C,0xF7,0xF6,0x6C,0x3C,0x07,0xF6,0x6B, +0x8A,0x0F,0xE4,0x1F,0xE9,0x13,0x2E,0x07,0xF1,0x69,0x8C,0x01,0x96,0x00,0xE6,0x03, +0x3E,0x07,0xF1,0x69,0xE8,0x04,0x5E,0xF0,0x80,0x65,0xE9,0x47,0x84,0x00,0x3E,0x07, +0xF1,0x6A,0x49,0x00,0x0A,0x4D,0x84,0x04,0xD5,0x3E,0x84,0x00,0x3E,0x07,0xF1,0x69, +0x84,0x02,0xD5,0x19,0x3C,0x07,0xF6,0x56,0xE4,0x1F,0xE8,0x0E,0x3C,0xF7,0xF6,0x52, +0x5E,0xF7,0xFF,0xE2,0xE9,0x0C,0x3C,0xF7,0xF6,0x6C,0x3C,0x07,0xF6,0x6B,0x8A,0x0F, +0xE4,0x1F,0xE8,0x05,0xD5,0x0B,0x5E,0xF0,0x00,0x65,0xE8,0xE1,0x84,0x00,0x3E,0x07, +0xF1,0x69,0x84,0x01,0x3E,0x07,0xF1,0x6A,0xD5,0x20,0x2E,0x07,0xF1,0x69,0x8C,0x01, +0x96,0x00,0xE6,0x03,0x3E,0x07,0xF1,0x69,0xE9,0x18,0x84,0x00,0xF1,0x81,0x3E,0x07, +0xF1,0x6A,0x49,0xFF,0xFF,0x85,0x49,0x00,0x0A,0x1B,0x49,0x00,0x2C,0x04,0xE6,0x03, +0xF1,0x01,0xE9,0x04,0x44,0x00,0x00,0x5A,0xD5,0x03,0x44,0x00,0x45,0xF6,0x49,0x00, +0x2C,0x2A,0x84,0x01,0x3E,0x07,0xF1,0x74,0xFC,0x81,0xFC,0x20,0x49,0x00,0x1A,0x2F, +0x5A,0x08,0x02,0x07,0x49,0xFF,0xFE,0x0B,0x84,0x00,0x3E,0x07,0xF1,0x74,0x2E,0x07, +0xF1,0x74,0xC8,0x3A,0x2E,0x17,0xF1,0x6F,0x5A,0x18,0x01,0x04,0x49,0x00,0x2E,0x5D, +0x84,0x00,0x49,0x00,0x2E,0x7E,0x5A,0x00,0x01,0x04,0x84,0xE0,0xD5,0x2E,0x49,0x00, +0x1A,0x16,0x5A,0x00,0x02,0xFC,0x49,0x00,0x15,0x83,0x5A,0x00,0x01,0x04,0x48,0x00, +0x00,0xDC,0x2E,0x07,0xF1,0x6B,0x8C,0x01,0x3E,0x07,0xF1,0x6B,0x49,0x00,0x15,0x78, +0x80,0xC0,0xC8,0x0E,0x84,0x01,0x80,0x26,0x49,0x00,0x2E,0x48,0x80,0x06,0x44,0x10, +0x75,0x30,0x49,0x00,0x2E,0x35,0x80,0x06,0x84,0x21,0x49,0x00,0x2E,0x3F,0x2E,0x07, +0xF1,0x6B,0x4E,0x02,0x00,0xD0,0x49,0xFF,0xFF,0x33,0x84,0x01,0x3E,0x07,0xF1,0x74, +0x84,0x00,0x3E,0x07,0xF1,0x6B,0x84,0xE1,0x2E,0x07,0xF1,0x74,0x5A,0x08,0x03,0x04, +0x49,0xFF,0xFF,0x32,0x2E,0x67,0xF1,0x74,0x5A,0x68,0x01,0x0C,0x80,0x06,0x49,0x00, +0x15,0x55,0x49,0x00,0x29,0x17,0x84,0x02,0x3E,0x07,0xF1,0x74,0x3E,0x67,0xF1,0x6D, +0x2E,0x07,0xF1,0x74,0x5A,0x00,0x02,0x04,0x48,0x00,0x00,0x95,0x2E,0x07,0xF1,0x6D, +0x5A,0x08,0x01,0x29,0x49,0x00,0x09,0xA0,0x84,0x00,0x3E,0x07,0xF1,0x6D,0x49,0x00, +0x2B,0xC2,0x44,0x00,0x00,0x64,0x49,0x00,0x2B,0xC9,0x49,0x00,0x2B,0x84,0xE6,0x03, +0xE9,0x04,0x44,0x00,0x00,0x64,0xD5,0x03,0x44,0x00,0x46,0x00,0x49,0x00,0x2B,0xBE, +0x49,0x00,0x2B,0xEA,0x5A,0x08,0x01,0x0A,0x49,0x00,0x15,0x22,0xC8,0x03,0x84,0x04, +0xD5,0x02,0x84,0x03,0x3E,0x07,0xF1,0x74,0x49,0x00,0x09,0x82,0x3E,0x07,0xDE,0xCE, +0xD5,0x69,0x2E,0x07,0xF1,0x6F,0x5A,0x08,0x01,0x06,0x84,0x04,0x3E,0x07,0xF1,0x74, +0xD5,0x61,0x2E,0x07,0xF1,0x68,0x5A,0x08,0x01,0x0D,0x84,0x00,0x3E,0x07,0xF1,0x68, +0x49,0x00,0x15,0x06,0x5A,0x08,0x01,0x06,0x49,0x00,0x09,0x78,0x3E,0x07,0xDE,0xCE, +0x2E,0x07,0xDE,0xCE,0x49,0x00,0x09,0x99,0x5A,0x08,0x01,0x4D,0x2E,0x67,0xF2,0xE5, +0x97,0xB0,0x5A,0x68,0x01,0x37,0x49,0x00,0x14,0xF3,0xC8,0x33,0x49,0x00,0x25,0x7B, +0x49,0xFF,0xBE,0x05,0xC8,0x2E,0x84,0x0B,0xFA,0x2E,0x49,0x00,0x2D,0xB1,0x84,0x0B, +0x80,0x26,0x49,0x00,0x2D,0xBB,0x80,0x06,0x49,0xFF,0xBD,0xEF,0x49,0xFF,0xBD,0xF7, +0x5A,0x00,0x01,0x0C,0x84,0x0B,0x49,0x00,0x2D,0xCC,0x5A,0x08,0x01,0xF9,0x84,0x00, +0x3E,0x07,0xF2,0xE5,0x3E,0x07,0xF2,0xE4,0x84,0x0B,0x84,0x20,0x49,0x00,0x2D,0xA6, +0x2E,0x67,0xF2,0xE5,0x97,0xB0,0x5A,0x68,0x01,0x0D,0x80,0x06,0x49,0x00,0x21,0xFE, +0x84,0x00,0x49,0xFF,0xBC,0xC5,0x80,0x06,0x49,0x00,0x22,0x04,0x3E,0x67,0xF2,0xE4, +0x49,0x00,0x09,0x22,0x49,0x00,0x2B,0x80,0x5A,0x08,0x01,0x0A,0x49,0x00,0x14,0xB8, +0xC8,0x03,0x84,0x04,0xD5,0x02,0x84,0x03,0x3E,0x07,0xF1,0x74,0x84,0x01,0x3E,0x07, +0xF1,0x68,0x2E,0x07,0xF1,0x74,0x5A,0x08,0x04,0x1E,0x49,0xFF,0xFD,0x18,0x84,0x00, +0x3E,0x07,0xF1,0x74,0xD5,0x17,0x49,0x00,0x14,0x34,0x4E,0x03,0xFF,0x18,0x49,0x00, +0x23,0xED,0x4E,0x03,0xFF,0x14,0x49,0x00,0x14,0x9B,0x4E,0x02,0xFF,0x1C,0x48,0xFF, +0xFF,0x0E,0x49,0x00,0x14,0x95,0x5A,0x00,0x01,0x04,0x48,0xFF,0xFF,0x36,0x48,0xFF, +0xFF,0x2C,0x80,0x07,0xFC,0xA0,0xFC,0x00,0x49,0x00,0x23,0xAE,0xC0,0x62,0x2E,0x07, +0xF1,0x77,0xC8,0x0C,0x44,0x14,0x50,0x28,0xB4,0x01,0x92,0x03,0x96,0x06,0x5A,0x00, +0x01,0x06,0xB4,0x01,0x92,0x01,0x96,0x06,0xC8,0x05,0x2E,0x07,0xF1,0x72,0x5A,0x00, +0x02,0x51,0x3C,0x0D,0xFE,0x44,0x5A,0x00,0x01,0x4D,0x49,0xFF,0xFC,0x03,0x2E,0x07, +0xDE,0xCD,0x5A,0x08,0x03,0x05,0x49,0x00,0x22,0x66,0xD5,0x40,0x49,0x00,0x22,0x4F, +0x49,0xFF,0xE0,0x79,0x49,0xFF,0xFD,0x07,0x49,0x00,0x22,0x5D,0x49,0xFF,0xFD,0x34, +0x5A,0x08,0x01,0x08,0x84,0x00,0x49,0x00,0x14,0x61,0x49,0xFF,0xCD,0x9C,0xD5,0x31, +0x49,0xFF,0xC9,0x36,0x2E,0x17,0xDE,0xCF,0x5A,0x00,0x01,0x08,0x5A,0x10,0x01,0x08, +0x3C,0x0D,0xFB,0x37,0x96,0x36,0xC0,0x1B,0x84,0x0A,0xC1,0x02,0x84,0x05,0x3E,0x07, +0xEC,0xA0,0x84,0x06,0xC1,0x02,0x84,0x05,0x3C,0x1D,0xFB,0x37,0x3E,0x07,0xEC,0x9F, +0x44,0x0F,0xFF,0xBF,0xFE,0x0E,0x3C,0x0F,0xFB,0x37,0x84,0x23,0x84,0x00,0x3E,0x07, +0xDE,0xCF,0x3E,0x17,0xF1,0x76,0x3E,0x07,0xF1,0x73,0xD5,0x0B,0x49,0xFF,0xFC,0xDF, +0x49,0xFF,0xFE,0x8D,0xC8,0x03,0x49,0xFF,0xFC,0xF4,0x84,0x01,0x3E,0x07,0xF1,0x76, +0xFC,0x80,0x00,0x00,0xFC,0x40,0x49,0x00,0x18,0xB2,0x80,0xC0,0x49,0x00,0x19,0x3C, +0x5A,0x08,0x01,0x05,0x84,0x20,0x49,0x00,0x2C,0xF1,0x84,0x01,0x49,0x00,0x2D,0x09, +0x80,0xE0,0x5A,0x08,0x01,0x08,0x84,0x20,0x49,0x00,0x2C,0xE8,0x80,0x07,0x49,0x00, +0x19,0x2F,0x2E,0x07,0xF1,0x70,0x5A,0x08,0x01,0x31,0x49,0x00,0x19,0x25,0x80,0xE0, +0x5A,0x08,0x01,0x2C,0x85,0x20,0x84,0x00,0x3E,0x97,0xF1,0x70,0x49,0x00,0x19,0x20, +0x84,0x00,0x3E,0x97,0xF1,0x6C,0x49,0xFF,0xFC,0x53,0x80,0x07,0x84,0x28,0x5A,0x68, +0x02,0x04,0x44,0x10,0x01,0x90,0x49,0x00,0x2C,0xBB,0x84,0x01,0x80,0x20,0x49,0x00, +0x2C,0xC5,0x5A,0x68,0x02,0x1C,0x49,0x00,0x19,0x07,0x80,0xE0,0xC8,0x17,0x84,0x01, +0x49,0x00,0x2C,0xD7,0x81,0x20,0x5A,0x08,0x01,0xF8,0x80,0x27,0x49,0x00,0x2C,0xB6, +0x80,0x09,0x49,0x00,0x18,0xFD,0xD5,0x0A,0x2E,0x77,0xF1,0x70,0x5A,0x78,0x01,0x07, +0x49,0x00,0x18,0xF2,0xC8,0x03,0x3E,0x77,0xF1,0x6C,0x2E,0x07,0xF1,0x76,0xC0,0x11, +0x5A,0x68,0x01,0x06,0x84,0x04,0x3E,0x07,0xF1,0x76,0xD5,0x0B,0x5A,0x08,0x04,0x0A, +0x84,0x01,0x3E,0x07,0xF1,0x76,0x84,0x01,0x49,0x00,0x24,0x4D,0x49,0x00,0x21,0xB1, +0x2E,0x07,0xF1,0x76,0xE6,0x05,0xE8,0x1A,0x44,0xF0,0x8C,0xF8,0x38,0x07,0x80,0x00, +0x40,0xF0,0x3C,0x00,0x4A,0x00,0x3C,0x00,0x06,0x12,0x18,0x1E,0x0C,0x00,0x49,0xFF, +0xFC,0x94,0xD5,0x0C,0x49,0xFF,0xFC,0xC9,0xD5,0x09,0x49,0xFF,0xFC,0xD2,0xD5,0x06, +0x49,0xFF,0xFF,0x1B,0xD5,0x03,0x49,0xFF,0xFC,0xDA,0xFC,0xC0,0xFC,0x40,0x46,0x61, +0xFF,0xFF,0x46,0x71,0xF1,0x10,0x50,0x63,0x0F,0x00,0x50,0x73,0x81,0x00,0x46,0xA1, +0xF2,0x90,0x85,0x20,0x49,0x00,0x2C,0x3D,0x2E,0x07,0xF1,0x84,0xC0,0x06,0x84,0x02, +0x49,0x00,0x2C,0x5B,0x3E,0x97,0xF1,0x84,0x84,0x02,0x49,0x00,0x2C,0x7A,0x5A,0x08, +0x01,0x04,0x49,0x00,0x15,0x96,0x2E,0x07,0xF1,0x72,0xE6,0x05,0xE8,0xEC,0x44,0xF0, +0x8D,0x6C,0x38,0x07,0x81,0x01,0x40,0xF0,0x3C,0x00,0xDD,0x0F,0x3C,0x01,0x0A,0x00, +0x50,0x00,0xD8,0x00,0x50,0x01,0x84,0x01,0x85,0x20,0x3E,0x07,0xF1,0x75,0x44,0x10, +0x01,0xF4,0x84,0x02,0x3E,0x97,0xF1,0x84,0x49,0x00,0x2C,0x32,0x84,0x02,0x84,0x21, +0x49,0x00,0x2C,0x3C,0x3E,0x97,0xF1,0x76,0x3E,0x97,0xF1,0x74,0x3E,0x97,0xF1,0x6E, +0x49,0x00,0x13,0x5E,0x5A,0x08,0x01,0x0A,0x49,0x00,0x13,0x5D,0x5A,0x08,0x02,0x04, +0x84,0x03,0xD5,0x7C,0x84,0x04,0xD5,0x7A,0x84,0x02,0xD5,0x78,0x85,0x20,0x49,0x00, +0x12,0xF9,0x49,0x00,0x13,0x09,0x3E,0x97,0xF1,0x77,0x49,0x00,0x12,0xFC,0x5A,0x08, +0x01,0x24,0x3E,0x07,0xF1,0x6E,0x84,0x00,0x49,0x00,0x23,0xCD,0x49,0xFF,0xC6,0x35, +0x80,0x06,0x49,0xFF,0xC6,0x27,0x46,0x01,0xF2,0x80,0x50,0x00,0x01,0x00,0x49,0xFF, +0xC6,0x21,0xFA,0x04,0x49,0xFF,0xC7,0xAF,0x46,0x01,0xF1,0x00,0x50,0x00,0x01,0x00, +0x49,0xFF,0xC6,0x18,0x44,0x00,0x00,0x32,0x49,0xFF,0xC7,0xA5,0x84,0x00,0x3E,0x97, +0xF1,0x75,0x49,0x00,0x28,0xC1,0x49,0x00,0x12,0xEF,0x2E,0x97,0xF1,0x6E,0x5A,0x08, +0x01,0x0D,0x84,0x00,0x3C,0x0F,0xFE,0x44,0x5A,0x98,0x01,0x08,0x49,0x00,0x13,0xAE, +0x3E,0x97,0xF1,0x72,0x48,0xFF,0xFF,0x7F,0x4E,0x93,0xFF,0x7D,0x49,0xFF,0xFE,0xEC, +0x48,0xFF,0xFF,0x79,0x49,0x00,0x12,0xBF,0x49,0x00,0x12,0xD6,0x49,0x00,0x12,0xC4, +0x5A,0x08,0x01,0x1F,0x3E,0x07,0xF1,0x6E,0x84,0x00,0x49,0x00,0x23,0x8C,0x49,0xFF, +0xC5,0xF4,0x80,0x06,0x49,0xFF,0xC5,0xE6,0x80,0x07,0x49,0xFF,0xC5,0xE3,0x44,0x00, +0x00,0x96,0x49,0xFF,0xC7,0x70,0x50,0x05,0x01,0x00,0x49,0xFF,0xC5,0xDB,0xFA,0x04, +0x49,0xFF,0xC7,0x69,0x49,0x00,0x13,0x2F,0x84,0x00,0x3E,0x07,0xF1,0x75,0x49,0x00, +0x12,0x91,0x5A,0x08,0x01,0x10,0x84,0x20,0x3E,0x17,0xF2,0xE6,0x49,0x00,0x28,0x7C, +0x2E,0x07,0xF1,0x6E,0x5A,0x08,0x01,0x07,0x84,0x01,0x3E,0x07,0xF1,0x72,0x48,0xFF, +0xFF,0x42,0x2E,0x07,0xF1,0x6E,0x4E,0x03,0xFF,0x3E,0xD5,0xC1,0x49,0x00,0x12,0x8C, +0x5A,0x08,0x01,0x1F,0x3E,0x07,0xF1,0x6E,0x84,0x00,0x49,0x00,0x23,0x54,0x49,0xFF, +0xC5,0xBC,0x80,0x06,0x49,0xFF,0xC5,0xAE,0x80,0x07,0x49,0xFF,0xC5,0xAB,0x44,0x00, +0x00,0x96,0x49,0xFF,0xC7,0x38,0x50,0x05,0x01,0x00,0x49,0xFF,0xC5,0xA3,0xFA,0x04, +0x49,0xFF,0xC7,0x31,0x49,0x00,0x12,0xF7,0x84,0x00,0x3E,0x07,0xF1,0x75,0x49,0x00, +0x12,0x59,0x5A,0x08,0x01,0x08,0x49,0x00,0x28,0x47,0x2E,0x07,0xF1,0x6E,0x5A,0x00, +0x01,0xCD,0x2E,0x07,0xF1,0x6E,0x4E,0x03,0xFF,0x0E,0x44,0x10,0x00,0xEB,0x49,0x00, +0x2B,0x4C,0x49,0x00,0x12,0x84,0x84,0x0A,0x49,0xFF,0xC7,0x15,0x84,0x01,0x44,0x10, +0x00,0xEB,0x49,0x00,0x2B,0x42,0x48,0xFF,0xFE,0xFE,0x92,0x00,0x2E,0x07,0xF1,0x75, +0xDD,0x9E,0x3E,0x07,0xF1,0x75,0xDD,0x9E,0xDD,0x9E,0x46,0x10,0x00,0x81,0xC8,0x06, +0x04,0x00,0x80,0x33,0x58,0x00,0x02,0x00,0xD5,0x06,0x04,0x20,0x80,0x33,0x44,0x0F, +0xFD,0xFF,0xFE,0x16,0x14,0x00,0x80,0x33,0xDD,0x9E,0x44,0x24,0x2C,0x14,0x84,0x30, +0xB4,0x62,0x96,0x1F,0xFE,0x5E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x24,0x2C,0x14, +0x96,0x1F,0xB4,0x62,0x40,0x10,0x20,0x08,0x44,0x0F,0xF0,0xFF,0xFE,0x1E,0xFE,0x0F, +0xB6,0x02,0xDD,0x9E,0xDD,0x9E,0x46,0x20,0x00,0x81,0x96,0x0F,0x04,0x31,0x00,0x12, +0x40,0x10,0x40,0x08,0x44,0x0C,0xFF,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x12, +0xDD,0x9E,0xFC,0x00,0x44,0x24,0x80,0x10,0x84,0x01,0xB4,0x22,0x96,0x46,0xC1,0x0B, +0xAE,0x10,0x3E,0x07,0xF1,0x87,0x3E,0x07,0xF1,0x84,0x3C,0x0D,0xFC,0x5F,0x8C,0x01, +0x3C,0x0F,0xFC,0x5F,0x44,0x04,0x80,0x10,0xB4,0x00,0x92,0x0A,0x96,0x06,0xC0,0x16, +0x44,0x04,0x80,0x11,0x84,0x24,0xAE,0x40,0x46,0x00,0x00,0x81,0x04,0x00,0x00,0x1E, +0x92,0x04,0x96,0x0F,0x3E,0x07,0xF9,0x14,0x49,0x00,0x01,0x78,0x44,0x07,0x20,0x00, +0xB4,0x00,0x92,0x08,0x96,0x06,0x49,0x00,0x01,0x56,0x44,0x14,0x80,0x10,0x84,0x41, +0xB4,0x01,0x92,0x08,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x11,0xAE,0x80,0x3E,0x27, +0xF1,0x85,0xB4,0x01,0x92,0x07,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x10,0x44,0x1F, +0xFF,0x80,0xAE,0x40,0x44,0x04,0x80,0x10,0xB4,0x20,0x92,0x25,0x96,0x46,0xC1,0x03, +0xFA,0x30,0xAE,0x40,0xB4,0x00,0x92,0x06,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x10, +0x44,0x10,0x00,0x40,0xAE,0x40,0x44,0x14,0x80,0x10,0xB4,0x01,0x92,0x18,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x13,0x84,0x41,0xAE,0x80,0xB4,0x01,0x92,0x15,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x12,0xFA,0x30,0xAE,0x40,0xFC,0x80,0xFC,0x00,0x44,0x24, +0x80,0x10,0x84,0x21,0xB4,0x02,0x92,0x04,0x96,0x06,0xC0,0x14,0xFA,0x00,0xAE,0x10, +0x3E,0x17,0xF1,0x86,0x3E,0x17,0xF1,0x84,0x3C,0x0D,0xFC,0x60,0x8C,0x01,0x3C,0x0F, +0xFC,0x60,0x46,0x00,0x00,0x81,0x04,0x00,0x00,0xF0,0x92,0x03,0x96,0x06,0x49,0x00, +0x01,0x02,0x44,0x14,0x80,0x10,0xB4,0x01,0x92,0x0B,0x96,0x06,0xC0,0x05,0x44,0x04, +0x80,0x11,0x84,0x48,0xAE,0x80,0xB4,0x01,0x92,0x03,0x96,0x06,0xC0,0x05,0x44,0x04, +0x80,0x10,0x84,0x28,0xAE,0x40,0x44,0x04,0x80,0x10,0xB4,0x20,0x92,0x22,0x96,0x46, +0xC1,0x03,0x84,0x24,0xAE,0x40,0xB4,0x00,0x92,0x01,0x96,0x06,0xC0,0x0E,0x44,0x04, +0x80,0x90,0xB4,0x00,0x92,0x01,0x96,0x06,0x5A,0x08,0x01,0x08,0x44,0x14,0x80,0x10, +0x84,0x42,0xAE,0x88,0x3E,0x07,0xF1,0x77,0x44,0x14,0x80,0x10,0xB4,0x01,0x92,0x19, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x13,0x84,0x42,0xAE,0x80,0xB4,0x01,0x92,0x16, +0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x12,0x44,0x10,0x00,0x40,0xAE,0x40,0xFC,0x80, +0xFC,0x00,0x80,0xC0,0x49,0xFF,0xBD,0x32,0x46,0x20,0x00,0x81,0x97,0x86,0x94,0x32, +0x04,0x61,0x03,0x93,0x84,0x3B,0xFF,0x8E,0xFF,0x87,0x14,0x61,0x03,0x93,0x49,0xFF, +0xBD,0x1E,0xFC,0x80,0x44,0x24,0x50,0x1C,0x40,0x00,0x40,0x08,0xB4,0x22,0x96,0x49, +0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x24,0x50,0x1C,0x44,0x1F,0x00,0xFF,0xB4,0x62, +0x40,0x00,0x20,0x08,0xFE,0x5E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x3E,0x07,0xF1,0x88, +0xDD,0x9E,0x44,0x07,0x21,0x00,0x46,0x10,0x20,0x00,0xB4,0x40,0xFE,0x57,0xB6,0x20, +0x80,0x20,0xB4,0x01,0x92,0x19,0x96,0x06,0xC8,0xFD,0xDD,0x9E,0x44,0x27,0x21,0x1C, +0x96,0x1F,0x40,0x10,0x60,0x08,0xB4,0x62,0x46,0x0F,0x0F,0xFF,0x50,0x00,0x0F,0xFF, +0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x24,0x80,0x84,0x96,0x06,0xB4,0x62, +0x40,0x10,0x40,0x08,0x44,0x0E,0xFF,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E, +0x44,0x04,0x80,0x04,0xA6,0x02,0x92,0x01,0x96,0x06,0xC0,0x08,0x44,0x04,0x80,0x06, +0x84,0x22,0xAE,0x40,0x84,0x01,0x3E,0x07,0xF1,0x89,0x44,0x04,0x80,0x04,0x84,0x21, +0xA6,0x02,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x06,0xAE,0x40,0x3E,0x17,0xF1,0x88, +0xDD,0x9E,0x44,0x04,0x80,0x04,0xA6,0x03,0x92,0x01,0x96,0x06,0xDD,0x9E,0x44,0x04, +0x10,0x04,0x44,0x10,0x00,0x55,0xAE,0x40,0x44,0x1F,0xFF,0xAA,0xAE,0x41,0x44,0x10, +0x00,0x66,0xAE,0x42,0x40,0x00,0x00,0x09,0xDD,0x9E,0x44,0x14,0x10,0x08,0xB6,0x01, +0x44,0x14,0x80,0x04,0xA6,0x0B,0x92,0x01,0x96,0x06,0xC0,0xFD,0x44,0x04,0x80,0x07, +0x84,0x22,0xAE,0x40,0xDD,0x9E,0xFC,0x00,0x80,0xC0,0x92,0x10,0x96,0x00,0x49,0xFF, +0xFF,0xEE,0x40,0x03,0x20,0x09,0x96,0x00,0x49,0xFF,0xFF,0xE9,0x96,0x30,0x49,0xFF, +0xFF,0xE6,0xFC,0x80,0x44,0x24,0x10,0x00,0x96,0x06,0xA6,0x53,0x54,0x10,0x80,0xFE, +0xFE,0x0F,0xAE,0x13,0xDD,0x9E,0x44,0x14,0x10,0x00,0xAE,0x08,0xDD,0x9E,0x44,0x04, +0x10,0x12,0x84,0x20,0xAE,0x40,0xDD,0x9E,0x44,0x14,0x10,0x0C,0xB6,0x01,0xDD,0x9E, +0x44,0x24,0x10,0x14,0x96,0x06,0xA6,0x53,0x54,0x10,0x80,0xFE,0xFE,0x0F,0xAE,0x13, +0xDD,0x9E,0x44,0x14,0x10,0x10,0xAE,0x0B,0xDD,0x9E,0x44,0x14,0x10,0x10,0xAE,0x08, +0xDD,0x9E,0x3E,0x07,0xDE,0xD0,0xDD,0x9E,0x2E,0x07,0xDE,0xD0,0xDD,0x9E,0x44,0x37, +0x20,0x00,0x54,0x10,0x00,0x01,0xB4,0x83,0x40,0x20,0xA0,0x08,0x44,0x1F,0xFE,0xFF, +0xFE,0x66,0xFE,0x57,0xB6,0x23,0x3E,0x07,0xDE,0xD0,0xDD,0x9E,0x44,0x07,0x20,0x00, +0xB4,0x00,0x92,0x18,0x96,0x06,0xDD,0x9E,0x84,0x01,0x3E,0x07,0xDE,0xD1,0xDD,0x9E, +0x2E,0x07,0xDE,0xD1,0xDD,0x9E,0x84,0x00,0x3E,0x07,0xDE,0xD1,0xDD,0x9E,0x3C,0x0D, +0xFC,0x67,0xC0,0x0B,0xA4,0x80,0x3C,0x13,0xF8,0xCA,0x8E,0x21,0xE0,0x41,0xE8,0x05, +0xA4,0x40,0x8C,0x21,0x96,0x49,0xAC,0x40,0xA4,0x80,0x3C,0x13,0xF8,0xCC,0xE2,0x41, +0xE9,0x05,0x84,0x01,0x3E,0x07,0xDE,0xD2,0xD5,0x0B,0x3C,0x1D,0xFC,0x69,0xC1,0x08, +0xA4,0x00,0x88,0x01,0x44,0x16,0x00,0x34,0xA6,0x00,0x96,0x00,0xB6,0x01,0x44,0x06, +0x00,0x00,0xB4,0x00,0x92,0x08,0x96,0x06,0x5A,0x08,0x01,0x0B,0x44,0x06,0x00,0x30, +0xB4,0x20,0x58,0x10,0x80,0x01,0xB6,0x20,0xB4,0x20,0x96,0x46,0xC9,0xFE,0xDD,0x9E, +0xFC,0x40,0x49,0xFF,0xC1,0x5D,0xE6,0x03,0xE8,0x0F,0x3C,0x0D,0xFC,0x67,0x84,0x20, +0xAC,0x40,0x3C,0x1D,0xFC,0x69,0xC1,0x08,0xA4,0x00,0x88,0x01,0x44,0x16,0x00,0x34, +0xA6,0x00,0x96,0x00,0xB6,0x01,0x85,0x40,0x44,0x74,0x80,0x00,0x44,0x94,0x80,0x80, +0xA7,0xBA,0xB4,0x09,0x92,0xC3,0x92,0x13,0x96,0x06,0xFF,0x86,0xC6,0x1A,0x44,0x04, +0x80,0x02,0x84,0x28,0xAE,0x40,0x49,0xFF,0xC1,0x3B,0xE6,0x03,0xE8,0x4D,0x44,0x06, +0x00,0x00,0xB4,0x00,0x92,0x08,0x96,0x06,0x5A,0x08,0x01,0x47,0x44,0x06,0x00,0x30, +0xB4,0x20,0x58,0x10,0x80,0x01,0xB6,0x20,0xB4,0x20,0x96,0x46,0xC9,0xFE,0xD5,0x3C, +0xA6,0x3A,0x40,0x10,0x10,0x09,0xB4,0x09,0x92,0x14,0x96,0x06,0xFE,0x0E,0xC0,0x1F, +0x49,0xFF,0xC1,0x1E,0xE6,0x03,0x44,0x04,0x80,0x02,0xFA,0x20,0xAE,0x40,0xE8,0x04, +0x49,0xFF,0xFF,0x87,0xD5,0x13,0x3C,0x0D,0xFC,0x67,0xC0,0x0B,0xA4,0x80,0x3C,0x13, +0xF8,0xCA,0x8E,0x21,0xE0,0x41,0xE8,0x05,0xA4,0x40,0x8C,0x21,0x96,0x49,0xAC,0x40, +0xA4,0x40,0x3C,0x03,0xF8,0xCC,0xE2,0x20,0xE8,0x17,0x85,0x40,0xA6,0x3A,0x40,0x10, +0x18,0x09,0xB4,0x09,0x92,0x16,0x96,0x06,0xFE,0x0E,0xC0,0x07,0x44,0x04,0x80,0x02, +0x44,0x10,0x00,0x40,0xAE,0x40,0xD5,0x08,0x84,0x01,0x8D,0x41,0x49,0xFF,0xC4,0x82, +0x5C,0xF5,0x27,0x11,0xE9,0xA6,0xFC,0xC0,0x44,0x16,0x00,0x34,0xB6,0x01,0xDD,0x9E, +0x3C,0x0F,0xFC,0x6A,0x02,0x0F,0x80,0x00,0x3C,0x4B,0xF8,0xCC,0x3C,0x1F,0xFC,0x69, +0x3C,0x5B,0xF8,0xCB,0x3C,0x2F,0xFC,0x68,0x3C,0x3F,0xFC,0x67,0x3C,0x0B,0xF8,0xCA, +0xDD,0x9E,0x3C,0x0B,0xF8,0xCC,0xDD,0x9E,0x3E,0x07,0xDE,0xD2,0xDD,0x9E,0x2E,0x07, +0xDE,0xD2,0xDD,0x9E,0x3C,0x0F,0xFC,0x64,0xDD,0x9E,0x3C,0x0F,0xFC,0x63,0xDD,0x9E, +0xFC,0x20,0x44,0x14,0x80,0x00,0x44,0x04,0x80,0x80,0xA6,0x8A,0xB4,0x00,0x92,0x10, +0x96,0x06,0xFE,0x16,0xC0,0x05,0x44,0x04,0x80,0x02,0x84,0x41,0xAE,0x80,0xA6,0x0A, +0x40,0x10,0x04,0x09,0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x11,0x96,0x06,0xFE,0x0E, +0xC0,0x05,0x44,0x04,0x80,0x02,0x84,0x22,0xAE,0x40,0x44,0x04,0x80,0x00,0xA6,0x02, +0x40,0x10,0x08,0x09,0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x12,0x96,0x06,0xFE,0x0E, +0x4E,0x02,0x00,0xB0,0x44,0x04,0x80,0x02,0x84,0x24,0xAE,0x40,0x3C,0x0D,0xFC,0x68, +0x3C,0x2D,0xFC,0x6A,0x84,0x20,0xAC,0x40,0xC2,0x06,0x44,0x16,0x00,0x28,0xB4,0x21, +0x96,0x48,0xAE,0x50,0xC0,0x0B,0xA4,0x80,0x3C,0x13,0xF8,0xCB,0x8E,0x21,0xE0,0x41, +0xE8,0x05,0xA4,0x40,0x8C,0x21,0x96,0x49,0xAC,0x40,0x49,0xFF,0xC0,0x81,0xE6,0x03, +0xE9,0x05,0x84,0xC0,0x44,0x76,0x00,0x30,0xD5,0x19,0x44,0x06,0x00,0x00,0xB4,0x00, +0x92,0x08,0x96,0x06,0x5A,0x08,0x01,0xF7,0x44,0x06,0x00,0x30,0xB4,0x20,0x58,0x10, +0x80,0x01,0xB6,0x20,0xB4,0x20,0x96,0x46,0xC9,0xFE,0xD5,0xEC,0x84,0x01,0x8C,0xC1, +0x49,0xFF,0xC3,0xF8,0x5C,0xF3,0x08,0x01,0xE8,0x6E,0x44,0x34,0x80,0x00,0x44,0x24, +0x80,0x80,0xA6,0x1A,0x40,0x10,0x0C,0x09,0xB4,0x02,0x92,0x13,0x96,0x06,0xFE,0x0E, +0xC0,0x1A,0x44,0x04,0x80,0x02,0x84,0x28,0xAE,0x40,0x49,0xFF,0xC0,0x51,0xE6,0x03, +0xE8,0x5A,0x44,0x06,0x00,0x00,0xB4,0x00,0x92,0x08,0x96,0x06,0x5A,0x08,0x01,0x54, +0x44,0x06,0x00,0x30,0xB4,0x20,0x58,0x10,0x80,0x01,0xB6,0x20,0xB4,0x20,0x96,0x46, +0xC9,0xFE,0xD5,0x49,0xA6,0x1A,0x40,0x10,0x10,0x09,0xB4,0x02,0x92,0x14,0x96,0x06, +0xFE,0x0E,0xC0,0x31,0x44,0x04,0x80,0x02,0xFA,0x20,0xAE,0x40,0x3C,0x2D,0xFC,0x6A, +0x3C,0x0D,0xFC,0x68,0xC2,0x08,0xA4,0x40,0x88,0x22,0x44,0x26,0x00,0x38,0xB4,0x42, +0x96,0x90,0xAE,0x88,0xC0,0x0B,0xA4,0x80,0x3C,0x13,0xF8,0xCB,0x8E,0x21,0xE0,0x41, +0xE8,0x05,0xA4,0x40,0x8C,0x21,0x96,0x49,0xAC,0x40,0x49,0xFF,0xC0,0x19,0xE6,0x03, +0xE9,0x03,0x84,0xC0,0xD5,0x10,0x44,0x06,0x00,0x00,0xB4,0x00,0x92,0x08,0x96,0x06, +0x5A,0x08,0x01,0xF9,0xB4,0x07,0x58,0x00,0x00,0x01,0xB6,0x07,0xB4,0x07,0x96,0x06, +0xC8,0xFE,0xD5,0xF0,0x44,0x04,0x80,0x00,0xA6,0x02,0x40,0x10,0x14,0x09,0x44,0x04, +0x80,0x80,0xB4,0x00,0x92,0x15,0x96,0x06,0xFE,0x0E,0xC0,0x91,0x44,0x04,0x80,0x02, +0xFA,0x30,0xAE,0x40,0x3C,0x0D,0xFC,0x64,0xC0,0x04,0x3C,0x0D,0xFC,0x64,0xDD,0x20, +0x44,0x04,0x80,0x00,0xA6,0x02,0x40,0x10,0x0C,0x09,0x44,0x04,0x80,0x80,0xB4,0x00, +0x92,0x13,0x96,0x06,0xFE,0x0E,0xC8,0x0E,0x44,0x04,0x80,0x00,0xA6,0x02,0x40,0x10, +0x10,0x09,0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x14,0x96,0x06,0xFE,0x0E,0xC0,0x3B, +0xD5,0x1A,0x44,0x04,0x80,0x02,0x84,0x28,0xAE,0x40,0x49,0xFF,0xBF,0xD1,0xE6,0x03, +0xE8,0xEC,0x44,0x06,0x00,0x00,0xB4,0x00,0x92,0x08,0x96,0x06,0x5A,0x08,0x01,0xE6, +0x44,0x06,0x00,0x30,0xB4,0x20,0x58,0x10,0x80,0x01,0xB6,0x20,0xB4,0x20,0x96,0x46, +0xC9,0xFE,0xD5,0xDB,0x44,0x04,0x80,0x02,0xFA,0x20,0xAE,0x40,0x49,0xFF,0xBF,0xB8, +0xE6,0x03,0xE8,0x04,0x49,0xFF,0xFE,0x25,0xD5,0x16,0x3C,0x0D,0xFC,0x67,0xC0,0x0B, +0xA4,0x80,0x3C,0x13,0xF8,0xCA,0x8E,0x21,0xE0,0x41,0xE8,0x05,0xA4,0x40,0x8C,0x21, +0x96,0x49,0xAC,0x40,0xA4,0x40,0x3C,0x03,0xF8,0xCC,0xE2,0x20,0xE9,0x04,0x84,0x01, +0x3E,0x07,0xDE,0xD2,0x44,0x14,0x80,0x00,0xA6,0x0A,0x40,0x20,0x14,0x09,0x44,0x04, +0x80,0x80,0xB4,0x00,0x92,0x15,0x96,0x06,0xFE,0x16,0xC0,0x05,0x44,0x04,0x80,0x02, +0xFA,0x50,0xAE,0x80,0xA6,0x0A,0x40,0x10,0x18,0x09,0x44,0x04,0x80,0x80,0xB4,0x00, +0x92,0x16,0x96,0x06,0xFE,0x0E,0xC0,0x06,0x44,0x04,0x80,0x02,0x44,0x10,0x00,0x40, +0xAE,0x40,0xFC,0xA0,0x46,0x20,0x00,0x83,0x96,0x06,0x04,0x31,0x00,0x14,0x94,0x41, +0x84,0x1D,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x14,0xDD,0x9E,0x46,0x00,0x00,0x83, +0x04,0x00,0x00,0x16,0x92,0x10,0x54,0x00,0x03,0xFF,0xDD,0x9E,0xFC,0x00,0x46,0x60, +0x00,0x83,0x44,0x01,0x00,0x00,0xA0,0x71,0xFE,0x0F,0xA8,0x31,0x84,0x01,0x49,0xFF, +0xC2,0xF1,0xA0,0x71,0x44,0x0E,0xFF,0xFF,0xFE,0x0E,0xA8,0x31,0xFC,0x80,0x46,0x20, +0x00,0x80,0x84,0x3E,0x04,0x31,0x00,0x77,0x96,0x06,0xFE,0x5E,0xFE,0x0F,0x14,0x01, +0x00,0x77,0xDD,0x9E,0x46,0x20,0x00,0x80,0x96,0x06,0x40,0x10,0x60,0x08,0x04,0x31, +0x00,0x77,0x46,0x0F,0xEF,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01, +0x00,0x77,0xDD,0x9E,0x40,0x21,0x40,0x08,0x40,0x10,0xA0,0x08,0x88,0x22,0x88,0x20, +0x46,0x00,0x00,0x80,0x14,0x10,0x00,0x78,0xDD,0x9E,0xFC,0x41,0xF5,0x81,0x81,0x00, +0x81,0x22,0x80,0xE3,0x80,0xC4,0x46,0x50,0x00,0x80,0xC0,0x03,0x5A,0x08,0x01,0x18, +0x83,0x85,0xBA,0x77,0x46,0x1F,0xEF,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x56,0xB9,0xF7, +0x84,0x23,0x49,0xFF,0xB5,0xDC,0x38,0x14,0x99,0x01,0x84,0x02,0x49,0xFF,0xBC,0x3A, +0x38,0x03,0x98,0x00,0x84,0x22,0x49,0xFF,0xBD,0x38,0xFC,0xC1,0x81,0x41,0x83,0x85, +0xB9,0x77,0x46,0x00,0x10,0x00,0xFE,0x0F,0xB8,0xF7,0x84,0x23,0x84,0x01,0x49,0xFF, +0xB5,0xC6,0x50,0x25,0x7F,0xFF,0x80,0x06,0xF1,0x01,0x96,0x90,0x49,0xFF,0xFF,0xC4, +0xFC,0xC1,0x46,0x20,0x00,0x80,0x96,0x06,0x04,0x31,0x00,0x75,0x40,0x10,0x28,0x08, +0x44,0x0F,0xFB,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x75,0xDD,0x9E,0x46,0x20, +0x00,0x91,0x96,0x06,0x04,0x31,0x00,0x14,0x94,0x41,0x84,0x1D,0xFE,0x1E,0xFE,0x0F, +0x14,0x01,0x00,0x14,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xC8,0x04,0x44,0x07, +0x20,0x14,0xD5,0x03,0x44,0x07,0x20,0x18,0xB4,0x00,0x40,0x00,0x30,0x08,0x92,0x0C, +0xDD,0x9E,0xFC,0x00,0x80,0xC1,0x49,0xFF,0xBE,0x77,0x84,0x40,0x50,0x00,0x09,0x48, +0x80,0x22,0xE2,0x46,0xE8,0x04,0xAA,0x41,0x8C,0x41,0xD5,0xFC,0xFC,0x80,0xFC,0x00, +0x44,0x04,0x00,0x9C,0xA6,0x00,0xE6,0x03,0xE9,0x24,0x44,0x64,0x0C,0x40,0x84,0x41, +0xA6,0x32,0x80,0x62,0x58,0x00,0x00,0x08,0xAE,0x32,0x44,0x14,0x04,0x40,0x44,0x04, +0x08,0x40,0x49,0xFF,0xB9,0x61,0xA6,0x32,0x84,0x41,0x54,0x00,0x00,0xF7,0xAE,0x32, +0x44,0x14,0x04,0x40,0x44,0x04,0x08,0x40,0x80,0x62,0x49,0xFF,0xB9,0x55,0x44,0x07, +0x20,0xC0,0x46,0x10,0x20,0x00,0xB4,0x40,0xFE,0x57,0xB6,0x20,0x80,0x20,0xD5,0x3C, +0x49,0x00,0x0D,0xBE,0xC8,0xDB,0x44,0x64,0x50,0x28,0xB4,0x06,0x92,0x10,0x96,0x06, +0x5A,0x00,0x01,0x05,0x44,0x64,0x50,0x28,0xD5,0x25,0x2E,0x07,0xF1,0x77,0xC8,0x0E, +0xB4,0x06,0x92,0x03,0x96,0x06,0xC8,0x0A,0xB4,0x06,0x92,0x01,0x96,0x06,0xC0,0x06, +0x3C,0x0D,0xFE,0x44,0x5A,0x08,0x01,0xEB,0xD5,0xEE,0x49,0xFF,0xF6,0x41,0x5A,0x08, +0x02,0xF9,0xD5,0xE9,0x2E,0x07,0xF1,0x77,0xC8,0x12,0xB4,0x06,0x92,0x03,0x96,0x06, +0xC8,0x0E,0xB4,0x06,0x92,0x01,0x96,0x06,0xC0,0x0A,0x3C,0x0D,0xFE,0x44,0x5A,0x00, +0x01,0xAE,0xB4,0x06,0x92,0x10,0x96,0x06,0xC0,0xEE,0xD5,0xA8,0x49,0xFF,0xF6,0x28, +0x5A,0x08,0x02,0xF5,0xD5,0xA3,0xB4,0x01,0x92,0x19,0x96,0x06,0xC8,0xFD,0x84,0x01, +0x49,0xFF,0xFC,0xA9,0xFC,0x80,0x44,0x04,0x00,0x50,0x44,0x1F,0x98,0x81,0xAC,0x40, +0x84,0x21,0xAC,0x41,0xDD,0x9E,0xFC,0x00,0x80,0xC0,0x44,0x04,0x80,0x90,0x54,0x13, +0x00,0x01,0xB4,0x80,0x84,0x5D,0x94,0xC9,0xFE,0xA6,0xFE,0x9F,0xB6,0x40,0x46,0x00, +0x00,0x81,0x84,0x5E,0x83,0x80,0xBB,0x15,0xFE,0x9E,0xFE,0x57,0xB9,0x95,0x84,0x00, +0x3E,0x07,0xF1,0x77,0x44,0x04,0x80,0x10,0x84,0x22,0xAE,0x40,0x49,0x00,0x23,0xAB, +0xE6,0x03,0xE9,0x2A,0x44,0x14,0x00,0x18,0xA6,0x0A,0x5A,0x68,0x01,0x13,0x54,0x00, +0x00,0xFB,0xAE,0x0A,0x44,0x14,0x50,0x10,0xB4,0x01,0x92,0x08,0x40,0x00,0x20,0x08, +0xB6,0x01,0x44,0x14,0x70,0x00,0x44,0x0E,0xFF,0xFF,0xB4,0x41,0xFE,0x16,0xD5,0x13, +0x58,0x00,0x00,0x04,0xAE,0x0A,0x44,0x14,0x50,0x10,0xB4,0x01,0x92,0x08,0x40,0x00, +0x20,0x08,0x58,0x00,0x00,0x98,0xB6,0x01,0x44,0x14,0x70,0x00,0x44,0x01,0x00,0x00, +0xB4,0x41,0xFE,0x17,0xB6,0x01,0xFC,0x80,0x46,0x00,0x00,0x91,0xA0,0x02,0x96,0x01, +0xDD,0x9E,0xDD,0x9E,0x46,0x30,0x00,0x91,0x58,0x20,0x00,0x01,0x14,0x21,0x80,0x11, +0x44,0x20,0xFF,0xFE,0xFE,0x16,0x14,0x01,0x80,0x11,0x46,0x00,0x20,0x00,0xFE,0x47, +0x44,0x07,0x20,0xC0,0xB6,0x20,0xDD,0x9E,0xFC,0x00,0x46,0x01,0xFF,0xF9,0x50,0x00, +0x05,0x27,0x46,0x61,0xFF,0xFF,0x49,0xFF,0xBF,0xF5,0x49,0xFF,0xBF,0xDF,0x50,0x03, +0x03,0xCC,0x49,0xFF,0xBF,0xEF,0x49,0xFF,0xBF,0xD9,0x80,0x06,0x44,0x67,0x30,0x04, +0x49,0xFF,0xBF,0xE8,0x49,0xFF,0xBF,0xD2,0x44,0x00,0x03,0xE8,0x49,0xFF,0xBF,0xDB, +0xB4,0x26,0x84,0x1E,0xFE,0x0E,0xB6,0x06,0x44,0x00,0x03,0xE8,0x49,0xFF,0xBF,0xD3, +0xB4,0x06,0x58,0x00,0x00,0x01,0xB6,0x06,0x44,0x00,0x03,0xE8,0x49,0xFF,0xBF,0xCB, +0x44,0x04,0x80,0x0A,0x84,0x24,0xAE,0x40,0x84,0x22,0xAE,0x40,0x84,0x21,0xAE,0x40, +0xFC,0x80,0xFC,0x00,0x44,0x27,0x30,0x10,0xB4,0x22,0x92,0x21,0x96,0x46,0x5A,0x10, +0x01,0xFD,0x84,0x20,0x3E,0x17,0xF1,0xB1,0x44,0x17,0x30,0x00,0x46,0x60,0x01,0x00, +0xB6,0x01,0x2E,0x07,0xF1,0xB1,0xC0,0x08,0x2E,0x07,0xF1,0xAE,0xC0,0x0E,0x84,0x00, +0x3E,0x07,0xF1,0xAE,0xD5,0x0A,0x2E,0x07,0xF1,0xAE,0xC8,0xF7,0x84,0x01,0x8E,0xC1, +0x49,0xFF,0xBF,0xA1,0xCE,0xEF,0xD5,0xF1,0x84,0x00,0x3E,0x07,0xF1,0xB1,0x44,0x07, +0x30,0x14,0xB4,0x00,0x92,0x10,0x96,0x00,0xFC,0x80,0xFC,0x20,0x46,0x01,0xFF,0xFF, +0x50,0x00,0x0F,0x05,0x49,0xFF,0xBF,0x96,0x46,0x02,0xF0,0xC0,0x50,0x00,0x01,0x00, +0x49,0xFF,0xFF,0xC9,0x80,0xC0,0x46,0x02,0xF0,0xD0,0x50,0x00,0x01,0x00,0x49,0xFF, +0xFF,0xC2,0x97,0xC0,0x46,0x01,0xFC,0x00,0x97,0xB0,0x50,0x00,0x01,0x00,0x88,0x06, +0x49,0xFF,0xBF,0x80,0x46,0x01,0xFC,0x10,0x50,0x00,0x01,0x00,0x88,0x07,0x49,0xFF, +0xBF,0x79,0x46,0x01,0xFC,0x20,0x50,0x00,0x01,0x00,0x88,0x06,0x49,0xFF,0xBF,0x72, +0x46,0x01,0xFC,0x30,0x50,0x00,0x01,0x00,0x88,0x07,0x49,0xFF,0xBF,0x6B,0x46,0x01, +0xFC,0x40,0x50,0x00,0x01,0x00,0x88,0x06,0x49,0xFF,0xBF,0x64,0x46,0x01,0xFC,0x50, +0x50,0x00,0x01,0x00,0x88,0x07,0x49,0xFF,0xBF,0x5D,0x46,0x01,0xFC,0x60,0x50,0x00, +0x01,0x00,0x88,0x06,0x49,0xFF,0xBF,0x56,0x46,0x01,0xFC,0x70,0x50,0x00,0x01,0x00, +0x88,0x07,0x49,0xFF,0xBF,0x4F,0xFC,0xA0,0x44,0x04,0x80,0x0A,0x84,0x24,0xAE,0x40, +0x44,0x17,0x21,0x04,0x46,0x38,0x00,0x00,0xB4,0x01,0x92,0x14,0x40,0x00,0x50,0x08, +0xB6,0x01,0x44,0x07,0x21,0x08,0xB4,0x20,0x92,0x28,0x40,0x10,0xA0,0x08,0xB6,0x20, +0xB4,0x40,0x46,0x1F,0xCF,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x8E,0xB6,0x40,0xB4,0x40, +0xFE,0x9F,0xB6,0x40,0x44,0x27,0x21,0x10,0xB4,0x02,0x94,0x01,0x92,0x01,0xB6,0x02, +0x44,0x27,0x21,0x14,0xB4,0x02,0x92,0x14,0x40,0x00,0x50,0x08,0xB6,0x02,0x44,0x07, +0x21,0x18,0xB4,0x40,0x92,0x48,0x40,0x21,0x20,0x08,0xB6,0x40,0xB4,0x40,0xFE,0x56, +0xB6,0x20,0xB4,0x20,0xFE,0x5F,0xB6,0x20,0x44,0x17,0x21,0x1C,0xB4,0x01,0x92,0x11, +0x40,0x00,0x44,0x08,0xB6,0x01,0xB4,0x41,0x46,0x0F,0x0F,0xFF,0x50,0x00,0x0F,0xFF, +0xFE,0x16,0xB6,0x01,0x44,0x17,0x21,0x00,0x46,0x0F,0xFF,0x0F,0xB4,0x41,0x50,0x00, +0x0F,0xFF,0xFE,0x16,0xB6,0x01,0x44,0x04,0x80,0x07,0x84,0x22,0xAE,0x40,0x44,0x04, +0x10,0x00,0x44,0x24,0x10,0x10,0xAE,0x42,0x84,0x21,0xAE,0x40,0x84,0x20,0xAE,0x53, +0xA6,0x43,0x54,0x10,0x80,0xFE,0xAE,0x43,0x44,0x04,0x10,0x0C,0x84,0x20,0xB6,0x20, +0x84,0x1F,0xAE,0x10,0xDD,0x9E,0x3C,0x0F,0xFC,0x6D,0xDD,0x9E,0xC8,0x04,0x44,0x05, +0x00,0x04,0xD5,0x03,0x44,0x05,0x00,0x14,0xA4,0x00,0x96,0x01,0xDD,0x9E,0x44,0x04, +0x80,0x00,0xA6,0x03,0x96,0x06,0xC0,0x0D,0x44,0x04,0x80,0x03,0x84,0x21,0xAE,0x40, +0x3C,0x0D,0xFC,0x6D,0xC0,0x06,0xFC,0x00,0x3C,0x0D,0xFC,0x6D,0xDD,0x20,0xFC,0x80, +0xDD,0x9E,0x46,0x20,0x00,0x81,0x84,0x3E,0x04,0x31,0x00,0x14,0x96,0x06,0xFE,0x5E, +0xFE,0x0F,0x14,0x01,0x00,0x14,0xDD,0x9E,0x44,0x14,0x70,0x08,0x5A,0x08,0x01,0x09, +0xB4,0x01,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00,0x78,0x23,0xD5,0x05,0xB4,0x01, +0x92,0x10,0x40,0x00,0x40,0x08,0xB6,0x01,0xDD,0x9E,0x44,0x14,0x00,0x0C,0xAE,0x09, +0xDD,0x9E,0x44,0x25,0x10,0x00,0x96,0x17,0xB4,0x62,0x40,0x10,0x20,0x08,0x44,0x0F, +0xF8,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x24,0x80,0x84,0x96,0x06, +0xB4,0x62,0x40,0x10,0x20,0x08,0x44,0x0F,0xFE,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02, +0xDD,0x9E,0x44,0x25,0x10,0x00,0x84,0x3E,0xB4,0x62,0x96,0x06,0xFE,0x5E,0xFE,0x0F, +0xB6,0x02,0xDD,0x9E,0x84,0x00,0x3E,0x07,0xF1,0xBA,0xDD,0x9E,0xFC,0x00,0x49,0x00, +0x0B,0x97,0x5A,0x00,0x01,0x03,0x84,0x03,0x3E,0x07,0xDE,0xD3,0x2E,0x07,0xDE,0xD3, +0x49,0xFF,0xBD,0x79,0x84,0x04,0xFC,0x80,0xFC,0x00,0x3C,0xF7,0xF6,0x56,0x2E,0x07, +0xDE,0xD3,0x5E,0xF7,0x80,0xC8,0xE8,0x0C,0x3C,0xF7,0xF6,0x52,0x5E,0xF7,0xFF,0x39, +0xE9,0x07,0xE4,0x02,0xE8,0x03,0x8C,0x01,0xD5,0x06,0x84,0x03,0xD5,0x07,0xE4,0x02, +0xE9,0x04,0x8E,0x01,0x96,0x00,0xD5,0x02,0x84,0x01,0x3E,0x07,0xDE,0xD3,0x2E,0x07, +0xDE,0xD3,0x49,0xFF,0xBD,0x58,0x2E,0x07,0xDE,0xD3,0x5A,0x08,0x03,0x04,0x84,0x02, +0xFC,0x80,0x84,0x04,0xFC,0x80,0x2E,0x17,0xF1,0xBA,0x8C,0x21,0x96,0x48,0xE2,0x20, +0x3E,0x17,0xF1,0xBA,0x56,0x07,0x80,0x01,0xDD,0x9E,0xFC,0x42,0x81,0x40,0xC1,0x05, +0x84,0xE8,0x5A,0x18,0x01,0x07,0xD5,0x03,0x44,0x70,0x00,0x64,0x84,0xC1,0xD5,0x03, +0x84,0xE1,0x84,0xC0,0x49,0x00,0x1A,0xA0,0x81,0x20,0x4E,0xA3,0x00,0x20,0x49,0x00, +0x0B,0x47,0xC8,0x0C,0x49,0x00,0x1A,0x8E,0xC8,0x06,0x2E,0x17,0xF9,0x14,0xC1,0x07, +0x84,0x01,0xD5,0x05,0x49,0x00,0x19,0x05,0xD5,0x04,0x80,0x09,0x49,0xFF,0xBB,0xBC, +0xF0,0x81,0x49,0x00,0x18,0xFB,0xF0,0x82,0xF1,0x01,0x44,0x00,0x02,0x40,0xF2,0x02, +0x80,0x66,0x80,0x87,0x49,0x00,0x03,0xA8,0xFC,0xC2,0x5A,0xA8,0x01,0x1E,0x49,0x00, +0x18,0xF3,0xF0,0x82,0x4E,0x93,0x00,0x05,0x49,0x00,0x1A,0xF2,0xD5,0x03,0x49,0x00, +0x1A,0xEC,0xF0,0x81,0x80,0x09,0x49,0xFF,0xFD,0x1B,0xF0,0x83,0x44,0x10,0x00,0x42, +0xF0,0x02,0xF2,0x01,0xF3,0x03,0xFD,0x23,0x49,0x00,0x03,0xA1,0xF0,0x01,0x44,0x10, +0x00,0x42,0x49,0xFF,0xB7,0x76,0xFC,0xC2,0xFC,0x20,0x51,0xFF,0xFB,0x80,0x44,0x10, +0x00,0xC0,0x84,0x40,0x80,0xE0,0x50,0x0F,0x81,0x80,0x49,0x00,0x03,0x01,0x80,0x1F, +0x44,0x10,0x00,0xC0,0x49,0x00,0x03,0x0B,0x84,0xC0,0x49,0x00,0x21,0x4C,0xE6,0x03, +0x50,0x0F,0x81,0x80,0xE8,0x10,0x38,0x00,0x1A,0x02,0x90,0x08,0x96,0x43,0x38,0x0F, +0x99,0x11,0x50,0x00,0x7F,0xC0,0x94,0x05,0x9A,0x08,0x40,0x00,0x20,0x08,0x38,0x03, +0x9A,0x0A,0xD5,0x0C,0x38,0x10,0x1A,0x02,0x38,0x0F,0x99,0x11,0x90,0x28,0x96,0x4B, +0x8A,0x20,0x40,0x10,0xA0,0x08,0x38,0x13,0x9A,0x0A,0x8C,0xC1,0x5A,0x68,0xC0,0xDF, +0x80,0x07,0x80,0x26,0x84,0x40,0x44,0x30,0x02,0x52,0x49,0x00,0x03,0x39,0x51,0xFF, +0x84,0x80,0xFC,0xA0,0xFC,0x21,0x80,0xC0,0x49,0x00,0x18,0xBB,0x84,0x00,0x49,0x00, +0x1B,0x52,0x84,0x01,0x49,0x00,0x18,0x99,0x84,0x01,0x49,0x00,0x17,0xE1,0x84,0x01, +0x49,0x00,0x05,0x1F,0x49,0x00,0x1A,0x10,0xB6,0x1F,0x49,0x00,0x1A,0x10,0xF0,0x81, +0xF2,0x01,0x84,0x01,0xB4,0x3F,0x49,0x00,0x1A,0x86,0x84,0x02,0x84,0x21,0x49,0x00, +0x06,0x05,0x84,0x01,0x49,0x00,0x1B,0x37,0x44,0x74,0x50,0x28,0x2E,0x07,0xF1,0x86, +0xC8,0x16,0x2E,0x07,0xF1,0x77,0xC8,0x0F,0xB4,0x27,0x92,0x23,0x96,0x46,0x5A,0x10, +0x01,0x0B,0xB4,0x27,0x92,0x21,0x96,0x46,0xC1,0x06,0x3C,0x0D,0xFE,0x44,0x5A,0x08, +0x01,0xEF,0xD5,0x05,0x49,0xFF,0xF3,0x34,0x5A,0x08,0x02,0xF9,0x49,0xFF,0xF9,0xDD, +0x84,0x00,0x3E,0x07,0xF1,0x86,0x49,0x00,0x18,0x72,0x49,0xFF,0xF9,0xB7,0x49,0xFF, +0xFC,0x87,0x50,0x13,0x04,0x80,0x80,0xA6,0x9A,0xAE,0x38,0x21,0x00,0x11,0x1A,0x22, +0x80,0x01,0xD9,0xFB,0xFC,0xA1,0xFC,0x21,0x80,0xC0,0x2E,0x0F,0xF1,0xB9,0xC8,0x29, +0x2E,0x1F,0xF1,0xB8,0xC9,0x26,0xCE,0x52,0x49,0xFF,0xF9,0xA0,0x49,0xFF,0xFC,0x70, +0x80,0xE0,0x49,0x00,0x19,0xC1,0xF0,0x81,0x49,0x00,0x0A,0x6A,0xC8,0x0C,0x49,0x00, +0x19,0xB1,0xC8,0x06,0x2E,0x17,0xF9,0x14,0xC1,0x07,0x84,0x01,0xD5,0x05,0x49,0x00, +0x18,0x28,0xD5,0x04,0xF0,0x01,0x49,0xFF,0xBA,0xDF,0x84,0x20,0x38,0x23,0x85,0x11, +0x40,0x21,0x20,0x08,0x38,0x20,0x06,0x0A,0x8C,0x21,0x5A,0x1A,0x40,0xF9,0xD5,0x2E, +0x2E,0x17,0xEC,0xA0,0xE0,0x20,0xE9,0x2A,0x2E,0xFF,0xF1,0xB8,0x2E,0x17,0xEC,0x9F, +0xE0,0x2F,0xE9,0x24,0xCE,0x23,0x49,0x00,0x18,0x06,0xF0,0x81,0x49,0x00,0x20,0x93, +0xE6,0x03,0xE9,0x04,0xF0,0x01,0x49,0xFF,0xC0,0x9E,0x3C,0x14,0x0A,0xD3,0xF5,0x01, +0x84,0x03,0xFE,0x44,0x50,0x22,0x84,0x80,0xD5,0x33,0x8C,0x01,0x5A,0x00,0x12,0x2E, +0x38,0xF2,0x81,0x11,0xE0,0x2F,0xE8,0xFA,0x84,0x00,0x3E,0x07,0xF1,0xB8,0x49,0x00, +0x17,0xD9,0x84,0x00,0x84,0x21,0x49,0xFF,0xFE,0xCA,0x2E,0x1F,0xF1,0xB9,0x2E,0x07, +0xEC,0xA0,0xE0,0x01,0xE9,0x07,0x2E,0x0F,0xF1,0xB8,0x2E,0x27,0xEC,0x9F,0xE0,0x40, +0xE8,0x0A,0x5A,0x68,0x01,0x11,0x84,0x00,0x3E,0x07,0xF1,0xB9,0x3E,0x07,0xF1,0xB8, +0x80,0x06,0xD5,0x10,0x5A,0x68,0x01,0x08,0x8C,0x21,0x8C,0x01,0x3E,0x17,0xF1,0xB9, +0x3E,0x07,0xF1,0xB8,0x84,0x00,0xD5,0x06,0x50,0x52,0x80,0x24,0xD2,0xD9,0x84,0x00, +0xD5,0xD0,0xFC,0xA1,0x4E,0x05,0x00,0x19,0x8E,0x01,0x40,0x10,0x04,0x0A,0xFE,0x0F, +0x40,0x10,0x08,0x0A,0xFE,0x0F,0x40,0x10,0x10,0x0A,0xFE,0x0F,0x40,0x10,0x20,0x0A, +0xFE,0x0F,0x40,0x10,0x40,0x0A,0xFE,0x47,0x8C,0x21,0x84,0x00,0x90,0x21,0xC1,0x03, +0x8C,0x01,0xD5,0xFD,0xDD,0x9E,0x84,0x00,0xDD,0x9E,0xFC,0x4D,0x44,0x00,0xF1,0x88, +0x50,0xFF,0x80,0x18,0x80,0xC0,0x80,0x2F,0x3A,0x23,0x14,0x04,0x3A,0x20,0x94,0x24, +0x81,0x46,0x83,0x81,0x3A,0x25,0x14,0x04,0x44,0x90,0xF1,0xB0,0x3A,0x2E,0x14,0x24, +0x80,0x89,0x3A,0x25,0x0C,0x00,0x80,0xBF,0x3A,0x2E,0x0C,0x20,0x3A,0x22,0x0C,0x04, +0x3A,0x22,0x8C,0x24,0x45,0xE2,0x16,0x30,0xA5,0xE0,0x3A,0x27,0x94,0x04,0x81,0xFE, +0x81,0x1F,0x3A,0x27,0x94,0x24,0x3A,0x20,0x94,0x04,0x80,0x2F,0x3A,0x20,0x94,0x24, +0x3A,0x2E,0x0C,0x00,0x3A,0x20,0x8C,0x20,0x44,0x12,0x16,0x24,0x3A,0x24,0x0C,0x04, +0x3A,0x20,0x8C,0x24,0xAD,0xC8,0xB0,0x50,0x3A,0x20,0x14,0x04,0x80,0x01,0x3A,0x20, +0x14,0x24,0x81,0x00,0x3A,0x23,0x14,0x04,0x3A,0x24,0x14,0x24,0xB1,0x83,0x3A,0x25, +0x0C,0x00,0x80,0x86,0x3A,0x24,0x0C,0x20,0x3A,0x24,0x8C,0x04,0x44,0x92,0x03,0x48, +0x3A,0x22,0x0C,0x24,0x3A,0x20,0x94,0x04,0x80,0x29,0x3A,0x20,0x94,0x24,0x3A,0x20, +0x14,0x04,0x80,0x01,0x3A,0x20,0x14,0x24,0x3A,0x24,0x0C,0x00,0x3A,0x20,0x0C,0x20, +0x44,0x02,0x03,0x3C,0x3A,0x23,0x0C,0x04,0x3A,0x20,0x0C,0x24,0xAD,0xC0,0xFC,0xCD, +0xFC,0x48,0x83,0x81,0x50,0xAF,0x80,0x14,0x44,0x10,0xF1,0xBC,0x80,0xC0,0x80,0x0A, +0x81,0x22,0x3A,0x20,0x8C,0x04,0x3A,0x20,0x0C,0x24,0xA4,0x48,0xFA,0x40,0xAC,0x40, +0x50,0x8F,0x80,0x30,0x84,0x20,0xB0,0x08,0x49,0x00,0x27,0x8A,0x80,0x08,0x84,0x20, +0xFA,0x40,0x49,0x00,0x27,0x85,0x5A,0x98,0x01,0x03,0x87,0x80,0xC6,0x04,0x44,0x12, +0x16,0xD4,0xD5,0x03,0x44,0x12,0x16,0xE0,0x58,0x04,0x80,0x01,0x8C,0x01,0x84,0xE0, +0xB6,0x1F,0x8D,0x21,0x84,0x83,0x84,0x61,0x38,0x05,0x1C,0x00,0xF3,0x83,0xF4,0x82, +0x38,0x60,0x80,0x00,0xB4,0x1F,0x8C,0xC1,0xFF,0x84,0x97,0xB1,0x40,0x63,0x24,0xD6, +0x80,0x06,0xF1,0x81,0x49,0xFF,0xFF,0x48,0xF4,0x02,0x97,0x78,0x40,0x52,0x90,0xB7, +0xF3,0x03,0x38,0x24,0x16,0x02,0x8C,0x08,0x41,0xE1,0x28,0x08,0x40,0x20,0x70,0x01, +0x40,0x01,0x80,0x0C,0x40,0x60,0x18,0xD7,0xB0,0x08,0x94,0x94,0x38,0x00,0x16,0x02, +0x88,0x5E,0x40,0x00,0x28,0x08,0x88,0xC0,0xB0,0x08,0x8C,0xE1,0x38,0x24,0x16,0x0A, +0x38,0x60,0x16,0x0A,0xF1,0x01,0x5A,0x78,0x0A,0xD1,0x80,0x28,0x49,0xFF,0xB2,0x57, +0xFC,0xC8,0x80,0x80,0x84,0x40,0x9A,0xE0,0x96,0xD8,0xE2,0x61,0xE8,0x08,0x08,0x32, +0x00,0x01,0xE2,0x62,0x40,0x31,0x3C,0x1B,0x80,0x43,0xD5,0xF6,0x9C,0x11,0x96,0x00, +0xDD,0x9E,0xFC,0x40,0x51,0xFF,0xFB,0xE0,0x44,0x20,0x04,0x00,0x81,0x40,0x81,0x21, +0xB0,0x08,0x84,0x20,0x49,0x00,0x27,0x24,0xB1,0x81,0x44,0x10,0xF1,0xBC,0x80,0x06, +0x3A,0x20,0x8C,0x04,0x3A,0x20,0x0C,0x24,0xA4,0x48,0xB1,0xC4,0xAC,0x40,0xFA,0x40, +0x80,0x07,0x84,0x20,0x49,0x00,0x27,0x14,0x4E,0xA3,0x00,0x3E,0x84,0x2A,0x44,0x02, +0x16,0xE0,0x49,0xFF,0xFF,0xD0,0x80,0x20,0x80,0x4A,0x84,0xA3,0x44,0xA2,0x16,0x30, +0x45,0xE2,0x16,0x24,0x96,0x10,0x40,0x00,0x14,0x17,0x40,0x30,0x20,0x08,0xB1,0x08, +0x41,0xC2,0x0C,0x00,0x84,0x60,0x54,0x81,0x80,0xFF,0xE3,0x01,0xE8,0x12,0x38,0x83, +0x08,0x00,0x38,0x85,0x22,0x02,0x38,0x84,0x0D,0x01,0x54,0xF4,0x03,0xFF,0x38,0x8E, +0x0E,0x02,0x40,0x84,0x28,0x08,0x89,0x0F,0x38,0x8E,0x0E,0x0A,0x8C,0x61,0xD5,0xEC, +0x38,0x33,0x08,0x00,0x8C,0x41,0x38,0x3F,0x0C,0x00,0x8A,0x69,0x54,0x81,0x83,0xFF, +0x38,0x33,0x82,0x02,0x40,0x31,0xA8,0x08,0x88,0x68,0x38,0x33,0x82,0x0A,0x5A,0x28, +0x0A,0xD3,0xD5,0x3C,0x84,0x2A,0x44,0x02,0x16,0xD4,0x49,0xFF,0xFF,0x94,0x80,0x20, +0x84,0x40,0x84,0xA3,0x44,0xA2,0x03,0x48,0x45,0xE2,0x03,0x3C,0x97,0x10,0x40,0x42, +0x14,0x97,0x40,0x32,0x20,0x08,0xB0,0x08,0x41,0xC0,0x0C,0x00,0x84,0x60,0x54,0x81, +0x80,0xFF,0xE3,0x01,0xE8,0x12,0x38,0x83,0x08,0x00,0x38,0x85,0x22,0x02,0x38,0x84, +0x0D,0x01,0x54,0xF4,0x03,0xFF,0x38,0x8E,0x0E,0x02,0x40,0x84,0x28,0x08,0x89,0x0F, +0x38,0x8E,0x0E,0x0A,0x8C,0x61,0xD5,0xEC,0x38,0x33,0x08,0x00,0x8C,0x41,0x38,0x3F, +0x0C,0x00,0x8A,0x69,0x54,0x81,0x83,0xFF,0x38,0x33,0x92,0x02,0x40,0x31,0xA8,0x08, +0x88,0x68,0x38,0x33,0x92,0x0A,0x5A,0x28,0x0A,0xD3,0xB0,0x08,0x49,0xFF,0xB1,0xFE, +0x80,0x07,0x49,0xFF,0xB2,0x34,0x51,0xFF,0x84,0x20,0xFC,0xC0,0xFC,0x00,0x49,0x00, +0x16,0x32,0x50,0x30,0x04,0x80,0x50,0x50,0x00,0x22,0x50,0x20,0x00,0x10,0x80,0x20, +0x22,0x40,0x80,0x00,0x22,0x62,0x80,0x00,0x1A,0x60,0x80,0x01,0x1A,0x42,0xFF,0xFF, +0xDA,0xF8,0x50,0x00,0x00,0x24,0x4C,0x01,0xFF,0xF0,0xFC,0x80,0xFC,0x40,0x51,0xFF, +0xFD,0xB8,0xF2,0x81,0x81,0x20,0x44,0x20,0x02,0x40,0x81,0x41,0xB0,0x02,0x44,0x10, +0xF1,0xD4,0x80,0xE3,0x80,0xC4,0x49,0x00,0x26,0x45,0xF0,0x01,0x49,0xFF,0xFA,0x48, +0x81,0x00,0x49,0x00,0x08,0x45,0xC8,0x0C,0x49,0x00,0x17,0x8C,0xC8,0x06,0x2E,0x17, +0xF9,0x14,0xC1,0x07,0x84,0x01,0xD5,0x05,0x49,0x00,0x16,0x03,0xD5,0x04,0x84,0x00, +0x49,0xFF,0xB8,0xBA,0x40,0x34,0x1C,0x00,0x95,0x32,0x84,0x40,0xFB,0x02,0x84,0xC0, +0x99,0x56,0x97,0x69,0x4E,0x92,0x00,0x13,0xB0,0x42,0x81,0xE6,0x38,0x10,0x94,0x00, +0x42,0xF0,0xA0,0x73,0x40,0x17,0x88,0x08,0x88,0x24,0x38,0x70,0x04,0x02,0x38,0x11, +0xBD,0x01,0x90,0xE8,0x8A,0xE1,0x38,0x74,0x95,0x09,0x4E,0xA2,0x00,0x06,0x38,0x11, +0x95,0x11,0x38,0x15,0x15,0x09,0x8C,0xC1,0x5A,0x68,0x12,0xE4,0x8C,0x52,0x96,0x91, +0x5A,0x2A,0x40,0xDF,0x49,0xFF,0xFF,0x9C,0x51,0xFF,0x82,0x48,0xFC,0xC0,0xFC,0x00, +0x80,0x43,0x84,0x60,0x80,0x83,0x49,0xFF,0xFF,0xAB,0xFC,0x80,0xFC,0x20,0xFD,0x30, +0x80,0x02,0x49,0xFF,0xB8,0x81,0x95,0xFA,0x80,0xA6,0x88,0xE6,0x50,0x00,0x09,0x48, +0xD7,0x04,0xA2,0x41,0xAA,0x69,0xD5,0xFD,0xFC,0xA0,0xFC,0x20,0xFD,0x30,0x84,0x00, +0x49,0xFF,0xF9,0xEE,0x95,0xF9,0x80,0xA6,0x88,0xE6,0x50,0x00,0x04,0xA4,0xD7,0x06, +0x2A,0x10,0x00,0x01,0x1A,0x12,0x80,0x01,0xD5,0xFB,0xFC,0xA0,0xFC,0x20,0x80,0xE0, +0x80,0x02,0x80,0xC1,0x49,0xFF,0xB8,0x60,0x84,0x40,0x96,0x51,0xE2,0x26,0xE8,0x07, +0x38,0x10,0x0A,0x02,0x38,0x13,0x8A,0x0A,0x8C,0x41,0xD5,0xF8,0xFC,0xA0,0xFC,0x40, +0x80,0xC0,0x80,0x02,0x81,0x23,0x80,0xE1,0x49,0xFF,0xF9,0xCA,0x81,0x40,0x84,0x6C, +0x5A,0x98,0x02,0x07,0x49,0x00,0x17,0x12,0x84,0x63,0xC0,0x02,0x80,0x69,0x80,0x0A, +0x40,0x91,0x84,0x08,0x84,0x20,0x40,0xA1,0x80,0x13,0x96,0x89,0xE2,0x47,0xE8,0x1E, +0x84,0xA0,0x80,0x85,0x40,0x82,0x80,0x13,0xE3,0x0A,0xE8,0x06,0x38,0x80,0x15,0x11, +0x8C,0xA1,0x88,0x88,0xD5,0xF8,0x5C,0xF1,0x00,0x40,0x40,0x42,0x0C,0x96,0xE8,0x09, +0x54,0x51,0x00,0x01,0x95,0x6D,0x92,0x41,0x88,0x45,0x38,0x43,0x09,0x09,0xD5,0x03, +0x38,0x43,0x05,0x09,0x8C,0x21,0x88,0x09,0xD5,0xE1,0xFC,0xC0,0xFC,0x40,0x81,0x20, +0x95,0x9A,0x80,0x02,0x80,0xE1,0x97,0xB1,0x49,0xFF,0xB8,0x16,0x88,0xC0,0x84,0x40, +0x96,0x51,0xE2,0x27,0xE8,0x07,0x38,0x04,0x8A,0x02,0x38,0x03,0x0A,0x0A,0x8C,0x41, +0xD5,0xF8,0xFC,0xC0,0xFC,0x00,0x94,0x02,0x88,0x01,0x4C,0x10,0x00,0x0F,0xB4,0xC1, +0x2A,0x51,0x00,0x01,0x40,0x52,0xA0,0x08,0x8A,0xA6,0xFF,0xA4,0x42,0x61,0x94,0x73, +0x40,0x63,0x10,0xD6,0xAB,0x89,0xD5,0xF2,0xFC,0x80,0xFC,0x40,0x84,0xE0,0x97,0xB9, +0xE2,0xC1,0xE8,0x12,0x38,0x91,0x9E,0x02,0xB4,0xC0,0x40,0x94,0xA0,0x08,0x8B,0x26, +0xFF,0xAC,0x42,0x62,0x24,0x73,0x40,0x63,0x14,0xD6,0xAB,0x81,0x90,0xC8,0x38,0x61, +0x1D,0x09,0x8C,0xE1,0xD5,0xED,0xFC,0xC0,0x2E,0x07,0xF2,0xE3,0xDD,0x9E,0x3E,0x07, +0xF2,0xE3,0xDD,0x9E,0xFC,0x01,0x84,0x1F,0xB6,0x1F,0xF0,0x81,0x80,0x1F,0x49,0xFF, +0xB3,0xAF,0x84,0x01,0x49,0xFF,0xB3,0xA1,0x84,0x01,0x49,0xFF,0xB4,0x17,0x84,0x22, +0x84,0x01,0x80,0x61,0x80,0xA0,0x44,0x40,0x00,0x3F,0x84,0x44,0x49,0xFF,0xB3,0x1A, +0x84,0x01,0x49,0xFF,0xF8,0xA1,0x84,0x01,0x80,0x20,0x80,0x40,0x49,0xFF,0xB3,0x57, +0x84,0x0A,0x49,0xFF,0xB3,0x7F,0x46,0x00,0x42,0x10,0x50,0x00,0x08,0x20,0x80,0x20, +0x84,0x40,0x49,0xFF,0xB3,0x6A,0x49,0xFF,0xB5,0xCD,0x84,0x01,0x49,0xFF,0xB5,0xBC, +0x84,0x00,0x49,0xFF,0xF9,0x08,0x44,0x04,0x55,0x08,0x44,0x1F,0xFF,0x7F,0xB4,0x40, +0xFE,0x56,0xB6,0x20,0xB4,0x20,0x58,0x10,0x80,0x10,0xB6,0x20,0x84,0x21,0x49,0xFF, +0xB2,0x78,0x84,0x00,0x3E,0x07,0xF2,0xE3,0xFC,0x81,0xFC,0x54,0x44,0x00,0xF4,0x14, +0x50,0xFF,0x80,0x10,0x80,0x20,0x80,0x8F,0x3A,0x20,0x8C,0x04,0x50,0x8F,0x80,0x1C, +0x3A,0x22,0x0C,0x24,0x3A,0x20,0x0C,0x04,0x80,0x08,0xA4,0x48,0xB1,0x8A,0x3A,0x20, +0x0C,0x24,0x44,0x00,0xF4,0x20,0xB6,0x3F,0x80,0x26,0x3A,0x20,0x14,0x04,0x3A,0x20, +0x94,0x24,0xB4,0x00,0xB0,0x54,0xF0,0x83,0x44,0x00,0xF4,0x34,0x80,0xE1,0x3A,0x20, +0x14,0x04,0x3A,0x23,0x94,0x24,0xF7,0x81,0x83,0xC7,0x3A,0x20,0x14,0x04,0x3A,0x2F, +0x14,0x24,0x3A,0x20,0x0C,0x00,0x50,0xAF,0x80,0x3C,0x44,0x00,0xF4,0x5C,0x80,0xEA, +0x3A,0x2F,0x0C,0x20,0x3A,0x20,0x14,0x04,0x3A,0x23,0x94,0x24,0xB4,0x00,0x50,0x9F, +0x80,0x78,0xF0,0x82,0x44,0x00,0xF4,0x70,0x80,0xE9,0x3A,0x20,0x14,0x04,0x3A,0x23, +0x94,0x24,0x83,0x87,0x3A,0x20,0x14,0x04,0x3A,0x2E,0x14,0x24,0x3A,0x20,0x0C,0x00, +0x3A,0x2E,0x0C,0x20,0x44,0x02,0x16,0x84,0x3A,0x25,0x14,0x04,0x3A,0x20,0x14,0x24, +0x80,0x40,0xF0,0x02,0xB6,0x02,0x44,0x02,0x16,0x5C,0x3A,0x24,0x94,0x04,0x3A,0x20, +0x14,0x24,0x3A,0x23,0x94,0x04,0x81,0x20,0x3A,0x20,0x14,0x24,0x3A,0x2E,0x0C,0x00, +0x3A,0x20,0x0C,0x20,0x44,0x02,0x16,0xE0,0x3A,0x27,0x8C,0x04,0x3A,0x20,0x0C,0x24, +0x80,0x40,0x02,0x0F,0x80,0x00,0xAC,0x10,0x44,0x02,0x16,0xD4,0x3A,0x24,0x0C,0x04, +0x3A,0x20,0x0C,0x24,0x80,0x40,0x02,0x0F,0x80,0x00,0xAC,0x10,0x44,0x02,0x16,0xC0, +0x3A,0x23,0x14,0x04,0x3A,0x20,0x14,0x24,0x80,0x40,0xF0,0x03,0xB6,0x02,0x44,0x02, +0x16,0x98,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x80,0x20,0xF0,0x01,0x3A,0x20, +0x14,0x04,0x80,0x01,0x3A,0x20,0x14,0x24,0x3A,0x2F,0x0C,0x00,0x3A,0x20,0x0C,0x20, +0xFC,0xD4,0xFC,0x00,0x49,0xFF,0xB2,0x3D,0xFC,0x80,0xFC,0x00,0xC8,0x06,0x44,0x02, +0x16,0x98,0x44,0x12,0x16,0xC0,0xD5,0x05,0x44,0x02,0x16,0x5C,0x44,0x12,0x16,0x84, +0x49,0xFF,0xB3,0x29,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xB2,0x83,0x49,0xFF,0xF7,0xD8, +0x84,0x00,0xFC,0x80,0xFC,0x00,0x84,0x20,0x44,0x20,0x00,0x50,0x44,0x02,0x16,0xF0, +0x49,0x00,0x24,0x7E,0xFC,0x80,0x2E,0x07,0xF2,0xDC,0xDD,0x9E,0xFC,0x00,0x2E,0x07, +0xF2,0xE3,0xE6,0x06,0xE9,0x0F,0x84,0x04,0x84,0x84,0x3E,0x07,0xF2,0x85,0x84,0x00, +0x80,0x20,0x44,0x22,0x16,0x98,0x44,0x32,0x16,0xC0,0x80,0xA4,0x49,0xFF,0xF7,0xF7, +0xFC,0x80,0x84,0x01,0x3E,0x07,0xF2,0x85,0x84,0x00,0x49,0xFF,0xF7,0xD5,0x84,0x00, +0x84,0x23,0x49,0xFF,0xAD,0xDC,0x46,0x10,0x00,0x81,0x83,0x81,0xB8,0x21,0x92,0x0B, +0x40,0x00,0x2C,0x08,0x58,0x00,0x01,0x30,0xB8,0xA1,0xBA,0x23,0x44,0x0F,0xF0,0xFF, +0xFE,0x16,0xB8,0xA3,0xFC,0x80,0xFC,0x00,0x2E,0x47,0xF2,0xE3,0x84,0x00,0x80,0x20, +0x44,0x22,0x16,0x5C,0x44,0x32,0x16,0x84,0x80,0xA4,0x49,0xFF,0xF7,0xD0,0xFC,0x80, +0x84,0x00,0x3E,0x07,0xF2,0x85,0x3E,0x07,0xF2,0x84,0xDD,0x9E,0xFC,0x00,0x80,0x41, +0xC9,0x05,0x5A,0x08,0x01,0x13,0x80,0x01,0xD5,0x05,0x84,0x20,0x5A,0x08,0x01,0x09, +0x84,0x02,0x94,0x01,0x96,0x40,0xCA,0x04,0x44,0x02,0x16,0xE0,0xD5,0x03,0x44,0x02, +0x16,0xD4,0x49,0xFF,0xB4,0xFC,0xFC,0x80,0xD5,0xF8,0xFC,0x00,0xC9,0x0C,0x84,0x00, +0x49,0xFF,0xB4,0x79,0x84,0x00,0x80,0x20,0x49,0xFF,0xB4,0xA6,0x84,0x00,0x49,0xFF, +0xB4,0xD8,0xFC,0x80,0x80,0xC0,0x5A,0x08,0x01,0xF4,0x84,0x02,0x49,0xFF,0xB4,0x6B, +0x80,0x06,0x84,0x20,0x49,0xFF,0xB4,0x98,0x80,0x06,0xD5,0xF2,0xFC,0x01,0xB6,0x1F, +0xF1,0x81,0x49,0xFF,0xFF,0xE4,0xB4,0x1F,0xF1,0x01,0x49,0xFF,0xFF,0xC9,0xFC,0x81, +0xFC,0x23,0x80,0xE0,0xC8,0x04,0x84,0xA4,0x84,0x23,0xD5,0x03,0x84,0xA6,0x84,0x22, +0x84,0x40,0x8E,0xA1,0x84,0x02,0x80,0x62,0x80,0x82,0x97,0x68,0x49,0xFF,0xF7,0x7F, +0x44,0x10,0xF4,0x98,0x80,0x1F,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20, +0x8C,0x00,0x3A,0x20,0x0C,0x20,0x80,0x1F,0x49,0xFF,0xB3,0x1E,0x84,0x00,0x49,0xFF, +0xB6,0x66,0x84,0x0C,0x80,0xDF,0x49,0xFF,0xB6,0x8D,0x5A,0x78,0x01,0x06,0x80,0x07, +0x80,0x27,0x49,0xFF,0xFF,0xCD,0xFC,0xA3,0xFC,0x00,0x84,0x46,0xC8,0x02,0x84,0x44, +0x84,0x20,0x3E,0x17,0xF2,0xDD,0x50,0x11,0x00,0x13,0x40,0x10,0x88,0x36,0x3E,0x17, +0xF2,0xDE,0x49,0xFF,0xFF,0xC7,0x84,0x02,0x49,0x00,0x12,0xD8,0xFC,0x80,0xFC,0x49, +0x80,0xE0,0x44,0x02,0x00,0x01,0xF0,0x86,0x44,0x01,0x00,0x02,0xF0,0x87,0x44,0x00, +0x80,0x04,0xF0,0x88,0x44,0x00,0x40,0x08,0xF0,0x89,0x44,0x00,0x20,0x10,0xF0,0x8A, +0x44,0x00,0x10,0x20,0xF0,0x8B,0x44,0x00,0x08,0x40,0xF0,0x8C,0x44,0x00,0x04,0x80, +0xF0,0x8D,0x44,0x00,0x03,0x00,0xF0,0x8E,0x3C,0x0D,0xF7,0xE5,0xF0,0x8F,0xF0,0x90, +0xF0,0x91,0xC7,0x08,0x44,0x92,0x16,0x84,0x44,0xA2,0x16,0x5C,0x85,0x02,0x87,0x85, +0xD5,0x07,0x85,0x03,0x44,0x92,0x16,0xC0,0x44,0xA2,0x16,0x98,0x83,0x88,0xB0,0x06, +0x49,0xFF,0xB2,0x94,0x80,0xDF,0xCF,0x11,0x44,0x00,0xF4,0xB0,0x80,0x3F,0x3A,0x20, +0x14,0x04,0x3A,0x20,0x94,0x24,0x3A,0x20,0x0C,0x00,0x3A,0x20,0x8C,0x20,0x80,0x1F, +0x49,0xFF,0xB2,0xBA,0x80,0x07,0xD5,0x10,0x44,0x00,0xF4,0xC8,0x80,0x3F,0x3A,0x20, +0x14,0x04,0x3A,0x20,0x94,0x24,0x3A,0x20,0x0C,0x00,0x3A,0x20,0x8C,0x20,0x80,0x1F, +0x49,0xFF,0xB2,0xAA,0x84,0x01,0x49,0x00,0x13,0x50,0x2E,0x47,0xF2,0xE3,0x84,0x00, +0x80,0x4A,0x80,0x69,0x80,0xA4,0x80,0x20,0x49,0xFF,0xF6,0xF1,0x2E,0x17,0xF2,0xE3, +0x84,0x00,0x38,0x15,0x05,0x01,0x49,0xFF,0xB3,0x3D,0x2E,0x17,0xF2,0xE3,0x84,0x03, +0x38,0x15,0x05,0x01,0x49,0xFF,0xB3,0x36,0x2E,0x07,0xF2,0xE3,0x84,0x20,0x38,0x04, +0x80,0x00,0x49,0xFF,0xB4,0x32,0x2E,0x07,0xF2,0xE3,0x84,0x23,0x38,0x04,0x80,0x00, +0x49,0xFF,0xB4,0x2B,0x44,0x00,0x02,0x40,0x49,0xFF,0xB5,0xC9,0x80,0x08,0x49,0xFF, +0xB5,0xF1,0x5A,0x78,0x01,0x0A,0x2E,0x07,0xF2,0xE3,0x40,0xFE,0x00,0x06,0xE9,0x04, +0x80,0x07,0x80,0x27,0xD5,0x03,0x84,0x00,0x80,0x27,0x49,0xFF,0xFF,0x29,0xFC,0xC9, +0xFC,0x00,0x2E,0x07,0xED,0x2E,0x2E,0x37,0xED,0x2F,0xE6,0x04,0xE8,0x03,0x84,0x01, +0xD5,0x06,0xE6,0x1C,0xE9,0x03,0xFA,0x0A,0xD5,0x02,0x8E,0x02,0x3E,0x07,0xF2,0xD8, +0x84,0x09,0x40,0x11,0x80,0x17,0x9E,0x5B,0xE6,0x2C,0x96,0x00,0xE9,0x0E,0xE6,0x02, +0xE9,0x1B,0xE6,0x07,0xE8,0x1D,0x5A,0x00,0x02,0x07,0x9E,0x42,0x96,0x48,0x5A,0x00, +0x06,0x1B,0xD5,0x0F,0x84,0x21,0xD5,0x0D,0xC0,0x19,0x5A,0x00,0x08,0x1C,0x5A,0x00, +0x01,0x08,0x9E,0x42,0x96,0x48,0x84,0x88,0x5A,0x00,0x07,0x19,0xD5,0x02,0x84,0x20, +0x9C,0x82,0x97,0x10,0xD5,0x14,0x84,0x83,0x84,0x02,0x84,0x21,0xD5,0x10,0x84,0x87, +0x84,0x06,0xD5,0x0C,0x84,0x87,0x84,0x24,0xD5,0x0A,0x84,0x82,0x84,0x01,0x84,0x20, +0xD5,0x06,0x80,0x80,0x84,0x26,0x84,0x07,0xD5,0x02,0x84,0x25,0x84,0x41,0x40,0x01, +0x00,0x0C,0x40,0x11,0x04,0x0C,0xFE,0x0F,0x40,0x21,0x10,0x0C,0xE6,0x69,0xFE,0x17, +0xE8,0x04,0x58,0x00,0x0E,0x00,0xD5,0x05,0x40,0x00,0x24,0x08,0x58,0x00,0x01,0xC0, +0x2E,0x17,0xF2,0xD8,0x3C,0x0F,0xF7,0xE5,0xFA,0x0F,0x40,0x00,0x04,0x0C,0x80,0x20, +0x84,0x40,0x49,0xFF,0xB0,0xEA,0xFC,0x80,0xFC,0x49,0xC1,0x07,0x44,0x92,0x16,0x5C, +0x85,0x42,0x44,0x72,0x16,0x84,0xD5,0x06,0x44,0x92,0x16,0x98,0x85,0x43,0x44,0x72, +0x16,0xC0,0x5A,0x08,0x03,0x32,0xB1,0x86,0x44,0x10,0xF4,0xE0,0x80,0x06,0x3A,0x20, +0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20, +0x94,0x00,0x44,0x10,0xF4,0x98,0x3A,0x20,0x14,0x20,0x80,0x1F,0x3A,0x20,0x94,0x04, +0x3A,0x20,0x14,0x24,0x3A,0x20,0x8C,0x00,0x3A,0x20,0x0C,0x20,0x2E,0x47,0xF2,0xE3, +0x84,0x20,0x80,0x49,0x80,0x67,0x80,0xA4,0x84,0x01,0x49,0xFF,0xF6,0x28,0x80,0x06, +0x49,0xFF,0xB1,0x9C,0x80,0x1F,0x49,0xFF,0xB1,0xCF,0x84,0x00,0x49,0xFF,0xB5,0x17, +0x81,0x5F,0x84,0x0C,0xD5,0x35,0x5A,0x08,0x02,0x36,0xB1,0x86,0x44,0x10,0xF5,0x10, +0x81,0x06,0x3A,0x20,0x94,0x04,0x3A,0x24,0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x24, +0x14,0x24,0x3A,0x20,0x94,0x00,0x44,0x10,0xF4,0xB0,0x81,0xFF,0x3A,0x24,0x14,0x20, +0x3A,0x20,0x94,0x04,0x3A,0x27,0x94,0x24,0x3A,0x20,0x8C,0x00,0x3A,0x27,0x8C,0x20, +0x49,0x00,0x12,0x53,0x2E,0x47,0xF2,0xE3,0x84,0x00,0x80,0x20,0x80,0x49,0x80,0x67, +0x80,0xA4,0x49,0xFF,0xF5,0xF4,0x80,0x06,0x49,0xFF,0xB1,0x68,0x80,0x1F,0x49,0xFF, +0xB1,0x9B,0x44,0x00,0x02,0x40,0x49,0xFF,0xB4,0xE2,0x81,0x1F,0x80,0x0A,0x49,0xFF, +0xB5,0x09,0xFC,0xC9,0xFC,0x49,0x44,0x10,0xF5,0x10,0xB1,0x86,0x80,0xE0,0x80,0x06, +0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24, +0x3A,0x20,0x94,0x00,0x3A,0x20,0x14,0x20,0xC7,0x07,0x44,0x82,0x16,0x5C,0x85,0x22, +0x44,0xA2,0x16,0x84,0xD5,0x06,0x44,0x82,0x16,0x98,0x85,0x23,0x44,0xA2,0x16,0xC0, +0x84,0x00,0x49,0x00,0x12,0x1A,0x2E,0x47,0xF2,0xE3,0x84,0x00,0x80,0x20,0x80,0x48, +0x80,0x6A,0x80,0xA4,0x49,0xFF,0xF5,0xBB,0x80,0x06,0x49,0xFF,0xB1,0x2F,0x44,0x00, +0x02,0x40,0x49,0xFF,0xB4,0xAC,0x80,0x09,0x49,0xFF,0xB4,0xD4,0x46,0x00,0x42,0x10, +0x50,0x00,0x08,0x20,0x80,0x20,0x84,0x40,0x49,0xFF,0xB0,0x37,0x80,0x1F,0xCF,0x04, +0x44,0x10,0xF4,0xB0,0xD5,0x03,0x44,0x10,0xF4,0xC8,0x80,0xC0,0x3A,0x20,0x94,0x04, +0x3A,0x23,0x14,0x24,0x3A,0x20,0x8C,0x00,0x3A,0x23,0x0C,0x20,0x49,0xFF,0xB1,0x44, +0xFC,0xC9,0xDD,0x9E,0xFC,0x45,0x80,0xC0,0x83,0x81,0x81,0x22,0xF3,0x85,0xF4,0x86, +0x49,0xFF,0xF3,0x0C,0xF0,0x81,0xCE,0x08,0x44,0x80,0x02,0x40,0x84,0xE1,0x84,0xC2, +0x4F,0xC3,0x00,0x11,0xD5,0x07,0x5A,0x68,0x02,0x08,0x4F,0xC3,0x00,0x0A,0x81,0x1C, +0x84,0xE4,0x84,0xC3,0xD5,0x07,0x85,0x00,0x80,0xE8,0x80,0xC8,0xD5,0x03,0x85,0x00, +0x84,0xE6,0x49,0x00,0x13,0x19,0xC8,0x04,0x49,0x00,0x13,0x92,0xD5,0x03,0x49,0x00, +0x13,0x8C,0x81,0x40,0xF0,0x01,0x49,0xFF,0xF5,0xBB,0xFF,0xF4,0x40,0x14,0x04,0x08, +0x88,0x01,0x94,0x79,0xF0,0x82,0x41,0xE0,0x84,0x09,0x2E,0x07,0xF2,0xD8,0x94,0x7A, +0xF1,0x83,0x96,0x71,0xF1,0x81,0x9C,0x44,0x84,0xE0,0xF1,0x88,0x42,0x14,0x98,0x24, +0x96,0xC1,0x80,0xA7,0xF1,0x89,0xF1,0x01,0xE2,0xA1,0xE8,0x33,0x85,0x00,0x40,0x14, +0x18,0x08,0x88,0x2A,0xF1,0x84,0x80,0x85,0xF1,0x09,0x42,0x44,0x78,0x73,0x88,0x81, +0xF1,0x02,0x95,0x21,0x88,0x24,0x80,0x43,0xF1,0x87,0xF1,0x08,0xE0,0x22,0xE9,0x1A, +0xF4,0x03,0xF1,0x07,0x42,0x11,0x10,0x73,0x22,0x40,0x80,0x00,0xF1,0x04,0x38,0x10, +0x89,0x11,0x40,0xF2,0x04,0x01,0x4E,0xF4,0x00,0x05,0x8A,0x24,0x96,0x49,0xD5,0x03, +0x40,0x17,0x80,0x13,0xE2,0x27,0x40,0x13,0xBC,0x1B,0x8C,0x41,0x80,0xE1,0x96,0x91, +0xD5,0xE5,0x5A,0x80,0x01,0x04,0x85,0x01,0xD5,0xD3,0x8C,0xA1,0x97,0x69,0xD5,0xCC, +0x50,0x80,0x00,0x04,0xE6,0x22,0x84,0x80,0x42,0x64,0x98,0x24,0x14,0xFF,0x80,0x04, +0xE1,0x03,0xE9,0x3C,0xF0,0x03,0x04,0xAF,0x80,0x02,0x42,0xA1,0x80,0x73,0x84,0xA0, +0x42,0x02,0xF8,0x24,0x88,0x06,0x4F,0xC3,0x00,0x03,0x8C,0x01,0x38,0x15,0x01,0x01, +0xF0,0x04,0xC8,0x1D,0x80,0x06,0x42,0x02,0xF8,0x73,0x9C,0x81,0x81,0x21,0x38,0x25, +0x09,0x01,0xE2,0x22,0x40,0x91,0x3C,0x1B,0xE2,0x41,0x40,0x11,0x3C,0x1B,0x80,0x41, +0xF1,0x01,0x5A,0x10,0x02,0x0F,0x8C,0x02,0x80,0x29,0x38,0x05,0x01,0x01,0xE3,0x20, +0x40,0x10,0x3C,0x1B,0xE2,0x02,0x40,0x20,0x3C,0x1B,0xD5,0x04,0x80,0x41,0xD5,0x02, +0x80,0x29,0x8A,0x22,0xE0,0x24,0x40,0x12,0x3C,0x1B,0x97,0x09,0x5A,0x50,0x01,0x04, +0x84,0xA1,0xD5,0xCF,0x8C,0x61,0x96,0xD9,0xD5,0xC4,0xF0,0x06,0xAD,0xC0,0xF0,0x05, +0xAD,0x00,0xFC,0xC5,0xFC,0x02,0x84,0x00,0x12,0x0F,0x80,0x07,0x49,0x00,0x12,0x6E, +0x84,0xC5,0xC8,0x02,0x84,0xC3,0x49,0x00,0x12,0x69,0xF0,0x81,0x84,0x00,0xF1,0x01, +0x80,0x40,0x44,0x32,0x16,0xEE,0x50,0x4F,0x80,0x0E,0x49,0xFF,0xFF,0x2D,0x3C,0x5D, +0xF7,0xE6,0x44,0x10,0xFF,0xFF,0x3C,0x03,0xF9,0x43,0xD9,0x04,0x3C,0x0F,0xF7,0xE6, +0xD5,0x05,0x88,0xA0,0x92,0xA1,0x3C,0x5F,0xF7,0xE6,0x49,0x00,0x12,0x4F,0x49,0x00, +0x12,0x4D,0x5A,0x08,0x01,0x0E,0x2E,0x07,0xF2,0xE3,0xE2,0xC0,0xE9,0x09,0x02,0x0F, +0x80,0x07,0xE6,0x0A,0x4E,0xF2,0x00,0x9D,0x84,0x00,0x12,0x0F,0x80,0x07,0x49,0x00, +0x10,0xC1,0x80,0xC0,0x2E,0x07,0xF2,0xE3,0xAC,0x30,0x49,0x00,0x12,0x37,0x2E,0x17, +0xF2,0xE3,0xC8,0x04,0x44,0x02,0x16,0x98,0xD5,0x03,0x44,0x02,0x16,0x5C,0x38,0x00, +0x05,0x11,0x46,0x20,0x00,0x81,0xAC,0x31,0x02,0x0F,0x80,0x07,0x3C,0x1D,0xF7,0xE6, +0xAC,0x32,0x3C,0x03,0xF9,0x43,0xAC,0x77,0xAC,0x33,0x2E,0x07,0xF2,0xDB,0x44,0x42, +0x16,0xF0,0x50,0x00,0x00,0x64,0xAC,0x34,0x2E,0x07,0xF2,0xDA,0x50,0x00,0x00,0xC8, +0xAC,0x35,0x2E,0x07,0xF2,0xD9,0x50,0x00,0x01,0x2C,0xAC,0x36,0x04,0x01,0x00,0x20, +0x54,0x00,0x07,0xFF,0x12,0x03,0x00,0x08,0x04,0x01,0x00,0x23,0x50,0x23,0x00,0x16, +0x96,0x1F,0x12,0x03,0x00,0x09,0x44,0x0F,0xFF,0x9C,0x12,0x03,0x00,0x0A,0x84,0x00, +0x38,0x32,0x02,0x02,0x8C,0x01,0x1A,0x31,0x00,0x01,0x5A,0x08,0x14,0xFB,0x3C,0x03, +0xF8,0x5F,0x3C,0x23,0xF9,0x43,0x12,0x03,0x00,0x21,0x3C,0x03,0xF8,0x5E,0x5C,0xF1, +0x04,0xB1,0x12,0x03,0x00,0x22,0x44,0x00,0x03,0x78,0x12,0x03,0x00,0x23,0x2E,0x07, +0xF2,0xDD,0x12,0x03,0x00,0x24,0x2E,0x07,0xDE,0xCC,0x12,0x03,0x00,0x25,0x2E,0x07, +0xF2,0x85,0x12,0x03,0x00,0x26,0x84,0x00,0x12,0x03,0x00,0x27,0x12,0x03,0x00,0x28, +0x12,0x03,0x00,0x29,0x12,0x03,0x00,0x2A,0xE9,0x04,0x2E,0x07,0xF1,0xF1,0x8C,0x01, +0x3E,0x07,0xF1,0xF1,0x2E,0x07,0xF1,0xF1,0xE6,0x03,0xE9,0x06,0x84,0x00,0x3E,0x07, +0xF1,0xF1,0x3E,0x07,0xF2,0xDA,0x2E,0x07,0xF2,0xDA,0xC8,0x0D,0x5C,0xF0,0x81,0x91, +0xE8,0x06,0x02,0x0F,0x80,0x07,0x5C,0xF0,0x04,0xB1,0xE9,0x05,0x2E,0x07,0xF1,0xF0, +0x8C,0x01,0xD5,0x02,0x84,0x00,0x3E,0x07,0xF1,0xF0,0x2E,0x07,0xF1,0xF0,0xE6,0x02, +0x84,0x00,0xE9,0x09,0x84,0x00,0x3E,0x07,0xF1,0xF0,0x84,0x01,0xD5,0x04,0x8E,0x0A, +0x48,0xFF,0xFF,0x65,0xFC,0x82,0xFC,0x01,0x84,0x00,0x12,0x0F,0x80,0x03,0x84,0x00, +0x84,0x21,0x80,0x40,0x44,0x32,0x16,0xEE,0x50,0x4F,0x80,0x06,0x49,0xFF,0xFE,0x64, +0x49,0x00,0x10,0x18,0x2E,0x17,0xF2,0xE3,0x44,0x22,0x16,0x98,0xAC,0x40,0x38,0x11, +0x05,0x01,0xAC,0x41,0x02,0x1F,0x80,0x03,0xAC,0x42,0x3C,0x13,0xF9,0x43,0xAC,0x43, +0x2E,0x17,0xF2,0xDB,0x50,0x10,0x80,0x64,0xAC,0x44,0x2E,0x17,0xF2,0xDA,0x50,0x10, +0x80,0xC8,0xAC,0x45,0x2E,0x17,0xF2,0xD9,0x50,0x10,0x81,0x2C,0xAC,0x46,0x3C,0x13, +0xF6,0x93,0x96,0x46,0xC9,0x04,0x44,0x10,0x01,0x90,0xD5,0x03,0x44,0x10,0x01,0x91, +0x46,0x20,0x00,0x81,0xAC,0x47,0x83,0x82,0xB9,0x20,0x44,0x42,0x16,0xF0,0x54,0x10, +0x87,0xFF,0x12,0x10,0x00,0x08,0xB9,0x23,0x50,0x20,0x00,0x16,0x96,0x5F,0x12,0x10, +0x00,0x09,0x44,0x1F,0xFF,0x9C,0x12,0x10,0x00,0x0A,0x84,0x20,0x38,0x32,0x06,0x02, +0x8C,0x21,0x1A,0x31,0x00,0x01,0x5A,0x18,0x14,0xFB,0x3C,0x13,0xF8,0x5F,0x3C,0x23, +0xF9,0x43,0x12,0x10,0x00,0x21,0x3C,0x13,0xF8,0x5E,0x44,0x30,0x03,0xE7,0x12,0x10, +0x00,0x22,0x44,0x10,0x03,0x78,0x12,0x10,0x00,0x23,0x2E,0x17,0xF2,0xDD,0x5C,0xF1, +0x01,0x5F,0x12,0x10,0x00,0x24,0x2E,0x17,0xDE,0xCC,0x12,0x30,0x00,0x27,0x12,0x10, +0x00,0x25,0x2E,0x17,0xF2,0x85,0x12,0x20,0x00,0x29,0x12,0x10,0x00,0x26,0x02,0x1F, +0x80,0x03,0x12,0x30,0x00,0x2A,0x12,0x10,0x00,0x28,0xE8,0x06,0x44,0x00,0x04,0xB0, +0x40,0x00,0x04,0x06,0xFC,0x81,0x84,0x01,0xFC,0x81,0xFC,0x00,0x49,0xFF,0xFF,0x7D, +0xC8,0x07,0x2E,0x07,0xF2,0x84,0x8C,0x01,0x3E,0x07,0xF2,0x84,0xD5,0x0D,0x2E,0x07, +0xF2,0x85,0x84,0x20,0x8C,0x01,0x96,0x00,0xE6,0x14,0x40,0x00,0xBC,0x1A,0x3E,0x17, +0xF2,0x84,0x3E,0x07,0xF2,0x85,0x2E,0x07,0xF2,0x84,0xE6,0x0B,0x84,0x00,0xE9,0x09, +0x84,0x00,0x3E,0x07,0xF2,0x84,0x2E,0x07,0xF2,0x85,0x3E,0x07,0xF2,0xE3,0x84,0x01, +0xFC,0x80,0xFC,0x44,0x81,0x00,0x84,0x00,0x12,0x0F,0x80,0x0F,0x4E,0x83,0x00,0x0D, +0x44,0x02,0x16,0xC0,0x85,0x43,0xF0,0x84,0x44,0x02,0x16,0x98,0xF0,0x82,0x14,0xAF, +0x80,0x03,0x84,0xC4,0xD5,0x0B,0x44,0x02,0x16,0x84,0xF0,0x84,0x44,0x02,0x16,0x5C, +0xF0,0x82,0x84,0x02,0x85,0x45,0xF0,0x83,0x84,0xC6,0x46,0x97,0xFF,0xFF,0x84,0x00, +0x80,0xA6,0x50,0x94,0x8F,0xFF,0xF0,0x81,0x2E,0x07,0xF2,0xDD,0xF7,0x01,0x42,0x73, +0x00,0x73,0x97,0xF8,0xE6,0xF4,0xE8,0x39,0x84,0x02,0x80,0x28,0xF2,0x01,0x44,0x32, +0x16,0xEE,0x50,0x4F,0x80,0x1E,0xF5,0x85,0x49,0xFF,0xFD,0x96,0xF5,0x05,0x5A,0x88, +0x01,0x0D,0xE3,0x47,0xE9,0x0A,0x02,0x0F,0x80,0x0F,0xE6,0x0A,0xE8,0x03,0x84,0x00, +0xD5,0x02,0x8E,0x0A,0x12,0x0F,0x80,0x0F,0x44,0x02,0x16,0xF0,0x38,0x10,0x1E,0x02, +0x3C,0x03,0xF9,0x43,0xC1,0x08,0x94,0x02,0x84,0x46,0x42,0x00,0x88,0x73,0x84,0x2A, +0x40,0x00,0x04,0x16,0x44,0x12,0x16,0xF0,0x38,0x00,0x9E,0x0A,0x44,0x02,0x16,0xF0, +0x38,0x00,0x1E,0x02,0xE0,0x09,0xE8,0x09,0x02,0x1F,0x80,0x0F,0x5C,0xF0,0x84,0xB0, +0x40,0x90,0x3C,0x1B,0x40,0x53,0xBC,0x1B,0xF0,0x01,0x8C,0x01,0x96,0x00,0xF0,0x81, +0x4C,0x03,0x7F,0xBC,0x44,0x00,0xFF,0xFF,0x3C,0x0F,0xF7,0xE6,0x5A,0x88,0x01,0x1F, +0xE3,0x47,0xE9,0x1C,0x84,0x00,0x80,0x20,0x44,0x42,0x16,0xF0,0x38,0x22,0x06,0x02, +0x8C,0x21,0xE0,0x40,0x96,0x48,0x40,0x20,0x3C,0x1B,0xE3,0x41,0x44,0x32,0x16,0xF0, +0x96,0x11,0xE8,0xF5,0x84,0x20,0x38,0x01,0x86,0x0A,0x8C,0x21,0x96,0x88,0xE3,0x42, +0xE8,0xFB,0xE3,0x45,0xE9,0x03,0x81,0x20,0x84,0xA0,0x2E,0x07,0xF2,0xDD,0xC0,0x05, +0x3C,0x13,0xF9,0x70,0xE1,0x21,0xE8,0x05,0x3E,0x57,0xF2,0xE2,0x3C,0x9B,0xF9,0x70, +0x3C,0x13,0xF9,0x70,0x5C,0xF0,0x80,0xFA,0xE8,0x0A,0x2E,0x07,0xF2,0xE2,0x3E,0x07, +0xF2,0xE3,0x84,0x00,0x3E,0x07,0xF2,0xDC,0x48,0x00,0x00,0x7F,0x8C,0x01,0x3E,0x07, +0xF2,0xDD,0x49,0x00,0x0E,0xD7,0x2E,0x17,0xF2,0xE3,0xF2,0x02,0xAC,0x40,0x38,0x11, +0x05,0x01,0xAC,0x41,0x02,0x1F,0x80,0x0F,0xAC,0x42,0x3C,0x13,0xF9,0x43,0xAC,0x43, +0x2E,0x17,0xF2,0xDB,0x50,0x10,0x80,0x64,0xAC,0x44,0x2E,0x17,0xF2,0xDA,0x50,0x10, +0x80,0xC8,0xAC,0x45,0x2E,0x17,0xF2,0xD9,0x50,0x10,0x81,0x2C,0xAC,0x46,0x3C,0x13, +0xF6,0x93,0x96,0x46,0xC9,0x04,0x44,0x10,0x01,0x90,0xD5,0x03,0x44,0x10,0x01,0x91, +0x46,0x20,0x00,0x81,0xAC,0x47,0x04,0x11,0x00,0x20,0x44,0x42,0x16,0xF0,0x54,0x10, +0x87,0xFF,0x12,0x10,0x00,0x08,0x04,0x11,0x00,0x23,0x50,0x20,0x00,0x16,0x96,0x5F, +0x12,0x10,0x00,0x09,0x44,0x1F,0xFF,0x9C,0x12,0x10,0x00,0x0A,0x84,0x20,0x38,0x32, +0x06,0x02,0x8C,0x21,0x1A,0x31,0x00,0x01,0x5A,0x18,0x14,0xFB,0x3C,0x23,0xF8,0x5F, +0x2E,0x47,0xF2,0xDD,0x12,0x20,0x00,0x21,0x3C,0x23,0xF8,0x5E,0x12,0x40,0x00,0x24, +0x12,0x20,0x00,0x22,0x44,0x20,0x03,0x78,0x12,0x20,0x00,0x23,0x2E,0x27,0xDE,0xCC, +0x12,0x20,0x00,0x25,0x2E,0x27,0xF2,0x85,0x12,0x20,0x00,0x26,0x2E,0x07,0xF2,0xDE, +0xE2,0x80,0xE9,0x1C,0x2E,0x07,0xF2,0xE2,0x3E,0x07,0xF2,0xE3,0x3C,0x03,0xF9,0x70, +0x5C,0xF0,0x00,0x96,0x84,0x01,0xE9,0x02,0x84,0x02,0x3E,0x07,0xF2,0xDC,0x84,0x01, +0x3E,0x07,0xF2,0xDA,0x84,0x21,0x84,0x06,0x49,0x00,0x19,0x68,0x84,0x06,0x44,0x10, +0x00,0x64,0x49,0x00,0x19,0x55,0x84,0x01,0xD5,0x16,0xFF,0x34,0x97,0x20,0x88,0xC4, +0xE4,0xD5,0x40,0x60,0xBC,0x1A,0x9F,0x71,0x84,0x02,0xF1,0x03,0xF2,0x02,0xF3,0x04, +0x97,0x68,0x49,0xFF,0xF2,0x2C,0x84,0x00,0x5A,0x88,0x01,0x06,0x80,0x28,0x49,0xFF, +0xFA,0x8F,0x84,0x00,0xFC,0xC4,0x84,0x01,0x3E,0x07,0xF2,0xDB,0xDD,0x9E,0x2E,0x07, +0xF2,0xDB,0xDD,0x9E,0x84,0x00,0x3E,0x07,0xF2,0xDB,0xDD,0x9E,0x2E,0x07,0xF2,0xDA, +0xDD,0x9E,0x84,0x00,0x3E,0x07,0xF2,0xDA,0xDD,0x9E,0x84,0x01,0x3E,0x07,0xF2,0xD9, +0xDD,0x9E,0x2E,0x07,0xF2,0xD9,0xDD,0x9E,0x84,0x00,0x3E,0x07,0xF2,0xD9,0xDD,0x9E, +0x2E,0x07,0xF2,0xED,0x96,0x00,0xC0,0x05,0x84,0x00,0x3E,0x07,0xF2,0xED,0x84,0x01, +0xDD,0x9E,0x2E,0x07,0xF2,0xEC,0x96,0x00,0xC0,0x05,0x84,0x00,0x3E,0x07,0xF2,0xEC, +0x84,0x01,0xDD,0x9E,0x2E,0x17,0xF2,0xEB,0x96,0x48,0xC1,0x05,0x84,0x00,0x3E,0x07, +0xF2,0xEB,0xD5,0x07,0x2E,0x07,0xF2,0xE7,0x96,0x00,0xC0,0x04,0x3E,0x17,0xF2,0xE7, +0x84,0x01,0xDD,0x9E,0x2E,0x07,0xF2,0xEA,0x96,0x00,0xC0,0x05,0x84,0x00,0x3E,0x07, +0xF2,0xEA,0xD5,0x10,0x2E,0x17,0xF2,0xE9,0x96,0x48,0xC1,0x05,0x3E,0x07,0xF2,0xE9, +0x84,0x01,0xD5,0x08,0x2E,0x07,0xF2,0xE8,0x96,0x00,0xC0,0x07,0x3E,0x17,0xF2,0xE8, +0x84,0x02,0x3E,0x07,0xF2,0xEF,0x84,0x01,0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xAB,0x3A, +0x5A,0x00,0x01,0xFE,0x44,0x16,0x20,0x10,0xB4,0x01,0x92,0x01,0x96,0x06,0x5A,0x00, +0x01,0xFD,0x49,0x00,0x16,0xA7,0x80,0xC0,0xC8,0x09,0x84,0x01,0x49,0xFF,0xF1,0xF9, +0x64,0x00,0x00,0x20,0x80,0x06,0x49,0xFF,0xF1,0xF4,0xFC,0x80,0x2E,0x07,0xF2,0xF0, +0xDD,0x9E,0x2E,0x07,0xF2,0xEF,0xDD,0x9E,0x3E,0x07,0xF2,0xEE,0xDD,0x9E,0xFC,0x00, +0x2E,0x07,0xF2,0xF0,0xC8,0x15,0x2E,0x07,0xF2,0xE4,0x44,0x65,0x09,0x10,0x5A,0x08, +0x01,0x08,0x49,0xFF,0xF2,0xC3,0xFE,0x34,0x44,0x61,0x2C,0x00,0xD5,0x06,0x49,0xFF, +0xF2,0xBD,0xFE,0x34,0x44,0x62,0x58,0x00,0x40,0x00,0x18,0x16,0xD5,0x09,0x84,0x01, +0x49,0xFF,0xF3,0xE6,0x80,0xC0,0x49,0xFF,0xF2,0xB1,0xFE,0x34,0x90,0x07,0x50,0x00, +0x00,0xEB,0x96,0x01,0xFC,0x80,0x2E,0x07,0xF1,0x78,0xC0,0xFE,0xFC,0x00,0x49,0xFF, +0xAC,0xC9,0xFC,0x80,0xFC,0x01,0xF0,0x81,0x84,0x01,0x49,0xFF,0xED,0x65,0xF0,0x01, +0x49,0xFF,0xF1,0xC3,0xF0,0x01,0x49,0xFF,0xF1,0xC1,0xF0,0x01,0x49,0xFF,0xF1,0xBF, +0xFC,0x81,0xFC,0x00,0x84,0x00,0x49,0xFF,0xF3,0xDE,0x84,0x00,0x49,0xFF,0xB3,0x85, +0x84,0x00,0x49,0xFF,0xF3,0xE3,0x2E,0x07,0xF2,0xEF,0xE6,0x02,0xE8,0x08,0x84,0x01, +0x84,0x20,0x49,0xFF,0xB3,0x11,0x84,0x01,0x49,0xFF,0xFF,0xDE,0x84,0x00,0x84,0x21, +0x49,0xFF,0xB3,0x0A,0x84,0x01,0x80,0x20,0x49,0xFF,0xB3,0x51,0x84,0x00,0x49,0x00, +0x0C,0xEF,0x84,0x00,0x49,0x00,0x0C,0xD2,0x46,0x00,0x00,0x81,0x83,0x80,0xB9,0x1A, +0x92,0x25,0x94,0x4D,0x58,0x10,0x80,0x10,0xB9,0x9A,0xBA,0x1A,0x44,0x1F,0xE0,0xFF, +0xFE,0x56,0x58,0x10,0x88,0x00,0xB9,0x9A,0xBA,0x1A,0x46,0x1F,0xFE,0x0F,0x50,0x10, +0x8F,0xFF,0xFE,0x56,0x44,0x25,0x00,0x00,0xFE,0x57,0xB9,0x9A,0xBA,0x1A,0x46,0x1E, +0x0F,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x56,0x46,0x20,0x10,0x00,0xFE,0x57,0xB9,0x9A, +0xB9,0x19,0x44,0x21,0xF4,0xCE,0x92,0x31,0x40,0x10,0xC4,0x08,0xFE,0x57,0xB9,0x99, +0x84,0x00,0x3E,0x07,0xF2,0xF0,0xFC,0x80,0xFC,0x20,0x84,0x01,0x49,0x00,0x0C,0xB8, +0x84,0x00,0x49,0x00,0x0C,0x9B,0x2E,0x67,0xF2,0xEF,0xCE,0x6A,0x80,0x06,0x49,0xFF, +0xF3,0x8D,0x80,0x06,0x49,0xFF,0xFF,0x90,0x80,0x06,0x80,0x26,0x49,0xFF,0xB2,0xBC, +0x44,0x04,0x20,0x18,0x46,0x1F,0xF7,0xFF,0xB4,0x40,0x50,0x10,0x8F,0xFF,0xFE,0x56, +0xB6,0x20,0xB4,0x40,0x46,0x10,0x10,0x00,0xFE,0x57,0xB6,0x20,0xB4,0x40,0x46,0x1F, +0x7F,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x56,0xB6,0x20,0xB4,0x40,0x46,0x1B,0xFF,0xFF, +0x50,0x10,0x8F,0xFF,0xFE,0x56,0x84,0x41,0xB6,0x20,0x80,0x62,0x80,0x20,0x49,0xFF, +0xAA,0xC3,0x44,0x04,0x20,0x20,0x84,0x3B,0xB4,0x40,0xFE,0x56,0xB6,0x20,0xB4,0x20, +0x84,0x41,0x58,0x10,0x80,0x08,0xB6,0x20,0xB4,0x20,0x80,0x62,0x58,0x10,0x80,0x40, +0xB6,0x20,0x80,0x20,0x49,0xFF,0xAA,0xB0,0x44,0x04,0x20,0x1C,0x44,0x1B,0xFF,0xFF, +0xB4,0x40,0xFE,0x56,0xB6,0x20,0xB4,0x40,0x46,0x10,0x00,0x80,0xFE,0x57,0xB6,0x20, +0xB4,0x40,0x46,0x10,0x04,0x00,0xFE,0x57,0x84,0x41,0xB6,0x20,0x80,0x62,0x80,0x20, +0x49,0xFF,0xAA,0x9A,0x44,0x14,0x50,0x0C,0x84,0x1E,0xB4,0x41,0xFE,0x16,0xB6,0x01, +0x84,0x0F,0x49,0xFF,0xB3,0x80,0x44,0x24,0x70,0x04,0x44,0x10,0x89,0x12,0xB4,0x02, +0x92,0x10,0x40,0x00,0x40,0x08,0xFE,0x0F,0xB6,0x02,0x48,0x00,0x00,0xE8,0x5A,0x60, +0x01,0x04,0x48,0x00,0x00,0xA3,0x84,0x00,0x49,0xFF,0xFF,0x26,0x84,0x00,0x80,0x20, +0x49,0xFF,0xB2,0x52,0x44,0x14,0x70,0x00,0x44,0x01,0x00,0x00,0xB4,0x41,0x44,0x74, +0x20,0x1C,0xFE,0x17,0xB6,0x01,0xB4,0x27,0x44,0x0F,0xFF,0xEF,0xFE,0x0E,0xB6,0x07, +0x80,0x27,0x80,0x07,0x80,0x46,0x80,0x66,0x49,0xFF,0xAA,0x66,0x44,0x04,0x20,0x18, +0x46,0x1F,0xF7,0xFF,0xB4,0x40,0x50,0x10,0x8F,0xFF,0xFE,0x56,0xB6,0x20,0xB4,0x40, +0x46,0x10,0x10,0x00,0xFE,0x57,0xB6,0x20,0xB4,0x40,0x46,0x1F,0x7F,0xFF,0x50,0x10, +0x8F,0xFF,0xFE,0x56,0xB6,0x20,0xB4,0x40,0x46,0x1B,0xFF,0xFF,0x50,0x10,0x8F,0xFF, +0xFE,0x56,0xB6,0x20,0x80,0x46,0x80,0x20,0x80,0x66,0x49,0xFF,0xAA,0x45,0x44,0x04, +0x20,0x20,0x84,0x3B,0xB4,0x40,0x80,0x66,0xFE,0x56,0xB6,0x20,0xB4,0x20,0x80,0x46, +0x58,0x10,0x80,0x08,0xB6,0x20,0xB4,0x20,0x58,0x10,0x80,0x40,0xB6,0x20,0x80,0x20, +0x49,0xFF,0xAA,0x32,0xB4,0x27,0x44,0x0B,0xFF,0xFF,0xFE,0x0E,0xB6,0x07,0xB4,0x27, +0x46,0x00,0x00,0x80,0xFE,0x0F,0xB6,0x07,0xB4,0x27,0x46,0x00,0x04,0x00,0xFE,0x0F, +0xB6,0x07,0x80,0x27,0x80,0x07,0x80,0x46,0x80,0x66,0x49,0xFF,0xAA,0x1D,0x44,0x14, +0x50,0x0C,0x84,0x1E,0xB4,0x41,0xFE,0x16,0xB6,0x01,0x84,0x0F,0x49,0xFF,0xB3,0x03, +0x44,0x04,0x70,0x18,0xB4,0x20,0x58,0x10,0x80,0x02,0xB6,0x20,0xB4,0x20,0x58,0x10, +0x80,0x04,0xB6,0x20,0x44,0x14,0x70,0x08,0xB4,0x01,0x92,0x10,0x40,0x00,0x40,0x08, +0x58,0x00,0x78,0x23,0xB6,0x01,0x44,0x14,0x70,0x04,0xB4,0x01,0x92,0x10,0x40,0x00, +0x40,0x08,0xB6,0x01,0x44,0x14,0x50,0x10,0xB4,0x01,0x92,0x08,0x40,0x00,0x20,0x08, +0x58,0x00,0x00,0x98,0xB6,0x01,0xB4,0x41,0x44,0x0F,0x00,0xFF,0xFE,0x16,0x44,0x20, +0x98,0x00,0xFE,0x17,0xB6,0x01,0x44,0x04,0x00,0x10,0x44,0x10,0x98,0x81,0xB6,0x20, +0x44,0x04,0x00,0x1C,0xAF,0x80,0xD5,0x54,0x46,0x00,0x00,0x81,0x83,0x80,0xB9,0x1A, +0x92,0x25,0x94,0x4D,0x58,0x10,0x80,0x03,0xB9,0x9A,0xBA,0x1A,0x44,0x1F,0xE0,0xFF, +0xFE,0x56,0x58,0x10,0x82,0x00,0xB9,0x9A,0xBA,0x1A,0x46,0x1F,0xFE,0x0F,0x50,0x10, +0x8F,0xFF,0xFE,0x56,0x44,0x21,0x00,0x00,0xFE,0x57,0xB9,0x9A,0xBA,0x1A,0x46,0x1E, +0x0F,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x56,0xB9,0x9A,0xB9,0x19,0x92,0x31,0x40,0x10, +0xC4,0x08,0x58,0x10,0xED,0x60,0xB9,0x99,0x84,0x21,0x84,0x00,0x49,0xFF,0xB1,0xD7, +0x84,0x01,0x49,0xFF,0xB1,0xDE,0x84,0x01,0x80,0x20,0x49,0xFF,0xB1,0x85,0x84,0x01, +0x49,0xFF,0xB1,0xB8,0x84,0x01,0x49,0xFF,0xB1,0xE8,0x44,0x00,0x00,0xD2,0x84,0x21, +0x49,0xFF,0xB1,0x94,0x84,0x01,0x49,0xFF,0xB1,0x9B,0x49,0xFF,0xF0,0xFF,0x46,0x01, +0xFF,0xFF,0x50,0x00,0x0F,0x00,0x49,0xFF,0xB0,0xF5,0x49,0xFF,0xB0,0xDF,0x46,0x01, +0xF0,0x00,0x50,0x00,0x01,0x00,0x49,0xFF,0xB0,0xED,0x49,0xFF,0xB0,0xD7,0x84,0x00, +0x49,0xFF,0xEC,0x7A,0x84,0x00,0x49,0xFF,0xEC,0x80,0x84,0x01,0x3E,0x07,0xF2,0xF0, +0x2E,0x07,0xF2,0xEF,0x5A,0x08,0x02,0x04,0x49,0xFF,0xB7,0x35,0xFC,0xA0,0xFC,0x00, +0x49,0xFF,0xF0,0x73,0xFC,0x80,0xDD,0x9E,0x84,0x40,0x44,0x42,0x17,0x68,0x96,0xD1, +0xE2,0x61,0xE8,0x07,0x38,0x30,0x08,0x00,0x38,0x32,0x08,0x08,0x8C,0x41,0xD5,0xF8, +0xDD,0x9E,0xFC,0x40,0x22,0xFF,0x80,0x14,0x22,0x8F,0x80,0x16,0x22,0xAF,0x80,0x18, +0x00,0x9F,0x80,0x34,0x00,0x7F,0x80,0x38,0x00,0x6F,0x80,0x3C,0x4E,0x03,0x00,0x99, +0x45,0xC7,0x20,0xC0,0x46,0x0F,0xFF,0x0F,0x05,0xEE,0x00,0x00,0x96,0x5F,0x50,0x00, +0x0F,0xFF,0x40,0x10,0xC0,0x08,0x40,0x0F,0x00,0x02,0xFE,0x0F,0xB6,0x1C,0xC4,0x0C, +0x44,0x07,0x20,0xC4,0x40,0x21,0x30,0x08,0xB4,0x20,0x92,0x4C,0x92,0x34,0x40,0x10, +0xD0,0x08,0xFE,0x8F,0xB6,0x40,0xC5,0x0C,0x44,0x07,0x20,0xCC,0x40,0x31,0xB0,0x08, +0xB4,0x20,0x92,0x6C,0x92,0x34,0x40,0x10,0xD0,0x08,0xFE,0xCF,0xB6,0x60,0x44,0x27, +0x20,0xD4,0xF0,0x08,0xB4,0x22,0x40,0x00,0x30,0x08,0x92,0x34,0x40,0x10,0xD0,0x08, +0x92,0x0C,0x44,0x37,0x20,0xC8,0xFE,0x0F,0xB6,0x02,0xB4,0x03,0x54,0x17,0x80,0xFF, +0x92,0x08,0x40,0x00,0x20,0x08,0x44,0x27,0x20,0xD0,0xFE,0x0F,0xB6,0x03,0xB4,0x02, +0x54,0x14,0x00,0xFF,0x92,0x08,0x40,0x00,0x20,0x08,0xFE,0x0F,0x44,0x17,0x20,0xD8, +0xB6,0x02,0xB4,0x01,0x54,0xA5,0x00,0xFF,0x92,0x08,0x40,0x00,0x20,0x08,0x40,0x00, +0x28,0x04,0xB6,0x01,0x54,0x04,0x80,0x03,0x40,0xA0,0x60,0x08,0xB5,0x23,0x46,0x0F, +0xCF,0xFF,0x50,0x00,0x0F,0xFF,0x40,0x94,0x80,0x02,0x40,0x94,0xA8,0x04,0xB7,0x23, +0x97,0xCF,0x40,0x93,0xE0,0x08,0xB4,0xE2,0x97,0x8F,0xFF,0xC6,0x40,0x73,0xA4,0x04, +0xB6,0xE2,0xB4,0xE1,0x40,0x63,0x60,0x08,0xFE,0x3E,0xFE,0x37,0xB6,0x01,0xF0,0x10, +0x44,0x77,0x20,0xDC,0x40,0x00,0x3C,0x08,0x40,0x60,0x3C,0x09,0xB4,0x07,0x40,0x42, +0x7C,0x08,0x92,0x11,0x40,0x00,0x44,0x08,0xFE,0x37,0xB6,0x07,0xB4,0x03,0x40,0x52, +0xFC,0x08,0x94,0x01,0x92,0x01,0xFE,0x27,0xB6,0x03,0xB4,0x02,0x94,0x01,0x92,0x01, +0xFE,0x2F,0xB6,0x02,0xF0,0x09,0x40,0x20,0x7C,0x08,0xB4,0x01,0x94,0x01,0x92,0x01, +0xFE,0x17,0xB6,0x01,0x84,0x20,0x44,0x07,0x20,0xE0,0x48,0x00,0x00,0x9D,0x5A,0x00, +0x01,0x04,0x48,0x00,0x00,0x9A,0x45,0xC7,0x21,0x00,0x46,0x0F,0xFF,0x0F,0x05,0xEE, +0x00,0x00,0x96,0x5F,0x50,0x00,0x0F,0xFF,0x40,0x10,0xC0,0x08,0x40,0x0F,0x00,0x02, +0xFE,0x0F,0xB6,0x1C,0xC4,0x0C,0x44,0x07,0x21,0x04,0x40,0x21,0x30,0x08,0xB4,0x20, +0x92,0x4C,0x92,0x34,0x40,0x10,0xD0,0x08,0xFE,0x8F,0xB6,0x40,0xC5,0x0C,0x44,0x07, +0x21,0x0C,0x40,0x31,0xB0,0x08,0xB4,0x20,0x92,0x6C,0x92,0x34,0x40,0x10,0xD0,0x08, +0xFE,0xCF,0xB6,0x60,0x44,0x27,0x21,0x14,0xF0,0x08,0xB4,0x22,0x40,0x00,0x30,0x08, +0x92,0x34,0x40,0x10,0xD0,0x08,0x92,0x0C,0xFE,0x0F,0xB6,0x02,0x44,0x27,0x21,0x08, +0x54,0x17,0x80,0xFF,0xB4,0x02,0x54,0x35,0x00,0xFF,0x92,0x08,0x40,0x00,0x20,0x08, +0xFE,0x0F,0x44,0x17,0x21,0x10,0xB6,0x02,0x54,0x04,0x00,0xFF,0xB5,0x01,0x97,0xCF, +0x93,0x08,0x40,0x84,0x20,0x08,0x40,0x84,0x00,0x04,0x44,0x07,0x21,0x18,0xB7,0x01, +0xB5,0x40,0x97,0x8F,0x93,0x48,0x40,0xA5,0x20,0x08,0x40,0xA5,0x0C,0x04,0xB7,0x40, +0x54,0x34,0x80,0x03,0x46,0xAF,0xCF,0xFF,0xB5,0x22,0x50,0xA5,0x0F,0xFF,0x40,0x31, +0xE0,0x08,0x40,0x94,0xA8,0x02,0x40,0x94,0x8C,0x04,0xB7,0x22,0x40,0x33,0xE0,0x08, +0xB4,0xE1,0x40,0x42,0x7C,0x08,0x40,0x73,0xA8,0x02,0xFF,0xDF,0xB6,0xE1,0x40,0x33, +0x60,0x08,0xB4,0xC0,0x44,0x77,0x21,0x1C,0x40,0x63,0x28,0x02,0xFF,0x9F,0xF3,0x10, +0xB6,0xC0,0x40,0x31,0xBC,0x08,0x40,0x61,0xBC,0x09,0xB4,0x67,0x40,0x52,0xFC,0x08, +0x92,0x71,0x40,0x31,0xC4,0x08,0xFE,0xF7,0xB6,0x67,0xB4,0x62,0x94,0xD9,0x92,0x61, +0xFF,0x1F,0xB6,0x82,0xB4,0x41,0x94,0x91,0x92,0x41,0xFF,0x57,0xB6,0xA1,0xF1,0x09, +0x40,0x20,0xFC,0x08,0xB4,0x20,0x94,0x49,0x92,0x21,0xFE,0x57,0xB6,0x20,0x84,0x20, +0x44,0x07,0x21,0x20,0xB6,0x20,0xFC,0xC0,0xFC,0x00,0x84,0x00,0x49,0xFF,0xB0,0xB7, +0x49,0x00,0x12,0xE9,0xE6,0x03,0xE8,0x09,0x44,0x16,0x00,0x00,0xB4,0x01,0x58,0x00, +0x01,0x00,0xB6,0x01,0x48,0x00,0x00,0x84,0x44,0x14,0x80,0x04,0x44,0x01,0x00,0x00, +0xB6,0x01,0x44,0x14,0x80,0x84,0xB4,0x41,0xFE,0x17,0xB6,0x01,0x44,0x16,0x00,0x30, +0xB4,0x01,0x58,0x00,0x01,0x00,0xB6,0x01,0x44,0x07,0x21,0x00,0x46,0x10,0x20,0x00, +0xB4,0x40,0xFE,0x57,0xB6,0x20,0xB4,0x40,0x44,0x37,0x21,0x00,0x92,0x59,0x96,0x86, +0xCA,0xFB,0xB4,0x23,0x46,0x0F,0xFF,0x0F,0x50,0x00,0x0F,0xFF,0xFE,0x0E,0x44,0x16, +0x00,0x00,0xFE,0x0F,0xB6,0x03,0x44,0x37,0x21,0x04,0x44,0x02,0x17,0x69,0x40,0x00, +0x30,0x08,0x40,0x10,0x30,0x09,0xB4,0x03,0x44,0x47,0x21,0x10,0x92,0x14,0x40,0x00, +0x50,0x08,0xFE,0x0F,0xB6,0x03,0x44,0x37,0x21,0x14,0x44,0x16,0x00,0x34,0xB4,0x03, +0x46,0x68,0x00,0x00,0x92,0x14,0x40,0x00,0x50,0x08,0xFE,0x0F,0xB6,0x03,0x44,0x37, +0x21,0x08,0x44,0x17,0x21,0x18,0xB4,0x03,0x92,0x08,0x40,0x00,0x20,0x08,0x58,0x00, +0x00,0x01,0xB6,0x03,0xB4,0x04,0x92,0x08,0x40,0x00,0x20,0x08,0xB6,0x04,0xB4,0x01, +0x92,0x08,0x40,0x00,0x20,0x08,0xB6,0x01,0xB4,0xA3,0x46,0x0F,0xCF,0xFF,0x50,0x00, +0x0F,0xFF,0xFF,0x46,0xB6,0xA3,0xB4,0xA4,0xFF,0x46,0xB6,0xA4,0xB4,0xA1,0xFE,0x2E, +0x44,0x57,0x21,0x1C,0xB6,0x01,0xB4,0x05,0x92,0x11,0x40,0x00,0x44,0x08,0x58,0x00, +0x05,0x77,0xB6,0x05,0xB4,0x03,0xFE,0x37,0xB6,0x03,0xB4,0x04,0x94,0x01,0x92,0x01, +0xB6,0x04,0xB4,0x01,0xFF,0x87,0x44,0x07,0x21,0x20,0xB6,0xC1,0xB6,0x40,0xB4,0x25, +0x46,0x0F,0x0F,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x0E,0xB6,0x05,0x44,0x00,0x00,0x79, +0x3E,0x07,0xF3,0x01,0x44,0x0F,0xFF,0x84,0x3E,0x07,0xF3,0x02,0x84,0x19,0x3E,0x07, +0xF3,0x03,0xFC,0x80,0x84,0x20,0x84,0x40,0x3C,0x1B,0xF9,0x7D,0x44,0x42,0x17,0x68, +0x80,0x22,0x96,0xD1,0xE2,0x60,0xE8,0x06,0x38,0x32,0x08,0x00,0x8C,0x41,0x88,0x23, +0xD5,0xF9,0xFC,0x00,0x44,0x22,0x17,0x68,0xFE,0x4A,0x96,0x48,0x38,0x11,0x00,0x08, +0x8C,0x01,0x96,0x01,0x49,0xFF,0xEB,0xEF,0x2E,0x07,0xF3,0x00,0x49,0xFF,0xEB,0xD6, +0x49,0x00,0x12,0x31,0xE6,0x03,0xE9,0x03,0x49,0xFF,0xEA,0x6D,0xFC,0x80,0xFC,0x01, +0x84,0x00,0x3E,0x07,0xF9,0x09,0x84,0x01,0x3E,0x07,0xDF,0x9C,0x84,0x20,0x44,0x00, +0x01,0x00,0x49,0xFF,0xA6,0xA7,0x44,0x00,0x01,0x00,0x84,0x20,0x49,0xFF,0xA6,0xB1, +0x44,0x00,0x01,0x00,0x84,0x20,0x49,0xFF,0xA6,0xBB,0x44,0x40,0x05,0x78,0xB6,0x9F, +0x44,0x12,0x17,0x68,0x44,0x22,0x17,0x64,0x44,0x32,0x17,0x62,0x44,0x50,0x00,0x90, +0x44,0x02,0x1C,0xE0,0x49,0xFF,0xEB,0xAE,0x44,0x00,0xBF,0x06,0x49,0xFF,0xEB,0xC4, +0x44,0x00,0xB8,0x86,0x49,0xFF,0xEB,0xC3,0x84,0x00,0x49,0xFF,0xA6,0xBD,0x84,0x00, +0x3C,0x0B,0xF9,0x7E,0x44,0x00,0x05,0x77,0x49,0xFF,0xFF,0xA6,0x49,0xFF,0xFF,0x06, +0xFC,0x81,0xFC,0x00,0xC8,0x03,0xFA,0x1A,0xD5,0x03,0x44,0x00,0x05,0x1B,0x49,0xFF, +0xFF,0x9B,0xFC,0x80,0xFC,0x00,0x84,0x07,0x49,0xFF,0xFF,0x96,0xFC,0x80,0xFC,0x00, +0x44,0x00,0x00,0xA9,0x49,0xFF,0xFF,0x90,0xFC,0x80,0xFC,0x00,0x44,0x00,0x01,0x00, +0x84,0x21,0x49,0xFF,0xA6,0x7D,0x84,0x00,0x3C,0x0F,0xFC,0xBD,0x3C,0x0D,0xFC,0xBD, +0x5C,0xF0,0x00,0x64,0xE8,0x05,0x3C,0x0D,0xFC,0xBD,0x8C,0x01,0xD5,0xF6,0x44,0x00, +0x01,0x00,0x84,0x20,0x49,0xFF,0xA6,0x6C,0xFC,0x80,0x2E,0x07,0xF9,0x09,0xDD,0x9E, +0x2E,0x07,0xDF,0x9C,0xDD,0x9E,0xFC,0x00,0x44,0x10,0x00,0xFF,0x80,0xC0,0x44,0x20, +0x05,0x78,0x44,0x02,0x17,0x68,0x49,0x00,0x19,0x83,0xCE,0x06,0x44,0x00,0x00,0x5A, +0x3E,0x07,0xF3,0x00,0xFC,0x80,0x44,0x0F,0xFF,0xA7,0x3E,0x07,0xF3,0x00,0x84,0x05, +0x3E,0x07,0xF3,0x01,0xFA,0x0C,0x3E,0x07,0xF3,0x02,0xFA,0x10,0x3E,0x07,0xF3,0x03, +0x84,0x1F,0x3E,0x07,0xF3,0x04,0xFC,0x80,0xFC,0x40,0x84,0x85,0x8C,0x24,0x84,0xA1, +0x44,0x32,0x17,0x68,0x84,0xD0,0xA6,0x8C,0x04,0x90,0xFF,0xFF,0x5A,0x28,0x01,0x2D, +0x22,0x70,0x80,0x01,0x22,0x20,0x80,0x00,0x90,0xE8,0x90,0x44,0x97,0xDF,0xFE,0xB6, +0xFE,0xBF,0x96,0x90,0xC8,0x17,0x38,0x21,0x94,0x08,0x9C,0xA9,0xA7,0xC8,0x38,0x71, +0x88,0x08,0x9C,0xAA,0xA7,0xCA,0x38,0x71,0x88,0x08,0x40,0x24,0x94,0x0A,0x5E,0xF1, +0x01,0x00,0x9D,0xEB,0xE9,0x03,0x44,0x20,0x00,0xFF,0x96,0x90,0x38,0x21,0x9C,0x08, +0xD5,0x0B,0x38,0x21,0x90,0x08,0x9C,0xA1,0xA7,0xC8,0x38,0x71,0x88,0x08,0x9C,0xA2, +0xA7,0xCA,0x38,0x71,0x88,0x08,0x8C,0x83,0x8C,0x2C,0x8C,0xA4,0x5A,0x48,0x23,0xCD, +0xFC,0xC0,0xFA,0xB3,0x44,0x22,0x17,0x68,0x44,0x30,0x04,0xA3,0x80,0x80,0x0A,0x10, +0x00,0x01,0x92,0x28,0x38,0x11,0x14,0x08,0x9C,0x69,0xA7,0x20,0x8C,0xA2,0x38,0x41, +0x04,0x08,0xDB,0xF5,0xDD,0x9E,0x44,0x50,0x04,0xA3,0x44,0x22,0x17,0x68,0x44,0x30, +0x05,0x07,0x80,0x80,0x0A,0x10,0x00,0x01,0x92,0x28,0x38,0x11,0x14,0x08,0x9C,0x69, +0xA7,0x20,0x8C,0xA2,0x38,0x41,0x04,0x08,0xDB,0xF5,0xDD,0x9E,0xC8,0x03,0x3E,0x17, +0xF3,0x29,0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xEA,0xF4,0xFC,0x80,0xFC,0x00,0x49,0xFF, +0xEA,0xED,0xFC,0x80,0xFC,0x00,0x49,0x00,0x08,0xA1,0x2E,0x57,0xF9,0x08,0xCD,0x18, +0x84,0x32,0x3E,0x17,0xF3,0x00,0x44,0x22,0x17,0x68,0x44,0x30,0x04,0x80,0x80,0x80, +0x2A,0x10,0x00,0x01,0x9D,0xA9,0x90,0x28,0x96,0x48,0x38,0x11,0x18,0x08,0x8C,0xA2, +0xA6,0x60,0x38,0x11,0x14,0x08,0xDB,0xF4,0x44,0x00,0x04,0x81,0xD5,0x12,0x5A,0x58, +0x01,0x13,0x84,0x12,0x3E,0x07,0xF3,0x00,0x44,0x12,0x17,0x68,0x80,0x05,0x84,0x40, +0x9C,0xC1,0x38,0x20,0x80,0x08,0x8C,0x02,0x38,0x20,0x8C,0x08,0x5A,0x08,0x65,0xFA, +0x49,0xFF,0xFE,0xB2,0xFC,0x80,0xFC,0x41,0x3C,0x23,0xF9,0x7E,0x2E,0x17,0xF8,0x78, +0x96,0x91,0x96,0x48,0x44,0x32,0x1C,0xE0,0x5A,0x18,0x35,0x04,0x48,0x00,0x01,0x00, +0x5A,0x18,0x30,0x04,0x48,0x00,0x03,0xF7,0x5A,0x18,0x33,0x04,0x48,0x00,0x03,0xF3, +0x5A,0x18,0x31,0x04,0x48,0x00,0x03,0xF3,0x5A,0x18,0x34,0x04,0x48,0x00,0x03,0xEF, +0x5A,0x18,0x32,0x04,0x48,0x00,0x03,0xEF,0x84,0x00,0x5A,0x20,0x03,0x04,0x48,0x00, +0x00,0xEC,0x5A,0x18,0x53,0x0F,0x2E,0x07,0xF8,0x79,0x2E,0x17,0xF8,0x7A,0x40,0x00, +0x20,0x08,0xFE,0x0F,0x5A,0x08,0x48,0x04,0x48,0x00,0x01,0xBC,0x48,0x00,0x03,0xDF, +0x5A,0x10,0x01,0x04,0x48,0x00,0x03,0xDB,0x2E,0x07,0xF8,0x79,0x5A,0x08,0x01,0x0E, +0x2E,0x17,0xF8,0x7A,0x84,0x00,0x5A,0x18,0x01,0x05,0x3C,0x0F,0xFE,0x44,0x84,0x06, +0x2E,0x17,0xF8,0x7A,0x48,0x00,0x01,0x40,0x2E,0x07,0xF8,0x79,0x5A,0x08,0x02,0x14, +0x2E,0x07,0xF8,0x7A,0x5A,0x08,0x03,0x04,0x48,0x00,0x02,0x1A,0x2E,0x07,0xF8,0x7A, +0x4E,0x02,0x02,0x0F,0x2E,0x07,0xF8,0x7A,0x5A,0x00,0x01,0x04,0x48,0x00,0x03,0xB7, +0x48,0x00,0x02,0x00,0x2E,0x07,0xF8,0x79,0x5A,0x08,0x03,0x0E,0x2E,0x07,0xF8,0x7A, +0x4E,0x02,0x03,0xAD,0x2E,0x07,0xF8,0x7A,0x5A,0x00,0x01,0x04,0x48,0x00,0x00,0x7F, +0x48,0x00,0x03,0xA5,0x2E,0x07,0xF8,0x79,0x5A,0x08,0x04,0x12,0x2E,0x07,0xF8,0x7A, +0x4E,0x02,0x03,0x9D,0x2E,0x07,0xF8,0x7A,0x5A,0x08,0x01,0x04,0x48,0x00,0x03,0x97, +0x2E,0x07,0xF8,0x7A,0x5A,0x08,0x10,0x6B,0x48,0x00,0x03,0x91,0x2E,0x07,0xF8,0x79, +0x5A,0x08,0x05,0x04,0x48,0x00,0x03,0x8B,0x2E,0x07,0xF8,0x79,0x5A,0x08,0x06,0x12, +0x2E,0x07,0xF8,0x7A,0x96,0x00,0x5A,0x08,0x01,0x04,0x3C,0x0F,0xFC,0x30,0x2E,0x07, +0xF8,0x7A,0x96,0x00,0x4E,0x03,0x03,0x7B,0x3C,0x0F,0xFC,0x30,0x48,0x00,0x03,0x77, +0x2E,0x07,0xF8,0x79,0x5A,0x08,0x07,0x12,0x2E,0x07,0xF8,0x7A,0x96,0x00,0x5A,0x08, +0x01,0x04,0x3C,0x0F,0xF7,0xA2,0x2E,0x07,0xF8,0x7A,0x96,0x00,0x4E,0x03,0x03,0x67, +0x3C,0x0F,0xF7,0xA2,0x48,0x00,0x03,0x63,0x2E,0x07,0xF8,0x79,0x5A,0x08,0x08,0x07, +0x2E,0x07,0xF8,0x7A,0x4E,0x02,0x03,0x5B,0xD5,0x31,0x2E,0x07,0xF8,0x79,0x5A,0x08, +0x09,0x04,0x48,0x00,0x03,0x54,0x2E,0x07,0xF8,0x79,0x5A,0x08,0x0A,0x14,0x2E,0x07, +0xF8,0x7A,0x4E,0x02,0x01,0xAC,0x2E,0x07,0xF8,0x7A,0x5A,0x08,0x01,0x04,0x48,0x00, +0x01,0xAC,0x2E,0x07,0xF8,0x7A,0x5A,0x00,0x02,0x04,0x48,0x00,0x03,0x40,0x48,0x00, +0x01,0xAD,0x2E,0x07,0xF8,0x79,0x5A,0x08,0x0B,0x04,0x48,0x00,0x03,0x38,0x2E,0x07, +0xF8,0x79,0x5A,0x00,0x0C,0x0A,0x2E,0x07,0xF8,0x79,0x5A,0x00,0x0F,0x06,0x2E,0x07, +0xF8,0x79,0x5A,0x08,0x10,0x08,0x2E,0x07,0xF8,0x7A,0x2E,0x07,0xF8,0x7A,0x48,0x00, +0x03,0x26,0x2E,0x07,0xF8,0x79,0x5A,0x00,0x11,0x04,0x48,0x00,0x03,0x20,0x2E,0x07, +0xF8,0x7A,0xC8,0x07,0x3C,0x0D,0xFB,0x37,0x58,0x00,0x00,0x08,0x3C,0x0F,0xFB,0x37, +0x2E,0x07,0xF8,0x7A,0x5A,0x00,0x01,0x04,0x48,0x00,0x03,0x11,0x3C,0x1D,0xFB,0x37, +0x84,0x17,0xFE,0x0E,0x3C,0x0F,0xFB,0x37,0x48,0x00,0x03,0x09,0x80,0x01,0x5A,0x28, +0x03,0x04,0x48,0x00,0x01,0x28,0x5A,0x28,0x01,0x28,0x5A,0x18,0x01,0x04,0x48,0x00, +0x00,0xF4,0x5A,0x18,0x03,0x04,0x48,0x00,0x01,0x20,0x5A,0x18,0x20,0x04,0x48,0x00, +0x01,0x81,0x5A,0x18,0x28,0x04,0x48,0x00,0x01,0xDE,0x5A,0x18,0x21,0x04,0x48,0x00, +0x01,0xA0,0x5A,0x18,0x22,0x04,0x48,0x00,0x01,0xB8,0x5A,0x18,0x23,0x04,0x48,0x00, +0x01,0xC5,0x5A,0x18,0xF3,0x04,0x48,0x00,0x01,0xFD,0x5A,0x10,0xFE,0x04,0x48,0x00, +0x00,0xB7,0x48,0x00,0x02,0xB5,0x5A,0x20,0x02,0x04,0x48,0x00,0x00,0xB1,0x5A,0x18, +0x02,0x14,0x2E,0x17,0xF8,0x79,0x5A,0x18,0x03,0x04,0x48,0x00,0x01,0x29,0x2E,0x17, +0xF8,0x79,0x4E,0x12,0x01,0x1E,0x2E,0x17,0xF8,0x79,0x5A,0x10,0x01,0x04,0x48,0x00, +0x00,0x9F,0x48,0x00,0x01,0x0F,0x5A,0x18,0xF0,0x1D,0x2E,0x17,0xF8,0x79,0x4E,0x12, +0x00,0xF0,0x2E,0x17,0xF8,0x79,0x5A,0x18,0x01,0x04,0x48,0x00,0x01,0x2E,0x2E,0x17, +0xF8,0x79,0x5A,0x18,0x02,0x04,0x48,0x00,0x00,0xE8,0x2E,0x17,0xF8,0x79,0x5A,0x10, +0xFA,0x04,0x48,0x00,0x00,0x85,0x84,0x21,0x3E,0x17,0xF2,0xF8,0x48,0x00,0x00,0x80, +0x5A,0x18,0xFA,0x0E,0x2E,0x17,0xF8,0x79,0x5A,0x18,0x03,0x04,0x48,0x00,0x01,0x1F, +0x2E,0x17,0xF8,0x79,0x5A,0x18,0x08,0x74,0x48,0x00,0x01,0x1D,0x5A,0x18,0x01,0x14, +0x2E,0x17,0xF8,0x79,0x5A,0x18,0x01,0x06,0x84,0x00,0x3C,0x0F,0xFE,0x44,0x84,0x06, +0x2E,0x17,0xF8,0x79,0xC9,0x64,0x84,0x01,0x3C,0x0F,0xFE,0x44,0x49,0xFF,0xAC,0xFB, +0x48,0x00,0x00,0xC9,0x5A,0x18,0x0A,0x12,0x2E,0x17,0xF8,0x79,0x4E,0x12,0x00,0xDF, +0x2E,0x17,0xF8,0x79,0x5A,0x18,0x01,0x04,0x48,0x00,0x00,0xDF,0x2E,0x17,0xF8,0x79, +0x5A,0x18,0x02,0x4E,0x48,0x00,0x00,0xE2,0x5A,0x18,0x06,0x10,0x2E,0x17,0xF8,0x79, +0x96,0x48,0x5A,0x18,0x01,0x04,0x3C,0x1F,0xFC,0x30,0x2E,0x17,0xF8,0x79,0x96,0x48, +0xC9,0x3E,0x3C,0x1F,0xFC,0x30,0xD5,0x3B,0x5A,0x18,0x07,0x0F,0x2E,0x17,0xF8,0x79, +0x5A,0x18,0x01,0x04,0x3C,0x1F,0xF7,0xA2,0x2E,0x17,0xF8,0x79,0x96,0x48,0xC9,0x2F, +0x3C,0x1F,0xF7,0xA2,0xD5,0x2C,0x5A,0x10,0x0D,0x2B,0x5A,0x18,0x60,0x0E,0x2E,0x17, +0xF8,0x79,0x5A,0x18,0x02,0x04,0x48,0x00,0x02,0x4A,0x2E,0x17,0xF8,0x79,0x5A,0x18, +0x01,0x1F,0x48,0x00,0x02,0x44,0x5A,0x18,0xF6,0x08,0x2E,0x17,0xF8,0x79,0x5A,0x18, +0xF2,0x17,0x48,0x00,0x01,0x6A,0x5A,0x18,0x61,0x13,0x2E,0x17,0xF8,0x79,0x96,0x48, +0xC1,0x08,0x2E,0x17,0xF8,0x79,0x96,0x48,0x5A,0x10,0x01,0x04,0x48,0x00,0x02,0x2F, +0x44,0x00,0x01,0x00,0x49,0xFF,0xA3,0xBC,0x48,0x00,0x02,0x29,0x5A,0x08,0x01,0x1B, +0x44,0x0F,0xFF,0xA7,0x3E,0x07,0xF3,0x00,0x84,0x05,0x3E,0x07,0xF3,0x01,0xFA,0x0C, +0x3E,0x07,0xF3,0x02,0xFA,0x22,0xFA,0x10,0x3E,0x07,0xF3,0x03,0x3E,0x07,0xF3,0x04, +0x3E,0x17,0xF3,0x05,0x3E,0x07,0xF3,0x06,0x3E,0x17,0xF3,0x07,0x84,0x08,0x48,0x00, +0x01,0xFD,0x5A,0x08,0x02,0x30,0x2E,0x07,0xF9,0x09,0x44,0x1F,0xFF,0x81,0x3E,0x17, +0xF3,0x00,0x3E,0x07,0xF3,0x01,0xC8,0x0D,0x44,0x10,0x00,0x5A,0x3E,0x17,0xF3,0x02, +0x3E,0x07,0xF3,0x03,0xFA,0x1B,0x3E,0x07,0xF3,0x04,0x84,0x05,0x48,0x00,0x01,0xE6, +0x44,0x0F,0xFF,0xA7,0x3E,0x07,0xF3,0x02,0x84,0x05,0x3E,0x07,0xF3,0x03,0xFA,0x0C, +0x3E,0x07,0xF3,0x04,0xFA,0x22,0xFA,0x10,0x3E,0x07,0xF3,0x05,0x3E,0x17,0xF3,0x06, +0x3E,0x07,0xF3,0x07,0x3E,0x17,0xF3,0x08,0x3E,0x07,0xF3,0x09,0x84,0x0A,0x48,0x00, +0x01,0xCD,0x5A,0x08,0x05,0x0C,0x84,0x20,0x84,0x01,0x3E,0x07,0xF3,0x00,0x3E,0x17, +0xF3,0x01,0x3E,0x17,0xF3,0x02,0x48,0x00,0x00,0xAA,0x5A,0x08,0x03,0x04,0x84,0x00, +0xD5,0x04,0x5A,0x08,0x04,0x07,0x84,0x02,0x3E,0x07,0xF9,0x09,0x48,0x00,0x01,0xC7, +0x5A,0x08,0x06,0x07,0x84,0x01,0x3E,0x07,0xF2,0xED,0x48,0x00,0x01,0xC0,0x5A,0x08, +0x07,0x07,0x84,0x01,0x3E,0x07,0xF2,0xEC,0x48,0x00,0x01,0xB9,0x5A,0x08,0x08,0x07, +0x84,0x01,0x3E,0x07,0xF2,0xEB,0x48,0x00,0x01,0xB2,0x5A,0x08,0x09,0x07,0x84,0x01, +0x3E,0x07,0xF2,0xEA,0x48,0x00,0x01,0xAB,0x5A,0x08,0x0A,0x07,0x84,0x01,0x3E,0x07, +0xF2,0xE9,0x48,0x00,0x01,0xA4,0x5A,0x08,0x0B,0x06,0x84,0x01,0x3E,0x07,0xF2,0xE7, +0xD5,0x0F,0x5A,0x08,0x0C,0x09,0x84,0x01,0x3E,0x07,0xF2,0xE8,0x3E,0x07,0xF2,0xE6, +0x48,0x00,0x01,0x95,0x5A,0x08,0x0D,0x07,0x84,0x01,0x3E,0x07,0xF2,0xE8,0x84,0x00, +0xD5,0xF6,0x5A,0x08,0x14,0x0A,0x84,0x01,0x3E,0x07,0xF9,0x09,0x84,0x01,0x49,0xFF, +0xE5,0x92,0x48,0x00,0x01,0x84,0x5A,0x08,0x29,0x04,0x84,0x01,0xD5,0x04,0x5A,0x08, +0x2A,0x07,0x84,0x02,0x3E,0x07,0xDF,0x9C,0x48,0x00,0x01,0x79,0x5A,0x08,0x22,0x27, +0x84,0x00,0xFA,0x30,0x84,0x7F,0x84,0x47,0x3E,0x17,0xF3,0x00,0x3E,0x07,0xF3,0x01, +0x3E,0x07,0xF3,0x02,0x3E,0x37,0xF3,0x03,0x3E,0x27,0xF3,0x04,0x3E,0x37,0xF3,0x05, +0x3E,0x27,0xF3,0x06,0x84,0x6A,0xFA,0x42,0x3E,0x27,0xF3,0x07,0x3E,0x17,0xF3,0x08, +0x3E,0x37,0xF3,0x09,0x3E,0x07,0xF3,0x0A,0x3E,0x27,0xF3,0x0B,0x3E,0x17,0xF3,0x0C, +0x3E,0x07,0xF3,0x0D,0x84,0x0E,0x48,0x00,0x01,0x41,0x5A,0x08,0x23,0x1C,0xFA,0x11, +0x3E,0x07,0xF3,0x00,0x44,0x20,0x00,0x69,0x84,0x00,0x84,0x26,0x3E,0x07,0xF3,0x01, +0x3E,0x27,0xF3,0x02,0x3E,0x17,0xF3,0x03,0x3E,0x07,0xF3,0x04,0x3E,0x07,0xF3,0x05, +0x3E,0x27,0xF3,0x06,0x3E,0x17,0xF3,0x07,0x3E,0x07,0xF3,0x08,0x84,0x09,0x48,0x00, +0x01,0x25,0x5A,0x08,0x24,0x11,0xFA,0x12,0x3E,0x07,0xF3,0x00,0x84,0x05,0x3E,0x07, +0xF3,0x01,0x84,0x04,0x3E,0x07,0xF3,0x02,0x84,0x00,0x3E,0x07,0xF3,0x03,0x84,0x04, +0x48,0x00,0x01,0x14,0x5A,0x08,0x25,0x0D,0xFA,0x13,0x3E,0x07,0xF3,0x00,0x84,0x01, +0x3E,0x07,0xF3,0x01,0x84,0x00,0x3E,0x07,0xF3,0x02,0x84,0x05,0xD5,0xEF,0x5A,0x08, +0x26,0x2F,0xFA,0x18,0x3E,0x07,0xF3,0x00,0x84,0x46,0x84,0x00,0x84,0x2C,0x84,0x62, +0x3E,0x37,0xF3,0x01,0x3E,0x07,0xF3,0x02,0x3E,0x27,0xF3,0x03,0x3E,0x17,0xF3,0x04, +0x3E,0x37,0xF3,0x05,0x3E,0x07,0xF3,0x06,0x3E,0x27,0xF3,0x07,0x3E,0x17,0xF3,0x08, +0x84,0x44,0x84,0x21,0x3E,0x17,0xF3,0x09,0x3E,0x07,0xF3,0x0A,0x3E,0x27,0xF3,0x0B, +0x3E,0x07,0xF3,0x0C,0x3E,0x17,0xF3,0x0D,0x84,0x23,0x3E,0x07,0xF3,0x0E,0x3E,0x17, +0xF3,0x0F,0x3E,0x07,0xF3,0x10,0xFA,0x01,0x48,0x00,0x00,0xD8,0x5A,0x08,0x27,0x13, +0x49,0xFF,0xE4,0xEE,0x5A,0x08,0x01,0x05,0x44,0x00,0x00,0x40,0xD5,0x06,0x49,0xFF, +0xE4,0xE7,0xC8,0x05,0x44,0x00,0x00,0x41,0x3E,0x07,0xF3,0x00,0x84,0x01,0x48,0x00, +0x00,0xC5,0x5A,0x08,0x2D,0x06,0x49,0xFF,0xFC,0x8F,0x48,0x00,0x00,0xD0,0x5A,0x08, +0x32,0x35,0x2E,0x67,0xF8,0x79,0x2E,0x57,0xF8,0x7B,0x2E,0x07,0xF8,0x7A,0x40,0x52, +0xA0,0x08,0x2E,0x47,0xF8,0x7D,0x2E,0x27,0xF8,0x7C,0xFF,0x47,0x84,0x26,0x97,0xB0, +0x97,0x20,0x96,0x90,0x84,0x00,0x99,0xE9,0xE0,0xE1,0xE9,0x08,0x8C,0x21,0x38,0x91, +0x84,0x00,0x96,0x49,0x88,0x09,0x96,0x00,0xD5,0xF8,0x4E,0x03,0x00,0xB0,0x5A,0x60, +0x02,0x04,0x48,0x00,0x00,0xAC,0x40,0x42,0x20,0x08,0xFE,0xA7,0x44,0x12,0x10,0x00, +0x88,0x41,0x96,0x41,0xE2,0x25,0x4E,0xF2,0x00,0xA2,0x9C,0x47,0x38,0x11,0x84,0x00, +0x38,0x11,0x00,0x08,0x8C,0x01,0xD5,0xF6,0x5A,0x08,0x33,0x28,0x2E,0x07,0xF8,0x7A, +0x2E,0x17,0xF8,0x79,0x40,0x00,0x20,0x08,0x2E,0x27,0xF8,0x7C,0xFE,0x0F,0x2E,0x17, +0xF8,0x7B,0x40,0x21,0x20,0x08,0xFE,0x8F,0x2E,0x17,0xF8,0x78,0x44,0x32,0x10,0x00, +0x96,0x48,0x3E,0x17,0xF3,0x00,0x88,0x43,0x84,0x20,0x44,0x42,0x17,0x68,0x96,0xC9, +0xE2,0x60,0xE8,0x08,0x9C,0xC9,0x38,0x11,0x04,0x00,0x38,0x12,0x0C,0x08,0x80,0x23, +0xD5,0xF7,0x8C,0x01,0x96,0x01,0xD5,0x61,0x5A,0x08,0x34,0x2C,0x2E,0x07,0xF8,0x79, +0x5A,0x08,0x5A,0x6D,0x2E,0x07,0xF8,0x7A,0x5A,0x08,0xA5,0x69,0xFA,0x00,0x49,0xFF, +0xDD,0xD3,0x44,0x11,0x00,0x00,0x44,0x20,0x01,0x00,0x44,0x02,0x10,0x00,0x49,0xFF, +0xDD,0xF4,0x49,0xFF,0xDE,0x6E,0x40,0x10,0x60,0x09,0x10,0x1F,0x80,0x04,0x40,0x10, +0x40,0x09,0x10,0x1F,0x80,0x05,0x40,0x10,0x20,0x09,0x10,0x1F,0x80,0x06,0x10,0x0F, +0x80,0x07,0x44,0x11,0x0F,0xFC,0xB0,0x01,0x84,0x44,0x49,0xFF,0xDD,0xDE,0xD5,0x46, +0x5A,0x08,0x35,0x1C,0x2E,0x17,0xF8,0x79,0x5A,0x18,0x01,0x0F,0x2E,0x07,0xF8,0x7A, +0x96,0x00,0x5A,0x00,0x01,0x06,0x5A,0x08,0x02,0x3A,0x84,0x04,0xD5,0x02,0x84,0x02, +0x3E,0x07,0xDE,0xCD,0xD5,0x33,0x3E,0x07,0xF3,0x00,0x2E,0x07,0xDE,0xCD,0x96,0x00, +0x3E,0x07,0xF3,0x01,0x84,0x02,0xD5,0x19,0x5A,0x08,0x28,0x29,0x84,0x1E,0x3E,0x07, +0xF3,0x00,0x84,0x01,0x3E,0x07,0xF3,0x01,0x84,0x02,0x3E,0x07,0xF3,0x02,0x84,0x03, +0x3E,0x07,0xF3,0x03,0x84,0x04,0x3E,0x07,0xF3,0x04,0x84,0x05,0x3E,0x07,0xF3,0x05, +0x84,0x06,0x3E,0x07,0xF3,0x06,0x84,0x07,0x49,0xFF,0xFA,0xAE,0x49,0xFF,0xE6,0x2A, +0xD5,0x0D,0x44,0x00,0x00,0x32,0x48,0xFF,0xFD,0x04,0x44,0x00,0x00,0x33,0x48,0xFF, +0xFD,0x00,0x44,0x00,0x00,0x34,0x48,0xFF,0xFC,0xFC,0xFC,0xC1,0xFC,0x00,0x44,0x04, +0x10,0x04,0x44,0x10,0x00,0x55,0xAE,0x40,0x44,0x1F,0xFF,0xAA,0xAE,0x41,0x44,0x10, +0x00,0x66,0xAE,0x42,0x84,0x01,0x49,0xFF,0xA9,0x6E,0xFC,0x80,0x44,0x14,0x10,0x08, +0xB6,0x01,0x44,0x14,0x80,0x04,0xA6,0x0B,0x92,0x01,0x96,0x06,0xC0,0xFD,0x44,0x04, +0x80,0x07,0x84,0x22,0xAE,0x40,0xDD,0x9E,0xFC,0x40,0x44,0x37,0x20,0xC0,0x46,0x10, +0x20,0x00,0xB4,0x43,0x44,0x47,0x20,0xC4,0xFE,0x57,0xB6,0x23,0xB4,0x24,0x44,0x24, +0x10,0x10,0x92,0x34,0x40,0x10,0xD0,0x08,0xFE,0x57,0xB6,0x24,0x44,0x17,0x20,0xC8, +0x40,0x00,0x30,0x08,0xB4,0x41,0x84,0xC1,0x92,0x48,0x40,0x21,0x20,0x08,0xB6,0x41, +0xB4,0x81,0x46,0x2F,0xCF,0xFF,0x50,0x21,0x0F,0xFF,0xFF,0x16,0xB6,0x81,0xB4,0xA1, +0x46,0x48,0x00,0x00,0xFF,0x67,0xB6,0xA1,0x44,0x57,0x20,0xD0,0x44,0x70,0xA0,0x00, +0xB4,0x25,0x44,0x94,0x10,0x00,0x94,0x49,0x92,0x21,0xB6,0x25,0x44,0x57,0x20,0xD4, +0x40,0x10,0x30,0x09,0xB4,0x05,0x92,0x14,0x40,0x00,0x50,0x08,0xFE,0x0F,0xB6,0x05, +0x44,0x07,0x20,0xD8,0xB4,0x20,0x92,0x28,0x40,0x10,0xA0,0x08,0xFE,0x77,0xB6,0x20, +0xB4,0x20,0xFE,0x8E,0xB6,0x40,0xB4,0x20,0x44,0x27,0x20,0xDC,0xFF,0x0F,0xB6,0x80, +0xB4,0x22,0x46,0x0F,0x0F,0xFF,0x92,0x31,0x40,0x10,0xC4,0x08,0xFE,0x7F,0xB6,0x22, +0xB4,0x22,0x50,0x00,0x0F,0xFF,0xFE,0x0E,0x44,0x14,0x80,0x84,0xB6,0x02,0xB4,0x41, +0x44,0x0D,0xFF,0xFF,0xFE,0x16,0xB6,0x01,0xB4,0x23,0x46,0x0F,0xFF,0x0F,0x50,0x00, +0x0F,0xFF,0xFE,0x0E,0x44,0x11,0x00,0x00,0xFE,0x0F,0xB6,0x03,0x84,0x00,0x10,0x64, +0x80,0x02,0x10,0x04,0x80,0x00,0x49,0xFF,0xFF,0x73,0x84,0x0B,0x49,0xFF,0xFF,0x80, +0x80,0x06,0x49,0xFF,0xFF,0x7D,0x44,0x00,0x00,0x30,0x49,0xFF,0xFF,0x79,0x84,0x00, +0x49,0xFF,0xFF,0x76,0x00,0x04,0x80,0x03,0x54,0x00,0x00,0xFE,0x10,0x04,0x80,0x03, +0x84,0x00,0x49,0xFF,0xFF,0x6D,0x44,0x04,0x10,0x0C,0x44,0x14,0x80,0x07,0xB6,0xE0, +0x84,0x02,0xAE,0x08,0x44,0x14,0x80,0x06,0xAE,0x08,0x44,0x04,0x10,0x13,0xAF,0x80, +0xFC,0xC0,0xFC,0x00,0x49,0xFF,0xE3,0x54,0x5A,0x08,0x01,0x0D,0x84,0x00,0x49,0xFF, +0xFF,0x65,0x49,0xFF,0xA9,0xE1,0x84,0x00,0x49,0x00,0x06,0x65,0x84,0x00,0x49,0xFF, +0xE3,0x4A,0xFC,0x80,0xE6,0x0F,0xE8,0x06,0x44,0x10,0xF5,0x40,0x38,0x00,0x80,0x00, +0xDD,0x9E,0x44,0x00,0x00,0xFF,0xDD,0x9E,0xFC,0x01,0x44,0x1F,0xFF,0xAA,0x10,0x1F, +0x80,0x00,0x10,0x0F,0x80,0x01,0x84,0x28,0x84,0x00,0x10,0x0F,0x80,0x02,0x10,0x1F, +0x80,0x03,0x10,0x0F,0x80,0x04,0x10,0x0F,0x80,0x05,0x10,0x0F,0x80,0x06,0x84,0x27, +0x80,0x1F,0x49,0xFF,0xF7,0xCB,0xFC,0x81,0xFC,0x36,0x3C,0x37,0xF6,0x62,0x10,0x0F, +0x80,0x05,0x84,0x00,0x3C,0x27,0xF6,0x5C,0x10,0x0F,0x80,0x06,0x40,0x01,0xA0,0x0A, +0x44,0x1F,0xFF,0xAA,0x96,0x01,0x10,0x1F,0x80,0x04,0x10,0x1F,0x80,0x07,0x94,0x44, +0x40,0x01,0x20,0x0A,0x10,0x3F,0x80,0x09,0x96,0x1F,0x3C,0x37,0xF6,0x58,0xFE,0x0F, +0x10,0x0F,0x80,0x08,0x10,0x2F,0x80,0x0A,0x40,0x01,0xA0,0x0A,0x3C,0x27,0xF6,0x68, +0x96,0x01,0x94,0x44,0x40,0x01,0x20,0x0A,0x10,0x3F,0x80,0x0C,0x96,0x1F,0x3C,0x37, +0xF6,0x5E,0xFE,0x0F,0x10,0x0F,0x80,0x0B,0x10,0x2F,0x80,0x0D,0x40,0x01,0xA0,0x0A, +0x3C,0x27,0xF6,0x60,0x96,0x01,0x94,0x44,0x40,0x01,0x20,0x0A,0x10,0x3F,0x80,0x0F, +0x96,0x1F,0x3C,0x37,0xF6,0x5D,0xFE,0x0F,0x10,0x0F,0x80,0x0E,0x10,0x2F,0x80,0x10, +0x40,0x01,0xA0,0x0A,0x3C,0x27,0xF6,0x65,0x96,0x01,0x94,0x44,0x40,0x01,0x20,0x0A, +0x10,0x3F,0x80,0x12,0x96,0x1F,0x3C,0x37,0xF6,0x82,0xFE,0x0F,0x10,0x0F,0x80,0x11, +0x10,0x2F,0x80,0x13,0x40,0x01,0xA0,0x0A,0x3C,0x27,0xF6,0x86,0x96,0x01,0x94,0x44, +0x40,0x01,0x20,0x0A,0x10,0x3F,0x80,0x15,0x96,0x1F,0x3C,0x37,0xF6,0x83,0xFE,0x0F, +0x10,0x0F,0x80,0x14,0x10,0x2F,0x80,0x16,0x40,0x01,0xA0,0x0A,0x3C,0x27,0xF6,0x87, +0x96,0x01,0x94,0x44,0x40,0x01,0x20,0x0A,0x96,0x1F,0xFE,0x0F,0x10,0x0F,0x80,0x17, +0x10,0x3F,0x80,0x18,0x10,0x2F,0x80,0x19,0x3C,0x37,0xF6,0x84,0x3C,0x27,0xF6,0x88, +0x40,0x01,0xA0,0x0A,0x96,0x01,0x94,0x44,0x40,0x01,0x20,0x0A,0x96,0x1F,0x10,0x3F, +0x80,0x1B,0x3C,0x37,0xF6,0x85,0xFE,0x0F,0x10,0x0F,0x80,0x1A,0x10,0x2F,0x80,0x1C, +0x40,0x01,0xA0,0x0A,0x3C,0x27,0xF6,0x89,0x96,0x01,0x94,0x44,0x40,0x01,0x20,0x0A, +0x96,0x1F,0x10,0x3F,0x80,0x1E,0x3C,0x37,0xF6,0x5F,0xFE,0x0F,0x10,0x0F,0x80,0x1D, +0x10,0x2F,0x80,0x1F,0x40,0x01,0xA0,0x0A,0x3C,0x27,0xF6,0x66,0x96,0x01,0x94,0x44, +0x40,0x01,0x20,0x0A,0x96,0x1F,0x10,0x3F,0x80,0x21,0x3C,0x37,0xF6,0x63,0xFE,0x0F, +0x10,0x0F,0x80,0x20,0x10,0x2F,0x80,0x22,0x40,0x01,0xA0,0x0A,0x3C,0x27,0xF6,0x59, +0x96,0x01,0x94,0x44,0x40,0x01,0x20,0x0A,0x96,0x1F,0xFE,0x0F,0x10,0x0F,0x80,0x23, +0x2E,0x07,0xEC,0x9A,0x10,0x3F,0x80,0x24,0x10,0x0F,0x80,0x26,0x2E,0x07,0xEC,0x9B, +0x3C,0x37,0xF6,0x5B,0x10,0x0F,0x80,0x27,0x2E,0x07,0xEC,0x99,0x10,0x2F,0x80,0x25, +0x10,0x0F,0x80,0x28,0x3C,0x27,0xF6,0x61,0x40,0x01,0xA0,0x0A,0x96,0x01,0x94,0x44, +0x40,0x01,0x20,0x0A,0x96,0x1F,0xFE,0x0F,0x10,0x0F,0x80,0x29,0x10,0x3F,0x80,0x2A, +0x10,0x2F,0x80,0x2B,0xB0,0x41,0x84,0x00,0x44,0x72,0x3A,0x10,0x44,0x62,0x3A,0x6C, +0x38,0x53,0x81,0x11,0x38,0x43,0x01,0x11,0x40,0x22,0xA0,0x0A,0x96,0x91,0x94,0xD4, +0x40,0x22,0x20,0x0A,0x96,0x9F,0xFE,0x9F,0x8C,0x01,0x10,0x20,0x80,0x28,0x10,0x50, +0x80,0x29,0x10,0x40,0x80,0x2A,0x8C,0x23,0x5A,0x08,0x28,0xEC,0x84,0x00,0x10,0x0F, +0x80,0xA4,0x10,0x0F,0x80,0xA5,0x10,0x0F,0x80,0xA6,0x10,0x0F,0x80,0xA7,0x10,0x0F, +0x80,0xA8,0x10,0x0F,0x80,0xA9,0x10,0x0F,0x80,0xAA,0x10,0x0F,0x80,0xAB,0x10,0x0F, +0x80,0xAC,0x44,0x10,0x00,0xA9,0xB0,0x01,0x49,0xFF,0xF6,0xB8,0xFC,0xB6,0xFC,0x00, +0x44,0x00,0x00,0x64,0x49,0xFF,0x9C,0x4F,0x44,0x00,0x0D,0xE9,0x49,0xFF,0x9C,0x56, +0x44,0x00,0x09,0xE9,0x49,0xFF,0x9C,0x5D,0x44,0x00,0x01,0x90,0x49,0xFF,0x9C,0x64, +0xFC,0x80,0xFC,0x40,0x51,0xFF,0xFA,0xE0,0x84,0x20,0xFA,0x50,0xB0,0x10,0x49,0x00, +0x12,0xA7,0xB1,0x98,0x44,0x00,0xF5,0x50,0x80,0x26,0x3A,0x20,0x14,0x04,0x3A,0x20, +0x94,0x24,0x3A,0x20,0x14,0x00,0xB1,0xE0,0x44,0x00,0xF5,0x70,0x3A,0x20,0x94,0x20, +0x80,0x27,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24,0x3A,0x20,0x14,0x00,0x3A,0x20, +0x94,0x20,0x44,0x10,0xF5,0x90,0xB0,0x0C,0x3A,0x20,0x94,0x00,0x44,0x10,0xF5,0xA0, +0x3A,0x20,0x14,0x20,0xB0,0x07,0x3A,0x20,0x8C,0x00,0x3A,0x20,0x0C,0x20,0x44,0x00, +0xF5,0xA8,0x50,0x8F,0x80,0x24,0x3A,0x20,0x10,0x00,0x3A,0x24,0x10,0x20,0x85,0x40, +0x39,0xC4,0x28,0x00,0x40,0x0E,0x00,0x13,0xC0,0x13,0x5A,0x00,0x03,0x12,0xB0,0x10, +0x38,0x00,0x72,0x02,0xC8,0x0D,0x38,0x03,0x72,0x02,0x40,0x90,0x04,0x08,0x49,0xFF, +0xE3,0x75,0x49,0xFF,0xE6,0x45,0xB0,0x50,0x88,0x09,0x38,0x00,0xF2,0x0A,0x8D,0x41, +0x5A,0xA8,0x0C,0xE8,0x84,0xC0,0xB0,0x10,0x38,0x00,0x1A,0x02,0xF0,0x81,0xC8,0x07, +0x8C,0xC1,0x5A,0x68,0x08,0xFA,0x51,0xFF,0x85,0x20,0xFC,0xC0,0xB0,0x07,0x87,0x80, +0x38,0xA3,0x00,0x00,0x40,0x95,0x04,0x08,0x40,0x04,0x80,0x13,0xF0,0x82,0x94,0x01, +0xF0,0x83,0x40,0xFE,0x24,0x07,0xE8,0xED,0x4F,0xC3,0x00,0x13,0x38,0x83,0x9A,0x01, +0x49,0xFF,0x9A,0xEF,0x44,0x20,0x01,0x20,0x80,0x28,0x42,0x10,0x08,0x73,0xB0,0x0C, +0x96,0x49,0x38,0x20,0x19,0x01,0xB0,0x28,0x92,0x42,0x49,0xFF,0x9B,0x49,0x4D,0xC5, +0x40,0x14,0x38,0x83,0x9A,0x01,0x49,0xFF,0x9A,0xDC,0x44,0x20,0x01,0x20,0x50,0x14, +0x02,0x40,0x42,0x10,0x08,0x73,0xB0,0x0C,0x96,0x49,0x38,0x20,0x19,0x01,0xB0,0x28, +0x92,0x42,0x49,0xFF,0x9B,0x35,0x40,0x14,0xF0,0x01,0x8E,0x21,0x94,0x4C,0x40,0x0E, +0x00,0x13,0x97,0x49,0x40,0x1E,0x28,0x06,0x94,0xC4,0x84,0x40,0xF1,0x84,0x50,0x8F, +0x80,0xA0,0xF4,0x04,0x40,0x11,0x04,0x09,0xC4,0x03,0x88,0x23,0xD5,0x03,0x88,0x25, +0x96,0x49,0x38,0x44,0x06,0x02,0x54,0xF2,0x00,0x1F,0x5A,0xF0,0x1F,0x1A,0x40,0xF2, +0x18,0x09,0x54,0xF7,0x80,0x1F,0x5A,0xF0,0x1F,0x14,0x04,0xFF,0x80,0x01,0x39,0xE7, +0x81,0x01,0x50,0xFF,0x5D,0xEB,0x5C,0xF7,0x82,0x13,0xE9,0x06,0x50,0xFF,0x64,0x27, +0x5C,0xF7,0x82,0x13,0xE8,0x05,0x50,0x42,0x00,0x41,0x38,0x44,0x06,0x0A,0x38,0x44, +0x06,0x02,0x40,0xF2,0x48,0x09,0x54,0xF7,0x80,0x1F,0x5A,0xF0,0x1F,0x21,0x40,0xF2, +0x30,0x09,0x54,0xF7,0x80,0x1F,0x5A,0xF0,0x1F,0x1B,0x04,0xFF,0x80,0x02,0x05,0xEF, +0x80,0x01,0x40,0xF0,0x3C,0x00,0x40,0xF7,0x80,0x13,0x39,0xEF,0x3D,0x01,0x50,0xFF, +0x5D,0xEB,0x5C,0xF7,0x82,0x13,0xE9,0x06,0x50,0xFF,0x64,0x27,0x5C,0xF7,0x82,0x13, +0xE8,0x06,0x44,0xF4,0x10,0x00,0x88,0x8F,0x38,0x44,0x06,0x0A,0xF1,0x03,0x8C,0x42, +0x88,0x01,0x96,0x90,0x96,0x01,0x5A,0x28,0x20,0xAE,0x50,0x5E,0x00,0x01,0x4C,0x55, +0x40,0x16,0x38,0x13,0x9A,0x01,0xF5,0x85,0xF1,0x84,0x49,0xFF,0x9A,0x62,0x44,0x20, +0x01,0x20,0xF1,0x04,0x42,0x10,0x08,0x73,0xB0,0x0C,0x96,0x49,0x38,0x20,0x19,0x01, +0x80,0x08,0x92,0x42,0x49,0xFF,0x9A,0xD8,0xF5,0x05,0x4C,0x54,0xC0,0x16,0x38,0x13, +0x9A,0x01,0xF1,0x84,0x49,0xFF,0x9A,0x4D,0xF1,0x04,0x44,0x20,0x01,0x20,0x50,0x10, +0x82,0x40,0x42,0x10,0x08,0x73,0xB0,0x0C,0x96,0x49,0x38,0x20,0x19,0x01,0x80,0x08, +0x92,0x42,0x49,0xFF,0x9A,0xC1,0x50,0x5E,0x00,0x01,0x55,0xC2,0x80,0xFF,0x48,0xFF, +0xFF,0x42,0xFC,0x49,0x44,0x10,0xF5,0xB4,0xB1,0x86,0x80,0x06,0x3A,0x20,0x94,0x04, +0x3A,0x20,0x14,0x24,0x3A,0x20,0x8C,0x00,0x3A,0x20,0x0C,0x20,0x84,0x20,0xB0,0x0C, +0xFA,0x48,0x49,0x00,0x11,0x75,0x84,0x21,0x10,0x1F,0x80,0x31,0x10,0x1F,0x80,0x3D, +0x84,0x02,0x44,0x10,0xF5,0xCC,0x10,0x0F,0x80,0x32,0x10,0x0F,0x80,0x3E,0x80,0x01, +0x80,0x9F,0x3A,0x20,0x0C,0x04,0x3A,0x22,0x0C,0x24,0xB1,0xC3,0x3A,0x20,0x8C,0x04, +0xA4,0x00,0x80,0x27,0xAC,0x20,0x3A,0x20,0x8C,0x24,0xAC,0x08,0x80,0x06,0x49,0xFF, +0xA0,0x93,0xB0,0x0C,0x49,0xFF,0xA0,0xE3,0x80,0x1F,0x49,0xFF,0xA0,0xA6,0x80,0x07, +0x81,0x3F,0x49,0xFF,0xA0,0xF5,0xFC,0xC9,0xFC,0x26,0x44,0x10,0xF5,0xD8,0x80,0xDF, +0x3A,0x20,0x94,0x04,0x3A,0x23,0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x23,0x14,0x24, +0x3A,0x20,0x94,0x00,0x80,0x1F,0x80,0xE6,0x3A,0x23,0x14,0x20,0x49,0xFF,0xA0,0x3E, +0xFC,0xA6,0xFC,0x2C,0x44,0x10,0xF6,0x08,0xB1,0x8C,0x80,0x06,0x3A,0x20,0x94,0x04, +0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x00, +0x3A,0x20,0x14,0x20,0x84,0x20,0xB0,0x06,0xFA,0x48,0x49,0x00,0x11,0x21,0xB1,0xC1, +0x44,0x10,0xF6,0x38,0x80,0x07,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0xB4,0x21, +0xB6,0x20,0x80,0x06,0x49,0xFF,0xA0,0x1A,0xB0,0x06,0x49,0xFF,0xA0,0x34,0x80,0x07, +0x49,0xFF,0xA0,0x1E,0xFC,0xAC,0xFC,0x01,0x44,0x10,0xF6,0x4C,0x80,0x1F,0x3A,0x20, +0x8C,0x00,0x3A,0x2F,0x8C,0x20,0x49,0xFF,0xA0,0x1D,0xFC,0x81,0xFC,0x00,0xC8,0x04, +0x49,0xFF,0xFE,0x3F,0xFC,0x80,0x44,0x00,0x00,0x64,0x49,0xFF,0x9A,0x8C,0x44,0x00, +0x13,0xA6,0x49,0xFF,0x9A,0x93,0x44,0x00,0x0F,0xA6,0x49,0xFF,0x9A,0x9A,0x44,0x00, +0x01,0x90,0x49,0xFF,0x9A,0xA1,0xFC,0x80,0xFC,0x00,0xE6,0x06,0xE8,0x06,0x44,0x10, +0xF6,0xEC,0x38,0x00,0x80,0x00,0xD5,0x02,0x84,0x00,0x49,0xFF,0x9A,0xAB,0xFC,0x80, +0xFC,0x00,0xE6,0x06,0xE8,0x06,0x44,0x10,0xF6,0xE4,0x38,0x00,0x80,0x00,0xD5,0x02, +0x84,0x00,0x8C,0x01,0x96,0x00,0x49,0xFF,0x9A,0x92,0xFC,0x80,0xFC,0x00,0x5A,0x00, +0x01,0x0E,0xC0,0x04,0x5A,0x00,0x02,0x1C,0xFC,0x80,0x84,0x00,0x49,0xFF,0xA6,0x83, +0x2E,0x07,0xF9,0x18,0x49,0xFF,0xFF,0xC4,0xD5,0x2A,0x49,0xFF,0xA6,0x7C,0x44,0x00, +0x00,0x64,0x49,0xFF,0x9A,0x50,0x44,0x00,0x17,0x8E,0x49,0xFF,0x9A,0x57,0x44,0x00, +0x17,0x8E,0x49,0xFF,0x9A,0x5E,0x44,0x00,0x01,0x90,0xD5,0x17,0x84,0x01,0x49,0xFF, +0xA6,0x6A,0x44,0x00,0x03,0xE8,0x49,0xFF,0x9A,0x3E,0x44,0x00,0x27,0x10,0x49,0xFF, +0x9A,0x50,0x46,0x20,0x00,0x81,0x46,0x12,0x71,0x00,0x83,0x82,0xB8,0x10,0x96,0x01, +0xFE,0x0F,0xB8,0x90,0x44,0x00,0x27,0x10,0x49,0xFF,0x9A,0x4E,0x84,0x00,0x49,0xFF, +0xFF,0xB9,0xFC,0x80,0xE6,0x04,0xE8,0x24,0xFC,0x00,0x44,0xF0,0xCF,0x88,0x38,0x07, +0x80,0x00,0x40,0xF0,0x3C,0x00,0xDD,0x0F,0x04,0x14,0x1E,0x22,0x84,0x00,0x49,0xFF, +0x9D,0x35,0x84,0x01,0x49,0xFF,0x9D,0x20,0x84,0x01,0xD5,0x0F,0x84,0x00,0x49,0xFF, +0x9D,0x2D,0x84,0x01,0xD5,0x07,0x84,0x01,0xD5,0x02,0x84,0x00,0x49,0xFF,0x9D,0x26, +0x84,0x00,0x49,0xFF,0x9D,0x11,0x84,0x00,0x49,0xFF,0x9D,0x17,0xFC,0x80,0xDD,0x9E, +0xFC,0x00,0x49,0xFF,0xE1,0x7B,0x80,0x60,0x3E,0x00,0x15,0x10,0x44,0x12,0x30,0x78, +0x44,0x02,0x34,0xF8,0x44,0x20,0x02,0x40,0x2E,0x47,0xF9,0x17,0x49,0xFF,0xEA,0x39, +0xFC,0x80,0x44,0x02,0x34,0xF8,0xDD,0x9E,0x44,0x02,0x30,0x78,0xDD,0x9E,0x44,0x02, +0x26,0x70,0xDD,0x9E,0x44,0x02,0x25,0x68,0xDD,0x9E,0x44,0x02,0x1D,0xE8,0xDD,0x9E, +0x44,0x02,0x1D,0x84,0xDD,0x9E,0xFC,0x00,0xC8,0x05,0x49,0xFF,0x9A,0x37,0x84,0xC3, +0xD5,0x05,0x84,0x01,0x49,0xFF,0x9A,0x32,0x84,0xC2,0x84,0x01,0x49,0xFF,0x9A,0x20, +0x9E,0x32,0x96,0x00,0x49,0xFF,0xDF,0xB9,0xFC,0x80,0xFC,0x00,0x84,0x00,0x80,0x20, +0x49,0xFF,0x9C,0x2E,0x84,0x00,0x84,0x21,0x49,0xFF,0x9C,0x2A,0xFC,0x80,0xFC,0x00, +0x84,0x01,0x84,0x20,0x49,0xFF,0x9C,0x24,0x84,0x01,0x80,0x20,0x49,0xFF,0x9C,0x20, +0xFC,0x80,0xFC,0x00,0x49,0xFF,0xE1,0x32,0x52,0x10,0x00,0x01,0x96,0x48,0x84,0x01, +0x49,0xFF,0x9C,0x16,0xFC,0x80,0xFC,0x49,0x80,0x20,0x44,0x00,0xF6,0x54,0x50,0xAF, +0x80,0x0C,0x3A,0x20,0x10,0x00,0x44,0x00,0xF6,0x60,0x3A,0x2F,0x90,0x20,0x3A,0x20, +0x10,0x00,0x3A,0x25,0x10,0x20,0x44,0x20,0xF6,0x6C,0xB0,0x06,0x3A,0x21,0x10,0x00, +0x3A,0x20,0x10,0x20,0x44,0x20,0xF6,0x78,0x50,0x9F,0x80,0x24,0x3A,0x21,0x10,0x00, +0x3A,0x24,0x90,0x20,0x44,0x20,0xF5,0xA8,0xB1,0xCC,0x3A,0x21,0x10,0x00,0x3A,0x23, +0x90,0x20,0x44,0x20,0xF6,0x84,0xB1,0x8F,0x3A,0x21,0x10,0x00,0x80,0xBF,0x3A,0x23, +0x10,0x20,0xC9,0x03,0x80,0x05,0xD5,0x12,0x5A,0x18,0x01,0x04,0x80,0x0A,0xD5,0x0E, +0x5A,0x10,0x02,0x0D,0x5A,0x18,0x03,0x04,0x80,0x09,0xD5,0x08,0x5A,0x18,0x04,0x04, +0x80,0x07,0xD5,0x04,0x5A,0x18,0x05,0x06,0x80,0x06,0x49,0xFF,0x99,0x2A,0xFC,0xC9, +0x5A,0x10,0x06,0xEA,0xFC,0xC9,0xFC,0x03,0x84,0x20,0x84,0x4C,0x80,0xC0,0x80,0x1F, +0x49,0x00,0x0F,0xCE,0x44,0x10,0xF6,0x90,0xB0,0x03,0x3A,0x20,0x90,0x00,0x3A,0x20, +0x10,0x20,0xCE,0x03,0x80,0x1F,0xD5,0x03,0x5A,0x68,0x01,0x04,0x49,0xFF,0x99,0x25, +0xFC,0x83,0xFC,0x00,0x84,0x00,0x49,0xFF,0xFF,0xA0,0x84,0x00,0x49,0xFF,0xFF,0xE5, +0x84,0x00,0x49,0xFF,0x9C,0x0B,0x84,0x00,0x49,0xFF,0x9C,0x3A,0x49,0xFF,0xFE,0x2B, +0x49,0xFF,0xFE,0x79,0x49,0xFF,0xFE,0xA1,0x84,0x00,0x49,0xFF,0xA2,0x25,0x44,0x00, +0x02,0x40,0x49,0xFF,0xA2,0x37,0x44,0x00,0x02,0x40,0x49,0xFF,0xA2,0x28,0x84,0x09, +0x49,0xFF,0xA2,0x3B,0x84,0x03,0x49,0xFF,0xA2,0x41,0x44,0x62,0x16,0x98,0x44,0x10, +0x00,0xF0,0x84,0x00,0x49,0xFF,0xA0,0xC9,0x49,0xFF,0xEA,0x18,0x38,0x13,0x01,0x01, +0x84,0x00,0x49,0xFF,0x9F,0x6F,0x44,0x10,0x00,0xF0,0x84,0x02,0x49,0xFF,0xA0,0xBD, +0x49,0xFF,0xEA,0x0C,0x38,0x13,0x01,0x01,0x84,0x02,0x49,0xFF,0x9F,0x63,0x44,0x10, +0x00,0xF0,0x84,0x05,0x49,0xFF,0xA0,0xB1,0x49,0xFF,0xEA,0x00,0x38,0x13,0x01,0x01, +0x46,0x60,0x00,0x81,0x84,0x03,0x49,0xFF,0x9F,0x55,0x49,0xFF,0x9C,0xEF,0x04,0x03, +0x03,0x91,0x92,0x0A,0x40,0x00,0x28,0x08,0x58,0x00,0x01,0xC0,0x14,0x03,0x03,0x91, +0x49,0xFF,0x9C,0xDD,0x04,0x03,0x00,0x2C,0x46,0x1F,0xFC,0x0F,0x92,0x06,0x94,0x06, +0x58,0x00,0x00,0x2D,0x14,0x03,0x00,0x2C,0x04,0x03,0x00,0x2C,0x50,0x10,0x8F,0xFF, +0x46,0x20,0x02,0xD0,0xFE,0x0E,0xFE,0x17,0x14,0x03,0x00,0x2C,0x04,0x03,0x00,0x2D, +0x92,0x06,0x94,0x06,0x58,0x00,0x00,0x0A,0x14,0x03,0x00,0x2D,0x04,0x03,0x00,0x2D, +0xFE,0x46,0x46,0x00,0x00,0xA0,0xFE,0x47,0x14,0x13,0x00,0x2D,0x04,0x13,0x00,0x2D, +0x46,0x0C,0x0F,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x0E,0x46,0x10,0xA0,0x00,0xFE,0x0F, +0x14,0x03,0x00,0x2D,0x04,0x03,0x00,0x2F,0x92,0x06,0x94,0x06,0x58,0x00,0x00,0x04, +0x14,0x03,0x00,0x2F,0x44,0x00,0x02,0x80,0x49,0xFF,0xA0,0x4E,0x84,0x00,0x49,0xFF, +0xFE,0xDC,0x49,0xFF,0xFC,0x66,0x49,0xFF,0xA0,0x3D,0x3E,0x07,0xF9,0x18,0xFC,0x80, +0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xE0,0x46,0x80,0xC0,0xC8,0x18,0x2E,0x07,0xF1,0x77, +0xC8,0x11,0x44,0x04,0x50,0x28,0xB4,0x20,0x92,0x23,0x96,0x46,0x5A,0x10,0x01,0x0B, +0xB4,0x20,0x92,0x21,0x96,0x46,0xC1,0x06,0x3C,0x0D,0xFE,0x44,0x5A,0x08,0x01,0x0A, +0xD5,0x05,0x49,0xFF,0xD9,0x85,0x5A,0x08,0x02,0xF9,0x49,0xFF,0xE0,0x2E,0x84,0xC1, +0x80,0x06,0xFC,0x80,0x2E,0x07,0xF1,0x86,0xC8,0x1F,0xFC,0x00,0x2E,0x07,0xF1,0x77, +0xC8,0x12,0x44,0x14,0x50,0x28,0xB4,0x01,0x92,0x03,0x96,0x06,0x5A,0x00,0x01,0x0C, +0xB4,0x01,0x92,0x01,0x96,0x06,0xC0,0x07,0x3C,0x0D,0xFE,0x44,0x5A,0x00,0x01,0x08, +0x84,0x00,0xFC,0x80,0x49,0xFF,0xD9,0x64,0x5A,0x08,0x02,0xF8,0x84,0x00,0x3E,0x07, +0xF1,0x86,0x84,0x01,0xFC,0x80,0x84,0x00,0x3E,0x07,0xF1,0x86,0x84,0x01,0xDD,0x9E, +0xFC,0x00,0x49,0xFF,0x9F,0xEF,0xFC,0x80,0x2E,0x07,0xF9,0x18,0xDD,0x9E,0x3E,0x07, +0xF9,0x18,0xDD,0x9E,0x2E,0x07,0xF9,0x17,0xDD,0x9E,0x2E,0x07,0xF9,0x16,0xDD,0x9E, +0x2E,0x07,0xF9,0x15,0xDD,0x9E,0x3E,0x07,0xF9,0x15,0xDD,0x9E,0xFC,0x00,0x80,0xC0, +0x49,0xFF,0xA0,0xE1,0x80,0x06,0x49,0xFF,0x97,0x60,0x84,0x00,0x84,0x2C,0x5A,0x68, +0x01,0x1F,0x49,0xFF,0xA0,0x3E,0x84,0x02,0x84,0x2C,0x49,0xFF,0xA0,0x3A,0x84,0x03, +0x84,0x2A,0x49,0xFF,0xA0,0x36,0x84,0x05,0x84,0x2C,0x49,0xFF,0xA0,0x32,0x84,0x07, +0x84,0x2C,0x49,0xFF,0xA0,0x2E,0x84,0x00,0x80,0x20,0x49,0xFF,0x96,0xE5,0x84,0x00, +0x84,0x22,0x49,0xFF,0x96,0xE1,0x44,0x02,0x2F,0x70,0xD5,0x41,0x49,0xFF,0xA0,0x21, +0x84,0x01,0x84,0x2C,0x49,0xFF,0xA0,0x1D,0x84,0x02,0x84,0x2C,0x49,0xFF,0xA0,0x19, +0x84,0x03,0x84,0x2A,0x49,0xFF,0xA0,0x15,0x84,0x04,0x84,0x2C,0x49,0xFF,0xA0,0x11, +0x84,0x05,0x84,0x2C,0x49,0xFF,0xA0,0x0D,0x84,0x06,0x84,0x2C,0x49,0xFF,0xA0,0x09, +0x84,0x07,0x84,0x2C,0x49,0xFF,0xA0,0x05,0x84,0x01,0x84,0x20,0x49,0xFF,0x96,0xBC, +0x84,0x01,0x80,0x20,0x49,0xFF,0x96,0xB8,0x84,0x01,0x84,0x22,0x49,0xFF,0x96,0xB4, +0x84,0x00,0x84,0x23,0x49,0xFF,0x96,0xB0,0x84,0x01,0x84,0x24,0x49,0xFF,0x96,0xAC, +0x84,0x01,0x84,0x25,0x49,0xFF,0x96,0xA8,0x84,0x01,0x84,0x26,0x49,0xFF,0x96,0xA4, +0x84,0x01,0x84,0x27,0x49,0xFF,0x96,0xA0,0x44,0x02,0x2F,0xF4,0x44,0x10,0x00,0x42, +0x49,0xFF,0x9C,0x9F,0xFC,0x80,0x44,0x02,0x2F,0x70,0xDD,0x9E,0x44,0x02,0x2F,0xF4, +0xDD,0x9E,0xFC,0x21,0x84,0xC2,0xF0,0x81,0x80,0xE2,0x80,0x01,0x5A,0x10,0x01,0x03, +0x84,0xC1,0x49,0xFF,0xFF,0x85,0xCF,0x0C,0x84,0x03,0x49,0xFF,0x99,0x3F,0x84,0x03, +0x49,0xFF,0x9C,0xCB,0xF0,0x01,0x80,0x26,0x49,0xFF,0xE7,0x0D,0xFC,0xA1,0x84,0x02, +0x49,0xFF,0x99,0x34,0x84,0x02,0x49,0xFF,0x9C,0xC0,0xF0,0x01,0x80,0x26,0x84,0x40, +0x49,0xFF,0xE6,0x90,0xFC,0xA1,0xFC,0x00,0x84,0x00,0x49,0xFF,0xFD,0x3F,0x84,0x00, +0x84,0x20,0x3E,0x07,0xF9,0x17,0x3E,0x07,0xF9,0x16,0x80,0x41,0x2E,0x07,0xF9,0x18, +0x49,0xFF,0xFF,0xD1,0xFC,0x80,0xFC,0x20,0x44,0x14,0x80,0x90,0x44,0x0F,0xFF,0xEF, +0xB4,0x41,0x46,0x70,0x00,0x81,0xFE,0x16,0xB6,0x01,0x44,0x04,0x80,0x10,0xFA,0x20, +0xAE,0x40,0xFA,0xC2,0x50,0x73,0x83,0xD4,0xB4,0x07,0x92,0x0B,0x96,0x27,0xC0,0x07, +0x8E,0xC1,0xC6,0x05,0x84,0x01,0x49,0xFF,0xA4,0x5E,0xD5,0xF7,0x44,0x04,0x80,0x10, +0xFA,0x20,0xAE,0x40,0x44,0x14,0x80,0x90,0xB4,0x01,0x58,0x00,0x00,0x10,0xB6,0x01, +0xFC,0xA0,0xFC,0x20,0x49,0xFF,0xEF,0xD4,0xC8,0x47,0x2E,0x07,0xF2,0xE4,0x5A,0x00, +0x01,0x44,0x44,0x64,0x80,0x10,0x44,0x00,0x00,0x40,0xAE,0x30,0x44,0x74,0x50,0x28, +0xB4,0x06,0x92,0x06,0x96,0x06,0x5A,0x00,0x01,0x07,0x44,0x74,0x80,0x10,0x44,0x64, +0x50,0x28,0xD5,0x25,0x2E,0x07,0xF1,0x77,0xC8,0x0E,0xB4,0x07,0x92,0x03,0x96,0x06, +0xC8,0x0A,0xB4,0x07,0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFE,0x44,0x5A,0x08, +0x01,0xE9,0xD5,0xEC,0x49,0xFF,0xD8,0x4C,0x5A,0x08,0x02,0xF9,0xD5,0xE7,0x2E,0x07, +0xF1,0x77,0xC8,0x15,0xB4,0x06,0x92,0x03,0x96,0x06,0xC8,0x11,0xB4,0x06,0x92,0x01, +0x96,0x06,0xC0,0x0D,0x3C,0x0D,0xFE,0x44,0x5A,0x00,0x01,0x06,0xB4,0x07,0x92,0x06, +0x96,0x06,0xC0,0xEE,0x84,0x01,0x49,0xFF,0xA4,0x05,0xFC,0xA0,0x49,0xFF,0xD8,0x30, +0x5A,0x08,0x02,0xF2,0xD5,0xF8,0x49,0xFF,0xEF,0x8B,0x5A,0x08,0x01,0x09,0x44,0x14, +0x50,0x28,0xB4,0x01,0x92,0x01,0x96,0x06,0x5A,0x00,0x01,0xFD,0xFC,0xA0,0xFC,0x01, +0x5A,0x08,0x01,0x06,0xF0,0x81,0x49,0xFF,0xFF,0xA6,0xF0,0x01,0x49,0xFF,0x97,0x6D, +0xFC,0x81,0xFC,0x20,0x84,0xE0,0x80,0xC0,0x84,0x00,0x49,0xFF,0x97,0x66,0x49,0xFF, +0xFF,0x74,0x49,0xFF,0xE1,0x86,0x3E,0x77,0xF1,0x87,0x49,0xFF,0xDE,0xB6,0x3E,0x77, +0xF1,0x86,0x5A,0x68,0x01,0x05,0x80,0x06,0x49,0xFF,0xFF,0xE3,0xFC,0xA0,0xFC,0x00, +0x3C,0x6D,0xFE,0x43,0xC6,0x0A,0x84,0x01,0x49,0xFF,0xFF,0xE5,0x49,0xFF,0xFD,0x49, +0x84,0x00,0x3C,0x0F,0xFE,0x43,0xFC,0x80,0x49,0xFF,0xEF,0x52,0x5A,0x00,0x01,0xF5, +0x3E,0x67,0xF1,0x87,0x49,0xFF,0xDE,0x99,0x3E,0x67,0xF1,0x86,0x49,0xFF,0xFD,0x39, +0xFC,0x80,0xFC,0x00,0x49,0xFF,0xFD,0x2B,0x46,0x00,0x00,0x81,0x04,0x00,0x00,0xF5, +0x92,0x0B,0x96,0x27,0xC8,0x04,0x49,0xFF,0xEF,0x3B,0xC0,0x05,0x84,0x00,0x49,0xFF, +0xFF,0xC2,0x84,0x01,0x3C,0x0F,0xFE,0x43,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xFF,0xD1, +0x44,0x64,0x50,0x28,0x2E,0x07,0xF1,0x86,0xC0,0x0F,0x84,0xC0,0x3E,0x67,0xF1,0x87, +0x49,0xFF,0xDE,0x73,0x3E,0x67,0xF1,0x86,0x49,0xFF,0xFD,0x09,0x49,0xFF,0xFA,0x93, +0x49,0xFF,0xFF,0xD9,0xFC,0x80,0x2E,0x07,0xF1,0x77,0xC8,0x0F,0xB4,0x06,0x92,0x03, +0x96,0x06,0x5A,0x00,0x01,0x0B,0xB4,0x06,0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D, +0xFE,0x44,0x5A,0x08,0x01,0xE1,0xD5,0xE2,0x49,0xFF,0xD7,0xAA,0x5A,0x08,0x02,0xF9, +0xD5,0xDD,0xFC,0x49,0x2E,0x67,0xF9,0x18,0x84,0x00,0x49,0xFF,0xFE,0x61,0x84,0x02, +0x49,0xFF,0x98,0x1C,0x80,0x06,0x84,0x20,0x84,0x41,0x49,0xFF,0xE5,0x7B,0x84,0x04, +0x49,0xFF,0xFC,0xFB,0xB1,0x86,0x44,0x10,0xF6,0x9C,0x80,0x06,0x3A,0x20,0x94,0x04, +0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x00, +0x3A,0x20,0x14,0x20,0x44,0x00,0xF6,0xCC,0x80,0x3F,0x3A,0x20,0x14,0x04,0x3A,0x20, +0x94,0x24,0x3A,0x20,0x0C,0x00,0x3A,0x20,0x8C,0x20,0x80,0x1F,0x49,0xFF,0x9C,0x34, +0x80,0x06,0x49,0xFF,0x9B,0xFB,0x84,0x00,0x3E,0x07,0xF1,0x85,0x80,0xFF,0x49,0xFF, +0xFF,0x70,0x44,0x94,0x50,0x28,0x49,0xFF,0x95,0x89,0x2E,0x07,0xF1,0x85,0xC0,0x2C, +0x84,0x00,0x3E,0x07,0xF1,0x85,0x49,0xFF,0xFF,0x7E,0x49,0xFF,0xFF,0x90,0x84,0x00, +0x49,0xFF,0xFC,0xC3,0x44,0x10,0xF6,0x08,0x80,0x06,0x3A,0x20,0x94,0x04,0x3A,0x20, +0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x00,0x3A,0x20, +0x14,0x20,0x44,0x00,0xF5,0xB4,0x80,0x27,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24, +0x3A,0x20,0x0C,0x00,0x3A,0x20,0x8C,0x20,0x80,0x07,0x49,0xFF,0x9B,0xFD,0x80,0x06, +0x49,0xFF,0x9B,0xC4,0xFC,0xC9,0x2E,0x07,0xF1,0x77,0xC8,0x0F,0xB4,0x09,0x92,0x03, +0x96,0x06,0x5A,0x00,0x01,0x0B,0xB4,0x09,0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D, +0xFE,0x44,0x5A,0x08,0x01,0xC4,0xD5,0xC5,0x49,0xFF,0xD7,0x2A,0x5A,0x08,0x02,0xF9, +0xD5,0xC0,0xFC,0x41,0x49,0xFF,0xE7,0x2A,0xF0,0x81,0x49,0xFF,0xEE,0x81,0x80,0xC0, +0xC8,0x47,0x84,0x01,0x49,0xFF,0xFC,0x51,0x84,0x01,0x49,0xFF,0xFB,0x99,0x84,0x06, +0x49,0xFF,0xE7,0x1F,0x84,0x01,0x49,0xFF,0xE8,0xD4,0x2E,0x27,0xF9,0x16,0x2E,0x17, +0xF9,0x17,0x84,0x01,0x49,0xFF,0xFE,0x3F,0x84,0x01,0x49,0xFF,0xE7,0xF0,0x84,0x21, +0x84,0x02,0x49,0xFF,0xE9,0xBB,0x80,0x06,0x49,0xFF,0x99,0x0A,0x44,0x10,0x02,0x40, +0x80,0x06,0x49,0xFF,0x9E,0xEC,0x84,0x02,0x49,0xFF,0xFB,0xE6,0x80,0x06,0x49,0xFF, +0x9A,0xAC,0x84,0x01,0x49,0xFF,0xFE,0xDF,0x44,0x64,0x50,0x28,0x2E,0x07,0xF1,0x86, +0xC8,0x47,0x2E,0x07,0xF1,0x77,0xC8,0x0F,0xB4,0x06,0x92,0x03,0x96,0x06,0x5A,0x00, +0x01,0x0B,0xB4,0x06,0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFE,0x44,0x5A,0x08, +0x01,0xEF,0xD5,0x36,0x49,0xFF,0xD6,0xDC,0x5A,0x08,0x02,0xF9,0xD5,0x31,0x84,0xC2, +0x44,0x74,0x50,0x28,0x84,0x00,0x49,0xFF,0x9E,0xB7,0xF0,0x81,0x84,0x00,0x49,0xFF, +0x98,0xD7,0x84,0x00,0x44,0x10,0x02,0x40,0x49,0xFF,0x9E,0xB9,0x84,0x02,0x49,0xFF, +0xFB,0xB3,0x49,0xFF,0xFE,0xC6,0x2E,0x07,0xF1,0x86,0x4E,0x03,0x00,0xFD,0x2E,0x07, +0xF1,0x77,0xC8,0x10,0xB4,0x07,0x92,0x03,0x96,0x06,0x5A,0x00,0x01,0x0C,0xB4,0x07, +0x92,0x01,0x96,0x06,0xC0,0x07,0x3C,0x0D,0xFE,0x44,0x5A,0x08,0x01,0xEE,0x48,0x00, +0x00,0xEB,0x49,0xFF,0xD6,0xAD,0x5A,0x08,0x02,0xF8,0x48,0x00,0x00,0xE5,0x44,0x10, +0x02,0x40,0x84,0x40,0x44,0x02,0x26,0x70,0x49,0xFF,0xE6,0x22,0x49,0xFF,0xFE,0xBB, +0x84,0x00,0x49,0xFF,0xFB,0xD2,0x49,0xFF,0xF9,0x5C,0xF0,0x01,0x49,0xFF,0xE6,0xA1, +0x84,0x00,0x49,0xFF,0xE8,0x56,0x2E,0x27,0xF9,0x16,0x2E,0x17,0xF9,0x17,0x84,0x00, +0x49,0xFF,0xFD,0xC1,0x84,0x00,0x49,0xFF,0xE7,0x72,0x84,0x20,0x84,0x02,0x49,0xFF, +0xE9,0x3D,0x84,0x00,0x49,0xFF,0x98,0x8C,0x84,0x00,0x44,0x10,0x02,0x40,0x49,0xFF, +0x9E,0x6E,0x49,0xFF,0xFE,0x7E,0x49,0xFF,0xDD,0x28,0x84,0x00,0x3E,0x07,0xF1,0x86, +0x44,0x64,0x50,0x28,0x49,0xFF,0xDD,0x1E,0xC8,0x16,0x2E,0x07,0xF1,0x77,0xC8,0x0F, +0xB4,0x06,0x92,0x03,0x96,0x06,0x5A,0x00,0x01,0x0B,0xB4,0x06,0x92,0x01,0x96,0x06, +0xC0,0x06,0x3C,0x0D,0xFE,0x44,0x5A,0x08,0x01,0xEF,0xD5,0x05,0x49,0xFF,0xD6,0x60, +0x5A,0x08,0x02,0xF9,0x44,0x64,0x50,0x28,0x2E,0x07,0xF1,0x86,0xC8,0x16,0x2E,0x07, +0xF1,0x77,0xC8,0x0F,0xB4,0x06,0x92,0x03,0x96,0x06,0x5A,0x00,0x01,0x0B,0xB4,0x06, +0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFE,0x44,0x5A,0x08,0x01,0xEF,0xD5,0x05, +0x49,0xFF,0xD6,0x46,0x5A,0x08,0x02,0xF9,0x44,0x64,0x50,0x28,0x2E,0x07,0xF9,0x14, +0xC8,0x10,0x49,0xFF,0xFE,0x58,0x84,0x01,0x49,0xFF,0x98,0x42,0x84,0x01,0x44,0x10, +0x02,0x40,0x49,0xFF,0x9E,0x24,0x44,0x64,0x50,0x28,0x49,0xFF,0xFE,0x32,0xD5,0x26, +0x2E,0x07,0xF1,0x77,0xC8,0x0E,0xB4,0x06,0x92,0x03,0x96,0x06,0xC8,0x0A,0xB4,0x06, +0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFE,0x44,0x5A,0x08,0x01,0xE1,0xD5,0xE2, +0x49,0xFF,0xD6,0x1E,0x5A,0x08,0x02,0xF9,0xD5,0xDD,0x2E,0x07,0xF1,0x77,0xC8,0x12, +0xB4,0x06,0x92,0x03,0x96,0x06,0x5A,0x00,0x01,0x0E,0xB4,0x06,0x92,0x01,0x96,0x06, +0xC0,0x09,0x3C,0x0D,0xFE,0x44,0x5A,0x00,0x01,0x0A,0x49,0xFF,0xDC,0xB3,0xC0,0xEE, +0xD5,0x05,0x49,0xFF,0xD6,0x05,0x5A,0x08,0x02,0xF6,0x44,0x64,0x50,0x28,0x2E,0x07, +0xF1,0x86,0xC8,0x16,0x2E,0x07,0xF1,0x77,0xC8,0x0F,0xB4,0x06,0x92,0x03,0x96,0x06, +0x5A,0x00,0x01,0x0B,0xB4,0x06,0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFE,0x44, +0x5A,0x08,0x01,0xEF,0xD5,0x05,0x49,0xFF,0xD5,0xEB,0x5A,0x08,0x02,0xF9,0x44,0x64, +0x50,0x28,0x2E,0x07,0xF9,0x14,0xC0,0x0A,0x49,0xFF,0xFD,0xFD,0x84,0x01,0x49,0xFF, +0xFA,0xCB,0x84,0x01,0x49,0xFF,0x99,0x91,0xD5,0x2D,0x2E,0x07,0xF1,0x77,0xC8,0x0E, +0xB4,0x06,0x92,0x03,0x96,0x06,0xC8,0x0A,0xB4,0x06,0x92,0x01,0x96,0x06,0xC0,0x06, +0x3C,0x0D,0xFE,0x44,0x5A,0x08,0x01,0xE7,0xD5,0xE8,0x49,0xFF,0xD5,0xC9,0x5A,0x08, +0x02,0xF9,0xD5,0xE3,0x85,0x20,0x3E,0x97,0xF1,0x87,0x49,0xFF,0xDC,0x6E,0x3E,0x97, +0xF1,0x86,0x49,0xFF,0xFD,0xD8,0xF0,0x01,0x44,0x10,0x02,0x40,0x84,0x40,0x49,0xFF, +0xE5,0x37,0x84,0x01,0x49,0xFF,0xFA,0xA0,0x5A,0x60,0x01,0x05,0x84,0xC1,0x48,0xFF, +0xFE,0xDB,0xFC,0xC1,0xFC,0x00,0x2E,0x07,0xF9,0x18,0x49,0xFF,0xE7,0x6A,0x84,0x00, +0x49,0xFF,0xFA,0x3C,0x84,0x20,0x2E,0x27,0xF9,0x16,0x2E,0x07,0xF9,0x18,0x49,0xFF, +0xFC,0xD2,0x84,0x00,0x49,0xFF,0xFE,0x77,0x2E,0x17,0xF9,0x17,0x2E,0x27,0xF9,0x16, +0x2E,0x07,0xF9,0x18,0x49,0xFF,0xFC,0xC7,0x2E,0x07,0xF9,0x18,0x49,0xFF,0xE7,0x51, +0x2E,0x07,0xF9,0x18,0x49,0xFF,0xE8,0xB8,0x49,0xFF,0xEC,0xEA,0xC8,0x09,0x2E,0x07, +0xF9,0x18,0x49,0xFF,0xFA,0x05,0x2E,0x07,0xF9,0x18,0x49,0xFF,0xFA,0xB6,0x84,0x01, +0x49,0xFF,0xFA,0x6A,0x49,0xFF,0xE6,0x5F,0x49,0xFF,0xFD,0x7B,0xFC,0x80,0xFC,0x21, +0x84,0x00,0x49,0xFF,0xFD,0x60,0x49,0xFF,0xDC,0x13,0xF0,0x81,0x84,0x00,0x49,0xFF, +0x97,0x77,0x49,0xFF,0xEC,0xCD,0xC8,0x31,0x49,0xFF,0x95,0xE1,0xC8,0x2E,0x84,0x01, +0x3E,0x07,0xF2,0xE5,0xFA,0x2E,0x84,0x0B,0x49,0x00,0x05,0x8A,0x84,0x0B,0x84,0x21, +0x49,0x00,0x05,0x94,0x84,0x01,0x49,0xFF,0x95,0xC8,0x49,0xFF,0x95,0xD0,0x5A,0x00, +0x01,0x0C,0x84,0x0B,0x49,0x00,0x05,0xA5,0x5A,0x08,0x01,0xF9,0x84,0x00,0x3E,0x07, +0xF2,0xE5,0x3E,0x07,0xF2,0xE4,0x84,0x0B,0x84,0x20,0x49,0x00,0x05,0x7F,0x2E,0x67, +0xF2,0xE5,0x97,0xB0,0x5A,0x68,0x01,0x0A,0x84,0x00,0x49,0xFF,0x94,0xA1,0x80,0x06, +0x49,0xFF,0xF9,0xE0,0x3E,0x67,0xF2,0xE4,0x44,0x10,0x00,0xC0,0x84,0x00,0x49,0xFF, +0xDE,0xA2,0x84,0x05,0x49,0xFF,0xF9,0xCA,0x84,0x05,0x49,0xFF,0xF9,0xD3,0x84,0x05, +0x49,0xFF,0xFA,0x93,0x49,0xFF,0xF9,0x62,0x84,0x02,0x49,0xFF,0xFA,0x15,0x84,0x01, +0x49,0xFF,0xFD,0x11,0x84,0xCA,0x49,0xFF,0xFA,0x74,0x44,0x74,0x50,0x28,0x84,0x00, +0x3E,0x07,0xF1,0x87,0x2E,0x07,0xF1,0x87,0xC8,0x15,0x2E,0x07,0xF1,0x77,0xC8,0x0E, +0xB4,0x07,0x92,0x03,0x96,0x06,0xC8,0x0A,0xB4,0x07,0x92,0x01,0x96,0x06,0xC0,0x06, +0x3C,0x0D,0xFE,0x44,0x5A,0x08,0x01,0xF0,0xD5,0x05,0x49,0xFF,0xD5,0x09,0x5A,0x08, +0x02,0xF9,0x84,0x00,0x3E,0x07,0xF1,0x86,0x2E,0x07,0xF1,0x86,0xC8,0x15,0x2E,0x07, +0xF1,0x77,0xC8,0x0E,0xB4,0x07,0x92,0x03,0x96,0x06,0xC8,0x0A,0xB4,0x07,0x92,0x01, +0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFE,0x44,0x5A,0x08,0x01,0xF0,0xD5,0x05,0x49,0xFF, +0xD4,0xEF,0x5A,0x08,0x02,0xF9,0x8E,0xC1,0x97,0xB0,0xCE,0xCA,0x80,0x06,0x49,0xFF, +0xFC,0xD2,0x49,0xFF,0xF9,0x30,0x80,0x06,0x49,0xFF,0xFA,0x47,0x80,0x06,0x49,0xFF, +0xF9,0x81,0x84,0x03,0x49,0xFF,0xF9,0xC8,0xF0,0x01,0x49,0xFF,0x96,0xE1,0xFC,0xA1, +0xFC,0x00,0x49,0xFF,0xFF,0x5E,0x44,0x02,0x1D,0xE8,0x44,0x10,0x00,0xC0,0x84,0x40, +0x49,0xFF,0xE4,0x2E,0xFC,0x80,0xFC,0x00,0x2E,0x07,0xF9,0x18,0x49,0xFF,0xE6,0x89, +0x2E,0x17,0xF9,0x18,0x84,0x03,0x49,0xFF,0xE7,0x79,0x84,0x03,0x49,0xFF,0xF9,0x56, +0x2E,0x07,0xF9,0x18,0x84,0x20,0x2E,0x27,0xF9,0x16,0x49,0xFF,0xFB,0xEC,0x49,0xFF, +0xEC,0x17,0xC8,0x04,0x84,0x01,0x49,0xFF,0xFC,0x9E,0x84,0x01,0x49,0xFF,0xF9,0x9C, +0x44,0x64,0x50,0x28,0x49,0xFF,0xFC,0xAD,0x2E,0x07,0xF1,0x86,0xC0,0x1D,0x84,0xC0, +0x3E,0x67,0xF1,0x87,0x49,0xFF,0xDB,0x51,0x3E,0x67,0xF1,0x86,0x49,0xFF,0xFC,0xBB, +0x44,0x10,0x00,0x42,0x84,0x40,0x84,0x63,0x44,0x02,0x2F,0xF4,0x49,0xFF,0xE4,0x29, +0x84,0x00,0x49,0xFF,0xF9,0x2B,0x2E,0x07,0xF9,0x18,0x49,0xFF,0xE7,0xBD,0x84,0x01, +0x49,0xFF,0xF9,0x7A,0xFC,0x80,0x2E,0x07,0xF1,0x77,0xC8,0x0F,0xB4,0x06,0x92,0x03, +0x96,0x06,0x5A,0x00,0x01,0x0B,0xB4,0x06,0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D, +0xFE,0x44,0x5A,0x08,0x01,0xD3,0xD5,0xD4,0x49,0xFF,0xD4,0x7A,0x5A,0x08,0x02,0xF9, +0xD5,0xCF,0xFC,0x00,0x49,0xFF,0x9C,0xB4,0xE6,0x03,0xE8,0x14,0x44,0x14,0x30,0x08, +0x84,0x1E,0xB4,0x41,0x44,0x64,0x30,0x0C,0xFE,0x16,0xB6,0x01,0xB4,0x06,0x58,0x00, +0x00,0x01,0xB6,0x06,0x84,0x01,0x49,0xFF,0xA0,0x3E,0xB4,0x06,0x58,0x00,0x00,0x02, +0xB6,0x06,0xFC,0x80,0xFC,0x00,0x44,0x64,0x55,0x08,0x44,0x0F,0xFF,0xBF,0xB4,0x26, +0xFE,0x0E,0xB6,0x06,0x84,0x21,0x80,0x06,0x49,0xFF,0x97,0x13,0x44,0x04,0x58,0x88, +0x84,0x37,0xB4,0x40,0xFE,0x56,0xB6,0x20,0x84,0x21,0x49,0xFF,0x96,0xDF,0xB4,0x26, +0x44,0x0F,0xFF,0x7F,0xFE,0x0E,0xB6,0x06,0xB4,0x26,0x44,0x0F,0xFF,0xEF,0xFE,0x0E, +0xB6,0x06,0x84,0x21,0x80,0x06,0x49,0xFF,0x96,0xFC,0x44,0x04,0x80,0x88,0x46,0x10, +0x10,0x00,0xB4,0x40,0xFE,0x57,0xB6,0x20,0xB4,0x40,0x46,0x10,0x20,0x00,0xFE,0x57, +0xB6,0x20,0x49,0xFF,0x9C,0x6D,0xE6,0x03,0xE8,0x1F,0x44,0x14,0x2C,0x0C,0x46,0x00, +0x01,0x00,0xB4,0x41,0xFE,0x17,0x84,0x41,0xB6,0x01,0x80,0x62,0x44,0x04,0x28,0x0C, +0x44,0x14,0x24,0x0C,0x49,0xFF,0x97,0x08,0x44,0x14,0x2C,0x18,0x46,0x00,0x04,0x00, +0xB4,0x41,0xFE,0x17,0x84,0x41,0xB6,0x01,0x80,0x62,0x44,0x04,0x28,0x18,0x44,0x14, +0x24,0x18,0x49,0xFF,0x96,0xF9,0x46,0x60,0x00,0x81,0x49,0xFF,0x97,0x07,0x04,0x13, +0x03,0xA0,0x84,0x10,0xFE,0x0E,0x14,0x03,0x03,0xA0,0x04,0x13,0x03,0x9F,0x46,0x0F, +0x0F,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x46,0x46,0x20,0x40,0x00,0xFE,0x57,0x14,0x13, +0x03,0x9F,0x04,0x13,0x03,0x9F,0x46,0x27,0x00,0x00,0x94,0x4C,0x92,0x24,0xFE,0x57, +0x14,0x13,0x03,0x9F,0x04,0x13,0x03,0x9E,0xFE,0x0E,0x46,0x10,0x70,0x00,0xFE,0x0F, +0x14,0x03,0x03,0x9E,0x49,0xFF,0x96,0xDB,0x46,0x00,0x03,0x33,0x50,0x00,0x03,0x33, +0x44,0x14,0x50,0x0C,0x14,0x03,0x00,0x34,0x14,0x03,0x00,0x36,0xB4,0x01,0x58,0x00, +0x00,0x01,0xB6,0x01,0x44,0x04,0x00,0x90,0x44,0x10,0x00,0x64,0xAC,0x40,0x44,0x14, +0x30,0x34,0x46,0x00,0x04,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0x44,0x07,0x10,0x18, +0x84,0x20,0xAE,0x41,0x44,0x16,0x20,0x04,0x46,0x00,0x10,0x00,0xB4,0x41,0xFE,0x17, +0xB6,0x01,0x44,0x14,0x2C,0x18,0x46,0x0B,0xFF,0xFF,0xB4,0x41,0x50,0x00,0x0F,0xFF, +0xFE,0x16,0xB6,0x01,0x44,0x14,0x2C,0x1C,0x44,0x0F,0xFF,0xEF,0xB4,0x41,0xFE,0x16, +0x84,0x41,0xB6,0x01,0x80,0x62,0x44,0x04,0x28,0x18,0x44,0x14,0x24,0x18,0x49,0xFF, +0x96,0x93,0x84,0x41,0x44,0x04,0x28,0x1C,0x44,0x14,0x24,0x1C,0x80,0x62,0x49,0xFF, +0x96,0x8B,0x49,0xFF,0x9B,0xDD,0xE6,0x03,0xE8,0x10,0x44,0x14,0x2C,0x04,0x46,0x00, +0x00,0x80,0xB4,0x41,0xFE,0x17,0x84,0x41,0xB6,0x01,0x80,0x62,0x44,0x04,0x28,0x04, +0x44,0x14,0x24,0x04,0x49,0xFF,0x96,0x78,0x49,0xFF,0x9B,0xCA,0xE6,0x03,0xE9,0x40, +0x84,0x08,0x49,0xFF,0xD8,0x6C,0x84,0x00,0x49,0xFF,0xD8,0x72,0x84,0x41,0x80,0x62, +0x44,0x04,0x28,0x14,0x44,0x14,0x24,0x14,0x49,0xFF,0x96,0x66,0x44,0x14,0x2C,0x0C, +0x46,0x0F,0xFE,0xFF,0xB4,0x41,0x50,0x00,0x0F,0xFF,0xFE,0x16,0x84,0x41,0xB6,0x01, +0x80,0x62,0x44,0x04,0x28,0x0C,0x44,0x14,0x24,0x0C,0x49,0xFF,0x96,0x55,0x44,0x04, +0x2C,0x04,0x46,0x10,0x08,0x00,0xB4,0x40,0xFE,0x57,0xB6,0x20,0xB4,0x40,0x46,0x1F, +0xFB,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x56,0xB6,0x20,0xB4,0x40,0x46,0x10,0x00,0x80, +0xFE,0x57,0xB6,0x20,0xB4,0x40,0x44,0x14,0x00,0x00,0xFE,0x57,0x84,0x41,0xB6,0x20, +0x80,0x62,0x44,0x04,0x28,0x04,0x44,0x14,0x24,0x04,0x49,0xFF,0x96,0x35,0xFC,0x80, +0xFC,0x00,0x84,0x01,0x49,0xFF,0x93,0x04,0x84,0x03,0x49,0xFF,0x93,0x24,0x84,0x00, +0x49,0xFF,0x93,0xAA,0x84,0x00,0x49,0xFF,0x93,0x2E,0x84,0x03,0x49,0xFF,0x93,0x4C, +0x84,0x00,0x49,0xFF,0x93,0x57,0x84,0x01,0x49,0xFF,0xD8,0xFC,0xFC,0x80,0xFC,0x00, +0x84,0x00,0x44,0x10,0x00,0xF0,0x49,0xFF,0x99,0xE0,0x84,0x02,0x44,0x10,0x00,0xF0, +0x49,0xFF,0x99,0xDB,0x84,0x01,0x44,0x10,0x00,0xF0,0x49,0xFF,0x99,0xD6,0x84,0x03, +0x44,0x10,0x00,0xF0,0x49,0xFF,0x99,0xD1,0x84,0x04,0x44,0x10,0x00,0xF0,0x49,0xFF, +0x99,0xCC,0x44,0x10,0x00,0xF0,0x84,0x05,0x49,0xFF,0x99,0xC7,0x84,0x01,0x49,0xFF, +0xD7,0xE6,0x84,0x03,0x84,0x2A,0x49,0xFF,0x9A,0x14,0x84,0x00,0x84,0x23,0x49,0xFF, +0x90,0xCB,0xFC,0x80,0xFC,0x01,0xF0,0x81,0x49,0xFF,0x9B,0x42,0xE6,0x03,0xE9,0x04, +0xF0,0x01,0x49,0xFF,0xDC,0xEA,0xFC,0x81,0xFC,0x00,0x84,0x01,0x49,0xFF,0x95,0x5F, +0x84,0x01,0x49,0xFF,0x91,0x07,0x84,0x01,0x49,0xFF,0x94,0x41,0x84,0x01,0x49,0xFF, +0xFF,0xEB,0x84,0x00,0x49,0xFF,0xF7,0x9C,0x84,0x00,0x49,0xFF,0xF7,0x7F,0x84,0x01, +0x49,0xFF,0x93,0x6F,0x84,0x01,0x49,0xFF,0x98,0x9E,0x84,0x01,0x49,0xFF,0x98,0xAB, +0x84,0x01,0x49,0xFF,0x93,0x66,0x84,0x01,0x49,0xFF,0x98,0xC3,0x84,0x00,0x80,0x20, +0x49,0xFF,0xE4,0x35,0x84,0x01,0x49,0xFF,0x94,0x63,0x84,0x01,0x49,0xFF,0x94,0x74, +0x84,0x01,0x49,0xFF,0x94,0x8A,0x49,0xFF,0x9B,0x0B,0xE6,0x03,0xE8,0x05,0x84,0x01, +0x84,0x2F,0x49,0xFF,0x9A,0x69,0x49,0xFF,0x9B,0x03,0xE6,0x03,0xE9,0x03,0x49,0xFF, +0x9B,0x03,0x49,0xFF,0x9A,0xFD,0xE6,0x03,0xE8,0x03,0x49,0xFF,0x9B,0x06,0x84,0x04, +0x49,0xFF,0x9B,0x17,0xFC,0x80,0xC2,0x65,0xFC,0x45,0x80,0xC0,0x84,0xE1,0x81,0x22, +0x81,0x41,0x49,0xFF,0xD8,0xA0,0xB6,0xDF,0x84,0xC0,0x80,0x27,0x44,0x24,0x10,0x10, +0x80,0x66,0x80,0x87,0x80,0xA6,0x80,0x07,0xF7,0x81,0xF6,0x82,0xF6,0x83,0xF7,0x84, +0xF6,0x85,0xF6,0x86,0xF6,0x87,0x14,0x9F,0x80,0x08,0x49,0xFF,0xEC,0x1C,0x80,0x06, +0x49,0xFF,0xD8,0x96,0x80,0x07,0x49,0xFF,0xD8,0xA1,0x80,0x06,0x49,0xFF,0xD8,0x80, +0x80,0x06,0x49,0xFF,0xD8,0xFA,0x49,0xFF,0xD8,0xC4,0x44,0x00,0x00,0x3B,0x49,0xFF, +0xD8,0xCE,0x80,0x0A,0x49,0xFF,0xD8,0xD9,0x80,0x07,0x49,0xFF,0xD8,0xE5,0x80,0x06, +0x49,0xFF,0xD8,0xC5,0x49,0xFF,0xD8,0xED,0x80,0x09,0x49,0xFF,0xD8,0xEF,0x80,0x06, +0x49,0xFF,0xD8,0xF0,0x80,0x07,0x49,0xFF,0xD8,0xED,0x40,0x00,0x00,0x09,0x44,0x04, +0x80,0x07,0x84,0x22,0xAE,0x40,0x80,0x07,0x49,0xFF,0xD8,0xED,0x44,0x00,0x00,0xFF, +0x49,0xFF,0xD8,0xED,0x44,0x64,0x80,0x04,0xA6,0x32,0x96,0x06,0xC0,0xFE,0x49,0xFF, +0xD8,0x92,0xC0,0xFB,0x44,0x04,0x80,0x07,0x84,0x22,0xAE,0x40,0x84,0x00,0x49,0xFF, +0xD8,0x47,0x84,0x01,0x49,0xFF,0xD8,0xC1,0x84,0x00,0x49,0xFF,0xD8,0xB5,0xFC,0xC5, +0xDD,0x9E,0xFC,0x00,0x49,0xFF,0x9A,0x8C,0xFC,0x80,0xFC,0x00,0x44,0x37,0x20,0x64, +0x84,0x30,0xB4,0x03,0xFE,0x0E,0x58,0x00,0x00,0x05,0xB6,0x03,0xB4,0x43,0x44,0x0F, +0xF0,0xFF,0xFE,0x86,0x58,0x21,0x05,0x00,0xB6,0x43,0x44,0x27,0x20,0x6C,0xB4,0x62, +0xFE,0x5E,0x58,0x10,0x80,0x05,0xB6,0x22,0xB4,0x22,0xFE,0x0E,0x58,0x00,0x05,0x00, +0xB6,0x02,0x44,0x00,0x00,0x3F,0x49,0xFF,0x94,0x01,0xFC,0x80,0xFC,0x00,0x49,0xFF, +0x9A,0xAA,0x49,0xFF,0x9B,0x10,0x49,0xFF,0x9B,0xB5,0x84,0x01,0x49,0xFF,0x9A,0x94, +0xFC,0x80,0xFC,0x01,0xF1,0x81,0x49,0xFF,0x9B,0x5D,0xF0,0x01,0x49,0xFF,0xDC,0x53, +0xFC,0x81,0xFC,0x00,0x49,0xFF,0xE9,0x74,0x5A,0x08,0x01,0x04,0x84,0x00,0xD5,0x02, +0x84,0x03,0x49,0xFF,0x9B,0x58,0xFC,0x80,0xFC,0x00,0x49,0xFF,0x9B,0x4B,0xFC,0x80, +0x44,0x04,0x40,0x0C,0xB4,0x00,0x92,0x01,0x96,0x06,0x5A,0x00,0x01,0x09,0x44,0x07, +0x10,0x14,0xB4,0x00,0x92,0x0E,0x56,0x00,0x00,0x01,0x96,0x06,0xDD,0x9E,0xFC,0x00, +0xC0,0x04,0x49,0xFF,0xFF,0xAC,0xFC,0x80,0x44,0x17,0x20,0x64,0x84,0x70,0xB4,0x41, +0xFE,0x9E,0xB6,0x41,0xB4,0x81,0x44,0x2F,0xF0,0xFF,0xFF,0x16,0xB6,0x81,0x44,0x17, +0x20,0x6C,0xB4,0x81,0xFE,0xE6,0xB6,0x61,0xB4,0x61,0xFE,0x9E,0xB6,0x41,0x49,0xFF, +0x93,0xB5,0xFC,0x80,0xFC,0x42,0x49,0xFF,0x91,0x59,0xB6,0x1F,0x49,0xFF,0xD8,0x78, +0xF0,0x81,0x84,0x00,0x49,0xFF,0xF9,0xBF,0x84,0x00,0x49,0xFF,0x93,0xD9,0x84,0x00, +0x49,0xFF,0xD8,0x5F,0x84,0x00,0x49,0xFF,0x93,0xE7,0x84,0x01,0x49,0xFF,0x93,0xED, +0x49,0xFF,0xFF,0x79,0xE6,0x03,0xE8,0x03,0x49,0xFF,0xFF,0x79,0x84,0x05,0x49,0xFF, +0xF6,0x61,0x84,0x05,0x49,0xFF,0xF6,0x52,0x84,0x05,0x49,0xFF,0xF7,0x1E,0x49,0xFF, +0xF5,0xED,0x84,0x01,0x49,0xFF,0xF9,0x9F,0x84,0xC2,0x49,0xFF,0xF7,0x02,0x44,0x74, +0x50,0x28,0x84,0x00,0x3E,0x07,0xF1,0x87,0x2E,0x07,0xF1,0x87,0xC8,0x15,0x2E,0x07, +0xF1,0x77,0xC8,0x0E,0xB4,0x07,0x92,0x03,0x96,0x06,0xC8,0x0A,0xB4,0x07,0x92,0x01, +0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFE,0x44,0x5A,0x08,0x01,0xF0,0xD5,0x05,0x49,0xFF, +0xD1,0x97,0x5A,0x08,0x02,0xF9,0x84,0x00,0x3E,0x07,0xF1,0x86,0x2E,0x07,0xF1,0x86, +0xC8,0x15,0x2E,0x07,0xF1,0x77,0xC8,0x0E,0xB4,0x07,0x92,0x03,0x96,0x06,0xC8,0x0A, +0xB4,0x07,0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFE,0x44,0x5A,0x08,0x01,0xF0, +0xD5,0x05,0x49,0xFF,0xD1,0x7D,0x5A,0x08,0x02,0xF9,0x84,0x00,0x3E,0x07,0xF1,0x86, +0x5A,0x60,0x01,0x04,0x84,0xC1,0xD5,0xC6,0x49,0xFF,0xF6,0xB9,0x84,0x00,0x49,0xFF, +0xF9,0x5A,0x49,0xFF,0xFF,0x20,0xE6,0x03,0xE9,0x04,0x84,0x00,0x49,0xFF,0x90,0xD6, +0x84,0x00,0x49,0xFF,0x92,0xB4,0x84,0x00,0x80,0x20,0x49,0xFF,0x9C,0x25,0x49,0xFF, +0x9C,0x95,0xF0,0x82,0x84,0x00,0x49,0xFF,0x9C,0x88,0x84,0x00,0x49,0xFF,0x9A,0xCD, +0x2E,0x07,0xF1,0x77,0xC8,0x10,0x44,0x14,0x50,0x28,0xB4,0x01,0x92,0x03,0x96,0x06, +0xC8,0x0A,0xB4,0x01,0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFE,0x44,0x5A,0x08, +0x01,0x0B,0xD5,0x05,0x49,0xFF,0xD1,0x44,0x5A,0x08,0x02,0xF9,0x84,0xC0,0x80,0xE6, +0x81,0x26,0xD5,0x26,0x49,0xFF,0xFE,0xEF,0xE6,0x03,0xE8,0x0D,0x46,0x00,0x00,0x91, +0x04,0x70,0x00,0x11,0x44,0x07,0x20,0xC0,0xB4,0xC0,0x80,0x07,0x80,0x26,0x49,0xFF, +0xDB,0x6B,0xD5,0x05,0x84,0xC0,0x49,0xFF,0x9B,0x53,0x80,0xE6,0x49,0xFF,0xE8,0x88, +0x81,0x20,0x5A,0x08,0x01,0x0A,0x44,0x00,0x02,0x58,0x80,0x29,0x49,0xFF,0x9B,0xFE, +0x80,0x09,0x49,0xFF,0x9C,0x05,0x84,0x01,0x49,0xFF,0x95,0x54,0x85,0x21,0x84,0x00, +0x49,0xFF,0xF5,0xAC,0x84,0x00,0x49,0xFF,0xF6,0x78,0x49,0xFF,0xF5,0x5C,0x49,0xFF, +0xF6,0x60,0x84,0x01,0x49,0xFF,0xF8,0xED,0x49,0xFF,0xE8,0x73,0xF0,0x83,0xF1,0x03, +0x84,0x01,0x49,0x00,0x01,0x12,0x5A,0x98,0x01,0x0B,0x49,0xFF,0x93,0x83,0x5A,0x00, +0x01,0xFE,0x64,0x00,0x00,0x20,0x84,0x01,0x3E,0x07,0xF1,0x84,0x84,0x00,0x49,0xFF, +0xF8,0xD8,0x49,0xFF,0xF6,0x3C,0x2E,0x07,0xF1,0xAD,0xC8,0x05,0x84,0x00,0x49,0xFF, +0xF8,0xDA,0xD5,0x05,0x49,0xFF,0xE8,0x4C,0x5A,0x00,0x01,0xFA,0x49,0xFF,0xFE,0x9B, +0xE6,0x03,0xE8,0x06,0x80,0x07,0x80,0x26,0x49,0xFF,0xDB,0x1E,0xD5,0x06,0x84,0x00, +0x49,0xFF,0x95,0x18,0x49,0xFF,0x9B,0x04,0x44,0x10,0x00,0xEB,0x84,0x01,0x49,0x00, +0x00,0xE4,0xB4,0x1F,0x49,0xFF,0x90,0x42,0xF0,0x02,0x49,0xFF,0x9B,0xFE,0x49,0xFF, +0xFE,0x82,0xE6,0x03,0xE8,0x04,0x84,0x00,0x49,0xFF,0xFE,0xD3,0x84,0x00,0x49,0xFF, +0x92,0xEC,0x84,0x20,0x84,0x01,0x49,0xFF,0x9B,0x87,0x84,0x00,0x49,0xFF,0xF6,0x25, +0x84,0x00,0x49,0xFF,0xF5,0x5F,0x84,0x01,0x49,0xFF,0x92,0x09,0x84,0x01,0x49,0xFF, +0x92,0xD3,0x2E,0x67,0xF1,0xAD,0x97,0xB0,0xC6,0x07,0x84,0x00,0x84,0xC1,0x3E,0x07, +0xF1,0xAD,0x3C,0x6E,0x05,0x45,0x2E,0x07,0xF1,0xAC,0xC0,0x08,0x84,0x00,0x3E,0x07, +0xF1,0xAC,0x49,0xFF,0xF5,0xD4,0x49,0xFF,0xDC,0xF9,0x49,0xFF,0xE1,0x84,0x2E,0x07, +0xF2,0xE5,0x5A,0x08,0x01,0x11,0x49,0xFF,0xE7,0xFB,0xC8,0x0D,0x49,0xFF,0x91,0x05, +0x49,0xFF,0x91,0x0D,0x80,0xE0,0xC8,0xFD,0x84,0x01,0x49,0xFF,0x8F,0xF1,0x80,0x07, +0x49,0xFF,0xF5,0x30,0x84,0x00,0x3E,0x07,0xF2,0xE4,0x84,0x00,0x49,0xFF,0xF5,0x1E, +0xF0,0x01,0x49,0xFF,0x92,0x8D,0x84,0x01,0x49,0xFF,0xF8,0x63,0x49,0xFF,0xF5,0xD1, +0xCE,0x05,0x84,0x01,0x49,0xFF,0xF8,0x67,0xD5,0x05,0x49,0xFF,0xE7,0xD9,0x5A,0x00, +0x01,0xFA,0x49,0xFF,0xE7,0xD5,0x5A,0x08,0x01,0x23,0x44,0x74,0x50,0x28,0x2E,0x07, +0xF1,0x86,0xC8,0x16,0x2E,0x07,0xF1,0x77,0xC8,0x0F,0xB4,0x07,0x92,0x03,0x96,0x06, +0x5A,0x00,0x01,0x0B,0xB4,0x07,0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFE,0x44, +0x5A,0x08,0x01,0xEF,0xD5,0x05,0x49,0xFF,0xD0,0x5B,0x5A,0x08,0x02,0xF9,0x49,0xFF, +0xD7,0x04,0x84,0x00,0x3E,0x07,0xF1,0x86,0x3E,0x07,0xF1,0x87,0x80,0x06,0xFC,0xC2, +0xFC,0x00,0x44,0x00,0xE6,0x72,0x49,0xFF,0xDB,0xB0,0x84,0x01,0x84,0x20,0x49,0xFF, +0x9A,0xED,0x84,0x01,0x80,0x20,0x49,0xFF,0x9A,0xE9,0x84,0x00,0x80,0x20,0x49,0xFF, +0x9A,0xF9,0x84,0x20,0xFA,0x0E,0x49,0xFF,0x9B,0x19,0x84,0x00,0x49,0xFF,0x9B,0x20, +0x84,0x00,0x80,0x20,0x49,0xFF,0x9B,0x43,0x84,0x00,0x80,0x20,0x49,0xFF,0x94,0x76, +0x84,0x00,0x49,0xFF,0x9B,0x46,0x84,0x20,0x84,0x01,0x49,0xFF,0x9A,0xED,0x84,0x00, +0x49,0xFF,0x9B,0x20,0x84,0x00,0x84,0x21,0x49,0xFF,0x9A,0xDC,0x84,0x21,0x44,0x00, +0x00,0xD2,0x49,0xFF,0x9A,0xFB,0x84,0x01,0x49,0xFF,0x9B,0x02,0x84,0x00,0x84,0x21, +0x49,0xFF,0x9B,0x25,0x84,0x01,0x80,0x20,0x49,0xFF,0x94,0x58,0x49,0x00,0x00,0x34, +0xFC,0x80,0xFC,0x00,0x84,0x01,0x49,0xFF,0xDB,0xAA,0x84,0x07,0x49,0xFF,0xDB,0xAB, +0x84,0x00,0x49,0xFF,0xDB,0xB4,0x84,0x00,0x49,0xFF,0xDB,0xBD,0x44,0x00,0xFF,0x14, +0x49,0xFF,0x9B,0x42,0x49,0xFF,0x9B,0x50,0x49,0xFF,0x9B,0x47,0xFC,0x80,0xFC,0x00, +0x49,0xFF,0x9B,0x43,0xFC,0x80,0xFC,0x00,0x5A,0x08,0x01,0x0B,0xFE,0x0B,0x96,0x01, +0x49,0xFF,0x9B,0x32,0x49,0xFF,0x9B,0x40,0x49,0xFF,0x9B,0x37,0xFC,0x80,0x49,0xFF, +0x9B,0x4B,0xFC,0x80,0x3C,0x00,0x0A,0xAE,0x3C,0x10,0x0A,0xAE,0x96,0x01,0x4C,0x00, +0xFF,0xFB,0xDD,0x9E,0x84,0x00,0x3C,0x08,0x0A,0x8C,0xDD,0x9E,0x44,0x22,0x39,0x84, +0x38,0x11,0x01,0x09,0xDD,0x9E,0xFC,0x00,0x80,0xC0,0x49,0xFF,0xFF,0xED,0x44,0x12, +0x39,0xA4,0x38,0x00,0x99,0x09,0xFC,0x80,0x80,0x60,0x5A,0x18,0x01,0x10,0xFC,0x20, +0x80,0xC1,0x80,0xE0,0x49,0xFF,0xFF,0xF1,0x3C,0x20,0x0A,0x8C,0x40,0x13,0x1C,0x0C, +0xFE,0x57,0x96,0x49,0x3C,0x18,0x0A,0x8C,0xFC,0xA0,0x84,0x41,0x3C,0x00,0x0A,0x8C, +0x40,0x11,0x0C,0x0C,0xFE,0x4B,0xFE,0x46,0x3C,0x18,0x0A,0x8C,0xDD,0x9E,0x3C,0x10, +0x0A,0x8C,0x40,0x10,0x80,0x0E,0x96,0x46,0xC9,0x03,0x80,0x01,0xDD,0x9E,0xFC,0x00, +0x80,0xC0,0x49,0xFF,0xFF,0xC1,0x44,0x12,0x39,0xA4,0x44,0x22,0x39,0x84,0x38,0x10, +0x99,0x01,0x38,0x21,0x19,0x01,0x8A,0x01,0x96,0x01,0xE2,0x02,0x56,0x07,0x80,0x01, +0xFC,0x80,0x3C,0x00,0x0A,0xAE,0x8C,0x01,0x96,0x01,0x3C,0x08,0x0A,0xAE,0xDD,0x9E, +0x3A,0x1F,0xA4,0x3C,0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72, +0x04,0x02,0x3A,0x6F,0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03, +0x64,0x00,0x00,0x08,0x49,0x00,0x02,0x13,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03, +0x64,0x72,0x04,0x03,0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04, +0x3A,0x1F,0xA4,0x3C,0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72, +0x04,0x02,0x3A,0x6F,0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03, +0x64,0x00,0x00,0x08,0x49,0x00,0x01,0xF4,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03, +0x64,0x72,0x04,0x03,0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04, +0x3A,0x1F,0xA4,0x3C,0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72, +0x04,0x02,0x3A,0x6F,0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03, +0x64,0x00,0x00,0x08,0x49,0x00,0x01,0xD5,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03, +0x64,0x72,0x04,0x03,0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04, +0x3A,0x1F,0xA4,0x3C,0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72, +0x04,0x02,0x3A,0x6F,0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03, +0x64,0x00,0x00,0x08,0x49,0x00,0x01,0xB6,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03, +0x64,0x72,0x04,0x03,0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04, +0x3A,0x1F,0xA4,0x3C,0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72, +0x04,0x02,0x3A,0x6F,0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03, +0x64,0x00,0x00,0x08,0x49,0x00,0x01,0x97,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03, +0x64,0x72,0x04,0x03,0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04, +0x3A,0x1F,0xA4,0x3C,0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72, +0x04,0x02,0x3A,0x6F,0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03, +0x64,0x00,0x00,0x08,0x49,0x00,0x01,0x78,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03, +0x64,0x72,0x04,0x03,0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04, +0x3A,0x1F,0xA4,0x3C,0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72, +0x04,0x02,0x3A,0x6F,0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03, +0x64,0x00,0x00,0x08,0x49,0x00,0x01,0x59,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03, +0x64,0x72,0x04,0x03,0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04, +0x3A,0x1F,0xA4,0x3C,0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72, +0x04,0x02,0x3A,0x6F,0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03, +0x64,0x00,0x00,0x08,0x49,0x00,0x01,0x3A,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03, +0x64,0x72,0x04,0x03,0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04, +0x3A,0x1F,0x94,0x3C,0x3A,0xFF,0xBF,0xBC,0x64,0x12,0xA4,0x02,0x64,0x22,0x04,0x02, +0x3A,0x1F,0x88,0x3C,0x64,0x12,0x00,0x02,0x9E,0x4A,0x64,0x12,0x00,0x03,0x44,0x10, +0x00,0xA4,0x38,0x10,0x82,0x02,0xDD,0x21,0x3A,0x0F,0x84,0x04,0x64,0x02,0x00,0x43, +0x64,0x00,0x00,0x08,0x64,0x02,0xA4,0x03,0x64,0x12,0x04,0x03,0x3A,0xFF,0xBF,0x84, +0x3A,0x1F,0x94,0x04,0x3A,0x0F,0x80,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0x94,0x3C, +0x3A,0xFF,0xBF,0xBC,0x44,0x10,0x00,0xA4,0x38,0x10,0x82,0x02,0xDD,0x21,0x3A,0xFF, +0xBF,0x84,0x3A,0x1F,0x94,0x04,0x3A,0x0F,0x80,0x04,0x64,0x00,0x00,0x04,0xD5,0x00, +0x3A,0x0F,0x80,0x3C,0x84,0x00,0xD5,0xEB,0x3A,0x0F,0x80,0x3C,0x84,0x01,0xD5,0xE7, +0x3A,0x0F,0x80,0x3C,0x84,0x02,0xD5,0xE3,0x3A,0x0F,0x80,0x3C,0x84,0x03,0xD5,0xDF, +0x3A,0x0F,0x80,0x3C,0x84,0x04,0xD5,0xDB,0x3A,0x0F,0x80,0x3C,0x84,0x05,0xD5,0xD7, +0x3A,0x0F,0x80,0x3C,0x84,0x06,0xD5,0xD3,0x3A,0x0F,0x80,0x3C,0x84,0x07,0xD5,0xCF, +0x3A,0x0F,0x80,0x3C,0x84,0x08,0xD5,0xCB,0x3A,0x0F,0x80,0x3C,0x84,0x09,0xD5,0xC7, +0x3A,0x0F,0x80,0x3C,0x84,0x0A,0xD5,0xC3,0x3A,0x0F,0x80,0x3C,0x84,0x0B,0xD5,0xBF, +0x3A,0x0F,0x80,0x3C,0x84,0x0C,0xD5,0xBB,0x3A,0x0F,0x80,0x3C,0x84,0x0D,0xD5,0xB7, +0x3A,0x0F,0x80,0x3C,0x84,0x0E,0xD5,0xB3,0x3A,0x0F,0x80,0x3C,0x84,0x0F,0xD5,0xAF, +0x3A,0x0F,0x80,0x3C,0xFA,0x00,0xD5,0xAB,0x3A,0x0F,0x80,0x3C,0xFA,0x01,0xD5,0xA7, +0x3A,0x0F,0x80,0x3C,0xFA,0x02,0xD5,0xA3,0x3A,0x0F,0x80,0x3C,0xFA,0x03,0xD5,0x9F, +0x3A,0x0F,0x80,0x3C,0xFA,0x04,0xD5,0x9B,0x3A,0x0F,0x80,0x3C,0xFA,0x05,0xD5,0x97, +0x3A,0x0F,0x80,0x3C,0xFA,0x06,0xD5,0x93,0x3A,0x0F,0x80,0x3C,0xFA,0x07,0xD5,0x8F, +0x3A,0x0F,0x80,0x3C,0xFA,0x08,0xD5,0x8B,0x3A,0x0F,0x80,0x3C,0xFA,0x09,0xD5,0x87, +0x3A,0x0F,0x80,0x3C,0xFA,0x0A,0xD5,0x83,0x3A,0x0F,0x80,0x3C,0xFA,0x0B,0x48,0xFF, +0xFF,0x7F,0x3A,0x0F,0x80,0x3C,0xFA,0x0C,0x48,0xFF,0xFF,0x7A,0x3A,0x0F,0x80,0x3C, +0xFA,0x0D,0x48,0xFF,0xFF,0x75,0x3A,0x0F,0x80,0x3C,0xFA,0x0E,0x48,0xFF,0xFF,0x70, +0x3A,0x0F,0x80,0x3C,0x44,0x00,0x00,0x1F,0x48,0xFF,0xFF,0x6A,0x84,0x01,0x64,0x04, +0xC0,0x02,0x44,0x02,0x00,0x01,0x64,0x04,0xE0,0x03,0x45,0xD2,0x24,0x68,0x3F,0xC8, +0x00,0x00,0x45,0xF2,0x5F,0xF8,0x84,0x00,0x42,0x0E,0x00,0x21,0x49,0x00,0x00,0x42, +0x49,0x00,0x03,0x0C,0xD5,0x00,0x92,0x00,0xFC,0x00,0x44,0x32,0x11,0x00,0x44,0x22, +0x10,0x00,0x9A,0x9A,0x44,0x02,0x10,0x00,0x44,0x11,0x20,0x00,0x49,0xFF,0xFA,0xFD, +0x49,0xFF,0xD8,0xAC,0x44,0x32,0x04,0x08,0x44,0x22,0x00,0x00,0x9A,0x9A,0x44,0x02, +0x00,0x00,0x44,0x11,0x00,0x00,0x49,0xFF,0xFA,0xF0,0x49,0xFF,0xD8,0x9F,0x44,0x02, +0x11,0x00,0x44,0x22,0x48,0xD0,0x84,0x20,0x8A,0x40,0x49,0x00,0x03,0x19,0xFC,0x80, +0x84,0x00,0x64,0x05,0xE4,0x03,0x84,0x1F,0x64,0x03,0x04,0x03,0x64,0x03,0x24,0x03, +0x64,0x12,0x24,0x02,0x44,0x0F,0x0F,0xFF,0xFE,0x0E,0x64,0x02,0x24,0x03,0x64,0x12, +0x00,0x02,0x84,0x18,0xFE,0x0E,0x64,0x02,0x00,0x03,0x64,0x00,0x00,0x08,0xDD,0x9E, +0xFC,0x00,0x49,0xFF,0xFF,0xE7,0x64,0x00,0x80,0x02,0x92,0x1E,0xC0,0x06,0x64,0x00, +0x84,0x02,0x96,0x2E,0xC0,0x02,0xD5,0x00,0x49,0xFF,0xFF,0xB8,0x64,0x12,0x00,0x43, +0x64,0x00,0x00,0x08,0x64,0x00,0x00,0x08,0xFC,0x80,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E, +0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0x44,0x04,0x80,0x04, +0xA6,0x01,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x05,0x84,0x21,0xAE,0x40,0xDD,0x9E, +0x44,0x04,0x80,0x01,0xA6,0x00,0xC8,0x05,0xFC,0x00,0x49,0xFF,0xD4,0xCB,0xFC,0x80, +0xDD,0x9E,0xFC,0x00,0x44,0x04,0x80,0x00,0xA6,0x03,0x49,0xFF,0xD8,0xBA,0xFC,0x80, +0x44,0x14,0x80,0x04,0x44,0x04,0x80,0x84,0xA6,0x88,0xB4,0x00,0x96,0x06,0xFE,0x16, +0xC0,0x03,0x84,0x01,0xAE,0x08,0xDD,0x9E,0xFC,0x00,0x44,0x04,0x80,0x00,0xA6,0x03, +0x49,0xFF,0x98,0x58,0xFC,0x80,0x44,0x14,0x80,0x14,0xA6,0x08,0x96,0x06,0xC0,0x09, +0x84,0x01,0xAE,0x08,0x44,0x14,0x80,0x94,0xB4,0x01,0x58,0x00,0x00,0x01,0xB6,0x01, +0x44,0x14,0x80,0x14,0xA6,0x08,0x92,0x01,0x96,0x06,0xC0,0x09,0x84,0x02,0xAE,0x08, +0x44,0x14,0x80,0x94,0xB4,0x01,0x58,0x00,0x00,0x02,0xB6,0x01,0xDD,0x9E,0xFC,0x00, +0x49,0xFF,0xD3,0x30,0xFC,0x80,0x44,0x04,0x80,0x04,0xA6,0x02,0x92,0x02,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x06,0x84,0x24,0xAE,0x40,0xDD,0x9E,0x44,0x14,0x80,0x04, +0xA6,0x0B,0x92,0x01,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x07,0x84,0x42,0xAE,0x80, +0xA6,0x0B,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x07,0x84,0x21,0xAE,0x40,0xDD,0x9E, +0x44,0x04,0x80,0x08,0xB4,0x20,0x92,0x24,0x96,0x46,0xC1,0x03,0xFA,0x20,0xAE,0x40, +0xB4,0x00,0x84,0x21,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x08,0xAE,0x40,0x3E,0x17, +0xF1,0xAD,0x44,0x04,0x80,0x08,0xB4,0x20,0x92,0x21,0x96,0x46,0xC1,0x06,0x84,0x22, +0xAE,0x40,0x84,0x21,0x3E,0x17,0xF1,0xAC,0xB4,0x00,0x92,0x02,0x96,0x06,0xC0,0x05, +0x44,0x04,0x80,0x08,0x84,0x24,0xAE,0x40,0xDD,0x9E,0xDD,0x9E,0xFC,0x00,0x49,0xFF, +0xD1,0xDA,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xD2,0x3B,0xFC,0x80,0xFC,0x00,0x49,0xFF, +0x8C,0x4C,0xFC,0x80,0x44,0x14,0x80,0x08,0xB4,0x01,0x92,0x08,0x96,0x06,0xC0,0x05, +0x44,0x04,0x80,0x09,0x84,0x41,0xAE,0x80,0xB4,0x01,0x92,0x09,0x96,0x06,0xC0,0x0F, +0x44,0x04,0x80,0x09,0x84,0x22,0xAE,0x40,0x84,0x01,0x3E,0x07,0xF1,0xB0,0x44,0x04, +0x50,0x28,0xB4,0x00,0x92,0x06,0x96,0x1F,0x3E,0x07,0xF1,0xAF,0xDD,0x9E,0x44,0x04, +0x80,0x08,0xB4,0x00,0x92,0x12,0x96,0x06,0xC0,0x15,0x44,0x14,0x80,0x88,0xB4,0x01, +0x92,0x12,0x96,0x06,0xC0,0x0F,0xB4,0x41,0x44,0x0B,0xFF,0xFF,0xFE,0x16,0xB6,0x01, +0x44,0x04,0x80,0x0A,0x84,0x22,0xAE,0x40,0x84,0x21,0x3E,0x17,0xF1,0xB3,0x84,0x24, +0xAE,0x40,0x44,0x04,0x80,0x08,0x84,0x21,0xB4,0x00,0x92,0x10,0x96,0x06,0xC0,0x0C, +0x44,0x04,0x80,0x88,0xB4,0x00,0x92,0x10,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0A, +0x3E,0x17,0xF1,0xB2,0xAE,0x40,0x44,0x04,0x80,0x08,0xB4,0x00,0x92,0x11,0x96,0x06, +0xC0,0x0E,0x44,0x04,0x80,0x88,0xB4,0x00,0x92,0x11,0x96,0x06,0xC0,0x08,0x84,0x01, +0x3E,0x07,0xF1,0xB1,0x44,0x04,0x80,0x0A,0x84,0x22,0xAE,0x40,0xDD,0x9E,0x44,0x14, +0x80,0x08,0x84,0x41,0xB4,0x01,0x92,0x18,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0B, +0xAE,0x80,0x3E,0x27,0xF1,0x78,0xB4,0x01,0x92,0x19,0x96,0x06,0xC0,0x1D,0x44,0x04, +0x80,0x0B,0x84,0x22,0xAE,0x40,0x84,0x00,0x3E,0x07,0xF1,0x78,0x46,0x00,0x00,0x81, +0x44,0x1F,0xFE,0xFF,0x50,0x30,0x00,0x70,0xB4,0x43,0xFE,0x56,0xB6,0x23,0xB4,0x43, +0x44,0x1E,0xFF,0xFF,0xFE,0x56,0xB6,0x23,0xB4,0x43,0x46,0x1F,0xEF,0xFF,0x50,0x10, +0x8F,0xFF,0xFE,0x56,0xB6,0x23,0x44,0x04,0x80,0x08,0xB4,0x00,0x92,0x1A,0x96,0x06, +0xC0,0x0E,0x44,0x14,0x70,0x08,0xB4,0x01,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00, +0x78,0x23,0xB6,0x01,0x44,0x04,0x80,0x0B,0x84,0x24,0xAE,0x40,0x44,0x04,0x80,0x08, +0xB4,0x00,0x92,0x1B,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0B,0x84,0x28,0xAE,0x40, +0xDD,0x9E,0x44,0x04,0x80,0x04,0xA6,0x02,0x92,0x03,0x96,0x06,0xC0,0x05,0x44,0x04, +0x80,0x06,0x84,0x28,0xAE,0x40,0xDD,0x9E,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x10, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0E,0x84,0x41,0xAE,0x80,0xB4,0x01,0x92,0x11, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0E,0x84,0x22,0xAE,0x40,0x44,0x14,0x80,0x0C, +0xB4,0x01,0x92,0x12,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0E,0x84,0x44,0xAE,0x80, +0xB4,0x01,0x92,0x13,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0E,0x84,0x28,0xAE,0x40, +0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x14,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0E, +0xFA,0x40,0xAE,0x80,0xB4,0x01,0x92,0x15,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0E, +0xFA,0x30,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x16,0x96,0x06,0xC0,0x06, +0x44,0x04,0x80,0x0E,0x44,0x20,0x00,0x40,0xAE,0x80,0xB4,0x01,0x92,0x17,0x96,0x06, +0xC0,0x06,0x44,0x04,0x80,0x0E,0x44,0x1F,0xFF,0x80,0xAE,0x40,0x44,0x14,0x80,0x0C, +0xB4,0x01,0x92,0x18,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0F,0x84,0x41,0xAE,0x80, +0xB4,0x01,0x92,0x19,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0F,0x84,0x22,0xAE,0x40, +0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x1A,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0F, +0x84,0x44,0xAE,0x80,0xB4,0x01,0x92,0x1B,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0F, +0x84,0x28,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x1C,0x96,0x06,0xC0,0x05, +0x44,0x04,0x80,0x0F,0xFA,0x40,0xAE,0x80,0xB4,0x01,0x92,0x1D,0x96,0x06,0xC0,0x05, +0x44,0x04,0x80,0x0F,0xFA,0x30,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x1E, +0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0F,0x44,0x20,0x00,0x40,0xAE,0x80,0xB4,0x01, +0x4E,0x04,0x00,0x07,0x44,0x04,0x80,0x0F,0x44,0x1F,0xFF,0x80,0xAE,0x40,0xDD,0x9E, +0x44,0x04,0x80,0x0C,0xB4,0x20,0x96,0x46,0xC1,0x03,0x84,0x21,0xAE,0x40,0xB4,0x00, +0x92,0x01,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0C,0x84,0x22,0xAE,0x40,0x44,0x04, +0x80,0x0C,0xB4,0x20,0x92,0x22,0x96,0x46,0xC1,0x03,0x84,0x24,0xAE,0x40,0xB4,0x00, +0x92,0x03,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0C,0x84,0x28,0xAE,0x40,0x44,0x04, +0x80,0x0C,0xB4,0x20,0x92,0x24,0x96,0x46,0xC1,0x03,0xFA,0x20,0xAE,0x40,0xB4,0x00, +0x92,0x05,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0C,0xFA,0x30,0xAE,0x40,0x44,0x04, +0x80,0x0C,0xB4,0x20,0x92,0x26,0x96,0x46,0xC1,0x04,0x44,0x10,0x00,0x40,0xAE,0x40, +0xB4,0x00,0x92,0x07,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0C,0x44,0x1F,0xFF,0x80, +0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x08,0x96,0x06,0xC0,0x05,0x44,0x04, +0x80,0x0D,0x84,0x41,0xAE,0x80,0xB4,0x01,0x92,0x09,0x96,0x06,0xC0,0x05,0x44,0x04, +0x80,0x0D,0x84,0x22,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x0A,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x0D,0x84,0x44,0xAE,0x80,0xB4,0x01,0x92,0x0B,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x0F,0x84,0x28,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01, +0x92,0x0C,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0D,0xFA,0x40,0xAE,0x80,0xB4,0x01, +0x92,0x0D,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0D,0xFA,0x30,0xAE,0x40,0x44,0x14, +0x80,0x0C,0xB4,0x01,0x92,0x0E,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0D,0x44,0x20, +0x00,0x40,0xAE,0x80,0xB4,0x01,0x92,0x0F,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0D, +0x44,0x1F,0xFF,0x80,0xAE,0x40,0xDD,0x9E,0x44,0x04,0x80,0x00,0x44,0x14,0x80,0x80, +0xA6,0x80,0xB4,0x21,0x96,0x46,0xFE,0x56,0xC1,0x03,0x84,0x21,0xAE,0x40,0xA6,0x00, +0x40,0x10,0x04,0x09,0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x01,0x96,0x06,0xFE,0x0E, +0xC0,0x05,0x44,0x04,0x80,0x00,0x84,0x22,0xAE,0x40,0x44,0x14,0x80,0x00,0xA6,0x08, +0x40,0x20,0x08,0x09,0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x02,0x96,0x06,0xFE,0x16, +0xC0,0x03,0x84,0x04,0xAE,0x08,0xA6,0x08,0x40,0x10,0x0C,0x09,0x44,0x04,0x80,0x80, +0xB4,0x00,0x92,0x03,0x96,0x06,0xFE,0x0E,0xC0,0x05,0x44,0x04,0x80,0x00,0x84,0x28, +0xAE,0x40,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E, +0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xFC,0x00,0x49,0xFF,0x95,0x06,0x49,0xFF, +0xD5,0x5E,0x49,0xFF,0xCA,0x92,0x49,0xFF,0xCE,0x6B,0x84,0x00,0xFC,0x80,0x00,0x00, +0x80,0x60,0x54,0xF1,0x00,0x03,0xE9,0x1B,0xE6,0x44,0xE9,0x19,0x54,0x41,0x80,0x03, +0x54,0x50,0x80,0x03,0xDC,0x14,0xC5,0x0A,0x52,0x52,0x80,0x04,0x9A,0x95,0x99,0x69, +0x08,0x40,0x80,0x01,0x18,0x41,0x80,0x01,0xD9,0xFC,0x40,0x51,0x08,0x09,0xC5,0x07, +0x95,0x6A,0x99,0x69,0x96,0x8F,0xA3,0x09,0xAB,0x19,0xD9,0xFE,0xC2,0x07,0x99,0x4A, +0x08,0x40,0x80,0x01,0x18,0x41,0x80,0x01,0xD9,0xFC,0xDD,0x9E,0x80,0x60,0xE6,0x44, +0xE9,0x1A,0x54,0x51,0x80,0x03,0xC5,0x08,0x52,0x52,0x80,0x04,0x9A,0x95,0x99,0x6B, +0x18,0x11,0x80,0x01,0xDB,0xFE,0x40,0x51,0x08,0x09,0x96,0x8F,0xC5,0x0C,0x95,0x6A, +0x99,0x5D,0x96,0x48,0x40,0x40,0xA0,0x08,0xFE,0x67,0x40,0x40,0xC0,0x08,0xFE,0x67, +0xAA,0x59,0xDB,0xFF,0x99,0x5A,0xD3,0x04,0x18,0x11,0x80,0x01,0xD5,0xFD,0xDD,0x9E, +0x69,0x1A,0xC1,0x37,0x01,0x00,0x12,0x20,0xFF,0x07,0xFF,0x07,0x05,0x0A,0x0B,0x00, +0x00,0xFF,0xFF,0x00,0x01,0x00,0x00,0x01,0x01,0x01,0xFF,0x01,0x01,0xFF,0xFF,0xFF, +0x00,0x00,0x00,0x00,0x5C,0x54,0x47,0x37,0x00,0x00,0x31,0x00,0x94,0x00,0xFA,0x00, +0x66,0x01,0xD9,0x01,0x57,0x02,0xE6,0x02,0x8A,0x03,0x4F,0x04,0x44,0x05,0x84,0x06, +0x42,0x08,0xEB,0x0A,0x98,0x0F,0x55,0x1A,0xF3,0x07,0xFF,0xFF,0x00,0xFF,0xFF,0x00, +0x01,0x00,0x00,0x01,0x01,0x01,0xFF,0x01,0x01,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0x00, +0x01,0x00,0x00,0x01,0xFF,0xFF,0x01,0xFF,0xFF,0x01,0x01,0x01,0x00,0xFF,0xFF,0x00, +0x01,0x00,0x00,0x01,0xFF,0xFF,0x01,0xFF,0xFF,0x01,0x01,0x01,0xFF,0xFF,0x00,0xFF, +0x01,0xFF,0xFF,0x00,0x00,0x00,0x01,0x00,0xFF,0x01,0x00,0x01,0x01,0x01,0x00,0x00, +0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00, +0x9C,0xFF,0xFF,0xFF,0x9C,0xFF,0xFF,0xFF,0xF8,0x03,0x02,0x00,0xF0,0x03,0x02,0x00, +0xE8,0x03,0x02,0x00,0xDC,0x03,0x02,0x00,0xD0,0x03,0x02,0x00,0xC0,0x03,0x02,0x00, +0xB0,0x03,0x02,0x00,0x9C,0x03,0x02,0x00,0x88,0x03,0x02,0x00,0x70,0x03,0x02,0x00, +0x0A,0x0A,0x0B,0x0B,0x0B,0x0C,0x0C,0x0C,0x0C,0x0C,0x00,0x00,0x01,0x05,0x00,0x07, +0x02,0x06,0x04,0x08,0x03,0x09,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x06,0x06, +0x06,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x1F,0x1F, +0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1E, +0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, +0x02,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x03,0x03,0x03,0x03,0x03,0x03, +0x03,0x03,0x03,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x04,0x04,0x04,0x04, +0x04,0x04,0x04,0x04,0x04,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x05,0x05, +0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A, +0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x19,0x19,0x19,0x19,0x19,0x19,0x19, +0x19,0x19,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x18,0x18,0x18,0x18,0x18, +0x18,0x18,0x18,0x18,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x17,0x17,0x17, +0x17,0x17,0x17,0x17,0x17,0x17,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x16, +0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A, +0x0A,0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B, +0x0B,0x0B,0x0B,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x0C,0x0C,0x0C,0x0C, +0x0C,0x0C,0x0C,0x0C,0x0C,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x0D,0x0D, +0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, +0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x11,0x11,0x11,0x11,0x11,0x11,0x11, +0x11,0x11,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x10,0x10,0x10,0x10,0x10, +0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x0F,0x0F,0x0F, +0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x0E, +0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, +0x12,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x13,0x13,0x13,0x13,0x13,0x13, +0x13,0x13,0x13,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x14,0x14,0x14,0x14, +0x14,0x14,0x14,0x14,0x14,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x15,0x15, +0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A, +0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x09,0x09,0x09,0x09,0x09,0x09,0x09, +0x09,0x09,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x08,0x08,0x08,0x08,0x08, +0x08,0x08,0x08,0x08,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x07,0x07,0x07, +0x07,0x07,0x07,0x07,0x07,0x07,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x06, +0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A, +0x1A,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B, +0x1B,0x1B,0x1B,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x1C,0x1C,0x1C,0x1C, +0x1C,0x1C,0x1C,0x1C,0x1C,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x1D,0x1D, +0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, +0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x01,0x01,0x01,0x01,0x01,0x01,0x01, +0x01,0x01,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x06,0x06,0x06,0x06,0x00,0x00, +0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, +0x01,0x01,0x01,0x01,0x30,0x01,0x30,0x01,0x30,0x01,0x30,0x01,0x88,0x01,0x32,0x01, +0x56,0x01,0x68,0x01,0x42,0x01,0x7A,0x01,0x8C,0x01,0x30,0x01,0x44,0x01,0x32,0x01, +0x56,0x01,0x86,0x01,0x30,0x01,0x54,0x01,0x68,0x01,0x42,0x01,0x00,0x00,0x00,0x00, +0x00,0x00,0x04,0x02,0x03,0x02,0x01,0x02,0x01,0x01,0x01,0x04,0x02,0x01,0x04,0x04, +0x30,0x01,0x30,0x01,0x30,0x01,0x30,0x01,0x30,0x01,0x30,0x01,0x30,0x01,0x93,0x01, +0x5C,0x01,0xDE,0x01,0x2D,0x02,0xB1,0x01,0x05,0x02,0xED,0x01,0x41,0x02,0x30,0x01, +0xBF,0x01,0x11,0x02,0x37,0x01,0x3C,0x01,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, +0x08,0x09,0x0A,0x0B,0x0B,0x0A,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, +0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x01,0x02,0x00,0x01,0x02,0x03, +0x04,0x05,0x06,0x07,0x08,0x02,0x01,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, +0x08,0x00,0x00,0x01,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x01,0x00, +0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00, +0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00, +0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00, +0x01,0x00,0x02,0x00,0x02,0x00,0x01,0x00,0x04,0x80,0x00,0x00,0x08,0x40,0x00,0x00, +0x10,0x20,0x00,0x00,0x20,0x10,0x00,0x00,0x40,0x08,0x00,0x00,0x80,0x04,0x00,0x00, +0x00,0x03,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00, +0x58,0x60,0x61,0x62,0x63,0x64,0x65,0xFF,0xFF,0x68,0x69,0x6C,0x6D,0x6E,0x6F,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x52,0x02,0x00,0x00,0x52,0x02,0x00,0x00,0x92,0x02,0x00,0x00,0xD2,0x02,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x90,0x00,0x00,0x00,0x90,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xF0,0x00,0x00,0x00, +0x00,0x00,0x40,0x02,0x40,0x02,0x00,0x00,0xC0,0x00,0xC0,0x00,0x40,0x00,0x40,0x00, +0x00,0x09,0x09,0x00,0x03,0x03,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, +0x01,0x04,0x04,0x04,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x01,0x02, +0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x02,0x01,0x00,0x00,0x01,0x02,0x03, +0x04,0x00,0x01,0x02,0x03,0x04,0x00,0x00,0x07,0x80,0x03,0x00,0x38,0x70,0x00,0x00, +0xC0,0x0F,0x00,0x00,0x08,0x40,0x00,0x00,0x10,0x20,0x00,0x00,0x20,0x10,0x00,0x00, +0x40,0x08,0x00,0x00,0x80,0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x07,0x80,0x03,0x00, +0x38,0x70,0x00,0x00,0xC0,0x0F,0x00,0x00,0x01,0x00,0x02,0x00,0x02,0x00,0x01,0x00, +0x04,0x80,0x00,0x00,0x08,0x40,0x00,0x00,0x10,0x20,0x00,0x00,0x20,0x10,0x00,0x00, +0x40,0x08,0x00,0x00,0x80,0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x38,0x70,0x00,0x00, +0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x07,0x80,0x03,0x00,0x38,0x70,0x00,0x00, +0xC0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x03,0x03,0x03, +0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x03,0x03,0x01,0x01,0x01,0x01, +0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, +0x03,0x03,0x03,0x03,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x02,0x00, +0x02,0x00,0x01,0x00,0x04,0x80,0x00,0x00,0x08,0x40,0x00,0x00,0x10,0x20,0x00,0x00, +0x20,0x10,0x00,0x00,0x40,0x08,0x00,0x00,0x80,0x04,0x00,0x00,0x00,0x03,0x00,0x00, +0x07,0x80,0x03,0x00,0x38,0x70,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x01,0x02,0x03, +0x04,0x05,0x06,0x07,0x08,0x00,0x01,0x02,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, +0x08,0x00,0x01,0x02,0x0B,0x02,0x0B,0x0B,0x0B,0x02,0x00,0x00,0x0B,0x02,0x0B,0x0B, +0x0B,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,0x24,0x01,0x00,0x00,0x66,0x1D,0x00,0x00, +0x00,0x69,0x06,0x00,0x02,0x00,0x06,0x0C,0x02,0x00,0x06,0x0C,0x01,0x00,0x04,0x00, +0x01,0x00,0x03,0x00,0x01,0x00,0x05,0x00,0x05,0x04,0x00,0x00,0xD0,0xDA,0xF7,0x84, +0x10,0x00,0x02,0x00,0x11,0x00,0x02,0x00,0x12,0x00,0x02,0x00,0x14,0x00,0x02,0x00, +0x32,0x32,0x20,0x00,0xC8,0x02,0x02,0x00,0x02,0x01,0x00,0x14,0x14,0x14,0x14,0x00, +0xC4,0x02,0x02,0x00,0x02,0x02,0x00,0x14,0x14,0x14,0x14,0x00,0xC0,0x02,0x02,0x00, +0x02,0x03,0x00,0x14,0x14,0x14,0x14,0x00,0xAC,0x11,0x02,0x00,0x02,0x04,0x00,0x14, +0x14,0x14,0x14,0x00,0xB4,0x02,0x02,0x00,0x0B,0x05,0x01,0x37,0x14,0x64,0x2C,0x00, +0xA4,0x02,0x02,0x00,0x0D,0x06,0x01,0x37,0x14,0x5A,0x2C,0x00,0x98,0x02,0x02,0x00, +0x0B,0x07,0x01,0x28,0x14,0x28,0x14,0x00,0x88,0x02,0x02,0x00,0x0E,0x08,0x01,0x3C, +0x1E,0x37,0x19,0x00,0x80,0x02,0x02,0x00,0x06,0x09,0x01,0x1E,0x14,0x30,0x1E,0x00, +0x70,0x02,0x02,0x00,0x0E,0x0A,0x01,0x3C,0x1C,0x3C,0x22,0x00,0x60,0x02,0x02,0x00, +0x0D,0x0A,0x01,0x3C,0x1C,0x3C,0x22,0x00,0x58,0x02,0x02,0x00,0x07,0x05,0x01,0x37, +0x14,0x78,0x2C,0x00,0x4C,0x02,0x02,0x00,0x0B,0x05,0x01,0x37,0x14,0x78,0x2C,0x00, +0x44,0x02,0x02,0x00,0x08,0x06,0x01,0x37,0x14,0x5A,0x2C,0x00,0x38,0x02,0x02,0x00, +0x0C,0x08,0x01,0x3C,0x1E,0x37,0x19,0x00,0x30,0x02,0x02,0x00,0x06,0x07,0x01,0x28, +0x14,0x28,0x14,0x00,0x28,0x02,0x02,0x00,0x07,0x07,0x01,0x28,0x14,0x28,0x14,0x00, +0x20,0x02,0x02,0x00,0x06,0x09,0x01,0x1E,0x14,0x30,0x1E,0x00,0x14,0x02,0x02,0x00, +0x09,0x09,0x01,0x1E,0x14,0x30,0x1E,0x00,0x08,0x02,0x02,0x00,0x0A,0x06,0x01,0x37, +0x14,0x5A,0x2C,0x00,0x00,0x02,0x02,0x00,0x08,0x06,0x01,0x37,0x14,0x5A,0x2C,0x00, +0xF8,0x01,0x02,0x00,0x05,0x0B,0x01,0x1E,0x14,0x30,0x1E,0x00,0xF0,0x01,0x02,0x00, +0x06,0x0B,0x01,0x1E,0x14,0x30,0x1E,0x00,0xE8,0x01,0x02,0x00,0x06,0x0C,0x01,0x30, +0x1E,0x1E,0x14,0x00,0xE4,0x01,0x02,0x00,0x04,0x0C,0x01,0x30,0x1E,0x1E,0x14,0x00, +0xDC,0x01,0x02,0x00,0x06,0x0D,0x01,0x30,0x1E,0x1E,0x14,0x00,0xD8,0x01,0x02,0x00, +0x04,0x0D,0x01,0x30,0x1E,0x1E,0x14,0x00,0xCC,0x01,0x02,0x00,0x0B,0x05,0x01,0x37, +0x14,0x78,0x2C,0x00,0xC0,0x01,0x02,0x00,0x0C,0x0A,0x01,0x3C,0x1C,0x3C,0x22,0x00, +0xB0,0x01,0x02,0x00,0x0E,0x0A,0x01,0x3C,0x1C,0x3C,0x22,0x00,0xA4,0x01,0x02,0x00, +0x0B,0x0A,0x01,0x3C,0x1C,0x3C,0x22,0x00,0x94,0x01,0x02,0x00,0x10,0x0A,0x01,0x3C, +0x1C,0x3C,0x22,0x00,0x12,0x10,0x0B,0x07,0x02,0x3C,0x36,0x30,0x2A,0x24,0x20,0x19, +0x15,0x10,0x0E,0x09,0x02,0x08,0x10,0x12,0x17,0x1C,0x20,0x23,0x29,0x30,0x33,0x00, +0x1D,0x15,0x10,0x07,0x00,0x3A,0x36,0x32,0x2B,0x25,0x1C,0x17,0x10,0x07,0x00,0x00, +0x26,0x1C,0x22,0x12,0x25,0x30,0x21,0x2A,0x2D,0x32,0x2C,0x30,0x34,0x2D,0x32,0x10, +0x08,0x36,0x30,0x34,0x08,0x0E,0x0C,0x00,0x1F,0x1D,0x08,0x04,0x17,0x17,0x17,0x07, +0x07,0x07,0x00,0x00,0x0A,0x07,0x12,0x1D,0x07,0x07,0x07,0x17,0x17,0x17,0x00,0x00, +0x2F,0x30,0x37,0x03,0x0C,0x0F,0x00,0x00,0x33,0x34,0x0E,0x07,0x07,0x00,0x00,0x00, +0x35,0x02,0x09,0x10,0x35,0x10,0x0A,0x34,0x30,0x34,0x38,0x0C,0x10,0x34,0x30,0x0E, +0x30,0x0A,0x00,0x00,0x3D,0x02,0x10,0x09,0x3D,0x34,0x30,0x0B,0x04,0x00,0x00,0x00, +0x10,0x0E,0x07,0x3C,0x33,0x30,0x00,0x00,0x21,0x1D,0x19,0x14,0x0E,0x04,0x01,0x00, +0x1D,0x18,0x12,0x0A,0x04,0x01,0x00,0x00,0x00,0x3C,0x38,0x34,0x2E,0x23,0x1D,0x16, +0x10,0x0B,0x05,0x01,0x0E,0x0D,0x36,0x32,0x09,0x0D,0x37,0x33,0x10,0x10,0x32,0x3E, +0x0D,0x10,0x33,0x38,0x01,0x0D,0x10,0x00,0x30,0x33,0x08,0x10,0x35,0x34,0x10,0x00, +0x25,0x1F,0x19,0x10,0x0B,0x07,0x03,0x3F,0x39,0x33,0x30,0x2C,0x22,0x00,0x00,0x00, +0x01,0x05,0x0B,0x0E,0x13,0x1A,0x1F,0x25,0x29,0x2E,0x34,0x36,0x3B,0x00,0x00,0x00, +0x0D,0x0D,0x0D,0x0D,0x35,0x35,0x00,0x00,0x00,0x3C,0x36,0x2A,0x23,0x1E,0x1A,0x15, +0x10,0x0B,0x08,0x03,0x3C,0x39,0x00,0x00,0x28,0x22,0x1E,0x19,0x13,0x10,0x0B,0x07, +0x02,0x3D,0x39,0x00,0x10,0x0E,0x0A,0x05,0x00,0x39,0x33,0x30,0x10,0x0B,0x04,0x35, +0x30,0x00,0x00,0x00,0x30,0x34,0x00,0x0D,0x10,0x32,0x34,0x3A,0x07,0x0D,0x10,0x00, +0x1F,0x1F,0x00,0x00,0x0F,0x0F,0x00,0x00,0x2F,0x2F,0x00,0x00,0xFF,0xFF,0x00,0x00, +0xFF,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0B,0x01,0x00,0x00, +0x01,0x00,0x00,0x00,0xF1,0xD8,0xFF,0xFF,0xF1,0xD8,0xFF,0xFF,0xF1,0xD8,0xFF,0xFF, +0xF1,0xD8,0xFF,0xFF,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0xC1,0x37,0x00,0x00, +0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x69,0x1A,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x01,0x01,0x04,0x01,0x01,0x01,0x01,0x01,0x0A,0x0A,0x0B,0x0B, +0x0B,0x0C,0x0C,0x0C,0x0C,0x0C,0x00,0x00,0xF8,0x03,0x02,0x00,0xF0,0x03,0x02,0x00, +0xE8,0x03,0x02,0x00,0xDC,0x03,0x02,0x00,0xD0,0x03,0x02,0x00,0xC0,0x03,0x02,0x00, +0xB0,0x03,0x02,0x00,0x9C,0x03,0x02,0x00,0x88,0x03,0x02,0x00,0x70,0x03,0x02,0x00, +0x61,0x00,0x4D,0x00,0x68,0x00,0x84,0x00,0xA2,0x00,0xBF,0x00,0xDA,0x00,0xF2,0x00, +0x06,0x01,0x13,0x01,0x1A,0x01,0x00,0x00,0x65,0x00,0x58,0x00,0x79,0x00,0x9D,0x00, +0xC1,0x00,0xE3,0x00,0x02,0x01,0x1C,0x01,0x2E,0x01,0x37,0x01,0x6A,0x00,0x67,0x00, +0x91,0x00,0xBE,0x00,0xEA,0x00,0x13,0x01,0x35,0x01,0x4E,0x01,0x5B,0x01,0x00,0x00, +0x72,0x00,0x7C,0x00,0xB2,0x00,0xEB,0x00,0x23,0x01,0x52,0x01,0x75,0x01,0x87,0x01, +0x7C,0x00,0x99,0x00,0xE2,0x00,0x2E,0x01,0x72,0x01,0xA5,0x01,0xC1,0x01,0x00,0x00, +0x45,0x00,0x63,0x00,0x96,0x00,0xC8,0x00,0xF0,0x00,0x07,0x01,0x51,0x00,0x88,0x00, +0xD3,0x00,0x15,0x01,0x3C,0x01,0x00,0x00,0x67,0x00,0xCC,0x00,0x40,0x01,0x8A,0x01, +0x4C,0x00,0xB0,0x00,0x02,0x01,0x00,0x00,0x99,0x00,0x66,0x01,0x38,0x70,0x00,0x00, +0xFF,0xFF,0x00,0x00,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xD0,0xCC,0x50,0x4B, +0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x64,0x00,0xC8,0x00,0x2C,0x01,0xC8,0x00,0x6E,0x00,0x96,0x00,0x3C,0x1E,0x21,0x5A, +0xC8,0x00,0x5E,0x01,0x58,0x02,0x96,0x00,0x6E,0x00,0xDC,0x00,0x5A,0x0F,0x21,0x5A, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x3C,0x50,0x10,0x32,0x32,0x37,0x3C, +0x2D,0x00,0xCE,0xFF,0x26,0x35,0x0A,0x00,0xFA,0x00,0x10,0xFF,0x3C,0x14,0x0A,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xEB,0x00,0x10,0x01, +0xA7,0x01,0xEA,0x01,0x00,0x00,0x00,0x00,0x11,0x01,0x11,0x01,0xEB,0x01,0xEB,0x01, +0x58,0x1B,0x00,0x00,0xC8,0x00,0x01,0x02,0x19,0x10,0x0F,0x19,0x01,0x01,0x03,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x5B,0x00,0x26,0x02,0x20,0x03,0x64,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x28,0x00,0x26,0x02,0x20,0x03,0x58,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x0F,0x0F,0x3C,0x46,0x1D,0x01,0x1E,0x05,0x02,0x32,0x0F,0x3C,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0x48,0x00,0x3C,0x1E,0x48,0x00,0x3A,0x5E,0x48,0x00,0x3A,0x7C,0x48,0x00,0x3A,0x9A, +0x48,0x00,0x3A,0xB8,0x48,0x00,0x3A,0xD6,0x48,0x00,0x3A,0xF4,0x48,0x00,0x3B,0x12, +0x48,0x00,0x3B,0x30,0x48,0x00,0x3B,0x86,0x48,0x00,0x3B,0x88,0x48,0x00,0x3B,0x8A, +0x48,0x00,0x3B,0x8C,0x48,0x00,0x3B,0x8E,0x48,0x00,0x3B,0x90,0x48,0x00,0x3B,0x92, +0x48,0x00,0x3B,0x94,0x48,0x00,0x3B,0x96,0x48,0x00,0x3B,0x98,0x48,0x00,0x3B,0x9A, +0x48,0x00,0x3B,0x9C,0x48,0x00,0x3B,0x9E,0x48,0x00,0x3B,0xA0,0x48,0x00,0x3B,0xA2, +0x48,0x00,0x3B,0xA4,0x48,0x00,0x3B,0xA6,0x48,0x00,0x3B,0xA8,0x48,0x00,0x3B,0xAA, +0x48,0x00,0x3B,0xAC,0x48,0x00,0x3B,0xAE,0x48,0x00,0x3B,0xB0,0x48,0x00,0x3B,0xB2, +0x48,0x00,0x3B,0xB4,0x48,0x00,0x3B,0xB6,0x48,0x00,0x3B,0xB8,0x48,0x00,0x3B,0xBA, +0x48,0x00,0x3B,0xBC,0x48,0x00,0x3B,0xBF,0x48,0x00,0x3B,0xC2,0x48,0x00,0x3B,0xC5, +0x48,0x00,0x3B,0xC8,0xF8,0x78,0x00,0x00,0xFA,0x78,0x00,0x00,0x0E,0x79,0x00,0x00, +0x20,0x79,0x00,0x00,0x2E,0x79,0x00,0x00,0x46,0x79,0x00,0x00,0x54,0x79,0x00,0x00, +0x8C,0x79,0x00,0x00,0x94,0x79,0x00,0x00,0xAA,0x79,0x00,0x00,0xCE,0x79,0x00,0x00, +0x18,0x7A,0x00,0x00,0x1A,0x7A,0x00,0x00,0x22,0x7A,0x00,0x00,0x2A,0x7A,0x00,0x00, +0x32,0x7A,0x00,0x00,0x6C,0x7A,0x00,0x00,0xEC,0x7A,0x00,0x00,0x80,0x7B,0x00,0x00, +0x96,0x7B,0x00,0x00,0xBE,0x7C,0x00,0x00,0xD6,0x7D,0x00,0x00,0x42,0x7E,0x00,0x00, +0x44,0x7E,0x00,0x00,0x46,0x7E,0x00,0x00,0x48,0x7E,0x00,0x00,0x4A,0x7E,0x00,0x00, +0x4C,0x7E,0x00,0x00,0x4E,0x7E,0x00,0x00,0x50,0x7E,0x00,0x00,0x52,0x7E,0x00,0x00, +0x54,0x7E,0x00,0x00,0xE6,0x28,0xE8,0x4C,0x96,0x06,0x46,0x20,0x00,0x81,0x44,0xF0, +0x01,0x3C,0x38,0x17,0x84,0x00,0x40,0xF0,0xBC,0x00,0xDD,0x0F,0x08,0x16,0x24,0x32, +0x40,0x4E,0x5C,0x6A,0x40,0x00,0x40,0x08,0x04,0x31,0x00,0x29,0x44,0x1E,0xFF,0xFF, +0xD5,0x33,0x40,0x00,0x44,0x08,0x04,0x31,0x00,0x29,0x44,0x1D,0xFF,0xFF,0xD5,0x2C, +0x40,0x00,0x48,0x08,0x04,0x31,0x00,0x29,0x44,0x1B,0xFF,0xFF,0xD5,0x25,0x40,0x00, +0x4C,0x08,0x04,0x31,0x00,0x29,0x46,0x1F,0xFF,0x7F,0xD5,0x1C,0x40,0x00,0x50,0x08, +0x04,0x31,0x00,0x29,0x46,0x1F,0xFE,0xFF,0xD5,0x15,0x40,0x00,0x54,0x08,0x04,0x31, +0x00,0x29,0x46,0x1F,0xFD,0xFF,0xD5,0x0E,0x40,0x00,0x58,0x08,0x04,0x31,0x00,0x29, +0x46,0x1F,0xFB,0xFF,0xD5,0x07,0x40,0x00,0x5C,0x08,0x04,0x31,0x00,0x29,0x46,0x1F, +0xF7,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x29,0xDD,0x9E, +0x44,0x24,0x80,0x90,0x96,0x06,0xB4,0x62,0x40,0x10,0x20,0x08,0x44,0x0F,0xFE,0xFF, +0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x46,0x10,0x00,0x82,0xA0,0x0A,0x58,0x00, +0x00,0x01,0xA8,0x0A,0xDD,0x9E,0x46,0x20,0x00,0x82,0x96,0x06,0xA0,0xD5,0x40,0x10, +0x40,0x08,0x44,0x0E,0xFF,0xFF,0xFE,0x1E,0xFE,0x0F,0xA8,0x15,0xDD,0x9E,0x46,0x00, +0x00,0x82,0xA0,0x05,0x92,0x10,0x96,0x06,0xDD,0x9E,0xFC,0x40,0x46,0x60,0x00,0x81, +0x96,0x2F,0x40,0x90,0x20,0x08,0x04,0x03,0x00,0x2A,0x44,0x7F,0xC0,0xFF,0xFE,0x3E, +0x40,0x00,0x24,0x04,0x14,0x03,0x00,0x2A,0x96,0x6F,0x40,0x90,0xC0,0x08,0x46,0x0F, +0xFC,0x0F,0x04,0x13,0x00,0x2A,0x50,0x00,0x0F,0xFF,0xFE,0x46,0x40,0x10,0xA4,0x04, +0x14,0x13,0x00,0x2A,0x96,0xAF,0x04,0x93,0x00,0x2A,0x40,0x11,0x60,0x08,0x46,0x2C, +0x0F,0xFF,0x50,0x21,0x0F,0xFF,0x40,0x24,0x88,0x02,0xFE,0x8F,0x14,0x23,0x00,0x2A, +0x04,0x13,0x00,0x2B,0x96,0xEF,0x92,0x26,0x94,0x4E,0xFE,0x5F,0x14,0x13,0x00,0x2B, +0x97,0x2F,0x40,0x12,0x20,0x08,0x04,0x43,0x00,0x2B,0x97,0x6F,0xFF,0x3E,0xFF,0x0F, +0x14,0x43,0x00,0x2B,0x04,0x13,0x00,0x2B,0x40,0x52,0xC0,0x08,0xFE,0x0E,0xFE,0x2F, +0x14,0x03,0x00,0x2B,0x49,0x00,0x04,0x82,0x00,0x0F,0x80,0x20,0x54,0x10,0x00,0x3F, +0x04,0x03,0x03,0x98,0x92,0x06,0x94,0x06,0xFE,0x0F,0x14,0x03,0x03,0x98,0x00,0x0F, +0x80,0x24,0x04,0x23,0x03,0x99,0x96,0x2F,0x94,0x46,0x44,0x0F,0xF0,0x3F,0xFE,0x16, +0xFE,0x0F,0x14,0x03,0x03,0x99,0x49,0x00,0x04,0x62,0xFC,0xC0,0x46,0x30,0x00,0x82, +0x44,0x41,0x00,0x00,0xB4,0xA3,0x54,0x10,0x87,0xFF,0xFF,0x2F,0xB6,0x83,0xB4,0x83, +0x94,0x92,0x92,0x8B,0x40,0x42,0x2C,0x08,0xFF,0x0F,0xB6,0x83,0x88,0x40,0x4C,0x01, +0x00,0x05,0xA0,0x59,0xAA,0x41,0xD5,0xFC,0xB4,0x23,0x44,0x0E,0xFF,0xFF,0xFE,0x0E, +0xB6,0x03,0xDD,0x9E,0x46,0x30,0x00,0x82,0x44,0x41,0x00,0x00,0xB4,0xA3,0x54,0x10, +0x87,0xFF,0xFF,0x2F,0xB6,0x83,0xB4,0x83,0x94,0x92,0x92,0x8B,0x40,0x42,0x2C,0x08, +0xFF,0x0F,0xB6,0x83,0x88,0x40,0x80,0x23,0x4C,0x01,0x00,0x05,0xA3,0x01,0xA9,0x09, +0xD5,0xFC,0xB4,0x23,0x44,0x0E,0xFF,0xFF,0xFE,0x0E,0xB6,0x03,0xDD,0x9E,0xFC,0x00, +0x46,0x50,0x00,0x80,0x46,0x3F,0xFF,0x00,0x9D,0x99,0x9D,0x2E,0x94,0xA9,0x88,0x40, +0x38,0x11,0x18,0x00,0x38,0x21,0x0C,0x00,0x94,0x4C,0xFE,0x57,0x96,0x48,0x18,0x12, +0x80,0x01,0xDC,0xF5,0xFC,0x80,0xFC,0x00,0x46,0x10,0x00,0x80,0x46,0x2F,0xFE,0xFF, +0x50,0x50,0x80,0x08,0x50,0x61,0x0F,0xF1,0x50,0x41,0x0F,0xF0,0x50,0x30,0x80,0x0E, +0x94,0xA9,0x88,0x40,0x38,0x11,0x18,0x00,0x38,0x21,0x10,0x00,0x94,0x4C,0xFE,0x57, +0x96,0x48,0x18,0x12,0x80,0x01,0xDB,0xF5,0xFC,0x80,0x46,0x20,0x00,0x80,0x84,0x7E, +0x50,0x51,0x01,0xC4,0xB4,0x85,0x96,0x06,0xFE,0xE6,0xFE,0x1F,0xB6,0x05,0x96,0x57, +0xB4,0x65,0x40,0x00,0xA0,0x08,0x44,0x1F,0xF8,0xFF,0xFE,0x5E,0xFE,0x47,0xB6,0x25, +0xDD,0x9E,0x46,0x20,0x00,0x81,0x40,0x00,0x40,0x08,0x04,0x11,0x00,0x11,0x96,0x49, +0xFE,0x0F,0x14,0x01,0x00,0x11,0xDD,0x9E,0x46,0x20,0x00,0x81,0x40,0x00,0x40,0x08, +0x04,0x11,0x00,0x10,0x96,0x49,0xFE,0x0F,0x14,0x01,0x00,0x10,0xDD,0x9E,0x46,0x20, +0x00,0x81,0x04,0x11,0x00,0x10,0x92,0x30,0x40,0x10,0xC0,0x08,0xFE,0x0F,0x14,0x01, +0x00,0x10,0xDD,0x9E,0x46,0x20,0x00,0x81,0x04,0x11,0x00,0x11,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x0F,0x14,0x01,0x00,0x11,0xDD,0x9E,0x46,0x20,0x00,0x81,0x84,0x30, +0x04,0x31,0x00,0x12,0x96,0x1F,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E, +0x46,0x20,0x00,0x80,0x84,0x30,0x04,0x31,0x00,0x70,0x96,0x1F,0xFE,0x5E,0xFE,0x0F, +0x14,0x01,0x00,0x70,0xDD,0x9E,0x46,0x20,0x00,0x81,0x84,0x3E,0x04,0x31,0x00,0x13, +0x96,0x06,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x13,0xDD,0x9E,0x46,0x20,0x00,0x81, +0x96,0x06,0x04,0x31,0x00,0x12,0x40,0x10,0x20,0x08,0x44,0x0F,0xFE,0xFF,0xFE,0x1E, +0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E,0x46,0x20,0x00,0x81,0x96,0x0F,0x40,0x10, +0x48,0x08,0x04,0x31,0x00,0x12,0x46,0x0F,0xFF,0x3F,0x50,0x00,0x0F,0xFF,0xFE,0x1E, +0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E,0x46,0x00,0x00,0x81,0x04,0x00,0x00,0x12, +0x92,0x12,0x96,0x0F,0xDD,0x9E,0x44,0x14,0x80,0x10,0xB4,0x01,0x92,0x0D,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x11,0xFA,0x50,0xAE,0x80,0xB4,0x01,0x92,0x0E,0x96,0x06, +0xC0,0x06,0x44,0x04,0x80,0x11,0x44,0x10,0x00,0x40,0xAE,0x40,0x44,0x14,0x80,0x10, +0xB4,0x01,0x92,0x0F,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x11,0x44,0x2F,0xFF,0x80, +0xAE,0x80,0xB4,0x01,0x92,0x09,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x11,0x84,0x22, +0xAE,0x40,0x44,0x14,0x80,0x10,0xB4,0x01,0x92,0x0C,0x96,0x06,0xC0,0x05,0x44,0x04, +0x80,0x11,0xFA,0x40,0xAE,0x80,0xB4,0x01,0x92,0x17,0x96,0x06,0xC0,0x06,0x44,0x04, +0x80,0x12,0x44,0x1F,0xFF,0x80,0xAE,0x40,0xDD,0x9E,0x00,0x00,0x46,0x10,0x00,0x81, +0x04,0x30,0x80,0x39,0xC0,0x0B,0x5A,0x00,0x01,0x0D,0x5A,0x00,0x02,0x0E,0x84,0x40, +0x5A,0x08,0x03,0x0D,0x46,0x23,0xF3,0xC0,0xD5,0x09,0x46,0x23,0x00,0x00,0xD5,0x06, +0x46,0x23,0x51,0x40,0xD5,0x03,0x46,0x23,0xA2,0x80,0x46,0x1C,0x0C,0x3F,0x50,0x10, +0x8F,0xFF,0x40,0x01,0x84,0x02,0x46,0x10,0x00,0x81,0xFE,0x17,0x14,0x00,0x80,0x39, +0xDD,0x9E,0x46,0x20,0x00,0x81,0x96,0x0F,0x40,0x10,0x58,0x08,0x04,0x31,0x00,0x39, +0x46,0x0F,0xF3,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x39, +0xDD,0x9E,0x46,0x10,0x00,0x81,0x04,0x30,0x80,0x39,0xC0,0x11,0x5A,0x00,0x01,0x09, +0x5A,0x00,0x02,0x0A,0x5A,0x08,0x03,0x0B,0x44,0x03,0xFF,0xC0,0xD5,0x08,0x44,0x01, +0x55,0x40,0xD5,0x05,0x44,0x02,0xAA,0x80,0xD5,0x02,0x84,0x00,0x44,0x23,0xFF,0xC0, +0x44,0x1C,0x00,0x3F,0xFE,0x5E,0xFE,0x16,0xFE,0x0F,0x46,0x10,0x00,0x81,0x14,0x00, +0x80,0x39,0xDD,0x9E,0x46,0x20,0x00,0x81,0x96,0x0F,0x04,0x31,0x00,0x39,0x40,0x10, +0x28,0x08,0x44,0x0F,0xF3,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x39,0xDD,0x9E, +0x46,0x20,0x00,0x81,0x46,0x31,0x55,0x10,0x50,0x31,0x84,0x40,0x50,0x51,0x00,0xE4, +0xB4,0x25,0xA8,0xE9,0xA1,0x2A,0x84,0x78,0xFE,0xE6,0xE6,0x05,0xA8,0xEA,0xE8,0x3A, +0x44,0xF0,0x06,0x10,0x38,0x07,0x80,0x00,0x40,0xF0,0x3C,0x00,0x4A,0x00,0x3C,0x00, +0x06,0x1C,0x28,0x3E,0x54,0x00,0x46,0x03,0xA3,0x2A,0x92,0x26,0x50,0x00,0x03,0x00, +0x94,0x4E,0x14,0x01,0x00,0x38,0x58,0x10,0x80,0x3A,0xD5,0x22,0x84,0x00,0x92,0x26, +0x14,0x01,0x00,0x38,0x94,0x4E,0xD5,0x1C,0x46,0x05,0x55,0x55,0x92,0x26,0x50,0x00, +0x05,0x55,0x94,0x4E,0x14,0x01,0x00,0x38,0x58,0x10,0x80,0x15,0xD5,0x11,0x46,0x0A, +0xAA,0xAA,0x92,0x26,0x50,0x00,0x0A,0xAA,0x94,0x4E,0x14,0x01,0x00,0x38,0x58,0x10, +0x80,0x2A,0xD5,0x06,0x84,0x1F,0x14,0x01,0x00,0x38,0x58,0x10,0x80,0x3F,0x14,0x11, +0x00,0x39,0xDD,0x9E,0xDD,0x9E,0x44,0x24,0x50,0x00,0x96,0x06,0xB4,0x62,0x94,0x42, +0x84,0x1B,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x04,0x50,0x28,0xB4,0x00, +0x92,0x04,0x96,0x06,0xDD,0x9E,0x92,0x00,0x46,0x20,0x00,0x83,0x84,0x3C,0x04,0x31, +0x00,0x10,0x96,0x0F,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x10,0xDD,0x9E,0x46,0x20, +0x00,0x83,0x96,0x06,0x04,0x31,0x00,0x10,0x40,0x10,0x20,0x08,0x44,0x0F,0xFE,0xFF, +0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x10,0xDD,0x9E,0xFC,0x40,0x46,0x20,0x00,0x83, +0x44,0x5F,0xFE,0xFF,0x83,0x82,0xBB,0x18,0x46,0x40,0x0F,0x03,0xFE,0xEE,0xBB,0x98, +0xBB,0x18,0x50,0x42,0x0C,0x0F,0x92,0x68,0x40,0x31,0xA0,0x08,0x58,0x31,0x80,0x46, +0xBB,0x98,0xBF,0x39,0xBE,0x3A,0x04,0x91,0x00,0x3B,0xFF,0xE6,0xFF,0xA6,0x40,0x44, +0x90,0x02,0x04,0x90,0x80,0x02,0xBB,0x3C,0xB5,0x41,0x89,0x24,0xA1,0x0B,0x96,0xDF, +0x88,0xEA,0x04,0xA0,0x80,0x01,0x98,0x5C,0xBB,0x18,0x88,0xCA,0x58,0x31,0x81,0x00, +0xBB,0x98,0xBB,0x18,0x92,0x68,0x40,0x31,0xA0,0x08,0x58,0x31,0x80,0x46,0xBB,0x98, +0xBF,0xB9,0xBE,0xBA,0x14,0x91,0x00,0x3B,0x96,0xC9,0xB9,0x3C,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x5F,0xB9,0xBC,0xB4,0x20,0xB9,0xB9,0xA0,0x41,0xB9,0xBA,0xA0,0x42, +0xB9,0xBB,0xA4,0x46,0xB8,0x3C,0x92,0x10,0x40,0x00,0x40,0x08,0xFE,0x0F,0xB8,0xBC, +0xB8,0x18,0xFF,0x46,0xBD,0x98,0xFC,0xC0,0xFC,0x20,0x46,0x20,0x00,0x83,0x44,0x3F, +0xFE,0xFF,0x83,0x82,0xBC,0x18,0x94,0x4A,0xFE,0xE6,0xBB,0x98,0xBB,0x18,0x50,0x51, +0x00,0xE4,0x92,0x68,0x40,0x31,0xA0,0x08,0x58,0x31,0x80,0x48,0xBB,0x98,0xBB,0x18, +0x50,0x61,0x00,0xE8,0x58,0x31,0x81,0x00,0xBB,0x98,0x50,0x71,0x00,0xEC,0x88,0x20, +0x50,0x21,0x00,0xF0,0x46,0x30,0x00,0x83,0x4C,0x00,0x80,0x12,0xA2,0xC1,0xB6,0x65, +0x83,0x80,0xBB,0x3F,0x02,0x40,0x01,0x7E,0xB6,0x66,0xBB,0x7F,0xB6,0x67,0xB4,0x62, +0x92,0x70,0x40,0x31,0xC0,0x08,0xFE,0xE7,0xB6,0x62,0xD5,0xED,0x83,0x83,0xB9,0x18, +0x44,0x0F,0xFE,0xFF,0xFE,0x0E,0xB8,0x98,0xFC,0xA0,0xFC,0x20,0x46,0x10,0x00,0x83, +0x44,0x2F,0xFE,0xFF,0x83,0x81,0xBB,0x18,0x46,0x40,0xF0,0x3C,0xFE,0x9E,0xBA,0x98, +0xBA,0x18,0x50,0x32,0x00,0xF0,0x92,0x48,0x40,0x21,0x20,0x08,0x58,0x21,0x00,0x46, +0xBA,0x98,0xBE,0x39,0xB4,0x80,0xBD,0x3A,0xFF,0x9E,0x88,0xC4,0xA1,0x01,0xBF,0x3B, +0xFF,0x5E,0x88,0xA4,0xA1,0x02,0xBA,0x3C,0xFE,0xFE,0x88,0x83,0xA0,0xC3,0x54,0x21, +0x00,0xF0,0xBE,0xB9,0x98,0x13,0xBD,0xBA,0xBC,0xBB,0x96,0x81,0xB8,0x3C,0x92,0x10, +0x40,0x00,0x40,0x08,0xFE,0x17,0xB8,0xBC,0xFC,0xA0,0x44,0x14,0x80,0x90,0x84,0x5E, +0xB4,0x61,0x96,0x06,0xFE,0x9E,0xFE,0x87,0xB6,0x41,0xB4,0x81,0x44,0x2F,0xFF,0xEF, +0x94,0xC4,0xFE,0xA6,0xFE,0x9F,0xB6,0x41,0xB4,0x61,0x44,0x2F,0xFB,0xFF,0xFE,0x9E, +0x40,0x00,0x28,0x08,0xFE,0x17,0xB6,0x01,0x46,0x10,0x00,0x80,0x84,0x18,0x50,0x30, +0x82,0x6C,0xB4,0x43,0xFE,0x16,0x58,0x00,0x00,0x06,0xB6,0x03,0xB4,0x43,0x44,0x0F, +0xF0,0xFF,0xFE,0x16,0x58,0x00,0x08,0x00,0xB6,0x03,0xDD,0x9E,0x52,0x00,0x00,0x01, +0x46,0x20,0x00,0x80,0x96,0x06,0xC9,0x08,0x40,0x10,0x20,0x08,0x04,0x31,0x00,0x79, +0x44,0x0F,0xFE,0xFF,0xD5,0x07,0x40,0x10,0x40,0x08,0x04,0x31,0x00,0x79,0x44,0x0E, +0xFF,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x79,0xDD,0x9E,0x46,0x10,0x00,0x82, +0x5A,0x08,0x01,0x0B,0x50,0x30,0x80,0x20,0xB4,0x03,0x58,0x00,0x00,0x02,0xB6,0x03, +0xB4,0x43,0x84,0x1E,0xD5,0x04,0x04,0x20,0x80,0x08,0x84,0x1D,0xFE,0x16,0x14,0x00, +0x80,0x08,0xDD,0x9E,0x46,0x30,0x00,0x83,0x54,0x10,0x00,0x01,0x50,0x51,0x83,0xB8, +0xB4,0x85,0x40,0x20,0xA0,0x08,0x44,0x1F,0xFE,0xFF,0xFE,0x66,0xFE,0x57,0xB6,0x25, +0xB4,0x25,0xE6,0x01,0x44,0x0F,0xFD,0xFF,0x40,0xF7,0xA4,0x08,0xFE,0x0E,0x40,0x00, +0x3C,0x04,0xB6,0x05,0xDD,0x9E,0x46,0x30,0x00,0x83,0x54,0x10,0x00,0x01,0x50,0x51, +0x83,0xB8,0xB4,0x85,0x40,0x20,0xA8,0x08,0x44,0x1F,0xFB,0xFF,0xFE,0x66,0xFE,0x57, +0xB6,0x25,0xB4,0x25,0xE6,0x01,0x44,0x0F,0xF7,0xFF,0x40,0xF7,0xAC,0x08,0xFE,0x0E, +0x40,0x00,0x3C,0x04,0xB6,0x05,0xDD,0x9E,0x44,0x27,0x20,0x68,0x96,0x01,0xB4,0x22, +0x92,0x30,0x40,0x10,0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x70,0xB4,0x22, +0x92,0x30,0x40,0x10,0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x78,0xB4,0x22, +0x92,0x30,0x40,0x10,0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x80,0xB4,0x22, +0x92,0x30,0x40,0x10,0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x88,0xB4,0x22, +0x92,0x30,0x40,0x10,0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x90,0xB4,0x22, +0x92,0x30,0x40,0x10,0xC0,0x08,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0xC8,0x0A,0x44,0x17, +0x20,0x00,0x46,0x0F,0xEF,0xFF,0xB4,0x41,0x50,0x00,0x0F,0xFF,0xFE,0x16,0xD5,0x09, +0x5A,0x08,0x01,0x09,0x44,0x17,0x20,0x00,0x46,0x00,0x10,0x00,0xB4,0x41,0xFE,0x17, +0xB6,0x01,0xDD,0x9E,0x44,0x27,0x20,0x00,0x84,0x3E,0xB4,0x62,0x96,0x06,0xFE,0x5E, +0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x27,0x20,0x04,0x84,0x3E,0xB4,0x62,0x96,0x06, +0xFE,0x5E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x27,0x20,0x00,0x96,0x06,0xB4,0x62, +0x40,0x10,0x40,0x08,0x44,0x0E,0xFF,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E, +0xC9,0x06,0x44,0x27,0x10,0x00,0xB4,0x22,0xFE,0x0F,0xD5,0x08,0x5A,0x18,0x01,0x08, +0x44,0x27,0x10,0x00,0xFE,0x03,0xB4,0x22,0xFE,0x0E,0xB6,0x02,0xDD,0x9E,0x5A,0x18, +0x01,0x07,0x44,0x27,0x10,0x04,0xB4,0x22,0xFE,0x0F,0xD5,0x07,0xC9,0x07,0x44,0x27, +0x10,0x04,0xFE,0x03,0xB4,0x22,0xFE,0x0E,0xB6,0x02,0xDD,0x9E,0x5A,0x18,0x01,0x07, +0x44,0x27,0x10,0x08,0xB4,0x22,0xFE,0x0F,0xD5,0x07,0xC9,0x07,0x44,0x27,0x10,0x08, +0xFE,0x03,0xB4,0x22,0xFE,0x0E,0xB6,0x02,0xDD,0x9E,0x44,0x16,0x00,0x00,0x5A,0x08, +0x01,0x06,0xB4,0x01,0x58,0x00,0x00,0x01,0xD5,0x04,0xB4,0x41,0x84,0x1E,0xFE,0x16, +0xB6,0x01,0xDD,0x9E,0x44,0x16,0x00,0x00,0xB4,0x41,0x5A,0x08,0x01,0x06,0x44,0x01, +0x00,0x00,0xFE,0x17,0xD5,0x04,0x44,0x0E,0xFF,0xFF,0xFE,0x16,0xB6,0x01,0xDD,0x9E, +0x44,0x06,0x00,0x00,0xB4,0x00,0x92,0x1C,0x96,0x06,0xDD,0x9E,0x44,0x16,0x00,0x30, +0x5A,0x08,0x01,0x06,0xB4,0x01,0x58,0x00,0x01,0x00,0xD5,0x05,0xB4,0x41,0x44,0x0F, +0xFE,0xFF,0xFE,0x16,0xB6,0x01,0xDD,0x9E,0x44,0x36,0x38,0x14,0x40,0x00,0x20,0x08, +0x40,0x20,0x20,0x09,0xB4,0x03,0x96,0x6F,0x92,0x18,0x40,0x00,0x60,0x08,0xFE,0x17, +0x44,0x26,0x38,0x08,0xB6,0x03,0xB4,0x62,0x40,0x00,0xE0,0x08,0x46,0x1C,0x0F,0xFF, +0x50,0x10,0x8F,0xFF,0xFE,0x5E,0xFE,0x47,0xB6,0x22,0x44,0x16,0x38,0x04,0x44,0x01, +0x00,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x03, +0x96,0x06,0xC0,0xFD,0x44,0x04,0x80,0x0C,0x84,0x28,0xAE,0x40,0xDD,0x9E,0x44,0x36, +0x34,0x14,0x40,0x00,0x20,0x08,0x40,0x20,0x20,0x09,0xB4,0x03,0x96,0x6F,0x92,0x18, +0x40,0x00,0x60,0x08,0xFE,0x17,0x44,0x26,0x34,0x08,0xB6,0x03,0xB4,0x62,0x40,0x00, +0xE0,0x08,0x46,0x1C,0x0F,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x5E,0xFE,0x47,0xB6,0x22, +0x44,0x16,0x34,0x04,0x44,0x01,0x00,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0x44,0x14, +0x80,0x0C,0xB4,0x01,0x92,0x13,0x96,0x06,0xC0,0xFD,0x44,0x04,0x80,0x0E,0x84,0x28, +0xAE,0x40,0xDD,0x9E,0xFC,0x01,0xB6,0x3F,0x80,0x22,0xF3,0x81,0x49,0xFF,0xFF,0xA6, +0xB4,0x1F,0xF1,0x01,0x49,0xFF,0xFF,0xCD,0xFC,0x81,0x44,0x16,0x3C,0x04,0xB4,0x01, +0x58,0x00,0x00,0x01,0xB6,0x01,0xDD,0x9E,0x44,0x26,0x38,0x04,0xB4,0x22,0x44,0x06, +0x38,0x04,0x92,0x28,0x96,0x46,0xC9,0xFB,0xB4,0x40,0x84,0x3E,0xFE,0x56,0xB6,0x20, +0x80,0x20,0xB4,0x01,0x92,0x08,0x96,0x06,0xC8,0xFD,0x44,0x26,0x34,0x04,0xB4,0x22, +0x44,0x06,0x34,0x04,0x92,0x28,0x96,0x46,0xC9,0xFB,0xB4,0x40,0x84,0x3E,0xFE,0x56, +0xB6,0x20,0x80,0x20,0xB4,0x01,0x92,0x08,0x96,0x06,0xC8,0xFD,0xDD,0x9E,0xFC,0x00, +0x46,0x60,0x00,0x83,0x83,0x86,0xB8,0x27,0x58,0x00,0x00,0x01,0xB8,0xA7,0xB8,0x27, +0x58,0x00,0x01,0x00,0xB8,0xA7,0x84,0x01,0x49,0x00,0x08,0xA4,0xB9,0x27,0x84,0x1E, +0xFE,0x0E,0xB8,0xA7,0xB9,0x27,0x44,0x0F,0xFE,0xFF,0xFE,0x0E,0xB8,0xA7,0xFC,0x80, +0xFC,0x40,0x46,0x60,0x00,0x83,0x84,0xFE,0x83,0x86,0x04,0x93,0x00,0x14,0x96,0x06, +0x40,0x74,0x9C,0x02,0xFE,0x3F,0xB8,0x94,0x96,0x5F,0xBF,0x14,0x40,0x00,0xE0,0x08, +0x46,0x1F,0x0F,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x7E,0xFE,0x47,0xB9,0x94,0x97,0x2F, +0xB9,0x14,0x40,0x02,0x44,0x08,0x46,0x4F,0xF8,0x1F,0x50,0x42,0x0F,0xFF,0xFF,0x0E, +0xFF,0x07,0xBC,0x94,0x97,0x6F,0x40,0x02,0xAC,0x08,0xBD,0x14,0x44,0x1E,0x07,0xFF, +0xFF,0x4E,0xFF,0x47,0xBD,0x94,0xB8,0x15,0x54,0x21,0x03,0xFF,0x92,0x0A,0x40,0x00, +0x28,0x08,0xFE,0x17,0xB8,0x95,0x96,0xF7,0x40,0x01,0xC0,0x08,0xB9,0x15,0x46,0x3F, +0xF8,0x0F,0x50,0x31,0x8F,0xFF,0xFE,0xCE,0xFE,0xC7,0xBB,0x95,0xFC,0xC0,0x46,0x00, +0x00,0x83,0x04,0x00,0x00,0x16,0x96,0x01,0xDD,0x9E,0xFC,0x00,0x46,0x40,0x00,0x83, +0x84,0x61,0x83,0x84,0xBE,0x13,0x84,0xBE,0xFE,0x1E,0xFF,0x76,0xFE,0x2F,0xB8,0x93, +0xFE,0x5E,0x40,0x30,0xA0,0x08,0xB9,0x60,0x44,0x0F,0xFE,0xFF,0xFE,0x46,0xFE,0x5F, +0xB9,0xE0,0x96,0xA7,0x40,0x01,0x20,0x08,0xBA,0x13,0x44,0x1F,0xE0,0xFF,0xFE,0x8E, +0xFE,0x87,0xBA,0x93,0xFC,0x80,0x46,0x30,0x00,0x83,0x96,0x8F,0x50,0x41,0x81,0x84, +0xB6,0x04,0xA8,0x61,0xA0,0x62,0x84,0x1C,0xFE,0x0E,0xFE,0x87,0xA8,0xA2,0xDD,0x9E, +0x46,0x20,0x00,0x83,0x96,0x27,0x04,0x11,0x00,0x60,0x92,0x25,0x94,0x4D,0xFE,0x47, +0x14,0x11,0x00,0x60,0xDD,0x9E,0x46,0x20,0x00,0x83,0x84,0x3E,0x04,0x31,0x00,0x12, +0x96,0x06,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E,0xB4,0x40,0x46,0x10, +0x00,0x80,0x14,0x20,0x80,0x48,0xA0,0x01,0x14,0x00,0x80,0x4A,0xDD,0x9E,0xFC,0x40, +0x46,0x20,0x00,0x83,0x50,0x61,0x00,0xE4,0x83,0x82,0xBB,0x18,0x50,0x71,0x00,0xE8, +0x58,0x31,0x81,0x00,0xBB,0x98,0xBB,0x18,0x50,0x91,0x00,0xEC,0x92,0x68,0x40,0x31, +0xA0,0x08,0x58,0x31,0x80,0x88,0xBB,0x98,0x8C,0x02,0x84,0xA0,0x50,0x21,0x00,0xF0, +0x46,0x30,0x00,0x83,0xD1,0x22,0x22,0x30,0x00,0x00,0x22,0x40,0x7F,0xFF,0x40,0x31, +0xC0,0x08,0x88,0x64,0xB6,0x66,0x22,0x30,0x00,0x02,0x22,0x40,0x00,0x01,0x40,0x31, +0xC0,0x08,0x88,0x64,0xB6,0x67,0x22,0x30,0x00,0x04,0x22,0x40,0x00,0x03,0x40,0x31, +0xC0,0x08,0x88,0x64,0xB6,0x69,0xB4,0x62,0x8C,0xA1,0x92,0x70,0x40,0x31,0xC0,0x08, +0xB6,0x62,0x97,0x68,0x8C,0x0C,0xD5,0xDD,0x83,0x83,0xB9,0x18,0x44,0x0F,0xFE,0xFF, +0xFE,0x0E,0xB8,0x98,0xFC,0xC0,0x46,0x20,0x00,0x83,0x96,0x0F,0x04,0x31,0x00,0x12, +0x40,0x10,0x40,0x08,0x44,0x0C,0xFF,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x12, +0xDD,0x9E,0xFC,0x00,0x84,0x40,0x44,0x64,0x00,0xC0,0x38,0x30,0x88,0x00,0x38,0x40, +0x09,0x01,0x99,0x56,0x40,0x31,0xB0,0x08,0x95,0x69,0xFE,0xE7,0x96,0xD9,0x8C,0x41, +0xAC,0xE8,0x5A,0x28,0x14,0xF4,0xFC,0x80,0x46,0x20,0x00,0x83,0x96,0x06,0x04,0x31, +0x00,0x26,0x94,0x41,0x84,0x1D,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x26,0xDD,0x9E, +0x46,0x20,0x00,0x81,0x96,0x06,0xA0,0xD5,0x40,0x10,0x20,0x08,0x44,0x0F,0xFE,0xFF, +0xFE,0x1E,0xFE,0x0F,0xA8,0x15,0xDD,0x9E,0xC9,0x08,0x46,0x20,0x00,0x91,0x84,0x3E, +0xB4,0x62,0x96,0x06,0xFE,0x5E,0xD5,0x0A,0x5A,0x18,0x01,0x0B,0x46,0x20,0x00,0x91, +0x96,0x06,0xB4,0x62,0x94,0x41,0x84,0x1D,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E, +0xFC,0x00,0x44,0x00,0x00,0xC8,0x49,0x00,0x07,0x75,0x44,0x14,0x50,0x0C,0x46,0x60, +0x00,0x81,0xB4,0x01,0x58,0x00,0x00,0x04,0xB6,0x01,0xFA,0x18,0x49,0x00,0x07,0x73, +0x83,0x86,0xB8,0x1C,0x58,0x00,0x01,0x00,0xB8,0x9C,0x84,0x01,0x49,0x00,0x07,0x6B, +0xB9,0x1C,0x44,0x01,0x00,0x00,0xFE,0x0F,0xB8,0x9C,0x44,0x00,0x01,0xF4,0x49,0x00, +0x07,0x59,0xB9,0x1C,0x46,0x00,0x10,0x00,0xFE,0x0F,0xB8,0x9C,0x44,0x00,0x01,0xF4, +0x49,0x00,0x07,0x50,0xFC,0x80,0x00,0x00,0x46,0x10,0x00,0x80,0x50,0x50,0x80,0x80, +0x50,0x10,0x80,0xB0,0xA2,0x81,0xAA,0xA9,0xD9,0xFE,0xDD,0x9E,0x46,0x10,0x00,0x80, +0x50,0x50,0x80,0xB0,0x50,0x10,0x80,0xC4,0xA2,0x81,0xAA,0xA9,0xD9,0xFE,0xDD,0x9E, +0xB4,0x40,0x46,0x10,0x00,0x80,0x14,0x20,0x80,0x47,0xA0,0x01,0x14,0x00,0x80,0x49, +0xDD,0x9E,0x46,0x10,0x00,0x80,0x50,0x50,0x81,0x40,0x50,0x30,0x81,0x4C,0xA6,0x41, +0xA6,0x80,0x94,0x4C,0xFE,0x57,0x96,0x48,0xAE,0x68,0x00,0x10,0x00,0x0D,0x00,0x20, +0x00,0x0C,0x94,0x4C,0xFE,0x57,0x96,0x48,0x10,0x12,0x80,0x0C,0x8C,0xA1,0x8C,0x02, +0xDB,0xEF,0xDD,0x9E,0x46,0x10,0x00,0x80,0x50,0x50,0x80,0x40,0x50,0x30,0x80,0x46, +0xA6,0x41,0xA6,0x80,0x94,0x4C,0xFE,0x57,0x96,0x48,0xAE,0x68,0x00,0x10,0x00,0x0D, +0x00,0x20,0x00,0x0C,0x94,0x4C,0xFE,0x57,0x96,0x48,0x10,0x12,0x80,0x0C,0x8C,0xA1, +0x8C,0x02,0xDB,0xEF,0xDD,0x9E,0xA6,0x81,0x80,0xA0,0x94,0x94,0x08,0x12,0x80,0x05, +0xFE,0x8F,0x46,0x10,0x00,0x80,0x96,0x90,0x10,0x20,0x80,0x48,0xA6,0xA9,0xA6,0xC5, +0x94,0x94,0xFE,0x9F,0x96,0x90,0x10,0x20,0x80,0x54,0xA6,0x83,0xA6,0xC2,0x94,0x94, +0xFE,0x9F,0x96,0x90,0x10,0x20,0x80,0x49,0xA6,0xAB,0xA6,0xEA,0x94,0x94,0xFE,0x9F, +0x96,0x90,0x10,0x20,0x80,0x55,0xA6,0x84,0x84,0x0F,0xFE,0x86,0x40,0x31,0x40,0x08, +0x46,0x4F,0xFF,0x0F,0x04,0x20,0x80,0x12,0x50,0x42,0x0F,0xFF,0xFE,0xA6,0xFE,0x9F, +0x14,0x20,0x80,0x12,0xA6,0xAC,0xFE,0x16,0x40,0x20,0x40,0x08,0x04,0x00,0x80,0x15, +0xFE,0x26,0xFE,0x17,0x14,0x00,0x80,0x15,0xDD,0x9E,0x46,0x10,0x00,0x80,0x50,0x50, +0x80,0x58,0x50,0x30,0x80,0x5E,0xA6,0x41,0xA6,0x80,0x94,0x4C,0xFE,0x57,0x96,0x48, +0xAE,0x68,0x00,0x10,0x00,0x0D,0x00,0x20,0x00,0x0C,0x94,0x4C,0xFE,0x57,0x96,0x48, +0x10,0x12,0x80,0x0C,0x8C,0xA1,0x8C,0x02,0xDB,0xEF,0xDD,0x9E,0xA6,0x81,0x80,0xA0, +0x94,0x94,0x08,0x12,0x80,0x05,0xFE,0x8F,0x46,0x10,0x00,0x80,0x96,0x90,0x10,0x20, +0x80,0x60,0xA6,0xA9,0xA6,0xC5,0x94,0x94,0xFE,0x9F,0x96,0x90,0x10,0x20,0x80,0x6C, +0xA6,0x83,0xA6,0xC2,0x94,0x94,0xFE,0x9F,0x96,0x90,0x10,0x20,0x80,0x61,0xA6,0xAB, +0xA6,0xEA,0x94,0x94,0xFE,0x9F,0x96,0x90,0x10,0x20,0x80,0x6D,0xA6,0x84,0x84,0x0F, +0xFE,0x86,0x40,0x31,0x40,0x08,0x46,0x4F,0xFF,0x0F,0x04,0x20,0x80,0x18,0x50,0x42, +0x0F,0xFF,0xFE,0xA6,0xFE,0x9F,0x14,0x20,0x80,0x18,0xA6,0xAC,0xFE,0x16,0x40,0x20, +0x40,0x08,0x04,0x00,0x80,0x1B,0xFE,0x26,0xFE,0x17,0x14,0x00,0x80,0x1B,0xDD,0x9E, +0xE6,0x06,0xE8,0x57,0xFC,0x20,0x46,0x70,0x00,0x81,0x44,0xF0,0x10,0x78,0x38,0x07, +0x80,0x00,0x40,0xF0,0x3C,0x00,0xDD,0x0F,0x06,0x16,0x34,0x44,0x62,0x76,0x04,0x03, +0x80,0x20,0x54,0x10,0x87,0xFF,0x92,0x0B,0x40,0x00,0x2C,0x08,0xD5,0x0C,0x54,0x10, +0x87,0xFF,0x40,0x00,0xC0,0x08,0x04,0x23,0x80,0x20,0x46,0x1F,0x80,0x0F,0x50,0x10, +0x8F,0xFF,0xFE,0x56,0xFE,0x47,0x14,0x13,0x80,0x20,0xFC,0xA0,0x04,0x03,0x80,0x21, +0x54,0x10,0x87,0xFF,0x92,0x0B,0x40,0x00,0x2C,0x08,0xD5,0x0C,0x54,0x10,0x87,0xFF, +0x40,0x00,0xC0,0x08,0x04,0x23,0x80,0x21,0x46,0x1F,0x80,0x0F,0x50,0x10,0x8F,0xFF, +0xFE,0x56,0xFE,0x47,0x14,0x13,0x80,0x21,0xFC,0xA0,0x83,0x87,0xB8,0x22,0x54,0x10, +0x87,0xFF,0x92,0x0B,0x40,0x00,0x2C,0x08,0xFE,0x47,0xB9,0xA2,0xFC,0xA0,0x80,0xC1, +0x49,0xFF,0xFD,0x54,0x54,0x13,0x03,0xFF,0x95,0x8E,0x04,0x13,0x83,0x98,0x44,0x0F, +0x00,0x3F,0xFE,0x46,0xFE,0x77,0x14,0x13,0x83,0x98,0x49,0xFF,0xFD,0x40,0xFC,0xA0, +0xDD,0x9E,0x46,0x20,0x00,0x83,0x96,0x06,0x40,0x10,0x60,0x08,0x04,0x31,0x00,0xC0, +0x46,0x0F,0xEF,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0xC0, +0xDD,0x9E,0x46,0x20,0x00,0x83,0x96,0x06,0x40,0x10,0x64,0x08,0x04,0x31,0x00,0xC0, +0x46,0x0F,0xDF,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0xC0, +0xDD,0x9E,0x46,0x20,0x00,0x83,0x96,0x0F,0x04,0x31,0x00,0x0F,0x40,0x10,0x20,0x08, +0x44,0x0F,0xFC,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x0F,0xDD,0x9E,0x46,0x20, +0x00,0x83,0x84,0x3E,0x04,0x31,0x00,0x0F,0x96,0x06,0xFE,0x5E,0xFE,0x0F,0x14,0x01, +0x00,0x0F,0xDD,0x9E,0x46,0x20,0x00,0x83,0x96,0x06,0x04,0x31,0x00,0x0F,0x40,0x10, +0x28,0x08,0x44,0x0F,0xFB,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x0F,0xDD,0x9E, +0x46,0x10,0x00,0x83,0x44,0x01,0x00,0x00,0x04,0x20,0x80,0x0E,0xFE,0x17,0x14,0x00, +0x80,0x0E,0xDD,0x9E,0x46,0x20,0x00,0x83,0x46,0x3F,0x7F,0xFF,0x50,0x51,0x00,0x38, +0xB4,0x85,0x50,0x31,0x8F,0xFF,0xFE,0xE6,0xB6,0x65,0xB4,0x85,0x46,0x3E,0xFF,0xFF, +0x50,0x31,0x8F,0xFF,0xFE,0xE6,0xB6,0x65,0x5A,0x08,0x01,0x04,0x84,0x1F,0xD5,0x02, +0x84,0x00,0x14,0x01,0x00,0x0B,0x14,0x01,0x00,0x0C,0x04,0x01,0x00,0x33,0x5A,0x18, +0x01,0x0B,0x58,0x00,0x0F,0xFF,0x14,0x01,0x00,0x33,0x04,0x01,0x00,0x34,0x58,0x00, +0x0F,0xFF,0xD5,0x0B,0x92,0x0C,0x40,0x00,0x30,0x08,0x14,0x01,0x00,0x33,0x04,0x01, +0x00,0x34,0x92,0x0C,0x40,0x00,0x30,0x08,0x14,0x01,0x00,0x34,0xDD,0x9E,0x46,0x10, +0x00,0x83,0x96,0x06,0x50,0x50,0x80,0x38,0xB4,0x85,0x44,0x2F,0xFE,0xFF,0x40,0x30, +0x20,0x08,0xFE,0xA6,0xFE,0x9F,0xB6,0x45,0xB4,0x65,0x44,0x2F,0xFD,0xFF,0x40,0x00, +0x24,0x08,0xFE,0x9E,0xFE,0x17,0xB6,0x05,0xDD,0x9E,0x46,0x20,0x00,0x81,0x50,0x51, +0x00,0x90,0x50,0x31,0x00,0x9A,0x08,0x20,0x00,0x01,0x88,0x41,0x96,0x90,0x18,0x22, +0x80,0x01,0xDB,0xFA,0xDD,0x9E,0xE6,0x25,0xE8,0x33,0x96,0x1F,0x46,0x20,0x00,0x81, +0x44,0xF0,0x12,0x80,0x38,0x17,0x84,0x00,0x40,0xF0,0xBC,0x00,0x4A,0x00,0x3C,0x00, +0x06,0x0E,0x1A,0x28,0x36,0x00,0x04,0x31,0x00,0x23,0x84,0x30,0xD5,0x1D,0x94,0x04, +0x04,0x31,0x00,0x23,0x44,0x1F,0xFF,0x0F,0xD5,0x17,0x40,0x00,0x20,0x08,0x04,0x31, +0x00,0x23,0x44,0x1F,0xF0,0xFF,0xD5,0x10,0x40,0x00,0x30,0x08,0x04,0x31,0x00,0x23, +0x44,0x1F,0x0F,0xFF,0xD5,0x09,0x46,0x1F,0xFF,0x0F,0x40,0x00,0x40,0x08,0x04,0x31, +0x00,0x23,0x50,0x10,0x8F,0xFF,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x23,0xDD,0x9E, +0x46,0x00,0x00,0x81,0x84,0x20,0x04,0x00,0x00,0x12,0x92,0x12,0x96,0x0F,0x40,0x00, +0x80,0x06,0xDD,0x9E,0x46,0x20,0x00,0x81,0x54,0x00,0x03,0xFF,0x40,0x10,0x40,0x08, +0x04,0x31,0x00,0x49,0x46,0x0F,0xC0,0x0F,0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F, +0x14,0x01,0x00,0x49,0xDD,0x9E,0xE6,0x06,0xE8,0x52,0xFC,0x20,0x46,0x70,0x00,0x81, +0x54,0x60,0x83,0xFF,0x44,0xF0,0x13,0x24,0x38,0x07,0x80,0x00,0x40,0xF0,0x3C,0x00, +0x4A,0x00,0x3C,0x00,0x06,0x2E,0x46,0x56,0x66,0x16,0x83,0x87,0xB9,0x30,0x92,0x2A, +0x40,0x10,0xA8,0x08,0xFF,0x8F,0xBE,0xB0,0xFC,0xA0,0x83,0x87,0xB8,0x31,0x46,0x2F, +0xC0,0x0F,0x50,0x21,0x0F,0xFF,0x40,0x63,0x40,0x08,0xFE,0x16,0xFE,0x37,0xB8,0xB1, +0xFC,0xA0,0x83,0x87,0xB8,0x30,0x46,0x2F,0xC0,0x0F,0x50,0x21,0x0F,0xFF,0x40,0x63, +0x40,0x08,0xFE,0x16,0xFE,0x37,0xB8,0xB0,0xFC,0xA0,0x83,0x87,0xB9,0x31,0x92,0x2A, +0x40,0x10,0xA8,0x08,0xFF,0x8F,0xBE,0xB1,0xFC,0xA0,0x83,0x87,0xB9,0x32,0x92,0x2A, +0x40,0x10,0xA8,0x08,0xFF,0x8F,0xBE,0xB2,0xFC,0xA0,0x49,0xFF,0xFC,0x07,0x40,0x13, +0x40,0x08,0x04,0x03,0x83,0x98,0x46,0x6F,0xC0,0x0F,0x50,0x63,0x0F,0xFF,0xFF,0x86, +0xFF,0x8F,0x14,0x63,0x83,0x98,0x49,0xFF,0xFB,0xF2,0xFC,0xA0,0xDD,0x9E,0x54,0x20, +0x80,0x03,0xE6,0x08,0x54,0x10,0x80,0x0C,0x90,0x22,0x4E,0xF2,0x00,0x93,0x96,0x4F, +0x46,0x30,0x00,0x81,0x96,0x8F,0x44,0xF0,0x13,0xD4,0x38,0x07,0x80,0x00,0x40,0xF0, +0x3C,0x00,0xDD,0x0F,0x08,0x24,0x40,0x5E,0x7C,0x9C,0xBC,0xE8,0x50,0x51,0x80,0x9C, +0xB4,0x85,0x84,0x1C,0xFE,0x26,0xFE,0x47,0xB6,0x25,0xB4,0x25,0x44,0x0C,0xFF,0xFF, +0x40,0x21,0x40,0x08,0xFE,0x0E,0xD5,0x72,0x50,0x51,0x80,0x9C,0xB4,0x85,0x84,0x13, +0xFE,0x26,0x94,0x4A,0xFE,0x47,0xB6,0x25,0x40,0x21,0x48,0x08,0xB4,0x05,0x46,0x1F, +0xFF,0x3F,0xD5,0x4E,0x50,0x51,0x80,0x9C,0xB4,0x85,0x44,0x0F,0xFF,0xCF,0xFE,0x26, +0x94,0x4C,0xFE,0x47,0xB6,0x25,0x40,0x21,0x50,0x08,0xB4,0x05,0x46,0x1F,0xFC,0xFF, +0xD5,0x3F,0x50,0x51,0x80,0x9C,0xB4,0x85,0x44,0x0F,0xFF,0x3F,0xFE,0x26,0x94,0x4E, +0xFE,0x47,0xB6,0x25,0x40,0x21,0x58,0x08,0xB4,0x05,0x46,0x1F,0xF3,0xFF,0xD5,0x30, +0x50,0x51,0x80,0x9C,0xB4,0x85,0x44,0x0F,0xFC,0xFF,0xFE,0x26,0x40,0x10,0xA0,0x08, +0xFE,0x47,0xB6,0x25,0x40,0x21,0x60,0x08,0xB4,0x05,0x46,0x1F,0xCF,0xFF,0xD5,0x20, +0x50,0x51,0x80,0x9C,0xB4,0x85,0x44,0x0F,0xF3,0xFF,0xFE,0x26,0x40,0x10,0xA8,0x08, +0xFE,0x47,0xB6,0x25,0x40,0x21,0x68,0x08,0xB4,0x05,0x46,0x1F,0x3F,0xFF,0xD5,0x10, +0x50,0x51,0x80,0x9C,0xB4,0x85,0x44,0x0F,0xCF,0xFF,0xFE,0x26,0x40,0x10,0xB0,0x08, +0xFE,0x47,0xB6,0x25,0x40,0x21,0x70,0x08,0xB4,0x05,0x46,0x1C,0xFF,0xFF,0x50,0x10, +0x8F,0xFF,0xFE,0x0E,0xFE,0x17,0x14,0x01,0x80,0x27,0xDD,0x9E,0x50,0x51,0x80,0x9C, +0xB4,0x85,0x44,0x0F,0x3F,0xFF,0xFE,0x26,0x40,0x10,0xB8,0x08,0xFE,0x47,0xB6,0x25, +0xB4,0x05,0x40,0x21,0x78,0x08,0x94,0x02,0x92,0x02,0xFE,0x87,0x14,0x21,0x80,0x27, +0xDD,0x9E,0xDD,0x9E,0xFC,0x40,0x80,0xE0,0x44,0x04,0x34,0x38,0x5A,0x78,0x01,0x2B, +0x80,0xC1,0xB4,0x20,0x44,0x9F,0xE0,0xFF,0x58,0x10,0x80,0x02,0xB6,0x20,0x80,0x27, +0x49,0xFF,0xFB,0x0F,0x44,0x04,0x34,0x50,0x97,0xA7,0xB4,0x20,0x40,0x63,0x20,0x08, +0x40,0x10,0xA4,0x02,0xFE,0x77,0xB6,0x20,0x80,0x27,0x49,0xFF,0xFB,0x02,0x44,0x04, +0x38,0x3C,0xB4,0x20,0x58,0x10,0x80,0x02,0xB6,0x20,0x80,0x27,0x49,0xFF,0xFA,0xCE, +0x44,0x04,0x38,0x58,0xB4,0x20,0x40,0x10,0xA4,0x02,0xFF,0x8F,0xB6,0xC0,0x80,0x27, +0xD5,0x0E,0xB4,0x20,0x84,0xDD,0xFE,0x76,0xB6,0x20,0x84,0x21,0x49,0xFF,0xFA,0xE9, +0x44,0x04,0x38,0x3C,0xB4,0x20,0xFF,0x8E,0xB6,0xC0,0x84,0x21,0x49,0xFF,0xFA,0xB6, +0xFC,0xC0,0x92,0x00,0xC8,0x04,0x44,0x07,0x20,0x0C,0xD5,0x03,0x44,0x07,0x20,0x10, +0xB4,0x00,0x40,0x00,0x30,0x08,0x92,0x0C,0xDD,0x9E,0xFC,0x00,0x80,0xC1,0x49,0xFF, +0xFF,0xF3,0x84,0x40,0x80,0x22,0xE2,0x46,0xE8,0x05,0x38,0x10,0x0A,0x0A,0x8C,0x41, +0xD5,0xFB,0xFC,0x80,0x44,0x27,0x20,0x1C,0x54,0x00,0x0F,0xFF,0xB4,0x22,0x92,0x2C, +0x40,0x10,0xB0,0x08,0xFE,0x47,0xB6,0x22,0xDD,0x9E,0x44,0x27,0x20,0x34,0x54,0x00, +0x0F,0xFF,0xB4,0x22,0x92,0x2C,0x40,0x10,0xB0,0x08,0xFE,0x47,0xB6,0x22,0xDD,0x9E, +0x44,0x27,0x20,0x38,0x54,0x00,0x0F,0xFF,0xB4,0x22,0x92,0x2C,0x40,0x10,0xB0,0x08, +0xFE,0x47,0xB6,0x22,0xDD,0x9E,0x44,0x27,0x20,0x54,0x84,0x30,0xB4,0x62,0x96,0x1F, +0xFE,0x5E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x27,0x20,0x54,0x96,0x1F,0xB4,0x62, +0x40,0x10,0x20,0x08,0x44,0x0F,0xF0,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E, +0x44,0x27,0x20,0x58,0x96,0x1F,0x40,0x10,0x40,0x08,0xB4,0x62,0x46,0x0F,0xFF,0x0F, +0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x04,0x00,0x9C, +0xA6,0x00,0xDD,0x9E,0x46,0x10,0x00,0x81,0x04,0x00,0x80,0x6E,0x58,0x00,0x00,0x01, +0x14,0x00,0x80,0x6E,0xDD,0x9E,0xFC,0x00,0x44,0x04,0x2C,0x1C,0x84,0x41,0xB4,0x20, +0x80,0x62,0x58,0x10,0x81,0x00,0xB6,0x20,0xB4,0x20,0x58,0x10,0x80,0x80,0xB6,0x20, +0x44,0x04,0x28,0x1C,0x44,0x14,0x24,0x1C,0x49,0xFF,0xFA,0x8E,0xFC,0x80,0x46,0x20, +0x00,0x80,0x84,0x30,0x50,0x41,0x01,0xD0,0xB4,0x64,0x96,0x1F,0xFE,0x5E,0xFE,0x47, +0xB6,0x24,0xB4,0x64,0x44,0x1F,0xFF,0x0F,0x94,0x04,0xFE,0x5E,0xFE,0x0F,0xB6,0x04, +0xDD,0x9E,0x00,0x00,0x44,0x24,0x80,0x88,0x84,0x3E,0xB4,0x62,0x96,0x06,0xFE,0x5E, +0xFE,0x47,0xB6,0x22,0xB4,0x62,0x84,0x3D,0x94,0x01,0xFE,0x5E,0xFE,0x0F,0xB6,0x02, +0xDD,0x9E,0xFC,0x00,0x44,0x64,0x00,0x18,0x84,0x21,0xAE,0x70,0xA6,0x32,0x44,0x20, +0x98,0x81,0x58,0x00,0x00,0x01,0xAE,0x32,0xA6,0x32,0x58,0x00,0x00,0x02,0xAE,0x32, +0xA6,0x32,0x58,0x00,0x00,0x04,0xAE,0x32,0xA6,0x33,0x58,0x00,0x00,0x02,0xAE,0x33, +0xA6,0x33,0x58,0x00,0x00,0x04,0xAE,0x33,0xA6,0x33,0x58,0x00,0x00,0x08,0xAE,0x33, +0xA6,0x33,0x58,0x00,0x00,0x10,0xAE,0x33,0x44,0x04,0x00,0x10,0xB6,0x40,0xAE,0x71, +0xA6,0x32,0x58,0x00,0x00,0x10,0xAE,0x32,0xA6,0x32,0x58,0x00,0x00,0x20,0xAE,0x32, +0xA6,0x72,0x44,0x0F,0xFF,0x80,0xFE,0x0F,0xAE,0x32,0xA6,0x33,0x54,0x00,0x00,0xFE, +0xAE,0x33,0xA6,0x33,0x58,0x00,0x00,0x20,0xAE,0x33,0xA6,0x33,0x58,0x00,0x00,0x40, +0xAE,0x33,0x49,0x00,0x2E,0x12,0xE6,0x03,0x44,0x14,0x50,0x10,0xA6,0x32,0xE9,0x10, +0x54,0x00,0x00,0xFB,0xAE,0x32,0xB4,0x01,0x92,0x08,0x40,0x00,0x20,0x08,0xB6,0x01, +0x44,0x14,0x70,0x00,0x44,0x0E,0xFF,0xFF,0xB4,0x41,0xFE,0x16,0xD5,0x11,0x58,0x00, +0x00,0x04,0xAE,0x32,0xB4,0x01,0x92,0x08,0x40,0x00,0x20,0x08,0x58,0x00,0x00,0x98, +0xB6,0x01,0x44,0x14,0x70,0x00,0x44,0x01,0x00,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01, +0xFC,0x80,0x44,0x37,0x20,0x44,0x46,0x0F,0xC0,0x0F,0xB4,0x23,0x50,0x00,0x0F,0xFF, +0xFE,0x46,0x46,0x20,0x24,0x00,0xFE,0x57,0xB6,0x23,0x44,0x37,0x20,0x48,0x44,0x23, +0x00,0x00,0xB4,0x23,0xFE,0x46,0xFE,0x57,0x44,0x27,0x20,0x4C,0xB6,0x23,0xB4,0x22, +0x46,0x30,0x0C,0x00,0x92,0x2C,0x40,0x10,0xB0,0x08,0x58,0x10,0x82,0x52,0xB6,0x22, +0xB4,0x22,0xFE,0x46,0xFE,0x5F,0xB6,0x22,0x44,0x27,0x20,0x50,0x46,0x30,0x00,0x90, +0xB4,0x22,0x92,0x2C,0x40,0x10,0xB0,0x08,0x58,0x10,0x82,0x40,0xB6,0x22,0xB4,0x22, +0xFE,0x0E,0xB6,0x02,0x44,0x27,0x20,0x04,0x46,0x0F,0xFF,0x0F,0xB4,0x22,0x50,0x00, +0x0F,0xFF,0xFE,0x46,0xFE,0x5F,0xB6,0x22,0xB4,0x62,0x46,0x1F,0xF0,0xFF,0x50,0x10, +0x8F,0xFF,0xFE,0x5E,0x46,0x30,0x03,0x00,0xFE,0x5F,0xB6,0x22,0x46,0x20,0x00,0x80, +0x44,0x1F,0xF0,0xFF,0x50,0x41,0x01,0xC0,0xB4,0x64,0xFE,0x5E,0x58,0x10,0x88,0x00, +0xB6,0x24,0xB4,0x24,0xFE,0x0E,0x44,0x12,0x00,0x00,0xFE,0x0F,0xB6,0x04,0xDD,0x9E, +0x46,0x20,0x00,0x91,0x40,0x00,0x40,0x08,0xA0,0x56,0x96,0x49,0xFE,0x0F,0xA8,0x16, +0xDD,0x9E,0xE6,0x04,0xE8,0x28,0x46,0x10,0x00,0x91,0x44,0xF0,0x18,0x48,0x38,0x07, +0x80,0x00,0x40,0xF0,0x3C,0x00,0xDD,0x0F,0x04,0x12,0x20,0x2E,0xA0,0x0A,0x92,0x10, +0x40,0x00,0x40,0x08,0x58,0x00,0x00,0x0A,0xD5,0x15,0xA0,0x0A,0x92,0x10,0x40,0x00, +0x40,0x08,0x58,0x00,0x00,0x46,0xD5,0x0E,0xA0,0x0A,0x92,0x10,0x40,0x00,0x40,0x08, +0x58,0x00,0x00,0x96,0xD5,0x07,0xA0,0x0A,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00, +0x01,0xF4,0xA8,0x0A,0xDD,0x9E,0x46,0x2F,0xFC,0x0F,0x46,0x10,0x00,0x91,0x50,0x21, +0x0F,0xFF,0x5A,0x08,0x01,0x08,0x04,0x00,0x80,0x0E,0xFE,0x86,0x44,0x02,0x00,0x00, +0xD5,0x06,0x04,0x00,0x80,0x0E,0xFE,0x86,0x44,0x01,0x00,0x00,0xFE,0x87,0x14,0x20, +0x80,0x0E,0xA0,0x09,0x92,0x06,0x94,0x06,0x58,0x00,0x00,0x01,0xA8,0x09,0xDD,0x9E, +0xFC,0x00,0x46,0x60,0x00,0x91,0x84,0x01,0xA8,0x31,0x44,0x00,0x01,0xF4,0xA8,0x32, +0x83,0x86,0xB9,0x0A,0x46,0x0F,0xC0,0x0F,0x50,0x00,0x0C,0x00,0xFE,0x46,0x58,0x10, +0x80,0x01,0xB9,0x8A,0xB9,0x0B,0xFE,0x0E,0x58,0x00,0x02,0x40,0xB8,0x8B,0xB9,0x0C, +0x44,0x0F,0xC0,0xC0,0xFE,0x0E,0x58,0x00,0x20,0x01,0xB8,0x8C,0xB9,0x0D,0x46,0x0E, +0x0E,0x0E,0x50,0x00,0x00,0xE0,0xFE,0x0E,0x46,0x10,0x60,0x11,0x50,0x10,0x82,0x01, +0xFE,0x0F,0xB8,0x8D,0xB9,0x0E,0x46,0x0F,0xFC,0x0C,0x50,0x00,0x00,0xF3,0xFE,0x0E, +0x44,0x11,0x01,0x0C,0xFE,0x0F,0xB8,0x8E,0x49,0xFF,0xFE,0x7A,0xE6,0x03,0xA0,0x36, +0x92,0x10,0x40,0x00,0x40,0x08,0xE9,0x1F,0x58,0x00,0x00,0x64,0xA8,0x36,0x83,0x86, +0xB8,0x08,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00,0x00,0x64,0xB8,0x88,0xA0,0x35, +0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00,0x00,0x64,0xA8,0x35,0xA0,0x37,0x92,0x10, +0x40,0x00,0x40,0x08,0x58,0x00,0x00,0x64,0xA8,0x37,0xA0,0x76,0x46,0x00,0x06,0x40, +0x96,0x49,0xD5,0x1E,0x58,0x00,0x46,0x00,0xA8,0x36,0x83,0x86,0xB8,0x08,0x92,0x10, +0x40,0x00,0x40,0x08,0x58,0x00,0x46,0x00,0xB8,0x88,0xA0,0x35,0x92,0x10,0x40,0x00, +0x40,0x08,0x58,0x00,0x46,0x00,0xA8,0x35,0xA0,0x37,0x92,0x10,0x40,0x00,0x40,0x08, +0x58,0x00,0x46,0x00,0xA8,0x37,0xA0,0x76,0x46,0x04,0x60,0x00,0x96,0x49,0xFE,0x47, +0xA8,0x76,0x83,0x86,0xB9,0x08,0x96,0x49,0xFE,0x47,0xB9,0x88,0xA0,0x75,0x96,0x49, +0xFE,0x47,0xA8,0x75,0xA0,0x77,0x96,0x49,0xFE,0x0F,0xA8,0x37,0x46,0x00,0x00,0x91, +0x83,0x80,0xB9,0x12,0x58,0x10,0xBF,0x3F,0xB9,0x92,0xBA,0x13,0x44,0x1E,0xE0,0x80, +0xFE,0x56,0x44,0x21,0x00,0x48,0xFE,0x57,0xB9,0x93,0xFC,0x80,0x46,0x00,0x00,0x91, +0x50,0x30,0x00,0x44,0xB4,0x23,0x58,0x10,0x80,0x01,0xB6,0x23,0xB4,0x43,0x84,0x3E, +0xFE,0x56,0xB6,0x23,0xDD,0x9E,0x92,0x00,0x44,0x17,0x30,0x10,0xB4,0x01,0x96,0x06, +0xC0,0xFE,0x44,0x17,0x30,0x10,0xB4,0x01,0x92,0x03,0x96,0x06,0x5A,0x00,0x01,0xFD, +0xDD,0x9E,0x84,0xA0,0xD0,0x05,0x40,0x00,0x00,0x09,0x8C,0xA1,0xD5,0xFC,0xDD,0x9E, +0x44,0x27,0x30,0x10,0xB4,0x22,0x92,0x21,0x96,0x46,0x5A,0x10,0x01,0xFD,0x44,0x17, +0x30,0x00,0xB6,0x01,0xDD,0x9E,0xFC,0x00,0x44,0x04,0x70,0x18,0x84,0x3B,0xB4,0x40, +0xFE,0x56,0xB6,0x20,0x40,0x00,0x00,0x09,0x40,0x00,0x00,0x09,0xB4,0x20,0x46,0x61, +0xFF,0xF9,0x58,0x10,0x80,0x02,0xB6,0x20,0x44,0x04,0x80,0x0A,0x84,0x24,0xAE,0x40, +0x44,0x04,0x80,0x88,0x44,0x14,0x00,0x00,0xB4,0x40,0xFE,0x57,0xB6,0x20,0xB4,0x40, +0x44,0x1D,0xFF,0xFF,0xFE,0x56,0xB6,0x20,0x44,0x17,0x30,0x08,0xB4,0x01,0x92,0x05, +0x94,0x05,0x58,0x00,0x00,0x01,0xB6,0x01,0x50,0x03,0x05,0x27,0x49,0xFF,0xFF,0xCA, +0x50,0x03,0x04,0x87,0x46,0x61,0xFF,0xFF,0x49,0xFF,0xFF,0xC4,0x50,0x03,0x03,0xCC, +0x49,0xFF,0xFF,0xC0,0x80,0x06,0x49,0xFF,0xFF,0xBD,0x50,0x03,0x0F,0x06,0x49,0xFF, +0xFF,0xB9,0x49,0xFF,0xFF,0xA3,0x46,0x01,0xF0,0x40,0x50,0x00,0x01,0x50,0x49,0xFF, +0xFF,0xB1,0x49,0xFF,0xFF,0x9B,0x46,0x01,0xF0,0x90,0x50,0x00,0x01,0x08,0x49,0xFF, +0xFF,0xA9,0x49,0xFF,0xFF,0x93,0xFC,0x80,0xC9,0x08,0x44,0x24,0x00,0x0C,0x96,0x06, +0xA6,0x50,0x54,0x10,0x80,0xFE,0xD5,0x0A,0x5A,0x18,0x01,0x0B,0x44,0x24,0x00,0x0C, +0x96,0x06,0xA6,0x50,0x94,0x01,0x54,0x10,0x80,0xFD,0xFE,0x0F,0xAE,0x10,0xDD,0x9E, +0xC9,0x04,0x44,0x15,0x00,0x00,0xD5,0x05,0x5A,0x18,0x01,0x05,0x44,0x15,0x00,0x10, +0xAE,0x0A,0xDD,0x9E,0xC9,0x0A,0x44,0x24,0x80,0x80,0x96,0x06,0x40,0x10,0x60,0x08, +0xB4,0x62,0x46,0x0F,0xEF,0xFF,0xD5,0x0B,0x5A,0x18,0x01,0x0F,0x44,0x24,0x80,0x80, +0x96,0x06,0x40,0x10,0x64,0x08,0xB4,0x62,0x46,0x0F,0xDF,0xFF,0x50,0x00,0x0F,0xFF, +0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0xC9,0x04,0x44,0x15,0x00,0x04,0xD5,0x05, +0x5A,0x18,0x01,0x05,0x44,0x15,0x00,0x14,0xAC,0x08,0xDD,0x9E,0xC8,0x08,0x44,0x14, +0x80,0x03,0x84,0x01,0xAE,0x08,0x44,0x15,0x00,0x08,0xD5,0x09,0x5A,0x08,0x01,0x09, +0x44,0x14,0x80,0x03,0x84,0x42,0xAE,0x88,0x44,0x15,0x00,0x18,0xAE,0x09,0xDD,0x9E, +0xC8,0x08,0x44,0x04,0x80,0x03,0x84,0x21,0xAE,0x40,0x44,0x15,0x00,0x08,0xD5,0x09, +0x5A,0x08,0x01,0x0C,0x44,0x04,0x80,0x03,0x84,0x22,0xAE,0x40,0x44,0x15,0x00,0x18, +0xA6,0x08,0x58,0x00,0x00,0x02,0xAE,0x08,0xDD,0x9E,0xC9,0x04,0x44,0x15,0x00,0x00, +0xD5,0x05,0x5A,0x18,0x01,0x05,0x44,0x15,0x00,0x10,0xAE,0x08,0xDD,0x9E,0x84,0x21, +0xC0,0x04,0x5A,0x08,0x01,0x06,0x84,0x22,0x44,0x04,0x80,0x03,0xAE,0x40,0xDD,0x9E, +0x44,0x04,0x80,0x00,0xA6,0x03,0x92,0x01,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x03, +0x84,0x22,0xAE,0x40,0xDD,0x9E,0x46,0x20,0x00,0x81,0x84,0x3E,0xA0,0xD5,0x96,0x06, +0xFE,0x5E,0xFE,0x0F,0xA8,0x15,0xDD,0x9E,0x46,0x00,0x00,0x81,0xA0,0x05,0x96,0x06, +0xDD,0x9E,0x44,0x24,0x50,0x14,0x84,0x3E,0xB4,0x62,0x96,0x06,0xFE,0x5E,0xFE,0x0F, +0xB6,0x02,0xDD,0x9E,0x44,0x25,0x10,0x04,0xB4,0x22,0x92,0x30,0x40,0x10,0xC0,0x08, +0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x15,0x10,0x0C,0xB4,0x01,0x58,0x00,0x01,0x00, +0xB6,0x01,0xDD,0x9E,0x44,0x15,0x10,0x0C,0xB4,0x01,0x92,0x08,0x40,0x00,0x20,0x08, +0x58,0x00,0x00,0x01,0xB6,0x01,0x44,0x15,0x10,0x18,0xB4,0x01,0x96,0x00,0x5A,0x00, +0x5A,0xFE,0xDD,0x9E,0xFC,0x00,0x44,0x00,0x01,0x2C,0x49,0x00,0x00,0x6B,0x44,0x05, +0x10,0x0C,0x44,0x1F,0xFF,0x81,0xAE,0x40,0x44,0x1F,0xFF,0x98,0xAE,0x40,0x44,0x15, +0x10,0x18,0xB4,0x01,0x96,0x00,0x5A,0x00,0xA5,0xFE,0x44,0x15,0x10,0x0C,0xB4,0x01, +0x92,0x08,0x40,0x00,0x20,0x08,0xB6,0x01,0xFC,0x80,0xC0,0x04,0x5A,0x00,0x01,0x1F, +0xDD,0x9E,0x44,0x14,0x80,0x82,0x44,0x22,0x00,0x00,0xAE,0x08,0x44,0x14,0x80,0x00, +0x44,0x04,0x80,0x80,0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x44,0x24,0x00,0x00, +0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x46,0x20,0x01,0x00,0xB6,0x41,0xB4,0x60, +0xFE,0x9F,0xB6,0x40,0x46,0x20,0x00,0x80,0xD5,0x23,0x44,0x04,0x80,0x82,0x84,0x20, +0xAE,0x40,0x44,0x14,0x80,0x00,0x44,0x04,0x80,0x80,0x44,0x21,0x00,0x00,0xB6,0x41, +0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x44,0x22,0x00,0x00,0xB6,0x41,0xB4,0x60,0xFE,0x9F, +0xB6,0x40,0x44,0x24,0x00,0x00,0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x46,0x20, +0x00,0x80,0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x46,0x20,0x01,0x00,0xB6,0x41, +0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x46,0x20,0x02,0x00,0xB6,0x41,0xB4,0x60,0xFE,0x9F, +0xB6,0x40,0x46,0x20,0x04,0x00,0xB6,0x41,0xB4,0x20,0xFE,0x8F,0xB6,0x40,0xDD,0x9E, +0x84,0x2E,0xFE,0x0C,0x8E,0x01,0x5A,0x07,0xFF,0x05,0x40,0x00,0x00,0x09,0xD5,0xFB, +0xDD,0x9E,0x44,0x10,0x36,0xB0,0xFE,0x0C,0x8E,0x01,0x5A,0x07,0xFF,0x05,0x40,0x00, +0x00,0x09,0xD5,0xFB,0xDD,0x9E,0xFF,0xFF,0xFC,0x00,0x49,0x00,0x18,0x39,0xC8,0x05, +0x84,0x01,0x3E,0x07,0xEF,0x94,0xFC,0x80,0x84,0x02,0x3E,0x07,0xEF,0x94,0x49,0x00, +0x0B,0x25,0xFC,0x80,0xFC,0x00,0x84,0x00,0x49,0x00,0x0F,0x0D,0x84,0x00,0x44,0x12, +0x00,0xDC,0x49,0x00,0x0F,0x29,0x84,0x00,0x84,0x27,0x49,0x00,0x0F,0x87,0xFC,0x80, +0xFC,0x00,0xC0,0x0B,0x5A,0x08,0x01,0x05,0x49,0x00,0x16,0xE7,0xD5,0x06,0x84,0xC0, +0x5A,0x08,0x02,0x05,0x49,0x00,0x16,0xE4,0x80,0xC0,0x84,0x02,0x49,0x00,0x0E,0xF3, +0x84,0x02,0x44,0x12,0x00,0xDC,0x49,0x00,0x0F,0x0F,0xC6,0x04,0x80,0x06,0x49,0x00, +0x0F,0x48,0x49,0x00,0x16,0xDB,0x49,0x00,0x0F,0x56,0x84,0x02,0x84,0x27,0x49,0x00, +0x0F,0x65,0xFC,0x80,0xFC,0x01,0xF0,0x81,0x49,0x00,0x0E,0xD7,0x80,0xC0,0xC8,0x05, +0xF0,0x01,0x49,0xFF,0xFF,0xC9,0xD5,0x08,0x5A,0x00,0x01,0x0C,0x49,0x00,0x0E,0xD0, +0xF1,0x01,0x49,0xFF,0xFF,0xCF,0x80,0x06,0x49,0x00,0x0E,0xA6,0x49,0x00,0x0E,0xAD, +0xFC,0x81,0xFC,0x00,0x49,0x00,0x0C,0x89,0x5A,0x08,0x01,0x06,0x84,0x00,0x44,0x10, +0x13,0x88,0xD5,0x04,0x84,0x00,0x44,0x10,0x75,0x30,0x49,0x00,0x2B,0x01,0x84,0x00, +0x84,0x21,0x49,0x00,0x2B,0x0B,0x84,0x00,0x49,0x00,0x0C,0x80,0xFC,0x80,0xFC,0x00, +0x64,0x12,0x00,0x43,0x64,0x00,0x00,0x08,0x64,0x00,0x00,0x08,0x49,0x00,0x28,0xBB, +0x49,0x00,0x28,0xD2,0x49,0x00,0x2A,0x8C,0x84,0x00,0x44,0x10,0x00,0xEB,0x49,0x00, +0x2A,0xCC,0x49,0x00,0x29,0xDF,0x49,0x00,0x29,0xF4,0x49,0x00,0x2A,0x27,0x49,0x00, +0x17,0x14,0x49,0x00,0x0E,0x37,0x84,0x00,0x3E,0x07,0xEF,0x95,0xFC,0x80,0x2E,0x07, +0xF0,0xE7,0xDD,0x9E,0xFC,0x00,0x49,0x00,0x09,0xF5,0x49,0x00,0x07,0x8E,0x49,0x00, +0x18,0x3F,0x49,0x00,0x17,0xA5,0x49,0x00,0x0A,0x8B,0x49,0x00,0x16,0x96,0x49,0x00, +0x19,0x15,0x84,0x00,0x49,0xFF,0xF3,0x3C,0x49,0x00,0x17,0x9A,0x49,0x00,0x0A,0xCE, +0x49,0x00,0x1A,0x56,0x44,0x02,0x1E,0x64,0x84,0x20,0x44,0x20,0x00,0x90,0x49,0xFF, +0xF2,0x07,0x44,0x10,0x02,0x40,0x44,0x20,0x00,0x90,0x44,0x02,0x1C,0x24,0x49,0xFF, +0xF1,0xFF,0x49,0x00,0x29,0xA7,0x49,0x00,0x17,0x83,0x49,0x00,0x0A,0xB7,0x49,0x00, +0x0C,0x24,0xC8,0x03,0x49,0x00,0x1A,0x3C,0x49,0xFF,0xFF,0x40,0x49,0x00,0x1A,0xA6, +0x49,0xFF,0xFF,0x91,0x49,0x00,0x00,0xB5,0x49,0x00,0x0C,0x17,0x80,0xC0,0x5A,0x08, +0x01,0x0A,0x44,0x00,0x02,0x58,0x80,0x26,0x49,0xFF,0xFE,0x28,0x80,0x06,0x49,0xFF, +0xFE,0x2F,0x84,0x04,0x3E,0x07,0xF0,0xEA,0xFC,0x80,0xFC,0x00,0x84,0x00,0x49,0x00, +0x18,0x8D,0x84,0x00,0x49,0x00,0x00,0x9E,0x49,0x00,0x16,0x45,0x49,0x00,0x13,0x84, +0xFC,0x80,0xFC,0x40,0x49,0x00,0x0E,0x31,0x80,0xE0,0x49,0x00,0x0E,0xBB,0x5A,0x08, +0x01,0x05,0x84,0x20,0x49,0x00,0x2A,0x82,0x84,0x01,0x49,0x00,0x2A,0x9A,0x80,0xC0, +0x5A,0x08,0x01,0x08,0x84,0x20,0x49,0x00,0x2A,0x79,0x80,0x06,0x49,0x00,0x0E,0xAE, +0x2E,0x07,0xF0,0xE6,0x5A,0x08,0x01,0x31,0x49,0x00,0x0E,0xA4,0x80,0xC0,0x5A,0x08, +0x01,0x2C,0x85,0x20,0x84,0x00,0x3E,0x97,0xF0,0xE6,0x49,0x00,0x0E,0x9F,0x84,0x00, +0x3E,0x97,0xF0,0xE4,0x49,0xFF,0xFF,0x30,0x80,0x06,0x84,0x28,0x5A,0x78,0x02,0x04, +0x44,0x10,0x01,0x90,0x49,0x00,0x2A,0x4C,0x84,0x01,0x80,0x20,0x49,0x00,0x2A,0x56, +0x5A,0x78,0x02,0x1C,0x49,0x00,0x0E,0x86,0x80,0xC0,0xC8,0x17,0x84,0x01,0x49,0x00, +0x2A,0x68,0x80,0xE0,0x5A,0x08,0x01,0xF8,0x80,0x26,0x49,0x00,0x2A,0x47,0x80,0x07, +0x49,0x00,0x0E,0x7C,0xD5,0x0A,0x2E,0x67,0xF0,0xE6,0x5A,0x68,0x01,0x07,0x49,0x00, +0x0E,0x71,0xC8,0x03,0x3E,0x67,0xF0,0xE4,0x2E,0x07,0xF0,0xEA,0xC0,0x04,0x5A,0x00, +0x04,0x06,0xFC,0xC0,0x49,0xFF,0xFF,0x50,0xFC,0xC0,0x49,0xFF,0xFF,0x98,0xFC,0xC0, +0xFC,0x20,0x84,0xC0,0x84,0xE2,0x49,0x00,0x29,0xFC,0x2E,0x07,0xF0,0xE7,0x5A,0x00, +0x01,0x08,0xC0,0x04,0x5A,0x00,0x02,0x1E,0xD5,0xF7,0x84,0x01,0xD5,0x12,0x3E,0x07, +0xF0,0xE9,0x3E,0x67,0xF0,0xEA,0x3E,0x67,0xF0,0xE8,0x3E,0x67,0xF0,0xE5,0x49,0x00, +0x0B,0x84,0x5A,0x08,0x01,0x0C,0x49,0x00,0x0B,0x86,0x5A,0x08,0x02,0x06,0x84,0x03, +0x3E,0x07,0xF0,0xE7,0xD5,0xE1,0x84,0x04,0xD5,0xFC,0x3E,0x77,0xF0,0xE7,0xD5,0xDC, +0x49,0x00,0x0B,0x5A,0x49,0x00,0x0B,0x61,0x2E,0x07,0xF0,0xE5,0x3E,0x67,0xF0,0xEB, +0xC8,0xD3,0x49,0xFF,0xFF,0x70,0xD5,0xD0,0x3E,0x07,0xF0,0xE9,0xDD,0x9E,0xDD,0x9E, +0x46,0x10,0x00,0x81,0xC8,0x06,0x04,0x00,0x80,0x33,0x58,0x00,0x02,0x00,0xD5,0x06, +0x04,0x20,0x80,0x33,0x44,0x0F,0xFD,0xFF,0xFE,0x16,0x14,0x00,0x80,0x33,0xDD,0x9E, +0x44,0x24,0x2C,0x14,0x84,0x30,0xB4,0x62,0x96,0x1F,0xFE,0x5E,0xFE,0x0F,0xB6,0x02, +0xDD,0x9E,0x44,0x24,0x2C,0x14,0x96,0x1F,0xB4,0x62,0x40,0x10,0x20,0x08,0x44,0x0F, +0xF0,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0xFC,0x20,0x80,0xC0,0x49,0x00, +0x03,0x9B,0x49,0x00,0x03,0x7F,0x44,0x14,0x24,0x04,0x44,0x5F,0xFF,0xEF,0xB4,0x01, +0x84,0x41,0xFE,0x2E,0xB6,0x01,0xB4,0x01,0x40,0x33,0x08,0x02,0x44,0x4F,0xFF,0xDF, +0x94,0xDD,0xFE,0x26,0xFE,0x1F,0xB6,0x01,0x44,0x04,0x28,0x04,0xB4,0xC0,0xFF,0xAE, +0xB6,0xC0,0xB4,0xC0,0xFF,0xA6,0xFF,0x9F,0xB6,0xC0,0x44,0x64,0x2C,0x04,0xB4,0xE6, +0xFF,0x7E,0xB6,0xA6,0xB4,0xA6,0xFF,0x2E,0xFE,0xE7,0xB6,0x66,0x80,0x62,0x49,0xFF, +0xF5,0x3B,0x49,0x00,0x03,0x64,0x49,0x00,0x03,0x7C,0xFC,0xA0,0x46,0x20,0x00,0x81, +0x96,0x0F,0x04,0x31,0x00,0x12,0x40,0x10,0x40,0x08,0x44,0x0C,0xFF,0xFF,0xFE,0x1E, +0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E,0xFC,0x00,0x44,0x24,0x80,0x10,0x84,0x01, +0xB4,0x22,0x96,0x46,0xC1,0x0B,0xAE,0x10,0x3E,0x07,0xF0,0xFB,0x3E,0x07,0xF0,0xF8, +0x3C,0x0D,0xFC,0x3C,0x8C,0x01,0x3C,0x0F,0xFC,0x3C,0x44,0x04,0x80,0x10,0xB4,0x00, +0x92,0x0A,0x96,0x06,0xC0,0x16,0x44,0x04,0x80,0x11,0x84,0x24,0xAE,0x40,0x46,0x00, +0x00,0x81,0x04,0x00,0x00,0x1E,0x92,0x04,0x96,0x0F,0x3E,0x07,0xF8,0x40,0x49,0x00, +0x01,0x03,0x44,0x07,0x20,0x00,0xB4,0x00,0x92,0x08,0x96,0x06,0x49,0x00,0x00,0xE7, +0x44,0x14,0x80,0x10,0x84,0x41,0xB4,0x01,0x92,0x08,0x96,0x06,0xC0,0x06,0x44,0x04, +0x80,0x11,0xAE,0x80,0x3E,0x27,0xF0,0xF9,0xB4,0x01,0x92,0x07,0x96,0x06,0xC0,0x06, +0x44,0x04,0x80,0x10,0x44,0x1F,0xFF,0x80,0xAE,0x40,0x44,0x04,0x80,0x10,0xB4,0x20, +0x92,0x25,0x96,0x46,0xC1,0x03,0xFA,0x30,0xAE,0x40,0xB4,0x00,0x92,0x06,0x96,0x06, +0xC0,0x06,0x44,0x04,0x80,0x10,0x44,0x10,0x00,0x40,0xAE,0x40,0x44,0x14,0x80,0x10, +0xB4,0x01,0x92,0x18,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x13,0x84,0x41,0xAE,0x80, +0xB4,0x01,0x92,0x15,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x12,0xFA,0x30,0xAE,0x40, +0xFC,0x80,0xFC,0x00,0x44,0x24,0x80,0x10,0x84,0x21,0xB4,0x02,0x92,0x04,0x96,0x06, +0xC0,0x14,0xFA,0x00,0xAE,0x10,0x3E,0x17,0xF0,0xFA,0x3E,0x17,0xF0,0xF8,0x3C,0x0D, +0xFC,0x3D,0x8C,0x01,0x3C,0x0F,0xFC,0x3D,0x46,0x00,0x00,0x81,0x04,0x00,0x00,0xF0, +0x92,0x03,0x96,0x06,0x49,0x00,0x00,0x93,0x44,0x14,0x80,0x10,0xB4,0x01,0x92,0x0B, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x11,0x84,0x48,0xAE,0x80,0xB4,0x01,0x92,0x03, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x10,0x84,0x28,0xAE,0x40,0x44,0x04,0x80,0x10, +0xB4,0x20,0x92,0x22,0x96,0x46,0xC1,0x03,0x84,0x24,0xAE,0x40,0xB4,0x00,0x92,0x01, +0x96,0x06,0xC0,0x0E,0x44,0x04,0x80,0x90,0xB4,0x00,0x92,0x01,0x96,0x06,0x5A,0x08, +0x01,0x08,0x44,0x14,0x80,0x10,0x84,0x42,0xAE,0x88,0x3E,0x07,0xF0,0xEB,0x44,0x14, +0x80,0x10,0xB4,0x01,0x92,0x19,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x13,0x84,0x42, +0xAE,0x80,0xB4,0x01,0x92,0x16,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x12,0x44,0x10, +0x00,0x40,0xAE,0x40,0xFC,0x80,0xFC,0x00,0x80,0xC0,0x49,0xFF,0xF4,0x77,0x46,0x20, +0x00,0x81,0x97,0x86,0x94,0x32,0x04,0x61,0x03,0x93,0x84,0x3B,0xFF,0x8E,0xFF,0x87, +0x14,0x61,0x03,0x93,0x49,0xFF,0xF4,0x63,0xFC,0x80,0x44,0x04,0x50,0x28,0xB4,0x00, +0x92,0x01,0x96,0x06,0xDD,0x9E,0x44,0x24,0x50,0x1C,0x40,0x00,0x40,0x08,0xB4,0x22, +0x96,0x49,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x24,0x50,0x1C,0x44,0x1F,0x00,0xFF, +0xB4,0x62,0x40,0x00,0x20,0x08,0xFE,0x5E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x07, +0x21,0x00,0x46,0x10,0x20,0x00,0xB4,0x40,0xFE,0x57,0xB6,0x20,0x80,0x20,0xB4,0x01, +0x92,0x19,0x96,0x06,0xC8,0xFD,0xDD,0x9E,0x44,0x04,0x80,0x04,0xA6,0x02,0x92,0x01, +0x96,0x06,0xC0,0x08,0x44,0x04,0x80,0x06,0x84,0x22,0xAE,0x40,0x84,0x01,0x3E,0x07, +0xF0,0xFD,0x44,0x04,0x80,0x04,0x84,0x21,0xA6,0x02,0x96,0x06,0xC0,0x06,0x44,0x04, +0x80,0x06,0xAE,0x40,0x3E,0x17,0xF0,0xFC,0xDD,0x9E,0x3E,0x07,0xEF,0x96,0xDD,0x9E, +0x2E,0x07,0xEF,0x96,0xDD,0x9E,0x44,0x37,0x20,0x00,0x54,0x10,0x00,0x01,0xB4,0x83, +0x40,0x20,0xA0,0x08,0x44,0x1F,0xFE,0xFF,0xFE,0x66,0xFE,0x57,0xB6,0x23,0x3E,0x07, +0xEF,0x96,0xDD,0x9E,0x84,0x01,0x3E,0x07,0xEF,0x97,0xDD,0x9E,0x2E,0x07,0xEF,0x97, +0xDD,0x9E,0x84,0x00,0x3E,0x07,0xEF,0x97,0xDD,0x9E,0x3C,0x0D,0xFC,0x44,0xC0,0x0B, +0xA4,0x80,0x3C,0x13,0xF8,0x84,0x8E,0x21,0xE0,0x41,0xE8,0x05,0xA4,0x40,0x8C,0x21, +0x96,0x49,0xAC,0x40,0xA4,0x80,0x3C,0x13,0xF8,0x86,0xE2,0x41,0xE9,0x05,0x84,0x01, +0x3E,0x07,0xEF,0x98,0xD5,0x0B,0x3C,0x1D,0xFC,0x46,0xC1,0x08,0xA4,0x00,0x88,0x01, +0x44,0x16,0x00,0x34,0xA6,0x00,0x96,0x00,0xB6,0x01,0x44,0x06,0x00,0x00,0xB4,0x00, +0x92,0x08,0x96,0x06,0x5A,0x08,0x01,0x0B,0x44,0x06,0x00,0x30,0xB4,0x20,0x58,0x10, +0x80,0x01,0xB6,0x20,0xB4,0x20,0x96,0x46,0xC9,0xFE,0xDD,0x9E,0xFC,0x40,0x49,0xFF, +0xF9,0x17,0xE6,0x03,0xE8,0x0F,0x3C,0x0D,0xFC,0x44,0x84,0x20,0xAC,0x40,0x3C,0x1D, +0xFC,0x46,0xC1,0x08,0xA4,0x00,0x88,0x01,0x44,0x16,0x00,0x34,0xA6,0x00,0x96,0x00, +0xB6,0x01,0x85,0x40,0x44,0x74,0x80,0x00,0x44,0x94,0x80,0x80,0xA7,0xBA,0xB4,0x09, +0x92,0xC3,0x92,0x13,0x96,0x06,0xFF,0x86,0xC6,0x1A,0x44,0x04,0x80,0x02,0x84,0x28, +0xAE,0x40,0x49,0xFF,0xF8,0xF5,0xE6,0x03,0xE8,0x4D,0x44,0x06,0x00,0x00,0xB4,0x00, +0x92,0x08,0x96,0x06,0x5A,0x08,0x01,0x47,0x44,0x06,0x00,0x30,0xB4,0x20,0x58,0x10, +0x80,0x01,0xB6,0x20,0xB4,0x20,0x96,0x46,0xC9,0xFE,0xD5,0x3C,0xA6,0x3A,0x40,0x10, +0x10,0x09,0xB4,0x09,0x92,0x14,0x96,0x06,0xFE,0x0E,0xC0,0x1F,0x49,0xFF,0xF8,0xD8, +0xE6,0x03,0x44,0x04,0x80,0x02,0xFA,0x20,0xAE,0x40,0xE8,0x04,0x49,0xFF,0xFF,0x87, +0xD5,0x13,0x3C,0x0D,0xFC,0x44,0xC0,0x0B,0xA4,0x80,0x3C,0x13,0xF8,0x84,0x8E,0x21, +0xE0,0x41,0xE8,0x05,0xA4,0x40,0x8C,0x21,0x96,0x49,0xAC,0x40,0xA4,0x40,0x3C,0x03, +0xF8,0x86,0xE2,0x20,0xE8,0x17,0x85,0x40,0xA6,0x3A,0x40,0x10,0x18,0x09,0xB4,0x09, +0x92,0x16,0x96,0x06,0xFE,0x0E,0xC0,0x07,0x44,0x04,0x80,0x02,0x44,0x10,0x00,0x40, +0xAE,0x40,0xD5,0x08,0x84,0x01,0x8D,0x41,0x49,0xFF,0xFC,0x3C,0x5C,0xF5,0x27,0x11, +0xE9,0xA6,0xFC,0xC0,0x44,0x16,0x00,0x34,0xB6,0x01,0xDD,0x9E,0x3C,0x0F,0xFC,0x47, +0x02,0x0F,0x80,0x00,0x3C,0x4B,0xF8,0x86,0x3C,0x1F,0xFC,0x46,0x3C,0x5B,0xF8,0x85, +0x3C,0x2F,0xFC,0x45,0x3C,0x3F,0xFC,0x44,0x3C,0x0B,0xF8,0x84,0xDD,0x9E,0x3C,0x0B, +0xF8,0x86,0xDD,0x9E,0x3E,0x07,0xEF,0x98,0xDD,0x9E,0x2E,0x07,0xEF,0x98,0xDD,0x9E, +0x3C,0x0F,0xFC,0x41,0xDD,0x9E,0x3C,0x0F,0xFC,0x40,0xDD,0x9E,0xFC,0x20,0x44,0x14, +0x80,0x00,0x44,0x04,0x80,0x80,0xA6,0x8A,0xB4,0x00,0x92,0x10,0x96,0x06,0xFE,0x16, +0xC0,0x05,0x44,0x04,0x80,0x02,0x84,0x41,0xAE,0x80,0xA6,0x0A,0x40,0x10,0x04,0x09, +0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x11,0x96,0x06,0xFE,0x0E,0xC0,0x05,0x44,0x04, +0x80,0x02,0x84,0x22,0xAE,0x40,0x44,0x04,0x80,0x00,0xA6,0x02,0x40,0x10,0x08,0x09, +0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x12,0x96,0x06,0xFE,0x0E,0x4E,0x02,0x00,0xB0, +0x44,0x04,0x80,0x02,0x84,0x24,0xAE,0x40,0x3C,0x0D,0xFC,0x45,0x3C,0x2D,0xFC,0x47, +0x84,0x20,0xAC,0x40,0xC2,0x06,0x44,0x16,0x00,0x28,0xB4,0x21,0x96,0x48,0xAE,0x50, +0xC0,0x0B,0xA4,0x80,0x3C,0x13,0xF8,0x85,0x8E,0x21,0xE0,0x41,0xE8,0x05,0xA4,0x40, +0x8C,0x21,0x96,0x49,0xAC,0x40,0x49,0xFF,0xF8,0x3B,0xE6,0x03,0xE9,0x05,0x84,0xC0, +0x44,0x76,0x00,0x30,0xD5,0x19,0x44,0x06,0x00,0x00,0xB4,0x00,0x92,0x08,0x96,0x06, +0x5A,0x08,0x01,0xF7,0x44,0x06,0x00,0x30,0xB4,0x20,0x58,0x10,0x80,0x01,0xB6,0x20, +0xB4,0x20,0x96,0x46,0xC9,0xFE,0xD5,0xEC,0x84,0x01,0x8C,0xC1,0x49,0xFF,0xFB,0xB2, +0x5C,0xF3,0x08,0x01,0xE8,0x6E,0x44,0x34,0x80,0x00,0x44,0x24,0x80,0x80,0xA6,0x1A, +0x40,0x10,0x0C,0x09,0xB4,0x02,0x92,0x13,0x96,0x06,0xFE,0x0E,0xC0,0x1A,0x44,0x04, +0x80,0x02,0x84,0x28,0xAE,0x40,0x49,0xFF,0xF8,0x0B,0xE6,0x03,0xE8,0x5A,0x44,0x06, +0x00,0x00,0xB4,0x00,0x92,0x08,0x96,0x06,0x5A,0x08,0x01,0x54,0x44,0x06,0x00,0x30, +0xB4,0x20,0x58,0x10,0x80,0x01,0xB6,0x20,0xB4,0x20,0x96,0x46,0xC9,0xFE,0xD5,0x49, +0xA6,0x1A,0x40,0x10,0x10,0x09,0xB4,0x02,0x92,0x14,0x96,0x06,0xFE,0x0E,0xC0,0x31, +0x44,0x04,0x80,0x02,0xFA,0x20,0xAE,0x40,0x3C,0x2D,0xFC,0x47,0x3C,0x0D,0xFC,0x45, +0xC2,0x08,0xA4,0x40,0x88,0x22,0x44,0x26,0x00,0x38,0xB4,0x42,0x96,0x90,0xAE,0x88, +0xC0,0x0B,0xA4,0x80,0x3C,0x13,0xF8,0x85,0x8E,0x21,0xE0,0x41,0xE8,0x05,0xA4,0x40, +0x8C,0x21,0x96,0x49,0xAC,0x40,0x49,0xFF,0xF7,0xD3,0xE6,0x03,0xE9,0x03,0x84,0xC0, +0xD5,0x10,0x44,0x06,0x00,0x00,0xB4,0x00,0x92,0x08,0x96,0x06,0x5A,0x08,0x01,0xF9, +0xB4,0x07,0x58,0x00,0x00,0x01,0xB6,0x07,0xB4,0x07,0x96,0x06,0xC8,0xFE,0xD5,0xF0, +0x44,0x04,0x80,0x00,0xA6,0x02,0x40,0x10,0x14,0x09,0x44,0x04,0x80,0x80,0xB4,0x00, +0x92,0x15,0x96,0x06,0xFE,0x0E,0xC0,0x91,0x44,0x04,0x80,0x02,0xFA,0x30,0xAE,0x40, +0x3C,0x0D,0xFC,0x41,0xC0,0x04,0x3C,0x0D,0xFC,0x41,0xDD,0x20,0x44,0x04,0x80,0x00, +0xA6,0x02,0x40,0x10,0x0C,0x09,0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x13,0x96,0x06, +0xFE,0x0E,0xC8,0x0E,0x44,0x04,0x80,0x00,0xA6,0x02,0x40,0x10,0x10,0x09,0x44,0x04, +0x80,0x80,0xB4,0x00,0x92,0x14,0x96,0x06,0xFE,0x0E,0xC0,0x3B,0xD5,0x1A,0x44,0x04, +0x80,0x02,0x84,0x28,0xAE,0x40,0x49,0xFF,0xF7,0x8B,0xE6,0x03,0xE8,0xEC,0x44,0x06, +0x00,0x00,0xB4,0x00,0x92,0x08,0x96,0x06,0x5A,0x08,0x01,0xE6,0x44,0x06,0x00,0x30, +0xB4,0x20,0x58,0x10,0x80,0x01,0xB6,0x20,0xB4,0x20,0x96,0x46,0xC9,0xFE,0xD5,0xDB, +0x44,0x04,0x80,0x02,0xFA,0x20,0xAE,0x40,0x49,0xFF,0xF7,0x72,0xE6,0x03,0xE8,0x04, +0x49,0xFF,0xFE,0x25,0xD5,0x16,0x3C,0x0D,0xFC,0x44,0xC0,0x0B,0xA4,0x80,0x3C,0x13, +0xF8,0x84,0x8E,0x21,0xE0,0x41,0xE8,0x05,0xA4,0x40,0x8C,0x21,0x96,0x49,0xAC,0x40, +0xA4,0x40,0x3C,0x03,0xF8,0x86,0xE2,0x20,0xE9,0x04,0x84,0x01,0x3E,0x07,0xEF,0x98, +0x44,0x14,0x80,0x00,0xA6,0x0A,0x40,0x20,0x14,0x09,0x44,0x04,0x80,0x80,0xB4,0x00, +0x92,0x15,0x96,0x06,0xFE,0x16,0xC0,0x05,0x44,0x04,0x80,0x02,0xFA,0x50,0xAE,0x80, +0xA6,0x0A,0x40,0x10,0x18,0x09,0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x16,0x96,0x06, +0xFE,0x0E,0xC0,0x06,0x44,0x04,0x80,0x02,0x44,0x10,0x00,0x40,0xAE,0x40,0xFC,0xA0, +0x44,0x16,0x38,0x20,0xB4,0x01,0x58,0x00,0x01,0x00,0xB6,0x01,0x44,0x16,0x34,0x20, +0xB4,0x01,0x58,0x00,0x01,0x00,0xB6,0x01,0xDD,0x9E,0x44,0x26,0x38,0x20,0x44,0x0F, +0xFE,0xFF,0xB4,0x22,0xFE,0x46,0xB6,0x22,0x44,0x16,0x34,0x20,0xB4,0x41,0xFE,0x16, +0xB6,0x01,0xDD,0x9E,0x44,0x16,0x38,0x20,0xB4,0x01,0x58,0x00,0x00,0x01,0xB6,0x01, +0x44,0x16,0x34,0x20,0xB4,0x01,0x58,0x00,0x00,0x01,0xB6,0x01,0xDD,0x9E,0x44,0x26, +0x38,0x20,0x84,0x1E,0xB4,0x22,0xFE,0x46,0xB6,0x22,0x44,0x16,0x34,0x20,0xB4,0x41, +0xFE,0x16,0xB6,0x01,0xDD,0x9E,0x46,0x20,0x00,0x83,0x96,0x06,0x04,0x31,0x00,0x14, +0x94,0x41,0x84,0x1D,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x14,0xDD,0x9E,0x46,0x20, +0x00,0x83,0x96,0x06,0x04,0x31,0x00,0x14,0x94,0x43,0x84,0x17,0xFE,0x1E,0xFE,0x0F, +0x14,0x01,0x00,0x14,0xDD,0x9E,0x40,0x21,0x40,0x08,0x40,0x10,0xA0,0x08,0x88,0x22, +0x88,0x20,0x46,0x00,0x00,0x80,0x14,0x10,0x00,0x78,0xDD,0x9E,0xFC,0x41,0xF5,0x81, +0x81,0x00,0x81,0x22,0x80,0xE3,0x80,0xC4,0x46,0x50,0x00,0x80,0xC0,0x03,0x5A,0x08, +0x01,0x18,0x83,0x85,0xBA,0x77,0x46,0x1F,0xEF,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x56, +0xB9,0xF7,0x84,0x23,0x49,0xFF,0xED,0x8B,0x38,0x14,0x99,0x01,0x84,0x02,0x49,0xFF, +0xF3,0xE9,0x38,0x03,0x98,0x00,0x84,0x22,0x49,0xFF,0xF4,0xE7,0xFC,0xC1,0x81,0x41, +0x83,0x85,0xB9,0x77,0x46,0x00,0x10,0x00,0xFE,0x0F,0xB8,0xF7,0x84,0x23,0x84,0x01, +0x49,0xFF,0xED,0x75,0x50,0x25,0x7F,0xFF,0x80,0x06,0xF1,0x01,0x96,0x90,0x49,0xFF, +0xFF,0xC4,0xFC,0xC1,0xE6,0x04,0xE8,0x2E,0x96,0x5F,0x46,0x20,0x00,0x81,0x44,0xF0, +0x28,0xDC,0x38,0x07,0x80,0x00,0x40,0xF0,0x3C,0x00,0xDD,0x0F,0x04,0x12,0x20,0x34, +0x40,0x10,0xC0,0x08,0x04,0x31,0x00,0x32,0x46,0x0F,0xFF,0x0F,0xD5,0x0E,0x40,0x10, +0xD0,0x08,0x04,0x31,0x00,0x32,0x46,0x0F,0xF0,0xFF,0xD5,0x07,0x40,0x10,0xE0,0x08, +0x04,0x31,0x00,0x32,0x46,0x0F,0x0F,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xD5,0x07, +0x04,0x01,0x00,0x32,0x40,0x10,0xF0,0x08,0x94,0x04,0x92,0x04,0xFE,0x47,0x14,0x11, +0x00,0x32,0xDD,0x9E,0xE6,0x08,0xE8,0x43,0x96,0x4F,0x46,0x20,0x00,0x81,0x44,0xF0, +0x29,0x3C,0x38,0x07,0x80,0x00,0x40,0xF0,0x3C,0x00,0xDD,0x0F,0x08,0x10,0x1A,0x26, +0x32,0x40,0x4E,0x5C,0x04,0x31,0x00,0x28,0x84,0x1C,0xD5,0x2D,0x94,0x4A,0x04,0x31, +0x00,0x28,0x84,0x13,0xD5,0x28,0x94,0x4C,0x04,0x31,0x00,0x28,0x44,0x0F,0xFF,0xCF, +0xD5,0x22,0x94,0x4E,0x04,0x31,0x00,0x28,0x44,0x0F,0xFF,0x3F,0xD5,0x1C,0x40,0x10, +0xA0,0x08,0x04,0x31,0x00,0x28,0x44,0x0F,0xFC,0xFF,0xD5,0x15,0x40,0x10,0xA8,0x08, +0x04,0x31,0x00,0x28,0x44,0x0F,0xF3,0xFF,0xD5,0x0E,0x40,0x10,0xB0,0x08,0x04,0x31, +0x00,0x28,0x44,0x0F,0xCF,0xFF,0xD5,0x07,0x40,0x10,0xB8,0x08,0x04,0x31,0x00,0x28, +0x44,0x0F,0x3F,0xFF,0xFE,0x1E,0xFE,0x47,0x14,0x11,0x00,0x28,0xDD,0x9E,0x92,0x00, +0xC8,0x04,0x44,0x07,0x20,0x14,0xD5,0x03,0x44,0x07,0x20,0x18,0xB4,0x00,0x40,0x00, +0x30,0x08,0x92,0x0C,0xDD,0x9E,0xFC,0x00,0x44,0x04,0x00,0x9C,0xA6,0x00,0xE6,0x03, +0xE9,0x24,0x44,0x64,0x0C,0x40,0x84,0x41,0xA6,0x32,0x80,0x62,0x58,0x00,0x00,0x08, +0xAE,0x32,0x44,0x14,0x04,0x40,0x44,0x04,0x08,0x40,0x49,0xFF,0xF0,0xC5,0xA6,0x32, +0x84,0x41,0x54,0x00,0x00,0xF7,0xAE,0x32,0x44,0x14,0x04,0x40,0x44,0x04,0x08,0x40, +0x80,0x62,0x49,0xFF,0xF0,0xB9,0x44,0x07,0x20,0xC0,0x46,0x10,0x20,0x00,0xB4,0x40, +0xFE,0x57,0xB6,0x20,0x80,0x20,0xD5,0x3C,0x49,0x00,0x06,0x87,0xC8,0xDB,0x44,0x64, +0x50,0x28,0xB4,0x06,0x92,0x10,0x96,0x06,0x5A,0x00,0x01,0x05,0x44,0x64,0x50,0x28, +0xD5,0x25,0x2E,0x07,0xF0,0xEB,0xC8,0x0E,0xB4,0x06,0x92,0x03,0x96,0x06,0xC8,0x0A, +0xB4,0x06,0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFE,0x0F,0x5A,0x08,0x01,0xEB, +0xD5,0xEE,0x49,0xFF,0xFA,0x16,0x5A,0x08,0x02,0xF9,0xD5,0xE9,0x2E,0x07,0xF0,0xEB, +0xC8,0x12,0xB4,0x06,0x92,0x03,0x96,0x06,0xC8,0x0E,0xB4,0x06,0x92,0x01,0x96,0x06, +0xC0,0x0A,0x3C,0x0D,0xFE,0x0F,0x5A,0x00,0x01,0xAE,0xB4,0x06,0x92,0x10,0x96,0x06, +0xC0,0xEE,0xD5,0xA8,0x49,0xFF,0xF9,0xFD,0x5A,0x08,0x02,0xF5,0xD5,0xA3,0xB4,0x01, +0x92,0x19,0x96,0x06,0xC8,0xFD,0x84,0x01,0x49,0xFF,0xFC,0x59,0xFC,0x80,0xFC,0x00, +0x80,0xC0,0x44,0x04,0x80,0x90,0x54,0x13,0x00,0x01,0xB4,0x80,0x84,0x5D,0x94,0xC9, +0xFE,0xA6,0xFE,0x9F,0xB6,0x40,0x46,0x00,0x00,0x81,0x84,0x5E,0x83,0x80,0xBB,0x15, +0xFE,0x9E,0xFE,0x57,0xB9,0x95,0x84,0x00,0x3E,0x07,0xF0,0xEB,0x44,0x04,0x80,0x10, +0x84,0x22,0xAE,0x40,0x49,0x00,0x24,0x39,0xE6,0x03,0xE9,0x2A,0x44,0x14,0x00,0x18, +0xA6,0x0A,0x5A,0x68,0x01,0x13,0x54,0x00,0x00,0xFB,0xAE,0x0A,0x44,0x14,0x50,0x10, +0xB4,0x01,0x92,0x08,0x40,0x00,0x20,0x08,0xB6,0x01,0x44,0x14,0x70,0x00,0x44,0x0E, +0xFF,0xFF,0xB4,0x41,0xFE,0x16,0xD5,0x13,0x58,0x00,0x00,0x04,0xAE,0x0A,0x44,0x14, +0x50,0x10,0xB4,0x01,0x92,0x08,0x40,0x00,0x20,0x08,0x58,0x00,0x00,0x98,0xB6,0x01, +0x44,0x14,0x70,0x00,0x44,0x01,0x00,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0xFC,0x80, +0xFC,0x00,0x46,0x01,0xFF,0xF9,0x50,0x00,0x05,0x27,0x46,0x61,0xFF,0xFF,0x49,0xFF, +0xF7,0x79,0x49,0xFF,0xF7,0x63,0x50,0x03,0x03,0xCC,0x49,0xFF,0xF7,0x73,0x49,0xFF, +0xF7,0x5D,0x80,0x06,0x44,0x67,0x30,0x04,0x49,0xFF,0xF7,0x6C,0x49,0xFF,0xF7,0x56, +0x44,0x00,0x03,0xE8,0x49,0xFF,0xF7,0x5F,0xB4,0x26,0x84,0x1E,0xFE,0x0E,0xB6,0x06, +0x44,0x00,0x03,0xE8,0x49,0xFF,0xF7,0x57,0xB4,0x06,0x58,0x00,0x00,0x01,0xB6,0x06, +0x44,0x00,0x03,0xE8,0x49,0xFF,0xF7,0x4F,0x44,0x04,0x80,0x0A,0x84,0x24,0xAE,0x40, +0x84,0x22,0xAE,0x40,0x84,0x21,0xAE,0x40,0xFC,0x80,0xFC,0x40,0x80,0xE1,0x44,0x17, +0x30,0x10,0xB4,0x41,0x92,0x41,0x96,0x86,0x5A,0x20,0x01,0xFD,0x44,0x17,0x30,0x00, +0x46,0x60,0x01,0x00,0xB6,0x01,0x84,0x00,0x3E,0x07,0xF1,0x25,0x44,0x04,0x80,0x0A, +0x84,0x22,0xAE,0x40,0x44,0x94,0x80,0x08,0xB4,0x09,0x92,0x11,0x96,0x06,0xC8,0x06, +0x84,0x01,0x8E,0xC1,0x49,0xFF,0xF7,0x27,0xCE,0xF8,0x44,0x04,0x80,0x0A,0x84,0x22, +0xAE,0x40,0x84,0x00,0x3E,0x07,0xF1,0x25,0x44,0x00,0x11,0x11,0x4C,0x70,0x40,0x0A, +0x44,0x07,0x30,0x14,0xB4,0x00,0x92,0x10,0x96,0x00,0x3C,0x0F,0xFC,0x85,0xD5,0x0C, +0x44,0x00,0x22,0x22,0x4C,0x70,0x40,0x09,0x44,0x07,0x30,0x14,0xB4,0x00,0x92,0x10, +0x96,0x00,0x3C,0x0F,0xFC,0x84,0xFC,0xC0,0xFC,0x00,0x44,0x27,0x30,0x10,0xB4,0x22, +0x92,0x21,0x96,0x46,0x5A,0x10,0x01,0xFD,0x84,0x20,0x3E,0x17,0xF1,0x25,0x44,0x17, +0x30,0x00,0x46,0x60,0x01,0x00,0xB6,0x01,0x2E,0x07,0xF1,0x25,0xC0,0x08,0x2E,0x07, +0xF1,0x22,0xC0,0x0E,0x84,0x00,0x3E,0x07,0xF1,0x22,0xD5,0x0A,0x2E,0x07,0xF1,0x22, +0xC8,0xF7,0x84,0x01,0x8E,0xC1,0x49,0xFF,0xF6,0xE6,0xCE,0xEF,0xD5,0xF1,0x84,0x00, +0x3E,0x07,0xF1,0x25,0x44,0x07,0x30,0x14,0xB4,0x00,0x92,0x10,0x96,0x00,0xFC,0x80, +0xFC,0x20,0x46,0x01,0xFF,0xFF,0x50,0x00,0x0F,0x05,0x49,0xFF,0xF6,0xDB,0x46,0x02, +0xF0,0xC0,0x50,0x00,0x01,0x00,0x49,0xFF,0xFF,0xC9,0x80,0xC0,0x46,0x02,0xF0,0xD0, +0x50,0x00,0x01,0x00,0x49,0xFF,0xFF,0xC2,0x97,0xC0,0x46,0x01,0xFC,0x00,0x97,0xB0, +0x50,0x00,0x01,0x00,0x88,0x06,0x49,0xFF,0xF6,0xC5,0x46,0x01,0xFC,0x10,0x50,0x00, +0x01,0x00,0x88,0x07,0x49,0xFF,0xF6,0xBE,0x46,0x01,0xFC,0x20,0x50,0x00,0x01,0x00, +0x88,0x06,0x49,0xFF,0xF6,0xB7,0x46,0x01,0xFC,0x30,0x50,0x00,0x01,0x00,0x88,0x07, +0x49,0xFF,0xF6,0xB0,0x46,0x01,0xFC,0x40,0x50,0x00,0x01,0x00,0x88,0x06,0x49,0xFF, +0xF6,0xA9,0x46,0x01,0xFC,0x50,0x50,0x00,0x01,0x00,0x88,0x07,0x49,0xFF,0xF6,0xA2, +0x46,0x01,0xFC,0x60,0x50,0x00,0x01,0x00,0x88,0x06,0x49,0xFF,0xF6,0x9B,0x46,0x01, +0xFC,0x70,0x50,0x00,0x01,0x00,0x88,0x07,0x49,0xFF,0xF6,0x94,0xFC,0xA0,0x3C,0x0F, +0xFC,0x4A,0xDD,0x9E,0x44,0x04,0x80,0x00,0xA6,0x03,0x96,0x06,0xC0,0x0D,0x44,0x04, +0x80,0x03,0x84,0x21,0xAE,0x40,0x3C,0x0D,0xFC,0x4A,0xC0,0x06,0xFC,0x00,0x3C,0x0D, +0xFC,0x4A,0xDD,0x20,0xFC,0x80,0xDD,0x9E,0x46,0x20,0x00,0x81,0x84,0x3E,0x04,0x31, +0x00,0x14,0x96,0x06,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x14,0xDD,0x9E,0x44,0x14, +0x70,0x08,0x5A,0x08,0x01,0x09,0xB4,0x01,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00, +0x78,0x23,0xD5,0x05,0xB4,0x01,0x92,0x10,0x40,0x00,0x40,0x08,0xB6,0x01,0xDD,0x9E, +0x4E,0x05,0x00,0x19,0x8E,0x01,0x40,0x10,0x04,0x0A,0xFE,0x0F,0x40,0x10,0x08,0x0A, +0xFE,0x0F,0x40,0x10,0x10,0x0A,0xFE,0x0F,0x40,0x10,0x20,0x0A,0xFE,0x0F,0x40,0x10, +0x40,0x0A,0xFE,0x47,0x8C,0x21,0x84,0x00,0x90,0x21,0xC1,0x03,0x8C,0x01,0xD5,0xFD, +0xDD,0x9E,0x84,0x00,0xDD,0x9E,0xFC,0x4D,0x44,0x00,0x7F,0x10,0x50,0xFF,0x80,0x18, +0x80,0xC0,0x80,0x2F,0x3A,0x23,0x14,0x04,0x3A,0x20,0x94,0x24,0x81,0x46,0x83,0x81, +0x3A,0x25,0x14,0x04,0x44,0x90,0x7F,0x38,0x3A,0x2E,0x14,0x24,0x80,0x89,0x3A,0x25, +0x0C,0x00,0x80,0xBF,0x3A,0x2E,0x0C,0x20,0x3A,0x22,0x0C,0x04,0x3A,0x22,0x8C,0x24, +0x45,0xE2,0x01,0xA8,0xA5,0xE0,0x3A,0x27,0x94,0x04,0x81,0xFE,0x81,0x1F,0x3A,0x27, +0x94,0x24,0x3A,0x20,0x94,0x04,0x80,0x2F,0x3A,0x20,0x94,0x24,0x3A,0x2E,0x0C,0x00, +0x3A,0x20,0x8C,0x20,0x44,0x12,0x01,0x9C,0x3A,0x24,0x0C,0x04,0x3A,0x20,0x8C,0x24, +0xAD,0xC8,0xB0,0x50,0x3A,0x20,0x14,0x04,0x80,0x01,0x3A,0x20,0x14,0x24,0x81,0x00, +0x3A,0x23,0x14,0x04,0x3A,0x24,0x14,0x24,0xB1,0x83,0x3A,0x25,0x0C,0x00,0x80,0x86, +0x3A,0x24,0x0C,0x20,0x3A,0x24,0x8C,0x04,0x44,0x92,0x00,0x18,0x3A,0x22,0x0C,0x24, +0x3A,0x20,0x94,0x04,0x80,0x29,0x3A,0x20,0x94,0x24,0x3A,0x20,0x14,0x04,0x80,0x01, +0x3A,0x20,0x14,0x24,0x3A,0x24,0x0C,0x00,0x3A,0x20,0x0C,0x20,0x44,0x02,0x00,0x0C, +0x3A,0x23,0x0C,0x04,0x3A,0x20,0x0C,0x24,0xAD,0xC0,0xFC,0xCD,0xFC,0x48,0x83,0x81, +0x50,0xAF,0x80,0x14,0x44,0x10,0x7F,0x44,0x80,0xC0,0x80,0x0A,0x81,0x22,0x3A,0x20, +0x8C,0x04,0x3A,0x20,0x0C,0x24,0xA4,0x48,0xFA,0x40,0xAC,0x40,0x50,0x8F,0x80,0x30, +0x84,0x20,0xB0,0x08,0x49,0x00,0x28,0x12,0x80,0x08,0x84,0x20,0xFA,0x40,0x49,0x00, +0x28,0x0D,0x5A,0x98,0x01,0x03,0x87,0x80,0xC6,0x04,0x44,0x12,0x02,0x48,0xD5,0x03, +0x44,0x12,0x02,0x54,0x58,0x04,0x80,0x01,0x8C,0x01,0x84,0xE0,0xB6,0x1F,0x8D,0x21, +0x84,0x83,0x84,0x61,0x38,0x05,0x1C,0x00,0xF3,0x83,0xF4,0x82,0x38,0x60,0x80,0x00, +0xB4,0x1F,0x8C,0xC1,0xFF,0x84,0x97,0xB1,0x40,0x63,0x24,0xD6,0x80,0x06,0xF1,0x81, +0x49,0xFF,0xFF,0x48,0xF4,0x02,0x97,0x78,0x40,0x52,0x90,0xB7,0xF3,0x03,0x38,0x24, +0x16,0x02,0x8C,0x08,0x41,0xE1,0x28,0x08,0x40,0x20,0x70,0x01,0x40,0x01,0x80,0x0C, +0x40,0x60,0x18,0xD7,0xB0,0x08,0x94,0x94,0x38,0x00,0x16,0x02,0x88,0x5E,0x40,0x00, +0x28,0x08,0x88,0xC0,0xB0,0x08,0x8C,0xE1,0x38,0x24,0x16,0x0A,0x38,0x60,0x16,0x0A, +0xF1,0x01,0x5A,0x78,0x0A,0xD1,0x80,0x28,0x49,0xFF,0xEB,0xD1,0xFC,0xC8,0x80,0x80, +0x84,0x40,0x9A,0xE0,0x96,0xD8,0xE2,0x61,0xE8,0x08,0x08,0x32,0x00,0x01,0xE2,0x62, +0x40,0x31,0x3C,0x1B,0x80,0x43,0xD5,0xF6,0x9C,0x11,0x96,0x00,0xDD,0x9E,0xFC,0x40, +0x51,0xFF,0xFB,0xE0,0x44,0x20,0x04,0x00,0x81,0x40,0x81,0x21,0xB0,0x08,0x84,0x20, +0x49,0x00,0x27,0xAC,0xB1,0x81,0x44,0x10,0x7F,0x44,0x80,0x06,0x3A,0x20,0x8C,0x04, +0x3A,0x20,0x0C,0x24,0xA4,0x48,0xB1,0xC4,0xAC,0x40,0xFA,0x40,0x84,0x20,0x80,0x07, +0x49,0x00,0x27,0x9C,0x84,0x2A,0x4E,0xA3,0x00,0x3D,0x44,0x02,0x02,0x54,0x49,0xFF, +0xFF,0xD0,0x80,0x20,0x80,0x4A,0x84,0xA3,0x44,0xA2,0x01,0xA8,0x45,0xE2,0x01,0x9C, +0x96,0x10,0x40,0x00,0x14,0x17,0x40,0x30,0x20,0x08,0xB1,0x08,0x41,0xC2,0x0C,0x00, +0x84,0x60,0x54,0x81,0x80,0xFF,0xE3,0x01,0xE8,0x12,0x38,0x83,0x08,0x00,0x38,0x85, +0x22,0x02,0x38,0x84,0x0D,0x01,0x54,0xF4,0x03,0xFF,0x38,0x8E,0x0E,0x02,0x40,0x84, +0x28,0x08,0x89,0x0F,0x38,0x8E,0x0E,0x0A,0x8C,0x61,0xD5,0xEC,0x38,0x33,0x08,0x00, +0x8C,0x41,0x38,0x3F,0x0C,0x00,0x8A,0x69,0x54,0x81,0x83,0xFF,0x38,0x33,0x82,0x02, +0x40,0x31,0xA8,0x08,0x88,0x68,0x38,0x33,0x82,0x0A,0x5A,0x28,0x0A,0xD3,0xD5,0x3B, +0x44,0x02,0x02,0x48,0x49,0xFF,0xFF,0x95,0x80,0x20,0x84,0x40,0x84,0xA3,0x44,0xA2, +0x00,0x18,0x45,0xE2,0x00,0x0C,0x97,0x10,0x40,0x42,0x14,0x97,0x40,0x32,0x20,0x08, +0xB0,0x08,0x41,0xC0,0x0C,0x00,0x84,0x60,0x54,0x81,0x80,0xFF,0xE3,0x01,0xE8,0x12, +0x38,0x83,0x08,0x00,0x38,0x85,0x22,0x02,0x38,0x84,0x0D,0x01,0x54,0xF4,0x03,0xFF, +0x38,0x8E,0x0E,0x02,0x40,0x84,0x28,0x08,0x89,0x0F,0x38,0x8E,0x0E,0x0A,0x8C,0x61, +0xD5,0xEC,0x38,0x33,0x08,0x00,0x8C,0x41,0x38,0x3F,0x0C,0x00,0x8A,0x69,0x54,0x81, +0x83,0xFF,0x38,0x33,0x92,0x02,0x40,0x31,0xA8,0x08,0x88,0x68,0x38,0x33,0x92,0x0A, +0x5A,0x28,0x0A,0xD3,0xB0,0x08,0x49,0xFF,0xEB,0x79,0x80,0x07,0x49,0xFF,0xEB,0xAF, +0x51,0xFF,0x84,0x20,0xFC,0xC0,0xFC,0x20,0x49,0x00,0x0D,0x77,0x80,0xC0,0x49,0x00, +0x0D,0x77,0x80,0xA6,0x50,0x43,0x04,0x80,0x50,0x32,0x80,0x22,0x50,0x20,0x00,0x22, +0x84,0x20,0x38,0x62,0x85,0x11,0x22,0x71,0x80,0x00,0x38,0x72,0x85,0x09,0x1A,0x61, +0xFF,0xFF,0x38,0x60,0x05,0x11,0x22,0x71,0x00,0x00,0x38,0x70,0x05,0x09,0x8C,0x21, +0x1A,0x61,0x7F,0xFF,0x5A,0x18,0x09,0xEF,0x50,0x52,0x80,0x24,0x50,0x00,0x00,0x24, +0xDC,0xE4,0xFC,0xA0,0xFC,0x40,0x51,0xFF,0xFD,0xB8,0xF2,0x81,0x81,0x20,0x44,0x20, +0x02,0x40,0x81,0x41,0xB0,0x02,0x44,0x10,0x7F,0x5C,0x80,0xE3,0x80,0xC4,0x49,0x00, +0x26,0xBF,0xF0,0x01,0x49,0xFF,0xFC,0x5E,0x81,0x00,0x49,0x00,0x03,0x16,0xC8,0x0C, +0x49,0x00,0x0E,0x6A,0xC8,0x06,0x2E,0x17,0xF8,0x40,0xC1,0x07,0x84,0x01,0xD5,0x05, +0x49,0x00,0x0D,0x39,0xD5,0x04,0x84,0x00,0x49,0xFF,0xF2,0x26,0x40,0x34,0x1C,0x00, +0x95,0x32,0x84,0xA0,0xFB,0x02,0x84,0x40,0x99,0xAA,0x97,0xB1,0x4E,0x92,0x00,0x13, +0xB0,0x42,0x81,0xE2,0x38,0x10,0x98,0x00,0x42,0xF0,0xA0,0x73,0x40,0x17,0x88,0x08, +0x88,0x24,0x38,0x70,0x04,0x02,0x38,0x11,0xBD,0x01,0x90,0xE8,0x8A,0xE1,0x38,0x74, +0x99,0x09,0x4E,0xA2,0x00,0x0C,0xB0,0x42,0x80,0xE2,0x38,0x10,0x98,0x00,0x42,0x70, +0xA0,0x73,0x38,0x11,0x9D,0x11,0x38,0x15,0x19,0x09,0x8C,0x41,0x5A,0x28,0x12,0xDE, +0x8C,0xB2,0x97,0x69,0x5A,0x5A,0x40,0xD9,0x49,0xFF,0xFF,0x87,0x51,0xFF,0x82,0x48, +0xFC,0xC0,0xFC,0x00,0x80,0x43,0x84,0x60,0x80,0x83,0x49,0xFF,0xFF,0xA5,0xFC,0x80, +0xFC,0x20,0x80,0xE0,0x80,0x02,0x80,0xC1,0x49,0xFF,0xFC,0x0C,0x84,0x40,0x96,0x51, +0xE2,0x26,0xE8,0x07,0x38,0x10,0x09,0x11,0x38,0x13,0x89,0x09,0x8C,0x41,0xD5,0xF8, +0xFC,0xA0,0xFC,0x20,0xFD,0x30,0x84,0x00,0x49,0xFF,0xFB,0xFC,0x95,0xF9,0x80,0xA6, +0x88,0xE6,0x50,0x00,0x04,0xA4,0xD7,0x06,0x2A,0x10,0x00,0x01,0x1A,0x12,0x80,0x01, +0xD5,0xFB,0xFC,0xA0,0xFC,0x20,0x80,0xE0,0x80,0x02,0x80,0xC1,0x49,0xFF,0xF1,0xC4, +0x84,0x40,0x96,0x51,0xE2,0x26,0xE8,0x07,0x38,0x10,0x0A,0x02,0x38,0x13,0x8A,0x0A, +0x8C,0x41,0xD5,0xF8,0xFC,0xA0,0xFC,0x40,0x80,0xC0,0x80,0x02,0x81,0x23,0x80,0xE1, +0x49,0xFF,0xFB,0xD8,0x81,0x40,0x84,0x6C,0x5A,0x98,0x02,0x07,0x49,0x00,0x0D,0xE8, +0x84,0x63,0xC0,0x02,0x80,0x69,0x80,0x0A,0x40,0x91,0x84,0x08,0x84,0x20,0x40,0xA1, +0x80,0x13,0x96,0x89,0xE2,0x47,0xE8,0x1E,0x84,0xA0,0x80,0x85,0x40,0x82,0x80,0x13, +0xE3,0x0A,0xE8,0x06,0x38,0x80,0x15,0x11,0x8C,0xA1,0x88,0x88,0xD5,0xF8,0x5C,0xF1, +0x00,0x40,0x40,0x42,0x0C,0x96,0xE8,0x09,0x54,0x51,0x00,0x01,0x95,0x6D,0x92,0x41, +0x88,0x45,0x38,0x43,0x09,0x09,0xD5,0x03,0x38,0x43,0x05,0x09,0x8C,0x21,0x88,0x09, +0xD5,0xE1,0xFC,0xC0,0x2E,0x07,0xF1,0xF2,0xDD,0x9E,0x3E,0x07,0xF1,0xF2,0xDD,0x9E, +0xFC,0x52,0x44,0x00,0x81,0x9C,0x80,0x20,0x80,0x9F,0x3A,0x20,0x8C,0x04,0x3A,0x22, +0x0C,0x24,0x50,0x9F,0x80,0x0C,0xA4,0x48,0x3A,0x20,0x0C,0x04,0x80,0x09,0xAC,0x60, +0x3A,0x20,0x0C,0x24,0xB1,0xC6,0xAC,0x40,0x44,0x00,0x81,0xA8,0x80,0x27,0x3A,0x20, +0x14,0x04,0x3A,0x20,0x94,0x24,0xB4,0x00,0xB1,0x90,0xB6,0x01,0x44,0x00,0x81,0xBC, +0x80,0x26,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24,0x3A,0x20,0x14,0x04,0x3A,0x20, +0x94,0x24,0x3A,0x20,0x0C,0x00,0x3A,0x20,0x8C,0x20,0x49,0x00,0x20,0x3E,0xE6,0x03, +0x81,0x5F,0xE9,0x07,0x2E,0x07,0xF1,0xF2,0x44,0x10,0x01,0x30,0x38,0x13,0x01,0x09, +0x50,0x8F,0x80,0x2C,0x44,0x00,0x81,0xE4,0x80,0x28,0x3A,0x20,0x14,0x04,0x3A,0x20, +0x94,0x24,0x04,0xF0,0x00,0x00,0x44,0x00,0x81,0xF8,0x14,0xF0,0x80,0x00,0xB0,0x5A, +0x83,0x81,0x3A,0x20,0x14,0x04,0x3A,0x2E,0x14,0x24,0x83,0xDC,0x3A,0x20,0x14,0x04, +0x3A,0x2F,0x14,0x24,0x3A,0x20,0x0C,0x00,0x3A,0x2F,0x0C,0x20,0x44,0x02,0x01,0xF8, +0x3A,0x24,0x14,0x04,0x3A,0x20,0x14,0x24,0x44,0x82,0x01,0xD0,0x14,0xF0,0x00,0x00, +0x80,0x08,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x2E,0x14,0x04,0x3A,0x20, +0x14,0x24,0x3A,0x2F,0x0C,0x00,0x3A,0x20,0x0C,0x20,0x44,0x02,0x02,0x54,0x3A,0x25, +0x0C,0x04,0x3A,0x20,0x0C,0x24,0x80,0x20,0x02,0x05,0x00,0x00,0xAC,0x08,0x44,0x02, +0x02,0x48,0x3A,0x24,0x8C,0x04,0x3A,0x20,0x0C,0x24,0x80,0x20,0x02,0x04,0x80,0x00, +0xAC,0x08,0x44,0x02,0x02,0x34,0x3A,0x23,0x94,0x04,0x3A,0x20,0x14,0x24,0x80,0x20, +0xB4,0x07,0xB6,0x01,0x44,0x12,0x02,0x0C,0x80,0x01,0x3A,0x23,0x14,0x04,0x3A,0x20, +0x14,0x24,0x3A,0x23,0x14,0x04,0x3A,0x20,0x14,0x24,0x3A,0x23,0x0C,0x00,0x3A,0x20, +0x0C,0x20,0xFC,0xD2,0xFC,0x00,0x49,0xFF,0xEC,0x1C,0xFC,0x80,0xFC,0x00,0xC8,0x06, +0x44,0x02,0x02,0x0C,0x44,0x12,0x02,0x34,0xD5,0x05,0x44,0x02,0x01,0xD0,0x44,0x12, +0x01,0xF8,0x49,0xFF,0xED,0x08,0xFC,0x80,0x84,0x00,0x3E,0x07,0xF1,0xF1,0x3E,0x07, +0xF1,0xF0,0xDD,0x9E,0xFC,0x00,0x80,0x41,0xC9,0x05,0x5A,0x08,0x01,0x13,0x80,0x01, +0xD5,0x05,0x84,0x20,0x5A,0x08,0x01,0x09,0x84,0x02,0x94,0x01,0x96,0x40,0xCA,0x04, +0x44,0x02,0x02,0x54,0xD5,0x03,0x44,0x02,0x02,0x48,0x49,0xFF,0xEF,0x28,0xFC,0x80, +0xD5,0xF8,0xFC,0x00,0xC9,0x0C,0x84,0x00,0x49,0xFF,0xEE,0xA5,0x84,0x00,0x80,0x20, +0x49,0xFF,0xEE,0xD2,0x84,0x00,0x49,0xFF,0xEF,0x04,0xFC,0x80,0x80,0xC0,0x5A,0x08, +0x01,0xF4,0x84,0x02,0x49,0xFF,0xEE,0x97,0x80,0x06,0x84,0x20,0x49,0xFF,0xEE,0xC4, +0x80,0x06,0xD5,0xF2,0xFC,0x01,0xB6,0x1F,0xF1,0x81,0x49,0xFF,0xFF,0xE4,0xB4,0x1F, +0xF1,0x01,0x49,0xFF,0xFF,0xC9,0xFC,0x81,0xFC,0x49,0x80,0xE0,0x44,0x02,0x00,0x01, +0xF0,0x86,0x44,0x01,0x00,0x02,0xF0,0x87,0x44,0x00,0x80,0x04,0xF0,0x88,0x44,0x00, +0x40,0x08,0xF0,0x89,0x44,0x00,0x20,0x10,0xF0,0x8A,0x44,0x00,0x10,0x20,0xF0,0x8B, +0x44,0x00,0x08,0x40,0xF0,0x8C,0x44,0x00,0x04,0x80,0xF0,0x8D,0x44,0x00,0x03,0x00, +0xF0,0x8E,0x3C,0x0D,0xFC,0x17,0xF0,0x8F,0xF0,0x90,0xF0,0x91,0xC7,0x08,0x44,0x92, +0x01,0xF8,0x44,0xA2,0x01,0xD0,0x85,0x02,0x87,0x85,0xD5,0x07,0x85,0x03,0x44,0x92, +0x02,0x34,0x44,0xA2,0x02,0x0C,0x83,0x88,0xB0,0x06,0x49,0xFF,0xEC,0xFF,0x80,0xDF, +0xCF,0x11,0x44,0x00,0x82,0x38,0x80,0x3F,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24, +0x3A,0x20,0x0C,0x00,0x3A,0x20,0x8C,0x20,0x80,0x1F,0x49,0xFF,0xED,0x25,0x80,0x07, +0xD5,0x10,0x44,0x00,0x82,0x50,0x80,0x3F,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24, +0x3A,0x20,0x0C,0x00,0x3A,0x20,0x8C,0x20,0x80,0x1F,0x49,0xFF,0xED,0x15,0x84,0x01, +0x49,0x00,0x0B,0x75,0x2E,0x47,0xF1,0xF2,0x84,0x00,0x80,0x4A,0x80,0x69,0x80,0xA4, +0x80,0x20,0x49,0xFF,0xF9,0xAD,0x2E,0x17,0xF1,0xF2,0x84,0x00,0x38,0x15,0x05,0x01, +0x49,0xFF,0xED,0xA8,0x2E,0x17,0xF1,0xF2,0x84,0x03,0x38,0x15,0x05,0x01,0x49,0xFF, +0xED,0xA1,0x2E,0x07,0xF1,0xF2,0x84,0x20,0x38,0x04,0x80,0x00,0x49,0xFF,0xEE,0x9D, +0x2E,0x07,0xF1,0xF2,0x84,0x23,0x38,0x04,0x80,0x00,0x49,0xFF,0xEE,0x96,0x44,0x00, +0x02,0x40,0x49,0xFF,0xF0,0x34,0x80,0x08,0x49,0xFF,0xF0,0x5C,0x5A,0x78,0x01,0x0A, +0x2E,0x07,0xF1,0xF2,0x40,0xFE,0x00,0x06,0xE9,0x04,0x80,0x07,0x80,0x27,0xD5,0x03, +0x84,0x00,0x80,0x27,0x49,0xFF,0xFF,0x68,0xFC,0xC9,0xFC,0x49,0xC1,0x07,0x44,0x92, +0x01,0xD0,0x85,0x42,0x44,0x72,0x01,0xF8,0xD5,0x06,0x44,0x92,0x02,0x0C,0x85,0x43, +0x44,0x72,0x02,0x34,0x5A,0x08,0x03,0x32,0xB1,0x86,0x44,0x10,0x82,0x68,0x80,0x06, +0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24, +0x3A,0x20,0x94,0x00,0x44,0x10,0x82,0x20,0x3A,0x20,0x14,0x20,0x80,0x1F,0x3A,0x20, +0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x8C,0x00,0x3A,0x20,0x0C,0x20,0x2E,0x47, +0xF1,0xF2,0x84,0x20,0x80,0x49,0x80,0x67,0x80,0xA4,0x84,0x01,0x49,0xFF,0xF9,0x48, +0x80,0x06,0x49,0xFF,0xEC,0x6B,0x80,0x1F,0x49,0xFF,0xEC,0x9E,0x84,0x00,0x49,0xFF, +0xEF,0xE6,0x81,0x5F,0x84,0x0C,0xD5,0x35,0x5A,0x08,0x02,0x36,0xB1,0x86,0x44,0x10, +0x82,0x98,0x81,0x06,0x3A,0x20,0x94,0x04,0x3A,0x24,0x14,0x24,0x3A,0x20,0x94,0x04, +0x3A,0x24,0x14,0x24,0x3A,0x20,0x94,0x00,0x44,0x10,0x82,0x38,0x81,0xFF,0x3A,0x24, +0x14,0x20,0x3A,0x20,0x94,0x04,0x3A,0x27,0x94,0x24,0x3A,0x20,0x8C,0x00,0x3A,0x27, +0x8C,0x20,0x49,0x00,0x0A,0xDC,0x2E,0x47,0xF1,0xF2,0x84,0x00,0x80,0x20,0x80,0x49, +0x80,0x67,0x80,0xA4,0x49,0xFF,0xF9,0x14,0x80,0x06,0x49,0xFF,0xEC,0x37,0x80,0x1F, +0x49,0xFF,0xEC,0x6A,0x44,0x00,0x02,0x40,0x49,0xFF,0xEF,0xB1,0x81,0x1F,0x80,0x0A, +0x49,0xFF,0xEF,0xD8,0xFC,0xC9,0xFC,0x49,0x44,0x10,0x82,0x98,0xB1,0x86,0x80,0xE0, +0x80,0x06,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x20, +0x14,0x24,0x3A,0x20,0x94,0x00,0x3A,0x20,0x14,0x20,0xC7,0x07,0x44,0x82,0x01,0xD0, +0x85,0x22,0x44,0xA2,0x01,0xF8,0xD5,0x06,0x44,0x82,0x02,0x0C,0x85,0x23,0x44,0xA2, +0x02,0x34,0x84,0x00,0x49,0x00,0x0A,0xA3,0x2E,0x47,0xF1,0xF2,0x84,0x00,0x80,0x20, +0x80,0x48,0x80,0x6A,0x80,0xA4,0x49,0xFF,0xF8,0xDB,0x80,0x06,0x49,0xFF,0xEB,0xFE, +0x44,0x00,0x02,0x40,0x49,0xFF,0xEF,0x7B,0x80,0x09,0x49,0xFF,0xEF,0xA3,0x46,0x00, +0x42,0x10,0x50,0x00,0x08,0x20,0x80,0x20,0x84,0x40,0x49,0xFF,0xEB,0x06,0x80,0x1F, +0xCF,0x04,0x44,0x10,0x82,0x38,0xD5,0x03,0x44,0x10,0x82,0x50,0x80,0xC0,0x3A,0x20, +0x94,0x04,0x3A,0x23,0x14,0x24,0x3A,0x20,0x8C,0x00,0x3A,0x23,0x0C,0x20,0x49,0xFF, +0xEC,0x13,0xFC,0xC9,0x2E,0x07,0xF1,0xFC,0x96,0x00,0xC0,0x05,0x84,0x00,0x3E,0x07, +0xF1,0xFC,0x84,0x01,0xDD,0x9E,0x2E,0x17,0xF1,0xFA,0x96,0x48,0xC1,0x05,0x84,0x00, +0x3E,0x07,0xF1,0xFA,0xD5,0x07,0x2E,0x07,0xF1,0xF6,0x96,0x00,0xC0,0x04,0x3E,0x17, +0xF1,0xF6,0x84,0x01,0xDD,0x9E,0x2E,0x07,0xF1,0xFF,0xDD,0x9E,0x3E,0x07,0xF1,0xFF, +0xDD,0x9E,0x2E,0x07,0xF1,0xFE,0xDD,0x9E,0x3E,0x07,0xF1,0xFD,0xDD,0x9E,0xDD,0x9E, +0xFC,0x20,0x49,0xFF,0xF5,0xC4,0x2E,0x17,0xF2,0x0C,0x5A,0x18,0x01,0x7D,0x5A,0x08, +0x01,0x06,0x3C,0x0D,0xFC,0x82,0x4E,0x02,0x01,0x01,0x49,0xFF,0xF1,0x76,0x46,0x01, +0xFF,0xFF,0x50,0x00,0x0F,0x00,0x49,0xFF,0xF1,0x65,0x46,0x01,0xF1,0x10,0x50,0x00, +0x01,0x00,0x49,0xFF,0xF1,0x5F,0x44,0x00,0x00,0x96,0x49,0xFF,0xF2,0xEC,0x46,0x01, +0xF2,0x90,0x50,0x00,0x01,0x00,0x49,0xFF,0xF1,0x55,0xFA,0x04,0x49,0xFF,0xF2,0xE3, +0x49,0xFF,0xEB,0x60,0x84,0x00,0x49,0xFF,0xFA,0xD1,0x84,0x00,0x49,0xFF,0xF2,0x2D, +0x84,0x00,0x49,0xFF,0xFA,0xD6,0x84,0x00,0x84,0x21,0x49,0xFF,0xF1,0xBD,0x84,0x01, +0x80,0x20,0x49,0xFF,0xF2,0x04,0x84,0x00,0x49,0x00,0x09,0x6C,0x84,0x00,0x49,0x00, +0x09,0x4F,0x84,0x00,0x49,0xFF,0xFF,0xB4,0x46,0x00,0x00,0x81,0x44,0x60,0x08,0x01, +0x83,0x80,0xB9,0x1A,0x44,0x74,0x50,0x28,0x92,0x25,0x94,0x4D,0x58,0x10,0x80,0x10, +0xB9,0x9A,0xBA,0x1A,0x44,0x1F,0xE0,0xFF,0xFE,0x56,0x58,0x10,0x88,0x00,0xB9,0x9A, +0xBA,0x1A,0x46,0x1F,0xFE,0x0F,0x50,0x10,0x8F,0xFF,0xFE,0x56,0x44,0x25,0x00,0x00, +0xFE,0x57,0xB9,0x9A,0xBA,0x1A,0x46,0x1E,0x0F,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x56, +0x46,0x20,0x10,0x00,0xFE,0x57,0xB9,0x9A,0xB9,0x19,0x44,0x21,0xF4,0xCE,0x92,0x31, +0x40,0x10,0xC4,0x08,0xFE,0x57,0xB9,0x99,0xB4,0x07,0x92,0x01,0x96,0x06,0x5A,0x08, +0x01,0x04,0x48,0x00,0x00,0x93,0x8E,0xC1,0x84,0x01,0x97,0xB1,0x49,0xFF,0xF2,0x82, +0xCE,0xF4,0xFC,0xA0,0x5A,0x10,0x02,0x04,0x48,0x00,0x00,0x88,0xC8,0x05,0x3C,0x0D, +0xFC,0x82,0x4E,0x02,0x00,0x83,0x46,0x61,0xFF,0xFF,0x50,0x63,0x0F,0x00,0x49,0xFF, +0xF0,0xF4,0x80,0x06,0x49,0xFF,0xF0,0xE6,0x46,0x01,0xF2,0x80,0x50,0x00,0x01,0x00, +0x49,0xFF,0xF0,0xE0,0xFA,0x04,0x49,0xFF,0xF2,0x6E,0x46,0x01,0xF1,0x00,0x50,0x00, +0x01,0x00,0x49,0xFF,0xF0,0xD7,0x44,0x00,0x00,0x32,0x49,0xFF,0xF2,0x64,0x84,0x01, +0x49,0x00,0x09,0x00,0x84,0x00,0x49,0x00,0x08,0xE3,0x46,0x10,0x00,0x81,0x44,0x0F, +0xE0,0xFF,0x83,0x81,0xBA,0x1A,0x44,0x74,0x50,0x28,0x92,0x45,0x94,0x95,0x58,0x21, +0x00,0x03,0xBA,0x9A,0xBA,0x1A,0xFE,0x16,0x58,0x00,0x02,0x00,0xB8,0x9A,0xBA,0x1A, +0x46,0x0F,0xFE,0x0F,0x50,0x00,0x0F,0xFF,0xFE,0x16,0x44,0x21,0x00,0x00,0xFE,0x17, +0xB8,0x9A,0xBA,0x1A,0x46,0x0E,0x0F,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x16,0xB8,0x9A, +0xBA,0x19,0x84,0x00,0x92,0x51,0x40,0x21,0x44,0x08,0x58,0x21,0x6D,0x60,0xBA,0x99, +0x84,0x21,0x49,0xFF,0xF1,0x64,0x84,0x01,0x49,0xFF,0xF1,0x6B,0x84,0x01,0x80,0x20, +0x49,0xFF,0xF1,0x12,0x84,0x01,0x49,0xFF,0xF1,0x45,0x84,0x01,0x49,0xFF,0xF1,0x75, +0x49,0xFF,0xF9,0x10,0x80,0x06,0x49,0xFF,0xF0,0x8D,0x49,0xFF,0xF0,0x77,0x46,0x01, +0xF0,0x00,0x50,0x00,0x01,0x00,0x49,0xFF,0xF0,0x85,0x49,0xFF,0xF0,0x6F,0x84,0x00, +0x49,0xFF,0xF4,0xD3,0x84,0x00,0x49,0xFF,0xF4,0xD9,0x84,0x01,0x49,0xFF,0xFE,0xF8, +0x44,0x60,0x08,0x01,0xB4,0x07,0x92,0x01,0x96,0x06,0xC0,0x07,0x8E,0xC1,0x84,0x01, +0x97,0xB1,0x49,0xFF,0xF1,0xF7,0xCE,0xF7,0xFC,0xA0,0xFC,0x00,0x84,0x00,0x49,0xFF, +0xF1,0x9E,0x49,0x00,0x1C,0xF2,0xE6,0x03,0xE8,0x09,0x44,0x16,0x00,0x00,0xB4,0x01, +0x58,0x00,0x01,0x00,0xB6,0x01,0x48,0x00,0x00,0x84,0x44,0x14,0x80,0x04,0x44,0x01, +0x00,0x00,0xB6,0x01,0x44,0x14,0x80,0x84,0xB4,0x41,0xFE,0x17,0xB6,0x01,0x44,0x16, +0x00,0x30,0xB4,0x01,0x58,0x00,0x01,0x00,0xB6,0x01,0x44,0x07,0x21,0x00,0x46,0x10, +0x20,0x00,0xB4,0x40,0xFE,0x57,0xB6,0x20,0xB4,0x40,0x44,0x37,0x21,0x00,0x92,0x59, +0x96,0x86,0xCA,0xFB,0xB4,0x23,0x46,0x0F,0xFF,0x0F,0x50,0x00,0x0F,0xFF,0xFE,0x0E, +0x44,0x16,0x00,0x00,0xFE,0x0F,0xB6,0x03,0x44,0x37,0x21,0x04,0x44,0x02,0x02,0xA1, +0x40,0x00,0x30,0x08,0x40,0x10,0x30,0x09,0xB4,0x03,0x44,0x47,0x21,0x10,0x92,0x14, +0x40,0x00,0x50,0x08,0xFE,0x0F,0xB6,0x03,0x44,0x37,0x21,0x14,0x44,0x16,0x00,0x34, +0xB4,0x03,0x46,0x68,0x00,0x00,0x92,0x14,0x40,0x00,0x50,0x08,0xFE,0x0F,0xB6,0x03, +0x44,0x37,0x21,0x08,0x44,0x17,0x21,0x18,0xB4,0x03,0x92,0x08,0x40,0x00,0x20,0x08, +0x58,0x00,0x00,0x01,0xB6,0x03,0xB4,0x04,0x92,0x08,0x40,0x00,0x20,0x08,0xB6,0x04, +0xB4,0x01,0x92,0x08,0x40,0x00,0x20,0x08,0xB6,0x01,0xB4,0xA3,0x46,0x0F,0xCF,0xFF, +0x50,0x00,0x0F,0xFF,0xFF,0x46,0xB6,0xA3,0xB4,0xA4,0xFF,0x46,0xB6,0xA4,0xB4,0xA1, +0xFE,0x2E,0x44,0x57,0x21,0x1C,0xB6,0x01,0xB4,0x05,0x92,0x11,0x40,0x00,0x44,0x08, +0x58,0x00,0x05,0x77,0xB6,0x05,0xB4,0x03,0xFE,0x37,0xB6,0x03,0xB4,0x04,0x94,0x01, +0x92,0x01,0xB6,0x04,0xB4,0x01,0xFF,0x87,0x44,0x07,0x21,0x20,0xB6,0xC1,0xB6,0x40, +0xB4,0x25,0x46,0x0F,0x0F,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x0E,0xB6,0x05,0x44,0x00, +0x00,0x79,0x3E,0x07,0xF2,0x31,0x44,0x0F,0xFF,0x84,0x3E,0x07,0xF2,0x32,0x84,0x19, +0x3E,0x07,0xF2,0x33,0xFC,0x80,0x84,0x20,0x84,0x40,0x3C,0x1B,0xF9,0x16,0x44,0x42, +0x02,0xA0,0x80,0x22,0x96,0xD1,0xE2,0x60,0xE8,0x06,0x38,0x32,0x08,0x00,0x8C,0x41, +0x88,0x23,0xD5,0xF9,0xFC,0x00,0x44,0x22,0x02,0xA0,0xFE,0x4A,0x96,0x48,0x38,0x11, +0x00,0x08,0x8C,0x01,0x96,0x01,0x49,0xFF,0xF5,0x1C,0x2E,0x07,0xF2,0x30,0x49,0xFF, +0xF5,0x03,0x49,0x00,0x1C,0x3A,0xE6,0x03,0xE9,0x03,0x49,0xFF,0xF4,0x12,0xFC,0x80, +0xFC,0x01,0x84,0x00,0x3E,0x07,0xF8,0x3A,0x84,0x01,0x3E,0x07,0xF0,0x61,0x84,0x20, +0x44,0x00,0x01,0x00,0x49,0xFF,0xE7,0x8E,0x44,0x00,0x01,0x00,0x84,0x20,0x49,0xFF, +0xE7,0x98,0x44,0x00,0x01,0x00,0x84,0x20,0x49,0xFF,0xE7,0xA2,0x44,0x40,0x05,0x78, +0xB6,0x9F,0x44,0x12,0x02,0xA0,0x44,0x22,0x02,0x9E,0x44,0x32,0x02,0x9C,0x44,0x50, +0x00,0x90,0x44,0x02,0x08,0x18,0x49,0xFF,0xF4,0xDB,0x44,0x00,0x3D,0x38,0x49,0xFF, +0xF4,0xF1,0x44,0x00,0x37,0x3E,0x49,0xFF,0xF4,0xF0,0x84,0x00,0x49,0xFF,0xE7,0xA4, +0x84,0x00,0x3C,0x0B,0xF9,0x17,0x44,0x00,0x05,0x77,0x49,0xFF,0xFF,0xA6,0x49,0xFF, +0xFF,0x06,0xFC,0x81,0xFC,0x00,0xC8,0x03,0xFA,0x1A,0xD5,0x03,0x44,0x00,0x05,0x1B, +0x49,0xFF,0xFF,0x9B,0xFC,0x80,0xFC,0x00,0x44,0x00,0x01,0x00,0x84,0x21,0x49,0xFF, +0xE7,0x6F,0x84,0x00,0x3C,0x0F,0xFC,0x80,0x3C,0x0D,0xFC,0x80,0x5C,0xF0,0x00,0x64, +0xE8,0x05,0x3C,0x0D,0xFC,0x80,0x8C,0x01,0xD5,0xF6,0x44,0x00,0x01,0x00,0x84,0x20, +0x49,0xFF,0xE7,0x5E,0xFC,0x80,0x2E,0x07,0xF8,0x3A,0xDD,0x9E,0x2E,0x07,0xF0,0x61, +0xDD,0x9E,0xFC,0x00,0x44,0x10,0x00,0xFF,0x80,0xC0,0x44,0x20,0x05,0x78,0x44,0x02, +0x02,0xA0,0x49,0x00,0x21,0x83,0xCE,0x06,0x44,0x00,0x00,0x5A,0x3E,0x07,0xF2,0x30, +0xFC,0x80,0x44,0x0F,0xFF,0xA7,0x3E,0x07,0xF2,0x30,0x84,0x05,0x3E,0x07,0xF2,0x31, +0xFA,0x0C,0x3E,0x07,0xF2,0x32,0xFA,0x10,0x3E,0x07,0xF2,0x33,0x84,0x1F,0x3E,0x07, +0xF2,0x34,0xFC,0x80,0xFC,0x40,0x84,0x85,0x8C,0x24,0x84,0xA1,0x44,0x32,0x02,0xA0, +0x84,0xD0,0xA6,0x8C,0x04,0x90,0xFF,0xFF,0x5A,0x28,0x01,0x2D,0x22,0x70,0x80,0x01, +0x22,0x20,0x80,0x00,0x90,0xE8,0x90,0x44,0x97,0xDF,0xFE,0xB6,0xFE,0xBF,0x96,0x90, +0xC8,0x17,0x38,0x21,0x94,0x08,0x9C,0xA9,0xA7,0xC8,0x38,0x71,0x88,0x08,0x9C,0xAA, +0xA7,0xCA,0x38,0x71,0x88,0x08,0x40,0x24,0x94,0x0A,0x5E,0xF1,0x01,0x00,0x9D,0xEB, +0xE9,0x03,0x44,0x20,0x00,0xFF,0x96,0x90,0x38,0x21,0x9C,0x08,0xD5,0x0B,0x38,0x21, +0x90,0x08,0x9C,0xA1,0xA7,0xC8,0x38,0x71,0x88,0x08,0x9C,0xA2,0xA7,0xCA,0x38,0x71, +0x88,0x08,0x8C,0x83,0x8C,0x2C,0x8C,0xA4,0x5A,0x48,0x23,0xCD,0xFC,0xC0,0xFA,0xB3, +0x44,0x22,0x02,0xA0,0x44,0x30,0x04,0xA3,0x80,0x80,0x0A,0x10,0x00,0x01,0x92,0x28, +0x38,0x11,0x14,0x08,0x9C,0x69,0xA7,0x20,0x8C,0xA2,0x38,0x41,0x04,0x08,0xDB,0xF5, +0xDD,0x9E,0x44,0x50,0x04,0xA3,0x44,0x22,0x02,0xA0,0x44,0x30,0x05,0x07,0x80,0x80, +0x0A,0x10,0x00,0x01,0x92,0x28,0x38,0x11,0x14,0x08,0x9C,0x69,0xA7,0x20,0x8C,0xA2, +0x38,0x41,0x04,0x08,0xDB,0xF5,0xDD,0x9E,0xC8,0x03,0x3E,0x17,0xF2,0x59,0xDD,0x9E, +0xFC,0x00,0x49,0xFF,0xF4,0x2C,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xF4,0x25,0xFC,0x80, +0xFC,0x00,0x49,0x00,0x07,0x5D,0x2E,0x57,0xF8,0x38,0xCD,0x18,0x84,0x32,0x3E,0x17, +0xF2,0x30,0x44,0x22,0x02,0xA0,0x44,0x30,0x04,0x80,0x80,0x80,0x2A,0x10,0x00,0x01, +0x9D,0xA9,0x90,0x28,0x96,0x48,0x38,0x11,0x18,0x08,0x8C,0xA2,0xA6,0x60,0x38,0x11, +0x14,0x08,0xDB,0xF4,0x44,0x00,0x04,0x81,0xD5,0x12,0x5A,0x58,0x01,0x13,0x84,0x12, +0x3E,0x07,0xF2,0x30,0x44,0x12,0x02,0xA0,0x80,0x05,0x84,0x40,0x9C,0xC1,0x38,0x20, +0x80,0x08,0x8C,0x02,0x38,0x20,0x8C,0x08,0x5A,0x08,0x65,0xFA,0x49,0xFF,0xFE,0xBD, +0xFC,0x80,0xFC,0x00,0x2E,0x07,0xF0,0x60,0x3E,0x07,0xF2,0x30,0x84,0x01,0x49,0xFF, +0xFE,0xB4,0x49,0xFF,0xF3,0x5D,0xFC,0x80,0xFC,0x00,0x3C,0x23,0xF9,0x17,0x2E,0x17, +0xF7,0xA8,0x84,0x00,0x96,0x91,0x3C,0x0F,0xFC,0x8A,0x96,0x48,0x44,0x32,0x08,0x18, +0x5A,0x20,0x03,0x04,0x48,0x00,0x00,0xDB,0x5A,0x18,0x53,0x0F,0x2E,0x07,0xF7,0xA9, +0x2E,0x17,0xF7,0xAA,0x40,0x00,0x20,0x08,0xFE,0x0F,0x5A,0x08,0x48,0x04,0x48,0x00, +0x01,0xF8,0x48,0x00,0x04,0x60,0x5A,0x10,0x01,0x04,0x48,0x00,0x04,0x5C,0x2E,0x17, +0xF7,0xA9,0x5A,0x18,0x01,0x0D,0x2E,0x17,0xF7,0xAA,0x5A,0x18,0x01,0x05,0x3C,0x0F, +0xFE,0x0F,0x84,0x06,0x2E,0x17,0xF7,0xAA,0x48,0x00,0x01,0x2D,0x2E,0x07,0xF7,0xA9, +0x5A,0x08,0x02,0x10,0x2E,0x07,0xF7,0xAA,0x5A,0x08,0x03,0x04,0x48,0x00,0x02,0x57, +0x2E,0x07,0xF7,0xAA,0x4E,0x02,0x02,0x4C,0x2E,0x07,0xF7,0xAA,0x48,0x00,0x00,0xDD, +0x2E,0x07,0xF7,0xA9,0x5A,0x08,0x03,0x0C,0x2E,0x07,0xF7,0xAA,0x4E,0x02,0x04,0x33, +0x2E,0x07,0xF7,0xAA,0x5A,0x08,0x01,0x7A,0x48,0x00,0x04,0x2D,0x2E,0x07,0xF7,0xA9, +0x5A,0x08,0x04,0x12,0x2E,0x07,0xF7,0xAA,0x4E,0x02,0x04,0x25,0x2E,0x07,0xF7,0xAA, +0x5A,0x08,0x01,0x04,0x48,0x00,0x04,0x1F,0x2E,0x07,0xF7,0xAA,0x5A,0x08,0x10,0x66, +0x48,0x00,0x04,0x19,0x2E,0x07,0xF7,0xA9,0x5A,0x08,0x05,0x04,0x48,0x00,0x04,0x13, +0x2E,0x07,0xF7,0xA9,0x5A,0x08,0x06,0x11,0x2E,0x07,0xF7,0xAA,0x5A,0x08,0x01,0x04, +0x3C,0x0F,0xFC,0x1A,0x2E,0x07,0xF7,0xAA,0x4E,0x03,0x04,0x05,0x84,0x00,0x3C,0x0F, +0xFC,0x1A,0x48,0x00,0x04,0x00,0x2E,0x07,0xF7,0xA9,0x5A,0x08,0x07,0x12,0x2E,0x07, +0xF7,0xAA,0x96,0x00,0x5A,0x08,0x01,0x04,0x3C,0x0F,0xFB,0xE4,0x2E,0x07,0xF7,0xAA, +0x4E,0x03,0x03,0xF1,0x84,0x00,0x3C,0x0F,0xFB,0xE4,0x48,0x00,0x03,0xEC,0x2E,0x07, +0xF7,0xA9,0x5A,0x08,0x08,0x07,0x2E,0x07,0xF7,0xAA,0x4E,0x02,0x03,0xE4,0xD5,0x2D, +0x2E,0x07,0xF7,0xA9,0x5A,0x08,0x09,0x04,0x48,0x00,0x03,0xDD,0x2E,0x07,0xF7,0xA9, +0x5A,0x08,0x0A,0x10,0x2E,0x07,0xF7,0xAA,0x4E,0x02,0x01,0xF0,0x2E,0x07,0xF7,0xAA, +0x5A,0x08,0x01,0x04,0x48,0x00,0x01,0xF0,0x2E,0x07,0xF7,0xAA,0x48,0x00,0x00,0xC2, +0x2E,0x07,0xF7,0xA9,0x5A,0x08,0x0B,0x04,0x48,0x00,0x03,0xC5,0x2E,0x07,0xF7,0xA9, +0x5A,0x00,0x0C,0x0A,0x2E,0x07,0xF7,0xA9,0x5A,0x00,0x0F,0x06,0x2E,0x07,0xF7,0xA9, +0x5A,0x08,0x10,0x08,0x2E,0x07,0xF7,0xAA,0x2E,0x07,0xF7,0xAA,0x48,0x00,0x03,0xB3, +0x2E,0x07,0xF7,0xA9,0x5A,0x00,0x11,0x04,0x48,0x00,0x03,0xAD,0x2E,0x07,0xF7,0xAA, +0xC8,0x07,0x3C,0x0D,0xFC,0x19,0x58,0x00,0x00,0x08,0x3C,0x0F,0xFC,0x19,0x2E,0x07, +0xF7,0xAA,0x5A,0x00,0x01,0x04,0x48,0x00,0x03,0x9E,0x3C,0x1D,0xFC,0x19,0x84,0x17, +0xFE,0x0E,0x3C,0x0F,0xFC,0x19,0x48,0x00,0x03,0x96,0x5A,0x28,0x01,0x24,0x5A,0x18, +0x01,0x04,0x48,0x00,0x01,0x41,0x5A,0x18,0x03,0x04,0x48,0x00,0x01,0x6D,0x5A,0x18, +0x20,0x04,0x48,0x00,0x02,0xC4,0x5A,0x18,0x28,0x04,0x48,0x00,0x03,0x1F,0x5A,0x18, +0x21,0x04,0x48,0x00,0x02,0xE3,0x5A,0x18,0x22,0x04,0x48,0x00,0x02,0xFA,0x5A,0x18, +0x23,0x04,0x48,0x00,0x03,0x06,0x5A,0x10,0xF3,0x04,0x48,0x00,0x03,0x74,0x48,0x00, +0x03,0x3B,0x5A,0x20,0x02,0x04,0x48,0x00,0x00,0xB3,0x5A,0x18,0x02,0x14,0x2E,0x07, +0xF7,0xA9,0x5A,0x08,0x03,0x04,0x48,0x00,0x01,0x7A,0x2E,0x07,0xF7,0xA9,0x4E,0x02, +0x01,0x6F,0x2E,0x07,0xF7,0xA9,0x5A,0x00,0x01,0x04,0x48,0x00,0x03,0x5C,0x48,0x00, +0x01,0x60,0x5A,0x18,0xF0,0x1D,0x2E,0x07,0xF7,0xA9,0x4E,0x02,0x01,0x41,0x2E,0x07, +0xF7,0xA9,0x5A,0x08,0x01,0x04,0x48,0x00,0x01,0x7F,0x2E,0x07,0xF7,0xA9,0x5A,0x08, +0x02,0x04,0x48,0x00,0x01,0x39,0x2E,0x07,0xF7,0xA9,0x5A,0x00,0xFA,0x04,0x48,0x00, +0x03,0x42,0x84,0x01,0x3E,0x07,0xF2,0x04,0x48,0x00,0x03,0x3D,0x5A,0x18,0xFA,0x10, +0x2E,0x07,0xF7,0xA9,0x5A,0x08,0x03,0x04,0x48,0x00,0x01,0x70,0x2E,0x07,0xF7,0xA9, +0x5A,0x00,0x08,0x04,0x48,0x00,0x03,0x2F,0x48,0x00,0x01,0x6C,0x5A,0x18,0x01,0x14, +0x2E,0x17,0xF7,0xA9,0x5A,0x18,0x01,0x05,0x3C,0x0F,0xFE,0x0F,0x84,0x06,0x2E,0x17, +0xF7,0xA9,0x4E,0x13,0x00,0xB4,0x84,0x01,0x3C,0x0F,0xFE,0x0F,0x49,0xFF,0xEE,0x0B, +0x48,0x00,0x01,0x18,0x5A,0x18,0x0A,0x14,0x2E,0x07,0xF7,0xA9,0x4E,0x02,0x01,0x2E, +0x2E,0x07,0xF7,0xA9,0x5A,0x08,0x01,0x04,0x48,0x00,0x01,0x2E,0x2E,0x07,0xF7,0xA9, +0x5A,0x00,0x02,0x04,0x48,0x00,0x03,0x07,0x48,0x00,0x01,0x2F,0x5A,0x18,0x06,0x0C, +0x2E,0x07,0xF7,0xA9,0x5A,0x08,0x01,0x04,0x3C,0x0F,0xFC,0x1A,0x2E,0x07,0xF7,0xA9, +0x48,0xFF,0xFE,0xF4,0x5A,0x18,0x07,0x0D,0x2E,0x07,0xF7,0xA9,0x96,0x00,0x5A,0x08, +0x01,0x04,0x3C,0x0F,0xFB,0xE4,0x2E,0x07,0xF7,0xA9,0x48,0xFF,0xFE,0xFB,0x5A,0x18, +0x0D,0x04,0x48,0x00,0x02,0xE8,0x5A,0x18,0x60,0x0C,0x2E,0x07,0xF7,0xA9,0x5A,0x08, +0x02,0x04,0x48,0x00,0x02,0xE0,0x2E,0x07,0xF7,0xA9,0x48,0x00,0x02,0xDC,0x5A,0x18, +0xF6,0x0A,0x2E,0x07,0xF7,0xA9,0x5A,0x00,0xF2,0x04,0x48,0x00,0x02,0xD4,0x48,0x00, +0x02,0xA0,0x5A,0x10,0x61,0x04,0x48,0x00,0x02,0xCE,0x2E,0x17,0xF7,0xA9,0x96,0x48, +0xC1,0x08,0x2E,0x17,0xF7,0xA9,0x96,0x48,0x5A,0x10,0x01,0x04,0x48,0x00,0x02,0xC3, +0x44,0x00,0x01,0x00,0x49,0xFF,0xE4,0xCC,0x48,0x00,0x02,0xBD,0x5A,0x20,0x10,0x04, +0x48,0x00,0x02,0xB9,0x5A,0x10,0xF1,0x04,0x48,0x00,0x02,0xA3,0x84,0x21,0x3C,0x1F, +0xFC,0x8A,0x44,0x12,0x02,0x88,0x38,0x21,0x80,0x00,0x38,0x20,0x80,0x08,0x8C,0x01, +0x5A,0x08,0x10,0xFB,0x2E,0x07,0xF7,0xA9,0x2E,0x57,0xF7,0xAA,0x96,0x00,0x96,0x68, +0x40,0x50,0x20,0x08,0x5A,0x00,0x10,0x06,0x5A,0x00,0x1D,0x04,0xFF,0x4F,0xD5,0x02, +0x97,0x69,0x44,0x00,0x08,0x00,0x4C,0x50,0x00,0xF7,0x5C,0xF2,0x88,0x01,0xE8,0x0F, +0x44,0x00,0x04,0x00,0x4C,0x50,0x00,0xDD,0x44,0x00,0x05,0x00,0x4C,0x50,0x00,0xE4, +0x5A,0x51,0x00,0x04,0x48,0x00,0x02,0x87,0x48,0x00,0x00,0xCB,0x44,0x00,0x30,0x00, +0x4C,0x50,0x01,0x04,0x5C,0xF2,0xB0,0x01,0xE8,0x07,0x44,0x00,0x1D,0x00,0x4C,0x50, +0x00,0xE3,0x48,0x00,0x02,0x78,0x44,0x00,0x32,0x00,0x4C,0x50,0x00,0xE5,0x44,0x00, +0x33,0x00,0x4C,0x50,0x00,0xE9,0x48,0x00,0x02,0x6E,0x5A,0x08,0x01,0x1B,0x44,0x0F, +0xFF,0xA7,0x3E,0x07,0xF2,0x30,0x84,0x05,0x3E,0x07,0xF2,0x31,0xFA,0x0C,0x3E,0x07, +0xF2,0x32,0xFA,0x22,0xFA,0x10,0x3E,0x07,0xF2,0x33,0x3E,0x07,0xF2,0x34,0x3E,0x17, +0xF2,0x35,0x3E,0x07,0xF2,0x36,0x3E,0x17,0xF2,0x37,0x84,0x08,0x48,0x00,0x02,0x3C, +0x5A,0x08,0x02,0x30,0x2E,0x07,0xF8,0x3A,0x44,0x1F,0xFF,0x81,0x3E,0x17,0xF2,0x30, +0x3E,0x07,0xF2,0x31,0xC8,0x0D,0x44,0x10,0x00,0x5A,0x3E,0x17,0xF2,0x32,0x3E,0x07, +0xF2,0x33,0xFA,0x1B,0x3E,0x07,0xF2,0x34,0x84,0x05,0x48,0x00,0x02,0x25,0x44,0x0F, +0xFF,0xA7,0x3E,0x07,0xF2,0x32,0x84,0x05,0x3E,0x07,0xF2,0x33,0xFA,0x0C,0x3E,0x07, +0xF2,0x34,0xFA,0x22,0xFA,0x10,0x3E,0x07,0xF2,0x35,0x3E,0x17,0xF2,0x36,0x3E,0x07, +0xF2,0x37,0x3E,0x17,0xF2,0x38,0x3E,0x07,0xF2,0x39,0x84,0x0A,0x48,0x00,0x02,0x0C, +0x5A,0x08,0x05,0x0C,0x84,0x20,0x84,0x01,0x3E,0x07,0xF2,0x30,0x3E,0x17,0xF2,0x31, +0x3E,0x17,0xF2,0x32,0x48,0x00,0x01,0x9F,0x5A,0x08,0x03,0x04,0x84,0x00,0xD5,0x04, +0x5A,0x08,0x04,0x07,0x84,0x02,0x3E,0x07,0xF8,0x3A,0x48,0x00,0x02,0x0C,0x5A,0x08, +0x06,0x07,0x84,0x01,0x3E,0x07,0xF1,0xFC,0x48,0x00,0x02,0x05,0x5A,0x08,0x07,0x07, +0x84,0x01,0x3E,0x07,0xF1,0xFB,0x48,0x00,0x01,0xFE,0x5A,0x08,0x08,0x07,0x84,0x01, +0x3E,0x07,0xF1,0xFA,0x48,0x00,0x01,0xF7,0x5A,0x08,0x09,0x07,0x84,0x01,0x3E,0x07, +0xF1,0xF9,0x48,0x00,0x01,0xF0,0x5A,0x08,0x0A,0x07,0x84,0x01,0x3E,0x07,0xF1,0xF8, +0x48,0x00,0x01,0xE9,0x5A,0x08,0x0B,0x06,0x84,0x01,0x3E,0x07,0xF1,0xF6,0xD5,0x0F, +0x5A,0x08,0x0C,0x09,0x84,0x01,0x3E,0x07,0xF1,0xF7,0x3E,0x07,0xF1,0xF5,0x48,0x00, +0x01,0xDA,0x5A,0x08,0x0D,0x07,0x84,0x01,0x3E,0x07,0xF1,0xF7,0x84,0x00,0xD5,0xF6, +0x5A,0x08,0x14,0x0A,0x84,0x01,0x3E,0x07,0xF8,0x3A,0x84,0x01,0x49,0xFF,0xEE,0xDE, +0x48,0x00,0x01,0xC9,0x5A,0x08,0x29,0x04,0x84,0x01,0xD5,0x04,0x5A,0x08,0x2A,0x07, +0x84,0x02,0x3E,0x07,0xF0,0x61,0x48,0x00,0x01,0xBE,0x5A,0x08,0x15,0x08,0x44,0x00, +0x00,0x50,0x3E,0x07,0xF0,0x60,0x84,0x01,0xD5,0x08,0x5A,0x08,0x18,0x0B,0x44,0x00, +0x00,0x50,0x3E,0x07,0xF0,0x60,0x84,0x04,0x3E,0x07,0xF8,0x39,0x48,0x00,0x01,0xAB, +0x5A,0x08,0x19,0x08,0x44,0x00,0x00,0x50,0x3E,0x07,0xF0,0x60,0x84,0x05,0xD5,0xF5, +0x5A,0x08,0x1D,0x08,0x44,0x00,0x00,0x50,0x3E,0x07,0xF0,0x60,0x84,0x09,0xD5,0xED, +0x5A,0x08,0x2E,0x08,0x44,0x00,0x00,0x50,0x3E,0x07,0xF0,0x60,0x84,0x0F,0xD5,0xE5, +0x5A,0x08,0x2F,0x08,0x44,0x00,0x00,0x50,0x3E,0x07,0xF0,0x60,0xFA,0x00,0xD5,0xDD, +0x5A,0x08,0x31,0x08,0x44,0x00,0x00,0x50,0x3E,0x07,0xF0,0x60,0xFA,0x02,0xD5,0xD5, +0x5A,0x00,0x32,0x04,0x48,0x00,0x00,0xA1,0x84,0x11,0x3E,0x07,0xF2,0x30,0x44,0x00, +0x00,0x30,0x3E,0x07,0xF2,0x31,0x84,0x00,0x3E,0x07,0xF2,0x32,0x2E,0x07,0xF0,0xEC, +0x96,0x00,0xC0,0x30,0x46,0x61,0xFF,0xF9,0x50,0x03,0x05,0x27,0x49,0xFF,0xEB,0x6A, +0x50,0x03,0x04,0x87,0x49,0xFF,0xEB,0x66,0x46,0x01,0xFF,0xFF,0x50,0x00,0x0F,0x0E, +0x49,0xFF,0xEB,0x60,0x46,0x02,0xF4,0x50,0x50,0x00,0x01,0x00,0x44,0x10,0x11,0x11, +0x49,0xFF,0xF4,0x0D,0x46,0x02,0xF4,0x60,0x44,0x10,0x22,0x22,0x50,0x00,0x01,0x00, +0x49,0xFF,0xF4,0x05,0x50,0x03,0x05,0x00,0x49,0xFF,0xEB,0x4C,0x50,0x03,0x04,0x00, +0x49,0xFF,0xEB,0x48,0x2E,0x07,0xF2,0x14,0x3E,0x07,0xF2,0x33,0x2E,0x07,0xF2,0x10, +0xD5,0x04,0x84,0x2A,0x3E,0x17,0xF2,0x33,0x3E,0x07,0xF2,0x34,0x84,0x03,0x3E,0x07, +0xF2,0x35,0x84,0x3F,0x84,0x00,0x84,0x82,0x84,0x50,0x3E,0x47,0xF2,0x36,0x44,0x5F, +0xFF,0x90,0x3E,0x17,0xF2,0x37,0x84,0x6A,0x3E,0x17,0xF2,0x38,0x44,0x60,0x00,0x64, +0x3E,0x07,0xF2,0x39,0x3E,0x27,0xF2,0x3A,0x84,0x41,0x3E,0x37,0xF2,0x3B,0x3E,0x07, +0xF2,0x3C,0x3E,0x67,0xF2,0x3D,0x3E,0x27,0xF2,0x3E,0x3E,0x57,0xF2,0x3F,0x3E,0x07, +0xF2,0x40,0x3E,0x67,0xF2,0x41,0x3E,0x27,0xF2,0x42,0x3E,0x57,0xF2,0x43,0x44,0x5F, +0xFF,0xC0,0x3E,0x27,0xF2,0x44,0x3E,0x57,0xF2,0x45,0x3E,0x47,0xF2,0x46,0x44,0x4F, +0xFF,0x80,0x3E,0x47,0xF2,0x47,0x84,0x8F,0x3E,0x07,0xF2,0x48,0x3E,0x07,0xF2,0x49, +0x3E,0x07,0xF2,0x4A,0x3E,0x27,0xF2,0x4B,0x3E,0x47,0xF2,0x4C,0x84,0x84,0x3E,0x47, +0xF2,0x4D,0xFA,0x9D,0x3E,0x47,0xF2,0x4E,0x3E,0x37,0xF2,0x4F,0x3E,0x27,0xF2,0x50, +0x84,0x4C,0x3E,0x27,0xF2,0x51,0x84,0x46,0x3E,0x27,0xF2,0x52,0x3E,0x07,0xF2,0x53, +0x3E,0x17,0xF2,0x54,0xFA,0x18,0x3E,0x17,0xF2,0x55,0x3E,0x17,0xF2,0x56,0x3E,0x17, +0xF2,0x57,0x48,0x00,0x00,0xC9,0x5A,0x08,0x33,0x12,0x84,0x0F,0x3E,0x07,0xF2,0x30, +0x84,0x01,0x3E,0x07,0xF2,0x31,0x44,0x04,0x50,0x28,0xB4,0x00,0x92,0x01,0x96,0x06, +0x3E,0x07,0xF2,0x32,0x84,0x03,0x48,0x00,0x00,0xB7,0x5A,0x08,0x34,0x06,0x49,0xFF, +0xF9,0x51,0x48,0x00,0x00,0xC8,0x5A,0x08,0x22,0x27,0x84,0x00,0xFA,0x30,0x84,0x7F, +0x84,0x47,0x3E,0x17,0xF2,0x30,0x3E,0x07,0xF2,0x31,0x3E,0x07,0xF2,0x32,0x3E,0x37, +0xF2,0x33,0x3E,0x27,0xF2,0x34,0x3E,0x37,0xF2,0x35,0x3E,0x27,0xF2,0x36,0x84,0x6A, +0xFA,0x42,0x3E,0x27,0xF2,0x37,0x3E,0x17,0xF2,0x38,0x3E,0x37,0xF2,0x39,0x3E,0x07, +0xF2,0x3A,0x3E,0x27,0xF2,0x3B,0x3E,0x17,0xF2,0x3C,0x3E,0x07,0xF2,0x3D,0x84,0x0E, +0x48,0x00,0x00,0x8A,0x5A,0x08,0x23,0x1B,0xFA,0x11,0x3E,0x07,0xF2,0x30,0x44,0x20, +0x00,0x69,0x84,0x00,0x84,0x26,0x3E,0x07,0xF2,0x31,0x3E,0x27,0xF2,0x32,0x3E,0x17, +0xF2,0x33,0x3E,0x07,0xF2,0x34,0x3E,0x07,0xF2,0x35,0x3E,0x27,0xF2,0x36,0x3E,0x17, +0xF2,0x37,0x3E,0x07,0xF2,0x38,0x84,0x09,0xD5,0x6E,0x5A,0x08,0x24,0x10,0xFA,0x12, +0x3E,0x07,0xF2,0x30,0x84,0x05,0x3E,0x07,0xF2,0x31,0x84,0x04,0x3E,0x07,0xF2,0x32, +0x84,0x00,0x3E,0x07,0xF2,0x33,0x84,0x04,0xD5,0x5E,0x5A,0x08,0x25,0x0D,0xFA,0x13, +0x3E,0x07,0xF2,0x30,0x84,0x01,0x3E,0x07,0xF2,0x31,0x84,0x00,0x3E,0x07,0xF2,0x32, +0x84,0x05,0xD5,0xF0,0x5A,0x08,0x26,0x2E,0xFA,0x18,0x3E,0x07,0xF2,0x30,0x84,0x46, +0x84,0x00,0x84,0x2C,0x84,0x62,0x3E,0x37,0xF2,0x31,0x3E,0x07,0xF2,0x32,0x3E,0x27, +0xF2,0x33,0x3E,0x17,0xF2,0x34,0x3E,0x37,0xF2,0x35,0x3E,0x07,0xF2,0x36,0x3E,0x27, +0xF2,0x37,0x3E,0x17,0xF2,0x38,0x84,0x44,0x84,0x21,0x3E,0x17,0xF2,0x39,0x3E,0x07, +0xF2,0x3A,0x3E,0x27,0xF2,0x3B,0x3E,0x07,0xF2,0x3C,0x3E,0x17,0xF2,0x3D,0x84,0x23, +0x3E,0x07,0xF2,0x3E,0x3E,0x17,0xF2,0x3F,0x3E,0x07,0xF2,0x40,0xFA,0x01,0xD5,0x23, +0x5A,0x08,0x27,0x05,0x49,0xFF,0xFB,0xAF,0xD5,0x35,0x5A,0x08,0x2D,0x05,0x49,0xFF, +0xFB,0x79,0xD5,0x30,0x5A,0x08,0x28,0x2F,0x84,0x1E,0x3E,0x07,0xF2,0x30,0x84,0x01, +0x3E,0x07,0xF2,0x31,0x84,0x02,0x3E,0x07,0xF2,0x32,0x84,0x03,0x3E,0x07,0xF2,0x33, +0x84,0x04,0x3E,0x07,0xF2,0x34,0x84,0x05,0x3E,0x07,0xF2,0x35,0x84,0x06,0x3E,0x07, +0xF2,0x36,0x84,0x07,0x49,0xFF,0xFA,0x49,0x49,0xFF,0xEE,0xF2,0xD5,0x13,0x5A,0x18, +0x0F,0x12,0x2E,0x07,0xF7,0xA9,0x5A,0x08,0x01,0x04,0x48,0xFF,0xFF,0x30,0x2E,0x07, +0xF7,0xA9,0x5A,0x08,0x02,0x08,0x2E,0x07,0xF7,0xAB,0x3E,0x07,0xF2,0x0C,0x48,0xFF, +0xFF,0x38,0xFC,0x80,0xFC,0x00,0x2E,0x07,0xF0,0x60,0x5A,0x08,0x52,0x06,0x44,0x00, +0x00,0x51,0x3E,0x07,0xF0,0x60,0x2E,0x07,0xF8,0x39,0x5A,0x08,0x01,0x08,0x84,0xC0, +0x3E,0x67,0xF8,0x38,0x49,0x00,0x09,0x75,0xD5,0x2A,0x5A,0x08,0x04,0x08,0x84,0xC0, +0x3E,0x67,0xF8,0x38,0x49,0x00,0x0B,0x8E,0xD5,0x22,0x5A,0x08,0x05,0x08,0x84,0xC0, +0x3E,0x67,0xF8,0x38,0x49,0x00,0x0F,0x68,0xD5,0x1A,0x5A,0x00,0x09,0xFA,0x5A,0x08, +0x0F,0x0E,0x2E,0x07,0xF7,0xAA,0x84,0xC0,0x2E,0x17,0xF7,0xAB,0x2E,0x27,0xF7,0xAC, +0x3E,0x67,0xF8,0x38,0x49,0x00,0x10,0xDD,0xD5,0x0A,0x54,0x00,0x00,0xFD,0x5A,0x08, +0x10,0x09,0x84,0xC0,0x3E,0x67,0xF8,0x38,0x49,0x00,0x12,0x22,0x3E,0x67,0xF8,0x39, +0xFC,0x80,0xFC,0x00,0x44,0x00,0x00,0x64,0x49,0xFF,0xDE,0x85,0x44,0x00,0x0D,0xE9, +0x49,0xFF,0xDE,0x8C,0x44,0x00,0x09,0xE9,0x49,0xFF,0xDE,0x93,0x44,0x00,0x01,0x90, +0x49,0xFF,0xDE,0x9A,0xFC,0x80,0xFC,0x40,0x51,0xFF,0xFA,0xE0,0x84,0x20,0xFA,0x50, +0xB0,0x10,0x49,0x00,0x1B,0xEB,0xB1,0x98,0x44,0x00,0x82,0xC8,0x80,0x26,0x3A,0x20, +0x14,0x04,0x3A,0x20,0x94,0x24,0x3A,0x20,0x14,0x00,0xB1,0xE0,0x44,0x00,0x82,0xE8, +0x3A,0x20,0x94,0x20,0x80,0x27,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24,0x3A,0x20, +0x14,0x00,0x3A,0x20,0x94,0x20,0x44,0x10,0x83,0x08,0xB0,0x0C,0x3A,0x20,0x94,0x00, +0x44,0x10,0x83,0x18,0x3A,0x20,0x14,0x20,0xB0,0x07,0x3A,0x20,0x8C,0x00,0x3A,0x20, +0x0C,0x20,0x44,0x00,0x83,0x20,0x50,0x8F,0x80,0x24,0x3A,0x20,0x10,0x00,0x3A,0x24, +0x10,0x20,0x85,0x40,0x39,0xC4,0x28,0x00,0x40,0x0E,0x00,0x13,0xC0,0x13,0x5A,0x00, +0x03,0x12,0xB0,0x10,0x38,0x00,0x72,0x02,0xC8,0x0D,0x38,0x03,0x72,0x02,0x40,0x90, +0x04,0x08,0x49,0xFF,0xED,0xF7,0x49,0xFF,0xF1,0x25,0xB0,0x50,0x88,0x09,0x38,0x00, +0xF2,0x0A,0x8D,0x41,0x5A,0xA8,0x0C,0xE8,0x84,0xC0,0xB0,0x10,0x38,0x00,0x1A,0x02, +0xF0,0x81,0xC8,0x07,0x8C,0xC1,0x5A,0x68,0x08,0xFA,0x51,0xFF,0x85,0x20,0xFC,0xC0, +0xB0,0x07,0x87,0x80,0x38,0xA3,0x00,0x00,0x40,0x95,0x04,0x08,0x40,0x04,0x80,0x13, +0xF0,0x82,0x94,0x01,0xF0,0x83,0x40,0xFE,0x24,0x07,0xE8,0xED,0x4F,0xC3,0x00,0x13, +0x38,0x83,0x9A,0x01,0x49,0xFF,0xDD,0x25,0x44,0x20,0x01,0x20,0x80,0x28,0x42,0x10, +0x08,0x73,0xB0,0x0C,0x96,0x49,0x38,0x20,0x19,0x01,0xB0,0x28,0x92,0x42,0x49,0xFF, +0xDD,0x7F,0x4D,0xC5,0x40,0x14,0x38,0x83,0x9A,0x01,0x49,0xFF,0xDD,0x12,0x44,0x20, +0x01,0x20,0x50,0x14,0x02,0x40,0x42,0x10,0x08,0x73,0xB0,0x0C,0x96,0x49,0x38,0x20, +0x19,0x01,0xB0,0x28,0x92,0x42,0x49,0xFF,0xDD,0x6B,0x40,0x14,0xF0,0x01,0x8E,0x21, +0x94,0x4C,0x40,0x0E,0x00,0x13,0x97,0x49,0x40,0x1E,0x28,0x06,0x94,0xC4,0x84,0x40, +0xF1,0x84,0x50,0x8F,0x80,0xA0,0xF4,0x04,0x40,0x11,0x04,0x09,0xC4,0x03,0x88,0x23, +0xD5,0x03,0x88,0x25,0x96,0x49,0x38,0x44,0x06,0x02,0x54,0xF2,0x00,0x1F,0x5A,0xF0, +0x1F,0x1A,0x40,0xF2,0x18,0x09,0x54,0xF7,0x80,0x1F,0x5A,0xF0,0x1F,0x14,0x04,0xFF, +0x80,0x01,0x39,0xE7,0x81,0x01,0x50,0xFF,0x5D,0xEB,0x5C,0xF7,0x82,0x13,0xE9,0x06, +0x50,0xFF,0x64,0x27,0x5C,0xF7,0x82,0x13,0xE8,0x05,0x50,0x42,0x00,0x41,0x38,0x44, +0x06,0x0A,0x38,0x44,0x06,0x02,0x40,0xF2,0x48,0x09,0x54,0xF7,0x80,0x1F,0x5A,0xF0, +0x1F,0x21,0x40,0xF2,0x30,0x09,0x54,0xF7,0x80,0x1F,0x5A,0xF0,0x1F,0x1B,0x04,0xFF, +0x80,0x02,0x05,0xEF,0x80,0x01,0x40,0xF0,0x3C,0x00,0x40,0xF7,0x80,0x13,0x39,0xEF, +0x3D,0x01,0x50,0xFF,0x5D,0xEB,0x5C,0xF7,0x82,0x13,0xE9,0x06,0x50,0xFF,0x64,0x27, +0x5C,0xF7,0x82,0x13,0xE8,0x06,0x44,0xF4,0x10,0x00,0x88,0x8F,0x38,0x44,0x06,0x0A, +0xF1,0x03,0x8C,0x42,0x88,0x01,0x96,0x90,0x96,0x01,0x5A,0x28,0x20,0xAE,0x50,0x5E, +0x00,0x01,0x4C,0x55,0x40,0x16,0x38,0x13,0x9A,0x01,0xF5,0x85,0xF1,0x84,0x49,0xFF, +0xDC,0x98,0x44,0x20,0x01,0x20,0xF1,0x04,0x42,0x10,0x08,0x73,0xB0,0x0C,0x96,0x49, +0x38,0x20,0x19,0x01,0x80,0x08,0x92,0x42,0x49,0xFF,0xDD,0x0E,0xF5,0x05,0x4C,0x54, +0xC0,0x16,0x38,0x13,0x9A,0x01,0xF1,0x84,0x49,0xFF,0xDC,0x83,0xF1,0x04,0x44,0x20, +0x01,0x20,0x50,0x10,0x82,0x40,0x42,0x10,0x08,0x73,0xB0,0x0C,0x96,0x49,0x38,0x20, +0x19,0x01,0x80,0x08,0x92,0x42,0x49,0xFF,0xDC,0xF7,0x50,0x5E,0x00,0x01,0x55,0xC2, +0x80,0xFF,0x48,0xFF,0xFF,0x42,0xFC,0x49,0x44,0x10,0x83,0x2C,0xB1,0x86,0x80,0x06, +0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x8C,0x00,0x3A,0x20,0x0C,0x20, +0x84,0x20,0xB0,0x0C,0xFA,0x48,0x49,0x00,0x1A,0xB9,0x84,0x21,0x10,0x1F,0x80,0x31, +0x10,0x1F,0x80,0x3D,0x84,0x02,0x44,0x10,0x83,0x44,0x10,0x0F,0x80,0x32,0x10,0x0F, +0x80,0x3E,0x80,0x01,0x80,0x9F,0x3A,0x20,0x0C,0x04,0x3A,0x22,0x0C,0x24,0xB1,0xC3, +0x3A,0x20,0x8C,0x04,0xA4,0x00,0x80,0x27,0xAC,0x20,0x3A,0x20,0x8C,0x24,0xAC,0x08, +0x80,0x06,0x49,0xFF,0xE2,0xC9,0xB0,0x0C,0x49,0xFF,0xE3,0x19,0x80,0x1F,0x49,0xFF, +0xE2,0xDC,0x80,0x07,0x81,0x3F,0x49,0xFF,0xE3,0x2B,0xFC,0xC9,0xFC,0x26,0x44,0x10, +0x83,0x50,0x80,0xDF,0x3A,0x20,0x94,0x04,0x3A,0x23,0x14,0x24,0x3A,0x20,0x94,0x04, +0x3A,0x23,0x14,0x24,0x3A,0x20,0x94,0x00,0x80,0x1F,0x80,0xE6,0x3A,0x23,0x14,0x20, +0x49,0xFF,0xE2,0x74,0xFC,0xA6,0xFC,0x2C,0x44,0x10,0x83,0x80,0xB1,0x8C,0x80,0x06, +0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24, +0x3A,0x20,0x94,0x00,0x3A,0x20,0x14,0x20,0x84,0x20,0xB0,0x06,0xFA,0x48,0x49,0x00, +0x1A,0x65,0xB1,0xC1,0x44,0x10,0x83,0xB0,0x80,0x07,0x3A,0x20,0x94,0x04,0x3A,0x20, +0x14,0x24,0xB4,0x21,0xB6,0x20,0x80,0x06,0x49,0xFF,0xE2,0x50,0xB0,0x06,0x49,0xFF, +0xE2,0x6A,0x80,0x07,0x49,0xFF,0xE2,0x54,0xFC,0xAC,0xFC,0x01,0x44,0x10,0x83,0xC4, +0x80,0x1F,0x3A,0x20,0x8C,0x00,0x3A,0x2F,0x8C,0x20,0x49,0xFF,0xE2,0x53,0xFC,0x81, +0xFC,0x00,0xC8,0x04,0x49,0xFF,0xFE,0x3F,0xFC,0x80,0x44,0x00,0x00,0x64,0x49,0xFF, +0xDC,0xC2,0x44,0x00,0x13,0xA6,0x49,0xFF,0xDC,0xC9,0x44,0x00,0x0F,0xA6,0x49,0xFF, +0xDC,0xD0,0x44,0x00,0x01,0x90,0x49,0xFF,0xDC,0xD7,0xFC,0x80,0xFC,0x00,0xE6,0x06, +0xE8,0x06,0x44,0x10,0x84,0x64,0x38,0x00,0x80,0x00,0xD5,0x02,0x84,0x00,0x49,0xFF, +0xDC,0xE1,0xFC,0x80,0xFC,0x00,0xE6,0x06,0xE8,0x06,0x44,0x10,0x84,0x5C,0x38,0x00, +0x80,0x00,0xD5,0x02,0x84,0x00,0x8C,0x01,0x96,0x00,0x49,0xFF,0xDC,0xC8,0xFC,0x80, +0xFC,0x00,0x5A,0x00,0x01,0x0E,0xC0,0x04,0x5A,0x00,0x02,0x1C,0xFC,0x80,0x84,0x00, +0x49,0xFF,0xE8,0xB9,0x2E,0x07,0xF8,0x43,0x49,0xFF,0xFF,0xC4,0xD5,0x2A,0x49,0xFF, +0xE8,0xB2,0x44,0x00,0x00,0x64,0x49,0xFF,0xDC,0x86,0x44,0x00,0x17,0x8E,0x49,0xFF, +0xDC,0x8D,0x44,0x00,0x17,0x8E,0x49,0xFF,0xDC,0x94,0x44,0x00,0x01,0x90,0xD5,0x17, +0x84,0x01,0x49,0xFF,0xE8,0xA0,0x44,0x00,0x03,0xE8,0x49,0xFF,0xDC,0x74,0x44,0x00, +0x27,0x10,0x49,0xFF,0xDC,0x86,0x46,0x20,0x00,0x81,0x46,0x12,0x71,0x00,0x83,0x82, +0xB8,0x10,0x96,0x01,0xFE,0x0F,0xB8,0x90,0x44,0x00,0x27,0x10,0x49,0xFF,0xDC,0x84, +0x84,0x00,0x49,0xFF,0xFF,0xB9,0xFC,0x80,0xE6,0x04,0xE8,0x24,0xFC,0x00,0x44,0xF0, +0x4B,0x1C,0x38,0x07,0x80,0x00,0x40,0xF0,0x3C,0x00,0xDD,0x0F,0x04,0x14,0x1E,0x22, +0x84,0x00,0x49,0xFF,0xDF,0x6B,0x84,0x01,0x49,0xFF,0xDF,0x56,0x84,0x01,0xD5,0x0F, +0x84,0x00,0x49,0xFF,0xDF,0x63,0x84,0x01,0xD5,0x07,0x84,0x01,0xD5,0x02,0x84,0x00, +0x49,0xFF,0xDF,0x5C,0x84,0x00,0x49,0xFF,0xDF,0x47,0x84,0x00,0x49,0xFF,0xDF,0x4D, +0xFC,0x80,0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xEB,0xFD,0x80,0x60,0x3E,0x00,0x0B,0xB0, +0x44,0x12,0x13,0x20,0x44,0x02,0x17,0xA0,0x44,0x20,0x02,0x40,0x2E,0x47,0xF8,0x42, +0x49,0xFF,0xF3,0x09,0xFC,0x80,0x44,0x02,0x17,0xA0,0xDD,0x9E,0x44,0x02,0x13,0x20, +0xDD,0x9E,0x44,0x02,0x09,0x18,0xDD,0x9E,0x44,0x02,0x08,0xB4,0xDD,0x9E,0xFC,0x00, +0xC8,0x05,0x49,0xFF,0xDC,0x73,0x84,0xC3,0xD5,0x05,0x84,0x01,0x49,0xFF,0xDC,0x6E, +0x84,0xC2,0x84,0x01,0x49,0xFF,0xDC,0x5C,0x9E,0x32,0x96,0x00,0x49,0xFF,0xEA,0xB0, +0xFC,0x80,0xFC,0x00,0x84,0x00,0x80,0x20,0x49,0xFF,0xDE,0x6A,0x84,0x00,0x84,0x21, +0x49,0xFF,0xDE,0x66,0xFC,0x80,0xFC,0x00,0x84,0x01,0x84,0x20,0x49,0xFF,0xDE,0x60, +0x84,0x01,0x80,0x20,0x49,0xFF,0xDE,0x5C,0xFC,0x80,0xFC,0x49,0x80,0x20,0x44,0x00, +0x83,0xCC,0x50,0xAF,0x80,0x0C,0x3A,0x20,0x10,0x00,0x44,0x00,0x83,0xD8,0x3A,0x2F, +0x90,0x20,0x3A,0x20,0x10,0x00,0x3A,0x25,0x10,0x20,0x44,0x20,0x83,0xE4,0xB0,0x06, +0x3A,0x21,0x10,0x00,0x3A,0x20,0x10,0x20,0x44,0x20,0x83,0xF0,0x50,0x9F,0x80,0x24, +0x3A,0x21,0x10,0x00,0x3A,0x24,0x90,0x20,0x44,0x20,0x83,0x20,0xB1,0xCC,0x3A,0x21, +0x10,0x00,0x3A,0x23,0x90,0x20,0x44,0x20,0x83,0xFC,0xB1,0x8F,0x3A,0x21,0x10,0x00, +0x80,0xBF,0x3A,0x23,0x10,0x20,0xC9,0x03,0x80,0x05,0xD5,0x12,0x5A,0x18,0x01,0x04, +0x80,0x0A,0xD5,0x0E,0x5A,0x10,0x02,0x0D,0x5A,0x18,0x03,0x04,0x80,0x09,0xD5,0x08, +0x5A,0x18,0x04,0x04,0x80,0x07,0xD5,0x04,0x5A,0x18,0x05,0x06,0x80,0x06,0x49,0xFF, +0xDB,0x70,0xFC,0xC9,0x5A,0x10,0x06,0xEA,0xFC,0xC9,0xFC,0x03,0x84,0x20,0x84,0x4C, +0x80,0xC0,0x80,0x1F,0x49,0x00,0x19,0x22,0x44,0x10,0x84,0x08,0xB0,0x03,0x3A,0x20, +0x90,0x00,0x3A,0x20,0x10,0x20,0xCE,0x03,0x80,0x1F,0xD5,0x03,0x5A,0x68,0x01,0x04, +0x49,0xFF,0xDB,0x6B,0xFC,0x83,0xFC,0x00,0x84,0x00,0x49,0xFF,0xFF,0xA0,0x84,0x00, +0x49,0xFF,0xFF,0xE5,0x84,0x00,0x49,0xFF,0xDE,0x51,0x84,0x00,0x49,0xFF,0xDE,0x80, +0x49,0xFF,0xFE,0x3B,0x49,0xFF,0xFE,0x89,0x49,0xFF,0xFE,0xB1,0x84,0x00,0x49,0xFF, +0xE4,0x6B,0x44,0x00,0x02,0x40,0x49,0xFF,0xE4,0x7D,0x44,0x00,0x02,0x40,0x49,0xFF, +0xE4,0x6E,0x84,0x09,0x49,0xFF,0xE4,0x81,0x84,0x03,0x49,0xFF,0xE4,0x87,0x44,0x62, +0x02,0x0C,0x44,0x10,0x00,0xF0,0x84,0x00,0x49,0xFF,0xE3,0x0F,0x49,0xFF,0xF2,0xBC, +0x38,0x13,0x01,0x01,0x84,0x00,0x49,0xFF,0xE1,0xB5,0x44,0x10,0x00,0xF0,0x84,0x02, +0x49,0xFF,0xE3,0x03,0x49,0xFF,0xF2,0xB0,0x38,0x13,0x01,0x01,0x84,0x02,0x49,0xFF, +0xE1,0xA9,0x44,0x10,0x00,0xF0,0x84,0x05,0x49,0xFF,0xE2,0xF7,0x49,0xFF,0xF2,0xA4, +0x38,0x13,0x01,0x01,0x46,0x60,0x00,0x81,0x84,0x03,0x49,0xFF,0xE1,0x9B,0x49,0xFF, +0xDF,0x35,0x04,0x03,0x03,0x91,0x92,0x0A,0x40,0x00,0x28,0x08,0x58,0x00,0x01,0xC0, +0x14,0x03,0x03,0x91,0x49,0xFF,0xDF,0x23,0x04,0x03,0x00,0x2C,0x46,0x1F,0xFC,0x0F, +0x92,0x06,0x94,0x06,0x58,0x00,0x00,0x2D,0x14,0x03,0x00,0x2C,0x04,0x03,0x00,0x2C, +0x50,0x10,0x8F,0xFF,0x46,0x20,0x02,0xD0,0xFE,0x0E,0xFE,0x17,0x14,0x03,0x00,0x2C, +0x04,0x03,0x00,0x2D,0x92,0x06,0x94,0x06,0x58,0x00,0x00,0x0A,0x14,0x03,0x00,0x2D, +0x04,0x03,0x00,0x2D,0xFE,0x46,0x46,0x00,0x00,0xA0,0xFE,0x47,0x14,0x13,0x00,0x2D, +0x04,0x13,0x00,0x2D,0x46,0x0C,0x0F,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x0E,0x46,0x10, +0xA0,0x00,0xFE,0x0F,0x14,0x03,0x00,0x2D,0x04,0x03,0x00,0x2F,0x92,0x06,0x94,0x06, +0x58,0x00,0x00,0x04,0x14,0x03,0x00,0x2F,0x44,0x00,0x02,0x80,0x49,0xFF,0xE2,0x94, +0x84,0x00,0x49,0xFF,0xFE,0xE6,0x49,0xFF,0xFC,0x76,0x49,0xFF,0xE2,0x83,0x3E,0x07, +0xF8,0x43,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xE2,0x7D,0xFC,0x80,0x2E,0x07,0xF8,0x43, +0xDD,0x9E,0x2E,0x07,0xF8,0x42,0xDD,0x9E,0x2E,0x07,0xF8,0x41,0xDD,0x9E,0xFC,0x00, +0x80,0xC0,0x49,0xFF,0xE3,0x78,0x80,0x06,0x49,0xFF,0xD9,0xF7,0x84,0x00,0x84,0x2C, +0x5A,0x68,0x01,0x1F,0x49,0xFF,0xE2,0xD5,0x84,0x02,0x84,0x2C,0x49,0xFF,0xE2,0xD1, +0x84,0x03,0x84,0x2A,0x49,0xFF,0xE2,0xCD,0x84,0x05,0x84,0x2C,0x49,0xFF,0xE2,0xC9, +0x84,0x07,0x84,0x2C,0x49,0xFF,0xE2,0xC5,0x84,0x00,0x80,0x20,0x49,0xFF,0xD9,0x7C, +0x84,0x00,0x84,0x22,0x49,0xFF,0xD9,0x78,0x44,0x02,0x12,0x18,0xD5,0x41,0x49,0xFF, +0xE2,0xB8,0x84,0x01,0x84,0x2C,0x49,0xFF,0xE2,0xB4,0x84,0x02,0x84,0x2C,0x49,0xFF, +0xE2,0xB0,0x84,0x03,0x84,0x2A,0x49,0xFF,0xE2,0xAC,0x84,0x04,0x84,0x2C,0x49,0xFF, +0xE2,0xA8,0x84,0x05,0x84,0x2C,0x49,0xFF,0xE2,0xA4,0x84,0x06,0x84,0x2C,0x49,0xFF, +0xE2,0xA0,0x84,0x07,0x84,0x2C,0x49,0xFF,0xE2,0x9C,0x84,0x01,0x84,0x20,0x49,0xFF, +0xD9,0x53,0x84,0x01,0x80,0x20,0x49,0xFF,0xD9,0x4F,0x84,0x01,0x84,0x22,0x49,0xFF, +0xD9,0x4B,0x84,0x00,0x84,0x23,0x49,0xFF,0xD9,0x47,0x84,0x01,0x84,0x24,0x49,0xFF, +0xD9,0x43,0x84,0x01,0x84,0x25,0x49,0xFF,0xD9,0x3F,0x84,0x01,0x84,0x26,0x49,0xFF, +0xD9,0x3B,0x84,0x01,0x84,0x27,0x49,0xFF,0xD9,0x37,0x44,0x02,0x12,0x9C,0x44,0x10, +0x00,0x42,0x49,0xFF,0xDF,0x36,0xFC,0x80,0xFC,0x21,0x84,0xC2,0xF0,0x81,0x80,0xE2, +0x80,0x01,0x5A,0x10,0x01,0x03,0x84,0xC1,0x49,0xFF,0xFF,0x8B,0xCF,0x0C,0x84,0x03, +0x49,0xFF,0xDB,0xDC,0x84,0x03,0x49,0xFF,0xDF,0x68,0xF0,0x01,0x80,0x26,0x49,0xFF, +0xF0,0x30,0xFC,0xA1,0x84,0x02,0x49,0xFF,0xDB,0xD1,0x84,0x02,0x49,0xFF,0xDF,0x5D, +0xF0,0x01,0x80,0x26,0x84,0x40,0x49,0xFF,0xEF,0xB3,0xFC,0xA1,0xFC,0x00,0x84,0x00, +0x49,0xFF,0xFD,0xA6,0x84,0x00,0x84,0x20,0x3E,0x07,0xF8,0x42,0x3E,0x07,0xF8,0x41, +0x80,0x41,0x2E,0x07,0xF8,0x43,0x49,0xFF,0xFF,0xD1,0xFC,0x80,0xFC,0x20,0x44,0x14, +0x80,0x90,0x44,0x0F,0xFF,0xEF,0xB4,0x41,0x46,0x70,0x00,0x81,0xFE,0x16,0xB6,0x01, +0x44,0x04,0x80,0x10,0xFA,0x20,0xAE,0x40,0xFA,0xC2,0x50,0x73,0x83,0xD4,0xB4,0x07, +0x92,0x0B,0x96,0x27,0xC0,0x07,0x8E,0xC1,0xC6,0x05,0x84,0x01,0x49,0xFF,0xE6,0xFB, +0xD5,0xF7,0x44,0x04,0x80,0x10,0xFA,0x20,0xAE,0x40,0x44,0x14,0x80,0x90,0xB4,0x01, +0x58,0x00,0x00,0x10,0xB6,0x01,0xFC,0xA0,0xFC,0x20,0x49,0xFF,0xF3,0xD6,0xC8,0x47, +0x2E,0x07,0xF1,0xF3,0x5A,0x00,0x01,0x44,0x44,0x64,0x80,0x10,0x44,0x00,0x00,0x40, +0xAE,0x30,0x44,0x74,0x50,0x28,0xB4,0x06,0x92,0x06,0x96,0x06,0x5A,0x00,0x01,0x07, +0x44,0x74,0x80,0x10,0x44,0x64,0x50,0x28,0xD5,0x25,0x2E,0x07,0xF0,0xEB,0xC8,0x0E, +0xB4,0x07,0x92,0x03,0x96,0x06,0xC8,0x0A,0xB4,0x07,0x92,0x01,0x96,0x06,0xC0,0x06, +0x3C,0x0D,0xFE,0x0F,0x5A,0x08,0x01,0xE9,0xD5,0xEC,0x49,0xFF,0xE7,0x5A,0x5A,0x08, +0x02,0xF9,0xD5,0xE7,0x2E,0x07,0xF0,0xEB,0xC8,0x15,0xB4,0x06,0x92,0x03,0x96,0x06, +0xC8,0x11,0xB4,0x06,0x92,0x01,0x96,0x06,0xC0,0x0D,0x3C,0x0D,0xFE,0x0F,0x5A,0x00, +0x01,0x06,0xB4,0x07,0x92,0x06,0x96,0x06,0xC0,0xEE,0x84,0x01,0x49,0xFF,0xE6,0xA2, +0xFC,0xA0,0x49,0xFF,0xE7,0x3E,0x5A,0x08,0x02,0xF2,0xD5,0xF8,0x49,0xFF,0xF3,0x8D, +0x5A,0x08,0x01,0x09,0x44,0x14,0x50,0x28,0xB4,0x01,0x92,0x01,0x96,0x06,0x5A,0x00, +0x01,0xFD,0xFC,0xA0,0xFC,0x01,0x5A,0x08,0x01,0x06,0xF0,0x81,0x49,0xFF,0xFF,0xA6, +0xF0,0x01,0x49,0xFF,0xDA,0x0A,0xFC,0x81,0xFC,0x20,0x84,0xE0,0x80,0xC0,0x84,0x00, +0x49,0xFF,0xDA,0x03,0x49,0xFF,0xFF,0x74,0x49,0xFF,0xEC,0xBF,0x3E,0x77,0xF0,0xFB, +0x49,0xFF,0xE9,0x99,0x3E,0x77,0xF0,0xFA,0x5A,0x68,0x01,0x05,0x80,0x06,0x49,0xFF, +0xFF,0xE3,0xFC,0xA0,0xFC,0x00,0x84,0x01,0x49,0xFF,0xFF,0xE8,0x2E,0x07,0xF0,0xFA, +0xC8,0x05,0x3C,0x0D,0xFE,0x0F,0x5A,0x08,0x01,0xFB,0x84,0xC0,0x3E,0x67,0xF0,0xFB, +0x49,0xFF,0xE9,0x81,0x3E,0x67,0xF0,0xFA,0x49,0xFF,0xFD,0x95,0x49,0xFF,0xFB,0x25, +0x49,0xFF,0xFD,0x9B,0x84,0x00,0x49,0xFF,0xFF,0xD1,0xFC,0x80,0xFC,0x00,0x84,0x01, +0x49,0xFF,0xFF,0xCC,0x2E,0x07,0xF0,0xFA,0xC8,0x05,0x3C,0x0D,0xFE,0x0F,0x5A,0x08, +0x01,0xFB,0x84,0xC0,0x3E,0x67,0xF0,0xFB,0x49,0xFF,0xE9,0x65,0x84,0x00,0x3E,0x67, +0xF0,0xFA,0x49,0xFF,0xFF,0xBB,0xFC,0x80,0xFC,0x00,0x2E,0x07,0xF8,0x43,0x49,0xFF, +0xF1,0xBD,0x2E,0x17,0xF8,0x43,0x84,0x03,0x49,0xFF,0xF2,0x49,0x84,0x03,0x49,0xFF, +0xFC,0xBF,0x84,0x20,0x2E,0x27,0xF8,0x41,0x2E,0x07,0xF8,0x43,0x49,0xFF,0xFE,0xEE, +0x84,0x01,0x49,0xFF,0xFD,0x0B,0x49,0xFF,0xFF,0xD3,0x44,0x10,0x00,0x42,0x84,0x40, +0x84,0x63,0x44,0x02,0x12,0x9C,0x49,0xFF,0xF0,0x78,0x84,0x00,0x49,0xFF,0xFC,0xA8, +0x2E,0x07,0xF8,0x43,0x49,0xFF,0xF2,0xA1,0x84,0x01,0x49,0xFF,0xFC,0xF7,0xFC,0x80, +0xFC,0x01,0x49,0xFF,0xF0,0xA1,0xF0,0x81,0x49,0xFF,0xF2,0xFF,0x80,0xC0,0xC8,0x34, +0x84,0x01,0x49,0xFF,0xFD,0x2E,0x84,0x01,0x49,0xFF,0xFC,0x7C,0x84,0x06,0x49,0xFF, +0xF0,0x96,0x84,0x01,0x49,0xFF,0xF1,0x82,0x2E,0x27,0xF8,0x41,0x2E,0x17,0xF8,0x42, +0x84,0x01,0x49,0xFF,0xFE,0xBB,0x84,0x01,0x49,0xFF,0xF1,0x2A,0x84,0x21,0x84,0x02, +0x49,0xFF,0xF2,0x05,0x80,0x06,0x49,0xFF,0xDC,0x23,0x44,0x10,0x02,0x40,0x80,0x06, +0x49,0xFF,0xE2,0x05,0x84,0x02,0x49,0xFF,0xFC,0xC9,0x80,0x06,0x49,0xFF,0xDD,0xC5, +0x84,0x01,0x49,0xFF,0xFF,0x5B,0x2E,0x07,0xF0,0xFA,0xC8,0x27,0x3C,0x0D,0xFE,0x0F, +0x5A,0x08,0x01,0xFB,0xD5,0x22,0x84,0xC2,0x84,0x00,0x49,0xFF,0xE1,0xE5,0xF0,0x81, +0x84,0x00,0x49,0xFF,0xDC,0x05,0x44,0x10,0x02,0x40,0x84,0x00,0x49,0xFF,0xE1,0xE7, +0x84,0x02,0x49,0xFF,0xFC,0xAB,0x49,0xFF,0xFF,0x73,0xF0,0x01,0x44,0x10,0x02,0x40, +0x84,0x40,0x49,0xFF,0xF0,0x09,0x84,0x01,0x49,0xFF,0xFC,0xA0,0x5A,0x68,0x01,0x04, +0x48,0x00,0x00,0xC5,0x84,0xC1,0xD5,0xE1,0x44,0x10,0x02,0x40,0x84,0x40,0x44,0x02, +0x09,0x18,0x49,0xFF,0xEF,0xF9,0x84,0x00,0x49,0xFF,0xFF,0x28,0x84,0x00,0x49,0xFF, +0xFC,0xD0,0x49,0xFF,0xFA,0x60,0xF0,0x01,0x49,0xFF,0xF0,0x39,0x84,0x00,0x49,0xFF, +0xF1,0x25,0x2E,0x27,0xF8,0x41,0x2E,0x17,0xF8,0x42,0x84,0x00,0x49,0xFF,0xFE,0x5E, +0x84,0x00,0x49,0xFF,0xF0,0xCD,0x84,0x20,0x84,0x02,0x49,0xFF,0xF1,0xA8,0x84,0x00, +0x49,0xFF,0xDB,0xC6,0x84,0x00,0x44,0x10,0x02,0x40,0x49,0xFF,0xE1,0xA8,0x84,0x01, +0x49,0xFF,0xFF,0x04,0x44,0x64,0x50,0x28,0x2E,0x07,0xF8,0x40,0xC0,0x09,0x49,0xFF, +0xE8,0xA2,0x84,0x00,0x3E,0x07,0xF0,0xFA,0x44,0x64,0x50,0x28,0xD5,0x26,0x2E,0x07, +0xF0,0xEB,0xC8,0x0E,0xB4,0x06,0x92,0x03,0x96,0x06,0xC8,0x0A,0xB4,0x06,0x92,0x01, +0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFE,0x0F,0x5A,0x08,0x01,0xE8,0xD5,0xE9,0x49,0xFF, +0xE6,0x08,0x5A,0x08,0x02,0xF9,0xD5,0xE4,0x2E,0x07,0xF0,0xEB,0xC8,0x12,0xB4,0x06, +0x92,0x03,0x96,0x06,0x5A,0x00,0x01,0x0E,0xB4,0x06,0x92,0x01,0x96,0x06,0xC0,0x09, +0x3C,0x0D,0xFE,0x0F,0x5A,0x00,0x01,0x0A,0x49,0xFF,0xE8,0x72,0xC0,0xEE,0xD5,0x05, +0x49,0xFF,0xE5,0xEF,0x5A,0x08,0x02,0xF6,0x2E,0x07,0xF0,0xFA,0xC8,0x05,0x3C,0x0D, +0xFE,0x0F,0x5A,0x08,0x01,0xFB,0x44,0x64,0x50,0x28,0x2E,0x07,0xF8,0x40,0xC8,0x11, +0x84,0x01,0x49,0xFF,0xDB,0x75,0x84,0x01,0x44,0x10,0x02,0x40,0x49,0xFF,0xE1,0x57, +0x49,0xFF,0xE8,0x59,0x84,0x00,0x3E,0x07,0xF0,0xFA,0x44,0x64,0x50,0x28,0xD5,0x26, +0x2E,0x07,0xF0,0xEB,0xC8,0x0E,0xB4,0x06,0x92,0x03,0x96,0x06,0xC8,0x0A,0xB4,0x06, +0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFE,0x0F,0x5A,0x08,0x01,0xE0,0xD5,0xE1, +0x49,0xFF,0xE5,0xBF,0x5A,0x08,0x02,0xF9,0xD5,0xDC,0x2E,0x07,0xF0,0xEB,0xC8,0x12, +0xB4,0x06,0x92,0x03,0x96,0x06,0x5A,0x00,0x01,0x0E,0xB4,0x06,0x92,0x01,0x96,0x06, +0xC0,0x09,0x3C,0x0D,0xFE,0x0F,0x5A,0x00,0x01,0x0A,0x49,0xFF,0xE8,0x29,0xC0,0xEE, +0xD5,0x05,0x49,0xFF,0xE5,0xA6,0x5A,0x08,0x02,0xF6,0x2E,0x07,0xF0,0xFA,0xC8,0x05, +0x3C,0x0D,0xFE,0x0F,0x5A,0x08,0x01,0xFB,0x84,0x00,0x49,0xFF,0xFE,0x77,0x84,0x01, +0x49,0xFF,0xFB,0xDC,0x84,0x01,0x49,0xFF,0xDC,0xD8,0xFC,0x81,0xFC,0x49,0x2E,0x67, +0xF8,0x43,0x84,0x00,0x49,0xFF,0xFD,0x45,0x84,0x02,0x49,0xFF,0xD9,0x97,0x80,0x06, +0x84,0x20,0x84,0x41,0x49,0xFF,0xED,0x7C,0x84,0x04,0x49,0xFF,0xFC,0x30,0xB1,0x86, +0x44,0x10,0x84,0x14,0x80,0x06,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20, +0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x00,0x3A,0x20,0x14,0x20,0x44,0x00, +0x84,0x44,0x80,0x3F,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24,0x3A,0x20,0x0C,0x00, +0x3A,0x20,0x8C,0x20,0x80,0x1F,0x49,0xFF,0xDD,0xAF,0x80,0x06,0x49,0xFF,0xDD,0x76, +0x84,0x00,0x3E,0x07,0xF0,0xF9,0x84,0x01,0x80,0xFF,0x49,0xFF,0xFE,0x37,0x49,0xFF, +0xD7,0x05,0x2E,0x07,0xF0,0xF9,0xC8,0x05,0x3C,0x0D,0xFE,0x0F,0x5A,0x08,0x01,0xFB, +0x84,0x00,0x3E,0x07,0xF0,0xF9,0x84,0x00,0x49,0xFF,0xFE,0x28,0x49,0xFF,0xFE,0x3C, +0x84,0x00,0x49,0xFF,0xFB,0xF4,0x44,0x10,0x83,0x80,0x80,0x06,0x3A,0x20,0x94,0x04, +0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x00, +0x3A,0x20,0x14,0x20,0x44,0x00,0x83,0x2C,0x80,0x27,0x3A,0x20,0x14,0x04,0x3A,0x20, +0x94,0x24,0x3A,0x20,0x0C,0x00,0x3A,0x20,0x8C,0x20,0x80,0x07,0x49,0xFF,0xDD,0x74, +0x80,0x06,0x49,0xFF,0xDD,0x3B,0xFC,0xC9,0xFC,0x00,0x2E,0x07,0xF8,0x43,0x49,0xFF, +0xF0,0x05,0x84,0x00,0x49,0xFF,0xFB,0x0C,0x84,0x20,0x2E,0x27,0xF8,0x41,0x2E,0x07, +0xF8,0x43,0x49,0xFF,0xFD,0x3B,0x84,0x00,0x49,0xFF,0xFE,0x64,0x2E,0x17,0xF8,0x42, +0x2E,0x27,0xF8,0x41,0x2E,0x07,0xF8,0x43,0x49,0xFF,0xFD,0x30,0x2E,0x07,0xF8,0x43, +0x49,0xFF,0xEF,0xEC,0x2E,0x07,0xF8,0x43,0x49,0xFF,0xF0,0xEF,0x49,0xFF,0xF1,0x55, +0xC8,0x09,0x2E,0x07,0xF8,0x43,0x49,0xFF,0xFA,0xD5,0x2E,0x07,0xF8,0x43,0x49,0xFF, +0xFB,0x80,0x84,0x01,0x49,0xFF,0xFB,0x3A,0x49,0xFF,0xEF,0x86,0x49,0xFF,0xFB,0x95, +0x84,0x01,0x49,0xFF,0xFD,0xCB,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xDD,0xDB,0xFC,0x80, +0x46,0x20,0x00,0x82,0x96,0x67,0xA1,0x54,0x44,0x3F,0xFF,0xC1,0x95,0x09,0xFE,0xEE, +0xFE,0xE7,0xA8,0xD4,0xA1,0x54,0x44,0x3F,0xE0,0xFF,0x40,0x40,0xA0,0x08,0xFE,0xEE, +0xFE,0xE7,0xA8,0xD4,0xA0,0xD4,0x46,0x4F,0xFC,0x1F,0x50,0x42,0x0F,0xFF,0x40,0x50, +0xC4,0x08,0xFE,0xE6,0xFE,0xEF,0xA8,0xD4,0xA1,0x14,0x46,0x3E,0x0F,0xFF,0x50,0x31, +0x8F,0xFF,0xFE,0xE6,0x40,0x10,0xE0,0x08,0xFE,0x5F,0xA8,0x54,0xA1,0x14,0x96,0x06, +0x44,0x1F,0xFF,0xBF,0x94,0xC6,0xFE,0x66,0xFE,0x5F,0xA8,0x54,0xA1,0x14,0x44,0x1F, +0xDF,0xFF,0x40,0x30,0x34,0x08,0xFE,0x66,0xFE,0x5F,0xA8,0x54,0xA0,0x54,0x46,0x3F, +0xFB,0xFF,0x50,0x31,0x8F,0xFF,0x40,0x40,0x58,0x08,0xFE,0x5E,0xFE,0x67,0xA8,0x54, +0xA0,0xD4,0x46,0x1D,0xFF,0xFF,0x50,0x10,0x8F,0xFF,0x40,0x00,0x74,0x08,0xFE,0x5E, +0xFE,0x0F,0xA8,0x14,0xDD,0x9E,0xFC,0x00,0x84,0xC0,0x3E,0x67,0xF0,0xFB,0x49,0xFF, +0xE7,0x1A,0x3E,0x67,0xF0,0xFA,0x2E,0x07,0xF0,0xFB,0xC8,0x05,0x3C,0x0D,0xFE,0x0F, +0x5A,0x08,0x01,0xFB,0x84,0x00,0x3E,0x07,0xF0,0xFB,0x44,0x64,0x50,0x28,0x49,0xFF, +0xE7,0x07,0xC8,0x16,0x2E,0x07,0xF0,0xEB,0xC8,0x0F,0xB4,0x06,0x92,0x03,0x96,0x06, +0x5A,0x00,0x01,0x0B,0xB4,0x06,0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFE,0x0F, +0x5A,0x08,0x01,0xEF,0xD5,0x05,0x49,0xFF,0xE4,0x74,0x5A,0x08,0x02,0xF9,0x49,0xFF, +0xE6,0xF2,0x2E,0x07,0xF0,0xFA,0xC8,0x05,0x3C,0x0D,0xFE,0x0F,0x5A,0x08,0x01,0xFB, +0x84,0x00,0x3E,0x07,0xF0,0xFA,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xFB,0x06,0x84,0x01, +0x49,0xFF,0xFD,0x3C,0x49,0xFF,0xFF,0xC1,0x49,0xFF,0xFA,0xF5,0x49,0xFF,0xFA,0xC4, +0x84,0x00,0x49,0xFF,0xFD,0x33,0xFC,0x80,0xFC,0x40,0x51,0xFF,0xFD,0xC0,0xFD,0x30, +0x81,0x22,0x44,0x10,0x84,0x6C,0x80,0x1F,0x44,0x20,0x02,0x40,0x49,0x00,0x14,0x40, +0xFB,0x42,0x84,0x20,0x44,0x80,0x20,0x00,0x84,0x40,0x98,0xCA,0x96,0xD9,0x80,0x82, +0x38,0x0F,0x8C,0x00,0x42,0x40,0x28,0x73,0x94,0x21,0x99,0x70,0x88,0x07,0x22,0xF2, +0x80,0x00,0x22,0x40,0x00,0x00,0x40,0x47,0x90,0x01,0x4E,0x44,0x00,0x03,0xFF,0x22, +0x94,0xD9,0x88,0x69,0x22,0xF1,0x80,0x00,0x40,0xF7,0x90,0x07,0xE8,0x02,0xAD,0x18, +0x22,0xF2,0x80,0x00,0x5E,0xF7,0xBF,0xC1,0xE8,0x06,0x22,0xF0,0x00,0x00,0x5E,0xF7, +0xBF,0xC1,0xE9,0x03,0x12,0x81,0x80,0x00,0x8C,0x41,0x5A,0x28,0x12,0xD8,0x8C,0x32, +0x96,0x49,0x5A,0x1A,0x40,0xD3,0x51,0xFF,0x82,0x40,0xFC,0xC0,0xFC,0x00,0x44,0x00, +0x01,0x00,0x84,0x20,0x49,0xFF,0xD9,0xF4,0x84,0x21,0x44,0x00,0x01,0x00,0x49,0xFF, +0xD9,0xEF,0x84,0x08,0x49,0xFF,0xE3,0x6F,0x44,0x00,0x01,0x00,0x84,0x20,0x49,0xFF, +0xD9,0xE7,0xFC,0x80,0xFC,0x40,0x44,0x74,0x2C,0x14,0x84,0xCF,0xB5,0x27,0x40,0x94, +0x98,0x02,0x10,0x90,0x00,0x00,0xB4,0x07,0x92,0x08,0xFE,0x36,0xAE,0x08,0xB4,0x27, +0x84,0x01,0x92,0x2C,0xFE,0x46,0xAE,0x50,0x44,0x14,0x24,0x14,0x44,0x24,0x2C,0x1C, +0xB4,0x21,0xFF,0x8E,0xAF,0x98,0xB4,0x22,0x92,0x32,0xFE,0x46,0xAE,0x60,0xB4,0x22, +0x92,0x33,0xFE,0x46,0xAE,0x68,0xB4,0x22,0x92,0x36,0xFE,0x0E,0xF1,0x08,0xAE,0x08, +0xFC,0xC0,0xFC,0x40,0x3C,0x8D,0xFC,0x8A,0x5A,0x88,0x01,0x64,0x80,0xC5,0x80,0xE4, +0x81,0x43,0x83,0x81,0x5A,0x08,0xFF,0x05,0x5A,0x18,0xFF,0x51,0xFC,0xC0,0x5A,0x08, +0xFE,0x4A,0x5A,0x18,0xFE,0x48,0x44,0x14,0x2C,0x14,0x54,0x41,0x00,0x01,0xB4,0x41, +0x44,0x0F,0xEF,0xFF,0x40,0x42,0x30,0x08,0xFE,0x86,0x40,0x91,0x10,0x04,0x80,0x68, +0x80,0x48,0xB7,0x21,0x44,0x04,0x28,0x14,0x44,0x14,0x24,0x14,0x49,0xFF,0xDA,0x2C, +0x80,0x0A,0x49,0xFF,0xE4,0xAF,0x44,0x04,0x24,0x14,0x80,0x28,0x49,0xFF,0xD9,0xF9, +0x44,0x24,0x2C,0x1C,0x97,0xC6,0x40,0x43,0xC8,0x08,0xB4,0xE2,0x44,0x0B,0xFF,0xFF, +0xFF,0xC6,0xFF,0xE7,0xB6,0xE2,0x97,0x86,0xB4,0x02,0x40,0x53,0x4C,0x08,0x46,0x6F, +0xFF,0x7F,0x50,0x63,0x0F,0xFF,0xFF,0x86,0xFF,0xAF,0xB6,0xC2,0x00,0x0F,0x80,0x20, +0xB4,0x62,0x96,0x06,0x40,0x10,0x58,0x08,0x46,0x0F,0xFB,0xFF,0x50,0x00,0x0F,0xFF, +0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0x44,0x14,0x24,0x1C,0x44,0x04,0x28,0x1C,0x80,0x48, +0xD5,0x0D,0x49,0xFF,0xE4,0x7F,0x5B,0xC0,0xFF,0x05,0x80,0x1C,0x49,0xFF,0xE4,0x83, +0x44,0x04,0x28,0x14,0x44,0x14,0x24,0x14,0x84,0x41,0x80,0x62,0x49,0xFF,0xD9,0xEC, +0xFC,0xC0,0xFC,0x41,0x80,0xE0,0x3C,0x0D,0xFC,0x8A,0x5A,0x08,0x01,0x32,0x81,0x25, +0x81,0x44,0x80,0x02,0x80,0xC1,0xF7,0x81,0x5A,0x30,0xFF,0x05,0x84,0x20,0x49,0xFF, +0xE8,0xB3,0x5A,0xA8,0x0F,0x04,0x5A,0x90,0x01,0x0C,0x46,0x10,0x00,0x81,0x83,0x81, +0xB8,0x33,0x58,0x00,0x02,0x00,0xB8,0xB3,0x84,0x00,0x49,0xFF,0xFB,0x0A,0x00,0x0F, +0x80,0x28,0x5A,0x08,0x0F,0x06,0x00,0x0F,0x80,0x2C,0x5A,0x00,0x0F,0x04,0x49,0x00, +0x0D,0x19,0x44,0x00,0xFF,0xFF,0x4C,0x60,0x00,0x0C,0xF0,0x01,0x80,0x26,0x49,0xFF, +0xDD,0x84,0x50,0x13,0x00,0x42,0xF0,0x01,0x96,0x49,0x49,0xFF,0xFE,0x4F,0xFC,0xC1, +0xFC,0x42,0x3C,0x6D,0xFC,0x8A,0x01,0xCF,0x80,0x30,0x00,0x8F,0x80,0x34,0x00,0xAF, +0x80,0x38,0x5A,0x68,0x01,0x4E,0x80,0xE5,0x81,0x24,0xF2,0x82,0x80,0xC1,0xF0,0x81, +0x5A,0x30,0xFF,0x05,0xFD,0x01,0x49,0xFF,0xE8,0x77,0x5A,0x90,0x0F,0x06,0xF0,0x02, +0x80,0x29,0x49,0xFF,0xDD,0xB6,0x5B,0xC0,0x01,0x06,0x80,0x1C,0x80,0x27,0x49,0xFF, +0xD4,0x6B,0x5A,0x80,0x0F,0x05,0x80,0x08,0x49,0xFF,0xD6,0x62,0x5A,0xA0,0x0F,0x05, +0x80,0x0A,0x49,0xFF,0xD6,0x90,0xF1,0x0F,0x44,0x00,0xFF,0xFF,0x5A,0x10,0x01,0x05, +0x4C,0x60,0x40,0x1A,0xFC,0xC2,0x4C,0x60,0x40,0x17,0x49,0xFF,0xF9,0xA6,0x49,0xFF, +0xFD,0x6F,0x49,0xFF,0xFA,0xAD,0xF0,0x81,0x49,0xFF,0xFA,0xAD,0xF0,0x82,0x49,0xFF, +0xFA,0xAD,0xF0,0x83,0xF1,0x02,0xF0,0x01,0xF2,0x03,0x49,0xFF,0xFB,0x17,0x49,0xFF, +0xF9,0x8A,0xFC,0xC2,0xF0,0x01,0x80,0x26,0x49,0xFF,0xDD,0x2F,0x50,0x13,0x00,0x42, +0xF0,0x01,0x96,0x49,0x49,0xFF,0xFD,0xFA,0xF0,0x0F,0x5A,0x00,0x01,0xE0,0xFC,0xC2, +0xFC,0x02,0x44,0x20,0x86,0xEC,0xB1,0x81,0x80,0x82,0x80,0xA6,0x3A,0x22,0x0C,0x04, +0x3A,0x22,0x8C,0x24,0xA4,0xA0,0x9F,0x01,0xAC,0xA8,0x92,0x48,0xE0,0x44,0x00,0x3F, +0x80,0x04,0xE9,0x05,0xE0,0x60,0x80,0x43,0x40,0x20,0x3C,0x1B,0x84,0x60,0x38,0x53, +0x0C,0x00,0x96,0x18,0x8C,0xA1,0xDA,0x04,0x49,0xFF,0xDC,0xB7,0xFC,0x82,0x8C,0x61, +0x5A,0x38,0x0A,0xF7,0xFC,0x82,0xFC,0x20,0x51,0xFF,0xFD,0xC0,0x44,0x20,0x02,0x40, +0xFD,0x30,0x80,0x1F,0x44,0x10,0x84,0x6C,0x49,0x00,0x12,0xAA,0x88,0xDF,0x84,0x40, +0xFA,0x22,0x42,0x31,0x04,0x24,0x96,0x10,0x38,0x53,0x0C,0x00,0xD7,0x06,0x8C,0x41, +0x5A,0x28,0x20,0xF9,0x44,0x00,0x00,0xFF,0x51,0xFF,0x82,0x40,0xFC,0xA0,0xFC,0x4B, +0x49,0xFF,0xF9,0x1E,0xF0,0x86,0x2E,0x07,0xF2,0x1C,0x3C,0x8D,0xFC,0x8A,0xF0,0x88, +0x2E,0x07,0xF2,0x1D,0x2E,0x77,0xF2,0x1B,0xF0,0x87,0x2E,0x07,0xF2,0x1F,0x2E,0x67, +0xF2,0x1E,0xF0,0x89,0x2E,0x07,0xF2,0x20,0x2E,0xA7,0xF2,0x22,0x54,0x10,0x00,0x0F, +0x92,0x04,0x96,0x06,0xF0,0x8B,0x2E,0x07,0xF2,0x21,0xF1,0x8A,0x54,0x10,0x00,0x0F, +0x92,0x04,0xF0,0x8D,0x84,0x1F,0xF1,0x8C,0x2E,0x97,0xF2,0x23,0x10,0x0F,0x80,0x51, +0x10,0x0F,0x80,0x52,0x10,0x0F,0x80,0x53,0x10,0x0F,0x80,0x54,0x10,0x0F,0x80,0x55, +0x10,0x0F,0x80,0x56,0x10,0x0F,0x80,0x57,0x5A,0x88,0x01,0x5C,0x2E,0x07,0xF2,0x0C, +0x5A,0x00,0x02,0x13,0x54,0x13,0x00,0x06,0x92,0x21,0xC9,0x06,0x80,0x08,0x49,0xFF, +0xF8,0xE8,0x80,0x08,0xD5,0x07,0x5A,0x18,0x01,0x08,0x84,0x00,0x49,0xFF,0xF8,0xE1, +0x84,0x00,0x49,0xFF,0xF8,0x2F,0xF0,0x07,0x5A,0x00,0xFF,0x11,0x46,0x00,0x00,0x81, +0x84,0x20,0x04,0x00,0x00,0x23,0x96,0x1F,0xF0,0x85,0xF0,0x07,0x49,0xFF,0xFF,0x6A, +0xF0,0x07,0x84,0x23,0x49,0xFF,0xFF,0x66,0xD5,0x03,0x84,0x00,0xF0,0x85,0x5A,0xA8, +0xFF,0x04,0x5A,0x90,0xFF,0x12,0x50,0x0F,0x80,0x57,0xB6,0x1F,0x50,0x1F,0x80,0x52, +0x50,0x0F,0x80,0x51,0x50,0x2F,0x80,0x53,0xB0,0xD5,0x50,0x4F,0x80,0x55,0x50,0x5F, +0x80,0x56,0x49,0xFF,0xFE,0x31,0x84,0x41,0xB6,0x5F,0x80,0x0A,0x80,0x29,0x80,0xA2, +0x84,0x65,0x84,0x80,0x49,0xFF,0xFE,0x4F,0xF0,0x0B,0xF2,0x08,0xB6,0x1F,0xF0,0x0C, +0x40,0x13,0xA0,0x08,0xF0,0x81,0xF0,0x0D,0x97,0x86,0xF0,0x82,0x84,0x00,0xFE,0x57, +0xF6,0x83,0x80,0x40,0xF3,0x09,0xF4,0x0A,0x80,0xA0,0x49,0xFF,0xFE,0xDB,0xD5,0x03, +0x84,0x00,0xF0,0x85,0x49,0xFF,0xF8,0xA7,0x84,0x00,0x49,0xFF,0xFA,0xE7,0x85,0x00, +0x46,0x30,0x00,0x82,0x40,0x04,0x10,0x08,0xF0,0x8E,0x54,0x04,0x00,0xFF,0x84,0xA0, +0xF0,0x88,0xB4,0x03,0xF1,0x0E,0x92,0x0B,0x88,0x25,0x40,0x00,0x2C,0x08,0xFE,0x0F, +0xB6,0x03,0xA0,0x19,0xA1,0xD9,0x54,0xA0,0x00,0xFF,0xA0,0x19,0x44,0x13,0xF0,0x00, +0xA1,0x99,0x95,0x29,0xFE,0x0E,0x40,0x90,0x30,0x09,0x97,0x20,0x46,0x00,0x0F,0xC0, +0x80,0x24,0xFF,0x86,0xF0,0x08,0xF3,0x91,0xF5,0x90,0xF4,0x8F,0x49,0xFF,0xFF,0x25, +0xFA,0x22,0x80,0x48,0x42,0x20,0x04,0x73,0x54,0x73,0x8F,0xC0,0x54,0x15,0x00,0x20, +0x80,0x02,0x92,0xE6,0x94,0x8A,0x54,0x15,0x00,0x1F,0x40,0xA1,0x04,0x04,0x54,0x13, +0x80,0x1F,0x97,0xEE,0x95,0xFA,0xFF,0xCF,0xF1,0x06,0xF4,0x0F,0x40,0x73,0xA0,0x08, +0x40,0xA5,0x1C,0x04,0x38,0xA0,0x81,0x09,0x9C,0x61,0x96,0x48,0xF0,0x08,0x49,0xFF, +0xFF,0x04,0xFA,0x22,0x54,0x44,0x80,0x20,0x80,0x48,0x92,0xD2,0x42,0x20,0x04,0x73, +0x95,0x22,0x54,0x14,0x80,0x1F,0x40,0x92,0x04,0x04,0x54,0x13,0x00,0x1F,0x97,0xAE, +0x95,0xB2,0xFF,0x8F,0xF5,0x10,0xF1,0x06,0x40,0x63,0x20,0x08,0x40,0x94,0x98,0x04, +0x8C,0xA1,0x38,0x90,0x89,0x09,0xF3,0x11,0x5A,0x58,0x10,0xA5,0x8D,0x01,0x5A,0x88, +0x09,0x9B,0x44,0x00,0x02,0x40,0xFA,0x41,0xF0,0x88,0x46,0xA0,0x00,0x82,0x84,0x10, +0xFE,0x14,0x50,0x00,0x03,0x50,0xF0,0x8F,0x96,0x10,0xF5,0x08,0x84,0x80,0xF0,0x8E, +0xF5,0x93,0xF2,0x90,0xB4,0x0A,0x54,0x12,0x87,0xFF,0x92,0x0B,0x40,0x00,0x2C,0x08, +0xFE,0x0F,0xB6,0x0A,0xB4,0x0A,0xF1,0x0F,0x92,0x0B,0x88,0x24,0x40,0x00,0x2C,0x08, +0xFE,0x0F,0xB6,0x0A,0x80,0x4A,0xA0,0x11,0xA1,0xD1,0x54,0x80,0x00,0xFF,0xA0,0x11, +0x44,0x13,0xF0,0x00,0xA1,0x91,0x94,0xE1,0xFE,0x0E,0x40,0x90,0x30,0x09,0x96,0xD8, +0x46,0x00,0x0F,0xC0,0x80,0x23,0xFF,0x86,0xF0,0x0E,0xF4,0x92,0xF3,0x91,0x49,0xFF, +0xFE,0xAC,0xF2,0x10,0xFA,0x22,0x80,0x62,0x42,0x30,0x04,0x73,0x54,0x73,0x8F,0xC0, +0x54,0x14,0x00,0x20,0x40,0xF0,0x88,0x08,0x92,0xE6,0x54,0x14,0x00,0x1F,0x40,0x87, +0x84,0x04,0x54,0x13,0x80,0x1F,0x97,0xEE,0x95,0xFA,0xFF,0xCF,0xF1,0x06,0x40,0x73, +0xA0,0x08,0x40,0x84,0x1C,0x04,0x38,0x80,0x8D,0x09,0xF3,0x11,0xF0,0x0E,0x9C,0x59, +0x96,0x48,0x49,0xFF,0xFE,0x8A,0xF2,0x10,0xFA,0x22,0x80,0x62,0x42,0x30,0x04,0x73, +0x80,0x03,0x54,0x34,0x80,0x20,0x92,0xD2,0x54,0x14,0x80,0x1F,0x94,0xDA,0x40,0x91, +0x84,0x04,0x54,0x13,0x00,0x1F,0x97,0xAE,0x95,0xB2,0xFF,0x8F,0xF4,0x12,0xF1,0x06, +0xF5,0x13,0x40,0x63,0x20,0x08,0x40,0x94,0x98,0x04,0x8C,0xA1,0x8C,0x81,0x38,0x90, +0x81,0x09,0x54,0x52,0x87,0xFF,0x5A,0x48,0x10,0x95,0xF0,0x08,0x8E,0x41,0x8C,0x10, +0x54,0x00,0x07,0xFF,0xF0,0x88,0x5A,0x28,0x08,0x84,0x49,0xFF,0xEA,0x1E,0x3C,0x0D, +0xFC,0x8A,0x5A,0x08,0x01,0x2B,0xF0,0x07,0x5A,0x00,0xFF,0x0A,0xF0,0x05,0x84,0x20, +0x49,0xFF,0xDB,0x03,0xF0,0x05,0x84,0x23,0x49,0xFF,0xDA,0xFF,0x00,0x6F,0x80,0x57, +0x00,0x0F,0x80,0x51,0x00,0x1F,0x80,0x52,0x00,0x2F,0x80,0x53,0x00,0x3F,0x80,0x54, +0x00,0x4F,0x80,0x55,0x00,0x5F,0x80,0x56,0xB6,0xDF,0x49,0xFF,0xFD,0x24,0xF0,0x0C, +0x44,0x10,0x00,0xF0,0xB6,0x1F,0xF0,0x0D,0xF3,0x09,0xF0,0x81,0x84,0x00,0x80,0x40, +0xF4,0x0A,0xF5,0x0B,0x49,0xFF,0xFD,0x7F,0x49,0xFF,0xFC,0xDA,0x44,0x00,0x00,0x51, +0x3E,0x07,0xF0,0x60,0xFC,0xCB,0xFC,0x41,0x81,0x00,0x81,0x23,0xF5,0x81,0x80,0xC2, +0x81,0x44,0xB6,0x3F,0x2E,0x76,0x6E,0x96,0x49,0xFF,0xF7,0x57,0x83,0x80,0x46,0x00, +0x00,0x80,0xB4,0x3F,0x14,0x80,0x00,0x47,0x14,0x80,0x00,0x49,0x84,0x01,0x49,0xFF, +0xDB,0x14,0x84,0x04,0xB4,0x3F,0x49,0xFF,0xDB,0x10,0x49,0xFF,0xF7,0x6E,0x49,0xFF, +0xFC,0x2C,0x49,0xFF,0xF7,0x60,0x80,0x1C,0x80,0x29,0x84,0x40,0x49,0xFF,0xEA,0x4A, +0xF5,0x01,0xCD,0x14,0x93,0x21,0x96,0x69,0xE2,0x29,0xE8,0x0F,0x40,0x22,0x9C,0x16, +0xC8,0x04,0xC1,0x03,0x8C,0xC2,0x97,0xB1,0x42,0x03,0x1C,0x73,0x38,0x1E,0x15,0x11, +0x38,0x15,0x01,0x09,0x8C,0xA1,0xD5,0xF0,0xFC,0xC1,0x5A,0x58,0x01,0x0F,0x95,0xB1, +0x89,0x46,0x84,0x00,0x96,0x41,0xE2,0x29,0xE8,0x07,0x38,0x1E,0x01,0x11,0x38,0x15, +0x01,0x09,0x8C,0x01,0xD5,0xF8,0xFC,0xC1,0x5A,0x58,0x02,0x1B,0x94,0xF9,0x84,0x40, +0x96,0x51,0xE2,0x29,0xE8,0x15,0x40,0x41,0x1C,0x16,0xC8,0x04,0xC1,0x03,0x8C,0xC1, +0x97,0xB1,0x40,0x51,0x0C,0x96,0xCC,0x04,0xC1,0x03,0x8C,0xC6,0x97,0xB1,0x42,0x03, +0x1C,0x73,0x38,0x1E,0x09,0x11,0x38,0x15,0x01,0x09,0x8C,0x41,0xD5,0xEA,0xFC,0xC1, +0xFC,0x40,0x51,0xFF,0xF5,0xF0,0x49,0xFF,0xF6,0xFB,0xF0,0x86,0x84,0x20,0x44,0x20, +0x04,0x80,0x50,0x0F,0x81,0x10,0x49,0x00,0x10,0x91,0x44,0x00,0x86,0xEC,0xB0,0x57, +0x3A,0x20,0x0C,0x04,0x3A,0x20,0x8C,0x24,0xA4,0x00,0x50,0x8F,0x80,0x68,0xAC,0x08, +0x00,0x0F,0x80,0x5C,0x84,0x20,0x8C,0x01,0x96,0x00,0xF0,0x88,0x44,0x00,0x86,0xF8, +0xB1,0xEC,0x3A,0x20,0x10,0x00,0x3A,0x24,0x10,0x20,0xB0,0x1D,0x84,0x4C,0x49,0x00, +0x10,0x75,0x2E,0x07,0xF2,0x1B,0x2E,0x97,0xF2,0x1F,0xF0,0x84,0x2E,0x07,0xF2,0x1C, +0x50,0xFF,0x80,0xE0,0xF0,0x87,0x2E,0x07,0xF2,0x1D,0xF0,0x90,0x2E,0x07,0xF2,0x1E, +0xF0,0x91,0x54,0x04,0x80,0x0F,0xF0,0x85,0x2E,0x07,0xF2,0x20,0xF0,0x89,0x2E,0x07, +0xF2,0x21,0xF0,0x8A,0x2E,0x07,0xF2,0x22,0xF0,0x8B,0x2E,0x07,0xF2,0x23,0x54,0x10, +0x00,0x0F,0x92,0x04,0x96,0x06,0xF0,0x8D,0x2E,0x07,0xF2,0x24,0xF1,0x8C,0x54,0x10, +0x00,0x0F,0x92,0x04,0xF0,0x8F,0x84,0x1F,0xF1,0x8E,0x10,0x0F,0x80,0x55,0x10,0x0F, +0x80,0x56,0x10,0x0F,0x80,0x57,0x10,0x0F,0x80,0x58,0x10,0x0F,0x80,0x59,0x10,0x0F, +0x80,0x5A,0x10,0x0F,0x80,0x5B,0x2E,0x66,0x6E,0x97,0x2E,0x06,0x6E,0x96,0x42,0xA3, +0x00,0x24,0xB1,0xA0,0x44,0x00,0x87,0x04,0x80,0x26,0x3A,0x20,0x14,0x04,0x3A,0x20, +0x94,0x24,0x3A,0x20,0x0C,0x00,0x44,0x00,0x87,0x1C,0x3A,0x20,0x8C,0x20,0x80,0x27, +0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24, +0x3A,0x20,0x14,0x00,0x44,0x00,0x87,0x4C,0x3A,0x20,0x94,0x20,0xB0,0x66,0x3A,0x20, +0x14,0x04,0x3A,0x20,0x94,0x24,0x3A,0x20,0x0C,0x00,0x44,0x00,0x87,0x64,0x3A,0x20, +0x8C,0x20,0x80,0x2F,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24,0x3A,0x20,0x14,0x04, +0x3A,0x20,0x94,0x24,0x3A,0x20,0x14,0x00,0x3A,0x20,0x94,0x20,0xF0,0x06,0x44,0x20, +0x04,0x80,0x84,0x20,0x49,0x00,0x0F,0xFA,0x80,0x08,0x46,0x80,0x00,0x80,0x49,0xFF, +0xD2,0x38,0xB0,0x1D,0x49,0xFF,0xD2,0x49,0x04,0x14,0x00,0x70,0x84,0x10,0xFE,0x0E, +0x58,0x00,0x00,0x08,0x14,0x04,0x00,0x70,0x80,0x07,0x49,0xFF,0xD7,0xDF,0x80,0x06, +0x49,0xFF,0xD8,0x12,0x84,0x03,0x49,0xFF,0xF6,0x09,0x84,0x00,0x49,0xFF,0xE2,0x2D, +0x44,0x17,0x20,0x1C,0x46,0x60,0x00,0x81,0xB4,0x01,0x92,0x0C,0x40,0x00,0x30,0x08, +0xB6,0x01,0x44,0x17,0x20,0x24,0xB4,0x01,0x92,0x0C,0x40,0x00,0x30,0x08,0xB6,0x01, +0x04,0x13,0x00,0x3A,0x46,0x0F,0xF8,0xFF,0x50,0x00,0x0F,0x8F,0xFE,0x0E,0x46,0x10, +0x05,0x00,0x50,0x10,0x80,0x40,0xFE,0x0F,0x14,0x03,0x00,0x3A,0x04,0x13,0x00,0x3B, +0x84,0x18,0xFE,0x0E,0x58,0x00,0x00,0x04,0x14,0x03,0x00,0x3B,0x84,0x00,0x49,0xFF, +0xD2,0xE7,0x84,0x03,0x49,0xFF,0xD3,0x17,0x04,0x13,0x00,0x38,0x46,0x00,0xCC,0x0C, +0x50,0x00,0x0F,0xF3,0xFE,0x0E,0x46,0x1A,0x22,0xA2,0x8C,0x28,0xFE,0x0F,0x14,0x03, +0x00,0x38,0x84,0x1F,0x14,0x04,0x00,0x47,0x14,0x04,0x00,0x49,0x04,0x03,0x03,0x97, +0x84,0x41,0x58,0x00,0x02,0x00,0x80,0x62,0x50,0x13,0x06,0x5C,0x14,0x03,0x03,0x97, +0x50,0x03,0x0A,0x5C,0x49,0xFF,0xD5,0xF0,0x84,0x01,0x49,0xFF,0xD8,0xBC,0x46,0x10, +0x00,0x83,0x46,0x0F,0x3F,0xFF,0x04,0x20,0x80,0xEE,0x50,0x00,0x0F,0xFF,0xFE,0x16, +0x44,0x24,0x2C,0x14,0x14,0x00,0x80,0xEE,0xB4,0x02,0x44,0x14,0x2C,0x1C,0xF0,0x92, +0xB4,0x01,0xB4,0x62,0xF0,0x93,0x44,0x0F,0xE0,0xF0,0xFE,0x1E,0x58,0x00,0x00,0x01, +0xB6,0x02,0xB4,0x41,0x46,0x0F,0xFB,0x3F,0x50,0x00,0x0F,0xFF,0xFE,0x16,0x46,0x20, +0x00,0xC0,0xFE,0x17,0x84,0x41,0xB6,0x01,0x80,0x62,0x44,0x04,0x28,0x14,0x44,0x14, +0x24,0x14,0x49,0xFF,0xD5,0xC1,0x84,0x41,0x80,0x62,0x44,0x04,0x28,0x1C,0x44,0x14, +0x24,0x1C,0x49,0xFF,0xD5,0xB9,0x84,0x01,0x84,0x20,0x49,0xFF,0xE4,0x8D,0x46,0x20, +0x00,0x82,0x46,0x0C,0x08,0x0C,0xA0,0x54,0x50,0x00,0x00,0x80,0xFE,0x0E,0x44,0x11, +0x00,0x01,0xFE,0x0F,0xA8,0x14,0x3C,0x0D,0xFC,0x8A,0x5A,0x08,0x01,0x1C,0xF0,0x04, +0xF1,0x07,0x40,0x00,0x20,0x08,0x40,0x80,0x04,0x04,0x44,0x10,0xFF,0xFF,0x4C,0x80, +0x80,0x12,0xF0,0x10,0xF5,0x11,0x40,0x00,0x20,0x08,0xFF,0x47,0xF5,0x84,0xD1,0x0A, +0xE2,0xA8,0xE9,0x04,0x14,0x8F,0x80,0x04,0x81,0x05,0x50,0x64,0x00,0x40,0x97,0xB1, +0xD5,0x08,0x44,0x00,0x00,0x40,0x44,0x60,0x00,0xCC,0xF0,0x84,0x44,0x80,0x00,0x8C, +0x84,0x01,0x80,0x28,0x49,0xFF,0xD9,0x49,0x80,0x26,0x84,0x01,0x49,0xFF,0xFA,0x16, +0x84,0x00,0x84,0x21,0x49,0xFF,0xD8,0xF1,0x80,0x26,0x84,0x05,0x46,0x60,0x00,0x81, +0x49,0xFF,0xFA,0x0C,0x84,0x01,0x84,0x24,0x49,0xFF,0xE4,0x16,0x04,0x13,0x03,0x97, +0x46,0x0F,0x81,0xC0,0x50,0x00,0x0F,0xFF,0xFE,0x0E,0x46,0x10,0x08,0x2D,0xFE,0x0F, +0x14,0x03,0x03,0x97,0x04,0x13,0x03,0x95,0x44,0x0F,0xF0,0x3F,0xFE,0x0E,0x58,0x00, +0x00,0x40,0x14,0x03,0x03,0x95,0x04,0x13,0x03,0x99,0x46,0x0F,0xFC,0x0F,0x50,0x00, +0x0F,0xFF,0xFE,0x0E,0x44,0x17,0x00,0x00,0xFE,0x0F,0x84,0x41,0x80,0x62,0x14,0x03, +0x03,0x99,0x50,0x13,0x06,0x5C,0x50,0x03,0x0A,0x5C,0x49,0xFF,0xD5,0x45,0x84,0x41, +0x80,0x62,0x50,0x03,0x0A,0x54,0x50,0x13,0x06,0x54,0x49,0xFF,0xD5,0x3D,0x84,0x41, +0x50,0x03,0x0A,0x64,0x50,0x13,0x06,0x64,0x80,0x62,0x49,0xFF,0xD5,0x35,0x84,0x04, +0x80,0x28,0x49,0xFF,0xD8,0xFA,0x04,0x13,0x00,0x2F,0x44,0x0F,0xC0,0xFF,0xFE,0x46, +0x58,0x10,0x84,0x00,0x14,0x13,0x00,0x2F,0x04,0x13,0x00,0x2D,0xFE,0x46,0x58,0x10, +0x81,0x00,0x14,0x13,0x00,0x2D,0x04,0x13,0x00,0x2C,0xFE,0x0E,0x58,0x00,0x2D,0x00, +0x14,0x03,0x00,0x2C,0x2E,0x07,0xF2,0x0C,0x5A,0x00,0x02,0x11,0x84,0x01,0x49,0xFF, +0xF5,0x20,0x46,0x01,0x3A,0x60,0x50,0x00,0x0F,0xA6,0x14,0x03,0x00,0x10,0x46,0x00, +0x06,0x40,0x50,0x00,0x01,0x90,0x14,0x03,0x00,0x11,0x84,0x00,0x49,0xFF,0xE3,0x55, +0x84,0x01,0x49,0xFF,0xE3,0x5E,0x84,0x01,0x84,0x20,0x84,0x44,0x84,0x62,0x44,0x40, +0x00,0x30,0xFA,0xA0,0x49,0xFF,0xD5,0x46,0x46,0x00,0x00,0x81,0x44,0x1F,0xFE,0xFF, +0x04,0x20,0x00,0x17,0xFE,0x56,0x14,0x10,0x00,0x17,0xA0,0x46,0x58,0x10,0x80,0x01, +0xA8,0x46,0x3C,0x1D,0xFC,0x8A,0x5A,0x18,0x01,0x41,0xF1,0x05,0x5A,0x10,0x0F,0x09, +0x04,0x00,0x00,0x23,0x80,0xC1,0x92,0x04,0x96,0x1F,0xF0,0x87,0xD5,0x04,0x84,0x00, +0xF0,0x87,0x84,0xC9,0xF0,0x09,0x5A,0x08,0xFF,0x05,0xF0,0x0A,0x5A,0x00,0xFF,0x12, +0x50,0x0F,0x80,0x5B,0xB6,0x1F,0x50,0x1F,0x80,0x56,0x50,0x0F,0x80,0x55,0x50,0x2F, +0x80,0x57,0xB0,0xD6,0x50,0x4F,0x80,0x59,0x50,0x5F,0x80,0x5A,0x49,0xFF,0xFA,0x4C, +0x84,0xE1,0xF0,0x09,0xF1,0x0A,0x80,0x47,0x80,0xA7,0xB6,0xFF,0x84,0x65,0x84,0x80, +0x49,0xFF,0xFA,0x69,0xF0,0x0D,0x44,0x10,0xFF,0xFF,0xB6,0x1F,0xF0,0x0E,0x80,0x47, +0xF0,0x81,0xF0,0x0F,0xF3,0x0B,0xF0,0x82,0x84,0x00,0xF0,0x83,0xF4,0x0C,0x80,0x07, +0x80,0xA7,0x49,0xFF,0xFA,0xF7,0xD5,0x04,0x84,0x00,0xF0,0x87,0x84,0xC9,0x84,0x00, +0x49,0xFF,0xF7,0x04,0x46,0x10,0x00,0x80,0x46,0x05,0x55,0x55,0x50,0x00,0x05,0x55, +0x14,0x00,0x80,0x47,0x14,0x00,0x80,0x49,0x49,0xFF,0xD4,0xCB,0x49,0xFF,0xF4,0xBD, +0x84,0x01,0x49,0xFF,0xF6,0xF3,0x3C,0x0D,0xFC,0x8A,0xC0,0x04,0x93,0x24,0x5A,0x98, +0x0F,0x1D,0xF0,0x08,0x85,0x24,0x9B,0xF0,0xE0,0xE9,0xE9,0x21,0x49,0xFF,0xF9,0x6D, +0x49,0xFF,0xF4,0xA1,0x49,0xFF,0xD5,0x0D,0xF0,0x89,0x49,0xFF,0xD4,0xB2,0xF0,0x09, +0xC0,0x16,0x50,0x04,0x80,0x01,0x54,0x90,0x00,0xFF,0x80,0x29,0x84,0x01,0x49,0xFF, +0xE3,0x1B,0x49,0xFF,0xF4,0x9A,0xD5,0xE9,0xF0,0x05,0xE2,0x09,0xE8,0x04,0x8E,0x01, +0x54,0x90,0x00,0xFF,0x84,0x01,0x80,0x29,0x49,0xFF,0xE3,0x0E,0x49,0xFF,0xF4,0x83, +0x84,0x00,0x49,0xFF,0xF6,0xC3,0xF0,0x08,0x9B,0xF0,0xE1,0x27,0xE9,0x07,0x54,0x93, +0x80,0xFF,0x84,0x01,0x80,0x29,0x49,0xFF,0xE2,0xFF,0xE1,0x27,0xE8,0x12,0x84,0x20, +0x8A,0xC9,0xB0,0xD7,0x9C,0x89,0x88,0x23,0x96,0x11,0xA7,0x49,0x8C,0xA1,0xDE,0x06, +0x96,0x00,0x84,0x21,0x49,0xFF,0xD7,0xC1,0xD5,0x04,0x80,0x22,0x5A,0x28,0x09,0xF3, +0x49,0xFF,0xF4,0x61,0x46,0x65,0x55,0x55,0x84,0x01,0x49,0xFF,0xF6,0x9F,0x50,0x63, +0x05,0x55,0x84,0x40,0x80,0x06,0x80,0xA2,0x80,0x28,0x80,0x6A,0x50,0x4F,0x81,0x10, +0x49,0xFF,0xFC,0xD3,0x80,0x06,0x84,0x40,0x46,0x6A,0xAA,0xAA,0x80,0xA2,0x50,0x63, +0x0A,0xAA,0xF1,0x04,0x80,0x6A,0x50,0x4F,0x85,0x90,0x49,0xFF,0xFC,0xC6,0x80,0x06, +0x80,0x28,0x84,0x41,0x80,0x6A,0x50,0x4F,0x81,0x10,0x84,0xA0,0x49,0xFF,0xFC,0xBD, +0x80,0x06,0xF1,0x04,0x84,0x41,0x80,0x6A,0x50,0x4F,0x85,0x90,0x84,0xA0,0x49,0xFF, +0xFC,0xB4,0x40,0x65,0x08,0x09,0x50,0x0F,0x81,0x10,0x50,0x1F,0x85,0x90,0xF2,0x06, +0x49,0xFF,0xF9,0x3C,0x97,0xB1,0x85,0x20,0x44,0x00,0x00,0xFF,0x40,0xA0,0x24,0x0C, +0xFA,0x02,0x42,0x04,0x80,0x24,0x96,0x01,0xF0,0x88,0x50,0x7F,0x81,0x10,0x80,0x0A, +0x80,0x28,0xF2,0x08,0x80,0x66,0x80,0x87,0x84,0xA1,0x49,0xFF,0xFC,0x96,0x8D,0x28, +0x80,0x0A,0xF1,0x04,0xF2,0x08,0x80,0x66,0x50,0x4F,0x85,0x90,0x84,0xA1,0x49,0xFF, +0xFC,0x8C,0x5A,0x98,0x20,0xE3,0x80,0x07,0x50,0x1F,0x85,0x90,0xF2,0x06,0x46,0xA0, +0x30,0x30,0x49,0xFF,0xF9,0x13,0x50,0x05,0x03,0x03,0x85,0x20,0xF0,0x89,0xF7,0x88, +0xF0,0x09,0x40,0x74,0x80,0x13,0x40,0xA0,0x24,0x0C,0x80,0x0A,0x80,0x28,0x80,0x47, +0x80,0x66,0xF4,0x08,0x84,0xA2,0x49,0xFF,0xFC,0x70,0x8D,0x22,0x80,0x0A,0xF1,0x04, +0x80,0x47,0x80,0x66,0x50,0x4F,0x85,0x90,0x84,0xA2,0x49,0xFF,0xFC,0x66,0x5A,0x98, +0x08,0xE9,0xF2,0x06,0xF0,0x08,0x50,0x1F,0x85,0x90,0x49,0xFF,0xF8,0xEF,0x46,0x00, +0x00,0x80,0x84,0x3F,0x46,0x60,0x00,0x81,0x14,0x10,0x00,0x47,0x14,0x10,0x00,0x49, +0x04,0x03,0x00,0x17,0x84,0xFE,0x58,0x00,0x01,0x00,0x14,0x03,0x00,0x17,0xA0,0x36, +0xFE,0x3E,0xA8,0x36,0x84,0x01,0x49,0xFF,0xD6,0x76,0xF1,0x12,0x44,0x04,0x2C,0x14, +0x84,0x41,0xB6,0x20,0xF1,0x13,0x44,0x04,0x2C,0x1C,0x80,0x62,0xB6,0x20,0x44,0x04, +0x28,0x14,0x44,0x14,0x24,0x14,0x49,0xFF,0xD3,0x97,0x84,0x41,0x80,0x62,0x44,0x04, +0x28,0x1C,0x44,0x14,0x24,0x1C,0x49,0xFF,0xD3,0x8F,0x46,0x00,0x00,0x82,0xA0,0x44, +0xFF,0xCE,0xA9,0xC4,0xA0,0x84,0x44,0x1E,0xFF,0xFF,0xFE,0x56,0xA8,0x44,0x84,0x01, +0x49,0xFF,0xF3,0x4C,0xB0,0x38,0x49,0xFF,0xD5,0x19,0xB0,0x26,0x49,0xFF,0xD5,0x4C, +0x84,0x01,0x49,0xFF,0xE1,0xCA,0x84,0x00,0x49,0xFF,0xE1,0xD3,0x84,0x01,0x84,0x22, +0x80,0x61,0x80,0xA0,0x84,0x44,0x44,0x40,0x00,0x3F,0x49,0xFF,0xD3,0xBB,0x84,0x20, +0x84,0x01,0x49,0xFF,0xE2,0x41,0x49,0x00,0x06,0xBD,0x84,0x00,0x49,0xFF,0xF3,0x97, +0x84,0x00,0x49,0xFF,0xF3,0xDC,0x2E,0x07,0xF2,0x0C,0x5A,0x00,0x02,0x11,0x84,0x00, +0x49,0xFF,0xF3,0x67,0x46,0x00,0xDE,0x90,0x50,0x00,0x09,0xE9,0x14,0x03,0x00,0x10, +0x46,0x00,0x06,0x40,0x50,0x00,0x01,0x90,0x14,0x03,0x00,0x11,0x84,0x00,0x49,0xFF, +0xF2,0xBF,0x3C,0x1D,0xFC,0x8A,0x5A,0x18,0x01,0x26,0xF0,0x05,0x5A,0x00,0x0F,0x05, +0xF0,0x07,0x49,0xFF,0xD6,0xBA,0x00,0x6F,0x80,0x5B,0x00,0x0F,0x80,0x55,0x00,0x1F, +0x80,0x56,0x00,0x2F,0x80,0x57,0x00,0x3F,0x80,0x58,0x00,0x4F,0x80,0x59,0x00,0x5F, +0x80,0x5A,0xB6,0xDF,0x49,0xFF,0xF8,0xDF,0xF0,0x0E,0x44,0x10,0x00,0xF0,0xB6,0x1F, +0xF0,0x0F,0xF3,0x0B,0xF0,0x81,0x84,0x01,0x80,0x40,0xF4,0x0C,0xF5,0x0D,0x49,0xFF, +0xF9,0x3A,0x49,0xFF,0xF8,0x95,0x44,0x00,0x00,0x51,0x3E,0x07,0xF0,0x60,0x51,0xFF, +0x8A,0x10,0xFC,0xC0,0xFC,0x48,0x2E,0x07,0xF2,0x1B,0x2E,0x87,0xF2,0x1D,0xF0,0x8C, +0x2E,0x07,0xF2,0x1C,0x2E,0x67,0xF2,0x22,0xF0,0x8D,0x2E,0x07,0xF2,0x1E,0xF0,0x86, +0x54,0x00,0x00,0x06,0x92,0x01,0xF0,0x85,0x2E,0x07,0xF2,0x1F,0xF0,0x87,0x2E,0x07, +0xF2,0x20,0x54,0x10,0x00,0x0F,0x92,0x04,0x96,0x06,0xF0,0x89,0x2E,0x07,0xF2,0x21, +0xF1,0x88,0x54,0x10,0x00,0x0F,0x92,0x04,0xF0,0x8B,0x84,0x1F,0x10,0x0F,0x80,0x39, +0x10,0x0F,0x80,0x3A,0x10,0x0F,0x80,0x3B,0x10,0x0F,0x80,0x3C,0x10,0x0F,0x80,0x3D, +0x10,0x0F,0x80,0x3E,0x10,0x0F,0x80,0x3F,0x2E,0x07,0xF2,0x19,0xF1,0x8A,0x5A,0x08, +0x08,0x08,0x2E,0x07,0xF2,0x23,0x2E,0x97,0xF2,0x24,0xF0,0x84,0xD5,0x0B,0x5A,0x08, +0x05,0x06,0x2E,0x97,0xF2,0x23,0xF6,0x84,0xD5,0x05,0x44,0x90,0x00,0xFF,0x14,0x9F, +0x80,0x04,0x3C,0x7D,0xFC,0x8A,0x5A,0x78,0x01,0x16,0x2E,0x07,0xF2,0x0C,0x5A,0x00, +0x02,0x12,0xF0,0x05,0xC8,0x06,0x80,0x07,0x49,0xFF,0xF2,0xD3,0x80,0x07,0xD5,0x08, +0xF0,0x05,0x5A,0x08,0x01,0x08,0x84,0x00,0x49,0xFF,0xF2,0xCB,0x84,0x00,0x49,0xFF, +0xF2,0x19,0x2E,0x07,0xF2,0x19,0x5A,0x08,0x05,0x26,0x46,0x00,0x00,0x82,0xA0,0x44, +0x58,0x10,0x80,0x01,0xA8,0x44,0xA0,0x84,0x44,0x11,0x00,0x00,0xFE,0x57,0xA8,0x44, +0x84,0x00,0x80,0x20,0x49,0xFF,0xF7,0x46,0x84,0x00,0x80,0x20,0x49,0xFF,0xE1,0x7C, +0x46,0x10,0x00,0x81,0x44,0x0F,0xFD,0xFF,0x04,0x20,0x80,0x33,0xFE,0x16,0x14,0x00, +0x80,0x33,0x84,0x00,0x84,0x21,0x49,0xFF,0xD6,0xB4,0x84,0x00,0x80,0x20,0x49,0xFF, +0xCD,0x6B,0x84,0x00,0x49,0xFF,0xD1,0x34,0x84,0x00,0x49,0xFF,0xD1,0x45,0x3C,0x0D, +0xFC,0x8A,0x84,0xE0,0x5A,0x08,0x01,0x73,0x84,0xE0,0x5A,0x80,0xFF,0x0F,0x46,0x00, +0x00,0x81,0x84,0x20,0x04,0x70,0x00,0x23,0x80,0x08,0x49,0xFF,0xF9,0x23,0x80,0x08, +0x84,0x23,0x97,0xDF,0x49,0xFF,0xF9,0x1E,0xF0,0x04,0x5A,0x08,0xFF,0x04,0x5A,0x90, +0xFF,0x12,0x50,0x0F,0x80,0x3F,0xB6,0x1F,0x50,0x1F,0x80,0x3A,0x50,0x0F,0x80,0x39, +0x50,0x2F,0x80,0x3B,0xB0,0xCF,0x50,0x4F,0x80,0x3D,0x50,0x5F,0x80,0x3E,0x49,0xFF, +0xF7,0xEB,0x85,0x41,0xF0,0x04,0x80,0x29,0x80,0x4A,0x84,0x65,0x84,0x80,0x80,0xAA, +0xB7,0x5F,0x49,0xFF,0xF8,0x08,0xF0,0x0C,0xF2,0x0D,0x40,0x10,0x20,0x08,0xF0,0x09, +0xFE,0x57,0xB6,0x1F,0xF0,0x0A,0xF3,0x07,0xF0,0x81,0xF0,0x0B,0xF4,0x08,0xF0,0x82, +0xF0,0x06,0x40,0x00,0x28,0x02,0xF0,0x83,0x84,0x00,0x80,0x40,0x80,0xA0,0x49,0xFF, +0xF8,0x91,0x2E,0x07,0xF2,0x19,0x5A,0x08,0x08,0x2A,0x96,0x32,0x4E,0x05,0x00,0x04, +0x80,0x0A,0xD5,0x02,0x84,0x00,0x49,0xFF,0xDC,0xD2,0x54,0x13,0x00,0x01,0x46,0x00, +0x00,0x82,0xC9,0x13,0xA0,0x44,0x58,0x10,0x80,0x01,0xA8,0x44,0xA0,0x84,0x44,0x11, +0x00,0x00,0xFE,0x57,0xA8,0x44,0x40,0x03,0x18,0x09,0x54,0x13,0x00,0x3E,0x96,0x06, +0x92,0x21,0x49,0xFF,0xF6,0xBF,0xD5,0x0A,0xA0,0x84,0x84,0x3E,0xFE,0x56,0xA8,0x44, +0xA0,0x84,0x44,0x1E,0xFF,0xFF,0xFE,0x56,0xA8,0x44,0x49,0xFF,0xF7,0x37,0x84,0x01, +0x49,0xFF,0xD0,0xB6,0x84,0x01,0x49,0xFF,0xD0,0xC7,0x2E,0x07,0xF2,0x19,0x5A,0x08, +0x08,0x08,0x84,0x00,0x49,0xFF,0xF4,0x6A,0x49,0xFF,0xF2,0x25,0xD5,0x19,0x5A,0x08, +0x05,0x18,0x46,0x00,0x00,0x82,0x84,0x3E,0xA0,0x84,0xFE,0x56,0xA8,0x44,0xA0,0x84, +0x44,0x1E,0xFF,0xFF,0xFE,0x56,0xA8,0x44,0x46,0x10,0x00,0x81,0x04,0x00,0x80,0x33, +0x58,0x00,0x02,0x00,0x14,0x00,0x80,0x33,0x84,0x00,0x49,0xFF,0xF3,0x2A,0x3C,0x0D, +0xFC,0x8A,0x5A,0x08,0x01,0x4F,0x2E,0x07,0xF2,0x19,0x5A,0x08,0x08,0x16,0x54,0x03, +0x00,0x01,0xC8,0x0C,0x46,0x00,0x00,0x82,0x84,0x3E,0xA0,0x84,0xFE,0x56,0xA8,0x44, +0xA0,0x84,0x44,0x1E,0xFF,0xFF,0xFE,0x56,0xA8,0x44,0x97,0xB2,0x4E,0x65,0x00,0x05, +0x84,0x00,0x49,0xFF,0xDC,0x6C,0x5A,0x80,0xFF,0x0A,0x80,0x07,0x84,0x20,0x49,0xFF, +0xD5,0x44,0x80,0x07,0x84,0x23,0x49,0xFF,0xD5,0x40,0xF0,0x05,0x5A,0x00,0x01,0x0C, +0x2E,0x07,0xF2,0x0C,0x5A,0x00,0x02,0x08,0x84,0x00,0x49,0xFF,0xF1,0xCA,0x84,0x00, +0x49,0xFF,0xF1,0x18,0x00,0x6F,0x80,0x3F,0x00,0x0F,0x80,0x39,0x00,0x1F,0x80,0x3A, +0x00,0x2F,0x80,0x3B,0x00,0x3F,0x80,0x3C,0x00,0x4F,0x80,0x3D,0x00,0x5F,0x80,0x3E, +0xB6,0xDF,0x49,0xFF,0xF7,0x58,0xF0,0x0A,0x44,0x10,0x00,0xF0,0xB6,0x1F,0xF0,0x0B, +0xF3,0x07,0xF0,0x81,0x84,0x00,0x80,0x40,0xF4,0x08,0xF5,0x09,0x49,0xFF,0xF7,0xB3, +0x49,0xFF,0xF7,0x0E,0x44,0x00,0x00,0x51,0x3E,0x07,0xF0,0x60,0xFC,0xC8,0xFC,0x40, +0x51,0xFF,0xF6,0xB8,0x80,0xE0,0x80,0xC1,0x81,0x22,0x84,0x20,0x44,0x20,0x04,0x80, +0xB0,0x12,0x49,0x00,0x0B,0x2B,0x84,0x20,0x44,0x20,0x04,0x80,0x50,0x0F,0x84,0xC8, +0x49,0x00,0x0B,0x24,0x2E,0x07,0xF2,0x1C,0x2E,0x17,0xF2,0x1B,0xF0,0x8D,0x2E,0x07, +0xF2,0x1D,0x2E,0x87,0xF2,0x1E,0xF0,0x8E,0x2E,0x07,0xF2,0x1F,0x2E,0xA7,0xF2,0x23, +0xF0,0x86,0x2E,0x07,0xF2,0x20,0x54,0x20,0x00,0x0F,0x92,0x04,0x96,0x06,0xF0,0x88, +0x2E,0x07,0xF2,0x21,0xF2,0x87,0x54,0x20,0x00,0x0F,0x92,0x04,0xF0,0x8A,0x2E,0x07, +0xF2,0x22,0xF2,0x89,0xF0,0x8B,0x84,0x1F,0x10,0x0F,0x80,0x41,0x10,0x0F,0x80,0x42, +0x10,0x0F,0x80,0x43,0x10,0x0F,0x80,0x44,0x10,0x0F,0x80,0x45,0x10,0x0F,0x80,0x46, +0x10,0x0F,0x80,0x47,0x3C,0x0D,0xFC,0x8A,0x5A,0x00,0x01,0x08,0x40,0x73,0xA0,0x08, +0xFF,0xBF,0x40,0x04,0x80,0x13,0xD5,0x08,0x84,0xCA,0x5A,0x10,0xFF,0x03,0x80,0xC1, +0x2E,0x07,0xF2,0x1A,0x96,0x06,0xF0,0x85,0xF0,0x05,0x5A,0x08,0x01,0x14,0x46,0x00, +0x00,0x82,0xA0,0x44,0x58,0x10,0x80,0x01,0xA8,0x44,0xA0,0x84,0x44,0x11,0x00,0x00, +0xFE,0x57,0xA8,0x44,0x84,0x00,0x80,0x20,0x49,0xFF,0xF5,0xCC,0xF0,0x05,0x49,0xFF, +0xDB,0xC6,0x49,0xFF,0xF1,0x2A,0xF0,0x8F,0x49,0xFF,0xF1,0x2A,0xF0,0x8C,0x84,0x00, +0x49,0xFF,0xCF,0xC6,0x84,0x00,0x49,0xFF,0xCF,0xD7,0x3C,0x0D,0xFC,0x8A,0x84,0xE0, +0x5A,0x08,0x01,0x45,0x84,0xE0,0x5A,0x80,0xFF,0x0F,0x46,0x00,0x00,0x81,0x84,0x20, +0x04,0x70,0x00,0x23,0x80,0x08,0x49,0xFF,0xF7,0xB5,0x80,0x08,0x84,0x23,0x97,0xDF, +0x49,0xFF,0xF7,0xB0,0xF0,0x0B,0x5A,0x08,0xFF,0x04,0x5A,0xA0,0xFF,0x12,0x50,0x0F, +0x80,0x47,0xB6,0x1F,0x50,0x1F,0x80,0x42,0x50,0x0F,0x80,0x41,0x50,0x2F,0x80,0x43, +0xB0,0xD1,0x50,0x4F,0x80,0x45,0x50,0x5F,0x80,0x46,0x49,0xFF,0xF6,0x7D,0x85,0x21, +0xF0,0x0B,0x80,0x2A,0x80,0x49,0x84,0x65,0x84,0x80,0x80,0xA9,0xB7,0x3F,0x49,0xFF, +0xF6,0x9A,0xF0,0x0D,0xF2,0x0E,0x40,0x10,0x20,0x08,0xF0,0x08,0xFE,0x57,0xB6,0x1F, +0xF0,0x09,0x14,0x9F,0x80,0x03,0xF0,0x81,0xF0,0x0A,0xF3,0x06,0xF0,0x82,0x84,0x00, +0x80,0x40,0xF4,0x07,0x80,0xA0,0x49,0xFF,0xF7,0x25,0x49,0xFF,0xF5,0xF7,0x85,0x20, +0x50,0xAF,0x84,0xC8,0x4C,0x93,0x00,0x36,0x49,0xFF,0xF5,0xF0,0xF2,0x0C,0x84,0x00, +0xB0,0xD2,0x9F,0x31,0xF1,0x0F,0x38,0x10,0x81,0x11,0x4E,0x93,0x00,0x11,0x38,0x15, +0x01,0x09,0x38,0x11,0x81,0x09,0x5A,0x68,0x01,0x1F,0xA5,0x50,0x9A,0x69,0x96,0x49, +0x97,0x4B,0xAD,0x50,0x4E,0x54,0x00,0x18,0xFE,0x4A,0xD5,0x14,0x38,0x55,0x01,0x11, +0xE0,0xA1,0xE8,0x03,0x38,0x15,0x01,0x09,0x38,0xF1,0x81,0x11,0xE0,0x2F,0xE8,0x03, +0x38,0x11,0x81,0x09,0x4C,0x92,0x40,0x08,0x38,0x15,0x01,0x01,0x38,0x51,0x81,0x01, +0x8A,0x25,0xAC,0x50,0x8C,0x01,0x8C,0x42,0x5A,0x0A,0x40,0xD6,0x8D,0x21,0xD5,0xCB, +0x46,0x00,0x00,0x82,0x84,0x3E,0xA0,0x84,0xFE,0x56,0xA8,0x44,0xA0,0x84,0x44,0x1E, +0xFF,0xFF,0xFE,0x56,0xA8,0x44,0x84,0x01,0x49,0xFF,0xCF,0x32,0x84,0x01,0x49,0xFF, +0xCF,0x43,0x4E,0x92,0x00,0x09,0xF0,0x05,0x5A,0x08,0x01,0x0D,0x84,0x00,0x49,0xFF, +0xDB,0x1E,0xD5,0x08,0xF5,0x0C,0x50,0x02,0x84,0x80,0x1A,0x92,0x80,0x01,0xD8,0xFE, +0xD5,0xF3,0x3C,0x0D,0xFC,0x8A,0x5A,0x08,0x01,0x2A,0x5A,0x80,0xFF,0x0A,0x80,0x07, +0x84,0x20,0x49,0xFF,0xD3,0xEA,0x80,0x07,0x84,0x23,0x49,0xFF,0xD3,0xE6,0x00,0x6F, +0x80,0x47,0x00,0x0F,0x80,0x41,0x00,0x1F,0x80,0x42,0x00,0x2F,0x80,0x43,0x00,0x3F, +0x80,0x44,0x00,0x4F,0x80,0x45,0x00,0x5F,0x80,0x46,0xB6,0xDF,0x49,0xFF,0xF6,0x0B, +0xF0,0x09,0x44,0x10,0x00,0xF0,0xB6,0x1F,0xF0,0x0A,0xF3,0x06,0xF0,0x81,0x84,0x00, +0x80,0x40,0xF4,0x07,0xF5,0x08,0x49,0xFF,0xF6,0x66,0x49,0xFF,0xF5,0xC1,0x44,0x00, +0x00,0x51,0x3E,0x07,0xF0,0x60,0x51,0xFF,0x89,0x48,0xFC,0xC0,0xFC,0x40,0x51,0xFF, +0xF5,0x40,0x49,0xFF,0xCC,0xD3,0xF0,0x8D,0x84,0x20,0x44,0x20,0x04,0x80,0x50,0x0F, +0x81,0xC0,0x49,0x00,0x09,0xDB,0x44,0x20,0x04,0x80,0x84,0x20,0x50,0x0F,0x86,0x40, +0x49,0x00,0x09,0xD4,0x2E,0x07,0xF2,0x1F,0x2E,0x77,0xF2,0x1E,0xF0,0x87,0x2E,0x07, +0xF2,0x20,0x2E,0xA7,0xF2,0x23,0x54,0x10,0x00,0x0F,0x92,0x04,0x96,0x06,0xF0,0x89, +0x2E,0x07,0xF2,0x21,0xF1,0x88,0x54,0x10,0x00,0x0F,0x92,0x04,0xF0,0x8B,0x2E,0x07, +0xF2,0x22,0xF1,0x8A,0x2E,0x17,0xF2,0x1B,0xF0,0x8C,0x84,0x1F,0xF1,0x85,0x10,0x0F, +0x80,0x39,0x10,0x0F,0x80,0x3A,0x10,0x0F,0x80,0x3B,0x10,0x0F,0x80,0x3C,0x10,0x0F, +0x80,0x3D,0x10,0x0F,0x80,0x3E,0x10,0x0F,0x80,0x3F,0x2E,0x67,0xF2,0x1C,0x2E,0x87, +0xF2,0x1D,0x49,0xFF,0xF0,0x05,0xF0,0x86,0x84,0x01,0x49,0xFF,0xDA,0x73,0x2E,0x07, +0xF2,0x19,0x54,0x93,0x80,0x01,0xF1,0x05,0x5A,0x08,0x32,0x10,0x2E,0x67,0xF2,0x1C, +0x2E,0x07,0xF2,0x1D,0x40,0x63,0x20,0x08,0xFF,0x87,0x2E,0x77,0xF2,0x1E,0x85,0x21, +0x5A,0x10,0xFF,0x16,0x81,0x01,0xD5,0x14,0x5A,0x00,0x33,0x07,0x40,0x63,0x20,0x08, +0x40,0x63,0x20,0x04,0xD5,0x0A,0x2E,0x67,0xF2,0x1B,0x2E,0x07,0xF2,0x1C,0x40,0x63, +0x20,0x08,0xFF,0x87,0x2E,0x77,0xF2,0x1D,0x85,0x00,0xD5,0x02,0x85,0x0A,0x3C,0x0D, +0xFC,0x8A,0x5A,0x08,0x01,0x44,0x5A,0x70,0xFF,0x11,0x46,0x00,0x00,0x81,0x84,0x20, +0x04,0x00,0x00,0x23,0x96,0x1F,0xF0,0x85,0x80,0x07,0x49,0xFF,0xF6,0x6B,0x80,0x07, +0x84,0x23,0x49,0xFF,0xF6,0x67,0xD5,0x03,0x84,0x00,0xF0,0x85,0xF0,0x0C,0x5A,0x08, +0xFF,0x04,0x5A,0xA0,0xFF,0x12,0x50,0x0F,0x80,0x3F,0xB6,0x1F,0x50,0x1F,0x80,0x3A, +0x50,0x0F,0x80,0x39,0x50,0x2F,0x80,0x3B,0xB0,0xCF,0x50,0x4F,0x80,0x3D,0x50,0x5F, +0x80,0x3E,0x49,0xFF,0xF5,0x31,0x84,0x41,0xB6,0x5F,0xF0,0x0C,0x80,0x2A,0x80,0xA2, +0x84,0x65,0x84,0x80,0x49,0xFF,0xF5,0x4F,0xF0,0x09,0x14,0x9F,0x80,0x03,0xB6,0x1F, +0xF0,0x0A,0x80,0x26,0xF0,0x81,0xF0,0x0B,0xF3,0x07,0xF0,0x82,0x84,0x00,0x80,0x40, +0xF4,0x08,0x80,0xA0,0x49,0xFF,0xF5,0xDE,0xD5,0x03,0x84,0x00,0xF0,0x85,0x49,0xFF, +0xCD,0x16,0xC0,0x09,0x49,0x00,0x03,0x71,0xE6,0x03,0xE9,0x30,0x84,0x00,0x49,0xFF, +0xCC,0x05,0xD5,0x2C,0x2E,0x07,0xF2,0x0C,0x5A,0x00,0x02,0xF6,0x84,0x01,0x3E,0x07, +0xF1,0xF4,0xFA,0x2E,0x3E,0x07,0xF1,0xF3,0x84,0x0B,0x49,0x00,0x03,0xD1,0x84,0x0B, +0x84,0x21,0x49,0x00,0x03,0xDB,0x84,0x01,0x49,0xFF,0xCC,0xEF,0x49,0xFF,0xCC,0xF7, +0x5A,0x00,0x01,0x0C,0x84,0x0B,0x49,0x00,0x03,0xEC,0x5A,0x08,0x01,0xF9,0x84,0x00, +0x3E,0x07,0xF1,0xF4,0x3E,0x07,0xF1,0xF3,0x84,0x0B,0x84,0x20,0x49,0x00,0x03,0xC6, +0x2E,0x07,0xF1,0xF4,0x4E,0x02,0x01,0x7C,0xD5,0xCE,0x84,0x00,0x49,0xFF,0xCB,0xC8, +0x84,0x00,0x49,0xFF,0xEF,0x84,0x84,0x00,0x49,0xFF,0xEE,0xC2,0x84,0x01,0x49,0xFF, +0xEE,0xCB,0x49,0xFF,0xEE,0x72,0x49,0xFF,0xEE,0x5B,0x84,0x03,0x49,0xFF,0xEF,0x0E, +0x84,0x00,0x49,0xFF,0xDB,0x32,0x49,0x00,0x03,0x2C,0x84,0x03,0x49,0xFF,0xD5,0x9B, +0x44,0x00,0x7F,0xFF,0x49,0x00,0x03,0x30,0x84,0x00,0x49,0xFF,0xD5,0xBE,0x84,0x00, +0x49,0xFF,0xD4,0xBA,0x84,0x00,0x49,0xFF,0xCE,0x5F,0x84,0x00,0x80,0x20,0x49,0xFF, +0xD7,0x03,0x49,0xFF,0xD7,0x73,0xF0,0x8C,0x84,0x00,0x49,0xFF,0xD7,0x66,0x84,0x01, +0x49,0xFF,0xD0,0x70,0x49,0xFF,0xD6,0x5C,0x49,0xFF,0xCE,0xB4,0x5A,0x00,0x01,0xFE, +0x84,0x01,0x49,0xFF,0xF1,0x7B,0x49,0xFF,0xEF,0x40,0x46,0x10,0x00,0x91,0x04,0x00, +0x80,0x11,0x58,0x00,0x00,0x02,0x14,0x00,0x80,0x11,0x2E,0x07,0xF2,0x19,0x5A,0x08, +0x32,0x23,0xF0,0x06,0x84,0x20,0x44,0x20,0x04,0x80,0x49,0x00,0x08,0xA7,0x85,0x20, +0x44,0x64,0x80,0x08,0x50,0x04,0x00,0x02,0xE3,0x20,0xE8,0x51,0x84,0x01,0x85,0x40, +0x49,0xFF,0xF1,0x52,0x3E,0xA7,0xF0,0xFB,0x49,0xFF,0xDA,0xFD,0x3E,0xA7,0xF0,0xFA, +0x2E,0x07,0xF0,0xFA,0x4E,0x03,0x00,0xC1,0x3C,0x0D,0xFE,0x0F,0x5A,0x08,0x01,0xFA, +0x48,0x00,0x00,0xBB,0x5A,0x08,0x33,0x3C,0x84,0x01,0x44,0xA4,0x80,0x08,0x49,0xFF, +0xF1,0x3B,0x85,0x23,0x80,0xCA,0x85,0x00,0x3E,0x87,0xF0,0xFB,0x49,0xFF,0xDA,0xE3, +0x3E,0x87,0xF0,0xFA,0x2E,0x07,0xF0,0xFA,0xC0,0x4E,0x84,0x00,0x3E,0x07,0xF0,0xFA, +0xB4,0x0A,0x92,0x02,0x96,0x06,0xC0,0xFD,0x84,0x04,0xAE,0x30,0x50,0x04,0xFF,0xFF, +0x40,0x90,0x00,0x13,0x4E,0x93,0xFF,0xE9,0x80,0x09,0xB1,0x90,0x49,0xFF,0xF1,0x1C, +0x80,0x06,0x44,0x10,0x00,0xC0,0x49,0xFF,0xE1,0xDE,0x44,0x20,0x04,0x80,0xF0,0x06, +0x80,0x29,0x49,0x00,0x08,0x5B,0x84,0x46,0xF3,0x06,0x42,0x04,0x88,0x24,0x38,0x13, +0x25,0x11,0x8D,0x21,0x38,0x11,0x80,0x09,0x5A,0x98,0xC0,0xF8,0x46,0x10,0x00,0x91, +0x84,0x1D,0x04,0x20,0x80,0x11,0xFE,0x16,0x14,0x00,0x80,0x11,0x49,0xFF,0xD5,0xE0, +0x84,0x00,0x49,0xFF,0xF1,0x03,0x84,0x00,0x49,0xFF,0xD9,0x14,0x84,0x00,0x49,0xFF, +0xCF,0xE9,0xF0,0x0C,0x49,0xFF,0xD6,0xD9,0x84,0x20,0x84,0x01,0x49,0xFF,0xD6,0x6C, +0x84,0x01,0x49,0xFF,0xCD,0xC1,0x49,0xFF,0xE2,0xA7,0x2E,0x07,0xF1,0xF4,0x5A,0x08, +0x01,0x10,0xD5,0x06,0x3C,0x0D,0xFE,0x0F,0x5A,0x08,0x01,0xAE,0xD5,0xAF,0x49,0xFF, +0xE4,0x5C,0xC8,0x06,0x49,0xFF,0xCC,0x01,0x49,0xFF,0xCC,0x09,0xC8,0xFE,0x84,0x00, +0x3E,0x07,0xF1,0xF4,0x3E,0x07,0xF1,0xF3,0xF0,0x0D,0x49,0xFF,0xCA,0xF7,0x84,0x01, +0x49,0xFF,0xCA,0xE6,0x84,0x00,0x49,0xFF,0xEE,0xA2,0x84,0x00,0x49,0xFF,0xED,0xE0, +0x84,0x00,0x49,0xFF,0xED,0xE9,0x49,0xFF,0xED,0x90,0x84,0x01,0x49,0xFF,0xD3,0xEC, +0x3C,0x0D,0xFC,0x8A,0x5A,0x08,0x01,0x2A,0x5A,0x70,0xFF,0x0A,0xF0,0x05,0x84,0x20, +0x49,0xFF,0xD1,0xD3,0xF0,0x05,0x84,0x23,0x49,0xFF,0xD1,0xCF,0x00,0x6F,0x80,0x3F, +0x00,0x0F,0x80,0x39,0x00,0x1F,0x80,0x3A,0x00,0x2F,0x80,0x3B,0x00,0x3F,0x80,0x3C, +0x00,0x4F,0x80,0x3D,0x00,0x5F,0x80,0x3E,0xB6,0xDF,0x49,0xFF,0xF3,0xF4,0xF0,0x0A, +0x44,0x10,0x00,0xF0,0xB6,0x1F,0xF0,0x0B,0xF3,0x07,0xF0,0x81,0x84,0x00,0x80,0x40, +0xF4,0x08,0xF5,0x09,0x49,0xFF,0xF4,0x4F,0x49,0xFF,0xF3,0xAA,0x44,0x00,0x00,0x51, +0x3E,0x07,0xF0,0x60,0xD5,0x54,0x84,0x00,0x3E,0x07,0xF0,0xFA,0xB4,0x06,0x92,0x02, +0x96,0x06,0xC0,0xFD,0x44,0x14,0x80,0x08,0x84,0x04,0xAE,0x08,0x84,0x00,0x49,0xFF, +0xF0,0x7B,0xE7,0x22,0xE9,0x3E,0x50,0xAF,0x80,0x40,0x80,0x0A,0x44,0x10,0x00,0xC0, +0x49,0xFF,0xE1,0x39,0xF3,0x06,0x84,0x20,0x50,0x0F,0x86,0x40,0x50,0x4F,0x81,0xC0, +0x51,0xE4,0x00,0x01,0x5A,0x98,0x02,0x14,0x38,0x55,0x05,0x11,0x38,0x50,0x05,0x09, +0x38,0x52,0x05,0x09,0x5A,0x88,0x01,0x22,0xA4,0x98,0x8A,0x45,0x96,0x91,0x97,0x53, +0x4E,0x55,0x00,0x04,0xAD,0x58,0xD5,0x19,0xFE,0x92,0xD5,0x16,0x38,0x25,0x05,0x11, +0x38,0x50,0x05,0x11,0xE0,0xA2,0xE8,0x03,0x38,0x20,0x05,0x09,0x38,0xF2,0x05,0x11, +0xE0,0x4F,0xE8,0x03,0x38,0x22,0x05,0x09,0x4C,0x9F,0x40,0x08,0x38,0x20,0x05,0x01, +0x38,0x52,0x05,0x01,0x8A,0x45,0xAC,0x98,0x8C,0x21,0x8C,0x66,0x5A,0x18,0xC0,0xD4, +0x50,0x54,0x80,0x01,0x40,0x92,0x80,0x13,0x48,0xFF,0xFE,0xDE,0x51,0xFF,0x8A,0xC0, +0xFC,0xC0,0xFC,0x00,0x49,0xFF,0xD3,0x2C,0xE6,0x03,0xE8,0x14,0x44,0x14,0x30,0x08, +0x84,0x1E,0xB4,0x41,0x44,0x64,0x30,0x0C,0xFE,0x16,0xB6,0x01,0xB4,0x06,0x58,0x00, +0x00,0x01,0xB6,0x06,0x84,0x01,0x49,0xFF,0xD6,0xB6,0xB4,0x06,0x58,0x00,0x00,0x02, +0xB6,0x06,0xFC,0x80,0xFC,0x00,0x44,0x64,0x55,0x08,0x44,0x0F,0xFF,0xBF,0xB4,0x26, +0xFE,0x0E,0xB6,0x06,0x84,0x21,0x80,0x06,0x49,0xFF,0xCD,0x8B,0x44,0x04,0x58,0x88, +0x84,0x37,0xB4,0x40,0xFE,0x56,0xB6,0x20,0x84,0x21,0x49,0xFF,0xCD,0x57,0xB4,0x26, +0x44,0x0F,0xFF,0x7F,0xFE,0x0E,0xB6,0x06,0xB4,0x26,0x44,0x0F,0xFF,0xEF,0xFE,0x0E, +0xB6,0x06,0x84,0x21,0x80,0x06,0x49,0xFF,0xCD,0x74,0x44,0x04,0x80,0x88,0x46,0x10, +0x10,0x00,0xB4,0x40,0xFE,0x57,0xB6,0x20,0xB4,0x40,0x46,0x10,0x20,0x00,0xFE,0x57, +0xB6,0x20,0x49,0xFF,0xD2,0xE5,0xE6,0x03,0xE8,0x1F,0x44,0x14,0x2C,0x0C,0x46,0x00, +0x01,0x00,0xB4,0x41,0xFE,0x17,0x84,0x41,0xB6,0x01,0x80,0x62,0x44,0x04,0x28,0x0C, +0x44,0x14,0x24,0x0C,0x49,0xFF,0xCD,0x80,0x44,0x14,0x2C,0x18,0x46,0x00,0x04,0x00, +0xB4,0x41,0xFE,0x17,0x84,0x41,0xB6,0x01,0x80,0x62,0x44,0x04,0x28,0x18,0x44,0x14, +0x24,0x18,0x49,0xFF,0xCD,0x71,0x46,0x60,0x00,0x81,0x49,0xFF,0xCD,0x7F,0x04,0x13, +0x03,0xA0,0x84,0x10,0xFE,0x0E,0x14,0x03,0x03,0xA0,0x04,0x13,0x03,0x9F,0x46,0x0F, +0x0F,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x46,0x46,0x20,0x40,0x00,0xFE,0x57,0x14,0x13, +0x03,0x9F,0x04,0x13,0x03,0x9F,0x46,0x27,0x00,0x00,0x94,0x4C,0x92,0x24,0xFE,0x57, +0x14,0x13,0x03,0x9F,0x04,0x13,0x03,0x9E,0xFE,0x0E,0x46,0x10,0x70,0x00,0xFE,0x0F, +0x14,0x03,0x03,0x9E,0x49,0xFF,0xCD,0x53,0x46,0x00,0x03,0x33,0x50,0x00,0x03,0x33, +0x44,0x14,0x50,0x0C,0x14,0x03,0x00,0x34,0x14,0x03,0x00,0x36,0xB4,0x01,0x58,0x00, +0x00,0x01,0xB6,0x01,0x44,0x04,0x00,0x90,0x44,0x10,0x00,0x64,0xAC,0x40,0x44,0x14, +0x30,0x34,0x46,0x00,0x04,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0x44,0x07,0x10,0x18, +0x84,0x20,0xAE,0x41,0x44,0x16,0x20,0x04,0x46,0x00,0x10,0x00,0xB4,0x41,0xFE,0x17, +0xB6,0x01,0x44,0x14,0x2C,0x18,0x46,0x0B,0xFF,0xFF,0xB4,0x41,0x50,0x00,0x0F,0xFF, +0xFE,0x16,0xB6,0x01,0x44,0x14,0x2C,0x1C,0x44,0x0F,0xFF,0xEF,0xB4,0x41,0xFE,0x16, +0x84,0x41,0xB6,0x01,0x80,0x62,0x44,0x04,0x28,0x18,0x44,0x14,0x24,0x18,0x49,0xFF, +0xCD,0x0B,0x84,0x41,0x44,0x04,0x28,0x1C,0x44,0x14,0x24,0x1C,0x80,0x62,0x49,0xFF, +0xCD,0x03,0x49,0xFF,0xD2,0x55,0xE6,0x03,0xE8,0x10,0x44,0x14,0x2C,0x04,0x46,0x00, +0x00,0x80,0xB4,0x41,0xFE,0x17,0x84,0x41,0xB6,0x01,0x80,0x62,0x44,0x04,0x28,0x04, +0x44,0x14,0x24,0x04,0x49,0xFF,0xCC,0xF0,0x49,0xFF,0xD2,0x42,0xE6,0x03,0xE9,0x40, +0x84,0x08,0x49,0xFF,0xD7,0x6F,0x84,0x00,0x49,0xFF,0xD7,0x75,0x84,0x41,0x80,0x62, +0x44,0x04,0x28,0x14,0x44,0x14,0x24,0x14,0x49,0xFF,0xCC,0xDE,0x44,0x14,0x2C,0x0C, +0x46,0x0F,0xFE,0xFF,0xB4,0x41,0x50,0x00,0x0F,0xFF,0xFE,0x16,0x84,0x41,0xB6,0x01, +0x80,0x62,0x44,0x04,0x28,0x0C,0x44,0x14,0x24,0x0C,0x49,0xFF,0xCC,0xCD,0x44,0x04, +0x2C,0x04,0x46,0x10,0x08,0x00,0xB4,0x40,0xFE,0x57,0xB6,0x20,0xB4,0x40,0x46,0x1F, +0xFB,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x56,0xB6,0x20,0xB4,0x40,0x46,0x10,0x00,0x80, +0xFE,0x57,0xB6,0x20,0xB4,0x40,0x44,0x14,0x00,0x00,0xFE,0x57,0x84,0x41,0xB6,0x20, +0x80,0x62,0x44,0x04,0x28,0x04,0x44,0x14,0x24,0x04,0x49,0xFF,0xCC,0xAD,0xFC,0x80, +0xFC,0x00,0x84,0x01,0x49,0xFF,0xC9,0x7C,0x84,0x03,0x49,0xFF,0xC9,0x9C,0x84,0x00, +0x49,0xFF,0xCA,0x22,0x84,0x00,0x49,0xFF,0xC9,0xA6,0x84,0x03,0x49,0xFF,0xC9,0xC4, +0x84,0x00,0x49,0xFF,0xC9,0xCF,0x84,0x01,0x49,0xFF,0xD8,0x2F,0xFC,0x80,0xFC,0x00, +0x84,0x00,0x44,0x10,0x00,0xF0,0x49,0xFF,0xD0,0x58,0x84,0x02,0x44,0x10,0x00,0xF0, +0x49,0xFF,0xD0,0x53,0x84,0x01,0x44,0x10,0x00,0xF0,0x49,0xFF,0xD0,0x4E,0x84,0x03, +0x44,0x10,0x00,0xF0,0x49,0xFF,0xD0,0x49,0x84,0x04,0x44,0x10,0x00,0xF0,0x49,0xFF, +0xD0,0x44,0x44,0x10,0x00,0xF0,0x84,0x05,0x49,0xFF,0xD0,0x3F,0x84,0x01,0x49,0xFF, +0xD6,0xE9,0x84,0x03,0x84,0x2A,0x49,0xFF,0xD0,0x8C,0x84,0x00,0x84,0x23,0x49,0xFF, +0xC7,0x43,0xFC,0x80,0xFC,0x01,0xF0,0x81,0x49,0xFF,0xD1,0xBA,0xE6,0x03,0xE9,0x04, +0xF0,0x01,0x49,0xFF,0xDB,0xF6,0xFC,0x81,0xFC,0x00,0x84,0x01,0x49,0xFF,0xCB,0xD7, +0x84,0x01,0x49,0xFF,0xC7,0x7F,0x84,0x01,0x49,0xFF,0xCA,0xB9,0x84,0x01,0x49,0xFF, +0xFF,0xEB,0x84,0x00,0x49,0xFF,0xEB,0xDE,0x84,0x00,0x49,0xFF,0xEB,0xC1,0x84,0x01, +0x49,0xFF,0xC9,0xE7,0x84,0x01,0x49,0xFF,0xCF,0x16,0x84,0x01,0x49,0xFF,0xCF,0x23, +0x84,0x01,0x49,0xFF,0xC9,0xDE,0x84,0x01,0x49,0xFF,0xCF,0x3B,0x84,0x00,0x80,0x20, +0x49,0xFF,0xE0,0x81,0x84,0x01,0x49,0xFF,0xCA,0xDB,0x84,0x01,0x49,0xFF,0xCA,0xEC, +0x84,0x01,0x49,0xFF,0xCB,0x02,0x49,0xFF,0xD1,0x83,0xE6,0x03,0xE8,0x05,0x84,0x01, +0x84,0x2F,0x49,0xFF,0xD0,0xE1,0x49,0xFF,0xD1,0x7B,0xE6,0x03,0xE9,0x03,0x49,0xFF, +0xD1,0x7B,0x49,0xFF,0xD1,0x75,0xE6,0x03,0xE8,0x03,0x49,0xFF,0xD1,0x7E,0x84,0x04, +0x49,0xFF,0xD1,0x8F,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xD1,0x6A,0xFC,0x80,0xFC,0x00, +0x49,0xFF,0xD1,0xA9,0x49,0xFF,0xD2,0x0F,0x49,0xFF,0xD2,0xB4,0x84,0x01,0x49,0xFF, +0xD1,0x93,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xD2,0x5D,0xFC,0x80,0xFC,0x00,0x44,0x00, +0x74,0xB2,0x49,0xFF,0xDC,0xCE,0x84,0x01,0x84,0x20,0x49,0xFF,0xD3,0xB7,0x84,0x01, +0x80,0x20,0x49,0xFF,0xD3,0xB3,0x84,0x00,0x80,0x20,0x49,0xFF,0xD3,0xC3,0x84,0x20, +0xFA,0x0E,0x49,0xFF,0xD3,0xE3,0x84,0x00,0x49,0xFF,0xD3,0xEA,0x84,0x00,0x80,0x20, +0x49,0xFF,0xD4,0x0D,0x84,0x00,0x80,0x20,0x49,0xFF,0xCD,0x40,0x84,0x00,0x49,0xFF, +0xD4,0x10,0x84,0x20,0x84,0x01,0x49,0xFF,0xD3,0xB7,0x84,0x00,0x49,0xFF,0xD3,0xEA, +0x84,0x00,0x84,0x21,0x49,0xFF,0xD3,0xA6,0x84,0x21,0x44,0x00,0x00,0xD2,0x49,0xFF, +0xD3,0xC5,0x84,0x01,0x49,0xFF,0xD3,0xCC,0x84,0x00,0x84,0x21,0x49,0xFF,0xD3,0xEF, +0x84,0x01,0x80,0x20,0x49,0xFF,0xCD,0x22,0x49,0x00,0x00,0x1E,0xFC,0x80,0xFC,0x00, +0x49,0xFF,0xD4,0x23,0xFC,0x80,0xFC,0x00,0x5A,0x08,0x01,0x0B,0xFE,0x0B,0x96,0x01, +0x49,0xFF,0xD4,0x12,0x49,0xFF,0xD4,0x20,0x49,0xFF,0xD4,0x17,0xFC,0x80,0x49,0xFF, +0xD4,0x2B,0xFC,0x80,0x3C,0x00,0x08,0x3C,0x3C,0x10,0x08,0x3C,0x96,0x01,0x4C,0x00, +0xFF,0xFB,0xDD,0x9E,0x84,0x00,0x3C,0x08,0x08,0x1A,0xDD,0x9E,0x44,0x22,0x20,0xA8, +0x38,0x11,0x01,0x09,0xDD,0x9E,0xFC,0x00,0x80,0xC0,0x49,0xFF,0xFF,0xED,0x44,0x12, +0x20,0xC8,0x38,0x00,0x99,0x09,0xFC,0x80,0x80,0x60,0x5A,0x18,0x01,0x10,0xFC,0x20, +0x80,0xC1,0x80,0xE0,0x49,0xFF,0xFF,0xF1,0x3C,0x20,0x08,0x1A,0x40,0x13,0x1C,0x0C, +0xFE,0x57,0x96,0x49,0x3C,0x18,0x08,0x1A,0xFC,0xA0,0x84,0x41,0x3C,0x00,0x08,0x1A, +0x40,0x11,0x0C,0x0C,0xFE,0x4B,0xFE,0x46,0x3C,0x18,0x08,0x1A,0xDD,0x9E,0x3C,0x10, +0x08,0x1A,0x40,0x10,0x80,0x0E,0x96,0x46,0xC9,0x03,0x80,0x01,0xDD,0x9E,0xFC,0x00, +0x80,0xC0,0x49,0xFF,0xFF,0xC1,0x44,0x12,0x20,0xC8,0x44,0x22,0x20,0xA8,0x38,0x10, +0x99,0x01,0x38,0x21,0x19,0x01,0x8A,0x01,0x96,0x01,0xE2,0x02,0x56,0x07,0x80,0x01, +0xFC,0x80,0x3C,0x00,0x08,0x3C,0x8C,0x01,0x96,0x01,0x3C,0x08,0x08,0x3C,0xDD,0x9E, +0x3A,0x1F,0xA4,0x3C,0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72, +0x04,0x02,0x3A,0x6F,0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03, +0x64,0x00,0x00,0x08,0x49,0x00,0x02,0x02,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03, +0x64,0x72,0x04,0x03,0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04, +0x3A,0x1F,0xA4,0x3C,0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72, +0x04,0x02,0x3A,0x6F,0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03, +0x64,0x00,0x00,0x08,0x49,0x00,0x01,0xE3,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03, +0x64,0x72,0x04,0x03,0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04, +0x3A,0x1F,0xA4,0x3C,0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72, +0x04,0x02,0x3A,0x6F,0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03, +0x64,0x00,0x00,0x08,0x49,0x00,0x01,0xC4,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03, +0x64,0x72,0x04,0x03,0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04, +0x3A,0x1F,0xA4,0x3C,0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72, +0x04,0x02,0x3A,0x6F,0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03, +0x64,0x00,0x00,0x08,0x49,0x00,0x01,0xA5,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03, +0x64,0x72,0x04,0x03,0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04, +0x3A,0x1F,0xA4,0x3C,0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72, +0x04,0x02,0x3A,0x6F,0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03, +0x64,0x00,0x00,0x08,0x49,0x00,0x01,0x86,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03, +0x64,0x72,0x04,0x03,0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04, +0x3A,0x1F,0xA4,0x3C,0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72, +0x04,0x02,0x3A,0x6F,0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03, +0x64,0x00,0x00,0x08,0x49,0x00,0x01,0x67,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03, +0x64,0x72,0x04,0x03,0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04, +0x3A,0x1F,0xA4,0x3C,0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72, +0x04,0x02,0x3A,0x6F,0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03, +0x64,0x00,0x00,0x08,0x49,0x00,0x01,0x48,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03, +0x64,0x72,0x04,0x03,0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04, +0x3A,0x1F,0xA4,0x3C,0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72, +0x04,0x02,0x3A,0x6F,0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03, +0x64,0x00,0x00,0x08,0x49,0x00,0x01,0x29,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03, +0x64,0x72,0x04,0x03,0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04, +0x3A,0x1F,0x94,0x3C,0x3A,0xFF,0xBF,0xBC,0x64,0x12,0xA4,0x02,0x64,0x22,0x04,0x02, +0x3A,0x1F,0x88,0x3C,0x64,0x12,0x00,0x02,0x9E,0x4A,0x64,0x12,0x00,0x03,0x44,0x10, +0x00,0xA4,0x38,0x10,0x82,0x02,0xDD,0x21,0x3A,0x0F,0x84,0x04,0x64,0x02,0x00,0x43, +0x64,0x00,0x00,0x08,0x64,0x02,0xA4,0x03,0x64,0x12,0x04,0x03,0x3A,0xFF,0xBF,0x84, +0x3A,0x1F,0x94,0x04,0x3A,0x0F,0x80,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0x94,0x3C, +0x3A,0xFF,0xBF,0xBC,0x44,0x10,0x00,0xA4,0x38,0x10,0x82,0x02,0xDD,0x21,0x3A,0xFF, +0xBF,0x84,0x3A,0x1F,0x94,0x04,0x3A,0x0F,0x80,0x04,0x64,0x00,0x00,0x04,0xD5,0x00, +0x3A,0x0F,0x80,0x3C,0x84,0x00,0xD5,0xEB,0x3A,0x0F,0x80,0x3C,0x84,0x01,0xD5,0xE7, +0x3A,0x0F,0x80,0x3C,0x84,0x02,0xD5,0xE3,0x3A,0x0F,0x80,0x3C,0x84,0x03,0xD5,0xDF, +0x3A,0x0F,0x80,0x3C,0x84,0x04,0xD5,0xDB,0x3A,0x0F,0x80,0x3C,0x84,0x05,0xD5,0xD7, +0x3A,0x0F,0x80,0x3C,0x84,0x06,0xD5,0xD3,0x3A,0x0F,0x80,0x3C,0x84,0x07,0xD5,0xCF, +0x3A,0x0F,0x80,0x3C,0x84,0x08,0xD5,0xCB,0x3A,0x0F,0x80,0x3C,0x84,0x09,0xD5,0xC7, +0x3A,0x0F,0x80,0x3C,0x84,0x0A,0xD5,0xC3,0x3A,0x0F,0x80,0x3C,0x84,0x0B,0xD5,0xBF, +0x3A,0x0F,0x80,0x3C,0x84,0x0C,0xD5,0xBB,0x3A,0x0F,0x80,0x3C,0x84,0x0D,0xD5,0xB7, +0x3A,0x0F,0x80,0x3C,0x84,0x0E,0xD5,0xB3,0x3A,0x0F,0x80,0x3C,0x84,0x0F,0xD5,0xAF, +0x3A,0x0F,0x80,0x3C,0xFA,0x00,0xD5,0xAB,0x3A,0x0F,0x80,0x3C,0xFA,0x01,0xD5,0xA7, +0x3A,0x0F,0x80,0x3C,0xFA,0x02,0xD5,0xA3,0x3A,0x0F,0x80,0x3C,0xFA,0x03,0xD5,0x9F, +0x3A,0x0F,0x80,0x3C,0xFA,0x04,0xD5,0x9B,0x3A,0x0F,0x80,0x3C,0xFA,0x05,0xD5,0x97, +0x3A,0x0F,0x80,0x3C,0xFA,0x06,0xD5,0x93,0x3A,0x0F,0x80,0x3C,0xFA,0x07,0xD5,0x8F, +0x3A,0x0F,0x80,0x3C,0xFA,0x08,0xD5,0x8B,0x3A,0x0F,0x80,0x3C,0xFA,0x09,0xD5,0x87, +0x3A,0x0F,0x80,0x3C,0xFA,0x0A,0xD5,0x83,0x3A,0x0F,0x80,0x3C,0xFA,0x0B,0x48,0xFF, +0xFF,0x7F,0x3A,0x0F,0x80,0x3C,0xFA,0x0C,0x48,0xFF,0xFF,0x7A,0x3A,0x0F,0x80,0x3C, +0xFA,0x0D,0x48,0xFF,0xFF,0x75,0x3A,0x0F,0x80,0x3C,0xFA,0x0E,0x48,0xFF,0xFF,0x70, +0x3A,0x0F,0x80,0x3C,0x44,0x00,0x00,0x1F,0x48,0xFF,0xFF,0x6A,0x84,0x01,0x64,0x04, +0xC0,0x02,0x44,0x02,0x00,0x01,0x64,0x04,0xE0,0x03,0x45,0xD2,0x10,0x70,0x3F,0xC8, +0x00,0x00,0x45,0xF2,0x5F,0xF8,0x84,0x00,0x42,0x0E,0x00,0x21,0x49,0x00,0x00,0x31, +0x49,0x00,0x02,0xFB,0xD5,0x00,0x92,0x00,0xFC,0x00,0x44,0x02,0x00,0x00,0x44,0x22, +0x00,0xD4,0x44,0x10,0x87,0xA0,0x8A,0x40,0x49,0x00,0x02,0xFA,0x44,0x02,0x00,0xD4, +0x44,0x22,0x20,0xEC,0x84,0x20,0x8A,0x40,0x49,0x00,0x03,0x18,0xFC,0x80,0x84,0x00, +0x64,0x05,0xE4,0x03,0x84,0x1F,0x64,0x03,0x04,0x03,0x64,0x03,0x24,0x03,0x64,0x12, +0x24,0x02,0x44,0x0F,0x0F,0xFF,0xFE,0x0E,0x64,0x02,0x24,0x03,0x64,0x12,0x00,0x02, +0x84,0x18,0xFE,0x0E,0x64,0x02,0x00,0x03,0x64,0x00,0x00,0x08,0xDD,0x9E,0xFC,0x00, +0x49,0xFF,0xFF,0xE7,0x64,0x00,0x80,0x02,0x92,0x1E,0xC0,0x06,0x64,0x00,0x84,0x02, +0x96,0x2E,0xC0,0x02,0xD5,0x00,0x49,0xFF,0xFF,0xC9,0x64,0x12,0x00,0x43,0x64,0x00, +0x00,0x08,0x64,0x00,0x00,0x08,0xFC,0x80,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E, +0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0x44,0x04,0x80,0x04,0xA6,0x01, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x05,0x84,0x21,0xAE,0x40,0xDD,0x9E,0x44,0x04, +0x80,0x01,0xA6,0x00,0xC8,0x05,0xFC,0x00,0x49,0xFF,0xD6,0x02,0xFC,0x80,0xDD,0x9E, +0xFC,0x00,0x44,0x04,0x80,0x00,0xA6,0x03,0x49,0xFF,0xD9,0xF6,0xFC,0x80,0x44,0x14, +0x80,0x04,0x44,0x04,0x80,0x84,0xA6,0x88,0xB4,0x00,0x96,0x06,0xFE,0x16,0xC0,0x03, +0x84,0x01,0xAE,0x08,0xDD,0x9E,0xFC,0x00,0x44,0x04,0x80,0x00,0xA6,0x03,0x49,0xFF, +0xD1,0x49,0xFC,0x80,0x44,0x14,0x80,0x14,0xA6,0x08,0x96,0x06,0xC0,0x09,0x84,0x01, +0xAE,0x08,0x44,0x14,0x80,0x94,0xB4,0x01,0x58,0x00,0x00,0x01,0xB6,0x01,0x44,0x14, +0x80,0x14,0xA6,0x08,0x92,0x01,0x96,0x06,0xC0,0x09,0x84,0x02,0xAE,0x08,0x44,0x14, +0x80,0x94,0xB4,0x01,0x58,0x00,0x00,0x02,0xB6,0x01,0xDD,0x9E,0xFC,0x00,0x49,0xFF, +0xD4,0xC5,0xFC,0x80,0x44,0x04,0x80,0x04,0xA6,0x02,0x92,0x02,0x96,0x06,0xC0,0x05, +0x44,0x04,0x80,0x06,0x84,0x24,0xAE,0x40,0xDD,0x9E,0x44,0x14,0x80,0x04,0xA6,0x0B, +0x92,0x01,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x07,0x84,0x42,0xAE,0x80,0xA6,0x0B, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x07,0x84,0x21,0xAE,0x40,0xDD,0x9E,0x44,0x04, +0x80,0x08,0xB4,0x20,0x92,0x24,0x96,0x46,0xC1,0x03,0xFA,0x20,0xAE,0x40,0xB4,0x00, +0x84,0x21,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x08,0xAE,0x40,0x3E,0x17,0xF1,0x21, +0x44,0x04,0x80,0x08,0xB4,0x20,0x92,0x21,0x96,0x46,0xC1,0x06,0x84,0x22,0xAE,0x40, +0x84,0x21,0x3E,0x17,0xF1,0x20,0xB4,0x00,0x92,0x02,0x96,0x06,0xC0,0x05,0x44,0x04, +0x80,0x08,0x84,0x24,0xAE,0x40,0xDD,0x9E,0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xD3,0x86, +0xFC,0x80,0xFC,0x00,0x49,0xFF,0xD3,0xE7,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xC5,0x3D, +0xFC,0x80,0x44,0x14,0x80,0x08,0xB4,0x01,0x92,0x08,0x96,0x06,0xC0,0x05,0x44,0x04, +0x80,0x09,0x84,0x41,0xAE,0x80,0xB4,0x01,0x92,0x09,0x96,0x06,0xC0,0x0F,0x44,0x04, +0x80,0x09,0x84,0x22,0xAE,0x40,0x84,0x01,0x3E,0x07,0xF1,0x24,0x44,0x04,0x50,0x28, +0xB4,0x00,0x92,0x06,0x96,0x1F,0x3E,0x07,0xF1,0x23,0xDD,0x9E,0x44,0x04,0x80,0x08, +0xB4,0x00,0x92,0x12,0x96,0x06,0xC0,0x15,0x44,0x14,0x80,0x88,0xB4,0x01,0x92,0x12, +0x96,0x06,0xC0,0x0F,0xB4,0x41,0x44,0x0B,0xFF,0xFF,0xFE,0x16,0xB6,0x01,0x44,0x04, +0x80,0x0A,0x84,0x22,0xAE,0x40,0x84,0x21,0x3E,0x17,0xF1,0x27,0x84,0x24,0xAE,0x40, +0x44,0x04,0x80,0x08,0x84,0x21,0xB4,0x00,0x92,0x10,0x96,0x06,0xC0,0x0C,0x44,0x04, +0x80,0x88,0xB4,0x00,0x92,0x10,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0A,0x3E,0x17, +0xF1,0x26,0xAE,0x40,0x44,0x04,0x80,0x08,0xB4,0x00,0x92,0x11,0x96,0x06,0xC0,0x0E, +0x44,0x04,0x80,0x88,0xB4,0x00,0x92,0x11,0x96,0x06,0xC0,0x08,0x84,0x01,0x3E,0x07, +0xF1,0x25,0x44,0x04,0x80,0x0A,0x84,0x22,0xAE,0x40,0xDD,0x9E,0x44,0x14,0x80,0x08, +0x84,0x41,0xB4,0x01,0x92,0x18,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0B,0xAE,0x80, +0x3E,0x27,0xF0,0xEC,0xB4,0x01,0x92,0x19,0x96,0x06,0xC0,0x1D,0x44,0x04,0x80,0x0B, +0x84,0x22,0xAE,0x40,0x84,0x00,0x3E,0x07,0xF0,0xEC,0x46,0x00,0x00,0x81,0x44,0x1F, +0xFE,0xFF,0x50,0x30,0x00,0x70,0xB4,0x43,0xFE,0x56,0xB6,0x23,0xB4,0x43,0x44,0x1E, +0xFF,0xFF,0xFE,0x56,0xB6,0x23,0xB4,0x43,0x46,0x1F,0xEF,0xFF,0x50,0x10,0x8F,0xFF, +0xFE,0x56,0xB6,0x23,0x44,0x04,0x80,0x08,0xB4,0x00,0x92,0x1A,0x96,0x06,0xC0,0x0E, +0x44,0x14,0x70,0x08,0xB4,0x01,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00,0x78,0x23, +0xB6,0x01,0x44,0x04,0x80,0x0B,0x84,0x24,0xAE,0x40,0x44,0x04,0x80,0x08,0xB4,0x00, +0x92,0x1B,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0B,0x84,0x28,0xAE,0x40,0xDD,0x9E, +0x44,0x04,0x80,0x04,0xA6,0x02,0x92,0x03,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x06, +0x84,0x28,0xAE,0x40,0xDD,0x9E,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x10,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x0E,0x84,0x41,0xAE,0x80,0xB4,0x01,0x92,0x11,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x0E,0x84,0x22,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01, +0x92,0x12,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0E,0x84,0x44,0xAE,0x80,0xB4,0x01, +0x92,0x13,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0E,0x84,0x28,0xAE,0x40,0x44,0x14, +0x80,0x0C,0xB4,0x01,0x92,0x14,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0E,0xFA,0x40, +0xAE,0x80,0xB4,0x01,0x92,0x15,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0E,0xFA,0x30, +0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x16,0x96,0x06,0xC0,0x06,0x44,0x04, +0x80,0x0E,0x44,0x20,0x00,0x40,0xAE,0x80,0xB4,0x01,0x92,0x17,0x96,0x06,0xC0,0x06, +0x44,0x04,0x80,0x0E,0x44,0x1F,0xFF,0x80,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01, +0x92,0x18,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0F,0x84,0x41,0xAE,0x80,0xB4,0x01, +0x92,0x19,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0F,0x84,0x22,0xAE,0x40,0x44,0x14, +0x80,0x0C,0xB4,0x01,0x92,0x1A,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0F,0x84,0x44, +0xAE,0x80,0xB4,0x01,0x92,0x1B,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0F,0x84,0x28, +0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x1C,0x96,0x06,0xC0,0x05,0x44,0x04, +0x80,0x0F,0xFA,0x40,0xAE,0x80,0xB4,0x01,0x92,0x1D,0x96,0x06,0xC0,0x05,0x44,0x04, +0x80,0x0F,0xFA,0x30,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x1E,0x96,0x06, +0xC0,0x06,0x44,0x04,0x80,0x0F,0x44,0x20,0x00,0x40,0xAE,0x80,0xB4,0x01,0x4E,0x04, +0x00,0x07,0x44,0x04,0x80,0x0F,0x44,0x1F,0xFF,0x80,0xAE,0x40,0xDD,0x9E,0x44,0x04, +0x80,0x0C,0xB4,0x20,0x96,0x46,0xC1,0x03,0x84,0x21,0xAE,0x40,0xB4,0x00,0x92,0x01, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0C,0x84,0x22,0xAE,0x40,0x44,0x04,0x80,0x0C, +0xB4,0x20,0x92,0x22,0x96,0x46,0xC1,0x03,0x84,0x24,0xAE,0x40,0xB4,0x00,0x92,0x03, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0C,0x84,0x28,0xAE,0x40,0x44,0x04,0x80,0x0C, +0xB4,0x20,0x92,0x24,0x96,0x46,0xC1,0x03,0xFA,0x20,0xAE,0x40,0xB4,0x00,0x92,0x05, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0C,0xFA,0x30,0xAE,0x40,0x44,0x04,0x80,0x0C, +0xB4,0x20,0x92,0x26,0x96,0x46,0xC1,0x04,0x44,0x10,0x00,0x40,0xAE,0x40,0xB4,0x00, +0x92,0x07,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0C,0x44,0x1F,0xFF,0x80,0xAE,0x40, +0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x08,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0D, +0x84,0x41,0xAE,0x80,0xB4,0x01,0x92,0x09,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0D, +0x84,0x22,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x0A,0x96,0x06,0xC0,0x05, +0x44,0x04,0x80,0x0D,0x84,0x44,0xAE,0x80,0xB4,0x01,0x92,0x0B,0x96,0x06,0xC0,0x05, +0x44,0x04,0x80,0x0F,0x84,0x28,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x0C, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0D,0xFA,0x40,0xAE,0x80,0xB4,0x01,0x92,0x0D, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0D,0xFA,0x30,0xAE,0x40,0x44,0x14,0x80,0x0C, +0xB4,0x01,0x92,0x0E,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0D,0x44,0x20,0x00,0x40, +0xAE,0x80,0xB4,0x01,0x92,0x0F,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0D,0x44,0x1F, +0xFF,0x80,0xAE,0x40,0xDD,0x9E,0x44,0x04,0x80,0x00,0x44,0x14,0x80,0x80,0xA6,0x80, +0xB4,0x21,0x96,0x46,0xFE,0x56,0xC1,0x03,0x84,0x21,0xAE,0x40,0xA6,0x00,0x40,0x10, +0x04,0x09,0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x01,0x96,0x06,0xFE,0x0E,0xC0,0x05, +0x44,0x04,0x80,0x00,0x84,0x22,0xAE,0x40,0x44,0x14,0x80,0x00,0xA6,0x08,0x40,0x20, +0x08,0x09,0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x02,0x96,0x06,0xFE,0x16,0xC0,0x03, +0x84,0x04,0xAE,0x08,0xA6,0x08,0x40,0x10,0x0C,0x09,0x44,0x04,0x80,0x80,0xB4,0x00, +0x92,0x03,0x96,0x06,0xFE,0x0E,0xC0,0x05,0x44,0x04,0x80,0x00,0x84,0x28,0xAE,0x40, +0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E, +0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xCD,0xF7,0x49,0xFF,0xD7,0x0A, +0x49,0xFF,0xCF,0xEF,0x49,0xFF,0xD0,0xC6,0x84,0x00,0xFC,0x80,0x80,0x60,0x54,0xF1, +0x00,0x03,0xE9,0x1B,0xE6,0x44,0xE9,0x19,0x54,0x41,0x80,0x03,0x54,0x50,0x80,0x03, +0xDC,0x14,0xC5,0x0A,0x52,0x52,0x80,0x04,0x9A,0x95,0x99,0x69,0x08,0x40,0x80,0x01, +0x18,0x41,0x80,0x01,0xD9,0xFC,0x40,0x51,0x08,0x09,0xC5,0x07,0x95,0x6A,0x99,0x69, +0x96,0x8F,0xA3,0x09,0xAB,0x19,0xD9,0xFE,0xC2,0x07,0x99,0x4A,0x08,0x40,0x80,0x01, +0x18,0x41,0x80,0x01,0xD9,0xFC,0xDD,0x9E,0x80,0x60,0xE6,0x44,0xE9,0x1A,0x54,0x51, +0x80,0x03,0xC5,0x08,0x52,0x52,0x80,0x04,0x9A,0x95,0x99,0x6B,0x18,0x11,0x80,0x01, +0xDB,0xFE,0x40,0x51,0x08,0x09,0x96,0x8F,0xC5,0x0C,0x95,0x6A,0x99,0x5D,0x96,0x48, +0x40,0x40,0xA0,0x08,0xFE,0x67,0x40,0x40,0xC0,0x08,0xFE,0x67,0xAA,0x59,0xDB,0xFF, +0x99,0x5A,0xD3,0x04,0x18,0x11,0x80,0x01,0xD5,0xFD,0xDD,0x9E,0x00,0x00,0x00,0x00, +0x69,0x1A,0xC1,0x37,0x01,0x00,0x12,0x20,0xFF,0x07,0xFF,0x07,0x05,0x0A,0x0B,0x00, +0xC8,0x00,0x02,0x00,0xC0,0x00,0x02,0x00,0xB8,0x00,0x02,0x00,0xAC,0x00,0x02,0x00, +0xA0,0x00,0x02,0x00,0x90,0x00,0x02,0x00,0x80,0x00,0x02,0x00,0x6C,0x00,0x02,0x00, +0x58,0x00,0x02,0x00,0x40,0x00,0x02,0x00,0x0A,0x0A,0x0B,0x0B,0x0B,0x0C,0x0C,0x0C, +0x0C,0x0C,0x00,0x00,0x01,0x05,0x00,0x07,0x02,0x06,0x04,0x08,0x03,0x09,0x00,0x00, +0x01,0x02,0x03,0x04,0x05,0x06,0x06,0x06,0x06,0x06,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x01,0x01, +0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E, +0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D, +0x1D,0x1D,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x1C,0x1C,0x1C,0x1C,0x1C, +0x1C,0x1C,0x1C,0x1C,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x1B,0x1B,0x1B, +0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x1A, +0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, +0x06,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x07,0x07,0x07,0x07,0x07,0x07, +0x07,0x07,0x07,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x08,0x08,0x08,0x08, +0x08,0x08,0x08,0x08,0x08,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x09,0x09, +0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, +0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x15,0x15,0x15,0x15,0x15,0x15,0x15, +0x15,0x15,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x14,0x14,0x14,0x14,0x14, +0x14,0x14,0x14,0x14,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x13,0x13,0x13, +0x13,0x13,0x13,0x13,0x13,0x13,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x12, +0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E, +0x0E,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, +0x0F,0x0F,0x0F,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, +0x10,0x10,0x10,0x10,0x10,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x11,0x11, +0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E, +0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D, +0x0D,0x0D,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x0C,0x0C,0x0C,0x0C,0x0C, +0x0C,0x0C,0x0C,0x0C,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x0B,0x0B,0x0B, +0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x0A, +0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, +0x16,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x17,0x17,0x17,0x17,0x17,0x17, +0x17,0x17,0x17,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x18,0x18,0x18,0x18, +0x18,0x18,0x18,0x18,0x18,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x19,0x19, +0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, +0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x05,0x05,0x05,0x05,0x05,0x05,0x05, +0x05,0x05,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x04,0x04,0x04,0x04,0x04, +0x04,0x04,0x04,0x04,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x03,0x03,0x03, +0x03,0x03,0x03,0x03,0x03,0x03,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x02, +0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E, +0x1E,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F, +0x1F,0x1F,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04, +0x05,0x06,0x06,0x06,0x06,0x06,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, +0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x30,0x01,0x30,0x01, +0x30,0x01,0x30,0x01,0x88,0x01,0x32,0x01,0x56,0x01,0x68,0x01,0x42,0x01,0x7A,0x01, +0x8C,0x01,0x30,0x01,0x44,0x01,0x32,0x01,0x56,0x01,0x86,0x01,0x30,0x01,0x54,0x01, +0x68,0x01,0x42,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x02,0x03,0x02,0x01,0x02, +0x01,0x01,0x01,0x04,0x02,0x01,0x04,0x04,0x30,0x01,0x30,0x01,0x30,0x01,0x30,0x01, +0x30,0x01,0x30,0x01,0x30,0x01,0x93,0x01,0x5C,0x01,0xDE,0x01,0x2D,0x02,0xB1,0x01, +0x05,0x02,0xED,0x01,0x41,0x02,0x30,0x01,0xBF,0x01,0x11,0x02,0x37,0x01,0x3C,0x01, +0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0B,0x0A,0x09,0x08, +0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, +0x08,0x00,0x01,0x02,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x02,0x01,0x00, +0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x00,0x01,0x00,0x01,0x02,0x03, +0x04,0x05,0x06,0x07,0x08,0x00,0x01,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00, +0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00, +0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00, +0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x01,0x00,0x02,0x00,0x02,0x00,0x01,0x00, +0x04,0x80,0x00,0x00,0x08,0x40,0x00,0x00,0x10,0x20,0x00,0x00,0x20,0x10,0x00,0x00, +0x40,0x08,0x00,0x00,0x80,0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x38,0x70,0x00,0x00, +0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x52,0x02,0x00,0x00,0x52,0x02,0x00,0x00, +0x92,0x02,0x00,0x00,0xD2,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0x00,0x00,0x00,0x90,0x00,0x00,0x00, +0xE0,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0x00,0x00,0x40,0x02,0x40,0x02,0x00,0x00, +0xC0,0x00,0xC0,0x00,0x40,0x00,0x40,0x00,0x00,0x09,0x09,0x00,0x03,0x03,0x01,0x01, +0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x04,0x04,0x04,0x00,0x01,0x02,0x03, +0x04,0x05,0x06,0x07,0x08,0x00,0x01,0x02,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, +0x08,0x02,0x01,0x00,0x00,0x01,0x02,0x03,0x04,0x00,0x01,0x02,0x03,0x04,0x00,0x00, +0x07,0x80,0x03,0x00,0x38,0x70,0x00,0x00,0xC0,0x0F,0x00,0x00,0x08,0x40,0x00,0x00, +0x10,0x20,0x00,0x00,0x20,0x10,0x00,0x00,0x40,0x08,0x00,0x00,0x80,0x04,0x00,0x00, +0x00,0x03,0x00,0x00,0x07,0x80,0x03,0x00,0x38,0x70,0x00,0x00,0xC0,0x0F,0x00,0x00, +0x01,0x00,0x02,0x00,0x02,0x00,0x01,0x00,0x04,0x80,0x00,0x00,0x08,0x40,0x00,0x00, +0x10,0x20,0x00,0x00,0x20,0x10,0x00,0x00,0x40,0x08,0x00,0x00,0x80,0x04,0x00,0x00, +0x00,0x03,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00, +0x07,0x80,0x03,0x00,0x38,0x70,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x01,0x01,0x01,0x01, +0x01,0x01,0x01,0x01,0x01,0x03,0x03,0x03,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, +0x01,0x00,0x03,0x03,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00, +0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x04,0x04,0x04,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00, +0x00,0x00,0x00,0x00,0x01,0x00,0x02,0x00,0x02,0x00,0x01,0x00,0x04,0x80,0x00,0x00, +0x08,0x40,0x00,0x00,0x10,0x20,0x00,0x00,0x20,0x10,0x00,0x00,0x40,0x08,0x00,0x00, +0x80,0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x07,0x80,0x03,0x00,0x38,0x70,0x00,0x00, +0xC0,0x0F,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x01,0x02, +0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x01,0x02,0x0B,0x02,0x0B,0x0B, +0x0B,0x02,0x00,0x00,0x0B,0x02,0x0B,0x0B,0x0B,0x02,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x01,0x01, +0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E, +0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D, +0x1D,0x1D,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x1C,0x1C,0x1C,0x1C,0x1C, +0x1C,0x1C,0x1C,0x1C,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x1B,0x1B,0x1B, +0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x05,0x1A, +0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, +0x06,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x07,0x07,0x07,0x07,0x07,0x07, +0x07,0x07,0x07,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x08,0x08,0x08,0x08, +0x08,0x08,0x08,0x08,0x08,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x17,0x09,0x09, +0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, +0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x15,0x15,0x15,0x15,0x15,0x15,0x15, +0x15,0x15,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x14,0x14,0x14,0x14,0x14, +0x14,0x14,0x14,0x14,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x13,0x13,0x13, +0x13,0x13,0x13,0x13,0x13,0x13,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x12, +0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E, +0x0E,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F, +0x0F,0x0F,0x0F,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, +0x10,0x10,0x10,0x10,0x10,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x0F,0x11,0x11, +0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E,0x0E, +0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D,0x0D, +0x0D,0x0D,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x13,0x0C,0x0C,0x0C,0x0C,0x0C, +0x0C,0x0C,0x0C,0x0C,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x14,0x0B,0x0B,0x0B, +0x0B,0x0B,0x0B,0x0B,0x0B,0x0B,0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x15,0x0A, +0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x16,0x16,0x16,0x16,0x16,0x16,0x16,0x16, +0x16,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x09,0x17,0x17,0x17,0x17,0x17,0x17, +0x17,0x17,0x17,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x18,0x18,0x18,0x18, +0x18,0x18,0x18,0x18,0x18,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x19,0x19, +0x19,0x19,0x19,0x19,0x19,0x19,0x19,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06, +0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x1A,0x05,0x05,0x05,0x05,0x05,0x05,0x05, +0x05,0x05,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x1B,0x04,0x04,0x04,0x04,0x04, +0x04,0x04,0x04,0x04,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x03,0x03,0x03, +0x03,0x03,0x03,0x03,0x03,0x03,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x1D,0x02, +0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E,0x1E, +0x1E,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F, +0x1F,0x1F,0x1F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF, +0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x00,0x00, +0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x02,0x03,0x04, +0x05,0x06,0x06,0x06,0x06,0x06,0x00,0x00,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02, +0x02,0x00,0x00,0x00,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00,0x00,0x01,0x02, +0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x02,0x01,0x00,0x00,0x01,0x02,0x00, +0x80,0x00,0x01,0x00,0x40,0x80,0x00,0x00,0x20,0x40,0x00,0x00,0x10,0x20,0x00,0x00, +0x08,0x10,0x00,0x00,0x04,0x08,0x00,0x00,0x02,0x04,0x00,0x00,0x01,0x02,0x00,0x00, +0x07,0x80,0x03,0x00,0x38,0x70,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x01,0x02,0x03, +0x04,0x05,0x06,0x07,0x08,0x00,0x01,0x02,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, +0x08,0x02,0x01,0x00,0x01,0x00,0x02,0x00,0x02,0x00,0x01,0x00,0x04,0x80,0x00,0x00, +0x08,0x40,0x00,0x00,0x10,0x20,0x00,0x00,0x20,0x10,0x00,0x00,0x40,0x08,0x00,0x00, +0x80,0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x07,0x80,0x03,0x00,0x38,0x70,0x00,0x00, +0xC0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x01,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x0A,0x0A,0x0B,0x0B, +0x0B,0x0C,0x0C,0x0C,0x0C,0x0C,0x00,0x00,0xC8,0x00,0x02,0x00,0xC0,0x00,0x02,0x00, +0xB8,0x00,0x02,0x00,0xAC,0x00,0x02,0x00,0xA0,0x00,0x02,0x00,0x90,0x00,0x02,0x00, +0x80,0x00,0x02,0x00,0x6C,0x00,0x02,0x00,0x58,0x00,0x02,0x00,0x40,0x00,0x02,0x00, +0x61,0x00,0x4D,0x00,0x68,0x00,0x84,0x00,0xA2,0x00,0xBF,0x00,0xDA,0x00,0xF2,0x00, +0x06,0x01,0x13,0x01,0x1A,0x01,0x00,0x00,0x65,0x00,0x58,0x00,0x79,0x00,0x9D,0x00, +0xC1,0x00,0xE3,0x00,0x02,0x01,0x1C,0x01,0x2E,0x01,0x37,0x01,0x6A,0x00,0x67,0x00, +0x91,0x00,0xBE,0x00,0xEA,0x00,0x13,0x01,0x35,0x01,0x4E,0x01,0x5B,0x01,0x00,0x00, +0x72,0x00,0x7C,0x00,0xB2,0x00,0xEB,0x00,0x23,0x01,0x52,0x01,0x75,0x01,0x87,0x01, +0x7C,0x00,0x99,0x00,0xE2,0x00,0x2E,0x01,0x72,0x01,0xA5,0x01,0xC1,0x01,0x00,0x00, +0x45,0x00,0x63,0x00,0x96,0x00,0xC8,0x00,0xF0,0x00,0x07,0x01,0x51,0x00,0x88,0x00, +0xD3,0x00,0x15,0x01,0x3C,0x01,0x00,0x00,0x67,0x00,0xCC,0x00,0x40,0x01,0x8A,0x01, +0x4C,0x00,0xB0,0x00,0x02,0x01,0x00,0x00,0x99,0x00,0x66,0x01,0x38,0x70,0x00,0x00, +0x52,0x01,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x24,0x01,0x00,0x00,0x66,0x1D,0x00,0x00, +0x00,0x69,0x06,0x00,0x02,0x00,0x06,0x0C,0x02,0x00,0x06,0x0C,0x01,0x00,0x04,0x00, +0x01,0x00,0x03,0x00,0x01,0x00,0x05,0x00,0x05,0x04,0x00,0x00,0xB9,0x07,0x59,0x8F, diff --git a/drivers/input/touchscreen/ilitek_mmi/core/config.c b/drivers/input/touchscreen/ilitek_mmi/core/config.c new file mode 100644 index 000000000000..fae70a782b9b --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/config.c @@ -0,0 +1,1155 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "../common.h" +#include "../platform.h" +#include "config.h" +#include "protocol.h" +#include "i2c.h" +#include "flash.h" +#include "finger_report.h" +#include "gesture.h" +#include "mp_test.h" + +/* the list of support chip */ +uint32_t ipio_chip_list[] = { + CHIP_TYPE_ILI7807, + CHIP_TYPE_ILI9881, +}; + +uint8_t g_read_buf[128] = { 0 }; + +struct core_config_data *core_config; + +static void read_flash_info(uint8_t cmd, int len) +{ + int i; + uint16_t flash_id = 0, flash_mid = 0; + uint8_t buf[4] = { 0 }; + + /* + * This command is used to fix the bug of spi clk for 7807F-AB + * when operating with its flash. + */ + if (core_config->chip_id == CHIP_TYPE_ILI7807 && + core_config->chip_type == ILI7807_TYPE_F_AB) { + core_config_ice_mode_write(0x4100C, 0x01, 1); + mdelay(25); + } + + core_config_ice_mode_write(0x41000, 0x0, 1); /* CS high */ + core_config_ice_mode_write(0x41004, 0x66aa55, 3); /* Key */ + core_config_ice_mode_write(0x41008, cmd, 1); + + for (i = 0 ; i < len ; i++) { + core_config_ice_mode_write(0x041008, 0xFF, 1); + buf[i] = core_config_ice_mode_read(0x41010); + } + + core_config_ice_mode_write(0x041000, 0x1, 1); /* CS high */ + + /* look up flash info and init its struct after obtained flash id. */ + flash_mid = buf[0]; + flash_id = buf[1] << 8 | buf[2]; + core_flash_init(flash_mid, flash_id); +} + +/* + * It checks chip id shifting sepcific bits based on chip's requirement. + * + * @pid_data: 4 bytes, reading from firmware. + * + */ +static uint32_t check_chip_id(uint32_t pid_data) +{ + int i; + uint32_t id = 0; + uint32_t type = 0; + + id = pid_data >> 16; + type = (pid_data & 0x0000FF00) >> 8; + + ipio_info("id = 0x%x, type = 0x%x\n", id, type); + + if (id == CHIP_TYPE_ILI9881) { + for (i = ILI9881_TYPE_F; i <= ILI9881_TYPE_H; i++) { + if (i == type) { + core_config->chip_type = i; + core_config->ic_reset_addr = 0x040050; + return id; + } + } + } + + if (id == CHIP_TYPE_ILI7807) { + for (i = ILI7807_TYPE_F_AA; i <= ILI7807_TYPE_H; i++) { + if (i == type) { + core_config->chip_type = i; + if (i == ILI7807_TYPE_F_AB) + core_config->ic_reset_addr = 0x04004C; + else if (i == ILI7807_TYPE_H) + core_config->ic_reset_addr = 0x040050; + return id; + } + } + } + + return 0; +} + +uint32_t core_config_read_pc_counter(void) +{ + uint32_t pc_cnt = 0x0; + + if (!core_config->icemodeenable) { + if (core_config_ice_mode_enable() < 0) + ipio_err("Failed to enter ice mode\n"); + } + + /* Read fw status if it was hanging on a unknown status */ + pc_cnt = core_config_ice_mode_read(ILI9881_PC_COUNTER_ADDR); + ipio_info("pc counter = 0x%x\n", pc_cnt); + + if (core_config->icemodeenable) { + if (core_config_ice_mode_disable() < 0) + ipio_err("Failed to disable ice mode\n"); + } + return pc_cnt; +} +EXPORT_SYMBOL(core_config_read_pc_counter); + +/* + * Read & Write one byte in ICE Mode. + */ +uint32_t core_config_read_write_onebyte(uint32_t addr) +{ + int res = 0; + uint32_t data = 0; + uint8_t szOutBuf[64] = { 0 }; + + szOutBuf[0] = 0x25; + szOutBuf[1] = (char)((addr & 0x000000FF) >> 0); + szOutBuf[2] = (char)((addr & 0x0000FF00) >> 8); + szOutBuf[3] = (char)((addr & 0x00FF0000) >> 16); + + res = core_write(core_config->slave_i2c_addr, szOutBuf, 4); + if (res < 0) + goto out; + + mdelay(1); + + res = core_read(core_config->slave_i2c_addr, szOutBuf, 1); + if (res < 0) + goto out; + + data = (szOutBuf[0]); + + return data; + +out: + ipio_err("Failed to read/write data in ICE mode, res = %d\n", res); + return res; +} +EXPORT_SYMBOL(core_config_read_write_onebyte); + +uint32_t core_config_ice_mode_read(uint32_t addr) +{ + int res = 0; + uint8_t szOutBuf[64] = { 0 }; + uint32_t data = 0; + + szOutBuf[0] = 0x25; + szOutBuf[1] = (char)((addr & 0x000000FF) >> 0); + szOutBuf[2] = (char)((addr & 0x0000FF00) >> 8); + szOutBuf[3] = (char)((addr & 0x00FF0000) >> 16); + + res = core_write(core_config->slave_i2c_addr, szOutBuf, 4); + if (res < 0) + goto out; + + mdelay(10); + + res = core_read(core_config->slave_i2c_addr, szOutBuf, 4); + if (res < 0) + goto out; + + data = (szOutBuf[0] + szOutBuf[1] * 256 + szOutBuf[2] * 256 * 256 + + szOutBuf[3] * 256 * 256 * 256); + + return data; + +out: + ipio_err("Failed to read data in ICE mode, res = %d\n", res); + return res; +} +EXPORT_SYMBOL(core_config_ice_mode_read); + +/* + * Write commands into firmware in ICE Mode. + * + */ +int core_config_ice_mode_write(uint32_t addr, uint32_t data, uint32_t size) +{ + int res = 0, i; + uint8_t szOutBuf[64] = { 0 }; + + szOutBuf[0] = 0x25; + szOutBuf[1] = (char)((addr & 0x000000FF) >> 0); + szOutBuf[2] = (char)((addr & 0x0000FF00) >> 8); + szOutBuf[3] = (char)((addr & 0x00FF0000) >> 16); + + for (i = 0; i < size; i++) + szOutBuf[i + 4] = (char)(data >> (8 * i)); + + res = core_write(core_config->slave_i2c_addr, szOutBuf, size + 4); + + if (res < 0) + ipio_err("Failed to write data in ICE mode, res = %d\n", res); + + return res; +} +EXPORT_SYMBOL(core_config_ice_mode_write); + +/* + * Doing soft reset on ic. + * + * It resets ic's status, moves code and leave ice mode automatically if in + * that mode. + */ +void core_config_ic_reset(void) +{ +#ifdef HOST_DOWNLOAD + core_config_ice_mode_disable(); +#else + uint32_t key = 0; + + if (core_config->chip_id == CHIP_TYPE_ILI7807) { + if (core_config->chip_type == ILI7807_TYPE_H) + key = 0x00117807; + else + key = 0x00017807; + } + if (core_config->chip_id == CHIP_TYPE_ILI9881) + key = 0x00019881; + + ipio_debug(DEBUG_CONFIG, "key = 0x%x\n", key); + if (key != 0) { + core_config->do_ic_reset = true; + core_config_ice_mode_write(core_config->ic_reset_addr, key, 4); + core_config->do_ic_reset = false; + } + + msleep(100); +#endif +} +EXPORT_SYMBOL(core_config_ic_reset); + +void core_config_sense_ctrl(bool start) +{ + ipio_info("sense start = %d\n", start); + + return core_protocol_func_control(0, start); +} +EXPORT_SYMBOL(core_config_sense_ctrl); + +void core_config_sleep_ctrl(bool out) +{ + ipio_info("Sleep Out = %d\n", out); + + return core_protocol_func_control(1, out); +} +EXPORT_SYMBOL(core_config_sleep_ctrl); + +void core_config_glove_ctrl(bool enable, bool seamless) +{ + int cmd = 0x2; /* default as semaless */ + + if (!seamless) { + if (enable) + cmd = 0x1; + else + cmd = 0x0; + } + + ipio_info("Glove = %d, seamless = %d, cmd = %d\n", + enable, seamless, cmd); + + return core_protocol_func_control(2, cmd); +} +EXPORT_SYMBOL(core_config_glove_ctrl); + +void core_config_stylus_ctrl(bool enable, bool seamless) +{ + int cmd = 0x2; /* default as semaless */ + + if (!seamless) { + if (enable) + cmd = 0x1; + else + cmd = 0x0; + } + + ipio_info("stylus = %d, seamless = %d, cmd = %x\n" + , enable, seamless, cmd); + + return core_protocol_func_control(3, cmd); +} +EXPORT_SYMBOL(core_config_stylus_ctrl); + +void core_config_tp_scan_mode(bool mode) +{ + ipio_info("TP Scan mode = %d\n", mode); + + return core_protocol_func_control(4, mode); +} +EXPORT_SYMBOL(core_config_tp_scan_mode); + +void core_config_lpwg_ctrl(bool enable) +{ + ipio_info("LPWG = %d\n", enable); + + return core_protocol_func_control(5, enable); +} +EXPORT_SYMBOL(core_config_lpwg_ctrl); + +void core_config_gesture_ctrl(uint8_t func) +{ + uint8_t max_byte = 0x0, min_byte = 0x0; + + ipio_info("Gesture function = 0x%x\n", func); + + max_byte = 0x3F; + min_byte = 0x20; + + if (func > max_byte || func < min_byte) { + ipio_err("Gesture ctrl error, 0x%x\n", func); + return; + } + + return core_protocol_func_control(6, func); +} +EXPORT_SYMBOL(core_config_gesture_ctrl); + +void core_config_phone_cover_ctrl(bool enable) +{ + ipio_info("Phone Cover = %d\n", enable); + + return core_protocol_func_control(7, enable); +} +EXPORT_SYMBOL(core_config_phone_cover_ctrl); + +void core_config_finger_sense_ctrl(bool enable) +{ + ipio_info("Finger sense = %d\n", enable); + + return core_protocol_func_control(0, enable); +} +EXPORT_SYMBOL(core_config_finger_sense_ctrl); + +void core_config_proximity_ctrl(bool enable) +{ + ipio_info("Proximity = %d\n", enable); + + return core_protocol_func_control(11, enable); +} +EXPORT_SYMBOL(core_config_proximity_ctrl); + +void core_config_plug_ctrl(bool out) +{ + ipio_info("Plug Out = %d\n", out); + + return core_protocol_func_control(12, out); +} +EXPORT_SYMBOL(core_config_plug_ctrl); + +void core_config_set_phone_cover(uint8_t *pattern) +{ + int i; + + if (pattern == NULL) { + ipio_err("Invaild pattern\n"); + return; + } + + for (i = 0 ; i < 8; i++) + protocol->phone_cover_window[i+1] = pattern[i]; + + ipio_info("window: cmd = 0x%x\n", protocol->phone_cover_window[0]); + ipio_info("window: ul_x_l = 0x%x, ul_x_h = 0x%x\n", + protocol->phone_cover_window[1], + protocol->phone_cover_window[2]); + ipio_info("window: ul_y_l = 0x%x, ul_y_l = 0x%x\n", + protocol->phone_cover_window[3], + protocol->phone_cover_window[4]); + ipio_info("window: br_x_l = 0x%x, br_x_l = 0x%x\n" + , protocol->phone_cover_window[5], + protocol->phone_cover_window[6]); + ipio_info("window: br_y_l = 0x%x, br_y_l = 0x%x\n", + protocol->phone_cover_window[7], + protocol->phone_cover_window[8]); + + core_protocol_func_control(9, 0); +} +EXPORT_SYMBOL(core_config_set_phone_cover); + +/* + * ic_suspend: Get IC to suspend called from system. + * + * The timing when goes to sense stop or houw much times the command need to + * be called is depending on customer's system requirement, which might be + * different due to the DDI design or other conditions. + */ +void core_config_ic_suspend(void) +{ + int i = 0; + + if (ipd->suspended) { + ipio_info("TP already in suspend status ...\n"); + return; + } + ipio_info("Starting to suspend ...\n"); + + ilitek_platform_disable_irq(); + + if (ipd->isEnablePollCheckPower) + cancel_delayed_work_sync(&ipd->check_power_status_work); + +#ifndef SUSPEND_TP_POWER_OFF + /* sense stop */ + core_config_sense_ctrl(false); + msleep(100); + + /* check system busy */ + if (core_config_check_cdc_busy(50, 10) < 0) + ipio_err("Check busy is timout !\n"); + + ipio_info("Enabled Gesture = %d\n", core_config->isEnableGesture); + + if (core_config->isEnableGesture) { + core_fr->actual_fw_mode = P5_0_FIRMWARE_GESTURE_MODE; +#ifdef HOST_DOWNLOAD + if (core_gesture_load_code() < 0) + ipio_err("load gesture code fail\n"); +#endif + ilitek_platform_enable_irq(); + } else { + /* sleep in */ + core_config_sleep_ctrl(false); + } +#endif + /* release all touches */ + if (ipd->MT_B_TYPE) { + for (i = 0 ; i < ipd->MAX_TOUCH_NUM; i++) { + core_fr_touch_release(0, 0, i); + } + input_report_key(core_fr->input_device, BTN_TOUCH, 0); + input_report_key(core_fr->input_device, BTN_TOOL_FINGER, 0); + } else{ + core_fr_touch_release(0, 0, 0); + } + + input_sync(core_fr->input_device); + + ipd->suspended = true; + ipio_info("Suspend done\n"); +} +EXPORT_SYMBOL(core_config_ic_suspend); + +/* + * ic_resume: Get IC to resume called from system. + * + * The timing when goes to sense start or houw much times the command need to + * be called is depending on customer's system requirement, which might be + * different due to the DDI design or other conditions. + */ +void core_config_ic_resume(void) +{ + if (!ipd->suspended) { + ipio_info("TP already in resume statues ...\n"); + return; + } + ipio_info("Starting to resume ...\n"); + +#ifndef SUSPEND_TP_POWER_OFF + + if (core_config->isEnableGesture) { +#ifdef HOST_DOWNLOAD + ilitek_platform_disable_irq(); + if (core_gesture_load_ap_code() < 0) { + ipio_err("load ap code fail\n"); + ilitek_platform_tp_hw_reset(true); + } +#endif + } else { + /*ilitek_platform_tp_hw_reset(true);*/ + /* Soft reset */ + core_config_ice_mode_enable(); + core_config_set_watch_dog(false); + mdelay(10); + if (ipd->hardware_rst) { + ilitek_platform_tp_hw_reset(true); + } else { + core_config_ic_reset(); + } + } + + core_fr_mode_control(&protocol->demo_mode); + +#endif + ilitek_platform_enable_irq(); + + if (ipd->isEnablePollCheckPower) + queue_delayed_work(ipd->check_power_status_queue, + &ipd->check_power_status_work, ipd->work_delay); + + ipd->suspended = false; + ipio_info("Resume done\n"); +} +EXPORT_SYMBOL(core_config_ic_resume); + +int core_config_ice_mode_disable(void) +{ + uint32_t res = 0; + uint8_t cmd[4]; + + cmd[0] = 0x1b; + cmd[1] = 0x62; + cmd[2] = 0x10; + cmd[3] = 0x18; + + ipio_info("ICE Mode disabled\n"); + res = core_write(core_config->slave_i2c_addr, cmd, 4); + core_config->icemodeenable = false; + return res; +} +EXPORT_SYMBOL(core_config_ice_mode_disable); + +int core_config_ice_mode_enable(void) +{ + ipio_info("ICE Mode enabled\n"); + core_config->icemodeenable = true; + if (core_config_ice_mode_write(0x181062, 0x0, 0) < 0) + return ERROR; + + return 0; +} +EXPORT_SYMBOL(core_config_ice_mode_enable); + +int core_config_set_watch_dog(bool enable) +{ + int timeout = 10, ret = 0; + uint8_t off_bit = 0x5A, on_bit = 0xA5; + uint8_t value_low = 0x0, value_high = 0x0; + uint32_t wdt_addr = core_config->wdt_addr; + + if (wdt_addr <= 0 || core_config->chip_id <= 0) { + ipio_err("WDT/CHIP ID is invalid\n"); + return -EINVAL; + } + + /* Config register and values by IC */ + if (core_config->chip_id == CHIP_TYPE_ILI7807) { + value_low = 0x07; + value_high = 0x78; + } else if (core_config->chip_id == CHIP_TYPE_ILI9881) { + value_low = 0x81; + value_high = 0x98; + } else { + ipio_err("Unknown CHIP type (0x%x)\n", core_config->chip_id); + return -ENODEV; + } + + if (enable) { + core_config_ice_mode_write(wdt_addr, 1, 1); + } else { + core_config_ice_mode_write(wdt_addr, value_low, 1); + core_config_ice_mode_write(wdt_addr, value_high, 1); + } + + while (timeout > 0) { + ret = core_config_ice_mode_read(0x51018); + ipio_debug(DEBUG_CONFIG, "bit = %x\n", ret); + + if (enable) { + if (CHECK_EQUAL(ret, on_bit) == 0) + break; + } else { + if (CHECK_EQUAL(ret, off_bit) == 0) + break; + } + + timeout--; + mdelay(10); + } + + if (timeout > 0) { + if (enable) { + ipio_info("WDT turn on succeed\n"); + } else { + core_config_ice_mode_write(wdt_addr, 0, 1); + ipio_info("WDT turn off succeed\n"); + } + } else { + ipio_err("WDT turn on/off timeout !\n"); + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(core_config_set_watch_dog); + +int core_config_check_cdc_busy(int conut, int delay) +{ + int timer = conut, res = ERROR; + uint8_t cmd[2] = { 0 }; + uint8_t busy = 0; + + cmd[0] = protocol->cmd_read_ctrl; + cmd[1] = protocol->cmd_cdc_busy; + + while (timer > 0) { + mdelay(delay); + core_write(core_config->slave_i2c_addr, cmd, 2); + core_write(core_config->slave_i2c_addr, &cmd[1], 1); + core_read(core_config->slave_i2c_addr, &busy, 1); + if (core_fr->actual_fw_mode == + P5_0_FIRMWARE_DEMO_MODE && busy == 0x41) { + ipio_info("Check busy is free\n"); + res = 0; + break; + } + if (core_fr->actual_fw_mode == + P5_0_FIRMWARE_TEST_MODE && busy == 0x51) { + ipio_info("Check busy is free\n"); + res = 0; + break; + } + timer--; + } + + if (res < -1) + ipio_info("%d mode Check busy timeout !!\n", + core_fr->actual_fw_mode); + + return res; +} +EXPORT_SYMBOL(core_config_check_cdc_busy); + +int core_config_check_int_status(bool high) +{ + int timer = 5000, res = ERROR; + + /* From FW request, timeout should at least be 5 sec */ + while (timer) { + if (high) { + if (gpio_get_value(ipd->int_gpio)) { + ipio_info("Check busy is free\n"); + res = 0; + break; + } + } else { + if (!gpio_get_value(ipd->int_gpio)) { + ipio_info("Check busy is free\n"); + res = 0; + break; + } + } + + mdelay(1); + timer--; + } + + if (res < -1) + ipio_info("Check busy timeout !!\n"); + + return res; +} +EXPORT_SYMBOL(core_config_check_int_status); + + + +int core_config_check_int_isr_flag(void) +{ + int timer = 5000, res = ERROR; + + /* From FW request, timeout should at least be 5 sec */ + while (core_config->interrupt_flag == false && timer > 0) { + timer--; + mdelay(1); + } + if (core_config->interrupt_flag == true) + res = 0; + + return res; +} +EXPORT_SYMBOL(core_config_check_int_isr_flag); + + +int core_config_get_project_id(uint8_t *pid_data) +{ + int i = 0, res = 0; + uint32_t pid_addr = 0x1D000, pid_size = 10; + + res = core_config_ice_mode_enable(); + if (res < 0) { + ipio_err("Failed to enter ICE mode, res = %d\n", res); + return ERROR; + } + + /* Disable watch dog */ + core_config_set_watch_dog(false); + + core_config_ice_mode_write(0x041000, 0x0, 1); /* CS low */ + core_config_ice_mode_write(0x041004, 0x66aa55, 3); /* Key */ + + core_config_ice_mode_write(0x041008, 0x06, 1); + core_config_ice_mode_write(0x041000, 0x01, 1); + core_config_ice_mode_write(0x041000, 0x00, 1); + core_config_ice_mode_write(0x041004, 0x66aa55, 3); /* Key */ + core_config_ice_mode_write(0x041008, 0x03, 1); + + core_config_ice_mode_write(0x041008, (pid_addr & 0xFF0000) >> 16, 1); + core_config_ice_mode_write(0x041008, (pid_addr & 0x00FF00) >> 8, 1); + core_config_ice_mode_write(0x041008, (pid_addr & 0x0000FF), 1); + + for (i = 0 ; i < pid_size; i++) { + core_config_ice_mode_write(0x041008, 0xFF, 1); + pid_data[i] = core_config_ice_mode_read(0x41010); + ipio_info("pid_data[%d] = 0x%x\n", i, pid_data[i]); + } + + core_config_ice_mode_write(0x041010, 0x1, 0); /* CS high */ + core_config_ic_reset(); + + return res; +} +EXPORT_SYMBOL(core_config_get_project_id); + +int core_config_get_key_info(void) +{ + int res = 0, i; + uint8_t cmd[2] = { 0 }; + + memset(g_read_buf, 0, sizeof(g_read_buf)); + + cmd[0] = protocol->cmd_read_ctrl; + cmd[1] = protocol->cmd_get_key_info; + + res = core_write(core_config->slave_i2c_addr, cmd, 2); + if (res < 0) { + ipio_err("Failed to write data via I2C, %d\n", res); + goto out; + } + + mdelay(1); + + res = core_write(core_config->slave_i2c_addr, &cmd[1], 1); + if (res < 0) { + ipio_err("Failed to write data via I2C, %d\n", res); + goto out; + } + + mdelay(1); + + res = core_read(core_config->slave_i2c_addr, + &g_read_buf[0], protocol->key_info_len); + if (res < 0) { + ipio_err("Failed to read data via I2C, %d\n", res); + goto out; + } + + if (core_config->tp_info->nKeyCount) { + /* NOTE: Firmware not ready yet */ + core_config->tp_info->nKeyAreaXLength = + (g_read_buf[0] << 8) + g_read_buf[1]; + core_config->tp_info->nKeyAreaYLength = + (g_read_buf[2] << 8) + g_read_buf[3]; + + ipio_info("key: length of X area = %x\n", + core_config->tp_info->nKeyAreaXLength); + ipio_info("key: length of Y area = %x\n", + core_config->tp_info->nKeyAreaYLength); + + for (i = 0 ; i < core_config->tp_info->nKeyCount; i++) { + core_config->tp_info->virtual_key[i].nId = + g_read_buf[i * 5 + 4]; + core_config->tp_info->virtual_key[i].nX = + (g_read_buf[i * 5 + 5] << 8) + g_read_buf[i * 5 + 6]; + core_config->tp_info->virtual_key[i].nY = + (g_read_buf[i * 5 + 7] << 8) + g_read_buf[i * 5 + 8]; + core_config->tp_info->virtual_key[i].nStatus = 0; + + ipio_info("key: id = %d, X = %d, Y = %d\n", + core_config->tp_info->virtual_key[i].nId, + core_config->tp_info->virtual_key[i].nX, + core_config->tp_info->virtual_key[i].nY); + } + } + +out: + return res; +} +EXPORT_SYMBOL(core_config_get_key_info); + +int core_config_get_tp_info(void) +{ + int res = 0; + uint8_t cmd[2] = { 0 }; + + memset(g_read_buf, 0, sizeof(g_read_buf)); + + cmd[0] = protocol->cmd_read_ctrl; + cmd[1] = protocol->cmd_get_tp_info; + + res = core_write(core_config->slave_i2c_addr, cmd, 2); + if (res < 0) { + ipio_err("Failed to write data via I2C, %d\n", res); + goto out; + } + + mdelay(1); + + res = core_write(core_config->slave_i2c_addr, &cmd[1], 1); + if (res < 0) { + ipio_err("Failed to write data via I2C, %d\n", res); + goto out; + } + + mdelay(1); + + res = core_read(core_config->slave_i2c_addr, &g_read_buf[0], + protocol->tp_info_len); + if (res < 0) { + ipio_err("Failed to read data via I2C, %d\n", res); + goto out; + } + + /* in protocol v5, ignore the first btye because of a header. */ + core_config->tp_info->nMinX = g_read_buf[1]; + core_config->tp_info->nMinY = g_read_buf[2]; + core_config->tp_info->nMaxX = (g_read_buf[4] << 8) + g_read_buf[3]; + core_config->tp_info->nMaxY = (g_read_buf[6] << 8) + g_read_buf[5]; + core_config->tp_info->nXChannelNum = g_read_buf[7]; + core_config->tp_info->nYChannelNum = g_read_buf[8]; + core_config->tp_info->self_tx_channel_num = g_read_buf[11]; + core_config->tp_info->self_rx_channel_num = g_read_buf[12]; + core_config->tp_info->side_touch_type = g_read_buf[13]; + core_config->tp_info->nMaxTouchNum = g_read_buf[9]; + core_config->tp_info->nKeyCount = g_read_buf[10]; + + core_config->tp_info->nMaxKeyButtonNum = 5; + + ipio_debug(DEBUG_BOOT,"minX = %d, minY = %d, maxX = %d, maxY = %d\n", + core_config->tp_info->nMinX, core_config->tp_info->nMinY, + core_config->tp_info->nMaxX, core_config->tp_info->nMaxY); + ipio_debug(DEBUG_BOOT,"xchannel = %d, ychannel = %d, self_tx = %d, self_rx = %d\n", + core_config->tp_info->nXChannelNum, + core_config->tp_info->nYChannelNum, + core_config->tp_info->self_tx_channel_num, + core_config->tp_info->self_rx_channel_num); + ipio_debug(DEBUG_BOOT,"side_touch_type = %d, max_touch_num= %d, touch_key_num = %d, max_key_num = %d\n", + core_config->tp_info->side_touch_type, + core_config->tp_info->nMaxTouchNum, + core_config->tp_info->nKeyCount, + core_config->tp_info->nMaxKeyButtonNum); + +out: + return res; +} +EXPORT_SYMBOL(core_config_get_tp_info); + +int core_config_get_protocol_ver(void) +{ + int res = 0, i = 0; + int major, mid, minor; + uint8_t cmd[2] = { 0 }; + + memset(g_read_buf, 0, sizeof(g_read_buf)); + memset(core_config->protocol_ver, 0x0, + sizeof(core_config->protocol_ver)); + + cmd[0] = protocol->cmd_read_ctrl; + cmd[1] = protocol->cmd_get_pro_ver; + + res = core_write(core_config->slave_i2c_addr, cmd, 2); + if (res < 0) { + ipio_err("Failed to write data via I2C, %d\n", res); + goto out; + } + + mdelay(1); + + res = core_write(core_config->slave_i2c_addr, &cmd[1], 1); + if (res < 0) { + ipio_err("Failed to write data via I2C, %d\n", res); + goto out; + } + + mdelay(1); + + res = core_read(core_config->slave_i2c_addr, &g_read_buf[0], + protocol->pro_ver_len); + if (res < 0) { + ipio_err("Failed to read data via I2C, %d\n", res); + goto out; + } + + /* ignore the first btye because of a header. */ + for (; i < protocol->pro_ver_len - 1; i++) + core_config->protocol_ver[i] = g_read_buf[i + 1]; + + ipio_debug(DEBUG_BOOT,"Procotol Version = %d.%d.%d\n", + core_config->protocol_ver[0], core_config->protocol_ver[1], + core_config->protocol_ver[2]); + + major = core_config->protocol_ver[0]; + mid = core_config->protocol_ver[1]; + minor = core_config->protocol_ver[2]; + + /* update protocol if they're different with the default ver set by + * driver + */ + if (major != PROTOCOL_MAJOR || mid != PROTOCOL_MID || + minor != PROTOCOL_MINOR) { + res = core_protocol_update_ver(major, mid, minor); + if (res < 0) + ipio_err("Protocol version is invalid\n"); + } + +out: + return res; +} +EXPORT_SYMBOL(core_config_get_protocol_ver); + +int core_config_get_core_ver(void) +{ + int res = 0, i = 0; + uint8_t cmd[2] = { 0 }; + + memset(g_read_buf, 0, sizeof(g_read_buf)); + + cmd[0] = protocol->cmd_read_ctrl; + cmd[1] = protocol->cmd_get_core_ver; + + res = core_write(core_config->slave_i2c_addr, cmd, 2); + if (res < 0) { + ipio_err("Failed to write data via I2C, %d\n", res); + goto out; + } + + mdelay(1); + + res = core_write(core_config->slave_i2c_addr, &cmd[1], 1); + if (res < 0) { + ipio_err("Failed to write data via I2C, %d\n", res); + goto out; + } + + mdelay(1); + + res = core_read(core_config->slave_i2c_addr, &g_read_buf[0], + protocol->core_ver_len); + if (res < 0) { + ipio_err("Failed to read data via I2C, %d\n", res); + goto out; + } + + for (; i < protocol->core_ver_len - 1; i++) + core_config->core_ver[i] = g_read_buf[i + 1]; + + /* in protocol v5, ignore the first btye because of a header. */ + ipio_debug(DEBUG_BOOT,"Core Version = %d.%d.%d.%d\n", + core_config->core_ver[1], core_config->core_ver[2], + core_config->core_ver[3], core_config->core_ver[4]); + +out: + return res; +} +EXPORT_SYMBOL(core_config_get_core_ver); + +/* + * Getting the version of firmware used on the current one. + * + */ +int core_config_get_fw_ver(void) +{ + int res = 0, i = 0; + uint8_t cmd[2] = { 0 }; + + memset(g_read_buf, 0, sizeof(g_read_buf)); + + cmd[0] = protocol->cmd_read_ctrl; + cmd[1] = protocol->cmd_get_fw_ver; + + res = core_write(core_config->slave_i2c_addr, cmd, 2); + if (res < 0) { + ipio_err("Failed to write data via I2C, %d\n", res); + goto out; + } + + mdelay(1); + + res = core_write(core_config->slave_i2c_addr, &cmd[1], 1); + if (res < 0) { + ipio_err("Failed to write data via I2C, %d\n", res); + goto out; + } + + mdelay(1); + + res = core_read(core_config->slave_i2c_addr, &g_read_buf[0], + protocol->fw_ver_len); + if (res < 0) { + ipio_err("Failed to read fw version %d\n", res); + goto out; + } + + for (; i < protocol->fw_ver_len ; i++) + core_config->firmware_ver[i] = g_read_buf[i]; + + if (protocol->mid >= 0x3) { + ipio_debug(DEBUG_BOOT,"Firmware Version = %d.%d.%d.%d\n", + core_config->firmware_ver[1], core_config->firmware_ver[2], + core_config->firmware_ver[3], core_config->firmware_ver[4]); + } else { + ipio_debug(DEBUG_BOOT,"Firmware Version = %d.%d.%d\n", + core_config->firmware_ver[1], + core_config->firmware_ver[2], + core_config->firmware_ver[3]); + } +out: + return res; +} +EXPORT_SYMBOL(core_config_get_fw_ver); + +int core_config_get_chip_id(void) +{ + int res = 0; + static int do_once; + uint32_t RealID = 0, PIDData = 0; + + res = core_config_ice_mode_enable(); + if (res < 0) { + ipio_err("Failed to enter ICE mode, res = %d\n", res); + goto out; + } + + mdelay(20); + + PIDData = core_config_ice_mode_read(core_config->pid_addr); + core_config->chip_pid = PIDData; + core_config->core_type = PIDData & 0xFF; + ipio_info("PID = 0x%x, Core type = 0x%x\n", + core_config->chip_pid, core_config->core_type); + + if (PIDData) { + RealID = check_chip_id(PIDData); + if (RealID != core_config->chip_id) { + ipio_err("CHIP ID ERROR: 0x%x, TP_TOUCH_IC = 0x%x\n", + RealID, ipd->chip_id); + res = -ENODEV; + goto out; + } + } else { + ipio_err("PID DATA error : 0x%x\n", PIDData); + res = -EINVAL; + goto out; + } + + if (do_once == 0) { + /* reading flash id needs to let ic entry to ICE mode */ + read_flash_info(0x9F, 4); + do_once = 1; + } + +out: + core_config_ic_reset(); + mdelay(150); + return res; +} +EXPORT_SYMBOL(core_config_get_chip_id); + +int core_config_init(void) +{ + int i = 0; + + core_config = kzalloc(sizeof(*core_config) * sizeof(uint8_t) * 6, + GFP_KERNEL); + if (ERR_ALLOC_MEM(core_config)) { + ipio_err("Failed to allocate core_config mem, %ld\n", + PTR_ERR(core_config)); + core_config_remove(); + return -ENOMEM; + } + + core_config->tp_info = kzalloc(sizeof(*core_config->tp_info), + GFP_KERNEL); + if (ERR_ALLOC_MEM(core_config->tp_info)) { + ipio_err("Failed to allocate core_config->tp_info mem, %ld\n", + PTR_ERR(core_config->tp_info)); + core_config_remove(); + return -ENOMEM; + } + + for (; i < ARRAY_SIZE(ipio_chip_list); i++) { + if (ipio_chip_list[i] == ipd->chip_id) { + core_config->chip_id = ipio_chip_list[i]; + core_config->chip_type = 0x0000; + core_config->do_ic_reset = false; + core_config->isEnableGesture = false; + + if (core_config->chip_id == CHIP_TYPE_ILI7807) { + core_config->slave_i2c_addr = + ILI7807_SLAVE_ADDR; + core_config->ice_mode_addr = + ILI7807_ICE_MODE_ADDR; + core_config->pid_addr = ILI7807_PID_ADDR; + core_config->wdt_addr = ILI7808_WDT_ADDR; + } else if (core_config->chip_id == CHIP_TYPE_ILI9881) { + core_config->slave_i2c_addr = + ILI9881_SLAVE_ADDR; + core_config->ice_mode_addr = + ILI9881_ICE_MODE_ADDR; + core_config->pid_addr = ILI9881_PID_ADDR; + core_config->wdt_addr = ILI9881_WDT_ADDR; + } + return 0; + } + } + + ipio_err("Can't find this chip in support list\n"); + return 0; +} +EXPORT_SYMBOL(core_config_init); + +void core_config_remove(void) +{ + ipio_info("Remove core-config memebers\n"); + + if (core_config != NULL) { + ipio_kfree((void **)&core_config->tp_info); + ipio_kfree((void **)&core_config); + } +} +EXPORT_SYMBOL(core_config_remove); diff --git a/drivers/input/touchscreen/ilitek_mmi/core/config.h b/drivers/input/touchscreen/ilitek_mmi/core/config.h new file mode 100644 index 000000000000..b3b63cb810e2 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/config.h @@ -0,0 +1,137 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __CONFIG_H +#define __CONFIG_H + +typedef struct { + int nId; + int nX; + int nY; + int nStatus; + int nFlag; +} VIRTUAL_KEYS; + +typedef struct { + uint16_t nMaxX; + uint16_t nMaxY; + uint16_t nMinX; + uint16_t nMinY; + + uint8_t nMaxTouchNum; + uint8_t nMaxKeyButtonNum; + + uint8_t nXChannelNum; + uint8_t nYChannelNum; + uint8_t nHandleKeyFlag; + uint8_t nKeyCount; + + uint16_t nKeyAreaXLength; + uint16_t nKeyAreaYLength; + + VIRTUAL_KEYS virtual_key[10]; + + /* added for protocol v5 */ + uint8_t self_tx_channel_num; + uint8_t self_rx_channel_num; + uint8_t side_touch_type; + +} TP_INFO; + +struct core_config_data { + uint32_t chip_id; + uint32_t chip_type; + uint32_t chip_pid; + + uint8_t core_type; + + uint32_t slave_i2c_addr; + uint32_t ice_mode_addr; + uint32_t pid_addr; + uint32_t wdt_addr; + uint32_t ic_reset_addr; + + uint8_t protocol_ver[4]; + uint8_t firmware_ver[9]; + uint8_t core_ver[5]; + + bool do_ic_reset; + bool isEnableGesture; + bool icemodeenable; + bool spi_pro_9881h11; + bool interrupt_flag; + TP_INFO *tp_info; +}; + +extern struct core_config_data *core_config; + +extern int fw_cmd_len; +extern int protocol_cmd_len; +extern int tp_info_len; +extern int key_info_len; +extern int core_cmd_len; + +/* R/W with Touch ICs */ +extern uint32_t core_config_ice_mode_read(uint32_t addr); +extern int core_config_ice_mode_write(uint32_t addr, uint32_t data, uint32_t size); +extern uint32_t core_config_read_write_onebyte(uint32_t addr); +extern int core_config_ice_mode_disable(void); +extern int core_config_ice_mode_enable(void); +extern uint32_t core_config_read_pc_counter(void); + +/* Touch IC status */ +extern int core_config_set_watch_dog(bool enable); +extern int core_config_check_cdc_busy(int count, int delay); +extern int core_config_check_int_status(bool high); +extern int core_config_check_int_isr_flag(void); +extern void core_config_ic_suspend(void); +extern void core_config_ic_resume(void); +extern void core_config_ic_reset(void); + +/* control features of Touch IC */ +extern void core_config_sense_ctrl(bool start); +extern void core_config_sleep_ctrl(bool out); +extern void core_config_glove_ctrl(bool enable, bool seamless); +extern void core_config_stylus_ctrl(bool enable, bool seamless); +extern void core_config_tp_scan_mode(bool mode); +extern void core_config_lpwg_ctrl(bool enable); +extern void core_config_gesture_ctrl(uint8_t func); +extern void core_config_phone_cover_ctrl(bool enable); +extern void core_config_finger_sense_ctrl(bool enable); +extern void core_config_proximity_ctrl(bool enable); +extern void core_config_plug_ctrl(bool out); +extern void core_config_set_phone_cover(uint8_t *pattern); + +/* Touch IC information */ +extern int core_config_get_core_ver(void); +extern int core_config_get_key_info(void); +extern int core_config_get_tp_info(void); +extern int core_config_get_protocol_ver(void); +extern int core_config_get_fw_ver(void); +extern int core_config_get_chip_id(void); + +extern int core_config_init(void); +extern void core_config_remove(void); + +#endif /* __CONFIG_H */ diff --git a/drivers/input/touchscreen/ilitek_mmi/core/finger_report.c b/drivers/input/touchscreen/ilitek_mmi/core/finger_report.c new file mode 100644 index 000000000000..9cfc1b838c3e --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/finger_report.c @@ -0,0 +1,839 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../common.h" +#include "../platform.h" +#include "config.h" +#include "i2c.h" +#include "finger_report.h" +#include "gesture.h" +#include "mp_test.h" +#include "protocol.h" + +/* An id with position in each fingers */ +struct mutual_touch_point { + uint16_t id; + uint16_t x; + uint16_t y; + uint16_t pressure; +}; + +/* Keys and code with each fingers */ +struct mutual_touch_info { + uint8_t touch_num; + uint8_t key_code; + struct mutual_touch_point mtp[10]; +}; + +/* Store the packet of finger report */ +struct fr_data_node { + uint8_t *data; + uint16_t len; +}; + +/* record the status of touch being pressed or released currently and previosuly */ +uint8_t g_current_touch[10]; +uint8_t g_previous_touch[10]; + +/* the total length of finger report packet */ +uint16_t g_total_len; + +struct mutual_touch_info g_mutual_data; +struct fr_data_node *g_fr_node = NULL, *g_fr_uart = NULL; +struct core_fr_data *core_fr; + +/** + * Calculate the check sum of each packet reported by firmware + * + * @pMsg: packet come from firmware + * @nLength : the length of its packet + */ +uint8_t core_fr_calc_checksum(uint8_t *pMsg, uint32_t nLength) +{ + int i; + int32_t nCheckSum = 0; + + for (i = 0 ; i < nLength; i++) { + nCheckSum += pMsg[i]; + } + + return (uint8_t) ((-nCheckSum) & 0xFF); +} +EXPORT_SYMBOL(core_fr_calc_checksum); + +/** + * Receive data when fw mode stays at i2cuart mode. + * + * the first is to receive N bytes depending on the mode that firmware stays + * before going in this function, and it would check with i2c buffer if it + * remains the rest of data. + */ +static void i2cuart_recv_packet(void) +{ + int res = 0, need_read_len = 0, one_data_bytes = 0; + int type = g_fr_node->data[3] & 0x0F; + int actual_len = g_fr_node->len - 5; + + ipio_debug(DEBUG_FINGER_REPORT, "pid = %x, data[3] = %x, type = %x, actual_len = %d\n", + g_fr_node->data[0], g_fr_node->data[3], type, actual_len); + + need_read_len = g_fr_node->data[1] * g_fr_node->data[2]; + + if (type == 0 || type == 1 || type == 6) { + one_data_bytes = 1; + } else if (type == 2 || type == 3) { + one_data_bytes = 2; + } else if (type == 4 || type == 5) { + one_data_bytes = 4; + } + + ipio_debug(DEBUG_FINGER_REPORT, "need_read_len = %d one_data_bytes = %d\n", need_read_len, one_data_bytes); + + need_read_len = need_read_len * one_data_bytes + 1; + + if (need_read_len > actual_len) { + g_fr_uart = kmalloc(sizeof(*g_fr_uart), GFP_ATOMIC); + if (ERR_ALLOC_MEM(g_fr_uart)) { + ipio_err("Failed to allocate g_fr_uart memory %ld\n", PTR_ERR(g_fr_uart)); + return; + } + + g_fr_uart->len = need_read_len - actual_len; + g_fr_uart->data = kzalloc(g_fr_uart->len, GFP_ATOMIC); + if (ERR_ALLOC_MEM(g_fr_uart->data)) { + ipio_err("Failed to allocate g_fr_uart memory %ld\n", PTR_ERR(g_fr_uart->data)); + return; + } + + g_total_len += g_fr_uart->len; + res = core_read(core_config->slave_i2c_addr, g_fr_uart->data, g_fr_uart->len); + if (res < 0) + ipio_err("Failed to read finger report packet\n"); + } +} + +/* + * It'd be called when a finger's touching down a screen. It'll notify the event + * to the uplayer from input device. + * + * @x: the axis of X + * @y: the axis of Y + * @pressure: the value of pressue on a screen + * @id: an id represents a finger pressing on a screen + */ +void core_fr_touch_press(int32_t x, int32_t y, uint32_t pressure, int32_t id) +{ + ipio_debug(DEBUG_FINGER_REPORT, "DOWN: id = %d, x = %d, y = %d\n", id, x, y); + + if (ipd->x_flip) + x = ipd->x_max - x; + if (ipd->y_flip) + y = ipd->y_max - y; + ipio_debug(DEBUG_FINGER_REPORT, "Flip:: DOWN: id = %d, x = %d, y = %d\n", id, x, y); + + if (ipd->MT_B_TYPE) { + input_mt_slot(core_fr->input_device, id); + input_mt_report_slot_state(core_fr->input_device, MT_TOOL_FINGER, true); + input_report_abs(core_fr->input_device, ABS_MT_POSITION_X, x); + input_report_abs(core_fr->input_device, ABS_MT_POSITION_Y, y); + if (core_fr->isEnablePressure) + input_report_abs(core_fr->input_device, ABS_MT_PRESSURE, pressure); + } else{ + input_report_key(core_fr->input_device, BTN_TOUCH, 1); + input_report_abs(core_fr->input_device, ABS_MT_TRACKING_ID, id); + input_report_abs(core_fr->input_device, ABS_MT_TOUCH_MAJOR, 1); + input_report_abs(core_fr->input_device, ABS_MT_WIDTH_MAJOR, 1); + input_report_abs(core_fr->input_device, ABS_MT_POSITION_X, x); + input_report_abs(core_fr->input_device, ABS_MT_POSITION_Y, y); + if (core_fr->isEnablePressure) + input_report_abs(core_fr->input_device, ABS_MT_PRESSURE, pressure); + + input_mt_sync(core_fr->input_device); + } +} +EXPORT_SYMBOL(core_fr_touch_press); + +/* + * It'd be called when a finger's touched up from a screen. It'll notify + * the event to the uplayer from input device. + * + * @x: the axis of X + * @y: the axis of Y + * @id: an id represents a finger leaving from a screen. + */ +void core_fr_touch_release(int32_t x, int32_t y, int32_t id) +{ + ipio_debug(DEBUG_FINGER_REPORT, "UP: id = %d, x = %d, y = %d\n", id, x, y); + + if (ipd->MT_B_TYPE) { + input_mt_slot(core_fr->input_device, id); + input_mt_report_slot_state(core_fr->input_device, MT_TOOL_FINGER, false); +} else{ + input_report_key(core_fr->input_device, BTN_TOUCH, 0); + input_mt_sync(core_fr->input_device); + } +} +EXPORT_SYMBOL(core_fr_touch_release); + +static int parse_touch_package_v3_2(void) +{ + ipio_info("Not implemented yet\n"); + return 0; +} + +static int finger_report_ver_3_2(void) +{ + ipio_info("Not implemented yet\n"); + parse_touch_package_v3_2(); + return 0; +} + +/* + * It mainly parses the packet assembled by protocol v5.0 + */ +static int parse_touch_package_v5_0(uint8_t pid) +{ + int i, res = 0; + uint8_t check_sum = 0; + uint32_t nX = 0, nY = 0; + + if(ipio_debug_level > 0) + { + dump_data(g_fr_node->data, 8, g_fr_node->len, 0, "touch report"); + } + check_sum = core_fr_calc_checksum(&g_fr_node->data[0], (g_fr_node->len - 1)); + ipio_debug(DEBUG_FINGER_REPORT, "data = %x ; check_sum : %x\n", g_fr_node->data[g_fr_node->len - 1], check_sum); + + if (g_fr_node->data[g_fr_node->len - 1] != check_sum) { + ipio_err("Wrong checksum\n"); + res = ERROR; + goto out; + } + + /* start to parsing the packet of finger report */ + if (pid == protocol->demo_pid) { + ipio_debug(DEBUG_FINGER_REPORT, " **** Parsing DEMO packets : 0x%x ****\n", pid); + + for (i = 0 ; i < ipd->MAX_TOUCH_NUM; i++) { + if ((g_fr_node->data[(4 * i) + 1] == 0xFF) && (g_fr_node->data[(4 * i) + 2] && 0xFF) + && (g_fr_node->data[(4 * i) + 3] == 0xFF)) { + if (ipd->MT_B_TYPE) + g_current_touch[i] = 0; + continue; + } + + nX = (((g_fr_node->data[(4 * i) + 1] & 0xF0) << 4) | (g_fr_node->data[(4 * i) + 2])); + nY = (((g_fr_node->data[(4 * i) + 1] & 0x0F) << 8) | (g_fr_node->data[(4 * i) + 3])); + + if (!core_fr->isSetResolution) { + g_mutual_data.mtp[g_mutual_data.touch_num].x = nX * (ipd->x_max) / (ipd->TPD_WIDTH); + g_mutual_data.mtp[g_mutual_data.touch_num].y = nY * (ipd->y_max) / (ipd->TPD_HEIGHT); + g_mutual_data.mtp[g_mutual_data.touch_num].id = i; + } else { + g_mutual_data.mtp[g_mutual_data.touch_num].x = nX; + g_mutual_data.mtp[g_mutual_data.touch_num].y = nY; + g_mutual_data.mtp[g_mutual_data.touch_num].id = i; + } + + if (core_fr->isEnablePressure) + g_mutual_data.mtp[g_mutual_data.touch_num].pressure = g_fr_node->data[(4 * i) + 4]; + else + g_mutual_data.mtp[g_mutual_data.touch_num].pressure = 1; + + ipio_debug(DEBUG_FINGER_REPORT, "[x,y]=[%d,%d]\n", nX, nY); + ipio_debug(DEBUG_FINGER_REPORT, "point[%d] : (%d,%d) = %d\n", + g_mutual_data.mtp[g_mutual_data.touch_num].id, + g_mutual_data.mtp[g_mutual_data.touch_num].x, + g_mutual_data.mtp[g_mutual_data.touch_num].y, g_mutual_data.mtp[g_mutual_data.touch_num].pressure); + + g_mutual_data.touch_num++; + + if (ipd->MT_B_TYPE) + g_current_touch[i] = 1; + } + } else if (pid == protocol->debug_pid) { + ipio_debug(DEBUG_FINGER_REPORT, " **** Parsing DEBUG packets : 0x%x ****\n", pid); + ipio_debug(DEBUG_FINGER_REPORT, "Length = %d\n", (g_fr_node->data[1] << 8 | g_fr_node->data[2])); + + for (i = 0 ; i < ipd->MAX_TOUCH_NUM; i++) { + if ((g_fr_node->data[(3 * i) + 5] == 0xFF) && (g_fr_node->data[(3 * i) + 6] && 0xFF) + && (g_fr_node->data[(3 * i) + 7] == 0xFF)) { + if (ipd->MT_B_TYPE) + g_current_touch[i] = 0; + continue; + } + + nX = (((g_fr_node->data[(3 * i) + 5] & 0xF0) << 4) | (g_fr_node->data[(3 * i) + 6])); + nY = (((g_fr_node->data[(3 * i) + 5] & 0x0F) << 8) | (g_fr_node->data[(3 * i) + 7])); + + if (!core_fr->isSetResolution) { + g_mutual_data.mtp[g_mutual_data.touch_num].x = nX * (ipd->x_max) / (ipd->TPD_WIDTH); + g_mutual_data.mtp[g_mutual_data.touch_num].y = nY * (ipd->y_max) / (ipd->TPD_HEIGHT); + g_mutual_data.mtp[g_mutual_data.touch_num].id = i; + } else { + g_mutual_data.mtp[g_mutual_data.touch_num].x = nX; + g_mutual_data.mtp[g_mutual_data.touch_num].y = nY; + g_mutual_data.mtp[g_mutual_data.touch_num].id = i; + } + + if (core_fr->isEnablePressure) + g_mutual_data.mtp[g_mutual_data.touch_num].pressure = g_fr_node->data[(4 * i) + 4]; + else + g_mutual_data.mtp[g_mutual_data.touch_num].pressure = 1; + + ipio_debug(DEBUG_FINGER_REPORT, "[x,y]=[%d,%d]\n", nX, nY); + ipio_debug(DEBUG_FINGER_REPORT, "point[%d] : (%d,%d) = %d\n", + g_mutual_data.mtp[g_mutual_data.touch_num].id, + g_mutual_data.mtp[g_mutual_data.touch_num].x, + g_mutual_data.mtp[g_mutual_data.touch_num].y, g_mutual_data.mtp[g_mutual_data.touch_num].pressure); + + g_mutual_data.touch_num++; + + if (ipd->MT_B_TYPE) + g_current_touch[i] = 1; + } + } else { + if (pid != 0) { + /* ignore the pid with 0x0 after enable irq at once */ + ipio_err(" **** Unknown PID : 0x%x ****\n", pid); + res = ERROR; + } + } + +out: + return res; +} + +/* + * The function is called by an interrupt and used to handle packet of finger + * touch from firmware. A differnece in the process of the data is acorrding to the protocol + */ +static int finger_report_ver_5_0(void) +{ + int i, gesture, res = 0; + static int last_touch; + uint8_t pid = 0x0; + + memset(&g_mutual_data, 0x0, sizeof(struct mutual_touch_info)); + +#ifdef I2C_SEGMENT + res = core_i2c_segmental_read(core_config->slave_i2c_addr, g_fr_node->data, g_fr_node->len); +#else + res = core_read(core_config->slave_i2c_addr, g_fr_node->data, g_fr_node->len); +#endif + + if (res < 0) { + ipio_err("Failed to read finger report packet\n"); + if (res == CHECK_RECOVER) { + ipio_err("==================Recover=================\n"); + /*ilitek_platform_tp_hw_reset(true);*/ + /* Soft reset */ + core_config_ice_mode_enable(); + core_config_set_watch_dog(false); + mdelay(10); + core_config_ic_reset(); + } + goto out; + } + + pid = g_fr_node->data[0]; + ipio_debug(DEBUG_FINGER_REPORT, "PID = 0x%x\n", pid); + + if (pid == 0) + goto out; + + + if (pid == protocol->i2cuart_pid) { + ipio_debug(DEBUG_FINGER_REPORT, "I2CUART(0x%x): prepare to receive rest of data\n", pid); + i2cuart_recv_packet(); + goto out; + } + + if (pid == protocol->ges_pid && core_config->isEnableGesture) { + ipio_debug(DEBUG_FINGER_REPORT, "pid = 0x%x, code = %x\n", pid, g_fr_node->data[1]); + gesture = core_gesture_match_key(g_fr_node->data[1]); + if (gesture != -1) { + input_report_key(core_fr->input_device, gesture, 1); + input_sync(core_fr->input_device); + input_report_key(core_fr->input_device, gesture, 0); + input_sync(core_fr->input_device); + } + goto out; + } + + res = parse_touch_package_v5_0(pid); + if (res < 0) { + ipio_err("Failed to parse packet of finger touch\n"); + goto out; + } + + ipio_debug(DEBUG_FINGER_REPORT, "Touch Num = %d, LastTouch = %d\n", g_mutual_data.touch_num, last_touch); + + /* interpret parsed packat and send input events to system */ + if (g_mutual_data.touch_num > 0) { + if (ipd->MT_B_TYPE) { + for (i = 0 ; i < g_mutual_data.touch_num; i++) { + input_report_key(core_fr->input_device, BTN_TOUCH, 1); + core_fr_touch_press(g_mutual_data.mtp[i].x, g_mutual_data.mtp[i].y, g_mutual_data.mtp[i].pressure, g_mutual_data.mtp[i].id); + + input_report_key(core_fr->input_device, BTN_TOOL_FINGER, 1); + } + + for (i = 0 ; i < ipd->MAX_TOUCH_NUM; i++) { + ipio_debug(DEBUG_FINGER_REPORT, "g_previous_touch[%d]=%d, g_current_touch[%d]=%d\n", i, + g_previous_touch[i], i, g_current_touch[i]); + + if (g_current_touch[i] == 0 && g_previous_touch[i] == 1) { + core_fr_touch_release(0, 0, i); + } + + g_previous_touch[i] = g_current_touch[i]; + } + } else{ + for (i = 0 ; i < g_mutual_data.touch_num; i++) { + core_fr_touch_press(g_mutual_data.mtp[i].x, g_mutual_data.mtp[i].y, g_mutual_data.mtp[i].pressure, g_mutual_data.mtp[i].id); + } + } + input_sync(core_fr->input_device); + + last_touch = g_mutual_data.touch_num; + } else { + if (last_touch > 0) { + if (ipd->MT_B_TYPE) { + for (i = 0 ; i < ipd->MAX_TOUCH_NUM; i++) { + ipio_debug(DEBUG_FINGER_REPORT, "g_previous_touch[%d]=%d, g_current_touch[%d]=%d\n", i, + g_previous_touch[i], i, g_current_touch[i]); + + if (g_current_touch[i] == 0 && g_previous_touch[i] == 1) { + core_fr_touch_release(0, 0, i); + } + g_previous_touch[i] = g_current_touch[i]; + } + input_report_key(core_fr->input_device, BTN_TOUCH, 0); + input_report_key(core_fr->input_device, BTN_TOOL_FINGER, 0); + } else{ + core_fr_touch_release(0, 0, 0); + } + + input_sync(core_fr->input_device); + + last_touch = 0; + } + } + +out: + return res; +} + +void core_fr_mode_control(uint8_t *from_user) +{ + int i, mode; + int checksum = 0, codeLength = 8; + uint8_t mp_code[8] = { 0 }; + uint8_t cmd[4] = { 0 }; + + ilitek_platform_disable_irq(); + + if (from_user == NULL) { + ipio_err("Arguments from user space are invaild\n"); + goto out; + } + + ipio_debug(DEBUG_FINGER_REPORT, "mode = %x, b1 = %x, b2 = %x, b3 = %x\n", + from_user[0], from_user[1], from_user[2], from_user[3]); + + mode = from_user[0]; + + if (protocol->major == 0x5) { + if (mode == protocol->i2cuart_mode) { + cmd[0] = protocol->cmd_i2cuart; + cmd[1] = *(from_user + 1); + cmd[2] = *(from_user + 2); + + ipio_info("Switch to I2CUART mode, cmd = %x, b1 = %x, b2 = %x\n", cmd[0], cmd[1], cmd[2]); + + if ((core_write(core_config->slave_i2c_addr, cmd, 3)) < 0) { + ipio_err("Failed to switch I2CUART mode\n"); + goto out; + } + + } else if (mode == protocol->demo_mode || mode == protocol->debug_mode) { + cmd[0] = protocol->cmd_mode_ctrl; + cmd[1] = mode; + + ipio_info("Switch to Demo/Debug mode, cmd = 0x%x, b1 = 0x%x\n", cmd[0], cmd[1]); + + if ((core_write(core_config->slave_i2c_addr, cmd, 2)) < 0) { + ipio_err("Failed to switch Demo/Debug mode\n"); + goto out; + } + + core_fr->actual_fw_mode = mode; + + } else if (mode == protocol->test_mode) { + cmd[0] = protocol->cmd_mode_ctrl; + cmd[1] = mode; + + ipio_info("Switch to Test mode, cmd = 0x%x, b1 = 0x%x\n", cmd[0], cmd[1]); + + if ((core_write(core_config->slave_i2c_addr, cmd, 2)) < 0) { + ipio_err("Failed to switch Test mode\n"); + goto out; + } + + cmd[0] = 0xFE; + + /* Read MP Test information to ensure if fw supports test mode. */ + core_write(core_config->slave_i2c_addr, cmd, 1); + mdelay(10); + core_read(core_config->slave_i2c_addr, mp_code, codeLength); + + for (i = 0 ; i < codeLength - 1; i++) + checksum += mp_code[i]; + + if ((-checksum & 0xFF) != mp_code[codeLength - 1]) { + ipio_info("checksume error (0x%x), FW doesn't support test mode.\n", + (-checksum & 0XFF)); + goto out; + } + + /* FW enter to Test Mode */ + #if (INTERFACE == SPI_INTERFACE) + core_fr->actual_fw_mode = mode; + #endif + if (core_mp_move_code() == 0) + core_fr->actual_fw_mode = mode; + + } else { + ipio_err("Unknown firmware mode: %x\n", mode); + } + } else { + ipio_err("Wrong the major version of protocol, 0x%x\n", protocol->major); + } + +out: + ilitek_platform_enable_irq(); +} +EXPORT_SYMBOL(core_fr_mode_control); + +/** + * Calculate the length with different modes according to the format of protocol 5.0 + * + * We compute the length before receiving its packet. If the length is differnet between + * firmware and the number we calculated, in this case I just print an error to inform users + * and still send up to users. + */ +static uint16_t calc_packet_length(void) +{ + uint16_t xch = 0, ych = 0, stx = 0, srx = 0; + /* FIXME: self_key not defined by firmware yet */ + uint16_t self_key = 2; + uint16_t rlen = 0; + + if (protocol->major == 0x5) { + if (!ERR_ALLOC_MEM(core_config->tp_info)) { + xch = core_config->tp_info->nXChannelNum; + ych = core_config->tp_info->nYChannelNum; + stx = core_config->tp_info->self_tx_channel_num; + srx = core_config->tp_info->self_rx_channel_num; + } + + ipio_debug(DEBUG_FINGER_REPORT, "firmware mode : 0x%x\n", core_fr->actual_fw_mode); + + if (protocol->demo_mode == core_fr->actual_fw_mode) { + rlen = protocol->demo_len; + } else if (protocol->test_mode == core_fr->actual_fw_mode) { + if (ERR_ALLOC_MEM(core_config->tp_info)) { + rlen = protocol->test_len; + } else { + rlen = (2 * xch * ych) + (stx * 2) + (srx * 2) + 2 * self_key + 1; + rlen += 1; + } + } else if (protocol->debug_mode == core_fr->actual_fw_mode) { + if (ERR_ALLOC_MEM(core_config->tp_info)) { + rlen = protocol->debug_len; + } else { + rlen = (2 * xch * ych) + (stx * 2) + (srx * 2) + 2 * self_key + (8 * 2) + 1; + rlen += 35; + } + } else if (protocol->gesture_mode == core_fr->actual_fw_mode) { + if (core_gesture->mode == GESTURE_NORMAL_MODE) + rlen = GESTURE_MORMAL_LENGTH; + else + rlen = GESTURE_INFO_LENGTH; + ipio_debug(DEBUG_FINGER_REPORT, "rlen = %d\n", rlen); + } else { + ipio_err("Unknown firmware mode : %d\n", core_fr->actual_fw_mode); + rlen = 0; + } + } else { + ipio_err("Wrong the major version of protocol, 0x%x\n", protocol->major); + return ERROR; + } + + ipio_debug(DEBUG_FINGER_REPORT, "rlen = %d\n", rlen); + return rlen; +} + +/** + * The table is used to handle calling functions that deal with packets of finger report. + * The callback function might be different of what a protocol is used on a chip. + * + * It's possible to have the different protocol according to customer's requirement on the same + * touch ic with customised firmware, so I don't have to identify which of the ic has been used; instead, + * the version of protocol should match its parsing pattern. + */ +typedef struct { + uint8_t protocol_marjor_ver; + uint8_t protocol_minor_ver; + int (*finger_report)(void); +} fr_hashtable; + +fr_hashtable fr_t[] = { + {0x3, 0x2, finger_report_ver_3_2}, + {0x5, 0x0, finger_report_ver_5_0}, + {0x5, 0x1, finger_report_ver_5_0}, +}; + +/** + * The function is an entry for the work queue registered by ISR activates. + * + * Here will allocate the size of packet depending on what the current protocol + * is used on its firmware. + */ +void core_fr_handler(void) +{ + int i = 0; + uint8_t *tdata = NULL; + + if (core_fr->isEnableFR) { + g_total_len = calc_packet_length(); + if (g_total_len) { + g_fr_node = kmalloc(sizeof(*g_fr_node), GFP_ATOMIC); + if (ERR_ALLOC_MEM(g_fr_node)) { + ipio_err("Failed to allocate g_fr_node memory %ld\n", PTR_ERR(g_fr_node)); + goto out; + } + + g_fr_node->data = kcalloc(g_total_len, sizeof(uint8_t), GFP_ATOMIC); + if (ERR_ALLOC_MEM(g_fr_node->data)) { + ipio_err("Failed to allocate g_fr_node memory %ld\n", PTR_ERR(g_fr_node->data)); + goto out; + } + + g_fr_node->len = g_total_len; + memset(g_fr_node->data, 0xFF, (uint8_t) sizeof(uint8_t) * g_total_len); + + while (i < ARRAY_SIZE(fr_t)) { + if (protocol->major == fr_t[i].protocol_marjor_ver) { + mutex_lock(&ipd->plat_mutex); + fr_t[i].finger_report(); + mutex_unlock(&ipd->plat_mutex); + + /* 2048 is referred to the defination by user */ + if (g_total_len < 2048) { + tdata = kmalloc(g_total_len, GFP_ATOMIC); + if (ERR_ALLOC_MEM(tdata)) { + ipio_err("Failed to allocate g_fr_node memory %ld\n", + PTR_ERR(tdata)); + goto out; + } + + memcpy(tdata, g_fr_node->data, g_fr_node->len); + /* merge uart data if it's at i2cuart mode */ + if (g_fr_uart != NULL) + memcpy(tdata + g_fr_node->len, g_fr_uart->data, g_fr_uart->len); + } else { + ipio_err("total length (%d) is too long than user can handle\n", + g_total_len); + goto out; + } + + if (core_fr->isEnableNetlink) + netlink_reply_msg(tdata, g_total_len); + + if (ipd->debug_node_open) { + mutex_lock(&ipd->ilitek_debug_mutex); + memset(ipd->debug_buf[ipd->debug_data_frame], 0x00, + (uint8_t) sizeof(uint8_t) * 2048); + memcpy(ipd->debug_buf[ipd->debug_data_frame], tdata, g_total_len); + ipd->debug_data_frame++; + if (ipd->debug_data_frame > 1) { + ipio_info("ipd->debug_data_frame = %d\n", ipd->debug_data_frame); + } + if (ipd->debug_data_frame > 1023) { + ipio_err("ipd->debug_data_frame = %d > 1024\n", + ipd->debug_data_frame); + ipd->debug_data_frame = 1023; + } + mutex_unlock(&ipd->ilitek_debug_mutex); + wake_up(&(ipd->inq)); + } + + if(ipd->debug_data_start_flag && (ipd->debug_data_frame < 1024)) { + mutex_lock(&ipd->ilitek_debug_mutex); + memset(ipd->debug_buf[ipd->debug_data_frame], 0x00, + (uint8_t) sizeof(uint8_t) * 2048); + if (g_total_len > 2048) { + memcpy(ipd->debug_buf[ipd->debug_data_frame], tdata, 2048); + } else { + memcpy(ipd->debug_buf[ipd->debug_data_frame], tdata, g_total_len); + } + + ipd->debug_data_frame ++; + + mutex_unlock(&ipd->ilitek_debug_mutex); + wake_up(&(ipd->inq)); + } + break; + } + i++; + } + + if (i >= ARRAY_SIZE(fr_t)) + ipio_err("Can't find any callback functions to handle INT event\n"); + } else { + ipio_err("Wrong the length of packet\n"); + } + } else { + ipio_err("The figner report was disabled\n"); + return; + } + ilitek_platform_enable_irq(); + +out: + ipio_kfree((void **)&tdata); + + if (g_fr_node != NULL) { + ipio_kfree((void **)&g_fr_node->data); + ipio_kfree((void **)&g_fr_node); + } + + if (g_fr_uart != NULL) { + ipio_kfree((void **)&g_fr_uart->data); + ipio_kfree((void **)&g_fr_uart); + } + + g_total_len = 0; + ipio_debug(DEBUG_IRQ, "handle INT done\n\n"); +} +EXPORT_SYMBOL(core_fr_handler); + +void core_fr_input_set_param(struct input_dev *input_device) +{ + int max_x = 0, max_y = 0, min_x = 0, min_y = 0; + int max_tp = 0; + + core_fr->input_device = input_device; + + /* set the supported event type for input device */ + set_bit(EV_ABS, core_fr->input_device->evbit); + set_bit(EV_SYN, core_fr->input_device->evbit); + set_bit(EV_KEY, core_fr->input_device->evbit); + set_bit(BTN_TOUCH, core_fr->input_device->keybit); + set_bit(BTN_TOOL_FINGER, core_fr->input_device->keybit); + set_bit(INPUT_PROP_DIRECT, core_fr->input_device->propbit); + + if (core_fr->isSetResolution) { + max_x = core_config->tp_info->nMaxX; + max_y = core_config->tp_info->nMaxY; + min_x = core_config->tp_info->nMinX; + min_y = core_config->tp_info->nMinY; + max_tp = core_config->tp_info->nMaxTouchNum; + } else { + max_x = ipd->x_max; + max_y = ipd->y_max; + min_x = ipd->x_min; + min_y = ipd->y_min; + max_tp = ipd->MAX_TOUCH_NUM; + } + + ipio_info("input resolution : max_x = %d, max_y = %d, min_x = %d, min_y = %d\n", max_x, max_y, min_x, min_y); + ipio_info("input touch number: max_tp = %d\n", max_tp); + + input_set_abs_params(core_fr->input_device, ABS_MT_POSITION_X, min_x, max_x - 1, 0, 0); + input_set_abs_params(core_fr->input_device, ABS_MT_POSITION_Y, min_y, max_y - 1, 0, 0); + + input_set_abs_params(core_fr->input_device, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(core_fr->input_device, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); + + if (core_fr->isEnablePressure) + input_set_abs_params(core_fr->input_device, ABS_MT_PRESSURE, 0, 255, 0, 0); + + if (ipd->MT_B_TYPE) { + #if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE + input_mt_init_slots(core_fr->input_device, max_tp, INPUT_MT_DIRECT); + #else + input_mt_init_slots(core_fr->input_device, max_tp); + #endif /* LINUX_VERSION_CODE */ + } else{ + input_set_abs_params(core_fr->input_device, ABS_MT_TRACKING_ID, 0, max_tp, 0, 0); + } /* MT_B_TYPE */ + + /* Set up virtual key with gesture code */ + core_gesture_set_key(core_fr); +} +EXPORT_SYMBOL(core_fr_input_set_param); + +int core_fr_init(void) +{ + int i = 0; + + core_fr = kzalloc(sizeof(*core_fr), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_fr)) { + ipio_err("Failed to allocate core_fr mem, %ld\n", PTR_ERR(core_fr)); + core_fr_remove(); + return -ENOMEM; + } + + for (i = 0 ; i < ARRAY_SIZE(ipio_chip_list); i++) { + if (ipio_chip_list[i] == ipd->chip_id) { + core_fr->isEnableFR = true; + core_fr->isEnableNetlink = false; + core_fr->isEnablePressure = false; + core_fr->isSetResolution = false; + core_fr->actual_fw_mode = protocol->demo_mode; + return 0; + } + } + + ipio_err("Can't find this chip in support list\n"); + return 0; +} +EXPORT_SYMBOL(core_fr_init); + +void core_fr_remove(void) +{ + ipio_info("Remove core-FingerReport members\n"); + ipio_kfree((void **)&core_fr); +} +EXPORT_SYMBOL(core_fr_remove); diff --git a/drivers/input/touchscreen/ilitek_mmi/core/finger_report.h b/drivers/input/touchscreen/ilitek_mmi/core/finger_report.h new file mode 100644 index 000000000000..d6c7f1414d3a --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/finger_report.h @@ -0,0 +1,67 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __FINGER_REPORT_H +#define __FINGER_REPORT_H + +struct core_fr_data { + struct input_dev *input_device; + + /* the default of finger report is enabled */ + bool isEnableFR; + /* used to send finger report packet to user psace */ + bool isEnableNetlink; + /* allow input dev to report the value of physical touch */ + bool isEnablePressure; + /* get screen resloution from fw if it's true */ + bool isSetResolution; + /* used to change I2C Uart Mode when fw mode is in this mode */ + uint8_t i2cuart_mode; + + /* current firmware mode in driver */ + uint16_t actual_fw_mode; + + /* mutual firmware info */ + uint16_t log_packet_length; + uint8_t log_packet_header; + uint8_t type; + uint8_t Mx; + uint8_t My; + uint8_t Sd; + uint8_t Ss; +}; + +extern struct core_fr_data *core_fr; + +extern uint8_t core_fr_calc_checksum(uint8_t *pMsg, uint32_t nLength); +extern void core_fr_touch_press(int32_t x, int32_t y, uint32_t pressure +, int32_t id); +extern void core_fr_touch_release(int32_t x, int32_t y, int32_t id); +extern void core_fr_mode_control(uint8_t *from_user); +extern void core_fr_handler(void); +extern void core_fr_input_set_param(struct input_dev *input_device); +extern int core_fr_init(void); +extern void core_fr_remove(void); + +#endif /* __FINGER_REPORT_H */ diff --git a/drivers/input/touchscreen/ilitek_mmi/core/firmware.c b/drivers/input/touchscreen/ilitek_mmi/core/firmware.c new file mode 100644 index 000000000000..e59b950178b8 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/firmware.c @@ -0,0 +1,1741 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../common.h" +#include "../platform.h" +#include "config.h" +#include "i2c.h" +#include "firmware.h" +#include "flash.h" +#include "protocol.h" +#include "finger_report.h" +#include "gesture.h" +#include "mp_test.h" + +#if defined(BOOT_FW_UPGRADE) || defined(HOST_DOWNLOAD) +#include "ilitek_fw.h" +#endif + +/* + * the size of two arrays is different depending on + * which of methods to upgrade firmware you choose for. + */ +uint8_t *flash_fw; + +#ifdef HOST_DOWNLOAD +uint8_t ap_fw[MAX_AP_FIRMWARE_SIZE] = { 0 }; +uint8_t dlm_fw[MAX_DLM_FIRMWARE_SIZE] = { 0 }; +uint8_t mp_fw[MAX_MP_FIRMWARE_SIZE] = { 0 }; +uint8_t gesture_fw[MAX_GESTURE_FIRMWARE_SIZE] = { 0 }; +#else +uint8_t iram_fw[MAX_IRAM_FIRMWARE_SIZE] = { 0 }; +#endif + +#define CRC_ONESET(X, Y) ({Y = (*(X+0) << 24) | (*(X+1) << 16) | (*(X+2) << 8) | (*(X+3));}) +/* the length of array in each sector */ +int g_section_len; +int g_total_sector; + +#ifdef BOOT_FW_UPGRADE +/* The addr of block reserved for customers */ +int g_start_resrv = 0x1C000; +int g_end_resrv = 0x1CFFF; +#endif + +struct flash_sector { + uint32_t ss_addr; + uint32_t se_addr; + uint32_t checksum; + uint32_t crc32; + uint32_t dlength; + bool data_flag; + bool inside_block; +}; + +struct flash_block_info { + uint32_t start_addr; + uint32_t end_addr; + uint32_t hex_crc; + uint32_t block_crc; +}; + +struct flash_sector *g_flash_sector; +struct flash_block_info g_flash_block_info[6]; +struct core_firmware_data *core_firmware; + +static uint32_t HexToDec(char *pHex, int32_t nLength) +{ + uint32_t nRetVal = 0, nTemp = 0, i; + int32_t nShift = (nLength - 1) * 4; + + for (i = 0 ; i < nLength; nShift -= 4, i++) { + if ((pHex[i] >= '0') && (pHex[i] <= '9')) { + nTemp = pHex[i] - '0'; + } else if ((pHex[i] >= 'a') && (pHex[i] <= 'f')) { + nTemp = (pHex[i] - 'a') + 10; + } else if ((pHex[i] >= 'A') && (pHex[i] <= 'F')) { + nTemp = (pHex[i] - 'A') + 10; + } else { + return ERROR; + } + + nRetVal |= (nTemp << nShift); + } + + return nRetVal; +} + +static uint32_t calc_crc32(uint32_t start_addr, uint32_t end_addr, uint8_t *data) +{ + int i, j; + uint32_t CRC_POLY = 0x04C11DB7; + uint32_t ReturnCRC = 0xFFFFFFFF; + uint32_t len = start_addr + end_addr; + + for (i = start_addr; i < len ; i++) { + ReturnCRC ^= (data[i] << 24); + + for (j = 0; j < 8; j++) { + if ((ReturnCRC & 0x80000000) != 0) { + ReturnCRC = ReturnCRC << 1 ^ CRC_POLY; + } else { + ReturnCRC = ReturnCRC << 1; + } + } + } + + return ReturnCRC; +} + +static uint32_t tddi_check_data(uint32_t start_addr, uint32_t end_addr) +{ + int timer = 500; + uint32_t busy = 0; + uint32_t write_len = 0; + uint32_t iram_check = 0; + uint32_t id = core_config->chip_id; + uint32_t type = core_config->chip_type; + + write_len = end_addr; + + ipio_debug(DEBUG_FIRMWARE, "start = 0x%x , write_len = 0x%x, max_count = %x\n", + start_addr, end_addr, core_firmware->max_count); + + if (write_len > core_firmware->max_count) { + ipio_err("The length (%x) written to firmware is greater than max count (%x)\n", + write_len, core_firmware->max_count); + goto out; + } + + core_config_ice_mode_write(0x041000, 0x0, 1); /* CS low */ + core_config_ice_mode_write(0x041004, 0x66aa55, 3); /* Key */ + + core_config_ice_mode_write(0x041008, 0x3b, 1); + core_config_ice_mode_write(0x041008, (start_addr & 0xFF0000) >> 16, 1); + core_config_ice_mode_write(0x041008, (start_addr & 0x00FF00) >> 8, 1); + core_config_ice_mode_write(0x041008, (start_addr & 0x0000FF), 1); + + core_config_ice_mode_write(0x041003, 0x01, 1); /* Enable Dio_Rx_dual */ + core_config_ice_mode_write(0x041008, 0xFF, 1); /* Dummy */ + + /* Set Receive count */ + if (core_firmware->max_count == 0xFFFF) + core_config_ice_mode_write(0x04100C, write_len, 2); + else if (core_firmware->max_count == 0x1FFFF) + core_config_ice_mode_write(0x04100C, write_len, 3); + + if (id == CHIP_TYPE_ILI9881 && type == ILI9881_TYPE_F) { + /* Checksum_En */ + core_config_ice_mode_write(0x041014, 0x10000, 3); + } else if (id == CHIP_TYPE_ILI9881 && type == ILI9881_TYPE_H) { + /* Clear Int Flag */ + core_config_ice_mode_write(0x048007, 0x02, 1); + + /* Checksum_En */ + core_config_ice_mode_write(0x041016, 0x00, 1); + core_config_ice_mode_write(0x041016, 0x01, 1); + } + + /* Start to receive */ + core_config_ice_mode_write(0x041010, 0xFF, 1); + + while (timer > 0) { + + mdelay(1); + + if (id == CHIP_TYPE_ILI9881 && type == ILI9881_TYPE_F) + busy = core_config_read_write_onebyte(0x041014); + else if (id == CHIP_TYPE_ILI9881 && type == ILI9881_TYPE_H) { + busy = core_config_read_write_onebyte(0x048007); + busy = busy >> 1; + } else { + ipio_err("Unknow chip type\n"); + break; + } + + if ((busy & 0x01) == 0x01) + break; + + timer--; + } + + core_config_ice_mode_write(0x041000, 0x1, 1); /* CS high */ + + if (timer >= 0) { + /* Disable dio_Rx_dual */ + core_config_ice_mode_write(0x041003, 0x0, 1); + iram_check = core_firmware->isCRC ? core_config_ice_mode_read(0x4101C) : core_config_ice_mode_read(0x041018); + } else { + ipio_err("TIME OUT\n"); + goto out; + } + + return iram_check; + +out: + ipio_err("Failed to read Checksum/CRC from IC\n"); + return ERROR; + +} + +static void calc_verify_data(uint32_t sa, uint32_t se, uint32_t *check) +{ + uint32_t i = 0; + uint32_t tmp_ck = 0, tmp_crc = 0; + + if (core_firmware->isCRC) { + tmp_crc = calc_crc32(sa, se, flash_fw); + *check = tmp_crc; + } else { + for (i = sa; i < (sa + se); i++) + tmp_ck = tmp_ck + flash_fw[i]; + + *check = tmp_ck; + } +} + +static int do_check(uint32_t start, uint32_t len) +{ + int res = 0; + uint32_t vd = 0, lc = 0; + + calc_verify_data(start, len, &lc); + vd = tddi_check_data(start, len); + res = CHECK_EQUAL(vd, lc); + + ipio_info("%s (%x) : (%x)\n", (res < 0 ? "Invalid !" : "Correct !"), vd, lc); + + return res; +} + +static int verify_flash_data(void) +{ + int i = 0, res = 0, len = 0; + int fps = flashtab->sector; + uint32_t ss = 0x0; + + /* check chip type with its max count */ + if (core_config->chip_id == CHIP_TYPE_ILI7807 && core_config->chip_type == ILI7807_TYPE_H) { + core_firmware->max_count = 0x1FFFF; + core_firmware->isCRC = true; + } + + for (i = 0 ; i < g_section_len + 1; i++) { + if (g_flash_sector[i].data_flag) { + if (ss > g_flash_sector[i].ss_addr || len == 0) + ss = g_flash_sector[i].ss_addr; + + len = len + g_flash_sector[i].dlength; + + /* if larger than max count, then committing data to check */ + if (len >= (core_firmware->max_count - fps)) { + res = do_check(ss, len); + if (res < 0) + goto out; + + ss = g_flash_sector[i].ss_addr; + len = 0; + } + } else { + /* split flash sector and commit the last data to fw */ + if (len != 0) { + res = do_check(ss, len); + if (res < 0) + goto out; + + ss = g_flash_sector[i].ss_addr; + len = 0; + } + } + } + + /* it might be lower than the size of sector if calc the last array. */ + if (len != 0 && res != -1) + res = do_check(ss, core_firmware->end_addr - ss); + +out: + return res; +} + +static int do_program_flash(uint32_t start_addr) +{ + int res = 0; + uint32_t k; + uint8_t buf[512] = { 0 }; + + res = core_flash_write_enable(); + if (res < 0) + goto out; + + core_config_ice_mode_write(0x041000, 0x0, 1); /* CS low */ + core_config_ice_mode_write(0x041004, 0x66aa55, 3); /* Key */ + + core_config_ice_mode_write(0x041008, 0x02, 1); + core_config_ice_mode_write(0x041008, (start_addr & 0xFF0000) >> 16, 1); + core_config_ice_mode_write(0x041008, (start_addr & 0x00FF00) >> 8, 1); + core_config_ice_mode_write(0x041008, (start_addr & 0x0000FF), 1); + + buf[0] = 0x25; + buf[3] = 0x04; + buf[2] = 0x10; + buf[1] = 0x08; + + for (k = 0 ; k < flashtab->program_page; k++) { + if (start_addr + k <= core_firmware->end_addr) + buf[4 + k] = flash_fw[start_addr + k]; + else + buf[4 + k] = 0xFF; + } + + if (core_write(core_config->slave_i2c_addr, buf, flashtab->program_page + 4) < 0) { + ipio_err("Failed to write data at start_addr = 0x%X, k = 0x%X, addr = 0x%x\n", + start_addr, k, start_addr + k); + res = -EIO; + goto out; + } + + core_config_ice_mode_write(0x041000, 0x1, 1); /* CS high */ + + res = core_flash_poll_busy(); + if (res < 0) + goto out; + + core_firmware->update_status = (start_addr * 101) / core_firmware->end_addr; + + /* holding the status until finish this upgrade. */ + if (core_firmware->update_status > 90) + core_firmware->update_status = 90; + + /* Don't use ipio_info to print log because it needs to be kpet in the same line */ + ipio_debug(DEBUG_FIRMWARE, "%cUpgrading firmware ... start_addr = 0x%x, %02d%c", 0x0D, + start_addr, core_firmware->update_status, '%'); + +out: + return res; +} + +static int flash_program_sector(void) +{ + int i, j, res = 0; + + for (i = 0 ; i < g_section_len + 1; i++) { + /* + * If running the boot stage, fw will only be upgrade data with the flag of block, + * otherwise data with the flag itself will be programed. + */ + if (core_firmware->isboot) { + if (!g_flash_sector[i].inside_block) + continue; + } else { + if (!g_flash_sector[i].data_flag) + continue; + } + + /* programming flash by its page size */ + for (j = g_flash_sector[i].ss_addr; j < g_flash_sector[i].se_addr; j += flashtab->program_page) { + if (j > core_firmware->end_addr) + goto out; + + res = do_program_flash(j); + if (res < 0) + goto out; + } + } + +out: + return res; +} + +static int do_erase_flash(uint32_t start_addr) +{ + int res = 0; + uint32_t temp_buf = 0; + + res = core_flash_write_enable(); + if (res < 0) { + ipio_err("Failed to config write enable\n"); + goto out; + } + + core_config_ice_mode_write(0x041000, 0x0, 1); /* CS low */ + core_config_ice_mode_write(0x041004, 0x66aa55, 3); /* Key */ + + core_config_ice_mode_write(0x041008, 0x20, 1); + core_config_ice_mode_write(0x041008, (start_addr & 0xFF0000) >> 16, 1); + core_config_ice_mode_write(0x041008, (start_addr & 0x00FF00) >> 8, 1); + core_config_ice_mode_write(0x041008, (start_addr & 0x0000FF), 1); + + core_config_ice_mode_write(0x041000, 0x1, 1); /* CS high */ + + mdelay(1); + + res = core_flash_poll_busy(); + if (res < 0) + goto out; + + core_config_ice_mode_write(0x041000, 0x0, 1); /* CS low */ + core_config_ice_mode_write(0x041004, 0x66aa55, 3); /* Key */ + + core_config_ice_mode_write(0x041008, 0x3, 1); + core_config_ice_mode_write(0x041008, (start_addr & 0xFF0000) >> 16, 1); + core_config_ice_mode_write(0x041008, (start_addr & 0x00FF00) >> 8, 1); + core_config_ice_mode_write(0x041008, (start_addr & 0x0000FF), 1); + core_config_ice_mode_write(0x041008, 0xFF, 1); + + temp_buf = core_config_read_write_onebyte(0x041010); + if (temp_buf != 0xFF) { + ipio_err("Failed to erase data(0x%x) at 0x%x\n", temp_buf, start_addr); + res = -EINVAL; + goto out; + } + + core_config_ice_mode_write(0x041000, 0x1, 1); /* CS high */ + + ipio_debug(DEBUG_FIRMWARE, "Earsing data at start addr: %x\n", start_addr); + +out: + return res; +} + +static int flash_erase_sector(void) +{ + int i, res = 0; + + for (i = 0 ; i < g_total_sector; i++) { + if (core_firmware->isboot) { + if (!g_flash_sector[i].inside_block) + continue; + } else { + if (!g_flash_sector[i].data_flag && !g_flash_sector[i].inside_block) + continue; + } + + res = do_erase_flash(g_flash_sector[i].ss_addr); + if (res < 0) + goto out; + } + +out: + return res; +} + +static int tddi_read_flash(uint32_t start, uint32_t end, uint8_t *data, int dlen) +{ + uint32_t i, cont = 0; + + ipio_info("start = %x , end = %x\n", start, end); + if (data == NULL) { + ipio_err("data is null, read failed\n"); + return -1; + } + + if (end - start > dlen) { + ipio_err("the length (%d) reading crc is over than dlen(%d)\n", end - start, dlen); + return -1; + } + + core_config_ice_mode_write(0x041000, 0x0, 1); /* CS low */ + core_config_ice_mode_write(0x041004, 0x66aa55, 3); /* Key */ + core_config_ice_mode_write(0x041008, 0x03, 1); + + core_config_ice_mode_write(0x041008, (start & 0xFF0000) >> 16, 1); + core_config_ice_mode_write(0x041008, (start & 0x00FF00) >> 8, 1); + core_config_ice_mode_write(0x041008, (start & 0x0000FF), 1); + + for (i = start; i <= end; i++) { + core_config_ice_mode_write(0x041008, 0xFF, 1); /* Dummy */ + + data[cont] = core_config_read_write_onebyte(0x41010); + ipio_debug(DEBUG_BOOT,"data[%d] = %x\n", cont, data[cont]); + cont++; + } + + core_config_ice_mode_write(0x041000, 0x1, 1); /* CS high */ + return 0; +} + +int tddi_check_fw_upgrade(void) +{ + int ret = NO_NEED_UPDATE; + int i, crc_byte_len = 4; + uint8_t flash_crc[4] = {0}; + uint32_t start_addr = 0, end_addr = 0, flash_crc_cb; + + ilitek_platform_disable_irq(); + + ret = core_config_ice_mode_enable(); + if (ret < 0) { + ipio_err("Failed to enable ICE mode\n"); + goto out; + } + + mdelay(25); + + if (core_config_set_watch_dog(false) < 0) { + ipio_err("Failed to disable watch dog\n"); + ret = -EINVAL; + goto out; + } + + g_flash_block_info[0].start_addr = AP_STAR_ADDR; + g_flash_block_info[0].end_addr = AP_END_ADDR; + g_flash_block_info[1].start_addr = DATA_STAR_ADDR; + g_flash_block_info[1].end_addr = DATA_END_ADDR; + g_flash_block_info[2].start_addr = MP_STAR_ADDR; + g_flash_block_info[2].end_addr = MP_END_ADDR; + /* Check Flash CRC and HW CRC */ + for(i = 0; i <3; i++) { + start_addr = g_flash_block_info[i].start_addr; + end_addr = g_flash_block_info[i].end_addr; + + /* Invaild end address */ + if (end_addr == 0) + continue; + + ret = tddi_read_flash(end_addr - crc_byte_len + 1, end_addr, flash_crc, sizeof(flash_crc)); + if (ret < 0) { + ipio_info("Read Flash failed\n"); + ret = CHECK_FW_FAIL; + goto out; + } + CRC_ONESET(flash_crc, flash_crc_cb); + + if (g_flash_block_info[i].block_crc == 0) + g_flash_block_info[i].block_crc = tddi_check_data(start_addr, end_addr - start_addr - crc_byte_len + 1); + + ipio_info("HW CRC = 0x%06x, Flash CRC = 0x%06x\n", g_flash_block_info[i].block_crc, flash_crc_cb); + + /* Compare Hex to HW's CRC directly instead of fw version */ + if(flash_crc_cb != g_flash_block_info[i].block_crc) + { + ret = NEED_UPDATE; + goto out; + } + + memset(flash_crc, 0, sizeof(flash_crc)); + } + +out: + /* We do have to reset chip in order to move new code from flash to iram. */ + ipio_info("Doing Soft Reset ..\n"); + core_config_ic_reset(); + + return ret; +} + +#ifndef HOST_DOWNLOAD +static int iram_upgrade(void) +{ + int i, j, res = 0; + uint8_t buf[512]; + int upl = flashtab->program_page; + + /* doing reset for erasing iram data before upgrade it. */ + /*ilitek_platform_tp_hw_reset(true);*/ + /* Soft reset */ + core_config_ice_mode_enable(); + core_config_set_watch_dog(false); + mdelay(10); + core_config_ic_reset(); + + mdelay(1); + + ipio_info("Upgrade firmware written data into IRAM directly\n"); + + res = core_config_ice_mode_enable(); + if (res < 0) { + ipio_err("Failed to enter ICE mode, res = %d\n", res); + return res; + } + + mdelay(20); + + core_config_set_watch_dog(false); + + ipio_debug(DEBUG_FIRMWARE, "nStartAddr = 0x%06X, nEndAddr = 0x%06X, nChecksum = 0x%06X\n", + core_firmware->start_addr, core_firmware->end_addr, core_firmware->checksum); + + /* write hex to the addr of iram */ + ipio_info("Writing data into IRAM ...\n"); + for (i = core_firmware->start_addr; i < core_firmware->end_addr; i += upl) { + if ((i + 256) > core_firmware->end_addr) { + upl = core_firmware->end_addr % upl; + } + + buf[0] = 0x25; + buf[3] = (char)((i & 0x00FF0000) >> 16); + buf[2] = (char)((i & 0x0000FF00) >> 8); + buf[1] = (char)((i & 0x000000FF)); + + for (j = 0 ; j < upl; j++) + buf[4 + j] = iram_fw[i + j]; + + if (core_write(core_config->slave_i2c_addr, buf, upl + 4)) { + ipio_err("Failed to write data via i2c, address = 0x%X, start_addr = 0x%X, end_addr = 0x%X\n", + (int)i, (int)core_firmware->start_addr, (int)core_firmware->end_addr); + res = -EIO; + return res; + } + + core_firmware->update_status = (i * 101) / core_firmware->end_addr; + printk("%cupgrade firmware(ap code), %02d%c", 0x0D, core_firmware->update_status, '%'); + + mdelay(3); + } + + /* ice mode code reset */ + ipio_info("Doing code reset ...\n"); + core_config_ice_mode_write(0x40040, 0xAE, 1); + core_config_ice_mode_write(0x40040, 0x00, 1); + + mdelay(10); + + core_config_set_watch_dog(true); + + core_config_ice_mode_disable(); + + /*TODO: check iram status */ + + return res; +} +#endif + +#ifdef HOST_DOWNLOAD +static int read_download(uint32_t start, uint32_t size, uint8_t *r_buf, uint32_t r_len) +{ + int addr = 0, i = 0; + uint32_t end = start + size; + uint8_t *buf = NULL; + + buf = kmalloc(sizeof(uint8_t) * size + 4, GFP_KERNEL); + if (ERR_ALLOC_MEM(buf)) { + ipio_err("Failed to allocate a buffer to be read, %ld\n", PTR_ERR(buf)); + return -ENOMEM; + } + + memset(buf, 0xFF, (int)sizeof(uint8_t) * size + 4); + + for (addr = start, i = 0 ; addr < end; i += r_len, addr += r_len) { + if ((addr + r_len) > end) { + r_len = end % r_len; + } + + buf[0] = 0x25; + buf[3] = (char)((addr & 0x00FF0000) >> 16); + buf[2] = (char)((addr & 0x0000FF00) >> 8); + buf[1] = (char)((addr & 0x000000FF)); + + if (core_write(core_config->slave_i2c_addr, buf, 4)) { + ipio_err("Failed to write data via SPI in host download\n"); + return -EIO; + } + + if (core_read(core_config->slave_i2c_addr, buf, r_len)) { + ipio_err("Failed to Read data via SPI in host download\n"); + return -EIO; + } + + memcpy(r_buf + i, buf, r_len); + } + + ipio_kfree((void **)&buf); + return 0; +} + +static int write_download(uint32_t start, uint32_t size, uint8_t *w_buf, uint32_t w_len) +{ + int addr = 0, i = 0, update_status = 0, j = 0; + uint32_t end = start + size; + uint8_t *buf = NULL; + + buf = (uint8_t *)kmalloc(sizeof(uint8_t) * size + 4, GFP_KERNEL); + if (ERR_ALLOC_MEM(buf)) { + ipio_err("Failed to allocate a buffer to be read, %ld\n", PTR_ERR(buf)); + return -ENOMEM; + } + + memset(buf, 0xFF, (int)sizeof(uint8_t) * size + 4); + + for (addr = start, i = 0 ; addr < end; addr += w_len, i += w_len) { + if ((addr + w_len) > end) { + w_len = end % w_len; + } + + buf[0] = 0x25; + buf[3] = (char)((addr & 0x00FF0000) >> 16); + buf[2] = (char)((addr & 0x0000FF00) >> 8); + buf[1] = (char)((addr & 0x000000FF)); + + for (j = 0 ; j < w_len ; j++) + buf[4 + j] = w_buf[i + j]; + + if (core_write(core_config->slave_i2c_addr, buf, w_len + 4)) { + ipio_err("Failed to write data via SPI in host download (%x)\n", w_len); + return -EIO; + } + + update_status = (i * 101) / end; + printk("%cupgrade firmware(mp code), %02d%c", 0x0D, update_status, '%'); + } + + ipio_kfree((void **)&buf); + return 0; +} + +static int host_download_dma_check(int block) +{ + int count = 50; + uint8_t ap_block = 0, dlm_block = 1; + uint32_t start_addr = 0, block_size = 0; + uint32_t busy = 0; + + if (block == ap_block) { + start_addr = 0; + block_size = MAX_AP_FIRMWARE_SIZE - 0x4; + } else if (block == dlm_block) { + start_addr = DLM_START_ADDRESS; + block_size = MAX_DLM_FIRMWARE_SIZE; + } + + /* dma1 src1 adress */ + core_config_ice_mode_write(0x072104, start_addr, 4); + /* dma1 src1 format */ + core_config_ice_mode_write(0x072108, 0x80000001, 4); + /* dma1 dest address */ + core_config_ice_mode_write(0x072114, 0x00030000, 4); + /* dma1 dest format */ + core_config_ice_mode_write(0x072118, 0x80000000, 4); + /* Block size*/ + core_config_ice_mode_write(0x07211C, block_size, 4); + /* crc off */ + core_config_ice_mode_write(0x041014, 0x00000000, 4); + /* dma crc */ + core_config_ice_mode_write(0x041048, 0x00000001, 4); + /* crc on */ + core_config_ice_mode_write(0x041014, 0x00010000, 4); + /* Dma1 stop */ + core_config_ice_mode_write(0x072100, 0x00000000, 4); + /* clr int */ + core_config_ice_mode_write(0x048006, 0x1, 1); + /* Dma1 start */ + core_config_ice_mode_write(0x072100, 0x01000000, 4); + + /* Polling BIT0 */ + while (count > 0) { + mdelay(1); + busy = core_config_read_write_onebyte(0x048006); + + if (busy == 1) + break; + + count--; + } + + if (count <= 0) { + ipio_err("BIT0 is busy\n"); + return ERROR; + } + + return core_config_ice_mode_read(0x04101C); +} + +int tddi_host_download(bool mode) +{ + int res = 0, ap_crc, ap_dma, dlm_crc, dlm_dma, method; + uint8_t *buf = NULL, *read_ap_buf = NULL, *read_dlm_buf = NULL, *read_mp_buf = NULL; + uint8_t *read_gesture_buf = NULL, *gesture_ap_buf = NULL; + + res = core_config_ice_mode_enable(); + if (res < 0) { + ipio_err("Failed to enter ICE mode, res = %d\n", res); + return res; + } + + method = core_config_ice_mode_read(core_config->pid_addr); + method = method & 0xff; + ipio_info("method of calculation for crc = %x\n", method); + + /* Allocate buffers for host download */ + read_ap_buf = kcalloc(MAX_AP_FIRMWARE_SIZE, sizeof(uint8_t), GFP_KERNEL); + read_mp_buf = kcalloc(MAX_MP_FIRMWARE_SIZE, sizeof(uint8_t), GFP_KERNEL); + read_dlm_buf = kcalloc(MAX_DLM_FIRMWARE_SIZE, sizeof(uint8_t), GFP_KERNEL); + buf = kcalloc(MAX_AP_FIRMWARE_SIZE + 4, sizeof(uint8_t), GFP_KERNEL); + + if (ERR_ALLOC_MEM(read_ap_buf) || ERR_ALLOC_MEM(read_mp_buf) || + ERR_ALLOC_MEM(read_dlm_buf) || ERR_ALLOC_MEM(buf)) { + ipio_err("Failed to allocate buffers for host download\n"); + return -ENOMEM; + } + + memset(read_ap_buf, 0xFF, MAX_AP_FIRMWARE_SIZE); + memset(read_dlm_buf, 0xFF, MAX_DLM_FIRMWARE_SIZE); + memset(read_mp_buf, 0xFF, MAX_MP_FIRMWARE_SIZE); + memset(buf, 0xFF, MAX_AP_FIRMWARE_SIZE + 4); + + /* Allocate buffers for gesture to load code */ + ipio_info("core_gesture->entry = %d\n", core_gesture->entry); + if (core_gesture->entry) { + read_gesture_buf = kmalloc(core_gesture->ap_length, GFP_KERNEL); + gesture_ap_buf = kmalloc(core_gesture->ap_length, GFP_KERNEL); + + if (ERR_ALLOC_MEM(read_gesture_buf) || ERR_ALLOC_MEM(gesture_ap_buf)) { + ipio_err("Failed to allocate buffers for gesture\n"); + return -ENOMEM; + } + + memset(gesture_ap_buf, 0xFF, core_gesture->ap_length); + memset(read_gesture_buf, 0xFF, core_gesture->ap_length); + } + + if (core_config_set_watch_dog(false) < 0) { + ipio_err("Failed to disable watch dog\n"); + res = -EINVAL; + goto out; + } + + if (core_fr->actual_fw_mode == P5_0_FIRMWARE_TEST_MODE) { + /* write hex to the addr of MP code */ + ipio_info("Writing data into MP code ...\n"); + + if (write_download(0, MAX_MP_FIRMWARE_SIZE, mp_fw, SPI_UPGRADE_LEN) < 0) { + ipio_err("SPI Write MP code data error\n"); + } + + if (read_download(0, MAX_MP_FIRMWARE_SIZE, read_mp_buf, SPI_UPGRADE_LEN)) { + ipio_err("SPI Read MP code data error\n"); + } + + if (memcmp(mp_fw, read_mp_buf, MAX_MP_FIRMWARE_SIZE) == 0) { + ipio_info("Check MP Mode upgrade: PASS\n"); + } else { + ipio_info("Check MP Mode upgrade: FAIL\n"); + res = UPDATE_FAIL; + goto out; + } + } else if (core_gesture->entry) { + if (mode) { + /* write hex to the addr of Gesture code */ + ipio_info("Writing data into Gesture code ...\n"); + if (write_download(core_gesture->ap_start_addr, core_gesture->length, + gesture_fw, core_gesture->length) < 0) { + ipio_err("SPI Write Gesture code data error\n"); + } + + if (read_download(core_gesture->ap_start_addr, core_gesture->length, + read_gesture_buf, core_gesture->length)) { + ipio_err("SPI Read Gesture code data error\n"); + } + + if (memcmp(gesture_fw, read_gesture_buf, core_gesture->length) == 0) { + ipio_info("Check Gesture Mode upgrade: PASS\n"); + } else { + ipio_info("Check Gesture Mode upgrade: FAIL\n"); + res = UPDATE_FAIL; + goto out; + } + } else { + /* write hex to the addr of AP code */ + memcpy(gesture_ap_buf, ap_fw + core_gesture->ap_start_addr, core_gesture->ap_length); + ipio_info("Writing data into AP code ...\n"); + if (write_download(core_gesture->ap_start_addr, core_gesture->ap_length, gesture_ap_buf, core_gesture->ap_length) < 0) { + ipio_err("SPI Write AP code data error\n"); + } + if (read_download(core_gesture->ap_start_addr, core_gesture->ap_length, + read_ap_buf, core_gesture->ap_length)) { + ipio_err("SPI Read AP code data error\n"); + } + + if (memcmp(gesture_ap_buf, read_ap_buf, core_gesture->ap_length) == 0) { + ipio_info("Check AP Mode upgrade: PASS\n"); + } else { + ipio_info("Check AP Mode upgrade: FAIL\n"); + res = UPDATE_FAIL; + goto out; + } + } + } else { + /* write hex to the addr of AP code */ + ipio_info("Writing data into AP code ...\n"); + if (write_download(0, MAX_AP_FIRMWARE_SIZE, ap_fw, SPI_UPGRADE_LEN) < 0) { + ipio_err("SPI Write AP code data error\n"); + } + + /* write hex to the addr of DLM code */ + ipio_info("Writing data into DLM code ...\n"); + if (write_download(DLM_START_ADDRESS, MAX_DLM_FIRMWARE_SIZE, dlm_fw, SPI_UPGRADE_LEN) < 0) { + ipio_err("SPI Write DLM code data error\n"); + } + + /* Check AP/DLM mode Buffer data */ + if (method == CORE_TYPE_E) { + ap_crc = calc_crc32(0, MAX_AP_FIRMWARE_SIZE - 4, ap_fw); + ap_dma = host_download_dma_check(0); + + dlm_crc = calc_crc32(0, MAX_DLM_FIRMWARE_SIZE, dlm_fw); + dlm_dma = host_download_dma_check(1); + + ipio_info("AP CRC %s (%x) : (%x)\n", + (ap_crc != ap_dma ? "Invalid !" : "Correct !"), ap_crc, ap_dma); + + ipio_info("DLM CRC %s (%x) : (%x)\n", + (dlm_crc != dlm_dma ? "Invalid !" : "Correct !"), dlm_crc, dlm_dma); + + if (CHECK_EQUAL(ap_crc, ap_dma) == UPDATE_FAIL || + CHECK_EQUAL(dlm_crc, dlm_dma) == UPDATE_FAIL) { + ipio_info("Check AP/DLM Mode upgrade: FAIL\n"); + res = UPDATE_FAIL; + goto out; + } + } else { + read_download(0, MAX_AP_FIRMWARE_SIZE, read_ap_buf, SPI_UPGRADE_LEN); + read_download(DLM_START_ADDRESS, MAX_DLM_FIRMWARE_SIZE, read_dlm_buf, SPI_UPGRADE_LEN); + + if (memcmp(ap_fw, read_ap_buf, MAX_AP_FIRMWARE_SIZE) != 0 || + memcmp(dlm_fw, read_dlm_buf, MAX_DLM_FIRMWARE_SIZE) != 0) { + ipio_info("Check AP/DLM Mode upgrade: FAIL\n"); + res = UPDATE_FAIL; + goto out; + } else { + ipio_info("Check AP/DLM Mode upgrade: SUCCESS\n"); + } + } + } + +out: + if (!core_gesture->entry) { + /* ice mode code reset */ + ipio_info("Doing code reset ...\n"); + core_config_ice_mode_write(0x40040, 0xAE, 1); + } + + if (core_config_set_watch_dog(true) < 0) { + ipio_err("Failed to enable watch dog\n"); + res = -EINVAL; + } + + core_config_ice_mode_disable(); + if (core_fr->actual_fw_mode == P5_0_FIRMWARE_TEST_MODE) + mdelay(1200); + + ipio_kfree((void **)&buf); + ipio_kfree((void **)&read_ap_buf); + ipio_kfree((void **)&read_dlm_buf); + ipio_kfree((void **)&read_mp_buf); + ipio_kfree((void **)&read_gesture_buf); + ipio_kfree((void **)&gesture_ap_buf); + return res; +} +EXPORT_SYMBOL(tddi_host_download); + +int core_firmware_boot_host_download(void) +{ + int res = 0; + bool power = false; + + ipio_info("BOOT: Starting to upgrade firmware ...\n"); + + core_firmware->isUpgrading = true; + core_firmware->update_status = 0; + + if (ipd->isEnablePollCheckPower) { + ipd->isEnablePollCheckPower = false; + cancel_delayed_work_sync(&ipd->check_power_status_work); + power = true; + } + + memcpy(ap_fw, CTPM_FW + ILI_FILE_HEADER, MAX_AP_FIRMWARE_SIZE); + memcpy(dlm_fw, CTPM_FW + ILI_FILE_HEADER + DLM_HEX_ADDRESS, MAX_DLM_FIRMWARE_SIZE); + + core_gesture->ap_start_addr = (ap_fw[0xFFD3] << 24) + (ap_fw[0xFFD2] << 16) + (ap_fw[0xFFD1] << 8) + ap_fw[0xFFD0]; + core_gesture->ap_length = MAX_GESTURE_FIRMWARE_SIZE; + core_gesture->start_addr = (ap_fw[0xFFDB] << 24) + (ap_fw[0xFFDA] << 16) + (ap_fw[0xFFD9] << 8) + ap_fw[0xFFD8]; + core_gesture->length = MAX_GESTURE_FIRMWARE_SIZE; + core_gesture->area_section = (ap_fw[0xFFCF] << 24) + (ap_fw[0xFFCE] << 16) + (ap_fw[0xFFCD] << 8) + ap_fw[0xFFCC]; + + ipio_info("gesture_start_addr = 0x%x, length = 0x%x\n", core_gesture->start_addr, core_gesture->length); + ipio_info("area = %d, ap_start_addr = 0x%x, ap_length = 0x%x\n", + core_gesture->area_section, core_gesture->ap_start_addr, core_gesture->ap_length); + + memcpy(mp_fw, CTPM_FW + ILI_FILE_HEADER + MP_HEX_ADDRESS, MAX_MP_FIRMWARE_SIZE); + memcpy(gesture_fw, CTPM_FW + ILI_FILE_HEADER + core_gesture->start_addr, core_gesture->length); + + gpio_direction_output(ipd->reset_gpio, 1); + mdelay(ipd->delay_time_high); + gpio_set_value(ipd->reset_gpio, 0); + mdelay(ipd->delay_time_low); + gpio_set_value(ipd->reset_gpio, 1); + mdelay(ipd->edge_delay); + + res = core_firmware->upgrade_func(true); + if (res < 0) { + core_firmware->update_status = res; + ipio_err("Failed to upgrade firmware, res = %d\n", res); + goto out; + } + + core_firmware->update_status = 100; + +out: + if (power) { + ipd->isEnablePollCheckPower = true; + queue_delayed_work(ipd->check_power_status_queue, &ipd->check_power_status_work, ipd->work_delay); + } + core_firmware->isUpgrading = false; + return res; +} +EXPORT_SYMBOL(core_firmware_boot_host_download); +#endif /* HOST_DOWNLOAD */ + +static int tddi_fw_upgrade(bool isIRAM) +{ + int res = 0; + +#ifndef HOST_DOWNLOAD + if (isIRAM) { + res = iram_upgrade(); + return res; + } +#endif + + /*ilitek_platform_tp_hw_reset(true);*/ + /* Soft reset */ + core_config_ice_mode_enable(); + core_config_set_watch_dog(false); + mdelay(10); + core_config_ic_reset(); + + ipio_info("Enter to ICE Mode\n"); + + res = core_config_ice_mode_enable(); + if (res < 0) { + ipio_err("Failed to enable ICE mode\n"); + goto out; + } + + mdelay(5); + + /* + * This command is used to fix the bug of spi clk in 7807F-AB + * while operating with flash. + */ + if (core_config->chip_id == CHIP_TYPE_ILI7807 && core_config->chip_type == ILI7807_TYPE_F_AB) { + res = core_config_ice_mode_write(0x4100C, 0x01, 1); + if (res < 0) + goto out; + } + + mdelay(25); + + if (core_config_set_watch_dog(false) < 0) { + ipio_err("Failed to disable watch dog\n"); + res = -EINVAL; + goto out; + } + + /* Disable flash protection from being written */ + core_flash_enable_protect(false); + + res = flash_erase_sector(); + if (res < 0) { + ipio_err("Failed to erase flash\n"); + goto out; + } + + mdelay(1); + + res = flash_program_sector(); + if (res < 0) { + ipio_err("Failed to program flash\n"); + goto out; + } + + /* We do have to reset chip in order to move new code from flash to iram. */ + ipio_info("Doing Soft Reset ..\n"); + core_config_ic_reset(); + + /* the delay time moving code depends on what the touch IC you're using. */ + mdelay(core_firmware->delay_after_upgrade); + + /* ensure that the chip has been updated */ + ipio_info("Enter to ICE Mode again\n"); + res = core_config_ice_mode_enable(); + if (res < 0) { + ipio_err("Failed to enable ICE mode\n"); + goto out; + } + + mdelay(20); + + /* check the data that we've just written into the iram. */ + res = verify_flash_data(); + if (res == 0) + ipio_info("Data Correct !\n"); + +out: + if (core_config_set_watch_dog(true) < 0) { + ipio_err("Failed to enable watch dog\n"); + res = -EINVAL; + } + + core_config_ice_mode_disable(); + return res; +} + +#ifdef BOOT_FW_UPGRADE +static int convert_hex_array(void) +{ + int i, j, index = 0, crc_byte_len = 4; + int block = 0, blen = 0, bindex = 0; + uint32_t tmp_addr = 0x0; + uint32_t start_addr = 0x0, end_addr = 0; + bool boot_update_flag = false; + + core_firmware->start_addr = 0; + core_firmware->end_addr = 0; + core_firmware->checksum = 0; + core_firmware->crc32 = 0; + core_firmware->hasBlockInfo = false; + + ipio_info("CTPM_FW = %d\n", (int)ARRAY_SIZE(CTPM_FW)); + + if (ARRAY_SIZE(CTPM_FW) <= 0) { + ipio_err("The size of CTPM_FW is invaild (%d)\n", (int)ARRAY_SIZE(CTPM_FW)); + goto out; + } + + /* Get new version from ILI array */ + if (protocol->mid >= 0x3) { + core_firmware->new_fw_ver[0] = CTPM_FW[18]; + core_firmware->new_fw_ver[1] = CTPM_FW[19]; + core_firmware->new_fw_ver[2] = CTPM_FW[20]; + core_firmware->new_fw_ver[3] = CTPM_FW[21]; + } else { + core_firmware->new_fw_ver[0] = CTPM_FW[19]; + core_firmware->new_fw_ver[1] = CTPM_FW[20]; + core_firmware->new_fw_ver[2] = CTPM_FW[21]; + } + /* The process will be executed if the comparison is different with origin ver */ + for (i = 0 ; i < ARRAY_SIZE(core_firmware->old_fw_ver); i++) { + if (core_firmware->old_fw_ver[i] != core_firmware->new_fw_ver[i]) { + ipio_info("FW version is different, preparing to upgrade FW\n"); + boot_update_flag = true; + break; + } + } + ipio_info("hw fw: %d.%d.%d.%d", core_firmware->old_fw_ver[0], core_firmware->old_fw_ver[1], core_firmware->old_fw_ver[2], core_firmware->old_fw_ver[3]); + ipio_info("hex fw: %d.%d.%d.%d", core_firmware->new_fw_ver[0], core_firmware->new_fw_ver[1], core_firmware->new_fw_ver[2], core_firmware->new_fw_ver[3]); + if (i == ARRAY_SIZE(core_firmware->old_fw_ver)) { + ipio_err("FW version is the same as previous version\n"); + } + + /* Extract block info */ + block = CTPM_FW[33]; + + if (block > 0) { + core_firmware->hasBlockInfo = true; + + /* Initialize block's index and length */ + blen = 6; + bindex = 34; + ilitek_platform_disable_irq(); + + core_config_ice_mode_enable(); + mdelay(30); + + if (core_config_set_watch_dog(false) < 0) { + ipio_err("Failed to disable watch dog\n"); + } + for (i = 0 ; i < block; i++) { + for (j = 0 ; j < blen ; j++) { + if (j < 3) + g_flash_block_info[i].start_addr = + (g_flash_block_info[i].start_addr << 8) | CTPM_FW[bindex + j]; + else + g_flash_block_info[i].end_addr = + (g_flash_block_info[i].end_addr << 8) | CTPM_FW[bindex + j]; + } + start_addr = g_flash_block_info[i].start_addr & 0xffffff; + end_addr = g_flash_block_info[i].end_addr & 0xffffff; + g_flash_block_info[i].hex_crc = (CTPM_FW[64 + end_addr - 3] << 24) + (CTPM_FW[64 + end_addr - 2] << 16) + + (CTPM_FW[64 + end_addr - 1] << 8) + CTPM_FW[64 + (end_addr)]; + g_flash_block_info[i].block_crc = tddi_check_data(start_addr, end_addr - start_addr - crc_byte_len + 1); + printk("block=%d, start_addr=0x%06x, end_addr=0x%06x, H_CRC=0x%06x, B_CRC=0x%06x\n", i, start_addr, end_addr, g_flash_block_info[i].hex_crc, g_flash_block_info[i].block_crc); + bindex += blen; + if (g_flash_block_info[i].hex_crc != g_flash_block_info[i].block_crc) { + boot_update_flag = true; + break; + } + } + } + core_config_ice_mode_disable(); + if (boot_update_flag == false) { + ipio_info("No need upgrade\n"); + goto out; + } + /* Fill data into buffer */ + for (i = 0 ; i < ARRAY_SIZE(CTPM_FW) - 64; i++) { + flash_fw[i] = CTPM_FW[i + 64]; + index = i / flashtab->sector; + if (!g_flash_sector[index].data_flag) { + g_flash_sector[index].ss_addr = index * flashtab->sector; + g_flash_sector[index].se_addr = (index + 1) * flashtab->sector - 1; + g_flash_sector[index].dlength = + (g_flash_sector[index].se_addr - g_flash_sector[index].ss_addr) + 1; + g_flash_sector[index].data_flag = true; + } + } + + g_section_len = index; + + if (g_flash_sector[g_section_len].se_addr > flashtab->mem_size) { + ipio_err("The size written to flash is larger than it required (%x) (%x)\n", + g_flash_sector[g_section_len].se_addr, flashtab->mem_size); + goto out; + } + + for (i = 0 ; i < g_total_sector; i++) { + /* fill meaing address in an array where is empty */ + if (g_flash_sector[i].ss_addr == 0x0 && g_flash_sector[i].se_addr == 0x0) { + g_flash_sector[i].ss_addr = tmp_addr; + g_flash_sector[i].se_addr = (i + 1) * flashtab->sector - 1; + } + + tmp_addr += flashtab->sector; + + /* set erase flag in the block if the addr of sectors is between them. */ + if (core_firmware->hasBlockInfo) { + for (j = 0 ; j < ARRAY_SIZE(g_flash_block_info); j++) { + if (g_flash_sector[i].ss_addr >= g_flash_block_info[j].start_addr + && g_flash_sector[i].se_addr <= g_flash_block_info[j].end_addr) { + g_flash_sector[i].inside_block = true; + break; + } + } + } + + /* + * protects the reserved address been written and erased. + * This feature only applies on the boot upgrade. The addr is progrmmable in normal case. + */ + if (g_flash_sector[i].ss_addr == g_start_resrv && g_flash_sector[i].se_addr == g_end_resrv) { + g_flash_sector[i].inside_block = false; + } + } + + /* DEBUG: for showing data with address that will write into fw or be erased */ + for (i = 0 ; i < g_total_sector; i++) { + ipio_info + ("g_flash_sector[%d]: ss_addr = 0x%x, se_addr = 0x%x, length = %x, data = %d, inside_block = %d\n", + i, g_flash_sector[i].ss_addr, g_flash_sector[i].se_addr, g_flash_sector[index].dlength, + g_flash_sector[i].data_flag, g_flash_sector[i].inside_block); + } + + core_firmware->start_addr = 0x0; + core_firmware->end_addr = g_flash_sector[g_section_len].se_addr; + ipio_info("start_addr = 0x%06X, end_addr = 0x%06X\n", core_firmware->start_addr, core_firmware->end_addr); + return 0; + +out: + ipio_err("Failed to convert ILI FW array\n"); + return ERROR; +} + +int core_firmware_boot_upgrade(void) +{ + int res = 0; + bool power = false; + + ipio_info("BOOT: Starting to upgrade firmware ...\n"); + + core_firmware->isUpgrading = true; + core_firmware->update_status = 0; + + if (ipd->isEnablePollCheckPower) { + ipd->isEnablePollCheckPower = false; + cancel_delayed_work_sync(&ipd->check_power_status_work); + power = true; + } + + /* store old version before upgrade fw */ + if (protocol->mid >= 0x3) { + core_firmware->old_fw_ver[0] = core_config->firmware_ver[1]; + core_firmware->old_fw_ver[1] = core_config->firmware_ver[2]; + core_firmware->old_fw_ver[2] = core_config->firmware_ver[3]; + core_firmware->old_fw_ver[3] = core_config->firmware_ver[4]; + } else { + core_firmware->old_fw_ver[0] = core_config->firmware_ver[1]; + core_firmware->old_fw_ver[1] = core_config->firmware_ver[2]; + core_firmware->old_fw_ver[2] = core_config->firmware_ver[3]; + } + if (flashtab == NULL) { + ipio_err("Flash table isn't created\n"); + res = -ENOMEM; + goto out; + } + + flash_fw = kcalloc(flashtab->mem_size, sizeof(uint8_t), GFP_KERNEL); + if (ERR_ALLOC_MEM(flash_fw)) { + ipio_err("Failed to allocate flash_fw memory, %ld\n", PTR_ERR(flash_fw)); + res = -ENOMEM; + goto out; + } + + memset(flash_fw, 0xff, (int)sizeof(uint8_t) * flashtab->mem_size); + + g_total_sector = flashtab->mem_size / flashtab->sector; + if (g_total_sector <= 0) { + ipio_err("Flash configure is wrong\n"); + res = ERROR; + goto out; + } + + g_flash_sector = kcalloc(g_total_sector, sizeof(struct flash_sector), GFP_KERNEL); + if (ERR_ALLOC_MEM(g_flash_sector)) { + ipio_err("Failed to allocate g_flash_sector memory, %ld\n", PTR_ERR(g_flash_sector)); + res = -ENOMEM; + goto out; + } + + res = convert_hex_array(); + if (res < 0) { + ipio_err("Failed to covert firmware data, res = %d\n", res); + goto out; + } + /* calling that function defined at init depends on chips. */ + res = core_firmware->upgrade_func(false); + if (res < 0) { + core_firmware->update_status = res; + ipio_err("Failed to upgrade firmware, res = %d\n", res); + goto out; + } + + core_firmware->update_status = 100; + ipio_info("Update firmware information...\n"); + core_config_get_fw_ver(); + core_config_get_protocol_ver(); + core_config_get_core_ver(); + core_config_get_tp_info(); + core_config_get_key_info(); + +out: + if (power) { + ipd->isEnablePollCheckPower = true; + queue_delayed_work(ipd->check_power_status_queue, &ipd->check_power_status_work, ipd->work_delay); + } + + ipio_kfree((void **)&flash_fw); + ipio_kfree((void **)&g_flash_sector); + core_firmware->isUpgrading = false; + return res; +} +#endif /* BOOT_FW_UPGRADE */ + +static int convert_hex_file(uint8_t *pBuf, uint32_t nSize, bool isIRAM) +{ + int index = 0, block = 0; +#ifdef HOST_DOWNLOAD + static int do_once; +#endif + uint32_t i = 0, j = 0, k = 0; + uint32_t nLength = 0, nAddr = 0, nType = 0; + uint32_t nStartAddr = 0x0, nEndAddr = 0x0, nChecksum = 0x0, nExAddr = 0; + uint32_t tmp_addr = 0x0; + + core_firmware->start_addr = 0; + core_firmware->end_addr = 0; + core_firmware->checksum = 0; + core_firmware->crc32 = 0; + core_firmware->hasBlockInfo = false; + memset(g_flash_block_info, 0x0, sizeof(g_flash_block_info)); + +#ifdef HOST_DOWNLOAD + memset(ap_fw, 0xFF, sizeof(ap_fw)); + memset(dlm_fw, 0xFF, sizeof(dlm_fw)); + memset(mp_fw, 0xFF, sizeof(mp_fw)); + memset(gesture_fw, 0xFF, sizeof(gesture_fw)); +#endif + + /* Parsing HEX file */ + for (; i < nSize; ) { + int32_t nOffset; + + nLength = HexToDec(&pBuf[i + 1], 2); + nAddr = HexToDec(&pBuf[i + 3], 4); + nType = HexToDec(&pBuf[i + 7], 2); + + /* calculate checksum */ + for (j = 8; j < (2 + 4 + 2 + (nLength * 2)); j += 2) { + if (nType == 0x00) { + /* for ice mode write method */ + nChecksum = nChecksum + HexToDec(&pBuf[i + 1 + j], 2); + } + } + + if (nType == 0x04) { + nExAddr = HexToDec(&pBuf[i + 9], 4); + } + + if (nType == 0x02) { + nExAddr = HexToDec(&pBuf[i + 9], 4); + nExAddr = nExAddr >> 12; + } + + if (nType == 0xAE || nType == 0xAF) { + core_firmware->hasBlockInfo = true; + /* insert block info extracted from hex */ + if (block < FW_BLOCK_INFO_NUM) { + g_flash_block_info[block].start_addr = HexToDec(&pBuf[i + 9], 6); + g_flash_block_info[block].end_addr = HexToDec(&pBuf[i + 9 + 6], 6); + ipio_debug(DEBUG_FIRMWARE, "Block[%d]: start_addr = %x, end = %x\n", + block, g_flash_block_info[block].start_addr, g_flash_block_info[block].end_addr); + } + block++; + } + + nAddr = nAddr + (nExAddr << 16); + if (pBuf[i + 1 + j + 2] == 0x0D) { + nOffset = 2; + } else { + nOffset = 1; + } + + if (nType == 0x00) { + if (nAddr > MAX_HEX_FILE_SIZE) { + ipio_err("Invalid hex format\n"); + goto out; + } + + if (nAddr < nStartAddr) { + nStartAddr = nAddr; + } + if ((nAddr + nLength) > nEndAddr) { + nEndAddr = nAddr + nLength; + } + /* fill data */ + for (j = 0, k = 0 ; j < (nLength * 2); j += 2, k++) { + if (isIRAM) { +#ifdef HOST_DOWNLOAD + if (nAddr < 0x10000) { + ap_fw[nAddr + k] = HexToDec(&pBuf[i + 9 + j], 2); + } else if (nAddr >= DLM_HEX_ADDRESS && nAddr < MP_HEX_ADDRESS) { + if (nAddr < DLM_HEX_ADDRESS + MAX_DLM_FIRMWARE_SIZE) + dlm_fw[nAddr - DLM_HEX_ADDRESS + k] = HexToDec(&pBuf[i + 9 + j], 2); + } else if (nAddr >= MP_HEX_ADDRESS) { + mp_fw[nAddr - MP_HEX_ADDRESS + k] = HexToDec(&pBuf[i + 9 + j], 2); + } + if (nAddr > MAX_AP_FIRMWARE_SIZE && do_once == 0) { + do_once = 1; + core_gesture->ap_start_addr = (ap_fw[0xFFD3] << 24) + (ap_fw[0xFFD2] << 16) + (ap_fw[0xFFD1] << 8) + ap_fw[0xFFD0]; + core_gesture->ap_length = MAX_GESTURE_FIRMWARE_SIZE; + core_gesture->start_addr = (ap_fw[0xFFDB] << 24) + (ap_fw[0xFFDA] << 16) + (ap_fw[0xFFD9] << 8) + ap_fw[0xFFD8]; + core_gesture->length = MAX_GESTURE_FIRMWARE_SIZE; + core_gesture->area_section = (ap_fw[0xFFCF] << 24) + (ap_fw[0xFFCE] << 16) + (ap_fw[0xFFCD] << 8) + ap_fw[0xFFCC]; + } + if (nAddr >= core_gesture->start_addr && + (nAddr < core_gesture->start_addr + MAX_GESTURE_FIRMWARE_SIZE)) { + gesture_fw[nAddr - core_gesture->start_addr + k] = HexToDec(&pBuf[i + 9 + j], 2); + } +#else + iram_fw[nAddr + k] = HexToDec(&pBuf[i + 9 + j], 2); +#endif + } else { + flash_fw[nAddr + k] = HexToDec(&pBuf[i + 9 + j], 2); + + if ((nAddr + k) != 0) { + index = ((nAddr + k) / flashtab->sector); + if (!g_flash_sector[index].data_flag) { + g_flash_sector[index].ss_addr = index * flashtab->sector; + g_flash_sector[index].se_addr = + (index + 1) * flashtab->sector - 1; + g_flash_sector[index].dlength = + (g_flash_sector[index].se_addr - + g_flash_sector[index].ss_addr) + 1; + g_flash_sector[index].data_flag = true; + } + } + } + } + } + i += 1 + 2 + 4 + 2 + (nLength * 2) + 2 + nOffset; + } + +#ifdef HOST_DOWNLOAD + return 0; +#endif + + /* Update the length of section */ + g_section_len = index; + + if (g_flash_sector[g_section_len - 1].se_addr > flashtab->mem_size) { + ipio_err("The size written to flash is larger than it required (%x) (%x)\n", + g_flash_sector[g_section_len - 1].se_addr, flashtab->mem_size); + goto out; + } + + for (i = 0 ; i < g_total_sector; i++) { + /* fill meaing address in an array where is empty */ + if (g_flash_sector[i].ss_addr == 0x0 && g_flash_sector[i].se_addr == 0x0) { + g_flash_sector[i].ss_addr = tmp_addr; + g_flash_sector[i].se_addr = (i + 1) * flashtab->sector - 1; + } + + tmp_addr += flashtab->sector; + + /* set erase flag in the block if the addr of sectors is between them. */ + if (core_firmware->hasBlockInfo) { + for (j = 0 ; j < ARRAY_SIZE(g_flash_block_info); j++) { + if (g_flash_sector[i].ss_addr >= g_flash_block_info[j].start_addr + && g_flash_sector[i].se_addr <= g_flash_block_info[j].end_addr) { + g_flash_sector[i].inside_block = true; + break; + } + } + } + } + + /* DEBUG: for showing data with address that will write into fw or be erased */ + for (i = 0 ; i < g_total_sector; i++) { + ipio_debug(DEBUG_FIRMWARE, + "g_flash_sector[%d]: ss_addr = 0x%x, se_addr = 0x%x, length = %x, data = %d, inside_block = %d\n", i, + g_flash_sector[i].ss_addr, g_flash_sector[i].se_addr, g_flash_sector[index].dlength, + g_flash_sector[i].data_flag, g_flash_sector[i].inside_block); + } + + core_firmware->start_addr = nStartAddr; + core_firmware->end_addr = nEndAddr; + ipio_info("nStartAddr = 0x%06X, nEndAddr = 0x%06X\n", nStartAddr, nEndAddr); + return 0; + +out: + ipio_err("Failed to convert HEX data\n"); + return ERROR; +} + +/* + * It would basically be called by ioctl when users want to upgrade firmware. + * + * @pFilePath: pass a path where locates user's firmware file. + * + */ +int core_firmware_upgrade(const char *pFilePath, bool isIRAM) +{ + int res = 0, fsize, i = 0; + uint8_t *hex_buffer = NULL; + bool power = false; + const struct firmware *fw; + + + core_firmware->isUpgrading = true; + core_firmware->update_status = 0; + + if (ipd->isEnablePollCheckPower) { + ipd->isEnablePollCheckPower = false; + cancel_delayed_work_sync(&ipd->check_power_status_work); + power = true; + } + + res = request_firmware(&fw, pFilePath, &ipd->client->dev); + + if (res < 0) { + ipio_err("Failed to open the file Name %s.\n", pFilePath); + /* if not hex file can ili file */ +#ifdef HOST_DOWNLOAD + goto no_hex_file; +#endif + res = -ENOENT; + return res; + } + + fsize = fw->size; + + ipio_info("fsize = %d\n", fsize); + + if (fsize <= 0) { + ipio_err("The size of file is zero\n"); + res = -EINVAL; + goto out; + } + +#ifndef HOST_DOWNLOAD + if (flashtab == NULL) { + ipio_err("Flash table isn't created\n"); + res = -ENOMEM; + goto out; + } + + flash_fw = kcalloc(flashtab->mem_size, sizeof(uint8_t), GFP_KERNEL); + if (ERR_ALLOC_MEM(flash_fw)) { + ipio_err("Failed to allocate flash_fw memory, %ld\n", PTR_ERR(flash_fw)); + res = -ENOMEM; + goto out; + } + + memset(flash_fw, 0xff, sizeof(uint8_t) * flashtab->mem_size); + + g_total_sector = flashtab->mem_size / flashtab->sector; + if (g_total_sector <= 0) { + ipio_err("Flash configure is wrong\n"); + res = ERROR; + goto out; + } + + g_flash_sector = kcalloc(g_total_sector, sizeof(*g_flash_sector), GFP_KERNEL); + if (ERR_ALLOC_MEM(g_flash_sector)) { + ipio_err("Failed to allocate g_flash_sector memory, %ld\n", PTR_ERR(g_flash_sector)); + res = -ENOMEM; + goto out; + } +#endif + + hex_buffer = kcalloc(fsize, sizeof(uint8_t), GFP_KERNEL); + if (ERR_ALLOC_MEM(hex_buffer)) { + ipio_err("Failed to allocate hex_buffer memory, %ld\n", PTR_ERR(hex_buffer)); + res = -ENOMEM; + goto out; + } + + for (i = 0 ; i < fw->size ; i++) + hex_buffer[i] = fw->data[i]; + + res = convert_hex_file(hex_buffer, fsize, isIRAM); + if (res < 0) { + ipio_err("Failed to covert firmware data, res = %d\n", res); + goto out; + } + +#ifdef HOST_DOWNLOAD +no_hex_file: +#endif + /* calling that function defined at init depends on chips. */ + res = core_firmware->upgrade_func(isIRAM); + if (res < 0) { + ipio_err("Failed to upgrade firmware, res = %d\n", res); + goto out; + } + +#ifndef HOST_DOWNLOAD + ipio_info("Update TP/Firmware information...\n"); + core_config_get_fw_ver(); + core_config_get_protocol_ver(); + core_config_get_core_ver(); + core_config_get_tp_info(); + core_config_get_key_info(); +#endif + +out: + if (power) { + ipd->isEnablePollCheckPower = true; + queue_delayed_work(ipd->check_power_status_queue, &ipd->check_power_status_work, ipd->work_delay); + } + core_firmware->isUpgrading = false; + + + release_firmware(fw); + ipio_kfree((void **)&hex_buffer); + ipio_kfree((void **)&flash_fw); + ipio_kfree((void **)&g_flash_sector); + return res; +} + + +int core_firmware_init(void) +{ + int i = 0, j = 0; + + core_firmware = kzalloc(sizeof(*core_firmware), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_firmware)) { + ipio_err("Failed to allocate core_firmware mem, %ld\n", PTR_ERR(core_firmware)); + core_firmware_remove(); + return -ENOMEM; + } + + core_firmware->hasBlockInfo = false; + core_firmware->isboot = false; + + for (; i < ARRAY_SIZE(ipio_chip_list); i++) { + if (ipio_chip_list[i] == ipd->chip_id) { + for (j = 0 ; j < 4; j++) { + core_firmware->old_fw_ver[i] = core_config->firmware_ver[i]; + core_firmware->new_fw_ver[i] = 0x0; + } + + if (ipio_chip_list[i] == CHIP_TYPE_ILI7807) { + core_firmware->max_count = 0xFFFF; + core_firmware->isCRC = false; + core_firmware->upgrade_func = tddi_fw_upgrade; + core_firmware->delay_after_upgrade = 100; + } else if (ipio_chip_list[i] == CHIP_TYPE_ILI9881) { + core_firmware->max_count = 0x1FFFF; + core_firmware->isCRC = true; + #ifdef HOST_DOWNLOAD + core_firmware->upgrade_func = tddi_host_download; + #else + core_firmware->upgrade_func = tddi_fw_upgrade; + #endif + core_firmware->delay_after_upgrade = 200; + } + return 0; + } + } + + ipio_err("Can't find this chip in support list\n"); + return 0; +} + +void core_firmware_remove(void) +{ + ipio_info("Remove core-firmware members\n"); + ipio_kfree((void **)&core_firmware); +} diff --git a/drivers/input/touchscreen/ilitek_mmi/core/firmware.h b/drivers/input/touchscreen/ilitek_mmi/core/firmware.h new file mode 100644 index 000000000000..9f830820da33 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/firmware.h @@ -0,0 +1,76 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __FIRMWARE_H +#define __FIRMWARE_H + +#define CHECK_FW_FAIL -1 +#define NEED_UPDATE 1 +#define NO_NEED_UPDATE 0 + +#define AP_STAR_ADDR 0x00000 +#define AP_END_ADDR 0x0FFFF +#define DATA_STAR_ADDR 0x10000 +#define DATA_END_ADDR 0x11FFF +#define MP_STAR_ADDR 0x13000 +#define MP_END_ADDR 0x1BFFF + +struct core_firmware_data { +uint8_t new_fw_ver[4]; +uint8_t old_fw_ver[4]; + +uint32_t start_addr; +uint32_t end_addr; +uint32_t checksum; +uint32_t crc32; +uint32_t update_status; +uint32_t max_count; + +int delay_after_upgrade; + +bool isUpgrading; +bool force_upgrad; +bool isCRC; +bool isboot; +bool hasBlockInfo; + +int (*upgrade_func)(bool isIRAM); +}; + +extern struct core_firmware_data *core_firmware; + +#ifdef HOST_DOWNLOAD +extern int core_firmware_boot_host_download(void); +extern int tddi_host_download(bool isIRAM); +#endif +#ifdef BOOT_FW_UPGRADE +extern int core_firmware_boot_upgrade(void); +#endif +/* extern int core_firmware_iram_upgrade(const char* fpath); */ +extern int core_firmware_upgrade(const char *pFilePath, bool isIRAM); +extern int core_firmware_init(void); +extern int tddi_check_fw_upgrade(void); +extern void core_firmware_remove(void); + +#endif /* __FIRMWARE_H */ diff --git a/drivers/input/touchscreen/ilitek_mmi/core/flash.c b/drivers/input/touchscreen/ilitek_mmi/core/flash.c new file mode 100644 index 000000000000..f7a02062577e --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/flash.c @@ -0,0 +1,187 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "../common.h" +#include "config.h" +#include "flash.h" + +#define K (1024) +#define M (K * K) + +/* + * The table contains fundamental data used to program our flash, which + * would be different according to the vendors. + */ +struct flash_table ft[] = { + {0xEF, 0x6011, (128 * K), 256, (4 * K), (64 * K)}, /* W25Q10EW */ + {0xEF, 0x6012, (256 * K), 256, (4 * K), (64 * K)}, /* W25Q20EW */ + {0xC8, 0x6012, (256 * K), 256, (4 * K), (64 * K)}, /* GD25LQ20B */ + {0xC8, 0x6013, (512 * K), 256, (4 * K), (64 * K)}, /* GD25LQ40 */ + {0x85, 0x6013, (4 * M), 256, (4 * K), (64 * K)}, + {0xC2, 0x2812, (256 * K), 256, (4 * K), (64 * K)}, + {0x1C, 0x3812, (256 * K), 256, (4 * K), (64 * K)}, +}; + +struct flash_table *flashtab; + +int core_flash_poll_busy(void) +{ + int timer = 500, res = 0; + + core_config_ice_mode_write(0x041000, 0x0, 1); /* CS low */ + core_config_ice_mode_write(0x041004, 0x66aa55, 3); /* Key */ + + core_config_ice_mode_write(0x041008, 0x5, 1); + while (timer > 0) { + core_config_ice_mode_write(0x041008, 0xFF, 1); + + mdelay(1); + + if ((core_config_read_write_onebyte(0x041010) & 0x03) == 0x00) + goto out; + timer--; + } + + ipio_err("Polling busy Time out !\n"); + res = ERROR; +out: + core_config_ice_mode_write(0x041000, 0x1, 1); /* CS high */ + return res; +} +EXPORT_SYMBOL(core_flash_poll_busy); + +int core_flash_write_enable(void) +{ + if (core_config_ice_mode_write(0x041000, 0x0, 1) < 0) + goto out; + if (core_config_ice_mode_write(0x041004, 0x66aa55, 3) < 0) + goto out; + if (core_config_ice_mode_write(0x041008, 0x6, 1) < 0) + goto out; + if (core_config_ice_mode_write(0x041000, 0x1, 1) < 0) + goto out; + + return 0; + +out: + ipio_err("Write enable failed !\n"); + return -EIO; +} +EXPORT_SYMBOL(core_flash_write_enable); + +void core_flash_enable_protect(bool enable) +{ + ipio_info("Set flash protect as (%d)\n", enable); + + if (core_flash_write_enable() < 0) { + ipio_err("Failed to config flash's write enable\n"); + return; + } + + core_config_ice_mode_write(0x041000, 0x0, 1); /* CS low */ + core_config_ice_mode_write(0x041004, 0x66aa55, 3); /* Key */ + + switch (flashtab->mid) { + case 0xEF: + if (flashtab->dev_id == 0x6012 || flashtab->dev_id == 0x6011) { + core_config_ice_mode_write(0x041008, 0x1, 1); + core_config_ice_mode_write(0x041008, 0x00, 1); + + if (enable) + core_config_ice_mode_write(0x041008, 0x7E, 1); + else + core_config_ice_mode_write(0x041008, 0x00, 1); + } + break; + case 0xC8: + if (flashtab->dev_id == 0x6012 || flashtab->dev_id == 0x6013) { + core_config_ice_mode_write(0x041008, 0x1, 1); + core_config_ice_mode_write(0x041008, 0x00, 1); + + if (enable) + core_config_ice_mode_write(0x041008, 0x7A, 1); + else + core_config_ice_mode_write(0x041008, 0x00, 1); + } + break; + default: + ipio_err("Can't find flash id, ignore protection\n"); + break; + } + + core_config_ice_mode_write(0x041000, 0x1, 1); /* CS high */ + mdelay(5); +} +EXPORT_SYMBOL(core_flash_enable_protect); + +void core_flash_init(uint16_t mid, uint16_t did) +{ + int i = 0; + + ipio_debug(DEBUG_FIRMWARE,"M_ID = %x, DEV_ID = %x", mid, did); + + flashtab = kzalloc(sizeof(ft), GFP_KERNEL); + if (ERR_ALLOC_MEM(flashtab)) { + ipio_err("Failed to allocate flashtab memory, %ld\n", PTR_ERR(flashtab)); + return; + } + + for (; i < ARRAY_SIZE(ft); i++) { + if (mid == ft[i].mid && did == ft[i].dev_id) { + ipio_debug(DEBUG_FIRMWARE,"Find them in flash table\n"); + + flashtab->mid = mid; + flashtab->dev_id = did; + flashtab->mem_size = ft[i].mem_size; + flashtab->program_page = ft[i].program_page; + flashtab->sector = ft[i].sector; + flashtab->block = ft[i].block; + break; + } + } + + if (i >= ARRAY_SIZE(ft)) { + ipio_err("Can't find them in flash table, apply default flash config\n"); + flashtab->mid = mid; + flashtab->dev_id = did; + flashtab->mem_size = (256 * K); + flashtab->program_page = 256; + flashtab->sector = (4 * K); + flashtab->block = (64 * K); + } + + ipio_debug(DEBUG_FIRMWARE,"Max Memory size = %d\n", flashtab->mem_size); + ipio_debug(DEBUG_FIRMWARE,"Per program page = %d\n", flashtab->program_page); + ipio_debug(DEBUG_FIRMWARE,"Sector size = %d\n", flashtab->sector); + ipio_debug(DEBUG_FIRMWARE,"Block size = %d\n", flashtab->block); +} +EXPORT_SYMBOL(core_flash_init); + +void core_flash_remove(void) +{ + ipio_info("Remove core-flash memebers\n"); + + ipio_kfree((void **)&flashtab); +} +EXPORT_SYMBOL(core_flash_remove); diff --git a/drivers/input/touchscreen/ilitek_mmi/core/flash.h b/drivers/input/touchscreen/ilitek_mmi/core/flash.h new file mode 100644 index 000000000000..3ff6bdddd4df --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/flash.h @@ -0,0 +1,45 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __FLASH_H +#define __FLASH_H + +struct flash_table { +uint16_t mid; +uint16_t dev_id; +int mem_size; +int program_page; +int sector; +int block; +}; + +extern struct flash_table *flashtab; + +extern int core_flash_poll_busy(void); +extern int core_flash_write_enable(void); +extern void core_flash_enable_protect(bool status); +extern void core_flash_init(uint16_t mid, uint16_t did); +extern void core_flash_remove(void); + +#endif /* __FLASH_H */ diff --git a/drivers/input/touchscreen/ilitek_mmi/core/gesture.c b/drivers/input/touchscreen/ilitek_mmi/core/gesture.c new file mode 100644 index 000000000000..bedd65d6cb74 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/gesture.c @@ -0,0 +1,289 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "../common.h" +#include "finger_report.h" +#include "firmware.h" +#include "gesture.h" +#include "protocol.h" +#include "config.h" + +struct core_gesture_data *core_gesture; + +#ifdef HOST_DOWNLOAD +int core_gesture_load_code(void) +{ + int i = 0, ret = 0; + uint8_t temp[64] = {0}; + + core_gesture->entry = true; + + /* Already read during parsing hex file */ + ipio_info("gesture_start_addr = 0x%x, length = 0x%x\n", + core_gesture->start_addr, core_gesture->length); + ipio_info("area = %d, ap_start_addr = 0x%x, ap_length = 0x%x\n", + core_gesture->area_section, core_gesture->ap_start_addr, + core_gesture->ap_length); + + /* write load gesture flag */ + ipio_info("write load gesture flag\n"); + temp[0] = 0x01; + temp[1] = 0x0A; + temp[2] = 0x03; + if ((core_write(core_config->slave_i2c_addr, temp, 3)) < 0) + ipio_err("write gesture flag error\n"); + + ipio_info("enter gesture cmd lpwg start\n"); + /* enter gesture cmd lpwg start */ + temp[0] = 0x01; + temp[1] = 0x0A; + temp[2] = core_gesture->mode + 1; + if ((core_write(core_config->slave_i2c_addr, temp, 3)) < 0) + ipio_err("write lpwg start error\n"); + + for (i = 0 ; i < 20 ; i++) { + temp[0] = 0xF6; + temp[1] = 0x0A; + temp[2] = 0x05; + if ((core_write(core_config->slave_i2c_addr, temp, 2)) < 0) + ipio_err("write 0xF6,0xA,0x05 command error\n"); + + mdelay(i * 50); + + temp[0] = 0x01; + temp[1] = 0x0A; + temp[2] = 0x05; + if ((core_write(core_config->slave_i2c_addr, temp, 3)) < 0) + ipio_err("write command error\n"); + + if ((core_read(core_config->slave_i2c_addr, temp, 1)) < 0) + ipio_err("Read command error\n"); + + if (temp[0] == 0x91) { + ipio_info("check fw ready\n"); + break; + } + } + + if (temp[0] != 0x91) + ipio_err("FW is busy, error\n"); + ipio_info("load gesture code\n"); + /* load gesture code */ + if (core_config_ice_mode_enable() < 0) + ipio_err("Failed to enter ICE mode\n"); + + tddi_host_download(true); + + temp[0] = 0x01; + temp[1] = 0x0A; + temp[2] = 0x06; + if ((core_write(core_config->slave_i2c_addr, temp, 3)) < 0) + ipio_err("write command error\n"); + + core_gesture->entry = false; + return ret; +} +EXPORT_SYMBOL(core_gesture_load_code); + +int core_gesture_load_ap_code(void) +{ + int i = 0, ret = 0; + uint8_t temp[64] = {0}; + + core_gesture->entry = true; + + /* Write Load AP Flag */ + temp[0] = 0x01; + temp[1] = 0x01; + temp[2] = 0x00; + if ((core_write(core_config->slave_i2c_addr, temp, 3)) < 0) + ipio_err("write command AP Flag error\n"); + + if ((core_read(core_config->slave_i2c_addr, temp, 20)) < 0) + ipio_err("Read AP Flag error\n"); + + /* Leave Gesture Cmd LPWG Stop */ + temp[0] = 0x01; + temp[1] = 0x0A; + temp[2] = 0x00; + if ((core_write(core_config->slave_i2c_addr, temp, 3)) < 0) + ipio_err("write command LPWG Stop error\n"); + + for (i = 0 ; i < 20 ; i++) { + + mdelay(i * 100 + 100); + + temp[0] = 0x01; + temp[1] = 0x0A; + temp[2] = 0x05; + if ((core_write(core_config->slave_i2c_addr, temp, 3)) < 0) + ipio_err("write command error\n"); + if ((core_read(core_config->slave_i2c_addr, temp, 1)) < 0) + ipio_err("Read command error\n"); + if (temp[0] == 0x91) { + ipio_info("check fw ready\n"); + break; + } + } + + if (i == 3 && temp[0] != 0x01) + ipio_err("FW is busy, error\n"); + + /* load AP code */ + if (core_config_ice_mode_enable() < 0) + ipio_err("Failed to enter ICE mode\n"); + + tddi_host_download(false); + + temp[0] = 0x01; + temp[1] = 0x0A; + temp[2] = 0x06; + if ((core_write(core_config->slave_i2c_addr, temp, 3)) < 0) + ipio_err("write command error\n"); + + core_gesture->entry = false; + return ret; +} +EXPORT_SYMBOL(core_gesture_load_ap_code); +#endif + +int core_gesture_match_key(uint8_t gdata) +{ + int gcode; + + switch (gdata) { + case GESTURE_LEFT: + gcode = KEY_GESTURE_LEFT; + break; + case GESTURE_RIGHT: + gcode = KEY_GESTURE_RIGHT; + break; + case GESTURE_UP: + gcode = KEY_GESTURE_UP; + break; + case GESTURE_DOWN: + gcode = KEY_GESTURE_DOWN; + break; + case GESTURE_DOUBLECLICK: + gcode = KEY_GESTURE_D; + break; + case GESTURE_O: + gcode = KEY_GESTURE_O; + break; + case GESTURE_W: + gcode = KEY_GESTURE_W; + break; + case GESTURE_M: + gcode = KEY_GESTURE_M; + break; + case GESTURE_E: + gcode = KEY_GESTURE_E; + break; + case GESTURE_S: + gcode = KEY_GESTURE_S; + break; + case GESTURE_V: + gcode = KEY_GESTURE_V; + break; + case GESTURE_Z: + gcode = KEY_GESTURE_Z; + break; + case GESTURE_C: + gcode = KEY_GESTURE_C; + break; + default: + gcode = ERROR; + break; + } + + ipio_debug(DEBUG_GESTURE, "gcode = %d\n", gcode); + return gcode; +} +EXPORT_SYMBOL(core_gesture_match_key); + +void core_gesture_set_key(struct core_fr_data *fr_data) +{ + struct input_dev *input_dev = fr_data->input_device; + + if (input_dev != NULL) { + input_set_capability(input_dev, EV_KEY, KEY_POWER); + 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_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_POWER, input_dev->keybit); + __set_bit(KEY_GESTURE_UP, input_dev->keybit); + __set_bit(KEY_GESTURE_DOWN, input_dev->keybit); + __set_bit(KEY_GESTURE_LEFT, input_dev->keybit); + __set_bit(KEY_GESTURE_RIGHT, 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_S, input_dev->keybit); + __set_bit(KEY_GESTURE_V, input_dev->keybit); + __set_bit(KEY_GESTURE_Z, input_dev->keybit); + __set_bit(KEY_GESTURE_C, input_dev->keybit); + return; + } + + ipio_err("GESTURE: input dev is NULL\n"); +} +EXPORT_SYMBOL(core_gesture_set_key); + +int core_gesture_init(void) +{ + if (core_gesture == NULL) { + core_gesture = kzalloc(sizeof(*core_gesture), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_gesture)) { + ipio_err("Failed to allocate core_gesture mem, %ld\n", + PTR_ERR(core_gesture)); + core_gesture_remove(); + return -ENOMEM; + } + + core_gesture->entry = false; + } + + return 0; +} +EXPORT_SYMBOL(core_gesture_init); + +void core_gesture_remove(void) +{ + ipio_info("Remove core-gesture members\n"); + ipio_kfree((void **)&core_gesture); +} +EXPORT_SYMBOL(core_gesture_remove); + + diff --git a/drivers/input/touchscreen/ilitek_mmi/core/gesture.h b/drivers/input/touchscreen/ilitek_mmi/core/gesture.h new file mode 100644 index 000000000000..e3e0928ec99c --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/gesture.h @@ -0,0 +1,88 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __GESTURE_H +#define __GESTURE_H +#define GESTURE_NORMAL_MODE 0 +#define GESTURE_INFO_MPDE 1 +#define GESTURE_INFO_LENGTH 170 +#define GESTURE_MORMAL_LENGTH 8 + +/* The example for the gesture virtual keys */ +#define GESTURE_DOUBLECLICK 0x58 +#define GESTURE_UP 0x60 +#define GESTURE_DOWN 0x61 +#define GESTURE_LEFT 0x62 +#define GESTURE_RIGHT 0x63 +#define GESTURE_M 0x64 +#define GESTURE_W 0x65 +#define GESTURE_C 0x66 +#define GESTURE_E 0x67 +#define GESTURE_V 0x68 +#define GESTURE_O 0x69 +#define GESTURE_S 0x6A +#define GESTURE_Z 0x6B + +/* oppo gesture define */ +#define GESTURE_CODE_V_DOWN 0x6C +#define GESTURE_CODE_V_LEFT 0x6D +#define GESTURE_CODE_V_RIGHT 0x6E +#define GESTURE_CODE_TWO_LINE_2_BOTTOM 0x6F + +#define KEY_GESTURE_D KEY_D +#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_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 + +struct core_gesture_data { + uint32_t start_addr; + uint32_t length; + bool entry; + uint8_t mode; /* normal:0 info:1 */ + uint32_t ap_start_addr; + uint32_t ap_length; + uint32_t area_section; + bool suspend; +}; + +extern struct core_gesture_data *core_gesture; +extern uint8_t ap_fw[MAX_AP_FIRMWARE_SIZE]; +#ifdef HOST_DOWNLOAD +extern int core_gesture_load_code(void); +extern int core_gesture_load_ap_code(void); +#endif +extern int core_gesture_match_key(uint8_t gid); +extern void core_gesture_set_key(struct core_fr_data *fr_data); +extern int core_gesture_init(void); +extern void core_gesture_remove(void); +#endif diff --git a/drivers/input/touchscreen/ilitek_mmi/core/i2c.c b/drivers/input/touchscreen/ilitek_mmi/core/i2c.c new file mode 100644 index 000000000000..5ecc8682cd00 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/i2c.c @@ -0,0 +1,294 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "../common.h" +#include "config.h" +#include "i2c.h" +#include "mp_test.h" +#include "finger_report.h" +#include "protocol.h" +#include "../platform.h" + + +struct core_i2c_data *core_i2c; + +#define I2C_RETRY_TIMES 3 + +#ifdef I2C_DMA +static unsigned char *ilitek_dma_va; +static dma_addr_t ilitek_dma_pa; + +#define DMA_VA_BUFFER 4096 + +static int dma_alloc(struct core_i2c_data *i2c) +{ + if (i2c->client != NULL) { + i2c->client->dev.coherent_dma_mask = DMA_BIT_MASK(32); + ilitek_dma_va = (u8 *) dma_alloc_coherent(&i2c->client->dev + , DMA_VA_BUFFER, &ilitek_dma_pa, GFP_KERNEL); + if (ERR_ALLOC_MEM(ilitek_dma_va)) { + ipio_err("Allocate DMA I2C Buffer failed\n"); + return -ENOMEM; + } + + memset(ilitek_dma_va, 0, DMA_VA_BUFFER); + i2c->client->ext_flag |= I2C_DMA_FLAG; + return 0; + } + + ipio_err("i2c->client is NULL, return fail\n"); + return -ENODEV; +} + +static void dma_free(void) +{ + if (ilitek_dma_va != NULL) { + dma_free_coherent(&core_i2c->client->dev, DMA_VA_BUFFER + , ilitek_dma_va, ilitek_dma_pa); + + ilitek_dma_va = NULL; + ilitek_dma_pa = 0; + + ipio_info("Succeed to free DMA buffer\n"); + } +} +#endif /* I2C_DMA */ + +int core_i2c_write(uint8_t nSlaveId, uint8_t *pBuf, uint16_t nSize) +{ + int res = 0; + uint8_t check_sum = 0; + uint8_t *txbuf = NULL; + int retry = 0; + + struct i2c_msg msgs[] = { + { + .addr = nSlaveId, + .flags = 0, /* write flag. */ + .len = nSize, + .buf = pBuf, + }, + }; + +#ifdef I2C_DMA + ipio_debug(DEBUG_I2C, "DMA: size = %d\n", nSize); + if (nSize > 8) { + msgs[0].addr = (core_i2c->client->addr & I2C_MASK_FLAG); + msgs[0].ext_flag = + (core_i2c->client->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG); + memcpy(ilitek_dma_va, pBuf, nSize); + msgs[0].buf = (uint8_t *) ilitek_dma_pa; + } +#endif /* I2C_DMA */ + + /* + * NOTE: If TP driver is doing MP test and commanding 0xF1 to FW, + * we add a checksum to the last index and plus 1 with size. + */ + txbuf = kcalloc(nSize + 1, sizeof(uint8_t), GFP_KERNEL); + if (ERR_ALLOC_MEM(txbuf)) { + ipio_err("Failed to allocate txbuf mem\n"); + res = -ENOMEM; + goto out; + } + if (protocol->major >= 5 && protocol->mid >= 4) { + if (core_fr->actual_fw_mode == protocol->test_mode && pBuf[0] == 0xF1) { + check_sum = core_fr_calc_checksum(pBuf, nSize); + memcpy(txbuf, pBuf, nSize); + txbuf[nSize] = check_sum; + msgs[0].buf = txbuf; + msgs[0].len = nSize + 1; + } + } + for (retry = 1; retry <= 3; retry++) { + if (i2c_transfer(core_i2c->client->adapter, msgs, 1) < 0) { + if (core_config->do_ic_reset) { + /* ignore i2c error if doing ic reset */ + res = 0; + break; + } else { + res = -EIO; + ipio_err("I2C Write Error, res = %d retry = %d\n", res, retry); + mdelay(20); + if (retry == I2C_RETRY_TIMES) + goto out; + + } + } + else { + res = 0; + break; + } + } +out: + ipio_kfree((void **)&txbuf); + return res; +} +EXPORT_SYMBOL(core_i2c_write); + +int core_i2c_read(uint8_t nSlaveId, uint8_t *pBuf, uint16_t nSize) +{ + int res = 0; + int retry = 0; + + struct i2c_msg msgs[] = { + { + .addr = nSlaveId, + .flags = I2C_M_RD, /* read flag */ + .len = nSize, + .buf = pBuf, + }, + }; + +#ifdef I2C_DMA + ipio_debug(DEBUG_I2C, "DMA: size = %d\n", nSize); + if (nSize > 8) { + msgs[0].addr = (core_i2c->client->addr & I2C_MASK_FLAG); + msgs[0].ext_flag = + (core_i2c->client->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG); + msgs[0].buf = (uint8_t *) ilitek_dma_pa; + } else { + msgs[0].buf = pBuf; + } +#endif /* I2C_DMA */ + for (retry = 1; retry <= I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(core_i2c->client->adapter, msgs, 1) < 0) { + res = -EIO; + ipio_err("I2C Read Error, res = %d retry = %d\n", res, retry); + mdelay(20); + if (retry == I2C_RETRY_TIMES) + goto out; + } + else { + res = 0; + break; + } + } +#ifdef I2C_DMA + if (nSize > 8) + memcpy(pBuf, ilitek_dma_va, nSize); + +#endif /* I2C_DMA */ + +out: + return res; +} +EXPORT_SYMBOL(core_i2c_read); + +int core_i2c_segmental_read(uint8_t nSlaveId, uint8_t *pBuf, uint16_t nSize) +{ + int res = 0; + int offset = 0; + + struct i2c_msg msgs[] = { + { + .addr = nSlaveId, + .flags = I2C_M_RD, + .len = nSize, +/* .buf = pBuf, */ + }, + }; + + while (nSize > 0) { + msgs[0].buf = &pBuf[offset]; + + if (nSize > core_i2c->seg_len) { + msgs[0].len = core_i2c->seg_len; + nSize -= core_i2c->seg_len; + offset += msgs[0].len; + } else { + msgs[0].len = nSize; + nSize = 0; + } + + ipio_debug(DEBUG_I2C, "Length = %d\n", msgs[0].len); + + if (i2c_transfer(core_i2c->client->adapter, msgs, 1) < 0) { + res = -EIO; + ipio_err("I2C Read Error, res = %d\n", res); + goto out; + } + } + +out: + return res; +} +EXPORT_SYMBOL(core_i2c_segmental_read); + +int core_i2c_init(struct i2c_client *client) +{ + int i; + + core_i2c = kmalloc(sizeof(*core_i2c), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_i2c)) { + ipio_err("Failed to alllocate core_i2c mem %ld\n", + PTR_ERR(core_i2c)); + core_i2c_remove(); + return -ENOMEM; + } + + core_i2c->client = client; + core_i2c->seg_len = 256; /* length of segment */ + +#ifdef I2C_DMA + if (dma_alloc(core_i2c->client) < 0) { + ipio_err("Failed to alllocate DMA mem %ld\n", + PTR_ERR(core_i2c)); + return -ENOMEM; + } +#endif /* I2C_DMA */ + + for (i = 0 ; i < ARRAY_SIZE(ipio_chip_list); i++) { + if (ipio_chip_list[i] == ipd->chip_id) { + if (ipio_chip_list[i] == CHIP_TYPE_ILI7807) { + if (core_config->chip_type == ILI7807_TYPE_F_AA + && core_config->chip_type == ILI7807_TYPE_F_AB) + core_i2c->clk = 100000; + else + core_i2c->clk = 400000; + + } else if (ipio_chip_list[i] == CHIP_TYPE_ILI9881) { + core_i2c->clk = 400000; + } + + return 0; + } + } + + ipio_err("Can't find this chip in support list\n"); + return 0; +} +EXPORT_SYMBOL(core_i2c_init); + +void core_i2c_remove(void) +{ + ipio_info("Remove core-i2c members\n"); + +#ifdef I2C_DMA + dma_free(); +#endif /* I2C_DMA */ + + ipio_kfree((void **)&core_i2c); +} +EXPORT_SYMBOL(core_i2c_remove); diff --git a/drivers/input/touchscreen/ilitek_mmi/core/i2c.h b/drivers/input/touchscreen/ilitek_mmi/core/i2c.h new file mode 100644 index 000000000000..f98214b9b1ef --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/i2c.h @@ -0,0 +1,45 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __I2C_H +#define __I2C_H + +struct core_i2c_data { + struct i2c_client *client; + int clk; + int seg_len; +}; + +extern struct core_i2c_data *core_i2c; + +extern int core_i2c_write(uint8_t nSlaveId, uint8_t *pBuf, uint16_t nSize); +extern int core_i2c_read(uint8_t nSlaveId, uint8_t *pBuf, uint16_t nSize); + +extern int core_i2c_segmental_read(uint8_t nSlaveId, uint8_t *pBuf +, uint16_t nSize); + +extern int core_i2c_init(struct i2c_client *client); +extern void core_i2c_remove(void); + +#endif diff --git a/drivers/input/touchscreen/ilitek_mmi/core/ilitek_fw.h b/drivers/input/touchscreen/ilitek_mmi/core/ilitek_fw.h new file mode 100644 index 000000000000..8eff50806aa1 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/ilitek_fw.h @@ -0,0 +1,3 @@ +static unsigned char CTPM_FW[] = { + #include "FW_TDDI_9881H.ili" +}; diff --git a/drivers/input/touchscreen/ilitek_mmi/core/mp_test.c b/drivers/input/touchscreen/ilitek_mmi/core/mp_test.c new file mode 100644 index 000000000000..1cd9b5bcb749 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/mp_test.c @@ -0,0 +1,2434 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../common.h" +#include "../platform.h" +#include "i2c.h" +#include "config.h" +#include "mp_test.h" +#include "protocol.h" +#include "parser.h" +#include "finger_report.h" + + + +#define CSV_FILE_SIZE (500 * 1024) + +#define Mathabs(x) ({ \ + long ret; \ + if (sizeof(x) == sizeof(long)) { \ + long __x = (x); \ + ret = (__x < 0) ? -__x : __x; \ + } else { \ + int __x = (x); \ + ret = (__x < 0) ? -__x : __x; \ + } \ + ret; \ + }) + +#define DUMP(level, fmt, arg...) \ + do { \ + if (level & ipio_debug_level) \ + printk(fmt, ##arg); \ + } while (0) + +enum mp_test_catalog { + MUTUAL_TEST = 0, + SELF_TEST = 1, + KEY_TEST = 2, + ST_TEST = 3, + TX_RX_DELTA = 4, + UNTOUCH_P2P = 5, + PIXEL = 6, + OPEN_TEST = 7, + PEAK_TO_PEAK_TEST = 8, + SHORT_TEST = 9, +}; + +enum open_test_node_type { + NO_COMPARE = 0x00, /*Not A Area, No Compare */ + AA_Area = 0x01, /*AA Area, Compare using Charge_AA */ + Border_Area = 0x02, /*Border Area, Compare using Charge_Border */ + Notch = 0x04, /*Notch Area, Compare using Charge_Notch */ + Round_Corner = 0x08,/* Round Corner, No Compare */ + Skip_Micro = 0x10 /* Skip_Micro, No Compare */ +}; + +/* You must declare a new test in this struct before running a new process of mp test */ +struct mp_test_items tItems[] = { + {.name = "mutual_dac", .desp = "Calibration Data(DAC)", .result = "FAIL", .catalog = MUTUAL_TEST}, + {.name = "mutual_bg", .desp = "Baseline Data(BG)", .result = "FAIL", .catalog = MUTUAL_TEST}, + {.name = "mutual_signal", .desp = "Untouch Signal Data(BG-Raw-4096) - Mutual", .result = "FAIL", .catalog = MUTUAL_TEST}, + {.name = "mutual_no_bk", .desp = "Raw Data(No BK)", .result = "FAIL", .catalog = MUTUAL_TEST}, + {.name = "mutual_no_bk_lcm_off", .desp = "Raw Data(No BK) (LCM OFF)", .result = "FAIL", .catalog = MUTUAL_TEST}, + {.name = "mutual_has_bk", .desp = "Raw Data(Have BK)", .result = "FAIL", .catalog = MUTUAL_TEST}, + {.name = "mutual_has_bk_lcm_off", .desp = "Raw Data(Have BK) (LCM OFF)", .result = "FAIL", .catalog = MUTUAL_TEST}, + {.name = "mutual_bk_dac", .desp = "Manual BK Data(Mutual)", .result = "FAIL", .catalog = MUTUAL_TEST}, + + {.name = "self_dac", .desp = "Calibration Data(DAC) - Self", .result = "FAIL", .catalog = SELF_TEST}, + {.name = "self_bg", .desp = "Baselin Data(BG,Self_Tx,Self_Rx)", .result = "FAIL", .catalog = SELF_TEST}, + {.name = "self_signal", .desp = "Untouch Signal Data(BG–Raw-4096) - Self", .result = "FAIL", .catalog = SELF_TEST}, + {.name = "self_no_bk", .desp = "Raw Data(No BK) - Self", .result = "FAIL", .catalog = SELF_TEST}, + {.name = "self_has_bk", .desp = "Raw Data(Have BK) - Self", .result = "FAIL", .catalog = SELF_TEST}, + {.name = "self_bk_dac", .desp = "Manual BK DAC Data(Self_Tx,Self_Rx)", .result = "FAIL", .catalog = SELF_TEST}, + + {.name = "key_dac", .desp = "Calibration Data(DAC/ICON)", .result = "FAIL", .catalog = KEY_TEST}, + {.name = "key_bg", .desp = "Key Baseline Data", .result = "FAIL", .catalog = KEY_TEST}, + {.name = "key_no_bk", .desp = "Key Raw Data", .result = "FAIL", .catalog = KEY_TEST}, + {.name = "key_has_bk", .desp = "Key Raw BK DAC", .result = "FAIL", .catalog = KEY_TEST}, + {.name = "key_open", .desp = "Key Raw Open Test", .result = "FAIL", .catalog = KEY_TEST}, + {.name = "key_short", .desp = "Key Raw Short Test", .result = "FAIL", .catalog = KEY_TEST}, + + {.name = "st_dac", .desp = "ST Calibration Data(DAC)", .result = "FAIL", .catalog = ST_TEST}, + {.name = "st_bg", .desp = "ST Baseline Data(BG)", .result = "FAIL", .catalog = ST_TEST}, + {.name = "st_no_bk", .desp = "ST Raw Data(No BK)", .result = "FAIL", .catalog = ST_TEST}, + {.name = "st_has_bk", .desp = "ST Raw(Have BK)", .result = "FAIL", .catalog = ST_TEST}, + {.name = "st_open", .desp = "ST Open Data", .result = "FAIL", .catalog = ST_TEST}, + + {.name = "tx_short", .desp = "Tx Short Test", .result = "FAIL", .catalog = MUTUAL_TEST}, + {.name = "rx_short", .desp = "Short Test -ILI9881", .result = "FAIL", .catalog = SHORT_TEST}, + {.name = "rx_open", .desp = "RX Open", .result = "FAIL", .catalog = MUTUAL_TEST}, + + {.name = "cm_data", .desp = "Untouch Cm Data", .result = "FAIL", .catalog = MUTUAL_TEST}, + {.name = "cs_data", .desp = "Untouch Cs Data", .result = "FAIL", .catalog = MUTUAL_TEST}, + + {.name = "tx_rx_delta", .desp = "Tx/Rx Delta", .result = "FAIL", .catalog = TX_RX_DELTA}, + + {.name = "p2p", .desp = "Untouch Peak to Peak", .result = "FAIL", .catalog = UNTOUCH_P2P}, + + {.name = "pixel_no_bk", .desp = "Pixel Raw (No BK)", .result = "FAIL", .catalog = PIXEL}, + {.name = "pixel_has_bk", .desp = "Pixel Raw (Have BK)", .result = "FAIL", .catalog = PIXEL}, + + {.name = "open_integration", .desp = "Open Test(integration)", .result = "FAIL", .catalog = OPEN_TEST}, + {.name = "open_integration_sp", .desp = "Open Test(integration)_SP", .result = "FAIL", .catalog = OPEN_TEST}, + {.name = "open_cap", .desp = "Open Test(Cap)", .result = "FAIL", .catalog = OPEN_TEST}, + + {.name = "noise_peak_to_peak_ic", .desp = "Noise Peak to Peak(IC Only)", .result = "FAIL", .catalog = PEAK_TO_PEAK_TEST}, + {.name = "noise_peak_to_peak_panel", .desp = "Noise Peak To Peak(With Panel)", .result = "FAIL", .catalog = PEAK_TO_PEAK_TEST}, + {.name = "noise_peak_to_peak_ic_lcm_off", .desp = "Noise Peak to Peak(IC Only) (LCM OFF)", .result = "FAIL", .catalog = PEAK_TO_PEAK_TEST}, + {.name = "noise_peak_to_peak_panel_lcm_off", .desp = "Noise Peak to Peak(With Panel) (LCM OFF)", .result = "FAIL", .catalog = PEAK_TO_PEAK_TEST}, + + {.name = "doze_raw", .desp = "Doze Raw Data", .result = "FAIL", .catalog = MUTUAL_TEST}, + {.name = "doze_p2p", .desp = "Doze Peak To Peak", .result = "FAIL", .catalog = PEAK_TO_PEAK_TEST}, + {.name = "doze_raw_td_lcm_off", .desp = "Raw Data_TD (LCM OFF)", .result = "FAIL", .catalog = MUTUAL_TEST}, + {.name = "doze_p2p_td_lcm_off", .desp = "Peak To Peak_TD (LCM OFF)", .result = "FAIL", .catalog = PEAK_TO_PEAK_TEST}, +}; + +int32_t *frame_buf; +int32_t *key_buf; +int32_t *frame1_cbk700 = NULL, *frame1_cbk250 = NULL, *frame1_cbk200 = NULL; +struct core_mp_test_data *core_mp; + +void dump_data(void *data, int type, int len, int row_len, const char *name) +{ + int i, row = 31; + uint8_t *p8 = NULL; + int32_t *p32 = NULL; + + if (row_len > 0) + row = row_len; + + if (ipio_debug_level & DEBUG_MP_TEST) { + if (data == NULL) { + ipio_err("The data going to dump is NULL\n"); + return; + } + + printk(KERN_CONT "\n\n"); + printk(KERN_CONT "Dump %s data\n", name); + + if (type == 8) + p8 = (uint8_t *) data; + if (type == 32 || type == 10) + p32 = (int32_t *) data; + + for (i = 0 ; i < len ; i++) { + if (type == 8) + printk(KERN_CONT " %4x ", p8[i]); + else if (type == 32) + printk(KERN_CONT " %4x ", p32[i]); + else if (type == 10) + printk(KERN_CONT " %4d ", p32[i]); + if ((i % row) == row-1) + printk(KERN_CONT "\n"); + } + printk(KERN_CONT "\n\n"); + } +} +EXPORT_SYMBOL(dump_data); + +static void dump_benchmark_data(int32_t *max_ptr, int32_t *min_ptr) +{ + int i; + + if (ipio_debug_level & DEBUG_MP_TEST) { + ipio_info("benchmark max\n"); + + for (i = 0 ; i < core_mp->frame_len ; i++) { + printk("%d, ", max_ptr[i]); + if (i % core_mp->xch_len == core_mp->xch_len - 1) + printk("\n"); + } + + ipio_info("benchmark min\n"); + + for (i = 0 ; i < core_mp->frame_len ; i++) { + printk("%d, ", min_ptr[i]); + if (i % core_mp->xch_len == core_mp->xch_len - 1) + printk("\n"); + } + } +} + +void dump_node_type_buffer(int32_t *node_ptr, uint8_t *name) +{ + int i; + + if (ipio_debug_level & DEBUG_MP_TEST) { + ipio_info("Node Type buffer: %s\n", name); + for (i = 0 ; i < core_mp->frame_len ; i++) { + printk("%d, ", node_ptr[i]); + if (i % core_mp->xch_len == core_mp->xch_len-1) + printk("\n"); + } + } + +} + +static void mp_compare_cdc_result(int32_t *tmp, int32_t *max_ts, int32_t *min_ts, int *result) +{ + int x, y; + + if (ERR_ALLOC_MEM(tmp)) { + ipio_err("The data of test item is null (%p)\n", tmp); + *result = MP_FAIL; + return; + } + + for (y = 0 ; y < core_mp->ych_len ; y++) { + for (x = 0 ; x < core_mp->xch_len ; x++) { + int shift = y * core_mp->xch_len + x; + if (tmp[shift] > max_ts[shift] || tmp[shift] < min_ts[shift]) { + *result = MP_FAIL; + } + } + } +} + + +static void mp_compare_cdc_show_result(int32_t *tmp, char *csv, int *csv_len, int type, int32_t *max_ts, int32_t *min_ts, const char *desp) +{ + int x, y, tmp_len = *csv_len; + int mp_result = MP_PASS; + + if (ERR_ALLOC_MEM(tmp)) { + ipio_err("The data of test item is null (%p)\n", tmp); + mp_result = MP_FAIL; + goto out; + } + + /* print X raw only */ + for (x = 0 ; x < core_mp->xch_len ; x++) { + if (x == 0) { + DUMP(DEBUG_MP_TEST, "\n %s ", desp); + tmp_len += snprintf(csv + tmp_len, (CSV_FILE_SIZE-tmp_len), "\n %s ,", desp); + } + + DUMP(DEBUG_MP_TEST, " X_%d ,", (x+1)); + tmp_len += snprintf(csv + tmp_len, (CSV_FILE_SIZE-tmp_len), " X_%d ,", (x+1)); + } + + DUMP(DEBUG_MP_TEST, "\n"); + tmp_len += snprintf(csv + tmp_len, (CSV_FILE_SIZE-tmp_len), "\n"); + + for (y = 0 ; y < core_mp->ych_len ; y++) { + DUMP(DEBUG_MP_TEST, " Y_%d ,", (y+1)); + tmp_len += snprintf(csv + tmp_len, (CSV_FILE_SIZE-tmp_len), " Y_%d ,", (y+1)); + + for (x = 0 ; x < core_mp->xch_len ; x++) { + int shift = y * core_mp->xch_len + x; + + if ((tmp[shift] <= max_ts[shift] && tmp[shift] >= min_ts[shift]) || (type != TYPE_JUGE)) { + + if ((tmp[shift] == INT_MAX || tmp[shift] == INT_MIN) && (type == TYPE_BENCHMARK)) { + DUMP(DEBUG_MP_TEST, "%s", "BYPASS,"); + tmp_len += snprintf(csv + tmp_len, (CSV_FILE_SIZE-tmp_len), "BYPASS,"); + } else { + DUMP(DEBUG_MP_TEST, " %7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (CSV_FILE_SIZE-tmp_len), " %7d, ", tmp[shift]); + } + } else { + if (tmp[shift] > max_ts[shift]) { + DUMP(DEBUG_MP_TEST, " *%7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (CSV_FILE_SIZE-tmp_len), "*%7d,", tmp[shift]); + } else { + DUMP(DEBUG_MP_TEST, " #%7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (CSV_FILE_SIZE-tmp_len), "#%7d,", tmp[shift]); + } + mp_result = MP_FAIL; + } + } + + DUMP(DEBUG_MP_TEST, "\n"); + tmp_len += snprintf(csv + tmp_len, (CSV_FILE_SIZE-tmp_len), "\n"); + } + +out: + if (type == TYPE_JUGE) { + if (mp_result == MP_PASS) { + DUMP(DEBUG_MP_TEST, "\n Result : PASS\n"); + tmp_len += snprintf(csv + tmp_len, (CSV_FILE_SIZE-tmp_len), "Result : PASS\n"); + } else { + DUMP(DEBUG_MP_TEST, "\n Result : FAIL\n"); + tmp_len += snprintf(csv + tmp_len, (CSV_FILE_SIZE-tmp_len), "Result : FAIL\n"); + } + } + + *csv_len = tmp_len; +} + +static int create_mp_test_frame_buffer(int index, int frame_count) +{ + //this item aready created buffer + if (tItems[index].test_count != 0) + return 0; + + ipio_debug(DEBUG_MP_TEST, "Create MP frame buffers (index = %d), count = %d\n", index, frame_count); + + if (tItems[index].catalog == TX_RX_DELTA) { + core_mp->tx_delta_buf = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + core_mp->rx_delta_buf = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + + core_mp->tx_max_buf = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + core_mp->tx_min_buf = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + + core_mp->rx_max_buf = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + core_mp->rx_min_buf = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + + if (ERR_ALLOC_MEM(core_mp->tx_delta_buf) || ERR_ALLOC_MEM(core_mp->rx_delta_buf)) { + ipio_err("Failed to allocate Tx/Rx Delta buffer\n"); + return -ENOMEM; + } + + if (ERR_ALLOC_MEM(core_mp->tx_max_buf) || ERR_ALLOC_MEM(core_mp->tx_min_buf)) { + ipio_err("Failed to allocate Tx Max/Min buffer\n"); + return -ENOMEM; + } + + if (ERR_ALLOC_MEM(core_mp->rx_max_buf) || ERR_ALLOC_MEM(core_mp->rx_min_buf)) { + ipio_err("Failed to allocate Rx Max/Min buffe\n"); + return -ENOMEM; + } + + } else { + tItems[index].buf = vmalloc(frame_count * core_mp->frame_len * sizeof(int32_t)); + tItems[index].result_buf = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + tItems[index].max_buf = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + tItems[index].min_buf = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + + if (tItems[index].spec_option == BENCHMARK) { + tItems[index].bench_mark_max = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + tItems[index].bench_mark_min = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + + if (ERR_ALLOC_MEM(tItems[index].bench_mark_max) || ERR_ALLOC_MEM(tItems[index].bench_mark_min)) { + ipio_err("Failed to allocate bench_mark FRAME buffer\n"); + return -ENOMEM; + } + } + if (ERR_ALLOC_MEM(tItems[index].buf) || ERR_ALLOC_MEM(tItems[index].max_buf) || + ERR_ALLOC_MEM(tItems[index].min_buf) || ERR_ALLOC_MEM(tItems[index].result_buf)) { + ipio_err("Failed to allocate FRAME buffer\n"); + return -ENOMEM; + } + } + + return 0; +} + +int core_mp_ctrl_lcm_status(bool on) +{ + int ret = 0, ctrl = 0, delay = 0; + uint8_t lcd[15] = {0}; + uint8_t header = 0x0F; + + memset(&lcd, 0xFF, ARRAY_SIZE(lcd)); + + ctrl = ((on) ? 1 : 2); + delay = ((on) ? 100 : 10); + + lcd[0] = header; + lcd[1] = protocol->mutual_bg; + lcd[2] = 0; + lcd[3] = ctrl; + + dump_data(lcd, 8, ARRAY_SIZE(lcd), 0, "LCM Command"); + + ret = core_write(core_config->slave_i2c_addr, lcd, ARRAY_SIZE(lcd)); + if (ret < 0) { + ipio_err("Failed to write LCM command\n"); + goto out; + } + + mdelay(delay); + +out: + return ret; +} +EXPORT_SYMBOL(core_mp_ctrl_lcm_status); + +static void mp_calc_nodp(bool long_v) +{ + uint8_t at, phase; + uint8_t r2d, rst, rst_back, dac_td, qsh_pw, qsh_td, dn; + uint16_t tshd, tsvd_to_tshd, qsh_tdf, dp2tp, twc, twcm, tp2dp; + uint16_t multi_term_num, tp_tsdh_wait, ddi_width; + uint32_t real_tx_dura, tmp, tmp1; + + ipio_info("DDI Mode = %d\n", long_v); + + tshd = core_mp->nodp.tshd; + tsvd_to_tshd = core_mp->nodp.tsvd_to_tshd; + multi_term_num = core_mp->nodp.multi_term_num_120; + qsh_tdf = core_mp->nodp.qsh_tdf; + at = core_mp->nodp.auto_trim; + tp_tsdh_wait = core_mp->nodp.tp_tshd_wait_120; + ddi_width = core_mp->nodp.ddi_width_120; + dp2tp = core_mp->nodp.dp_to_tp; + twc = core_mp->nodp.tx_wait_const; + twcm = core_mp->nodp.tx_wait_const_multi; + tp2dp = core_mp->nodp.tp_to_dp; + phase = core_mp->nodp.phase_adc; + r2d = core_mp->nodp.r2d_pw; + rst = core_mp->nodp.rst_pw; + rst_back = core_mp->nodp.rst_pw_back; + dac_td = core_mp->nodp.dac_td; + qsh_pw = core_mp->nodp.qsh_pw; + qsh_td = core_mp->nodp.qsh_td; + dn = core_mp->nodp.drop_nodp; + + /* NODP formulation */ + if (!long_v) { + if (core_mp->nodp.is60HZ) { + multi_term_num = core_mp->nodp.multi_term_num_60; + tp_tsdh_wait = core_mp->nodp.tp_tshd_wait_60; + ddi_width = core_mp->nodp.ddi_width_60; + } + + if (multi_term_num == 0) + multi_term_num = 1; + + tmp = ((tshd << 2) - (at << 6) - tp_tsdh_wait - ddi_width * (multi_term_num - 1) - 64 - dp2tp - twc) * 5; + tmp1 = (phase << 5) - ((twcm * 5 + (phase << 5) + (tp2dp * 5 << 6)) * (multi_term_num - 1)); + real_tx_dura = (tmp - tmp1) / (multi_term_num * 5); + + core_mp->nodp.first_tp_width = (dp2tp * 5 + twc * 5 + (phase << 5) + real_tx_dura * 5) / 5; + core_mp->nodp.tp_width = (((tp2dp * 10 + phase) << 6) + real_tx_dura * 10) / 10; + core_mp->nodp.txpw = (qsh_tdf + rst + qsh_pw + qsh_td + 2); + + if (core_mp->nodp.txpw % 2 == 1) + core_mp->nodp.txpw = core_mp->nodp.txpw + 1; + + core_mp->nodp.nodp = real_tx_dura / core_mp->nodp.txpw / 2; + } else { + if (multi_term_num == 0) + multi_term_num = 1; + + real_tx_dura = (((tshd << 2) - (at << 6) - ddi_width * (11) - 64 - dp2tp - twc) * 5 - (phase << 5) - ((twcm * 5 + (phase << 5) + (tp2dp * 5 << 6)) * (11))) / (12 * 5); + + core_mp->nodp.long_tsdh_wait = (tsvd_to_tshd + 10) << 6; + + core_mp->nodp.first_tp_width = (dp2tp * 5 + twc * 5 + (phase << 5) + real_tx_dura * 5) / 5; + core_mp->nodp.tp_width = (((tp2dp * 10 + phase) << 6) + real_tx_dura * 10) / 10; + core_mp->nodp.txpw = (qsh_tdf + rst + qsh_pw + qsh_td + 2); + + if (core_mp->nodp.txpw % 2 == 1) + core_mp->nodp.txpw = core_mp->nodp.txpw + 1; + + core_mp->nodp.nodp = real_tx_dura / core_mp->nodp.txpw / 2; + } + + ipio_info("Read Tx Duration = %d\n", real_tx_dura); + ipio_info("First TP Width = %d\n", core_mp->nodp.first_tp_width); + ipio_info("TP Width = %d\n", core_mp->nodp.tp_width); + ipio_info("TXPW = %d\n", core_mp->nodp.txpw); + ipio_info("NODP = %d\n", core_mp->nodp.nodp); +} + +static int allnode_key_cdc_data(int index) +{ + int i, res = 0, len = 0; + int inDACp = 0, inDACn = 0; + uint8_t cmd[3] = { 0 }; + uint8_t *ori = NULL; + + len = core_mp->key_len * 2; + + ipio_debug(DEBUG_MP_TEST, "Read key's length = %d\n", len); + ipio_debug(DEBUG_MP_TEST, "core_mp->key_len = %d\n", core_mp->key_len); + + if (len <= 0) { + ipio_err("Length is invalid\n"); + res = ERROR; + goto out; + } + + /* CDC init */ + cmd[0] = protocol->cmd_cdc; + cmd[1] = tItems[index].cmd; + cmd[2] = 0; + + res = core_write(core_config->slave_i2c_addr, cmd, 3); + if (res < 0) { + ipio_err("I2C Write Error while initialising cdc\n"); + goto out; + } + + mdelay(1); + + /* Check busy */ + if (core_config_check_cdc_busy(50, 50) < 0) { + ipio_err("Check busy is timout !\n"); + res = ERROR; + goto out; + } + + mdelay(1); + + /* Prepare to get cdc data */ + cmd[0] = protocol->cmd_read_ctrl; + cmd[1] = protocol->cmd_get_cdc; + + res = core_write(core_config->slave_i2c_addr, cmd, 2); + if (res < 0) { + ipio_err("I2C Write Error\n"); + goto out; + } + + mdelay(1); + + res = core_write(core_config->slave_i2c_addr, &cmd[1], 1); + if (res < 0) { + ipio_err("I2C Write Error\n"); + goto out; + } + + /* Allocate a buffer for the original */ + ori = kcalloc(len, sizeof(uint8_t), GFP_KERNEL); + if (ERR_ALLOC_MEM(ori)) { + ipio_err("Failed to allocate ori mem (%ld)\n", PTR_ERR(ori)); + goto out; + } + + mdelay(1); + + /* Get original frame(cdc) data */ + res = core_read(core_config->slave_i2c_addr, ori, len); + if (res < 0) { + ipio_err("I2C Read Error while getting original cdc data\n"); + goto out; + } + + dump_data(ori, 8, len, 0, "Key CDC original"); + + if (key_buf == NULL) { + key_buf = kcalloc(core_mp->key_len, sizeof(int32_t), GFP_KERNEL); + if (ERR_ALLOC_MEM(key_buf)) { + ipio_err("Failed to allocate FrameBuffer mem (%ld)\n", PTR_ERR(key_buf)); + goto out; + } + } else { + memset(key_buf, 0x0, core_mp->key_len); + } + + /* Convert original data to the physical one in each node */ + for (i = 0 ; i < core_mp->frame_len ; i++) { + if (tItems[index].cmd == protocol->key_dac) { + /* DAC - P */ + if (((ori[(2 * i) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACp = 0 - (int)(ori[(2 * i) + 1] & 0x7F); + } else { + inDACp = ori[(2 * i) + 1] & 0x7F; + } + + /* DAC - N */ + if (((ori[(1 + (2 * i)) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACn = 0 - (int)(ori[(1 + (2 * i)) + 1] & 0x7F); + } else { + inDACn = ori[(1 + (2 * i)) + 1] & 0x7F; + } + + key_buf[i] = (inDACp + inDACn) / 2; + } + } + + dump_data(key_buf, 32, core_mp->frame_len, core_mp->xch_len, "Key CDC combined data"); + +out: + ipio_kfree((void **)&ori); + return res; +} + +int core_mp_calc_timing_nodp(void) +{ + int ret = 0; + uint8_t test_type = 0x0; + uint8_t timing_cmd[15] = {0}; + uint8_t get_timing[64] = {0}; + + if (ERR_ALLOC_MEM(core_mp)) { + ipio_err("core_mp is NULL\n"); + return -ENOMEM; + } + + memset(timing_cmd, 0xFF, sizeof(timing_cmd)); + + timing_cmd[0] = protocol->cmd_cdc; + timing_cmd[1] = protocol->get_timing; + timing_cmd[2] = test_type; + + /* + * To calculate NODP, we need to get timing parameters first from fw, + * which returnes 40 bytes data. + */ + dump_data(timing_cmd, 8, sizeof(timing_cmd), 0, "Timing command"); + + ret = core_write(core_config->slave_i2c_addr, timing_cmd, sizeof(timing_cmd)); + if (ret < 0) { + ipio_err("Failed to write timing command\n"); + goto out; + } + + ret = core_read(core_config->slave_i2c_addr, get_timing, sizeof(get_timing)); + if (ret < 0) { + ipio_err("Failed to read timing parameters\n"); + goto out; + } + + dump_data(get_timing, 8, 41, 0, "Timing parameters (41bytes)"); + + /* Combine timing data */ + core_mp->nodp.is60HZ = false; // This will get from ini file by default. + core_mp->nodp.isLongV = get_timing[2]; + core_mp->nodp.tshd = (get_timing[3] << 8) + get_timing[4]; + core_mp->nodp.multi_term_num_120 = get_timing[5]; + core_mp->nodp.multi_term_num_60 = get_timing[6]; + core_mp->nodp.tsvd_to_tshd = (get_timing[7] << 8) + get_timing[8]; + core_mp->nodp.qsh_tdf = (get_timing[9] << 8) + get_timing[10]; + core_mp->nodp.auto_trim = get_timing[11]; + core_mp->nodp.tp_tshd_wait_120 = (get_timing[12] << 8) + get_timing[13]; + core_mp->nodp.ddi_width_120 = (get_timing[14] << 8) + get_timing[15]; + core_mp->nodp.tp_tshd_wait_60 = (get_timing[16] << 8) + get_timing[17]; + core_mp->nodp.ddi_width_60 = (get_timing[18] << 8) + get_timing[19]; + core_mp->nodp.dp_to_tp = (get_timing[20] << 8) + get_timing[21]; + core_mp->nodp.tx_wait_const = (get_timing[22] << 8) + get_timing[23]; + core_mp->nodp.tx_wait_const_multi = (get_timing[24] << 8) + get_timing[25]; + core_mp->nodp.tp_to_dp = (get_timing[26] << 8) + get_timing[27]; + core_mp->nodp.phase_adc = get_timing[28]; + core_mp->nodp.r2d_pw = get_timing[29]; + core_mp->nodp.rst_pw = get_timing[30]; + core_mp->nodp.rst_pw_back = get_timing[31]; + core_mp->nodp.dac_td = get_timing[32]; + core_mp->nodp.qsh_pw = get_timing[33]; + core_mp->nodp.qsh_td = get_timing[34]; + core_mp->nodp.drop_nodp = get_timing[35]; + + ipio_info("60HZ = %d\n", core_mp->nodp.is60HZ); + ipio_info("DDI Mode = %d\n", core_mp->nodp.isLongV); + ipio_info("TSHD = %d\n", core_mp->nodp.tshd); + ipio_info("Multi Term Num (120Hz) = %d\n", core_mp->nodp.multi_term_num_120); + ipio_info("Multi Term Num (60Hz) = %d\n", core_mp->nodp.multi_term_num_60); + ipio_info("TSVD to TSHD = %d\n", core_mp->nodp.tsvd_to_tshd); + ipio_info("QSH TDF = %d\n", core_mp->nodp.qsh_tdf); + ipio_info("AutoTrim Variation = %d\n", core_mp->nodp.auto_trim); + ipio_info("TP TSHD Wait (120Hz) = %d\n", core_mp->nodp.tp_tshd_wait_120); + ipio_info("DDI Width (120Hz) = %d\n", core_mp->nodp.ddi_width_120); + ipio_info("TP TSHD Wait (60Hz) = %d\n", core_mp->nodp.tp_tshd_wait_60); + ipio_info("DDI Width (60Hz) = %d\n", core_mp->nodp.ddi_width_60); + ipio_info("DP to TP = %d\n", core_mp->nodp.dp_to_tp); + ipio_info("TX Wait Const = %d\n", core_mp->nodp.tx_wait_const); + ipio_info("TX Wait Const Multi = %d\n", core_mp->nodp.tx_wait_const_multi); + ipio_info("TP to DP = %d\n", core_mp->nodp.tp_to_dp); + ipio_info("Phase ADC = %d\n", core_mp->nodp.phase_adc); + ipio_info("R2D PW = %d\n", core_mp->nodp.r2d_pw); + ipio_info("RST PW = %d\n", core_mp->nodp.rst_pw); + ipio_info("RST PW Back = %d\n", core_mp->nodp.rst_pw_back); + ipio_info("DAC TD = %d\n", core_mp->nodp.dac_td); + ipio_info("QSH PW = %d\n", core_mp->nodp.qsh_pw); + ipio_info("QSH TD = %d\n", core_mp->nodp.qsh_td); + ipio_info("Drop NODP Num = %d\n", core_mp->nodp.drop_nodp); + + mp_calc_nodp(core_mp->nodp.isLongV); + +out: + return ret; +} +EXPORT_SYMBOL(core_mp_calc_timing_nodp); + +static int mp_cdc_get_pv5_4_command(uint8_t *cmd, int len, int index) +{ + int ret = 0; + char str[128] = {0}; + char tmp[128] = {0}; + char *key = tItems[index].desp; + + if (strncmp(key, "Raw Data_TD (LCM OFF)", strlen(key)) == 0) + key = "Doze Raw Data"; + else if (strncmp(key, "Peak To Peak_TD (LCM OFF)", strlen(key)) == 0) + key = "Doze Peak To Peak"; + else if (strncmp(key, "Raw Data(No BK) (LCM OFF)", strlen(key)) == 0) + key = "Raw Data(No BK)"; + else if (strncmp(key, "Raw Data(Have BK) (LCM OFF)", strlen(key)) == 0) + key = "Raw Data(Have BK)"; + else if (strncmp(key, "Noise Peak to Peak(With Panel) (LCM OFF)", strlen(key)) == 0) + key = "Noise Peak To Peak(With Panel)"; + + ipio_debug(DEBUG_MP_TEST, "%s gets %s command from INI.\n", tItems[index].desp, key); + + ret = core_parser_get_int_data("PV5_4 Command", key, str); + if (ret < 0) { + ipio_err("Failed to parse PV54 command, ret = %d\n", ret); + goto out; + } + + strlcpy(tmp, str, sizeof(tmp)); + core_parser_get_u8_array(tmp, cmd); + +out: + return ret; +} + +static int mp_cdc_init_cmd_common(uint8_t *cmd, int len, int index) +{ + int ret = 0; + + if (protocol->major >= 5 && protocol->mid >= 4) { + ipio_debug(DEBUG_MP_TEST, "Get CDC command with protocol v5.4\n"); + protocol->cdc_len = 15; + return mp_cdc_get_pv5_4_command(cmd, len, index); + } + + cmd[0] = protocol->cmd_cdc; + cmd[1] = tItems[index].cmd; + cmd[2] = 0; + + protocol->cdc_len = 3; + + if (strcmp(tItems[index].name, "open_integration") == 0) + cmd[2] = 0x2; + if (strcmp(tItems[index].name, "open_cap") == 0) + cmd[2] = 0x3; + + if (tItems[index].catalog == PEAK_TO_PEAK_TEST) { + cmd[2] = ((tItems[index].frame_count & 0xff00) >> 8); + cmd[3] = tItems[index].frame_count & 0xff; + cmd[4] = 0; + + protocol->cdc_len = 5; + + if (strcmp(tItems[index].name, "noise_peak_to_peak_cut") == 0) + cmd[4] = 0x1; + + ipio_debug(DEBUG_MP_TEST, "P2P CMD: %d,%d,%d,%d,%d\n", + cmd[0], cmd[1], cmd[2], cmd[3], cmd[4]); + } + + return ret; +} + +static int allnode_mutual_cdc_data(int index) +{ + static int i, res, len; + int inDACp = 0, inDACn = 0; + static uint8_t cmd[15] = {0}; + uint8_t *ori = NULL; + + /* Multipling by 2 is due to the 16 bit in each node */ + len = (core_mp->xch_len * core_mp->ych_len * 2) + 2; + + ipio_debug(DEBUG_MP_TEST, "Read X/Y Channel length = %d\n", len); + ipio_debug(DEBUG_MP_TEST, "core_mp->frame_len = %d\n", core_mp->frame_len); + + if (len <= 2) { + ipio_err("Length is invalid\n"); + res = ERROR; + goto out; + } + + memset(cmd, 0xFF, protocol->cdc_len); + + /* CDC init */ + mp_cdc_init_cmd_common(cmd, protocol->cdc_len, index); + + dump_data(cmd, 8, protocol->cdc_len, 0, "Mutual CDC command"); + + core_config->interrupt_flag = false; + res = core_write(core_config->slave_i2c_addr, cmd, protocol->cdc_len); + if (res < 0) { + ipio_err("I2C Write Error while initialising cdc\n"); + goto out; + } + + /* Check busy */ + if (core_mp->busy_cdc == POLL_CHECK) { + res = core_config_check_cdc_busy(50, 50); + } else if (core_mp->busy_cdc == INT_CHECK) { + res = core_config_check_int_status(true); + } else if (core_mp->busy_cdc == DELAY_CHECK) { + mdelay(600); + } else if (core_mp->busy_cdc == ISR_CHECK) { + core_config_check_int_isr_flag(); + } + + if (res < 0) { + ipio_err("Check busy timeout ! %d\n", core_mp->busy_cdc); + res = ERROR; + goto out; + } + + /* Prepare to get cdc data */ + cmd[0] = protocol->cmd_read_ctrl; + cmd[1] = protocol->cmd_get_cdc; + + res = core_write(core_config->slave_i2c_addr, cmd, 2); + if (res < 0) { + ipio_err("I2C Write Error\n"); + goto out; + } + + mdelay(1); + + res = core_write(core_config->slave_i2c_addr, &cmd[1], 1); + if (res < 0) { + ipio_err("I2C Write Error\n"); + goto out; + } + + mdelay(1); + + /* Allocate a buffer for the original */ + ori = kcalloc(len, sizeof(uint8_t), GFP_KERNEL); + if (ERR_ALLOC_MEM(ori)) { + ipio_err("Failed to allocate ori mem (%ld)\n", PTR_ERR(ori)); + goto out; + } + + /* Get original frame(cdc) data */ + res = core_read(core_config->slave_i2c_addr, ori, len); + if (res < 0) { + ipio_err("I2C Read Error while getting original cdc data\n"); + goto out; + } + + dump_data(ori, 8, len, 0, "Mutual CDC original"); + + if (frame_buf == NULL) { + frame_buf = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + if (ERR_ALLOC_MEM(frame_buf)) { + ipio_err("Failed to allocate FrameBuffer mem (%ld)\n", PTR_ERR(frame_buf)); + goto out; + } + } else { + memset(frame_buf, 0x0, core_mp->frame_len); + } + + /* Convert original data to the physical one in each node */ + for (i = 0 ; i < core_mp->frame_len ; i++) { + if (tItems[index].cmd == protocol->mutual_dac) { + /* DAC - P */ + if (((ori[(2 * i) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACp = 0 - (int)(ori[(2 * i) + 1] & 0x7F); + } else { + inDACp = ori[(2 * i) + 1] & 0x7F; + } + + /* DAC - N */ + if (((ori[(1 + (2 * i)) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACn = 0 - (int)(ori[(1 + (2 * i)) + 1] & 0x7F); + } else { + inDACn = ori[(1 + (2 * i)) + 1] & 0x7F; + } + + frame_buf[i] = (inDACp + inDACn) / 2; + } else { + /* H byte + L byte */ + int32_t tmp = (ori[(2 * i) + 1] << 8) + ori[(1 + (2 * i)) + 1]; + + if ((tmp & 0x8000) == 0x8000) + frame_buf[i] = tmp - 65536; + else + frame_buf[i] = tmp; + + if (strncmp(tItems[index].name, "mutual_no_bk", strlen("mutual_no_bk")) == 0 || + strncmp(tItems[index].name, "mutual_no_bk_lcm_off", strlen("mutual_no_bk_lcm_off")) == 0) { + if (core_config->chip_id == CHIP_TYPE_ILI9881) { + if (core_config->chip_type == ILI9881_TYPE_H) + frame_buf[i] -= RAWDATA_NO_BK_DATA_SHIFT_9881H; + if (core_config->chip_type == ILI9881_TYPE_F) + frame_buf[i] -= RAWDATA_NO_BK_DATA_SHIFT_9881F; + } + } + } + } + + dump_data(frame_buf, 32, core_mp->frame_len, core_mp->xch_len, "Mutual CDC combined"); + +out: + ipio_kfree((void **)&ori); + return res; +} + +static void run_pixel_test(int index) +{ + int i, x, y; + int32_t *p_comb = frame_buf; + + for (y = 0 ; y < core_mp->ych_len ; y++) { + for (x = 0 ; x < core_mp->xch_len ; x++) { + int tmp[4] = { 0 }, max = 0; + int shift = y * core_mp->xch_len; + int centre = p_comb[shift + x]; + + /* if its position is in corner, the number of point + we have to minus is around 2 to 3. */ + if (y == 0 && x == 0) { + tmp[0] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[1] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y == (core_mp->ych_len - 1) && x == 0) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y == 0 && x == (core_mp->xch_len - 1)) { + tmp[0] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + } else if (y == (core_mp->ych_len - 1) && x == (core_mp->xch_len - 1)) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + } else if (y == 0 && x != 0) { + tmp[0] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[2] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y != 0 && x == 0) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + tmp[2] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + + } else if (y == (core_mp->ych_len - 1) && x != 0) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[2] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y != 0 && x == (core_mp->xch_len - 1)) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[2] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + } else { + /* middle minus four directions */ + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[2] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[3] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } + + max = tmp[0]; + + for (i = 0; i < 4; i++) { + if (tmp[i] > max) + max = tmp[i]; + } + + tItems[index].buf[shift + x] = max; + } + } +} + +static int run_open_test(int index) +{ + int i, x, y, k, res = 0; + int border_x[] = {-1, 0, 1, 1, 1, 0, -1, -1}; + int border_y[] = {-1, -1, -1, 0, 1, 1, 1, 0}; + int32_t *p_comb = frame_buf; + + if (strcmp(tItems[index].name, "open_integration") == 0) { + for (i = 0 ; i < core_mp->frame_len ; i++) + tItems[index].buf[i] = p_comb[i]; + } else if (strcmp(tItems[index].name, "open_cap") == 0) { + /* Each result is getting from a 3 by 3 grid depending on where the centre location is. + So if the centre is at corner, the number of node grabbed from a grid will be different. */ + for (y = 0 ; y < core_mp->ych_len ; y++) { + for (x = 0 ; x < core_mp->xch_len ; x++) { + int sum = 0, avg = 0, count = 0; + int shift = y * core_mp->xch_len; + int centre = p_comb[shift + x]; + + for (k = 0 ; k < 8; k++) { + if (((y + border_y[k] >= 0) && (y + border_y[k] < core_mp->ych_len)) && + ((x + border_x[k] >= 0) && (x + border_x[k] < core_mp->xch_len))) { + count++; + sum += p_comb[(y + border_y[k]) * core_mp->xch_len + (x + border_x[k])]; + } + } + + avg = (sum + centre) / (count + 1); /* plus 1 because of centre */ + tItems[index].buf[shift + x] = (centre * 100) / avg; + } + } + } + return res; +} + +static void run_tx_rx_delta_test(int index) +{ + int x, y; + int32_t *p_comb = frame_buf; + + for (y = 0 ; y < core_mp->ych_len ; y++) { + for (x = 0 ; x < core_mp->xch_len ; x++) { + int shift = y * core_mp->xch_len; + + /* Tx Delta */ + if (y != (core_mp->ych_len - 1)) { + core_mp->tx_delta_buf[shift + x] = Mathabs(p_comb[shift + x] - p_comb[(shift + 1) + x]); + } + + /* Rx Delta */ + if (x != (core_mp->xch_len - 1)) { + core_mp->rx_delta_buf[shift + x] = Mathabs(p_comb[shift + x] - p_comb[shift + (x + 1)]); + } + } + } +} + +static void run_untouch_p2p_test(int index) +{ + int x, y; + int32_t *p_comb = frame_buf; + + for (y = 0 ; y < core_mp->ych_len ; y++) { + for (x = 0 ; x < core_mp->xch_len ; x++) { + int shift = y * core_mp->xch_len; + + if (p_comb[shift + x] > tItems[index].max_buf[shift + x]) { + tItems[index].max_buf[shift + x] = p_comb[shift + x]; + } + + if (p_comb[shift + x] < tItems[index].min_buf[shift + x]) { + tItems[index].min_buf[shift + x] = p_comb[shift + x]; + } + + tItems[index].buf[shift + x] = + tItems[index].max_buf[shift + x] - tItems[index].min_buf[shift + x]; + } + } +} + +static void compare_MaxMin_result(int index, int32_t *data) +{ + int x, y; + + for (y = 0 ; y < core_mp->ych_len ; y++) { + for (x = 0 ; x < core_mp->xch_len ; x++) { + int shift = y * core_mp->xch_len; + + if (tItems[index].catalog == UNTOUCH_P2P) + return; + else if (tItems[index].catalog == TX_RX_DELTA) { + /* Tx max/min comparison */ + if (core_mp->tx_delta_buf[shift + x] < data[shift + x]) { + core_mp->tx_max_buf[shift + x] = data[shift + x]; + } + + if (core_mp->tx_delta_buf[shift + x] > data[shift + x]) { + core_mp->tx_min_buf[shift + x] = data[shift + x]; + } + + /* Rx max/min comparison */ + if (core_mp->rx_delta_buf[shift + x] < data[shift + x]) { + core_mp->rx_max_buf[shift + x] = data[shift + x]; + } + + if (core_mp->rx_delta_buf[shift + x] > data[shift + x]) { + core_mp->rx_min_buf[shift + x] = data[shift + x]; + } + } else { + if (tItems[index].max_buf[shift + x] < data[shift + x]) { + tItems[index].max_buf[shift + x] = data[shift + x]; + } + + if (tItems[index].min_buf[shift + x] > data[shift + x]) { + tItems[index].min_buf[shift + x] = data[shift + x]; + } + } + } + } +} + + +#define ABS(a, b) ((a > b) ? (a - b) : (b - a)) +#define ADDR(x, y) ((y*core_mp->xch_len)+(x)) + +int full_open_rate_compare(int32_t *full_open, int32_t *cbk, int x, int y, int32_t inNodeType, int full_open_rate) +{ + int ret = true; + + if ((inNodeType == NO_COMPARE) || ((inNodeType & Round_Corner) == Round_Corner)) { + return true; + } + + if (full_open[ADDR(x, y)] < (cbk[ADDR(x, y)] * full_open_rate / 100)) + ret = false; + + return ret; +} + + +int compare_charge(int32_t *charge_rate, int x, int y, int32_t *inNodeType, int Charge_AA, int Charge_Border, int Charge_Notch) +{ + int OpenThreadhold, tempY, tempX, ret, k; + int sx[8] = { -1, 0, 1, -1, 1, -1, 0, 1 }; + int sy[8] = { -1, -1, -1, 0, 0, 1, 1, 1 }; + + ret = charge_rate[ADDR(x, y)]; + + /*Setting Threadhold from node type */ + + if (charge_rate[ADDR(x, y)] == 0) + return ret; + else if ((inNodeType[ADDR(x, y)] & AA_Area) == AA_Area) + OpenThreadhold = Charge_AA; + else if ((inNodeType[ADDR(x, y)] & Border_Area) == Border_Area) + OpenThreadhold = Charge_Border; + else if ((inNodeType[ADDR(x, y)] & Notch) == Notch) + OpenThreadhold = Charge_Notch; + else + return ret; + + /*compare carge rate with 3*3 node*/ + /*by pass => 1.no compare 2.corner 3.Skip_Micro 4.full open fail node*/ + for (k = 0 ; k < 8; k++) { + tempX = x + sx[k]; + tempY = y + sy[k]; + + if ((tempX < 0) || (tempX >= core_mp->xch_len) || (tempY < 0) || (tempY >= core_mp->ych_len)) /*out of range */ + continue; + + if ((inNodeType[ADDR(tempX, tempY)] == NO_COMPARE) || ((inNodeType[ADDR(tempX, tempY)] & Round_Corner) == Round_Corner) || + ((inNodeType[ADDR(tempX, tempY)] & Skip_Micro) == Skip_Micro) || charge_rate[ADDR(tempX, tempY)] == 0) + continue; + + if ((charge_rate[ADDR(tempX, tempY)] - charge_rate[ADDR(x, y)]) > OpenThreadhold) + return OpenThreadhold; + } + return ret; +} + +/* This will be merged to allnode_mutual_cdc_data in next version */ +int allnode_open_cdc_data(int mode, int *buf, int *dac) +{ + int i = 0, res = 0, len = 0; + int inDACp = 0, inDACn = 0; + uint8_t cmd[15] = {0}; + uint8_t *ori = NULL; + char str[128] = {0}; + char tmp[128] = {0}; + char *key[] = {"OPEN DAC", "OPEN Raw1", "OPEN Raw2", "OPEN Raw3"}; + + /* Multipling by 2 is due to the 16 bit in each node */ + len = (core_mp->xch_len * core_mp->ych_len * 2) + 2; + + ipio_debug(DEBUG_MP_TEST, "Read X/Y Channel length = %d\n", len); + ipio_debug(DEBUG_MP_TEST, "core_mp->frame_len = %d, mode= %d\n", core_mp->frame_len, mode); + + if (len <= 2) { + ipio_err("Length is invalid\n"); + res = ERROR; + goto out; + } + + /* CDC init. Read command from ini file */ + res = core_parser_get_int_data("PV5_4 Command", key[mode], str); + if (res < 0) { + ipio_err("Failed to parse PV54 command, res = %d\n", res); + goto out; + } + + strlcpy(tmp, str, sizeof(tmp)); + core_parser_get_u8_array(tmp, cmd); + + dump_data(cmd, 8, sizeof(cmd), 0, "Open SP command"); + core_config->interrupt_flag = false; + res = core_write(core_config->slave_i2c_addr, cmd, protocol->cdc_len); + if (res < 0) { + ipio_err("I2C Write Error while initialising cdc\n"); + goto out; + } + + /* Check busy */ + if (core_mp->busy_cdc == POLL_CHECK) { + res = core_config_check_cdc_busy(50, 50); + } else if (core_mp->busy_cdc == INT_CHECK) { + res = core_config_check_int_status(true); + } else if (core_mp->busy_cdc == DELAY_CHECK) { + mdelay(600); + } else if (core_mp->busy_cdc == ISR_CHECK) { + core_config_check_int_isr_flag(); + } + if (res < 0) { + ipio_err("Check busy timeout ! %d\n", core_mp->busy_cdc); + res = ERROR; + goto out; + } + + /* Prepare to get cdc data */ + cmd[0] = protocol->cmd_read_ctrl; + cmd[1] = protocol->cmd_get_cdc; + + res = core_write(core_config->slave_i2c_addr, cmd, 2); + if (res < 0) { + ipio_err("I2C Write Error\n"); + goto out; + } + + mdelay(1); + + res = core_write(core_config->slave_i2c_addr, &cmd[1], 1); + if (res < 0) { + ipio_err("I2C Write Error\n"); + goto out; + } + + mdelay(1); + + /* Allocate a buffer for the original */ + ori = kcalloc(len, sizeof(uint8_t), GFP_KERNEL); + if (ERR_ALLOC_MEM(ori)) { + ipio_err("Failed to allocate ori mem (%ld)\n", PTR_ERR(ori)); + goto out; + } + + /* Get original frame(cdc) data */ + res = core_read(core_config->slave_i2c_addr, ori, len); + if (res < 0) { + ipio_err("I2C Read Error while getting original cdc data\n"); + goto out; + } + + dump_data(ori, 8, len, 0, "Open SP CDC original"); + + /* Convert original data to the physical one in each node */ + for (i = 0 ; i < core_mp->frame_len ; i++) { + if (mode == 0) { + /* DAC - P */ + if (((ori[(2 * i) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACp = 0 - (int)(ori[(2 * i) + 1] & 0x7F); + } else { + inDACp = ori[(2 * i) + 1] & 0x7F; + } + + /* DAC - N */ + if (((ori[(1 + (2 * i)) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACn = 0 - (int)(ori[(1 + (2 * i)) + 1] & 0x7F); + } else { + inDACn = ori[(1 + (2 * i)) + 1] & 0x7F; + } + + buf[i] = (inDACp + inDACn) / 2; + } else { + /* H byte + L byte */ + int32_t tmp = (ori[(2 * i) + 1] << 8) + ori[(1 + (2 * i)) + 1]; + if ((tmp & 0x8000) == 0x8000) + buf[i] = tmp - 65536; + else + buf[i] = tmp; + buf[i] = (int)((int)(dac[i] * 2 * 10000 * 161 / 100) - (int)(16384 / 2 - (int)buf[i]) * 20000 * 7 / 16384 * 36 / 10) / 31 / 2; + } + } + dump_data(buf, 10, core_mp->frame_len, core_mp->xch_len, "Open SP CDC combined"); +out: + ipio_kfree((void **)&ori); + + return res; +} + +static int open_test_sp(int index) +{ + struct mp_test_P540_open open[tItems[index].frame_count]; + int i = 0, x = 0, y = 0, res = 0, addr; + int Charge_AA = 0, Charge_Border = 0, Charge_Notch = 0, full_open_rate = 0; + char str[512] = { 0 }; + + ipio_debug(DEBUG_MP_TEST, "index = %d, name = %s, CMD = 0x%x, Frame Count = %d\n", + index, tItems[index].name, tItems[index].cmd, tItems[index].frame_count); + + /* + * We assume that users who are calling the test forget to config frame count + * as 1, so we just help them to set it up. + */ + if (tItems[index].frame_count <= 0) { + ipio_err("Frame count is zero, which is at least set as 1\n"); + tItems[index].frame_count = 1; + } + + if (tItems[index].test_count == 0) { + res = create_mp_test_frame_buffer(index, tItems[index].frame_count); + if (res < 0) + goto out; + frame1_cbk700 = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + frame1_cbk250 = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + frame1_cbk200 = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + + if (ERR_ALLOC_MEM(frame1_cbk700) || ERR_ALLOC_MEM(frame1_cbk250) || ERR_ALLOC_MEM(frame1_cbk200)) { + ipio_err("Failed to allocate cbk buffer\n"); + return -ENOMEM; + } + } + + tItems[index].node_type = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].node_type)) { + ipio_err("Failed to allocate node_type FRAME buffer\n"); + return -ENOMEM; + } + + /* Init Max/Min buffer */ + for (y = 0 ; y < core_mp->ych_len ; y++) { + for (x = 0 ; x < core_mp->xch_len ; x++) { + tItems[index].max_buf[y * core_mp->xch_len + x] = INT_MIN; + tItems[index].min_buf[y * core_mp->xch_len + x] = INT_MAX; + } + } + + if (tItems[index].spec_option == BENCHMARK) { + core_parser_benchmark(tItems[index].bench_mark_max, tItems[index].bench_mark_min, tItems[index].type_option, tItems[index].desp); + if (ipio_debug_level && DEBUG_PARSER > 0) + dump_benchmark_data(tItems[index].bench_mark_max, tItems[index].bench_mark_min); + } + + core_parser_nodetype(tItems[index].node_type, "Node Type"); + if (ipio_debug_level && DEBUG_PARSER > 0) + dump_node_type_buffer(tItems[index].node_type, "node type"); + + res = core_parser_get_int_data(tItems[index].desp, "Charge_AA", str); + if (res || res == 0) + Charge_AA = katoi(str); + + res = core_parser_get_int_data(tItems[index].desp, "Charge_Border", str); + if (res || res == 0) + Charge_Border = katoi(str); + + res = core_parser_get_int_data(tItems[index].desp, "Charge_Notch", str); + if (res || res == 0) + Charge_Notch = katoi(str); + + res = core_parser_get_int_data(tItems[index].desp, "Full Open", str); + if (res || res == 0) + full_open_rate = katoi(str); + + if (res < 0) { + ipio_err("Failed to get parameters from ini file\n"); + goto out; + } + + ipio_debug(DEBUG_MP_TEST, "pen test frame_cont %d, AA %d,Border %d, Notch %d, full_open_rate %d \n", + tItems[index].frame_count, Charge_AA, Charge_Border, Charge_Notch, full_open_rate); + + for (i = 0 ; i < tItems[index].frame_count ; i++) { + open[i].cbk_700 = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + open[i].cbk_250 = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + open[i].cbk_200 = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + open[i].charg_rate = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + open[i].full_Open = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + open[i].dac = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + open[i].cdc = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + + if (ERR_ALLOC_MEM(open[i].cbk_700) || ERR_ALLOC_MEM(open[i].cbk_250) || + ERR_ALLOC_MEM(open[i].cbk_200) || ERR_ALLOC_MEM(open[i].charg_rate) || + ERR_ALLOC_MEM(open[i].full_Open) || ERR_ALLOC_MEM(open[i].dac) || + ERR_ALLOC_MEM(open[i].cdc)) { + ipio_err("Failed to allocate open test buffer\n"); + goto out; + } + } + + for (i = 0 ; i < tItems[index].frame_count ; i++) { + + res = allnode_open_cdc_data(0, open[i].dac, open[i].dac); + if (res < 0) { + ipio_err("Failed to get Open SP DAC data, %d\n", res); + goto out; + } + res = allnode_open_cdc_data(1, open[i].cbk_700, open[i].dac); + if (res < 0) { + ipio_err("Failed to get Open SP Raw1 data, %d\n", res); + goto out; + } + res = allnode_open_cdc_data(2, open[i].cbk_250, open[i].dac); + if (res < 0) { + ipio_err("Failed to get Open SP Raw2 data, %d\n", res); + goto out; + } + res = allnode_open_cdc_data(3, open[i].cbk_200, open[i].dac); + if (res < 0) { + ipio_err("Failed to get Open SP Raw3 data, %d\n", res); + goto out; + } + addr = 0; + + /*record fist frame for drbug*/ + if (i == 0) { + memcpy(frame1_cbk700, open[i].cbk_700, core_mp->frame_len * sizeof(int32_t)); + memcpy(frame1_cbk250, open[i].cbk_250, core_mp->frame_len * sizeof(int32_t)); + memcpy(frame1_cbk200, open[i].cbk_200, core_mp->frame_len * sizeof(int32_t)); + } + + dump_data(open[i].cbk_700, 10, core_mp->frame_len, core_mp->xch_len, "cbk 700"); + dump_data(open[i].cbk_250, 10, core_mp->frame_len, core_mp->xch_len, "cbk 250"); + dump_data(open[i].cbk_200, 10, core_mp->frame_len, core_mp->xch_len, "cbk 200"); + for (y = 0 ; y < core_mp->ych_len ; y++) { + for (x = 0 ; x < core_mp->xch_len ; x++) { + open[i].charg_rate[addr] = open[i].cbk_250[addr] * 100 / open[i].cbk_700[addr]; + open[i].full_Open[addr] = open[i].cbk_700[addr] - open[i].cbk_200[addr]; + addr++; + } + } + if (ipio_debug_level & DEBUG_MP_TEST) { + dump_data(open[i].charg_rate, 10, core_mp->frame_len, core_mp->xch_len, "origin charge rate"); + dump_data(open[i].full_Open, 10, core_mp->frame_len, core_mp->xch_len, "origin full open"); + } + + addr = 0; + for (y = 0 ; y < core_mp->ych_len ; y++) { + for (x = 0 ; x < core_mp->xch_len ; x++) { + if (full_open_rate_compare(open[i].full_Open, open[i].cbk_700, x, y, tItems[index].node_type[addr], full_open_rate) == false) { + tItems[index].buf[(i * core_mp->frame_len) + addr] = 0; + open[i].charg_rate[addr] = 0; + } + addr++; + } + } + if (ipio_debug_level & DEBUG_MP_TEST) + dump_data(&tItems[index].buf[(i * core_mp->frame_len)], 10, core_mp->frame_len, core_mp->xch_len, "after full_open_rate_compare"); + + addr = 0; + for (y = 0 ; y < core_mp->ych_len ; y++) { + for (x = 0 ; x < core_mp->xch_len ; x++) { + tItems[index].buf[(i * core_mp->frame_len) + addr] = compare_charge(open[i].charg_rate, x, y, tItems[index].node_type, Charge_AA, Charge_Border, Charge_Notch); + addr++; + } + } + if (ipio_debug_level & DEBUG_MP_TEST) + dump_data(&tItems[index].buf[(i * core_mp->frame_len)], 10, core_mp->frame_len, core_mp->xch_len, "after compare charge rate"); + + compare_MaxMin_result(index, &tItems[index].buf[(i * core_mp->frame_len)]); + } + +out: + ipio_kfree((void **)&tItems[index].node_type); + + for (i = 0 ; i < tItems[index].frame_count; i++) { + ipio_kfree((void **)&open[i].cbk_700); + ipio_kfree((void **)&open[i].cbk_250); + ipio_kfree((void **)&open[i].cbk_200); + ipio_kfree((void **)&open[i].charg_rate); + ipio_kfree((void **)&open[i].full_Open); + ipio_kfree((void **)&open[i].dac); + } + + return res; +} + +int codeToOhm(int32_t Code) +{ + int douTDF1 = 0; + int douTDF2 = 0; + int douTVCH = 24; + int douTVCL = 8; + int douCint = 7; + int douVariation = 64; + int douRinternal = 930; + int32_t temp = 0; + + if (core_mp->nodp.isLongV) { + douTDF1 = 300; + douTDF2 = 100; + } else { + douTDF1 = 219; + douTDF2 = 100; + } + + if (Code == 0) { + ipio_debug(DEBUG_MP_TEST, "code is invalid\n"); + } else { + temp = ((douTVCH - douTVCL) * douVariation * (douTDF1 - douTDF2) * (1<<12) / (9 * Code * douCint)) * 100; + temp = (temp - douRinternal) / 1000; + } + /* Unit = M Ohm */ + return temp; +} + +static int short_test(int index, int frame_index) +{ + int j = 0, res = 0; + + if (protocol->major >= 5 && protocol->mid >= 4) { + /* Calculate code to ohm and save to tItems[index].buf */ + for (j = 0 ; j < core_mp->frame_len ; j++) + tItems[index].buf[frame_index * core_mp->frame_len + j] = codeToOhm(frame_buf[j]); + } else { + for (j = 0 ; j < core_mp->frame_len ; j++) + tItems[index].buf[frame_index * core_mp->frame_len + j] = frame_buf[j]; + } + + return res; +} + +static int mutual_test(int index) +{ + int i = 0, j = 0, x = 0, y = 0, res = 0, get_frame_cont = 1 ; + + ipio_debug(DEBUG_MP_TEST, "index = %d, name = %s, CMD = 0x%x, Frame Count = %d\n", + index, tItems[index].name, tItems[index].cmd, tItems[index].frame_count); + + /* + * We assume that users who are calling the test forget to config frame count + * as 1, so we just help them to set it up. + */ + if (tItems[index].frame_count <= 0) { + ipio_err("Frame count is zero, which is at least set as 1\n"); + tItems[index].frame_count = 1; + } + + //makesure it's first time + if (tItems[index].test_count == 0) { + res = create_mp_test_frame_buffer(index, tItems[index].frame_count); + if (res < 0) + goto out; + } + + /* Init Max/Min buffer */ + for (y = 0 ; y < core_mp->ych_len ; y++) { + for (x = 0 ; x < core_mp->xch_len ; x++) { + if (tItems[i].catalog == TX_RX_DELTA) { + core_mp->tx_max_buf[y * core_mp->xch_len + x] = INT_MIN; + core_mp->rx_max_buf[y * core_mp->xch_len + x] = INT_MIN; + core_mp->tx_min_buf[y * core_mp->xch_len + x] = INT_MAX; + core_mp->rx_min_buf[y * core_mp->xch_len + x] = INT_MAX; + } else { + tItems[index].max_buf[y * core_mp->xch_len + x] = INT_MIN; + tItems[index].min_buf[y * core_mp->xch_len + x] = INT_MAX; + } + } + } + + if (tItems[index].catalog != PEAK_TO_PEAK_TEST) + get_frame_cont = tItems[index].frame_count; + + if (tItems[index].spec_option == BENCHMARK) { + core_parser_benchmark(tItems[index].bench_mark_max, tItems[index].bench_mark_min, tItems[index].type_option, tItems[index].desp); + if (ipio_debug_level && DEBUG_PARSER > 0) + dump_benchmark_data(tItems[index].bench_mark_max, tItems[index].bench_mark_min); + } + + for (i = 0 ; i < get_frame_cont; i++) { + res = allnode_mutual_cdc_data(index); + if (res < 0) { + ipio_err("Failed to initialise CDC data, %d\n", res); + goto out; + } + switch (tItems[index].catalog) { + case PIXEL: + run_pixel_test(index); + break; + case UNTOUCH_P2P: + run_untouch_p2p_test(index); + break; + case OPEN_TEST: + run_open_test(index); + break; + case TX_RX_DELTA: + run_tx_rx_delta_test(index); + break; + case SHORT_TEST: + short_test(index, i); + break; + default: + for (j = 0 ; j < core_mp->frame_len ; j++) + tItems[index].buf[i * core_mp->frame_len + j] = frame_buf[j]; + break; + } + compare_MaxMin_result(index, &tItems[index].buf[i * core_mp->frame_len]); + } + +out: + return res; +} + +static int key_test(int index) +{ + int i, j = 0, res = 0; + + ipio_debug(DEBUG_MP_TEST, "Item = %s, CMD = 0x%x, Frame Count = %d\n", + tItems[index].name, tItems[index].cmd, tItems[index].frame_count); + + if (tItems[index].frame_count == 0) { + ipio_err("Frame count is zero, which at least sets as 1\n"); + res = -EINVAL; + goto out; + } + + if (tItems[index].test_count == 0) + res = create_mp_test_frame_buffer(index, tItems[index].frame_count); + + if (res < 0) + goto out; + + for (i = 0 ; i < tItems[index].frame_count; i++) { + res = allnode_key_cdc_data(index); + if (res < 0) { + ipio_err("Failed to initialise CDC data, %d\n", res); + goto out; + } + + for (j = 0 ; j < core_mp->key_len ; j++) + tItems[index].buf[j] = key_buf[j]; + } + + compare_MaxMin_result(index, tItems[index].buf); + +out: + return res; +} + +static int self_test(int index) +{ + ipio_err("TDDI has no self to be tested currently\n"); + return ERROR; +} + +static int st_test(int index) +{ + ipio_err("ST Test is not supported by the driver\n"); + return ERROR; +} + +int mp_test_data_sort_average(int32_t *oringin_data, int index, int32_t *avg_result) +{ + int i, j, k, x, y, len = 5; + int32_t u32temp; + int u32up_frame, u32down_frame; + int32_t *u32sum_raw_data; + int32_t *u32data_buff; + + if (tItems[index].frame_count <= 1) + return 0; + + + if (ERR_ALLOC_MEM(oringin_data)) { + ipio_err("Input wrong adress\n"); + return -ENOMEM; + } + + u32data_buff = kcalloc(core_mp->frame_len * tItems[index].frame_count, sizeof(int32_t), GFP_KERNEL); + u32sum_raw_data = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + if (ERR_ALLOC_MEM(u32sum_raw_data) || (ERR_ALLOC_MEM(u32data_buff))) { + ipio_err("Failed to allocate u32sum_raw_data FRAME buffer\n"); + return -ENOMEM; + } + + for (i = 0 ; i < core_mp->frame_len * tItems[index].frame_count; i++) { + u32data_buff[i] = oringin_data[i]; + } + + u32up_frame = tItems[index].frame_count * tItems[index].highest_percentage / 100; + u32down_frame = tItems[index].frame_count * tItems[index].lowest_percentage / 100; + ipio_debug(DEBUG_MP_TEST, "Up=%d,Down=%d -%s\n", u32up_frame, u32down_frame, tItems[index].desp); + + if (ipio_debug_level & DEBUG_MP_TEST) { + printk("\n[Show Original frist%d and last%d node data]\n", len, len); + for (i = 0 ; i < core_mp->frame_len ; i++) { + for (j = 0 ; j < tItems[index].frame_count ; j++) { + if ((i < len) || (i >= (core_mp->frame_len-len))) + printk("%d, ", u32data_buff[j * core_mp->frame_len + i]); + } + if ((i < len) || (i >= (core_mp->frame_len-len))) + printk("\n"); + } + } + + for (i = 0 ; i < core_mp->frame_len ; i++) { + for (j = 0 ; j < tItems[index].frame_count-1 ; j++) { + for (k = 0 ; k < (tItems[index].frame_count-1-j) ; k++) { + x = i + k * core_mp->frame_len; + y = i + (k + 1) * core_mp->frame_len; + if (*(u32data_buff+x) > *(u32data_buff+y)) { + u32temp = *(u32data_buff+x); + *(u32data_buff+x) = *(u32data_buff+y); + *(u32data_buff+y) = u32temp; + } + } + } + } + + if (ipio_debug_level & DEBUG_MP_TEST) { + printk("\n[After sorting frist%d and last%d node data]\n", len, len); + for (i = 0 ; i < core_mp->frame_len ; i++) { + for (j = u32down_frame ; j < tItems[index].frame_count - u32up_frame ; j++) { + if ((i < len) || (i >= (core_mp->frame_len-len))) + printk("%d, ", u32data_buff[i + j * core_mp->frame_len]); + } + if ((i < len) || (i >= (core_mp->frame_len-len))) + printk("\n"); + } + } + + for (i = 0 ; i < core_mp->frame_len ; i++) { + u32sum_raw_data[i] = 0; + for (j = u32down_frame ; j < tItems[index].frame_count - u32up_frame ; j++) + u32sum_raw_data[i] += u32data_buff[i + j * core_mp->frame_len]; + + avg_result[i] = u32sum_raw_data[i] / (tItems[index].frame_count - u32down_frame - u32up_frame); + } + + if (ipio_debug_level & DEBUG_MP_TEST) { + printk("\n[Average result frist%d and last%d node data]\n", len, len); + for (i = 0 ; i < core_mp->frame_len ; i++) { + if ((i < len) || (i >= (core_mp->frame_len-len))) + printk("%d, ", avg_result[i]); + } + if ((i < len) || (i >= (core_mp->frame_len-len))) + printk("\n"); + } + + ipio_kfree((void **)&u32data_buff); + ipio_kfree((void **)&u32sum_raw_data); + return 0; +} + + +int core_mp_compare_retry_cdc_result(int i) +{ + int j, test_result = MP_PASS; + int32_t *max_threshold = NULL, *min_threshold = NULL ; + + max_threshold = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + min_threshold = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + if (ERR_ALLOC_MEM(max_threshold) || ERR_ALLOC_MEM(min_threshold)) { + ipio_err("Failed to allocate threshold FRAME buffer\n"); + test_result = MP_FAIL; + goto fail_open; + } + + /* Show test result as below */ + if (tItems[i].catalog == TX_RX_DELTA) { + + if (ERR_ALLOC_MEM(core_mp->rx_delta_buf) || ERR_ALLOC_MEM(core_mp->tx_delta_buf)) { + ipio_err("This test item (%s) has no data inside its buffer\n", tItems[i].desp); + test_result = MP_FAIL; + goto fail_open; + } + + for (j = 0 ; j < core_mp->frame_len ; j++) { + max_threshold[j] = core_mp->TxDeltaMax; + min_threshold[j] = core_mp->TxDeltaMin; + } + mp_compare_cdc_result(core_mp->tx_max_buf, max_threshold, min_threshold, &test_result); + mp_compare_cdc_result(core_mp->tx_min_buf, max_threshold, min_threshold, &test_result); + for (j = 0 ; j < core_mp->frame_len ; j++) { + max_threshold[j] = core_mp->RxDeltaMax; + min_threshold[j] = core_mp->RxDeltaMin; + } + mp_compare_cdc_result(core_mp->rx_max_buf, max_threshold, min_threshold, &test_result); + mp_compare_cdc_result(core_mp->rx_min_buf, max_threshold, min_threshold, &test_result); + } else { + + if (ERR_ALLOC_MEM(tItems[i].buf) || ERR_ALLOC_MEM(tItems[i].max_buf) || + ERR_ALLOC_MEM(tItems[i].min_buf) || ERR_ALLOC_MEM(tItems[i].result_buf)) { + ipio_err("This test item (%s) has no data inside its buffer\n", tItems[i].desp); + test_result = MP_FAIL; + goto fail_open; + } + + if (tItems[i].spec_option == BENCHMARK) { + for (j = 0 ; j < core_mp->frame_len ; j++) { + max_threshold[j] = tItems[i].bench_mark_max[j]; + min_threshold[j] = tItems[i].bench_mark_min[j]; + } + } else { + for (j = 0 ; j < core_mp->frame_len ; j++) { + max_threshold[j] = tItems[i].max; + min_threshold[j] = tItems[i].min; + } + } + /* general result */ + if (tItems[i].trimmed_mean && tItems[i].catalog != PEAK_TO_PEAK_TEST) { + mp_test_data_sort_average(tItems[i].buf, i, tItems[i].result_buf); + mp_compare_cdc_result(tItems[i].result_buf, max_threshold, min_threshold, &test_result); + } else { + mp_compare_cdc_result(tItems[i].max_buf, max_threshold, min_threshold, &test_result); + mp_compare_cdc_result(tItems[i].min_buf, max_threshold, min_threshold, &test_result); + } + } + +fail_open: + ipio_kfree((void **)&max_threshold); + ipio_kfree((void **)&min_threshold); + + return test_result; +} + +void core_mp_retry(int index) +{ + core_config_ice_mode_enable(); + + if (core_config_set_watch_dog(false) < 0) { + ipio_err("Failed to disable watch dog\n"); + } + + core_config_ic_reset(); + + core_config_ice_mode_disable(); + + /* Switch to Demo mode */ + core_fr_mode_control(&protocol->demo_mode); + + /* Switch to test mode */ + core_fr_mode_control(&protocol->test_mode); + + + tItems[index].do_test(index); + tItems[index].test_count++; +} + + +void core_mp_show_result(void) +{ + int i, x, y, j, csv_len = 0, pass_item_count = 0, line_count = 0, get_frame_cont = 1; + int32_t *max_threshold = NULL, *min_threshold = NULL; + char *csv = NULL; + char csv_name[128] = { 0 }; + char *ret_pass_name = NULL, *ret_fail_name = NULL; + struct file *f = NULL; + mm_segment_t fs; + loff_t pos; + + csv = vmalloc(CSV_FILE_SIZE); + if (ERR_ALLOC_MEM(csv)) { + ipio_err("Failed to allocate CSV mem\n"); + goto fail_open; + } + + max_threshold = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + min_threshold = kcalloc(core_mp->frame_len, sizeof(int32_t), GFP_KERNEL); + if (ERR_ALLOC_MEM(max_threshold) || ERR_ALLOC_MEM(min_threshold)) { + ipio_err("Failed to allocate threshold FRAME buffer\n"); + goto fail_open; + } + /* header must has 19 line*/ + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "==============================================================================\n"); + line_count++; + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "ILITek C-TP Utility V%s %x : Driver Sensor Test\n", DRIVER_VERSION, core_config->chip_pid); + line_count++; + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "Confidentiality Notice:\n"); + line_count++; + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "Any information of this tool is confidential and privileged.\n"); + line_count++; + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "@ ILI TECHNOLOGY CORP. All Rights Reserved.\n"); + line_count++; + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "==============================================================================\n"); + line_count++; + if (protocol->mid >= 0x3) {/*line7*/ + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "Firmware Version ,V%d.%d.%d.%d\n", core_config->firmware_ver[1], core_config->firmware_ver[2], core_config->firmware_ver[3], core_config->firmware_ver[4]); + } else { + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "Firmware Version ,V%d.%d.%d\n", core_config->firmware_ver[1], core_config->firmware_ver[2], core_config->firmware_ver[3]); + } + line_count++; + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "Panel information ,XCH=%d, YCH=%d\n", core_mp->xch_len, core_mp->ych_len); + line_count++; + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "Test Item:\n"); + line_count++; + for (i = 0 ; i < ARRAY_SIZE(tItems); i++) { + if (tItems[i].run == 1) { + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), " ---%s\n", tItems[i].desp); + line_count++; + } + } + while (line_count < 19) { + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "\n"); + line_count++; + } + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "==============================================================================\n"); + + for (i = 0 ; i < ARRAY_SIZE(tItems); i++) { + if (tItems[i].run != 1) + continue; + + if (tItems[i].item_result == MP_PASS) { + pr_info("\n\n[%s],OK \n", tItems[i].desp); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "\n\n[%s],OK\n", tItems[i].desp); + } else { + pr_info("\n\n[%s],NG \n", tItems[i].desp); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "\n\n[%s],NG\n", tItems[i].desp); + } + + pr_info("Frame count = %d\n", tItems[i].frame_count); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "Frame count = %d\n", tItems[i].frame_count); + + if (tItems[i].trimmed_mean && tItems[i].catalog != PEAK_TO_PEAK_TEST) { + pr_info("Lowest Percentage = %d\n", tItems[i].lowest_percentage); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "Lowest Percentage = %d\n", tItems[i].lowest_percentage); + + pr_info("Highest Percentage = %d\n", tItems[i].highest_percentage); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "Highest Percentage = %d\n", tItems[i].highest_percentage); + } + + /* Show result of benchmark max and min */ + if (tItems[i].spec_option == BENCHMARK) { + for (j = 0 ; j < core_mp->frame_len ; j++) { + max_threshold[j] = tItems[i].bench_mark_max[j]; + min_threshold[j] = tItems[i].bench_mark_min[j]; + } + + mp_compare_cdc_show_result(tItems[i].bench_mark_max, csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, "Max_Bench"); + mp_compare_cdc_show_result(tItems[i].bench_mark_min, csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, "Min_Bench"); + } else { + + for (j = 0 ; j < core_mp->frame_len ; j++) { + max_threshold[j] = tItems[i].max; + min_threshold[j] = tItems[i].min; + } + + pr_info("Max = %d\n", tItems[i].max); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "Max = %d\n", tItems[i].max); + + pr_info("Min = %d\n", tItems[i].min); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "Min = %d\n", tItems[i].min); + } + if (strcmp(tItems[i].name, "open_integration_sp") == 0) { + mp_compare_cdc_show_result(frame1_cbk700, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "frame1 cbk700"); + mp_compare_cdc_show_result(frame1_cbk250, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "frame1 cbk250"); + mp_compare_cdc_show_result(frame1_cbk200, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "frame1 cbk200"); + ipio_kfree((void **)&frame1_cbk700); + ipio_kfree((void **)&frame1_cbk250); + ipio_kfree((void **)&frame1_cbk200); + } + + if (tItems[i].catalog == TX_RX_DELTA) { + if (ERR_ALLOC_MEM(core_mp->rx_delta_buf) || ERR_ALLOC_MEM(core_mp->tx_delta_buf)) { + ipio_err("This test item (%s) has no data inside its buffer\n", tItems[i].desp); + continue; + } + } else { + if (ERR_ALLOC_MEM(tItems[i].buf) || ERR_ALLOC_MEM(tItems[i].max_buf) || + ERR_ALLOC_MEM(tItems[i].min_buf)) { + ipio_err("This test item (%s) has no data inside its buffer\n", tItems[i].desp); + continue; + } + } + + /* Show test result as below */ + if (tItems[i].catalog == KEY_TEST) { + for (x = 0 ; x < core_mp->key_len ; x++) { + DUMP(DEBUG_MP_TEST, "KEY_%02d ", x); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "KEY_%02d, ", x); + } + + DUMP(DEBUG_MP_TEST, "\n"); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "\n"); + + for (y = 0 ; y < core_mp->key_len ; y++) { + DUMP(DEBUG_MP_TEST, " %3d ", tItems[i].buf[y]); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), " %3d, ", tItems[i].buf[y]); + } + + DUMP(DEBUG_MP_TEST, "\n"); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "\n"); + } else if (tItems[i].catalog == TX_RX_DELTA) { + + for (j = 0 ; j < core_mp->frame_len ; j++) { + max_threshold[j] = core_mp->TxDeltaMax; + min_threshold[j] = core_mp->TxDeltaMin; + } + mp_compare_cdc_show_result(core_mp->tx_max_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "TX Max Hold"); + mp_compare_cdc_show_result(core_mp->tx_min_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "TX Min Hold"); + + for (j = 0 ; j < core_mp->frame_len ; j++) { + max_threshold[j] = core_mp->RxDeltaMax; + min_threshold[j] = core_mp->RxDeltaMin; + } + mp_compare_cdc_show_result(core_mp->rx_max_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "RX Max Hold"); + mp_compare_cdc_show_result(core_mp->rx_min_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "RX Min Hold"); + + } else { + /* general result */ + if (tItems[i].trimmed_mean && tItems[i].catalog != PEAK_TO_PEAK_TEST) { + mp_compare_cdc_show_result(tItems[i].result_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "Mean result"); + } else { + mp_compare_cdc_show_result(tItems[i].max_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "Max Hold"); + mp_compare_cdc_show_result(tItems[i].min_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "Min Hold"); + } + if (tItems[i].catalog != PEAK_TO_PEAK_TEST) + get_frame_cont = tItems[i].frame_count; + /* result of each frame */ + for (j = 0 ; j < get_frame_cont; j++) { + char frame_name[128] = { 0 }; + snprintf(frame_name, sizeof(frame_name), "Frame %d", (j+1)); + mp_compare_cdc_show_result(&tItems[i].buf[(j*core_mp->frame_len)], csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, frame_name); + } + + } + } + + memset(csv_name, 0, 128 * sizeof(char)); + + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "==============================================================================\n"); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), "Result_Summary \n"); + + for (i = 0 ; i < ARRAY_SIZE(tItems); i++) { + if (tItems[i].run) { + if (tItems[i].item_result == MP_PASS) + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), " {%s} ,OK\n", tItems[i].desp); + else + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE-csv_len), " {%s} ,NG\n", tItems[i].desp); + } + } + + for (i = 0 ; i < ARRAY_SIZE(tItems); i++) { + if (tItems[i].run) { + if (tItems[i].item_result == MP_FAIL) { + pass_item_count = 0; + break; + } + pass_item_count++; + } + } + + /* define csv file name */ + if (core_mp->oppo_run) { + if (core_mp->oppo_lcm) { + ret_pass_name = OPPO_CSV_LCM_PASS_NAME; + ret_fail_name = OPPO_CSV_LCM_FAIL_NAME; + } else { + ret_pass_name = OPPO_CSV_PASS_NAME; + ret_fail_name = OPPO_CSV_FAIL_NAME; + } + } else { + ret_pass_name = NORMAL_CSV_PASS_NAME; + ret_fail_name = NORMAL_CSV_FAIL_NAME; + } + + if (pass_item_count == 0) { + core_mp->final_result = MP_FAIL; + snprintf(csv_name, sizeof(csv_name), "%s/%s.csv", CSV_PATH, ret_fail_name); + } else { + core_mp->final_result = MP_PASS; + snprintf(csv_name, sizeof(csv_name), "%s/%s.csv", CSV_PATH, ret_pass_name); + } + + ipio_info("Open CSV : %s\n", csv_name); + + if (f == NULL) + f = filp_open(csv_name, O_WRONLY | O_CREAT | O_TRUNC, 644); + + if (ERR_ALLOC_MEM(f)) { + ipio_err("Failed to open CSV file"); + goto fail_open; + } + + ipio_info("Open CSV succeed, its length = %d\n ", csv_len); + + if (csv_len >= CSV_FILE_SIZE) { + ipio_err("The length saved to CSV is too long !\n"); + goto fail_open; + } + + fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + vfs_write(f, csv, csv_len, &pos); + set_fs(fs); + filp_close(f, NULL); + + ipio_info("Writing Data into CSV succeed\n"); + +fail_open: + if (csv != NULL) + vfree(csv); + ipio_kfree((void **)&max_threshold); + ipio_kfree((void **)&min_threshold); +} +EXPORT_SYMBOL(core_mp_show_result); + +void core_mp_run_test(char *item, bool ini) +{ + int i = 0, count, test_result = MP_PASS; + char str[512] = { 0 }; + + /* update X/Y channel length if they're changed */ + core_mp->xch_len = core_config->tp_info->nXChannelNum; + core_mp->ych_len = core_config->tp_info->nYChannelNum; + + /* update key's length if they're changed */ + core_mp->key_len = core_config->tp_info->nKeyCount; + + /* compute the total length in one frame */ + core_mp->frame_len = core_mp->xch_len * core_mp->ych_len; + + if (item == NULL || strncmp(item, " ", strlen(item)) == 0 || core_mp->frame_len == 0) { + tItems[i].result = "FAIL"; + core_mp->final_result = MP_FAIL; + ipio_err("Invaild string or length\n"); + return; + } + + ipio_debug(DEBUG_MP_TEST, "item = %s, core type = %d\n", item, core_config->core_type); + + for (i = 0 ; i < core_mp->mp_items; i++) { + if (strncmp(item, tItems[i].desp, strlen(item)) == 0) { + if (ini) { + core_parser_get_int_data(item, "Enable", str); + tItems[i].run = katoi(str); + core_parser_get_int_data(item, "SPEC Option", str); + tItems[i].spec_option = katoi(str); + core_parser_get_int_data(item, "Type Option", str); + tItems[i].type_option = katoi(str); + core_parser_get_int_data(item, "Frame Count", str); + tItems[i].frame_count = katoi(str); + core_parser_get_int_data(item, "Trimmed Mean", str); + tItems[i].trimmed_mean = katoi(str); + core_parser_get_int_data(item, "Lowest Percentage", str); + tItems[i].lowest_percentage = katoi(str); + core_parser_get_int_data(item, "Highest Percentage", str); + tItems[i].highest_percentage = katoi(str); + + /* Get threshold from ini structure in parser */ + if (strcmp(item, "Tx/Rx Delta") == 0) { + core_parser_get_int_data(item, "Tx Max", str); + core_mp->TxDeltaMax = katoi(str); + core_parser_get_int_data(item, "Tx Min", str); + core_mp->TxDeltaMin = katoi(str); + core_parser_get_int_data(item, "Rx Max", str); + core_mp->RxDeltaMax = katoi(str); + core_parser_get_int_data(item, "Rx Min", str); + core_mp->RxDeltaMin = katoi(str); + ipio_debug(DEBUG_MP_TEST, "%s: Tx Max = %d, Tx Min = %d, Rx Max = %d, Rx Min = %d\n", + tItems[i].desp, core_mp->TxDeltaMax, core_mp->TxDeltaMin, + core_mp->RxDeltaMax, core_mp->RxDeltaMin); + } else { + core_parser_get_int_data(item, "Max", str); + tItems[i].max = katoi(str); + core_parser_get_int_data(item, "Min", str); + tItems[i].min = katoi(str); + } + + core_parser_get_int_data(item, "Frame Count", str); + tItems[i].frame_count = katoi(str); + + ipio_debug(DEBUG_MP_TEST, "%s: run = %d, max = %d, min = %d, frame_count = %d\n", tItems[i].desp, + tItems[i].run, tItems[i].max, tItems[i].min, tItems[i].frame_count); + } + + if (tItems[i].run) { + /* LCM off */ + if (strnstr(tItems[i].desp, "LCM", strlen(tItems[i].desp)) != NULL) { + if (!core_mp->oppo_run && !core_mp->oppo_lcm) + core_mp_ctrl_lcm_status(false); + } + + ipio_info("Running Test Item : %s\n", tItems[i].desp); + tItems[i].do_test(i); + tItems[i].test_count++; + test_result = core_mp_compare_retry_cdc_result(i); + + /* To see if this item needs to do retry */ + if (core_mp->retry && (test_result == MP_FAIL)) { + for (count = 0 ; count < RETRY_COUNT; count++) { + ipio_info("retry = %d, item = %s\n", (count+1), tItems[i].desp); + core_mp_retry(i); + test_result = core_mp_compare_retry_cdc_result(i); + if (test_result == MP_PASS) + break; + } + } + tItems[i].item_result = test_result; + /* LCM on */ + if (strnstr(tItems[i].desp, "LCM", strlen(tItems[i].desp)) != NULL) { + if (!core_mp->oppo_run && !core_mp->oppo_lcm) + core_mp_ctrl_lcm_status(true); + } + } + break; + } + } +} +EXPORT_SYMBOL(core_mp_run_test); + +int core_mp_move_code(void) +{ + int ret = 0; + + ipio_info("Prepaing to enter Test Mode\n"); +#ifdef HOST_DOWNLOAD + ilitek_platform_tp_hw_reset(true); +#else + if (core_config_check_cdc_busy(50, 50) < 0) { + ipio_err("%d mode Check busy is timout ! Enter Test Mode failed\n", core_fr->actual_fw_mode); + return ERROR; + } + + if (core_config_ice_mode_enable() < 0) { + ipio_err("Failed to enter ICE mode\n"); + return ERROR; + } + + if (core_config_set_watch_dog(false) < 0) { + ipio_err("Failed to disable watch dog\n"); + } + + /* DMA Trigger */ + core_config_ice_mode_write(0x41010, 0xFF, 1); + + mdelay(30); + + /* CS High */ + core_config_ice_mode_write(0x041000, 0x1, 1); + + mdelay(60); + + core_fr->actual_fw_mode = protocol->test_mode; + /* Code reset */ + core_config_ice_mode_write(0x40040, 0xAE, 1); + + if (core_config_set_watch_dog(true) < 0) { + ipio_err("Failed to enable watch dog\n"); + } + + core_config_ice_mode_disable(); + + if (core_config_check_cdc_busy(300, 50) < 0) { + ipio_err("Check busy is timout ! Enter Test Mode failed\n"); + return ERROR; + } +#endif + ipio_info("FW Test Mode ready\n"); + return ret; +} +EXPORT_SYMBOL(core_mp_move_code); + +void core_mp_test_free(void) +{ + int i; + + ipio_info("Free all allocated mem\n"); + + core_mp->final_result = MP_FAIL; + + for (i = 0 ; i < ARRAY_SIZE(tItems); i++) { + tItems[i].run = false; + tItems[i].max_res = MP_FAIL; + tItems[i].min_res = MP_FAIL; + tItems[i].item_result = MP_PASS; + tItems[i].test_count = 0; + snprintf(tItems[i].result, 16, "%s", "FAIL"); + + if (tItems[i].catalog == TX_RX_DELTA) { + ipio_kfree((void **)&core_mp->rx_delta_buf); + ipio_kfree((void **)&core_mp->tx_delta_buf); + ipio_kfree((void **)&core_mp->tx_max_buf); + ipio_kfree((void **)&core_mp->tx_min_buf); + ipio_kfree((void **)&core_mp->rx_max_buf); + ipio_kfree((void **)&core_mp->rx_min_buf); + } else { + if (tItems[i].spec_option == BENCHMARK) { + ipio_kfree((void **)&tItems[i].bench_mark_max); + ipio_kfree((void **)&tItems[i].bench_mark_min); + } + + ipio_kfree((void **)&tItems[i].result_buf); + ipio_kfree((void **)&tItems[i].max_buf); + ipio_kfree((void **)&tItems[i].min_buf); + vfree(tItems[i].buf); + tItems[i].buf = NULL; + } + } + + ipio_kfree((void **)&frame_buf); + ipio_kfree((void **)&key_buf); + ipio_kfree((void **)&core_mp); +} +EXPORT_SYMBOL(core_mp_test_free); + +static void mp_test_init_item(void) +{ + int i; + + core_mp->mp_items = ARRAY_SIZE(tItems); + + /* assign test functions run on MP flow according to their catalog */ + for (i = 0 ; i < ARRAY_SIZE(tItems); i++) { + + tItems[i].spec_option = 0; + tItems[i].type_option = 0; + tItems[i].run = false; + tItems[i].max = 0; + tItems[i].max_res = MP_FAIL; + tItems[i].item_result = MP_PASS; + tItems[i].min = 0; + tItems[i].min_res = MP_FAIL; + tItems[i].frame_count = 0; + tItems[i].trimmed_mean = 0; + tItems[i].lowest_percentage = 0; + tItems[i].highest_percentage = 0; + tItems[i].result_buf = NULL; + tItems[i].buf = NULL; + tItems[i].max_buf = NULL; + tItems[i].min_buf = NULL; + tItems[i].bench_mark_max = NULL; + tItems[i].bench_mark_min = NULL; + tItems[i].node_type = NULL; + tItems[i].test_count = 0; + + if (tItems[i].catalog == MUTUAL_TEST) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == TX_RX_DELTA) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == UNTOUCH_P2P) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == PIXEL) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == OPEN_TEST) { + if (strcmp(tItems[i].name, "open_integration_sp") == 0) + tItems[i].do_test = open_test_sp; + else + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == KEY_TEST) { + tItems[i].do_test = key_test; + } else if (tItems[i].catalog == SELF_TEST) { + tItems[i].do_test = self_test; + } else if (tItems[i].catalog == ST_TEST) { + tItems[i].do_test = st_test; + } else if (tItems[i].catalog == PEAK_TO_PEAK_TEST) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == SHORT_TEST) { + tItems[i].do_test = mutual_test; + } + + tItems[i].result = kmalloc(16, GFP_KERNEL); + snprintf(tItems[i].result, 16, "%s", "FAIL"); + } + + /* + * assign protocol command written into firmware via I2C, + * which might be differnet if the version of protocol was changed. + */ + tItems[0].cmd = protocol->mutual_dac; + tItems[1].cmd = protocol->mutual_bg; + tItems[2].cmd = protocol->mutual_signal; + tItems[3].cmd = protocol->mutual_no_bk; + tItems[4].cmd = protocol->mutual_has_bk; + tItems[5].cmd = protocol->mutual_bk_dac; + tItems[6].cmd = protocol->self_dac; + tItems[7].cmd = protocol->self_bg; + tItems[8].cmd = protocol->self_signal; + tItems[9].cmd = protocol->self_no_bk; + tItems[10].cmd = protocol->self_has_bk; + tItems[11].cmd = protocol->self_bk_dac; + tItems[12].cmd = protocol->key_dac; + tItems[13].cmd = protocol->key_bg; + tItems[14].cmd = protocol->key_no_bk; + tItems[15].cmd = protocol->key_has_bk; + tItems[16].cmd = protocol->key_open; + tItems[17].cmd = protocol->key_short; + tItems[18].cmd = protocol->st_dac; + tItems[19].cmd = protocol->st_bg; + tItems[20].cmd = protocol->st_no_bk; + tItems[21].cmd = protocol->st_has_bk; + tItems[22].cmd = protocol->st_open; + tItems[23].cmd = protocol->tx_short; + tItems[24].cmd = protocol->rx_short; + tItems[25].cmd = protocol->rx_open; + tItems[26].cmd = protocol->cm_data; + tItems[27].cmd = protocol->cs_data; + tItems[28].cmd = protocol->tx_rx_delta; + tItems[29].cmd = protocol->mutual_signal; + tItems[30].cmd = protocol->mutual_no_bk; + tItems[31].cmd = protocol->mutual_has_bk; + tItems[32].cmd = protocol->rx_open; + tItems[33].cmd = protocol->rx_open; + tItems[34].cmd = protocol->peak_to_peak; +} + +int core_mp_init(void) +{ + int res = 0; + + if (!ERR_ALLOC_MEM(core_config->tp_info)) { + if (core_mp == NULL) { + core_mp = kzalloc(sizeof(*core_mp), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp)) { + ipio_err("Failed to init core_mp, %ld\n", PTR_ERR(core_mp)); + res = -ENOMEM; + goto out; + } + + core_mp->xch_len = core_config->tp_info->nXChannelNum; + core_mp->ych_len = core_config->tp_info->nYChannelNum; + + core_mp->stx_len = core_config->tp_info->self_tx_channel_num; + core_mp->srx_len = core_config->tp_info->self_rx_channel_num; + + core_mp->key_len = core_config->tp_info->nKeyCount; + core_mp->st_len = core_config->tp_info->side_touch_type; + + core_mp->tdf = 240; + core_mp->busy_cdc = POLL_CHECK; + ipio_info("Check busy method = %d\n", core_mp->busy_cdc); + core_mp->run = false; + core_mp->retry = true; + core_mp->oppo_run = false; + core_mp->oppo_lcm = false; + core_mp->final_result = MP_FAIL; + + mp_test_init_item(); + } + } else { + ipio_err("Failed to allocate core_mp mem as did not find TP info\n"); + res = -ENOMEM; + } + +out: + return res; +} +EXPORT_SYMBOL(core_mp_init); diff --git a/drivers/input/touchscreen/ilitek_mmi/core/mp_test.h b/drivers/input/touchscreen/ilitek_mmi/core/mp_test.h new file mode 100644 index 000000000000..5d82e1683c31 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/mp_test.h @@ -0,0 +1,188 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __MP_TEST_H +#define __MP_TEST_H + +#define MP_PASS 1 +#define MP_FAIL -1 +#define BENCHMARK 1 +#define NODETYPE 1 +#define RAWDATA_NO_BK_DATA_SHIFT_9881H 8192 +#define RAWDATA_NO_BK_DATA_SHIFT_9881F 4096 +#define TYPE_BENCHMARK 0 +#define TYPE_NO_JUGE 1 +#define TYPE_JUGE 2 + +#define EXEC_READ 0 +#define EXEC_WRITE 1 + +#define INT_CHECK 0 +#define POLL_CHECK 1 +#define DELAY_CHECK 2 +#define ISR_CHECK 3 +#define RETRY_COUNT 0 + +#define NORMAL_CSV_PASS_NAME "mp_pass" +#define NORMAL_CSV_FAIL_NAME "mp_fail" +#define OPPO_CSV_PASS_NAME "oppo_mp_pass" +#define OPPO_CSV_FAIL_NAME "oppo_mp_fail" +#define OPPO_CSV_LCM_PASS_NAME "oppo_mp_lcm_pass" +#define OPPO_CSV_LCM_FAIL_NAME "oppo_mp_lcm_fail" + +struct mp_test_P540_open { + int32_t *cbk_700; + int32_t *cbk_250; + int32_t *cbk_200; + int32_t *charg_rate; + int32_t *full_Open; + int32_t *dac; + int32_t *cdc; +}; + +struct mp_test_items { + char *name; + /* The description must be the same as ini's section name */ + char *desp; + char *result; + int catalog; + uint8_t cmd; + uint8_t spec_option; + uint8_t type_option; + bool run; + int max; + int max_res; + int item_result; + int test_count; + int min; + int min_res; + int frame_count; + int trimmed_mean; + int lowest_percentage; + int highest_percentage; + int32_t *result_buf; + int32_t *buf; + int32_t *max_buf; + int32_t *min_buf; + int32_t *bench_mark_max; + int32_t *bench_mark_min; + int32_t *node_type; + int (*do_test)(int index); +}; + +struct mp_nodp_calc { + bool is60HZ; + bool isLongV; + + /* Input */ + uint16_t tshd; + uint8_t multi_term_num_120; + uint8_t multi_term_num_60; + uint16_t tsvd_to_tshd; + uint16_t qsh_tdf; + + /* Settings */ + uint8_t auto_trim; + uint16_t tp_tshd_wait_120; + uint16_t ddi_width_120; + uint16_t tp_tshd_wait_60; + uint16_t ddi_width_60; + uint16_t dp_to_tp; + uint16_t tx_wait_const; + uint16_t tx_wait_const_multi; + uint16_t tp_to_dp; + uint8_t phase_adc; + uint8_t r2d_pw; + uint8_t rst_pw; + uint8_t rst_pw_back; + uint8_t dac_td; + uint8_t qsh_pw; + uint8_t qsh_td; + uint8_t drop_nodp; + + /* Output */ + uint32_t first_tp_width; + uint32_t tp_width; + uint32_t txpw; + uint32_t long_tsdh_wait; + uint32_t nodp; +}; + +struct core_mp_test_data { + struct mp_nodp_calc nodp; + /* A flag shows a test run in particular */ + bool run; + bool oppo_run; + bool oppo_lcm; + bool m_signal; + bool m_dac; + bool s_signal; + bool s_dac; + bool key_dac; + bool st_dac; + bool p_no_bk; + bool p_has_bk; + bool open_integ; + bool open_cap; + + int xch_len; + int ych_len; + int stx_len; + int srx_len; + int key_len; + int st_len; + int frame_len; + int mp_items; + int final_result; + + /* Tx/Rx threshold & buffer */ + int TxDeltaMax; + int TxDeltaMin; + int RxDeltaMax; + int RxDeltaMin; + int32_t *tx_delta_buf; + int32_t *rx_delta_buf; + int32_t *tx_max_buf; + int32_t *tx_min_buf; + int32_t *rx_max_buf; + int32_t *rx_min_buf; + + int tdf; + bool retry; + int busy_cdc; + bool ctrl_lcm; +}; + +extern struct core_mp_test_data *core_mp; +extern struct mp_test_items tItems[]; + +extern void dump_data(void *data, int type, int len, int row_len, const char *name); +extern int core_mp_calc_timing_nodp(void); +extern int core_mp_ctrl_lcm_status(bool on); +extern void core_mp_test_free(void); +extern void core_mp_show_result(void); +extern void core_mp_run_test(char *item, bool ini); +extern int core_mp_move_code(void); +extern int core_mp_init(void); +#endif diff --git a/drivers/input/touchscreen/ilitek_mmi/core/parser.c b/drivers/input/touchscreen/ilitek_mmi/core/parser.c new file mode 100644 index 000000000000..e752d35659f2 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/parser.c @@ -0,0 +1,487 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "../common.h" +#include "parser.h" +#include "config.h" +#include +#include "../platform.h" + +#define PARSER_MAX_CFG_BUF (512 * 3) +#define PARSER_MAX_KEY_NUM (600 * 3) +#define PARSER_MAX_KEY_NAME_LEN 100 +#define PARSER_MAX_KEY_VALUE_LEN 2000 + +#define INI_ERR_OUT_OF_LINE -1 + +struct ini_file_data { + char pSectionName[PARSER_MAX_KEY_NAME_LEN]; + char pKeyName[PARSER_MAX_KEY_NAME_LEN]; + char pKeyValue[PARSER_MAX_KEY_VALUE_LEN]; + int iSectionNameLen; + int iKeyNameLen; + int iKeyValueLen; +} ilitek_ini_file_data[PARSER_MAX_KEY_NUM]; + +int g_ini_items; + +static int isspace_t(int x) +{ + if (x == ' ' || x == '\t' || x == '\n' || x == '\f' || x == '\b' || x == '\r') + return ERROR; + else + return SUCCESS; +} + +static char *ini_str_trim_r(char *buf) +{ + int len, i; + char tmp[512] = { 0 }; + + len = strlen(buf)+1; + for (i = 0 ; i < len ; i++) { + if (buf[i] != ' ') + break; + } + if (i < len) + strlcpy(tmp, (buf + i), (len - i)); + + strlcpy(buf, tmp, len); + return buf; +} + +/* Count the number of each line and assign the content to tmp buffer */ +static int get_ini_phy_line(char *data, char *buffer, int maxlen) +{ + int i = 0; + int j = 0; + int iRetNum = -1; + char ch1 = '\0'; + + for (i = 0, j = 0 ; i < maxlen ; j++) { + ch1 = data[j]; + iRetNum = j + 1; + if (ch1 == '\n' || ch1 == '\r') { /* line end */ + ch1 = data[j + 1]; + if (ch1 == '\n' || ch1 == '\r') { + iRetNum++; + } + + break; + } else if (ch1 == 0x00) { + break; /* file end */ + } + + buffer[i++] = ch1; + } + buffer[i] = '\0'; + return iRetNum; +} + +static int get_ini_phy_data(char *data, int fsize) +{ + int i, n = 0, res = 0, banchmark_flag = 0, empty_section, nodetype_flag = 0; + int offset = 0, isEqualSign = 0; + char *ini_buf = NULL, *tmpSectionName = NULL, *temp; + char M_CFG_SSL = '['; + char M_CFG_SSR = ']'; +/* char M_CFG_NIS = ':'; */ + char M_CFG_NTS = '#'; + char M_CFG_EQS = '='; + + if (data == NULL) { + ipio_err("INI data is NULL\n"); + res = -EINVAL; + goto out; + } + + ini_buf = kzalloc((PARSER_MAX_CFG_BUF + 1) * sizeof(char), GFP_KERNEL); + if (ERR_ALLOC_MEM(ini_buf)) { + ipio_err("Failed to allocate ini_buf memory, %ld\n", PTR_ERR(ini_buf)); + res = -ENOMEM; + goto out; + } + + tmpSectionName = kzalloc((PARSER_MAX_CFG_BUF + 1) * sizeof(char), GFP_KERNEL); + if (ERR_ALLOC_MEM(tmpSectionName)) { + ipio_err("Failed to allocate tmpSectionName memory, %ld\n", PTR_ERR(tmpSectionName)); + res = -ENOMEM; + goto out; + } + + temp = strnstr(data, TYPE_MARK, fsize); + if (temp != NULL) { + if (core_config->core_type == CORE_TYPE_B) + offset = temp-data; + else + fsize = temp-data; + } + + while (true) { + banchmark_flag = 0; + empty_section = 0; + nodetype_flag = 0; + if (g_ini_items > PARSER_MAX_KEY_NUM) { + ipio_err("MAX_KEY_NUM: Out of length\n"); + goto out; + } + + if (offset >= fsize) + goto out;/*over size*/ + n = get_ini_phy_line(data + offset, ini_buf, PARSER_MAX_CFG_BUF); + + if (n < 0) { + ipio_err("End of Line\n"); + goto out; + } + + offset += n; + + n = strlen(ini_str_trim_r(ini_buf)); + + if (n == 0 || ini_buf[0] == M_CFG_NTS) + continue; + + /* Get section names */ + if (n > 2 && ((ini_buf[0] == M_CFG_SSL && ini_buf[n - 1] != M_CFG_SSR))) { + ipio_err("Bad Section: %s\n", ini_buf); + res = -EINVAL; + goto out; + } else { + if (ini_buf[0] == M_CFG_SSL) { + ilitek_ini_file_data[g_ini_items].iSectionNameLen = n - 2; + if (ilitek_ini_file_data[g_ini_items].iSectionNameLen > PARSER_MAX_KEY_NAME_LEN) { + ipio_err("MAX_KEY_NAME_LEN: Out Of Length\n"); + res = INI_ERR_OUT_OF_LINE; + goto out; + } + + ini_buf[n - 1] = 0x00; + strlcpy((char *)tmpSectionName, ini_buf + 1, ((PARSER_MAX_CFG_BUF + 1) * sizeof(char))); + banchmark_flag = 0; + nodetype_flag = 0; + ipio_debug(DEBUG_PARSER, "Section Name: %s, Len: %d, offset = %d\n", tmpSectionName, n - 2, offset); + continue; + } + } + + /* copy section's name without square brackets to its real buffer */ + strlcpy(ilitek_ini_file_data[g_ini_items].pSectionName, tmpSectionName, strlen(tmpSectionName)+1); + ilitek_ini_file_data[g_ini_items].iSectionNameLen = strlen(tmpSectionName); + + isEqualSign = 0; + for (i = 0 ; i < n ; i++) { + if (ini_buf[i] == M_CFG_EQS) { + isEqualSign = i; + break; + } + if (ini_buf[i] == M_CFG_SSL || ini_buf[i] == M_CFG_SSR) { + empty_section = 1; + break; + } + } + + if (isEqualSign == 0) { + if (empty_section) + continue; + + if (strnstr(ilitek_ini_file_data[g_ini_items].pSectionName, "Benchmark_Data", strlen(ilitek_ini_file_data[g_ini_items].pSectionName)) > 0) { + banchmark_flag = 1; + isEqualSign = -1; + } else if (strnstr(ilitek_ini_file_data[g_ini_items].pSectionName, "Node Type", strlen(ilitek_ini_file_data[g_ini_items].pSectionName)) > 0) { + nodetype_flag = 1; + isEqualSign = -1; + } else { + continue; + } + } + if (banchmark_flag) { + /* Get Key names */ + ilitek_ini_file_data[g_ini_items].iKeyNameLen = strlen(BENCHMARK_KEY_NAME); + strlcpy(ilitek_ini_file_data[g_ini_items].pKeyName, BENCHMARK_KEY_NAME, strlen(BENCHMARK_KEY_NAME)+1); + ilitek_ini_file_data[g_ini_items].iKeyValueLen = n; + } else if (nodetype_flag) { + /* Get Key names */ + ilitek_ini_file_data[g_ini_items].iKeyNameLen = strlen(NODE_TYPE_KEY_NAME); + strlcpy(ilitek_ini_file_data[g_ini_items].pKeyName, NODE_TYPE_KEY_NAME, strlen(NODE_TYPE_KEY_NAME)+1); + ilitek_ini_file_data[g_ini_items].iKeyValueLen = n; + } else { + /* Get Key names */ + ilitek_ini_file_data[g_ini_items].iKeyNameLen = isEqualSign; + if (ilitek_ini_file_data[g_ini_items].iKeyNameLen > PARSER_MAX_KEY_NAME_LEN) { + /* ret = CFG_ERR_OUT_OF_LEN; */ + ipio_err("MAX_KEY_NAME_LEN: Out Of Length\n"); + res = INI_ERR_OUT_OF_LINE; + goto out; + } + + memcpy(ilitek_ini_file_data[g_ini_items].pKeyName, + ini_buf, ilitek_ini_file_data[g_ini_items].iKeyNameLen); + ilitek_ini_file_data[g_ini_items].iKeyValueLen = n - isEqualSign - 1; + } + + /* Get a value assigned to a key */ + + if (ilitek_ini_file_data[g_ini_items].iKeyValueLen > PARSER_MAX_KEY_VALUE_LEN) { + ipio_err("MAX_KEY_VALUE_LEN: Out Of Length\n"); + res = INI_ERR_OUT_OF_LINE; + goto out; + } + + memcpy(ilitek_ini_file_data[g_ini_items].pKeyValue, + ini_buf + isEqualSign + 1, ilitek_ini_file_data[g_ini_items].iKeyValueLen); + + ipio_debug(DEBUG_PARSER, "%s = %s\n", ilitek_ini_file_data[g_ini_items].pKeyName, + ilitek_ini_file_data[g_ini_items].pKeyValue); + + g_ini_items++; + } + +out: + ipio_kfree((void **)&ini_buf); + ipio_kfree((void **)&tmpSectionName); + return res; +} + +static void init_ilitek_ini_data(void) +{ + int i; + + g_ini_items = 0; + + /* Initialise ini strcture */ + for (i = 0 ; i < PARSER_MAX_KEY_NUM; i++) { + memset(ilitek_ini_file_data[i].pSectionName, 0, PARSER_MAX_KEY_NAME_LEN); + memset(ilitek_ini_file_data[i].pKeyName, 0, PARSER_MAX_KEY_NAME_LEN); + memset(ilitek_ini_file_data[i].pKeyValue, 0, PARSER_MAX_KEY_VALUE_LEN); + ilitek_ini_file_data[i].iSectionNameLen = 0; + ilitek_ini_file_data[i].iKeyNameLen = 0; + ilitek_ini_file_data[i].iKeyValueLen = 0; + } +} + +/* get_ini_key_value - get ini's key and value based on its section from its array + * + * A function is digging into the key and value by its section from the ini array. + * The comparsion is not only a string's name, but its length. + */ +static int get_ini_key_value(char *section, char *key, char *value) +{ + int i = 0; + int ret = ERROR; + int len = 0; + + len = strlen(key); + + for (i = 0 ; i < g_ini_items; i++) { + + if (strcmp(section, ilitek_ini_file_data[i].pSectionName) != 0) + continue; + if (strcmp(key, ilitek_ini_file_data[i].pKeyName) == 0) { + memcpy(value, ilitek_ini_file_data[i].pKeyValue, ilitek_ini_file_data[i].iKeyValueLen); + ret = 0; + break; + } + } + return ret; +} + + + +void core_parser_nodetype(int32_t *type_ptr, char *desp) +{ + + int i = 0, j = 0, index1 = 0, temp, count = 0; + char str[512] = { 0 }, record = ','; + + for (i = 0 ; i < g_ini_items; i++) { + + if ((strnstr(ilitek_ini_file_data[i].pSectionName, desp, strlen(ilitek_ini_file_data[i].pSectionName)) <= 0) || + strcmp(ilitek_ini_file_data[i].pKeyName, NODE_TYPE_KEY_NAME) != 0) { + continue; + } + + record = ','; + for (j = 0, index1 = 0 ; j <= ilitek_ini_file_data[i].iKeyValueLen ; j++) { + + if (ilitek_ini_file_data[i].pKeyValue[j] == ';' || j == ilitek_ini_file_data[i].iKeyValueLen) { + + if (record != '.') { + memset(str, 0, sizeof(str)); + memcpy(str, &ilitek_ini_file_data[i].pKeyValue[index1], (j - index1)); + temp = katoi(str); + type_ptr[count] = temp; + count++; + } + record = ilitek_ini_file_data[i].pKeyValue[j]; + index1 = j+1; + + } + } + + } +} + +void core_parser_benchmark(int32_t *max_ptr, int32_t *min_ptr, int8_t type, char *desp) +{ + int i = 0, j = 0, index1 = 0, temp, count = 0; + char str[512] = { 0 }, record = ','; + int32_t data[4]; + + for (i = 0 ; i < g_ini_items; i++) { + if ((strnstr(ilitek_ini_file_data[i].pSectionName, desp, strlen(ilitek_ini_file_data[i].pSectionName)) <= 0) || + strcmp(ilitek_ini_file_data[i].pKeyName, BENCHMARK_KEY_NAME) != 0) { + continue; + } + + record = ','; + for (j = 0, index1 = 0 ; j <= ilitek_ini_file_data[i].iKeyValueLen ; j++) { + if (ilitek_ini_file_data[i].pKeyValue[j] == ',' || ilitek_ini_file_data[i].pKeyValue[j] == ';' || + ilitek_ini_file_data[i].pKeyValue[j] == '.' || j == ilitek_ini_file_data[i].iKeyValueLen) { + + if (record != '.') { + memset(str, 0, sizeof(str)); + memcpy(str, &ilitek_ini_file_data[i].pKeyValue[index1], (j - index1)); + temp = katoi(str); + data[(count % 4)] = temp; + if ((count % 4) == 3) { + if (data[0] == 1) { + if (type == VALUE) { + max_ptr[count/4] = data[1] + data[2]; + min_ptr[count/4] = data[1] + data[3]; + } else { + max_ptr[count/4] = data[1] + (data[1] * data[2]) / 100; + min_ptr[count/4] = data[1] - (data[1] * data[3]) / 100; + } + } else { + max_ptr[count/4] = INT_MAX; + min_ptr[count/4] = INT_MIN; + } + } + count++; + } + record = ilitek_ini_file_data[i].pKeyValue[j]; + index1 = j + 1; + } + } + } +} +EXPORT_SYMBOL(core_parser_benchmark); + +int core_parser_get_u8_array(char *key, uint8_t *buf) +{ + char *s = key; + char *pToken; + int res, conut = 0; + long s_to_long = 0; + + if (isspace_t((int)(unsigned char)*s) == SUCCESS) { + while ((pToken = strsep(&s, ",")) != NULL) { + res = kstrtol(pToken, 0, &s_to_long); + if (res == 0) + buf[conut] = s_to_long; + else + ipio_info("convert string too long, res = %d\n", res); + conut++; + } + } + + return conut; +} +EXPORT_SYMBOL(core_parser_get_u8_array); + +int core_parser_get_int_data(char *section, char *keyname, char *rv) +{ + int len = 0; + char value[512] = { 0 }; + + if (rv == NULL || section == NULL || keyname == NULL) { + ipio_err("Parameters are invalid\n"); + return -EINVAL; + } + + /* return a white-space string if get nothing */ + if (get_ini_key_value(section, keyname, value) < 0) { + snprintf(rv, sizeof(value), "%s", value); + return 0; + } + + len = snprintf(rv, sizeof(value), "%s", value); + + return len; +} +EXPORT_SYMBOL(core_parser_get_int_data); + + +int core_parser_path(char *path) +{ + int res = 0, fsize = 0, i = 0; + char *tmp = NULL; + const struct firmware *mp_ini; + + res = request_firmware(&mp_ini, path, &ipd->client->dev); + if (res < 0) { + ipio_err("Failed to open the file Name %s.\n", path); + return -ENOENT; + } + + ipio_info("path = %s\n", path); + + fsize = mp_ini->size; + ipio_info("fsize = %d\n", fsize); + if (fsize <= 0) { + ipio_err("The size of file is invaild\n"); + res = -EINVAL; + goto out; + } + + tmp = kmalloc(fsize+1, GFP_KERNEL); + if (ERR_ALLOC_MEM(tmp)) { + ipio_err("Failed to allocate tmp memory, %ld\n", PTR_ERR(tmp)); + res = -ENOMEM; + goto out; + } + + + for (i = 0 ; i < fsize ; i++) + tmp[i] = mp_ini->data[i]; + + tmp[fsize] = 0x00; + + init_ilitek_ini_data(); + + res = get_ini_phy_data(tmp, fsize); + if (res < 0) { + ipio_err("Failed to get physical ini data, res = %d\n", res); + goto out; + } + + ipio_info("Parsing INI file doen\n"); + +out: + ipio_kfree((void **)&tmp); + release_firmware(mp_ini); + return res; +} +EXPORT_SYMBOL(core_parser_path); diff --git a/drivers/input/touchscreen/ilitek_mmi/core/parser.h b/drivers/input/touchscreen/ilitek_mmi/core/parser.h new file mode 100644 index 000000000000..7d85612bd812 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/parser.h @@ -0,0 +1,40 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __PARSER_H +#define __PARSER_H +#include "config.h" + +#define BENCHMARK_KEY_NAME "Benchmark_Data" +#define NODE_TYPE_KEY_NAME "node_type_Data" +#define TYPE_MARK "[Driver Type Mark]" +#define VALUE 0 + +extern void core_parser_nodetype(int32_t *type_ptr, char *desp); +extern void core_parser_benchmark(int32_t *max_ptr, int32_t *min_ptr, int8_t type, char *desp); +extern int core_parser_get_u8_array(char *key, uint8_t *buf); +extern int core_parser_get_int_data(char *section, char *keyname, char *rv); +extern int core_parser_path(char *path); + +#endif diff --git a/drivers/input/touchscreen/ilitek_mmi/core/protocol.c b/drivers/input/touchscreen/ilitek_mmi/core/protocol.c new file mode 100644 index 000000000000..60332bd29b33 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/protocol.c @@ -0,0 +1,425 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "../common.h" +#include "config.h" +#include "i2c.h" +#include "spi.h" +#include "protocol.h" + +#define FUNC_NUM 20 + +struct protocol_sup_list { + uint8_t major; + uint8_t mid; + uint8_t minor; +}; + +struct DataItem { + int key; + char *name; + int len; + uint8_t *cmd; +}; + +struct DataItem *hashArray[FUNC_NUM]; +struct protocol_cmd_list *protocol; + +int core_write(uint8_t nSlaveId, uint8_t *pBuf, uint16_t nSize) +{ +#if (INTERFACE == I2C_INTERFACE) + return core_i2c_write(nSlaveId, pBuf, nSize); +#else + return core_spi_write(pBuf, nSize); +#endif +} +EXPORT_SYMBOL(core_write); + +int core_read(uint8_t nSlaveId, uint8_t *pBuf, uint16_t nSize) +{ +#if (INTERFACE == I2C_INTERFACE) + return core_i2c_read(nSlaveId, pBuf, nSize); +#else + return core_spi_read(pBuf, nSize); +#endif +} +EXPORT_SYMBOL(core_read); + +static int hashCode(int key) +{ + return key % FUNC_NUM; +} + +static struct DataItem *search_func(int key) +{ + int hashIndex = hashCode(key); + + while (hashArray[hashIndex] != NULL) { + if (hashArray[hashIndex]->key == key) + return hashArray[hashIndex]; + + hashIndex++; + hashIndex %= FUNC_NUM; + } + + return NULL; +} + +static void insert_func(int key, int len, uint8_t *cmd, char *name) +{ + int i, hashIndex; + struct DataItem *tmp = NULL; + + tmp = kmalloc(sizeof(struct DataItem), GFP_KERNEL); + if (ERR_ALLOC_MEM(tmp)) { + ipio_err("Failed to allocate memory\n"); + return; + } + + tmp->key = key; + tmp->name = name; + tmp->len = len; + + tmp->cmd = kcalloc(len, sizeof(uint8_t), GFP_KERNEL); + for (i = 0 ; i < len ; i++) + tmp->cmd[i] = cmd[i]; + + hashIndex = hashCode(key); + + while (hashArray[hashIndex] != NULL) { + hashIndex++; + hashIndex %= FUNC_NUM; + } + + hashArray[hashIndex] = tmp; +} + +static void free_func_hash(void) +{ + int i; + + for (i = 0 ; i < FUNC_NUM; i++) + ipio_kfree((void **)&hashArray[i]); +} + +static void create_func_hash(void) +{ + /* if protocol is updated, we free its allocated mem at all before create new ones. */ + free_func_hash(); + + insert_func(0, protocol->func_ctrl_len, protocol->sense_ctrl, "sense_ctrl"); + insert_func(1, protocol->func_ctrl_len, protocol->sleep_ctrl, "sleep_ctrl"); + insert_func(2, protocol->func_ctrl_len, protocol->glove_ctrl, "glove_ctrl"); + insert_func(3, protocol->func_ctrl_len, protocol->stylus_ctrl, "stylus_ctrl"); + insert_func(4, protocol->func_ctrl_len, protocol->tp_scan_mode, "tp_scan_mode"); + insert_func(5, protocol->func_ctrl_len, protocol->lpwg_ctrl, "lpwg_ctrl"); + insert_func(6, protocol->func_ctrl_len, protocol->gesture_ctrl, "gesture_ctrl"); + insert_func(7, protocol->func_ctrl_len, protocol->phone_cover_ctrl, "phone_cover_ctrl"); + insert_func(8, protocol->func_ctrl_len, protocol->finger_sense_ctrl, "finger_sense_ctrl"); + insert_func(9, protocol->window_len, protocol->phone_cover_window, "phone_cover_window"); + insert_func(10, protocol->func_ctrl_len, protocol->finger_sense_ctrl, "finger_sense_ctrl"); + insert_func(11, protocol->func_ctrl_len, protocol->proximity_ctrl, "proximity_ctrl"); + insert_func(12, protocol->func_ctrl_len, protocol->plug_ctrl, "plug_ctrl"); +} + +static void config_protocol_v5_cmd(void) +{ + if (protocol->mid == 0x0) { + protocol->func_ctrl_len = 2; + + protocol->sense_ctrl[0] = 0x1; + protocol->sense_ctrl[1] = 0x0; + + protocol->sleep_ctrl[0] = 0x2; + protocol->sleep_ctrl[1] = 0x0; + + protocol->glove_ctrl[0] = 0x6; + protocol->glove_ctrl[1] = 0x0; + + protocol->stylus_ctrl[0] = 0x7; + protocol->stylus_ctrl[1] = 0x0; + + protocol->tp_scan_mode[0] = 0x8; + protocol->tp_scan_mode[1] = 0x0; + + protocol->lpwg_ctrl[0] = 0xA; + protocol->lpwg_ctrl[1] = 0x0; + + protocol->gesture_ctrl[0] = 0xB; + protocol->gesture_ctrl[1] = 0x3F; + + protocol->phone_cover_ctrl[0] = 0xC; + protocol->phone_cover_ctrl[1] = 0x0; + + protocol->finger_sense_ctrl[0] = 0xF; + protocol->finger_sense_ctrl[1] = 0x0; + + protocol->phone_cover_window[0] = 0xD; + + /* Non support on v5.0 */ + /* protocol->proximity_ctrl[0] = 0x10; */ + /* protocol->proximity_ctrl[1] = 0x0; */ + + /* protocol->plug_ctrl[0] = 0x11; */ + /* protocol->plug_ctrl[1] = 0x0; */ + } else { + protocol->func_ctrl_len = 3; + + protocol->sense_ctrl[0] = 0x1; + protocol->sense_ctrl[1] = 0x1; + protocol->sense_ctrl[2] = 0x0; + + protocol->sleep_ctrl[0] = 0x1; + protocol->sleep_ctrl[1] = 0x2; + protocol->sleep_ctrl[2] = 0x0; + + protocol->glove_ctrl[0] = 0x1; + protocol->glove_ctrl[1] = 0x6; + protocol->glove_ctrl[2] = 0x0; + + protocol->stylus_ctrl[0] = 0x1; + protocol->stylus_ctrl[1] = 0x7; + protocol->stylus_ctrl[2] = 0x0; + + protocol->tp_scan_mode[0] = 0x1; + protocol->tp_scan_mode[1] = 0x8; + protocol->tp_scan_mode[2] = 0x0; + + protocol->lpwg_ctrl[0] = 0x1; + protocol->lpwg_ctrl[1] = 0xA; + protocol->lpwg_ctrl[2] = 0x0; + + protocol->gesture_ctrl[0] = 0x1; + protocol->gesture_ctrl[1] = 0xB; + protocol->gesture_ctrl[2] = 0x3F; + + protocol->phone_cover_ctrl[0] = 0x1; + protocol->phone_cover_ctrl[1] = 0xC; + protocol->phone_cover_ctrl[2] = 0x0; + + protocol->finger_sense_ctrl[0] = 0x1; + protocol->finger_sense_ctrl[1] = 0xF; + protocol->finger_sense_ctrl[2] = 0x0; + + protocol->proximity_ctrl[0] = 0x1; + protocol->proximity_ctrl[1] = 0x10; + protocol->proximity_ctrl[2] = 0x0; + + protocol->plug_ctrl[0] = 0x1; + protocol->plug_ctrl[1] = 0x11; + protocol->plug_ctrl[2] = 0x0; + + protocol->phone_cover_window[0] = 0xE; + } + if (protocol->mid >= 0x3) + protocol->fw_ver_len = 9; + else + protocol->fw_ver_len = 4; + + if (protocol->mid == 0x1) { + protocol->pro_ver_len = 3; + } else { + protocol->pro_ver_len = 4; + } + + protocol->tp_info_len = 14; + protocol->key_info_len = 30; + protocol->core_ver_len = 5; + protocol->window_len = 8; + + /* The commadns about panel information */ + protocol->cmd_read_ctrl = P5_0_READ_DATA_CTRL; + protocol->cmd_get_tp_info = P5_0_GET_TP_INFORMATION; + protocol->cmd_get_key_info = P5_0_GET_KEY_INFORMATION; + protocol->cmd_get_fw_ver = P5_0_GET_FIRMWARE_VERSION; + protocol->cmd_get_pro_ver = P5_0_GET_PROTOCOL_VERSION; + protocol->cmd_get_core_ver = P5_0_GET_CORE_VERSION; + protocol->cmd_mode_ctrl = P5_0_MODE_CONTROL; + protocol->cmd_cdc_busy = P5_0_CDC_BUSY_STATE; + protocol->cmd_i2cuart = P5_0_I2C_UART; + + /* The commands about the packets of finger report from FW */ + protocol->unknown_mode = P5_0_FIRMWARE_UNKNOWN_MODE; + protocol->demo_mode = P5_0_FIRMWARE_DEMO_MODE; + protocol->debug_mode = P5_0_FIRMWARE_DEBUG_MODE; + protocol->test_mode = P5_0_FIRMWARE_TEST_MODE; + protocol->i2cuart_mode = P5_0_FIRMWARE_I2CUART_MODE; + protocol->gesture_mode = P5_0_FIRMWARE_GESTURE_MODE; + + protocol->delta_data = P5_0_FIRMWARE_DELTA_DATA_MODE; + protocol->raw_data = P5_0_FIRMWARE_RAW_DATA_MODE; + + protocol->demo_pid = P5_0_DEMO_PACKET_ID; + protocol->debug_pid = P5_0_DEBUG_PACKET_ID; + protocol->test_pid = P5_0_TEST_PACKET_ID; + protocol->i2cuart_pid = P5_0_I2CUART_PACKET_ID; + protocol->ges_pid = P5_0_GESTURE_PACKET_ID; + + protocol->demo_len = P5_0_DEMO_MODE_PACKET_LENGTH; + protocol->debug_len = P5_0_DEBUG_MODE_PACKET_LENGTH; + protocol->test_len = P5_0_TEST_MODE_PACKET_LENGTH; + + /* The commands about MP test */ + protocol->cmd_cdc = P5_0_SET_CDC_INIT; + protocol->cmd_get_cdc = P5_0_GET_CDC_DATA; + + if (protocol->mid < 4) { + protocol->cdc_len = 3; + } else { + protocol->cdc_len = 15; + } + + protocol->mutual_dac = 0x1; + protocol->mutual_bg = 0x2; + protocol->mutual_signal = 0x3; + protocol->mutual_no_bk = 0x5; + protocol->mutual_has_bk = 0x8; + protocol->mutual_bk_dac = 0x10; + + protocol->self_dac = 0xC; + protocol->self_bg = 0xF; + protocol->self_signal = 0xD; + protocol->self_no_bk = 0xE; + protocol->self_has_bk = 0xB; + protocol->self_bk_dac = 0x11; + + protocol->key_dac = 0x14; + protocol->key_bg = 0x16; + protocol->key_no_bk = 0x7; + protocol->key_has_bk = 0x15; + protocol->key_open = 0x12; + protocol->key_short = 0x13; + + protocol->st_dac = 0x1A; + protocol->st_bg = 0x1C; + protocol->st_no_bk = 0x17; + protocol->st_has_bk = 0x1B; + protocol->st_open = 0x18; + + protocol->tx_short = 0x19; + + protocol->rx_short = 0x4; + protocol->rx_open = 0x6; + + protocol->tx_rx_delta = 0x1E; + + protocol->cm_data = 0x9; + protocol->cs_data = 0xA; + + protocol->trcrq_pin = 0x20; + protocol->resx2_pin = 0x21; + protocol->mutual_integra_time = 0x22; + protocol->self_integra_time = 0x23; + protocol->key_integra_time = 0x24; + protocol->st_integra_time = 0x25; + protocol->peak_to_peak = 0x1D; + + protocol->get_timing = 0x30; + protocol->doze_p2p = 0x32; + protocol->doze_raw = 0x33; +} + +void core_protocol_func_control(int key, int ctrl) +{ + struct DataItem *tmp = search_func(key); + + if (tmp != NULL) { + ipio_debug(DEBUG_BOOT,"Found func's name: %s, key = %d\n", tmp->name, key); + + /* last element is used to control this func */ + if (tmp->key != 9) { + if (tmp->key == 1 && ctrl == 0) { + ipio_info("TP enter deep sleep in\n"); + tmp->cmd[tmp->len - 1] = 0x03; + } else { + tmp->cmd[tmp->len - 1] = ctrl; + } + } + ipio_info("Found func's name: %s, key = %d cmd: 0x%02X 0x%02X 0x%02X\n",\ + tmp->name, key, tmp->cmd[0], tmp->cmd[1], tmp->cmd[2]); + + core_write(core_config->slave_i2c_addr, tmp->cmd, tmp->len); + return; + } + + ipio_info("Can't find any main functions\n"); +} +EXPORT_SYMBOL(core_protocol_func_control); + +int core_protocol_update_ver(uint8_t major, uint8_t mid, uint8_t minor) +{ + int i = 0; + struct protocol_sup_list pver[] = { + {0x5, 0x0, 0x0}, + {0x5, 0x1, 0x0}, + {0x5, 0x2, 0x0}, + {0x5, 0x3, 0x0}, + {0x5, 0x4, 0x0}, + }; + + for (i = 0 ; i < ARRAY_SIZE(pver); i++) { + if (pver[i].major == major && pver[i].mid == mid && pver[i].minor == minor) { + protocol->major = major; + protocol->mid = mid; + protocol->minor = minor; + ipio_debug(DEBUG_CONFIG, "protocol: major = %d, mid = %d, minor = %d\n", + protocol->major, protocol->mid, protocol->minor); + + if (protocol->major == 0x5) + config_protocol_v5_cmd(); + + /* We need to recreate function controls because of the commands updated. */ + create_func_hash(); + return 0; + } + } + + ipio_err("Doesn't support this version of protocol\n"); + return ERROR; +} +EXPORT_SYMBOL(core_protocol_update_ver); + +int core_protocol_init(void) +{ + if (protocol == NULL) { + protocol = kzalloc(sizeof(*protocol), GFP_KERNEL); + if (ERR_ALLOC_MEM(protocol)) { + ipio_err("Failed to allocate protocol mem, %ld\n", PTR_ERR(protocol)); + core_protocol_remove(); + return -ENOMEM; + } + } + + /* The default version must only once be set up at this initial time. */ + core_protocol_update_ver(PROTOCOL_MAJOR, PROTOCOL_MID, PROTOCOL_MINOR); + return 0; +} +EXPORT_SYMBOL(core_protocol_init); + +void core_protocol_remove(void) +{ + ipio_info("Remove core-protocol memebers\n"); + ipio_kfree((void **)&protocol); + free_func_hash(); +} +EXPORT_SYMBOL(core_protocol_remove); diff --git a/drivers/input/touchscreen/ilitek_mmi/core/protocol.h b/drivers/input/touchscreen/ilitek_mmi/core/protocol.h new file mode 100644 index 000000000000..bc5373e74e53 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/protocol.h @@ -0,0 +1,196 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef __PROTOCOL_H +#define __PROTOCOL_H + +/* V3.2 */ +#define P3_2_GET_TP_INFORMATION 0x20 +#define P3_2_GET_KEY_INFORMATION 0x22 +#define P3_2_GET_FIRMWARE_VERSION 0x40 +#define P3_2_GET_PROTOCOL_VERSION 0x42 + +/* V5.X */ +#define P5_0_READ_DATA_CTRL 0xF6 +#define P5_0_GET_TP_INFORMATION 0x20 +#define P5_0_GET_KEY_INFORMATION 0x27 +#define P5_0_GET_FIRMWARE_VERSION 0x21 +#define P5_0_GET_PROTOCOL_VERSION 0x22 +#define P5_0_GET_CORE_VERSION 0x23 +#define P5_0_MODE_CONTROL 0xF0 +#define P5_0_SET_CDC_INIT 0xF1 +#define P5_0_GET_CDC_DATA 0xF2 +#define P5_0_CDC_BUSY_STATE 0xF3 +#define P5_0_I2C_UART 0x40 + +#define P5_0_FIRMWARE_UNKNOWN_MODE 0xFF +#define P5_0_FIRMWARE_DEMO_MODE 0x00 +#define P5_0_FIRMWARE_TEST_MODE 0x01 +#define P5_0_FIRMWARE_DEBUG_MODE 0x02 +#define P5_0_FIRMWARE_I2CUART_MODE 0x03 +#define P5_0_FIRMWARE_GESTURE_MODE 0x04 + +#define P5_0_FIRMWARE_DELTA_DATA_MODE 0x03 +#define P5_0_FIRMWARE_RAW_DATA_MODE 0x08 + +#define P5_0_DEMO_PACKET_ID 0x5A +#define P5_0_DEBUG_PACKET_ID 0xA7 +#define P5_0_TEST_PACKET_ID 0xF2 +#define P5_0_GESTURE_PACKET_ID 0xAA +#define P5_0_I2CUART_PACKET_ID 0x7A + +#define P5_0_DEMO_MODE_PACKET_LENGTH 43 +#define P5_0_DEBUG_MODE_PACKET_LENGTH 1280 +#define P5_0_TEST_MODE_PACKET_LENGTH 1180 + +struct protocol_cmd_list { + /* version of protocol */ + uint8_t major; + uint8_t mid; + uint8_t minor; + + /* Length of command */ + int fw_ver_len; + int pro_ver_len; + int tp_info_len; + int key_info_len; + int core_ver_len; + int func_ctrl_len; + int window_len; + int cdc_len; + int cdc_raw_len; + + /* TP information */ + uint8_t cmd_read_ctrl; + uint8_t cmd_get_tp_info; + uint8_t cmd_get_key_info; + uint8_t cmd_get_fw_ver; + uint8_t cmd_get_pro_ver; + uint8_t cmd_get_core_ver; + uint8_t cmd_mode_ctrl; + uint8_t cmd_i2cuart; + uint8_t cmd_cdc_busy; + + /* Function control */ + uint8_t sense_ctrl[3]; + uint8_t sleep_ctrl[3]; + uint8_t glove_ctrl[3]; + uint8_t stylus_ctrl[3]; + uint8_t tp_scan_mode[3]; + uint8_t lpwg_ctrl[3]; + uint8_t gesture_ctrl[3]; + uint8_t phone_cover_ctrl[3]; + uint8_t finger_sense_ctrl[3]; + uint8_t proximity_ctrl[3]; + uint8_t plug_ctrl[3]; + uint8_t phone_cover_window[9]; + + /* firmware mode */ + uint8_t unknown_mode; + uint8_t demo_mode; + uint8_t debug_mode; + uint8_t test_mode; + uint8_t i2cuart_mode; + uint8_t gesture_mode; + + /* debug mode type*/ + uint8_t delta_data; + uint8_t raw_data; + + /* Pakcet ID reported by FW */ + uint8_t demo_pid; + uint8_t debug_pid; + uint8_t i2cuart_pid; + uint8_t test_pid; + uint8_t ges_pid; + + /* Length of finger report */ + int demo_len; + int debug_len; + int test_len; + int gesture_len; + + /* MP Test with cdc commands */ + uint8_t cmd_cdc; + uint8_t cmd_get_cdc; + + uint8_t cm_data; + uint8_t cs_data; + + uint8_t rx_short; + uint8_t rx_open; + uint8_t tx_short; + + uint8_t mutual_dac; + uint8_t mutual_bg; + uint8_t mutual_signal; + uint8_t mutual_no_bk; + uint8_t mutual_bk_dac; + uint8_t mutual_has_bk; + uint16_t mutual_has_bk_16; + + uint8_t self_dac; + uint8_t self_bk_dac; + uint8_t self_has_bk; + uint8_t self_no_bk; + uint8_t self_signal; + uint8_t self_bg; + + uint8_t key_dac; + uint8_t key_has_bk; + uint8_t key_bg; + uint8_t key_no_bk; + uint8_t key_open; + uint8_t key_short; + + uint8_t st_no_bk; + uint8_t st_open; + uint8_t st_dac; + uint8_t st_has_bk; + uint8_t st_bg; + + uint8_t tx_rx_delta; + + uint8_t trcrq_pin; + uint8_t resx2_pin; + uint8_t mutual_integra_time; + uint8_t self_integra_time; + uint8_t key_integra_time; + uint8_t st_integra_time; + uint8_t peak_to_peak; + + uint8_t get_timing; + uint8_t doze_p2p; + uint8_t doze_raw; +}; + +extern struct protocol_cmd_list *protocol; + +extern void core_protocol_func_control(int key, int ctrl); +extern int core_protocol_update_ver(uint8_t major, uint8_t mid, uint8_t minor); +extern int core_protocol_init(void); +extern void core_protocol_remove(void); +extern int core_write(uint8_t, uint8_t *, uint16_t); +extern int core_read(uint8_t, uint8_t *, uint16_t); + +#endif diff --git a/drivers/input/touchscreen/ilitek_mmi/core/spi.c b/drivers/input/touchscreen/ilitek_mmi/core/spi.c new file mode 100644 index 000000000000..e811bf623f19 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/spi.c @@ -0,0 +1,412 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "../common.h" +#include "config.h" +#include "spi.h" +#include "finger_report.h" + +struct core_spi_data *core_spi; + +int core_Rx_check(uint16_t check) +{ + int size = 0, i, count = 100; + uint8_t txbuf[5] = { 0 }, rxbuf[4] = {0}; + uint16_t status = 0; + + txbuf[0] = SPI_WRITE; + txbuf[1] = 0x25; + txbuf[2] = 0x94; + txbuf[3] = 0x0; + txbuf[4] = 0x2; + + for (i = 0 ; i < count; i++) { + + txbuf[0] = SPI_WRITE; + if (spi_write_then_read(core_spi->spi, txbuf, 5, txbuf, 0) < 0) { + size = -EIO; + ipio_err("spi Write Error, res = %d\n", size); + } + + txbuf[0] = SPI_READ; + if (spi_write_then_read(core_spi->spi, txbuf, 1, rxbuf, 4) < 0) { + size = -EIO; + ipio_err("spi Read Error, res = %d\n", size); + } + + status = (rxbuf[2] << 8) + rxbuf[3]; + size = (rxbuf[0] << 8) + rxbuf[1]; + + if (status == check) + return size; + + mdelay(1); + } + + size = -EIO; + ipio_err("Check lock error\n"); + return size; +} + +int core_Tx_unlock_check(void) +{ + int res = 0, i, count = 100; + uint8_t txbuf[5] = { 0 }, rxbuf[4] = {0}; + uint16_t unlock = 0; + + txbuf[0] = SPI_WRITE; + txbuf[1] = 0x25; + txbuf[2] = 0x0; + txbuf[3] = 0x0; + txbuf[4] = 0x2; + + for (i = 0; i < count; i++) { + txbuf[0] = SPI_WRITE; + if (spi_write_then_read(core_spi->spi, txbuf, 5, txbuf, 0) < 0) { + res = -EIO; + ipio_err("spi Write Error, res = %d\n", res); + } + + txbuf[0] = SPI_READ; + if (spi_write_then_read(core_spi->spi, txbuf, 1, rxbuf, 4) < 0) { + res = -EIO; + ipio_err("spi Read Error, res = %d\n", res); + } + + unlock = (rxbuf[2] << 8) + rxbuf[3]; + + if (unlock == 0x9881) + return res; + + mdelay(1); + } + + ipio_err("Check unlock error\n"); + return res; +} + +int core_ice_mode_read_9881H11(uint8_t *data, uint32_t size) +{ + int res = 0; + uint8_t txbuf[64] = { 0 }; + + /* set read address */ + txbuf[0] = SPI_WRITE; + txbuf[1] = 0x25; + txbuf[2] = 0x98; + txbuf[3] = 0x0; + txbuf[4] = 0x2; + if (spi_write_then_read(core_spi->spi, txbuf, 5, txbuf, 0) < 0) { + res = -EIO; + return res; + } + + /* read data */ + txbuf[0] = SPI_READ; + if (spi_write_then_read(core_spi->spi, txbuf, 1, data, size) < 0) { + res = -EIO; + return res; + } + + /* write data unlock */ + txbuf[0] = SPI_WRITE; + txbuf[1] = 0x25; + txbuf[2] = 0x94; + txbuf[3] = 0x0; + txbuf[4] = 0x2; + txbuf[5] = (size & 0xFF00) >> 8; + txbuf[6] = size & 0xFF; + txbuf[7] = (char)0x98; + txbuf[8] = (char)0x81; + if (spi_write_then_read(core_spi->spi, txbuf, 9, txbuf, 0) < 0) { + res = -EIO; + ipio_err("spi Write data unlock error, res = %d\n", res); + } + + return res; +} + +int core_ice_mode_write_9881H11(uint8_t *data, uint32_t size) +{ + int res = 0; + uint8_t check_sum = 0, wsize = 0; + uint8_t *txbuf = NULL; + + txbuf = kcalloc(size + 9, sizeof(uint8_t), GFP_KERNEL); + if (ERR_ALLOC_MEM(txbuf)) { + ipio_err("Failed to allocate txbuf\n"); + res = -ENOMEM; + goto out; + } + + /* Write data */ + txbuf[0] = SPI_WRITE; + txbuf[1] = 0x25; + txbuf[2] = 0x4; + txbuf[3] = 0x0; + txbuf[4] = 0x2; + + /* Calcuate checsum and fill it in the last byte */ + check_sum = core_fr_calc_checksum(data, size); + memcpy(txbuf + 5, data, size); + txbuf[5 + size] = check_sum; + size++; + wsize = size; + if (wsize % 4 != 0) + wsize += 4 - (wsize % 4); + + if (spi_write_then_read(core_spi->spi, txbuf, wsize + 5, txbuf, 0) < 0) { + res = -EIO; + ipio_err("spi Write Error, res = %d\n", res); + goto out; + } + + /* write data lock */ + txbuf[0] = SPI_WRITE; + txbuf[1] = 0x25; + txbuf[2] = 0x0; + txbuf[3] = 0x0; + txbuf[4] = 0x2; + txbuf[5] = (size & 0xFF00) >> 8; + txbuf[6] = size & 0xFF; + txbuf[7] = (char)0x5A; + txbuf[8] = (char)0xA5; + if (spi_write_then_read(core_spi->spi, txbuf, 9, txbuf, 0) < 0) { + res = -EIO; + ipio_err("spi Write data lock Error, res = %d\n", res); + } + +out: + ipio_kfree((void **)&txbuf); + return res; +} + +int core_ice_mode_disable_9881H11(void) +{ + int res = 0; + uint8_t txbuf[5] = {0}; + + txbuf[0] = 0x82; + txbuf[1] = 0x1B; + txbuf[2] = 0x62; + txbuf[3] = 0x10; + txbuf[4] = 0x18; + + if (spi_write_then_read(core_spi->spi, txbuf, 5, txbuf, 0) < 0) { + res = -EIO; + ipio_err("spi_write_then_read Error, res = %d\n", res); + } + + return res; +} + +int core_ice_mode_enable_9881H11(void) +{ + int res = 0; + uint8_t txbuf[5] = {0}, rxbuf[2] = {0}; + + txbuf[0] = 0x82; + txbuf[1] = 0x1F; + txbuf[2] = 0x62; + txbuf[3] = 0x10; + txbuf[4] = 0x18; + + if (spi_write_then_read(core_spi->spi, txbuf, 1, rxbuf, 1) < 0) { + res = -EIO; + ipio_err("spi Write Error, res = %d\n", res); + return res; + } + + /* check recover data */ + if (rxbuf[0] == 0x82) { + ipio_info("rxbuf:0x%x\n", rxbuf[0]); + return CHECK_RECOVER; + } + + if (spi_write_then_read(core_spi->spi, txbuf, 5, rxbuf, 0) < 0) { + res = -EIO; + ipio_err("spi Write Error, res = %d\n", res); + } + + return res; +} + +int core_spi_read_9881H11(uint8_t *pBuf, uint16_t nSize) +{ + int res = 0, size = 0; + + res = core_ice_mode_enable_9881H11(); + if (res < 0) { + goto out; + } + + size = core_Rx_check(0x5AA5); + if (size < 0) { + res = -EIO; + goto out; + } + + if (core_ice_mode_read_9881H11(pBuf, size) < 0) { + res = -EIO; + goto out; + } + + if (core_ice_mode_disable_9881H11() < 0) { + res = -EIO; + goto out; + } + +out: + return res; +} + +int core_spi_write_9881H11(uint8_t *pBuf, uint16_t nSize) +{ + int res = 0; + uint8_t *txbuf = NULL; + + txbuf = kcalloc(nSize + 5, sizeof(uint8_t), GFP_KERNEL); + if (ERR_ALLOC_MEM(txbuf)) { + ipio_err("Failed to allocate txbuf\n"); + return -ENOMEM; + goto out; + } + + res = core_ice_mode_enable_9881H11(); + if (res < 0) + return res; + + if (core_ice_mode_write_9881H11(pBuf, nSize) < 0) { + res = -EIO; + goto out; + } + + if (core_Tx_unlock_check() < 0) { + res = -ETXTBSY; + } + +out: + ipio_kfree((void **)&txbuf); + return res; +} + +int core_spi_write(uint8_t *pBuf, uint16_t nSize) +{ + int res = 0; + uint8_t *txbuf = NULL; + + txbuf = kcalloc(nSize + 1, sizeof(uint8_t), GFP_KERNEL); + if (ERR_ALLOC_MEM(txbuf)) { + ipio_err("Failed to allocate txbuf\n"); + return -ENOMEM; + goto out; + } + + if (core_config->icemodeenable == false) { + res = core_spi_write_9881H11(pBuf, nSize); + core_ice_mode_disable_9881H11(); + ipio_kfree((void **)&txbuf); + return res; + } + + txbuf[0] = SPI_WRITE; + memcpy(txbuf+1, pBuf, nSize); + + if (spi_write_then_read(core_spi->spi, txbuf, nSize+1, txbuf, 0) < 0) { + if (core_config->do_ic_reset) { + /* ignore spi error if doing ic reset */ + res = 0; + } else { + res = -EIO; + ipio_err("spi Write Error, res = %d\n", res); + goto out; + } + } + +out: + ipio_kfree((void **)&txbuf); + return res; +} +EXPORT_SYMBOL(core_spi_write); + +int core_spi_read(uint8_t *pBuf, uint16_t nSize) +{ + int res = 0; + uint8_t txbuf[1] = {0}; + + txbuf[0] = SPI_READ; + + if (core_config->icemodeenable == false) + return core_spi_read_9881H11(pBuf, nSize); + + if (spi_write_then_read(core_spi->spi, txbuf, 1, pBuf, nSize) < 0) { + if (core_config->do_ic_reset) { + /* ignore spi error if doing ic reset */ + res = 0; + } else { + res = -EIO; + ipio_err("spi Read Error, res = %d\n", res); + goto out; + } + } + +out: + return res; +} +EXPORT_SYMBOL(core_spi_read); + +int core_spi_init(struct spi_device *spi) +{ + int ret; + + core_spi = kmalloc(sizeof(*core_spi), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_spi)) { + ipio_err("Failed to alllocate core_i2c mem %ld\n", PTR_ERR(core_spi)); + core_spi_remove(); + return -ENOMEM; + } + + core_spi->spi = spi; + spi->mode = SPI_MODE_0; + spi->bits_per_word = 8; + + ret = spi_setup(spi); + if (ret < 0) { + ipio_err("ERR: fail to setup spi\n"); + return -ENODEV; + } + + ipio_info("name = %s, bus_num = %d,cs = %d, mode = %d, speed = %d\n", spi->modalias, + spi->master->bus_num, spi->chip_select, spi->mode, spi->max_speed_hz); + + return 0; +} +EXPORT_SYMBOL(core_spi_init); + +void core_spi_remove(void) +{ + ipio_info("Remove core-spi members\n"); + ipio_kfree((void **)&core_spi); +} +EXPORT_SYMBOL(core_spi_remove); diff --git a/drivers/input/touchscreen/ilitek_mmi/core/spi.h b/drivers/input/touchscreen/ilitek_mmi/core/spi.h new file mode 100644 index 000000000000..281650223568 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/core/spi.h @@ -0,0 +1,42 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SPI_H +#define __SPI_H + +#define SPI_WRITE 0X82 +#define SPI_READ 0X83 + +struct core_spi_data { + struct spi_device *spi; +}; + +extern struct core_spi_data *core_spi; + +extern int core_spi_write(uint8_t *pBuf, uint16_t nSize); +extern int core_spi_read(uint8_t *pBuf, uint16_t nSize); +extern int core_spi_init(struct spi_device *spi); +extern void core_spi_remove(void); + +#endif diff --git a/drivers/input/touchscreen/ilitek_mmi/platform.c b/drivers/input/touchscreen/ilitek_mmi/platform.c new file mode 100644 index 000000000000..36c0cf423431 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/platform.c @@ -0,0 +1,1184 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include "common.h" +#include "core/config.h" +#include "core/i2c.h" +#include "core/spi.h" +#include "core/firmware.h" +#include "core/finger_report.h" +#include "core/flash.h" +#include "core/protocol.h" +#include "platform.h" +#include "core/mp_test.h" +#include "core/gesture.h" + +#define DTS_INT_GPIO "ilitek,irq-gpio" +#define DTS_RESET_GPIO "ilitek,reset-gpio" + +#define DTS_OF_NAME "ilitek,tchip" + +#define DEVICE_ID "ILITEK_TDDI" + +#define ILI_COORDS_ARR_SIZE 4 + + +#ifdef USE_KTHREAD +static DECLARE_WAIT_QUEUE_HEAD(waiter); +#endif + +/* Debug level */ +uint32_t ipio_debug_level = DEBUG_NONE; +EXPORT_SYMBOL(ipio_debug_level); + +struct ilitek_platform_data *ipd; + +void ilitek_platform_disable_irq(void) +{ + unsigned long nIrqFlag; + + ipio_debug(DEBUG_IRQ, "IRQ = %d\n", ipd->isEnableIRQ); + + spin_lock_irqsave(&ipd->plat_spinlock, nIrqFlag); + + if (ipd->isEnableIRQ) { + if (ipd->isr_gpio) { + disable_irq_nosync(ipd->isr_gpio); + ipd->isEnableIRQ = false; + ipio_debug(DEBUG_IRQ, "Disable IRQ: %d\n", + ipd->isEnableIRQ); + } else + ipio_err("The number of gpio to irq is incorrect\n"); + } else + ipio_debug(DEBUG_IRQ, "IRQ was already disabled\n"); + + spin_unlock_irqrestore(&ipd->plat_spinlock, nIrqFlag); +} + +EXPORT_SYMBOL(ilitek_platform_disable_irq); + +void ilitek_platform_enable_irq(void) +{ + unsigned long nIrqFlag; + + ipio_debug(DEBUG_IRQ, "IRQ = %d\n", ipd->isEnableIRQ); + + spin_lock_irqsave(&ipd->plat_spinlock, nIrqFlag); + + if (!ipd->isEnableIRQ) { + if (ipd->isr_gpio) { + enable_irq(ipd->isr_gpio); + ipd->isEnableIRQ = true; + ipio_debug(DEBUG_IRQ, "Enable IRQ: %d\n", + ipd->isEnableIRQ); + } else + ipio_err("The number of gpio to irq is incorrect\n"); + } else + ipio_debug(DEBUG_IRQ, "IRQ was already enabled\n"); + + spin_unlock_irqrestore(&ipd->plat_spinlock, nIrqFlag); +} + +EXPORT_SYMBOL(ilitek_platform_enable_irq); + +void ilitek_platform_tp_hw_reset(bool isEnable) +{ + ipio_info("HW Reset: %d\n", isEnable); + + if (!gpio_is_valid(ipd->reset_gpio)) + return ; + ilitek_platform_disable_irq(); + + if (isEnable) { + gpio_direction_output(ipd->reset_gpio, 1); + mdelay(ipd->delay_time_high); + gpio_set_value(ipd->reset_gpio, 0); + mdelay(ipd->delay_time_low); + gpio_set_value(ipd->reset_gpio, 1); + mdelay(ipd->edge_delay); + } else { + gpio_set_value(ipd->reset_gpio, 0); + } + +#ifdef HOST_DOWNLOAD + core_config_ice_mode_enable(); + core_firmware_upgrade(UPDATE_FW_PATH, true); +#endif + mdelay(10); + ilitek_platform_enable_irq(); +} + +EXPORT_SYMBOL(ilitek_platform_tp_hw_reset); + +void ilitek_regulator_power_on(bool status) +{ + int res = 0; + + if (!ipd->REGULATOR_POWER_ON) + return ; + ipio_info("%s\n", status ? "POWER ON" : "POWER OFF"); + + if (status) { + if (ipd->vdd) { + res = regulator_enable(ipd->vdd); + if (res < 0) + ipio_err("regulator_enable vdd fail\n"); + } + if (ipd->vdd_i2c) { + res = regulator_enable(ipd->vdd_i2c); + if (res < 0) + ipio_err("regulator_enable vdd_i2c fail\n"); + } + } else { + if (ipd->vdd) { + res = regulator_disable(ipd->vdd); + if (res < 0) + ipio_err("regulator_enable vdd fail\n"); + } + if (ipd->vdd_i2c) { + res = regulator_disable(ipd->vdd_i2c); + if (res < 0) + ipio_err("regulator_enable vdd_i2c fail\n"); + } + } + core_config->icemodeenable = false; + mdelay(5); +} + +EXPORT_SYMBOL(ilitek_regulator_power_on); + +#ifdef BATTERY_CHECK +static void read_power_status(uint8_t *buf) +{ + struct file *f = NULL; + mm_segment_t old_fs; + ssize_t byte = 0; + + old_fs = get_fs(); + set_fs(get_ds()); + + f = filp_open(POWER_STATUS_PATH, O_RDONLY, 0); + if (ERR_ALLOC_MEM(f)) { + ipio_err("Failed to open %s\n", POWER_STATUS_PATH); + return; + } + + f->f_op->llseek(f, 0, SEEK_SET); + byte = f->f_op->read(f, buf, 20, &f->f_pos); + + ipio_debug(DEBUG_BATTERY, "Read %d bytes\n", (int)byte); + + set_fs(old_fs); + filp_close(f, NULL); +} + +static void ilitek_platform_vpower_notify(struct work_struct *pWork) +{ + uint8_t charge_status[20] = { 0 }; + static int charge_mode; + + ipio_debug(DEBUG_BATTERY, "isEnableCheckPower = %d\n", + ipd->isEnablePollCheckPower); + read_power_status(charge_status); + ipio_debug(DEBUG_BATTERY, "Batter Status: %s\n", charge_status); + + if (strnstr(charge_status, "Charging", strlen(charge_status)) != NULL + || strnstr(charge_status, "Full", strlen(charge_status)) != NULL + || strnstr(charge_status, "Fully charged", strlen(charge_status)) != NULL) { + if (charge_mode != 1) { + ipio_debug(DEBUG_BATTERY, "Charging mode\n"); + core_config_plug_ctrl(false); + charge_mode = 1; + } + } else { + if (charge_mode != 2) { + ipio_debug(DEBUG_BATTERY, "Not charging mode\n"); + core_config_plug_ctrl(true); + charge_mode = 2; + } + } + + if (ipd->isEnablePollCheckPower) + queue_delayed_work(ipd->check_power_status_queue, + &ipd->check_power_status_work, + ipd->work_delay); +} +#endif/*BATTERY_CHECK*/ + +#ifdef CONFIG_FB +static int ilitek_platform_notifier_fb(struct notifier_block *self, + unsigned long event, void *data) +{ + int *blank; + struct fb_event *evdata = data; + + ipio_debug(DEBUG_BOOT, "Notifier's event = %ld\n", event); + + /* + * FB_EVENT_BLANK(0x09): A hardware display blank change occurred. + * FB_EARLY_EVENT_BLANK(0x10): A hardware display blank early change + * occurred. + */ + if (evdata && evdata->data && ((event == FB_EARLY_EVENT_BLANK) || (event == FB_EVENT_BLANK))) { + //if (evdata && evdata->data && (event == FB_EVENT_BLANK)) { + blank = evdata->data; + + if ((*blank == FB_BLANK_POWERDOWN) && event == FB_EARLY_EVENT_BLANK) { + ipio_info("TP Suspend\n"); + + if (!core_firmware->isUpgrading) + core_config_ic_suspend(); + } else if ((*blank == FB_BLANK_UNBLANK + || *blank == FB_BLANK_NORMAL) && event == FB_EVENT_BLANK){ + ipio_info("TP Resume\n"); + + if (!core_firmware->isUpgrading) + core_config_ic_resume(); + } + } + + return NOTIFY_OK; +} +#else /* CONFIG_HAS_EARLYSUSPEND */ +static void ilitek_platform_early_suspend(struct early_suspend *h) +{ + ipio_info("TP Suspend\n"); + + /* TODO: there is doing nothing if an upgrade firmware's processing. */ + + core_fr_touch_release(0, 0, 0); + + input_sync(core_fr->input_device); + + core_fr->isEnableFR = false; + + core_config_ic_suspend(); +} + +static void ilitek_platform_late_resume(struct early_suspend *h) +{ + ipio_info("TP Resuem\n"); + + core_fr->isEnableFR = true; + core_config_ic_resume(); +} + +#endif + +/** + * reg_power_check - register a thread to inquery status at certain time. + */ +static int ilitek_platform_reg_power_check(void) +{ + int res = 0; + +#ifdef BATTERY_CHECK + INIT_DELAYED_WORK(&ipd->check_power_status_work, + ilitek_platform_vpower_notify); + ipd->check_power_status_queue = create_workqueue("ili_power_check"); + ipd->work_delay = msecs_to_jiffies(CHECK_BATTERY_TIME); + if (!ipd->check_power_status_queue) { + ipio_err + ("Failed to create a work thread to check power status\n"); + ipd->vpower_reg_nb = false; + res = ERROR; + } else { + ipio_info + ("Created a work thread to check power status at every %u jiffies\n", (unsigned int)ipd->work_delay); + + if (ipd->isEnablePollCheckPower) { + queue_delayed_work(ipd->check_power_status_queue, + &ipd->check_power_status_work, + ipd->work_delay); + ipd->vpower_reg_nb = true; + } + } +#endif /* BATTERY_CHECK */ + + return res; +} + +/** + * Register a callback function when the event of suspend and resume occurs. + * + * The default used to wake up the cb function comes from notifier block + * mechnaism. If you'd rather liek to use early suspend, + * CONFIG_HAS_EARLYSUSPEND in kernel config must be enabled. + */ +static int ilitek_platform_reg_suspend(void) +{ + int res = 0; + ipio_debug(DEBUG_BOOT,"Register suspend/resume callback function\n"); +#ifdef CONFIG_FB + ipd->notifier_fb.notifier_call = ilitek_platform_notifier_fb; + res = fb_register_client(&ipd->notifier_fb); +#else + ipd->early_suspend->suspend = ilitek_platform_early_suspend; + ipd->early_suspend->esume = ilitek_platform_late_resume; + ipd->early_suspend->level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + res = register_early_suspend(ipd->early_suspend); +#endif /* CONFIG_FB */ + + return res; +} + +#ifndef USE_KTHREAD +static void ilitek_platform_work_queue(struct work_struct *work) +{ + ipio_debug(DEBUG_IRQ, "work_queue: IRQ = %d\n", ipd->isEnableIRQ); + + core_fr_handler(); + + if (!ipd->isEnableIRQ) + ilitek_platform_enable_irq(); +} +#endif /* USE_KTHREAD */ + +static irqreturn_t ilitek_platform_irq_handler(int irq, void *dev_id) +{ + ipio_debug(DEBUG_IRQ, "IRQ = %d\n", ipd->isEnableIRQ); + + if (core_fr->actual_fw_mode == P5_0_FIRMWARE_TEST_MODE) { + ipio_debug(DEBUG_IRQ, "MP INT enter irq\n"); + core_config->interrupt_flag = true; + ipio_info("MP isr check busy is free ,%d\n", core_config->interrupt_flag); + } else { + if (ipd->isEnableIRQ) { + ilitek_platform_disable_irq(); + #ifdef USE_KTHREAD + ipd->irq_trigger = true; + ipio_debug(DEBUG_IRQ, "kthread: irq_trigger = %d\n", + ipd->irq_trigger); + wake_up_interruptible(&waiter); + #else + schedule_work(&ipd->report_work_queue); + #endif /* USE_KTHREAD */ + } + } + return IRQ_HANDLED; +} + +static int ilitek_platform_input_init(void) +{ + int res = 0; + + ipd->input_device = input_allocate_device(); + + if (ERR_ALLOC_MEM(ipd->input_device)) { + ipio_err("Failed to allocate touch input device\n"); + res = -ENOMEM; + goto fail_alloc; + } +#if (INTERFACE == I2C_INTERFACE) + ipd->input_device->name = ipd->client->name; + ipd->input_device->phys = "I2C"; + ipd->input_device->dev.parent = &ipd->client->dev; + ipd->input_device->id.bustype = BUS_I2C; +#else + ipd->input_device->name = DEVICE_ID; + ipd->input_device->phys = "SPI"; + ipd->input_device->dev.parent = &ipd->spi->dev; + ipd->input_device->id.bustype = BUS_SPI; +#endif + + core_fr_input_set_param(ipd->input_device); + /* register the input device to input sub-system */ + res = input_register_device(ipd->input_device); + if (res < 0) { + ipio_err("Failed to register touch input device, res = %d\n", + res); + goto out; + } + + return res; + +fail_alloc: + input_free_device(core_fr->input_device); + return res; + +out: + input_unregister_device(ipd->input_device); + input_free_device(core_fr->input_device); + return res; +} + +#ifdef USE_KTHREAD +static int kthread_handler(void *arg) +{ + int res = 0; + char *str = (char *)arg; + + DEFINE_WAIT_FUNC(wait, woken_wake_function); + + if (strcmp(str, "boot_fw") == 0) { + /* FW Upgrade event */ + core_firmware->isboot = true; + + ilitek_platform_disable_irq(); + +#ifdef BOOT_FW_UPGRADE + res = core_firmware_boot_upgrade(); + if (res < 0) + ipio_err("Failed to upgrade FW at boot stage\n"); +#endif + ilitek_platform_enable_irq(); + + ilitek_platform_input_init(); + + core_firmware->isboot = false; + } else if (strcmp(str, "irq") == 0) { + /* IRQ event */ + struct sched_param param = {.sched_priority = 4 }; + + sched_setscheduler(current, SCHED_RR, ¶m); + + while (!kthread_should_stop() && !ipd->free_irq_thread) { + ipio_debug(DEBUG_IRQ, + "kthread: before->irq_trigger = %d\n", + ipd->irq_trigger); + add_wait_queue(&waiter, &wait); + for (;;) { + if (ipd->irq_trigger) + break; + wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); + } + remove_wait_queue(&waiter, &wait); + wait_event_interruptible(waiter, ipd->irq_trigger); + ipd->irq_trigger = false; + set_current_state(TASK_RUNNING); + ipio_debug(DEBUG_IRQ, + "kthread: after->irq_trigger = %d\n", + ipd->irq_trigger); + mutex_lock(&ipd->touch_mutex); + core_fr_handler(); + ilitek_platform_enable_irq(); + mutex_unlock(&ipd->touch_mutex); + } + } else { + ipio_err("Unknown EVENT\n"); + } + + return res; +} +#endif + +static int ilitek_platform_isr_register(void) +{ + int res = 0; + +#ifdef USE_KTHREAD + ipd->irq_thread = kthread_run(kthread_handler, "irq", + "ili_irq_thread"); + if (ipd->irq_thread == (struct task_struct *)ERR_PTR) { + ipd->irq_thread = NULL; + ipio_err("Failed to create kthread\n"); + res = -ENOMEM; + goto out; + } + ipd->irq_trigger = false; + ipd->free_irq_thread = false; +#else + INIT_WORK(&ipd->report_work_queue, ilitek_platform_work_queue); +#endif /* USE_KTHREAD */ + + ipd->isr_gpio = gpio_to_irq(ipd->int_gpio); + + ipio_info("ipd->isr_gpio = %d\n", ipd->isr_gpio); + + res = request_threaded_irq(ipd->isr_gpio, + NULL, + ilitek_platform_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "ilitek", NULL); + + if (res != 0) { + ipio_err("Failed to register irq handler, irq = %d, res = %d\n", ipd->isr_gpio, res); + goto out; + } + + ipd->isEnableIRQ = true; + +out: + return res; +} + +static int ilitek_platform_gpio(void) +{ + int res = 0; +#ifdef CONFIG_OF +#if (INTERFACE == I2C_INTERFACE) + struct device_node *dev_node = ipd->client->dev.of_node; +#else + struct device_node *dev_node = ipd->spi->dev.of_node; +#endif + uint32_t flag; + + ipd->int_gpio = + of_get_named_gpio_flags(dev_node, DTS_INT_GPIO, 0, &flag); + ipd->reset_gpio = + of_get_named_gpio_flags(dev_node, DTS_RESET_GPIO, 0, &flag); +#endif /* CONFIG_OF */ + + ipio_debug(DEBUG_BOOT,"GPIO INT: %d\n", ipd->int_gpio); + ipio_debug(DEBUG_BOOT,"GPIO RESET: %d\n", ipd->reset_gpio); + + if (!gpio_is_valid(ipd->int_gpio)) { + ipio_err("Invalid INT gpio: %d\n", ipd->int_gpio); + return -EBADR; + } + + res = gpio_request(ipd->int_gpio, "ILITEK_TP_IRQ"); + if (res < 0) { + ipio_err("Request IRQ GPIO failed, res = %d\n", res); + goto out; + } + + if (gpio_is_valid(ipd->reset_gpio)) { + res = gpio_request(ipd->reset_gpio, "ILITEK_TP_RESET"); + if (res < 0) { + ipio_err("Request RESET GPIO failed, res = %d\n", res); + if (ipd->hardware_rst) { + goto out2; + } else { + return 0; + } + } + } else { + if (ipd->hardware_rst) { + ipio_err("Invalid RESET gpio: %d\n", ipd->reset_gpio); + return -EBADR; + } + } + gpio_direction_input(ipd->int_gpio); + +out: + return res; +out2: + if (gpio_is_valid(ipd->int_gpio)) + gpio_free(ipd->int_gpio); + return res; +} + +int ilitek_platform_read_tp_info(void) +{ + if (core_config_get_chip_id() < 0) { + ipio_err("Failed to get chip id\n"); + return CHIP_ID_ERR; + } + + if (core_config_get_protocol_ver() < 0) + ipio_err("Failed to get protocol version\n"); + + if (core_config_get_fw_ver() < 0) + ipio_err("Failed to get firmware version\n"); + + if (core_config_get_core_ver() < 0) + ipio_err("Failed to get core version\n"); + + if (core_config_get_tp_info() < 0) + ipio_err("Failed to get TP information\n"); + + if (core_config_get_key_info() < 0) + ipio_err("Failed to get key information\n"); + + return 0; + +} + +EXPORT_SYMBOL(ilitek_platform_read_tp_info); + +/** + * Remove Core APIs memeory being allocated. + */ +static void ilitek_platform_core_remove(void) +{ + ipio_info("Remove all core's compoenets\n"); + ilitek_proc_remove(); + ilitek_sys_remove(); + core_flash_remove(); + core_firmware_remove(); + core_fr_remove(); + core_config_remove(); + core_i2c_remove(); + core_protocol_remove(); + core_gesture_remove(); +} + +/** + * The function is to initialise all necessary structurs in those core APIs, + * they must be called before the i2c dev probes up successfully. + */ +static int ilitek_platform_core_init(void) +{ + ipio_debug(DEBUG_BOOT,"Initialise core's components\n"); + + if (core_config_init() < 0 || core_protocol_init() < 0 || + core_firmware_init() < 0 || core_fr_init() < 0 || + core_gesture_init() < 0) { + ipio_err("Failed to initialise core components\n"); + return -EINVAL; + } +#if (INTERFACE == I2C_INTERFACE) + if (core_i2c_init(ipd->client) < 0) +#else + if (core_spi_init(ipd->spi) < 0) +#endif + { + ipio_err("Failed to initialise interface\n"); + return -EINVAL; + } + return 0; +} + +#if (INTERFACE == I2C_INTERFACE) +static int ilitek_platform_remove(struct i2c_client *client) +#else +static int ilitek_platform_remove(struct spi_device *spi) +#endif +{ + ipio_info("Remove platform components\n"); + + if (ipd->isEnableIRQ) + disable_irq_nosync(ipd->isr_gpio); + + if (ipd->isr_gpio != 0 && ipd->int_gpio != 0 && ipd->reset_gpio != 0) { + free_irq(ipd->isr_gpio, (void *)ipd->i2c_id); + gpio_free(ipd->int_gpio); + gpio_free(ipd->reset_gpio); + } +#ifdef CONFIG_FB + fb_unregister_client(&ipd->notifier_fb); +#else + unregister_early_suspend(&ipd->early_suspend); +#endif /* CONFIG_FB */ + +#ifdef USE_KTHREAD + if (ipd->irq_thread != NULL) { + ipd->irq_trigger = true; + ipd->free_irq_thread = true; + wake_up_interruptible(&waiter); + kthread_stop(ipd->irq_thread); + ipd->irq_thread = NULL; + } +#endif /* USE_KTHREAD */ + + if (ipd->input_device != NULL) { + input_unregister_device(ipd->input_device); +// input_free_device(ipd->input_device); + input_free_device(NULL); + } + + if (ipd->vpower_reg_nb) { + cancel_delayed_work_sync(&ipd->check_power_status_work); + destroy_workqueue(ipd->check_power_status_queue); + } + + ilitek_platform_core_remove(); + + ipio_kfree((void **)&ipd); + + return 0; +} + +/*The Ili_parse_dt func would be called when driver probe*/ +static int Ili_parse_dt(struct device *dev, + struct ilitek_platform_data *pdata) +{ + int rc; + struct device_node *np = dev->of_node; + struct property *prop; + + u32 coords[ILI_COORDS_ARR_SIZE]; + u32 temp_val; + int coords_size; + + rc = of_property_read_string(np, "ilitek,tp_ic_type", &pdata->TP_IC_TYPE); + if (rc && (rc != -EINVAL)) { + ipio_err("Unable to read name\n"); + return rc; + } + + ipio_debug(DEBUG_BOOT,"pdata->TP_IC_TYPE = %s\n", pdata->TP_IC_TYPE); + + prop = of_find_property(np, "ilitek,display-coords", NULL); + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + + coords_size = prop->length / sizeof(u32); + if (coords_size != ILI_COORDS_ARR_SIZE) { + ipio_err("invalid %s\n", pdata->TP_IC_TYPE); + return -EINVAL; + } + + rc = of_property_read_u32_array(np, "ilitek,display-coords", coords, coords_size); + if (rc && (rc != -EINVAL)) { + ipio_err("Unable to read ilitek,display-coords\n"); + return rc; + } + + pdata->x_min = coords[0]; + pdata->y_min = coords[1]; + pdata->x_max = coords[2]; + pdata->y_max = coords[3]; + + ipio_debug(DEBUG_BOOT,"x_min = %d,y_min = %d,x_max = %d,y_max = %d,coords_size = %d\n ", + ipd->x_min, ipd->y_min, ipd->x_max, ipd->y_max, coords_size); + + pdata->MT_B_TYPE = of_property_read_bool(np, + "ilitek,mt_b_type"); + if (pdata->MT_B_TYPE) + ipio_debug(DEBUG_BOOT,"pdata->MT_B_TYPE ture "); + else + ipio_debug(DEBUG_BOOT,"pdata->MT_B_TYPE false "); + + pdata->hardware_rst = of_property_read_bool(np, + "ilitek,hardware_rst"); + if (pdata->hardware_rst) + ipio_debug(DEBUG_BOOT,"pdata->hardware_rst ture "); + else + ipio_debug(DEBUG_BOOT,"pdata->hardware_rst false "); + + pdata->REGULATOR_POWER_ON = of_property_read_bool(np, + "ilitek,regulator_power_on"); + if (pdata->REGULATOR_POWER_ON) + ipio_debug(DEBUG_BOOT,"pdata->REGULATOR_POWER_ON ture "); + else + ipio_debug(DEBUG_BOOT,"pdata->REGULATOR_POWER_ON false "); + + pdata->BATTERY_CHECK = of_property_read_bool(np, + "ilitek,battery_check"); + if (pdata->BATTERY_CHECK) + ipio_debug(DEBUG_BOOT,"pdata->BATTERY_CHECK ture "); + else + ipio_debug(DEBUG_BOOT,"pdata->BATTERY_CHECK false "); + + pdata->ENABLE_BATTERY_CHECK = of_property_read_bool(np, + "ilitek,enable_battery_check"); + if (pdata->ENABLE_BATTERY_CHECK) + ipio_debug(DEBUG_BOOT,"pdata->ENABLE_BATTERY_CHECK ture "); + else + ipio_debug(DEBUG_BOOT,"pdata->ENABLE_BATTERY_CHECK false "); + + if (of_property_read_bool(np, "ilitek,x_flip")) { + pr_notice("using flipped X axis\n"); + pdata->x_flip = true; + } + + if (of_property_read_bool(np, "ilitek,y_flip")) { + pr_notice("using flipped Y axis\n"); + pdata->y_flip = true; + } + + rc = of_property_read_u32(np, "ilitek,tpd_height", + &temp_val); + if (!rc) + pdata->TPD_HEIGHT = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "ilitek,tpd_width", + &temp_val); + if (!rc) + pdata->TPD_WIDTH = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "ilitek,max_touch_num", + &temp_val); + if (!rc) + pdata->MAX_TOUCH_NUM = temp_val; + else + return rc; + + rc = of_property_read_u32(np, "ilitek,tp_touch_ic", + &temp_val); + if (!rc) + pdata->chip_id = temp_val; + else + return rc; + + return 0; + +} + + +/* + * The function will check the flash data is correct. + */ +void flash_data_check(void) +{ + int ret = NO_NEED_UPDATE; + ret = tddi_check_fw_upgrade(); + if (ret == NEED_UPDATE || ret == CHECK_FW_FAIL) { + core_config->firmware_ver[1] = 0; + core_config->firmware_ver[2] = 0; + core_config->firmware_ver[3] = 0; + core_config->firmware_ver[4] = 0; + ipio_err(" flash data is not correct\n"); + } else { + + ipio_info(" flash data is correct\n"); + } +} + +/* + * The probe func would be called after an i2c device was detected by kernel. + * + * It will still return zero even if it couldn't get a touch ic info. + * The reason for why we allow it passing the process is because + * users/developersmight want to have access to ICE mode to upgrade a + * firwmare forcelly. + */ +#if (INTERFACE == I2C_INTERFACE) +static int ilitek_platform_probe(struct i2c_client *client, + const struct i2c_device_id *id) +#else +static int ilitek_platform_probe(struct spi_device *spi) +#endif +{ + int ret; + + const char *vdd_name = "vdd"; + const char *vcc_i2c_name = "vcc_i2c"; + + /* initialise the struct of touch ic memebers. */ + ipd = kzalloc(sizeof(*ipd), GFP_KERNEL); + if (ERR_ALLOC_MEM(ipd)) { + ipio_err("Failed to allocate ipd memory, %ld\n", PTR_ERR(ipd)); + return -ENOMEM; + } +#if (INTERFACE == I2C_INTERFACE) + if (client == NULL) { + ipio_err("i2c client is NULL\n"); + return -ENODEV; + } + + /* Set i2c slave addr if it's not configured */ + ipio_debug(DEBUG_BOOT,"I2C Slave address = 0x%x\n", client->addr); + if (client->addr != ILI7807_SLAVE_ADDR + || client->addr != ILI9881_SLAVE_ADDR) { + client->addr = ILI9881_SLAVE_ADDR; + ipio_err + ("I2C Slave addr doesn't be set up, use default : 0x%x\n", + client->addr); + } + ipd->client = client; + ipd->i2c_id = id; +#else + if (spi == NULL) { + ipio_err("spi device is NULL\n"); + return -ENODEV; + } + + ipd->spi = spi; +#endif + /* Get dtsi configured value*/ + ipd->TP_IC_TYPE = kzalloc(128, GFP_KERNEL); + ret = Ili_parse_dt(&client->dev, ipd); + if (ret) { + ipio_err("DT parsing failed\n"); + return ret; + } + + ipd->isEnableIRQ = false; + ipd->isEnablePollCheckPower = false; + ipd->vpower_reg_nb = false; + + ipio_debug(DEBUG_BOOT,"Driver Version : %s\n", DRIVER_VERSION); + ipio_debug(DEBUG_BOOT,"Driver for Touch IC : %x\n", ipd->chip_id); + ipio_debug(DEBUG_BOOT,"Driver interface : %s\n", + (INTERFACE == I2C_INTERFACE) ? "I2C" : "SPI"); + + /* + * Different ICs may require different delay time for the reset. + * They may also depend on what your platform need to. + */ + if (ipd->chip_id == CHIP_TYPE_ILI7807) { + ipd->delay_time_high = 10; + ipd->delay_time_low = 5; + ipd->edge_delay = 200; + } else if (ipd->chip_id == CHIP_TYPE_ILI9881) { + ipd->delay_time_high = 10; + ipd->delay_time_low = 5; +#if (INTERFACE == I2C_INTERFACE) + ipd->edge_delay = 100; +#else + ipd->edge_delay = 1; +#endif + } else { + ipd->delay_time_high = 10; + ipd->delay_time_low = 10; + ipd->edge_delay = 10; + } + + mutex_init(&ipd->plat_mutex); + spin_lock_init(&ipd->plat_spinlock); + mutex_init(&ipd->touch_mutex); + + /* Init members for debug */ + mutex_init(&ipd->ilitek_debug_mutex); + mutex_init(&ipd->ilitek_debug_read_mutex); + init_waitqueue_head(&(ipd->inq)); + ipd->debug_data_frame = 0; + ipd->debug_node_open = false; + + ipd->raw_count = 1; + ipd->delta_count = 10; + ipd->bg_count = 0; + ipd->debug_data_start_flag = false; + + if (ipd->REGULATOR_POWER_ON) { + ipd->vdd = regulator_get(&ipd->client->dev, vdd_name); + if (ERR_ALLOC_MEM(ipd->vdd)) { + ipio_err("regulator_get vdd fail\n"); + ipd->vdd = NULL; + return -ENOMEM; + } else { + if (regulator_set_voltage(ipd->vdd, VDD_VOLTAGE, VDD_VOLTAGE) < + 0) + ipio_err("Failed to set vdd %d.\n", VDD_VOLTAGE); + goto reg_vdd_put; + } + + ipd->vdd_i2c = regulator_get(&ipd->client->dev, vcc_i2c_name); + if (ERR_ALLOC_MEM(ipd->vdd_i2c)) { + ipio_err("regulator_get vdd_i2c fail.\n"); + ipd->vdd_i2c = NULL; + goto reg_vdd_set_vtg; + } else { + if (regulator_set_voltage + (ipd->vdd_i2c, VDD_I2C_VOLTAGE, VDD_I2C_VOLTAGE) < 0) + ipio_err("Failed to set vdd_i2c %d\n", + VDD_I2C_VOLTAGE); + goto reg_vcc_put; + } + ilitek_regulator_power_on(true); + } /* REGULATOR_POWER_ON */ + + ret = ilitek_platform_gpio() ; + if (ret < 0) { + ipio_err("Failed to request gpios\n "); + goto gpio_err; + } + + /* + * If kernel failes to allocate memory to the core components, driver + * will be unloaded. + */ + ret = ilitek_platform_core_init() ; + if (ret < 0) { + ipio_err("Failed to allocate cores' mem\n"); + goto core_init_err; + } +#ifdef HOST_DOWNLOAD + core_firmware_boot_host_download(); +#else + /*ilitek_platform_tp_hw_reset(true);*/ + /* Soft reset */ + core_config_ice_mode_enable(); + core_config_set_watch_dog(false); + mdelay(10); + core_config_ic_reset(); +#endif + + /* get our tp ic information */ + ret = ilitek_platform_read_tp_info(); + if (ret == CHIP_ID_ERR) { + core_config->chip_pid = CHIP_ID_ERR; + ipio_err("Failed to raed chip id %d,%d\n", ret, (-ENODEV)); + goto read_tp_info_err; + } + + /* + * Check flash data is correct. + */ + flash_data_check(); + + /* If it defines boot upgrade, input register will be done inside boot + * function. + */ +#ifndef BOOT_FW_UPGRADE + ret = ilitek_platform_input_init() ; + if (ret < 0) { + ipio_err("Failed to init input device in kernel\n"); + goto input_init_err; + } +#endif /* BOOT_FW_UPGRADE */ + + ret = ilitek_platform_isr_register() ; + if (ret < 0) { + ipio_err("Failed to register ISR\n"); + goto isr_register_err; + } + + ret = ilitek_platform_reg_suspend() ; + if (ret < 0) { + ipio_err("Failed to register suspend/resume function\n"); + goto reg_suspend_err; + } + + ret = ilitek_platform_reg_power_check() ; + if (ret < 0) { + ipio_err("Failed to register power check function\n"); + goto reg_power_check_err; + } + /* Create nodes for users */ + ilitek_proc_init(); + + /* Create sys node */ + ret = ilitek_sys_init() ; + if (ret < 0) { + ipio_err("sys class files creation failed\n"); + goto sys_init_err; + } + +#ifdef BOOT_FW_UPGRADE + ipd->update_thread = + kthread_run(kthread_handler, "boot_fw", "ili_fw_boot"); + if (ipd->update_thread == (struct task_struct *)ERR_PTR) { + ipd->update_thread = NULL; + ipio_err("Failed to create fw upgrade thread\n"); + goto sys_init_err; + } +#endif /* BOOT_FW_UPGRADE */ + ipd->suspended = false; + return 0; + +sys_init_err: +reg_power_check_err: +reg_suspend_err: + free_irq(ipd->isr_gpio, (void *)ipd->i2c_id); +isr_register_err: +#ifndef BOOT_FW_UPGRADE +input_init_err: +#endif +read_tp_info_err: + +core_init_err: + if (gpio_is_valid(ipd->int_gpio)) + gpio_free(ipd->int_gpio); + if (gpio_is_valid(ipd->reset_gpio)) + gpio_free(ipd->reset_gpio); +gpio_err: + +reg_vcc_put: + if (ipd->REGULATOR_POWER_ON) + regulator_put(ipd->vdd); +reg_vdd_set_vtg: + if (ipd->REGULATOR_POWER_ON) { + if (regulator_count_voltages(ipd->vdd) > 0) + regulator_set_voltage(ipd->vdd, 0, 3300000); + } +reg_vdd_put: + if (ipd->REGULATOR_POWER_ON) + regulator_put(ipd->vdd); + return ret; +} + +static const struct i2c_device_id tp_device_id[] = { + {DEVICE_ID, 0}, + {}, /* should not omitted */ +}; + +MODULE_DEVICE_TABLE(i2c, tp_device_id); + +/* + * The name in the table must match the definiation + * in a dts file. + * + */ +static struct of_device_id tp_match_table[] = { + {.compatible = DTS_OF_NAME}, + {}, +}; + +#if (INTERFACE == I2C_INTERFACE) +static struct i2c_driver tp_i2c_driver = { + .driver = { + .name = DEVICE_ID, + .owner = THIS_MODULE, + .of_match_table = tp_match_table, + }, + .probe = ilitek_platform_probe, + .remove = ilitek_platform_remove, + .id_table = tp_device_id, +}; +#else +static struct spi_driver tp_spi_driver = { + .driver = { + .name = DEVICE_ID, + .owner = THIS_MODULE, + .of_match_table = tp_match_table, + }, + .probe = ilitek_platform_probe, + .remove = ilitek_platform_remove, +}; +#endif + +static int __init ilitek_platform_init(void) +{ + int res = 0; + + ipio_info("TP driver init\n"); + +#if (INTERFACE == I2C_INTERFACE) + ipio_debug(DEBUG_BOOT,"TP driver add i2c interface\n"); + res = i2c_add_driver(&tp_i2c_driver); + if (res < 0) { + ipio_err("Failed to add i2c driver\n"); + i2c_del_driver(&tp_i2c_driver); + return -ENODEV; + } +#else + ipio_debug(DEBUG_BOOT,"TP driver add spi interface\n"); + res = spi_register_driver(&tp_spi_driver); + if (res < 0) { + ipio_err("Failed to add ilitek driver\n"); + spi_unregister_driver(&tp_spi_driver); + return -ENODEV; + } +#endif + + ipio_info("Succeed to add ilitek driver\n"); + return res; +} + +static void __exit ilitek_platform_exit(void) +{ + ipio_info("I2C driver has been removed\n"); + +#if (INTERFACE == I2C_INTERFACE) + i2c_del_driver(&tp_i2c_driver); +#else + spi_unregister_driver(&tp_spi_driver); +#endif +} + +module_init(ilitek_platform_init); +module_exit(ilitek_platform_exit); +MODULE_AUTHOR("ILITEK"); +MODULE_LICENSE("GPL"); + + diff --git a/drivers/input/touchscreen/ilitek_mmi/platform.h b/drivers/input/touchscreen/ilitek_mmi/platform.h new file mode 100644 index 000000000000..12e545833b90 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/platform.h @@ -0,0 +1,134 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __PLATFORM_H +#define __PLATFORM_H + +struct ilitek_platform_data { + + struct i2c_client *client; + + struct input_dev *input_device; + + const struct i2c_device_id *i2c_id; + + struct regulator *vdd; + struct regulator *vdd_i2c; + + struct mutex plat_mutex; + spinlock_t plat_spinlock; + struct mutex touch_mutex; + + uint32_t chip_id; + + int int_gpio; + int reset_gpio; + int isr_gpio; + + int delay_time_high; + int delay_time_low; + int edge_delay; + + bool isEnableIRQ; + bool isEnablePollCheckPower; + +#ifdef USE_KTHREAD + struct task_struct *irq_thread; + bool irq_trigger; + bool free_irq_thread; +#else + struct work_struct report_work_queue; +#endif + +#ifdef CONFIG_FB + struct notifier_block notifier_fb; +#else + struct early_suspend early_suspend; +#endif + +#ifdef BOOT_FW_UPGRADE + struct task_struct *update_thread; +#endif + + /* obtain msg when battery status has changed */ + struct delayed_work check_power_status_work; + struct workqueue_struct *check_power_status_queue; + unsigned long work_delay; + bool vpower_reg_nb; + + /* Sending report data to users for the debug */ + bool debug_node_open; + int debug_data_frame; + wait_queue_head_t inq; + unsigned char debug_buf[1024][2048]; + int raw_count; + int delta_count; + int bg_count; + bool debug_data_start_flag; + struct mutex ilitek_debug_mutex; + struct mutex ilitek_debug_read_mutex; + + struct spi_device *spi; + bool suspended; + + const char *TP_IC_TYPE; + bool hardware_rst; + bool MT_B_TYPE; + bool REGULATOR_POWER_ON; + bool BATTERY_CHECK; + bool ENABLE_BATTERY_CHECK; + + u32 x_max; + u32 y_max; + u32 x_min; + u32 y_min; + + bool x_flip; + bool y_flip; + + int TPD_WIDTH; + int TPD_HEIGHT; + int MAX_TOUCH_NUM; + +}; + +extern struct ilitek_platform_data *ipd; +extern struct tpd_device *tpd; +/* exported from platform.c */ +extern void ilitek_platform_disable_irq(void); +extern void ilitek_platform_enable_irq(void); +extern int ilitek_platform_read_tp_info(void); +extern void ilitek_platform_tp_hw_reset(bool isEnable); +#ifdef ENABLE_REGULATOR_POWER_ON +extern void ilitek_regulator_power_on(bool status); +#endif + +/* exported from userspsace.c */ +extern void netlink_reply_msg(void *raw, int size); +extern int ilitek_proc_init(void); +extern void ilitek_proc_remove(void); +extern int ilitek_sys_init(void); +extern void ilitek_sys_remove(void); + +#endif /* __PLATFORM_H */ diff --git a/drivers/input/touchscreen/ilitek_mmi/userspace.c b/drivers/input/touchscreen/ilitek_mmi/userspace.c new file mode 100644 index 000000000000..964dd8d21a84 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_mmi/userspace.c @@ -0,0 +1,2259 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * Based on TDD v7.0 implemented by Mstar & ILITEK + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "common.h" +#include "platform.h" +#include "core/config.h" +#include "core/firmware.h" +#include "core/finger_report.h" +#include "core/flash.h" +#include "core/i2c.h" +#include "core/protocol.h" +#include "core/mp_test.h" +#include "core/parser.h" +#include "core/gesture.h" + +#define USER_STR_BUFF 2000 +#define USER_DUG_BUFF_LEN 4096 +#define ILITEK_IOCTL_MAGIC 100 +#define ILITEK_IOCTL_MAXNR 21 +#define UPGRADE_RETRY_TIMES 3 + +#define ILITEK_IOCTL_I2C_WRITE_DATA _IOWR(ILITEK_IOCTL_MAGIC, 0, uint8_t*) +#define ILITEK_IOCTL_I2C_SET_WRITE_LENGTH _IOWR(ILITEK_IOCTL_MAGIC, 1, int) +#define ILITEK_IOCTL_I2C_READ_DATA _IOWR(ILITEK_IOCTL_MAGIC, 2, uint8_t*) +#define ILITEK_IOCTL_I2C_SET_READ_LENGTH _IOWR(ILITEK_IOCTL_MAGIC, 3, int) + +#define ILITEK_IOCTL_TP_HW_RESET _IOWR(ILITEK_IOCTL_MAGIC, 4, int) +#define ILITEK_IOCTL_TP_POWER_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 5, int) +#define ILITEK_IOCTL_TP_REPORT_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 6, int) +#define ILITEK_IOCTL_TP_IRQ_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 7, int) + +#define ILITEK_IOCTL_TP_DEBUG_LEVEL _IOWR(ILITEK_IOCTL_MAGIC, 8, int) +#define ILITEK_IOCTL_TP_FUNC_MODE _IOWR(ILITEK_IOCTL_MAGIC, 9, int) + +#define ILITEK_IOCTL_TP_FW_VER _IOWR(ILITEK_IOCTL_MAGIC, 10, uint8_t*) +#define ILITEK_IOCTL_TP_PL_VER _IOWR(ILITEK_IOCTL_MAGIC, 11, uint8_t*) +#define ILITEK_IOCTL_TP_CORE_VER _IOWR(ILITEK_IOCTL_MAGIC, 12, uint8_t*) +#define ILITEK_IOCTL_TP_DRV_VER _IOWR(ILITEK_IOCTL_MAGIC, 13, uint8_t*) +#define ILITEK_IOCTL_TP_CHIP_ID _IOWR(ILITEK_IOCTL_MAGIC, 14, uint32_t*) + +#define ILITEK_IOCTL_TP_NETLINK_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 15, int*) +#define ILITEK_IOCTL_TP_NETLINK_STATUS _IOWR(ILITEK_IOCTL_MAGIC, 16, int*) + +#define ILITEK_IOCTL_TP_MODE_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 17, uint8_t*) +#define ILITEK_IOCTL_TP_MODE_STATUS _IOWR(ILITEK_IOCTL_MAGIC, 18, int*) +#define ILITEK_IOCTL_ICE_MODE_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 19, int) + +#define ILITEK_IOCTL_MP_WRITE_CDC_FLAG_FALSE _IOWR(ILITEK_IOCTL_MAGIC, 20, int*) +#define ILITEK_IOCTL_MP_READ_CDC_FLAG _IOWR(ILITEK_IOCTL_MAGIC, 21, int*) + +unsigned char *g_user_buf; +static struct class *touchscreen_class; +static struct device *touchscreen_class_dev; + +#define DEBUG_DATA_FILE_SIZE (10 * 1024) +#define DEBUG_DATA_FILE_PATH "/vendor/firmware/ILITEK_log.csv" + +struct file_buffer { + char *ptr; + char file_name[128]; + int32_t file_len; + int32_t file_max_zise; +}; + +int katoi(char *string) +{ + int result = 0; + unsigned int digit; + int sign; + + if (*string == '-') { + sign = 1; + string += 1; + } else { + sign = 0; + if (*string == '+') { + string += 1; + } + } + + for (;; string += 1) { + digit = *string - '0'; + if (digit > 9) + break; + result = (10 * result) + digit; + } + + if (sign) { + return -result; + } + return result; +} + +EXPORT_SYMBOL(katoi); + +int str2hex(char *str) +{ + int strlen, result, intermed, intermedtop; + char *s = str; + + while (*s != 0x0) { + s++; + } + + strlen = (int)(s - str); + s = str; + if (*s != 0x30) { + return ERROR; + } + + s++; + + if (*s != 0x78 && *s != 0x58) { + return ERROR; + } + s++; + + strlen = strlen - 3; + result = 0; + while (*s != 0x0) { + intermed = *s & 0x0f; + intermedtop = *s & 0xf0; + if (intermedtop == 0x60 || intermedtop == 0x40) { + intermed += 0x09; + } + intermed = intermed << (strlen << 2); + result = result | intermed; + strlen -= 1; + s++; + } + return result; +} + +EXPORT_SYMBOL(str2hex); + +static ssize_t ilitek_proc_fw_pc_counter_read(struct file *pFile, char __user *buf, size_t nCount, loff_t *pos) +{ + uint32_t pc; + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + mutex_lock(&ipd->plat_mutex); + + pc = core_config_read_pc_counter(); + + mutex_unlock(&ipd->plat_mutex); + + nCount = snprintf(g_user_buf, PAGE_SIZE, "pc counter = 0x%x\n", pc); + + pc = copy_to_user(buf, g_user_buf, nCount); + if (pc < 0) { + ipio_err("Failed to copy data to user space"); + } + + *pos += nCount; + + return nCount; +} + +static ssize_t ilitek_proc_debug_switch_read(struct file *pFile, + char __user *buff, size_t nCount, + loff_t *pPos) +{ + int res = 0; + + if (*pPos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + ipd->debug_node_open = !ipd->debug_node_open; + + ipio_info(" %s debug_flag message = %x\n", + ipd->debug_node_open ? "Enabled" : "Disabled", + ipd->debug_node_open); + + nCount = + snprintf(g_user_buf, USER_STR_BUFF * sizeof(unsigned char), "ipd->debug_node_open : %s\n", + ipd->debug_node_open ? "Enabled" : "Disabled"); + + *pPos += nCount; + + res = copy_to_user(buff, g_user_buf, nCount); + if (res < 0) { + ipio_err("Failed to copy data to user space"); + } + + return nCount; +} + +static ssize_t ilitek_proc_debug_message_write(struct file *filp, + const char *buff, size_t size, + loff_t *pPos) +{ + int ret = 0; + unsigned char buffer[512] = { 0 }; + + /* check the buffer size whether it exceeds the local buffer size or not */ + if (size > 512) { + ipio_err("buffer exceed 512 bytes\n"); + size = 512; + } + + ret = copy_from_user(buffer, buff, size - 1); + if (ret < 0) { + ipio_err("copy data from user space, failed"); + return ERROR; + } + + if (strcmp(buffer, "dbg_flag") == 0) { + ipd->debug_node_open = !ipd->debug_node_open; + ipio_info(" %s debug_flag message(%X).\n", + ipd->debug_node_open ? "Enabled" : "Disabled", + ipd->debug_node_open); + } + return size; +} + +static ssize_t ilitek_proc_debug_message_read(struct file *filp, + char __user *buff, size_t size, + loff_t *pPos) +{ + unsigned long p = *pPos; + unsigned int count = size; + int i = 0; + int send_data_len = 0; + size_t ret = 0; + int data_count = 0; + int one_data_bytes = 0; + int need_read_data_len = 0; + int type = 0; + unsigned char *tmpbuf = NULL; + unsigned char tmpbufback[128] = { 0 }; + + mutex_lock(&ipd->ilitek_debug_read_mutex); + + while (ipd->debug_data_frame <= 0) { + if (filp->f_flags & O_NONBLOCK) { + return -EAGAIN; + } + wait_event_interruptible(ipd->inq, ipd->debug_data_frame > 0); + } + + mutex_lock(&ipd->ilitek_debug_mutex); + + tmpbuf = vmalloc(USER_DUG_BUFF_LEN); /* buf size if even */ + if (ERR_ALLOC_MEM(tmpbuf)) { + ipio_err("buffer vmalloc error\n"); + send_data_len += + snprintf(tmpbufback + send_data_len, + (sizeof(tmpbufback)-send_data_len), + "buffer vmalloc error\n"); + ret = copy_to_user(buff, tmpbufback, send_data_len); /*ipd->debug_buf[0] */ + } else { + if (ipd->debug_data_frame > 0) { + if (ipd->debug_buf[0][0] == 0x5A) { + need_read_data_len = 43; + } else if (ipd->debug_buf[0][0] == 0x7A) { + type = ipd->debug_buf[0][3] & 0x0F; + + data_count = + ipd->debug_buf[0][1] * ipd->debug_buf[0][2]; + + if (type == 0 || type == 1 || type == 6) { + one_data_bytes = 1; + } else if (type == 2 || type == 3) { + one_data_bytes = 2; + } else if (type == 4 || type == 5) { + one_data_bytes = 4; + } + need_read_data_len = + data_count * one_data_bytes + 1 + 5; + } + + send_data_len = 0; /* ipd->debug_buf[0][1] - 2; */ + need_read_data_len = 2040; + if (need_read_data_len <= 0) { + ipio_err("parse data err data len = %d\n", + need_read_data_len); + send_data_len += + snprintf(tmpbuf + send_data_len, + (USER_DUG_BUFF_LEN-send_data_len), + "parse data err data len = %d\n", + need_read_data_len); + } else { + for (i = 0 ; i < need_read_data_len ; i++) { + send_data_len += + snprintf(tmpbuf + send_data_len, + (USER_DUG_BUFF_LEN-send_data_len), + "%02X", ipd->debug_buf[0][i]); + if (send_data_len >= USER_DUG_BUFF_LEN) { + ipio_err + ("send_data_len = %d set 4096 i = %d\n", + send_data_len, i); + send_data_len = 4096; + break; + } + } + } + send_data_len += snprintf(tmpbuf + send_data_len, + (USER_DUG_BUFF_LEN-send_data_len), "\n\n"); + + if (p == 5 || size == 4096 || size == 2048) { + ipd->debug_data_frame--; + if (ipd->debug_data_frame < 0) { + ipd->debug_data_frame = 0; + } + + for (i = 1; i <= ipd->debug_data_frame; i++) { + memcpy(ipd->debug_buf[i - 1], + ipd->debug_buf[i], 2048); + } + } + } else { + ipio_err("no data send\n"); + send_data_len += snprintf(tmpbuf + send_data_len, + (USER_DUG_BUFF_LEN-send_data_len), "no data send\n"); + } + + /* Preparing to send data to user */ + if (size == 4096) + ret = copy_to_user(buff, tmpbuf, send_data_len); + else + ret = copy_to_user(buff, tmpbuf + p, send_data_len - p); + + if (ret) { + ipio_err("copy_to_user err\n"); + ret = -EFAULT; + } else { + *pPos += count; + ret = count; + ipio_debug(DEBUG_FINGER_REPORT, + "Read %d bytes(s) from %ld\n", count, p); + } + } + /* ipio_err("send_data_len = %d\n", send_data_len); */ + if (send_data_len <= 0 || send_data_len > 4096) { + ipio_err("send_data_len = %d set 2048\n", send_data_len); + send_data_len = 4096; + } + if (tmpbuf != NULL) { + vfree(tmpbuf); + tmpbuf = NULL; + } + ipio_debug(DEBUG_NETLINK, "send len:%d\n", send_data_len); + mutex_unlock(&ipd->ilitek_debug_mutex); + mutex_unlock(&ipd->ilitek_debug_read_mutex); + return send_data_len; +} + +/* Created only for oppo */ +static ssize_t ilitek_proc_oppo_mp_lcm_on_read(struct file *filp, + char __user *buff, size_t size, + loff_t *pPos) +{ + int len = 0; + + if (*pPos != 0) + return 0; + + if (core_parser_path(INI_NAME_PATH) < 0) { + ipio_err("Failed to parsing INI file\n"); + goto out; + } + + /* Init MP structure */ + if (core_mp_init() < 0) { + ipio_err("Failed to init mp\n"); + goto out; + } + + /* Switch to test mode */ + core_fr_mode_control(&protocol->test_mode); + + if (core_mp->busy_cdc != ISR_CHECK) + ilitek_platform_disable_irq(); + + core_fr->isEnableFR = false; + + core_mp->oppo_run = true; + + /* Do not chang the sequence of test */ + core_mp_run_test("Noise Peak To Peak(With Panel)", true); + core_mp_run_test("Noise Peak to Peak(IC Only)", true); + core_mp_run_test("Short Test -ILI9881", true); + core_mp_run_test("Open Test(integration)_SP", true); + core_mp_run_test("Raw Data(Have BK)", true); + core_mp_run_test("Calibration Data(DAC)", true); + core_mp_run_test("Raw Data(No BK)", true); + core_mp_run_test("Doze Raw Data", true); + core_mp_run_test("Doze Peak To Peak", true); + + if (core_mp->busy_cdc == ISR_CHECK) + ilitek_platform_disable_irq(); + + core_mp_show_result(); + + core_mp->oppo_run = false; + + core_mp_test_free(); + + /* Switch to demo mode */ + core_fr_mode_control(&protocol->demo_mode); + +#ifdef HOST_DOWNLOAD + ilitek_platform_tp_hw_reset(true); +#endif + + /*ilitek_platform_tp_hw_reset(true);*/ + /* Soft reset */ + core_config_ice_mode_enable(); + core_config_set_watch_dog(false); + mdelay(10); + core_config_ic_reset(); + + core_fr->isEnableFR = true; + ilitek_platform_enable_irq(); + +out: + *pPos = len; + return len; +} + +/* Created only for oppo */ +static ssize_t ilitek_proc_oppo_mp_lcm_off_read(struct file *filp, + char __user *buff, size_t size, + loff_t *pPos) +{ + int len = 0; + + if (*pPos != 0) + return 0; + + if (core_parser_path(INI_NAME_PATH) < 0) { + ipio_err("Failed to parsing INI file\n"); + goto out; + } + + /* Init MP structure */ + if (core_mp_init() < 0) { + ipio_err("Failed to init mp\n"); + goto out; + } + + if (core_mp->busy_cdc != ISR_CHECK) + ilitek_platform_disable_irq(); + + core_fr->isEnableFR = false; + + /* Enter to suspend and move gesture code to iram */ + core_config->isEnableGesture = true; + core_gesture->mode = GESTURE_INFO_MPDE; + + /* sense stop */ + core_config_sense_ctrl(false); + + if (core_config_check_cdc_busy(50, 100) < 0) + ipio_err("Check busy is timout !\n"); + + core_fr->actual_fw_mode = P5_0_FIRMWARE_GESTURE_MODE; + +#ifdef HOST_DOWNLOAD + core_gesture_load_code(); +#endif + + /* Switch to test mode which moves mp code to iram */ + core_fr_mode_control(&protocol->test_mode); + + + /* Indicates running mp test is called by oppo node */ + core_mp->oppo_run = true; + core_mp->oppo_lcm = true; + + /* Do not chang the sequence of test */ + core_mp_run_test("Raw Data(No BK) (LCM OFF)", true); + core_mp_run_test("Noise Peak to Peak(With Panel) (LCM OFF)", true); + core_mp_run_test("Raw Data_TD (LCM OFF)", true); + core_mp_run_test("Peak To Peak_TD (LCM OFF)", true); + + if (core_mp->busy_cdc == ISR_CHECK) + ilitek_platform_disable_irq(); + + core_mp_show_result(); + + core_mp->oppo_run = false; + core_mp->oppo_lcm = false; + + core_mp_ctrl_lcm_status(true); + + core_mp_test_free(); + + core_fr_mode_control(&protocol->demo_mode); + + /*ilitek_platform_tp_hw_reset(true);*/ + /* Soft reset */ + core_config_ice_mode_enable(); + core_config_set_watch_dog(false); + mdelay(10); + core_config_ic_reset(); + + core_fr->isEnableFR = true; + ilitek_platform_enable_irq(); + +out: + *pPos = len; + return len; +} + +static ssize_t ilitek_proc_mp_test_read(struct file *filp, char __user *buff, + size_t size, loff_t *pPos) +{ + uint32_t len = 0; + + if (*pPos != 0) + return 0; + + if (core_parser_path(INI_NAME_PATH) < 0) { + ipio_err("Failed to parsing INI file\n"); + goto out; + } + + /* Init MP structure */ + if (core_mp_init() < 0) { + ipio_err("Failed to init mp\n"); + goto out; + } + + /* Switch to Test mode nad move mp code */ + core_fr_mode_control(&protocol->test_mode); + + if (core_mp->busy_cdc != ISR_CHECK) + ilitek_platform_disable_irq(); + + core_fr->isEnableFR = false; + /* Start to run MP test */ + core_mp->run = true; + + /* + * Get timing parameters first. + * Howerver, this can be ignored if read them from ini. + */ + if (protocol->major >= 5 && protocol->mid >= 4) { + if (core_mp_calc_timing_nodp() < 0) { + ipio_err("Can't get timing parameters\n"); + goto out; + } + } + + /* Do not chang the sequence of test */ + core_mp_run_test("Noise Peak To Peak(With Panel)", true); + core_mp_run_test("Noise Peak to Peak(IC Only)", true); + core_mp_run_test("Short Test -ILI9881", true); + core_mp_run_test("Open Test(integration)_SP", true); + core_mp_run_test("Raw Data(Have BK)", true); + /*core_mp_run_test("Raw Data(Have BK) (LCM OFF)", true); */ + core_mp_run_test("Calibration Data(DAC)", true); + core_mp_run_test("Raw Data(No BK)", true); + core_mp_run_test("Raw Data(No BK) (LCM OFF)", true); + core_mp_run_test("Noise Peak to Peak(With Panel) (LCM OFF)", true); + /*core_mp_run_test("Noise Peak to Peak(IC Only) (LCM OFF)", true); */ + core_mp_run_test("Raw Data_TD (LCM OFF)", true); + core_mp_run_test("Peak To Peak_TD (LCM OFF)", true); + core_mp_run_test("Doze Raw Data", true); + core_mp_run_test("Doze Peak To Peak", true); + /*core_mp_run_test("Pin Test ( INT and RST )", true); */ + + + if (core_mp->busy_cdc == ISR_CHECK) + ilitek_platform_disable_irq(); + + core_mp_show_result(); + + core_mp->run = false; + + core_mp_test_free(); + +#ifndef HOST_DOWNLOAD + core_config_ice_mode_enable(); + + if (core_config_set_watch_dog(false) < 0) { + ipio_err("Failed to disable watch dog\n"); + } + + core_config_ic_reset(); +#endif + /* Switch to Demo mode */ + core_fr_mode_control(&protocol->demo_mode); + +#ifdef HOST_DOWNLOAD + ilitek_platform_tp_hw_reset(true); +#endif + + + core_fr->isEnableFR = true; + ilitek_platform_enable_irq(); + +out: + *pPos = len; + return len; +} + +static ssize_t ilitek_proc_mp_test_write(struct file *filp, const char *buff, + size_t size, loff_t *pPos) +{ + int i, res = 0, count = 0; + char cmd[64] = { 0 }, str[512] = { + 0}; + char *token = NULL, *cur = NULL; + uint8_t *va = NULL; + + if (buff != NULL) { + res = copy_from_user(cmd, buff, size - 1); + if (res < 0) { + ipio_info("copy data from user space, failed\n"); + return ERROR; + } + } + + ipio_info("size = %d, cmd = %s\n", (int)size, cmd); + + if (size > 64) { + ipio_err("The size of string is too long\n"); + return size; + } + + token = cur = cmd; + + va = kcalloc(64, sizeof(uint8_t), GFP_KERNEL); + + while ((token = strsep(&cur, ",")) != NULL) { + va[count] = katoi(token); + ipio_info("data[%d] = %x\n", count, va[count]); + count++; + } + + ipio_info("cmd = %s\n", cmd); + + /* Init MP structure */ + if (core_mp_init() < 0) { + ipio_err("Failed to init mp\n"); + return size; + } + + /* Switch to Test mode */ + core_fr_mode_control(&protocol->test_mode); + + ilitek_platform_disable_irq(); + + for (i = 0 ; i < core_mp->mp_items; i++) { + if (strcmp(cmd, tItems[i].name) == 0) { + strlcpy(str, tItems[i].desp, strlen(str)+1); + tItems[i].run = 1; + tItems[i].max = va[1]; + tItems[i].min = va[2]; + tItems[i].frame_count = va[3]; + break; + } + } + + core_mp_run_test(str, false); + + core_mp_show_result(); + + core_mp_test_free(); + +#ifndef HOST_DOWNLOAD + core_config_ice_mode_enable(); + + if (core_config_set_watch_dog(false) < 0) { + ipio_err("Failed to disable watch dog\n"); + } + + core_config_ic_reset(); +#endif + + core_fr_mode_control(&protocol->demo_mode); + +#ifdef HOST_DOWNLOAD + ilitek_platform_tp_hw_reset(true); +#endif + + ilitek_platform_enable_irq(); + + ipio_info("MP Test DONE\n"); + ipio_kfree((void **)&va); + return size; +} + +static ssize_t ilitek_proc_debug_level_read(struct file *filp, + char __user *buff, size_t size, + loff_t *pPos) +{ + int res = 0; + uint32_t len = 0; + + if (*pPos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + len = snprintf(g_user_buf, USER_STR_BUFF, "%d", ipio_debug_level); + + ipio_info("Current DEBUG Level = %d\n", ipio_debug_level); + ipio_info("You can set one of levels for debug as below:\n"); + ipio_info("DEBUG_NONE = %d\n", DEBUG_NONE); + ipio_info("DEBUG_IRQ = %d\n", DEBUG_IRQ); + ipio_info("DEBUG_FINGER_REPORT = %d\n", DEBUG_FINGER_REPORT); + ipio_info("DEBUG_FIRMWARE = %d\n", DEBUG_FIRMWARE); + ipio_info("DEBUG_CONFIG = %d\n", DEBUG_CONFIG); + ipio_info("DEBUG_I2C = %d\n", DEBUG_I2C); + ipio_info("DEBUG_BATTERY = %d\n", DEBUG_BATTERY); + ipio_info("DEBUG_MP_TEST = %d\n", DEBUG_MP_TEST); + ipio_info("DEBUG_IOCTL = %d\n", DEBUG_IOCTL); + ipio_info("DEBUG_NETLINK = %d\n", DEBUG_NETLINK); + ipio_info("DEBUG_ALL = %d\n", DEBUG_ALL); + + res = copy_to_user((uint32_t *) buff, &ipio_debug_level, len); + if (res < 0) { + ipio_err("Failed to copy data to user space\n"); + } + + *pPos = len; + + return len; +} + +static ssize_t ilitek_proc_debug_level_write(struct file *filp, + const char *buff, size_t size, + loff_t *pPos) +{ + int res = 0; + char cmd[10] = { 0 }; + + if (buff != NULL) { + res = copy_from_user(cmd, buff, size - 1); + if (res < 0) { + ipio_info("copy data from user space, failed\n"); + return ERROR; + } + } + + ipio_debug_level = katoi(cmd); + + ipio_info("ipio_debug_level = %d\n", ipio_debug_level); + + return size; +} + +static int file_write(struct file_buffer *file, bool new_open) +{ + struct file *f = NULL; + mm_segment_t fs; + loff_t pos; + + if (file->ptr == NULL) { + ipio_err("str is invaild\n"); + return -1; + } + + if (file->file_name == NULL) { + ipio_err("file name is invaild\n"); + return -1; + } + + if (file->file_len >= file->file_max_zise) { + ipio_err("The length saved to file is too long !\n"); + return -1; + } + + if(new_open) + f = filp_open(file->file_name, O_WRONLY | O_CREAT | O_TRUNC, 644); + else + f = filp_open(file->file_name, O_WRONLY | O_CREAT | O_APPEND, 644); + + if (ERR_ALLOC_MEM(f)) { + ipio_err("Failed to open %s file\n", file->file_name); + return -1; + } + + fs = get_fs(); + set_fs(KERNEL_DS); + pos = 0; + vfs_write(f, file->ptr, file->file_len, &pos); + set_fs(fs); + filp_close(f, NULL); + + return 0; +} + +static int debug_mode_get_data(struct file_buffer *file, uint8_t type, uint32_t frame_count) +{ + int ret; + int timeout = 50; + uint8_t cmd[2] = { 0 }, row, col; + int16_t temp; + unsigned char *ptr; + int j; + uint16_t write_index = 0; + + ipd->debug_data_start_flag = false; + ipd->debug_data_frame = 0; + row = core_config->tp_info->nYChannelNum; + col = core_config->tp_info->nXChannelNum; + + mutex_lock(&ipd->touch_mutex); + cmd[0] = 0xFA; + cmd[1] = type; + ret = core_write(core_config->slave_i2c_addr, cmd, 2); + ipd->debug_data_start_flag = true; + mutex_unlock(&ipd->touch_mutex); + if(ret < 0) + return ret; + + while((write_index < frame_count) && (timeout > 0)) { + wait_event_interruptible(ipd->inq, ipd->debug_data_frame > write_index); + + ipio_info("frame %d,index=%d,count=%d\n",write_index, write_index % 1024, ipd->debug_data_frame); + if ((write_index % 1024) < ipd->debug_data_frame) { + file->file_len = 0; + memset(file->ptr, 0, file->file_max_zise); + file->file_len += sprintf(file->ptr + file->file_len, "\n\nFrame%d,",write_index); + for (j = 0; j < col; j ++) + file->file_len += sprintf(file->ptr + file->file_len, "[X%d] ,",j); + ptr = &ipd->debug_buf[write_index%1024][35]; + for (j = 0; j < row * col; j ++, ptr+=2) { + temp = (*ptr << 8) + *(ptr + 1); + if (j % col == 0) + file->file_len += sprintf(file->ptr + file->file_len, "\n[Y%d] ,",(j / col)); + file->file_len += sprintf(file->ptr + file->file_len, "%d, ",temp); + } + file->file_len += sprintf(file->ptr + file->file_len, "\n[X] ,"); + for (j = 0; j < row + col; j ++, ptr+=2) { + temp = (*ptr << 8) + *(ptr + 1); + if (j == col) + file->file_len += sprintf(file->ptr + file->file_len, "\n[Y] ,"); + file->file_len += sprintf(file->ptr + file->file_len, "%d, ",temp); + } + file_write(file, false); + write_index ++ ; + timeout = 50; + } + + if (write_index%1024 == 0 && ipd->debug_data_frame == 1024) + ipd->debug_data_frame = 0; + + mdelay(20);/*get one frame data take around 130ms*/ + timeout -- ; + if(timeout == 0) + ipio_err("debug mode get data timeout!\n"); + } + ipd->debug_data_start_flag = false; + + return 0; +} + +static ssize_t ilitek_proc_get_debug_mode_data_read(struct file *filp, char __user *buff, size_t size, loff_t *pPos) +{ + int ret; + uint8_t cmd[5] = { 0 }; + struct file_buffer csv; + + if (*pPos != 0) + return 0; + + /*initialize file*/ + memset(csv.file_name, 0, sizeof(csv.file_name)); + sprintf(csv.file_name, "%s", DEBUG_DATA_FILE_PATH); + csv.file_len = 0; + csv.file_max_zise = DEBUG_DATA_FILE_SIZE; + csv.ptr = vmalloc(csv.file_max_zise); + + if (ERR_ALLOC_MEM(csv.ptr)) { + ipio_err("Failed to allocate CSV mem\n"); + goto out; + } + + /*save data to csv*/ + ipio_info("Get Raw data %d frame\n", ipd->raw_count); + ipio_info("Get Delta data %d frame\n", ipd->delta_count); + + csv.file_len += sprintf(csv.ptr + csv.file_len, "Get Raw data%d frame\n", ipd->raw_count); + csv.file_len += sprintf(csv.ptr + csv.file_len, "Get Delta data %d frame\n", ipd->delta_count); + file_write(&csv, true); + + /*change to debug mode*/ + cmd[0] = protocol->debug_mode; + core_fr_mode_control(cmd); + + /*get raw data*/ + csv.file_len = 0; + memset(csv.ptr, 0, csv.file_max_zise); + csv.file_len += sprintf(csv.ptr + csv.file_len, "\n\n=======Raw data======="); + file_write(&csv, false); + ret = debug_mode_get_data(&csv, protocol->raw_data, ipd->raw_count); + if(ret < 0) + goto out; + + /*get delta data*/ + csv.file_len = 0; + memset(csv.ptr, 0, csv.file_max_zise); + csv.file_len += sprintf(csv.ptr + csv.file_len, "\n\n=======Delta data======="); + file_write(&csv, false); + ret = debug_mode_get_data(&csv, protocol->delta_data, ipd->delta_count); + if(ret < 0) + goto out; + + /*change to demo mode*/ + cmd[0] = protocol->demo_mode; + core_fr_mode_control(cmd); + +out: + if (csv.ptr != NULL) { + vfree(csv.ptr); + csv.ptr = NULL; + } + + return 0; +} + +static ssize_t ilitek_proc_get_debug_mode_data_write(struct file *filp, const char *buff, size_t size, loff_t *pPos) +{ + int ret = 0; + char *token = NULL, *cur = NULL; + char cmd[256] = { 0 }; + uint8_t temp[256] = { 0 }, count = 0; + + if (buff != NULL) { + ret = copy_from_user(cmd, buff, size - 1); + if (ret < 0) { + ipio_info("copy data from user space, failed\n"); + return -1; + } + } + ipio_info("size = %d, cmd = %s\n", (int)size, cmd); + + token = cur = cmd; + + while ((token = strsep(&cur, ",")) != NULL) { + temp[count] = str2hex(token); + ipio_info("temp[%d] = %d\n", count, temp[count]); + count++; + } + + ipd->raw_count = ((temp[0] << 8) | temp[1]); + ipd->delta_count = ((temp[2] << 8) | temp[3]); + ipd->bg_count = ((temp[4] << 8) | temp[5]); + + ipio_info("Raw_count = %d, Delta_count = %d, BG_count = %d\n", ipd->raw_count, ipd->delta_count, ipd->bg_count); + + return size; +} + +static ssize_t ilitek_proc_gesture_read(struct file *filp, char __user *buff, + size_t size, loff_t *pPos) +{ + int res = 0; + uint32_t len = 0; + + if (*pPos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + len = snprintf(g_user_buf, USER_STR_BUFF, "%d", core_config->isEnableGesture); + + ipio_info("isEnableGesture = %d\n", core_config->isEnableGesture); + + res = + copy_to_user((uint32_t *) buff, &core_config->isEnableGesture, len); + if (res < 0) { + ipio_err("Failed to copy data to user space\n"); + } + + *pPos = len; + + return len; +} + +static ssize_t ilitek_proc_gesture_write(struct file *filp, const char *buff, + size_t size, loff_t *pPos) +{ + int res = 0; + char cmd[10] = { 0 }; + + if (buff != NULL) { + res = copy_from_user(cmd, buff, size - 1); + if (res < 0) { + ipio_info("copy data from user space, failed\n"); + return ERROR; + } + } + + ipio_info("size = %d, cmd = %s\n", (int)size, cmd); + + if (strcmp(cmd, "on") == 0) { + ipio_info("enable gesture mode\n"); + core_config->isEnableGesture = true; + } else if (strcmp(cmd, "off") == 0) { + ipio_info("disable gesture mode\n"); + core_config->isEnableGesture = false; + } else if (strcmp(cmd, "info") == 0) { + ipio_info("gesture info mode\n"); + core_gesture->mode = GESTURE_INFO_MPDE; + } else if (strcmp(cmd, "normal") == 0) { + ipio_info("gesture normal mode\n"); + core_gesture->mode = GESTURE_NORMAL_MODE; + } else + ipio_err("Unknown command\n"); + + return size; +} + +static ssize_t ilitek_proc_check_battery_read(struct file *filp, + char __user *buff, size_t size, + loff_t *pPos) +{ + int res = 0; + uint32_t len = 0; + + if (*pPos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + len = snprintf(g_user_buf, USER_STR_BUFF, "%d", ipd->isEnablePollCheckPower); + + ipio_info("isEnablePollCheckPower = %d\n", ipd->isEnablePollCheckPower); + + res = + copy_to_user((uint32_t *) buff, &ipd->isEnablePollCheckPower, len); + if (res < 0) { + ipio_err("Failed to copy data to user space\n"); + } + + *pPos = len; + + return len; +} + +static ssize_t ilitek_proc_check_battery_write(struct file *filp, + const char *buff, size_t size, + loff_t *pPos) +{ + int res = 0; + char cmd[10] = { 0 }; + + if (buff != NULL) { + res = copy_from_user(cmd, buff, size - 1); + if (res < 0) { + ipio_info("copy data from user space, failed\n"); + return ERROR; + } + } + + ipio_info("size = %d, cmd = %s\n", (int)size, cmd); + +#ifdef ENABLE_BATTERY_CHECK + if (strcmp(cmd, "on") == 0) { + ipio_info("Start the thread of check power status\n"); + queue_delayed_work(ipd->check_power_status_queue, + &ipd->check_power_status_work, + ipd->work_delay); + ipd->isEnablePollCheckPower = true; + } else if (strcmp(cmd, "off") == 0) { + ipio_info("Cancel the thread of check power status\n"); + cancel_delayed_work_sync(&ipd->check_power_status_work); + ipd->isEnablePollCheckPower = false; + } else + ipio_err("Unknown command\n"); +#else + ipio_err("You need to enable its MACRO before operate it.\n"); +#endif + + return size; +} + +static ssize_t ilitek_proc_fw_process_read(struct file *filp, + char __user *buff, size_t size, + loff_t *pPos) +{ + int res = 0; + uint32_t len = 0; + + /* + * If file position is non-zero, we assume the string has been read + * and indicates that there is no more data to be read. + */ + if (*pPos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + len = snprintf(g_user_buf, USER_STR_BUFF, "%02d", core_firmware->update_status); + + ipio_info("update status = %d\n", core_firmware->update_status); + + res = + copy_to_user((uint32_t *) buff, &core_firmware->update_status, len); + if (res < 0) { + ipio_err("Failed to copy data to user space"); + } + + *pPos = len; + + return len; +} + +/* + * To avoid the restriction of selinux, we assigned a fixed path where locates firmware file, + * reading (cat) this node to notify driver running the upgrade process from user space. + */ +static ssize_t ilitek_proc_fw_upgrade_read(struct file *filp, + char __user *buff, size_t size, + loff_t *pPos) +{ + int res = 0; + uint32_t len = 0; + + ipio_info("Preparing to upgarde firmware\n"); + + if (*pPos != 0) + return 0; + + ilitek_platform_disable_irq(); + + if (ipd->isEnablePollCheckPower) + cancel_delayed_work_sync(&ipd->check_power_status_work); +#ifdef HOST_DOWNLOAD + ilitek_platform_tp_hw_reset(true); +#else + res = core_firmware_upgrade(UPDATE_FW_PATH, false); +#endif + ilitek_platform_enable_irq(); + + if (ipd->isEnablePollCheckPower) + queue_delayed_work(ipd->check_power_status_queue, + &ipd->check_power_status_work, + ipd->work_delay); + + if (res < 0) { + core_firmware->update_status = res; + ipio_err("Failed to upgrade firwmare\n"); + } else { + core_firmware->update_status = 100; + ipio_info("Succeed to upgrade firmware\n"); + } + + *pPos = len; + + return len; +} + +static ssize_t ilitek_proc_iram_upgrade_read(struct file *filp, + char __user *buff, size_t size, + loff_t *pPos) +{ + int res = 0; + uint32_t len = 0; + + ipio_info("Preparing to upgarde firmware by IRAM\n"); + + if (*pPos != 0) + return 0; + + ilitek_platform_disable_irq(); + + res = core_firmware_upgrade(UPDATE_FW_PATH, true); + + ilitek_platform_enable_irq(); + + if (res < 0) { + /* return the status to user space even if any error occurs. */ + core_firmware->update_status = res; + ipio_err("Failed to upgrade firwmare by IRAM, res = %d\n", res); + } else { + ipio_info("Succeed to upgrade firmware by IRAM\n"); + } + + *pPos = len; + + return len; +} + +/* for debug */ +static ssize_t ilitek_proc_ioctl_read(struct file *filp, char __user *buff, + size_t size, loff_t *pPos) +{ + int res = 0; + uint32_t len = 0; + uint8_t cmd[2] = { 0 }; + + if (*pPos != 0) + return 0; + + if (size < 4095) { + res = copy_from_user(cmd, buff, size - 1); + if (res < 0) { + ipio_info("copy data from user space, failed\n"); + return ERROR; + } + } + + ipio_info("size = %d, cmd = %d", (int)size, cmd[0]); + + /* test */ + if (cmd[0] == 0x1) { + ipio_info("HW Reset\n"); + ilitek_platform_tp_hw_reset(true); + } else if (cmd[0] == 0x02) { + ipio_info("Disable IRQ\n"); + ilitek_platform_disable_irq(); + } else if (cmd[0] == 0x03) { + ipio_info("Enable IRQ\n"); + ilitek_platform_enable_irq(); + } else if (cmd[0] == 0x04) { + ipio_info("Get Chip id\n"); + core_config_get_chip_id(); + } + + *pPos = len; + + return len; +} + +/* for debug */ +static ssize_t ilitek_proc_ioctl_write(struct file *filp, const char *buff, + size_t size, loff_t *pPos) +{ + int res = 0, count = 0, i; + int w_len = 0, r_len = 0, delay = 0; + char cmd[512] = { 0 }; + char *token = NULL, *cur = NULL; + uint8_t temp[256] = { 0 }; + uint8_t *data = NULL; + + if (buff != NULL) { + res = copy_from_user(cmd, buff, size - 1); + if (res < 0) { + ipio_info("copy data from user space, failed\n"); + return ERROR; + } + } + + ipio_info("size = %d, cmd = %s\n", (int)size, cmd); + + token = cur = cmd; + + data = kmalloc(512 * sizeof(uint8_t), GFP_KERNEL); + memset(data, 0, 512); + + while ((token = strsep(&cur, ",")) != NULL) { + data[count] = str2hex(token); + ipio_info("data[%d] = %x\n", count, data[count]); + count++; + } + + ipio_info("cmd = %s\n", cmd); + + if (strcmp(cmd, "reset") == 0) { + ipio_info("HW Reset\n"); + ilitek_platform_tp_hw_reset(true); + } else if (strcmp(cmd, "disirq") == 0) { + ipio_info("Disable IRQ\n"); + ilitek_platform_disable_irq(); + } else if (strcmp(cmd, "enairq") == 0) { + ipio_info("Enable IRQ\n"); + ilitek_platform_enable_irq(); + } else if (strcmp(cmd, "getchip") == 0) { + ipio_info("Get Chip id\n"); + core_config_get_chip_id(); + } else if (strcmp(cmd, "dispcc") == 0) { + ipio_info("disable phone cover\n"); + core_config_phone_cover_ctrl(false); + } else if (strcmp(cmd, "enapcc") == 0) { + ipio_info("enable phone cover\n"); + core_config_phone_cover_ctrl(true); + } else if (strcmp(cmd, "disfsc") == 0) { + ipio_info("disable finger sense\n"); + core_config_finger_sense_ctrl(false); + } else if (strcmp(cmd, "enafsc") == 0) { + ipio_info("enable finger sense\n"); + core_config_finger_sense_ctrl(true); + } else if (strcmp(cmd, "disprox") == 0) { + ipio_info("disable proximity\n"); + core_config_proximity_ctrl(false); + } else if (strcmp(cmd, "enaprox") == 0) { + ipio_info("enable proximity\n"); + core_config_proximity_ctrl(true); + } else if (strcmp(cmd, "disglove") == 0) { + ipio_info("disable glove function\n"); + core_config_glove_ctrl(false, false); + } else if (strcmp(cmd, "enaglove") == 0) { + ipio_info("enable glove function\n"); + core_config_glove_ctrl(true, false); + } else if (strcmp(cmd, "glovesl") == 0) { + ipio_info("set glove as seamless\n"); + core_config_glove_ctrl(true, true); + } else if (strcmp(cmd, "enastylus") == 0) { + ipio_info("enable stylus\n"); + core_config_stylus_ctrl(true, false); + } else if (strcmp(cmd, "disstylus") == 0) { + ipio_info("disable stylus\n"); + core_config_stylus_ctrl(false, false); + } else if (strcmp(cmd, "stylussl") == 0) { + ipio_info("set stylus as seamless\n"); + core_config_stylus_ctrl(true, true); + } else if (strcmp(cmd, "tpscan_ab") == 0) { + ipio_info("set TP scan as mode AB\n"); + core_config_tp_scan_mode(true); + } else if (strcmp(cmd, "tpscan_b") == 0) { + ipio_info("set TP scan as mode B\n"); + core_config_tp_scan_mode(false); + } else if (strcmp(cmd, "phone_cover") == 0) { + ipio_info("set size of phone conver window\n"); + core_config_set_phone_cover(data); + } else if (strcmp(cmd, "debugmode") == 0) { + ipio_info("debug mode test enter\n"); + temp[0] = protocol->debug_mode; + core_fr_mode_control(temp); + } else if (strcmp(cmd, "baseline") == 0) { + ipio_info("test baseline raw\n"); + temp[0] = protocol->debug_mode; + core_fr_mode_control(temp); + ilitek_platform_disable_irq(); + temp[0] = 0xFA; + temp[1] = 0x08; + core_write(core_config->slave_i2c_addr, temp, 2); + ilitek_platform_enable_irq(); + } else if (strcmp(cmd, "delac_on") == 0) { + ipio_info("test get delac\n"); + temp[0] = protocol->debug_mode; + core_fr_mode_control(temp); + ilitek_platform_disable_irq(); + temp[0] = 0xFA; + temp[1] = 0x03; + core_write(core_config->slave_i2c_addr, temp, 2); + ilitek_platform_enable_irq(); + } else if (strcmp(cmd, "delac_off") == 0) { + ipio_info("test get delac\n"); + temp[0] = protocol->demo_mode; + core_fr_mode_control(temp); + } else if (strcmp(cmd, "test") == 0) { + ipio_info("test test_reset test 1\n"); + gpio_direction_output(ipd->reset_gpio, 1); + mdelay(1); + gpio_set_value(ipd->reset_gpio, 0); + mdelay(1); + gpio_set_value(ipd->reset_gpio, 1); + mdelay(10); + } else if (strcmp(cmd, "gt") == 0) { + ipio_info("test Gesture test\n"); +#ifdef HOST_DOWNLOAD + core_gesture_load_code(); +#endif + } else if (strcmp(cmd, "suspend") == 0) { + ipio_info("test suspend test\n"); + core_config_ic_suspend(); + } else if (strcmp(cmd, "resume") == 0) { + ipio_info("test resume test\n"); + core_config_ic_resume(); + } + + else if (strcmp(cmd, "gt1") == 0) { + ipio_info("test Gesture test 1\n"); + temp[0] = 0x01; + temp[1] = 0x01; + temp[2] = 0x00; + w_len = 3; + core_write(core_config->slave_i2c_addr, temp, w_len); + if (core_config_check_cdc_busy(50, 100) < 0) + ipio_err("Check busy is timout !\n"); + } else if (strcmp(cmd, "gt2") == 0) { + temp[0] = 0x01; + temp[1] = 0x0A; + temp[2] = 0x01; + w_len = 3; + core_write(core_config->slave_i2c_addr, temp, w_len); + ipio_info("test Gesture test\n"); + } else if (strcmp(cmd, "i2c_w") == 0) { + w_len = data[1]; + ipio_info("w_len = %d\n", w_len); + + for (i = 0 ; i < w_len ; i++) { + temp[i] = data[2 + i]; + ipio_info("i2c[%d] = %x\n", i, temp[i]); + } + + core_write(core_config->slave_i2c_addr, temp, w_len); + } else if (strcmp(cmd, "i2c_r") == 0) { + r_len = data[1]; + ipio_info("r_len = %d\n", r_len); + + core_read(core_config->slave_i2c_addr, &temp[0], r_len); + + for (i = 0 ; i < r_len ; i++) + ipio_info("temp[%d] = %x\n", i, temp[i]); + } else if (strcmp(cmd, "i2c_w_r") == 0) { + w_len = data[1]; + r_len = data[2]; + delay = data[3]; + ipio_info("w_len = %d, r_len = %d, delay = %d\n", w_len, r_len, + delay); + + for (i = 0 ; i < w_len ; i++) { + temp[i] = data[4 + i]; + ipio_info("temp[%d] = %x\n", i, temp[i]); + } + + core_write(core_config->slave_i2c_addr, temp, w_len); + + memset(temp, 0, sizeof(temp)); + mdelay(delay); + + core_read(core_config->slave_i2c_addr, &temp[0], r_len); + + for (i = 0 ; i < r_len ; i++) + ipio_info("temp[%d] = %x\n", i, temp[i]); + } else { + ipio_err("Unknown command\n"); + } + + ipio_kfree((void **)&data); + return size; +} + +static long ilitek_proc_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int res = 0, length = 0; + uint8_t *szBuf = NULL; + static uint16_t i2c_rw_length; + uint32_t id_to_user = 0x0; + char dbg[10] = { 0 }; + + ipio_debug(DEBUG_IOCTL, "cmd = %d\n", _IOC_NR(cmd)); + + if (_IOC_TYPE(cmd) != ILITEK_IOCTL_MAGIC) { + ipio_err("The Magic number doesn't match\n"); + return -ENOTTY; + } + + if (_IOC_NR(cmd) > ILITEK_IOCTL_MAXNR) { + ipio_err("The number of ioctl doesn't match\n"); + return -ENOTTY; + } + + szBuf = kmalloc(2048 * sizeof(uint8_t), GFP_KERNEL); + if (ERR_ALLOC_MEM(szBuf)) { + ipio_err("Failed to allocate szBuf memory\n"); + return -ENOMEM; + } + + switch (cmd) { + case ILITEK_IOCTL_I2C_WRITE_DATA: + res = copy_from_user(szBuf, (uint8_t *) arg, i2c_rw_length); + if (res < 0) { + ipio_err("Failed to copy data from user space\n"); + } else { + res = + core_write(core_config->slave_i2c_addr, &szBuf[0], + i2c_rw_length); + if (res < 0) { + ipio_err("Failed to write data via i2c\n"); + } + } + break; + + case ILITEK_IOCTL_I2C_READ_DATA: + res = + core_read(core_config->slave_i2c_addr, szBuf, + i2c_rw_length); + if (res < 0) { + ipio_err("Failed to read data via i2c\n"); + } else { + res = + copy_to_user((uint8_t *) arg, szBuf, i2c_rw_length); + if (res < 0) { + ipio_err("Failed to copy data to user space\n"); + } + } + break; + + case ILITEK_IOCTL_I2C_SET_WRITE_LENGTH: + case ILITEK_IOCTL_I2C_SET_READ_LENGTH: + i2c_rw_length = arg; + break; + + case ILITEK_IOCTL_TP_HW_RESET: + ilitek_platform_tp_hw_reset(true); + break; + + case ILITEK_IOCTL_TP_POWER_SWITCH: + ipio_info("Not implemented yet\n"); + break; + + case ILITEK_IOCTL_TP_REPORT_SWITCH: + res = copy_from_user(szBuf, (uint8_t *) arg, 1); + if (res < 0) { + ipio_err("Failed to copy data from user space\n"); + } else { + if (szBuf[0]) { + core_fr->isEnableFR = true; + ipio_debug(DEBUG_IOCTL, + "Function of finger report was enabled\n"); + } else { + core_fr->isEnableFR = false; + ipio_debug(DEBUG_IOCTL, + "Function of finger report was disabled\n"); + } + } + break; + + case ILITEK_IOCTL_TP_IRQ_SWITCH: + res = copy_from_user(szBuf, (uint8_t *) arg, 1); + if (res < 0) { + ipio_err("Failed to copy data from user space\n"); + } else { + if (szBuf[0]) { + ilitek_platform_enable_irq(); + } else { + ilitek_platform_disable_irq(); + } + } + break; + + case ILITEK_IOCTL_TP_DEBUG_LEVEL: + res = copy_from_user(dbg, (uint32_t *) arg, sizeof(uint32_t)); + if (res < 0) { + ipio_err("Failed to copy data from user space\n"); + } else { + ipio_debug_level = katoi(dbg); + ipio_info("ipio_debug_level = %d", ipio_debug_level); + } + break; + + case ILITEK_IOCTL_TP_FUNC_MODE: + res = copy_from_user(szBuf, (uint8_t *) arg, 3); + if (res < 0) { + ipio_err("Failed to copy data from user space\n"); + } else { + core_write(core_config->slave_i2c_addr, &szBuf[0], 3); + } + + break; + + case ILITEK_IOCTL_TP_FW_VER: + res = core_config_get_fw_ver(); + if (res < 0) { + ipio_err("Failed to get firmware version\n"); + } else { + res = + copy_to_user((uint8_t *) arg, + core_config->firmware_ver, + protocol->fw_ver_len); + if (res < 0) { + ipio_err + ("Failed to copy firmware version to user space\n"); + } + } + break; + + case ILITEK_IOCTL_TP_PL_VER: + res = core_config_get_protocol_ver(); + if (res < 0) { + ipio_err("Failed to get protocol version\n"); + } else { + res = + copy_to_user((uint8_t *) arg, + core_config->protocol_ver, + protocol->pro_ver_len); + if (res < 0) { + ipio_err + ("Failed to copy protocol version to user space\n"); + } + } + break; + + case ILITEK_IOCTL_TP_CORE_VER: + res = core_config_get_core_ver(); + if (res < 0) { + ipio_err("Failed to get core version\n"); + } else { + res = + copy_to_user((uint8_t *) arg, core_config->core_ver, + protocol->core_ver_len); + if (res < 0) { + ipio_err + ("Failed to copy core version to user space\n"); + } + } + break; + + case ILITEK_IOCTL_TP_DRV_VER: + length = snprintf(szBuf, 2048, "%s", DRIVER_VERSION); + if (!length) { + ipio_err + ("Failed to convert driver version from definiation\n"); + } else { + res = copy_to_user((uint8_t *) arg, szBuf, length); + if (res < 0) { + ipio_err + ("Failed to copy driver ver to user space\n"); + } + } + break; + + case ILITEK_IOCTL_TP_CHIP_ID: + res = core_config_get_chip_id(); + if (res < 0) { + ipio_err("Failed to get chip id\n"); + } else { + id_to_user = + core_config->chip_id << 16 | core_config->chip_type; + + res = + copy_to_user((uint32_t *) arg, &id_to_user, + sizeof(uint32_t)); + if (res < 0) { + ipio_err + ("Failed to copy chip id to user space\n"); + } + } + break; + + case ILITEK_IOCTL_TP_NETLINK_CTRL: + res = copy_from_user(szBuf, (uint8_t *) arg, 1); + if (res < 0) { + ipio_err("Failed to copy data from user space\n"); + } else { + if (szBuf[0]) { + core_fr->isEnableNetlink = true; + ipio_debug(DEBUG_IOCTL, + "Netlink has been enabled\n"); + } else { + core_fr->isEnableNetlink = false; + ipio_debug(DEBUG_IOCTL, + "Netlink has been disabled\n"); + } + } + break; + + case ILITEK_IOCTL_TP_NETLINK_STATUS: + ipio_debug(DEBUG_IOCTL, "Netlink is enabled : %d\n", + core_fr->isEnableNetlink); + res = + copy_to_user((int *)arg, &core_fr->isEnableNetlink, + sizeof(int)); + if (res < 0) { + ipio_err("Failed to copy chip id to user space\n"); + } + break; + + case ILITEK_IOCTL_TP_MODE_CTRL: + res = copy_from_user(szBuf, (uint8_t *) arg, 4); + if (res < 0) { + ipio_err("Failed to copy data from user space\n"); + } else { + core_fr_mode_control(szBuf); + } + break; + + case ILITEK_IOCTL_TP_MODE_STATUS: + ipio_debug(DEBUG_IOCTL, "Current firmware mode : %d", + core_fr->actual_fw_mode); + res = + copy_to_user((int *)arg, &core_fr->actual_fw_mode, + sizeof(int)); + if (res < 0) { + ipio_err("Failed to copy chip id to user space\n"); + } + break; + + case ILITEK_IOCTL_ICE_MODE_SWITCH: + res = copy_from_user(szBuf, (uint8_t *) arg, 1); + if (res < 0) { + ipio_err("Failed to copy data from user space\n"); + } else { + if (szBuf[0]) { + core_config->icemodeenable = true; + } else { + core_config->icemodeenable = false; + } + } + break; + case ILITEK_IOCTL_MP_WRITE_CDC_FLAG_FALSE: + res = copy_from_user(szBuf, (uint8_t *) arg, 1); + if (res < 0) { + ipio_err("Failed to copy data from user space\n"); + } else { + ipio_debug(DEBUG_IOCTL, "ioctl write = %d\n", szBuf[0]); + if (szBuf[0] == 0) { + core_config->interrupt_flag = false; + ipio_debug(DEBUG_IOCTL, "core_config->interrupt_flag = %d\n", core_config->interrupt_flag); + } + } + break; + + case ILITEK_IOCTL_MP_READ_CDC_FLAG: + + res = + copy_to_user((int *)arg, &core_config->interrupt_flag, + sizeof(int)); + ipio_debug(DEBUG_IOCTL, "core_config->interrupt_flag = %d\n", core_config->interrupt_flag); + if (res < 0) { + ipio_err("Failed to copy interrupt_flag flag to user space\n"); + } + break; + + default: + res = -ENOTTY; + break; + } + kfree((void **)szBuf); + + return res; +} + +struct proc_dir_entry *proc_dir_ilitek; +struct proc_dir_entry *proc_ioctl; +struct proc_dir_entry *proc_fw_process; +struct proc_dir_entry *proc_fw_upgrade; +struct proc_dir_entry *proc_iram_upgrade; +struct proc_dir_entry *proc_gesture; +struct proc_dir_entry *proc_debug_level; +struct proc_dir_entry *proc_mp_test; +struct proc_dir_entry *proc_debug_message; +struct proc_dir_entry *proc_debug_message_switch; +struct proc_dir_entry *proc_get_debug_mode_data; + +struct file_operations proc_ioctl_fops = { + .unlocked_ioctl = ilitek_proc_ioctl, + .read = ilitek_proc_ioctl_read, + .write = ilitek_proc_ioctl_write, +}; + +struct file_operations proc_fw_process_fops = { + .read = ilitek_proc_fw_process_read, +}; + +struct file_operations proc_fw_upgrade_fops = { + .read = ilitek_proc_fw_upgrade_read, +}; + +struct file_operations proc_iram_upgrade_fops = { + .read = ilitek_proc_iram_upgrade_read, +}; + +struct file_operations proc_gesture_fops = { + .write = ilitek_proc_gesture_write, + .read = ilitek_proc_gesture_read, +}; + +struct file_operations proc_check_battery_fops = { + .write = ilitek_proc_check_battery_write, + .read = ilitek_proc_check_battery_read, +}; + +struct file_operations proc_debug_level_fops = { + .write = ilitek_proc_debug_level_write, + .read = ilitek_proc_debug_level_read, +}; + +struct file_operations proc_mp_test_fops = { + .write = ilitek_proc_mp_test_write, + .read = ilitek_proc_mp_test_read, +}; + +struct file_operations proc_oppo_mp_lcm_on_fops = { + .read = ilitek_proc_oppo_mp_lcm_on_read, +}; + +struct file_operations proc_oppo_mp_lcm_off_fops = { + .read = ilitek_proc_oppo_mp_lcm_off_read, +}; + +struct file_operations proc_debug_message_fops = { + .write = ilitek_proc_debug_message_write, + .read = ilitek_proc_debug_message_read, +}; + +struct file_operations proc_debug_message_switch_fops = { + .read = ilitek_proc_debug_switch_read, +}; + +struct file_operations proc_fw_pc_counter_fops = { + .read = ilitek_proc_fw_pc_counter_read, +}; + +struct file_operations proc_get_debug_mode_data_fops = { + .read = ilitek_proc_get_debug_mode_data_read, + .write = ilitek_proc_get_debug_mode_data_write, +}; + +/** + * This struct lists all file nodes will be created under /proc filesystem. + * + * Before creating a node that you want, declaring its file_operations structure + * is necessary. After that, puts the structure into proc_table, defines its + * node's name in the same row, and the init function lterates the table and + * creates all nodes under /proc. + * + */ +typedef struct { + char *name; + struct proc_dir_entry *node; + struct file_operations *fops; + bool isCreated; +} proc_node_t; + +proc_node_t proc_table[] = { + {"ioctl", NULL, &proc_ioctl_fops, false}, + {"fw_process", NULL, &proc_fw_process_fops, false}, + {"fw_upgrade", NULL, &proc_fw_upgrade_fops, false}, + {"iram_upgrade", NULL, &proc_iram_upgrade_fops, false}, + {"gesture", NULL, &proc_gesture_fops, false}, + {"check_battery", NULL, &proc_check_battery_fops, false}, + {"debug_level", NULL, &proc_debug_level_fops, false}, + {"mp_test", NULL, &proc_mp_test_fops, false}, + {"oppo_mp_lcm_on", NULL, &proc_oppo_mp_lcm_on_fops, false}, + {"oppo_mp_lcm_off", NULL, &proc_oppo_mp_lcm_off_fops, false}, + {"debug_message", NULL, &proc_debug_message_fops, false}, + {"debug_message_switch", NULL, &proc_debug_message_switch_fops, false}, + {"fw_pc_counter", NULL, &proc_fw_pc_counter_fops, false}, + {"get_debug_mode_data", NULL, &proc_get_debug_mode_data_fops, false}, +}; + +#define NETLINK_USER 21 +struct sock *_gNetLinkSkb; +struct nlmsghdr *_gNetLinkHead; +struct sk_buff *_gSkbOut; +int _gPID; + +void netlink_reply_msg(void *raw, int size) +{ + int res; + int msg_size = size; + uint8_t *data = (uint8_t *) raw; + + ipio_debug(DEBUG_NETLINK, "The size of data being sent to user = %d\n", + msg_size); + ipio_debug(DEBUG_NETLINK, "pid = %d\n", _gPID); + ipio_debug(DEBUG_NETLINK, "Netlink is enable = %d\n", + core_fr->isEnableNetlink); + + if (core_fr->isEnableNetlink) { + _gSkbOut = nlmsg_new(msg_size, 0); + + if (!_gSkbOut) { + ipio_err("Failed to allocate new skb\n"); + return; + } + + _gNetLinkHead = + nlmsg_put(_gSkbOut, 0, 0, NLMSG_DONE, msg_size, 0); + NETLINK_CB(_gSkbOut).dst_group = 0; /* not in mcast group */ + + /* strncpy(NLMSG_DATA(_gNetLinkHead), data, msg_size); */ + memcpy(nlmsg_data(_gNetLinkHead), data, msg_size); + + res = nlmsg_unicast(_gNetLinkSkb, _gSkbOut, _gPID); + if (res < 0) + ipio_err("Failed to send data back to user\n"); + } +} + +EXPORT_SYMBOL(netlink_reply_msg); + +static void netlink_recv_msg(struct sk_buff *skb) +{ + _gPID = 0; + + ipio_debug(DEBUG_NETLINK, "Netlink is enable = %d\n", + core_fr->isEnableNetlink); + + _gNetLinkHead = (struct nlmsghdr *)skb->data; + + ipio_debug(DEBUG_NETLINK, "Received a request from client: %s, %d\n", + (char *)NLMSG_DATA(_gNetLinkHead), + (int)strlen((char *)NLMSG_DATA(_gNetLinkHead))); + + /* pid of sending process */ + _gPID = _gNetLinkHead->nlmsg_pid; + + ipio_debug(DEBUG_NETLINK, "the pid of sending process = %d\n", _gPID); + + /* TODO: may do something if there's not receiving msg from user. */ + if (_gPID != 0) { + ipio_err + ("The channel of Netlink has been established successfully !\n"); + core_fr->isEnableNetlink = true; + } else { + ipio_err + ("Failed to establish the channel between kernel and user space\n"); + core_fr->isEnableNetlink = false; + } +} + +static int netlink_init(void) +{ + int res = 0; + +#if KERNEL_VERSION(3, 4, 0) > LINUX_VERSION_CODE + _gNetLinkSkb = + netlink_kernel_create(&init_net, NETLINK_USER, netlink_recv_msg, + NULL, THIS_MODULE); +#else + struct netlink_kernel_cfg cfg = { + .input = netlink_recv_msg, + }; + + _gNetLinkSkb = netlink_kernel_create(&init_net, NETLINK_USER, &cfg); +#endif + + ipio_debug(DEBUG_BOOT,"Initialise Netlink and create its socket\n"); + + if (!_gNetLinkSkb) { + ipio_err("Failed to create nelink socket\n"); + res = -EFAULT; + } + + return res; +} + +int ilitek_proc_init(void) +{ + int i = 0, res = 0; + + g_user_buf = kcalloc(USER_STR_BUFF, sizeof(char), GFP_KERNEL); + + if (ERR_ALLOC_MEM(g_user_buf)) { + ipio_err("Failed to allocate buffers for user space\n"); + return -ENOMEM; + } + + proc_dir_ilitek = proc_mkdir("ilitek", NULL); + + for (; i < ARRAY_SIZE(proc_table); i++) { + proc_table[i].node = + proc_create(proc_table[i].name, 0444, proc_dir_ilitek, + proc_table[i].fops); + + if (proc_table[i].node == NULL) { + proc_table[i].isCreated = false; + ipio_err("Failed to create %s under /proc\n", + proc_table[i].name); + res = -ENODEV; + } else { + proc_table[i].isCreated = true; + ipio_debug(DEBUG_BOOT,"Succeed to create %s under /proc\n", + proc_table[i].name); + } + } + + netlink_init(); + + return res; +} + +EXPORT_SYMBOL(ilitek_proc_init); + + +void ilitek_proc_remove(void) +{ + int i = 0; + + for (; i < ARRAY_SIZE(proc_table); i++) { + if (proc_table[i].isCreated == true) { + ipio_info("Removed %s under /proc\n", + proc_table[i].name); + remove_proc_entry(proc_table[i].name, proc_dir_ilitek); + } + } + + remove_proc_entry("ilitek", NULL); + netlink_kernel_release(_gNetLinkSkb); + ipio_kfree((void **)&g_user_buf); +} + +EXPORT_SYMBOL(ilitek_proc_remove); + + +static ssize_t drv_power_on_show(struct device *pDevice, struct device_attribute *pAttr, char *pBuf) +{ + ipio_info("*** %s() ipd->suspended = %d ***\n", __func__, ipd->suspended); + + if (ipd->suspended == true) + return snprintf(pBuf, PAGE_SIZE, "0\n"); + else + return snprintf(pBuf, PAGE_SIZE, "1\n"); +} +static DEVICE_ATTR(poweron, 0444, drv_power_on_show, NULL); + +static ssize_t drv_product_info_show(struct device *pDevice, struct device_attribute *pAttr, char *pBuf) +{ + ipio_info ("*** %s() productinfo = %s ***\n", __func__, ipd->TP_IC_TYPE); + + return scnprintf(pBuf, PAGE_SIZE, "%s\n", ipd->TP_IC_TYPE); +} +static DEVICE_ATTR(productinfo, 0444, drv_product_info_show, NULL); + +static ssize_t drv_force_reflash_store(struct device *pDevice, struct device_attribute *pAttr, const char *pBuf, size_t nSize) +{ + int temp; + ipio_info ("*** %s() ***\n", __func__); + // ipio_info( "pBuf = %s\n", pBuf); + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + memcpy(g_user_buf, pBuf, nSize - 1); + + ipio_info ("%s g_user_buf=%s\n", __func__, g_user_buf); + temp = katoi(g_user_buf); + ipio_info ("%s temp=%d\n", __func__, temp); + if (temp == 1) + core_firmware->force_upgrad = true; + else + core_firmware->force_upgrad = false; + + ipio_info("%s core_firmware->force_upgrad =%d\n", __func__, core_firmware->force_upgrad); + return nSize; +} +static DEVICE_ATTR(forcereflash, 0220, NULL, drv_force_reflash_store); + + +static ssize_t drv_flash_prog_show(struct device *pDevice, struct device_attribute *pAttr, char *pBuf) +{ + ipio_info ("*** %s() core_firmware->isUpgrading = %d ***\n", __func__, core_firmware->isUpgrading); + + return scnprintf(pBuf, PAGE_SIZE, "%d\n", core_firmware->isUpgrading); +} +static DEVICE_ATTR(flashprog, 0444, drv_flash_prog_show, NULL); + +static ssize_t do_reflash_store(struct device *pDevice, struct device_attribute *pAttr, const char *pBuf, size_t nSize) +{ + int ret; + int retry = 0; + char prefix[128] = "ILITEK"; + const char *chip_name = ipd->TP_IC_TYPE; + + //int temp; + ipio_info ("*** %s() ***\n", __func__); + // ipio_info( "pBuf = %s\n", pBuf); + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + memcpy(g_user_buf, pBuf, nSize - 1); + ipio_info ("%s g_user_buf=%s\n", __func__, g_user_buf); + + if (ipd->suspended == true) { + ipio_err("%s: In suspend state, try again later\n", __func__); + ret = -EINVAL; + goto exit; + } + + if (core_firmware->isUpgrading == true) { + ipio_err("%s: In FW flashing state, try again later\n", __func__); + ret = -EINVAL; + goto exit; + } + + if (core_firmware->force_upgrad != 1) { + if (strnstr(g_user_buf, prefix, strnlen(g_user_buf, USER_STR_BUFF * sizeof(unsigned char))) <= 0) { + ipio_err("%s: FW does not belong to %s\n", __func__, prefix); + ret = -EINVAL; + goto exit; + } + if (strnstr(g_user_buf, chip_name, strnlen(g_user_buf, USER_STR_BUFF * sizeof(unsigned char))) <= 0) { + ipio_err("%s: FW does not belong to chip %s\n", __func__, chip_name); + ret = -EINVAL; + goto exit; + } + ipio_info("%s: FW belong to %s\n", __func__, prefix); + ipio_err("%s: FW belong to chip %s\n", __func__, chip_name); + } + + ilitek_platform_disable_irq(); + + if (ipd->isEnablePollCheckPower) + cancel_delayed_work_sync(&ipd->check_power_status_work); + for (retry = 1; retry <= UPGRADE_RETRY_TIMES; retry++) { + ret = core_firmware_upgrade(g_user_buf, false); + if (ret < 0) { + msleep(100); + /* We do have to reset chip in order to move new code from flash to iram. */ + ipio_info("Upgrade fail Doing Soft Reset res = %d retry = %d\n", ret, retry); + core_config_ic_reset(); + } + else { + break; + } + } + + ilitek_platform_enable_irq(); + + if (ipd->isEnablePollCheckPower) + queue_delayed_work(ipd->check_power_status_queue, + &ipd->check_power_status_work, + ipd->work_delay); + if (ret < 0) { + core_firmware->update_status = ret; + ipio_err("Failed to upgrade firwmare\n"); + } else { + core_firmware->update_status = 100; + ipio_info("Succeed to upgrade firmware\n"); + } + + + core_firmware->isUpgrading = false; +exit: + return nSize; +} + +static DEVICE_ATTR(doreflash, 0220, NULL, do_reflash_store); + +static ssize_t drv_build_id_show(struct device *pDevice, struct device_attribute *pAttr, char *pBuf) +{ + core_config_get_fw_ver(); + ipio_info ("*** %s() Fw Version = V%d.%d.%d.%d***\n", __func__, core_config->firmware_ver[1], core_config->firmware_ver[2], core_config->firmware_ver[3], core_config->firmware_ver[4]); + + return scnprintf(pBuf, PAGE_SIZE, "%02x-%02x\n", core_config->firmware_ver[2], core_config->firmware_ver[3]); +} +static DEVICE_ATTR(buildid, 0444, drv_build_id_show, NULL); + +static const struct attribute *dev_attrs_list[] = { + &dev_attr_poweron.attr, + &dev_attr_productinfo.attr, + &dev_attr_forcereflash.attr, + &dev_attr_flashprog.attr, + &dev_attr_doreflash.attr, + &dev_attr_buildid.attr, + NULL +}; + +static ssize_t path_show(struct device *pDevice, struct device_attribute *pAttr, char *pBuf) +{ + ssize_t blen; + const char *path; + + path = kobject_get_path(&ipd->client->dev.kobj, GFP_KERNEL); + blen = scnprintf(pBuf, PAGE_SIZE, "%s\n", path ? path : "na"); + kfree(path); + + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ipio_info("*** %s() vendor = %s ***\n", __func__, "ilitek"); + return scnprintf(buf, PAGE_SIZE, "ilitek"); +} + + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_NULL +}; + +int ilitek_sys_init(void) +{ + int i; + s32 ret = 0; + dev_t devno; + struct device_attribute *attrs = touchscreen_attributes; + + ret = alloc_chrdev_region(&devno, 0, 1, ipd->TP_IC_TYPE); + if (ret) { + ipio_err ("can't allocate chrdev\n"); + return ret; + } else { + + /* set sysfs for firmware */ + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + ret = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + ipio_err("Failed to create touchscreen class!\n"); + return ret; + } + + touchscreen_class_dev = device_create(touchscreen_class, NULL, devno, NULL, ipd->TP_IC_TYPE); + pr_info(" touchscreen_class_dev = %p \n", touchscreen_class_dev); + if (IS_ERR(touchscreen_class_dev)) { + ret = PTR_ERR(touchscreen_class_dev); + touchscreen_class_dev = NULL; + ipio_err("Failed to create device(touchscreen_class_dev)!\n"); + return ret; + } + ipio_info("Succeed to create device(touchscreen_class_dev)!\n"); + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + ret = device_create_file(touchscreen_class_dev, &attrs[i]); + if (ret < 0) + goto device_destroy; + } + + ret = sysfs_create_files(&ipd->client->dev.kobj, dev_attrs_list); + if (ret < 0) { + ipio_info("Fail to create dev_attrs_list files)!\n"); + goto device_destroy; + } + + ipio_info("Succeed to sysfs create files)!\n"); + } + + return ret; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(touchscreen_class_dev, &attrs[i]); + + touchscreen_class_dev = NULL; + class_unregister(touchscreen_class); + ipio_err("error creating touchscreen class\n"); + + return -ENODEV; +} + +EXPORT_SYMBOL(ilitek_sys_init); + +void ilitek_sys_remove(void) +{ + int i; + struct device_attribute *attrs = touchscreen_attributes; + + sysfs_remove_files(&ipd->client->dev.kobj, dev_attrs_list); + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(touchscreen_class_dev, &attrs[i]); + + + device_unregister(touchscreen_class_dev); + + class_unregister(touchscreen_class); +} + +EXPORT_SYMBOL(ilitek_sys_remove); diff --git a/drivers/input/touchscreen/ilitek_v3_mmi/Android.mk b/drivers/input/touchscreen/ilitek_v3_mmi/Android.mk new file mode 100644 index 000000000000..46a3704fbc83 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_v3_mmi/Android.mk @@ -0,0 +1,69 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifneq ($(BOARD_USES_DOUBLE_TAP),) + KERNEL_CFLAGS += CONFIG_INPUT_ILI_ENABLE_DOUBLE_TAP=y +endif + +ifeq ($(BOARD_USES_DOUBLE_TAP_CTRL),true) + KERNEL_CFLAGS += CONFIG_BOARD_USES_DOUBLE_TAP_CTRL=y +endif + +ifneq ($(MOTO_PANEL_CHECK_TOUCH_STATE),) + KERNEL_CFLAGS += CONFIG_INPUT_ILI_SET_TOUCH_STATE=y +endif + +ifneq ($(BOARD_USES_PANEL_NOTIFICATIONS),) + KERNEL_CFLAGS += CONFIG_INPUT_ILI_PANEL_NOTIFICATIONS=y +endif + +ifneq ($(BOARD_USES_MTK_CHECK_PANEL),) + KERNEL_CFLAGS += CONFIG_ILITEK_MTK_CHECK_PANEL=y +endif + +ifeq ($(ILITEK_MULTI_TP_MODULE),true) + KERNEL_CFLAGS += CONFIG_ILITEK_MULTI_TP_MODULE=y +endif + +ifeq ($(CONFIG_STOWED_MODE_EN),true) + KERNEL_CFLAGS += CONFIG_STOWED_MODE_EN=y +endif + +ifneq ($(ILITEK_RESUME_BY_DDI),) + KERNEL_CFLAGS += CONFIG_ILITEK_RESUME_BY_DDI=y +endif + +ifeq ($(CONFIG_INPUT_CHARGER_DETECTION),true) + KERNEL_CFLAGS += CONFIG_ILITEK_CHARGER=y +endif + +ifeq ($(ILITEK_ESD),true) + KERNEL_CFLAGS += CONFIG_ILITEK_ESD=y +endif + +ifeq ($(ILITEK_GESTURE),true) + KERNEL_CFLAGS += CONFIG_ILITEK_GESTURE=y +endif + +ifeq ($(TOUCHSCREEN_LAST_TIME),true) + KERNEL_CFLAGS += CONFIG_ILI_TOUCH_LAST_TIME=y +endif + +ifneq ($(findstring mmi_info.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KBUILD_OPTIONS += CONFIG_INPUT_TOUCHSCREEN_MMI=y + endif + +include $(CLEAR_VARS) +ifneq ($(BOARD_USES_DOUBLE_TAP),) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/sensors_class.ko +LOCAL_REQUIRED_MODULES := sensors_class.ko +endif +LOCAL_MODULE := ilitek_v3_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/mmi_info.ko +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +endif +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/ilitek_v3_mmi/Kbuild b/drivers/input/touchscreen/ilitek_v3_mmi/Kbuild new file mode 100644 index 000000000000..8b681f25514f --- /dev/null +++ b/drivers/input/touchscreen/ilitek_v3_mmi/Kbuild @@ -0,0 +1,70 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/ilitek_v3_mmi \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/ilitek_v3_mmi/firmware + +ifeq ($(filter m y, $(CONFIG_TOUCHSCREEN_ILITEK_CHIPSET)),) + obj-m += ilitek_v3_mmi.o +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_ILI_ENABLE_DOUBLE_TAP)),) + EXTRA_CFLAGS += -DILI_SENSOR_EN + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers +endif + +ifneq ($(filter m y, $(CONFIG_BOARD_USES_DOUBLE_TAP_CTRL)),) + EXTRA_CFLAGS += -DILI_DOUBLE_TAP_CTRL + EXTRA_CFLAGS += -DILI_PRIMARY_NODE +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_ILI_SET_TOUCH_STATE)),) + EXTRA_CFLAGS += -DILI_SET_TOUCH_STATE +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_ILI_PANEL_NOTIFICATIONS)),) +EXTRA_CFLAGS += -DILI_CONFIG_PANEL_NOTIFICATIONS +endif +ifneq ($(filter m y,$(CONFIG_ILITEK_RESUME_BY_DDI)),) +EXTRA_CFLAGS += -DILI_CONFIG_RESUME_BY_DDI +endif +ifneq ($(filter m y,$(CONFIG_ILITEK_CHARGER)),) +EXTRA_CFLAGS += -DILI_CONFIG_MTK_CHARGER +endif +ifneq ($(filter m y,$(CONFIG_ILITEK_ESD)),) +EXTRA_CFLAGS += -DILI_CONFIG_ESD +endif + +ifneq ($(filter m y,$(CONFIG_ILITEK_MTK_CHECK_PANEL)),) + EXTRA_CFLAGS += -DILI_MTK_CHECK_PANEL +endif + +ifneq ($(filter m y,$(CONFIG_ILITEK_MULTI_TP_MODULE)),) + EXTRA_CFLAGS += -DILI_TP_MODULE_EN +endif + +ifneq ($(filter m y,$(CONFIG_ILI_TOUCH_LAST_TIME)),) + EXTRA_CFLAGS += -DILI_TOUCH_LAST_TIME +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI +endif + +ifeq ($(TARGET_BUILD_VARIANT),user) + EXTRA_CFLAGS += -DGKI_EN_FLAG +endif + +ifneq ($(filter m y, $(CONFIG_STOWED_MODE_EN)),) + EXTRA_CFLAGS += -DILI_STOWED_MODE_EN +endif +ilitek_v3_mmi-objs += ilitek_v3.o +ilitek_v3_mmi-objs += ilitek_v3_spi.o +ilitek_v3_mmi-objs += ilitek_v3_qcom.o +ilitek_v3_mmi-objs += ilitek_v3_ic.o +ilitek_v3_mmi-objs += ilitek_v3_touch.o +ilitek_v3_mmi-objs += ilitek_v3_mp.o +ilitek_v3_mmi-objs += ilitek_v3_hostdl.o +ilitek_v3_mmi-objs += ilitek_v3_node.o +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/ilitek_v3_mmi/Makefile b/drivers/input/touchscreen/ilitek_v3_mmi/Makefile new file mode 100644 index 000000000000..14bfd106b983 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_v3_mmi/Makefile @@ -0,0 +1,13 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + +KBUILD_EXTRA_SYMBOLS += $(M)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(M)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/ilitek_v3_mmi/firmware/FW_TDDI_TRUNK_FB.ili b/drivers/input/touchscreen/ilitek_v3_mmi/firmware/FW_TDDI_TRUNK_FB.ili new file mode 100644 index 000000000000..49ecb8dd561c --- /dev/null +++ b/drivers/input/touchscreen/ilitek_v3_mmi/firmware/FW_TDDI_TRUNK_FB.ili @@ -0,0 +1,8194 @@ +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x01,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x02,0x00,0x00, +0x1F,0x05,0x00,0x00,0x00,0x00,0xFF,0xFF,0x01,0x00,0x00,0x01,0x0F,0xFF,0x01,0x10,0x00,0x01,0x1F,0xFF,0x01,0xE0,0x00,0x01,0xFF,0xFF,0x01,0x20,0x00,0x01,0xAF,0xFF, +0x48,0x00,0x66,0xD4,0x48,0x00,0x65,0x14,0x48,0x00,0x65,0x32,0x48,0x00,0x65,0x50, +0x48,0x00,0x65,0x6E,0x48,0x00,0x65,0x8C,0x48,0x00,0x65,0xAA,0x48,0x00,0x65,0xC8, +0x48,0x00,0x65,0xE6,0x48,0x00,0x66,0x3C,0x48,0x00,0x66,0x3E,0x48,0x00,0x66,0x40, +0x48,0x00,0x66,0x42,0x48,0x00,0x66,0x44,0x48,0x00,0x66,0x46,0x48,0x00,0x66,0x48, +0x48,0x00,0x66,0x4A,0x48,0x00,0x66,0x4C,0x48,0x00,0x66,0x4E,0x48,0x00,0x66,0x50, +0x48,0x00,0x66,0x52,0x48,0x00,0x66,0x54,0x48,0x00,0x66,0x56,0x48,0x00,0x66,0x58, +0x48,0x00,0x66,0x5A,0x48,0x00,0x66,0x5C,0x48,0x00,0x66,0x5E,0x48,0x00,0x66,0x60, +0x48,0x00,0x66,0x62,0x48,0x00,0x66,0x64,0x48,0x00,0x66,0x66,0x48,0x00,0x66,0x68, +0x48,0x00,0x66,0x6A,0x48,0x00,0x66,0x6C,0x48,0x00,0x66,0x6E,0x48,0x00,0x66,0x70, +0x48,0x00,0x66,0x72,0x48,0x00,0x66,0x75,0x48,0x00,0x66,0x78,0x48,0x00,0x66,0x7B, +0x48,0x00,0x66,0x7E,0x86,0xCE,0x00,0x00,0x88,0xCE,0x00,0x00,0x9C,0xCE,0x00,0x00, +0xAE,0xCE,0x00,0x00,0xBC,0xCE,0x00,0x00,0xD4,0xCE,0x00,0x00,0xE2,0xCE,0x00,0x00, +0x1A,0xCF,0x00,0x00,0x22,0xCF,0x00,0x00,0x38,0xCF,0x00,0x00,0x5C,0xCF,0x00,0x00, +0xA6,0xCF,0x00,0x00,0xA8,0xCF,0x00,0x00,0xB0,0xCF,0x00,0x00,0xB8,0xCF,0x00,0x00, +0xC0,0xCF,0x00,0x00,0xFA,0xCF,0x00,0x00,0xFC,0xD0,0x00,0x00,0x90,0xD1,0x00,0x00, +0xA6,0xD1,0x00,0x00,0xCE,0xD2,0x00,0x00,0xE6,0xD3,0x00,0x00,0x52,0xD4,0x00,0x00, +0x54,0xD4,0x00,0x00,0x56,0xD4,0x00,0x00,0x58,0xD4,0x00,0x00,0x5A,0xD4,0x00,0x00, +0x5C,0xD4,0x00,0x00,0x5E,0xD4,0x00,0x00,0x60,0xD4,0x00,0x00,0x62,0xD4,0x00,0x00, +0x64,0xD4,0x00,0x00,0xE6,0x28,0xE8,0x4C,0x96,0x06,0x46,0x20,0x00,0x81,0x44,0xF0, +0x01,0x3C,0x38,0x17,0x84,0x00,0x40,0xF0,0xBC,0x00,0xDD,0x0F,0x08,0x16,0x24,0x32, +0x40,0x4E,0x5C,0x6A,0x40,0x00,0x40,0x08,0x04,0x31,0x00,0x29,0x44,0x1E,0xFF,0xFF, +0xD5,0x33,0x40,0x00,0x44,0x08,0x04,0x31,0x00,0x29,0x44,0x1D,0xFF,0xFF,0xD5,0x2C, +0x40,0x00,0x48,0x08,0x04,0x31,0x00,0x29,0x44,0x1B,0xFF,0xFF,0xD5,0x25,0x40,0x00, +0x4C,0x08,0x04,0x31,0x00,0x29,0x46,0x1F,0xFF,0x7F,0xD5,0x1C,0x40,0x00,0x50,0x08, +0x04,0x31,0x00,0x29,0x46,0x1F,0xFE,0xFF,0xD5,0x15,0x40,0x00,0x54,0x08,0x04,0x31, +0x00,0x29,0x46,0x1F,0xFD,0xFF,0xD5,0x0E,0x40,0x00,0x58,0x08,0x04,0x31,0x00,0x29, +0x46,0x1F,0xFB,0xFF,0xD5,0x07,0x40,0x00,0x5C,0x08,0x04,0x31,0x00,0x29,0x46,0x1F, +0xF7,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x29,0xDD,0x9E, +0x44,0x24,0x80,0x90,0x96,0x06,0xB4,0x62,0x40,0x10,0x20,0x08,0x44,0x0F,0xFE,0xFF, +0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x46,0x10,0x00,0x82,0xA0,0x0A,0x58,0x00, +0x00,0x01,0xA8,0x0A,0xDD,0x9E,0x46,0x20,0x00,0x82,0x96,0x06,0xA0,0xD5,0x40,0x10, +0x40,0x08,0x44,0x0E,0xFF,0xFF,0xFE,0x1E,0xFE,0x0F,0xA8,0x15,0xDD,0x9E,0x46,0x00, +0x00,0x82,0xA0,0x05,0x92,0x10,0x96,0x06,0xDD,0x9E,0xFC,0x40,0x46,0x60,0x00,0x81, +0x96,0x2F,0x40,0x90,0x20,0x08,0x04,0x03,0x00,0x2A,0x44,0x7F,0xC0,0xFF,0xFE,0x3E, +0x40,0x00,0x24,0x04,0x14,0x03,0x00,0x2A,0x96,0x6F,0x40,0x90,0xC0,0x08,0x46,0x0F, +0xFC,0x0F,0x04,0x13,0x00,0x2A,0x50,0x00,0x0F,0xFF,0xFE,0x46,0x40,0x10,0xA4,0x04, +0x14,0x13,0x00,0x2A,0x96,0xAF,0x04,0x93,0x00,0x2A,0x40,0x11,0x60,0x08,0x46,0x2C, +0x0F,0xFF,0x50,0x21,0x0F,0xFF,0x40,0x24,0x88,0x02,0xFE,0x8F,0x14,0x23,0x00,0x2A, +0x04,0x13,0x00,0x2B,0x96,0xEF,0x92,0x26,0x94,0x4E,0xFE,0x5F,0x14,0x13,0x00,0x2B, +0x97,0x2F,0x40,0x12,0x20,0x08,0x04,0x43,0x00,0x2B,0x97,0x6F,0xFF,0x3E,0xFF,0x0F, +0x14,0x43,0x00,0x2B,0x04,0x13,0x00,0x2B,0x40,0x52,0xC0,0x08,0xFE,0x0E,0xFE,0x2F, +0x14,0x03,0x00,0x2B,0x49,0x00,0x04,0x80,0x00,0x0F,0x80,0x20,0x54,0x10,0x00,0x3F, +0x04,0x03,0x03,0x98,0x92,0x06,0x94,0x06,0xFE,0x0F,0x14,0x03,0x03,0x98,0x00,0x0F, +0x80,0x24,0x04,0x23,0x03,0x99,0x96,0x2F,0x94,0x46,0x44,0x0F,0xF0,0x3F,0xFE,0x16, +0xFE,0x0F,0x14,0x03,0x03,0x99,0x49,0x00,0x04,0x60,0xFC,0xC0,0x46,0x30,0x00,0x82, +0x44,0x41,0x00,0x00,0xB4,0xA3,0x54,0x10,0x87,0xFF,0xFF,0x2F,0xB6,0x83,0xB4,0x83, +0x94,0x92,0x92,0x8B,0x40,0x42,0x2C,0x08,0xFF,0x0F,0xB6,0x83,0x88,0x40,0x4C,0x01, +0x00,0x05,0xA0,0x59,0xAA,0x41,0xD5,0xFC,0xB4,0x23,0x44,0x0E,0xFF,0xFF,0xFE,0x0E, +0xB6,0x03,0xDD,0x9E,0x46,0x30,0x00,0x82,0x44,0x41,0x00,0x00,0xB4,0xA3,0x54,0x10, +0x87,0xFF,0xFF,0x2F,0xB6,0x83,0xB4,0x83,0x94,0x92,0x92,0x8B,0x40,0x42,0x2C,0x08, +0xFF,0x0F,0xB6,0x83,0x88,0x40,0x80,0x23,0x4C,0x01,0x00,0x05,0xA3,0x01,0xA9,0x09, +0xD5,0xFC,0xB4,0x23,0x44,0x0E,0xFF,0xFF,0xFE,0x0E,0xB6,0x03,0xDD,0x9E,0xFC,0x00, +0x46,0x50,0x00,0x80,0x46,0x3F,0xFF,0x00,0x9D,0x99,0x9D,0x2E,0x94,0xA9,0x88,0x40, +0x38,0x11,0x18,0x00,0x38,0x21,0x0C,0x00,0x94,0x4C,0xFE,0x57,0x96,0x48,0x18,0x12, +0x80,0x01,0xDC,0xF5,0xFC,0x80,0xFC,0x00,0x46,0x10,0x00,0x80,0x46,0x2F,0xFE,0xFF, +0x50,0x50,0x80,0x08,0x50,0x61,0x0F,0xF1,0x50,0x41,0x0F,0xF0,0x50,0x30,0x80,0x0E, +0x94,0xA9,0x88,0x40,0x38,0x11,0x18,0x00,0x38,0x21,0x10,0x00,0x94,0x4C,0xFE,0x57, +0x96,0x48,0x18,0x12,0x80,0x01,0xDB,0xF5,0xFC,0x80,0x46,0x20,0x00,0x80,0x84,0x7E, +0x50,0x51,0x01,0xC4,0xB4,0x85,0x96,0x06,0xFE,0xE6,0xFE,0x1F,0xB6,0x05,0x96,0x57, +0xB4,0x65,0x40,0x00,0xA0,0x08,0x44,0x1F,0xF8,0xFF,0xFE,0x5E,0xFE,0x47,0xB6,0x25, +0xDD,0x9E,0x46,0x20,0x00,0x81,0x40,0x00,0x40,0x08,0x04,0x11,0x00,0x11,0x96,0x49, +0xFE,0x0F,0x14,0x01,0x00,0x11,0xDD,0x9E,0x46,0x20,0x00,0x81,0x40,0x00,0x40,0x08, +0x04,0x11,0x00,0x10,0x96,0x49,0xFE,0x0F,0x14,0x01,0x00,0x10,0xDD,0x9E,0x46,0x20, +0x00,0x81,0x04,0x11,0x00,0x10,0x92,0x30,0x40,0x10,0xC0,0x08,0xFE,0x0F,0x14,0x01, +0x00,0x10,0xDD,0x9E,0x46,0x20,0x00,0x81,0x04,0x11,0x00,0x11,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x0F,0x14,0x01,0x00,0x11,0xDD,0x9E,0x46,0x20,0x00,0x81,0x84,0x30, +0x04,0x31,0x00,0x12,0x96,0x1F,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E, +0x46,0x20,0x00,0x80,0x84,0x30,0x04,0x31,0x00,0x70,0x96,0x1F,0xFE,0x5E,0xFE,0x0F, +0x14,0x01,0x00,0x70,0xDD,0x9E,0x46,0x20,0x00,0x81,0x84,0x3E,0x04,0x31,0x00,0x13, +0x96,0x06,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x13,0xDD,0x9E,0x46,0x20,0x00,0x81, +0x96,0x06,0x04,0x31,0x00,0x12,0x40,0x10,0x20,0x08,0x44,0x0F,0xFE,0xFF,0xFE,0x1E, +0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E,0x46,0x20,0x00,0x81,0x96,0x0F,0x40,0x10, +0x48,0x08,0x04,0x31,0x00,0x12,0x46,0x0F,0xFF,0x3F,0x50,0x00,0x0F,0xFF,0xFE,0x1E, +0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E,0x46,0x00,0x00,0x81,0x04,0x00,0x00,0x12, +0x92,0x12,0x96,0x0F,0xDD,0x9E,0x44,0x14,0x80,0x10,0xB4,0x01,0x92,0x0D,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x11,0xFA,0x50,0xAE,0x80,0xB4,0x01,0x92,0x0E,0x96,0x06, +0xC0,0x06,0x44,0x04,0x80,0x11,0x44,0x10,0x00,0x40,0xAE,0x40,0x44,0x14,0x80,0x10, +0xB4,0x01,0x92,0x0F,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x11,0x44,0x2F,0xFF,0x80, +0xAE,0x80,0xB4,0x01,0x92,0x09,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x11,0x84,0x22, +0xAE,0x40,0x44,0x14,0x80,0x10,0xB4,0x01,0x92,0x0C,0x96,0x06,0xC0,0x05,0x44,0x04, +0x80,0x11,0xFA,0x40,0xAE,0x80,0xB4,0x01,0x92,0x17,0x96,0x06,0xC0,0x06,0x44,0x04, +0x80,0x12,0x44,0x1F,0xFF,0x80,0xAE,0x40,0xDD,0x9E,0x00,0x00,0x46,0x10,0x00,0x81, +0x04,0x30,0x80,0x39,0xC0,0x0B,0x5A,0x00,0x01,0x0D,0x5A,0x00,0x02,0x0E,0x84,0x40, +0x5A,0x08,0x03,0x0D,0x46,0x23,0xF3,0xC0,0xD5,0x09,0x46,0x23,0x00,0x00,0xD5,0x06, +0x46,0x23,0x51,0x40,0xD5,0x03,0x46,0x23,0xA2,0x80,0x46,0x1C,0x0C,0x3F,0x50,0x10, +0x8F,0xFF,0x40,0x01,0x84,0x02,0x46,0x10,0x00,0x81,0xFE,0x17,0x14,0x00,0x80,0x39, +0xDD,0x9E,0x46,0x20,0x00,0x81,0x96,0x0F,0x40,0x10,0x58,0x08,0x04,0x31,0x00,0x39, +0x46,0x0F,0xF3,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x39, +0xDD,0x9E,0x46,0x10,0x00,0x81,0x04,0x30,0x80,0x39,0xC0,0x11,0x5A,0x00,0x01,0x09, +0x5A,0x00,0x02,0x0A,0x5A,0x08,0x03,0x0B,0x44,0x03,0xFF,0xC0,0xD5,0x08,0x44,0x01, +0x55,0x40,0xD5,0x05,0x44,0x02,0xAA,0x80,0xD5,0x02,0x84,0x00,0x44,0x23,0xFF,0xC0, +0x44,0x1C,0x00,0x3F,0xFE,0x5E,0xFE,0x16,0xFE,0x0F,0x46,0x10,0x00,0x81,0x14,0x00, +0x80,0x39,0xDD,0x9E,0x46,0x20,0x00,0x81,0x96,0x0F,0x04,0x31,0x00,0x39,0x40,0x10, +0x28,0x08,0x44,0x0F,0xF3,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x39,0xDD,0x9E, +0x46,0x20,0x00,0x81,0x46,0x31,0x55,0x10,0x50,0x31,0x84,0x40,0x50,0x51,0x00,0xE4, +0xB4,0x25,0xA8,0xE9,0xA1,0x2A,0x84,0x78,0xFE,0xE6,0xE6,0x05,0xA8,0xEA,0xE8,0x3A, +0x44,0xF0,0x06,0x10,0x38,0x07,0x80,0x00,0x40,0xF0,0x3C,0x00,0x4A,0x00,0x3C,0x00, +0x06,0x1C,0x28,0x3E,0x54,0x00,0x46,0x03,0xA3,0x2A,0x92,0x26,0x50,0x00,0x03,0x00, +0x94,0x4E,0x14,0x01,0x00,0x38,0x58,0x10,0x80,0x3A,0xD5,0x22,0x84,0x00,0x92,0x26, +0x14,0x01,0x00,0x38,0x94,0x4E,0xD5,0x1C,0x46,0x05,0x55,0x55,0x92,0x26,0x50,0x00, +0x05,0x55,0x94,0x4E,0x14,0x01,0x00,0x38,0x58,0x10,0x80,0x15,0xD5,0x11,0x46,0x0A, +0xAA,0xAA,0x92,0x26,0x50,0x00,0x0A,0xAA,0x94,0x4E,0x14,0x01,0x00,0x38,0x58,0x10, +0x80,0x2A,0xD5,0x06,0x84,0x1F,0x14,0x01,0x00,0x38,0x58,0x10,0x80,0x3F,0x14,0x11, +0x00,0x39,0xDD,0x9E,0xDD,0x9E,0x44,0x24,0x50,0x00,0x96,0x06,0xB4,0x62,0x94,0x42, +0x84,0x1B,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x04,0x50,0x28,0xB4,0x00, +0x92,0x04,0x96,0x06,0xDD,0x9E,0x92,0x00,0x46,0x20,0x00,0x83,0x84,0x3C,0x04,0x31, +0x00,0x10,0x96,0x0F,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x10,0xDD,0x9E,0x46,0x20, +0x00,0x83,0x96,0x06,0x04,0x31,0x00,0x10,0x40,0x10,0x20,0x08,0x44,0x0F,0xFE,0xFF, +0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x10,0xDD,0x9E,0xFC,0x40,0x46,0x20,0x00,0x83, +0x44,0x5F,0xFE,0xFF,0x83,0x82,0xBB,0x18,0x46,0x40,0x0F,0x03,0xFE,0xEE,0xBB,0x98, +0xBB,0x18,0x50,0x42,0x0C,0x0F,0x92,0x68,0x40,0x31,0xA0,0x08,0x58,0x31,0x80,0x46, +0xBB,0x98,0xBF,0x39,0xBE,0x3A,0x04,0x91,0x00,0x3B,0xFF,0xE6,0xFF,0xA6,0x40,0x44, +0x90,0x02,0x04,0x90,0x80,0x02,0xBB,0x3C,0xB5,0x41,0x89,0x24,0xA1,0x0B,0x96,0xDF, +0x88,0xEA,0x04,0xA0,0x80,0x01,0x98,0x5C,0xBB,0x18,0x88,0xCA,0x58,0x31,0x81,0x00, +0xBB,0x98,0xBB,0x18,0x92,0x68,0x40,0x31,0xA0,0x08,0x58,0x31,0x80,0x46,0xBB,0x98, +0xBF,0xB9,0xBE,0xBA,0x14,0x91,0x00,0x3B,0x96,0xC9,0xB9,0x3C,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x5F,0xB9,0xBC,0xB4,0x20,0xB9,0xB9,0xA0,0x41,0xB9,0xBA,0xA0,0x42, +0xB9,0xBB,0xA4,0x46,0xB8,0x3C,0x92,0x10,0x40,0x00,0x40,0x08,0xFE,0x0F,0xB8,0xBC, +0xB8,0x18,0xFF,0x46,0xBD,0x98,0xFC,0xC0,0xFC,0x20,0x46,0x20,0x00,0x83,0x44,0x3F, +0xFE,0xFF,0x83,0x82,0xBC,0x18,0x94,0x4A,0xFE,0xE6,0xBB,0x98,0xBB,0x18,0x50,0x51, +0x00,0xE4,0x92,0x68,0x40,0x31,0xA0,0x08,0x58,0x31,0x80,0x48,0xBB,0x98,0xBB,0x18, +0x50,0x61,0x00,0xE8,0x58,0x31,0x81,0x00,0xBB,0x98,0x50,0x71,0x00,0xEC,0x88,0x20, +0x50,0x21,0x00,0xF0,0x46,0x30,0x00,0x83,0x4C,0x00,0x80,0x12,0xA2,0xC1,0xB6,0x65, +0x83,0x80,0xBB,0x3F,0x02,0x40,0x01,0x7E,0xB6,0x66,0xBB,0x7F,0xB6,0x67,0xB4,0x62, +0x92,0x70,0x40,0x31,0xC0,0x08,0xFE,0xE7,0xB6,0x62,0xD5,0xED,0x83,0x83,0xB9,0x18, +0x44,0x0F,0xFE,0xFF,0xFE,0x0E,0xB8,0x98,0xFC,0xA0,0xFC,0x20,0x46,0x10,0x00,0x83, +0x44,0x2F,0xFE,0xFF,0x83,0x81,0xBB,0x18,0x46,0x40,0xF0,0x3C,0xFE,0x9E,0xBA,0x98, +0xBA,0x18,0x50,0x32,0x00,0xF0,0x92,0x48,0x40,0x21,0x20,0x08,0x58,0x21,0x00,0x46, +0xBA,0x98,0xBE,0x39,0xB4,0x80,0xBD,0x3A,0xFF,0x9E,0x88,0xC4,0xA1,0x01,0xBF,0x3B, +0xFF,0x5E,0x88,0xA4,0xA1,0x02,0xBA,0x3C,0xFE,0xFE,0x88,0x83,0xA0,0xC3,0x54,0x21, +0x00,0xF0,0xBE,0xB9,0x98,0x13,0xBD,0xBA,0xBC,0xBB,0x96,0x81,0xB8,0x3C,0x92,0x10, +0x40,0x00,0x40,0x08,0xFE,0x17,0xB8,0xBC,0xFC,0xA0,0x44,0x14,0x80,0x90,0x84,0x5E, +0xB4,0x61,0x96,0x06,0xFE,0x9E,0xFE,0x87,0xB6,0x41,0xB4,0x81,0x44,0x2F,0xFF,0xEF, +0x94,0xC4,0xFE,0xA6,0xFE,0x9F,0xB6,0x41,0xB4,0x61,0x44,0x2F,0xFB,0xFF,0xFE,0x9E, +0x40,0x00,0x28,0x08,0xFE,0x17,0xB6,0x01,0x46,0x10,0x00,0x80,0x84,0x18,0x50,0x30, +0x82,0x6C,0xB4,0x43,0xFE,0x16,0xB6,0x03,0xB4,0x43,0x44,0x0F,0xF0,0xFF,0xFE,0x16, +0x58,0x00,0x0A,0x00,0xB6,0x03,0xDD,0x9E,0x52,0x00,0x00,0x01,0x46,0x20,0x00,0x80, +0x96,0x06,0xC9,0x08,0x40,0x10,0x20,0x08,0x04,0x31,0x00,0x79,0x44,0x0F,0xFE,0xFF, +0xD5,0x07,0x40,0x10,0x40,0x08,0x04,0x31,0x00,0x79,0x44,0x0E,0xFF,0xFF,0xFE,0x1E, +0xFE,0x0F,0x14,0x01,0x00,0x79,0xDD,0x9E,0x46,0x10,0x00,0x82,0x5A,0x08,0x01,0x0B, +0x50,0x30,0x80,0x20,0xB4,0x03,0x58,0x00,0x00,0x02,0xB6,0x03,0xB4,0x43,0x84,0x1E, +0xD5,0x04,0x04,0x20,0x80,0x08,0x84,0x1D,0xFE,0x16,0x14,0x00,0x80,0x08,0xDD,0x9E, +0x46,0x30,0x00,0x83,0x54,0x10,0x00,0x01,0x50,0x51,0x83,0xB8,0xB4,0x85,0x40,0x20, +0xA0,0x08,0x44,0x1F,0xFE,0xFF,0xFE,0x66,0xFE,0x57,0xB6,0x25,0xB4,0x25,0xE6,0x01, +0x44,0x0F,0xFD,0xFF,0x40,0xF7,0xA4,0x08,0xFE,0x0E,0x40,0x00,0x3C,0x04,0xB6,0x05, +0xDD,0x9E,0x46,0x30,0x00,0x83,0x54,0x10,0x00,0x01,0x50,0x51,0x83,0xB8,0xB4,0x85, +0x40,0x20,0xA8,0x08,0x44,0x1F,0xFB,0xFF,0xFE,0x66,0xFE,0x57,0xB6,0x25,0xB4,0x25, +0xE6,0x01,0x44,0x0F,0xF7,0xFF,0x40,0xF7,0xAC,0x08,0xFE,0x0E,0x40,0x00,0x3C,0x04, +0xB6,0x05,0xDD,0x9E,0x44,0x27,0x20,0x68,0x96,0x01,0xB4,0x22,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x70,0xB4,0x22,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x78,0xB4,0x22,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x80,0xB4,0x22,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x88,0xB4,0x22,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x90,0xB4,0x22,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0xC8,0x0A,0x44,0x17,0x20,0x00,0x46,0x0F, +0xEF,0xFF,0xB4,0x41,0x50,0x00,0x0F,0xFF,0xFE,0x16,0xD5,0x09,0x5A,0x08,0x01,0x09, +0x44,0x17,0x20,0x00,0x46,0x00,0x10,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0xDD,0x9E, +0x44,0x27,0x20,0x00,0x84,0x3E,0xB4,0x62,0x96,0x06,0xFE,0x5E,0xFE,0x0F,0xB6,0x02, +0xDD,0x9E,0x44,0x27,0x20,0x04,0x84,0x3E,0xB4,0x62,0x96,0x06,0xFE,0x5E,0xFE,0x0F, +0xB6,0x02,0xDD,0x9E,0x44,0x27,0x20,0x00,0x96,0x06,0xB4,0x62,0x40,0x10,0x40,0x08, +0x44,0x0E,0xFF,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0xC9,0x06,0x44,0x27, +0x10,0x00,0xB4,0x22,0xFE,0x0F,0xD5,0x08,0x5A,0x18,0x01,0x08,0x44,0x27,0x10,0x00, +0xFE,0x03,0xB4,0x22,0xFE,0x0E,0xB6,0x02,0xDD,0x9E,0x5A,0x18,0x01,0x07,0x44,0x27, +0x10,0x04,0xB4,0x22,0xFE,0x0F,0xD5,0x07,0xC9,0x07,0x44,0x27,0x10,0x04,0xFE,0x03, +0xB4,0x22,0xFE,0x0E,0xB6,0x02,0xDD,0x9E,0x5A,0x18,0x01,0x07,0x44,0x27,0x10,0x08, +0xB4,0x22,0xFE,0x0F,0xD5,0x07,0xC9,0x07,0x44,0x27,0x10,0x08,0xFE,0x03,0xB4,0x22, +0xFE,0x0E,0xB6,0x02,0xDD,0x9E,0x44,0x16,0x00,0x00,0x5A,0x08,0x01,0x06,0xB4,0x01, +0x58,0x00,0x00,0x01,0xD5,0x04,0xB4,0x41,0x84,0x1E,0xFE,0x16,0xB6,0x01,0xDD,0x9E, +0x44,0x16,0x00,0x00,0xB4,0x41,0x5A,0x08,0x01,0x06,0x44,0x01,0x00,0x00,0xFE,0x17, +0xD5,0x04,0x44,0x0E,0xFF,0xFF,0xFE,0x16,0xB6,0x01,0xDD,0x9E,0x44,0x06,0x00,0x00, +0xB4,0x00,0x92,0x1C,0x96,0x06,0xDD,0x9E,0x44,0x16,0x00,0x30,0x5A,0x08,0x01,0x06, +0xB4,0x01,0x58,0x00,0x01,0x00,0xD5,0x05,0xB4,0x41,0x44,0x0F,0xFE,0xFF,0xFE,0x16, +0xB6,0x01,0xDD,0x9E,0x44,0x36,0x38,0x14,0x40,0x00,0x20,0x08,0x40,0x20,0x20,0x09, +0xB4,0x03,0x96,0x6F,0x92,0x18,0x40,0x00,0x60,0x08,0xFE,0x17,0x44,0x26,0x38,0x08, +0xB6,0x03,0xB4,0x62,0x40,0x00,0xE0,0x08,0x46,0x1C,0x0F,0xFF,0x50,0x10,0x8F,0xFF, +0xFE,0x5E,0xFE,0x47,0xB6,0x22,0x44,0x16,0x38,0x04,0x44,0x01,0x00,0x00,0xB4,0x41, +0xFE,0x17,0xB6,0x01,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x03,0x96,0x06,0xC0,0xFD, +0x44,0x04,0x80,0x0C,0x84,0x28,0xAE,0x40,0xDD,0x9E,0x44,0x36,0x34,0x14,0x40,0x00, +0x20,0x08,0x40,0x20,0x20,0x09,0xB4,0x03,0x96,0x6F,0x92,0x18,0x40,0x00,0x60,0x08, +0xFE,0x17,0x44,0x26,0x34,0x08,0xB6,0x03,0xB4,0x62,0x40,0x00,0xE0,0x08,0x46,0x1C, +0x0F,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x5E,0xFE,0x47,0xB6,0x22,0x44,0x16,0x34,0x04, +0x44,0x01,0x00,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0x44,0x14,0x80,0x0C,0xB4,0x01, +0x92,0x13,0x96,0x06,0xC0,0xFD,0x44,0x04,0x80,0x0E,0x84,0x28,0xAE,0x40,0xDD,0x9E, +0xFC,0x01,0xB6,0x3F,0x80,0x22,0xF3,0x81,0x49,0xFF,0xFF,0xA6,0xB4,0x1F,0xF1,0x01, +0x49,0xFF,0xFF,0xCD,0xFC,0x81,0x44,0x16,0x3C,0x04,0xB4,0x01,0x58,0x00,0x00,0x01, +0xB6,0x01,0xDD,0x9E,0x44,0x26,0x38,0x04,0xB4,0x22,0x44,0x06,0x38,0x04,0x92,0x28, +0x96,0x46,0xC9,0xFB,0xB4,0x40,0x84,0x3E,0xFE,0x56,0xB6,0x20,0x80,0x20,0xB4,0x01, +0x92,0x08,0x96,0x06,0xC8,0xFD,0x44,0x26,0x34,0x04,0xB4,0x22,0x44,0x06,0x34,0x04, +0x92,0x28,0x96,0x46,0xC9,0xFB,0xB4,0x40,0x84,0x3E,0xFE,0x56,0xB6,0x20,0x80,0x20, +0xB4,0x01,0x92,0x08,0x96,0x06,0xC8,0xFD,0xDD,0x9E,0xFC,0x00,0x46,0x60,0x00,0x83, +0x83,0x86,0xB8,0x27,0x58,0x00,0x00,0x01,0xB8,0xA7,0xB8,0x27,0x58,0x00,0x01,0x00, +0xB8,0xA7,0x84,0x01,0x49,0x00,0x08,0x9B,0xB9,0x27,0x84,0x1E,0xFE,0x0E,0xB8,0xA7, +0xB9,0x27,0x44,0x0F,0xFE,0xFF,0xFE,0x0E,0xB8,0xA7,0xFC,0x80,0xFC,0x40,0x46,0x60, +0x00,0x83,0x84,0xFE,0x83,0x86,0x04,0x93,0x00,0x14,0x96,0x06,0x40,0x74,0x9C,0x02, +0xFE,0x3F,0xB8,0x94,0x96,0x5F,0xBF,0x14,0x40,0x00,0xE0,0x08,0x46,0x1F,0x0F,0xFF, +0x50,0x10,0x8F,0xFF,0xFE,0x7E,0xFE,0x47,0xB9,0x94,0x97,0x2F,0xB9,0x14,0x40,0x02, +0x44,0x08,0x46,0x4F,0xF8,0x1F,0x50,0x42,0x0F,0xFF,0xFF,0x0E,0xFF,0x07,0xBC,0x94, +0x97,0x6F,0x40,0x02,0xAC,0x08,0xBD,0x14,0x44,0x1E,0x07,0xFF,0xFF,0x4E,0xFF,0x47, +0xBD,0x94,0xB8,0x15,0x54,0x21,0x03,0xFF,0x92,0x0A,0x40,0x00,0x28,0x08,0xFE,0x17, +0xB8,0x95,0x96,0xF7,0x40,0x01,0xC0,0x08,0xB9,0x15,0x46,0x3F,0xF8,0x0F,0x50,0x31, +0x8F,0xFF,0xFE,0xCE,0xFE,0xC7,0xBB,0x95,0xFC,0xC0,0x46,0x00,0x00,0x83,0x04,0x00, +0x00,0x16,0x96,0x01,0xDD,0x9E,0xFC,0x00,0x46,0x40,0x00,0x83,0x84,0x61,0x83,0x84, +0xBE,0x13,0x84,0xBE,0xFE,0x1E,0xFF,0x76,0xFE,0x2F,0xB8,0x93,0xFE,0x5E,0x40,0x30, +0xA0,0x08,0xB9,0x60,0x44,0x0F,0xFE,0xFF,0xFE,0x46,0xFE,0x5F,0xB9,0xE0,0x96,0xA7, +0x40,0x01,0x20,0x08,0xBA,0x13,0x44,0x1F,0xE0,0xFF,0xFE,0x8E,0xFE,0x87,0xBA,0x93, +0xFC,0x80,0x46,0x30,0x00,0x83,0x96,0x8F,0x50,0x41,0x81,0x84,0xB6,0x04,0xA8,0x61, +0xA0,0x62,0x84,0x1C,0xFE,0x0E,0xFE,0x87,0xA8,0xA2,0xDD,0x9E,0x46,0x20,0x00,0x83, +0x96,0x27,0x04,0x11,0x00,0x60,0x92,0x25,0x94,0x4D,0xFE,0x47,0x14,0x11,0x00,0x60, +0xDD,0x9E,0x46,0x20,0x00,0x83,0x84,0x3E,0x04,0x31,0x00,0x12,0x96,0x06,0xFE,0x5E, +0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E,0xB4,0x40,0x46,0x10,0x00,0x80,0x14,0x20, +0x80,0x48,0xA0,0x01,0x14,0x00,0x80,0x4A,0xDD,0x9E,0xFC,0x40,0x46,0x20,0x00,0x83, +0x50,0x61,0x00,0xE4,0x83,0x82,0xBB,0x18,0x50,0x71,0x00,0xE8,0x58,0x31,0x81,0x00, +0xBB,0x98,0xBB,0x18,0x50,0x91,0x00,0xEC,0x92,0x68,0x40,0x31,0xA0,0x08,0x58,0x31, +0x80,0x88,0xBB,0x98,0x8C,0x02,0x84,0xA0,0x50,0x21,0x00,0xF0,0x46,0x30,0x00,0x83, +0xD1,0x22,0x22,0x30,0x00,0x00,0x22,0x40,0x7F,0xFF,0x40,0x31,0xC0,0x08,0x88,0x64, +0xB6,0x66,0x22,0x30,0x00,0x02,0x22,0x40,0x00,0x01,0x40,0x31,0xC0,0x08,0x88,0x64, +0xB6,0x67,0x22,0x30,0x00,0x04,0x22,0x40,0x00,0x03,0x40,0x31,0xC0,0x08,0x88,0x64, +0xB6,0x69,0xB4,0x62,0x8C,0xA1,0x92,0x70,0x40,0x31,0xC0,0x08,0xB6,0x62,0x97,0x68, +0x8C,0x0C,0xD5,0xDD,0x83,0x83,0xB9,0x18,0x44,0x0F,0xFE,0xFF,0xFE,0x0E,0xB8,0x98, +0xFC,0xC0,0x46,0x20,0x00,0x83,0x96,0x0F,0x04,0x31,0x00,0x12,0x40,0x10,0x40,0x08, +0x44,0x0C,0xFF,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E,0xFC,0x00, +0x84,0x40,0x44,0x64,0x00,0xC0,0x38,0x30,0x88,0x00,0x38,0x40,0x09,0x01,0x99,0x56, +0x40,0x31,0xB0,0x08,0x95,0x69,0xFE,0xE7,0x96,0xD9,0x8C,0x41,0xAC,0xE8,0x5A,0x28, +0x14,0xF4,0xFC,0x80,0x46,0x20,0x00,0x83,0x96,0x06,0x04,0x31,0x00,0x26,0x94,0x41, +0x84,0x1D,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x26,0xDD,0x9E,0x46,0x20,0x00,0x81, +0x96,0x06,0xA0,0xD5,0x40,0x10,0x20,0x08,0x44,0x0F,0xFE,0xFF,0xFE,0x1E,0xFE,0x0F, +0xA8,0x15,0xDD,0x9E,0xC9,0x08,0x46,0x20,0x00,0x91,0x84,0x3E,0xB4,0x62,0x96,0x06, +0xFE,0x5E,0xD5,0x0A,0x5A,0x18,0x01,0x0B,0x46,0x20,0x00,0x91,0x96,0x06,0xB4,0x62, +0x94,0x41,0x84,0x1D,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0xFC,0x00,0x44,0x00, +0x00,0xC8,0x49,0x00,0x07,0x6C,0x44,0x14,0x50,0x0C,0x46,0x60,0x00,0x81,0xB4,0x01, +0x58,0x00,0x00,0x04,0xB6,0x01,0xFA,0x18,0x49,0x00,0x07,0x6A,0x83,0x86,0xB8,0x1C, +0x58,0x00,0x01,0x00,0xB8,0x9C,0x84,0x01,0x49,0x00,0x07,0x62,0xB9,0x1C,0x44,0x01, +0x00,0x00,0xFE,0x0F,0xB8,0x9C,0x44,0x00,0x01,0xF4,0x49,0x00,0x07,0x50,0xB9,0x1C, +0x46,0x00,0x10,0x00,0xFE,0x0F,0xB8,0x9C,0x44,0x00,0x01,0xF4,0x49,0x00,0x07,0x47, +0xFC,0x80,0x00,0x00,0x46,0x10,0x00,0x80,0x50,0x50,0x80,0x80,0x50,0x10,0x80,0xB0, +0xA2,0x81,0xAA,0xA9,0xD9,0xFE,0xDD,0x9E,0x46,0x10,0x00,0x80,0x50,0x50,0x80,0xB0, +0x50,0x10,0x80,0xC4,0xA2,0x81,0xAA,0xA9,0xD9,0xFE,0xDD,0x9E,0xB4,0x40,0x46,0x10, +0x00,0x80,0x14,0x20,0x80,0x47,0xA0,0x01,0x14,0x00,0x80,0x49,0xDD,0x9E,0x46,0x10, +0x00,0x80,0x50,0x50,0x81,0x40,0x50,0x30,0x81,0x4C,0xA6,0x41,0xA6,0x80,0x94,0x4C, +0xFE,0x57,0x96,0x48,0xAE,0x68,0x00,0x10,0x00,0x0D,0x00,0x20,0x00,0x0C,0x94,0x4C, +0xFE,0x57,0x96,0x48,0x10,0x12,0x80,0x0C,0x8C,0xA1,0x8C,0x02,0xDB,0xEF,0xDD,0x9E, +0x46,0x10,0x00,0x80,0x50,0x50,0x80,0x40,0x50,0x30,0x80,0x46,0xA6,0x41,0xA6,0x80, +0x94,0x4C,0xFE,0x57,0x96,0x48,0xAE,0x68,0x00,0x10,0x00,0x0D,0x00,0x20,0x00,0x0C, +0x94,0x4C,0xFE,0x57,0x96,0x48,0x10,0x12,0x80,0x0C,0x8C,0xA1,0x8C,0x02,0xDB,0xEF, +0xDD,0x9E,0xA6,0x81,0x80,0xA0,0x94,0x94,0x08,0x12,0x80,0x05,0xFE,0x8F,0x46,0x10, +0x00,0x80,0x96,0x90,0x10,0x20,0x80,0x48,0xA6,0xA9,0xA6,0xC5,0x94,0x94,0xFE,0x9F, +0x96,0x90,0x10,0x20,0x80,0x54,0xA6,0x83,0xA6,0xC2,0x94,0x94,0xFE,0x9F,0x96,0x90, +0x10,0x20,0x80,0x49,0xA6,0xAB,0xA6,0xEA,0x94,0x94,0xFE,0x9F,0x96,0x90,0x10,0x20, +0x80,0x55,0xA6,0x84,0x84,0x0F,0xFE,0x86,0x40,0x31,0x40,0x08,0x46,0x4F,0xFF,0x0F, +0x04,0x20,0x80,0x12,0x50,0x42,0x0F,0xFF,0xFE,0xA6,0xFE,0x9F,0x14,0x20,0x80,0x12, +0xA6,0xAC,0xFE,0x16,0x40,0x20,0x40,0x08,0x04,0x00,0x80,0x15,0xFE,0x26,0xFE,0x17, +0x14,0x00,0x80,0x15,0xDD,0x9E,0x46,0x10,0x00,0x80,0x50,0x50,0x80,0x58,0x50,0x30, +0x80,0x5E,0xA6,0x41,0xA6,0x80,0x94,0x4C,0xFE,0x57,0x96,0x48,0xAE,0x68,0x00,0x10, +0x00,0x0D,0x00,0x20,0x00,0x0C,0x94,0x4C,0xFE,0x57,0x96,0x48,0x10,0x12,0x80,0x0C, +0x8C,0xA1,0x8C,0x02,0xDB,0xEF,0xDD,0x9E,0xA6,0x81,0x80,0xA0,0x94,0x94,0x08,0x12, +0x80,0x05,0xFE,0x8F,0x46,0x10,0x00,0x80,0x96,0x90,0x10,0x20,0x80,0x60,0xA6,0xA9, +0xA6,0xC5,0x94,0x94,0xFE,0x9F,0x96,0x90,0x10,0x20,0x80,0x6C,0xA6,0x83,0xA6,0xC2, +0x94,0x94,0xFE,0x9F,0x96,0x90,0x10,0x20,0x80,0x61,0xA6,0xAB,0xA6,0xEA,0x94,0x94, +0xFE,0x9F,0x96,0x90,0x10,0x20,0x80,0x6D,0xA6,0x84,0x84,0x0F,0xFE,0x86,0x40,0x31, +0x40,0x08,0x46,0x4F,0xFF,0x0F,0x04,0x20,0x80,0x18,0x50,0x42,0x0F,0xFF,0xFE,0xA6, +0xFE,0x9F,0x14,0x20,0x80,0x18,0xA6,0xAC,0xFE,0x16,0x40,0x20,0x40,0x08,0x04,0x00, +0x80,0x1B,0xFE,0x26,0xFE,0x17,0x14,0x00,0x80,0x1B,0xDD,0x9E,0xE6,0x06,0xE8,0x57, +0xFC,0x20,0x46,0x70,0x00,0x81,0x44,0xF0,0x10,0x74,0x38,0x07,0x80,0x00,0x40,0xF0, +0x3C,0x00,0xDD,0x0F,0x06,0x16,0x34,0x62,0x76,0x44,0x04,0x03,0x80,0x20,0x54,0x10, +0x87,0xFF,0x92,0x0B,0x40,0x00,0x2C,0x08,0xD5,0x0C,0x54,0x10,0x87,0xFF,0x40,0x00, +0xC0,0x08,0x04,0x23,0x80,0x20,0x46,0x1F,0x80,0x0F,0x50,0x10,0x8F,0xFF,0xFE,0x56, +0xFE,0x47,0x14,0x13,0x80,0x20,0xFC,0xA0,0x04,0x03,0x80,0x21,0x54,0x10,0x87,0xFF, +0x92,0x0B,0x40,0x00,0x2C,0x08,0xD5,0x0C,0x54,0x10,0x87,0xFF,0x40,0x00,0xC0,0x08, +0x04,0x23,0x80,0x21,0x46,0x1F,0x80,0x0F,0x50,0x10,0x8F,0xFF,0xFE,0x56,0xFE,0x47, +0x14,0x13,0x80,0x21,0xFC,0xA0,0x83,0x87,0xB8,0x22,0x54,0x10,0x87,0xFF,0x92,0x0B, +0x40,0x00,0x2C,0x08,0xFE,0x47,0xB9,0xA2,0xFC,0xA0,0x80,0xC1,0x49,0xFF,0xFD,0x54, +0x54,0x13,0x03,0xFF,0x95,0x8E,0x04,0x13,0x83,0x98,0x44,0x0F,0x00,0x3F,0xFE,0x46, +0xFE,0x77,0x14,0x13,0x83,0x98,0x49,0xFF,0xFD,0x40,0xFC,0xA0,0xDD,0x9E,0x46,0x20, +0x00,0x83,0x96,0x06,0x40,0x10,0x60,0x08,0x04,0x31,0x00,0xC0,0x46,0x0F,0xEF,0xFF, +0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0xC0,0xDD,0x9E,0x46,0x20, +0x00,0x83,0x96,0x06,0x40,0x10,0x64,0x08,0x04,0x31,0x00,0xC0,0x46,0x0F,0xDF,0xFF, +0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0xC0,0xDD,0x9E,0x46,0x20, +0x00,0x83,0x96,0x0F,0x04,0x31,0x00,0x0F,0x40,0x10,0x20,0x08,0x44,0x0F,0xFC,0xFF, +0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x0F,0xDD,0x9E,0x46,0x20,0x00,0x83,0x84,0x3E, +0x04,0x31,0x00,0x0F,0x96,0x06,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x0F,0xDD,0x9E, +0x46,0x20,0x00,0x83,0x96,0x06,0x04,0x31,0x00,0x0F,0x40,0x10,0x28,0x08,0x44,0x0F, +0xFB,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x0F,0xDD,0x9E,0x46,0x10,0x00,0x83, +0x44,0x01,0x00,0x00,0x04,0x20,0x80,0x0E,0xFE,0x17,0x14,0x00,0x80,0x0E,0xDD,0x9E, +0x46,0x20,0x00,0x83,0x46,0x3F,0x7F,0xFF,0x50,0x51,0x00,0x38,0xB4,0x85,0x50,0x31, +0x8F,0xFF,0xFE,0xE6,0xB6,0x65,0xB4,0x85,0x46,0x3E,0xFF,0xFF,0x50,0x31,0x8F,0xFF, +0xFE,0xE6,0xB6,0x65,0x5A,0x08,0x01,0x04,0x84,0x1F,0xD5,0x02,0x84,0x00,0x14,0x01, +0x00,0x0B,0x14,0x01,0x00,0x0C,0x04,0x01,0x00,0x33,0x5A,0x18,0x01,0x0B,0x58,0x00, +0x0F,0xFF,0x14,0x01,0x00,0x33,0x04,0x01,0x00,0x34,0x58,0x00,0x0F,0xFF,0xD5,0x0B, +0x92,0x0C,0x40,0x00,0x30,0x08,0x14,0x01,0x00,0x33,0x04,0x01,0x00,0x34,0x92,0x0C, +0x40,0x00,0x30,0x08,0x14,0x01,0x00,0x34,0xDD,0x9E,0x46,0x10,0x00,0x83,0x96,0x06, +0x50,0x50,0x80,0x38,0xB4,0x85,0x44,0x2F,0xFE,0xFF,0x40,0x30,0x20,0x08,0xFE,0xA6, +0xFE,0x9F,0xB6,0x45,0xB4,0x65,0x44,0x2F,0xFD,0xFF,0x40,0x00,0x24,0x08,0xFE,0x9E, +0xFE,0x17,0xB6,0x05,0xDD,0x9E,0x46,0x20,0x00,0x81,0x50,0x51,0x00,0x90,0x50,0x31, +0x00,0x9A,0x08,0x20,0x00,0x01,0x88,0x41,0x96,0x90,0x18,0x22,0x80,0x01,0xDB,0xFA, +0xDD,0x9E,0xE6,0x25,0xE8,0x33,0x96,0x1F,0x46,0x20,0x00,0x81,0x44,0xF0,0x12,0x7C, +0x38,0x17,0x84,0x00,0x40,0xF0,0xBC,0x00,0x4A,0x00,0x3C,0x00,0x06,0x0E,0x1A,0x28, +0x36,0x00,0x04,0x31,0x00,0x23,0x84,0x30,0xD5,0x1D,0x94,0x04,0x04,0x31,0x00,0x23, +0x44,0x1F,0xFF,0x0F,0xD5,0x17,0x40,0x00,0x20,0x08,0x04,0x31,0x00,0x23,0x44,0x1F, +0xF0,0xFF,0xD5,0x10,0x40,0x00,0x30,0x08,0x04,0x31,0x00,0x23,0x44,0x1F,0x0F,0xFF, +0xD5,0x09,0x46,0x1F,0xFF,0x0F,0x40,0x00,0x40,0x08,0x04,0x31,0x00,0x23,0x50,0x10, +0x8F,0xFF,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x23,0xDD,0x9E,0x84,0x00,0xDD,0x9E, +0x46,0x20,0x00,0x81,0x54,0x00,0x03,0xFF,0x40,0x10,0x40,0x08,0x04,0x31,0x00,0x49, +0x46,0x0F,0xC0,0x0F,0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x49, +0xDD,0x9E,0xE6,0x06,0xE8,0x52,0xFC,0x20,0x46,0x70,0x00,0x81,0x54,0x60,0x83,0xFF, +0x44,0xF0,0x13,0x10,0x38,0x07,0x80,0x00,0x40,0xF0,0x3C,0x00,0x4A,0x00,0x3C,0x00, +0x06,0x2E,0x46,0x56,0x66,0x16,0x83,0x87,0xB9,0x30,0x92,0x2A,0x40,0x10,0xA8,0x08, +0xFF,0x8F,0xBE,0xB0,0xFC,0xA0,0x83,0x87,0xB8,0x31,0x46,0x2F,0xC0,0x0F,0x50,0x21, +0x0F,0xFF,0x40,0x63,0x40,0x08,0xFE,0x16,0xFE,0x37,0xB8,0xB1,0xFC,0xA0,0x83,0x87, +0xB8,0x30,0x46,0x2F,0xC0,0x0F,0x50,0x21,0x0F,0xFF,0x40,0x63,0x40,0x08,0xFE,0x16, +0xFE,0x37,0xB8,0xB0,0xFC,0xA0,0x83,0x87,0xB9,0x31,0x92,0x2A,0x40,0x10,0xA8,0x08, +0xFF,0x8F,0xBE,0xB1,0xFC,0xA0,0x83,0x87,0xB9,0x32,0x92,0x2A,0x40,0x10,0xA8,0x08, +0xFF,0x8F,0xBE,0xB2,0xFC,0xA0,0x49,0xFF,0xFC,0x0F,0x40,0x13,0x40,0x08,0x04,0x03, +0x83,0x98,0x46,0x6F,0xC0,0x0F,0x50,0x63,0x0F,0xFF,0xFF,0x86,0xFF,0x8F,0x14,0x63, +0x83,0x98,0x49,0xFF,0xFB,0xFA,0xFC,0xA0,0xDD,0x9E,0x54,0x20,0x80,0x03,0xE6,0x08, +0x54,0x10,0x80,0x0C,0x90,0x22,0x4E,0xF2,0x00,0x93,0x96,0x4F,0x46,0x30,0x00,0x81, +0x96,0x8F,0x44,0xF0,0x13,0xC0,0x38,0x07,0x80,0x00,0x40,0xF0,0x3C,0x00,0xDD,0x0F, +0x08,0x24,0x40,0x5E,0x7C,0x9C,0xBC,0xE8,0x50,0x51,0x80,0x9C,0xB4,0x85,0x84,0x1C, +0xFE,0x26,0xFE,0x47,0xB6,0x25,0xB4,0x25,0x44,0x0C,0xFF,0xFF,0x40,0x21,0x40,0x08, +0xFE,0x0E,0xD5,0x72,0x50,0x51,0x80,0x9C,0xB4,0x85,0x84,0x13,0xFE,0x26,0x94,0x4A, +0xFE,0x47,0xB6,0x25,0x40,0x21,0x48,0x08,0xB4,0x05,0x46,0x1F,0xFF,0x3F,0xD5,0x4E, +0x50,0x51,0x80,0x9C,0xB4,0x85,0x44,0x0F,0xFF,0xCF,0xFE,0x26,0x94,0x4C,0xFE,0x47, +0xB6,0x25,0x40,0x21,0x50,0x08,0xB4,0x05,0x46,0x1F,0xFC,0xFF,0xD5,0x3F,0x50,0x51, +0x80,0x9C,0xB4,0x85,0x44,0x0F,0xFF,0x3F,0xFE,0x26,0x94,0x4E,0xFE,0x47,0xB6,0x25, +0x40,0x21,0x58,0x08,0xB4,0x05,0x46,0x1F,0xF3,0xFF,0xD5,0x30,0x50,0x51,0x80,0x9C, +0xB4,0x85,0x44,0x0F,0xFC,0xFF,0xFE,0x26,0x40,0x10,0xA0,0x08,0xFE,0x47,0xB6,0x25, +0x40,0x21,0x60,0x08,0xB4,0x05,0x46,0x1F,0xCF,0xFF,0xD5,0x20,0x50,0x51,0x80,0x9C, +0xB4,0x85,0x44,0x0F,0xF3,0xFF,0xFE,0x26,0x40,0x10,0xA8,0x08,0xFE,0x47,0xB6,0x25, +0x40,0x21,0x68,0x08,0xB4,0x05,0x46,0x1F,0x3F,0xFF,0xD5,0x10,0x50,0x51,0x80,0x9C, +0xB4,0x85,0x44,0x0F,0xCF,0xFF,0xFE,0x26,0x40,0x10,0xB0,0x08,0xFE,0x47,0xB6,0x25, +0x40,0x21,0x70,0x08,0xB4,0x05,0x46,0x1C,0xFF,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x0E, +0xFE,0x17,0x14,0x01,0x80,0x27,0xDD,0x9E,0x50,0x51,0x80,0x9C,0xB4,0x85,0x44,0x0F, +0x3F,0xFF,0xFE,0x26,0x40,0x10,0xB8,0x08,0xFE,0x47,0xB6,0x25,0xB4,0x05,0x40,0x21, +0x78,0x08,0x94,0x02,0x92,0x02,0xFE,0x87,0x14,0x21,0x80,0x27,0xDD,0x9E,0xDD,0x9E, +0xFC,0x40,0x80,0xE0,0x44,0x04,0x34,0x38,0x5A,0x78,0x01,0x2B,0x80,0xC1,0xB4,0x20, +0x44,0x9F,0xE0,0xFF,0x58,0x10,0x80,0x02,0xB6,0x20,0x80,0x27,0x49,0xFF,0xFB,0x17, +0x44,0x04,0x34,0x50,0x97,0xA7,0xB4,0x20,0x40,0x63,0x20,0x08,0x40,0x10,0xA4,0x02, +0xFE,0x77,0xB6,0x20,0x80,0x27,0x49,0xFF,0xFB,0x0A,0x44,0x04,0x38,0x3C,0xB4,0x20, +0x58,0x10,0x80,0x02,0xB6,0x20,0x80,0x27,0x49,0xFF,0xFA,0xD6,0x44,0x04,0x38,0x58, +0xB4,0x20,0x40,0x10,0xA4,0x02,0xFF,0x8F,0xB6,0xC0,0x80,0x27,0xD5,0x0E,0xB4,0x20, +0x84,0xDD,0xFE,0x76,0xB6,0x20,0x84,0x21,0x49,0xFF,0xFA,0xF1,0x44,0x04,0x38,0x3C, +0xB4,0x20,0xFF,0x8E,0xB6,0xC0,0x84,0x21,0x49,0xFF,0xFA,0xBE,0xFC,0xC0,0x92,0x00, +0xC8,0x04,0x44,0x07,0x20,0x0C,0xD5,0x03,0x44,0x07,0x20,0x10,0xB4,0x00,0x40,0x00, +0x30,0x08,0x92,0x0C,0xDD,0x9E,0xFC,0x00,0x80,0xC1,0x49,0xFF,0xFF,0xF3,0x84,0x40, +0x80,0x22,0xE2,0x46,0xE8,0x05,0x38,0x10,0x0A,0x0A,0x8C,0x41,0xD5,0xFB,0xFC,0x80, +0x44,0x27,0x20,0x1C,0x54,0x00,0x0F,0xFF,0xB4,0x22,0x92,0x2C,0x40,0x10,0xB0,0x08, +0xFE,0x47,0xB6,0x22,0xDD,0x9E,0x44,0x27,0x20,0x34,0x54,0x00,0x0F,0xFF,0xB4,0x22, +0x92,0x2C,0x40,0x10,0xB0,0x08,0xFE,0x47,0xB6,0x22,0xDD,0x9E,0x44,0x27,0x20,0x38, +0x54,0x00,0x0F,0xFF,0xB4,0x22,0x92,0x2C,0x40,0x10,0xB0,0x08,0xFE,0x47,0xB6,0x22, +0xDD,0x9E,0x44,0x27,0x20,0x54,0x84,0x30,0xB4,0x62,0x96,0x1F,0xFE,0x5E,0xFE,0x0F, +0xB6,0x02,0xDD,0x9E,0x44,0x27,0x20,0x54,0x96,0x1F,0xB4,0x62,0x40,0x10,0x20,0x08, +0x44,0x0F,0xF0,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x27,0x20,0x58, +0x96,0x1F,0x40,0x10,0x40,0x08,0xB4,0x62,0x46,0x0F,0xFF,0x0F,0x50,0x00,0x0F,0xFF, +0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x04,0x00,0x9C,0xA6,0x00,0xDD,0x9E, +0x46,0x10,0x00,0x81,0x04,0x00,0x80,0x6E,0x58,0x00,0x00,0x01,0x14,0x00,0x80,0x6E, +0xDD,0x9E,0xFC,0x00,0x44,0x04,0x2C,0x1C,0x84,0x41,0xB4,0x20,0x80,0x62,0x58,0x10, +0x81,0x00,0xB6,0x20,0xB4,0x20,0x58,0x10,0x80,0x80,0xB6,0x20,0x44,0x04,0x28,0x1C, +0x44,0x14,0x24,0x1C,0x49,0xFF,0xFA,0x96,0xFC,0x80,0x46,0x20,0x00,0x80,0x84,0x30, +0x50,0x41,0x01,0xD0,0xB4,0x64,0x96,0x1F,0xFE,0x5E,0xFE,0x47,0xB6,0x24,0xB4,0x64, +0x44,0x1F,0xFF,0x0F,0x94,0x04,0xFE,0x5E,0xFE,0x0F,0xB6,0x04,0xDD,0x9E,0x00,0x00, +0x44,0x24,0x80,0x88,0x84,0x3E,0xB4,0x62,0x96,0x06,0xFE,0x5E,0xFE,0x47,0xB6,0x22, +0xB4,0x62,0x84,0x3D,0x94,0x01,0xFE,0x5E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0xFC,0x00, +0x44,0x64,0x00,0x18,0x84,0x41,0xAE,0xB0,0xA6,0x72,0x84,0x1E,0xFE,0x46,0xAE,0x72, +0xA6,0x72,0x44,0x30,0x98,0x81,0x58,0x10,0x80,0x02,0xAE,0x72,0xA6,0x72,0x58,0x10, +0x80,0x04,0xAE,0x72,0xA6,0x73,0x58,0x10,0x80,0x02,0xAE,0x73,0xA6,0x73,0x58,0x10, +0x80,0x04,0xAE,0x73,0xA6,0x73,0x58,0x10,0x80,0x08,0xAE,0x73,0xA6,0x73,0x58,0x10, +0x80,0x10,0xAE,0x73,0x44,0x14,0x00,0x10,0xB6,0x61,0xAE,0xB1,0xA6,0x72,0x58,0x10, +0x80,0x10,0xAE,0x72,0xA6,0x72,0x54,0x10,0x80,0xDF,0xAE,0x72,0xA6,0xB2,0x44,0x1F, +0xFF,0x80,0xFE,0x57,0xAE,0x72,0xA6,0x73,0xFE,0x0E,0xAE,0x33,0xA6,0x33,0x58,0x00, +0x00,0x20,0xAE,0x33,0xA6,0x33,0x58,0x00,0x00,0x40,0xAE,0x33,0x49,0x00,0x03,0x0B, +0xE6,0x03,0x44,0x14,0x50,0x10,0xA6,0x32,0xE9,0x10,0x54,0x00,0x00,0xFB,0xAE,0x32, +0xB4,0x01,0x92,0x08,0x40,0x00,0x20,0x08,0xB6,0x01,0x44,0x14,0x70,0x00,0x44,0x0E, +0xFF,0xFF,0xB4,0x41,0xFE,0x16,0xD5,0x11,0x58,0x00,0x00,0x04,0xAE,0x32,0xB4,0x01, +0x92,0x08,0x40,0x00,0x20,0x08,0x58,0x00,0x00,0x98,0xB6,0x01,0x44,0x14,0x70,0x00, +0x44,0x01,0x00,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0xFC,0x80,0x44,0x37,0x20,0x44, +0x46,0x0F,0xC0,0x0F,0xB4,0x23,0x50,0x00,0x0F,0xFF,0xFE,0x46,0x46,0x20,0x24,0x00, +0xFE,0x57,0xB6,0x23,0x44,0x37,0x20,0x48,0x44,0x23,0x00,0x00,0xB4,0x23,0xFE,0x46, +0xFE,0x57,0x44,0x27,0x20,0x4C,0xB6,0x23,0xB4,0x22,0x46,0x30,0x0C,0x00,0x92,0x2C, +0x40,0x10,0xB0,0x08,0x58,0x10,0x82,0x52,0xB6,0x22,0xB4,0x22,0xFE,0x46,0xFE,0x5F, +0xB6,0x22,0x44,0x27,0x20,0x50,0x46,0x30,0x00,0x90,0xB4,0x22,0x92,0x2C,0x40,0x10, +0xB0,0x08,0x58,0x10,0x82,0x40,0xB6,0x22,0xB4,0x22,0xFE,0x0E,0xB6,0x02,0x44,0x27, +0x20,0x04,0x46,0x0F,0xFF,0x0F,0xB4,0x22,0x50,0x00,0x0F,0xFF,0xFE,0x46,0xFE,0x5F, +0xB6,0x22,0xB4,0x62,0x46,0x1F,0xF0,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x5E,0x46,0x30, +0x03,0x00,0xFE,0x5F,0xB6,0x22,0x46,0x20,0x00,0x80,0x44,0x1F,0xF0,0xFF,0x50,0x41, +0x01,0xC0,0xB4,0x64,0xFE,0x5E,0x58,0x10,0x88,0x00,0xB6,0x24,0xB4,0x24,0xFE,0x0E, +0x44,0x12,0x00,0x00,0xFE,0x0F,0xB6,0x04,0xDD,0x9E,0x46,0x20,0x00,0x91,0x40,0x00, +0x40,0x08,0xA0,0x56,0x96,0x49,0xFE,0x0F,0xA8,0x16,0xDD,0x9E,0xE6,0x05,0xE8,0x31, +0x46,0x10,0x00,0x91,0x44,0xF0,0x18,0x34,0x38,0x07,0x80,0x00,0x40,0xF0,0x3C,0x00, +0x4A,0x00,0x3C,0x00,0x06,0x14,0x22,0x30,0x3E,0x00,0xA0,0x0A,0x92,0x10,0x40,0x00, +0x40,0x08,0x58,0x00,0x00,0x0A,0xD5,0x1C,0xA0,0x0A,0x92,0x10,0x40,0x00,0x40,0x08, +0x58,0x00,0x00,0x23,0xD5,0x15,0xA0,0x0A,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00, +0x00,0x46,0xD5,0x0E,0xA0,0x0A,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00,0x00,0x96, +0xD5,0x07,0xA0,0x0A,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00,0x01,0xF4,0xA8,0x0A, +0xDD,0x9E,0x46,0x2F,0xFC,0x0F,0x46,0x10,0x00,0x91,0x50,0x21,0x0F,0xFF,0x5A,0x08, +0x01,0x08,0x04,0x00,0x80,0x0E,0xFE,0x86,0x44,0x02,0x00,0x00,0xD5,0x06,0x04,0x00, +0x80,0x0E,0xFE,0x86,0x44,0x01,0x00,0x00,0xFE,0x87,0x14,0x20,0x80,0x0E,0xA0,0x09, +0x92,0x06,0x94,0x06,0x58,0x00,0x00,0x01,0xA8,0x09,0xDD,0x9E,0xFC,0x00,0x46,0x60, +0x00,0x91,0x84,0x01,0xA8,0x31,0x44,0x00,0x01,0xF4,0xA8,0x32,0x83,0x86,0xB9,0x0A, +0x46,0x0F,0xC0,0x0F,0x50,0x00,0x0C,0x00,0xFE,0x46,0x58,0x10,0x80,0x01,0xB9,0x8A, +0xB9,0x0B,0xFE,0x0E,0x58,0x00,0x02,0x40,0xB8,0x8B,0xB9,0x0C,0x44,0x0F,0xC0,0xC0, +0xFE,0x0E,0x58,0x00,0x20,0x01,0xB8,0x8C,0xB9,0x0D,0x46,0x0E,0x0E,0x0E,0x50,0x00, +0x00,0xE0,0xFE,0x0E,0x46,0x10,0x60,0x11,0x50,0x10,0x82,0x01,0xFE,0x0F,0xB8,0x8D, +0xB9,0x0E,0x46,0x0F,0xFC,0x0C,0x50,0x00,0x00,0xF3,0xFE,0x0E,0x44,0x11,0x01,0x0C, +0xFE,0x0F,0xB8,0x8E,0x49,0xFF,0xFE,0x72,0xE6,0x03,0xA0,0x36,0x92,0x10,0x40,0x00, +0x40,0x08,0xE9,0x1F,0x58,0x00,0x00,0x28,0xA8,0x36,0x83,0x86,0xB8,0x08,0x92,0x10, +0x40,0x00,0x40,0x08,0x58,0x00,0x00,0x28,0xB8,0x88,0xA0,0x35,0x92,0x10,0x40,0x00, +0x40,0x08,0x58,0x00,0x00,0x28,0xA8,0x35,0xA0,0x37,0x92,0x10,0x40,0x00,0x40,0x08, +0x58,0x00,0x00,0x28,0xA8,0x37,0xA0,0x76,0x46,0x00,0x02,0x80,0x96,0x49,0xD5,0x1E, +0x58,0x00,0x46,0x00,0xA8,0x36,0x83,0x86,0xB8,0x08,0x92,0x10,0x40,0x00,0x40,0x08, +0x58,0x00,0x46,0x00,0xB8,0x88,0xA0,0x35,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00, +0x46,0x00,0xA8,0x35,0xA0,0x37,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00,0x46,0x00, +0xA8,0x37,0xA0,0x76,0x46,0x04,0x60,0x00,0x96,0x49,0xFE,0x47,0xA8,0x76,0x83,0x86, +0xB9,0x08,0x96,0x49,0xFE,0x47,0xB9,0x88,0xA0,0x75,0x96,0x49,0xFE,0x47,0xA8,0x75, +0xA0,0x77,0x96,0x49,0xFE,0x0F,0xA8,0x37,0x46,0x00,0x00,0x91,0x83,0x80,0xB9,0x12, +0x58,0x10,0xBF,0x3F,0xB9,0x92,0xBA,0x13,0x44,0x1E,0xE0,0x80,0xFE,0x56,0x44,0x21, +0x00,0x48,0xFE,0x57,0xB9,0x93,0xFC,0x80,0x46,0x00,0x00,0x91,0x50,0x30,0x00,0x44, +0xB4,0x23,0x58,0x10,0x80,0x01,0xB6,0x23,0xB4,0x43,0x84,0x3E,0xFE,0x56,0xB6,0x23, +0xDD,0x9E,0x92,0x00,0x44,0x17,0x30,0x10,0xB4,0x01,0x96,0x06,0xC0,0xFE,0x44,0x17, +0x30,0x10,0xB4,0x01,0x92,0x03,0x96,0x06,0x5A,0x00,0x01,0xFD,0xDD,0x9E,0x84,0xA0, +0xD0,0x05,0x40,0x00,0x00,0x09,0x8C,0xA1,0xD5,0xFC,0xDD,0x9E,0xC9,0x08,0x44,0x24, +0x00,0x0C,0x96,0x06,0xA6,0x50,0x54,0x10,0x80,0xFE,0xD5,0x0A,0x5A,0x18,0x01,0x0B, +0x44,0x24,0x00,0x0C,0x96,0x06,0xA6,0x50,0x94,0x01,0x54,0x10,0x80,0xFD,0xFE,0x0F, +0xAE,0x10,0xDD,0x9E,0xC8,0x04,0x44,0x05,0x00,0x00,0xD5,0x03,0x44,0x05,0x00,0x10, +0xA6,0x02,0xDD,0x9E,0xC9,0x04,0x44,0x15,0x00,0x00,0xD5,0x05,0x5A,0x18,0x01,0x05, +0x44,0x15,0x00,0x10,0xAE,0x0A,0xDD,0x9E,0xC9,0x0A,0x44,0x24,0x80,0x80,0x96,0x06, +0x40,0x10,0x60,0x08,0xB4,0x62,0x46,0x0F,0xEF,0xFF,0xD5,0x0B,0x5A,0x18,0x01,0x0F, +0x44,0x24,0x80,0x80,0x96,0x06,0x40,0x10,0x64,0x08,0xB4,0x62,0x46,0x0F,0xDF,0xFF, +0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0xCA,0x0C,0x44,0x40, +0xFA,0x00,0x42,0x20,0x10,0x24,0x40,0x11,0x04,0x0E,0x44,0x20,0x00,0x64,0x40,0x10, +0x88,0x36,0xD5,0x0E,0x3C,0x2D,0xF8,0xEF,0xFE,0x84,0x44,0x00,0x03,0xE8,0x40,0x21, +0x00,0x57,0x40,0x11,0x04,0x0D,0x44,0x20,0x00,0x64,0x40,0x10,0x88,0x37,0x96,0x49, +0xCB,0x04,0x44,0x05,0x00,0x04,0xD5,0x05,0x5A,0x38,0x01,0x05,0x44,0x05,0x00,0x14, +0xAC,0x40,0xDD,0x9E,0xC8,0x08,0x44,0x14,0x80,0x03,0x84,0x01,0xAE,0x08,0x44,0x15, +0x00,0x08,0xD5,0x09,0x5A,0x08,0x01,0x09,0x44,0x14,0x80,0x03,0x84,0x42,0xAE,0x88, +0x44,0x15,0x00,0x18,0xAE,0x09,0xDD,0x9E,0xC8,0x08,0x44,0x04,0x80,0x03,0x84,0x21, +0xAE,0x40,0x44,0x15,0x00,0x08,0xD5,0x09,0x5A,0x08,0x01,0x0C,0x44,0x04,0x80,0x03, +0x84,0x22,0xAE,0x40,0x44,0x15,0x00,0x18,0xA6,0x08,0x58,0x00,0x00,0x02,0xAE,0x08, +0xDD,0x9E,0xC8,0x08,0x44,0x04,0x80,0x03,0x84,0x21,0xAE,0x40,0x44,0x15,0x00,0x08, +0xD5,0x09,0x5A,0x08,0x01,0x0C,0x44,0x04,0x80,0x03,0x84,0x22,0xAE,0x40,0x44,0x15, +0x00,0x18,0xA6,0x08,0x54,0x00,0x00,0xFD,0xAE,0x08,0xDD,0x9E,0xC9,0x04,0x44,0x15, +0x00,0x00,0xD5,0x05,0x5A,0x18,0x01,0x05,0x44,0x15,0x00,0x10,0xAE,0x08,0xDD,0x9E, +0x84,0x21,0xC0,0x04,0x5A,0x08,0x01,0x06,0x84,0x22,0x44,0x04,0x80,0x03,0xAE,0x40, +0xDD,0x9E,0x44,0x04,0x80,0x00,0xA6,0x03,0x92,0x01,0x96,0x06,0xC0,0x1A,0xFC,0x00, +0x44,0x04,0x80,0x03,0x84,0x22,0xAE,0x40,0x49,0x00,0x3F,0xA6,0xC8,0x11,0x3C,0x0D, +0xF8,0xB4,0xC0,0x0E,0x49,0x00,0x3C,0x13,0x80,0xC0,0xC8,0x0A,0x49,0x00,0x24,0x51, +0x49,0x00,0x40,0xFD,0xC0,0x05,0x80,0x06,0x80,0x26,0x49,0x00,0x23,0xB6,0xFC,0x80, +0xDD,0x9E,0x46,0x20,0x00,0x81,0x84,0x3E,0xA0,0xD5,0x96,0x06,0xFE,0x5E,0xFE,0x0F, +0xA8,0x15,0xDD,0x9E,0x46,0x00,0x00,0x81,0xA0,0x05,0x96,0x06,0xDD,0x9E,0x44,0x24, +0x50,0x14,0x84,0x3E,0xB4,0x62,0x96,0x06,0xFE,0x5E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E, +0x44,0x25,0x10,0x04,0xB4,0x22,0x92,0x30,0x40,0x10,0xC0,0x08,0xFE,0x0F,0xB6,0x02, +0xDD,0x9E,0x44,0x15,0x10,0x0C,0xB4,0x01,0x58,0x00,0x01,0x00,0xB6,0x01,0xDD,0x9E, +0x44,0x15,0x10,0x0C,0xB4,0x01,0x92,0x08,0x40,0x00,0x20,0x08,0x58,0x00,0x00,0x01, +0xB6,0x01,0x44,0x15,0x10,0x18,0xB4,0x01,0x96,0x00,0x5A,0x00,0x5A,0xFE,0xDD,0x9E, +0xFC,0x40,0x44,0x00,0x01,0x2C,0x49,0x00,0x00,0x72,0x44,0x75,0x10,0x0C,0x44,0x9F, +0xFF,0x98,0x84,0x00,0xAE,0x38,0xFA,0x18,0x10,0x93,0x80,0x00,0x49,0x00,0x00,0x67, +0x44,0x05,0x10,0x18,0x44,0x65,0x10,0x0C,0xB4,0x00,0x96,0x00,0x5A,0x00,0xA5,0xF3, +0xB4,0x06,0x92,0x08,0x40,0x00,0x20,0x08,0xB6,0x06,0xFC,0xC0,0xC0,0x04,0x5A,0x00, +0x01,0x1F,0xDD,0x9E,0x44,0x14,0x80,0x82,0x44,0x22,0x00,0x00,0xAE,0x08,0x44,0x14, +0x80,0x00,0x44,0x04,0x80,0x80,0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x44,0x24, +0x00,0x00,0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x46,0x20,0x01,0x00,0xB6,0x41, +0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x46,0x20,0x00,0x80,0xD5,0x23,0x44,0x04,0x80,0x82, +0x84,0x20,0xAE,0x40,0x44,0x14,0x80,0x00,0x44,0x04,0x80,0x80,0x44,0x21,0x00,0x00, +0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x44,0x22,0x00,0x00,0xB6,0x41,0xB4,0x60, +0xFE,0x9F,0xB6,0x40,0x44,0x24,0x00,0x00,0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40, +0x46,0x20,0x00,0x80,0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x46,0x20,0x01,0x00, +0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x46,0x20,0x02,0x00,0xB6,0x41,0xB4,0x60, +0xFE,0x9F,0xB6,0x40,0x46,0x20,0x04,0x00,0xB6,0x41,0xB4,0x20,0xFE,0x8F,0xB6,0x40, +0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xFC,0x72,0xFC,0x80,0x84,0x2E,0xFE,0x0C,0x8E,0x01, +0x5A,0x07,0xFF,0x05,0x40,0x00,0x00,0x09,0xD5,0xFB,0xDD,0x9E,0x44,0x10,0x36,0xB0, +0xFE,0x0C,0x8E,0x01,0x5A,0x07,0xFF,0x05,0x40,0x00,0x00,0x09,0xD5,0xFB,0xDD,0x9E, +0xFC,0x20,0x44,0x62,0x33,0x90,0x44,0x72,0x10,0x80,0x80,0x06,0x49,0x00,0x19,0xC8, +0xFD,0x03,0x49,0x00,0x1A,0x12,0xFD,0x03,0x50,0x23,0xFF,0xE8,0x50,0x33,0xFF,0xF4, +0x00,0x43,0x7F,0xF9,0x49,0x00,0x68,0x5E,0xFC,0xA0,0xFC,0x00,0x84,0x20,0x44,0x20, +0x0C,0x8C,0x44,0x02,0x33,0x90,0x49,0x00,0x5B,0xC7,0x49,0x00,0x1E,0xCB,0xFC,0x80, +0xFC,0x00,0x49,0x00,0x4D,0x86,0x50,0x30,0x04,0x80,0x3C,0x44,0x05,0x93,0x84,0x20, +0x2A,0x20,0x00,0x01,0xE0,0x82,0xE8,0x05,0x84,0x00,0x3E,0x07,0xEF,0x88,0xD5,0x22, +0x5E,0xF1,0x7F,0x38,0xE8,0x0B,0x8C,0x21,0x5E,0xF1,0x7E,0x70,0x96,0x49,0xE8,0x06, +0xE6,0x27,0x80,0x41,0xE8,0x02,0x84,0x47,0x96,0x51,0x4C,0x01,0xFF,0xEB,0xE6,0x27, +0xE9,0x08,0x84,0x01,0x3E,0x07,0xEF,0x89,0x2E,0x07,0xEF,0x88,0x8C,0x01,0xD5,0x04, +0x84,0x00,0x3E,0x07,0xEF,0x89,0x3E,0x07,0xEF,0x88,0x2E,0x07,0xEF,0x88,0xE6,0x02, +0xE8,0x03,0x84,0x00,0xD5,0x07,0x49,0x00,0x3A,0x83,0xC8,0xFC,0x3E,0x07,0xEF,0x88, +0x84,0x01,0xFC,0x80,0xFC,0x00,0x49,0x00,0x3A,0x7B,0xC8,0x04,0x49,0xFF,0xFF,0xC2, +0xFC,0x80,0x84,0x00,0xFC,0x80,0xFC,0x40,0xB4,0x80,0xA5,0x4C,0x22,0x30,0x80,0x02, +0xE0,0x85,0x22,0x70,0x80,0x05,0x22,0x60,0x80,0x03,0xE8,0x2B,0x96,0xD9,0xE0,0x83, +0x81,0x43,0x40,0x92,0x8C,0x01,0x40,0xA2,0x3C,0x1A,0x80,0xC9,0x4E,0x94,0x00,0x04, +0x52,0x64,0x80,0x00,0x40,0x21,0xA8,0x01,0x4E,0x24,0x00,0x03,0xFE,0x92,0x22,0x70, +0x80,0x00,0x40,0x15,0x0C,0x01,0x9A,0xEF,0xFE,0x5C,0x40,0x10,0xA4,0x36,0x88,0x27, +0xE0,0x27,0xE9,0x05,0xE0,0xA1,0x40,0x50,0xBC,0x1A,0x80,0xE5,0x9A,0x72,0xFF,0x14, +0x42,0x23,0x84,0x24,0x40,0x42,0x18,0x96,0x40,0x21,0x18,0x56,0x88,0x82,0xD5,0x38, +0x41,0xC3,0x80,0x13,0x40,0x91,0x70,0x01,0xE1,0x24,0xE8,0x35,0x40,0xA3,0x00,0x13, +0x40,0x31,0x28,0x01,0xE0,0x64,0x80,0xA3,0x40,0x7E,0x28,0x01,0x40,0x52,0x3C,0x1A, +0x4E,0x74,0x00,0x03,0xFF,0xFA,0x8A,0x65,0x4E,0x34,0x00,0x05,0x40,0x35,0x08,0x01, +0x88,0x65,0x22,0x80,0x80,0x01,0x9B,0x55,0x8A,0xAA,0x40,0x6E,0x20,0x01,0xFF,0x74, +0x40,0x15,0x70,0x01,0x40,0x12,0x84,0x36,0x8A,0x28,0x88,0x22,0xE0,0x29,0xE9,0x06, +0x8A,0x48,0xE0,0x41,0x40,0x20,0xBC,0x1A,0x81,0x22,0x9A,0x7B,0x42,0x52,0x0C,0x24, +0x42,0x34,0x84,0x24,0x40,0x52,0x9C,0xB6,0x40,0x31,0x9C,0x76,0x99,0x2B,0xB6,0x80, +0x84,0x01,0xFC,0xC0,0x84,0x00,0xFC,0xC0,0x3C,0x03,0xF7,0xC6,0x96,0x06,0xC0,0x5F, +0xFC,0x42,0x45,0xC2,0x33,0x90,0x84,0xE0,0x50,0x9E,0x01,0x18,0x00,0x0E,0x01,0x05, +0x9E,0x41,0xE6,0x22,0xE9,0x03,0x5A,0x08,0x04,0x4D,0xB0,0x43,0x84,0x40,0x80,0x1C, +0x49,0x00,0x00,0x88,0xF0,0x03,0x44,0x12,0x10,0x68,0x22,0x00,0x00,0x04,0xF0,0x81, +0x3C,0x63,0xEF,0x08,0xB0,0x01,0x80,0x46,0x49,0xFF,0xFF,0x6F,0x81,0x40,0x3C,0x27, +0xF7,0x3C,0xF0,0x01,0xF1,0x03,0xE0,0x02,0xE9,0x08,0x3C,0x27,0xF7,0x3D,0x9A,0xB2, +0xE0,0x02,0x40,0x20,0x3C,0x1B,0x96,0x93,0x22,0x00,0x80,0x05,0xAC,0x8C,0xF0,0x82, +0x44,0x12,0x10,0x74,0x3C,0x63,0xEF,0x09,0xB0,0x02,0x80,0x46,0x49,0xFF,0xFF,0x55, +0x81,0x00,0x3C,0x27,0xF7,0x42,0xF0,0x02,0xF1,0x03,0xE0,0x02,0xE9,0x08,0x3C,0x27, +0xF7,0x43,0x9A,0xB2,0xE0,0x02,0x40,0x20,0x3C,0x1B,0x96,0x93,0xAC,0x8D,0x80,0x1C, +0x49,0x00,0x00,0x3B,0x4E,0xA3,0x00,0x04,0x4E,0x82,0x00,0x0C,0x44,0x00,0x01,0x24, +0x80,0x29,0x42,0x13,0x80,0x73,0x80,0x01,0xA6,0x4F,0x58,0x10,0x80,0x40,0xAE,0x47, +0x8C,0xE1,0x51,0xCE,0x01,0x24,0x5A,0x78,0x0B,0xAB,0xFC,0xC2,0xDD,0x9E,0x04,0x30, +0x00,0x47,0x92,0x79,0x96,0xD7,0xE4,0x65,0xE8,0x1D,0xFC,0x00,0xFA,0x4C,0x80,0xC0, +0x42,0x01,0x88,0x73,0x49,0x00,0x5A,0x7A,0x04,0x13,0x00,0x47,0x50,0x03,0x01,0x1B, +0x92,0x39,0x8C,0x21,0x96,0x57,0xE6,0x25,0xE8,0x07,0xA6,0x84,0x94,0x49,0x54,0x21, +0x00,0xF1,0xFE,0x57,0xD5,0x04,0xA6,0x44,0x54,0x10,0x80,0xF1,0xAE,0x44,0x84,0x01, +0xFC,0x80,0x84,0x00,0xDD,0x9E,0x04,0x20,0x00,0x47,0x92,0x59,0x96,0x97,0xE4,0x45, +0xE8,0x0E,0xFC,0x00,0x9E,0xD1,0x96,0xD8,0x5A,0x38,0xFF,0x03,0x9C,0xD4,0xFA,0x4C, +0x42,0x01,0x88,0x73,0x49,0x00,0x5A,0x52,0x84,0x01,0xFC,0x80,0x84,0x00,0xDD,0x9E, +0xE6,0x45,0xE8,0x14,0x04,0x30,0x00,0x47,0x92,0x79,0x96,0xD7,0x9A,0x9A,0x96,0x90, +0x9E,0xD1,0x96,0xD8,0x97,0x1A,0x4E,0x44,0x00,0x04,0x8C,0x44,0x96,0xD0,0xFA,0x4C, +0x42,0x01,0x88,0x73,0xB6,0x01,0x84,0x01,0xDD,0x9E,0x84,0x00,0xDD,0x9E,0xFC,0x40, +0x85,0x46,0x80,0xC0,0x50,0x90,0x04,0x80,0x84,0xE0,0x49,0xFF,0xFE,0x5C,0xE6,0x03, +0xE9,0x08,0x38,0x03,0x1D,0x11,0x42,0x00,0x28,0x24,0x90,0x01,0x38,0x03,0x1D,0x09, +0x8C,0xE1,0x5A,0x78,0x12,0xF4,0x50,0x63,0x00,0x24,0x4C,0x64,0xFF,0xEF,0xFC,0xC0, +0xFC,0x40,0xFF,0x92,0x81,0x40,0x80,0xE1,0x80,0x01,0x81,0x22,0x84,0x20,0x44,0x20, +0x00,0x40,0x49,0x00,0x5A,0x39,0x97,0xB3,0x84,0x80,0x84,0x49,0xFA,0x22,0x83,0x8A, +0x42,0x82,0x08,0x24,0x43,0xC2,0x04,0x73,0x84,0x00,0x38,0x3E,0x01,0x11,0x97,0x40, +0xE0,0x69,0xE9,0x06,0x96,0x2A,0x4E,0x05,0x00,0x20,0xFA,0x61,0xD5,0x12,0xE0,0xC3, +0xE8,0xFA,0x8C,0x01,0x5A,0x08,0x12,0xF3,0xD5,0x17,0x40,0xF1,0xA0,0x00,0x39,0xC5, +0x3D,0x11,0x40,0xFE,0x24,0x07,0xE8,0x08,0xE0,0xDC,0xE8,0x06,0x8E,0x61,0x96,0xD8, +0xE0,0x60,0xE8,0xF4,0xD5,0x03,0x96,0xDA,0xD5,0x02,0x84,0x7F,0x98,0x3C,0x8C,0x61, +0x38,0x53,0x90,0x08,0xAE,0xC1,0x8C,0x82,0x5A,0x48,0x40,0xD3,0xFC,0xC0,0xFC,0x42, +0x84,0xA0,0x80,0xE5,0x81,0x25,0xA7,0x89,0x55,0xC2,0x80,0xFF,0xF6,0x82,0xFA,0xC2, +0xFF,0xAC,0xF6,0x83,0x2E,0x67,0xEF,0x92,0xA7,0x08,0x2F,0xE7,0xEF,0x93,0xF6,0x81, +0x2E,0x87,0xEF,0x91,0x2E,0xA7,0xEF,0x90,0xF6,0x02,0xE2,0x86,0xE8,0x13,0xF6,0x03, +0x88,0xC4,0x38,0x60,0x19,0x11,0xE1,0x26,0xE8,0x05,0x15,0xCF,0x80,0x01,0x83,0xC4, +0x81,0x26,0xE0,0xC7,0xE8,0x04,0x81,0x5C,0x81,0x04,0x80,0xE6,0x8C,0x81,0x97,0x20, +0xD5,0xEC,0x00,0x4F,0x80,0x04,0x8C,0xA1,0x3F,0xE7,0xEF,0x93,0x3E,0x47,0xEF,0x92, +0x3E,0x87,0xEF,0x91,0x3E,0xA7,0xEF,0x90,0x8C,0x22,0x5A,0x58,0x20,0xCE,0x12,0x91, +0x00,0x00,0xAD,0xD8,0xFC,0xC2,0xFC,0x41,0x81,0x20,0x80,0xE1,0x80,0x02,0xFA,0x2F, +0x81,0x42,0x44,0x20,0x02,0x40,0xB6,0x7F,0xF4,0x81,0x49,0x00,0x59,0xBD,0x84,0x60, +0x80,0xC3,0x80,0x23,0x80,0xA3,0x84,0x09,0x38,0x43,0x8C,0x00,0x43,0xE1,0x80,0x24, +0x41,0xC3,0x8C,0x00,0x00,0x0E,0x00,0x01,0xE2,0x80,0xE8,0x1B,0x40,0x22,0x78,0x00, +0xB5,0x1F,0x38,0x04,0x89,0x11,0xE1,0x00,0x88,0x20,0xE8,0x10,0x04,0x8F,0x80,0x01, +0x8C,0xC1,0xE1,0x00,0x97,0xB3,0xE8,0x04,0x44,0x00,0x00,0xDF,0xD5,0x03,0x44,0x00, +0x00,0x5F,0x8C,0xA1,0x38,0x05,0x08,0x08,0x97,0x69,0x8C,0x81,0x97,0x20,0xD5,0xE3, +0x8C,0x62,0x5A,0x38,0x40,0xDA,0x3C,0x1F,0xFC,0x58,0x3C,0x6B,0xF8,0xB2,0x80,0x05, +0xFC,0xC1,0xFC,0x48,0xFA,0x94,0x50,0x50,0x00,0x24,0x84,0x20,0x80,0x61,0x44,0x60, +0x7F,0xFF,0x42,0x20,0x90,0x24,0x38,0x20,0x08,0x11,0x38,0x2F,0x85,0x09,0xE0,0x46, +0x40,0x61,0x3C,0x1B,0x8C,0x21,0xE0,0x62,0x40,0x31,0x3C,0x1B,0x5A,0x18,0x20,0xF3, +0x3C,0x14,0x05,0xAF,0xE0,0x23,0xE9,0x05,0x8C,0x02,0x4C,0x02,0xFF,0xE8,0xFC,0xC8, +0x84,0x60,0x80,0x43,0x80,0xE3,0x38,0x9F,0x8D,0x11,0x40,0xA4,0x98,0x01,0xE1,0x41, +0xE8,0x04,0x8C,0x41,0x88,0xE9,0x96,0x90,0x8C,0x61,0x5A,0x38,0x20,0xF6,0xCA,0x02, +0x84,0x41,0x40,0x11,0x04,0x09,0x88,0x27,0x40,0x10,0x88,0x36,0x5E,0xF0,0x80,0xC9, +0xE9,0x04,0x44,0x10,0x00,0xC8,0xD5,0x04,0x4E,0x14,0x00,0x03,0x84,0x20,0x40,0x60, +0x84,0x0A,0x97,0xB1,0x84,0x60,0x3C,0x74,0x05,0xAF,0x38,0x2F,0x8D,0x11,0x88,0xE1, +0xE0,0xE2,0x42,0x71,0x90,0x24,0xE8,0x03,0x8A,0x46,0xD5,0x02,0x8A,0x41,0x8C,0x61, +0x38,0x20,0x1C,0x09,0x5A,0x38,0x20,0xF1,0xD5,0xC8,0xFC,0x00,0x84,0xC0,0x84,0x03, +0x84,0x20,0x3E,0x67,0xEF,0xAD,0x49,0x00,0x53,0x73,0x84,0x04,0x84,0x20,0x49,0x00, +0x53,0x6F,0x84,0x09,0x84,0x20,0x49,0x00,0x53,0x6B,0x84,0x0E,0x84,0x20,0x49,0x00, +0x53,0x67,0x84,0x0F,0x84,0x20,0x49,0x00,0x53,0x63,0x84,0x0A,0x84,0x20,0x49,0x00, +0x53,0x5F,0x84,0x0B,0x84,0x20,0x49,0x00,0x53,0x5B,0x3E,0x67,0xEF,0xAC,0xFC,0x80, +0xFC,0x21,0x44,0x52,0x00,0x00,0x80,0xE2,0x80,0xC3,0xA4,0xA8,0xA4,0xE9,0x22,0x10, +0x80,0x00,0x22,0x00,0x00,0x00,0x9A,0x59,0x9A,0x10,0xB6,0x1F,0xF1,0x81,0xA5,0x2C, +0x3A,0x0F,0x84,0x00,0xA5,0x6D,0x49,0x00,0x1F,0x15,0x3A,0x0F,0x84,0x20,0xB4,0x1F, +0xAC,0x38,0xF0,0x01,0xAC,0x30,0xFC,0xA1,0x46,0x27,0xFF,0xFF,0x84,0x20,0x44,0x52, +0x2F,0x04,0x50,0x21,0x0F,0xFF,0x44,0x32,0x2E,0xD8,0x38,0x22,0x86,0x0A,0x38,0x21, +0x86,0x0A,0x8C,0x21,0x44,0x42,0x2F,0x04,0x5A,0x18,0x0B,0xF9,0xFC,0x40,0x2E,0x37, +0xE2,0x44,0x84,0xA0,0x40,0x91,0x88,0x08,0x81,0x44,0xD3,0x14,0x40,0x70,0x24,0x00, +0x80,0x40,0x4C,0x23,0x80,0x0B,0x38,0x62,0x16,0x02,0xA2,0x51,0xE0,0xC1,0x40,0x13, +0x3C,0x1B,0x38,0x15,0x16,0x0A,0xD5,0xF6,0x8C,0xA1,0x97,0x68,0x50,0x00,0x00,0x2C, +0xD5,0xED,0xFC,0xC0,0x2E,0x27,0xE2,0x44,0x46,0x18,0x00,0x00,0x84,0x00,0x44,0x42, +0x2E,0xAC,0x44,0x52,0x2E,0x80,0xE0,0x02,0xE8,0x0B,0x38,0x32,0x02,0x02,0xCB,0x06, +0x38,0x32,0x82,0x02,0xE0,0x23,0x40,0x11,0xBC,0x1B,0x8C,0x01,0xD5,0xF5,0x84,0x00, +0x44,0x52,0x2E,0x54,0x44,0x42,0x2F,0x04,0xE0,0x02,0xE8,0x0B,0x38,0x32,0x82,0x02, +0xC3,0x06,0x38,0x32,0x02,0x02,0x8A,0x61,0x38,0x32,0x02,0x0A,0x8C,0x01,0xD5,0xF5, +0x84,0x00,0x44,0x52,0x2E,0xAC,0x44,0x42,0x2E,0xD8,0xE0,0x02,0xE8,0x0B,0x38,0x32, +0x82,0x02,0xC3,0x06,0x38,0x32,0x02,0x02,0x88,0x61,0x38,0x32,0x02,0x0A,0x8C,0x01, +0xD5,0xF5,0x84,0x00,0x44,0x52,0x2E,0xAC,0x44,0x42,0x2E,0x80,0xE0,0x02,0xE8,0x0B, +0x38,0x32,0x82,0x02,0xCB,0x06,0x38,0x32,0x02,0x02,0x8A,0x61,0x38,0x32,0x02,0x0A, +0x8C,0x01,0xD5,0xF5,0xDD,0x9E,0xFC,0x40,0x44,0x32,0x2E,0x54,0x84,0x81,0x38,0x41, +0x82,0x0A,0x44,0x32,0x2F,0x30,0x2E,0x57,0xE2,0x44,0x38,0x11,0x82,0x0A,0xFA,0x3C, +0x42,0x20,0x04,0x73,0x44,0x62,0x2F,0x04,0x84,0x20,0x44,0x72,0x2E,0xD8,0x44,0x42, +0x2E,0x80,0x44,0x92,0x2D,0xFC,0x96,0xC8,0xE2,0x65,0xE8,0x13,0x38,0xA3,0x02,0x02, +0x38,0x33,0x86,0x02,0x88,0x6A,0x38,0xA1,0x06,0x02,0x8A,0x6A,0x38,0xA2,0x06,0x02, +0xE1,0x43,0xE8,0x05,0x38,0x32,0x06,0x0A,0x38,0x04,0x86,0x0A,0x8C,0x21,0xD5,0xEC, +0xFC,0xC0,0xFC,0x48,0x44,0xA2,0x2F,0x5C,0xB6,0x1F,0x2E,0x97,0xE2,0x44,0x2E,0x57, +0xEF,0x14,0x4C,0x54,0x80,0xE0,0x84,0x20,0xFA,0x5C,0x44,0x02,0x2E,0x54,0x49,0x00, +0x58,0x43,0x84,0x20,0xFA,0x5C,0x44,0x02,0x2E,0xAC,0x49,0x00,0x58,0x3D,0x44,0x02, +0x2F,0x30,0x84,0x3F,0xFA,0x5C,0x49,0x00,0x58,0x37,0x84,0xE0,0xE0,0xE9,0xE8,0x15, +0x38,0x05,0x1E,0x02,0x5A,0x0F,0xFF,0x10,0x44,0x02,0x2F,0x30,0x84,0x3E,0x38,0x10, +0x1E,0x0A,0x44,0x02,0x2E,0x54,0x84,0x21,0xF7,0x85,0x38,0x10,0x1E,0x0A,0x80,0xC9, +0x80,0x47,0xD5,0x06,0x8C,0xE1,0xD5,0xEB,0x84,0x20,0x80,0xC9,0x80,0x41,0xFA,0x1C, +0xB4,0x7F,0x42,0x31,0x00,0x73,0x84,0xA0,0x45,0xE2,0x2E,0x80,0x44,0x42,0x2F,0x04, +0x44,0x82,0x2E,0xD8,0x44,0xF2,0x2D,0xFC,0x4C,0x54,0x80,0x10,0x39,0xC2,0x0A,0x02, +0x38,0x04,0x16,0x02,0x88,0x1C,0x39,0xC1,0x96,0x02,0x38,0x27,0x96,0x0A,0x8A,0x1C, +0x38,0x0F,0x16,0x0A,0x8C,0xA1,0xD5,0xF1,0x85,0x20,0x44,0x82,0x2E,0xAC,0xE1,0x21, +0xE8,0x39,0xB0,0x05,0x05,0xCF,0x80,0x00,0x38,0x70,0x26,0x02,0xFA,0x1C,0x43,0xC3, +0x80,0x73,0x84,0xC0,0x44,0x42,0x2F,0x04,0x44,0x52,0x2E,0xD8,0x2E,0x07,0xE2,0x44, +0xE0,0xC0,0x4E,0xF2,0x00,0x7D,0x38,0x22,0x1E,0x02,0x38,0x02,0x9A,0x02,0x94,0xF2, +0x88,0x02,0x38,0x2E,0x1A,0x02,0x4C,0x20,0x40,0x1C,0x38,0x04,0x1A,0x02,0xC8,0x18, +0x44,0x02,0x2E,0x28,0xF5,0x83,0x38,0x00,0x0C,0x02,0xF4,0x82,0x5A,0x07,0xFF,0x13, +0x84,0x41,0x38,0x24,0x0C,0x0A,0x88,0x41,0xF2,0x81,0xB0,0x85,0x38,0x01,0x06,0x0A, +0x80,0x27,0xB4,0x5F,0x49,0xFF,0xFF,0x49,0xF1,0x01,0xF5,0x03,0xF4,0x02,0x8C,0xC1, +0xD5,0xD6,0x2E,0x07,0xE2,0x44,0xE0,0xC0,0xE9,0x37,0x49,0xFF,0xFE,0xF5,0x84,0x20, +0x80,0xC1,0x45,0xC2,0x2E,0x80,0x2E,0x07,0xE2,0x44,0xE0,0xC0,0xE8,0x2A,0x38,0x04, +0x1A,0x02,0x94,0xB2,0xC8,0x24,0x38,0x0E,0x1A,0x02,0xC8,0x21,0x44,0x02,0x2E,0x28, +0x38,0x00,0x08,0x02,0x5A,0x0F,0xFF,0x07,0x44,0x02,0x2D,0xFC,0x38,0x70,0x1A,0x02, +0xD5,0x1B,0x84,0x61,0x38,0x34,0x08,0x0A,0x44,0x32,0x2E,0x54,0x38,0x31,0x82,0x02, +0xCB,0x0E,0xB0,0xC5,0x50,0x90,0x80,0x01,0x38,0x01,0x86,0x0A,0x44,0x12,0x2D,0xFC, +0x38,0x10,0x88,0x02,0xB4,0x5F,0x49,0xFF,0xFF,0x10,0x80,0x29,0x8C,0xC1,0xD5,0xD4, +0x85,0x20,0x48,0xFF,0xFF,0x8E,0x2E,0x07,0xE2,0x44,0xE0,0xC0,0xE8,0x1B,0x2E,0x07, +0xEF,0x14,0x44,0x12,0x2F,0x30,0x8C,0x01,0x3E,0x07,0xEF,0x14,0x44,0x02,0x2E,0x28, +0x5A,0x7F,0xFE,0x04,0x48,0xFF,0xFF,0x2B,0x38,0x25,0x1E,0x02,0x38,0x70,0x1A,0x0A, +0x38,0x65,0x1E,0x0A,0x38,0x70,0x9E,0x02,0x80,0xC2,0xD5,0xF3,0x8D,0x21,0x48,0xFF, +0xFF,0x70,0xFC,0xC8,0xFC,0x01,0xF0,0x81,0x84,0x00,0x80,0xC1,0x3E,0x07,0xEF,0x14, +0x84,0x3F,0xFA,0x5C,0x44,0x02,0x2F,0x5C,0x49,0x00,0x57,0x5E,0x84,0x3F,0xFA,0x5C, +0x44,0x02,0x2E,0x28,0x49,0x00,0x57,0x58,0xF0,0x01,0x3E,0x67,0xE2,0x44,0x49,0xFF, +0xFE,0x5D,0xF0,0x01,0x49,0xFF,0xFE,0xFF,0xFC,0x81,0x00,0x00,0x3A,0x6F,0xAA,0xBC, +0xEF,0xEC,0x9E,0x11,0xF0,0x81,0x83,0x81,0x84,0x80,0x85,0x40,0x4E,0x07,0x00,0x36, +0xF0,0x01,0x84,0xC0,0x40,0x80,0x10,0x01,0x4E,0x86,0x00,0x08,0xD5,0x27,0x8C,0xC1, +0x97,0xB0,0xE0,0xC8,0x4E,0xF2,0x00,0x23,0x40,0x93,0x08,0x08,0x50,0x34,0x80,0x04, +0x38,0x0E,0x1A,0x11,0x38,0x1E,0x0C,0x11,0x89,0x3C,0xE0,0x01,0x40,0x7E,0x0C,0x00, +0xE8,0xEF,0x84,0x44,0x80,0x29,0xB0,0x03,0x49,0x00,0x57,0x00,0x8C,0xC1,0x80,0x27, +0x84,0x44,0x80,0x09,0x49,0x00,0x56,0xFA,0x97,0xB0,0x80,0x07,0xB0,0x43,0x84,0x44, +0x49,0x00,0x56,0xF4,0x40,0x03,0x20,0x07,0xC8,0xE0,0x8D,0x41,0xF0,0x01,0x54,0xA5, +0x00,0xFF,0xE1,0x40,0x80,0x8A,0xE9,0xCD,0xEC,0x14,0x3A,0x6F,0xAA,0x84,0xDD,0x9E, +0xC3,0x66,0x3A,0x6F,0xAA,0xBC,0x8E,0x61,0xA7,0x83,0x00,0x90,0x00,0x02,0x96,0xD8, +0x94,0xD9,0x2E,0x77,0xDE,0x16,0x8C,0x63,0x9D,0x51,0xEF,0xFC,0x2E,0x07,0xDE,0x17, +0x88,0x43,0xFB,0x4F,0x87,0x80,0x85,0x00,0x00,0x32,0xFF,0xFF,0x20,0xF2,0x80,0x00, +0x88,0x66,0x96,0xDA,0x4E,0x35,0x00,0x24,0x41,0xE1,0x9C,0x07,0x4F,0xE2,0x00,0x20, +0x40,0xF4,0xBC,0x00,0x40,0xF7,0x80,0x10,0x4E,0xF5,0x00,0x1A,0x41,0xE7,0x80,0x07, +0x4F,0xE2,0x00,0x16,0x83,0xC1,0x43,0xE3,0xBC,0x73,0x38,0x3F,0x0C,0x00,0x54,0xF1, +0x80,0x20,0xE8,0x0D,0x54,0xF1,0x80,0x3F,0x5A,0xF0,0x3F,0x24,0x54,0xF1,0x80,0x1F, +0x5A,0xF0,0x1F,0x06,0x4E,0x83,0x00,0x18,0x81,0x43,0x85,0x01,0x8C,0xA2,0xDA,0xD5, +0xE7,0x02,0xE9,0x1E,0xA6,0x60,0x44,0x00,0x00,0x3F,0xE6,0x39,0xE8,0x08,0x94,0x8A, +0x88,0x44,0x8C,0x21,0xAF,0x95,0x10,0x91,0x00,0x04,0xAE,0x60,0xEC,0x04,0x3A,0x6F, +0xAA,0x84,0xDD,0x9E,0x4C,0x35,0x3F,0xEC,0x8D,0x01,0x54,0x84,0x00,0xFF,0xD5,0xE9, +0x51,0xCE,0x00,0x01,0x8C,0xA2,0x55,0xCE,0x00,0xFF,0xDA,0xB7,0xD5,0xE2,0x80,0x0A, +0x4E,0x83,0xFF,0xEE,0xFA,0x0F,0x4F,0xC2,0xFF,0xEB,0xD5,0xDD,0xFA,0x0F,0xDD,0x9E, +0x3A,0x6F,0xAA,0xBC,0xEF,0xD4,0x44,0x02,0x00,0x68,0x80,0xC2,0xF2,0x81,0x81,0x43, +0xF4,0x84,0xF5,0x82,0x3A,0x20,0x14,0x00,0x22,0x0F,0x80,0x24,0xF0,0x85,0x00,0x0F, +0x80,0x4C,0xF0,0x83,0xB0,0x06,0x3A,0x20,0x14,0x20,0x4E,0xA2,0x00,0x23,0x81,0x21, +0x2E,0x87,0xDE,0x16,0xA6,0x72,0xA6,0x33,0x80,0x69,0x42,0x30,0xA0,0x73,0x38,0x01, +0x80,0x00,0x80,0x26,0x96,0x2E,0x8C,0x27,0x84,0xC0,0xC8,0x0E,0x48,0x00,0x00,0x20, +0x00,0x20,0x7F,0xFF,0x80,0x69,0xA6,0x00,0x42,0x31,0x20,0x73,0x38,0x01,0x80,0x00, +0x8C,0x24,0x96,0x2E,0xC0,0x14,0x8C,0xC1,0x97,0xB0,0x80,0x01,0x4C,0x65,0x7F,0xF2, +0xF0,0x04,0xEC,0x2C,0x3A,0x6F,0xAA,0x84,0xDD,0x9E,0xA6,0xBA,0xA6,0x7B,0x80,0x69, +0x42,0x31,0x20,0x73,0x38,0x01,0x84,0x08,0x8C,0xC1,0x97,0xB0,0xE2,0xCA,0xE8,0xF1, +0xF0,0x01,0x41,0xC3,0x08,0x08,0x40,0x70,0x70,0x00,0x80,0x07,0x80,0x29,0xB0,0x86, +0xF3,0x03,0xF4,0x02,0x49,0xFF,0xFF,0x46,0x5A,0x08,0x1F,0xE9,0xF0,0x04,0xE6,0x0B, +0xE8,0xEC,0xF0,0x01,0xF3,0x05,0xF2,0x04,0x38,0x00,0x70,0x11,0x58,0x11,0x00,0x20, +0xE0,0x03,0x8C,0x41,0xE9,0xE2,0xA6,0xFA,0xA6,0x3B,0x80,0x89,0x42,0x41,0xA0,0x73, +0x96,0x90,0xF2,0x84,0x38,0x12,0x00,0x08,0xD5,0xD8,0x92,0x00,0xB4,0x81,0x3A,0x6F, +0xA8,0xBC,0xB5,0x00,0xA1,0x81,0xA0,0x09,0xA0,0x4A,0xC4,0x07,0x44,0xA0,0x7F,0xFF, +0xFA,0xA0,0x80,0xEA,0x48,0x00,0x00,0x25,0xC8,0xFA,0xD5,0x4D,0xE1,0x49,0xE8,0x26, +0x85,0xFF,0x4E,0x45,0x00,0x03,0x85,0xE1,0x40,0x44,0x84,0x0A,0x42,0x47,0x90,0x24, +0x85,0x21,0x4E,0x04,0x00,0x03,0x85,0x3F,0x4E,0x04,0x00,0x03,0xFE,0x02,0x90,0x01, +0x42,0x04,0x80,0x24,0x85,0x21,0x4E,0x14,0x00,0x03,0x85,0x3F,0x4E,0x14,0x00,0x03, +0xFE,0x4A,0x8E,0xA1,0x90,0x21,0x97,0x68,0x42,0x14,0x84,0x24,0xC5,0x0E,0x81,0x24, +0x4E,0x44,0xFF,0xDE,0x52,0x92,0x00,0x00,0xD5,0xDA,0x81,0xE0,0x4E,0x04,0x00,0x04, +0x52,0xF0,0x00,0x00,0xE0,0xEF,0xE9,0xD5,0xFF,0x84,0x42,0x62,0x20,0x73,0xFE,0x04, +0x88,0xC1,0x42,0x02,0x10,0x73,0x81,0x22,0x80,0xE3,0x49,0x00,0x0C,0x15,0x84,0x41, +0x4E,0x64,0x00,0x04,0x44,0x2F,0xFF,0xFF,0x10,0x24,0x80,0x00,0x80,0x26,0x4E,0x64, +0x00,0x03,0xFE,0x72,0x40,0x10,0x80,0x36,0x40,0x00,0x94,0x0A,0x96,0x02,0xB6,0x27, +0x42,0x00,0x00,0x24,0x3A,0x6F,0xA8,0x84,0xDD,0x9E,0x92,0x00,0xE6,0x62,0xE9,0x3A, +0xA5,0x08,0xE0,0x80,0xE9,0x04,0xA4,0x10,0x4A,0x00,0x78,0x20,0x46,0x47,0xFF,0xFF, +0x50,0x42,0x0F,0xFF,0x88,0x83,0x38,0x50,0x91,0x01,0xE0,0x05,0xE8,0x28,0x3A,0x6F, +0xA4,0x3C,0x84,0x80,0x8E,0x61,0xE0,0x83,0xE8,0x20,0x9D,0x61,0x95,0xE1,0x38,0x40, +0x91,0x01,0x97,0x69,0xE0,0x04,0xE9,0x17,0x38,0x60,0x95,0x01,0x40,0x92,0x84,0x08, +0xE0,0x06,0xE8,0x11,0x38,0x11,0x1C,0x01,0x9B,0x04,0x38,0x51,0x24,0x01,0x9A,0x30, +0x98,0xE0,0xFE,0x0C,0x42,0x02,0x14,0x73,0x40,0x11,0x84,0x0A,0x88,0x01,0x40,0x00, +0x0C,0x16,0xD5,0x0B,0x80,0x85,0xD5,0xE0,0x84,0x00,0xD5,0x07,0x38,0x01,0x11,0x01, +0xDD,0x9E,0x84,0x00,0x4A,0x00,0x78,0x20,0x3A,0x6F,0xA4,0x04,0xDD,0x9E,0x92,0x00, +0x3A,0x6F,0xA8,0xBC,0x80,0xC0,0x81,0x21,0x80,0xE2,0x4E,0x14,0x00,0x05,0xFF,0x82, +0x52,0x90,0x80,0x00,0x80,0x06,0x49,0x00,0x0B,0xDA,0x81,0x40,0x80,0x09,0x49,0x00, +0x0B,0xD6,0x8A,0x0A,0x80,0x2A,0x96,0x00,0xE6,0x36,0xE9,0x0E,0x84,0x41,0x4E,0x64, +0x00,0x03,0x84,0x5F,0x4E,0x64,0x00,0x03,0xFF,0xB2,0x90,0xC1,0x8E,0x21,0xFF,0x94, +0x91,0x21,0x96,0x48,0xD5,0xF2,0x98,0x81,0x96,0x90,0xE6,0x56,0xE8,0xF0,0xCE,0x05, +0x4E,0x92,0x00,0x43,0x81,0x46,0xD5,0x06,0x81,0x46,0x4E,0x64,0x00,0x04,0x52,0xA3, +0x00,0x00,0xE1,0x2A,0xE8,0x08,0x40,0x34,0xA0,0x08,0x40,0x31,0x98,0x76,0x4E,0x34, +0x00,0x0B,0xD5,0x07,0x40,0x33,0x20,0x08,0x40,0x31,0xA4,0x76,0x4E,0x34,0x00,0x04, +0x52,0x31,0x80,0x00,0x44,0x12,0x04,0x38,0x80,0x03,0x50,0x20,0x80,0x16,0x84,0x6B, +0x49,0xFF,0xFF,0x76,0xE1,0x2A,0x84,0x2A,0xE8,0x06,0x52,0x00,0x03,0x84,0x40,0x00, +0x04,0x16,0xD5,0x03,0x40,0x00,0x04,0x16,0x4E,0x65,0x00,0x04,0x52,0x00,0x00,0x00, +0x5A,0x07,0xA6,0x06,0x5E,0xF0,0x7F,0xA7,0xE9,0x0A,0xD5,0x03,0x44,0x00,0x00,0x5A, +0x5E,0xF0,0x00,0x5B,0xE9,0x06,0x44,0x00,0x00,0x5A,0xD5,0x03,0x44,0x0F,0xFF,0xA7, +0xB6,0x07,0x84,0x01,0xD5,0x02,0x80,0x09,0x3A,0x6F,0xA8,0x84,0xDD,0x9E,0x92,0x00, +0x3A,0x6F,0xA8,0xBC,0x80,0xE0,0x84,0x00,0xAE,0x10,0xEF,0xF8,0xB4,0x07,0x81,0x21, +0x50,0xA1,0x00,0x00,0x49,0x00,0x0B,0x6B,0x10,0x0F,0x80,0x04,0x04,0x03,0x80,0x01, +0x49,0x00,0x0B,0x65,0x10,0x0F,0x80,0x05,0x50,0x83,0x80,0x08,0x04,0x03,0x80,0x02, +0x49,0x00,0x0B,0x5D,0x10,0x0F,0x80,0x06,0x04,0x04,0x00,0x01,0x49,0x00,0x0B,0x57, +0x10,0x0F,0x80,0x07,0x00,0x1F,0x80,0x04,0xE6,0x2F,0xE8,0x0F,0x00,0x0F,0x80,0x07, +0xE6,0x0F,0xE8,0x0B,0x88,0x01,0x5A,0x00,0x1C,0x09,0x00,0x1F,0x80,0x06,0x00,0x0F, +0x80,0x05,0x88,0x01,0xE4,0x1C,0xE9,0x26,0x80,0xA7,0x84,0x20,0xB0,0x01,0x98,0x81, +0x50,0x32,0x80,0x08,0xB4,0x05,0x84,0x81,0x4E,0x04,0x00,0x04,0x44,0x4F,0xFF,0xFF, +0x4E,0x04,0x00,0x03,0xFE,0x02,0x90,0x01,0xFE,0x24,0xAA,0x29,0xA6,0x10,0xE4,0x02, +0xE9,0x07,0x5E,0xF0,0x00,0x22,0xE8,0x06,0x8E,0x01,0x96,0x00,0xD5,0x04,0x84,0x01, +0xD5,0x02,0xFA,0x10,0x18,0x01,0x00,0x01,0xDB,0xE6,0x8C,0x22,0x5A,0x18,0x04,0xE0, +0xD5,0xCA,0xB4,0x67,0x04,0x24,0x00,0x01,0xA0,0x79,0xA1,0x3A,0x42,0x01,0x88,0x24, +0x42,0x02,0x04,0x75,0xFF,0x9A,0x84,0x3C,0x8A,0xC2,0xFE,0x0C,0x42,0x03,0x18,0x73, +0x4E,0x05,0x00,0x67,0xCB,0x0C,0x44,0x00,0x00,0x40,0x14,0x04,0x80,0x01,0x44,0x00, +0x00,0x64,0xB6,0x69,0x10,0x05,0x00,0x00,0x48,0x00,0x00,0x53,0xCA,0x0C,0x44,0x00, +0x00,0x40,0xB6,0x09,0x44,0x00,0x00,0x64,0x14,0x24,0x80,0x01,0x10,0x05,0x00,0x00, +0x48,0x00,0x00,0x47,0x49,0x00,0x0A,0xC8,0x9A,0x46,0xFF,0xB2,0x9B,0x30,0xB4,0xC7, +0x04,0x04,0x00,0x01,0x90,0x81,0x88,0x06,0xE0,0x80,0x90,0x21,0xE8,0x16,0x9A,0xC1, +0x44,0x20,0x00,0x64,0xFE,0xD4,0x9B,0x04,0x40,0x61,0x90,0xD6,0x4E,0x65,0x00,0x0A, +0x5E,0xF3,0x00,0x65,0xE9,0x02,0x80,0xC2,0x52,0x63,0x00,0x64,0x97,0xB0,0xD5,0x03, +0x50,0x61,0x00,0x00,0x10,0x65,0x00,0x00,0xA0,0xF9,0xFF,0x1A,0x4E,0x45,0x00,0x03, +0x80,0x64,0xB4,0x47,0x8A,0x41,0x80,0x02,0x4E,0x24,0x00,0x03,0xFE,0x12,0xA1,0xBA, +0x88,0x60,0xFF,0x72,0x4E,0x55,0x00,0x04,0x50,0x62,0x80,0x00,0x04,0x04,0x00,0x01, +0x9A,0x41,0x80,0x01,0x4E,0x14,0x00,0x03,0xFE,0x0A,0x88,0x06,0xE0,0x03,0xE8,0x05, +0xB6,0x89,0x14,0x24,0x80,0x01,0xD5,0x04,0xB6,0x29,0x14,0x54,0x80,0x01,0xB4,0x09, +0xC8,0x09,0x04,0x04,0x80,0x01,0xC8,0x06,0x10,0x05,0x00,0x00,0xD5,0x04,0x84,0x00, +0xD5,0x02,0x84,0x01,0xEC,0x08,0x3A,0x6F,0xA8,0x84,0xDD,0x9E,0x3A,0x6F,0xAA,0xBC, +0xEF,0x9C,0x85,0x00,0x81,0x40,0x80,0xE1,0xB6,0x5F,0x80,0x28,0xFA,0x40,0xB0,0x14, +0x81,0x23,0x80,0xC4,0x14,0x8F,0x80,0x04,0x95,0xFA,0x49,0x00,0x54,0x3D,0x80,0x28, +0xB0,0x06,0x84,0x48,0x49,0x00,0x54,0x38,0x88,0xEA,0x80,0xAA,0x50,0x14,0x00,0x00, +0x45,0xE2,0x30,0x20,0xD7,0x3C,0xA6,0x2B,0x22,0x82,0x80,0x00,0x44,0x32,0x33,0x08, +0x38,0x2F,0x01,0x01,0x88,0x28,0x8A,0x49,0xF2,0x8C,0xA6,0x2A,0x5E,0xF1,0x0C,0x9A, +0x38,0x01,0x81,0x01,0x8A,0x06,0xF0,0x8D,0xE8,0x28,0x5E,0xF0,0x19,0x33,0xE8,0x25, +0x91,0x05,0x84,0x80,0xB0,0x0C,0xB0,0x94,0x38,0xF0,0x12,0x02,0x94,0x23,0x88,0x02, +0x84,0x60,0xB0,0x8C,0x38,0x21,0x0E,0x02,0x42,0x27,0x88,0x24,0x4E,0x27,0x00,0x04, +0x90,0x48,0xD5,0x05,0xFE,0x92,0x90,0x48,0x52,0x21,0x00,0x00,0x05,0xC0,0x00,0x00, +0x43,0xC1,0x20,0x73,0x1D,0xC0,0x00,0x01,0x5A,0x30,0x01,0x04,0x84,0x61,0xD5,0xEA, +0x5A,0x40,0x01,0x04,0x80,0x83,0xD5,0xDF,0x8C,0xA4,0xD5,0xC5,0x90,0x25,0x4E,0x16, +0x00,0x03,0x84,0x21,0xF0,0x14,0x50,0x2F,0x80,0x1D,0x40,0x00,0x04,0x16,0xF0,0x94, +0xF0,0x15,0x40,0x00,0x04,0x16,0xF0,0x95,0xF0,0x16,0x40,0x00,0x04,0x16,0xF0,0x96, +0xF0,0x17,0x40,0x10,0x04,0x36,0xF1,0x97,0xB0,0x14,0xB0,0x48,0x49,0xFF,0xFE,0xBA, +0x5A,0x00,0x01,0x04,0x48,0x00,0x00,0x7C,0x04,0x8F,0x80,0x09,0x05,0xCF,0x80,0x08, +0x10,0x0F,0x80,0x1E,0x80,0x28,0x80,0x1C,0x50,0x2F,0x80,0x14,0x49,0xFF,0xFE,0x3A, +0x5A,0x08,0x01,0x6E,0x52,0x14,0x00,0x00,0x52,0x0E,0x00,0x00,0xF1,0x92,0x42,0x14, +0x18,0x24,0x42,0x10,0x24,0x75,0xF1,0x93,0xF1,0x08,0xF0,0x91,0xF0,0x09,0xFF,0x8C, +0x42,0x60,0x24,0x75,0xF0,0x8E,0xF0,0x05,0xFE,0x8A,0xF6,0x90,0x84,0xC0,0xF2,0x8F, +0x10,0x0F,0x80,0x1C,0x81,0x06,0x81,0x26,0x45,0xC2,0x30,0x20,0x44,0x42,0x33,0x08, +0x4C,0xA3,0x80,0x2A,0x22,0x05,0x00,0x00,0xB0,0x51,0x89,0x20,0x00,0x05,0x00,0x03, +0x50,0x2F,0x80,0x0F,0xB0,0xC4,0x38,0x0E,0x01,0x01,0xF4,0x81,0xF0,0x8A,0x00,0x05, +0x00,0x02,0x38,0x02,0x01,0x01,0xF0,0x8B,0x50,0x0F,0x80,0x28,0x49,0xFF,0xFD,0x60, +0x22,0x15,0x00,0x00,0x50,0x2F,0x80,0x0F,0x42,0x80,0x04,0x73,0xB0,0xC4,0xB0,0x4E, +0x50,0x0F,0x80,0x28,0x49,0xFF,0xFD,0x54,0x2A,0x15,0x00,0x02,0x42,0x60,0x04,0x73, +0xF4,0x01,0xD5,0xD7,0x40,0x04,0x24,0x16,0x49,0x00,0x09,0xA6,0x44,0x10,0x00,0x6E, +0xFE,0x0C,0x50,0x10,0x00,0x32,0x80,0x01,0x96,0x49,0x5C,0xF0,0x93,0x89,0xE9,0x03, +0x44,0x00,0x13,0x88,0x12,0x0F,0x80,0x0D,0x40,0x03,0x24,0x16,0x49,0x00,0x09,0x94, +0x44,0x10,0x00,0x68,0xFE,0x0C,0x50,0x00,0x00,0x32,0x80,0x20,0x96,0x01,0x5C,0xF0, +0x13,0x89,0xE9,0x03,0x44,0x10,0x13,0x88,0x12,0x1F,0x80,0x0C,0xB4,0x1F,0xB0,0x46, +0x84,0x48,0x49,0x00,0x53,0x33,0xEC,0x64,0x3A,0x6F,0xAA,0x84,0xDD,0x9E,0x92,0x00, +0x3A,0x6F,0xAA,0xBC,0x51,0xFF,0xFA,0x9C,0xF0,0x8D,0x84,0x00,0xF1,0x9D,0xF2,0x9E, +0x10,0x0F,0x80,0x9D,0x84,0x20,0x44,0x20,0x02,0x68,0x50,0x0F,0x82,0xF8,0x49,0x00, +0x53,0x43,0x84,0x20,0x44,0x20,0x00,0x66,0x44,0x02,0x2F,0x88,0x49,0x00,0x53,0x3C, +0x84,0x20,0x84,0x4B,0x44,0x02,0x13,0x1C,0x49,0x00,0x53,0x36,0x84,0x00,0x84,0x3F, +0x3C,0x0F,0xFB,0xE0,0x3E,0x07,0xEF,0x16,0x3E,0x17,0xEF,0x15,0x3C,0x0B,0xF7,0xA7, +0x3C,0x0B,0xFA,0x03,0x44,0x12,0x33,0x48,0xF0,0x0D,0x44,0x22,0x30,0xC8,0x44,0x30, +0x00,0x64,0xF4,0x1E,0x49,0xFF,0xF9,0x59,0xF0,0x93,0x3C,0x07,0xF9,0xF5,0x5E,0xF0, +0x01,0x68,0xE9,0x05,0x90,0x01,0xF0,0x94,0x48,0x00,0x00,0x05,0x44,0x00,0x00,0xB4, +0xF0,0x94,0xF0,0x13,0x4E,0x02,0x01,0xC7,0x84,0x00,0xF0,0x8C,0xF0,0x9A,0x84,0x08, +0xF0,0x8F,0x84,0x00,0xF0,0x90,0xF0,0x8B,0xF0,0x0C,0xF2,0x0C,0x94,0x41,0x44,0x02, +0x33,0x48,0xF3,0x0C,0x38,0x20,0x09,0x00,0x88,0x01,0xF2,0x91,0xF0,0xA3,0xFA,0x42, +0xF0,0x0B,0xFE,0x9C,0xF2,0xA4,0xF0,0x8E,0xF0,0x23,0xF1,0x11,0xA6,0x01,0xE2,0x20, +0x4E,0xF2,0x01,0x9D,0xF1,0x11,0x44,0x02,0x30,0xC8,0x88,0x01,0xF1,0x24,0x38,0x00, +0x04,0x00,0x96,0x36,0x4E,0x02,0x01,0x8B,0x50,0x0F,0x80,0x9D,0xF0,0x81,0xF0,0x0F, +0x84,0xC0,0xF0,0x82,0x84,0x01,0xF0,0x83,0x50,0x0F,0x80,0x9E,0xF0,0x84,0x50,0x7F, +0x82,0x08,0xB0,0x28,0xF0,0x89,0x22,0x1F,0x80,0x18,0xB6,0xFF,0xF6,0x85,0xF6,0x86, +0xF6,0x87,0xF6,0x88,0xF0,0x11,0x44,0x20,0x00,0x40,0xF3,0x10,0x44,0x42,0x30,0xC8, +0x04,0x5F,0x80,0x0D,0x49,0x00,0x14,0x0F,0x2E,0x17,0xEF,0x16,0x8C,0x21,0x3E,0x17, +0xEF,0x16,0xF1,0x1D,0xE0,0x01,0x4E,0xF3,0x01,0x62,0xF0,0x0D,0x50,0x13,0x80,0x00, +0x00,0x2F,0x80,0x9D,0x49,0xFF,0xFB,0x6C,0x02,0x0F,0x80,0x4F,0x2E,0x10,0x0B,0x40, +0xE2,0x20,0xE9,0x09,0x00,0x0F,0x80,0x9D,0x9E,0x42,0xE6,0x2D,0x4E,0xF3,0x00,0x2F, +0x48,0x00,0x03,0xFD,0x00,0x1F,0x80,0x9D,0x80,0x06,0x96,0xB0,0xE2,0x41,0xE8,0x0B, +0x38,0xF3,0x9A,0x11,0xF2,0x14,0xE0,0x4F,0xE8,0x03,0x8C,0x01,0x96,0x01,0x8C,0xC1, +0x48,0xFF,0xFF,0xF5,0x3C,0x13,0xFA,0x03,0xE2,0x20,0xE8,0xE5,0x5C,0xF0,0x00,0x3D, +0x3C,0x0B,0xFA,0x03,0x80,0x20,0xE9,0x03,0x44,0x10,0x00,0x3C,0x96,0x49,0x84,0x00, +0x50,0x2F,0x81,0x90,0x50,0x3F,0x82,0x08,0xE2,0x01,0xE8,0xD5,0x38,0x41,0x82,0x01, +0x38,0x41,0x01,0x09,0x8C,0x01,0x96,0x00,0xD5,0xF8,0xE6,0x06,0x80,0x20,0xE9,0x03, +0x44,0x10,0x00,0x05,0x55,0xE0,0x80,0xFF,0x50,0x0F,0x7F,0xFF,0x84,0x20,0x80,0x61, +0x80,0x41,0xF0,0x92,0xF0,0x12,0xE0,0x40,0x4E,0xF2,0x03,0xC1,0x9C,0x11,0x96,0x00, +0xF0,0x8F,0x94,0x92,0x50,0x0F,0x82,0x08,0x88,0x40,0xF6,0x0F,0xE2,0xDE,0xE8,0x4D, +0x50,0x0F,0x82,0x08,0x95,0x32,0x88,0x80,0xA7,0xD3,0xA6,0x23,0x00,0x91,0x00,0x02, +0x9A,0x38,0x96,0x02,0xA7,0x62,0x81,0x00,0x4E,0x04,0x00,0x04,0x52,0x80,0x00,0x00, +0x5A,0x88,0x01,0x39,0x40,0x54,0x94,0x01,0x97,0x6A,0x81,0x05,0x4E,0x54,0x00,0x04, +0x52,0x82,0x80,0x00,0x5A,0x88,0x01,0x2F,0xA5,0x20,0x02,0x81,0x00,0x00,0x40,0xF2, +0x00,0x11,0x40,0xA4,0x00,0x11,0xE1,0x4F,0x40,0x44,0x3C,0x1A,0x97,0x23,0xC4,0x1B, +0x9A,0x38,0xFA,0x22,0x42,0x04,0x84,0x73,0xF1,0x0D,0x40,0x54,0x94,0x01,0x38,0x30, +0x81,0x11,0x44,0x00,0x00,0x64,0xFE,0xC4,0xFA,0x02,0x42,0x72,0x80,0x73,0x38,0x10, +0x9D,0x11,0x44,0x00,0x00,0x64,0xFE,0x44,0x40,0x31,0x90,0x76,0x40,0x10,0x90,0x36, +0x96,0xDB,0x96,0x4B,0xE0,0x61,0x80,0x03,0x40,0x00,0xBC,0x1B,0x5E,0xF0,0x00,0x3C, +0xE9,0x06,0x8C,0xC1,0x97,0xB0,0xD5,0xB3,0xF2,0x0F,0xD5,0xA5,0x00,0x0F,0x82,0x0A, +0xE6,0x02,0x4E,0xF3,0x03,0x64,0x84,0x04,0xF0,0x8F,0xF6,0x0B,0x44,0x70,0x00,0x00, +0x50,0x9F,0x82,0x08,0x00,0x0F,0x80,0x9D,0xE2,0xE0,0xE8,0x07,0x38,0xF4,0x9E,0x11, +0xF0,0x1E,0xE0,0x0F,0x4E,0xF3,0x01,0x7D,0xF0,0x1D,0x80,0x86,0xB6,0x1F,0xF0,0x0F, +0x00,0x3F,0x80,0x9D,0xF0,0x81,0x44,0x12,0x30,0xC8,0x50,0x2F,0x82,0x08,0x44,0x52, +0x2F,0x88,0xF0,0x0D,0x49,0xFF,0xFB,0x4E,0xF0,0x8B,0x00,0x0F,0x80,0x9D,0xB1,0xF1, +0xF0,0x99,0x44,0x02,0x00,0x78,0x84,0xC0,0x3A,0x20,0x14,0x00,0x3A,0x23,0x94,0x20, +0x84,0x21,0x84,0x4B,0xB0,0x2E,0xF6,0xA9,0xF6,0xAA,0xF6,0xAB,0x49,0x00,0x51,0xDC, +0x80,0x26,0xFA,0x5C,0xB0,0x35,0x49,0x00,0x51,0xD7,0x80,0x26,0x44,0x20,0x00,0x2C, +0x50,0x0F,0x81,0x00,0x49,0x00,0x51,0xD0,0x50,0x9F,0x81,0x58,0x80,0x26,0xFA,0x5C, +0x50,0x0F,0x81,0x2C,0x49,0x00,0x51,0xC8,0x80,0x09,0x80,0x26,0x44,0x20,0x00,0x38, +0x49,0x00,0x51,0xC2,0x50,0xAF,0x82,0x08,0x2E,0x07,0xDE,0x16,0xF6,0x95,0xF0,0x92, +0x14,0x9F,0x80,0x25,0x2E,0x07,0xDE,0x17,0xF7,0xA1,0xF0,0xA2,0xF5,0x15,0xF1,0x19, +0x4C,0x50,0xC1,0x64,0x00,0x0F,0x81,0x86,0xF4,0x29,0xF0,0x95,0x00,0x0F,0x81,0x85, +0xF3,0x2A,0xF0,0x92,0xF1,0x2B,0x05,0xEF,0x80,0x5D,0x00,0x6F,0x81,0x5B,0x00,0x7F, +0x81,0x5A,0x00,0x9F,0x81,0x59,0x00,0xAF,0x81,0x58,0xF0,0x0E,0xF2,0x0B,0xE2,0x02, +0x4E,0xF3,0x02,0x67,0x00,0x0F,0x80,0x54,0x10,0xAF,0x81,0x58,0x10,0x0F,0x81,0x86, +0x00,0x0F,0x80,0x48,0x10,0x9F,0x81,0x59,0x10,0x0F,0x81,0x85,0xF0,0x0E,0x10,0x7F, +0x81,0x5A,0xE2,0x02,0x10,0x6F,0x81,0x5B,0x15,0xEF,0x80,0x5D,0xF4,0xA9,0xF3,0xAA, +0xF1,0xAB,0xE8,0x24,0xB1,0xAC,0x40,0x31,0x90,0x76,0x50,0x0F,0x82,0x08,0x40,0x40, +0x90,0x96,0x80,0x46,0xF1,0x19,0xF3,0xD9,0xF4,0xDA,0x49,0xFF,0xFD,0x29,0xF5,0x0E, +0x44,0x10,0x00,0x38,0x50,0x0F,0x85,0x60,0x42,0x02,0x84,0x73,0x50,0x00,0x7D,0x9C, +0x3A,0x23,0x0C,0x00,0x3A,0x20,0x0C,0x20,0x8C,0xA1,0xF0,0x0B,0x97,0x68,0xD8,0xF3, +0xF0,0x0E,0xF1,0x0B,0xE2,0x01,0x4E,0xF3,0x02,0x85,0xF0,0x11,0x8C,0x01,0x96,0x00, +0xF0,0x91,0xF0,0x0B,0xF0,0x8E,0x48,0xFF,0xFE,0x61,0xF0,0x1A,0x5A,0x00,0x01,0x0D, +0xF0,0x0C,0x8C,0x01,0x14,0x0F,0x80,0x0C,0x5A,0x00,0x20,0x04,0x48,0xFF,0xFE,0x46, +0xD5,0x03,0xF0,0x13,0xF0,0x8B,0xF1,0x13,0x50,0x0F,0x81,0x90,0x49,0x00,0x58,0x62, +0x3C,0x0F,0xFB,0xE0,0x44,0x10,0x00,0xFF,0x44,0x02,0x2F,0xF0,0x84,0x4B,0x3C,0x60, +0x05,0x95,0x49,0x00,0x51,0x39,0xF0,0x0B,0x5A,0x08,0x02,0x06,0x95,0xB2,0x84,0x05, +0x40,0x63,0x00,0xD6,0xF0,0x0B,0x8E,0x03,0x96,0x00,0xE6,0x03,0xE8,0x06,0x95,0xB1, +0x84,0x03,0x40,0x63,0x00,0xD6,0xD5,0x05,0xF0,0x0B,0xE6,0x06,0xE9,0x02,0x90,0xC1, +0x84,0x45,0x42,0x03,0x08,0x24,0x90,0x03,0xF0,0x8D,0x84,0x07,0xFE,0x34,0x90,0x03, +0xF0,0x8E,0x84,0x03,0xFE,0x34,0x90,0x01,0x85,0x40,0xF0,0x8F,0x40,0x83,0x04,0x0A, +0x50,0x1F,0x82,0xF8,0x80,0xEA,0xF2,0x90,0xF2,0x0B,0x54,0x05,0x00,0xFF,0xE2,0x02, +0x4E,0xF2,0x00,0x7C,0x00,0x00,0x80,0x2B,0xC0,0x14,0x5A,0x00,0x11,0x13,0x00,0x00, +0x80,0x2C,0xC0,0x0F,0x5A,0x00,0x1F,0x0E,0x9E,0x81,0xE6,0x42,0xE9,0x0C,0x9E,0x83, +0xE6,0x44,0xE9,0x0B,0x8E,0x1C,0xE6,0x03,0xF0,0x0F,0x40,0x03,0x3C,0x1A,0xD5,0x07, +0x80,0x08,0xD5,0x05,0xF0,0x0D,0xD5,0x03,0x04,0x0F,0x80,0x0E,0x3C,0x2D,0xFB,0xD6, +0x54,0x21,0x20,0x00,0xC2,0x06,0x84,0x46,0xFE,0x14,0xF2,0x10,0x40,0x00,0x08,0x16, +0x04,0x20,0x80,0x08,0xE0,0x40,0xE9,0x05,0x44,0x00,0x00,0x01,0x3C,0x0F,0xFC,0x4A, +0x00,0x00,0x80,0x2E,0xC0,0x44,0x44,0x02,0x13,0x1C,0x38,0x00,0x28,0x00,0xF0,0x8C, +0xC8,0x3E,0x3C,0xFD,0xFC,0x4A,0x5A,0xF8,0x01,0x3B,0x44,0x00,0x00,0x38,0x42,0x93, +0x80,0x24,0x44,0x02,0x13,0x4C,0x88,0x09,0x51,0xE0,0x00,0x0C,0x80,0x01,0x3A,0x20, +0x14,0x04,0x3A,0x2F,0x14,0x24,0x3A,0x20,0x14,0x04,0x3A,0x2F,0x14,0x24,0x3A,0x20, +0x14,0x04,0x3A,0x2F,0x14,0x24,0x3A,0x20,0x0C,0x00,0x44,0x02,0x13,0x4C,0x89,0x20, +0x3A,0x2F,0x0C,0x20,0x50,0x24,0x80,0x30,0x9C,0x39,0x10,0xA1,0x00,0x05,0x44,0x32, +0x13,0x4C,0x44,0x22,0x2F,0xF0,0x44,0x40,0x00,0x38,0x42,0x30,0x10,0x73,0x38,0x71, +0x28,0x08,0x00,0x2F,0x80,0x30,0x10,0xF1,0x80,0x04,0x84,0x7F,0xE4,0xEB,0x10,0x34, +0x80,0x3B,0x84,0xEB,0x10,0x24,0x80,0x30,0xE8,0x02,0x97,0xC0,0x50,0xA5,0x00,0x01, +0x50,0x10,0x80,0x38,0x48,0xFF,0xFF,0x82,0x4E,0x73,0x00,0x04,0x3C,0x7F,0xFC,0x4A, +0x3E,0x77,0xF1,0x5C,0x51,0xFF,0x85,0x64,0x3A,0x6F,0xAA,0x84,0xDD,0x9E,0x94,0x7A, +0x88,0x29,0xFA,0x42,0xA6,0x0B,0xA6,0x4A,0x80,0x60,0x42,0x30,0x88,0x73,0x80,0x43, +0x44,0x32,0x30,0xC8,0x38,0x21,0x88,0x10,0x4E,0x24,0x00,0x20,0x50,0x2F,0x80,0x9D, +0xF2,0x81,0xF2,0x0F,0x58,0x33,0x00,0x20,0xF2,0x82,0x50,0x4F,0x80,0x9E,0x84,0x40, +0x8C,0xC1,0xF2,0x83,0xF4,0x84,0xF2,0x85,0xF2,0x86,0xF2,0x87,0xF2,0x88,0xF2,0x89, +0xB7,0x3F,0x44,0x20,0x00,0x80,0x96,0xD8,0x44,0x42,0x30,0xC8,0xF5,0x0D,0x97,0xB0, +0x49,0x00,0x11,0xA1,0xE6,0xCB,0xE8,0x05,0x8C,0xE1,0x97,0xF8,0x48,0xFF,0xFE,0x4C, +0x84,0x01,0xF0,0x9A,0x48,0xFF,0xFE,0x52,0x00,0x75,0x00,0x03,0xF1,0x12,0x00,0x25, +0x00,0x02,0x80,0x07,0x42,0x01,0x04,0x73,0xF0,0x9B,0xF1,0x1B,0x44,0x02,0x30,0xC8, +0x38,0x10,0x04,0x00,0x54,0x00,0x80,0x20,0x4E,0x02,0x01,0x04,0x5A,0x18,0x3F,0x04, +0x48,0x00,0x00,0xBE,0x96,0x67,0xE6,0x2B,0x4E,0xF2,0x00,0xFC,0x94,0x0A,0x50,0x9F, +0x80,0xD4,0x40,0x34,0x80,0x00,0x50,0x5F,0x81,0x00,0x50,0x4F,0x81,0x2C,0xF3,0x96, +0x98,0xE8,0x88,0x04,0xF3,0x97,0xF0,0x98,0x50,0x3F,0x82,0xF8,0x44,0x00,0x00,0x38, +0x42,0x30,0x80,0x73,0x80,0x03,0xB0,0xEE,0x38,0x31,0x84,0x00,0x5A,0x30,0x01,0x04, +0x48,0x00,0x00,0x89,0x84,0x60,0x14,0x9F,0x80,0x1F,0x80,0xC3,0x81,0x25,0xF4,0xA0, +0xF4,0x21,0x04,0x8F,0x80,0x21,0x38,0x52,0x0D,0x00,0x95,0x19,0x88,0xA7,0x97,0x68, +0x88,0x88,0x41,0xE2,0x80,0x10,0x20,0x42,0x00,0x01,0x4F,0xE5,0x00,0x4C,0x04,0x8F, +0x80,0x12,0x40,0xFF,0x20,0x07,0xE8,0x46,0x88,0x82,0x97,0x20,0xF4,0x9C,0x20,0x8F, +0x80,0x70,0x4E,0x85,0x00,0x40,0xF4,0x22,0xE1,0x04,0xE8,0x3C,0xF4,0x12,0x8C,0xC1, +0x43,0xE4,0x10,0x73,0xF4,0x0D,0x04,0x80,0x00,0x08,0x97,0xB0,0x38,0xF2,0x79,0x11, +0x44,0x42,0x30,0xC8,0x89,0x0F,0x14,0x80,0x00,0x08,0x38,0x82,0x78,0x00,0x5A,0x88, +0x1F,0x2A,0x4E,0xF7,0x00,0x07,0x04,0x80,0x00,0x07,0x89,0x0F,0x14,0x80,0x00,0x07, +0x50,0xF7,0xFF,0x9C,0x4E,0xF7,0x00,0x1F,0xF4,0x1F,0x38,0x82,0x06,0x02,0x89,0x0F, +0x38,0x82,0x06,0x0A,0x44,0x42,0x30,0x20,0x38,0x82,0x15,0x01,0x38,0x54,0x86,0x02, +0x42,0x57,0xA0,0x73,0x38,0x54,0x86,0x0A,0xF5,0x1C,0x44,0x42,0x33,0x08,0x38,0x52, +0x15,0x01,0xF4,0x20,0x38,0x42,0x06,0x02,0x42,0x47,0x94,0x73,0xF5,0x20,0x38,0x42, +0x86,0x0A,0x8C,0x61,0xF5,0x0F,0x97,0x18,0xE2,0x85,0xE9,0xA3,0x22,0x35,0x00,0x00, +0x04,0x40,0x00,0x08,0xFF,0x9C,0x9B,0x74,0x44,0x30,0x03,0xE8,0xFE,0xEC,0x40,0x61, +0x98,0xD6,0xAE,0x82,0xAE,0x83,0x10,0x20,0x00,0x2C,0xF3,0x1B,0xF2,0x0D,0x12,0x60, +0x00,0x1A,0xAF,0xC0,0xAF,0xC1,0x10,0x70,0x00,0x2B,0x38,0x21,0x0D,0x11,0xB0,0xEE, +0x88,0x82,0x14,0x40,0x00,0x08,0x84,0x80,0x12,0x20,0x00,0x13,0x38,0x41,0x84,0x08, +0xD5,0x25,0xA6,0x43,0xE2,0x22,0xE8,0x03,0xAE,0x83,0xD5,0x05,0xA6,0x42,0xE2,0x41, +0xE8,0x02,0xAE,0x82,0x00,0x15,0x00,0x03,0xA6,0x81,0xE2,0x41,0xE8,0x03,0xAE,0x41, +0xD5,0x0D,0xA6,0x80,0xE2,0x22,0xE8,0x0A,0xAE,0x40,0xD5,0x08,0xB0,0x6B,0xF1,0x98, +0xB0,0x6A,0xF1,0x97,0xB0,0x69,0xF0,0x25,0xF1,0x96,0xF2,0x12,0x00,0x15,0x00,0x02, +0x42,0x70,0x88,0x73,0xF2,0x0D,0x38,0x21,0x1D,0x11,0xA0,0x47,0x88,0x22,0xA8,0x47, +0xF1,0x14,0xE0,0x22,0xE8,0x06,0x00,0x10,0x00,0x2D,0x8C,0x21,0x10,0x10,0x00,0x2D, +0x50,0x21,0x7F,0x9C,0x4E,0x27,0x00,0x26,0xF1,0x16,0xF3,0x16,0xB4,0x21,0x88,0x22, +0xB6,0x23,0x00,0x15,0x00,0x03,0x44,0x32,0x30,0x20,0x38,0x31,0x85,0x01,0xF1,0x17, +0xB4,0x21,0x42,0x11,0x0C,0x73,0xF3,0x17,0xB6,0x23,0x00,0x15,0x00,0x02,0x44,0x32, +0x33,0x08,0x38,0x31,0x85,0x01,0xF1,0x18,0xB4,0x21,0x42,0x11,0x0C,0x73,0xF2,0x18, +0xB6,0x22,0x00,0x10,0x00,0x2E,0x5A,0x10,0xFF,0x05,0x8C,0x21,0x10,0x10,0x00,0x2E, +0xF6,0x15,0x8D,0x44,0x8C,0xC1,0x96,0x30,0xF0,0x95,0x48,0xFF,0xFD,0x81,0xB0,0xB5, +0x38,0x51,0x02,0x02,0x4E,0x57,0x00,0x26,0x44,0x20,0x00,0x38,0x50,0xFF,0x82,0xF8, +0x42,0xF0,0x08,0x73,0x50,0x2F,0x81,0x00,0x88,0x85,0x38,0x21,0x02,0x02,0xF2,0x96, +0x40,0x22,0x84,0x0A,0xF2,0x97,0x04,0x8F,0x80,0x17,0xF2,0x16,0x89,0x02,0x50,0x2F, +0x81,0x2C,0x40,0x84,0x15,0x16,0x14,0x87,0x80,0x03,0x38,0x81,0x02,0x02,0xF2,0x17, +0x88,0x28,0x88,0x48,0x40,0x21,0x14,0x56,0x14,0x27,0x80,0x04,0xF2,0x16,0x88,0x62, +0x44,0x50,0x00,0x38,0x50,0x2F,0x82,0xF8,0x42,0x20,0x14,0x73,0xA1,0x57,0x04,0x8F, +0x80,0x15,0x41,0xEF,0x14,0x00,0x00,0x51,0x00,0x2E,0x89,0x05,0x80,0xA8,0x97,0x68, +0x04,0x8F,0x80,0x12,0xF5,0x95,0x00,0x51,0x00,0x2D,0x89,0x05,0x80,0xA8,0x00,0x81, +0x00,0x03,0x97,0x68,0xE2,0xC8,0xF5,0x92,0xE9,0x06,0xA7,0x52,0x81,0x06,0xE2,0xA7, +0x40,0x72,0xBC,0x1B,0xA7,0x51,0xE3,0x25,0xE9,0x06,0xA6,0x90,0x80,0xA9,0xE2,0x4A, +0x40,0xA1,0x3C,0x1B,0x8C,0x01,0x96,0x00,0x80,0xC8,0x81,0x25,0x48,0xFF,0xFD,0x40, +0x00,0x0F,0x80,0x9D,0x3C,0x13,0xF7,0xA7,0xE2,0x20,0xE8,0x07,0x3C,0x0B,0xF7,0xA7, +0x00,0x0F,0x80,0x40,0x3E,0x07,0xEF,0x15,0x44,0x10,0x00,0x38,0xF2,0x0E,0x50,0x0F, +0x82,0xF8,0x42,0x01,0x04,0x73,0x00,0x2F,0x80,0x40,0x10,0x20,0x00,0x32,0xF0,0x0E, +0xF2,0x0B,0x8C,0x01,0x97,0x40,0xF5,0x8E,0xDA,0xF2,0xF0,0x10,0xE6,0x0B,0x4E,0xF2, +0xFD,0x74,0x8C,0x01,0x96,0x00,0xF0,0x90,0x5A,0x08,0x0B,0x04,0x48,0xFF,0xFD,0x6D, +0xE6,0x4B,0x4E,0xF3,0xFD,0x54,0x48,0xFF,0xFD,0x68,0x84,0x08,0xF0,0x8F,0x48,0xFF, +0xFC,0x9E,0x92,0x00,0x3B,0xFF,0xFC,0xBC,0xEF,0xFC,0xFA,0x40,0x5A,0x08,0x04,0x0A, +0x44,0x12,0x10,0x30,0x44,0x02,0x2D,0x14,0x49,0x00,0x4E,0x78,0x48,0x00,0x00,0x08, +0x44,0x02,0x2D,0x14,0x44,0x12,0x10,0x20,0x49,0x00,0x4E,0x70,0xEC,0x04,0x3B,0xFF, +0xFC,0x84,0xDD,0x9E,0x3A,0x6F,0xA4,0xBC,0xEF,0xF4,0x3C,0x64,0x05,0x92,0xF0,0x81, +0x3C,0x94,0x05,0x93,0x3C,0x74,0x05,0x94,0x49,0x00,0x41,0x9D,0x5A,0x08,0x01,0x19, +0x3C,0x27,0xF9,0xF5,0x84,0x24,0x40,0x11,0x04,0x36,0x5E,0xF0,0x81,0xC2,0xE8,0x03, +0x44,0x10,0x01,0xC2,0xFA,0x1D,0x44,0xF0,0x00,0x64,0xFE,0x84,0x40,0x21,0x3C,0x56, +0x5E,0xF1,0x02,0x58,0x96,0x4B,0xE8,0x51,0x44,0x20,0x02,0x58,0xD5,0x4E,0x84,0xA0, +0x44,0x12,0x33,0x88,0x44,0x00,0x0C,0x8C,0x98,0xA9,0x00,0x21,0x01,0x0D,0xE6,0x42, +0xE8,0x06,0x50,0x52,0x81,0x24,0xD8,0xF9,0x84,0x20,0xD5,0x03,0x44,0x10,0x00,0x01, +0x3C,0x0D,0xFB,0xD6,0x3C,0x47,0xF9,0xF5,0x96,0x1E,0xC8,0x1B,0x5A,0x18,0x01,0x1A, +0x2E,0x10,0x11,0x99,0xE6,0x23,0xE9,0x15,0x5E,0xF2,0x01,0xF5,0xE8,0x12,0x84,0x1A, +0xFE,0x0C,0x50,0x00,0x00,0x64,0x42,0x14,0x80,0x24,0x44,0xF0,0x00,0x64,0x40,0x10, +0xBC,0x36,0x96,0x4B,0x9C,0x31,0xE0,0x20,0x40,0x10,0x3C,0x1B,0x40,0x90,0x80,0x11, +0xFA,0x62,0x44,0x20,0x00,0x64,0xFE,0xE4,0x40,0x31,0x88,0x76,0xE0,0x66,0x40,0x61, +0xBC,0x1A,0x84,0x24,0x44,0x30,0x00,0x55,0x40,0x12,0x04,0x36,0x42,0x02,0x0C,0x24, +0xE1,0x21,0x40,0x00,0x08,0x16,0x40,0x90,0xBC,0x1B,0x80,0x47,0xE0,0x07,0x97,0xB3, +0x50,0x14,0x80,0x00,0x40,0x20,0x3C,0x1A,0x3C,0x0D,0xFB,0xD6,0x96,0x93,0x54,0x00, +0x20,0x00,0xC0,0x15,0x84,0x03,0x84,0x62,0xFF,0x84,0x40,0x63,0x0C,0xD6,0xE0,0x26, +0x40,0x60,0xBC,0x1B,0xFE,0x44,0x40,0x10,0x8C,0x36,0xE0,0x41,0x40,0x11,0x3C,0x1B, +0xFE,0x84,0x40,0x21,0x0C,0x56,0x97,0xB3,0x96,0x4B,0x96,0x93,0xF0,0x01,0xEC,0x0C, +0x3C,0x6B,0xF7,0xBC,0x3A,0x6F,0xA4,0x84,0x3C,0x1B,0xF7,0xA6,0x3C,0x1B,0xF7,0xBD, +0x3C,0x2B,0xF7,0xBE,0x48,0xFF,0xFA,0xA6,0xFC,0x40,0xEE,0x98,0x44,0x62,0x33,0x90, +0x87,0x80,0x80,0xE6,0x81,0x3C,0x44,0x82,0x15,0xC0,0x84,0x02,0x10,0x03,0x81,0x06, +0x02,0x03,0x80,0x80,0x54,0xAE,0x00,0xFF,0xC0,0x38,0x00,0x03,0x81,0x05,0x5A,0x08, +0x02,0x2A,0xB0,0x41,0x84,0x40,0x80,0x07,0x49,0xFF,0xF3,0x6C,0xF0,0x01,0x40,0x14, +0x94,0x08,0xB0,0x82,0x88,0x22,0x22,0x20,0x00,0x04,0x22,0x33,0x80,0x46,0xA8,0x89, +0x22,0x20,0x00,0x05,0xA8,0x8A,0x50,0x20,0x80,0x08,0xA8,0xD1,0x80,0x41,0x22,0x33, +0x80,0x47,0x18,0xA1,0x00,0x10,0xA8,0xCC,0x04,0x33,0x80,0x3D,0xB4,0x00,0xA8,0xD1, +0x04,0x23,0x80,0x3C,0xA8,0x0F,0x50,0x04,0x80,0x01,0xA8,0x8E,0x54,0x90,0x00,0xFF, +0xD5,0x0C,0x5A,0x00,0x04,0xD8,0x5A,0x08,0x03,0x09,0x80,0x0A,0x49,0x00,0x10,0xB0, +0x38,0x14,0x70,0x00,0xE2,0x20,0xE8,0xCE,0x51,0xCE,0x00,0x01,0x50,0x73,0x81,0x24, +0x5B,0xC8,0x0B,0xBD,0x2E,0x07,0xF1,0x5C,0xC0,0x41,0x80,0x49,0x44,0x02,0x13,0x4C, +0xB0,0x42,0x84,0xE0,0x49,0x00,0x06,0x93,0x81,0x27,0x02,0x03,0x00,0x80,0xC0,0x28, +0x00,0x03,0x01,0x05,0x5A,0x08,0x01,0x25,0xB0,0x41,0x84,0x40,0x80,0x06,0x49,0xFF, +0xF3,0x21,0xF1,0x01,0x94,0x3D,0xB0,0x82,0x88,0x02,0x22,0x20,0x80,0x04,0x22,0x33, +0x00,0x46,0xA8,0x81,0x22,0x20,0x80,0x05,0x8C,0xE1,0xA8,0x82,0x50,0x20,0x00,0x08, +0xA8,0xD1,0x80,0x40,0x22,0x33,0x00,0x47,0x18,0x91,0x00,0x10,0xA8,0xC4,0x83,0x86, +0xBB,0x3D,0xB4,0x21,0xA8,0xD1,0xBA,0x3C,0xA8,0x47,0xA8,0x86,0x97,0xF8,0x50,0x04, +0x80,0x01,0x54,0x90,0x00,0xFF,0x50,0x63,0x01,0x24,0x5A,0x98,0x0B,0xD0,0x44,0x02, +0x13,0x4C,0xB0,0x42,0x80,0x47,0x49,0x00,0x06,0x5A,0xED,0x68,0xFC,0xC0,0xFC,0x47, +0xE6,0x2C,0x80,0xE1,0xF2,0x81,0x4E,0xF2,0x01,0x86,0x81,0x00,0x5A,0x20,0x01,0x04, +0x48,0x00,0x01,0x49,0x44,0x00,0x00,0x38,0x45,0xC2,0x13,0x4C,0x42,0x04,0x00,0x24, +0x40,0x9E,0x00,0x00,0xF0,0x82,0x04,0x04,0x80,0x0A,0x44,0x60,0x01,0x24,0xF0,0x87, +0x00,0x04,0x80,0x3A,0x10,0x14,0x80,0x3B,0x10,0x0F,0x80,0x30,0x22,0x04,0x80,0x0E, +0x22,0x14,0x80,0x0C,0x12,0x0F,0x80,0x13,0x12,0x0F,0x80,0x11,0x50,0x04,0x80,0x28, +0xF0,0x83,0xA4,0x05,0xFF,0xBC,0x44,0xA2,0x33,0x88,0x12,0x0F,0x80,0x19,0x50,0x03, +0x00,0x08,0x50,0x34,0x80,0x30,0x50,0x24,0x80,0x20,0x12,0x1F,0x80,0x12,0x12,0x1F, +0x80,0x10,0x88,0x0A,0xB0,0x47,0xF2,0x85,0xF3,0x84,0x49,0xFF,0xF2,0x7A,0xF3,0x04, +0x40,0x15,0x18,0x00,0x50,0x10,0x81,0x23,0xA6,0x1F,0x8D,0x28,0x54,0x40,0x00,0x0F, +0x95,0x64,0xA7,0x09,0x92,0x04,0x97,0x1F,0xFF,0x2F,0xAF,0x09,0x00,0x41,0x80,0x08, +0x96,0x0F,0x95,0x22,0xFE,0x27,0xAE,0x0A,0xF2,0x05,0x40,0x45,0x18,0x00,0x50,0x52, +0x01,0x10,0x05,0xE1,0x00,0x02,0x04,0x12,0x00,0x40,0xF0,0x03,0xE0,0x3E,0x40,0x1F, +0x3C,0x1B,0x14,0x12,0x00,0x40,0xA6,0x5C,0x44,0xF0,0x00,0x38,0xAE,0x6E,0x80,0x7C, +0x50,0x14,0x00,0x01,0x42,0x30,0xBC,0x73,0xA6,0x5E,0xA4,0x05,0xAE,0x6F,0xF1,0x02, +0xAC,0x2A,0x88,0x3C,0x80,0x1C,0xA0,0x4B,0x80,0x4A,0x14,0x12,0x00,0x39,0x02,0x12, +0x00,0x84,0xC9,0x1F,0x00,0x24,0x80,0x0E,0x50,0x03,0x00,0xE8,0x5A,0x28,0x01,0x14, +0x89,0x40,0xF0,0x02,0x88,0x1C,0x8C,0x10,0x3A,0x20,0x0C,0x00,0x00,0x04,0x80,0x0D, +0x3A,0x25,0x0C,0x20,0x5C,0xF0,0x00,0x29,0x4E,0xF2,0x00,0xD7,0x10,0x12,0x00,0xEC, +0x48,0x00,0x00,0xD3,0x88,0x0A,0x84,0x48,0x49,0x00,0x4C,0xCE,0x48,0x00,0x00,0xCD, +0x00,0x14,0x80,0x0D,0x5C,0xF0,0x80,0x29,0xE9,0x4D,0x20,0x52,0x00,0xEC,0x20,0x44, +0x80,0x0C,0x8A,0x85,0x5E,0xF2,0x00,0x5B,0xE9,0x04,0x50,0x42,0x7F,0x4C,0xD5,0x07, +0x5E,0xF2,0x7F,0xA6,0xE8,0x04,0x50,0x42,0x00,0xB4,0x97,0x23,0x80,0x64,0x4E,0x44, +0x00,0x03,0xFE,0xE2,0xE4,0x62,0xE9,0x36,0x84,0x21,0x4E,0x44,0x00,0x04,0x44,0x10, +0xFF,0xFF,0x8E,0x61,0xFE,0x5C,0x96,0x4B,0x80,0x61,0x4E,0x14,0x00,0x03,0xFE,0xCA, +0xE4,0x62,0xE9,0x04,0x84,0x62,0x40,0x10,0x8C,0x36,0x88,0x25,0x96,0x49,0x97,0x4B, +0x5E,0xF2,0x80,0x5B,0xE9,0x05,0x50,0x10,0xFF,0x4C,0x97,0x4B,0xD5,0x0A,0x5E,0xF2, +0xFF,0xA7,0xE8,0x07,0x50,0x10,0x80,0xB4,0x97,0x4B,0x5E,0xF2,0xFF,0xA7,0xE9,0x09, +0x5E,0xF2,0x80,0x5B,0x80,0x25,0xE9,0x03,0x44,0x10,0x00,0x5A,0x96,0x4A,0xD5,0x03, +0x44,0x1F,0xFF,0xA7,0x44,0x30,0x01,0x24,0x80,0x82,0x42,0x43,0x8C,0x73,0x10,0x12, +0x00,0xEC,0x44,0x10,0x00,0x38,0x80,0x80,0x42,0x44,0x04,0x73,0x00,0x12,0x00,0x16, +0x8C,0x88,0x5A,0x18,0x01,0x72,0x44,0x10,0x01,0x24,0x80,0x62,0x42,0x33,0x84,0x73, +0xA5,0x25,0x02,0x11,0x80,0x75,0x8A,0x81,0x80,0x64,0x4E,0x44,0x00,0x03,0xFE,0xE2, +0x5E,0xF1,0x80,0x33,0xE9,0x1E,0x4E,0x45,0x00,0x04,0x84,0x81,0xD5,0x02,0x84,0x9F, +0x50,0x31,0xFF,0xCE,0xFE,0xE4,0x84,0x82,0x40,0x31,0x90,0x76,0x88,0x23,0x4E,0x15, +0x00,0x09,0x5E,0xF0,0x93,0x89,0xE9,0x03,0x44,0x10,0x13,0x88,0x96,0x49,0xD5,0x02, +0x84,0x20,0x44,0x30,0x01,0x24,0x80,0x82,0x42,0x43,0x8C,0x73,0x12,0x12,0x00,0x75, +0x44,0x10,0x01,0x24,0x80,0x62,0x42,0x33,0x84,0x73,0x44,0x10,0x00,0x38,0x42,0x04, +0x04,0x73,0x02,0x41,0x80,0x74,0x02,0x30,0x00,0x08,0x8A,0x64,0x80,0x23,0x4E,0x34, +0x00,0x03,0xFE,0x5A,0x5E,0xF0,0x80,0x33,0xE9,0x2F,0x84,0x01,0x4E,0x34,0x00,0x03, +0x84,0x1F,0x50,0x10,0xFF,0xCE,0xFE,0x44,0x84,0x02,0x40,0x10,0x80,0x36,0x98,0x21, +0x4E,0x05,0x00,0x09,0x5E,0xF0,0x13,0x89,0xE9,0x03,0x44,0x00,0x13,0x88,0x96,0x01, +0xD5,0x02,0x84,0x00,0x44,0x10,0x01,0x24,0x42,0x23,0x84,0x73,0x12,0x01,0x00,0x74, +0xD5,0x13,0xF0,0x01,0x5A,0x08,0x02,0x37,0x44,0x00,0x01,0x24,0x44,0x62,0x33,0x90, +0x42,0x60,0x80,0x73,0x80,0x06,0xB0,0x46,0x84,0x40,0x49,0xFF,0xF1,0x93,0x80,0x06, +0xF1,0x06,0x49,0xFF,0xF1,0x56,0x44,0x02,0x33,0x88,0x44,0x10,0x01,0x24,0x80,0x40, +0x42,0x23,0x84,0x73,0x02,0x11,0x00,0x84,0xC9,0x0D,0x44,0x30,0x00,0x38,0x44,0x12, +0x13,0x4C,0x42,0x14,0x0C,0x73,0xA0,0xCE,0xA0,0x4F,0x12,0x31,0x00,0x8C,0x12,0x11, +0x00,0x8D,0x44,0x10,0x01,0x24,0x42,0x03,0x84,0x73,0x00,0x1F,0x80,0x04,0x02,0x50, +0x00,0x84,0x10,0x10,0x01,0x0E,0x44,0x10,0xFF,0xFF,0xD1,0x04,0x8C,0xA1,0x12,0x50, +0x00,0x84,0xFC,0xC7,0xFC,0x01,0x49,0x00,0x3D,0x9C,0xF0,0x81,0x49,0xFF,0xEF,0xDB, +0xE6,0x03,0xE9,0x04,0xF0,0x01,0x49,0xFF,0xF1,0x74,0xF0,0x01,0x44,0x12,0x33,0x48, +0x3C,0x24,0x05,0xAF,0x49,0xFF,0xF1,0x86,0xF0,0x01,0x44,0x12,0x33,0x48,0x44,0x22, +0x15,0xDA,0x44,0x32,0x15,0xD8,0x49,0xFF,0xF1,0xBC,0x3C,0x13,0xF7,0xC7,0x3C,0xF7, +0xF9,0xF5,0x3C,0x24,0x05,0xAF,0x84,0x1E,0xFE,0x0E,0xE0,0x4F,0x3C,0x0B,0xF7,0xC7, +0xE8,0x05,0x58,0x00,0x00,0x01,0x3C,0x0B,0xF7,0xC7,0xFC,0x81,0xFC,0x40,0x84,0x20, +0x84,0x4C,0x44,0x02,0x13,0x40,0x49,0x00,0x4B,0xA7,0x84,0x00,0x3C,0x0B,0xF7,0xC7, +0x84,0x01,0x49,0x00,0x0E,0x4E,0x84,0x03,0x49,0x00,0x0E,0x5E,0x84,0x20,0x44,0x20, +0x0C,0x94,0x44,0x02,0x33,0x88,0x49,0x00,0x4B,0x97,0x49,0x00,0x0E,0x9B,0x49,0x00, +0x2A,0xF6,0x5A,0x00,0x01,0x05,0x44,0x00,0x00,0x5A,0xD5,0x03,0x44,0x00,0x00,0x7D, +0x3C,0x08,0x05,0xAF,0x84,0x00,0x3E,0x07,0xF1,0x4C,0x84,0x01,0x49,0xFF,0xFC,0xDC, +0x44,0x00,0x00,0xB3,0x3C,0x08,0x05,0x9A,0x44,0x00,0x18,0x7F,0x3C,0x08,0x05,0x9B, +0x44,0x00,0x00,0xCA,0x3C,0x08,0x05,0x9C,0x44,0x00,0x31,0x9A,0x3C,0x08,0x05,0x9D, +0x49,0x00,0x02,0x01,0x84,0x01,0x49,0x00,0x02,0x09,0x84,0x04,0x3E,0x00,0x0B,0x44, +0x84,0x05,0x3E,0x00,0x0B,0x46,0x44,0x00,0x00,0x40,0x3E,0x00,0x0B,0x45,0x84,0x0A, +0x3E,0x00,0x0B,0x47,0x3C,0x70,0x05,0x9A,0x84,0x01,0x3E,0x00,0x0B,0x48,0x3C,0x50, +0x05,0x9C,0x3C,0x04,0x05,0x9B,0x97,0x3B,0x84,0x60,0x2E,0x97,0xDE,0x16,0x40,0xA0, +0x10,0x01,0x96,0x6B,0x3C,0x64,0x05,0x9D,0x80,0x43,0x44,0x82,0x30,0x20,0x8A,0x80, +0x45,0xE0,0x00,0x11,0x96,0x10,0xE2,0x09,0x88,0x6A,0xE8,0x09,0x98,0x23,0x40,0x00, +0x78,0x16,0x88,0x07,0x38,0x04,0x09,0x09,0x8C,0x41,0xD5,0xF5,0x84,0x60,0x9B,0xF1, +0x2E,0x47,0xDE,0x17,0x8A,0x26,0x80,0x43,0x44,0x92,0x33,0x08,0xFA,0xCF,0x96,0x10, +0xE2,0x04,0x88,0x67,0xE8,0x09,0x98,0x0B,0x40,0x00,0x18,0x16,0x88,0x05,0x38,0x04, +0x89,0x09,0x8C,0x41,0xD5,0xF5,0x3C,0x0D,0xFB,0xD6,0x58,0x00,0x08,0x00,0x3C,0x0F, +0xFB,0xD6,0x3C,0x1D,0xFB,0xD6,0x84,0x01,0x3C,0x0B,0xF7,0xC6,0x44,0x0F,0xFF,0x7F, +0xFE,0x0E,0x3C,0x0F,0xFB,0xD6,0x3C,0x0D,0xFB,0xD6,0x58,0x00,0x00,0x04,0x3C,0x0F, +0xFB,0xD6,0x84,0x00,0x3C,0x0B,0xF7,0xC5,0xFC,0xC0,0x84,0x00,0x3E,0x07,0xF1,0x4C, +0xDD,0x9E,0xFC,0x00,0x2E,0x07,0xE2,0x74,0x96,0x16,0xC0,0x03,0x84,0x04,0xD5,0x02, +0x84,0x01,0x49,0xFF,0xFC,0x59,0xFC,0x80,0xFC,0x00,0x84,0x01,0x49,0x00,0x01,0x96, +0xFC,0x80,0x84,0x00,0x3E,0x07,0xE2,0x74,0x3C,0x0D,0xFB,0xD6,0x96,0x1E,0xC0,0x04, +0x84,0x04,0x3E,0x07,0xE2,0x74,0x2E,0x07,0xE2,0x74,0xC8,0x04,0x84,0x01,0x3E,0x07, +0xE2,0x74,0xDD,0x9E,0xFC,0x00,0x49,0x00,0x29,0xDB,0xC8,0x05,0x2E,0x07,0xE2,0x74, +0x5A,0x08,0x04,0x04,0x84,0x04,0xD5,0x02,0x84,0x01,0x49,0xFF,0xFC,0x35,0x49,0xFF, +0xFF,0xE2,0x2E,0x07,0xE2,0x74,0x2E,0x57,0xF1,0x4C,0xD0,0x07,0x3E,0x07,0xF1,0x4C, +0x49,0xFF,0xFF,0xC9,0x49,0xFF,0xFF,0xD2,0xFC,0x80,0xFC,0x42,0x81,0x21,0x80,0xC2, +0xB0,0x41,0x84,0x40,0x81,0x40,0x80,0xE3,0x49,0xFF,0xF0,0x54,0xF2,0x01,0x22,0x15, +0x00,0x8A,0x22,0x05,0x00,0x8B,0x22,0x41,0x00,0x06,0x9B,0x0C,0x22,0x11,0x00,0x07, +0xF4,0x82,0x8A,0x01,0xF0,0x83,0xB0,0x02,0x49,0x00,0x08,0x27,0xE0,0x09,0xE8,0x1A, +0x84,0x20,0x84,0x4C,0x44,0x32,0x30,0x44,0x80,0x03,0x42,0x00,0x88,0x73,0x00,0x40, +0x00,0x0A,0xCC,0x0D,0xF1,0x01,0x22,0x20,0x80,0x06,0x22,0x10,0x80,0x07,0xB6,0x40, +0xA8,0x41,0x10,0x70,0x00,0x0A,0xAD,0x84,0x84,0x01,0xFC,0xC2,0x8C,0x21,0x5A,0x18, +0x0B,0xED,0x84,0x00,0xFC,0xC2,0xFC,0x41,0x3C,0x63,0xF9,0xF7,0x84,0x0A,0xFF,0x84, +0x2E,0x00,0x11,0x99,0x97,0xB1,0x3C,0x17,0xF9,0xF6,0xC0,0x07,0xC6,0x06,0x44,0x00, +0x00,0x64,0xFE,0x44,0x96,0x49,0xC9,0x0B,0x44,0x02,0x13,0x2C,0x84,0x20,0x84,0x4B, +0x49,0x00,0x4A,0x82,0x84,0x00,0x3E,0x07,0xF1,0x3A,0xFC,0xC1,0x2E,0x07,0xF1,0x3A, +0xC8,0x18,0x44,0x22,0x33,0x88,0x84,0x0B,0x52,0x40,0xB2,0x64,0x52,0x53,0x19,0x32, +0x00,0x31,0x01,0x0D,0xC3,0x17,0x22,0x71,0x00,0x8F,0x22,0x31,0x00,0x8E,0xE0,0xE4, +0xE8,0x11,0xE0,0xC3,0xE8,0x0F,0xE0,0x65,0xE8,0x0D,0x84,0x01,0x3E,0x07,0xF1,0x3A, +0x44,0x92,0x33,0x90,0x44,0xA2,0x13,0x2C,0x50,0x84,0x8C,0x8C,0x52,0x70,0xB2,0x64, +0xD5,0x07,0x8E,0x01,0x96,0x00,0x50,0x21,0x01,0x24,0xC8,0xE3,0xD5,0xF2,0x00,0x04, +0x81,0x05,0xC0,0x1F,0xB0,0x41,0x84,0x40,0x80,0x09,0x49,0xFF,0xEF,0xDB,0xF1,0x01, +0x2E,0x20,0x11,0x99,0xE6,0x42,0x22,0x00,0x80,0x08,0x22,0x10,0x80,0x09,0xE9,0x1E, +0x2E,0x27,0xF1,0x3A,0x5A,0x28,0x01,0x1B,0xE0,0xE1,0xE8,0x0E,0xE0,0x06,0xE9,0x05, +0x52,0x13,0x19,0x32,0xE0,0x20,0xE8,0x08,0x84,0x04,0x10,0x04,0x81,0x05,0x84,0x01, +0x10,0x05,0x00,0x00,0xD5,0x0B,0x00,0x05,0x00,0x00,0x5A,0x08,0x01,0x08,0x84,0x00, +0x10,0x05,0x00,0x00,0x84,0x02,0x10,0x04,0x81,0x05,0x50,0x94,0x81,0x24,0x8D,0x41, +0x4C,0x94,0x7F,0xCF,0xFC,0xC1,0x84,0xA0,0x80,0x25,0x44,0x32,0x33,0x88,0x44,0x20, +0x0C,0x8C,0x98,0x2B,0x00,0x00,0x01,0x0D,0x8E,0x02,0xE6,0x02,0xE8,0x03,0x8C,0x21, +0x96,0x48,0x50,0x52,0x81,0x24,0xDA,0xF6,0xC1,0x05,0x84,0x01,0x3C,0x0F,0xFC,0x52, +0xDD,0x9E,0x3C,0x0D,0xFC,0x52,0x5A,0x08,0x01,0x07,0x3E,0x07,0xF4,0xA8,0x3C,0x1F, +0xFC,0x52,0xDD,0x9E,0x3C,0x1F,0xFC,0x52,0x80,0x01,0xDD,0x9E,0x4E,0x05,0x00,0x22, +0xE4,0x02,0xE9,0x20,0x40,0x50,0x08,0x0A,0x84,0x22,0xCD,0x05,0x8E,0x22,0x96,0x4A, +0x84,0x40,0xD5,0x07,0xD0,0xFC,0x8C,0x22,0x96,0x4A,0x40,0x50,0x04,0x0E,0xD5,0xF6, +0x4E,0x15,0x00,0x0E,0x94,0x91,0x9C,0xD1,0x40,0x50,0x04,0x0E,0x42,0xF1,0x8C,0x24, +0xE0,0xAF,0x8E,0x22,0x40,0x21,0xBC,0x1A,0x96,0x4A,0xD5,0xF3,0x80,0x02,0xDD,0x9E, +0x84,0x00,0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xFF,0xDB,0xFC,0x80,0x9A,0xCB,0x8A,0x02, +0xFE,0xDC,0x42,0x30,0x00,0x73,0x44,0x00,0x00,0x64,0x40,0x01,0x80,0x16,0xDD,0x9E, +0xFE,0x5C,0x42,0x10,0x08,0x73,0x80,0x01,0xDD,0x9E,0xB4,0x20,0x84,0x61,0x4E,0x14, +0x00,0x03,0x84,0x7F,0x4E,0x14,0x00,0x03,0xFE,0x4A,0x84,0x40,0x40,0x40,0x88,0x0E, +0xC4,0x04,0x8C,0x41,0x96,0x90,0xD5,0xFB,0x52,0x21,0x00,0x1F,0x96,0x90,0x40,0x10, +0x88,0x0C,0xFE,0x5C,0xB6,0x20,0x80,0x02,0xDD,0x9E,0x46,0x18,0x00,0x00,0x4C,0x00, +0x80,0x11,0xC0,0x10,0x4E,0x04,0x00,0x03,0xFE,0x02,0xFA,0xAF,0x40,0x10,0x14,0x0E, +0x96,0x46,0xC9,0x04,0x8E,0xA1,0x97,0x68,0xD5,0xFA,0x9C,0x29,0x96,0x00,0xDD,0x9E, +0xFA,0x10,0xDD,0x9E,0x84,0x60,0x80,0x43,0xE2,0x61,0xE8,0x07,0x38,0x40,0x0D,0x11, +0x8C,0x61,0x88,0x44,0x96,0xD8,0xD5,0xF9,0x4E,0x26,0x00,0x03,0x84,0x41,0x40,0x21, +0x04,0x56,0x84,0x80,0x96,0x93,0x80,0x64,0xE2,0x81,0xE8,0x09,0x38,0x50,0x11,0x11, +0x8C,0x81,0x8A,0xA2,0x42,0x32,0x94,0x73,0x97,0x20,0xD5,0xF7,0xFC,0x00,0x80,0x03, +0x4E,0x36,0x00,0x03,0x84,0x01,0x40,0x00,0x04,0x16,0x49,0xFF,0xFF,0x9D,0x96,0x03, +0xFC,0x80,0x84,0x00,0x84,0x20,0x3E,0x07,0xF4,0x04,0x3C,0x1F,0xFD,0x00,0x3E,0x17, +0xF1,0x51,0x3E,0x07,0xF3,0xFD,0xDD,0x9E,0xFC,0x00,0x5A,0x08,0x04,0x07,0x44,0x02, +0x2D,0x2C,0x44,0x12,0x10,0x58,0xD5,0x05,0x44,0x02,0x2D,0x2C,0x44,0x12,0x10,0x50, +0x84,0x48,0x49,0x00,0x49,0x33,0xFC,0x80,0xA7,0x08,0xA6,0xC0,0xE2,0x64,0x40,0x32, +0x3C,0x1A,0xAE,0xD0,0xA7,0x01,0xA6,0xC9,0xE2,0x83,0x40,0x32,0x3C,0x1A,0xAE,0xD1, +0xA7,0x0A,0xA6,0xC2,0xA6,0x4B,0xA6,0x03,0xE2,0x64,0x40,0x32,0x3C,0x1A,0xE2,0x01, +0x40,0x10,0x3C,0x1A,0xAE,0xD2,0xAE,0x53,0xDD,0x9E,0xFC,0x48,0x51,0xCF,0x80,0x14, +0x80,0xE1,0x81,0x20,0x84,0x20,0x81,0x42,0x80,0x1C,0xFA,0x5C,0x81,0x05,0xF3,0x83, +0x80,0xC4,0x49,0x00,0x49,0x31,0xA6,0x7B,0x2F,0xE7,0xDE,0x16,0xA6,0x3A,0x80,0x41, +0x42,0x20,0x78,0x73,0x38,0x54,0x89,0x01,0x8A,0xA8,0x96,0xAB,0xF2,0x81,0x4E,0x22, +0x00,0x82,0x44,0x22,0x30,0x20,0x44,0x40,0x00,0xFF,0x38,0x11,0x05,0x01,0x85,0x20, +0xF1,0x82,0x44,0x12,0x33,0x08,0x80,0x4A,0x38,0x80,0x81,0x01,0x80,0x1C,0x45,0xC0, +0xFF,0xFF,0x40,0x11,0x28,0x01,0xF3,0x03,0x96,0x48,0xE2,0x23,0xE8,0x27,0x08,0x41, +0x00,0x01,0x80,0x66,0x44,0x10,0x00,0x38,0x42,0x32,0x04,0x73,0x00,0xF1,0x80,0x30, +0x5A,0xF8,0x01,0xF1,0x38,0x10,0x12,0x02,0xC9,0xED,0xA0,0x5B,0xA0,0xDC,0xF5,0x02, +0x40,0x34,0x0C,0x01,0x9A,0x69,0xFE,0xDC,0x42,0x30,0x84,0x73,0x92,0x63,0xC3,0x07, +0x40,0xFE,0x0C,0x06,0xE8,0x05,0x44,0x30,0xFF,0xFF,0xD5,0x02,0x80,0x6F,0x40,0x3E, +0x0C,0x77,0x38,0x30,0x12,0x0A,0x89,0x23,0xD5,0xD5,0x84,0x20,0x50,0x23,0x00,0x1C, +0x80,0xA1,0xB0,0x05,0x55,0xC0,0x80,0xFF,0x38,0x30,0x06,0x02,0xC3,0x20,0xF4,0x01, +0xB4,0x02,0xFE,0xE4,0x04,0x41,0x7F,0xFC,0x04,0xFF,0x80,0x02,0x40,0x31,0xA4,0x77, +0xFF,0x04,0x40,0xA0,0x0C,0x00,0x42,0x41,0xBC,0x73,0x40,0x42,0x28,0x97,0x14,0x41, +0x7F,0xFC,0x04,0x41,0x7F,0xFD,0x8C,0xA1,0xFE,0x24,0x42,0x01,0xA0,0x73,0x40,0x00, +0x28,0x17,0x97,0x68,0x14,0x01,0x7F,0xFD,0xB7,0x42,0x80,0x9C,0x8C,0x21,0x50,0x21, +0x00,0x38,0x5A,0x18,0x0B,0xD8,0x5A,0x58,0x01,0x16,0x44,0x00,0x00,0x38,0x42,0x62, +0x00,0x73,0x00,0x03,0x00,0x2E,0xF2,0x18,0x8C,0x01,0x10,0x03,0x00,0x2E,0xA6,0x3B, +0xA6,0x7A,0x88,0x02,0x42,0x00,0xF8,0x73,0x00,0x13,0x00,0x29,0x58,0x10,0x80,0x20, +0xAE,0x40,0xFC,0xC8,0xFC,0x47,0x3C,0x0D,0xFB,0xD6,0x54,0x00,0x20,0x00,0xC8,0x06, +0x44,0x00,0x03,0x20,0x44,0x20,0x08,0x34,0xD5,0x05,0x44,0x00,0x00,0xC8,0x44,0x20, +0x04,0x1A,0x3C,0x1D,0xFB,0xD6,0x54,0x10,0xA0,0x00,0xC9,0x04,0x44,0x70,0x05,0xDC, +0xD5,0x03,0x44,0x70,0x02,0xEE,0x3C,0x1D,0xFB,0xD6,0x54,0x10,0xA0,0x00,0xC9,0x03, +0x84,0x22,0xD5,0x02,0x84,0x21,0x3C,0x33,0xF7,0xC5,0x96,0xC6,0xC3,0x04,0xFA,0x64, +0xF3,0x81,0xD5,0x02,0xF1,0x81,0x42,0xA1,0x08,0x24,0xF2,0x01,0xFE,0x04,0x42,0x31, +0x28,0x24,0x44,0x20,0x00,0x64,0x40,0x00,0x08,0x16,0xFF,0xFC,0xF0,0x85,0x42,0x00, +0x9C,0x24,0xF0,0x82,0x40,0x00,0x08,0x16,0xF0,0x86,0x46,0x00,0x08,0x95,0x50,0x00, +0x04,0x40,0x40,0x31,0x88,0x76,0xFE,0x0C,0xF3,0x84,0xF0,0x83,0x44,0x62,0x33,0x90, +0x85,0x20,0x02,0x03,0x00,0x80,0x4E,0x02,0x00,0xBF,0xB0,0x49,0x84,0x40,0x80,0x06, +0x49,0xFF,0xED,0xE0,0xB0,0x4A,0x84,0x41,0x80,0x06,0x49,0xFF,0xED,0xDB,0xB0,0x4B, +0x84,0x42,0x80,0x06,0x49,0xFF,0xED,0xD6,0xB0,0x4C,0x84,0x43,0x80,0x06,0x49,0xFF, +0xED,0xD1,0xB0,0x4D,0x84,0x44,0x80,0x06,0x49,0xFF,0xED,0xCC,0xF1,0x09,0x87,0xC4, +0x22,0x20,0x80,0x04,0x22,0x00,0x80,0x05,0x12,0x20,0x80,0x08,0x12,0x00,0x80,0x09, +0x02,0x33,0x00,0x80,0xE6,0x65,0xE8,0x04,0x9E,0x59,0x41,0xE0,0x80,0x13,0x44,0x40, +0x01,0x24,0x44,0x12,0x34,0xA8,0x42,0x14,0x90,0x73,0xA0,0x49,0x4E,0x15,0x00,0x25, +0x22,0xF3,0x00,0x89,0x22,0x43,0x00,0x88,0x8A,0x0F,0x8A,0x44,0xFE,0x04,0x42,0x01, +0x08,0x73,0x44,0x22,0x71,0x00,0xE0,0x40,0xE8,0x17,0x40,0x00,0xD8,0x08,0x00,0x53, +0x01,0x18,0x90,0x1A,0xD8,0x06,0x96,0x4B,0x00,0x53,0x01,0x19,0x90,0x2A,0xD1,0x0C, +0x44,0x10,0x01,0x24,0x44,0x02,0x34,0xA8,0x42,0x04,0x84,0x73,0xA6,0x87,0x44,0x1F, +0xFF,0x80,0xFE,0x57,0xAE,0x47,0x44,0x40,0x00,0x49,0x84,0x00,0x42,0x24,0x90,0x24, +0x80,0x20,0xB1,0x49,0xF2,0x87,0xE2,0x3E,0xE8,0x2F,0x9C,0x89,0x38,0x42,0x86,0x02, +0x38,0x82,0x8A,0x02,0x38,0x22,0x86,0x02,0x22,0xF1,0x00,0x04,0x22,0x24,0x00,0x04, +0x22,0x84,0x00,0x05,0x40,0x27,0x88,0x01,0x22,0xF2,0x00,0x05,0x40,0x87,0xA0,0x01, +0xE6,0x22,0xE8,0x10,0xF4,0x07,0x40,0xF2,0x04,0x00,0x50,0xF7,0x80,0x24,0x40,0xF7, +0x88,0x08,0x44,0x42,0x33,0x88,0x40,0xF2,0x3C,0x00,0x12,0x27,0x80,0x02,0x12,0x87, +0x80,0x03,0x42,0x84,0x20,0x24,0x42,0x81,0x08,0x73,0xE0,0x08,0x8C,0x21,0x40,0x04, +0x3C,0x1B,0x96,0x48,0xD5,0xD1,0xE6,0x62,0xE8,0x03,0xF0,0x04,0xD5,0x0B,0x44,0x20, +0x01,0x24,0x44,0x12,0x34,0xA8,0x42,0x14,0x88,0x73,0xA0,0x49,0x4E,0x15,0x00,0x06, +0xF0,0x05,0x14,0x03,0x00,0x3D,0xD5,0x1D,0x98,0x78,0xE0,0x27,0xE9,0x05,0xE1,0x41, +0x40,0x15,0x3C,0x1B,0xD5,0x02,0x80,0x27,0xF2,0x01,0x94,0x02,0xFE,0x54,0x44,0x20, +0x00,0x64,0x40,0x10,0x88,0x36,0x14,0x13,0x00,0x3D,0xF1,0x02,0xE0,0x01,0xE9,0x08, +0xF1,0x03,0xE0,0x20,0x40,0x10,0x3C,0x1A,0x40,0x00,0x88,0x16,0xD5,0x02,0xF0,0x06, +0x14,0x03,0x00,0x3C,0x8D,0x21,0x50,0x63,0x01,0x24,0x5A,0x90,0x0B,0x04,0x48,0xFF, +0xFF,0x3A,0xFC,0xC7,0xFC,0x42,0x44,0x62,0x30,0x44,0x81,0x20,0x81,0x41,0x81,0x02, +0x83,0x83,0x50,0x73,0x00,0x84,0x00,0x03,0x00,0x0A,0xC8,0x06,0x8C,0xCC,0x4C,0x63, +0xFF,0xFC,0x84,0x00,0xFC,0xC2,0xB4,0x26,0x40,0x04,0x84,0x01,0xB6,0x1F,0xA0,0x31, +0x40,0x14,0x04,0x01,0x40,0x25,0x00,0x01,0x40,0x0E,0x00,0x01,0xF0,0x83,0x80,0x1F, +0xF1,0x82,0xF2,0x81,0x49,0x00,0x04,0xE1,0xA4,0x74,0xE0,0x01,0xE8,0xE8,0xB0,0x02, +0x49,0x00,0x04,0xDB,0xA4,0x74,0xE0,0x20,0xE8,0xE2,0x84,0x01,0xFC,0xC2,0xFC,0x43, +0x94,0xDD,0x40,0xA0,0x8C,0x00,0x80,0xE0,0x00,0x05,0x00,0x00,0x44,0x80,0x01,0x24, +0x42,0x00,0x20,0x24,0x8C,0x08,0x44,0x62,0x33,0x88,0x81,0x22,0x88,0x06,0xB0,0x41, +0x84,0x40,0x49,0xFF,0xEC,0xDF,0x2E,0x00,0x11,0x99,0xE6,0x03,0xE9,0x03,0x84,0x00, +0xFC,0xC3,0xF2,0x01,0x44,0x00,0x00,0x38,0x42,0x74,0x80,0x73,0x22,0x11,0x00,0x08, +0xA0,0x3E,0x8A,0x01,0xF0,0x82,0x22,0x01,0x00,0x09,0xA0,0xBF,0x8A,0x40,0xF2,0x83, +0x00,0x25,0x00,0x00,0x42,0x61,0x20,0x73,0x50,0x23,0x01,0x00,0x22,0x31,0x00,0x02, +0x8A,0x23,0xF1,0x84,0x22,0x11,0x00,0x03,0x8A,0x01,0x04,0x13,0x80,0x0A,0xF0,0x85, +0x44,0x00,0x00,0x64,0xFE,0x0C,0x04,0x13,0x00,0x40,0x40,0x00,0x04,0x16,0x5E,0xF0, +0x00,0x33,0xE8,0xD6,0xE4,0x1F,0xE8,0x04,0x44,0x60,0x01,0x90,0xD5,0x03,0x44,0x60, +0x02,0x8A,0xB0,0x04,0x49,0x00,0x04,0x89,0x5E,0xF0,0x02,0x58,0xE8,0xC9,0xB0,0x02, +0x49,0x00,0x04,0x83,0x40,0x03,0x00,0x07,0xFC,0xC3,0xFC,0x40,0x51,0xFF,0xFD,0xE8, +0x81,0x00,0xA6,0x00,0xF1,0x84,0x4E,0x02,0x01,0x84,0x4E,0x22,0x01,0x82,0xE6,0x0C, +0x80,0x20,0xE9,0x02,0x84,0x2B,0x96,0x08,0xE6,0x4C,0xF0,0x83,0x80,0x02,0xE9,0x02, +0x84,0x0B,0x96,0x00,0xF0,0x82,0x46,0x30,0x50,0x00,0x84,0x00,0xB0,0x8D,0x84,0x20, +0x88,0x40,0x38,0x31,0x06,0x0A,0x8C,0x21,0x5A,0x18,0x0B,0xFD,0x50,0x00,0x00,0x2C, +0x5A,0x09,0xE4,0xF6,0x3C,0x03,0xF7,0xC5,0x96,0x06,0x56,0x00,0x00,0x01,0xF0,0x89, +0x49,0x00,0x25,0xDD,0xC0,0x04,0x3C,0x03,0xF7,0xC5,0x96,0x06,0xF0,0x85,0xF0,0x04, +0x87,0x80,0x9D,0x84,0x81,0x3C,0x54,0x0E,0x00,0xFF,0xF0,0x88,0xB0,0x4D,0xFA,0x1C, +0x42,0x1E,0x00,0x73,0x84,0x00,0x50,0xA4,0x00,0x18,0xF1,0x87,0xF0,0x81,0x00,0x0F, +0x80,0x04,0xF0,0x8A,0x84,0x00,0xF0,0x8C,0x00,0x05,0x00,0x23,0x5A,0x00,0xFF,0x04, +0x48,0x00,0x00,0xD7,0xB4,0xE6,0xA1,0x31,0xA0,0xB2,0xA0,0xF3,0x88,0x47,0x88,0x64, +0x04,0x15,0x00,0x01,0xB4,0x0A,0x49,0xFF,0xFC,0xE3,0xA1,0x36,0xF0,0x86,0x04,0x15, +0x00,0x01,0xB4,0x0A,0xB4,0x46,0xA0,0xF1,0xF4,0x8B,0x04,0x75,0x00,0x04,0x49,0xFF, +0xFF,0x1B,0x5A,0x08,0x01,0x04,0x48,0x00,0x00,0xBC,0x80,0x08,0xF1,0x04,0xF2,0x0A, +0xF3,0x08,0x49,0xFF,0xFF,0x3E,0x5A,0x08,0x01,0x04,0x48,0x00,0x00,0xB2,0xF4,0x0B, +0xE0,0xE4,0x80,0x04,0x40,0x03,0xBC,0x1A,0xE0,0x87,0x94,0x05,0x40,0x43,0xBC,0x1A, +0x40,0x70,0x10,0xF6,0xF0,0x05,0x5A,0x08,0x01,0x32,0x00,0x04,0x00,0x00,0x5A,0x08, +0x01,0x2E,0x00,0x13,0x7F,0xFC,0x44,0x00,0x01,0x24,0xFE,0x0C,0x44,0x12,0x33,0x88, +0x88,0x20,0x00,0x10,0x81,0x0D,0x5A,0x18,0x02,0x22,0x2E,0x10,0x11,0x99,0x5A,0x18, +0x01,0x1E,0x44,0x12,0x34,0xA8,0x88,0x01,0x00,0x25,0x7F,0xF4,0xA0,0x01,0x40,0x10, +0x58,0x08,0x90,0x3A,0x96,0x48,0x96,0x03,0xE2,0x22,0x90,0x0A,0xE9,0x0F,0x00,0x25, +0x7F,0xF5,0xE2,0x41,0xE9,0x0B,0x00,0x15,0x7F,0xF6,0x96,0x00,0xE2,0x01,0xE9,0x06, +0x00,0x15,0x7F,0xF7,0xE2,0x20,0x4E,0xF2,0x00,0xD3,0xF5,0x06,0x46,0x00,0x50,0x00, +0xE0,0x05,0xE8,0x02,0x80,0xA0,0xF0,0x09,0xC0,0x0A,0x5E,0xF3,0x80,0x20,0xE9,0x07, +0x5E,0xF3,0x80,0x41,0xE9,0x05,0x44,0x70,0x00,0x40,0xD5,0x02,0xFA,0xF0,0xA0,0x34, +0xE0,0x05,0xE8,0x5A,0xA0,0x75,0xE0,0x01,0xE8,0x5B,0xB4,0x6A,0xB4,0x46,0x04,0x45, +0x00,0x01,0x9A,0x9A,0xA0,0x73,0xA0,0xF1,0xA0,0x32,0x9A,0xE3,0x42,0x40,0x84,0x24, +0x42,0x40,0x00,0x73,0x42,0x91,0x8C,0x24,0xF5,0x8A,0xF4,0x86,0x42,0x91,0x08,0x73, +0x49,0xFF,0xFC,0x68,0xF4,0x06,0xF5,0x0A,0x4E,0x04,0x00,0x03,0xFE,0x02,0x44,0x10, +0x7F,0xFF,0xE0,0x20,0xE8,0x08,0xE0,0x89,0x90,0x01,0xE8,0x03,0x91,0x22,0xD5,0xFA, +0x90,0x82,0xD5,0xF8,0x4E,0x92,0x00,0x23,0xC4,0x23,0xFE,0x04,0xF0,0x8C,0xB0,0x0C, +0xF4,0x8B,0xF5,0x8A,0x49,0xFF,0xFC,0x53,0xF0,0x86,0xF0,0x0C,0x40,0x90,0x25,0x36, +0xB0,0x0C,0x14,0x9F,0x80,0x0C,0x49,0xFF,0xFC,0x4A,0xF1,0x06,0xF4,0x0B,0x88,0x01, +0x96,0x40,0xF0,0x0C,0xF5,0x0A,0x40,0x40,0x10,0x96,0xE6,0x3F,0xE9,0x05,0x9E,0x09, +0x90,0x81,0x96,0x40,0xD5,0xFB,0xF4,0x8C,0xD5,0x04,0x80,0x29,0xD5,0x02,0x80,0x24, +0x84,0x01,0x40,0x00,0x04,0x0C,0x90,0x07,0xF1,0x0C,0x94,0x06,0xE0,0x01,0xE8,0x08, +0xA0,0x35,0xE0,0x05,0xE9,0x05,0xFF,0x7C,0x40,0x92,0x94,0x0A,0xD5,0x03,0x46,0x90, +0x50,0x00,0xF1,0x01,0xF0,0x07,0x50,0xA5,0x00,0x38,0x38,0x90,0x06,0x0A,0x80,0x01, +0x8C,0x01,0xF0,0x81,0x00,0x0F,0x80,0x04,0xF1,0x03,0xE2,0x01,0x4E,0xF3,0xFF,0x11, +0x51,0xCE,0x00,0x01,0xF1,0x02,0x54,0x0E,0x00,0xFF,0xE2,0x01,0x50,0x63,0x00,0x20, +0x4E,0xF3,0xFE,0xFB,0xF0,0x02,0xF1,0x03,0xF2,0x03,0xE2,0x01,0xF1,0x02,0xB1,0x8D, +0x40,0x11,0x3C,0x1B,0x80,0x06,0x49,0xFF,0xEE,0x5F,0x44,0x70,0x01,0x24,0x44,0x22, +0x2F,0x5C,0x84,0x20,0x84,0x0B,0x46,0x30,0x50,0x00,0x44,0x40,0x00,0x38,0x44,0x52, +0x33,0x88,0x85,0x41,0x87,0x9F,0x0C,0x91,0x00,0x01,0x81,0xE9,0x42,0xF0,0x80,0x73, +0x38,0xF3,0x3E,0x02,0x4C,0xF1,0xC0,0x05,0x15,0xC1,0x7F,0xFF,0xD5,0x18,0x4E,0x95, +0x00,0x17,0x05,0xEF,0x80,0x04,0x40,0xF0,0x94,0x08,0x38,0xFF,0x3C,0x00,0x83,0xC8, +0x43,0xE4,0x90,0x73,0x81,0x25,0x42,0x97,0x9C,0x73,0x10,0xFF,0x00,0x3B,0x81,0xE9, +0x10,0xA4,0x81,0x0E,0x04,0x91,0x7F,0xFF,0x10,0x97,0x81,0x22,0x8C,0x21,0x04,0xFF, +0x80,0x02,0x54,0x90,0x80,0xFF,0xE3,0x2F,0xE9,0xD7,0xD5,0x0A,0xF0,0x06,0xA0,0x75, +0x40,0x00,0x1C,0x16,0x94,0x05,0xE0,0x01,0x4E,0xF2,0xFF,0x29,0xD5,0x9B,0x51,0xFF, +0x82,0x18,0xFC,0xC0,0xFC,0x40,0x84,0x81,0x40,0x42,0x04,0x0C,0xE4,0x02,0x40,0x20, +0x04,0x0C,0x95,0xA1,0x9D,0xE5,0x50,0x92,0x7F,0xFB,0xE9,0x23,0x84,0x60,0x90,0x01, +0x8C,0x61,0x5A,0x08,0x01,0xFE,0x40,0x21,0x0C,0x0E,0x40,0x01,0x84,0x0C,0x85,0x00, +0xE0,0x49,0xE9,0x03,0xE0,0xE2,0xE8,0x16,0x80,0x68,0xE0,0x46,0xE8,0x06,0xFE,0x94, +0x40,0x21,0x04,0x0E,0x8C,0x61,0xD5,0xFA,0x81,0x03,0x81,0x44,0x84,0xA0,0xD3,0x04, +0x91,0x41,0x8C,0xA1,0xD5,0xFD,0xE4,0xAA,0x88,0x0A,0x90,0x41,0xE9,0xEA,0xD5,0x02, +0x84,0x00,0xFC,0xC0,0xFC,0x58,0xF0,0x8F,0x44,0x02,0x00,0x98,0xF1,0x88,0xB0,0x5B, +0xF2,0x81,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24,0xA4,0x00,0xAC,0x08,0x44,0x12, +0x00,0xAC,0xB0,0x17,0x3A,0x20,0x94,0x00,0x3A,0x20,0x14,0x20,0xF0,0x01,0xC8,0x09, +0x3C,0x04,0x05,0x9B,0x3C,0x14,0x05,0x9A,0x9A,0x41,0x2E,0x07,0xDE,0x16,0xD5,0x08, +0x3C,0x04,0x05,0x9D,0x3C,0x14,0x05,0x9C,0x9A,0x41,0x2E,0x07,0xDE,0x17,0x8E,0x01, +0x96,0x02,0x40,0x00,0x80,0x16,0x96,0x03,0xF0,0x8D,0x84,0x00,0x44,0x62,0x13,0x4C, +0xF0,0x84,0xF5,0x04,0xF1,0x08,0x4C,0x50,0x82,0x0D,0x00,0x03,0x00,0x3C,0x4E,0x02, +0x02,0x01,0x04,0x03,0x00,0x0A,0xF0,0x85,0x4E,0x07,0x01,0xFC,0xF0,0x01,0xC8,0x08, +0x00,0x53,0x00,0x37,0x4E,0x52,0x01,0xF6,0x2E,0x07,0xDE,0x16,0xD5,0x07,0x00,0x53, +0x00,0x38,0x4E,0x52,0x01,0xEF,0x2E,0x07,0xDE,0x17,0x8E,0x01,0x4C,0x50,0x01,0xEA, +0x00,0x03,0x00,0x37,0xF0,0x82,0x00,0x03,0x00,0x38,0xF0,0x83,0x84,0x02,0xF0,0x8E, +0x84,0x20,0x84,0x46,0xB0,0x11,0x49,0x00,0x44,0xF7,0xB0,0x20,0x84,0x20,0xFA,0x42, +0x49,0x00,0x44,0xF2,0x84,0x00,0x50,0xAF,0x80,0x6C,0xF0,0x86,0x00,0x05,0x00,0x00, +0xF1,0x02,0xF0,0x89,0x88,0x01,0x96,0x00,0xF0,0x87,0x20,0x0F,0x80,0x1C,0x20,0x15, +0x00,0x01,0x4E,0x05,0x00,0xD1,0xF2,0x03,0x96,0x48,0x99,0xCA,0x97,0xF8,0xF1,0x8A, +0x96,0x7A,0x4E,0x15,0x00,0xC9,0x2E,0x97,0xDE,0x16,0xE0,0x09,0x4E,0xF2,0x00,0xC4, +0x2E,0x27,0xDE,0x17,0xE0,0x22,0xF2,0x8B,0x4E,0xF2,0x00,0xBE,0x42,0x00,0xA4,0x73, +0xF2,0x0F,0x94,0x41,0x88,0x22,0xF1,0x8C,0x22,0x10,0x80,0x00,0x84,0x60,0x4E,0x15, +0x00,0x06,0xF2,0x06,0x80,0x61,0x88,0x41,0xF2,0x86,0xF2,0x08,0x5A,0x20,0x02,0x04, +0x48,0x00,0x00,0x9A,0x2E,0x57,0xF1,0x9A,0x2E,0x27,0xF1,0xD2,0x4C,0x51,0x40,0x94, +0x2E,0x47,0xF1,0x96,0x2E,0x27,0xF1,0xCE,0x88,0x44,0xE4,0x54,0x4E,0xF2,0x00,0x8C, +0x45,0xC2,0x30,0xC8,0x44,0x8F,0xFF,0xDF,0x38,0x0E,0x00,0x00,0x84,0x21,0x40,0x00, +0x20,0x02,0x3E,0x17,0xF4,0x13,0x5A,0x00,0x1F,0x04,0x48,0x00,0x00,0x81,0x84,0x20, +0xFA,0x5C,0xB0,0x25,0x49,0x00,0x44,0x98,0x84,0x40,0x80,0x9C,0x80,0xA8,0x80,0x22, +0x51,0xEF,0x80,0x5C,0x44,0x82,0x2F,0xF0,0x51,0xCF,0x80,0x54,0xF3,0x07,0x38,0x01, +0x78,0x00,0x88,0x03,0x96,0x00,0x40,0x3F,0x08,0x00,0xE2,0x09,0x20,0x31,0x80,0x01, +0xE8,0x18,0x88,0x67,0x04,0xFF,0x80,0x0B,0x96,0xD8,0xE2,0x6F,0xE8,0x12,0x88,0x04, +0x42,0x01,0xA4,0x73,0xA6,0x00,0x40,0x30,0x14,0x02,0x5A,0x30,0x1F,0x0B,0x96,0x27, +0x38,0x34,0x00,0x00,0x5A,0x30,0xFF,0x06,0x38,0x0E,0x04,0x08,0x8C,0x21,0x96,0x49, +0x8C,0x42,0x5A,0x28,0x10,0xDD,0xF2,0x07,0x44,0x02,0x30,0x20,0x50,0x8F,0x80,0x54, +0x38,0x90,0x09,0x01,0x44,0x02,0x33,0x08,0x45,0xC0,0x00,0x38,0x39,0xE0,0x1D,0x01, +0x84,0x00,0x80,0x60,0x44,0x70,0xFF,0xFF,0xE2,0x01,0xE8,0x29,0x38,0x44,0x00,0x00, +0x44,0x52,0x13,0x58,0x42,0x52,0x70,0x73,0x00,0xF2,0x80,0x30,0x5A,0xF8,0x01,0x1D, +0xB0,0xA5,0x38,0x21,0x12,0x02,0xCA,0x18,0xA0,0xAB,0xA1,0x6C,0x40,0x24,0x88,0x01, +0x40,0x5F,0x14,0x01,0xFF,0x6C,0x42,0x51,0x08,0x73,0x92,0xA5,0xC5,0x06,0xE2,0xE5, +0xE8,0x05,0x44,0x50,0xFF,0xFF,0xD5,0x02,0x80,0xAF,0xB0,0xA5,0x40,0x53,0x94,0xB7, +0x38,0x51,0x12,0x0A,0x88,0x65,0x8C,0x01,0x96,0x00,0xD5,0xD7,0xF0,0x0C,0xF2,0x04, +0x22,0x10,0x00,0x00,0xB0,0x25,0x38,0x00,0x0A,0x02,0xFE,0x0C,0x40,0x30,0x0C,0x77, +0x96,0xDB,0xD5,0x05,0x84,0x00,0x3E,0x07,0xF4,0x13,0x80,0x61,0xF0,0x0A,0xF1,0x09, +0x8C,0x01,0x8C,0x21,0x96,0x48,0x96,0x00,0x84,0x43,0x42,0x00,0x88,0x73,0xB0,0x60, +0x38,0x30,0x81,0x09,0x8D,0x42,0x50,0x0F,0x80,0x7E,0x4C,0xA0,0x7F,0x21,0xB0,0xA0, +0x84,0x20,0x50,0xAF,0x80,0x44,0x84,0x00,0xF3,0x01,0x38,0x41,0x01,0x01,0xCB,0x07, +0x38,0x35,0x05,0x01,0x88,0x64,0x38,0x35,0x05,0x09,0xD5,0x06,0x38,0x35,0x01,0x01, +0x88,0x64,0x38,0x35,0x01,0x09,0x8C,0x01,0x5A,0x08,0x03,0xF0,0x8C,0x21,0x8C,0x46, +0x5A,0x18,0x03,0xEB,0xF0,0x08,0x5A,0x08,0x01,0x25,0x22,0x0F,0x80,0x23,0x22,0xFF, +0x80,0x22,0xE0,0x0F,0xE8,0x09,0xF0,0x01,0xC8,0x04,0xF0,0x02,0x8E,0x01,0xD5,0x0C, +0xF0,0x03,0x8E,0x01,0xD5,0x0E,0x22,0xFF,0x80,0x24,0xE0,0x0F,0xE8,0x0C,0xF0,0x01, +0xC8,0x06,0xF0,0x02,0x8C,0x01,0x96,0x00,0xF0,0x82,0xD5,0x05,0xF0,0x03,0x8C,0x01, +0x96,0x00,0xF0,0x83,0xF0,0x0E,0x5A,0x00,0x01,0x05,0x84,0x01,0x48,0xFF,0xFE,0xD1, +0x2E,0x07,0xF4,0x13,0x5A,0x08,0x01,0x07,0xA0,0x36,0x83,0x86,0xB8,0x88,0xA0,0x37, +0xB8,0x89,0xF0,0x06,0x44,0x70,0x00,0x64,0xF1,0x05,0xFF,0xC4,0x44,0x00,0x00,0x55, +0xFE,0x0C,0xE0,0xE0,0x4E,0xF3,0x00,0x9E,0xF0,0x06,0xE0,0x01,0xE8,0x08,0x40,0x73, +0x84,0xF6,0x50,0x93,0xFF,0xAB,0x52,0x73,0x80,0x64,0xD5,0x03,0x84,0xE0,0x85,0x21, +0x22,0x15,0x00,0x00,0x22,0x05,0x00,0x01,0xE0,0x01,0xE8,0x03,0x12,0x15,0x00,0x01, +0x22,0x0F,0x80,0x24,0x22,0x2F,0x80,0x23,0xE0,0x40,0xE8,0x03,0x12,0x0F,0x80,0x23, +0x88,0x01,0x22,0x2F,0x80,0x23,0x44,0x10,0x00,0x64,0xFE,0x0C,0x44,0x10,0x00,0x82, +0xFE,0x54,0xE0,0x20,0xE9,0x1B,0x44,0x10,0x00,0x5A,0xFE,0x54,0xE0,0x01,0xE9,0x18, +0x84,0x7E,0x94,0x51,0xFE,0x9C,0x40,0x10,0x04,0x36,0x40,0x00,0x08,0x16,0x50,0x10, +0xFF,0xD3,0x50,0x00,0x00,0x41,0x42,0x30,0x1C,0x24,0x42,0xF4,0x84,0x24,0xE0,0x6F, +0x40,0x70,0xBC,0x1B,0x40,0x90,0x3C,0x1B,0xD5,0x03,0x84,0xE1,0x85,0x20,0x85,0x00, +0x51,0xCF,0x80,0x4C,0xB0,0x11,0x38,0x00,0x21,0x11,0x4E,0x06,0x00,0x06,0xB0,0x51, +0x84,0x01,0x38,0x00,0xA1,0x09,0x38,0x05,0x21,0x11,0x84,0x28,0x49,0xFF,0xFD,0xE4, +0x38,0x0E,0x21,0x09,0x8D,0x01,0x5A,0x88,0x03,0xEF,0x02,0x0E,0x00,0x01,0x22,0x1E, +0x00,0x02,0x22,0x2E,0x00,0x00,0x94,0x01,0x8A,0x01,0x8A,0x02,0x96,0x03,0xC0,0x08, +0x8A,0x22,0xF2,0x0D,0x94,0x01,0xFE,0x54,0x40,0x00,0x80,0x16,0x96,0x03,0xF1,0x01, +0x40,0x24,0x9C,0x00,0xC9,0x18,0xF3,0x02,0x44,0x12,0x30,0x20,0x38,0x10,0x8D,0x11, +0x88,0x01,0xA0,0x76,0xFF,0xCC,0x42,0x70,0x24,0x73,0x40,0x73,0x88,0xF6,0x4E,0x75, +0x00,0x08,0x3C,0x23,0xEF,0x08,0xE0,0x47,0x40,0x71,0x3C,0x1B,0xD5,0x02,0xF7,0x01, +0xA9,0xF6,0xD5,0x17,0xF3,0x03,0x44,0x12,0x33,0x08,0x38,0x10,0x8D,0x11,0x88,0x01, +0xA0,0x77,0xFF,0xCC,0x42,0x70,0x24,0x73,0x40,0x73,0x88,0xF6,0x4E,0x75,0x00,0x08, +0x3C,0x03,0xEF,0x09,0xE0,0x07,0x40,0x70,0x3C,0x1B,0xD5,0x02,0x84,0xE0,0xA9,0xF7, +0xF0,0x04,0x50,0x63,0x00,0x38,0x8C,0x01,0x96,0x00,0xF0,0x84,0x48,0xFF,0xFD,0xF3, +0xFC,0xD8,0xFC,0x41,0x80,0xE0,0x84,0x48,0x80,0xC1,0x80,0x1F,0x84,0x20,0x49,0x00, +0x43,0x0B,0x84,0x60,0x3C,0x9D,0xFB,0xA4,0x2E,0x4F,0xF4,0x12,0x50,0x03,0x80,0x1C, +0x80,0xA3,0x80,0x43,0xD6,0x18,0x00,0x10,0x00,0x14,0x5A,0x18,0x01,0x10,0xE6,0x42, +0xB4,0xE0,0xE8,0x03,0x38,0x7F,0x8A,0x0A,0x00,0x10,0x00,0x12,0x8C,0x41,0x88,0x61, +0xE1,0x27,0x96,0x90,0x96,0xD9,0xE8,0x02,0x84,0x80,0x8C,0xA1,0x97,0x68,0x50,0x00, +0x00,0x38,0xD5,0xE9,0x5A,0x28,0x02,0x17,0xE6,0x79,0xE8,0x14,0xF1,0x01,0xB4,0x1F, +0x80,0xA1,0xE0,0x01,0x40,0x50,0x3C,0x1A,0xE0,0x20,0x40,0x00,0xBC,0x1B,0x84,0x25, +0xFE,0x0C,0x40,0xF0,0x09,0xF6,0xE0,0xAF,0xE8,0x05,0x8C,0x81,0x3E,0x47,0xF4,0x12, +0xD5,0x11,0x8E,0x81,0xE6,0x79,0x3E,0x47,0xF4,0x12,0xE9,0x0C,0x3C,0x03,0xFA,0x08, +0xE6,0x19,0xE9,0x08,0x84,0x01,0x3C,0x0F,0xFD,0x03,0x84,0x00,0x3E,0x07,0xF4,0x12, +0xD5,0x07,0x2E,0x07,0xF4,0x08,0xC8,0x04,0xCA,0x03,0x3C,0x2F,0xFD,0x03,0x2E,0x17, +0xF4,0x12,0x3E,0x27,0xF4,0x08,0x80,0x01,0x96,0x4A,0xE4,0x2C,0x3C,0x3B,0xFA,0x08, +0xE9,0x03,0x84,0x0B,0xD5,0x05,0x96,0x42,0x4E,0x14,0x00,0x03,0x84,0x00,0x96,0x42, +0x3E,0x07,0xF4,0x12,0xC1,0x0A,0x3C,0x0D,0xFD,0x03,0xC8,0x07,0x3C,0x03,0xF7,0xC7, +0x58,0x00,0x00,0x40,0x3C,0x0B,0xF7,0xC7,0xFC,0xC1,0xFC,0x01,0x44,0x62,0x13,0x4C, +0x84,0x40,0xA6,0x70,0xF0,0x81,0x49,0xFF,0xFD,0x47,0xF0,0x01,0xA6,0x70,0x84,0x41, +0x49,0xFF,0xFD,0x42,0xFC,0x81,0xFC,0x00,0xB4,0x40,0x80,0xA2,0x4E,0x24,0x00,0x03, +0xFF,0x52,0x84,0xC0,0x50,0x43,0x00,0x0F,0x40,0x12,0x90,0x0E,0xC1,0x03,0x8C,0xC1, +0xD5,0xFA,0xA0,0x41,0x80,0x61,0x4E,0x14,0x00,0x03,0xFE,0xCA,0x40,0x31,0x90,0x0E, +0xCB,0xF7,0x40,0x21,0x18,0x0E,0x40,0x10,0x98,0x0E,0xB6,0x40,0xA8,0x41,0x42,0x00, +0x84,0x24,0x42,0x01,0x08,0x73,0x49,0xFF,0xF8,0x97,0x40,0x00,0x18,0x0C,0xFC,0x80, +0xFC,0x41,0x80,0xC0,0x81,0x23,0x22,0x00,0x80,0x08,0x22,0x31,0x00,0x08,0x8A,0x03, +0xB6,0x1F,0x22,0x00,0x80,0x09,0x22,0x11,0x00,0x09,0x8A,0x01,0xF0,0x81,0x80,0x1F, +0x49,0xFF,0xFF,0xCB,0x80,0xE0,0x80,0x06,0x49,0xFF,0xFF,0xC7,0x94,0x79,0xE0,0x20, +0xE8,0x3C,0x84,0xA0,0x80,0x25,0x80,0x45,0xFA,0x71,0x4C,0x54,0x80,0x09,0x40,0x13, +0x94,0x1A,0xFE,0x5C,0x90,0x25,0x88,0x41,0xE0,0x02,0xE8,0x04,0xFA,0x30,0x84,0x40, +0xD5,0x04,0x8C,0xA1,0x97,0x68,0xD5,0xF2,0x50,0x30,0xFF,0xE0,0x96,0xD8,0xE2,0x69, +0xE8,0x04,0x88,0x41,0x8C,0x21,0xD5,0xF9,0xE2,0xA9,0xE9,0x0C,0xB4,0x26,0xA0,0x31, +0x94,0x4D,0x94,0x05,0x40,0x10,0x88,0x36,0x40,0x00,0x08,0x16,0xB6,0x26,0xA8,0x31, +0xD5,0x12,0x5A,0x50,0x01,0x13,0xB4,0x26,0xFA,0x51,0xFE,0x7C,0x40,0x10,0x80,0x36, +0xFE,0x54,0x90,0x25,0xB6,0x26,0xA0,0x71,0xFF,0xCC,0x40,0x03,0x80,0x16,0xFE,0x84, +0x90,0x45,0xA8,0xB1,0x84,0x01,0xFC,0xC1,0x84,0x00,0xFC,0xC1,0xFC,0x22,0x80,0xC0, +0x50,0x70,0x0C,0x8C,0x00,0x03,0x01,0x05,0xC0,0x42,0xB0,0x41,0x84,0x40,0x80,0x06, +0x49,0xFF,0xE7,0x98,0xF0,0x01,0x22,0x20,0x00,0x04,0x22,0x10,0x00,0x05,0xAC,0x86, +0xAC,0x47,0x12,0x20,0x00,0x08,0x12,0x10,0x00,0x09,0x02,0x03,0x00,0x80,0x5A,0x08, +0x01,0x07,0x12,0x23,0x00,0x8A,0x12,0x13,0x00,0x8B,0xD5,0x1E,0x02,0x33,0x00,0x81, +0x5A,0x38,0x01,0x26,0xE6,0x04,0xE8,0x23,0x22,0x03,0x00,0x8A,0x9A,0x82,0x22,0x03, +0x00,0x8B,0xF2,0x82,0x9A,0x41,0xB0,0x02,0xF1,0x83,0x49,0xFF,0xFF,0x56,0x5E,0xF0, +0x00,0x64,0xE8,0x15,0xF0,0x01,0x22,0x10,0x00,0x04,0x12,0x13,0x00,0x8A,0x22,0x00, +0x00,0x05,0x12,0x03,0x00,0x8B,0x04,0x03,0x00,0x47,0x40,0x10,0x58,0x08,0x96,0x03, +0x90,0x3A,0x90,0x0A,0x10,0x13,0x01,0x18,0x10,0x03,0x01,0x19,0x50,0x63,0x01,0x24, +0x4C,0x63,0xFF,0xBA,0xFC,0xA2,0xFC,0x46,0xFA,0x44,0x80,0xC0,0x00,0xA0,0x80,0x0C, +0x00,0x80,0x80,0x0D,0xB0,0x02,0x84,0x20,0xB1,0xC7,0x49,0x00,0x41,0xBD,0x80,0x07, +0x84,0x20,0xFA,0x44,0x49,0x00,0x41,0xB8,0x51,0xC3,0x0C,0x8C,0x00,0x03,0x01,0x05, +0x5A,0x08,0x02,0x69,0x85,0x24,0x80,0x06,0xB0,0x41,0x80,0x49,0x49,0xFF,0xE7,0x3A, +0x4E,0xA2,0x00,0x0D,0x5A,0xA8,0x01,0x15,0xF0,0x01,0xB0,0x82,0x22,0x10,0x00,0x06, +0x38,0x11,0x26,0x0A,0x22,0x00,0x00,0x07,0xD5,0x09,0xF0,0x01,0xB0,0x82,0x22,0x10, +0x00,0x04,0x38,0x11,0x26,0x0A,0x22,0x00,0x00,0x05,0x38,0x03,0xA6,0x0A,0x4E,0x92, +0x00,0x07,0x50,0x04,0xFF,0xFF,0x54,0x90,0x00,0xFF,0xD5,0xDE,0xF1,0x01,0x5A,0x80, +0x01,0x05,0x5A,0x80,0x02,0x08,0xD5,0x38,0x02,0x03,0x00,0x81,0xE6,0x02,0xE8,0x12, +0xD5,0x33,0x02,0x03,0x00,0x81,0x5A,0x08,0x02,0x18,0x02,0x03,0x00,0x80,0xE6,0x02, +0xE9,0x2B,0x04,0x03,0x00,0x48,0x96,0x0E,0xC0,0x05,0xF0,0x03,0xAC,0x0E,0xA0,0x39, +0xD5,0x22,0xF2,0x02,0xF0,0x03,0x88,0x02,0x90,0x01,0xAC,0x0E,0xB4,0x47,0xA0,0x39, +0x88,0x02,0x90,0x01,0xD5,0x18,0xE6,0x03,0xE9,0x17,0x02,0x03,0x00,0x80,0xE6,0x03, +0xE9,0x13,0xF2,0x02,0xF0,0x04,0x88,0x02,0x40,0x20,0x08,0x0A,0xF0,0x03,0x90,0x01, +0x88,0x02,0xAC,0x0E,0xB4,0x47,0xA0,0x3A,0x88,0x02,0x40,0x20,0x08,0x0A,0xA0,0x39, +0x90,0x01,0x88,0x02,0xAC,0x0F,0xA4,0x0E,0x12,0x00,0x80,0x08,0xA4,0x0F,0x12,0x00, +0x80,0x09,0x50,0x63,0x01,0x24,0x4C,0x6E,0x7F,0x93,0xFC,0xC6,0xFC,0x40,0xB4,0xE0, +0xA1,0x42,0xA1,0x09,0xB4,0xC1,0x04,0x90,0x00,0x01,0xA0,0x0A,0x42,0x33,0x90,0x24, +0x42,0x12,0x90,0x24,0x42,0x10,0x24,0x75,0x42,0x33,0x24,0x75,0xFE,0x3C,0x42,0x03, +0x14,0x75,0xCB,0x05,0xC9,0x0C,0x5C,0x00,0x00,0x01,0xFC,0xC0,0x40,0x00,0x0C,0x16, +0x40,0x10,0x8C,0x36,0xA8,0x11,0xB6,0x22,0x84,0x02,0xFC,0xC0,0x80,0x03,0xFC,0xC0, +0xFC,0x49,0x81,0x40,0x00,0x0F,0x80,0x68,0x81,0x21,0xF0,0x83,0x84,0x00,0xF0,0x87, +0xF0,0x88,0xF0,0x89,0xF0,0x8A,0xF0,0x8B,0xF0,0x8C,0x02,0x05,0x00,0x80,0x80,0xC2, +0xE6,0x06,0xF3,0x85,0x83,0x84,0xF5,0x82,0x80,0xE0,0xE9,0x02,0x84,0xE5,0x84,0x03, +0xFE,0x34,0x97,0xF9,0xF0,0x84,0x85,0x00,0x40,0x14,0x08,0x08,0xB0,0x8D,0x88,0x22, +0x80,0x0A,0x54,0x24,0x00,0xFF,0x8D,0x01,0x49,0xFF,0xE6,0x8C,0x5A,0x88,0x05,0xF6, +0xF2,0x0D,0xF0,0x0E,0xF4,0x09,0x22,0x31,0x00,0x06,0x22,0x10,0x00,0x08,0x85,0x00, +0x9A,0x59,0x22,0x31,0x00,0x07,0x22,0x20,0x00,0x09,0xF1,0x81,0x41,0xE1,0x88,0x01, +0xF1,0x87,0x15,0xEF,0x80,0x08,0xF3,0x0A,0xB1,0x4D,0x50,0x24,0x00,0x01,0x96,0x90, +0xE2,0x47,0xE8,0x17,0x50,0x81,0x7F,0xFF,0x38,0xF2,0xA2,0x02,0x38,0x82,0x8A,0x02, +0x22,0x17,0x80,0x06,0x22,0x84,0x00,0x08,0x8A,0x28,0x22,0x87,0x80,0x07,0x38,0xF2, +0x8A,0x02,0x88,0x81,0x22,0xF7,0x80,0x09,0x8B,0x0F,0x88,0x68,0x81,0x02,0xD5,0xE6, +0xE6,0xE3,0xF4,0x89,0xF3,0x8A,0xE9,0x11,0xF3,0x0F,0x22,0x40,0x00,0x06,0xF1,0x01, +0x22,0x21,0x80,0x08,0x22,0x00,0x00,0x07,0x8A,0x44,0x88,0x22,0xF1,0x8B,0x22,0x11, +0x80,0x09,0x9A,0x08,0x88,0x1E,0xF0,0x8C,0xF1,0x09,0x4E,0x14,0x00,0x03,0xFE,0x4A, +0x44,0x00,0x7F,0xFF,0xE0,0x01,0x80,0x20,0xE9,0x07,0xF0,0x0A,0x4E,0x04,0x00,0x03, +0xFE,0x02,0xE0,0x20,0xE8,0x04,0x84,0x00,0xF0,0x89,0xF0,0x8A,0xF1,0x0B,0x4E,0x14, +0x00,0x03,0xFE,0x4A,0x44,0x00,0x7F,0xFF,0xE0,0x01,0x80,0x20,0xE9,0x07,0xF0,0x0C, +0x4E,0x04,0x00,0x03,0xFE,0x02,0xE0,0x20,0xE8,0x04,0x84,0x00,0xF0,0x8B,0xF0,0x8C, +0xB0,0x07,0x49,0xFF,0xFE,0x02,0x4E,0x07,0x00,0x06,0xB0,0x07,0x49,0xFF,0xFD,0xFD, +0xD5,0x02,0x84,0x01,0xF1,0x04,0xE0,0x20,0xE9,0x1F,0xE0,0xC0,0xE8,0x1A,0xF2,0x07, +0xF4,0x08,0x50,0x13,0x7F,0xF6,0x96,0x49,0x42,0x31,0x04,0x24,0xFE,0x64,0x40,0x31, +0x80,0x76,0x40,0x00,0x80,0x16,0x8A,0x43,0x9A,0x20,0x84,0x63,0xFE,0x9C,0x84,0xA2, +0xFE,0xC4,0x40,0x21,0x14,0x56,0x40,0x31,0x94,0x76,0xF2,0x87,0xF3,0x88,0xD5,0x04, +0x84,0x00,0xF0,0x87,0xF0,0x88,0xB0,0x0B,0x49,0xFF,0xFD,0xD7,0x4E,0x07,0x00,0x06, +0xB0,0x0B,0x49,0xFF,0xFD,0xD2,0xD5,0x02,0x84,0x01,0x04,0x25,0x00,0x47,0x46,0x11, +0x00,0x00,0xFE,0x56,0xC1,0x04,0x84,0x00,0xF0,0x8B,0xD5,0x16,0x84,0x23,0x95,0xB2, +0x40,0x63,0x04,0xD6,0x96,0x71,0xE0,0x20,0xE8,0xF7,0xF2,0x0B,0x8E,0xCA,0x97,0xB1, +0xF1,0x0C,0x42,0x31,0x18,0x24,0x40,0x31,0x80,0x76,0xFF,0x8C,0x8A,0x43,0x40,0x03, +0x00,0x16,0xF2,0x8B,0x9A,0x08,0xF0,0x8C,0xB0,0x09,0x49,0xFF,0xFD,0xAE,0x4E,0x07, +0x00,0x06,0xB0,0x09,0x49,0xFF,0xFD,0xA9,0xD5,0x02,0x84,0x01,0x04,0x35,0x00,0x47, +0x46,0x11,0x00,0x00,0xFE,0xCE,0xC3,0x05,0x84,0x00,0xF0,0x89,0xF0,0x8A,0xD5,0x15, +0xF1,0x05,0x94,0x8A,0x96,0x51,0xE0,0x20,0xE8,0x12,0xF4,0x09,0xF2,0x0A,0x92,0x21, +0x42,0x52,0x04,0x24,0xFE,0x54,0x40,0x52,0x80,0xB6,0x40,0x10,0x80,0x36,0x8A,0x85, +0x9A,0x51,0xF4,0x89,0xF1,0x8A,0x80,0x03,0x80,0x60,0xD5,0x31,0x5A,0x88,0x04,0x27, +0xF0,0x07,0xC8,0x24,0xF1,0x08,0xC9,0x25,0xF0,0x0B,0xC8,0x22,0xF1,0x0C,0xC9,0x21, +0x84,0x21,0x80,0x01,0x80,0x61,0xB0,0x8D,0xE2,0x27,0xE8,0x16,0x9F,0x09,0x38,0x51, +0x12,0x02,0x38,0x41,0x06,0x02,0x22,0xA2,0x80,0x06,0x22,0x62,0x00,0x06,0x4C,0xA3, +0x00,0x03,0x84,0x60,0x22,0x52,0x80,0x07,0x22,0x42,0x00,0x07,0xD4,0x02,0x84,0x00, +0x8C,0x21,0x96,0x48,0xD5,0xEA,0xCB,0x07,0xD5,0x04,0x80,0x03,0xD5,0x02,0x80,0x01, +0x84,0x60,0xF3,0x89,0xC8,0x03,0xF0,0x8A,0xD5,0x02,0x84,0x01,0xF1,0x09,0xF2,0x02, +0xF5,0x03,0x42,0x41,0x04,0x24,0x84,0x24,0xF2,0x07,0x40,0x42,0x04,0x96,0x42,0x4E, +0x08,0x73,0xF2,0x0B,0xF7,0x02,0x42,0x42,0x88,0x73,0xF2,0x0A,0xF6,0x08,0xFE,0xBC, +0x40,0x11,0x04,0x36,0x42,0x1E,0x18,0x73,0xF2,0x0C,0xF6,0x03,0x44,0x50,0x00,0x64, +0x42,0x13,0x08,0x73,0x40,0x42,0x14,0x96,0x40,0x10,0x94,0x36,0x5A,0x38,0x01,0x15, +0xF2,0x0D,0x22,0x51,0x00,0x06,0x3C,0x27,0xF7,0x3C,0x4C,0x22,0x80,0x08,0x3C,0x37, +0xF7,0x3D,0x3C,0x23,0xEF,0x08,0x8A,0x43,0xDA,0x07,0xF2,0x0E,0x22,0x21,0x00,0x08, +0x8A,0xA2,0xB6,0xA9,0xD5,0x02,0xB6,0x89,0x5A,0x08,0x01,0x16,0xF0,0x0D,0x22,0x50, +0x00,0x07,0x3C,0x07,0xF7,0x42,0x4C,0x02,0x80,0x08,0x3C,0x27,0xF7,0x43,0x3C,0x03, +0xEF,0x09,0x8A,0x02,0xD8,0x08,0xF0,0x0E,0x22,0x00,0x00,0x09,0x8A,0xA0,0x14,0x54, +0x80,0x01,0xFC,0xC9,0x14,0x14,0x80,0x01,0xFC,0xC9,0xFC,0x43,0xB1,0xC2,0xB5,0x00, +0x3A,0x23,0x94,0x20,0x00,0x4F,0x80,0x38,0x04,0x90,0x00,0x01,0x4E,0x42,0x00,0x8E, +0x4E,0x85,0x00,0x03,0x80,0x43,0x4E,0x95,0x00,0x04,0xA1,0xFB,0xD5,0x02,0xA1,0xFA, +0x5A,0x48,0x03,0x26,0x80,0x28,0x4E,0x84,0x00,0x04,0x52,0x14,0x00,0x00,0xE0,0x41, +0xE9,0x04,0x84,0x20,0xB6,0x20,0xD5,0x08,0x84,0x21,0x4E,0x84,0x00,0x03,0x84,0x3F, +0x42,0x81,0x04,0x75,0xB7,0x00,0x80,0x29,0x4E,0x94,0x00,0x04,0x52,0x14,0x80,0x00, +0xE0,0xE1,0xE9,0x04,0x84,0x20,0xA8,0x41,0xFC,0xC3,0x84,0x21,0x4E,0x94,0x00,0x03, +0x84,0x3F,0x42,0x93,0x84,0x75,0x14,0x90,0x00,0x01,0xFC,0xC3,0x81,0x40,0x5A,0x40, +0x02,0x04,0x80,0xC1,0xD5,0x38,0x42,0x53,0xA0,0x24,0x42,0x41,0x24,0x24,0x84,0xC0, +0xFE,0xEA,0x52,0xF2,0x00,0x00,0x51,0xC3,0x00,0x0F,0x80,0x25,0x4E,0x54,0x00,0x03, +0x80,0x23,0x40,0x10,0xF0,0x0E,0xC1,0x04,0x8C,0xC1,0x97,0xB0,0xD5,0xF5,0x80,0x04, +0x4E,0x44,0x00,0x03,0x80,0x0F,0x40,0x00,0x70,0x0E,0xC8,0xF7,0x40,0x42,0x18,0x0E, +0x40,0x52,0x98,0x0E,0x42,0x02,0x10,0x24,0x42,0x02,0x94,0x73,0xF2,0x81,0x49,0xFF, +0xF5,0x5B,0x40,0x60,0x18,0x0C,0xF2,0x01,0xC6,0x30,0x42,0x04,0xA4,0x24,0x42,0x04, +0x20,0x73,0xFF,0xD4,0x49,0xFF,0xF5,0x50,0xFE,0x3C,0x40,0x60,0x18,0xD6,0x4E,0x64, +0x00,0x03,0xFF,0xB2,0x80,0x0A,0x49,0xFF,0xFC,0x90,0x84,0x65,0x42,0x13,0x0C,0x24, +0xE0,0x20,0xE9,0x1B,0xE0,0xC0,0xE9,0x06,0x84,0x00,0xB6,0x0A,0x14,0x05,0x00,0x01, +0xFC,0xC3,0x42,0x24,0x18,0x24,0x42,0x64,0x98,0x24,0x40,0x21,0x00,0x56,0xB4,0x8A, +0x40,0x63,0x00,0xD6,0x80,0x2A,0xA0,0x09,0x9A,0xA2,0x9B,0x86,0xFE,0x9C,0xFE,0xF4, +0x90,0x42,0x90,0x62,0xB6,0x4A,0xA8,0xC9,0xFC,0xC3,0x84,0x4A,0xFE,0x54,0xE0,0x20, +0xE8,0x04,0x8E,0x0A,0x96,0x01,0xDD,0x9E,0x96,0x09,0xDD,0x9E,0xB4,0x60,0x4E,0x35, +0x00,0x0B,0xE0,0x23,0xE9,0x08,0xA0,0x01,0x4E,0x05,0x00,0x06,0xE0,0x40,0x56,0x07, +0x80,0x01,0xDD,0x9E,0x84,0x00,0xDD,0x9E,0xA7,0x60,0x5C,0xF2,0x80,0x3C,0xE8,0x0F, +0x95,0x6A,0x88,0xA3,0xAE,0x2B,0xA6,0x20,0x94,0x02,0x88,0x03,0xAE,0x42,0xA6,0x20, +0x38,0x21,0x82,0x09,0xA6,0x20,0x8C,0x01,0xAE,0x20,0xDD,0x9E,0xFC,0x40,0x84,0xC0, +0x44,0x70,0x7F,0xFF,0x80,0x86,0x38,0x91,0x9A,0x11,0x54,0xA3,0x00,0xFF,0xE0,0xE9, +0x8C,0xC1,0x40,0x74,0xBC,0x1A,0x54,0x93,0x00,0xFF,0x40,0x45,0x3C,0x1A,0xE3,0x25, +0xE9,0xF3,0xE0,0xE2,0xE8,0x06,0x95,0x22,0x88,0x64,0xAE,0x1B,0xAE,0x5A,0xAC,0x98, +0xFC,0xC0,0xFC,0x5A,0x81,0x22,0x00,0x2F,0x80,0xF8,0x12,0x1F,0x80,0x19,0xF2,0x89, +0x00,0x2F,0x80,0xFC,0xF1,0x40,0xF2,0x85,0x00,0x2F,0x81,0x04,0x12,0x0F,0x80,0x18, +0xF2,0x87,0x84,0x00,0x2E,0x27,0xDE,0x16,0xAC,0x08,0xF2,0x81,0xF1,0x05,0x2E,0x27, +0xDE,0x17,0xF3,0x86,0xB6,0x9F,0xF5,0x83,0xF2,0x88,0x5A,0x18,0x01,0x04,0xF1,0x3D, +0xAE,0x08,0x85,0x01,0x87,0x80,0x4E,0x82,0x01,0x42,0x50,0x04,0x7F,0xFF,0xB0,0x4C, +0xB0,0x8C,0x38,0x60,0x82,0x11,0x94,0x42,0x88,0x22,0x54,0x80,0x00,0xFF,0x22,0x70, +0x80,0x01,0xF0,0x01,0xFE,0x3C,0xF0,0x82,0xF1,0x02,0xB4,0x1F,0x88,0x01,0x38,0x00, +0x18,0x00,0x40,0x50,0x24,0x02,0x4C,0x54,0xFF,0xE8,0x40,0x14,0xA4,0x05,0x81,0x46, +0xF1,0x8A,0x40,0x50,0x24,0x02,0x4C,0x54,0x80,0x11,0x50,0x25,0x7F,0xFF,0x96,0x10, +0xF0,0x84,0x8E,0xC1,0xB4,0x1F,0xF1,0x02,0x97,0xB3,0x88,0x06,0x38,0x00,0x04,0x00, +0x40,0x14,0xA4,0x05,0xF1,0x8A,0xD5,0x73,0xF1,0x01,0xE1,0x41,0xE8,0xEF,0xF1,0x0A, +0xFE,0x0E,0xF1,0x02,0x54,0x00,0x00,0xE0,0x40,0x55,0x04,0x00,0xF1,0x06,0xFE,0x0F, +0xB4,0x3F,0x38,0x00,0x94,0x08,0xF0,0x40,0xF1,0x40,0xA4,0x00,0x8C,0x01,0xAC,0x08, +0xF0,0x05,0x5A,0x08,0x01,0x14,0xF0,0x03,0x80,0x27,0xF3,0x3C,0x38,0x00,0x15,0x11, +0xF4,0x3D,0xF0,0x84,0xF2,0x04,0x80,0x0A,0xF5,0x8B,0x49,0xFF,0xFF,0x5F,0xF0,0x04, +0xF5,0x0B,0xE0,0x1C,0x40,0x0E,0x3C,0x1B,0x83,0x80,0xF0,0x07,0x5A,0x08,0x01,0x0E, +0xF0,0x03,0x38,0x00,0x15,0x11,0x40,0xFE,0x00,0x07,0xE8,0x07,0xF1,0x42,0x83,0x80, +0x10,0xA0,0x80,0x00,0xF1,0x43,0xAF,0xC8,0x50,0x05,0x00,0x01,0x40,0xA0,0x00,0x11, +0xB4,0x1F,0xF1,0x02,0x88,0x0A,0x38,0x00,0x04,0x00,0xD5,0xAC,0x4E,0x65,0x00,0x34, +0xF1,0x0A,0xFE,0x0E,0xF1,0x02,0x54,0x00,0x00,0xE0,0x40,0xA3,0x04,0x00,0xF1,0x06, +0xFE,0x0F,0xB4,0x3F,0x38,0x00,0xA8,0x08,0xF0,0x40,0xF1,0x40,0xA4,0x00,0x8C,0x01, +0xAC,0x08,0xF0,0x05,0x5A,0x08,0x01,0x12,0xF0,0x03,0x80,0x27,0xF3,0x3C,0x38,0x50, +0x29,0x11,0xF4,0x3D,0x80,0x45,0x80,0x06,0xF5,0x8B,0x49,0xFF,0xFF,0x1F,0xF5,0x0B, +0xE0,0xBC,0x40,0x5E,0x3C,0x1B,0x83,0x85,0xF0,0x07,0x5A,0x00,0x01,0x15,0x8E,0xC1, +0xB4,0x1F,0x97,0xB3,0xF1,0x02,0x88,0x06,0x38,0x00,0x04,0x00,0x40,0x50,0x24,0x02, +0x4C,0x54,0xBF,0xCE,0xF0,0x09,0x8C,0xC1,0x97,0xB0,0x5A,0x00,0x08,0x11,0x84,0x40, +0x80,0x02,0xD5,0x15,0xF0,0x03,0x38,0x00,0x29,0x11,0x40,0xFE,0x00,0x07,0xE8,0xE8, +0xF1,0x42,0x83,0x80,0xAF,0x88,0xF1,0x43,0xAF,0xC8,0xD5,0xE2,0xF0,0x01,0x9E,0x81, +0xF0,0x04,0x40,0x20,0x08,0x07,0x84,0x00,0x40,0x00,0x18,0x06,0xF1,0x08,0x8E,0x21, +0xE0,0xE1,0xE8,0x40,0x97,0xF9,0x8A,0xC0,0x9C,0xF9,0xF0,0x01,0x96,0xDB,0x42,0x51, +0x80,0x24,0xF0,0x04,0x80,0x26,0x40,0xA0,0x08,0x00,0xB4,0x1F,0x41,0xE0,0x14,0x00, +0xE1,0x41,0xE9,0x2C,0x80,0x01,0x84,0x80,0xB4,0x3F,0x88,0x20,0x38,0x10,0x94,0x00, +0x40,0x14,0x84,0x02,0x4C,0x14,0x80,0x11,0x80,0x20,0xC4,0x19,0xB1,0x0C,0x8E,0x01, +0x38,0x02,0x22,0x09,0x40,0x04,0x08,0x08,0x88,0x04,0xAC,0xC1,0x50,0x04,0x00,0x01, +0x54,0x80,0x00,0xFF,0xD5,0x0C,0xF1,0x01,0xE0,0x01,0xE8,0xEF,0x8C,0x01,0x96,0x03, +0x84,0x81,0xD5,0xE3,0xE1,0x41,0xE9,0xDD,0x8C,0x21,0x96,0x4B,0x38,0x0F,0x04,0x00, +0x40,0x04,0x80,0x02,0x4C,0x04,0xFF,0xF8,0xD5,0xD4,0xE4,0x62,0xE8,0x07,0x48,0xFF, +0xFE,0xFC,0xE4,0xE2,0x4E,0xF3,0xFE,0xF9,0x8A,0xC0,0xF0,0x01,0x8E,0xE1,0x97,0xFB, +0x42,0x33,0x80,0x24,0xF0,0x04,0x88,0x02,0x80,0x40,0xB4,0x1F,0x99,0x03,0xE0,0x46, +0x4E,0xF3,0xFE,0xEB,0x80,0x06,0x84,0x20,0xB4,0xBF,0x88,0xA0,0x38,0x52,0x8C,0x00, +0x40,0x54,0x94,0x02,0x4C,0x54,0x80,0x11,0x80,0xC0,0xC1,0x19,0xB0,0x4C,0x8E,0x01, +0x38,0x00,0xA2,0x09,0x40,0x04,0x08,0x08,0x88,0x01,0xAD,0xC1,0x50,0x04,0x00,0x01, +0x54,0x80,0x00,0xFF,0xD5,0x0C,0xF5,0x01,0xE0,0x05,0xE8,0xEF,0x8C,0x01,0x96,0x03, +0x84,0x21,0xD5,0xE3,0xE0,0x46,0xE9,0xDC,0x8C,0xC1,0x97,0xB3,0x38,0x52,0x18,0x00, +0x40,0x54,0x94,0x02,0x4C,0x54,0xFF,0xF8,0xD5,0xD3,0x80,0x1C,0xFC,0xDA,0x84,0x20, +0x44,0x22,0x15,0xCC,0x9D,0x02,0xE6,0x24,0x80,0x60,0x40,0x32,0x3C,0x1A,0x38,0x31, +0x04,0x08,0x8C,0x21,0x5A,0x18,0x0B,0xF9,0xDD,0x9E,0x44,0x12,0x15,0xCC,0x38,0x00, +0x80,0x00,0xDD,0x9E,0x84,0x20,0x44,0x22,0x15,0xC0,0x38,0x01,0x04,0x08,0x8C,0x21, +0x5A,0x18,0x0B,0xFD,0xDD,0x9E,0x44,0x12,0x15,0xC0,0x38,0x00,0x80,0x00,0xDD,0x9E, +0x44,0x30,0x01,0x24,0x44,0x22,0x33,0x88,0x42,0x20,0x0C,0x73,0x10,0x11,0x01,0x0D, +0xDD,0x9E,0x44,0x22,0x34,0xAB,0x44,0x30,0x01,0x24,0xC1,0x17,0xFE,0xC4,0x99,0x13, +0x8C,0x45,0x38,0x01,0x0C,0x02,0x92,0x05,0x8C,0x01,0x96,0x1F,0x54,0x10,0x00,0x07, +0x94,0x8D,0xA6,0x65,0x92,0x03,0x96,0x67,0xFE,0x57,0xAE,0x65,0xA6,0x66,0x54,0x10, +0x80,0xFE,0xFE,0x0F,0xAE,0x26,0xDD,0x9E,0x42,0x20,0x0C,0x73,0xA6,0x55,0x96,0x67, +0xAE,0x55,0xA6,0x56,0x54,0x10,0x80,0xFE,0xAE,0x56,0xDD,0x9E,0x44,0x20,0x01,0x24, +0x44,0x12,0x34,0xB0,0x42,0x10,0x08,0x73,0xB4,0x01,0x92,0x05,0x96,0x1F,0xDD,0x9E, +0x84,0x00,0x3C,0x0B,0xFA,0x0C,0xDD,0x9E,0x3C,0x23,0xFA,0x0C,0x84,0x20,0x40,0x31, +0x04,0x0E,0x96,0xC6,0x96,0x08,0xC3,0x07,0x8C,0x21,0x5A,0x18,0x0B,0xFA,0x44,0x00, +0x00,0xFF,0xDD,0x9E,0x84,0x61,0x40,0x11,0x84,0x0C,0xFE,0x8F,0x3C,0x2B,0xFA,0x0C, +0xDD,0x9E,0x84,0x21,0x40,0x00,0x80,0x0C,0x3C,0x13,0xFA,0x0C,0xFE,0x03,0xFE,0x0E, +0x3C,0x0B,0xFA,0x0C,0xDD,0x9E,0xFC,0x00,0x49,0x00,0x2F,0xE4,0xC8,0x05,0x84,0x01, +0x3E,0x07,0xE2,0xCC,0xFC,0x80,0x84,0x02,0x3E,0x07,0xE2,0xCC,0x49,0x00,0x18,0x82, +0xFC,0x80,0xFC,0x00,0x49,0x00,0x17,0xEA,0x84,0x01,0x49,0x00,0x0F,0x33,0x49,0x00, +0x0F,0x20,0xFC,0x80,0xFC,0x06,0x84,0x00,0x3E,0x07,0xF4,0x20,0x3C,0x03,0xF7,0xC7, +0x96,0x0E,0x96,0x01,0xC0,0x02,0x84,0x01,0x3E,0x07,0xF4,0x1A,0x84,0x07,0x49,0x00, +0x37,0x12,0x5A,0x08,0x01,0x08,0x84,0x07,0x84,0x20,0x49,0x00,0x36,0xF1,0x49,0x00, +0x1B,0xA9,0x2E,0x07,0xE2,0xCC,0x5A,0x00,0x01,0x05,0x5A,0x00,0x03,0x32,0xD5,0x50, +0x49,0x00,0x1B,0x2B,0x5A,0x08,0x01,0x0F,0x84,0x00,0x49,0x00,0x18,0xB6,0x84,0x01, +0x49,0x00,0x2F,0xB4,0x84,0x03,0x3E,0x07,0xE2,0xCC,0x84,0x00,0x3C,0x0B,0xFA,0x0E, +0xD5,0x3C,0x49,0x00,0x18,0x40,0x5A,0x00,0x01,0xF1,0x49,0x00,0x2F,0xA4,0x5A,0x08, +0x01,0x38,0x2E,0x07,0xF4,0x1A,0x5A,0x08,0x01,0x06,0x3C,0x03,0xFA,0x0E,0x8C,0x01, +0xD5,0x02,0x84,0x00,0x3C,0x0B,0xFA,0x0E,0x3C,0x03,0xFA,0x0E,0xE6,0x0A,0xE9,0x28, +0x84,0x00,0x49,0x00,0x2F,0x93,0x84,0x00,0x3C,0x0B,0xFA,0x0E,0xD5,0x21,0x84,0x00, +0x49,0x00,0x4E,0xA7,0x80,0xC0,0x5A,0x08,0x01,0x19,0x49,0x00,0x1B,0x6F,0x49,0x00, +0x1B,0x5B,0x84,0x00,0x49,0x00,0x2D,0x9F,0x84,0x00,0x49,0x00,0x18,0x91,0x49,0x00, +0x17,0x85,0x84,0x00,0x3C,0x0B,0xFA,0x0F,0x3C,0x0B,0xFA,0x0E,0x3E,0x67,0xE2,0xCC, +0x49,0x00,0x0C,0xA7,0x84,0x02,0xD5,0x02,0x84,0x01,0x3E,0x07,0xF4,0x20,0x2E,0x07, +0xE2,0xCC,0x8E,0x03,0xE6,0x02,0x3C,0x0D,0xF8,0xEA,0xE8,0x0B,0xB6,0x1F,0xF0,0x81, +0xF0,0x82,0xF0,0x83,0xF0,0x84,0xF0,0x85,0xF0,0x86,0xF0,0x87,0xF0,0x88,0xD5,0x1C, +0x44,0x12,0x00,0x01,0xB6,0x3F,0x44,0x11,0x00,0x02,0xF1,0x81,0x44,0x10,0x80,0x04, +0xF1,0x82,0x44,0x10,0x40,0x08,0xF1,0x83,0x44,0x10,0x20,0x10,0xF1,0x84,0x44,0x10, +0x10,0x20,0xF1,0x85,0x44,0x10,0x08,0x40,0xF1,0x86,0x44,0x10,0x04,0x80,0xF1,0x87, +0x44,0x10,0x03,0x00,0xF1,0x88,0xF0,0x89,0xF0,0x8A,0xF0,0x8B,0x80,0x1F,0x49,0xFF, +0xD8,0xEB,0x2E,0x07,0xF4,0x20,0xFC,0x86,0xDD,0x9E,0x3C,0xF7,0xF7,0x92,0x5E,0xF7, +0x80,0xB4,0xE8,0x0C,0xFC,0x00,0x49,0x00,0x1B,0x7A,0x80,0x20,0x84,0x00,0x5A,0x10, +0x01,0x03,0x80,0x20,0x49,0x00,0x12,0xA1,0xFC,0x80,0xDD,0x9E,0x44,0x24,0x10,0x14, +0xA6,0x10,0x44,0x14,0x10,0x14,0x96,0x06,0xC0,0xFC,0xA6,0x08,0x54,0x00,0x00,0xFE, +0xAE,0x08,0xDD,0x9E,0xFC,0x20,0x80,0xC1,0x44,0x14,0x10,0x14,0x80,0xE0,0xA6,0x08, +0x54,0x00,0x00,0xFE,0xAE,0x08,0x84,0x00,0x49,0x00,0x0C,0x03,0x49,0x00,0x0B,0xCD, +0x40,0x00,0x00,0x09,0x88,0xE6,0x4C,0x63,0x80,0x07,0x08,0x03,0x00,0x01,0x49,0x00, +0x0B,0xD2,0xD5,0xFA,0x84,0x01,0x49,0x00,0x0B,0xF4,0xFC,0xA0,0xFC,0x01,0x84,0x06, +0x10,0x0F,0x80,0x04,0xB0,0x41,0x84,0x01,0x49,0xFF,0xFF,0xDE,0xFC,0x81,0xFC,0x00, +0x44,0x14,0x10,0x14,0xA6,0x08,0x54,0x00,0x00,0xFE,0xAE,0x08,0x84,0x00,0x49,0x00, +0x0B,0xE0,0x49,0x00,0x0B,0xAA,0x40,0x00,0x00,0x09,0x84,0x05,0x49,0x00,0x0B,0xB3, +0x44,0x00,0x00,0xFF,0x49,0x00,0x0B,0xAF,0x44,0x04,0x10,0x10,0xA7,0x80,0x84,0x01, +0x97,0xB0,0x49,0x00,0x0B,0xCE,0x80,0x06,0xFC,0x80,0xFC,0x40,0x44,0x14,0x10,0x14, +0x80,0xC0,0xA6,0x08,0x54,0x00,0x00,0xFE,0xAE,0x08,0x44,0x04,0x10,0x00,0x84,0x20, +0xAE,0x40,0x44,0x04,0x10,0x04,0x44,0x10,0x00,0x55,0xAE,0x40,0x44,0x1F,0xFF,0xAA, +0xAE,0x41,0x44,0x10,0x00,0x66,0xAE,0x42,0x40,0x00,0x00,0x09,0x44,0x74,0x10,0x08, +0x44,0x00,0x00,0x9F,0xB6,0x07,0x49,0xFF,0xFF,0x93,0x50,0x93,0x00,0x03,0x44,0x80, +0x00,0xFF,0x44,0xA4,0x10,0x10,0xB7,0x07,0x49,0xFF,0xFF,0x8A,0x00,0x05,0x00,0x00, +0x18,0x03,0x00,0x01,0x4C,0x64,0xFF,0xF9,0x44,0x04,0x10,0x00,0x84,0x21,0xAE,0x40, +0xFC,0xC0,0xFC,0x00,0x80,0xC0,0x49,0xFF,0xFF,0xA3,0x84,0x00,0x49,0x00,0x0B,0x91, +0x49,0x00,0x0B,0x5B,0xFA,0x10,0x49,0x00,0x0B,0x66,0x5C,0xF3,0x00,0x40,0x80,0x26, +0xE9,0x03,0x44,0x10,0x00,0x3F,0x96,0x08,0x40,0x00,0x30,0x08,0x49,0x00,0x0B,0x69, +0x84,0x01,0x49,0x00,0x0B,0x7E,0x40,0x00,0x00,0x09,0x40,0x00,0x00,0x09,0x40,0x00, +0x00,0x09,0x49,0xFF,0xFF,0x8E,0x96,0x06,0xC0,0x05,0x84,0x0A,0x49,0xFF,0xDF,0x6F, +0xD5,0xF9,0xFC,0x80,0xFC,0x43,0x83,0x80,0x84,0x00,0xF0,0x85,0xB0,0x05,0x81,0x01, +0x81,0x42,0x49,0xFF,0xFF,0x9C,0xF0,0x05,0x44,0x5E,0xFF,0xFF,0xFF,0x46,0x46,0x00, +0x03,0x02,0x50,0x00,0x05,0xC2,0xD0,0x04,0x44,0x90,0x01,0x00,0xD5,0x02,0xFB,0x30, +0x84,0xC0,0x44,0x74,0x10,0x08,0x49,0xFF,0xFF,0x63,0x44,0x04,0x10,0x00,0x84,0x20, +0xAE,0x40,0x44,0x04,0x10,0x04,0x44,0x10,0x00,0x55,0xAE,0x40,0x44,0x1F,0xFF,0xAA, +0xAE,0x41,0x44,0x10,0x00,0x66,0xAE,0x42,0x40,0x03,0x20,0x00,0x40,0x20,0x40,0x09, +0x40,0x10,0x20,0x09,0x84,0x62,0x96,0x00,0x96,0x48,0x96,0x97,0xF0,0x81,0xF1,0x82, +0xF2,0x83,0xB6,0x67,0x49,0xFF,0xFF,0x1C,0xF2,0x03,0xB6,0x47,0x49,0xFF,0xFF,0x18, +0xF1,0x02,0xB6,0x27,0x49,0xFF,0xFF,0x14,0xF0,0x01,0xB6,0x07,0x49,0xFF,0xFF,0x10, +0x40,0x00,0x00,0x09,0x38,0x0E,0x18,0x00,0x8C,0xC1,0xB6,0x07,0x97,0xB1,0x49,0xFF, +0xFF,0x07,0x40,0x03,0x20,0x00,0x40,0x00,0x24,0x37,0xC1,0x03,0x4C,0x65,0x7F,0xF4, +0x44,0x04,0x10,0x00,0x84,0x21,0xAE,0x40,0x40,0x00,0x00,0x09,0x40,0x00,0x00,0x09, +0x49,0xFF,0xFF,0x27,0x96,0x06,0xC0,0x05,0x84,0x02,0x49,0xFF,0xDF,0x08,0xD5,0xF9, +0x4C,0x65,0x7F,0xB3,0xFC,0xC3,0x40,0x00,0x60,0x08,0x46,0x20,0x4C,0x11,0xFE,0x0D, +0x50,0x21,0x0D,0xB7,0x84,0x28,0x94,0xC1,0x4E,0x04,0x00,0x05,0x40,0x01,0x88,0x03, +0xD5,0x02,0x80,0x03,0x8E,0x21,0x96,0x48,0xC9,0xF7,0xDD,0x9E,0xFC,0x41,0x84,0x1F, +0xF0,0x81,0x84,0xC0,0x44,0x92,0x10,0x00,0x44,0x70,0x0F,0xFC,0x5C,0xF3,0x01,0x00, +0xE8,0x04,0x38,0x04,0x98,0x00,0xD5,0x03,0x44,0x00,0x00,0xFF,0xF1,0x01,0x49,0xFF, +0xFF,0xDC,0x8C,0xC1,0xF0,0x81,0x4C,0x63,0xFF,0xF3,0xF0,0x01,0xFC,0xC1,0x2E,0x07, +0xE2,0xCD,0x5A,0x00,0x01,0x14,0xFC,0x00,0x2E,0x07,0xE2,0xCD,0x5A,0x08,0x02,0x05, +0x84,0x03,0x3E,0x07,0xE2,0xCD,0x2E,0x07,0xE2,0xCD,0x5A,0x08,0x04,0x05,0x84,0x01, +0x3E,0x07,0xE2,0xCD,0x49,0xFF,0xEF,0xAB,0xFC,0x80,0xDD,0x9E,0xFC,0x00,0x49,0x00, +0x1D,0xA3,0x80,0xC0,0xC8,0x06,0x49,0xFF,0xF0,0x90,0x40,0x03,0x00,0x06,0xFC,0x80, +0x84,0x01,0xFC,0x80,0xFC,0x42,0x44,0x70,0x01,0x24,0x44,0x22,0x33,0x88,0xFE,0x3C, +0x40,0xA1,0x00,0x00,0x50,0x95,0x01,0x08,0x00,0x34,0x80,0x05,0xE6,0x62,0xE9,0x74, +0x8C,0x08,0x99,0xC2,0x80,0xC1,0x84,0x40,0xB0,0x41,0x80,0x07,0x49,0xFF,0xE0,0x22, +0x80,0x07,0xB0,0x42,0x84,0x41,0x49,0xFF,0xE0,0x1D,0x02,0x05,0x00,0x84,0x5A,0x08, +0x01,0x09,0xF0,0x01,0x02,0x10,0x00,0x08,0x02,0x00,0x00,0x09,0xAC,0x72,0xD5,0x41, +0x02,0x04,0x80,0x01,0x5A,0x00,0x01,0x04,0x5A,0x08,0x02,0x12,0xF2,0x01,0xF1,0x02, +0x22,0x31,0x00,0x08,0x22,0x00,0x80,0x08,0x88,0x03,0x90,0x01,0xAC,0x32,0x22,0x21, +0x00,0x09,0x22,0x00,0x80,0x09,0x88,0x02,0x90,0x01,0xD5,0x2B,0x80,0x07,0xB0,0x43, +0x84,0x42,0x49,0xFF,0xDF,0xF7,0xF2,0x03,0xF0,0x01,0xF7,0x02,0x22,0x41,0x00,0x08, +0x22,0x10,0x00,0x08,0x22,0x31,0x00,0x09,0x22,0x23,0x80,0x08,0x9B,0x0C,0x90,0x81, +0x40,0x91,0x0C,0x08,0x9A,0x8A,0x9A,0x54,0x95,0x62,0x88,0xA9,0x94,0x49,0x88,0x25, +0x22,0x00,0x00,0x09,0x90,0x23,0xAC,0x72,0x22,0x13,0x80,0x09,0x9A,0xC3,0x90,0x61, +0x95,0x0B,0x9A,0x41,0x94,0x9A,0x9A,0x0B,0x88,0x44,0x94,0x01,0x88,0x02,0x90,0x03, +0xA4,0x72,0xAC,0x33,0x80,0x01,0x96,0x4B,0x5E,0xF0,0x99,0x33,0xE9,0x03,0x44,0x00, +0x19,0x32,0x96,0x43,0x4E,0x14,0x00,0x03,0x84,0x00,0xA4,0x73,0xAC,0x32,0x80,0x01, +0x96,0x4B,0x5E,0xF0,0xB2,0x65,0xE9,0x03,0x44,0x00,0x32,0x64,0x96,0x43,0x4E,0x14, +0x00,0x03,0x84,0x00,0xAC,0x33,0xFC,0xC2,0x04,0x00,0x00,0x47,0x46,0x10,0x10,0x00, +0xFE,0x46,0xC1,0x09,0x40,0x10,0x50,0x09,0x92,0x10,0xFE,0x0D,0x96,0x1F,0x5C,0x00, +0x00,0x01,0xDD,0x9E,0x84,0x01,0xDD,0x9E,0xFC,0x20,0x04,0x60,0x00,0x47,0x84,0x4C, +0x92,0xD4,0x97,0x9F,0x80,0xE0,0x42,0x03,0x08,0x73,0x50,0x00,0x00,0x94,0x49,0x00, +0x39,0xE5,0x8C,0xC1,0x84,0x26,0x40,0x23,0x04,0xD6,0x02,0x23,0x80,0x8F,0x97,0x70, +0x96,0x9F,0x50,0x03,0x81,0x1B,0xDA,0x0B,0x8C,0xA1,0x40,0x52,0x84,0x36,0x54,0x50, +0x80,0x0F,0xA6,0x43,0x54,0x10,0x80,0xF0,0xFE,0x6F,0xAE,0x43,0xA6,0x43,0x95,0xB4, +0x96,0x5F,0xFF,0x8F,0xA6,0x44,0xAF,0x83,0x58,0x10,0x80,0x01,0xAE,0x44,0xFC,0xA0, +0xFC,0x42,0x3A,0x0F,0x84,0x20,0x80,0xE0,0x2E,0x07,0xEE,0x21,0x80,0xC1,0xC0,0x02, +0x9B,0xD7,0x2E,0x07,0xEE,0x22,0xC0,0x02,0x9B,0x9E,0x2E,0x07,0xEE,0x23,0xC0,0x0B, +0x40,0x93,0x00,0x13,0x96,0x11,0x96,0x61,0x80,0xC7,0x80,0x85,0x80,0x43,0x80,0xE9, +0x80,0xA1,0x80,0x60,0x42,0x03,0x90,0x24,0x42,0x13,0x14,0x24,0x40,0x00,0x08,0x17, +0x40,0x10,0x8C,0x37,0xFC,0xC2,0xFC,0x47,0x49,0x00,0x1C,0xB6,0xF0,0x84,0x44,0x62, +0x16,0x14,0x44,0x92,0x33,0x90,0x85,0x40,0xB1,0xCB,0x54,0x05,0x00,0xFF,0xF0,0x85, +0x84,0x00,0x10,0x03,0x00,0x08,0x10,0x03,0x80,0x08,0x2E,0x07,0xF4,0xA8,0x5A,0x08, +0x01,0x04,0x48,0x00,0x00,0x95,0x00,0x44,0x81,0x05,0x9E,0x22,0xE6,0x02,0x4E,0xF2, +0x00,0x8F,0x44,0x20,0x01,0x24,0x44,0x02,0x34,0xB0,0x42,0x25,0x08,0x24,0x38,0x10, +0x08,0x02,0x40,0x30,0xAC,0x09,0x96,0xC6,0x4E,0x33,0x00,0x82,0x44,0x32,0x15,0xC0, +0x92,0x25,0x38,0x31,0xA8,0x00,0x96,0x5F,0xE0,0x61,0xE8,0x09,0x5A,0x48,0x03,0x08, +0x8E,0x05,0x88,0x02,0xA6,0x46,0x58,0x10,0x80,0x08,0xAE,0x46,0xB0,0x47,0x84,0x40, +0x80,0x09,0x49,0xFF,0xDF,0x17,0xF0,0x07,0x50,0x8F,0x80,0x24,0x22,0x20,0x00,0x08, +0xAC,0xB2,0x22,0x10,0x00,0x09,0xB4,0x00,0xF2,0x89,0xB6,0x06,0x84,0x01,0x10,0x03, +0x00,0x08,0xF1,0x8A,0x3C,0x03,0xEF,0x08,0xAC,0x73,0xF0,0x81,0xF2,0x01,0x3C,0x03, +0xEF,0x09,0xF0,0x82,0xF3,0x02,0x3C,0x03,0xEF,0x0C,0xF0,0x83,0xF4,0x03,0x3D,0xC3, +0xEF,0x0D,0x3A,0x04,0x04,0x00,0x80,0xBC,0x49,0xFF,0xFF,0x7C,0xF0,0x89,0xAC,0x32, +0x02,0x04,0x80,0x81,0x14,0x14,0x00,0x01,0xE6,0x02,0xAC,0x73,0xE8,0x05,0x00,0x14, +0x81,0x05,0x5A,0x18,0x03,0x2B,0x84,0x41,0xB0,0x48,0x80,0x09,0x49,0xFF,0xDE,0xE2, +0xF0,0x05,0x80,0x27,0x49,0xFF,0xFE,0xA8,0xF0,0x07,0xF2,0x01,0xF3,0x02,0xB4,0x20, +0xF0,0x08,0xF4,0x03,0x80,0xBC,0xB4,0x00,0x88,0x01,0x84,0x22,0x40,0x00,0x04,0x16, +0xB6,0x07,0x84,0x01,0x22,0x13,0x80,0x02,0x10,0x03,0x80,0x08,0x22,0x03,0x80,0x03, +0xF1,0x89,0xF0,0x8A,0x3A,0x04,0x04,0x00,0x49,0xFF,0xFF,0x4C,0xB6,0x08,0x14,0x14, +0x00,0x01,0xAC,0x3A,0xAC,0x7B,0xD5,0x09,0x5A,0x08,0x01,0x08,0x3A,0x23,0x10,0x00, +0x3A,0x23,0x90,0x20,0x10,0x03,0x80,0x08,0xF0,0x04,0xC8,0x05,0x80,0x09,0x80,0x27, +0x49,0xFF,0xFF,0x0C,0x80,0x09,0x80,0x26,0x49,0xFF,0xFF,0x08,0x8D,0x41,0x8C,0xCC, +0x50,0x94,0x81,0x24,0x5A,0xA0,0x0A,0x04,0x48,0xFF,0xFF,0x59,0xFC,0xC7,0xFC,0x00, +0x2E,0x17,0xF6,0x28,0xC1,0x08,0x49,0x00,0x27,0x08,0x49,0x00,0x27,0x12,0x49,0x00, +0x1B,0xD8,0xD5,0x07,0x49,0x00,0x27,0x01,0x49,0x00,0x27,0x39,0x49,0x00,0x1B,0xD6, +0x49,0x00,0x1B,0xDA,0xFC,0x80,0xFC,0x01,0x84,0x00,0x49,0x00,0x1B,0xF3,0x44,0x12, +0x16,0x14,0x84,0x00,0x49,0x00,0x1C,0x42,0xF0,0x81,0x84,0x27,0x84,0x00,0x49,0x00, +0x1D,0x42,0xF0,0x01,0xFC,0x81,0xFC,0x20,0x3C,0x2D,0xFF,0x13,0x5A,0x20,0x01,0x3E, +0x80,0xE1,0x80,0xC0,0x49,0x00,0x1B,0xD8,0xC8,0x2E,0x5A,0x68,0x01,0x05,0x49,0x00, +0x1B,0xD9,0xD5,0x14,0x49,0xFF,0xFF,0xE1,0x80,0xC0,0xC8,0x10,0x84,0x01,0x49,0xFF, +0xDB,0xF2,0x80,0x06,0x84,0x21,0x49,0xFF,0xDB,0x89,0x3E,0x67,0xF4,0xAE,0x3E,0x67, +0xF4,0xA8,0x3C,0x6F,0xFC,0x52,0x5A,0x70,0x01,0x21,0x84,0x01,0x84,0x2F,0x49,0x00, +0x33,0x01,0x84,0x01,0x80,0x20,0x49,0x00,0x33,0x0B,0x84,0x01,0x49,0xFF,0xDB,0xB4, +0x84,0x00,0x49,0x00,0x1B,0x85,0x49,0x00,0x1B,0x97,0x84,0x00,0x49,0x00,0x1D,0x13, +0x84,0x01,0xD5,0x0C,0x84,0x01,0x49,0xFF,0xDB,0xCE,0x84,0x00,0x84,0x21,0x49,0xFF, +0xDB,0x65,0x84,0x00,0x3E,0x07,0xF4,0xAE,0x84,0x00,0xFC,0xA0,0xFC,0x00,0xC0,0x0B, +0x5A,0x08,0x01,0x05,0x49,0x00,0x2A,0x6D,0xD5,0x06,0x84,0xC0,0x5A,0x08,0x02,0x05, +0x49,0x00,0x2A,0x6A,0x80,0xC0,0x84,0x02,0x49,0x00,0x1B,0x94,0x84,0x02,0x44,0x12, +0x16,0x14,0x49,0x00,0x1B,0xE3,0xC6,0x04,0x80,0x06,0x49,0x00,0x1C,0x9E,0x49,0x00, +0x2A,0x64,0x49,0x00,0x1C,0xAC,0x84,0x02,0x84,0x27,0x49,0x00,0x1C,0xDC,0xFC,0x80, +0x3C,0x0D,0xFF,0x13,0x5A,0x00,0x01,0x16,0xFC,0x00,0x49,0x00,0x1B,0x75,0x80,0xC0, +0xC8,0x04,0x49,0xFF,0xFF,0x82,0xD5,0x07,0x5A,0x00,0x01,0x0B,0x49,0x00,0x1B,0x6F, +0x49,0xFF,0xFF,0xCE,0x80,0x06,0x49,0x00,0x1B,0x3B,0x49,0x00,0x1B,0x4D,0xFC,0x80, +0xDD,0x9E,0xFC,0x00,0x49,0x00,0x17,0xD3,0x5A,0x08,0x01,0x06,0x84,0x00,0x44,0x10, +0x07,0xD0,0xD5,0x04,0x84,0x00,0x44,0x10,0x75,0x30,0x49,0x00,0x32,0x9B,0x84,0x00, +0x84,0x21,0x49,0x00,0x32,0xA5,0x84,0x00,0x49,0x00,0x17,0xCA,0xFC,0x80,0x3E,0x07, +0xF4,0xAC,0xDD,0x9E,0x2E,0x07,0xF4,0xAF,0xDD,0x9E,0xFC,0x00,0x49,0x00,0x15,0x0F, +0x49,0xFF,0xFB,0x92,0x3E,0x07,0xF4,0xAD,0x49,0xFF,0xFC,0x30,0x49,0xFF,0xFB,0x83, +0xFC,0x80,0xFC,0x41,0x2E,0x07,0xF4,0xFB,0x96,0x0E,0xC0,0x0C,0x2E,0x07,0xF4,0xFB, +0x84,0x20,0x54,0x00,0x00,0xFD,0x3E,0x07,0xF4,0xFB,0x3E,0x17,0xF4,0x9F,0x3E,0x17, +0xF6,0x28,0x49,0x00,0x17,0x9C,0xC0,0x50,0x2E,0x07,0xF4,0x9F,0xC8,0x4D,0x2E,0x07, +0xF4,0xFB,0x96,0x06,0xC0,0x08,0x46,0x05,0xB9,0x2E,0x44,0x14,0x00,0x54,0x50,0x00, +0x07,0xF4,0xB6,0x01,0x44,0x64,0x80,0x80,0x46,0xAF,0xEF,0xFF,0xB5,0x26,0xB4,0xE6, +0x40,0x04,0xE0,0x09,0x54,0x90,0x00,0x01,0xB4,0x06,0x50,0xA5,0x0F,0xFF,0x40,0x00, +0x28,0x02,0xB6,0x06,0xB4,0x06,0x46,0x8F,0xDF,0xFF,0x44,0x11,0xE0,0x00,0x50,0x84, +0x0F,0xFF,0x44,0x21,0xF3,0xEE,0x8A,0x41,0x40,0x00,0x20,0x02,0x96,0x91,0xB6,0x06, +0x44,0x00,0xDD,0x40,0x49,0x00,0x2F,0x7A,0x49,0x00,0x0D,0x7A,0x84,0x01,0x3E,0x07, +0xF4,0x9F,0x49,0x00,0x1A,0x51,0x46,0x08,0x88,0x88,0x44,0x14,0x00,0x54,0x50,0x00, +0x08,0x88,0xB6,0x01,0xB4,0x06,0x40,0x94,0xE0,0x08,0x40,0x00,0x28,0x02,0x40,0x90, +0x24,0x04,0xB7,0x26,0xB4,0x06,0x92,0xF9,0x97,0xC6,0x40,0x73,0xE4,0x08,0x40,0x00, +0x20,0x02,0xFF,0xC7,0xB6,0xE6,0x2E,0x07,0xF4,0xFB,0x96,0x06,0xC0,0x1A,0x44,0x14, +0x00,0x54,0x46,0x0A,0x67,0xC9,0x50,0x00,0x0D,0xFE,0xB6,0x01,0x2E,0x07,0xF4,0xFB, +0x44,0x17,0x10,0x08,0x58,0x00,0x00,0x04,0x3E,0x07,0xF4,0xFB,0x2E,0x07,0xF4,0xFB, +0x54,0x00,0x00,0xFE,0x3E,0x07,0xF4,0xFB,0xB4,0x01,0x58,0x00,0x01,0x00,0xB6,0x01, +0x84,0x00,0x3E,0x07,0xF4,0xAB,0x49,0x00,0x17,0x2A,0x80,0x60,0x2E,0x07,0xF4,0x9F, +0xCB,0x48,0x5A,0x08,0x01,0x3E,0x44,0x64,0x80,0x80,0x46,0x8F,0xEF,0xFF,0xB5,0x26, +0xB4,0xE6,0x40,0x04,0xE0,0x09,0x54,0x90,0x00,0x01,0xB4,0x06,0x50,0x84,0x0F,0xFF, +0x40,0x00,0x20,0x02,0xF3,0x81,0xB6,0x06,0xB4,0x06,0x46,0xAF,0xDF,0xFF,0x50,0xA5, +0x0F,0xFF,0x40,0x00,0x28,0x02,0xB6,0x06,0x44,0x20,0xFD,0xBE,0x44,0x00,0xDD,0x40, +0x80,0x20,0x8A,0x40,0x49,0x00,0x2F,0x12,0x49,0x00,0x0D,0x12,0xF3,0x01,0x44,0x04, +0x00,0x54,0x3E,0x37,0xF4,0x9F,0xB6,0x60,0x49,0x00,0x19,0xE6,0xB4,0x06,0x40,0x94, +0xE0,0x08,0x40,0x00,0x20,0x02,0x40,0x90,0x24,0x04,0xB7,0x26,0xB4,0x06,0x92,0xF9, +0x97,0xC6,0x40,0x73,0xE4,0x08,0x40,0x00,0x28,0x02,0xFF,0xC7,0xB6,0xE6,0x49,0x00, +0x3B,0x99,0x2E,0x07,0xF1,0x5C,0xC0,0x2A,0x84,0x01,0x3E,0x07,0xF4,0xAB,0xD5,0x26, +0xC0,0x25,0x3C,0x0D,0xFF,0x13,0xC8,0x04,0x49,0x00,0x45,0x3E,0xD5,0x03,0x49,0xFF, +0xDE,0x3E,0x49,0xFF,0xFB,0x54,0x3C,0x0D,0xFB,0xD6,0x54,0x00,0x10,0x00,0xC0,0x11, +0x3C,0xF7,0xF7,0x8E,0x5E,0xF7,0xFE,0x70,0xE8,0x11,0x84,0x01,0x3C,0x1D,0xFB,0xD6, +0x3E,0x07,0xF4,0xAB,0x44,0x0F,0xEF,0xFF,0xFE,0x0E,0x3C,0x0F,0xFB,0xD6,0xD5,0x06, +0x3C,0xF7,0xF7,0x92,0x5E,0xF7,0x80,0xC8,0xE8,0xD8,0xFC,0xC1,0xFC,0x00,0x49,0x00, +0x2F,0x74,0x5A,0x08,0x01,0x0A,0x49,0x00,0x16,0xB2,0xC8,0x03,0x84,0x04,0xD5,0x02, +0x84,0x03,0x3E,0x07,0xF4,0xB1,0xFC,0x80,0xFC,0x00,0x49,0x00,0x12,0x79,0x49,0xFF, +0xFC,0x8F,0x80,0xC0,0x5A,0x08,0x01,0x06,0x49,0xFF,0xFD,0x77,0x3E,0x67,0xF4,0xAC, +0xFC,0x80,0x2E,0x07,0xF4,0xAD,0x5A,0x08,0x01,0x06,0x84,0x22,0x3E,0x17,0xF4,0xB3, +0xDD,0x9E,0x5A,0x08,0x02,0x07,0x84,0x01,0x3E,0x07,0xF4,0xB3,0x84,0x01,0xDD,0x9E, +0x84,0x00,0xDD,0x9E,0xFC,0x00,0x49,0x00,0x16,0x8A,0xC8,0x03,0x49,0x00,0x16,0xDB, +0x49,0x00,0x11,0xB0,0x49,0x00,0x0E,0xE8,0x49,0x00,0x2A,0xD0,0x49,0x00,0x2A,0x2A, +0x49,0x00,0x12,0x40,0x49,0x00,0x28,0xFF,0x49,0x00,0x2D,0x26,0x49,0x00,0x2A,0x22, +0x49,0x00,0x13,0x3E,0x49,0x00,0x16,0x73,0xC8,0x04,0x49,0x00,0x2B,0x7F,0xD5,0x05, +0x2E,0x07,0xF4,0xFB,0x96,0x06,0xC8,0xFA,0x49,0xFF,0xFA,0x2F,0x49,0x00,0x2C,0x66, +0x49,0xFF,0xEB,0x56,0x49,0xFF,0xFE,0x8F,0x49,0x00,0x05,0x88,0x49,0x00,0x16,0x5F, +0x80,0x40,0x5A,0x08,0x01,0x08,0x44,0x00,0x07,0xD0,0x84,0x20,0x80,0x62,0x49,0x00, +0x30,0x88,0x84,0x01,0x3E,0x07,0xF4,0xB3,0x49,0x00,0x16,0x51,0xC8,0x04,0x84,0x01, +0x49,0x00,0x2E,0x12,0xFC,0x80,0xFC,0x00,0x49,0x00,0x29,0xDE,0x49,0x00,0x29,0xDD, +0xC0,0x08,0x49,0x00,0x28,0xBE,0x49,0x00,0x28,0x93,0x84,0x02,0x3E,0x07,0xF4,0xB3, +0xFC,0x80,0xFC,0x01,0x2E,0x07,0xF4,0xB0,0x5A,0x00,0x01,0x2E,0xC0,0x04,0x5A,0x00, +0x02,0x37,0xD5,0x69,0x49,0x00,0x28,0xB7,0x84,0x00,0x49,0x00,0x2A,0xCF,0x49,0x00, +0x16,0x2E,0x80,0xC0,0xC8,0x1C,0x49,0x00,0x29,0xD5,0xC0,0x15,0x80,0x06,0x49,0x00, +0x12,0xEF,0x80,0x06,0x49,0x00,0x28,0x94,0x80,0x06,0x49,0x00,0x27,0xDE,0x49,0x00, +0x29,0xCC,0xB6,0x1F,0x49,0x00,0x29,0xCC,0xF0,0x81,0xB4,0x3F,0x80,0x06,0xF2,0x01, +0x49,0x00,0x2A,0x42,0x84,0x02,0x84,0x20,0x49,0x00,0x13,0xBD,0x84,0x01,0x49,0x00, +0x2A,0xAD,0xD5,0x3E,0x49,0x00,0x29,0xA0,0x49,0x00,0x29,0x9F,0xC0,0x3C,0x49,0x00, +0x28,0x80,0x84,0x00,0x49,0x00,0x0D,0xAF,0x84,0x02,0xD5,0x33,0x49,0x00,0x29,0x9D, +0xC0,0x32,0x2E,0x07,0xF4,0xB4,0xC8,0x0C,0x44,0x14,0x50,0x28,0xB4,0x01,0x92,0x03, +0x96,0x06,0x5A,0x00,0x01,0x06,0xB4,0x01,0x92,0x01,0x96,0x06,0xC8,0x05,0x2E,0x07, +0xF4,0xAF,0x5A,0x00,0x02,0x21,0x3C,0x0D,0xFF,0x15,0x5A,0x00,0x01,0x1D,0x49,0x00, +0x28,0x60,0x84,0x01,0x49,0x00,0x0D,0x8F,0x5A,0x08,0x01,0x11,0x49,0x00,0x15,0xDF, +0xC8,0x05,0x49,0x00,0x29,0x87,0x49,0x00,0x13,0xFC,0x49,0x00,0x14,0x49,0x84,0x00, +0x49,0x00,0x27,0xA9,0x84,0x01,0x3E,0x07,0xF4,0xB3,0x49,0x00,0x28,0x5E,0x84,0x01, +0x3E,0x07,0xF4,0xB0,0xFC,0x81,0xFC,0x00,0x84,0x01,0x84,0x20,0x49,0x00,0x30,0xA8, +0x84,0x01,0x49,0x00,0x1A,0xB8,0x84,0x00,0x3E,0x07,0xF4,0xAC,0xFC,0x80,0xFC,0x00, +0x2E,0x07,0xF4,0x9E,0x5A,0x00,0x01,0x3C,0xC0,0x07,0x5A,0x08,0x02,0x04,0x48,0x00, +0x00,0x81,0x48,0x00,0x00,0xD3,0x3C,0x0D,0xFB,0xD6,0x54,0x00,0x10,0x00,0xC0,0x65, +0x2E,0x07,0xEF,0x93,0x2E,0x17,0xEF,0xAF,0x8A,0x01,0x4E,0x04,0x00,0x03,0xFE,0x02, +0xE4,0x02,0xE8,0x16,0x2E,0x07,0xEF,0x92,0x2E,0x17,0xEF,0xAE,0x8A,0x01,0x4E,0x04, +0x00,0x03,0xFE,0x02,0xE4,0x02,0xE8,0x0C,0x3C,0x17,0xF7,0xD8,0x84,0x03,0xFE,0x0C, +0x85,0xE5,0x40,0xF0,0x3D,0xF6,0x3C,0x07,0xF7,0xD9,0xE0,0x0F,0xE8,0x05,0x2E,0x07, +0xEF,0xA8,0x4E,0x02,0x00,0x8C,0x3C,0xF7,0xF7,0x92,0x5E,0xF7,0x81,0x91,0xE9,0x2E, +0x2E,0x07,0xEF,0xA8,0x5A,0x08,0x01,0x2B,0x48,0x00,0x00,0x81,0x2E,0x07,0xEF,0x93, +0x2E,0x17,0xEF,0xAF,0x8A,0x01,0x4E,0x04,0x00,0x03,0xFE,0x02,0xE4,0x02,0xE8,0x22, +0x2E,0x07,0xEF,0x92,0x2E,0x17,0xEF,0xAE,0x8A,0x01,0x4E,0x04,0x00,0x03,0xFE,0x02, +0xE4,0x02,0xE8,0x18,0x3C,0x17,0xF7,0xD8,0x84,0x03,0xFE,0x0C,0x85,0xE5,0x40,0xF0, +0x3D,0xF6,0x3C,0x07,0xF7,0xD9,0xE0,0x0F,0xE9,0x0D,0x3C,0xF7,0xF7,0x92,0x5E,0xF7, +0x81,0x91,0xE8,0x1A,0x84,0x00,0x3E,0x07,0xF4,0x9D,0x84,0x02,0x3E,0x07,0xF4,0x9E, +0xD5,0x74,0x2E,0x07,0xEF,0xA8,0xC8,0xF2,0x2E,0x07,0xF4,0x9D,0x8C,0x01,0x96,0x00, +0xE6,0x02,0x3E,0x07,0xF4,0x9D,0xE9,0x0D,0x84,0x00,0x3E,0x07,0xF4,0x9E,0x49,0x00, +0x0C,0x27,0x84,0x04,0xD5,0x60,0x2E,0x07,0xEF,0xA8,0x5A,0x08,0x01,0xE5,0xD5,0xED, +0x3C,0x07,0xF7,0xDA,0x3C,0x17,0xF7,0x8E,0xFE,0x02,0xE0,0x20,0xE9,0xEE,0xD5,0x55, +0x2E,0x07,0xEF,0x93,0x2E,0x17,0xEF,0xAF,0x8A,0x01,0x4E,0x04,0x00,0x03,0xFE,0x02, +0xE4,0x02,0xE8,0x16,0x2E,0x07,0xEF,0x92,0x2E,0x17,0xEF,0xAE,0x8A,0x01,0x4E,0x04, +0x00,0x03,0xFE,0x02,0xE4,0x02,0xE8,0x0C,0x3C,0x17,0xF7,0xD8,0x84,0x03,0xFE,0x0C, +0x85,0xE5,0x40,0xF0,0x3D,0xF6,0x3C,0x07,0xF7,0xD9,0xE0,0x0F,0xE8,0x04,0x2E,0x07, +0xEF,0xA8,0xC0,0x0A,0x3C,0xF7,0xF7,0x92,0x5E,0xF7,0x81,0x91,0xE9,0x11,0x2E,0x07, +0xEF,0xA8,0x5A,0x08,0x01,0x0E,0x3C,0x07,0xF7,0xDA,0x3C,0x17,0xF7,0x8E,0xFE,0x02, +0xE0,0x20,0xE9,0xBB,0x84,0x00,0x3E,0x07,0xF4,0x9D,0x84,0x01,0xD5,0xA8,0x2E,0x07, +0xF4,0x9D,0x8C,0x01,0x96,0x00,0xE6,0x02,0x3E,0x07,0xF4,0x9D,0xE9,0x16,0x84,0x00, +0x3E,0x07,0xF4,0x9E,0x49,0xFF,0xFF,0x29,0x49,0x00,0x0B,0xD2,0x49,0xFF,0xD9,0x93, +0xE6,0x03,0xE9,0x03,0xFA,0x0E,0xD5,0x03,0x44,0x00,0x45,0xF6,0x84,0x22,0x49,0x00, +0x2D,0x63,0x84,0x01,0x3E,0x07,0xF4,0xB1,0xFC,0x80,0xFC,0x20,0x49,0x00,0x18,0x6C, +0x5A,0x08,0x02,0x08,0x49,0xFF,0xFD,0x07,0x84,0x00,0x3E,0x07,0xF4,0xB1,0xD5,0x0C, +0x49,0xFF,0xD9,0x79,0xE6,0x03,0xE9,0xF7,0x49,0x00,0x14,0xD1,0xC0,0xF4,0x3C,0x0D, +0xFF,0x13,0x5A,0x00,0x01,0xF1,0x2E,0x07,0xF4,0xB1,0xC8,0x44,0x2E,0x17,0xF4,0xAB, +0x5A,0x18,0x01,0x04,0x49,0x00,0x2F,0x9B,0x84,0x00,0x49,0x00,0x2F,0xBC,0x5A,0x00, +0x01,0x0E,0x3C,0x0D,0xFB,0xD6,0x54,0x00,0x10,0x00,0xC0,0x3A,0x2E,0x07,0xEF,0xB6, +0xC8,0x37,0x49,0x00,0x14,0xB4,0x5A,0x08,0x01,0x34,0x49,0x00,0x18,0x3D,0x5A,0x00, +0x02,0x30,0x49,0x00,0x14,0xAC,0x5A,0x08,0x01,0x28,0x2E,0x07,0xF4,0xA0,0x8C,0x01, +0x3E,0x07,0xF4,0xA0,0x49,0x00,0x14,0xA3,0x80,0xC0,0xC8,0x0E,0x84,0x01,0x80,0x26, +0x49,0x00,0x2F,0x7E,0x80,0x06,0x44,0x10,0x75,0x30,0x49,0x00,0x2F,0x6B,0x80,0x06, +0x84,0x21,0x49,0x00,0x2F,0x75,0x2E,0x07,0xF4,0xA0,0x4E,0x02,0x00,0xDE,0x84,0xC0, +0x3E,0x60,0x0A,0xDC,0x49,0xFF,0xFE,0xC1,0x84,0x01,0x3E,0x07,0xF4,0xB1,0x3E,0x67, +0xF4,0xA0,0x84,0xE1,0xD5,0x06,0x49,0x00,0x14,0x13,0x4E,0x02,0x00,0xC4,0x84,0xE0, +0x2E,0x07,0xF4,0xB1,0x5A,0x08,0x03,0x04,0x49,0xFF,0xFE,0xBB,0x2E,0x67,0xF4,0xB1, +0x5A,0x68,0x01,0x0C,0x80,0x06,0x49,0x00,0x14,0x7B,0x49,0x00,0x2B,0x12,0x84,0x02, +0x3E,0x07,0xF4,0xB1,0x3E,0x67,0xF4,0xA9,0x2E,0x07,0xF4,0xB1,0x5A,0x00,0x02,0x04, +0x48,0x00,0x00,0x87,0x2E,0x07,0xF4,0xA9,0x5A,0x08,0x01,0x1D,0x49,0x00,0x0B,0x3C, +0x84,0x00,0x3E,0x07,0xF4,0xA9,0x49,0x00,0x2C,0xDF,0xFA,0x18,0x49,0x00,0x2C,0xE7, +0x49,0xFF,0xD8,0xF9,0xE6,0x03,0xE9,0x03,0xFA,0x18,0xD5,0x03,0x44,0x00,0x46,0x00, +0x49,0x00,0x2C,0xDD,0x49,0xFF,0xFD,0x94,0x49,0x00,0x0B,0x2A,0x3E,0x07,0xE2,0xCE, +0xD5,0x67,0x2E,0x07,0xF4,0xAB,0x5A,0x08,0x01,0x06,0x84,0x04,0x3E,0x07,0xF4,0xB1, +0xD5,0x5F,0x2E,0x07,0xF4,0x9C,0x84,0xC0,0x5A,0x08,0x01,0x13,0x84,0x00,0x3E,0x07, +0xF4,0x9C,0x49,0x00,0x14,0x34,0x5A,0x08,0x01,0x06,0x49,0x00,0x0B,0x21,0x3E,0x07, +0xE2,0xCE,0x3C,0x67,0xF7,0x92,0x44,0x00,0x00,0x82,0x40,0x60,0x18,0x07,0x2E,0x07, +0xE2,0xCE,0x49,0x00,0x0B,0x48,0x5A,0x08,0x01,0x44,0xCE,0x42,0x2E,0x67,0xF6,0x27, +0x97,0xB0,0x5A,0x68,0x01,0x37,0x49,0x00,0x14,0x1A,0xC8,0x33,0x49,0x00,0x28,0xB6, +0x49,0xFF,0xCD,0x6D,0xC8,0x2E,0x84,0x0C,0xFA,0x2E,0x49,0x00,0x2E,0xE3,0x84,0x0C, +0x80,0x26,0x49,0x00,0x2E,0xED,0x80,0x06,0x49,0xFF,0xCD,0x57,0x49,0xFF,0xCD,0x5F, +0x5A,0x00,0x01,0x0C,0x84,0x0C,0x49,0x00,0x2E,0xFE,0x5A,0x08,0x01,0xF9,0x84,0x00, +0x3E,0x07,0xF6,0x27,0x3E,0x07,0xF6,0x26,0x84,0x0C,0x84,0x20,0x49,0x00,0x2E,0xD8, +0x2E,0x67,0xF6,0x27,0x97,0xB0,0x5A,0x68,0x01,0x0D,0x80,0x06,0x49,0x00,0x25,0xC3, +0x84,0x00,0x49,0xFF,0xCC,0x2D,0x80,0x06,0x49,0x00,0x25,0xC9,0x3E,0x67,0xF6,0x26, +0x49,0x00,0x0A,0xC2,0x49,0xFF,0xFD,0x2C,0x84,0x01,0x3E,0x07,0xF4,0x9C,0x2E,0x07, +0xF4,0xB1,0x5A,0x08,0x04,0x0E,0x3C,0x1D,0xFB,0xD6,0x44,0x0F,0xEF,0xFF,0xFE,0x0E, +0x3C,0x0F,0xFB,0xD6,0x49,0xFF,0xFB,0xFF,0x84,0x00,0x3E,0x07,0xF4,0xB1,0x49,0x00, +0x13,0xCE,0x80,0x40,0x5A,0x08,0x01,0x21,0x2E,0x10,0x0A,0xDC,0xC1,0x05,0x8E,0x21, +0x3E,0x10,0x0A,0xDC,0xD5,0x19,0x44,0x00,0x07,0xD0,0x80,0x62,0x49,0x00,0x2D,0xF1, +0xD5,0x13,0x49,0x00,0x27,0x67,0x4E,0x03,0xFF,0x3C,0x49,0x00,0x13,0xB8,0x4E,0x02, +0xFF,0x0E,0x48,0xFF,0xFF,0x36,0x49,0x00,0x13,0xB2,0x5A,0x00,0x01,0x04,0x48,0xFF, +0xFF,0x2A,0x48,0xFF,0xFF,0x1E,0x80,0x07,0xFC,0xA0,0xFC,0x00,0x49,0x00,0x27,0x45, +0xC0,0x6A,0x49,0xFF,0xF9,0x76,0x2E,0x07,0xE2,0xCD,0x5A,0x08,0x03,0x05,0x49,0x00, +0x26,0x2C,0xD5,0x52,0x49,0x00,0x26,0x15,0x49,0xFF,0xE8,0x5E,0x49,0xFF,0xFB,0xDF, +0x49,0x00,0x26,0x23,0x49,0xFF,0xFC,0xF7,0x5A,0x08,0x01,0x08,0x84,0x00,0x49,0x00, +0x13,0x97,0x49,0xFF,0xDA,0xF4,0xD5,0x4F,0x49,0xFF,0xD8,0x9E,0x2E,0x17,0xE2,0xCF, +0x5A,0x00,0x01,0x0C,0x5A,0x10,0x01,0x0C,0x3C,0x0D,0xFB,0xD6,0x96,0x36,0xC8,0x05, +0x2E,0x07,0xEF,0xB6,0x5A,0x08,0x01,0x1E,0x84,0x0A,0xC1,0x02,0x84,0x05,0x3E,0x07, +0xEF,0x18,0x84,0x06,0xC1,0x02,0x84,0x05,0x3C,0x1D,0xFB,0xD6,0x3E,0x07,0xEF,0x17, +0x44,0x0F,0xFF,0xBF,0xFE,0x0E,0x3C,0x0F,0xFB,0xD6,0x84,0x23,0x84,0x00,0x3E,0x07, +0xEF,0xB6,0x3E,0x07,0xE2,0xCF,0x3E,0x17,0xF4,0xB3,0x3E,0x07,0xF4,0xB0,0xD5,0x23, +0x49,0xFF,0xFB,0xB1,0x2E,0x07,0xF4,0x21,0xC0,0x18,0x5A,0x08,0x01,0x12,0x49,0x00, +0x25,0xB8,0xC0,0x03,0x49,0x00,0x18,0x1E,0x44,0x00,0x04,0x84,0x49,0x00,0x16,0x51, +0x84,0x00,0x3E,0x07,0xF4,0x21,0x84,0x01,0x3E,0x07,0xF4,0xB3,0xD5,0x0C,0x5A,0x08, +0x02,0xF5,0x49,0x00,0x25,0xA9,0xD5,0xEE,0x49,0xFF,0xFE,0x61,0xC8,0xF5,0x49,0xFF, +0xFC,0x95,0xD5,0xF2,0xFC,0x80,0x00,0x00,0xFC,0x40,0x49,0x00,0x16,0xC5,0x80,0xE0, +0x49,0x00,0x18,0x25,0x5A,0x08,0x01,0x05,0x84,0x20,0x49,0x00,0x2E,0x11,0x84,0x01, +0x49,0x00,0x2E,0x29,0x5A,0x08,0x01,0x07,0x84,0x20,0x49,0x00,0x2E,0x09,0x49,0x00, +0x16,0x9B,0x5A,0x78,0x02,0x5C,0x2E,0x07,0xF4,0xAC,0x5A,0x00,0x01,0x04,0x48,0x00, +0x00,0x8F,0x49,0x00,0x18,0x0C,0x5A,0x00,0x01,0x04,0x48,0x00,0x00,0x89,0x2E,0x07, +0xF4,0xA8,0x5A,0x08,0x01,0x05,0x84,0x00,0x3E,0x07,0xF4,0xA8,0x44,0x64,0x80,0x80, +0x44,0x0B,0xFF,0xFF,0xB4,0x26,0xFE,0x0E,0xB6,0x06,0xB4,0x26,0x46,0x0F,0xFE,0xFF, +0x50,0x00,0x0F,0xFF,0xFE,0x0E,0xB6,0x06,0xB4,0x26,0x46,0x0F,0xFD,0xFF,0x50,0x00, +0x0F,0xFF,0xFE,0x0E,0xB6,0x06,0x84,0x00,0x49,0xFF,0xFB,0x0C,0x84,0x00,0x3E,0x07, +0xF4,0xAC,0x84,0x00,0x49,0x00,0x17,0xE7,0xB4,0x26,0x44,0x04,0x00,0x00,0xFE,0x0F, +0xB6,0x06,0xB4,0x26,0x46,0x00,0x01,0x00,0xFE,0x0F,0xB6,0x06,0xB4,0x26,0x46,0x00, +0x02,0x00,0xFE,0x0F,0xB6,0x06,0x44,0x10,0x01,0x90,0x84,0x01,0x49,0x00,0x2D,0xB2, +0x84,0x01,0x80,0x20,0x49,0x00,0x2D,0xBC,0x49,0x00,0x17,0xC9,0x80,0xC0,0xC8,0x47, +0x84,0x01,0x49,0x00,0x2D,0xD0,0x81,0x20,0x5A,0x08,0x01,0xF8,0x80,0x26,0x49,0x00, +0x2D,0xAF,0x80,0x09,0x49,0x00,0x17,0xBF,0xD5,0x3A,0xCF,0x39,0x2E,0x07,0xF4,0xAE, +0x44,0x15,0x10,0x20,0xB6,0x01,0xC8,0x26,0x2E,0x67,0xF4,0xAC,0x5A,0x68,0x01,0x23, +0x80,0x26,0x80,0x07,0x49,0xFF,0xD6,0x84,0x80,0x06,0x49,0xFF,0xD6,0x45,0x80,0x06, +0x49,0xFF,0xD6,0x54,0x80,0x06,0x80,0x26,0x49,0xFF,0xD6,0x00,0x80,0x07,0x80,0x26, +0x49,0xFF,0xFA,0x5B,0x5A,0x08,0x01,0x08,0x3E,0x07,0xF4,0xAE,0x80,0x07,0x49,0x00, +0x17,0x9A,0xD5,0x08,0x80,0x06,0x49,0xFF,0xD6,0x56,0x80,0x07,0x80,0x26,0x49,0xFF, +0xD5,0xED,0x2E,0x67,0xF4,0xAE,0xCE,0x0B,0x2E,0x07,0xF4,0xA8,0x5A,0x08,0x01,0x08, +0x49,0x00,0x17,0x85,0x5A,0x08,0x01,0x04,0x3E,0x67,0xF4,0xA8,0x2E,0x07,0xF4,0xB3, +0xC0,0x19,0x5A,0x78,0x01,0x0E,0x84,0x04,0x3E,0x07,0xF4,0xB3,0x84,0x00,0x49,0x00, +0x27,0x25,0x84,0x00,0x49,0x00,0x01,0xAB,0x49,0x00,0x24,0xFB,0xD5,0x0B,0x5A,0x08, +0x04,0x0A,0x84,0x01,0x3E,0x07,0xF4,0xB3,0x84,0x01,0x49,0x00,0x27,0x17,0x49,0x00, +0x24,0xFA,0x2E,0x07,0xF4,0xB3,0xE6,0x05,0xE8,0x19,0x44,0xF0,0x6F,0x08,0x38,0x07, +0x80,0x00,0x40,0xF0,0x3C,0x00,0xDD,0x0F,0x06,0x12,0x18,0x1E,0x0C,0x00,0x49,0xFF, +0xFB,0xDB,0xD5,0x0C,0x49,0x00,0x20,0xEA,0xD5,0x09,0x49,0xFF,0xFC,0x16,0xD5,0x06, +0x49,0xFF,0xFE,0xB5,0xD5,0x03,0x49,0xFF,0xFC,0x1E,0xFC,0xC0,0xFC,0x40,0x2E,0x07, +0xF4,0xFB,0x84,0xC1,0x96,0x06,0xC0,0x2C,0x2E,0x07,0xF4,0xFB,0x58,0x00,0x00,0x02, +0x3E,0x07,0xF4,0xFB,0x49,0x00,0x07,0xD4,0x84,0x00,0x80,0x80,0xFA,0x2F,0xFA,0x58, +0x84,0x61,0x49,0x00,0x07,0x55,0xFA,0x04,0x49,0xFF,0xD6,0xF2,0x84,0x00,0x80,0x80, +0xFA,0x2F,0xFA,0x40,0x84,0x61,0x49,0x00,0x07,0x4B,0x44,0x00,0x00,0x32,0x49,0xFF, +0xD6,0xE7,0x84,0x00,0x49,0x00,0x29,0xF8,0x84,0x01,0x49,0x00,0x12,0x33,0x3E,0x67, +0xF4,0xAF,0x3E,0x67,0xF6,0x2A,0x49,0x00,0x11,0xF6,0x49,0x00,0x12,0xEC,0x84,0xC0, +0x84,0xE1,0x49,0x00,0x2C,0xD8,0x49,0x00,0x2C,0xAE,0x2E,0x07,0xF4,0xAF,0xE6,0x05, +0xE8,0xF9,0x44,0xF0,0x6F,0xB0,0x38,0x07,0x81,0x01,0x40,0xF0,0x3C,0x00,0xDD,0x0F, +0x0A,0x00,0x12,0x00,0x5E,0x00,0xDE,0x00,0x9A,0x01,0x49,0x00,0x12,0x5D,0x48,0x00, +0x00,0xE8,0x84,0x01,0x3E,0x77,0xF4,0xB2,0x49,0x00,0x2C,0x81,0x3E,0x67,0xF4,0xB3, +0x3E,0x67,0xF4,0xB1,0x3E,0x67,0xF4,0xAA,0x49,0x00,0x12,0x01,0x5A,0x08,0x01,0x17, +0x49,0x00,0x12,0x03,0x5A,0x08,0x02,0x04,0x84,0x03,0xD5,0x02,0x84,0x04,0x3E,0x07, +0xF4,0xAF,0x2E,0x07,0xF6,0x25,0xC0,0x03,0x84,0x04,0xD5,0x05,0x2E,0x07,0xF6,0x24, +0xC0,0xC9,0x84,0x03,0x3E,0x07,0xF4,0xAF,0xD5,0xC5,0x84,0x02,0xD5,0xFC,0x49,0x00, +0x11,0x90,0x49,0x00,0x11,0xA0,0x3E,0x67,0xF4,0xB4,0x49,0x00,0x11,0x93,0x81,0x20, +0x5A,0x08,0x01,0x23,0x3E,0x07,0xF4,0xAA,0x84,0x00,0x49,0x00,0x26,0x77,0x49,0x00, +0x07,0x5F,0x84,0x00,0x80,0x80,0xFA,0x2F,0xFA,0x58,0x80,0x69,0x49,0x00,0x06,0xE0, +0xFA,0x04,0x49,0xFF,0xD6,0x7D,0x84,0x00,0x80,0x80,0xFA,0x2F,0xFA,0x40,0x80,0x69, +0x49,0x00,0x06,0xD6,0x44,0x00,0x00,0x32,0x49,0xFF,0xD6,0x72,0x84,0x00,0x3E,0x67, +0xF4,0xB2,0x49,0x00,0x29,0x81,0x49,0x00,0x11,0x86,0x2E,0x17,0xF4,0xAA,0x5A,0x08, +0x01,0x0B,0x84,0x00,0x3C,0x0F,0xFF,0x15,0x5A,0x18,0x01,0x06,0x49,0x00,0x12,0x73, +0x48,0x00,0x00,0x87,0xC9,0x87,0x49,0xFF,0xFE,0x71,0x48,0xFF,0xFF,0x84,0x2E,0x07, +0xF6,0x2C,0xC8,0x04,0x2E,0x07,0xF6,0x2B,0xC0,0x1A,0x2E,0x07,0xF6,0x24,0x54,0x90, +0x00,0xFF,0x4E,0x93,0x00,0x15,0x80,0x09,0x3E,0x77,0xF6,0x25,0x3E,0x77,0xF4,0xAA, +0x49,0x00,0x26,0x34,0x3E,0x97,0xF4,0xB2,0x49,0x00,0x11,0x5D,0x5A,0x08,0x01,0x08, +0x2E,0x07,0xF4,0xAA,0x3C,0x9F,0xFF,0x15,0x5A,0x00,0x01,0xDA,0x3E,0x67,0xF6,0x24, +0x2E,0x07,0xF6,0x25,0xC8,0x05,0x49,0x00,0x11,0x35,0x49,0x00,0x11,0x4C,0x49,0x00, +0x11,0x3A,0x81,0x20,0x5A,0x08,0x01,0x22,0x3E,0x07,0xF4,0xAA,0x84,0x00,0x49,0x00, +0x26,0x15,0x49,0x00,0x06,0xFD,0x84,0x00,0x80,0x80,0xFA,0x2F,0xFA,0x41,0x80,0x69, +0x49,0x00,0x06,0x7E,0x44,0x00,0x00,0x96,0x49,0xFF,0xD6,0x1A,0x84,0x00,0x80,0x80, +0xFA,0x2F,0xFA,0x59,0x80,0x69,0x49,0x00,0x06,0x73,0xFA,0x04,0x49,0xFF,0xD6,0x10, +0x49,0x00,0x11,0xC7,0x3E,0x67,0xF4,0xB2,0x49,0x00,0x11,0x03,0x5A,0x08,0x01,0x0A, +0x3E,0x67,0xF6,0x28,0x49,0x00,0x29,0x18,0x2E,0x07,0xF4,0xAA,0x5A,0x00,0x01,0x29, +0x2E,0x07,0xF4,0xAA,0x4E,0x03,0xFF,0x27,0xD5,0x9F,0x3E,0x67,0xF6,0x2C,0x2E,0x07, +0xF6,0x25,0xC0,0x22,0x2E,0x07,0xF6,0x2A,0xC0,0x1F,0x84,0x00,0x3E,0x77,0xF4,0xAA, +0x49,0x00,0x25,0xDC,0x2E,0xA7,0xF4,0xAA,0x85,0x20,0x3E,0x67,0xF4,0xB2,0x3C,0x9F, +0xFF,0x15,0x5A,0xA8,0x01,0x12,0x3E,0x77,0xF6,0x24,0x3E,0x67,0xF6,0x25,0x3E,0x67, +0xF6,0x2A,0x49,0x00,0x11,0xF0,0x80,0x0A,0x80,0x29,0x49,0xFF,0xD4,0x77,0x3E,0x77, +0xF4,0xAF,0x48,0xFF,0xFF,0x00,0x49,0x00,0x10,0xDE,0x81,0x20,0x5A,0x08,0x01,0x22, +0x3E,0x07,0xF4,0xAA,0x84,0x00,0x49,0x00,0x25,0xB9,0x49,0x00,0x06,0xA1,0x84,0x00, +0x80,0x80,0xFA,0x2F,0xFA,0x41,0x80,0x69,0x49,0x00,0x06,0x22,0x44,0x00,0x00,0x96, +0x49,0xFF,0xD5,0xBE,0x84,0x00,0x80,0x80,0xFA,0x2F,0xFA,0x59,0x80,0x69,0x49,0x00, +0x06,0x17,0xFA,0x04,0x49,0xFF,0xD5,0xB4,0x49,0x00,0x11,0x6B,0x3E,0x67,0xF4,0xB2, +0x49,0x00,0x10,0xA7,0x5A,0x08,0x01,0x08,0x49,0x00,0x28,0xBE,0x2E,0x07,0xF4,0xAA, +0x5A,0x00,0x01,0xCF,0x2E,0x07,0xF4,0xAA,0x4E,0x03,0xFE,0xCD,0x44,0x10,0x00,0xEB, +0x49,0x00,0x2B,0xA5,0x49,0x00,0x10,0xD2,0x84,0x0A,0x49,0xFF,0xD5,0x99,0x84,0x01, +0x44,0x10,0x00,0xEB,0x49,0x00,0x2B,0x9B,0x48,0xFF,0xFE,0xBD,0x2E,0x07,0xF4,0xB2, +0xDD,0x9E,0x3E,0x07,0xF4,0xB2,0xDD,0x9E,0xDD,0x9E,0x46,0x10,0x00,0x81,0xC8,0x06, +0x04,0x00,0x80,0x33,0x58,0x00,0x02,0x00,0xD5,0x06,0x04,0x20,0x80,0x33,0x44,0x0F, +0xFD,0xFF,0xFE,0x16,0x14,0x00,0x80,0x33,0xDD,0x9E,0x44,0x24,0x2C,0x14,0x84,0x30, +0xB4,0x62,0x96,0x1F,0xFE,0x5E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x24,0x2C,0x14, +0x96,0x1F,0xB4,0x62,0x40,0x10,0x20,0x08,0x44,0x0F,0xF0,0xFF,0xFE,0x1E,0xFE,0x0F, +0xB6,0x02,0xDD,0x9E,0xDD,0x9E,0xFC,0x00,0x44,0x24,0x80,0x10,0x84,0x01,0xB4,0x22, +0x96,0x46,0xC1,0x0B,0xAE,0x10,0x3E,0x07,0xF4,0xC3,0x3E,0x07,0xF4,0xC0,0x3C,0x0D, +0xFD,0x2E,0x8C,0x01,0x3C,0x0F,0xFD,0x2E,0x44,0x04,0x80,0x10,0xB4,0x00,0x92,0x0A, +0x96,0x06,0xC0,0x0E,0x44,0x04,0x80,0x11,0x84,0x24,0xAE,0x40,0x49,0x00,0x01,0x7A, +0x44,0x07,0x20,0x00,0xB4,0x00,0x92,0x08,0x96,0x06,0x49,0x00,0x01,0x58,0x44,0x04, +0x80,0x10,0x84,0x21,0xB4,0x00,0x92,0x08,0x96,0x06,0xC0,0x08,0x44,0x04,0x80,0x11, +0xAE,0x40,0x3E,0x17,0xF4,0xC1,0x3E,0x17,0xF4,0xC0,0x44,0x04,0x80,0x10,0xB4,0x20, +0x92,0x27,0x96,0x46,0xC1,0x04,0x44,0x1F,0xFF,0x80,0xAE,0x40,0xB4,0x00,0x92,0x05, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x10,0xFA,0x30,0xAE,0x40,0x44,0x04,0x80,0x10, +0xB4,0x20,0x92,0x26,0x96,0x46,0xC1,0x04,0x44,0x10,0x00,0x40,0xAE,0x40,0xB4,0x00, +0x92,0x18,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x13,0x84,0x21,0xAE,0x40,0x44,0x04, +0x80,0x10,0xB4,0x00,0x92,0x15,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x12,0xFA,0x30, +0xAE,0x40,0xFC,0x80,0xFC,0x00,0x44,0x24,0x80,0x10,0x84,0x21,0xB4,0x02,0x92,0x04, +0x96,0x06,0xC0,0x14,0xFA,0x00,0xAE,0x10,0x3E,0x17,0xF4,0xC2,0x3E,0x17,0xF4,0xC0, +0x3C,0x0D,0xFD,0x2F,0x8C,0x01,0x3C,0x0F,0xFD,0x2F,0x46,0x00,0x00,0x81,0x04,0x00, +0x00,0xF0,0x92,0x03,0x96,0x06,0x49,0x00,0x01,0x02,0x44,0x14,0x80,0x10,0xB4,0x01, +0x92,0x0B,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x11,0x84,0x48,0xAE,0x80,0xB4,0x01, +0x92,0x03,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x10,0x84,0x28,0xAE,0x40,0x44,0x04, +0x80,0x10,0xB4,0x20,0x92,0x22,0x96,0x46,0xC1,0x03,0x84,0x24,0xAE,0x40,0xB4,0x00, +0x92,0x01,0x96,0x06,0xC0,0x0E,0x44,0x04,0x80,0x90,0xB4,0x00,0x92,0x01,0x96,0x06, +0x5A,0x08,0x01,0x08,0x44,0x14,0x80,0x10,0x84,0x42,0xAE,0x88,0x3E,0x07,0xF4,0xB4, +0x44,0x14,0x80,0x10,0xB4,0x01,0x92,0x19,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x13, +0x84,0x42,0xAE,0x80,0xB4,0x01,0x92,0x16,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x12, +0x44,0x10,0x00,0x40,0xAE,0x40,0xFC,0x80,0xFC,0x00,0x80,0xC0,0x49,0xFF,0xCB,0xD4, +0x46,0x20,0x00,0x81,0x97,0x86,0x94,0x32,0x04,0x61,0x03,0x93,0x84,0x3B,0xFF,0x8E, +0xFF,0x87,0x14,0x61,0x03,0x93,0x49,0xFF,0xCB,0xC0,0xFC,0x80,0x44,0x24,0x50,0x1C, +0x40,0x00,0x40,0x08,0xB4,0x22,0x96,0x49,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x24, +0x50,0x1C,0x44,0x1F,0x00,0xFF,0xB4,0x62,0x40,0x00,0x20,0x08,0xFE,0x5E,0xFE,0x0F, +0xB6,0x02,0xDD,0x9E,0x3E,0x07,0xF4,0xC4,0xDD,0x9E,0x44,0x07,0x21,0x00,0x46,0x10, +0x20,0x00,0xB4,0x40,0xFE,0x57,0xB6,0x20,0x80,0x20,0xB4,0x01,0x92,0x19,0x96,0x06, +0xC8,0xFD,0xDD,0x9E,0x44,0x27,0x21,0x1C,0x96,0x1F,0x40,0x10,0x60,0x08,0xB4,0x62, +0x46,0x0F,0x0F,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E, +0x44,0x24,0x80,0x84,0x96,0x06,0xB4,0x62,0x40,0x10,0x40,0x08,0x44,0x0E,0xFF,0xFF, +0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x04,0x80,0x04,0xA6,0x02,0x92,0x01, +0x96,0x06,0xC0,0x08,0x44,0x04,0x80,0x06,0x84,0x22,0xAE,0x40,0x84,0x01,0x3E,0x07, +0xF4,0xC5,0x44,0x04,0x80,0x04,0x84,0x21,0xA6,0x02,0x96,0x06,0xC0,0x06,0x44,0x04, +0x80,0x06,0xAE,0x40,0x3E,0x17,0xF4,0xC4,0xDD,0x9E,0x44,0x04,0x80,0x04,0xA6,0x03, +0x92,0x01,0x96,0x06,0xDD,0x9E,0x44,0x04,0x10,0x04,0x44,0x10,0x00,0x55,0xAE,0x40, +0x44,0x1F,0xFF,0xAA,0xAE,0x41,0x44,0x10,0x00,0x66,0xAE,0x42,0x40,0x00,0x00,0x09, +0xDD,0x9E,0x44,0x14,0x10,0x08,0xB6,0x01,0x44,0x14,0x80,0x04,0xA6,0x0B,0x92,0x01, +0x96,0x06,0xC0,0xFD,0x44,0x04,0x80,0x07,0x84,0x22,0xAE,0x40,0xDD,0x9E,0xFC,0x00, +0x80,0xC0,0x92,0x10,0x96,0x00,0x49,0xFF,0xFF,0xEE,0x40,0x03,0x20,0x09,0x96,0x00, +0x49,0xFF,0xFF,0xE9,0x96,0x30,0x49,0xFF,0xFF,0xE6,0xFC,0x80,0x44,0x24,0x10,0x00, +0x96,0x06,0xA6,0x53,0x54,0x10,0x80,0xFE,0xFE,0x0F,0xAE,0x13,0xDD,0x9E,0x44,0x14, +0x10,0x00,0xAE,0x08,0xDD,0x9E,0x44,0x04,0x10,0x12,0x84,0x20,0xAE,0x40,0xDD,0x9E, +0x44,0x14,0x10,0x0C,0xB6,0x01,0xDD,0x9E,0x44,0x24,0x10,0x14,0x96,0x06,0xA6,0x53, +0x54,0x10,0x80,0xFE,0xFE,0x0F,0xAE,0x13,0xDD,0x9E,0x44,0x14,0x10,0x10,0xAE,0x0B, +0xDD,0x9E,0x44,0x14,0x10,0x10,0xAE,0x08,0xDD,0x9E,0x3E,0x07,0xE2,0xD4,0xDD,0x9E, +0x2E,0x07,0xE2,0xD4,0xDD,0x9E,0x44,0x37,0x20,0x00,0x54,0x10,0x00,0x01,0xB4,0x83, +0x40,0x20,0xA0,0x08,0x44,0x1F,0xFE,0xFF,0xFE,0x66,0xFE,0x57,0xB6,0x23,0x3E,0x07, +0xE2,0xD4,0xDD,0x9E,0x44,0x07,0x20,0x00,0xB4,0x00,0x92,0x18,0x96,0x06,0xDD,0x9E, +0x84,0x01,0x3E,0x07,0xE2,0xD5,0xDD,0x9E,0x2E,0x07,0xE2,0xD5,0xDD,0x9E,0x84,0x00, +0x3E,0x07,0xE2,0xD5,0xDD,0x9E,0xC8,0x07,0x44,0x17,0x10,0x18,0xA6,0x08,0x54,0x00, +0x00,0xFE,0xD5,0x08,0x5A,0x08,0x01,0x0F,0x44,0x17,0x10,0x18,0xA6,0x08,0x58,0x00, +0x00,0x01,0xAE,0x08,0x44,0x17,0x10,0x0C,0xB4,0x01,0x58,0x00,0x01,0x00,0xB6,0x01, +0xDD,0x9E,0x5A,0x08,0x02,0x0D,0x44,0x17,0x10,0x18,0xA6,0x08,0x58,0x00,0x00,0x01, +0xAE,0x08,0x44,0x07,0x10,0x0C,0xB4,0x20,0x84,0x20,0xB6,0x20,0xDD,0x9E,0x3C,0x0D, +0xFD,0x36,0xC0,0x0B,0xA4,0x80,0x3C,0x13,0xFA,0x68,0x8E,0x21,0xE0,0x41,0xE8,0x05, +0xA4,0x40,0x8C,0x21,0x96,0x49,0xAC,0x40,0xA4,0x80,0x3C,0x13,0xFA,0x6A,0xE2,0x41, +0xE9,0x0C,0x84,0x20,0xAC,0x40,0x44,0x06,0x00,0x34,0x44,0x10,0x00,0x5A,0xB6,0x20, +0x84,0x01,0x3E,0x07,0xE2,0xD6,0xD5,0x0B,0x3C,0x1D,0xFD,0x38,0xC1,0x08,0xA4,0x00, +0x88,0x01,0x44,0x16,0x00,0x34,0xA6,0x00,0x96,0x00,0xB6,0x01,0x44,0x06,0x00,0x00, +0xB4,0x00,0x92,0x08,0x96,0x06,0x5A,0x08,0x01,0x0B,0x44,0x06,0x00,0x30,0xB4,0x20, +0x58,0x10,0x80,0x01,0xB6,0x20,0xB4,0x20,0x96,0x46,0xC9,0xFE,0xDD,0x9E,0xFC,0x40, +0x3C,0x0D,0xFD,0x36,0x84,0x20,0xAC,0x40,0x3C,0x1D,0xFD,0x38,0xC1,0x08,0xA4,0x00, +0x88,0x01,0x44,0x16,0x00,0x34,0xA6,0x00,0x96,0x00,0xB6,0x01,0x84,0xE0,0x44,0x94, +0x80,0x00,0x44,0xA4,0x80,0x80,0x00,0x64,0x80,0x02,0xB4,0x0A,0x92,0xC3,0x92,0x13, +0x96,0x06,0xFF,0x86,0xC6,0x16,0x44,0x04,0x80,0x02,0x84,0x28,0xAE,0x40,0x44,0x06, +0x00,0x00,0xB4,0x00,0x92,0x08,0x96,0x06,0x5A,0x08,0x01,0x32,0x44,0x06,0x00,0x30, +0xB4,0x20,0x58,0x10,0x80,0x01,0xB6,0x20,0xB4,0x20,0x96,0x46,0xC9,0xFE,0xFC,0xC0, +0x00,0x04,0x80,0x02,0x40,0x10,0x10,0x09,0xB4,0x0A,0x92,0x14,0x96,0x06,0xFE,0x0E, +0xC0,0x08,0x44,0x04,0x80,0x02,0xFA,0x20,0xAE,0x40,0x49,0xFF,0xFF,0x8A,0x80,0xE6, +0x00,0x04,0x80,0x02,0x40,0x10,0x18,0x09,0xB4,0x0A,0x92,0x16,0x96,0x06,0xFE,0x0E, +0xC0,0x07,0x44,0x04,0x80,0x02,0x44,0x10,0x00,0x40,0xAE,0x40,0xFC,0xC0,0x84,0x01, +0x8C,0xE1,0x49,0xFF,0xD3,0x0C,0x5C,0xF3,0xA7,0x11,0xE9,0xBE,0xFC,0xC0,0x44,0x16, +0x00,0x34,0xB6,0x01,0xDD,0x9E,0x3C,0x0F,0xFD,0x39,0x02,0x0F,0x80,0x00,0x3C,0x4B, +0xFA,0x6A,0x3C,0x1F,0xFD,0x38,0x3C,0x5B,0xFA,0x69,0x3C,0x2F,0xFD,0x37,0x3C,0x3F, +0xFD,0x36,0x3C,0x0B,0xFA,0x68,0xDD,0x9E,0x3C,0x0B,0xFA,0x6A,0xDD,0x9E,0x3E,0x07, +0xE2,0xD6,0xDD,0x9E,0x2E,0x07,0xE2,0xD6,0xDD,0x9E,0x3C,0x0F,0xFD,0x33,0xDD,0x9E, +0x3C,0x0F,0xFD,0x32,0xDD,0x9E,0xFC,0x20,0x44,0x14,0x80,0x00,0x44,0x04,0x80,0x80, +0xA6,0x8A,0xB4,0x00,0x92,0x10,0x96,0x06,0xFE,0x16,0xC0,0x05,0x44,0x04,0x80,0x02, +0x84,0x41,0xAE,0x80,0xA6,0x0A,0x40,0x10,0x04,0x09,0x44,0x04,0x80,0x80,0xB4,0x00, +0x92,0x11,0x96,0x06,0xFE,0x0E,0xC0,0x05,0x44,0x04,0x80,0x02,0x84,0x22,0xAE,0x40, +0x44,0x04,0x80,0x00,0xA6,0x02,0x40,0x10,0x08,0x09,0x44,0x04,0x80,0x80,0xB4,0x00, +0x92,0x12,0x96,0x06,0xFE,0x0E,0x4E,0x02,0x00,0xA4,0x44,0x04,0x80,0x02,0x84,0x24, +0xAE,0x40,0x3C,0x0D,0xFD,0x37,0x3C,0x2D,0xFD,0x39,0x84,0x20,0xAC,0x40,0xC2,0x06, +0x44,0x16,0x00,0x28,0xB4,0x21,0x96,0x48,0xAE,0x50,0xC0,0x0B,0xA4,0x80,0x3C,0x13, +0xFA,0x69,0x8E,0x21,0xE0,0x41,0xE8,0x05,0xA4,0x40,0x8C,0x21,0x96,0x49,0xAC,0x40, +0x44,0x06,0x00,0x00,0xB4,0x00,0x92,0x08,0x96,0x06,0x5A,0x00,0x01,0x06,0x84,0xC0, +0x44,0x76,0x00,0x30,0xD5,0x12,0x44,0x06,0x00,0x30,0xB4,0x20,0x58,0x10,0x80,0x01, +0xB6,0x20,0xB4,0x20,0x96,0x46,0xC9,0xFE,0xD5,0xF3,0x84,0x01,0x8C,0xC1,0x49,0xFF, +0xD2,0x86,0x5C,0xF3,0x08,0x01,0xE8,0x66,0x44,0x34,0x80,0x00,0x44,0x24,0x80,0x80, +0xA6,0x1A,0x40,0x10,0x0C,0x09,0xB4,0x02,0x92,0x13,0x96,0x06,0xFE,0x0E,0xC0,0x16, +0x44,0x04,0x80,0x02,0x84,0x28,0xAE,0x40,0x44,0x06,0x00,0x00,0xB4,0x00,0x92,0x08, +0x96,0x06,0x5A,0x08,0x01,0x50,0x44,0x06,0x00,0x30,0xB4,0x20,0x58,0x10,0x80,0x01, +0xB6,0x20,0xB4,0x20,0x96,0x46,0xC9,0xFE,0xD5,0x45,0xA6,0x1A,0x40,0x10,0x10,0x09, +0xB4,0x02,0x92,0x14,0x96,0x06,0xFE,0x0E,0xC0,0x2D,0x44,0x04,0x80,0x02,0xFA,0x20, +0xAE,0x40,0x3C,0x2D,0xFD,0x39,0x3C,0x0D,0xFD,0x37,0xC2,0x08,0xA4,0x40,0x88,0x22, +0x44,0x26,0x00,0x38,0xB4,0x42,0x96,0x90,0xAE,0x88,0xC0,0x0B,0xA4,0x80,0x3C,0x13, +0xFA,0x69,0x8E,0x21,0xE0,0x41,0xE8,0x05,0xA4,0x40,0x8C,0x21,0x96,0x49,0xAC,0x40, +0x44,0x06,0x00,0x00,0xB4,0x00,0x92,0x08,0x96,0x06,0x5A,0x00,0x01,0x04,0x84,0xC0, +0xD5,0x09,0xB4,0x07,0x58,0x00,0x00,0x01,0xB6,0x07,0xB4,0x07,0x96,0x06,0xC8,0xFE, +0xD5,0xF7,0x44,0x04,0x80,0x00,0xA6,0x02,0x40,0x10,0x14,0x09,0x44,0x04,0x80,0x80, +0xB4,0x00,0x92,0x15,0x96,0x06,0xFE,0x0E,0xC0,0x99,0x44,0x04,0x80,0x02,0xFA,0x30, +0xAE,0x40,0x3C,0x0D,0xFD,0x33,0xC0,0x04,0x3C,0x0D,0xFD,0x33,0xDD,0x20,0x44,0x04, +0x80,0x00,0xA6,0x02,0x40,0x10,0x0C,0x09,0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x13, +0x96,0x06,0xFE,0x0E,0xC8,0x14,0x44,0x04,0x80,0x00,0xA6,0x02,0x40,0x10,0x10,0x09, +0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x14,0x96,0x06,0xFE,0x0E,0xC0,0x1D,0x44,0x04, +0x80,0x02,0xFA,0x20,0xAE,0x40,0x49,0xFF,0xFE,0x64,0xD5,0x16,0x44,0x04,0x80,0x02, +0x84,0x28,0xAE,0x40,0x44,0x06,0x00,0x00,0xB4,0x00,0x92,0x08,0x96,0x06,0x5A,0x08, +0x01,0xE4,0x44,0x06,0x00,0x30,0xB4,0x20,0x58,0x10,0x80,0x01,0xB6,0x20,0xB4,0x20, +0x96,0x46,0xC9,0xFE,0xD5,0xD9,0x44,0x14,0x80,0x00,0xA6,0x0A,0x40,0x20,0x14,0x09, +0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x15,0x96,0x06,0xFE,0x16,0xC0,0x05,0x44,0x04, +0x80,0x02,0xFA,0x50,0xAE,0x80,0xA6,0x0A,0x40,0x10,0x18,0x09,0x44,0x04,0x80,0x80, +0xB4,0x00,0x92,0x16,0x96,0x06,0xFE,0x0E,0xC0,0x06,0x44,0x04,0x80,0x02,0x44,0x10, +0x00,0x40,0xAE,0x40,0xFC,0xA0,0x46,0x20,0x00,0x83,0x96,0x06,0x04,0x31,0x00,0x14, +0x94,0x41,0x84,0x1D,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x14,0xDD,0x9E,0x46,0x00, +0x00,0x83,0x04,0x00,0x00,0x16,0x92,0x10,0x54,0x00,0x03,0xFF,0xDD,0x9E,0xFC,0x00, +0x46,0x60,0x00,0x83,0x44,0x01,0x00,0x00,0xA0,0x71,0xFE,0x0F,0xA8,0x31,0x84,0x01, +0x49,0xFF,0xD1,0xA5,0xA0,0x71,0x44,0x0E,0xFF,0xFF,0xFE,0x0E,0xA8,0x31,0xFC,0x80, +0x46,0x20,0x00,0x80,0x84,0x3E,0x04,0x31,0x00,0x77,0x96,0x06,0xFE,0x5E,0xFE,0x0F, +0x14,0x01,0x00,0x77,0xDD,0x9E,0x40,0x21,0x40,0x08,0x40,0x10,0xA0,0x08,0x88,0x22, +0x88,0x20,0x46,0x00,0x00,0x80,0x14,0x10,0x00,0x78,0xDD,0x9E,0xFC,0x41,0xF5,0x81, +0x81,0x00,0x81,0x22,0x80,0xE3,0x80,0xC4,0x46,0x50,0x00,0x80,0xC0,0x03,0x5A,0x08, +0x01,0x18,0x83,0x85,0xBA,0x77,0x46,0x1F,0xEF,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x56, +0xB9,0xF7,0x84,0x23,0x49,0xFF,0xC4,0xAB,0x38,0x14,0x99,0x01,0x84,0x02,0x49,0xFF, +0xCB,0x07,0x38,0x03,0x98,0x00,0x84,0x22,0x49,0xFF,0xCC,0x05,0xFC,0xC1,0x81,0x41, +0x83,0x85,0xB9,0x77,0x46,0x00,0x10,0x00,0xFE,0x0F,0xB8,0xF7,0x84,0x23,0x84,0x01, +0x49,0xFF,0xC4,0x95,0x50,0x25,0x7F,0xFF,0x80,0x06,0xF1,0x01,0x96,0x90,0x49,0xFF, +0xFF,0xC4,0xFC,0xC1,0x46,0x20,0x00,0x80,0x96,0x06,0x04,0x31,0x00,0x75,0x40,0x10, +0x28,0x08,0x44,0x0F,0xFB,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x75,0xDD,0x9E, +0x46,0x20,0x00,0x91,0x96,0x06,0x04,0x31,0x00,0x14,0x94,0x41,0x84,0x1D,0xFE,0x1E, +0xFE,0x0F,0x14,0x01,0x00,0x14,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0x44,0x14, +0x00,0x48,0x84,0x1F,0xB6,0x01,0x44,0x14,0x00,0x4C,0xB6,0x01,0xDD,0x9E,0xC8,0x04, +0x44,0x07,0x20,0x14,0xD5,0x03,0x44,0x07,0x20,0x18,0xB4,0x00,0x40,0x00,0x30,0x08, +0x92,0x0C,0xDD,0x9E,0xFC,0x00,0x80,0xC1,0x49,0xFF,0xCD,0x34,0x84,0x40,0x50,0x00, +0x09,0x48,0x80,0x22,0xE2,0x46,0xE8,0x04,0xAA,0x41,0x8C,0x41,0xD5,0xFC,0xFC,0x80, +0xFC,0x00,0x44,0x04,0x00,0x9C,0xA6,0x00,0xE6,0x03,0xE9,0x24,0x44,0x64,0x0C,0x40, +0x84,0x41,0xA6,0x32,0x80,0x62,0x58,0x00,0x00,0x08,0xAE,0x32,0x44,0x14,0x04,0x40, +0x44,0x04,0x08,0x40,0x49,0xFF,0xC8,0x26,0xA6,0x32,0x84,0x41,0x54,0x00,0x00,0xF7, +0xAE,0x32,0x44,0x14,0x04,0x40,0x44,0x04,0x08,0x40,0x80,0x62,0x49,0xFF,0xC8,0x1A, +0x44,0x07,0x20,0xC0,0x46,0x10,0x20,0x00,0xB4,0x40,0xFE,0x57,0xB6,0x20,0x80,0x20, +0xD5,0x3C,0x49,0x00,0x0C,0x44,0xC8,0xDB,0x44,0x64,0x50,0x28,0xB4,0x06,0x92,0x10, +0x96,0x06,0x5A,0x00,0x01,0x05,0x44,0x64,0x50,0x28,0xD5,0x25,0x2E,0x07,0xF4,0xB4, +0xC8,0x0E,0xB4,0x06,0x92,0x03,0x96,0x06,0xC8,0x0A,0xB4,0x06,0x92,0x01,0x96,0x06, +0xC0,0x06,0x3C,0x0D,0xFF,0x15,0x5A,0x08,0x01,0xEB,0xD5,0xEE,0x49,0xFF,0xF4,0x6C, +0x5A,0x08,0x02,0xF9,0xD5,0xE9,0x2E,0x07,0xF4,0xB4,0xC8,0x12,0xB4,0x06,0x92,0x03, +0x96,0x06,0xC8,0x0E,0xB4,0x06,0x92,0x01,0x96,0x06,0xC0,0x0A,0x3C,0x0D,0xFF,0x15, +0x5A,0x00,0x01,0xAE,0xB4,0x06,0x92,0x10,0x96,0x06,0xC0,0xEE,0xD5,0xA8,0x49,0xFF, +0xF4,0x53,0x5A,0x08,0x02,0xF5,0xD5,0xA3,0xB4,0x01,0x92,0x19,0x96,0x06,0xC8,0xFD, +0x84,0x01,0x49,0xFF,0xFC,0xCC,0xFC,0x80,0x44,0x34,0x00,0x54,0x84,0x21,0xB4,0x43, +0x40,0x00,0x80,0x0C,0xFE,0x03,0xFE,0x16,0xB6,0x03,0xDD,0x9E,0x44,0x04,0x00,0x50, +0x44,0x1F,0x98,0x81,0xAC,0x40,0x84,0x21,0xAC,0x41,0xDD,0x9E,0xFC,0x00,0x80,0xC0, +0x44,0x04,0x80,0x90,0x54,0x13,0x00,0x01,0xB4,0x80,0x84,0x5D,0x94,0xC9,0xFE,0xA6, +0xFE,0x9F,0xB6,0x40,0x46,0x00,0x00,0x81,0x84,0x5E,0x83,0x80,0xBB,0x15,0xFE,0x9E, +0xFE,0x57,0xB9,0x95,0x84,0x00,0x3E,0x07,0xF4,0xB4,0x44,0x04,0x80,0x10,0x84,0x22, +0xAE,0x40,0x49,0xFF,0xD0,0x78,0xE6,0x03,0xE9,0x2A,0x44,0x14,0x00,0x18,0xA6,0x0A, +0x5A,0x68,0x01,0x13,0x54,0x00,0x00,0xFB,0xAE,0x0A,0x44,0x14,0x50,0x10,0xB4,0x01, +0x92,0x08,0x40,0x00,0x20,0x08,0xB6,0x01,0x44,0x14,0x70,0x00,0x44,0x0E,0xFF,0xFF, +0xB4,0x41,0xFE,0x16,0xD5,0x13,0x58,0x00,0x00,0x04,0xAE,0x0A,0x44,0x14,0x50,0x10, +0xB4,0x01,0x92,0x08,0x40,0x00,0x20,0x08,0x58,0x00,0x00,0x98,0xB6,0x01,0x44,0x14, +0x70,0x00,0x44,0x01,0x00,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0xFC,0x80,0x46,0x00, +0x00,0x91,0xA0,0x02,0x96,0x01,0xDD,0x9E,0xDD,0x9E,0x46,0x30,0x00,0x91,0x58,0x20, +0x00,0x01,0x14,0x21,0x80,0x11,0x44,0x20,0xFF,0xFE,0xFE,0x16,0x14,0x01,0x80,0x11, +0x46,0x00,0x20,0x00,0xFE,0x47,0x44,0x07,0x20,0xC0,0xB6,0x20,0xDD,0x9E,0x46,0x10, +0x00,0x81,0x44,0x02,0x00,0x00,0x04,0x20,0x80,0x6E,0xFE,0x17,0x14,0x00,0x80,0x6E, +0xDD,0x9E,0x44,0x27,0x30,0x10,0xB4,0x22,0x92,0x21,0x96,0x46,0x5A,0x10,0x01,0xFD, +0xFC,0x00,0x44,0x17,0x30,0x00,0xB6,0x01,0x84,0x02,0x49,0xFF,0xCE,0x9A,0xFC,0x80, +0xFC,0x00,0x46,0x61,0xFF,0xF9,0x50,0x03,0x05,0x27,0x49,0xFF,0xFF,0xEC,0x50,0x03, +0x04,0x87,0x49,0xFF,0xFF,0xE8,0xFC,0x80,0xFC,0x00,0x46,0x61,0xFF,0xF9,0x50,0x03, +0x05,0x00,0x49,0xFF,0xFF,0xE0,0x50,0x03,0x04,0x00,0x49,0xFF,0xFF,0xDC,0xFC,0x80, +0xFC,0x00,0x46,0x11,0xFF,0xFF,0x50,0x10,0x8F,0x00,0xFE,0x0F,0x49,0xFF,0xFF,0xD3, +0xFC,0x80,0xFC,0x00,0x46,0x11,0xFF,0xFF,0xFE,0x0F,0x49,0xFF,0xFF,0xCC,0xFC,0x80, +0xFC,0x00,0x46,0x01,0xFF,0xFF,0x50,0x00,0x02,0xBB,0x49,0xFF,0xFF,0xC4,0xFC,0x80, +0xFC,0x00,0x46,0x01,0xFF,0xFF,0x50,0x00,0x03,0xCC,0x49,0xFF,0xFF,0xBC,0xFC,0x80, +0xFC,0x00,0x44,0x67,0x30,0x04,0x49,0xFF,0xFF,0xC5,0x49,0xFF,0xCE,0x4D,0xB4,0x26, +0x84,0x1E,0xFE,0x0E,0xB6,0x06,0x44,0x00,0x03,0xE8,0x49,0xFF,0xCE,0x52,0xB4,0x06, +0x58,0x00,0x00,0x01,0xB6,0x06,0x44,0x00,0x03,0xE8,0x49,0xFF,0xCE,0x4A,0x49,0xFF, +0xFF,0xD9,0x49,0xFF,0xCE,0x39,0x46,0x01,0xFF,0xFF,0x49,0xFF,0xFF,0x9C,0x49,0xFF, +0xCE,0x33,0x49,0xFF,0xCF,0xC0,0xE6,0x03,0xE8,0x0B,0x49,0xFF,0xFF,0xD3,0x49,0xFF, +0xCE,0x2B,0x46,0x01,0xFF,0xFF,0x49,0xFF,0xFF,0x8E,0x49,0xFF,0xCE,0x25,0x44,0x67, +0x30,0x04,0x44,0x00,0x03,0xE8,0x49,0xFF,0xCE,0x2C,0xB4,0x26,0x84,0x1E,0xFE,0x0E, +0xB6,0x06,0x44,0x00,0x03,0xE8,0x49,0xFF,0xCE,0x24,0xB4,0x06,0x58,0x00,0x00,0x01, +0xB6,0x06,0x44,0x00,0x03,0xE8,0x49,0xFF,0xCE,0x1C,0x44,0x04,0x80,0x0A,0x84,0x24, +0xAE,0x40,0x84,0x22,0xAE,0x40,0x84,0x21,0xAE,0x40,0xFC,0x80,0xFC,0x41,0xF0,0x81, +0x80,0xE1,0x80,0xC2,0x81,0x43,0x81,0x24,0x49,0xFF,0xFF,0x74,0xF0,0x01,0x5A,0x00, +0xFF,0x06,0x49,0xFF,0xFF,0x87,0x49,0xFF,0xCD,0xF7,0x40,0xA5,0x20,0x08,0x40,0x95, +0x24,0x04,0x40,0x63,0x40,0x08,0x40,0x64,0x98,0x04,0x40,0x03,0xE0,0x08,0xFE,0x37, +0x49,0xFF,0xFF,0x51,0x49,0xFF,0xFF,0x6A,0xFC,0xC1,0x44,0x27,0x30,0x10,0xB4,0x22, +0x92,0x21,0x96,0x46,0x5A,0x10,0x01,0xFD,0xFC,0x20,0x44,0x14,0x80,0x0A,0x84,0x42, +0xAE,0x88,0x44,0x17,0x30,0x00,0x44,0x60,0x10,0x00,0xB6,0x01,0x44,0x74,0x80,0x08, +0xB4,0x07,0x92,0x11,0x96,0x06,0xC0,0x0D,0x44,0x04,0x80,0x08,0xB4,0x00,0x92,0x12, +0x96,0x06,0xC8,0x10,0x44,0x07,0x30,0x14,0xB4,0x00,0x92,0x10,0x96,0x00,0xFC,0xA0, +0x2E,0x07,0xF4,0xF0,0xC8,0xF2,0x84,0x01,0x8E,0xC1,0x49,0xFF,0xCD,0xCA,0xCE,0xE9, +0xD5,0xEC,0x44,0x00,0xFF,0xFF,0xFC,0xA0,0xFC,0x41,0xF0,0x81,0x40,0x21,0x20,0x08, +0x46,0x02,0xF0,0x00,0x40,0x91,0x00,0x04,0x40,0x10,0xC0,0x08,0x40,0x94,0x84,0x04, +0x84,0xC0,0x49,0xFF,0xFF,0x1F,0xF0,0x01,0x49,0xFF,0xFF,0x34,0x49,0xFF,0xCD,0xA4, +0x80,0x09,0x49,0xFF,0xFF,0xBC,0x5C,0xF0,0x01,0x00,0x80,0xE0,0xE9,0x08,0x8C,0xC1, +0x84,0x01,0x97,0xB0,0x49,0xFF,0xCF,0x34,0x5A,0x68,0x04,0xED,0x49,0xFF,0xFF,0x16, +0x5A,0x60,0x04,0x04,0x80,0x07,0xFC,0xC1,0x84,0x00,0xFC,0xC1,0xFC,0x00,0x44,0x17, +0x30,0x08,0xB4,0x01,0x92,0x05,0x94,0x05,0x58,0x00,0x00,0x01,0xB6,0x01,0x49,0xFF, +0xFE,0xF9,0x49,0xFF,0xCF,0x10,0xE6,0x03,0xE8,0x07,0x49,0xFF,0xFF,0x23,0x46,0x01, +0xFF,0xFF,0x49,0xFF,0xFE,0xE0,0x84,0x06,0xFA,0x2F,0x44,0x20,0x00,0xAC,0x84,0x61, +0x84,0x80,0x49,0xFF,0xFF,0x6D,0x84,0x06,0xFA,0x2F,0x44,0x20,0x00,0xAD,0x84,0x61, +0x84,0x80,0x49,0xFF,0xFF,0x65,0x44,0x04,0x80,0x0A,0x84,0x24,0xAE,0x40,0x84,0x22, +0xAE,0x40,0x44,0x04,0x80,0x88,0x44,0x14,0x00,0x00,0xB4,0x40,0xFE,0x57,0xB6,0x20, +0xB4,0x40,0x44,0x12,0x00,0x00,0xFE,0x57,0xB6,0x20,0x49,0xFF,0xFE,0xD7,0xFC,0x80, +0xFC,0x20,0x84,0x2C,0x84,0x41,0x84,0x05,0x49,0xFF,0xFF,0x98,0x97,0xC0,0x84,0x2D, +0x84,0x41,0x84,0x05,0x49,0xFF,0xFF,0x92,0x97,0x80,0x80,0x87,0x84,0x05,0xFA,0x2F, +0x44,0x20,0x00,0xC0,0x84,0x61,0x49,0xFF,0xFF,0x3B,0x80,0x86,0x84,0x05,0xFA,0x2F, +0x44,0x20,0x00,0xC1,0x84,0x61,0x49,0xFF,0xFF,0x33,0x80,0x87,0x84,0x05,0xFA,0x2F, +0x44,0x20,0x00,0xC2,0x84,0x61,0x49,0xFF,0xFF,0x2B,0x80,0x86,0x84,0x05,0xFA,0x2F, +0x44,0x20,0x00,0xC3,0x84,0x61,0x49,0xFF,0xFF,0x23,0x80,0x87,0x84,0x05,0xFA,0x2F, +0x44,0x20,0x00,0xC4,0x84,0x61,0x49,0xFF,0xFF,0x1B,0x80,0x86,0x84,0x05,0xFA,0x2F, +0x44,0x20,0x00,0xC5,0x84,0x61,0x49,0xFF,0xFF,0x13,0x80,0x87,0x84,0x05,0xFA,0x2F, +0x44,0x20,0x00,0xC6,0x84,0x61,0x49,0xFF,0xFF,0x0B,0x84,0x05,0xFA,0x2F,0x44,0x20, +0x00,0xC7,0x84,0x61,0x80,0x86,0x49,0xFF,0xFF,0x03,0xFC,0xA0,0x44,0x04,0x80,0x0A, +0x84,0x24,0xAE,0x40,0x44,0x17,0x21,0x04,0x46,0x38,0x00,0x00,0xB4,0x01,0x92,0x14, +0x40,0x00,0x50,0x08,0xB6,0x01,0x44,0x07,0x21,0x08,0xB4,0x20,0x92,0x28,0x40,0x10, +0xA0,0x08,0xB6,0x20,0xB4,0x40,0x46,0x1F,0xCF,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x8E, +0xB6,0x40,0xB4,0x40,0xFE,0x9F,0xB6,0x40,0x44,0x27,0x21,0x10,0xB4,0x02,0x94,0x01, +0x92,0x01,0xB6,0x02,0x44,0x27,0x21,0x14,0xB4,0x02,0x92,0x14,0x40,0x00,0x50,0x08, +0xB6,0x02,0x44,0x07,0x21,0x18,0xB4,0x40,0x92,0x48,0x40,0x21,0x20,0x08,0xB6,0x40, +0xB4,0x40,0xFE,0x56,0xB6,0x20,0xB4,0x20,0xFE,0x5F,0xB6,0x20,0x44,0x17,0x21,0x1C, +0xB4,0x01,0x92,0x11,0x40,0x00,0x44,0x08,0xB6,0x01,0xB4,0x41,0x46,0x0F,0x0F,0xFF, +0x50,0x00,0x0F,0xFF,0xFE,0x16,0xB6,0x01,0x44,0x17,0x21,0x00,0x46,0x0F,0xFF,0x0F, +0xB4,0x41,0x50,0x00,0x0F,0xFF,0xFE,0x16,0xB6,0x01,0x44,0x04,0x80,0x07,0x84,0x22, +0xAE,0x40,0x44,0x04,0x10,0x00,0x44,0x24,0x10,0x10,0xAE,0x42,0x84,0x21,0xAE,0x40, +0x84,0x20,0xAE,0x53,0xA6,0x43,0x54,0x10,0x80,0xFE,0xAE,0x43,0x44,0x04,0x10,0x0C, +0x84,0x20,0xB6,0x20,0x84,0x1F,0xAE,0x10,0xDD,0x9E,0x3C,0x0F,0xFD,0x3D,0xDD,0x9E, +0xC8,0x04,0x44,0x05,0x00,0x04,0xD5,0x03,0x44,0x05,0x00,0x14,0xA4,0x00,0x96,0x01, +0xDD,0x9E,0x44,0x04,0x80,0x00,0xA6,0x03,0x96,0x06,0xC0,0x0D,0x44,0x04,0x80,0x03, +0x84,0x21,0xAE,0x40,0x3C,0x0D,0xFD,0x3D,0xC0,0x06,0xFC,0x00,0x3C,0x0D,0xFD,0x3D, +0xDD,0x20,0xFC,0x80,0xDD,0x9E,0x46,0x20,0x00,0x81,0x84,0x3E,0x04,0x31,0x00,0x14, +0x96,0x06,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x14,0xDD,0x9E,0x44,0x14,0x70,0x08, +0x5A,0x08,0x01,0x09,0xB4,0x01,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00,0x78,0x23, +0xD5,0x05,0xB4,0x01,0x92,0x10,0x40,0x00,0x40,0x08,0xB6,0x01,0xDD,0x9E,0x44,0x14, +0x00,0x0C,0xAE,0x09,0xDD,0x9E,0x44,0x05,0x10,0x00,0xB4,0x00,0x92,0x08,0x96,0x17, +0xDD,0x9E,0x44,0x25,0x10,0x00,0x96,0x17,0xB4,0x62,0x40,0x10,0x20,0x08,0x44,0x0F, +0xF8,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x24,0x80,0x84,0x96,0x06, +0xB4,0x62,0x40,0x10,0x20,0x08,0x44,0x0F,0xFE,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02, +0xDD,0x9E,0x44,0x25,0x10,0x00,0x84,0x3E,0xB4,0x62,0x96,0x06,0xFE,0x5E,0xFE,0x0F, +0xB6,0x02,0xDD,0x9E,0x84,0x00,0x3E,0x07,0xF4,0xFA,0xDD,0x9E,0xFC,0x00,0x49,0x00, +0x09,0x1E,0x5A,0x08,0x01,0x04,0x84,0x02,0xD5,0x02,0x84,0x04,0x3E,0x07,0xE2,0xD7, +0x2E,0x07,0xE2,0xD7,0x49,0xFF,0xCB,0x34,0x84,0x04,0xFC,0x80,0xFC,0x00,0x3C,0x0D, +0xFB,0xD6,0x54,0x00,0x10,0x00,0xC0,0x08,0x2E,0x07,0xEF,0xA8,0x5A,0x00,0x01,0x1D, +0x2E,0x07,0xE2,0xD7,0xD5,0x0D,0x3C,0xF7,0xF7,0x92,0x2E,0x07,0xE2,0xD7,0x5E,0xF7, +0x80,0x64,0xE8,0x0C,0x3C,0xF7,0xF7,0x8E,0x5E,0xF7,0xFF,0x9D,0xE9,0x07,0xE4,0x03, +0xE8,0x03,0x8C,0x01,0xD5,0x06,0x84,0x04,0xD5,0x07,0xE4,0x03,0xE9,0x04,0x8E,0x01, +0x96,0x00,0xD5,0x02,0x84,0x02,0x3E,0x07,0xE2,0xD7,0x2E,0x07,0xE2,0xD7,0x49,0xFF, +0xCB,0x07,0x2E,0x07,0xE2,0xD7,0x5A,0x08,0x04,0x04,0x84,0x02,0xFC,0x80,0x84,0x04, +0xFC,0x80,0x2E,0x17,0xF4,0xFA,0x8C,0x21,0x96,0x48,0xE2,0x20,0x3E,0x17,0xF4,0xFA, +0x56,0x07,0x80,0x01,0xDD,0x9E,0xFC,0x42,0x81,0x40,0xC1,0x05,0x84,0xE8,0x5A,0x18, +0x01,0x07,0xD5,0x03,0x44,0x70,0x00,0x64,0x84,0xC1,0xD5,0x03,0x84,0xE1,0x84,0xC0, +0x49,0x00,0x1C,0x73,0x81,0x20,0x4E,0xA3,0x00,0x11,0x49,0xFF,0xC9,0x7B,0xF0,0x81, +0x49,0x00,0x1B,0x22,0xF0,0x82,0xF1,0x01,0x44,0x00,0x02,0x40,0xF2,0x02,0x80,0x66, +0x80,0x87,0x49,0x00,0x03,0x6F,0xFC,0xC2,0x5A,0xA8,0x01,0x1E,0x49,0x00,0x1B,0x17, +0xF0,0x82,0x4E,0x93,0x00,0x05,0x49,0x00,0x1C,0xD4,0xD5,0x03,0x49,0x00,0x1C,0xCE, +0xF0,0x81,0x80,0x09,0x49,0xFF,0xFC,0x1D,0xF0,0x83,0x44,0x10,0x00,0x42,0xF0,0x02, +0xF2,0x01,0xF3,0x03,0xFD,0x23,0x49,0x00,0x03,0x68,0xF0,0x01,0x44,0x10,0x00,0x42, +0x49,0xFF,0xC5,0x3D,0xFC,0xC2,0xFC,0x20,0x51,0xFF,0xFB,0x80,0x44,0x10,0x00,0xC0, +0x84,0x40,0x80,0xE0,0x50,0x0F,0x81,0x80,0x49,0x00,0x02,0xC8,0x80,0x1F,0x44,0x10, +0x00,0xC0,0x49,0x00,0x02,0xD2,0x84,0xC0,0x49,0xFF,0xCD,0x25,0xE6,0x03,0x50,0x0F, +0x81,0x80,0xE8,0x10,0x38,0x00,0x1A,0x02,0x90,0x08,0x96,0x43,0x38,0x0F,0x99,0x11, +0x50,0x00,0x7F,0xC0,0x94,0x05,0x9A,0x08,0x40,0x00,0x20,0x08,0x38,0x03,0x9A,0x0A, +0xD5,0x0C,0x38,0x10,0x1A,0x02,0x38,0x0F,0x99,0x11,0x90,0x28,0x96,0x4B,0x8A,0x20, +0x40,0x10,0xA0,0x08,0x38,0x13,0x9A,0x0A,0x8C,0xC1,0x5A,0x68,0xC0,0xDF,0x80,0x07, +0x80,0x26,0x84,0x40,0x44,0x30,0x02,0x52,0x49,0x00,0x03,0x00,0x51,0xFF,0x84,0x80, +0xFC,0xA0,0xFC,0x21,0x80,0xC0,0x2E,0x0F,0xF4,0xF9,0xC8,0x19,0x2E,0x1F,0xF4,0xF8, +0xC9,0x16,0xCE,0x42,0x49,0xFF,0xF9,0x16,0x49,0xFF,0xFB,0xC3,0x80,0xE0,0x49,0x00, +0x1B,0xF4,0x49,0xFF,0xC8,0xFF,0x80,0x26,0x38,0x23,0x85,0x11,0x40,0x21,0x20,0x08, +0x38,0x20,0x06,0x0A,0x8C,0x21,0x5A,0x1A,0x40,0xF9,0xD5,0x2E,0x2E,0x17,0xEF,0x18, +0xE0,0x20,0xE9,0x2A,0x2E,0xFF,0xF4,0xF8,0x2E,0x17,0xEF,0x17,0xE0,0x2F,0xE9,0x24, +0xCE,0x23,0x49,0x00,0x1A,0x8E,0xF0,0x81,0x49,0xFF,0xCC,0xCD,0xE6,0x03,0xE9,0x04, +0xF0,0x01,0x49,0xFF,0xCE,0x66,0x3C,0x14,0x05,0xAF,0xF5,0x01,0x84,0x03,0xFE,0x44, +0x50,0x22,0x84,0x80,0xD5,0x33,0x8C,0x01,0x5A,0x00,0x12,0x2E,0x38,0xF2,0x81,0x11, +0xE0,0x2F,0xE8,0xFA,0x84,0x00,0x3E,0x07,0xF4,0xF8,0x49,0x00,0x1A,0x61,0x84,0x00, +0x84,0x21,0x49,0xFF,0xFF,0x3A,0x2E,0x1F,0xF4,0xF9,0x2E,0x07,0xEF,0x18,0xE0,0x01, +0xE9,0x07,0x2E,0x0F,0xF4,0xF8,0x2E,0x27,0xEF,0x17,0xE0,0x40,0xE8,0x0A,0x5A,0x68, +0x01,0x11,0x84,0x00,0x3E,0x07,0xF4,0xF9,0x3E,0x07,0xF4,0xF8,0x80,0x06,0xD5,0x10, +0x5A,0x68,0x01,0x08,0x8C,0x21,0x8C,0x01,0x3E,0x17,0xF4,0xF9,0x3E,0x07,0xF4,0xF8, +0x84,0x00,0xD5,0x06,0x50,0x52,0x80,0x24,0xD2,0xD9,0x84,0x00,0xD5,0xD0,0xFC,0xA1, +0x44,0x04,0x00,0x54,0x46,0x1C,0x9A,0xE7,0xB4,0xA0,0x50,0x10,0x83,0xDB,0xD1,0x27, +0x46,0x18,0x88,0x88,0x50,0x10,0x88,0x88,0xD9,0x08,0x44,0x15,0x10,0x10,0xB4,0x21, +0x40,0x10,0xA0,0x08,0xC1,0x1C,0xD5,0x06,0x46,0x1F,0x38,0xA9,0x50,0x10,0x84,0xEF, +0xD9,0x0D,0x46,0x14,0xE1,0xD5,0x50,0x10,0x8C,0x63,0xB6,0x20,0x2E,0x07,0xF4,0xFB, +0x58,0x00,0x00,0x01,0x3E,0x07,0xF4,0xFB,0xDD,0x9E,0x46,0x1A,0x67,0xC9,0x50,0x10, +0x8D,0xFE,0xD9,0x05,0x46,0x1B,0xEE,0xFF,0x8C,0x2D,0xB6,0x20,0xDD,0x9E,0x4E,0x05, +0x00,0x19,0x8E,0x01,0x40,0x10,0x04,0x0A,0xFE,0x0F,0x40,0x10,0x08,0x0A,0xFE,0x0F, +0x40,0x10,0x10,0x0A,0xFE,0x0F,0x40,0x10,0x20,0x0A,0xFE,0x0F,0x40,0x10,0x40,0x0A, +0xFE,0x47,0x8C,0x21,0x84,0x00,0x90,0x21,0xC1,0x03,0x8C,0x01,0xD5,0xFD,0xDD,0x9E, +0x84,0x00,0xDD,0x9E,0xFC,0x4D,0x44,0x02,0x00,0xD4,0x50,0xFF,0x80,0x18,0x80,0xC0, +0x80,0x2F,0x3A,0x23,0x14,0x04,0x3A,0x20,0x94,0x24,0x81,0x46,0x83,0x81,0x3A,0x25, +0x14,0x04,0x44,0x92,0x00,0xFC,0x3A,0x2E,0x14,0x24,0x80,0x89,0x3A,0x25,0x0C,0x00, +0x80,0xBF,0x3A,0x2E,0x0C,0x20,0x3A,0x22,0x0C,0x04,0x3A,0x22,0x8C,0x24,0x45,0xE2, +0x16,0xF8,0xA5,0xE0,0x3A,0x27,0x94,0x04,0x81,0xFE,0x81,0x1F,0x3A,0x27,0x94,0x24, +0x3A,0x20,0x94,0x04,0x80,0x2F,0x3A,0x20,0x94,0x24,0x3A,0x2E,0x0C,0x00,0x3A,0x20, +0x8C,0x20,0x44,0x12,0x16,0xEC,0x3A,0x24,0x0C,0x04,0x3A,0x20,0x8C,0x24,0xAD,0xC8, +0xB0,0x50,0x3A,0x20,0x14,0x04,0x80,0x01,0x3A,0x20,0x14,0x24,0x81,0x00,0x3A,0x23, +0x14,0x04,0x3A,0x24,0x14,0x24,0xB1,0x83,0x3A,0x25,0x0C,0x00,0x80,0x86,0x3A,0x24, +0x0C,0x20,0x3A,0x24,0x8C,0x04,0x44,0x92,0x04,0xD4,0x3A,0x22,0x0C,0x24,0x3A,0x20, +0x94,0x04,0x80,0x29,0x3A,0x20,0x94,0x24,0x3A,0x20,0x14,0x04,0x80,0x01,0x3A,0x20, +0x14,0x24,0x3A,0x24,0x0C,0x00,0x3A,0x20,0x0C,0x20,0x44,0x02,0x04,0xC8,0x3A,0x23, +0x0C,0x04,0x3A,0x20,0x0C,0x24,0xAD,0xC0,0xFC,0xCD,0xFC,0x48,0x83,0x81,0x50,0xAF, +0x80,0x14,0x44,0x12,0x01,0x08,0x80,0xC0,0x80,0x0A,0x81,0x22,0x3A,0x20,0x8C,0x04, +0x3A,0x20,0x0C,0x24,0xA4,0x48,0xFA,0x40,0xAC,0x40,0x50,0x8F,0x80,0x30,0x84,0x20, +0xB0,0x08,0x49,0x00,0x27,0xC1,0x80,0x08,0x84,0x20,0xFA,0x40,0x49,0x00,0x27,0xBC, +0x5A,0x98,0x01,0x03,0x87,0x80,0xC6,0x04,0x44,0x12,0x17,0x9C,0xD5,0x03,0x44,0x12, +0x17,0xA8,0x58,0x04,0x80,0x01,0x8C,0x01,0x84,0xE0,0xB6,0x1F,0x8D,0x21,0x84,0x83, +0x84,0x61,0x38,0x05,0x1C,0x00,0xF3,0x83,0xF4,0x82,0x38,0x60,0x80,0x00,0xB4,0x1F, +0x8C,0xC1,0xFF,0x84,0x97,0xB1,0x40,0x63,0x24,0xD6,0x80,0x06,0xF1,0x81,0x49,0xFF, +0xFF,0x48,0xF4,0x02,0x97,0x78,0x40,0x52,0x90,0xB7,0xF3,0x03,0x38,0x24,0x16,0x02, +0x8C,0x08,0x41,0xE1,0x28,0x08,0x40,0x20,0x70,0x01,0x40,0x01,0x80,0x0C,0x40,0x60, +0x18,0xD7,0xB0,0x08,0x94,0x94,0x38,0x00,0x16,0x02,0x88,0x5E,0x40,0x00,0x28,0x08, +0x88,0xC0,0xB0,0x08,0x8C,0xE1,0x38,0x24,0x16,0x0A,0x38,0x60,0x16,0x0A,0xF1,0x01, +0x5A,0x78,0x0A,0xD1,0x80,0x28,0x49,0xFF,0xC0,0x52,0xFC,0xC8,0x80,0x80,0x84,0x40, +0x9A,0xE0,0x96,0xD8,0xE2,0x61,0xE8,0x08,0x08,0x32,0x00,0x01,0xE2,0x62,0x40,0x31, +0x3C,0x1B,0x80,0x43,0xD5,0xF6,0x9C,0x11,0x96,0x00,0xDD,0x9E,0xFC,0x40,0x51,0xFF, +0xFB,0xE0,0x44,0x20,0x04,0x00,0x81,0x40,0x81,0x21,0xB0,0x08,0x84,0x20,0x49,0x00, +0x27,0x5B,0xB1,0x81,0x44,0x12,0x01,0x08,0x80,0x06,0x3A,0x20,0x8C,0x04,0x3A,0x20, +0x0C,0x24,0xA4,0x48,0xB1,0xC4,0xAC,0x40,0xFA,0x40,0x84,0x20,0x80,0x07,0x49,0x00, +0x27,0x4B,0x84,0x2A,0x4E,0xA3,0x00,0x3D,0x44,0x02,0x17,0xA8,0x49,0xFF,0xFF,0xD0, +0x80,0x20,0x80,0x4A,0x84,0xA3,0x44,0xA2,0x16,0xF8,0x45,0xE2,0x16,0xEC,0x96,0x10, +0x40,0x00,0x14,0x17,0x40,0x30,0x20,0x08,0xB1,0x08,0x41,0xC2,0x0C,0x00,0x84,0x60, +0x54,0x81,0x80,0xFF,0xE3,0x01,0xE8,0x12,0x38,0x83,0x08,0x00,0x38,0x85,0x22,0x02, +0x38,0x84,0x0D,0x01,0x54,0xF4,0x03,0xFF,0x38,0x8E,0x0E,0x02,0x40,0x84,0x28,0x08, +0x89,0x0F,0x38,0x8E,0x0E,0x0A,0x8C,0x61,0xD5,0xEC,0x38,0x33,0x08,0x00,0x8C,0x41, +0x38,0x3F,0x0C,0x00,0x8A,0x69,0x54,0x81,0x83,0xFF,0x38,0x33,0x82,0x02,0x40,0x31, +0xA8,0x08,0x88,0x68,0x38,0x33,0x82,0x0A,0x5A,0x28,0x0A,0xD3,0xD5,0x3B,0x44,0x02, +0x17,0x9C,0x49,0xFF,0xFF,0x95,0x80,0x20,0x84,0x40,0x84,0xA3,0x44,0xA2,0x04,0xD4, +0x45,0xE2,0x04,0xC8,0x97,0x10,0x40,0x42,0x14,0x97,0x40,0x32,0x20,0x08,0xB0,0x08, +0x41,0xC0,0x0C,0x00,0x84,0x60,0x54,0x81,0x80,0xFF,0xE3,0x01,0xE8,0x12,0x38,0x83, +0x08,0x00,0x38,0x85,0x22,0x02,0x38,0x84,0x0D,0x01,0x54,0xF4,0x03,0xFF,0x38,0x8E, +0x0E,0x02,0x40,0x84,0x28,0x08,0x89,0x0F,0x38,0x8E,0x0E,0x0A,0x8C,0x61,0xD5,0xEC, +0x38,0x33,0x08,0x00,0x8C,0x41,0x38,0x3F,0x0C,0x00,0x8A,0x69,0x54,0x81,0x83,0xFF, +0x38,0x33,0x92,0x02,0x40,0x31,0xA8,0x08,0x88,0x68,0x38,0x33,0x92,0x0A,0x5A,0x28, +0x0A,0xD3,0xB0,0x08,0x49,0xFF,0xBF,0xFA,0x80,0x07,0x49,0xFF,0xC0,0x30,0x51,0xFF, +0x84,0x20,0xFC,0xC0,0xFC,0x00,0x49,0x00,0x18,0x8C,0x50,0x30,0x04,0x80,0x50,0x50, +0x00,0x22,0x50,0x20,0x00,0x10,0x80,0x20,0x22,0x40,0x80,0x00,0x22,0x62,0x80,0x00, +0x1A,0x60,0x80,0x01,0x1A,0x42,0xFF,0xFF,0xDA,0xF8,0x50,0x00,0x00,0x24,0x4C,0x01, +0xFF,0xF0,0xFC,0x80,0xFC,0x00,0x49,0x00,0x18,0x74,0x50,0x20,0x04,0x5C,0x50,0x40, +0x02,0x40,0x50,0x30,0x00,0x24,0x80,0x22,0x22,0x60,0x80,0x00,0x22,0x50,0x00,0x00, +0x1A,0x60,0x00,0x01,0x1A,0x50,0x80,0x01,0x4C,0x01,0xFF,0xF8,0x50,0x21,0x7F,0xDC, +0x4C,0x02,0x7F,0xF1,0xFC,0x80,0xFC,0x41,0x80,0xE0,0x80,0x02,0xF3,0x81,0x81,0x44, +0x80,0xC5,0x81,0x21,0x49,0xFF,0xF9,0x6D,0x81,0x00,0xF0,0x01,0x49,0xFF,0xC6,0xAA, +0x40,0x44,0x28,0x00,0x95,0x72,0x84,0x60,0x84,0x20,0x98,0x99,0x96,0x91,0xC7,0x0B, +0x95,0x92,0x88,0xC5,0x38,0xA2,0x09,0x01,0x38,0x60,0x18,0x02,0x90,0xC8,0x8A,0xCA, +0x38,0x63,0x89,0x09,0x4E,0x92,0x00,0x06,0x38,0x62,0x09,0x11,0x38,0x64,0x89,0x09, +0x8C,0x21,0x5A,0x18,0x12,0xEC,0x8C,0x72,0x96,0xD9,0x5A,0x3A,0x40,0xE7,0x49,0xFF, +0xFF,0xA3,0x49,0xFF,0xFF,0xB9,0xFC,0xC1,0xFC,0x00,0x80,0x43,0x80,0x64,0x84,0x80, +0x80,0xA4,0x49,0xFF,0xFF,0xCA,0xFC,0x80,0xFC,0x20,0xFD,0x30,0x80,0x02,0x49,0xFF, +0xC6,0x79,0x95,0xFA,0x80,0xA6,0x88,0xE6,0x50,0x00,0x09,0x48,0xD7,0x04,0xA2,0x41, +0xAA,0x69,0xD5,0xFD,0xFC,0xA0,0xFC,0x20,0xFD,0x30,0x84,0x00,0x49,0xFF,0xF9,0x29, +0x95,0xF9,0x80,0xA6,0x88,0xE6,0x50,0x00,0x04,0xA4,0xD7,0x06,0x2A,0x10,0x00,0x01, +0x1A,0x12,0x80,0x01,0xD5,0xFB,0xFC,0xA0,0xFC,0x20,0x80,0xE0,0x80,0x02,0x80,0xC1, +0x49,0xFF,0xC6,0x58,0x84,0x40,0x96,0x51,0xE2,0x26,0xE8,0x07,0x38,0x10,0x0A,0x02, +0x38,0x13,0x8A,0x0A,0x8C,0x41,0xD5,0xF8,0xFC,0xA0,0xFC,0x40,0x80,0xC0,0x80,0x02, +0x81,0x23,0x80,0xE1,0x49,0xFF,0xF9,0x05,0x81,0x40,0x84,0x6C,0x5A,0x98,0x02,0x07, +0x49,0x00,0x19,0x30,0x84,0x63,0xC0,0x02,0x80,0x69,0x80,0x0A,0x40,0x91,0x84,0x08, +0x84,0x20,0x40,0xA1,0x80,0x13,0x96,0x89,0xE2,0x47,0xE8,0x1E,0x84,0xA0,0x80,0x85, +0x40,0x82,0x80,0x13,0xE3,0x0A,0xE8,0x06,0x38,0x80,0x15,0x11,0x8C,0xA1,0x88,0x88, +0xD5,0xF8,0x5C,0xF1,0x00,0x40,0x40,0x42,0x0C,0x96,0xE8,0x09,0x54,0x51,0x00,0x01, +0x95,0x6D,0x92,0x41,0x88,0x45,0x38,0x43,0x09,0x09,0xD5,0x03,0x38,0x43,0x05,0x09, +0x8C,0x21,0x88,0x09,0xD5,0xE1,0xFC,0xC0,0xFC,0x40,0x81,0x20,0x95,0x9A,0x80,0x02, +0x80,0xE1,0x97,0xB1,0x49,0xFF,0xC6,0x0E,0x88,0xC0,0x84,0x40,0x96,0x51,0xE2,0x27, +0xE8,0x07,0x38,0x04,0x8A,0x02,0x38,0x03,0x0A,0x0A,0x8C,0x41,0xD5,0xF8,0xFC,0xC0, +0xFC,0x00,0x94,0x02,0x88,0x01,0x4C,0x10,0x00,0x0F,0xB4,0xC1,0x2A,0x51,0x00,0x01, +0x40,0x52,0xA0,0x08,0x8A,0xA6,0xFF,0xA4,0x42,0x61,0x94,0x73,0x40,0x63,0x10,0xD6, +0xAB,0x89,0xD5,0xF2,0xFC,0x80,0xFC,0x40,0x84,0xE0,0x97,0xB9,0xE2,0xC1,0xE8,0x12, +0x38,0x91,0x9E,0x02,0xB4,0xC0,0x40,0x94,0xA0,0x08,0x8B,0x26,0xFF,0xAC,0x42,0x62, +0x24,0x73,0x40,0x63,0x14,0xD6,0xAB,0x81,0x90,0xC8,0x38,0x61,0x1D,0x09,0x8C,0xE1, +0xD5,0xED,0xFC,0xC0,0x2E,0x07,0xF6,0x23,0xDD,0x9E,0xFC,0x01,0x84,0x1F,0xB6,0x1F, +0xF0,0x81,0x80,0x1F,0x49,0xFF,0xC1,0xB2,0x84,0x01,0x49,0xFF,0xC1,0xA4,0x84,0x01, +0x49,0xFF,0xC2,0x1A,0x84,0x22,0x84,0x01,0x80,0x61,0x80,0xA0,0x44,0x40,0x00,0x3F, +0x84,0x44,0x49,0xFF,0xC1,0x1D,0x84,0x01,0x49,0xFF,0xF7,0xE7,0x84,0x01,0x80,0x20, +0x80,0x40,0x49,0xFF,0xC1,0x5A,0x84,0x0A,0x49,0xFF,0xC1,0x82,0x46,0x00,0x42,0x10, +0x50,0x00,0x08,0x20,0x80,0x20,0x84,0x40,0x49,0xFF,0xC1,0x6D,0x49,0xFF,0xC3,0xD0, +0x84,0x01,0x49,0xFF,0xC3,0xBF,0x84,0x00,0x49,0xFF,0xF8,0x3E,0x44,0x04,0x55,0x08, +0x44,0x1F,0xFF,0x7F,0xB4,0x40,0xFE,0x56,0xB6,0x20,0xB4,0x20,0x58,0x10,0x80,0x10, +0xB6,0x20,0x84,0x21,0x49,0xFF,0xC0,0x7B,0x84,0x06,0x3E,0x07,0xF6,0x23,0xFC,0x81, +0xFC,0x54,0x44,0x02,0x01,0x20,0x50,0xAF,0x80,0x10,0x80,0x20,0x80,0x8A,0x3A,0x20, +0x8C,0x04,0x3A,0x22,0x0C,0x24,0x50,0x9F,0x80,0x1C,0xA4,0x48,0x3A,0x20,0x0C,0x04, +0x80,0x09,0xF1,0x81,0x3A,0x20,0x0C,0x24,0x44,0x12,0x01,0x2C,0xB0,0x0A,0x80,0xC0, +0x3A,0x20,0x94,0x04,0x3A,0x23,0x14,0x24,0xB4,0x21,0xB1,0x94,0xF1,0x83,0x44,0x12, +0x01,0x40,0x81,0xE1,0xF1,0x82,0x3A,0x27,0x94,0x04,0x3A,0x23,0x14,0x24,0x81,0x0F, +0x83,0xC6,0x3A,0x24,0x14,0x04,0x3A,0x2F,0x14,0x24,0x83,0x86,0xB1,0xCF,0x44,0x62, +0x01,0x68,0x3A,0x24,0x0C,0x00,0x80,0x27,0x3A,0x2F,0x0C,0x20,0x3A,0x23,0x14,0x04, +0x3A,0x20,0x94,0x24,0xB4,0x46,0x50,0x14,0x7F,0xE0,0xB1,0x9E,0xF2,0x82,0x3A,0x20, +0x94,0x04,0x80,0x26,0x3A,0x20,0x94,0x24,0x3A,0x27,0x94,0x04,0x81,0xE1,0x3A,0x27, +0x94,0x24,0x3A,0x24,0x0C,0x00,0x3A,0x27,0x8C,0x20,0x44,0x82,0x17,0x4C,0x3A,0x23, +0x94,0x04,0x3A,0x24,0x14,0x24,0xF3,0x02,0x44,0x72,0x17,0x24,0xB6,0x68,0x3A,0x23, +0x14,0x04,0x80,0xC7,0x3A,0x23,0x14,0x24,0x3A,0x20,0x94,0x04,0x80,0x26,0x44,0x62, +0x17,0x60,0x3A,0x20,0x94,0x24,0x3A,0x27,0x8C,0x00,0x3A,0x20,0x8C,0x20,0x44,0x12, +0x17,0xA8,0x3A,0x25,0x0C,0x04,0x3A,0x20,0x8C,0x24,0x80,0x41,0x02,0x1F,0x80,0x02, +0xAC,0x50,0x44,0x12,0x17,0x9C,0x3A,0x24,0x8C,0x04,0x3A,0x20,0x8C,0x24,0x80,0x41, +0x02,0x1F,0x80,0x02,0xAC,0x50,0x44,0x12,0x17,0x88,0x3A,0x20,0x14,0x04,0x80,0x01, +0xF1,0x03,0x3A,0x20,0x14,0x24,0xB6,0x20,0xB0,0x14,0x80,0x26,0x3A,0x20,0x14,0x04, +0x3A,0x20,0x94,0x24,0x80,0x01,0x3A,0x2E,0x14,0x04,0x3A,0x20,0x14,0x24,0x3A,0x2F, +0x0C,0x00,0x3A,0x20,0x0C,0x20,0xFC,0xD4,0xFC,0x00,0x49,0xFF,0xC0,0x40,0xFC,0x80, +0xFC,0x00,0xC8,0x06,0x44,0x02,0x17,0x60,0x44,0x12,0x17,0x88,0xD5,0x05,0x44,0x02, +0x17,0x24,0x44,0x12,0x17,0x4C,0x49,0xFF,0xC1,0x2C,0xFC,0x80,0xFC,0x00,0x49,0x00, +0x16,0x9C,0x80,0xC0,0x49,0x00,0x07,0xB8,0xC0,0x74,0x2E,0x07,0xF6,0x23,0xAC,0x30, +0x49,0x00,0x17,0xD0,0x2E,0x17,0xF6,0x23,0xC8,0x04,0x44,0x02,0x17,0x60,0xD5,0x03, +0x44,0x02,0x17,0x24,0x38,0x00,0x05,0x11,0x46,0x10,0x00,0x81,0xAC,0x31,0x3C,0x03, +0xFA,0x99,0x44,0x32,0x17,0xB8,0xAC,0x32,0x3C,0x03,0xFA,0xE3,0xAC,0x33,0x2E,0x07, +0xF6,0x1B,0x50,0x00,0x00,0x64,0xAC,0x34,0x2E,0x07,0xF6,0x1A,0x50,0x00,0x00,0xC8, +0xAC,0x35,0x2E,0x07,0xF6,0x19,0x50,0x00,0x01,0x2C,0xAC,0x36,0x3C,0x0D,0xF8,0xEB, +0xAC,0x37,0x83,0x81,0xB8,0x20,0x54,0x00,0x07,0xFF,0x12,0x03,0x00,0x08,0xB8,0x23, +0x50,0x13,0x00,0x16,0x96,0x1F,0x12,0x03,0x00,0x09,0x44,0x0F,0xFF,0x9C,0x12,0x03, +0x00,0x0A,0x84,0x00,0x38,0x21,0x82,0x02,0x8C,0x01,0x1A,0x20,0x80,0x01,0x5A,0x08, +0x14,0xFB,0x3C,0x03,0xF7,0xBC,0x12,0x03,0x00,0x0B,0x3C,0x03,0xF7,0xBD,0x12,0x03, +0x00,0x0C,0x3C,0x03,0xF7,0xBE,0x12,0x03,0x00,0x0D,0x3C,0x03,0xF9,0xF5,0x12,0x03, +0x00,0x21,0x3C,0x03,0xF9,0xF4,0x12,0x03,0x00,0x22,0x44,0x00,0x03,0x78,0x12,0x03, +0x00,0x23,0x2E,0x07,0xF6,0x1D,0x12,0x03,0x00,0x24,0x2E,0x07,0xE2,0xCC,0x12,0x03, +0x00,0x25,0x2E,0x07,0xF5,0xC5,0x12,0x03,0x00,0x26,0x49,0x00,0x17,0x6B,0x12,0x03, +0x00,0x27,0x84,0x00,0x12,0x03,0x00,0x28,0x12,0x03,0x00,0x29,0x12,0x03,0x00,0x2A, +0xFC,0x80,0xFC,0x00,0x49,0xFF,0xC0,0x0B,0x49,0xFF,0xF6,0xA3,0x84,0x00,0xFC,0x80, +0x84,0x00,0x3E,0x07,0xF5,0xC5,0x3E,0x07,0xF5,0xC4,0xDD,0x9E,0xFC,0x00,0x80,0x41, +0x84,0x20,0x5A,0x08,0x01,0x03,0x84,0x24,0xCA,0x04,0x44,0x02,0x17,0xA8,0xD5,0x03, +0x44,0x02,0x17,0x9C,0x49,0xFF,0xC2,0xD1,0xFC,0x80,0xFC,0x00,0x80,0xC0,0x5A,0x08, +0x01,0x0B,0x84,0x02,0x49,0xFF,0xC2,0x4D,0x80,0x06,0x84,0x20,0x49,0xFF,0xC2,0x7A, +0x80,0x06,0xD5,0x09,0x84,0x00,0x49,0xFF,0xC2,0x44,0x84,0x00,0x80,0x20,0x49,0xFF, +0xC2,0x71,0x84,0x00,0x49,0xFF,0xC2,0xA3,0xFC,0x80,0xFC,0x01,0xB6,0x1F,0xF1,0x81, +0x49,0xFF,0xFF,0xE5,0xB4,0x1F,0xF1,0x01,0x49,0xFF,0xFF,0xD2,0xFC,0x81,0xFC,0x23, +0x80,0xE0,0xC8,0x04,0x84,0xA4,0x84,0x23,0xD5,0x03,0x84,0xA6,0x84,0x22,0x84,0x40, +0x8E,0xA1,0x80,0x62,0x80,0x82,0x84,0x02,0x97,0x68,0x49,0xFF,0xF6,0x89,0x44,0x22, +0x01,0x7C,0x80,0xC2,0x80,0x3F,0x3A,0x23,0x14,0x04,0x3A,0x20,0x94,0x24,0x3A,0x23, +0x0C,0x00,0x3A,0x20,0x8C,0x20,0x80,0x1F,0x49,0xFF,0xC0,0xF4,0x84,0x00,0x49,0xFF, +0xC4,0x34,0x84,0x0C,0x49,0xFF,0xC4,0x5C,0xE6,0xE2,0xE8,0x05,0x84,0x01,0x80,0x27, +0x49,0xFF,0xFF,0xCD,0xFC,0xA3,0xFC,0x00,0x84,0x46,0xC8,0x02,0x84,0x44,0x84,0x20, +0x3E,0x17,0xF6,0x1D,0x50,0x11,0x00,0x13,0x40,0x10,0x88,0x36,0x3E,0x17,0xF6,0x1E, +0x49,0xFF,0xFF,0xC7,0x84,0x02,0x49,0x00,0x15,0x0E,0xFC,0x80,0xFC,0x49,0x80,0xE0, +0x44,0x02,0x00,0x01,0xF0,0x86,0x44,0x01,0x00,0x02,0xF0,0x87,0x44,0x00,0x80,0x04, +0xF0,0x88,0x44,0x00,0x40,0x08,0xF0,0x89,0x44,0x00,0x20,0x10,0xF0,0x8A,0x44,0x00, +0x10,0x20,0xF0,0x8B,0x44,0x00,0x08,0x40,0xF0,0x8C,0x44,0x00,0x04,0x80,0xF0,0x8D, +0x44,0x00,0x03,0x00,0xF0,0x8E,0x3C,0x0D,0xF8,0xEA,0xF0,0x8F,0xF0,0x90,0xF0,0x91, +0xC7,0x08,0x44,0x92,0x17,0x4C,0x44,0xA2,0x17,0x24,0x85,0x02,0x87,0x85,0xD5,0x07, +0x85,0x03,0x44,0x92,0x17,0x88,0x44,0xA2,0x17,0x60,0x83,0x88,0xB0,0x06,0x49,0xFF, +0xC0,0x6B,0x80,0xDF,0xCF,0x11,0x44,0x02,0x01,0x94,0x80,0x3F,0x3A,0x20,0x14,0x04, +0x3A,0x20,0x94,0x24,0x3A,0x20,0x0C,0x00,0x3A,0x20,0x8C,0x20,0x80,0x1F,0x49,0xFF, +0xC0,0x91,0x80,0x07,0xD5,0x10,0x44,0x02,0x01,0xAC,0x80,0x3F,0x3A,0x20,0x14,0x04, +0x3A,0x20,0x94,0x24,0x3A,0x20,0x0C,0x00,0x3A,0x20,0x8C,0x20,0x80,0x1F,0x49,0xFF, +0xC0,0x81,0x84,0x01,0x49,0x00,0x15,0x7B,0x2E,0x47,0xF6,0x23,0x84,0x00,0x80,0x4A, +0x80,0x69,0x80,0xA4,0x80,0x20,0x49,0xFF,0xF5,0xFB,0x2E,0x17,0xF6,0x23,0x84,0x00, +0x38,0x15,0x05,0x01,0x49,0xFF,0xC1,0x14,0x2E,0x17,0xF6,0x23,0x84,0x05,0x38,0x15, +0x05,0x01,0x49,0xFF,0xC1,0x0D,0x2E,0x07,0xF6,0x23,0x84,0x20,0x38,0x04,0x80,0x00, +0x49,0xFF,0xC2,0x09,0x2E,0x07,0xF6,0x23,0x84,0x23,0x38,0x04,0x80,0x00,0x49,0xFF, +0xC2,0x02,0x44,0x00,0x02,0x40,0x49,0xFF,0xC3,0x98,0x80,0x08,0x49,0xFF,0xC3,0xC0, +0xE6,0xE2,0xE8,0x07,0x2E,0x07,0xF6,0x23,0x40,0xFE,0x00,0x06,0x84,0x01,0xE8,0x02, +0x84,0x00,0x80,0x27,0x49,0xFF,0xFF,0x2B,0xFC,0xC9,0xFC,0x00,0x2E,0x17,0xEF,0x92, +0x2E,0x07,0xEF,0x93,0xE6,0x23,0xE8,0x03,0x84,0x20,0xD5,0x06,0xE6,0x3D,0xE9,0x03, +0xFA,0x2B,0xD5,0x02,0x8E,0x22,0x3E,0x17,0xF6,0x18,0xE6,0x09,0x84,0x29,0x40,0x10, +0x04,0x17,0xE9,0x03,0x52,0x00,0x00,0x08,0x96,0x00,0xC0,0x0E,0x5A,0x00,0x08,0x11, +0x5A,0x00,0x01,0x07,0x9E,0x42,0x96,0x48,0x5A,0x00,0x07,0x0F,0xD5,0x02,0x84,0x20, +0x9C,0xC2,0x96,0xD8,0xD5,0x0B,0x84,0x62,0x84,0x01,0x84,0x20,0xD5,0x07,0x80,0x60, +0x84,0x26,0x84,0x07,0xD5,0x03,0x84,0x68,0x84,0x25,0x84,0x41,0x40,0x51,0x00,0x0C, +0x40,0x41,0x04,0x0C,0xFF,0x2F,0x52,0x10,0x80,0x11,0x40,0x51,0x0C,0x0C,0xFF,0x2F, +0x40,0x11,0x04,0x0C,0x52,0x00,0x00,0x11,0xFE,0x67,0x40,0x01,0x00,0x0C,0xFE,0x0F, +0x52,0x11,0x80,0x11,0x40,0x11,0x04,0x0C,0xFE,0x0F,0x2E,0x17,0xF6,0x18,0x3C,0x0F, +0xF8,0xEA,0xFA,0x0F,0x40,0x00,0x04,0x0C,0x80,0x20,0x84,0x40,0x49,0xFF,0xBE,0xD3, +0xFC,0x80,0xFC,0x49,0xC1,0x07,0x44,0x92,0x17,0x24,0x85,0x42,0x44,0x72,0x17,0x4C, +0xD5,0x06,0x44,0x92,0x17,0x60,0x85,0x43,0x44,0x72,0x17,0x88,0x5A,0x08,0x03,0x32, +0xB1,0x86,0x44,0x12,0x01,0xC4,0x80,0x06,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24, +0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x00,0x44,0x12,0x01,0x7C, +0x3A,0x20,0x14,0x20,0x80,0x1F,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20, +0x8C,0x00,0x3A,0x20,0x0C,0x20,0x2E,0x47,0xF6,0x23,0x84,0x20,0x80,0x49,0x80,0x67, +0x80,0xA4,0x84,0x01,0x49,0xFF,0xF5,0x44,0x80,0x06,0x49,0xFF,0xBF,0x85,0x80,0x1F, +0x49,0xFF,0xBF,0xB8,0x84,0x00,0x49,0xFF,0xC2,0xF8,0x81,0x5F,0x84,0x0C,0xD5,0x35, +0x5A,0x08,0x02,0x36,0xB1,0x86,0x44,0x12,0x01,0xF4,0x81,0x06,0x3A,0x20,0x94,0x04, +0x3A,0x24,0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x24,0x14,0x24,0x3A,0x20,0x94,0x00, +0x44,0x12,0x01,0x94,0x81,0xFF,0x3A,0x24,0x14,0x20,0x3A,0x20,0x94,0x04,0x3A,0x27, +0x94,0x24,0x3A,0x20,0x8C,0x00,0x3A,0x27,0x8C,0x20,0x49,0x00,0x14,0x90,0x2E,0x47, +0xF6,0x23,0x84,0x00,0x80,0x20,0x80,0x49,0x80,0x67,0x80,0xA4,0x49,0xFF,0xF5,0x10, +0x80,0x06,0x49,0xFF,0xBF,0x51,0x80,0x1F,0x49,0xFF,0xBF,0x84,0x44,0x00,0x02,0x40, +0x49,0xFF,0xC2,0xC3,0x81,0x1F,0x80,0x0A,0x49,0xFF,0xC2,0xEA,0xFC,0xC9,0xFC,0x49, +0x44,0x12,0x01,0xF4,0xB1,0x86,0x80,0xE0,0x80,0x06,0x3A,0x20,0x94,0x04,0x3A,0x20, +0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x00,0x3A,0x20, +0x14,0x20,0xC7,0x07,0x44,0x82,0x17,0x24,0x85,0x22,0x44,0xA2,0x17,0x4C,0xD5,0x06, +0x44,0x82,0x17,0x60,0x85,0x23,0x44,0xA2,0x17,0x88,0x84,0x00,0x49,0x00,0x14,0x57, +0x2E,0x47,0xF6,0x23,0x84,0x00,0x80,0x20,0x80,0x48,0x80,0x6A,0x80,0xA4,0x49,0xFF, +0xF4,0xD7,0x80,0x06,0x49,0xFF,0xBF,0x18,0x44,0x00,0x02,0x40,0x49,0xFF,0xC2,0x8D, +0x80,0x09,0x49,0xFF,0xC2,0xB5,0x46,0x00,0x42,0x10,0x50,0x00,0x08,0x20,0x80,0x20, +0x84,0x40,0x49,0xFF,0xBE,0x20,0x80,0x1F,0xCF,0x04,0x44,0x12,0x01,0x94,0xD5,0x03, +0x44,0x12,0x01,0xAC,0x80,0xC0,0x3A,0x20,0x94,0x04,0x3A,0x23,0x14,0x24,0x3A,0x20, +0x8C,0x00,0x3A,0x23,0x0C,0x20,0x49,0xFF,0xBF,0x2D,0xFC,0xC9,0xDD,0x9E,0xFC,0x45, +0x80,0xE0,0xF1,0x81,0x80,0xC2,0xF3,0x87,0xF4,0x88,0x49,0xFF,0xF2,0x53,0xB6,0x1F, +0xCF,0x08,0xF0,0x01,0x44,0x80,0x02,0x40,0x85,0x41,0x85,0x22,0xC8,0x11,0xD5,0x07, +0x5A,0x78,0x02,0x08,0xF0,0x01,0xC8,0x09,0x81,0x00,0x85,0x44,0x85,0x23,0xD5,0x08, +0x85,0x00,0x81,0x48,0x81,0x28,0xD5,0x04,0x85,0x00,0x85,0x46,0x81,0x27,0x49,0x00, +0x15,0x1C,0xC8,0x04,0x49,0x00,0x15,0x95,0xD5,0x03,0x49,0x00,0x15,0x8F,0x80,0xE0, +0xB4,0x1F,0x49,0xFF,0xF4,0xDE,0x40,0x14,0x04,0x08,0x88,0x20,0x42,0x05,0x24,0x24, +0x94,0x81,0x94,0x02,0x80,0x69,0xB6,0x1F,0x42,0x03,0x24,0x24,0xF0,0x85,0x96,0x19, +0xF2,0x82,0x84,0xA0,0x92,0x41,0xF0,0x83,0x50,0x03,0x80,0xC0,0xF2,0x84,0x80,0x85, +0x51,0xC3,0x80,0x40,0xF0,0x89,0xF0,0x03,0xE2,0x80,0xE8,0x2C,0xF0,0x05,0x80,0xFC, +0x98,0xA0,0x94,0x91,0x88,0x41,0x85,0x40,0x40,0x05,0x08,0x00,0x50,0x93,0xFF,0xC0, +0xF0,0x86,0x85,0x00,0xF0,0x06,0x39,0xE0,0x20,0x11,0x2A,0x04,0x80,0x01,0x40,0xFF, +0x00,0x01,0x4E,0xF4,0x00,0x05,0x8A,0x1E,0x96,0x01,0xD5,0x03,0x40,0x07,0x80,0x13, +0xE2,0xA0,0x40,0x50,0x3C,0x1B,0xB4,0x1F,0x89,0x00,0x4C,0x93,0xFF,0xED,0xF0,0x02, +0x50,0x74,0x80,0x40,0x89,0x40,0xF0,0x09,0x4C,0x70,0x7F,0xE0,0x8C,0x81,0x97,0x21, +0xD5,0xD3,0x94,0x31,0x81,0x41,0x42,0xA0,0x0C,0x73,0x81,0x01,0x80,0x01,0xFA,0x90, +0x84,0xE0,0xFE,0xF4,0x83,0xCA,0x87,0x80,0xF1,0x04,0xF2,0x01,0x42,0x1E,0x04,0x24, +0x88,0x23,0xCA,0x05,0x8C,0x21,0x38,0x24,0x05,0x01,0xD5,0x03,0x38,0x20,0x05,0x01, +0x80,0x22,0x84,0xC0,0x8C,0xC1,0x04,0xFF,0x80,0x03,0x40,0x93,0x00,0x13,0xE3,0x2F, +0xE8,0x0B,0x38,0x9F,0x19,0x01,0xE2,0x29,0x40,0x14,0xBC,0x1B,0xE3,0x22,0x96,0x49, +0x40,0x24,0xBC,0x1B,0xD5,0xF0,0x8A,0x22,0xE0,0x27,0x40,0x13,0xBC,0x1B,0x97,0xC9, +0xF1,0x02,0x41,0xEF,0x04,0x00,0x5B,0xC0,0x01,0x04,0x87,0x81,0xD5,0xD6,0xB4,0x3F, +0x8E,0x81,0x97,0x21,0x89,0x01,0x88,0x01,0x89,0x41,0xCC,0xCD,0xF0,0x08,0xAD,0x40, +0xF0,0x07,0xAD,0xC0,0xFC,0xC5,0xFC,0x01,0x49,0x00,0x14,0x84,0x84,0xC5,0xC8,0x02, +0x84,0xC3,0x49,0x00,0x14,0x7F,0xF0,0x81,0x84,0x00,0xF1,0x01,0x80,0x40,0x44,0x32, +0x17,0xB6,0x44,0x42,0x17,0x22,0x49,0xFF,0xFF,0x3C,0x3C,0x5D,0xF8,0xEB,0x44,0x10, +0xFF,0xFF,0x3C,0x03,0xFA,0xE3,0xD9,0x04,0x3C,0x0F,0xF8,0xEB,0xD5,0x05,0x88,0xA0, +0x92,0xA1,0x3C,0x5F,0xF8,0xEB,0x49,0x00,0x14,0x65,0xC0,0x05,0x49,0x00,0x14,0x62, +0x5A,0x08,0x01,0x0D,0x2E,0x07,0xF6,0x23,0xE2,0xC0,0xE9,0x08,0x3C,0x03,0xFA,0x99, +0xE6,0x0A,0xE8,0x36,0x84,0x00,0x3C,0x0B,0xFA,0x99,0x3C,0x03,0xFA,0xE3,0x5C,0xF0, +0x02,0x59,0x84,0x00,0xE9,0x04,0x2E,0x07,0xF5,0x31,0x8C,0x01,0x3E,0x07,0xF5,0x31, +0x2E,0x07,0xF5,0x31,0xE6,0x02,0xE9,0x06,0x84,0x00,0x3E,0x07,0xF5,0x31,0x3E,0x07, +0xF6,0x1A,0x2E,0x07,0xF6,0x1A,0xC8,0x0F,0x3C,0x0D,0xF8,0xEB,0x5C,0xF0,0x00,0xC9, +0xE8,0x06,0x3C,0x03,0xFA,0x99,0x5C,0xF0,0x02,0x59,0xE9,0x05,0x2E,0x07,0xF5,0x30, +0x8C,0x01,0xD5,0x02,0x84,0x00,0x3E,0x07,0xF5,0x30,0x2E,0x07,0xF5,0x30,0xE6,0x02, +0x84,0x00,0xE9,0x08,0x84,0x00,0x3E,0x07,0xF5,0x30,0x84,0x01,0xD5,0x03,0x8E,0x0A, +0xD5,0xCB,0xFC,0x81,0x84,0x01,0x3E,0x07,0xF6,0x1B,0xDD,0x9E,0x2E,0x07,0xF6,0x1B, +0xDD,0x9E,0x84,0x00,0x3E,0x07,0xF6,0x1B,0xDD,0x9E,0x2E,0x07,0xF6,0x1A,0xDD,0x9E, +0x84,0x00,0x3E,0x07,0xF6,0x1A,0xDD,0x9E,0x84,0x01,0x3E,0x07,0xF6,0x19,0xDD,0x9E, +0x2E,0x07,0xF6,0x19,0xDD,0x9E,0x84,0x00,0x3E,0x07,0xF6,0x19,0xDD,0x9E,0x2E,0x07, +0xF6,0x2F,0x96,0x00,0xC0,0x05,0x84,0x00,0x3E,0x07,0xF6,0x2F,0x84,0x01,0xDD,0x9E, +0x2E,0x07,0xF6,0x2E,0x96,0x00,0xC0,0x05,0x84,0x00,0x3E,0x07,0xF6,0x2E,0x84,0x01, +0xDD,0x9E,0x2E,0x17,0xF6,0x2D,0x96,0x48,0xC1,0x05,0x84,0x00,0x3E,0x07,0xF6,0x2D, +0xD5,0x07,0x2E,0x07,0xF6,0x29,0x96,0x00,0xC0,0x04,0x3E,0x17,0xF6,0x29,0x84,0x01, +0xDD,0x9E,0x2E,0x07,0xF6,0x2C,0x96,0x00,0xC0,0x05,0x84,0x00,0x3E,0x07,0xF6,0x2C, +0xD5,0x10,0x2E,0x17,0xF6,0x2B,0x96,0x48,0xC1,0x05,0x3E,0x07,0xF6,0x2B,0x84,0x01, +0xD5,0x08,0x2E,0x07,0xF6,0x2A,0x96,0x00,0xC0,0x07,0x3E,0x17,0xF6,0x2A,0x84,0x02, +0x3E,0x07,0xF6,0x31,0x84,0x01,0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xBB,0x79,0x5A,0x00, +0x01,0xFE,0x44,0x16,0x20,0x10,0xB4,0x01,0x92,0x01,0x96,0x06,0x5A,0x00,0x01,0xFD, +0x49,0x00,0x18,0xA1,0x80,0xC0,0xC8,0x09,0x84,0x01,0x49,0xFF,0xF3,0x6B,0x64,0x00, +0x00,0x20,0x80,0x06,0x49,0xFF,0xF3,0x66,0xFC,0x80,0x2E,0x07,0xF6,0x32,0xDD,0x9E, +0x3E,0x07,0xF6,0x32,0xDD,0x9E,0x2E,0x07,0xF6,0x31,0xDD,0x9E,0x3E,0x07,0xF6,0x30, +0xDD,0x9E,0xFC,0x40,0x2E,0x07,0xF6,0x32,0xC8,0x1C,0x49,0xFF,0xF4,0x4A,0x3C,0x1D, +0xF8,0xEF,0x84,0xCB,0xFF,0x8C,0xFF,0x84,0x49,0xFF,0xF6,0x9F,0x3C,0x7D,0xF8,0xEF, +0x81,0x20,0x49,0xFF,0xF6,0x9A,0x40,0x03,0x80,0x0D,0x9D,0xC1,0x44,0x00,0x00,0x3C, +0x40,0x00,0x24,0x0C,0x84,0x2A,0xFE,0x0C,0x40,0x63,0x00,0xD7,0x98,0x3E,0xD5,0x21, +0x49,0xFF,0xF4,0x2F,0x80,0xC0,0x84,0x01,0x49,0xFF,0xF6,0x4C,0x42,0xA3,0x00,0x24, +0x84,0x01,0x49,0xFF,0xC3,0x09,0x81,0x20,0x49,0xFF,0xF6,0x7F,0x3C,0x6D,0xF8,0xEF, +0x80,0xE0,0x49,0xFF,0xF6,0x7A,0x40,0x03,0x00,0x0D,0x9D,0x81,0x40,0x25,0x24,0x0C, +0x84,0x6B,0x84,0x0A,0x40,0x00,0x1C,0x0C,0xFE,0x9C,0x40,0x11,0x00,0x36,0x98,0x31, +0x96,0x01,0xFC,0xC0,0xFC,0x00,0x84,0x03,0x49,0xFF,0xF3,0xB0,0x49,0xFF,0xF3,0x21, +0xFC,0x80,0x2E,0x07,0xF4,0xB5,0xC0,0xFE,0xFC,0x00,0x49,0xFF,0xBC,0xE1,0xFC,0x80, +0xFC,0x01,0xF0,0x81,0x84,0x01,0x49,0xFF,0xEE,0xEF,0xF0,0x01,0x49,0xFF,0xF3,0x0E, +0xF0,0x01,0x49,0xFF,0xF3,0x0C,0xF0,0x01,0x49,0xFF,0xF3,0x0A,0xFC,0x81,0xFC,0x00, +0x84,0x00,0x49,0xFF,0xF6,0x2A,0x84,0x00,0x49,0xFF,0xC3,0x8D,0x84,0x00,0x49,0xFF, +0xF6,0x2F,0x44,0x00,0x03,0x41,0x84,0x27,0x84,0x40,0x84,0x61,0x49,0x00,0x19,0xB9, +0x2E,0x07,0xF6,0x31,0xE6,0x02,0xE8,0x08,0x84,0x01,0x84,0x20,0x49,0xFF,0xC2,0xCE, +0x84,0x01,0x49,0xFF,0xFF,0xD7,0x84,0x00,0x84,0x21,0x49,0xFF,0xC2,0xC7,0x84,0x01, +0x80,0x20,0x49,0xFF,0xC3,0x3D,0x84,0x00,0x49,0x00,0x11,0x5F,0x84,0x00,0x49,0x00, +0x11,0x42,0x46,0x00,0x00,0x81,0x83,0x80,0xB9,0x1A,0x92,0x25,0x94,0x4D,0x58,0x10, +0x80,0x09,0xB9,0x9A,0xBA,0x1A,0x44,0x1F,0xE0,0xFF,0xFE,0x56,0x58,0x10,0x85,0x00, +0xB9,0x9A,0xBA,0x1A,0x46,0x1F,0xFE,0x0F,0x50,0x10,0x8F,0xFF,0xFE,0x56,0x44,0x25, +0x00,0x00,0xFE,0x57,0xB9,0x9A,0xBA,0x1A,0x46,0x1E,0x0F,0xFF,0x50,0x10,0x8F,0xFF, +0xFE,0x56,0x46,0x20,0x30,0x00,0xFE,0x57,0xB9,0x9A,0xB9,0x19,0x44,0x21,0x39,0x0A, +0x92,0x31,0x40,0x10,0xC4,0x08,0xFE,0x57,0xB9,0x99,0x84,0x00,0x3E,0x07,0xF6,0x32, +0xFC,0x80,0xFC,0x00,0x84,0x01,0x49,0x00,0x11,0x28,0x84,0x00,0x49,0x00,0x11,0x0B, +0x2E,0x67,0xF6,0x31,0xCE,0x14,0x80,0x06,0x49,0xFF,0xF5,0xD2,0x80,0x06,0x49,0xFF, +0xFF,0x89,0x80,0x06,0x80,0x26,0x49,0xFF,0xC2,0x79,0x49,0xFF,0xC3,0xCC,0xE6,0x03, +0x4E,0xF2,0x00,0xA4,0x49,0xFF,0xF3,0xE6,0x48,0x00,0x00,0xA0,0x5A,0x68,0x01,0x56, +0x84,0x00,0x49,0xFF,0xFF,0x77,0x84,0x00,0x80,0x20,0x49,0xFF,0xC2,0x67,0x44,0x24, +0x2C,0x18,0x46,0x0B,0x67,0xFF,0xB4,0x22,0x50,0x00,0x0F,0xFF,0xFE,0x0E,0x46,0x10, +0x10,0x00,0xFE,0x0F,0xB6,0x02,0x44,0x14,0x24,0x18,0x44,0x04,0x28,0x18,0x80,0x46, +0x80,0x66,0x49,0xFF,0xBA,0xCF,0x44,0x14,0x2C,0x20,0x44,0x0F,0xFF,0xB3,0xB4,0x41, +0x80,0x66,0xFE,0x16,0x58,0x00,0x00,0x48,0xB6,0x01,0x80,0x46,0x44,0x04,0x28,0x20, +0x44,0x14,0x24,0x20,0x49,0xFF,0xBA,0xBE,0x44,0x24,0x2C,0x1C,0x46,0x0F,0xFB,0x3F, +0xB4,0x22,0x50,0x00,0x0F,0xFF,0xFE,0x0E,0x46,0x10,0x04,0x80,0xFE,0x0F,0xB6,0x02, +0x44,0x14,0x24,0x1C,0x44,0x04,0x28,0x1C,0x80,0x46,0x80,0x66,0x49,0xFF,0xBA,0xAA, +0x44,0x14,0x50,0x0C,0x84,0x1E,0xB4,0x41,0xFE,0x16,0xB6,0x01,0x84,0x0F,0x49,0xFF, +0xC3,0x87,0x44,0x24,0x70,0x04,0x44,0x10,0x89,0x12,0xB4,0x02,0x92,0x10,0x40,0x00, +0x40,0x08,0xFE,0x0F,0xB6,0x02,0xD5,0x52,0x46,0x00,0x00,0x81,0x83,0x80,0xB9,0x1A, +0x92,0x25,0x94,0x4D,0x58,0x10,0x80,0x03,0xB9,0x9A,0xBA,0x1A,0x44,0x1F,0xE0,0xFF, +0xFE,0x56,0x58,0x10,0x82,0x00,0xB9,0x9A,0xBA,0x1A,0x46,0x1F,0xFE,0x0F,0x50,0x10, +0x8F,0xFF,0xFE,0x56,0x44,0x21,0x00,0x00,0xFE,0x57,0xB9,0x9A,0xBA,0x1A,0x46,0x1E, +0x0F,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x56,0xB9,0x9A,0xB9,0x19,0x84,0x41,0x92,0x31, +0x40,0x10,0xC4,0x08,0x58,0x10,0xED,0x60,0x80,0x62,0xB9,0x99,0x44,0x00,0x02,0xBC, +0x84,0x20,0x49,0x00,0x18,0xCE,0x84,0x21,0x84,0x00,0x49,0xFF,0xC2,0x61,0x84,0x01, +0x49,0xFF,0xC2,0x68,0x84,0x01,0x80,0x20,0x49,0xFF,0xC1,0xE0,0x84,0x01,0x49,0xFF, +0xC2,0x2D,0x84,0x01,0x49,0xFF,0xC2,0x87,0x49,0xFF,0xC3,0x2D,0xE6,0x03,0xE8,0x03, +0x49,0xFF,0xF3,0x48,0x49,0xFF,0xC1,0x98,0x84,0x00,0xFA,0x2F,0x80,0x40,0x84,0x61, +0x80,0x80,0x49,0xFF,0xF3,0x8D,0x49,0xFF,0xC1,0x8F,0x84,0x00,0x49,0xFF,0xEE,0x90, +0x84,0x00,0x49,0xFF,0xEE,0x96,0x84,0x01,0x3E,0x07,0xF6,0x32,0x2E,0x07,0xF6,0x31, +0x5A,0x08,0x02,0x04,0x49,0xFF,0xC5,0xD3,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xF2,0x70, +0xFC,0x80,0xDD,0x9E,0x84,0x40,0x44,0x42,0x18,0x30,0x96,0xD1,0xE2,0x61,0xE8,0x07, +0x38,0x30,0x08,0x00,0x38,0x32,0x08,0x08,0x8C,0x41,0xD5,0xF8,0xDD,0x9E,0xFC,0x40, +0x22,0xFF,0x80,0x14,0x22,0x8F,0x80,0x16,0x22,0xAF,0x80,0x18,0x00,0x9F,0x80,0x34, +0x00,0x7F,0x80,0x38,0x00,0x6F,0x80,0x3C,0x4E,0x03,0x00,0x99,0x45,0xC7,0x20,0xC0, +0x46,0x0F,0xFF,0x0F,0x05,0xEE,0x00,0x00,0x96,0x5F,0x50,0x00,0x0F,0xFF,0x40,0x10, +0xC0,0x08,0x40,0x0F,0x00,0x02,0xFE,0x0F,0xB6,0x1C,0xC4,0x0C,0x44,0x07,0x20,0xC4, +0x40,0x21,0x30,0x08,0xB4,0x20,0x92,0x4C,0x92,0x34,0x40,0x10,0xD0,0x08,0xFE,0x8F, +0xB6,0x40,0xC5,0x0C,0x44,0x07,0x20,0xCC,0x40,0x31,0xB0,0x08,0xB4,0x20,0x92,0x6C, +0x92,0x34,0x40,0x10,0xD0,0x08,0xFE,0xCF,0xB6,0x60,0x44,0x27,0x20,0xD4,0xF0,0x08, +0xB4,0x22,0x40,0x00,0x30,0x08,0x92,0x34,0x40,0x10,0xD0,0x08,0x92,0x0C,0x44,0x37, +0x20,0xC8,0xFE,0x0F,0xB6,0x02,0xB4,0x03,0x54,0x17,0x80,0xFF,0x92,0x08,0x40,0x00, +0x20,0x08,0x44,0x27,0x20,0xD0,0xFE,0x0F,0xB6,0x03,0xB4,0x02,0x54,0x14,0x00,0xFF, +0x92,0x08,0x40,0x00,0x20,0x08,0xFE,0x0F,0x44,0x17,0x20,0xD8,0xB6,0x02,0xB4,0x01, +0x54,0xA5,0x00,0xFF,0x92,0x08,0x40,0x00,0x20,0x08,0x40,0x00,0x28,0x04,0xB6,0x01, +0x54,0x04,0x80,0x03,0x40,0xA0,0x60,0x08,0xB5,0x23,0x46,0x0F,0xCF,0xFF,0x50,0x00, +0x0F,0xFF,0x40,0x94,0x80,0x02,0x40,0x94,0xA8,0x04,0xB7,0x23,0x97,0xCF,0x40,0x93, +0xE0,0x08,0xB4,0xE2,0x97,0x8F,0xFF,0xC6,0x40,0x73,0xA4,0x04,0xB6,0xE2,0xB4,0xE1, +0x40,0x63,0x60,0x08,0xFE,0x3E,0xFE,0x37,0xB6,0x01,0xF0,0x10,0x44,0x77,0x20,0xDC, +0x40,0x00,0x3C,0x08,0x40,0x60,0x3C,0x09,0xB4,0x07,0x40,0x42,0x7C,0x08,0x92,0x11, +0x40,0x00,0x44,0x08,0xFE,0x37,0xB6,0x07,0xB4,0x03,0x40,0x52,0xFC,0x08,0x94,0x01, +0x92,0x01,0xFE,0x27,0xB6,0x03,0xB4,0x02,0x94,0x01,0x92,0x01,0xFE,0x2F,0xB6,0x02, +0xF0,0x09,0x40,0x20,0x7C,0x08,0xB4,0x01,0x94,0x01,0x92,0x01,0xFE,0x17,0xB6,0x01, +0x84,0x20,0x44,0x07,0x20,0xE0,0x48,0x00,0x00,0x9D,0x5A,0x00,0x01,0x04,0x48,0x00, +0x00,0x9A,0x45,0xC7,0x21,0x00,0x46,0x0F,0xFF,0x0F,0x05,0xEE,0x00,0x00,0x96,0x5F, +0x50,0x00,0x0F,0xFF,0x40,0x10,0xC0,0x08,0x40,0x0F,0x00,0x02,0xFE,0x0F,0xB6,0x1C, +0xC4,0x0C,0x44,0x07,0x21,0x04,0x40,0x21,0x30,0x08,0xB4,0x20,0x92,0x4C,0x92,0x34, +0x40,0x10,0xD0,0x08,0xFE,0x8F,0xB6,0x40,0xC5,0x0C,0x44,0x07,0x21,0x0C,0x40,0x31, +0xB0,0x08,0xB4,0x20,0x92,0x6C,0x92,0x34,0x40,0x10,0xD0,0x08,0xFE,0xCF,0xB6,0x60, +0x44,0x27,0x21,0x14,0xF0,0x08,0xB4,0x22,0x40,0x00,0x30,0x08,0x92,0x34,0x40,0x10, +0xD0,0x08,0x92,0x0C,0xFE,0x0F,0xB6,0x02,0x44,0x27,0x21,0x08,0x54,0x17,0x80,0xFF, +0xB4,0x02,0x54,0x35,0x00,0xFF,0x92,0x08,0x40,0x00,0x20,0x08,0xFE,0x0F,0x44,0x17, +0x21,0x10,0xB6,0x02,0x54,0x04,0x00,0xFF,0xB5,0x01,0x97,0xCF,0x93,0x08,0x40,0x84, +0x20,0x08,0x40,0x84,0x00,0x04,0x44,0x07,0x21,0x18,0xB7,0x01,0xB5,0x40,0x97,0x8F, +0x93,0x48,0x40,0xA5,0x20,0x08,0x40,0xA5,0x0C,0x04,0xB7,0x40,0x54,0x34,0x80,0x03, +0x46,0xAF,0xCF,0xFF,0xB5,0x22,0x50,0xA5,0x0F,0xFF,0x40,0x31,0xE0,0x08,0x40,0x94, +0xA8,0x02,0x40,0x94,0x8C,0x04,0xB7,0x22,0x40,0x33,0xE0,0x08,0xB4,0xE1,0x40,0x42, +0x7C,0x08,0x40,0x73,0xA8,0x02,0xFF,0xDF,0xB6,0xE1,0x40,0x33,0x60,0x08,0xB4,0xC0, +0x44,0x77,0x21,0x1C,0x40,0x63,0x28,0x02,0xFF,0x9F,0xF3,0x10,0xB6,0xC0,0x40,0x31, +0xBC,0x08,0x40,0x61,0xBC,0x09,0xB4,0x67,0x40,0x52,0xFC,0x08,0x92,0x71,0x40,0x31, +0xC4,0x08,0xFE,0xF7,0xB6,0x67,0xB4,0x62,0x94,0xD9,0x92,0x61,0xFF,0x1F,0xB6,0x82, +0xB4,0x41,0x94,0x91,0x92,0x41,0xFF,0x57,0xB6,0xA1,0xF1,0x09,0x40,0x20,0xFC,0x08, +0xB4,0x20,0x94,0x49,0x92,0x21,0xFE,0x57,0xB6,0x20,0x84,0x20,0x44,0x07,0x21,0x20, +0xB6,0x20,0xFC,0xC0,0xFC,0x00,0x84,0x00,0x49,0xFF,0xC1,0x62,0x44,0x16,0x00,0x00, +0xB4,0x01,0x58,0x00,0x01,0x00,0xB6,0x01,0x44,0x02,0x18,0x30,0x44,0x10,0x00,0x79, +0xAE,0x41,0x44,0x1F,0xFF,0x84,0xAE,0x42,0x84,0x39,0xAE,0x43,0xFC,0x80,0x84,0x20, +0x84,0x40,0x3C,0x1B,0xFB,0x1E,0x44,0x42,0x18,0x30,0x80,0x22,0x96,0xD1,0xE2,0x60, +0xE8,0x06,0x38,0x32,0x08,0x00,0x8C,0x41,0x88,0x23,0xD5,0xF9,0xFC,0x00,0x44,0x22, +0x18,0x30,0xFE,0x4A,0x96,0x48,0x38,0x11,0x00,0x08,0x8C,0x01,0x96,0x01,0x49,0xFF, +0xEE,0x9D,0x2E,0x07,0xF6,0x40,0x49,0xFF,0xEE,0x84,0x49,0xFF,0xC1,0x84,0xE6,0x03, +0xE9,0x03,0x49,0xFF,0xED,0x0C,0xFC,0x80,0xFC,0x01,0x84,0x00,0x3E,0x07,0xFC,0x49, +0x84,0x01,0x3E,0x07,0xE3,0xB8,0x84,0x20,0x44,0x00,0x01,0x00,0x49,0xFF,0xB7,0xE8, +0x44,0x00,0x01,0x00,0x84,0x20,0x49,0xFF,0xB7,0xF2,0x44,0x00,0x01,0x00,0x84,0x21, +0x49,0xFF,0xB7,0xFC,0x44,0x40,0x05,0x78,0xB6,0x9F,0x44,0x12,0x18,0x30,0x44,0x22, +0x18,0x2E,0x44,0x32,0x18,0x2C,0x44,0x50,0x00,0x90,0x44,0x02,0x1D,0xA8,0x49,0xFF, +0xEE,0x5C,0x44,0x00,0x9E,0x2C,0x49,0xFF,0xEE,0x72,0x44,0x00,0x97,0x12,0x49,0xFF, +0xEE,0x71,0x84,0x00,0x49,0xFF,0xB7,0xFE,0x84,0x00,0x3C,0x0B,0xFB,0x1F,0x44,0x00, +0x05,0x77,0x49,0xFF,0xFF,0xA6,0x49,0xFF,0xFF,0x8F,0xFC,0x81,0xFC,0x00,0xC8,0x03, +0xFA,0x1A,0xD5,0x03,0x44,0x00,0x05,0x1B,0x49,0xFF,0xFF,0x9B,0xFC,0x80,0xFC,0x00, +0x84,0x07,0x49,0xFF,0xFF,0x96,0xFC,0x80,0xFC,0x00,0x44,0x00,0x00,0xA9,0x49,0xFF, +0xFF,0x90,0xFC,0x80,0xFC,0x00,0x44,0x00,0x01,0x00,0x84,0x20,0x49,0xFF,0xB7,0xBE, +0x84,0x00,0x3C,0x0F,0xFD,0x8D,0x3C,0x0D,0xFD,0x8D,0x5C,0xF0,0x00,0x64,0xE8,0x05, +0x3C,0x0D,0xFD,0x8D,0x8C,0x01,0xD5,0xF6,0x44,0x00,0x01,0x00,0x84,0x21,0x49,0xFF, +0xB7,0xAD,0xFC,0x80,0x2E,0x07,0xFC,0x49,0xDD,0x9E,0x2E,0x07,0xE3,0xB8,0xDD,0x9E, +0xFC,0x00,0x44,0x10,0x00,0xFF,0x80,0xC0,0x44,0x20,0x05,0x78,0x44,0x02,0x18,0x30, +0x49,0x00,0x1D,0x02,0xCE,0x06,0x44,0x00,0x00,0x5A,0x3E,0x07,0xF6,0x40,0xFC,0x80, +0x44,0x0F,0xFF,0xA7,0x3E,0x07,0xF6,0x40,0x84,0x05,0x3E,0x07,0xF6,0x41,0xFA,0x0C, +0x3E,0x07,0xF6,0x42,0xFA,0x10,0x3E,0x07,0xF6,0x43,0x84,0x1F,0x3E,0x07,0xF6,0x44, +0xFC,0x80,0xFC,0x00,0x04,0x30,0x00,0x47,0x46,0x40,0x10,0x00,0x40,0x51,0xC0,0x09, +0xFF,0x1E,0x80,0xC0,0x97,0x5F,0xC4,0x26,0x92,0x74,0x96,0xDF,0x4C,0x32,0xC0,0x09, +0x9D,0x5D,0x84,0x66,0x40,0x32,0x8C,0xB6,0x97,0x68,0x84,0x01,0xD5,0x0F,0x50,0x00, +0x01,0x1B,0x9D,0x29,0x84,0x66,0x40,0x42,0x0C,0x76,0x54,0x41,0x80,0x0F,0xA6,0xC3, +0x54,0x31,0x80,0xF0,0xFE,0xE7,0xAE,0xC3,0x84,0x02,0xAE,0x10,0x84,0x4C,0x80,0x01, +0x80,0x26,0x42,0x12,0x88,0x73,0x50,0x10,0x80,0x94,0x49,0x00,0x1C,0x97,0x84,0x01, +0xFC,0x80,0xAF,0x10,0x80,0x04,0xFC,0x80,0xFC,0x42,0x84,0x20,0x80,0xE0,0x84,0x4C, +0xB0,0x01,0x49,0x00,0x1C,0xB1,0x2E,0x07,0xF6,0x33,0x44,0x82,0x33,0x90,0x8C,0x01, +0x3E,0x07,0xF6,0x33,0x85,0x45,0x84,0xC1,0x87,0x80,0x44,0x92,0x18,0x30,0x80,0x08, +0xB0,0x41,0x50,0x2F,0x80,0x03,0x49,0xFF,0xFF,0xB6,0x4E,0x02,0x00,0x9C,0x00,0x14, +0x01,0x05,0x5A,0x18,0x03,0x08,0x00,0x0F,0x80,0x03,0x5A,0x08,0x01,0x04,0x48,0x00, +0x00,0x92,0x8E,0x22,0xE6,0x22,0x4E,0xF2,0x00,0x8E,0x3C,0x0D,0xFB,0xD6,0x96,0x1E, +0xC8,0x06,0x44,0x20,0x00,0x5F,0x44,0x10,0x00,0x3C,0xD5,0x05,0x44,0x20,0x00,0xC8, +0x44,0x10,0x00,0x58,0xF0,0x01,0x5E,0xF0,0x1B,0x59,0xE9,0x21,0x40,0x10,0x08,0x36, +0x5E,0xF0,0x81,0x00,0xE9,0x03,0x44,0x10,0x00,0xFF,0x50,0x00,0xFF,0x92,0xE6,0x14, +0xE8,0x02,0x8C,0x34,0x50,0x00,0xFF,0xA6,0xE6,0x14,0xE8,0x04,0x50,0x10,0x80,0x28, +0xD5,0x2C,0x50,0x00,0xFF,0x69,0xE6,0x14,0xE8,0x03,0x8E,0x34,0xD5,0x26,0x50,0x00, +0xFF,0x55,0xE6,0x14,0xE8,0x22,0x50,0x10,0xFF,0xD8,0xD5,0x1F,0x40,0x10,0x04,0x36, +0x5E,0xF0,0x81,0x00,0xE9,0x03,0x44,0x10,0x00,0xFF,0x50,0x00,0xFF,0xE2,0xE6,0x0A, +0xE8,0x03,0x8C,0x2A,0xD5,0x12,0x50,0x00,0xFF,0xEC,0xE6,0x0A,0xE8,0x03,0x8C,0x34, +0xD5,0x0C,0x50,0x20,0xFF,0xCD,0xE6,0x4A,0xE8,0x03,0x8E,0x2A,0xD5,0x06,0x50,0x20, +0xFF,0xC3,0xE6,0x4A,0x40,0x10,0x3C,0x1B,0x00,0x4F,0x80,0x0C,0x5A,0x48,0x01,0x3B, +0x22,0x0F,0x80,0x04,0x22,0x2F,0x80,0x05,0xCF,0x1F,0x40,0x31,0x20,0x0A,0x54,0x41, +0x80,0x0F,0x40,0x30,0x10,0x0A,0x54,0x31,0x80,0xF0,0xFE,0xE7,0x38,0x34,0x98,0x08, +0x9C,0xF1,0x96,0x00,0x38,0x04,0x8C,0x08,0x9C,0x32,0x96,0x90,0x5E,0xF0,0x80,0xFF, +0x38,0x24,0x80,0x08,0x9C,0x33,0xE9,0x03,0x44,0x10,0x00,0xFE,0x96,0x48,0x38,0x14, +0x80,0x08,0x87,0x81,0xD5,0x17,0x40,0x11,0x20,0x0A,0x54,0x30,0x80,0x0F,0x40,0x10, +0x10,0x0A,0x54,0x10,0x80,0xF0,0xFE,0x5F,0x38,0x14,0xA8,0x08,0x50,0x15,0x00,0x01, +0x96,0x00,0x38,0x04,0x84,0x08,0x50,0x05,0x00,0x02,0x96,0x90,0x38,0x24,0x80,0x08, +0x83,0x84,0x8C,0xC4,0x50,0x84,0x01,0x24,0x8D,0x43,0x5A,0x60,0x29,0x04,0x48,0xFF, +0xFF,0x58,0x80,0x1C,0xFC,0xC2,0xFA,0xB3,0x44,0x22,0x18,0x30,0x44,0x30,0x04,0xA3, +0x80,0x80,0x0A,0x10,0x00,0x01,0x92,0x28,0x38,0x11,0x14,0x08,0x9C,0x69,0xA7,0x20, +0x8C,0xA2,0x38,0x41,0x04,0x08,0xDB,0xF5,0xDD,0x9E,0x44,0x50,0x04,0xA3,0x44,0x22, +0x18,0x30,0x44,0x30,0x05,0x07,0x80,0x80,0x0A,0x10,0x00,0x01,0x92,0x28,0x38,0x11, +0x14,0x08,0x9C,0x69,0xA7,0x20,0x8C,0xA2,0x38,0x41,0x04,0x08,0xDB,0xF5,0xDD,0x9E, +0x44,0x1F,0xFF,0xB7,0x3E,0x17,0xF6,0x40,0x84,0x24,0x3E,0x17,0xF6,0x41,0x44,0x1F, +0xFF,0x85,0x3E,0x17,0xF6,0x42,0x2E,0x17,0xF4,0x21,0x3E,0x17,0xF6,0x43,0x84,0xA4, +0x44,0x22,0x18,0x30,0x44,0x30,0x04,0x84,0x80,0x80,0x0A,0x10,0x00,0x01,0x92,0x28, +0x38,0x11,0x14,0x08,0x9C,0x69,0xA7,0x20,0x8C,0xA2,0x38,0x41,0x04,0x08,0xDB,0xF5, +0xDD,0x9E,0xC8,0x03,0x3E,0x17,0xF6,0x69,0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xEC,0xCC, +0xFC,0x80,0xFC,0x00,0x49,0xFF,0xEC,0xC5,0xFC,0x80,0xFC,0x00,0x49,0x00,0x0D,0x6C, +0x2E,0x57,0xFC,0x48,0xCD,0x18,0x84,0x32,0x3E,0x17,0xF6,0x40,0x44,0x22,0x18,0x30, +0x44,0x30,0x04,0x80,0x80,0x80,0x2A,0x10,0x00,0x01,0x9D,0xA9,0x90,0x28,0x96,0x48, +0x38,0x11,0x18,0x08,0x8C,0xA2,0xA6,0x60,0x38,0x11,0x14,0x08,0xDB,0xF4,0x44,0x00, +0x04,0x81,0xD5,0x12,0x5A,0x58,0x01,0x13,0x84,0x12,0x3E,0x07,0xF6,0x40,0x44,0x12, +0x18,0x30,0x80,0x05,0x84,0x40,0x9C,0xC1,0x38,0x20,0x80,0x08,0x8C,0x02,0x38,0x20, +0x8C,0x08,0x5A,0x08,0x65,0xFA,0x49,0xFF,0xFD,0xDC,0xFC,0x80,0xFC,0x41,0x3C,0x23, +0xFB,0x1F,0x2E,0x17,0xFB,0xB8,0x96,0x91,0x96,0x48,0x44,0x62,0x1D,0xA8,0x5A,0x18, +0x35,0x04,0x48,0x00,0x08,0x81,0x5A,0x18,0x30,0x04,0x48,0x00,0x08,0x74,0x5A,0x18, +0x33,0x04,0x48,0x00,0x08,0x70,0x5A,0x18,0x31,0x04,0x48,0x00,0x08,0x8A,0x5A,0x18, +0x34,0x04,0x48,0x00,0x08,0x86,0x84,0x00,0x5A,0x18,0x32,0x04,0x48,0x00,0x08,0x77, +0x5A,0x20,0x03,0x04,0x48,0x00,0x01,0x53,0x5A,0x18,0x53,0x0F,0x2E,0x07,0xFB,0xB9, +0x2E,0x17,0xFB,0xBA,0x40,0x00,0x20,0x08,0xFE,0x0F,0x50,0x00,0x7F,0xB8,0x5C,0x00, +0x00,0x01,0x48,0x00,0x04,0x42,0x5A,0x10,0x01,0x05,0x84,0x00,0x48,0x00,0x04,0x3D, +0x2E,0x07,0xFB,0xB9,0x5A,0x08,0x01,0x16,0x2E,0x17,0xFB,0xBA,0x84,0x00,0x5A,0x18, +0x01,0x05,0x3C,0x0F,0xFF,0x15,0x84,0x06,0x2E,0x17,0xFB,0xBA,0x4E,0x13,0x04,0x2D, +0x84,0x01,0x3C,0x0F,0xFF,0x15,0x49,0xFF,0xBE,0x94,0x84,0x07,0x48,0x00,0x04,0x25, +0x2E,0x07,0xFB,0xB9,0x5A,0x08,0x02,0x13,0x2E,0x07,0xFB,0xBA,0x5A,0x08,0x03,0x04, +0x48,0x00,0x03,0xC6,0x2E,0x07,0xFB,0xBA,0x4E,0x02,0x03,0xC4,0x2E,0x07,0xFB,0xBA, +0x5A,0x08,0x01,0xD5,0x84,0x08,0x48,0x00,0x04,0x10,0x2E,0x07,0xFB,0xB9,0x5A,0x08, +0x03,0x0E,0x2E,0x07,0xFB,0xBA,0xC0,0xCA,0x2E,0x07,0xFB,0xBA,0x5A,0x00,0x01,0xC7, +0x2E,0x07,0xFB,0xBA,0x84,0x00,0x48,0x00,0x04,0x00,0x2E,0x07,0xFB,0xB9,0x5A,0x08, +0x04,0x12,0x2E,0x07,0xFB,0xBA,0xC0,0xBA,0x2E,0x07,0xFB,0xBA,0x5A,0x00,0x01,0xB7, +0x2E,0x07,0xFB,0xBA,0x5A,0x00,0x10,0xB3,0x2E,0x07,0xFB,0xBA,0x84,0x00,0x48,0x00, +0x03,0xEC,0x2E,0x07,0xFB,0xB9,0x5A,0x00,0x05,0xAA,0x2E,0x07,0xFB,0xB9,0x5A,0x08, +0x06,0x11,0x2E,0x07,0xFB,0xBA,0x96,0x00,0x5A,0x08,0x01,0x04,0x3C,0x0F,0xFC,0xFD, +0x2E,0x07,0xFB,0xBA,0x96,0x00,0xC8,0x9A,0x3C,0x0F,0xFC,0xFD,0x48,0x00,0x03,0xD5, +0x2E,0x07,0xFB,0xB9,0x5A,0x08,0x07,0x11,0x2E,0x07,0xFB,0xBA,0x96,0x00,0x5A,0x08, +0x01,0x04,0x3C,0x0F,0xFC,0xFC,0x2E,0x07,0xFB,0xBA,0x96,0x00,0xC8,0x87,0x3C,0x0F, +0xFC,0xFC,0x48,0x00,0x03,0xC2,0x2E,0x07,0xFB,0xB9,0x5A,0x08,0x08,0x0B,0x2E,0x07, +0xFB,0xBA,0x4E,0x02,0xFF,0x7C,0x2E,0x07,0xFB,0xBA,0x84,0x00,0x48,0x00,0x03,0xB5, +0x2E,0x07,0xFB,0xB9,0x5A,0x08,0x09,0x04,0x48,0xFF,0xFF,0x71,0x2E,0x07,0xFB,0xB9, +0x5A,0x08,0x0A,0x33,0x2E,0x07,0xFB,0xBA,0x4E,0x02,0x03,0x56,0x2E,0x07,0xFB,0xBA, +0x5A,0x08,0x01,0x04,0x48,0x00,0x03,0x52,0x2E,0x07,0xFB,0xBA,0x5A,0x08,0x02,0x04, +0x48,0x00,0x03,0x4E,0x2E,0x07,0xFB,0xBA,0x5A,0x08,0x03,0x04,0x48,0x00,0x03,0x4A, +0x2E,0x07,0xFB,0xBA,0x5A,0x08,0x04,0x04,0x48,0x00,0x03,0x46,0x2E,0x07,0xFB,0xBA, +0x5A,0x08,0x05,0x04,0x48,0x00,0x03,0x42,0x2E,0x07,0xFB,0xBA,0x5A,0x08,0x06,0x04, +0x48,0x00,0x03,0x3E,0x2E,0x07,0xFB,0xBA,0x5A,0x00,0x07,0x04,0x48,0xFF,0xFF,0x3F, +0xFA,0x03,0x48,0x00,0x03,0x7A,0x2E,0x07,0xFB,0xB9,0x5A,0x08,0x0B,0x04,0x48,0xFF, +0xFF,0x36,0x2E,0x07,0xFB,0xB9,0x5A,0x08,0x0C,0x09,0x2E,0x07,0xFB,0xBA,0x2E,0x07, +0xFB,0xBA,0x84,0x00,0x48,0x00,0x03,0x69,0x2E,0x07,0xFB,0xB9,0x5A,0x08,0x0F,0x09, +0x2E,0x07,0xFB,0xBA,0x2E,0x07,0xFB,0xBA,0x84,0x00,0x48,0x00,0x03,0x5E,0x2E,0x07, +0xFB,0xB9,0x5A,0x08,0x10,0x09,0x2E,0x07,0xFB,0xBA,0x2E,0x07,0xFB,0xBA,0x84,0x00, +0x48,0x00,0x03,0x53,0x2E,0x07,0xFB,0xB9,0x5A,0x08,0x11,0x1A,0x2E,0x07,0xFB,0xBA, +0xC8,0x07,0x3C,0x0D,0xFB,0xD6,0x58,0x00,0x00,0x08,0x3C,0x0F,0xFB,0xD6,0x2E,0x07, +0xFB,0xBA,0x5A,0x00,0x01,0x04,0x48,0xFF,0xFF,0x02,0x3C,0x1D,0xFB,0xD6,0x84,0x17, +0xFE,0x0E,0x3C,0x0F,0xFB,0xD6,0x84,0x00,0x48,0x00,0x03,0x37,0x2E,0x07,0xFB,0xB9, +0x5A,0x08,0x12,0x09,0x2E,0x07,0xFB,0xBA,0x3C,0x0B,0xF9,0xF7,0x84,0x00,0x48,0x00, +0x03,0x2C,0x2E,0x07,0xFB,0xB9,0x5A,0x08,0x13,0x13,0x2E,0x07,0xFB,0xBA,0x2E,0x07, +0xFB,0xBA,0x5C,0xF0,0x00,0x82,0xE8,0x04,0x2E,0x07,0xFB,0xBA,0xD5,0x03,0x44,0x00, +0x00,0x81,0x3C,0x0B,0xF9,0xF6,0x84,0x00,0x48,0x00,0x03,0x17,0x2E,0x07,0xFB,0xB9, +0x5A,0x00,0x14,0x04,0x48,0xFF,0xFE,0xD3,0x2E,0x07,0xFB,0xBA,0xC8,0x08,0x3C,0x1D, +0xFB,0xD6,0x44,0x0F,0xDF,0xFF,0xFE,0x0E,0x3C,0x0F,0xFB,0xD6,0x2E,0x07,0xFB,0xBA, +0x84,0x00,0x48,0x00,0x03,0x02,0x44,0x00,0x00,0x3C,0x5A,0x28,0x01,0x36,0x5A,0x18, +0x01,0x04,0x48,0x00,0x02,0xB7,0x5A,0x18,0x03,0x04,0x48,0x00,0x02,0xB5,0x5A,0x18, +0x20,0x04,0x48,0x00,0x02,0xB3,0x5A,0x18,0x28,0x04,0x48,0x00,0x02,0xB1,0x5A,0x18, +0x21,0x04,0x48,0x00,0x02,0xAF,0x5A,0x18,0x22,0x04,0x48,0x00,0x02,0xAD,0x5A,0x18, +0x23,0x04,0x48,0x00,0x02,0xAB,0x5A,0x18,0x15,0x04,0x48,0x00,0x02,0xA9,0x5A,0x18, +0x17,0x04,0x48,0x00,0x02,0xA7,0x5A,0x18,0x04,0x04,0x48,0x00,0x02,0xA5,0x5A,0x18, +0xF3,0x04,0x48,0x00,0x02,0xA4,0x5A,0x10,0xFE,0x04,0x48,0x00,0x02,0xCE,0x44,0x00, +0x00,0x31,0x48,0x00,0x02,0xCA,0x5A,0x20,0x02,0x04,0x48,0x00,0x01,0x3A,0x5A,0x18, +0xF6,0x0C,0x2E,0x17,0xFB,0xB9,0x5A,0x10,0xF2,0x04,0x48,0x00,0x02,0xBE,0x44,0x00, +0x00,0x36,0x48,0x00,0x02,0xBA,0x5A,0x18,0x61,0x1D,0x2E,0x17,0xFB,0xB9,0x96,0x48, +0xC9,0x09,0x44,0x00,0x01,0x00,0x49,0xFF,0xB4,0x49,0x44,0x00,0x00,0x40,0x48,0x00, +0x02,0xAC,0x2E,0x17,0xFB,0xB9,0x96,0x48,0x5A,0x10,0x01,0x04,0x48,0x00,0x02,0x7A, +0x44,0x00,0x01,0x00,0x49,0xFF,0xB4,0x3A,0x44,0x00,0x00,0x40,0x48,0x00,0x02,0x9D, +0x5A,0x18,0x62,0x24,0x2E,0x07,0xFB,0xB9,0x96,0x00,0xC8,0x07,0x49,0xFF,0xE9,0xE5, +0x44,0x00,0x00,0x41,0x48,0x00,0x02,0x91,0x2E,0x07,0xFB,0xB9,0x96,0x00,0x5A,0x08, +0x01,0x08,0x49,0xFF,0xE9,0xDA,0x44,0x00,0x00,0x41,0x48,0x00,0x02,0x86,0x2E,0x07, +0xFB,0xB9,0x5A,0x00,0x02,0x04,0x48,0x00,0x02,0x58,0x80,0x02,0x49,0xFF,0xE9,0xCD, +0x44,0x00,0x00,0x41,0x48,0x00,0x02,0x79,0x5A,0x18,0xF0,0x1D,0x2E,0x17,0xFB,0xB9, +0x4E,0x12,0x02,0x4E,0x2E,0x17,0xFB,0xB9,0x5A,0x18,0x01,0x04,0x48,0x00,0x02,0x4A, +0x2E,0x17,0xFB,0xB9,0x5A,0x18,0x02,0x04,0x48,0x00,0x02,0x46,0x2E,0x17,0xFB,0xB9, +0x5A,0x10,0xFA,0x04,0x48,0x00,0x02,0x61,0x84,0x21,0x3E,0x17,0xF6,0x38,0x48,0x00, +0x02,0x5C,0x5A,0x18,0x02,0x15,0x2E,0x17,0xFB,0xB9,0x5A,0x18,0x03,0x04,0x48,0x00, +0x02,0x35,0x2E,0x17,0xFB,0xB9,0x4E,0x12,0x02,0x33,0x2E,0x17,0xFB,0xB9,0x5A,0x10, +0x01,0x04,0x48,0x00,0x02,0x4A,0x84,0x08,0x48,0x00,0x02,0x47,0x5A,0x18,0xFA,0x12, +0x2E,0x17,0xFB,0xB9,0x5A,0x18,0x03,0x04,0x48,0x00,0x02,0x24,0x2E,0x17,0xFB,0xB9, +0x5A,0x10,0x08,0x04,0x48,0x00,0x02,0x39,0x44,0x00,0x00,0x33,0x48,0x00,0x02,0x35, +0x5A,0x18,0x01,0x16,0x2E,0x17,0xFB,0xB9,0x5A,0x18,0x01,0x06,0x84,0x00,0x3C,0x0F, +0xFF,0x15,0x84,0x06,0x2E,0x17,0xFB,0xB9,0x4E,0x13,0x02,0x27,0x84,0x01,0x3C,0x0F, +0xFF,0x15,0x49,0xFF,0xBC,0x8E,0x84,0x07,0x48,0x00,0x02,0x1F,0x5A,0x18,0x0A,0x33, +0x2E,0x17,0xFB,0xB9,0x4E,0x12,0x02,0x01,0x2E,0x17,0xFB,0xB9,0x5A,0x18,0x01,0x04, +0x48,0x00,0x01,0xFD,0x2E,0x17,0xFB,0xB9,0x5A,0x18,0x02,0x04,0x48,0x00,0x01,0xF9, +0x2E,0x17,0xFB,0xB9,0x5A,0x18,0x03,0x04,0x48,0x00,0x01,0xF5,0x2E,0x17,0xFB,0xB9, +0x5A,0x18,0x04,0x04,0x48,0x00,0x01,0xF1,0x2E,0x17,0xFB,0xB9,0x5A,0x18,0x05,0x04, +0x48,0x00,0x01,0xED,0x2E,0x17,0xFB,0xB9,0x5A,0x18,0x06,0x04,0x48,0x00,0x01,0xE9, +0x2E,0x17,0xFB,0xB9,0x5A,0x10,0x07,0x04,0x48,0x00,0x01,0xEF,0xFA,0x03,0x48,0x00, +0x01,0xEC,0x5A,0x18,0x06,0x12,0x2E,0x17,0xFB,0xB9,0x96,0x48,0x5A,0x18,0x01,0x04, +0x3C,0x1F,0xFC,0xFD,0x2E,0x17,0xFB,0xB9,0x96,0x48,0x4E,0x13,0x01,0xDE,0x3C,0x1F, +0xFC,0xFD,0x48,0x00,0x01,0xDA,0x5A,0x18,0x07,0x12,0x2E,0x17,0xFB,0xB9,0x96,0x48, +0x5A,0x18,0x01,0x04,0x3C,0x1F,0xFC,0xFC,0x2E,0x17,0xFB,0xB9,0x96,0x48,0x4E,0x13, +0x01,0xCC,0x3C,0x1F,0xFC,0xFC,0x48,0x00,0x01,0xC8,0x5A,0x18,0x0D,0x04,0x48,0x00, +0x01,0xC4,0x5A,0x18,0x60,0x12,0x2E,0x17,0xFB,0xB9,0x5A,0x18,0x02,0x04,0x48,0x00, +0x01,0xB2,0x2E,0x17,0xFB,0xB9,0x5A,0x10,0x01,0x04,0x48,0x00,0x01,0xB6,0x44,0x00, +0x00,0x35,0x48,0x00,0x01,0xB2,0x5A,0x10,0xB7,0x04,0x48,0x00,0x01,0xAE,0x2E,0x17, +0xFB,0xB9,0x96,0x48,0x5A,0x18,0x01,0x08,0x3E,0x17,0xF4,0x21,0x44,0x00,0x00,0x44, +0x48,0x00,0x01,0xA3,0x2E,0x17,0xFB,0xB9,0x96,0x48,0x5A,0x18,0x02,0x08,0x3E,0x17, +0xF4,0x21,0x44,0x00,0x00,0x44,0x48,0x00,0x01,0x98,0x2E,0x17,0xFB,0xB9,0x5A,0x10, +0x03,0x04,0x48,0x00,0x01,0x92,0x44,0x00,0x00,0x45,0x48,0x00,0x01,0x8E,0x5A,0x10, +0x01,0x04,0x48,0x00,0x01,0x8A,0x2E,0x17,0xFB,0xB9,0x5A,0x10,0x0A,0x04,0x48,0x00, +0x01,0x84,0x2E,0x17,0xFB,0xBA,0x5A,0x18,0x08,0x13,0x5A,0x28,0x06,0x11,0x2E,0x37, +0xFB,0xBB,0x2E,0x07,0xFB,0xBC,0x2E,0x17,0xFB,0xBD,0x40,0x00,0x20,0x08,0x88,0x03, +0x40,0x10,0xC0,0x08,0x88,0x01,0x3C,0x0F,0xF8,0xED,0xFA,0x04,0x2E,0x17,0xFB,0xBA, +0x5A,0x18,0x09,0x19,0x5A,0x28,0x04,0x17,0x2E,0x17,0xFB,0xBB,0x5A,0x18,0x01,0x04, +0x48,0x00,0x01,0x5C,0x2E,0x17,0xFB,0xBB,0x5A,0x18,0x02,0x04,0x48,0x00,0x01,0x58, +0x2E,0x17,0xFB,0xBB,0x5A,0x10,0x03,0x04,0x48,0x00,0x01,0x57,0xFA,0x07,0x48,0x00, +0x01,0x54,0x2E,0x17,0xFB,0xBA,0x5A,0x18,0x10,0x33,0x5A,0x28,0x06,0x31,0x2E,0x17, +0xFB,0xBB,0x5A,0x18,0x01,0x0D,0x2E,0x27,0xFB,0xBC,0x2E,0x17,0xFB,0xBD,0x40,0x10, +0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF1,0xD9,0x48,0x00,0x01,0x3F,0x2E,0x17,0xFB,0xBB, +0x5A,0x18,0x02,0x0D,0x2E,0x27,0xFB,0xBC,0x2E,0x17,0xFB,0xBD,0x40,0x10,0xA0,0x08, +0x88,0x22,0x3C,0x1B,0xF1,0xD9,0x48,0x00,0x01,0x30,0x2E,0x17,0xFB,0xBB,0x5A,0x10, +0x03,0x04,0x48,0x00,0x01,0x2A,0x2E,0x27,0xFB,0xBC,0x2E,0x17,0xFB,0xBD,0x40,0x10, +0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF1,0xD8,0x48,0x00,0x01,0x1F,0x2E,0x17,0xFB,0xBA, +0x5A,0x18,0x11,0x19,0x5A,0x28,0x04,0x17,0x2E,0x17,0xFB,0xBB,0x5A,0x18,0x01,0x04, +0x48,0x00,0x01,0x10,0x2E,0x17,0xFB,0xBB,0x5A,0x18,0x02,0x04,0x48,0x00,0x01,0x0C, +0x2E,0x17,0xFB,0xBB,0x5A,0x10,0x03,0x04,0x48,0x00,0x01,0x07,0xFA,0x0A,0x48,0x00, +0x01,0x04,0x2E,0x17,0xFB,0xBA,0x5A,0x10,0x12,0x04,0x48,0x00,0x00,0xFE,0x5A,0x20, +0x19,0x04,0x48,0x00,0x00,0xFA,0x2E,0x17,0xFB,0xBB,0x5A,0x18,0x01,0x46,0x2E,0x17, +0xFB,0xBC,0x3E,0x17,0xE1,0xFC,0x2E,0x17,0xFB,0xBD,0x3E,0x17,0xE1,0xFE,0x2E,0x17, +0xFB,0xBE,0x3E,0x17,0xE1,0xFD,0x2E,0x17,0xFB,0xBF,0x2E,0x27,0xFB,0xC0,0x40,0x10, +0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF0,0xFD,0x2E,0x17,0xFB,0xC1,0x2E,0x27,0xFB,0xC2, +0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF0,0xFC,0x2E,0x17,0xFB,0xC3,0x2E,0x27, +0xFB,0xC4,0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF1,0x00,0x2E,0x17,0xFB,0xC5, +0x2E,0x27,0xFB,0xC6,0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF1,0x01,0x2E,0x17, +0xFB,0xC7,0x2E,0x27,0xFB,0xC8,0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF1,0x02, +0x2E,0x17,0xFB,0xC9,0x2E,0x27,0xFB,0xCA,0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B, +0xF1,0x03,0x48,0x00,0x00,0xB2,0x2E,0x17,0xFB,0xBB,0x5A,0x18,0x02,0x04,0x48,0x00, +0x00,0xAC,0x2E,0x17,0xFB,0xBB,0x5A,0x10,0x03,0x04,0x48,0x00,0x00,0xA6,0x2E,0x17, +0xFB,0xBC,0x3E,0x17,0xE1,0xF0,0x2E,0x17,0xFB,0xBD,0x3E,0x17,0xE1,0xF1,0x2E,0x17, +0xFB,0xBE,0x3E,0x17,0xE1,0xF2,0x2E,0x17,0xFB,0xBF,0x2E,0x27,0xFB,0xC0,0x40,0x10, +0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF0,0xF6,0x2E,0x17,0xFB,0xC1,0x2E,0x27,0xFB,0xC2, +0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF0,0xF7,0x2E,0x17,0xFB,0xC3,0x2E,0x27, +0xFB,0xC4,0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF0,0xF2,0x2E,0x17,0xFB,0xC5, +0x2E,0x27,0xFB,0xC6,0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF0,0xF3,0x2E,0x17, +0xFB,0xC7,0x2E,0x27,0xFB,0xC8,0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF0,0xF4, +0x2E,0x17,0xFB,0xC9,0x2E,0x27,0xFB,0xCA,0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B, +0xF0,0xF5,0x2E,0x17,0xFB,0xCB,0x3E,0x17,0xE1,0xF3,0x2E,0x17,0xFB,0xCC,0x3E,0x17, +0xE1,0xF4,0x2E,0x17,0xFB,0xCD,0x3E,0x17,0xE1,0xF5,0xD5,0x56,0x84,0x0A,0xD5,0x54, +0x84,0x09,0xD5,0x52,0x84,0x0B,0xD5,0x50,0x84,0x0C,0xD5,0x4E,0x84,0x0D,0xD5,0x4C, +0x84,0x0E,0xD5,0x4A,0xFA,0x00,0xD5,0x48,0xFA,0x01,0xD5,0x46,0xFA,0x02,0xD5,0x44, +0x84,0x02,0xD5,0x42,0x84,0x05,0xD5,0x40,0xFA,0x19,0xD5,0x3E,0xFA,0x1D,0xD5,0x3C, +0xFA,0x1A,0xD5,0x3A,0xFA,0x1B,0xD5,0x38,0xFA,0x1C,0xD5,0x36,0xFA,0x1E,0xD5,0x34, +0xFA,0x1F,0xD5,0x32,0x44,0x00,0x00,0x43,0xD5,0x2F,0x44,0x00,0x00,0x30,0xD5,0x2C, +0x44,0x00,0x00,0x40,0xD5,0x29,0x44,0x00,0x00,0x41,0xD5,0x26,0x84,0x03,0xD5,0x24, +0xFA,0x0B,0xD5,0x22,0x84,0x04,0xD5,0x20,0x84,0x0A,0xD5,0x1E,0x84,0x09,0xD5,0x1C, +0x44,0x00,0x00,0x32,0xD5,0x19,0x84,0x0B,0xD5,0x17,0x84,0x0C,0xD5,0x15,0x84,0x0D, +0xD5,0x13,0x84,0x0E,0xD5,0x11,0xFA,0x00,0xD5,0x0F,0xFA,0x01,0xD5,0x0D,0xFA,0x02, +0xD5,0x0B,0x44,0x00,0x00,0x34,0xD5,0x08,0xFA,0x05,0xD5,0x06,0xFA,0x06,0xD5,0x04, +0xFA,0x08,0xD5,0x02,0xFA,0x09,0x84,0x20,0x3C,0x1F,0xF8,0xB4,0x5A,0x08,0x01,0x1A, +0x44,0x0F,0xFF,0xA7,0x3E,0x07,0xF6,0x40,0x84,0x05,0x3E,0x07,0xF6,0x41,0xFA,0x0C, +0x3E,0x07,0xF6,0x42,0xFA,0x22,0xFA,0x10,0x3E,0x07,0xF6,0x43,0x3E,0x07,0xF6,0x44, +0x3E,0x17,0xF6,0x45,0x3E,0x07,0xF6,0x46,0x3E,0x17,0xF6,0x47,0x84,0x08,0xD5,0x3B, +0x5A,0x08,0x02,0x2E,0x2E,0x07,0xFC,0x49,0x44,0x1F,0xFF,0x81,0x3E,0x17,0xF6,0x40, +0x3E,0x07,0xF6,0x41,0xC8,0x0C,0x44,0x10,0x00,0x5A,0x3E,0x17,0xF6,0x42,0x3E,0x07, +0xF6,0x43,0xFA,0x1B,0x3E,0x07,0xF6,0x44,0x84,0x05,0xD5,0x25,0x44,0x0F,0xFF,0xA7, +0x3E,0x07,0xF6,0x42,0x84,0x05,0x3E,0x07,0xF6,0x43,0xFA,0x0C,0x3E,0x07,0xF6,0x44, +0xFA,0x22,0xFA,0x10,0x3E,0x07,0xF6,0x45,0x3E,0x17,0xF6,0x46,0x3E,0x07,0xF6,0x47, +0x3E,0x17,0xF6,0x48,0x3E,0x07,0xF6,0x49,0x84,0x0A,0xD5,0x0D,0x5A,0x08,0x05,0x12, +0x84,0x01,0x3E,0x07,0xF6,0x40,0x3E,0x17,0xF6,0x41,0x3E,0x17,0xF6,0x42,0x3E,0x07, +0xF6,0x43,0x84,0x04,0x49,0xFF,0xF9,0x0D,0x49,0xFF,0xE7,0x53,0x48,0x00,0x03,0xAF, +0x5A,0x08,0x29,0x25,0xFA,0x10,0x84,0x7F,0x84,0x47,0x3E,0x07,0xF6,0x40,0x3E,0x17, +0xF6,0x41,0x3E,0x17,0xF6,0x42,0x3E,0x37,0xF6,0x43,0x3E,0x27,0xF6,0x44,0x3E,0x37, +0xF6,0x45,0x3E,0x27,0xF6,0x46,0x84,0x6A,0xFA,0x42,0x3E,0x27,0xF6,0x47,0x3E,0x07, +0xF6,0x48,0x3E,0x37,0xF6,0x49,0x3E,0x17,0xF6,0x4A,0x3E,0x27,0xF6,0x4B,0x3E,0x07, +0xF6,0x4C,0x3E,0x17,0xF6,0x4D,0x84,0x0E,0xD5,0xD6,0x5A,0x08,0x2A,0x1C,0xFA,0x11, +0x3E,0x07,0xF6,0x40,0x44,0x2F,0xFF,0x81,0x84,0x08,0xFA,0x6A,0x3E,0x27,0xF6,0x41, +0x3E,0x07,0xF6,0x42,0x3E,0x17,0xF6,0x43,0x3E,0x37,0xF6,0x44,0x3E,0x27,0xF6,0x45, +0x3E,0x07,0xF6,0x46,0x84,0x01,0x3E,0x17,0xF6,0x47,0x3E,0x07,0xF6,0x48,0x84,0x09, +0xD5,0xBA,0x5A,0x08,0x2B,0x0E,0xFA,0x12,0x3E,0x07,0xF6,0x40,0x84,0x05,0x3E,0x07, +0xF6,0x41,0x84,0x04,0x3E,0x07,0xF6,0x42,0x3E,0x17,0xF6,0x43,0xD5,0xAB,0x5A,0x08, +0x43,0x05,0x2E,0x00,0x0A,0xD4,0xD5,0x5E,0x5A,0x08,0x2C,0x0C,0xFA,0x13,0x3E,0x07, +0xF6,0x40,0x84,0x01,0x3E,0x07,0xF6,0x41,0x3E,0x17,0xF6,0x42,0x84,0x06,0xD5,0x98, +0x5A,0x08,0x2E,0x06,0x3E,0x17,0xF6,0x40,0x84,0x01,0xD5,0x95,0x5A,0x08,0x2F,0x04, +0x84,0x00,0xD5,0x48,0x5A,0x08,0x2D,0x2F,0xFA,0x18,0x3E,0x07,0xF6,0x40,0x84,0x62, +0x84,0x00,0x84,0x26,0x84,0x4C,0x3E,0x37,0xF6,0x41,0x3E,0x07,0xF6,0x42,0x3E,0x17, +0xF6,0x43,0x3E,0x27,0xF6,0x44,0x3E,0x37,0xF6,0x45,0x3E,0x07,0xF6,0x46,0x84,0x64, +0x3E,0x17,0xF6,0x47,0x3E,0x27,0xF6,0x48,0x84,0x41,0x3E,0x27,0xF6,0x49,0x3E,0x07, +0xF6,0x4A,0x3E,0x37,0xF6,0x4B,0x3E,0x37,0xF6,0x4C,0x3E,0x27,0xF6,0x4D,0x3E,0x07, +0xF6,0x4E,0x84,0x03,0x3E,0x07,0xF6,0x4F,0x3E,0x17,0xF6,0x50,0xFA,0x01,0x48,0xFF, +0xFF,0x63,0x5A,0x08,0x1B,0x0A,0x84,0x01,0x3E,0x07,0xFC,0x49,0x84,0x01,0x49,0xFF, +0xE4,0x8A,0x48,0x00,0x03,0x0C,0x5A,0x08,0x30,0x11,0x49,0xFF,0xE4,0x81,0x5A,0x08, +0x01,0x05,0x44,0x00,0x00,0x40,0xD5,0x06,0x49,0xFF,0xE4,0x7A,0xC8,0xB6,0x44,0x00, +0x00,0x41,0x3E,0x07,0xF6,0x40,0xD5,0xB1,0x5A,0x08,0x36,0x06,0x49,0xFF,0xFA,0x47, +0x48,0x00,0x02,0xF5,0x5A,0x08,0x03,0x04,0x84,0x00,0xD5,0x04,0x5A,0x08,0x04,0x07, +0x84,0x02,0x3E,0x07,0xFC,0x49,0x48,0x00,0x02,0xEA,0x5A,0x08,0x06,0x07,0x84,0x01, +0x3E,0x07,0xF6,0x2F,0x48,0x00,0x02,0xE3,0x5A,0x08,0x07,0x07,0x84,0x01,0x3E,0x07, +0xF6,0x2E,0x48,0x00,0x02,0xDC,0x5A,0x08,0x08,0x07,0x84,0x01,0x3E,0x07,0xF6,0x2D, +0x48,0x00,0x02,0xD5,0x5A,0x08,0x09,0x07,0x84,0x01,0x3E,0x07,0xF6,0x2C,0x48,0x00, +0x02,0xCE,0x5A,0x08,0x0A,0x07,0x84,0x01,0x3E,0x07,0xF6,0x2B,0x48,0x00,0x02,0xC7, +0x5A,0x08,0x0B,0x06,0x84,0x01,0x3E,0x07,0xF6,0x29,0xD5,0x0F,0x5A,0x08,0x0C,0x09, +0x84,0x01,0x3E,0x07,0xF6,0x2A,0x3E,0x07,0xF6,0x28,0x48,0x00,0x02,0xB8,0x5A,0x08, +0x0D,0x07,0x84,0x01,0x3E,0x07,0xF6,0x2A,0x84,0x00,0xD5,0xF6,0x5A,0x08,0x32,0x04, +0x84,0x01,0xD5,0x04,0x5A,0x08,0x33,0x07,0x84,0x02,0x3E,0x07,0xE3,0xB8,0x48,0x00, +0x02,0xA6,0x5A,0x08,0x3C,0x35,0x2E,0x57,0xFB,0xB9,0x2E,0x47,0xFB,0xBB,0x2E,0x07, +0xFB,0xBA,0x40,0x42,0x20,0x08,0x2E,0x37,0xFB,0xBD,0x2E,0x27,0xFB,0xBC,0xFF,0x07, +0x84,0x26,0x97,0x68,0x96,0xD8,0x96,0x90,0x84,0x00,0x99,0xE1,0xE0,0xE1,0xE9,0x08, +0x8C,0x21,0x38,0x93,0x04,0x00,0x96,0x49,0x88,0x09,0x96,0x00,0xD5,0xF8,0x4E,0x03, +0x02,0x86,0x5A,0x50,0x02,0x04,0x48,0x00,0x02,0x82,0x40,0x31,0xA0,0x08,0xFE,0x9F, +0x44,0x12,0x10,0x00,0x88,0x41,0x96,0x41,0xE2,0x24,0x4E,0xF2,0x02,0x78,0x9C,0x47, +0x38,0x13,0x04,0x00,0x38,0x11,0x00,0x08,0x8C,0x01,0xD5,0xF6,0x5A,0x08,0x3D,0x29, +0x2E,0x07,0xFB,0xBA,0x2E,0x17,0xFB,0xB9,0x40,0x00,0x20,0x08,0x2E,0x27,0xFB,0xBC, +0xFE,0x0F,0x2E,0x17,0xFB,0xBB,0x40,0x21,0x20,0x08,0xFE,0x8F,0x2E,0x17,0xFB,0xB8, +0x44,0x32,0x10,0x00,0x96,0x48,0x3E,0x17,0xF6,0x40,0x88,0x43,0x84,0x20,0x44,0x42, +0x18,0x30,0x96,0xC9,0xE2,0x60,0xE8,0x08,0x9C,0xC9,0x38,0x11,0x04,0x00,0x38,0x12, +0x0C,0x08,0x80,0x23,0xD5,0xF7,0x8C,0x01,0x96,0x01,0x48,0xFF,0xFE,0x95,0x5A,0x08, +0x3E,0x31,0x2E,0x07,0xFB,0xB9,0x5A,0x00,0x5A,0x04,0x48,0x00,0x02,0x40,0x2E,0x07, +0xFB,0xBA,0x5A,0x00,0xA5,0x04,0x48,0x00,0x02,0x3A,0xFA,0x00,0x49,0xFF,0xD9,0xA3, +0x44,0x11,0x00,0x00,0x44,0x20,0x01,0x00,0x44,0x02,0x10,0x00,0x49,0xFF,0xD9,0xC4, +0x49,0xFF,0xDA,0x3E,0x40,0x10,0x60,0x09,0x10,0x1F,0x80,0x04,0x40,0x10,0x40,0x09, +0x10,0x1F,0x80,0x05,0x40,0x10,0x20,0x09,0x10,0x1F,0x80,0x06,0x10,0x0F,0x80,0x07, +0x44,0x11,0x0F,0xFC,0xB0,0x01,0x84,0x44,0x49,0xFF,0xD9,0xAE,0x48,0x00,0x02,0x17, +0x5A,0x08,0x3F,0x22,0x2E,0x07,0xFB,0xB9,0x5A,0x08,0x01,0x12,0x2E,0x07,0xFB,0xBA, +0x96,0x00,0x5A,0x00,0x01,0x08,0x5A,0x00,0x02,0x04,0x48,0x00,0x02,0x08,0x84,0x04, +0xD5,0x02,0x84,0x02,0x3E,0x07,0xE2,0xCD,0x48,0x00,0x02,0x01,0x44,0x00,0x00,0x35, +0x3E,0x07,0xF6,0x40,0x2E,0x07,0xE2,0xCD,0x96,0x00,0x3E,0x07,0xF6,0x41,0x84,0x02, +0x48,0xFF,0xFE,0x42,0x5A,0x08,0x0E,0x0D,0x2E,0x07,0xEF,0xAA,0x58,0x00,0x00,0x01, +0x3E,0x07,0xEF,0xAA,0x2E,0x07,0xEF,0xAA,0x58,0x00,0x00,0x04,0xD5,0x0D,0x5A,0x08, +0x10,0x13,0x2E,0x07,0xEF,0xAA,0x58,0x00,0x00,0x01,0x3E,0x07,0xEF,0xAA,0x2E,0x07, +0xEF,0xAA,0x58,0x00,0x00,0x02,0x3E,0x07,0xEF,0xAA,0x2E,0x07,0xEF,0xAA,0x54,0x00, +0x00,0xF7,0xD5,0x15,0x5A,0x08,0x11,0x0E,0x2E,0x07,0xEF,0xAB,0x5A,0x08,0x01,0x06, +0x44,0x0F,0xFF,0x91,0x48,0xFF,0xFE,0xCF,0x44,0x0F,0xFF,0x90,0x48,0xFF,0xFE,0xCB, +0x5A,0x08,0x12,0x0A,0x2E,0x07,0xEF,0xAA,0x58,0x00,0x00,0x08,0x3E,0x07,0xEF,0xAA, +0x48,0x00,0x01,0xBD,0x5A,0x08,0x13,0x77,0x3C,0x0D,0xB7,0x75,0x46,0x10,0x0F,0xF0, +0x92,0x18,0x3E,0x07,0xF6,0x40,0x3C,0x0D,0xB7,0x75,0xFE,0x0E,0x92,0x10,0x3E,0x07, +0xF6,0x41,0x3C,0x2D,0xB7,0x75,0x44,0x00,0xFF,0x00,0xFE,0x86,0x92,0x48,0x3E,0x27, +0xF6,0x42,0x3C,0x2D,0xB7,0x75,0x96,0x90,0x3E,0x27,0xF6,0x43,0x3C,0x2D,0xB7,0x76, +0x92,0x58,0x3E,0x27,0xF6,0x44,0x3C,0x2D,0xB7,0x76,0xFE,0x8E,0x92,0x50,0x3E,0x27, +0xF6,0x45,0x3C,0x2D,0xB7,0x76,0xFE,0x86,0x92,0x48,0x3E,0x27,0xF6,0x46,0x3C,0x2D, +0xB7,0x76,0x96,0x90,0x3E,0x27,0xF6,0x47,0x3C,0x2D,0xB7,0x77,0x92,0x58,0x3E,0x27, +0xF6,0x48,0x3C,0x2D,0xB7,0x77,0xFE,0x8E,0x92,0x50,0x3E,0x27,0xF6,0x49,0x3C,0x2D, +0xB7,0x77,0xFE,0x86,0x92,0x48,0x3E,0x27,0xF6,0x4A,0x3C,0x2D,0xB7,0x77,0x96,0x90, +0x3E,0x27,0xF6,0x4B,0x3C,0x2D,0xB7,0x78,0x92,0x58,0x3E,0x27,0xF6,0x4C,0x3C,0x2D, +0xB7,0x78,0xFE,0x8E,0x92,0x50,0x3E,0x27,0xF6,0x4D,0x3C,0x2D,0xB7,0x78,0xFE,0x86, +0x92,0x48,0x3E,0x27,0xF6,0x4E,0x3C,0x2D,0xB7,0x78,0x96,0x90,0x3E,0x27,0xF6,0x4F, +0x3C,0x2D,0xB7,0x79,0x92,0x58,0x3E,0x27,0xF6,0x50,0x3C,0x2D,0xB7,0x79,0xFE,0x56, +0x92,0x30,0x3E,0x17,0xF6,0x51,0x3C,0x1D,0xB7,0x79,0xFE,0x0E,0x92,0x08,0x3E,0x07, +0xF6,0x52,0x3C,0x0D,0xB7,0x79,0x96,0x00,0x3E,0x07,0xF6,0x53,0xFA,0x04,0x48,0xFF, +0xFD,0x93,0x5A,0x08,0x15,0x07,0x84,0x09,0x3E,0x07,0xF6,0x40,0x84,0x01,0xD5,0x07, +0x5A,0x08,0x16,0x0B,0x84,0x09,0x3E,0x07,0xF6,0x40,0x84,0x02,0x3E,0x07,0xF6,0x41, +0x2E,0x07,0xF6,0x3B,0xD5,0x0B,0x5A,0x08,0x17,0x10,0x84,0x09,0x3E,0x07,0xF6,0x40, +0x84,0x03,0x3E,0x07,0xF6,0x41,0x2E,0x07,0xF6,0x39,0x96,0x1F,0x3E,0x07,0xF6,0x42, +0x84,0x03,0x48,0xFF,0xFD,0x71,0x5A,0x08,0x18,0x5B,0xFA,0x21,0x3E,0x17,0xF6,0x40, +0x84,0x21,0x3E,0x17,0xF6,0x41,0x2E,0x27,0xE1,0xFC,0x84,0x20,0x3E,0x17,0xF6,0x42, +0x3E,0x07,0xF6,0x43,0x3E,0x27,0xF6,0x44,0x2E,0x27,0xE1,0xFE,0x3E,0x27,0xF6,0x45, +0x2E,0x27,0xE1,0xFD,0x3E,0x27,0xF6,0x46,0x3C,0x23,0xF0,0xFD,0x40,0x31,0x20,0x09, +0x96,0x90,0x3E,0x37,0xF6,0x47,0x3E,0x27,0xF6,0x48,0x3C,0x23,0xF0,0xFC,0x40,0x31, +0x20,0x09,0x96,0x90,0x3E,0x37,0xF6,0x49,0x3E,0x27,0xF6,0x4A,0x3C,0x23,0xF1,0x00, +0x40,0x31,0x20,0x09,0x96,0x90,0x3E,0x37,0xF6,0x4B,0x3E,0x27,0xF6,0x4C,0x3C,0x23, +0xF1,0x01,0x40,0x31,0x20,0x09,0x96,0x90,0x3E,0x37,0xF6,0x4D,0x3E,0x27,0xF6,0x4E, +0x3C,0x23,0xF1,0x02,0x40,0x31,0x20,0x09,0x96,0x90,0x3E,0x37,0xF6,0x4F,0x3E,0x27, +0xF6,0x50,0x3C,0x23,0xF1,0x03,0x40,0x31,0x20,0x09,0x96,0x90,0x3E,0x37,0xF6,0x51, +0x3E,0x27,0xF6,0x52,0x3E,0x17,0xF6,0x53,0x3E,0x17,0xF6,0x54,0x3E,0x17,0xF6,0x55, +0x3E,0x17,0xF6,0x56,0x3E,0x17,0xF6,0x57,0x48,0xFF,0xFD,0x16,0x5A,0x08,0x19,0x39, +0xFA,0x01,0x3E,0x07,0xF6,0x40,0x84,0x02,0x3E,0x07,0xF6,0x41,0xFA,0x28,0x84,0x00, +0x3E,0x07,0xF6,0x42,0x3E,0x17,0xF6,0x43,0x3E,0x07,0xF6,0x44,0x3E,0x07,0xF6,0x45, +0x3E,0x07,0xF6,0x46,0x3E,0x07,0xF6,0x47,0x3E,0x07,0xF6,0x48,0x3E,0x07,0xF6,0x49, +0x3E,0x07,0xF6,0x4A,0x3E,0x07,0xF6,0x4B,0x3E,0x07,0xF6,0x4C,0x3E,0x07,0xF6,0x4D, +0x3E,0x07,0xF6,0x4E,0x3E,0x07,0xF6,0x4F,0x3E,0x07,0xF6,0x50,0x3E,0x07,0xF6,0x51, +0x3E,0x07,0xF6,0x52,0x3E,0x07,0xF6,0x53,0x3E,0x07,0xF6,0x54,0x3E,0x07,0xF6,0x55, +0x3E,0x07,0xF6,0x56,0x3E,0x07,0xF6,0x57,0xFA,0x08,0x48,0xFF,0xFC,0xDD,0x5A,0x08, +0x1A,0x63,0xFA,0x01,0x3E,0x07,0xF6,0x40,0x84,0x03,0x3E,0x07,0xF6,0x41,0x84,0x20, +0xFA,0x08,0x3E,0x17,0xF6,0x42,0x3E,0x07,0xF6,0x43,0x2E,0x07,0xE1,0xF0,0x3E,0x07, +0xF6,0x44,0x2E,0x07,0xE1,0xF1,0x3E,0x07,0xF6,0x45,0x2E,0x07,0xE1,0xF2,0x3E,0x07, +0xF6,0x46,0x3C,0x03,0xF0,0xF6,0x40,0x20,0x20,0x09,0x96,0x00,0x3E,0x27,0xF6,0x47, +0x3E,0x07,0xF6,0x48,0x3C,0x03,0xF0,0xF7,0x40,0x20,0x20,0x09,0x96,0x00,0x3E,0x27, +0xF6,0x49,0x3E,0x07,0xF6,0x4A,0x3C,0x03,0xF0,0xF2,0x40,0x20,0x20,0x09,0x96,0x00, +0x3E,0x27,0xF6,0x4B,0x3E,0x07,0xF6,0x4C,0x3C,0x03,0xF0,0xF3,0x40,0x20,0x20,0x09, +0x96,0x00,0x3E,0x27,0xF6,0x4D,0x3E,0x07,0xF6,0x4E,0x3C,0x03,0xF0,0xF4,0x40,0x20, +0x20,0x09,0x96,0x00,0x3E,0x27,0xF6,0x4F,0x3E,0x07,0xF6,0x50,0x3C,0x03,0xF0,0xF5, +0x40,0x20,0x20,0x09,0x96,0x00,0x3E,0x27,0xF6,0x51,0x3E,0x07,0xF6,0x52,0x2E,0x07, +0xE1,0xF3,0x3E,0x07,0xF6,0x53,0x2E,0x07,0xE1,0xF4,0x3E,0x07,0xF6,0x54,0x2E,0x07, +0xE1,0xF5,0x3E,0x07,0xF6,0x55,0x3E,0x17,0xF6,0x56,0xFA,0x07,0x3E,0x17,0xF6,0x57, +0x48,0xFF,0xFC,0x7A,0x5A,0x08,0x31,0x1A,0x84,0x1E,0x3E,0x07,0xF6,0x40,0x84,0x01, +0x3E,0x07,0xF6,0x41,0x84,0x02,0x3E,0x07,0xF6,0x42,0x84,0x03,0x3E,0x07,0xF6,0x43, +0x84,0x04,0x3E,0x07,0xF6,0x44,0x84,0x05,0x3E,0x07,0xF6,0x45,0x84,0x06,0x3E,0x07, +0xF6,0x46,0x84,0x07,0x48,0xFF,0xFC,0x60,0x5A,0x08,0x44,0x0C,0x84,0x00,0x49,0xFF, +0xE4,0x20,0x84,0x01,0x3C,0x0F,0xFF,0x13,0x84,0x03,0x3C,0x0F,0xFD,0x29,0xD5,0x06, +0x5A,0x08,0x45,0x05,0x84,0x00,0x3C,0x0F,0xFF,0x13,0x84,0x01,0x3C,0x0F,0xF8,0xB4, +0xFC,0xC1,0x5A,0x20,0x03,0x04,0x48,0xFF,0xF8,0xF0,0x84,0x00,0x3C,0x0F,0xF8,0xB4, +0x48,0xFF,0xFD,0x53,0x5A,0x28,0x03,0x07,0x84,0x00,0x3C,0x0F,0xF8,0xB4,0x48,0xFF, +0xFD,0xDB,0x44,0x00,0x00,0x3F,0x48,0xFF,0xF8,0xE2,0x5A,0x28,0x03,0x06,0x3C,0x0F, +0xF8,0xB4,0x48,0xFF,0xFD,0xA0,0x44,0x00,0x00,0x3E,0x48,0xFF,0xF8,0xD8,0x5A,0x28, +0x03,0x07,0x84,0x00,0x3C,0x0F,0xF8,0xB4,0x48,0xFF,0xFD,0x6C,0x44,0x00,0x00,0x3D, +0x48,0xFF,0xF8,0xCD,0xFC,0x00,0x44,0x04,0x10,0x04,0x44,0x10,0x00,0x55,0xAE,0x40, +0x44,0x1F,0xFF,0xAA,0xAE,0x41,0x44,0x10,0x00,0x66,0xAE,0x42,0x84,0x01,0x49,0xFF, +0xB5,0x40,0xFC,0x80,0x44,0x14,0x10,0x08,0xB6,0x01,0x44,0x14,0x80,0x04,0xA6,0x0B, +0x92,0x01,0x96,0x06,0xC0,0xFD,0x44,0x04,0x80,0x07,0x84,0x22,0xAE,0x40,0xDD,0x9E, +0xFC,0x40,0x44,0x37,0x20,0xC0,0x46,0x10,0x20,0x00,0xB4,0x43,0x44,0x47,0x20,0xC4, +0xFE,0x57,0xB6,0x23,0xB4,0x24,0x44,0x24,0x10,0x10,0x92,0x34,0x40,0x10,0xD0,0x08, +0xFE,0x57,0xB6,0x24,0x44,0x17,0x20,0xC8,0x40,0x00,0x30,0x08,0xB4,0x41,0x84,0xC1, +0x92,0x48,0x40,0x21,0x20,0x08,0xB6,0x41,0xB4,0x81,0x46,0x2F,0xCF,0xFF,0x50,0x21, +0x0F,0xFF,0xFF,0x16,0xB6,0x81,0xB4,0xA1,0x46,0x48,0x00,0x00,0xFF,0x67,0xB6,0xA1, +0x44,0x57,0x20,0xD0,0x44,0x70,0xA0,0x00,0xB4,0x25,0x44,0x94,0x10,0x00,0x94,0x49, +0x92,0x21,0xB6,0x25,0x44,0x57,0x20,0xD4,0x40,0x10,0x30,0x09,0xB4,0x05,0x92,0x14, +0x40,0x00,0x50,0x08,0xFE,0x0F,0xB6,0x05,0x44,0x07,0x20,0xD8,0xB4,0x20,0x92,0x28, +0x40,0x10,0xA0,0x08,0xFE,0x77,0xB6,0x20,0xB4,0x20,0xFE,0x8E,0xB6,0x40,0xB4,0x20, +0x44,0x27,0x20,0xDC,0xFF,0x0F,0xB6,0x80,0xB4,0x22,0x46,0x0F,0x0F,0xFF,0x92,0x31, +0x40,0x10,0xC4,0x08,0xFE,0x7F,0xB6,0x22,0xB4,0x22,0x50,0x00,0x0F,0xFF,0xFE,0x0E, +0x44,0x14,0x80,0x84,0xB6,0x02,0xB4,0x41,0x44,0x0D,0xFF,0xFF,0xFE,0x16,0xB6,0x01, +0xB4,0x23,0x46,0x0F,0xFF,0x0F,0x50,0x00,0x0F,0xFF,0xFE,0x0E,0x44,0x11,0x00,0x00, +0xFE,0x0F,0xB6,0x03,0x84,0x00,0x10,0x64,0x80,0x02,0x10,0x04,0x80,0x00,0x49,0xFF, +0xFF,0x73,0x84,0x0B,0x49,0xFF,0xFF,0x80,0x80,0x06,0x49,0xFF,0xFF,0x7D,0xFA,0x10, +0x49,0xFF,0xFF,0x7A,0x84,0x00,0x49,0xFF,0xFF,0x77,0x00,0x04,0x80,0x03,0x54,0x00, +0x00,0xFE,0x10,0x04,0x80,0x03,0x84,0x00,0x49,0xFF,0xFF,0x6E,0x44,0x04,0x10,0x0C, +0x44,0x14,0x80,0x07,0xB6,0xE0,0x84,0x02,0xAE,0x08,0x44,0x14,0x80,0x06,0xAE,0x08, +0x44,0x04,0x10,0x13,0xAF,0x80,0xFC,0xC0,0xFC,0x00,0x49,0xFF,0xE0,0x99,0x5A,0x08, +0x01,0x0B,0x84,0x00,0x49,0xFF,0xFF,0x66,0x49,0xFF,0xB5,0xA4,0x84,0x00,0x49,0xFF, +0xE0,0x92,0xD5,0x00,0xFC,0x80,0xC0,0x06,0x5A,0x08,0x01,0x08,0x44,0x00,0x00,0x60, +0xDD,0x9E,0x44,0x00,0x00,0x58,0xDD,0x9E,0x44,0x00,0x00,0xFF,0xDD,0x9E,0xFC,0x01, +0x2E,0x17,0xF6,0x3B,0xC9,0x0A,0x2E,0x17,0xF6,0x3A,0xC9,0x0A,0x2E,0x17,0xF6,0x39, +0xC1,0x0E,0x44,0x0F,0xFF,0xAD,0xD5,0x06,0x44,0x0F,0xFF,0xAB,0xD5,0x03,0x44,0x0F, +0xFF,0xAC,0x10,0x0F,0x80,0x00,0x10,0x1F,0x80,0x01,0xD5,0x07,0x44,0x1F,0xFF,0xAA, +0x10,0x1F,0x80,0x00,0x10,0x0F,0x80,0x01,0x84,0x00,0x84,0x28,0x10,0x0F,0x80,0x02, +0x10,0x1F,0x80,0x03,0x10,0x0F,0x80,0x04,0x10,0x0F,0x80,0x05,0x10,0x0F,0x80,0x06, +0x84,0x27,0x80,0x1F,0x49,0xFF,0xF2,0xD0,0xFC,0x81,0xFC,0x36,0x2E,0x17,0xF6,0x3B, +0xC9,0x0A,0x2E,0x17,0xF6,0x3A,0xC9,0x0A,0x2E,0x17,0xF6,0x39,0xC1,0x0E,0x44,0x0F, +0xFF,0xAD,0xD5,0x06,0x44,0x0F,0xFF,0xAB,0xD5,0x03,0x44,0x0F,0xFF,0xAC,0x10,0x0F, +0x80,0x04,0x10,0x1F,0x80,0x05,0xD5,0x07,0x44,0x1F,0xFF,0xAA,0x10,0x1F,0x80,0x04, +0x10,0x0F,0x80,0x05,0x3C,0x37,0xF7,0x9F,0x84,0x00,0x10,0x0F,0x80,0x06,0x44,0x0F, +0xFF,0xAA,0x3C,0x27,0xF7,0x99,0x10,0x0F,0x80,0x07,0x40,0x01,0xA0,0x0A,0x96,0x01, +0x94,0x44,0x40,0x01,0x20,0x0A,0x10,0x3F,0x80,0x09,0x96,0x1F,0x3C,0x37,0xF7,0x94, +0xFE,0x0F,0x10,0x0F,0x80,0x08,0x10,0x2F,0x80,0x0A,0x40,0x01,0xA0,0x0A,0x3C,0x27, +0xF7,0xA5,0x96,0x01,0x94,0x44,0x40,0x01,0x20,0x0A,0x10,0x3F,0x80,0x0C,0x96,0x1F, +0x3C,0x37,0xF7,0x9B,0xFE,0x0F,0x10,0x0F,0x80,0x0B,0x10,0x2F,0x80,0x0D,0x40,0x01, +0xA0,0x0A,0x3C,0x27,0xF7,0x9D,0x96,0x01,0x94,0x44,0x40,0x01,0x20,0x0A,0x10,0x3F, +0x80,0x0F,0x96,0x1F,0x3C,0x37,0xF7,0x9A,0xFE,0x0F,0x10,0x0F,0x80,0x0E,0x10,0x2F, +0x80,0x10,0x40,0x01,0xA0,0x0A,0x3C,0x27,0xF7,0xA2,0x96,0x01,0x94,0x44,0x40,0x01, +0x20,0x0A,0x10,0x3F,0x80,0x12,0x96,0x1F,0x3C,0x37,0xF7,0xB2,0xFE,0x0F,0x10,0x0F, +0x80,0x11,0x10,0x2F,0x80,0x13,0x40,0x01,0xA0,0x0A,0x3C,0x27,0xF7,0xB6,0x96,0x01, +0x94,0x44,0x40,0x01,0x20,0x0A,0x10,0x3F,0x80,0x15,0x96,0x1F,0x3C,0x37,0xF7,0xB3, +0xFE,0x0F,0x10,0x0F,0x80,0x14,0x10,0x2F,0x80,0x16,0x40,0x01,0xA0,0x0A,0x3C,0x27, +0xF7,0xB7,0x96,0x01,0x94,0x44,0x40,0x01,0x20,0x0A,0x10,0x3F,0x80,0x18,0x96,0x1F, +0x3C,0x37,0xF7,0xB4,0xFE,0x0F,0x10,0x0F,0x80,0x17,0x10,0x2F,0x80,0x19,0x40,0x01, +0xA0,0x0A,0x3C,0x27,0xF7,0xB8,0x96,0x01,0x94,0x44,0x40,0x01,0x20,0x0A,0x96,0x1F, +0x10,0x3F,0x80,0x1B,0x3C,0x37,0xF7,0xB5,0xFE,0x0F,0x10,0x0F,0x80,0x1A,0x10,0x2F, +0x80,0x1C,0x40,0x01,0xA0,0x0A,0x3C,0x27,0xF7,0xB9,0x96,0x01,0x94,0x44,0x40,0x01, +0x20,0x0A,0x96,0x1F,0x10,0x3F,0x80,0x1E,0x3C,0x37,0xF7,0x9C,0xFE,0x0F,0x10,0x0F, +0x80,0x1D,0x10,0x2F,0x80,0x1F,0x40,0x01,0xA0,0x0A,0x3C,0x27,0xF7,0xA3,0x96,0x01, +0x94,0x44,0x40,0x01,0x20,0x0A,0x96,0x1F,0x10,0x3F,0x80,0x21,0x3C,0x37,0xF7,0xA0, +0xFE,0x0F,0x10,0x0F,0x80,0x20,0x10,0x2F,0x80,0x22,0x40,0x01,0xA0,0x0A,0x3C,0x27, +0xF7,0x95,0x96,0x01,0x94,0x44,0x40,0x01,0x20,0x0A,0x96,0x1F,0xFE,0x0F,0x10,0x0F, +0x80,0x23,0x2E,0x07,0xEF,0x12,0x10,0x3F,0x80,0x24,0x10,0x0F,0x80,0x26,0x2E,0x07, +0xEF,0x13,0x3C,0x37,0xF7,0x98,0x10,0x0F,0x80,0x27,0x2E,0x07,0xEF,0x11,0x10,0x2F, +0x80,0x25,0x10,0x0F,0x80,0x28,0x3C,0x27,0xF7,0x9E,0x40,0x01,0xA0,0x0A,0x96,0x01, +0x94,0x44,0x40,0x01,0x20,0x0A,0x96,0x1F,0xFE,0x0F,0x10,0x0F,0x80,0x29,0x10,0x3F, +0x80,0x2A,0x10,0x2F,0x80,0x2B,0xB0,0x41,0x84,0x00,0x44,0x72,0x2D,0x50,0x44,0x62, +0x2D,0xAC,0x38,0x53,0x81,0x11,0x38,0x43,0x01,0x11,0x40,0x22,0xA0,0x0A,0x96,0x91, +0x94,0xD4,0x40,0x22,0x20,0x0A,0x96,0x9F,0xFE,0x9F,0x8C,0x01,0x10,0x20,0x80,0x28, +0x10,0x50,0x80,0x29,0x10,0x40,0x80,0x2A,0x8C,0x23,0x5A,0x08,0x28,0xEC,0x3C,0x03, +0xF7,0x96,0x40,0x10,0x20,0x09,0x10,0x0F,0x80,0xA5,0x84,0x00,0x10,0x1F,0x80,0xA4, +0x10,0x0F,0x80,0xA6,0x10,0x0F,0x80,0xA7,0x10,0x0F,0x80,0xA8,0x10,0x0F,0x80,0xA9, +0x10,0x0F,0x80,0xAA,0x10,0x0F,0x80,0xAB,0x10,0x0F,0x80,0xAC,0x44,0x10,0x00,0xA9, +0xB0,0x01,0x49,0xFF,0xF1,0xA1,0xFC,0xB6,0xFC,0x00,0x44,0x00,0x00,0x64,0x49,0xFF, +0xA7,0xF2,0x44,0x00,0x23,0x56,0x49,0xFF,0xA7,0xF9,0x44,0x00,0x1F,0x57,0x49,0xFF, +0xA8,0x00,0x44,0x00,0x01,0x90,0x49,0xFF,0xA8,0x07,0xFC,0x80,0xFC,0x40,0x51,0xFF, +0xFA,0xE0,0x84,0x20,0xFA,0x50,0xB0,0x10,0x49,0x00,0x10,0x86,0xB1,0x98,0x44,0x02, +0x02,0x24,0x80,0x26,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24,0x3A,0x20,0x14,0x00, +0xB1,0xE0,0x44,0x02,0x02,0x44,0x3A,0x20,0x94,0x20,0x80,0x27,0x3A,0x20,0x14,0x04, +0x3A,0x20,0x94,0x24,0x3A,0x20,0x14,0x00,0x3A,0x20,0x94,0x20,0x44,0x12,0x02,0x64, +0xB0,0x0C,0x3A,0x20,0x94,0x00,0x44,0x12,0x02,0x74,0x3A,0x20,0x14,0x20,0xB0,0x07, +0x3A,0x20,0x8C,0x00,0x3A,0x20,0x0C,0x20,0x44,0x02,0x02,0x7C,0x50,0x8F,0x80,0x24, +0x3A,0x20,0x10,0x00,0x3A,0x24,0x10,0x20,0x85,0x40,0x39,0xC4,0x28,0x00,0x40,0x0E, +0x00,0x13,0xC0,0x13,0x5A,0x00,0x03,0x12,0xB0,0x10,0x38,0x00,0x72,0x02,0xC8,0x0D, +0x38,0x03,0x72,0x02,0x40,0x90,0x04,0x08,0x49,0xFF,0xE0,0x74,0x49,0xFF,0xE3,0x21, +0xB0,0x50,0x88,0x09,0x38,0x00,0xF2,0x0A,0x8D,0x41,0x5A,0xA8,0x0C,0xE8,0x84,0xC0, +0xB0,0x10,0x38,0x00,0x1A,0x02,0xF0,0x81,0xC8,0x07,0x8C,0xC1,0x5A,0x68,0x08,0xFA, +0x51,0xFF,0x85,0x20,0xFC,0xC0,0xB0,0x07,0x87,0x80,0x38,0xA3,0x00,0x00,0x40,0x95, +0x04,0x08,0x40,0x04,0x80,0x13,0xF0,0x82,0x94,0x01,0xF0,0x83,0x40,0xFE,0x24,0x07, +0xE8,0xED,0x4F,0xC3,0x00,0x13,0x38,0x83,0x9A,0x01,0x49,0xFF,0xA6,0x92,0x44,0x20, +0x01,0x20,0x80,0x28,0x42,0x10,0x08,0x73,0xB0,0x0C,0x96,0x49,0x38,0x20,0x19,0x01, +0xB0,0x28,0x92,0x42,0x49,0xFF,0xA6,0xEC,0x4D,0xC5,0x40,0x14,0x38,0x83,0x9A,0x01, +0x49,0xFF,0xA6,0x7F,0x44,0x20,0x01,0x20,0x50,0x14,0x02,0x40,0x42,0x10,0x08,0x73, +0xB0,0x0C,0x96,0x49,0x38,0x20,0x19,0x01,0xB0,0x28,0x92,0x42,0x49,0xFF,0xA6,0xD8, +0x40,0x14,0xF0,0x01,0x8E,0x21,0x94,0x4C,0x40,0x0E,0x00,0x13,0x97,0x49,0x40,0x1E, +0x28,0x06,0x94,0xC4,0x84,0x40,0xF1,0x84,0x50,0x8F,0x80,0xA0,0xF4,0x04,0x40,0x11, +0x04,0x09,0xC4,0x03,0x88,0x23,0xD5,0x03,0x88,0x25,0x96,0x49,0x38,0x44,0x06,0x02, +0x54,0xF2,0x00,0x1F,0x5A,0xF0,0x1F,0x1A,0x40,0xF2,0x18,0x09,0x54,0xF7,0x80,0x1F, +0x5A,0xF0,0x1F,0x14,0x04,0xFF,0x80,0x01,0x39,0xE7,0x81,0x01,0x50,0xFF,0x5D,0xEB, +0x5C,0xF7,0x82,0x13,0xE9,0x06,0x50,0xFF,0x64,0x27,0x5C,0xF7,0x82,0x13,0xE8,0x05, +0x50,0x42,0x00,0x41,0x38,0x44,0x06,0x0A,0x38,0x44,0x06,0x02,0x40,0xF2,0x48,0x09, +0x54,0xF7,0x80,0x1F,0x5A,0xF0,0x1F,0x21,0x40,0xF2,0x30,0x09,0x54,0xF7,0x80,0x1F, +0x5A,0xF0,0x1F,0x1B,0x04,0xFF,0x80,0x02,0x05,0xEF,0x80,0x01,0x40,0xF0,0x3C,0x00, +0x40,0xF7,0x80,0x13,0x39,0xEF,0x3D,0x01,0x50,0xFF,0x5D,0xEB,0x5C,0xF7,0x82,0x13, +0xE9,0x06,0x50,0xFF,0x64,0x27,0x5C,0xF7,0x82,0x13,0xE8,0x06,0x44,0xF4,0x10,0x00, +0x88,0x8F,0x38,0x44,0x06,0x0A,0xF1,0x03,0x8C,0x42,0x88,0x01,0x96,0x90,0x96,0x01, +0x5A,0x28,0x20,0xAE,0x50,0x5E,0x00,0x01,0x4C,0x55,0x40,0x16,0x38,0x13,0x9A,0x01, +0xF5,0x85,0xF1,0x84,0x49,0xFF,0xA6,0x05,0x44,0x20,0x01,0x20,0xF1,0x04,0x42,0x10, +0x08,0x73,0xB0,0x0C,0x96,0x49,0x38,0x20,0x19,0x01,0x80,0x08,0x92,0x42,0x49,0xFF, +0xA6,0x7B,0xF5,0x05,0x4C,0x54,0xC0,0x16,0x38,0x13,0x9A,0x01,0xF1,0x84,0x49,0xFF, +0xA5,0xF0,0xF1,0x04,0x44,0x20,0x01,0x20,0x50,0x10,0x82,0x40,0x42,0x10,0x08,0x73, +0xB0,0x0C,0x96,0x49,0x38,0x20,0x19,0x01,0x80,0x08,0x92,0x42,0x49,0xFF,0xA6,0x64, +0x50,0x5E,0x00,0x01,0x55,0xC2,0x80,0xFF,0x48,0xFF,0xFF,0x42,0xFC,0x49,0x44,0x12, +0x02,0x88,0xB1,0x86,0x80,0x06,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20, +0x8C,0x00,0x3A,0x20,0x0C,0x20,0x84,0x20,0xB0,0x0C,0xFA,0x48,0x49,0x00,0x0F,0x54, +0x84,0x21,0x10,0x1F,0x80,0x31,0x10,0x1F,0x80,0x3D,0x84,0x02,0x44,0x12,0x02,0xA0, +0x10,0x0F,0x80,0x32,0x10,0x0F,0x80,0x3E,0x80,0x01,0x80,0x9F,0x3A,0x20,0x0C,0x04, +0x3A,0x22,0x0C,0x24,0xB1,0xC3,0x3A,0x20,0x8C,0x04,0xA4,0x00,0x80,0x27,0xAC,0x20, +0x3A,0x20,0x8C,0x24,0xAC,0x08,0x80,0x06,0x49,0xFF,0xAC,0x34,0xB0,0x0C,0x49,0xFF, +0xAC,0x84,0x80,0x1F,0x49,0xFF,0xAC,0x47,0x80,0x07,0x81,0x3F,0x49,0xFF,0xAC,0x96, +0xFC,0xC9,0xFC,0x26,0x44,0x12,0x02,0xAC,0x80,0xDF,0x3A,0x20,0x94,0x04,0x3A,0x23, +0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x23,0x14,0x24,0x3A,0x20,0x94,0x00,0x80,0x1F, +0x80,0xE6,0x3A,0x23,0x14,0x20,0x49,0xFF,0xAB,0xDF,0xFC,0xA6,0xFC,0x2C,0x44,0x12, +0x02,0xDC,0xB1,0x8C,0x80,0x06,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20, +0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x00,0x3A,0x20,0x14,0x20,0x84,0x20, +0xB0,0x06,0xFA,0x48,0x49,0x00,0x0F,0x00,0xB1,0xC1,0x44,0x12,0x03,0x0C,0x80,0x07, +0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0xB4,0x21,0xB6,0x20,0x80,0x06,0x49,0xFF, +0xAB,0xBB,0xB0,0x06,0x49,0xFF,0xAB,0xD5,0x80,0x07,0x49,0xFF,0xAB,0xBF,0xFC,0xAC, +0xFC,0x01,0x44,0x12,0x03,0x20,0x80,0x1F,0x3A,0x20,0x8C,0x00,0x3A,0x2F,0x8C,0x20, +0x49,0xFF,0xAB,0xBE,0xFC,0x81,0xFC,0x00,0xC8,0x04,0x49,0xFF,0xFE,0x3F,0xFC,0x80, +0x44,0x00,0x00,0x64,0x49,0xFF,0xA6,0x2F,0x44,0x00,0x13,0xA6,0x49,0xFF,0xA6,0x36, +0x44,0x00,0x0F,0xA6,0x49,0xFF,0xA6,0x3D,0x44,0x00,0x01,0x90,0x49,0xFF,0xA6,0x44, +0xFC,0x80,0xFC,0x00,0xE6,0x06,0xE8,0x06,0x44,0x12,0x03,0xC0,0x38,0x00,0x80,0x00, +0xD5,0x02,0x84,0x00,0x49,0xFF,0xA6,0x4E,0xFC,0x80,0xFC,0x00,0xE6,0x06,0xE8,0x06, +0x44,0x12,0x03,0xB8,0x38,0x00,0x80,0x00,0xD5,0x02,0x84,0x00,0x8C,0x01,0x96,0x00, +0x49,0xFF,0xA6,0x35,0xFC,0x80,0xFC,0x00,0x5A,0x00,0x01,0x0E,0xC0,0x04,0x5A,0x00, +0x02,0x1C,0xFC,0x80,0x84,0x00,0x49,0xFF,0xB2,0x14,0x2E,0x07,0xFC,0x5B,0x49,0xFF, +0xFF,0xC4,0xD5,0x2A,0x49,0xFF,0xB2,0x0D,0x44,0x00,0x00,0x64,0x49,0xFF,0xA5,0xF3, +0x44,0x00,0x23,0x96,0x49,0xFF,0xA5,0xFA,0x44,0x00,0x23,0x96,0x49,0xFF,0xA6,0x01, +0x44,0x00,0x01,0x90,0xD5,0x17,0x84,0x01,0x49,0xFF,0xB1,0xFB,0x44,0x00,0x03,0xE8, +0x49,0xFF,0xA5,0xE1,0x44,0x00,0x27,0x10,0x49,0xFF,0xA5,0xF3,0x46,0x20,0x00,0x81, +0x46,0x12,0x71,0x00,0x83,0x82,0xB8,0x10,0x96,0x01,0xFE,0x0F,0xB8,0x90,0x44,0x00, +0x27,0x10,0x49,0xFF,0xA5,0xF1,0x84,0x00,0x49,0xFF,0xFF,0xB9,0xFC,0x80,0x00,0x00, +0xE6,0x04,0xE8,0x24,0xFC,0x00,0x44,0xF0,0xB8,0x44,0x38,0x07,0x80,0x00,0x40,0xF0, +0x3C,0x00,0xDD,0x0F,0x04,0x14,0x1E,0x22,0x84,0x00,0x49,0xFF,0xA8,0xD5,0x84,0x01, +0x49,0xFF,0xA8,0xC0,0x84,0x01,0xD5,0x0F,0x84,0x00,0x49,0xFF,0xA8,0xCD,0x84,0x01, +0xD5,0x07,0x84,0x01,0xD5,0x02,0x84,0x00,0x49,0xFF,0xA8,0xC6,0x84,0x00,0x49,0xFF, +0xA8,0xB1,0x84,0x00,0x49,0xFF,0xA8,0xB7,0xFC,0x80,0xDD,0x9E,0xFC,0x00,0x49,0xFF, +0xDE,0x79,0x80,0x60,0x3E,0x00,0x0A,0xD0,0x44,0x12,0x23,0xC0,0x44,0x02,0x28,0x40, +0x44,0x20,0x02,0x40,0x2E,0x47,0xFC,0x5A,0x49,0xFF,0xE7,0xD8,0xFC,0x80,0x44,0x02, +0x28,0x40,0xDD,0x9E,0x44,0x02,0x23,0xC0,0xDD,0x9E,0x44,0x02,0x21,0xB0,0xDD,0x9E, +0x44,0x02,0x1E,0xB0,0xDD,0x9E,0x44,0x02,0x1E,0x4C,0xDD,0x9E,0xFC,0x00,0xC0,0x02, +0x84,0x01,0x49,0xFF,0xA5,0xDB,0x84,0x00,0x49,0xFF,0xA5,0xCA,0xFC,0x80,0xFC,0x00, +0x84,0x00,0x80,0x20,0x49,0xFF,0xA7,0xDA,0x84,0x00,0x84,0x21,0x49,0xFF,0xA7,0xD6, +0xFC,0x80,0xFC,0x00,0x84,0x01,0x84,0x20,0x49,0xFF,0xA7,0xD0,0x84,0x01,0x80,0x20, +0x49,0xFF,0xA7,0xCC,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xDE,0x3C,0x52,0x10,0x00,0x01, +0x96,0x48,0x84,0x01,0x49,0xFF,0xA7,0xC2,0xFC,0x80,0xFC,0x49,0x80,0x20,0x44,0x02, +0x03,0x28,0x50,0xAF,0x80,0x0C,0x3A,0x20,0x10,0x00,0x44,0x02,0x03,0x34,0x3A,0x2F, +0x90,0x20,0x3A,0x20,0x10,0x00,0x3A,0x25,0x10,0x20,0x44,0x22,0x03,0x40,0xB0,0x06, +0x3A,0x21,0x10,0x00,0x3A,0x20,0x10,0x20,0x44,0x22,0x03,0x4C,0x50,0x9F,0x80,0x24, +0x3A,0x21,0x10,0x00,0x3A,0x24,0x90,0x20,0x44,0x22,0x02,0x7C,0xB1,0xCC,0x3A,0x21, +0x10,0x00,0x3A,0x23,0x90,0x20,0x44,0x22,0x03,0x58,0xB1,0x8F,0x3A,0x21,0x10,0x00, +0x80,0xBF,0x3A,0x23,0x10,0x20,0xC9,0x03,0x80,0x05,0xD5,0x12,0x5A,0x18,0x01,0x04, +0x80,0x0A,0xD5,0x0E,0x5A,0x10,0x02,0x0D,0x5A,0x18,0x03,0x04,0x80,0x09,0xD5,0x08, +0x5A,0x18,0x04,0x04,0x80,0x07,0xD5,0x04,0x5A,0x18,0x05,0x06,0x80,0x06,0x49,0xFF, +0xA4,0xD8,0xFC,0xC9,0x5A,0x10,0x06,0xEA,0xFC,0xC9,0xFC,0x03,0x84,0x20,0x84,0x4C, +0x80,0xC0,0x80,0x1F,0x49,0x00,0x0D,0xB8,0x44,0x12,0x03,0x64,0xB0,0x03,0x3A,0x20, +0x90,0x00,0x3A,0x20,0x10,0x20,0xCE,0x03,0x80,0x1F,0xD5,0x03,0x5A,0x68,0x01,0x04, +0x49,0xFF,0xA4,0xD3,0xFC,0x83,0xFC,0x00,0x84,0x00,0x49,0xFF,0xFF,0xA0,0x84,0x00, +0x49,0xFF,0xFF,0xE5,0x84,0x00,0x49,0xFF,0xA7,0xB7,0x84,0x00,0x49,0xFF,0xA7,0xE6, +0x49,0xFF,0xFE,0x36,0x49,0xFF,0xFE,0x84,0x49,0xFF,0xFE,0xAC,0x84,0x00,0x49,0xFF, +0xAD,0xC9,0x44,0x00,0x02,0x40,0x49,0xFF,0xAD,0xDB,0x44,0x00,0x02,0x40,0x49,0xFF, +0xAD,0xCC,0x84,0x09,0x49,0xFF,0xAD,0xDF,0x84,0x03,0x49,0xFF,0xAD,0xE5,0x44,0x62, +0x17,0x60,0x44,0x10,0x00,0xF0,0x84,0x00,0x49,0xFF,0xAC,0x6D,0x49,0xFF,0xE7,0xC4, +0x38,0x13,0x01,0x01,0x84,0x00,0x49,0xFF,0xAB,0x1B,0x44,0x10,0x00,0xF0,0x84,0x02, +0x49,0xFF,0xAC,0x61,0x49,0xFF,0xE7,0xB8,0x38,0x13,0x01,0x01,0x84,0x02,0x49,0xFF, +0xAB,0x0F,0x44,0x10,0x00,0xF0,0x84,0x05,0x49,0xFF,0xAC,0x55,0x49,0xFF,0xE7,0xAC, +0x38,0x13,0x01,0x01,0x46,0x60,0x00,0x81,0x84,0x05,0x49,0xFF,0xAB,0x01,0x49,0xFF, +0xA8,0x9B,0x04,0x03,0x03,0x91,0x92,0x0A,0x40,0x00,0x28,0x08,0x58,0x00,0x01,0xC0, +0x14,0x03,0x03,0x91,0x49,0xFF,0xA8,0x89,0x04,0x03,0x00,0x2C,0x46,0x1F,0xFC,0x0F, +0x92,0x06,0x94,0x06,0x58,0x00,0x00,0x2D,0x14,0x03,0x00,0x2C,0x04,0x03,0x00,0x2C, +0x50,0x10,0x8F,0xFF,0x46,0x20,0x02,0xD0,0xFE,0x0E,0xFE,0x17,0x14,0x03,0x00,0x2C, +0x04,0x03,0x00,0x2D,0x92,0x06,0x94,0x06,0x58,0x00,0x00,0x0A,0x14,0x03,0x00,0x2D, +0x04,0x03,0x00,0x2D,0xFE,0x46,0x46,0x00,0x00,0xA0,0xFE,0x47,0x14,0x13,0x00,0x2D, +0x04,0x13,0x00,0x2D,0x46,0x0C,0x0F,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x0E,0x46,0x10, +0xA0,0x00,0xFE,0x0F,0x14,0x03,0x00,0x2D,0x04,0x03,0x00,0x2F,0x92,0x06,0x94,0x06, +0x58,0x00,0x00,0x04,0x14,0x03,0x00,0x2F,0x44,0x00,0x02,0x80,0x49,0xFF,0xAB,0xF2, +0x84,0x00,0x49,0xFF,0xFE,0xE5,0x49,0xFF,0xFC,0x71,0x49,0xFF,0xAB,0xE9,0x3E,0x07, +0xFC,0x5B,0xFC,0x80,0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xDD,0x50,0xC0,0x04,0x49,0xFF, +0xDD,0x50,0x84,0x01,0xFC,0x80,0x2E,0x07,0xF4,0xC2,0x96,0x00,0xC0,0x05,0x84,0x00, +0x3E,0x07,0xF4,0xC2,0x84,0x01,0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xAB,0xD1,0xFC,0x80, +0x2E,0x07,0xFC,0x5B,0xDD,0x9E,0x2E,0x07,0xFC,0x5A,0xDD,0x9E,0x2E,0x07,0xFC,0x59, +0xDD,0x9E,0x2E,0x07,0xFC,0x58,0xDD,0x9E,0x3E,0x07,0xFC,0x58,0xDD,0x9E,0xFC,0x00, +0x80,0xC0,0x49,0xFF,0xAC,0xBE,0x80,0x06,0x49,0xFF,0xA3,0x47,0x84,0x00,0x84,0x2C, +0x5A,0x68,0x01,0x1F,0x49,0xFF,0xAC,0x1B,0x84,0x02,0x84,0x2A,0x49,0xFF,0xAC,0x17, +0x84,0x03,0x84,0x2A,0x49,0xFF,0xAC,0x13,0x84,0x05,0x84,0x2C,0x49,0xFF,0xAC,0x0F, +0x84,0x07,0x84,0x2C,0x49,0xFF,0xAC,0x0B,0x84,0x00,0x80,0x20,0x49,0xFF,0xA2,0xCC, +0x84,0x00,0x84,0x22,0x49,0xFF,0xA2,0xC8,0x44,0x02,0x22,0xB8,0xD5,0x41,0x49,0xFF, +0xAB,0xFE,0x84,0x01,0x84,0x2C,0x49,0xFF,0xAB,0xFA,0x84,0x02,0x84,0x2A,0x49,0xFF, +0xAB,0xF6,0x84,0x03,0x84,0x2A,0x49,0xFF,0xAB,0xF2,0x84,0x04,0x84,0x2C,0x49,0xFF, +0xAB,0xEE,0x84,0x05,0x84,0x2C,0x49,0xFF,0xAB,0xEA,0x84,0x06,0x84,0x2C,0x49,0xFF, +0xAB,0xE6,0x84,0x07,0x84,0x2C,0x49,0xFF,0xAB,0xE2,0x84,0x01,0x84,0x20,0x49,0xFF, +0xA2,0xA3,0x84,0x01,0x80,0x20,0x49,0xFF,0xA2,0x9F,0x84,0x00,0x84,0x22,0x49,0xFF, +0xA2,0x9B,0x84,0x00,0x84,0x23,0x49,0xFF,0xA2,0x97,0x84,0x01,0x84,0x24,0x49,0xFF, +0xA2,0x93,0x84,0x01,0x84,0x25,0x49,0xFF,0xA2,0x8F,0x84,0x01,0x84,0x26,0x49,0xFF, +0xA2,0x8B,0x84,0x01,0x84,0x27,0x49,0xFF,0xA2,0x87,0x44,0x02,0x23,0x3C,0x44,0x10, +0x00,0x42,0x49,0xFF,0xA8,0x84,0xFC,0x80,0x44,0x02,0x22,0xB8,0xDD,0x9E,0x44,0x02, +0x23,0x3C,0xDD,0x9E,0xFC,0x21,0x84,0xC2,0xF0,0x81,0x80,0xE2,0x80,0x01,0x5A,0x10, +0x01,0x03,0x84,0xC1,0x49,0xFF,0xFF,0x85,0xCF,0x0C,0x84,0x03,0x49,0xFF,0xA5,0x26, +0x84,0x03,0x49,0xFF,0xA8,0xB0,0xF0,0x01,0x80,0x26,0x49,0xFF,0xE4,0xF9,0xFC,0xA1, +0x84,0x02,0x49,0xFF,0xA5,0x1B,0x84,0x02,0x49,0xFF,0xA8,0xA5,0xF0,0x01,0x80,0x26, +0x84,0x40,0x49,0xFF,0xE4,0x7C,0xFC,0xA1,0xFC,0x00,0x84,0x00,0x49,0xFF,0xFD,0x83, +0x84,0x00,0x84,0x20,0x3E,0x07,0xFC,0x5A,0x3E,0x07,0xFC,0x59,0x80,0x41,0x2E,0x07, +0xFC,0x5B,0x49,0xFF,0xFF,0xD1,0xFC,0x80,0xFC,0x20,0x44,0x14,0x80,0x90,0x44,0x0F, +0xFF,0xEF,0xB4,0x41,0x46,0x70,0x00,0x81,0xFE,0x16,0xB6,0x01,0x44,0x04,0x80,0x10, +0xFA,0x20,0xAE,0x40,0xFA,0xC2,0x50,0x73,0x83,0xD4,0xB4,0x07,0x92,0x0B,0x96,0x27, +0xC0,0x07,0x8E,0xC1,0xC6,0x05,0x84,0x01,0x49,0xFF,0xB0,0x3A,0xD5,0xF7,0x44,0x04, +0x80,0x10,0xFA,0x20,0xAE,0x40,0x44,0x14,0x80,0x90,0xB4,0x01,0x58,0x00,0x00,0x10, +0xB6,0x01,0xFC,0xA0,0xFC,0x00,0x49,0xFF,0xEB,0x7A,0x5A,0x08,0x01,0x09,0x44,0x14, +0x50,0x28,0xB4,0x01,0x92,0x01,0x96,0x06,0x5A,0x00,0x01,0xFD,0xFC,0x80,0xFC,0x00, +0x80,0xC0,0x5A,0x08,0x01,0x04,0x49,0xFF,0xFF,0xEF,0x80,0x06,0x49,0x00,0x05,0xDF, +0x80,0x06,0x49,0xFF,0xA3,0x9A,0xFC,0x80,0xFC,0x20,0x84,0xE0,0x80,0xC0,0x84,0x00, +0x49,0xFF,0xFF,0xEF,0x49,0xFF,0xFF,0xBA,0x49,0xFF,0xDE,0xEC,0x3E,0x77,0xF4,0xC3, +0x49,0xFF,0xDC,0x3F,0x3E,0x77,0xF4,0xC2,0x5A,0x68,0x01,0x05,0x80,0x06,0x49,0xFF, +0xFF,0xE0,0xFC,0xA0,0xFC,0x00,0x3C,0x6D,0xFF,0x14,0xC6,0x0A,0x84,0x01,0x49,0xFF, +0xFF,0xE5,0x49,0xFF,0xFD,0xC8,0x84,0x00,0x3C,0x0F,0xFF,0x14,0xFC,0x80,0x49,0xFF, +0xEB,0x3E,0x5A,0x00,0x01,0xF5,0x3E,0x67,0xF4,0xC3,0x49,0xFF,0xDC,0x22,0x3E,0x67, +0xF4,0xC2,0x49,0xFF,0xFD,0xB8,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xFD,0xAA,0x46,0x00, +0x00,0x81,0x04,0x00,0x00,0xF5,0x92,0x0B,0x96,0x27,0xC8,0x04,0x49,0xFF,0xEB,0x27, +0xC0,0x05,0x84,0x00,0x49,0xFF,0xFF,0xC2,0x84,0x01,0x3C,0x0F,0xFF,0x14,0xFC,0x80, +0xFC,0x00,0x49,0xFF,0xFF,0xD1,0x44,0x64,0x50,0x28,0x2E,0x07,0xF4,0xC2,0xC0,0x0F, +0x84,0xC0,0x3E,0x67,0xF4,0xC3,0x49,0xFF,0xDB,0xFC,0x3E,0x67,0xF4,0xC2,0x49,0xFF, +0xFD,0x88,0x49,0xFF,0xFB,0x1D,0x49,0xFF,0xFF,0xD9,0xFC,0x80,0x2E,0x07,0xF4,0xB4, +0xC8,0x0F,0xB4,0x06,0x92,0x03,0x96,0x06,0x5A,0x00,0x01,0x0B,0xB4,0x06,0x92,0x01, +0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFF,0x15,0x5A,0x08,0x01,0xE1,0xD5,0xE2,0x49,0xFF, +0xD3,0x3B,0x5A,0x08,0x02,0xF9,0xD5,0xDD,0xFC,0x49,0x2E,0x67,0xFC,0x5B,0x84,0x00, +0x49,0xFF,0xFE,0xA7,0x84,0x02,0x49,0xFF,0xA4,0x49,0x80,0x06,0x84,0x20,0x84,0x41, +0x49,0xFF,0xE3,0xAD,0x84,0x04,0x49,0xFF,0xFD,0x7A,0xB1,0x86,0x44,0x12,0x03,0x70, +0x80,0x06,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x20, +0x14,0x24,0x3A,0x20,0x94,0x00,0x3A,0x20,0x14,0x20,0x44,0x02,0x03,0xA0,0x80,0x3F, +0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24,0x3A,0x20,0x0C,0x00,0x3A,0x20,0x8C,0x20, +0x80,0x1F,0x49,0xFF,0xA8,0x5F,0x80,0x06,0x49,0xFF,0xA8,0x26,0x84,0x00,0x3E,0x07, +0xF4,0xC1,0x80,0xFF,0x49,0xFF,0xFF,0x70,0x44,0x94,0x50,0x28,0x49,0xFF,0xA1,0xB6, +0x2E,0x07,0xF4,0xC1,0xC0,0x2C,0x84,0x00,0x3E,0x07,0xF4,0xC1,0x49,0xFF,0xFF,0x7E, +0x49,0xFF,0xFF,0x90,0x84,0x00,0x49,0xFF,0xFD,0x42,0x44,0x12,0x02,0xDC,0x80,0x06, +0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24, +0x3A,0x20,0x94,0x00,0x3A,0x20,0x14,0x20,0x44,0x02,0x02,0x88,0x80,0x27,0x3A,0x20, +0x14,0x04,0x3A,0x20,0x94,0x24,0x3A,0x20,0x0C,0x00,0x3A,0x20,0x8C,0x20,0x80,0x07, +0x49,0xFF,0xA8,0x28,0x80,0x06,0x49,0xFF,0xA7,0xEF,0xFC,0xC9,0x2E,0x07,0xF4,0xB4, +0xC8,0x0F,0xB4,0x09,0x92,0x03,0x96,0x06,0x5A,0x00,0x01,0x0B,0xB4,0x09,0x92,0x01, +0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFF,0x15,0x5A,0x08,0x01,0xC4,0xD5,0xC5,0x49,0xFF, +0xD2,0xBB,0x5A,0x08,0x02,0xF9,0xD5,0xC0,0xFC,0x21,0xB6,0x1F,0x2E,0x07,0xFC,0x5A, +0x49,0xFF,0xAB,0x28,0xF0,0x81,0xB4,0x1F,0x49,0xFF,0xA5,0x50,0x44,0x10,0x02,0x40, +0xB4,0x1F,0x49,0xFF,0xAB,0x2A,0x84,0x02,0x49,0xFF,0xFC,0x8C,0x49,0xFF,0xFF,0x14, +0x49,0xFF,0xEA,0x5D,0x5A,0x08,0x01,0x1E,0x84,0xC2,0x44,0x74,0x50,0x28,0x2E,0x07, +0xF4,0xC2,0xC8,0x32,0x2E,0x07,0xF4,0xB4,0xC8,0x0F,0xB4,0x07,0x92,0x03,0x96,0x06, +0x5A,0x00,0x01,0x0B,0xB4,0x07,0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFF,0x15, +0x5A,0x08,0x01,0xEF,0xD5,0x21,0x49,0xFF,0xD2,0x87,0x5A,0x08,0x02,0xF9,0xD5,0x1C, +0x44,0x64,0x50,0x28,0x2E,0x07,0xF4,0xC2,0xC8,0x22,0x2E,0x07,0xF4,0xB4,0xC8,0x0F, +0xB4,0x06,0x92,0x03,0x96,0x06,0x5A,0x00,0x01,0x0B,0xB4,0x06,0x92,0x01,0x96,0x06, +0xC0,0x06,0x3C,0x0D,0xFF,0x15,0x5A,0x08,0x01,0xEF,0xD5,0x11,0x49,0xFF,0xD2,0x6C, +0x5A,0x08,0x02,0xF9,0xD5,0x0C,0x49,0xFF,0xDB,0x0C,0x84,0x00,0x3E,0x07,0xF4,0xC2, +0x3E,0x07,0xF4,0xC3,0x5A,0x60,0x01,0xDE,0x84,0xC1,0xD5,0xC2,0x84,0xC0,0x3E,0x67, +0xF4,0xC3,0x49,0xFF,0xDA,0xFE,0x3E,0x67,0xF4,0xC2,0x49,0xFF,0xFE,0xDF,0x44,0x10, +0x02,0x40,0xB4,0x5F,0xF0,0x01,0x49,0xFF,0xE4,0x69,0x84,0x01,0x49,0xFF,0xFC,0x32, +0x84,0x01,0x49,0xFF,0xFE,0xA3,0xFC,0xA1,0xFC,0x00,0x2E,0x07,0xFC,0x5B,0x49,0xFF, +0xE6,0xC7,0x84,0x00,0x49,0xFF,0xFB,0xCF,0x84,0x20,0x2E,0x27,0xFC,0x59,0x2E,0x07, +0xFC,0x5B,0x49,0xFF,0xFE,0x21,0x84,0x00,0x49,0xFF,0xFF,0x80,0x2E,0x17,0xFC,0x5A, +0x2E,0x27,0xFC,0x59,0x2E,0x07,0xFC,0x5B,0x49,0xFF,0xFE,0x16,0x2E,0x07,0xFC,0x5B, +0x49,0xFF,0xE6,0xAE,0x2E,0x07,0xFC,0x5B,0x49,0xFF,0xE8,0x03,0x49,0xFF,0xE9,0xDF, +0xC8,0x09,0x2E,0x07,0xFC,0x5B,0x49,0xFF,0xFB,0x98,0x2E,0x07,0xFC,0x5B,0x49,0xFF, +0xFC,0x47,0x84,0x01,0x49,0xFF,0xFB,0xFE,0x49,0xFF,0xE5,0x90,0x49,0xFF,0xFE,0x84, +0xFC,0x80,0xFC,0x21,0x84,0x00,0x49,0xFF,0xFE,0x69,0x49,0xFF,0xDA,0xA5,0xF0,0x81, +0x84,0x00,0x49,0xFF,0xA4,0xAB,0x44,0x10,0x00,0xC0,0x84,0x00,0x49,0xFF,0xDD,0x44, +0x84,0x05,0x49,0xFF,0xFB,0x90,0x84,0x05,0x49,0xFF,0xFB,0x99,0x84,0x05,0x49,0xFF, +0xFC,0x4E,0x49,0xFF,0xFB,0x28,0x84,0x02,0x49,0xFF,0xFB,0xDC,0x84,0x01,0x49,0xFF, +0xFE,0x4D,0x84,0xCA,0x49,0xFF,0xFC,0x2F,0x44,0x74,0x50,0x28,0x84,0x00,0x3E,0x07, +0xF4,0xC3,0x2E,0x07,0xF4,0xC3,0xC8,0x15,0x2E,0x07,0xF4,0xB4,0xC8,0x0E,0xB4,0x07, +0x92,0x03,0x96,0x06,0xC8,0x0A,0xB4,0x07,0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D, +0xFF,0x15,0x5A,0x08,0x01,0xF0,0xD5,0x05,0x49,0xFF,0xD1,0xD6,0x5A,0x08,0x02,0xF9, +0x84,0x00,0x3E,0x07,0xF4,0xC2,0x2E,0x07,0xF4,0xC2,0xC8,0x15,0x2E,0x07,0xF4,0xB4, +0xC8,0x0E,0xB4,0x07,0x92,0x03,0x96,0x06,0xC8,0x0A,0xB4,0x07,0x92,0x01,0x96,0x06, +0xC0,0x06,0x3C,0x0D,0xFF,0x15,0x5A,0x08,0x01,0xF0,0xD5,0x05,0x49,0xFF,0xD1,0xBC, +0x5A,0x08,0x02,0xF9,0x8E,0xC1,0x97,0xB0,0xCE,0xCA,0x80,0x06,0x49,0xFF,0xFE,0x0E, +0x49,0xFF,0xFA,0xF6,0x80,0x06,0x49,0xFF,0xFC,0x02,0x80,0x06,0x49,0xFF,0xFB,0x47, +0x84,0x03,0x49,0xFF,0xFB,0x8F,0xF0,0x01,0x49,0xFF,0xA4,0x48,0xFC,0xA1,0xFC,0x00, +0x49,0xFF,0xFF,0x91,0x44,0x02,0x1E,0xB0,0x44,0x10,0x00,0xC0,0x84,0x40,0x49,0xFF, +0xE3,0x95,0xFC,0x80,0xFC,0x00,0x2E,0x07,0xFC,0x5B,0x49,0xFF,0xE6,0x19,0x2E,0x17, +0xFC,0x5B,0x84,0x03,0x49,0xFF,0xE6,0xF7,0x84,0x03,0x49,0xFF,0xFB,0x1C,0x2E,0x07, +0xFC,0x5B,0x84,0x20,0x2E,0x27,0xFC,0x59,0x49,0xFF,0xFD,0x6E,0x49,0xFF,0xE9,0x3F, +0xC8,0x04,0x84,0x01,0x49,0xFF,0xFD,0xDA,0x84,0x01,0x49,0xFF,0xFB,0x63,0x44,0x64, +0x50,0x28,0x49,0xFF,0xFD,0xE9,0x2E,0x07,0xF4,0xC2,0xC0,0x1D,0x84,0xC0,0x3E,0x67, +0xF4,0xC3,0x49,0xFF,0xDA,0x16,0x3E,0x67,0xF4,0xC2,0x49,0xFF,0xFD,0xF7,0x44,0x10, +0x00,0x42,0x84,0x40,0x84,0x63,0x44,0x02,0x23,0x3C,0x49,0xFF,0xE3,0x90,0x84,0x00, +0x49,0xFF,0xFA,0xF1,0x2E,0x07,0xFC,0x5B,0x49,0xFF,0xE7,0x3B,0x84,0x01,0x49,0xFF, +0xFB,0x41,0xFC,0x80,0x2E,0x07,0xF4,0xB4,0xC8,0x0F,0xB4,0x06,0x92,0x03,0x96,0x06, +0x5A,0x00,0x01,0x0B,0xB4,0x06,0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFF,0x15, +0x5A,0x08,0x01,0xD3,0xD5,0xD4,0x49,0xFF,0xD1,0x47,0x5A,0x08,0x02,0xF9,0xD5,0xCF, +0xFC,0x46,0x80,0xC0,0x84,0xE1,0x81,0x22,0xF1,0x8B,0x49,0xFF,0xD9,0x28,0xB6,0xDF, +0x84,0xC0,0x80,0x27,0x44,0x24,0x10,0x10,0x80,0x66,0x80,0x87,0x80,0xA6,0x80,0x07, +0xF7,0x81,0xF6,0x82,0xF6,0x83,0xF7,0x84,0xF6,0x85,0xF6,0x86,0xF6,0x87,0x14,0x9F, +0x80,0x08,0x49,0xFF,0xEA,0x8E,0x80,0x06,0x49,0xFF,0xD9,0x1E,0x80,0x07,0x49,0xFF, +0xD9,0x29,0x80,0x06,0x49,0xFF,0xD9,0x08,0x80,0x06,0x49,0xFF,0xD9,0x82,0x49,0xFF, +0xD9,0x4C,0x44,0x00,0x00,0x3B,0x49,0xFF,0xD9,0x56,0xF0,0x0B,0x49,0xFF,0xD9,0x61, +0x80,0x07,0x49,0xFF,0xD9,0x6D,0x80,0x06,0x49,0xFF,0xD9,0x4D,0x49,0xFF,0xD9,0x75, +0x80,0x09,0x49,0xFF,0xD9,0x77,0x80,0x06,0x49,0xFF,0xD9,0x78,0x80,0x07,0x49,0xFF, +0xD9,0x75,0x40,0x00,0x00,0x09,0x44,0x04,0x80,0x07,0x84,0x22,0xAE,0x40,0x80,0x07, +0x49,0xFF,0xD9,0x75,0xFC,0xC6,0xFC,0x00,0x44,0x04,0x30,0x10,0xB4,0x00,0x92,0x08, +0x96,0x00,0xC0,0x15,0x44,0x14,0x30,0x08,0x84,0x1E,0xB4,0x41,0x44,0x64,0x30,0x0C, +0xFE,0x16,0xB6,0x01,0xB4,0x06,0x58,0x00,0x00,0x01,0xB6,0x06,0x84,0x01,0x49,0xFF, +0xAD,0x4F,0xB4,0x06,0x58,0x00,0x00,0x02,0xB6,0x06,0xFC,0x80,0x44,0x14,0x00,0x40, +0x46,0x20,0x81,0x00,0xB6,0x41,0x44,0x64,0x30,0x0C,0xB6,0x01,0xB4,0x06,0x92,0x04, +0x96,0x06,0xC8,0xE1,0x44,0x00,0x00,0x64,0x49,0xFF,0xAD,0x31,0x3C,0x00,0x05,0x69, +0x8C,0x01,0x96,0x01,0x5C,0xF0,0x00,0x64,0x3C,0x08,0x05,0x69,0xE9,0xF0,0xD5,0xD3, +0xFC,0x00,0x84,0x01,0x49,0xFF,0xA1,0x1C,0x84,0x03,0x49,0xFF,0xA1,0x3C,0x84,0x00, +0x49,0xFF,0xA1,0xC2,0x84,0x00,0x49,0xFF,0xA1,0x46,0x84,0x03,0x49,0xFF,0xA1,0x64, +0x84,0x00,0x49,0xFF,0xA1,0x6F,0x84,0x01,0x49,0xFF,0xD8,0x70,0xFC,0x80,0xFC,0x00, +0x84,0x00,0x44,0x10,0x00,0xF0,0x49,0xFF,0xA7,0xEE,0x84,0x02,0x44,0x10,0x00,0xF0, +0x49,0xFF,0xA7,0xE9,0x84,0x01,0x44,0x10,0x00,0xF0,0x49,0xFF,0xA7,0xE4,0x84,0x03, +0x44,0x10,0x00,0xF0,0x49,0xFF,0xA7,0xDF,0x84,0x04,0x44,0x10,0x00,0xF0,0x49,0xFF, +0xA7,0xDA,0x44,0x10,0x00,0xF0,0x84,0x05,0x49,0xFF,0xA7,0xD5,0x84,0x01,0x49,0xFF, +0xD7,0x6E,0x84,0x03,0x84,0x2A,0x49,0xFF,0xA8,0x22,0x84,0x00,0x84,0x23,0x49,0xFF, +0x9E,0xE3,0xFC,0x80,0xFC,0x01,0xF0,0x81,0x49,0xFF,0xA9,0x50,0xE6,0x03,0xE9,0x04, +0xF0,0x01,0x49,0xFF,0xDC,0x45,0xFC,0x81,0xFC,0x00,0x84,0x01,0x49,0xFF,0xA3,0x75, +0x84,0x01,0x49,0xFF,0x9F,0x1F,0x84,0x01,0x49,0xFF,0xA2,0x59,0x84,0x00,0x49,0xFF, +0xFA,0x14,0x84,0x00,0x49,0xFF,0xF9,0xF7,0x84,0x01,0x49,0xFF,0xA1,0x8A,0x84,0x01, +0x49,0xFF,0xA6,0xB7,0x84,0x01,0x49,0xFF,0xA6,0xC4,0x84,0x01,0x49,0xFF,0xA1,0x81, +0x84,0x01,0x49,0xFF,0xA6,0xDC,0x84,0x01,0x49,0xFF,0xA2,0x80,0x84,0x01,0x49,0xFF, +0xA2,0x91,0x84,0x01,0x49,0xFF,0xA2,0xA7,0x49,0xFF,0xA9,0x20,0xE6,0x03,0xE8,0x05, +0x84,0x01,0x84,0x2F,0x49,0xFF,0xA8,0x7E,0x49,0xFF,0xA9,0x18,0xE6,0x03,0xE9,0x03, +0x49,0xFF,0xA9,0x18,0x49,0xFF,0xA9,0x12,0xE6,0x03,0xE8,0x03,0x49,0xFF,0xA9,0x1B, +0x84,0x04,0x49,0xFF,0xA9,0x2C,0xFC,0x80,0xC2,0x19,0xFC,0x00,0x49,0xFF,0xFE,0xF2, +0x44,0x00,0x00,0xFF,0x49,0xFF,0xD8,0xAF,0x49,0xFF,0xD8,0x59,0xC0,0xFE,0x44,0x04, +0x80,0x07,0x84,0x22,0xAE,0x40,0x84,0x00,0x49,0xFF,0xD8,0x0E,0x84,0x01,0x49,0xFF, +0xD8,0x88,0x84,0x00,0x49,0xFF,0xD8,0x7C,0xFC,0x80,0xDD,0x9E,0xC2,0x25,0xFC,0x00, +0x49,0xFF,0xFE,0xD8,0x44,0x00,0x00,0xFF,0x49,0xFF,0xD8,0x95,0x49,0xFF,0xA8,0xE6, +0xE6,0x03,0xE8,0x04,0x84,0x0A,0x49,0xFF,0xAC,0x7B,0x44,0x64,0x80,0x04,0xA6,0x32, +0x96,0x06,0xC0,0xFE,0x49,0xFF,0xD8,0x33,0xC0,0xFB,0x44,0x04,0x80,0x07,0x84,0x22, +0xAE,0x40,0x84,0x00,0x49,0xFF,0xD7,0xE8,0x84,0x01,0x49,0xFF,0xD8,0x62,0x84,0x00, +0x49,0xFF,0xD8,0x56,0xFC,0x80,0xDD,0x9E,0xFC,0x00,0x44,0x37,0x20,0x64,0x84,0x30, +0xB4,0x03,0xFE,0x0E,0x58,0x00,0x00,0x05,0xB6,0x03,0xB4,0x43,0x44,0x0F,0xF0,0xFF, +0xFE,0x86,0x58,0x21,0x05,0x00,0xB6,0x43,0x44,0x27,0x20,0x6C,0xB4,0x62,0xFE,0x5E, +0x58,0x10,0x80,0x05,0xB6,0x22,0xB4,0x22,0xFE,0x0E,0x58,0x00,0x05,0x00,0xB6,0x02, +0x44,0x00,0x00,0x3F,0x49,0xFF,0xA2,0x48,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xA8,0xE9, +0x49,0xFF,0xA9,0x4E,0x49,0xFF,0xA9,0xFC,0x84,0x01,0x49,0xFF,0xA8,0xD3,0x49,0xFF, +0xDB,0xF8,0xFC,0x80,0xFC,0x01,0xF1,0x81,0x49,0xFF,0xA9,0x99,0xF0,0x01,0x49,0xFF, +0xDB,0xDD,0xFC,0x81,0xFC,0x00,0x49,0xFF,0xE7,0x7A,0x5A,0x08,0x01,0x04,0x84,0x00, +0xD5,0x02,0x84,0x04,0x49,0xFF,0xA9,0x94,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xA9,0x87, +0xFC,0x80,0x44,0x04,0x40,0x0C,0xB4,0x00,0x92,0x01,0x96,0x06,0x5A,0x00,0x01,0x09, +0x44,0x07,0x10,0x14,0xB4,0x00,0x92,0x0E,0x56,0x00,0x00,0x01,0x96,0x06,0xDD,0x9E, +0xFC,0x00,0xC0,0x04,0x49,0xFF,0xFF,0xAA,0xFC,0x80,0x44,0x17,0x20,0x64,0x84,0x70, +0xB4,0x41,0xFE,0x9E,0xB6,0x41,0xB4,0x81,0x44,0x2F,0xF0,0xFF,0xFF,0x16,0xB6,0x81, +0x44,0x17,0x20,0x6C,0xB4,0x81,0xFE,0xE6,0xB6,0x61,0xB4,0x61,0xFE,0x9E,0xB6,0x41, +0x49,0xFF,0xA1,0xFA,0xFC,0x80,0xFC,0x42,0x49,0xFF,0x9F,0xA0,0xB6,0x1F,0x49,0xFF, +0xD8,0x1B,0xF0,0x81,0x84,0x00,0x49,0xFF,0xFB,0xD9,0x84,0x00,0x49,0xFF,0xA2,0x1E, +0x84,0x00,0x49,0xFF,0xD8,0x02,0x84,0x00,0x49,0xFF,0xA2,0x2C,0x84,0x01,0x49,0xFF, +0xA2,0x32,0x49,0xFF,0xAB,0xD0,0xE6,0x03,0xE8,0x03,0x49,0xFF,0xFF,0x77,0x84,0x05, +0x49,0xFF,0xF9,0x05,0x84,0x05,0x49,0xFF,0xF8,0xF6,0x84,0x05,0x49,0xFF,0xF9,0xB7, +0x49,0xFF,0xF8,0x91,0x84,0x01,0x49,0xFF,0xFB,0xB9,0x84,0xC2,0x49,0xFF,0xF9,0x9B, +0x44,0x74,0x50,0x28,0x84,0x00,0x3E,0x07,0xF4,0xC3,0x2E,0x07,0xF4,0xC3,0xC8,0x15, +0x2E,0x07,0xF4,0xB4,0xC8,0x0E,0xB4,0x07,0x92,0x03,0x96,0x06,0xC8,0x0A,0xB4,0x07, +0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFF,0x15,0x5A,0x08,0x01,0xF0,0xD5,0x05, +0x49,0xFF,0xCF,0x42,0x5A,0x08,0x02,0xF9,0x84,0x00,0x3E,0x07,0xF4,0xC2,0x2E,0x07, +0xF4,0xC2,0xC8,0x15,0x2E,0x07,0xF4,0xB4,0xC8,0x0E,0xB4,0x07,0x92,0x03,0x96,0x06, +0xC8,0x0A,0xB4,0x07,0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFF,0x15,0x5A,0x08, +0x01,0xF0,0xD5,0x05,0x49,0xFF,0xCF,0x28,0x5A,0x08,0x02,0xF9,0x84,0x00,0x3E,0x07, +0xF4,0xC2,0x5A,0x60,0x01,0x04,0x84,0xC1,0xD5,0xC6,0x49,0xFF,0xF9,0x52,0x84,0x00, +0x49,0xFF,0xFB,0x74,0x49,0xFF,0xAB,0x77,0xE6,0x03,0xE9,0x04,0x84,0x00,0x49,0xFF, +0x9F,0x1D,0x84,0x00,0x49,0xFF,0xA0,0xFB,0x84,0x00,0x80,0x20,0x49,0xFF,0xAA,0x16, +0x49,0xFF,0xAA,0xCA,0xF0,0x82,0x84,0x00,0x49,0xFF,0xAA,0xBD,0x84,0x00,0x49,0xFF, +0xA9,0x12,0x2E,0x07,0xF4,0xB4,0xC8,0x10,0x44,0x14,0x50,0x28,0xB4,0x01,0x92,0x03, +0x96,0x06,0xC8,0x0A,0xB4,0x01,0x92,0x01,0x96,0x06,0xC0,0x06,0x3C,0x0D,0xFF,0x15, +0x5A,0x08,0x01,0x0B,0xD5,0x05,0x49,0xFF,0xCE,0xEF,0x5A,0x08,0x02,0xF9,0x84,0xC0, +0x80,0xE6,0x81,0x26,0xD5,0x31,0x49,0xFF,0xAB,0x46,0xE6,0x03,0xE8,0x0D,0x46,0x00, +0x00,0x91,0x04,0x70,0x00,0x11,0x44,0x07,0x20,0xC0,0xB4,0xC0,0x80,0x07,0x80,0x26, +0x49,0xFF,0xDA,0xF5,0xD5,0x05,0x84,0xC0,0x49,0xFF,0xA9,0x98,0x80,0xE6,0x49,0xFF, +0xE6,0x8E,0x80,0x40,0x5A,0x08,0x01,0x08,0x44,0x00,0x0D,0xAC,0x84,0x20,0x80,0x62, +0x49,0x00,0x00,0xB7,0x46,0x00,0x00,0x91,0x85,0x21,0xA0,0x43,0x92,0x26,0x94,0x4E, +0x58,0x10,0x80,0x20,0xA8,0x43,0x49,0xFF,0xF8,0xFE,0x84,0x01,0x49,0xFF,0xFB,0x09, +0x84,0x01,0x49,0xFF,0xA3,0x8D,0x49,0xFF,0xE6,0x72,0x80,0x40,0x5A,0x08,0x01,0x08, +0x44,0x00,0x0D,0xAC,0x84,0x20,0x80,0x62,0x49,0x00,0x00,0x9B,0x84,0x00,0x49,0xFF, +0xF8,0x3A,0x84,0x00,0x49,0xFF,0xF8,0xFB,0x49,0xFF,0xF7,0xEA,0x49,0xFF,0xE6,0x6B, +0xF0,0x83,0xF1,0x03,0x84,0x01,0x49,0x00,0x01,0x12,0x5A,0x98,0x01,0x0B,0x49,0xFF, +0xA1,0xB7,0x5A,0x00,0x01,0xFE,0x64,0x00,0x00,0x20,0x84,0x01,0x3E,0x07,0xF4,0xC0, +0x84,0x00,0x49,0xFF,0xFA,0xDE,0x49,0xFF,0xF8,0xC4,0x44,0x04,0x80,0x10,0x84,0x21, +0xAE,0x40,0x44,0x14,0x80,0x11,0x84,0x44,0xAE,0x88,0xFA,0x20,0xAE,0x40,0x85,0x20, +0x49,0xFF,0xFA,0x9C,0x3E,0x97,0xF4,0xC3,0x49,0xFF,0xD7,0x23,0x3E,0x97,0xF4,0xC2, +0x49,0xFF,0xAA,0xD9,0xE6,0x03,0xE8,0x06,0x80,0x07,0x80,0x26,0x49,0xFF,0xDA,0x8F, +0xD5,0x06,0x84,0x00,0x49,0xFF,0xA3,0x44,0x49,0xFF,0xA9,0x30,0x44,0x10,0x00,0xEB, +0x84,0x01,0x49,0x00,0x00,0xDC,0xB4,0x1F,0x49,0xFF,0x9E,0x70,0xF0,0x02,0x49,0xFF, +0xAA,0x1A,0x49,0xFF,0xAA,0xC0,0xE6,0x03,0xE8,0x04,0x84,0x00,0x49,0xFF,0xFE,0xBA, +0x84,0x00,0x49,0xFF,0xA1,0x18,0x84,0x20,0x84,0x01,0x49,0xFF,0xA9,0x5F,0x84,0x00, +0x49,0xFF,0xF8,0xA5,0x84,0x00,0x49,0xFF,0xF7,0xEA,0x84,0x01,0x49,0xFF,0xA0,0x37, +0x84,0x01,0x49,0xFF,0xA0,0xFF,0x2E,0x67,0xF4,0xE9,0x44,0x0F,0xFF,0xC8,0x97,0xB0, +0x3E,0x00,0x0A,0xDC,0xC6,0x07,0x84,0x00,0x84,0xC1,0x3E,0x07,0xF4,0xE9,0x3C,0x6E, +0x02,0xB6,0x2E,0x07,0xF4,0xE8,0xC0,0x08,0x84,0x00,0x3E,0x07,0xF4,0xE8,0x49,0xFF, +0xF8,0x59,0x49,0xFF,0xDD,0x5A,0x49,0xFF,0xE1,0xA9,0x49,0xFF,0xE5,0xE8,0x80,0x40, +0x5A,0x08,0x01,0x08,0x44,0x00,0x01,0xF4,0x84,0x20,0x80,0x62,0x49,0x00,0x00,0x11, +0x84,0x00,0x49,0xFF,0xF7,0xB0,0xF0,0x01,0x49,0xFF,0xA0,0xC0,0x84,0x01,0x49,0xFF, +0xFA,0x68,0x49,0xFF,0xF8,0x58,0x49,0xFF,0xFA,0x31,0x80,0x06,0xFC,0xC2,0xFC,0x02, +0xF0,0x83,0xF1,0x81,0xFD,0x01,0xF2,0x82,0xB6,0x7F,0x49,0xFF,0xA8,0xF1,0xF0,0x01, +0xB4,0x3F,0x49,0xFF,0xA9,0x09,0xF0,0x03,0xF1,0x01,0xF2,0x02,0xB4,0x7F,0x49,0xFF, +0xA9,0x27,0xB4,0x1F,0x49,0xFF,0xA9,0x48,0xFC,0x82,0xFC,0x00,0x44,0x00,0xCA,0x1C, +0x49,0xFF,0xDC,0x2D,0x84,0x20,0x80,0x61,0x84,0x41,0x44,0x00,0x00,0x64,0x49,0xFF, +0xFF,0xE0,0x84,0x00,0x80,0x20,0x49,0xFF,0xA9,0x73,0x84,0x00,0x80,0x20,0x49,0xFF, +0xA2,0xCB,0x84,0x00,0x49,0xFF,0xA9,0x76,0x84,0x20,0x84,0x01,0x49,0xFF,0xA8,0xEE, +0x84,0x00,0x49,0xFF,0xA9,0x3B,0x84,0x40,0x84,0x61,0x44,0x00,0x03,0x41,0x84,0x27, +0x49,0xFF,0xFF,0xC7,0x84,0x00,0x84,0x21,0x49,0xFF,0xA9,0x5A,0x84,0x01,0x80,0x20, +0x49,0xFF,0xA2,0xB2,0x49,0x00,0x00,0x5A,0xFC,0x80,0xFC,0x00,0x80,0xC0,0x5A,0x08, +0x01,0x0D,0x84,0x00,0x3E,0x07,0xF4,0xC0,0x44,0x10,0x01,0xF4,0x84,0x02,0x49,0x00, +0x00,0x51,0x84,0x02,0x80,0x26,0xD5,0x03,0x84,0x02,0x84,0x20,0x49,0x00,0x00,0x58, +0xFC,0x80,0xFC,0x00,0x2E,0x07,0xF4,0xC0,0xC0,0x07,0x84,0x02,0x49,0x00,0x00,0x47, +0x84,0x00,0x3E,0x07,0xF4,0xC0,0x84,0x02,0x49,0x00,0x00,0x65,0x5A,0x08,0x01,0x04, +0x49,0xFF,0xE6,0xFD,0xFC,0x80,0xFC,0x00,0x84,0x01,0x49,0xFF,0xDC,0x12,0x84,0x07, +0x49,0xFF,0xDC,0x19,0x84,0x00,0x49,0xFF,0xDC,0x22,0x84,0x00,0x49,0xFF,0xDC,0x2B, +0x44,0x00,0xFF,0x14,0x49,0xFF,0xA9,0x66,0x49,0xFF,0xA9,0x74,0x49,0xFF,0xA9,0x6B, +0xFC,0x80,0xFC,0x00,0x49,0xFF,0xA9,0x67,0xFC,0x80,0xFC,0x00,0x5A,0x08,0x01,0x0B, +0xFE,0x0B,0x96,0x01,0x49,0xFF,0xA9,0x56,0x49,0xFF,0xA9,0x64,0x49,0xFF,0xA9,0x5B, +0xFC,0x80,0x49,0xFF,0xA9,0x6F,0xFC,0x80,0x3C,0x00,0x05,0x90,0x3C,0x10,0x05,0x90, +0x96,0x01,0x4C,0x00,0xFF,0xFB,0xDD,0x9E,0x84,0x00,0x3C,0x08,0x05,0x6F,0xDD,0x9E, +0x44,0x22,0x2C,0xD0,0x38,0x11,0x01,0x09,0xDD,0x9E,0xFC,0x00,0x80,0xC0,0x49,0xFF, +0xFF,0xED,0x44,0x12,0x2C,0xF0,0x38,0x00,0x99,0x09,0xFC,0x80,0x80,0x60,0x5A,0x18, +0x01,0x10,0xFC,0x20,0x80,0xC1,0x80,0xE0,0x49,0xFF,0xFF,0xF1,0x3C,0x20,0x05,0x6F, +0x40,0x13,0x1C,0x0C,0xFE,0x57,0x96,0x49,0x3C,0x18,0x05,0x6F,0xFC,0xA0,0x84,0x41, +0x3C,0x00,0x05,0x6F,0x40,0x11,0x0C,0x0C,0xFE,0x4B,0xFE,0x46,0x3C,0x18,0x05,0x6F, +0xDD,0x9E,0x3C,0x10,0x05,0x6F,0x40,0x10,0x80,0x0E,0x96,0x46,0xC9,0x03,0x80,0x01, +0xDD,0x9E,0xFC,0x00,0x80,0xC0,0x49,0xFF,0xFF,0xC1,0x44,0x12,0x2C,0xF0,0x44,0x22, +0x2C,0xD0,0x38,0x10,0x99,0x01,0x38,0x21,0x19,0x01,0x8A,0x01,0x96,0x01,0xE2,0x02, +0x56,0x07,0x80,0x01,0xFC,0x80,0xFC,0x00,0x80,0xC0,0x49,0xFF,0xFF,0xAF,0x44,0x12, +0x2C,0xF0,0x38,0x10,0x99,0x01,0x8A,0x01,0x96,0x01,0xFC,0x80,0x3C,0x00,0x05,0x90, +0x8C,0x01,0x96,0x01,0x3C,0x08,0x05,0x90,0xDD,0x9E,0x00,0x00,0x3A,0x1F,0xA4,0x3C, +0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72,0x04,0x02,0x3A,0x6F, +0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03,0x64,0x00,0x00,0x08, +0x49,0x00,0x02,0x13,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03,0x64,0x72,0x04,0x03, +0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0xA4,0x3C, +0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72,0x04,0x02,0x3A,0x6F, +0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03,0x64,0x00,0x00,0x08, +0x49,0x00,0x01,0xF4,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03,0x64,0x72,0x04,0x03, +0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0xA4,0x3C, +0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72,0x04,0x02,0x3A,0x6F, +0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03,0x64,0x00,0x00,0x08, +0x49,0x00,0x01,0xD5,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03,0x64,0x72,0x04,0x03, +0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0xA4,0x3C, +0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72,0x04,0x02,0x3A,0x6F, +0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03,0x64,0x00,0x00,0x08, +0x49,0x00,0x01,0xB6,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03,0x64,0x72,0x04,0x03, +0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0xA4,0x3C, +0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72,0x04,0x02,0x3A,0x6F, +0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03,0x64,0x00,0x00,0x08, +0x49,0x00,0x01,0x97,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03,0x64,0x72,0x04,0x03, +0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0xA4,0x3C, +0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72,0x04,0x02,0x3A,0x6F, +0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03,0x64,0x00,0x00,0x08, +0x49,0x00,0x01,0x78,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03,0x64,0x72,0x04,0x03, +0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0xA4,0x3C, +0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72,0x04,0x02,0x3A,0x6F, +0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03,0x64,0x00,0x00,0x08, +0x49,0x00,0x01,0x59,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03,0x64,0x72,0x04,0x03, +0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0xA4,0x3C, +0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72,0x04,0x02,0x3A,0x6F, +0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03,0x64,0x00,0x00,0x08, +0x49,0x00,0x01,0x3A,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03,0x64,0x72,0x04,0x03, +0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0x94,0x3C, +0x3A,0xFF,0xBF,0xBC,0x64,0x12,0xA4,0x02,0x64,0x22,0x04,0x02,0x3A,0x1F,0x88,0x3C, +0x64,0x12,0x00,0x02,0x9E,0x4A,0x64,0x12,0x00,0x03,0x44,0x10,0x00,0xA4,0x38,0x10, +0x82,0x02,0xDD,0x21,0x3A,0x0F,0x84,0x04,0x64,0x02,0x00,0x43,0x64,0x00,0x00,0x08, +0x64,0x02,0xA4,0x03,0x64,0x12,0x04,0x03,0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0x94,0x04, +0x3A,0x0F,0x80,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0x94,0x3C,0x3A,0xFF,0xBF,0xBC, +0x44,0x10,0x00,0xA4,0x38,0x10,0x82,0x02,0xDD,0x21,0x3A,0xFF,0xBF,0x84,0x3A,0x1F, +0x94,0x04,0x3A,0x0F,0x80,0x04,0x64,0x00,0x00,0x04,0xD5,0x00,0x3A,0x0F,0x80,0x3C, +0x84,0x00,0xD5,0xEB,0x3A,0x0F,0x80,0x3C,0x84,0x01,0xD5,0xE7,0x3A,0x0F,0x80,0x3C, +0x84,0x02,0xD5,0xE3,0x3A,0x0F,0x80,0x3C,0x84,0x03,0xD5,0xDF,0x3A,0x0F,0x80,0x3C, +0x84,0x04,0xD5,0xDB,0x3A,0x0F,0x80,0x3C,0x84,0x05,0xD5,0xD7,0x3A,0x0F,0x80,0x3C, +0x84,0x06,0xD5,0xD3,0x3A,0x0F,0x80,0x3C,0x84,0x07,0xD5,0xCF,0x3A,0x0F,0x80,0x3C, +0x84,0x08,0xD5,0xCB,0x3A,0x0F,0x80,0x3C,0x84,0x09,0xD5,0xC7,0x3A,0x0F,0x80,0x3C, +0x84,0x0A,0xD5,0xC3,0x3A,0x0F,0x80,0x3C,0x84,0x0B,0xD5,0xBF,0x3A,0x0F,0x80,0x3C, +0x84,0x0C,0xD5,0xBB,0x3A,0x0F,0x80,0x3C,0x84,0x0D,0xD5,0xB7,0x3A,0x0F,0x80,0x3C, +0x84,0x0E,0xD5,0xB3,0x3A,0x0F,0x80,0x3C,0x84,0x0F,0xD5,0xAF,0x3A,0x0F,0x80,0x3C, +0xFA,0x00,0xD5,0xAB,0x3A,0x0F,0x80,0x3C,0xFA,0x01,0xD5,0xA7,0x3A,0x0F,0x80,0x3C, +0xFA,0x02,0xD5,0xA3,0x3A,0x0F,0x80,0x3C,0xFA,0x03,0xD5,0x9F,0x3A,0x0F,0x80,0x3C, +0xFA,0x04,0xD5,0x9B,0x3A,0x0F,0x80,0x3C,0xFA,0x05,0xD5,0x97,0x3A,0x0F,0x80,0x3C, +0xFA,0x06,0xD5,0x93,0x3A,0x0F,0x80,0x3C,0xFA,0x07,0xD5,0x8F,0x3A,0x0F,0x80,0x3C, +0xFA,0x08,0xD5,0x8B,0x3A,0x0F,0x80,0x3C,0xFA,0x09,0xD5,0x87,0x3A,0x0F,0x80,0x3C, +0xFA,0x0A,0xD5,0x83,0x3A,0x0F,0x80,0x3C,0xFA,0x0B,0x48,0xFF,0xFF,0x7F,0x3A,0x0F, +0x80,0x3C,0xFA,0x0C,0x48,0xFF,0xFF,0x7A,0x3A,0x0F,0x80,0x3C,0xFA,0x0D,0x48,0xFF, +0xFF,0x75,0x3A,0x0F,0x80,0x3C,0xFA,0x0E,0x48,0xFF,0xFF,0x70,0x3A,0x0F,0x80,0x3C, +0x44,0x00,0x00,0x1F,0x48,0xFF,0xFF,0x6A,0x84,0x01,0x64,0x04,0xC0,0x02,0x44,0x02, +0x00,0x01,0x64,0x04,0xE0,0x03,0x45,0xD2,0x21,0xF0,0x3F,0xC8,0x00,0x00,0x45,0xF2, +0x5F,0xF4,0x84,0x00,0x42,0x0E,0x00,0x21,0x49,0x00,0x00,0x42,0x49,0x00,0x03,0x4D, +0xD5,0x00,0x92,0x00,0xFC,0x00,0x44,0x32,0x11,0x00,0x44,0x22,0x10,0x00,0x9A,0x9A, +0x44,0x02,0x10,0x00,0x44,0x11,0x10,0x00,0x49,0xFF,0xFB,0x22,0x49,0xFF,0xD9,0x08, +0x44,0x32,0x05,0xB0,0x44,0x22,0x00,0x00,0x9A,0x9A,0x44,0x02,0x00,0x00,0x44,0x11, +0x00,0x00,0x49,0xFF,0xFB,0x15,0x49,0xFF,0xD8,0xFB,0x44,0x02,0x11,0x00,0x44,0x22, +0x40,0x1C,0x84,0x20,0x8A,0x40,0x49,0x00,0x03,0x7F,0xFC,0x80,0x84,0x00,0x64,0x05, +0xE4,0x03,0x84,0x1F,0x64,0x03,0x04,0x03,0x64,0x03,0x24,0x03,0x64,0x12,0x24,0x02, +0x44,0x0F,0x0F,0xFF,0xFE,0x0E,0x64,0x02,0x24,0x03,0x64,0x12,0x00,0x02,0x84,0x18, +0xFE,0x0E,0x64,0x02,0x00,0x03,0x64,0x00,0x00,0x08,0xDD,0x9E,0xFC,0x00,0x49,0xFF, +0xFF,0xE7,0x64,0x00,0x80,0x02,0x92,0x1E,0xC0,0x06,0x64,0x00,0x84,0x02,0x96,0x2E, +0xC0,0x02,0xD5,0x00,0x49,0xFF,0xFF,0xB8,0x64,0x12,0x00,0x43,0x64,0x00,0x00,0x08, +0x64,0x00,0x00,0x08,0xFC,0x80,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E, +0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0x44,0x04,0x80,0x04,0xA6,0x01,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x05,0x84,0x21,0xAE,0x40,0xDD,0x9E,0x44,0x04,0x80,0x01, +0xA6,0x00,0xC8,0x05,0xFC,0x00,0x49,0xFF,0xD4,0x60,0xFC,0x80,0xDD,0x9E,0xFC,0x00, +0x44,0x04,0x80,0x00,0xA6,0x03,0x49,0xFF,0xD9,0x16,0xFC,0x80,0x44,0x14,0x80,0x04, +0x44,0x04,0x80,0x84,0xA6,0x88,0xB4,0x00,0x96,0x06,0xFE,0x16,0xC0,0x03,0x84,0x01, +0xAE,0x08,0xDD,0x9E,0xFC,0x00,0x44,0x04,0x80,0x00,0xA6,0x03,0x49,0xFF,0xA6,0x5B, +0xFC,0x80,0x44,0x14,0x80,0x14,0xA6,0x08,0x96,0x06,0xC0,0x09,0x84,0x01,0xAE,0x08, +0x44,0x14,0x80,0x94,0xB4,0x01,0x58,0x00,0x00,0x01,0xB6,0x01,0x44,0x14,0x80,0x14, +0xA6,0x08,0x92,0x01,0x96,0x06,0xC0,0x09,0x84,0x02,0xAE,0x08,0x44,0x14,0x80,0x94, +0xB4,0x01,0x58,0x00,0x00,0x02,0xB6,0x01,0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xD2,0xB6, +0xFC,0x80,0x44,0x04,0x80,0x04,0xA6,0x02,0x92,0x02,0x96,0x06,0xC0,0x05,0x44,0x04, +0x80,0x06,0x84,0x24,0xAE,0x40,0xDD,0x9E,0x44,0x14,0x80,0x04,0xA6,0x0B,0x92,0x01, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x07,0x84,0x42,0xAE,0x80,0xA6,0x0B,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x07,0x84,0x21,0xAE,0x40,0xDD,0x9E,0x44,0x04,0x80,0x08, +0xB4,0x20,0x92,0x24,0x96,0x46,0xC1,0x03,0xFA,0x20,0xAE,0x40,0xB4,0x00,0x84,0x21, +0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x08,0xAE,0x40,0x3E,0x17,0xF4,0xE9,0x44,0x04, +0x80,0x08,0xB4,0x20,0x92,0x21,0x96,0x46,0xC1,0x06,0x84,0x22,0xAE,0x40,0x84,0x21, +0x3E,0x17,0xF4,0xE8,0xB4,0x00,0x92,0x02,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x08, +0x84,0x24,0xAE,0x40,0xDD,0x9E,0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xD1,0x66,0xFC,0x80, +0xFC,0x00,0x49,0xFF,0xD1,0xC1,0xFC,0x80,0xFC,0x00,0x49,0xFF,0x9A,0x76,0xFC,0x80, +0x44,0x14,0x80,0x08,0xB4,0x01,0x92,0x08,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x09, +0x84,0x41,0xAE,0x80,0xB4,0x01,0x92,0x09,0x96,0x06,0xC0,0x0F,0x44,0x04,0x80,0x09, +0x84,0x22,0xAE,0x40,0x84,0x01,0x3E,0x07,0xF4,0xF2,0x44,0x04,0x50,0x28,0xB4,0x00, +0x92,0x06,0x96,0x1F,0x3E,0x07,0xF4,0xF1,0xDD,0x9E,0xFC,0x00,0x44,0x04,0x80,0x08, +0xB4,0x00,0x92,0x12,0x96,0x06,0xC0,0x58,0x44,0x14,0x80,0x88,0xB4,0x01,0x92,0x12, +0x96,0x06,0xC0,0x52,0x44,0x04,0x80,0x0A,0x84,0x44,0xAE,0x80,0xB4,0x41,0x44,0x0D, +0xFF,0xFF,0xFE,0x16,0x44,0x67,0x30,0x04,0xB6,0x01,0xB4,0x26,0x84,0x1E,0xFE,0x0E, +0xB6,0x06,0x84,0x0A,0x49,0xFF,0xA6,0x7B,0xB4,0x06,0x58,0x00,0x00,0x01,0xB6,0x06, +0x84,0x0A,0x49,0xFF,0xA6,0x74,0x49,0xFF,0xD6,0x7D,0x44,0x00,0x00,0x87,0x49,0xFF, +0xD6,0x72,0x44,0x17,0x30,0x10,0xB4,0x01,0x96,0x06,0xC0,0xFE,0x44,0x17,0x30,0x10, +0xB4,0x01,0x92,0x03,0x96,0x06,0x5A,0x00,0x01,0xFD,0x44,0x67,0x30,0x04,0x84,0x0A, +0x49,0xFF,0xA6,0x5D,0xB4,0x26,0x84,0x1E,0xFE,0x0E,0xB6,0x06,0x84,0x0A,0x49,0xFF, +0xA6,0x56,0xB4,0x06,0x58,0x00,0x00,0x01,0xB6,0x06,0x84,0x0A,0x49,0xFF,0xA6,0x4F, +0x44,0x14,0x80,0x88,0x84,0x01,0x3E,0x07,0xF4,0xF0,0xB4,0x41,0x44,0x02,0x00,0x00, +0xFE,0x17,0xB6,0x01,0x44,0x04,0x80,0x0A,0x84,0x22,0xAE,0x40,0x3C,0x0D,0xFD,0x3B, +0x8C,0x01,0x3C,0x0F,0xFD,0x3B,0x44,0x04,0x80,0x08,0x84,0x21,0xB4,0x00,0x92,0x10, +0x96,0x06,0xC0,0x0C,0x44,0x04,0x80,0x88,0xB4,0x00,0x92,0x10,0x96,0x06,0xC0,0x06, +0x44,0x04,0x80,0x0A,0x3E,0x17,0xF4,0xF3,0xAE,0x40,0x44,0x04,0x80,0x08,0xB4,0x00, +0x92,0x11,0x96,0x06,0xC0,0x0B,0x44,0x04,0x80,0x88,0xB4,0x00,0x92,0x11,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x0A,0x84,0x22,0xAE,0x40,0xFC,0x80,0x44,0x14,0x80,0x08, +0x84,0x41,0xB4,0x01,0x92,0x18,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0B,0xAE,0x80, +0x3E,0x27,0xF4,0xB5,0xB4,0x01,0x92,0x19,0x96,0x06,0xC0,0x1D,0x44,0x04,0x80,0x0B, +0x84,0x22,0xAE,0x40,0x84,0x00,0x3E,0x07,0xF4,0xB5,0x46,0x00,0x00,0x81,0x44,0x1F, +0xFE,0xFF,0x50,0x30,0x00,0x70,0xB4,0x43,0xFE,0x56,0xB6,0x23,0xB4,0x43,0x44,0x1E, +0xFF,0xFF,0xFE,0x56,0xB6,0x23,0xB4,0x43,0x46,0x1F,0xEF,0xFF,0x50,0x10,0x8F,0xFF, +0xFE,0x56,0xB6,0x23,0x44,0x04,0x80,0x08,0xB4,0x00,0x92,0x1A,0x96,0x06,0xC0,0x0E, +0x44,0x14,0x70,0x08,0xB4,0x01,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00,0x78,0x23, +0xB6,0x01,0x44,0x04,0x80,0x0B,0x84,0x24,0xAE,0x40,0x44,0x04,0x80,0x08,0xB4,0x00, +0x92,0x1B,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0B,0x84,0x28,0xAE,0x40,0xDD,0x9E, +0x44,0x04,0x80,0x04,0xA6,0x02,0x92,0x03,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x06, +0x84,0x28,0xAE,0x40,0xDD,0x9E,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x10,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x0E,0x84,0x41,0xAE,0x80,0xB4,0x01,0x92,0x11,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x0E,0x84,0x22,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01, +0x92,0x12,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0E,0x84,0x44,0xAE,0x80,0xB4,0x01, +0x92,0x13,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0E,0x84,0x28,0xAE,0x40,0x44,0x14, +0x80,0x0C,0xB4,0x01,0x92,0x14,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0E,0xFA,0x40, +0xAE,0x80,0xB4,0x01,0x92,0x15,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0E,0xFA,0x30, +0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x16,0x96,0x06,0xC0,0x06,0x44,0x04, +0x80,0x0E,0x44,0x20,0x00,0x40,0xAE,0x80,0xB4,0x01,0x92,0x17,0x96,0x06,0xC0,0x06, +0x44,0x04,0x80,0x0E,0x44,0x1F,0xFF,0x80,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01, +0x92,0x18,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0F,0x84,0x41,0xAE,0x80,0xB4,0x01, +0x92,0x19,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0F,0x84,0x22,0xAE,0x40,0x44,0x14, +0x80,0x0C,0xB4,0x01,0x92,0x1A,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0F,0x84,0x44, +0xAE,0x80,0xB4,0x01,0x92,0x1B,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0F,0x84,0x28, +0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x1C,0x96,0x06,0xC0,0x05,0x44,0x04, +0x80,0x0F,0xFA,0x40,0xAE,0x80,0xB4,0x01,0x92,0x1D,0x96,0x06,0xC0,0x05,0x44,0x04, +0x80,0x0F,0xFA,0x30,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x1E,0x96,0x06, +0xC0,0x06,0x44,0x04,0x80,0x0F,0x44,0x20,0x00,0x40,0xAE,0x80,0xB4,0x01,0x4E,0x04, +0x00,0x07,0x44,0x04,0x80,0x0F,0x44,0x1F,0xFF,0x80,0xAE,0x40,0xDD,0x9E,0x44,0x04, +0x80,0x0C,0xB4,0x20,0x96,0x46,0xC1,0x03,0x84,0x21,0xAE,0x40,0xB4,0x00,0x92,0x01, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0C,0x84,0x22,0xAE,0x40,0x44,0x04,0x80,0x0C, +0xB4,0x20,0x92,0x22,0x96,0x46,0xC1,0x03,0x84,0x24,0xAE,0x40,0xB4,0x00,0x92,0x03, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0C,0x84,0x28,0xAE,0x40,0x44,0x04,0x80,0x0C, +0xB4,0x20,0x92,0x24,0x96,0x46,0xC1,0x03,0xFA,0x20,0xAE,0x40,0xB4,0x00,0x92,0x05, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0C,0xFA,0x30,0xAE,0x40,0x44,0x04,0x80,0x0C, +0xB4,0x20,0x92,0x26,0x96,0x46,0xC1,0x04,0x44,0x10,0x00,0x40,0xAE,0x40,0xB4,0x00, +0x92,0x07,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0C,0x44,0x1F,0xFF,0x80,0xAE,0x40, +0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x08,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0D, +0x84,0x41,0xAE,0x80,0xB4,0x01,0x92,0x09,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0D, +0x84,0x22,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x0A,0x96,0x06,0xC0,0x05, +0x44,0x04,0x80,0x0D,0x84,0x44,0xAE,0x80,0xB4,0x01,0x92,0x0B,0x96,0x06,0xC0,0x05, +0x44,0x04,0x80,0x0F,0x84,0x28,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x0C, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0D,0xFA,0x40,0xAE,0x80,0xB4,0x01,0x92,0x0D, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0D,0xFA,0x30,0xAE,0x40,0x44,0x14,0x80,0x0C, +0xB4,0x01,0x92,0x0E,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0D,0x44,0x20,0x00,0x40, +0xAE,0x80,0xB4,0x01,0x92,0x0F,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0D,0x44,0x1F, +0xFF,0x80,0xAE,0x40,0xDD,0x9E,0x44,0x04,0x80,0x00,0x44,0x14,0x80,0x80,0xA6,0x80, +0xB4,0x21,0x96,0x46,0xFE,0x56,0xC1,0x03,0x84,0x21,0xAE,0x40,0xA6,0x00,0x40,0x10, +0x04,0x09,0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x01,0x96,0x06,0xFE,0x0E,0xC0,0x05, +0x44,0x04,0x80,0x00,0x84,0x22,0xAE,0x40,0x44,0x14,0x80,0x00,0xA6,0x08,0x40,0x20, +0x08,0x09,0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x02,0x96,0x06,0xFE,0x16,0xC0,0x03, +0x84,0x04,0xAE,0x08,0xA6,0x08,0x40,0x10,0x0C,0x09,0x44,0x04,0x80,0x80,0xB4,0x00, +0x92,0x03,0x96,0x06,0xFE,0x0E,0xC0,0x05,0x44,0x04,0x80,0x00,0x84,0x28,0xAE,0x40, +0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E, +0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xFC,0x00,0x44,0x07,0x10,0x18,0x84,0x20,0xAE,0x41, +0x44,0x16,0x20,0x04,0x46,0x00,0x10,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0x44,0x00, +0x01,0x00,0x84,0x20,0x49,0xFF,0x9A,0xC4,0x44,0x00,0x01,0x00,0x84,0x20,0x49,0xFF, +0x9A,0xCE,0x44,0x00,0x01,0x00,0x84,0x21,0x49,0xFF,0x9A,0xD8,0x49,0xFF,0xE2,0xC6, +0x44,0x14,0x30,0x34,0x46,0x00,0x04,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0x49,0xFF, +0xD5,0x1F,0x49,0xFF,0xD5,0x57,0x49,0x00,0x12,0x46,0x49,0xFF,0xD7,0xAB,0x49,0xFF, +0xCD,0x37,0x84,0x00,0xFC,0x80,0x00,0x00,0x80,0x60,0x54,0xF1,0x00,0x03,0xE9,0x1B, +0xE6,0x44,0xE9,0x19,0x54,0x41,0x80,0x03,0x54,0x50,0x80,0x03,0xDC,0x14,0xC5,0x0A, +0x52,0x52,0x80,0x04,0x9A,0x95,0x99,0x69,0x08,0x40,0x80,0x01,0x18,0x41,0x80,0x01, +0xD9,0xFC,0x40,0x51,0x08,0x09,0xC5,0x07,0x95,0x6A,0x99,0x69,0x96,0x8F,0xA3,0x09, +0xAB,0x19,0xD9,0xFE,0xC2,0x07,0x99,0x4A,0x08,0x40,0x80,0x01,0x18,0x41,0x80,0x01, +0xD9,0xFC,0xDD,0x9E,0x80,0x60,0xE6,0x44,0xE9,0x1A,0x54,0x51,0x80,0x03,0xC5,0x08, +0x52,0x52,0x80,0x04,0x9A,0x95,0x99,0x6B,0x18,0x11,0x80,0x01,0xDB,0xFE,0x40,0x51, +0x08,0x09,0x96,0x8F,0xC5,0x0C,0x95,0x6A,0x99,0x5D,0x96,0x48,0x40,0x40,0xA0,0x08, +0xFE,0x67,0x40,0x40,0xC0,0x08,0xFE,0x67,0xAA,0x59,0xDB,0xFF,0x99,0x5A,0xD3,0x04, +0x18,0x11,0x80,0x01,0xD5,0xFD,0xDD,0x9E,0x00,0x00,0x00,0x00,0x00,0x00,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, +0xFC,0x45,0x3C,0x13,0xF7,0xC7,0x84,0x00,0x3E,0x07,0xF1,0x5C,0x44,0x0F,0xFF,0xBF, +0xFE,0x0E,0x3C,0x13,0xF7,0xC5,0x3C,0x0B,0xF7,0xC7,0x84,0x1D,0xFE,0x0E,0x3C,0x0B, +0xF7,0xC5,0x49,0xFF,0xDA,0xDF,0x5A,0x00,0x01,0x06,0x3C,0x0C,0x02,0xB6,0x5A,0x08, +0x01,0x0A,0x3C,0x03,0xF7,0xC5,0x58,0x00,0x00,0x03,0x3C,0x0B,0xF7,0xC5,0x49,0xFF, +0xDA,0xD4,0x3C,0x23,0xF7,0xC5,0x54,0x01,0x00,0x01,0xC0,0x06,0x3C,0x13,0xF8,0x9C, +0x8C,0x21,0x3C,0x1B,0xF8,0x9C,0x3C,0x03,0xF8,0x9C,0xE6,0x03,0xE9,0x08,0x84,0x1E, +0xFE,0x16,0x3C,0x0B,0xF7,0xC5,0x84,0x00,0x3C,0x0B,0xF8,0x9C,0x3C,0xF7,0xF9,0xF5, +0x5E,0xF7,0x80,0x5A,0xE8,0x0B,0x3C,0x03,0xF9,0xF4,0x50,0x00,0x00,0x59,0x96,0x01, +0x5C,0xF0,0x00,0xB3,0xE8,0x03,0x49,0xFF,0xDA,0x9E,0x49,0xFF,0xED,0x6A,0x80,0xC0, +0x49,0xFF,0xA2,0x19,0x49,0xFF,0xB0,0xB8,0x80,0x06,0x49,0xFF,0xAD,0x0D,0x3C,0x0D, +0xFB,0xE0,0x5A,0x08,0x01,0x0C,0x49,0xFF,0xDA,0x8B,0xC8,0x08,0x49,0xFF,0xDA,0x8F, +0xC8,0x05,0x49,0xFF,0x9F,0xC4,0x48,0x00,0x02,0xAB,0x49,0xFF,0xAD,0x97,0x44,0x02, +0x13,0x58,0x2E,0x17,0xF1,0x5C,0x49,0xFF,0xB8,0x76,0x44,0x02,0x2F,0x88,0xB6,0x1F, +0x44,0x02,0x30,0xC8,0xF0,0x81,0x3C,0x10,0x05,0x95,0x3C,0x00,0x05,0x93,0x3C,0x20, +0x05,0x97,0x3C,0x30,0x05,0x99,0x3C,0x90,0x05,0x92,0x3C,0x70,0x05,0x94,0x3C,0x50, +0x05,0x96,0x3C,0x40,0x05,0x98,0x40,0x00,0x40,0x08,0x40,0x10,0xC0,0x08,0x40,0x21, +0x40,0x08,0x40,0x31,0xC0,0x08,0x40,0x00,0x24,0x04,0xFE,0x7F,0xFE,0xAF,0xFE,0xE7, +0xF6,0x82,0x44,0x42,0x33,0x90,0x44,0x52,0x13,0x4C,0x49,0x00,0x03,0x63,0x80,0x06, +0x44,0x12,0x13,0x58,0x2E,0x27,0xF1,0x5C,0x3C,0x34,0x05,0x92,0x49,0xFF,0xB8,0xB7, +0x2E,0x00,0x0B,0x32,0x3C,0x17,0xF7,0xA6,0x50,0x00,0x00,0x64,0x44,0x90,0x00,0x64, +0xFE,0x0C,0x40,0x00,0x24,0x16,0x44,0x72,0x33,0x88,0x40,0x90,0x00,0x11,0x81,0x47, +0x84,0xC0,0x02,0x05,0x00,0x84,0xC0,0x08,0x00,0x05,0x01,0x22,0x80,0x26,0x00,0x25, +0x01,0x0E,0x49,0xFF,0xAD,0xD6,0x8C,0xC1,0x97,0xB0,0x50,0xA5,0x01,0x24,0x5A,0x68, +0x0B,0xF2,0x84,0x60,0x44,0x62,0x13,0x4C,0x44,0x82,0x34,0xA8,0x2E,0x07,0xF1,0x5C, +0xE2,0x60,0xE8,0x1B,0x44,0x00,0x00,0x38,0x80,0x26,0x42,0x11,0x80,0x73,0x00,0x10, +0x80,0x3B,0x50,0xA1,0x80,0x01,0x5A,0x18,0xFF,0x25,0x80,0x26,0x42,0x15,0x00,0x73, +0xA6,0x8C,0x5A,0x28,0x01,0x1F,0xF2,0x85,0xF3,0x84,0x49,0xFF,0xBE,0x1F,0xE6,0x0B, +0x80,0x20,0xF3,0x04,0xF2,0x05,0xE9,0x09,0x49,0xFF,0xB2,0x6E,0x3C,0x0D,0xFB,0xD6, +0x96,0x1E,0xC0,0x12,0x84,0x04,0xD5,0x11,0x44,0x00,0x01,0x24,0x80,0x88,0x42,0x40, +0x80,0x73,0xA6,0x27,0x58,0x00,0x00,0x10,0xAE,0x27,0x80,0x03,0x49,0xFF,0xAD,0x99, +0x54,0x35,0x00,0xFF,0xD5,0xCC,0x84,0x03,0x49,0xFF,0xBD,0xB6,0x3C,0x0C,0x02,0xB6, +0xC8,0x06,0x85,0x40,0x44,0x72,0x33,0x90,0x80,0xCA,0xD5,0x2A,0x84,0xC0,0x00,0x03, +0x81,0x0E,0x5A,0x08,0x01,0x0E,0x00,0x03,0x81,0x0D,0xC8,0x0A,0x22,0xF3,0x80,0x8A, +0x5E,0xF7,0x80,0xC9,0xE9,0x05,0x80,0x06,0x84,0x22,0x49,0xFF,0xBD,0xAB,0x8C,0xC1, +0x97,0xB0,0x50,0x73,0x81,0x24,0x5A,0x68,0x0B,0xEC,0xD5,0xE4,0x00,0x13,0x81,0x06, +0x5A,0x10,0x01,0x2C,0x00,0x03,0x81,0x05,0x5A,0x08,0x02,0x04,0x48,0x00,0x01,0x18, +0x8D,0x41,0x50,0x73,0x81,0x24,0x5A,0xA8,0x0B,0x04,0x48,0x00,0x01,0x30,0x54,0x05, +0x00,0xFF,0xF0,0x84,0xB0,0x47,0x80,0x07,0x84,0x40,0x49,0xFF,0xA0,0x4B,0x00,0x03, +0x81,0x05,0xE6,0x05,0xE8,0xE8,0x44,0xF0,0xDF,0xB4,0x38,0x07,0x82,0x02,0x40,0xF0, +0x3C,0x00,0xDD,0x0F,0xB8,0xFF,0xFF,0xFF,0x8A,0x00,0x00,0x00,0xF8,0x00,0x00,0x00, +0x6C,0x00,0x00,0x00,0xB0,0x01,0x00,0x00,0x3C,0x0D,0xFB,0xD6,0x8C,0xC1,0x54,0x00, +0x08,0x00,0x97,0xB0,0xC0,0x17,0xF0,0x07,0x22,0x23,0x80,0x86,0x00,0x00,0x00,0x14, +0x5C,0xF0,0x00,0x21,0xE9,0x0A,0x3C,0x00,0x05,0x93,0x91,0x21,0x96,0xC3,0xE1,0x23, +0x40,0x04,0xBC,0x1A,0x40,0x90,0x00,0x11,0xE0,0x49,0xF0,0x04,0xE8,0x04,0x84,0x20, +0xD5,0x02,0xF0,0x04,0x49,0xFF,0xBD,0x5F,0xF0,0x04,0x49,0xFF,0xBD,0x81,0x81,0x00, +0xF0,0x04,0x49,0xFF,0xBD,0x3C,0xE2,0x08,0x84,0x21,0xF0,0x04,0xE8,0x0E,0xD5,0x0C, +0x00,0x03,0x81,0x06,0x5A,0x08,0x01,0x66,0xF0,0x04,0x8C,0xC1,0x84,0x20,0x49,0xFF, +0xBD,0x4A,0x97,0xB0,0xF0,0x04,0x84,0x22,0x49,0xFF,0xBD,0x3C,0xD5,0x9C,0x00,0x13, +0x81,0x06,0x5A,0x18,0x02,0x04,0x48,0x00,0x00,0xA0,0x5A,0x18,0x01,0x95,0x3C,0x0D, +0xFB,0xD6,0x8C,0xC1,0x54,0x00,0x08,0x00,0x97,0xB0,0xC0,0x17,0xF0,0x07,0x22,0x23, +0x80,0x86,0x00,0x00,0x00,0x14,0x5C,0xF0,0x00,0x21,0xE9,0x0A,0x3C,0x00,0x05,0x93, +0x91,0x21,0x96,0xC3,0xE1,0x23,0x40,0x04,0xBC,0x1A,0x40,0x90,0x00,0x11,0xE0,0x49, +0xF0,0x04,0xE8,0x04,0x84,0x20,0xD5,0x02,0xF0,0x04,0x49,0xFF,0xBD,0x1C,0xF0,0x04, +0x49,0xFF,0xBD,0x3E,0x81,0x00,0xF0,0x04,0x49,0xFF,0xBC,0xF9,0xE2,0x08,0x4E,0xF2, +0xFF,0x6B,0xF0,0x04,0x84,0x22,0x49,0xFF,0xBD,0x05,0xD5,0x1D,0xF0,0x04,0x84,0x20, +0x49,0xFF,0xBD,0x09,0x00,0x03,0x81,0x06,0x5A,0x08,0x02,0x14,0x84,0x21,0xF0,0x04, +0x49,0xFF,0xBD,0x01,0xF0,0x04,0x49,0xFF,0xBD,0x23,0x81,0x00,0xF0,0x04,0x49,0xFF, +0xBC,0xEC,0xE2,0x08,0xF0,0x04,0xE9,0x59,0x84,0x23,0x49,0xFF,0xBC,0xEB,0xD5,0x63, +0x8C,0xC1,0x97,0xB0,0xF0,0x04,0x84,0x20,0x49,0xFF,0xBC,0xED,0x48,0xFF,0xFF,0x44, +0x5A,0x00,0x02,0x04,0x48,0xFF,0xFF,0x40,0xF0,0x04,0x84,0x21,0x49,0xFF,0xBC,0xE3, +0x44,0x10,0x01,0x2C,0x44,0x20,0x02,0xBC,0x84,0x65,0x80,0x07,0x49,0xFF,0xAF,0x37, +0xF0,0x04,0x49,0xFF,0xBC,0xFD,0x81,0x00,0xF0,0x04,0x49,0xFF,0xBC,0xC6,0xE2,0x08, +0xE8,0x0B,0x44,0x10,0x01,0x24,0x44,0x02,0x34,0xAB,0x42,0x05,0x04,0x73,0xA6,0x46, +0x58,0x10,0x80,0x08,0xAE,0x46,0x44,0x80,0x01,0x24,0x44,0x02,0x34,0xB0,0x42,0x05, +0x20,0x73,0xB4,0x00,0x92,0x0B,0x96,0x06,0x5A,0x08,0x01,0x2E,0x80,0x07,0x49,0xFF, +0xBF,0xBD,0x5A,0x08,0x01,0x29,0xF0,0x04,0x49,0xFF,0xBC,0xFD,0x80,0x07,0x84,0x20, +0x80,0x48,0xD5,0x19,0x00,0x03,0x81,0x06,0x5A,0x08,0x02,0x1A,0x84,0x21,0xF0,0x04, +0x49,0xFF,0xBC,0xA9,0xF0,0x04,0x49,0xFF,0xBC,0xCB,0x44,0x12,0x15,0xC0,0x38,0x10, +0xA8,0x00,0xE2,0x20,0xE8,0x10,0xF0,0x04,0x49,0xFF,0xBC,0xE5,0x80,0x07,0x84,0x20, +0x44,0x20,0x01,0x24,0x49,0xFF,0xF9,0xC0,0x48,0xFF,0xFE,0xEE,0xF0,0x04,0x84,0x20, +0x49,0xFF,0xBC,0x91,0x8C,0xC1,0x97,0xB0,0x48,0xFF,0xFE,0xE6,0x02,0x53,0x80,0x81, +0x44,0x00,0xFF,0xFF,0x4C,0x50,0x3E,0xE6,0x22,0x13,0x80,0x12,0x22,0x03,0x80,0x04, +0x8C,0xA1,0x8A,0x01,0xF0,0x88,0x22,0x13,0x80,0x13,0x22,0x03,0x80,0x05,0x12,0x53, +0x80,0x81,0x8A,0x01,0xF0,0x89,0xB0,0x08,0x49,0xFF,0xB7,0x0F,0x02,0x13,0x80,0x84, +0x88,0x01,0x12,0x03,0x80,0x84,0x48,0xFF,0xFE,0xCD,0x3C,0x13,0xF7,0xC7,0x84,0x1D, +0xFE,0x0E,0xC6,0x03,0x58,0x00,0x00,0x02,0x3C,0x0B,0xF7,0xC7,0x2E,0x00,0x11,0x9C, +0x3E,0x00,0x11,0x9D,0x2E,0x00,0x11,0x9B,0x3E,0x00,0x11,0x9C,0x2E,0x00,0x11,0x9A, +0x3E,0x00,0x11,0x9B,0x2E,0x00,0x11,0x99,0x3E,0x00,0x11,0x9A,0x84,0x00,0x3E,0x60, +0x11,0x99,0x3C,0x0E,0x02,0xB6,0x49,0xFF,0xEC,0x81,0x3C,0x13,0xF7,0xC7,0x5A,0x08, +0x01,0x09,0x54,0x00,0x80,0x01,0x3E,0x07,0xF1,0x56,0x3E,0x07,0xF1,0x57,0xD5,0x05, +0x54,0x00,0x80,0x01,0x3E,0x07,0xF1,0x56,0x96,0x4E,0x84,0x02,0xC9,0x30,0x2E,0x07, +0xF1,0x50,0xE6,0x04,0xE8,0x3C,0x44,0xF0,0xE2,0x64,0x38,0x07,0x80,0x00,0x40,0xF0, +0x3C,0x00,0xDD,0x0F,0x04,0x16,0x4E,0x36,0x2E,0x07,0xF1,0x56,0x5A,0x08,0x01,0x30, +0x3E,0x07,0xF1,0x50,0x2E,0x07,0xF1,0x54,0xD5,0x0D,0x2E,0x07,0xF1,0x56,0x5A,0x00, +0x01,0x06,0x2E,0x07,0xF1,0x57,0x5A,0x08,0x01,0x12,0x2E,0x07,0xF1,0x54,0x5A,0x00, +0xFF,0x1F,0x8C,0x01,0x3E,0x07,0xF1,0x54,0xD5,0x1A,0x2E,0x07,0xF1,0x56,0x5A,0x00, +0x01,0x10,0x2E,0x07,0xF1,0x57,0x5A,0x00,0x01,0x0C,0x84,0x00,0x3E,0x07,0xF1,0x50, +0xD5,0x0E,0x84,0x03,0x3E,0x07,0xF1,0x50,0x2E,0x07,0xF1,0x55,0xD5,0x05,0x2E,0x07, +0xF1,0x55,0x5A,0x00,0xFF,0x05,0x8C,0x01,0x3E,0x07,0xF1,0x55,0x2E,0x07,0xF1,0x50, +0x5A,0x08,0x01,0x06,0x84,0x00,0x3E,0x07,0xF1,0x55,0xD5,0x06,0x84,0x20,0x3E,0x17, +0xF1,0x54,0x5A,0x08,0x03,0xF9,0x49,0xFF,0x9E,0x09,0x49,0xFF,0x9D,0x33,0x49,0xFF, +0xAE,0x7C,0x84,0x00,0x44,0x32,0x30,0x44,0x98,0x83,0x00,0x11,0x00,0x0A,0xC1,0x04, +0x8E,0x21,0x10,0x11,0x00,0x0A,0x8C,0x0C,0x5A,0x08,0x84,0xF8,0x2E,0x07,0xF1,0x50, +0x5A,0x00,0x01,0x19,0xC0,0x07,0x5A,0x08,0x03,0x1B,0x2E,0x07,0xF1,0x55,0xE6,0x1F, +0xD5,0x15,0x49,0xFF,0xD8,0x5C,0x5A,0x00,0x01,0x06,0x49,0xFF,0xD7,0xE9,0x5A,0x08, +0x01,0x05,0x84,0x00,0x84,0x21,0xD5,0x03,0x84,0x00,0x80,0x20,0x49,0xFF,0xCF,0x7D, +0xD5,0x06,0x2E,0x07,0xF1,0x54,0x5C,0xF0,0x00,0xC9,0xE8,0xF4,0xFC,0xC5,0x92,0x00, +0xFC,0x21,0x80,0xC1,0x84,0x20,0x3E,0x17,0xF3,0xFC,0x44,0x10,0x00,0xFF,0x3C,0x1B, +0xF7,0xA8,0x2E,0x10,0x0B,0x40,0x85,0xE3,0x40,0x10,0xBC,0x37,0xE2,0x26,0xF0,0x81, +0x84,0xE0,0xE8,0x30,0x3C,0x13,0xFA,0x03,0xE6,0x2B,0xE8,0x03,0x84,0x00,0xD5,0x27, +0x5C,0xF0,0x80,0x29,0xE9,0x06,0xFA,0x2E,0x49,0xFF,0xAF,0x2E,0x3C,0x0B,0xF7,0xA8, +0x3C,0x03,0xFA,0x03,0x2E,0x10,0x0B,0x40,0xE2,0x01,0xE9,0xF1,0x92,0x01,0x5C,0xF0, +0x00,0x33,0x80,0x20,0xE9,0x03,0x44,0x10,0x00,0x32,0xF0,0x01,0x54,0x10,0xFF,0xFF, +0x49,0xFF,0xAF,0x1A,0x5E,0xF0,0x00,0x5F,0xE9,0x07,0x3C,0xF7,0xF9,0xFC,0x84,0x21, +0x5E,0xF7,0x80,0x5F,0xE8,0x02,0x84,0x22,0x3E,0x17,0xF3,0xFC,0x3C,0x0B,0xF9,0xFC, +0x84,0xE1,0x3C,0x0D,0xFD,0x00,0xC8,0x1B,0x2E,0x10,0x0B,0x40,0x5A,0x78,0x01,0x0A, +0x3C,0x03,0xFA,0x03,0xE2,0x20,0xE8,0x05,0x84,0x01,0x3C,0x0F,0xFD,0x00,0xD5,0x05, +0x84,0x03,0xFE,0x0C,0xE0,0x06,0xE9,0xF9,0x2E,0x07,0xEF,0x16,0x2E,0x10,0x0B,0x42, +0xE2,0x20,0xE8,0x13,0x84,0x00,0x3C,0x0F,0xFD,0x00,0xD5,0x0F,0x49,0xFF,0xD7,0x78, +0xC0,0x04,0x44,0xF0,0x01,0xC2,0xD5,0x03,0x44,0xF0,0x00,0x96,0x5A,0x70,0x01,0x06, +0x3C,0x17,0xF9,0xF5,0xE0,0x2F,0xD5,0xEE,0x2E,0x17,0xF1,0x51,0x3C,0x0D,0xFD,0x00, +0xE6,0x24,0xE8,0x40,0x44,0xF0,0xE4,0x44,0x38,0x17,0x84,0x00,0x40,0xF0,0xBC,0x00, +0x4A,0x00,0x3C,0x00,0x04,0x28,0x42,0x68,0x5A,0x08,0x01,0x0E,0x2E,0x27,0xF3,0xFA, +0x84,0x20,0x3E,0x17,0xF3,0xFA,0x5A,0x20,0xFF,0x2E,0x3E,0x07,0xF1,0x51,0x3E,0x07, +0xF3,0xFD,0xD5,0x28,0x84,0x00,0x3E,0x07,0xF3,0xFA,0xD5,0x24,0x2E,0x17,0xF3,0xFD, +0x2E,0x20,0x0B,0x44,0xE2,0x22,0xE8,0x05,0x8C,0x21,0x3E,0x17,0xF3,0xFD,0xD5,0x1A, +0x84,0x22,0xC0,0x0E,0xD5,0x17,0xC8,0x11,0x2E,0x27,0xF4,0x04,0x2E,0x10,0x0B,0x46, +0x8C,0x41,0x96,0x90,0xE2,0x22,0x3E,0x27,0xF4,0x04,0xE8,0x0C,0x84,0x23,0x3E,0x17, +0xF1,0x51,0x3E,0x07,0xF4,0x04,0xD5,0x06,0x84,0x01,0xD5,0x02,0x84,0x00,0x3E,0x07, +0xF1,0x51,0x2E,0x17,0xF3,0xFC,0x2E,0x07,0xF3,0xFB,0x5A,0x18,0x01,0x04,0x8C,0x04, +0xD5,0x0D,0x5A,0x18,0x02,0x09,0x3C,0x13,0xFA,0x03,0x5C,0xF0,0x80,0x38,0xE9,0x03, +0x8E,0x01,0xD5,0x04,0xE6,0xC4,0xE9,0xFD,0x8C,0x01,0x3E,0x07,0xF3,0xFB,0x2E,0x07, +0xF3,0xFB,0x80,0x20,0x96,0x02,0x5E,0xF0,0x00,0x3D,0xE9,0x04,0x44,0x10,0x00,0x3C, +0xD5,0x05,0x96,0x0A,0x4E,0x04,0x00,0x03,0x84,0x20,0x96,0x8A,0x5E,0xF1,0x00,0x29, +0x3E,0x17,0xF3,0xFB,0xE9,0x05,0x49,0xFF,0xAE,0x96,0x84,0x00,0xD5,0x09,0x2E,0x07, +0xF1,0x51,0xC0,0x06,0xC2,0x04,0x8E,0x21,0x3E,0x17,0xF3,0xFB,0x84,0x01,0xFC,0xA1, +0xFC,0x40,0x51,0xFF,0xFD,0x58,0xB1,0x8E,0x80,0xE4,0x3A,0x03,0x0C,0x20,0x00,0x03, +0x00,0x0C,0x44,0x12,0x00,0x88,0xF0,0x82,0xB0,0x19,0x81,0x25,0x3A,0x20,0x94,0x00, +0x3A,0x20,0x14,0x20,0xA4,0x34,0x3C,0x6D,0xFB,0xD6,0x54,0x63,0x20,0x00,0x4E,0x63, +0x04,0x6E,0xF0,0x84,0x44,0x10,0x00,0xFF,0x84,0x4B,0xB0,0x16,0xF7,0x8A,0x49,0xFF, +0xF7,0xDB,0xB0,0x32,0x80,0x26,0x44,0x20,0x01,0xE0,0x49,0xFF,0xF7,0xD5,0x3C,0x0D, +0xFB,0xD6,0x83,0x89,0x54,0x00,0x20,0x00,0xC0,0x13,0xF0,0x04,0xF1,0x02,0x90,0x01, +0xF0,0x84,0x84,0x06,0xFE,0x44,0x5E,0xF0,0x81,0xF9,0x80,0x01,0xE8,0x06,0x84,0x25, +0x40,0x00,0x04,0x16,0x96,0x00,0xD5,0x03,0x44,0x00,0x00,0x64,0xF0,0x82,0x3C,0x03, +0xF7,0xC7,0x96,0x36,0xC0,0x0B,0xF1,0x02,0x00,0x0F,0x80,0x08,0x5C,0xF0,0x80,0x55, +0xE8,0x03,0x44,0x00,0x00,0x55,0x96,0x00,0xF0,0x82,0x04,0x2F,0x80,0xB2,0x2E,0x37, +0xDE,0x16,0x51,0xEF,0x80,0x50,0x2E,0x07,0xDE,0x17,0xF0,0x86,0xA6,0x10,0x94,0x02, +0x88,0x02,0xF0,0x88,0x84,0x00,0xF0,0x83,0xF0,0x08,0x4C,0x20,0x00,0xAE,0x84,0x80, +0xA7,0x55,0x00,0x91,0x00,0x04,0x80,0x04,0xB1,0xD9,0x44,0xAF,0xFF,0xDF,0x38,0x12, +0x1C,0x00,0x99,0xBC,0x88,0x25,0x96,0x48,0xE2,0x23,0x20,0x63,0x00,0x01,0xE8,0x25, +0x88,0xC9,0x04,0x8F,0x80,0x06,0x97,0xB0,0xE2,0xC8,0xE8,0x1F,0x04,0x8F,0x80,0xB3, +0x88,0x28,0x42,0x13,0x0C,0x73,0xA7,0x88,0x40,0x13,0x28,0x02,0x5A,0x10,0x1F,0x16, +0x97,0xA7,0x84,0x20,0x54,0x80,0x80,0xFF,0x4C,0x80,0x40,0x07,0x38,0x6F,0x00,0x08, +0x8C,0x01,0x96,0x00,0xD5,0x0A,0x38,0x8F,0x04,0x00,0x4C,0x83,0x00,0x07,0x8C,0x21, +0x54,0xF0,0x80,0xFF,0xE2,0x0F,0xE8,0xEF,0x8C,0x82,0x5A,0x48,0x10,0xD2,0x42,0x54, +0x8C,0x73,0x04,0x1F,0x80,0xB4,0x95,0x69,0x88,0x25,0xF1,0x87,0x84,0xC0,0x4C,0x60, +0x00,0x69,0x9D,0xF1,0x96,0x78,0xF1,0x85,0x80,0xA1,0xD0,0x61,0x38,0x1F,0x18,0x00, +0x44,0x42,0x2F,0xF0,0x44,0x72,0x2F,0xF0,0x38,0xF2,0x04,0x00,0x38,0x4F,0x14,0x00, +0x38,0x83,0x90,0x00,0x5A,0xF0,0xFF,0x51,0x5A,0x80,0xFF,0x4F,0xF7,0x07,0x81,0x3C, +0x22,0xA3,0x80,0x00,0x44,0x70,0x00,0x64,0x42,0xA5,0x1C,0x24,0x44,0x70,0x00,0x38, +0x42,0x97,0x9C,0x73,0x50,0x94,0x80,0x30,0x00,0xF4,0x80,0x07,0x00,0x94,0x80,0x08, +0x04,0x7F,0x80,0xB4,0x42,0xF4,0x8C,0x73,0x38,0x93,0xBD,0x11,0x44,0xF0,0x00,0x38, +0x80,0xFC,0x42,0x74,0x3C,0x73,0x50,0x83,0x80,0x30,0x00,0xF4,0x00,0x07,0x00,0x84, +0x00,0x08,0x04,0x7F,0x80,0xB4,0x42,0xF4,0x0C,0x73,0x38,0x83,0xBD,0x11,0x40,0x95, +0x25,0x37,0x40,0xA5,0x21,0x57,0xE3,0x49,0xE8,0x04,0x54,0x94,0x80,0xFF,0xD5,0x03, +0x54,0x95,0x00,0xFF,0xF7,0x03,0xE6,0xF4,0xE8,0x17,0x04,0xAF,0x80,0x03,0x85,0x03, +0xB1,0xE3,0x42,0x75,0x20,0x73,0xE2,0x24,0x81,0x04,0x40,0x80,0xBC,0x1B,0xE2,0x81, +0x40,0x12,0x3C,0x1A,0xAE,0x79,0xF1,0x03,0x10,0x83,0x80,0x00,0x8C,0x21,0x96,0x48, +0x10,0x93,0x80,0x02,0xF1,0x83,0x8C,0xA1,0x97,0x68,0xD5,0xA0,0xF6,0x05,0xD5,0x98, +0x8C,0x44,0x48,0xFF,0xFF,0x53,0x3C,0x03,0xF7,0xA7,0x5C,0xF0,0x00,0x24,0x84,0x00, +0xE9,0x16,0x80,0x3C,0x84,0x00,0x08,0x20,0x80,0x3E,0x2E,0x37,0xEF,0x15,0x80,0xA0, +0xD2,0x0B,0xA7,0x08,0x4C,0x41,0xC0,0x04,0x8C,0x01,0x96,0x00,0x8C,0xA1,0x97,0x68, +0x50,0x10,0x80,0x38,0xD5,0xF6,0x84,0x21,0x40,0x00,0x80,0x06,0xF0,0x85,0x46,0x00, +0x08,0x95,0x85,0x00,0x50,0x00,0x04,0x3F,0x80,0xE8,0xF0,0x89,0x00,0x0E,0x00,0x00, +0xE2,0xE0,0x4E,0xF2,0x01,0x10,0xB0,0x16,0x44,0x90,0x00,0x38,0x38,0x70,0x1C,0x08, +0x9C,0x39,0x96,0x00,0xF0,0x87,0x80,0xC0,0x42,0x03,0xA4,0x24,0xF0,0x88,0x88,0x1C, +0xF0,0x86,0x00,0x0E,0x00,0x00,0xE2,0xC0,0x4E,0xF2,0x00,0xFA,0xF0,0x06,0x00,0x10, +0x00,0x3B,0x5A,0x10,0xFF,0x27,0x80,0x1C,0x42,0x03,0x24,0x73,0x00,0x30,0x00,0x3B, +0x44,0x02,0x33,0x88,0x5A,0x30,0xFF,0x14,0x44,0x20,0x01,0x24,0x80,0x80,0x42,0x40, +0x88,0x73,0x00,0x42,0x01,0x0D,0x5A,0x48,0x02,0x0B,0x80,0x80,0x42,0x41,0x88,0x73, +0x00,0x22,0x01,0x0D,0x5A,0x28,0x02,0x04,0x48,0x00,0x00,0xD6,0x44,0x20,0x01,0x24, +0x42,0x00,0x88,0x73,0x00,0x00,0x01,0x0C,0x5A,0x08,0x01,0x04,0x48,0x00,0x00,0xCC, +0x80,0x1C,0x42,0x03,0x24,0x73,0x00,0x00,0x00,0x3B,0x5A,0x00,0xFF,0x0E,0x44,0x30, +0x01,0x24,0x44,0x22,0x33,0x88,0x42,0x20,0x0C,0x73,0x00,0x21,0x01,0x0C,0x5A,0x28, +0x01,0x04,0x48,0x00,0x00,0xB9,0xE7,0x14,0x4E,0xF2,0x00,0xB6,0xF2,0x06,0x80,0x7C, +0x42,0x33,0x24,0x73,0xA0,0x96,0xA0,0xDE,0x44,0x40,0x00,0x38,0x9A,0xD3,0x4E,0x34, +0x00,0x03,0xFE,0xDA,0x80,0x5C,0x80,0xBC,0x42,0x23,0x90,0x73,0x42,0x53,0x10,0x73, +0xA0,0x97,0xA1,0x2F,0x8A,0x44,0x4E,0x24,0x00,0x03,0xFE,0x92,0xFE,0x94,0x42,0x21, +0x8C,0x73,0xF3,0x09,0xE0,0x62,0xE8,0x16,0xF3,0x05,0x5A,0x30,0x01,0x04,0x48,0x00, +0x00,0x93,0x9C,0xF9,0x80,0x9C,0x42,0x41,0xA4,0x73,0x2E,0x57,0xEF,0x15,0xA6,0xE6, +0x4C,0x51,0xC0,0x8A,0x9C,0xF1,0x80,0x9C,0x42,0x41,0xA4,0x73,0xA6,0xE6,0x4C,0x32, +0xC0,0x83,0xF5,0x08,0xFA,0x68,0xB1,0x32,0x42,0x44,0x0C,0x73,0x88,0xBC,0x8C,0xAC, +0xB6,0xA4,0x80,0xBC,0x42,0x53,0x24,0x73,0x8C,0xAC,0x50,0x32,0x00,0x10,0xA9,0x61, +0xA8,0xA2,0x10,0x72,0x00,0x10,0xF4,0x06,0xAF,0x99,0xB0,0xA3,0x84,0xA0,0x51,0xE2, +0x00,0x30,0xF4,0x03,0xD4,0x19,0x20,0xAF,0x00,0x05,0xA7,0x10,0x4C,0xA2,0x40,0x11, +0x80,0x9C,0x42,0x43,0x24,0x73,0x20,0xA2,0x00,0x35,0xA7,0x11,0x4C,0xA2,0x40,0x09, +0xA7,0x1B,0x00,0xA1,0x00,0x02,0xE3,0x44,0x40,0x45,0x3C,0x1A,0xAF,0x1B,0x8C,0xA1, +0x97,0x68,0x8C,0x43,0xD5,0xE7,0xFA,0x48,0x5A,0x10,0xFF,0x1F,0x5A,0x00,0xFF,0x19, +0x44,0x32,0x33,0x88,0x44,0x50,0x01,0x24,0x80,0x83,0x42,0x40,0x94,0x73,0x42,0x30, +0x14,0x73,0x00,0xA2,0x01,0x0D,0x00,0x51,0x81,0x0D,0xE2,0xAA,0xE9,0x09,0xE3,0x45, +0xE9,0x0D,0x02,0x42,0x00,0x84,0x02,0x31,0x80,0x84,0xE2,0x83,0xE9,0x07,0xB0,0x32, +0x42,0x04,0x08,0x73,0xD5,0x0D,0x5A,0x00,0xFF,0x08,0xB0,0x72,0x42,0x14,0x08,0x73, +0x10,0x00,0x80,0x12,0xD5,0x07,0xB0,0x32,0x42,0x04,0x08,0x73,0x84,0x3F,0x10,0x10, +0x00,0x12,0xFA,0x08,0xB0,0x72,0x42,0x14,0x00,0x73,0x50,0x30,0x80,0x10,0xA6,0x1A, +0x81,0x41,0xF3,0x8B,0x5A,0x00,0xFF,0x14,0x44,0x10,0x01,0x24,0xF2,0x0A,0x42,0x20, +0x04,0x73,0x80,0x02,0xB0,0x53,0x84,0x40,0x49,0xFF,0x9B,0x5C,0xF0,0x13,0xF3,0x0B, +0xB4,0x20,0x14,0x15,0x00,0x03,0x00,0x00,0x00,0x14,0xAE,0x1C,0x50,0x04,0x00,0x01, +0x54,0x80,0x00,0xFF,0x8C,0xC1,0x97,0xB0,0x48,0xFF,0xFF,0x05,0xF7,0x07,0x48,0xFF, +0xFE,0xEF,0xB1,0xB2,0x80,0x06,0x85,0x20,0x4C,0x94,0x00,0x3A,0x50,0x14,0x80,0x01, +0x54,0x90,0x80,0xFF,0x81,0x49,0x4C,0xA4,0x00,0x31,0xFA,0x48,0xB0,0x72,0x42,0x15, +0x08,0x73,0xA0,0xC2,0xA0,0x8A,0xE0,0x43,0xE8,0x23,0xB1,0xDD,0x83,0xC0,0x81,0xE7, +0x3A,0x2F,0x14,0x04,0x3A,0x27,0x94,0x24,0x3A,0x2F,0x0C,0x00,0x80,0xE1,0x3A,0x27, +0x8C,0x20,0x3A,0x23,0x94,0x04,0xF7,0x83,0x80,0xE0,0x3A,0x23,0x94,0x24,0xF2,0x03, +0x3A,0x21,0x0C,0x00,0x3A,0x2F,0x0C,0x20,0xB0,0x9D,0x80,0xE2,0x3A,0x23,0x94,0x04, +0x3A,0x20,0x94,0x24,0xF1,0x03,0x3A,0x27,0x8C,0x00,0x3A,0x20,0x8C,0x20,0x50,0x15, +0x00,0x01,0x54,0xA0,0x80,0xFF,0xD5,0xD0,0x8C,0x18,0xD5,0xC7,0xF0,0x04,0x44,0x10, +0x00,0x64,0xFE,0x04,0xFE,0x0C,0xF0,0x83,0xB0,0x72,0xFA,0x08,0x42,0x14,0x80,0x73, +0xF1,0x8D,0x44,0x90,0x00,0x38,0xF0,0x0D,0x4C,0x60,0x01,0x56,0x00,0x03,0x00,0x11, +0xB0,0x56,0x38,0x50,0x80,0x00,0x00,0x03,0x00,0x10,0x38,0x70,0x80,0x00,0x4C,0x53, +0x81,0x48,0xF0,0x05,0x5A,0x08,0x01,0x4F,0xE2,0xE5,0x80,0x07,0x40,0x02,0xBC,0x1B, +0x80,0x3C,0x42,0x10,0x24,0x73,0x00,0x10,0x80,0x3B,0x5A,0x10,0xFF,0x0B,0x44,0x20, +0x01,0x24,0x44,0x02,0x34,0xA8,0x42,0x00,0x88,0x73,0xA0,0x01,0x4E,0x04,0x00,0x6D, +0xE2,0xA7,0x80,0x07,0x40,0x02,0xBC,0x1B,0x80,0x3C,0x42,0x10,0x24,0x73,0x00,0x10, +0x80,0x3B,0x5A,0x10,0xFF,0x0B,0x44,0x20,0x01,0x24,0x44,0x02,0x34,0xA8,0x42,0x00, +0x88,0x73,0xA0,0x01,0x4E,0x04,0x00,0x59,0x00,0x03,0x00,0x13,0x5C,0xF0,0x00,0x47, +0x4E,0xF3,0x01,0x17,0x80,0x1C,0x42,0x03,0xA4,0x73,0x00,0x20,0x00,0x3B,0x5A,0x20, +0xFF,0x5A,0x44,0x02,0x33,0x88,0x44,0x10,0x01,0x24,0x80,0x60,0x42,0x31,0x04,0x73, +0x02,0x21,0x80,0x85,0xC2,0x4F,0x80,0x5C,0x42,0x22,0xA4,0x73,0x00,0x21,0x00,0x3B, +0x5A,0x20,0xFF,0x49,0x42,0x01,0x04,0x73,0x02,0x00,0x00,0x85,0xC0,0x43,0x48,0x00, +0x00,0xF8,0x3C,0x03,0xF7,0xC7,0x96,0x36,0xC0,0x07,0xA0,0x32,0xF1,0x03,0xE0,0x01, +0x4E,0xF2,0x00,0xEF,0xD5,0x31,0xE2,0xE5,0x80,0x07,0x40,0x02,0xBC,0x1B,0x80,0x3C, +0x42,0x10,0x24,0x73,0x00,0x10,0x80,0x3B,0x5A,0x10,0xFF,0x0B,0x44,0x20,0x01,0x24, +0x44,0x02,0x34,0xA8,0x42,0x00,0x88,0x73,0xA0,0x01,0x4E,0x04,0x00,0x16,0xE2,0xA7, +0x80,0x07,0x40,0x02,0xBC,0x1B,0x80,0x3C,0x42,0x10,0x24,0x73,0x00,0x10,0x80,0x3B, +0x5A,0x10,0xFF,0x0F,0x44,0x20,0x01,0x24,0x44,0x02,0x34,0xA8,0x42,0x00,0x88,0x73, +0xA0,0x01,0x4E,0x05,0x00,0x06,0xA0,0x32,0xF1,0x03,0xE0,0x01,0xD5,0x09,0xA0,0x32, +0xF1,0x03,0xE0,0x01,0xE9,0x07,0x00,0x03,0x00,0x13,0xF1,0x02,0xE2,0x20,0x4E,0xF2, +0x00,0xB8,0xE2,0xA7,0x81,0x47,0x40,0xA2,0xBC,0x1B,0xE2,0xE5,0x40,0x72,0xBC,0x1B, +0x80,0x3C,0x42,0x13,0xA4,0x73,0x50,0x10,0x80,0x30,0x00,0x40,0x80,0x0B,0x5A,0x48, +0xFF,0x04,0x48,0x00,0x01,0x47,0x80,0x5C,0x42,0x25,0x24,0x73,0x50,0x21,0x00,0x30, +0x00,0xF1,0x00,0x0B,0x80,0xAA,0x5A,0xF0,0xFF,0x04,0x48,0x00,0x00,0xF7,0x50,0x05, +0x00,0x01,0x80,0x3C,0x42,0x10,0x24,0x73,0x80,0x01,0x84,0x20,0xAE,0x44,0x42,0x05, +0x24,0x24,0x40,0x3E,0x00,0x00,0x50,0x51,0x80,0x30,0xF0,0x87,0xA6,0x2F,0x04,0xF1, +0x80,0x0A,0xF0,0x89,0x00,0x01,0x80,0x38,0xF0,0x8A,0x42,0x03,0xA4,0x24,0xF0,0x88, +0x88,0x1C,0x50,0x80,0x00,0x30,0x00,0x14,0x00,0x07,0xA0,0x86,0xF1,0x8B,0x00,0x14, +0x00,0x08,0xF1,0x8C,0x04,0x10,0x00,0x0A,0xF1,0x84,0xF4,0x04,0x88,0x2F,0xFE,0xA4, +0xA1,0x1E,0xF1,0x86,0x42,0x27,0x90,0x73,0xF4,0x06,0x40,0x21,0x10,0x56,0xA8,0x86, +0x50,0x20,0x00,0x18,0xF4,0x04,0xA0,0x51,0xF5,0x84,0xFE,0x64,0xA1,0x1F,0x42,0x17, +0x90,0x73,0xF4,0x06,0x40,0x10,0x90,0x36,0xA8,0x51,0x00,0x11,0x80,0x3A,0x00,0x24, +0x00,0x0A,0x14,0x40,0x00,0x0A,0x88,0x22,0x10,0x14,0x00,0x0A,0x00,0x24,0x00,0x09, +0x00,0x11,0x80,0x39,0x88,0x22,0x10,0x14,0x00,0x09,0x02,0x10,0x00,0x19,0x02,0x21, +0x80,0x19,0x96,0xCB,0x97,0x13,0xE0,0x83,0x40,0x11,0x3C,0x1A,0x12,0x10,0x00,0x19, +0xF0,0x08,0x40,0x1E,0x00,0x00,0xF0,0x07,0x8C,0x2C,0x88,0x1C,0x8C,0x0C,0x80,0x41, +0x49,0xFF,0xAA,0xFC,0xF5,0x04,0x00,0x02,0x80,0x0B,0x5A,0x08,0xFF,0x04,0x48,0x00, +0x00,0xC3,0x10,0x04,0x00,0x0B,0xF2,0x0A,0x2E,0x07,0xDE,0x16,0xF1,0x09,0xF3,0x0C, +0x42,0x11,0x00,0x73,0xF2,0x0B,0x42,0x21,0x80,0x73,0x80,0x02,0x04,0x2F,0x80,0xB4, +0x38,0xF1,0x05,0x11,0x38,0x01,0x01,0x11,0xE0,0x0F,0xE8,0x11,0x80,0x3C,0x42,0x15, +0x24,0x73,0x50,0x10,0x80,0x30,0x80,0x1C,0x42,0x03,0xA4,0x73,0xA6,0x8F,0x50,0x00, +0x00,0x30,0x00,0x10,0x80,0x08,0xAE,0x87,0x10,0x10,0x00,0x08,0xB0,0x96,0x00,0x3E, +0x00,0x00,0x80,0x02,0x9A,0x42,0x96,0x48,0xE2,0x23,0x4E,0xF3,0x00,0x9B,0x8C,0xD8, +0x48,0xFF,0xFE,0xAB,0x84,0xC0,0x44,0x72,0x2F,0xF0,0x04,0x0F,0x80,0xB2,0xA6,0x00, +0xE2,0xC0,0x4E,0xF2,0x00,0x9C,0x04,0x0F,0x80,0xB2,0x94,0x72,0x88,0x01,0xB1,0x59, +0xA6,0x85,0xA6,0x04,0xF2,0x82,0xF0,0x83,0x84,0x40,0x2E,0x97,0xDE,0x16,0x80,0x62, +0x2E,0x07,0xDE,0x17,0x45,0xEF,0xFF,0xDF,0xF0,0x84,0x50,0x8F,0x80,0x50,0x50,0xAF, +0x80,0x58,0xF4,0x02,0x38,0x01,0x14,0x00,0x88,0x04,0x96,0x00,0x99,0x2A,0xE2,0x09, +0x20,0x42,0x00,0x01,0xE8,0x1E,0x04,0xFF,0x80,0x03,0x88,0x8F,0x04,0xFF,0x80,0x04, +0x97,0x20,0xE2,0x8F,0xE8,0x16,0x04,0xFF,0x80,0xB3,0x88,0x0F,0x42,0x02,0x24,0x73, +0xA6,0x00,0x40,0x40,0x78,0x02,0x5A,0x40,0x1F,0x0D,0x96,0x27,0x38,0x03,0x80,0x00, +0x5A,0x00,0xFF,0x08,0x38,0x05,0x00,0x00,0x38,0x04,0x0C,0x08,0x8C,0x61,0x96,0xD8, +0x8C,0x42,0x5A,0x28,0x10,0xD8,0x50,0x5F,0x82,0xC8,0xB4,0x05,0xB0,0x94,0x88,0x20, +0xA0,0x29,0x8C,0x22,0xB6,0x1F,0x50,0x4E,0x00,0x0C,0xA0,0x2A,0x84,0xAA,0x8C,0xC1, +0x49,0xFF,0xAA,0x85,0x97,0xB0,0xD5,0xAA,0x44,0x32,0x33,0x88,0x44,0x00,0x01,0x24, +0x81,0x03,0x42,0x82,0x00,0x73,0x50,0x44,0x01,0x08,0x01,0xE2,0x00,0x05,0x42,0x07, +0x80,0x24,0x88,0x03,0x5B,0xE8,0x01,0x1A,0x50,0x30,0x01,0x08,0x00,0xF1,0x80,0x05, +0x5A,0xF8,0x01,0x0B,0x02,0x00,0x00,0x84,0x02,0x84,0x00,0x84,0xE3,0x00,0x84,0x02, +0xE9,0x04,0xAE,0x1E,0xD5,0x13,0x84,0x02,0xAE,0x26,0x84,0x1F,0x81,0x47,0x10,0x00, +0x80,0x0B,0x80,0xE5,0x48,0xFF,0xFE,0xE5,0x50,0x00,0x01,0x08,0xA6,0x45,0x5A,0x10, +0x01,0x04,0x48,0xFF,0xFF,0x76,0x84,0x22,0xAE,0x46,0x84,0x1F,0x10,0x01,0x00,0x0B, +0x48,0xFF,0xFE,0xD7,0x00,0x04,0x00,0x0B,0x10,0x02,0x80,0x0B,0x48,0xFF,0xFF,0x3D, +0x08,0x10,0x00,0x01,0x4C,0xA0,0xFF,0x60,0x10,0x70,0x7F,0xFF,0x48,0xFF,0xFF,0x5C, +0x80,0x0A,0x81,0x47,0x80,0xE0,0x48,0xFF,0xFE,0xC4,0x51,0xFF,0x82,0xA8,0xFC,0xC0, +0xFC,0x58,0xF2,0x87,0x44,0x22,0x00,0xBC,0xF1,0x84,0xB0,0x6C,0xF3,0x88,0xF4,0x93, +0x3A,0x21,0x14,0x00,0x3A,0x20,0x94,0x20,0x84,0xE0,0x3C,0x13,0xEF,0x08,0x80,0xC0, +0xF1,0x91,0xF2,0x11,0x3C,0x13,0xEF,0x09,0xF7,0xA3,0xF1,0x92,0xF1,0x07,0xF7,0xA4, +0x22,0x10,0x80,0x00,0xF1,0x8D,0xF1,0x07,0xA4,0x49,0x9A,0x51,0x96,0x4B,0xF1,0x8E, +0xF1,0x08,0xF2,0x12,0x22,0x10,0x80,0x00,0xF1,0x8F,0xF1,0x08,0xA4,0x49,0x9A,0x51, +0x96,0x4B,0xF1,0x90,0x2E,0x17,0xF4,0x13,0x5A,0x10,0x01,0x04,0x48,0x00,0x00,0x84, +0x80,0x47,0xB0,0x5E,0x49,0xFF,0x98,0xCE,0xB0,0x5F,0x80,0x47,0x50,0x03,0x01,0x24, +0x49,0xFF,0x98,0xC8,0xF4,0x1E,0xF1,0x1F,0x22,0x32,0x00,0x08,0x22,0x50,0x80,0x08, +0x9B,0xDD,0x4E,0x74,0x00,0x03,0xFF,0xFA,0x22,0x02,0x00,0x09,0x22,0x90,0x80,0x09, +0x40,0x20,0x24,0x01,0x4E,0x24,0x00,0x03,0xFE,0x92,0xFE,0x94,0x42,0x23,0x9C,0x73, +0x46,0x70,0x00,0xE0,0x50,0x73,0x8F,0xFF,0xE0,0xE2,0xE9,0x5D,0x2E,0x70,0x12,0xBA, +0x44,0x22,0x13,0x4C,0xE0,0x65,0x81,0x42,0x44,0x50,0x00,0x38,0x42,0xA3,0x94,0x73, +0x04,0x75,0x00,0x08,0xE8,0x10,0xE0,0xE3,0x40,0x33,0xBC,0x1B,0x12,0x32,0x00,0x08, +0x2E,0x70,0x13,0xDE,0x22,0x30,0x80,0x08,0x42,0x23,0x94,0x73,0x04,0x21,0x00,0x08, +0xE0,0x62,0xD5,0x0F,0xE0,0x67,0x40,0x33,0xBC,0x1B,0x12,0x32,0x00,0x08,0x2E,0x70, +0x13,0xDE,0x22,0x30,0x80,0x08,0x42,0x23,0x94,0x73,0x04,0x21,0x00,0x08,0xE0,0x43, +0x40,0x31,0x3C,0x1B,0x12,0x30,0x80,0x08,0x2E,0x30,0x12,0xBA,0x44,0x22,0x13,0x4C, +0x44,0x50,0x00,0x38,0x80,0xE2,0x42,0x71,0x94,0x73,0xE0,0x09,0x04,0x33,0x80,0x09, +0xE8,0x10,0xE0,0x60,0x40,0x01,0xBC,0x1B,0x12,0x02,0x00,0x09,0x2E,0x00,0x13,0xDE, +0x22,0x30,0x80,0x09,0x42,0x20,0x14,0x73,0x04,0x01,0x00,0x09,0xE0,0x60,0xD5,0x0F, +0xE0,0x03,0x40,0x01,0xBC,0x1B,0x12,0x02,0x00,0x09,0x2E,0x00,0x13,0xDE,0x22,0x30, +0x80,0x09,0x42,0x20,0x14,0x73,0x04,0x01,0x00,0x09,0xE0,0x03,0x40,0x30,0x3C,0x1B, +0x12,0x30,0x80,0x09,0xF0,0x11,0x84,0xE0,0x92,0x01,0xF0,0x97,0xF0,0x12,0xF7,0x94, +0x92,0x01,0xF0,0x98,0x44,0x00,0x00,0x64,0xF0,0x83,0xF0,0x8B,0xF0,0x8A,0xF0,0x89, +0x3C,0x03,0xEF,0x08,0xF0,0x85,0x3C,0x03,0xEF,0x09,0xF0,0x86,0xF0,0x05,0x92,0x03, +0xF0,0x99,0xF0,0x06,0x92,0x03,0xF0,0x96,0x96,0x38,0xF0,0x95,0x00,0x03,0x01,0x05, +0x4E,0x02,0x04,0xAB,0x04,0x23,0x00,0x48,0x54,0x21,0x08,0x00,0x4E,0x23,0x04,0xA5, +0xB0,0x5E,0x80,0x06,0x49,0xFF,0x98,0x26,0xB0,0x5F,0x84,0x41,0x80,0x06,0x49,0xFF, +0x98,0x21,0xB0,0x60,0x84,0x42,0x80,0x06,0x49,0xFF,0x98,0x1C,0xB0,0x61,0x84,0x43, +0x80,0x06,0x49,0xFF,0x98,0x17,0xB0,0x62,0x84,0x44,0x80,0x06,0x49,0xFF,0x98,0x12, +0xF0,0x04,0x84,0x23,0x00,0x20,0x00,0x0E,0x44,0x02,0x04,0xA8,0x42,0x01,0x04,0x73, +0xF0,0x8C,0x02,0x03,0x00,0x81,0xE6,0x02,0x4E,0xF2,0x00,0xEB,0x02,0x03,0x00,0x80, +0x5A,0x00,0x02,0x04,0x48,0x00,0x00,0xE5,0x44,0x22,0x00,0xCC,0xB0,0x27,0x3A,0x21, +0x0C,0x00,0x3A,0x20,0x0C,0x20,0xF4,0x1E,0x3C,0x93,0xEF,0x08,0x22,0x52,0x00,0x06, +0x3C,0xA3,0xEF,0x09,0xF0,0x07,0xF1,0x1F,0x22,0x00,0x00,0x00,0x4C,0x02,0x80,0xD1, +0xF0,0x07,0x22,0x00,0x00,0x01,0x40,0x04,0x80,0x01,0x4C,0x50,0x00,0xCA,0xF0,0x08, +0x22,0x52,0x00,0x07,0x22,0x00,0x00,0x00,0x4C,0x02,0x80,0xC3,0xF0,0x08,0x22,0x00, +0x00,0x01,0x40,0x05,0x00,0x01,0x4C,0x50,0x00,0xBC,0x22,0x02,0x00,0x04,0x40,0x34, +0x88,0x09,0x40,0x24,0x80,0x01,0x22,0x42,0x00,0x05,0x4E,0x24,0x00,0x03,0xFE,0x92, +0x96,0x93,0xE0,0x62,0xE8,0x10,0x40,0x25,0x10,0x01,0x40,0x55,0x08,0x09,0x4E,0x24, +0x00,0x03,0xFE,0x92,0x96,0x93,0xE0,0xA2,0xE8,0x06,0xE0,0x60,0xE8,0x04,0xE0,0xA4, +0x4E,0xF3,0x00,0x9F,0x22,0x50,0x80,0x04,0x22,0x30,0x80,0x05,0x9A,0x85,0x8A,0x83, +0x84,0x03,0xFF,0x04,0x84,0x02,0x40,0x82,0x01,0x16,0xF2,0xA5,0x14,0x8F,0x80,0x26, +0xCA,0x03,0x4E,0x82,0x00,0x8E,0xB0,0x25,0xF3,0x9D,0xF2,0x9C,0xF5,0x9B,0xF1,0x9A, +0x49,0xFF,0xAF,0x83,0x5E,0xF0,0x00,0xFA,0x4E,0xF3,0x00,0x83,0xF5,0x1B,0xF2,0x1C, +0xF3,0x1D,0x9A,0x2A,0x40,0x81,0xA0,0x01,0xF1,0x1A,0x4E,0x07,0x00,0x08,0xE0,0x09, +0xE8,0x05,0x4E,0x87,0x00,0x04,0xE1,0x0A,0xE9,0x73,0x22,0x50,0x80,0x06,0xF4,0x25, +0x22,0x10,0x80,0x07,0xF2,0x26,0xFE,0x64,0xFE,0x92,0x42,0x11,0x14,0x73,0xF2,0xA9, +0xF4,0xAA,0xF1,0xAB,0x4E,0x06,0x00,0x06,0xB0,0x29,0x44,0x12,0x04,0x9C,0xD5,0x07, +0xE0,0x09,0x84,0x00,0xE9,0x07,0xB0,0x29,0x44,0x12,0x04,0x90,0xB0,0xA7,0x49,0xFF, +0xB0,0x9F,0xF1,0x27,0x4E,0x15,0x00,0x09,0xE1,0x21,0xE9,0x06,0xF2,0x28,0x4E,0x25, +0x00,0x04,0xE1,0x42,0xE8,0x4B,0x4E,0x86,0x00,0x06,0xB0,0x29,0x44,0x12,0x04,0x84, +0xD5,0x06,0xE1,0x0A,0xE9,0x07,0xB0,0x29,0x44,0x12,0x04,0x78,0xB0,0xA7,0x49,0xFF, +0xB0,0x87,0x5A,0x08,0x02,0x3E,0xF1,0x27,0x4E,0x15,0x00,0x06,0xE1,0x21,0x40,0x90, +0xBC,0x1A,0xD5,0x02,0x85,0x20,0xF0,0x28,0x3C,0x9F,0xF8,0xA0,0x4E,0x05,0x00,0x06, +0xE0,0x0A,0x40,0xA0,0x3C,0x1B,0xD5,0x02,0x85,0x40,0x50,0x93,0x01,0x1B,0x84,0x01, +0x3C,0xAF,0xF8,0xA1,0x12,0x03,0x00,0x81,0x00,0x04,0x80,0x05,0x84,0x22,0x58,0x00, +0x00,0x02,0x10,0x04,0x80,0x05,0xF0,0x15,0x49,0xFF,0xB4,0x74,0xF0,0x15,0x84,0x20, +0x49,0xFF,0xB4,0x79,0xF0,0x1E,0x3C,0x27,0xF1,0x40,0x3C,0x17,0xF1,0x42,0x12,0x20, +0x00,0x08,0x12,0x10,0x00,0x09,0xAC,0x86,0xAC,0x47,0x00,0x04,0x80,0x04,0x54,0x00, +0x00,0xEF,0x10,0x04,0x80,0x04,0x48,0x00,0x03,0x7A,0x5A,0x00,0x02,0xC9,0x02,0xA3, +0x00,0x81,0x5A,0xA8,0x01,0x3E,0x04,0x13,0x00,0x47,0x46,0x01,0x00,0x00,0xFE,0x0E, +0x4E,0x02,0x03,0x4E,0xF0,0x13,0x5A,0x00,0x01,0x04,0x48,0x00,0x03,0x49,0x44,0x02, +0x04,0x68,0xF1,0x11,0xF2,0x12,0x49,0xFF,0xB2,0x83,0x5A,0x00,0x01,0x04,0x48,0x00, +0x03,0x3F,0xF1,0x1E,0x3C,0x2D,0xF8,0x9F,0x3C,0x3D,0xF8,0x9E,0x22,0x00,0x80,0x07, +0x22,0x40,0x80,0x06,0x8A,0x02,0x8A,0x83,0xFE,0x04,0x42,0x02,0x10,0x73,0x5E,0xF0, +0x27,0x10,0x4E,0xF2,0x03,0x2D,0x5E,0xF0,0x19,0x00,0xE8,0x04,0x12,0x30,0x80,0x08, +0xD5,0x0B,0x22,0x00,0x80,0x08,0x88,0x03,0x90,0x01,0x12,0x00,0x80,0x08,0x22,0x00, +0x80,0x09,0x88,0x40,0x90,0x41,0x12,0x20,0x80,0x09,0x48,0x00,0x03,0x3B,0xE7,0x42, +0x4E,0xF3,0x03,0x35,0x04,0x03,0x00,0x47,0x8C,0x0F,0x40,0x00,0x70,0x08,0x90,0x1C, +0x80,0x20,0x4E,0x04,0x00,0x03,0x84,0x20,0x50,0x03,0x01,0x1B,0xA6,0x81,0x96,0x5F, +0x54,0x21,0x00,0xF0,0xFE,0x57,0xAE,0x41,0x3C,0x0D,0xFB,0xD6,0x96,0x1E,0xC8,0x04, +0xF0,0x04,0xB4,0x00,0xD5,0x03,0x44,0x00,0x6D,0x60,0x04,0x9F,0x80,0x1E,0xB4,0x29, +0xE0,0x01,0xE8,0x09,0x50,0x13,0x01,0x1B,0xA6,0x09,0x54,0x00,0x00,0xF0,0x58,0x00, +0x00,0x08,0xAE,0x09,0x04,0x03,0x00,0x47,0x54,0x10,0x00,0x0F,0x50,0x03,0x01,0x1B, +0xC9,0x05,0xA6,0x44,0x54,0x10,0x80,0xDF,0xD5,0x04,0xA6,0x44,0x58,0x10,0x80,0x20, +0xAE,0x44,0x04,0x23,0x00,0x47,0x46,0x01,0x00,0x00,0xFE,0x16,0xC0,0x21,0x00,0x03, +0x01,0x05,0x5A,0x08,0x02,0x1E,0x2E,0x37,0xF4,0x13,0x46,0x14,0x00,0x00,0x02,0x03, +0x00,0x85,0xFE,0x56,0xC3,0x0C,0x5E,0xF0,0x00,0x65,0xE9,0x05,0x8E,0x0A,0x96,0x01, +0x48,0x00,0x00,0x86,0x44,0x00,0x00,0x64,0x48,0x00,0x00,0x82,0xC1,0x05,0xF1,0x04, +0x00,0x10,0x80,0x09,0xD5,0x7A,0xF1,0x04,0x00,0x10,0x80,0x08,0xD5,0x76,0x5C,0xF5, +0x00,0x33,0xE9,0x33,0x46,0x12,0x00,0x00,0xFE,0x56,0xC1,0x2F,0xF0,0x13,0x5A,0x08, +0x01,0x2D,0x3C,0x0D,0xFD,0x05,0x84,0x27,0xFE,0x0C,0xB4,0x29,0x90,0x03,0xE0,0x20, +0xE9,0x05,0x00,0x03,0x01,0x05,0x5A,0x08,0x03,0x21,0xF2,0x20,0xF0,0x1F,0x22,0xF1, +0x00,0x09,0x22,0x30,0x00,0x08,0x22,0x00,0x00,0x09,0x22,0x11,0x00,0x08,0x8A,0x0F, +0x9A,0x59,0xFE,0x04,0x42,0x00,0x84,0x73,0x44,0x10,0x9C,0x3F,0xE0,0x20,0xE9,0x0D, +0xF0,0x04,0xA4,0x42,0x84,0x0A,0xFE,0x0C,0x12,0x03,0x00,0x85,0x44,0x02,0x04,0xA8, +0xF0,0x8C,0x84,0x01,0xF0,0x94,0xD5,0x45,0x00,0x03,0x01,0x05,0x5A,0x08,0x03,0x22, +0xF1,0x04,0x02,0x03,0x00,0x85,0x00,0x10,0x80,0x0B,0x49,0xFF,0xB1,0xB0,0xE7,0x46, +0x12,0x03,0x00,0x85,0xE8,0x36,0x22,0x13,0x00,0x88,0x22,0x04,0x80,0x04,0x8A,0x01, +0xF0,0xA3,0x22,0x13,0x00,0x89,0x22,0x04,0x80,0x05,0x8A,0x01,0xF0,0xA4,0xB0,0x23, +0x49,0xFF,0xAE,0x0B,0x5E,0xF0,0x03,0xE8,0xE8,0x24,0x44,0x00,0x03,0xE8,0xD5,0x1F, +0xF0,0x1F,0xF1,0x0D,0x22,0x50,0x00,0x08,0xD1,0x09,0xF1,0x0E,0xD1,0x07,0x22,0x50, +0x00,0x09,0xF0,0x0F,0xD0,0x03,0xF0,0x10,0xD8,0x0B,0xF0,0x04,0x00,0x10,0x00,0x0A, +0x84,0x03,0xFE,0x44,0x90,0x21,0x02,0x03,0x00,0x85,0x96,0x48,0xD5,0x06,0xF1,0x04, +0x02,0x03,0x00,0x85,0x00,0x10,0x80,0x0A,0x49,0xFF,0xB1,0x79,0x12,0x03,0x00,0x85, +0xF0,0x14,0x02,0x93,0x00,0x85,0xC8,0x55,0x00,0x03,0x01,0x05,0x5A,0x00,0x03,0x52, +0xF2,0x1F,0xF0,0x0E,0xF1,0x0D,0x22,0x31,0x00,0x08,0xF4,0x0F,0x8A,0x03,0x9A,0x59, +0xE0,0x20,0x40,0x10,0x3C,0x1A,0x22,0x21,0x00,0x09,0xF0,0x10,0x9B,0x14,0x8A,0x02, +0xE0,0x80,0x40,0x02,0x3C,0x1B,0x50,0x41,0xFD,0x30,0x97,0x21,0x5C,0xF2,0x0A,0x05, +0x80,0x89,0x14,0x9F,0x80,0x0A,0x96,0x4B,0x96,0x03,0x95,0x22,0xE8,0x04,0xE0,0x82, +0x40,0x01,0x3C,0x1A,0xE0,0x24,0xE8,0x0F,0xF5,0x17,0x90,0x22,0xE0,0x65,0x96,0x4B, +0xE8,0x05,0xF1,0x89,0x14,0x9F,0x80,0x03,0xD5,0x04,0xF1,0x83,0x14,0x9F,0x80,0x09, +0x85,0x02,0xD5,0x06,0x14,0x9F,0x80,0x03,0x14,0x9F,0x80,0x09,0x85,0x01,0xE0,0x04, +0xE8,0x0D,0xF1,0x18,0x90,0x02,0xE0,0x41,0xE8,0x04,0x96,0x03,0xF0,0x8B,0xD5,0x04, +0x14,0x9F,0x80,0x0B,0xF0,0x8A,0x85,0x02,0xD5,0x03,0x14,0x9F,0x80,0x0B,0xF0,0x0B, +0xC0,0x0A,0xF0,0x0A,0xC0,0x08,0xF0,0x09,0xC0,0x06,0xF0,0x03,0xC8,0x05,0xD5,0x03, +0x85,0x01,0xD5,0x02,0x85,0x03,0x04,0x03,0x00,0x48,0x54,0x10,0x00,0x01,0xF0,0x1F, +0xC9,0x0E,0xF2,0x1E,0x22,0x30,0x00,0x08,0x22,0x00,0x00,0x09,0x22,0x11,0x00,0x06, +0x8A,0x23,0xF1,0xA3,0x22,0x11,0x00,0x07,0x9A,0x08,0xD5,0x0C,0x22,0x20,0x00,0x08, +0x04,0x13,0x00,0x3A,0x8A,0x22,0xF1,0xA3,0x22,0x10,0x00,0x09,0x04,0x03,0x00,0x3B, +0x8A,0x01,0xF0,0xA4,0x00,0x03,0x01,0x05,0x5A,0x08,0x02,0x08,0x50,0x13,0x01,0x1B, +0xA6,0x0D,0x54,0x00,0x00,0xFE,0xAE,0x0D,0x04,0x03,0x00,0x48,0x96,0x06,0x4E,0x03, +0x01,0x26,0x44,0x12,0x00,0xCC,0xB0,0x27,0x3A,0x20,0x8C,0x00,0x3A,0x20,0x0C,0x20, +0x00,0x03,0x01,0x06,0x04,0xAF,0x80,0x1F,0xF4,0x22,0x5A,0x08,0x01,0x0B,0x00,0x13, +0x01,0x05,0x84,0x00,0x5A,0x10,0x03,0x04,0x48,0x00,0x01,0x03,0x48,0x00,0x00,0xDC, +0x02,0x03,0x00,0x80,0xE6,0x05,0xE9,0xF4,0x02,0x03,0x00,0x84,0x5C,0xF0,0x07,0xD0, +0xE9,0xEF,0xF0,0x07,0x23,0xE5,0x00,0x08,0x22,0x50,0x00,0x00,0x4C,0x5F,0x3F,0xE9, +0x22,0x00,0x00,0x01,0xF1,0x05,0x9A,0x08,0x4D,0xE0,0x3F,0xE3,0xF0,0x08,0x22,0x55, +0x00,0x09,0x22,0x00,0x00,0x00,0x4C,0x02,0xBF,0xDC,0xF0,0x08,0xF1,0x06,0x22,0x00, +0x00,0x01,0x9A,0x08,0xD0,0xD5,0x22,0x25,0x00,0x04,0xF3,0x05,0x02,0x0F,0x80,0x32, +0x8A,0x62,0x22,0x15,0x00,0x05,0xF0,0x95,0x4E,0x34,0x00,0x03,0xFE,0xDA,0xF0,0x15, +0x96,0xDB,0xE0,0x03,0xE8,0x10,0xF3,0x06,0x8A,0x61,0x4E,0x34,0x00,0x03,0xFE,0xDA, +0xF0,0x16,0x96,0xDB,0xE0,0x03,0xE8,0x07,0xF0,0x15,0xE0,0x02,0xE8,0x04,0xF0,0x16, +0xE0,0x01,0xE9,0xB6,0xF0,0x23,0xC8,0x03,0xF0,0x24,0xC0,0xB2,0x22,0x02,0x00,0x04, +0x5A,0x07,0xFF,0xAF,0x22,0x32,0x00,0x05,0x5A,0x37,0xFF,0xAB,0x22,0xF2,0x00,0x09, +0x22,0x42,0x00,0x08,0x4D,0xE2,0x40,0x04,0x4C,0x57,0xBF,0xA3,0x9B,0x10,0x9A,0xCB, +0x84,0x05,0x84,0xA4,0xFF,0x04,0xFE,0xC4,0x40,0x42,0x14,0x96,0x40,0x31,0x94,0x76, +0xF4,0xA5,0xF3,0xA6,0xCC,0x02,0xC3,0x94,0xB0,0x25,0xF3,0x9C,0xF4,0x9B,0xF1,0x9A, +0xF2,0x95,0x49,0xFF,0xAC,0xDA,0x5E,0xF0,0x00,0x32,0xE9,0x8A,0xF2,0x15,0xF4,0x1B, +0xF1,0x1A,0xF3,0x1C,0x88,0x44,0x88,0x61,0x4E,0x27,0x00,0x0B,0xF0,0x05,0xE0,0x40, +0xE8,0x07,0x4E,0x37,0x00,0x06,0xF0,0x06,0xE0,0x60,0x4E,0xF3,0xFF,0x7A,0xF1,0x23, +0x22,0x05,0x00,0x09,0xF4,0x24,0x22,0x55,0x00,0x08,0xFF,0x22,0xF1,0xAA,0xFE,0x44, +0x42,0x12,0x14,0x73,0xF4,0xA9,0xF1,0xAB,0x4E,0x26,0x00,0x07,0xF3,0x95,0xB0,0x29, +0x44,0x12,0x04,0x9C,0xD5,0x09,0xF0,0x05,0xE0,0x40,0x84,0x00,0xE9,0x09,0xF3,0x95, +0xB0,0x29,0x44,0x12,0x04,0x90,0xB0,0xA7,0x49,0xFF,0xAD,0xF2,0xF3,0x15,0xF1,0x27, +0x4E,0x15,0x00,0x0C,0xF2,0x05,0xE0,0x41,0xE9,0x08,0xF2,0x28,0x4E,0x25,0x00,0x06, +0xF4,0x06,0xE0,0x82,0x4E,0xF2,0x01,0x39,0x4E,0x36,0x00,0x06,0xB0,0x29,0x44,0x12, +0x04,0x84,0xD5,0x07,0xF1,0x06,0xE0,0x61,0xE9,0x07,0xB0,0x29,0x44,0x12,0x04,0x78, +0xB0,0xA7,0x49,0xFF,0xAD,0xD5,0x5A,0x00,0x02,0x04,0x48,0xFF,0xFF,0x3A,0xF1,0x27, +0x4E,0x15,0x00,0x07,0xF0,0x05,0xE0,0x01,0x40,0x10,0x3C,0x1B,0xD5,0x02,0x84,0x20, +0xF0,0x28,0x4E,0x05,0x00,0x07,0xF2,0x06,0xE0,0x02,0x40,0x01,0x3C,0x1A,0xD5,0x02, +0x84,0x00,0x22,0x25,0x00,0x08,0x22,0xF5,0x00,0x09,0x8A,0x22,0x8A,0x0F,0xF1,0xA3, +0xF0,0xA4,0xD5,0x34,0xF0,0x09,0xB0,0xAC,0xF0,0xAC,0xF0,0x03,0xB7,0x1F,0xF0,0xAD, +0xF0,0x0B,0xF6,0x81,0xF0,0xAE,0xF0,0x0A,0x80,0x29,0xF0,0xAF,0x3A,0x21,0x14,0x00, +0xB0,0x23,0x49,0xFF,0xAF,0x54,0x04,0x03,0x00,0x48,0x54,0x00,0x01,0xE0,0x5A,0x08, +0x20,0x35,0xF1,0x1F,0xF0,0x23,0x22,0x20,0x80,0x08,0x22,0x10,0x80,0x09,0x88,0x02, +0x14,0x03,0x00,0x3A,0xF0,0x24,0x88,0x01,0x14,0x03,0x00,0x3B,0xD5,0x26,0xF0,0xA3, +0xF0,0xA4,0xF0,0x0C,0xB0,0x63,0x80,0x49,0xA7,0x00,0xA7,0x41,0xA6,0x02,0xFA,0x6E, +0xB6,0x1F,0x80,0x06,0x49,0xFF,0xAD,0xA6,0xD5,0x3E,0x04,0x03,0x00,0x48,0x96,0x06, +0xC8,0x14,0xF1,0x1F,0xF0,0x23,0x22,0x20,0x80,0x08,0x22,0x10,0x80,0x09,0x88,0x02, +0x14,0x03,0x00,0x3A,0xF0,0x24,0x88,0x01,0x50,0x13,0x01,0x1B,0x14,0x03,0x00,0x3B, +0xA6,0x0D,0x58,0x00,0x00,0x01,0xAE,0x0D,0x44,0x02,0x15,0xC0,0xF1,0x1F,0x38,0x30, +0x1C,0x00,0x00,0x03,0x01,0x05,0xF2,0x20,0x5A,0x00,0x03,0x1A,0x44,0x02,0x15,0xC0, +0x50,0x33,0x01,0x1B,0x38,0x00,0x1C,0x00,0x8C,0x01,0x96,0x00,0x54,0x10,0x00,0x07, +0x94,0x8D,0xA6,0x5D,0x92,0x03,0x96,0x67,0xFE,0x57,0xAE,0x5D,0x54,0x10,0x00,0x01, +0xA6,0x1E,0x54,0x00,0x00,0xFE,0xFE,0x0F,0xAE,0x1E,0xD5,0x05,0xB0,0x23,0x49,0xFF, +0xAC,0x21,0xC0,0xE5,0xF3,0x23,0xF0,0x1E,0xF1,0x1F,0xCB,0x03,0xF2,0x24,0xC2,0x0B, +0x02,0x20,0x80,0x08,0x02,0x10,0x80,0x09,0x88,0x43,0x12,0x20,0x00,0x08,0xF2,0x24, +0x88,0x22,0xD5,0x07,0x22,0x20,0x80,0x08,0x22,0x10,0x80,0x09,0x12,0x20,0x00,0x08, +0x12,0x10,0x00,0x09,0x04,0x13,0x00,0x47,0x46,0x01,0x00,0x00,0xFE,0x0E,0xC0,0x19, +0xF0,0x23,0xC8,0x03,0xF0,0x24,0xC0,0x0C,0x50,0x13,0x01,0x1B,0xA6,0x0C,0x54,0x00, +0x00,0xEF,0xAE,0x0C,0x44,0x0F,0xFF,0x9C,0x3C,0x0F,0xF8,0x9E,0xD5,0x08,0xF0,0x1E, +0x22,0x10,0x00,0x08,0x3C,0x1F,0xF8,0x9E,0x22,0x00,0x00,0x09,0x3C,0x0F,0xF8,0x9F, +0xF0,0x1E,0xF2,0x0D,0x22,0x10,0x00,0x08,0xE0,0x22,0xE9,0x07,0xF2,0x0E,0xE0,0x22, +0x40,0x20,0xBC,0x1B,0x80,0x22,0xD5,0x02,0xF1,0x0D,0x12,0x10,0x00,0x08,0xF2,0x0F, +0x22,0x10,0x00,0x09,0xE0,0x22,0xE9,0x07,0xF2,0x10,0xE0,0x22,0x40,0x20,0xBC,0x1B, +0x80,0x22,0xD5,0x02,0xF1,0x0F,0x12,0x10,0x00,0x09,0xD5,0x20,0x00,0x03,0x01,0x05, +0x5A,0x08,0x03,0x1D,0x00,0x03,0x01,0x06,0x5A,0x00,0x01,0x19,0x44,0x02,0x15,0xC0, +0x50,0x33,0x01,0x1B,0x38,0x00,0x1C,0x00,0x8C,0x01,0x96,0x00,0x54,0x10,0x00,0x07, +0x94,0x8D,0xA6,0x5D,0x92,0x03,0x96,0x67,0xFE,0x57,0xAE,0x5D,0x54,0x10,0x00,0x01, +0xA6,0x1E,0x54,0x00,0x00,0xFE,0xFE,0x0F,0xAE,0x1E,0xF0,0x13,0x5A,0x08,0x01,0x0D, +0x02,0x03,0x00,0x80,0xE6,0x02,0xE9,0x08,0xF0,0x1E,0xB4,0x20,0x3C,0x0D,0xFD,0x05, +0x88,0x01,0x90,0x01,0xD5,0x02,0x84,0x00,0x3C,0x0F,0xFD,0x05,0x02,0x03,0x00,0x81, +0xE6,0x02,0xE8,0x0A,0xF0,0x1E,0x22,0x10,0x00,0x08,0x12,0x13,0x00,0x7E,0x22,0x00, +0x00,0x09,0x12,0x03,0x00,0x7F,0x8C,0xE1,0x50,0x63,0x01,0x24,0x5A,0x70,0x0B,0x04, +0x48,0xFF,0xFB,0x4C,0xFC,0xD8,0x5A,0x00,0x02,0x04,0x48,0xFF,0xFE,0x12,0x48,0xFF, +0xFE,0xDB,0xFC,0x00,0x49,0xFF,0xE4,0x99,0x49,0x00,0x00,0xF6,0x49,0xFF,0xE7,0x8F, +0x49,0xFF,0xE7,0xE3,0x49,0xFF,0xE4,0xC6,0x49,0xFF,0xE4,0xDB,0x49,0xFF,0xE5,0x0E, +0x49,0xFF,0xC8,0x25,0x49,0xFF,0xE5,0xAB,0x49,0xFF,0xE0,0x2F,0x84,0x00,0x3E,0x07, +0xE2,0xCF,0xFC,0x80,0x44,0x04,0x00,0x54,0x84,0x20,0xB6,0x20,0xDD,0x9E,0xFC,0x44, +0x81,0x00,0x84,0x00,0x12,0x0F,0x80,0x0F,0x4E,0x82,0x00,0x0D,0x44,0x02,0x17,0x4C, +0xF0,0x84,0x44,0x02,0x17,0x24,0xF0,0x82,0x84,0x02,0x85,0x45,0xF0,0x83,0x84,0xC6, +0xD5,0x0B,0x44,0x02,0x17,0x88,0x85,0x43,0xF0,0x84,0x44,0x02,0x17,0x60,0xF0,0x82, +0x14,0xAF,0x80,0x03,0x84,0xC4,0x46,0x97,0xFF,0xFF,0x84,0x00,0x80,0xA6,0x50,0x94, +0x8F,0xFF,0xF0,0x81,0x2E,0x07,0xF6,0x1D,0xF7,0x01,0x42,0x73,0x00,0x73,0x97,0xF8, +0xE6,0xF4,0xE8,0x39,0x84,0x02,0x80,0x28,0xF2,0x01,0x44,0x32,0x17,0xB6,0x50,0x4F, +0x80,0x1E,0xF5,0x85,0x49,0xFF,0xCB,0x6D,0xE7,0x02,0xF5,0x05,0xE8,0x0C,0xE3,0x47, +0xE9,0x0A,0x02,0x0F,0x80,0x0F,0xE6,0x0A,0xE8,0x03,0x84,0x00,0xD5,0x02,0x8E,0x0A, +0x12,0x0F,0x80,0x0F,0x44,0x02,0x17,0xB8,0x38,0x10,0x1E,0x02,0x3C,0x03,0xFA,0xE3, +0xC1,0x08,0x94,0x02,0x84,0x46,0x42,0x00,0x88,0x73,0x84,0x2A,0x40,0x00,0x04,0x16, +0x44,0x12,0x17,0xB8,0x38,0x00,0x9E,0x0A,0x44,0x02,0x17,0xB8,0x38,0x00,0x1E,0x02, +0xE0,0x09,0xE8,0x09,0x02,0x1F,0x80,0x0F,0x5C,0xF0,0x82,0x58,0x40,0x90,0x3C,0x1B, +0x40,0x53,0xBC,0x1B,0xF0,0x01,0x8C,0x01,0x96,0x00,0xF0,0x81,0x4C,0x03,0x7F,0xBC, +0x44,0x00,0xFF,0xFF,0xE7,0x02,0x3C,0x0F,0xF8,0xEB,0xE8,0x1E,0xE3,0x47,0xE9,0x1C, +0x84,0x00,0x80,0x20,0x44,0x42,0x17,0xB8,0x38,0x22,0x06,0x02,0x8C,0x21,0xE0,0x40, +0x96,0x48,0x40,0x20,0x3C,0x1B,0xE3,0x41,0x44,0x32,0x17,0xB8,0x96,0x11,0xE8,0xF5, +0x84,0x20,0x38,0x01,0x86,0x0A,0x8C,0x21,0x96,0x88,0xE3,0x42,0xE8,0xFB,0xE3,0x45, +0xE9,0x03,0x81,0x20,0x84,0xA0,0x2E,0x47,0xF6,0x1D,0xC4,0x05,0x3C,0x03,0xFB,0x10, +0xE1,0x20,0xE8,0x05,0x3E,0x57,0xF6,0x22,0x3C,0x9B,0xFB,0x10,0x3C,0x03,0xFB,0x10, +0x5C,0xF0,0x00,0x50,0xE8,0x09,0x2E,0x07,0xF6,0x22,0x3E,0x07,0xF6,0x23,0x84,0x00, +0x3E,0x07,0xF6,0x1C,0xD5,0x20,0x8C,0x81,0x2E,0x17,0xF6,0x1E,0x97,0x20,0xE2,0x81, +0x3E,0x47,0xF6,0x1D,0xE9,0x1A,0x2E,0x17,0xF6,0x22,0x5C,0xF0,0x00,0x78,0x3E,0x17, +0xF6,0x23,0x84,0x01,0xE9,0x02,0x84,0x02,0x3E,0x07,0xF6,0x1C,0x84,0x01,0x3E,0x07, +0xF6,0x1A,0x84,0x21,0x84,0x07,0x49,0xFF,0xE7,0x53,0x84,0x07,0x44,0x10,0x00,0x64, +0x49,0xFF,0xE7,0x40,0x84,0x01,0xD5,0x16,0xFF,0x34,0x97,0x20,0x88,0xC4,0xE4,0xD5, +0xE9,0x02,0xFA,0xC4,0x9F,0x71,0x84,0x02,0xF1,0x03,0xF2,0x02,0xF3,0x04,0x97,0x68, +0x49,0xFF,0xBF,0x7E,0xE7,0x02,0x84,0x00,0xE8,0x05,0x80,0x28,0x49,0xFF,0xC8,0xD7, +0x84,0x00,0xFC,0xC4,0xFC,0x40,0x44,0x64,0x80,0x88,0x46,0xA0,0x10,0x00,0xB4,0x06, +0x46,0x90,0x20,0x00,0x40,0x00,0x28,0x04,0xB6,0x06,0xB4,0x06,0x44,0x74,0x55,0x08, +0x40,0x00,0x24,0x04,0xB6,0x06,0xB4,0x27,0x44,0x0F,0xFF,0xBF,0xFE,0x0E,0xB6,0x07, +0x84,0x21,0x80,0x07,0x49,0xFF,0x87,0xDB,0x44,0x04,0x58,0x88,0x84,0x37,0xB4,0x40, +0xFE,0x56,0xB6,0x20,0x84,0x21,0x49,0xFF,0x87,0xA7,0xB4,0x27,0x44,0x0F,0xFF,0x7F, +0xFE,0x0E,0xB6,0x07,0xB4,0x27,0x44,0x0F,0xFF,0xEF,0xFE,0x0E,0xB6,0x07,0x84,0x21, +0x80,0x07,0x49,0xFF,0x87,0xC4,0xB4,0x06,0x40,0xA0,0x28,0x04,0xB7,0x46,0xB4,0x06, +0x40,0x90,0x24,0x04,0xB7,0x26,0x49,0xFF,0x8D,0x31,0xE6,0x03,0xE8,0x1F,0x44,0x14, +0x2C,0x0C,0x46,0x00,0x01,0x00,0xB4,0x41,0xFE,0x17,0x84,0x41,0xB6,0x01,0x80,0x62, +0x44,0x04,0x28,0x0C,0x44,0x14,0x24,0x0C,0x49,0xFF,0x87,0xD4,0x44,0x14,0x2C,0x18, +0x46,0x00,0x04,0x00,0xB4,0x41,0xFE,0x17,0x84,0x41,0xB6,0x01,0x80,0x62,0x44,0x04, +0x28,0x18,0x44,0x14,0x24,0x18,0x49,0xFF,0x87,0xC5,0x49,0xFF,0x8D,0x0F,0xE6,0x03, +0xE8,0x2B,0x44,0x14,0x2C,0x14,0xB4,0x01,0x58,0x00,0x10,0x00,0xB6,0x01,0x84,0x05, +0x49,0xFF,0xBB,0x25,0x84,0x41,0x80,0x62,0x44,0x04,0x28,0x14,0x44,0x14,0x24,0x14, +0x49,0xFF,0x87,0xB0,0x44,0x04,0x2C,0x1C,0x44,0x1B,0xFF,0xFF,0xB4,0x40,0xFE,0x56, +0xB6,0x20,0xB4,0x40,0x46,0x10,0x00,0x80,0xFE,0x57,0xB6,0x20,0xB4,0x40,0x46,0x10, +0x04,0x00,0xFE,0x57,0x84,0x41,0xB6,0x20,0x80,0x62,0x44,0x04,0x28,0x1C,0x44,0x14, +0x24,0x1C,0x49,0xFF,0x87,0x97,0x46,0x60,0x00,0x81,0x49,0xFF,0x87,0xA5,0x04,0x13, +0x03,0xA0,0x84,0x10,0xFE,0x0E,0x14,0x03,0x03,0xA0,0x04,0x13,0x03,0x9F,0x46,0x0F, +0x0F,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x46,0x46,0x20,0x40,0x00,0xFE,0x57,0x14,0x13, +0x03,0x9F,0x04,0x13,0x03,0x9F,0x46,0x27,0x00,0x00,0x94,0x4C,0x92,0x24,0xFE,0x57, +0x14,0x13,0x03,0x9F,0x04,0x13,0x03,0x9E,0xFE,0x0E,0x46,0x10,0x70,0x00,0xFE,0x0F, +0x14,0x03,0x03,0x9E,0x49,0xFF,0x87,0x79,0x46,0x00,0x03,0x33,0x50,0x00,0x03,0x33, +0x44,0x14,0x50,0x0C,0x14,0x03,0x00,0x34,0x14,0x03,0x00,0x36,0xB4,0x01,0x58,0x00, +0x00,0x01,0xB6,0x01,0x44,0x04,0x00,0x90,0x84,0x2A,0xAC,0x40,0x84,0x00,0x49,0xFF, +0xBC,0x7C,0x44,0x16,0x20,0x04,0x46,0x00,0x10,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01, +0x44,0x14,0x2C,0x18,0x46,0x0B,0xFF,0xFF,0xB4,0x41,0x50,0x00,0x0F,0xFF,0xFE,0x16, +0xB6,0x01,0x44,0x14,0x2C,0x1C,0x44,0x0F,0xFF,0xEF,0xB4,0x41,0xFE,0x16,0x84,0x41, +0xB6,0x01,0x80,0x62,0x44,0x04,0x28,0x18,0x44,0x14,0x24,0x18,0x49,0xFF,0x87,0x3A, +0x84,0x41,0x44,0x04,0x28,0x1C,0x44,0x14,0x24,0x1C,0x80,0x62,0x49,0xFF,0x87,0x32, +0x49,0xFF,0x8C,0x7C,0xE6,0x03,0xE8,0x10,0x44,0x14,0x2C,0x04,0x46,0x00,0x00,0x80, +0xB4,0x41,0xFE,0x17,0x84,0x41,0xB6,0x01,0x80,0x62,0x44,0x04,0x28,0x04,0x44,0x14, +0x24,0x04,0x49,0xFF,0x87,0x1F,0x49,0xFF,0x8C,0x69,0xE6,0x03,0xE9,0x40,0x84,0x08, +0x49,0xFF,0xBA,0x85,0x84,0x00,0x49,0xFF,0xBA,0x8B,0x84,0x41,0x80,0x62,0x44,0x04, +0x28,0x14,0x44,0x14,0x24,0x14,0x49,0xFF,0x87,0x0D,0x44,0x14,0x2C,0x0C,0x46,0x0F, +0xFE,0xFF,0xB4,0x41,0x50,0x00,0x0F,0xFF,0xFE,0x16,0x84,0x41,0xB6,0x01,0x80,0x62, +0x44,0x04,0x28,0x0C,0x44,0x14,0x24,0x0C,0x49,0xFF,0x86,0xFC,0x44,0x04,0x2C,0x04, +0x46,0x10,0x08,0x00,0xB4,0x40,0xFE,0x57,0xB6,0x20,0xB4,0x40,0x46,0x1F,0xFB,0xFF, +0x50,0x10,0x8F,0xFF,0xFE,0x56,0xB6,0x20,0xB4,0x40,0x46,0x10,0x00,0x80,0xFE,0x57, +0xB6,0x20,0xB4,0x40,0x44,0x14,0x00,0x00,0xFE,0x57,0x84,0x41,0xB6,0x20,0x80,0x62, +0x44,0x04,0x28,0x04,0x44,0x14,0x24,0x04,0x49,0xFF,0x86,0xDC,0xFC,0xC0,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0x40,0xDD,0x00,0x00,0x40,0xDD,0x00,0x00,0xBE,0xFD,0x00,0x00, +0x00,0xE0,0x01,0x00,0xEE,0xF3,0x01,0x00,0x24,0x01,0x00,0x00,0x50,0x1D,0x00,0x00, +0x81,0x08,0x00,0x1A,0x02,0x00,0x06,0x0C,0x02,0x00,0x06,0x0C,0x01,0x00,0x04,0x04, +0x01,0x00,0x03,0x06,0x01,0x00,0x06,0x00,0x05,0x04,0x00,0x00,0xA9,0xC8,0xB9,0x7E, +0x32,0x19,0x64,0x32,0x33,0x11,0x12,0x20,0xFF,0x07,0xFF,0x07,0x05,0x0A,0x0B,0x00, +0x00,0xFF,0xFF,0x00,0x01,0x00,0x00,0x01,0x01,0x01,0xFF,0x01,0x01,0xFF,0xFF,0xFF, +0x00,0x00,0x00,0x00,0x5C,0x54,0x47,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, +0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37,0x37, +0x37,0x37,0x37,0x37,0x00,0x00,0x31,0x00,0x94,0x00,0xFA,0x00,0x66,0x01,0xD9,0x01, +0x57,0x02,0xE6,0x02,0x8A,0x03,0x4F,0x04,0x44,0x05,0x84,0x06,0x42,0x08,0xEB,0x0A, +0x98,0x0F,0x55,0x1A,0xF3,0x07,0xFF,0xFF,0x00,0xFF,0xFF,0x00,0x01,0x00,0x00,0x01, +0x01,0x01,0xFF,0x01,0x01,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0x00,0x01,0x00,0x00,0x01, +0xFF,0xFF,0x01,0xFF,0xFF,0x01,0x01,0x01,0x00,0xFF,0xFF,0x00,0x01,0x00,0x00,0x01, +0xFF,0xFF,0x01,0xFF,0xFF,0x01,0x01,0x01,0xFF,0xFF,0x00,0xFF,0x01,0xFF,0xFF,0x00, +0x00,0x00,0x01,0x00,0xFF,0x01,0x00,0x01,0x01,0x01,0x00,0x00,0x00,0xFF,0xFF,0x00, +0x01,0x00,0x00,0x01,0xFF,0xFF,0x01,0xFF,0xFF,0x01,0x01,0x01,0x64,0x00,0x00,0x00, +0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x64,0x00,0x00,0x00,0x9C,0xFF,0xFF,0xFF, +0x9C,0xFF,0xFF,0xFF,0x94,0x05,0x02,0x00,0x8C,0x05,0x02,0x00,0x80,0x05,0x02,0x00, +0x74,0x05,0x02,0x00,0x64,0x05,0x02,0x00,0x54,0x05,0x02,0x00,0x40,0x05,0x02,0x00, +0x2C,0x05,0x02,0x00,0x14,0x05,0x02,0x00,0xFC,0x04,0x02,0x00,0x0A,0x0B,0x0B,0x0B, +0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x00,0x00,0x01,0x05,0x00,0x07,0x02,0x06,0x04,0x08, +0x03,0x09,0x00,0x00,0x01,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x00,0x00, +0x01,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x00,0x00,0x05,0x05,0x05,0x05, +0x05,0x05,0x08,0x08,0x08,0x09,0x07,0x06,0x03,0x02,0x08,0x09,0x04,0x09,0x09,0x08, +0x30,0x01,0x30,0x01,0x30,0x01,0x30,0x01,0x30,0x01,0x30,0x01,0x30,0x01,0x62,0x01, +0x50,0x01,0x44,0x01,0x74,0x01,0x96,0x01,0x84,0x02,0xC4,0x02,0x5A,0x01,0x46,0x01, +0xEE,0x01,0x30,0x01,0x44,0x01,0x50,0x01,0x05,0x05,0x05,0x05,0x05,0x05,0x09,0x08, +0x08,0x09,0x07,0x06,0x03,0x02,0x08,0x09,0x04,0x09,0x09,0x08,0x00,0x01,0x02,0x03, +0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0B,0x0A,0x09,0x08,0x07,0x06,0x05,0x04, +0x03,0x02,0x01,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x01,0x02, +0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x02,0x01,0x00,0x00,0x01,0x02,0x03, +0x04,0x05,0x06,0x07,0x08,0x00,0x00,0x01,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, +0x08,0x00,0x01,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00, +0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00, +0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00, +0x38,0x70,0x00,0x00,0x01,0x00,0x02,0x00,0x02,0x00,0x01,0x00,0x04,0x80,0x00,0x00, +0x08,0x40,0x00,0x00,0x10,0x20,0x00,0x00,0x20,0x10,0x00,0x00,0x40,0x08,0x00,0x00, +0x80,0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00, +0x38,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x52,0x02,0x00,0x00,0x52,0x02,0x00,0x00,0x92,0x02,0x00,0x00, +0xD2,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x90,0x00,0x00,0x00,0x90,0x00,0x00,0x00,0xE0,0x00,0x00,0x00, +0xF0,0x00,0x00,0x00,0x00,0x00,0x40,0x02,0x40,0x02,0x00,0x00,0xC0,0x00,0xC0,0x00, +0x40,0x00,0x40,0x00,0x00,0x09,0x09,0x00,0x03,0x03,0x01,0x01,0x01,0x01,0x01,0x01, +0x01,0x01,0x01,0x01,0x01,0x04,0x04,0x04,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, +0x08,0x00,0x01,0x02,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x02,0x01,0x00, +0x00,0x01,0x02,0x03,0x04,0x00,0x01,0x02,0x03,0x04,0x00,0x00,0x07,0x80,0x03,0x00, +0x38,0x70,0x00,0x00,0xC0,0x0F,0x00,0x00,0x08,0x40,0x00,0x00,0x10,0x20,0x00,0x00, +0x20,0x10,0x00,0x00,0x40,0x08,0x00,0x00,0x80,0x04,0x00,0x00,0x00,0x03,0x00,0x00, +0x07,0x80,0x03,0x00,0x38,0x70,0x00,0x00,0xC0,0x0F,0x00,0x00,0x01,0x00,0x02,0x00, +0x02,0x00,0x01,0x00,0x04,0x80,0x00,0x00,0x08,0x40,0x00,0x00,0x10,0x20,0x00,0x00, +0x20,0x10,0x00,0x00,0x40,0x08,0x00,0x00,0x80,0x04,0x00,0x00,0x00,0x03,0x00,0x00, +0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x07,0x80,0x03,0x00, +0x38,0x70,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, +0x01,0x03,0x03,0x03,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x03,0x03, +0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x03,0x03,0x03,0x03, +0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00, +0x01,0x00,0x02,0x00,0x02,0x00,0x01,0x00,0x04,0x80,0x00,0x00,0x08,0x40,0x00,0x00, +0x10,0x20,0x00,0x00,0x20,0x10,0x00,0x00,0x40,0x08,0x00,0x00,0x80,0x04,0x00,0x00, +0x00,0x03,0x00,0x00,0x07,0x80,0x03,0x00,0x38,0x70,0x00,0x00,0xC0,0x0F,0x00,0x00, +0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x01,0x02,0x00,0x01,0x02,0x03, +0x04,0x05,0x06,0x07,0x08,0x00,0x01,0x02,0x0B,0x02,0x0B,0x0B,0x0B,0x02,0x00,0x00, +0x0B,0x02,0x0B,0x0B,0x0B,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x0A,0x00,0x00,0x00,0xDC,0x00,0x0A,0x05,0xF3,0x01,0xA0,0x05,0x04,0x00,0x96,0x00, +0x01,0x14,0x96,0x05,0x04,0x64,0x00,0x00,0x2C,0x01,0x46,0x00,0x01,0x0A,0x08,0x00, +0x27,0x00,0x27,0x00,0xA9,0x02,0x79,0x05,0x00,0x04,0x02,0x00,0x04,0x04,0x02,0x00, +0x01,0x00,0x00,0x00,0x10,0x04,0x02,0x00,0x02,0x01,0x00,0x14,0x14,0x14,0x14,0x00, +0x2F,0x2F,0x00,0x00,0xFF,0xFF,0x00,0x00,0xFF,0xFF,0x00,0x00,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x0B,0x00,0x00,0x00,0x00,0x00,0x1A,0x00,0x33,0x00,0x4D,0x00, +0x66,0x00,0x80,0x00,0x9A,0x00,0xB3,0x00,0xCD,0x00,0xE6,0x00,0x00,0x01,0x00,0x00, +0x39,0x00,0x71,0x00,0xA7,0x00,0xDA,0x00,0x0A,0x01,0x36,0x01,0x5E,0x01,0x83,0x01, +0xA4,0x01,0xC2,0x01,0x01,0x00,0x00,0x00,0xF1,0xD8,0xFF,0xFF,0xF1,0xD8,0xFF,0xFF, +0xF1,0xD8,0xFF,0xFF,0xF1,0xD8,0xFF,0xFF,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, +0x64,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x32,0x19,0x00,0x00,0x01,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x21,0x00,0x00,0x3C,0x0C,0x18,0x32,0x0D, +0x1B,0x28,0x0E,0x17,0x1E,0x0F,0x17,0x14,0x11,0x1D,0x00,0x00,0x01,0x01,0x04,0x01, +0x01,0x00,0x00,0x00,0x01,0x01,0x01,0x02,0x0A,0x0B,0x0B,0x0B,0x0C,0x0C,0x0C,0x0C, +0x0C,0x0C,0x00,0x00,0x94,0x05,0x02,0x00,0x8C,0x05,0x02,0x00,0x80,0x05,0x02,0x00, +0x74,0x05,0x02,0x00,0x64,0x05,0x02,0x00,0x54,0x05,0x02,0x00,0x40,0x05,0x02,0x00, +0x2C,0x05,0x02,0x00,0x14,0x05,0x02,0x00,0xFC,0x04,0x02,0x00,0x5D,0x00,0x44,0x00, +0x5A,0x00,0x72,0x00,0x8A,0x00,0xA3,0x00,0xBB,0x00,0xD0,0x00,0xE3,0x00,0xF2,0x00, +0xFD,0x00,0x02,0x01,0x61,0x00,0x4D,0x00,0x68,0x00,0x84,0x00,0xA2,0x00,0xBF,0x00, +0xDA,0x00,0xF2,0x00,0x06,0x01,0x13,0x01,0x1A,0x01,0x00,0x00,0x65,0x00,0x58,0x00, +0x79,0x00,0x9D,0x00,0xC1,0x00,0xE3,0x00,0x02,0x01,0x1C,0x01,0x2E,0x01,0x37,0x01, +0x6A,0x00,0x67,0x00,0x91,0x00,0xBE,0x00,0xEA,0x00,0x13,0x01,0x35,0x01,0x4E,0x01, +0x5B,0x01,0x00,0x00,0x72,0x00,0x7C,0x00,0xB2,0x00,0xEB,0x00,0x23,0x01,0x52,0x01, +0x75,0x01,0x87,0x01,0x7C,0x00,0x99,0x00,0xE2,0x00,0x2E,0x01,0x72,0x01,0xA5,0x01, +0xC1,0x01,0x00,0x00,0x45,0x00,0x63,0x00,0x96,0x00,0xC8,0x00,0xF0,0x00,0x07,0x01, +0x51,0x00,0x88,0x00,0xD3,0x00,0x15,0x01,0x3C,0x01,0x00,0x00,0x67,0x00,0xCC,0x00, +0x40,0x01,0x8A,0x01,0x99,0x00,0x66,0x01,0x38,0x70,0x00,0x00,0xFF,0xFF,0x00,0x00, +0xFF,0x00,0xFF,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x30,0x75,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,0xA7,0x9A,0x7E,0xD7, +0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x64,0x00,0xC8,0x00,0x58,0x02,0x60,0x09,0x5A,0x00,0xDC,0x00,0x4B,0x0F,0x28,0x5A, +0xFA,0x00,0xC2,0x01,0xB0,0x04,0xB8,0x0B,0x64,0x00,0xDC,0x00,0x4B,0x0F,0x28,0x64, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x3C,0x50,0x10,0x32,0x32,0x37,0x3C, +0x96,0x00,0x9C,0xFF,0x28,0x23,0x04,0x00,0x2C,0x01,0x38,0xFF,0x32,0x23,0x04,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x01,0x04,0x01, +0x90,0x01,0x90,0x01,0x00,0x00,0x00,0x00,0x04,0x01,0x16,0x01,0xD4,0x01,0xF4,0x01, +0x50,0x46,0x00,0x00,0xC8,0x00,0x01,0x01,0x14,0x14,0x0C,0x20,0x00,0x02,0x02,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x5B,0x00,0x26,0x02,0x20,0x03,0x64,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x28,0x00,0x26,0x02,0x20,0x03,0x58,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x0D,0x0D,0x3C,0x46,0x3C,0x00,0x08,0x04,0x1E,0x32,0x0F,0x3C,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,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,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x43,0xF6,0x7B,0xA7, +0x48,0x00,0x3D,0x84,0x48,0x00,0x3B,0xC4,0x48,0x00,0x3B,0xE2,0x48,0x00,0x3C,0x00, +0x48,0x00,0x3C,0x1E,0x48,0x00,0x3C,0x3C,0x48,0x00,0x3C,0x5A,0x48,0x00,0x3C,0x78, +0x48,0x00,0x3C,0x96,0x48,0x00,0x3C,0xEC,0x48,0x00,0x3C,0xEE,0x48,0x00,0x3C,0xF0, +0x48,0x00,0x3C,0xF2,0x48,0x00,0x3C,0xF4,0x48,0x00,0x3C,0xF6,0x48,0x00,0x3C,0xF8, +0x48,0x00,0x3C,0xFA,0x48,0x00,0x3C,0xFC,0x48,0x00,0x3C,0xFE,0x48,0x00,0x3D,0x00, +0x48,0x00,0x3D,0x02,0x48,0x00,0x3D,0x04,0x48,0x00,0x3D,0x06,0x48,0x00,0x3D,0x08, +0x48,0x00,0x3D,0x0A,0x48,0x00,0x3D,0x0C,0x48,0x00,0x3D,0x0E,0x48,0x00,0x3D,0x10, +0x48,0x00,0x3D,0x12,0x48,0x00,0x3D,0x14,0x48,0x00,0x3D,0x16,0x48,0x00,0x3D,0x18, +0x48,0x00,0x3D,0x1A,0x48,0x00,0x3D,0x1C,0x48,0x00,0x3D,0x1E,0x48,0x00,0x3D,0x20, +0x48,0x00,0x3D,0x22,0x48,0x00,0x3D,0x25,0x48,0x00,0x3D,0x28,0x48,0x00,0x3D,0x2B, +0x48,0x00,0x3D,0x2E,0xC4,0x7B,0x00,0x00,0xC6,0x7B,0x00,0x00,0xDA,0x7B,0x00,0x00, +0xEC,0x7B,0x00,0x00,0xFA,0x7B,0x00,0x00,0x12,0x7C,0x00,0x00,0x20,0x7C,0x00,0x00, +0x58,0x7C,0x00,0x00,0x60,0x7C,0x00,0x00,0x76,0x7C,0x00,0x00,0x9A,0x7C,0x00,0x00, +0xE4,0x7C,0x00,0x00,0xE6,0x7C,0x00,0x00,0xEE,0x7C,0x00,0x00,0xF6,0x7C,0x00,0x00, +0xFE,0x7C,0x00,0x00,0x38,0x7D,0x00,0x00,0x3A,0x7E,0x00,0x00,0xCE,0x7E,0x00,0x00, +0xE4,0x7E,0x00,0x00,0x0C,0x80,0x00,0x00,0x24,0x81,0x00,0x00,0x90,0x81,0x00,0x00, +0x92,0x81,0x00,0x00,0x94,0x81,0x00,0x00,0x96,0x81,0x00,0x00,0x98,0x81,0x00,0x00, +0x9A,0x81,0x00,0x00,0x9C,0x81,0x00,0x00,0x9E,0x81,0x00,0x00,0xA0,0x81,0x00,0x00, +0xA2,0x81,0x00,0x00,0xE6,0x28,0xE8,0x4C,0x96,0x06,0x46,0x20,0x00,0x81,0x44,0xF0, +0x01,0x3C,0x38,0x17,0x84,0x00,0x40,0xF0,0xBC,0x00,0xDD,0x0F,0x08,0x16,0x24,0x32, +0x40,0x4E,0x5C,0x6A,0x40,0x00,0x40,0x08,0x04,0x31,0x00,0x29,0x44,0x1E,0xFF,0xFF, +0xD5,0x33,0x40,0x00,0x44,0x08,0x04,0x31,0x00,0x29,0x44,0x1D,0xFF,0xFF,0xD5,0x2C, +0x40,0x00,0x48,0x08,0x04,0x31,0x00,0x29,0x44,0x1B,0xFF,0xFF,0xD5,0x25,0x40,0x00, +0x4C,0x08,0x04,0x31,0x00,0x29,0x46,0x1F,0xFF,0x7F,0xD5,0x1C,0x40,0x00,0x50,0x08, +0x04,0x31,0x00,0x29,0x46,0x1F,0xFE,0xFF,0xD5,0x15,0x40,0x00,0x54,0x08,0x04,0x31, +0x00,0x29,0x46,0x1F,0xFD,0xFF,0xD5,0x0E,0x40,0x00,0x58,0x08,0x04,0x31,0x00,0x29, +0x46,0x1F,0xFB,0xFF,0xD5,0x07,0x40,0x00,0x5C,0x08,0x04,0x31,0x00,0x29,0x46,0x1F, +0xF7,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x29,0xDD,0x9E, +0x44,0x24,0x80,0x90,0x96,0x06,0xB4,0x62,0x40,0x10,0x20,0x08,0x44,0x0F,0xFE,0xFF, +0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x46,0x10,0x00,0x82,0xA0,0x0A,0x58,0x00, +0x00,0x01,0xA8,0x0A,0xDD,0x9E,0x46,0x20,0x00,0x82,0x96,0x06,0xA0,0xD5,0x40,0x10, +0x40,0x08,0x44,0x0E,0xFF,0xFF,0xFE,0x1E,0xFE,0x0F,0xA8,0x15,0xDD,0x9E,0x46,0x00, +0x00,0x82,0xA0,0x05,0x92,0x10,0x96,0x06,0xDD,0x9E,0xFC,0x40,0x46,0x60,0x00,0x81, +0x96,0x2F,0x40,0x90,0x20,0x08,0x04,0x03,0x00,0x2A,0x44,0x7F,0xC0,0xFF,0xFE,0x3E, +0x40,0x00,0x24,0x04,0x14,0x03,0x00,0x2A,0x96,0x6F,0x40,0x90,0xC0,0x08,0x46,0x0F, +0xFC,0x0F,0x04,0x13,0x00,0x2A,0x50,0x00,0x0F,0xFF,0xFE,0x46,0x40,0x10,0xA4,0x04, +0x14,0x13,0x00,0x2A,0x96,0xAF,0x04,0x93,0x00,0x2A,0x40,0x11,0x60,0x08,0x46,0x2C, +0x0F,0xFF,0x50,0x21,0x0F,0xFF,0x40,0x24,0x88,0x02,0xFE,0x8F,0x14,0x23,0x00,0x2A, +0x04,0x13,0x00,0x2B,0x96,0xEF,0x92,0x26,0x94,0x4E,0xFE,0x5F,0x14,0x13,0x00,0x2B, +0x97,0x2F,0x40,0x12,0x20,0x08,0x04,0x43,0x00,0x2B,0x97,0x6F,0xFF,0x3E,0xFF,0x0F, +0x14,0x43,0x00,0x2B,0x04,0x13,0x00,0x2B,0x40,0x52,0xC0,0x08,0xFE,0x0E,0xFE,0x2F, +0x14,0x03,0x00,0x2B,0x49,0x00,0x04,0x80,0x00,0x0F,0x80,0x20,0x54,0x10,0x00,0x3F, +0x04,0x03,0x03,0x98,0x92,0x06,0x94,0x06,0xFE,0x0F,0x14,0x03,0x03,0x98,0x00,0x0F, +0x80,0x24,0x04,0x23,0x03,0x99,0x96,0x2F,0x94,0x46,0x44,0x0F,0xF0,0x3F,0xFE,0x16, +0xFE,0x0F,0x14,0x03,0x03,0x99,0x49,0x00,0x04,0x60,0xFC,0xC0,0x46,0x30,0x00,0x82, +0x44,0x41,0x00,0x00,0xB4,0xA3,0x54,0x10,0x87,0xFF,0xFF,0x2F,0xB6,0x83,0xB4,0x83, +0x94,0x92,0x92,0x8B,0x40,0x42,0x2C,0x08,0xFF,0x0F,0xB6,0x83,0x88,0x40,0x4C,0x01, +0x00,0x05,0xA0,0x59,0xAA,0x41,0xD5,0xFC,0xB4,0x23,0x44,0x0E,0xFF,0xFF,0xFE,0x0E, +0xB6,0x03,0xDD,0x9E,0x46,0x30,0x00,0x82,0x44,0x41,0x00,0x00,0xB4,0xA3,0x54,0x10, +0x87,0xFF,0xFF,0x2F,0xB6,0x83,0xB4,0x83,0x94,0x92,0x92,0x8B,0x40,0x42,0x2C,0x08, +0xFF,0x0F,0xB6,0x83,0x88,0x40,0x80,0x23,0x4C,0x01,0x00,0x05,0xA3,0x01,0xA9,0x09, +0xD5,0xFC,0xB4,0x23,0x44,0x0E,0xFF,0xFF,0xFE,0x0E,0xB6,0x03,0xDD,0x9E,0xFC,0x00, +0x46,0x50,0x00,0x80,0x46,0x3F,0xFF,0x00,0x9D,0x99,0x9D,0x2E,0x94,0xA9,0x88,0x40, +0x38,0x11,0x18,0x00,0x38,0x21,0x0C,0x00,0x94,0x4C,0xFE,0x57,0x96,0x48,0x18,0x12, +0x80,0x01,0xDC,0xF5,0xFC,0x80,0xFC,0x00,0x46,0x10,0x00,0x80,0x46,0x2F,0xFE,0xFF, +0x50,0x50,0x80,0x08,0x50,0x61,0x0F,0xF1,0x50,0x41,0x0F,0xF0,0x50,0x30,0x80,0x0E, +0x94,0xA9,0x88,0x40,0x38,0x11,0x18,0x00,0x38,0x21,0x10,0x00,0x94,0x4C,0xFE,0x57, +0x96,0x48,0x18,0x12,0x80,0x01,0xDB,0xF5,0xFC,0x80,0x46,0x20,0x00,0x80,0x84,0x7E, +0x50,0x51,0x01,0xC4,0xB4,0x85,0x96,0x06,0xFE,0xE6,0xFE,0x1F,0xB6,0x05,0x96,0x57, +0xB4,0x65,0x40,0x00,0xA0,0x08,0x44,0x1F,0xF8,0xFF,0xFE,0x5E,0xFE,0x47,0xB6,0x25, +0xDD,0x9E,0x46,0x20,0x00,0x81,0x40,0x00,0x40,0x08,0x04,0x11,0x00,0x11,0x96,0x49, +0xFE,0x0F,0x14,0x01,0x00,0x11,0xDD,0x9E,0x46,0x20,0x00,0x81,0x40,0x00,0x40,0x08, +0x04,0x11,0x00,0x10,0x96,0x49,0xFE,0x0F,0x14,0x01,0x00,0x10,0xDD,0x9E,0x46,0x20, +0x00,0x81,0x04,0x11,0x00,0x10,0x92,0x30,0x40,0x10,0xC0,0x08,0xFE,0x0F,0x14,0x01, +0x00,0x10,0xDD,0x9E,0x46,0x20,0x00,0x81,0x04,0x11,0x00,0x11,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x0F,0x14,0x01,0x00,0x11,0xDD,0x9E,0x46,0x20,0x00,0x81,0x84,0x30, +0x04,0x31,0x00,0x12,0x96,0x1F,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E, +0x46,0x20,0x00,0x80,0x84,0x30,0x04,0x31,0x00,0x70,0x96,0x1F,0xFE,0x5E,0xFE,0x0F, +0x14,0x01,0x00,0x70,0xDD,0x9E,0x46,0x20,0x00,0x81,0x84,0x3E,0x04,0x31,0x00,0x13, +0x96,0x06,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x13,0xDD,0x9E,0x46,0x20,0x00,0x81, +0x96,0x06,0x04,0x31,0x00,0x12,0x40,0x10,0x20,0x08,0x44,0x0F,0xFE,0xFF,0xFE,0x1E, +0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E,0x46,0x20,0x00,0x81,0x96,0x0F,0x40,0x10, +0x48,0x08,0x04,0x31,0x00,0x12,0x46,0x0F,0xFF,0x3F,0x50,0x00,0x0F,0xFF,0xFE,0x1E, +0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E,0x46,0x00,0x00,0x81,0x04,0x00,0x00,0x12, +0x92,0x12,0x96,0x0F,0xDD,0x9E,0x44,0x14,0x80,0x10,0xB4,0x01,0x92,0x0D,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x11,0xFA,0x50,0xAE,0x80,0xB4,0x01,0x92,0x0E,0x96,0x06, +0xC0,0x06,0x44,0x04,0x80,0x11,0x44,0x10,0x00,0x40,0xAE,0x40,0x44,0x14,0x80,0x10, +0xB4,0x01,0x92,0x0F,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x11,0x44,0x2F,0xFF,0x80, +0xAE,0x80,0xB4,0x01,0x92,0x09,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x11,0x84,0x22, +0xAE,0x40,0x44,0x14,0x80,0x10,0xB4,0x01,0x92,0x0C,0x96,0x06,0xC0,0x05,0x44,0x04, +0x80,0x11,0xFA,0x40,0xAE,0x80,0xB4,0x01,0x92,0x17,0x96,0x06,0xC0,0x06,0x44,0x04, +0x80,0x12,0x44,0x1F,0xFF,0x80,0xAE,0x40,0xDD,0x9E,0x00,0x00,0x46,0x10,0x00,0x81, +0x04,0x30,0x80,0x39,0xC0,0x0B,0x5A,0x00,0x01,0x0D,0x5A,0x00,0x02,0x0E,0x84,0x40, +0x5A,0x08,0x03,0x0D,0x46,0x23,0xF3,0xC0,0xD5,0x09,0x46,0x23,0x00,0x00,0xD5,0x06, +0x46,0x23,0x51,0x40,0xD5,0x03,0x46,0x23,0xA2,0x80,0x46,0x1C,0x0C,0x3F,0x50,0x10, +0x8F,0xFF,0x40,0x01,0x84,0x02,0x46,0x10,0x00,0x81,0xFE,0x17,0x14,0x00,0x80,0x39, +0xDD,0x9E,0x46,0x20,0x00,0x81,0x96,0x0F,0x40,0x10,0x58,0x08,0x04,0x31,0x00,0x39, +0x46,0x0F,0xF3,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x39, +0xDD,0x9E,0x46,0x10,0x00,0x81,0x04,0x30,0x80,0x39,0xC0,0x11,0x5A,0x00,0x01,0x09, +0x5A,0x00,0x02,0x0A,0x5A,0x08,0x03,0x0B,0x44,0x03,0xFF,0xC0,0xD5,0x08,0x44,0x01, +0x55,0x40,0xD5,0x05,0x44,0x02,0xAA,0x80,0xD5,0x02,0x84,0x00,0x44,0x23,0xFF,0xC0, +0x44,0x1C,0x00,0x3F,0xFE,0x5E,0xFE,0x16,0xFE,0x0F,0x46,0x10,0x00,0x81,0x14,0x00, +0x80,0x39,0xDD,0x9E,0x46,0x20,0x00,0x81,0x96,0x0F,0x04,0x31,0x00,0x39,0x40,0x10, +0x28,0x08,0x44,0x0F,0xF3,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x39,0xDD,0x9E, +0x46,0x20,0x00,0x81,0x46,0x31,0x55,0x10,0x50,0x31,0x84,0x40,0x50,0x51,0x00,0xE4, +0xB4,0x25,0xA8,0xE9,0xA1,0x2A,0x84,0x78,0xFE,0xE6,0xE6,0x05,0xA8,0xEA,0xE8,0x3A, +0x44,0xF0,0x06,0x10,0x38,0x07,0x80,0x00,0x40,0xF0,0x3C,0x00,0x4A,0x00,0x3C,0x00, +0x06,0x1C,0x28,0x3E,0x54,0x00,0x46,0x03,0xA3,0x2A,0x92,0x26,0x50,0x00,0x03,0x00, +0x94,0x4E,0x14,0x01,0x00,0x38,0x58,0x10,0x80,0x3A,0xD5,0x22,0x84,0x00,0x92,0x26, +0x14,0x01,0x00,0x38,0x94,0x4E,0xD5,0x1C,0x46,0x05,0x55,0x55,0x92,0x26,0x50,0x00, +0x05,0x55,0x94,0x4E,0x14,0x01,0x00,0x38,0x58,0x10,0x80,0x15,0xD5,0x11,0x46,0x0A, +0xAA,0xAA,0x92,0x26,0x50,0x00,0x0A,0xAA,0x94,0x4E,0x14,0x01,0x00,0x38,0x58,0x10, +0x80,0x2A,0xD5,0x06,0x84,0x1F,0x14,0x01,0x00,0x38,0x58,0x10,0x80,0x3F,0x14,0x11, +0x00,0x39,0xDD,0x9E,0xDD,0x9E,0x44,0x24,0x50,0x00,0x96,0x06,0xB4,0x62,0x94,0x42, +0x84,0x1B,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x04,0x50,0x28,0xB4,0x00, +0x92,0x04,0x96,0x06,0xDD,0x9E,0x92,0x00,0x46,0x20,0x00,0x83,0x84,0x3C,0x04,0x31, +0x00,0x10,0x96,0x0F,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x10,0xDD,0x9E,0x46,0x20, +0x00,0x83,0x96,0x06,0x04,0x31,0x00,0x10,0x40,0x10,0x20,0x08,0x44,0x0F,0xFE,0xFF, +0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x10,0xDD,0x9E,0xFC,0x40,0x46,0x20,0x00,0x83, +0x44,0x5F,0xFE,0xFF,0x83,0x82,0xBB,0x18,0x46,0x40,0x0F,0x03,0xFE,0xEE,0xBB,0x98, +0xBB,0x18,0x50,0x42,0x0C,0x0F,0x92,0x68,0x40,0x31,0xA0,0x08,0x58,0x31,0x80,0x46, +0xBB,0x98,0xBF,0x39,0xBE,0x3A,0x04,0x91,0x00,0x3B,0xFF,0xE6,0xFF,0xA6,0x40,0x44, +0x90,0x02,0x04,0x90,0x80,0x02,0xBB,0x3C,0xB5,0x41,0x89,0x24,0xA1,0x0B,0x96,0xDF, +0x88,0xEA,0x04,0xA0,0x80,0x01,0x98,0x5C,0xBB,0x18,0x88,0xCA,0x58,0x31,0x81,0x00, +0xBB,0x98,0xBB,0x18,0x92,0x68,0x40,0x31,0xA0,0x08,0x58,0x31,0x80,0x46,0xBB,0x98, +0xBF,0xB9,0xBE,0xBA,0x14,0x91,0x00,0x3B,0x96,0xC9,0xB9,0x3C,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x5F,0xB9,0xBC,0xB4,0x20,0xB9,0xB9,0xA0,0x41,0xB9,0xBA,0xA0,0x42, +0xB9,0xBB,0xA4,0x46,0xB8,0x3C,0x92,0x10,0x40,0x00,0x40,0x08,0xFE,0x0F,0xB8,0xBC, +0xB8,0x18,0xFF,0x46,0xBD,0x98,0xFC,0xC0,0xFC,0x20,0x46,0x20,0x00,0x83,0x44,0x3F, +0xFE,0xFF,0x83,0x82,0xBC,0x18,0x94,0x4A,0xFE,0xE6,0xBB,0x98,0xBB,0x18,0x50,0x51, +0x00,0xE4,0x92,0x68,0x40,0x31,0xA0,0x08,0x58,0x31,0x80,0x48,0xBB,0x98,0xBB,0x18, +0x50,0x61,0x00,0xE8,0x58,0x31,0x81,0x00,0xBB,0x98,0x50,0x71,0x00,0xEC,0x88,0x20, +0x50,0x21,0x00,0xF0,0x46,0x30,0x00,0x83,0x4C,0x00,0x80,0x12,0xA2,0xC1,0xB6,0x65, +0x83,0x80,0xBB,0x3F,0x02,0x40,0x01,0x7E,0xB6,0x66,0xBB,0x7F,0xB6,0x67,0xB4,0x62, +0x92,0x70,0x40,0x31,0xC0,0x08,0xFE,0xE7,0xB6,0x62,0xD5,0xED,0x83,0x83,0xB9,0x18, +0x44,0x0F,0xFE,0xFF,0xFE,0x0E,0xB8,0x98,0xFC,0xA0,0xFC,0x20,0x46,0x10,0x00,0x83, +0x44,0x2F,0xFE,0xFF,0x83,0x81,0xBB,0x18,0x46,0x40,0xF0,0x3C,0xFE,0x9E,0xBA,0x98, +0xBA,0x18,0x50,0x32,0x00,0xF0,0x92,0x48,0x40,0x21,0x20,0x08,0x58,0x21,0x00,0x46, +0xBA,0x98,0xBE,0x39,0xB4,0x80,0xBD,0x3A,0xFF,0x9E,0x88,0xC4,0xA1,0x01,0xBF,0x3B, +0xFF,0x5E,0x88,0xA4,0xA1,0x02,0xBA,0x3C,0xFE,0xFE,0x88,0x83,0xA0,0xC3,0x54,0x21, +0x00,0xF0,0xBE,0xB9,0x98,0x13,0xBD,0xBA,0xBC,0xBB,0x96,0x81,0xB8,0x3C,0x92,0x10, +0x40,0x00,0x40,0x08,0xFE,0x17,0xB8,0xBC,0xFC,0xA0,0x44,0x14,0x80,0x90,0x84,0x5E, +0xB4,0x61,0x96,0x06,0xFE,0x9E,0xFE,0x87,0xB6,0x41,0xB4,0x81,0x44,0x2F,0xFF,0xEF, +0x94,0xC4,0xFE,0xA6,0xFE,0x9F,0xB6,0x41,0xB4,0x61,0x44,0x2F,0xFB,0xFF,0xFE,0x9E, +0x40,0x00,0x28,0x08,0xFE,0x17,0xB6,0x01,0x46,0x10,0x00,0x80,0x84,0x18,0x50,0x30, +0x82,0x6C,0xB4,0x43,0xFE,0x16,0xB6,0x03,0xB4,0x43,0x44,0x0F,0xF0,0xFF,0xFE,0x16, +0x58,0x00,0x0A,0x00,0xB6,0x03,0xDD,0x9E,0x52,0x00,0x00,0x01,0x46,0x20,0x00,0x80, +0x96,0x06,0xC9,0x08,0x40,0x10,0x20,0x08,0x04,0x31,0x00,0x79,0x44,0x0F,0xFE,0xFF, +0xD5,0x07,0x40,0x10,0x40,0x08,0x04,0x31,0x00,0x79,0x44,0x0E,0xFF,0xFF,0xFE,0x1E, +0xFE,0x0F,0x14,0x01,0x00,0x79,0xDD,0x9E,0x46,0x10,0x00,0x82,0x5A,0x08,0x01,0x0B, +0x50,0x30,0x80,0x20,0xB4,0x03,0x58,0x00,0x00,0x02,0xB6,0x03,0xB4,0x43,0x84,0x1E, +0xD5,0x04,0x04,0x20,0x80,0x08,0x84,0x1D,0xFE,0x16,0x14,0x00,0x80,0x08,0xDD,0x9E, +0x46,0x30,0x00,0x83,0x54,0x10,0x00,0x01,0x50,0x51,0x83,0xB8,0xB4,0x85,0x40,0x20, +0xA0,0x08,0x44,0x1F,0xFE,0xFF,0xFE,0x66,0xFE,0x57,0xB6,0x25,0xB4,0x25,0xE6,0x01, +0x44,0x0F,0xFD,0xFF,0x40,0xF7,0xA4,0x08,0xFE,0x0E,0x40,0x00,0x3C,0x04,0xB6,0x05, +0xDD,0x9E,0x46,0x30,0x00,0x83,0x54,0x10,0x00,0x01,0x50,0x51,0x83,0xB8,0xB4,0x85, +0x40,0x20,0xA8,0x08,0x44,0x1F,0xFB,0xFF,0xFE,0x66,0xFE,0x57,0xB6,0x25,0xB4,0x25, +0xE6,0x01,0x44,0x0F,0xF7,0xFF,0x40,0xF7,0xAC,0x08,0xFE,0x0E,0x40,0x00,0x3C,0x04, +0xB6,0x05,0xDD,0x9E,0x44,0x27,0x20,0x68,0x96,0x01,0xB4,0x22,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x70,0xB4,0x22,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x78,0xB4,0x22,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x80,0xB4,0x22,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x88,0xB4,0x22,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x47,0xB6,0x22,0x44,0x27,0x20,0x90,0xB4,0x22,0x92,0x30,0x40,0x10, +0xC0,0x08,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0xC8,0x0A,0x44,0x17,0x20,0x00,0x46,0x0F, +0xEF,0xFF,0xB4,0x41,0x50,0x00,0x0F,0xFF,0xFE,0x16,0xD5,0x09,0x5A,0x08,0x01,0x09, +0x44,0x17,0x20,0x00,0x46,0x00,0x10,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0xDD,0x9E, +0x44,0x27,0x20,0x00,0x84,0x3E,0xB4,0x62,0x96,0x06,0xFE,0x5E,0xFE,0x0F,0xB6,0x02, +0xDD,0x9E,0x44,0x27,0x20,0x04,0x84,0x3E,0xB4,0x62,0x96,0x06,0xFE,0x5E,0xFE,0x0F, +0xB6,0x02,0xDD,0x9E,0x44,0x27,0x20,0x00,0x96,0x06,0xB4,0x62,0x40,0x10,0x40,0x08, +0x44,0x0E,0xFF,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0xC9,0x06,0x44,0x27, +0x10,0x00,0xB4,0x22,0xFE,0x0F,0xD5,0x08,0x5A,0x18,0x01,0x08,0x44,0x27,0x10,0x00, +0xFE,0x03,0xB4,0x22,0xFE,0x0E,0xB6,0x02,0xDD,0x9E,0x5A,0x18,0x01,0x07,0x44,0x27, +0x10,0x04,0xB4,0x22,0xFE,0x0F,0xD5,0x07,0xC9,0x07,0x44,0x27,0x10,0x04,0xFE,0x03, +0xB4,0x22,0xFE,0x0E,0xB6,0x02,0xDD,0x9E,0x5A,0x18,0x01,0x07,0x44,0x27,0x10,0x08, +0xB4,0x22,0xFE,0x0F,0xD5,0x07,0xC9,0x07,0x44,0x27,0x10,0x08,0xFE,0x03,0xB4,0x22, +0xFE,0x0E,0xB6,0x02,0xDD,0x9E,0x44,0x16,0x00,0x00,0x5A,0x08,0x01,0x06,0xB4,0x01, +0x58,0x00,0x00,0x01,0xD5,0x04,0xB4,0x41,0x84,0x1E,0xFE,0x16,0xB6,0x01,0xDD,0x9E, +0x44,0x16,0x00,0x00,0xB4,0x41,0x5A,0x08,0x01,0x06,0x44,0x01,0x00,0x00,0xFE,0x17, +0xD5,0x04,0x44,0x0E,0xFF,0xFF,0xFE,0x16,0xB6,0x01,0xDD,0x9E,0x44,0x06,0x00,0x00, +0xB4,0x00,0x92,0x1C,0x96,0x06,0xDD,0x9E,0x44,0x16,0x00,0x30,0x5A,0x08,0x01,0x06, +0xB4,0x01,0x58,0x00,0x01,0x00,0xD5,0x05,0xB4,0x41,0x44,0x0F,0xFE,0xFF,0xFE,0x16, +0xB6,0x01,0xDD,0x9E,0x44,0x36,0x38,0x14,0x40,0x00,0x20,0x08,0x40,0x20,0x20,0x09, +0xB4,0x03,0x96,0x6F,0x92,0x18,0x40,0x00,0x60,0x08,0xFE,0x17,0x44,0x26,0x38,0x08, +0xB6,0x03,0xB4,0x62,0x40,0x00,0xE0,0x08,0x46,0x1C,0x0F,0xFF,0x50,0x10,0x8F,0xFF, +0xFE,0x5E,0xFE,0x47,0xB6,0x22,0x44,0x16,0x38,0x04,0x44,0x01,0x00,0x00,0xB4,0x41, +0xFE,0x17,0xB6,0x01,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x03,0x96,0x06,0xC0,0xFD, +0x44,0x04,0x80,0x0C,0x84,0x28,0xAE,0x40,0xDD,0x9E,0x44,0x36,0x34,0x14,0x40,0x00, +0x20,0x08,0x40,0x20,0x20,0x09,0xB4,0x03,0x96,0x6F,0x92,0x18,0x40,0x00,0x60,0x08, +0xFE,0x17,0x44,0x26,0x34,0x08,0xB6,0x03,0xB4,0x62,0x40,0x00,0xE0,0x08,0x46,0x1C, +0x0F,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x5E,0xFE,0x47,0xB6,0x22,0x44,0x16,0x34,0x04, +0x44,0x01,0x00,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0x44,0x14,0x80,0x0C,0xB4,0x01, +0x92,0x13,0x96,0x06,0xC0,0xFD,0x44,0x04,0x80,0x0E,0x84,0x28,0xAE,0x40,0xDD,0x9E, +0xFC,0x01,0xB6,0x3F,0x80,0x22,0xF3,0x81,0x49,0xFF,0xFF,0xA6,0xB4,0x1F,0xF1,0x01, +0x49,0xFF,0xFF,0xCD,0xFC,0x81,0x44,0x16,0x3C,0x04,0xB4,0x01,0x58,0x00,0x00,0x01, +0xB6,0x01,0xDD,0x9E,0x44,0x26,0x38,0x04,0xB4,0x22,0x44,0x06,0x38,0x04,0x92,0x28, +0x96,0x46,0xC9,0xFB,0xB4,0x40,0x84,0x3E,0xFE,0x56,0xB6,0x20,0x80,0x20,0xB4,0x01, +0x92,0x08,0x96,0x06,0xC8,0xFD,0x44,0x26,0x34,0x04,0xB4,0x22,0x44,0x06,0x34,0x04, +0x92,0x28,0x96,0x46,0xC9,0xFB,0xB4,0x40,0x84,0x3E,0xFE,0x56,0xB6,0x20,0x80,0x20, +0xB4,0x01,0x92,0x08,0x96,0x06,0xC8,0xFD,0xDD,0x9E,0xFC,0x00,0x46,0x60,0x00,0x83, +0x83,0x86,0xB8,0x27,0x58,0x00,0x00,0x01,0xB8,0xA7,0xB8,0x27,0x58,0x00,0x01,0x00, +0xB8,0xA7,0x84,0x01,0x49,0x00,0x08,0x9B,0xB9,0x27,0x84,0x1E,0xFE,0x0E,0xB8,0xA7, +0xB9,0x27,0x44,0x0F,0xFE,0xFF,0xFE,0x0E,0xB8,0xA7,0xFC,0x80,0xFC,0x40,0x46,0x60, +0x00,0x83,0x84,0xFE,0x83,0x86,0x04,0x93,0x00,0x14,0x96,0x06,0x40,0x74,0x9C,0x02, +0xFE,0x3F,0xB8,0x94,0x96,0x5F,0xBF,0x14,0x40,0x00,0xE0,0x08,0x46,0x1F,0x0F,0xFF, +0x50,0x10,0x8F,0xFF,0xFE,0x7E,0xFE,0x47,0xB9,0x94,0x97,0x2F,0xB9,0x14,0x40,0x02, +0x44,0x08,0x46,0x4F,0xF8,0x1F,0x50,0x42,0x0F,0xFF,0xFF,0x0E,0xFF,0x07,0xBC,0x94, +0x97,0x6F,0x40,0x02,0xAC,0x08,0xBD,0x14,0x44,0x1E,0x07,0xFF,0xFF,0x4E,0xFF,0x47, +0xBD,0x94,0xB8,0x15,0x54,0x21,0x03,0xFF,0x92,0x0A,0x40,0x00,0x28,0x08,0xFE,0x17, +0xB8,0x95,0x96,0xF7,0x40,0x01,0xC0,0x08,0xB9,0x15,0x46,0x3F,0xF8,0x0F,0x50,0x31, +0x8F,0xFF,0xFE,0xCE,0xFE,0xC7,0xBB,0x95,0xFC,0xC0,0x46,0x00,0x00,0x83,0x04,0x00, +0x00,0x16,0x96,0x01,0xDD,0x9E,0xFC,0x00,0x46,0x40,0x00,0x83,0x84,0x61,0x83,0x84, +0xBE,0x13,0x84,0xBE,0xFE,0x1E,0xFF,0x76,0xFE,0x2F,0xB8,0x93,0xFE,0x5E,0x40,0x30, +0xA0,0x08,0xB9,0x60,0x44,0x0F,0xFE,0xFF,0xFE,0x46,0xFE,0x5F,0xB9,0xE0,0x96,0xA7, +0x40,0x01,0x20,0x08,0xBA,0x13,0x44,0x1F,0xE0,0xFF,0xFE,0x8E,0xFE,0x87,0xBA,0x93, +0xFC,0x80,0x46,0x30,0x00,0x83,0x96,0x8F,0x50,0x41,0x81,0x84,0xB6,0x04,0xA8,0x61, +0xA0,0x62,0x84,0x1C,0xFE,0x0E,0xFE,0x87,0xA8,0xA2,0xDD,0x9E,0x46,0x20,0x00,0x83, +0x96,0x27,0x04,0x11,0x00,0x60,0x92,0x25,0x94,0x4D,0xFE,0x47,0x14,0x11,0x00,0x60, +0xDD,0x9E,0x46,0x20,0x00,0x83,0x84,0x3E,0x04,0x31,0x00,0x12,0x96,0x06,0xFE,0x5E, +0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E,0xB4,0x40,0x46,0x10,0x00,0x80,0x14,0x20, +0x80,0x48,0xA0,0x01,0x14,0x00,0x80,0x4A,0xDD,0x9E,0xFC,0x40,0x46,0x20,0x00,0x83, +0x50,0x61,0x00,0xE4,0x83,0x82,0xBB,0x18,0x50,0x71,0x00,0xE8,0x58,0x31,0x81,0x00, +0xBB,0x98,0xBB,0x18,0x50,0x91,0x00,0xEC,0x92,0x68,0x40,0x31,0xA0,0x08,0x58,0x31, +0x80,0x88,0xBB,0x98,0x8C,0x02,0x84,0xA0,0x50,0x21,0x00,0xF0,0x46,0x30,0x00,0x83, +0xD1,0x22,0x22,0x30,0x00,0x00,0x22,0x40,0x7F,0xFF,0x40,0x31,0xC0,0x08,0x88,0x64, +0xB6,0x66,0x22,0x30,0x00,0x02,0x22,0x40,0x00,0x01,0x40,0x31,0xC0,0x08,0x88,0x64, +0xB6,0x67,0x22,0x30,0x00,0x04,0x22,0x40,0x00,0x03,0x40,0x31,0xC0,0x08,0x88,0x64, +0xB6,0x69,0xB4,0x62,0x8C,0xA1,0x92,0x70,0x40,0x31,0xC0,0x08,0xB6,0x62,0x97,0x68, +0x8C,0x0C,0xD5,0xDD,0x83,0x83,0xB9,0x18,0x44,0x0F,0xFE,0xFF,0xFE,0x0E,0xB8,0x98, +0xFC,0xC0,0x46,0x20,0x00,0x83,0x96,0x0F,0x04,0x31,0x00,0x12,0x40,0x10,0x40,0x08, +0x44,0x0C,0xFF,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x12,0xDD,0x9E,0xFC,0x00, +0x84,0x40,0x44,0x64,0x00,0xC0,0x38,0x30,0x88,0x00,0x38,0x40,0x09,0x01,0x99,0x56, +0x40,0x31,0xB0,0x08,0x95,0x69,0xFE,0xE7,0x96,0xD9,0x8C,0x41,0xAC,0xE8,0x5A,0x28, +0x14,0xF4,0xFC,0x80,0x46,0x20,0x00,0x83,0x96,0x06,0x04,0x31,0x00,0x26,0x94,0x41, +0x84,0x1D,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x26,0xDD,0x9E,0x46,0x20,0x00,0x81, +0x96,0x06,0xA0,0xD5,0x40,0x10,0x20,0x08,0x44,0x0F,0xFE,0xFF,0xFE,0x1E,0xFE,0x0F, +0xA8,0x15,0xDD,0x9E,0xC9,0x08,0x46,0x20,0x00,0x91,0x84,0x3E,0xB4,0x62,0x96,0x06, +0xFE,0x5E,0xD5,0x0A,0x5A,0x18,0x01,0x0B,0x46,0x20,0x00,0x91,0x96,0x06,0xB4,0x62, +0x94,0x41,0x84,0x1D,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0xFC,0x00,0x44,0x00, +0x00,0xC8,0x49,0x00,0x07,0x6C,0x44,0x14,0x50,0x0C,0x46,0x60,0x00,0x81,0xB4,0x01, +0x58,0x00,0x00,0x04,0xB6,0x01,0xFA,0x18,0x49,0x00,0x07,0x6A,0x83,0x86,0xB8,0x1C, +0x58,0x00,0x01,0x00,0xB8,0x9C,0x84,0x01,0x49,0x00,0x07,0x62,0xB9,0x1C,0x44,0x01, +0x00,0x00,0xFE,0x0F,0xB8,0x9C,0x44,0x00,0x01,0xF4,0x49,0x00,0x07,0x50,0xB9,0x1C, +0x46,0x00,0x10,0x00,0xFE,0x0F,0xB8,0x9C,0x44,0x00,0x01,0xF4,0x49,0x00,0x07,0x47, +0xFC,0x80,0x00,0x00,0x46,0x10,0x00,0x80,0x50,0x50,0x80,0x80,0x50,0x10,0x80,0xB0, +0xA2,0x81,0xAA,0xA9,0xD9,0xFE,0xDD,0x9E,0x46,0x10,0x00,0x80,0x50,0x50,0x80,0xB0, +0x50,0x10,0x80,0xC4,0xA2,0x81,0xAA,0xA9,0xD9,0xFE,0xDD,0x9E,0xB4,0x40,0x46,0x10, +0x00,0x80,0x14,0x20,0x80,0x47,0xA0,0x01,0x14,0x00,0x80,0x49,0xDD,0x9E,0x46,0x10, +0x00,0x80,0x50,0x50,0x81,0x40,0x50,0x30,0x81,0x4C,0xA6,0x41,0xA6,0x80,0x94,0x4C, +0xFE,0x57,0x96,0x48,0xAE,0x68,0x00,0x10,0x00,0x0D,0x00,0x20,0x00,0x0C,0x94,0x4C, +0xFE,0x57,0x96,0x48,0x10,0x12,0x80,0x0C,0x8C,0xA1,0x8C,0x02,0xDB,0xEF,0xDD,0x9E, +0x46,0x10,0x00,0x80,0x50,0x50,0x80,0x40,0x50,0x30,0x80,0x46,0xA6,0x41,0xA6,0x80, +0x94,0x4C,0xFE,0x57,0x96,0x48,0xAE,0x68,0x00,0x10,0x00,0x0D,0x00,0x20,0x00,0x0C, +0x94,0x4C,0xFE,0x57,0x96,0x48,0x10,0x12,0x80,0x0C,0x8C,0xA1,0x8C,0x02,0xDB,0xEF, +0xDD,0x9E,0xA6,0x81,0x80,0xA0,0x94,0x94,0x08,0x12,0x80,0x05,0xFE,0x8F,0x46,0x10, +0x00,0x80,0x96,0x90,0x10,0x20,0x80,0x48,0xA6,0xA9,0xA6,0xC5,0x94,0x94,0xFE,0x9F, +0x96,0x90,0x10,0x20,0x80,0x54,0xA6,0x83,0xA6,0xC2,0x94,0x94,0xFE,0x9F,0x96,0x90, +0x10,0x20,0x80,0x49,0xA6,0xAB,0xA6,0xEA,0x94,0x94,0xFE,0x9F,0x96,0x90,0x10,0x20, +0x80,0x55,0xA6,0x84,0x84,0x0F,0xFE,0x86,0x40,0x31,0x40,0x08,0x46,0x4F,0xFF,0x0F, +0x04,0x20,0x80,0x12,0x50,0x42,0x0F,0xFF,0xFE,0xA6,0xFE,0x9F,0x14,0x20,0x80,0x12, +0xA6,0xAC,0xFE,0x16,0x40,0x20,0x40,0x08,0x04,0x00,0x80,0x15,0xFE,0x26,0xFE,0x17, +0x14,0x00,0x80,0x15,0xDD,0x9E,0x46,0x10,0x00,0x80,0x50,0x50,0x80,0x58,0x50,0x30, +0x80,0x5E,0xA6,0x41,0xA6,0x80,0x94,0x4C,0xFE,0x57,0x96,0x48,0xAE,0x68,0x00,0x10, +0x00,0x0D,0x00,0x20,0x00,0x0C,0x94,0x4C,0xFE,0x57,0x96,0x48,0x10,0x12,0x80,0x0C, +0x8C,0xA1,0x8C,0x02,0xDB,0xEF,0xDD,0x9E,0xA6,0x81,0x80,0xA0,0x94,0x94,0x08,0x12, +0x80,0x05,0xFE,0x8F,0x46,0x10,0x00,0x80,0x96,0x90,0x10,0x20,0x80,0x60,0xA6,0xA9, +0xA6,0xC5,0x94,0x94,0xFE,0x9F,0x96,0x90,0x10,0x20,0x80,0x6C,0xA6,0x83,0xA6,0xC2, +0x94,0x94,0xFE,0x9F,0x96,0x90,0x10,0x20,0x80,0x61,0xA6,0xAB,0xA6,0xEA,0x94,0x94, +0xFE,0x9F,0x96,0x90,0x10,0x20,0x80,0x6D,0xA6,0x84,0x84,0x0F,0xFE,0x86,0x40,0x31, +0x40,0x08,0x46,0x4F,0xFF,0x0F,0x04,0x20,0x80,0x18,0x50,0x42,0x0F,0xFF,0xFE,0xA6, +0xFE,0x9F,0x14,0x20,0x80,0x18,0xA6,0xAC,0xFE,0x16,0x40,0x20,0x40,0x08,0x04,0x00, +0x80,0x1B,0xFE,0x26,0xFE,0x17,0x14,0x00,0x80,0x1B,0xDD,0x9E,0xE6,0x06,0xE8,0x57, +0xFC,0x20,0x46,0x70,0x00,0x81,0x44,0xF0,0x10,0x74,0x38,0x07,0x80,0x00,0x40,0xF0, +0x3C,0x00,0xDD,0x0F,0x06,0x16,0x34,0x62,0x76,0x44,0x04,0x03,0x80,0x20,0x54,0x10, +0x87,0xFF,0x92,0x0B,0x40,0x00,0x2C,0x08,0xD5,0x0C,0x54,0x10,0x87,0xFF,0x40,0x00, +0xC0,0x08,0x04,0x23,0x80,0x20,0x46,0x1F,0x80,0x0F,0x50,0x10,0x8F,0xFF,0xFE,0x56, +0xFE,0x47,0x14,0x13,0x80,0x20,0xFC,0xA0,0x04,0x03,0x80,0x21,0x54,0x10,0x87,0xFF, +0x92,0x0B,0x40,0x00,0x2C,0x08,0xD5,0x0C,0x54,0x10,0x87,0xFF,0x40,0x00,0xC0,0x08, +0x04,0x23,0x80,0x21,0x46,0x1F,0x80,0x0F,0x50,0x10,0x8F,0xFF,0xFE,0x56,0xFE,0x47, +0x14,0x13,0x80,0x21,0xFC,0xA0,0x83,0x87,0xB8,0x22,0x54,0x10,0x87,0xFF,0x92,0x0B, +0x40,0x00,0x2C,0x08,0xFE,0x47,0xB9,0xA2,0xFC,0xA0,0x80,0xC1,0x49,0xFF,0xFD,0x54, +0x54,0x13,0x03,0xFF,0x95,0x8E,0x04,0x13,0x83,0x98,0x44,0x0F,0x00,0x3F,0xFE,0x46, +0xFE,0x77,0x14,0x13,0x83,0x98,0x49,0xFF,0xFD,0x40,0xFC,0xA0,0xDD,0x9E,0x46,0x20, +0x00,0x83,0x96,0x06,0x40,0x10,0x60,0x08,0x04,0x31,0x00,0xC0,0x46,0x0F,0xEF,0xFF, +0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0xC0,0xDD,0x9E,0x46,0x20, +0x00,0x83,0x96,0x06,0x40,0x10,0x64,0x08,0x04,0x31,0x00,0xC0,0x46,0x0F,0xDF,0xFF, +0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0xC0,0xDD,0x9E,0x46,0x20, +0x00,0x83,0x96,0x0F,0x04,0x31,0x00,0x0F,0x40,0x10,0x20,0x08,0x44,0x0F,0xFC,0xFF, +0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x0F,0xDD,0x9E,0x46,0x20,0x00,0x83,0x84,0x3E, +0x04,0x31,0x00,0x0F,0x96,0x06,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x0F,0xDD,0x9E, +0x46,0x20,0x00,0x83,0x96,0x06,0x04,0x31,0x00,0x0F,0x40,0x10,0x28,0x08,0x44,0x0F, +0xFB,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x0F,0xDD,0x9E,0x46,0x10,0x00,0x83, +0x44,0x01,0x00,0x00,0x04,0x20,0x80,0x0E,0xFE,0x17,0x14,0x00,0x80,0x0E,0xDD,0x9E, +0x46,0x20,0x00,0x83,0x46,0x3F,0x7F,0xFF,0x50,0x51,0x00,0x38,0xB4,0x85,0x50,0x31, +0x8F,0xFF,0xFE,0xE6,0xB6,0x65,0xB4,0x85,0x46,0x3E,0xFF,0xFF,0x50,0x31,0x8F,0xFF, +0xFE,0xE6,0xB6,0x65,0x5A,0x08,0x01,0x04,0x84,0x1F,0xD5,0x02,0x84,0x00,0x14,0x01, +0x00,0x0B,0x14,0x01,0x00,0x0C,0x04,0x01,0x00,0x33,0x5A,0x18,0x01,0x0B,0x58,0x00, +0x0F,0xFF,0x14,0x01,0x00,0x33,0x04,0x01,0x00,0x34,0x58,0x00,0x0F,0xFF,0xD5,0x0B, +0x92,0x0C,0x40,0x00,0x30,0x08,0x14,0x01,0x00,0x33,0x04,0x01,0x00,0x34,0x92,0x0C, +0x40,0x00,0x30,0x08,0x14,0x01,0x00,0x34,0xDD,0x9E,0x46,0x10,0x00,0x83,0x96,0x06, +0x50,0x50,0x80,0x38,0xB4,0x85,0x44,0x2F,0xFE,0xFF,0x40,0x30,0x20,0x08,0xFE,0xA6, +0xFE,0x9F,0xB6,0x45,0xB4,0x65,0x44,0x2F,0xFD,0xFF,0x40,0x00,0x24,0x08,0xFE,0x9E, +0xFE,0x17,0xB6,0x05,0xDD,0x9E,0x46,0x20,0x00,0x81,0x50,0x51,0x00,0x90,0x50,0x31, +0x00,0x9A,0x08,0x20,0x00,0x01,0x88,0x41,0x96,0x90,0x18,0x22,0x80,0x01,0xDB,0xFA, +0xDD,0x9E,0xE6,0x25,0xE8,0x33,0x96,0x1F,0x46,0x20,0x00,0x81,0x44,0xF0,0x12,0x7C, +0x38,0x17,0x84,0x00,0x40,0xF0,0xBC,0x00,0x4A,0x00,0x3C,0x00,0x06,0x0E,0x1A,0x28, +0x36,0x00,0x04,0x31,0x00,0x23,0x84,0x30,0xD5,0x1D,0x94,0x04,0x04,0x31,0x00,0x23, +0x44,0x1F,0xFF,0x0F,0xD5,0x17,0x40,0x00,0x20,0x08,0x04,0x31,0x00,0x23,0x44,0x1F, +0xF0,0xFF,0xD5,0x10,0x40,0x00,0x30,0x08,0x04,0x31,0x00,0x23,0x44,0x1F,0x0F,0xFF, +0xD5,0x09,0x46,0x1F,0xFF,0x0F,0x40,0x00,0x40,0x08,0x04,0x31,0x00,0x23,0x50,0x10, +0x8F,0xFF,0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x23,0xDD,0x9E,0x84,0x00,0xDD,0x9E, +0x46,0x20,0x00,0x81,0x54,0x00,0x03,0xFF,0x40,0x10,0x40,0x08,0x04,0x31,0x00,0x49, +0x46,0x0F,0xC0,0x0F,0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x49, +0xDD,0x9E,0xE6,0x06,0xE8,0x52,0xFC,0x20,0x46,0x70,0x00,0x81,0x54,0x60,0x83,0xFF, +0x44,0xF0,0x13,0x10,0x38,0x07,0x80,0x00,0x40,0xF0,0x3C,0x00,0x4A,0x00,0x3C,0x00, +0x06,0x2E,0x46,0x56,0x66,0x16,0x83,0x87,0xB9,0x30,0x92,0x2A,0x40,0x10,0xA8,0x08, +0xFF,0x8F,0xBE,0xB0,0xFC,0xA0,0x83,0x87,0xB8,0x31,0x46,0x2F,0xC0,0x0F,0x50,0x21, +0x0F,0xFF,0x40,0x63,0x40,0x08,0xFE,0x16,0xFE,0x37,0xB8,0xB1,0xFC,0xA0,0x83,0x87, +0xB8,0x30,0x46,0x2F,0xC0,0x0F,0x50,0x21,0x0F,0xFF,0x40,0x63,0x40,0x08,0xFE,0x16, +0xFE,0x37,0xB8,0xB0,0xFC,0xA0,0x83,0x87,0xB9,0x31,0x92,0x2A,0x40,0x10,0xA8,0x08, +0xFF,0x8F,0xBE,0xB1,0xFC,0xA0,0x83,0x87,0xB9,0x32,0x92,0x2A,0x40,0x10,0xA8,0x08, +0xFF,0x8F,0xBE,0xB2,0xFC,0xA0,0x49,0xFF,0xFC,0x0F,0x40,0x13,0x40,0x08,0x04,0x03, +0x83,0x98,0x46,0x6F,0xC0,0x0F,0x50,0x63,0x0F,0xFF,0xFF,0x86,0xFF,0x8F,0x14,0x63, +0x83,0x98,0x49,0xFF,0xFB,0xFA,0xFC,0xA0,0xDD,0x9E,0x54,0x20,0x80,0x03,0xE6,0x08, +0x54,0x10,0x80,0x0C,0x90,0x22,0x4E,0xF2,0x00,0x93,0x96,0x4F,0x46,0x30,0x00,0x81, +0x96,0x8F,0x44,0xF0,0x13,0xC0,0x38,0x07,0x80,0x00,0x40,0xF0,0x3C,0x00,0xDD,0x0F, +0x08,0x24,0x40,0x5E,0x7C,0x9C,0xBC,0xE8,0x50,0x51,0x80,0x9C,0xB4,0x85,0x84,0x1C, +0xFE,0x26,0xFE,0x47,0xB6,0x25,0xB4,0x25,0x44,0x0C,0xFF,0xFF,0x40,0x21,0x40,0x08, +0xFE,0x0E,0xD5,0x72,0x50,0x51,0x80,0x9C,0xB4,0x85,0x84,0x13,0xFE,0x26,0x94,0x4A, +0xFE,0x47,0xB6,0x25,0x40,0x21,0x48,0x08,0xB4,0x05,0x46,0x1F,0xFF,0x3F,0xD5,0x4E, +0x50,0x51,0x80,0x9C,0xB4,0x85,0x44,0x0F,0xFF,0xCF,0xFE,0x26,0x94,0x4C,0xFE,0x47, +0xB6,0x25,0x40,0x21,0x50,0x08,0xB4,0x05,0x46,0x1F,0xFC,0xFF,0xD5,0x3F,0x50,0x51, +0x80,0x9C,0xB4,0x85,0x44,0x0F,0xFF,0x3F,0xFE,0x26,0x94,0x4E,0xFE,0x47,0xB6,0x25, +0x40,0x21,0x58,0x08,0xB4,0x05,0x46,0x1F,0xF3,0xFF,0xD5,0x30,0x50,0x51,0x80,0x9C, +0xB4,0x85,0x44,0x0F,0xFC,0xFF,0xFE,0x26,0x40,0x10,0xA0,0x08,0xFE,0x47,0xB6,0x25, +0x40,0x21,0x60,0x08,0xB4,0x05,0x46,0x1F,0xCF,0xFF,0xD5,0x20,0x50,0x51,0x80,0x9C, +0xB4,0x85,0x44,0x0F,0xF3,0xFF,0xFE,0x26,0x40,0x10,0xA8,0x08,0xFE,0x47,0xB6,0x25, +0x40,0x21,0x68,0x08,0xB4,0x05,0x46,0x1F,0x3F,0xFF,0xD5,0x10,0x50,0x51,0x80,0x9C, +0xB4,0x85,0x44,0x0F,0xCF,0xFF,0xFE,0x26,0x40,0x10,0xB0,0x08,0xFE,0x47,0xB6,0x25, +0x40,0x21,0x70,0x08,0xB4,0x05,0x46,0x1C,0xFF,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x0E, +0xFE,0x17,0x14,0x01,0x80,0x27,0xDD,0x9E,0x50,0x51,0x80,0x9C,0xB4,0x85,0x44,0x0F, +0x3F,0xFF,0xFE,0x26,0x40,0x10,0xB8,0x08,0xFE,0x47,0xB6,0x25,0xB4,0x05,0x40,0x21, +0x78,0x08,0x94,0x02,0x92,0x02,0xFE,0x87,0x14,0x21,0x80,0x27,0xDD,0x9E,0xDD,0x9E, +0xFC,0x40,0x80,0xE0,0x44,0x04,0x34,0x38,0x5A,0x78,0x01,0x2B,0x80,0xC1,0xB4,0x20, +0x44,0x9F,0xE0,0xFF,0x58,0x10,0x80,0x02,0xB6,0x20,0x80,0x27,0x49,0xFF,0xFB,0x17, +0x44,0x04,0x34,0x50,0x97,0xA7,0xB4,0x20,0x40,0x63,0x20,0x08,0x40,0x10,0xA4,0x02, +0xFE,0x77,0xB6,0x20,0x80,0x27,0x49,0xFF,0xFB,0x0A,0x44,0x04,0x38,0x3C,0xB4,0x20, +0x58,0x10,0x80,0x02,0xB6,0x20,0x80,0x27,0x49,0xFF,0xFA,0xD6,0x44,0x04,0x38,0x58, +0xB4,0x20,0x40,0x10,0xA4,0x02,0xFF,0x8F,0xB6,0xC0,0x80,0x27,0xD5,0x0E,0xB4,0x20, +0x84,0xDD,0xFE,0x76,0xB6,0x20,0x84,0x21,0x49,0xFF,0xFA,0xF1,0x44,0x04,0x38,0x3C, +0xB4,0x20,0xFF,0x8E,0xB6,0xC0,0x84,0x21,0x49,0xFF,0xFA,0xBE,0xFC,0xC0,0x92,0x00, +0xC8,0x04,0x44,0x07,0x20,0x0C,0xD5,0x03,0x44,0x07,0x20,0x10,0xB4,0x00,0x40,0x00, +0x30,0x08,0x92,0x0C,0xDD,0x9E,0xFC,0x00,0x80,0xC1,0x49,0xFF,0xFF,0xF3,0x84,0x40, +0x80,0x22,0xE2,0x46,0xE8,0x05,0x38,0x10,0x0A,0x0A,0x8C,0x41,0xD5,0xFB,0xFC,0x80, +0x44,0x27,0x20,0x1C,0x54,0x00,0x0F,0xFF,0xB4,0x22,0x92,0x2C,0x40,0x10,0xB0,0x08, +0xFE,0x47,0xB6,0x22,0xDD,0x9E,0x44,0x27,0x20,0x34,0x54,0x00,0x0F,0xFF,0xB4,0x22, +0x92,0x2C,0x40,0x10,0xB0,0x08,0xFE,0x47,0xB6,0x22,0xDD,0x9E,0x44,0x27,0x20,0x38, +0x54,0x00,0x0F,0xFF,0xB4,0x22,0x92,0x2C,0x40,0x10,0xB0,0x08,0xFE,0x47,0xB6,0x22, +0xDD,0x9E,0x44,0x27,0x20,0x54,0x84,0x30,0xB4,0x62,0x96,0x1F,0xFE,0x5E,0xFE,0x0F, +0xB6,0x02,0xDD,0x9E,0x44,0x27,0x20,0x54,0x96,0x1F,0xB4,0x62,0x40,0x10,0x20,0x08, +0x44,0x0F,0xF0,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x27,0x20,0x58, +0x96,0x1F,0x40,0x10,0x40,0x08,0xB4,0x62,0x46,0x0F,0xFF,0x0F,0x50,0x00,0x0F,0xFF, +0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x04,0x00,0x9C,0xA6,0x00,0xDD,0x9E, +0x46,0x10,0x00,0x81,0x04,0x00,0x80,0x6E,0x58,0x00,0x00,0x01,0x14,0x00,0x80,0x6E, +0xDD,0x9E,0xFC,0x00,0x44,0x04,0x2C,0x1C,0x84,0x41,0xB4,0x20,0x80,0x62,0x58,0x10, +0x81,0x00,0xB6,0x20,0xB4,0x20,0x58,0x10,0x80,0x80,0xB6,0x20,0x44,0x04,0x28,0x1C, +0x44,0x14,0x24,0x1C,0x49,0xFF,0xFA,0x96,0xFC,0x80,0x46,0x20,0x00,0x80,0x84,0x30, +0x50,0x41,0x01,0xD0,0xB4,0x64,0x96,0x1F,0xFE,0x5E,0xFE,0x47,0xB6,0x24,0xB4,0x64, +0x44,0x1F,0xFF,0x0F,0x94,0x04,0xFE,0x5E,0xFE,0x0F,0xB6,0x04,0xDD,0x9E,0x00,0x00, +0x44,0x24,0x80,0x88,0x84,0x3E,0xB4,0x62,0x96,0x06,0xFE,0x5E,0xFE,0x47,0xB6,0x22, +0xB4,0x62,0x84,0x3D,0x94,0x01,0xFE,0x5E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0xFC,0x00, +0x44,0x64,0x00,0x18,0x84,0x41,0xAE,0xB0,0xA6,0x72,0x84,0x1E,0xFE,0x46,0xAE,0x72, +0xA6,0x72,0x44,0x30,0x98,0x81,0x58,0x10,0x80,0x02,0xAE,0x72,0xA6,0x72,0x58,0x10, +0x80,0x04,0xAE,0x72,0xA6,0x73,0x58,0x10,0x80,0x02,0xAE,0x73,0xA6,0x73,0x58,0x10, +0x80,0x04,0xAE,0x73,0xA6,0x73,0x58,0x10,0x80,0x08,0xAE,0x73,0xA6,0x73,0x58,0x10, +0x80,0x10,0xAE,0x73,0x44,0x14,0x00,0x10,0xB6,0x61,0xAE,0xB1,0xA6,0x72,0x58,0x10, +0x80,0x10,0xAE,0x72,0xA6,0x72,0x54,0x10,0x80,0xDF,0xAE,0x72,0xA6,0xB2,0x44,0x1F, +0xFF,0x80,0xFE,0x57,0xAE,0x72,0xA6,0x73,0xFE,0x0E,0xAE,0x33,0xA6,0x33,0x58,0x00, +0x00,0x20,0xAE,0x33,0xA6,0x33,0x58,0x00,0x00,0x40,0xAE,0x33,0x49,0x00,0x03,0x0B, +0xE6,0x03,0x44,0x14,0x50,0x10,0xA6,0x32,0xE9,0x10,0x54,0x00,0x00,0xFB,0xAE,0x32, +0xB4,0x01,0x92,0x08,0x40,0x00,0x20,0x08,0xB6,0x01,0x44,0x14,0x70,0x00,0x44,0x0E, +0xFF,0xFF,0xB4,0x41,0xFE,0x16,0xD5,0x11,0x58,0x00,0x00,0x04,0xAE,0x32,0xB4,0x01, +0x92,0x08,0x40,0x00,0x20,0x08,0x58,0x00,0x00,0x98,0xB6,0x01,0x44,0x14,0x70,0x00, +0x44,0x01,0x00,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0xFC,0x80,0x44,0x37,0x20,0x44, +0x46,0x0F,0xC0,0x0F,0xB4,0x23,0x50,0x00,0x0F,0xFF,0xFE,0x46,0x46,0x20,0x24,0x00, +0xFE,0x57,0xB6,0x23,0x44,0x37,0x20,0x48,0x44,0x23,0x00,0x00,0xB4,0x23,0xFE,0x46, +0xFE,0x57,0x44,0x27,0x20,0x4C,0xB6,0x23,0xB4,0x22,0x46,0x30,0x0C,0x00,0x92,0x2C, +0x40,0x10,0xB0,0x08,0x58,0x10,0x82,0x52,0xB6,0x22,0xB4,0x22,0xFE,0x46,0xFE,0x5F, +0xB6,0x22,0x44,0x27,0x20,0x50,0x46,0x30,0x00,0x90,0xB4,0x22,0x92,0x2C,0x40,0x10, +0xB0,0x08,0x58,0x10,0x82,0x40,0xB6,0x22,0xB4,0x22,0xFE,0x0E,0xB6,0x02,0x44,0x27, +0x20,0x04,0x46,0x0F,0xFF,0x0F,0xB4,0x22,0x50,0x00,0x0F,0xFF,0xFE,0x46,0xFE,0x5F, +0xB6,0x22,0xB4,0x62,0x46,0x1F,0xF0,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x5E,0x46,0x30, +0x03,0x00,0xFE,0x5F,0xB6,0x22,0x46,0x20,0x00,0x80,0x44,0x1F,0xF0,0xFF,0x50,0x41, +0x01,0xC0,0xB4,0x64,0xFE,0x5E,0x58,0x10,0x88,0x00,0xB6,0x24,0xB4,0x24,0xFE,0x0E, +0x44,0x12,0x00,0x00,0xFE,0x0F,0xB6,0x04,0xDD,0x9E,0x46,0x20,0x00,0x91,0x40,0x00, +0x40,0x08,0xA0,0x56,0x96,0x49,0xFE,0x0F,0xA8,0x16,0xDD,0x9E,0xE6,0x05,0xE8,0x31, +0x46,0x10,0x00,0x91,0x44,0xF0,0x18,0x34,0x38,0x07,0x80,0x00,0x40,0xF0,0x3C,0x00, +0x4A,0x00,0x3C,0x00,0x06,0x14,0x22,0x30,0x3E,0x00,0xA0,0x0A,0x92,0x10,0x40,0x00, +0x40,0x08,0x58,0x00,0x00,0x0A,0xD5,0x1C,0xA0,0x0A,0x92,0x10,0x40,0x00,0x40,0x08, +0x58,0x00,0x00,0x23,0xD5,0x15,0xA0,0x0A,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00, +0x00,0x46,0xD5,0x0E,0xA0,0x0A,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00,0x00,0x96, +0xD5,0x07,0xA0,0x0A,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00,0x01,0xF4,0xA8,0x0A, +0xDD,0x9E,0x46,0x2F,0xFC,0x0F,0x46,0x10,0x00,0x91,0x50,0x21,0x0F,0xFF,0x5A,0x08, +0x01,0x08,0x04,0x00,0x80,0x0E,0xFE,0x86,0x44,0x02,0x00,0x00,0xD5,0x06,0x04,0x00, +0x80,0x0E,0xFE,0x86,0x44,0x01,0x00,0x00,0xFE,0x87,0x14,0x20,0x80,0x0E,0xA0,0x09, +0x92,0x06,0x94,0x06,0x58,0x00,0x00,0x01,0xA8,0x09,0xDD,0x9E,0xFC,0x00,0x46,0x60, +0x00,0x91,0x84,0x01,0xA8,0x31,0x44,0x00,0x01,0xF4,0xA8,0x32,0x83,0x86,0xB9,0x0A, +0x46,0x0F,0xC0,0x0F,0x50,0x00,0x0C,0x00,0xFE,0x46,0x58,0x10,0x80,0x01,0xB9,0x8A, +0xB9,0x0B,0xFE,0x0E,0x58,0x00,0x02,0x40,0xB8,0x8B,0xB9,0x0C,0x44,0x0F,0xC0,0xC0, +0xFE,0x0E,0x58,0x00,0x20,0x01,0xB8,0x8C,0xB9,0x0D,0x46,0x0E,0x0E,0x0E,0x50,0x00, +0x00,0xE0,0xFE,0x0E,0x46,0x10,0x60,0x11,0x50,0x10,0x82,0x01,0xFE,0x0F,0xB8,0x8D, +0xB9,0x0E,0x46,0x0F,0xFC,0x0C,0x50,0x00,0x00,0xF3,0xFE,0x0E,0x44,0x11,0x01,0x0C, +0xFE,0x0F,0xB8,0x8E,0x49,0xFF,0xFE,0x72,0xE6,0x03,0xA0,0x36,0x92,0x10,0x40,0x00, +0x40,0x08,0xE9,0x1F,0x58,0x00,0x00,0x28,0xA8,0x36,0x83,0x86,0xB8,0x08,0x92,0x10, +0x40,0x00,0x40,0x08,0x58,0x00,0x00,0x28,0xB8,0x88,0xA0,0x35,0x92,0x10,0x40,0x00, +0x40,0x08,0x58,0x00,0x00,0x28,0xA8,0x35,0xA0,0x37,0x92,0x10,0x40,0x00,0x40,0x08, +0x58,0x00,0x00,0x28,0xA8,0x37,0xA0,0x76,0x46,0x00,0x02,0x80,0x96,0x49,0xD5,0x1E, +0x58,0x00,0x46,0x00,0xA8,0x36,0x83,0x86,0xB8,0x08,0x92,0x10,0x40,0x00,0x40,0x08, +0x58,0x00,0x46,0x00,0xB8,0x88,0xA0,0x35,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00, +0x46,0x00,0xA8,0x35,0xA0,0x37,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00,0x46,0x00, +0xA8,0x37,0xA0,0x76,0x46,0x04,0x60,0x00,0x96,0x49,0xFE,0x47,0xA8,0x76,0x83,0x86, +0xB9,0x08,0x96,0x49,0xFE,0x47,0xB9,0x88,0xA0,0x75,0x96,0x49,0xFE,0x47,0xA8,0x75, +0xA0,0x77,0x96,0x49,0xFE,0x0F,0xA8,0x37,0x46,0x00,0x00,0x91,0x83,0x80,0xB9,0x12, +0x58,0x10,0xBF,0x3F,0xB9,0x92,0xBA,0x13,0x44,0x1E,0xE0,0x80,0xFE,0x56,0x44,0x21, +0x00,0x48,0xFE,0x57,0xB9,0x93,0xFC,0x80,0x46,0x00,0x00,0x91,0x50,0x30,0x00,0x44, +0xB4,0x23,0x58,0x10,0x80,0x01,0xB6,0x23,0xB4,0x43,0x84,0x3E,0xFE,0x56,0xB6,0x23, +0xDD,0x9E,0x92,0x00,0x44,0x17,0x30,0x10,0xB4,0x01,0x96,0x06,0xC0,0xFE,0x44,0x17, +0x30,0x10,0xB4,0x01,0x92,0x03,0x96,0x06,0x5A,0x00,0x01,0xFD,0xDD,0x9E,0x84,0xA0, +0xD0,0x05,0x40,0x00,0x00,0x09,0x8C,0xA1,0xD5,0xFC,0xDD,0x9E,0xC9,0x08,0x44,0x24, +0x00,0x0C,0x96,0x06,0xA6,0x50,0x54,0x10,0x80,0xFE,0xD5,0x0A,0x5A,0x18,0x01,0x0B, +0x44,0x24,0x00,0x0C,0x96,0x06,0xA6,0x50,0x94,0x01,0x54,0x10,0x80,0xFD,0xFE,0x0F, +0xAE,0x10,0xDD,0x9E,0xC8,0x04,0x44,0x05,0x00,0x00,0xD5,0x03,0x44,0x05,0x00,0x10, +0xA6,0x02,0xDD,0x9E,0xC9,0x04,0x44,0x15,0x00,0x00,0xD5,0x05,0x5A,0x18,0x01,0x05, +0x44,0x15,0x00,0x10,0xAE,0x0A,0xDD,0x9E,0xC9,0x0A,0x44,0x24,0x80,0x80,0x96,0x06, +0x40,0x10,0x60,0x08,0xB4,0x62,0x46,0x0F,0xEF,0xFF,0xD5,0x0B,0x5A,0x18,0x01,0x0F, +0x44,0x24,0x80,0x80,0x96,0x06,0x40,0x10,0x64,0x08,0xB4,0x62,0x46,0x0F,0xDF,0xFF, +0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0xCA,0x0C,0x44,0x40, +0xFA,0x00,0x42,0x20,0x10,0x24,0x40,0x11,0x04,0x0E,0x44,0x20,0x00,0x64,0x40,0x10, +0x88,0x36,0xD5,0x0E,0x3C,0x2D,0xFB,0xC6,0xFE,0x84,0x44,0x00,0x03,0xE8,0x40,0x21, +0x00,0x57,0x40,0x11,0x04,0x0D,0x44,0x20,0x00,0x64,0x40,0x10,0x88,0x37,0x96,0x49, +0xCB,0x04,0x44,0x05,0x00,0x04,0xD5,0x05,0x5A,0x38,0x01,0x05,0x44,0x05,0x00,0x14, +0xAC,0x40,0xDD,0x9E,0xC8,0x08,0x44,0x14,0x80,0x03,0x84,0x01,0xAE,0x08,0x44,0x15, +0x00,0x08,0xD5,0x09,0x5A,0x08,0x01,0x09,0x44,0x14,0x80,0x03,0x84,0x42,0xAE,0x88, +0x44,0x15,0x00,0x18,0xAE,0x09,0xDD,0x9E,0xC8,0x08,0x44,0x04,0x80,0x03,0x84,0x21, +0xAE,0x40,0x44,0x15,0x00,0x08,0xD5,0x09,0x5A,0x08,0x01,0x0C,0x44,0x04,0x80,0x03, +0x84,0x22,0xAE,0x40,0x44,0x15,0x00,0x18,0xA6,0x08,0x58,0x00,0x00,0x02,0xAE,0x08, +0xDD,0x9E,0xC8,0x08,0x44,0x04,0x80,0x03,0x84,0x21,0xAE,0x40,0x44,0x15,0x00,0x08, +0xD5,0x09,0x5A,0x08,0x01,0x0C,0x44,0x04,0x80,0x03,0x84,0x22,0xAE,0x40,0x44,0x15, +0x00,0x18,0xA6,0x08,0x54,0x00,0x00,0xFD,0xAE,0x08,0xDD,0x9E,0xC9,0x04,0x44,0x15, +0x00,0x00,0xD5,0x05,0x5A,0x18,0x01,0x05,0x44,0x15,0x00,0x10,0xAE,0x08,0xDD,0x9E, +0x84,0x21,0xC0,0x04,0x5A,0x08,0x01,0x06,0x84,0x22,0x44,0x04,0x80,0x03,0xAE,0x40, +0xDD,0x9E,0x44,0x04,0x80,0x00,0xA6,0x03,0x92,0x01,0x96,0x06,0xC0,0x1A,0xFC,0x00, +0x44,0x04,0x80,0x03,0x84,0x22,0xAE,0x40,0x49,0x00,0x10,0x4E,0xC8,0x11,0x3C,0x0D, +0xFB,0x8C,0xC0,0x0E,0x49,0x00,0x0E,0x89,0x80,0xC0,0xC8,0x0A,0x49,0x00,0x01,0x7C, +0x49,0x00,0x11,0x84,0xC0,0x05,0x80,0x06,0x80,0x26,0x49,0x00,0x00,0xE1,0xFC,0x80, +0xDD,0x9E,0x46,0x20,0x00,0x81,0x84,0x3E,0xA0,0xD5,0x96,0x06,0xFE,0x5E,0xFE,0x0F, +0xA8,0x15,0xDD,0x9E,0x46,0x00,0x00,0x81,0xA0,0x05,0x96,0x06,0xDD,0x9E,0x44,0x24, +0x50,0x14,0x84,0x3E,0xB4,0x62,0x96,0x06,0xFE,0x5E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E, +0x44,0x25,0x10,0x04,0xB4,0x22,0x92,0x30,0x40,0x10,0xC0,0x08,0xFE,0x0F,0xB6,0x02, +0xDD,0x9E,0x44,0x15,0x10,0x0C,0xB4,0x01,0x58,0x00,0x01,0x00,0xB6,0x01,0xDD,0x9E, +0x44,0x15,0x10,0x0C,0xB4,0x01,0x92,0x08,0x40,0x00,0x20,0x08,0x58,0x00,0x00,0x01, +0xB6,0x01,0x44,0x15,0x10,0x18,0xB4,0x01,0x96,0x00,0x5A,0x00,0x5A,0xFE,0xDD,0x9E, +0xFC,0x40,0x44,0x00,0x01,0x2C,0x49,0x00,0x00,0x72,0x44,0x75,0x10,0x0C,0x44,0x9F, +0xFF,0x98,0x84,0x00,0xAE,0x38,0xFA,0x18,0x10,0x93,0x80,0x00,0x49,0x00,0x00,0x67, +0x44,0x05,0x10,0x18,0x44,0x65,0x10,0x0C,0xB4,0x00,0x96,0x00,0x5A,0x00,0xA5,0xF3, +0xB4,0x06,0x92,0x08,0x40,0x00,0x20,0x08,0xB6,0x06,0xFC,0xC0,0xC0,0x04,0x5A,0x00, +0x01,0x1F,0xDD,0x9E,0x44,0x14,0x80,0x82,0x44,0x22,0x00,0x00,0xAE,0x08,0x44,0x14, +0x80,0x00,0x44,0x04,0x80,0x80,0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x44,0x24, +0x00,0x00,0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x46,0x20,0x01,0x00,0xB6,0x41, +0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x46,0x20,0x00,0x80,0xD5,0x23,0x44,0x04,0x80,0x82, +0x84,0x20,0xAE,0x40,0x44,0x14,0x80,0x00,0x44,0x04,0x80,0x80,0x44,0x21,0x00,0x00, +0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x44,0x22,0x00,0x00,0xB6,0x41,0xB4,0x60, +0xFE,0x9F,0xB6,0x40,0x44,0x24,0x00,0x00,0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40, +0x46,0x20,0x00,0x80,0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x46,0x20,0x01,0x00, +0xB6,0x41,0xB4,0x60,0xFE,0x9F,0xB6,0x40,0x46,0x20,0x02,0x00,0xB6,0x41,0xB4,0x60, +0xFE,0x9F,0xB6,0x40,0x46,0x20,0x04,0x00,0xB6,0x41,0xB4,0x20,0xFE,0x8F,0xB6,0x40, +0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xFC,0x72,0xFC,0x80,0x84,0x2E,0xFE,0x0C,0x8E,0x01, +0x5A,0x07,0xFF,0x05,0x40,0x00,0x00,0x09,0xD5,0xFB,0xDD,0x9E,0x44,0x10,0x36,0xB0, +0xFE,0x0C,0x8E,0x01,0x5A,0x07,0xFF,0x05,0x40,0x00,0x00,0x09,0xD5,0xFB,0xDD,0x9E, +0xFC,0x00,0x49,0x00,0x19,0x51,0xC8,0x05,0x84,0x01,0x3E,0x07,0xEE,0x2C,0xFC,0x80, +0x84,0x02,0x3E,0x07,0xEE,0x2C,0x49,0x00,0x0C,0x82,0xFC,0x80,0xFC,0x01,0x84,0x00, +0x49,0x00,0x0F,0x70,0x44,0x12,0x04,0xE4,0x84,0x00,0x49,0x00,0x0F,0xBF,0xF0,0x81, +0x84,0x27,0x84,0x00,0x49,0x00,0x10,0x9E,0xF0,0x01,0xFC,0x81,0xFC,0x20,0x3C,0x2D, +0xFD,0xC2,0x5A,0x20,0x01,0x3E,0x80,0xE1,0x80,0xC0,0x49,0x00,0x0F,0x55,0xC8,0x2E, +0x5A,0x68,0x01,0x05,0x49,0x00,0x0F,0x56,0xD5,0x14,0x49,0xFF,0xFF,0xE1,0x80,0xC0, +0xC8,0x10,0x84,0x01,0x49,0xFF,0xFE,0xC7,0x80,0x06,0x84,0x21,0x49,0xFF,0xFE,0x5E, +0x3E,0x67,0xEF,0xB7,0x3E,0x67,0xEF,0xB4,0x3C,0x6F,0xFB,0xCE,0x5A,0x70,0x01,0x21, +0x84,0x01,0x84,0x2F,0x49,0x00,0x2C,0x92,0x84,0x01,0x80,0x20,0x49,0x00,0x2C,0x9C, +0x84,0x01,0x49,0xFF,0xFE,0x89,0x84,0x00,0x49,0x00,0x0F,0x0D,0x49,0x00,0x0F,0x14, +0x84,0x00,0x49,0x00,0x10,0x6F,0x84,0x01,0xD5,0x0C,0x84,0x01,0x49,0xFF,0xFE,0xA3, +0x84,0x00,0x84,0x21,0x49,0xFF,0xFE,0x3A,0x84,0x00,0x3E,0x07,0xEF,0xB7,0x84,0x00, +0xFC,0xA0,0xFC,0x00,0xC0,0x0B,0x5A,0x08,0x01,0x05,0x49,0x00,0x17,0xCA,0xD5,0x06, +0x84,0xC0,0x5A,0x08,0x02,0x05,0x49,0x00,0x17,0xC7,0x80,0xC0,0x84,0x02,0x49,0x00, +0x0F,0x11,0x84,0x02,0x44,0x12,0x04,0xE4,0x49,0x00,0x0F,0x60,0xC6,0x04,0x80,0x06, +0x49,0x00,0x10,0x1B,0x49,0x00,0x17,0xBB,0x49,0x00,0x10,0x29,0x84,0x02,0x84,0x27, +0x49,0x00,0x10,0x38,0xFC,0x80,0x3C,0x0D,0xFD,0xC2,0x5A,0x00,0x01,0x16,0xFC,0x00, +0x49,0x00,0x0E,0xF2,0x80,0xC0,0xC8,0x04,0x49,0xFF,0xFF,0x82,0xD5,0x07,0x5A,0x00, +0x01,0x0B,0x49,0x00,0x0E,0xEC,0x49,0xFF,0xFF,0xCE,0x80,0x06,0x49,0x00,0x0E,0xC3, +0x49,0x00,0x0E,0xCA,0xFC,0x80,0xDD,0x9E,0xFC,0x00,0x49,0x00,0x0D,0x1E,0x5A,0x08, +0x01,0x06,0x84,0x00,0x44,0x10,0x07,0xD0,0xD5,0x04,0x84,0x00,0x44,0x10,0x75,0x30, +0x49,0x00,0x2C,0x2C,0x84,0x00,0x84,0x21,0x49,0x00,0x2C,0x36,0x84,0x00,0x49,0x00, +0x0D,0x15,0xFC,0x80,0x3E,0x07,0xEF,0xB6,0xDD,0x9E,0xFC,0x00,0x64,0x12,0x00,0x43, +0x64,0x00,0x00,0x08,0x64,0x00,0x00,0x08,0x49,0x00,0x29,0x76,0x49,0x00,0x29,0xA9, +0x49,0x00,0x2B,0x9F,0x84,0x00,0x44,0x10,0x00,0xEB,0x49,0x00,0x2B,0xF4,0x49,0x00, +0x2A,0xE5,0x49,0x00,0x2A,0xFA,0x49,0x00,0x2B,0x2D,0x49,0x00,0x0A,0xD0,0x49,0x00, +0x17,0xE4,0x84,0x00,0x3E,0x07,0xEE,0x2D,0xFC,0x80,0x2E,0x07,0xEF,0xB8,0xDD,0x9E, +0xFC,0x00,0x49,0x00,0x0B,0x07,0x49,0x00,0x08,0x97,0x49,0x00,0x19,0x0D,0x49,0x00, +0x18,0x73,0x49,0x00,0x0B,0x9E,0x49,0x00,0x17,0x68,0x49,0x00,0x18,0x6D,0x49,0x00, +0x0B,0xDD,0x49,0x00,0x0C,0xD2,0xC8,0x04,0x49,0x00,0x19,0xBD,0xD5,0x05,0x2E,0x07, +0xF0,0x00,0x96,0x06,0xC8,0xFA,0x49,0xFF,0xFF,0x0D,0x49,0x00,0x1A,0x28,0x49,0xFF, +0xFF,0xA5,0x49,0x00,0x01,0x19,0x49,0x00,0x0C,0xC0,0x80,0x40,0x5A,0x08,0x01,0x08, +0x44,0x00,0x07,0xD0,0x84,0x20,0x80,0x62,0x49,0x00,0x2B,0x45,0x84,0x04,0x3E,0x07, +0xEF,0xBB,0x84,0x00,0x49,0x00,0x19,0x30,0x84,0x00,0x49,0x00,0x01,0x06,0x49,0x00, +0x17,0x32,0x49,0x00,0x0C,0xAA,0xC8,0x04,0x84,0x01,0x49,0x00,0x2A,0xD9,0xFC,0x80, +0xFC,0x20,0x49,0x00,0x0E,0x61,0x80,0xE0,0x49,0x00,0x0F,0xA0,0x5A,0x08,0x01,0x05, +0x84,0x20,0x49,0x00,0x2B,0xC1,0x84,0x01,0x49,0x00,0x2B,0xD9,0x5A,0x08,0x01,0x07, +0x84,0x20,0x49,0x00,0x2B,0xB9,0x49,0x00,0x0E,0x37,0x5A,0x78,0x02,0x5C,0x2E,0x07, +0xEF,0xB6,0x5A,0x00,0x01,0x04,0x48,0x00,0x00,0x8F,0x49,0x00,0x0F,0x87,0x5A,0x00, +0x01,0x04,0x48,0x00,0x00,0x89,0x2E,0x07,0xEF,0xB4,0x5A,0x08,0x01,0x05,0x84,0x00, +0x3E,0x07,0xEF,0xB4,0x44,0x64,0x80,0x80,0x44,0x0B,0xFF,0xFF,0xB4,0x26,0xFE,0x0E, +0xB6,0x06,0xB4,0x26,0x46,0x0F,0xFE,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x0E,0xB6,0x06, +0xB4,0x26,0x46,0x0F,0xFD,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x0E,0xB6,0x06,0x84,0x00, +0x49,0xFF,0xFF,0x2B,0x84,0x00,0x3E,0x07,0xEF,0xB6,0x84,0x00,0x49,0x00,0x0F,0x62, +0xB4,0x26,0x44,0x04,0x00,0x00,0xFE,0x0F,0xB6,0x06,0xB4,0x26,0x46,0x00,0x01,0x00, +0xFE,0x0F,0xB6,0x06,0xB4,0x26,0x46,0x00,0x02,0x00,0xFE,0x0F,0xB6,0x06,0x44,0x10, +0x01,0x90,0x84,0x01,0x49,0x00,0x2B,0x62,0x84,0x01,0x80,0x20,0x49,0x00,0x2B,0x6C, +0x49,0x00,0x0F,0x44,0x80,0xC0,0xC8,0x47,0x84,0x01,0x49,0x00,0x2B,0x80,0x80,0xE0, +0x5A,0x08,0x01,0xF8,0x80,0x26,0x49,0x00,0x2B,0x5F,0x80,0x07,0x49,0x00,0x0F,0x3A, +0xD5,0x3A,0xCF,0x39,0x2E,0x07,0xEF,0xB7,0x44,0x15,0x10,0x20,0xB6,0x01,0xC8,0x26, +0x2E,0x67,0xEF,0xB6,0x5A,0x68,0x01,0x23,0x80,0x26,0x80,0x07,0x49,0xFF,0xFD,0x78, +0x80,0x06,0x49,0xFF,0xFD,0x39,0x80,0x06,0x49,0xFF,0xFD,0x48,0x80,0x06,0x80,0x26, +0x49,0xFF,0xFC,0xF4,0x80,0x07,0x80,0x26,0x49,0xFF,0xFE,0x7A,0x5A,0x08,0x01,0x08, +0x3E,0x07,0xEF,0xB7,0x80,0x07,0x49,0x00,0x0F,0x15,0xD5,0x08,0x80,0x06,0x49,0xFF, +0xFD,0x4A,0x80,0x07,0x80,0x26,0x49,0xFF,0xFC,0xE1,0x2E,0x67,0xEF,0xB7,0xCE,0x0B, +0x2E,0x07,0xEF,0xB4,0x5A,0x08,0x01,0x08,0x49,0x00,0x0F,0x00,0x5A,0x08,0x01,0x04, +0x3E,0x67,0xEF,0xB4,0x2E,0x07,0xEF,0xBB,0xC0,0x04,0x5A,0x00,0x04,0x06,0xFC,0xA0, +0x49,0xFF,0xFF,0x10,0xFC,0xA0,0x49,0x00,0x13,0xBA,0xFC,0xA0,0xFC,0x20,0x84,0xC0, +0x84,0xE2,0x49,0x00,0x2A,0xE4,0x2E,0x07,0xEF,0xB8,0x5A,0x00,0x01,0x0A,0xC0,0x04, +0x5A,0x00,0x02,0x2B,0xD5,0xF7,0x49,0x00,0x0B,0xEC,0x84,0x01,0xD5,0x1A,0x3E,0x07, +0xEF,0xBA,0x3E,0x67,0xEF,0xBB,0x3E,0x67,0xEF,0xB9,0x3E,0x67,0xEF,0xB5,0x49,0x00, +0x0B,0xD4,0x5A,0x08,0x01,0x17,0x49,0x00,0x0B,0xD6,0x5A,0x08,0x02,0x04,0x84,0x03, +0xD5,0x02,0x84,0x04,0x3E,0x07,0xEF,0xB8,0x2E,0x07,0xF0,0xCC,0xC0,0x05,0x84,0x04, +0x3E,0x07,0xEF,0xB8,0xD5,0xD7,0x2E,0x07,0xF0,0xCB,0xC0,0xD4,0x84,0x03,0xD5,0xF9, +0x3E,0x77,0xEF,0xB8,0xD5,0xCF,0x49,0x00,0x0B,0x9F,0x49,0x00,0x0B,0xA6,0x2E,0x07, +0xEF,0xB5,0x3E,0x67,0xEF,0xBC,0xC8,0xC6,0x49,0xFF,0xFF,0x0C,0xD5,0xC3,0x3E,0x07, +0xEF,0xBA,0xDD,0x9E,0xDD,0x9E,0x46,0x10,0x00,0x81,0xC8,0x06,0x04,0x00,0x80,0x33, +0x58,0x00,0x02,0x00,0xD5,0x06,0x04,0x20,0x80,0x33,0x44,0x0F,0xFD,0xFF,0xFE,0x16, +0x14,0x00,0x80,0x33,0xDD,0x9E,0x44,0x24,0x2C,0x14,0x84,0x30,0xB4,0x62,0x96,0x1F, +0xFE,0x5E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x24,0x2C,0x14,0x96,0x1F,0xB4,0x62, +0x40,0x10,0x20,0x08,0x44,0x0F,0xF0,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E, +0xFC,0x20,0x80,0xC0,0x49,0x00,0x03,0x69,0x49,0x00,0x03,0x4D,0x44,0x14,0x24,0x04, +0x44,0x5F,0xFF,0xEF,0xB4,0x01,0x84,0x41,0xFE,0x2E,0xB6,0x01,0xB4,0x01,0x40,0x33, +0x08,0x02,0x44,0x4F,0xFF,0xDF,0x94,0xDD,0xFE,0x26,0xFE,0x1F,0xB6,0x01,0x44,0x04, +0x28,0x04,0xB4,0xC0,0xFF,0xAE,0xB6,0xC0,0xB4,0xC0,0xFF,0xA6,0xFF,0x9F,0xB6,0xC0, +0x44,0x64,0x2C,0x04,0xB4,0xE6,0xFF,0x7E,0xB6,0xA6,0xB4,0xA6,0xFF,0x2E,0xFE,0xE7, +0xB6,0x66,0x80,0x62,0x49,0xFF,0xF4,0xAE,0x49,0x00,0x03,0x32,0x49,0x00,0x03,0x4A, +0xFC,0xA0,0xFC,0x00,0x44,0x24,0x80,0x10,0x84,0x01,0xB4,0x22,0x96,0x46,0xC1,0x0B, +0xAE,0x10,0x3E,0x07,0xEF,0xCC,0x3E,0x07,0xEF,0xC9,0x3C,0x0D,0xFB,0xF0,0x8C,0x01, +0x3C,0x0F,0xFB,0xF0,0x44,0x04,0x80,0x10,0xB4,0x00,0x92,0x0A,0x96,0x06,0xC0,0x0E, +0x44,0x04,0x80,0x11,0x84,0x24,0xAE,0x40,0x49,0x00,0x01,0x05,0x44,0x07,0x20,0x00, +0xB4,0x00,0x92,0x08,0x96,0x06,0x49,0x00,0x00,0xE9,0x44,0x04,0x80,0x10,0x84,0x21, +0xB4,0x00,0x92,0x08,0x96,0x06,0xC0,0x08,0x44,0x04,0x80,0x11,0xAE,0x40,0x3E,0x17, +0xEF,0xCA,0x3E,0x17,0xEF,0xC9,0x44,0x04,0x80,0x10,0xB4,0x20,0x92,0x27,0x96,0x46, +0xC1,0x04,0x44,0x1F,0xFF,0x80,0xAE,0x40,0xB4,0x00,0x92,0x05,0x96,0x06,0xC0,0x05, +0x44,0x04,0x80,0x10,0xFA,0x30,0xAE,0x40,0x44,0x04,0x80,0x10,0xB4,0x20,0x92,0x26, +0x96,0x46,0xC1,0x04,0x44,0x10,0x00,0x40,0xAE,0x40,0xB4,0x00,0x92,0x18,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x13,0x84,0x21,0xAE,0x40,0x44,0x04,0x80,0x10,0xB4,0x00, +0x92,0x15,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x12,0xFA,0x30,0xAE,0x40,0xFC,0x80, +0xFC,0x00,0x44,0x24,0x80,0x10,0x84,0x21,0xB4,0x02,0x92,0x04,0x96,0x06,0xC0,0x14, +0xFA,0x00,0xAE,0x10,0x3E,0x17,0xEF,0xCB,0x3E,0x17,0xEF,0xC9,0x3C,0x0D,0xFB,0xF1, +0x8C,0x01,0x3C,0x0F,0xFB,0xF1,0x46,0x00,0x00,0x81,0x04,0x00,0x00,0xF0,0x92,0x03, +0x96,0x06,0x49,0x00,0x00,0x93,0x44,0x14,0x80,0x10,0xB4,0x01,0x92,0x0B,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x11,0x84,0x48,0xAE,0x80,0xB4,0x01,0x92,0x03,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x10,0x84,0x28,0xAE,0x40,0x44,0x04,0x80,0x10,0xB4,0x20, +0x92,0x22,0x96,0x46,0xC1,0x03,0x84,0x24,0xAE,0x40,0xB4,0x00,0x92,0x01,0x96,0x06, +0xC0,0x0E,0x44,0x04,0x80,0x90,0xB4,0x00,0x92,0x01,0x96,0x06,0x5A,0x08,0x01,0x08, +0x44,0x14,0x80,0x10,0x84,0x42,0xAE,0x88,0x3E,0x07,0xEF,0xBC,0x44,0x14,0x80,0x10, +0xB4,0x01,0x92,0x19,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x13,0x84,0x42,0xAE,0x80, +0xB4,0x01,0x92,0x16,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x12,0x44,0x10,0x00,0x40, +0xAE,0x40,0xFC,0x80,0xFC,0x00,0x80,0xC0,0x49,0xFF,0xF3,0xFE,0x46,0x20,0x00,0x81, +0x97,0x86,0x94,0x32,0x04,0x61,0x03,0x93,0x84,0x3B,0xFF,0x8E,0xFF,0x87,0x14,0x61, +0x03,0x93,0x49,0xFF,0xF3,0xEA,0xFC,0x80,0x44,0x04,0x50,0x28,0xB4,0x00,0x92,0x01, +0x96,0x06,0xDD,0x9E,0x44,0x24,0x50,0x1C,0x40,0x00,0x40,0x08,0xB4,0x22,0x96,0x49, +0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x24,0x50,0x1C,0x44,0x1F,0x00,0xFF,0xB4,0x62, +0x40,0x00,0x20,0x08,0xFE,0x5E,0xFE,0x0F,0xB6,0x02,0xDD,0x9E,0x44,0x07,0x21,0x00, +0x46,0x10,0x20,0x00,0xB4,0x40,0xFE,0x57,0xB6,0x20,0x80,0x20,0xB4,0x01,0x92,0x19, +0x96,0x06,0xC8,0xFD,0xDD,0x9E,0x44,0x04,0x80,0x04,0xA6,0x02,0x92,0x01,0x96,0x06, +0xC0,0x08,0x44,0x04,0x80,0x06,0x84,0x22,0xAE,0x40,0x84,0x01,0x3E,0x07,0xEF,0xCE, +0x44,0x04,0x80,0x04,0x84,0x21,0xA6,0x02,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x06, +0xAE,0x40,0x3E,0x17,0xEF,0xCD,0xDD,0x9E,0x3E,0x07,0xEE,0x34,0xDD,0x9E,0x2E,0x07, +0xEE,0x34,0xDD,0x9E,0x44,0x37,0x20,0x00,0x54,0x10,0x00,0x01,0xB4,0x83,0x40,0x20, +0xA0,0x08,0x44,0x1F,0xFE,0xFF,0xFE,0x66,0xFE,0x57,0xB6,0x23,0x3E,0x07,0xEE,0x34, +0xDD,0x9E,0x84,0x01,0x3E,0x07,0xEE,0x35,0xDD,0x9E,0x84,0x00,0x3E,0x07,0xEE,0x35, +0xDD,0x9E,0xC8,0x07,0x44,0x17,0x10,0x18,0xA6,0x08,0x54,0x00,0x00,0xFE,0xD5,0x08, +0x5A,0x08,0x01,0x0F,0x44,0x17,0x10,0x18,0xA6,0x08,0x58,0x00,0x00,0x01,0xAE,0x08, +0x44,0x17,0x10,0x0C,0xB4,0x01,0x58,0x00,0x01,0x00,0xB6,0x01,0xDD,0x9E,0x5A,0x08, +0x02,0x0D,0x44,0x17,0x10,0x18,0xA6,0x08,0x58,0x00,0x00,0x01,0xAE,0x08,0x44,0x07, +0x10,0x0C,0xB4,0x20,0x84,0x20,0xB6,0x20,0xDD,0x9E,0x3C,0x0D,0xFB,0xF8,0xC0,0x0B, +0xA4,0x80,0x3C,0x13,0xF7,0xEC,0x8E,0x21,0xE0,0x41,0xE8,0x05,0xA4,0x40,0x8C,0x21, +0x96,0x49,0xAC,0x40,0xA4,0x80,0x3C,0x13,0xF7,0xEE,0xE2,0x41,0xE9,0x0C,0x84,0x20, +0xAC,0x40,0x44,0x06,0x00,0x34,0x44,0x10,0x00,0x5A,0xB6,0x20,0x84,0x01,0x3E,0x07, +0xEE,0x36,0xD5,0x0B,0x3C,0x1D,0xFB,0xFA,0xC1,0x08,0xA4,0x00,0x88,0x01,0x44,0x16, +0x00,0x34,0xA6,0x00,0x96,0x00,0xB6,0x01,0x44,0x06,0x00,0x00,0xB4,0x00,0x92,0x08, +0x96,0x06,0x5A,0x08,0x01,0x0B,0x44,0x06,0x00,0x30,0xB4,0x20,0x58,0x10,0x80,0x01, +0xB6,0x20,0xB4,0x20,0x96,0x46,0xC9,0xFE,0xDD,0x9E,0xFC,0x40,0x3C,0x0D,0xFB,0xF8, +0x84,0x20,0xAC,0x40,0x3C,0x1D,0xFB,0xFA,0xC1,0x08,0xA4,0x00,0x88,0x01,0x44,0x16, +0x00,0x34,0xA6,0x00,0x96,0x00,0xB6,0x01,0x84,0xE0,0x44,0x94,0x80,0x00,0x44,0xA4, +0x80,0x80,0x00,0x64,0x80,0x02,0xB4,0x0A,0x92,0xC3,0x92,0x13,0x96,0x06,0xFF,0x86, +0xC6,0x16,0x44,0x04,0x80,0x02,0x84,0x28,0xAE,0x40,0x44,0x06,0x00,0x00,0xB4,0x00, +0x92,0x08,0x96,0x06,0x5A,0x08,0x01,0x32,0x44,0x06,0x00,0x30,0xB4,0x20,0x58,0x10, +0x80,0x01,0xB6,0x20,0xB4,0x20,0x96,0x46,0xC9,0xFE,0xFC,0xC0,0x00,0x04,0x80,0x02, +0x40,0x10,0x10,0x09,0xB4,0x0A,0x92,0x14,0x96,0x06,0xFE,0x0E,0xC0,0x08,0x44,0x04, +0x80,0x02,0xFA,0x20,0xAE,0x40,0x49,0xFF,0xFF,0x8A,0x80,0xE6,0x00,0x04,0x80,0x02, +0x40,0x10,0x18,0x09,0xB4,0x0A,0x92,0x16,0x96,0x06,0xFE,0x0E,0xC0,0x07,0x44,0x04, +0x80,0x02,0x44,0x10,0x00,0x40,0xAE,0x40,0xFC,0xC0,0x84,0x01,0x8C,0xE1,0x49,0xFF, +0xFB,0xAE,0x5C,0xF3,0xA7,0x11,0xE9,0xBE,0xFC,0xC0,0x44,0x16,0x00,0x34,0xB6,0x01, +0xDD,0x9E,0x3C,0x0F,0xFB,0xFB,0x02,0x0F,0x80,0x00,0x3C,0x4B,0xF7,0xEE,0x3C,0x1F, +0xFB,0xFA,0x3C,0x5B,0xF7,0xED,0x3C,0x2F,0xFB,0xF9,0x3C,0x3F,0xFB,0xF8,0x3C,0x0B, +0xF7,0xEC,0xDD,0x9E,0x3C,0x0B,0xF7,0xEE,0xDD,0x9E,0x3E,0x07,0xEE,0x36,0xDD,0x9E, +0x2E,0x07,0xEE,0x36,0xDD,0x9E,0x3C,0x0F,0xFB,0xF5,0xDD,0x9E,0x3C,0x0F,0xFB,0xF4, +0xDD,0x9E,0xFC,0x20,0x44,0x14,0x80,0x00,0x44,0x04,0x80,0x80,0xA6,0x8A,0xB4,0x00, +0x92,0x10,0x96,0x06,0xFE,0x16,0xC0,0x05,0x44,0x04,0x80,0x02,0x84,0x41,0xAE,0x80, +0xA6,0x0A,0x40,0x10,0x04,0x09,0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x11,0x96,0x06, +0xFE,0x0E,0xC0,0x05,0x44,0x04,0x80,0x02,0x84,0x22,0xAE,0x40,0x44,0x04,0x80,0x00, +0xA6,0x02,0x40,0x10,0x08,0x09,0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x12,0x96,0x06, +0xFE,0x0E,0x4E,0x02,0x00,0xA4,0x44,0x04,0x80,0x02,0x84,0x24,0xAE,0x40,0x3C,0x0D, +0xFB,0xF9,0x3C,0x2D,0xFB,0xFB,0x84,0x20,0xAC,0x40,0xC2,0x06,0x44,0x16,0x00,0x28, +0xB4,0x21,0x96,0x48,0xAE,0x50,0xC0,0x0B,0xA4,0x80,0x3C,0x13,0xF7,0xED,0x8E,0x21, +0xE0,0x41,0xE8,0x05,0xA4,0x40,0x8C,0x21,0x96,0x49,0xAC,0x40,0x44,0x06,0x00,0x00, +0xB4,0x00,0x92,0x08,0x96,0x06,0x5A,0x00,0x01,0x06,0x84,0xC0,0x44,0x76,0x00,0x30, +0xD5,0x12,0x44,0x06,0x00,0x30,0xB4,0x20,0x58,0x10,0x80,0x01,0xB6,0x20,0xB4,0x20, +0x96,0x46,0xC9,0xFE,0xD5,0xF3,0x84,0x01,0x8C,0xC1,0x49,0xFF,0xFB,0x28,0x5C,0xF3, +0x08,0x01,0xE8,0x66,0x44,0x34,0x80,0x00,0x44,0x24,0x80,0x80,0xA6,0x1A,0x40,0x10, +0x0C,0x09,0xB4,0x02,0x92,0x13,0x96,0x06,0xFE,0x0E,0xC0,0x16,0x44,0x04,0x80,0x02, +0x84,0x28,0xAE,0x40,0x44,0x06,0x00,0x00,0xB4,0x00,0x92,0x08,0x96,0x06,0x5A,0x08, +0x01,0x50,0x44,0x06,0x00,0x30,0xB4,0x20,0x58,0x10,0x80,0x01,0xB6,0x20,0xB4,0x20, +0x96,0x46,0xC9,0xFE,0xD5,0x45,0xA6,0x1A,0x40,0x10,0x10,0x09,0xB4,0x02,0x92,0x14, +0x96,0x06,0xFE,0x0E,0xC0,0x2D,0x44,0x04,0x80,0x02,0xFA,0x20,0xAE,0x40,0x3C,0x2D, +0xFB,0xFB,0x3C,0x0D,0xFB,0xF9,0xC2,0x08,0xA4,0x40,0x88,0x22,0x44,0x26,0x00,0x38, +0xB4,0x42,0x96,0x90,0xAE,0x88,0xC0,0x0B,0xA4,0x80,0x3C,0x13,0xF7,0xED,0x8E,0x21, +0xE0,0x41,0xE8,0x05,0xA4,0x40,0x8C,0x21,0x96,0x49,0xAC,0x40,0x44,0x06,0x00,0x00, +0xB4,0x00,0x92,0x08,0x96,0x06,0x5A,0x00,0x01,0x04,0x84,0xC0,0xD5,0x09,0xB4,0x07, +0x58,0x00,0x00,0x01,0xB6,0x07,0xB4,0x07,0x96,0x06,0xC8,0xFE,0xD5,0xF7,0x44,0x04, +0x80,0x00,0xA6,0x02,0x40,0x10,0x14,0x09,0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x15, +0x96,0x06,0xFE,0x0E,0xC0,0x99,0x44,0x04,0x80,0x02,0xFA,0x30,0xAE,0x40,0x3C,0x0D, +0xFB,0xF5,0xC0,0x04,0x3C,0x0D,0xFB,0xF5,0xDD,0x20,0x44,0x04,0x80,0x00,0xA6,0x02, +0x40,0x10,0x0C,0x09,0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x13,0x96,0x06,0xFE,0x0E, +0xC8,0x14,0x44,0x04,0x80,0x00,0xA6,0x02,0x40,0x10,0x10,0x09,0x44,0x04,0x80,0x80, +0xB4,0x00,0x92,0x14,0x96,0x06,0xFE,0x0E,0xC0,0x1D,0x44,0x04,0x80,0x02,0xFA,0x20, +0xAE,0x40,0x49,0xFF,0xFE,0x64,0xD5,0x16,0x44,0x04,0x80,0x02,0x84,0x28,0xAE,0x40, +0x44,0x06,0x00,0x00,0xB4,0x00,0x92,0x08,0x96,0x06,0x5A,0x08,0x01,0xE4,0x44,0x06, +0x00,0x30,0xB4,0x20,0x58,0x10,0x80,0x01,0xB6,0x20,0xB4,0x20,0x96,0x46,0xC9,0xFE, +0xD5,0xD9,0x44,0x14,0x80,0x00,0xA6,0x0A,0x40,0x20,0x14,0x09,0x44,0x04,0x80,0x80, +0xB4,0x00,0x92,0x15,0x96,0x06,0xFE,0x16,0xC0,0x05,0x44,0x04,0x80,0x02,0xFA,0x50, +0xAE,0x80,0xA6,0x0A,0x40,0x10,0x18,0x09,0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x16, +0x96,0x06,0xFE,0x0E,0xC0,0x06,0x44,0x04,0x80,0x02,0x44,0x10,0x00,0x40,0xAE,0x40, +0xFC,0xA0,0x44,0x16,0x38,0x20,0xB4,0x01,0x58,0x00,0x01,0x00,0xB6,0x01,0x44,0x16, +0x34,0x20,0xB4,0x01,0x58,0x00,0x01,0x00,0xB6,0x01,0xDD,0x9E,0x44,0x26,0x38,0x20, +0x44,0x0F,0xFE,0xFF,0xB4,0x22,0xFE,0x46,0xB6,0x22,0x44,0x16,0x34,0x20,0xB4,0x41, +0xFE,0x16,0xB6,0x01,0xDD,0x9E,0x44,0x16,0x38,0x20,0xB4,0x01,0x58,0x00,0x00,0x01, +0xB6,0x01,0x44,0x16,0x34,0x20,0xB4,0x01,0x58,0x00,0x00,0x01,0xB6,0x01,0xDD,0x9E, +0x44,0x26,0x38,0x20,0x84,0x1E,0xB4,0x22,0xFE,0x46,0xB6,0x22,0x44,0x16,0x34,0x20, +0xB4,0x41,0xFE,0x16,0xB6,0x01,0xDD,0x9E,0x46,0x20,0x00,0x83,0x96,0x06,0x04,0x31, +0x00,0x14,0x94,0x41,0x84,0x1D,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x14,0xDD,0x9E, +0x46,0x20,0x00,0x83,0x96,0x06,0x04,0x31,0x00,0x14,0x94,0x43,0x84,0x17,0xFE,0x1E, +0xFE,0x0F,0x14,0x01,0x00,0x14,0xDD,0x9E,0x40,0x21,0x40,0x08,0x40,0x10,0xA0,0x08, +0x88,0x22,0x88,0x20,0x46,0x00,0x00,0x80,0x14,0x10,0x00,0x78,0xDD,0x9E,0xFC,0x41, +0xF5,0x81,0x81,0x00,0x81,0x22,0x80,0xE3,0x80,0xC4,0x46,0x50,0x00,0x80,0xC0,0x03, +0x5A,0x08,0x01,0x18,0x83,0x85,0xBA,0x77,0x46,0x1F,0xEF,0xFF,0x50,0x10,0x8F,0xFF, +0xFE,0x56,0xB9,0xF7,0x84,0x23,0x49,0xFF,0xED,0x32,0x38,0x14,0x99,0x01,0x84,0x02, +0x49,0xFF,0xF3,0x8E,0x38,0x03,0x98,0x00,0x84,0x22,0x49,0xFF,0xF4,0x8C,0xFC,0xC1, +0x81,0x41,0x83,0x85,0xB9,0x77,0x46,0x00,0x10,0x00,0xFE,0x0F,0xB8,0xF7,0x84,0x23, +0x84,0x01,0x49,0xFF,0xED,0x1C,0x50,0x25,0x7F,0xFF,0x80,0x06,0xF1,0x01,0x96,0x90, +0x49,0xFF,0xFF,0xC4,0xFC,0xC1,0x46,0x20,0x00,0x80,0x96,0x06,0x04,0x31,0x00,0x75, +0x40,0x10,0x28,0x08,0x44,0x0F,0xFB,0xFF,0xFE,0x1E,0xFE,0x0F,0x14,0x01,0x00,0x75, +0xDD,0x9E,0x44,0x14,0x00,0x48,0x84,0x1F,0xB6,0x01,0x44,0x14,0x00,0x4C,0xB6,0x01, +0xDD,0x9E,0x00,0x00,0xE6,0x04,0xE8,0x2E,0x96,0x5F,0x46,0x20,0x00,0x81,0x44,0xF0, +0x29,0xBC,0x38,0x07,0x80,0x00,0x40,0xF0,0x3C,0x00,0xDD,0x0F,0x04,0x12,0x20,0x34, +0x40,0x10,0xC0,0x08,0x04,0x31,0x00,0x32,0x46,0x0F,0xFF,0x0F,0xD5,0x0E,0x40,0x10, +0xD0,0x08,0x04,0x31,0x00,0x32,0x46,0x0F,0xF0,0xFF,0xD5,0x07,0x40,0x10,0xE0,0x08, +0x04,0x31,0x00,0x32,0x46,0x0F,0x0F,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xD5,0x07, +0x04,0x01,0x00,0x32,0x40,0x10,0xF0,0x08,0x94,0x04,0x92,0x04,0xFE,0x47,0x14,0x11, +0x00,0x32,0xDD,0x9E,0xE6,0x08,0xE8,0x43,0x96,0x4F,0x46,0x20,0x00,0x81,0x44,0xF0, +0x2A,0x1C,0x38,0x07,0x80,0x00,0x40,0xF0,0x3C,0x00,0xDD,0x0F,0x08,0x10,0x1A,0x26, +0x32,0x40,0x4E,0x5C,0x04,0x31,0x00,0x28,0x84,0x1C,0xD5,0x2D,0x94,0x4A,0x04,0x31, +0x00,0x28,0x84,0x13,0xD5,0x28,0x94,0x4C,0x04,0x31,0x00,0x28,0x44,0x0F,0xFF,0xCF, +0xD5,0x22,0x94,0x4E,0x04,0x31,0x00,0x28,0x44,0x0F,0xFF,0x3F,0xD5,0x1C,0x40,0x10, +0xA0,0x08,0x04,0x31,0x00,0x28,0x44,0x0F,0xFC,0xFF,0xD5,0x15,0x40,0x10,0xA8,0x08, +0x04,0x31,0x00,0x28,0x44,0x0F,0xF3,0xFF,0xD5,0x0E,0x40,0x10,0xB0,0x08,0x04,0x31, +0x00,0x28,0x44,0x0F,0xCF,0xFF,0xD5,0x07,0x40,0x10,0xB8,0x08,0x04,0x31,0x00,0x28, +0x44,0x0F,0x3F,0xFF,0xFE,0x1E,0xFE,0x47,0x14,0x11,0x00,0x28,0xDD,0x9E,0x92,0x00, +0xC8,0x04,0x44,0x07,0x20,0x14,0xD5,0x03,0x44,0x07,0x20,0x18,0xB4,0x00,0x40,0x00, +0x30,0x08,0x92,0x0C,0xDD,0x9E,0xFC,0x00,0x44,0x04,0x00,0x9C,0xA6,0x00,0xE6,0x03, +0xE9,0x24,0x44,0x64,0x0C,0x40,0x84,0x41,0xA6,0x32,0x80,0x62,0x58,0x00,0x00,0x08, +0xAE,0x32,0x44,0x14,0x04,0x40,0x44,0x04,0x08,0x40,0x49,0xFF,0xF0,0x53,0xA6,0x32, +0x84,0x41,0x54,0x00,0x00,0xF7,0xAE,0x32,0x44,0x14,0x04,0x40,0x44,0x04,0x08,0x40, +0x80,0x62,0x49,0xFF,0xF0,0x47,0x44,0x07,0x20,0xC0,0x46,0x10,0x20,0x00,0xB4,0x40, +0xFE,0x57,0xB6,0x20,0x80,0x20,0xD5,0x44,0x49,0x00,0x06,0xE7,0xC8,0xDB,0x44,0x64, +0x50,0x28,0xB4,0x06,0x92,0x10,0x96,0x06,0x5A,0x08,0x01,0x1B,0x2E,0x07,0xEF,0xBC, +0xC8,0x13,0xB4,0x06,0x92,0x03,0x96,0x06,0xC8,0x0F,0xB4,0x06,0x92,0x01,0x96,0x06, +0xC0,0x0B,0x3C,0x0D,0xFD,0xC3,0x5A,0x00,0x01,0x0C,0x49,0x00,0x25,0xB6,0x2E,0x07, +0xEF,0xC8,0xC0,0xE8,0xD5,0x05,0x49,0xFF,0xF9,0xE2,0x5A,0x08,0x02,0xF4,0x44,0x64, +0x50,0x28,0xB4,0x06,0x92,0x10,0x96,0x06,0xC8,0xB5,0x2E,0x07,0xEF,0xBC,0xC8,0x13, +0xB4,0x06,0x92,0x03,0x96,0x06,0xC8,0x0F,0xB4,0x06,0x92,0x01,0x96,0x06,0xC0,0x0B, +0x3C,0x0D,0xFD,0xC3,0x5A,0x00,0x01,0xA7,0x49,0x00,0x25,0x97,0x2E,0x07,0xEF,0xC8, +0xC0,0xE9,0xD5,0xA0,0x49,0xFF,0xF9,0xC3,0x5A,0x08,0x02,0xF4,0xD5,0x9B,0xB4,0x01, +0x92,0x19,0x96,0x06,0xC8,0xFD,0x84,0x01,0x49,0xFF,0xFC,0x58,0xFC,0x80,0x44,0x34, +0x00,0x54,0x84,0x21,0xB4,0x43,0x40,0x00,0x80,0x0C,0xFE,0x03,0xFE,0x16,0xB6,0x03, +0xDD,0x9E,0xFC,0x00,0x80,0xC0,0x44,0x04,0x80,0x90,0x54,0x13,0x00,0x01,0xB4,0x80, +0x84,0x5D,0x94,0xC9,0xFE,0xA6,0xFE,0x9F,0xB6,0x40,0x46,0x00,0x00,0x81,0x84,0x5E, +0x83,0x80,0xBB,0x15,0xFE,0x9E,0xFE,0x57,0xB9,0x95,0x84,0x00,0x3E,0x07,0xEF,0xBC, +0x44,0x04,0x80,0x10,0x84,0x22,0xAE,0x40,0x49,0xFF,0xF8,0xA5,0xE6,0x03,0xE9,0x2A, +0x44,0x14,0x00,0x18,0xA6,0x0A,0x5A,0x68,0x01,0x13,0x54,0x00,0x00,0xFB,0xAE,0x0A, +0x44,0x14,0x50,0x10,0xB4,0x01,0x92,0x08,0x40,0x00,0x20,0x08,0xB6,0x01,0x44,0x14, +0x70,0x00,0x44,0x0E,0xFF,0xFF,0xB4,0x41,0xFE,0x16,0xD5,0x13,0x58,0x00,0x00,0x04, +0xAE,0x0A,0x44,0x14,0x50,0x10,0xB4,0x01,0x92,0x08,0x40,0x00,0x20,0x08,0x58,0x00, +0x00,0x98,0xB6,0x01,0x44,0x14,0x70,0x00,0x44,0x01,0x00,0x00,0xB4,0x41,0xFE,0x17, +0xB6,0x01,0xFC,0x80,0x46,0x10,0x00,0x81,0x44,0x02,0x00,0x00,0x04,0x20,0x80,0x6E, +0xFE,0x17,0x14,0x00,0x80,0x6E,0xDD,0x9E,0x44,0x27,0x30,0x10,0xB4,0x22,0x92,0x21, +0x96,0x46,0x5A,0x10,0x01,0xFD,0xFC,0x00,0x44,0x17,0x30,0x00,0xB6,0x01,0x84,0x02, +0x49,0xFF,0xF6,0xDF,0xFC,0x80,0xFC,0x00,0x46,0x61,0xFF,0xF9,0x50,0x03,0x05,0x27, +0x49,0xFF,0xFF,0xEC,0x50,0x03,0x04,0x87,0x49,0xFF,0xFF,0xE8,0xFC,0x80,0xFC,0x00, +0x46,0x61,0xFF,0xF9,0x50,0x03,0x05,0x00,0x49,0xFF,0xFF,0xE0,0x50,0x03,0x04,0x00, +0x49,0xFF,0xFF,0xDC,0xFC,0x80,0xFC,0x00,0x46,0x11,0xFF,0xFF,0x50,0x10,0x8F,0x00, +0xFE,0x0F,0x49,0xFF,0xFF,0xD3,0xFC,0x80,0xFC,0x00,0x46,0x11,0xFF,0xFF,0xFE,0x0F, +0x49,0xFF,0xFF,0xCC,0xFC,0x80,0xFC,0x00,0x46,0x01,0xFF,0xFF,0x50,0x00,0x02,0xBB, +0x49,0xFF,0xFF,0xC4,0xFC,0x80,0xFC,0x00,0x46,0x01,0xFF,0xFF,0x50,0x00,0x03,0xCC, +0x49,0xFF,0xFF,0xBC,0xFC,0x80,0xFC,0x00,0x44,0x67,0x30,0x04,0x49,0xFF,0xFF,0xC5, +0x49,0xFF,0xF6,0x92,0xB4,0x26,0x84,0x1E,0xFE,0x0E,0xB6,0x06,0x44,0x00,0x03,0xE8, +0x49,0xFF,0xF6,0x97,0xB4,0x06,0x58,0x00,0x00,0x01,0xB6,0x06,0x44,0x00,0x03,0xE8, +0x49,0xFF,0xF6,0x8F,0x49,0xFF,0xFF,0xD9,0x49,0xFF,0xF6,0x7E,0x46,0x01,0xFF,0xFF, +0x49,0xFF,0xFF,0x9C,0x49,0xFF,0xF6,0x78,0x49,0xFF,0xF8,0x05,0xE6,0x03,0xE8,0x0B, +0x49,0xFF,0xFF,0xD3,0x49,0xFF,0xF6,0x70,0x46,0x01,0xFF,0xFF,0x49,0xFF,0xFF,0x8E, +0x49,0xFF,0xF6,0x6A,0x44,0x67,0x30,0x04,0x44,0x00,0x03,0xE8,0x49,0xFF,0xF6,0x71, +0xB4,0x26,0x84,0x1E,0xFE,0x0E,0xB6,0x06,0x44,0x00,0x03,0xE8,0x49,0xFF,0xF6,0x69, +0xB4,0x06,0x58,0x00,0x00,0x01,0xB6,0x06,0x44,0x00,0x03,0xE8,0x49,0xFF,0xF6,0x61, +0x44,0x04,0x80,0x0A,0x84,0x24,0xAE,0x40,0x84,0x22,0xAE,0x40,0x84,0x21,0xAE,0x40, +0xFC,0x80,0xFC,0x41,0xF0,0x81,0x80,0xE1,0x80,0xC2,0x81,0x43,0x81,0x24,0x49,0xFF, +0xFF,0x74,0xF0,0x01,0x5A,0x00,0xFF,0x06,0x49,0xFF,0xFF,0x87,0x49,0xFF,0xF6,0x3C, +0x40,0xA5,0x20,0x08,0x40,0x95,0x24,0x04,0x40,0x63,0x40,0x08,0x40,0x64,0x98,0x04, +0x40,0x03,0xE0,0x08,0xFE,0x37,0x49,0xFF,0xFF,0x51,0x49,0xFF,0xFF,0x6A,0xFC,0xC1, +0x44,0x27,0x30,0x10,0xB4,0x22,0x92,0x21,0x96,0x46,0x5A,0x10,0x01,0xFD,0xFC,0x20, +0x44,0x14,0x80,0x0A,0x84,0x42,0xAE,0x88,0x44,0x17,0x30,0x00,0x44,0x60,0x10,0x00, +0xB6,0x01,0x44,0x74,0x80,0x08,0xB4,0x07,0x92,0x11,0x96,0x06,0xC0,0x0D,0x44,0x04, +0x80,0x08,0xB4,0x00,0x92,0x12,0x96,0x06,0xC8,0x10,0x44,0x07,0x30,0x14,0xB4,0x00, +0x92,0x10,0x96,0x00,0xFC,0xA0,0x2E,0x07,0xEF,0xF8,0xC8,0xF2,0x84,0x01,0x8E,0xC1, +0x49,0xFF,0xF6,0x0F,0xCE,0xE9,0xD5,0xEC,0x44,0x00,0xFF,0xFF,0xFC,0xA0,0xFC,0x41, +0xF0,0x81,0x40,0x21,0x20,0x08,0x46,0x02,0xF0,0x00,0x40,0x91,0x00,0x04,0x40,0x10, +0xC0,0x08,0x40,0x94,0x84,0x04,0x84,0xC0,0x49,0xFF,0xFF,0x1F,0xF0,0x01,0x49,0xFF, +0xFF,0x34,0x49,0xFF,0xF5,0xE9,0x80,0x09,0x49,0xFF,0xFF,0xBC,0x5C,0xF0,0x01,0x00, +0x80,0xE0,0xE9,0x08,0x8C,0xC1,0x84,0x01,0x97,0xB0,0x49,0xFF,0xF7,0x79,0x5A,0x68, +0x04,0xED,0x49,0xFF,0xFF,0x16,0x5A,0x60,0x04,0x04,0x80,0x07,0xFC,0xC1,0x84,0x00, +0xFC,0xC1,0xFC,0x00,0x44,0x17,0x30,0x08,0xB4,0x01,0x92,0x05,0x94,0x05,0x58,0x00, +0x00,0x01,0xB6,0x01,0x49,0xFF,0xFE,0xF9,0x49,0xFF,0xF7,0x55,0xE6,0x03,0xE8,0x07, +0x49,0xFF,0xFF,0x23,0x46,0x01,0xFF,0xFF,0x49,0xFF,0xFE,0xE0,0x84,0x06,0xFA,0x2F, +0x44,0x20,0x00,0xAC,0x84,0x61,0x84,0x80,0x49,0xFF,0xFF,0x6D,0x84,0x06,0xFA,0x2F, +0x44,0x20,0x00,0xAD,0x84,0x61,0x84,0x80,0x49,0xFF,0xFF,0x65,0x44,0x04,0x80,0x0A, +0x84,0x24,0xAE,0x40,0x84,0x22,0xAE,0x40,0x44,0x04,0x80,0x88,0x44,0x14,0x00,0x00, +0xB4,0x40,0xFE,0x57,0xB6,0x20,0xB4,0x40,0x44,0x12,0x00,0x00,0xFE,0x57,0xB6,0x20, +0x49,0xFF,0xFE,0xD7,0xFC,0x80,0xFC,0x20,0x84,0x2C,0x84,0x41,0x84,0x05,0x49,0xFF, +0xFF,0x98,0x97,0xC0,0x84,0x2D,0x84,0x41,0x84,0x05,0x49,0xFF,0xFF,0x92,0x97,0x80, +0x80,0x87,0x84,0x05,0xFA,0x2F,0x44,0x20,0x00,0xC0,0x84,0x61,0x49,0xFF,0xFF,0x3B, +0x80,0x86,0x84,0x05,0xFA,0x2F,0x44,0x20,0x00,0xC1,0x84,0x61,0x49,0xFF,0xFF,0x33, +0x80,0x87,0x84,0x05,0xFA,0x2F,0x44,0x20,0x00,0xC2,0x84,0x61,0x49,0xFF,0xFF,0x2B, +0x80,0x86,0x84,0x05,0xFA,0x2F,0x44,0x20,0x00,0xC3,0x84,0x61,0x49,0xFF,0xFF,0x23, +0x80,0x87,0x84,0x05,0xFA,0x2F,0x44,0x20,0x00,0xC4,0x84,0x61,0x49,0xFF,0xFF,0x1B, +0x80,0x86,0x84,0x05,0xFA,0x2F,0x44,0x20,0x00,0xC5,0x84,0x61,0x49,0xFF,0xFF,0x13, +0x80,0x87,0x84,0x05,0xFA,0x2F,0x44,0x20,0x00,0xC6,0x84,0x61,0x49,0xFF,0xFF,0x0B, +0x84,0x05,0xFA,0x2F,0x44,0x20,0x00,0xC7,0x84,0x61,0x80,0x86,0x49,0xFF,0xFF,0x03, +0xFC,0xA0,0xFC,0x00,0x44,0x10,0x00,0x60,0x84,0x41,0x84,0x06,0x49,0xFF,0xFF,0x49, +0x80,0xC0,0x44,0x10,0x00,0x61,0x84,0x41,0x84,0x06,0x49,0xFF,0xFF,0x42,0x96,0x00, +0x97,0xB0,0x40,0x00,0x20,0x08,0xFE,0x37,0xFC,0x80,0x3C,0x0F,0xFB,0xFF,0xDD,0x9E, +0x44,0x04,0x80,0x00,0xA6,0x03,0x96,0x06,0xC0,0x0D,0x44,0x04,0x80,0x03,0x84,0x21, +0xAE,0x40,0x3C,0x0D,0xFB,0xFF,0xC0,0x06,0xFC,0x00,0x3C,0x0D,0xFB,0xFF,0xDD,0x20, +0xFC,0x80,0xDD,0x9E,0x46,0x20,0x00,0x81,0x84,0x3E,0x04,0x31,0x00,0x14,0x96,0x06, +0xFE,0x5E,0xFE,0x0F,0x14,0x01,0x00,0x14,0xDD,0x9E,0x44,0x14,0x70,0x08,0x5A,0x08, +0x01,0x09,0xB4,0x01,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00,0x78,0x23,0xD5,0x05, +0xB4,0x01,0x92,0x10,0x40,0x00,0x40,0x08,0xB6,0x01,0xDD,0x9E,0xDD,0x9E,0x4E,0x05, +0x00,0x19,0x8E,0x01,0x40,0x10,0x04,0x0A,0xFE,0x0F,0x40,0x10,0x08,0x0A,0xFE,0x0F, +0x40,0x10,0x10,0x0A,0xFE,0x0F,0x40,0x10,0x20,0x0A,0xFE,0x0F,0x40,0x10,0x40,0x0A, +0xFE,0x47,0x8C,0x21,0x84,0x00,0x90,0x21,0xC1,0x03,0x8C,0x01,0xD5,0xFD,0xDD,0x9E, +0x84,0x00,0xDD,0x9E,0xFC,0x4D,0x44,0x02,0x00,0x10,0x50,0xFF,0x80,0x18,0x80,0xC0, +0x80,0x2F,0x3A,0x23,0x14,0x04,0x3A,0x20,0x94,0x24,0x81,0x46,0x83,0x81,0x3A,0x25, +0x14,0x04,0x44,0x92,0x00,0x38,0x3A,0x2E,0x14,0x24,0x80,0x89,0x3A,0x25,0x0C,0x00, +0x80,0xBF,0x3A,0x2E,0x0C,0x20,0x3A,0x22,0x0C,0x04,0x3A,0x22,0x8C,0x24,0x45,0xE2, +0x05,0xB8,0xA5,0xE0,0x3A,0x27,0x94,0x04,0x81,0xFE,0x81,0x1F,0x3A,0x27,0x94,0x24, +0x3A,0x20,0x94,0x04,0x80,0x2F,0x3A,0x20,0x94,0x24,0x3A,0x2E,0x0C,0x00,0x3A,0x20, +0x8C,0x20,0x44,0x12,0x05,0xAC,0x3A,0x24,0x0C,0x04,0x3A,0x20,0x8C,0x24,0xAD,0xC8, +0xB0,0x50,0x3A,0x20,0x14,0x04,0x80,0x01,0x3A,0x20,0x14,0x24,0x81,0x00,0x3A,0x23, +0x14,0x04,0x3A,0x24,0x14,0x24,0xB1,0x83,0x3A,0x25,0x0C,0x00,0x80,0x86,0x3A,0x24, +0x0C,0x20,0x3A,0x24,0x8C,0x04,0x44,0x92,0x03,0xEC,0x3A,0x22,0x0C,0x24,0x3A,0x20, +0x94,0x04,0x80,0x29,0x3A,0x20,0x94,0x24,0x3A,0x20,0x14,0x04,0x80,0x01,0x3A,0x20, +0x14,0x24,0x3A,0x24,0x0C,0x00,0x3A,0x20,0x0C,0x20,0x44,0x02,0x03,0xE0,0x3A,0x23, +0x0C,0x04,0x3A,0x20,0x0C,0x24,0xAD,0xC0,0xFC,0xCD,0xFC,0x48,0x83,0x81,0x50,0xAF, +0x80,0x14,0x44,0x12,0x00,0x44,0x80,0xC0,0x80,0x0A,0x81,0x22,0x3A,0x20,0x8C,0x04, +0x3A,0x20,0x0C,0x24,0xA4,0x48,0xFA,0x40,0xAC,0x40,0x50,0x8F,0x80,0x30,0x84,0x20, +0xB0,0x08,0x49,0x00,0x28,0x97,0x80,0x08,0x84,0x20,0xFA,0x40,0x49,0x00,0x28,0x92, +0x5A,0x98,0x01,0x03,0x87,0x80,0xC6,0x04,0x44,0x12,0x06,0x58,0xD5,0x03,0x44,0x12, +0x06,0x64,0x58,0x04,0x80,0x01,0x8C,0x01,0x84,0xE0,0xB6,0x1F,0x8D,0x21,0x84,0x83, +0x84,0x61,0x38,0x05,0x1C,0x00,0xF3,0x83,0xF4,0x82,0x38,0x60,0x80,0x00,0xB4,0x1F, +0x8C,0xC1,0xFF,0x84,0x97,0xB1,0x40,0x63,0x24,0xD6,0x80,0x06,0xF1,0x81,0x49,0xFF, +0xFF,0x48,0xF4,0x02,0x97,0x78,0x40,0x52,0x90,0xB7,0xF3,0x03,0x38,0x24,0x16,0x02, +0x8C,0x08,0x41,0xE1,0x28,0x08,0x40,0x20,0x70,0x01,0x40,0x01,0x80,0x0C,0x40,0x60, +0x18,0xD7,0xB0,0x08,0x94,0x94,0x38,0x00,0x16,0x02,0x88,0x5E,0x40,0x00,0x28,0x08, +0x88,0xC0,0xB0,0x08,0x8C,0xE1,0x38,0x24,0x16,0x0A,0x38,0x60,0x16,0x0A,0xF1,0x01, +0x5A,0x78,0x0A,0xD1,0x80,0x28,0x49,0xFF,0xEA,0x8A,0xFC,0xC8,0x80,0x80,0x84,0x40, +0x9A,0xE0,0x96,0xD8,0xE2,0x61,0xE8,0x08,0x08,0x32,0x00,0x01,0xE2,0x62,0x40,0x31, +0x3C,0x1B,0x80,0x43,0xD5,0xF6,0x9C,0x11,0x96,0x00,0xDD,0x9E,0xFC,0x40,0x51,0xFF, +0xFB,0xE0,0x44,0x20,0x04,0x00,0x81,0x40,0x81,0x21,0xB0,0x08,0x84,0x20,0x49,0x00, +0x28,0x31,0xB1,0x81,0x44,0x12,0x00,0x44,0x80,0x06,0x3A,0x20,0x8C,0x04,0x3A,0x20, +0x0C,0x24,0xA4,0x48,0xB1,0xC4,0xAC,0x40,0xFA,0x40,0x84,0x20,0x80,0x07,0x49,0x00, +0x28,0x21,0x84,0x2A,0x4E,0xA3,0x00,0x3D,0x44,0x02,0x06,0x64,0x49,0xFF,0xFF,0xD0, +0x80,0x20,0x80,0x4A,0x84,0xA3,0x44,0xA2,0x05,0xB8,0x45,0xE2,0x05,0xAC,0x96,0x10, +0x40,0x00,0x14,0x17,0x40,0x30,0x20,0x08,0xB1,0x08,0x41,0xC2,0x0C,0x00,0x84,0x60, +0x54,0x81,0x80,0xFF,0xE3,0x01,0xE8,0x12,0x38,0x83,0x08,0x00,0x38,0x85,0x22,0x02, +0x38,0x84,0x0D,0x01,0x54,0xF4,0x03,0xFF,0x38,0x8E,0x0E,0x02,0x40,0x84,0x28,0x08, +0x89,0x0F,0x38,0x8E,0x0E,0x0A,0x8C,0x61,0xD5,0xEC,0x38,0x33,0x08,0x00,0x8C,0x41, +0x38,0x3F,0x0C,0x00,0x8A,0x69,0x54,0x81,0x83,0xFF,0x38,0x33,0x82,0x02,0x40,0x31, +0xA8,0x08,0x88,0x68,0x38,0x33,0x82,0x0A,0x5A,0x28,0x0A,0xD3,0xD5,0x3B,0x44,0x02, +0x06,0x58,0x49,0xFF,0xFF,0x95,0x80,0x20,0x84,0x40,0x84,0xA3,0x44,0xA2,0x03,0xEC, +0x45,0xE2,0x03,0xE0,0x97,0x10,0x40,0x42,0x14,0x97,0x40,0x32,0x20,0x08,0xB0,0x08, +0x41,0xC0,0x0C,0x00,0x84,0x60,0x54,0x81,0x80,0xFF,0xE3,0x01,0xE8,0x12,0x38,0x83, +0x08,0x00,0x38,0x85,0x22,0x02,0x38,0x84,0x0D,0x01,0x54,0xF4,0x03,0xFF,0x38,0x8E, +0x0E,0x02,0x40,0x84,0x28,0x08,0x89,0x0F,0x38,0x8E,0x0E,0x0A,0x8C,0x61,0xD5,0xEC, +0x38,0x33,0x08,0x00,0x8C,0x41,0x38,0x3F,0x0C,0x00,0x8A,0x69,0x54,0x81,0x83,0xFF, +0x38,0x33,0x92,0x02,0x40,0x31,0xA8,0x08,0x88,0x68,0x38,0x33,0x92,0x0A,0x5A,0x28, +0x0A,0xD3,0xB0,0x08,0x49,0xFF,0xEA,0x32,0x80,0x07,0x49,0xFF,0xEA,0x68,0x51,0xFF, +0x84,0x20,0xFC,0xC0,0xFC,0x20,0x49,0x00,0x0D,0x4C,0x80,0xC0,0x49,0x00,0x0D,0x4C, +0x80,0xA6,0x50,0x43,0x04,0x80,0x50,0x32,0x80,0x22,0x50,0x20,0x00,0x22,0x84,0x20, +0x38,0x62,0x85,0x11,0x22,0x71,0x80,0x00,0x38,0x72,0x85,0x09,0x1A,0x61,0xFF,0xFF, +0x38,0x60,0x05,0x11,0x22,0x71,0x00,0x00,0x38,0x70,0x05,0x09,0x8C,0x21,0x1A,0x61, +0x7F,0xFF,0x5A,0x18,0x09,0xEF,0x50,0x52,0x80,0x24,0x50,0x00,0x00,0x24,0xDC,0xE4, +0xFC,0xA0,0xFC,0x40,0x49,0x00,0x0D,0x25,0x80,0xC0,0x49,0x00,0x0D,0x25,0x80,0xA6, +0x84,0x40,0x52,0x11,0x04,0x5C,0x99,0x31,0x98,0xC2,0x88,0x20,0x50,0x72,0x80,0x24, +0x22,0x92,0x80,0x00,0x22,0xA2,0x00,0x00,0x1A,0xA2,0x80,0x01,0x1A,0x92,0x00,0x01, +0x22,0x91,0x80,0x00,0x22,0xA0,0x80,0x00,0x1A,0xA1,0x80,0x01,0x1A,0x90,0x80,0x01, +0xDF,0xF0,0x50,0x21,0x00,0x24,0x5A,0x2A,0x40,0xE6,0xFC,0xC0,0xFC,0x41,0x80,0xE0, +0x80,0x02,0xF3,0x81,0x81,0x44,0x80,0xC5,0x81,0x21,0x49,0xFF,0xFB,0x6B,0x81,0x00, +0xF0,0x01,0x49,0xFF,0xF0,0xC7,0x40,0x44,0x28,0x00,0x95,0x72,0x84,0x60,0x84,0x20, +0x98,0x99,0x96,0x91,0xC7,0x0B,0x95,0x92,0x88,0xC5,0x38,0xA2,0x09,0x01,0x38,0x60, +0x18,0x02,0x90,0xC8,0x8A,0xCA,0x38,0x63,0x89,0x09,0x4E,0x92,0x00,0x06,0x38,0x62, +0x09,0x11,0x38,0x64,0x89,0x09,0x8C,0x21,0x5A,0x18,0x12,0xEC,0x8C,0x72,0x96,0xD9, +0x5A,0x3A,0x40,0xE7,0x49,0xFF,0xFF,0x88,0x49,0xFF,0xFF,0xAD,0xFC,0xC1,0xFC,0x00, +0x80,0x43,0x80,0x64,0x84,0x80,0x80,0xA4,0x49,0xFF,0xFF,0xCA,0xFC,0x80,0xFC,0x20, +0x80,0xE0,0x80,0x02,0x80,0xC1,0x49,0xFF,0xFB,0x35,0x84,0x40,0x96,0x51,0xE2,0x26, +0xE8,0x07,0x38,0x10,0x09,0x11,0x38,0x13,0x89,0x09,0x8C,0x41,0xD5,0xF8,0xFC,0xA0, +0xFC,0x20,0xFD,0x30,0x84,0x00,0x49,0xFF,0xFB,0x25,0x95,0xF9,0x80,0xA6,0x88,0xE6, +0x50,0x00,0x04,0xA4,0xD7,0x06,0x2A,0x10,0x00,0x01,0x1A,0x12,0x80,0x01,0xD5,0xFB, +0xFC,0xA0,0xFC,0x20,0x80,0xE0,0x80,0x02,0x80,0xC1,0x49,0xFF,0xF0,0x73,0x84,0x40, +0x96,0x51,0xE2,0x26,0xE8,0x07,0x38,0x10,0x0A,0x02,0x38,0x13,0x8A,0x0A,0x8C,0x41, +0xD5,0xF8,0xFC,0xA0,0x2E,0x07,0xF0,0xCA,0xDD,0x9E,0xFC,0x01,0x84,0x1F,0xB6,0x1F, +0xF0,0x81,0x80,0x1F,0x49,0xFF,0xEC,0x42,0x84,0x01,0x49,0xFF,0xEC,0x34,0x84,0x01, +0x49,0xFF,0xEC,0xAA,0x84,0x22,0x84,0x01,0x80,0x61,0x80,0xA0,0x44,0x40,0x00,0x3F, +0x84,0x44,0x49,0xFF,0xEB,0xAD,0x84,0x01,0x49,0xFF,0xFA,0x08,0x84,0x01,0x80,0x20, +0x80,0x40,0x49,0xFF,0xEB,0xEA,0x84,0x0A,0x49,0xFF,0xEC,0x12,0x46,0x00,0x42,0x10, +0x50,0x00,0x08,0x20,0x80,0x20,0x84,0x40,0x49,0xFF,0xEB,0xFD,0x49,0xFF,0xEE,0x60, +0x84,0x01,0x49,0xFF,0xEE,0x4F,0x84,0x00,0x49,0xFF,0xFA,0x47,0x44,0x04,0x55,0x08, +0x44,0x1F,0xFF,0x7F,0xB4,0x40,0xFE,0x56,0xB6,0x20,0xB4,0x20,0x58,0x10,0x80,0x10, +0xB6,0x20,0x84,0x21,0x49,0xFF,0xEB,0x0B,0x84,0x06,0x3E,0x07,0xF0,0xCA,0xFC,0x81, +0xFC,0x52,0x44,0x02,0x00,0x5C,0x80,0x20,0x80,0x9F,0x3A,0x20,0x8C,0x04,0x3A,0x22, +0x0C,0x24,0x50,0xAF,0x80,0x0C,0xA4,0x48,0x3A,0x20,0x0C,0x04,0x80,0x0A,0xAC,0x60, +0x3A,0x20,0x0C,0x24,0x50,0x9F,0x80,0x18,0xAC,0x40,0x44,0x02,0x00,0x68,0x80,0x29, +0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24,0xB4,0x00,0xB1,0x90,0xB6,0x01,0x44,0x12, +0x00,0x7C,0x80,0x01,0x80,0xE6,0x3A,0x20,0x14,0x04,0x81,0xE0,0x3A,0x23,0x94,0x24, +0x3A,0x27,0x94,0x04,0x3A,0x23,0x94,0x24,0x3A,0x27,0x8C,0x00,0x3A,0x23,0x8C,0x20, +0x44,0x22,0x00,0xA4,0x51,0xCF,0x80,0x2C,0x80,0xE2,0x83,0xDC,0x3A,0x23,0x94,0x04, +0x3A,0x2F,0x14,0x24,0xB4,0x67,0xB1,0xDA,0xB6,0x7E,0x3A,0x20,0x94,0x04,0x80,0x27, +0x81,0x1F,0x3A,0x20,0x94,0x24,0x3A,0x20,0x14,0x04,0x80,0x01,0x3A,0x20,0x14,0x24, +0x3A,0x27,0x8C,0x00,0x3A,0x20,0x0C,0x20,0x49,0xFF,0xF3,0xBD,0xE6,0x03,0xE9,0x07, +0x2E,0x07,0xF0,0xCA,0x44,0x10,0x01,0x32,0x38,0x13,0x01,0x09,0x44,0x02,0x06,0x08, +0x3A,0x2E,0x14,0x04,0x3A,0x20,0x14,0x24,0x80,0x20,0xB4,0x1C,0xB6,0x01,0x44,0x12, +0x05,0xE0,0x80,0x01,0x3A,0x23,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x23,0x94,0x04, +0x3A,0x20,0x14,0x24,0x3A,0x23,0x8C,0x00,0x3A,0x20,0x0C,0x20,0x44,0x02,0x06,0x64, +0x3A,0x24,0x0C,0x04,0x3A,0x20,0x0C,0x24,0x80,0x20,0x02,0x04,0x00,0x00,0xAC,0x08, +0x44,0x02,0x06,0x58,0x3A,0x25,0x0C,0x04,0x3A,0x20,0x0C,0x24,0x80,0x20,0x02,0x05, +0x00,0x00,0xAC,0x08,0x44,0x02,0x06,0x44,0x3A,0x24,0x94,0x04,0x3A,0x20,0x14,0x24, +0x80,0x20,0xB4,0x09,0xB6,0x01,0x44,0x12,0x06,0x1C,0x80,0x01,0x3A,0x23,0x14,0x04, +0x3A,0x20,0x14,0x24,0x3A,0x23,0x14,0x04,0x3A,0x20,0x14,0x24,0x3A,0x23,0x0C,0x00, +0x3A,0x20,0x0C,0x20,0xFC,0xD2,0xFC,0x00,0x49,0xFF,0xEA,0xC9,0xFC,0x80,0xFC,0x00, +0xC8,0x06,0x44,0x02,0x06,0x1C,0x44,0x12,0x06,0x44,0xD5,0x05,0x44,0x02,0x05,0xE0, +0x44,0x12,0x06,0x08,0x49,0xFF,0xEB,0xB5,0xFC,0x80,0x84,0x00,0x3E,0x07,0xF0,0xC9, +0x3E,0x07,0xF0,0xC8,0xDD,0x9E,0xFC,0x00,0x80,0x41,0x84,0x20,0x5A,0x08,0x01,0x03, +0x84,0x24,0xCA,0x04,0x44,0x02,0x06,0x64,0xD5,0x03,0x44,0x02,0x06,0x58,0x49,0xFF, +0xED,0xDC,0xFC,0x80,0xFC,0x00,0x80,0xC0,0x5A,0x08,0x01,0x0B,0x84,0x02,0x49,0xFF, +0xED,0x58,0x80,0x06,0x84,0x20,0x49,0xFF,0xED,0x85,0x80,0x06,0xD5,0x09,0x84,0x00, +0x49,0xFF,0xED,0x4F,0x84,0x00,0x80,0x20,0x49,0xFF,0xED,0x7C,0x84,0x00,0x49,0xFF, +0xED,0xAE,0xFC,0x80,0xFC,0x01,0xB6,0x1F,0xF1,0x81,0x49,0xFF,0xFF,0xE5,0xB4,0x1F, +0xF1,0x01,0x49,0xFF,0xFF,0xD2,0xFC,0x81,0xFC,0x49,0x80,0xE0,0x44,0x02,0x00,0x01, +0xF0,0x86,0x44,0x01,0x00,0x02,0xF0,0x87,0x44,0x00,0x80,0x04,0xF0,0x88,0x44,0x00, +0x40,0x08,0xF0,0x89,0x44,0x00,0x20,0x10,0xF0,0x8A,0x44,0x00,0x10,0x20,0xF0,0x8B, +0x44,0x00,0x08,0x40,0xF0,0x8C,0x44,0x00,0x04,0x80,0xF0,0x8D,0x44,0x00,0x03,0x00, +0xF0,0x8E,0x3C,0x0D,0xFB,0xC2,0xF0,0x8F,0xF0,0x90,0xF0,0x91,0xC7,0x08,0x44,0x92, +0x06,0x08,0x44,0xA2,0x05,0xE0,0x85,0x02,0x87,0x85,0xD5,0x07,0x85,0x03,0x44,0x92, +0x06,0x44,0x44,0xA2,0x06,0x1C,0x83,0x88,0xB0,0x06,0x49,0xFF,0xEB,0xB5,0x80,0xDF, +0xCF,0x11,0x44,0x02,0x00,0xD0,0x80,0x3F,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24, +0x3A,0x20,0x0C,0x00,0x3A,0x20,0x8C,0x20,0x80,0x1F,0x49,0xFF,0xEB,0xDB,0x80,0x07, +0xD5,0x10,0x44,0x02,0x00,0xE8,0x80,0x3F,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24, +0x3A,0x20,0x0C,0x00,0x3A,0x20,0x8C,0x20,0x80,0x1F,0x49,0xFF,0xEB,0xCB,0x84,0x01, +0x49,0x00,0x0B,0x3D,0x2E,0x47,0xF0,0xCA,0x84,0x00,0x80,0x4A,0x80,0x69,0x80,0xA4, +0x80,0x20,0x49,0xFF,0xF8,0xBE,0x2E,0x17,0xF0,0xCA,0x84,0x00,0x38,0x15,0x05,0x01, +0x49,0xFF,0xEC,0x5E,0x2E,0x17,0xF0,0xCA,0x84,0x05,0x38,0x15,0x05,0x01,0x49,0xFF, +0xEC,0x57,0x2E,0x07,0xF0,0xCA,0x84,0x20,0x38,0x04,0x80,0x00,0x49,0xFF,0xED,0x53, +0x2E,0x07,0xF0,0xCA,0x84,0x23,0x38,0x04,0x80,0x00,0x49,0xFF,0xED,0x4C,0x44,0x00, +0x02,0x40,0x49,0xFF,0xEE,0xE2,0x80,0x08,0x49,0xFF,0xEF,0x0A,0xE6,0xE2,0xE8,0x07, +0x2E,0x07,0xF0,0xCA,0x40,0xFE,0x00,0x06,0x84,0x01,0xE8,0x02,0x84,0x00,0x80,0x27, +0x49,0xFF,0xFF,0x6A,0xFC,0xC9,0xFC,0x49,0x44,0x12,0x01,0x30,0xB1,0x86,0x80,0xE0, +0x80,0x06,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x20, +0x14,0x24,0x3A,0x20,0x94,0x00,0x3A,0x20,0x14,0x20,0xC7,0x07,0x44,0x82,0x05,0xE0, +0x85,0x22,0x44,0xA2,0x06,0x08,0xD5,0x06,0x44,0x82,0x06,0x1C,0x85,0x23,0x44,0xA2, +0x06,0x44,0x84,0x00,0x49,0x00,0x0A,0xE3,0x2E,0x47,0xF0,0xCA,0x84,0x00,0x80,0x20, +0x80,0x48,0x80,0x6A,0x80,0xA4,0x49,0xFF,0xF8,0x64,0x80,0x06,0x49,0xFF,0xEB,0x2C, +0x44,0x00,0x02,0x40,0x49,0xFF,0xEE,0xA1,0x80,0x09,0x49,0xFF,0xEE,0xC9,0x46,0x00, +0x42,0x10,0x50,0x00,0x08,0x20,0x80,0x20,0x84,0x40,0x49,0xFF,0xEA,0x34,0x80,0x1F, +0xCF,0x04,0x44,0x12,0x00,0xD0,0xD5,0x03,0x44,0x12,0x00,0xE8,0x80,0xC0,0x3A,0x20, +0x94,0x04,0x3A,0x23,0x14,0x24,0x3A,0x20,0x8C,0x00,0x3A,0x23,0x0C,0x20,0x49,0xFF, +0xEB,0x41,0xFC,0xC9,0x2E,0x07,0xF0,0xD1,0x96,0x00,0xC0,0x05,0x84,0x00,0x3E,0x07, +0xF0,0xD1,0x84,0x01,0xDD,0x9E,0x2E,0x17,0xF0,0xD0,0x96,0x48,0xC1,0x05,0x84,0x00, +0x3E,0x07,0xF0,0xD0,0xD5,0x07,0x2E,0x07,0xF0,0xCF,0x96,0x00,0xC0,0x04,0x3E,0x17, +0xF0,0xCF,0x84,0x01,0xDD,0x9E,0x2E,0x07,0xF0,0xD4,0xDD,0x9E,0x3E,0x07,0xF0,0xD4, +0xDD,0x9E,0x2E,0x07,0xF0,0xD3,0xDD,0x9E,0x3E,0x07,0xF0,0xD2,0xDD,0x9E,0xFC,0x00, +0x84,0x03,0x49,0xFF,0xF9,0x56,0x49,0xFF,0xF8,0x56,0xFC,0x80,0xDD,0x9E,0xFC,0x20, +0x49,0xFF,0xF5,0x64,0x2E,0x17,0xF0,0xE4,0x5A,0x10,0x01,0x04,0x48,0x00,0x00,0x80, +0x5A,0x08,0x01,0x06,0x3C,0x0D,0xFC,0x38,0x4E,0x02,0x01,0x08,0x49,0xFF,0xFA,0xAB, +0x84,0x00,0x80,0x80,0xFA,0x2F,0xFA,0x41,0x84,0x61,0x49,0xFF,0xFA,0x2C,0x44,0x00, +0x00,0x96,0x49,0xFF,0xF2,0x0D,0x84,0x00,0x80,0x80,0xFA,0x2F,0xFA,0x59,0x84,0x61, +0x49,0xFF,0xFA,0x21,0xFA,0x04,0x49,0xFF,0xF2,0x03,0x49,0xFF,0xEA,0x89,0x84,0x00, +0x49,0xFF,0xFB,0x42,0x84,0x00,0x49,0xFF,0xF1,0x46,0x84,0x00,0x49,0xFF,0xFB,0x47, +0x84,0x40,0x84,0x61,0x44,0x00,0x03,0x41,0x84,0x27,0x49,0x00,0x1E,0x44,0x84,0x00, +0x84,0x21,0x49,0xFF,0xF0,0x8B,0x84,0x01,0x80,0x20,0x49,0xFF,0xF1,0x01,0x84,0x00, +0x49,0x00,0x09,0xAB,0x84,0x00,0x49,0x00,0x09,0x8E,0x84,0x00,0x49,0xFF,0xFF,0xA8, +0x46,0x00,0x00,0x81,0x44,0x60,0x08,0x01,0x83,0x80,0xB9,0x1A,0x44,0x74,0x50,0x28, +0x92,0x25,0x94,0x4D,0x58,0x10,0x80,0x09,0xB9,0x9A,0xBA,0x1A,0x44,0x1F,0xE0,0xFF, +0xFE,0x56,0x58,0x10,0x85,0x00,0xB9,0x9A,0xBA,0x1A,0x46,0x1F,0xFE,0x0F,0x50,0x10, +0x8F,0xFF,0xFE,0x56,0x44,0x25,0x00,0x00,0xFE,0x57,0xB9,0x9A,0xBA,0x1A,0x46,0x1E, +0x0F,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x56,0x46,0x20,0x30,0x00,0xFE,0x57,0xB9,0x9A, +0xB9,0x19,0x44,0x21,0x39,0x0A,0x92,0x31,0x40,0x10,0xC4,0x08,0xFE,0x57,0xB9,0x99, +0xB4,0x07,0x92,0x01,0x96,0x06,0x5A,0x08,0x01,0x04,0x48,0x00,0x00,0x97,0x8E,0xC1, +0x84,0x01,0x97,0xB1,0x49,0xFF,0xF1,0x9B,0xCE,0xF4,0xFC,0xA0,0x5A,0x10,0x02,0x04, +0x48,0x00,0x00,0x8C,0xC8,0x05,0x3C,0x0D,0xFC,0x38,0x4E,0x02,0x00,0x87,0x49,0xFF, +0xFA,0x2A,0x84,0x00,0x80,0x80,0xFA,0x2F,0xFA,0x58,0x84,0x61,0x49,0xFF,0xF9,0xAB, +0xFA,0x04,0x49,0xFF,0xF1,0x8D,0x84,0x00,0x80,0x80,0xFA,0x2F,0xFA,0x40,0x84,0x61, +0x49,0xFF,0xF9,0xA1,0x44,0x00,0x00,0x32,0x49,0xFF,0xF1,0x82,0x84,0x01,0x49,0x00, +0x09,0x44,0x84,0x00,0x49,0x00,0x09,0x27,0x46,0x00,0x00,0x81,0x83,0x80,0xB9,0x1A, +0x92,0x25,0x94,0x4D,0x58,0x10,0x80,0x03,0xB9,0x9A,0xBA,0x1A,0x44,0x1F,0xE0,0xFF, +0xFE,0x56,0x58,0x10,0x82,0x00,0xB9,0x9A,0xBA,0x1A,0x46,0x1F,0xFE,0x0F,0x50,0x10, +0x8F,0xFF,0xFE,0x56,0x44,0x21,0x00,0x00,0xFE,0x57,0xB9,0x9A,0xBA,0x1A,0x46,0x1E, +0x0F,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x56,0xB9,0x9A,0xB9,0x19,0x84,0x41,0x92,0x31, +0x40,0x10,0xC4,0x08,0x58,0x10,0xED,0x60,0x80,0x62,0xB9,0x99,0x44,0x00,0x03,0x41, +0x84,0x20,0x49,0x00,0x1D,0xA0,0x84,0x21,0x84,0x00,0x49,0xFF,0xF0,0x61,0x84,0x01, +0x49,0xFF,0xF0,0x68,0x84,0x01,0x80,0x20,0x49,0xFF,0xEF,0xE0,0x84,0x01,0x49,0xFF, +0xF0,0x2D,0x84,0x01,0x49,0xFF,0xF0,0x87,0x49,0xFF,0xF1,0x2D,0xE6,0x03,0xE8,0x03, +0x49,0xFF,0xF9,0x03,0x49,0xFF,0xEF,0x98,0x84,0x00,0x80,0x40,0x80,0x80,0xFA,0x2F, +0x84,0x61,0x49,0xFF,0xF9,0x48,0x49,0xFF,0xEF,0x8F,0x84,0x00,0x49,0xFF,0xF4,0x6C, +0x84,0x00,0x49,0xFF,0xF4,0x72,0x84,0x01,0x49,0xFF,0xFE,0xEA,0x44,0x60,0x08,0x01, +0x44,0x74,0x50,0x28,0xB4,0x07,0x92,0x01,0x96,0x06,0xC0,0x07,0x8E,0xC1,0x84,0x01, +0x97,0xB1,0x49,0xFF,0xF1,0x0C,0xCE,0xF7,0xFC,0xA0,0xFC,0x00,0x84,0x00,0x49,0xFF, +0xF0,0xAF,0x44,0x16,0x00,0x00,0xB4,0x01,0x58,0x00,0x01,0x00,0xB6,0x01,0x44,0x02, +0x06,0xA4,0x44,0x10,0x00,0x79,0xAE,0x41,0x44,0x1F,0xFF,0x84,0xAE,0x42,0x84,0x39, +0xAE,0x43,0xFC,0x80,0x84,0x20,0x84,0x40,0x3C,0x1B,0xF8,0x7C,0x44,0x42,0x06,0xA4, +0x80,0x22,0x96,0xD1,0xE2,0x60,0xE8,0x06,0x38,0x32,0x08,0x00,0x8C,0x41,0x88,0x23, +0xD5,0xF9,0xFC,0x00,0x44,0x22,0x06,0xA4,0xFE,0x4A,0x96,0x48,0x38,0x11,0x00,0x08, +0x8C,0x01,0x96,0x01,0x49,0xFF,0xF5,0x48,0x2E,0x07,0xF0,0xFC,0x49,0xFF,0xF5,0x2F, +0x49,0xFF,0xF0,0xD1,0xE6,0x03,0xE9,0x03,0x49,0xFF,0xF4,0x32,0xFC,0x80,0xFC,0x01, +0x84,0x00,0x3E,0x07,0xF7,0x06,0x84,0x01,0x3E,0x07,0xEF,0x15,0x84,0x20,0x44,0x00, +0x01,0x00,0x49,0xFF,0xE7,0x35,0x44,0x00,0x01,0x00,0x84,0x20,0x49,0xFF,0xE7,0x3F, +0x44,0x00,0x01,0x00,0x84,0x20,0x49,0xFF,0xE7,0x49,0x44,0x40,0x05,0x78,0xB6,0x9F, +0x44,0x12,0x06,0xA4,0x44,0x22,0x06,0xA2,0x44,0x32,0x06,0xA0,0x44,0x50,0x00,0x90, +0x44,0x02,0x0C,0x1C,0x49,0xFF,0xF5,0x07,0x44,0x00,0x3F,0x66,0x49,0xFF,0xF5,0x1D, +0x44,0x00,0x38,0xEC,0x49,0xFF,0xF5,0x1C,0x84,0x00,0x49,0xFF,0xE7,0x4B,0x84,0x00, +0x3C,0x0B,0xF8,0x7D,0x44,0x00,0x05,0x77,0x49,0xFF,0xFF,0xA6,0x49,0xFF,0xFF,0x8F, +0xFC,0x81,0xFC,0x00,0xC8,0x03,0xFA,0x1A,0xD5,0x03,0x44,0x00,0x05,0x1B,0x49,0xFF, +0xFF,0x9B,0xFC,0x80,0xFC,0x00,0x44,0x00,0x01,0x00,0x84,0x20,0x49,0xFF,0xE7,0x16, +0x84,0x00,0x3C,0x0F,0xFC,0x36,0x3C,0x0D,0xFC,0x36,0x5C,0xF0,0x00,0x64,0xE8,0x05, +0x3C,0x0D,0xFC,0x36,0x8C,0x01,0xD5,0xF6,0x44,0x00,0x01,0x00,0x84,0x21,0x49,0xFF, +0xE7,0x05,0xFC,0x80,0x2E,0x07,0xF7,0x06,0xDD,0x9E,0x2E,0x07,0xEF,0x15,0xDD,0x9E, +0xFC,0x00,0x44,0x10,0x00,0xFF,0x80,0xC0,0x44,0x20,0x05,0x78,0x44,0x02,0x06,0xA4, +0x49,0x00,0x22,0xF8,0xCE,0x06,0x44,0x00,0x00,0x5A,0x3E,0x07,0xF0,0xFC,0xFC,0x80, +0x44,0x0F,0xFF,0xA7,0x3E,0x07,0xF0,0xFC,0x84,0x05,0x3E,0x07,0xF0,0xFD,0xFA,0x0C, +0x3E,0x07,0xF0,0xFE,0xFA,0x10,0x3E,0x07,0xF0,0xFF,0x84,0x1F,0x3E,0x07,0xF1,0x00, +0xFC,0x80,0xFC,0x00,0x04,0x30,0x00,0x47,0x46,0x40,0x10,0x00,0x40,0x51,0xC0,0x09, +0xFF,0x1E,0x80,0xC0,0x97,0x5F,0xC4,0x26,0x92,0x74,0x96,0xDF,0x4C,0x32,0xC0,0x09, +0x9D,0x5D,0x84,0x66,0x40,0x32,0x8C,0xB6,0x97,0x68,0x84,0x01,0xD5,0x0F,0x50,0x00, +0x01,0x1B,0x9D,0x29,0x84,0x66,0x40,0x42,0x0C,0x76,0x54,0x41,0x80,0x0F,0xA6,0xC3, +0x54,0x31,0x80,0xF0,0xFE,0xE7,0xAE,0xC3,0x84,0x02,0xAE,0x10,0x84,0x4C,0x80,0x01, +0x80,0x26,0x42,0x12,0x88,0x73,0x50,0x10,0x80,0x94,0x49,0x00,0x22,0x8D,0x84,0x01, +0xFC,0x80,0xAF,0x10,0x80,0x04,0xFC,0x80,0xFC,0x42,0x84,0x20,0x80,0xE0,0x84,0x4C, +0xB0,0x01,0x49,0x00,0x22,0xA7,0x2E,0x07,0xF0,0xD5,0x44,0x82,0x1B,0x14,0x8C,0x01, +0x3E,0x07,0xF0,0xD5,0x85,0x45,0x84,0xC1,0x87,0x80,0x44,0x92,0x06,0xA4,0x80,0x08, +0xB0,0x41,0x50,0x2F,0x80,0x03,0x49,0xFF,0xFF,0xB6,0x4E,0x02,0x00,0x9C,0x00,0x14, +0x01,0x05,0x5A,0x18,0x03,0x08,0x00,0x0F,0x80,0x03,0x5A,0x08,0x01,0x04,0x48,0x00, +0x00,0x92,0x8E,0x22,0xE6,0x22,0x4E,0xF2,0x00,0x8E,0x3C,0x0D,0xFB,0xCA,0x96,0x1E, +0xC8,0x06,0x44,0x20,0x00,0x5F,0x44,0x10,0x00,0x3C,0xD5,0x05,0x44,0x20,0x00,0xC8, +0x44,0x10,0x00,0x58,0xF0,0x01,0x5E,0xF0,0x1B,0x59,0xE9,0x21,0x40,0x10,0x08,0x36, +0x5E,0xF0,0x81,0x00,0xE9,0x03,0x44,0x10,0x00,0xFF,0x50,0x00,0xFF,0x92,0xE6,0x14, +0xE8,0x02,0x8C,0x34,0x50,0x00,0xFF,0xA6,0xE6,0x14,0xE8,0x04,0x50,0x10,0x80,0x28, +0xD5,0x2C,0x50,0x00,0xFF,0x69,0xE6,0x14,0xE8,0x03,0x8E,0x34,0xD5,0x26,0x50,0x00, +0xFF,0x55,0xE6,0x14,0xE8,0x22,0x50,0x10,0xFF,0xD8,0xD5,0x1F,0x40,0x10,0x04,0x36, +0x5E,0xF0,0x81,0x00,0xE9,0x03,0x44,0x10,0x00,0xFF,0x50,0x00,0xFF,0xE2,0xE6,0x0A, +0xE8,0x03,0x8C,0x2A,0xD5,0x12,0x50,0x00,0xFF,0xEC,0xE6,0x0A,0xE8,0x03,0x8C,0x34, +0xD5,0x0C,0x50,0x20,0xFF,0xCD,0xE6,0x4A,0xE8,0x03,0x8E,0x2A,0xD5,0x06,0x50,0x20, +0xFF,0xC3,0xE6,0x4A,0x40,0x10,0x3C,0x1B,0x00,0x4F,0x80,0x0C,0x5A,0x48,0x01,0x3B, +0x22,0x0F,0x80,0x04,0x22,0x2F,0x80,0x05,0xCF,0x1F,0x40,0x31,0x20,0x0A,0x54,0x41, +0x80,0x0F,0x40,0x30,0x10,0x0A,0x54,0x31,0x80,0xF0,0xFE,0xE7,0x38,0x34,0x98,0x08, +0x9C,0xF1,0x96,0x00,0x38,0x04,0x8C,0x08,0x9C,0x32,0x96,0x90,0x5E,0xF0,0x80,0xFF, +0x38,0x24,0x80,0x08,0x9C,0x33,0xE9,0x03,0x44,0x10,0x00,0xFE,0x96,0x48,0x38,0x14, +0x80,0x08,0x87,0x81,0xD5,0x17,0x40,0x11,0x20,0x0A,0x54,0x30,0x80,0x0F,0x40,0x10, +0x10,0x0A,0x54,0x10,0x80,0xF0,0xFE,0x5F,0x38,0x14,0xA8,0x08,0x50,0x15,0x00,0x01, +0x96,0x00,0x38,0x04,0x84,0x08,0x50,0x05,0x00,0x02,0x96,0x90,0x38,0x24,0x80,0x08, +0x83,0x84,0x8C,0xC4,0x50,0x84,0x01,0x24,0x8D,0x43,0x5A,0x60,0x29,0x04,0x48,0xFF, +0xFF,0x58,0x80,0x1C,0xFC,0xC2,0xFA,0xB3,0x44,0x22,0x06,0xA4,0x44,0x30,0x04,0xA3, +0x80,0x80,0x0A,0x10,0x00,0x01,0x92,0x28,0x38,0x11,0x14,0x08,0x9C,0x69,0xA7,0x20, +0x8C,0xA2,0x38,0x41,0x04,0x08,0xDB,0xF5,0xDD,0x9E,0x44,0x50,0x04,0xA3,0x44,0x22, +0x06,0xA4,0x44,0x30,0x05,0x07,0x80,0x80,0x0A,0x10,0x00,0x01,0x92,0x28,0x38,0x11, +0x14,0x08,0x9C,0x69,0xA7,0x20,0x8C,0xA2,0x38,0x41,0x04,0x08,0xDB,0xF5,0xDD,0x9E, +0xC8,0x03,0x3E,0x17,0xF1,0x25,0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xF3,0xA3,0xFC,0x80, +0xFC,0x00,0x49,0xFF,0xF3,0x9C,0xFC,0x80,0xFC,0x20,0x49,0x00,0x07,0x6D,0x2E,0x17, +0xF7,0x04,0xC9,0x23,0x84,0x52,0x3E,0x27,0xF0,0xFC,0x44,0x32,0x06,0xA4,0x80,0x40, +0x44,0x60,0x00,0x4D,0x84,0x01,0x44,0x50,0x04,0x81,0x2E,0x77,0xEF,0xC8,0x9D,0x01, +0xC7,0x06,0x38,0x11,0x80,0x08,0x38,0x61,0x90,0x08,0xD5,0x07,0xA7,0xD1,0x38,0x71, +0x80,0x08,0xA7,0xD0,0x38,0x71,0x90,0x08,0x8C,0x02,0x8C,0x42,0x4C,0x02,0xFF,0xEF, +0x84,0x20,0x3E,0x17,0xEF,0xC8,0xD5,0x12,0x5A,0x18,0x01,0x13,0x84,0x12,0x3E,0x07, +0xF0,0xFC,0x84,0x40,0x80,0x01,0x44,0x12,0x06,0xA4,0x9C,0xC1,0x38,0x20,0x80,0x08, +0x8C,0x02,0x38,0x20,0x8C,0x08,0x5A,0x08,0x65,0xFA,0x49,0xFF,0xFD,0xFD,0xFC,0xA0, +0xFC,0x00,0x2E,0x07,0xEF,0x14,0x3E,0x07,0xF0,0xFC,0x84,0x01,0x49,0xFF,0xFD,0xF4, +0x49,0xFF,0xF2,0xE5,0xFC,0x80,0xFC,0x00,0x3C,0x13,0xF8,0x7D,0x2E,0x07,0xF6,0x74, +0x96,0x49,0x96,0x00,0x44,0x32,0x0C,0x1C,0x5A,0x18,0x03,0x12,0x5A,0x00,0x53,0x04, +0x48,0x00,0x04,0x50,0x2E,0x07,0xF6,0x75,0x2E,0x17,0xF6,0x76,0x40,0x00,0x20,0x08, +0xFE,0x0F,0x50,0x00,0x7F,0xB8,0xE6,0x01,0x48,0x00,0x02,0x3A,0x5A,0x18,0x01,0x36, +0x5A,0x08,0x01,0x04,0x48,0x00,0x02,0x04,0x5A,0x08,0x03,0x04,0x48,0x00,0x02,0x02, +0x5A,0x08,0x20,0x04,0x48,0x00,0x02,0x00,0x5A,0x08,0x28,0x04,0x48,0x00,0x01,0xFF, +0x5A,0x08,0x21,0x04,0x48,0x00,0x01,0xFE,0x5A,0x08,0x22,0x04,0x48,0x00,0x01,0xFD, +0x5A,0x08,0x23,0x04,0x48,0x00,0x01,0xFC,0x5A,0x08,0x15,0x04,0x48,0x00,0x01,0xFB, +0x5A,0x08,0x17,0x04,0x48,0x00,0x01,0xFA,0x5A,0x08,0x04,0x04,0x48,0x00,0x01,0xF9, +0x5A,0x08,0xF3,0x04,0x48,0x00,0x01,0xF8,0x5A,0x00,0xD0,0x04,0x48,0x00,0x04,0x12, +0x44,0xF0,0x00,0x41,0x48,0x00,0x02,0x04,0x5A,0x18,0x02,0x39,0x5A,0x08,0xF6,0x0C, +0x2E,0x07,0xF6,0x75,0x5A,0x00,0xF2,0x04,0x48,0x00,0x04,0x04,0x44,0xF0,0x00,0x36, +0x48,0x00,0x01,0xF6,0x5A,0x08,0x61,0x13,0x2E,0x17,0xF6,0x75,0x96,0x48,0xC1,0x08, +0x2E,0x17,0xF6,0x75,0x96,0x48,0x5A,0x10,0x01,0x04,0x48,0x00,0x01,0xD8,0x44,0x00, +0x01,0x00,0x49,0xFF,0xE5,0x03,0x48,0x00,0x01,0xD2,0x5A,0x00,0x62,0x04,0x48,0x00, +0x03,0xE9,0x2E,0x07,0xF6,0x75,0x96,0x00,0xC0,0x0D,0x2E,0x07,0xF6,0x75,0x96,0x00, +0x5A,0x00,0x01,0x09,0x2E,0x07,0xF6,0x75,0x96,0x00,0x5A,0x00,0x02,0x04,0x48,0x00, +0x01,0xC1,0x49,0xFF,0xF2,0x00,0x48,0x00,0x01,0xBD,0x5A,0x18,0x10,0x68,0x5A,0x00, +0xF1,0x04,0x48,0x00,0x03,0xD6,0x84,0x00,0x44,0x12,0x06,0x90,0x38,0x21,0x80,0x00, +0x38,0x20,0x80,0x08,0x8C,0x01,0x5A,0x08,0x10,0xFB,0x2E,0x07,0xF6,0x75,0x2E,0x57, +0xF6,0x76,0x96,0x00,0x96,0x68,0x40,0x50,0x20,0x08,0x5A,0x00,0x10,0x06,0x5A,0x00, +0x1D,0x04,0xFF,0x4F,0xD5,0x02,0x97,0x69,0x44,0x00,0x1D,0x00,0xD0,0x3B,0x5C,0xF2, +0x9D,0x01,0xE8,0x1B,0x44,0x00,0x04,0x00,0x4C,0x50,0x03,0xAF,0x5C,0xF2,0x84,0x01, +0xE8,0x09,0x5A,0x51,0x00,0x04,0x48,0x00,0x03,0xA5,0x44,0xF0,0x00,0x1C,0x48,0x00, +0x01,0x97,0x44,0x00,0x05,0x00,0xD0,0x22,0x44,0x00,0x08,0x00,0x4C,0x50,0x43,0x9A, +0x44,0xF0,0x00,0x24,0x48,0x00,0x01,0x8C,0x44,0x00,0x32,0x00,0xD0,0x1F,0x5C,0xF2, +0xB2,0x01,0xE8,0x09,0x44,0x00,0x30,0x00,0x4C,0x50,0x43,0x8C,0x44,0xF0,0x00,0x3C, +0x48,0x00,0x01,0x7E,0x44,0x00,0x33,0x00,0xD0,0x15,0x44,0x00,0x34,0x00,0x4C,0x50, +0x43,0x81,0x44,0xF0,0x00,0x3B,0x48,0x00,0x01,0x73,0x44,0xF0,0x00,0x20,0x48,0x00, +0x01,0x6F,0x44,0xF0,0x00,0x37,0x48,0x00,0x01,0x6B,0x44,0xF0,0x00,0x38,0x48,0x00, +0x01,0x67,0x44,0xF0,0x00,0x3A,0x48,0x00,0x01,0x63,0x5A,0x00,0x01,0x04,0x48,0x00, +0x03,0x69,0x2E,0x07,0xF6,0x75,0x5A,0x00,0x0A,0x04,0x48,0x00,0x03,0x63,0x2E,0x07, +0xF6,0x76,0x5A,0x08,0x08,0x15,0x5A,0x18,0x06,0x13,0x2E,0x37,0xF6,0x77,0x2E,0x07, +0xF6,0x78,0x2E,0x27,0xF6,0x79,0x40,0x00,0x20,0x08,0x88,0x03,0x40,0x21,0x40,0x08, +0x88,0x02,0x3C,0x0F,0xFB,0xC4,0x44,0xF0,0x00,0x14,0xD5,0x02,0x85,0xE0,0x2E,0x27, +0xF6,0x76,0x5A,0x28,0x09,0x1A,0x5A,0x18,0x04,0x18,0x2E,0x17,0xF6,0x77,0x5A,0x18, +0x01,0x04,0x48,0x00,0x01,0x2A,0x2E,0x17,0xF6,0x77,0x5A,0x18,0x02,0x04,0x48,0x00, +0x01,0x27,0x2E,0x17,0xF6,0x77,0x5A,0x10,0x03,0x04,0x48,0x00,0x01,0x29,0x44,0xF0, +0x00,0x17,0x48,0x00,0x01,0x25,0x2E,0x27,0xF6,0x76,0x5A,0x28,0x10,0x28,0x5A,0x18, +0x06,0x26,0x2E,0x17,0xF6,0x77,0x5A,0x10,0x01,0x06,0x2E,0x17,0xF6,0x77,0x5A,0x18, +0x02,0x0D,0x2E,0x27,0xF6,0x78,0x2E,0x17,0xF6,0x79,0x40,0x10,0xA0,0x08,0x88,0x22, +0x3C,0x1B,0xF7,0x87,0x48,0x00,0x01,0x0C,0x2E,0x17,0xF6,0x77,0x5A,0x10,0x03,0x04, +0x48,0x00,0x01,0x06,0x2E,0x27,0xF6,0x78,0x2E,0x17,0xF6,0x79,0x40,0x10,0xA0,0x08, +0x88,0x22,0x3C,0x1B,0xF7,0x86,0x48,0x00,0x00,0xFB,0x2E,0x27,0xF6,0x76,0x5A,0x28, +0x11,0x1A,0x5A,0x18,0x04,0x18,0x2E,0x17,0xF6,0x77,0x5A,0x18,0x01,0x04,0x48,0x00, +0x00,0xEA,0x2E,0x17,0xF6,0x77,0x5A,0x18,0x02,0x04,0x48,0x00,0x00,0xE7,0x2E,0x17, +0xF6,0x77,0x5A,0x10,0x03,0x04,0x48,0x00,0x00,0xE3,0x44,0xF0,0x00,0x1A,0x48,0x00, +0x00,0xDF,0x2E,0x27,0xF6,0x76,0x5A,0x20,0x12,0x04,0x48,0x00,0x00,0xD9,0x5A,0x10, +0x19,0x04,0x48,0x00,0x00,0xD5,0x2E,0x17,0xF6,0x77,0x5A,0x18,0x01,0x46,0x2E,0x17, +0xF6,0x78,0x3E,0x17,0xEE,0x20,0x2E,0x17,0xF6,0x79,0x3E,0x17,0xEE,0x22,0x2E,0x17, +0xF6,0x7A,0x3E,0x17,0xEE,0x21,0x2E,0x17,0xF6,0x7B,0x2E,0x27,0xF6,0x7C,0x40,0x10, +0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF7,0x0F,0x2E,0x17,0xF6,0x7D,0x2E,0x27,0xF6,0x7E, +0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF7,0x0E,0x2E,0x17,0xF6,0x7F,0x2E,0x27, +0xF6,0x80,0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF7,0x12,0x2E,0x17,0xF6,0x81, +0x2E,0x27,0xF6,0x82,0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF7,0x13,0x2E,0x17, +0xF6,0x83,0x2E,0x27,0xF6,0x84,0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF7,0x14, +0x2E,0x17,0xF6,0x85,0x2E,0x27,0xF6,0x86,0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B, +0xF7,0x15,0x48,0x00,0x00,0x8D,0x2E,0x17,0xF6,0x77,0x5A,0x18,0x02,0x04,0x48,0x00, +0x00,0x87,0x2E,0x17,0xF6,0x77,0x5A,0x10,0x03,0x04,0x48,0x00,0x00,0x81,0x2E,0x17, +0xF6,0x78,0x3E,0x17,0xEE,0x14,0x2E,0x17,0xF6,0x79,0x3E,0x17,0xEE,0x15,0x2E,0x17, +0xF6,0x7A,0x3E,0x17,0xEE,0x16,0x2E,0x17,0xF6,0x7B,0x2E,0x27,0xF6,0x7C,0x40,0x10, +0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF7,0x08,0x2E,0x17,0xF6,0x7D,0x2E,0x27,0xF6,0x7E, +0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF7,0x09,0x2E,0x17,0xF6,0x7F,0x2E,0x27, +0xF6,0x80,0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF7,0x04,0x2E,0x17,0xF6,0x81, +0x2E,0x27,0xF6,0x82,0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF7,0x05,0x2E,0x17, +0xF6,0x83,0x2E,0x27,0xF6,0x84,0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B,0xF7,0x06, +0x2E,0x17,0xF6,0x85,0x2E,0x27,0xF6,0x86,0x40,0x10,0xA0,0x08,0x88,0x22,0x3C,0x1B, +0xF7,0x07,0x2E,0x17,0xF6,0x87,0x3E,0x17,0xEE,0x17,0x2E,0x17,0xF6,0x88,0x3E,0x17, +0xEE,0x18,0x2E,0x17,0xF6,0x89,0x3E,0x17,0xEE,0x19,0xD5,0x31,0x85,0xE2,0xD5,0x2F, +0x85,0xE5,0xD5,0x2D,0x44,0xF0,0x00,0x29,0xD5,0x2A,0x44,0xF0,0x00,0x2D,0xD5,0x27, +0x44,0xF0,0x00,0x2A,0xD5,0x24,0x44,0xF0,0x00,0x2B,0xD5,0x21,0x44,0xF0,0x00,0x2C, +0xD5,0x1E,0x44,0xF0,0x00,0x2E,0xD5,0x1B,0x44,0xF0,0x00,0x2F,0xD5,0x18,0x44,0xF0, +0x00,0x42,0xD5,0x15,0x44,0xF0,0x00,0x30,0xD5,0x12,0x44,0xF0,0x00,0x3F,0xD5,0x0F, +0x44,0xF0,0x00,0x40,0xD5,0x0C,0x44,0xF0,0x00,0x15,0xD5,0x09,0x44,0xF0,0x00,0x16, +0xD5,0x06,0x44,0xF0,0x00,0x18,0xD5,0x03,0x44,0xF0,0x00,0x19,0x84,0x20,0x3C,0x1F, +0xFB,0x8C,0x5A,0xF8,0x01,0x1B,0x44,0x0F,0xFF,0xA7,0x3E,0x07,0xF0,0xFC,0x84,0x05, +0x3E,0x07,0xF0,0xFD,0xFA,0x0C,0x3E,0x07,0xF0,0xFE,0xFA,0x22,0xFA,0x10,0x3E,0x07, +0xF0,0xFF,0x3E,0x07,0xF1,0x00,0x3E,0x17,0xF1,0x01,0x3E,0x07,0xF1,0x02,0x3E,0x17, +0xF1,0x03,0x84,0x08,0x48,0x00,0x01,0xE1,0x5A,0xF8,0x02,0x30,0x2E,0x07,0xF7,0x06, +0x44,0x1F,0xFF,0x81,0x3E,0x17,0xF0,0xFC,0x3E,0x07,0xF0,0xFD,0xC8,0x0D,0x44,0x10, +0x00,0x5A,0x3E,0x17,0xF0,0xFE,0x3E,0x07,0xF0,0xFF,0xFA,0x1B,0x3E,0x07,0xF1,0x00, +0x84,0x05,0x48,0x00,0x01,0xCA,0x44,0x0F,0xFF,0xA7,0x3E,0x07,0xF0,0xFE,0x84,0x05, +0x3E,0x07,0xF0,0xFF,0xFA,0x0C,0x3E,0x07,0xF1,0x00,0xFA,0x22,0xFA,0x10,0x3E,0x07, +0xF1,0x01,0x3E,0x17,0xF1,0x02,0x3E,0x07,0xF1,0x03,0x3E,0x17,0xF1,0x04,0x3E,0x07, +0xF1,0x05,0x84,0x0A,0x48,0x00,0x01,0xB1,0x5A,0xF8,0x05,0x0A,0x84,0x01,0x3E,0x07, +0xF0,0xFC,0x3E,0x17,0xF0,0xFD,0x3E,0x17,0xF0,0xFE,0xD5,0x64,0x5A,0xF8,0x29,0x26, +0xFA,0x10,0x84,0x7F,0x84,0x47,0x3E,0x07,0xF0,0xFC,0x3E,0x17,0xF0,0xFD,0x3E,0x17, +0xF0,0xFE,0x3E,0x37,0xF0,0xFF,0x3E,0x27,0xF1,0x00,0x3E,0x37,0xF1,0x01,0x3E,0x27, +0xF1,0x02,0x84,0x6A,0xFA,0x42,0x3E,0x27,0xF1,0x03,0x3E,0x07,0xF1,0x04,0x3E,0x37, +0xF1,0x05,0x3E,0x17,0xF1,0x06,0x3E,0x27,0xF1,0x07,0x3E,0x07,0xF1,0x08,0x3E,0x17, +0xF1,0x09,0x84,0x0E,0x48,0x00,0x01,0x81,0x5A,0xF8,0x2A,0x1D,0xFA,0x11,0x3E,0x07, +0xF0,0xFC,0x44,0x2F,0xFF,0x81,0x84,0x08,0xFA,0x6A,0x3E,0x27,0xF0,0xFD,0x3E,0x07, +0xF0,0xFE,0x3E,0x17,0xF0,0xFF,0x3E,0x37,0xF1,0x00,0x3E,0x27,0xF1,0x01,0x3E,0x07, +0xF1,0x02,0x84,0x01,0x3E,0x17,0xF1,0x03,0x3E,0x07,0xF1,0x04,0x84,0x09,0x48,0x00, +0x01,0x64,0x5A,0xF8,0x2B,0x10,0xFA,0x12,0x3E,0x07,0xF0,0xFC,0x84,0x05,0x3E,0x07, +0xF0,0xFD,0x84,0x04,0x3E,0x07,0xF0,0xFE,0x3E,0x17,0xF0,0xFF,0x84,0x04,0x48,0x00, +0x01,0x54,0x5A,0xF8,0x42,0x05,0x2E,0x00,0x01,0x84,0xD5,0x17,0x5A,0xF8,0x2C,0x0E, +0xFA,0x13,0x3E,0x07,0xF0,0xFC,0x84,0x01,0x3E,0x07,0xF0,0xFD,0x3E,0x17,0xF0,0xFE, +0x84,0x06,0x3E,0x07,0xF0,0xFF,0xD5,0xEB,0x5A,0xF8,0x2E,0x05,0x3E,0x17,0xF0,0xFC, +0xD5,0x06,0x5A,0xF8,0x2F,0x08,0x84,0x00,0x3E,0x07,0xF0,0xFC,0x84,0x01,0x48,0x00, +0x01,0x34,0x5A,0xF8,0x2D,0x2F,0xFA,0x18,0x3E,0x07,0xF0,0xFC,0x84,0x62,0x84,0x00, +0x84,0x26,0x84,0x4C,0x3E,0x37,0xF0,0xFD,0x3E,0x07,0xF0,0xFE,0x3E,0x17,0xF0,0xFF, +0x3E,0x27,0xF1,0x00,0x3E,0x37,0xF1,0x01,0x3E,0x07,0xF1,0x02,0x84,0x64,0x3E,0x17, +0xF1,0x03,0x3E,0x27,0xF1,0x04,0x84,0x41,0x3E,0x27,0xF1,0x05,0x3E,0x07,0xF1,0x06, +0x3E,0x37,0xF1,0x07,0x3E,0x37,0xF1,0x08,0x3E,0x27,0xF1,0x09,0x3E,0x07,0xF1,0x0A, +0x84,0x03,0x3E,0x07,0xF1,0x0B,0x3E,0x17,0xF1,0x0C,0xFA,0x01,0x48,0x00,0x01,0x05, +0x5A,0xF8,0x1B,0x0A,0x84,0x01,0x3E,0x07,0xF7,0x06,0x84,0x01,0x49,0xFF,0xED,0xB1, +0x48,0x00,0x01,0x04,0x5A,0xF8,0x30,0x06,0x49,0xFF,0xFC,0x9C,0x48,0x00,0x00,0xFE, +0x5A,0xF8,0x36,0x06,0x49,0xFF,0xFC,0x5A,0x48,0x00,0x00,0xF8,0x5A,0xF8,0x41,0x0E, +0x44,0x0F,0xFF,0xD0,0x3E,0x07,0xF0,0xFC,0x3C,0x03,0xF8,0x6F,0x96,0x40,0x3E,0x17, +0xF0,0xFD,0x92,0x08,0x48,0x00,0x00,0xDE,0x5A,0xF8,0x1C,0x08,0x44,0x00,0x00,0x50, +0x3E,0x07,0xEF,0x14,0x84,0x01,0xD5,0x38,0x5A,0xF8,0x1F,0x08,0x44,0x00,0x00,0x50, +0x3E,0x07,0xEF,0x14,0x84,0x04,0xD5,0x30,0x5A,0xF8,0x20,0x08,0x44,0x00,0x00,0x50, +0x3E,0x07,0xEF,0x14,0x84,0x05,0xD5,0x28,0x5A,0xF8,0x24,0x08,0x44,0x00,0x00,0x50, +0x3E,0x07,0xEF,0x14,0x84,0x09,0xD5,0x20,0x5A,0xF8,0x37,0x08,0x44,0x00,0x00,0x50, +0x3E,0x07,0xEF,0x14,0x84,0x0F,0xD5,0x18,0x5A,0xF8,0x38,0x08,0x44,0x00,0x00,0x50, +0x3E,0x07,0xEF,0x14,0xFA,0x00,0xD5,0x10,0x5A,0xF8,0x3A,0x08,0x44,0x00,0x00,0x50, +0x3E,0x07,0xEF,0x14,0xFA,0x02,0xD5,0x08,0x5A,0xF8,0x3B,0x0B,0x44,0x00,0x00,0x50, +0x3E,0x07,0xEF,0x14,0xFA,0x03,0x3E,0x07,0xF7,0x05,0x48,0x00,0x00,0xA7,0x5A,0xF0, +0x3C,0x04,0x48,0x00,0x00,0x8A,0x84,0x11,0x3E,0x07,0xF0,0xFC,0x44,0x00,0x00,0x30, +0x3E,0x07,0xF0,0xFD,0x84,0x01,0x3E,0x07,0xF0,0xFE,0x44,0x00,0x00,0x64,0x3E,0x07, +0xF0,0xFF,0x84,0x00,0x3E,0x07,0xF1,0x00,0x84,0x1F,0x3E,0x07,0xF1,0x01,0x3E,0x07, +0xF1,0x02,0x46,0x00,0x00,0x81,0x44,0x10,0x00,0x9C,0x04,0x00,0x00,0x19,0x85,0xEA, +0x40,0x00,0x3C,0x08,0x92,0x0F,0xFE,0x0C,0x50,0x00,0x03,0xA8,0x40,0x00,0x3C,0x17, +0x8C,0x19,0x44,0xF0,0x03,0xE8,0x40,0x00,0x3C,0x17,0x96,0x01,0x84,0x27,0x5C,0xF0, +0x07,0xD0,0x3E,0x17,0xF1,0x03,0x80,0x20,0xE8,0x03,0x44,0x10,0x07,0xD0,0x96,0x48, +0x3E,0x17,0xF1,0x04,0x84,0x10,0x84,0x20,0x3E,0x17,0xF1,0x05,0x84,0x41,0x3E,0x07, +0xF1,0x06,0x44,0x4F,0xFF,0x90,0x84,0x1F,0x84,0x6A,0x3E,0x37,0xF1,0x07,0x3E,0x07, +0xF1,0x08,0x3E,0x07,0xF1,0x09,0x3E,0x27,0xF1,0x0A,0x3E,0x47,0xF1,0x0B,0x3E,0x07, +0xF1,0x0C,0x3E,0x07,0xF1,0x0D,0x3E,0x27,0xF1,0x0E,0x3E,0x47,0xF1,0x0F,0x44,0x4F, +0xFF,0xC0,0x3E,0x27,0xF1,0x10,0x3E,0x47,0xF1,0x11,0x84,0x82,0x3E,0x47,0xF1,0x12, +0x44,0x4F,0xFF,0x80,0x3E,0x47,0xF1,0x13,0x84,0x8F,0x3E,0x17,0xF1,0x14,0x3E,0x17, +0xF1,0x15,0x3E,0x17,0xF1,0x16,0x3E,0x27,0xF1,0x17,0x3E,0x47,0xF1,0x18,0x84,0x84, +0x3E,0x47,0xF1,0x19,0xFA,0x9D,0x3E,0x47,0xF1,0x1A,0x3E,0x37,0xF1,0x1B,0x3E,0x27, +0xF1,0x1C,0x84,0x4C,0x3E,0x27,0xF1,0x1D,0x84,0x46,0x3E,0x27,0xF1,0x1E,0x3E,0x17, +0xF1,0x1F,0x3E,0x07,0xF1,0x20,0x3E,0x07,0xF1,0x21,0x3E,0x07,0xF1,0x22,0x3E,0x07, +0xF1,0x23,0xFA,0x18,0xD5,0x11,0x5A,0xF8,0x3D,0x15,0x84,0x0F,0x3E,0x07,0xF0,0xFC, +0x84,0x01,0x3E,0x07,0xF0,0xFD,0x44,0x04,0x50,0x28,0xB4,0x00,0x92,0x01,0x96,0x06, +0x3E,0x07,0xF0,0xFE,0x84,0x03,0x49,0xFF,0xF9,0x9F,0x49,0xFF,0xEE,0x90,0xD5,0x05, +0x5A,0xF8,0x3E,0x04,0x49,0xFF,0xF8,0x6D,0x84,0x01,0x3C,0x0F,0xFB,0x8C,0xFC,0x80, +0x85,0xE0,0x48,0xFF,0xFD,0xF5,0x44,0xF0,0x00,0x1F,0x48,0xFF,0xFD,0xF1,0x5A,0x08, +0x0F,0xF5,0x2E,0x07,0xF6,0x75,0x5A,0x00,0x01,0x0E,0x2E,0x07,0xF6,0x75,0x5A,0x08, +0x02,0xF1,0x2E,0x07,0xF6,0x77,0x3E,0x07,0xF0,0xE4,0x44,0xF0,0x00,0x3E,0x48,0xFF, +0xFD,0xDF,0x44,0xF0,0x00,0x3D,0x48,0xFF,0xFD,0xDB,0xFC,0x00,0x2E,0x07,0xEF,0x14, +0x5A,0x08,0x52,0x06,0x44,0x00,0x00,0x51,0x3E,0x07,0xEF,0x14,0x2E,0x07,0xF7,0x05, +0x5A,0x08,0x01,0x08,0x84,0xC0,0x3E,0x67,0xF7,0x04,0x49,0x00,0x07,0xC6,0xD5,0x32, +0x5A,0x08,0x04,0x08,0x84,0xC0,0x3E,0x67,0xF7,0x04,0x49,0x00,0x09,0xD3,0xD5,0x2A, +0x5A,0x08,0x05,0x08,0x84,0xC0,0x3E,0x67,0xF7,0x04,0x49,0x00,0x0D,0x94,0xD5,0x22, +0x5A,0x00,0x09,0xFA,0x5A,0x08,0x0F,0x0E,0x2E,0x07,0xF6,0x76,0x84,0xC0,0x2E,0x17, +0xF6,0x77,0x2E,0x27,0xF6,0x78,0x3E,0x67,0xF7,0x04,0x49,0x00,0x0F,0x1E,0xD5,0x12, +0x54,0x10,0x00,0xFD,0x5A,0x18,0x10,0x08,0x84,0xC0,0x3E,0x67,0xF7,0x04,0x49,0x00, +0x10,0x9B,0xD5,0x08,0x5A,0x08,0x13,0x09,0x84,0xC0,0x3E,0x67,0xF7,0x04,0x49,0x00, +0x12,0xEB,0x3E,0x67,0xF7,0x05,0xFC,0x80,0xFC,0x00,0x44,0x00,0x00,0x64,0x49,0xFF, +0xDD,0x6A,0x44,0x00,0x23,0x56,0x49,0xFF,0xDD,0x71,0x44,0x00,0x1F,0x57,0x49,0xFF, +0xDD,0x78,0x44,0x00,0x01,0x90,0x49,0xFF,0xDD,0x7F,0xFC,0x80,0xFC,0x40,0x51,0xFF, +0xFA,0xE0,0x84,0x20,0xFA,0x50,0xB0,0x10,0x49,0x00,0x1C,0x9C,0xB1,0x98,0x44,0x02, +0x01,0x60,0x80,0x26,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24,0x3A,0x20,0x14,0x00, +0xB1,0xE0,0x44,0x02,0x01,0x80,0x3A,0x20,0x94,0x20,0x80,0x27,0x3A,0x20,0x14,0x04, +0x3A,0x20,0x94,0x24,0x3A,0x20,0x14,0x00,0x3A,0x20,0x94,0x20,0x44,0x12,0x01,0xA0, +0xB0,0x0C,0x3A,0x20,0x94,0x00,0x44,0x12,0x01,0xB0,0x3A,0x20,0x14,0x20,0xB0,0x07, +0x3A,0x20,0x8C,0x00,0x3A,0x20,0x0C,0x20,0x44,0x02,0x01,0xB8,0x50,0x8F,0x80,0x24, +0x3A,0x20,0x10,0x00,0x3A,0x24,0x10,0x20,0x85,0x40,0x39,0xC4,0x28,0x00,0x40,0x0E, +0x00,0x13,0xC0,0x13,0x5A,0x00,0x03,0x12,0xB0,0x10,0x38,0x00,0x72,0x02,0xC8,0x0D, +0x38,0x03,0x72,0x02,0x40,0x90,0x04,0x08,0x49,0xFF,0xED,0x53,0x49,0xFF,0xF0,0x7A, +0xB0,0x50,0x88,0x09,0x38,0x00,0xF2,0x0A,0x8D,0x41,0x5A,0xA8,0x0C,0xE8,0x84,0xC0, +0xB0,0x10,0x38,0x00,0x1A,0x02,0xF0,0x81,0xC8,0x07,0x8C,0xC1,0x5A,0x68,0x08,0xFA, +0x51,0xFF,0x85,0x20,0xFC,0xC0,0xB0,0x07,0x87,0x80,0x38,0xA3,0x00,0x00,0x40,0x95, +0x04,0x08,0x40,0x04,0x80,0x13,0xF0,0x82,0x94,0x01,0xF0,0x83,0x40,0xFE,0x24,0x07, +0xE8,0xED,0x4F,0xC3,0x00,0x13,0x38,0x83,0x9A,0x01,0x49,0xFF,0xDC,0x0A,0x44,0x20, +0x01,0x20,0x80,0x28,0x42,0x10,0x08,0x73,0xB0,0x0C,0x96,0x49,0x38,0x20,0x19,0x01, +0xB0,0x28,0x92,0x42,0x49,0xFF,0xDC,0x64,0x4D,0xC5,0x40,0x14,0x38,0x83,0x9A,0x01, +0x49,0xFF,0xDB,0xF7,0x44,0x20,0x01,0x20,0x50,0x14,0x02,0x40,0x42,0x10,0x08,0x73, +0xB0,0x0C,0x96,0x49,0x38,0x20,0x19,0x01,0xB0,0x28,0x92,0x42,0x49,0xFF,0xDC,0x50, +0x40,0x14,0xF0,0x01,0x8E,0x21,0x94,0x4C,0x40,0x0E,0x00,0x13,0x97,0x49,0x40,0x1E, +0x28,0x06,0x94,0xC4,0x84,0x40,0xF1,0x84,0x50,0x8F,0x80,0xA0,0xF4,0x04,0x40,0x11, +0x04,0x09,0xC4,0x03,0x88,0x23,0xD5,0x03,0x88,0x25,0x96,0x49,0x38,0x44,0x06,0x02, +0x54,0xF2,0x00,0x1F,0x5A,0xF0,0x1F,0x1A,0x40,0xF2,0x18,0x09,0x54,0xF7,0x80,0x1F, +0x5A,0xF0,0x1F,0x14,0x04,0xFF,0x80,0x01,0x39,0xE7,0x81,0x01,0x50,0xFF,0x5D,0xEB, +0x5C,0xF7,0x82,0x13,0xE9,0x06,0x50,0xFF,0x64,0x27,0x5C,0xF7,0x82,0x13,0xE8,0x05, +0x50,0x42,0x00,0x41,0x38,0x44,0x06,0x0A,0x38,0x44,0x06,0x02,0x40,0xF2,0x48,0x09, +0x54,0xF7,0x80,0x1F,0x5A,0xF0,0x1F,0x21,0x40,0xF2,0x30,0x09,0x54,0xF7,0x80,0x1F, +0x5A,0xF0,0x1F,0x1B,0x04,0xFF,0x80,0x02,0x05,0xEF,0x80,0x01,0x40,0xF0,0x3C,0x00, +0x40,0xF7,0x80,0x13,0x39,0xEF,0x3D,0x01,0x50,0xFF,0x5D,0xEB,0x5C,0xF7,0x82,0x13, +0xE9,0x06,0x50,0xFF,0x64,0x27,0x5C,0xF7,0x82,0x13,0xE8,0x06,0x44,0xF4,0x10,0x00, +0x88,0x8F,0x38,0x44,0x06,0x0A,0xF1,0x03,0x8C,0x42,0x88,0x01,0x96,0x90,0x96,0x01, +0x5A,0x28,0x20,0xAE,0x50,0x5E,0x00,0x01,0x4C,0x55,0x40,0x16,0x38,0x13,0x9A,0x01, +0xF5,0x85,0xF1,0x84,0x49,0xFF,0xDB,0x7D,0x44,0x20,0x01,0x20,0xF1,0x04,0x42,0x10, +0x08,0x73,0xB0,0x0C,0x96,0x49,0x38,0x20,0x19,0x01,0x80,0x08,0x92,0x42,0x49,0xFF, +0xDB,0xF3,0xF5,0x05,0x4C,0x54,0xC0,0x16,0x38,0x13,0x9A,0x01,0xF1,0x84,0x49,0xFF, +0xDB,0x68,0xF1,0x04,0x44,0x20,0x01,0x20,0x50,0x10,0x82,0x40,0x42,0x10,0x08,0x73, +0xB0,0x0C,0x96,0x49,0x38,0x20,0x19,0x01,0x80,0x08,0x92,0x42,0x49,0xFF,0xDB,0xDC, +0x50,0x5E,0x00,0x01,0x55,0xC2,0x80,0xFF,0x48,0xFF,0xFF,0x42,0xFC,0x49,0x44,0x12, +0x01,0xC4,0xB1,0x86,0x80,0x06,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20, +0x8C,0x00,0x3A,0x20,0x0C,0x20,0x84,0x20,0xB0,0x0C,0xFA,0x48,0x49,0x00,0x1B,0x6A, +0x84,0x21,0x10,0x1F,0x80,0x31,0x10,0x1F,0x80,0x3D,0x84,0x02,0x44,0x12,0x01,0xDC, +0x10,0x0F,0x80,0x32,0x10,0x0F,0x80,0x3E,0x80,0x01,0x80,0x9F,0x3A,0x20,0x0C,0x04, +0x3A,0x22,0x0C,0x24,0xB1,0xC3,0x3A,0x20,0x8C,0x04,0xA4,0x00,0x80,0x27,0xAC,0x20, +0x3A,0x20,0x8C,0x24,0xAC,0x08,0x80,0x06,0x49,0xFF,0xE1,0xAC,0xB0,0x0C,0x49,0xFF, +0xE1,0xFC,0x80,0x1F,0x49,0xFF,0xE1,0xBF,0x80,0x07,0x81,0x3F,0x49,0xFF,0xE2,0x0E, +0xFC,0xC9,0xFC,0x26,0x44,0x12,0x01,0xE8,0x80,0xDF,0x3A,0x20,0x94,0x04,0x3A,0x23, +0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x23,0x14,0x24,0x3A,0x20,0x94,0x00,0x80,0x1F, +0x80,0xE6,0x3A,0x23,0x14,0x20,0x49,0xFF,0xE1,0x57,0xFC,0xA6,0xFC,0x2C,0x44,0x12, +0x02,0x18,0xB1,0x8C,0x80,0x06,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20, +0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x00,0x3A,0x20,0x14,0x20,0x84,0x20, +0xB0,0x06,0xFA,0x48,0x49,0x00,0x1B,0x16,0xB1,0xC1,0x44,0x12,0x02,0x48,0x80,0x07, +0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0xB4,0x21,0xB6,0x20,0x80,0x06,0x49,0xFF, +0xE1,0x33,0xB0,0x06,0x49,0xFF,0xE1,0x4D,0x80,0x07,0x49,0xFF,0xE1,0x37,0xFC,0xAC, +0xFC,0x01,0x44,0x12,0x02,0x5C,0x80,0x1F,0x3A,0x20,0x8C,0x00,0x3A,0x2F,0x8C,0x20, +0x49,0xFF,0xE1,0x36,0xFC,0x81,0xFC,0x00,0xC8,0x04,0x49,0xFF,0xFE,0x3F,0xFC,0x80, +0x44,0x00,0x00,0x64,0x49,0xFF,0xDB,0xA7,0x44,0x00,0x13,0xA6,0x49,0xFF,0xDB,0xAE, +0x44,0x00,0x0F,0xA6,0x49,0xFF,0xDB,0xB5,0x44,0x00,0x01,0x90,0x49,0xFF,0xDB,0xBC, +0xFC,0x80,0xFC,0x00,0xE6,0x06,0xE8,0x06,0x44,0x12,0x02,0xFC,0x38,0x00,0x80,0x00, +0xD5,0x02,0x84,0x00,0x49,0xFF,0xDB,0xC6,0xFC,0x80,0xFC,0x00,0xE6,0x06,0xE8,0x06, +0x44,0x12,0x02,0xF4,0x38,0x00,0x80,0x00,0xD5,0x02,0x84,0x00,0x8C,0x01,0x96,0x00, +0x49,0xFF,0xDB,0xAD,0xFC,0x80,0xFC,0x00,0x5A,0x00,0x01,0x0E,0xC0,0x04,0x5A,0x00, +0x02,0x1C,0xFC,0x80,0x84,0x00,0x49,0xFF,0xE7,0x8C,0x2E,0x07,0xF7,0x12,0x49,0xFF, +0xFF,0xC4,0xD5,0x2A,0x49,0xFF,0xE7,0x85,0x44,0x00,0x00,0x64,0x49,0xFF,0xDB,0x6B, +0x44,0x00,0x23,0x96,0x49,0xFF,0xDB,0x72,0x44,0x00,0x23,0x96,0x49,0xFF,0xDB,0x79, +0x44,0x00,0x01,0x90,0xD5,0x17,0x84,0x01,0x49,0xFF,0xE7,0x73,0x44,0x00,0x03,0xE8, +0x49,0xFF,0xDB,0x59,0x44,0x00,0x27,0x10,0x49,0xFF,0xDB,0x6B,0x46,0x20,0x00,0x81, +0x46,0x12,0x71,0x00,0x83,0x82,0xB8,0x10,0x96,0x01,0xFE,0x0F,0xB8,0x90,0x44,0x00, +0x27,0x10,0x49,0xFF,0xDB,0x69,0x84,0x00,0x49,0xFF,0xFF,0xB9,0xFC,0x80,0x00,0x00, +0xE6,0x04,0xE8,0x24,0xFC,0x00,0x44,0xF0,0x4D,0x54,0x38,0x07,0x80,0x00,0x40,0xF0, +0x3C,0x00,0xDD,0x0F,0x04,0x14,0x1E,0x22,0x84,0x00,0x49,0xFF,0xDE,0x4D,0x84,0x01, +0x49,0xFF,0xDE,0x38,0x84,0x01,0xD5,0x0F,0x84,0x00,0x49,0xFF,0xDE,0x45,0x84,0x01, +0xD5,0x07,0x84,0x01,0xD5,0x02,0x84,0x00,0x49,0xFF,0xDE,0x3E,0x84,0x00,0x49,0xFF, +0xDE,0x29,0x84,0x00,0x49,0xFF,0xDE,0x2F,0xFC,0x80,0xDD,0x9E,0xFC,0x00,0x49,0xFF, +0xEB,0x58,0x80,0x60,0x3E,0x00,0x01,0x80,0x44,0x12,0x0E,0x28,0x44,0x02,0x12,0xA8, +0x44,0x20,0x02,0x40,0x2E,0x47,0xF7,0x11,0x49,0xFF,0xF3,0x33,0xFC,0x80,0x44,0x02, +0x12,0xA8,0xDD,0x9E,0x44,0x02,0x0E,0x28,0xDD,0x9E,0x44,0x02,0x0C,0xBC,0xDD,0x9E, +0xFC,0x00,0xC0,0x02,0x84,0x01,0x49,0xFF,0xDB,0x59,0x84,0x00,0x49,0xFF,0xDB,0x48, +0xFC,0x80,0xFC,0x00,0x84,0x00,0x80,0x20,0x49,0xFF,0xDD,0x58,0x84,0x00,0x84,0x21, +0x49,0xFF,0xDD,0x54,0xFC,0x80,0xFC,0x00,0x84,0x01,0x84,0x20,0x49,0xFF,0xDD,0x4E, +0x84,0x01,0x80,0x20,0x49,0xFF,0xDD,0x4A,0xFC,0x80,0xFC,0x49,0x80,0x20,0x44,0x02, +0x02,0x64,0x50,0xAF,0x80,0x0C,0x3A,0x20,0x10,0x00,0x44,0x02,0x02,0x70,0x3A,0x2F, +0x90,0x20,0x3A,0x20,0x10,0x00,0x3A,0x25,0x10,0x20,0x44,0x22,0x02,0x7C,0xB0,0x06, +0x3A,0x21,0x10,0x00,0x3A,0x20,0x10,0x20,0x44,0x22,0x02,0x88,0x50,0x9F,0x80,0x24, +0x3A,0x21,0x10,0x00,0x3A,0x24,0x90,0x20,0x44,0x22,0x01,0xB8,0xB1,0xCC,0x3A,0x21, +0x10,0x00,0x3A,0x23,0x90,0x20,0x44,0x22,0x02,0x94,0xB1,0x8F,0x3A,0x21,0x10,0x00, +0x80,0xBF,0x3A,0x23,0x10,0x20,0xC9,0x03,0x80,0x05,0xD5,0x12,0x5A,0x18,0x01,0x04, +0x80,0x0A,0xD5,0x0E,0x5A,0x10,0x02,0x0D,0x5A,0x18,0x03,0x04,0x80,0x09,0xD5,0x08, +0x5A,0x18,0x04,0x04,0x80,0x07,0xD5,0x04,0x5A,0x18,0x05,0x06,0x80,0x06,0x49,0xFF, +0xDA,0x60,0xFC,0xC9,0x5A,0x10,0x06,0xEA,0xFC,0xC9,0xFC,0x03,0x84,0x20,0x84,0x4C, +0x80,0xC0,0x80,0x1F,0x49,0x00,0x19,0xDE,0x44,0x12,0x02,0xA0,0xB0,0x03,0x3A,0x20, +0x90,0x00,0x3A,0x20,0x10,0x20,0xCE,0x03,0x80,0x1F,0xD5,0x03,0x5A,0x68,0x01,0x04, +0x49,0xFF,0xDA,0x5B,0xFC,0x83,0xFC,0x00,0x84,0x00,0x49,0xFF,0xFF,0xA0,0x84,0x00, +0x49,0xFF,0xFF,0xE5,0x84,0x00,0x49,0xFF,0xDD,0x3F,0x84,0x00,0x49,0xFF,0xDD,0x6E, +0x49,0xFF,0xFE,0x46,0x49,0xFF,0xFE,0x94,0x49,0xFF,0xFE,0xBC,0x84,0x00,0x49,0xFF, +0xE3,0x51,0x44,0x00,0x02,0x40,0x49,0xFF,0xE3,0x63,0x44,0x00,0x02,0x40,0x49,0xFF, +0xE3,0x54,0x84,0x09,0x49,0xFF,0xE3,0x67,0x84,0x03,0x49,0xFF,0xE3,0x6D,0x44,0x62, +0x06,0x1C,0x44,0x10,0x00,0xF0,0x84,0x00,0x49,0xFF,0xE1,0xF5,0x49,0xFF,0xF2,0xBC, +0x38,0x13,0x01,0x01,0x84,0x00,0x49,0xFF,0xE0,0xA3,0x44,0x10,0x00,0xF0,0x84,0x02, +0x49,0xFF,0xE1,0xE9,0x49,0xFF,0xF2,0xB0,0x38,0x13,0x01,0x01,0x84,0x02,0x49,0xFF, +0xE0,0x97,0x44,0x10,0x00,0xF0,0x84,0x05,0x49,0xFF,0xE1,0xDD,0x49,0xFF,0xF2,0xA4, +0x38,0x13,0x01,0x01,0x46,0x60,0x00,0x81,0x84,0x05,0x49,0xFF,0xE0,0x89,0x49,0xFF, +0xDE,0x23,0x04,0x03,0x03,0x91,0x92,0x0A,0x40,0x00,0x28,0x08,0x58,0x00,0x01,0xC0, +0x14,0x03,0x03,0x91,0x49,0xFF,0xDE,0x11,0x04,0x03,0x00,0x2C,0x46,0x1F,0xFC,0x0F, +0x92,0x06,0x94,0x06,0x58,0x00,0x00,0x2D,0x14,0x03,0x00,0x2C,0x04,0x03,0x00,0x2C, +0x50,0x10,0x8F,0xFF,0x46,0x20,0x02,0xD0,0xFE,0x0E,0xFE,0x17,0x14,0x03,0x00,0x2C, +0x04,0x03,0x00,0x2D,0x92,0x06,0x94,0x06,0x58,0x00,0x00,0x0A,0x14,0x03,0x00,0x2D, +0x04,0x03,0x00,0x2D,0xFE,0x46,0x46,0x00,0x00,0xA0,0xFE,0x47,0x14,0x13,0x00,0x2D, +0x04,0x13,0x00,0x2D,0x46,0x0C,0x0F,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x0E,0x46,0x10, +0xA0,0x00,0xFE,0x0F,0x14,0x03,0x00,0x2D,0x04,0x03,0x00,0x2F,0x92,0x06,0x94,0x06, +0x58,0x00,0x00,0x04,0x14,0x03,0x00,0x2F,0x44,0x00,0x02,0x80,0x49,0xFF,0xE1,0x7A, +0x84,0x00,0x49,0xFF,0xFE,0xEF,0x49,0xFF,0xFC,0x81,0x49,0xFF,0xE1,0x71,0x3E,0x07, +0xF7,0x12,0xFC,0x80,0x2E,0x07,0xF7,0x12,0xDD,0x9E,0x2E,0x07,0xF7,0x11,0xDD,0x9E, +0x2E,0x07,0xF7,0x10,0xDD,0x9E,0xFC,0x00,0x80,0xC0,0x49,0xFF,0xE2,0x62,0x80,0x06, +0x49,0xFF,0xD8,0xEB,0x84,0x00,0x84,0x2C,0x5A,0x68,0x01,0x1F,0x49,0xFF,0xE1,0xBF, +0x84,0x02,0x84,0x2A,0x49,0xFF,0xE1,0xBB,0x84,0x03,0x84,0x2A,0x49,0xFF,0xE1,0xB7, +0x84,0x05,0x84,0x2C,0x49,0xFF,0xE1,0xB3,0x84,0x07,0x84,0x2C,0x49,0xFF,0xE1,0xAF, +0x84,0x00,0x80,0x20,0x49,0xFF,0xD8,0x70,0x84,0x00,0x84,0x22,0x49,0xFF,0xD8,0x6C, +0x44,0x02,0x0D,0x20,0xD5,0x41,0x49,0xFF,0xE1,0xA2,0x84,0x01,0x84,0x2C,0x49,0xFF, +0xE1,0x9E,0x84,0x02,0x84,0x2A,0x49,0xFF,0xE1,0x9A,0x84,0x03,0x84,0x2A,0x49,0xFF, +0xE1,0x96,0x84,0x04,0x84,0x2C,0x49,0xFF,0xE1,0x92,0x84,0x05,0x84,0x2C,0x49,0xFF, +0xE1,0x8E,0x84,0x06,0x84,0x2C,0x49,0xFF,0xE1,0x8A,0x84,0x07,0x84,0x2C,0x49,0xFF, +0xE1,0x86,0x84,0x01,0x84,0x20,0x49,0xFF,0xD8,0x47,0x84,0x01,0x80,0x20,0x49,0xFF, +0xD8,0x43,0x84,0x00,0x84,0x22,0x49,0xFF,0xD8,0x3F,0x84,0x00,0x84,0x23,0x49,0xFF, +0xD8,0x3B,0x84,0x01,0x84,0x24,0x49,0xFF,0xD8,0x37,0x84,0x01,0x84,0x25,0x49,0xFF, +0xD8,0x33,0x84,0x01,0x84,0x26,0x49,0xFF,0xD8,0x2F,0x84,0x01,0x84,0x27,0x49,0xFF, +0xD8,0x2B,0x44,0x02,0x0D,0xA4,0x44,0x10,0x00,0x42,0x49,0xFF,0xDE,0x28,0xFC,0x80, +0xFC,0x21,0x84,0xC2,0xF0,0x81,0x80,0xE2,0x80,0x01,0x5A,0x10,0x01,0x03,0x84,0xC1, +0x49,0xFF,0xFF,0x8B,0xCF,0x0C,0x84,0x03,0x49,0xFF,0xDA,0xD0,0x84,0x03,0x49,0xFF, +0xDE,0x5A,0xF0,0x01,0x80,0x26,0x49,0xFF,0xF0,0x6B,0xFC,0xA1,0x84,0x02,0x49,0xFF, +0xDA,0xC5,0x84,0x02,0x49,0xFF,0xDE,0x4F,0xF0,0x01,0x80,0x26,0x84,0x40,0x49,0xFF, +0xEF,0xEE,0xFC,0xA1,0xFC,0x00,0x84,0x00,0x49,0xFF,0xFD,0xB5,0x84,0x00,0x84,0x20, +0x3E,0x07,0xF7,0x11,0x3E,0x07,0xF7,0x10,0x80,0x41,0x2E,0x07,0xF7,0x12,0x49,0xFF, +0xFF,0xD1,0xFC,0x80,0xFC,0x20,0x44,0x14,0x80,0x90,0x44,0x0F,0xFF,0xEF,0xB4,0x41, +0x46,0x70,0x00,0x81,0xFE,0x16,0xB6,0x01,0x44,0x04,0x80,0x10,0xFA,0x20,0xAE,0x40, +0xFA,0xC2,0x50,0x73,0x83,0xD4,0xB4,0x07,0x92,0x0B,0x96,0x27,0xC0,0x07,0x8E,0xC1, +0xC6,0x05,0x84,0x01,0x49,0xFF,0xE5,0xE4,0xD5,0xF7,0x44,0x04,0x80,0x10,0xFA,0x20, +0xAE,0x40,0x44,0x14,0x80,0x90,0xB4,0x01,0x58,0x00,0x00,0x10,0xB6,0x01,0xFC,0xA0, +0xFC,0x00,0x49,0xFF,0xF3,0x9A,0x5A,0x08,0x01,0x09,0x44,0x14,0x50,0x28,0xB4,0x01, +0x92,0x01,0x96,0x06,0x5A,0x00,0x01,0xFD,0xFC,0x80,0xFC,0x00,0x80,0xC0,0x5A,0x08, +0x01,0x04,0x49,0xFF,0xFF,0xEF,0x80,0x06,0x49,0x00,0x12,0x5B,0x80,0x06,0x49,0xFF, +0xD9,0x44,0xFC,0x80,0xFC,0x20,0x84,0xE0,0x80,0xC0,0x84,0x00,0x49,0xFF,0xFF,0xEF, +0x49,0xFF,0xFF,0xBA,0x49,0xFF,0xEC,0x69,0x3E,0x77,0xEF,0xCC,0x49,0xFF,0xE9,0x47, +0x3E,0x77,0xEF,0xCB,0x5A,0x68,0x01,0x05,0x80,0x06,0x49,0xFF,0xFF,0xE0,0xFC,0xA0, +0xFC,0x00,0x84,0x01,0x49,0xFF,0xFF,0xE8,0x2E,0x07,0xEF,0xCB,0xC0,0x12,0x84,0xC0, +0x3E,0x67,0xEF,0xCC,0x49,0xFF,0xE9,0x33,0x3E,0x67,0xEF,0xCB,0x49,0xFF,0xFD,0xE3, +0x49,0xFF,0xFB,0x7E,0x49,0xFF,0xFD,0xE9,0x84,0x00,0x49,0xFF,0xFF,0xD5,0xFC,0x80, +0x3C,0x0D,0xFD,0xC3,0x5A,0x00,0x01,0xED,0x49,0x00,0x12,0x37,0x2E,0x07,0xEF,0xC8, +0xC0,0xE4,0xD5,0xE6,0xFC,0x00,0x84,0x01,0x49,0xFF,0xFF,0xC6,0x2E,0x07,0xEF,0xCB, +0xC8,0x05,0x3C,0x0D,0xFD,0xC3,0x5A,0x08,0x01,0xFB,0x84,0xC0,0x3E,0x67,0xEF,0xCC, +0x49,0xFF,0xE9,0x0D,0x84,0x00,0x3E,0x67,0xEF,0xCB,0x49,0xFF,0xFF,0xB5,0xFC,0x80, +0xFC,0x01,0xB6,0x1F,0x2E,0x07,0xF7,0x11,0x49,0xFF,0xE1,0x74,0xF0,0x81,0xB4,0x1F, +0x49,0xFF,0xDB,0x9C,0x44,0x10,0x02,0x40,0xB4,0x1F,0x49,0xFF,0xE1,0x76,0x84,0x02, +0x49,0xFF,0xFD,0x60,0x49,0xFF,0xFF,0xD8,0xF0,0x01,0x44,0x10,0x02,0x40,0xB4,0x5F, +0x49,0xFF,0xF0,0xE9,0x84,0x01,0x49,0xFF,0xFD,0x55,0x84,0x01,0x49,0xFF,0xFF,0x94, +0xFC,0x81,0xFC,0x49,0x2E,0x67,0xF7,0x12,0x84,0x00,0x49,0xFF,0xFE,0xAE,0x84,0x02, +0x49,0xFF,0xD9,0xF4,0x80,0x06,0x84,0x20,0x84,0x41,0x49,0xFF,0xEF,0x20,0x84,0x04, +0x49,0xFF,0xFD,0x9D,0xB1,0x86,0x44,0x12,0x02,0xAC,0x80,0x06,0x3A,0x20,0x94,0x04, +0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x00, +0x3A,0x20,0x14,0x20,0x44,0x02,0x02,0xDC,0x80,0x3F,0x3A,0x20,0x14,0x04,0x3A,0x20, +0x94,0x24,0x3A,0x20,0x0C,0x00,0x3A,0x20,0x8C,0x20,0x80,0x1F,0x49,0xFF,0xDE,0x0A, +0x80,0x06,0x49,0xFF,0xDD,0xD1,0x84,0x00,0x3E,0x07,0xEF,0xCA,0x84,0x01,0x80,0xFF, +0x49,0xFF,0xFF,0x5A,0x49,0xFF,0xD7,0x62,0x2E,0x07,0xEF,0xCA,0xC0,0x2D,0x84,0x00, +0x3E,0x07,0xEF,0xCA,0x84,0x00,0x49,0xFF,0xFF,0x4F,0x49,0xFF,0xFF,0x63,0x84,0x00, +0x49,0xFF,0xFD,0x65,0x44,0x12,0x02,0x18,0x80,0x06,0x3A,0x20,0x94,0x04,0x3A,0x20, +0x14,0x24,0x3A,0x20,0x94,0x04,0x3A,0x20,0x14,0x24,0x3A,0x20,0x94,0x00,0x3A,0x20, +0x14,0x20,0x44,0x02,0x01,0xC4,0x80,0x27,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24, +0x3A,0x20,0x0C,0x00,0x3A,0x20,0x8C,0x20,0x80,0x07,0x49,0xFF,0xDD,0xD3,0x80,0x06, +0x49,0xFF,0xDD,0x9A,0xFC,0xC9,0x3C,0x0D,0xFD,0xC3,0x5A,0x00,0x01,0xD2,0x49,0x00, +0x11,0x8C,0x2E,0x07,0xEF,0xC8,0xC0,0xC9,0xD5,0xCB,0xFC,0x00,0x2E,0x07,0xF7,0x12, +0x49,0xFF,0xF1,0xA4,0x84,0x00,0x49,0xFF,0xFC,0x7E,0x84,0x20,0x2E,0x27,0xF7,0x10, +0x2E,0x07,0xF7,0x12,0x49,0xFF,0xFE,0x9E,0x84,0x00,0x49,0xFF,0xFF,0x5B,0x2E,0x17, +0xF7,0x11,0x2E,0x27,0xF7,0x10,0x2E,0x07,0xF7,0x12,0x49,0xFF,0xFE,0x93,0x2E,0x07, +0xF7,0x12,0x49,0xFF,0xF1,0x8B,0x2E,0x07,0xF7,0x12,0x49,0xFF,0xF2,0x16,0x49,0xFF, +0xF2,0x7C,0xC8,0x09,0x2E,0x07,0xF7,0x12,0x49,0xFF,0xFC,0x47,0x2E,0x07,0xF7,0x12, +0x49,0xFF,0xFC,0xF0,0x84,0x01,0x49,0xFF,0xFC,0xAD,0x49,0xFF,0xF1,0x2E,0x49,0xFF, +0xFC,0xFC,0x84,0x01,0x49,0xFF,0xFE,0xE8,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xDE,0x30, +0xFC,0x80,0x46,0x20,0x00,0x82,0x96,0x67,0xA1,0x54,0x44,0x3F,0xFF,0xC1,0x95,0x09, +0xFE,0xEE,0xFE,0xE7,0xA8,0xD4,0xA1,0x54,0x44,0x3F,0xE0,0xFF,0x40,0x40,0xA0,0x08, +0xFE,0xEE,0xFE,0xE7,0xA8,0xD4,0xA0,0xD4,0x46,0x4F,0xFC,0x1F,0x50,0x42,0x0F,0xFF, +0x40,0x50,0xC4,0x08,0xFE,0xE6,0xFE,0xEF,0xA8,0xD4,0xA1,0x14,0x46,0x3E,0x0F,0xFF, +0x50,0x31,0x8F,0xFF,0xFE,0xE6,0x40,0x10,0xE0,0x08,0xFE,0x5F,0xA8,0x54,0xA1,0x14, +0x96,0x06,0x44,0x1F,0xFF,0xBF,0x94,0xC6,0xFE,0x66,0xFE,0x5F,0xA8,0x54,0xA1,0x14, +0x44,0x1F,0xDF,0xFF,0x40,0x30,0x34,0x08,0xFE,0x66,0xFE,0x5F,0xA8,0x54,0xA0,0x54, +0x46,0x3F,0xFB,0xFF,0x50,0x31,0x8F,0xFF,0x40,0x40,0x58,0x08,0xFE,0x5E,0xFE,0x67, +0xA8,0x54,0xA0,0xD4,0x46,0x1D,0xFF,0xFF,0x50,0x10,0x8F,0xFF,0x40,0x00,0x74,0x08, +0xFE,0x5E,0xFE,0x0F,0xA8,0x14,0xDD,0x9E,0xFC,0x00,0x84,0xC0,0x3E,0x67,0xEF,0xCC, +0x49,0xFF,0xE7,0xE5,0x3E,0x67,0xEF,0xCB,0x2E,0x07,0xEF,0xCB,0xC0,0x05,0x84,0x00, +0x3E,0x07,0xEF,0xCB,0xFC,0x80,0x3C,0x0D,0xFD,0xC3,0x5A,0x00,0x01,0xFA,0x49,0x00, +0x10,0xEC,0x2E,0x07,0xEF,0xC8,0xC0,0xF1,0xD5,0xF3,0xFC,0x00,0x49,0xFF,0xFC,0x8D, +0x84,0x01,0x49,0xFF,0xFE,0x79,0x49,0xFF,0xFF,0xE1,0x49,0xFF,0xFC,0x7C,0x49,0xFF, +0xFC,0x57,0x84,0x00,0x49,0xFF,0xFE,0x70,0xFC,0x80,0xFC,0x40,0x84,0xC0,0x44,0x90, +0x20,0x00,0x84,0xA0,0x98,0xEE,0x96,0xD9,0x94,0xD9,0x40,0xA0,0x0C,0x00,0x99,0xCB, +0x22,0x45,0x00,0x00,0x22,0x83,0x80,0x00,0x8A,0x88,0x4E,0x44,0x00,0x03,0xFF,0x22, +0x88,0x62,0x22,0x81,0x80,0x00,0xE1,0x04,0xE8,0x02,0xAD,0x18,0x22,0xF5,0x00,0x00, +0x5E,0xF7,0xBF,0xC1,0xE8,0x06,0x22,0xF3,0x80,0x00,0x5E,0xF7,0xBF,0xC1,0xE9,0x03, +0x12,0x91,0x80,0x00,0x8C,0xA1,0x97,0x69,0x5A,0x58,0x12,0xDE,0x8C,0xD2,0x97,0xB1, +0x5A,0x6A,0x40,0xD9,0xFC,0xC0,0xFC,0x00,0x44,0x00,0x01,0x00,0x84,0x20,0x49,0xFF, +0xDA,0x7D,0x84,0x21,0x44,0x00,0x01,0x00,0x49,0xFF,0xDA,0x78,0x84,0x08,0x49,0xFF, +0xE3,0xEF,0x44,0x00,0x01,0x00,0x84,0x20,0x49,0xFF,0xDA,0x70,0x44,0x00,0x00,0x51, +0x3E,0x07,0xEF,0x14,0xFC,0x80,0xFC,0x40,0x44,0x74,0x2C,0x14,0x84,0xCF,0xB5,0x27, +0x40,0x94,0x98,0x02,0x10,0x90,0x00,0x00,0xB4,0x07,0x92,0x08,0xFE,0x36,0xAE,0x08, +0xB4,0x27,0x84,0x01,0x92,0x2C,0xFE,0x46,0xAE,0x50,0x44,0x14,0x24,0x14,0x44,0x24, +0x2C,0x1C,0xB4,0x21,0xFF,0x8E,0xAF,0x98,0xB4,0x22,0x92,0x32,0xFE,0x46,0xAE,0x60, +0xB4,0x22,0x92,0x33,0xFE,0x46,0xAE,0x68,0xB4,0x22,0x92,0x36,0xFE,0x0E,0xF1,0x08, +0xAE,0x08,0xFC,0xC0,0xFC,0x41,0x81,0x21,0xF3,0x81,0x5A,0x08,0xFF,0x05,0x5A,0x18, +0xFF,0x50,0xFC,0xC1,0x5A,0x08,0xFE,0x49,0x5A,0x18,0xFE,0x47,0x44,0x34,0x2C,0x14, +0x96,0x86,0x40,0x01,0x30,0x08,0xB4,0x43,0x44,0x1F,0xEF,0xFF,0xFE,0x8E,0xFE,0x87, +0xB6,0x43,0x84,0x41,0x80,0x62,0x44,0x14,0x24,0x14,0x44,0x04,0x28,0x14,0x80,0xE4, +0x80,0xC5,0x49,0xFF,0xDA,0xB7,0xF0,0x01,0x49,0xFF,0xE5,0xC7,0x44,0x04,0x24,0x14, +0x84,0x21,0x49,0xFF,0xDA,0x84,0x44,0x24,0x2C,0x1C,0x97,0xC6,0x40,0x43,0xC8,0x08, +0xB4,0xE2,0x44,0x0B,0xFF,0xFF,0xFF,0xC6,0xFF,0xE7,0xB6,0xE2,0x97,0x86,0xB4,0x02, +0x40,0x53,0x4C,0x08,0x46,0x6F,0xFF,0x7F,0x50,0x63,0x0F,0xFF,0xFF,0x86,0xFF,0xAF, +0xB6,0xC2,0x00,0x0F,0x80,0x28,0xB4,0x62,0x96,0x06,0x40,0x10,0x58,0x08,0x46,0x0F, +0xFB,0xFF,0x50,0x00,0x0F,0xFF,0xFE,0x1E,0xFE,0x0F,0xB6,0x02,0x44,0x14,0x24,0x1C, +0x44,0x04,0x28,0x1C,0xD5,0x0C,0x49,0xFF,0xE5,0x98,0x5A,0x90,0xFF,0x05,0x80,0x09, +0x49,0xFF,0xE5,0x9C,0x44,0x04,0x28,0x14,0x44,0x14,0x24,0x14,0x84,0x41,0x80,0x62, +0x49,0xFF,0xDA,0x78,0xFC,0xC1,0xFC,0x41,0xB6,0x1F,0x80,0xC1,0xF2,0x81,0x81,0x24, +0x80,0xE5,0x5A,0x30,0xFF,0x06,0x80,0x02,0x84,0x20,0x49,0xFF,0xE9,0xB5,0x5A,0x98, +0x0F,0x04,0x5A,0x70,0x01,0x0C,0x46,0x10,0x00,0x81,0x83,0x81,0xB8,0x33,0x58,0x00, +0x02,0x00,0xB8,0xB3,0x84,0x00,0x49,0xFF,0xFC,0xA8,0x00,0x0F,0x80,0x28,0x5A,0x08, +0x0F,0x06,0x00,0x0F,0x80,0x2C,0x5A,0x00,0x0F,0x04,0x49,0x00,0x0E,0xEF,0x44,0x00, +0xFF,0xFF,0x4C,0x60,0x00,0x0C,0xB4,0x1F,0x80,0x26,0x49,0xFF,0xDE,0x0C,0x50,0x13, +0x00,0x42,0xB4,0x1F,0x96,0x49,0x49,0xFF,0xFE,0x8A,0xFC,0xC1,0xFC,0x42,0x80,0xC1, +0xF0,0x81,0x80,0xE2,0xF3,0x82,0x80,0x24,0x01,0xCF,0x80,0x34,0x00,0x8F,0x80,0x38, +0x00,0xAF,0x80,0x3C,0x04,0x9F,0x80,0x10,0x5A,0x40,0xFF,0x07,0x80,0x03,0xF5,0x83, +0x49,0xFF,0xE9,0x7A,0xF5,0x03,0x5A,0x50,0x0F,0x06,0xF0,0x02,0x80,0x25,0x49,0xFF, +0xDE,0x3E,0x5B,0xC0,0x01,0x07,0x80,0x1C,0x00,0x1F,0x80,0x30,0x49,0xFF,0xD4,0xFC, +0x5A,0x80,0x0F,0x05,0x80,0x08,0x49,0xFF,0xD6,0xF3,0x5A,0xA0,0x0F,0x05,0x80,0x0A, +0x49,0xFF,0xD7,0x21,0x44,0x00,0xFF,0xFF,0x5A,0x90,0x01,0x05,0x4C,0x60,0x40,0x05, +0xFC,0xC2,0x4C,0x60,0x00,0x2F,0xF0,0x01,0x80,0x26,0x49,0xFF,0xDD,0xCC,0x50,0x13, +0x00,0x42,0xF0,0x01,0x96,0x49,0x49,0xFF,0xFE,0x4A,0x5A,0x70,0xFF,0x0C,0xE6,0xF5, +0xE9,0x02,0xFA,0xE3,0x44,0x12,0x06,0x1C,0xF0,0x01,0x38,0x10,0x9D,0x01,0x49,0xFF, +0xFE,0x3E,0x5A,0x98,0x01,0x1A,0x49,0xFF,0xFB,0x30,0x49,0xFF,0xFD,0x8C,0x49,0xFF, +0xFC,0x33,0xF0,0x81,0x49,0xFF,0xFC,0x33,0xF0,0x82,0x49,0xFF,0xFC,0x33,0xF0,0x83, +0xF1,0x02,0xF0,0x01,0xF2,0x03,0x49,0xFF,0xFC,0x9D,0x49,0xFF,0xFB,0x14,0xFC,0xC2, +0x5A,0x78,0xFF,0xDF,0xD5,0xE9,0xFC,0xC2,0xFC,0x03,0xF1,0x81,0xB1,0x03,0x44,0x12, +0x03,0x04,0x80,0xA4,0x3A,0x20,0x8C,0x04,0x3A,0x22,0x8C,0x24,0xA4,0x48,0x9E,0xC1, +0xAC,0x68,0x92,0x28,0xE0,0x23,0x00,0x2F,0x80,0x0C,0xE8,0x03,0x8C,0x21,0xD5,0x04, +0xE0,0x40,0xE9,0x03,0x9C,0x51,0x96,0x08,0x84,0x40,0x38,0x52,0x08,0x00,0x96,0x50, +0x8C,0xA1,0xD8,0x06,0x80,0x01,0xF1,0x01,0x49,0xFF,0xDD,0x35,0xFC,0x83,0x8C,0x41, +0x5A,0x28,0x0A,0xF5,0xFC,0x83,0xFC,0x49,0x49,0xFF,0xFA,0xD6,0x2E,0x77,0xF0,0xEB, +0xF0,0x87,0x2E,0x07,0xF0,0xEC,0x40,0x73,0xA0,0x08,0xFF,0xC7,0x2E,0x07,0xF0,0xED, +0x2E,0x67,0xF0,0xF0,0xF0,0x88,0x2E,0x07,0xF0,0xEE,0x2E,0x87,0xF0,0xF2,0x54,0x90, +0x00,0x01,0x2E,0x07,0xF0,0xEF,0x2E,0xA7,0xF0,0xF3,0xF0,0x89,0x54,0x03,0x00,0x0F, +0x92,0xC4,0xF0,0x8A,0x54,0x03,0x00,0x01,0xF0,0x8B,0x2E,0x07,0xF0,0xF1,0x54,0x10, +0x00,0x0F,0x40,0x60,0x10,0x09,0x84,0x1F,0x10,0x0F,0x80,0x41,0x10,0x0F,0x80,0x42, +0x10,0x0F,0x80,0x43,0x10,0x0F,0x80,0x44,0x10,0x0F,0x80,0x45,0x10,0x0F,0x80,0x46, +0x10,0x0F,0x80,0x47,0x84,0x00,0x3C,0x0B,0xF8,0x6F,0x3E,0x07,0xF0,0xDC,0xF0,0x08, +0xF1,0x8C,0x5A,0x00,0xFF,0x16,0x46,0x00,0x00,0x81,0x04,0x10,0x00,0x23,0x04,0x00, +0x00,0x23,0x96,0x5F,0x92,0x0C,0x96,0x1F,0xF1,0x8E,0xF0,0x8D,0x84,0x20,0xF0,0x08, +0x49,0xFF,0xFF,0x8C,0xF0,0x08,0x84,0x23,0x49,0xFF,0xFF,0x88,0xD5,0x04,0x84,0x00, +0xF0,0x8D,0xF0,0x8E,0x5A,0x88,0xFF,0x04,0x5A,0xA0,0xFF,0x12,0x50,0x0F,0x80,0x47, +0xB6,0x1F,0x50,0x1F,0x80,0x42,0x50,0x0F,0x80,0x41,0x50,0x2F,0x80,0x43,0xB0,0xD1, +0x50,0x4F,0x80,0x45,0x50,0x5F,0x80,0x46,0x49,0xFF,0xFE,0x4F,0x84,0x41,0x80,0x08, +0x80,0x2A,0x80,0xA2,0xB6,0x5F,0x84,0x65,0x84,0x80,0x49,0xFF,0xFE,0x6D,0xF0,0x0B, +0x85,0x40,0xF0,0x81,0xF0,0x0C,0x80,0x27,0xF0,0x82,0x44,0x20,0x00,0xFF,0x80,0x0A, +0x80,0x6A,0xF4,0x09,0xF5,0x0A,0xB7,0x5F,0xF6,0x83,0x14,0xAF,0x80,0x04,0x49,0xFF, +0xFE,0xEF,0xF0,0x0B,0x84,0x63,0xF0,0x81,0xF0,0x0C,0xF5,0x0A,0xB6,0x7F,0x80,0x27, +0x44,0x20,0x00,0xFF,0xF4,0x09,0xF0,0x82,0xF6,0x83,0x84,0x05,0x14,0x9F,0x80,0x04, +0x49,0xFF,0xFE,0xDE,0x49,0xFF,0xFA,0x4F,0x80,0x0A,0x49,0xFF,0xFC,0x45,0x46,0x80, +0x0F,0xC0,0x46,0x50,0x00,0x82,0x44,0xF3,0xF0,0x00,0x41,0xE5,0x10,0x08,0x40,0x45, +0x00,0x13,0x84,0xE0,0xB4,0x05,0x40,0x13,0xF8,0x00,0x92,0x0B,0x40,0x00,0x2C,0x08, +0xFE,0x0F,0xB6,0x05,0xA0,0xE9,0xA0,0x69,0x96,0xD8,0x54,0x91,0x80,0x20,0x54,0x10, +0x8F,0xC0,0x92,0x26,0x40,0x94,0x88,0x08,0x96,0xE7,0xA0,0xA9,0x40,0x34,0x8C,0x04, +0x54,0x90,0x80,0x1F,0x96,0x6E,0x94,0x4A,0xA0,0x29,0x40,0x94,0x84,0x04,0x40,0x21, +0x3C,0x02,0x92,0x4C,0x40,0x94,0xA0,0x08,0x40,0x91,0xA4,0x04,0x40,0x00,0x20,0x02, +0x54,0x31,0x00,0x20,0x92,0x12,0xF1,0x07,0x94,0xDA,0x96,0xA7,0xFE,0x9F,0x54,0x30, +0x00,0x1F,0x96,0x2E,0x94,0x02,0x38,0x90,0x91,0x09,0xFE,0xC7,0x50,0x12,0x00,0x12, +0xF0,0x07,0x96,0x49,0x40,0x31,0xA0,0x08,0xFE,0x9F,0x50,0x42,0x00,0x24,0x8C,0xE1, +0x38,0x20,0x05,0x09,0x97,0x21,0x5A,0x78,0x10,0xBF,0x8D,0x41,0x5A,0xA8,0x09,0xB7, +0xFB,0x41,0x44,0x90,0x02,0x40,0x46,0x30,0x00,0x82,0x80,0xEA,0x81,0x09,0x84,0xA0, +0x55,0xE4,0x87,0xFF,0xB4,0x23,0x40,0x02,0xF8,0x00,0x92,0x2B,0x40,0x10,0xAC,0x08, +0x54,0x00,0x07,0xFF,0xFE,0x0F,0xB6,0x03,0xB4,0x03,0x8C,0xA1,0x92,0x0B,0x40,0x00, +0x2C,0x08,0x40,0x04,0x00,0x04,0xB6,0x03,0xA1,0x19,0xA0,0x59,0xA0,0x99,0x54,0x10, +0x8F,0xC0,0x40,0x00,0x98,0x09,0xF0,0x8F,0x44,0x03,0xF0,0x00,0xFE,0x86,0xA0,0x19, +0x97,0x20,0x46,0x10,0x0F,0xC0,0xFE,0x0E,0x54,0xF2,0x00,0x20,0xF1,0x0F,0x40,0xF7, +0x88,0x08,0x97,0x27,0x40,0x47,0x90,0x04,0x54,0xF0,0x80,0x1F,0x96,0x6E,0x94,0x4A, +0x40,0xF7,0x84,0x04,0xF1,0x07,0x40,0xF7,0xA0,0x08,0x92,0x4C,0x40,0x42,0x3C,0x04, +0x38,0x40,0x9D,0x09,0x54,0x41,0x00,0x20,0x92,0x12,0x95,0x22,0x96,0xA7,0xFE,0xA7, +0x54,0x40,0x00,0x1F,0x96,0x2E,0x94,0x02,0x50,0x13,0x80,0x12,0xFF,0x07,0xF0,0x07, +0x96,0x49,0x40,0x42,0x20,0x08,0xFE,0xA7,0x50,0x73,0x80,0x24,0x54,0x52,0x87,0xFF, +0x38,0x20,0x05,0x09,0x8D,0x01,0x97,0xF9,0x5A,0x58,0x10,0xAE,0x50,0x05,0x7F,0xFF, +0x8D,0x30,0x40,0xA0,0x00,0x13,0x5A,0x9A,0xD0,0xA2,0x49,0xFF,0xEC,0x35,0x49,0xFF, +0xEC,0x5A,0x2E,0x07,0xEF,0xBD,0xC0,0x04,0x49,0xFF,0xEA,0x5D,0xD5,0x03,0x44,0x0F, +0xED,0xED,0x44,0x44,0x80,0x10,0x3C,0x0B,0xF8,0x6F,0xB4,0xA4,0xB4,0x04,0xB4,0x24, +0x40,0x30,0x58,0x09,0xB4,0x04,0x92,0xB6,0x92,0x14,0x40,0x20,0x88,0x09,0x54,0x42, +0x80,0x08,0x54,0x10,0x00,0x01,0x54,0x01,0x80,0x04,0xFE,0x27,0xFE,0x0F,0x54,0x11, +0x00,0x02,0xFE,0x0F,0x3E,0x07,0xF0,0xDC,0xF0,0x08,0x5A,0x00,0xFF,0x0A,0xF0,0x0E, +0x84,0x20,0x49,0xFF,0xDB,0xB0,0xF0,0x0D,0x84,0x23,0x49,0xFF,0xDB,0xAC,0x00,0x7F, +0x80,0x47,0x00,0x0F,0x80,0x41,0x00,0x1F,0x80,0x42,0x00,0x2F,0x80,0x43,0x00,0x3F, +0x80,0x44,0x00,0x4F,0x80,0x45,0x00,0x5F,0x80,0x46,0xB6,0xFF,0x49,0xFF,0xFD,0x4C, +0xF0,0x0C,0xF3,0x09,0xB6,0x1F,0x84,0x00,0x80,0x40,0xF4,0x0A,0xF5,0x0B,0xF6,0x81, +0x44,0x10,0x00,0xF0,0x49,0xFF,0xFD,0xA1,0xF0,0x0C,0x44,0x10,0x00,0xF0,0xB6,0x1F, +0x84,0x43,0x84,0x05,0xF3,0x09,0xF4,0x0A,0xF5,0x0B,0xF6,0x81,0x49,0xFF,0xFD,0x95, +0x49,0xFF,0xFC,0xF3,0xFC,0xC9,0xFC,0x41,0x81,0x00,0x81,0x23,0xF5,0x81,0x80,0xC2, +0x81,0x44,0xB6,0x3F,0x2E,0x77,0xEA,0x5E,0x49,0xFF,0xF9,0x1B,0x83,0x80,0x46,0x00, +0x00,0x80,0xB4,0x3F,0x14,0x80,0x00,0x47,0x14,0x80,0x00,0x49,0x84,0x01,0x49,0xFF, +0xDB,0xB2,0x84,0x04,0xB4,0x3F,0x49,0xFF,0xDB,0xAE,0x49,0xFF,0xF9,0x26,0x49,0xFF, +0xFC,0x7D,0x49,0xFF,0xF9,0x18,0x80,0x1C,0x80,0x29,0x84,0x40,0x49,0xFF,0xEC,0x39, +0xF5,0x01,0xCD,0x14,0x93,0x21,0x96,0x69,0xE2,0x29,0xE8,0x0F,0x40,0x22,0x9C,0x16, +0xC8,0x04,0xC1,0x03,0x8C,0xC2,0x97,0xB1,0x42,0x03,0x1C,0x73,0x38,0x1E,0x15,0x11, +0x38,0x15,0x01,0x09,0x8C,0xA1,0xD5,0xF0,0xFC,0xC1,0x5A,0x58,0x01,0x0F,0x95,0xB1, +0x89,0x46,0x84,0x00,0x96,0x41,0xE2,0x29,0xE8,0x07,0x38,0x1E,0x01,0x11,0x38,0x15, +0x01,0x09,0x8C,0x01,0xD5,0xF8,0xFC,0xC1,0x5A,0x58,0x02,0x1B,0x94,0xF9,0x84,0x40, +0x96,0x51,0xE2,0x29,0xE8,0x15,0x40,0x41,0x1C,0x16,0xC8,0x04,0xC1,0x03,0x8C,0xC1, +0x97,0xB1,0x40,0x51,0x0C,0x96,0xCC,0x04,0xC1,0x03,0x8C,0xC6,0x97,0xB1,0x42,0x03, +0x1C,0x73,0x38,0x1E,0x09,0x11,0x38,0x15,0x01,0x09,0x8C,0x41,0xD5,0xEA,0xFC,0xC1, +0xFC,0x40,0x51,0xFF,0xF5,0xF0,0x49,0xFF,0xF8,0xBF,0xF0,0x87,0x84,0x20,0x44,0x20, +0x04,0x80,0x50,0x0F,0x81,0x10,0x49,0x00,0x13,0x05,0x44,0x02,0x03,0x04,0xB0,0x57, +0x3A,0x20,0x0C,0x04,0x3A,0x20,0x8C,0x24,0xA4,0x00,0xAC,0x08,0x00,0x0F,0x80,0x5C, +0x44,0x12,0x03,0x10,0x8C,0x01,0x96,0x00,0xF0,0x8B,0xB0,0x1A,0x3A,0x20,0x90,0x00, +0x3A,0x20,0x10,0x20,0x84,0x20,0x84,0x4C,0xB0,0x1D,0x49,0x00,0x12,0xEB,0x2E,0x07, +0xF0,0xEB,0x2E,0x17,0xF0,0xEC,0x40,0x00,0x20,0x08,0x2E,0x77,0xF0,0xEF,0x40,0x80, +0x04,0x04,0x2E,0x07,0xF0,0xEE,0x2E,0x67,0xF0,0xED,0xF0,0x86,0x54,0x03,0x80,0x0F, +0xF0,0x88,0x2E,0x07,0xF0,0xF1,0x2E,0xA7,0xF0,0xF0,0xF0,0x8C,0x2E,0x07,0xF0,0xF2, +0x50,0xFF,0x80,0xB0,0xF0,0x8D,0x2E,0x07,0xF0,0xF3,0x92,0xE4,0x54,0x10,0x00,0x0F, +0x92,0x04,0x96,0x06,0xF0,0x8F,0x2E,0x07,0xF0,0xF4,0xF1,0x8E,0x54,0x10,0x00,0x0F, +0x92,0x04,0xF0,0x91,0x84,0x1F,0xF1,0x90,0x10,0x0F,0x80,0x55,0x10,0x0F,0x80,0x56, +0x10,0x0F,0x80,0x57,0x10,0x0F,0x80,0x58,0x10,0x0F,0x80,0x59,0x10,0x0F,0x80,0x5A, +0x10,0x0F,0x80,0x5B,0x2E,0x17,0xEA,0x5F,0x2E,0x07,0xEA,0x5E,0xFE,0x0C,0xF0,0x89, +0x44,0x02,0x03,0x1C,0xB0,0x60,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24,0x3A,0x20, +0x0C,0x00,0x44,0x02,0x03,0x34,0x3A,0x20,0x8C,0x20,0x80,0x2F,0x3A,0x20,0x14,0x04, +0x3A,0x20,0x94,0x24,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24,0x3A,0x20,0x14,0x00, +0x44,0x02,0x03,0x64,0x3A,0x20,0x94,0x20,0xB0,0x66,0x3A,0x20,0x14,0x04,0x3A,0x20, +0x94,0x24,0x3A,0x20,0x0C,0x00,0x50,0xFF,0x80,0xE0,0x44,0x02,0x03,0x7C,0x3A,0x20, +0x8C,0x20,0x80,0x2F,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24,0x3A,0x20,0x14,0x04, +0x3A,0x20,0x94,0x24,0x3A,0x20,0x14,0x00,0x3A,0x20,0x94,0x20,0xF5,0x07,0x84,0x21, +0x50,0x02,0x84,0x80,0x1A,0x12,0x80,0x01,0xD8,0xFE,0x84,0x00,0x3C,0x0B,0xF8,0x6F, +0x3E,0x07,0xF0,0xDC,0xB0,0x1A,0x49,0xFF,0xD2,0xDC,0xB0,0x1D,0x49,0xFF,0xD2,0xED, +0x46,0x30,0x00,0x80,0x84,0x10,0x04,0x11,0x80,0x70,0x46,0x90,0x00,0x81,0xFE,0x0E, +0x58,0x00,0x00,0x0B,0x14,0x01,0x80,0x70,0xB0,0x2C,0xF3,0x8A,0x49,0xFF,0xD8,0x7C, +0xB0,0x20,0x49,0xFF,0xD8,0xAF,0x84,0x03,0x49,0xFF,0xF7,0xC4,0x84,0x00,0x49,0xFF, +0xE3,0x43,0x44,0x17,0x20,0x1C,0xB4,0x01,0x92,0x0C,0x40,0x00,0x30,0x08,0xB6,0x01, +0x44,0x17,0x20,0x24,0xB4,0x01,0x92,0x0C,0x40,0x00,0x30,0x08,0xB6,0x01,0x04,0x24, +0x80,0x3A,0x46,0x0F,0xF8,0xFF,0x50,0x00,0x0F,0x8F,0xFE,0x16,0x46,0x20,0x05,0x00, +0x50,0x21,0x00,0x40,0xFE,0x17,0x14,0x04,0x80,0x3A,0x04,0x24,0x80,0x3B,0x84,0x18, +0xFE,0x16,0x58,0x00,0x00,0x04,0x14,0x04,0x80,0x3B,0x84,0x00,0x49,0xFF,0xD3,0x88, +0x84,0x03,0x49,0xFF,0xD3,0xB8,0x04,0x24,0x80,0x38,0x46,0x00,0xCC,0x0C,0x50,0x00, +0x0F,0xF3,0xFE,0x16,0x46,0x2A,0x22,0xA2,0xF3,0x0A,0x8C,0x48,0xFE,0x17,0x14,0x04, +0x80,0x38,0x84,0x1F,0x14,0x01,0x80,0x47,0x14,0x01,0x80,0x49,0x04,0x04,0x83,0x97, +0x84,0x41,0x58,0x00,0x02,0x00,0x80,0x62,0x50,0x14,0x86,0x5C,0x14,0x04,0x83,0x97, +0x50,0x04,0x8A,0x5C,0x49,0xFF,0xD6,0x8E,0x84,0x01,0x49,0xFF,0xD9,0x5A,0x46,0x10, +0x00,0x83,0x46,0x0F,0x3F,0xFF,0x04,0x20,0x80,0xEE,0x50,0x00,0x0F,0xFF,0xFE,0x16, +0x44,0x24,0x2C,0x14,0x14,0x00,0x80,0xEE,0xB4,0x02,0x44,0x14,0x2C,0x1C,0xF0,0x92, +0xB4,0x01,0xB4,0x62,0xF0,0x93,0x44,0x0F,0xE0,0xF0,0xFE,0x1E,0x58,0x00,0x00,0x01, +0xB6,0x02,0xB4,0x41,0x46,0x0F,0xFB,0x3F,0x50,0x00,0x0F,0xFF,0xFE,0x16,0x46,0x20, +0x00,0xC0,0xFE,0x17,0x84,0x41,0xB6,0x01,0x80,0x62,0x44,0x04,0x28,0x14,0x44,0x14, +0x24,0x14,0x49,0xFF,0xD6,0x5F,0x84,0x41,0x80,0x62,0x44,0x04,0x28,0x1C,0x44,0x14, +0x24,0x1C,0x49,0xFF,0xD6,0x57,0x84,0x01,0x84,0x20,0x49,0xFF,0xE5,0x9D,0x46,0x20, +0x00,0x82,0x46,0x0C,0x08,0x0C,0xA0,0x54,0x50,0x00,0x00,0x80,0xFE,0x0E,0x44,0x11, +0x00,0x01,0xFE,0x0F,0xA8,0x14,0x44,0x00,0xFF,0xFF,0x4C,0x80,0x00,0x11,0xF5,0x06, +0x40,0x63,0x20,0x08,0xFF,0x77,0xF5,0x86,0xD0,0x0A,0xE2,0xA8,0xE9,0x04,0x14,0x8F, +0x80,0x06,0x81,0x05,0x50,0x64,0x00,0x42,0x97,0xB1,0xD5,0x08,0x44,0x00,0x00,0x40, +0x44,0x60,0x01,0x00,0xF0,0x86,0x44,0x80,0x00,0xC0,0x84,0x01,0x80,0x28,0x49,0xFF, +0xD9,0xEA,0x80,0x26,0x84,0x01,0x49,0xFF,0xFA,0x6A,0x84,0x00,0x84,0x21,0x49,0xFF, +0xD9,0x9A,0x80,0x26,0x84,0x04,0x46,0x60,0x00,0x81,0x49,0xFF,0xFA,0x60,0x84,0x01, +0x84,0x26,0x49,0xFF,0xE5,0x31,0x04,0x13,0x03,0x97,0x46,0x0F,0x81,0xC0,0x50,0x00, +0x0F,0xFF,0xFE,0x0E,0x46,0x10,0x08,0x2D,0xFE,0x0F,0x14,0x03,0x03,0x97,0x04,0x13, +0x03,0x95,0x44,0x0F,0xF0,0x3F,0xFE,0x0E,0x58,0x00,0x00,0x40,0x14,0x03,0x03,0x95, +0x04,0x13,0x03,0x99,0x46,0x0F,0xFC,0x0F,0x50,0x00,0x0F,0xFF,0xFE,0x0E,0x44,0x17, +0x00,0x00,0xFE,0x0F,0x84,0x41,0x80,0x62,0x14,0x03,0x03,0x99,0x50,0x13,0x06,0x5C, +0x50,0x03,0x0A,0x5C,0x49,0xFF,0xD5,0xEE,0x84,0x41,0x80,0x62,0x50,0x03,0x0A,0x54, +0x50,0x13,0x06,0x54,0x49,0xFF,0xD5,0xE6,0x84,0x41,0x80,0x62,0x50,0x03,0x0A,0x64, +0x50,0x13,0x06,0x64,0x49,0xFF,0xD5,0xDE,0x84,0x04,0x80,0x28,0x49,0xFF,0xD9,0x9B, +0x04,0x13,0x00,0x2F,0x44,0x0F,0xC0,0xFF,0xFE,0x46,0x58,0x10,0x84,0x00,0x14,0x13, +0x00,0x2F,0x04,0x13,0x00,0x2D,0xFE,0x46,0x58,0x10,0x81,0x00,0x14,0x13,0x00,0x2D, +0x04,0x13,0x00,0x2C,0xFE,0x0E,0x58,0x00,0x2D,0x00,0x14,0x03,0x00,0x2C,0x84,0x00, +0x49,0xFF,0xE4,0x6C,0x84,0x01,0x49,0xFF,0xE4,0x75,0x84,0x01,0x84,0x20,0x84,0x44, +0x84,0x62,0x44,0x40,0x00,0x30,0xFA,0xA0,0x49,0xFF,0xD6,0x02,0x04,0x13,0x00,0x17, +0x44,0x0F,0xFE,0xFF,0xFE,0x0E,0x14,0x03,0x00,0x17,0xA0,0x36,0x58,0x00,0x00,0x01, +0xA8,0x36,0xF0,0x08,0x5A,0x00,0x0F,0x0A,0x04,0x03,0x00,0x23,0x04,0x9F,0x80,0x08, +0x92,0x04,0x96,0x1F,0xF0,0x8A,0xD5,0x04,0x84,0x00,0xF0,0x8A,0x85,0x2D,0x5A,0xA8, +0xFF,0x05,0xF0,0x0C,0x5A,0x00,0xFF,0x12,0x50,0x0F,0x80,0x5B,0xB6,0x1F,0x50,0x1F, +0x80,0x56,0x50,0x0F,0x80,0x55,0x50,0x2F,0x80,0x57,0xB0,0xD6,0x50,0x4F,0x80,0x59, +0x50,0x5F,0x80,0x5A,0x49,0xFF,0xFA,0x89,0x84,0xC1,0x80,0x0A,0xF1,0x0C,0x80,0x46, +0x84,0x65,0x84,0x80,0x80,0xA6,0xB6,0xDF,0x49,0xFF,0xFA,0xA6,0xF0,0x0F,0x85,0x40, +0xF0,0x81,0xF0,0x10,0x44,0x20,0x00,0xFF,0xF0,0x82,0xF0,0x11,0x80,0x66,0xF4,0x0D, +0xF5,0x0E,0x44,0x10,0xFF,0xFF,0xF0,0x83,0xB6,0xDF,0x80,0x06,0x14,0xAF,0x80,0x04, +0x49,0xFF,0xFB,0x26,0x80,0x0A,0x49,0xFF,0xF8,0x8F,0x46,0x10,0x00,0x80,0x46,0x05, +0x55,0x55,0x50,0x00,0x05,0x55,0x14,0x00,0x80,0x47,0x14,0x00,0x80,0x49,0x49,0xFF, +0xD5,0x8E,0x49,0xFF,0xF6,0x92,0x80,0x06,0x49,0xFF,0xF8,0x7E,0x5A,0x78,0x0F,0x1C, +0xF0,0x0B,0x84,0xE6,0x40,0x64,0x80,0x01,0xE0,0xC7,0xE9,0x1E,0x49,0xFF,0xF9,0xDE, +0x49,0xFF,0xF6,0x79,0x49,0xFF,0xD5,0xD3,0x81,0x40,0x49,0xFF,0xD5,0x78,0x4E,0xA2, +0x00,0x14,0x8C,0xE1,0x97,0xF8,0x84,0x01,0x80,0x27,0x49,0xFF,0xE4,0x55,0x49,0xFF, +0xF6,0x74,0xD5,0xEB,0xF0,0x08,0xE2,0x07,0xE8,0x03,0x9F,0xC1,0x97,0xF8,0x84,0x01, +0x80,0x27,0x49,0xFF,0xE4,0x49,0x49,0xFF,0xF6,0x5E,0x84,0x00,0x49,0xFF,0xF8,0x54, +0xF0,0x0B,0x40,0x64,0x80,0x01,0xE0,0xE6,0xE9,0x06,0x97,0xF0,0x84,0x01,0x80,0x27, +0x49,0xFF,0xE4,0x3A,0xE0,0xE6,0xE8,0x13,0x84,0x20,0x40,0x74,0x9C,0x01,0xB0,0xD7, +0x9C,0x89,0x88,0x23,0x96,0x11,0xA7,0x49,0x8C,0xA1,0xDF,0x06,0x96,0x00,0x84,0x21, +0x49,0xFF,0xD8,0x89,0xD5,0x04,0x80,0x22,0x5A,0x28,0x09,0xF3,0x49,0xFF,0xF6,0x3B, +0x46,0x65,0x55,0x55,0x84,0x01,0x49,0xFF,0xF8,0x2F,0x50,0x63,0x05,0x55,0x84,0x40, +0x80,0x06,0x80,0xA2,0x80,0x28,0xF3,0x09,0x50,0x4F,0x81,0x10,0x49,0xFF,0xFC,0xF5, +0x80,0x06,0x84,0x40,0x46,0x6A,0xAA,0xAA,0x80,0xA2,0x50,0x63,0x0A,0xAA,0xF1,0x06, +0xF3,0x09,0x50,0x4F,0x85,0x90,0x49,0xFF,0xFC,0xE8,0x80,0x06,0x80,0x28,0x84,0x41, +0xF3,0x09,0x50,0x4F,0x81,0x10,0x84,0xA0,0x49,0xFF,0xFC,0xDF,0x80,0x06,0xF1,0x06, +0x84,0x41,0xF3,0x09,0x50,0x4F,0x85,0x90,0x84,0xA0,0x49,0xFF,0xFC,0xD6,0x50,0x0F, +0x81,0x10,0x50,0x1F,0x85,0x90,0xF2,0x07,0x49,0xFF,0xF9,0x91,0xF6,0x09,0x85,0x20, +0x92,0xC2,0x97,0xB1,0x44,0x00,0x00,0xFF,0x40,0xA0,0x24,0x0C,0xFA,0x02,0x42,0x04, +0x80,0x24,0x96,0x01,0xF0,0x89,0x50,0x7F,0x81,0x10,0x80,0x0A,0x80,0x28,0xF2,0x09, +0x80,0x66,0x80,0x87,0x84,0xA1,0x49,0xFF,0xFC,0xB8,0x8D,0x28,0x80,0x0A,0xF1,0x06, +0xF2,0x09,0x80,0x66,0x50,0x4F,0x85,0x90,0x84,0xA1,0x49,0xFF,0xFC,0xAE,0x5A,0x98, +0x20,0xE3,0x80,0x07,0x50,0x1F,0x85,0x90,0xF2,0x07,0x46,0xA0,0x30,0x30,0x49,0xFF, +0xF9,0x66,0x50,0x05,0x03,0x03,0x85,0x20,0xF0,0x8B,0xF7,0x89,0xF0,0x0B,0x40,0x74, +0x80,0x13,0x40,0xA0,0x24,0x0C,0x80,0x0A,0x80,0x28,0x80,0x47,0x80,0x66,0xF4,0x09, +0x84,0xA2,0x49,0xFF,0xFC,0x92,0x8D,0x22,0x80,0x0A,0xF1,0x06,0x80,0x47,0x80,0x66, +0x50,0x4F,0x85,0x90,0x84,0xA2,0x49,0xFF,0xFC,0x88,0x5A,0x98,0x08,0xE9,0xF0,0x09, +0x50,0x1F,0x85,0x90,0xF2,0x07,0x49,0xFF,0xF9,0x42,0x2E,0x07,0xEF,0xBD,0xC0,0x04, +0x49,0xFF,0xE6,0x81,0xD5,0x03,0x44,0x0F,0xED,0xED,0x44,0x44,0x80,0x10,0x3C,0x0B, +0xF8,0x6F,0xB4,0xA4,0xB4,0x04,0xB4,0x24,0x40,0x30,0x58,0x09,0xB4,0x04,0x92,0xB6, +0x92,0x14,0x54,0x42,0x80,0x08,0x40,0x20,0x88,0x09,0x54,0x10,0x00,0x01,0x54,0x01, +0x80,0x04,0xFE,0x27,0xFE,0x0F,0x54,0x11,0x00,0x02,0xFE,0x0F,0x3E,0x07,0xF0,0xDC, +0x46,0x00,0x00,0x80,0x84,0x3F,0x14,0x10,0x00,0x47,0x14,0x10,0x00,0x49,0x04,0x10, +0x04,0x17,0x84,0xDE,0x58,0x10,0x81,0x00,0x14,0x10,0x04,0x17,0x04,0x10,0x04,0x06, +0xFE,0x76,0x14,0x10,0x04,0x06,0x84,0x01,0x49,0xFF,0xD7,0x1B,0xF1,0x12,0x44,0x04, +0x2C,0x14,0x84,0x41,0xB6,0x20,0xF1,0x13,0x44,0x04,0x2C,0x1C,0x80,0x62,0xB6,0x20, +0x44,0x04,0x28,0x14,0x44,0x14,0x24,0x14,0x49,0xFF,0xD4,0x3C,0x84,0x41,0x80,0x62, +0x44,0x04,0x28,0x1C,0x44,0x14,0x24,0x1C,0x49,0xFF,0xD4,0x34,0x46,0x00,0x00,0x82, +0xA0,0x44,0xFF,0x8E,0xA9,0x84,0xA0,0x84,0x44,0x1E,0xFF,0xFF,0xFE,0x56,0xA8,0x44, +0x84,0x01,0x49,0xFF,0xF5,0x0F,0xB0,0x38,0x49,0xFF,0xD5,0xBE,0xB0,0x26,0x49,0xFF, +0xD5,0xF1,0x84,0x01,0x49,0xFF,0xE2,0xCA,0x84,0x00,0x49,0xFF,0xE2,0xD3,0x84,0x01, +0x84,0x22,0x80,0x61,0x80,0xA0,0x84,0x44,0x44,0x40,0x00,0x3F,0x49,0xFF,0xD4,0x60, +0x84,0x20,0x84,0x01,0x49,0xFF,0xE3,0x58,0x49,0x00,0x08,0xA8,0x84,0x00,0x49,0xFF, +0xF5,0x4E,0x84,0x00,0x49,0xFF,0xF5,0x93,0x84,0x00,0x49,0xFF,0xF4,0x94,0xF0,0x08, +0x5A,0x00,0x0F,0x06,0xF0,0x0A,0x84,0x21,0x49,0xFF,0xD7,0x75,0x00,0x6F,0x80,0x5B, +0x00,0x0F,0x80,0x55,0x00,0x1F,0x80,0x56,0x00,0x2F,0x80,0x57,0x00,0x3F,0x80,0x58, +0x00,0x4F,0x80,0x59,0x00,0x5F,0x80,0x5A,0xB6,0xDF,0x49,0xFF,0xF9,0x15,0xF0,0x10, +0x44,0x10,0x00,0xF0,0xB6,0x1F,0xF0,0x11,0xF3,0x0D,0xF0,0x81,0x84,0x01,0x80,0x40, +0xF4,0x0E,0xF5,0x0F,0x49,0xFF,0xF9,0x69,0x49,0xFF,0xF8,0xC7,0x51,0xFF,0x8A,0x10, +0xFC,0xC0,0xFC,0x48,0x2E,0x07,0xF0,0xEB,0x2E,0x17,0xF0,0xEC,0x40,0x00,0x20,0x08, +0xFE,0x0F,0xF0,0x8C,0x2E,0x07,0xF0,0xED,0x2E,0x67,0xF0,0xF0,0xF0,0x87,0x2E,0x07, +0xF0,0xEE,0x2E,0x77,0xF0,0xF2,0x96,0x06,0xF0,0x8D,0x2E,0x07,0xF0,0xEF,0xF0,0x88, +0x54,0x03,0x00,0x0F,0xF0,0x89,0x2E,0x07,0xF0,0xF1,0x92,0xC4,0x54,0xA0,0x00,0x0F, +0x40,0x80,0x10,0x09,0x84,0x1F,0x10,0x0F,0x80,0x39,0x10,0x0F,0x80,0x3A,0x10,0x0F, +0x80,0x3B,0x10,0x0F,0x80,0x3C,0x10,0x0F,0x80,0x3D,0x10,0x0F,0x80,0x3E,0x10,0x0F, +0x80,0x3F,0x2E,0x07,0xF0,0xE9,0x97,0x86,0x5A,0x08,0x08,0x08,0x2E,0x17,0xF0,0xF3, +0x2E,0x97,0xF0,0xF4,0xF1,0x86,0xD5,0x0B,0x5A,0x08,0x05,0x06,0x2E,0x97,0xF0,0xF3, +0xF7,0x86,0xD5,0x05,0x44,0x90,0x00,0xFF,0x14,0x9F,0x80,0x06,0x84,0x20,0x3C,0x1B, +0xF8,0x6F,0x3E,0x17,0xF0,0xDC,0x5A,0x08,0x05,0x26,0x46,0x00,0x00,0x82,0xA0,0x44, +0x58,0x10,0x80,0x01,0xA8,0x44,0xA0,0x84,0x44,0x11,0x00,0x00,0xFE,0x57,0xA8,0x44, +0x84,0x00,0x80,0x20,0x49,0xFF,0xF7,0xC7,0x84,0x00,0x80,0x20,0x49,0xFF,0xE2,0xC4, +0x46,0x10,0x00,0x81,0x44,0x0F,0xFD,0xFF,0x04,0x20,0x80,0x33,0xFE,0x16,0x14,0x00, +0x80,0x33,0x84,0x00,0x84,0x21,0x49,0xFF,0xD7,0x82,0x84,0x00,0x80,0x20,0x49,0xFF, +0xCE,0x43,0x84,0x00,0x49,0xFF,0xD2,0x0A,0x84,0x00,0x49,0xFF,0xD2,0x1B,0xF0,0x07, +0x5A,0x00,0xFF,0x16,0x46,0x00,0x00,0x81,0x04,0x10,0x00,0x23,0x04,0x00,0x00,0x23, +0x96,0x5F,0x92,0x0C,0x96,0x1F,0xF1,0x8B,0xF0,0x8A,0x84,0x20,0xF0,0x07,0x49,0xFF, +0xF9,0x75,0xF0,0x07,0x84,0x23,0x49,0xFF,0xF9,0x71,0xD5,0x04,0x84,0x00,0xF0,0x8A, +0xF0,0x8B,0xF0,0x06,0x5A,0x08,0xFF,0x04,0x5A,0x90,0xFF,0x12,0x50,0x0F,0x80,0x3F, +0xB6,0x1F,0x50,0x1F,0x80,0x3A,0x50,0x0F,0x80,0x39,0x50,0x2F,0x80,0x3B,0xB0,0xCF, +0x50,0x4F,0x80,0x3D,0x50,0x5F,0x80,0x3E,0x49,0xFF,0xF8,0x37,0x84,0x41,0x84,0x01, +0x80,0x29,0x80,0xA2,0x85,0x20,0xB6,0x1F,0x84,0x65,0xF0,0x06,0x84,0x80,0x49,0xFF, +0xF8,0x53,0x80,0x09,0xF1,0x0C,0x44,0x20,0x00,0xFF,0x80,0x69,0xF4,0x08,0xF5,0x09, +0xB7,0x3F,0xF6,0x81,0x14,0xAF,0x80,0x02,0x14,0x8F,0x80,0x03,0x14,0x9F,0x80,0x04, +0x49,0xFF,0xF8,0xD6,0xF0,0x0D,0x84,0x63,0xF0,0x84,0xB6,0x7F,0x84,0x05,0xF6,0x81, +0x14,0xAF,0x80,0x02,0x14,0x8F,0x80,0x03,0xF1,0x0C,0x44,0x20,0x00,0xFF,0xF4,0x08, +0xF5,0x09,0x49,0xFF,0xF8,0xC5,0x2E,0x07,0xF0,0xE9,0x5A,0x08,0x08,0x2A,0x96,0x3A, +0x4E,0x05,0x00,0x04,0x84,0x01,0xD5,0x02,0x80,0x09,0x49,0xFF,0xDE,0x23,0x54,0x13, +0x80,0x01,0x46,0x00,0x00,0x82,0xC9,0x13,0xA0,0x44,0x58,0x10,0x80,0x01,0xA8,0x44, +0xA0,0x84,0x44,0x11,0x00,0x00,0xFE,0x57,0xA8,0x44,0x40,0x03,0x98,0x09,0x54,0x13, +0x80,0x3E,0x96,0x06,0x92,0x21,0x49,0xFF,0xF7,0x2E,0xD5,0x0A,0xA0,0x84,0x84,0x3E, +0xFE,0x56,0xA8,0x44,0xA0,0x84,0x44,0x1E,0xFF,0xFF,0xFE,0x56,0xA8,0x44,0x49,0xFF, +0xF7,0x86,0x2E,0x07,0xEF,0xBD,0xC0,0x04,0x49,0xFF,0xE4,0xD5,0xD5,0x03,0x44,0x0F, +0xED,0xED,0x44,0x44,0x80,0x10,0x3C,0x0B,0xF8,0x6F,0xB4,0xA4,0xB4,0x04,0xB4,0x24, +0x40,0x30,0x58,0x09,0xB4,0x04,0x92,0xB6,0x92,0x14,0x40,0x20,0x88,0x09,0x54,0x42, +0x80,0x08,0x54,0x10,0x00,0x01,0x54,0x01,0x80,0x04,0xFE,0x27,0xFE,0x0F,0x54,0x11, +0x00,0x02,0xFE,0x0F,0x3E,0x07,0xF0,0xDC,0x84,0x01,0x49,0xFF,0xD1,0x57,0x84,0x01, +0x49,0xFF,0xD1,0x68,0x2E,0x07,0xF0,0xE9,0x5A,0x08,0x08,0x08,0x84,0x00,0x49,0xFF, +0xF5,0xD3,0x49,0xFF,0xF3,0xD8,0xD5,0x19,0x5A,0x08,0x05,0x18,0x46,0x00,0x00,0x82, +0x84,0x3E,0xA0,0x84,0xFE,0x56,0xA8,0x44,0xA0,0x84,0x44,0x1E,0xFF,0xFF,0xFE,0x56, +0xA8,0x44,0x46,0x10,0x00,0x81,0x04,0x00,0x80,0x33,0x58,0x00,0x02,0x00,0x14,0x00, +0x80,0x33,0x84,0x00,0x49,0xFF,0xF4,0xD9,0x2E,0x07,0xF0,0xE9,0x5A,0x08,0x08,0x16, +0x54,0x03,0x80,0x01,0xC8,0x0C,0x46,0x00,0x00,0x82,0x84,0x3E,0xA0,0x84,0xFE,0x56, +0xA8,0x44,0xA0,0x84,0x44,0x1E,0xFF,0xFF,0xFE,0x56,0xA8,0x44,0x97,0xFA,0x4E,0x75, +0x00,0x05,0x84,0x00,0x49,0xFF,0xDD,0x9E,0xF0,0x07,0x5A,0x00,0xFF,0x0A,0xF0,0x0B, +0x84,0x20,0x49,0xFF,0xD5,0xE8,0xF0,0x0A,0x84,0x23,0x49,0xFF,0xD5,0xE4,0x00,0x7F, +0x80,0x3F,0x00,0x0F,0x80,0x39,0x00,0x1F,0x80,0x3A,0x00,0x2F,0x80,0x3B,0x00,0x3F, +0x80,0x3C,0x00,0x4F,0x80,0x3D,0x00,0x5F,0x80,0x3E,0xB6,0xFF,0x49,0xFF,0xF7,0x84, +0x84,0x00,0x80,0x40,0xF3,0x08,0xF4,0x09,0x80,0xA6,0xB7,0x5F,0x14,0x8F,0x80,0x01, +0x44,0x10,0x00,0xF0,0x49,0xFF,0xF7,0xD9,0x84,0x05,0x44,0x10,0x00,0xF0,0x84,0x43, +0xF3,0x08,0xF4,0x09,0x80,0xA6,0xB7,0x5F,0x14,0x8F,0x80,0x01,0x49,0xFF,0xF7,0xCD, +0x49,0xFF,0xF7,0x2B,0xFC,0xC8,0xFC,0x40,0x51,0xFF,0xF6,0xB0,0xB0,0x14,0x84,0x20, +0x44,0x20,0x04,0x80,0x49,0x00,0x0D,0xA6,0x84,0x20,0x44,0x20,0x04,0x80,0x50,0x0F, +0x84,0xD0,0x49,0x00,0x0D,0x9F,0x2E,0x67,0xF0,0xEC,0x2E,0x07,0xF0,0xED,0x40,0x63, +0x20,0x08,0xFE,0x37,0xF0,0x8D,0x2E,0x07,0xF0,0xEE,0x2E,0x17,0xF0,0xEB,0xF0,0x86, +0x2E,0x07,0xF0,0xEF,0x2E,0x67,0xF0,0xF3,0xF0,0x87,0x2E,0x07,0xF0,0xF0,0x85,0x4A, +0x54,0x20,0x00,0x0F,0x92,0x04,0x96,0x06,0xF0,0x89,0x2E,0x07,0xF0,0xF1,0xF2,0x88, +0x54,0x90,0x00,0x0F,0x40,0x80,0x10,0x09,0x2E,0x07,0xF0,0xF2,0xF0,0x8E,0x2E,0x07, +0xF0,0xF4,0xF0,0x8F,0x84,0x1F,0x10,0x0F,0x80,0x49,0x10,0x0F,0x80,0x4A,0x10,0x0F, +0x80,0x4B,0x10,0x0F,0x80,0x4C,0x10,0x0F,0x80,0x4D,0x10,0x0F,0x80,0x4E,0x10,0x0F, +0x80,0x4F,0x84,0x00,0x3C,0x0B,0xF8,0x6F,0x3E,0x07,0xF0,0xDC,0x5A,0x10,0xFF,0x03, +0x81,0x41,0x2E,0x07,0xF0,0xEA,0x96,0x06,0xF0,0x8B,0x5A,0x08,0x01,0x14,0x46,0x00, +0x00,0x82,0xA0,0x44,0x58,0x10,0x80,0x01,0xA8,0x44,0xA0,0x84,0x44,0x11,0x00,0x00, +0xFE,0x57,0xA8,0x44,0x84,0x00,0x80,0x20,0x49,0xFF,0xF6,0x25,0xF0,0x0B,0x49,0xFF, +0xDD,0x01,0x49,0xFF,0xF2,0xF6,0xF0,0x91,0x49,0xFF,0xF2,0xF6,0xF0,0x90,0x84,0x00, +0x49,0xFF,0xD0,0x74,0x84,0x00,0x49,0xFF,0xD0,0x85,0xF0,0x06,0x5A,0x00,0xFF,0x16, +0x46,0x00,0x00,0x81,0x04,0x10,0x00,0x23,0x04,0x00,0x00,0x23,0x96,0x5F,0x92,0x0C, +0x96,0x1F,0xF1,0x8C,0xF0,0x8A,0x84,0x20,0xF0,0x06,0x49,0xFF,0xF7,0xDF,0xF0,0x06, +0x84,0x23,0x49,0xFF,0xF7,0xDB,0xD5,0x04,0x84,0x00,0xF0,0x8A,0xF0,0x8C,0xF0,0x0E, +0x5A,0x08,0xFF,0x04,0x5A,0x60,0xFF,0x12,0x50,0x0F,0x80,0x4F,0xB6,0x1F,0x50,0x1F, +0x80,0x4A,0x50,0x0F,0x80,0x49,0x50,0x2F,0x80,0x4B,0xB0,0xD3,0x50,0x4F,0x80,0x4D, +0x50,0x5F,0x80,0x4E,0x49,0xFF,0xF6,0xA1,0x84,0xE1,0x80,0x26,0xF0,0x0E,0x80,0x47, +0x84,0x65,0x84,0x80,0x80,0xA7,0xB6,0xFF,0x49,0xFF,0xF6,0xBE,0xF1,0x09,0x84,0x00, +0x80,0x60,0xB6,0x1F,0xF1,0x81,0xF0,0x84,0xF1,0x0D,0xF2,0x0F,0xF4,0x07,0xF5,0x08, +0x14,0x9F,0x80,0x02,0x14,0x8F,0x80,0x03,0x49,0xFF,0xF7,0x42,0xF0,0x09,0x84,0x63, +0xB6,0x7F,0xF0,0x81,0xF1,0x0D,0x84,0x05,0xF2,0x0F,0xF4,0x07,0xF5,0x08,0x14,0x9F, +0x80,0x02,0x14,0x8F,0x80,0x03,0xF7,0x84,0x49,0xFF,0xF7,0x32,0x49,0xFF,0xF6,0x1F, +0x2E,0x67,0xEF,0xC8,0x97,0xB0,0xC6,0x07,0x2E,0x07,0xEF,0xBD,0xC0,0x3C,0x49,0xFF, +0xE3,0x6A,0xD5,0x3B,0x50,0x7F,0x84,0xD0,0x4C,0x65,0x3F,0xF8,0x49,0xFF,0xF6,0x0F, +0xF2,0x10,0x84,0x00,0xB0,0xD4,0x50,0x45,0x7F,0xFF,0xF1,0x11,0x38,0x10,0x81,0x11, +0xCE,0x10,0x38,0x13,0x81,0x09,0x38,0x11,0x81,0x09,0x5A,0xA8,0x01,0x1F,0xA5,0x50, +0x9A,0x69,0x96,0x49,0x97,0x4B,0xAD,0x50,0x4E,0x54,0x00,0x18,0xFE,0x4A,0xD5,0x14, +0x38,0x53,0x81,0x11,0xE0,0xA1,0xE8,0x03,0x38,0x13,0x81,0x09,0x38,0xF1,0x81,0x11, +0xE0,0x2F,0xE8,0x03,0x38,0x11,0x81,0x09,0x4C,0x62,0x40,0x08,0x38,0x13,0x81,0x01, +0x38,0x51,0x81,0x01,0x8A,0x25,0xAC,0x50,0x8C,0x01,0x8C,0x42,0x5A,0x0A,0x40,0xD7, +0x8C,0xC1,0xD5,0xCB,0x44,0x0F,0xED,0xED,0x44,0x44,0x80,0x10,0x3C,0x0B,0xF8,0x6F, +0xB4,0xA4,0xB4,0x04,0xB4,0x24,0x40,0x30,0x58,0x09,0xB4,0x04,0x92,0xB6,0x92,0x14, +0x40,0x20,0x88,0x09,0x54,0x42,0x80,0x08,0x54,0x10,0x00,0x01,0x54,0x01,0x80,0x04, +0xFE,0x27,0xFE,0x0F,0x54,0x11,0x00,0x02,0xFE,0x0F,0x3E,0x07,0xF0,0xDC,0x46,0x00, +0x00,0x82,0x84,0x3E,0xA0,0x84,0xFE,0x56,0xA8,0x44,0xA0,0x84,0x44,0x1E,0xFF,0xFF, +0xFE,0x56,0xA8,0x44,0x84,0x01,0x49,0xFF,0xCF,0xA9,0x84,0x01,0x49,0xFF,0xCF,0xBA, +0x4E,0xA2,0x00,0x09,0xF0,0x0B,0x5A,0x08,0x01,0x0E,0x84,0x00,0x49,0xFF,0xDC,0x22, +0xD5,0x09,0xF5,0x10,0x80,0x0A,0x50,0x12,0x84,0x80,0x1A,0x02,0x80,0x01,0xD9,0xFE, +0xD5,0xF2,0xF0,0x06,0x5A,0x00,0xFF,0x0A,0xF0,0x0C,0x84,0x20,0x49,0xFF,0xD4,0x63, +0xF0,0x0A,0x84,0x23,0x49,0xFF,0xD4,0x5F,0x00,0x6F,0x80,0x4F,0x00,0x0F,0x80,0x49, +0x00,0x1F,0x80,0x4A,0x00,0x2F,0x80,0x4B,0x00,0x3F,0x80,0x4C,0x00,0x4F,0x80,0x4D, +0x00,0x5F,0x80,0x4E,0xB6,0xDF,0x49,0xFF,0xF5,0xFF,0x84,0x00,0x80,0x40,0xF3,0x07, +0xF4,0x08,0xF5,0x09,0xB7,0x3F,0x14,0x8F,0x80,0x01,0x44,0x10,0x00,0xF0,0x49,0xFF, +0xF6,0x54,0x84,0x05,0x44,0x10,0x00,0xF0,0x84,0x43,0xF3,0x07,0xF4,0x08,0xF5,0x09, +0xB7,0x3F,0x14,0x8F,0x80,0x01,0x49,0xFF,0xF6,0x48,0x49,0xFF,0xF5,0xA6,0x51,0xFF, +0x89,0x50,0xFC,0xC0,0xFC,0x40,0x51,0xFF,0xF5,0x30,0x49,0xFF,0xCD,0x47,0xF0,0x91, +0x84,0x20,0x44,0x20,0x04,0x80,0x50,0x0F,0x81,0xD0,0x49,0x00,0x0C,0x1B,0x44,0x20, +0x04,0x80,0x84,0x20,0x50,0x0F,0x86,0x50,0x49,0x00,0x0C,0x14,0x2E,0x07,0xF0,0xEF, +0x2E,0x87,0xF0,0xEE,0xF0,0x88,0x2E,0x07,0xF0,0xF0,0x2E,0xA7,0xF0,0xF2,0x54,0x10, +0x00,0x0F,0x92,0x04,0x96,0x06,0xF0,0x8A,0x2E,0x07,0xF0,0xF1,0xF1,0x89,0x54,0x10, +0x00,0x0F,0x92,0x04,0xF0,0x8C,0x84,0x1F,0xF1,0x8B,0x10,0x0F,0x80,0x49,0x2E,0x17, +0xF0,0xED,0x10,0x0F,0x80,0x4A,0x10,0x0F,0x80,0x4B,0x10,0x0F,0x80,0x4C,0x10,0x0F, +0x80,0x4D,0x10,0x0F,0x80,0x4E,0x10,0x0F,0x80,0x4F,0x54,0x04,0x00,0x01,0xF0,0x8F, +0x84,0x00,0xF1,0x87,0x2E,0x67,0xF0,0xF3,0x2E,0x77,0xF0,0xEC,0x2E,0x97,0xF0,0xEB, +0x3C,0x0B,0xF8,0x6F,0x3E,0x07,0xF0,0xDC,0x49,0xFF,0xF1,0x8E,0xF0,0x8D,0x84,0x01, +0x49,0xFF,0xDB,0x6B,0x2E,0x07,0xF0,0xE9,0xF1,0x07,0x5A,0x08,0x32,0x12,0x2E,0x77, +0xF0,0xEC,0x2E,0x07,0xF0,0xED,0x40,0x73,0xA0,0x08,0xFF,0xC7,0x2E,0x87,0xF0,0xEE, +0x84,0x01,0x5A,0x90,0xFF,0x17,0x14,0x9F,0x80,0x07,0xF0,0x8F,0xD5,0x15,0x5A,0x00, +0x33,0x06,0x40,0x73,0xA0,0x08,0xFF,0xCF,0xD5,0x0A,0x2E,0x77,0xF0,0xEB,0x2E,0x07, +0xF0,0xEC,0x40,0x73,0xA0,0x08,0xFF,0xC7,0x2E,0x87,0xF0,0xED,0x84,0x00,0xD5,0x03, +0xF0,0x8F,0x84,0x0A,0xF0,0x87,0x5A,0x80,0xFF,0x16,0x46,0x00,0x00,0x81,0x04,0x10, +0x00,0x23,0x04,0x00,0x00,0x23,0x96,0x5F,0x92,0x0C,0x96,0x1F,0xF1,0x90,0xF0,0x8E, +0x84,0x20,0x80,0x08,0x49,0xFF,0xF6,0x52,0x80,0x08,0x84,0x23,0x49,0xFF,0xF6,0x4E, +0xD5,0x04,0x84,0x00,0xF0,0x8E,0xF0,0x90,0x5A,0xA8,0xFF,0x04,0x5A,0x60,0xFF,0x12, +0x50,0x0F,0x80,0x4F,0xB6,0x1F,0x50,0x1F,0x80,0x4A,0x50,0x0F,0x80,0x49,0x50,0x2F, +0x80,0x4B,0xB0,0xD3,0x50,0x4F,0x80,0x4D,0x50,0x5F,0x80,0x4E,0x49,0xFF,0xF5,0x15, +0x85,0x21,0x80,0x0A,0x80,0x26,0x80,0x49,0x84,0x65,0x84,0x80,0x80,0xA9,0xB7,0x3F, +0x49,0xFF,0xF5,0x32,0xF0,0x0A,0x84,0xC0,0xF0,0x81,0xF0,0x0B,0x80,0x27,0xF0,0x82, +0xF0,0x0C,0x44,0x20,0x00,0xFF,0xF0,0x83,0x80,0x66,0x80,0x06,0xF4,0x08,0xF5,0x09, +0xB6,0xDF,0xF6,0x84,0x49,0xFF,0xF5,0xB4,0xF0,0x0A,0x85,0x43,0xF0,0x81,0xF0,0x0B, +0x80,0x27,0xF0,0x82,0xF0,0x0C,0x44,0x20,0x00,0xFF,0xF0,0x83,0xF0,0x0F,0x80,0x6A, +0xF4,0x08,0xF5,0x09,0xF0,0x84,0xB7,0x5F,0x84,0x05,0x49,0xFF,0xF5,0xA1,0x49,0xFF, +0xCD,0x6E,0x80,0x06,0x49,0xFF,0xCC,0x62,0x80,0x06,0x49,0xFF,0xCC,0x51,0x80,0x06, +0x49,0xFF,0xF1,0x1D,0x80,0x06,0x49,0xFF,0xF0,0x66,0x80,0x09,0x49,0xFF,0xF0,0x6F, +0x49,0xFF,0xF0,0x16,0x49,0xFF,0xEF,0xFF,0x80,0x0A,0x49,0xFF,0xF0,0xB3,0x80,0x06, +0x49,0xFF,0xDC,0x32,0x49,0x00,0x04,0xEE,0x84,0x04,0x49,0xFF,0xD6,0x19,0x44,0x00, +0x7F,0xFF,0x49,0x00,0x04,0xF4,0x80,0x06,0x49,0xFF,0xD6,0x45,0x80,0x06,0x49,0xFF, +0xD5,0x39,0x80,0x06,0x49,0xFF,0xCE,0xE6,0x49,0xFF,0xD7,0xEE,0xF0,0x8F,0x80,0x06, +0x49,0xFF,0xD7,0xE1,0x80,0x09,0x49,0xFF,0xD0,0xFB,0x49,0xFF,0xD6,0xE7,0x49,0xFF, +0xCF,0x3F,0x5A,0x00,0x01,0xFE,0x84,0x01,0x49,0xFF,0xF2,0xCE,0x49,0xFF,0xF0,0xDD, +0x46,0x10,0x00,0x91,0x04,0x00,0x80,0x11,0x58,0x00,0x00,0x02,0x14,0x00,0x80,0x11, +0x2E,0x07,0xF0,0xE9,0x5A,0x08,0x32,0x74,0xF0,0x0D,0x84,0x20,0x44,0x20,0x04,0x80, +0x49,0x00,0x0B,0x00,0x2E,0x67,0xEF,0xC8,0x97,0xB0,0xC6,0x09,0x2E,0x07,0xEF,0xBD, +0x4E,0x02,0x00,0xA0,0x49,0xFF,0xE1,0x87,0x48,0x00,0x00,0x9E,0x44,0x74,0x80,0x08, +0xF0,0x07,0x8C,0x02,0xE2,0xC0,0xE8,0xF3,0x84,0x01,0x49,0xFF,0xF2,0x98,0x49,0xFF, +0xF4,0x0D,0xB4,0x07,0x92,0x02,0x96,0x06,0xC8,0x06,0x49,0x00,0x05,0x06,0x2E,0x07, +0xEF,0xC8,0xC0,0xF8,0x84,0x04,0xAE,0x38,0x84,0x00,0x49,0xFF,0xF2,0x88,0xE6,0xC2, +0xE9,0x43,0x50,0x9F,0x80,0x50,0x80,0x09,0x44,0x10,0x00,0xC0,0x49,0xFF,0xE3,0xCA, +0xF2,0x07,0x80,0x09,0xF3,0x0D,0x84,0x20,0x50,0x4F,0x86,0x50,0x50,0x5F,0x81,0xD0, +0x50,0x91,0x00,0x01,0x5A,0x68,0x02,0x17,0xF2,0x07,0x38,0xA0,0x05,0x11,0x38,0xA2, +0x05,0x09,0x38,0xA2,0x85,0x09,0x5A,0x28,0x01,0x24,0xA4,0x98,0x8A,0x4A,0x96,0x91, +0x40,0xA1,0x00,0x11,0x4E,0xA5,0x00,0x05,0x12,0xA1,0x80,0x00,0xD5,0x19,0xFE,0x92, +0xD5,0x16,0x38,0x20,0x05,0x11,0x38,0xA2,0x05,0x11,0xE1,0x42,0xE8,0x03,0x38,0x22, +0x05,0x09,0x38,0xF2,0x85,0x11,0xE0,0x4F,0xE8,0x03,0x38,0x22,0x85,0x09,0x4C,0x64, +0xC0,0x08,0x38,0x22,0x05,0x01,0x38,0xA2,0x85,0x01,0x8A,0x4A,0xAC,0x98,0x8C,0x21, +0x8C,0x66,0x5A,0x18,0xC0,0xD1,0x8C,0xC1,0x97,0xB1,0xD5,0xA3,0x5A,0x08,0x33,0x98, +0x84,0x01,0x49,0xFF,0xF2,0x3C,0x2E,0x07,0xEF,0xC8,0xC0,0x13,0x84,0x00,0xB1,0x94, +0x49,0xFF,0xF2,0x35,0x80,0x06,0x44,0x10,0x00,0xC0,0x49,0xFF,0xE3,0x7B,0x84,0x20, +0xF0,0x0D,0x44,0x20,0x04,0x80,0x49,0x00,0x0A,0x7D,0x84,0x20,0x84,0x66,0xD5,0x15, +0x84,0xE3,0x44,0x64,0x80,0x08,0x49,0xFF,0xF3,0x99,0xB4,0x06,0x92,0x02,0x96,0x06, +0xC8,0x06,0x49,0x00,0x04,0x92,0x2E,0x07,0xEF,0xC8,0xC0,0xF8,0x8E,0xE1,0x84,0x04, +0x97,0xF9,0xAE,0x30,0xCF,0xF1,0xD5,0xDB,0xF4,0x0D,0x42,0x00,0x8C,0x24,0x38,0x23, +0x05,0x11,0x8C,0x21,0x38,0x22,0x00,0x09,0x5A,0x18,0xC0,0xF8,0x48,0xFF,0xFF,0x60, +0x44,0x0F,0xED,0xED,0x44,0x44,0x80,0x10,0x3C,0x0B,0xF8,0x6F,0xB4,0xA4,0xB4,0x04, +0xB4,0x24,0x40,0x30,0x58,0x09,0xB4,0x04,0x92,0xB6,0x92,0x14,0x54,0x42,0x80,0x08, +0x40,0x20,0x88,0x09,0x54,0x10,0x00,0x01,0x54,0x01,0x80,0x04,0xFE,0x27,0xFE,0x0F, +0x54,0x11,0x00,0x02,0xFE,0x0F,0x46,0x10,0x00,0x91,0x3E,0x07,0xF0,0xDC,0x04,0x20, +0x80,0x11,0x84,0x1D,0xFE,0x16,0x14,0x00,0x80,0x11,0x49,0xFF,0xD5,0xFF,0x84,0x00, +0x49,0xFF,0xF1,0xEA,0x84,0x00,0x49,0xFF,0xD9,0xC0,0x84,0x00,0x49,0xFF,0xD0,0x08, +0xF0,0x0F,0x49,0xFF,0xD6,0xE8,0x84,0x01,0x49,0xFF,0xCD,0xE4,0x49,0xFF,0xE4,0x1D, +0x2E,0x07,0xF0,0xCE,0x84,0x00,0x3E,0x07,0xF0,0xCE,0x3E,0x07,0xF0,0xCD,0xF0,0x11, +0x49,0xFF,0xCB,0x2C,0x84,0x00,0x49,0xFF,0xEF,0xEA,0x84,0x00,0x49,0xFF,0xEF,0x33, +0x84,0x00,0x49,0xFF,0xEF,0x3C,0x49,0xFF,0xEE,0xE3,0x84,0x01,0x49,0xFF,0xD4,0x1A, +0x5A,0x80,0xFF,0x0A,0xF0,0x10,0x84,0x20,0x49,0xFF,0xD2,0x0D,0xF0,0x0E,0x84,0x23, +0x49,0xFF,0xD2,0x09,0x00,0x6F,0x80,0x4F,0x00,0x0F,0x80,0x49,0x00,0x1F,0x80,0x4A, +0x00,0x2F,0x80,0x4B,0x00,0x3F,0x80,0x4C,0x00,0x4F,0x80,0x4D,0x00,0x5F,0x80,0x4E, +0xB6,0xDF,0x49,0xFF,0xF3,0xA9,0xF0,0x0B,0xF3,0x08,0xB6,0x1F,0xF0,0x0C,0xF4,0x09, +0xF0,0x81,0x84,0x00,0x80,0x40,0xF5,0x0A,0x44,0x10,0x00,0xF0,0x49,0xFF,0xF3,0xFD, +0xF0,0x0B,0x44,0x10,0x00,0xF0,0xB6,0x1F,0xF0,0x0C,0x84,0x43,0xF0,0x81,0xF3,0x08, +0x84,0x05,0xF4,0x09,0xF5,0x0A,0x49,0xFF,0xF3,0xF0,0x49,0xFF,0xF3,0x4E,0x51,0xFF, +0x8A,0xD0,0xFC,0xC0,0xFC,0x40,0x51,0xFF,0xF6,0x98,0x2E,0x07,0xF0,0xED,0x2E,0x77, +0xF0,0xF0,0xF0,0x8A,0x2E,0x07,0xF0,0xEF,0xFA,0x58,0xF0,0x83,0x54,0x03,0x80,0x0F, +0x92,0xE4,0xF0,0x84,0x54,0x03,0x80,0x01,0xF0,0x85,0x2E,0x07,0xF0,0xF1,0x2E,0x67, +0xF0,0xF4,0x54,0x10,0x00,0x0F,0x92,0x04,0xF1,0x86,0xF0,0x87,0x84,0x20,0xB0,0x10, +0x49,0x00,0x09,0xB0,0x84,0x20,0x44,0x20,0x04,0x80,0xB0,0x1A,0x49,0x00,0x09,0xAA, +0x50,0x0F,0x84,0xE8,0x84,0x20,0x44,0x20,0x04,0x80,0x49,0x00,0x09,0xA3,0x84,0x00, +0x3C,0x0B,0xF8,0x6F,0x3E,0x07,0xF0,0xDC,0x5A,0x60,0xFF,0x04,0x97,0xF1,0xD5,0x02, +0xFA,0xF8,0x49,0xFF,0xEF,0x46,0xF0,0x8C,0x49,0xFF,0xEF,0x46,0x81,0x20,0x84,0x00, +0x80,0x20,0x80,0x80,0x80,0xA0,0x44,0x22,0x06,0x1C,0x44,0x32,0x06,0x44,0x49,0xFF, +0xDC,0xE8,0x49,0xFF,0xEF,0x48,0x84,0x01,0x49,0xFF,0xF1,0x3E,0xF0,0x0A,0x5A,0x00, +0xFF,0x12,0x46,0x00,0x00,0x81,0x50,0x20,0x00,0x8C,0xB4,0x22,0x96,0x5F,0xF1,0x88, +0xB4,0x22,0xB4,0x02,0x92,0x2C,0x96,0x5F,0x92,0x08,0xF1,0x89,0x54,0xA0,0x00,0x0F, +0xD5,0x06,0x85,0x40,0x14,0xAF,0x80,0x09,0x14,0xAF,0x80,0x08,0x46,0x00,0x00,0x82, +0x84,0xC0,0x50,0x80,0x00,0x10,0x44,0x22,0x06,0x1C,0x84,0x00,0x38,0x11,0x19,0x01, +0xF2,0x8B,0x49,0xFF,0xD0,0x65,0xF2,0x0B,0x84,0x05,0x38,0x11,0x19,0x01,0x49,0xFF, +0xD0,0x5F,0xF2,0x0B,0x84,0x02,0x38,0x11,0x19,0x01,0x49,0xFF,0xD0,0x59,0x44,0x22, +0x06,0x44,0x84,0x20,0x38,0x01,0x18,0x00,0xF2,0x8B,0x49,0xFF,0xD1,0x54,0xF2,0x0B, +0x84,0x23,0x38,0x01,0x18,0x00,0x49,0xFF,0xD1,0x4E,0xF2,0x0B,0x84,0x22,0x38,0x01, +0x18,0x00,0x49,0xFF,0xD1,0x48,0x49,0xFF,0xEF,0x08,0x49,0xFF,0xF2,0x5F,0x49,0xFF, +0xEE,0xFA,0x84,0xA0,0x50,0x3F,0x84,0xE8,0x96,0x29,0xE2,0x07,0xF0,0x8B,0xE8,0x44, +0xF3,0x8F,0xF5,0x8E,0x49,0xFF,0xEE,0xF9,0x49,0xFF,0xF2,0x50,0x49,0xFF,0xEE,0xEB, +0x9E,0x79,0x80,0x49,0x84,0x00,0x51,0xEF,0x80,0x68,0xF1,0x8D,0xF3,0x0F,0xF5,0x0E, +0xF1,0x0C,0xF4,0x0B,0x38,0x10,0x81,0x11,0xCC,0x14,0x38,0x11,0x81,0x09,0x38,0x1F, +0x01,0x09,0x5A,0x78,0x01,0x24,0x02,0xF1,0x00,0x00,0x40,0x17,0x84,0x01,0x96,0x49, +0x40,0xF0,0x80,0x11,0x12,0xF1,0x00,0x00,0x4E,0xF4,0x00,0x19,0xFE,0x4A,0xD5,0x15, +0x38,0xF1,0x81,0x11,0x40,0xF7,0x84,0x07,0xE8,0x03,0x38,0x11,0x81,0x09,0x38,0xFF, +0x01,0x11,0xE0,0x2F,0xE8,0x03,0x38,0x1F,0x01,0x09,0xF1,0x0D,0xD9,0x07,0x38,0x11, +0x81,0x01,0x38,0xFF,0x01,0x01,0x8A,0x2F,0xAC,0x50,0x8C,0x01,0x8C,0x42,0x5A,0x0A, +0x40,0xD1,0x8C,0xA1,0xD5,0xBA,0xB4,0x28,0x84,0x1E,0xFE,0x0E,0xB6,0x08,0xB4,0x28, +0x44,0x0E,0xFF,0xFF,0xFE,0x0E,0xB6,0x08,0x84,0x01,0x49,0xFF,0xCC,0x17,0x84,0x01, +0x49,0xFF,0xCC,0x28,0xB0,0x10,0x80,0xA9,0x38,0x00,0x19,0x01,0x50,0x24,0x84,0x80, +0x2A,0x12,0x80,0x01,0xE0,0x20,0x40,0x00,0xBC,0x1A,0x96,0x01,0xDA,0xFA,0xB0,0xD0, +0x38,0x01,0x99,0x09,0x8C,0xC1,0x5A,0x60,0x14,0x04,0x48,0xFF,0xFF,0x66,0x84,0x00, +0x44,0x10,0xFF,0xFF,0x80,0x40,0x38,0x41,0x81,0x01,0x97,0x40,0xE2,0x81,0x8C,0x01, +0x40,0x12,0x3C,0x1B,0x40,0x22,0xBC,0x1B,0x5A,0x08,0x14,0xF7,0x44,0x02,0x06,0x1C, +0x12,0x24,0x80,0x00,0x38,0x00,0x09,0x01,0x12,0x14,0x80,0x03,0x12,0x04,0x80,0x01, +0x44,0x00,0x00,0xF0,0x12,0x04,0x80,0x02,0x2E,0x07,0xEF,0xBD,0xC0,0x04,0x49,0xFF, +0xDF,0x3A,0xD5,0x03,0x44,0x0F,0xED,0xED,0x44,0x44,0x80,0x10,0x3C,0x0B,0xF8,0x6F, +0xB4,0xA4,0xB4,0x04,0xB4,0x24,0x40,0x30,0x58,0x09,0xB4,0x04,0x92,0xB6,0x92,0x14, +0x40,0x20,0x88,0x09,0x54,0x42,0x80,0x08,0x54,0x10,0x00,0x01,0x54,0x01,0x80,0x04, +0xFE,0x27,0xFE,0x0F,0x54,0x11,0x00,0x02,0xFE,0x0F,0x3E,0x07,0xF0,0xDC,0xF0,0x0A, +0x5A,0x00,0xFF,0x0E,0xF0,0x08,0x84,0x20,0x49,0xFF,0xD0,0x8D,0xF0,0x09,0x84,0x23, +0x49,0xFF,0xD0,0x89,0x80,0x0A,0x84,0x22,0x49,0xFF,0xD0,0x85,0xF0,0x06,0xF3,0x03, +0xB6,0x1F,0xF0,0x07,0xF4,0x04,0xF0,0x81,0x84,0x00,0x80,0x40,0xF5,0x05,0x44,0x10, +0x00,0xF0,0x49,0xFF,0xF2,0x8A,0xF0,0x06,0xF3,0x03,0xB6,0x1F,0xF0,0x07,0xF4,0x04, +0xF0,0x81,0xF5,0x05,0x84,0x05,0x44,0x10,0x00,0xF0,0x84,0x43,0x49,0xFF,0xF2,0x7D, +0xF0,0x06,0x44,0x10,0x00,0xF0,0xB6,0x1F,0xF0,0x07,0xF3,0x03,0xF0,0x81,0x84,0x02, +0x80,0x40,0xF4,0x04,0xF5,0x05,0x49,0xFF,0xF2,0x70,0x49,0xFF,0xF1,0xCE,0x51,0xFF, +0x89,0x68,0xFC,0xC0,0xFC,0x00,0x44,0x04,0x30,0x10,0xB4,0x00,0x92,0x08,0x96,0x00, +0xC0,0x15,0x44,0x14,0x30,0x08,0x84,0x1E,0xB4,0x41,0x44,0x64,0x30,0x0C,0xFE,0x16, +0xB6,0x01,0xB4,0x06,0x58,0x00,0x00,0x01,0xB6,0x06,0x84,0x01,0x49,0xFF,0xD5,0xB0, +0xB4,0x06,0x58,0x00,0x00,0x02,0xB6,0x06,0xFC,0x80,0x44,0x14,0x00,0x40,0x46,0x20, +0x81,0x00,0xB6,0x41,0x44,0x64,0x30,0x0C,0xB6,0x01,0xB4,0x06,0x92,0x04,0x96,0x06, +0xC8,0xE1,0x44,0x00,0x00,0x64,0x49,0xFF,0xD5,0x92,0x3C,0x00,0x00,0xC1,0x8C,0x01, +0x96,0x01,0x5C,0xF0,0x00,0x64,0x3C,0x08,0x00,0xC1,0xE9,0xF0,0xD5,0xD3,0xFC,0x40, +0x44,0x64,0x80,0x88,0x46,0xA0,0x10,0x00,0xB4,0x06,0x46,0x90,0x20,0x00,0x40,0x00, +0x28,0x04,0xB6,0x06,0xB4,0x06,0x44,0x74,0x55,0x08,0x40,0x00,0x24,0x04,0xB6,0x06, +0xB4,0x27,0x44,0x0F,0xFF,0xBF,0xFE,0x0E,0xB6,0x07,0x84,0x21,0x80,0x07,0x49,0xFF, +0xCC,0x66,0x44,0x04,0x58,0x88,0x84,0x37,0xB4,0x40,0xFE,0x56,0xB6,0x20,0x84,0x21, +0x49,0xFF,0xCC,0x32,0xB4,0x27,0x44,0x0F,0xFF,0x7F,0xFE,0x0E,0xB6,0x07,0xB4,0x27, +0x44,0x0F,0xFF,0xEF,0xFE,0x0E,0xB6,0x07,0x84,0x21,0x80,0x07,0x49,0xFF,0xCC,0x4F, +0xB4,0x06,0x40,0xA0,0x28,0x04,0xB7,0x46,0xB4,0x06,0x40,0x90,0x24,0x04,0xB7,0x26, +0x49,0xFF,0xD1,0xBC,0xE6,0x03,0xE8,0x1F,0x44,0x14,0x2C,0x0C,0x46,0x00,0x01,0x00, +0xB4,0x41,0xFE,0x17,0x84,0x41,0xB6,0x01,0x80,0x62,0x44,0x04,0x28,0x0C,0x44,0x14, +0x24,0x0C,0x49,0xFF,0xCC,0x5F,0x44,0x14,0x2C,0x18,0x46,0x00,0x04,0x00,0xB4,0x41, +0xFE,0x17,0x84,0x41,0xB6,0x01,0x80,0x62,0x44,0x04,0x28,0x18,0x44,0x14,0x24,0x18, +0x49,0xFF,0xCC,0x50,0x49,0xFF,0xD1,0x9A,0xE6,0x03,0xE8,0x2B,0x44,0x14,0x2C,0x14, +0xB4,0x01,0x58,0x00,0x10,0x00,0xB6,0x01,0x84,0x05,0x49,0xFF,0xD7,0x56,0x84,0x41, +0x80,0x62,0x44,0x04,0x28,0x14,0x44,0x14,0x24,0x14,0x49,0xFF,0xCC,0x3B,0x44,0x04, +0x2C,0x1C,0x44,0x1B,0xFF,0xFF,0xB4,0x40,0xFE,0x56,0xB6,0x20,0xB4,0x40,0x46,0x10, +0x00,0x80,0xFE,0x57,0xB6,0x20,0xB4,0x40,0x46,0x10,0x04,0x00,0xFE,0x57,0x84,0x41, +0xB6,0x20,0x80,0x62,0x44,0x04,0x28,0x1C,0x44,0x14,0x24,0x1C,0x49,0xFF,0xCC,0x22, +0x46,0x60,0x00,0x81,0x49,0xFF,0xCC,0x30,0x04,0x13,0x03,0xA0,0x84,0x10,0xFE,0x0E, +0x14,0x03,0x03,0xA0,0x04,0x13,0x03,0x9F,0x46,0x0F,0x0F,0xFF,0x50,0x00,0x0F,0xFF, +0xFE,0x46,0x46,0x20,0x40,0x00,0xFE,0x57,0x14,0x13,0x03,0x9F,0x04,0x13,0x03,0x9F, +0x46,0x27,0x00,0x00,0x94,0x4C,0x92,0x24,0xFE,0x57,0x14,0x13,0x03,0x9F,0x04,0x13, +0x03,0x9E,0xFE,0x0E,0x46,0x10,0x70,0x00,0xFE,0x0F,0x14,0x03,0x03,0x9E,0x49,0xFF, +0xCC,0x04,0x46,0x00,0x03,0x33,0x50,0x00,0x03,0x33,0x44,0x14,0x50,0x0C,0x14,0x03, +0x00,0x34,0x14,0x03,0x00,0x36,0xB4,0x01,0x58,0x00,0x00,0x01,0xB6,0x01,0x44,0x04, +0x00,0x90,0x84,0x2A,0xAC,0x40,0x84,0x00,0x49,0xFF,0xD8,0x65,0x44,0x16,0x20,0x04, +0x46,0x00,0x10,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0x44,0x14,0x2C,0x18,0x46,0x0B, +0xFF,0xFF,0xB4,0x41,0x50,0x00,0x0F,0xFF,0xFE,0x16,0xB6,0x01,0x44,0x14,0x2C,0x1C, +0x44,0x0F,0xFF,0xEF,0xB4,0x41,0xFE,0x16,0x84,0x41,0xB6,0x01,0x80,0x62,0x44,0x04, +0x28,0x18,0x44,0x14,0x24,0x18,0x49,0xFF,0xCB,0xC5,0x84,0x41,0x44,0x04,0x28,0x1C, +0x44,0x14,0x24,0x1C,0x80,0x62,0x49,0xFF,0xCB,0xBD,0x49,0xFF,0xD1,0x07,0xE6,0x03, +0xE8,0x10,0x44,0x14,0x2C,0x04,0x46,0x00,0x00,0x80,0xB4,0x41,0xFE,0x17,0x84,0x41, +0xB6,0x01,0x80,0x62,0x44,0x04,0x28,0x04,0x44,0x14,0x24,0x04,0x49,0xFF,0xCB,0xAA, +0x49,0xFF,0xD0,0xF4,0xE6,0x03,0xE9,0x40,0x84,0x08,0x49,0xFF,0xD6,0xB6,0x84,0x00, +0x49,0xFF,0xD6,0xBC,0x84,0x41,0x80,0x62,0x44,0x04,0x28,0x14,0x44,0x14,0x24,0x14, +0x49,0xFF,0xCB,0x98,0x44,0x14,0x2C,0x0C,0x46,0x0F,0xFE,0xFF,0xB4,0x41,0x50,0x00, +0x0F,0xFF,0xFE,0x16,0x84,0x41,0xB6,0x01,0x80,0x62,0x44,0x04,0x28,0x0C,0x44,0x14, +0x24,0x0C,0x49,0xFF,0xCB,0x87,0x44,0x04,0x2C,0x04,0x46,0x10,0x08,0x00,0xB4,0x40, +0xFE,0x57,0xB6,0x20,0xB4,0x40,0x46,0x1F,0xFB,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x56, +0xB6,0x20,0xB4,0x40,0x46,0x10,0x00,0x80,0xFE,0x57,0xB6,0x20,0xB4,0x40,0x44,0x14, +0x00,0x00,0xFE,0x57,0x84,0x41,0xB6,0x20,0x80,0x62,0x44,0x04,0x28,0x04,0x44,0x14, +0x24,0x04,0x49,0xFF,0xCB,0x67,0xFC,0xC0,0xFC,0x00,0x84,0x01,0x49,0xFF,0xC8,0x38, +0x84,0x03,0x49,0xFF,0xC8,0x58,0x84,0x00,0x49,0xFF,0xC8,0xDE,0x84,0x00,0x49,0xFF, +0xC8,0x62,0x84,0x03,0x49,0xFF,0xC8,0x80,0x84,0x00,0x49,0xFF,0xC8,0x8B,0x84,0x01, +0x49,0xFF,0xD7,0x62,0xFC,0x80,0xFC,0x00,0x84,0x00,0x44,0x10,0x00,0xF0,0x49,0xFF, +0xCF,0x0A,0x84,0x02,0x44,0x10,0x00,0xF0,0x49,0xFF,0xCF,0x05,0x84,0x01,0x44,0x10, +0x00,0xF0,0x49,0xFF,0xCF,0x00,0x84,0x03,0x44,0x10,0x00,0xF0,0x49,0xFF,0xCE,0xFB, +0x84,0x04,0x44,0x10,0x00,0xF0,0x49,0xFF,0xCE,0xF6,0x44,0x10,0x00,0xF0,0x84,0x05, +0x49,0xFF,0xCE,0xF1,0x84,0x01,0x49,0xFF,0xD6,0x30,0x84,0x03,0x84,0x2A,0x49,0xFF, +0xCF,0x3E,0x84,0x00,0x84,0x23,0x49,0xFF,0xC5,0xFF,0xFC,0x80,0xFC,0x01,0xF0,0x81, +0x49,0xFF,0xD0,0x6C,0xE6,0x03,0xE9,0x04,0xF0,0x01,0x49,0xFF,0xDB,0x34,0xFC,0x81, +0xFC,0x00,0x84,0x01,0x49,0xFF,0xCA,0x91,0x84,0x01,0x49,0xFF,0xC6,0x3B,0x84,0x01, +0x49,0xFF,0xC9,0x75,0x84,0x00,0x49,0xFF,0xEB,0xB8,0x84,0x00,0x49,0xFF,0xEB,0x9B, +0x84,0x01,0x49,0xFF,0xC8,0xA6,0x84,0x01,0x49,0xFF,0xCD,0xD3,0x84,0x01,0x49,0xFF, +0xCD,0xE0,0x84,0x01,0x49,0xFF,0xC8,0x9D,0x84,0x01,0x49,0xFF,0xCD,0xF8,0x84,0x01, +0x49,0xFF,0xC9,0x9C,0x84,0x01,0x49,0xFF,0xC9,0xAD,0x84,0x01,0x49,0xFF,0xC9,0xC3, +0x49,0xFF,0xD0,0x3C,0xE6,0x03,0xE8,0x05,0x84,0x01,0x84,0x2F,0x49,0xFF,0xCF,0x9A, +0x49,0xFF,0xD0,0x34,0xE6,0x03,0xE9,0x03,0x49,0xFF,0xD0,0x34,0x49,0xFF,0xD0,0x2E, +0xE6,0x03,0xE8,0x03,0x49,0xFF,0xD0,0x37,0x84,0x04,0x49,0xFF,0xD0,0x48,0xFC,0x80, +0xFC,0x00,0x49,0xFF,0xD0,0x66,0x49,0xFF,0xD0,0xCB,0x49,0xFF,0xD1,0x79,0x84,0x01, +0x49,0xFF,0xD0,0x50,0x49,0xFF,0xDB,0x30,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xD1,0x17, +0xFC,0x80,0xFC,0x02,0xF0,0x83,0xF1,0x81,0xFD,0x01,0xF2,0x82,0xB6,0x7F,0x49,0xFF, +0xD2,0x1F,0xF0,0x01,0xB4,0x3F,0x49,0xFF,0xD2,0x37,0xF0,0x03,0xF1,0x01,0xF2,0x02, +0xB4,0x7F,0x49,0xFF,0xD2,0x55,0xB4,0x1F,0x49,0xFF,0xD2,0x76,0xFC,0x82,0xFC,0x00, +0x44,0x00,0x77,0x7E,0x49,0xFF,0xDC,0xC3,0x84,0x20,0x80,0x61,0x84,0x41,0x44,0x00, +0x00,0x64,0x49,0xFF,0xFF,0xE0,0x84,0x00,0x80,0x20,0x49,0xFF,0xD2,0xA1,0x84,0x00, +0x80,0x20,0x49,0xFF,0xCB,0xF9,0x84,0x00,0x49,0xFF,0xD2,0xA4,0x84,0x20,0x84,0x01, +0x49,0xFF,0xD2,0x1C,0x84,0x00,0x49,0xFF,0xD2,0x69,0x84,0x40,0x84,0x61,0x44,0x00, +0x03,0x41,0x84,0x27,0x49,0xFF,0xFF,0xC7,0x84,0x00,0x84,0x21,0x49,0xFF,0xD2,0x88, +0x84,0x01,0x80,0x20,0x49,0xFF,0xCB,0xE0,0x49,0x00,0x00,0x44,0xFC,0x80,0xFC,0x00, +0x80,0xC0,0x5A,0x08,0x01,0x0D,0x84,0x00,0x3E,0x07,0xEF,0xC9,0x44,0x10,0x01,0xF4, +0x84,0x02,0x49,0x00,0x00,0x3B,0x84,0x02,0x80,0x26,0xD5,0x03,0x84,0x02,0x84,0x20, +0x49,0x00,0x00,0x42,0xFC,0x80,0xFC,0x00,0x2E,0x07,0xEF,0xC9,0xC0,0x07,0x84,0x02, +0x49,0x00,0x00,0x31,0x84,0x00,0x3E,0x07,0xEF,0xC9,0x84,0x02,0x49,0x00,0x00,0x4F, +0x5A,0x08,0x01,0x04,0x3E,0x07,0xEF,0xC8,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xD2,0xAB, +0xFC,0x80,0xFC,0x00,0x5A,0x08,0x01,0x0B,0xFE,0x0B,0x96,0x01,0x49,0xFF,0xD2,0x9A, +0x49,0xFF,0xD2,0xA8,0x49,0xFF,0xD2,0x9F,0xFC,0x80,0x49,0xFF,0xD2,0xB3,0xFC,0x80, +0x3C,0x00,0x00,0xE4,0x3C,0x10,0x00,0xE4,0x96,0x01,0x4C,0x00,0xFF,0xFB,0xDD,0x9E, +0x84,0x00,0x3C,0x08,0x00,0xC3,0xDD,0x9E,0x44,0x22,0x17,0x30,0x38,0x11,0x01,0x09, +0xDD,0x9E,0xFC,0x00,0x80,0xC0,0x49,0xFF,0xFF,0xED,0x44,0x12,0x17,0x50,0x38,0x00, +0x99,0x09,0xFC,0x80,0x80,0x60,0x5A,0x18,0x01,0x10,0xFC,0x20,0x80,0xC1,0x80,0xE0, +0x49,0xFF,0xFF,0xF1,0x3C,0x20,0x00,0xC3,0x40,0x13,0x1C,0x0C,0xFE,0x57,0x96,0x49, +0x3C,0x18,0x00,0xC3,0xFC,0xA0,0x84,0x41,0x3C,0x00,0x00,0xC3,0x40,0x11,0x0C,0x0C, +0xFE,0x4B,0xFE,0x46,0x3C,0x18,0x00,0xC3,0xDD,0x9E,0x3C,0x10,0x00,0xC3,0x40,0x10, +0x80,0x0E,0x96,0x46,0xC9,0x03,0x80,0x01,0xDD,0x9E,0xFC,0x00,0x80,0xC0,0x49,0xFF, +0xFF,0xC1,0x44,0x12,0x17,0x50,0x44,0x22,0x17,0x30,0x38,0x10,0x99,0x01,0x38,0x21, +0x19,0x01,0x8A,0x01,0x96,0x01,0xE2,0x02,0x56,0x07,0x80,0x01,0xFC,0x80,0x3C,0x00, +0x00,0xE4,0x8C,0x01,0x96,0x01,0x3C,0x08,0x00,0xE4,0xDD,0x9E,0x3A,0x1F,0xA4,0x3C, +0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72,0x04,0x02,0x3A,0x6F, +0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03,0x64,0x00,0x00,0x08, +0x49,0x00,0x02,0x02,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03,0x64,0x72,0x04,0x03, +0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0xA4,0x3C, +0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72,0x04,0x02,0x3A,0x6F, +0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03,0x64,0x00,0x00,0x08, +0x49,0x00,0x01,0xE3,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03,0x64,0x72,0x04,0x03, +0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0xA4,0x3C, +0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72,0x04,0x02,0x3A,0x6F, +0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03,0x64,0x00,0x00,0x08, +0x49,0x00,0x01,0xC4,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03,0x64,0x72,0x04,0x03, +0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0xA4,0x3C, +0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72,0x04,0x02,0x3A,0x6F, +0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03,0x64,0x00,0x00,0x08, +0x49,0x00,0x01,0xA5,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03,0x64,0x72,0x04,0x03, +0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0xA4,0x3C, +0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72,0x04,0x02,0x3A,0x6F, +0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03,0x64,0x00,0x00,0x08, +0x49,0x00,0x01,0x86,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03,0x64,0x72,0x04,0x03, +0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0xA4,0x3C, +0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72,0x04,0x02,0x3A,0x6F, +0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03,0x64,0x00,0x00,0x08, +0x49,0x00,0x01,0x67,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03,0x64,0x72,0x04,0x03, +0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0xA4,0x3C, +0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72,0x04,0x02,0x3A,0x6F, +0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03,0x64,0x00,0x00,0x08, +0x49,0x00,0x01,0x48,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03,0x64,0x72,0x04,0x03, +0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0xA4,0x3C, +0x3A,0xFF,0xBF,0xBC,0x64,0x62,0xA4,0x02,0x9D,0xB4,0x64,0x72,0x04,0x02,0x3A,0x6F, +0x9C,0x3C,0x64,0x62,0x00,0x02,0x9F,0xB2,0x64,0x62,0x00,0x03,0x64,0x00,0x00,0x08, +0x49,0x00,0x01,0x29,0x3A,0x6F,0x9C,0x04,0x64,0x62,0xA4,0x03,0x64,0x72,0x04,0x03, +0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0xA4,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0x94,0x3C, +0x3A,0xFF,0xBF,0xBC,0x64,0x12,0xA4,0x02,0x64,0x22,0x04,0x02,0x3A,0x1F,0x88,0x3C, +0x64,0x12,0x00,0x02,0x9E,0x4A,0x64,0x12,0x00,0x03,0x44,0x10,0x00,0xA4,0x38,0x10, +0x82,0x02,0xDD,0x21,0x3A,0x0F,0x84,0x04,0x64,0x02,0x00,0x43,0x64,0x00,0x00,0x08, +0x64,0x02,0xA4,0x03,0x64,0x12,0x04,0x03,0x3A,0xFF,0xBF,0x84,0x3A,0x1F,0x94,0x04, +0x3A,0x0F,0x80,0x04,0x64,0x00,0x00,0x04,0x3A,0x1F,0x94,0x3C,0x3A,0xFF,0xBF,0xBC, +0x44,0x10,0x00,0xA4,0x38,0x10,0x82,0x02,0xDD,0x21,0x3A,0xFF,0xBF,0x84,0x3A,0x1F, +0x94,0x04,0x3A,0x0F,0x80,0x04,0x64,0x00,0x00,0x04,0xD5,0x00,0x3A,0x0F,0x80,0x3C, +0x84,0x00,0xD5,0xEB,0x3A,0x0F,0x80,0x3C,0x84,0x01,0xD5,0xE7,0x3A,0x0F,0x80,0x3C, +0x84,0x02,0xD5,0xE3,0x3A,0x0F,0x80,0x3C,0x84,0x03,0xD5,0xDF,0x3A,0x0F,0x80,0x3C, +0x84,0x04,0xD5,0xDB,0x3A,0x0F,0x80,0x3C,0x84,0x05,0xD5,0xD7,0x3A,0x0F,0x80,0x3C, +0x84,0x06,0xD5,0xD3,0x3A,0x0F,0x80,0x3C,0x84,0x07,0xD5,0xCF,0x3A,0x0F,0x80,0x3C, +0x84,0x08,0xD5,0xCB,0x3A,0x0F,0x80,0x3C,0x84,0x09,0xD5,0xC7,0x3A,0x0F,0x80,0x3C, +0x84,0x0A,0xD5,0xC3,0x3A,0x0F,0x80,0x3C,0x84,0x0B,0xD5,0xBF,0x3A,0x0F,0x80,0x3C, +0x84,0x0C,0xD5,0xBB,0x3A,0x0F,0x80,0x3C,0x84,0x0D,0xD5,0xB7,0x3A,0x0F,0x80,0x3C, +0x84,0x0E,0xD5,0xB3,0x3A,0x0F,0x80,0x3C,0x84,0x0F,0xD5,0xAF,0x3A,0x0F,0x80,0x3C, +0xFA,0x00,0xD5,0xAB,0x3A,0x0F,0x80,0x3C,0xFA,0x01,0xD5,0xA7,0x3A,0x0F,0x80,0x3C, +0xFA,0x02,0xD5,0xA3,0x3A,0x0F,0x80,0x3C,0xFA,0x03,0xD5,0x9F,0x3A,0x0F,0x80,0x3C, +0xFA,0x04,0xD5,0x9B,0x3A,0x0F,0x80,0x3C,0xFA,0x05,0xD5,0x97,0x3A,0x0F,0x80,0x3C, +0xFA,0x06,0xD5,0x93,0x3A,0x0F,0x80,0x3C,0xFA,0x07,0xD5,0x8F,0x3A,0x0F,0x80,0x3C, +0xFA,0x08,0xD5,0x8B,0x3A,0x0F,0x80,0x3C,0xFA,0x09,0xD5,0x87,0x3A,0x0F,0x80,0x3C, +0xFA,0x0A,0xD5,0x83,0x3A,0x0F,0x80,0x3C,0xFA,0x0B,0x48,0xFF,0xFF,0x7F,0x3A,0x0F, +0x80,0x3C,0xFA,0x0C,0x48,0xFF,0xFF,0x7A,0x3A,0x0F,0x80,0x3C,0xFA,0x0D,0x48,0xFF, +0xFF,0x75,0x3A,0x0F,0x80,0x3C,0xFA,0x0E,0x48,0xFF,0xFF,0x70,0x3A,0x0F,0x80,0x3C, +0x44,0x00,0x00,0x1F,0x48,0xFF,0xFF,0x6A,0x84,0x01,0x64,0x04,0xC0,0x02,0x44,0x02, +0x00,0x01,0x64,0x04,0xE0,0x03,0x45,0xD2,0x15,0xA8,0x3F,0xC8,0x00,0x00,0x45,0xF2, +0x5F,0xF8,0x84,0x00,0x42,0x0E,0x00,0x21,0x49,0x00,0x00,0x31,0x49,0x00,0x03,0x3C, +0xD5,0x00,0x92,0x00,0xFC,0x00,0x44,0x02,0x00,0x00,0x44,0x22,0x04,0xC4,0x44,0x10, +0x82,0xA0,0x8A,0x40,0x49,0x00,0x03,0x60,0x44,0x02,0x04,0xC4,0x44,0x22,0x27,0xA0, +0x84,0x20,0x8A,0x40,0x49,0x00,0x03,0x7E,0xFC,0x80,0x84,0x00,0x64,0x05,0xE4,0x03, +0x84,0x1F,0x64,0x03,0x04,0x03,0x64,0x03,0x24,0x03,0x64,0x12,0x24,0x02,0x44,0x0F, +0x0F,0xFF,0xFE,0x0E,0x64,0x02,0x24,0x03,0x64,0x12,0x00,0x02,0x84,0x18,0xFE,0x0E, +0x64,0x02,0x00,0x03,0x64,0x00,0x00,0x08,0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xFF,0xE7, +0x64,0x00,0x80,0x02,0x92,0x1E,0xC0,0x06,0x64,0x00,0x84,0x02,0x96,0x2E,0xC0,0x02, +0xD5,0x00,0x49,0xFF,0xFF,0xC9,0x64,0x12,0x00,0x43,0x64,0x00,0x00,0x08,0x64,0x00, +0x00,0x08,0xFC,0x80,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E, +0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0x44,0x04,0x80,0x04,0xA6,0x01,0x96,0x06,0xC0,0x05, +0x44,0x04,0x80,0x05,0x84,0x21,0xAE,0x40,0xDD,0x9E,0x44,0x04,0x80,0x01,0xA6,0x00, +0xC8,0x05,0xFC,0x00,0x49,0xFF,0xD5,0x1F,0xFC,0x80,0xDD,0x9E,0xFC,0x00,0x44,0x04, +0x80,0x00,0xA6,0x03,0x49,0xFF,0xD9,0xD6,0xFC,0x80,0x44,0x14,0x80,0x04,0x44,0x04, +0x80,0x84,0xA6,0x88,0xB4,0x00,0x96,0x06,0xFE,0x16,0xC0,0x03,0x84,0x01,0xAE,0x08, +0xDD,0x9E,0xFC,0x00,0x44,0x04,0x80,0x00,0xA6,0x03,0x49,0xFF,0xCF,0xBC,0xFC,0x80, +0x44,0x14,0x80,0x14,0xA6,0x08,0x96,0x06,0xC0,0x09,0x84,0x01,0xAE,0x08,0x44,0x14, +0x80,0x94,0xB4,0x01,0x58,0x00,0x00,0x01,0xB6,0x01,0x44,0x14,0x80,0x14,0xA6,0x08, +0x92,0x01,0x96,0x06,0xC0,0x09,0x84,0x02,0xAE,0x08,0x44,0x14,0x80,0x94,0xB4,0x01, +0x58,0x00,0x00,0x02,0xB6,0x01,0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xD3,0xD6,0xFC,0x80, +0x44,0x04,0x80,0x04,0xA6,0x02,0x92,0x02,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x06, +0x84,0x24,0xAE,0x40,0xDD,0x9E,0x44,0x14,0x80,0x04,0xA6,0x0B,0x92,0x01,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x07,0x84,0x42,0xAE,0x80,0xA6,0x0B,0x96,0x06,0xC0,0x05, +0x44,0x04,0x80,0x07,0x84,0x21,0xAE,0x40,0xDD,0x9E,0x44,0x04,0x80,0x08,0xB4,0x20, +0x92,0x24,0x96,0x46,0xC1,0x03,0xFA,0x20,0xAE,0x40,0xB4,0x00,0x84,0x21,0x96,0x06, +0xC0,0x06,0x44,0x04,0x80,0x08,0xAE,0x40,0x3E,0x17,0xEF,0xF1,0x44,0x04,0x80,0x08, +0xB4,0x20,0x92,0x21,0x96,0x46,0xC1,0x06,0x84,0x22,0xAE,0x40,0x84,0x21,0x3E,0x17, +0xEF,0xF0,0xB4,0x00,0x92,0x02,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x08,0x84,0x24, +0xAE,0x40,0xDD,0x9E,0xDD,0x9E,0xFC,0x00,0x49,0xFF,0xD2,0x9D,0xFC,0x80,0xFC,0x00, +0x49,0xFF,0xD2,0xF8,0xFC,0x80,0xFC,0x00,0x49,0xFF,0xC3,0xD7,0xFC,0x80,0x44,0x14, +0x80,0x08,0xB4,0x01,0x92,0x08,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x09,0x84,0x41, +0xAE,0x80,0xB4,0x01,0x92,0x09,0x96,0x06,0xC0,0x0F,0x44,0x04,0x80,0x09,0x84,0x22, +0xAE,0x40,0x84,0x01,0x3E,0x07,0xEF,0xFA,0x44,0x04,0x50,0x28,0xB4,0x00,0x92,0x06, +0x96,0x1F,0x3E,0x07,0xEF,0xF9,0xDD,0x9E,0xFC,0x00,0x44,0x04,0x80,0x08,0xB4,0x00, +0x92,0x12,0x96,0x06,0xC0,0x58,0x44,0x14,0x80,0x88,0xB4,0x01,0x92,0x12,0x96,0x06, +0xC0,0x52,0x44,0x04,0x80,0x0A,0x84,0x44,0xAE,0x80,0xB4,0x41,0x44,0x0D,0xFF,0xFF, +0xFE,0x16,0x44,0x67,0x30,0x04,0xB6,0x01,0xB4,0x26,0x84,0x1E,0xFE,0x0E,0xB6,0x06, +0x84,0x0A,0x49,0xFF,0xCF,0xDC,0xB4,0x06,0x58,0x00,0x00,0x01,0xB6,0x06,0x84,0x0A, +0x49,0xFF,0xCF,0xD5,0x49,0xFF,0xD7,0x99,0x44,0x00,0x00,0x87,0x49,0xFF,0xD7,0x8E, +0x44,0x17,0x30,0x10,0xB4,0x01,0x96,0x06,0xC0,0xFE,0x44,0x17,0x30,0x10,0xB4,0x01, +0x92,0x03,0x96,0x06,0x5A,0x00,0x01,0xFD,0x44,0x67,0x30,0x04,0x84,0x0A,0x49,0xFF, +0xCF,0xBE,0xB4,0x26,0x84,0x1E,0xFE,0x0E,0xB6,0x06,0x84,0x0A,0x49,0xFF,0xCF,0xB7, +0xB4,0x06,0x58,0x00,0x00,0x01,0xB6,0x06,0x84,0x0A,0x49,0xFF,0xCF,0xB0,0x44,0x14, +0x80,0x88,0x84,0x01,0x3E,0x07,0xEF,0xF8,0xB4,0x41,0x44,0x02,0x00,0x00,0xFE,0x17, +0xB6,0x01,0x44,0x04,0x80,0x0A,0x84,0x22,0xAE,0x40,0x3C,0x0D,0xFB,0xFD,0x8C,0x01, +0x3C,0x0F,0xFB,0xFD,0x44,0x04,0x80,0x08,0x84,0x21,0xB4,0x00,0x92,0x10,0x96,0x06, +0xC0,0x0C,0x44,0x04,0x80,0x88,0xB4,0x00,0x92,0x10,0x96,0x06,0xC0,0x06,0x44,0x04, +0x80,0x0A,0x3E,0x17,0xEF,0xFB,0xAE,0x40,0x44,0x04,0x80,0x08,0xB4,0x00,0x92,0x11, +0x96,0x06,0xC0,0x0B,0x44,0x04,0x80,0x88,0xB4,0x00,0x92,0x11,0x96,0x06,0xC0,0x05, +0x44,0x04,0x80,0x0A,0x84,0x22,0xAE,0x40,0xFC,0x80,0x44,0x14,0x80,0x08,0x84,0x41, +0xB4,0x01,0x92,0x18,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0B,0xAE,0x80,0x3E,0x27, +0xEF,0xBD,0xB4,0x01,0x92,0x19,0x96,0x06,0xC0,0x1D,0x44,0x04,0x80,0x0B,0x84,0x22, +0xAE,0x40,0x84,0x00,0x3E,0x07,0xEF,0xBD,0x46,0x00,0x00,0x81,0x44,0x1F,0xFE,0xFF, +0x50,0x30,0x00,0x70,0xB4,0x43,0xFE,0x56,0xB6,0x23,0xB4,0x43,0x44,0x1E,0xFF,0xFF, +0xFE,0x56,0xB6,0x23,0xB4,0x43,0x46,0x1F,0xEF,0xFF,0x50,0x10,0x8F,0xFF,0xFE,0x56, +0xB6,0x23,0x44,0x04,0x80,0x08,0xB4,0x00,0x92,0x1A,0x96,0x06,0xC0,0x0E,0x44,0x14, +0x70,0x08,0xB4,0x01,0x92,0x10,0x40,0x00,0x40,0x08,0x58,0x00,0x78,0x23,0xB6,0x01, +0x44,0x04,0x80,0x0B,0x84,0x24,0xAE,0x40,0x44,0x04,0x80,0x08,0xB4,0x00,0x92,0x1B, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0B,0x84,0x28,0xAE,0x40,0xDD,0x9E,0x44,0x04, +0x80,0x04,0xA6,0x02,0x92,0x03,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x06,0x84,0x28, +0xAE,0x40,0xDD,0x9E,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x10,0x96,0x06,0xC0,0x05, +0x44,0x04,0x80,0x0E,0x84,0x41,0xAE,0x80,0xB4,0x01,0x92,0x11,0x96,0x06,0xC0,0x05, +0x44,0x04,0x80,0x0E,0x84,0x22,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x12, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0E,0x84,0x44,0xAE,0x80,0xB4,0x01,0x92,0x13, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0E,0x84,0x28,0xAE,0x40,0x44,0x14,0x80,0x0C, +0xB4,0x01,0x92,0x14,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0E,0xFA,0x40,0xAE,0x80, +0xB4,0x01,0x92,0x15,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0E,0xFA,0x30,0xAE,0x40, +0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x16,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0E, +0x44,0x20,0x00,0x40,0xAE,0x80,0xB4,0x01,0x92,0x17,0x96,0x06,0xC0,0x06,0x44,0x04, +0x80,0x0E,0x44,0x1F,0xFF,0x80,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x18, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0F,0x84,0x41,0xAE,0x80,0xB4,0x01,0x92,0x19, +0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0F,0x84,0x22,0xAE,0x40,0x44,0x14,0x80,0x0C, +0xB4,0x01,0x92,0x1A,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0F,0x84,0x44,0xAE,0x80, +0xB4,0x01,0x92,0x1B,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0F,0x84,0x28,0xAE,0x40, +0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x1C,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0F, +0xFA,0x40,0xAE,0x80,0xB4,0x01,0x92,0x1D,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0F, +0xFA,0x30,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x1E,0x96,0x06,0xC0,0x06, +0x44,0x04,0x80,0x0F,0x44,0x20,0x00,0x40,0xAE,0x80,0xB4,0x01,0x4E,0x04,0x00,0x07, +0x44,0x04,0x80,0x0F,0x44,0x1F,0xFF,0x80,0xAE,0x40,0xDD,0x9E,0x44,0x04,0x80,0x0C, +0xB4,0x20,0x96,0x46,0xC1,0x03,0x84,0x21,0xAE,0x40,0xB4,0x00,0x92,0x01,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x0C,0x84,0x22,0xAE,0x40,0x44,0x04,0x80,0x0C,0xB4,0x20, +0x92,0x22,0x96,0x46,0xC1,0x03,0x84,0x24,0xAE,0x40,0xB4,0x00,0x92,0x03,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x0C,0x84,0x28,0xAE,0x40,0x44,0x04,0x80,0x0C,0xB4,0x20, +0x92,0x24,0x96,0x46,0xC1,0x03,0xFA,0x20,0xAE,0x40,0xB4,0x00,0x92,0x05,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x0C,0xFA,0x30,0xAE,0x40,0x44,0x04,0x80,0x0C,0xB4,0x20, +0x92,0x26,0x96,0x46,0xC1,0x04,0x44,0x10,0x00,0x40,0xAE,0x40,0xB4,0x00,0x92,0x07, +0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0C,0x44,0x1F,0xFF,0x80,0xAE,0x40,0x44,0x14, +0x80,0x0C,0xB4,0x01,0x92,0x08,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0D,0x84,0x41, +0xAE,0x80,0xB4,0x01,0x92,0x09,0x96,0x06,0xC0,0x05,0x44,0x04,0x80,0x0D,0x84,0x22, +0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x0A,0x96,0x06,0xC0,0x05,0x44,0x04, +0x80,0x0D,0x84,0x44,0xAE,0x80,0xB4,0x01,0x92,0x0B,0x96,0x06,0xC0,0x05,0x44,0x04, +0x80,0x0F,0x84,0x28,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01,0x92,0x0C,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x0D,0xFA,0x40,0xAE,0x80,0xB4,0x01,0x92,0x0D,0x96,0x06, +0xC0,0x05,0x44,0x04,0x80,0x0D,0xFA,0x30,0xAE,0x40,0x44,0x14,0x80,0x0C,0xB4,0x01, +0x92,0x0E,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0D,0x44,0x20,0x00,0x40,0xAE,0x80, +0xB4,0x01,0x92,0x0F,0x96,0x06,0xC0,0x06,0x44,0x04,0x80,0x0D,0x44,0x1F,0xFF,0x80, +0xAE,0x40,0xDD,0x9E,0x44,0x04,0x80,0x00,0x44,0x14,0x80,0x80,0xA6,0x80,0xB4,0x21, +0x96,0x46,0xFE,0x56,0xC1,0x03,0x84,0x21,0xAE,0x40,0xA6,0x00,0x40,0x10,0x04,0x09, +0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x01,0x96,0x06,0xFE,0x0E,0xC0,0x05,0x44,0x04, +0x80,0x00,0x84,0x22,0xAE,0x40,0x44,0x14,0x80,0x00,0xA6,0x08,0x40,0x20,0x08,0x09, +0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x02,0x96,0x06,0xFE,0x16,0xC0,0x03,0x84,0x04, +0xAE,0x08,0xA6,0x08,0x40,0x10,0x0C,0x09,0x44,0x04,0x80,0x80,0xB4,0x00,0x92,0x03, +0x96,0x06,0xFE,0x0E,0xC0,0x05,0x44,0x04,0x80,0x00,0x84,0x28,0xAE,0x40,0xDD,0x9E, +0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E,0xDD,0x9E, +0xDD,0x9E,0xDD,0x9E,0xFC,0x00,0x44,0x07,0x10,0x18,0x84,0x20,0xAE,0x41,0x44,0x16, +0x20,0x04,0x46,0x00,0x10,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0x44,0x00,0x01,0x00, +0x84,0x20,0x49,0xFF,0xC4,0x25,0x44,0x00,0x01,0x00,0x84,0x20,0x49,0xFF,0xC4,0x2F, +0x44,0x00,0x01,0x00,0x84,0x21,0x49,0xFF,0xC4,0x39,0x49,0xFF,0xDC,0xDA,0x44,0x14, +0x30,0x34,0x46,0x00,0x04,0x00,0xB4,0x41,0xFE,0x17,0xB6,0x01,0x49,0xFF,0xD6,0x3B, +0x49,0xFF,0xD6,0x73,0x49,0xFF,0xCE,0x63,0x49,0xFF,0xD7,0x02,0x49,0xFF,0xCF,0x78, +0x84,0x00,0xFC,0x80,0x80,0x60,0x54,0xF1,0x00,0x03,0xE9,0x1B,0xE6,0x44,0xE9,0x19, +0x54,0x41,0x80,0x03,0x54,0x50,0x80,0x03,0xDC,0x14,0xC5,0x0A,0x52,0x52,0x80,0x04, +0x9A,0x95,0x99,0x69,0x08,0x40,0x80,0x01,0x18,0x41,0x80,0x01,0xD9,0xFC,0x40,0x51, +0x08,0x09,0xC5,0x07,0x95,0x6A,0x99,0x69,0x96,0x8F,0xA3,0x09,0xAB,0x19,0xD9,0xFE, +0xC2,0x07,0x99,0x4A,0x08,0x40,0x80,0x01,0x18,0x41,0x80,0x01,0xD9,0xFC,0xDD,0x9E, +0x80,0x60,0xE6,0x44,0xE9,0x1A,0x54,0x51,0x80,0x03,0xC5,0x08,0x52,0x52,0x80,0x04, +0x9A,0x95,0x99,0x6B,0x18,0x11,0x80,0x01,0xDB,0xFE,0x40,0x51,0x08,0x09,0x96,0x8F, +0xC5,0x0C,0x95,0x6A,0x99,0x5D,0x96,0x48,0x40,0x40,0xA0,0x08,0xFE,0x67,0x40,0x40, +0xC0,0x08,0xFE,0x67,0xAA,0x59,0xDB,0xFF,0x99,0x5A,0xD3,0x04,0x18,0x11,0x80,0x01, +0xD5,0xFD,0xDD,0x9E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x32,0x19,0x64,0x32,0x33,0x11,0x12,0x20,0xFF,0x07,0xFF,0x07,0x05,0x0A,0x0B,0x00, +0xAC,0x04,0x02,0x00,0xA4,0x04,0x02,0x00,0x98,0x04,0x02,0x00,0x8C,0x04,0x02,0x00, +0x7C,0x04,0x02,0x00,0x6C,0x04,0x02,0x00,0x58,0x04,0x02,0x00,0x44,0x04,0x02,0x00, +0x2C,0x04,0x02,0x00,0x14,0x04,0x02,0x00,0x0A,0x0B,0x0B,0x0B,0x0C,0x0C,0x0C,0x0C, +0x0C,0x0C,0x00,0x00,0x01,0x05,0x00,0x07,0x02,0x06,0x04,0x08,0x03,0x09,0x00,0x00, +0x01,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x00,0x00,0x01,0x03,0x04,0x05, +0x06,0x07,0x08,0x09,0x0A,0x0B,0x00,0x00,0x05,0x05,0x05,0x05,0x05,0x05,0x08,0x08, +0x08,0x09,0x07,0x06,0x03,0x02,0x08,0x09,0x04,0x09,0x09,0x08,0x30,0x01,0x30,0x01, +0x30,0x01,0x30,0x01,0x30,0x01,0x30,0x01,0x30,0x01,0x62,0x01,0x50,0x01,0x44,0x01, +0x74,0x01,0x96,0x01,0x84,0x02,0xC4,0x02,0x5A,0x01,0x46,0x01,0xEE,0x01,0x30,0x01, +0x44,0x01,0x50,0x01,0x05,0x05,0x05,0x05,0x05,0x05,0x09,0x08,0x08,0x09,0x07,0x06, +0x03,0x02,0x08,0x09,0x04,0x09,0x09,0x08,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, +0x08,0x09,0x0A,0x0B,0x0B,0x0A,0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01,0x00, +0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x01,0x02,0x00,0x01,0x02,0x03, +0x04,0x05,0x06,0x07,0x08,0x02,0x01,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, +0x08,0x00,0x00,0x01,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x01,0x00, +0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00, +0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00, +0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00, +0x01,0x00,0x02,0x00,0x02,0x00,0x01,0x00,0x04,0x80,0x00,0x00,0x08,0x40,0x00,0x00, +0x10,0x20,0x00,0x00,0x20,0x10,0x00,0x00,0x40,0x08,0x00,0x00,0x80,0x04,0x00,0x00, +0x00,0x03,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x52,0x02,0x00,0x00,0x52,0x02,0x00,0x00,0x92,0x02,0x00,0x00,0xD2,0x02,0x00,0x00, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x90,0x00,0x00,0x00,0x90,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xF0,0x00,0x00,0x00, +0x00,0x00,0x40,0x02,0x40,0x02,0x00,0x00,0xC0,0x00,0xC0,0x00,0x40,0x00,0x40,0x00, +0x00,0x09,0x09,0x00,0x03,0x03,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, +0x01,0x04,0x04,0x04,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x01,0x02, +0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x02,0x01,0x00,0x00,0x01,0x02,0x03, +0x04,0x00,0x01,0x02,0x03,0x04,0x00,0x00,0x07,0x80,0x03,0x00,0x38,0x70,0x00,0x00, +0xC0,0x0F,0x00,0x00,0x08,0x40,0x00,0x00,0x10,0x20,0x00,0x00,0x20,0x10,0x00,0x00, +0x40,0x08,0x00,0x00,0x80,0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x07,0x80,0x03,0x00, +0x38,0x70,0x00,0x00,0xC0,0x0F,0x00,0x00,0x01,0x00,0x02,0x00,0x02,0x00,0x01,0x00, +0x04,0x80,0x00,0x00,0x08,0x40,0x00,0x00,0x10,0x20,0x00,0x00,0x20,0x10,0x00,0x00, +0x40,0x08,0x00,0x00,0x80,0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x38,0x70,0x00,0x00, +0x38,0x70,0x00,0x00,0x38,0x70,0x00,0x00,0x07,0x80,0x03,0x00,0x38,0x70,0x00,0x00, +0xC0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x03,0x03,0x03, +0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x03,0x03,0x01,0x01,0x01,0x01, +0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03, +0x03,0x03,0x03,0x03,0x04,0x04,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +0x01,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x02,0x00, +0x02,0x00,0x01,0x00,0x04,0x80,0x00,0x00,0x08,0x40,0x00,0x00,0x10,0x20,0x00,0x00, +0x20,0x10,0x00,0x00,0x40,0x08,0x00,0x00,0x80,0x04,0x00,0x00,0x00,0x03,0x00,0x00, +0x07,0x80,0x03,0x00,0x38,0x70,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x01,0x02,0x03, +0x04,0x05,0x06,0x07,0x08,0x00,0x01,0x02,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, +0x08,0x00,0x01,0x02,0x0B,0x02,0x0B,0x0B,0x0B,0x02,0x00,0x00,0x0B,0x02,0x0B,0x0B, +0x0B,0x02,0x00,0x00,0x01,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x00,0x00, +0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x00,0x00,0x00,0x08,0x07,0x06,0x05, +0x04,0x03,0x02,0x01,0x00,0x00,0x01,0x02,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, +0x08,0x02,0x01,0x00,0x00,0x01,0x02,0x00,0x80,0x00,0x01,0x00,0x40,0x80,0x00,0x00, +0x20,0x40,0x00,0x00,0x10,0x20,0x00,0x00,0x08,0x10,0x00,0x00,0x04,0x08,0x00,0x00, +0x02,0x04,0x00,0x00,0x01,0x02,0x00,0x00,0x07,0x80,0x03,0x00,0x38,0x70,0x00,0x00, +0xC0,0x0F,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x01,0x02, +0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x02,0x01,0x00,0x01,0x00,0x02,0x00, +0x02,0x00,0x01,0x00,0x04,0x80,0x00,0x00,0x08,0x40,0x00,0x00,0x10,0x20,0x00,0x00, +0x20,0x10,0x00,0x00,0x40,0x08,0x00,0x00,0x80,0x04,0x00,0x00,0x00,0x03,0x00,0x00, +0x07,0x80,0x03,0x00,0x38,0x70,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x00,0x00,0x00, +0xDC,0x00,0x0A,0x05,0xF3,0x01,0xA0,0x05,0x04,0x00,0x96,0x00,0x01,0x14,0x96,0x05, +0x04,0x64,0x00,0x00,0x2C,0x01,0x46,0x00,0x01,0x0A,0x08,0x00,0x27,0x00,0x27,0x00, +0xA9,0x02,0x79,0x05,0x01,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x01,0x01,0x00, +0x0A,0x0B,0x0B,0x0B,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x00,0x00,0xAC,0x04,0x02,0x00, +0xA4,0x04,0x02,0x00,0x98,0x04,0x02,0x00,0x8C,0x04,0x02,0x00,0x7C,0x04,0x02,0x00, +0x6C,0x04,0x02,0x00,0x58,0x04,0x02,0x00,0x44,0x04,0x02,0x00,0x2C,0x04,0x02,0x00, +0x14,0x04,0x02,0x00,0x5D,0x00,0x44,0x00,0x5A,0x00,0x72,0x00,0x8A,0x00,0xA3,0x00, +0xBB,0x00,0xD0,0x00,0xE3,0x00,0xF2,0x00,0xFD,0x00,0x02,0x01,0x61,0x00,0x4D,0x00, +0x68,0x00,0x84,0x00,0xA2,0x00,0xBF,0x00,0xDA,0x00,0xF2,0x00,0x06,0x01,0x13,0x01, +0x1A,0x01,0x00,0x00,0x65,0x00,0x58,0x00,0x79,0x00,0x9D,0x00,0xC1,0x00,0xE3,0x00, +0x02,0x01,0x1C,0x01,0x2E,0x01,0x37,0x01,0x6A,0x00,0x67,0x00,0x91,0x00,0xBE,0x00, +0xEA,0x00,0x13,0x01,0x35,0x01,0x4E,0x01,0x5B,0x01,0x00,0x00,0x72,0x00,0x7C,0x00, +0xB2,0x00,0xEB,0x00,0x23,0x01,0x52,0x01,0x75,0x01,0x87,0x01,0x7C,0x00,0x99,0x00, +0xE2,0x00,0x2E,0x01,0x72,0x01,0xA5,0x01,0xC1,0x01,0x00,0x00,0x45,0x00,0x63,0x00, +0x96,0x00,0xC8,0x00,0xF0,0x00,0x07,0x01,0x51,0x00,0x88,0x00,0xD3,0x00,0x15,0x01, +0x3C,0x01,0x00,0x00,0x67,0x00,0xCC,0x00,0x40,0x01,0x8A,0x01,0x99,0x00,0x66,0x01, +0x38,0x70,0x00,0x00,0xFF,0x00,0xFF,0x00,0x03,0x00,0x00,0x00,0x52,0x01,0x00,0x00, +0x30,0x75,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,0x24,0x01,0x00,0x00,0x50,0x1D,0x00,0x00, +0x81,0x08,0x00,0x01,0x02,0x00,0x06,0x0C,0x02,0x00,0x06,0x0C,0x01,0x00,0x04,0x04, +0x01,0x00,0x03,0x06,0x01,0x00,0x06,0x00,0x05,0x04,0x00,0x00,0x62,0xA4,0xF7,0xD5, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFC,0x4B,0x80,0xC0,0x3C,0x00,0x05,0x9A,0xB0,0x51,0xF0,0x8B,0x3C,0x00,0x05,0x9B, +0x84,0xE0,0xF0,0x8D,0x3C,0x00,0x05,0x9C,0xF0,0x8C,0x3C,0x00,0x05,0x9D,0xF0,0x8E, +0x44,0x02,0x00,0x10,0x3A,0x20,0x14,0x04,0x3A,0x20,0x94,0x24,0xA4,0x00,0xAC,0x08, +0x49,0xFF,0xED,0x97,0x81,0x00,0x3C,0x7B,0xF7,0xD9,0x49,0xFF,0xA2,0x44,0x84,0x20, +0x44,0x02,0x30,0xC8,0x44,0x20,0x02,0x40,0x49,0xFF,0xFB,0xC6,0x44,0x00,0x7F,0xFF, +0x3C,0x17,0xF9,0xF5,0x3C,0x0B,0xF7,0x8E,0xFA,0x0E,0xFE,0x0C,0x44,0x10,0x4E,0x1F, +0xE0,0x20,0x3C,0x7B,0xF7,0x92,0xE8,0x06,0x44,0x10,0x00,0x64,0x40,0x00,0x04,0x16, +0xD5,0x03,0x44,0x00,0x00,0xC8,0x84,0x40,0x44,0x92,0x30,0xC8,0x95,0x51,0x88,0xA8, +0x84,0x20,0x40,0x71,0x24,0x00,0x3C,0x43,0xF7,0x92,0x38,0x32,0x85,0x11,0x40,0xF2, +0x00,0x11,0xE0,0x6F,0x40,0x41,0xBC,0x1A,0x3C,0x4B,0xF7,0x92,0x3C,0x43,0xF7,0x8E, +0x40,0xA2,0x00,0x11,0xE1,0x43,0x40,0x41,0xBC,0x1A,0xE0,0x03,0x3C,0x4B,0xF7,0x8E, +0xE8,0x07,0x38,0x33,0x84,0x00,0x58,0x31,0x80,0x40,0x38,0x33,0x84,0x08,0x8C,0x21, +0x5A,0x18,0x12,0xE3,0x8C,0x52,0x5A,0x2A,0x40,0xDB,0x85,0x20,0x44,0xA2,0x30,0xC8, +0xFA,0x02,0x42,0xA4,0x80,0x73,0x84,0xE0,0x38,0x05,0x1C,0x00,0x96,0x36,0xC0,0x3B, +0x84,0x08,0xF0,0x82,0x50,0x0F,0x80,0x42,0xF0,0x84,0x84,0x01,0xF0,0x85,0x44,0x02, +0x11,0x83,0x84,0x60,0xF0,0x86,0x44,0x02,0x11,0x82,0xF0,0x87,0x40,0x14,0x80,0x11, +0x44,0x20,0x00,0x40,0xB6,0x7F,0xF3,0x81,0xF3,0x83,0xF3,0x88,0xF3,0x89,0x96,0x3B, +0x44,0x42,0x30,0xC8,0x80,0xA8,0x49,0xFF,0xBC,0xA6,0x2E,0x27,0xEF,0x92,0x2E,0x07, +0xEF,0x93,0xFA,0x22,0x42,0x01,0x04,0x73,0x38,0xF4,0x01,0x11,0x5E,0xF7,0x81,0x2C, +0xE9,0x04,0xA6,0x30,0x8C,0x01,0xAE,0x30,0x02,0x0F,0x80,0x21,0xE6,0x1F,0xE9,0x0B, +0x84,0x01,0x3E,0x07,0xEF,0x9E,0x3C,0x03,0xF1,0xD9,0x96,0x36,0xC0,0x04,0x84,0x06, +0x3E,0x07,0xF6,0x3B,0x8C,0xE1,0x5A,0x78,0x12,0xC1,0x8D,0x21,0x5A,0x98,0x20,0xB8, +0x3C,0xF7,0xF7,0x92,0x5E,0xF7,0x80,0xC8,0xE8,0x04,0x84,0x00,0x3E,0x07,0xEF,0x9E, +0x2E,0x17,0xEF,0x9E,0xC9,0x04,0xA6,0x30,0xE6,0x02,0xE9,0x03,0x84,0x0A,0xD5,0x05, +0x2E,0x07,0xEF,0x9D,0xC0,0x04,0x8E,0x01,0x3E,0x07,0xEF,0x9D,0x84,0x00,0x3E,0x07, +0xEF,0xA8,0x2E,0x07,0xEF,0x9D,0xC0,0x07,0x84,0x01,0x3E,0x07,0xEF,0x9C,0x3E,0x07, +0xEF,0xA8,0xD5,0x03,0x3E,0x07,0xEF,0x9C,0x84,0x00,0x3E,0x07,0xEF,0xA9,0x2E,0x07, +0xEF,0x9C,0x5A,0x00,0x01,0x05,0xA6,0x30,0xE6,0x02,0xE9,0x1B,0x84,0x01,0x3E,0x07, +0xEF,0xA9,0x3C,0x03,0xF1,0xD9,0x96,0x26,0xC0,0x08,0xA6,0x30,0xE6,0x02,0xE9,0x05, +0xC9,0x04,0x84,0x04,0x3E,0x07,0xF6,0x3B,0x3C,0x03,0xF1,0xD8,0x96,0x16,0xC0,0x6B, +0xA6,0x30,0xE6,0x02,0xE9,0x68,0xC9,0x67,0x84,0x02,0x3E,0x07,0xF6,0x39,0xD5,0x63, +0xC0,0x63,0x2E,0x0F,0xEF,0x92,0x22,0x1F,0x80,0x16,0xF0,0x8F,0x22,0x0F,0x80,0x1A, +0x84,0x40,0x8A,0x01,0xF0,0x8D,0x22,0x1F,0x80,0x18,0x22,0x0F,0x80,0x1C,0x2F,0xEF, +0xEF,0x93,0x8A,0x01,0x80,0x82,0x80,0xA2,0xF2,0x8A,0xB1,0x91,0xF0,0x8E,0x38,0x11, +0x18,0x10,0x98,0x32,0x88,0x3E,0x97,0xC9,0xE6,0xF2,0x20,0x00,0x00,0x01,0xE8,0x31, +0xF3,0x0F,0x88,0x03,0x97,0xC1,0x5C,0xF3,0x80,0x20,0xE8,0x2B,0x80,0xE1,0xFA,0x62, +0x42,0x70,0x0C,0x73,0x38,0x94,0x1D,0x11,0x3C,0xA3,0xF7,0xD9,0x40,0x74,0x80,0x13, +0x89,0x47,0x3C,0xAB,0xF7,0xD9,0x4E,0x97,0x00,0x03,0x88,0x89,0x8E,0xE5,0x97,0xFB, +0x4E,0x77,0x00,0x18,0xF3,0x0D,0xFE,0x5C,0xFA,0x61,0x40,0x10,0x8C,0x36,0xF3,0x0B, +0x88,0x23,0x96,0x4B,0xF3,0x0A,0x42,0x33,0x84,0x73,0xF1,0x0E,0xF3,0x8A,0xFE,0x0C, +0xFA,0x2F,0x40,0x00,0x04,0x16,0xF1,0x0C,0x88,0x01,0x96,0x03,0x42,0x53,0x80,0x73, +0x8C,0x42,0x5A,0x28,0x12,0xC6,0xF3,0x0A,0x40,0x02,0x04,0x0A,0x88,0x60,0x88,0xA0, +0x40,0x31,0x90,0x76,0x40,0x42,0x90,0x96,0x3C,0x3F,0xFB,0xD7,0x3C,0x4F,0xFB,0xD8, +0x84,0x01,0xD5,0x02,0x84,0x02,0xFC,0xCB,0x84,0x00,0x3E,0x07,0xEF,0xAC,0xDD,0x9E, +0xFC,0x00,0x2E,0x07,0xEF,0xAC,0x2E,0x67,0xE1,0xFD,0xC0,0x04,0x5A,0x00,0x01,0x15, +0xD5,0x3F,0x3C,0x27,0xF1,0x13,0x3C,0x17,0xF1,0x12,0x88,0x22,0x90,0x21,0x3C,0x1F, +0xFB,0xE5,0x3C,0x27,0xF1,0x15,0x3C,0x17,0xF1,0x14,0x88,0x22,0x90,0x21,0x3C,0x1F, +0xFB,0xE6,0x84,0x21,0xD5,0x1D,0x3C,0x17,0xF1,0x12,0x3C,0x37,0xF1,0x13,0x3C,0x27, +0xF1,0x15,0x88,0x61,0x3C,0x17,0xF1,0x14,0x3C,0x5D,0xFB,0xE5,0x88,0x41,0x3C,0x1D, +0xFB,0xE6,0x90,0x41,0x90,0x61,0x8A,0x22,0x44,0x40,0x00,0x64,0x8A,0xA3,0xFF,0x34, +0xFE,0x4C,0x42,0x12,0x94,0x73,0xFF,0x24,0xE0,0x24,0xE8,0x05,0x84,0x20,0x3E,0x17, +0xEF,0xAC,0xD5,0x0F,0x3C,0x13,0xF1,0xD9,0x96,0x4E,0xC1,0x03,0x3E,0x07,0xF6,0x3B, +0x84,0x01,0x3C,0x3F,0xFB,0xE5,0x3C,0x2F,0xFB,0xE6,0x3E,0x07,0xEF,0xAC,0x84,0x00, +0xFC,0x80,0xFC,0x00,0x49,0xFF,0xEC,0x05,0x50,0x30,0x04,0x80,0x84,0x20,0x84,0x40, +0x38,0x40,0x09,0x11,0x8C,0x41,0x88,0x24,0x5A,0x28,0x12,0xFC,0x50,0x00,0x00,0x24, +0x4C,0x01,0xFF,0xF7,0x44,0xF0,0x02,0x40,0x40,0x10,0xBC,0x36,0x3C,0xF7,0xF7,0x92, +0x8A,0x2F,0x50,0xF0,0x80,0xC7,0x5C,0xF7,0x81,0x8F,0xE9,0x06,0x3C,0xF7,0xF7,0x8E, +0x5E,0xF7,0x80,0x97,0xE9,0x09,0x3C,0x0D,0xFB,0xD6,0x58,0x00,0x00,0x40,0x3C,0x0F, +0xFB,0xD6,0x84,0x01,0xFC,0x80,0x84,0x00,0xFC,0x80,0xFC,0x40,0x44,0x50,0x02,0xD0, +0x3C,0x73,0xEF,0x08,0x3C,0x93,0xF1,0x00,0x9A,0x38,0x42,0x40,0x14,0x24,0x40,0x42, +0x1C,0x96,0x97,0x21,0xE2,0x89,0x3C,0x63,0xEF,0x09,0xE9,0x29,0x3C,0x03,0xF1,0x02, +0xE2,0x04,0xE9,0x25,0x9A,0x71,0x44,0xA0,0x05,0xA0,0x42,0x10,0xA8,0x24,0x40,0x10, +0x98,0x36,0x3C,0x83,0xF1,0x01,0x96,0x49,0xE2,0x28,0xE9,0x19,0x3C,0x43,0xF1,0x03, +0xE2,0x81,0xE9,0x15,0x9A,0xBA,0xFF,0x54,0x40,0x72,0x9C,0xF6,0x97,0xF9,0xE2,0xE9, +0xE9,0x0E,0xE2,0x07,0xE9,0x0C,0x9A,0x33,0x42,0x00,0x28,0x24,0x40,0x00,0x18,0x16, +0x96,0x01,0xE2,0x08,0xE9,0x04,0x40,0x02,0x00,0x06,0xFC,0xC0,0x84,0x01,0xFC,0xC0, +0x44,0x30,0x00,0x64,0x3C,0x23,0xEF,0x09,0x9A,0x51,0x9A,0x10,0x2E,0x27,0xE1,0xF3, +0x96,0x4B,0x96,0x03,0x9A,0x08,0xFE,0xD4,0xE0,0x60,0x56,0x07,0x80,0x01,0xDD,0x9E, +0x9A,0x59,0xC1,0x0C,0x8A,0x40,0x44,0x40,0x00,0x64,0x42,0x31,0x10,0x24,0x40,0x11, +0x84,0x36,0x4E,0x14,0x00,0x03,0xFE,0x4A,0x96,0x48,0x2E,0x07,0xE1,0xF2,0xE2,0x01, +0x56,0x07,0x80,0x01,0xDD,0x9E,0x44,0x30,0x02,0xD0,0x3C,0x23,0xEF,0x08,0x9A,0x10, +0xFE,0x1C,0x40,0x20,0x08,0x56,0x3C,0x33,0xF0,0xF2,0x96,0x91,0xE2,0x43,0x3C,0x03, +0xEF,0x09,0xE9,0x15,0x3C,0x33,0xF0,0xF4,0xE2,0x62,0xE9,0x11,0x9A,0x41,0x44,0x20, +0x05,0xA0,0xFE,0x54,0x40,0x10,0x80,0x36,0x3C,0x03,0xF0,0xF3,0x96,0x49,0xE2,0x20, +0xE9,0x06,0x3C,0x03,0xF0,0xF5,0x40,0x00,0x04,0x06,0xDD,0x9E,0x84,0x01,0xDD,0x9E, +0xFC,0x43,0x80,0xE0,0x84,0x00,0x10,0x0F,0x80,0x17,0x3C,0x03,0xF0,0xFD,0x3C,0x83, +0xF0,0xFC,0xF0,0x82,0x3C,0x03,0xF0,0xF7,0x3C,0xA3,0xF0,0xF6,0xF0,0x83,0x2E,0x07, +0xE1,0xE0,0xC0,0x09,0x8E,0x01,0x3E,0x07,0xE1,0xE0,0x49,0xFF,0x9D,0xBB,0x85,0x20, +0x48,0x00,0x02,0x82,0x2E,0x67,0xEF,0xAD,0xCE,0x1D,0x84,0x04,0x49,0xFF,0xF3,0xD3, +0x81,0x20,0x5A,0x08,0x01,0x18,0x80,0x26,0x84,0x04,0x49,0xFF,0xF3,0xB1,0x3C,0x13, +0xF1,0xD9,0x3E,0x67,0xEF,0xAC,0x96,0x5E,0xC1,0x0D,0x84,0x03,0x3E,0x07,0xF6,0x3B, +0x3C,0x0D,0xFB,0xE8,0xC8,0x07,0x3C,0x1D,0xF8,0xED,0x96,0x46,0xC1,0x03,0x3C,0x9F, +0xFB,0xE8,0x50,0x0F,0x80,0x17,0x49,0xFF,0xFD,0x6D,0x3C,0x4D,0xFB,0xD6,0x80,0xC0, +0x54,0x42,0x10,0x00,0xC4,0x04,0x49,0xFF,0xA0,0x1A,0xD5,0xD2,0x2E,0x07,0xEF,0xAD, +0xE6,0x05,0x4E,0xF2,0x02,0x50,0x44,0xF0,0xE2,0x94,0x38,0x07,0x81,0x01,0x40,0xF0, +0x3C,0x00,0xDD,0x0F,0x0A,0x00,0xDA,0x00,0xDE,0x01,0x0A,0x01,0x86,0x01,0x84,0xE0, +0x49,0x00,0x03,0xA2,0x3C,0x7F,0xFB,0xE9,0x3E,0x77,0xF6,0x3B,0x3E,0x77,0xF6,0x3A, +0x3E,0x77,0xF6,0x39,0x3E,0x77,0xEF,0x9F,0x5A,0x68,0x01,0x4D,0x49,0x00,0x04,0x13, +0x44,0x30,0x07,0xFF,0x44,0x20,0x32,0x64,0x80,0x83,0x80,0xA6,0x44,0x02,0x03,0xF8, +0x44,0x10,0x19,0x32,0xB6,0xDF,0x49,0x00,0x04,0x0C,0x85,0x2A,0x84,0x09,0x44,0x10, +0x0B,0xB8,0x49,0xFF,0xF3,0x4F,0x42,0x15,0x24,0x24,0x96,0x49,0x84,0x0E,0x49,0xFF, +0xF3,0x49,0xF0,0x03,0x42,0x10,0x24,0x24,0x96,0x49,0x84,0x0F,0x49,0xFF,0xF3,0x42, +0x42,0x14,0x24,0x24,0x96,0x49,0x84,0x0A,0x49,0xFF,0xF3,0x3C,0x84,0x0B,0x44,0x10, +0x13,0x88,0x49,0xFF,0xF3,0x37,0x84,0x09,0x80,0x26,0x49,0xFF,0xF3,0x41,0x84,0x0E, +0x80,0x26,0x49,0xFF,0xF3,0x3D,0x84,0x0F,0x80,0x26,0x49,0xFF,0xF3,0x39,0x84,0x0A, +0x80,0x26,0x49,0xFF,0xF3,0x35,0x84,0x0B,0x80,0x26,0x49,0xFF,0xF3,0x31,0x3C,0x07, +0xF7,0xAE,0x3C,0x17,0xF7,0xB0,0x49,0x00,0x04,0x0B,0x3E,0x67,0xEF,0xAD,0x48,0xFF, +0xFF,0x68,0x5A,0x60,0x02,0x04,0x48,0xFF,0xFF,0x64,0x84,0x0B,0x44,0x10,0x13,0x88, +0x49,0xFF,0xF3,0x10,0x84,0x0B,0x84,0x21,0x49,0xFF,0xF3,0x1A,0xD5,0x0C,0x84,0x0B, +0x49,0xFF,0xF3,0x31,0x5A,0x08,0x01,0x04,0x84,0x04,0xD5,0x06,0x84,0x02,0xC6,0x04, +0x5A,0x68,0x02,0x07,0x84,0x03,0x3E,0x07,0xEF,0xAD,0x48,0xFF,0xFF,0x4A,0x3C,0x07, +0xF7,0xAE,0x3C,0x17,0xF7,0xB0,0x49,0x00,0x03,0xE3,0x48,0xFF,0xFF,0x42,0x84,0x0B, +0x49,0xFF,0xF3,0x19,0x5A,0x00,0x01,0xEA,0x4E,0x63,0xFF,0x3B,0x49,0x00,0x03,0xAD, +0xC0,0x03,0x84,0x01,0xD5,0x02,0x84,0x03,0x3E,0x07,0xEF,0x9F,0x2E,0x07,0xF6,0x3B, +0x5A,0x08,0x04,0x13,0x2E,0x07,0xF6,0x39,0x5A,0x08,0x02,0x0F,0x2E,0x07,0xEF,0x9F, +0x5A,0x08,0x03,0x06,0x84,0x00,0x3E,0x07,0xF6,0x3B,0xD5,0x06,0x5A,0x08,0x01,0x05, +0x84,0x00,0x3E,0x07,0xF6,0x39,0x3C,0x0D,0xF8,0xED,0x54,0x10,0x00,0x01,0xC9,0x03, +0x3E,0x17,0xF6,0x3B,0x96,0x0E,0xC8,0x03,0x3E,0x07,0xF6,0x39,0x2E,0x07,0xF6,0x3B, +0xC8,0x07,0x2E,0x07,0xF6,0x3A,0xC8,0x04,0x2E,0x07,0xF6,0x39,0xC0,0x05,0x85,0x21, +0x3C,0x9F,0xFB,0xE9,0xD5,0x27,0x81,0x20,0xD5,0x25,0x49,0xFF,0xFE,0x3C,0x81,0x20, +0x4E,0x03,0xFF,0x2B,0x3C,0x17,0xF7,0x8E,0x3C,0xF4,0x05,0xAF,0xE0,0x2F,0xE8,0x1E, +0x2E,0x07,0xEF,0x93,0x3E,0x07,0xEF,0xAF,0x2E,0x07,0xEF,0x92,0x3E,0x07,0xEF,0xAE, +0x3C,0x03,0xF7,0x92,0x3C,0x0B,0xF7,0xDA,0x3C,0x03,0xF7,0xD9,0x3C,0x0B,0xF7,0xD8, +0x84,0x01,0x3E,0x07,0xEF,0xB6,0x3C,0x0D,0xFB,0xD6,0x58,0x00,0x10,0x00,0x3C,0x0F, +0xFB,0xD6,0x49,0xFF,0x9F,0x24,0x48,0x00,0x01,0x5F,0x4E,0x63,0xFE,0xDA,0x48,0xFF, +0xFF,0x04,0x49,0x00,0x03,0x4A,0xC0,0x55,0x2E,0xF7,0xEF,0xA9,0xE9,0x52,0x84,0x01, +0x3E,0x07,0xEF,0x9F,0x84,0x0A,0x49,0xFF,0xF2,0xA6,0x80,0xC0,0x81,0x20,0xC8,0x41, +0x49,0xFF,0xFD,0xB8,0x5A,0x08,0x01,0x06,0xAF,0xB8,0x3E,0x67,0xF6,0x3B,0xD5,0x74, +0x2E,0x47,0xF6,0x3B,0x54,0x12,0x00,0xFD,0x5A,0x18,0x01,0x10,0x3C,0x0D,0xFB,0xE9, +0xC8,0x0C,0x3C,0x4D,0xF8,0xED,0x97,0x06,0xCC,0x04,0x3E,0x47,0xF6,0x3B,0xD5,0x72, +0x3C,0x1F,0xFB,0xE9,0x81,0x01,0xD5,0x75,0xF0,0x02,0x84,0x2A,0xFE,0x0C,0x96,0x41, +0x84,0x04,0x49,0xFF,0xF2,0x57,0x84,0x04,0x84,0x21,0x49,0xFF,0xF2,0x61,0x84,0x0A, +0x84,0x20,0x49,0xFF,0xF2,0x5D,0x84,0x09,0x84,0x20,0x49,0xFF,0xF2,0x59,0x84,0x0E, +0x84,0x20,0x49,0xFF,0xF2,0x55,0x84,0x0F,0x84,0x20,0x49,0xFF,0xF2,0x51,0x84,0x0B, +0x84,0x20,0x49,0xFF,0xF2,0x4D,0x84,0x00,0x3E,0x07,0xEF,0xAD,0x48,0x00,0x01,0x0C, +0x3C,0x13,0xF1,0xD9,0x96,0x6E,0xC1,0x46,0x84,0x05,0x3E,0x07,0xF6,0x3B,0xD5,0x42, +0x84,0x03,0x3E,0x07,0xEF,0x9F,0x84,0x09,0x49,0xFF,0xF2,0x55,0x80,0xC0,0xC8,0x3C, +0x80,0x07,0x49,0x00,0x04,0x23,0x8E,0x01,0x5C,0x80,0x00,0x01,0xA6,0x38,0x5A,0x08, +0x01,0x09,0x84,0x0F,0x3C,0x6B,0xF7,0x96,0x49,0xFF,0xF2,0x5F,0x3C,0x0B,0xF7,0x96, +0x84,0x0E,0x49,0xFF,0xF2,0x40,0xC8,0x04,0x5A,0x88,0x01,0x25,0xD5,0x07,0x84,0x0F, +0x49,0xFF,0xF2,0x39,0x5A,0x08,0x01,0x26,0xD5,0xF8,0xA6,0x38,0x5A,0x08,0x01,0x22, +0x3C,0x03,0xF1,0xD8,0x96,0x1E,0xC0,0x0A,0x84,0x0E,0x49,0xFF,0xF2,0x2C,0xC8,0x06, +0x84,0x23,0x3E,0x17,0xF6,0x39,0x81,0x00,0xD5,0x14,0x3C,0x03,0xF1,0xD8,0x96,0x26, +0xC0,0x09,0x84,0x0F,0x49,0xFF,0xF2,0x1F,0x5A,0x08,0x01,0x05,0x84,0x04,0x3E,0x07, +0xF6,0x39,0x85,0x00,0xD5,0x06,0x3C,0x03,0xF1,0xD8,0x96,0x26,0xC8,0xF8,0xD5,0xFA, +0x3C,0x07,0xF7,0x94,0x3C,0x97,0xF7,0x9F,0x3C,0x67,0xF7,0x99,0x3C,0xA7,0xF7,0xA5, +0xF0,0x82,0x80,0x26,0x80,0x09,0xF2,0x02,0x80,0x6A,0x49,0xFF,0xFD,0x90,0x5A,0x08, +0x01,0x13,0x5A,0x88,0x01,0x0E,0xA6,0x38,0xC8,0x0E,0x3C,0x43,0xF1,0xD9,0x97,0x3E, +0x97,0x21,0xC4,0x08,0x84,0x27,0x3E,0x17,0xF6,0x3B,0x81,0x00,0xD5,0x04,0x85,0x00, +0xD5,0x02,0x81,0x04,0x80,0x06,0x80,0x2A,0x49,0xFF,0xFD,0xB4,0xC8,0x0E,0xA6,0x78, +0xC1,0x0C,0x3C,0x43,0xF1,0xD8,0x97,0x2E,0x97,0x21,0xC4,0x06,0x84,0x25,0x3E,0x17, +0xF6,0x39,0x81,0x00,0xD5,0x02,0x81,0x04,0x80,0x09,0x80,0x26,0xF2,0x02,0x80,0x6A, +0x49,0xFF,0xFD,0xB0,0xC8,0x0E,0xA6,0x78,0xC1,0x0C,0x3C,0x43,0xF1,0xD8,0x97,0x36, +0x97,0x21,0xC4,0x06,0x84,0x26,0x3E,0x17,0xF6,0x39,0x81,0x00,0xD5,0x02,0x81,0x04, +0x80,0x09,0x80,0x26,0x49,0xFF,0xFD,0xB1,0x5A,0x08,0x01,0x12,0xA6,0x38,0x5A,0x08, +0x01,0x0F,0x5A,0x88,0x01,0x0A,0x3C,0x43,0xF1,0xD8,0x97,0x3E,0x97,0x21,0xC4,0x06, +0x84,0x07,0x3E,0x07,0xF6,0x39,0x85,0x00,0xD5,0x02,0x81,0x04,0x80,0x07,0x49,0x00, +0x04,0xDE,0x44,0x12,0x2D,0xAC,0xFA,0x58,0x44,0x02,0x2D,0x50,0x49,0x00,0x01,0xF5, +0x3E,0x07,0xEF,0x13,0x49,0x00,0x02,0x1E,0x3E,0x07,0xEF,0x11,0x3C,0x0D,0xF8,0xED, +0x54,0x10,0x00,0x03,0x5A,0x18,0x03,0x06,0xA6,0x78,0xE6,0x22,0xE8,0x0C,0xD5,0x0C, +0x5A,0x18,0x01,0x05,0xA6,0x78,0xC1,0x13,0xD5,0x11,0x5A,0x18,0x02,0x10,0xA6,0x78, +0x5A,0x10,0x01,0x03,0x85,0x00,0x2E,0x27,0xF6,0x3B,0x5A,0x28,0x02,0x09,0x5A,0x18, +0x01,0x07,0x84,0x20,0x3E,0x17,0xF6,0x3B,0xD5,0x02,0x85,0x00,0x2E,0x17,0xE1,0xFC, +0xC9,0x06,0xA6,0x78,0xC9,0x04,0x3E,0x17,0xF6,0x3B,0x81,0x01,0x2E,0x17,0xE1,0xF0, +0xC9,0x07,0xA6,0xB8,0x5A,0x28,0x01,0x05,0x3E,0x17,0xF6,0x39,0x81,0x01,0x54,0x10, +0x00,0x01,0xC9,0x03,0x3E,0x17,0xF6,0x3B,0x96,0x0E,0xC8,0x03,0x3E,0x07,0xF6,0x39, +0x2E,0x07,0xF6,0x3B,0xC8,0x07,0x2E,0x07,0xF6,0x3A,0xC8,0x04,0x2E,0x07,0xF6,0x39, +0xC0,0x05,0x84,0x81,0x3C,0x4F,0xFB,0xE9,0x81,0x04,0x49,0xFF,0x9D,0xC8,0x81,0x28, +0xD5,0x02,0x81,0x24,0x80,0x09,0xFC,0xC3,0xFC,0x00,0x84,0x40,0x44,0x52,0x11,0xA8, +0x44,0x40,0x00,0x40,0x2E,0x37,0xEF,0xD8,0xE2,0x43,0xE8,0x0D,0x38,0x32,0x88,0x00, +0x88,0x61,0x50,0x31,0x80,0x40,0x40,0x31,0x90,0xD6,0x38,0x60,0x08,0x08,0x8C,0x41, +0x96,0x90,0xD5,0xF1,0xFC,0x80,0x2E,0x17,0xEF,0xD8,0x44,0x22,0x11,0xA8,0x38,0x01, +0x04,0x08,0x9C,0x09,0x96,0x00,0x5C,0xF0,0x00,0x27,0x3E,0x07,0xEF,0xD8,0xE8,0x0E, +0x44,0x12,0x12,0xC8,0x3C,0x23,0xF7,0x97,0x38,0x20,0x81,0x09,0x44,0x12,0x12,0x78, +0x3C,0x23,0xF7,0xA4,0x38,0x20,0x81,0x09,0xDD,0x9E,0x44,0x00,0x7F,0xFF,0x3C,0x0B, +0xF8,0x93,0x3C,0x0B,0xF8,0x6B,0xDD,0x9E,0xFC,0x00,0x44,0x52,0x00,0x44,0x84,0x60, +0x96,0x98,0x0A,0x62,0x80,0x01,0x5A,0x20,0x10,0x07,0xE2,0x26,0xA5,0x28,0xE9,0x19, +0x81,0xE4,0xD5,0x04,0xE2,0x26,0xE9,0x1B,0x85,0xFF,0xE2,0x2F,0xE8,0x12,0x5A,0x08, +0x01,0x06,0x52,0x01,0x80,0x40,0x96,0x2F,0xFC,0x80,0x5A,0x08,0x02,0x05,0x50,0x01, +0x00,0x20,0xD5,0x05,0x5A,0x08,0x03,0x0A,0x52,0x01,0x00,0x20,0x96,0x00,0xFC,0x80, +0x8C,0x61,0x5A,0x38,0x11,0xDF,0xD5,0x03,0x80,0x02,0xFC,0x80,0x44,0x00,0x00,0xFF, +0xFC,0x80,0x94,0x4E,0x5C,0xF1,0x80,0x41,0x88,0x41,0x84,0x20,0xE8,0x0C,0xCB,0x04, +0x44,0x10,0x00,0x64,0xD5,0x08,0x8C,0x61,0x44,0x12,0x00,0x24,0x90,0x61,0x88,0x61, +0x00,0x11,0xFF,0xFF,0x38,0x10,0x09,0x09,0xDD,0x9E,0xFC,0x40,0xEE,0xB0,0xF1,0x83, +0x94,0x41,0xE0,0x22,0xF0,0x82,0xE9,0x07,0x40,0x10,0x88,0x36,0xC1,0x05,0x8C,0x21, +0x96,0x48,0xD5,0x02,0x84,0x21,0x42,0xF1,0x04,0x24,0x5E,0xF7,0x80,0x41,0xE9,0x03, +0x8E,0x21,0x96,0x48,0x42,0x00,0x88,0x24,0x96,0x00,0xF0,0x81,0xF0,0x02,0x52,0x90, +0x00,0x02,0xF0,0x01,0x88,0x09,0x54,0x90,0x00,0xFF,0x84,0x00,0x80,0xA0,0xD2,0x11, +0xB1,0x84,0x84,0x80,0x99,0xF0,0x97,0xA0,0xE2,0xC1,0xE8,0x07,0x38,0x61,0x94,0x00, +0x38,0x63,0x90,0x08,0x8C,0x81,0xD5,0xF8,0x8C,0xA1,0x97,0x68,0x88,0x01,0xD5,0xF0, +0x84,0xC0,0xF0,0x02,0x4C,0x60,0x00,0x79,0xB0,0x14,0x83,0x80,0x80,0xE0,0x85,0x00, +0x54,0xA4,0x00,0xFF,0xE3,0x49,0xE8,0x62,0xF0,0x03,0xCE,0x11,0xA6,0xC0,0xB0,0x04, +0x50,0x31,0x80,0x40,0x38,0x00,0x20,0x00,0x8A,0x60,0x44,0x00,0x00,0x40,0x40,0x01, +0x80,0x76,0x96,0xD8,0x5C,0xF1,0x80,0x21,0xE8,0x15,0xD5,0x18,0x38,0x30,0x18,0x00, +0xB0,0x44,0x40,0x04,0x18,0x00,0x88,0x01,0x50,0x31,0x80,0x40,0x00,0x00,0x7F,0xFF, +0x8A,0x60,0x44,0x00,0x00,0x40,0x40,0x01,0x80,0x76,0x96,0xD8,0x5C,0xF1,0x80,0x21, +0xE9,0x12,0x52,0x31,0x80,0x40,0x96,0xD8,0xCE,0x0E,0xB0,0x14,0x84,0x20,0x80,0x4A, +0x49,0xFF,0xFF,0x81,0x4E,0xA2,0x00,0x30,0xA4,0x78,0x02,0x03,0xFF,0xFF,0x88,0x01, +0xAC,0x38,0xD5,0x29,0xB0,0x14,0x5A,0x68,0x01,0x0D,0x80,0x26,0x80,0x4A,0x49,0xFF, +0xFF,0x72,0x4E,0xA2,0x00,0x21,0x02,0x03,0x80,0x3F,0x02,0x13,0xFF,0xFF,0xD5,0x12, +0x84,0x21,0x80,0x4A,0x49,0xFF,0xFF,0x67,0x4E,0xA3,0x00,0x0A,0x02,0x1F,0x80,0x68, +0x02,0x0F,0x80,0x28,0x88,0x01,0x12,0x0F,0x80,0x68,0xD5,0x0D,0x02,0x03,0x80,0x3F, +0xA4,0x78,0xE2,0x20,0x40,0x10,0x3C,0x1B,0x02,0x03,0x80,0x40,0x81,0xE1,0x88,0x0F, +0x12,0x03,0x80,0x40,0x8D,0x01,0x8C,0xE2,0xD5,0x9C,0x84,0xA0,0xC6,0x0A,0x4C,0x54, +0x80,0x09,0x02,0x0E,0x00,0x40,0x8C,0xA1,0x1A,0x0E,0x00,0x01,0x97,0x68,0xD5,0xF8, +0x8C,0xC1,0x97,0xB0,0xD5,0x87,0x5A,0x68,0x01,0x04,0x8F,0x21,0xD5,0x03,0x50,0x94, +0x80,0x3F,0xB0,0x14,0xF1,0x01,0x38,0x00,0x25,0x01,0x40,0x00,0x04,0x16,0x96,0x00, +0xED,0x50,0xFC,0xC0,0xFC,0x00,0x4E,0x17,0x00,0x0E,0x4E,0x05,0x00,0x07,0x44,0x20, +0x03,0xE8,0xFE,0x84,0x84,0x01,0xD5,0x0D,0x44,0x2F,0xFC,0x18,0xFE,0x84,0x84,0x04, +0xD5,0x08,0xC1,0x11,0x4E,0x05,0x00,0x0B,0x44,0x2F,0xFC,0x18,0xFE,0x84,0x84,0x02, +0x40,0x11,0x04,0x36,0x49,0xFF,0xFE,0xEA,0xFC,0x80,0x44,0x20,0x03,0xE8,0xFE,0x84, +0x84,0x03,0xD5,0xF7,0xC0,0x07,0x4E,0x06,0x00,0x04,0xFA,0x00,0xFC,0x80,0x44,0x00, +0x00,0x30,0xFC,0x80,0xFC,0x00,0x84,0x00,0x3E,0x07,0xEF,0x13,0x3E,0x07,0xEF,0x11, +0x84,0x00,0xFA,0xD8,0x3C,0x0B,0xF7,0x98,0x3C,0x0B,0xF7,0x9E,0x3C,0x0B,0xF7,0x9F, +0x3C,0x0B,0xF7,0x99,0x3C,0x0B,0xF7,0x94,0x3C,0x0B,0xF7,0xA5,0x3C,0x0B,0xF7,0x9B, +0x3C,0x0B,0xF7,0x9D,0x3C,0x0B,0xF7,0x9A,0x3C,0x0B,0xF7,0xA2,0x3C,0x0B,0xF7,0x9C, +0x3C,0x0B,0xF7,0xA3,0x3C,0x0B,0xF7,0xA0,0x3C,0x0B,0xF7,0x95,0x84,0x1F,0x80,0x46, +0x3E,0x07,0xEF,0x12,0x84,0x20,0x44,0x02,0x2D,0x50,0x49,0xFF,0xF5,0x6D,0x80,0x46, +0x84,0x20,0x84,0xC4,0x44,0x02,0x2D,0xAC,0x49,0xFF,0xF5,0x66,0x80,0x46,0x84,0x20, +0x44,0x02,0x11,0x54,0x49,0xFF,0xF5,0x60,0x84,0x20,0x80,0x46,0x44,0x02,0x11,0x5C, +0x49,0xFF,0xF5,0x5A,0xFC,0x80,0xFC,0x40,0x5C,0xF1,0x00,0x29,0xE8,0x03,0x80,0xA2, +0xD5,0x02,0xFA,0xB8,0x84,0x60,0x44,0x92,0x12,0x28,0x44,0xA2,0x11,0xD8,0x80,0xC3, +0xE0,0x65,0xE8,0x12,0x95,0x19,0x40,0x72,0x24,0x00,0x88,0x8A,0x22,0x83,0x80,0x00, +0x38,0x80,0x0D,0x09,0x22,0x82,0x00,0x00,0x38,0x80,0x8D,0x09,0x8C,0x61,0xAD,0xB8, +0xAD,0xA0,0x96,0xD8,0xD5,0xEE,0x2E,0x17,0xEF,0xDC,0x5C,0xF0,0x80,0x29,0x80,0x01, +0xE9,0x02,0xFA,0x18,0x96,0x40,0xE2,0x41,0x40,0x01,0x3C,0x1B,0x96,0x00,0xFC,0xC0, +0x84,0x20,0x2E,0x07,0xEF,0xDD,0x3E,0x17,0xEF,0xDD,0xDD,0x9E,0x44,0x22,0x04,0x14, +0xA4,0xD1,0xA5,0x10,0x8A,0x64,0xAC,0xC0,0xA4,0x13,0xA4,0x92,0x8A,0x02,0xAC,0x08, +0xDD,0x9E,0x84,0x00,0x3C,0x0B,0xF7,0xF3,0x3E,0x07,0xEF,0x10,0xDD,0x9E,0x3C,0x0F, +0xFB,0xDD,0x44,0x02,0x2D,0xA0,0xAC,0x40,0x84,0x21,0xAC,0x81,0xAC,0xC3,0xAD,0x04, +0xAF,0x44,0xAC,0x45,0xDD,0x9E,0x3C,0x17,0xF1,0x12,0x3C,0x07,0xF1,0x13,0x2E,0x27, +0xE1,0xFE,0x8A,0x01,0x44,0x10,0x00,0x64,0xFE,0x54,0xE0,0x01,0xE8,0x08,0x3C,0xF7, +0xF1,0x14,0x3C,0x27,0xF1,0x15,0x8A,0x4F,0xE0,0x41,0xE9,0x15,0x5E,0xF0,0x03,0xE8, +0xE8,0x14,0x3C,0xF7,0xF1,0x14,0x3C,0x07,0xF1,0x15,0x8A,0x0F,0x5E,0xF0,0x03,0xE8, +0xE8,0x0C,0x3C,0x03,0xF1,0xD9,0x96,0x16,0x96,0x01,0xC0,0x08,0x84,0x02,0x3E,0x07, +0xF6,0x3B,0xD5,0x03,0x84,0x01,0xDD,0x9E,0x84,0x00,0xDD,0x9E,0xFC,0x41,0x52,0x00, +0x19,0x32,0x96,0xC3,0x3C,0x03,0xF7,0xF3,0x52,0x10,0xB2,0x64,0x8C,0x01,0x84,0x81, +0x96,0x8B,0x96,0x01,0x3E,0x47,0xEF,0xDE,0x3C,0x3B,0xF7,0x97,0x3C,0x2B,0xF7,0xA4, +0x3C,0x0B,0xF7,0xF3,0x5A,0x08,0x01,0x46,0x84,0x00,0x3C,0x3B,0xF1,0x12,0x3C,0x3B, +0xF1,0x13,0x3C,0x2B,0xF1,0x14,0x3C,0x2B,0xF1,0x15,0x3C,0x3B,0xF1,0x16,0x3C,0x2B, +0xF1,0x17,0x3C,0x0F,0xF8,0x8D,0x3C,0x0F,0xF8,0x8E,0x3E,0x07,0xEF,0xD8,0x3E,0x07, +0xEF,0xDF,0x3E,0x07,0xEF,0x10,0x3C,0x3B,0xF7,0xA1,0x3C,0x2B,0xF7,0x93,0x3C,0x3B, +0xF7,0xF2,0x3C,0x2B,0xF7,0xF1,0x44,0x72,0x12,0x28,0x80,0x20,0x44,0x62,0x11,0xD8, +0x44,0x52,0x12,0xC8,0x44,0x42,0x12,0x78,0x38,0x13,0x81,0x09,0x38,0x13,0x01,0x09, +0x38,0x12,0x81,0x09,0x38,0x12,0x01,0x09,0x8C,0x01,0x5A,0x08,0x28,0xF7,0x84,0x01, +0x3C,0x3B,0xF8,0x1C,0x3C,0x2B,0xF7,0xF4,0x3C,0x3B,0xF7,0x9F,0x3C,0x2B,0xF7,0x99, +0x3C,0x3B,0xF8,0x6C,0x3C,0x2B,0xF8,0x44,0x3E,0x07,0xEF,0xDC,0x48,0x00,0x00,0xB5, +0x2E,0x77,0xEF,0xD8,0x3C,0x3B,0xF7,0x94,0xE6,0xFE,0x3C,0x2B,0xF7,0xA5,0x4E,0xF2, +0x00,0xA1,0x3C,0xA7,0xF7,0x93,0x3C,0x97,0xF7,0xA1,0x40,0x01,0x28,0x01,0x40,0x11, +0xA4,0x01,0x42,0xF0,0x00,0x24,0x42,0xF0,0x84,0x73,0x5E,0xF7,0x84,0xCA,0x4E,0xF3, +0x00,0x9C,0x2E,0x47,0xEF,0xDC,0x5C,0xF2,0x00,0x27,0xE8,0x0A,0x44,0x52,0x12,0x28, +0x38,0x32,0x91,0x09,0x44,0x52,0x11,0xD8,0x38,0x22,0x91,0x09,0xD5,0x07,0x44,0x50, +0x7F,0xFF,0x3C,0x5B,0xF8,0x43,0x3C,0x5B,0xF8,0x1B,0x3C,0xF7,0xF1,0x12,0x8C,0x81, +0x97,0xA0,0xE0,0x6F,0x3E,0x67,0xEF,0xDC,0xE8,0x03,0x3C,0x3B,0xF1,0x12,0x3C,0x47, +0xF1,0x13,0xE0,0x83,0xE8,0x03,0x3C,0x3B,0xF1,0x13,0x3C,0xF7,0xF1,0x14,0xE0,0x4F, +0xE8,0x03,0x3C,0x2B,0xF1,0x14,0x3C,0x37,0xF1,0x15,0xE0,0x62,0xE8,0x03,0x3C,0x2B, +0xF1,0x15,0x80,0x41,0x4E,0x14,0x00,0x03,0xFE,0x8A,0x3C,0x3D,0xF8,0x8D,0x88,0x43, +0x3C,0x2F,0xF8,0x8D,0x80,0x40,0x4E,0x04,0x00,0x03,0xFE,0x82,0x3C,0x3D,0xF8,0x8E, +0x88,0x43,0x3C,0x2F,0xF8,0x8E,0x49,0xFF,0xFE,0x67,0x81,0x00,0x3E,0x07,0xEF,0xE1, +0x5A,0x60,0x02,0x3D,0xE6,0xC3,0xE9,0x3C,0x2E,0x67,0xEF,0xE0,0x44,0xF0,0x00,0x40, +0x50,0x63,0x00,0x40,0x8A,0xC0,0x40,0xF3,0x3C,0xD6,0x96,0xF0,0xE6,0x64,0xE9,0x11, +0x96,0x30,0x5E,0xF0,0x00,0x3D,0xE8,0x0D,0x3C,0x27,0xF7,0xF1,0x3C,0x07,0xF7,0xF2, +0x8A,0x4A,0x8A,0x09,0xFE,0x94,0x42,0x20,0x00,0x73,0x5E,0xF1,0x04,0xCA,0xE8,0x02, +0xCF,0x1F,0x3C,0x07,0xF7,0xF1,0x3C,0x17,0xF7,0xF2,0x40,0x05,0x00,0x01,0x40,0x14, +0x84,0x01,0xF3,0x81,0x49,0xFF,0xFE,0x38,0x49,0xFF,0xFD,0x17,0xF3,0x01,0xE6,0x68, +0xE9,0x05,0x97,0xB0,0x5E,0xF3,0x00,0x39,0xE9,0x19,0x3C,0x03,0xF7,0xA1,0x3C,0x0B, +0xF7,0xF2,0x3C,0x03,0xF7,0x93,0x3C,0x0B,0xF7,0xF1,0x3E,0x87,0xEF,0xE0,0x3C,0x03, +0xF7,0x97,0x3C,0x0B,0xF7,0xA1,0x3C,0x03,0xF7,0xA4,0x3C,0x0B,0xF7,0x93,0xD5,0x0C, +0x5A,0x78,0x1E,0x0B,0x3E,0x47,0xEF,0x10,0xD5,0x07,0x2E,0x07,0xEF,0xDF,0x8C,0x01, +0x3E,0x07,0xEF,0xDF,0xD5,0xE3,0xFC,0xC1,0xFC,0x48,0x51,0xCF,0x80,0x60,0x2E,0x27, +0xEF,0xD8,0x14,0x0E,0x7F,0xE9,0x3C,0x17,0xF7,0xA1,0x3C,0x07,0xF7,0x93,0xE6,0x5F, +0x3C,0x1B,0xF1,0x18,0x3C,0x0B,0xF1,0x19,0xE8,0x0B,0x3C,0x37,0xF7,0xF1,0x3C,0x27, +0xF7,0xF2,0x8A,0x03,0x8A,0x22,0x49,0xFF,0xFD,0xF7,0x49,0xFF,0xFC,0xD6,0x3C,0x57, +0xF1,0x13,0x3C,0x17,0xF1,0x12,0x3C,0x0D,0xF8,0x8D,0x3C,0x3D,0xF8,0x8E,0x3C,0x0F, +0xF8,0x8F,0x3C,0x3F,0xF8,0x90,0x9A,0xA9,0xD1,0x07,0x44,0x10,0x07,0xD0,0xFE,0x44, +0x40,0x10,0x88,0x36,0xD5,0x02,0x84,0x20,0x3C,0x1F,0xF8,0x8D,0x3C,0x57,0xF1,0x15, +0x3C,0x17,0xF1,0x14,0x84,0x00,0xD1,0x07,0x44,0x00,0x07,0xD0,0xFE,0x1C,0x9A,0xE9, +0x40,0x00,0x0C,0x16,0x3C,0x0F,0xF8,0x8E,0x3C,0x04,0x05,0xDD,0xE0,0x40,0xE8,0x04, +0x8A,0xA1,0xE0,0xA0,0xE9,0x09,0x2E,0x07,0xEF,0x10,0x5A,0x00,0x01,0x06,0x2E,0x07, +0xEF,0xD8,0xE6,0x02,0xE8,0x09,0x84,0x00,0x84,0xE0,0x3E,0x07,0xEF,0x10,0x14,0x7E, +0x7F,0xEE,0x48,0x00,0x00,0xDA,0x3C,0x0D,0xFB,0xDD,0x15,0xFE,0x7F,0xEA,0x84,0xE0, +0xB4,0x00,0x14,0x7E,0x7F,0xEE,0x14,0x7E,0x7F,0xEC,0xA4,0x00,0x80,0xC7,0x8C,0x07, +0x92,0x03,0x94,0x03,0x41,0xFF,0x80,0x01,0x15,0xFE,0x7F,0xEB,0x41,0xFF,0x80,0x01, +0x15,0xFE,0x7F,0xED,0x3C,0x0D,0xFB,0xDD,0xB4,0x20,0xA4,0x48,0xE2,0xC1,0x4E,0xF2, +0x00,0xB8,0x84,0x2C,0xA0,0x01,0x42,0x93,0x04,0x24,0x88,0x09,0x00,0xA0,0x00,0x05, +0x5A,0xA8,0x01,0x4F,0x84,0x00,0x14,0x0E,0x7F,0xEF,0x85,0x00,0x44,0x4F,0xFF,0xC0, +0x80,0x24,0x50,0x0E,0x7F,0xC0,0x14,0x4E,0x7F,0xE8,0x49,0xFF,0xFC,0x4F,0x3C,0x1D, +0xFB,0xDD,0x2E,0x07,0xEF,0xD8,0x04,0x4E,0x7F,0xE8,0xA0,0x49,0x88,0x29,0xA6,0x8C, +0xB4,0x61,0xC2,0x0D,0x44,0x12,0x11,0xA8,0x49,0xFF,0xFC,0xB9,0xE3,0x00,0x04,0x4E, +0x7F,0xE8,0xE8,0x05,0x96,0x60,0x14,0x1E,0x7F,0xEF,0x81,0x00,0x8C,0x83,0x97,0x22, +0x5A,0x48,0x41,0xE0,0x04,0x0E,0x7F,0xEB,0x5C,0xF4,0x00,0x3D,0x38,0x80,0x18,0x08, +0xE9,0x1A,0x50,0x0E,0x7F,0xC0,0x20,0x1E,0x7F,0xBC,0x49,0xFF,0xFC,0x27,0x3C,0x0D, +0xFB,0xDD,0x2E,0x27,0xEF,0xD8,0xA0,0x41,0x88,0x29,0xA6,0x0C,0xB4,0x21,0xC2,0x06, +0x44,0x32,0x11,0xA8,0x49,0xFF,0xFC,0x93,0x80,0x40,0x04,0x0E,0x7F,0xED,0x38,0x20, +0x18,0x08,0xD5,0x06,0x04,0x1E,0x7F,0xED,0x84,0x00,0x38,0x00,0x98,0x08,0x04,0x0E, +0x7F,0xEB,0x38,0x80,0x18,0x00,0x5C,0xF4,0x00,0x3C,0xE9,0x56,0x04,0x0E,0x7F,0xED, +0x38,0x00,0x18,0x00,0x5C,0xF0,0x00,0x37,0xE9,0x4F,0x5A,0xA8,0x01,0x12,0x2E,0x27, +0xE1,0xF1,0x44,0x10,0x00,0x64,0xFE,0x54,0x3C,0x2D,0xF8,0x90,0xE0,0x41,0xE8,0x08, +0x3C,0x03,0xF1,0xD8,0x96,0x0E,0xC0,0x40,0x3E,0xA7,0xF6,0x39,0xD5,0x3D,0x3C,0x1D, +0xFB,0xDD,0xA0,0xC9,0x88,0x69,0xA6,0x5E,0x5A,0x18,0x01,0x29,0x3C,0x2D,0xF8,0x8D, +0xC2,0x25,0x3C,0x5D,0xF8,0x8E,0xC5,0x22,0x00,0x91,0x80,0x09,0x00,0xA1,0x80,0x08, +0x01,0xE1,0x80,0x0A,0xA6,0xDF,0x44,0x10,0x07,0xD0,0x44,0x43,0x34,0x50,0x42,0xF1, +0x04,0x24,0xFE,0xE4,0xE2,0x6F,0xE9,0x20,0xFA,0x64,0xFE,0x9C,0x42,0xF5,0x04,0x24, +0xE2,0x4F,0xE9,0x1A,0x42,0xF2,0x84,0x24,0x42,0x44,0x90,0x24,0xE2,0x8F,0xE9,0x14, +0xFE,0xEC,0x42,0x1F,0x04,0x24,0xE2,0x61,0xE9,0x0F,0x88,0x08,0x04,0x1E,0x7F,0xEC, +0x90,0x01,0xE0,0x20,0xE8,0x09,0x96,0x00,0x50,0x1E,0x7F,0xB0,0x14,0x0E,0x7F,0xEC, +0xB4,0xE1,0x14,0x6E,0x7F,0xEE,0x8C,0xC1,0x97,0xB0,0x48,0xFF,0xFF,0x45,0x3E,0x77, +0xEF,0xDD,0x05,0xFE,0x7F,0xEA,0x49,0xFF,0xFD,0x86,0xCF,0x03,0x84,0x02,0xD5,0x14, +0x3C,0x0D,0xFB,0xDD,0x81,0x1C,0xB2,0x4E,0x84,0x2C,0xA0,0x01,0x42,0x01,0x04,0x73, +0xB2,0x29,0xA6,0x05,0xE6,0x02,0xAE,0x08,0xE8,0xF2,0x5A,0x00,0x01,0x06,0x84,0x00, +0x3E,0x07,0xF6,0x39,0x84,0x01,0x51,0xFE,0x7F,0xE0,0xFC,0xC0,0x38,0x20,0x80,0x00, +0x88,0x01,0xA6,0x01,0x9A,0x10,0x4E,0x04,0x00,0x03,0xFE,0x02,0x96,0x00,0xDD,0x9E, +0xA6,0x80,0xA6,0xC8,0xAE,0xC0,0xAE,0x88,0xDD,0x9E,0xFC,0x40,0x3C,0x13,0xF1,0x12, +0x3C,0x03,0xF1,0x13,0x44,0x72,0x12,0x28,0x8A,0x01,0x3C,0x0B,0xF7,0x98,0x3C,0x13, +0xF1,0x14,0x3C,0x03,0xF1,0x15,0x44,0x62,0x11,0xD8,0x8A,0x01,0x3C,0x0B,0xF7,0x9E, +0x44,0x12,0x11,0x2C,0x44,0x02,0x11,0x20,0xFD,0x10,0x49,0xFF,0x99,0x4B,0x80,0x07, +0x80,0x26,0x44,0x22,0x11,0x2E,0x44,0x32,0x11,0x22,0x49,0xFF,0x99,0x43,0x2E,0x27, +0xEF,0xDC,0x44,0x32,0x11,0x3A,0x8E,0x41,0x94,0x51,0x98,0x39,0x44,0x22,0x11,0x18, +0x88,0x26,0x49,0xFF,0x99,0x37,0x84,0xC0,0x44,0x92,0x2D,0x50,0x44,0x72,0x2D,0xAC, +0x40,0x03,0x24,0x00,0x98,0x77,0xFD,0x10,0x8C,0xC2,0x49,0xFF,0x99,0x2B,0x5A,0x68, +0x50,0xF9,0xFC,0xC0,0xFC,0x21,0xFA,0x04,0x10,0x0F,0x80,0x07,0x50,0x0F,0x80,0x07, +0x49,0xFF,0xF8,0xA0,0x3C,0x1D,0xFB,0xE8,0x5A,0x00,0x01,0x05,0x5A,0x18,0x01,0x30, +0xD5,0x03,0x5A,0x18,0x01,0x08,0x84,0x03,0x3E,0x07,0xF6,0x3B,0x84,0x00,0x3C,0x0F, +0xFB,0xE8,0x84,0x00,0x49,0xFF,0xD6,0x77,0x00,0x0F,0x80,0x07,0x49,0xFF,0xB9,0x09, +0x84,0x03,0x44,0x10,0x01,0xF4,0x49,0xFF,0xEC,0x4D,0x84,0x03,0x84,0x21,0x49,0xFF, +0xEC,0x57,0x49,0xFF,0xD6,0x64,0x80,0xC0,0xC8,0x0E,0x84,0x03,0x49,0xFF,0xEC,0x6B, +0x80,0xE0,0x5A,0x08,0x01,0xF8,0x84,0x03,0x80,0x26,0x49,0xFF,0xEC,0x49,0x80,0x07, +0x49,0xFF,0xD6,0x59,0x84,0x03,0x84,0x20,0x49,0xFF,0xEC,0x42,0xFC,0xA1,0x46,0x08, +0x88,0x88,0x44,0x14,0x00,0x54,0x50,0x00,0x08,0x88,0xB6,0x01,0xDD,0x9E,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xAA,0x7A,0x3E,0x60, diff --git a/drivers/input/touchscreen/ilitek_v3_mmi/firmware/ilitek_v3_fw.h b/drivers/input/touchscreen/ilitek_v3_mmi/firmware/ilitek_v3_fw.h new file mode 100644 index 000000000000..92f7729a6d2d --- /dev/null +++ b/drivers/input/touchscreen/ilitek_v3_mmi/firmware/ilitek_v3_fw.h @@ -0,0 +1,91 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __ILITEK_V3_FW_H +#define __ILITEK_V3_FW_H + +/* define names and paths for the variety of tp modules */ +#define DEF_INI_NAME_PATH "/sdcard/mp.ini" +#define DEF_FW_FILP_PATH "/sdcard/ILITEK_FW" +#define DEF_INI_REQUEST_PATH "mp.ini" +#define DEF_FW_REQUEST_PATH "ILITEK_FW" +static unsigned char CTPM_FW_DEF[] = { + #include "FW_TDDI_TRUNK_FB.ili" +}; + +#define CSOT_INI_NAME_PATH "/sdcard/mp_csot.ini" +#define CSOT_FW_FILP_PATH "/sdcard/ILITEK_FW_CSOT" +#define CSOT_INI_REQUEST_PATH "mp_csot.ini" +#define CSOT_FW_REQUEST_PATH "ILITEK_FW_CSOT" +static unsigned char CTPM_FW_CSOT[] = { + 0xFF, +}; + +#define AUO_INI_NAME_PATH "/sdcard/mp_auo.ini" +#define AUO_FW_FILP_PATH "/sdcard/ILITEK_FW_AUO" +#define AUO_INI_REQUEST_PATH "mp_auo.ini" +#define AUO_FW_REQUEST_PATH "ILITEK_FW_AUO" +static unsigned char CTPM_FW_AUO[] = { + 0xFF, +}; + +#define BOE_INI_NAME_PATH "/sdcard/mp_boe.ini" +#define BOE_FW_FILP_PATH "/sdcard/ILITEK_FW_BOE" +#define BOE_INI_REQUEST_PATH "mp_boe.ini" +#define BOE_FW_REQUEST_PATH "ILITEK_FW_BOE" +static unsigned char CTPM_FW_BOE[] = { + 0xFF, +}; + +#define INX_INI_NAME_PATH "/sdcard/mp_inx.ini" +#define INX_FW_FILP_PATH "/sdcard/ILITEK_FW_INX" +#define INX_INI_REQUEST_PATH "mp_inx.ini" +#define INX_FW_REQUEST_PATH "ILITEK_FW_INX" +static unsigned char CTPM_FW_INX[] = { + 0xFF, +}; + +#define DJ_INI_NAME_PATH "/sdcard/mp_dj.ini" +#define DJ_FW_FILP_PATH "/sdcard/ILITEK_FW_DJ" +#define DJ_INI_REQUEST_PATH "mp_dj.ini" +#define DJ_FW_REQUEST_PATH "ILITEK_FW_DJ" +static unsigned char CTPM_FW_DJ[] = { + 0xFF, +}; + +#define TXD_INI_NAME_PATH "/sdcard/mp_txd.ini" +#define TXD_FW_FILP_PATH "/sdcard/ILITEK_FW_TXD" +#define TXD_INI_REQUEST_PATH "mp_txd.ini" +#define TXD_FW_REQUEST_PATH "ILITEK_FW_TXD" +static unsigned char CTPM_FW_TXD[] = { + 0xFF, +}; + +#define TM_INI_NAME_PATH "/sdcard/mp_tm.ini" +#define TM_FW_FILP_PATH "/sdcard/ILITEK_FW_TM" +#define TM_INI_REQUEST_PATH "mp_tm.ini" +#define TM_FW_REQUEST_PATH "ILITEK_FW_TM" +static unsigned char CTPM_FW_TM[] = { + 0xFF, +}; + +#endif \ No newline at end of file diff --git a/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3.c b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3.c new file mode 100644 index 000000000000..db6e5f7e623a --- /dev/null +++ b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3.c @@ -0,0 +1,1729 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "firmware/ilitek_v3_fw.h" +#include "ilitek_v3.h" + +/* Debug level */ +bool debug_en = DEBUG_OUTPUT; +EXPORT_SYMBOL(debug_en); + +static struct workqueue_struct *esd_wq; +static struct workqueue_struct *bat_wq; +static struct delayed_work esd_work; +static struct delayed_work bat_work; + +#if CHARGER_NOTIFIER_CALLBACK +#if KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE +/* add_for_charger_start */ +static int ilitek_charger_notifier_callback(struct notifier_block *nb, unsigned long val, void *v) +{ + int ret = 0; + struct power_supply *psy = NULL; + union power_supply_propval prop; + + if (ilits->psy_name) + psy = power_supply_get_by_name(ilits->psy_name); + else + psy= power_supply_get_by_name("usb"); + if (!psy) { + if (ilits->psy_name) + ILI_ERR("Couldn't get psy:%s\n", ilits->psy_name); + else + ILI_ERR("Couldn't get usbpsy\n"); + return -EINVAL; + } + + if (!strcmp(psy->desc->name, "battery") && (val == POWER_SUPPLY_PROP_STATUS)) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, &prop); + if (ret < 0) { + ILI_ERR("Couldn't get POWER_SUPPLY_PROP_STATUS rc=%d\n", ret); + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,&prop); + if (ret < 0) { + ILI_ERR("Couldn't get POWER_SUPPLY_PROP_PRESENT rc=%d\n", ret); + return ret; + } + } + } + else if (!strcmp(psy->desc->name, "usb")) { + if (psy && val == POWER_SUPPLY_PROP_STATUS) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &prop); + if (ret < 0) { + ILI_ERR("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret); + return ret; + } + } + } + if (!ret) { + if((prop.intval == USB_DETECT_IN) || (prop.intval == USB_DETECT_OUT) || (prop.intval == USB_DETECT_OUT_QCOM)) { + if(ilits->usb_plug_status != prop.intval) { + ILI_INFO("usb prop.intval =%d\n", prop.intval); + ilits->usb_plug_status = prop.intval; + if(!ilits->tp_suspend && (ilits->charger_notify_wq != NULL)) + queue_work(ilits->charger_notify_wq,&ilits->update_charger); + } + } + else + ILI_DBG("unsupport prop.intval =%d\n", prop.intval); + } + return 0; +} +static void ilitek_update_charger(struct work_struct *work) +{ + int ret = 0; + if (ilits->fw_update_stat != 100) + return; + mutex_lock(&ilits->touch_mutex); + if (USB_DETECT_IN == ilits->usb_plug_status) + ret = ili_ic_func_ctrl("plug", 0);// plug in + else + ret = ili_ic_func_ctrl("plug", 1);// plug out + if (ret < 0) { + ILI_ERR("Write plug in failed\n"); + } + mutex_unlock(&ilits->touch_mutex); +} +void ilitek_plat_charger_init(void) +{ + int ret = 0; + struct power_supply *psy = NULL; + union power_supply_propval prop; + + ilits->usb_plug_status = 2; + ilits->charger_notify_wq = create_singlethread_workqueue("ili_charger_wq"); + if (!ilits->charger_notify_wq) { + ILI_ERR("allocate ili_charger_notify_wq failed\n"); + return; + } + INIT_WORK(&ilits->update_charger, ilitek_update_charger); + ilits->notifier_charger.notifier_call = ilitek_charger_notifier_callback; + ret = power_supply_reg_notifier(&ilits->notifier_charger); + if (ret < 0) { + ILI_ERR("power_supply_reg_notifier failed\n"); + return; + } + + /* if power supply supplier registered brfore TP + * ps_notify_callback will not receive PSY_EVENT_PROP_ADDED + * event, and will cause miss to set TP into charger state. + * So check PS state in probe. + */ + if (ilits->psy_name) + psy = power_supply_get_by_name(ilits->psy_name); + else + psy = power_supply_get_by_name("usb"); + if (psy) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, &prop); + if (ret < 0) { + ILI_ERR("Couldn't get POWER_SUPPLY_PROP_STATUS rc=%d\n", ret); + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &prop); + if (ret < 0) { + ILI_ERR("Couldn't get POWER_SUPPLY_PROP_PRESENT rc=%d\n", ret); + } + } + if (!ret) { + ilits->usb_plug_status = prop.intval; + ILI_INFO("boot check usb_plug_status = %d\n", prop.intval); + } + } +} +/* add_for_charger_end */ +#endif +#endif + +#if RESUME_BY_DDI +static struct workqueue_struct *resume_by_ddi_wq; +static struct work_struct resume_by_ddi_work; + +static void ilitek_resume_by_ddi_work(struct work_struct *work) +{ + mutex_lock(&ilits->touch_mutex); + +#ifdef ILI_SENSOR_EN + if (ilits->wakeable) { + disable_irq_wake(ilits->irq_num); + ilits->gesture_enabled = false; + ilits->wakeable = false; + } +#else + if (ilits->gesture) + disable_irq_wake(ilits->irq_num); +#endif + + /* Set tp as demo mode and reload code if it's iram. */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + if (ilits->fw_upgrade_mode == UPGRADE_IRAM) { + ili_fw_upgrade_handler(NULL); + } else { + if (ilits->cascade_info_block.nNum != 0) { + if (ili_cascade_reset_ctrl(ilits->reset, true) < 0) + ILI_ERR("TP Cascade Reset failed during ili_resume_by_ddi_work\n"); + } else { + ili_reset_ctrl(ilits->reset); + } + } + + ili_irq_enable(); +#if defined (ILI_STOWED_MODE_EN)&& defined(ILI_SENSOR_EN) + ilits->set_stowed = 0; + ilits->prox_near = false; + ILI_ERR("ilits->set_stowed = %d,ilits->prox_near = %d\n",ilits->set_stowed,ilits->prox_near); +#endif + ILI_INFO("TP resume end by wq\n"); + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + mutex_unlock(&ilits->touch_mutex); +} + +void ili_resume_by_ddi(void) +{ + if (!resume_by_ddi_wq) { + ILI_INFO("resume_by_ddi_wq is null\n"); + return; + } + +// mutex_lock(&ilits->touch_mutex); + + ILI_INFO("TP resume start called by ddi\n"); + ilits->tp_suspend = false; + ilits->report = ENABLE; + /* + * To match the timing of sleep out, the first of mipi cmd must be sent within 10ms + * after TP reset. We then create a wq doing host download before resume. + */ + if (ilits->fw_upgrade_mode == UPGRADE_IRAM) { +#if (ENGINEER_FLOW) + if (!ilits->eng_flow) { + atomic_set(&ilits->fw_stat, ENABLE); + + if (ilits->cascade_info_block.nNum != 0) { + if (ili_cascade_reset_ctrl(ilits->reset, true) < 0) + ILI_ERR("TP Cascade Reset failed during ili_resume_by_ddi\n"); + } else { + ili_reset_ctrl(ilits->reset); + } + + ili_ice_mode_ctrl(ENABLE, OFF); + ilits->ddi_rest_done = true; + mdelay(5); + } +#else + atomic_set(&ilits->fw_stat, ENABLE); + + if (ilits->cascade_info_block.nNum != 0) { + if (ili_cascade_reset_ctrl(ilits->reset, true) < 0) + ILI_ERR("TP Cascade Reset failed during ili_resume_by_ddi\n"); + } else { + ili_reset_ctrl(ilits->reset); + } + + ili_ice_mode_ctrl(ENABLE, OFF); + ilits->ddi_rest_done = true; + mdelay(5); +#endif + queue_work(resume_by_ddi_wq, &(resume_by_ddi_work)); + } else { + if (ilits->gesture) + disable_irq_wake(ilits->irq_num); + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + + if (ilits->cascade_info_block.nNum != 0) { + if (ili_cascade_reset_ctrl(ilits->reset, true) < 0) + ILI_ERR("TP Cascade Reset failed during ili_resume_by_ddi\n"); + } else { + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP Reset failed during resume by ddi\n"); + } + + ili_ic_func_ctrl_reset(); + ILI_INFO("TP resume end by ddi\n"); + } + +// mutex_unlock(&ilits->touch_mutex); +} +#endif + +#ifdef ROI +static int ili_get_knuckle_roi_switch(u8 *roi_switch) +{ + *roi_switch = ili_config_get_knuckle_roi_status(); + + ILI_INFO("get roi status = %d\n", *roi_switch); + + return 0; +} + +static int ili_set_knuckle_roi_switch(bool roi_enable) +{ + int ret = 0; + + ret = ili_config_knuckle_roi_ctrl(roi_enable ? CMD_ENABLE : CMD_DISABLE); + if (ret) { + ILI_ERR("roi control failed, enable = %d, ret = %d\n", roi_enable, ret); + return ret; + } + + ILI_INFO("set roi status = %d\n", roi_enable); + + return 0; +} + +static int ili_knuckle_roi_switch(struct ts_roi_info *info) +{ + int i = 0; + int ret = 0; + + if (IS_ERR_OR_NULL(info)) { + ILI_ERR("info invaild\n"); + return -EINVAL; + } + + switch (info->op_action) { + case TS_ACTION_READ: + ret = ili_get_knuckle_roi_switch(&info->roi_switch); + if (ret) { + ILI_ERR("get roi switch failed, ret = %d\n", ret); + return ret; + } + break; + + case TS_ACTION_WRITE: + ret = ili_set_knuckle_roi_switch(info->roi_switch); + if (ret) { + ILI_ERR("set roi switch failed, ret = %d\n", ret); + return ret; + } + + if (!info->roi_switch) { + for (i = 0; i < ROI_DATA_READ_LENGTH; i++) { + ilits->knuckle_roi_data[i] = 0; + } + } + break; + + default: + ILI_ERR("op action invalid : %d\n", info->op_action); + return -EINVAL; + } + + return 0; +} + +static unsigned char *ili_knuckle_roi_rawdata(void) +{ + ILI_DBG("return roi data\n"); + return ilits->knuckle_roi_data; +} +#endif + +int ili_mp_test_handler(char *apk, bool lcm_on) +{ + int ret = 0; + + if (atomic_read(&ilits->fw_stat)) { + ILI_ERR("fw upgrade processing, ignore\n"); + return -EMP_FW_PROC; + } + + atomic_set(&ilits->mp_stat, ENABLE); + + if (ilits->actual_tp_mode != P5_X_FW_TEST_MODE) { + ret = ili_switch_tp_mode(P5_X_FW_TEST_MODE); + if (ret < 0) { + ILI_ERR("Switch MP mode failed\n"); + ret = -EMP_MODE; + goto out; + } + } + + ret = ili_mp_test_main(apk, lcm_on); + +out: + /* + * If there's running mp test with lcm off, we suspose that + * users will soon call resume from suspend. TP mode will be changed + * from MP to AP mode until resume finished. + */ + if (!lcm_on) { + atomic_set(&ilits->mp_stat, DISABLE); + return ret; + } + + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + if (ilits->fw_upgrade_mode == UPGRADE_IRAM) { + if (ili_fw_upgrade_handler(NULL) < 0) + ILI_ERR("FW upgrade failed during mp test\n"); + } else { + if (ilits->cascade_info_block.nNum != 0) { + if (ili_cascade_reset_ctrl(ilits->reset, true) < 0) + ILI_ERR("TP Cascade Reset failed during mp test\n"); + } else { + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP Reset failed during mp test\n"); + } + } + + atomic_set(&ilits->mp_stat, DISABLE); + return ret; +} + +int ili_switch_tp_mode(u8 mode) +{ + int ret = 0; + bool ges_dbg = false; + u8 data_type = P5_X_FW_SIGNAL_DATA_MODE; + + atomic_set(&ilits->tp_sw_mode, START); + + ilits->actual_tp_mode = mode; + + /* able to see cdc data in gesture mode */ + if (ilits->tp_data_format == DATA_FORMAT_DEBUG && + ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) + ges_dbg = true; + + switch (ilits->actual_tp_mode) { + case P5_X_FW_AP_MODE: + ILI_INFO("Switch to AP mode\n"); + ilits->wait_int_timeout = AP_INT_TIMEOUT; + if (ilits->fw_upgrade_mode == UPGRADE_IRAM) { + if (ili_fw_upgrade_handler(NULL) < 0) + ILI_ERR("FW upgrade failed\n"); + } else { + if (ilits->cascade_info_block.nNum != 0) { + if (ili_cascade_reset_ctrl(ilits->reset, true) < 0) + ILI_ERR("TP Cascade Reset failed during ili_switch_tp_mode\n"); + } else { + ret = ili_reset_ctrl(ilits->reset); + } + } + if (ret < 0) + ILI_ERR("TP Reset failed\n"); + + break; + case P5_X_FW_GESTURE_MODE: + ILI_INFO("Switch to Gesture mode\n"); + ilits->wait_int_timeout = AP_INT_TIMEOUT; + ret = ilits->gesture_move_code(ilits->gesture_mode); + if (ret < 0) + ILI_ERR("Move gesture code failed\n"); + if (ges_dbg) { + ILI_INFO("Enable gesture debug func\n"); + ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEBUG, false, &data_type); + } + break; + case P5_X_FW_TEST_MODE: + ILI_INFO("Switch to Test mode\n"); + ilits->wait_int_timeout = MP_INT_TIMEOUT; + ret = ilits->mp_move_code(); + break; + default: + ILI_ERR("Unknown TP mode: %x\n", mode); + ret = -1; + break; + } + + if (ret < 0) + ILI_ERR("Switch TP mode (%d) failed \n", mode); + + ILI_DBG("Actual TP mode = %d\n", ilits->actual_tp_mode); + atomic_set(&ilits->tp_sw_mode, END); + return ret; +} + +int ili_gesture_recovery(void) +{ + int ret = 0; + + atomic_set(&ilits->esd_stat, START); + + ILI_INFO("Doing gesture recovery\n"); + ret = ilits->ges_recover(); + + atomic_set(&ilits->esd_stat, END); + return ret; +} + +void ili_spi_recovery(void) +{ + atomic_set(&ilits->esd_stat, START); + + ILI_INFO("Doing spi recovery\n"); + if (ili_fw_upgrade_handler(NULL) < 0) + ILI_ERR("FW upgrade failed\n"); + + atomic_set(&ilits->esd_stat, END); +} + +int ili_wq_esd_spi_check(void) +{ + int ret = 0; + u8 tx = SPI_WRITE, rx = 0; + + ret = ilits->spi_write_then_read(ilits->spi, &tx, 1, &rx, 1); + ILI_DBG("spi esd check = 0x%x\n", ret); + if (ret == DO_SPI_RECOVER) { + ILI_ERR("ret = 0x%x\n", ret); + return -1; + } + return 0; +} + +int ili_wq_esd_i2c_check(void) +{ + ILI_DBG(""); + return 0; +} + +static void ilitek_tddi_wq_esd_check(struct work_struct *work) +{ + if (mutex_is_locked(&ilits->touch_mutex)) { + ILI_INFO("touch is locked, ignore\n"); + return ; + } + mutex_lock(&ilits->touch_mutex); + if (ilits->esd_recover() < 0) { + ILI_ERR("SPI ACK failed, doing spi recovery\n"); + ili_spi_recovery(); + } + mutex_unlock(&ilits->touch_mutex); + complete_all(&ilits->esd_done); + ili_wq_ctrl(WQ_ESD, ENABLE); +} + +static int read_power_status(u8 *buf) +{ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + struct file *f = NULL; + mm_segment_t old_fs; + ssize_t byte = 0; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + f = filp_open(POWER_STATUS_PATH, O_RDONLY, 0); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open %s\n", POWER_STATUS_PATH); + return -1; + } + + f->f_op->llseek(f, 0, SEEK_SET); + byte = f->f_op->read(f, buf, 20, &f->f_pos); + + ILI_DBG("Read %d bytes\n", (int)byte); + + set_fs(old_fs); + filp_close(f, NULL); +#else + return -1; +#endif + return 0; +} + +static void ilitek_tddi_wq_bat_check(struct work_struct *work) +{ + u8 str[20] = {0}; + static int charge_mode; + + if (read_power_status(str) < 0) + ILI_ERR("Read power status failed\n"); + + ILI_DBG("Batter Status: %s\n", str); + + if (strstr(str, "Charging") != NULL || strstr(str, "Full") != NULL + || strstr(str, "Fully charged") != NULL) { + if (charge_mode != 1) { + ILI_DBG("Charging mode\n"); + if (ili_ic_func_ctrl("plug", DISABLE) < 0) /* plug in */ + ILI_ERR("Write plug in failed\n"); + charge_mode = 1; + } + } else { + if (charge_mode != 2) { + ILI_DBG("Not charging mode\n"); + if (ili_ic_func_ctrl("plug", ENABLE) < 0) /* plug out */ + ILI_ERR("Write plug out failed\n"); + charge_mode = 2; + } + } + ili_wq_ctrl(WQ_BAT, ENABLE); +} + +void ili_wq_ctrl(int type, int ctrl) +{ + switch (type) { + case WQ_ESD: + if (ilits->esd_func_ctrl || ilits->wq_ctrl) { + if (!esd_wq) { + ILI_ERR("wq esd is null\n"); + break; + } + ilits->wq_esd_ctrl = ctrl; + if (ctrl == ENABLE) { + ILI_DBG("execute esd check\n"); + if (!queue_delayed_work(esd_wq, &esd_work, msecs_to_jiffies(WQ_ESD_DELAY))) + ILI_DBG("esd check was already on queue\n"); + } else { + cancel_delayed_work_sync(&esd_work); + flush_workqueue(esd_wq); + ILI_DBG("cancel esd wq\n"); + } + } + break; + case WQ_BAT: + if (ENABLE_WQ_BAT || ilits->wq_ctrl) { + if (!bat_wq) { + ILI_ERR("WQ BAT is null\n"); + break; + } + ilits->wq_bat_ctrl = ctrl; + if (ctrl == ENABLE) { + ILI_DBG("execute bat check\n"); + if (!queue_delayed_work(bat_wq, &bat_work, msecs_to_jiffies(WQ_BAT_DELAY))) + ILI_DBG("bat check was already on queue\n"); + } else { + cancel_delayed_work_sync(&bat_work); + flush_workqueue(bat_wq); + ILI_DBG("cancel bat wq\n"); + } + } + break; + default: + ILI_ERR("Unknown WQ type, %d\n", type); + break; + } +} + +static void ilitek_tddi_wq_init(void) +{ + esd_wq = alloc_workqueue("esd_check", WQ_MEM_RECLAIM, 0); + bat_wq = alloc_workqueue("bat_check", WQ_MEM_RECLAIM, 0); + + WARN_ON(!esd_wq); + WARN_ON(!bat_wq); + + INIT_DELAYED_WORK(&esd_work, ilitek_tddi_wq_esd_check); + INIT_DELAYED_WORK(&bat_work, ilitek_tddi_wq_bat_check); + +#if RESUME_BY_DDI + resume_by_ddi_wq = create_singlethread_workqueue("resume_by_ddi_wq"); + WARN_ON(!resume_by_ddi_wq); + INIT_WORK(&resume_by_ddi_work, ilitek_resume_by_ddi_work); +#endif +} + +int ili_sleep_handler(int mode) +{ + int ret = 0; + bool sense_stop = true; + + mutex_lock(&ilits->touch_mutex); + atomic_set(&ilits->tp_sleep, START); + + if (atomic_read(&ilits->fw_stat) || + atomic_read(&ilits->mp_stat)) { + ILI_INFO("fw upgrade or mp still running, ignore sleep requst\n"); + atomic_set(&ilits->tp_sleep, END); + mutex_unlock(&ilits->touch_mutex); + return 0; + } + + ili_wq_ctrl(WQ_ESD, DISABLE); + ili_wq_ctrl(WQ_BAT, DISABLE); + ili_irq_disable(); + + ILI_INFO("Sleep Mode = %d\n", mode); + + if (ilits->ss_ctrl) + sense_stop = true; + else if ((ilits->chip->core_ver >= CORE_VER_1430)) + sense_stop = false; + else + sense_stop = true; + + switch (mode) { + case TP_SUSPEND: + ILI_INFO("TP suspend start\n"); + ilits->tp_suspend = true; + ilits->power_status = false; + if (sense_stop) { + if (ili_ic_func_ctrl("sense", DISABLE) < 0) + ILI_ERR("Write sense stop cmd failed\n"); + + if (ili_ic_check_busy(50, 20, ON) < 0) + ILI_ERR("Check busy timeout during suspend\n"); + } + +#ifdef ILI_SENSOR_EN + if (ilits->should_enable_gesture) { + ili_switch_tp_mode(P5_X_FW_GESTURE_MODE); + enable_irq_wake(ilits->irq_num); + ili_irq_enable(); + ilits->wakeable = true; + } else { + if (ili_ic_func_ctrl("sleep", DEEP_SLEEP_IN) < 0) + ILI_ERR("Write sleep in cmd failed\n"); + ilits->wakeable = false; + } +#else + if ((ilits->gesture) || (ilits->prox_face_mode == PROXIMITY_SUSPEND_RESUME)) { + ili_switch_tp_mode(P5_X_FW_GESTURE_MODE); + enable_irq_wake(ilits->irq_num); + ili_irq_enable(); + } else { + if (ili_ic_func_ctrl("sleep", SLEEP_IN) < 0) + ILI_ERR("Write sleep in cmd failed\n"); + } +#endif +#if defined (ILI_STOWED_MODE_EN) && defined (ILI_SENSOR_EN) + if (ilits->should_enable_gesture && ilits->get_stowed && ilits->tp_suspend) { + ili_proximity_near(DDI_POWER_ON); + ilits->set_stowed = ilits->get_stowed; + ILI_INFO("Enable stowed mode suspend\n"); + } +#endif + ILI_INFO("TP suspend end\n"); + break; + case TP_DEEP_SLEEP: + ILI_INFO("TP deep suspend start\n"); + ilits->tp_suspend = true; + ilits->power_status = false; + if (sense_stop) { + if (ili_ic_func_ctrl("sense", DISABLE) < 0) + ILI_ERR("Write sense stop cmd failed\n"); + + if (ili_ic_check_busy(50, 20, ON) < 0) + ILI_ERR("Check busy timeout during deep suspend\n"); + } +#ifndef ILI_SENSOR_EN + if ((ilits->gesture) || (ilits->prox_face_mode == PROXIMITY_SUSPEND_RESUME)) { + ili_switch_tp_mode(P5_X_FW_GESTURE_MODE); + enable_irq_wake(ilits->irq_num); + ili_irq_enable(); + } else +#endif + { + if (ili_ic_func_ctrl("sleep", DEEP_SLEEP_IN) < 0) + ILI_ERR("Write deep sleep in cmd failed\n"); + } + ILI_INFO("TP deep suspend end\n"); + break; + case TP_RESUME: +#if !RESUME_BY_DDI + ILI_INFO("TP resume start\n"); + ilits->tp_suspend = false; + ilits->report = ENABLE; + + /* Set tp as demo mode and reload code if it's iram. */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + if (ilits->fw_upgrade_mode == UPGRADE_IRAM) { + if (ili_fw_upgrade_handler(NULL) < 0) + ILI_ERR("FW upgrade failed during resume\n"); + } else { + if (ilits->cascade_info_block.nNum != 0) { + if (ili_cascade_reset_ctrl(ilits->reset, true) < 0) + ILI_ERR("TP Cascade Reset failed during resume\n"); + } else { + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP Reset failed during resume\n"); + } + ili_ic_func_ctrl_reset(); + } + ilits->power_status = true; + + if (ilits->prox_face_mode == PROXIMITY_SUSPEND_RESUME) { + ilits->proxmity_face = false; + + if (ilits->prox_face_gesture == true) { + ilits->prox_face_gesture = false; + if (ili_ic_func_ctrl("proximity", 0x01) < 0) { + ILI_ERR("Write proximity cmd failed\n"); + } + } + } + +#ifdef ILI_SENSOR_EN + if (ilits->wakeable) { + disable_irq_wake(ilits->irq_num); + ilits->gesture_enabled = false; + ilits->wakeable = false; + } +#else + if (ilits->gesture) + disable_irq_wake(ilits->irq_num); +#endif +#if defined (ILI_STOWED_MODE_EN) && defined (ILI_SENSOR_EN) + ilits->set_stowed = 0; + ilits->prox_near = false; + ILI_ERR("ilits->set_stowed = %d,ilits->prox_near = %d\n", ilits->set_stowed,ilits->prox_near); +#endif + ILI_INFO("TP resume end\n"); +#else + ili_resume_by_ddi(); +#endif + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + ili_irq_enable(); + break; + default: + ILI_ERR("Unknown sleep mode, %d\n", mode); + ret = -EINVAL; + break; + } + + ili_pen_release(); + ili_touch_release_all_point(); + atomic_set(&ilits->tp_sleep, END); + mutex_unlock(&ilits->touch_mutex); + return ret; +} + +int ili_fw_upgrade_handler(void *data) +{ + int ret = 0; + + atomic_set(&ilits->fw_stat, START); + + ilits->fw_update_stat = FW_STAT_INIT; + ret = ili_fw_upgrade(ilits->fw_open); + if (ret != 0) { + ILI_INFO("FW upgrade fail\n"); + ilits->fw_update_stat = FW_UPDATE_FAIL; + } else { + ILI_INFO("FW upgrade pass\n"); +#if CHARGER_NOTIFIER_CALLBACK +#if KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE + /* add_for_charger_start */ + if (ilits->actual_tp_mode != P5_X_FW_TEST_MODE) { + ILI_INFO("charge status is %d\n", ilits->usb_plug_status); + if (ilits->usb_plug_status == USB_DETECT_IN) { + ret = ili_ic_func_ctrl("plug", 0);/* plug in */ + } else if ((ilits->usb_plug_status == USB_DETECT_OUT) || (ilits->usb_plug_status == USB_DETECT_OUT_QCOM)) { + ret = ili_ic_func_ctrl("plug", 1);/* plug out */ + } + if (ret < 0) { + ILI_ERR("Write plug in failed\n"); + } + } + /* add_for_charger_end */ +#endif +#endif + ilits->fw_update_stat = FW_UPDATE_PASS; + } + + if (!ilits->boot) { + ilits->boot = true; + ILI_INFO("Registre touch to input subsystem\n"); + ili_input_register(); + + if (ilits->PenType == POSITION_PEN_TYPE_ON) { + ili_input_pen_register(); + } + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + } + + atomic_set(&ilits->fw_stat, END); + return ret; +} + +int ili_set_tp_data_len(int format, bool send, u8 *data) +{ + u8 cmd[12] = {0}, ctrl = 0, debug_ctrl = 0, data_type = 0; + u16 self_key = 2; + int ret = 0, tp_mode = ilits->actual_tp_mode, len = 0, geture_info_length = 0, demo_mode_packet_len = 0; + + if (data == NULL) { + data_type = P5_X_FW_SIGNAL_DATA_MODE; + ILI_INFO("Data Type is Null, Set Single data type\n"); + } else { + data_type = data[0]; + ILI_INFO("Set data type = 0x%X \n", data[0]); + } + + if (ilits->rib.nCustomerType == ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + geture_info_length = P5_X_GESTURE_INFO_LENGTH; + + if (ilits->PenType == POSITION_PEN_TYPE_ON) { + demo_mode_packet_len = P5_X_5B_LOW_RESOLUTION_LENGTH; + } else { + demo_mode_packet_len = P5_X_DEMO_MODE_PACKET_LEN; + } + } else if (ilits->rib.nCustomerType != ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + geture_info_length = P5_X_GESTURE_INFO_LENGTH; + demo_mode_packet_len = P5_X_5B_LOW_RESOLUTION_LENGTH + P5_X_CUSTOMER_LENGTH; + } else if (ilits->rib.nCustomerType != ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + geture_info_length = P5_X_GESTURE_INFO_LENGTH_HIGH_RESOLUTION; + demo_mode_packet_len = P5_X_DEMO_MODE_PACKET_LEN_HIGH_RESOLUTION + P5_X_CUSTOMER_LENGTH; + } else if (ilits->rib.nCustomerType == ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + geture_info_length = P5_X_GESTURE_INFO_LENGTH_HIGH_RESOLUTION; + demo_mode_packet_len = P5_X_DEMO_MODE_PACKET_LEN_HIGH_RESOLUTION; + } + + switch (format) { + case DATA_FORMAT_DEMO: + if (ilits->PenType == POSITION_PEN_TYPE_ON) { + ilits->touch_num = MAX_TOUCH_NUM + MAX_PEN_NUM; + ilits->pen_info.report_type = P5_X_HAND_PEN_TYPE; + ilits->pen_info.pen_data_mode = HAND_PEN_DEMO_MODE; + len = demo_mode_packet_len + P5_X_PEN_DATA_LEN; + } else { + len = demo_mode_packet_len; + } + ctrl = DATA_FORMAT_DEMO_CMD; + break; + case DATA_FORMAT_GESTURE_DEMO: + len = demo_mode_packet_len; + ctrl = DATA_FORMAT_DEMO_CMD; + break; + case DATA_FORMAT_DEBUG: + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + len = P5_X_DEBUG_LOW_RESOLUTION_FINGER_DATA_LENGTH; + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + len = P5_X_DEBUG_HIGH_RESOLUTION_FINGER_DATA_LENGTH; + } + + if (ilits->PenType == POSITION_PEN_TYPE_ON) { + ilits->touch_num = MAX_TOUCH_NUM + MAX_PEN_NUM; + ilits->pen_info.report_type = P5_X_HAND_PEN_TYPE; + + ilits->cdc_data_len = (ilits->xch_num * ilits->ych_num * 2) + ((ilits->stx + ilits->srx) * 2) + (2 * self_key); + + if (data_type == P5_X_FW_RAW_DATA_MODE) { + ilits->pen_info.pen_data_mode = HAND_PEN_RAW_DATA_MODE; + len += ilits->cdc_data_len + P5_X_PEN_DATA_LEN + (((ilits->pen_info_block.nPxRaw * ilits->xch_num * 2) + (ilits->pen_info_block.nPyRaw * ilits->ych_num * 2)) * 2 + 4) + P5_X_OTHER_DATA_LEN; + } else { + ilits->pen_info.pen_data_mode = HAND_PEN_SIGNAL_MODE; + len += ilits->cdc_data_len + P5_X_PEN_DATA_LEN + (((ilits->pen_info_block.nPxVa * ilits->xch_num * 2) + (ilits->pen_info_block.nPyVa * ilits->ych_num * 2)) * 2 + 4) + P5_X_OTHER_DATA_LEN; + } + } else { + len += (2 * ilits->xch_num * ilits->ych_num) + (ilits->stx * 2) + (ilits->srx * 2) + (2 * self_key) + 16; + } + + if (ilits->rib.nCustomerType != ilits->customertype_off) { + len += P5_X_CUSTOMER_LENGTH; + } + + len += P5_X_INFO_CHECKSUM_LENGTH; + if (!ilits->compress_disable) { + len = COMPRESS_PACKET_LEN; + } + ctrl = DATA_FORMAT_DEBUG_CMD; + break; + case DATA_FORMAT_GESTURE_DEBUG: + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + len = P5_X_DEBUG_LOW_RESOLUTION_FINGER_DATA_LENGTH; + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + len = P5_X_DEBUG_HIGH_RESOLUTION_FINGER_DATA_LENGTH; + } + + if (ilits->PenType == POSITION_PEN_TYPE_ON) { + ilits->touch_num = MAX_TOUCH_NUM + MAX_PEN_NUM; + ilits->pen_info.report_type = P5_X_HAND_PEN_TYPE; + + ilits->cdc_data_len = (ilits->xch_num * ilits->ych_num * 2) + ((ilits->stx + ilits->srx) * 2) + (2 * self_key); + + if (data_type == P5_X_FW_RAW_DATA_MODE) { + ilits->pen_info.pen_data_mode = HAND_PEN_RAW_DATA_MODE; + len += ilits->cdc_data_len + P5_X_PEN_DATA_LEN + (((ilits->pen_info_block.nPxRaw * ilits->xch_num * 2) + (ilits->pen_info_block.nPyRaw * ilits->ych_num * 2)) * 2 + 4) + P5_X_OTHER_DATA_LEN; + } else { + ilits->pen_info.pen_data_mode = HAND_PEN_SIGNAL_MODE; + len += ilits->cdc_data_len + P5_X_PEN_DATA_LEN + (((ilits->pen_info_block.nPxVa * ilits->xch_num * 2) + (ilits->pen_info_block.nPyVa * ilits->ych_num * 2)) * 2 + 4) + P5_X_OTHER_DATA_LEN; + } + } else { + len += (2 * ilits->xch_num * ilits->ych_num) + (ilits->stx * 2) + (ilits->srx * 2) + (2 * self_key) + 16; + } + + if (ilits->rib.nCustomerType != ilits->customertype_off) { + len += P5_X_CUSTOMER_LENGTH; + } + + len += P5_X_INFO_CHECKSUM_LENGTH; + + ctrl = DATA_FORMAT_DEBUG_CMD; + break; + case DATA_FORMAT_DEMO_DEBUG_INFO: + /*only suport SPI interface now, so defult use size 1024 buffer*/ + len = demo_mode_packet_len + + P5_X_DEMO_DEBUG_INFO_ID0_LENGTH + P5_X_INFO_HEADER_LENGTH; + ctrl = DATA_FORMAT_DEMO_DEBUG_INFO_CMD; + break; + case DATA_FORMAT_GESTURE_INFO: + len = geture_info_length; + ctrl = DATA_FORMAT_GESTURE_INFO_CMD; + break; + case DATA_FORMAT_GESTURE_NORMAL: + len = P5_X_GESTURE_NORMAL_LENGTH; + ctrl = DATA_FORMAT_GESTURE_NORMAL_CMD; + break; + case DATA_FORMAT_GESTURE_SPECIAL_DEMO: + if (ilits->gesture_demo_ctrl == ENABLE) { + if (ilits->gesture_mode == DATA_FORMAT_GESTURE_INFO) + len = geture_info_length + P5_X_INFO_HEADER_LENGTH + P5_X_INFO_CHECKSUM_LENGTH; + else + len = demo_mode_packet_len + P5_X_INFO_HEADER_LENGTH + P5_X_INFO_CHECKSUM_LENGTH; + } else { + if (ilits->gesture_mode == DATA_FORMAT_GESTURE_INFO) + len = geture_info_length; + else + len = P5_X_GESTURE_NORMAL_LENGTH; + } + ILI_INFO("Gesture demo mode control = %d\n", ilits->gesture_demo_ctrl); + ili_ic_func_ctrl("gesture_demo_en", ilits->gesture_demo_ctrl); + ILI_INFO("knock_en setting\n"); + ili_ic_func_ctrl("knock_en", 0x8); + break; + case DATA_FORMAT_DEBUG_LITE_ROI: + debug_ctrl = DATA_FORMAT_DEBUG_LITE_ROI_CMD; + ctrl = DATA_FORMAT_DEBUG_LITE_CMD; + break; + case DATA_FORMAT_DEBUG_LITE_WINDOW: + debug_ctrl = DATA_FORMAT_DEBUG_LITE_WINDOW_CMD; + ctrl = DATA_FORMAT_DEBUG_LITE_CMD; + break; + case DATA_FORMAT_DEBUG_LITE_AREA: + if (data == NULL) { + ILI_ERR("DATA_FORMAT_DEBUG_LITE_AREA error cmd\n"); + return -1; + } + debug_ctrl = DATA_FORMAT_DEBUG_LITE_AREA_CMD; + ctrl = DATA_FORMAT_DEBUG_LITE_CMD; + + if (ilits->chip->core_ver >= CORE_VER_1700) { + data_type = data[0]; + cmd[4] = data[1]; + cmd[5] = data[2]; + cmd[6] = data[3]; + cmd[7] = data[4]; + } else { + cmd[3] = data[0]; + cmd[4] = data[1]; + cmd[5] = data[2]; + cmd[6] = data[3]; + } + break; +#if (TDDI_INTERFACE == BUS_I2C) + case DATA_FORMAT_DEBUG_LITE_PEN: + debug_ctrl = DATA_FORMAT_DEBUG_LITE_PEN_CMD; + ctrl = DATA_FORMAT_DEBUG_LITE_CMD; + break; +#endif + default: + ILI_ERR("Unknow TP data format\n"); + return -1; + } + + if (ctrl == DATA_FORMAT_DEBUG_LITE_CMD) { + if (format == DATA_FORMAT_DEBUG_LITE_PEN) + len = P5_X_DEBUG_LITE_PEN_LENGTH; + else + len = P5_X_DEBUG_LITE_LENGTH; + + if (ilits->chip->core_ver >= CORE_VER_1700) { + cmd[0] = P5_X_NEW_CONTROL_FORMAT; + cmd[1] = ctrl; + cmd[2] = data_type; + cmd[3] = debug_ctrl; + ret = ilits->wrapper(cmd, 11, NULL, 0, ON, OFF); + } else { + cmd[0] = P5_X_MODE_CONTROL; + cmd[1] = ctrl; + cmd[2] = debug_ctrl; + ret = ilits->wrapper(cmd, 10, NULL, 0, ON, OFF); + } + + if ((ret < 0) && !(atomic_read(&ilits->fw_stat) || atomic_read(&ilits->tp_sw_mode))) { + ILI_ERR("switch to format %d failed\n", format); + ili_switch_tp_mode(P5_X_FW_AP_MODE); + } + + } else if ((atomic_read(&ilits->tp_reset) == END) && ( + tp_mode == P5_X_FW_AP_MODE || + format == DATA_FORMAT_GESTURE_DEMO || + format == DATA_FORMAT_GESTURE_DEBUG || + format == DATA_FORMAT_DEMO)) { + + if (ilits->chip->core_ver >= CORE_VER_1700) { + cmd[0] = P5_X_NEW_CONTROL_FORMAT; + cmd[1] = ctrl; + cmd[2] = data_type; + ret = ilits->wrapper(cmd, 3, NULL, 0, ON, OFF); + } else { + cmd[0] = P5_X_MODE_CONTROL; + cmd[1] = ctrl; + ret = ilits->wrapper(cmd, 2, NULL, 0, ON, OFF); + } + + if ((ret < 0) && !(atomic_read(&ilits->fw_stat) || atomic_read(&ilits->tp_sw_mode))) { + ILI_ERR("switch to format %d failed\n", format); + ili_switch_tp_mode(P5_X_FW_AP_MODE); + } + } else if (tp_mode == P5_X_FW_GESTURE_MODE) { + + /*set gesture symbol*/ + ili_set_gesture_symbol(); + + if (send) { + if (ilits->proxmity_face == true) { + ret = ili_ic_func_ctrl("lpwg", 0x20); + if (ilits->gesture_load_code == false) { + ret = ili_ic_func_ctrl("lpwg", 0x21); + } + } else { + if (ilits->prox_face_mode == PROXIMITY_SUSPEND_RESUME) { + ilits->prox_face_gesture= true; + if (ili_ic_func_ctrl("proximity", 0x00) < 0) { + ILI_ERR("Write proximity cmd failed\n"); + } + } + ret = ili_ic_func_ctrl("lpwg", ctrl); + } + if (ret < 0) + ILI_ERR("write gesture mode failed\n"); + } + } + + ilits->tp_data_format = format; + ilits->tp_data_len = len; + ILI_INFO("TP mode = %d, format = %d, len = %d\n", + tp_mode, ilits->tp_data_format, ilits->tp_data_len); + + if (ilits->PenType == POSITION_PEN_TYPE_ON) { + ILI_INFO("Pen Type = 0x%X, Max Touch Num = %d, Pen Data Mode = %d\n",ilits->pen_info.report_type, ilits->touch_num, ilits->pen_info.pen_data_mode); + } + return ret; +} + +int ili_set_pen_data_len(u8 header, u8 ctrl, u8 type) +{ + int len = 0; + u8 ret = 0, self_key = 2; + u8 cmd[3] = {0}; + + cmd[0] = header; + cmd[1] = ctrl; + cmd[2] = type; + ret = ilits->wrapper(cmd, 3, NULL, 0, ON, OFF); + ILI_INFO("write = 0x%X, 0x%X, 0x%X\n", cmd[0], cmd[1], cmd[2]); + + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + len = P5_X_DEBUG_LOW_RESOLUTION_FINGER_DATA_LENGTH; + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + len = P5_X_DEBUG_HIGH_RESOLUTION_FINGER_DATA_LENGTH; + } + + switch(ilits->pen_info.report_type) { + case P5_X_ONLY_PEN_TYPE: + ilits->cdc_data_len = (ilits->stx + ilits->srx) * 2; + len += ilits->cdc_data_len + P5_X_PEN_DATA_LEN; + + if (ilits->rib.nCustomerType != ilits->customertype_off) { + len += P5_X_CUSTOMER_LENGTH; + } + + if (type == P5_X_FW_RAW_DATA_MODE ) { + len += ((ilits->pen_info_block.nPxRaw * ilits->xch_num * 2) + (ilits->pen_info_block.nPyRaw * ilits->ych_num * 2)) * 2 + 4; + ilits->pen_info.pen_data_mode = PEN_ONLY_RAW_DATA_MODE; + } else { + len += ((ilits->pen_info_block.nPxVa * ilits->xch_num * 2) + (ilits->pen_info_block.nPyVa * ilits->ych_num * 2)) * 2 + 4; + ilits->pen_info.pen_data_mode = PEN_ONLY_SIGNAL_MODE; + } + if (!ilits->compress_penonly_disable) { + len = COMPRESS_PACKET_LEN; + } + break; + case P5_X_ONLY_HAND_TYPE: + if (type == P5_X_FW_RAW_DATA_MODE) { + ilits->pen_info.pen_data_mode = HAND_ONLY_RAW_DATA_MODE; + } else { + ilits->pen_info.pen_data_mode = HAND_ONLY_SIGNAL_MODE; + } + + ilits->cdc_data_len = (ilits->xch_num * ilits->ych_num * 2) + ((ilits->stx + ilits->srx) * 2) + (2 * self_key); + len += ilits->cdc_data_len; + + if (ilits->rib.nCustomerType != ilits->customertype_off) { + len += P5_X_CUSTOMER_LENGTH; + } + + if (ilits->PenType == POSITION_PEN_TYPE_ON) { + len += P5_X_PEN_DATA_LEN; + } + + if (!ilits->compress_handonly_disable) { + len = COMPRESS_PACKET_LEN; + } + break; + + default: + ILI_ERR("PEN Type = 0x%X, CMD Error\n", ilits->pen_info.report_type); + break; + } + len += P5_X_OTHER_DATA_LEN + P5_X_INFO_CHECKSUM_LENGTH; /* 17 */ + ilits->tp_data_len = len; + ILI_INFO("Packet Length = %d, Pen Type = 0x%X, Pen Data Mode = %d\n", ilits->tp_data_len, ilits->pen_info.report_type, ilits->pen_info.pen_data_mode); + + return ret; +} + +int ili_report_handler(void) +{ + int ret = 0, pid = 0; + u8 checksum = 0, pack_checksum = 0; + u8 *trdata = NULL; + int rlen = 0; + int tmp = debug_en; + + /* Just in case these stats couldn't be blocked in top half context */ + if (!ilits->boot || !ilits->report || atomic_read(&ilits->tp_reset) || atomic_read(&ilits->ignore_report) || + atomic_read(&ilits->fw_stat) || atomic_read(&ilits->tp_sw_mode) || + atomic_read(&ilits->mp_stat) || atomic_read(&ilits->tp_sleep)) { + ILI_INFO("ignore report request\n"); + return -EINVAL; + } + + if (ilits->irq_after_recovery) { + ILI_INFO("ignore int triggered by recovery\n"); + return -EINVAL; + } + + ili_wq_ctrl(WQ_ESD, DISABLE); + ili_wq_ctrl(WQ_BAT, DISABLE); + + if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) { + PM_STAY_AWAKE(ilits->ws); + + if (ilits->pm_suspend) { + /* Waiting for pm resume completed */ + ret = wait_for_completion_timeout(&ilits->pm_completion, msecs_to_jiffies(700)); + if (!ret) { + ILI_ERR("system(spi) can't finished resuming procedure."); + } + } + } + + rlen = ilits->tp_data_len; + + ILI_DBG("Packget length = %d\n", rlen); + + if (!rlen || rlen > TR_BUF_SIZE) { + ILI_ERR("Length of packet is invaild\n"); + goto out; + } + + memset(ilits->tr_buf, 0x0, TR_BUF_SIZE); + + ret = ilits->wrapper(NULL, 0, ilits->tr_buf, rlen, OFF, OFF); + + if (ilits->tr_buf[0] == P5_X_DEMO_PROXIMITY_ID) { + if (ilits->prox_face_mode == PROXIMITY_SUSPEND_RESUME) { + ili_report_proximity_mode(ilits->tr_buf, 1); + } else if (ilits->prox_face_mode == PROXIMITY_BACKLIGHT) { + if (ilits->tr_buf[1] == PROXIMITY_NEAR_STATE_1 || ilits->tr_buf[1] == PROXIMITY_NEAR_STATE_2 || ilits->tr_buf[1] == PROXIMITY_NEAR_STATE_3) { + ILI_DBG("proxmity face status : Near, cmd : %X\n", ilits->tr_buf[1]); + } else if(ilits->tr_buf[1] == PROXIMITY_FAR_STATE_5) { + ILI_DBG("proxmity face status : Far, cmd : %X\n", ilits->tr_buf[1]); + } else { + ILI_DBG("proxmity face status : Ignore, cmd : %X\n", ilits->tr_buf[1]); + } + } + goto out; + } + + if (ret < 0) { + ILI_ERR("Read report packet failed, ret = %d\n", ret); + if (ret == DO_SPI_RECOVER) { + ilits->irq_after_recovery = true; + ili_ic_get_pc_counter(DO_SPI_RECOVER); + + if (ilits->cascade_info_block.nNum != 0) { + ilits->slave_wr_ctrl = 1; + ili_ic_get_pc_counter(DO_SPI_RECOVER); + ilits->slave_wr_ctrl = 0; + } + + if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE && ilits->gesture && !ilits->prox_near) { + ILI_ERR("Gesture failed, doing gesture recovery\n"); + if (ili_gesture_recovery() < 0) + ILI_ERR("Failed to recover gesture\n"); + } else { + ILI_ERR("SPI ACK failed, doing spi recovery\n"); + ili_spi_recovery(); + } + msleep(100); + ilits->irq_after_recovery = false; + } + goto out; + } + + ili_dump_data(ilits->tr_buf, 8, rlen, 0, "finger report"); + + if (ilits->tr_buf[0] == P5_X_GESTURE_PACKET_ID) { + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + rlen = P5_X_GESTURE_INFO_LENGTH; + } else { + rlen = P5_X_GESTURE_INFO_LENGTH_HIGH_RESOLUTION; + } + } + checksum = ili_calc_packet_checksum(ilits->tr_buf, rlen - 1); + pack_checksum = ilits->tr_buf[rlen-1]; + trdata = ilits->tr_buf; + pid = trdata[0]; + ILI_DBG("Packet ID = %x\n", pid); + + if (checksum != pack_checksum && pid != P5_X_I2CUART_PACKET_ID) { + ILI_ERR("Checksum Error (0x%X)! Pack = 0x%X, len = %d\n", checksum, pack_checksum, rlen); + debug_en = DEBUG_ALL; + ili_dump_data(trdata, 8, rlen, 0, "finger report with wrong"); + debug_en = tmp; + ret = -EINVAL; + goto out; + } + + if (pid == P5_X_INFO_HEADER_PACKET_ID) { + trdata = ilits->tr_buf + P5_X_INFO_HEADER_LENGTH; + pid = trdata[0]; + } + + switch (pid) { + case P5_X_DEMO_PACKET_ID: + ili_report_ap_mode(trdata, rlen); + break; + case P5_X_DEBUG_PACKET_ID: + ili_report_debug_mode(trdata, rlen); + break; + case P5_X_DEMO_HIGH_RESOLUTION_PACKET_ID: + if (ilits->PenType == POSITION_PEN_TYPE_ON) { + ILI_DBG("Report Switch = 0x%X\n", trdata[rlen - 2]); + ili_report_pen_ap_mode(trdata, rlen); + } else { + ili_report_ap_mode(trdata, rlen); + } + break; + case P5_X_DEBUG_HIGH_RESOLUTION_PACKET_ID: + if (ilits->PenType == POSITION_PEN_TYPE_ON) { + ILI_DBG("Report Switch = 0x%X\n", trdata[rlen - 2]); + ili_report_pen_debug_mode(trdata, rlen); + } else { + ili_report_debug_mode(trdata, rlen); + } + break; + case P5_X_DEBUG_LITE_PACKET_ID: + ili_report_debug_lite_mode(trdata, rlen); + break; + case P5_X_I2CUART_PACKET_ID: + ili_report_i2cuart_mode(trdata, rlen); + break; + case P5_X_GESTURE_PACKET_ID: + ili_report_gesture_mode(trdata, rlen); + break; + case P5_X_GESTURE_FAIL_ID: + ILI_INFO("gesture fail reason code = 0x%02x", trdata[1]); + break; + case P5_X_DEMO_DEBUG_INFO_PACKET_ID: + ili_demo_debug_info_mode(trdata, rlen); + break; + default: + ILI_ERR("Unknown packet id, %x\n", pid); +#if (TDDI_INTERFACE == BUS_I2C) + msleep(50); + + if (ilits->cascade_info_block.nNum != 0) { + ilits->slave_wr_ctrl = 1; + ili_ic_get_pc_counter(DO_I2C_RECOVER); + ilits->slave_wr_ctrl = 0; + } + + ili_ic_get_pc_counter(DO_I2C_RECOVER); + + if ((ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) && ilits->fw_latch != 0) { + ILI_ERR("I2C esd_gesture\n"); + ili_touch_esd_gesture_flash(); + } + if (ilits->fw_latch != 0) { + msleep(50); + ili_ic_func_ctrl_reset(); + ILI_ERR("I2C func_ctrl_reset\n"); + } + if ((ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) && ilits->fw_latch != 0) { + msleep(50); + ili_set_gesture_symbol(); + ILI_ERR("I2C gesture_symbol\n"); + } +#endif + break; + } + +out: + if (ilits->actual_tp_mode != P5_X_FW_GESTURE_MODE) { + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + } + + if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) + PM_RELAX(ilits->ws); + return ret; +} + +int ili_cascade_reset_ctrl(int reset_mode, bool enter_ice) +{ + int ret = 0; + + if (ili_reset_ctrl(reset_mode) < 0) { + ILI_ERR("TP Reset failed during init\n"); + ret = -EFW_REST; + } + + if (reset_mode == TP_IC_CODE_RST) { + ili_ice_mode_ctrl_by_mode(DISABLE, OFF, BOTH); + } + +#if (TDDI_INTERFACE == BUS_SPI && ENABLE_CASCADE) + if (reset_mode != TP_IC_CODE_RST) { + atomic_set(&ilits->slave_ice_stat, DISABLE); + ILI_INFO("Set slave ice state atomic : %d\n", atomic_read(&ilits->slave_ice_stat)); + } +#endif + /* No need start sync cmd after reset. */ + atomic_set(&ilits->sync_stat, DISABLE); + ILI_DBG("Set atomic sync stat = %d\n", atomic_read(&ilits->sync_stat)); + + return ret; +} + +int ili_reset_ctrl(int mode) +{ + int ret = 0; + u8 data_type = P5_X_FW_SIGNAL_DATA_MODE; + + atomic_set(&ilits->tp_reset, START); + +#if MULTI_REPORT_RATE + if (mode == TP_IC_WHOLE_RST_WITH_FLASH || mode == TP_HW_RST_ONLY) { + if (ili_ic_report_rate_register_set() < 0) + ILI_ERR("write report rate password failed\n"); + } +#endif + + switch (mode) { + case TP_IC_CODE_RST: + ILI_INFO("TP IC Code RST \n"); + ret = ili_ic_code_reset(OFF); + ilits->pll_clk_wakeup = false; + if (ret < 0) + ILI_ERR("IC Code reset failed\n"); + break; + case TP_IC_WHOLE_RST_WITH_FLASH: + ILI_INFO("TP IC whole RST\n"); + ret = ili_ic_whole_reset(OFF, ON); + if (ret < 0) + ILI_ERR("IC whole reset failed\n"); + ilits->pll_clk_wakeup = true; + break; + case TP_IC_WHOLE_RST_WITHOUT_FLASH: + ILI_INFO("TP IC whole RST without flash\n"); + ret = ili_ic_whole_reset(OFF, OFF); + if (ret < 0) + ILI_ERR("IC whole reset without flash failed\n"); + ilits->pll_clk_wakeup = true; + break; + case TP_HW_RST_ONLY: + ILI_INFO("TP HW RST\n"); + ili_tp_reset(); + ilits->pll_clk_wakeup = true; + break; + default: + ILI_ERR("Unknown reset mode, %d\n", mode); + ret = -EINVAL; + break; + } + /* + * Since OTP must be folloing with reset, except for code rest, + * the stat of ice mode should be set as 0. + */ + if (mode != TP_IC_CODE_RST) + atomic_set(&ilits->ice_stat, DISABLE); + + if (ili_set_tp_data_len(DATA_FORMAT_DEMO, false, &data_type) < 0) + ILI_ERR("Failed to set tp data length\n"); + + atomic_set(&ilits->tp_reset, END); + return ret; +} + +#if 0 +static int ilitek_get_tp_module(void) +{ + ILI_DBG("return tp_module: %d", ilits->tp_module); + return ilits->tp_module; +} +#endif + +static void ili_update_tp_module_info(void) +{ + int module; + + module = ilits->tp_module; + + switch (module) { + case MODEL_CSOT: + ilits->md_name = "CSOT"; + ilits->md_fw_filp_path = CSOT_FW_FILP_PATH; + ilits->md_fw_rq_path = CSOT_FW_REQUEST_PATH; + ilits->md_ini_path = CSOT_INI_NAME_PATH; + ilits->md_ini_rq_path = CSOT_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_CSOT; + ilits->md_fw_ili_size = sizeof(CTPM_FW_CSOT); + break; + case MODEL_AUO: + ilits->md_name = "AUO"; + ilits->md_fw_filp_path = AUO_FW_FILP_PATH; + ilits->md_fw_rq_path = AUO_FW_REQUEST_PATH; + ilits->md_ini_path = AUO_INI_NAME_PATH; + ilits->md_ini_rq_path = AUO_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_AUO; + ilits->md_fw_ili_size = sizeof(CTPM_FW_AUO); + break; + case MODEL_BOE: + ilits->md_name = "BOE"; + ilits->md_fw_filp_path = BOE_FW_FILP_PATH; + ilits->md_fw_rq_path = BOE_FW_REQUEST_PATH; + ilits->md_ini_path = BOE_INI_NAME_PATH; + ilits->md_ini_rq_path = BOE_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_BOE; + ilits->md_fw_ili_size = sizeof(CTPM_FW_BOE); + break; + case MODEL_INX: + ilits->md_name = "INX"; + ilits->md_fw_filp_path = INX_FW_FILP_PATH; + ilits->md_fw_rq_path = INX_FW_REQUEST_PATH; + ilits->md_ini_path = INX_INI_NAME_PATH; + ilits->md_ini_rq_path = INX_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_INX; + ilits->md_fw_ili_size = sizeof(CTPM_FW_INX); + break; + case MODEL_DJ: + ilits->md_name = "DJ"; + ilits->md_fw_filp_path = DJ_FW_FILP_PATH; + ilits->md_fw_rq_path = DJ_FW_REQUEST_PATH; + ilits->md_ini_path = DJ_INI_NAME_PATH; + ilits->md_ini_rq_path = DJ_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_DJ; + ilits->md_fw_ili_size = sizeof(CTPM_FW_DJ); + break; + case MODEL_TXD: + ilits->md_name = "TXD"; + ilits->md_fw_filp_path = TXD_FW_FILP_PATH; + ilits->md_fw_rq_path = TXD_FW_REQUEST_PATH; + ilits->md_ini_path = TXD_INI_NAME_PATH; + ilits->md_ini_rq_path = TXD_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_TXD; + ilits->md_fw_ili_size = sizeof(CTPM_FW_TXD); + break; + case MODEL_TM: + ilits->md_name = "TM"; + ilits->md_fw_filp_path = TM_FW_FILP_PATH; + ilits->md_fw_rq_path = TM_FW_REQUEST_PATH; + ilits->md_ini_path = TM_INI_NAME_PATH; + ilits->md_ini_rq_path = TM_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_TM; + ilits->md_fw_ili_size = sizeof(CTPM_FW_TM); + break; + default: + if(module) { + ILI_INFO("No md info for module:%d, use default\n", module); + module = 0; + } + ILI_ERR("Couldn't find any tp modules, applying default settings\n"); + ilits->md_name = "DEF"; + ilits->md_fw_filp_path = DEF_FW_FILP_PATH; + ilits->md_fw_rq_path = DEF_FW_REQUEST_PATH; + ilits->md_ini_path = DEF_INI_NAME_PATH; + ilits->md_ini_rq_path = DEF_INI_REQUEST_PATH; + ilits->md_fw_ili = CTPM_FW_DEF; + ilits->md_fw_ili_size = sizeof(CTPM_FW_DEF); + break; + } + + ILI_INFO("Found %s module: ini path = %s, fw path = (%s, %s, %d)\n", + ilits->md_name, + ilits->md_ini_path, + ilits->md_fw_filp_path, + ilits->md_fw_rq_path, + ilits->md_fw_ili_size); + + ilits->tp_module = module; +} + +int ili_tddi_init(void) +{ +#if (BOOT_FW_UPDATE | HOST_DOWN_LOAD) + struct task_struct *fw_boot_th; +#else + u8 data_type = P5_X_FW_SIGNAL_DATA_MODE; +#endif + + ILI_INFO("driver version = %s\n", DRIVER_VERSION); + + mutex_init(&ilits->touch_mutex); + mutex_init(&ilits->debug_mutex); + mutex_init(&ilits->debug_read_mutex); + init_waitqueue_head(&(ilits->inq)); + spin_lock_init(&ilits->irq_spin); + init_completion(&ilits->esd_done); + + atomic_set(&ilits->ice_stat, DISABLE); + atomic_set(&ilits->tp_reset, END); + atomic_set(&ilits->fw_stat, END); + atomic_set(&ilits->mp_stat, DISABLE); + atomic_set(&ilits->tp_sleep, END); + atomic_set(&ilits->cmd_int_check, DISABLE); + atomic_set(&ilits->esd_stat, END); + atomic_set(&ilits->tp_sw_mode, END); + atomic_set(&ilits->ignore_report, END); + + ili_ic_init(); + ilitek_tddi_wq_init(); + + /* Must do hw reset once in first time for work normally if tp reset is avaliable */ +#if !TDDI_RST_BIND + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP Reset failed during init\n"); +#endif + + ilits->demo_debug_info[0] = ili_demo_debug_info_id0; + ilits->tp_data_format = DATA_FORMAT_DEMO; + ilits->boot = false; + + /* + * This status of ice enable will be reset until process of fw upgrade runs. + * it might cause unknown problems if we disable ice mode without any + * codes inside touch ic. + */ +#if (TDDI_INTERFACE == BUS_I2C && ENABLE_CASCADE) + ili_ice_mode_ctrl_by_mode(ENABLE, OFF, BOTH); +#elif (TDDI_INTERFACE == BUS_SPI && ENABLE_CASCADE) + ili_set_bypass_mode(OFF); +#else + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Failed to enable ice mode during ili_tddi_init\n"); +#endif + + if (ili_ic_dummy_check() < 0) { + ILI_ERR("Not found ilitek chip\n"); + return -ENODEV; + } +#if ENABLE_CASCADE + if (ili_cascade_ic_get_info(false, false) < 0) + ILI_ERR("Slave Chip info is incorrect\n"); +#else + if (ili_ic_get_info() < 0) + ILI_ERR("Chip info is incorrect\n"); +#endif + + ili_update_tp_module_info(); + + ili_node_init(); + + ili_fw_read_flash_info(OFF); + +#if (BOOT_FW_UPDATE | HOST_DOWN_LOAD) + fw_boot_th = kthread_run(ili_fw_upgrade_handler, NULL, "ili_fw_boot"); + if (fw_boot_th == (struct task_struct *)ERR_PTR) { + fw_boot_th = NULL; + /*WARN_ON(!fw_boot_th);*/ + ILI_ERR("Failed to create fw upgrade thread\n"); + } +#else +#if (TDDI_INTERFACE == BUS_I2C && ENABLE_CASCADE) + ili_ice_mode_ctrl_by_mode(DISABLE, OFF, BOTH); +#else + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Failed to disable ice mode failed during init\n"); +#endif + +#if (TDDI_INTERFACE == BUS_I2C) + ilits->info_from_hex = DISABLE; +#endif + + ili_ic_get_all_info(); + + ili_set_tp_data_len(DATA_FORMAT_DEMO, false, &data_type); + +#if (TDDI_INTERFACE == BUS_I2C) + ilits->info_from_hex = ENABLE; +#endif + + ILI_INFO("Registre touch to input subsystem\n"); + ili_input_register(); + + if (ilits->PenType == POSITION_PEN_TYPE_ON) { + ili_input_pen_register(); + } + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + ilits->boot = true; +#endif + + PM_WAKEUP_REGISTER(NULL, ilits->ws, "ili_wakelock"); + if (!ilits->ws) + ILI_ERR("wakeup source request failed\n"); + + ili_ic_edge_palm_para_init(); + + return 0; +} + +void ili_dev_remove(bool flag) +{ + ILI_INFO("remove ilitek dev\n"); + + if (!ilits) + return; + + gpio_free(ilits->tp_int); + gpio_free(ilits->tp_rst); + + if (esd_wq != NULL) { + cancel_delayed_work_sync(&esd_work); + flush_workqueue(esd_wq); + destroy_workqueue(esd_wq); + } + if (bat_wq != NULL) { + cancel_delayed_work_sync(&bat_work); + flush_workqueue(bat_wq); + destroy_workqueue(bat_wq); + } + + if (ilits->ws) + PM_WAKEUP_UNREGISTER(ilits->ws); + + kfree(ilits->tr_buf); + kfree(ilits->gcoord); + + ili_interface_dev_exit(ilits, flag); +} + +int ili_dev_init(struct ilitek_hwif_info *hwif) +{ + ILI_INFO("TP Interface: %s\n", (hwif->bus_type == BUS_I2C) ? "I2C" : "SPI"); + return ili_interface_dev_init(hwif); +} diff --git a/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3.h b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3.h new file mode 100644 index 000000000000..7e58d7f0cccb --- /dev/null +++ b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3.h @@ -0,0 +1,1680 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __ILITEK_V3_H +#define __ILITEK_V3_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_COMPAT +#include +#endif +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_OF +#include +#include +#include +#include +#include +#endif + +#ifdef CONFIG_FB +#include +#include +#endif + +#ifdef CONFIG_DRM +#include +#endif + +#if IS_ENABLED(CONFIG_DRM_MEDIATEK) +#include "mtk_panel_ext.h" +#include "mtk_disp_notify.h" +#endif + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + +#ifdef CONFIG_DRM_MSM +#include +#endif + +#ifdef ILI_SENSOR_EN +#include +#endif + +#ifdef CONFIG_MTK_SPI +#include "mt_spi.h" +#include "sync_write.h" +#endif +#include + +#define DRIVER_VERSION "3.0.9.0.230110" + +#define ENABLE 1 +#define DISABLE 0 +#define K (1024) +#define M (K * K) +#define START 1 +#define ON 1 +#define ILI_WRITE 1 +#define ILI_READ 0 +#define END 0 +#define OFF 0 +#define NONE -1 + +/* Options */ +#define TDDI_INTERFACE BUS_SPI /* BUS_I2C(0x18) or BUS_SPI(0x1C) */ +#define VDD_VOLTAGE 1800000 +#define VCC_VOLTAGE 1800000 +#define SPI_CLK 9 /* follow by clk list */ +#define SPI_RETRY 5 +#define IRQ_GPIO_NUM 66 +#define TR_BUF_SIZE (6*K) /* Buffer size of touch report */ +#define TR_BUF_LIST_SIZE (256) /* Buffer size of touch report for debug data */ +#define SPI_TX_BUF_SIZE (6*K) +#define SPI_RX_BUF_SIZE (6*K) +#define WQ_ESD_DELAY 4000 +#define WQ_BAT_DELAY 2000 +#define AP_INT_TIMEOUT 600 /*600ms*/ +#define MP_INT_TIMEOUT 5000 /*5s*/ +#define MT_B_TYPE ENABLE +#define TDDI_RST_BIND DISABLE +#define MT_PRESSURE DISABLE +#define ENABLE_WQ_ESD DISABLE +#define ENABLE_WQ_BAT DISABLE +#ifdef ILI_SENSOR_EN +#define ENABLE_GESTURE ENABLE +#else +#define ENABLE_GESTURE DISABLE +#endif +#define REGULATOR_POWER DISABLE +#define TP_SUSPEND_PRIO ENABLE +#define BOOT_FW_UPDATE ENABLE +#ifdef ILI_CONFIG_RESUME_BY_DDI +#define RESUME_BY_DDI ENABLE +#else +#define RESUME_BY_DDI DISABLE +#endif +#define MP_INT_LEVEL DISABLE +#define PLL_CLK_WAKEUP_TP_RESUME DISABLE +#ifdef ILI_CONFIG_MTK_CHARGER +#define CHARGER_NOTIFIER_CALLBACK ENABLE +#define USB_DETECT_IN 1 +#define USB_DETECT_OUT 2 +#else +#define USB_DETECT_IN 1 +#define USB_DETECT_OUT 2 +#define USB_DETECT_OUT_QCOM 0 +#define CHARGER_NOTIFIER_CALLBACK ENABLE +#endif +#define ENABLE_EDGE_PALM_PARA DISABLE +#define MULTI_REPORT_RATE DISABLE +#define ENGINEER_FLOW ENABLE +#define DMESG_SEQ_FILE ENABLE +#ifdef GKI_EN_FLAG +#define GENERIC_KERNEL_IMAGE ENABLE/*follow gki */ +#else +#define GENERIC_KERNEL_IMAGE DISABLE/*follow gki */ +#endif +#define SUSPEND_RESUME_SUPPORT ENABLE + +#define BOOT_FW_UPDATE_MODE BOOT_FW_VER_DIFF +#define BOOT_FW_VER_DIFF 0 +#define BOOT_FW_VER_UPGRADE 1 +#define BOOT_FW_VER_DOWNGRADE 2 + +/*Compress mode options*/ +#define ENABLE_COMPRESS_MODE DISABLE +#define COMPRESS_PACKET_LEN 4093 + +/*GET_ALL_INFO options*/ +#define ENABLE_GET_ALL_INFO DISABLE + +/*Pen mode options*/ +#define ENABLE_PEN_MODE DISABLE +#define ENABLE_CASCADE DISABLE + +#define PROXIMITY_RESET_CMD 0 +#define PROXIMITY_NO_RESET_CMD 1 + +#define PROXIMITY_IGNORE_STATE 0 +#define PROXIMITY_NEAR_STATE 1 +#define PROXIMITY_FAR_STATE 2 + +/*if current interface is spi, must to hostdownload */ +#if (TDDI_INTERFACE == BUS_SPI) +#define HOST_DOWN_LOAD ENABLE +#else +#define HOST_DOWN_LOAD DISABLE +#endif + +/* Plaform compatibility */ +#define CONFIG_PLAT_SPRD DISABLE +#define I2C_DMA_TRANSFER DISABLE +#if (ENABLE_CASCADE == ENABLE) +#define SPI_DMA_TRANSFER_SPLIT ENABLE +#else +#define SPI_DMA_TRANSFER_SPLIT DISABLE +#endif +#define SPI_DMA_TRANSFER_SPLIT_OLD DISABLE +#define SPRD_SYSFS_SUSPEND_RESUME DISABLE + +/* Path */ +#define DEBUG_DATA_FILE_SIZE (10*K) +#define DEBUG_DATA_FILE_PATH "/sdcard/ILITEK_log.csv" +#define CSV_LCM_ON_PATH "/sdcard/ilitek_mp_lcm_on_log" +#define CSV_LCM_OFF_PATH "/sdcard/ilitek_mp_lcm_off_log" +#define CSV_LCM_DEF_PATH "/data/local/tmp" +#define POWER_STATUS_PATH "/sys/class/power_supply/battery/status" +#define DUMP_FLASH_PATH "/sdcard/flash_dump" +#define DUMP_IRAM_PATH "/sdcard/iram_dump" + +/* Debug messages */ +#define DEBUG_NONE 0 +#define DEBUG_ALL 1 +#define DEBUG_OUTPUT DEBUG_NONE + +#define ILI_INFO(fmt, arg...) \ +({ \ + pr_info("ILITEK[INFO]: (%s, %d): " fmt, __func__, __LINE__, ##arg); \ +}) \ + +#define ILI_ERR(fmt, arg...) \ +({ \ + pr_err("ILITEK[ERR]: (%s, %d): " fmt, __func__, __LINE__, ##arg); \ +}) \ + +extern bool debug_en; +#define ILI_DBG(fmt, arg...) \ +do { \ + if (debug_en) \ + pr_info("ILITEK[DBG]: (%s, %d): " fmt, __func__, __LINE__, ##arg); \ +} while (0) + +#define ERR_ALLOC_MEM(X) ((IS_ERR(X) || X == NULL) ? 1 : 0) +#define DO_SPI_RECOVER -2 +#define DO_I2C_RECOVER -3 + +#define PEN_INPUT_DEVICE "ILITEK_PEN" + +#define SINGLE_TOUCH 0 +#define MULTI_TOUCH 1 +#define SPI_CLK_CASCADE_TABLE_NUM 3 + +#define NEGITIVE_FLAG BIT(7) +#define TIPSWITCH_HOVER_BIT (BIT(0)|BIT(4)) +#define MAX_PRESSURE (BIT(0)|BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)|BIT(8)|BIT(9)|BIT(10)|BIT(11)) + +/* Path */ +#define TXT_FILE_SIZE (1*K) +#define PEN_ID_NODE_TEST_PATH "/sdcard/Get_Wacom_PenID.txt" + +enum CASCADE_MODE { + MASTER = 0, + SLAVE, + BOTH, +}; + +enum PROXIMITY_MODE { + PROXIMITY_NULL = 0, + PROXIMITY_SUSPEND_RESUME, + PROXIMITY_BACKLIGHT +}; + +enum TP_SPI_CLK_LIST { + TP_SPI_CLK_1M = 1000000, + TP_SPI_CLK_2M = 2000000, + TP_SPI_CLK_3M = 3000000, + TP_SPI_CLK_4M = 4000000, + TP_SPI_CLK_5M = 5000000, + TP_SPI_CLK_6M = 6000000, + TP_SPI_CLK_7M = 7000000, + TP_SPI_CLK_8M = 8000000, + TP_SPI_CLK_9M = 9000000, + TP_SPI_CLK_10M = 10000000, + TP_SPI_CLK_11M = 11000000, + TP_SPI_CLK_12M = 12000000, + TP_SPI_CLK_13M = 13000000, + TP_SPI_CLK_14M = 14000000, + TP_SPI_CLK_15M = 15000000, +}; + +enum TP_PLAT_TYPE { + TP_PLAT_MTK = 0, + TP_PLAT_QCOM +}; + +enum TP_RST_METHOD { + TP_IC_WHOLE_RST_WITH_FLASH = 0, + TP_IC_WHOLE_RST_WITHOUT_FLASH, + TP_IC_CODE_RST, + TP_HW_RST_ONLY, +}; + +enum TP_FW_UPGRADE_TYPE { + UPGRADE_FLASH = 0, + UPGRADE_IRAM +}; + +enum TP_FW_UPGRADE_STATUS { + FW_STAT_INIT = 0, + FW_UPDATING = 90, + FW_UPDATE_PASS = 100, + FW_UPDATE_FAIL = -1 +}; + +enum TP_FW_OPEN_METHOD { + REQUEST_FIRMWARE = 0, + FILP_OPEN +}; + +enum TP_SLEEP_STATUS { + TP_SUSPEND = 0, + TP_DEEP_SLEEP = 1, + TP_RESUME = 2 +}; + +enum TP_PROXIMITY_STATUS { + DDI_POWER_OFF = 0, + DDI_POWER_ON = 1, + WAKE_UP_GESTURE_RECOVERY = 2, + WAKE_UP_SWITCH_GESTURE_MODE = 3 +}; + +enum TP_SLEEP_CTRL { + SLEEP_IN = 0x0, + SLEEP_OUT = 0x1, + DEEP_SLEEP_IN = 0x3 +}; + +enum TP_FW_BLOCK_NUM { + AP = 1, + DATA = 2, + TUNING = 3, + GESTURE = 4, + MP = 5, + DDI = 6, + TAG = 7, + PARA_BACKUP = 8, + RESERVE_BLOCK3 = 9, + PEN = 10, + RESERVE_BLOCK5 = 11, + RESERVE_BLOCK6 = 12, + RESERVE_BLOCK7 = 13, + RESERVE_BLOCK8 = 14, + RESERVE_BLOCK9 = 15, + RESERVE_BLOCK10 = 16, +}; + +enum TP_FW_BLOCK_TAG { + BLOCK_TAG_AF = 0xAF, + BLOCK_TAG_B0 = 0xB0 +}; + +enum TP_WQ_TYPE { + WQ_ESD = 0, + WQ_BAT, +}; + +enum TP_RECORD_DATA { + DISABLE_RECORD = 0, + ENABLE_RECORD, + DATA_RECORD +}; + +enum TP_DATA_FORMAT { + DATA_FORMAT_DEMO = 0, + DATA_FORMAT_DEBUG, + DATA_FORMAT_DEMO_DEBUG_INFO, + DATA_FORMAT_GESTURE_SPECIAL_DEMO, + DATA_FORMAT_GESTURE_INFO, + DATA_FORMAT_GESTURE_NORMAL, + DATA_FORMAT_GESTURE_DEMO, + DATA_FORMAT_GESTURE_DEBUG, + DATA_FORMAT_DEBUG_LITE_ROI, + DATA_FORMAT_DEBUG_LITE_WINDOW, + DATA_FORMAT_DEBUG_LITE_AREA, + DATA_FORMAT_DEBUG_LITE_PEN, +}; + +enum NODE_MODE_SWITCH { + AP_MODE = 0, + TEST_MODE, + DEBUG_MODE, + DEBUG_LITE_ROI, + DEBUG_LITE_WINDOW, + DEBUG_LITE_AREA, + DEBUG_PEN_ONLY_MODE, + DEBUG_HAND_ONLY_MODE, + DEBUG_LITE_PEN, +}; + +enum TP_MODEL { + MODEL_DEF = 0, + MODEL_CSOT, + MODEL_AUO, + MODEL_BOE, + MODEL_INX, + MODEL_DJ, + MODEL_TXD, + MODEL_TM +}; + +enum TP_ERR_CODE { + EMP_CMD = 100, + EMP_PROTOCOL, + EMP_FILE, + EMP_INI, + EMP_TIMING_INFO, + EMP_INVAL, + EMP_PARSE, + EMP_NOMEM, + EMP_GET_CDC, + EMP_INT, + EMP_CHECK_BUY, + EMP_MODE, + EMP_FW_PROC, + EMP_FORMUL_NULL, + EMP_PARA_NULL, + EFW_CONVERT_FILE, + EFW_ICE_MODE, + EFW_CRC, + EFW_REST, + EFW_ERASE, + EFW_PROGRAM, + EFW_INTERFACE, +}; + +enum TP_IC_TYPE { + ILI_A = 0x0A, + ILI_B, + ILI_C, + ILI_D, + ILI_E, + ILI_F, + ILI_G, + ILI_H, + ILI_I, + ILI_J, + ILI_K, + ILI_L, + ILI_M, + ILI_N, + ILI_O, + ILI_P, + ILI_Q, + ILI_R, + ILI_S, + ILI_T, + ILI_U, + ILI_V, + ILI_W, + ILI_X, + ILI_Y, + ILI_Z, +}; + +enum PROXIMITY_REPORT_STATE { + PROXIMITY_NONE_STATE_0 = 0, + PROXIMITY_NEAR_STATE_1 = 1, + PROXIMITY_NEAR_STATE_2 = 2, + PROXIMITY_NEAR_STATE_3 = 3, + PROXIMITY_NONE_STATE_4 = 4, + PROXIMITY_FAR_STATE_5 = 5, +}; + +#if MULTI_REPORT_RATE +enum MULTI_REPORT_RATE_TYPE { + DDI120_TP120 = 0, + DDI60_TP120 = 1, + DDI90_TP180 = 2, + DDI120_TP240 = 3, + DDI144_TP144 = 4, + DDI90_TP120 = 5, +}; +#endif + +typedef enum cmd_types{ + CMD_DISABLE = 0x00, + CMD_ENABLE = 0x01, + CMD_STATUS = 0x02, + CMD_ROI_DATA = 0x03, +} cmd_types; + +struct gesture_symbol { + u8 double_tap :1; + u8 alphabet_line_2_top :1; + u8 alphabet_line_2_bottom :1; + u8 alphabet_line_2_left :1; + u8 alphabet_line_2_right :1; + u8 alphabet_m :1; + u8 alphabet_w :1; + u8 alphabet_c :1; + u8 alphabet_E :1; + u8 alphabet_V :1; + u8 alphabet_O :1; + u8 alphabet_S :1; + u8 alphabet_Z :1; + u8 alphabet_V_down :1; + u8 alphabet_V_left :1; + u8 alphabet_V_right :1; + u8 alphabet_two_line_2_bottom :1; + u8 alphabet_F :1; + u8 alphabet_AT :1; + u8 single_tap :1; + u8 reserve0 :4; +}; + +struct report_info_block { + u8 nReportByPixel :1; + u8 nIsHostDownload :1; + u8 nIsSPIICE :1; + u8 nIsSPISLAVE :1; + u8 nIsI2C :1; + u8 nReserved00 :3; + u8 nReportResolutionMode:3; + u8 nCustomerType :5; + u8 nReserved02 :8; + u8 nReserved03 :8; +}; + +struct file_buffer { + char *ptr; + char fname[128]; + int32_t wlen; + int32_t flen; + int32_t max_size; +}; + +struct cascade_info_block { + u8 nDisable :1; + u8 nNum :3; + u8 nReserved00 :4; + u8 nReserved01 :8; + u8 nReserved02 :8; + u8 nReserved03 :8; +}; + +struct pen_info_block { + u8 nPxRaw :8; + u8 nPyRaw :8; + u8 nPxVa :8; + u8 nPyVa :8; + u8 nReserved00 :8; + u8 nReserved01 :8; + u8 nReserved02 :8; + u8 nReserved03 :8; +}; + +typedef enum pen_data_modes { + HAND_PEN_DEMO_MODE = 0, + HAND_PEN_SIGNAL_MODE, + HAND_PEN_RAW_DATA_MODE, + PEN_ONLY_SIGNAL_MODE, + PEN_ONLY_RAW_DATA_MODE, + HAND_ONLY_SIGNAL_MODE, + HAND_ONLY_RAW_DATA_MODE, +}pen_data_modes; + +typedef enum report_types { + P5_X_HAND_PEN_TYPE = 0, /* 0x39, FigType 0-2 bits, CustomType 3-5, PenType 6-7 */ + P5_X_ONLY_PEN_TYPE = 1, /* 0x3F */ + P5_X_ONLY_HAND_TYPE = 2, /* 0xF9 */ + +}report_types; + +struct ilitek_pen_info { + u16 x; + u16 y; + u16 pressure; + s32 tilt_x; + s32 tilt_y; + u8 pen_id[8]; + u8 distance_cnt; + bool TipSwitch; + bool BarelSwitch; + bool Eraser; + bool InRange; + bool active; + bool finger_touch; + report_types report_type; + pen_data_modes pen_data_mode; +}; + +#define TDDI_I2C_ADDR 0x41 +#define TDDI_I2C_SLAVE_ADDR 0x4f +#define TDDI_DEV_ID "ILITEK_TDDI" + + /* define the width and heigth of a screen. */ +#define TOUCH_SCREEN_X_MIN 0 +#define TOUCH_SCREEN_Y_MIN 0 +#define TOUCH_SCREEN_X_MAX 720 +#define TOUCH_SCREEN_Y_MAX 1440 +#define MAX_TOUCH_NUM 10 +#define MAX_PEN_NUM 1 +#define PEN_INDEX 10 +#define ILITEK_KNUCKLE_ROI_FINGERS 2 + +/* define the range on space, don't change */ +#define TPD_HEIGHT 2048 +#define TPD_WIDTH 2048 +#define TPD_HIGH_RESOLUTION_HEIGHT 8192 +#define TPD_HIGH_RESOLUTION_WIDTH 8192 + +/* Firmware upgrade */ +#define CORE_VER_1410 0x01040100 +#define CORE_VER_1420 0x01040200 +#define CORE_VER_1430 0x01040300 +#define CORE_VER_1460 0x01040600 +#define CORE_VER_1470 0x01040700 +#define CORE_VER_1600 0x01060000 +#define CORE_VER_1700 0x01070000 +#define MAX_HEX_FILE_SIZE (256*K) +#define ILI_FILE_HEADER 256 +#define DLM_START_ADDRESS 0x20610 +#define DLM_HEX_ADDRESS 0x10000 +#define MP_HEX_ADDRESS 0x13000 +#define DDI_RSV_BK_ST_ADDR 0x1E000 +#define DDI_RSV_BK_END_ADDR 0x1FFFF +#define DDI_RSV_BK_SIZE (1*K) +#define RSV_BK_ST_ADDR 0x1E000 +#define RSV_BK_END_ADDR 0x1E3FF +#define FW_VER_ADDR 0xFFE0 +#define FW_BLOCK_INFO_NUM 17 +#if SPI_DMA_TRANSFER_SPLIT +#define SPI_UPGRADE_LEN 2048 +#else +#define SPI_UPGRADE_LEN 0 +#endif +#define SPI_BUF_SIZE MAX_HEX_FILE_SIZE +#define INFO_HEX_ST_ADDR 0x4F +#define INFO_MP_HEX_ADDR 0x1F +#define INFO_PEN_ST_ADDR 0x67 +#define INFO_CASCADE_ST_ADDR 0x6B + + +/* DMA Control Registers */ +#define DMA_BASED_ADDR 0x72000 +#define DMA48_ADDR (DMA_BASED_ADDR + 0xC0) +#define DMA48_reg_dma_ch0_busy_flag DMA48_ADDR +#define DMA48_reserved_0 0xFFFE +#define DMA48_reg_dma_ch0_trigger_sel (BIT(16)|BIT(17)|BIT(18)|BIT(19)) +#define DMA48_reserved_1 (BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define DMA48_reg_dma_ch0_start_set BIT(24) +#define DMA48_reg_dma_ch0_start_clear BIT(25) +#define DMA48_reg_dma_ch0_trigger_src_mask BIT(26) +#define DMA48_reserved_2 BIT(27) + +#define DMA49_ADDR (DMA_BASED_ADDR + 0xC4) +#define DMA49_reg_dma_ch0_src1_addr DMA49_ADDR +#define DMA49_reserved_0 BIT(20) + +#define DMA50_ADDR (DMA_BASED_ADDR + 0xC8) +#define DMA50_reg_dma_ch0_src1_step_inc DMA50_ADDR +#define DMA50_reserved_0 (DMA50_ADDR + 0x01) +#define DMA50_reg_dma_ch0_src1_format (BIT(24)|BIT(25)) +#define DMA50_reserved_1 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)) +#define DMA50_reg_dma_ch0_src1_en BIT(31) + +#define DMA52_ADDR (DMA_BASED_ADDR + 0xD0) +#define DMA52_reg_dma_ch0_src2_step_inc DMA52_ADDR +#define DMA52_reserved_0 (DMA52_ADDR + 0x01) +#define DMA52_reg_dma_ch0_src2_format (BIT(24)|BIT(25)) +#define DMA52_reserved_1 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)) +#define DMA52_reg_dma_ch0_src2_en BIT(31) + +#define DMA53_ADDR (DMA_BASED_ADDR + 0xD4) +#define DMA53_reg_dma_ch0_dest_addr DMA53_ADDR +#define DMA53_reserved_0 BIT(20) + +#define DMA54_ADDR (DMA_BASED_ADDR + 0xD8) +#define DMA54_reg_dma_ch0_dest_step_inc DMA54_ADDR +#define DMA54_reserved_0 (DMA54_ADDR + 0x01) +#define DMA54_reg_dma_ch0_dest_format (BIT(24)|BIT(25)) +#define DMA54_reserved_1 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)) +#define DMA54_reg_dma_ch0_dest_en BIT(31) + +#define DMA55_ADDR (DMA_BASED_ADDR + 0xDC) +#define DMA55_reg_dma_ch0_trafer_counts DMA55_ADDR +#define DMA55_reserved_0 (BIT(17)|BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define DMA55_reg_dma_ch0_trafer_mode (BIT(24)|BIT(25)|BIT(26)|BIT(27)) +#define DMA55_reserved_1 (BIT(28)|BIT(29)|BIT(30)|BIT(31)) + +/* INT Function Registers */ +#define INTR_BASED_ADDR 0x48000 +#define INTR1_ADDR (INTR_BASED_ADDR + 0x4) +#define INTR1_FLASH_INT_FLAG (INTR_BASED_ADDR + 0x7) +#define INTR1_reg_uart_tx_int_flag INTR1_ADDR +#define INTR1_reserved_0 (BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)) +#define INTR1_reg_wdt_alarm_int_flag BIT(8) +#define INTR1_reserved_1 (BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) +#define INTR1_reg_dma_ch1_int_flag BIT(16) +#define INTR1_reg_dma_ch0_int_flag BIT(17) +#define INTR1_reg_dma_frame_done_int_flag BIT(18) +#define INTR1_reg_dma_tdi_done_int_flag BIT(19) +#define INTR1_reserved_2 (BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define INTR1_reg_flash_error_flag BIT(24) +#define INTR1_reg_flash_int_flag BIT(25) +#define INTR1_reserved_3 BIT(26) + +#define INTR2_ADDR (INTR_BASED_ADDR + 0x8) +#define INTR2_td_int_flag_clear INTR2_ADDR +#define INTR2_td_timeout_int_flag_clear BIT(1) +#define INTR2_td_debug_frame_done_int_flag_clear BIT(2) +#define INTR2_td_frame_start_scan_int_flag_clear BIT(3) +#define INTR2_log_int_flag_clear BIT(4) +#define INTR2_d2t_crc_err_int_flag_clear BIT(8) +#define INTR2_d2t_flash_req_int_flag_clear BIT(9) +#define INTR2_d2t_ddi_int_flag_clear BIT(10) +#define INTR2_wr_done_int_flag_clear BIT(16) +#define INTR2_rd_done_int_flag_clear BIT(17) +#define INTR2_tdi_err_int_flag_clear BIT(18) +#define INTR2_d2t_slpout_rise_flag_clear BIT(24) +#define INTR2_d2t_slpout_fall_flag_clear BIT(25) +#define INTR2_d2t_dstby_flag_clear BIT(26) +#define INTR2_ddi_pwr_rdy_flag_clear BIT(27) + +#define INTR32_ADDR (INTR_BASED_ADDR + 0x80) +#define INTR32_reg_ice_sw_int_en INTR32_ADDR +#define INTR32_reg_ice_apb_conflict_int_en BIT(1) +#define INTR32_reg_ice_ilm_conflict_int_en BIT(2) +#define INTR32_reg_ice_dlm_conflict_int_en BIT(3) +#define INTR32_reserved_0 (BIT(4)|BIT(5)|BIT(6)|BIT(7)) +#define INTR32_reg_spi_sr_int_en BIT(8) +#define INTR32_reg_spi_sp_int_en BIT(9) +#define INTR32_reg_spi_trx_int_en BIT(10) +#define INTR32_reg_spi_cmd_int_en BIT(11) +#define INTR32_reg_spi_rw_int_en BIT(12) +#define INTR32_reserved_1 (BIT(13)|BIT(14)|BIT(15)) +#define INTR32_reg_i2c_start_int_en BIT(16) +#define INTR32_reg_i2c_addr_match_int_en BIT(17) +#define INTR32_reg_i2c_cmd_int_en BIT(18) +#define INTR32_reg_i2c_sr_int_en BIT(19) +#define INTR32_reg_i2c_trx_int_en BIT(20) +#define INTR32_reg_i2c_rx_stop_int_en BIT(21) +#define INTR32_reg_i2c_tx_stop_int_en BIT(22) +#define INTR32_reserved_2 BIT(23) +#define INTR32_reg_t0_int_en BIT(24) +#define INTR32_reg_t1_int_en BIT(25) +#define INTR32_reserved_3 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) + +#define INTR33_ADDR (INTR_BASED_ADDR + 0x84) +#define INTR33_reg_uart_tx_int_en INTR33_ADDR +#define INTR33_reserved_0 (BIT(1)|BIT(2)|BIT(3)|BIT(4)|BIT(5)|BIT(6)|BIT(7)) +#define INTR33_reg_wdt_alarm_int_en BIT(8) +#define INTR33_reserved_1 (BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) +#define INTR33_reg_dma_ch1_int_en BIT(16) +#define INTR33_reg_dma_ch0_int_en BIT(17) +#define INTR33_reg_dma_frame_done_int_en BIT(18) +#define INTR33_reg_dma_tdi_done_int_en BIT(19) +#define INTR33_reserved_2 (BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define INTR33_reg_flash_error_en BIT(24) +#define INTR33_reg_flash_int_en BIT(25) +#define INTR33_reserved_3 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) + +/* Flash */ +#define FLASH_BASED_ADDR 0x41000 +#define FLASH0_ADDR (FLASH_BASED_ADDR + 0x0) +#define FLASH0_reg_flash_csb FLASH0_ADDR +#define FLASH0_reserved_0 (BIT(8)|BIT(9)|BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) +#define FLASH0_reg_preclk_sel (BIT(16)|BIT(17)|BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define FLASH0_reg_rx_dual BIT(24) +#define FLASH0_reg_tx_dual BIT(25) +#define FLASH0_reserved_26 (BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) +#define FLASH0_dual_mode (FLASH0_ADDR + 0x3) +#define FLASH1_ADDR (FLASH_BASED_ADDR + 0x4) +#define FLASH1_reg_flash_key1 FLASH1_ADDR +#define FLASH1_reg_flash_key2 (FLASH1_ADDR + 0x01) +#define FLASH1_reg_flash_key3 (FLASH1_ADDR + 0x02) +#define FLASH1_reserved_0 (FLASH1_ADDR + 0x03) +#define FLASH2_ADDR (FLASH_BASED_ADDR + 0x8) +#define FLASH2_reg_tx_data FLASH2_ADDR +#define FLASH3_ADDR (FLASH_BASED_ADDR + 0xC) +#define FLASH3_reg_rcv_cnt FLASH3_ADDR +#define FLASH4_ADDR (FLASH_BASED_ADDR + 0x10) +#define FLASH4_reg_rcv_data FLASH4_ADDR +#define FLASH4_reg_rcv_dly BIT(8) +#define FLASH4_reg_sutrg_en BIT(9) +#define FLASH4_reserved_1 (BIT(10)|BIT(11)|BIT(12)|BIT(13)|BIT(14)|BIT(15)) +#define FLASH4_reg_rcv_data_valid_state BIT(16) +#define FLASH4_reg_flash_rd_finish_state BIT(17) +#define FLASH4_reserved_2 (BIT(18)|BIT(19)|BIT(20)|BIT(21)|BIT(22)|BIT(23)) +#define FLASH4_reg_flash_dma_trigger_en (BIT(24)|BIT(25)|BIT(26)|BIT(27)|BIT(28)|BIT(29)|BIT(30)|BIT(31)) + +/* Dummy Registers */ +#define WDT_DUMMY_BASED_ADDR 0x5101C +#define WDT7_DUMMY0 WDT_DUMMY_BASED_ADDR +#define WDT8_DUMMY1 (WDT_DUMMY_BASED_ADDR + 0x04) +#define WDT9_DUMMY2 (WDT_DUMMY_BASED_ADDR + 0x08) + +/* Cascade Register */ +#define QUAL_MODE_REG 0x62020 +#define MS_MISO_SEL_REG 0x62021 +#define SINGLE_TO_QUAL_REG 0x6202C +#define READ_BACK_LOCK_REG 0x6202D +#define SLAVE_READ_BACK_WORD_REG 0x62030 +#define MSPI_REG 0x62084 +#define ENABLE_CASCADE_ADDR 0x71062 +#define MSPI_TRIG_BY_DMA_ADDR 0x62070 +#define MSPI_TRIGGER_ADDR 0x62085 +#define MSPI_CLOCK_ADDR 0x62086 +#define MSPI_CS_ADDR 0x62087 +#define MSPI_INIT_EN_ADDR 0x480C4 +#define DMA_ONE_GROUP_ADDR 0x72318 +#define DMA_ONE_CLEAR_ADDR 0x72103 +#define SRC_ONE_ADDR 0x72104 +#define SRC_ONE_SET_ADDR 0x72108 +#define SRC_TWO_EN_ADDR 0x72113 +#define DEST_ADDR 0x72114 +#define DEST_SET_ADDR 0x72118 +#define TRIG_SEL_MSPI_ADDR 0x72102 +#define TRANSFER_CNT_ADDR 0x7211C +#define WRITE_BUF_SIZE_ADDR 0x62080 +#define READ_BUF_SIZE_ADDR 0x62082 +#define WRITE_BUF_ADDR 0x6205C +#define MSPI_DONE_FLAG_ADDR 0x48028 +#define CONTROL_SLAVE_EXIT_ICE_ADDR 0x181062 +#define WDT_ADDR 0x4004D +#define DMA_CRC_ADDR 0x04101C + +/* The example for the gesture virtual keys */ +#define GESTURE_DOUBLECLICK 0x58 +#define GESTURE_SINGLECLICK 0x57 +#define GESTURE_UP 0x60 +#define GESTURE_DOWN 0x61 +#define GESTURE_LEFT 0x62 +#define GESTURE_RIGHT 0x63 +#define GESTURE_M 0x64 +#define GESTURE_W 0x65 +#define GESTURE_C 0x66 +#define GESTURE_E 0x67 +#define GESTURE_V 0x68 +#define GESTURE_O 0x69 +#define GESTURE_S 0x6A +#define GESTURE_Z 0x6B +#define KEY_GESTURE_POWER KEY_POWER +#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_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 KEY_GESTURE_F KEY_F +#define GESTURE_V_DOWN 0x6C +#define GESTURE_V_LEFT 0x6D +#define GESTURE_V_RIGHT 0x6E +#define GESTURE_TWOLINE_DOWN 0x6F +#define GESTURE_F 0x70 +#define GESTURE_AT 0x71 +#define ESD_GESTURE_PWD 0xF38A94EF +#define SPI_ESD_GESTURE_RUN 0xA67C9DFE +#define I2C_ESD_GESTURE_RUN 0xA67C9DFE +#define SPI_ESD_GESTURE_PWD_ADDR 0x25FF8 +#define I2C_ESD_GESTURE_PWD_ADDR 0x40054 + +#define ESD_GESTURE_CORE146_PWD 0xF38A +#define SPI_ESD_GESTURE_CORE146_RUN 0x5B92 +#define I2C_ESD_GESTURE_CORE146_RUN 0xA67C +#define SPI_ESD_GESTURE_CORE146_PWD_ADDR 0x4005C +#define I2C_ESD_GESTURE_CORE146_PWD_ADDR 0x4005C + +#define DOUBLE_TAP (ON)/* BIT0 */ +#define ALPHABET_LINE_2_TOP (ON)/* BIT1 */ +#define ALPHABET_LINE_2_BOTTOM (ON)/* BIT2 */ +#define ALPHABET_LINE_2_LEFT (ON)/* BIT3 */ +#define ALPHABET_LINE_2_RIGHT (ON)/* BIT4 */ +#define ALPHABET_M (ON)/* BIT5 */ +#define ALPHABET_W (ON)/* BIT6 */ +#define ALPHABET_C (ON)/* BIT7 */ +#define ALPHABET_E (ON)/* BIT8 */ +#define ALPHABET_V (ON)/* BIT9 */ +#define ALPHABET_O (ON)/* BIT10 */ +#define ALPHABET_S (ON)/* BIT11 */ +#define ALPHABET_Z (ON)/* BIT12 */ +#define ALPHABET_V_DOWN (ON)/* BIT13 */ +#define ALPHABET_V_LEFT (ON)/* BIT14 */ +#define ALPHABET_V_RIGHT (ON)/* BIT15 */ +#define ALPHABET_TWO_LINE_2_BOTTOM (ON)/* BIT16 */ +#define ALPHABET_F (ON)/* BIT17 */ +#define ALPHABET_AT (ON)/* BIT18 */ +#define SINGLE_TAP (ON)/* BIT19 */ + +/* FW data format */ +#define DATA_FORMAT_DEMO_CMD 0x00 +#define DATA_FORMAT_DEBUG_CMD 0x02 +#define DATA_FORMAT_DEMO_DEBUG_INFO_CMD 0x04 +#define DATA_FORMAT_GESTURE_NORMAL_CMD 0x01 +#define DATA_FORMAT_GESTURE_INFO_CMD 0x02 +#define DATA_FORMAT_DEBUG_LITE_CMD 0x05 +#define DATA_FORMAT_DEBUG_LITE_ROI_CMD 0x01 +#define DATA_FORMAT_DEBUG_LITE_WINDOW_CMD 0x02 +#define DATA_FORMAT_DEBUG_LITE_AREA_CMD 0x03 +#define DATA_FORMAT_DEBUG_LITE_PEN_CMD 0x04 + +/* Pen data format */ +#define DATA_FORMAT_DEBUG_PEN_CMD 0x06 +#define DATA_FORMAT_DEBUG_HAND_CMD 0x07 + +#define P5_X_DEMO_MODE_PACKET_INFO_LEN 3 +#define P5_X_DEMO_MODE_PACKET_LEN 43 +#define P5_X_DEMO_MODE_PACKET_LEN_HIGH_RESOLUTION 72 +#define P5_X_DEMO_LOW_RESOLUTION_FINGER_DATA_LENGTH 44 +#define P5_X_DEMO_HIGH_RESOLUTION_FINGER_DATA_LENGTH 54 +#define P5_X_DEMO_MODE_AXIS_LEN 50 +#define P5_X_DEMO_MODE_STATE_INFO 16 +#define P5_X_INFO_HEADER_LENGTH 3 +#define P5_X_P_SENSOR_KEY_LENGTH 1 +#define P5_X_INFO_CHECKSUM_LENGTH 1 +#define P5_X_DEMO_DEBUG_INFO_ID0_LENGTH 14 +#define P5_X_DEBUG_MODE_PACKET_LENGTH 1280 +#define P5_X_TEST_MODE_PACKET_LENGTH 1180 +#define P5_X_GESTURE_NORMAL_LENGTH 8 +#define P5_X_GESTURE_INFO_LENGTH 170 +#define P5_X_GESTURE_INFO_LENGTH_HIGH_RESOLUTION 221 +#if (TDDI_INTERFACE == BUS_SPI) +#define P5_X_DEBUG_LITE_LENGTH 4093 +#else +#define P5_X_DEBUG_LITE_LENGTH 300 +#endif +#define P5_X_DEBUG_LITE_PEN_LENGTH 136 +#define P5_X_CORE_VER_THREE_LENGTH 5 +#define P5_X_CORE_VER_FOUR_LENGTH 6 +#define P5_X_EDGE_PALM_PARA_LENGTH 31 +#define P5_X_DEBUG_LOW_RESOLUTION_FINGER_DATA_LENGTH 35 +#define P5_X_DEBUG_HIGH_RESOLUTION_FINGER_DATA_LENGTH 45 +#define P5_X_5B_LOW_RESOLUTION_LENGTH 62 +#define P5_X_CUSTOMER_LENGTH 50 +#define P5_X_DEBUG_MODE_PACKET_INFO_LEN 4 + +/* Pen data info len */ +#define P5_X_ID_TYPE_LEN 5 +#define P5_X_PEN_INFO_X_DATA_LEN 6 +#define P5_X_PEN_INFO_Y_DATA_LEN 4 +#define P5_X_PEN_DATA_LEN 24 +#define P5_X_OTHER_DATA_LEN 16 +#define P5_X_PEN_ID_LEN 8 +#define P5_X_HAND_PEN_TYPE_PACKET_LEN 96 +#define P5_X_NEW_DEBUG_FORMAT_X_LEN 50 +#define P5_X_NEW_DEBUG_FORMAT_Y_LEN 50 +#define TOUCH_TILT_OFFSET 10 +#define TOUCH_PRESS_OFFSET 12 +#define PEN_ID_OFFSET 14 + +/* Pen Report mode */ +#define ONLY_REPORT_HAND 2 +#define ONLY_REPORT_PEN 1 +#define REPORT_HAND_PEN 0 +#define RELEASE_PEN 0 +#define PEN_CONTACT 1 + +/* Protocol */ +#define PROTOCOL_VER_500 0x050000 +#define PROTOCOL_VER_510 0x050100 +#define PROTOCOL_VER_520 0x050200 +#define PROTOCOL_VER_530 0x050300 +#define PROTOCOL_VER_540 0x050400 +#define PROTOCOL_VER_550 0x050500 +#define PROTOCOL_VER_560 0x050600 +#define PROTOCOL_VER_570 0x050700 +#define P5_X_READ_DATA_CTRL 0xF6 +#define P5_X_GET_TP_INFORMATION 0x20 +#define P5_X_GET_KEY_INFORMATION 0x27 +#define P5_X_GET_TOOL_VERSION 0x28 +#define P5_X_GET_PANEL_INFORMATION 0x29 +#define P5_X_GET_FW_VERSION 0x21 +#define P5_X_GET_PROTOCOL_VERSION 0x22 +#define P5_X_GET_CORE_VERSION 0x23 +#define P5_X_GET_CORE_VERSION_NEW 0x24 +#define P5_X_GET_COMPRESS_INFORMATION 0x2B +#define P5_X_GET_ALL_INFORMATION 0x2F +#define P5_X_GET_REPORT_FORMAT 0x37 +#define P5_X_MODE_CONTROL 0xF0 +#define P5_X_NEW_CONTROL_FORMAT 0xF2 +#define P5_X_SET_CDC_INIT 0xF1 +#define P5_X_GET_CDC_DATA 0xF2 +#define P5_X_CDC_BUSY_STATE 0xF3 +#define P5_X_MP_TEST_MODE_INFO 0xFE +#define P5_X_I2C_UART 0x40 +#define CMD_GET_FLASH_DATA 0x41 +#define CMD_CTRL_INT_ACTION 0x1B +#define P5_X_FW_UNKNOWN_MODE 0xFF +#define P5_X_FW_AP_MODE 0x00 +#define P5_X_FW_TEST_MODE 0x01 +#define P5_X_FW_GESTURE_MODE 0x0F +#define P5_X_FW_SIGNAL_DATA_MODE 0x03 +#define P5_X_FW_RAW_DATA_MODE 0x08 +#define P5_X_DEMO_PACKET_ID 0x5A +#define P5_X_DEMO_AXIS_PACKET_ID 0x5B +#define P5_X_DEBUG_PACKET_ID 0xA7 +#define P5_X_DEBUG_AXIS_PACKET_ID 0xA8 +#define P5_X_TEST_PACKET_ID 0xF2 +#define P5_X_GESTURE_PACKET_ID 0xAA +#define P5_X_GESTURE_FAIL_ID 0xAE +#define P5_X_I2CUART_PACKET_ID 0x7A +#define P5_X_DEBUG_LITE_PACKET_ID 0x9A +#define P5_X_SLAVE_MODE_CMD_ID 0x5F +#define P5_X_INFO_HEADER_PACKET_ID 0xB7 +#define P5_X_DEMO_DEBUG_INFO_PACKET_ID 0x5C +#define P5_X_EDGE_PLAM_CTRL_1 0x01 +#define P5_X_EDGE_PLAM_CTRL_2 0x12 +#define P5_X_EDGE_PALM_TUNING_PARA 0x1E +#define SPI_WRITE 0x82 +#define SPI_READ 0x83 +#define SPI_ACK 0xA3 +#define P5_X_DEMO_PROXIMITY_ID 0xBC +#define P5_X_DEMO_HIGH_RESOLUTION_PACKET_ID 0x5B +#define P5_X_DEBUG_HIGH_RESOLUTION_PACKET_ID 0xA8 + +/*Pen & Cascade info cmd*/ +#define P5_X_GET_PEN_INFO 0x27 +#define P5_X_GET_CASCADE_INFO 0x2A + +/* Pen Type */ +#define P5_X_HAND_PEN_TYPE 0 /* 0x39, FigType 0-2 bits, CustomType 3-5, PenType 6-7 */ +#define P5_X_ONLY_PEN_TYPE 1 /* 0x3F */ +#define P5_X_ONLY_HAND_TYPE 2 /* 0xF9 */ + +/* Chips */ +#define ILI2882_CHIP 0x2882 +#define ILI9881_CHIP 0x9881 +#define ILI7807_CHIP 0x7807 +#define ILI9881N_AA 0x98811700 +#define ILI9881O_AA 0x98811800 +#define ILI7807Q_AA 0x78071A00 +#define ILI9881N_AA 0x98811700 +#define ILI9882_CHIP 0x9882 +#define ILI9883_CHIP 0x9883 +#define TDDI_PID_ADDR 0x4009C +#define TDDI_OTP_ID_ADDR 0x400A0 +#define TDDI_ANA_ID_ADDR 0x400A4 +#define TDDI_PC_COUNTER_ADDR 0x44008 +#define TDDI_PC_LATCH_ADDR 0x51010 +#define TDDI_CHIP_RESET_ADDR 0x40050 +#define RAWDATA_NO_BK_SHIFT 8192 +#define TDDI_WHOLE_CHIP_RST_WITH_FLASH_KEY 0x00019878 +#define TDDI_WHOLE_CHIP_RST_WITHOUT_FLASH_KEY 0xA0019878 +#define TDDI_WHOLE_CHIP_RST_WITHOUT_FLASH_PRE_KEY 0xA0009878 + +/* Edge Palm */ +#define ZONE_A_W 6 +#define ZONE_B_W 32 +#define ZONE_A_ROTATION_W 6 +#define ZONE_B_ROTATION_W 32 +#define ZONE_C_WIDTH 80 +#define ZONE_C_HEIGHT 320 +#define ZONE_C_HEIGHT_LITTLE 100 + +/* DDI */ +#define PAGE00_CMD10_SLEEPIN 0x10 +#define PAGE00_CMD11_SLEEPOUT 0x11 +#define PAGE00_CMD28_DISPLAYOFF 0x28 +#define PAGE00_CMD29_DISPLAYON 0x29 + +/* Report Format Resolution */ +#define POSITION_LOW_RESOLUTION 0X00 +#define POSITION_HIGH_RESOLUTION 0x01 +#define POSITION_CUSTOMER_TYPE_ON 0x00 +#define POSITION_CUSTOMER_TYPE_OFF 0x1F +#define POSITION_PEN_TYPE_ON 0x00 +#define POSITION_PEN_TYPE_OFF 0x03 +#define POSITION_CUSTOMER_TYPE_OFF_3BITS 0x07 /*core ver 1700, CustomerType 3 bits*/ + +#define SRAM_TEST_GROUP_LEN 16 +#define SRAM_TEST_GROUP_STR_LEN 64 + +#ifdef ILI_SENSOR_EN +enum touch_panel_id { + TOUCH_PANEL_IDX_PRIMARY = 0, + TOUCH_PANEL_MAX_IDX, +}; + +enum touch_state { + TOUCH_DEEP_SLEEP_STATE = 0, + TOUCH_LOW_POWER_STATE, +}; +struct ili_sensor_platform_data { + struct input_dev *input_sensor_dev; + struct sensors_classdev ps_cdev; + int sensor_opened; + char sensor_data; /* 0 near, 1 far */ + struct ilitek_ts_data *data; +}; +#define REPORT_MAX_COUNT 10000 +#endif + +struct sram_test_para { + /* ini param */ + bool tpu_clock_en; + bool sram_group_en; + bool check_failbus; + u8 group_num; + u32 MaxAddr; + u32 MinAddr; + u32 write_addr; + u32 read_addr; + u32 SramPartialAreaTest_en[SRAM_TEST_GROUP_LEN]; + u32 SramResult[SRAM_TEST_GROUP_LEN]; + char strSramPartialArea[SRAM_TEST_GROUP_LEN][SRAM_TEST_GROUP_STR_LEN]; +}; + +struct ilitek_ts_data { + struct i2c_client *i2c; + struct spi_device *spi; + struct input_dev *input; + struct input_dev *input_pen; + struct device *dev; + struct wakeup_source *ws; + + struct ilitek_hwif_info *hwif; + struct ilitek_ic_info *chip; + struct ilitek_protocol_info *protocol; + struct gesture_coordinate *gcoord; + struct regulator *vdd; + struct regulator *vcc; + struct sram_test_para sram_para; + +#if IS_ENABLED(CONFIG_DRM_MEDIATEK) + struct notifier_block disp_notifier; +#endif +#if defined(CONFIG_FB) || defined(CONFIG_DRM) + struct notifier_block notifier_fb; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif +#if CHARGER_NOTIFIER_CALLBACK +#if KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE +/* add_for_charger_start */ + struct notifier_block notifier_charger; + struct workqueue_struct *charger_notify_wq; + struct work_struct update_charger; + int usb_plug_status; +/* add_for_charger_end */ +#endif +#endif +#ifdef ROI + struct ts_kit_device_data *ts_dev_data; +#endif + struct mutex touch_mutex; + struct mutex debug_mutex; + struct mutex debug_read_mutex; + spinlock_t irq_spin; + + struct completion pm_completion; + bool pm_suspend; + + /* physical path to the input device in the system hierarchy */ + const char *phys; + + bool boot; + u32 fw_pc; + u32 fw_latch; + + u16 max_x; + u16 max_y; + u16 min_x; + u16 min_y; + u16 panel_wid; + u16 panel_hei; + u8 xch_num; + u8 ych_num; + u8 stx; + u8 srx; + u8 *update_buf; + u8 *tr_buf; + u8 *spi_tx; + u8 *spi_rx; +#ifdef ROI + u8 knuckle_roi_data[ROI_DATA_READ_LENGTH]; +#endif + struct firmware tp_fw; + + int actual_tp_mode; + int tp_data_mode; + int tp_data_format; + int tp_data_len; + + int irq_num; + int irq_gpio; + int irq_tirgger_type; + int tp_rst; + int tp_int; + int wait_int_timeout; + + int finger; + u8 customertype_off; + u32 cdc_data_len; + u8 gesture_data_type; + bool compress_disable; + bool compress_handonly_disable; + bool compress_penonly_disable; + + /* pen report */ + u8 PenType; + int pen; + struct pen_info_block pen_info_block; + struct ilitek_pen_info pen_info; + int curt_touch[MAX_TOUCH_NUM + MAX_PEN_NUM]; + int prev_touch[MAX_TOUCH_NUM + MAX_PEN_NUM]; + u8 touch_num; + + int last_touch; + int fw_retry; + int fw_update_stat; + int fw_open; + u8 fw_info[75]; + u8 fw_mp_ver[4]; + u8 edge_palm_para[P5_X_EDGE_PALM_PARA_LENGTH]; + bool wq_ctrl; + bool wq_esd_ctrl; + bool wq_bat_ctrl; + bool esd_func_ctrl; + + /* Cascade */ + struct cascade_info_block cascade_info_block; + bool mspi_en; + bool slave_wr_ctrl; + bool enable_cascade; + bool mspi_done; + bool i2c_addr_change; + int cascade_rst_edge_delay; + u8 cascade_ctrl_mode; + + u16 addr_value; + + bool report; + bool gesture; + bool mp_retry; + bool knuckle; + int gesture_mode; + int gesture_demo_ctrl; + struct gesture_symbol ges_sym; + struct report_info_block rib; + +#if CHARGER_NOTIFIER_CALLBACK + const char *psy_name; +#endif + + u16 flash_mid; + u16 flash_devid; + u8 current_report_rate_mode; + int program_page; + int flash_sector; + + /* Sending report data to users for the debug */ + bool dnp; /* debug node open */ + int dbf; /* debug data frame */ + int odi; /* out data index */ + bool all_frame_full; /* all debug data frame is full */ + wait_queue_head_t inq; + struct debug_buf_list *dbl; + int raw_count; + int delta_count; + int bg_count; + + /* ping pong buffer */ + bool dlnp; /* debug all node open */ + bool ppb; + struct debug_buf_list *dbf_a; + struct debug_buf_list *dbf_b; + struct debug_buf_list *dbf_out; + struct debug_buf_list *dbf_in; + int dbf_in_idx; + int dbf_out_idx; + + int reset; + int rst_edge_delay; + int fw_upgrade_mode; + int mp_ret_len; + bool wtd_ctrl; + bool force_fw_update; + bool irq_after_recovery; + bool ddi_rest_done; + bool resume_by_ddi; + bool tp_suspend; + bool info_from_hex; + bool prox_near; + bool gesture_load_code; + bool trans_xy; + bool ss_ctrl; + bool node_update; + bool int_pulse; + bool pll_clk_wakeup; + bool power_status; + bool proxmity_face; + bool eng_flow; + bool ice_flash_cs_ctrl; + bool prox_face_gesture; + u8 prox_face_mode; + /* module info */ + int tp_module; + int md_fw_ili_size; + char *md_name; + char *md_fw_filp_path; + char *md_fw_rq_path; + char *md_ini_path; + char *md_ini_rq_path; + char *outputStrArr; + u8 *md_fw_ili; + char *mp_result; + + atomic_t irq_stat; + atomic_t tp_reset; + atomic_t ice_stat; + atomic_t fw_stat; + atomic_t mp_stat; + atomic_t tp_sleep; + atomic_t tp_sw_mode; + atomic_t cmd_int_check; + atomic_t esd_stat; + + int set_stowed; + int get_stowed; +#ifdef ILI_SENSOR_EN + bool wakeable; + bool should_enable_gesture; + bool gesture_enabled; + uint32_t report_gesture_key; +#ifdef ILI_DOUBLE_TAP_CTRL + uint8_t supported_gesture_type; + uint8_t sys_gesture_type; + uint8_t rst_pull_flag; +#endif + struct mutex state_mutex; + struct ili_sensor_platform_data *sensor_pdata; +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock gesture_wakelock; +#else + struct wakeup_source *gesture_wakelock; +#endif +#endif //ILI_SENSOR_EN + + atomic_t ignore_report; + atomic_t sync_stat; + atomic_t slave_ice_stat; + + /* Event for driver test */ + struct completion esd_done; + + int (*spi_write_then_read)(struct spi_device *spi, const void *txbuf, + unsigned n_tx, void *rxbuf, unsigned n_rx); + int (*wrapper)(u8 *wdata, u32 wlen, u8 *rdata, u32 rlen, bool spi_irq, bool i2c_irq); + int (*mp_move_code)(void); + int (*gesture_move_code)(int mode); + int (*esd_recover)(void); + int (*ges_recover)(void); + void (*demo_debug_info[5])(u8 *, size_t); + int (*detect_int_stat)(bool status); +#ifdef ILI_TOUCH_LAST_TIME + ktime_t last_event_time; +#endif +}; +extern struct ilitek_ts_data *ilits; + +struct debug_buf_list { + bool mark; + unsigned char *data; +}; + +struct ilitek_touch_info { + u16 id; + u16 x; + u16 y; + u16 pressure; +}; + +struct ilitek_axis_info { + s8 degree; + u16 width_major; + u16 width_minor; +}; + +struct ilitek_dma_config { + u32 src_addr; + u32 src_fmt; + u32 dest_addr; + u32 dest_fmt; + u32 block_size; +}; + +struct gesture_coordinate { + u16 code; + u8 clockwise; + int type; + struct ilitek_touch_info pos_start; + struct ilitek_touch_info pos_end; + struct ilitek_touch_info pos_1st; + struct ilitek_touch_info pos_2nd; + struct ilitek_touch_info pos_3rd; + struct ilitek_touch_info pos_4th; +}; + +struct ilitek_protocol_info { + u32 ver; + int fw_ver_len; + int pro_ver_len; + int tp_info_len; + int key_info_len; + int panel_info_len; + int core_ver_len; + int func_ctrl_len; + int window_len; + int cdc_len; + int mp_info_len; +}; + +#define FUNC_CTRL_NUM 17 +#define FUNC_CTRL_PROXIMITY_IDX 5 +struct ilitek_ic_func_ctrl { + const char *name; + u8 cmd[6]; + int len; + u8 def_cmd; + u8 rec_state; /* 0:disable, 1: enable, 2: ignore record */ + u8 rec_cmd; +}; +extern struct ilitek_ic_func_ctrl func_ctrl[FUNC_CTRL_NUM]; + +struct ilitek_ic_info { + u8 type; + u8 ver; + u16 id; + u8 slave_ver; + u16 slave_id; + u32 slave_pid; + u32 pid; + u32 pid_addr; + u32 product_id; + u32 pc_counter_addr; + u32 pc_latch_addr; + u32 reset_addr; + u32 otp_addr; + u32 ana_addr; + u32 otp_id; + u32 ana_id; + u32 fw_ver; + u32 core_ver; + u32 fw_mp_ver; + u32 max_count; + u32 reset_key; + u16 wtd_key; + int no_bk_shift; + bool dma_reset; + int (*dma_crc)(u32 start_addr, u32 block_size); +}; + +struct ilitek_hwif_info { + u8 bus_type; + u8 plat_type; + const char *name; + struct module *owner; + const struct of_device_id *of_match_table; + const struct dev_pm_ops *pm; + int (*plat_probe)(void); + int (*plat_remove)(void); + void *info; +}; + +/* Prototypes for tddi firmware/flash functions */ +extern void ili_flash_dma_write(u32 start, u32 end, u32 len); +extern void ili_flash_clear_dma(void); +extern void ili_fw_read_flash_info(bool mcu); +extern void ilitek_tddi_flash_protect(bool enable, bool mcu); +extern int ili_fw_read_hw_crc(u32 start, u32 end, u32 *flash_crc); +extern int ili_fw_read_flash(u32 start, u32 end, u8 *data, int len); +extern int ili_fw_dump_iram_data(u32 start, u32 end, bool save, bool mcu); +extern int ili_fw_dump_flash_data(u32 start, u32 end, bool user, bool mcu); +extern int ili_fw_upgrade(int op); + +/* Prototypes for tddi mp test */ +extern int ili_mp_test_main(char *apk, bool lcm_on); + +/* Prototypes for tddi core functions */ +extern int ili_touch_esd_gesture_flash(void); +extern int ili_touch_esd_gesture_iram(void); +extern void ili_set_gesture_symbol(void); +extern int ili_move_gesture_code_flash(int mode); +extern int ili_move_gesture_code_iram(int mode); +extern int ili_move_mp_code_flash(void); +extern int ili_move_mp_code_iram(void); +extern void ili_touch_press(u16 x, u16 y, u16 pressure, u16 id); +extern void ili_touch_release(u16 x, u16 y, u16 id); +extern void ili_touch_release_all_point(void); +extern void ili_report_ap_mode(u8 *buf, int len); +extern void ili_pen_release(void); +extern void ili_pen_demo_mode_report_point(u8 *buf, int len); +extern void ili_report_pen_ap_mode(u8 *buf, int len); +extern void ili_report_pen_debug_mode(u8 *buf, int len); +extern void ili_report_debug_mode(u8 *buf, int rlen); +extern void ili_pen_debug_mode_report_point(u8 *buf, int len, u8 offset); +extern void ili_report_debug_lite_mode(u8 *buf, int rlen); +extern void ili_report_gesture_mode(u8 *buf, int rlen); +extern void ili_report_proximity_mode(u8 *buf, int len); +extern void ili_report_i2cuart_mode(u8 *buf, int rlen); +extern void ili_ic_set_ddi_reg_onepage(u8 page, u8 reg, u8 data, bool mcu); +extern void ili_ic_get_ddi_reg_onepage(u8 page, u8 reg, u8 *data, bool mcu); +extern void ili_ic_set_cascade_ddi_reg_onepage(u8 page, u8 reg, u8 data, bool mcu); +extern void ili_ic_get_cascade_ddi_reg_onepage(u8 page, u8 reg, u8 *data, bool mcu); +extern int ili_ic_whole_reset(bool mcu, bool withflash); +extern int ili_ic_code_reset(bool mcu); +extern int ili_ic_func_ctrl(const char *name, int ctrl); +extern int ili_ic_func_ctrl_export(const char *name, int ctrl); +extern void ili_ic_func_ctrl_reset(void); +extern void ili_ic_edge_palm_para_init(void); +extern void ili_ic_send_edge_palm_para(void); +extern int ili_ic_int_trigger_ctrl(bool pulse); +extern void ili_ic_get_pc_counter(int stat); +extern int ili_ic_check_int_level(bool level); +extern int ili_ic_check_int_pulse(bool pulse); +extern int ili_ic_check_busy(int count, int delay, bool irq); +extern int ili_ic_get_project_id(u8 *pdata, int size); +extern int ili_ic_get_panel_info(void); +extern int ili_ic_get_tp_info(void); +extern int ili_ic_get_core_ver(void); +extern int ili_ic_get_protocl_ver(void); +extern int ili_ic_get_fw_ver(void); +extern int ili_ic_get_info(void); +extern int ili_ic_get_all_info(void); +extern void ili_ic_get_compress_info(void); +extern int ili_set_bypass_mode(bool mcu); +extern int ili_cascade_ic_get_info(bool enter_ice, bool exit_ice); +extern void ili_mspi_set_slave_to_qual_mode(void); +extern int ili_mspi_write_slave_register(u32 addr, u32 data, int len); +extern void ili_mspi_read_slave_register(u32 addr, u32 *data, int len); +extern void ili_wdt_reset_status( bool enter_ice, bool exit_ice); +extern int ili_cascade_reset_ctrl(int reset_mode, bool enter_ice); +extern int ili_cascade_rw_tp_reg(u32 mcu, u32 type, u32 addr, u32 wdata, u32 wlen, u32 *rdata); +extern int ili_ic_dummy_check(void); +extern int ili_ic_report_rate_set(u8 mode); +extern int ili_ic_report_rate_get(void); +extern int ili_ic_report_rate_register_set(void); +extern int ili_ice_mode_bit_mask_write(u32 addr, u32 mask, u32 value); +extern int ili_ice_mode_write(u32 addr, u32 data, int len); +extern void ili_cascade_sync_ctrl(bool mode); +extern int ili_ice_mode_ctrl_by_mode(bool enable, bool mcu, int mode); +extern int ili_ice_mode_write_by_mode(u32 addr, u32 data, int len, int mode); +extern int ili_ice_mode_read_by_mode(u32 addr, u32 *data, int len, int mode); +extern int ili_ice_mode_read(u32 addr, u32 *data, int len); +extern int ili_ice_mode_ctrl(bool enable, bool mcu); +extern void ili_ic_init(void); +extern void ili_fw_uart_ctrl(u8 ctrl); +extern int ilitek_tddi_mp_ini_parser(const char *path); +extern int ili_parsing_sram_param(void); +extern int ili_tddi_ic_sram_test(void); +#ifdef ROI +extern int ili_read_knuckle_roi_data(void); +extern int ili_config_get_knuckle_roi_status(void); +extern int ili_config_knuckle_roi_ctrl(cmd_types cmd); +#endif +/* Prototypes for tddi events */ +#if RESUME_BY_DDI +extern void ili_resume_by_ddi(void); +#endif +extern int ili_proximity_far(int mode); +extern int ili_proximity_near(int mode); +extern int ili_switch_tp_mode(u8 data); +extern int ili_set_tp_data_len(int format, bool send, u8 *data); +extern int ili_set_pen_data_len(u8 header, u8 ctrl, u8 type); +extern int ili_fw_upgrade_handler(void *data); +extern int ili_wq_esd_i2c_check(void); +extern int ili_wq_esd_spi_check(void); +extern int ili_gesture_recovery(void); +extern void ili_spi_recovery(void); +extern void ili_wq_ctrl(int type, int ctrl); +extern int ili_mp_test_handler(char *apk, bool lcm_on); +extern int ili_report_handler(void); +extern int ili_sleep_handler(int mode); +extern int ili_reset_ctrl(int mode); +extern int ili_tddi_init(void); +extern int ili_dev_init(struct ilitek_hwif_info *hwif); +extern void ili_dev_remove(bool flag); + +/* Prototypes for i2c/spi interface */ +extern int ili_core_spi_setup(int num); +extern int ili_interface_dev_init(struct ilitek_hwif_info *hwif); +extern void ili_interface_dev_exit(struct ilitek_ts_data *ts, bool flag); +extern int ili_spi_write_then_read_split(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx); +extern int ili_spi_write_then_read_direct(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx); + +/* Prototypes for platform level */ +extern void ili_plat_regulator_power_on(bool status); +extern void ili_input_register(void); +extern void ili_input_pen_register(void); +extern void ili_irq_disable(void); +extern void ili_irq_enable(void); +extern void ili_tp_reset(void); +extern void ili_irq_unregister(void); +extern int ili_irq_register(int type); +extern void ilitek_plat_charger_init(void); + +/* Prototypes for miscs */ +extern void ili_node_init(void); +extern void ili_dump_data(void *data, int type, int len, int row_len, const char *name); +extern u8 ili_calc_packet_checksum(u8 *packet, int len); +extern int ili_katoi(char *str); +extern int ili_str2hex(char *str); + +extern int ili_mp_read_write(u8 *wdata, u32 wlen, u8 *rdata, u32 rlen); +/* Prototypes for additional functionalities */ +extern void ili_gesture_fail_reason(bool enable); +extern int ili_get_tp_recore_ctrl(int data); +extern int ili_get_tp_recore_data(bool mcu); +extern void ili_demo_debug_info_mode(u8 *buf, size_t rlen); +extern void ili_demo_debug_info_id0(u8 *buf, size_t len); +extern void ili_get_dma1_config(struct ilitek_dma_config *dma); +extern void ili_set_dma1_config(struct ilitek_dma_config *dma); +extern int file_write(struct file_buffer *file, bool new_open); +extern int ili_ic_check_debug_lite_support(int format, bool send, u8 *data); + +#ifdef ILI_SET_TOUCH_STATE +extern int touch_set_state(int state, int panel_idx); +#endif + +#ifdef ILI_DOUBLE_TAP_CTRL +void ili_gesture_state_switch(void); +#endif + +static inline void ipio_kfree(void **mem) +{ + if (*mem != NULL) { + kfree(*mem); + *mem = NULL; + } +} + +static inline void ipio_vfree(void **mem) +{ + if (*mem != NULL) { + vfree(*mem); + *mem = NULL; + } +} + +static inline void *ipio_memcpy(void *dest, const void *src, int n, int dest_size) +{ + if (n > dest_size) + n = dest_size; + + return memcpy(dest, src, n); +} + +static inline int ipio_strcmp(const char *s1, const char *s2) +{ + return (strlen(s1) != strlen(s2)) ? -1 : strncmp(s1, s2, strlen(s1)); +} + +#endif /* __ILI9881X_H */ diff --git a/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_flash.c b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_flash.c new file mode 100644 index 000000000000..c9ec5525271d --- /dev/null +++ b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_flash.c @@ -0,0 +1,1905 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek_v3.h" + +#define UPDATE_PASS 0 +#define UPDATE_FAIL -1 +#define TIMEOUT_SECTOR 500 +#define TIMEOUT_PAGE 3500 +#define TIMEOUT_PROGRAM 10 +#define TIMEOUT_PROTECT 100 + +static struct touch_fw_data { + u8 block_number; + u32 start_addr; + u32 end_addr; + u32 new_fw_cb; + u32 new_mp_fw_cb; + int delay_after_upgrade; + bool isCRC; + bool isboot; + bool is80k; + int hex_tag; +} tfd; + +static struct flash_block_info { + char *name; + u32 start; + u32 end; + u32 len; + u32 mem_start; + u32 fix_mem_start; + u8 mode; +} fbi[FW_BLOCK_INFO_NUM]; + +static u8 fw_flash_tmp[K + 7]; +static u8 *pfw; +static u8 *CTPM_FW; + +/*sync with tool*/ +struct ilitek_flash_info { + u8 rgisterNum; + u8 readOPCount; + u8 writeOPCount; + u8 protectAllOPCount; + u8 resetOPCount; + u32 flashUID; + u8 readOperator[30]; + u8 writeOperator[30]; + u8 protectAllOperator[30]; + u8 resetOperator[30]; +} flash_protect_list[] = { + [0] = {1,1,1,4,4,0x1C3812,{0x05,1},{0x01,1},{2,0x1,3,0x1,4,0x1,5,0x1},{2,0x0,3,0x0,4,0x0,5,0x0}}, + [1] = {3,2,1,5,6,0xC22810,{0x05,1,0x15,2},{0x01,3},{2,0x1,3,0x1,4,0x1,5,0x1,11,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,11,0x0}}, + [2] = {3,2,1,5,6,0xC22811,{0x05,1,0x15,2},{0x01,3},{2,0x1,3,0x1,4,0x1,5,0x1,11,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,11,0x0}}, + [3] = {3,2,1,5,6,0xC22812,{0x05,1,0x15,2},{0x01,3},{2,0x1,3,0x1,4,0x1,5,0x1,11,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,11,0x0}}, + [4] = {3,2,1,5,6,0xC22813,{0x05,1,0x15,2},{0x01,3},{2,0x1,3,0x1,4,0x1,5,0x1,11,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,11,0x0}}, + [5] = {1,1,1,2,3,0xC22531,{0x05,1},{0x01,1},{2,0x1,3,0x1},{2,0x0,3,0x0,6,0x0}}, + [6] = {2,2,1,5,6,0xC22310,{0x05,1,0x15,1},{0x01,2},{2,0x1,3,0x1,4,0x1,5,0x1,11,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,11,0x0}}, + [7] = {1,1,1,2,2,0xC22011,{0x05,1},{0x01,1},{2,0x1,3,0x1},{2,0x0,3,0x0}}, + [8] = {2,2,1,6,7,0x856013,{0x05,1,0x35,1},{0x01,2},{2,0x1,3,0x1,4,0x1,5,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,9,0x0,14,0x0}}, + [9] = {2,2,1,6,7,0xC86013,{0x05,1,0x35,1},{0x01,2},{2,0x1,3,0x1,4,0x1,5,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,9,0x0,14,0x0}}, + [10] = {2,2,2,6,7,0xEF6011,{0x05,1,0x35,1},{0x01,1,0x31,1},{2,0x1,3,0x1,4,0x1,5,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,9,0x0,14,0x0}}, + [11] = {2,2,2,6,7,0xEF6012,{0x05,1,0x35,1},{0x01,1,0x31,1},{2,0x1,3,0x1,4,0x1,5,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,9,0x0,14,0x0}}, + [12] = {1,1,1,4,5,0x9D1152,{0x05,1},{0x01,1},{2,0x0,3,0x0,4,0x0,5,0x01},{2,0x0,3,0x00,4,0x0,5,0x00,6,0x00}}, + [13] = {1,1,1,3,3,0xC86012,{0x05,1},{0x01,1},{2,0x1,3,0x01,4,0x1},{2,0x0,3,0x00,4,0x0}}, + [14] = {2,2,1,6,7,0xEF4012,{0x05,1,0x35,1},{0x01,2},{2,0x1,3,0x1,4,0x1,5,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,9,0x0,14,0x0}}, + [15] = {2,2,2,6,9,0xA12813,{0x05,1,0x35,1},{0x01,1,0x31,1},{2,0x1,3,0x1,4,0x1,5,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,14,0x0,9,0x0,11,0x0,12,0x0}}, + [16] = {2,2,2,6,7,0xEF6013,{0x05,1,0x35,1},{0x01,1,0x31,1},{2,0x1,3,0x1,4,0x1,5,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,9,0x0,14,0x0}}, + [17] = {2,2,1,6,7,0xEB6012,{0x05,1,0x35,1},{0x01,2},{2,0x1,3,0x1,4,0x1,5,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,9,0x0,14,0x0}}, + [18] = {3,3,3,6,7,0xC44012,{0x05,1,0x35,1,0x15,1},{0x01,1,0x31,1,0x11,1},{2,0x1,3,0x1,4,0x1,5,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,9,0x0,14,0x0}}, + [19] = {2,2,1,6,7,0x856012,{0x05,1,0x35,1},{0x01,2},{2,0x1,3,0x1,4,0x1,5,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,9,0x0,14,0x0}}, + [20] = {1,1,1,2,2,0x0B6012,{0x05,1},{0x01,1},{2,0x1,3,0x1},{2,0x0,3,0x0}}, + [21] = {2,2,1,6,7,0x854012,{0x05,1,0x35,1},{0x01,2},{2,0x1,3,0x1,4,0x1,5,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,9,0x0,14,0x0}}, + [22] = {2,2,1,6,7,0x854011,{0x05,1,0x35,1},{0x01,2},{2,0x1,3,0x1,4,0x1,5,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,9,0x0,14,0x0}}, + [23] = {1,1,1,3,3,0x5E1013,{0x05,1},{0x01,1},{2,0x1,3,0x1,4,0x1},{2,0x0,3,0x0,4,0x0}}, + [24] = {1,1,1,3,3,0x5E1012,{0x05,1},{0x01,1},{2,0x1,3,0x1,4,0x1},{2,0x0,3,0x0,4,0x0}}, + [25] = {3,3,3,6,7,0x681012,{0x05,1,0x35,1,0x15,1},{0x01,1,0x31,1,0x11,1},{2,0x1,3,0x1,4,0x1,5,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,9,0x0,14,0x0}}, + [26] = {2,2,1,6,7,0x686013,{0x05,1,0x35,1},{0x01,2},{2,0x1,3,0x1,4,0x1,5,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,9,0x0,14,0x0}}, + [27] = {1,1,1,3,3,0x5E1011,{0x05,1},{0x01,1},{2,0x1,3,0x1,4,0x1},{2,0x0,3,0x0,4,0x0}}, + [28] = {3,3,3,6,7,0xC44013,{0x05,1,0x35,1,0x15,1},{0x01,1,0x31,1,0x11,1},{2,0x1,3,0x1,4,0x1,5,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,9,0x0,14,0x0}}, + [29] = {3,3,3,6,7,0xC44011,{0x05,1,0x35,1,0x15,1},{0x01,1,0x31,1,0x11,1},{2,0x1,3,0x1,4,0x1,5,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,9,0x0,14,0x0}}, + [30] = {3,3,3,6,7,0xC44010,{0x05,1,0x35,1,0x15,1},{0x01,1,0x31,1,0x11,1},{2,0x1,3,0x1,4,0x1,5,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,9,0x0,14,0x0}}, + /* 3.3V flash */ + [31] = {1,1,1,2,2,0xEF3012,{0x05,1},{0x01,1},{2,0x1,3,0x1},{2,0x0,3,0x0}}, + [32] = {2,2,1,6,7,0xC84013,{0x05,1,0x35,1},{0x01,2},{2,0x1,3,0x1,4,0x1,5,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,4,0x0,5,0x0,6,0x0,9,0x0,14,0x0}}, + [33] = {2,2,1,5,5,0x114012,{0x05,1,0x35,1},{0x01,2},{2,0x1,3,0x1,4,0x1,6,0x1,14,0x0},{2,0x0,3,0x0,6,0x0,9,0x0,14,0x0}}, +}; + +static u32 HexToDec(char *phex, s32 len) +{ + u32 ret = 0, temp = 0, i; + s32 shift = (len - 1) * 4; + + for (i = 0; i < len; shift -= 4, i++) { + if ((phex[i] >= '0') && (phex[i] <= '9')) + temp = phex[i] - '0'; + else if ((phex[i] >= 'a') && (phex[i] <= 'f')) + temp = (phex[i] - 'a') + 10; + else if ((phex[i] >= 'A') && (phex[i] <= 'F')) + temp = (phex[i] - 'A') + 10; + + ret |= (temp << shift); + } + return ret; +} + +static int CalculateCRC32(u32 start_addr, u32 len, u8 *pfw) +{ + int i = 0, j = 0; + int crc_poly = 0x04C11DB7; + int tmp_crc = 0xFFFFFFFF; + + for (i = start_addr; i < start_addr + len; i++) { + tmp_crc ^= (pfw[i] << 24); + + for (j = 0; j < 8; j++) { + if ((tmp_crc & 0x80000000) != 0) + tmp_crc = tmp_crc << 1 ^ crc_poly; + else + tmp_crc = tmp_crc << 1; + } + } + return tmp_crc; +} + +static int ilitek_tddi_fw_iram_read(u8 *buf, u32 start, int len) +{ + int limit = 4*K; + int addr = 0, loop = 0, tmp_len = len, cnt = 0; + u8 cmd[4] = {0}; + + if (!buf) { + ILI_ERR("buf is null\n"); + return -ENOMEM; + } + + if (len % limit) + loop = (len / limit) + 1; + else + loop = len / limit; + + for (cnt = 0, addr = start; cnt < loop; cnt++, addr += limit) { + tmp_len = len - cnt * limit; + ilits->fw_update_stat = ((len - tmp_len) * 100) / len; + ILI_DBG("Reading iram data .... %d%c", ilits->fw_update_stat, '%'); + + if (tmp_len > limit) + tmp_len = limit; + + cmd[0] = 0x25; + cmd[3] = (char)((addr & 0x00FF0000) >> 16); + cmd[2] = (char)((addr & 0x0000FF00) >> 8); + cmd[1] = (char)((addr & 0x000000FF)); + + if (ilits->wrapper(cmd, 4, NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Failed to write iram data\n"); + return -ENODEV; + } + + if (ilits->wrapper(NULL, 0, buf + cnt * limit, tmp_len, OFF, OFF) < 0) { + ILI_ERR("Failed to Read iram data\n"); + return -ENODEV; + } + + } + return 0; +} + +int ili_fw_dump_iram_data(u32 start, u32 end, bool save, bool mcu) +{ +#if GENERIC_KERNEL_IMAGE + int tmp = debug_en; +#else + struct file *f = NULL; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + mm_segment_t old_fs; +#endif + loff_t pos = 0; +#endif + int i, ret, len; + u8 *fw_buf = NULL; + + if (ili_ice_mode_ctrl(ENABLE, mcu) < 0) { + ILI_ERR("Enable ice mode failed\n"); + ret = -1; + goto out; + } + + len = end - start + 1; + + if (len > MAX_HEX_FILE_SIZE) { + ILI_ERR("len is larger than buffer, abort\n"); + ret = -EINVAL; + goto out; + } + + fw_buf = kzalloc(MAX_HEX_FILE_SIZE, GFP_KERNEL); + if (ERR_ALLOC_MEM(fw_buf)) { + ILI_ERR("Failed to allocate update_buf\n"); + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + fw_buf[i] = 0xFF; + + ret = ilitek_tddi_fw_iram_read(fw_buf, start, len); + if (ret < 0) + goto out; + +#if GENERIC_KERNEL_IMAGE + ILI_ERR("GKI version not allow drivers to use filp_open\n"); + ret = -1; + debug_en = DEBUG_ALL; + ili_dump_data(fw_buf, 8, len, 0, "IRAM"); + debug_en = tmp; +#else + f = filp_open(DUMP_IRAM_PATH, O_WRONLY | O_CREAT | O_TRUNC, 644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file at %ld.\n", PTR_ERR(f)); + ret = -1; + goto out; + } + pos = 0; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + old_fs = get_fs(); + set_fs(KERNEL_DS); + vfs_write(f, fw_buf, len, &pos); + set_fs(old_fs); +#else + kernel_write(f, fw_buf, len, &pos); +#endif + filp_close(f, NULL); + ILI_INFO("Save iram data to %s\n", DUMP_IRAM_PATH); +#endif +out: + ili_ice_mode_ctrl(DISABLE, mcu); + ILI_INFO("Dump IRAM %s\n", (ret < 0) ? "FAIL" : "SUCCESS"); + ipio_kfree((void **)&fw_buf); + return ret; +} + +static int ilitek_tddi_flash_poll_busy(int timer) +{ + int ret = UPDATE_PASS, retry = timer; + u8 cmd = 0x5; + u32 temp = 0; + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Pull cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, cmd, 1) < 0) + ILI_ERR("Write 0x5 cmd failed\n"); + + do { + if (ili_ice_mode_write(FLASH2_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + mdelay(1); + + if (ili_ice_mode_read(FLASH4_ADDR, &temp, sizeof(u8)) < 0) + ILI_ERR("Read flash busy error\n"); + + if ((temp & 0x3) == 0) + break; + } while (--retry >= 0); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Pull cs high failed\n"); + + if (retry <= 0) { + ILI_ERR("Flash polling busy timeout ! tmp = %x\n", temp); + ret = UPDATE_FAIL; + } + + return ret; +} + +void ili_flash_clear_dma(void) +{ + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_preclk_sel, (2 << 16)) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_preclk_sel, FLASH0_ADDR); + + if (ili_ice_mode_bit_mask_write(FLASH4_ADDR, FLASH4_reg_flash_dma_trigger_en, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH4_reg_flash_dma_trigger_en, FLASH4_ADDR); + + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_rx_dual, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_rx_dual, FLASH0_ADDR); + + if (ili_ice_mode_write(FLASH3_reg_rcv_cnt, 0x00, 1) < 0) + ILI_ERR("Write 0x0 at %x failed\n", FLASH3_reg_rcv_cnt); + + if (ili_ice_mode_write(FLASH4_reg_rcv_data, 0xFF, 1) < 0) + ILI_ERR("Write 0xFF at %x failed\n", FLASH4_reg_rcv_data); +} + +static int ilitek_tddi_flash_read_int_flag(void) +{ + int retry = 100; + u32 data = 0; + + do { + if (ili_ice_mode_read(INTR1_ADDR & BIT(25), &data, sizeof(u32)) < 0) + ILI_ERR("Read flash int flag error\n"); + + ILI_DBG("int flag = %x\n", data); + if (data) + break; + mdelay(2); + } while (--retry >= 0); + + if (retry <= 0) { + ILI_ERR("Read Flash INT flag timeout !, flag = 0x%x\n", data); + return -1; + } + return 0; +} + +void ili_flash_dma_write(u32 start, u32 end, u32 len) +{ + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_preclk_sel, 1 << 16) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_preclk_sel, FLASH0_ADDR); + + if (ili_ice_mode_write(FLASH0_reg_flash_csb, 0x00, 1) < 0) + ILI_ERR("Pull cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_reg_flash_key1, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, 0x0b, 1) < 0) + ILI_ERR("Write 0x0b at %x failed\n", FLASH2_reg_tx_data); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write 0xb timeout \n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, (start & 0xFF0000) >> 16, 1) < 0) + ILI_ERR("Write %x at %x failed\n", (start & 0xFF0000), FLASH2_reg_tx_data); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write addr1 timeout\n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, (start & 0x00FF00) >> 8, 1) < 0) + ILI_ERR("Write %x at %x failed\n", (start & 0x00FF00) >> 8, FLASH2_reg_tx_data); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write addr2 timeout\n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, (start & 0x0000FF), 1) < 0) + ILI_ERR("Write %x at %x failed\n", (start & 0x0000FF), FLASH2_reg_tx_data); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write addr3 timeout\n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_bit_mask_write(FLASH0_ADDR, FLASH0_reg_rx_dual, 0 << 24) < 0) + ILI_ERR("Write %lu at %x failed\n", FLASH0_reg_rx_dual, FLASH0_ADDR); + + if (ili_ice_mode_write(FLASH2_reg_tx_data, 0x00, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + if (ilitek_tddi_flash_read_int_flag() < 0) { + ILI_ERR("Write dummy timeout\n"); + return; + } + + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + if (ili_ice_mode_write(FLASH3_reg_rcv_cnt, len, 4) < 0) + ILI_ERR("Write length failed\n"); +} + +static void ilitek_tddi_flash_write_enable(void) +{ + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Pull CS low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x6, 1) < 0) + ILI_ERR("Write Write_En failed\n"); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Pull CS high failed\n"); +} + +int ili_fw_read_hw_crc(u32 start, u32 end, u32 *flash_crc) +{ + int retry = 100; + u32 busy = 0; + u32 write_len = end; + + if (write_len > ilits->chip->max_count) { + ILI_ERR("The length (%x) written into firmware is greater than max count (%x)\n", + write_len, ilits->chip->max_count); + return -1; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Pull CS low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x3b, 1) < 0) + ILI_ERR("Write 0x3b failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0xFF0000) >> 16, 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0x00FF00) >> 8, 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0x0000FF), 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(0x041003, 0x01, 1) < 0) + ILI_ERR("Write enable Dio_Rx_dual failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + if (ili_ice_mode_write(0x04100C, write_len, 3) < 0) + ILI_ERR("Write Set Receive count failed\n"); + + if (ili_ice_mode_write(0x048007, 0x02, 1) < 0) + ILI_ERR("Write clearing int flag failed\n"); + + if (ili_ice_mode_write(0x041016, 0x00, 1) < 0) + ILI_ERR("Write 0x0 at 0x041016 failed\n"); + + if (ili_ice_mode_write(0x041016, 0x01, 1) < 0) + ILI_ERR("Write Checksum_En failed\n"); + + if (ili_ice_mode_write(FLASH4_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write start to receive failed\n"); + + do { + if (ili_ice_mode_read(0x048007, &busy, sizeof(u8)) < 0) + ILI_ERR("Read busy error\n"); + + ILI_DBG("busy = %x\n", busy); + if (((busy >> 1) & 0x01) == 0x01) + break; + mdelay(2); + } while (--retry >= 0); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write CS high failed\n"); + + if (retry <= 0) { + ILI_ERR("Read HW CRC timeout !, busy = 0x%x\n", busy); + return -1; + } + + if (ili_ice_mode_write(0x041003, 0x0, 1) < 0) + ILI_ERR("Write disable dio_Rx_dual failed\n"); + + if (ili_ice_mode_read(0x04101C, flash_crc, sizeof(u32)) < 0) { + ILI_ERR("Read hw crc error\n"); + return -1; + } + + return 0; +} + +static int ilitek_tddi_fw_read_flash_data(u32 start, u32 end, u8 *data, int len) +{ + u32 i, j, index = 0; + u32 tmp; + + if (end - start > len) { + ILI_ERR("the length (%d) reading crc is over than len(%d)\n", end - start, len); + return -1; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x03, 1) < 0) + ILI_ERR("Write 0x3 failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0xFF0000) >> 16, 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0x00FF00) >> 8, 1) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, (start & 0x0000FF), 1) < 0) + ILI_ERR("Write address failed\n"); + + for (i = start, j = 0; i <= end; i++, j++) { + if (ili_ice_mode_write(FLASH2_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + if (ili_ice_mode_read(FLASH4_ADDR, &tmp, sizeof(u8)) < 0) + ILI_ERR("Read flash data error!\n"); + + data[index] = tmp; + index++; + ilits->fw_update_stat = (j * 100) / len; + ILI_DBG("Reading flash data .... %d%c", ilits->fw_update_stat, '%'); + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + + return 0; +} + +int ili_fw_dump_flash_data(u32 start, u32 end, bool user, bool mcu) +{ + u32 start_addr, end_addr; + int ret, length; + u8 *buf = NULL; +#if GENERIC_KERNEL_IMAGE + int tmp = debug_en; +#else + struct file *f = NULL; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + mm_segment_t old_fs; +#endif + loff_t pos = 0; + f = filp_open(DUMP_FLASH_PATH, O_WRONLY | O_CREAT | O_TRUNC, 644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file at %ld.\n", PTR_ERR(f)); + ret = -1; + goto out; + } +#endif + ret = ili_ice_mode_ctrl(ENABLE, mcu); + if (ret < 0) + goto out; + + if (user) { + start_addr = 0x0; + end_addr = 0x1FFFF; + } else { + start_addr = start; + end_addr = end; + } + + length = end_addr - start_addr + 1; + ILI_INFO("len = %d\n", length); + + buf = vmalloc(length * sizeof(u8)); + if (ERR_ALLOC_MEM(buf)) { + ILI_ERR("Failed to allocate buf memory, %ld\n", PTR_ERR(buf)); + ret = -1; + goto out; + } + + ret = ilitek_tddi_fw_read_flash_data(start_addr, end_addr, buf, length); + if (ret < 0) + goto out; + +#if GENERIC_KERNEL_IMAGE + ILI_ERR("GKI version not allow drivers to use filp_open, dump flash data\n"); + debug_en = DEBUG_ALL; + ili_dump_data(buf, 8, length, 0, "flash"); + debug_en = tmp; +#else + pos = 0; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + old_fs = get_fs(); + set_fs(KERNEL_DS); + vfs_write(f, buf, length, &pos); + set_fs(old_fs); +#else + kernel_write(f, buf, length, &pos); +#endif +#endif +out: +#if (!GENERIC_KERNEL_IMAGE) + filp_close(f, NULL); +#endif + ipio_vfree((void **)&buf); + ili_ice_mode_ctrl(DISABLE, mcu); + ILI_INFO("Dump flash %s\n", (ret < 0) ? "FAIL" : "SUCCESS"); + return ret; +} + +void ilitek_tddi_flash_read(u32 *data, u8 cmd, int len) +{ + u32 rxbuf[4] = {0}; + int i = 0; + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0)/* CS Low */ + ILI_ERR("Write cs low failed\n"); + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, cmd, 1) < 0) + ILI_ERR("Write read command failed\n"); + for (i = 0; i < len; i++) { + if (ili_ice_mode_write(FLASH2_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write read command failed\n"); + ili_ice_mode_read(FLASH4_ADDR, data, 1); + rxbuf[i] = *data; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + + *data = 0; + if (len == 1) + *data = rxbuf[0]; + else if (len == 2) + *data = (rxbuf[0] | rxbuf[1] << 8); + else if (len == 3) + *data = (rxbuf[0] | rxbuf[1] << 8 | rxbuf[2] << 16); + else + *data = (rxbuf[0] | rxbuf[1] << 8 | rxbuf[2] << 16 | rxbuf[3] << 24); + ILI_DBG("flash read data = 0X%X\n", *data); +} + +void ilitek_tddi_flash_write(u8 cmd, u8 *sendbuf,int len) +{ + int i = 0; + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0)/* CS Low */ + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, cmd, 1) < 0) + ILI_ERR("Write read command failed\n"); + + for (i = 0; i < len; i++) { + if (ili_ice_mode_write(FLASH2_ADDR, sendbuf[i], 1) < 0) + ILI_ERR("Write sendbuf failed\n"); + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) /* CS high */ + ILI_ERR("Write cs high failed\n"); +} + +void ilitek_tddi_flash_protect(bool enable, bool mcu) +{ + int ret = 0, i = 0, index = 0, w_count = 0; + u32 flash_uid = 0, data = 0, ckreaddata = 0; + u8 bitPosition = 0, setBitValue = 0; + u8 w_buf[4] = { 0 }; + bool isSupportFlash = false; + bool ice = atomic_read(&ilits->ice_stat); + + ILI_INFO("%s flash protection\n", enable ? "Enable" : "Disable"); + + if (!ice) { + if (ilits->cascade_info_block.nNum != 0) + ret = ili_ice_mode_ctrl_by_mode(ENABLE, OFF, BOTH); + else + ret = ili_ice_mode_ctrl(ENABLE, mcu); + + if (ret < 0) + ILI_ERR("Enable ice mode failed\n"); + } + + flash_uid = (ilits->flash_mid << 16) + ilits->flash_devid; + + if (flash_uid == 0x0) { + ILI_ERR("flash_uid error, get flash info again!\n"); + ili_fw_read_flash_info(OFF); + flash_uid = (ilits->flash_mid << 16) + ilits->flash_devid; + } + + for (i = 0; i < ARRAY_SIZE(flash_protect_list); i++) { + if (flash_uid == flash_protect_list[i].flashUID) { + isSupportFlash = true; + index = i; + break; + } + } + + ILI_INFO("flash(0x%X) is %s\n", flash_uid, isSupportFlash ? "supported" : "not supported"); + + if (isSupportFlash) { + + /* read flash data */ + for (i = 0; i < flash_protect_list[index].readOPCount; i++) { + u32 tmp = 0; + u8 cmd = flash_protect_list[index].readOperator[i * 2]; + int readlen = flash_protect_list[index].readOperator[(i * 2) + 1]; + ilitek_tddi_flash_read(&tmp, cmd, readlen); + data |= tmp << (i * 8); + } + + ILI_DBG("data = 0x%X\n", data); + if (enable) { + /* set flash all protect bit */ + for (i = 0; i < flash_protect_list[index].protectAllOPCount; i++) { + bitPosition = flash_protect_list[index].protectAllOperator[i * 2]; + setBitValue = flash_protect_list[index].protectAllOperator[(i * 2) + 1]; + + if (setBitValue == 1) + data |= (0x1 << bitPosition); + else + data &= ~(0x1 << bitPosition); + } + } else { + /* set flash non-protect bit */ + for (i = 0; i < flash_protect_list[index].resetOPCount; i++) { + bitPosition = flash_protect_list[index].resetOperator[i * 2]; + setBitValue = flash_protect_list[index].resetOperator[(i * 2) + 1]; + + if (setBitValue == 1) + data |= (0x1 << bitPosition); + else + data &= ~(0x1 << bitPosition); + } + } + + for (i = 0; i < 4; i++) + w_buf[i] = (data >> (8 * i)) & 0xFF; + + ILI_DBG("write data = 0x%X\n", data); + + for (i = 0; i < flash_protect_list[index].writeOPCount; i++) { + u8 cmd = flash_protect_list[index].writeOperator[i * 2]; + int writelen = flash_protect_list[index].writeOperator[(i * 2) + 1]; + + ilitek_tddi_flash_write_enable(); + + ilitek_tddi_flash_write(cmd, &w_buf[w_count], writelen); + + if (ilitek_tddi_flash_poll_busy(TIMEOUT_PROTECT) < 0) + ILI_ERR("pulling busy fail\n"); + w_count += writelen; + } + + if (enable) { + /* make sure the flash is protected */ + ckreaddata = 0; + + for (i = 0; i < flash_protect_list[index].readOPCount; i++) { + u32 tmp = 0; + u8 cmd = flash_protect_list[index].readOperator[i * 2]; + int readlen = flash_protect_list[index].readOperator[(i * 2) + 1]; + ilitek_tddi_flash_read(&tmp, cmd, readlen); + ckreaddata |= tmp << (i * 8); + } + + ILI_DBG("data = 0x%X, ckreaddata = 0x%X\n", data, ckreaddata); + + if (ckreaddata != data) + ILI_ERR("protect flash fail, data = 0x%X, ckreaddata = 0x%X\n", data, ckreaddata); + } + + } else { + /*It only tries to unprotect flash in the unsupported list.*/ + if (enable) { + ILI_ERR("no need to protect flash\n"); + goto out; + } + + ilitek_tddi_flash_write_enable(); + mdelay(30); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0)/* CS Low */ + ILI_ERR("Write cs low failed\n"); + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + if (ili_ice_mode_write(FLASH2_ADDR, 0x01, 1) < 0) + ILI_ERR("Write Start_Write failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x00, 1) < 0) + ILI_ERR("Write Un-Protect Low byte failed\n"); + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + } +out: + if (!ice) { + if (ilits->cascade_info_block.nNum != 0) + ret = ili_ice_mode_ctrl_by_mode(DISABLE, OFF, BOTH); + else + ret = ili_ice_mode_ctrl(DISABLE, mcu); + + if (ret < 0) + ILI_ERR("Enable ice mode failed\n"); + } + +} + +static int ilitek_fw_check_ddi_chunk(u8 *pfw) +{ + int ret = 0; + u8 cmd[7] = {0x0}; + u32 start = 0x0, end = 0x0, len = 0x0; + bool hex_info = ilits->info_from_hex; + + if (!fbi[DDI].start || !fbi[DDI].end) + return UPDATE_PASS; + + /* To make sure if flash has data. */ + if (hex_info) + ilits->info_from_hex = DISABLE; + + if (ili_ic_get_fw_ver() < 0) { + ILI_INFO("Failed to get fw ver, reading flash by dma instead\n"); + goto dma; + } + + /* Command for getting ddi info from firmware. */ + cmd[0] = CMD_GET_FLASH_DATA; /* cmd */ + cmd[1] = DDI_RSV_BK_SIZE & 0xFF; /* len L byte */ + cmd[2] = (DDI_RSV_BK_SIZE >> 8) & 0xFF; /* len H byte */ + + /* Read customer reserved block */ + start = DDI_RSV_BK_ST_ADDR; + cmd[3] = (start & 0xFF); /* addr L byte */ + cmd[4] = (start >> 8) & 0xFF ; /* addr M byte */ + cmd[5] = (start >> 16) & 0xFF; /* addr H byte */ + cmd[6] = ili_calc_packet_checksum(cmd, sizeof(cmd) - 1); + ILI_INFO("Read customer reserved block: start addr = 0x%X, len = 0x%X ", start, DDI_RSV_BK_SIZE); + if (ilits->wrapper(cmd, sizeof(cmd), fw_flash_tmp, sizeof(fw_flash_tmp), OFF, ON) < 0) { + ILI_ERR("Failed to read customer reserved block data from fw\n"); + goto dma; + } + ipio_memcpy(&pfw[start], fw_flash_tmp + 6, DDI_RSV_BK_SIZE, MAX_HEX_FILE_SIZE - start); + + /* Read mp data block */ + start = DDI_RSV_BK_END_ADDR - DDI_RSV_BK_SIZE + 1; + cmd[3] = (start & 0xFF); /* addr L byte */ + cmd[4] = (start >> 8) & 0xFF ; /* addr M byte */ + cmd[5] = (start >> 16) & 0xFF; /* addr H byte */ + cmd[6] = ili_calc_packet_checksum(cmd, sizeof(cmd) - 1); + ILI_INFO("Read mp data block: start addr = 0x%X, len = 0x%X ", start, DDI_RSV_BK_SIZE); + if (ilits->wrapper(cmd, sizeof(cmd), fw_flash_tmp, sizeof(fw_flash_tmp), OFF, ON) < 0) { + ILI_ERR("Failed to read mp data block from fw\n"); + goto dma; + } + ipio_memcpy(&pfw[start], fw_flash_tmp + 6, DDI_RSV_BK_SIZE, MAX_HEX_FILE_SIZE - start); + + goto out; + +dma: + ILI_INFO("Obtain block data from dma\n"); + + ili_ice_mode_ctrl(ENABLE, OFF); + + /* Read customer reserved block */ + start = DDI_RSV_BK_ST_ADDR; + end = DDI_RSV_BK_ST_ADDR + DDI_RSV_BK_SIZE - 1; + len = DDI_RSV_BK_SIZE; + if (ilitek_tddi_fw_read_flash_data(start, end, fw_flash_tmp, len) < 0) { + ILI_ERR("Failed to read customer reserved block data from dma\\n"); + ret = UPDATE_FAIL; + goto out; + } + ipio_memcpy(&pfw[start], fw_flash_tmp + 6, DDI_RSV_BK_SIZE, MAX_HEX_FILE_SIZE - start); + + /* Read mp data block */ + start = DDI_RSV_BK_END_ADDR - DDI_RSV_BK_SIZE + 1; + end = DDI_RSV_BK_END_ADDR; + len = DDI_RSV_BK_SIZE; + if (ilitek_tddi_fw_read_flash_data(start, end, fw_flash_tmp, len) < 0) { + ILI_ERR("Failed to read mp data block from dma\n"); + ret = UPDATE_FAIL; + goto out; + } + ipio_memcpy(&pfw[start], fw_flash_tmp + 6, DDI_RSV_BK_SIZE, MAX_HEX_FILE_SIZE - start); + + ili_ice_mode_ctrl(DISABLE, OFF); +out: + /* + * Since there're two blocks combined with DDI chunk togeter, + * we need to extend the addresses where are going to upgrade. + */ + fbi[DDI].start = DDI_RSV_BK_ST_ADDR; + fbi[DDI].end = DDI_RSV_BK_END_ADDR; + tfd.end_addr = DDI_RSV_BK_END_ADDR; + + if (hex_info) + ilits->info_from_hex = ENABLE; + + atomic_set(&ilits->cmd_int_check, DISABLE); + return ret; +} + +static int ilitek_tddi_flash_fw_crc_check(u8 *pfw, bool check_fw_ver) +{ + int i, len = 0, crc_byte_len = 4; + u8 flash_crc[4] = {0}; + u32 start_addr = 0, end_addr = 0; + u32 hw_crc = 0; + u32 flash_crc_cb = 0, hex_crc = 0; + u8 check_hex_hw_crc = ENABLE; + + /* Check Flash and HW CRC */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + start_addr = fbi[i].start; + end_addr = fbi[i].end; + + /* Invaild end address */ + if (end_addr == 0) + continue; + + if (ilitek_tddi_fw_read_flash_data(end_addr - crc_byte_len + 1, end_addr, + flash_crc, sizeof(flash_crc)) < 0) { + ILI_ERR("Read Flash failed\n"); + return UPDATE_FAIL; + } + + flash_crc_cb = flash_crc[0] << 24 | flash_crc[1] << 16 | flash_crc[2] << 8 | flash_crc[3]; + + if (ili_fw_read_hw_crc(start_addr, end_addr - start_addr - crc_byte_len + 1, &hw_crc) < 0) { + ILI_ERR("Read HW CRC failed\n"); + return UPDATE_FAIL; + } + + ILI_INFO("Block = %d, HW CRC = 0x%06x, Flash CRC = 0x%06x\n", i, hw_crc, flash_crc_cb); + + /* Compare Flash CRC with HW CRC */ + if (flash_crc_cb != hw_crc) { + ILI_INFO("HW and Flash CRC not matched\n"); + return UPDATE_FAIL; + } + memset(flash_crc, 0, sizeof(flash_crc)); + } + + if (check_fw_ver) { +#if BOOT_FW_UPDATE_MODE + if (ilits->force_fw_update) { + if (tfd.new_fw_cb != ilits->chip->fw_ver) { + ILI_INFO("FW version not matched\n"); + return UPDATE_FAIL; + } + } else { +#if (BOOT_FW_UPDATE_MODE == BOOT_FW_VER_UPGRADE) + if (tfd.new_fw_cb > ilits->chip->fw_ver || tfd.new_mp_fw_cb > ilits->chip->fw_mp_ver + || ilits->chip->fw_ver == 0x0 || ilits->chip->fw_mp_ver == 0x0) { +#elif (BOOT_FW_UPDATE_MODE == BOOT_FW_VER_DOWNGRADE) + if (tfd.new_fw_cb < ilits->chip->fw_ver || tfd.new_mp_fw_cb < ilits->chip->fw_mp_ver + || ilits->chip->fw_ver == 0x0 || ilits->chip->fw_mp_ver == 0x0) { +#endif + ILI_INFO("after check FW version, do FW Upgrade.\n"); + return UPDATE_FAIL; + } + check_hex_hw_crc = DISABLE; + } +#else + if (tfd.new_fw_cb != ilits->chip->fw_ver) { + ILI_INFO("FW version not matched\n"); + return UPDATE_FAIL; + } +#endif + } + + if (check_hex_hw_crc == ENABLE) { + /* Check Hex and HW CRC */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].end == 0) + continue; + + start_addr = fbi[i].start; + end_addr = fbi[i].end; + + len = fbi[i].end - fbi[i].start + 1 - 4; + hex_crc = CalculateCRC32(fbi[i].start, len, pfw); + + if (ili_fw_read_hw_crc(start_addr, end_addr - start_addr - crc_byte_len + 1, &hw_crc) < 0) { + ILI_ERR("Read HW CRC failed\n"); + return UPDATE_FAIL; + } + + ILI_INFO("Block = %d, HW CRC = 0x%06x, Hex CRC = 0x%06x\n", i, hw_crc, hex_crc); + if (hex_crc != hw_crc) { + ILI_ERR("Hex and HW CRC not matched\n"); + return UPDATE_FAIL; + } + } + } + + ILI_INFO("Flash FW is the same as targe file FW\n"); + return UPDATE_PASS; +} + +static int ilitek_tddi_fw_flash_program(u8 *pfw) +{ + u8 *buf; + u32 i = 0, j = 0, addr = 0, k = 0, recv_addr = 0; + int page = ilits->program_page; + bool skip = true; + + buf = kmalloc(512 * sizeof(u8), GFP_DMA); + if (ERR_ALLOC_MEM(buf)) { + ILI_ERR("Failed to allocate buf memory, %ld\n", PTR_ERR(buf)); + ipio_kfree((void **)&buf); + return -ENOMEM; + } + + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].end == 0) + continue; + + ILI_INFO("Block[%d]: Programing from (0x%x) to (0x%x), tfd.end_addr = 0x%x\n", + i, fbi[i].start, fbi[i].end, tfd.end_addr); + + for (addr = fbi[i].start; addr < fbi[i].end; addr += page) { + buf[0] = 0x25; + buf[3] = 0x04; + buf[2] = 0x10; + buf[1] = 0x08; + + for (k = 0; k < page; k++) { + if (addr + k <= tfd.end_addr) + buf[4 + k] = pfw[addr + k]; + else + buf[4 + k] = 0xFF; + + if (buf[4 + k] != 0xFF) + skip = false; + } + + if (skip) { + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + ipio_kfree((void **)&buf); + return UPDATE_FAIL; + } + + ilitek_tddi_flash_write_enable(); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, 0x2, 1) < 0) + ILI_ERR("Write 0x2 failed\n"); + + recv_addr = ((addr & 0xFF0000) >> 16) | (addr & 0x00FF00) | ((addr & 0x0000FF) << 16); + if (ili_ice_mode_write(FLASH2_ADDR, recv_addr, 3) < 0) + ILI_ERR("Write address failed\n"); + + if (ilits->wrapper(buf, page + 4, NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Failed to program data at start_addr = 0x%X, k = 0x%X, addr = 0x%x\n", + addr, k, addr + k); + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + ipio_kfree((void **)&buf); + return UPDATE_FAIL; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + + if (ilits->flash_mid == 0xEF) { + mdelay(1); + } else { + if (ilitek_tddi_flash_poll_busy(TIMEOUT_PROGRAM) < 0) { + ipio_kfree((void **)&buf); + return UPDATE_FAIL; + } + } + if (ilits->fw_update_stat != ((j * 100) / tfd.end_addr)) { + ilits->fw_update_stat = (j * 100) / tfd.end_addr; + ILI_DBG("Program flahse data .... %d%c", ilits->fw_update_stat, '%'); + } + } + } + ipio_kfree((void **)&buf); + return UPDATE_PASS; +} + +static int ilitek_tddi_fw_flash_erase(bool mcu) +{ + int ret = 0; + u32 i = 0, addr = 0, recv_addr = 0; + bool bk_erase = false; + bool ice = atomic_read(&ilits->ice_stat); + + if (!ice) { + if (ili_ice_mode_ctrl(ENABLE, mcu) < 0) + ILI_ERR("Enable ice mode failed while erasing flash\n"); + } + + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].end == 0) + continue; + + ILI_INFO("Block[%d]: Erasing from (0x%x) to (0x%x) \n", i, fbi[i].start, fbi[i].end); + + for (addr = fbi[i].start; addr <= fbi[i].end; addr += ilits->flash_sector) { + ilitek_tddi_flash_write_enable(); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (addr == fbi[AP].start) { + /* block erase with 64k bytes. */ + if (ili_ice_mode_write(FLASH2_ADDR, 0xD8, 1) < 0) + ILI_ERR("Write 0xB at %x failed\n", FLASH2_ADDR); + bk_erase = true; + } else { + /* sector erase with 4k bytes. */ + if (ili_ice_mode_write(FLASH2_ADDR, 0x20, 1) < 0) + ILI_ERR("Write 0x20 at %x failed\n", FLASH2_ADDR); + } + + recv_addr = ((addr & 0xFF0000) >> 16) | (addr & 0x00FF00) | ((addr & 0x0000FF) << 16); + if (ili_ice_mode_write(FLASH2_ADDR, recv_addr, 3) < 0) + ILI_ERR("Write address failed\n"); + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + + /* Waitint for flash setting ready */ + mdelay(1); + + if (addr == fbi[AP].start) + ret = ilitek_tddi_flash_poll_busy(TIMEOUT_PAGE); + else + ret = ilitek_tddi_flash_poll_busy(TIMEOUT_SECTOR); + + if (ret < 0) + return UPDATE_FAIL; + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); + + if (i == AP && tfd.is80k && bk_erase) { + addr = 0x10000 - 0x1000; + bk_erase = false; + } + } + } + if (!ice) { + if (ili_ice_mode_ctrl(DISABLE, mcu) < 0) + ILI_ERR("Disable ice mode failed after erase flash\n"); + } + return UPDATE_PASS; +} + +static int ilitek_fw_flash_upgrade(u8 *pfw, bool mcu) +{ + int ret = UPDATE_PASS; + bool hex_info = ilits->info_from_hex; + + if (ilits->cascade_info_block.nNum != 0) { + if (ili_cascade_reset_ctrl(ilits->reset, true) < 0) { + ILI_ERR("TP reset failed during flash progam\n"); + return -EFW_REST; + } + } else { + if (ili_reset_ctrl(ilits->reset) < 0) { + ILI_ERR("TP reset failed during flash progam\n"); + return -EFW_REST; + } + } + + ilitek_fw_check_ddi_chunk(pfw); + if (hex_info) + ilits->info_from_hex = DISABLE; + + if (ili_ic_get_fw_ver() < 0) { + ILI_INFO("Failed to get fw ver\n"); + } + + if (hex_info) + ilits->info_from_hex = ENABLE; + + /* Check FW version */ + ILI_INFO("New FW ver = 0x%x, Current FW ver = 0x%x, New MP FW ver = 0x%x, Current MP FW ver = 0x%x\n", + tfd.new_fw_cb, ilits->chip->fw_ver, tfd.new_mp_fw_cb, ilits->chip->fw_mp_ver); + if (ilits->cascade_info_block.nNum != 0) { + ret = ili_ice_mode_ctrl_by_mode(ENABLE, mcu, BOTH); + } else { + ret = ili_ice_mode_ctrl(ENABLE, mcu); + } + + if (ret < 0) + return -EFW_ICE_MODE; + + ret = ilitek_tddi_flash_fw_crc_check(pfw, true); + if (ret == UPDATE_PASS) { + if (ilits->cascade_info_block.nNum != 0) { + if (ili_ice_mode_ctrl_by_mode(DISABLE, OFF, BOTH) < 0) { + if (ili_cascade_reset_ctrl(ilits->reset, false) < 0) { + ILI_ERR("TP reset failed during flash progam\n"); + return -EFW_REST; + } + } + } else { + if (ili_ice_mode_ctrl(DISABLE, mcu) < 0) { + ILI_ERR("Disable ice mode failed, call reset instead\n"); + if (ili_reset_ctrl(ilits->reset) < 0) { + ILI_ERR("TP reset failed during flash progam\n"); + return -EFW_REST; + } + } + } + return UPDATE_PASS; + } else { + ILI_INFO("Flash FW and target file FW are different, do upgrade\n"); + } + + ilitek_tddi_flash_protect(DISABLE, OFF); + + ret = ilitek_tddi_fw_flash_erase(mcu); + if (ret == UPDATE_FAIL) + return -EFW_ERASE; + + ret = ilitek_tddi_fw_flash_program(pfw); + if (ret == UPDATE_FAIL) + return -EFW_PROGRAM; + + ilitek_tddi_flash_protect(ENABLE, OFF); + + ret = ilitek_tddi_flash_fw_crc_check(pfw, false); + if (ret == UPDATE_FAIL) + return -EFW_CRC; + + /* We do have to reset chip in order to move new code from flash to iram. */ + if (ilits->cascade_info_block.nNum != 0) { + if (ili_cascade_reset_ctrl(ilits->reset, false) < 0) { + ILI_ERR("TP reset failed after flash progam\n"); + ret = -EFW_REST; + } + } else { + if (ili_reset_ctrl(ilits->reset) < 0) { + ILI_ERR("TP reset failed after flash progam\n"); + ret = -EFW_REST; + } + } + return ret; +} + +static int ilitek_fw_calc_file_crc(u8 *pfw) +{ + int i, block_num = 0; + u32 ex_addr, data_crc, file_crc; + + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].len >= MAX_HEX_FILE_SIZE) { + ILI_ERR("Content of fw file is invalid. (fbi[%d].len=0x%x)\n", + i, fbi[i].len); + return -1; + } + + if (fbi[i].end <= 4) + continue; + block_num++; + ex_addr = fbi[i].end; + data_crc = CalculateCRC32(fbi[i].start, fbi[i].len - 4, pfw); + file_crc = pfw[ex_addr - 3] << 24 | pfw[ex_addr - 2] << 16 | pfw[ex_addr - 1] << 8 | pfw[ex_addr]; + ILI_DBG("data crc = %x, file crc = %x\n", data_crc, file_crc); + if (data_crc != file_crc) { + ILI_ERR("Content of fw file is broken. (%d, %x, %x)\n", + i, data_crc, file_crc); + return -1; + } + } + + if (fbi[MP].end <= 1 * K || fbi[AP].end <= 1 * K || (block_num == 0)) { + ILI_ERR("Content of fw file is broken. fbi[AP].end = 0x%x, fbi[MP].end = 0x%x, block_num = %d\n", + fbi[AP].end, fbi[MP].end, block_num); + return -1; + } + + ILI_INFO("Content of fw file is correct\n"); + return 0; +} + +static void ilitek_tddi_fw_update_block_info(u8 *pfw) +{ + u32 fw_info_addr = 0, fw_mp_ver_addr = 0; + + fbi[AP].name = "AP"; + fbi[DATA].name = "DATA"; + fbi[TUNING].name = "TUNING"; + fbi[MP].name = "MP"; + fbi[GESTURE].name = "GESTURE"; + fbi[PEN].name = "PEN"; + + /* upgrade mode define */ + fbi[DATA].mode = fbi[AP].mode = fbi[TUNING].mode = AP; + fbi[PEN].mode = AP; + fbi[MP].mode = MP; + fbi[GESTURE].mode = GESTURE; + + if (fbi[AP].end > (64*K)) + tfd.is80k = true; + + /* Copy fw info */ + fw_info_addr = fbi[AP].end - INFO_HEX_ST_ADDR; + ILI_INFO("Parsing hex info start addr = 0x%x\n", fw_info_addr); + ipio_memcpy(ilits->fw_info, (pfw + fw_info_addr), sizeof(ilits->fw_info), sizeof(ilits->fw_info)); + + /* copy fw mp ver */ + fw_mp_ver_addr = fbi[MP].end - INFO_MP_HEX_ADDR; + ILI_INFO("Parsing hex mp ver addr = 0x%x\n", fw_mp_ver_addr); + ipio_memcpy(ilits->fw_mp_ver, pfw + fw_mp_ver_addr, sizeof(ilits->fw_mp_ver), sizeof(ilits->fw_mp_ver)); + tfd.new_mp_fw_cb = (ilits->fw_mp_ver[0] << 24) | (ilits->fw_mp_ver[1]<< 16) | + (ilits->fw_mp_ver[2] << 8) | ilits->fw_mp_ver[3]; + + /* copy fw core ver */ + ilits->chip->core_ver = (ilits->fw_info[68] << 24) | (ilits->fw_info[69] << 16) | + (ilits->fw_info[70] << 8) | ilits->fw_info[71]; + ILI_INFO("New FW Core version = %x\n", ilits->chip->core_ver); + + /* Get hex fw vers */ + tfd.new_fw_cb = (ilits->fw_info[48] << 24) | (ilits->fw_info[49] << 16) | + (ilits->fw_info[50] << 8) | ilits->fw_info[51]; + + /* Get hex report info block*/ + ipio_memcpy(&ilits->rib, ilits->fw_info, sizeof(ilits->rib), sizeof(ilits->rib)); + /*1 byte, Resolution 0-2 bits, CustomType 3-5 bits, PenType 6-7 bits*/ + ilits->rib.nReportResolutionMode = (ilits->chip->core_ver >= CORE_VER_1470) ? (ilits->rib.nReportResolutionMode & 0x07) : POSITION_LOW_RESOLUTION; + ilits->PenType = (ilits->chip->core_ver >= CORE_VER_1700) ? (ilits->rib.nCustomerType >> 3) : POSITION_PEN_TYPE_OFF; + + if (ilits->chip->core_ver >= CORE_VER_1700) { + /*CustomerType 3 bits*/ + ilits->rib.nCustomerType = ilits->rib.nCustomerType & 0x07; + ilits->customertype_off = POSITION_CUSTOMER_TYPE_OFF_3BITS; + } else { + /*CustomerType 5 bits*/ + ilits->rib.nCustomerType = (ilits->chip->core_ver >= CORE_VER_1470) ? ilits->rib.nCustomerType : POSITION_CUSTOMER_TYPE_OFF; + ilits->customertype_off = POSITION_CUSTOMER_TYPE_OFF; + } + + ILI_INFO("report_info_block : nReportByPixel = %d, nIsHostDownload = %d, nIsSPIICE = %d, nIsSPISLAVE = %d, nIsI2C = %d\n", + ilits->rib.nReportByPixel, ilits->rib.nIsHostDownload, ilits->rib.nIsSPIICE, ilits->rib.nIsSPISLAVE, ilits->rib.nIsI2C); + + ILI_INFO("report_info_block : nReserved00 = %d, nReportResolutionMode = %d, nCustomerType = %d, nReserved02 = %x, nReserved03 = %x\n", + ilits->rib.nReserved00, ilits->rib.nReportResolutionMode, ilits->rib.nCustomerType, ilits->rib.nReserved02, ilits->rib.nReserved03); + + ILI_INFO("PenType = 0x%x, Customer Type OFF = 0x%x\n", ilits->PenType, ilits->customertype_off); + +#if ENABLE_PEN_MODE + /* Get hex Pen info block*/ + fw_info_addr = fbi[AP].end - INFO_PEN_ST_ADDR; + ILI_INFO("Parsing pen info block start addr = 0x%x\n", fw_info_addr); + ipio_memcpy(&ilits->pen_info_block, (pfw + fw_info_addr), sizeof(ilits->pen_info_block), sizeof(ilits->pen_info_block)); + + ILI_INFO("pen_info_block : nPxRaw = %d, nPyRaw = %d, nPxVa = %d,nPyVa = %d, nReserved0 = %d, nReserved01 = %d, nReserved2 = %d, nReserved03 = %d\n", + ilits->pen_info_block.nPxRaw, ilits->pen_info_block.nPyRaw, ilits->pen_info_block.nPxVa, ilits->pen_info_block.nPyVa, ilits->pen_info_block.nReserved00, ilits->pen_info_block.nReserved01, ilits->pen_info_block.nReserved02, ilits->pen_info_block.nReserved03); +#endif + +#if ENABLE_CASCADE + /* Get hex Cascade info block*/ + fw_info_addr = fbi[AP].end - INFO_CASCADE_ST_ADDR; + ILI_INFO("Parsing cascade info block start addr = 0x%x\n", fw_info_addr); + ipio_memcpy(&ilits->cascade_info_block, (pfw + fw_info_addr), sizeof(ilits->cascade_info_block), sizeof(ilits->cascade_info_block)); + + ILI_INFO("cascade_info_block : nDisable = %d, nNum = %d, nReserved0 = %d, nReserved1 = %d, nReserved2 = %d, nReserved03 = %d\n", + ilits->cascade_info_block.nDisable, ilits->cascade_info_block.nNum, ilits->cascade_info_block.nReserved00, ilits->cascade_info_block.nReserved01, ilits->cascade_info_block.nReserved02, ilits->cascade_info_block.nReserved03); +#else + ilits->cascade_info_block.nDisable = ENABLE; + ilits->cascade_info_block.nNum = 0; + ILI_INFO("cascade_info_block : nDisable = %d, nNum = %d\n", ilits->cascade_info_block.nDisable, ilits->cascade_info_block.nNum); +#endif + + /* Calculate update address */ + ILI_INFO("New FW ver = 0x%x, New MP FW version = 0x%x,\n", tfd.new_fw_cb, tfd.new_mp_fw_cb); + ILI_INFO("star_addr = 0x%06X, end_addr = 0x%06X, Block Num = %d\n", tfd.start_addr, tfd.end_addr, tfd.block_number); +} + +static int ilitek_tddi_fw_ili_convert(u8 *pfw) +{ + int i, size, blk_num = 0, blk_map = 0, num = 0; + int b0_addr = 0, b0_num = 0; + + if (ERR_ALLOC_MEM(ilits->md_fw_ili)) + return -ENOMEM; + + CTPM_FW = ilits->md_fw_ili; + size = ilits->md_fw_ili_size; + + if (size < ILI_FILE_HEADER || size > (MAX_HEX_FILE_SIZE + ILI_FILE_HEADER)) { + ILI_ERR("size of ILI file is invalid\n"); + return -EINVAL; + } + + /* Check if it's old version of ILI format. */ + if (CTPM_FW[22] == 0xFF && CTPM_FW[23] == 0xFF && + CTPM_FW[24] == 0xFF && CTPM_FW[25] == 0xFF) { + ILI_ERR("Invaild ILI format, abort!\n"); + return -EINVAL; + } + + blk_num = CTPM_FW[131]; + blk_map = (CTPM_FW[129] << 8) | CTPM_FW[130]; + ILI_INFO("Parsing ILI file, block num = %d, block mapping = %x\n", blk_num, blk_map); + + if (blk_num > (FW_BLOCK_INFO_NUM - 1) || !blk_num || !blk_map) { + ILI_ERR("Number of block or block mapping is invalid, abort!\n"); + return -EINVAL; + } + + memset(fbi, 0x0, sizeof(fbi)); + tfd.start_addr = 0; + tfd.end_addr = 0; + tfd.hex_tag = BLOCK_TAG_AF; + + /* Parsing block info */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + /* B0 tag */ + b0_addr = (CTPM_FW[4 + i * 4] << 16) | (CTPM_FW[5 + i * 4] << 8) | (CTPM_FW[6 + i * 4]); + b0_num = CTPM_FW[7 + i * 4]; + if ((b0_num != 0) && (b0_addr != 0x000000)) + fbi[b0_num].fix_mem_start = b0_addr; + + /* AF tag */ + num = i + 1; + if (num >= FW_BLOCK_INFO_NUM) + break; + if (((blk_map >> i) & 0x01) == 0x01) { + fbi[num].start = (CTPM_FW[132 + i * 6] << 16) | (CTPM_FW[133 + i * 6] << 8) | CTPM_FW[134 + i * 6]; + fbi[num].end = (CTPM_FW[135 + i * 6] << 16) | (CTPM_FW[136 + i * 6] << 8) | CTPM_FW[137 + i * 6]; + + if (fbi[num].fix_mem_start == 0) + fbi[num].fix_mem_start = INT_MAX; + + fbi[num].len = fbi[num].end - fbi[num].start + 1; + ILI_INFO("Block[%d]: start_addr = %x, end = %x, fix_mem_start = 0x%x\n", num, fbi[num].start, + fbi[num].end, fbi[num].fix_mem_start); + } + } + + memcpy(pfw, CTPM_FW + ILI_FILE_HEADER, size - ILI_FILE_HEADER); + + if (ilitek_fw_calc_file_crc(pfw) < 0) + return -1; + + tfd.block_number = blk_num; + tfd.end_addr = size - ILI_FILE_HEADER; + return 0; +} + +static int ilitek_tddi_fw_hex_convert(u8 *phex, int size, u8 *pfw) +{ + int block = 0; + u32 i = 0, j = 0, k = 0, num = 0; + u32 len = 0, addr = 0, type = 0; + u32 start_addr = 0x0, end_addr = 0x0, ex_addr = 0; + u32 offset; + + memset(fbi, 0x0, sizeof(fbi)); + + /* Parsing HEX file */ + for (; i < size;) { + len = HexToDec(&phex[i + 1], 2); + addr = HexToDec(&phex[i + 3], 4); + type = HexToDec(&phex[i + 7], 2); + + if (type == 0x04) { + ex_addr = HexToDec(&phex[i + 9], 4); + } else if (type == 0x02) { + ex_addr = HexToDec(&phex[i + 9], 4); + ex_addr = ex_addr >> 12; + } else if (type == BLOCK_TAG_AF) { + /* insert block info extracted from hex */ + tfd.hex_tag = type; + if (tfd.hex_tag == BLOCK_TAG_AF) + num = HexToDec(&phex[i + 9 + 6 + 6], 2); + else + num = 0xFF; + + if (num > (FW_BLOCK_INFO_NUM - 1)) { + ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n", + num, FW_BLOCK_INFO_NUM - 1); + return -EINVAL; + } + + fbi[num].start = HexToDec(&phex[i + 9], 6); + fbi[num].end = HexToDec(&phex[i + 9 + 6], 6); + fbi[num].fix_mem_start = INT_MAX; + fbi[num].len = fbi[num].end - fbi[num].start + 1; + ILI_INFO("Block[%d]: start_addr = %x, end = %x", num, fbi[num].start, fbi[num].end); + + block++; + } else if (type == BLOCK_TAG_B0 && tfd.hex_tag == BLOCK_TAG_AF) { + num = HexToDec(&phex[i + 9 + 6], 2); + + if (num > (FW_BLOCK_INFO_NUM - 1)) { + ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n", + num, FW_BLOCK_INFO_NUM - 1); + return -EINVAL; + } + + fbi[num].fix_mem_start = HexToDec(&phex[i + 9], 6); + ILI_INFO("Tag 0xB0: change Block[%d] to addr = 0x%x\n", num, fbi[num].fix_mem_start); + } + + addr = addr + (ex_addr << 16); + + if (phex[i + 1 + 2 + 4 + 2 + (len * 2) + 2] == 0x0D) + offset = 2; + else + offset = 1; + + if (addr >= MAX_HEX_FILE_SIZE) { + ILI_ERR("Invalid hex format %d\n", addr); + return -1; + } + + if (type == 0x00) { + end_addr = addr + len; + if (addr < start_addr) + start_addr = addr; + /* fill data */ + for (j = 0, k = 0; j < (len * 2); j += 2, k++) + pfw[addr + k] = HexToDec(&phex[i + 9 + j], 2); + } + i += 1 + 2 + 4 + 2 + (len * 2) + 2 + offset; + } + + if (ilitek_fw_calc_file_crc(pfw) < 0) + return -1; + + tfd.start_addr = start_addr; + tfd.end_addr = end_addr; + tfd.block_number = block; + return 0; +} + +static int ilitek_tdd_fw_hex_open(u8 op, u8 *pfw) +{ + int ret = 0, fsize = 0; + const struct firmware *fw = NULL; +#if (!GENERIC_KERNEL_IMAGE) + struct file *f = NULL; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + mm_segment_t old_fs; +#endif + loff_t pos = 0; +#endif + ILI_INFO("Open file method = %s, path = %s\n", + op ? "FILP_OPEN" : "REQUEST_FIRMWARE", + op ? ilits->md_fw_filp_path : ilits->md_fw_rq_path); + + switch (op) { + case REQUEST_FIRMWARE: + if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) { + ILI_ERR("Request firmware failed, try again\n"); + if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) { + ILI_ERR("Request firmware failed after retry\n"); + ret = -1; + goto out; + } + } + + fsize = fw->size; + ILI_INFO("fsize = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is zero\n"); + release_firmware(fw); + ret = -1; + goto out; + } + + ilits->tp_fw.size = 0; + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n"); + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw after retry\n"); + release_firmware(fw); + ret = -ENOMEM; + goto out; + } + } + + /* Copy fw data got from request_firmware to global */ + ipio_memcpy((u8 *)ilits->tp_fw.data, fw->data, fsize * sizeof(*fw->data), fsize); + ilits->tp_fw.size = fsize; + release_firmware(fw); + break; + case FILP_OPEN: +#if GENERIC_KERNEL_IMAGE + ILI_ERR("GKI version not allow drivers to use filp_open\n"); + return -1; +#else + f = filp_open(ilits->md_fw_filp_path, O_RDONLY, 0644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file at %ld\n", PTR_ERR(f)); + ret = -1; + goto out; + } + + fsize = f->f_inode->i_size; + ILI_INFO("fsize = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is invaild\n"); + filp_close(f, NULL); + ret = -1; + goto out; + } + + ipio_vfree((void **)&(ilits->tp_fw.data)); + ilits->tp_fw.size = 0; + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n"); + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw after retry\n"); + filp_close(f, NULL); + ret = -ENOMEM; + goto out; + } + } + + /* ready to map user's memory to obtain data by reading files */ + pos = 0; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + old_fs = get_fs(); + set_fs(KERNEL_DS); + vfs_read(f, (u8 *)ilits->tp_fw.data, fsize, &pos); + set_fs(old_fs); +#else + kernel_read(f, (u8 *)ilits->tp_fw.data, fsize, &pos); +#endif + filp_close(f, NULL); + ilits->tp_fw.size = fsize; +#endif + break; + default: + ILI_ERR("Unknown open file method, %d\n", op); + break; + } + + if (ERR_ALLOC_MEM(ilits->tp_fw.data) || ilits->tp_fw.size <= 0) { + ILI_ERR("fw data/size is invaild\n"); + ret = -1; + goto out; + } + + /* Convert hex and copy data from tp_fw.data to pfw */ + if (ilitek_tddi_fw_hex_convert((u8 *)ilits->tp_fw.data, ilits->tp_fw.size, pfw) < 0) { + ILI_ERR("Convert hex file failed\n"); + ret = -1; + } + +out: + ipio_vfree((void **)&(ilits->tp_fw.data)); + return ret; +} + +int ili_fw_upgrade(int op) +{ + int i, ret = 0, retry = 3; + + if (!ilits->boot || ilits->force_fw_update || ERR_ALLOC_MEM(pfw)) { + if (ERR_ALLOC_MEM(pfw)) { + ipio_vfree((void **)&pfw); + pfw = vmalloc(MAX_HEX_FILE_SIZE * sizeof(u8)); + if (ERR_ALLOC_MEM(pfw)) { + ILI_ERR("Failed to allocate pfw memory, %ld\n", PTR_ERR(pfw)); + ipio_vfree((void **)&pfw); + ret = -ENOMEM; + goto out; + } + } + + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + pfw[i] = 0xFF; + + if (ilitek_tdd_fw_hex_open(op, pfw) < 0) { + ILI_ERR("Open hex file fail, try upgrade from ILI file\n"); + + /* + * Users might not be aware of a broken hex file when recovering + * fw from ILI file. We should force them to check + * hex files they attempt to update via device node. + */ + if (ilits->node_update) { + ILI_ERR("Ignore update from ILI file\n"); + ipio_vfree((void **)&pfw); + return -EFW_CONVERT_FILE; + } + + if (ilitek_tddi_fw_ili_convert(pfw) < 0) { + ILI_ERR("Convert ILI file error\n"); + ret = -EFW_CONVERT_FILE; + goto out; + } + } + ilitek_tddi_fw_update_block_info(pfw); + + if (ilits->chip->core_ver >= CORE_VER_1470 && ilits->rib.nIsHostDownload == 1) { + ILI_ERR("hex file interface no match error\n"); + ret = -EFW_INTERFACE; + goto out; + } + } + +#if (ENGINEER_FLOW) + if (!ilits->eng_flow) { + do { + ret = ilitek_fw_flash_upgrade(pfw, OFF); + if (ret == UPDATE_PASS) + break; + + ILI_ERR("Upgrade failed, do retry!\n"); + } while (--retry > 0); + + if (ret != UPDATE_PASS) { + ILI_ERR("Failed to upgrade fw %d times, erasing flash\n", retry); + if (ilitek_tddi_fw_flash_erase(OFF) < 0) + ILI_ERR("Failed to erase flash\n"); + + if (ilits->cascade_info_block.nNum != 0) { + if (ili_cascade_reset_ctrl(ilits->reset, false) < 0) + ILI_ERR("TP reset failed after erase flash\n"); + } else { + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP reset failed after erase flash\n"); + } + } + } else { + ILI_ERR("eng_flow do reset!\n"); + if (ilits->cascade_info_block.nNum != 0) { + ili_cascade_reset_ctrl(ilits->reset, true); + } else { + ili_reset_ctrl(ilits->reset); + } + } +#else + do { + ret = ilitek_fw_flash_upgrade(pfw, OFF); + if (ret == UPDATE_PASS) + break; + + ILI_ERR("Upgrade failed, do retry!\n"); + } while (--retry > 0); + + if (ret != UPDATE_PASS) { + ILI_ERR("Failed to upgrade fw %d times, erasing flash\n", retry); + if (ilitek_tddi_fw_flash_erase(OFF) < 0) + ILI_ERR("Failed to erase flash\n"); + + if (ilits->cascade_info_block.nNum != 0) { + if (ili_cascade_reset_ctrl(ilits->reset, false) < 0) + ILI_ERR("TP reset failed after erase flash\n"); + } else { + if (ili_reset_ctrl(ilits->reset) < 0) + ILI_ERR("TP reset failed after erase flash\n"); + } + } +#endif + +out: + if (ret < 0) + ilits->info_from_hex = DISABLE; + + if (ilits->cascade_info_block.nNum != 0) { + if (atomic_read(&ilits->ice_stat)) { + ili_ice_mode_ctrl_by_mode(DISABLE, OFF, BOTH); + } + } else { + if (atomic_read(&ilits->ice_stat)) + ili_ice_mode_ctrl(DISABLE, OFF); + } + + ili_ic_get_all_info(); + ili_ic_func_ctrl_reset(); + + if (!ilits->info_from_hex) + ilits->info_from_hex = ENABLE; + + return ret; +} + +struct flash_table { + u16 mid; + u16 dev_id; + int mem_size; + int program_page; + int sector; +} flash_t[] = { + [0] = {0x00, 0x0000, (256 * K), 256, (4 * K)}, /* Default */ + [1] = {0xEF, 0x3010, (64 * K), 256, (4 * K)}, /* W25X05CL */ + [2] = {0xEF, 0x4012, (256 * K), 256, (4 * K)}, /* W25Q20CL */ + [3] = {0xEF, 0x6011, (128 * K), 256, (4 * K)}, /* W25Q10EW */ + [4] = {0xEF, 0x6012, (256 * K), 256, (4 * K)}, /* W25Q20EW */ + [5] = {0xC8, 0x6012, (256 * K), 256, (4 * K)}, /* GD25LQ20B */ + [6] = {0xC8, 0x6013, (512 * K), 256, (4 * K)}, /* GD25LQ40 */ + [7] = {0x85, 0x6012, (2 * M), 256, (4 * K)}, /* P25Q20L */ + [8] = {0x85, 0x6013, (4 * M), 256, (4 * K)}, /* P25Q40L */ + [9] = {0xC2, 0x2010, (128 * K), 256, (4 * K)},/* MX25L512 */ + [10] = {0xC2, 0x2530, (512 * K), 256, (4 * K)},/* MX25U5121E */ + [11] = {0xC2, 0x2531, (1 * M), 256, (4 * K)},/* MX25U1001E */ + [12] = {0xC2, 0x2812, (256 * K), 256, (4 * K)},/* MX25R2035F */ + [13] = {0xC2, 0x2813, (512 * K), 256, (4 * K)},/* MX25R4035F */ + [14] = {0x1C, 0x3812, (256 * K), 256, (4 * K)},/* EN25S20A */ +}; + +void ili_fw_read_flash_info(bool mcu) +{ + int i = 0; + u8 buf[3] = {0}; + u8 cmd = 0x9F; + u32 tmp = 0; + u16 flash_id = 0, flash_mid = 0; + bool ice = atomic_read(&ilits->ice_stat); + + if (!ice) { + if (ili_ice_mode_ctrl(ENABLE, mcu) < 0) + ILI_ERR("Enable ice mode failed while reading flash info\n"); + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x0, 1) < 0) /* CS Low */ + ILI_ERR("Write cs low failed\n"); + + if (ili_ice_mode_write(FLASH1_ADDR, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(FLASH2_ADDR, cmd, 1) < 0) /* Read JEDEC ID */ + ILI_ERR("Write 0x9F failed\n"); + + for (i = 0; i < ARRAY_SIZE(buf); i++) { + if (ili_ice_mode_write(FLASH2_ADDR, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + + if (ili_ice_mode_read(FLASH4_ADDR, &tmp, sizeof(u8)) < 0) + ILI_ERR("Read flash info error\n"); + + buf[i] = tmp; + } + + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) /* CS High */ + ILI_ERR("Write cs high failed\n"); + + flash_mid = buf[0]; + flash_id = buf[1] << 8 | buf[2]; + + ilits->flash_mid = flash_mid; + ilits->flash_devid = flash_id; + + for (i = 0; i < ARRAY_SIZE(flash_t); i++) { + if (flash_mid == flash_t[i].mid && flash_id == flash_t[i].dev_id) { + ilits->program_page = flash_t[i].program_page; + ilits->flash_sector = flash_t[i].sector; + break; + } + } + + if (i >= ARRAY_SIZE(flash_t)) { + ILI_INFO("Not found flash id in tab, use default\n"); + ilits->program_page = flash_t[0].program_page; + ilits->flash_sector = flash_t[0].sector; + } + + ILI_INFO("Flash MID = %x, Flash DEV_ID = %x\n", ilits->flash_mid, ilits->flash_devid); + ILI_INFO("Flash program page = %d\n", ilits->program_page); + ILI_INFO("Flash sector = %d\n", ilits->flash_sector); + + if (!ice) { + if (ili_ice_mode_ctrl(DISABLE, mcu) < 0) + ILI_ERR("Disable ice mode failed while reading flash info\n"); + } +} \ No newline at end of file diff --git a/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_hostdl.c b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_hostdl.c new file mode 100644 index 000000000000..17dbbc379fb4 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_hostdl.c @@ -0,0 +1,1105 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek_v3.h" + +#define UPDATE_PASS 0 +#define UPDATE_FAIL -1 +#define TIMEOUT_SECTOR 500 +#define TIMEOUT_PAGE 3500 +#define TIMEOUT_PROGRAM 10 + +static struct touch_fw_data { + u8 block_number; + u32 start_addr; + u32 end_addr; + u32 new_fw_cb; + int delay_after_upgrade; + bool isCRC; + bool isboot; + bool is80k; + int hex_tag; +} tfd; + +static struct flash_block_info { + char *name; + u32 start; + u32 end; + u32 len; + u32 mem_start; + u32 fix_mem_start; + u8 mode; +} fbi[FW_BLOCK_INFO_NUM]; + +static u8 *pfw; +static u8 *CTPM_FW; + +static u32 HexToDec(char *phex, s32 len) +{ + u32 ret = 0, temp = 0, i; + s32 shift = (len - 1) * 4; + + for (i = 0; i < len; shift -= 4, i++) { + if ((phex[i] >= '0') && (phex[i] <= '9')) + temp = phex[i] - '0'; + else if ((phex[i] >= 'a') && (phex[i] <= 'f')) + temp = (phex[i] - 'a') + 10; + else if ((phex[i] >= 'A') && (phex[i] <= 'F')) + temp = (phex[i] - 'A') + 10; + + ret |= (temp << shift); + } + return ret; +} + +static int CalculateCRC32(u32 start_addr, u32 len, u8 *pfw) +{ + int i = 0, j = 0; + int crc_poly = 0x04C11DB7; + int tmp_crc = 0xFFFFFFFF; + + for (i = start_addr; i < start_addr + len; i++) { + tmp_crc ^= (pfw[i] << 24); + + for (j = 0; j < 8; j++) { + if ((tmp_crc & 0x80000000) != 0) + tmp_crc = tmp_crc << 1 ^ crc_poly; + else + tmp_crc = tmp_crc << 1; + } + } + return tmp_crc; +} + +static int calc_hw_dma_crc(u32 start_addr, u32 block_size) +{ + int count = 50; + u32 busy = 0; + + if (ilits->chip->dma_reset) { + ILI_DBG("operate dma reset in reg after tp reset\n"); + if (ili_ice_mode_write(0x40040, 0x00800000, 4) < 0) + ILI_ERR("Failed to open DMA reset\n"); + if (ili_ice_mode_write(0x40040, 0x00000000, 4) < 0) + ILI_ERR("Failed to close DMA reset\n"); + } + /* dma1 src1 address */ + if (ili_ice_mode_write(0x072104, start_addr, 4) < 0) + ILI_ERR("Write dma1 src1 address failed\n"); + /* dma1 src1 format */ + if (ili_ice_mode_write(0x072108, 0x80000001, 4) < 0) + ILI_ERR("Write dma1 src1 format failed\n"); + /* dma1 dest address */ + if (ili_ice_mode_write(0x072114, 0x0002725C, 4) < 0) + ILI_ERR("Write dma1 src1 format failed\n"); + /* dma1 dest format */ + if (ili_ice_mode_write(0x072118, 0x80000000, 4) < 0) + ILI_ERR("Write dma1 dest format failed\n"); + /* Block size*/ + if (ili_ice_mode_write(0x07211C, block_size, 4) < 0) + ILI_ERR("Write block size (%d) failed\n", block_size); + /* crc off */ + if (ili_ice_mode_write(0x041016, 0x00, 1) < 0) + ILI_INFO("Write crc of failed\n"); + /* dma crc */ + if (ili_ice_mode_write(0x041017, 0x03, 1) < 0) + ILI_ERR("Write dma 1 crc failed\n"); + /* crc on */ + if (ili_ice_mode_write(0x041016, 0x01, 1) < 0) + ILI_ERR("Write crc on failed\n"); + /* Dma1 stop */ + if (ili_ice_mode_write(0x072100, 0x02000000, 4) < 0) + ILI_ERR("Write dma1 stop failed\n"); + /* clr int */ + if (ili_ice_mode_write(0x048006, 0x2, 1) < 0) + ILI_ERR("Write clr int failed\n"); + /* Dma1 start */ + if (ili_ice_mode_write(0x072100, 0x01000000, 4) < 0) + ILI_ERR("Write dma1 start failed\n"); + + /* Polling BIT0 */ + while (count > 0) { + mdelay(1); + if (ili_ice_mode_read(0x048006, &busy, sizeof(u8)) < 0) + ILI_ERR("Read busy error\n"); + ILI_DBG("busy = %x\n", busy); + if ((busy & 0x02) == 2) + break; + count--; + } + + if (count <= 0) { + ILI_ERR("BIT0 is busy\n"); + return -1; + } + + if (ili_ice_mode_read(0x04101C, &busy, sizeof(u32)) < 0) { + ILI_ERR("Read dma crc error\n"); + return -1; + } + return busy; +} + +static int ilitek_tddi_fw_iram_read(u8 *buf, u32 start, int len) +{ + int limit = 3 * K; /* SPI transfer data length must less than 4K */ + int addr = 0, loop = 0, tmp_len = len, cnt = 0; + u8 cmd[4] = {0}; + + if (!buf) { + ILI_ERR("buf is null\n"); + return -ENOMEM; + } + + if (len % limit) + loop = (len / limit) + 1; + else + loop = len / limit; + + for (cnt = 0, addr = start; cnt < loop; cnt++, addr += limit) { + tmp_len = len - cnt * limit; + ilits->fw_update_stat = ((len - tmp_len) * 100) / len; + ILI_INFO("Reading iram data .... %d%c", ilits->fw_update_stat, '%'); + + if (tmp_len > limit) + tmp_len = limit; + + cmd[0] = 0x25; + cmd[3] = (char)((addr & 0x00FF0000) >> 16); + cmd[2] = (char)((addr & 0x0000FF00) >> 8); + cmd[1] = (char)((addr & 0x000000FF)); + + if (ilits->wrapper(cmd, 4, NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Failed to write iram data\n"); + return -ENODEV; + } + + if (ilits->wrapper(NULL, 0, buf + cnt * limit, tmp_len, OFF, OFF) < 0) { + ILI_ERR("Failed to Read iram data\n"); + return -ENODEV; + } + + } + return 0; +} + +int ili_fw_dump_iram_data(u32 start, u32 end, bool save, bool mcu) +{ +#if (!GENERIC_KERNEL_IMAGE) + struct file *f = NULL; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + mm_segment_t old_fs; +#endif + loff_t pos = 0; +#endif + int i, ret = 0; + int len, tmp = debug_en; + bool ice = atomic_read(&ilits->ice_stat); + + if (!ice) { + ret = ili_ice_mode_ctrl(ENABLE, mcu); + if (ret < 0) { + ILI_ERR("Enable ice mode failed\n"); + return ret; + } + } + + len = end - start + 1; + + if (len > MAX_HEX_FILE_SIZE) { + ILI_ERR("len is larger than buffer, abort\n"); + ret = -EINVAL; + goto out; + } + + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + ilits->update_buf[i] = 0xFF; + + ret = ilitek_tddi_fw_iram_read(ilits->update_buf, start, len); + if (ret < 0) { + ILI_ERR("Read IRAM data failed\n"); + goto out; + } + + if (save) { +#if GENERIC_KERNEL_IMAGE + ILI_ERR("GKI version not allow drivers to use filp_open, dump IRAM data\n"); + debug_en = DEBUG_ALL; + ili_dump_data(ilits->update_buf, 8, len, 0, "IRAM"); + debug_en = tmp; + ret = -ENOMEM; + goto out; +#else + f = filp_open(DUMP_IRAM_PATH, O_WRONLY | O_CREAT | O_TRUNC, 644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file at %ld.\n", PTR_ERR(f)); + ret = -ENOMEM; + goto out; + } + + pos = 0; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + old_fs = get_fs(); + set_fs(KERNEL_DS); + vfs_write(f, ilits->update_buf, len, &pos); + set_fs(old_fs); +#else + kernel_write(f, ilits->update_buf, len, &pos); +#endif + filp_close(f, NULL); + ILI_INFO("Save iram data to %s\n", DUMP_IRAM_PATH); +#endif + } else { + debug_en = DEBUG_ALL; + ili_dump_data(ilits->update_buf, 8, len, 0, "IRAM"); + debug_en = tmp; + } + +out: + if (!ice) { + if (ili_ice_mode_ctrl(DISABLE, mcu) < 0) + ILI_ERR("Enable ice mode failed after code reset\n"); + } + + ILI_INFO("Dump IRAM %s\n", (ret < 0) ? "FAIL" : "SUCCESS"); + return ret; +} + +static int ilitek_tddi_fw_iram_program(u32 start, u8 *w_buf, u32 w_len, u32 split_len) +{ + int i = 0, j = 0, addr = 0; + u32 end = start + w_len; + bool fix_4_alignment = false; + + if (split_len % 4 > 0) + ILI_ERR("Since split_len must be four-aligned, it must be a multiple of four"); + + if (split_len != 0) { + for (addr = start, i = 0; addr < end; addr += split_len, i += split_len) { + if ((addr + split_len) > end) { + split_len = end - addr; + if (split_len % 4 != 0) + fix_4_alignment = true; + } + + ilits->update_buf[0] = SPI_WRITE; + ilits->update_buf[1] = 0x25; + ilits->update_buf[2] = (char)((addr & 0x000000FF)); + ilits->update_buf[3] = (char)((addr & 0x0000FF00) >> 8); + ilits->update_buf[4] = (char)((addr & 0x00FF0000) >> 16); + + for (j = 0; j < split_len; j++) + ilits->update_buf[5 + j] = w_buf[i + j]; + + if (fix_4_alignment) { + ILI_INFO("org split_len = 0x%X\n", split_len); + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 4, ilits->update_buf[5 + split_len - 4]); + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 3, ilits->update_buf[5 + split_len - 3]); + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 2, ilits->update_buf[5 + split_len - 2]); + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 1, ilits->update_buf[5 + split_len - 1]); + for (j = 0; j < (4 - (split_len % 4)); j++) { + ilits->update_buf[5 + j + split_len] = 0xFF; + ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", j + split_len, ilits->update_buf[5 + j + split_len]); + } + + ILI_INFO("split_len %% 4 = %d\n", split_len % 4); + split_len = split_len + (4 - (split_len % 4)); + ILI_INFO("fix split_len = 0x%X\n", split_len); + } + if (ilits->spi_write_then_read(ilits->spi, ilits->update_buf, split_len + 5, NULL, 0)) { + ILI_ERR("Failed to write data via SPI in host download (%x)\n", split_len + 5); + return -EIO; + } + ilits->fw_update_stat = (i * 100) / w_len; + } + } else { + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + ilits->update_buf[i] = 0xFF; + + ilits->update_buf[0] = SPI_WRITE; + ilits->update_buf[1] = 0x25; + ilits->update_buf[2] = (char)((start & 0x000000FF)); + ilits->update_buf[3] = (char)((start & 0x0000FF00) >> 8); + ilits->update_buf[4] = (char)((start & 0x00FF0000) >> 16); + + memcpy(&ilits->update_buf[5], w_buf, w_len); + if (w_len % 4 != 0) { + ILI_INFO("org w_len = %d\n", w_len); + w_len = w_len + (4 - (w_len % 4)); + ILI_INFO("w_len = %d w_len %% 4 = %d\n", w_len, w_len % 4); + } + /* It must be supported by platforms that have the ability to transfer all data at once. */ + if (ilits->spi_write_then_read(ilits->spi, ilits->update_buf, w_len + 5, NULL, 0) < 0) { + ILI_ERR("Failed to write data via SPI in host download (%x)\n", w_len + 5); + return -EIO; + } + } + return 0; +} + +static int ilitek_tddi_fw_iram_upgrade(u8 *pfw, bool mcu) +{ + int i, ret = UPDATE_PASS; + u32 mode, crc, dma, iram_crc = 0, slave_crc; + u8 *fw_ptr = NULL, crc_temp[4], crc_len = 4; + bool iram_crc_err = false; + struct ilitek_dma_config dma1; + + if (!ilits->ddi_rest_done) { + if (ilits->actual_tp_mode != P5_X_FW_GESTURE_MODE) { + if (ilits->cascade_info_block.nNum != 0) { + ili_cascade_reset_ctrl(ilits->reset, true); + } else { + ili_reset_ctrl(ilits->reset); + } + } + + if (ilits->cascade_info_block.nNum != 0) { + ili_set_bypass_mode(mcu); + } else { + ret = ili_ice_mode_ctrl(ENABLE, mcu); + } + + if (ret < 0) + return -EFW_ICE_MODE; + } else { + /* Restore it if the wq of load_fw_ddi has been called. */ + ilits->ddi_rest_done = false; + } + + /* Point to pfw with different addresses for getting its block data. */ + fw_ptr = pfw; + if (ilits->actual_tp_mode == P5_X_FW_TEST_MODE) { + + mode = MP; + } else if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) { + mode = GESTURE; + crc_len = 0; + + if (ilits->cascade_info_block.nNum != 0) { + ili_set_bypass_mode(mcu); + } + } else { + mode = AP; + } + + /* backup dma1 parameter */ + ili_get_dma1_config(&dma1); + + /* Program data to iram acorrding to each block */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + if (fbi[i].mode == mode && fbi[i].len != 0) { + ILI_DBG("Download %s code from hex 0x%x to IRAM 0x%x, len = 0x%x\n", + fbi[i].name, fbi[i].start, fbi[i].mem_start, fbi[i].len); + + if (ilitek_tddi_fw_iram_program(fbi[i].mem_start, (fw_ptr + fbi[i].start), fbi[i].len, SPI_UPGRADE_LEN) < 0) + ILI_ERR("IRAM program failed\n"); + + crc = CalculateCRC32(fbi[i].start, fbi[i].len - crc_len, fw_ptr); + dma = calc_hw_dma_crc(fbi[i].mem_start, fbi[i].len - crc_len); + + if (mode != GESTURE) { + ilitek_tddi_fw_iram_read(crc_temp, (fbi[i].mem_start + fbi[i].len - crc_len), sizeof(crc_temp)); + iram_crc = crc_temp[0] << 24 | crc_temp[1] << 16 | crc_temp[2] << 8 | crc_temp[3]; + if (iram_crc != dma) + iram_crc_err = true; + } + + ILI_INFO("%s CRC is %s hex(%x) : dma(%x) : iram(%x), calculation len is 0x%x\n", + fbi[i].name, ((crc != dma) || (iram_crc_err)) ? "Invalid !" : "Correct !", crc, dma, iram_crc, fbi[i].len - crc_len); + + if (ilits->cascade_info_block.nNum != 0) { + ili_mspi_read_slave_register(DMA_CRC_ADDR, &slave_crc, sizeof(u32)); + if (ili_ice_mode_write_by_mode(MSPI_REG, 0x00, 1, MASTER) < 0) + ILI_ERR("Failed to write MSPI_REG in ice mode\n"); + + ILI_INFO("Slave %s CRC is %s hex(%x) : dma(%x) : iram(%x)\n", + fbi[i].name, ((slave_crc != dma) || (iram_crc_err)) ? "Invalid !" : "Correct !", slave_crc, dma, iram_crc); + + if ((crc != dma) || (slave_crc != dma) || iram_crc_err) { + ILI_ERR("CRC Error! print iram data with first 16 bytes\n"); + ili_fw_dump_iram_data(0x0, 0xF, false, OFF); + return -EFW_CRC; + } + } else { + if ((crc != dma) || iram_crc_err) { + ILI_ERR("CRC Error! print iram data with first 16 bytes\n"); + ili_fw_dump_iram_data(0x0, 0xF, false, OFF); + return -EFW_CRC; + } + } + } + } + + /* recovery dma1 parameter */ + ILI_INFO("recovery DMA 1 parameters\n"); + ili_set_dma1_config(&dma1); + + if (ilits->actual_tp_mode != P5_X_FW_GESTURE_MODE) { + if (ilits->cascade_info_block.nNum != 0) { + if (ili_cascade_reset_ctrl(TP_IC_WHOLE_RST_WITHOUT_FLASH, false) < 0) { + ILI_ERR("TP cascade Whole reset without flash failed during iram programming\n"); + ret = -EFW_REST; + return ret; + } + } else { + if (ili_reset_ctrl(TP_IC_WHOLE_RST_WITHOUT_FLASH) < 0) { + ILI_ERR("TP Whole reset without flash failed during iram programming\n"); + ret = -EFW_REST; + return ret; + } + } + } + + if (ili_ice_mode_ctrl(DISABLE, mcu) < 0) { + ILI_ERR("Disable ice mode failed after code reset\n"); + ret = -EFW_ICE_MODE; + } + + mdelay(20); + + return ret; +} + +static int ilitek_fw_calc_file_crc(u8 *pfw) +{ + int i, block_num = 0; + u32 ex_addr, data_crc, file_crc; + + for (i = 0; i < ARRAY_SIZE(fbi); i++) { + if (fbi[i].len >= MAX_HEX_FILE_SIZE) { + ILI_ERR("Content of fw file is invalid. (fbi[%d].len=0x%x)\n", + i, fbi[i].len); + return -1; + } + + if (fbi[i].end <= 4) + continue; + block_num++; + ex_addr = fbi[i].end; + data_crc = CalculateCRC32(fbi[i].start, fbi[i].len - 4, pfw); + file_crc = pfw[ex_addr - 3] << 24 | pfw[ex_addr - 2] << 16 | pfw[ex_addr - 1] << 8 | pfw[ex_addr]; + ILI_DBG("data crc = %x, file crc = %x\n", data_crc, file_crc); + if (data_crc != file_crc) { + ILI_ERR("Content of fw file is broken. (%d, %x, %x)\n", + i, data_crc, file_crc); + return -1; + } + } + + if (fbi[MP].end <= 1 * K || fbi[AP].end <= 1 * K || (block_num == 0)) { + ILI_ERR("Content of fw file is broken. fbi[AP].end = 0x%x, fbi[MP].end = 0x%x, block_num = %d\n", + fbi[AP].end, fbi[MP].end, block_num); + return -1; + } + + ILI_INFO("Content of fw file is correct\n"); + return 0; +} + +static int ilitek_tddi_fw_update_block_info(u8 *pfw) +{ + u32 ges_area_section = 0, ges_info_addr = 0, ges_fw_start = 0, ges_fw_end = 0; + u32 ap_end = 0, ap_len = 0; + u32 fw_info_addr = 0, fw_mp_ver_addr = 0; + + if (tfd.hex_tag != BLOCK_TAG_AF) { + ILI_ERR("HEX TAG is invalid (0x%X)\n", tfd.hex_tag); + return -EINVAL; + } + + fbi[AP].mem_start = (fbi[AP].fix_mem_start != INT_MAX) ? fbi[AP].fix_mem_start : 0; + fbi[DATA].mem_start = (fbi[DATA].fix_mem_start != INT_MAX) ? fbi[DATA].fix_mem_start : DLM_START_ADDRESS; + fbi[TUNING].mem_start = (fbi[TUNING].fix_mem_start != INT_MAX) ? fbi[TUNING].fix_mem_start : fbi[DATA].mem_start + fbi[DATA].len; + fbi[MP].mem_start = (fbi[MP].fix_mem_start != INT_MAX) ? fbi[MP].fix_mem_start : 0; + fbi[GESTURE].mem_start = (fbi[GESTURE].fix_mem_start != INT_MAX) ? fbi[GESTURE].fix_mem_start : 0; + fbi[TAG].mem_start = (fbi[TAG].fix_mem_start != INT_MAX) ? fbi[TAG].fix_mem_start : 0; + fbi[PARA_BACKUP].mem_start = (fbi[PARA_BACKUP].fix_mem_start != INT_MAX) ? fbi[PARA_BACKUP].fix_mem_start : 0; + fbi[DDI].mem_start = (fbi[DDI].fix_mem_start != INT_MAX) ? fbi[DDI].fix_mem_start : 0; + fbi[PEN].mem_start = (fbi[PEN].fix_mem_start != INT_MAX) ? fbi[PEN].fix_mem_start : 0; + + /* Parsing gesture info form AP code */ + ges_info_addr = (fbi[AP].end + 1 - 60); + ges_area_section = (pfw[ges_info_addr + 3] << 24) + (pfw[ges_info_addr + 2] << 16) + (pfw[ges_info_addr + 1] << 8) + pfw[ges_info_addr]; + fbi[GESTURE].mem_start = (pfw[ges_info_addr + 7] << 24) + (pfw[ges_info_addr + 6] << 16) + (pfw[ges_info_addr + 5] << 8) + pfw[ges_info_addr + 4]; + ap_end = (pfw[ges_info_addr + 11] << 24) + (pfw[ges_info_addr + 10] << 16) + (pfw[ges_info_addr + 9] << 8) + pfw[ges_info_addr + 8]; + + if (ap_end != fbi[GESTURE].mem_start) + ap_len = ap_end - fbi[GESTURE].mem_start + 1; + + ges_fw_start = (pfw[ges_info_addr + 15] << 24) + (pfw[ges_info_addr + 14] << 16) + (pfw[ges_info_addr + 13] << 8) + pfw[ges_info_addr + 12]; + ges_fw_end = (pfw[ges_info_addr + 19] << 24) + (pfw[ges_info_addr + 18] << 16) + (pfw[ges_info_addr + 17] << 8) + pfw[ges_info_addr + 16]; + + if (ges_fw_end != ges_fw_start) + fbi[GESTURE].len = ges_fw_end - ges_fw_start; + + /* update gesture address */ + fbi[GESTURE].start = ges_fw_start; + + ILI_INFO("==== Gesture loader info ====\n"); + ILI_INFO("gesture move to ap addr => start = 0x%x, ap_end = 0x%x, ap_len = 0x%x\n", fbi[GESTURE].mem_start, ap_end, ap_len); + ILI_INFO("gesture hex addr => start = 0x%x, gesture_end = 0x%x, gesture_len = 0x%x\n", ges_fw_start, ges_fw_end, fbi[GESTURE].len); + ILI_INFO("=============================\n"); + + fbi[AP].name = "AP"; + fbi[DATA].name = "DATA"; + fbi[TUNING].name = "TUNING"; + fbi[MP].name = "MP"; + fbi[GESTURE].name = "GESTURE"; + fbi[TAG].name = "TAG"; + fbi[PARA_BACKUP].name = "PARA_BACKUP"; + fbi[DDI].name = "DDI"; + fbi[PEN].name = "PEN"; + + /* upgrade mode define */ + fbi[DATA].mode = fbi[AP].mode = fbi[TUNING].mode = AP; + fbi[PEN].mode = AP; + fbi[MP].mode = MP; + fbi[GESTURE].mode = GESTURE; + + if (fbi[AP].end > (64*K)) + tfd.is80k = true; + + /* Copy fw info */ + fw_info_addr = fbi[AP].end - INFO_HEX_ST_ADDR; + ILI_INFO("Parsing hex info start addr = 0x%x\n", fw_info_addr); + ipio_memcpy(ilits->fw_info, (pfw + fw_info_addr), sizeof(ilits->fw_info), sizeof(ilits->fw_info)); + + /* copy fw mp ver */ + fw_mp_ver_addr = fbi[MP].end - INFO_MP_HEX_ADDR; + ILI_INFO("Parsing hex mp ver addr = 0x%x\n", fw_mp_ver_addr); + ipio_memcpy(ilits->fw_mp_ver, pfw + fw_mp_ver_addr, sizeof(ilits->fw_mp_ver), sizeof(ilits->fw_mp_ver)); + + /* copy fw core ver */ + ilits->chip->core_ver = (ilits->fw_info[68] << 24) | (ilits->fw_info[69] << 16) | + (ilits->fw_info[70] << 8) | ilits->fw_info[71]; + ILI_INFO("New FW Core version = %x\n", ilits->chip->core_ver); + + /* Get hex fw vers */ + tfd.new_fw_cb = (ilits->fw_info[48] << 24) | (ilits->fw_info[49] << 16) | + (ilits->fw_info[50] << 8) | ilits->fw_info[51]; + + /* Get hex report info block*/ + ipio_memcpy(&ilits->rib, ilits->fw_info, sizeof(ilits->rib), sizeof(ilits->rib)); + /*1 byte, Resolution 0-2 bits, CustomType 3-5 bits, PenType 6-7 bits*/ + ilits->rib.nReportResolutionMode = (ilits->chip->core_ver >= CORE_VER_1470) ? (ilits->rib.nReportResolutionMode & 0x07) : POSITION_LOW_RESOLUTION; + ilits->PenType = (ilits->chip->core_ver >= CORE_VER_1700) ? (ilits->rib.nCustomerType >> 3) : POSITION_PEN_TYPE_OFF; + + if (ilits->chip->core_ver >= CORE_VER_1700) { + /*CustomerType 3 bits*/ + ilits->rib.nCustomerType = ilits->rib.nCustomerType & 0x07; + ilits->customertype_off = POSITION_CUSTOMER_TYPE_OFF_3BITS; + } else { + /*CustomerType 5 bits*/ + ilits->rib.nCustomerType = (ilits->chip->core_ver >= CORE_VER_1470) ? ilits->rib.nCustomerType : POSITION_CUSTOMER_TYPE_OFF; + ilits->customertype_off = POSITION_CUSTOMER_TYPE_OFF; + } + + ILI_INFO("report_info_block : nReportByPixel = %d, nIsHostDownload = %d, nIsSPIICE = %d, nIsSPISLAVE = %d, nIsI2C = %d\n", + ilits->rib.nReportByPixel, ilits->rib.nIsHostDownload, ilits->rib.nIsSPIICE, ilits->rib.nIsSPISLAVE, ilits->rib.nIsI2C); + + ILI_INFO("report_info_block : nReserved00 = %d, nReportResolutionMode = %d, nCustomerType = %d, nReserved02 = %x, nReserved03 = %x\n", + ilits->rib.nReserved00, ilits->rib.nReportResolutionMode, ilits->rib.nCustomerType, ilits->rib.nReserved02, ilits->rib.nReserved03); + + + ILI_INFO("PenType = 0x%x, Customer Type OFF = 0x%x\n", ilits->PenType, ilits->customertype_off); + +#if ENABLE_PEN_MODE + /* Get hex Pen info block*/ + fw_info_addr = fbi[AP].end - INFO_PEN_ST_ADDR; + ILI_INFO("Parsing pen info block start addr = 0x%x\n", fw_info_addr); + ipio_memcpy(&ilits->pen_info_block, (pfw + fw_info_addr), sizeof(ilits->pen_info_block), sizeof(ilits->pen_info_block)); + + ILI_INFO("pen_info_block : nPxRaw = %d, nPyRaw = %d, nPxVa = %d,nPyVa = %d, nReserved0 = %d, nReserved01 = %d, nReserved2 = %d, nReserved03 = %d\n", + ilits->pen_info_block.nPxRaw, ilits->pen_info_block.nPyRaw, ilits->pen_info_block.nPxVa, ilits->pen_info_block.nPyVa, ilits->pen_info_block.nReserved00, ilits->pen_info_block.nReserved01, ilits->pen_info_block.nReserved02, ilits->pen_info_block.nReserved03); +#endif + +#if ENABLE_CASCADE + /* Get hex Cascade info block*/ + fw_info_addr = fbi[AP].end - INFO_CASCADE_ST_ADDR; + ILI_INFO("Parsing cascade info block start addr = 0x%x\n", fw_info_addr); + ipio_memcpy(&ilits->cascade_info_block, (pfw + fw_info_addr), sizeof(ilits->cascade_info_block), sizeof(ilits->cascade_info_block)); + + ILI_INFO("cascade_info_block : nDisable = %d, nNum = %d, nReserved0 = %d, nReserved1 = %d, nReserved2 = %d, nReserved03 = %d\n", + ilits->cascade_info_block.nDisable, ilits->cascade_info_block.nNum, ilits->cascade_info_block.nReserved00, ilits->cascade_info_block.nReserved01, ilits->cascade_info_block.nReserved02, ilits->cascade_info_block.nReserved03); +#else + ilits->cascade_info_block.nDisable = ENABLE; + ilits->cascade_info_block.nNum = 0; + ILI_INFO("cascade_info_block : nDisable = %d, nNum = %d\n", ilits->cascade_info_block.nDisable, ilits->cascade_info_block.nNum); +#endif + + /* Calculate update address */ + ILI_INFO("New FW ver = 0x%x\n", tfd.new_fw_cb); + ILI_INFO("star_addr = 0x%06X, end_addr = 0x%06X, Block Num = %d\n", tfd.start_addr, tfd.end_addr, tfd.block_number); + + return 0; +} + +static int ilitek_tddi_fw_ili_convert(u8 *pfw) +{ + int i, size, blk_num = 0, blk_map = 0, num; + int b0_addr = 0, b0_num = 0; + + if (ERR_ALLOC_MEM(ilits->md_fw_ili)) + return -ENOMEM; + + CTPM_FW = ilits->md_fw_ili; + size = ilits->md_fw_ili_size; + + if (size < ILI_FILE_HEADER || size > (MAX_HEX_FILE_SIZE + ILI_FILE_HEADER)) { + ILI_ERR("size of ILI file is invalid\n"); + return -EINVAL; + } + + /* Check if it's old version of ILI format. */ + if (CTPM_FW[22] == 0xFF && CTPM_FW[23] == 0xFF && + CTPM_FW[24] == 0xFF && CTPM_FW[25] == 0xFF) { + ILI_ERR("Invaild ILI format, abort!\n"); + return -EINVAL; + } + + blk_num = CTPM_FW[131]; + blk_map = (CTPM_FW[129] << 8) | CTPM_FW[130]; + ILI_INFO("Parsing ILI file, block num = %d, block mapping = %x\n", blk_num, blk_map); + + if (blk_num > (FW_BLOCK_INFO_NUM - 1) || !blk_num || !blk_map) { + ILI_ERR("Number of block or block mapping is invalid, abort!\n"); + return -EINVAL; + } + + memset(fbi, 0x0, sizeof(fbi)); + + tfd.start_addr = 0; + tfd.end_addr = 0; + tfd.hex_tag = BLOCK_TAG_AF; + + /* Parsing block info */ + for (i = 0; i < FW_BLOCK_INFO_NUM; i++) { + /* B0 tag */ + b0_addr = (CTPM_FW[4 + i * 4] << 16) | (CTPM_FW[5 + i * 4] << 8) | (CTPM_FW[6 + i * 4]); + b0_num = CTPM_FW[7 + i * 4]; + if ((b0_num != 0) && (b0_addr != 0x000000)) + fbi[b0_num].fix_mem_start = b0_addr; + + /* AF tag */ + num = i + 1; + if (num >= FW_BLOCK_INFO_NUM) + break; + if (((blk_map >> i) & 0x01) == 0x01) { + fbi[num].start = (CTPM_FW[132 + i * 6] << 16) | (CTPM_FW[133 + i * 6] << 8) | CTPM_FW[134 + i * 6]; + fbi[num].end = (CTPM_FW[135 + i * 6] << 16) | (CTPM_FW[136 + i * 6] << 8) | CTPM_FW[137 + i * 6]; + + if (fbi[num].fix_mem_start == 0) + fbi[num].fix_mem_start = INT_MAX; + + fbi[num].len = fbi[num].end - fbi[num].start + 1; + ILI_DBG("Block[%d]: start_addr = %x, end = %x, fix_mem_start = 0x%x\n", num, fbi[num].start, + fbi[num].end, fbi[num].fix_mem_start); + if (num == GESTURE) + ilits->gesture_load_code = true; + } + } + + memcpy(pfw, CTPM_FW + ILI_FILE_HEADER, size - ILI_FILE_HEADER); + + if (ilitek_fw_calc_file_crc(pfw) < 0) + return -1; + + tfd.block_number = blk_num; + tfd.end_addr = size - ILI_FILE_HEADER; + return 0; +} + +static int ilitek_tddi_fw_hex_convert(u8 *phex, int size, u8 *pfw) +{ + int block = 0; + u32 i = 0, j = 0, k = 0, num = 0; + u32 len = 0, addr = 0, type = 0; + u32 start_addr = 0x0, end_addr = 0x0, ex_addr = 0; + u32 offset; + + memset(fbi, 0x0, sizeof(fbi)); + + /* Parsing HEX file */ + for (; i < size;) { + len = HexToDec(&phex[i + 1], 2); + addr = HexToDec(&phex[i + 3], 4); + type = HexToDec(&phex[i + 7], 2); + + if (type == 0x04) { + ex_addr = HexToDec(&phex[i + 9], 4); + } else if (type == 0x02) { + ex_addr = HexToDec(&phex[i + 9], 4); + ex_addr = ex_addr >> 12; + } else if (type == BLOCK_TAG_AF) { + /* insert block info extracted from hex */ + tfd.hex_tag = type; + if (tfd.hex_tag == BLOCK_TAG_AF) + num = HexToDec(&phex[i + 9 + 6 + 6], 2); + else + num = 0xFF; + + if (num > (FW_BLOCK_INFO_NUM - 1)) { + ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n", + num, FW_BLOCK_INFO_NUM - 1); + return -EINVAL; + } + + fbi[num].start = HexToDec(&phex[i + 9], 6); + fbi[num].end = HexToDec(&phex[i + 9 + 6], 6); + fbi[num].fix_mem_start = INT_MAX; + fbi[num].len = fbi[num].end - fbi[num].start + 1; + ILI_DBG("Block[%d]: start_addr = %x, end = %x", num, fbi[num].start, fbi[num].end); + + if (num == GESTURE) + ilits->gesture_load_code = true; + + block++; + } else if (type == BLOCK_TAG_B0 && tfd.hex_tag == BLOCK_TAG_AF) { + num = HexToDec(&phex[i + 9 + 6], 2); + + if (num > (FW_BLOCK_INFO_NUM - 1)) { + ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n", + num, FW_BLOCK_INFO_NUM - 1); + return -EINVAL; + } + + fbi[num].fix_mem_start = HexToDec(&phex[i + 9], 6); + ILI_DBG("Tag 0xB0: change Block[%d] to addr = 0x%x\n", num, fbi[num].fix_mem_start); + } + + addr = addr + (ex_addr << 16); + + if (phex[i + 1 + 2 + 4 + 2 + (len * 2) + 2] == 0x0D) + offset = 2; + else + offset = 1; + + if (addr >= MAX_HEX_FILE_SIZE) { + ILI_ERR("Invalid hex format %d\n", addr); + return -1; + } + + if (type == 0x00) { + end_addr = addr + len; + if (addr < start_addr) + start_addr = addr; + /* fill data */ + for (j = 0, k = 0; j < (len * 2); j += 2, k++) + pfw[addr + k] = HexToDec(&phex[i + 9 + j], 2); + } + i += 1 + 2 + 4 + 2 + (len * 2) + 2 + offset; + } + + if (ilitek_fw_calc_file_crc(pfw) < 0) + return -1; + + tfd.start_addr = start_addr; + tfd.end_addr = end_addr; + tfd.block_number = block; + return 0; +} + +static int ilitek_tdd_fw_hex_open(u8 op, u8 *pfw) +{ + int ret = 0, fsize = 0; + const struct firmware *fw = NULL; +#if (!GENERIC_KERNEL_IMAGE) +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + mm_segment_t old_fs; +#endif + struct file *f = NULL; + loff_t pos = 0; +#endif + + ILI_INFO("Open file method = %s, path = %s\n", + op ? "FILP_OPEN" : "REQUEST_FIRMWARE", + op ? ilits->md_fw_filp_path : ilits->md_fw_rq_path); + + switch (op) { + case REQUEST_FIRMWARE: + if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) { + ILI_ERR("Request firmware failed, try again\n"); + if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) { + ILI_ERR("Request firmware failed after retry\n"); + ret = -1; + goto out; + } + } + + fsize = fw->size; + ILI_INFO("fsize = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is invaild\n"); + release_firmware(fw); + ret = -1; + goto out; + } + + ilits->tp_fw.size = 0; + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n"); + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw after retry\n"); + release_firmware(fw); + ret = -ENOMEM; + goto out; + } + } + + /* Copy fw data got from request_firmware to global */ + ipio_memcpy((u8 *)ilits->tp_fw.data, fw->data, fsize * sizeof(*fw->data), fsize); + ilits->tp_fw.size = fsize; + release_firmware(fw); + break; + case FILP_OPEN: +#if GENERIC_KERNEL_IMAGE + ILI_ERR("GKI version not allow drivers to use filp_open\n"); + return -1; +#if 0 + ilits->tp_fw.data = NULL; + ret = kernel_read_file_from_path(ilits->md_fw_filp_path, 0, (void **)&ilits->tp_fw.data, INT_MAX, &ilits->tp_fw.size, + READING_POLICY); + if (ret < 0 || (ilits->tp_fw.data == NULL)) { + ILI_ERR("Unable to open file: %s (%d)", ilits->md_fw_filp_path, ret); + goto out; + } + + ILI_INFO("fsize = %d\n", (int)ilits->tp_fw.size); +#endif +#else + f = filp_open(ilits->md_fw_filp_path, O_RDONLY, 0644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open the file, %ld\n", PTR_ERR(f)); + ret = -1; + goto out; + } + + fsize = f->f_inode->i_size; + ILI_INFO("fsize = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is invaild\n"); + filp_close(f, NULL); + ret = -1; + goto out; + } + + ipio_vfree((void **)&(ilits->tp_fw.data)); + ilits->tp_fw.size = 0; + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n"); + ilits->tp_fw.data = vmalloc(fsize); + if (ERR_ALLOC_MEM(ilits->tp_fw.data)) { + ILI_ERR("Failed to allocate tp_fw after retry\n"); + filp_close(f, NULL); + ret = -ENOMEM; + goto out; + } + } + + /* ready to map user's memory to obtain data by reading files */ + pos = 0; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + old_fs = get_fs(); + set_fs(KERNEL_DS); + vfs_read(f, (u8 *)ilits->tp_fw.data, fsize, &pos); + set_fs(old_fs); +#else + kernel_read(f, (u8 *)ilits->tp_fw.data, fsize, &pos); +#endif + filp_close(f, NULL); + ilits->tp_fw.size = fsize; +#endif + break; + default: + ILI_ERR("Unknown open file method, %d\n", op); + break; + } + + if (ERR_ALLOC_MEM(ilits->tp_fw.data) || ilits->tp_fw.size <= 0) { + ILI_ERR("fw data/size is invaild\n"); + ret = -1; + goto out; + } + + /* Convert hex and copy data from tp_fw.data to pfw */ + if (ilitek_tddi_fw_hex_convert((u8 *)ilits->tp_fw.data, ilits->tp_fw.size, pfw) < 0) { + ILI_ERR("Convert hex file failed\n"); + ret = -1; + } + +out: + ipio_vfree((void **)&(ilits->tp_fw.data)); + return ret; +} + +int ili_fw_upgrade(int op) +{ + int i, ret = 0, retry = 3; + static bool get_firmware; + + if (!ilits->boot || ilits->force_fw_update || ERR_ALLOC_MEM(pfw)) { + ilits->gesture_load_code = false; + get_firmware = false; + + if (ERR_ALLOC_MEM(pfw)) { + ipio_vfree((void **)&pfw); + pfw = vmalloc(MAX_HEX_FILE_SIZE * sizeof(u8)); + if (ERR_ALLOC_MEM(pfw)) { + ILI_ERR("Failed to allocate pfw memory, %ld\n", PTR_ERR(pfw)); + ipio_vfree((void **)&pfw); + ret = -ENOMEM; + goto out; + } + } + + for (i = 0; i < MAX_HEX_FILE_SIZE; i++) + pfw[i] = 0xFF; + + if (ilitek_tdd_fw_hex_open(op, pfw) < 0) { + ILI_ERR("Open hex file fail, try upgrade from ILI file\n"); + + /* + * Users might not be aware of a broken hex file when recovering + * fw from ILI file. We should force them to check + * hex files if they attempt to update via device node. + */ + if (ilits->node_update) { + ILI_ERR("Ignore update from ILI file\n"); + ipio_vfree((void **)&pfw); + return -EFW_CONVERT_FILE; + } + + if (ilitek_tddi_fw_ili_convert(pfw) < 0) { + ILI_ERR("Convert ILI file error\n"); + ret = -EFW_CONVERT_FILE; + goto out; + } + } + + if (ilitek_tddi_fw_update_block_info(pfw) < 0) { + ret = -EFW_CONVERT_FILE; + goto out; + } + + if (ilits->chip->core_ver >= CORE_VER_1470 && ilits->rib.nIsHostDownload == 0) { + ILI_ERR("hex file interface no match error\n"); + return -EFW_INTERFACE; + } + get_firmware = true; + } + + if (!get_firmware) { + ILI_ERR("Convert ILI file error\n"); + return -EFW_CONVERT_FILE; + } + +#if (ENGINEER_FLOW) + if (!ilits->eng_flow) { + do { + ret = ilitek_tddi_fw_iram_upgrade(pfw, OFF); + if (ret == UPDATE_PASS) + break; + + ILI_ERR("Upgrade failed, do retry!\n"); + } while (--retry > 0); + + if (ret != UPDATE_PASS) { + ILI_ERR("Failed to upgrade fw %d times, erasing iram\n", retry); + if (ilits->cascade_info_block.nNum != 0) { + if (ili_cascade_reset_ctrl(ilits->reset, false) < 0) + ILI_ERR("TP cascade reset failed while erasing data\n"); + } else { + ili_reset_ctrl(ilits->reset); + } + ilits->xch_num = 0; + ilits->ych_num = 0; + return ret; + } + } else { + ILI_ERR("eng_flow do reset!\n"); + if (ilits->cascade_info_block.nNum != 0) { + if (ili_cascade_reset_ctrl(ilits->reset, true) < 0) + ILI_ERR("TP cascade reset failed while erasing data\n"); + } else { + ili_reset_ctrl(ilits->reset); + } + mdelay(50); + } +#else + do { + ret = ilitek_tddi_fw_iram_upgrade(pfw, OFF); + if (ret == UPDATE_PASS) + break; + + ILI_ERR("Upgrade failed, do retry!\n"); + } while (--retry > 0); + + if (ret != UPDATE_PASS) { + ILI_ERR("Failed to upgrade fw %d times, erasing iram\n", retry); + if (ilits->cascade_info_block.nNum != 0) { + if (ili_cascade_reset_ctrl(ilits->reset, false) < 0) + ILI_ERR("TP cascade reset failed while erasing data\n"); + } else { + ili_reset_ctrl(ilits->reset); + } + + ilits->xch_num = 0; + ilits->ych_num = 0; + return ret; + } +#endif +out: + if (!ilits->info_from_hex) + ili_irq_enable(); + ili_ic_get_all_info(); + ili_ic_func_ctrl_reset(); + + return ret; +} + +void ili_fw_read_flash_info(bool mcu) +{ + return; +} +void ilitek_tddi_flash_protect(bool enable, bool mcu) +{ + return; +} + +void ili_flash_clear_dma(void) +{ + return; +} + +void ili_flash_dma_write(u32 start, u32 end, u32 len) +{ + return; +} + +int ili_fw_dump_flash_data(u32 start, u32 end, bool user, bool mcu) +{ + return 0; +} diff --git a/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_i2c.c b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_i2c.c new file mode 100644 index 000000000000..f348a2f356e0 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_i2c.c @@ -0,0 +1,504 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek_v3.h" + +struct touch_bus_info { + struct i2c_driver bus_driver; + struct ilitek_hwif_info *hwif; +}; + +struct ilitek_ts_data *ilits; + +#define RW_SYNC 0 +#define R_ONLY 1 +#define W_ONLY 2 + +#if I2C_DMA_TRANSFER +static unsigned char *ilitek_dma_va; +static dma_addr_t ilitek_dma_pa; + +#define DMA_VA_BUFFER 4096 + +static int dma_i2c_alloc(struct i2c_client *client) +{ + if (client != NULL) { + client->dev.coherent_dma_mask = DMA_BIT_MASK(32); + ilitek_dma_va = (u8 *)dmam_alloc_coherent(client->dev, DMA_VA_BUFFER, &ilitek_dma_pa, GFP_KERNEL); + if (ERR_ALLOC_MEM(ilitek_dma_va)) { + ILI_ERR("Allocate DMA I2C Buffer failed\n"); + return -ENOMEM; + } + + memset(ilitek_dma_va, 0, DMA_VA_BUFFER); + client->ext_flag |= I2C_DMA_FLAG; + return 0; + } + + ILI_ERR("client is NULL, allocated dma i2c failed\n"); + return -ENODEV; +} +#endif + +static int core_i2c_write(void *buf, int len) +{ + int ret = 0; + u8 *txbuf = (u8 *)buf; + u8 check_sum = 0; + u8 *mpbuf = NULL; + + struct i2c_msg msgs[] = { + { + .addr = ilits->i2c->addr, + .flags = 0, /* write flag. */ + .len = len, + .buf = txbuf, + }, + }; + +#if I2C_DMA_TRANSFER + if (len > 8) { + msgs[0].addr = (ilits->i2c->addr & I2C_MASK_FLAG); + msgs[0].ext_flag = (ilits->i2c->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG); + memcpy(ilitek_dma_va, txbuf, len); + msgs[0].buf = (u8 *)ilitek_dma_pa; + } +#endif + + + /* + * NOTE: If TP driver is doing MP test and commanding 0xF1 to FW, we add a checksum + * to the last index and plus 1 with size. + */ + if (atomic_read(&ilits->mp_stat) && txbuf[0] == P5_X_SET_CDC_INIT) { + check_sum = ili_calc_packet_checksum(txbuf, len); + mpbuf = kcalloc(len + 1, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(mpbuf)) { + ILI_ERR("Failed to allocate mpbuf mem\n"); + return -ENOMEM; + } + ipio_memcpy(mpbuf, txbuf, len, msgs[0].len); + mpbuf[len] = check_sum; + msgs[0].buf = mpbuf; + ili_dump_data(mpbuf, 8, len+1, 0, "mp cdc cmd"); + msgs[0].len = len + 1; + } + + if (i2c_transfer(ilits->i2c->adapter, msgs, 1) != 1) + ret = -1; + + if (!atomic_read(&ilits->ice_stat)) + mdelay(1); + + ipio_kfree((void **)&mpbuf); + return ret; +} + +static int core_i2c_read(void *buf, int len) +{ + int ret; + u8 *rxbuf = (u8 *)buf; + + struct i2c_msg msgs[] = { + { + .addr = ilits->i2c->addr, + .flags = I2C_M_RD, /* read flag. */ + .len = len, + .buf = rxbuf, + }, + }; + + +#if I2C_DMA_TRANSFER + if (len > 8) { + msgs[0].addr = (ilits->i2c->addr & I2C_MASK_FLAG); + msgs[0].ext_flag = (ilits->i2c->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG); + msgs[0].buf = (u8 *)ilitek_dma_pa; + } else { + msgs[0].buf = rxbuf; + } +#endif + + + ret = i2c_transfer(ilits->i2c->adapter, msgs, 1); + +#if I2C_DMA_TRANSFER + if (len > 8) + memcpy(rxbuf, ilitek_dma_va, len); +#endif + + /* + * If i2c_transfer is ok (must return 1 because only sends one msg), + * return #bytes transferred, else error code. + */ + return (ret == 1) ? len : ret; +} + +static int ilitek_i2c_write(void *buf, int len) +{ + int ret = 0; + + if (len == 0) { + ILI_ERR("i2c write len is invalid\n"); + return -EINVAL; + } + + ret = core_i2c_write(buf, len); + if (ret < 0) { + if (atomic_read(&ilits->tp_reset) == START) { + ret = 0; + goto out; + } + ILI_ERR("i2c write error, ret = %d\n", ret); + } + +out: + return ret; +} + +static int ilitek_i2c_read(void *buf, int len) +{ + int ret = 0; + + if (len == 0) { + ILI_ERR("i2c read len is invalid\n"); + return -EINVAL; + } + + ret = core_i2c_read(buf, len); + if (ret < 0) { + if (atomic_read(&ilits->tp_reset) == START) { + ret = 0; + goto out; + } + ILI_ERR("i2c read error, ret = %d\n", ret); + } + +out: + return ret; +} + +static int ili_i2c_pll_clk_wakeup(void) +{ + int ret = 0; + u8 wakeup = 0xA3; +#if (PLL_CLK_WAKEUP_TP_RESUME == ENABLE) + if (ilits->pll_clk_wakeup == true) { +#else + if ((ilits->pll_clk_wakeup == true) && (ilits->tp_suspend == true)) { +#endif + ret = ilitek_i2c_write(&wakeup, sizeof(wakeup)); + if (ret < 0) + ILI_ERR("i2c write dummy cmd error\n"); + } + return ret; +} + +static int ili_i2c_wrapper(u8 *txbuf, u32 wlen, u8 *rxbuf, u32 rlen, bool spi_irq, bool i2c_irq) +{ + int ret = 0, operate = -1; + + if (wlen > 0) { + if (ERR_ALLOC_MEM(txbuf)) { + ILI_ERR("txbuf is null\n"); + return -ENOMEM; + } + } + + if (rlen > 0) { + if (ERR_ALLOC_MEM(rxbuf)) { + ILI_ERR("rxbuf is null\n"); + return -ENOMEM; + } + } + +#if ENABLE_CASCADE + if (ilits->i2c_addr_change == SLAVE) { + ilits->i2c->addr = TDDI_I2C_SLAVE_ADDR; + } else { + ilits->i2c->addr = TDDI_I2C_ADDR; + } +#endif + + if (wlen > 0 && rlen > 0) + operate = RW_SYNC; + else if (wlen > 0 && !rlen) + operate = W_ONLY; + else + operate = R_ONLY; + + if (ilits->int_pulse) + ilits->detect_int_stat = ili_ic_check_int_pulse; + else + ilits->detect_int_stat = ili_ic_check_int_level; + + if (i2c_irq) + atomic_set(&ilits->cmd_int_check, ENABLE); + + switch (operate) { + case RW_SYNC: + ret = ili_i2c_pll_clk_wakeup(); + if (ret < 0) { + ILI_ERR("i2c-wrapper dummy cmd write error\n"); + } + ret = ilitek_i2c_write(txbuf, wlen); + if (ret < 0) { + ILI_ERR("i2c-wrapper write error\n"); + break; + } + + if (i2c_irq) { + if (ilits->detect_int_stat(false) < 0) { + ILI_ERR("ERROR! Check INT timeout\n"); + ret = -ETIME; + break; + } + } else { + msleep(1); + } + + ret = ilitek_i2c_read(rxbuf, rlen); + if (ret < 0) { + ILI_ERR("i2c-wrapper read error\n"); + break; + } + break; + case W_ONLY: + ret = ili_i2c_pll_clk_wakeup(); + if (ret < 0) { + ILI_ERR("i2c-wrapper dummy cmd write error\n"); + } + ret = ilitek_i2c_write(txbuf, wlen); + if (ret < 0) { + ILI_ERR("i2c-wrapper write error\n"); + break; + } + break; + case R_ONLY: + if (i2c_irq) { + if (ilits->detect_int_stat(false) < 0) { + ILI_ERR("ERROR! Check INT timeout\n"); + ret = -ETIME; + break; + } + } + + ret = ilitek_i2c_read(rxbuf, rlen); + if (ret < 0) { + ILI_ERR("i2c-wrapper read error\n"); + break; + } + break; + default: + ILI_ERR("Unknown ts-i2c operation\n"); + ret = -EINVAL; + break; + } + + if (i2c_irq) + atomic_set(&ilits->cmd_int_check, DISABLE); + + return ret; +} + +int ili_core_spi_setup(int num) +{ + ILI_ERR("Not support this interface\n"); + return 0; +} + +static int ilitek_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct touch_bus_info *info = + container_of(to_i2c_driver(i2c->dev.driver), + struct touch_bus_info, bus_driver); + + ILI_INFO("ilitek i2c probe\n"); + + if (!i2c) { + ILI_ERR("i2c client is NULL\n"); + return -ENODEV; + } + + if (i2c->addr != TDDI_I2C_ADDR) { + i2c->addr = TDDI_I2C_ADDR; + ILI_INFO("i2c addr doesn't be set up, use default : 0x%x\n", i2c->addr); + } + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + ILI_ERR("i2c functions are not supported!\n"); + return -ENODEV; + } + + ilits = devm_kzalloc(&i2c->dev, sizeof(struct ilitek_ts_data), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits)) { + ILI_ERR("Failed to allocate ts memory, %ld\n", PTR_ERR(ilits)); + return -ENOMEM; + } + + /* Used for receiving touch data only, do not mix up with others. */ + ilits->tr_buf = kzalloc(TR_BUF_SIZE, GFP_ATOMIC); + if (ERR_ALLOC_MEM(ilits->tr_buf)) { + ILI_ERR("failed to allocate touch report buffer\n"); + return -ENOMEM; + } + + ilits->gcoord = kzalloc(sizeof(struct gesture_coordinate), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->gcoord)) { + ILI_ERR("Failed to allocate gresture coordinate buffer\n"); + return -ENOMEM; + } + + ilits->i2c = i2c; + ilits->spi = NULL; + ilits->dev = &i2c->dev; + ilits->hwif = info->hwif; + ilits->phys = "I2C"; + ilits->wrapper = ili_i2c_wrapper; + ilits->detect_int_stat = ili_ic_check_int_pulse; + ilits->int_pulse = true; + ilits->mp_retry = false; + +#if I2C_DMA_TRANSFER + if (dma_i2c_alloc(ilits->i2c) < 0) + ILI_ERR("Failed to alllocate DMA mem %ld\n", PTR_ERR(ilits->i2c)); +#endif + + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + ilits->tp_data_mode = AP_MODE; + + if (TDDI_RST_BIND) + ilits->reset = TP_IC_WHOLE_RST_WITH_FLASH; + else + ilits->reset = TP_HW_RST_ONLY; + +#if ENABLE_CASCADE + ilits->rst_edge_delay = 60; +#else + ilits->rst_edge_delay = 40; +#endif +#if GENERIC_KERNEL_IMAGE + ilits->fw_open = REQUEST_FIRMWARE; +#else + ilits->fw_open = FILP_OPEN; +#endif + ilits->fw_upgrade_mode = UPGRADE_FLASH; + ilits->mp_move_code = ili_move_mp_code_flash; + ilits->gesture_move_code = ili_move_gesture_code_flash; + ilits->esd_recover = ili_wq_esd_i2c_check; + ilits->esd_func_ctrl = ENABLE_WQ_ESD; + ilits->ges_recover = ili_touch_esd_gesture_flash; + ilits->gesture_mode = DATA_FORMAT_GESTURE_INFO; + ilits->gesture_demo_ctrl = DISABLE; + ilits->wtd_ctrl = OFF; + ilits->report = ENABLE; + ilits->dnp = DISABLE; + ilits->dlnp = DISABLE; + ilits->irq_tirgger_type = IRQF_TRIGGER_RISING; + ilits->info_from_hex = DISABLE; + ilits->wait_int_timeout = AP_INT_TIMEOUT; + ilits->ice_flash_cs_ctrl = ENABLE; + ilits->compress_disable = ENABLE; + ilits->compress_handonly_disable = ENABLE; + ilits->compress_penonly_disable = ENABLE; +#if MULTI_REPORT_RATE + ilits->current_report_rate_mode = DDI60_TP120; /*Default report mode*/ +#endif + +#if ENABLE_GESTURE + ilits->gesture = DISABLE; + ilits->ges_sym.double_tap = DOUBLE_TAP; + ilits->ges_sym.alphabet_line_2_top = ALPHABET_LINE_2_TOP; + ilits->ges_sym.alphabet_line_2_bottom = ALPHABET_LINE_2_BOTTOM; + ilits->ges_sym.alphabet_line_2_left = ALPHABET_LINE_2_LEFT; + ilits->ges_sym.alphabet_line_2_right = ALPHABET_LINE_2_RIGHT; + ilits->ges_sym.alphabet_m = ALPHABET_M; + ilits->ges_sym.alphabet_w = ALPHABET_W; + ilits->ges_sym.alphabet_c = ALPHABET_C; + ilits->ges_sym.alphabet_E = ALPHABET_E; + ilits->ges_sym.alphabet_V = ALPHABET_V; + ilits->ges_sym.alphabet_O = ALPHABET_O; + ilits->ges_sym.alphabet_S = ALPHABET_S; + ilits->ges_sym.alphabet_Z = ALPHABET_Z; + ilits->ges_sym.alphabet_V_down = ALPHABET_V_DOWN; + ilits->ges_sym.alphabet_V_left = ALPHABET_V_LEFT; + ilits->ges_sym.alphabet_V_right = ALPHABET_V_RIGHT; + ilits->ges_sym.alphabet_two_line_2_bottom = ALPHABET_TWO_LINE_2_BOTTOM; + ilits->ges_sym.alphabet_F = ALPHABET_F; + ilits->ges_sym.alphabet_AT = ALPHABET_AT; +#endif + + return info->hwif->plat_probe(); +} + +static int ilitek_i2c_remove(struct i2c_client *i2c) +{ + ILI_INFO(); + return 0; +} + +static const struct i2c_device_id tp_i2c_id[] = { + {TDDI_DEV_ID, 0}, + {}, +}; + +int ili_interface_dev_init(struct ilitek_hwif_info *hwif) +{ + struct touch_bus_info *info; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ILI_ERR("faied to allocate i2c_driver\n"); + return -ENOMEM; + } + + if (hwif->bus_type != BUS_I2C) { + ILI_ERR("Not I2C dev\n"); + ipio_kfree((void **)&info); + return -EINVAL; + } + + hwif->info = info; + + info->bus_driver.driver.name = hwif->name; + info->bus_driver.driver.owner = hwif->owner; + info->bus_driver.driver.of_match_table = hwif->of_match_table; + info->bus_driver.driver.pm = hwif->pm; + + info->bus_driver.probe = ilitek_i2c_probe; + info->bus_driver.remove = ilitek_i2c_remove; + info->bus_driver.id_table = tp_i2c_id; + + info->hwif = hwif; + return i2c_add_driver(&info->bus_driver); +} + +void ili_interface_dev_exit(struct ilitek_ts_data *ts, bool flag) +{ + struct touch_bus_info *info = (struct touch_bus_info *)ilits->hwif->info; + + ILI_INFO("remove i2c dev\n"); + if (flag == ENABLE) { + i2c_del_driver(&info->bus_driver); + ipio_kfree((void **)&info); + } +} diff --git a/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_ic.c b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_ic.c new file mode 100644 index 000000000000..2d46d81832c6 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_ic.c @@ -0,0 +1,3021 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek_v3.h" + +#define PROTOCL_VER_NUM 8 +static struct ilitek_protocol_info protocol_info[PROTOCL_VER_NUM] = { + /* length -> fw, protocol, tp, key, panel, core, func, window, cdc, mp_info */ + [0] = {PROTOCOL_VER_500, 4, 4, 14, 30, 5, 5, 2, 8, 3, 8}, + [1] = {PROTOCOL_VER_510, 4, 3, 14, 30, 5, 5, 3, 8, 3, 8}, + [2] = {PROTOCOL_VER_520, 4, 4, 14, 30, 5, 5, 3, 8, 3, 8}, + [3] = {PROTOCOL_VER_530, 9, 4, 14, 30, 5, 5, 3, 8, 3, 8}, + [4] = {PROTOCOL_VER_540, 9, 4, 14, 30, 5, 5, 3, 8, 15, 8}, + [5] = {PROTOCOL_VER_550, 9, 4, 14, 30, 5, 5, 3, 8, 15, 14}, + [6] = {PROTOCOL_VER_560, 9, 4, 14, 30, 5, 5, 3, 8, 15, 14}, + [7] = {PROTOCOL_VER_570, 9, 4, 14, 30, 5, 5, 3, 8, 15, 14}, +}; + +struct ilitek_ic_func_ctrl func_ctrl[FUNC_CTRL_NUM] = { + /* cmd[3] = cmd, func, ctrl */ + /* rec_state 0:disable, 1: enable, 2: ignore record */ + [0] = {"sense", {0x1, 0x1, 0x0}, 3, 0x0, 2, 0xFF}, + [1] = {"sleep", {0x1, 0x2, 0x0}, 3, 0x0, 2, 0xFF}, + [2] = {"glove", {0x1, 0x6, 0x0}, 3, 0x0, 0, 0xFF}, + [3] = {"stylus", {0x1, 0x7, 0x0}, 3, 0x0, 0, 0xFF}, + [4] = {"lpwg", {0x1, 0xA, 0x0}, 3, 0x0, 2, 0xFF}, + [5] = {"proximity", {0x1, 0x10, 0x0}, 3, 0x0, 0, 0xFF}, + [6] = {"plug", {0x1, 0x11, 0x0}, 3, 0x1, 0, 0xFF}, + [7] = {"edge_palm", {0x1, 0x12, 0x0}, 3, 0x1, 0, 0xFF}, + [8] = {"lock_point", {0x1, 0x13, 0x0}, 3, 0x0, 0, 0xFF}, + [9] = {"active", {0x1, 0x14, 0x0}, 3, 0x0, 2, 0xFF}, + [10] = {"idle", {0x1, 0x19, 0x0}, 3, 0x1, 0, 0xFF}, + [11] = {"gesture_demo_en", {0x1, 0x16, 0x0}, 3, 0x0, 0, 0xFF}, + [12] = {"tp_recore", {0x1, 0x18, 0x0}, 3, 0x0, 2, 0xFF}, + [13] = {"knock_en", {0x1, 0xA, 0x8, 0x03, 0x0, 0x0}, 6, 0xFF, 0, 0xFF}, + [14] = {"int_trigger", {0x1, 0x1B, 0x0}, 3, 0x0, 2, 0xFF}, + [15] = {"ear_phone", {0x1, 0x17, 0x0}, 3, 0x0, 0, 0xFF}, + [16] = {"knuckle", {0x1, 0xF, 0x0}, 3, 0x0, 0, 0xFF}, +}; + +static int ili_check_polling_bist(u8 GroupNumIdx, u32 SramRsultStatus) +{ + u32 Bist_Sram_Fail = 0xFFFFFFFF; + int CheckCount = 10, ret = 0; + u32 bist_done = 0, bist_fail = 0, bist_fail_bus = 0; + + ili_ice_mode_write(0x46000, 0, 4); + + ili_ice_mode_read(0x04600C, &bist_done, 1); + ili_ice_mode_read(0x04600D, &bist_fail, 1); + + /* Check Clear MBist Status */ + if (ilits->sram_para.check_failbus == ENABLE) { + ili_ice_mode_read(0x046010, &bist_fail_bus, sizeof(bist_fail_bus)); + + if (((bist_done & BIT(0)) != 0x00) || ((bist_fail & BIT(0)) != 0x01) || (bist_fail_bus != 0xFFFFFFFF)) { + ILI_ERR("Clear MBist Fail\n"); + ILI_ERR("bist_done = 0x%X, bist_fail = 0x%X, bist_fail_bus = 0x%X\n", bist_done, bist_fail, bist_fail_bus); + ret = -1; + goto out; + } + } else { + if (((bist_done & BIT(0)) != 0x00) || ((bist_fail & BIT(0)) != 0x01)) { + ILI_ERR("Clear MBist Fail\n"); + ILI_ERR("bist_done = 0x%X, bist_fail = 0x%X\n", bist_done, bist_fail); + ret = -1; + goto out; + } + } + + ili_ice_mode_write(0x046000, 0x00000001, 4); + ili_ice_mode_write(0x046000, 0x00000101, 4); + + while (CheckCount > 0) { + mdelay(2); + ili_ice_mode_read(0x04600C, &bist_done, 1); + + if ((bist_done & BIT(0)) == 0x01) { + break; + } + CheckCount--; + } + + if (CheckCount <= 0) { + ILI_ERR("Check Bist_Done TimeOut! bist_done = 0x%X\n", bist_done); + ret = -1; + goto out; + } + + ili_ice_mode_read(0x04600D, &bist_fail, 1); + + if(ilits->sram_para.sram_group_en == ENABLE) { + ili_ice_mode_read(ilits->sram_para.read_addr, &Bist_Sram_Fail, 4); + + if ((Bist_Sram_Fail & SramRsultStatus) > 0) { + ILI_ERR("Check Bist Fail Flag Fail!\n"); + ILI_ERR("Bist Sram Fail Status = 0x%X, Sram Rsult Status = 0x%X\n", Bist_Sram_Fail, SramRsultStatus); + ret = -1; + goto out; + } + } else { + if (ilits->sram_para.check_failbus == ENABLE) { + ili_ice_mode_read(0x046010, &bist_fail_bus, sizeof(bist_fail_bus)); + + if (((bist_fail & BIT(0)) != 0x00) || (bist_fail_bus != 0x00000000)) { + ILI_ERR("Check Bist Fail Flag Fail\n"); + ILI_DBG("bist_fail = %d, bist_fail_bus = %d\n", bist_fail, bist_fail_bus); + ret = -1; + goto out; + } + } else { + if ((bist_fail & BIT(0)) != 0x00) { + ILI_ERR("Check Bist Fail Flag Fail\n"); + ILI_DBG("bist_fail = %d\n", bist_fail); + ret = -1; + goto out; + } + } + } + +out: + return ret; +} + +static void ili_sram_test(u32 MaxAddr, u32 MinAddr) +{ + u32 SetValue; + + /* Strat Sram Test */ + ili_ice_mode_write_by_mode(0x40004, 0x00000001, 4, BOTH); + ili_ice_mode_write_by_mode(0x40010, 0x00009878, 4, BOTH); + ili_ice_mode_write_by_mode(0x40088, 0x00006B8A, 4, BOTH); + + /* Set MinAddr */ + ili_ice_mode_write_by_mode(0x46038, MinAddr, 2, BOTH); + + /* March_LR */ + SetValue = ((MaxAddr << 16) & 0xFFFF0000) | 0xFF00; + + ili_ice_mode_write_by_mode(0x46000, 0x00010000, 4, BOTH); + ili_ice_mode_write_by_mode(0x46004, SetValue, 4, BOTH); + ili_ice_mode_write_by_mode(0x46014, 0xA4409800, 4, BOTH); + ili_ice_mode_write_by_mode(0x46018, 0xB480F590, 4, BOTH); + ili_ice_mode_write_by_mode(0x4601C, 0x9000F560, 4, BOTH); + ili_ice_mode_write_by_mode(0x46020, 0x9280C460, 4, BOTH); + ili_ice_mode_write_by_mode(0x46024, 0x0000, 2, BOTH); +} + +static int ili_sram_by_partial_area_test(void) +{ + int ret = 0; + u8 nSramNumIdx; + + for(nSramNumIdx = 0; nSramNumIdx < ilits->sram_para.group_num; nSramNumIdx++) { + ILI_DBG("Sram Partial Area : %s\n", ilits->sram_para.strSramPartialArea[nSramNumIdx]); + + ili_ice_mode_write_by_mode(ilits->sram_para.write_addr, ilits->sram_para.SramPartialAreaTest_en[nSramNumIdx], 4, BOTH); + ILI_DBG("Write Sram Partial Area En : 0x%X\n", ilits->sram_para.SramPartialAreaTest_en[nSramNumIdx]); + + ili_sram_test(ilits->sram_para.MaxAddr, ilits->sram_para.MinAddr); + + if (ili_check_polling_bist(nSramNumIdx, ilits->sram_para.SramResult[nSramNumIdx]) < 0) { + ILI_ERR("Check March_LR Bist Fail during partial area test\n"); + ret = -1; + } + } + + return ret; +} + +static int ili_ic_cascade_polling_bist(void) +{ + int retry = 10, ret = 0; + u32 bist_done = 0, bist_fail = 0, bist_fail_bus = 0; + u32 bist_slave_done = 0, bist_slave_fail = 0, bist_slave_fail_bus = 0; + u32 pen_bist_fail_bus = 0, pen_bist_slave_fail_bus = 0; + + ili_ice_mode_write_by_mode(0x046000, 0x000000, 4, BOTH); + +#if (TDDI_INTERFACE == BUS_I2C) + ilits->i2c_addr_change = SLAVE; + ili_ice_mode_read(0x04600C, &bist_slave_done, sizeof(bist_slave_done)); + ili_ice_mode_read(0x04600D, &bist_slave_fail, sizeof(bist_slave_fail)); + + + ilits->i2c_addr_change = MASTER; + ili_ice_mode_read(0x04600C, &bist_done, sizeof(bist_done)); + ili_ice_mode_read(0x04600D, &bist_fail, sizeof(bist_fail)); + + if (ilits->sram_para.check_failbus == ENABLE) { + ilits->i2c_addr_change = SLAVE; + ili_ice_mode_read(0x046010, &bist_slave_fail_bus, sizeof(bist_slave_fail_bus)); + ili_ice_mode_read(0x046048, &pen_bist_slave_fail_bus, sizeof(pen_bist_slave_fail_bus)); + + ilits->i2c_addr_change = MASTER; + ili_ice_mode_read(0x046010, &bist_fail_bus, sizeof(bist_fail_bus)); + ili_ice_mode_read(0x046048, &pen_bist_fail_bus, sizeof(pen_bist_fail_bus)); + + if (((bist_done & BIT(0)) != 0x00) || ((bist_fail & BIT(0)) != 0x01) || (bist_fail_bus != 0xFFFFFFFF) + || ((bist_slave_done & BIT(0)) != 0x00) || ((bist_slave_fail & BIT(0)) != 0x01) + || (bist_slave_fail_bus != 0xFFFFFFFF)) { + + ILI_ERR("SRAM TEST FAIL\n"); + ILI_ERR("bist_done = 0x%X, bist_fail = 0x%X, bist_fail_bus = 0x%X\n", bist_done, bist_fail, bist_fail_bus); + ILI_ERR("bist_slave_done = 0x%X, bist_slave_fail = 0x%X, bist_slave_fail_bus = 0x%X\n", bist_slave_done, bist_slave_fail, bist_slave_fail_bus); + ILI_ERR("pen_bist_fail_bus = 0x%X, pen_bist_slave_fail_bus = 0x%X\n", pen_bist_fail_bus, pen_bist_slave_fail_bus); + ret = -1; + goto out; + } + } else { + if (((bist_done & BIT(0)) != 0x00) || ((bist_fail & BIT(0)) != 0x01) || ((bist_slave_done & BIT(0)) != 0x00) + || ((bist_slave_fail & BIT(0)) != 0x01)) { + + ILI_ERR("SRAM TEST FAIL\n"); + ILI_ERR("bist_done = 0x%X, bist_fail = 0x%X\n", bist_done, bist_fail); + ILI_ERR("bist_slave_done = 0x%X, bist_slave_fail = 0x%X\n", bist_slave_done, bist_slave_fail); + ret = -1; + goto out; + } + } + + ili_ice_mode_write_by_mode(0x046000, 0x00000001, 4, BOTH); + ili_ice_mode_write_by_mode(0x046000, 0x00000101, 4, BOTH); + + while (retry > 0) { + mdelay(10); + ilits->i2c_addr_change = SLAVE; + ili_ice_mode_read(0x04600C, &bist_slave_done, sizeof(bist_slave_done)); + + ilits->i2c_addr_change = MASTER; + ili_ice_mode_read(0x04600C, &bist_done, sizeof(bist_done)); + ILI_ERR("bist_done = 0x%X, bist_slave_done = 0x%X\n", bist_done, bist_slave_done); + + + if (((bist_done & BIT(0)) == 0x01) && ((bist_slave_done & BIT(0)) == 0x01)) { + break; + } + retry--; + } + + if (retry <= 0) { + ILI_DBG("SRAM TEST FAIL\n"); + ret = -1; + goto out; + } + + ilits->i2c_addr_change = SLAVE; + ili_ice_mode_read(0x04600D, &bist_slave_fail, sizeof(bist_slave_fail)); + + ilits->i2c_addr_change = MASTER; + ili_ice_mode_read(0x04600D, &bist_fail, sizeof(bist_fail)); + + if (ilits->sram_para.check_failbus == ENABLE) { + ilits->i2c_addr_change = SLAVE; + ili_ice_mode_read(0x046010, &bist_slave_fail_bus, sizeof(bist_slave_fail_bus)); + ili_ice_mode_read(0x046048, &pen_bist_slave_fail_bus, sizeof(pen_bist_slave_fail_bus)); + + ilits->i2c_addr_change = MASTER; + ili_ice_mode_read(0x046010, &bist_fail_bus, sizeof(bist_fail_bus)); + ili_ice_mode_read(0x046048, &pen_bist_fail_bus, sizeof(pen_bist_fail_bus)); + + if (((bist_fail & BIT(0)) != 0x00) || (bist_fail_bus != 0x00000000) || (((bist_slave_fail & BIT(24)) >> 24) != 0x00) + || (bist_slave_fail_bus != 0x00000000)) { + + ILI_ERR("SRAM TEST FAIL\n"); + ILI_ERR("bist_fail = 0x%X, bist_fail_bus = 0x%X\n", bist_fail, bist_fail_bus); + ILI_ERR("bist_slave_fail = 0x%X, bist_slave_fail_bus = 0x%X\n", bist_slave_fail, bist_slave_fail_bus); + ILI_ERR("pen_bist_fail_bus = 0x%X, pen_bist_slave_fail_bus = 0x%X\n", pen_bist_fail_bus, pen_bist_slave_fail_bus); + ret = -1; + goto out; + } + } else { + if (((bist_fail & BIT(0)) != 0x00) || (((bist_slave_fail & BIT(24)) >> 24) != 0x00)) { + ILI_ERR("SRAM TEST FAIL\n"); + ILI_ERR("bist_fail = 0x%X, bist_slave_fail = 0x%X\n", bist_fail, bist_slave_fail); + ret = -1; + goto out; + } + } + + +#elif (TDDI_INTERFACE == BUS_SPI) + ili_ice_mode_write(READ_BACK_LOCK_REG, 0x01, 1); + ili_ice_mode_read(0x04600C, &bist_done, sizeof(bist_done)); + ili_ice_mode_read(SLAVE_READ_BACK_WORD_REG, &bist_slave_done, sizeof(bist_slave_done)); + + ili_ice_mode_write(READ_BACK_LOCK_REG, 0x01, 1); + ili_ice_mode_read(0x04600D, &bist_fail, sizeof(bist_fail)); + ili_ice_mode_read(SLAVE_READ_BACK_WORD_REG, &bist_slave_fail, sizeof(bist_slave_fail)); + + if (ilits->sram_para.check_failbus == ENABLE) { + ili_ice_mode_write(READ_BACK_LOCK_REG, 0x01, 1); + ili_ice_mode_read(0x046010, &bist_fail_bus, sizeof(bist_fail_bus)); + ili_ice_mode_read(SLAVE_READ_BACK_WORD_REG, &bist_slave_fail_bus, sizeof(bist_slave_fail_bus)); + + ili_ice_mode_write(READ_BACK_LOCK_REG, 0x01, 1); + ili_ice_mode_read(0x046048, &pen_bist_fail_bus, sizeof(pen_bist_fail_bus)); + ili_ice_mode_read(SLAVE_READ_BACK_WORD_REG, &pen_bist_slave_fail_bus, sizeof(pen_bist_slave_fail_bus)); + + if (((bist_done & BIT(0)) != 0x00) || ((bist_fail & BIT(0)) != 0x01) || (bist_fail_bus != 0xFFFFFFFF) + || (((bist_slave_done & BIT(24)) >> 24) != 0x00) || (((bist_slave_fail & BIT(24)) >> 24) != 0x01) + || (bist_slave_fail_bus != 0xFFFFFFFF)) { + + ILI_ERR("SRAM TEST FAIL\n"); + ILI_ERR("bist_done = 0x%X, bist_fail = 0x%X, bist_fail_bus = 0x%X\n", bist_done, bist_fail, bist_fail_bus); + ILI_ERR("bist_slave_done = 0x%X, bist_slave_fail = 0x%X, bist_slave_fail_bus = 0x%X\n", bist_slave_done, bist_slave_fail, bist_slave_fail_bus); + ILI_ERR("pen_bist_fail_bus = 0x%X, pen_bist_slave_fail_bus = 0x%X\n", pen_bist_fail_bus, pen_bist_slave_fail_bus); + ret = -1; + goto out; + } + } else { + if (((bist_done & BIT(0)) != 0x00) || ((bist_fail & BIT(0)) != 0x01) || (((bist_slave_done & BIT(24) >> 24)) != 0x00) + || (((bist_slave_fail & BIT(24)) >> 24) != 0x01)) { + + ILI_ERR("SRAM TEST FAIL\n"); + ILI_ERR("bist_done = 0x%X, bist_fail = 0x%X\n", bist_done, bist_fail); + ILI_ERR("bist_slave_done = 0x%X, bist_slave_fail = 0x%X\n", bist_slave_done, bist_slave_fail); + ret = -1; + goto out; + } + } + + ili_ice_mode_write(0x046000, 0x00000001, 4); + ili_ice_mode_write(0x046000, 0x00000101, 4); + + while (retry > 0) { + mdelay(10); + ili_ice_mode_write(READ_BACK_LOCK_REG, 0x01, 1); + ili_ice_mode_read(0x04600C, &bist_done, sizeof(bist_done)); + ili_ice_mode_read(SLAVE_READ_BACK_WORD_REG, &bist_slave_done, sizeof(bist_slave_done)); + ILI_DBG("bist_done = 0x%X, bist_slave_done = 0x%X\n", bist_done, bist_slave_done); + + if (((bist_done & BIT(0)) == 0x01) && (((bist_slave_done & BIT(24)) >> 24) == 0x01)) { + break; + } + retry--; + } + + if (retry <= 0) { + ILI_DBG("SRAM TEST FAIL\n"); + ret = -1; + goto out; + } + + ili_ice_mode_write(READ_BACK_LOCK_REG, 0x01, 1); + ili_ice_mode_read(0x04600D, &bist_fail, sizeof(bist_fail)); + ili_ice_mode_read(SLAVE_READ_BACK_WORD_REG, &bist_slave_fail, sizeof(bist_slave_fail)); + + if (ilits->sram_para.check_failbus == ENABLE) { + ili_ice_mode_write(READ_BACK_LOCK_REG, 0x01, 1); + ili_ice_mode_read(0x046010, &bist_fail_bus, sizeof(bist_fail_bus)); + ili_ice_mode_read(SLAVE_READ_BACK_WORD_REG, &bist_slave_fail_bus, sizeof(bist_slave_fail_bus)); + + ili_ice_mode_write(READ_BACK_LOCK_REG, 0x01, 1); + ili_ice_mode_read(0x046048, &pen_bist_fail_bus, sizeof(pen_bist_fail_bus)); + ili_ice_mode_read(SLAVE_READ_BACK_WORD_REG, &pen_bist_slave_fail_bus, sizeof(pen_bist_slave_fail_bus)); + + if (((bist_fail & BIT(0)) != 0x00) || (bist_fail_bus != 0x00000000) || (((bist_slave_fail & BIT(24)) >> 24) != 0x00) + || (bist_slave_fail_bus != 0x00000000)) { + + ILI_ERR("SRAM TEST FAIL\n"); + ILI_ERR("bist_fail = 0x%X, bist_fail_bus = 0x%X\n", bist_fail, bist_fail_bus); + ILI_ERR("bist_slave_fail = 0x%X, bist_slave_fail_bus = 0x%X\n", bist_slave_fail, bist_slave_fail_bus); + ILI_ERR("pen_bist_fail_bus = 0x%X, pen_bist_slave_fail_bus = 0x%X\n", pen_bist_fail_bus, pen_bist_slave_fail_bus); + + ret = -1; + goto out; + } + } else { + if (((bist_fail & BIT(0)) != 0x00) || (((bist_slave_fail & BIT(24)) >> 24) != 0x00)) { + ILI_ERR("SRAM TEST FAIL\n"); + ILI_ERR("bist_fail = 0x%X, bist_slave_fail = 0x%X\n", bist_fail, bist_slave_fail); + + ret = -1; + goto out; + } + } + +#endif +out: + return ret; +} + +int ili_tddi_ic_sram_test() +{ + int ret = 0; + + ili_ice_mode_write_by_mode(0x047003, 0x00, 1, BOTH); + ili_ice_mode_write_by_mode(0x04001C, 0x01, 1, BOTH); + ili_ice_mode_write_by_mode(0x071001, 0x02, 1, BOTH); + ili_ice_mode_write_by_mode(0x071005, 0x3C, 1, BOTH); + ili_ice_mode_write_by_mode(0x046003, 0x00, 1, BOTH); + ili_ice_mode_write_by_mode(0x046000, 0x010001, 3, BOTH); + ili_ice_mode_write_by_mode(0x046002, 0x00, 1, BOTH); + + if (ilits->sram_para.sram_group_en == ENABLE) { + ret = ili_sram_by_partial_area_test(); + goto out; + } + + ili_sram_test(ilits->sram_para.MaxAddr, ilits->sram_para.MinAddr); + + if (ilits->cascade_info_block.nNum != 0) { + ret = ili_ic_cascade_polling_bist(); + } else { + ret = ili_check_polling_bist(false, false); + } +out: +#if (TDDI_INTERFACE == BUS_SPI) + if (ilits->cascade_info_block.nNum != 0) { + ili_core_spi_setup(SPI_CLK); + } +#endif + return ret; +} + +#ifdef ROI +int ili_config_knuckle_roi_ctrl(cmd_types cmd) +{ + int ret = 0; + switch (cmd) { + case CMD_DISABLE: + ILI_DBG("knuckle roi disbale\n"); + ret = ili_ic_func_ctrl("knuckle", cmd); + break; + case CMD_ENABLE: + ILI_DBG("knuckle roi enable\n"); + ret = ili_ic_func_ctrl("knuckle", cmd); + break; + case CMD_STATUS: + ILI_DBG("knuckle roi read status\n"); + ret = ili_ic_func_ctrl("knuckle", cmd); + break; + case CMD_ROI_DATA: + ILI_DBG("knuckle roi read data\n"); + ret = ili_ic_func_ctrl("knuckle", cmd); + break; + default: + ILI_ERR("knuckle roi unknown cmd: %d\n", cmd); + return -EINVAL; + } + return ret; +} + +int ili_config_get_knuckle_roi_status(void) +{ + int ret = 0; + u8 roi_status = 0; + + ret = ili_config_knuckle_roi_ctrl(CMD_STATUS); + if (ret) { + ILI_ERR("config to read roi status failed\n"); + return ret; + } + + msleep(1); + ret = ilits->wrapper(NULL, 0, &roi_status, sizeof(roi_status), OFF, OFF); + if (ret) { + ILI_ERR("read data failed\n"); + return ret; + } + + ILI_INFO("roi %s\n", roi_status ? "enable" : "disable"); + + return roi_status; +} +#endif + +static int ilitek_tddi_ic_check_info(u32 pid, u16 id) +{ + ILI_INFO("ILITEK CHIP found.\n"); + + ilits->chip->pid = pid; + + ilits->chip->reset_key = TDDI_WHOLE_CHIP_RST_WITH_FLASH_KEY; + ilits->chip->wtd_key = 0x9881; + + if (((pid & 0xFFFFFF00) == ILI9881N_AA) || ((pid & 0xFFFFFF00) == ILI9881O_AA)) { + ilits->chip->dma_reset = ENABLE; + } else { + ilits->chip->dma_reset = DISABLE; + } + + ilits->chip->no_bk_shift = RAWDATA_NO_BK_SHIFT; + ilits->chip->max_count = 0x1FFFF; + return 0; +} + +int ili_ice_mode_bit_mask_write(u32 addr, u32 mask, u32 value) +{ + int ret = 0; + u32 data = 0; + + if (ili_ice_mode_read(addr, &data, sizeof(u32)) < 0) { + ILI_ERR("Read data error\n"); + return -1; + } + + data &= (~mask); + data |= (value & mask); + + ILI_DBG("mask value data = %x\n", data); + + ret = ili_ice_mode_write(addr, data, sizeof(u32)); + if (ret < 0) + ILI_ERR("Failed to re-write data in ICE mode, ret = %d\n", ret); + + return ret; +} + +int ili_ice_mode_write(u32 addr, u32 data, int len) +{ + int ret = 0, i; + u8 txbuf[64] = {0}; + + if (!atomic_read(&ilits->ice_stat)) { + ILI_ERR("ice mode not enabled\n"); + return -1; + } + + txbuf[0] = 0x25; + txbuf[1] = (char)((addr & 0x000000FF) >> 0); + txbuf[2] = (char)((addr & 0x0000FF00) >> 8); + txbuf[3] = (char)((addr & 0x00FF0000) >> 16); + + for (i = 0; i < len; i++) + txbuf[i + 4] = (char)(data >> (8 * i)); + + ret = ilits->wrapper(txbuf, len + 4, NULL, 0, OFF, OFF); + if (ret < 0) + ILI_ERR("Failed to write data in ice mode, ret = %d\n", ret); + + return ret; +} + +int ili_ice_mode_read(u32 addr, u32 *data, int len) +{ + int ret = 0; + u8 rxbuf[4] = {0}; + u8 txbuf[4] = {0}; + + if (len > sizeof(u32)) { + ILI_ERR("ice mode read lenght = %d, must less than or equal to 4 bytes\n", len); + len = 4; + } + + if (!atomic_read(&ilits->ice_stat)) { + ILI_ERR("ice mode not enabled\n"); + return -1; + } + + txbuf[0] = 0x25; + txbuf[1] = (char)((addr & 0x000000FF) >> 0); + txbuf[2] = (char)((addr & 0x0000FF00) >> 8); + txbuf[3] = (char)((addr & 0x00FF0000) >> 16); + + ret = ilits->wrapper(txbuf, sizeof(txbuf), NULL, 0, OFF, OFF); + if (ret < 0) + goto out; + + ret = ilits->wrapper(NULL, 0, rxbuf, len, OFF, OFF); + if (ret < 0) + goto out; + + *data = 0; + if (len == 1) + *data = rxbuf[0]; + else if (len == 2) + *data = (rxbuf[0] | rxbuf[1] << 8); + else if (len == 3) + *data = (rxbuf[0] | rxbuf[1] << 8 | rxbuf[2] << 16); + else + *data = (rxbuf[0] | rxbuf[1] << 8 | rxbuf[2] << 16 | rxbuf[3] << 24); + +out: + if (ret < 0) + ILI_ERR("Failed to read data in ice mode, ret = %d\n", ret); + + return ret; +} + +void ili_cascade_sync_ctrl(bool mode) +{ + bool stop = false, backup_addr = MASTER; + u8 cmd[2] = {0}; + + + backup_addr = ilits->i2c_addr_change; + ilits->i2c_addr_change = MASTER; + + if (mode == stop) { + cmd[0] = 0x10; + cmd[1] = 0x10; + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Failed to stop Sync Cascade\n"); + } else { + atomic_set(&ilits->sync_stat, ENABLE); + ILI_DBG("Stop Sync Cascade, Set atomic sync stat = %d\n", atomic_read(&ilits->sync_stat)); + } + + mdelay(1); + } else { + cmd[0] = 0x10; + cmd[1] = 0x11; + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Failed to start Sync Cascade\n"); + } else { + atomic_set(&ilits->sync_stat, DISABLE); + ILI_DBG("Start Sync Cascade, Set atomic sync stat = %d\n", atomic_read(&ilits->sync_stat)); + } + } + ilits->i2c_addr_change = backup_addr; +} + +int ili_ice_mode_ctrl(bool enable, bool mcu) +{ + int ret = 0; + u8 cmd_open[4] = {0x25, 0x62, 0x10, 0x18}; + u8 cmd_close[4] = {0x1B, 0x62, 0x10, 0x18}; + + if (enable) { + if (atomic_read(&ilits->ice_stat)) { + ILI_INFO("ice mode already enabled\n"); + return 0; + } + + /* Cascade Stop Sync */ +#if (TDDI_INTERFACE == BUS_I2C && ENABLE_CASCADE) + if (ilits->cascade_ctrl_mode == BOTH) { + if (ilits->i2c_addr_change == SLAVE && atomic_read(&ilits->sync_stat) == DISABLE) { + ili_cascade_sync_ctrl(false); + } + } else { + if (atomic_read(&ilits->sync_stat) == DISABLE) { + ili_cascade_sync_ctrl(false); + } + } +#elif (TDDI_INTERFACE == BUS_SPI && ENABLE_CASCADE) + if (atomic_read(&ilits->sync_stat) == DISABLE) { + ili_cascade_sync_ctrl(false); + } +#endif + + if (mcu) + cmd_open[0] = 0x1F; + + atomic_set(&ilits->ice_stat, ENABLE); + + if (ilits->wrapper(cmd_open, sizeof(cmd_open), NULL, 0, OFF, OFF) < 0) { + ILI_ERR("write ice mode cmd error\n"); + atomic_set(&ilits->ice_stat, DISABLE); + } + ilits->pll_clk_wakeup = false; + +#if (TDDI_INTERFACE == BUS_I2C) + if (ilits->ice_flash_cs_ctrl && !mcu) { + if (ili_ice_mode_write(FLASH_BASED_ADDR, 0x1, 1) < 0) + ILI_ERR("Write cs high failed\n"); /* CS high */ + if (ili_ice_mode_write(FLASH0_dual_mode, 0x0, 1) < 0) + ILI_ERR("Write daul mode close failed\n"); /* daul mode close */ + } else { + ILI_INFO("ignore ice mode flash cs control\n"); + } +#endif + } else { + if (!atomic_read(&ilits->ice_stat)) { + ILI_INFO("ice mode already disabled\n"); + return 0; + } +#if (TDDI_INTERFACE == BUS_SPI && ENABLE_CASCADE) + /* Slave exit ice mode */ + if (atomic_read(&ilits->slave_ice_stat)) { + if (ili_mspi_write_slave_register(CONTROL_SLAVE_EXIT_ICE_ADDR, 0, 0) < 0) { + ILI_ERR("Disable Slave ICE mode Fail\n"); + } else { + atomic_set(&ilits->slave_ice_stat, DISABLE); + ILI_INFO("Set slave ice state atomic : %d, Disable Slave ICE mode\n", atomic_read(&ilits->slave_ice_stat)); + } + } +#endif + ret = ilits->wrapper(cmd_close, sizeof(cmd_close), NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Exit to ICE Mode failed !!\n"); + atomic_set(&ilits->ice_stat, ENABLE); + } else { + atomic_set(&ilits->ice_stat, DISABLE); +#if (!ENABLE_CASCADE) + ilits->pll_clk_wakeup = true; +#endif + atomic_set(&ilits->ignore_report, END); + } + + /* Cascade Start Sync */ +#if (TDDI_INTERFACE == BUS_I2C && ENABLE_CASCADE) + if (ilits->cascade_ctrl_mode == BOTH) { + if (ilits->i2c_addr_change == MASTER && atomic_read(&ilits->sync_stat) == ENABLE) { + ilits->pll_clk_wakeup = true; + ili_cascade_sync_ctrl(true); + } + } else { + if (atomic_read(&ilits->sync_stat) == ENABLE) { + ilits->pll_clk_wakeup = true; + ili_cascade_sync_ctrl(true); + } + } +#elif (TDDI_INTERFACE == BUS_SPI && ENABLE_CASCADE) + if (atomic_read(&ilits->sync_stat) == ENABLE) { + ilits->pll_clk_wakeup = true; + ili_cascade_sync_ctrl(true); + } +#endif + } + ILI_INFO("%s ICE mode, mcu on = %d\n", (enable ? "Enable" : "Disable"), mcu); + + return ret; +} + +int ili_ice_mode_ctrl_by_mode(bool enable, bool mcu, int mode) +{ + int ret = 0; +#if (TDDI_INTERFACE == BUS_I2C && ENABLE_CASCADE) + ilits->cascade_ctrl_mode = mode; + switch (mode) { + case SLAVE: + case BOTH: + ilits->i2c_addr_change = SLAVE; + if (ili_ice_mode_ctrl(enable, mcu) < 0) { + ILI_ERR("Failed to write Slave ice mode\n"); + ret = -1; + } else { + ILI_DBG("Slave %s ice mode\n", enable ? "Enable" : "Disable"); + } + if (mode == SLAVE) { + ilits->i2c_addr_change = MASTER; + break; + } else { + if (enable == ENABLE) + atomic_set(&ilits->ice_stat, DISABLE); + else + atomic_set(&ilits->ice_stat, ENABLE); + } + case MASTER: + ilits->i2c_addr_change = MASTER; + if (ili_ice_mode_ctrl(enable, mcu) < 0) { + ILI_ERR("Failed to write Master ice mode\n"); + ret = -1; + } else { + ILI_DBG("Master %s ice mode\n", enable ? "Enable" : "Disable"); + } + break; + default: + break; + } + ilits->cascade_ctrl_mode = MASTER; +#else + if (ili_ice_mode_ctrl(enable, mcu) < 0) { + ILI_ERR("Failed to write ice mode\n"); + } +#endif + return ret; +} + +int ili_ice_mode_write_by_mode(u32 addr, u32 data, int len, int mode) +{ + int ret = 1; +#if (TDDI_INTERFACE == BUS_I2C && ENABLE_CASCADE) + switch (mode) { + case SLAVE: + case BOTH: + ilits->i2c_addr_change = SLAVE; + if (ili_ice_mode_write(addr, data, len) < 0) { + ILI_ERR("Failed to write Slave, addr: 0x%X\n", addr); + ret = -1; + } + if (mode == SLAVE) { + ilits->i2c_addr_change = MASTER; + break; + } + case MASTER: + ilits->i2c_addr_change = MASTER; + if (ili_ice_mode_write(addr, data, len) < 0) { + ILI_ERR("Failed to write Master, addr: 0x%X\n", addr); + ret = -1; + } + break; + default: + break; + } +#else + if (ili_ice_mode_write(addr, data, len) < 0) { + ILI_ERR("Failed to write addr: 0x%X\n", addr); + ret = -1; + } +#endif + return ret; +} + +int ili_ice_mode_read_by_mode(u32 addr, u32 *data, int len, int mode) +{ + int ret = 1; +#if (TDDI_INTERFACE == BUS_I2C && ENABLE_CASCADE) + switch (mode) { + case SLAVE: + case BOTH: + ilits->i2c_addr_change = SLAVE; + if (ili_ice_mode_read(addr, data, len) < 0) { + ILI_ERR("Failed to read Slave, addr: 0x%x\n", addr); + ret = -1; + } else { + ILI_DBG("Slave [READ]:addr = 0x%06x, read = 0x%08x\n", addr, *data); + } + if (mode == SLAVE) { + ilits->i2c_addr_change = MASTER; + break; + } + case MASTER: + ilits->i2c_addr_change = MASTER; + if (ili_ice_mode_read(addr, data, len) < 0) { + ILI_ERR("Failed to read Master, addr: 0x%x\n", addr); + ret = -1; + } else { + ILI_DBG("Master [READ]:addr = 0x%06x, read = 0x%08x\n", addr, *data); + } + break; + default: + break; + } +#else + if (ili_ice_mode_read(addr, data, len) < 0) { + ILI_ERR("Failed to write addr: 0x%x\n", addr); + ret = -1; + } else { + ILI_DBG("[READ]:addr = 0x%06x, read = 0x%08x\n", addr, *data); + } +#endif + return ret; +} + +static void ili_mspi_init(void) +{ + if (ili_ice_mode_write(MSPI_REG, 0x01, 1) < 0) + ILI_ERR("Write MSPI_REG failed\n"); + + if (ili_ice_mode_write(MSPI_TRIG_BY_DMA_ADDR, 0x101, 2) < 0) + ILI_ERR("Write MSPI_TRIG_BY_DMA_ADDR failed\n"); + + if (ili_ice_mode_write(MSPI_CLOCK_ADDR, 0x02, 1) < 0) + ILI_ERR("Write MSPI_CLOCK_ADDR failed\n"); + + if (ili_ice_mode_write(MSPI_CS_ADDR, 0x01, 1) < 0) + ILI_ERR("Write MSPI_CS_ADDR failed\n"); +} + +static void ili_dma_write_setting(void) +{ + if (ili_ice_mode_write(DMA_ONE_CLEAR_ADDR, 0x02, 1) < 0) + ILI_ERR("Write DMA_ONE_CLEAR_ADDR failed\n"); + + if (ili_ice_mode_write(SRC_ONE_ADDR, 0x308D0, 3) < 0) + ILI_ERR("Write SRC_ONE_ADDR failed\n"); + + if (ili_ice_mode_write(SRC_ONE_SET_ADDR, 0x82000004, sizeof(u32)) < 0) + ILI_ERR("Write SRC_ONE_SET_ADDR failed\n"); + + if (ili_ice_mode_write(SRC_TWO_EN_ADDR, 0x00, 1) < 0) + ILI_ERR("Write SRC_TWO_EN_ADDR failed\n"); + + if (ili_ice_mode_write(DEST_ADDR, 0x6205C, 3) < 0) + ILI_ERR("Write DEST_ADDR failed\n"); + + if (ili_ice_mode_write(DEST_SET_ADDR, 0x82000000, sizeof(u32)) < 0) + ILI_ERR("Write DEST_SET_ADDR failed\n"); + + if (ili_ice_mode_write(TRIG_SEL_MSPI_ADDR, 0x0D, 1) < 0) + ILI_ERR("Write TRIG_SEL_MSPI_ADDR failed\n"); + + if (ili_ice_mode_write(DMA_ONE_GROUP_ADDR, 0x00, 1) < 0) + ILI_ERR("Write DMA_ONE_GROUP_ADDR failed\n"); +} + +static void ili_dma_read_setting(void) +{ + if (ili_ice_mode_write(DMA_ONE_CLEAR_ADDR, 0x02, 1) < 0) + ILI_ERR("Write DMA_ONE_CLEAR_ADDR failed\n"); + + if (ili_ice_mode_write(SRC_ONE_ADDR, 0x62060, 3) < 0) + ILI_ERR("Write SRC_ONE_ADDR failed\n"); + + if (ili_ice_mode_write(SRC_ONE_SET_ADDR, 0x80000000, sizeof(u32)) < 0) + ILI_ERR("Write SRC_ONE_SET_ADDR failed\n"); + + if (ili_ice_mode_write(SRC_TWO_EN_ADDR, 0x00, 1) < 0) + ILI_ERR("Write SRC_TWO_EN_ADDR failed\n"); + + if (ili_ice_mode_write(DEST_ADDR, 0x308D0, 3) < 0) + ILI_ERR("Write DEST_ADDR failed\n"); + + if (ili_ice_mode_write(DEST_SET_ADDR, 0x80000001, sizeof(u32)) < 0) + ILI_ERR("Write DEST_SET_ADDR failed\n"); + + if (ili_ice_mode_write(TRIG_SEL_MSPI_ADDR, 0x0E, 1) < 0) + ILI_ERR("Write DEST_SET_ADDR failed\n"); + + if (ili_ice_mode_write(DMA_ONE_GROUP_ADDR, 0x00, 1) < 0) + ILI_ERR("Write DMA_ONE_GROUP_ADDR failed\n"); +} + +static void ili_trigger_mspi(void) +{ + u8 BusyCnt = 50; + u32 BusyData; + + if (ili_ice_mode_write(MSPI_CS_ADDR, 0x00, 1) < 0) + ILI_ERR("Write MSPI_CS_ADDR failed\n"); + + if (ili_ice_mode_write(MSPI_REG, 0x0F, 1) < 0) + ILI_ERR("Write MSPI_REG failed\n"); + + if (ili_ice_mode_write(MSPI_TRIGGER_ADDR, 0x01, 1) < 0) + ILI_ERR("Write WRITE_BUF_ADDR failed\n"); + + /* Polling Done Flag */ + while (BusyCnt > 0) { + if (ili_ice_mode_read(MSPI_DONE_FLAG_ADDR, &BusyData, 1) < 0) + ILI_ERR("Read MSPI_DONE_FLAG_ADDR failed\n"); + + if ( (BusyData & BIT(0)) == 0x01) { + break; + } else { + ILI_ERR("MSPI Busy : 0x%X\n", BusyData); + } + + mdelay(1); + BusyCnt--; + } + + if (ili_ice_mode_write(MSPI_DONE_FLAG_ADDR, 0x01, 1) < 0) + ILI_ERR("Read MSPI_DONE_FLAG_ADDR failed\n"); + + if (ili_ice_mode_write(MSPI_CS_ADDR, 0x01, 1) < 0) + ILI_ERR("Write MSPI_CS_ADDR failed\n"); +} + +void ili_mspi_set_slave_to_qual_mode(void) +{ + u32 read_data, backup_mspi_init_en; + + ili_mspi_init(); + + if (ili_ice_mode_read(MSPI_INIT_EN_ADDR, &backup_mspi_init_en, 1) < 0) + ILI_ERR("Read MSPI_INIT_EN_ADDR failed\n"); + + if (ili_ice_mode_write(MSPI_INIT_EN_ADDR, backup_mspi_init_en & 0xFE, 1) < 0) + ILI_ERR("Write MSPI_INIT_EN_ADDR failed\n"); + + ili_dma_write_setting(); + + /* Set Slave to Qual Mode */ + if (ili_ice_mode_read(TRANSFER_CNT_ADDR, &read_data, sizeof(u32)) < 0) + ILI_ERR("Read TRANSFER_CNT_ADDR failed\n"); + + read_data = (read_data & 0xFFFC0000) + 0x18; + + if (ili_ice_mode_write(TRANSFER_CNT_ADDR, read_data, sizeof(u32)) < 0) + ILI_ERR("Write TRANSFER_CNT_ADDR failed\n"); + + if (ili_ice_mode_read(WRITE_BUF_SIZE_ADDR, &read_data, 2) < 0) + ILI_ERR("Read WRITE_BUF_SIZE_ADDR failed\n"); + + read_data = (read_data & 0xF800) + 0x18; + + if (ili_ice_mode_write(WRITE_BUF_SIZE_ADDR, read_data, 2) < 0) + ILI_ERR("Write WRITE_BUF_SIZE_ADDR failed\n"); + + if (ili_ice_mode_read(READ_BUF_SIZE_ADDR, &read_data, 2) < 0) + ILI_ERR("Read READ_BUF_SIZE_ADDR failed\n"); + + read_data = (read_data & 0xF800) + 0x00; + + if (ili_ice_mode_write(READ_BUF_SIZE_ADDR, read_data, 2) < 0) + ILI_ERR("Write READ_BUF_SIZE_ADDR failed\n"); + + if (ili_ice_mode_write(WRITE_BUF_ADDR, 0x40000040, sizeof(u32)) < 0) + ILI_ERR("Write WRITE_BUF_ADDR failed\n"); + + if (ili_ice_mode_write(0x308D0, 0x4044000, sizeof(u32)) < 0) + ILI_ERR("Write failed\n"); + + if (ili_ice_mode_write(0x308D4, 0x4000, sizeof(u32)) < 0) + ILI_ERR("Write failed\n"); + + if (ili_ice_mode_write(0x308D8, 0x4000, sizeof(u32)) < 0) + ILI_ERR("Write failed\n"); + + if (ili_ice_mode_write(0x308DC, 0x40040000, sizeof(u32)) < 0) + ILI_ERR("Write failed\n"); + + if (ili_ice_mode_write(0x308E0, 0x40000000, sizeof(u32)) < 0) + ILI_ERR("Write failed\n"); + + ili_trigger_mspi(); + + /* Recovery Mspi int en */ + if (ili_ice_mode_write(MSPI_INIT_EN_ADDR, backup_mspi_init_en, 1) < 0) + ILI_ERR("Write MSPI_INIT_EN_ADDR failed\n"); + + ILI_INFO("Backup MSPI Init EN : 0x%X\n", backup_mspi_init_en); +} + +int ili_mspi_write_slave_register(u32 addr, u32 data, int len) +{ + int ret = 0; + u32 backup_mspi_init_en, write_data; + + ili_mspi_init(); + + if (ili_ice_mode_read(MSPI_INIT_EN_ADDR, &backup_mspi_init_en, 1) < 0) + ILI_ERR("Read MSPI_INIT_EN_ADDR failed\n"); + + if (ili_ice_mode_write(MSPI_INIT_EN_ADDR, backup_mspi_init_en & 0xFE, 1) < 0) + ILI_ERR("Write MSPI_INIT_EN_ADDR failed\n"); + + ili_dma_write_setting(); + + if (ili_ice_mode_read(TRANSFER_CNT_ADDR, &write_data, sizeof(u32)) < 0) + ILI_ERR("Read TRANSFER_CNT_ADDR failed\n"); + + write_data = (write_data & 0xFFFC0000) + 5 + len; + + if (ili_ice_mode_write(TRANSFER_CNT_ADDR, write_data, sizeof(u32)) < 0) + ILI_ERR("Write TRANSFER_CNT_ADDR failed\n"); + + if (ili_ice_mode_read(WRITE_BUF_SIZE_ADDR, &write_data, 2) < 0) + ILI_ERR("Read WRITE_BUF_SIZE_ADDR failed\n"); + + write_data = (write_data & 0xF800) + 5 + len; + + if (ili_ice_mode_write(WRITE_BUF_SIZE_ADDR, write_data, 2) < 0) + ILI_ERR("Write WRITE_BUF_SIZE_ADDR failed\n"); + + if (ili_ice_mode_read(READ_BUF_SIZE_ADDR, &write_data, 2) < 0) + ILI_ERR("Read READ_BUF_SIZE_ADDR failed\n"); + + write_data = write_data & 0xF800; + + if (ili_ice_mode_write(READ_BUF_SIZE_ADDR, write_data, 2) < 0) + ILI_ERR("Write READ_BUF_SIZE_ADDR failed\n"); + + if (addr == CONTROL_SLAVE_EXIT_ICE_ADDR) { + write_data = ((addr & 0xFF00) << 16) + ((addr & 0xFF) << 16) + (0x1B << 8) + 0x82; + } else { + write_data = ((addr & 0xFF00) << 16) + ((addr & 0xFF) << 16) + (0x25 << 8) + 0x82; + } + + if (ili_ice_mode_write(WRITE_BUF_ADDR, write_data, sizeof(u32)) < 0) + ILI_ERR("Write WRITE_BUF_ADDR failed\n"); + + write_data = ((data & 0xFF0000) << 8) + ((data & 0xFF00) << 8) + ((data & 0xFF) << 8) + ((addr & 0xFF0000) >> 16); + + if (ili_ice_mode_write(0x308D0, write_data, sizeof(u32)) < 0) { + ILI_ERR("Write failed\n"); + ret = -1; + } + + write_data = (data & 0xFF000000) >> 24; + + if (ili_ice_mode_write(0x308D4, write_data, sizeof(u32)) < 0) { + ILI_ERR("Write failed\n"); + ret = -1; + } + + ili_trigger_mspi(); + + /* Recovery Mspi int en */ + if (ili_ice_mode_write(MSPI_INIT_EN_ADDR, backup_mspi_init_en, 1) < 0) + ILI_ERR("Write MSPI_INIT_EN_ADDR failed\n"); + + return ret; +} + +void ili_mspi_read_slave_register(u32 addr, u32 *data, int len) +{ + u32 read_data, backup_mspi_init_en; + + ili_mspi_write_slave_register(addr, 0, 0); + + if (ili_ice_mode_read(MSPI_INIT_EN_ADDR, &backup_mspi_init_en, 1) < 0) + ILI_ERR("Read MSPI_INIT_EN_ADDR failed\n"); + + + if (ili_ice_mode_write(MSPI_INIT_EN_ADDR, backup_mspi_init_en & 0xFE, 1) < 0) + ILI_ERR("Write MSPI_INIT_EN_ADDR failed\n"); + + ili_dma_read_setting(); + + if (ili_ice_mode_write(WRITE_BUF_ADDR, 0x83, 1) < 0) + ILI_ERR("Write WRITE_BUF_ADDR failed\n"); + + if (ili_ice_mode_read(TRANSFER_CNT_ADDR, &read_data, sizeof(u32)) < 0) + ILI_ERR("Read TRANSFER_CNT_ADDR failed\n"); + + read_data = (read_data & 0xFFFC0000) + len; + + if (ili_ice_mode_write(TRANSFER_CNT_ADDR, read_data, sizeof(u32)) < 0) + ILI_ERR("Write TRANSFER_CNT_ADDR failed\n"); + + if (ili_ice_mode_read(WRITE_BUF_SIZE_ADDR, &read_data, 2) < 0) + ILI_ERR("Read WRITE_BUF_SIZE_ADDR failed\n"); + + read_data = (read_data & 0xF800) + 0x01; + + if (ili_ice_mode_write(WRITE_BUF_SIZE_ADDR, read_data, 2) < 0) + ILI_ERR("Write WRITE_BUF_SIZE_ADDR failed\n"); + + if (ili_ice_mode_read(READ_BUF_SIZE_ADDR, &read_data, 2) < 0) + ILI_ERR("Read READ_BUF_SIZE_ADDR failed\n"); + + read_data = (read_data & 0xF800) + len; + + if (ili_ice_mode_write(READ_BUF_SIZE_ADDR, read_data, 2) < 0) + ILI_ERR("Write READ_BUF_SIZE_ADDR failed\n"); + + ili_trigger_mspi(); + + /* Recovery Mspi int en */ + if (ili_ice_mode_write(MSPI_INIT_EN_ADDR, backup_mspi_init_en, 1) < 0) + ILI_ERR("Write MSPI_INIT_EN_ADDR failed\n"); + + if (ili_ice_mode_read(0x308D0, data, len) < 0) + ILI_ERR("Read 0x308D0 failed\n"); +} + +int ili_set_bypass_mode(bool mcu) +{ +#if (TDDI_INTERFACE == BUS_SPI) + if (atomic_read(&ilits->ice_stat)) { + ILI_INFO("ice mode already enabled\n"); + return 0; + } + + ili_ice_mode_ctrl_by_mode(ENABLE, mcu, MASTER); + + /* set mspi 0 */ + if (ili_ice_mode_write_by_mode(MSPI_REG, 0x00, 1, MASTER) < 0) { + ILI_ERR("Failed to write MSPI_REG in ice mode\n"); + } + + /* set s_to_q bit[1] = 1 */ + ili_ice_mode_write(SINGLE_TO_QUAL_REG, 0x02, 1); + + /* set slave ic to ice mode */ + atomic_set(&ilits->ice_stat, DISABLE); + + atomic_set(&ilits->slave_ice_stat, ENABLE); + ILI_INFO("Set slave ice state atomic : %d\n", atomic_read(&ilits->slave_ice_stat)); + + ili_ice_mode_ctrl_by_mode(ENABLE, mcu, SLAVE); + + ILI_INFO("Set ByPass Mode\n"); +#endif + return 0; +} + +void ili_wdt_reset_status( bool enter_ice, bool exit_ice) +{ +#if (TDDI_INTERFACE == BUS_I2C) + if (enter_ice) { + if (atomic_read(&ilits->ice_stat)) { + ILI_INFO("ice mode already enabled\n"); + } else { + ili_ice_mode_ctrl_by_mode(ENABLE, OFF, BOTH); + } + } +#else + if (enter_ice) { + if (atomic_read(&ilits->ice_stat)) { + ILI_INFO("ice mode already enabled\n"); + } else { + ili_set_bypass_mode(OFF); + } + } +#endif + if (ili_ice_mode_write_by_mode(WDT_ADDR, 0x01, 1, BOTH) > 0) { + ILI_INFO("Clear Master & Slave WDT Status\n"); + } + + if (exit_ice) { + if (!atomic_read(&ilits->ice_stat)) { + ILI_INFO("ice mode already disabled\n"); + } else { + ili_ice_mode_ctrl_by_mode(DISABLE, OFF, BOTH); + } + } +} + +void ili_ic_edge_palm_para_init(void) +{ +#if ENABLE_EDGE_PALM_PARA + int i; + + ilits->edge_palm_para[0] = P5_X_EDGE_PLAM_CTRL_1; + ilits->edge_palm_para[1] = P5_X_EDGE_PALM_TUNING_PARA; + ilits->edge_palm_para[2] = (ZONE_A_W & 0xFF00) >> 8; + ilits->edge_palm_para[3] = ZONE_A_W & 0xFF; + ilits->edge_palm_para[4] = (ZONE_B_W & 0xFF00) >> 8; + ilits->edge_palm_para[5] = ZONE_B_W & 0xFF; + ilits->edge_palm_para[6] = (ZONE_A_ROTATION_W & 0xFF00) >> 8; + ilits->edge_palm_para[7] = ZONE_A_ROTATION_W & 0xFF; + ilits->edge_palm_para[8] = (ZONE_B_ROTATION_W & 0xFF00) >> 8; + ilits->edge_palm_para[9] = ZONE_B_ROTATION_W & 0xFF; + ilits->edge_palm_para[10] = (ZONE_C_WIDTH & 0xFF00) >> 8; + ilits->edge_palm_para[11] = ZONE_C_WIDTH & 0xFF; + ilits->edge_palm_para[12] = (ZONE_C_HEIGHT & 0xFF00) >> 8; + ilits->edge_palm_para[13] = ZONE_C_HEIGHT & 0xFF; + ilits->edge_palm_para[14] = (ZONE_C_HEIGHT_LITTLE & 0xFF00) >> 8; + ilits->edge_palm_para[15] = ZONE_C_HEIGHT_LITTLE & 0xFF; + ilits->edge_palm_para[16] = (ZONE_A_W & 0xFF00) >> 8; + ilits->edge_palm_para[17] = ZONE_A_W & 0xFF; + ilits->edge_palm_para[18] = (ZONE_B_W & 0xFF00) >> 8; + ilits->edge_palm_para[19] = ZONE_B_W & 0xFF; + ilits->edge_palm_para[20] = (ZONE_A_ROTATION_W & 0xFF00) >> 8; + ilits->edge_palm_para[21] = ZONE_A_ROTATION_W & 0xFF; + ilits->edge_palm_para[22] = (ZONE_B_ROTATION_W & 0xFF00) >> 8; + ilits->edge_palm_para[23] = ZONE_B_ROTATION_W & 0xFF; + ilits->edge_palm_para[24] = (ZONE_C_WIDTH & 0xFF00) >> 8; + ilits->edge_palm_para[25] = ZONE_C_WIDTH & 0xFF; + ilits->edge_palm_para[26] = (ZONE_C_HEIGHT & 0xFF00) >> 8; + ilits->edge_palm_para[27] = ZONE_C_HEIGHT & 0xFF; + ilits->edge_palm_para[28] = (ZONE_C_HEIGHT_LITTLE & 0xFF00) >> 8; + ilits->edge_palm_para[29] = ZONE_C_HEIGHT_LITTLE & 0xFF; + ilits->edge_palm_para[30] = ili_calc_packet_checksum(ilits->edge_palm_para, P5_X_EDGE_PALM_PARA_LENGTH - 1); + + for (i = 0; i < P5_X_EDGE_PALM_PARA_LENGTH; i++) { + ILI_DBG("edge_palm_para[%d] = 0x%2x\n", i, ilits->edge_palm_para[i]); + } +#endif +} +void ili_ic_send_edge_palm_para(void) +{ +#if ENABLE_EDGE_PALM_PARA + int ret = 0; + + ILI_INFO("send edge palm para\n"); + + ret = ilits->wrapper(ilits->edge_palm_para, P5_X_EDGE_PALM_PARA_LENGTH, NULL, 0, OFF, OFF); + if (ret < 0) + ILI_ERR("Write edge palm para function failed\n"); +#endif +} + +int ili_ic_func_ctrl_export(const char *name, int ctrl) +{ + int i = 0, ret; + if (ERR_ALLOC_MEM(ilits)) { + ILI_ERR("Failed to allocate ts memory, ilits is NULL.\n"); + return -ENOMEM; + } + + mutex_lock(&ilits->touch_mutex); + for (i = 0; i < FUNC_CTRL_NUM; i++) { + if (ipio_strcmp(name, func_ctrl[i].name) == 0) { + if (strlen(name) != strlen(func_ctrl[i].name)) + continue; + break; + } + } + + if (i >= FUNC_CTRL_NUM) { + ILI_ERR("Not found function ctrl, %s\n", name); + ret = -1; + goto out; + } + + if (ilits->protocol->ver == PROTOCOL_VER_500) { + ILI_ERR("Non support function ctrl with protocol v5.0\n"); + ret = -1; + goto out; + } + + if (ilits->protocol->ver >= PROTOCOL_VER_560) { + if (ipio_strcmp(func_ctrl[i].name, "gesture") == 0 || + ipio_strcmp(func_ctrl[i].name, "phone_cover_window") == 0) { + ILI_INFO("Non support %s function ctrl\n", func_ctrl[i].name); + ret = -1; + goto out; + } + } + + func_ctrl[i].cmd[2] = ctrl; + + ILI_INFO("func = %s, len = %d, cmd = 0x%x, 0%x, 0x%x\n", func_ctrl[i].name, func_ctrl[i].len, + func_ctrl[i].cmd[0], func_ctrl[i].cmd[1], func_ctrl[i].cmd[2]); + + ret = ilits->wrapper(func_ctrl[i].cmd, func_ctrl[i].len, NULL, 0, OFF, OFF); + if (ret < 0) + ILI_ERR("Write TP function failed\n"); + + if (func_ctrl[i].rec_state < 2) { + if (ctrl == func_ctrl[i].def_cmd) + func_ctrl[i].rec_state = DISABLE; + else + func_ctrl[i].rec_state = ENABLE; + + func_ctrl[i].rec_cmd = ctrl; + } + + ILI_DBG("record %s func cmd %d, rec_state %d\n", func_ctrl[i].name, func_ctrl[i].rec_cmd, func_ctrl[i].rec_state); +out: + mutex_unlock(&ilits->touch_mutex); + return ret; +} +EXPORT_SYMBOL(ili_ic_func_ctrl_export); + + +int ili_ic_func_ctrl(const char *name, int ctrl) +{ + int i = 0, ret; + + for (i = 0; i < FUNC_CTRL_NUM; i++) { + if (ipio_strcmp(name, func_ctrl[i].name) == 0) { + if (strlen(name) != strlen(func_ctrl[i].name)) + continue; + break; + } + } + + if (i >= FUNC_CTRL_NUM) { + ILI_ERR("Not found function ctrl, %s\n", name); + ret = -1; + goto out; + } + + if (ilits->protocol->ver == PROTOCOL_VER_500) { + ILI_ERR("Non support function ctrl with protocol v5.0\n"); + ret = -1; + goto out; + } + + if (ilits->protocol->ver >= PROTOCOL_VER_560) { + if (ipio_strcmp(func_ctrl[i].name, "gesture") == 0 || + ipio_strcmp(func_ctrl[i].name, "phone_cover_window") == 0) { + ILI_INFO("Non support %s function ctrl\n", func_ctrl[i].name); + ret = -1; + goto out; + } + } + + if (ilits->chip->core_ver >= CORE_VER_1700) { + if (ipio_strcmp(func_ctrl[i].name, "sense") == 0) { + ILI_INFO("Non support %s function ctrl after core ver %x\n", func_ctrl[i].name, ilits->chip->core_ver); + ret = -1; + goto out; + } + } + + func_ctrl[i].cmd[2] = ctrl; + + ILI_INFO("func = %s, len = %d, cmd = 0x%x, 0x%x, 0x%x\n", func_ctrl[i].name, func_ctrl[i].len, + func_ctrl[i].cmd[0], func_ctrl[i].cmd[1], func_ctrl[i].cmd[2]); + + ret = ilits->wrapper(func_ctrl[i].cmd, func_ctrl[i].len, NULL, 0, OFF, OFF); + if (ret < 0) + ILI_ERR("Write TP function failed\n"); + + if (func_ctrl[i].rec_state < 2) { + if (ctrl == func_ctrl[i].def_cmd) + func_ctrl[i].rec_state = DISABLE; + else + func_ctrl[i].rec_state = ENABLE; + + func_ctrl[i].rec_cmd = ctrl; + } + + ILI_DBG("record %s func cmd %d, rec_state %d\n", func_ctrl[i].name, func_ctrl[i].rec_cmd, func_ctrl[i].rec_state); +out: + return ret; +} + +void ili_ic_func_ctrl_reset(void) +{ + int i = 0; + + ili_ic_send_edge_palm_para(); + + for (i = 0; i < FUNC_CTRL_NUM; i++) { + if (func_ctrl[i].rec_state == ENABLE) { + ILI_DBG("reset func ctrl %s, record status = %d, cmd = %d\n", func_ctrl[i].name, + func_ctrl[i].rec_state, func_ctrl[i].rec_cmd); + if (ili_ic_func_ctrl(func_ctrl[i].name, func_ctrl[i].rec_cmd) < 0) + ILI_ERR("reset ic func ctrl %s failed\n", func_ctrl[i].name); + } + } +} + +int ili_ic_code_reset(bool mcu) +{ + int ret; + bool ice = atomic_read(&ilits->ice_stat); + + if (!ice) { + if (ilits->cascade_info_block.nNum != 0) { +#if (TDDI_INTERFACE == BUS_I2C) + ili_ice_mode_ctrl_by_mode(ENABLE, mcu, BOTH); +#else + ili_set_bypass_mode(mcu); +#endif + } else { + if (ili_ice_mode_ctrl(ENABLE, mcu) < 0) + ILI_ERR("Enable ice mode failed before code reset\n"); + } + } + + ret = ili_ice_mode_write_by_mode(0x40040, 0xAE, 1, BOTH); + if (ret < 0) + ILI_ERR("ic code reset failed\n"); + + if (!ice) { + if (ilits->cascade_info_block.nNum != 0) { +#if (TDDI_INTERFACE == BUS_I2C) + ili_ice_mode_ctrl_by_mode(DISABLE, mcu, BOTH); +#else + ili_ice_mode_ctrl_by_mode(DISABLE, mcu, BOTH); +#endif + } else { + if (ili_ice_mode_ctrl(DISABLE, mcu) < 0) + ILI_ERR("Enable ice mode failed after code reset\n"); + } + } + return ret; +} + +void ili_get_dma1_config(struct ilitek_dma_config *dma) +{ + /* dma1 src1 address */ + if (ili_ice_mode_read(0x072104, &dma->src_addr, 4) < 0) + ILI_ERR("read dma1 src1 address failed\n"); + /* dma1 src1 format */ + if (ili_ice_mode_read(0x072108, &dma->src_fmt, 4) < 0) + ILI_ERR("read dma1 src1 format failed\n"); + /* dma1 dest address */ + if (ili_ice_mode_read(0x072114, &dma->dest_addr, 4) < 0) + ILI_ERR("read dma1 src1 format failed\n"); + /* dma1 dest format */ + if (ili_ice_mode_read(0x072118, &dma->dest_fmt, 4) < 0) + ILI_ERR("read dma1 dest format failed\n"); + /* Block size */ + if (ili_ice_mode_read(0x07211C, &dma->block_size, 4) < 0) + ILI_ERR("read block size (%d) failed\n", dma->block_size); + ILI_DBG("dma.src_addr=0x%x, dma.src_fmt=0x%x, dma.dest_addr=0x%x, dma.dest_fmt=0x%x, dma.block_size=0x%x\n" + , dma->src_addr, dma->src_fmt, dma->dest_addr, dma->dest_fmt, dma->block_size); +} + +void ili_set_dma1_config(struct ilitek_dma_config *dma) +{ + ILI_DBG("dma.src_addr=0x%x, dma.src_fmt=0x%x, dma.dest_addr=0x%x, dma.dest_fmt=0x%x, dma.block_size=0x%x\n" + , dma->src_addr, dma->src_fmt, dma->dest_addr, dma->dest_fmt, dma->block_size); + /* dma1 src1 address */ + if (ili_ice_mode_write(0x072104, dma->src_addr, 4) < 0) + ILI_ERR("Write dma1 src1 address failed\n"); + /* dma1 src1 format */ + if (ili_ice_mode_write(0x072108, dma->src_fmt, 4) < 0) + ILI_ERR("Write dma1 src1 format failed\n"); + /* dma1 dest address */ + if (ili_ice_mode_write(0x072114, dma->dest_addr, 4) < 0) + ILI_ERR("Write dma1 src1 format failed\n"); + /* dma1 dest format */ + if (ili_ice_mode_write(0x072118, dma->dest_fmt, 4) < 0) + ILI_ERR("Write dma1 dest format failed\n"); + /* Block size*/ + if (ili_ice_mode_write(0x07211C, dma->block_size, 4) < 0) + ILI_ERR("Write block size (%d) failed\n", dma->block_size); + /* Disable CRC calc settings */ + if (ili_ice_mode_write(0x041014, 0x0, 4) < 0) + ILI_ERR("Write dma CRC calc settings failed\n"); + /* Dma1 stop */ + if (ili_ice_mode_write(0x072100, 0x02040000, 4) < 0) + ILI_ERR("Write dma1 stop failed\n"); + /* clr int */ + if (ili_ice_mode_write(0x048006, 0x2, 1) < 0) + ILI_ERR("Write clr int failed\n"); + /* Dma1 start */ + if (ili_ice_mode_write(0x072100, 0x01040000, 4) < 0) + ILI_ERR("Write dma1 start failed\n"); +} + +int ili_ic_whole_reset(bool mcu, bool withflash) +{ + int ret = 0; + bool ice = atomic_read(&ilits->ice_stat); + + if (withflash) + ilits->chip->reset_key = TDDI_WHOLE_CHIP_RST_WITH_FLASH_KEY; + else + ilits->chip->reset_key = TDDI_WHOLE_CHIP_RST_WITHOUT_FLASH_KEY; + + if (!ice) { +#if (TDDI_INTERFACE == BUS_I2C && ENABLE_CASCADE) + ili_ice_mode_ctrl_by_mode(ENABLE, mcu, BOTH); +#elif (TDDI_INTERFACE == BUS_SPI && ENABLE_CASCADE) + ili_set_bypass_mode(mcu); +#else + if (ili_ice_mode_ctrl(ENABLE, mcu) < 0) + ILI_ERR("Enable ice mode failed before chip reset\n"); +#endif + } + + if (!withflash) + ili_ice_mode_write_by_mode(ilits->chip->reset_addr, TDDI_WHOLE_CHIP_RST_WITHOUT_FLASH_PRE_KEY, sizeof(u32), BOTH); + + ILI_INFO("ic whole reset key = 0x%x, edge_delay = %d, withflash = %d\n", + ilits->chip->reset_key, ilits->rst_edge_delay, withflash); + + ret = ili_ice_mode_write_by_mode(ilits->chip->reset_addr, ilits->chip->reset_key, sizeof(u32), BOTH); + if (ret < 0) { + ILI_ERR("ic whole reset failed\n"); + goto out; + } + + /* Need accurate power sequence, do not change it to msleep */ + mdelay(ilits->rst_edge_delay); + +out: + if (!ice) { +#if (TDDI_INTERFACE == BUS_I2C && ENABLE_CASCADE) + ili_ice_mode_ctrl_by_mode(DISABLE, mcu, BOTH); +#elif (TDDI_INTERFACE == BUS_SPI && ENABLE_CASCADE) + ili_ice_mode_ctrl_by_mode(DISABLE, mcu, BOTH); +#else + if (ili_ice_mode_ctrl(DISABLE, mcu) < 0) + ILI_ERR("Enable ice mode failed after chip reset\n"); +#endif + } + + return ret; +} + +static void ilitek_tddi_ic_cascade_wr_pack(int packet) +{ + int ret = 0, retry = 100; + u32 reg_data = 0; + + while (retry--) { +#if (TDDI_INTERFACE == BUS_I2C) + if (ili_ice_mode_read_by_mode(0x73010, ®_data, sizeof(u8), ilits->slave_wr_ctrl) < 0) + ILI_ERR("Read 0x73010 error\n"); +#else + if (ilits->slave_wr_ctrl) { + ili_ice_mode_write(READ_BACK_LOCK_REG, 0x01, 1); + ili_ice_mode_read(0x73010, ®_data, sizeof(reg_data)); + ILI_DBG("check ok 0x73010 read Master 0x%X\n", reg_data); + ili_ice_mode_read(SLAVE_READ_BACK_WORD_REG, ®_data, sizeof(reg_data)); + ILI_DBG("check ok 0x62030 read Slave 0x%X\n", reg_data); + reg_data = (reg_data >> 24) & 0xFF; + } else { + if (ili_ice_mode_read(0x73010, ®_data, sizeof(u8)) < 0) + ILI_ERR("Read 0x73010 error\n"); + } +#endif + if ((reg_data & 0x02) == 0) { + ILI_INFO("check ok 0x73010 read 0x%X retry = %d\n", reg_data, retry); + break; + } + + mdelay(10); + } + + if (retry <= 0) + ILI_INFO("check 0x73010 error read 0x%X\n", reg_data); + +#if (TDDI_INTERFACE == BUS_I2C) + ret = ili_ice_mode_write_by_mode(0x73000, packet, 4, ilits->slave_wr_ctrl); +#else + if (ilits->slave_wr_ctrl) { + ret = ili_mspi_write_slave_register(0x73000, packet, 4); + + /* set mspi 0 */ + if (ili_ice_mode_write_by_mode(MSPI_REG, 0x00, 1, MASTER) < 0) + ILI_ERR("Failed to write MSPI_REG in ice mode\n"); + } else { + ret = ili_ice_mode_write(0x73000, packet, 4); + } +#endif + if (ret < 0) { + ILI_ERR("Write %x at 0x73000 Fail\n", packet); + } +} + +static u32 ilitek_tddi_ic_cascade_rd_pack(int packet) +{ + int ret = 0, retry = 100; + u32 reg_data = 0; + + ilitek_tddi_ic_cascade_wr_pack(packet); + + while (retry--) { +#if (TDDI_INTERFACE == BUS_I2C) + if (ili_ice_mode_read_by_mode(0x4800A, ®_data, sizeof(u8), ilits->slave_wr_ctrl) < 0) + ILI_ERR("Read 0x4800A error\n"); +#else + if (ilits->slave_wr_ctrl) { + ili_ice_mode_write(READ_BACK_LOCK_REG, 0x01, 1); + ili_ice_mode_read(0x4800A, ®_data, sizeof(reg_data)); + ILI_DBG("check ok 0x4800A read Master 0x%X\n", reg_data); + ili_ice_mode_read(SLAVE_READ_BACK_WORD_REG, ®_data, sizeof(reg_data)); + ILI_DBG("check ok 0x4800A read Slave 0x%X\n", reg_data); + reg_data = (reg_data >> 24) & 0xFF; + } else { + if (ili_ice_mode_read(0x4800A, ®_data, sizeof(u8)) < 0) + ILI_ERR("Read 0x4800A error\n"); + } +#endif + + if ((reg_data & 0x02) == 0x02) { + ILI_INFO("check ok 0x4800A read 0x%X retry = %d\n", reg_data, retry); + break; + } + mdelay(10); + } + + if (retry <= 0) + ILI_INFO("check 0x4800A error read 0x%X\n", reg_data); + +#if (TDDI_INTERFACE == BUS_I2C) + ret = ili_ice_mode_write_by_mode(0x4800A, 0x02, 1, ilits->slave_wr_ctrl); +#else + if (ilits->slave_wr_ctrl) { + ret = ili_mspi_write_slave_register(0x4800A, 0x02, 1); + + /* set mspi 0 */ + if (ili_ice_mode_write_by_mode(MSPI_REG, 0x00, 1, MASTER) < 0) + ILI_ERR("Failed to write MSPI_REG in ice mode\n"); + } else { + ret = ili_ice_mode_write(0x4800A, 0x02, 1); + } +#endif + if (ret < 0) { + ILI_ERR("Write 0x2 at 0x4800A Fail\n"); + } + +#if (TDDI_INTERFACE == BUS_I2C) + if (ili_ice_mode_read_by_mode(0x73016, ®_data, sizeof(u8), ilits->slave_wr_ctrl) < 0) + ILI_ERR("Read 0x73016 error\n"); +#else + if (ilits->slave_wr_ctrl) { + ili_ice_mode_write(READ_BACK_LOCK_REG, 0x01, 1); + ili_ice_mode_read(0x73016, ®_data, sizeof(reg_data)); + ILI_DBG("check ok 0x73016 read Master 0x%X\n", reg_data); + ili_ice_mode_read(SLAVE_READ_BACK_WORD_REG, ®_data, sizeof(reg_data)); + ILI_DBG("check ok 0x73016 read Slave 0x%X\n", reg_data); + reg_data = (reg_data >> 24) & 0xFF; + } else { + if (ili_ice_mode_read(0x73016, ®_data, sizeof(u8)) < 0) + ILI_ERR("Read 0x73016 error\n"); + } +#endif + + return reg_data; +} + +void ili_ic_set_cascade_ddi_reg_onepage(u8 page, u8 reg, u8 data, bool mcu) +{ + + u32 setpage = 0x1FFFFF00 | page; + u32 setreg = 0x1F000100 | (reg << 16) | data; + bool ice = atomic_read(&ilits->ice_stat); + u8 spiclk_num = 3; + + ILI_INFO("setpage = 0x%X setreg = 0x%X\n", setpage, setreg); + + if (ilits->slave_wr_ctrl) { + ili_core_spi_setup(spiclk_num); + } + + if (!ice) { +#if (TDDI_INTERFACE == BUS_I2C) + ili_ice_mode_ctrl_by_mode(ENABLE, mcu, ilits->slave_wr_ctrl); +#else + if (ilits->slave_wr_ctrl) { + ili_set_bypass_mode(mcu); + } else { + ili_ice_mode_ctrl(ENABLE, mcu); + } +#endif + } + + /*TDI_WR_KEY*/ + ilitek_tddi_ic_cascade_wr_pack(0x1FFF9527); + /*Switch to Page*/ + ilitek_tddi_ic_cascade_wr_pack(setpage); + /* Page*/ + ilitek_tddi_ic_cascade_wr_pack(setreg); + /*TDI_WR_KEY OFF*/ + ilitek_tddi_ic_cascade_wr_pack(0x1FFF9500); + + if (!ice) { +#if (TDDI_INTERFACE == BUS_I2C) + ili_ice_mode_ctrl_by_mode(DISABLE, mcu, ilits->slave_wr_ctrl); +#else + ili_ice_mode_ctrl_by_mode(DISABLE, mcu, ilits->slave_wr_ctrl); +#endif + } + + if (ilits->slave_wr_ctrl) { + ili_core_spi_setup(SPI_CLK); + } +} + +void ili_ic_get_cascade_ddi_reg_onepage(u8 page, u8 reg, u8 *data, bool mcu) +{ + int ret = 0; + u32 setpage = 0x1FFFFF00 | page; + u32 setreg = 0x2F000100 | (reg << 16); + bool ice = atomic_read(&ilits->ice_stat); + u8 spiclk_num = 3; + + ILI_INFO("setpage = 0x%X setreg = 0x%X\n", setpage, setreg); + + if (ilits->slave_wr_ctrl) { + ili_core_spi_setup(spiclk_num); + } + + if (!ice) { +#if (TDDI_INTERFACE == BUS_I2C) + if (ili_ice_mode_ctrl_by_mode(ENABLE, mcu, ilits->slave_wr_ctrl) < 0) + ILI_ERR("Enable ice mode failed before reading ddi reg\n"); +#else + if (ilits->slave_wr_ctrl) { + ili_set_bypass_mode(mcu); + } else { + if (ili_ice_mode_ctrl(ENABLE, mcu) < 0) + ILI_ERR("Enable ice mode failed before reading ddi reg\n"); + } +#endif + } + + /*TDI_WR_KEY*/ + ilitek_tddi_ic_cascade_wr_pack(0x1FFF9527); + /*Set Read Page reg*/ + ilitek_tddi_ic_cascade_wr_pack(setpage); + + /*TDI_RD_KEY*/ + ilitek_tddi_ic_cascade_wr_pack(0x1FFF9487); + + /*( *( __IO uint8 *) (0x4800A) ) =0x2*/ +#if (TDDI_INTERFACE == BUS_I2C) + ret = ili_ice_mode_write_by_mode(0x4800A, 0x02, 1, ilits->slave_wr_ctrl); +#else + if (ilits->slave_wr_ctrl) { + ret = ili_mspi_write_slave_register(0x4800A, 0x02, 1); + + /* set mspi 0 */ + if (ili_ice_mode_write_by_mode(MSPI_REG, 0x00, 1, MASTER) < 0) + ILI_ERR("Failed to write MSPI_REG in ice mode\n"); + } else { + ret = ili_ice_mode_write(0x4800A, 0x02, 1); + } +#endif + if (ret < 0) { + ILI_ERR("Write 0x2 at 0x4800A Fail\n"); + } + + *data = ilitek_tddi_ic_cascade_rd_pack(setreg); + ILI_INFO("check page = 0x%X, reg = 0x%X, read 0x%X\n", page, reg, *data); + + /*TDI_RD_KEY OFF*/ + ilitek_tddi_ic_cascade_wr_pack(0x1FFF9400); + /*TDI_WR_KEY OFF*/ + ilitek_tddi_ic_cascade_wr_pack(0x1FFF9500); + + if (!ice) { +#if (TDDI_INTERFACE == BUS_I2C) + ili_ice_mode_ctrl_by_mode(DISABLE, mcu, ilits->slave_wr_ctrl); +#else + ili_ice_mode_ctrl_by_mode(DISABLE, mcu, ilits->slave_wr_ctrl); +#endif + } + + if (ilits->slave_wr_ctrl) { + ili_core_spi_setup(SPI_CLK); + } +} + +static void ilitek_tddi_ic_wr_pack(int packet) +{ + int retry = 100; + u32 reg_data = 0; + + while (retry--) { + if (ili_ice_mode_read(0x73010, ®_data, sizeof(u8)) < 0) + ILI_ERR("Read 0x73010 error\n"); + + if ((reg_data & 0x02) == 0) { + ILI_INFO("check ok 0x73010 read 0x%X retry = %d\n", reg_data, retry); + break; + } + mdelay(10); + } + + if (retry <= 0) + ILI_INFO("check 0x73010 error read 0x%X\n", reg_data); + + if (ili_ice_mode_write(0x73000, packet, 4) < 0) + ILI_ERR("Write %x at 0x73000\n", packet); +} + +static u32 ilitek_tddi_ic_rd_pack(int packet) +{ + int retry = 100; + u32 reg_data = 0; + + ilitek_tddi_ic_wr_pack(packet); + + while (retry--) { + if (ili_ice_mode_read(0x4800A, ®_data, sizeof(u8)) < 0) + ILI_ERR("Read 0x4800A error\n"); + + if ((reg_data & 0x02) == 0x02) { + ILI_INFO("check ok 0x4800A read 0x%X retry = %d\n", reg_data, retry); + break; + } + mdelay(10); + } + if (retry <= 0) + ILI_INFO("check 0x4800A error read 0x%X\n", reg_data); + + if (ili_ice_mode_write(0x4800A, 0x02, 1) < 0) + ILI_ERR("Write 0x2 at 0x4800A\n"); + + if (ili_ice_mode_read(0x73016, ®_data, sizeof(u8)) < 0) + ILI_ERR("Read 0x73016 error\n"); + + return reg_data; +} + +void ili_ic_set_ddi_reg_onepage(u8 page, u8 reg, u8 data, bool mcu) +{ + u32 setpage = 0x1FFFFF00 | page; + u32 setreg = 0x1F000100 | (reg << 16) | data; + bool ice = atomic_read(&ilits->ice_stat); + + ILI_INFO("setpage = 0x%X setreg = 0x%X\n", setpage, setreg); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, mcu) < 0) + ILI_ERR("Enable ice mode failed before writing ddi reg\n"); + + /*TDI_WR_KEY*/ + ilitek_tddi_ic_wr_pack(0x1FFF9527); + /*Switch to Page*/ + ilitek_tddi_ic_wr_pack(setpage); + /* Page*/ + ilitek_tddi_ic_wr_pack(setreg); + /*TDI_WR_KEY OFF*/ + ilitek_tddi_ic_wr_pack(0x1FFF9500); + + if (!ice) + if (ili_ice_mode_ctrl(DISABLE, mcu) < 0) + ILI_ERR("Disable ice mode failed after writing ddi reg\n"); +} + +void ili_ic_get_ddi_reg_onepage(u8 page, u8 reg, u8 *data, bool mcu) +{ + u32 setpage = 0x1FFFFF00 | page; + u32 setreg = 0x2F000100 | (reg << 16); + bool ice = atomic_read(&ilits->ice_stat); + + ILI_INFO("setpage = 0x%X setreg = 0x%X\n", setpage, setreg); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, mcu) < 0) + ILI_ERR("Enable ice mode failed before reading ddi reg\n"); + + /*TDI_WR_KEY*/ + ilitek_tddi_ic_wr_pack(0x1FFF9527); + /*Set Read Page reg*/ + ilitek_tddi_ic_wr_pack(setpage); + + /*TDI_RD_KEY*/ + ilitek_tddi_ic_wr_pack(0x1FFF9487); + /*( *( __IO uint8 *) (0x4800A) ) =0x2*/ + if (ili_ice_mode_write(0x4800A, 0x02, 1) < 0) + ILI_ERR("Write 0x2 at 0x4800A\n"); + + *data = ilitek_tddi_ic_rd_pack(setreg); + ILI_INFO("check page = 0x%X, reg = 0x%X, read 0x%X\n", page, reg, *data); + + /*TDI_RD_KEY OFF*/ + ilitek_tddi_ic_wr_pack(0x1FFF9400); + /*TDI_WR_KEY OFF*/ + ilitek_tddi_ic_wr_pack(0x1FFF9500); + + if (!ice) + if (ili_ice_mode_ctrl(DISABLE, mcu) < 0) + ILI_ERR("Disable ice mode failed after reading ddi reg\n"); +} + +void ili_ic_get_pc_counter(int stat) +{ + bool ice = atomic_read(&ilits->ice_stat); + u32 pc = 0, pc_addr = ilits->chip->pc_counter_addr; + u32 latch = 0, latch_addr = ilits->chip->pc_latch_addr; + int ret = 0; +#if (TDDI_INTERFACE == BUS_SPI) + u32 mcu_state = 0; + bool read = 0; +#endif + + ILI_DBG("stat = %d\n", stat); + + if (ilits->cascade_info_block.nNum != 0) { +#if (TDDI_INTERFACE == BUS_SPI) + if (stat == DO_SPI_RECOVER) + mcu_state = OFF; + else + mcu_state = ON; + + ili_cascade_rw_tp_reg(mcu_state, read, pc_addr, 0, 0, &pc); + ili_cascade_rw_tp_reg(mcu_state, read, latch_addr, 0, 0, &latch); + + ilits->fw_pc = pc; + ilits->fw_latch = latch; + + if (ilits->slave_wr_ctrl) { + ILI_ERR("Read Slave counter (addr: 0x%x) = 0x%x, latch (addr: 0x%x) = 0x%x\n", + pc_addr, ilits->fw_pc, latch_addr, ilits->fw_latch); + } else { + ILI_ERR("Read Master counter (addr: 0x%x) = 0x%x, latch (addr: 0x%x) = 0x%x\n", + pc_addr, ilits->fw_pc, latch_addr, ilits->fw_latch); + } + + /* Avoid screen abnormal. */ + if (stat == DO_SPI_RECOVER) { + atomic_set(&ilits->ice_stat, DISABLE); + return; + } + +#elif (TDDI_INTERFACE == BUS_I2C) + if (ilits->slave_wr_ctrl) { + ilits->i2c_addr_change = SLAVE; + ILI_DBG("Change to Slave Address\n"); + } else { + ilits->i2c_addr_change = MASTER; + ILI_DBG("Change to Master Address\n"); + } + + if (!ice) { + if (stat == DO_I2C_RECOVER) + ret = ili_ice_mode_ctrl_by_mode(ENABLE, OFF, ilits->slave_wr_ctrl); + else + ret = ili_ice_mode_ctrl_by_mode(ENABLE, ON, ilits->slave_wr_ctrl); + + if (ret < 0) + ILI_ERR("Enable ice mode failed while reading pc counter\n"); + } + + ili_ice_mode_read_by_mode(pc_addr, &pc, sizeof(u32), ilits->slave_wr_ctrl); + ili_ice_mode_read_by_mode(latch_addr, &latch, sizeof(u32), ilits->slave_wr_ctrl); + + ilits->fw_pc = pc; + ilits->fw_latch = latch; + + if (ilits->slave_wr_ctrl) { + ILI_ERR("Read Slave counter (addr: 0x%x) = 0x%x, latch (addr: 0x%x) = 0x%x\n", + pc_addr, ilits->fw_pc, latch_addr, ilits->fw_latch); + } else { + ILI_ERR("Read Master counter (addr: 0x%x) = 0x%x, latch (addr: 0x%x) = 0x%x\n", + pc_addr, ilits->fw_pc, latch_addr, ilits->fw_latch); + } + + if (!ice) { + if (ili_ice_mode_ctrl_by_mode(DISABLE, ON, ilits->slave_wr_ctrl) < 0) + ILI_ERR("Disable ice mode failed while reading pc counter\n"); + } +#endif + } else { + if (!ice) { + if (stat == DO_SPI_RECOVER || stat == DO_I2C_RECOVER) + ret = ili_ice_mode_ctrl(ENABLE, OFF); + else + ret = ili_ice_mode_ctrl(ENABLE, ON); + + if (ret < 0) + ILI_ERR("Enable ice mode failed while reading pc counter\n"); + } + + if (ili_ice_mode_read(pc_addr, &pc, sizeof(u32)) < 0) + ILI_ERR("Read pc conter error\n"); + + if (ili_ice_mode_read(latch_addr, &latch, sizeof(u32)) < 0) + ILI_ERR("Read pc latch error\n"); + + ilits->fw_pc = pc; + ilits->fw_latch = latch; + ILI_ERR("Read counter (addr: 0x%x) = 0x%x, latch (addr: 0x%x) = 0x%x\n", + pc_addr, ilits->fw_pc, latch_addr, ilits->fw_latch); + + /* Avoid screen abnormal. */ + if (stat == DO_SPI_RECOVER) { + atomic_set(&ilits->ice_stat, DISABLE); + return; + } + + if (!ice) { + if (ili_ice_mode_ctrl(DISABLE, ON) < 0) + ILI_ERR("Disable ice mode failed while reading pc counter\n"); + } + } +} + +int ili_ic_int_trigger_ctrl(bool pulse) +{ + /* It's supported by fw, and the level will be kept at high until data was already prepared. */ + if (ili_ic_func_ctrl("int_trigger", pulse) < 0) { + ILI_ERR("Write CMD error, set back to <%s> trigger\n", ilits->int_pulse ? "Level" : "Pulse"); + return -1; + } + + ilits->int_pulse = pulse; + ILI_INFO("INT Trigger = %s\n", ilits->int_pulse ? "Level" : "Pulse"); + return 0; +} + +int ili_ic_check_int_level(bool level) +{ + int timer = 3000; + int gpio = IRQ_GPIO_NUM; /* ilits->tp_int */ + + /* + * If callers have a trouble to use the gpio that is passed by vendors, + * please utlises a physical gpio number instead or call a help from them. + */ + + while (--timer > 0) { + if (level) { + if (gpio_get_value(gpio)) { + ILI_DBG("INT high detected.\n"); + return 0; + } + } else { + if (!gpio_get_value(gpio)) { + ILI_DBG("INT low detected.\n");; + return 0; + } + } + mdelay(1); + } + ILI_ERR("Error! INT level no detected.\n"); + return -1; +} + +int ili_ic_check_int_pulse(bool pulse) +{ + if (!wait_event_interruptible_timeout(ilits->inq, !atomic_read(&ilits->cmd_int_check), msecs_to_jiffies(ilits->wait_int_timeout))) { + ILI_ERR("Error! INT pulse no detected. Timeout = %d ms\n", ilits->wait_int_timeout); + atomic_set(&ilits->cmd_int_check, DISABLE); + return -1; + } + ILI_DBG("INT pulse detected.\n"); + return 0; +} + +int ili_ic_check_busy(int count, int delay, bool irq) +{ + u8 cmd[2] = {0}; + u8 busy = 0, rby = 0; + + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_CDC_BUSY_STATE; + + if (ilits->actual_tp_mode == P5_X_FW_AP_MODE) + rby = 0x41; + else if (ilits->actual_tp_mode == P5_X_FW_TEST_MODE) + rby = 0x51; + else { + ILI_ERR("Unknown TP mode (0x%x)\n", ilits->actual_tp_mode); + return -EINVAL; + } + + ILI_INFO("read byte = %x, delay = %d\n", rby, delay); + + do { + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) + ILI_ERR("Write check busy cmd failed\n"); + + if (ilits->wrapper(&cmd[1], sizeof(u8), &busy, sizeof(u8), irq, OFF) < 0) + ILI_ERR("Read check busy failed\n"); + + ILI_DBG("busy = 0x%x\n", busy); + + if (busy == rby) { + ILI_INFO("Check busy free\n"); + return 0; + } + + mdelay(delay); + } while (--count > 0); + + ILI_ERR("Check busy (0x%x) timeout !\n", busy); + ili_ic_get_pc_counter(0); + + if (ilits->cascade_info_block.nNum != 0) { + ilits->slave_wr_ctrl = 1; + ili_ic_get_pc_counter(0); + ilits->slave_wr_ctrl = 0; + } + return -1; +} + +int ili_ic_get_project_id(u8 *pdata, int size) +{ + int i; + u32 tmp; + bool ice = atomic_read(&ilits->ice_stat); + + if (!pdata) { + ILI_ERR("pdata is null\n"); + return -ENOMEM; + } + + ILI_INFO("Read size = %d\n", size); + + if (!ice) + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed while reading project id\n"); + + if (ili_ice_mode_write(0x041000, 0x0, 1) < 0) + ILI_ERR("Pull cs low failed\n"); + if (ili_ice_mode_write(0x041004, 0x66aa55, 3) < 0) + ILI_ERR("Write key failed\n"); + + if (ili_ice_mode_write(0x041008, 0x03, 1) < 0) + ILI_ERR("Write 0x03 at 0x041008\n"); + + if (ili_ice_mode_write(0x041008, (RSV_BK_ST_ADDR & 0xFF0000) >> 16, 1) < 0) + ILI_ERR("Write address failed\n"); + if (ili_ice_mode_write(0x041008, (RSV_BK_ST_ADDR & 0x00FF00) >> 8, 1) < 0) + ILI_ERR("Write address failed\n"); + if (ili_ice_mode_write(0x041008, (RSV_BK_ST_ADDR & 0x0000FF), 1) < 0) + ILI_ERR("Write address failed\n"); + + for (i = 0; i < size; i++) { + if (ili_ice_mode_write(0x041008, 0xFF, 1) < 0) + ILI_ERR("Write dummy failed\n"); + if (ili_ice_mode_read(0x41010, &tmp, sizeof(u8)) < 0) + ILI_ERR("Read project id error\n"); + pdata[i] = tmp; + ILI_INFO("project_id[%d] = 0x%x\n", i, pdata[i]); + } + + ili_flash_clear_dma(); + + if (ili_ice_mode_write(0x041000, 0x1, 1) < 0) + ILI_ERR("Pull cs high\n"); + + if (!ice) + if (ili_ice_mode_ctrl(DISABLE, OFF) < 0) + ILI_ERR("Disable ice mode failed while reading project id\n"); + + return 0; +} + +int ili_ic_get_core_ver(void) +{ + int i = 0, ret = 0; + u8 cmd[2] = {0}, buf[10] = {0}; + + ilits->protocol->core_ver_len = P5_X_CORE_VER_FOUR_LENGTH; + + if (ilits->info_from_hex) { + buf[1] = ilits->fw_info[68]; + buf[2] = ilits->fw_info[69]; + buf[3] = ilits->fw_info[70]; + buf[4] = ilits->fw_info[71]; + goto out; + } + + do { + if (i == 0) { + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_CORE_VERSION_NEW; + } else { + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_CORE_VERSION; + ilits->protocol->core_ver_len = P5_X_CORE_VER_THREE_LENGTH; + } + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) + ILI_ERR("Write core ver cmd failed\n"); + + if (ilits->wrapper(&cmd[1], sizeof(u8), buf, ilits->protocol->core_ver_len, ON, OFF) < 0) + ILI_ERR("Write core ver (0x%x) failed\n", cmd[1]); + + ILI_DBG("header = 0x%x\n", buf[0]); + + if (buf[0] == P5_X_GET_CORE_VERSION || + buf[0] == P5_X_GET_CORE_VERSION_NEW) + break; + + } while (++i < 2); + + if (buf[0] == P5_X_GET_CORE_VERSION) + buf[4] = 0; + + if (i >= 2) { + ILI_ERR("Invalid header (0x%x)\n", buf[0]); + ret = -EINVAL; + } + +out: + ILI_INFO("Core version = %d.%d.%d.%d\n", buf[1], buf[2], buf[3], buf[4]); + ilits->chip->core_ver = buf[1] << 24 | buf[2] << 16 | buf[3] << 8 | buf[4]; + return ret; +} + +void ili_fw_uart_ctrl(u8 ctrl) +{ + u8 cmd[4] = {0}; + + if (ctrl > 1) { + ILI_INFO("Unknown cmd, ignore\n"); + return; + } + + ILI_INFO("%s UART mode\n", ctrl ? "Enable" : "Disable"); + + cmd[0] = P5_X_I2C_UART; + cmd[1] = 0x3; + cmd[2] = 0; + cmd[3] = ctrl; + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) { + ILI_INFO("Write fw uart cmd failed\n"); + return; + } +} + +int ili_ic_get_fw_ver(void) +{ + int ret = 0; + u8 cmd[2] = {0}; + u8 buf[10] = {0}; + + if (ilits->info_from_hex) { + buf[1] = ilits->fw_info[48]; + buf[2] = ilits->fw_info[49]; + buf[3] = ilits->fw_info[50]; + buf[4] = ilits->fw_info[51]; + buf[5] = ilits->fw_mp_ver[0]; + buf[6] = ilits->fw_mp_ver[1]; + buf[7] = ilits->fw_mp_ver[2]; + buf[8] = ilits->fw_mp_ver[3]; + goto out; + } + + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_FW_VERSION; + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Write pre cmd failed\n"); + ret = -EINVAL; + goto out; + + } + + if (ilits->wrapper(&cmd[1], sizeof(u8), buf, ilits->protocol->fw_ver_len, ON, OFF) < 0) { + ILI_ERR("Write fw version cmd failed\n"); + ret = -EINVAL; + goto out; + } + + if (buf[0] != P5_X_GET_FW_VERSION) { + ILI_ERR("Invalid firmware ver\n"); + ret = -1; + } + +out: + ILI_INFO("Firmware version = %d.%d.%d.%d\n", buf[1], buf[2], buf[3], buf[4]); + ILI_INFO("Firmware MP version = %d.%d.%d.%d\n", buf[5], buf[6], buf[7], buf[8]); + ilits->chip->fw_ver = buf[1] << 24 | buf[2] << 16 | buf[3] << 8 | buf[4]; + ilits->chip->fw_mp_ver = buf[5] << 24 | buf[6] << 16 | buf[7] << 8 | buf[8]; + return ret; +} + +int ili_ic_get_panel_info(void) +{ + int ret = 0; + u8 cmd = P5_X_GET_PANEL_INFORMATION; + u8 buf[10] = {0}; + u8 len = ilits->protocol->panel_info_len; + u8 data_type = P5_X_FW_SIGNAL_DATA_MODE; + + if (ilits->info_from_hex && (ilits->chip->core_ver >= CORE_VER_1410)) { + buf[1] = ilits->fw_info[16]; + buf[2] = ilits->fw_info[17]; + buf[3] = ilits->fw_info[18]; + buf[4] = ilits->fw_info[19]; + ilits->panel_wid = buf[2] << 8 | buf[1]; + ilits->panel_hei = buf[4] << 8 | buf[3]; + ilits->trans_xy = (ilits->chip->core_ver >= CORE_VER_1430 + && (ilits->rib.nReportByPixel > 0)) ? ON : OFF; + goto out; + } + + len = (ilits->chip->core_ver >= CORE_VER_1430) ? 6 : len; + + ret = ilits->wrapper(&cmd, sizeof(cmd), buf, len, ON, OFF); + if (ret < 0) + ILI_ERR("Read panel info error\n"); + + if (buf[0] != cmd) { + ILI_INFO("Invalid panel info, use default resolution\n"); + ilits->panel_wid = TOUCH_SCREEN_X_MAX; + ilits->panel_hei = TOUCH_SCREEN_Y_MAX; + ilits->trans_xy = OFF; + } else { + ilits->panel_wid = buf[1] << 8 | buf[2]; + ilits->panel_hei = buf[3] << 8 | buf[4]; + ilits->trans_xy = (ilits->chip->core_ver >= CORE_VER_1430) ? buf[5] : OFF; + } + + if (ilits->chip->core_ver >= CORE_VER_1470) { + cmd = P5_X_GET_REPORT_FORMAT; + ret = ilits->wrapper(&cmd, sizeof(cmd), buf, 2, ON, OFF); + if (ret < 0) + ILI_ERR("Read report format info error\n"); + + if (buf[0] != cmd) { + ILI_INFO("Invalid report format info, use default report format resolution\n"); + ilits->rib.nReportResolutionMode = POSITION_LOW_RESOLUTION; + ilits->rib.nCustomerType = POSITION_CUSTOMER_TYPE_OFF; + ilits->PenType = POSITION_PEN_TYPE_OFF; + if (ilits->chip->core_ver >= CORE_VER_1700) { + ilits->customertype_off = POSITION_CUSTOMER_TYPE_OFF_3BITS; + } else { + ilits->customertype_off = POSITION_CUSTOMER_TYPE_OFF; + } + } else { + ilits->rib.nReportResolutionMode = buf[1] & 0x07; + + if (ilits->chip->core_ver >= CORE_VER_1700) { + ilits->rib.nCustomerType = (buf[1] >> 3) & 0x07; + ilits->PenType = buf[1] >> 6; + ilits->customertype_off = POSITION_CUSTOMER_TYPE_OFF_3BITS; + + if (ilits->PenType == POSITION_PEN_TYPE_ON) { + ilits->touch_num = MAX_TOUCH_NUM + MAX_PEN_NUM; + ili_set_tp_data_len(DATA_FORMAT_DEMO, false, &data_type); + ILI_INFO("Panel support Pen Mode, Packet Len = %d, Touch Number = %d\n", ilits->tp_data_len, ilits->touch_num); + } + } else { + ilits->PenType = POSITION_PEN_TYPE_OFF; + ilits->rib.nCustomerType = buf[1] >> 3; + ilits->customertype_off = POSITION_CUSTOMER_TYPE_OFF; + } + } + } else { + ilits->PenType = POSITION_PEN_TYPE_OFF; + ilits->rib.nReportResolutionMode = POSITION_LOW_RESOLUTION; + ilits->rib.nCustomerType = POSITION_CUSTOMER_TYPE_OFF; + ilits->customertype_off = POSITION_CUSTOMER_TYPE_OFF; + } + ILI_INFO("PenType = 0x%x, Customer Type OFF = 0x%x\n", ilits->PenType, ilits->customertype_off); + +#if ENABLE_PEN_MODE + cmd = P5_X_GET_PEN_INFO; + ret = ilits->wrapper(&cmd, sizeof(cmd), buf, 9, ON, OFF); + ILI_INFO("cmd = 0x%X, buf[0] = 0x%X, buf[1] = 0x%X, buf[2] = 0x%X, buf[3] = 0x%X, buf[4] = 0x%X\n", cmd, buf[0], buf[1], buf[2], buf[3], buf[4]); + if (ret < 0) + ILI_ERR("Read report format info error\n"); + + if (buf[0] != cmd) { + ILI_INFO("Invalid Pen info, use default report format resolution\n"); + ilits->pen_info_block.nPxRaw = P5_X_PEN_INFO_X_DATA_LEN; + ilits->pen_info_block.nPyRaw = P5_X_PEN_INFO_Y_DATA_LEN; + ilits->pen_info_block.nPxVa = 1; + ilits->pen_info_block.nPyVa = 1; + } else { + ilits->pen_info_block.nPxRaw = buf[1]; + ilits->pen_info_block.nPyRaw = buf[2]; + ilits->pen_info_block.nPxVa = buf[3]; + ilits->pen_info_block.nPyVa = buf[4]; + } +#endif +#if ENABLE_CASCADE + cmd = P5_X_GET_CASCADE_INFO; + ret = ilits->wrapper(&cmd, sizeof(cmd), buf, 5, ON, OFF); + ILI_INFO("cmd = 0x%X, buf[0] = 0x%X, buf[1] = 0x%X, buf[2] = 0x%X, buf[3] = 0x%X\n", cmd, buf[0], buf[1], buf[2], buf[3]); + if (ret < 0) + ILI_ERR("Read report format info error\n"); + + if (buf[0] != cmd) { + ilits->cascade_info_block.nDisable = ENABLE; + ilits->cascade_info_block.nNum = 2; + } else { + ilits->cascade_info_block.nDisable = buf[1] & BIT(0); + ilits->cascade_info_block.nNum = (buf[1] >> 1) & 0x07; + } +#else + ilits->cascade_info_block.nDisable = ENABLE; + ilits->cascade_info_block.nNum = 0; +#endif +out: + ILI_INFO("Panel info: width = %d, height = %d\n", ilits->panel_wid, ilits->panel_hei); + ILI_INFO("Transfer touch coordinate = %s\n", ilits->trans_xy ? "ON" : "OFF"); + ILI_INFO("Customer Type = %X\n", ilits->rib.nCustomerType); + ILI_INFO("Report Resolution Format Mode = %X\n", ilits->rib.nReportResolutionMode); + + if (ilits->chip->core_ver >= CORE_VER_1700) { + ILI_INFO("Pen Type = %X\n", ilits->PenType); + ILI_INFO("Pen Info Data, nPxRaw = %d, nPyRaw = %d, nPxVa = %d, nPyVa = %d\n", ilits->pen_info_block.nPxRaw, ilits->pen_info_block.nPyRaw, ilits->pen_info_block.nPxVa, ilits->pen_info_block.nPyVa); + ILI_INFO("Cascade Info Data, nDisable = %d, nNum = %d\n", ilits->cascade_info_block.nDisable, ilits->cascade_info_block.nNum); + } + + return ret; +} + +int ili_ic_get_tp_info(void) +{ + int ret = 0; + u8 cmd[2] = {0}; + u8 buf[20] = {0}; + + if (ilits->info_from_hex && (ilits->chip->core_ver >= CORE_VER_1410)) { + buf[1] = ilits->fw_info[5]; + buf[2] = ilits->fw_info[7]; + buf[3] = ilits->fw_info[8]; + buf[4] = ilits->fw_info[9]; + buf[5] = ilits->fw_info[10]; + buf[6] = ilits->fw_info[11]; + buf[7] = ilits->fw_info[12]; + buf[8] = ilits->fw_info[14]; + if (ilits->chip->core_ver >= CORE_VER_1700) { + buf[11] = ilits->fw_info[13]; + buf[12] = ilits->fw_info[15]; + } else { + buf[11] = buf[7]; + buf[12] = buf[8]; + } + goto out; + } + + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_TP_INFORMATION; + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Write tp info pre cmd failed\n"); + ret = -EINVAL; + goto out; + + } + + ret = ilits->wrapper(&cmd[1], sizeof(u8), buf, ilits->protocol->tp_info_len, ON, OFF); + if (ret < 0) { + ILI_ERR("Read tp info error\n"); + goto out; + } + + if (buf[0] != P5_X_GET_TP_INFORMATION) { + ILI_ERR("Invalid tp info\n"); + ret = -1; + goto out; + } + +out: + ilits->min_x = buf[1]; + ilits->min_y = buf[2]; + ilits->max_x = buf[4] << 8 | buf[3]; + ilits->max_y = buf[6] << 8 | buf[5]; + ilits->xch_num = buf[7]; + ilits->ych_num = buf[8]; + ilits->stx = buf[11]; + ilits->srx = buf[12]; + + ILI_INFO("TP Info: min_x = %d, min_y = %d, max_x = %d, max_y = %d\n", ilits->min_x, ilits->min_y, ilits->max_x, ilits->max_y); + ILI_INFO("TP Info: xch = %d, ych = %d, stx = %d, srx = %d\n", ilits->xch_num, ilits->ych_num, ilits->stx, ilits->srx); + return ret; +} + +void ili_ic_get_compress_info(void) +{ +#if ENABLE_COMPRESS_MODE + int ret = 0; + u8 cmd = P5_X_GET_COMPRESS_INFORMATION; + u8 buf[10] = {0}; + + if (ilits->info_from_hex && (ilits->chip->core_ver >= CORE_VER_1700)) { + buf[1] = ilits->fw_info[0]; + goto out; + } + + ret = ilits->wrapper(&cmd, sizeof(cmd), buf, 5, ON, OFF); + if (ret < 0) + ILI_ERR("Read compress info error\n"); + + if (buf[0] != cmd) { + ILI_INFO("Invalid compress info, use default value\n"); + ilits->compress_disable = true; + ilits->compress_handonly_disable = true; + ilits->compress_penonly_disable = true; + } else { + ilits->compress_disable = (buf[1] & 0x20) >> 5; + ilits->compress_handonly_disable = (buf[1] & 0x40) >> 6; + ilits->compress_penonly_disable = (buf[1] & 0x80) >> 7; + } + +out: + ilits->compress_disable = (buf[1] & 0x20) >> 5; + ilits->compress_handonly_disable = (buf[1] & 0x40) >> 6; + ilits->compress_penonly_disable = (buf[1] & 0x80) >> 7; + ILI_INFO("compress_disable = %d, compress_handonly_disable = %d, compress_penonly_disable = %d\n", ilits->compress_disable, ilits->compress_handonly_disable, ilits->compress_penonly_disable); +#endif +} + + + +int ili_ic_get_all_info(void) +{ + int ret = 0; +#if (TDDI_INTERFACE == BUS_I2C && ENABLE_GET_ALL_INFO) + u8 cmd = P5_X_GET_ALL_INFORMATION; + u8 buf[100] = {0}; + bool get_all_info; + + if (ilits->wrapper(&cmd, sizeof(cmd), buf, 96, ON, OFF) < 0) { + ILI_ERR("Write ALL info cmd failed\n"); + get_all_info = false; + ret = -EINVAL; + } + + if (buf[0] == P5_X_GET_ALL_INFORMATION) { + get_all_info = true; + + /* CORE_VERSION */ + ilits->chip->core_ver = buf[25] << 24 | buf[26] << 16 | buf[27] << 8 | buf[28]; + + /*GET_TP_INFORMATION*/ + ilits->min_x = buf[2]; + ilits->min_y = buf[3]; + ilits->max_x = buf[5] << 8 | buf[4]; + ilits->max_y = buf[7] << 8 | buf[6]; + ilits->xch_num = buf[8]; + ilits->ych_num = buf[9]; + ilits->stx = buf[12]; + ilits->srx = buf[13]; + + /* GET_FW_VERSION */ + ilits->chip->fw_ver = buf[16] << 24 | buf[17] << 16 | buf[18] << 8 | buf[19]; + ilits->chip->fw_mp_ver = buf[20] << 24 | buf[21] << 16 | buf[22] << 8 | buf[23]; + + /* GET_PANEL_INFORMATION */ + cmd = P5_X_GET_PANEL_INFORMATION; + if (buf[46] != cmd) { + ILI_INFO("Invalid panel info, use default resolution\n"); + ilits->panel_wid = TOUCH_SCREEN_X_MAX; + ilits->panel_hei = TOUCH_SCREEN_Y_MAX; + ilits->trans_xy = OFF; + } else { + ilits->panel_wid = buf[47] << 8 | buf[48]; + ilits->panel_hei = buf[49] << 8 | buf[50]; + ilits->trans_xy = (ilits->chip->core_ver >= CORE_VER_1430) ? buf[51] : OFF; + } + + /* GET_COMPRESS_INFORMATION */ + cmd = P5_X_GET_COMPRESS_INFORMATION; + if (buf[52] != cmd) { + ILI_INFO("Invalid compress & report format info, use default report format resolution\n"); + ilits->compress_disable = true; + ilits->compress_handonly_disable = true; + ilits->compress_penonly_disable = true; + ilits->rib.nReportResolutionMode = POSITION_LOW_RESOLUTION; + ilits->rib.nCustomerType = POSITION_CUSTOMER_TYPE_OFF; + ilits->PenType = POSITION_PEN_TYPE_OFF; + if (ilits->chip->core_ver >= CORE_VER_1700) { + ilits->customertype_off = POSITION_CUSTOMER_TYPE_OFF_3BITS; + } else { + ilits->customertype_off = POSITION_CUSTOMER_TYPE_OFF; + } + } else { + /* GET_COMPRESS_INFORMATION(0x2B) */ + ilits->compress_disable = (buf[53] & 0x20) >> 5; + ilits->compress_handonly_disable = (buf[53] & 0x40) >> 6; + ilits->compress_penonly_disable = (buf[53] & 0x80) >> 7; + /* GET_REPORT_FORMAT(0x37) */ + ilits->rib.nReportResolutionMode = buf[54] & 0x07; + + /* note : After core ver 1.7.0.0 support 0x2F cmd, so ignore before case */ + ilits->rib.nCustomerType = (buf[54] >> 3) & 0x07; + ilits->PenType = buf[54] >> 6; + ilits->customertype_off = POSITION_CUSTOMER_TYPE_OFF_3BITS; + + if (ilits->PenType == POSITION_PEN_TYPE_ON) { + ilits->touch_num = MAX_TOUCH_NUM + MAX_PEN_NUM; + ili_set_tp_data_len(DATA_FORMAT_DEMO, false, NULL); + ILI_INFO("Panel support Pen Mode, Packet Len = %d, Touch Number = %d\n", ilits->tp_data_len, ilits->touch_num); + } + + /* reserved : buf[55] ~ buf[56] = 0xFF */ + } + +#if ENABLE_PEN_MODE + /* GET_PEN_INFO */ + cmd = P5_X_GET_PEN_INFO; + if (buf[57] != cmd) { + ILI_INFO("Invalid Pen info, use default report format resolution\n"); + ilits->pen_info_block.nPxRaw = P5_X_PEN_INFO_X_DATA_LEN; + ilits->pen_info_block.nPyRaw = P5_X_PEN_INFO_Y_DATA_LEN; + ilits->pen_info_block.nPxVa = 1; + ilits->pen_info_block.nPyVa = 1; + } else { + ilits->pen_info_block.nPxRaw = buf[58]; + ilits->pen_info_block.nPyRaw = buf[59]; + ilits->pen_info_block.nPxVa = buf[60]; + ilits->pen_info_block.nPyVa = buf[61]; + } + /* reserved : buf[62] ~ buf[65] = 0xFF */ +#endif + +#if ENABLE_CASCADE + /* GET_CASCADE_INFO */ + cmd = P5_X_GET_CASCADE_INFO; + if (buf[66] != cmd) { + ilits->cascade_info_block.nDisable = ENABLE; + ilits->cascade_info_block.nNum = 2; + } else { + ilits->cascade_info_block.nDisable = buf[67] & BIT(0); + ilits->cascade_info_block.nNum = (buf[67] >> 1) & 0x07; + } +#else + ilits->cascade_info_block.nDisable = ENABLE; + ilits->cascade_info_block.nNum = 0; +#endif + } else { + ILI_ERR("Read 0x2F error, buf[0] = 0x%X\n", buf[0]); + get_all_info = false; + } + + if (get_all_info == true) { + ILI_INFO("Firmware version(0x%8X) = %d.%d.%d.%d\n",ilits->chip->fw_ver , buf[16], buf[17], buf[18], buf[19]); + ILI_INFO("Firmware MP version(0x%8X) = %d.%d.%d.%d\n",ilits->chip->fw_mp_ver , buf[20], buf[21], buf[22], buf[23]); + ILI_INFO("Core version = %d.%d.%d.%d\n", buf[25], buf[26], buf[27], buf[28]); + ILI_INFO("TP Info: min_x = %d, min_y = %d, max_x = %d, max_y = %d\n", ilits->min_x, ilits->min_y, ilits->max_x, ilits->max_y); + ILI_INFO("TP Info: xch = %d, ych = %d, stx = %d, srx = %d\n", ilits->xch_num, ilits->ych_num, ilits->stx, ilits->srx); + ILI_INFO("Compress Info : compress_disable = %d, compress_handonly_disable = %d, compress_penonly_disable = %d\n",ilits->compress_disable, ilits->compress_handonly_disable, ilits->compress_penonly_disable); + ILI_INFO("Pen Len Info : PxRaw = %d, PyRaw = %d, PxVa = %d, PyVa = %d\n", ilits->pen_info_block.nPxRaw, ilits->pen_info_block.nPyRaw, ilits->pen_info_block.nPxVa, ilits->pen_info_block.nPyVa); + ILI_INFO("CASCADE Info : cascade_info_block.nDisable = %d, cascade_info_block.nNum = %d\n", ilits->cascade_info_block.nDisable, ilits->cascade_info_block.nNum); + ILI_INFO("Panel info: width = %d, height = %d\n", ilits->panel_wid, ilits->panel_hei); + ILI_INFO("Transfer touch coordinate = %s, Customer Type = %X, Report Resolution Format Mode = %X\n", ilits->trans_xy ? "ON" : "OFF", ilits->rib.nCustomerType, ilits->rib.nReportResolutionMode); + } else { + ili_ic_get_core_ver(); + ili_ic_get_protocl_ver(); + ili_ic_get_fw_ver(); + ili_ic_get_tp_info(); + ili_ic_get_panel_info(); + ili_ic_get_compress_info(); + } +#else + ili_ic_get_core_ver(); + ili_ic_get_protocl_ver(); + ili_ic_get_fw_ver(); + ili_ic_get_tp_info(); + ili_ic_get_panel_info(); + ili_ic_get_compress_info(); +#endif + + return ret; +} + +static void ilitek_tddi_ic_check_protocol_ver(u32 pver) +{ + int i = 0; + + if (ilits->protocol->ver == pver) { + ILI_DBG("same procotol version, do nothing\n"); + return; + } + + for (i = 0; i < PROTOCL_VER_NUM - 1; i++) { + if (protocol_info[i].ver == pver) { + ilits->protocol = &protocol_info[i]; + ILI_INFO("update protocol version = %x\n", ilits->protocol->ver); + return; + } + } + + ILI_ERR("Not found a correct protocol version in list, use newest version\n"); + ilits->protocol = &protocol_info[PROTOCL_VER_NUM - 1]; +} + +int ili_ic_get_protocl_ver(void) +{ + int ret = 0; + u8 cmd[2] = {0}; + u8 buf[10] = {0}; + u32 ver; + + if (ilits->info_from_hex) { + buf[1] = ilits->fw_info[72]; + buf[2] = ilits->fw_info[73]; + buf[3] = ilits->fw_info[74]; + goto out; + } + + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = P5_X_GET_PROTOCOL_VERSION; + + if (ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Write protocol ver pre cmd failed\n"); + ret = -EINVAL; + goto out; + + } + + if (ilits->wrapper(&cmd[1], sizeof(u8), buf, ilits->protocol->pro_ver_len, ON, OFF) < 0) { + ILI_ERR("Read protocol version error\n"); + ret = -EINVAL; + goto out; + } + + if (buf[0] != P5_X_GET_PROTOCOL_VERSION) { + ILI_ERR("Invalid protocol ver\n"); + ret = -1; + goto out; + } + +out: + ver = buf[1] << 16 | buf[2] << 8 | buf[3]; + + ilitek_tddi_ic_check_protocol_ver(ver); + + ILI_INFO("Protocol version = %d.%d.%d\n", ilits->protocol->ver >> 16, + (ilits->protocol->ver >> 8) & 0xFF, ilits->protocol->ver & 0xFF); + return ret; +} + +int ili_ic_get_info(void) +{ + int ret = 0; + u8 ddi_data = 0; + + if (!atomic_read(&ilits->ice_stat)) { + ILI_ERR("ice mode doesn't enable\n"); + return -1; + } + + + if (ili_ice_mode_read(ilits->chip->pid_addr, &ilits->chip->pid, sizeof(u32)) < 0) + ILI_ERR("Read chip pid error\n"); + + if (ili_ice_mode_read(ilits->chip->otp_addr, &ilits->chip->otp_id, sizeof(u32)) < 0) + ILI_ERR("Read otp id error\n"); + if (ili_ice_mode_read(ilits->chip->ana_addr, &ilits->chip->ana_id, sizeof(u32)) < 0) + ILI_ERR("Read ana id error\n"); + + ilits->chip->id = ilits->chip->pid >> 16; + ilits->chip->type = (ilits->chip->pid & 0x0000FF00) >> 8; + ilits->chip->ver = ilits->chip->pid & 0xFF; + ilits->chip->otp_id &= 0xFF; + ilits->chip->ana_id &= 0xFF; + + ilits->chip->product_id = ilits->chip->id; + if (ilits->chip->id == ILI9882_CHIP) { + ili_ic_get_ddi_reg_onepage(0x6, 0xF3, &ddi_data, OFF); + if (ddi_data == 0x30) + ilits->chip->product_id = ILI2882_CHIP; + } + + ILI_INFO("CHIP ID = %x\n", ilits->chip->product_id); + + ret = ilitek_tddi_ic_check_info(ilits->chip->pid, ilits->chip->id); + return ret; +} + +int ili_cascade_ic_get_info(bool enter_ice, bool exit_ice) +{ + int ret = 0; + +#if (TDDI_INTERFACE == BUS_I2C) + if (enter_ice) { + ili_ice_mode_ctrl_by_mode(ENABLE, OFF, BOTH); + } + + if (ili_ice_mode_read_by_mode(ilits->chip->pid_addr, &ilits->chip->slave_pid, sizeof(u32), SLAVE) < 0) + ILI_ERR("Read Slave chip pid error\n"); + + + ili_ic_get_info(); + +#else + if (enter_ice) { + ili_set_bypass_mode(OFF); + } + + ili_ic_get_info(); + + ili_mspi_read_slave_register(TDDI_PID_ADDR, &ilits->chip->slave_pid, sizeof(u32)); + + /* set mspi 0 */ + if (ili_ice_mode_write_by_mode(MSPI_REG, 0x00, 1, MASTER) < 0) { + ILI_ERR("Failed to write MSPI_REG in ice mode\n"); + } + +#endif + + if (exit_ice) { + ili_ice_mode_ctrl_by_mode(DISABLE, OFF, BOTH); + } + + ilits->chip->slave_id = ilits->chip->slave_pid >> 16; + + if (ilits->chip->pid != ilits->chip->slave_pid) { + ILI_ERR("Slave chip pid different from Master\n"); + ret = -1; + } + + ILI_INFO("Read Slave CHIP PID = 0x%X, CHIP ID = 0x%X\n", ilits->chip->slave_pid, ilits->chip->slave_id); + return ret; +} + +int ili_ic_dummy_check(void) +{ + int ret = 0; + u32 wdata = 0xA55A5AA5; + u32 rdata = 0; +#if (ENGINEER_FLOW) + int i = 0; + if (!atomic_read(&ilits->ice_stat)) { + ILI_ERR("ice mode doesn't enable\n"); + return -1; + } + + for (i = 0; i < 3; i++) { + if (ili_ice_mode_write(WDT9_DUMMY2, wdata, sizeof(u32)) < 0) + ILI_ERR("Write dummy error\n"); + + if (ili_ice_mode_read(WDT9_DUMMY2, &rdata, sizeof(u32)) < 0) + ILI_ERR("Read dummy error\n"); + + if (rdata == wdata || rdata == (u32)-wdata) { + if (rdata == -wdata) { + ilits->eng_flow = true; + } else { + ilits->eng_flow = false; + } + break; + } + mdelay(30); + } + if (i >= 3) { + ILI_ERR("Dummy check incorrect, rdata = %x wdata = %x \n", rdata, wdata); + return -1; + } + ILI_INFO("Ilitek IC check successe ilits->eng_flow = %d\n", ilits->eng_flow); +#else + if (!atomic_read(&ilits->ice_stat)) { + ILI_ERR("ice mode doesn't enable\n"); + return -1; + } + + if (ili_ice_mode_write(WDT9_DUMMY2, wdata, sizeof(u32)) < 0) + ILI_ERR("Write dummy error\n"); + + + if (ili_ice_mode_read(WDT9_DUMMY2, &rdata, sizeof(u32)) < 0) + ILI_ERR("Read dummy error\n"); + + if (rdata != wdata) { + ILI_ERR("Dummy check incorrect, rdata = %x wdata = %x \n", rdata, wdata); + return -1; + } + ILI_INFO("Ilitek IC check successe\n"); +#endif + return ret; +} + +int ili_ic_report_rate_set(u8 mode) +{ + int ret = 0; + u8 cmd[4] = {0}; + ILI_INFO("current report rate mode = %x\n", ilits->current_report_rate_mode); + + if (mode == ilits->current_report_rate_mode) { + ILI_INFO("set mode = %x, same as current mode = %x\n", mode, ilits->current_report_rate_mode); + return ret; + } + + cmd[0] = 0x01; + cmd[1] = 0x1F; + cmd[2] = 0x00; + cmd[3] = mode; + + ret = ilits->wrapper(cmd, 4, NULL, 0, OFF, OFF); + + if (ret < 0) + ILI_ERR("Write TP function failed\n"); + + ilits->current_report_rate_mode = mode; + return ret; +} + +int ili_ic_check_debug_lite_support(int format, bool send, u8 *data) +{ + u8 cmd[12] = {0}, buf[4] = {0}, ctrl = 0, debug_ctrl = 0, data_type = 0; + int ret; + if (ilits->chip->core_ver < CORE_VER_1700) { + ILI_INFO("no support check debug lite\n"); + return -1; + } + + if (data == NULL) { + data_type = P5_X_FW_SIGNAL_DATA_MODE; + ILI_INFO("Data Type is Null, Set Single data type\n"); + } else { + data_type = data[0]; + ILI_INFO("Set data type = 0x%X \n", data[0]); + } + + switch (format) { + case DATA_FORMAT_DEBUG_LITE_ROI: + debug_ctrl = DATA_FORMAT_DEBUG_LITE_ROI_CMD; + ctrl = DATA_FORMAT_DEBUG_LITE_CMD; + break; + case DATA_FORMAT_DEBUG_LITE_WINDOW: + debug_ctrl = DATA_FORMAT_DEBUG_LITE_WINDOW_CMD; + ctrl = DATA_FORMAT_DEBUG_LITE_CMD; + break; + case DATA_FORMAT_DEBUG_LITE_AREA: + if (data == NULL) { + ILI_ERR("DATA_FORMAT_DEBUG_LITE_AREA error cmd\n"); + return -1; + } + debug_ctrl = DATA_FORMAT_DEBUG_LITE_AREA_CMD; + ctrl = DATA_FORMAT_DEBUG_LITE_CMD; + data_type = data[0]; + cmd[4] = data[1]; + cmd[5] = data[2]; + cmd[6] = data[3]; + cmd[7] = data[4]; + break; + } + cmd[0] = P5_X_NEW_CONTROL_FORMAT; + cmd[1] = ctrl; + cmd[2] = data_type; + cmd[3] = debug_ctrl; + cmd[9] = 0x0A; /* pre command */ + + ret = ilits->wrapper(cmd, 10, buf, 2, ON, OFF); + if (ret < 0) { + ILI_ERR("check debug lite function failed\n"); + return ret; + } + + ILI_DBG("check ,0x%X\n", buf[0]); + return buf[0]; +} + +int ili_ic_report_rate_get(void) +{ + int ret = 0; + u8 cmd[3] = {0}, rxbuf[4] = {0}; + + cmd[0] = 0x01; + cmd[1] = 0x1F; + cmd[2] = 0x01; + + ret = ilits->wrapper(cmd, 3, rxbuf, 4, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write TP function failed\n"); + return ret; + } + ILI_INFO("get report rate mode = %x, rxbuf = %x %x %x %x \n", rxbuf[3], rxbuf[0], rxbuf[1], rxbuf[2], rxbuf[3]); + ret = rxbuf[3]; + return ret; +} + +int ili_ic_report_rate_register_set(void) +{ + int ret = 0; + + if (ili_ice_mode_ctrl(ENABLE, OFF) < 0) + ILI_ERR("Enable ice mode failed\n"); + ILI_INFO("current_report_rate_mode: %X when reset\n", ilits->current_report_rate_mode); + ret = ili_ice_mode_write(0x4005E, (0x5A00 | (ilits->current_report_rate_mode & 0xFF)), 2); + if (ret < 0) + ILI_ERR("write report rate password failed\n"); + + return ret; +} + +static struct ilitek_ic_info chip; + +void ili_ic_init(void) +{ + chip.pid_addr = TDDI_PID_ADDR; + chip.pc_counter_addr = TDDI_PC_COUNTER_ADDR; + chip.pc_latch_addr = TDDI_PC_LATCH_ADDR; + chip.otp_addr = TDDI_OTP_ID_ADDR; + chip.ana_addr = TDDI_ANA_ID_ADDR; + chip.reset_addr = TDDI_CHIP_RESET_ADDR; + + ilits->protocol = &protocol_info[PROTOCL_VER_NUM - 1]; + ilits->chip = &chip; +} diff --git a/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_mp.c b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_mp.c new file mode 100644 index 000000000000..4880d4148b38 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_mp.c @@ -0,0 +1,4641 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek_v3.h" + +#define VALUE 0 +#define RETRY_COUNT 3 +#define INT_CHECK 0 +#define POLL_CHECK 1 + +#define BENCHMARK 1 +#define NODETYPE 1 + +#define TYPE_BENCHMARK 0 +#define TYPE_NO_JUGE 1 +#define TYPE_JUGE 2 + +#define NORMAL_CSV_PASS_NAME "mp_pass" +#define NORMAL_CSV_FAIL_NAME "mp_fail" +#define NORMAL_CSV_WARNING_NAME "mp_warning" + +#define CSV_FILE_SIZE (1 * M) + +#define PARSER_MAX_CFG_BUF (512 * 3) +#define PARSER_MAX_KEY_NUM (600 * 3) +#define PARSER_MAX_KEY_NAME_LEN 100 +#define PARSER_MAX_KEY_VALUE_LEN 2000 +#define MAX_SECTION_NUM 100 +#define BENCHMARK_KEY_NAME "benchmark_data" +#define NODE_TYPE_KEY_NAME "node type" +#define PRECOMMAND_KEY_NAME "_precommand" +#define INI_ERR_OUT_OF_LINE -1 + +#define CMD_MUTUAL_DAC 0x1 +#define CMD_MUTUAL_BG 0x2 +#define CMD_MUTUAL_SIGNAL 0x3 +#define CMD_MUTUAL_NO_BK 0x5 +#define CMD_MUTUAL_HAVE_BK 0x8 +#define CMD_MUTUAL_BK_DAC 0x10 +#define CMD_SELF_DAC 0xC +#define CMD_SELF_BG 0xF +#define CMD_SELF_SIGNAL 0xD +#define CMD_SELF_NO_BK 0xE +#define CMD_SELF_HAVE_BK 0xB +#define CMD_SELF_BK_DAC 0x11 +#define CMD_KEY_DAC 0x14 +#define CMD_KEY_BG 0x16 +#define CMD_KEY_NO_BK 0x7 +#define CMD_KEY_HAVE_BK 0x15 +#define CMD_KEY_OPEN 0x12 +#define CMD_KEY_SHORT 0x13 +#define CMD_ST_DAC 0x1A +#define CMD_ST_BG 0x1C +#define CMD_ST_NO_BK 0x17 +#define CMD_ST_HAVE_BK 0x1B +#define CMD_ST_OPEN 0x18 +#define CMD_TX_SHORT 0x19 +#define CMD_RX_SHORT 0x4 +#define CMD_RX_OPEN 0x6 +#define CMD_TX_RX_DELTA 0x1E +#define CMD_CM_DATA 0x9 +#define CMD_CS_DATA 0xA +#define CMD_TRCRQ_PIN 0x20 +#define CMD_RESX2_PIN 0x21 +#define CMD_MUTUAL_INTEGRA_TIME 0x22 +#define CMD_SELF_INTEGRA_TIME 0x23 +#define CMD_KEY_INTERGRA_TIME 0x24 +#define CMD_ST_INTERGRA_TIME 0x25 +#define CMD_PEAK_TO_PEAK 0x1D +#define CMD_GET_TIMING_INFO 0x30 +#define CMD_DOZE_P2P 0x32 +#define CMD_DOZE_RAW 0x33 +#define CMD_PIN_TEST 0x61 +#define CMD_FW_MP_PRE_CMD 0xF7 +#define CMD_FW_MP_PRE_SET_CMD 0xF8 + + +#define MP_DATA_PASS 0 +#define MP_DATA_FAIL -1 + +#define Mathabs(x) ({ \ + long ret; \ + if (sizeof(x) == sizeof(long)) { \ + long __x = (x); \ + ret = (__x < 0) ? -__x : __x; \ + } else { \ + int __x = (x); \ + ret = (__x < 0) ? -__x : __x; \ + } \ + ret; \ + }) + +#define DUMP(fmt, arg...) \ + do { \ + if (debug_en) \ + pr_cont(fmt, ##arg); \ + } while (0) + +static struct ini_file_data { + char section_name[PARSER_MAX_KEY_NAME_LEN]; + char key_name[PARSER_MAX_KEY_NAME_LEN]; + char key_value[PARSER_MAX_KEY_VALUE_LEN]; + int section_len; + int key_name_len; + int key_val_len; +} *ini_info; + +enum open_test_node_type { + NO_COMPARE = 0x00, /* Not A Area, No Compare */ + AA_Area = 0x01, /* AA Area, Compare using Charge_AA */ + Border_Area = 0x02, /* Border Area, Compare using Charge_Border */ + Notch = 0x04, /* Notch Area, Compare using Charge_Notch */ + Round_Corner = 0x08, /* Round Corner, No Compare */ + Skip_Micro = 0x10 /* Skip_Micro, No Compare */ +}; + +enum mp_test_catalog { + MUTUAL_TEST = 0, + SELF_TEST = 1, + KEY_TEST = 2, + ST_TEST = 3, + TX_RX_DELTA = 4, + UNTOUCH_P2P = 5, + PIXEL = 6, + OPEN_TEST = 7, + PEAK_TO_PEAK_TEST = 8, + SHORT_TEST = 9, + PIN_TEST = 10, + TIPRING_RAW = 11, + TIPRING_P2P = 12, +}; + +enum mp_tipring_type { + TIPRING_XY = 0, + TIPRING_X, + TIPRING_Y +}; + +struct mp_test_P540_open { + s32 *tdf_700; + s32 *tdf_250; + s32 *tdf_200; + s32 *cbk_700; + s32 *cbk_250; + s32 *cbk_200; + s32 *charg_rate; + s32 *full_Open; + s32 *dac; +}; + +struct mp_test_open_c { + s32 *cap_dac; + s32 *cap_raw; + s32 *dcl_cap; +}; + +static struct open_test_para { + int tvch; + int tvcl; + int gain; + int cbk_step; + int cint; + int invbk; + int vadc_range; +} open_para; + +static struct shor_test_para { + int tvch; + int tvcl; + int variation; + int rinternal; + int cint; +} short_para; + +static struct core_mp_test_data { + u32 chip_pid; + u16 chip_id; + u8 chip_type; + u8 chip_ver; + u32 fw_ver; + u32 protocol_ver; + u32 core_ver; + char ini_date[128]; + char ini_ver[64]; + int no_bk_shift; + bool retry; + bool m_signal; + bool m_dac; + bool s_signal; + bool s_dac; + bool key_dac; + bool st_dac; + bool p_no_bk; + bool p_has_bk; + bool open_integ; + bool open_cap; + bool isLongV; + bool td_retry; + bool pre_cmd_sp; + + int cdc_len; + int xch_len; + int ych_len; + int nPxRaw; + int nPyRaw; + int stx_len; + int srx_len; + int key_len; + int st_len; + int frame_len; + int mp_items; + int final_result; + int short_varia; + int nPxRaw_tipring_len; + int nPyRaw_tipring_len; + int tipring_len; + u32 overlay_start_addr; + u32 overlay_end_addr; + u32 mp_flash_addr; + u32 mp_size; + u8 dma_trigger_enable; + + /* Tx/Rx threshold & buffer */ + int TxDeltaMax; + int TxDeltaMin; + int RxDeltaMax; + int RxDeltaMin; + s32 *tx_delta_buf; + s32 *rx_delta_buf; + s32 *tx_max_buf; + s32 *tx_min_buf; + s32 *rx_max_buf; + s32 *rx_min_buf; + + int tdf; + int busy_cdc; + bool ctrl_lcm; + bool lost_benchmark; + bool lost_parameter; +} core_mp = {0}; + +struct mp_test_items { + /* The description must be the same as ini's section name */ + char *desp; + char *result; + int catalog; + u8 cmd; + u8 spec_option; + u8 type_option; + bool run; + bool lcm; + int bch_mrk_multi; + int max; + int max_res; + int item_result; + int min; + int min_res; + int max1; + int max2; + int min1; + int min2; + int frame_count; + int trimmed_mean; + int lowest_percentage; + int highest_percentage; + int v_tdf_1; + int v_tdf_2; + int h_tdf_1; + int h_tdf_2; + int goldenmode; + int max_min_mode; + int bch_mrk_frm_num; + int retry_cnt; + int pre_cmd_en; + u8 delay_time; + u8 test_int_pin; + u8 int_pulse_test; + s32 *result_buf; + s32 *buf; + s32 *max_buf; + s32 *min_buf; + s32 *bench_mark_max; + s32 *bench_mark_min; + s32 **bch_mrk_max; + s32 **bch_mrk_min; + s32 *node_type; + int (*do_test)(int index); +}; + +#define MP_TEST_ITEM 55 +static struct mp_test_items tItems[MP_TEST_ITEM] = { + {.desp = "baseline data(bg)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_BG, .lcm = ON}, + {.desp = "untouch signal data(bg-raw-4096) - mutual", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_SIGNAL, .lcm = ON}, + {.desp = "manual bk data(mutual)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_BK_DAC, .lcm = ON}, + {.desp = "calibration data(dac) - self", .catalog = SELF_TEST, .cmd = CMD_SELF_DAC, .lcm = ON}, + {.desp = "baselin data(bg,self_tx,self_r)", .catalog = SELF_TEST, .cmd = CMD_SELF_BG, .lcm = ON}, + {.desp = "untouch signal data(bg-raw-4096) - self", .catalog = SELF_TEST, .cmd = CMD_SELF_SIGNAL, .lcm = ON}, + {.desp = "raw data(no bk) - self", .catalog = SELF_TEST, .cmd = CMD_SELF_NO_BK, .lcm = ON}, + {.desp = "raw data(have bk) - self", .catalog = SELF_TEST, .cmd = CMD_SELF_HAVE_BK, .lcm = ON}, + {.desp = "manual bk dac data(self_tx,self_rx)", .catalog = SELF_TEST, .cmd = CMD_SELF_BK_DAC, .lcm = ON}, + {.desp = "calibration data(dac/icon)", .catalog = KEY_TEST, .cmd = CMD_KEY_DAC, .lcm = ON}, + {.desp = "key baseline data", .catalog = KEY_TEST, .cmd = CMD_KEY_BG, .lcm = ON}, + {.desp = "key raw data", .catalog = KEY_TEST, .cmd = CMD_KEY_NO_BK, .lcm = ON}, + {.desp = "key raw bk dac", .catalog = KEY_TEST, .cmd = CMD_KEY_HAVE_BK, .lcm = ON}, + {.desp = "key raw open test", .catalog = KEY_TEST, .cmd = CMD_KEY_OPEN, .lcm = ON}, + {.desp = "key raw short test", .catalog = KEY_TEST, .cmd = CMD_KEY_SHORT, .lcm = ON}, + {.desp = "st calibration data(dac)", .catalog = ST_TEST, .cmd = CMD_ST_DAC, .lcm = ON}, + {.desp = "st baseline data(bg)", .catalog = ST_TEST, .cmd = CMD_ST_BG, .lcm = ON}, + {.desp = "st raw data(no bk)", .catalog = ST_TEST, .cmd = CMD_ST_NO_BK, .lcm = ON}, + {.desp = "st raw(have bk)", .catalog = ST_TEST, .cmd = CMD_ST_HAVE_BK, .lcm = ON}, + {.desp = "st open data", .catalog = ST_TEST, .cmd = CMD_ST_OPEN, .lcm = ON}, + {.desp = "tx short test", .catalog = MUTUAL_TEST, .cmd = CMD_TX_SHORT, .lcm = ON}, + {.desp = "rx open", .catalog = MUTUAL_TEST, .cmd = CMD_RX_OPEN, .lcm = ON}, + {.desp = "untouch cm data", .catalog = MUTUAL_TEST, .cmd = CMD_CM_DATA, .lcm = ON}, + {.desp = "untouch cs data", .catalog = MUTUAL_TEST, .cmd = CMD_CS_DATA, .lcm = ON}, + {.desp = "tx/rx delta", .catalog = TX_RX_DELTA, .cmd = CMD_TX_RX_DELTA, .lcm = ON}, + {.desp = "untouch peak to peak", .catalog = UNTOUCH_P2P, .cmd = CMD_MUTUAL_SIGNAL, .lcm = ON}, + {.desp = "pixel raw (no bk)", .catalog = PIXEL, .cmd = CMD_MUTUAL_NO_BK, .lcm = ON}, + {.desp = "pixel raw (have bk)", .catalog = PIXEL, .cmd = CMD_MUTUAL_HAVE_BK, .lcm = ON}, + {.desp = "noise peak to peak(cut panel)", .catalog = PEAK_TO_PEAK_TEST, .lcm = ON}, + {.desp = "open test(integration)", .catalog = OPEN_TEST, .cmd = CMD_RX_SHORT, .lcm = ON}, + {.desp = "open test(cap)", .catalog = OPEN_TEST, .cmd = CMD_RX_SHORT, .lcm = ON}, + /* Following is the new test items for protocol 5.4.0 above */ + {.desp = "pin test ( int and rst )", .catalog = PIN_TEST, .cmd = CMD_PIN_TEST, .lcm = ON}, + {.desp = "noise peak to peak(with panel)", .catalog = PEAK_TO_PEAK_TEST, .lcm = ON}, + {.desp = "noise peak to peak(ic only)", .catalog = PEAK_TO_PEAK_TEST, .cmd = CMD_PEAK_TO_PEAK, .lcm = ON}, + {.desp = "open test(integration)_sp", .catalog = OPEN_TEST, .lcm = ON}, + {.desp = "raw data(no bk)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_NO_BK, .lcm = ON}, + {.desp = "raw data(have bk)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_HAVE_BK, .lcm = ON}, + {.desp = "calibration data(dac)", .catalog = MUTUAL_TEST, .cmd = CMD_MUTUAL_DAC, .lcm = ON}, + {.desp = "short test -ili9881", .catalog = SHORT_TEST, .cmd = CMD_RX_SHORT, .lcm = ON}, + {.desp = "short test", .catalog = SHORT_TEST, .lcm = ON}, + {.desp = "doze raw data", .catalog = MUTUAL_TEST, .lcm = ON}, + {.desp = "doze peak to peak", .catalog = PEAK_TO_PEAK_TEST, .lcm = ON}, + {.desp = "open test_c", .catalog = OPEN_TEST, .lcm = ON}, + {.desp = "touch deltac", .catalog = MUTUAL_TEST, .lcm = ON}, + {.desp = "pen tipring raw test", .catalog = TIPRING_RAW, .lcm = ON}, + {.desp = "pen tipring p2p test", .catalog = TIPRING_P2P, .lcm = ON}, + {.desp = "open test_c (openx enable)", .catalog = OPEN_TEST, .lcm = ON}, + /* LCM OFF TEST */ + {.desp = "raw data(have bk) (lcm off)", .catalog = MUTUAL_TEST, .lcm = OFF}, + {.desp = "raw data(no bk) (lcm off)", .catalog = MUTUAL_TEST, .lcm = OFF}, + {.desp = "noise peak to peak(with panel) (lcm off)", .catalog = PEAK_TO_PEAK_TEST, .lcm = OFF}, + {.desp = "noise peak to peak(ic only) (lcm off)", .catalog = PEAK_TO_PEAK_TEST, .lcm = OFF}, + {.desp = "raw data_td (lcm off)", .catalog = MUTUAL_TEST, .lcm = OFF}, + {.desp = "peak to peak_td (lcm off)", .catalog = PEAK_TO_PEAK_TEST, .lcm = OFF}, + {.desp = "pen tipring raw test (lcm off)", .catalog = TIPRING_RAW, .lcm = OFF}, + {.desp = "pen tipring p2p test (lcm off)", .catalog = TIPRING_P2P, .lcm = OFF}, +}; + +static struct run_index { + int count; + int index[MP_TEST_ITEM]; +} ri; + +static s32 *frame_buf; +static s32 **frm_buf; +static s32 *key_buf; +static s32 *frame1_cbk700, *frame1_cbk250, *frame1_cbk200; +static s32 *cap_dac, *cap_raw; +static s32 *openx_cap_dac, *openx_cap_raw; +static int g_ini_items; +static char csv_name[128] = {0}; +static char seq_item[MAX_SECTION_NUM][PARSER_MAX_KEY_NAME_LEN] = {{0}}; + +static int isspace_t(int x) +{ + if (x == ' ' || x == '\t' || x == '\n' || + x == '\f' || x == '\b' || x == '\r') + return 1; + else + return 0; +} + +static void dump_benchmark_data(s32 *max_ptr, s32 *min_ptr) +{ + ili_dump_data(max_ptr, 32, core_mp.frame_len, 0, "Dump Benchmark Max : "); + ili_dump_data(min_ptr, 32, core_mp.frame_len, 0, "Dump Benchmark Min : "); +} + +static void dump_node_type_buffer(s32 *node_ptr, u8 *name) +{ + ili_dump_data(node_ptr, 32, core_mp.frame_len, 0, "Dump NodeType : "); +} + +static int parser_get_ini_key_value(char *section, char *key, char *value) +{ + int i = 0; + int ret = -2; + + for (i = 0; i < g_ini_items; i++) { + if (ipio_strcmp(section, ini_info[i].section_name) != 0) + continue; + + if (ipio_strcmp(key, ini_info[i].key_name) == 0) { + ipio_memcpy(value, ini_info[i].key_value, ini_info[i].key_val_len, PARSER_MAX_KEY_VALUE_LEN); + ILI_DBG("(key: %s, value:%s) => (ini key: %s, val: %s)\n", + key, + value, + ini_info[i].key_name, + ini_info[i].key_value); + ret = 0; + break; + } + } + return ret; +} + +static void parser_ini_nodetype(s32 *type_ptr, char *desp, int frame_len) +{ + int i = 0, j = 0, index1 = 0, temp, count = 0; + char str[512] = {0}, record = ','; + + for (i = 0; i < g_ini_items; i++) { + if (!strstr(ini_info[i].section_name, desp) || + ipio_strcmp(ini_info[i].key_name, NODE_TYPE_KEY_NAME) != 0) { + continue; + } + + record = ','; + for (j = 0, index1 = 0; j <= ini_info[i].key_val_len; j++) { + if (ini_info[i].key_value[j] == ';' || j == ini_info[i].key_val_len) { + if (record != '.') { + memset(str, 0, sizeof(str)); + ipio_memcpy(str, &ini_info[i].key_value[index1], (j - index1), sizeof(str)); + temp = ili_katoi(str); + + /* Over boundary, end to calculate. */ + if (count >= frame_len) { + ILI_ERR("count(%d) is larger than frame length, break\n", count); + break; + } + type_ptr[count] = temp; + count++; + } + record = ini_info[i].key_value[j]; + index1 = j + 1; + } + } + } +} + +static int parser_sramtest_group(char *str, char (*strSramPartialArea)[SRAM_TEST_GROUP_STR_LEN]) +{ + const char *delim = ";"; + char *temp = NULL; + char *pstrtemp = str; + u8 group_num = 0; + + ILI_INFO("Parsing Sram group\n"); + + temp = strsep(&pstrtemp, delim); + strcpy(strSramPartialArea[group_num], temp); + + while(temp != NULL) { + ILI_DBG("temp = %s, Sram Partial Area[%d] = %s\n", temp, group_num, strSramPartialArea[group_num]); + group_num++; + temp = strsep(&pstrtemp, delim); + if (temp == NULL) { + ILI_INFO("Parsing Sram group Done\n"); + break; + } + strcpy(strSramPartialArea[group_num], temp); + } + + return group_num; +} + +static void parser_ini_benchmark(s32 *max_ptr, s32 *min_ptr, int8_t type, char *desp, int frame_len, char *bchmrk_name) +{ + int i = 0, j = 0, index1 = 0, temp, count = 0; + char str[512] = {0}, record = ','; + s32 data[4]; + char benchmark_str[256] = {0}; + + /* format complete string from the name of section "_Benchmark_Data". */ + snprintf(benchmark_str, sizeof(benchmark_str), "%s%s%s", desp, "_", bchmrk_name); + ILI_DBG("benchmark_str = %s\n", benchmark_str); + + for (i = 0; i < g_ini_items; i++) { + if ((ipio_strcmp(ini_info[i].section_name, benchmark_str) != 0)) + continue; + record = ','; + for (j = 0, index1 = 0; j <= ini_info[i].key_val_len; j++) { + if (ini_info[i].key_value[j] == ',' || ini_info[i].key_value[j] == ';' || + ini_info[i].key_value[j] == '.' || j == ini_info[i].key_val_len) { + + if (record != '.') { + memset(str, 0, sizeof(str)); + ipio_memcpy(str, &ini_info[i].key_value[index1], (j - index1), sizeof(str)); + temp = ili_katoi(str); + data[(count % 4)] = temp; + + /* Over boundary, end to calculate. */ + if ((count / 4) >= frame_len) { + ILI_ERR("count (%d) is larger than frame length, break\n", (count / 4)); + break; + } + + if ((count % 4) == 3) { + if (data[0] == 1) { + if (type == VALUE) { + max_ptr[count/4] = data[1] + data[2]; + min_ptr[count/4] = data[1] - data[3]; + } else { + max_ptr[count/4] = data[1] + (data[1] * data[2]) / 100; + min_ptr[count/4] = data[1] - (data[1] * data[3]) / 100; + } + } else { + max_ptr[count/4] = INT_MAX; + min_ptr[count/4] = INT_MIN; + } + } + count++; + } + record = ini_info[i].key_value[j]; + index1 = j + 1; + } + } + } +} + +static char *get_csv_frame_name(char *name, int num) +{ + static char frame_name[128] = { 0 }; + memset(frame_name, 0, sizeof(frame_name)); + sprintf(frame_name, "%s%d", name, num); + + return frame_name; +} + + +static void parser_tip_ring_ini_benchmark(s32 *max_ptr, s32 *min_ptr, int8_t type, char *desp, int frame_len) { + char benchmark_str[128] = {0}; + + snprintf(benchmark_str, sizeof(benchmark_str), "%s%d", BENCHMARK_KEY_NAME, 1); + parser_ini_benchmark(max_ptr, min_ptr, type, desp, core_mp.nPxRaw_tipring_len, benchmark_str); + + memset(benchmark_str, 0x0, sizeof(benchmark_str)); + snprintf(benchmark_str, sizeof(benchmark_str), "%s%d", BENCHMARK_KEY_NAME, 2); + + parser_ini_benchmark(max_ptr + core_mp.nPxRaw_tipring_len, min_ptr + core_mp.nPxRaw_tipring_len, + type, desp, core_mp.nPyRaw_tipring_len, benchmark_str); + + ili_dump_data(max_ptr, 32, core_mp.tipring_len, 0, "Dump Tip Ring Benchmark Max"); + ili_dump_data(min_ptr, 32, core_mp.tipring_len, 0, "Dump Tip Ring Benchmark Min"); +} + +static int parser_get_tdf_value(char *str, int catalog) +{ + u32 i, ans, index = 0, flag = 0, count = 0, size = 0; + char s[10] = {0}; + + if (!str) { + ILI_ERR("String is null\n"); + return -1; + } + + size = strlen(str); + for (i = 0, count = 0; i < size; i++) { + if (str[i] == '.') { + flag = 1; + continue; + } + s[index++] = str[i]; + if (flag) + count++; + } + ans = ili_katoi(s); + + /* Multiply by 100 to shift out of decimal point */ + if (catalog == SHORT_TEST) { + if (count == 0) + ans = ans * 100; + else if (count == 1) + ans = ans * 10; + } + + return ans; +} + +static int parser_get_u8_array(char *key, u8 *buf, u16 base, int len) +{ + char *s = key; + char *pToken; + int ret, conut = 0; + long s_to_long = 0; + + if (strlen(s) == 0 || len <= 0) { + ILI_ERR("Can't find any characters inside buffer\n"); + return -1; + } + + /* + * @base: The number base to use. The maximum supported base is 16. If base is + * given as 0, then the base of the string is automatically detected with the + * conventional semantics - If it begins with 0x the number will be parsed as a + * hexadecimal (case insensitive), if it otherwise begins with 0, it will be + * parsed as an octal number. Otherwise it will be parsed as a decimal. + */ + if (isspace_t((int)(unsigned char)*s) == 0) { + while ((pToken = strsep(&s, ",")) != NULL) { + ret = kstrtol(pToken, base, &s_to_long); + if (ret == 0) + buf[conut] = s_to_long; + else + ILI_INFO("convert string too long, ret = %d\n", ret); + conut++; + + if (conut >= len) + break; + } + } + + return conut; +} + +static int parser_get_u32_array(char *key, u32 *buf, u16 base, int len) +{ + char *s = key; + char *pToken; + int ret, conut = 0; + long s_to_long = 0; + + if (strlen(s) == 0 || len <= 0) { + ILI_ERR("Can't find any characters inside buffer\n"); + return -1; + } + + /* + * @base: The number base to use. The maximum supported base is 16. If base is + * given as 0, then the base of the string is automatically detected with the + * conventional semantics - If it begins with 0x the number will be parsed as a + * hexadecimal (case insensitive), if it otherwise begins with 0, it will be + * parsed as an octal number. Otherwise it will be parsed as a decimal. + */ + if (isspace_t((int)(unsigned char)*s) == 0) { + while ((pToken = strsep(&s, ";")) != NULL) { + ret = kstrtol(pToken, base, &s_to_long); + if (ret == 0) + buf[conut] = s_to_long; + else + ILI_INFO("convert string too long, ret = %d\n", ret); + conut++; + + if (conut >= len) + break; + } + } + + return conut; +} + +static int parser_get_int_data(char *section, char *keyname, char *rv, int rv_len) +{ + int len = 0; + char value[512] = { 0 }; + + if (rv == NULL || section == NULL || keyname == NULL) { + ILI_ERR("Parameters are invalid\n"); + return -EINVAL; + } + + /* return a white-space string if get nothing */ + if (parser_get_ini_key_value(section, keyname, value) < 0) { + snprintf(rv, rv_len, "%s", value); + return 0; + } + + len = snprintf(rv, rv_len, "%s", value); + return len; +} + +/* Count the number of each line and assign the content to tmp buffer */ +static int parser_get_ini_phy_line(char *data, char *buffer, int maxlen) +{ + int i = 0; + int j = 0; + int iRetNum = -1; + char ch1 = '\0'; + + for (i = 0, j = 0; i < maxlen; j++) { + ch1 = data[j]; + iRetNum = j + 1; + if (ch1 == '\n' || ch1 == '\r') { /* line end */ + ch1 = data[j + 1]; + if (ch1 == '\n' || ch1 == '\r') + iRetNum++; + break; + } else if (ch1 == 0x00) { + break; /* file end */ + } + + buffer[i++] = ch1; + } + + buffer[i] = '\0'; + return iRetNum; +} + +static char *parser_ini_str_trim_r(char *buf) +{ + int len, i; + char *tmp = NULL; + char x[512] = {0}; + char *y = NULL; + char *empty = ""; + + len = strlen(buf); + + if (len < sizeof(x)) { + tmp = x; + goto copy; + } + + y = kzalloc(len, GFP_KERNEL); + if (ERR_ALLOC_MEM(y)) { + ILI_ERR("Failed to allocate tmp buf\n"); + return empty; + } + tmp = y; + +copy: + for (i = 0; i < len; i++) { + if (buf[i] != ' ') + break; + } + + if (i < len) + strncpy(tmp, (buf + i), (len - i)); + + strncpy(buf, tmp, len); + ipio_kfree((void **)&y); + return buf; +} + +static int parser_get_ini_phy_data(char *data, int fsize) +{ + int i, n = 0, ret = 0, empty_section; + int offset = 0, isEqualSign = 0, scount = 0; + char *ini_buf = NULL, *tmpSectionName = NULL; + char M_CFG_SSL = '['; + char M_CFG_SSR = ']'; +/* char M_CFG_NIS = ':'; */ + char M_CFG_NTS = '#'; + char M_CFG_EQS = '='; + + if (data == NULL) { + ILI_ERR("INI data is NULL\n"); + ret = -EINVAL; + goto out; + } + + ini_buf = kzalloc((PARSER_MAX_CFG_BUF + 1) * sizeof(char), GFP_KERNEL); + if (ERR_ALLOC_MEM(ini_buf)) { + ILI_ERR("Failed to allocate ini_buf memory, %ld\n", PTR_ERR(ini_buf)); + ret = -ENOMEM; + goto out; + } + + tmpSectionName = kzalloc((PARSER_MAX_CFG_BUF + 1) * sizeof(char), GFP_KERNEL); + if (ERR_ALLOC_MEM(tmpSectionName)) { + ILI_ERR("Failed to allocate tmpSectionName memory, %ld\n", PTR_ERR(tmpSectionName)); + ret = -ENOMEM; + goto out; + } + + memset(seq_item, 0, MP_TEST_ITEM * PARSER_MAX_KEY_NAME_LEN * sizeof(char)); + + while (true) { + empty_section = 0; + if (g_ini_items > PARSER_MAX_KEY_NUM) { + ILI_ERR("MAX_KEY_NUM: Out of length\n"); + goto out; + } + + if (offset >= fsize) + goto out;/*over size*/ + + n = parser_get_ini_phy_line(data + offset, ini_buf, PARSER_MAX_CFG_BUF); + if (n < 0) { + ILI_ERR("End of Line\n"); + goto out; + } + + offset += n; + + n = strlen(parser_ini_str_trim_r(ini_buf)); + + if (n == 0 || ini_buf[0] == M_CFG_NTS) + continue; + + /* Get section names */ + if (n > 2 && ((ini_buf[0] == M_CFG_SSL && ini_buf[n - 1] != M_CFG_SSR))) { + ILI_ERR("Bad Section: %s\n", ini_buf); + ret = -EINVAL; + goto out; + } else { + if (ini_buf[0] == M_CFG_SSL) { + ini_info[g_ini_items].section_len = n - 2; + if (ini_info[g_ini_items].section_len > PARSER_MAX_KEY_NAME_LEN) { + ILI_ERR("MAX_KEY_NAME_LEN: Out Of Length\n"); + ret = INI_ERR_OUT_OF_LINE; + goto out; + } + + if (scount > MAX_SECTION_NUM) { + ILI_ERR("seq_item is over than its define (%d), abort\n", scount); + ret = INI_ERR_OUT_OF_LINE; + goto out; + } + + ini_buf[n - 1] = 0x00; + strncpy((char *)tmpSectionName, ini_buf + 1, (PARSER_MAX_CFG_BUF + 1) * sizeof(char)); + strncpy(seq_item[scount], tmpSectionName, PARSER_MAX_KEY_NAME_LEN); + scount++; + ILI_DBG("Section Name: %s, Len: %d, offset = %d\n", seq_item[scount], n - 2, offset); + continue; + } + } + + /* copy section's name without square brackets to its real buffer */ + strncpy(ini_info[g_ini_items].section_name, tmpSectionName, (PARSER_MAX_KEY_NAME_LEN * sizeof(char))); + ini_info[g_ini_items].section_len = strlen(tmpSectionName); + + isEqualSign = 0; + for (i = 0; i < n; i++) { + if (ini_buf[i] == M_CFG_EQS) { + isEqualSign = i; + break; + } + if (ini_buf[i] == M_CFG_SSL || ini_buf[i] == M_CFG_SSR) { + empty_section = 1; + break; + } + } + + if (isEqualSign == 0) { + if (empty_section) + continue; + + if (strstr(ini_info[g_ini_items].section_name, BENCHMARK_KEY_NAME) != NULL) { + isEqualSign = -1; + ini_info[g_ini_items].key_name_len = strlen(ini_info[g_ini_items].section_name); + strncpy(ini_info[g_ini_items].key_name, ini_info[g_ini_items].section_name, (PARSER_MAX_KEY_NAME_LEN * sizeof(char))); + ini_info[g_ini_items].key_val_len = n; + } else if (strstr(ini_info[g_ini_items].section_name, NODE_TYPE_KEY_NAME) != NULL) { + isEqualSign = -1; + ini_info[g_ini_items].key_name_len = strlen(NODE_TYPE_KEY_NAME); + strncpy(ini_info[g_ini_items].key_name, NODE_TYPE_KEY_NAME, (PARSER_MAX_KEY_NAME_LEN * sizeof(char))); + ini_info[g_ini_items].key_val_len = n; + } else if (strstr(ini_info[g_ini_items].section_name, PRECOMMAND_KEY_NAME) != NULL) { + isEqualSign = -1; + ini_info[g_ini_items].key_name_len = strlen(ini_info[g_ini_items].section_name); + strncpy(ini_info[g_ini_items].key_name, ini_info[g_ini_items].section_name, (PARSER_MAX_KEY_NAME_LEN * sizeof(char))); + ini_info[g_ini_items].key_val_len = n; + } else { + continue; + } + } else { + ini_info[g_ini_items].key_name_len = isEqualSign; + if (ini_info[g_ini_items].key_name_len > PARSER_MAX_KEY_NAME_LEN) { + /* ret = CFG_ERR_OUT_OF_LEN; */ + ILI_ERR("MAX_KEY_NAME_LEN: Out Of Length\n"); + ret = INI_ERR_OUT_OF_LINE; + goto out; + } + + ipio_memcpy(ini_info[g_ini_items].key_name, ini_buf, + ini_info[g_ini_items].key_name_len, PARSER_MAX_KEY_NAME_LEN); + ini_info[g_ini_items].key_val_len = n - isEqualSign - 1; + } + + if (ini_info[g_ini_items].key_val_len > PARSER_MAX_KEY_VALUE_LEN) { + ILI_ERR("MAX_KEY_VALUE_LEN: Out Of Length\n"); + ret = INI_ERR_OUT_OF_LINE; + goto out; + } + + ipio_memcpy(ini_info[g_ini_items].key_value, + ini_buf + isEqualSign + 1, ini_info[g_ini_items].key_val_len, PARSER_MAX_KEY_VALUE_LEN); + + ILI_DBG("%s = %s\n", ini_info[g_ini_items].key_name, + ini_info[g_ini_items].key_value); + + g_ini_items++; + } +out: + ipio_kfree((void **)&ini_buf); + ipio_kfree((void **)&tmpSectionName); + return ret; +} + +int ilitek_tddi_mp_ini_parser(const char *path) +{ + int i, ret = 0, fsize = 0; + char *tmp = NULL; + const struct firmware *ini = NULL; +#if GENERIC_KERNEL_IMAGE + ILI_ERR("GKI version not allow drivers to use filp_open\n"); + path = ilits->md_ini_rq_path; + ILI_INFO("request path = %s\n", path); + if (request_firmware(&ini, path, ilits->dev) < 0) { + ILI_ERR("Request ini file failed\n"); + return -EINVAL; + } + + fsize = ini->size; +#else + struct file *f = NULL; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + mm_segment_t old_fs; +#endif + loff_t pos = 0; + + ILI_INFO("ini file path = %s\n", path); + + f = filp_open(path, O_RDONLY, 644); + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open ini file at %ld, try to request_firmware\n", PTR_ERR(f)); + f = NULL; + path = ilits->md_ini_rq_path; + ILI_INFO("request path = %s\n", path); + if (request_firmware(&ini, path, ilits->dev) < 0) { + ILI_ERR("Request ini file failed\n"); + return -EINVAL; + } + } + + if (f != NULL) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)) + fsize = f->f_inode->i_size; +#else + struct inode *inode; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)) + inode = f->f_dentry->d_inode; +#else + inode = f->f_path.dentry->d_inode; +#endif + fsize = inode->i_size; +#endif + } else { + fsize = ini->size; + } +#endif + + ILI_INFO("ini file size = %d\n", fsize); + if (fsize <= 0) { + ILI_ERR("The size of file is invaild\n"); + ret = -EINVAL; + goto out; + } + + tmp = vmalloc(fsize+1); + if (ERR_ALLOC_MEM(tmp)) { + ILI_ERR("Failed to allocate tmp memory, %ld\n", PTR_ERR(tmp)); + ret = -ENOMEM; + goto out; + } +#if GENERIC_KERNEL_IMAGE + memcpy(tmp, ini->data, fsize); +#else + if (f != NULL) { + pos = 0; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + old_fs = get_fs(); + set_fs(KERNEL_DS); + vfs_read(f, tmp, fsize, &pos); + set_fs(old_fs); +#else + kernel_read(f, tmp, fsize, &pos); +#endif + tmp[fsize] = 0x0; + } else { + memcpy(tmp, ini->data, fsize); + } +#endif + g_ini_items = 0; + + /* Initialise ini strcture */ + for (i = 0; i < PARSER_MAX_KEY_NUM; i++) { + memset(ini_info[i].section_name, 0, PARSER_MAX_KEY_NAME_LEN); + memset(ini_info[i].key_name, 0, PARSER_MAX_KEY_NAME_LEN); + memset(ini_info[i].key_value, 0, PARSER_MAX_KEY_VALUE_LEN); + ini_info[i].section_len = 0; + ini_info[i].key_name_len = 0; + ini_info[i].key_val_len = 0; + } + + /* change all characters to lower case */ + for (i = 0; i < fsize; i++) + tmp[i] = tolower(tmp[i]); + + ret = parser_get_ini_phy_data(tmp, fsize); + if (ret < 0) { + ILI_ERR("Failed to get physical ini data, ret = %d\n", ret); + goto out; + } + + ILI_INFO("Parsed ini file done\n"); +out: + ipio_vfree((void **)&tmp); +#if GENERIC_KERNEL_IMAGE + release_firmware(ini); +#else + if (f != NULL) + filp_close(f, NULL); + else + release_firmware(ini); +#endif + return ret; +} + +static int parser_ini_precmd(char *desp, u8 *buf) +{ + + int i = 0, cmdlen = 0; + u8 cmdnum = 0; + u8 cmd[16] = {0}; + char precmd_str[128] = {0}; + + /* format complete string from the name of section "_Benchmark_Data". */ + snprintf(precmd_str, sizeof(precmd_str), "%s_precommand", desp); + ILI_INFO("precmd = %s\n", precmd_str); + + for (i = 0; i < g_ini_items; i++) { + if ((ipio_strcmp(ini_info[i].section_name, precmd_str) != 0)) + continue; + + if (parser_get_u8_array(ini_info[i].key_value, cmd, 16, 11) < 0) + return -1; + cmdnum++; + + buf[0] = CMD_FW_MP_PRE_SET_CMD; + buf[1] = cmdnum; + strncpy(buf + 2 + ((cmdnum - 1) * 11), cmd, 11); + + } + cmdlen = 2 + cmdnum * 11; + + return cmdlen; +} + +static int precommd(u8 *cmd, int cmdlen, u8 *precmd, int precmdlen, u8 *rdata, u32 rlen, bool spi_irq, bool i2c_irq) +{ + u8 buf[2] = {0}; + + buf[0] = CMD_FW_MP_PRE_CMD; + buf[1] = 0x1; + + if (ilits->wrapper(buf, 2, NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Failed precommand cmd\n"); + return -EMP_CMD; + } + + mdelay(1); + + if (ilits->wrapper(cmd, cmdlen, NULL, 0, OFF, OFF) < 0) { + ILI_ERR("Failed to get cdc data\n"); + return -EMP_GET_CDC; + } + + mdelay(1); + + if (ilits->wrapper(precmd, precmdlen, rdata, rlen, spi_irq, i2c_irq) < 0) { + ILI_ERR("Failed to get cdc data\n"); + return -EMP_GET_CDC; + } + + return 0; + +} + +static void run_pixel_test(int index) +{ + int i, x, y; + s32 *p_comb = frame_buf; + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int tmp[4] = { 0 }, max = 0; + int shift = y * core_mp.xch_len; + int centre = p_comb[shift + x]; + + /* + * if its position is in corner, the number of point + * we have to minus is around 2 to 3. + */ + if (y == 0 && x == 0) { + tmp[0] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[1] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y == (core_mp.ych_len - 1) && x == 0) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y == 0 && x == (core_mp.xch_len - 1)) { + tmp[0] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + } else if (y == (core_mp.ych_len - 1) && x == (core_mp.xch_len - 1)) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + } else if (y == 0 && x != 0) { + tmp[0] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[2] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y != 0 && x == 0) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + tmp[2] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + + } else if (y == (core_mp.ych_len - 1) && x != 0) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[2] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } else if (y != 0 && x == (core_mp.xch_len - 1)) { + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[2] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + } else { + /* middle minus four directions */ + tmp[0] = Mathabs(centre - p_comb[(shift - 1) + x]); /* up */ + tmp[1] = Mathabs(centre - p_comb[(shift + 1) + x]); /* down */ + tmp[2] = Mathabs(centre - p_comb[shift + (x - 1)]); /* left */ + tmp[3] = Mathabs(centre - p_comb[shift + (x + 1)]); /* right */ + } + + max = tmp[0]; + + for (i = 0; i < 4; i++) { + if (tmp[i] > max) + max = tmp[i]; + } + + tItems[index].buf[shift + x] = max; + } + } +} + +static void run_untouch_p2p_test(int index) +{ + int x, y; + s32 *p_comb = frame_buf; + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int shift = y * core_mp.xch_len; + + if (p_comb[shift + x] > tItems[index].max_buf[shift + x]) + tItems[index].max_buf[shift + x] = p_comb[shift + x]; + + if (p_comb[shift + x] < tItems[index].min_buf[shift + x]) + tItems[index].min_buf[shift + x] = p_comb[shift + x]; + + tItems[index].buf[shift + x] = + tItems[index].max_buf[shift + x] - tItems[index].min_buf[shift + x]; + } + } +} + +static int run_open_test(int index) +{ + int i, x, y, k, ret = 0; + int border_x[] = {-1, 0, 1, 1, 1, 0, -1, -1}; + int border_y[] = {-1, -1, -1, 0, 1, 1, 1, 0}; + s32 *p_comb = frame_buf; + + if (ipio_strcmp(tItems[index].desp, "open test(integration)") == 0) { + for (i = 0; i < core_mp.frame_len; i++) + tItems[index].buf[i] = p_comb[i]; + } else if (ipio_strcmp(tItems[index].desp, "open test(cap)") == 0) { + /* + * Each result is getting from a 3 by 3 grid depending on where the centre location is. + * So if the centre is at corner, the number of node grabbed from a grid will be different. + */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int sum = 0, avg = 0, count = 0; + int shift = y * core_mp.xch_len; + int centre = p_comb[shift + x]; + + for (k = 0; k < 8; k++) { + if (((y + border_y[k] >= 0) && (y + border_y[k] < core_mp.ych_len)) && + ((x + border_x[k] >= 0) && (x + border_x[k] < core_mp.xch_len))) { + count++; + sum += p_comb[(y + border_y[k]) * core_mp.xch_len + (x + border_x[k])]; + } + } + + avg = (sum + centre) / (count + 1); /* plus 1 because of centre */ + tItems[index].buf[shift + x] = (centre * 100) / avg; + } + } + } + return ret; +} + +static void run_tx_rx_delta_test(int index) +{ + int x, y; + s32 *p_comb = frame_buf; + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int shift = y * core_mp.xch_len; + + /* Tx Delta */ + if (y != (core_mp.ych_len - 1)) + core_mp.tx_delta_buf[shift + x] = Mathabs(p_comb[shift + x] - p_comb[(shift + 1) + x]); + + /* Rx Delta */ + if (x != (core_mp.xch_len - 1)) + core_mp.rx_delta_buf[shift + x] = Mathabs(p_comb[shift + x] - p_comb[shift + (x + 1)]); + } + } +} + +static char *get_date_time_str(void) +{ + static char time_data_buf[128] = { 0 }; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + struct timespec now_time; + struct rtc_time rtc_now_time; + + getnstimeofday(&now_time); + rtc_time_to_tm(now_time.tv_sec, &rtc_now_time); + snprintf(time_data_buf, sizeof(time_data_buf), "%04d%02d%02d-%02d%02d%02d", + (rtc_now_time.tm_year + 1900), rtc_now_time.tm_mon + 1, + rtc_now_time.tm_mday, rtc_now_time.tm_hour, rtc_now_time.tm_min, + rtc_now_time.tm_sec); +#else + struct tm tm; + time64_to_tm(ktime_get_real_seconds(), 0, &tm); + + snprintf(time_data_buf, sizeof(time_data_buf), "%04d%02d%02d-%02d%02d%02d", + (int)(tm.tm_year + 1900), tm.tm_mon + 1, + tm.tm_mday, tm.tm_hour, tm.tm_min, + tm.tm_sec); +#endif + + return time_data_buf; +} + +static void mp_print_csv_header(char *csv, int *csv_len, int *csv_line, int file_size) +{ + int i, seq, tmp_len = *csv_len, tmp_line = *csv_line; + + /* header must has 19 line*/ + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "==============================================================================\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "ILITek C-TP Utility V%s %x : Driver Sensor Test\n", DRIVER_VERSION, core_mp.chip_pid); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Confidentiality Notice:\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Any information of this tool is confidential and privileged.\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "@ ILI TECHNOLOGY CORP. All Rights Reserved.\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "==============================================================================\n"); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Firmware Version ,0x%x\n", core_mp.fw_ver); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Panel information ,XCH=%d, YCH=%d\n", core_mp.xch_len, core_mp.ych_len); + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "INI Release Version ,%s\n", core_mp.ini_ver); + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "INI Release Date ,%s\n", core_mp.ini_date); + tmp_line++; + tmp_line++; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Test Item:\n"); + tmp_line++; + + for (seq = 0; seq < ri.count; seq++) { + i = ri.index[seq]; + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), " ---%s\n", tItems[i].desp); + tmp_line++; + } + + while (tmp_line < 19) { + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "\n"); + tmp_line++; + } + + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "==============================================================================\n"); + + *csv_len = tmp_len; + *csv_line = tmp_line; +} + +static void mp_print_csv_tail(char *csv, int *csv_len, int file_size) +{ + int i, seq, tmp_len = *csv_len; + + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "==============================================================================\n"); + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Result_Summary \n"); + + for (seq = 0; seq < ri.count; seq++) { + i = ri.index[seq]; + if (tItems[i].item_result == MP_DATA_PASS) { + if (tItems[i].goldenmode && (tItems[i].spec_option != tItems[i].goldenmode)) { + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), " {%s} ,OK, Warning, No Golden\n", tItems[i].desp); + } else { + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), " {%s} ,OK\n", tItems[i].desp); + } + } else { + if (tItems[i].goldenmode && (tItems[i].spec_option != tItems[i].goldenmode)) { + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), " {%s} ,NG, Warning, No Golden\n", tItems[i].desp); + } else { + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), " {%s} ,NG\n", tItems[i].desp); + } + } + } + + *csv_len = tmp_len; +} + +static void mp_print_csv_cdc_cmd(char *csv, int *csv_len, int index, int file_size) +{ + int i, slen = 0, tmp_len = *csv_len, size; + char str[128] = {0}; + char *open_sp_cmd[] = {"open dac", "open raw1", "open raw2", "open raw3"}; + char *open_c_cmd[] = {"open cap1 dac", "open cap1 raw"}; + char *open_x_cmd[] = {"open_x cap1 dac", "open_x cap1 raw"}; + char *name = tItems[index].desp; + + if (ipio_strcmp(name, "open test(integration)_sp") == 0) { + size = ARRAY_SIZE(open_sp_cmd); + for (i = 0; i < size; i++) { + slen = parser_get_int_data("pv5_4 command", open_sp_cmd[i], str, sizeof(str)); + if (slen < 0) + ILI_ERR("Failed to get CDC command %s from ini\n", open_sp_cmd[i]); + else + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "%s = ,%s\n", open_sp_cmd[i], str); + } + } else if (ipio_strcmp(name, "open test_c") == 0) { + size = ARRAY_SIZE(open_c_cmd); + for (i = 0; i < size; i++) { + slen = parser_get_int_data("pv5_4 command", open_c_cmd[i], str, sizeof(str)); + if (slen < 0) + ILI_ERR("Failed to get CDC command %s from ini\n", open_sp_cmd[i]); + else + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "%s = ,%s\n", open_c_cmd[i], str); + } + } else if (ipio_strcmp(name, "open test_c (openx enable)") == 0) { + size = ARRAY_SIZE(open_x_cmd); + for (i = 0; i < size; i++) { + slen = parser_get_int_data("pv5_4 command", open_x_cmd[i], str, sizeof(str)); + if (slen < 0) + ILI_ERR("Failed to get CDC command %s from ini\n", open_x_cmd[i]); + else + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "%s = ,%s\n", open_x_cmd[i], str); + } + } else { + slen = parser_get_int_data("pv5_4 command", name, str, sizeof(str)); + if (slen < 0) + ILI_ERR("Failed to get CDC command %s from ini\n", name); + else + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "CDC command = ,%s\n", str); + + /* Print short parameters */ + if (ipio_strcmp(name, "short test") == 0) + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "Variation = ,%d\n", short_para.variation); + + /* Print td second command */ + if (core_mp.td_retry && (ipio_strcmp(name, "peak to peak_td (lcm off)") == 0)) { + name = "peak to peak_td (lcm off)_2"; + parser_get_int_data("pv5_4 command", name, str, sizeof(str)); + tmp_len += snprintf(csv + tmp_len, (file_size - tmp_len), "CDC command 2 = ,%s\n", str); + } + } + *csv_len = tmp_len; +} + +static void mp_compare_cdc_show_result(int index, s32 *tmp, char *csv, + int *csv_len, int type, s32 *max_ts, + s32 *min_ts, const char *desp, int file_zise) +{ + int x, y, tmp_len = *csv_len; + int mp_result = MP_DATA_PASS; + + if (ERR_ALLOC_MEM(tmp)) { + ILI_ERR("The data of test item is null (%p)\n", tmp); + mp_result = -EMP_INVAL; + goto out; + } + + /* print X raw only */ + for (x = 0; x < core_mp.xch_len; x++) { + if (x == 0) { + DUMP("\n %s ", desp); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "\n %s ,", desp); + } + DUMP(" X_%d ,", (x+1)); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " X_%d ,", (x+1)); + } + + DUMP("\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "\n"); + + for (y = 0; y < core_mp.ych_len; y++) { + DUMP(" Y_%d ,", (y+1)); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " Y_%d ,", (y+1)); + + for (x = 0; x < core_mp.xch_len; x++) { + int shift = y * core_mp.xch_len + x; + + /* In Short teset, we only identify if its value is low than min threshold. */ + if (tItems[index].catalog == SHORT_TEST) { + if (tmp[shift] < min_ts[shift]) { + DUMP(" #%7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "#%7d,", tmp[shift]); + mp_result = MP_DATA_FAIL; + } else { + DUMP(" %7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " %7d, ", tmp[shift]); + } + continue; + } + + if ((tmp[shift] <= max_ts[shift] && tmp[shift] >= min_ts[shift]) || (type != TYPE_JUGE)) { + if ((tmp[shift] == INT_MAX || tmp[shift] == INT_MIN) && (type == TYPE_BENCHMARK)) { + DUMP("%s", "BYPASS,"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "BYPASS,"); + } else { + DUMP(" %7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " %7d, ", tmp[shift]); + } + } else { + if (tmp[shift] > max_ts[shift]) { + DUMP(" *%7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "*%7d,", tmp[shift]); + } else { + DUMP(" #%7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "#%7d,", tmp[shift]); + } + mp_result = MP_DATA_FAIL; + } + } + DUMP("\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "\n"); + } + +out: + if (type == TYPE_JUGE) { + if (mp_result == MP_DATA_PASS) { + pr_info("\n Result : PASS\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "Result : PASS\n"); + } else { + pr_info("\n Result : FAIL\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "Result : FAIL\n"); + } + } + *csv_len = tmp_len; +} + +static void mp_compare_tip_ring_cdc_show_result(int index, s32 *tmp, char *csv, + int *csv_len, int type, s32 *max_ts, s32 *min_ts, const char *desp, + int file_zise, int trtype) +{ + int x, y, tmp_len = *csv_len; + int row, col, typeshift; + int mp_result = MP_DATA_PASS; + + if (ERR_ALLOC_MEM(tmp)) { + ILI_ERR("The data of test item is null (%p)\n", tmp); + mp_result = -EMP_INVAL; + goto out; + } + if (trtype == TIPRING_X) { + row = core_mp.xch_len; + col = core_mp.nPxRaw * 2; + typeshift = 0; + } else { + row = core_mp.nPyRaw * 2; + col = core_mp.ych_len; + typeshift = core_mp.nPxRaw_tipring_len; + } + + /* print X row only */ + for (x = 0; x < row; x++) { + if (x == 0) { + DUMP("\n %s ", desp); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "\n %s ,", desp); + } + DUMP(" X_%d ,", (x+1)); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " X_%d ,", (x+1)); + } + + DUMP("\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "\n"); + for (y = 0; y < col; y++) { + DUMP(" Y_%d ,", (y+1)); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " Y_%d ,", (y+1)); + + for (x = 0; x < row; x++) { + int shift = typeshift + y * row + x; + if ((tmp[shift] <= max_ts[shift] && tmp[shift] >= min_ts[shift]) || (type != TYPE_JUGE)) { + if ((tmp[shift] == INT_MAX || tmp[shift] == INT_MIN) && (type == TYPE_BENCHMARK)) { + DUMP("%s", "BYPASS,"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "BYPASS,"); + } else { + DUMP(" %7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), " %7d, ", tmp[shift]); + } + } else { + if (tmp[shift] > max_ts[shift]) { + DUMP(" *%7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "*%7d,", tmp[shift]); + } else { + DUMP(" #%7d ", tmp[shift]); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "#%7d,", tmp[shift]); + } + mp_result = MP_DATA_FAIL; + } + } + DUMP("\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "\n"); + } + +out: + if (type == TYPE_JUGE) { + if (mp_result == MP_DATA_PASS) { + pr_info("\n Result : PASS\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "Result : PASS\n"); + } else { + pr_info("\n Result : FAIL\n"); + tmp_len += snprintf(csv + tmp_len, (file_zise - tmp_len), "Result : FAIL\n"); + } + } + *csv_len = tmp_len; +} + +#define ABS(a, b) ((a > b) ? (a - b) : (b - a)) +#define ADDR(x, y) ((y * core_mp.xch_len) + (x)) + +static int compare_charge(s32 *charge_rate, int x, int y, s32 *inNodeType, + int Charge_AA, int Charge_Border, int Charge_Notch) +{ + int OpenThreadhold, tempY, tempX, ret, k; + int sx[8] = {-1, 0, 1, -1, 1, -1, 0, 1}; + int sy[8] = {-1, -1, -1, 0, 0, 1, 1, 1}; + + ret = charge_rate[ADDR(x, y)]; + + /*Setting Threadhold from node type */ + if (charge_rate[ADDR(x, y)] == 0) + return ret; + else if ((inNodeType[ADDR(x, y)] & AA_Area) == AA_Area) + OpenThreadhold = Charge_AA; + else if ((inNodeType[ADDR(x, y)] & Border_Area) == Border_Area) + OpenThreadhold = Charge_Border; + else if ((inNodeType[ADDR(x, y)] & Notch) == Notch) + OpenThreadhold = Charge_Notch; + else + return ret; + + /* compare carge rate with 3*3 node */ + /* by pass => 1.no compare 2.corner 3.Skip_Micro 4.full open fail node */ + for (k = 0; k < 8; k++) { + tempX = x + sx[k]; + tempY = y + sy[k]; + + /*out of range */ + if ((tempX < 0) || (tempX >= core_mp.xch_len) || (tempY < 0) || (tempY >= core_mp.ych_len)) + continue; + + if ((inNodeType[ADDR(tempX, tempY)] == NO_COMPARE) || ((inNodeType[ADDR(tempX, tempY)] & Round_Corner) == Round_Corner) || + ((inNodeType[ADDR(tempX, tempY)] & Skip_Micro) == Skip_Micro) || charge_rate[ADDR(tempX, tempY)] == 0) + continue; + + if ((charge_rate[ADDR(tempX, tempY)] - charge_rate[ADDR(x, y)]) > OpenThreadhold) + return OpenThreadhold; + } + return ret; +} + +static int full_open_rate_compare(s32 *full_open, s32 *cbk, int x, int y, s32 inNodeType, int full_open_rate) +{ + int ret = true; + + if ((inNodeType == NO_COMPARE) || ((inNodeType & Round_Corner) == Round_Corner)) + return true; + + if (full_open[ADDR(x, y)] < (cbk[ADDR(x, y)] * full_open_rate / 100)) + ret = false; + + return ret; +} + +static s32 open_sp_formula(int dac, int raw, int tvch, int tvcl) +{ + s32 ret = 0; + u16 id = core_mp.chip_id; + + if (id == ILI9881_CHIP) { + ret = ((dac * 10000 * 161 / 100) - (16384 / 2 - raw) * 20000 * 7 / 16384 * 36 / 10) / (tvch - tvcl) / 2; + } else { + ret = ((dac * 10000 * 131 / 100) - (16384 / 2 - raw) * 20000 * 7 / 16384 * 36 / 10) / (tvch - tvcl) / 2; + } + return ret; +} + +static s32 open_c_formula(int inCap1DAC, int inCap1Raw, int accuracy) +{ + s32 inCap1Value = 0; + + u16 id = core_mp.chip_id; + u8 type = core_mp.chip_type; + + int inFoutRange = 16384; + int inFoutRange_half = 8192; + int inVadc_range = open_para.vadc_range; + int inVbk = open_para.invbk;/* from mp.ini */ + int inCbk_step = open_para.cbk_step;/* from mp.ini */ + int inCint = open_para.cint;/* from mp.ini */ + int inVdrv = open_para.tvch - open_para.tvcl;/* from mp.ini */ + int inGain = open_para.gain;/* from mp.ini */ + int inMagnification = 10; + int inPartDac = 0; + int inPartRaw = 0; + + if (inVbk == 0) { + if ((id == ILI7807_CHIP && ((type == ILI_S) || (type == ILI_V))) + || (id == ILI9882_CHIP && type == ILI_T)) + inVbk = 42; + else + inVbk = 39; + } + + if (inVadc_range == 0) + inVadc_range = 36; + + if ((inCbk_step == 0) || (inCint == 0)) { + if (id == ILI9881_CHIP) { + if ((type == ILI_N) || (type == ILI_O)) { + inCbk_step = 32; + inCint = 70; + } + } else if (id == ILI9882_CHIP) { + if (type == ILI_N) { + inCbk_step = 42; + inCint = 70; + } else if (type == ILI_H) { + inCbk_step = 42; + inCint = 69; + } + } else if (id == ILI7807_CHIP) { + if (type == ILI_Q) { + inCbk_step = 28; + inCint = 70; + } else if (type == ILI_S) { + inCbk_step = 38; + inCint = 66; + inVdrv = inVdrv / 10; + inGain = inGain / 10; + } else if (type == ILI_V) { + inCbk_step = 42; + inCint = 69; + } + } else if (id == ILI9883_CHIP) { + if (type == ILI_A) { + inCbk_step = 42; + inCint = 70; + } + } + } + + inPartDac = (inCap1DAC * inCbk_step * inVbk / 2); + inPartRaw = ((inCap1Raw - inFoutRange_half) * inVadc_range * inCint * 10 / inFoutRange); + + if (accuracy) { + inCap1Value = ((inPartDac + inPartRaw) * 10) / inVdrv / inMagnification / inGain; + } else { + inCap1Value = (inPartDac + inPartRaw) / inVdrv / inMagnification / inGain; + } + + return inCap1Value; +} + +static void allnode_open_cdc_result(int index, int *buf, int *dac, int *raw) +{ + int i; + char *desp = tItems[index].desp; + char str[32] = {0}; + int accuracy = 0; + + if (ipio_strcmp(desp, "open test(integration)_sp") == 0) { + for (i = 0; i < core_mp.frame_len; i++) + buf[i] = open_sp_formula(dac[i], raw[i], open_para.tvch, open_para.tvcl); + } else if (ipio_strcmp(desp, "open test_c") == 0 || ipio_strcmp(desp, "open test_c (openx enable)") == 0) { + + if (parser_get_int_data(desp, "accuracy", str, sizeof(str)) > 0) + accuracy = ili_katoi(str); + + for (i = 0; i < core_mp.frame_len; i++) + buf[i] = open_c_formula(dac[i], raw[i], accuracy); + } +} + +static int codeToOhm(s32 *ohm, s32 *Code, u16 *v_tdf, u16 *h_tdf) +{ + u16 id = core_mp.chip_id; + u8 type = core_mp.chip_type; + + int inTVCH = short_para.tvch; + int inTVCL = short_para.tvcl; + int inVariation = short_para.variation; + int inTDF1 = 0; + int inTDF2 = 0; + int inCint = short_para.cint; + int inRinternal = short_para.rinternal; + s32 temp = 0; + int j = 0; + + if (core_mp.isLongV) { + inTDF1 = *v_tdf; + inTDF2 = *(v_tdf + 1); + } else { + inTDF1 = *h_tdf; + inTDF2 = *(h_tdf + 1); + } + + if (inVariation == 0) { + inVariation = 100; + } + + if ((inCint == 0) || (inRinternal == 0)) { + if (id == ILI9881_CHIP) { + if ((type == ILI_N) || (type == ILI_O)) { + inRinternal = 1915; + inCint = 70; + } + } else if (id == ILI9882_CHIP) { + if (type == ILI_N) { + inRinternal = 2000; + inCint = 70; + } else if (type == ILI_H) { + inRinternal = 2000; + inCint = 69; + } + } else if (id == ILI7807_CHIP) { + if (type == ILI_Q) { + inRinternal = 1500; + inCint = 70; + } else if (type == ILI_S) { + inRinternal = 1500; + inCint = 66; + inTVCH = inTVCH/10; + inTVCL = inTVCL/10; + } else if (type == ILI_V) { + inRinternal = 2000; + inCint = 69; + } + } else if (id == ILI9883_CHIP) { + if (type == ILI_A) { + inRinternal = 2000; + inCint = 70; + } + } + } + for (j = 0; j < core_mp.frame_len; j++) { + if (Code[j] == 0) { + ILI_ERR("code is invalid\n"); + } else { + temp = ((inTVCH - inTVCL) * inVariation * (inTDF1 - inTDF2) * (1 << 12) / (9 * Code[j] * inCint)) * 1000; + temp = (temp - inRinternal) / 1000; + } + ohm[j] = temp; + } + + /* Unit = M Ohm */ + return temp; + +} + +static int short_test(int index, int frame_index) +{ + u32 pid = core_mp.chip_pid >> 8; + int j = 0, ret = 0; + u16 v_tdf[2] = {0}; + u16 h_tdf[2] = {0}; + char str[32] = {0}; + + v_tdf[0] = tItems[index].v_tdf_1; + v_tdf[1] = tItems[index].v_tdf_2; + h_tdf[0] = tItems[index].h_tdf_1; + h_tdf[1] = tItems[index].h_tdf_2; + + parser_get_int_data("short test", "tvch", str, sizeof(str)); + short_para.tvch = ili_katoi(str); + + parser_get_int_data("short test", "tvcl", str, sizeof(str)); + short_para.tvcl = ili_katoi(str); + + parser_get_int_data("short test", "variation", str, sizeof(str)); + short_para.variation = ili_katoi(str); + + parser_get_int_data("short test", "cint", str, sizeof(str)); + short_para.cint = ili_katoi(str); + + parser_get_int_data("short test", "rinternal", str, sizeof(str)); + short_para.rinternal = ili_katoi(str); + + /* 9881N, 9881O, 7807Q, 7807S not support cbk_step and cint read from mp.ini */ + if ((pid != 0x988117) && (pid != 0x988118) && (pid != 0x78071A) && (pid != 0x78071C)) { + if ((short_para.cint == 0) || (short_para.rinternal == 0)) { + ILI_ERR("Failed to get short parameter"); + core_mp.lost_parameter = true; + return -1; + } + } + + ILI_INFO("TVCH = %d, TVCL = %d, Variation = %d, V_TDF1 = %d, V_TDF2 = %d, H_TDF1 = %d, H_TDF2 = %d, Cint = %d, Rinternal = %d\n" + , short_para.tvch, short_para.tvcl, short_para.variation, tItems[index].v_tdf_1, tItems[index].v_tdf_2, tItems[index].h_tdf_1, tItems[index].h_tdf_2, short_para.cint, short_para.rinternal); + + if (core_mp.protocol_ver >= PROTOCOL_VER_540) { + /* Calculate code to ohm and save to tItems[index].buf */ + ili_dump_data(frame_buf, 10, core_mp.frame_len, core_mp.xch_len, "Short Raw 1"); + codeToOhm(&tItems[index].buf[frame_index * core_mp.frame_len], frame_buf, v_tdf, h_tdf); + ili_dump_data(&tItems[index].buf[frame_index * core_mp.frame_len], 10, core_mp.frame_len, core_mp.xch_len, "Short Ohm 1"); + } else { + for (j = 0; j < core_mp.frame_len; j++) + tItems[index].buf[frame_index * core_mp.frame_len + j] = frame_buf[j]; + } + + return ret; +} + +static int allnode_key_cdc_data(int index) +{ + int i, ret = 0, len = 0; + int inDACp = 0, inDACn = 0; + u8 cmd[3] = {0}; + u8 *ori = NULL; + + len = core_mp.key_len * 2; + + ILI_DBG("Read key's length = %d\n", len); + ILI_DBG("core_mp.key_len = %d\n", core_mp.key_len); + + if (len <= 0) { + ILI_ERR("Length is invalid\n"); + ret = -1; + goto out; + } + + /* CDC init */ + cmd[0] = P5_X_SET_CDC_INIT; + cmd[1] = tItems[index].cmd; + cmd[2] = 0; + + /* Allocate a buffer for the original */ + ori = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(ori)) { + ILI_ERR("Failed to allocate ori mem (%ld)\n", PTR_ERR(ori)); + ret = -1; + goto out; + } + ret = ilits->wrapper(cmd, sizeof(cmd), ori, len, ON, ON); + if (ret < 0) { + ILI_ERR(" fail to get cdc data\n"); + goto out; + } + ili_dump_data(ori, 8, len, 0, "Key CDC original"); + + if (key_buf == NULL) { + key_buf = kcalloc(core_mp.key_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(key_buf)) { + ILI_ERR("Failed to allocate FrameBuffer mem (%ld)\n", PTR_ERR(key_buf)); + goto out; + } + } else { + memset(key_buf, 0x0, core_mp.key_len); + } + + /* Convert original data to the physical one in each node */ + for (i = 0; i < core_mp.frame_len; i++) { + if (tItems[index].cmd == CMD_KEY_DAC) { + /* DAC - P */ + if (((ori[(2 * i) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACp = 0 - (int)(ori[(2 * i) + 1] & 0x7F); + } else { + inDACp = ori[(2 * i) + 1] & 0x7F; + } + + /* DAC - N */ + if (((ori[(1 + (2 * i)) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACn = 0 - (int)(ori[(1 + (2 * i)) + 1] & 0x7F); + } else { + inDACn = ori[(1 + (2 * i)) + 1] & 0x7F; + } + + key_buf[i] = (inDACp + inDACn) / 2; + } + } + ili_dump_data(key_buf, 32, core_mp.frame_len, core_mp.xch_len, "Key CDC combined data"); + +out: + ipio_kfree((void **)&ori); + return ret; +} + +static int mp_cdc_get_pv5_4_command(u8 *cmd, int len, int index) +{ + int slen = 0; + char str[128] = {0}; + char *key = tItems[index].desp; + + if (core_mp.td_retry && (ipio_strcmp(key, "peak to peak_td (lcm off)") == 0)) + key = "peak to peak_td (lcm off)_2"; + + slen = parser_get_int_data("pv5_4 command", key, str, sizeof(str)); + if (slen < 0) + return -1; + + if (parser_get_u8_array(str, cmd, 16, len) < 0) + return -1; + + return 0; +} + +static int mp_cdc_init_cmd_common(u8 *cmd, int len, int index) +{ + int ret = 0; + + if (core_mp.protocol_ver >= PROTOCOL_VER_540) { + core_mp.cdc_len = 15; + return mp_cdc_get_pv5_4_command(cmd, len, index); + } + + cmd[0] = P5_X_SET_CDC_INIT; + cmd[1] = tItems[index].cmd; + cmd[2] = 0; + + core_mp.cdc_len = 3; + + if (ipio_strcmp(tItems[index].desp, "open test(integration)") == 0) + cmd[2] = 0x2; + if (ipio_strcmp(tItems[index].desp, "open test(cap)") == 0) + cmd[2] = 0x3; + + if (tItems[index].catalog == PEAK_TO_PEAK_TEST) { + cmd[2] = ((tItems[index].frame_count & 0xff00) >> 8); + cmd[3] = tItems[index].frame_count & 0xff; + cmd[4] = 0; + + core_mp.cdc_len = 5; + + if (ipio_strcmp(tItems[index].desp, "noise peak to peak(cut panel)") == 0) + cmd[4] = 0x1; + + ILI_DBG("P2P CMD: %d,%d,%d,%d,%d\n", + cmd[0], cmd[1], cmd[2], cmd[3], cmd[4]); + } + + return ret; +} + +static int allnode_open_cdc_data(int mode, int *buf, int index) +{ + int i = 0, ret = 0, len = 0, precmd_len = 0; + int inDACp = 0, inDACn = 0; + u8 cmd[15] = {0}; + u8 precmd[256] = {0}; + u8 *ori = NULL; + char str[128] = {0}; + char *key[] = {"open dac", "open raw1", "open raw2", "open raw3", + "open cap1 dac", "open cap1 raw", "open_x cap1 dac", "open_x cap1 raw"}; + + /* Multipling by 2 is due to the 16 bit in each node */ + len = (core_mp.xch_len * core_mp.ych_len * 2) + 2; + + ILI_DBG("Read X/Y Channel length = %d, mode = %d\n", len, mode); + + if (len <= 2) { + ILI_ERR("Length is invalid\n"); + ret = -EMP_INVAL; + goto out; + } + + /*Pre-Command Init*/ + if (tItems[index].pre_cmd_en) { + memset(precmd, 0xFF, sizeof(precmd)); + precmd_len = parser_ini_precmd(tItems[index].desp, precmd); + + ili_dump_data(precmd, 16, precmd_len, 0, "PreCommand"); + + /*ini file no pre-command, disable precommand*/ + if (precmd_len < 13) + tItems[index].pre_cmd_en = DISABLE; + } + + /* CDC init. Read command from ini file */ + if (parser_get_int_data("pv5_4 command", key[mode], str, sizeof(str)) < 0) { + ILI_ERR("Failed to parse PV54 command, ret = %d\n", ret); + ret = -EMP_PARSE; + goto out; + } + + parser_get_u8_array(str, cmd, 16, sizeof(cmd)); + + ili_dump_data(cmd, 8, sizeof(cmd), 0, "Open SP command"); + + /* Allocate a buffer for the original */ + ori = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(ori)) { + ILI_ERR("Failed to allocate ori, (%ld)\n", PTR_ERR(ori)); + ret = -EMP_NOMEM; + goto out; + } + + /* Get original frame(cdc) data */ +#if (ENGINEER_FLOW) + if (!ilits->eng_flow) { + if (tItems[index].pre_cmd_en) { + if (precommd(cmd, core_mp.cdc_len, precmd, precmd_len, ori, len, ON, ON) < 0) + goto out; + } else { + if (ilits->wrapper(cmd, core_mp.cdc_len, ori, len, ON, ON) < 0) { + ILI_ERR("Failed to get cdc data\n"); + ret = -EMP_GET_CDC; + goto out; + } + } + } else { + msleep(200); + if (ilits->wrapper(cmd, core_mp.cdc_len, ori, len, OFF, OFF) < 0) { + ILI_ERR("Failed to get cdc data\n"); + } + } +#else + if (tItems[index].pre_cmd_en) { + if (precommd(cmd, core_mp.cdc_len, precmd, precmd_len, ori, len, ON, ON) < 0) + goto out; + } else { + if (ilits->wrapper(cmd, core_mp.cdc_len, ori, len, ON, ON) < 0) { + ILI_ERR("Failed to get cdc data\n"); + ret = -EMP_GET_CDC; + goto out; + } + } +#endif + + ili_dump_data(ori, 8, len, 0, "Open SP CDC original"); + + /* Convert original data to the physical one in each node */ + for (i = 0; i < core_mp.frame_len; i++) { + if ((mode == 0) || (mode == 4) || (mode == 6)) { + /* DAC - P */ + if (((ori[(2 * i) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACp = 0 - (int)(ori[(2 * i) + 1] & 0x7F); + } else { + inDACp = ori[(2 * i) + 1] & 0x7F; + } + + /* DAC - N */ + if (((ori[(1 + (2 * i)) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACn = 0 - (int)(ori[(1 + (2 * i)) + 1] & 0x7F); + } else { + inDACn = ori[(1 + (2 * i)) + 1] & 0x7F; + } + + buf[i] = inDACp + inDACn; + } else { + /* H byte + L byte */ + s32 tmp = (ori[(2 * i) + 1] << 8) + ori[(1 + (2 * i)) + 1]; + if ((tmp & 0x8000) == 0x8000) + buf[i] = tmp - 65536; + else + buf[i] = tmp; + + } + } + ili_dump_data(buf, 10, core_mp.frame_len, core_mp.xch_len, "Open SP CDC combined"); +out: + + ipio_kfree((void **)&ori); + return ret; +} + +static int allnode_peak_to_peak_cdc_data(int index) +{ + int i, k, ret = 0, len = 0, rd_frame_num = 1, cmd_len = 0, precmd_len = 0; + u8 cmd[15] = {0}; + u8 precmd[256] = {0}; + u8 *ori = NULL; + + /* Multipling by 2 is due to the 16 bit in each node */ + if (tItems[index].catalog == TIPRING_P2P) + len = (core_mp.tipring_len * 2) + 2; + else + len = (core_mp.frame_len * 2) + 2; + + ILI_DBG("Read X/Y Channel length = %d\n", len); + + if (len <= 2) { + ILI_ERR("Length is invalid\n"); + ret = -EMP_INVAL; + goto out; + } + + memset(cmd, 0xFF, sizeof(cmd)); + + /* Allocate a buffer for the original */ + ori = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(ori)) { + ILI_ERR("Failed to allocate ori, (%ld)\n", PTR_ERR(ori)); + ret = -EMP_NOMEM; + goto out; + } + + if (tItems[index].bch_mrk_multi) { + rd_frame_num = tItems[index].bch_mrk_frm_num - 1; + } + + if (frm_buf == NULL) { + frm_buf = (s32 **)kzalloc(tItems[index].bch_mrk_frm_num * sizeof(s32 *), GFP_KERNEL); + if (ERR_ALLOC_MEM(frm_buf)) { + ILI_ERR("Failed to allocate frm_buf mem (%ld)\n", PTR_ERR(frm_buf)); + ret = -EMP_NOMEM; + goto out; + } + for (i = 0; i < tItems[index].bch_mrk_frm_num; i++) { + frm_buf[i] = (s32 *)kzalloc(core_mp.frame_len * sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frm_buf)) { + ILI_ERR("Failed to allocate frm_buf[%d] mem (%ld)\n", i, PTR_ERR(frm_buf)); + ret = -EMP_NOMEM; + goto out; + } + } + } + + /*Pre-Command Init*/ + if (tItems[index].pre_cmd_en) { + memset(precmd, 0xFF, sizeof(precmd)); + precmd_len = parser_ini_precmd(tItems[index].desp, precmd); + + ili_dump_data(precmd, 16, precmd_len, 0, "PreCommand"); + + /*ini file no pre-command, disable precommand*/ + if (precmd_len < 13) + tItems[index].pre_cmd_en = DISABLE; + } + + /* CDC init */ + if (mp_cdc_init_cmd_common(cmd, sizeof(cmd), index) < 0) { + ILI_ERR("Failed to get cdc command\n"); + ret = -EMP_CMD; + goto out; + } + + ili_dump_data(cmd, 8, core_mp.cdc_len, 0, "Mutual CDC command"); + + for (k = 0; k < rd_frame_num; k++) { + if (k == 0) + cmd_len = core_mp.cdc_len; + else + cmd_len = 0; + memset(ori, 0, len); + /* Get original frame(cdc) data */ +#if (ENGINEER_FLOW) + if (!ilits->eng_flow) { + if (tItems[index].pre_cmd_en) { + if (precommd(cmd, cmd_len, precmd, precmd_len, ori, len, ON, ON) < 0) { + goto out; + } + } else { + if (ilits->wrapper(cmd, cmd_len, ori, len, ON, ON) < 0) { + ILI_ERR("Failed to get cdc data\n"); + ret = -EMP_GET_CDC; + goto out; + } + } + } else { + msleep(200); + if (ilits->wrapper(cmd, cmd_len, ori, len, OFF, OFF) < 0) { + ILI_ERR("Failed to get cdc data\n"); + } + } +#else + if (tItems[index].pre_cmd_en) { + if (precommd(cmd, cmd_len, precmd, precmd_len, ori, len, ON, ON) < 0) { + goto out; + } + } else { + if (ilits->wrapper(cmd, cmd_len, ori, len, ON, ON) < 0) { + ILI_ERR("Failed to get cdc data\n"); + ret = -EMP_GET_CDC; + goto out; + } + } +#endif + + ili_dump_data(ori, 8, len, 0, "Mutual CDC original"); + + /* Convert original data to the physical one in each node */ + for (i = 0; i < (len / 2 - 1); i++) { + /* H byte + L byte */ + s32 tmp = (ori[(2 * i) + 1] << 8) + ori[(1 + (2 * i)) + 1]; + + if ((tmp & 0x8000) == 0x8000) + frm_buf[k][i] = tmp - 65536; + else + frm_buf[k][i] = tmp; + + /* multiple case frame3 = frame1 - frame2 */ + if (tItems[index].bch_mrk_multi && rd_frame_num == k + 1) + frm_buf[k+1][i] = frm_buf[k-1][i] - frm_buf[k][i]; + } + } + + if (tItems[index].bch_mrk_multi) { + ili_dump_data(frm_buf[0], 32, (len / 2 - 1), core_mp.xch_len, "Mutual CDC combined[0]/frame1"); + ili_dump_data(frm_buf[1], 32, (len / 2 - 1), core_mp.xch_len, "Mutual CDC combined[1]/frame2"); + ili_dump_data(frm_buf[2], 32, (len / 2 - 1), core_mp.xch_len, "Mutual CDC combined[2]/frame3"); + } else { + ili_dump_data(frm_buf[0], 32, (len / 2 - 1), core_mp.xch_len, "Mutual CDC combined[0]/frame1"); + } + +out: + ipio_kfree((void **)&ori); + return ret; +} + +static int allnode_mutual_cdc_data(int index) +{ + int i, ret = 0, len = 0, precmd_len = 0, cdc_len = 0; + int inDACp = 0, inDACn = 0; + u8 cmd[15] = {0}; + u8 precmd[256] = {0}; + u8 *ori = NULL; + + if (tItems[index].catalog == TIPRING_RAW) { + cdc_len = core_mp.tipring_len; + } else { + cdc_len = core_mp.frame_len; + } + + /* Multipling by 2 is due to the 16 bit in each node */ + len = (cdc_len * 2) + 2; + + ILI_DBG("Read X/Y Channel length = %d\n", len); + + if (len <= 2) { + ILI_ERR("Length is invalid\n"); + ret = -EMP_INVAL; + goto out; + } + + /*Pre-Command Init*/ + if (tItems[index].pre_cmd_en) { + memset(precmd, 0xFF, sizeof(precmd)); + precmd_len = parser_ini_precmd(tItems[index].desp, precmd); + + ili_dump_data(precmd, 16, precmd_len, 0, "PreCommand"); + + /*ini file no pre-command, disable precommand*/ + if (precmd_len < 13) + tItems[index].pre_cmd_en = DISABLE; + } + + memset(cmd, 0xFF, sizeof(cmd)); + + /* CDC init */ + if (mp_cdc_init_cmd_common(cmd, sizeof(cmd), index) < 0) { + ILI_ERR("Failed to get cdc command\n"); + ret = -EMP_CMD; + goto out; + } + + ili_dump_data(cmd, 8, core_mp.cdc_len, 0, "Mutual CDC command"); + + /* Allocate a buffer for the original */ + ori = kcalloc(len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(ori)) { + ILI_ERR("Failed to allocate ori, (%ld)\n", PTR_ERR(ori)); + ret = -EMP_NOMEM; + goto out; + } + + /* Get original frame(cdc) data */ +#if (ENGINEER_FLOW) + if (!ilits->eng_flow) { + if (tItems[index].pre_cmd_en) { + if (precommd(cmd, core_mp.cdc_len, precmd, precmd_len, ori, len, ON, ON) < 0) + goto out; + } else { + if (ilits->wrapper(cmd, core_mp.cdc_len, ori, len, ON, ON) < 0) { + ILI_ERR("Failed to get cdc data\n"); + ret = -EMP_GET_CDC; + goto out; + } + } + } else { + msleep(200); + if (ilits->wrapper(cmd, core_mp.cdc_len, ori, len, OFF, OFF) < 0) { + ILI_ERR("Failed to get cdc data\n"); + } + } +#else + if (tItems[index].pre_cmd_en) { + if (precommd(cmd, core_mp.cdc_len, precmd, precmd_len, ori, len, ON, ON) < 0) + goto out; + } else { + if (ilits->wrapper(cmd, core_mp.cdc_len, ori, len, ON, ON) < 0) { + ILI_ERR("Failed to get cdc data\n"); + ret = -EMP_GET_CDC; + goto out; + } + } +#endif + + ili_dump_data(ori, 8, len, 0, "Mutual CDC original"); + + if (frame_buf == NULL) { + frame_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frame_buf)) { + ILI_ERR("Failed to allocate FrameBuffer mem (%ld)\n", PTR_ERR(frame_buf)); + ret = -EMP_NOMEM; + goto out; + } + } else { + memset(frame_buf, 0x0, core_mp.frame_len); + } + + /* Convert original data to the physical one in each node */ + for (i = 0; i < cdc_len; i++) { + if (ipio_strcmp(tItems[index].desp, "calibration data(dac)") == 0) { + /* DAC - P */ + if (((ori[(2 * i) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACp = 0 - (int)(ori[(2 * i) + 1] & 0x7F); + } else { + inDACp = ori[(2 * i) + 1] & 0x7F; + } + + /* DAC - N */ + if (((ori[(1 + (2 * i)) + 1] & 0x80) >> 7) == 1) { + /* Negative */ + inDACn = 0 - (int)(ori[(1 + (2 * i)) + 1] & 0x7F); + } else { + inDACn = ori[(1 + (2 * i)) + 1] & 0x7F; + } + + frame_buf[i] = (inDACp + inDACn) / 2; + } else { + /* H byte + L byte */ + s32 tmp = (ori[(2 * i) + 1] << 8) + ori[(1 + (2 * i)) + 1]; + + if ((tmp & 0x8000) == 0x8000) + frame_buf[i] = tmp - 65536; + else + frame_buf[i] = tmp; + + if (ipio_strcmp(tItems[index].desp, "raw data(no bk)") == 0 || + ipio_strcmp(tItems[index].desp, "raw data(no bk) (lcm off)") == 0) { + frame_buf[i] -= core_mp.no_bk_shift; + } + } + } + + ili_dump_data(frame_buf, 32, cdc_len, core_mp.xch_len, "Mutual CDC combined"); + +out: + ipio_kfree((void **)&ori); + return ret; +} + +static void compare_MaxMin_result(int index, s32 *data) +{ + int x, y; + if (tItems[index].catalog == TIPRING_RAW || tItems[index].catalog == TIPRING_P2P) { + for (x = 0; x < core_mp.tipring_len; x++) { + if (tItems[index].max_buf[x] < data[x]) + tItems[index].max_buf[x] = data[x]; + + if (tItems[index].min_buf[x] > data[x]) + tItems[index].min_buf[x] = data[x]; + } + } else { + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + int shift = y * core_mp.xch_len; + + if (tItems[index].catalog == UNTOUCH_P2P) + return; + else if (tItems[index].catalog == TX_RX_DELTA) { + /* Tx max/min comparison */ + if (core_mp.tx_delta_buf[shift + x] < data[shift + x]) + core_mp.tx_max_buf[shift + x] = data[shift + x]; + + if (core_mp.tx_delta_buf[shift + x] > data[shift + x]) + core_mp.tx_min_buf[shift + x] = data[shift + x]; + + /* Rx max/min comparison */ + if (core_mp.rx_delta_buf[shift + x] < data[shift + x]) + core_mp.rx_max_buf[shift + x] = data[shift + x]; + + if (core_mp.rx_delta_buf[shift + x] > data[shift + x]) + core_mp.rx_min_buf[shift + x] = data[shift + x]; + } else { + if (tItems[index].max_buf[shift + x] < data[shift + x]) + tItems[index].max_buf[shift + x] = data[shift + x]; + + if (tItems[index].min_buf[shift + x] > data[shift + x]) + tItems[index].min_buf[shift + x] = data[shift + x]; + } + } + } + } + +} + +static int create_mp_test_frame_buffer(int index, int frame_count) +{ + int i; + + ILI_DBG("Create MP frame buffers (index = %d), count = %d\n", + index, frame_count); + + if (tItems[index].catalog == TX_RX_DELTA) { + if (core_mp.tx_delta_buf == NULL) { + core_mp.tx_delta_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.tx_delta_buf)) { + ILI_ERR("Failed to allocate tx_delta_buf mem\n"); + ipio_kfree((void **)&core_mp.tx_delta_buf); + return -ENOMEM; + } + } + + if (core_mp.rx_delta_buf == NULL) { + core_mp.rx_delta_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.rx_delta_buf)) { + ILI_ERR("Failed to allocate rx_delta_buf mem\n"); + ipio_kfree((void **)&core_mp.rx_delta_buf); + return -ENOMEM; + } + } + + if (core_mp.tx_max_buf == NULL) { + core_mp.tx_max_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.tx_max_buf)) { + ILI_ERR("Failed to allocate tx_max_buf mem\n"); + ipio_kfree((void **)&core_mp.tx_max_buf); + return -ENOMEM; + } + } + + if (core_mp.tx_min_buf == NULL) { + core_mp.tx_min_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.tx_min_buf)) { + ILI_ERR("Failed to allocate tx_min_buf mem\n"); + ipio_kfree((void **)&core_mp.tx_min_buf); + return -ENOMEM; + } + } + + if (core_mp.rx_max_buf == NULL) { + core_mp.rx_max_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.rx_max_buf)) { + ILI_ERR("Failed to allocate rx_max_buf mem\n"); + ipio_kfree((void **)&core_mp.rx_max_buf); + return -ENOMEM; + } + } + + if (core_mp.rx_min_buf == NULL) { + core_mp.rx_min_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(core_mp.rx_min_buf)) { + ILI_ERR("Failed to allocate rx_min_buf mem\n"); + ipio_kfree((void **)&core_mp.rx_min_buf); + return -ENOMEM; + } + } + } else { + if (tItems[index].buf == NULL) { + tItems[index].buf = vmalloc(frame_count * core_mp.frame_len * sizeof(s32)); + if (ERR_ALLOC_MEM(tItems[index].buf)) { + ILI_ERR("Failed to allocate buf mem\n"); + ipio_kfree((void **)&tItems[index].buf); + return -ENOMEM; + } + } + + if (tItems[index].result_buf == NULL) { + tItems[index].result_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].result_buf)) { + ILI_ERR("Failed to allocate result_buf mem\n"); + ipio_kfree((void **)&tItems[index].result_buf); + return -ENOMEM; + } + } + + if (tItems[index].max_buf == NULL) { + tItems[index].max_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].max_buf)) { + ILI_ERR("Failed to allocate max_buf mem\n"); + ipio_kfree((void **)&tItems[index].max_buf); + return -ENOMEM; + } + } + + if (tItems[index].min_buf == NULL) { + tItems[index].min_buf = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].min_buf)) { + ILI_ERR("Failed to allocate min_buf mem\n"); + ipio_kfree((void **)&tItems[index].min_buf); + return -ENOMEM; + } + } + + if (tItems[index].spec_option == BENCHMARK) { + if (tItems[index].bch_mrk_max == NULL) { + tItems[index].bch_mrk_max = (s32 **)kzalloc(tItems[index].bch_mrk_frm_num * sizeof(s32 *), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bch_mrk_max)) { + ILI_ERR("Failed to allocate bch_mrk_max mem\n"); + ipio_kfree((void **)&tItems[index].bch_mrk_max); + return -ENOMEM; + } + + for (i = 0; i < tItems[index].bch_mrk_frm_num; i++) { + tItems[index].bch_mrk_max[i] = (s32 *)kzalloc(core_mp.frame_len * sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bch_mrk_max[i])) { + ILI_ERR("Failed to allocate bch_mrk_max[%d] mem\n", i); + ipio_kfree((void **)&tItems[index].bch_mrk_max[i]); + return -ENOMEM; + } + } + } + + if (tItems[index].bch_mrk_min == NULL) { + tItems[index].bch_mrk_min = (s32 **)kzalloc(tItems[index].bch_mrk_frm_num * sizeof(s32 *), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bch_mrk_min)) { + ILI_ERR("Failed to allocate bch_mrk_min mem\n"); + ipio_kfree((void **)&tItems[index].bch_mrk_min); + return -ENOMEM; + } + for (i = 0; i < tItems[index].bch_mrk_frm_num; i++) { + tItems[index].bch_mrk_min[i] = (s32 *)kzalloc(core_mp.frame_len * sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bch_mrk_min[i])) { + ILI_ERR("Failed to allocate bch_mrk_min[%d] mem\n", i); + ipio_kfree((void **)&tItems[index].bch_mrk_min[i]); + return -ENOMEM; + } + } + } + + if (tItems[index].bench_mark_max == NULL) { + tItems[index].bench_mark_max = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bench_mark_max)) { + ILI_ERR("Failed to allocate bench_mark_max mem\n"); + ipio_kfree((void **)&tItems[index].bench_mark_max); + return -ENOMEM; + } + } + if (tItems[index].bench_mark_min == NULL) { + tItems[index].bench_mark_min = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].bench_mark_min)) { + ILI_ERR("Failed to allocate bench_mark_min mem\n"); + ipio_kfree((void **)&tItems[index].bench_mark_min); + return -ENOMEM; + } + } + } + } + return 0; +} + +static int pin_test(int index) +{ + int ret = 0; + u8 cmd[5] = {0}; + + ILI_INFO("PIN test start"); + ILI_INFO("test_int_pin = 0x%x\n", tItems[index].test_int_pin); + ILI_INFO("int_pulse_test = 0x%x\n", tItems[index].int_pulse_test); + ILI_INFO("delay_time = 0x%x\n", tItems[index].delay_time); + + if (tItems[index].test_int_pin == ENABLE) { + /* test HIGH level*/ + cmd[0] = tItems[index].cmd; + cmd[1] = 0x1; + ret = ilits->wrapper(cmd, 2, NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write command failed\n"); + ret = -EMP_CMD; + goto out; + } + + ret = ilits->detect_int_stat(false); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + + /* test LOW level*/ + cmd[1] = 0x0; + ret = ilits->wrapper(cmd, 2, NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write command failed\n"); + ret = -EMP_CMD; + goto out; + } + + ret = ilits->detect_int_stat(false); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + } + + if (tItems[index].int_pulse_test == ENABLE) { + + ILI_INFO("MP IRQ Rising Trigger Test\n"); + cmd[1] = 0x2; + cmd[2] = tItems[index].delay_time; + + ili_irq_unregister(); + ret = ili_irq_register(IRQF_TRIGGER_RISING); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + + atomic_set(&ilits->cmd_int_check, ENABLE); + ret = ilits->wrapper(cmd, 3, NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write command failed\n"); + ret = -EMP_CMD; + goto out; + } + + ret = ilits->detect_int_stat(false); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + + ILI_INFO("MP IRQ Falling Trigger Test\n"); + ili_irq_unregister(); + ret = ili_irq_register(IRQF_TRIGGER_FALLING); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + + atomic_set(&ilits->cmd_int_check, ENABLE); + + ret = ilits->wrapper(cmd, 3, NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write command failed\n"); + ret = -EMP_CMD; + goto out; + } + + ret = ilits->detect_int_stat(false); + if (ret < 0) { + ret = -EMP_INT; + goto out; + } + } + + tItems[index].item_result = MP_DATA_PASS; + +out: + if (ret < 0) + tItems[index].item_result = MP_DATA_FAIL; + + ILI_INFO("Change to defualt IRQ trigger type\n"); + ili_irq_unregister(); + ili_irq_register(ilits->irq_tirgger_type); + atomic_set(&ilits->cmd_int_check, DISABLE); + + return ret; +} + +static int mutual_test(int index) +{ + int i = 0, j = 0, x = 0, y = 0, ret = 0, get_frame_cont = 1; + + ILI_DBG("index = %d, desp = %s, Frame Count = %d\n", + index, tItems[index].desp, tItems[index].frame_count); + + /* + * We assume that users who are calling the test forget to config frame count + * as 1, so we just help them to set it up. + */ + if (tItems[index].frame_count <= 0) { + ILI_ERR("Frame count is zero, which is at least set as 1\n"); + tItems[index].frame_count = 1; + } + + ret = create_mp_test_frame_buffer(index, tItems[index].frame_count); + if (ret < 0) { + ret = -EMP_NOMEM; + goto out; + } + + if (tItems[index].catalog == TIPRING_RAW) { + /* Init Max/Min buffer */ + for (x = 0; x < core_mp.tipring_len; x++) { + tItems[index].max_buf[x] = INT_MIN; + tItems[index].min_buf[x] = INT_MAX; + } + + get_frame_cont = tItems[index].frame_count; + + if (tItems[index].spec_option == BENCHMARK) { + parser_tip_ring_ini_benchmark(tItems[index].bench_mark_max, tItems[index].bench_mark_min, + tItems[index].type_option, tItems[index].desp, core_mp.tipring_len); + } + } else { + /* Init Max/Min buffer */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + if (tItems[i].catalog == TX_RX_DELTA) { + core_mp.tx_max_buf[y * core_mp.xch_len + x] = INT_MIN; + core_mp.rx_max_buf[y * core_mp.xch_len + x] = INT_MIN; + core_mp.tx_min_buf[y * core_mp.xch_len + x] = INT_MAX; + core_mp.rx_min_buf[y * core_mp.xch_len + x] = INT_MAX; + } else { + tItems[index].max_buf[y * core_mp.xch_len + x] = INT_MIN; + tItems[index].min_buf[y * core_mp.xch_len + x] = INT_MAX; + } + } + } + + if (tItems[index].spec_option == BENCHMARK) { + parser_ini_benchmark(tItems[index].bench_mark_max, tItems[index].bench_mark_min, + tItems[index].type_option, tItems[index].desp, core_mp.frame_len, BENCHMARK_KEY_NAME); + dump_benchmark_data(tItems[index].bench_mark_max, tItems[index].bench_mark_min); + } + } + + if (tItems[index].catalog != PEAK_TO_PEAK_TEST) + get_frame_cont = tItems[index].frame_count; + + + for (i = 0; i < get_frame_cont; i++) { + ret = allnode_mutual_cdc_data(index); + if (ret < 0) { + ILI_ERR("Failed to initialise CDC data, %d\n", ret); + goto out; + } + switch (tItems[index].catalog) { + case PIXEL: + run_pixel_test(index); + break; + case UNTOUCH_P2P: + run_untouch_p2p_test(index); + break; + case OPEN_TEST: + run_open_test(index); + break; + case TX_RX_DELTA: + run_tx_rx_delta_test(index); + break; + case SHORT_TEST: + ret = short_test(index, i); + if (ret < 0) { + ret = -EMP_PARA_NULL; + goto out; + } + break; + case TIPRING_RAW: + for (j = 0; j < core_mp.tipring_len; j++) + tItems[index].buf[i * core_mp.tipring_len + j] = frame_buf[j]; + break; + default: + for (j = 0; j < core_mp.frame_len; j++) + tItems[index].buf[i * core_mp.frame_len + j] = frame_buf[j]; + break; + } + if (tItems[index].catalog == TIPRING_RAW) + compare_MaxMin_result(index, &tItems[index].buf[i * core_mp.tipring_len]); + else + compare_MaxMin_result(index, &tItems[index].buf[i * core_mp.frame_len]); + + } + +out: + return ret; +} + +static int peak_to_peak_test(int index) +{ + int i = 0, j = 0, x = 0, y = 0, ret = 0, cdc_len = 0; + char benchmark_str[128] = {0}; + + if (tItems[index].catalog == TIPRING_P2P) + cdc_len = core_mp.tipring_len; + else + cdc_len = core_mp.frame_len; + + ILI_DBG("index = %d, desp = %s bch_mrk_frm_num = %d, cdc len = %d\n" + , index, tItems[index].desp, tItems[index].bch_mrk_frm_num, cdc_len); + + ret = create_mp_test_frame_buffer(index, tItems[index].bch_mrk_frm_num); + if (ret < 0) { + ret = -EMP_NOMEM; + goto out; + } + + if (tItems[index].spec_option == BENCHMARK) { + if (tItems[index].catalog == TIPRING_P2P) { + for (i = 0; i < tItems[index].bch_mrk_frm_num; i++) { + if (tItems[index].bch_mrk_multi){ + parser_ini_benchmark(tItems[index].bch_mrk_max[i], tItems[index].bch_mrk_min[i], tItems[index].type_option, + tItems[index].desp, core_mp.nPxRaw_tipring_len, get_csv_frame_name(BENCHMARK_KEY_NAME, i + 1)); + + parser_ini_benchmark(tItems[index].bch_mrk_max[i] + core_mp.nPxRaw_tipring_len, + tItems[index].bch_mrk_min[i] + core_mp.nPxRaw_tipring_len, tItems[index].type_option, tItems[index].desp, + core_mp.nPyRaw_tipring_len, get_csv_frame_name(BENCHMARK_KEY_NAME, i + 4)); + } else { + parser_ini_benchmark(tItems[index].bch_mrk_max[i], tItems[index].bch_mrk_min[i], tItems[index].type_option, + tItems[index].desp, core_mp.nPxRaw_tipring_len, get_csv_frame_name(BENCHMARK_KEY_NAME, 1)); + + parser_ini_benchmark(tItems[index].bch_mrk_max[i] + core_mp.nPxRaw_tipring_len, + tItems[index].bch_mrk_min[i] + core_mp.nPxRaw_tipring_len, tItems[index].type_option, + tItems[index].desp, core_mp.nPyRaw_tipring_len, get_csv_frame_name(BENCHMARK_KEY_NAME, 2)); + } + + if (debug_en) { + ili_dump_data(tItems[index].bch_mrk_max[i], 32, core_mp.tipring_len, 0, get_csv_frame_name("Dump Tip Ring Benchmark Max ", i + 1)); + ili_dump_data(tItems[index].bch_mrk_min[i], 32, core_mp.tipring_len, 0, get_csv_frame_name("Dump Tip Ring Benchmark Min ", i + 1)); + } + } + } else { + for (i = 0; i < tItems[index].bch_mrk_frm_num; i++) { + if (tItems[index].bch_mrk_multi) + snprintf(benchmark_str, sizeof(benchmark_str), "%s%d", BENCHMARK_KEY_NAME, i+1); + else + snprintf(benchmark_str, sizeof(benchmark_str), "%s", BENCHMARK_KEY_NAME); + + parser_ini_benchmark(tItems[index].bch_mrk_max[i], tItems[index].bch_mrk_min[i], + tItems[index].type_option, tItems[index].desp, core_mp.frame_len, benchmark_str); + + dump_benchmark_data(tItems[index].bch_mrk_max[i], tItems[index].bch_mrk_min[i]); + } + } + } + + /* Init Max/Min buffer */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].max_buf[y * core_mp.xch_len + x] = INT_MIN; + tItems[index].min_buf[y * core_mp.xch_len + x] = INT_MAX; + } + } + + ret = allnode_peak_to_peak_cdc_data(index); + if (ret < 0) { + ILI_ERR("Failed to initialise CDC data, %d\n", ret); + goto out; + } + for (i = 0; i < tItems[index].bch_mrk_frm_num; i++) { + for (j = 0; j < cdc_len; j++) + tItems[index].buf[i * cdc_len + j] = frm_buf[i][j]; + + compare_MaxMin_result(index, &tItems[index].buf[i * cdc_len]); + } + +out: + return ret; +} + + +static int open_test_sp(int index) +{ + struct mp_test_P540_open *open; + int i = 0, x = 0, y = 0, ret = 0, addr = 0; + int Charge_AA = 0, Charge_Border = 0, Charge_Notch = 0, full_open_rate = 0; + char str[512] = {0}; + + ILI_DBG("index = %d, desp = %s, Frame Count = %d\n", + index, tItems[index].desp, tItems[index].frame_count); + + /* + * We assume that users who are calling the test forget to config frame count + * as 1, so we just help them to set it up. + */ + if (tItems[index].frame_count <= 0) { + ILI_ERR("Frame count is zero, which is at least set as 1\n"); + tItems[index].frame_count = 1; + } + + open = kzalloc(tItems[index].frame_count * sizeof(struct mp_test_P540_open), GFP_KERNEL); + if (ERR_ALLOC_MEM(open)) { + ILI_ERR("Failed to allocate open mem (%ld)\n", PTR_ERR(open)); + ret = -EMP_NOMEM; + goto out; + } + + ret = create_mp_test_frame_buffer(index, tItems[index].frame_count); + if (ret < 0) { + ret = -EMP_NOMEM; + goto out; + } + + if (frame1_cbk700 == NULL) { + frame1_cbk700 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frame1_cbk700)) { + ILI_ERR("Failed to allocate frame1_cbk700 buffer\n"); + return -EMP_NOMEM; + } + } else { + memset(frame1_cbk700, 0x0, core_mp.frame_len); + } + + if (frame1_cbk250 == NULL) { + frame1_cbk250 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frame1_cbk250)) { + ILI_ERR("Failed to allocate frame1_cbk250 buffer\n"); + ipio_kfree((void **)&frame1_cbk700); + return -EMP_NOMEM; + } + } else { + memset(frame1_cbk250, 0x0, core_mp.frame_len); + } + + if (frame1_cbk200 == NULL) { + frame1_cbk200 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(frame1_cbk200)) { + ILI_ERR("Failed to allocate cbk buffer\n"); + ipio_kfree((void **)&frame1_cbk700); + ipio_kfree((void **)&frame1_cbk250); + return -EMP_NOMEM; + } + } else { + memset(frame1_cbk200, 0x0, core_mp.frame_len); + } + + tItems[index].node_type = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(tItems[index].node_type)) { + ILI_ERR("Failed to allocate node_type FRAME buffer\n"); + return -EMP_NOMEM; + } + + /* Init Max/Min buffer */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].max_buf[y * core_mp.xch_len + x] = INT_MIN; + tItems[index].min_buf[y * core_mp.xch_len + x] = INT_MAX; + } + } + + if (tItems[index].spec_option == BENCHMARK) { + parser_ini_benchmark(tItems[index].bench_mark_max, tItems[index].bench_mark_min, + tItems[index].type_option, tItems[index].desp, core_mp.frame_len, BENCHMARK_KEY_NAME); + dump_benchmark_data(tItems[index].bench_mark_max, tItems[index].bench_mark_min); + } + + parser_ini_nodetype(tItems[index].node_type, NODE_TYPE_KEY_NAME, core_mp.frame_len); + dump_node_type_buffer(tItems[index].node_type, "node type"); + + parser_get_int_data(tItems[index].desp, "charge_aa", str, sizeof(str)); + Charge_AA = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "charge_border", str, sizeof(str)); + Charge_Border = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "charge_notch", str, sizeof(str)); + Charge_Notch = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "full open", str, sizeof(str)); + full_open_rate = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "tvch", str, sizeof(str)); + open_para.tvch = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "tvcl", str, sizeof(str)); + open_para.tvcl = ili_katoi(str); + + ILI_DBG("AA = %d, Border = %d, Notch = %d, full_open_rate = %d, tvch = %d, tvcl = %d\n", + Charge_AA, Charge_Border, Charge_Notch, + full_open_rate, open_para.tvch, open_para.tvcl); + + for (i = 0; i < tItems[index].frame_count; i++) { + open[i].tdf_700 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].tdf_250 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].tdf_200 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].cbk_700 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].cbk_250 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].cbk_200 = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].charg_rate = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].full_Open = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].dac = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + } + + for (i = 0; i < tItems[index].frame_count; i++) { + ret = allnode_open_cdc_data(0, open[i].dac, index); + if (ret < 0) { + ILI_ERR("Failed to get Open SP DAC data, %d\n", ret); + goto out; + } + ret = allnode_open_cdc_data(1, open[i].tdf_700, index); + if (ret < 0) { + ILI_ERR("Failed to get Open SP Raw1 data, %d\n", ret); + goto out; + } + ret = allnode_open_cdc_data(2, open[i].tdf_250, index); + if (ret < 0) { + ILI_ERR("Failed to get Open SP Raw2 data, %d\n", ret); + goto out; + } + ret = allnode_open_cdc_data(3, open[i].tdf_200, index); + if (ret < 0) { + ILI_ERR("Failed to get Open SP Raw3 data, %d\n", ret); + goto out; + } + allnode_open_cdc_result(index, open[i].cbk_700, open[i].dac, open[i].tdf_700); + allnode_open_cdc_result(index, open[i].cbk_250, open[i].dac, open[i].tdf_250); + allnode_open_cdc_result(index, open[i].cbk_200, open[i].dac, open[i].tdf_200); + + addr = 0; + + /* record fist frame for debug */ + if (i == 0) { + ipio_memcpy(frame1_cbk700, open[i].cbk_700, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + ipio_memcpy(frame1_cbk250, open[i].cbk_250, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + ipio_memcpy(frame1_cbk200, open[i].cbk_200, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + } + + ili_dump_data(open[i].cbk_700, 10, core_mp.frame_len, core_mp.xch_len, "cbk 700"); + ili_dump_data(open[i].cbk_250, 10, core_mp.frame_len, core_mp.xch_len, "cbk 250"); + ili_dump_data(open[i].cbk_200, 10, core_mp.frame_len, core_mp.xch_len, "cbk 200"); + + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + open[i].charg_rate[addr] = open[i].cbk_250[addr] * 100 / open[i].cbk_700[addr]; + open[i].full_Open[addr] = open[i].cbk_700[addr] - open[i].cbk_200[addr]; + addr++; + } + } + + ili_dump_data(open[i].charg_rate, 10, core_mp.frame_len, core_mp.xch_len, "origin charge rate"); + ili_dump_data(open[i].full_Open, 10, core_mp.frame_len, core_mp.xch_len, "origin full open"); + + addr = 0; + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + if (full_open_rate_compare(open[i].full_Open, open[i].cbk_700, x, y, tItems[index].node_type[addr], full_open_rate) == false) { + tItems[index].buf[(i * core_mp.frame_len) + addr] = 0; + open[i].charg_rate[addr] = 0; + } + addr++; + } + } + + ili_dump_data(&tItems[index].buf[(i * core_mp.frame_len)], 10, core_mp.frame_len, core_mp.xch_len, "after full_open_rate_compare"); + + addr = 0; + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].buf[(i * core_mp.frame_len) + addr] = compare_charge(open[i].charg_rate, x, y, tItems[index].node_type, Charge_AA, Charge_Border, Charge_Notch); + addr++; + } + } + + ili_dump_data(&tItems[index].buf[(i * core_mp.frame_len)], 10, core_mp.frame_len, core_mp.xch_len, "after compare charge rate"); + + compare_MaxMin_result(index, &tItems[index].buf[(i * core_mp.frame_len)]); + } + +out: + ipio_kfree((void **)&tItems[index].node_type); + + if (open != NULL) { + for (i = 0; i < tItems[index].frame_count; i++) { + ipio_kfree((void **)&open[i].tdf_700); + ipio_kfree((void **)&open[i].tdf_250); + ipio_kfree((void **)&open[i].tdf_200); + ipio_kfree((void **)&open[i].cbk_700); + ipio_kfree((void **)&open[i].cbk_250); + ipio_kfree((void **)&open[i].cbk_200); + ipio_kfree((void **)&open[i].charg_rate); + ipio_kfree((void **)&open[i].full_Open); + ipio_kfree((void **)&open[i].dac); + } + kfree(open); + open = NULL; + } + + return ret; +} + +static int open_test_cap(int index) +{ + u32 pid = core_mp.chip_pid >> 8; + struct mp_test_open_c *open; + int i = 0, x = 0, y = 0, ret = 0, addr = 0; + bool isOpenX = DISABLE; + char str[512] = {0}; + + isOpenX = (ipio_strcmp(tItems[index].desp, "open test_c (openx enable)") == 0) ? ENABLE : DISABLE; + + ILI_DBG("index = %d, desp = %s, Frame Count = %d, isOpenX = %d\n", + index, tItems[index].desp, tItems[index].frame_count, isOpenX); + + if (tItems[index].frame_count <= 0) { + ILI_ERR("Frame count is zero, which is at least set as 1\n"); + tItems[index].frame_count = 1; + } + + open = kzalloc(tItems[index].frame_count * sizeof(struct mp_test_open_c), GFP_KERNEL); + if (ERR_ALLOC_MEM(open)) { + ILI_ERR("Failed to allocate open mem (%ld)\n", PTR_ERR(open)); + ret = -EMP_NOMEM; + goto out; + } + + if (create_mp_test_frame_buffer(index, tItems[index].frame_count) < 0) { + ret = -EMP_NOMEM; + goto out; + } + + if (isOpenX) { + if (openx_cap_dac == NULL) { + openx_cap_dac = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(openx_cap_dac)) { + ILI_ERR("Failed to allocate cap_dac buffer\n"); + return -EMP_NOMEM; + } + } else { + memset(openx_cap_dac, 0x0, core_mp.frame_len); + } + + if (openx_cap_raw == NULL) { + openx_cap_raw = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(openx_cap_raw)) { + ILI_ERR("Failed to allocate cap_raw buffer\n"); + ipio_kfree((void **)&openx_cap_dac); + return -EMP_NOMEM; + } + } else { + memset(openx_cap_raw, 0x0, core_mp.frame_len); + } + } else { + if (cap_dac == NULL) { + cap_dac = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(cap_dac)) { + ILI_ERR("Failed to allocate cap_dac buffer\n"); + return -EMP_NOMEM; + } + } else { + memset(cap_dac, 0x0, core_mp.frame_len); + } + + if (cap_raw == NULL) { + cap_raw = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(cap_raw)) { + ILI_ERR("Failed to allocate cap_raw buffer\n"); + ipio_kfree((void **)&cap_dac); + return -EMP_NOMEM; + } + } else { + memset(cap_raw, 0x0, core_mp.frame_len); + } + } + + /* Init Max/Min buffer */ + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].max_buf[y * core_mp.xch_len + x] = INT_MIN; + tItems[index].min_buf[y * core_mp.xch_len + x] = INT_MAX; + } + } + + if (tItems[index].spec_option == BENCHMARK) { + parser_ini_benchmark(tItems[index].bench_mark_max, tItems[index].bench_mark_min, + tItems[index].type_option, tItems[index].desp, core_mp.frame_len, BENCHMARK_KEY_NAME); + dump_benchmark_data(tItems[index].bench_mark_max, tItems[index].bench_mark_min); + } + + parser_get_int_data(tItems[index].desp, "gain", str, sizeof(str)); + open_para.gain = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "tvch", str, sizeof(str)); + open_para.tvch = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "tvcl", str, sizeof(str)); + open_para.tvcl = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "cbk_step", str, sizeof(str)); + open_para.cbk_step = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "cint", str, sizeof(str)); + open_para.cint = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "vbk", str, sizeof(str)); + open_para.invbk = ili_katoi(str); + + parser_get_int_data(tItems[index].desp, "vadc_range", str, sizeof(str)); + open_para.vadc_range = ili_katoi(str); + + /* 9881N, 9881O, 7807Q, 7807S not support cbk_step and cint read from mp.ini */ + if ((pid != 0x988117) && (pid != 0x988118) && (pid != 0x78071A) && (pid != 0x78071C)) { + if ((open_para.cbk_step == 0) || (open_para.cint == 0)) { + ILI_ERR("Failed to get open parameter"); + ret = -EMP_PARA_NULL; + core_mp.lost_parameter = true; + goto out; + } + } + + ILI_INFO("gain = %d, tvch = %d, tvcl = %d, cbk_step = %d, cint = %d\n", open_para.gain, open_para.tvch, open_para.tvcl, open_para.cbk_step, open_para.cint); + for (i = 0; i < tItems[index].frame_count; i++) { + open[i].cap_dac = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].cap_raw = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + open[i].dcl_cap = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + } + + for (i = 0; i < tItems[index].frame_count; i++) { + if (isOpenX) { + ret = allnode_open_cdc_data(6, open[i].cap_dac, index); + if (ret < 0) { + ILI_ERR("Failed to get Open_X CAP DAC data, %d\n", ret); + goto out; + } + ret = allnode_open_cdc_data(7, open[i].cap_raw, index); + if (ret < 0) { + ILI_ERR("Failed to get Open_X CAP RAW data, %d\n", ret); + goto out; + } + } else { + ret = allnode_open_cdc_data(4, open[i].cap_dac, index); + if (ret < 0) { + ILI_ERR("Failed to get Open CAP DAC data, %d\n", ret); + goto out; + } + ret = allnode_open_cdc_data(5, open[i].cap_raw, index); + if (ret < 0) { + ILI_ERR("Failed to get Open CAP RAW data, %d\n", ret); + goto out; + } + } + + allnode_open_cdc_result(index, open[i].dcl_cap, open[i].cap_dac, open[i].cap_raw); + + /* record fist frame for debug */ + if (i == 0) { + if (isOpenX) { + ipio_memcpy(openx_cap_dac, open[i].cap_dac, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + ipio_memcpy(openx_cap_raw, open[i].cap_raw, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + } else { + ipio_memcpy(cap_dac, open[i].cap_dac, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + ipio_memcpy(cap_raw, open[i].cap_raw, core_mp.frame_len * sizeof(s32), core_mp.frame_len * sizeof(s32)); + } + } + + ili_dump_data(open[i].dcl_cap, 10, core_mp.frame_len, core_mp.xch_len, "DCL_Cap"); + + addr = 0; + for (y = 0; y < core_mp.ych_len; y++) { + for (x = 0; x < core_mp.xch_len; x++) { + tItems[index].buf[(i * core_mp.frame_len) + addr] = open[i].dcl_cap[addr]; + addr++; + } + } + compare_MaxMin_result(index, &tItems[index].buf[i * core_mp.frame_len]); + } + +out: + if (open != NULL) { + for (i = 0; i < tItems[index].frame_count; i++) { + ipio_kfree((void **)&open[i].cap_dac); + ipio_kfree((void **)&open[i].cap_raw); + ipio_kfree((void **)&open[i].dcl_cap); + } + kfree(open); + open = NULL; + } + + return ret; +} + +static int key_test(int index) +{ + int i, j = 0, ret = 0; + + ILI_DBG("Item = %s, Frame Count = %d\n", + tItems[index].desp, tItems[index].frame_count); + + if (tItems[index].frame_count == 0) { + ILI_ERR("Frame count is zero, which at least sets as 1\n"); + ret = -EINVAL; + goto out; + } + + ret = create_mp_test_frame_buffer(index, tItems[index].frame_count); + if (ret < 0) + goto out; + + for (i = 0; i < tItems[index].frame_count; i++) { + ret = allnode_key_cdc_data(index); + if (ret < 0) { + ILI_ERR("Failed to initialise CDC data, %d\n", ret); + goto out; + } + + for (j = 0; j < core_mp.key_len; j++) + tItems[index].buf[j] = key_buf[j]; + } + + compare_MaxMin_result(index, tItems[index].buf); + +out: + return ret; +} + +static int self_test(int index) +{ + ILI_ERR("TDDI has no self to be tested currently\n"); + return -1; +} + +static int st_test(int index) +{ + ILI_ERR("ST Test is not supported by the driver\n"); + return -1; +} + +int ili_parsing_sram_param(void) +{ + char str[100] = {0}; + int i, ret = 0; + + memset(&ilits->sram_para, 0x0, sizeof(ilits->sram_para)); + + if (ini_info == NULL) { + ini_info = (struct ini_file_data *)vmalloc(sizeof(struct ini_file_data) * PARSER_MAX_KEY_NUM); + if (ERR_ALLOC_MEM(ini_info)) { + ILI_ERR("Failed to malloc ini_info\n"); + ret = -EMP_NOMEM; + goto out; + } + } + + ret = ilitek_tddi_mp_ini_parser(ilits->md_ini_path); + if (ret < 0) { + ILI_ERR("Failed to parsing INI file\n"); + ret = -EMP_INI; + goto out; + } + + parser_get_int_data("sram test", "free run tpu clock enable", str, sizeof(str)); + ilits->sram_para.tpu_clock_en = ili_katoi(str); + + ret = parser_get_int_data("sram test", "sram group enable", str, sizeof(str)); + ilits->sram_para.sram_group_en = ili_katoi(str); + + if (ret <= 0) { + ILI_ERR("Mp ini not support sram test, Please update tool version.\n"); + goto out; + } + + parser_get_int_data("sram test", "check fail bus enable", str, sizeof(str)); + ilits->sram_para.check_failbus = ili_katoi(str); + + parser_get_int_data("sram test", "max addr", str, sizeof(str)); + parser_get_u32_array(str, &ilits->sram_para.MaxAddr, 16, sizeof(ilits->sram_para.MaxAddr)); + + parser_get_int_data("sram test", "min addr", str, sizeof(str)); + parser_get_u32_array(str, &ilits->sram_para.MinAddr, 16, sizeof(ilits->sram_para.MinAddr)); + + ILI_INFO("Free Run TPU Clock EN : %s, Sram Group EN : %s, Check Fail Bus EN : %s\n", ilits->sram_para.tpu_clock_en ? "ENABLE" : "DISABLE", ilits->sram_para.sram_group_en ? "ENABLE" : "DISABLE", ilits->sram_para.check_failbus ? "ENABLE" : "DISABLE"); + ILI_INFO("MaxAddr = 0x%X, MinAddr = 0x%X\n", ilits->sram_para.MaxAddr, ilits->sram_para.MinAddr); + + if (ilits->sram_para.sram_group_en == ENABLE) { + parser_get_int_data("sram test", "sram group", str, sizeof(str)); + ilits->sram_para.group_num = parser_sramtest_group(str, ilits->sram_para.strSramPartialArea); + + parser_get_int_data("sram test", "group write addr", str, sizeof(str)); + parser_get_u32_array(str, &ilits->sram_para.write_addr, 16, sizeof(ilits->sram_para.write_addr)); + + parser_get_int_data("sram test", "group write", str, sizeof(str)); + parser_get_u32_array(str, ilits->sram_para.SramPartialAreaTest_en, 16, sizeof(ilits->sram_para.SramPartialAreaTest_en)); + + parser_get_int_data("sram test", "group read addr", str, sizeof(str)); + parser_get_u32_array(str, &ilits->sram_para.read_addr, 16, sizeof(ilits->sram_para.read_addr)); + + parser_get_int_data("sram test", "group result", str, sizeof(str)); + parser_get_u32_array(str, ilits->sram_para.SramResult, 16, sizeof(ilits->sram_para.SramResult)); + + ILI_INFO("Group Number : %d, Write Addr = 0x%X, Read Addr = 0x%X\n", ilits->sram_para.group_num, ilits->sram_para.write_addr, ilits->sram_para.read_addr); + + for( i = 0; i < ilits->sram_para.group_num; i++) { + ILI_INFO("Sram Partial Area : %s, Sram Partial Area Test en = 0x%X, Sram Result = 0x%X\n", ilits->sram_para.strSramPartialArea[i], ilits->sram_para.SramPartialAreaTest_en[i], ilits->sram_para.SramResult[i]); + } + } + +out: + ipio_vfree((void **)&ini_info); + return ret; +} + +static int mp_get_timing_info(void) +{ + int slen = 0; + char str[256] = {0}; + u8 info[64] = {0}; + char *key = "timing_info_raw"; + + core_mp.isLongV = 0; + + slen = parser_get_int_data("pv5_4 command", key, str, sizeof(str)); + if (slen < 0) + return -1; + + if (parser_get_u8_array(str, info, 16, slen) < 0) + return -1; + + core_mp.isLongV = info[6]; + + ILI_INFO("DDI Mode = %s\n", (core_mp.isLongV ? "Long V" : "Long H")); + + return 0; +} + +static int mp_test_data_sort_average(s32 *oringin_data, int index, s32 *avg_result) +{ + int i, j, k, x, y, len = 5, size; + s32 u32temp; + int u32up_frame, u32down_frame; + s32 *u32sum_raw_data = NULL; + s32 *u32data_buff = NULL; + int ret = 0; + + if (tItems[index].frame_count <= 1) { + ret = 0; + goto out; + } + + if (ERR_ALLOC_MEM(oringin_data)) { + ILI_ERR("Input wrong address\n"); + ret = -ENOMEM; + goto out; + } + + u32data_buff = kcalloc(core_mp.frame_len * tItems[index].frame_count, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(u32data_buff)) { + ILI_ERR("Failed to allocate u32data_buff FRAME buffer\n"); + ret = -ENOMEM; + goto out; + } + + u32sum_raw_data = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(u32data_buff)) { + ILI_ERR("Failed to allocate u32sum_raw_data FRAME buffer\n"); + ret = -ENOMEM; + goto out; + } + + size = core_mp.frame_len * tItems[index].frame_count; + for (i = 0; i < size; i++) { + u32data_buff[i] = oringin_data[i]; + } + + u32up_frame = tItems[index].frame_count * tItems[index].highest_percentage / 100; + u32down_frame = tItems[index].frame_count * tItems[index].lowest_percentage / 100; + ILI_DBG("Up=%d, Down=%d -%s\n", u32up_frame, u32down_frame, tItems[index].desp); + + if (debug_en) { + pr_cont("\n[Show Original frist%d and last%d node data]\n", len, len); + for (i = 0; i < core_mp.frame_len; i++) { + for (j = 0 ; j < tItems[index].frame_count ; j++) { + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("%d,", u32data_buff[j * core_mp.frame_len + i]); + } + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("\n"); + } + } + + for (i = 0; i < core_mp.frame_len; i++) { + for (j = 0; j < tItems[index].frame_count-1; j++) { + for (k = 0; k < (tItems[index].frame_count-1-j); k++) { + x = i+k*core_mp.frame_len; + y = i+(k+1)*core_mp.frame_len; + if (*(u32data_buff+x) > *(u32data_buff+y)) { + u32temp = *(u32data_buff+x); + *(u32data_buff+x) = *(u32data_buff+y); + *(u32data_buff+y) = u32temp; + } + } + } + } + + if (debug_en) { + pr_cont("\n[After sorting frist%d and last%d node data]\n", len, len); + for (i = 0; i < core_mp.frame_len; i++) { + for (j = u32down_frame; j < tItems[index].frame_count - u32up_frame; j++) { + if ((i < len) || (i >= (core_mp.frame_len - len))) + pr_cont("%d,", u32data_buff[i + j * core_mp.frame_len]); + } + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("\n"); + } + } + + for (i = 0 ; i < core_mp.frame_len ; i++) { + u32sum_raw_data[i] = 0; + for (j = u32down_frame; j < tItems[index].frame_count - u32up_frame; j++) + u32sum_raw_data[i] += u32data_buff[i + j * core_mp.frame_len]; + + avg_result[i] = u32sum_raw_data[i] / (tItems[index].frame_count - u32down_frame - u32up_frame); + } + + if (debug_en) { + pr_cont("\n[Average result frist%d and last%d node data]\n", len, len); + for (i = 0; i < core_mp.frame_len; i++) { + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("%d,", avg_result[i]); + } + if ((i < len) || (i >= (core_mp.frame_len-len))) + pr_cont("\n"); + } + +out: + ipio_kfree((void **)&u32data_buff); + ipio_kfree((void **)&u32sum_raw_data); + return ret; +} + +static void mp_compare_cdc_result(int index, s32 *tmp, s32 *max_ts, s32 *min_ts, int *result) +{ + int i, cdclen; + + if (tItems[index].catalog == TIPRING_RAW || tItems[index].catalog == TIPRING_P2P) + cdclen = core_mp.tipring_len; + else + cdclen = core_mp.frame_len; + + if (ERR_ALLOC_MEM(tmp)) { + ILI_ERR("The data of test item is null (%p)\n", tmp); + *result = MP_DATA_FAIL; + return; + } + + if (tItems[index].catalog == SHORT_TEST) { + for (i = 0; i < core_mp.frame_len; i++) { + if (tmp[i] < min_ts[i]) { + *result = MP_DATA_FAIL; + return; + } + } + } else { + for (i = 0; i < cdclen; i++) { + if (tmp[i] > max_ts[i] || tmp[i] < min_ts[i]) { + ILI_DBG("Fail No.%d: max=%d, val=%d, min=%d\n", i, max_ts[i], tmp[i], min_ts[i]); + *result = MP_DATA_FAIL; + return; + } + } + } +} + +static int mp_compare_test_result(int index) +{ + int i, test_result = MP_DATA_PASS, cdclen; + s32 *max_threshold = NULL, *min_threshold = NULL; + + if (tItems[index].catalog == PIN_TEST) + return tItems[index].item_result; + + max_threshold = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(max_threshold)) { + ILI_ERR("Failed to allocate threshold FRAME buffer\n"); + test_result = MP_DATA_FAIL; + goto out; + } + + min_threshold = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(min_threshold)) { + ILI_ERR("Failed to allocate threshold FRAME buffer\n"); + test_result = MP_DATA_FAIL; + goto out; + } + + if (tItems[index].catalog == TIPRING_RAW) + cdclen = core_mp.tipring_len; + else + cdclen = core_mp.frame_len; + + /* Show test result as below */ + if (tItems[index].catalog == TX_RX_DELTA) { + if (ERR_ALLOC_MEM(core_mp.rx_delta_buf) || ERR_ALLOC_MEM(core_mp.tx_delta_buf)) { + ILI_ERR("This test item (%s) has no data inside its buffer\n", tItems[index].desp); + test_result = MP_DATA_FAIL; + goto out; + } + + for (i = 0; i < core_mp.frame_len; i++) { + max_threshold[i] = core_mp.TxDeltaMax; + min_threshold[i] = core_mp.TxDeltaMin; + } + mp_compare_cdc_result(index, core_mp.tx_max_buf, max_threshold, min_threshold, &test_result); + mp_compare_cdc_result(index, core_mp.tx_min_buf, max_threshold, min_threshold, &test_result); + + for (i = 0; i < core_mp.frame_len; i++) { + max_threshold[i] = core_mp.RxDeltaMax; + min_threshold[i] = core_mp.RxDeltaMin; + } + + mp_compare_cdc_result(index, core_mp.rx_max_buf, max_threshold, min_threshold, &test_result); + mp_compare_cdc_result(index, core_mp.rx_min_buf, max_threshold, min_threshold, &test_result); + } else { + if (ERR_ALLOC_MEM(tItems[index].buf) || ERR_ALLOC_MEM(tItems[index].max_buf) || + ERR_ALLOC_MEM(tItems[index].min_buf) || ERR_ALLOC_MEM(tItems[index].result_buf)) { + ILI_ERR("This test item (%s) has no data inside its buffer\n", tItems[index].desp); + test_result = MP_DATA_FAIL; + goto out; + } + + if (tItems[index].spec_option == BENCHMARK) { + if (tItems[index].catalog == PEAK_TO_PEAK_TEST || tItems[index].catalog == TIPRING_P2P) { + for (i = 0; i < cdclen; i++) { + max_threshold[i] = tItems[index].bch_mrk_max[0][i]; + min_threshold[i] = tItems[index].bch_mrk_min[0][i]; + } + } else { + for (i = 0; i < cdclen; i++) { + max_threshold[i] = tItems[index].bench_mark_max[i]; + min_threshold[i] = tItems[index].bench_mark_min[i]; + } + } + } else { + if (tItems[index].catalog == TIPRING_RAW || tItems[index].catalog == TIPRING_P2P) { + for (i = 0; i < core_mp.nPxRaw_tipring_len; i++) { + max_threshold[i] = tItems[index].max1; + min_threshold[i] = tItems[index].min1; + } + for (i = core_mp.nPxRaw_tipring_len; i < core_mp.tipring_len; i++) { + max_threshold[i] = tItems[index].max2; + min_threshold[i] = tItems[index].min2; + } + } else { + for (i = 0; i < cdclen; i++) { + max_threshold[i] = tItems[index].max; + min_threshold[i] = tItems[index].min; + } + } + } + + /* general result */ + if (tItems[index].trimmed_mean && tItems[index].catalog != PEAK_TO_PEAK_TEST) { + mp_test_data_sort_average(tItems[index].buf, index, tItems[index].result_buf); + mp_compare_cdc_result(index, tItems[index].result_buf, max_threshold, min_threshold, &test_result); + } else { + if (tItems[index].bch_mrk_multi) { + for (i = 0; i < tItems[index].bch_mrk_frm_num; i++) { + mp_compare_cdc_result(index, frm_buf[i], tItems[index].bch_mrk_max[i], tItems[index].bch_mrk_min[i], &test_result); + } + } else { + mp_compare_cdc_result(index, tItems[index].max_buf, max_threshold, min_threshold, &test_result); + mp_compare_cdc_result(index, tItems[index].min_buf, max_threshold, min_threshold, &test_result); + } + } + } + +out: + ipio_kfree((void **)&max_threshold); + ipio_kfree((void **)&min_threshold); + if (frm_buf != NULL) { + for (i = 0; i < tItems[index].bch_mrk_frm_num; i++) + if (frm_buf[i] != NULL) { + kfree(frm_buf[i]); + frm_buf[i] = NULL; + } + kfree(frm_buf); + frm_buf = NULL; + } + if (ilits->eng_flow == true) { + test_result = MP_DATA_PASS; + } + tItems[index].item_result = test_result; + return test_result; +} + +static void mp_do_retry(int index, int count) +{ + if (count == 0) { + ILI_INFO("Finish retry action\n"); + return; + } + + ILI_INFO("retry = %d, item = %s\n", count, tItems[index].desp); + + tItems[index].do_test(index); + + if (mp_compare_test_result(index) < 0) + return mp_do_retry(index, count - 1); +} + +static int mp_show_result(bool lcm_on) +{ + int ret = MP_DATA_PASS, seq; + int i, x, y, j, csv_len = 0, pass_item_count = 0, line_count = 0, get_frame_cont = 1; + s32 *max_threshold = NULL, *min_threshold = NULL; + char *csv = NULL; + char *ret_pass_name = NULL, *ret_fail_name = NULL; +#if (!GENERIC_KERNEL_IMAGE) + struct file *f = NULL; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + mm_segment_t fs; +#endif + loff_t pos; +#endif + csv = vmalloc(CSV_FILE_SIZE); + if (ERR_ALLOC_MEM(csv)) { + ILI_ERR("Failed to allocate CSV mem\n"); + ret = -EMP_NOMEM; + goto fail_open; + } + + max_threshold = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + min_threshold = kcalloc(core_mp.frame_len, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(max_threshold) || ERR_ALLOC_MEM(min_threshold)) { + ILI_ERR("Failed to allocate threshold FRAME buffer\n"); + ret = -EMP_NOMEM; + goto fail_open; + } + + if (parser_get_ini_key_value("pv5_4 command", "date", core_mp.ini_date) < 0) + ipio_memcpy(core_mp.ini_date, "Unknown", strlen("Unknown"), strlen("Unknown")); + if (parser_get_ini_key_value("pv5_4 command", "version", core_mp.ini_ver) < 0) + ipio_memcpy(core_mp.ini_ver, "Unknown", strlen("Unknown"), strlen("Unknown")); + + mp_print_csv_header(csv, &csv_len, &line_count, CSV_FILE_SIZE); + + for (seq = 0; seq < ri.count; seq++) { + i = ri.index[seq]; + get_frame_cont = 1; + + if (tItems[i].item_result == MP_DATA_PASS) { + pr_info("\n[%s],OK \n\n", tItems[i].desp); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "\n[%s],OK\n\n", tItems[i].desp); + } else { + pr_info("\n[%s],NG \n\n", tItems[i].desp); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "\n[%s],NG\n\n", tItems[i].desp); + } + + if (tItems[i].catalog == PIN_TEST) { + pr_info("Test INT Pin = %d\n", tItems[i].test_int_pin); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Test INT Pin = %d\n", tItems[i].test_int_pin); + pr_info("Pulse Test = %d\n", tItems[i].int_pulse_test); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Pulse Test = %d\n", tItems[i].int_pulse_test); + pr_info("Delay Time = %d\n", tItems[i].delay_time); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Delay Time = %d\n", tItems[i].delay_time); + continue; + } + + mp_print_csv_cdc_cmd(csv, &csv_len, i, CSV_FILE_SIZE); + + pr_info("Frame count = %d\n", tItems[i].frame_count); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Frame count = %d\n", tItems[i].frame_count); + + if (tItems[i].trimmed_mean && tItems[i].catalog != PEAK_TO_PEAK_TEST) { + pr_info("lowest percentage = %d\n", tItems[i].lowest_percentage); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "lowest percentage = %d\n", tItems[i].lowest_percentage); + + pr_info("highest percentage = %d\n", tItems[i].highest_percentage); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "highest percentage = %d\n", tItems[i].highest_percentage); + } + + /* Show result of benchmark max and min */ + if (tItems[i].spec_option == BENCHMARK) { + if (tItems[i].catalog == PEAK_TO_PEAK_TEST) { + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = tItems[i].bch_mrk_max[0][j]; + min_threshold[j] = tItems[i].bch_mrk_min[0][j]; + } + + for (j = 0; j < tItems[i].bch_mrk_frm_num; j++) { + char bench_name[128] = {0}; + snprintf(bench_name, (CSV_FILE_SIZE - csv_len), "Max_Bench%d", (j+1)); + mp_compare_cdc_show_result(i, tItems[i].bch_mrk_max[j], csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, bench_name, CSV_FILE_SIZE); + snprintf(bench_name, (CSV_FILE_SIZE - csv_len), "Min_Bench%d", (j+1)); + mp_compare_cdc_show_result(i, tItems[i].bch_mrk_min[j], csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, bench_name, CSV_FILE_SIZE); + } + } else if (tItems[i].catalog == TIPRING_RAW) { + for (j = 0; j < core_mp.tipring_len; j++) { + max_threshold[j] = tItems[i].bench_mark_max[j]; + min_threshold[j] = tItems[i].bench_mark_min[j]; + } + mp_compare_tip_ring_cdc_show_result(i, tItems[i].bench_mark_max, csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, "Max_Bench1", CSV_FILE_SIZE, TIPRING_X); + mp_compare_tip_ring_cdc_show_result(i, tItems[i].bench_mark_min, csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, "Min_Bench1", CSV_FILE_SIZE, TIPRING_X); + mp_compare_tip_ring_cdc_show_result(i, tItems[i].bench_mark_max, csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, "Max_Bench2", CSV_FILE_SIZE, TIPRING_Y); + mp_compare_tip_ring_cdc_show_result(i, tItems[i].bench_mark_min, csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, "Min_Bench2", CSV_FILE_SIZE, TIPRING_Y); + } else if (tItems[i].catalog == TIPRING_P2P) { + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = tItems[i].bch_mrk_max[0][j]; + min_threshold[j] = tItems[i].bch_mrk_min[0][j]; + } + + for (j = 0; j < tItems[i].bch_mrk_frm_num; j++) { + mp_compare_tip_ring_cdc_show_result(i, tItems[i].bch_mrk_max[j], csv, &csv_len, TYPE_BENCHMARK, + max_threshold, min_threshold, get_csv_frame_name("Max_Bench", j + 1), CSV_FILE_SIZE, TIPRING_X); + mp_compare_tip_ring_cdc_show_result(i, tItems[i].bch_mrk_min[j], csv, &csv_len, TYPE_BENCHMARK, + max_threshold, min_threshold, get_csv_frame_name("Min_Bench", j + 1), CSV_FILE_SIZE, TIPRING_X); + } + + for (j = 0; j < tItems[i].bch_mrk_frm_num; j++) { + mp_compare_tip_ring_cdc_show_result(i, tItems[i].bch_mrk_max[j], csv, &csv_len, TYPE_BENCHMARK, + max_threshold, min_threshold, get_csv_frame_name("Max_Bench", tItems[i].bch_mrk_frm_num + j + 1), CSV_FILE_SIZE, TIPRING_Y); + mp_compare_tip_ring_cdc_show_result(i, tItems[i].bch_mrk_min[j], csv, &csv_len, TYPE_BENCHMARK, + max_threshold, min_threshold, get_csv_frame_name("Min_Bench", tItems[i].bch_mrk_frm_num + j + 1), CSV_FILE_SIZE, TIPRING_Y); + } + } else { + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = tItems[i].bench_mark_max[j]; + min_threshold[j] = tItems[i].bench_mark_min[j]; + } + mp_compare_cdc_show_result(i, tItems[i].bench_mark_max, csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, "Max_Bench", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, tItems[i].bench_mark_min, csv, &csv_len, TYPE_BENCHMARK, max_threshold, min_threshold, "Min_Bench", CSV_FILE_SIZE); + } + } else { + if (tItems[i].catalog == TIPRING_RAW || tItems[i].catalog == TIPRING_P2P) { + for (j = 0; j < core_mp.nPxRaw_tipring_len; j++) { + max_threshold[j] = tItems[i].max1; + min_threshold[j] = tItems[i].min1; + } + for (j = core_mp.nPxRaw_tipring_len; j < core_mp.tipring_len; j++) { + max_threshold[j] = tItems[i].max2; + min_threshold[j] = tItems[i].min2; + } + + pr_info("Max1 = %d\n", tItems[i].max1); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Max1 = %d\n", tItems[i].max1); + + pr_info("Max2 = %d\n", tItems[i].max2); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Max2 = %d\n", tItems[i].max2); + + pr_info("Min1 = %d\n", tItems[i].min1); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Min1 = %d\n", tItems[i].min1); + + pr_info("Min2 = %d\n", tItems[i].min2); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Min2 = %d\n", tItems[i].min2); + } else { + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = tItems[i].max; + min_threshold[j] = tItems[i].min; + } + + pr_info("Max = %d\n", tItems[i].max); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Max = %d\n", tItems[i].max); + + pr_info("Min = %d\n", tItems[i].min); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "Min = %d\n", tItems[i].min); + } + } + + if (ipio_strcmp(tItems[i].desp, "open test(integration)_sp") == 0) { + mp_compare_cdc_show_result(i, frame1_cbk700, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "frame1 cbk700", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, frame1_cbk250, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "frame1 cbk250", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, frame1_cbk200, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "frame1 cbk200", CSV_FILE_SIZE); + } + + if (ipio_strcmp(tItems[i].desp, "open test_c") == 0) { + mp_compare_cdc_show_result(i, cap_dac, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "CAP_DAC", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, cap_raw, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "CAP_RAW", CSV_FILE_SIZE); + } + + if (ipio_strcmp(tItems[i].desp, "open test_c (openx enable)") == 0) { + mp_compare_cdc_show_result(i, openx_cap_dac, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "CAP_DAC", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, openx_cap_raw, csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, "CAP_RAW", CSV_FILE_SIZE); + } + + if (tItems[i].catalog == TX_RX_DELTA) { + if (ERR_ALLOC_MEM(core_mp.rx_delta_buf) || ERR_ALLOC_MEM(core_mp.tx_delta_buf)) { + ILI_ERR("This test item (%s) has no data inside its buffer\n", tItems[i].desp); + continue; + } + } else { + if (ERR_ALLOC_MEM(tItems[i].buf) || ERR_ALLOC_MEM(tItems[i].max_buf) || + ERR_ALLOC_MEM(tItems[i].min_buf)) { + ILI_ERR("This test item (%s) has no data inside its buffer\n", tItems[i].desp); + continue; + } + } + + /* Show test result as below */ + if (tItems[i].catalog == KEY_TEST) { + for (x = 0; x < core_mp.key_len; x++) { + DUMP("KEY_%02d ", x); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "KEY_%02d,", x); + } + + DUMP("\n"); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "\n"); + + for (y = 0; y < core_mp.key_len; y++) { + DUMP(" %3d ", tItems[i].buf[y]); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), " %3d, ", tItems[i].buf[y]); + } + + DUMP("\n"); + csv_len += snprintf(csv + csv_len, (CSV_FILE_SIZE - csv_len), "\n"); + } else if (tItems[i].catalog == TX_RX_DELTA) { + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = core_mp.TxDeltaMax; + min_threshold[j] = core_mp.TxDeltaMin; + } + mp_compare_cdc_show_result(i, core_mp.tx_max_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "TX Max Hold", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, core_mp.tx_min_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "TX Min Hold", CSV_FILE_SIZE); + + for (j = 0; j < core_mp.frame_len; j++) { + max_threshold[j] = core_mp.RxDeltaMax; + min_threshold[j] = core_mp.RxDeltaMin; + } + mp_compare_cdc_show_result(i, core_mp.rx_max_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "RX Max Hold", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, core_mp.rx_min_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "RX Min Hold", CSV_FILE_SIZE); + } else if (tItems[i].catalog == TIPRING_RAW || tItems[i].catalog == TIPRING_P2P) { + if (tItems[i].catalog == TIPRING_RAW) + get_frame_cont = tItems[i].frame_count; + + if (tItems[i].catalog == TIPRING_P2P) + get_frame_cont = tItems[i].bch_mrk_frm_num; + + for (j = 0; j < get_frame_cont; j++) { + mp_compare_tip_ring_cdc_show_result(i,&tItems[i].buf[(j * core_mp.tipring_len)], csv, &csv_len, TYPE_NO_JUGE, + max_threshold, min_threshold, get_csv_frame_name("Frame ", j + 1), CSV_FILE_SIZE, TIPRING_X); + } + for (j = 0; j < get_frame_cont; j++) { + mp_compare_tip_ring_cdc_show_result(i,&tItems[i].buf[(j * core_mp.tipring_len)], csv, &csv_len, TYPE_NO_JUGE, + max_threshold, min_threshold, get_csv_frame_name("Frame ", get_frame_cont + j + 1), CSV_FILE_SIZE, TIPRING_Y); + } + } else { + /* general result */ + if (tItems[i].trimmed_mean && tItems[i].catalog != PEAK_TO_PEAK_TEST) { + mp_compare_cdc_show_result(i, tItems[i].result_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "Mean result", CSV_FILE_SIZE); + } else { + if (!tItems[i].bch_mrk_multi) { + mp_compare_cdc_show_result(i, tItems[i].max_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "Max Hold", CSV_FILE_SIZE); + mp_compare_cdc_show_result(i, tItems[i].min_buf, csv, &csv_len, TYPE_JUGE, max_threshold, min_threshold, "Min Hold", CSV_FILE_SIZE); + } + + } + if (tItems[i].catalog != PEAK_TO_PEAK_TEST) + get_frame_cont = tItems[i].frame_count; + + if (tItems[i].bch_mrk_multi) + get_frame_cont = tItems[i].bch_mrk_frm_num; + + /* result of each frame */ + for (j = 0; j < get_frame_cont; j++) { + mp_compare_cdc_show_result(i, &tItems[i].buf[(j*core_mp.frame_len)], csv, &csv_len, TYPE_NO_JUGE, max_threshold, min_threshold, get_csv_frame_name("Frame ", j + 1), CSV_FILE_SIZE); + } + } + } + + memset(csv_name, 0, 128 * sizeof(char)); + + mp_print_csv_tail(csv, &csv_len, CSV_FILE_SIZE); + + for (i = 0; i < MP_TEST_ITEM; i++) { + if (tItems[i].run) { + if (tItems[i].item_result < 0) { + pass_item_count = 0; + break; + } + pass_item_count++; + } + } + + /* define csv file name */ + ret_pass_name = NORMAL_CSV_PASS_NAME; + ret_fail_name = NORMAL_CSV_FAIL_NAME; + + if (pass_item_count == 0) { + core_mp.final_result = MP_DATA_FAIL; + ret = MP_DATA_FAIL; + + if (core_mp.lost_parameter) { + ret_fail_name = NULL; + ret_fail_name = NORMAL_CSV_WARNING_NAME; + ILI_ERR("WARNING! OPEN or SHORT parameter not found in ini file!!\n"); + } + + if (lcm_on) { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/%s_%s.csv", CSV_LCM_ON_PATH, get_date_time_str(), ret_fail_name); +#else + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/lcm_on_%s_%s.csv", CSV_LCM_DEF_PATH, get_date_time_str(), ret_fail_name); +#endif + } else { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/%s_%s.csv", CSV_LCM_OFF_PATH, get_date_time_str(), ret_fail_name); +#else + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/lcm_off_%s_%s.csv", CSV_LCM_DEF_PATH, get_date_time_str(), ret_fail_name); +#endif + } + } else { + core_mp.final_result = MP_DATA_PASS; + ret = MP_DATA_PASS; + + if (core_mp.lost_benchmark) { + ret_pass_name = NULL; + ret_pass_name = NORMAL_CSV_WARNING_NAME; + ILI_ERR("WARNING! Golden and SPEC in ini file aren't matched!!\n"); + } + + if (lcm_on) { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/%s_%s.csv", CSV_LCM_ON_PATH, get_date_time_str(), ret_pass_name); +#else + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/lcm_on_%s_%s.csv", CSV_LCM_DEF_PATH, get_date_time_str(), ret_pass_name); +#endif + } else { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/%s_%s.csv", CSV_LCM_OFF_PATH, get_date_time_str(), ret_pass_name); +#else + snprintf(csv_name, (CSV_FILE_SIZE - csv_len), "%s/lcm_off_%s_%s.csv", CSV_LCM_DEF_PATH, get_date_time_str(), ret_pass_name); +#endif + } + } + +#if GENERIC_KERNEL_IMAGE + ILI_ERR("GKI version not allow drivers to use filp_open\n"); + if (ilits->mp_result == NULL) { + ilits->mp_result = vmalloc(CSV_FILE_SIZE); + if (ERR_ALLOC_MEM(ilits->mp_result)) { + ILI_ERR("Failed to allocate ilits->mp_result mem\n"); + ret = -EMP_NOMEM; + goto fail_open; + } + } + + memset(ilits->mp_result, 0, CSV_FILE_SIZE); + ipio_memcpy(ilits->mp_result, csv, csv_len, strlen(csv)); +#else + ILI_INFO("Open CSV : %s\n", csv_name); + if (f == NULL) + f = filp_open(csv_name, O_WRONLY | O_CREAT | O_TRUNC, 644); + + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open CSV file"); + ret = -EMP_NOMEM; + goto fail_open; + } + + ILI_INFO("Open CSV succeed, its length = %d\n ", csv_len); + + if (csv_len >= CSV_FILE_SIZE) { + ILI_ERR("The length saved to CSV is too long !\n"); + ret = -EMP_INVAL; + goto fail_open; + } + + pos = 0; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + fs = get_fs(); + set_fs(KERNEL_DS); + vfs_write(f, csv, csv_len, &pos); + set_fs(fs); +#else + kernel_write(f, csv, csv_len, &pos); +#endif + filp_close(f, NULL); + + ILI_INFO("Writing Data into CSV succeed\n"); +#endif + +fail_open: + ipio_vfree((void **)&csv); + ipio_kfree((void **)&max_threshold); + ipio_kfree((void **)&min_threshold); + return ret; +} + +static void ilitek_tddi_mp_init_item(void) +{ + int i = 0; + + memset(&core_mp, 0, sizeof(core_mp)); + memset(&open_para, 0, sizeof(open_para)); + memset(&short_para, 0, sizeof(short_para)); + + core_mp.chip_pid = ilits->chip->pid; + core_mp.chip_id = ilits->chip->id; + core_mp.chip_type = ilits->chip->type; + core_mp.chip_ver = ilits->chip->ver; + core_mp.fw_ver = ilits->chip->fw_ver; + core_mp.protocol_ver = ilits->protocol->ver; + core_mp.core_ver = ilits->chip->core_ver; + core_mp.cdc_len = ilits->protocol->cdc_len; + core_mp.no_bk_shift = ilits->chip->no_bk_shift; + core_mp.xch_len = ilits->xch_num; + core_mp.ych_len = ilits->ych_num; + + if (ilits->PenType == POSITION_PEN_TYPE_ON) { + core_mp.nPxRaw = ilits->pen_info_block.nPxRaw; + core_mp.nPyRaw = ilits->pen_info_block.nPyRaw; + core_mp.nPxRaw_tipring_len = 2 * core_mp.nPxRaw * core_mp.xch_len; + core_mp.nPyRaw_tipring_len = 2 * core_mp.nPyRaw * core_mp.ych_len; + core_mp.tipring_len = core_mp.nPxRaw_tipring_len + core_mp.nPyRaw_tipring_len; + } + + core_mp.frame_len = core_mp.xch_len * core_mp.ych_len; + core_mp.stx_len = 0; + core_mp.srx_len = 0; + core_mp.key_len = 0; + core_mp.st_len = 0; + core_mp.tdf = 240; + core_mp.busy_cdc = INT_CHECK; + core_mp.retry = ilits->mp_retry; + core_mp.td_retry = false; + core_mp.final_result = MP_DATA_FAIL; + core_mp.lost_benchmark = false; + core_mp.lost_parameter = false; + core_mp.pre_cmd_sp = false; + + ILI_INFO("============== TP & Panel info ================\n"); + ILI_INFO("Driver version = %s\n", DRIVER_VERSION); + ILI_INFO("TP Module = %s\n", ilits->md_name); + ILI_INFO("CHIP = 0x%x\n", core_mp.chip_pid); + ILI_INFO("Firmware version = %x\n", core_mp.fw_ver); + ILI_INFO("Protocol version = %x\n", core_mp.protocol_ver); + ILI_INFO("Core version = %x\n", core_mp.core_ver); + ILI_INFO("Read CDC Length = %d\n", core_mp.cdc_len); + ILI_INFO("X length = %d, Y length = %d\n", core_mp.xch_len, core_mp.ych_len); + ILI_INFO("Frame length = %d\n", core_mp.frame_len); + + if (ilits->PenType == POSITION_PEN_TYPE_ON) { + ILI_INFO("nPxRaw = %d,nPyRaw = %d\n", core_mp.nPxRaw, core_mp.nPyRaw); + ILI_INFO("Tip Ring length = %d, nPxRaw buffer length = %d, nPyRaw buffer length = %d\n" + , core_mp.tipring_len, core_mp.nPxRaw_tipring_len, core_mp.nPyRaw_tipring_len); + } + + ILI_INFO("Check busy method = %s\n", (core_mp.busy_cdc ? "Polling" : "Interrupt")); + ILI_INFO("===============================================\n"); + + for (i = 0; i < MP_TEST_ITEM; i++) { + tItems[i].spec_option = 0; + tItems[i].type_option = 0; + tItems[i].run = false; + tItems[i].max = 0; + tItems[i].max1 = 0; + tItems[i].max2 = 0; + tItems[i].max_res = MP_DATA_FAIL; + tItems[i].item_result = MP_DATA_PASS; + tItems[i].min = 0; + tItems[i].min1 = 0; + tItems[i].min2 = 0; + tItems[i].min_res = MP_DATA_FAIL; + tItems[i].frame_count = 0; + tItems[i].trimmed_mean = 0; + tItems[i].lowest_percentage = 0; + tItems[i].highest_percentage = 0; + tItems[i].v_tdf_1 = 0; + tItems[i].v_tdf_2 = 0; + tItems[i].h_tdf_1 = 0; + tItems[i].h_tdf_2 = 0; + tItems[i].max_min_mode = 0; + tItems[i].bch_mrk_multi = false; + tItems[i].bch_mrk_frm_num = 1; + tItems[i].goldenmode = 0; + tItems[i].retry_cnt = RETRY_COUNT; + tItems[i].result_buf = NULL; + tItems[i].buf = NULL; + tItems[i].max_buf = NULL; + tItems[i].min_buf = NULL; + tItems[i].bench_mark_max = NULL; + tItems[i].bench_mark_min = NULL; + tItems[i].bch_mrk_max = NULL; + tItems[i].bch_mrk_min = NULL; + tItems[i].node_type = NULL; + tItems[i].delay_time = 0; + tItems[i].test_int_pin = 0; + tItems[i].int_pulse_test = 0; + tItems[i].pre_cmd_en = DISABLE; + + if (tItems[i].catalog == MUTUAL_TEST) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == TX_RX_DELTA) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == UNTOUCH_P2P) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == PIXEL) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == OPEN_TEST) { + if (ipio_strcmp(tItems[i].desp, "open test(integration)_sp") == 0) + tItems[i].do_test = open_test_sp; + else if (ipio_strcmp(tItems[i].desp, "open test_c") == 0) + tItems[i].do_test = open_test_cap; + else if (ipio_strcmp(tItems[i].desp, "open test_c (openx enable)") == 0) + tItems[i].do_test = open_test_cap; + else + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == KEY_TEST) { + tItems[i].do_test = key_test; + } else if (tItems[i].catalog == SELF_TEST) { + tItems[i].do_test = self_test; + } else if (tItems[i].catalog == ST_TEST) { + tItems[i].do_test = st_test; + } else if (tItems[i].catalog == PEAK_TO_PEAK_TEST) { + tItems[i].do_test = peak_to_peak_test; + } else if (tItems[i].catalog == SHORT_TEST) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == PIN_TEST) { + tItems[i].do_test = pin_test; + } else if (tItems[i].catalog == TIPRING_RAW) { + tItems[i].do_test = mutual_test; + } else if (tItems[i].catalog == TIPRING_P2P) { + tItems[i].do_test = peak_to_peak_test; + } + + tItems[i].result = kmalloc(16, GFP_KERNEL); + snprintf(tItems[i].result, 16, "%s", "FAIL"); + } +} + +static void ilitek_tddi_fw_mp_precmd_support(void) +{ + int ret = 0; + u8 cmd[2] = {0}; + u8 buftmp[3] = {0}; + + core_mp.pre_cmd_sp = false; + + if (core_mp.core_ver < CORE_VER_1600) { + ILI_INFO("FW no support MP pre command\n"); + core_mp.pre_cmd_sp = false; + return; + } + + cmd[0] = CMD_FW_MP_PRE_CMD; + cmd[1] = 0x02; + + ret = ilits->wrapper(cmd, 2, buftmp, 3, ON, OFF); + if (ret < 0) + ILI_ERR("check FW pre command failed\n"); + + if (buftmp[2] == 0xAA) + core_mp.pre_cmd_sp = true; + + ILI_INFO("FW support MP Pre-Command : %s\n", core_mp.pre_cmd_sp ? "ENABLE" : "DISABLE"); + +} + +static void mp_p2p_td_retry_after_ra_fail(int p2p_td) +{ + int i; + + for (i = 0; i < MP_TEST_ITEM; i++) { + if (ipio_strcmp(tItems[i].desp, "noise peak to peak(with panel) (lcm off)") == 0) + break; + } + + if (i >= MP_TEST_ITEM) + return; + + ILI_DBG("i = %d, p2p_noise_ret = %d, p2p_noise_run = %d\n", + i, tItems[i].item_result, tItems[i].run); + + if (tItems[i].item_result == MP_DATA_PASS && tItems[i].run == 1) + tItems[p2p_td].do_test(p2p_td); +} + +static void mp_test_run(int index) +{ + int i = index, j; + char str[512] = {0}; + + + /* Get parameters from ini */ + parser_get_int_data(tItems[i].desp, "spec option", str, sizeof(str)); + tItems[i].spec_option = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "type option", str, sizeof(str)); + tItems[i].type_option = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "frame count", str, sizeof(str)); + tItems[i].frame_count = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "trimmed mean", str, sizeof(str)); + tItems[i].trimmed_mean = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "lowest percentage", str, sizeof(str)); + tItems[i].lowest_percentage = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "highest percentage", str, sizeof(str)); + tItems[i].highest_percentage = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "goldenmode", str, sizeof(str)); + tItems[i].goldenmode = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "max min mode", str, sizeof(str)); + tItems[i].max_min_mode = ili_katoi(str); + + parser_get_int_data(tItems[i].desp, "retry count", str, sizeof(str)); + tItems[i].retry_cnt = ili_katoi(str); + + if (tItems[i].goldenmode && (tItems[i].spec_option != tItems[i].goldenmode)) + core_mp.lost_benchmark = true; + + if (core_mp.pre_cmd_sp) { + parser_get_int_data(tItems[i].desp, "precommand", str, sizeof(str)); + tItems[i].pre_cmd_en = ili_katoi(str); + } + + /* Get pin test delay time */ + if (tItems[i].catalog == PIN_TEST) { + parser_get_int_data(tItems[i].desp, "test int pin", str, sizeof(str)); + tItems[i].test_int_pin = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "int pulse test", str, sizeof(str)); + tItems[i].int_pulse_test = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "delay time", str, sizeof(str)); + tItems[i].delay_time = ili_katoi(str); + } + + /* Get TDF value from ini */ + if (tItems[i].catalog == SHORT_TEST) { + parser_get_int_data(tItems[i].desp, "v_tdf_1", str, sizeof(str)); + tItems[i].v_tdf_1 = parser_get_tdf_value(str, tItems[i].catalog); + parser_get_int_data(tItems[i].desp, "v_tdf_2", str, sizeof(str)); + tItems[i].v_tdf_2 = parser_get_tdf_value(str, tItems[i].catalog); + parser_get_int_data(tItems[i].desp, "h_tdf_1", str, sizeof(str)); + tItems[i].h_tdf_1 = parser_get_tdf_value(str, tItems[i].catalog); + parser_get_int_data(tItems[i].desp, "h_tdf_2", str, sizeof(str)); + tItems[i].h_tdf_2 = parser_get_tdf_value(str, tItems[i].catalog); + } else { + parser_get_int_data(tItems[i].desp, "v_tdf", str, sizeof(str)); + tItems[i].v_tdf_1 = parser_get_tdf_value(str, tItems[i].catalog); + parser_get_int_data(tItems[i].desp, "h_tdf", str, sizeof(str)); + tItems[i].h_tdf_1 = parser_get_tdf_value(str, tItems[i].catalog); + } + + /* Get threshold from ini structure in parser */ + if (ipio_strcmp(tItems[i].desp, "tx/rx delta") == 0) { + parser_get_int_data(tItems[i].desp, "tx max", str, sizeof(str)); + core_mp.TxDeltaMax = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "tx min", str, sizeof(str)); + core_mp.TxDeltaMin = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "rx max", str, sizeof(str)); + core_mp.RxDeltaMax = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "rx min", str, sizeof(str)); + core_mp.RxDeltaMin = ili_katoi(str); + ILI_DBG("%s: Tx Max = %d, Tx Min = %d, Rx Max = %d, Rx Min = %d\n", + tItems[i].desp, core_mp.TxDeltaMax, core_mp.TxDeltaMin, + core_mp.RxDeltaMax, core_mp.RxDeltaMin); + } else { + parser_get_int_data(tItems[i].desp, "max", str, sizeof(str)); + tItems[i].max = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "min", str, sizeof(str)); + tItems[i].min = ili_katoi(str); + } + + if (tItems[i].catalog == TIPRING_RAW || tItems[i].catalog == TIPRING_P2P) { + parser_get_int_data(tItems[i].desp, "max1", str, sizeof(str)); + tItems[i].max1 = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "max2", str, sizeof(str)); + tItems[i].max2 = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "min1", str, sizeof(str)); + tItems[i].min1 = ili_katoi(str); + parser_get_int_data(tItems[i].desp, "min2", str, sizeof(str)); + tItems[i].min2 = ili_katoi(str); + } + + /* Get pin test delay time */ + if (tItems[i].catalog == PEAK_TO_PEAK_TEST) { + if (tItems[i].max_min_mode == 1 && tItems[i].spec_option == 1) { + tItems[i].bch_mrk_multi = true; + for (j = 1; j < 11; j++) { + char tmp[8] = {0}; + /* format complete string from the name of section "_Benchmark_Data". */ + snprintf(str, sizeof(str), "%s%s%s%d", tItems[index].desp, "_", BENCHMARK_KEY_NAME, j); + if (parser_get_int_data(str, str, tmp, sizeof(tmp)) <= 0) + break; + + tItems[i].bch_mrk_frm_num = j; + } + ILI_DBG("set bch_mrk_frm_num = %d\n", tItems[i].bch_mrk_frm_num); + } + } + + if (tItems[i].catalog == TIPRING_P2P && tItems[i].max_min_mode == 1) { + tItems[i].bch_mrk_frm_num = 3; + tItems[i].bch_mrk_multi = true; + ILI_DBG("set bch_mrk_frm_num = %d\n", tItems[i].bch_mrk_frm_num); + } + + ILI_DBG("%s: run = %d, max = %d, min = %d, frame_count = %d\n", tItems[i].desp, + tItems[i].run, tItems[i].max, tItems[i].min, tItems[i].frame_count); + + ILI_DBG("v_tdf_1 = %d, v_tdf_2 = %d, h_tdf_1 = %d, h_tdf_2 = %d", tItems[i].v_tdf_1, + tItems[i].v_tdf_2, tItems[i].h_tdf_1, tItems[i].h_tdf_2); + + if (tItems[i].catalog == TIPRING_RAW || tItems[i].catalog == TIPRING_P2P) + ILI_DBG("max1 = %d, max2 = %d, min1 = %d, min2 = %d\n", tItems[i].max1, tItems[i].max2, tItems[i].min1, tItems[i].min2); + + ILI_INFO("Run MP Test Item : %s\n", tItems[i].desp); + tItems[i].do_test(i); + + mp_compare_test_result(i); + + /* P2P TD retry after RA sample failed. */ + if (ipio_strcmp(tItems[i].desp, "peak to peak_td (lcm off)") == 0 && + tItems[i].item_result == MP_DATA_FAIL) { + parser_get_int_data(tItems[i].desp, "recheck ptop lcm off", str, sizeof(str)); + ILI_INFO("Peak to Peak TD retry = %d\n", ili_katoi(str)); + core_mp.td_retry = ili_katoi(str); + if (core_mp.td_retry) + mp_p2p_td_retry_after_ra_fail(i); + } + + if (core_mp.retry && tItems[i].item_result == MP_DATA_FAIL) { + ILI_INFO("MP failed, doing retry %d times\n", tItems[i].retry_cnt); + mp_do_retry(i, tItems[i].retry_cnt); + } + +} + +static void mp_test_free(void) +{ + int i, j; + + ILI_INFO("Free all allocated mem for MP\n"); + + core_mp.final_result = MP_DATA_FAIL; + core_mp.td_retry = false; + + for (i = 0; i < MP_TEST_ITEM; i++) { + tItems[i].run = false; + tItems[i].max_res = MP_DATA_FAIL; + tItems[i].min_res = MP_DATA_FAIL; + tItems[i].item_result = MP_DATA_PASS; + + if (tItems[i].catalog == TX_RX_DELTA) { + ipio_kfree((void **)&core_mp.rx_delta_buf); + ipio_kfree((void **)&core_mp.tx_delta_buf); + ipio_kfree((void **)&core_mp.tx_max_buf); + ipio_kfree((void **)&core_mp.tx_min_buf); + ipio_kfree((void **)&core_mp.rx_max_buf); + ipio_kfree((void **)&core_mp.rx_min_buf); + } else { + if (tItems[i].spec_option == BENCHMARK) { + ipio_kfree((void **)&tItems[i].bench_mark_max); + ipio_kfree((void **)&tItems[i].bench_mark_min); + if (tItems[i].bch_mrk_max != NULL) { + for (j = 0; j < tItems[i].bch_mrk_frm_num; j++) + if (tItems[i].bch_mrk_max[j] != NULL) { + kfree(tItems[i].bch_mrk_max[j]); + tItems[i].bch_mrk_max[j] = NULL; + } + kfree(tItems[i].bch_mrk_max); + tItems[i].bch_mrk_max = NULL; + } + if (tItems[i].bch_mrk_min != NULL) { + for (j = 0; j < tItems[i].bch_mrk_frm_num; j++) + if (tItems[i].bch_mrk_min[j] != NULL) { + kfree(tItems[i].bch_mrk_min[j]); + tItems[i].bch_mrk_min[j] = NULL; + } + kfree(tItems[i].bch_mrk_min); + tItems[i].bch_mrk_min = NULL; + } + } + ipio_kfree((void **)&tItems[i].node_type); + ipio_kfree((void **)&tItems[i].result); + ipio_kfree((void **)&tItems[i].result_buf); + ipio_kfree((void **)&tItems[i].max_buf); + ipio_kfree((void **)&tItems[i].min_buf); + ipio_vfree((void **)&tItems[i].buf); + } + } + + ipio_kfree((void **)&frame1_cbk700); + ipio_kfree((void **)&frame1_cbk250); + ipio_kfree((void **)&frame1_cbk200); + ipio_kfree((void **)&frame_buf); + ipio_kfree((void **)&key_buf); + ipio_kfree((void **)&cap_dac); + ipio_kfree((void **)&cap_raw); + ipio_kfree((void **)&openx_cap_dac); + ipio_kfree((void **)&openx_cap_raw); + if (frm_buf != NULL) { + for (j = 0; j < 3; j++) + if (frm_buf[j] != NULL) { + kfree(frm_buf[j]); + frm_buf[j] = NULL; + } + kfree(frm_buf); + frm_buf = NULL; + } +} + +/* The method to copy results to user depends on what APK needs */ +static void mp_copy_ret_to_apk(char *buf) +{ + int i, seq, len = 2; + + if (!buf) { + ILI_ERR("apk buffer is null\n"); + return; + } +#if !GENERIC_KERNEL_IMAGE + len += snprintf(buf + len, PAGE_SIZE - len, "CSV path: %s\n\n", csv_name); +#endif + for (seq = 0; seq < ri.count; seq++) { + i = ri.index[seq]; + if (tItems[i].item_result == MP_DATA_FAIL) { + ILI_ERR("[%s] = FAIL\n", tItems[i].desp); + len += snprintf(buf + len, PAGE_SIZE - len, "[%s] = FAIL\n", tItems[i].desp); + } else { + ILI_INFO("[%s] = PASS\n", tItems[i].desp); + len += snprintf(buf + len, PAGE_SIZE - len, "[%s] = PASS\n", tItems[i].desp); + } + } + ilits->mp_ret_len = len; +} + +static int mp_sort_item(bool lcm_on) +{ + int i, j; + char str[128] = {0}; + + ri.count = 0; + memset(ri.index, 0x0, sizeof(ri.index)); + + for (i = 0; i < MAX_SECTION_NUM; i++) { + for (j = 0; j < MP_TEST_ITEM; j++) { + if (ipio_strcmp(seq_item[i], tItems[j].desp) != 0) + continue; + + parser_get_int_data(tItems[j].desp, "enable", str, sizeof(str)); + tItems[j].run = ili_katoi(str); + if (tItems[j].run != 1 || tItems[j].lcm != lcm_on) + continue; + + if (ri.count > MP_TEST_ITEM) { + ILI_ERR("Test item(%d) is invaild, abort\n", ri.count); + return -EINVAL; + } + + ri.index[ri.count] = j; + ri.count++; + } + } + return 0; +} + +int ili_mp_test_main(char *apk, bool lcm_on) +{ + int i, ret = 0; + char str[8] = {0}, ver[8] = {0}; + + if (ilits->xch_num <= 0 || ilits->ych_num <= 0) { + ILI_ERR("Invalid frame length (%d, %d)\n", ilits->xch_num, ilits->ych_num); + ret = -EMP_INVAL; + goto out; + } + + if (ini_info == NULL) { + ini_info = (struct ini_file_data *)vmalloc(sizeof(struct ini_file_data) * PARSER_MAX_KEY_NUM); + if (ERR_ALLOC_MEM(ini_info)) { + ILI_ERR("Failed to malloc ini_info\n"); + ret = -EMP_NOMEM; + goto out; + } + } + + ilitek_tddi_mp_init_item(); + + ilitek_tddi_fw_mp_precmd_support(); + + ret = ilitek_tddi_mp_ini_parser(ilits->md_ini_path); + if (ret < 0) { + ILI_ERR("Failed to parsing INI file\n"); + ret = -EMP_INI; + goto out; + } + + /* Compare both protocol version of ini and firmware */ + parser_get_ini_key_value("pv5_4 command", "protocol", str); + snprintf(ver, sizeof(ver), "0x%s", str); + ILI_INFO("str = 0x%s, ver = %s\n", str, ver); + if ((ili_str2hex(ver)) != (core_mp.protocol_ver >> 8)) { + ILI_ERR("ERROR! MP Protocol version is invaild, 0x%x\n", ili_str2hex(ver)); + ret = -EMP_PROTOCOL; + goto out; + } + + /* Read timing info from ini file */ + if (mp_get_timing_info() < 0) { + ILI_ERR("Failed to get timing info from ini\n"); + ret = -EMP_TIMING_INFO; + goto out; + } + +#if MP_INT_LEVEL + if (ili_ic_int_trigger_ctrl(true) < 0) { + ILI_ERR("Failed to set INT as Level trigger"); + ret = -EMP_CMD; + goto out; + } +#endif + + /* Sort test item by ini file */ + if (mp_sort_item(lcm_on) < 0) { + ILI_ERR("Failed to sort test item\n"); + ret = -EMP_INI; + goto out; + } + + for (i = 0; i < ri.count; i++) + mp_test_run(ri.index[i]); + + ILI_INFO("Test item end\n"); + + ret = mp_show_result(lcm_on); + + mp_copy_ret_to_apk(apk); + +out: + mp_test_free(); + +#if MP_INT_LEVEL + if (ili_ic_int_trigger_ctrl(false) < 0) { + ILI_ERR("Failed to set INT back to pluse trigger"); + ret = -EMP_CMD; + } +#endif + + ipio_vfree((void **)&ini_info); + return ret; +}; diff --git a/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_mtk.c b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_mtk.c new file mode 100644 index 000000000000..cd7e83b91985 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_mtk.c @@ -0,0 +1,518 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek_v3.h" +#include "tpd.h" + +#define DTS_INT_GPIO "touch,irq-gpio" +#define DTS_RESET_GPIO "touch,reset-gpio" +#define DTS_OF_NAME "mediatek,cap_touch" +#define MTK_RST_GPIO GTP_RST_PORT +#define MTK_INT_GPIO GTP_INT_PORT + +extern struct tpd_device *tpd; + +void ili_tp_reset(void) +{ + ILI_INFO("edge delay = %d\n", ilits->rst_edge_delay); + + /* Need accurate power sequence, do not change it to msleep */ + tpd_gpio_output(ilits->tp_rst, 1); + mdelay(1); + tpd_gpio_output(ilits->tp_rst, 0); + mdelay(5); + tpd_gpio_output(ilits->tp_rst, 1); + mdelay(ilits->rst_edge_delay); +} + +void ili_input_register(void) +{ + int i; + + ilits->input = tpd->dev; + + if (tpd_dts_data.use_tpd_button) { + for (i = 0; i < tpd_dts_data.tpd_key_num; i++) + input_set_capability(ilits->input, EV_KEY, tpd_dts_data.tpd_key_local[i]); + } + + /* set the supported event type for input device */ + set_bit(EV_ABS, ilits->input->evbit); + set_bit(EV_SYN, ilits->input->evbit); + set_bit(EV_KEY, ilits->input->evbit); + set_bit(BTN_TOUCH, ilits->input->keybit); + set_bit(BTN_TOOL_FINGER, ilits->input->keybit); + set_bit(INPUT_PROP_DIRECT, ilits->input->propbit); + + if (MT_PRESSURE) + input_set_abs_params(ilits->input, ABS_MT_PRESSURE, 0, MAX_PRESSURE, 0, 0); + + if (MT_B_TYPE) { +#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE + input_mt_init_slots(ilits->input, MAX_TOUCH_NUM, INPUT_MT_DIRECT); +#else + input_mt_init_slots(ilits->input, MAX_TOUCH_NUM); +#endif /* LINUX_VERSION_CODE */ + } else { + input_set_abs_params(ilits->input, ABS_MT_TRACKING_ID, 0, MAX_TOUCH_NUM, 0, 0); + } + + input_set_abs_params(ilits->input, ABS_MT_POSITION_X, TOUCH_SCREEN_X_MIN, ilits->panel_wid - 1, 0, 0); + input_set_abs_params(ilits->input, ABS_MT_POSITION_Y, TOUCH_SCREEN_Y_MIN, ilits->panel_hei - 1, 0, 0); + + /* Gesture keys register */ + input_set_capability(ilits->input, EV_KEY, KEY_POWER); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_UP); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_DOWN); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_LEFT); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_RIGHT); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_O); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_E); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_M); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_W); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_S); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_V); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_Z); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_C); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_F); + + __set_bit(KEY_GESTURE_POWER, ilits->input->keybit); + __set_bit(KEY_GESTURE_UP, ilits->input->keybit); + __set_bit(KEY_GESTURE_DOWN, ilits->input->keybit); + __set_bit(KEY_GESTURE_LEFT, ilits->input->keybit); + __set_bit(KEY_GESTURE_RIGHT, ilits->input->keybit); + __set_bit(KEY_GESTURE_O, ilits->input->keybit); + __set_bit(KEY_GESTURE_E, ilits->input->keybit); + __set_bit(KEY_GESTURE_M, ilits->input->keybit); + __set_bit(KEY_GESTURE_W, ilits->input->keybit); + __set_bit(KEY_GESTURE_S, ilits->input->keybit); + __set_bit(KEY_GESTURE_V, ilits->input->keybit); + __set_bit(KEY_GESTURE_Z, ilits->input->keybit); + __set_bit(KEY_GESTURE_C, ilits->input->keybit); + __set_bit(KEY_GESTURE_F, ilits->input->keybit); +} + +void ili_input_pen_register(void) +{ + ilits->input_pen = input_allocate_device(); + if (ERR_ALLOC_MEM(ilits->input_pen)) { + ILI_ERR("Failed to allocate touch input device\n"); + input_free_device(ilits->input_pen); + return; + } + + ilits->input_pen->name = PEN_INPUT_DEVICE; + ilits->input_pen->phys = ilits->phys; + ilits->input_pen->dev.parent = ilits->dev; + ilits->input_pen->id.bustype = ilits->hwif->bus_type; + + /* set the supported event type for input device */ + set_bit(EV_ABS, ilits->input_pen->evbit); + set_bit(EV_SYN, ilits->input_pen->evbit); + set_bit(EV_KEY, ilits->input_pen->evbit); + set_bit(BTN_TOUCH, ilits->input_pen->keybit); + set_bit(BTN_TOOL_PEN, ilits->input_pen->keybit); + set_bit(BTN_STYLUS, ilits->input_pen->keybit); + set_bit(BTN_STYLUS2, ilits->input_pen->keybit); + set_bit(INPUT_PROP_DIRECT, ilits->input_pen->propbit); + + input_set_abs_params(ilits->input_pen, ABS_X, 0, ilits->panel_wid, 0, 0); + input_set_abs_params(ilits->input_pen, ABS_Y, 0, ilits->panel_hei, 0, 0); + input_set_abs_params(ilits->input_pen, ABS_PRESSURE, 0, MAX_PRESSURE, 0, 0); + input_set_abs_params(ilits->input_pen, ABS_TILT_X, -60, 60, 0, 0); + input_set_abs_params(ilits->input_pen, ABS_TILT_Y, -60, 60, 0, 0); + input_set_abs_params(ilits->input_pen, ABS_DISTANCE, 0, 1, 0, 0); + + /* register the input device to input sub-system */ + if (input_register_device(ilits->input_pen) < 0) { + ILI_ERR("Failed to register touch input device\n"); + input_unregister_device(ilits->input_pen); + input_free_device(ilits->input_pen); + } + ILI_INFO("Input Pen device for Single Touch\n"); +} + +#if REGULATOR_POWER +void ili_plat_regulator_power_on(bool status) +{ + ILI_INFO("%s\n", status ? "POWER ON" : "POWER OFF"); + + if (status) { + if (ilits->vdd) { + if (regulator_enable(ilits->vdd) < 0) + ILI_ERR("regulator_enable VDD fail\n"); + } + if (ilits->vcc) { + if (regulator_enable(ilits->vcc) < 0) + ILI_ERR("regulator_enable VCC fail\n"); + } + } else { + if (ilits->vdd) { + if (regulator_disable(ilits->vdd) < 0) + ILI_ERR("regulator_enable VDD fail\n"); + } + if (ilits->vcc) { + if (regulator_disable(ilits->vcc) < 0) + ILI_ERR("regulator_enable VCC fail\n"); + } + } + atomic_set(&ilits->ice_stat, DISABLE); + mdelay(5); +} + +static void ilitek_plat_regulator_power_init(void) +{ + const char *vdd_name = "vdd"; + const char *vcc_name = "vcc"; + + ilits->vdd = regulator_get(tpd->tpd_dev, vdd_name); + if (ERR_ALLOC_MEM(ilits->vdd)) { + ILI_ERR("regulator_get VDD fail\n"); + ilits->vdd = NULL; + } + + tpd->reg = ilits->vdd; + + if (regulator_set_voltage(ilits->vdd, VDD_VOLTAGE, VDD_VOLTAGE) < 0) + ILI_ERR("Failed to set VDD %d\n", VDD_VOLTAGE); + + ilits->vcc = regulator_get(ilits->dev, vcc_name); + if (ERR_ALLOC_MEM(ilits->vcc)) { + ILI_ERR("regulator_get VCC fail.\n"); + ilits->vcc = NULL; + } + if (regulator_set_voltage(ilits->vcc, VCC_VOLTAGE, VCC_VOLTAGE) < 0) + ILI_ERR("Failed to set VCC %d\n", VCC_VOLTAGE); + + ili_plat_regulator_power_on(true); +} +#endif + +static int ilitek_plat_gpio_register(void) +{ + int ret = 0; + + ilits->tp_int = MTK_INT_GPIO; + ilits->tp_rst = MTK_RST_GPIO; + + ILI_INFO("TP INT: %d\n", ilits->tp_int); + ILI_INFO("TP RESET: %d\n", ilits->tp_rst); + + if (!gpio_is_valid(ilits->tp_int)) { + ILI_ERR("Invalid INT gpio: %d\n", ilits->tp_int); + return -EBADR; + } + + if (!gpio_is_valid(ilits->tp_rst)) { + ILI_ERR("Invalid RESET gpio: %d\n", ilits->tp_rst); + return -EBADR; + } + + gpio_direction_input(ilits->tp_int); + return ret; +} + +void ili_irq_disable(void) +{ + unsigned long flag; + + spin_lock_irqsave(&ilits->irq_spin, flag); + + if (atomic_read(&ilits->irq_stat) == DISABLE) + goto out; + + if (!ilits->irq_num) { + ILI_ERR("gpio_to_irq (%d) is incorrect\n", ilits->irq_num); + goto out; + } + + disable_irq_nosync(ilits->irq_num); + atomic_set(&ilits->irq_stat, DISABLE); + ILI_DBG("Disable irq success\n"); + +out: + spin_unlock_irqrestore(&ilits->irq_spin, flag); +} + +void ili_irq_enable(void) +{ + unsigned long flag; + + spin_lock_irqsave(&ilits->irq_spin, flag); + + if (atomic_read(&ilits->irq_stat) == ENABLE) + goto out; + + if (!ilits->irq_num) { + ILI_ERR("gpio_to_irq (%d) is incorrect\n", ilits->irq_num); + goto out; + } + + enable_irq(ilits->irq_num); + atomic_set(&ilits->irq_stat, ENABLE); + ILI_DBG("Enable irq success\n"); + +out: + spin_unlock_irqrestore(&ilits->irq_spin, flag); +} + +static irqreturn_t ilitek_plat_isr_top_half(int irq, void *dev_id) +{ + if (irq != ilits->irq_num) { + ILI_ERR("Incorrect irq number (%d)\n", irq); + return IRQ_NONE; + } + + if (atomic_read(&ilits->cmd_int_check) == ENABLE) { + atomic_set(&ilits->cmd_int_check, DISABLE); + ILI_DBG("CMD INT detected, ignore\n"); + wake_up(&(ilits->inq)); + return IRQ_HANDLED; + } + + if (ilits->prox_near) { + ILI_INFO("Proximity event, ignore interrupt!\n"); + return IRQ_HANDLED; + } + + ILI_DBG("report: %d, rst: %d, fw: %d, switch: %d, mp: %d, sleep: %d, esd: %d, igr:%d\n", + ilits->report, + atomic_read(&ilits->tp_reset), + atomic_read(&ilits->fw_stat), + atomic_read(&ilits->tp_sw_mode), + atomic_read(&ilits->mp_stat), + atomic_read(&ilits->tp_sleep), + atomic_read(&ilits->esd_stat), + atomic_read(&ilits->ignore_report)); + + if (!ilits->report || atomic_read(&ilits->tp_reset) || atomic_read(&ilits->ignore_report) || + atomic_read(&ilits->fw_stat) || atomic_read(&ilits->tp_sw_mode) || + atomic_read(&ilits->mp_stat) || atomic_read(&ilits->tp_sleep) || + atomic_read(&ilits->esd_stat)) { + ILI_DBG("ignore interrupt !\n"); + return IRQ_HANDLED; + } + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t ilitek_plat_isr_bottom_half(int irq, void *dev_id) +{ + if (mutex_is_locked(&ilits->touch_mutex)) { + ILI_DBG("touch is locked, ignore\n"); + return IRQ_HANDLED; + } + mutex_lock(&ilits->touch_mutex); + ili_report_handler(); + mutex_unlock(&ilits->touch_mutex); + return IRQ_HANDLED; +} + +void ili_irq_unregister(void) +{ + devm_free_irq(ilits->dev, ilits->irq_num, NULL); +} + +int ili_irq_register(int type) +{ + int ret = 0; + static bool get_irq_pin; + struct device_node *node; + + atomic_set(&ilits->irq_stat, DISABLE); + + if (get_irq_pin == false) { + node = of_find_matching_node(NULL, touch_of_match); + if (node) + ilits->irq_num = irq_of_parse_and_map(node, 0); + + ILI_INFO("ilits->irq_num = %d\n", ilits->irq_num); + get_irq_pin = true; + } + + ret = devm_request_threaded_irq(ilits->dev, ilits->irq_num, + ilitek_plat_isr_top_half, + ilitek_plat_isr_bottom_half, + type | IRQF_ONESHOT, "ilitek", NULL); + + if (type == IRQF_TRIGGER_FALLING) + ILI_INFO("IRQ TYPE = IRQF_TRIGGER_FALLING\n"); + if (type == IRQF_TRIGGER_RISING) + ILI_INFO("IRQ TYPE = IRQF_TRIGGER_RISING\n"); + + if (ret != 0) + ILI_ERR("Failed to register irq handler, irq = %d, ret = %d\n", ilits->irq_num, ret); + + atomic_set(&ilits->irq_stat, ENABLE); + + return ret; +} + +static void tpd_resume(struct device *h) +{ + if (ili_sleep_handler(TP_RESUME) < 0) + ILI_ERR("TP resume failed\n"); +} + +static void tpd_suspend(struct device *h) +{ + if (ili_sleep_handler(TP_DEEP_SLEEP) < 0) + ILI_ERR("TP suspend failed\n"); +} + +static int ilitek_tp_pm_suspend(struct device *dev) +{ + ILI_INFO("CALL BACK TP PM SUSPEND"); + ilits->pm_suspend = true; + reinit_completion(&ilits->pm_completion); + return 0; +} + +static int ilitek_tp_pm_resume(struct device *dev) +{ + ILI_INFO("CALL BACK TP PM RESUME"); + ilits->pm_suspend = false; + complete(&ilits->pm_completion); + return 0; +} + +static int ilitek_plat_probe(void) +{ + ILI_INFO("platform probe\n"); + +#if REGULATOR_POWER + ilitek_plat_regulator_power_init(); +#endif + + if (ilitek_plat_gpio_register() < 0) + ILI_ERR("Register gpio failed\n"); + + ili_irq_register(ilits->irq_tirgger_type); + + if (ili_tddi_init() < 0) { + ILI_ERR("ILITEK Driver probe failed\n"); + ili_irq_unregister(); + ili_dev_remove(DISABLE); + return -ENODEV; + } + tpd_load_status = 1; + ilits->pm_suspend = false; + init_completion(&ilits->pm_completion); + +#if CHARGER_NOTIFIER_CALLBACK +#if KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE + /* add_for_charger_start */ + ilitek_plat_charger_init(); + /* add_for_charger_end */ +#endif +#endif + ILI_INFO("ILITEK Driver loaded successfully!#"); + return 0; +} + +static int ilitek_plat_remove(void) +{ + ILI_INFO("remove plat dev\n"); + ili_dev_remove(ENABLE); + return 0; +} + +static const struct dev_pm_ops tp_pm_ops = { + .suspend = ilitek_tp_pm_suspend, + .resume = ilitek_tp_pm_resume, +}; + +static const struct of_device_id tp_match_table[] = { + {.compatible = DTS_OF_NAME}, + {}, +}; + +#ifdef ROI +struct ts_device_ops ilitek_ops = { + .chip_roi_rawdata = ili_knuckle_roi_rawdata, + .chip_roi_switch = ili_knuckle_roi_switch, +}; +#endif + +static struct ilitek_hwif_info hwif = { + .bus_type = TDDI_INTERFACE, + .plat_type = TP_PLAT_MTK, + .owner = THIS_MODULE, + .name = TDDI_DEV_ID, + .of_match_table = of_match_ptr(tp_match_table), + .plat_probe = ilitek_plat_probe, + .plat_remove = ilitek_plat_remove, + .pm = &tp_pm_ops, +}; + +static int tpd_local_init(void) +{ + ILI_INFO("TPD init device driver\n"); + + if (ili_dev_init(&hwif) < 0) { + ILI_ERR("Failed to register i2c/spi bus driver\n"); + return -ENODEV; + } + if (tpd_load_status == 0) { + ILI_ERR("Add error touch panel driver\n"); + return -1; + } + if (tpd_dts_data.use_tpd_button) { + tpd_button_setting(tpd_dts_data.tpd_key_num, tpd_dts_data.tpd_key_local, + tpd_dts_data.tpd_key_dim_local); + } + tpd_type_cap = 1; + return 0; +} + +static struct tpd_driver_t tpd_device_driver = { + .tpd_device_name = TDDI_DEV_ID, + .tpd_local_init = tpd_local_init, + .suspend = tpd_suspend, + .resume = tpd_resume, +}; + +static int __init ilitek_plat_dev_init(void) +{ + int ret = 0; + + ILI_INFO("ILITEK TP driver init for MTK\n"); + tpd_get_dts_info(); + ret = tpd_driver_add(&tpd_device_driver); + if (ret < 0) { + ILI_ERR("ILITEK add TP driver failed\n"); + tpd_driver_remove(&tpd_device_driver); + return -ENODEV; + } + return 0; +} + +static void __exit ilitek_plat_dev_exit(void) +{ + ILI_INFO("ilitek driver has been removed\n"); + tpd_driver_remove(&tpd_device_driver); +} + +module_init(ilitek_plat_dev_init); +module_exit(ilitek_plat_dev_exit); +MODULE_AUTHOR("ILITEK"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_node.c b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_node.c new file mode 100644 index 000000000000..ffb027b8d3b8 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_node.c @@ -0,0 +1,4275 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek_v3.h" + +#if ENABLE_PEN_MODE +#define USER_STR_BUFF (PAGE_SIZE * 2) +#define IOCTL_I2C_BUFF (PAGE_SIZE * 2) +#else +#define USER_STR_BUFF PAGE_SIZE +#define IOCTL_I2C_BUFF PAGE_SIZE +#endif +#define ILITEK_IOCTL_MAGIC 100 +#define ILITEK_IOCTL_MAXNR 38 + +#define ILITEK_IOCTL_I2C_WRITE_DATA _IOWR(ILITEK_IOCTL_MAGIC, 0, u8*) +#define ILITEK_IOCTL_I2C_SET_WRITE_LENGTH _IOWR(ILITEK_IOCTL_MAGIC, 1, int) +#define ILITEK_IOCTL_I2C_READ_DATA _IOWR(ILITEK_IOCTL_MAGIC, 2, u8*) +#define ILITEK_IOCTL_I2C_SET_READ_LENGTH _IOWR(ILITEK_IOCTL_MAGIC, 3, int) + +#define ILITEK_IOCTL_TP_HW_RESET _IOWR(ILITEK_IOCTL_MAGIC, 4, int) +#define ILITEK_IOCTL_TP_POWER_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 5, int) +#define ILITEK_IOCTL_TP_REPORT_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 6, int) +#define ILITEK_IOCTL_TP_IRQ_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 7, int) + +#define ILITEK_IOCTL_TP_DEBUG_LEVEL _IOWR(ILITEK_IOCTL_MAGIC, 8, int) +#define ILITEK_IOCTL_TP_FUNC_MODE _IOWR(ILITEK_IOCTL_MAGIC, 9, int) + +#define ILITEK_IOCTL_TP_FW_VER _IOWR(ILITEK_IOCTL_MAGIC, 10, u8*) +#define ILITEK_IOCTL_TP_PL_VER _IOWR(ILITEK_IOCTL_MAGIC, 11, u8*) +#define ILITEK_IOCTL_TP_CORE_VER _IOWR(ILITEK_IOCTL_MAGIC, 12, u8*) +#define ILITEK_IOCTL_TP_DRV_VER _IOWR(ILITEK_IOCTL_MAGIC, 13, u8*) +#define ILITEK_IOCTL_TP_CHIP_ID _IOWR(ILITEK_IOCTL_MAGIC, 14, u32*) + +#define ILITEK_IOCTL_TP_MODE_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 17, u8*) +#define ILITEK_IOCTL_TP_MODE_STATUS _IOWR(ILITEK_IOCTL_MAGIC, 18, int*) +#define ILITEK_IOCTL_ICE_MODE_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 19, int) + +#define ILITEK_IOCTL_TP_INTERFACE_TYPE _IOWR(ILITEK_IOCTL_MAGIC, 20, u8*) +#define ILITEK_IOCTL_TP_DUMP_FLASH _IOWR(ILITEK_IOCTL_MAGIC, 21, int) +#define ILITEK_IOCTL_TP_FW_UART_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 22, u8*) +#define ILITEK_IOCTL_TP_PANEL_INFO _IOWR(ILITEK_IOCTL_MAGIC, 23, u32*) +#define ILITEK_IOCTL_TP_INFO _IOWR(ILITEK_IOCTL_MAGIC, 24, u32*) +#define ILITEK_IOCTL_WRAPPER_RW _IOWR(ILITEK_IOCTL_MAGIC, 25, u8*) +#define ILITEK_IOCTL_DDI_WRITE _IOWR(ILITEK_IOCTL_MAGIC, 26, u8*) +#define ILITEK_IOCTL_DDI_READ _IOWR(ILITEK_IOCTL_MAGIC, 27, u8*) +#define ILITEK_IOCTL_REPORT_RATE_SET _IOWR(ILITEK_IOCTL_MAGIC, 28, u8*) +#define ILITEK_IOCTL_REPORT_RATE_GET _IOWR(ILITEK_IOCTL_MAGIC, 29, u8*) +#define ILITEK_IOCTL_MP_LCM_OFF_ENV _IOWR(ILITEK_IOCTL_MAGIC, 30, u8*) +#define ILITEK_IOCTL_RELEASE_TOUCH _IOWR(ILITEK_IOCTL_MAGIC, 31, u8*) +#define ILITEK_IOCTL_LCM_STATUS _IOWR(ILITEK_IOCTL_MAGIC, 32, u8*) +#define ILITEK_IOCTL_SLAVE_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 33, u8*) +#define ILITEK_IOCTL_CASCADE_REG_RW _IOWR(ILITEK_IOCTL_MAGIC, 34, u8*) +#define ILITEK_IOCTL_INTERFACE_GET _IOWR(ILITEK_IOCTL_MAGIC, 35, uint8_t *) +#define ILITEK_IOCTL_ICE_MODE_FLAG_SET _IOWR(ILITEK_IOCTL_MAGIC, 36, uint8_t *) +#define ILITEK_IOCTL_RELEASE_PEN _IOWR(ILITEK_IOCTL_MAGIC, 37, uint8_t *) + +#ifdef CONFIG_COMPAT +#define ILITEK_COMPAT_IOCTL_I2C_WRITE_DATA _IOWR(ILITEK_IOCTL_MAGIC, 0, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_I2C_SET_WRITE_LENGTH _IOWR(ILITEK_IOCTL_MAGIC, 1, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_I2C_READ_DATA _IOWR(ILITEK_IOCTL_MAGIC, 2, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_I2C_SET_READ_LENGTH _IOWR(ILITEK_IOCTL_MAGIC, 3, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_HW_RESET _IOWR(ILITEK_IOCTL_MAGIC, 4, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_POWER_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 5, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_REPORT_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 6, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_IRQ_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 7, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_DEBUG_LEVEL _IOWR(ILITEK_IOCTL_MAGIC, 8, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_FUNC_MODE _IOWR(ILITEK_IOCTL_MAGIC, 9, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_FW_VER _IOWR(ILITEK_IOCTL_MAGIC, 10, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_PL_VER _IOWR(ILITEK_IOCTL_MAGIC, 11, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_CORE_VER _IOWR(ILITEK_IOCTL_MAGIC, 12, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_DRV_VER _IOWR(ILITEK_IOCTL_MAGIC, 13, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_CHIP_ID _IOWR(ILITEK_IOCTL_MAGIC, 14, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_MODE_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 17, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_MODE_STATUS _IOWR(ILITEK_IOCTL_MAGIC, 18, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_ICE_MODE_SWITCH _IOWR(ILITEK_IOCTL_MAGIC, 19, compat_uptr_t) + +#define ILITEK_COMPAT_IOCTL_TP_INTERFACE_TYPE _IOWR(ILITEK_IOCTL_MAGIC, 20, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_DUMP_FLASH _IOWR(ILITEK_IOCTL_MAGIC, 21, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_FW_UART_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 22, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_PANEL_INFO _IOWR(ILITEK_IOCTL_MAGIC, 23, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_TP_INFO _IOWR(ILITEK_IOCTL_MAGIC, 24, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_WRAPPER_RW _IOWR(ILITEK_IOCTL_MAGIC, 25, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_DDI_WRITE _IOWR(ILITEK_IOCTL_MAGIC, 26, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_DDI_READ _IOWR(ILITEK_IOCTL_MAGIC, 27, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_REPORT_RATE_SET _IOWR(ILITEK_IOCTL_MAGIC, 28, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_REPORT_RATE_GET _IOWR(ILITEK_IOCTL_MAGIC, 29, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_MP_LCM_OFF_ENV _IOWR(ILITEK_IOCTL_MAGIC, 30, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_RELEASE_TOUCH _IOWR(ILITEK_IOCTL_MAGIC, 31, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_LCM_STATUS _IOWR(ILITEK_IOCTL_MAGIC, 32, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_SLAVE_CTRL _IOWR(ILITEK_IOCTL_MAGIC, 33, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_CASCADE_REG_RW _IOWR(ILITEK_IOCTL_MAGIC, 34, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_INTERFACE_GET _IOWR(ILITEK_IOCTL_MAGIC, 35, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_ICE_MODE_FLAG_SET _IOWR(ILITEK_IOCTL_MAGIC, 36, compat_uptr_t) +#define ILITEK_COMPAT_IOCTL_RELEASE_PEN _IOWR(ILITEK_IOCTL_MAGIC, 37, compat_uptr_t) +#endif + +struct record_state { + u8 touch_palm_state_e : 2; + u8 app_an_statu_e : 3; + u8 app_sys_check_bg_abnormal : 1; + u8 g_b_wrong_bg : 1; +}; + +static unsigned char g_user_buf[USER_STR_BUFF] = {0}; +#define ILI_SPI_NAME "ilitek" +#define ILI_SPI_NAME_PRIMARY "primary" + +static struct class *touchscreen_class; +static struct device *touchscreen_class_dev; + +int ili_str2hex(char *str) +{ + int strlen, result, intermed, intermedtop; + char *s = str; + + while (*s != 0x0) { + s++; + } + + strlen = (int)(s - str); + s = str; + if (*s != 0x30) { + return -1; + } + + s++; + + if (*s != 0x78 && *s != 0x58) { + return -1; + } + s++; + + strlen = strlen - 3; + result = 0; + while (*s != 0x0) { + intermed = *s & 0x0f; + intermedtop = *s & 0xf0; + if (intermedtop == 0x60 || intermedtop == 0x40) { + intermed += 0x09; + } + intermed = intermed << (strlen << 2); + result = result | intermed; + strlen -= 1; + s++; + } + return result; +} + +int ili_katoi(char *str) +{ + int result = 0; + unsigned int digit; + int sign; + + if (*str == '-') { + sign = 1; + str += 1; + } else { + sign = 0; + if (*str == '+') { + str += 1; + } + } + + for (;; str += 1) { + digit = *str - '0'; + if (digit > 9) + break; + result = (10 * result) + digit; + } + + if (sign) { + return -result; + } + return result; +} + +int file_write(struct file_buffer *file, bool new_open) +{ +#if GENERIC_KERNEL_IMAGE + ILI_ERR("GKI version not allow drivers to use filp_open\n"); + return -1; +#else + struct file *f = NULL; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + mm_segment_t fs; +#endif + loff_t pos; + + if (file->ptr == NULL) { + ILI_ERR("str is invaild\n"); + return -1; + } + + if (strlen(file->fname) == 0) { + ILI_ERR("file name is invaild\n"); + return -1; + } + + if (file->flen >= file->max_size) { + ILI_ERR("The length saved to file is too long !\n"); + return -1; + } + + if (new_open) + f = filp_open(file->fname, O_WRONLY | O_CREAT | O_TRUNC, 644); + else + f = filp_open(file->fname, O_WRONLY | O_CREAT | O_APPEND, 644); + + if (ERR_ALLOC_MEM(f)) { + ILI_ERR("Failed to open %s file\n", file->fname); + return -1; + } + + pos = 0; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + fs = get_fs(); + set_fs(KERNEL_DS); + vfs_write(f, file->ptr, file->wlen, &pos); + set_fs(fs); +#else + kernel_write(f, file->ptr, file->wlen, &pos); +#endif + filp_close(f, NULL); + return 0; +#endif +} + +static int ilitek_debug_node_buff_control(bool open) +{ + int i, ret; + + ilits->dnp = open; + + ILI_INFO("Debug buf ctrl = %s\n", ilits->dnp ? "Enabled" : "Disabled"); + + if (open) { + ilits->dlnp = DISABLE; + ilits->dbf = 0; + ilits->odi = 0; + if (!ERR_ALLOC_MEM(ilits->dbl)) { + for (i = 0; i < TR_BUF_LIST_SIZE; i++) { + ilits->dbl[i].mark = false; + ipio_kfree((void **)&ilits->dbl[i].data); + } + ipio_kfree((void **)&ilits->dbl); + } + + ilits->dbl = kzalloc(TR_BUF_LIST_SIZE * sizeof(*ilits->dbl), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->dbl)) { + ILI_ERR("Failed to allocate ilits->dbl mem, %ld\n", PTR_ERR(ilits->dbl)); + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < TR_BUF_LIST_SIZE; i++) { + ilits->dbl[i].mark = false; + ilits->dbl[i].data = kzalloc(TR_BUF_SIZE * sizeof(unsigned char), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->dbl[i].data)) { + ILI_ERR("Failed to allocate dbl[%d] mem, %ld\n", i, PTR_ERR(ilits->dbl[i].data)); + ret = -ENOMEM; + goto out; + } + } + ilits->outputStrArr = vmalloc(TR_BUF_SIZE * 2); + return 0; + } +out: + ilits->dnp = DISABLE; + if (!ERR_ALLOC_MEM(ilits->dbl)) { + for (i = 0; i < TR_BUF_LIST_SIZE; i++) { + ilits->dbl[i].mark = false; + ipio_kfree((void **)&ilits->dbl[i].data); + } + } + ipio_kfree((void **)&ilits->dbl); + ipio_vfree((void **)&ilits->outputStrArr); + return ret; +} + +static int ilitek_debug_all_node_buff_control(bool open) +{ + int i, ret; + + ilits->dlnp = open; + + ILI_INFO("Debug all buf ctrl = %s\n", ilits->dlnp ? "Enabled" : "Disabled"); + + if (open) { + ilits->dnp = DISABLE; + ilits->dbf = 0; + ilits->odi = 0; + /* ping pong buffer*/ + /* debug buffer A*/ + if (!ERR_ALLOC_MEM(ilits->dbf_a)) { + for (i = 0; i < TR_BUF_LIST_SIZE; i++) { + ilits->dbf_a[i].mark = false; + ipio_kfree((void **)&ilits->dbf_a[i].data); + } + ipio_kfree((void **)&ilits->dbf_a); + } + + ilits->dbf_a = kzalloc(TR_BUF_LIST_SIZE * sizeof(*ilits->dbf_a), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->dbf_a)) { + ILI_ERR("Failed to allocate ilits->dbf_a mem, %ld\n", PTR_ERR(ilits->dbf_a)); + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < TR_BUF_LIST_SIZE; i++) { + ilits->dbf_a[i].mark = false; + ilits->dbf_a[i].data = kzalloc(TR_BUF_SIZE * sizeof(unsigned char), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->dbf_a[i].data)) { + ILI_ERR("Failed to allocate dbl_a[%d] mem, %ld\n", i, PTR_ERR(ilits->dbf_a[i].data)); + ret = -ENOMEM; + goto out; + } + } + + /* debug buffer B */ + if (!ERR_ALLOC_MEM(ilits->dbf_b)) { + for (i = 0; i < TR_BUF_LIST_SIZE; i++) { + ilits->dbf_b[i].mark = false; + ipio_kfree((void **)&ilits->dbf_b[i].data); + } + ipio_kfree((void **)&ilits->dbf_b); + } + + ilits->dbf_b = kzalloc(TR_BUF_LIST_SIZE * sizeof(*ilits->dbf_b), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->dbf_b)) { + ILI_ERR("Failed to allocate ilits->dbf_b mem, %ld\n", PTR_ERR(ilits->dbf_b)); + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < TR_BUF_LIST_SIZE; i++) { + ilits->dbf_b[i].mark = false; + ilits->dbf_b[i].data = kzalloc(TR_BUF_SIZE * sizeof(unsigned char), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->dbf_b[i].data)) { + ILI_ERR("Failed to allocate dbl_b[%d] mem, %ld\n", i, PTR_ERR(ilits->dbf_b[i].data)); + ret = -ENOMEM; + goto out; + } + } + + ilits->dbf_in = ilits->dbf_a; + ilits->dbf_out = ilits->dbf_b; + ilits->dbf_in_idx = 0; + ilits->dbf_out_idx = 0; + ilits->ppb = ON; + + return 0; + } + +out: + ilits->dlnp = DISABLE; + /* free ping pong buffer */ + if (!ERR_ALLOC_MEM(ilits->dbf_a)) { + for (i = 0; i < TR_BUF_LIST_SIZE; i++) { + ilits->dbf_a[i].mark = false; + ipio_kfree((void **)&ilits->dbf_a[i].data); + } + } + ipio_kfree((void **)&ilits->dbf_a); + + if (!ERR_ALLOC_MEM(ilits->dbf_b)) { + for (i = 0; i < TR_BUF_LIST_SIZE; i++) { + ilits->dbf_b[i].mark = false; + ipio_kfree((void **)&ilits->dbf_b[i].data); + } + } + ipio_kfree((void **)&ilits->dbf_b); + + return ret; +} + +static int debug_mode_get_data(struct file_buffer *file, u8 type, u32 frame_count) +{ + int ret = 0, cdc_starIdx = 0; + u8 cmd[2] = { 0 }, row, col; + s16 temp; + unsigned char *ptr; + int j; + u16 write_index = 0; + + ilitek_debug_node_buff_control(DISABLE); + + ilits->dbf = 0; + row = ilits->ych_num; + col = ilits->xch_num; + mutex_lock(&ilits->touch_mutex); + + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + cdc_starIdx = P5_X_DEBUG_LOW_RESOLUTION_FINGER_DATA_LENGTH; + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + cdc_starIdx = P5_X_DEBUG_HIGH_RESOLUTION_FINGER_DATA_LENGTH; + } + + if (ilits->chip->core_ver < CORE_VER_1700) { + cmd[0] = 0xFA; + cmd[1] = type; + ret = ilits->wrapper(cmd, 2, NULL, 0, ON, OFF); + } + + if (ilitek_debug_node_buff_control(ENABLE) < 0) { + ILI_ERR("Failed to allocate debug buf\n"); + ret = -ENOMEM; + goto out; + } + + mutex_unlock(&ilits->touch_mutex); + + if (ret < 0) { + ILI_ERR("Write 0xFA,0x%x failed\n", type); + goto out; + } + + while (write_index < frame_count) { + file->wlen = 0; + ILI_INFO("frame = %d,index = %d,count = %d\n", write_index, ilits->odi, ilits->dbf); + if (!wait_event_interruptible_timeout(ilits->inq, ilits->dbl[ilits->odi].mark, msecs_to_jiffies(3000))) { + ILI_ERR("debug mode get data timeout!\n"); + goto out; + } + + mutex_lock(&ilits->touch_mutex); + + memset(file->ptr, 0, file->max_size); + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "\n\nFrame%d,", write_index); + for (j = 0; j < col; j++) + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "[X%d] ,", j); + ptr = &ilits->dbl[ilits->odi].data[cdc_starIdx]; + for (j = 0; j < row * col; j++, ptr += 2) { + temp = (*ptr << 8) + *(ptr + 1); + if (j % col == 0) + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "\n[Y%d] ,", (j / col)); + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "%d, ", temp); + } + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "\n[X] ,"); + for (j = 0; j < row + col; j++, ptr += 2) { + temp = (*ptr << 8) + *(ptr + 1); + if (j == col) + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "\n[Y] ,"); + file->wlen += snprintf(file->ptr + file->wlen, (file->max_size - file->wlen), "%d, ", temp); + } + file_write(file, false); + write_index++; + + mutex_unlock(&ilits->touch_mutex); + + ilits->dbl[ilits->odi].mark = false; + ilits->odi = ((ilits->odi + 1) % TR_BUF_LIST_SIZE); + } +out: + ilitek_debug_node_buff_control(DISABLE); + return ret; +} + +static int dev_mkdir(char *name, umode_t mode) +{ + int err = 0; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 14, 0) + mm_segment_t fs; + + ILI_INFO("mkdir: %s\n", name); + fs = get_fs(); + set_fs(KERNEL_DS); +#ifdef CONFIG_ARCH_HAS_SYSCALL_WRAPPER + err = ksys_mkdir(name, mode); +#else + err = sys_mkdir(name, mode); +#endif + set_fs(fs); +#else + err = -1; +#endif + return err; +} + +static ssize_t ilitek_proc_get_delta_data_read(struct file *pFile, char __user *buf, size_t size, loff_t *pos) +{ + s16 *delta = NULL; + int row = 0, col = 0, index = 0; + int ret, i, x, y; + int read_length = 0, len = 0; + u8 cmd[2] = {0}; + u8 *data = NULL; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + ili_wq_ctrl(WQ_ESD, DISABLE); + ili_wq_ctrl(WQ_BAT, DISABLE); + mutex_lock(&ilits->touch_mutex); + + row = ilits->ych_num; + col = ilits->xch_num; + read_length = 4 + 2 * row * col + 1 ; + + ILI_INFO("read length = %d\n", read_length); + + data = kcalloc(read_length + 1, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(data)) { + ILI_ERR("Failed to allocate data mem\n"); + goto out; + } + + delta = kcalloc(P5_X_DEBUG_MODE_PACKET_LENGTH, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(delta)) { + ILI_ERR("Failed to allocate delta mem\n"); + goto out; + } + + cmd[0] = 0xB7; + cmd[1] = 0x1; /* get delta */ + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Failed to write 0xB7,0x1 command, %d\n", ret); + goto out; + } + + msleep(120); + + /* read debug packet header */ + ret = ilits->wrapper(NULL, 0, data, read_length, OFF, OFF); + if (ret < 0) { + ILI_ERR("Read debug packet header failed, %d\n", ret); + goto out; + } + + cmd[1] = 0x03; /* switch to normal mode */ + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, ON, OFF); + if (ret < 0) { + ILI_ERR("Failed to write 0xB7,0x3 command, %d\n", ret); + goto out; + } + + for (i = 4, index = 0; i < row * col * 2 + 4; i += 2, index++) + delta[index] = (data[i] << 8) + data[i + 1]; + + len = snprintf(g_user_buf, PAGE_SIZE - len, "======== Deltadata ========\n"); + ILI_INFO("======== Deltadata ========\n"); + + len += snprintf(g_user_buf + len, PAGE_SIZE - len, + "Header 0x%x ,Type %d, Length %d\n", data[0], data[1], (data[2] << 8) | data[3]); + ILI_INFO("Header 0x%x ,Type %d, Length %d\n", data[0], data[1], (data[2] << 8) | data[3]); + + /* print delta data */ + for (y = 0; y < row; y++) { + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "[%2d] ", (y+1)); + ILI_INFO("[%2d] ", (y+1)); + + for (x = 0; x < col; x++) { + int shift = y * col + x; + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "%5d", delta[shift]); + printk(KERN_CONT "%5d", delta[shift]); + } + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "\n"); + printk(KERN_CONT "\n"); + } + + if (copy_to_user(buf, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + +out: + mutex_unlock(&ilits->touch_mutex); + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + ipio_kfree((void **)&data); + ipio_kfree((void **)&delta); + return len; +} + +static ssize_t ilitek_proc_fw_get_raw_data_read(struct file *pFile, char __user *buf, size_t size, loff_t *pos) +{ + s16 *rawdata = NULL; + int row = 0, col = 0, index = 0; + int ret, i, x, y; + int read_length = 0, len = 0; + u8 cmd[2] = {0}; + u8 *data = NULL; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + ili_wq_ctrl(WQ_ESD, DISABLE); + ili_wq_ctrl(WQ_BAT, DISABLE); + mutex_lock(&ilits->touch_mutex); + + row = ilits->ych_num; + col = ilits->xch_num; + read_length = 4 + 2 * row * col + 1 ; + + ILI_INFO("read length = %d\n", read_length); + + data = kcalloc(read_length + 1, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(data)) { + ILI_ERR("Failed to allocate data mem\n"); + goto out; + } + + rawdata = kcalloc(P5_X_DEBUG_MODE_PACKET_LENGTH, sizeof(s32), GFP_KERNEL); + if (ERR_ALLOC_MEM(rawdata)) { + ILI_ERR("Failed to allocate rawdata mem\n"); + goto out; + } + + cmd[0] = 0xB7; + cmd[1] = 0x2; /* get rawdata */ + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Failed to write 0xB7,0x2 command, %d\n", ret); + goto out; + } + + msleep(120); + + /* read debug packet header */ + ret = ilits->wrapper(NULL, 0, data, read_length, OFF, OFF); + if (ret < 0) { + ILI_ERR("Read debug packet header failed, %d\n", ret); + goto out; + } + + cmd[1] = 0x03; /* switch to normal mode */ + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, ON, OFF); + if (ret < 0) { + ILI_ERR("Failed to write 0xB7,0x3 command, %d\n", ret); + goto out; + } + + for (i = 4, index = 0; i < row * col * 2 + 4; i += 2, index++) + rawdata[index] = (data[i] << 8) + data[i + 1]; + + len = snprintf(g_user_buf, PAGE_SIZE, "======== RawData ========\n"); + ILI_INFO("======== RawData ========\n"); + + len += snprintf(g_user_buf + len, PAGE_SIZE - len, + "Header 0x%x ,Type %d, Length %d\n", data[0], data[1], (data[2] << 8) | data[3]); + ILI_INFO("Header 0x%x ,Type %d, Length %d\n", data[0], data[1], (data[2] << 8) | data[3]); + + /* print raw data */ + for (y = 0; y < row; y++) { + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "[%2d] ", (y+1)); + ILI_INFO("[%2d] ", (y+1)); + + for (x = 0; x < col; x++) { + int shift = y * col + x; + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "%5d", rawdata[shift]); + printk(KERN_CONT "%5d", rawdata[shift]); + } + len += snprintf(g_user_buf + len, PAGE_SIZE - len, "\n"); + printk(KERN_CONT "\n"); + } + + if (copy_to_user(buf, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + +out: + mutex_unlock(&ilits->touch_mutex); + ili_wq_ctrl(WQ_ESD, ENABLE); + ili_wq_ctrl(WQ_BAT, ENABLE); + ipio_kfree((void **)&data); + ipio_kfree((void **)&rawdata); + return len; +} + +static ssize_t ilitek_proc_fw_pc_counter_read(struct file *pFile, char __user *buf, size_t size, loff_t *pos) +{ + int len = 0; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + ili_ic_get_pc_counter(0); + len = snprintf(g_user_buf, PAGE_SIZE, "pc = 0x%x, latch = 0x%x\n", ilits->fw_pc, ilits->fw_latch); + + if (copy_to_user(buf, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + return len; +} + +int ili_cascade_rw_tp_reg(u32 mcu, u32 type, u32 addr, u32 wdata, u32 wlen, u32 *rdata) +{ + int len = 0; +#if (TDDI_INTERFACE == BUS_SPI) + int ret = 0; + bool mcu_on = ON, read = 0; + u32 read_data; + + if (ilits->slave_wr_ctrl == SLAVE) { + ili_set_bypass_mode(mcu); + } else { + if (mcu == mcu_on) + ret = ili_ice_mode_ctrl_by_mode(ENABLE, ON, MASTER); + else + ret = ili_ice_mode_ctrl_by_mode(ENABLE, OFF, MASTER); + + if (ret < 0) { + ILI_ERR("Failed to enter ICE mode, ret = %d\n", ret); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to enter ICE mode"); + } + } + + if (type == read) { + if (ilits->slave_wr_ctrl == SLAVE) { + ili_mspi_read_slave_register(addr, &read_data, sizeof(u32)); + } else { + if (ili_ice_mode_read_by_mode(addr, &read_data, sizeof(u32), MASTER) < 0) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Read data error"); + } + } + + *rdata = read_data; + + ILI_INFO("[READ]:addr = 0x%06x, read = 0x%08x\n", addr, read_data); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "READ:addr = 0x%06x, read = 0x%08x\n", addr, read_data); + } else { + if (ilits->slave_wr_ctrl == SLAVE) { + ili_mspi_write_slave_register(addr, wdata, wlen); + } else { + if (ili_ice_mode_write_by_mode(addr, wdata, wlen, MASTER) < 0) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Write data error"); + } + } + + ILI_INFO("[WRITE]:addr = 0x%06x, write = 0x%08x, len = %d byte\n", addr, wdata, wlen); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "WRITE:addr = 0x%06x, write = 0x%08x, len =%d byte\n", addr, wdata, wlen); + } + + if (mcu == mcu_on) + ret = ili_ice_mode_ctrl(DISABLE, ON); + else + ret = ili_ice_mode_ctrl(DISABLE, OFF); + + if (ret < 0) { + ILI_ERR("Failed to disable ICE mode, ret = %d\n", ret); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to disable ICE mode"); + } +#endif + return len; +} + +static u32 rw_reg[5] = {0}; +static ssize_t ilitek_proc_rw_tp_reg_read(struct file *pFile, char __user *buf, size_t size, loff_t *pos) +{ + int ret = 0, len = 0; + bool mcu_on = 0, read = 0; + u32 type, addr, read_data, write_data, write_len, stop_mcu; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (*pos != 0) + return 0; + + stop_mcu = rw_reg[0]; + type = rw_reg[1]; + addr = rw_reg[2]; + write_data = rw_reg[3]; + write_len = rw_reg[4]; + + ILI_INFO("stop_mcu = %d\n", rw_reg[0]); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + mutex_lock(&ilits->touch_mutex); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + if (ilits->cascade_info_block.nNum != 0) { +#if (TDDI_INTERFACE == BUS_SPI) + len = ili_cascade_rw_tp_reg(stop_mcu, type, addr, write_data, write_len, &read_data); + +#elif (TDDI_INTERFACE == BUS_I2C) + if (stop_mcu == mcu_on) + ret = ili_ice_mode_ctrl_by_mode(ENABLE, ON, ilits->slave_wr_ctrl); + else + ret = ili_ice_mode_ctrl_by_mode(ENABLE, OFF, ilits->slave_wr_ctrl); + + if (ret < 0) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to enter ICE mode"); + } + + if (type == read) { + if (ili_ice_mode_read_by_mode(addr, &read_data, sizeof(u32), ilits->slave_wr_ctrl) < 0) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Read data error"); + } + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "READ:addr = 0x%06x, read = 0x%08x\n", addr, read_data); + } else { + if (ili_ice_mode_write_by_mode(addr, write_data, write_len, ilits->slave_wr_ctrl) < 0) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Write data error"); + } + + ILI_INFO("[WRITE]:addr = 0x%06x, write = 0x%08x, len = %d byte\n", addr, write_data, write_len); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "WRITE:addr = 0x%06x, write = 0x%08x, len =%d byte\n", addr, write_data, write_len); + } + + if (stop_mcu == mcu_on) + ret = ili_ice_mode_ctrl_by_mode(DISABLE, ON, ilits->slave_wr_ctrl); + else + ret = ili_ice_mode_ctrl_by_mode(DISABLE, OFF, ilits->slave_wr_ctrl); + + if (ret < 0) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to disable ICE mode"); + } +#endif + } else { + if (stop_mcu == mcu_on) + ret = ili_ice_mode_ctrl(ENABLE, ON); + else + ret = ili_ice_mode_ctrl(ENABLE, OFF); + + if (ret < 0) { + ILI_ERR("Failed to enter ICE mode, ret = %d\n", ret); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to enter ICE mode"); + } + + if (type == read) { + if (ili_ice_mode_read(addr, &read_data, sizeof(u32)) < 0) { + ILI_ERR("Read data error\n"); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Read data error"); + } + + ILI_INFO("[READ]:addr = 0x%06x, read = 0x%08x\n", addr, read_data); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "READ:addr = 0x%06x, read = 0x%08x\n", addr, read_data); + } else { + if (ili_ice_mode_write(addr, write_data, write_len) < 0) { + ILI_ERR("Write data error\n"); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Write data error"); + } + + ILI_INFO("[WRITE]:addr = 0x%06x, write = 0x%08x, len = %d byte\n", addr, write_data, write_len); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "WRITE:addr = 0x%06x, write = 0x%08x, len =%d byte\n", addr, write_data, write_len); + } + + if (stop_mcu == mcu_on) + ret = ili_ice_mode_ctrl(DISABLE, ON); + else + ret = ili_ice_mode_ctrl(DISABLE, OFF); + + if (ret < 0) { + ILI_ERR("Failed to disable ICE mode, ret = %d\n", ret); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to disable ICE mode"); + } + } + + if (copy_to_user((char *)buf, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_proc_rw_tp_reg_write(struct file *filp, const char *buff, size_t size, loff_t *pos) +{ + char *token = NULL, *cur = NULL; + char cmd[256] = { 0 }; + u32 count = 0; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if ((size - 1) > sizeof(cmd)) { + ILI_ERR("ERROR! input length is larger than local buffer\n"); + return -1; + } + + mutex_lock(&ilits->touch_mutex); + + if (buff != NULL) { + if (copy_from_user(cmd, buff, size - 1)) { + ILI_INFO("Failed to copy data from user space\n"); + size = -1; + goto out; + } + } + token = cur = cmd; + while ((token = strsep(&cur, ",")) != NULL) { + if (count >= (sizeof(rw_reg) / sizeof(u32))) { + ILI_ERR("command length is larger than function need\n"); + break; + } + rw_reg[count] = ili_str2hex(token); + ILI_INFO("rw_reg[%d] = 0x%x\n", count, rw_reg[count]); + count++; + } + +out: + mutex_unlock(&ilits->touch_mutex); + return size; +} + +static u8 fw_cmd_buff[256] = { 0 }; +static ssize_t ilitek_proc_fw_cmd_read(struct file *pFile, char __user *buf, size_t size, loff_t *pos) +{ + int len = 0, i = 0; + u8 *wrap_rbuf = NULL; + u16 wrap_wlen = 0, wrap_rlen = 0; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (*pos != 0) + return 0; + + mutex_lock(&ilits->touch_mutex); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + wrap_rlen = (fw_cmd_buff[0] << 8) | fw_cmd_buff[1]; + wrap_wlen = (fw_cmd_buff[2] << 8) | fw_cmd_buff[3]; + + ILI_INFO("fw_cmd read wrap_wlen = %d, wrap_rlen = %d\n", wrap_wlen, wrap_rlen); + + if (wrap_wlen > IOCTL_I2C_BUFF || wrap_rlen > IOCTL_I2C_BUFF) { + ILI_ERR("ERROR! R/W len is largn than ioctl buf\n"); + return -ENOTTY; + } + + if (wrap_rlen > 0) { + wrap_rbuf = kcalloc(IOCTL_I2C_BUFF, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(wrap_rbuf)) { + ILI_ERR("Failed to allocate mem\n"); + return -ENOMEM; + } + } + + if (atomic_read(&ilits->ice_stat)) { + ilits->wrapper(fw_cmd_buff + 4, wrap_wlen, wrap_rbuf, wrap_rlen, OFF, OFF); + } else { + if (ilits->actual_tp_mode == P5_X_FW_TEST_MODE) { + ilits->wrapper(fw_cmd_buff + 4, wrap_wlen, wrap_rbuf, wrap_rlen, ON, ON); + } else { + ilits->wrapper(fw_cmd_buff + 4, wrap_wlen, wrap_rbuf, wrap_rlen, ON, OFF); + } + } + + ili_dump_data(fw_cmd_buff + 4, 8, wrap_wlen, 16, "write fw data:"); + + if (wrap_rlen > 0) { + ili_dump_data(wrap_rbuf, 8, wrap_rlen, 16, "read fw data:"); + for (i = 0; i < wrap_rlen; i++) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%02X", wrap_rbuf[i]); + } + } else { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "Ack"); + } + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "\n"); + + if (copy_to_user((char *)buf, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_proc_fw_cmd_write(struct file *filp, const char *buff, size_t size, loff_t *pos) +{ + char *token = NULL, *cur = NULL; + char cmd[256] = { 0 }; + u32 count = 0; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if ((size - 1) > sizeof(cmd)) { + ILI_ERR("ERROR! input length is larger than local buffer\n"); + return -1; + } + + mutex_lock(&ilits->touch_mutex); + + if (buff != NULL) { + if (copy_from_user(cmd, buff, size - 1)) { + ILI_INFO("Failed to copy data from user space\n"); + size = -1; + goto out; + } + } + token = cur = cmd; + while ((token = strsep(&cur, ",")) != NULL) { + if (count >= sizeof(fw_cmd_buff)) { + ILI_ERR("command length is larger than function need\n"); + break; + } + fw_cmd_buff[count] = ili_str2hex(token); + ILI_INFO("fw_cmd write buff[%d] = 0x%x\n", count, fw_cmd_buff[count]); + count++; + } + +out: + mutex_unlock(&ilits->touch_mutex); + return size; +} + +static ssize_t ilitek_proc_debug_switch_read(struct file *pFile, char __user *buff, size_t size, loff_t *pos) +{ + bool open; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + mutex_lock(&ilits->debug_read_mutex); + mutex_lock(&ilits->debug_mutex); + + open = !ilits->dnp; + ilits->all_frame_full = false; + + ilitek_debug_node_buff_control(open); + + size = snprintf(g_user_buf, USER_STR_BUFF * sizeof(unsigned char), "dnp : %s\n", ilits->dnp ? "Enabled" : "Disabled"); + *pos = size; + + if (copy_to_user(buff, g_user_buf, size)) + ILI_ERR("Failed to copy data to user space\n"); + + mutex_unlock(&ilits->debug_mutex); + mutex_unlock(&ilits->debug_read_mutex); + return size; +} + +static ssize_t ilitek_proc_debug_all_switch_read(struct file *pFile, char __user *buff, size_t size, loff_t *pos) +{ + bool open; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + mutex_lock(&ilits->debug_read_mutex); + mutex_lock(&ilits->debug_mutex); + + open = !ilits->dlnp; + + ilitek_debug_all_node_buff_control(open); + + size = snprintf(g_user_buf, USER_STR_BUFF * sizeof(unsigned char), "dlnp : %s\n", ilits->dlnp ? "Enabled" : "Disabled"); + *pos = size; + + if (copy_to_user(buff, g_user_buf, size)) + ILI_ERR("Failed to copy data to user space\n"); + + mutex_unlock(&ilits->debug_mutex); + mutex_unlock(&ilits->debug_read_mutex); + return size; +} + +static void ilitek_switch_debug_message_ping_pong_buff(void) +{ + bool flag; + int temp; + ILI_DBG("switch debug_message buff\n"); + mutex_lock(&ilits->debug_mutex); + + flag = !ilits->ppb; + + ILI_DBG("Before dbf_in_idx=%d, dbf_out_idx=%d\n", ilits->dbf_in_idx, ilits->dbf_out_idx); + if (flag) { + ILI_INFO("debug_message buff, input set a, output set b\n"); + ilits->dbf_in = ilits->dbf_a; + ilits->dbf_out = ilits->dbf_b; + } else { + ILI_INFO("debug_message buff, input set b, output set a\n"); + ilits->dbf_in = ilits->dbf_b; + ilits->dbf_out = ilits->dbf_a; + } + temp = ilits->dbf_in_idx; + ilits->dbf_in_idx = ilits->dbf_out_idx; + ilits->dbf_out_idx = temp; + + ilits->ppb = flag; + + ILI_DBG("dbf_in_idx=%d, dbf_out_idx=%d, ilits->ppb=%d\n", ilits->dbf_in_idx, ilits->dbf_out_idx, ilits->ppb); + + mutex_unlock(&ilits->debug_mutex); +} + +static int ilitek_proc_debug_all_message_show(struct seq_file *m, void *v) +{ + int i = 0, count = 0; + int data_count = 0; + int one_data_bytes = 0; + int need_read_data_len = 0; + int type = 0; + + if (m->size < 500 * K) { + m->count = m->size; + return 0; + } + + if (!ilits->dlnp) { + ILI_ERR("Debug flag isn't enabled (%d)\n", ilits->dlnp); + seq_printf(m, "Debug flag isn't enabled (%d)\n", ilits->dlnp); + return -EAGAIN; + } + ilitek_switch_debug_message_ping_pong_buff(); + + count = 0; + do { + if (ilits->dbf_out[count].mark) { + if (ilits->dbf_out[count].data[0] == P5_X_DEMO_PACKET_ID) { + need_read_data_len = P5_X_DEMO_MODE_PACKET_LEN; + } else if (ilits->dbf_out[count].data[0] == P5_X_I2CUART_PACKET_ID) { + type = ilits->dbf_out[count].data[3] & 0x0F; + + data_count = ilits->dbf_out[count].data[1] * ilits->dbf_out[count].data[2]; + + if (type == 0 || type == 1 || type == 6) { + one_data_bytes = 1; + } else if (type == 2 || type == 3) { + one_data_bytes = 2; + } else if (type == 4 || type == 5) { + one_data_bytes = 4; + } + need_read_data_len = data_count * one_data_bytes + 1 + 5; + } else if (ilits->dbf_out[count].data[0] == P5_X_DEBUG_PACKET_ID /* A7 */ + || ilits->dbf_out[count].data[0] == P5_X_DEBUG_HIGH_RESOLUTION_PACKET_ID /* A8 */ + || ilits->dbf_out[count].data[0] == P5_X_DEMO_HIGH_RESOLUTION_PACKET_ID /* 5B */ + || ilits->dbf_out[count].data[0] == P5_X_DEBUG_LITE_PACKET_ID) { /* 9A */ + need_read_data_len = (int) ((ilits->dbf_out[count].data[1] << 8) + ilits->dbf_out[count].data[2]) & 0xFFFF; + } + if (ilits->pen_info.pen_data_mode == PEN_ONLY_SIGNAL_MODE + || ilits->pen_info.pen_data_mode == PEN_ONLY_RAW_DATA_MODE) { + seq_printf(m, ":0x80,"); + } else if (ilits->pen_info.pen_data_mode == HAND_ONLY_SIGNAL_MODE + || ilits->pen_info.pen_data_mode == HAND_ONLY_RAW_DATA_MODE) { + seq_printf(m, ":0x81,"); + } else if (ilits->tp_data_format == DATA_FORMAT_DEMO) { + seq_printf(m, ":0x10,"); + } else if(ilits->tp_data_format == DATA_FORMAT_DEBUG) { + seq_printf(m, ":0x45,"); + } + + for (i = 0; i < need_read_data_len; i++) { + seq_printf(m, "%02X", ilits->dbf_out[count].data[i]); + } + seq_printf(m, ":\n"); + ilits->dbf_out[count].mark = false; + } + count++; + + } while (count < ilits->dbf_out_idx); + + ILI_DBG("total send %d frame\n", ilits->dbf_out_idx); + + ilits->dbf_out_idx = 0; + return 0; +} + +#if DMESG_SEQ_FILE +static int ilitek_proc_debug_message_show(struct seq_file *m, void *v) +{ + int i = 0; + int data_count = 0; + int one_data_bytes = 0; + int need_read_data_len = 0; + int type = 0; + + /* '0' ~ 'F' mapping table */ + char charmapping[16] = {0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46 }; + + if (m->size < 16 * K) { + m->count = m->size; + return 0; + } + + if (!ilits->dnp) { + ILI_ERR("Debug flag isn't enabled (%d)\n", ilits->dnp); + seq_printf(m, "Debug flag isn't enabled (%d)\n", ilits->dnp); + return -EAGAIN; + } + + if (ilits->odi == ilits->dbf && ilits->all_frame_full == false) { + /* ILI_DBG("WARNING ! there's no cdc data.\n"); */ + return -EAGAIN; + } + + mutex_lock(&ilits->debug_read_mutex); + ILI_DBG("f_count= %d, index = %d, mark = %d\n", ilits->dbf, ilits->odi, ilits->dbl[ilits->odi].mark); + if (!wait_event_interruptible_timeout(ilits->inq, ilits->dbl[ilits->odi].mark, msecs_to_jiffies(ilits->wait_int_timeout))) { + ILI_ERR("WARNING ! there's no data received.\n"); + mutex_unlock(&ilits->debug_read_mutex); + return -EAGAIN; + } + mutex_lock(&ilits->debug_mutex); + + ILI_DBG("dbl mark = %d, data[0] = 0x%X, data[1] = 0x%X, data[2] = 0x%X \n", ilits->dbl[ilits->odi].mark, ilits->dbl[ilits->odi].data[0], ilits->dbl[ilits->odi].data[1], ilits->dbl[ilits->odi].data[2]); + + if (ilits->dbl[ilits->odi].mark) { + if (ilits->dbl[ilits->odi].data[0] == P5_X_DEMO_PACKET_ID) { + need_read_data_len = P5_X_DEMO_MODE_PACKET_LEN; + } else if (ilits->dbl[ilits->odi].data[0] == P5_X_I2CUART_PACKET_ID) { + type = ilits->dbl[ilits->odi].data[3] & 0x0F; + + data_count = ilits->dbl[ilits->odi].data[1] * ilits->dbl[ilits->odi].data[2]; + + if (type == 0 || type == 1 || type == 6) { + one_data_bytes = 1; + } else if (type == 2 || type == 3) { + one_data_bytes = 2; + } else if (type == 4 || type == 5) { + one_data_bytes = 4; + } + need_read_data_len = data_count * one_data_bytes + 1 + 5; + } else if (ilits->dbl[ilits->odi].data[0] == P5_X_DEBUG_PACKET_ID /* A7 */ + || ilits->dbl[ilits->odi].data[0] == P5_X_DEBUG_HIGH_RESOLUTION_PACKET_ID /* A8 */ + || ilits->dbl[ilits->odi].data[0] == P5_X_DEMO_HIGH_RESOLUTION_PACKET_ID /* 5B */ + || ilits->dbl[ilits->odi].data[0] == P5_X_DEBUG_LITE_PACKET_ID) { /* 9A */ + need_read_data_len = (int) ((ilits->dbl[ilits->odi].data[1] << 8) + ilits->dbl[ilits->odi].data[2]) & 0xFFFF; + } + + ILI_DBG("Need Read data len = %d\n", need_read_data_len); + + if (ilits->outputStrArr == NULL || ERR_ALLOC_MEM(ilits->outputStrArr)) { + ILI_ERR("ilits->outputStrArr memory error.\n"); + ilits->outputStrArr = vmalloc(TR_BUF_SIZE * 2); + } + memset(ilits->outputStrArr, 0, need_read_data_len * 2 + 4); + for (i = 0; i < need_read_data_len * 2; i+=2) { + ilits->outputStrArr[i] = charmapping[(ilits->dbl[ilits->odi].data[i >> 1]) >> 4]; + ilits->outputStrArr[i+1] = charmapping[(ilits->dbl[ilits->odi].data[i >> 1]) & 0x0F]; + } + seq_printf(m, "%s", ilits->outputStrArr); + seq_printf(m, "\n\n"); + ilits->all_frame_full = false; + ilits->dbl[ilits->odi].mark = false; + ilits->odi = ((ilits->odi + 1) % TR_BUF_LIST_SIZE); + } + + mutex_unlock(&ilits->debug_mutex); + mutex_unlock(&ilits->debug_read_mutex); + return 0; +} +#else +static ssize_t ilitek_proc_debug_message_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + unsigned long p = *pos; + int i = 0; + int send_data_len = 0; + int ret = 0; + int data_count = 0; + int one_data_bytes = 0; + int need_read_data_len = 0; + int type = 0; + unsigned char *tmpbuf = NULL; + unsigned char tmpbufback[128] = {0}; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (!ilits->dnp) { + ILI_ERR("Debug flag isn't enabled (%d)\n", ilits->dnp); + return -EAGAIN; + } + + if (ilits->odi == ilits->dbf && ilits->all_frame_full == false) { + /* ILI_DBG("WARNING ! there's no cdc data.\n"); */ + *pos = send_data_len; + return send_data_len; + } + + mutex_lock(&ilits->debug_read_mutex); + ILI_DBG("f_count= %d, index = %d, mark = %d\n", ilits->dbf, ilits->odi, ilits->dbl[ilits->odi].mark); + if (!wait_event_interruptible_timeout(ilits->inq, ilits->dbl[ilits->odi].mark, msecs_to_jiffies(ilits->wait_int_timeout))) { + ILI_ERR("WARNING ! there's no data received.\n"); + mutex_unlock(&ilits->debug_read_mutex); + *pos = send_data_len; + return send_data_len; + } + mutex_lock(&ilits->debug_mutex); + + tmpbuf = vmalloc(4096); /* buf size if even */ + if (ERR_ALLOC_MEM(tmpbuf)) { + ILI_ERR("buffer vmalloc error\n"); + send_data_len += snprintf(tmpbufback + send_data_len, sizeof(tmpbufback), "buffer vmalloc error\n"); + ret = copy_to_user(buff, tmpbufback, send_data_len); /*ilits->dbl[0] */ + goto out; + } + + if (ilits->dbl[ilits->odi].mark) { + if (ilits->dbl[ilits->odi].data[0] == P5_X_DEMO_PACKET_ID) { + need_read_data_len = 43; + } else if (ilits->dbl[ilits->odi].data[0] == P5_X_DEMO_HIGH_RESOLUTION_PACKET_ID) { + need_read_data_len = P5_X_DEMO_MODE_PACKET_LEN_HIGH_RESOLUTION; + if (ilits->rib.nCustomerType != ilits->customertype_off) { + need_read_data_len += P5_X_CUSTOMER_LENGTH; + } + } else if (ilits->dbl[ilits->odi].data[0] == P5_X_I2CUART_PACKET_ID) { + type = ilits->dbl[ilits->odi].data[3] & 0x0F; + + data_count = ilits->dbl[ilits->odi].data[1] * ilits->dbl[ilits->odi].data[2]; + + if (type == 0 || type == 1 || type == 6) { + one_data_bytes = 1; + } else if (type == 2 || type == 3) { + one_data_bytes = 2; + } else if (type == 4 || type == 5) { + one_data_bytes = 4; + } + need_read_data_len = data_count * one_data_bytes + 1 + 5; + } else if (ilits->dbl[ilits->odi].data[0] == P5_X_DEBUG_PACKET_ID || ilits->dbl[ilits->odi].data[0] == P5_X_DEBUG_LITE_PACKET_ID) { + send_data_len = 0; /* ilits->dbl[0][1] - 2; */ + need_read_data_len = TR_BUF_SIZE - 8; + } else if (ilits->dbl[ilits->odi].data[0] == P5_X_I2CUART_PACKET_ID) { + need_read_data_len = P5_X_DEMO_MODE_PACKET_LEN+P5_X_DEMO_MODE_AXIS_LEN+P5_X_DEMO_MODE_STATE_INFO; + } else if (ilits->dbl[ilits->odi].data[0] == P5_X_DEBUG_HIGH_RESOLUTION_PACKET_ID) { + send_data_len = 0; /* ilits->dbl[0][1] - 2; */ + need_read_data_len = TR_BUF_SIZE - 8; + } + + for (i = 0; i < need_read_data_len; i++) { + send_data_len += snprintf(tmpbuf + send_data_len, sizeof(tmpbufback), "%02X", ilits->dbl[ilits->odi].data[i]); + if (send_data_len >= 4096) { + ILI_ERR("send_data_len = %d set 4096 i = %d\n", send_data_len, i); + send_data_len = 4096; + break; + } + } + + send_data_len += snprintf(tmpbuf + send_data_len, sizeof(tmpbufback), "\n\n"); + + if (p == 5 || size == 4096 || size == 2048) { + ilits->all_frame_full = false; + ilits->dbl[ilits->odi].mark = false; + ilits->odi = ((ilits->odi + 1) % TR_BUF_LIST_SIZE); + } + } + + /* Preparing to send debug data to user */ + if (size == 4096) + ret = copy_to_user(buff, tmpbuf, send_data_len); + else + ret = copy_to_user(buff, tmpbuf + p, send_data_len - p); + + /* ILI_ERR("send_data_len = %d\n", send_data_len); */ + if (send_data_len <= 0 || send_data_len > 4096) { + ILI_ERR("send_data_len = %d set 4096\n", send_data_len); + send_data_len = 4096; + } + + if (ret) { + ILI_ERR("copy_to_user err\n"); + ret = -EFAULT; + } else { + *pos += send_data_len; + ret = send_data_len; + ILI_DBG("Read %d bytes(s) from %ld\n", send_data_len, p); + } + +out: + mutex_unlock(&ilits->debug_mutex); + mutex_unlock(&ilits->debug_read_mutex); + ipio_vfree((void **)&tmpbuf); + return send_data_len; +} +#endif + +static ssize_t ilitek_proc_get_debug_mode_data_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + int ret; + struct file_buffer csv; + u8 data_type = 0; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (*pos != 0) + return 0; + + /* initialize file */ + memset(csv.fname, 0, sizeof(csv.fname)); + snprintf(csv.fname, sizeof(csv.fname), "%s", DEBUG_DATA_FILE_PATH); + csv.flen = 0; + csv.wlen = 0; + csv.max_size = DEBUG_DATA_FILE_SIZE; + + csv.ptr = vmalloc(csv.max_size); + if (ERR_ALLOC_MEM(csv.ptr)) { + ILI_ERR("Failed to allocate CSV mem\n"); + goto out; + } + + /* save data to csv */ + ILI_INFO("Get Raw data %d frame\n", ilits->raw_count); + ILI_INFO("Get Delta data %d frame\n", ilits->delta_count); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "Get Raw data %d frame\n", ilits->raw_count); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "Get Delta data %d frame\n", ilits->delta_count); + + file_write(&csv, true); + + data_type = P5_X_FW_RAW_DATA_MODE; + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG, false, &data_type) < 0) { + ILI_ERR("Failed to set tp data length\n"); + goto out; + } + + /* get raw data */ + csv.wlen = 0; + memset(csv.ptr, 0, csv.max_size); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "\n\n=======Raw data======="); + file_write(&csv, false); + + ret = debug_mode_get_data(&csv, P5_X_FW_RAW_DATA_MODE, ilits->raw_count); + if (ret < 0) + goto out; + + /* get delta data */ + csv.wlen = 0; + memset(csv.ptr, 0, csv.max_size); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "\n\n=======Delta data======="); + file_write(&csv, false); + + data_type = P5_X_FW_SIGNAL_DATA_MODE; + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG, false, &data_type) < 0) { + ILI_ERR("Failed to set tp data length\n"); + goto out; + } + + ret = debug_mode_get_data(&csv, P5_X_FW_SIGNAL_DATA_MODE, ilits->delta_count); + if (ret < 0) + goto out; + + /* change to demo mode */ + data_type = P5_X_FW_SIGNAL_DATA_MODE; + if (ili_set_tp_data_len(DATA_FORMAT_DEMO, false, &data_type) < 0) + ILI_ERR("Failed to set tp data length\n"); + +out: + ipio_vfree((void **)&csv.ptr); + return 0; +} + +static ssize_t ilitek_proc_get_debug_mode_data_write(struct file *filp, const char *buff, size_t size, loff_t *pos) +{ + char *token = NULL, *cur = NULL; + char cmd[256] = {0}; + u8 temp[256] = {0}, count = 0; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if ((size - 1) > sizeof(cmd)) { + ILI_ERR("ERROR! input length is larger than local buffer\n"); + return -1; + } + + if (buff != NULL) { + if (copy_from_user(cmd, buff, size - 1)) { + ILI_INFO("Failed to copy data from user space\n"); + return -1; + } + } + + ILI_INFO("size = %d, cmd = %s\n", (int)size, cmd); + token = cur = cmd; + while ((token = strsep(&cur, ",")) != NULL) { + temp[count] = ili_str2hex(token); + ILI_INFO("temp[%d] = %d\n", count, temp[count]); + count++; + } + + ilits->raw_count = ((temp[0] << 8) | temp[1]); + ilits->delta_count = ((temp[2] << 8) | temp[3]); + ilits->bg_count = ((temp[4] << 8) | temp[5]); + + ILI_INFO("Raw_count = %d, Delta_count = %d, BG_count = %d\n", ilits->raw_count, ilits->delta_count, ilits->bg_count); + return size; +} + +#if GENERIC_KERNEL_IMAGE +static int ilitek_proc_mp_result_show(struct seq_file *m, void *v) +{ + if (ilits->mp_result== NULL) { + ILI_INFO("mp result is NULL\n"); + seq_printf(m, "mp result is NULL\n"); + return 0; + } + + if (m->size < 500 * K) { + m->count = m->size; + return 0; + } + + if (m->size < strlen(ilits->mp_result)) { + ILI_INFO("size = %d, mp_result = %d\n Unable to print all data\n", + (int) m->size, (int) strlen(ilits->mp_result)); + m->count = m->size; + return 0; + } + + seq_printf(m, "%s", ilits->mp_result); + return 0; +} +#endif + +static ssize_t ilitek_node_mp_lcm_on_test_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + int ret = 0, len = 2; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (*pos != 0) + return 0; + + ILI_INFO("Run MP test with LCM on\n"); + + mutex_lock(&ilits->touch_mutex); + + /* Create the directory for mp_test result */ + if ((dev_mkdir(CSV_LCM_ON_PATH, S_IRUGO | S_IWUSR)) != 0) + ILI_ERR("Failed to create directory for mp_test\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + ilits->mp_ret_len = 0; + + ret = ili_mp_test_handler(g_user_buf, ON); + ILI_INFO("MP TEST %s, Error code = %d\n", (ret < 0) ? "FAIL" : "PASS", ret); + + g_user_buf[0] = 3; + g_user_buf[1] = (ret < 0) ? -ret : ret; + len += ilits->mp_ret_len; + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "MP TEST %s\n", (ret < 0) ? "FAIL" : "PASS"); + if (g_user_buf[1] == EMP_MODE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to switch MP mode, abort!"); + } else if (g_user_buf[1] == EMP_FW_PROC) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "FW still upgrading, abort!"); + } else if (g_user_buf[1] == EMP_FORMUL_NULL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "MP formula is null, abort!"); + } else if (g_user_buf[1] == EMP_INI) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Not found ini file, abort!"); + } else if (g_user_buf[1] == EMP_NOMEM) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to allocated memory, abort!"); + } else if (g_user_buf[1] == EMP_PROTOCOL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Protocol version isn't matched, abort!"); + } else if (g_user_buf[1] == EMP_TIMING_INFO) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to get timing info, abort!"); + } else if (g_user_buf[1] == EMP_PARA_NULL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to get mp parameter, abort!"); + } + + if (copy_to_user((char *)buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_node_mp_lcm_off_test_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + int ret = 0, len = 2; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (*pos != 0) + return 0; + + ILI_INFO("Run MP test with LCM off\n"); + + mutex_lock(&ilits->touch_mutex); + + /* Create the directory for mp_test result */ + if ((dev_mkdir(CSV_LCM_OFF_PATH, S_IRUGO | S_IWUSR)) != 0) + ILI_ERR("Failed to create directory for mp_test\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + ilits->mp_ret_len = 0; + + ret = ili_mp_test_handler(g_user_buf, OFF); + ILI_INFO("MP TEST %s, Error code = %d\n", (ret < 0) ? "FAIL" : "PASS", ret); + + g_user_buf[0] = 3; + g_user_buf[1] = (ret < 0) ? -ret : ret; + len += ilits->mp_ret_len; + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "MP TEST %s\n", (ret < 0) ? "FAIL" : "PASS"); + if (g_user_buf[1] == EMP_MODE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to switch MP mode, abort!"); + } else if (g_user_buf[1] == EMP_FW_PROC) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "FW still upgrading, abort!"); + } else if (g_user_buf[1] == EMP_FORMUL_NULL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "MP formula is null, abort!"); + } else if (g_user_buf[1] == EMP_INI) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Not found ini file, abort!"); + } else if (g_user_buf[1] == EMP_NOMEM) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to allocated memory, abort!"); + } else if (g_user_buf[1] == EMP_PROTOCOL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Protocol version isn't matched, abort!"); + } else if (g_user_buf[1] == EMP_TIMING_INFO) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to get timing info, abort!"); + } else if (g_user_buf[1] == EMP_PARA_NULL) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to get mp parameter, abort!"); + } + + if (copy_to_user((char *)buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_node_ver_info_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + u32 len = 0; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (*pos != 0) + return 0; + + mutex_lock(&ilits->touch_mutex); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "CHIP ID = %x\n", ilits->chip->product_id); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "FW version = %d.%d.%d.%d\n", + ilits->chip->fw_ver >> 24, (ilits->chip->fw_ver >> 16) & 0xFF, + (ilits->chip->fw_ver >> 8) & 0xFF, ilits->chip->fw_ver & 0xFF); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "FW MP version = %d.%d.%d.%d\n", + ilits->chip->fw_mp_ver >> 24, (ilits->chip->fw_mp_ver >> 16) & 0xFF, + (ilits->chip->fw_mp_ver >> 8) & 0xFF, ilits->chip->fw_mp_ver & 0xFF); + + if (ilits->protocol->core_ver_len == P5_X_CORE_VER_THREE_LENGTH) + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "Core version = %d.%d.%d\n", + (ilits->chip->core_ver >> 24) & 0xFF, (ilits->chip->core_ver >> 16) & 0xFF, + (ilits->chip->core_ver >> 8) & 0xFF); + else + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "Core version = %d.%d.%d.%d\n", + (ilits->chip->core_ver >> 24) & 0xFF, (ilits->chip->core_ver >> 16) & 0xFF, + (ilits->chip->core_ver >> 8) & 0xFF, ilits->chip->core_ver & 0xFF); + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "Protocol version = %d.%d.%d\n", + ilits->protocol->ver >> 16, (ilits->protocol->ver >> 8) & 0xFF, + ilits->protocol->ver & 0xFF); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "Driver version = %s\n", DRIVER_VERSION); + + if (copy_to_user((char *)buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_node_change_list_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + u32 len = 0; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (*pos != 0) + return 0; + + mutex_lock(&ilits->touch_mutex); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "============= Change list ==============\n"); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "[Drive version] = %s\n", DRIVER_VERSION); + /* len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "[Patch] 202001-0001\n"); */ + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "========================================\n"); + + if (copy_to_user((char *)buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_proc_fw_process_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + u32 len = 0; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + len = snprintf(g_user_buf, USER_STR_BUFF * sizeof(unsigned char), "%02d\n", ilits->fw_update_stat); + + ILI_INFO("update status = %d\n", ilits->fw_update_stat); + + if (copy_to_user((char *)buff, &ilits->fw_update_stat, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos = len; + return len; +} + +static ssize_t ilitek_node_fw_upgrade_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + int ret = 0, len = 0; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (*pos != 0) + return 0; + + ILI_INFO("Prepar to upgarde firmware\n"); + + mutex_lock(&ilits->touch_mutex); + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + ilits->force_fw_update = ENABLE; + ilits->node_update = true; + + ret = ili_fw_upgrade_handler(NULL); + + ilits->node_update = false; + ilits->force_fw_update = DISABLE; + + ret = (ret < 0) ? -ret : ret; + + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%3d ", (ret == 0) ? 1 : ret); + + if (ret == 0) + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Upgrade firmware = PASS"); + else + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Upgrade firmware = FAIL"); + + /* Reason for fail */ + if (ret == EFW_CONVERT_FILE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to convert hex/ili file, abort!"); + } else if (ret == ENOMEM) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to allocate memory, abort!"); + } else if (ret == EFW_ICE_MODE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to operate ice mode, abort!"); + } else if (ret == EFW_CRC) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "CRC not matched, abort!"); + } else if (ret == EFW_REST) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to do reset, abort!"); + } else if (ret == EFW_ERASE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to erase flash, abort!"); + } else if (ret == EFW_PROGRAM) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to program flash, abort!"); + } else if (ret == EFW_INTERFACE) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to hex file interface no match, abort!"); + } + + if (copy_to_user((u32 *) buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + *pos += len; + mutex_unlock(&ilits->touch_mutex); + return len; +} + +static ssize_t ilitek_proc_debug_level_read(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + debug_en = !debug_en; + + ILI_INFO(" %s debug level = %x\n", debug_en ? "Enable" : "Disable", debug_en); + + size = snprintf(g_user_buf, USER_STR_BUFF * sizeof(unsigned char), "debug level : %s\n", debug_en ? "Enable" : "Disable"); + + *pos += size; + + if (copy_to_user((u32 *) buff, g_user_buf, size)) + ILI_ERR("Failed to copy data to user space\n"); + + return size; +} + +static ssize_t ilitek_proc_sram_test_info(struct file *filp, char __user *buff, size_t size, loff_t *pos) +{ + int len = 0, ret = 0; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (*pos != 0) + return 0; + + memset(g_user_buf, 0, USER_STR_BUFF * sizeof(unsigned char)); + + if (ilits->cascade_info_block.nNum != 0) { + ret = ili_cascade_reset_ctrl(TP_IC_WHOLE_RST_WITH_FLASH, true); + } else { + ret = ili_reset_ctrl(TP_IC_WHOLE_RST_WITH_FLASH); + } + + if (ret < 0) { + ILI_ERR("TP_IC_WHOLE_RST_WITH_FLASH Failed, ret = %d\n", ret); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "TP_IC_WHOLE_RST_WITH_FLASH Failed"); + } + + mdelay(30); + + ili_parsing_sram_param(); + + if (ilits->cascade_info_block.nNum != 0) { +#if (TDDI_INTERFACE == BUS_I2C) + ili_ice_mode_ctrl_by_mode(ENABLE, OFF, BOTH); +#else + ili_core_spi_setup(SPI_CLK_CASCADE_TABLE_NUM); + ili_set_bypass_mode(OFF); +#endif + } else { + ret = ili_ice_mode_ctrl(ENABLE, OFF); + } + + if (ret < 0) { + ILI_ERR("Failed to disable ICE mode, ret = %d\n", ret); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "Failed to disable ICE mode"); + } + + /* close FW SRAM power saving */ + /* 7807Q 9881N don't have to close it */ + if (ilits->sram_para.tpu_clock_en == ENABLE) { + ILI_INFO("close FW SRAM power saving\n"); + ili_ice_mode_write_by_mode(0x8003F, 0x8A, 1, BOTH); + ili_ice_mode_write_by_mode(0x8003C, 0x00, 1, BOTH); + } + + ret = ili_tddi_ic_sram_test(); + if (ret < 0) { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "SRAM TEST Failed"); + } else { + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "SRAM TEST Pass"); + } + + if (ilits->cascade_info_block.nNum != 0) { + ret = ili_cascade_reset_ctrl(TP_IC_WHOLE_RST_WITH_FLASH, false); +#if (TDDI_INTERFACE == BUS_I2C) + ili_ice_mode_ctrl_by_mode(ENABLE, OFF, BOTH); +#else + ili_set_bypass_mode(OFF); +#endif + } else { + ret = ili_reset_ctrl(TP_IC_WHOLE_RST_WITH_FLASH); + } + + if (ret < 0) { + ILI_ERR("TP_IC_WHOLE_RST_WITH_FLASH Failed, ret = %d\n", ret); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "TP_IC_WHOLE_RST_WITH_FLASH Failed"); + } + + mdelay(50); + + ret = ili_fw_upgrade_handler(NULL); + if (ret < 0) { + ILI_ERR("fw upgrade Failed, ret = %d\n", ret); + len += snprintf(g_user_buf + len, USER_STR_BUFF - len, "%s\n", "fw upgrade Failed"); + } + + if (copy_to_user((char *)buff, g_user_buf, len)) + ILI_ERR("Failed to copy data to user space\n"); + + *pos += len; + return len; +} + +int ili_get_tp_recore_ctrl(int data) +{ + int ret = 0; + + switch ((int)data) { + + case ENABLE_RECORD: + ILI_INFO("recore enable"); + ret = ili_ic_func_ctrl("tp_recore", 1); + mdelay(200); + break; + case DATA_RECORD: + mdelay(50); + ILI_INFO("Get data"); + ret = ili_ic_func_ctrl("tp_recore", 2); + if (ret < 0) { + ILI_ERR("cmd fail\n"); + goto out; + } + + ret = ili_get_tp_recore_data(ON); + if (ret < 0) + ILI_ERR("get data fail\n"); + + ILI_INFO("recore reset"); + ret = ili_ic_func_ctrl("tp_recore", 3); + if (ret < 0) { + ILI_ERR("cmd fail\n"); + goto out; + } + break; + case DISABLE_RECORD: + ILI_INFO("recore disable"); + ret = ili_ic_func_ctrl("tp_recore", 0); + break; + + } +out: + return ret; + +} + +int ili_get_tp_recore_data(bool mcu) +{ + u8 buf[8] = {0}, record_case = 0; + s8 index; + u16 *raw = NULL, *raw_ptr = NULL, frame_len = 0; + u32 base_addr = 0x20000, addr, len, i, fcnt; + struct record_state record_stat; + bool ice = atomic_read(&ilits->ice_stat); + + if (ilits->wrapper(NULL, 0, buf, sizeof(buf), OFF, OFF) < 0) { + ILI_ERR("Get info fail\n"); + return -1; + } + + addr = ((buf[0] << 8) | buf[1]) + base_addr; + len = ((buf[2] << 8) | buf[3]); + index = buf[4]; + fcnt = buf[5]; + record_case = buf[6]; + ipio_memcpy(&record_stat, &buf[7], 1, 1); + ILI_INFO("addr = 0x%x, len = %d, lndex = 0x%x, fram num = %d, record_case = 0x%x\n", + addr, len, index, fcnt, record_case); + ili_dump_data(buf, 8, sizeof(buf), 0, "all record bytes"); + + raw = kcalloc(len, sizeof(u8), GFP_ATOMIC); + if (ERR_ALLOC_MEM(raw)) { + ILI_ERR("Failed to allocate packet memory, %ld\n", PTR_ERR(raw)); + return -1; + } + + if (!ice) + ili_ice_mode_ctrl(ENABLE, ON); + + buf[0] = 0x25; + buf[3] = (char)((addr & 0x00FF0000) >> 16); + buf[2] = (char)((addr & 0x0000FF00) >> 8); + buf[1] = (char)((addr & 0x000000FF)); + + if (ilits->wrapper(buf, 4, NULL, 0, ON, OFF)) { + ILI_ERR("Failed to write iram data\n"); + return -ENODEV; + } + + if (ilits->wrapper(NULL, 0, (u8 *)raw, len, ON, OFF)) { + ILI_ERR("Failed to Read iram data\n"); + return -ENODEV; + } + + frame_len = (len / (fcnt * 2)); + for (i = 0; i < fcnt; i++) { + raw_ptr = raw + (index * frame_len); + + ili_dump_data(raw_ptr, 16, frame_len, ilits->xch_num, "recore_data"); + index--; + if (index < 0) + index = fcnt - 1; + } + + if (!ice) + ili_ice_mode_ctrl(DISABLE, ON); + + if (record_case == 2) { + ILI_INFO("tp_palm_stat = %d\n", record_stat.touch_palm_state_e); + ILI_INFO("app_an_stat = %d\n", record_stat.app_an_statu_e); + ILI_INFO("app_check_abnor = %d\n", record_stat.app_sys_check_bg_abnormal); + ILI_INFO("wrong_bg = %d\n", record_stat.g_b_wrong_bg); + } + + ipio_kfree((void **)&raw); + + return 0; +} + +void ili_gesture_fail_reason(bool enable) +{ + + u8 cmd[24] = {0}; + + /* set symbol */ + if (ili_ic_func_ctrl("knock_en", 0x8) < 0) + ILI_ERR("set symbol failed"); + + /* enable gesture fail reason */ + cmd[0] = 0x01; + cmd[1] = 0x0A; + cmd[2] = 0x10; + if (enable) + cmd[3] = 0x01; + else + cmd[3] = 0x00; + cmd[4] = 0xFF; + cmd[5] = 0xFF; + if ((ilits->wrapper(cmd, 6, NULL, 0, ON, OFF)) < 0) + ILI_ERR("enable gesture fail reason failed"); + + /* set gesture parameters */ + cmd[0] = 0x01; + cmd[1] = 0x0A; + cmd[2] = 0x12; + cmd[3] = 0x01; + memset(cmd + 4, 0xFF, 20); + if ((ilits->wrapper(cmd, 24, NULL, 0, ON, OFF)) < 0) + ILI_ERR("set gesture parameters failed"); + + /* get gesture parameters */ + cmd[0] = 0x01; + cmd[1] = 0x0A; + cmd[2] = 0x11; + cmd[3] = 0x01; + if ((ilits->wrapper(cmd, 4, NULL, 0, ON, OFF)) < 0) + ILI_ERR("get gesture parameters failed"); + +} + +int ili_tp_data_mode_ctrl(u8 *cmd) +{ + int ret = 0; +#if (TDDI_INTERFACE == BUS_SPI) + int check_debugLite = -1; +#endif + switch (cmd[0]) { + case AP_MODE: + if (ilits->tp_suspend) { + if (ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEMO, false, &cmd[1]) < 0) { + ILI_ERR("Failed to set demo len from gesture mode\n"); + ret = -ENOTTY; + } + } else { + if (ilits->actual_tp_mode == P5_X_FW_TEST_MODE) { + if (ili_switch_tp_mode(P5_X_FW_AP_MODE) < 0) { + ILI_ERR("Failed to switch demo mode\n"); + ret = -ENOTTY; + } + } else { + if (ili_set_tp_data_len(DATA_FORMAT_DEMO, false, &cmd[1]) < 0) { + ILI_ERR("Failed to switch demo mode\n"); + ret = -ENOTTY; + } + } + } + break; + case TEST_MODE: + if (ili_switch_tp_mode(P5_X_FW_TEST_MODE) < 0) { + ILI_ERR("Failed to switch test mode\n"); + ret = -ENOTTY; + } + break; + case DEBUG_MODE: + if (!ilits->tp_suspend && (ilits->actual_tp_mode != P5_X_FW_AP_MODE)) { + if (ili_switch_tp_mode(P5_X_FW_AP_MODE) < 0) { + ILI_ERR("Failed to switch Debug mode\n"); + ret = -ENOTTY; + break; + } + } + if (ilits->tp_suspend) { + if (ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEBUG, false, &cmd[1]) < 0) { + ILI_ERR("Failed to set debug len from gesture mode\n"); + ret = -ENOTTY; + } + } else { + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG, false, &cmd[1]) < 0) { + ILI_ERR("Failed to set debug len\n"); + ret = -ENOTTY; + } + } + break; +#if (TDDI_INTERFACE == BUS_I2C) + case DEBUG_LITE_ROI: + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG_LITE_ROI, false, &cmd[1]) < 0) { + ILI_ERR("Failed to set debug lite roi\n"); + ret = -ENOTTY; + } + break; + case DEBUG_LITE_WINDOW: + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG_LITE_WINDOW, false, &cmd[1]) < 0) { + ILI_ERR("Failed to set debug lite window\n"); + ret = -ENOTTY; + } + break; + case DEBUG_LITE_AREA: + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG_LITE_AREA, false, &cmd[1]) < 0) { + ILI_ERR("Failed to set debug lite area\n"); + ret = -ENOTTY; + } + break; + case DEBUG_LITE_PEN: + if (ilits->PenType == POSITION_PEN_TYPE_ON) { + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG_LITE_PEN, false, &cmd[1]) < 0) { + ILI_ERR("Failed to set debug lite pen\n"); + ret = -ENOTTY; + } + } + break; +#else + case DEBUG_LITE_ROI: + check_debugLite = ili_ic_check_debug_lite_support(DATA_FORMAT_DEBUG_LITE_ROI, false, &cmd[1]); + if ( check_debugLite == ENABLE) { + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG_LITE_ROI, false, &cmd[1]) < 0) { + ILI_ERR("Failed to set debug lite roi\n"); + ret = -ENOTTY; + } + } else if (check_debugLite == DISABLE){ + ILI_INFO("no support DEBUG LITE ROI mode, set to debug mode\n"); + cmd[0] = DEBUG_MODE; + ili_tp_data_mode_ctrl(cmd); + } else { + ILI_ERR("change DEBUG LITE ROI fail\n"); + } + + break; + case DEBUG_LITE_WINDOW: + check_debugLite = ili_ic_check_debug_lite_support(DATA_FORMAT_DEBUG_LITE_WINDOW, false, &cmd[1]); + if ( check_debugLite == ENABLE) { + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG_LITE_WINDOW, false, &cmd[1]) < 0) { + ILI_ERR("Failed to set debug lite window\n"); + ret = -ENOTTY; + } + } else if (check_debugLite == DISABLE){ + ILI_INFO("no support DEBUG LITE WINDOW mode, set to debug mode\n"); + cmd[0] = DEBUG_MODE; + ili_tp_data_mode_ctrl(cmd); + } else { + ILI_ERR("change DEBUG LITE WINDOW fail\n"); + } + break; + case DEBUG_LITE_AREA: + check_debugLite = ili_ic_check_debug_lite_support(DATA_FORMAT_DEBUG_LITE_AREA, false, &cmd[1]); + if ( check_debugLite == ENABLE) { + if (ili_set_tp_data_len(DATA_FORMAT_DEBUG_LITE_AREA, false, &cmd[1]) < 0) { + ILI_ERR("Failed to set debug lite area\n"); + ret = -ENOTTY; + } + } else if (check_debugLite == DISABLE){ + ILI_INFO("no support DEBUG LITE AREA mode, set to debug mode\n"); + cmd[0] = DEBUG_MODE; + ili_tp_data_mode_ctrl(cmd); + } else { + ILI_ERR("change DEBUG LITE AREA fail\n"); + } + break; + case DEBUG_LITE_PEN: + ILI_INFO("SPI no support debug lite pen\n"); + break; +#endif + case DEBUG_PEN_ONLY_MODE: + if (ilits->PenType == POSITION_PEN_TYPE_ON) { + ilits->pen_info.report_type = P5_X_ONLY_PEN_TYPE; + if (ili_set_pen_data_len(P5_X_NEW_CONTROL_FORMAT, DATA_FORMAT_DEBUG_PEN_CMD, cmd[1]) < 0) { + ILI_ERR("Failed to set debug pen, Data Type : 0x%X\n", cmd[1]); + ret = -ENOTTY; + } + } else { + ILI_INFO("no support debug pen only\n"); + } + break; + case DEBUG_HAND_ONLY_MODE: + ilits->pen_info.report_type = P5_X_ONLY_HAND_TYPE; + if (ili_set_pen_data_len(P5_X_NEW_CONTROL_FORMAT, DATA_FORMAT_DEBUG_HAND_CMD, cmd[1]) < 0) { + ILI_ERR("Failed to set debug hand data\n"); + ret = -ENOTTY; + } + break; + default: + ILI_ERR("Unknown TP mode ctrl\n"); + ret = -ENOTTY; + break; + } + ilits->tp_data_mode = cmd[0]; + + return ret; +} + +int ili_mp_lcm_off_env_ctrl(u8 lcm_on) +{ + int ret = 0; + ILI_INFO("set mp lcm off env to %d\n", lcm_on); + if (lcm_on == ENABLE) { + if (ENABLE_WQ_ESD) + ilits->esd_func_ctrl = ENABLE; + + /* enable esd recovery */ + ili_wq_ctrl(WQ_ESD, ENABLE); + } else { + /* disable esd recovery */ + ili_wq_ctrl(WQ_ESD, DISABLE); + + if (ENABLE_WQ_ESD) + ilits->esd_func_ctrl = DISABLE; + + /* enable gesture */ + ilits->gesture = ENABLE; + } + return ret; + +} + +static ssize_t ilitek_node_ioctl_write(struct file *filp, const char *buff, size_t size, loff_t *pos) +{ + int i, ret = 0, count = 0; + char cmd[512] = {0}; + char *token = NULL, *cur = NULL; + u8 temp[256] = {0}; + u32 *data = NULL; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if ((size - 1) > sizeof(cmd)) { + ILI_ERR("ERROR! input length is larger than local buffer\n"); + return -1; + } + + mutex_lock(&ilits->touch_mutex); + + if (buff != NULL) { + if (copy_from_user(cmd, buff, size - 1)) { + ILI_INFO("Failed to copy data from user space\n"); + return -1; + } + } + + ILI_INFO("size = %d, cmd = %s\n", (int)size, cmd); + + token = cur = cmd; + + data = kcalloc(512, sizeof(u32), GFP_KERNEL); + if (ERR_ALLOC_MEM(data)) { + ILI_ERR("Failed to allocate data mem\n"); + size = -1; + goto out; + } + + while ((token = strsep(&cur, ",")) != NULL) { + data[count] = ili_str2hex(token); + ILI_INFO("data[%d] = %x\n", count, data[count]); + count++; + } + + ILI_INFO("cmd = %s\n", cmd); + + if (strncmp(cmd, "hwreset", strlen(cmd)) == 0) { + if (ilits->cascade_info_block.nNum != 0) { + ili_cascade_reset_ctrl(TP_HW_RST_ONLY, true); + } else { + ili_reset_ctrl(TP_HW_RST_ONLY); + } + } else if (strcmp(cmd, "rawdatarecore") == 0) { + if (data[1] == ENABLE_RECORD) + ili_get_tp_recore_ctrl(ENABLE_RECORD); + else if (data[1] == DATA_RECORD) + ili_get_tp_recore_ctrl(DATA_RECORD); + else if (data[1] == DISABLE_RECORD) + ili_get_tp_recore_ctrl(DISABLE_RECORD); + } else if (strcmp(cmd, "switchdemodebuginfomode") == 0) { + u8 data_type; + data_type = data[1] & 0xFF; + ili_set_tp_data_len(DATA_FORMAT_DEMO_DEBUG_INFO, false, &data_type); + } else if (strcmp(cmd, "gesturedemoen") == 0) { + u8 data_type; + data_type = data[2] & 0xFF; + if (data[1] == DISABLE) + ilits->gesture_demo_ctrl = DISABLE; + else + ilits->gesture_demo_ctrl = ENABLE; + ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEMO, false, &data_type); + } else if (strcmp(cmd, "gesturefailrsn") == 0) { + if (data[1] == DISABLE) + ili_gesture_fail_reason(DISABLE); + else + ili_gesture_fail_reason(ENABLE); + ILI_INFO("%s gesture fail reason\n", data[1] ? "ENABLE" : "DISABLE"); + } else if (strncmp(cmd, "icwholereset", strlen(cmd)) == 0) { + if (ilits->cascade_info_block.nNum != 0) { + ili_cascade_reset_ctrl(TP_IC_WHOLE_RST_WITH_FLASH, true); + } else { + ili_ice_mode_ctrl(ENABLE, OFF); + ili_reset_ctrl(TP_IC_WHOLE_RST_WITH_FLASH); + } + } else if (strncmp(cmd, "icwholeresetwithoutflash", strlen(cmd)) == 0) { + if (ilits->cascade_info_block.nNum != 0) { + ili_cascade_reset_ctrl(TP_IC_WHOLE_RST_WITHOUT_FLASH, true); + } else { + ili_ice_mode_ctrl(ENABLE, OFF); + ili_reset_ctrl(TP_IC_WHOLE_RST_WITHOUT_FLASH); + } + } else if (strncmp(cmd, "iccodereset", strlen(cmd)) == 0) { + if (ilits->cascade_info_block.nNum != 0) { + ili_cascade_reset_ctrl(TP_IC_CODE_RST, true); + } else { + ili_ice_mode_ctrl(ENABLE, OFF); + ili_reset_ctrl(TP_IC_CODE_RST); + ili_ice_mode_ctrl(DISABLE, OFF); + } + } else if (strncmp(cmd, "enableirq", strlen(cmd)) == 0) { + ILI_INFO("Enable IRQ\n"); + ili_irq_enable(); + } else if (strncmp(cmd, "disableirq", strlen(cmd)) == 0) { + ILI_INFO("Disable IRQ\n"); + ili_irq_disable(); + } else if (strncmp(cmd, "infofromhex", strlen(cmd)) == 0) { + ilits->info_from_hex = data[1]; + ILI_INFO("info from hex = %d\n", data[1]); + } else if (strncmp(cmd, "getinfo", strlen(cmd)) == 0) { +#if (TDDI_INTERFACE == BUS_I2C) + ilits->info_from_hex = DISABLE; +#endif + + if (ilits->cascade_info_block.nNum != 0) { + if (ili_cascade_ic_get_info(true, true) < 0) + ILI_ERR("Slave Chip info is incorrect\n"); + } else { + ili_ice_mode_ctrl(ENABLE, OFF); + ili_ic_get_info(); + ili_ice_mode_ctrl(DISABLE, OFF); + } + + ili_ic_get_all_info(); + ILI_INFO("Driver version = %s\n", DRIVER_VERSION); + ILI_INFO("TP module = %s\n", ilits->md_name); +#if (TDDI_INTERFACE == BUS_I2C) + ilits->info_from_hex = ENABLE; +#endif + } else if (strncmp(cmd, "enableicemode", strlen(cmd)) == 0) { + bool mcu; + u8 mode; + mcu = data[1] & BIT(0); + mode = data[2] & 0x0F; + + if (ilits->cascade_info_block.nNum != 0) { +#if (TDDI_INTERFACE == BUS_I2C) + ili_ice_mode_ctrl_by_mode(ENABLE, mcu, mode); +#else + if (mode == MASTER) { + ili_ice_mode_ctrl(ENABLE, mcu); + } else { + ili_set_bypass_mode(mcu); + } +#endif + } else { + ili_ice_mode_ctrl(ENABLE, mcu); + } + } else if (strncmp(cmd, "wqctrl", strlen(cmd)) == 0) { + ilits->wq_ctrl = !ilits->wq_ctrl; + ILI_INFO("wq_ctrl flag= %d\n", ilits->wq_ctrl); + } else if (strcmp(cmd, "spiwrslavereg") == 0 || strcmp(cmd, "slavectrl") == 0) { + ilits->slave_wr_ctrl = (data[1] == ENABLE) ? SLAVE: MASTER; + ILI_INFO(" %s slave write read ctrl = %x\n", ilits->slave_wr_ctrl ? "Enable" : "Disable", ilits->slave_wr_ctrl); + } + else if (strncmp(cmd, "file_write", strlen(cmd)) == 0) { + /*For Wacom Test Pen ID*/ + struct file_buffer csv; + + memset(csv.fname, 0, sizeof(csv.fname)); + snprintf(csv.fname, sizeof(csv.fname), "%s", PEN_ID_NODE_TEST_PATH); + csv.wlen = 0; + csv.max_size = TXT_FILE_SIZE; + + csv.ptr = vmalloc(csv.max_size); + if (ERR_ALLOC_MEM(csv.ptr)) { + ILI_ERR("Failed to allocate CSV mem\n"); + ipio_vfree((void **)&csv.ptr); + } + + memset(csv.ptr, 0, csv.max_size); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "PEN_ID = 0x"); + for(i = 0; i < P5_X_PEN_ID_LEN; i++) { + ILI_INFO("Pen ID[%d] = 0x%02X", i, ilits->pen_info.pen_id[P5_X_PEN_ID_LEN - 1 - i]); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "%02X", ilits->pen_info.pen_id[P5_X_PEN_ID_LEN - 1 - i]); + } + ILI_INFO("\n"); + csv.wlen += snprintf(csv.ptr + csv.wlen, (csv.max_size - csv.wlen), "\n"); + file_write(&csv, true); + + }else if (strncmp(cmd, "disableicemode", strlen(cmd)) == 0) { + bool mcu; + u8 mode; + mcu = data[1] & BIT(0); + mode = data[2] & 0x0F; + + if (ilits->cascade_info_block.nNum != 0) { +#if (TDDI_INTERFACE == BUS_I2C) + ili_ice_mode_ctrl_by_mode(DISABLE, mcu, mode); +#else + ili_ice_mode_ctrl(DISABLE, mcu); +#endif + } else { + ili_ice_mode_ctrl(DISABLE, mcu); + } + } else if (strncmp(cmd, "enablewqesd", strlen(cmd)) == 0) { + ili_wq_ctrl(WQ_ESD, ENABLE); + } else if (strncmp(cmd, "enablewqbat", strlen(cmd)) == 0) { + ili_wq_ctrl(WQ_BAT, ENABLE); + } else if (strncmp(cmd, "disablewqesd", strlen(cmd)) == 0) { + ili_wq_ctrl(WQ_ESD, DISABLE); + } else if (strncmp(cmd, "disablewqbat", strlen(cmd)) == 0) { + ili_wq_ctrl(WQ_BAT, DISABLE); + } else if (strncmp(cmd, "gesture", strlen(cmd)) == 0) { + ilits->gesture = !ilits->gesture; + ILI_INFO("gesture = %d\n", ilits->gesture); + } else if (strncmp(cmd, "gestureon", strlen(cmd)) == 0) { + ilits->gesture = ENABLE; + ILI_INFO("gestureon = %d\n", ilits->gesture); + } else if (strncmp(cmd, "gestureoff", strlen(cmd)) == 0) { + ilits->gesture = DISABLE; + ILI_INFO("gestureoff = %d\n", ilits->gesture); + } else if (strncmp(cmd, "esdgesture", strlen(cmd)) == 0) { + ilits->pll_clk_wakeup = true; + if (ili_gesture_recovery() < 0) { + ILI_ERR("Gesture recovery failed\n"); + size = -1; + } + } else if (strncmp(cmd, "esdspi", strlen(cmd)) == 0) { + ili_spi_recovery(); + } else if (strncmp(cmd, "sleepin", strlen(cmd)) == 0) { + ili_ic_func_ctrl("sleep", SLEEP_IN); + } else if (strncmp(cmd, "deepsleepin", strlen(cmd)) == 0) { + ili_ic_func_ctrl("sleep", DEEP_SLEEP_IN); + } else if (strncmp(cmd, "iceflag", strlen(cmd)) == 0) { + if (data[1] == ENABLE) + atomic_set(&ilits->ice_stat, ENABLE); + else + atomic_set(&ilits->ice_stat, DISABLE); + ILI_INFO("ice mode flag = %d\n", atomic_read(&ilits->ice_stat)); + } else if (strncmp(cmd, "gesturenormal", strlen(cmd)) == 0) { + ilits->gesture_mode = DATA_FORMAT_GESTURE_NORMAL; + ILI_INFO("gesture mode = %d\n", ilits->gesture_mode); + } else if (strncmp(cmd, "gestureinfo", strlen(cmd)) == 0) { + ilits->gesture_mode = DATA_FORMAT_GESTURE_INFO; + ILI_INFO("gesture mode = %d\n", ilits->gesture_mode); + } else if (strncmp(cmd, "switchtestmode", strlen(cmd)) == 0) { + ili_switch_tp_mode(P5_X_FW_TEST_MODE); + } else if (strncmp(cmd, "switchdebugmode", strlen(cmd)) == 0) { + u8 data_type; + data_type = data[1] & 0xFF; + ili_set_tp_data_len(DATA_FORMAT_DEBUG, false, &data_type); + } else if (strncmp(cmd, "switchdemomode", strlen(cmd)) == 0) { + u8 data_type; + data_type = data[1] & 0xFF; + ili_set_tp_data_len(DATA_FORMAT_DEMO, false, &data_type); + } else if (strncmp(cmd, "switchgesturedebugmode", strlen(cmd)) == 0) { + u8 data_type; + data_type = data[1] & 0xFF; + ili_set_tp_data_len(DATA_FORMAT_GESTURE_DEBUG, false, &data_type); + } else if (strncmp(cmd, "dbgflag", strlen(cmd)) == 0) { + ilits->dnp = !ilits->dnp; + ILI_INFO("debug flag message = %d\n", ilits->dnp); + } else if (strncmp(cmd, "spiclk", strlen(cmd)) == 0) { + ILI_INFO("spi clk num = %d\n", data[1]); + ili_core_spi_setup(data[1]); + } else if (strncmp(cmd, "ss", strlen(cmd)) == 0) { + ILI_INFO("sense_stop = %d\n", data[1]); + ilits->ss_ctrl = data[1]; + } else if (strncmp(cmd, "iow", strlen(cmd)) == 0) { + int w_len = 0; + w_len = data[1]; + ILI_INFO("w_len = %d\n", w_len); + for (i = 0; i < w_len; i++) { + temp[i] = data[2 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + + ilits->wrapper(temp, w_len, NULL, 0, ON, OFF); + } else if (strncmp(cmd, "ior", strlen(cmd)) == 0) { + int r_len = 0; + r_len = data[1]; + ILI_INFO("r_len = %d\n", r_len); + ilits->wrapper(NULL, 0, temp, r_len, ON, OFF); + for (i = 0; i < r_len; i++) + ILI_INFO("read[%d] = %x\n", i, temp[i]); + } else if (strncmp(cmd, "iowr", strlen(cmd)) == 0) { + int delay = 0; + int w_len = 0, r_len = 0; + w_len = data[1]; + r_len = data[2]; + delay = data[3]; + ILI_INFO("w_len = %d, r_len = %d, delay = %d\n", w_len, r_len, delay); + + for (i = 0; i < w_len; i++) { + temp[i] = data[4 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + ret = ilits->wrapper(temp, w_len, NULL, 0, OFF, OFF); + memset(temp, 0, sizeof(temp)); + mdelay(delay); + ilits->wrapper(NULL, 0, temp, r_len, OFF, OFF); + for (i = 0; i < r_len; i++) + ILI_INFO("read[%d] = %x\n", i, temp[i]); + } else if (strcmp(cmd, "iowrint") == 0) { + int delay = 0; + int w_len = 0, r_len = 0; + w_len = data[1]; + r_len = data[2]; + delay = data[3]; + ILI_INFO("w_len = %d, r_len = %d, delay = %d\n", w_len, r_len, delay); + for (i = 0; i < w_len; i++) { + temp[i] = data[4 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + ret = ilits->wrapper(temp, w_len, temp, r_len, ON, ON); + for (i = 0; i < r_len; i++) + ILI_INFO("read[%d] = %x\n", i, temp[i]); + } else if (strncmp(cmd, "getddiregdata", strlen(cmd)) == 0) { + u8 ddi_data = 0; + ILI_INFO("Get ddi reg one page: page = %x, reg = %x\n", data[1], data[2]); + if (ilits->cascade_info_block.nNum != 0) { + ili_ic_get_cascade_ddi_reg_onepage(data[1], data[2], &ddi_data, OFF); + } else { + ili_ic_get_ddi_reg_onepage(data[1], data[2], &ddi_data, OFF); + } + ILI_INFO("ddi_data = %x\n", ddi_data); + } else if (strncmp(cmd, "setddiregdata", strlen(cmd)) == 0) { + ILI_INFO("Set ddi reg one page: page = %x, reg = %x, data = %x\n", data[1], data[2], data[3]); + if (ilits->cascade_info_block.nNum != 0) { + ili_ic_set_cascade_ddi_reg_onepage(data[1], data[2], data[3], OFF); + } else { + ili_ic_set_ddi_reg_onepage(data[1], data[2], data[3], OFF); + } + } else if (strncmp(cmd, "dumpflashdata", strlen(cmd)) == 0) { + ilits->fw_update_stat = FW_STAT_INIT; + ILI_INFO("Start = 0x%x, End = 0x%x, Dump Hex path = %s\n", data[1], data[2], DUMP_FLASH_PATH); + ret = ili_fw_dump_flash_data(data[1], data[2], false, OFF); + ilits->fw_update_stat = (ret < 0) ? FW_UPDATE_FAIL : FW_UPDATE_PASS; + ILI_INFO("ilits->fw_update_stat = %d\n", ilits->fw_update_stat); + } else if (strncmp(cmd, "dumpiramdata", strlen(cmd)) == 0) { + ILI_INFO("Start = 0x%x, End = 0x%x, Dump IRAM path = %s\n", data[1], data[2], DUMP_IRAM_PATH); + ilits->fw_update_stat = FW_STAT_INIT; + ret = ili_fw_dump_iram_data(data[1], data[2], true, OFF); + ilits->fw_update_stat = (ret < 0) ? FW_UPDATE_FAIL : FW_UPDATE_PASS; + ILI_INFO("ilits->fw_update_stat = %d\n", ilits->fw_update_stat); + } else if (strncmp(cmd, "edge_palm_ctrl", strlen(cmd)) == 0) { + ili_ic_func_ctrl("edge_palm", data[1]); + } else if (strncmp(cmd, "uart_mode_ctrl", strlen(cmd)) == 0) { + ili_fw_uart_ctrl(data[1]); + } else if (strncmp(cmd, "flashesdgesture", strlen(cmd)) == 0) { + ili_touch_esd_gesture_flash(); + } else if (strncmp(cmd, "intact", strlen(cmd)) == 0) { + ILI_INFO("Set INT trigger = %x\n", data[1]); + ili_ic_int_trigger_ctrl(data[1]); + } else if (strncmp(cmd, "spiw", strlen(cmd)) == 0) { + int wlen; + wlen = data[1]; + temp[0] = 0x82; + for (i = 0; i < wlen; i++) { + temp[i] = data[2 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + ilits->spi_write_then_read(ilits->spi, temp, wlen, NULL, 0); + } else if (strncmp(cmd, "spir", strlen(cmd)) == 0) { + int rlen; + u8 *rbuf = NULL; + rlen = data[1]; + rbuf = kzalloc(rlen, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(rbuf)) { + ILI_ERR("Failed to allocate dma_rxbuf, %ld\n", PTR_ERR(rbuf)); + kfree(rbuf); + goto out; + } + temp[0] = 0x83; + ilits->spi_write_then_read(ilits->spi, temp, 1, rbuf, rlen); + for (i = 0; i < rlen; i++) + ILI_INFO("read[%d] = %x\n", i, rbuf[i]); + kfree(rbuf); + } else if (strncmp(cmd, "spirw", strlen(cmd)) == 0) { + int wlen, rlen; + u8 *rbuf = NULL; + wlen = data[1]; + rlen = data[2]; + for (i = 0; i < wlen; i++) { + temp[i] = data[3 + i]; + ILI_INFO("write[%d] = %x\n", i, temp[i]); + } + if (rlen != 0) { + rbuf = kzalloc(rlen, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(rbuf)) { + ILI_ERR("Failed to allocate dma_rxbuf, %ld\n", PTR_ERR(rbuf)); + kfree(rbuf); + goto out; + } + } + ilits->spi_write_then_read(ilits->spi, temp, wlen, rbuf, rlen); + if (rlen != 0) { + for (i = 0; i < rlen; i++) + ILI_INFO("read[%d] = %x\n", i, rbuf[i]); + } + kfree(rbuf); + } else if (strncmp(cmd, "ges_sym", strlen(cmd)) == 0) { + struct gesture_symbol *ptr_sym = &ilits->ges_sym; + u8 *ptr; + ptr = (u8 *) ptr_sym; + ptr[0] = data[1]; + ptr[1] = data[2]; + ptr[2] = data[3]; + ili_dump_data(ptr, 8, 3, 0, "Gesture symbol"); + } else if (strncmp(cmd, "mode", strlen(cmd)) == 0) { + temp[0] = data[1]; + temp[1] = data[2]; + temp[2] = data[3]; + temp[3] = data[4]; + temp[4] = data[5]; + temp[5] = data[6]; + ret = ili_tp_data_mode_ctrl(temp); + } else if (strncmp(cmd, "switchmpretry", strlen(cmd)) == 0) { + ilits->mp_retry = !ilits->mp_retry; + ILI_INFO("switchmpretry = %d\n", ilits->mp_retry); + } else if (strncmp(cmd, "edgepalmpara", strlen(cmd)) == 0) { + ilits->edge_palm_para[0] = P5_X_EDGE_PLAM_CTRL_1; + ilits->edge_palm_para[1] = P5_X_EDGE_PALM_TUNING_PARA; + for (i = 0; i < 14; i++) { + ilits->edge_palm_para[i * 2 + 2] = (data[i + 1] & 0xFF00) >> 8; + ilits->edge_palm_para[i * 2 + 3] = data[i + 1] & 0xFF; + } + ilits->edge_palm_para[30] = ili_calc_packet_checksum(ilits->edge_palm_para + , P5_X_EDGE_PALM_PARA_LENGTH - 1); + + for (i = 0; i < P5_X_EDGE_PALM_PARA_LENGTH; i++) { + ILI_INFO("edge_palm_para[%d] = 0x%2x\n", i, ilits->edge_palm_para[i]); + } + + ili_ic_send_edge_palm_para(); +#ifdef ROI + } else if (strncmp(cmd, "knuckle", strlen(cmd)) == 0) { + ilits->knuckle = !ilits->knuckle; + if (ilits->knuckle == true) { + ili_config_knuckle_roi_ctrl(CMD_ENABLE); + } else { + ili_config_knuckle_roi_ctrl(CMD_DISABLE); + } + ILI_INFO("knuckle = %s\n", ilits->knuckle ? "ENABLE" : "DISABLE"); +#endif + } else if (strncmp(cmd, "proxfacemode", strlen(cmd)) == 0) { + ilits->proxmity_face = false; + ilits->prox_face_mode = data[1] & 0xFF; + if (data[1] != 0x00) { + if (ilits->prox_face_mode == PROXIMITY_BACKLIGHT) { + data[1] = 0x01; + } + if (ili_ic_func_ctrl("proximity", data[1]) < 0) { + ILI_ERR("Write proximity cmd failed\n"); + } + } else { + if (ili_ic_func_ctrl("proximity", 0x00) < 0) { + ILI_ERR("Write proximity cmd failed\n"); + } + } + ilits->power_status = true; + ILI_INFO("switch proximity face mode = %d, level = %d\n", ilits->prox_face_mode, data[1]); + } else if (strncmp(cmd, "mp_lcm_off_env", strlen(cmd)) == 0) { + ili_mp_lcm_off_env_ctrl(data[1]); + } else if (strncmp(cmd, "releasetouch", strlen(cmd)) == 0) { + ILI_INFO("ioctl: release all touch\n"); + ili_touch_release_all_point(); + } else if (strncmp(cmd, "releasepen", strlen(cmd)) == 0) { + ILI_INFO("ioctl: release pen\n"); + ili_pen_release(); + } else { + ILI_ERR("Unknown command\n"); + size = -1; + } +out: + ipio_kfree((void **)&data); + mutex_unlock(&ilits->touch_mutex); + return size; +} + +#ifdef CONFIG_COMPAT +static long ilitek_node_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + long ret = 0; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (!filp->f_op || !filp->f_op->unlocked_ioctl) { + ILI_ERR("There's no unlocked_ioctl defined in file\n"); + return -ENOTTY; + } + + ILI_DBG("cmd = %d\n", _IOC_NR(cmd)); + + switch (cmd) { + case ILITEK_COMPAT_IOCTL_I2C_WRITE_DATA: + ILI_DBG("compat_ioctl: convert i2c/spi write\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_I2C_WRITE_DATA, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_I2C_READ_DATA: + ILI_DBG("compat_ioctl: convert i2c/spi read\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_I2C_READ_DATA, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_I2C_SET_WRITE_LENGTH: + ILI_DBG("compat_ioctl: convert set write length\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_I2C_SET_WRITE_LENGTH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_I2C_SET_READ_LENGTH: + ILI_DBG("compat_ioctl: convert set read length\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_I2C_SET_READ_LENGTH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_HW_RESET: + ILI_DBG("compat_ioctl: convert hw reset\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_HW_RESET, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_POWER_SWITCH: + ILI_DBG("compat_ioctl: convert power switch\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_POWER_SWITCH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_REPORT_SWITCH: + ILI_DBG("compat_ioctl: convert report switch\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_REPORT_SWITCH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_IRQ_SWITCH: + ILI_DBG("compat_ioctl: convert irq switch\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_IRQ_SWITCH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_DEBUG_LEVEL: + ILI_DBG("compat_ioctl: convert debug level\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_DEBUG_LEVEL, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_FUNC_MODE: + ILI_DBG("compat_ioctl: convert format mode\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_FUNC_MODE, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_FW_VER: + ILI_DBG("compat_ioctl: convert set read length\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_FW_VER, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_PL_VER: + ILI_DBG("compat_ioctl: convert fw version\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_PL_VER, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_CORE_VER: + ILI_DBG("compat_ioctl: convert core version\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_CORE_VER, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_DRV_VER: + ILI_DBG("compat_ioctl: convert driver version\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_DRV_VER, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_CHIP_ID: + ILI_DBG("compat_ioctl: convert chip id\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_CHIP_ID, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_MODE_CTRL: + ILI_DBG("compat_ioctl: convert tp mode ctrl\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_MODE_CTRL, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_MODE_STATUS: + ILI_DBG("compat_ioctl: convert tp mode status\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_MODE_STATUS, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_ICE_MODE_SWITCH: + ILI_DBG("compat_ioctl: convert tp mode switch\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_ICE_MODE_SWITCH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_INTERFACE_TYPE: + ILI_DBG("compat_ioctl: convert interface type\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_INTERFACE_TYPE, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_DUMP_FLASH: + ILI_DBG("compat_ioctl: convert dump flash\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_DUMP_FLASH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_FW_UART_CTRL: + ILI_DBG("compat_ioctl: convert fw uart\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_FW_UART_CTRL, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_PANEL_INFO: + ILI_DBG("compat_ioctl: convert resolution\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_PANEL_INFO, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_TP_INFO: + ILI_DBG("compat_ioctl: convert tp info\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_TP_INFO, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_WRAPPER_RW: + ILI_DBG("compat_ioctl: convert wrapper\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_WRAPPER_RW, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_DDI_WRITE: + ILI_DBG("compat_ioctl: convert ddi write\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_DDI_WRITE, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_DDI_READ: + ILI_DBG("compat_ioctl: convert ddi read\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_DDI_READ, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_REPORT_RATE_SET: + ILI_DBG("compat_ioctl: convert report rate set\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_REPORT_RATE_SET, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_MP_LCM_OFF_ENV: + ILI_DBG("compat_ioctl: mp lcm env\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_MP_LCM_OFF_ENV, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_RELEASE_TOUCH: + ILI_DBG("compat_ioctl: release touch\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_RELEASE_TOUCH, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_LCM_STATUS: + ILI_DBG("compat_ioctl: lcm status\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_LCM_STATUS, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_SLAVE_CTRL: + ILI_DBG("compat_ioctl: lcm status\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_SLAVE_CTRL, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_CASCADE_REG_RW: + ILI_DBG("compat_ioctl: lcm status\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_CASCADE_REG_RW, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_INTERFACE_GET: + ILI_DBG("compat_ioctl: get interface\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_INTERFACE_GET, (unsigned long)compat_ptr(arg)); + return ret; + case ILITEK_COMPAT_IOCTL_ICE_MODE_FLAG_SET: + ILI_DBG("compat_ioctl: ice mode flag set\n"); + ret = filp->f_op->unlocked_ioctl(filp, ILITEK_IOCTL_ICE_MODE_FLAG_SET, (unsigned long)compat_ptr(arg)); + return ret; + default: + ILI_ERR("no ioctl cmd, return ilitek_node_ioctl\n"); + return -ENOIOCTLCMD; + } +} +#endif + +static long ilitek_node_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + int ret = 0, length = 0; + u8 *szBuf = NULL, if_to_user = 0; + static u16 i2c_rw_length; + u32 id_to_user[64] = {0}; + bool esd_en = ilits->wq_esd_ctrl, bat_en = ilits->wq_bat_ctrl; + u8 *wrap_rbuf = NULL, wrap_int = 0; + u16 wrap_wlen = 0, wrap_rlen = 0; + bool spi_irq, i2c_irq; + bool read = 0; + u32 type, addr, read_data, write_data, write_len; + + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + if (_IOC_TYPE(cmd) != ILITEK_IOCTL_MAGIC) { + ILI_ERR("The Magic number doesn't match\n"); + return -ENOTTY; + } + + if (_IOC_NR(cmd) > ILITEK_IOCTL_MAXNR) { + ILI_ERR("The number of ioctl doesn't match\n"); + return -ENOTTY; + } + + ILI_DBG("cmd = %d\n", _IOC_NR(cmd)); + + mutex_lock(&ilits->touch_mutex); + + szBuf = kcalloc(IOCTL_I2C_BUFF, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(szBuf)) { + ILI_ERR("Failed to allocate mem\n"); + ret = -ENOMEM; + goto out; + } + + if (esd_en) + ili_wq_ctrl(WQ_ESD, DISABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, DISABLE); + + switch (cmd) { + case ILITEK_IOCTL_I2C_WRITE_DATA: + ILI_DBG("ioctl: write len = %d\n", i2c_rw_length); + + if (i2c_rw_length > IOCTL_I2C_BUFF) { + ILI_ERR("ERROR! write len is largn than ioctl buf (%d, %ld)\n", + i2c_rw_length, IOCTL_I2C_BUFF); + ret = -ENOTTY; + break; + } + + if (copy_from_user(szBuf, (u8 *) arg, i2c_rw_length)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + ret = ilits->wrapper(szBuf, i2c_rw_length, NULL, 0, OFF, OFF); + if (ret < 0) + ILI_ERR("Failed to write data\n"); + break; + case ILITEK_IOCTL_I2C_READ_DATA: + ILI_DBG("ioctl: read len = %d\n", i2c_rw_length); + + if (i2c_rw_length > IOCTL_I2C_BUFF) { + ILI_ERR("ERROR! read len is largn than ioctl buf (%d, %ld)\n", + i2c_rw_length, IOCTL_I2C_BUFF); + ret = -ENOTTY; + break; + } + + ret = ilits->wrapper(NULL, 0, szBuf, i2c_rw_length, OFF, OFF); + if (ret < 0) { + ILI_ERR("Failed to read data\n"); + break; + } + + if (copy_to_user((u8 *) arg, szBuf, i2c_rw_length)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_I2C_SET_WRITE_LENGTH: + case ILITEK_IOCTL_I2C_SET_READ_LENGTH: + i2c_rw_length = arg; + break; + case ILITEK_IOCTL_TP_HW_RESET: + ILI_DBG("ioctl: hw reset\n"); + if (ilits->cascade_info_block.nNum != 0) { + ili_wdt_reset_status(true, false); + } + ili_reset_ctrl(ilits->reset); + break; + case ILITEK_IOCTL_TP_POWER_SWITCH: + ILI_INFO("Not implemented yet\n"); + break; + case ILITEK_IOCTL_TP_REPORT_SWITCH: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + ILI_DBG("ioctl: report switch = %d\n", szBuf[0]); + if (szBuf[0]) { + ilits->report = ENABLE; + ILI_DBG("report is enabled\n"); + } else { + ilits->report = DISABLE; + ILI_DBG("report is disabled\n"); + } + break; + case ILITEK_IOCTL_TP_IRQ_SWITCH: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: irq switch = %d\n", szBuf[0]); + if (szBuf[0]) + ili_irq_enable(); + else + ili_irq_disable(); + break; + case ILITEK_IOCTL_TP_DEBUG_LEVEL: + if (copy_from_user(szBuf, (u8 *) arg, sizeof(u32))) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + debug_en = !debug_en; + ILI_DBG("debug_en = %d", debug_en); + break; + case ILITEK_IOCTL_TP_FUNC_MODE: + if (copy_from_user(szBuf, (u8 *) arg, 3)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + ILI_DBG("ioctl: set func mode = %x,%x,%x\n", szBuf[0], szBuf[1], szBuf[2]); + ret = ilits->wrapper(szBuf, 3, NULL, 0, ON, OFF); + break; + case ILITEK_IOCTL_TP_FW_VER: + ILI_DBG("ioctl: get fw and mp version\n"); + szBuf[7] = ilits->chip->fw_mp_ver & 0xFF; + szBuf[6] = (ilits->chip->fw_mp_ver >> 8) & 0xFF; + szBuf[5] = (ilits->chip->fw_mp_ver >> 16) & 0xFF; + szBuf[4] = ilits->chip->fw_mp_ver >> 24; + szBuf[3] = ilits->chip->fw_ver & 0xFF; + szBuf[2] = (ilits->chip->fw_ver >> 8) & 0xFF; + szBuf[1] = (ilits->chip->fw_ver >> 16) & 0xFF; + szBuf[0] = ilits->chip->fw_ver >> 24; + ILI_DBG("Firmware version = %d.%d.%d.%d\n", szBuf[0], szBuf[1], szBuf[2], szBuf[3]); + ILI_DBG("Firmware MP version = %d.%d.%d.%d\n", szBuf[4], szBuf[5], szBuf[6], szBuf[7]); + + if (copy_to_user((u8 *) arg, szBuf, 8)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_PL_VER: + ILI_DBG("ioctl: get protocl version\n"); + szBuf[2] = ilits->protocol->ver & 0xFF; + szBuf[1] = (ilits->protocol->ver >> 8) & 0xFF; + szBuf[0] = ilits->protocol->ver >> 16; + ILI_DBG("Protocol version = %d.%d.%d\n", szBuf[0], szBuf[1], szBuf[2]); + + if (copy_to_user((u8 *) arg, szBuf, 3)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_CORE_VER: + ILI_DBG("ioctl: get core version\n"); + if (ilits->protocol->core_ver_len == P5_X_CORE_VER_THREE_LENGTH) + szBuf[3] = 0xFF; + else + szBuf[3] = ilits->chip->core_ver & 0xFF; + szBuf[2] = (ilits->chip->core_ver >> 8) & 0xFF; + szBuf[1] = (ilits->chip->core_ver >> 16) & 0xFF; + szBuf[0] = ilits->chip->core_ver >> 24; + ILI_DBG("Core version = %d.%d.%d.%d, length = %d\n", + szBuf[0], szBuf[1], szBuf[2], szBuf[3], ilits->protocol->core_ver_len); + + if (copy_to_user((u8 *) arg, szBuf, 4)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_DRV_VER: + ILI_DBG("ioctl: get driver version\n"); + length = snprintf(szBuf, USER_STR_BUFF * sizeof(unsigned char), "%s", DRIVER_VERSION); + + if (copy_to_user((u8 *) arg, szBuf, length)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_CHIP_ID: + ILI_DBG("ioctl: get chip id\n"); + id_to_user[0] = (ilits->chip->product_id << 16) | (ilits->chip->pid & 0x0000FFFF); + id_to_user[1] = ilits->chip->otp_id; + id_to_user[2] = ilits->chip->ana_id; + + if (copy_to_user((u32 *) arg, id_to_user, sizeof(u32) * 3)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_MODE_CTRL: + if (copy_from_user(szBuf, (u8 *) arg, 10)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: switch fw format = %d\n", szBuf[0]); + ret = ili_tp_data_mode_ctrl(szBuf); + + break; + case ILITEK_IOCTL_TP_MODE_STATUS: + ILI_DBG("ioctl: current firmware mode = %d", ilits->tp_data_mode); + if (copy_to_user((int *)arg, &ilits->tp_data_mode, sizeof(int))) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + /* It works for host downloado only */ + case ILITEK_IOCTL_ICE_MODE_SWITCH: + if (copy_from_user(szBuf, (u8 *) arg, 3)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + ILI_DBG("ioctl: switch ice mode = %d, mcu on = %d\n", szBuf[0], szBuf[1]); + + if (ilits->cascade_info_block.nNum != 0) { +#if (TDDI_INTERFACE == BUS_I2C) + if (szBuf[2] == 0) { + /* for wifi daemon flow */ + atomic_set(&ilits->ice_stat, (szBuf[0] == ENABLE) ? DISABLE : ENABLE); + + ili_ice_mode_ctrl_by_mode(szBuf[0] ? ENABLE : DISABLE, + szBuf[1] ? ENABLE : DISABLE, ilits->slave_wr_ctrl); + } else { + ILI_DBG("ioctl: mode = %d\n", szBuf[2]); + + ili_ice_mode_ctrl_by_mode(szBuf[0] ? ENABLE : DISABLE, + szBuf[1] ? ENABLE : DISABLE, szBuf[2]); + } +#elif (TDDI_INTERFACE == BUS_SPI) + if (ilits->slave_wr_ctrl == ENABLE && szBuf[0] == ENABLE) { + ili_set_bypass_mode(szBuf[1]); + } + + /* for wifi daemon flow */ + if (szBuf[0] == ENABLE) + atomic_set(&ilits->ice_stat, DISABLE); + + if (ili_ice_mode_ctrl(szBuf[0] ? ENABLE : DISABLE, + szBuf[1] ? ENABLE : DISABLE) < 0) + ILI_ERR("ioctl: set ice mode failed\n"); +#endif + } else { + if (ili_ice_mode_ctrl(szBuf[0] ? ENABLE : DISABLE, + szBuf[1] ? ENABLE : DISABLE) < 0) + ILI_ERR("ioctl: set ice mode failed\n"); + } + break; + case ILITEK_IOCTL_TP_INTERFACE_TYPE: + if_to_user = ilits->hwif->bus_type; + if (copy_to_user((u8 *) arg, &if_to_user, sizeof(if_to_user))) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_DUMP_FLASH: + ILI_DBG("ioctl: dump flash data\n"); + ret = ili_fw_dump_flash_data(0, 0, true, OFF); + if (ret < 0) { + ILI_ERR("ioctl: Failed to dump flash data\n"); + } + break; + case ILITEK_IOCTL_TP_FW_UART_CTRL: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: fw UART = %d\n", szBuf[0]); + + ili_fw_uart_ctrl(szBuf[0]); + + if_to_user = szBuf[0]; + + if (copy_to_user((u8 *) arg, &if_to_user, sizeof(if_to_user))) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_PANEL_INFO: + ILI_DBG("ioctl: get panel resolution\n"); + id_to_user[0] = ilits->panel_wid; + id_to_user[1] = ilits->panel_hei; + id_to_user[2] = (ilits->trans_xy) ? 0x1 : 0x0; + + if (copy_to_user((u32 *) arg, id_to_user, sizeof(u32) * 3)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_TP_INFO: + ILI_DBG("ioctl: get tp info\n"); + id_to_user[0] = ilits->min_x; + id_to_user[1] = ilits->min_y; + id_to_user[2] = ilits->max_x; + id_to_user[3] = ilits->max_y; + id_to_user[4] = ilits->xch_num; + id_to_user[5] = ilits->ych_num; + id_to_user[6] = ilits->stx; + id_to_user[7] = ilits->srx; + + if (copy_to_user((u32 *) arg, id_to_user, sizeof(u32) * 8)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_WRAPPER_RW: + ILI_DBG("ioctl: wrapper rw\n"); + + if (i2c_rw_length > IOCTL_I2C_BUFF || i2c_rw_length == 0) { + ILI_ERR("ERROR! i2c_rw_length is is invalid\n"); + ret = -ENOTTY; + break; + } + + if (copy_from_user(szBuf, (u8 *) arg, i2c_rw_length)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + wrap_int = szBuf[0]; + wrap_rlen = (szBuf[1] << 8) | szBuf[2]; + wrap_wlen = (szBuf[3] << 8) | szBuf[4]; + + ILI_DBG("wrap_int = %d\n", wrap_int); + ILI_DBG("wrap_rlen = %d\n", wrap_rlen); + ILI_DBG("wrap_wlen = %d\n", wrap_wlen); + + if (wrap_wlen > IOCTL_I2C_BUFF || wrap_rlen > IOCTL_I2C_BUFF) { + ILI_ERR("ERROR! R/W len is largn than ioctl buf\n"); + ret = -ENOTTY; + break; + } + + if (wrap_rlen > 0) { + wrap_rbuf = kcalloc(IOCTL_I2C_BUFF, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(wrap_rbuf)) { + ILI_ERR("Failed to allocate mem\n"); + ret = -ENOMEM; + break; + } + } + + if (wrap_int == 1) { + i2c_irq = ON; + spi_irq = ON; + } else if (wrap_int == 2) { + i2c_irq = OFF; + spi_irq = OFF; + } else { + i2c_irq = OFF; + spi_irq = (wrap_rlen > 0 ? ON : OFF); + } + + ILI_DBG("i2c_irq = %d, spi_irq = %d\n", i2c_irq, spi_irq); + + ilits->wrapper(szBuf + 5, wrap_wlen, wrap_rbuf, wrap_rlen, spi_irq, i2c_irq); + + ili_dump_data(szBuf + 5, 8, wrap_wlen, 16, "wrap_wbuf:"); + ili_dump_data(wrap_rbuf, 8, wrap_rlen, 16, "wrap_rbuf:"); + if (copy_to_user((u8 *) arg, wrap_rbuf, wrap_rlen)) { + ILI_ERR("Failed to copy driver ver to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_DDI_WRITE: + if (copy_from_user(szBuf, (u8 *) arg, 3)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: page = %x, reg = %x, data = %x\n", szBuf[0], szBuf[1], szBuf[2]); + ili_ic_set_ddi_reg_onepage(szBuf[0], szBuf[1], szBuf[2], OFF); + break; + case ILITEK_IOCTL_DDI_READ: + if (copy_from_user(szBuf, (u8 *) arg, 2)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_DBG("ioctl: page = %x, reg = %x\n", szBuf[0], szBuf[1]); + ili_ic_get_ddi_reg_onepage(szBuf[0], szBuf[1], &szBuf[2], OFF); + ILI_DBG("ioctl: data = %x\n", szBuf[2]); + if (copy_to_user((u8 *) arg, szBuf + 2, 1)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_REPORT_RATE_SET: + if (copy_from_user(szBuf, (u8 *) arg, 2)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ILI_INFO("ioctl: set report rate mode = %x\n", szBuf[0]); + if (ili_ic_report_rate_set(szBuf[0]) < 0) { + ILI_ERR("Failed to set report rate\n"); + ret = -ENOTTY; + } + + break; + case ILITEK_IOCTL_REPORT_RATE_GET: + + szBuf[0] = ili_ic_report_rate_get(); + if (copy_to_user((u8 *) arg, szBuf, 1)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_MP_LCM_OFF_ENV: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + ili_mp_lcm_off_env_ctrl(szBuf[0]); + break; + case ILITEK_IOCTL_RELEASE_TOUCH: + ILI_INFO("ioctl: release all touch\n"); + ili_touch_release_all_point(); + break; + case ILITEK_IOCTL_RELEASE_PEN: + ILI_INFO("ioctl: release pen\n"); + ili_pen_release(); + break; + case ILITEK_IOCTL_LCM_STATUS: + ILI_INFO("ioctl: lcm status is %d\n", ilits->power_status); + szBuf[0] = ilits->power_status; + if (copy_to_user((u8 *) arg, szBuf, 1)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_SLAVE_CTRL: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + ilits->slave_wr_ctrl = (szBuf[0] == ENABLE) ? ENABLE : DISABLE; + ILI_DBG(" %s slave write read ctrl = %x\n", ilits->slave_wr_ctrl ? "Enable" : "Disable", ilits->slave_wr_ctrl); +#if (TDDI_INTERFACE == BUS_I2C && ENABLE_CASCADE) + ilits->i2c_addr_change = (ilits->slave_wr_ctrl == SLAVE) ? SLAVE : MASTER; +#endif + break; + case ILITEK_IOCTL_CASCADE_REG_RW: + ILI_DBG("ioctl: cascade register rw\n"); + + if (copy_from_user(szBuf, (u8 *) arg, i2c_rw_length)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + type = szBuf[0]; + addr = (szBuf[1] << 24) + (szBuf[2] << 16) + (szBuf[3] << 8) + (szBuf[4]); + write_data = (szBuf[5] << 24) + (szBuf[6] << 16) + (szBuf[7] << 8) + (szBuf[8]); + write_len = szBuf[9]; + + ILI_DBG("type = %d\n", type); + ILI_DBG("addr = 0x%X\n", addr); + ILI_DBG("write_data = %X\n", write_data); + ILI_DBG("write_len = %d\n", write_len); + +#if (TDDI_INTERFACE == BUS_I2C) + if (type == read) { + ili_ice_mode_read_by_mode(addr, &read_data, sizeof(u32), ilits->slave_wr_ctrl); + + szBuf[0] = (read_data >> 24) & 0xFF; + szBuf[1] = (read_data >> 16) & 0xFF; + szBuf[2] = (read_data >> 8) & 0xFF; + szBuf[3] = read_data & 0xFF; + if (copy_to_user((u8 *) arg, szBuf, 4)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + } else { + ili_ice_mode_write_by_mode(addr, write_data, write_len, ilits->slave_wr_ctrl); + } +#else + if (type == read) { + if (ilits->slave_wr_ctrl == SLAVE) { + ili_mspi_read_slave_register(addr, &read_data, sizeof(u32)); + ili_ice_mode_write_by_mode(MSPI_REG, 0x00, 1, BOTH); + } else { + if (ili_ice_mode_read_by_mode(addr, &read_data, sizeof(u32), MASTER) < 0) { + ILI_ERR("Read data error\n"); + } + } + ILI_INFO("[READ]:addr = 0x%06x, read = 0x%08x\n", addr, read_data); + szBuf[0] = (read_data >> 24) & 0xFF; + szBuf[1] = (read_data >> 16) & 0xFF; + szBuf[2] = (read_data >> 8) & 0xFF; + szBuf[3] = read_data & 0xFF; + if (copy_to_user((u8 *) arg, szBuf, 4)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + } else { + if (ilits->slave_wr_ctrl == SLAVE) { + ili_mspi_write_slave_register(addr, write_data, write_len); + ili_ice_mode_write_by_mode(MSPI_REG, 0x00, 1, BOTH); + } else { + if (ili_ice_mode_write_by_mode(addr, write_data, write_len, MASTER) < 0) { + ILI_ERR("Write data error\n"); + } + } + ILI_INFO("[WRITE]:addr = 0x%06x, write = 0x%08x, len = %d byte\n", addr, write_data, write_len); + } + + break; +#endif + case ILITEK_IOCTL_INTERFACE_GET: +#if (TDDI_INTERFACE == BUS_I2C) + szBuf[0] = 0x0; +#else + szBuf[0] = 0x1; +#endif + ILI_INFO("ioctl: get interface is %d\n", szBuf[0]); + if (copy_to_user((u8 *) arg, szBuf, 1)) { + ILI_ERR("Failed to copy data to user space\n"); + ret = -ENOTTY; + } + break; + case ILITEK_IOCTL_ICE_MODE_FLAG_SET: + if (copy_from_user(szBuf, (u8 *) arg, 1)) { + ILI_ERR("Failed to copy data from user space\n"); + ret = -ENOTTY; + break; + } + + ILI_DBG("ioctl: set ice mode flag = %d\n", szBuf[0]); + + if (szBuf[0] ? ENABLE : DISABLE) { + atomic_set(&ilits->ice_stat, ENABLE); + ilits->pll_clk_wakeup = false; + } else { + atomic_set(&ilits->ice_stat, DISABLE); + ilits->pll_clk_wakeup = true; + } + default: + ret = -ENOTTY; + break; + } + + ipio_kfree((void **)&szBuf); + ipio_kfree((void **)&wrap_rbuf); + +out: + if (esd_en) + ili_wq_ctrl(WQ_ESD, ENABLE); + if (bat_en) + ili_wq_ctrl(WQ_BAT, ENABLE); + + mutex_unlock(&ilits->touch_mutex); + return ret; +} + +static struct proc_dir_entry *proc_dir_ilitek; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +typedef struct { + char *name; + struct proc_dir_entry *node; + struct proc_ops *fops; + bool isCreated; +} proc_node; +#else +typedef struct { + char *name; + struct proc_dir_entry *node; + struct file_operations *fops; + bool isCreated; +} proc_node; +#endif + +#if GENERIC_KERNEL_IMAGE +static int proc_show_mp_test_result_read(struct inode *inode, struct file *file) +{ + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + return single_open(file, ilitek_proc_mp_result_show, NULL); +} + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_show_mp_test_result_fops = { + .proc_open = proc_show_mp_test_result_read, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +#else +static struct file_operations proc_show_mp_test_result_fops = { + .open = proc_show_mp_test_result_read, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_mp_lcm_on_test_fops = { + .proc_read = ilitek_node_mp_lcm_on_test_read, + .proc_lseek = default_llseek, +}; +#else +static struct file_operations proc_mp_lcm_on_test_fops = { + .read = ilitek_node_mp_lcm_on_test_read, + .llseek = default_llseek, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_mp_lcm_off_test_fops = { + .proc_read = ilitek_node_mp_lcm_off_test_read, + .proc_lseek = default_llseek, +}; +#else +static struct file_operations proc_mp_lcm_off_test_fops = { + .read = ilitek_node_mp_lcm_off_test_read, + .llseek = default_llseek, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_ver_info_fops = { + .proc_read = ilitek_node_ver_info_read, + .proc_lseek = default_llseek, +}; +#else +static struct file_operations proc_ver_info_fops = { + .read = ilitek_node_ver_info_read, + .llseek = default_llseek, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_change_list_fops = { + .proc_read = ilitek_node_change_list_read, + .proc_lseek = default_llseek, +}; +#else +static struct file_operations proc_change_list_fops = { + .read = ilitek_node_change_list_read, + .llseek = default_llseek, +}; +#endif + +#if DMESG_SEQ_FILE +static int ilitek_proc_seq_debug_message_read(struct inode *inode, struct file *file) +{ + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + return single_open(file, ilitek_proc_debug_message_show, NULL); +} + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_debug_message_fops = { + .proc_open = ilitek_proc_seq_debug_message_read, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +#else +static struct file_operations proc_debug_message_fops = { + .open = ilitek_proc_seq_debug_message_read, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +#else +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_debug_message_fops = { + .proc_read = ilitek_proc_debug_message_read, + .proc_lseek = default_llseek, +}; +#else +static struct file_operations proc_debug_message_fops = { + .read = ilitek_proc_debug_message_read, + .llseek = default_llseek, +}; +#endif +#endif + +static int ilitek_proc_seq_debug_all_message_read(struct inode *inode, struct file *file) +{ + if (atomic_read(&ilits->tp_reset) == START) { + ILI_ERR("ignore request! tp reset atomic is START.\n"); + return -EINVAL; + } + + return single_open(file, ilitek_proc_debug_all_message_show, NULL); +} + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_debug_all_message_fops = { + .proc_open = ilitek_proc_seq_debug_all_message_read, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +#else +static struct file_operations proc_debug_all_message_fops = { + .open = ilitek_proc_seq_debug_all_message_read, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_debug_all_message_switch_fops = { + .proc_read = ilitek_proc_debug_all_switch_read, + .proc_lseek = default_llseek, +}; +#else +static struct file_operations proc_debug_all_message_switch_fops = { + .read = ilitek_proc_debug_all_switch_read, + .llseek = default_llseek, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_debug_message_switch_fops = { + .proc_read = ilitek_proc_debug_switch_read, + .proc_lseek = default_llseek, +}; +#else +static struct file_operations proc_debug_message_switch_fops = { + .read = ilitek_proc_debug_switch_read, + .llseek = default_llseek, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_ioctl_fops = { + .proc_ioctl = ilitek_node_ioctl, +#ifdef CONFIG_COMPAT + .proc_compat_ioctl = ilitek_node_compat_ioctl, +#endif + .proc_write = ilitek_node_ioctl_write, + .proc_lseek = default_llseek, +}; +#else +static struct file_operations proc_ioctl_fops = { + .unlocked_ioctl = ilitek_node_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ilitek_node_compat_ioctl, +#endif + .write = ilitek_node_ioctl_write, + .llseek = default_llseek, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_fw_upgrade_fops = { + .proc_read = ilitek_node_fw_upgrade_read, + .proc_lseek = default_llseek, +}; +#else +static struct file_operations proc_fw_upgrade_fops = { + .read = ilitek_node_fw_upgrade_read, + .llseek = default_llseek, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_fw_process_fops = { + .proc_read = ilitek_proc_fw_process_read, + .proc_lseek = default_llseek, +}; +#else +static struct file_operations proc_fw_process_fops = { + .read = ilitek_proc_fw_process_read, + .llseek = default_llseek, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_get_delta_data_fops = { + .proc_read = ilitek_proc_get_delta_data_read, + .proc_lseek = default_llseek, +}; +#else +static struct file_operations proc_get_delta_data_fops = { + .read = ilitek_proc_get_delta_data_read, + .llseek = default_llseek, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_get_raw_data_fops = { + .proc_read = ilitek_proc_fw_get_raw_data_read, + .proc_lseek = default_llseek, +}; +#else +static struct file_operations proc_get_raw_data_fops = { + .read = ilitek_proc_fw_get_raw_data_read, + .llseek = default_llseek, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_rw_tp_reg_fops = { + .proc_read = ilitek_proc_rw_tp_reg_read, + .proc_write = ilitek_proc_rw_tp_reg_write, + .proc_lseek = default_llseek, +}; +#else +static struct file_operations proc_rw_tp_reg_fops = { + .read = ilitek_proc_rw_tp_reg_read, + .write = ilitek_proc_rw_tp_reg_write, + .llseek = default_llseek, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_fw_pc_counter_fops = { + .proc_read = ilitek_proc_fw_pc_counter_read, + .proc_lseek = default_llseek, +}; +#else +static struct file_operations proc_fw_pc_counter_fops = { + .read = ilitek_proc_fw_pc_counter_read, + .llseek = default_llseek, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_get_debug_mode_data_fops = { + .proc_read = ilitek_proc_get_debug_mode_data_read, + .proc_write = ilitek_proc_get_debug_mode_data_write, + .proc_lseek = default_llseek, +}; +#else +static struct file_operations proc_get_debug_mode_data_fops = { + .read = ilitek_proc_get_debug_mode_data_read, + .write = ilitek_proc_get_debug_mode_data_write, + .llseek = default_llseek, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_debug_level_fops = { + .proc_read = ilitek_proc_debug_level_read, + .proc_lseek = default_llseek, +}; +#else +static struct file_operations proc_debug_level_fops = { + .read = ilitek_proc_debug_level_read, + .llseek = default_llseek, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_sram_test_fops = { + .proc_read = ilitek_proc_sram_test_info, + .proc_lseek = default_llseek, +}; +#else +static struct file_operations proc_sram_test_fops = { + .read = ilitek_proc_sram_test_info, + .llseek = default_llseek, +}; +#endif + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 6, 0) +static struct proc_ops proc_fw_cmd_fops = { + .proc_read = ilitek_proc_fw_cmd_read, + .proc_write = ilitek_proc_fw_cmd_write, + .proc_lseek = default_llseek, +}; +#else +static struct file_operations proc_fw_cmd_fops = { + .read = ilitek_proc_fw_cmd_read, + .write = ilitek_proc_fw_cmd_write, + .llseek = default_llseek, +}; +#endif + +proc_node iliproc[] = { + {"ioctl", NULL, &proc_ioctl_fops, false}, + {"fw_process", NULL, &proc_fw_process_fops, false}, + {"fw_upgrade", NULL, &proc_fw_upgrade_fops, false}, + {"debug_level", NULL, &proc_debug_level_fops, false}, + {"mp_lcm_on_test", NULL, &proc_mp_lcm_on_test_fops, false}, + {"mp_lcm_off_test", NULL, &proc_mp_lcm_off_test_fops, false}, + {"debug_message", NULL, &proc_debug_message_fops, false}, + {"debug_message_switch", NULL, &proc_debug_message_switch_fops, false}, + {"debug_all_message", NULL, &proc_debug_all_message_fops, false}, + {"debug_all_message_switch", NULL, &proc_debug_all_message_switch_fops, false}, + {"fw_pc_counter", NULL, &proc_fw_pc_counter_fops, false}, + {"show_delta_data", NULL, &proc_get_delta_data_fops, false}, + {"show_raw_data", NULL, &proc_get_raw_data_fops, false}, + {"get_debug_mode_data", NULL, &proc_get_debug_mode_data_fops, false}, + {"rw_tp_reg", NULL, &proc_rw_tp_reg_fops, false}, + {"ver_info", NULL, &proc_ver_info_fops, false}, + {"change_list", NULL, &proc_change_list_fops, false}, + {"sram_test", NULL, &proc_sram_test_fops, false}, + {"fw_cmd", NULL, &proc_fw_cmd_fops, false}, +#if GENERIC_KERNEL_IMAGE + {"show_mp_test_result", NULL, &proc_show_mp_test_result_fops, false}, +#endif +}; + +static char *mmi_kobject_get_path(struct kobject *kobj, gfp_t gfp_mask) +{ + char *path; + int len = 1; + struct kobject *parent = kobj; + + do { + if (parent->name == NULL) { + len = 0; + break; + } + len += strlen(parent->name) + 1; + parent = parent->parent; + } while (parent); + + if (len == 0) + return NULL; + + path = kzalloc(len, gfp_mask); + if (!path) + return NULL; + + --len; + for (parent = kobj; parent; parent = parent->parent) { + int cur = strlen(parent->name); + len -= cur; + memcpy(path + len, parent->name, cur); + *(path + --len) = '/'; + } + pr_debug("kobject: '%s' (%p): %s: path = '%s'\n", kobj->name, + kobj, __func__, path); + + return path; +} + +#ifdef ILI_TOUCH_LAST_TIME +static ssize_t timestamp_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ktime_t last_ktime; + struct timespec64 last_ts; + + last_ktime = ilits->last_event_time; + ilits->last_event_time = 0; + + last_ts = ktime_to_timespec64(last_ktime); + return scnprintf(buf, PAGE_SIZE, "%lld.%ld\n", last_ts.tv_sec, last_ts.tv_nsec); +} +#endif + +static ssize_t path_show(struct device *pDevice, struct device_attribute *pAttr, char *pBuf) +{ + ssize_t blen; + const char *path; + + //path = kobject_get_path(&ilits->spi->dev.kobj, GFP_KERNEL); + path = mmi_kobject_get_path(&ilits->spi->dev.kobj, GFP_KERNEL); + blen = scnprintf(pBuf, PAGE_SIZE, "%s\n", path ? path : "na"); + kfree(path); + + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ILI_INFO("*** %s() vendor = %s ***\n", __func__, "ilitek"); + return scnprintf(buf, PAGE_SIZE, "ilitek"); +} + +/* Attribute: ic_ver (RO) */ +static ssize_t ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%s%x\n%s%d.%d.%.d.%d\n%s%d.%d.%d.%d\n", + "Product ID: ", ilits->chip->product_id, + "Build ID: ", ilits->chip->fw_ver >> 24, (ilits->chip->fw_ver >> 16) & 0xFF, + (ilits->chip->fw_ver >> 8) & 0xFF, ilits->chip->fw_ver & 0xFF, + "Config ID: ", + (ilits->chip->core_ver >> 24) & 0xFF, (ilits->chip->core_ver >> 16) & 0xFF, + (ilits->chip->core_ver >> 8) & 0xFF, ilits->chip->core_ver & 0xFF); +} + +#ifdef ILI_DOUBLE_TAP_CTRL +/* + * gesture value used to indicate which gesture mode type is supported + */ +static ssize_t gesture_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ILI_INFO("supported_gesture_type=%d", ilits->supported_gesture_type); + return scnprintf(buf, PAGE_SIZE, "%02x\n", ilits->supported_gesture_type); +} + +/* + * gesture value used to indicate which gesture mode type is supported. + * the gesture node will be stored when gesture mode updated from system + */ +static ssize_t gesture_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value; + int err = 0; + + err = sscanf(buf, "%d", &value); + if (err < 0) { + ILI_INFO("Failed to convert value\n"); + return -EINVAL; + } + + err = count; + + switch (value) { + case 0x20: + ILI_INFO("single tap disable\n"); + ilits->ges_sym.single_tap = OFF; + ilits->sys_gesture_type &= 0xFD; + break; + case 0x21: + ILI_INFO("single tap enable\n"); + ilits->ges_sym.single_tap = SINGLE_TAP; + ilits->sys_gesture_type |= 0x02; + break; + case 0x30: + ILI_INFO("double tap disable\n"); + ilits->ges_sym.double_tap = OFF; + ilits->sys_gesture_type &= 0xFB; + break; + case 0x31: + ILI_INFO("double tap enable\n"); + ilits->ges_sym.double_tap = DOUBLE_TAP; + ilits->sys_gesture_type |= 0x04; + break; + default: + ILI_INFO("unsupport gesture mode type\n"); + return err; + } + + ili_gesture_state_switch(); + ILI_INFO("sys_gesture_type=%d, should_enable_gesture=%d\n", ilits->sys_gesture_type, ilits->should_enable_gesture); + + return err; +} + +#if defined (ILI_STOWED_MODE_EN) && defined (ILI_SENSOR_EN) +static ssize_t stowed_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ILI_INFO("Stowed state = %d.\n", ilits->set_stowed); + return scnprintf(buf, PAGE_SIZE, "0x%02x", ilits->set_stowed); +} + +static ssize_t stowed_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + unsigned long mode = 0; + + ret = kstrtoul(buf, 0, &mode); + if (ret < 0) { + ILI_INFO("Failed to convert value.\n"); + return -EINVAL; + } + ilits->get_stowed = mode; + if (ilits->set_stowed == mode) { + ILI_INFO("The value = %lu is same, so not to write", mode); + ret = size; + return ret; + } + + mutex_lock(&ilits->touch_mutex); + if (ilits->tp_suspend && ilits->should_enable_gesture) { + if (mode) { + ili_proximity_near(DDI_POWER_ON); + } else { + ili_proximity_far(WAKE_UP_SWITCH_GESTURE_MODE); + } + } else { + ILI_INFO("Skip stowed mode setting suspended:%d,ilits->should_enable_gesture:%d", ilits->tp_suspend,ilits->should_enable_gesture); + ret = size; + mutex_unlock(&ilits->touch_mutex); + return ret; + } + + ilits->set_stowed = mode; + ret = size; + ILI_INFO("Success to set stowed mode %lu\n", mode); + mutex_unlock(&ilits->touch_mutex); + return ret; +} +#endif + +/* + * gesture type debug value used for nvt driver to check current gesture mode + */ +static ssize_t gesture_type_dbg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%02x\n", ilits->sys_gesture_type); +} + +static ssize_t gesture_type_dbg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value; + int err = 0; + + ILI_DBG("enter\n"); + err = sscanf(buf, "%d", &value); + if (err < 0) { + ILI_INFO("Failed to convert value\n"); + return -EINVAL; + } + + err = count; + ILI_INFO("value=%d\n", value); + + switch (value) { + case 2: //SYS_GESTURE_TYPE_SINGLE_ONLY: + ilits->sys_gesture_type = value; + ilits->ges_sym.single_tap = SINGLE_TAP; + ilits->ges_sym.double_tap = OFF; + ILI_INFO("sys_gesture_type: %d\n", ilits->sys_gesture_type); + break; + case 4: //SYS_GESTURE_TYPE_DOUBLE_ONLY: + ilits->sys_gesture_type = value; + ilits->ges_sym.single_tap = OFF; + ilits->ges_sym.double_tap = DOUBLE_TAP; + ILI_INFO("sys_gesture_type: %d\n", ilits->sys_gesture_type); + break; + case 6: //SYS_GESTURE_TYPE_SINGLE_DOUBLE: + ilits->sys_gesture_type = value; + ilits->ges_sym.single_tap = SINGLE_TAP; + ilits->ges_sym.double_tap = DOUBLE_TAP; + ILI_INFO("sys_gesture_type: %d\n", ilits->sys_gesture_type); + break; + default: + //disable gesture + ilits->sys_gesture_type = 0; + ilits->ges_sym.single_tap = OFF; + ilits->ges_sym.double_tap = OFF; + ILI_INFO("unsupport gesture mode type\n"); + break; + } + + ili_gesture_state_switch(); + ILI_INFO("sys_gesture_type=%d, should_enable_gesture=%d\n", ilits->sys_gesture_type, ilits->should_enable_gesture); + + return err; +} +#endif //ILI_DOUBLE_TAP_CTRL + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_RO(ic_ver), +#ifdef ILI_TOUCH_LAST_TIME + __ATTR_RO(timestamp), +#endif +#ifdef ILI_DOUBLE_TAP_CTRL + __ATTR(gesture, S_IRUGO | S_IWUSR | S_IWGRP, gesture_show, gesture_store), + __ATTR(gesture_type_dbg, S_IRUGO | S_IWUSR | S_IWGRP, gesture_type_dbg_show, gesture_type_dbg_store), +#endif +#if defined (ILI_STOWED_MODE_EN) && defined (ILI_SENSOR_EN) + __ATTR(stowed, S_IRUGO | S_IWUSR | S_IWGRP, stowed_show, stowed_store), +#endif + __ATTR_NULL +}; + +int ilitek_sys_init(void) +{ + int i; + s32 ret = 0; + dev_t devno; + struct device_attribute *attrs = touchscreen_attributes; + +#ifdef ILI_PRIMARY_NODE + ret = alloc_chrdev_region(&devno, 0, 1, ILI_SPI_NAME_PRIMARY); +#else + ret = alloc_chrdev_region(&devno, 0, 1, ILI_SPI_NAME); +#endif + if (ret) { + ILI_ERR ("can't allocate chrdev\n"); + return ret; + } else { + + /* set sysfs for firmware */ + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + ret = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + ILI_ERR("Failed to create touchscreen class!\n"); + return ret; + } + +#ifdef ILI_PRIMARY_NODE + touchscreen_class_dev = device_create(touchscreen_class, NULL, devno, NULL, ILI_SPI_NAME_PRIMARY); +#else + touchscreen_class_dev = device_create(touchscreen_class, NULL, devno, NULL, ILI_SPI_NAME); +#endif + + pr_info(" touchscreen_class_dev = %p \n", touchscreen_class_dev); + if (IS_ERR(touchscreen_class_dev)) { + ret = PTR_ERR(touchscreen_class_dev); + touchscreen_class_dev = NULL; + ILI_ERR("Failed to create device(touchscreen_class_dev)!\n"); + return ret; + } + ILI_INFO("Succeed to create device(touchscreen_class_dev)!\n"); + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + ret = device_create_file(touchscreen_class_dev, &attrs[i]); + if (ret < 0) + goto device_destroy; + } + } + + return ret; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(touchscreen_class_dev, &attrs[i]); + + touchscreen_class_dev = NULL; + class_unregister(touchscreen_class); + ILI_ERR("error creating touchscreen class\n"); + + return -ENODEV; +} + +void ili_node_init(void) +{ + int i = 0; + + proc_dir_ilitek = proc_mkdir("ilitek", NULL); + + for (; i < ARRAY_SIZE(iliproc); i++) { + iliproc[i].node = proc_create(iliproc[i].name, 0644, proc_dir_ilitek, iliproc[i].fops); + + if (iliproc[i].node == NULL) { + iliproc[i].isCreated = false; + ILI_ERR("Failed to create %s under /proc\n", iliproc[i].name); + } else { + iliproc[i].isCreated = true; + ILI_INFO("Succeed to create %s under /proc\n", iliproc[i].name); + } + } + + ilitek_sys_init(); +} diff --git a/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_qcom.c b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_qcom.c new file mode 100644 index 000000000000..724c1659d0b4 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_qcom.c @@ -0,0 +1,1107 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek_v3.h" +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +#include +#endif + +#define DTS_INT_GPIO "touch,irq-gpio" +#define DTS_RESET_GPIO "touch,reset-gpio" +#define DTS_OF_NAME "tchip,ilitek" + +#if IS_ENABLED(CONFIG_DRM_MEDIATEK) +static int ili_disp_notifier_callback(struct notifier_block *nb, unsigned long value, void *v); + +static int ili_tp_power_on_reinit(void) +{ + int ret = 0; + + ILI_DBG("enter, TBD\n"); + + // do esd recovery, bootloader reset + ret = ili_sleep_handler(TP_DEEP_SLEEP); + if (ret < 0) + ILI_INFO("reinit deep sleep suspend ret:%d\n", ret); + else + ILI_INFO("reinit deep sleep suspend!\n"); + + usleep_range(5000, 5100); + + ret = ili_sleep_handler(TP_RESUME); + if (ret < 0) + ILI_INFO("reinit resume ret:%d\n", ret); + else + ILI_INFO("reinit resume!\n"); + + return ret; +} +#endif + +#ifdef ILI_SENSOR_EN +static struct sensors_classdev __maybe_unused sensors_touch_cdev = { + + .name = "dt-gesture", + .vendor = "ilitek", + .version = 1, + .type = SENSOR_TYPE_MOTO_DOUBLE_TAP, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + /* WAKE_UP & SPECIAL_REPORT */ + .flags = 1 | 6, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#endif + +void ili_tp_reset(void) +{ + ILI_INFO("edge delay = %d\n", ilits->rst_edge_delay); + + /* Need accurate power sequence, do not change it to msleep */ + gpio_direction_output(ilits->tp_rst, 1); + mdelay(1); + gpio_set_value(ilits->tp_rst, 0); + mdelay(5); + gpio_set_value(ilits->tp_rst, 1); + mdelay(ilits->rst_edge_delay); +} + +void ili_input_register(void) +{ + ILI_INFO(); + + ilits->input = input_allocate_device(); + if (ERR_ALLOC_MEM(ilits->input)) { + ILI_ERR("Failed to allocate touch input device\n"); + input_free_device(ilits->input); + return; + } + + ilits->input->name = ilits->hwif->name; + ilits->input->phys = ilits->phys; + ilits->input->dev.parent = ilits->dev; + ilits->input->id.bustype = ilits->hwif->bus_type; + + /* set the supported event type for input device */ + set_bit(EV_ABS, ilits->input->evbit); + set_bit(EV_SYN, ilits->input->evbit); + set_bit(EV_KEY, ilits->input->evbit); + set_bit(BTN_TOUCH, ilits->input->keybit); + set_bit(BTN_TOOL_FINGER, ilits->input->keybit); + set_bit(INPUT_PROP_DIRECT, ilits->input->propbit); + + input_set_abs_params(ilits->input, ABS_MT_POSITION_X, TOUCH_SCREEN_X_MIN, ilits->panel_wid - 1, 0, 0); + input_set_abs_params(ilits->input, ABS_MT_POSITION_Y, TOUCH_SCREEN_Y_MIN, ilits->panel_hei - 1, 0, 0); + input_set_abs_params(ilits->input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(ilits->input, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); + + if (MT_PRESSURE) + input_set_abs_params(ilits->input, ABS_MT_PRESSURE, 0, 255, 0, 0); + + if (MT_B_TYPE) { +#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE + input_mt_init_slots(ilits->input, MAX_TOUCH_NUM, INPUT_MT_DIRECT); +#else + input_mt_init_slots(ilits->input, MAX_TOUCH_NUM); +#endif /* LINUX_VERSION_CODE */ + } else { + input_set_abs_params(ilits->input, ABS_MT_TRACKING_ID, 0, MAX_TOUCH_NUM, 0, 0); + } + + /* Gesture keys register */ + input_set_capability(ilits->input, EV_KEY, KEY_POWER); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_UP); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_DOWN); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_LEFT); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_RIGHT); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_O); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_E); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_M); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_W); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_S); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_V); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_Z); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_C); + input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_F); + + __set_bit(KEY_GESTURE_POWER, ilits->input->keybit); + __set_bit(KEY_GESTURE_UP, ilits->input->keybit); + __set_bit(KEY_GESTURE_DOWN, ilits->input->keybit); + __set_bit(KEY_GESTURE_LEFT, ilits->input->keybit); + __set_bit(KEY_GESTURE_RIGHT, ilits->input->keybit); + __set_bit(KEY_GESTURE_O, ilits->input->keybit); + __set_bit(KEY_GESTURE_E, ilits->input->keybit); + __set_bit(KEY_GESTURE_M, ilits->input->keybit); + __set_bit(KEY_GESTURE_W, ilits->input->keybit); + __set_bit(KEY_GESTURE_S, ilits->input->keybit); + __set_bit(KEY_GESTURE_V, ilits->input->keybit); + __set_bit(KEY_GESTURE_Z, ilits->input->keybit); + __set_bit(KEY_GESTURE_C, ilits->input->keybit); + __set_bit(KEY_GESTURE_F, ilits->input->keybit); + + /* register the input device to input sub-system */ + if (input_register_device(ilits->input) < 0) { + ILI_ERR("Failed to register touch input device\n"); + input_unregister_device(ilits->input); + input_free_device(ilits->input); + } +} + +void ili_input_pen_register(void) +{ + ilits->input_pen = input_allocate_device(); + if (ERR_ALLOC_MEM(ilits->input_pen)) { + ILI_ERR("Failed to allocate touch input device\n"); + input_free_device(ilits->input_pen); + return; + } + + ilits->input_pen->name = PEN_INPUT_DEVICE; + ilits->input_pen->phys = ilits->phys; + ilits->input_pen->dev.parent = ilits->dev; + ilits->input_pen->id.bustype = ilits->hwif->bus_type; + + /* set the supported event type for input device */ + set_bit(EV_ABS, ilits->input_pen->evbit); + set_bit(EV_SYN, ilits->input_pen->evbit); + set_bit(EV_KEY, ilits->input_pen->evbit); + set_bit(BTN_TOUCH, ilits->input_pen->keybit); + set_bit(BTN_TOOL_PEN, ilits->input_pen->keybit); + set_bit(BTN_STYLUS, ilits->input_pen->keybit); + set_bit(BTN_STYLUS2, ilits->input_pen->keybit); + set_bit(INPUT_PROP_DIRECT, ilits->input_pen->propbit); + + input_set_abs_params(ilits->input_pen, ABS_X, 0, ilits->panel_wid, 0, 0); + input_set_abs_params(ilits->input_pen, ABS_Y, 0, ilits->panel_hei, 0, 0); + input_set_abs_params(ilits->input_pen, ABS_PRESSURE, 0, MAX_PRESSURE, 0, 0); + input_set_abs_params(ilits->input_pen, ABS_TILT_X, -60, 60, 0, 0); + input_set_abs_params(ilits->input_pen, ABS_TILT_Y, -60, 60, 0, 0); + input_set_abs_params(ilits->input_pen, ABS_DISTANCE, 0, 1, 0, 0); + + /* register the input device to input sub-system */ + if (input_register_device(ilits->input_pen) < 0) { + ILI_ERR("Failed to register touch input device\n"); + input_unregister_device(ilits->input_pen); + input_free_device(ilits->input_pen); + } + ILI_INFO("Input Pen device for Single Touch\n"); + ILI_DBG("Input pen Register.\n"); +} + +#if REGULATOR_POWER +void ili_plat_regulator_power_on(bool status) +{ + ILI_INFO("%s\n", status ? "POWER ON" : "POWER OFF"); + + if (status) { + if (ilits->vdd) { + if (regulator_enable(ilits->vdd) < 0) + ILI_ERR("regulator_enable VDD fail\n"); + } + if (ilits->vcc) { + if (regulator_enable(ilits->vcc) < 0) + ILI_ERR("regulator_enable VCC fail\n"); + } + } else { + if (ilits->vdd) { + if (regulator_disable(ilits->vdd) < 0) + ILI_ERR("regulator_enable VDD fail\n"); + } + if (ilits->vcc) { + if (regulator_disable(ilits->vcc) < 0) + ILI_ERR("regulator_enable VCC fail\n"); + } + } + atomic_set(&ilits->ice_stat, DISABLE); + mdelay(5); +} + +static void ilitek_plat_regulator_power_init(void) +{ + const char *vdd_name = "vdd"; + const char *vcc_name = "vcc"; + + ilits->vdd = regulator_get(ilits->dev, vdd_name); + if (ERR_ALLOC_MEM(ilits->vdd)) { + ILI_ERR("regulator_get VDD fail\n"); + ilits->vdd = NULL; + } + if (regulator_set_voltage(ilits->vdd, VDD_VOLTAGE, VDD_VOLTAGE) < 0) + ILI_ERR("Failed to set VDD %d\n", VDD_VOLTAGE); + + ilits->vcc = regulator_get(ilits->dev, vcc_name); + if (ERR_ALLOC_MEM(ilits->vcc)) { + ILI_ERR("regulator_get VCC fail.\n"); + ilits->vcc = NULL; + } + if (regulator_set_voltage(ilits->vcc, VCC_VOLTAGE, VCC_VOLTAGE) < 0) + ILI_ERR("Failed to set VCC %d\n", VCC_VOLTAGE); + + ili_plat_regulator_power_on(true); +} +#endif + +static int ilitek_plat_gpio_register(void) +{ + int ret = 0; + u32 flag; + struct device_node *dev_node = ilits->dev->of_node; + + ilits->tp_int = of_get_named_gpio_flags(dev_node, DTS_INT_GPIO, 0, &flag); + ilits->tp_rst = of_get_named_gpio_flags(dev_node, DTS_RESET_GPIO, 0, &flag); + + ILI_INFO("TP INT: %d\n", ilits->tp_int); + ILI_INFO("TP RESET: %d\n", ilits->tp_rst); + + if (!gpio_is_valid(ilits->tp_int)) { + ILI_ERR("Invalid INT gpio: %d\n", ilits->tp_int); + return -EBADR; + } + + if (!gpio_is_valid(ilits->tp_rst)) { + ILI_ERR("Invalid RESET gpio: %d\n", ilits->tp_rst); + return -EBADR; + } + + ret = gpio_request(ilits->tp_int, "TP_INT"); + if (ret < 0) { + ILI_ERR("Request IRQ GPIO failed, ret = %d\n", ret); + gpio_free(ilits->tp_int); + ret = gpio_request(ilits->tp_int, "TP_INT"); + if (ret < 0) { + ILI_ERR("Retrying request INT GPIO still failed , ret = %d\n", ret); + goto out; + } + } + + ret = gpio_request(ilits->tp_rst, "TP_RESET"); + if (ret < 0) { + ILI_ERR("Request RESET GPIO failed, ret = %d\n", ret); + gpio_free(ilits->tp_rst); + ret = gpio_request(ilits->tp_rst, "TP_RESET"); + if (ret < 0) { + ILI_ERR("Retrying request RESET GPIO still failed , ret = %d\n", ret); + goto out; + } + } + +out: + gpio_direction_input(ilits->tp_int); + return ret; +} + +void ili_irq_disable(void) +{ + unsigned long flag; + + spin_lock_irqsave(&ilits->irq_spin, flag); + + if (atomic_read(&ilits->irq_stat) == DISABLE) + goto out; + + if (!ilits->irq_num) { + ILI_ERR("gpio_to_irq (%d) is incorrect\n", ilits->irq_num); + goto out; + } + + disable_irq_nosync(ilits->irq_num); + atomic_set(&ilits->irq_stat, DISABLE); + ILI_DBG("Disable irq success\n"); + +out: + spin_unlock_irqrestore(&ilits->irq_spin, flag); +} + +void ili_irq_enable(void) +{ + unsigned long flag; + + spin_lock_irqsave(&ilits->irq_spin, flag); + + if (atomic_read(&ilits->irq_stat) == ENABLE) + goto out; + + if (!ilits->irq_num) { + ILI_ERR("gpio_to_irq (%d) is incorrect\n", ilits->irq_num); + goto out; + } + + enable_irq(ilits->irq_num); + atomic_set(&ilits->irq_stat, ENABLE); + ILI_DBG("Enable irq success\n"); + +out: + spin_unlock_irqrestore(&ilits->irq_spin, flag); +} + +static irqreturn_t ilitek_plat_isr_top_half(int irq, void *dev_id) +{ + if (irq != ilits->irq_num) { + ILI_ERR("Incorrect irq number (%d)\n", irq); + return IRQ_NONE; + } + + if (atomic_read(&ilits->cmd_int_check) == ENABLE) { + atomic_set(&ilits->cmd_int_check, DISABLE); + ILI_DBG("CMD INT detected, ignore\n"); + wake_up(&(ilits->inq)); + return IRQ_HANDLED; + } + + if (ilits->prox_near) { + ILI_INFO("Proximity event, ignore interrupt!\n"); + return IRQ_HANDLED; + } + + ILI_DBG("report: %d, rst: %d, fw: %d, switch: %d, mp: %d, sleep: %d, esd: %d, igr:%d\n", + ilits->report, + atomic_read(&ilits->tp_reset), + atomic_read(&ilits->fw_stat), + atomic_read(&ilits->tp_sw_mode), + atomic_read(&ilits->mp_stat), + atomic_read(&ilits->tp_sleep), + atomic_read(&ilits->esd_stat), + atomic_read(&ilits->ignore_report)); + + if (!ilits->report || atomic_read(&ilits->tp_reset) || atomic_read(&ilits->ignore_report) || + atomic_read(&ilits->fw_stat) || atomic_read(&ilits->tp_sw_mode) || + atomic_read(&ilits->mp_stat) || atomic_read(&ilits->tp_sleep) || + atomic_read(&ilits->esd_stat)) { + ILI_DBG("ignore interrupt !\n"); + return IRQ_HANDLED; + } + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t ilitek_plat_isr_bottom_half(int irq, void *dev_id) +{ + if (mutex_is_locked(&ilits->touch_mutex)) { + ILI_DBG("touch is locked, ignore\n"); + return IRQ_HANDLED; + } + mutex_lock(&ilits->touch_mutex); + ili_report_handler(); + mutex_unlock(&ilits->touch_mutex); + return IRQ_HANDLED; +} + +void ili_irq_unregister(void) +{ + devm_free_irq(ilits->dev, ilits->irq_num, NULL); +} + +int ili_irq_register(int type) +{ + int ret = 0; + static bool get_irq_pin; + + atomic_set(&ilits->irq_stat, DISABLE); + + if (get_irq_pin == false) { + ilits->irq_num = gpio_to_irq(ilits->tp_int); + get_irq_pin = true; + } + + ILI_INFO("ilits->irq_num = %d\n", ilits->irq_num); + + ret = devm_request_threaded_irq(ilits->dev, ilits->irq_num, + ilitek_plat_isr_top_half, + ilitek_plat_isr_bottom_half, + type | IRQF_ONESHOT, "ilitek", NULL); + + if (type == IRQF_TRIGGER_FALLING) + ILI_INFO("IRQ TYPE = IRQF_TRIGGER_FALLING\n"); + if (type == IRQF_TRIGGER_RISING) + ILI_INFO("IRQ TYPE = IRQF_TRIGGER_RISING\n"); + + if (ret != 0) + ILI_ERR("Failed to register irq handler, irq = %d, ret = %d\n", ilits->irq_num, ret); + + atomic_set(&ilits->irq_stat, ENABLE); + + return ret; +} + +#if IS_ENABLED(CONFIG_DRM_MEDIATEK) +static int ili_disp_notifier_callback(struct notifier_block *nb, + unsigned long value, void *v) +{ + int *data = (int *)v; + + ILI_DBG("entery\n"); + if (v) { + if (value == MTK_DISP_EARLY_EVENT_BLANK) { + /* before fb blank */ + ILI_INFO("event %lu not care", value); + } else if (value == MTK_DISP_EVENT_BLANK) { + if (*data == MTK_DISP_BLANK_UNBLANK) { + ILI_INFO("TP resume: event = %lu, TP_RESUME\n", value); + if (ili_sleep_handler(TP_RESUME) < 0) + ILI_ERR("TP resume failed\n"); + } + else if (*data == MTK_DISP_BLANK_POWERDOWN) { +#ifdef ILI_DOUBLE_TAP_CTRL + if (ilits->should_enable_gesture) { + ILI_INFO("TP suspend: tap gesture suspend\n"); + if (ili_sleep_handler(TP_SUSPEND) < 0) + ILI_ERR("TP suspend failed\n"); +#ifdef ILI_SET_TOUCH_STATE + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); +#endif + } + else { + ILI_INFO("TP suspend: TP_DEEP_SLEEP event = %lu\n", value); + if (ili_sleep_handler(TP_DEEP_SLEEP) < 0) + ILI_ERR("TP suspend deep sleep fail\n"); +#ifdef ILI_SET_TOUCH_STATE + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); +#endif + } +#else //ILI_DOUBLE_TAP_CTRL + ILI_INFO("TP suspend: event = %lu, TP_DEEP_SLEEP\n", value); + if (ili_sleep_handler(TP_DEEP_SLEEP) < 0) + ILI_ERR("TP suspend deep sleep failed\n"); +#endif //ILI_DOUBLE_TAP_CTRL + } + } + } + + return 0; +} +#endif + +#if SPRD_SYSFS_SUSPEND_RESUME +static ssize_t ts_suspend_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%s\n", ilits->tp_suspend ? "true" : "false"); +} + +static ssize_t ts_suspend_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + if ((buf[0] == '1') && !ilits->tp_suspend) + ili_sleep_handler(TP_DEEP_SLEEP); + else if ((buf[0] == '0') && ilits->tp_suspend) + ili_sleep_handler(TP_RESUME); + + return count; +} +static DEVICE_ATTR_RW(ts_suspend); + +static struct attribute *ilitek_dev_suspend_atts[] = { + &dev_attr_ts_suspend.attr, + NULL +}; + +static const struct attribute_group ilitek_dev_suspend_atts_group = { + .attrs = ilitek_dev_suspend_atts, +}; + +static const struct attribute_group *ilitek_dev_attr_groups[] = { + &ilitek_dev_suspend_atts_group, + NULL +}; + +int ili_sysfs_add_device(struct device *dev) +{ + int ret = 0, i; + + for (i = 0; ilitek_dev_attr_groups[i]; i++) { + ret = sysfs_create_group(&dev->kobj, ilitek_dev_attr_groups[i]); + if (ret) { + while (--i >= 0) { + sysfs_remove_group(&dev->kobj, ilitek_dev_attr_groups[i]); + } + break; + } + } + + return ret; +} + +int ili_sysfs_remove_device(struct device *dev) +{ + int i; + + sysfs_remove_link(NULL, "touchscreen"); + for (i = 0; ilitek_dev_attr_groups[i]; i++) { + sysfs_remove_group(&dev->kobj, ilitek_dev_attr_groups[i]); + } + + return 0; +} +#elif SUSPEND_RESUME_SUPPORT + +#if defined(CONFIG_FB) || defined(CONFIG_DRM) +#if defined(__DRM_PANEL_H__) && defined(DRM_PANEL_EARLY_EVENT_BLANK) +static struct drm_panel *active_panel; + +static int ili_v3_drm_check_dt(struct device_node *np) +{ + int i = 0; + int count = 0; + struct device_node *node = NULL; + struct drm_panel *panel = NULL; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) { + ILI_ERR("find drm_panel count(%d) fail", count); + return 0; + } + ILI_INFO("find drm_panel count(%d) ", count); + for (i = 0; i < count; i++) { + node = of_parse_phandle(np, "panel", i); + ILI_INFO("node%p", node); + panel = of_drm_find_panel(node); + ILI_INFO("panel%p ", panel); + + of_node_put(node); + if (!IS_ERR(panel)) { + ILI_INFO("find drm_panel successfully"); + active_panel = panel; + return 0; + } + } + + ILI_ERR("no find drm_panel"); + return -ENODEV; +} + +static int drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data) +{ + struct drm_panel_notifier *evdata = data; + int *blank = NULL; + + if (!evdata) { + ILI_ERR("evdata is null"); + return 0; + } + + if (!((event == DRM_PANEL_EARLY_EVENT_BLANK) + || (event == DRM_PANEL_EVENT_BLANK))) { + ILI_INFO("event(%lu) do not need process\n", event); + return 0; + } + + blank = evdata->data; + ILI_DBG("DRM event:%lu,blank:%d", event, *blank); + switch (*blank) { + case DRM_PANEL_BLANK_UNBLANK: + if (DRM_PANEL_EARLY_EVENT_BLANK == event) { + ILI_INFO("resume: event = %lu, not care\n", event); + } else if (DRM_PANEL_EVENT_BLANK == event) { + ILI_INFO("resume: event = %lu, TP_RESUME\n", event); + if (ili_sleep_handler(TP_RESUME) < 0) + ILI_ERR("TP resume failed\n"); + } + break; + case DRM_PANEL_BLANK_POWERDOWN: + if (DRM_PANEL_EARLY_EVENT_BLANK == event) { +#ifdef ILI_DOUBLE_TAP_CTRL + if (ilits->should_enable_gesture) { + ILI_INFO("TP suspend: tap gesture suspend\n"); + if (ili_sleep_handler(TP_SUSPEND) < 0) + ILI_ERR("TP suspend failed\n"); +#ifdef ILI_SET_TOUCH_STATE + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); +#endif + } + else { + ILI_INFO("TP suspend: TP_DEEP_SLEEP event = %lu\n", event); + if (ili_sleep_handler(TP_DEEP_SLEEP) < 0) + ILI_ERR("TP suspend deep sleep fail\n"); +#ifdef ILI_SET_TOUCH_STATE + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); +#endif + if (ilits->rst_pull_flag && gpio_get_value(ilits->tp_rst)) + gpio_set_value(ilits->tp_rst, 0); + } +#else //ILI_DOUBLE_TAP_CTRL + ILI_INFO("TP suspend: event = %lu, TP_DEEP_SLEEP\n", event); + if (ili_sleep_handler(TP_DEEP_SLEEP) < 0) + ILI_ERR("TP suspend deep sleep failed\n"); +#endif //ILI_DOUBLE_TAP_CTRL + } else if (DRM_PANEL_EVENT_BLANK == event) { + ILI_INFO("suspend: event = %lu, not care\n", event); + } + break; + default: + ILI_DBG("DRM BLANK(%d) do not need process\n", *blank); + break; + } + + return 0; +} +#else +static int ilitek_plat_notifier_fb(struct notifier_block *self, unsigned long event, void *data) +{ + int *blank; + struct fb_event *evdata = data; + + ILI_INFO("Notifier's event = %ld\n", event); + + /* + * FB_EVENT_BLANK(0x09): A hardware display blank change occurred. + * FB_EARLY_EVENT_BLANK(0x10): A hardware display blank early change occurred. + */ + if (evdata && evdata->data) { + blank = evdata->data; + switch (*blank) { +#ifdef CONFIG_DRM_MSM + case MSM_DRM_BLANK_POWERDOWN: +#else + case FB_BLANK_POWERDOWN: +#endif +#if CONFIG_PLAT_SPRD + case DRM_MODE_DPMS_OFF: +#endif /* CONFIG_PLAT_SPRD */ + if (TP_SUSPEND_PRIO) { +#ifdef CONFIG_DRM_MSM + if (event != MSM_DRM_EARLY_EVENT_BLANK) +#else + if (event != FB_EARLY_EVENT_BLANK) +#endif + return NOTIFY_DONE; + } else { +#ifdef CONFIG_DRM_MSM + if (event != MSM_DRM_EVENT_BLANK) +#else + if (event != FB_EVENT_BLANK) +#endif + return NOTIFY_DONE; + } + if (ili_sleep_handler(TP_DEEP_SLEEP) < 0) + ILI_ERR("TP suspend failed\n"); + break; +#ifdef CONFIG_DRM_MSM + case MSM_DRM_BLANK_UNBLANK: + case MSM_DRM_BLANK_NORMAL: +#else + case FB_BLANK_UNBLANK: + case FB_BLANK_NORMAL: +#endif + +#if CONFIG_PLAT_SPRD + case DRM_MODE_DPMS_ON: +#endif /* CONFIG_PLAT_SPRD */ + +#ifdef CONFIG_DRM_MSM + if (event == MSM_DRM_EVENT_BLANK) +#else + if (event == FB_EVENT_BLANK) +#endif + { + if (ili_sleep_handler(TP_RESUME) < 0) + ILI_ERR("TP resume failed\n"); + + } + break; + default: + ILI_ERR("Unknown event, blank = %d\n", *blank); + break; + } + } + return NOTIFY_OK; +} +#endif/*defined(__DRM_PANEL_H__) && defined(DRM_PANEL_EARLY_EVENT_BLANK)*/ +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void ilitek_plat_early_suspend(struct early_suspend *h) +{ + if (ili_sleep_handler(TP_DEEP_SLEEP) < 0) + ILI_ERR("TP suspend failed\n"); +} + +static void ilitek_plat_late_resume(struct early_suspend *h) +{ + if (ili_sleep_handler(TP_RESUME) < 0) + ILI_ERR("TP resume failed\n"); +} +#endif/*defined(CONFIG_FB) || defined(CONFIG_DRM_MSM)*/ + +#ifdef ILI_SENSOR_EN +static int ili_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ +#ifdef ILI_DOUBLE_TAP_CTRL + ILI_DBG("double tap ctrl, do nothing\n"); +#else + ILI_INFO("Gesture set enable %d!", enable); + mutex_lock(&ilits->state_mutex); + if (enable == 1) { + ilits->should_enable_gesture = true; + } else if (enable == 0) { + ilits->should_enable_gesture = false; + } else { + ILI_INFO("unknown enable symbol\n"); + } + mutex_unlock(&ilits->state_mutex); +#endif + return 0; +} + +static int ili_sensor_init(struct ilitek_ts_data *data) +{ + struct ili_sensor_platform_data *sensor_pdata; + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + ILI_ERR("Failed to allocate device"); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct ili_sensor_platform_data), + GFP_KERNEL); + if (!sensor_pdata) { + ILI_ERR("Failed to allocate memory"); + goto free_sensor_pdata; + } + data->sensor_pdata = sensor_pdata; + + if (data->report_gesture_key) { + __set_bit(EV_KEY, sensor_input_dev->evbit); + __set_bit(BTN_TRIGGER_HAPPY3, sensor_input_dev->keybit); +#ifdef ILI_DOUBLE_TAP_CTRL + __set_bit(BTN_TRIGGER_HAPPY6, sensor_input_dev->keybit); +#endif + } else { + __set_bit(EV_ABS, sensor_input_dev->evbit); + input_set_abs_params(sensor_input_dev, ABS_DISTANCE, + 0, REPORT_MAX_COUNT, 0, 0); + } + __set_bit(EV_SYN, sensor_input_dev->evbit); + + sensor_input_dev->name = "double-tap"; + data->sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + ILI_ERR("Unable to register device, err=%d", err); + goto free_sensor_input_dev; + } + + sensor_pdata->ps_cdev = sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = ili_sensor_set_enable; + sensor_pdata->data = data; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(data->sensor_pdata->input_sensor_dev); +free_sensor_input_dev: + input_free_device(data->sensor_pdata->input_sensor_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, sensor_pdata); + data->sensor_pdata = NULL; +exit: + return 1; +} + +int ili_sensor_remove(struct ilitek_ts_data *data) +{ + sensors_classdev_unregister(&data->sensor_pdata->ps_cdev); + input_unregister_device(data->sensor_pdata->input_sensor_dev); + devm_kfree(&data->sensor_pdata->input_sensor_dev->dev, + data->sensor_pdata); + data->sensor_pdata = NULL; + data->wakeable = false; + data->should_enable_gesture = false; + return 0; +} +#endif + +#ifdef ILI_DOUBLE_TAP_CTRL +void ili_gesture_state_switch(void) +{ + if (ilits->sys_gesture_type) { + //gesture enable + if (!ilits->should_enable_gesture) { + ilits->should_enable_gesture = true; + +#ifdef ILI_SET_TOUCH_STATE + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); +#endif + ILI_INFO("gesture switch to enable"); + } + } + else { + //gesture disable + if (ilits->should_enable_gesture) { + ilits->should_enable_gesture = false; +#ifdef ILI_SET_TOUCH_STATE + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); +#endif + ILI_INFO("gesture switch to disable"); + } + } +} +#endif + +static void ilitek_plat_sleep_init(void) +{ +#if IS_ENABLED(CONFIG_DRM_MEDIATEK) + int ret; + void **mtk_ret = NULL; + + ILI_INFO("init disp_notifier cb\n"); + ilits->disp_notifier.notifier_call = ili_disp_notifier_callback; + ret = mtk_disp_notifier_register("ILI Touch", &ilits->disp_notifier); + if (ret) { + ILI_ERR("Failed to register disp notifier client:%d", ret); + goto err_register_disp_notif_failed; + } + + ILI_DBG("disp notifier TP power_on reset config\n"); + if (mtk_panel_tch_handle_init()) { + mtk_ret = mtk_panel_tch_handle_init(); + *mtk_ret = (void *)ili_tp_power_on_reinit; + } + else + ILI_INFO("mtk_panel_tch_handle_init NULL\n"); + + return; +#elif defined(CONFIG_FB) || defined(CONFIG_DRM) + ILI_INFO("Init notifier_fb struct\n"); +#if defined(__DRM_PANEL_H__) + ilits->notifier_fb.notifier_call = drm_notifier_callback; + if (active_panel) { + if (drm_panel_notifier_register(active_panel, &ilits->notifier_fb)) + ILI_ERR("[DRM]drm_panel_notifier_register fail\n"); + } +#else + ilits->notifier_fb.notifier_call = ilitek_plat_notifier_fb; +#if defined(CONFIG_DRM_MSM) + if (msm_drm_register_client(&ilits->notifier_fb)) { + ILI_ERR("msm_drm_register_client Unable to register fb_notifier\n"); + } +#else +#if CONFIG_PLAT_SPRD + if (adf_register_client(&ilits->notifier_fb)) + ILI_ERR("Unable to register notifier_fb\n"); +#else + if (fb_register_client(&ilits->notifier_fb)) + ILI_ERR("Unable to register notifier_fb\n"); +#endif /* CONFIG_PLAT_SPRD */ +#endif /* CONFIG_DRM_MSM */ +#endif/*defined(__DRM_PANEL_H__) && defined(DRM_PANEL_EARLY_EVENT_BLANK)*/ +#elif defined(CONFIG_HAS_EARLYSUSPEND) + ILI_INFO("Init eqarly_suspend struct\n"); + ilits->early_suspend.suspend = ilitek_plat_early_suspend; + ilits->early_suspend.resume = ilitek_plat_late_resume; + ilits->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + register_early_suspend(&ilits->early_suspend); +#endif + +#if IS_ENABLED(CONFIG_DRM_MEDIATEK) +err_register_disp_notif_failed: + ret = mtk_disp_notifier_unregister(&ilits->disp_notifier); + if (ret) + ILI_ERR("Error unregistering disp_notifier\n"); +#endif +} +#endif/*SPRD_SYSFS_SUSPEND_RESUME*/ + +static int ilitek_plat_probe(void) +{ +#ifdef ILI_SENSOR_EN + static bool initialized_sensor; +#endif +#if SUSPEND_RESUME_SUPPORT +#if defined(__DRM_PANEL_H__) && defined(DRM_PANEL_EARLY_EVENT_BLANK) + int ret = 0; +#endif +#endif + ILI_INFO("platform probe\n"); + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + if (ilits->dev->of_node && !mmi_device_is_available(ilits->dev->of_node)) { + ILI_ERR("%s : mmi: device not supported\n", __func__); + return -ENODEV; + } +#endif +#if REGULATOR_POWER + ilitek_plat_regulator_power_init(); +#endif + +#if SUSPEND_RESUME_SUPPORT +#if defined(__DRM_PANEL_H__) && defined(DRM_PANEL_EARLY_EVENT_BLANK) + ret = ili_v3_drm_check_dt(ilits->dev->of_node); + if (ret) { + ILI_ERR("[ili_v3_drm_check_dt] parse drm-panel fail"); + return ret; + } +#endif +#endif + + if (ilitek_plat_gpio_register() < 0) + ILI_ERR("Register gpio failed\n"); + + ili_irq_register(ilits->irq_tirgger_type); + + if (ili_tddi_init() < 0) { + ILI_ERR("ILITEK Driver probe failed\n"); + ili_irq_unregister(); + ili_dev_remove(DISABLE); + return -ENODEV; + } +#if SPRD_SYSFS_SUSPEND_RESUME + ili_sysfs_add_device(ilits->dev); + if (sysfs_create_link(NULL, &ilits->dev->kobj, "touchscreen") < 0) + ILI_INFO("Failed to create link!\n"); +#elif SUSPEND_RESUME_SUPPORT + ilitek_plat_sleep_init(); +#endif + ilits->pm_suspend = false; + init_completion(&ilits->pm_completion); +#if CHARGER_NOTIFIER_CALLBACK +#if KERNEL_VERSION(4, 1, 0) <= LINUX_VERSION_CODE + /* add_for_charger_start */ + ilitek_plat_charger_init(); + /* add_for_charger_end */ +#endif +#endif + +#ifdef ILI_SENSOR_EN + mutex_init(&ilits->state_mutex); + if (!initialized_sensor) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&(ilits->gesture_wakelock), WAKE_LOCK_SUSPEND, "dt-wake-lock"); +#else + PM_WAKEUP_REGISTER(ilits->dev, ilits->gesture_wakelock, "dt-wake-lock"); + if (!ilits->gesture_wakelock) { + ILI_ERR("ILITEK Driver failed to load. wakeup_source_init failed."); + return -ENOMEM; + } +#endif + if (!ili_sensor_init(ilits)) + initialized_sensor = true; + } +#endif + + ILI_INFO("ILITEK Driver loaded successfully!#"); + return 0; +} + +static int ilitek_tp_pm_suspend(struct device *dev) +{ + ILI_INFO("CALL BACK TP PM SUSPEND"); + ilits->pm_suspend = true; +#if KERNEL_VERSION(3, 12, 0) >= LINUX_VERSION_CODE + ilits->pm_completion.done = 0; +#else + reinit_completion(&ilits->pm_completion); +#endif + return 0; +} + +static int ilitek_tp_pm_resume(struct device *dev) +{ + ILI_INFO("CALL BACK TP PM RESUME"); + ilits->pm_suspend = false; + complete(&ilits->pm_completion); + return 0; +} + +static int ilitek_plat_remove(void) +{ + ILI_INFO("remove plat dev\n"); +#if IS_ENABLED(CONFIG_DRM_MEDIATEK) + if (ilits && mtk_disp_notifier_unregister(&ilits->disp_notifier)) + ILI_ERR("Error unregistering disp_notifier\n"); +#endif +#if SPRD_SYSFS_SUSPEND_RESUME + ili_sysfs_remove_device(ilits->dev); +#endif + ili_dev_remove(ENABLE); + return 0; +} + +static const struct dev_pm_ops tp_pm_ops = { + .suspend = ilitek_tp_pm_suspend, + .resume = ilitek_tp_pm_resume, +}; + +static const struct of_device_id tp_match_table[] = { + {.compatible = DTS_OF_NAME}, + {}, +}; + +#ifdef ROI +struct ts_device_ops ilitek_ops = { + .chip_roi_rawdata = ili_knuckle_roi_rawdata, + .chip_roi_switch = ili_knuckle_roi_switch, +}; +#endif + +static struct ilitek_hwif_info hwif = { + .bus_type = TDDI_INTERFACE, + .plat_type = TP_PLAT_QCOM, + .owner = THIS_MODULE, + .name = TDDI_DEV_ID, + .of_match_table = of_match_ptr(tp_match_table), + .plat_probe = ilitek_plat_probe, + .plat_remove = ilitek_plat_remove, + .pm = &tp_pm_ops, +}; + +static int __init ilitek_plat_dev_init(void) +{ + ILI_INFO("ILITEK TP driver init for QCOM\n"); + if (ili_dev_init(&hwif) < 0) { + ILI_ERR("Failed to register i2c/spi bus driver\n"); + return -ENODEV; + } + return 0; +} + +static void __exit ilitek_plat_dev_exit(void) +{ + ILI_INFO("remove plat dev\n"); + ili_dev_remove(ENABLE); +} + +#if defined(__DRM_PANEL_H__) && defined(DRM_PANEL_EARLY_EVENT_BLANK) +late_initcall(ilitek_plat_dev_init); +#else +module_init(ilitek_plat_dev_init); +#endif +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +module_exit(ilitek_plat_dev_exit); +MODULE_AUTHOR("ILITEK"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_spi.c b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_spi.c new file mode 100644 index 000000000000..9347cbb0f4a4 --- /dev/null +++ b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_spi.c @@ -0,0 +1,917 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek_v3.h" + +struct touch_bus_info { + struct spi_driver bus_driver; + struct ilitek_hwif_info *hwif; +}; + +struct ilitek_ts_data *ilits; + +#if defined(ILI_MTK_CHECK_PANEL) || defined(ILI_TP_MODULE_EN) +const char *active_panel_name; +#endif + +#if SPI_DMA_TRANSFER_SPLIT +#if SPI_DMA_TRANSFER_SPLIT_OLD +#define DMA_TRANSFER_MAX_CHUNK 64 /* number of chunks to be transferred. */ +#define DMA_TRANSFER_MAX_LEN 1024 /* length of a chunk. */ + +int ili_spi_write_then_read_split(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx) +{ + int status = -1, duplex_len = 0; + int xfercnt = 0, xferlen = 0, xferloop = 0; + int offset = 0; + u8 cmd = 0; + struct spi_message message; + struct spi_transfer *xfer; + + xfer = kzalloc(DMA_TRANSFER_MAX_CHUNK * sizeof(struct spi_transfer), GFP_KERNEL); + + if (n_rx > SPI_RX_BUF_SIZE) { + ILI_ERR("Rx length is greater than spi local buf, abort\n"); + status = -ENOMEM; + goto out; + } + + spi_message_init(&message); + memset(ilits->spi_tx, 0x0, SPI_TX_BUF_SIZE); + memset(ilits->spi_rx, 0x0, SPI_RX_BUF_SIZE); + + if ((n_tx > 0) && (n_rx > 0)) + cmd = SPI_READ; + else + cmd = SPI_WRITE; + + switch (cmd) { + case SPI_WRITE: + if (n_tx % DMA_TRANSFER_MAX_LEN) + xferloop = (n_tx / DMA_TRANSFER_MAX_LEN) + 1; + else + xferloop = n_tx / DMA_TRANSFER_MAX_LEN; + + xferlen = n_tx; + memcpy(ilits->spi_tx, (u8 *)txbuf, xferlen); + + for (xfercnt = 0; xfercnt < xferloop; xfercnt++) { + if (xferlen > DMA_TRANSFER_MAX_LEN) + xferlen = DMA_TRANSFER_MAX_LEN; + + xfer[xfercnt].len = xferlen; + xfer[xfercnt].tx_buf = ilits->spi_tx + xfercnt * DMA_TRANSFER_MAX_LEN; + spi_message_add_tail(&xfer[xfercnt], &message); + xferlen = n_tx - (xfercnt+1) * DMA_TRANSFER_MAX_LEN; + } + status = spi_sync(spi, &message); + break; + case SPI_READ: + if (n_tx > DMA_TRANSFER_MAX_LEN) { + ILI_ERR("Tx length must be lower than dma length (%d).\n", DMA_TRANSFER_MAX_LEN); + status = -EINVAL; + break; + } + + if (!atomic_read(&ilits->ice_stat)) + offset = 2; + + memcpy(ilits->spi_tx, txbuf, n_tx); + duplex_len = n_tx + n_rx + offset; + + if (duplex_len % DMA_TRANSFER_MAX_LEN) + xferloop = (duplex_len / DMA_TRANSFER_MAX_LEN) + 1; + else + xferloop = duplex_len / DMA_TRANSFER_MAX_LEN; + + xferlen = duplex_len; + for (xfercnt = 0; xfercnt < xferloop; xfercnt++) { + if (xferlen > DMA_TRANSFER_MAX_LEN) + xferlen = DMA_TRANSFER_MAX_LEN; + + xfer[xfercnt].len = xferlen; + xfer[xfercnt].tx_buf = ilits->spi_tx; + xfer[xfercnt].rx_buf = ilits->spi_rx + xfercnt * DMA_TRANSFER_MAX_LEN; + spi_message_add_tail(&xfer[xfercnt], &message); + xferlen = duplex_len - (xfercnt + 1) * DMA_TRANSFER_MAX_LEN; + } + + status = spi_sync(spi, &message); + if (status == 0) { + if (ilits->spi_rx[1] != SPI_ACK && !atomic_read(&ilits->ice_stat)) { + status = DO_SPI_RECOVER; + ILI_ERR("Do spi recovery: rxbuf[1] = 0x%x, ice = %d\n", ilits->spi_rx[1], atomic_read(&ilits->ice_stat)); + break; + } + + memcpy((u8 *)rxbuf, ilits->spi_rx + offset + 1, n_rx); + } else { + ILI_ERR("spi read fail, status = %d\n", status); + } + break; + default: + ILI_INFO("Unknown command 0x%x\n", cmd); + break; + } + +out: + ipio_kfree((void **)&xfer); + return status; +} +#else +#define DMA_TRANSFER_MAX_CHUNK 4 /* number of chunks to be transferred. */ +#define DMA_TRANSFER_MAX_LEN 4096 /* length of a chunk. */ + +int ili_spi_write_then_read_split(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx) +{ + int status = -1, duplex_len = 0; + int xfercnt = 0, xferlen = 0, xferloop = 0; + int offset = 0; + u8 cmd = 0; + struct spi_message message; + struct spi_transfer xfer[DMA_TRANSFER_MAX_CHUNK]; + + if (n_rx > SPI_RX_BUF_SIZE) { + ILI_ERR("Rx length is greater than spi local buf, abort\n"); + status = -ENOMEM; + goto out; + } + + spi_message_init(&message); + memset(xfer, 0, sizeof(xfer)); + memset(ilits->spi_tx, 0x0, SPI_TX_BUF_SIZE); + memset(ilits->spi_rx, 0x0, SPI_RX_BUF_SIZE); + + if ((n_tx > 0) && (n_rx > 0)) + cmd = SPI_READ; + else + cmd = SPI_WRITE; + + switch (cmd) { + case SPI_WRITE: + if (n_tx % DMA_TRANSFER_MAX_LEN) + xferloop = (n_tx / DMA_TRANSFER_MAX_LEN) + 1; + else + xferloop = n_tx / DMA_TRANSFER_MAX_LEN; + + if (xferloop > DMA_TRANSFER_MAX_CHUNK) { + ILI_ERR("xferloop = %d > %d\n", xferloop, DMA_TRANSFER_MAX_CHUNK); + status = -EINVAL; + break; + } + + xferlen = n_tx; + memcpy(ilits->spi_tx, (u8 *)txbuf, xferlen); + + for (xfercnt = 0; xfercnt < xferloop; xfercnt++) { + if (xferlen > DMA_TRANSFER_MAX_LEN) + xferlen = DMA_TRANSFER_MAX_LEN; + + xfer[xfercnt].len = xferlen; + xfer[xfercnt].tx_buf = ilits->spi_tx + xfercnt * DMA_TRANSFER_MAX_LEN; + spi_message_add_tail(&xfer[xfercnt], &message); + xferlen = n_tx - (xfercnt+1) * DMA_TRANSFER_MAX_LEN; + } + status = spi_sync(spi, &message); + break; + case SPI_READ: + if (n_tx > DMA_TRANSFER_MAX_LEN) { + ILI_ERR("Tx length must be lower than dma length (%d).\n", DMA_TRANSFER_MAX_LEN); + status = -EINVAL; + break; + } + + if (!atomic_read(&ilits->ice_stat)) + offset = 2; + + memcpy(ilits->spi_tx, txbuf, n_tx); + duplex_len = n_tx + n_rx + offset; + + if (duplex_len % DMA_TRANSFER_MAX_LEN) + xferloop = (duplex_len / DMA_TRANSFER_MAX_LEN) + 1; + else + xferloop = duplex_len / DMA_TRANSFER_MAX_LEN; + + if (xferloop > DMA_TRANSFER_MAX_CHUNK) { + ILI_ERR("xferloop = %d > %d\n", xferloop, DMA_TRANSFER_MAX_CHUNK); + status = -EINVAL; + break; + } + + xferlen = duplex_len; + for (xfercnt = 0; xfercnt < xferloop; xfercnt++) { + if (xferlen > DMA_TRANSFER_MAX_LEN) + xferlen = DMA_TRANSFER_MAX_LEN; + + xfer[xfercnt].len = xferlen; + xfer[xfercnt].tx_buf = ilits->spi_tx; + xfer[xfercnt].rx_buf = ilits->spi_rx + xfercnt * DMA_TRANSFER_MAX_LEN; + spi_message_add_tail(&xfer[xfercnt], &message); + xferlen = duplex_len - (xfercnt + 1) * DMA_TRANSFER_MAX_LEN; + } + + status = spi_sync(spi, &message); + if (status == 0) { + if (ilits->spi_rx[1] != SPI_ACK && !atomic_read(&ilits->ice_stat)) { + status = DO_SPI_RECOVER; + ILI_ERR("Do spi recovery: rxbuf[1] = 0x%x, ice = %d\n", ilits->spi_rx[1], atomic_read(&ilits->ice_stat)); + break; + } + + memcpy((u8 *)rxbuf, ilits->spi_rx + offset + 1, n_rx); + } else { + ILI_ERR("spi read fail, status = %d\n", status); + } + break; + default: + ILI_INFO("Unknown command 0x%x\n", cmd); + break; + } + +out: + return status; +} +#endif +#else +int ili_spi_write_then_read_direct(struct spi_device *spi, + const void *txbuf, unsigned n_tx, + void *rxbuf, unsigned n_rx) +{ + int status = -1, duplex_len = 0; + int offset = 0; + u8 cmd; + struct spi_message message; + struct spi_transfer xfer; + + if (n_rx > SPI_RX_BUF_SIZE) { + ILI_ERR("Rx length is greater than spi local buf, abort\n"); + status = -ENOMEM; + goto out; + } + + spi_message_init(&message); + memset(&xfer, 0, sizeof(xfer)); + + if ((n_tx > 0) && (n_rx > 0)) + cmd = SPI_READ; + else + cmd = SPI_WRITE; + + switch (cmd) { + case SPI_WRITE: + xfer.len = n_tx; + xfer.tx_buf = txbuf; + spi_message_add_tail(&xfer, &message); + status = spi_sync(spi, &message); + break; + case SPI_READ: + if (!atomic_read(&ilits->ice_stat)) + offset = 2; + + duplex_len = n_tx + n_rx + offset; + if ((duplex_len > SPI_TX_BUF_SIZE) || + (duplex_len > SPI_RX_BUF_SIZE)) { + ILI_ERR("duplex_len is over than dma buf, abort\n"); + status = -ENOMEM; + break; + } + + memset(ilits->spi_tx, 0x0, SPI_TX_BUF_SIZE); + memset(ilits->spi_rx, 0x0, SPI_RX_BUF_SIZE); + + xfer.len = duplex_len; + memcpy(ilits->spi_tx, txbuf, n_tx); + xfer.tx_buf = ilits->spi_tx; + xfer.rx_buf = ilits->spi_rx; + + spi_message_add_tail(&xfer, &message); + status = spi_sync(spi, &message); + if (status == 0) { + if (ilits->spi_rx[1] != SPI_ACK && !atomic_read(&ilits->ice_stat)) { + status = DO_SPI_RECOVER; + ILI_ERR("Do spi recovery: rxbuf[1] = 0x%x, ice = %d\n", ilits->spi_rx[1], atomic_read(&ilits->ice_stat)); + break; + } + + memcpy((u8 *)rxbuf, ilits->spi_rx + offset + 1, n_rx); + } else { + ILI_ERR("spi read fail, status = %d\n", status); + } + break; + default: + ILI_INFO("Unknown command 0x%x\n", cmd); + break; + } + +out: + return status; +} +#endif + +static int ili_spi_mp_pre_cmd(u8 cdc) +{ + u8 pre[5] = {0}; + + if (!atomic_read(&ilits->mp_stat) || cdc != P5_X_SET_CDC_INIT || + ilits->chip->core_ver >= CORE_VER_1430) + return 0; + + ILI_DBG("mp test with pre commands\n"); + + pre[0] = SPI_WRITE; + pre[1] = 0x0;/* dummy byte */ + pre[2] = 0x2;/* Write len byte */ + pre[3] = P5_X_READ_DATA_CTRL; + pre[4] = P5_X_GET_CDC_DATA; + if (ilits->spi_write_then_read(ilits->spi, pre, 5, NULL, 0) < 0) { + ILI_ERR("Failed to write pre commands\n"); + return -1; + } + + pre[0] = SPI_WRITE; + pre[1] = 0x0;/* dummy byte */ + pre[2] = 0x1;/* Write len byte*/ + pre[3] = P5_X_GET_CDC_DATA; + if (ilits->spi_write_then_read(ilits->spi, pre, 4, NULL, 0) < 0) { + ILI_ERR("Failed to write pre commands\n"); + return -1; + } + return 0; +} + +static int ili_spi_pll_clk_wakeup(void) +{ + int index = 0; + u8 wdata[32] = {0}; + u8 wakeup[9] = {0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3}; + u32 wlen = sizeof(wakeup); + + wdata[0] = SPI_WRITE; + wdata[1] = wlen >> 8; + wdata[2] = wlen & 0xff; + index = 3; + + ipio_memcpy(&wdata[index], wakeup, wlen, wlen); + + wlen += index; + + ILI_INFO("Write dummy to wake up spi pll clk\n"); + if (ilits->spi_write_then_read(ilits->spi, wdata, wlen, NULL, 0) < 0) { + ILI_INFO("spi slave write error\n"); + return -1; + } + mdelay(1); + return 0; +} + +static int ili_spi_wrapper(u8 *txbuf, u32 wlen, u8 *rxbuf, u32 rlen, bool spi_irq, bool i2c_irq) +{ + int ret = 0; + int mode = 0, index = 0; + u8 wdata[128] = {0}; + u8 checksum = 0; + bool ice = atomic_read(&ilits->ice_stat); + + if (wlen > 0) { + if (!txbuf) { + ILI_ERR("txbuf is null\n"); + return -ENOMEM; + } + + /* 3 bytes data consist of length and header */ + if ((wlen + 4) > sizeof(wdata)) { + ILI_ERR("WARNING! wlen(%d) > wdata(%d), using wdata length to transfer\n", wlen, (int)sizeof(wdata)); + wlen = sizeof(wdata) - 4; + } + } + + if (rlen > 0) { + if (!rxbuf) { + ILI_ERR("rxbuf is null\n"); + return -ENOMEM; + } + } + + if (rlen > 0 && !wlen) + mode = SPI_READ; + else + mode = SPI_WRITE; + + if (ilits->int_pulse) + ilits->detect_int_stat = ili_ic_check_int_pulse; + else + ilits->detect_int_stat = ili_ic_check_int_level; + + if (spi_irq) + atomic_set(&ilits->cmd_int_check, ENABLE); + + switch (mode) { + case SPI_WRITE: + atomic_set(&ilits->ignore_report, START); +#if (PLL_CLK_WAKEUP_TP_RESUME == ENABLE) + if (ilits->pll_clk_wakeup == true) { +#else + if ((ilits->pll_clk_wakeup == true) && (ilits->tp_suspend == true)) { +#endif + ret = ili_spi_pll_clk_wakeup(); + if (ret < 0) { + ILI_ERR("Wakeup pll clk error\n"); + break; + } + } + if (ice) { + wdata[0] = SPI_WRITE; + index = 1; + } else { + wdata[0] = SPI_WRITE; + wdata[1] = wlen >> 8; + wdata[2] = wlen & 0xff; + index = 3; + } + + ipio_memcpy(&wdata[index], txbuf, wlen, wlen); + + wlen += index; + + /* + * NOTE: If TP driver is doing MP test and commanding 0xF1 to FW, we add a checksum + * to the last index and plus 1 with size. + */ + if (atomic_read(&ilits->mp_stat) && wdata[index] == P5_X_SET_CDC_INIT) { + checksum = ili_calc_packet_checksum(&wdata[index], wlen - index); + wdata[wlen] = checksum; + wlen++; + wdata[1] = (wlen - index) >> 8; + wdata[2] = (wlen - index) & 0xff; + ili_dump_data(wdata, 8, wlen, 0, "mp cdc cmd with checksum"); + } + + ret = ilits->spi_write_then_read(ilits->spi, wdata, wlen, txbuf, 0); + if (!ice) { + ILI_DBG("send cmd delay 1ms\n"); + mdelay(1); + } + + if (ret < 0) { + ILI_INFO("spi-wrapper write error\n"); + break; + } + + /* Won't break if it needs to read data following with writing. */ + if (!rlen) + break; + #if 0 /* if warning: this statement may fall through modify 0 to 1 */ + else { + if (!ice && spi_irq) { + /* Check INT triggered by FW when sending cmds. */ + if (ilits->detect_int_stat(false) < 0) { + ILI_ERR("ERROR! Check INT timeout\n"); + ret = -ETIME; + break; + } + } + + ret = ili_spi_mp_pre_cmd(wdata[3]); + if (ret < 0) + ILI_ERR("spi-wrapper mp pre cmd error\n"); + + wdata[0] = SPI_READ; + + ret = ilits->spi_write_then_read(ilits->spi, wdata, 1, rxbuf, rlen); + if (ret < 0) + ILI_ERR("spi-wrapper read error\n"); + + break; + } + #endif + case SPI_READ: + if (!ice && spi_irq) { + /* Check INT triggered by FW when sending cmds. */ + if (ilits->detect_int_stat(false) < 0) { + ILI_ERR("ERROR! Check INT timeout\n"); + ret = -ETIME; + break; + } + } + + ret = ili_spi_mp_pre_cmd(wdata[3]); + if (ret < 0) + ILI_ERR("spi-wrapper mp pre cmd error\n"); + + wdata[0] = SPI_READ; + + ret = ilits->spi_write_then_read(ilits->spi, wdata, 1, rxbuf, rlen); + if (ret < 0) + ILI_ERR("spi-wrapper read error\n"); + + break; + default: + ILI_ERR("Unknown spi mode (%d)\n", mode); + ret = -EINVAL; + break; + } + + atomic_set(&ilits->ignore_report, END); + + if (spi_irq) + atomic_set(&ilits->cmd_int_check, DISABLE); + + return ret; +} + +int ili_core_spi_setup(int num) +{ + u32 freq[] = { + TP_SPI_CLK_1M, + TP_SPI_CLK_2M, + TP_SPI_CLK_3M, + TP_SPI_CLK_4M, + TP_SPI_CLK_5M, + TP_SPI_CLK_6M, + TP_SPI_CLK_7M, + TP_SPI_CLK_8M, + TP_SPI_CLK_9M, + TP_SPI_CLK_10M, + TP_SPI_CLK_11M, + TP_SPI_CLK_12M, + TP_SPI_CLK_13M, + TP_SPI_CLK_14M, + TP_SPI_CLK_15M + }; + + if (num >= ARRAY_SIZE(freq)) { + ILI_ERR("Invaild clk freq, set default clk freq\n"); + num = 7; + } + + ILI_INFO("spi clock = %d\n", freq[num]); + + ilits->spi->mode = SPI_MODE_0; + ilits->spi->bits_per_word = 8; + ilits->spi->max_speed_hz = freq[num]; + ilits->spi->chip_select = 0; + + if (spi_setup(ilits->spi) < 0) { + ILI_ERR("Failed to setup spi device\n"); + return -ENODEV; + } + + ILI_INFO("name = %s, bus_num = %d,cs = %d, mode = %d, speed = %d\n", + ilits->spi->modalias, + ilits->spi->master->bus_num, + ilits->spi->chip_select, + ilits->spi->mode, + ilits->spi->max_speed_hz); + return 0; +} + +void ili_parse_tp_module(void) +{ + int tp_module = 0; + +#ifdef ILI_TP_MODULE_EN + if(active_panel_name) { + if (strstr(active_panel_name, "tm") || strstr(active_panel_name, "tianma")) { + tp_module = MODEL_TM; + } + else if (strstr(active_panel_name, "djn")) { + tp_module = MODEL_DJ; + } + else if (strstr(active_panel_name, "txd")) { + tp_module = MODEL_TXD; + } + else if (strstr(active_panel_name, "csot")) { + tp_module = MODEL_CSOT; + } + else if (strstr(active_panel_name, "boe")) { + tp_module = MODEL_BOE; + } + else if (strstr(active_panel_name, "auo")) { + tp_module = MODEL_AUO; + } + else + ILI_INFO("MODEL for panel:%s to be added!\n", active_panel_name); + + if (tp_module) + ILI_INFO("get tp_module:%d\n", tp_module); + } + else + ILI_INFO("active_panel NULL\n"); + +#endif + + ilits->tp_module = tp_module; +} + +static int parse_dt(struct device_node *np) +{ + int32_t ret = 0; + +#ifdef ILI_DOUBLE_TAP_CTRL + uint32_t value; + + if (!of_property_read_u32(np, "ilitek,supported_gesture_type", &value)) { + ilits->supported_gesture_type = (uint8_t)value; + ILI_INFO("supported_gesture_type=%d\n", ilits->supported_gesture_type); + } + else + ILI_INFO("warn: supported_gesture_type not set\n"); + + if (of_property_read_bool(np, "ilitek,rst-pull-flag")) { + ILI_INFO("ilitek,rst pull flag set"); + ilits->rst_pull_flag = 1; + } else { + ilits->rst_pull_flag = 0; + } +#endif //ILI_DOUBLE_TAP_CTRL + +#ifdef ILI_SENSOR_EN + if (of_property_read_bool(np, "ilitek,report_gesture_key")) { + ILI_INFO("ilitek,report_gesture_key set"); + ilits->report_gesture_key = 1; + } else { + ilits->report_gesture_key = 0; + } +#endif + +#if CHARGER_NOTIFIER_CALLBACK + ret = of_property_read_string(np, "touch,psy-name", &ilits->psy_name); + if (ilits->psy_name) + ILI_INFO("%s: get psy_name:%s", __func__, ilits->psy_name); +#endif + + return ret; +} + +#ifdef ILI_MTK_CHECK_PANEL +static void ili_get_active_panel(void) +{ + int rc; + struct device_node *chosen = of_find_node_by_name(NULL, "chosen"); + + if(chosen) { + rc = of_property_read_string(chosen, "mmi,panel_name", (const char **)&active_panel_name); + if (rc) + ILI_INFO("mmi,panel_name null\n"); + else + ILI_DBG("active_panel_name=%s\n", active_panel_name); + } + else + ILI_INFO("chosen node null\n"); + +} + +static int ili_check_panel(void) +{ + ILI_DBG("enter"); + ili_get_active_panel(); + + if (!active_panel_name) + ILI_INFO("active_panel NULL\n"); + else if(strstr(active_panel_name, "ili78") || strstr(active_panel_name, "ili98") || strstr(active_panel_name, "il77")) + { + ILI_INFO("matched active_panel:%s ", active_panel_name); + return 0; + } + else + ILI_DBG("not macthed active_panel:%s\n", active_panel_name); + + return -1; +} + +#endif + +static int ilitek_spi_probe(struct spi_device *spi) +{ + struct touch_bus_info *info = + container_of(to_spi_driver(spi->dev.driver), + struct touch_bus_info, bus_driver); + +#ifdef ILI_MTK_CHECK_PANEL + int ret; +#endif + + ILI_INFO("ilitek spi probe\n"); + + if (!spi) { + ILI_ERR("spi device is NULL\n"); + return -ENODEV; + } + +#ifdef ILI_MTK_CHECK_PANEL + ret = ili_check_panel(); + if (ret) { + ILI_INFO("check panel error\n"); + return ret; + } +#endif + + ilits = devm_kzalloc(&spi->dev, sizeof(struct ilitek_ts_data), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits)) { + ILI_ERR("Failed to allocate ts memory, %ld\n", PTR_ERR(ilits)); + return -ENOMEM; + } + + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) { + ILI_ERR("Full duplex not supported by master\n"); + return -EIO; + } + + ilits->update_buf = kzalloc(MAX_HEX_FILE_SIZE, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(ilits->update_buf)) { + ILI_ERR("fw kzalloc error\n"); + return -ENOMEM; + } + + /* Used for receiving touch data only, do not mix up with others. */ + ilits->tr_buf = kzalloc(TR_BUF_SIZE, GFP_ATOMIC); + if (ERR_ALLOC_MEM(ilits->tr_buf)) { + ILI_ERR("failed to allocate touch report buffer\n"); + return -ENOMEM; + } + + ilits->spi_tx = kzalloc(SPI_TX_BUF_SIZE, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(ilits->spi_tx)) { + ILI_ERR("Failed to allocate spi tx buffer\n"); + return -ENOMEM; + } + + ilits->spi_rx = kzalloc(SPI_RX_BUF_SIZE, GFP_KERNEL | GFP_DMA); + if (ERR_ALLOC_MEM(ilits->spi_rx)) { + ILI_ERR("Failed to allocate spi rx buffer\n"); + return -ENOMEM; + } + + ilits->gcoord = kzalloc(sizeof(struct gesture_coordinate), GFP_KERNEL); + if (ERR_ALLOC_MEM(ilits->gcoord)) { + ILI_ERR("Failed to allocate gresture coordinate buffer\n"); + return -ENOMEM; + } + + ilits->i2c = NULL; + ilits->spi = spi; + ilits->dev = &spi->dev; + ilits->hwif = info->hwif; + ilits->phys = "SPI"; + ilits->wrapper = ili_spi_wrapper; + ilits->detect_int_stat = ili_ic_check_int_pulse; + ilits->int_pulse = true; + ilits->mp_retry = false; + +#if SPI_DMA_TRANSFER_SPLIT + ilits->spi_write_then_read = ili_spi_write_then_read_split; +#else + ilits->spi_write_then_read = ili_spi_write_then_read_direct; +#endif + + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + ilits->tp_data_format = DATA_FORMAT_DEMO; + ilits->tp_data_len = P5_X_DEMO_MODE_PACKET_LEN; + ilits->tp_data_mode = AP_MODE; + + if (TDDI_RST_BIND) + ilits->reset = TP_IC_WHOLE_RST_WITH_FLASH; + else + ilits->reset = TP_HW_RST_ONLY; + + ilits->rst_edge_delay = 10; + ilits->fw_open = REQUEST_FIRMWARE; + ilits->fw_upgrade_mode = UPGRADE_IRAM; + ilits->mp_move_code = ili_move_mp_code_iram; + ilits->gesture_move_code = ili_move_gesture_code_iram; + ilits->esd_recover = ili_wq_esd_spi_check; + ilits->esd_func_ctrl = ENABLE_WQ_ESD; + ilits->ges_recover = ili_touch_esd_gesture_iram; + ilits->gesture_mode = DATA_FORMAT_GESTURE_INFO; + ilits->gesture_demo_ctrl = DISABLE; + ilits->wtd_ctrl = OFF; + ilits->report = ENABLE; + ilits->dnp = DISABLE; + ilits->dlnp = DISABLE; + ilits->irq_tirgger_type = IRQF_TRIGGER_FALLING; + ilits->info_from_hex = ENABLE; + ilits->wait_int_timeout = AP_INT_TIMEOUT; + ilits->ice_flash_cs_ctrl = ENABLE; + ilits->compress_disable = ENABLE; + ilits->compress_handonly_disable = ENABLE; + ilits->compress_penonly_disable = ENABLE; +#if MULTI_REPORT_RATE + ilits->current_report_rate_mode = DDI60_TP120; /*Default report mode*/ +#endif + + //---parse dts--- + parse_dt(spi->dev.of_node); + + //---parse tp module--- + ili_parse_tp_module(); + +#if ENABLE_GESTURE + ilits->gesture = ENABLE; + ilits->ges_sym.double_tap = DOUBLE_TAP; + ilits->ges_sym.alphabet_line_2_top = ALPHABET_LINE_2_TOP; + ilits->ges_sym.alphabet_line_2_bottom = ALPHABET_LINE_2_BOTTOM; + ilits->ges_sym.alphabet_line_2_left = ALPHABET_LINE_2_LEFT; + ilits->ges_sym.alphabet_line_2_right = ALPHABET_LINE_2_RIGHT; + ilits->ges_sym.alphabet_m = ALPHABET_M; + ilits->ges_sym.alphabet_w = ALPHABET_W; + ilits->ges_sym.alphabet_c = ALPHABET_C; + ilits->ges_sym.alphabet_E = ALPHABET_E; + ilits->ges_sym.alphabet_V = ALPHABET_V; + ilits->ges_sym.alphabet_O = ALPHABET_O; + ilits->ges_sym.alphabet_S = ALPHABET_S; + ilits->ges_sym.alphabet_Z = ALPHABET_Z; + ilits->ges_sym.alphabet_V_down = ALPHABET_V_DOWN; + ilits->ges_sym.alphabet_V_left = ALPHABET_V_LEFT; + ilits->ges_sym.alphabet_V_right = ALPHABET_V_RIGHT; + ilits->ges_sym.alphabet_two_line_2_bottom = ALPHABET_TWO_LINE_2_BOTTOM; + ilits->ges_sym.alphabet_F = ALPHABET_F; + ilits->ges_sym.alphabet_AT = ALPHABET_AT; + + ILI_INFO("gesture eanble:%d\n", ilits->gesture); +#endif + + if (ili_core_spi_setup(SPI_CLK) < 0) + return -EINVAL; + + return info->hwif->plat_probe(); +} + +static int ilitek_spi_remove(struct spi_device *spi) +{ + ILI_INFO(); + return 0; +} + +static struct spi_device_id tp_spi_id[] = { + {TDDI_DEV_ID, 0}, + {}, +}; + +int ili_interface_dev_init(struct ilitek_hwif_info *hwif) +{ + struct touch_bus_info *info; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + ILI_ERR("faied to allocate spi_driver\n"); + return -ENOMEM; + } + + if (hwif->bus_type != BUS_SPI) { + ILI_ERR("Not SPI dev\n"); + ipio_kfree((void **)&info); + return -EINVAL; + } + + hwif->info = info; + + info->bus_driver.driver.name = hwif->name; + info->bus_driver.driver.owner = hwif->owner; + info->bus_driver.driver.of_match_table = hwif->of_match_table; + info->bus_driver.driver.pm = hwif->pm; + + info->bus_driver.probe = ilitek_spi_probe; + info->bus_driver.remove = ilitek_spi_remove; + info->bus_driver.id_table = tp_spi_id; + + info->hwif = hwif; + return spi_register_driver(&info->bus_driver); +} + +void ili_interface_dev_exit(struct ilitek_ts_data *ts, bool flag) +{ + struct touch_bus_info *info = (struct touch_bus_info *)ilits->hwif->info; + + ILI_INFO("remove spi dev\n"); + kfree(ilits->update_buf); + kfree(ilits->spi_tx); + kfree(ilits->spi_rx); + + if (flag == ENABLE) { + spi_unregister_driver(&info->bus_driver); + ipio_kfree((void **)&info); + } +} diff --git a/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_touch.c b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_touch.c new file mode 100644 index 000000000000..f66cf2bf14af --- /dev/null +++ b/drivers/input/touchscreen/ilitek_v3_mmi/ilitek_v3_touch.c @@ -0,0 +1,2380 @@ +/* + * ILITEK Touch IC driver + * + * Copyright (C) 2011 ILI Technology Corporation. + * + * Author: Dicky Chiang + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ilitek_v3.h" + +/*gesture info mode*/ +struct ili_demo_debug_info_id0 { + u32 id : 8; + u32 sys_powr_state_e : 3; + u32 sys_state_e : 3; + u32 tp_state_e : 2; + + u32 touch_palm_state : 2; + u32 app_an_statu_e : 3; + u32 app_sys_bg_err : 1; + u32 g_b_wrong_bg : 1; + u32 reserved0 : 1; + + u32 normal_mode : 1; + u32 charger_mode : 1; + u32 glove_mode : 1; + u32 stylus_mode : 1; + u32 multi_mode : 1; + u32 noise_mode : 1; + u32 palm_plus_mode : 1; + u32 floating_mode : 1; + + u32 algo_pt_status0 : 3; + u32 algo_pt_status1 : 3; + u32 algo_pt_status2 : 3; + u32 algo_pt_status3 : 3; + u32 algo_pt_status4 : 3; + u32 algo_pt_status5 : 3; + u32 algo_pt_status6 : 3; + u32 algo_pt_status7 : 3; + u32 algo_pt_status8 : 3; + u32 algo_pt_status9 : 3; + u32 reserved2 : 2; + + u32 hopping_flg : 1; + u32 hopping_index : 5; + u32 frequency_h : 2; + u32 frequency_l : 8; + u32 reserved3 : 8; + u32 reserved4 : 8; + +}; + +void ili_dump_data(void *data, int type, int len, int row_len, const char *name) +{ + int i, row = 31; + u8 *p8 = NULL; + s32 *p32 = NULL; + s16 *p16 = NULL; + + if (!debug_en) + return; + + if (row_len > 0) + row = row_len; + + if (data == NULL) { + ILI_ERR("The data going to dump is NULL\n"); + return; + } + + pr_cont("\n\n"); + pr_cont("ILITEK: Dump %s data\n", name); + pr_cont("ILITEK: "); + + if (type == 8) + p8 = (u8 *) data; + if (type == 32 || type == 10) + p32 = (s32 *) data; + if (type == 16) + p16 = (s16 *) data; + + for (i = 0; i < len; i++) { + if (type == 8) + pr_cont(" %4x ", p8[i]); + else if (type == 32) + pr_cont(" %4x ", p32[i]); + else if (type == 10) + pr_cont(" %4d ", p32[i]); + else if (type == 16) + pr_cont(" %4d ", p16[i]); + + if ((i % row) == row - 1) { + pr_cont("\n"); + pr_cont("ILITEK: "); + } + } + pr_cont("\n\n"); +} + +static void dma_clear_reg_setting(void) +{ + /* 1. interrupt t0/t1 enable flag */ + if (ili_ice_mode_bit_mask_write(INTR32_ADDR, INTR32_reg_t0_int_en | INTR32_reg_t1_int_en, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR32_reg_t0_int_en | INTR32_reg_t1_int_en, INTR32_ADDR); + + /* 2. clear tdi_err_int_flag */ + if (ili_ice_mode_bit_mask_write(INTR2_ADDR, INTR2_tdi_err_int_flag_clear, (1 << 18)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR2_tdi_err_int_flag_clear, INTR2_ADDR); + + /* 3. clear dma channel 0 src1 info */ + if (ili_ice_mode_write(DMA49_reg_dma_ch0_src1_addr, 0x00000000, 4) < 0) + ILI_ERR("Write 0x00000000 at %x failed\n", DMA49_reg_dma_ch0_src1_addr); + if (ili_ice_mode_write(DMA50_reg_dma_ch0_src1_step_inc, 0x00, 1) < 0) + ILI_ERR("Write 0x0 at %x failed\n", DMA50_reg_dma_ch0_src1_step_inc); + if (ili_ice_mode_bit_mask_write(DMA50_ADDR, DMA50_reg_dma_ch0_src1_format | DMA50_reg_dma_ch0_src1_en, BIT(31)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA50_reg_dma_ch0_src1_format | DMA50_reg_dma_ch0_src1_en, DMA50_ADDR); + + /* 4. clear dma channel 0 trigger select */ + if (ili_ice_mode_bit_mask_write(DMA48_ADDR, DMA48_reg_dma_ch0_trigger_sel, (0 << 16)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA48_reg_dma_ch0_trigger_sel, DMA48_ADDR); + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_flash_int_flag, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_flash_int_flag, INTR1_ADDR); + + /* 5. clear dma flash setting */ + ili_flash_clear_dma(); +} + +static void dma_trigger_reg_setting(u32 reg_dest_addr, u32 flash_start_addr, u32 copy_size) +{ + int retry = 30; + u32 stat = 0; + + /* 1. set dma channel 0 clear */ + if (ili_ice_mode_bit_mask_write(DMA48_ADDR, DMA48_reg_dma_ch0_start_clear, (1 << 25)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA48_reg_dma_ch0_start_clear, DMA48_ADDR); + + /* 2. set dma channel 0 src1 info */ + if (ili_ice_mode_write(DMA49_reg_dma_ch0_src1_addr, 0x00041010, 4) < 0) + ILI_ERR("Write 0x00041010 at %x failed\n", DMA49_reg_dma_ch0_src1_addr); + if (ili_ice_mode_write(DMA50_reg_dma_ch0_src1_step_inc, 0x00, 1) < 0) + ILI_ERR("Write 0x00 at %x failed\n", DMA50_reg_dma_ch0_src1_step_inc); + if (ili_ice_mode_bit_mask_write(DMA50_ADDR, DMA50_reg_dma_ch0_src1_format | DMA50_reg_dma_ch0_src1_en, BIT(31)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA50_reg_dma_ch0_src1_format | DMA50_reg_dma_ch0_src1_en, DMA50_ADDR); + + /* 3. set dma channel 0 src2 info */ + if (ili_ice_mode_bit_mask_write(DMA52_ADDR, DMA52_reg_dma_ch0_src2_en, (0 << 31)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA52_reg_dma_ch0_src2_en, DMA52_ADDR); + + /* 4. set dma channel 0 dest info */ + if (ili_ice_mode_write(DMA53_reg_dma_ch0_dest_addr, reg_dest_addr, 3) < 0) + ILI_ERR("Write %x at %x failed\n", reg_dest_addr, DMA53_reg_dma_ch0_dest_addr); + if (ili_ice_mode_write(DMA54_reg_dma_ch0_dest_step_inc, 0x01, 1) < 0) + ILI_ERR("Write 0x01 at %x failed\n", DMA54_reg_dma_ch0_dest_step_inc); + + if (ili_ice_mode_write(DMA54_ADDR, DMA54_reg_dma_ch0_dest_format | DMA54_reg_dma_ch0_dest_en, BIT(31)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA54_reg_dma_ch0_dest_format | DMA54_reg_dma_ch0_dest_en, DMA54_ADDR); + + /* 5. set dma channel 0 trafer info */ + if (ili_ice_mode_write(DMA55_reg_dma_ch0_trafer_counts, copy_size, 4) < 0) + ILI_ERR("Write %x at %x failed\n", copy_size, DMA55_reg_dma_ch0_trafer_counts); + if (ili_ice_mode_bit_mask_write(DMA55_ADDR, DMA55_reg_dma_ch0_trafer_mode, (0 << 24)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA55_reg_dma_ch0_trafer_mode, DMA55_ADDR); + + /* 6. set dma channel 0 int info */ + if (ili_ice_mode_bit_mask_write(INTR33_ADDR, INTR33_reg_dma_ch0_int_en, (1 << 17)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR33_reg_dma_ch0_int_en, INTR33_ADDR); + + /* 7. set dma channel 0 trigger select */ + if (ili_ice_mode_bit_mask_write(DMA48_ADDR, DMA48_reg_dma_ch0_trigger_sel, (1 << 16)) < 0) + ILI_ERR("Write %lu at %x failed\n", DMA48_reg_dma_ch0_trigger_sel, DMA48_ADDR); + + /* 8. set dma flash setting */ + ili_flash_dma_write(flash_start_addr, (flash_start_addr+copy_size), copy_size); + + /* 9. clear flash and dma ch0 int flag */ + if (ili_ice_mode_bit_mask_write(INTR1_ADDR, INTR1_reg_dma_ch1_int_flag | INTR1_reg_flash_int_flag, BIT(16) | BIT(25)) < 0) + ILI_ERR("Write %lu at %x failed\n", INTR1_reg_dma_ch1_int_flag | INTR1_reg_flash_int_flag, INTR1_ADDR); + if (ili_ice_mode_bit_mask_write(0x041013, BIT(0), 1) < 0) /* patch */ + ILI_ERR("Write %lu at %x failed\n", BIT(0), 0x041013); + + /* DMA Trigger */ + if (ili_ice_mode_write(FLASH4_reg_rcv_data, 0xFF, 1) < 0) + ILI_ERR("Trigger DMA failed\n"); + + /* waiting for fw reload code completed. */ + while (retry > 0) { + if (ili_ice_mode_read(INTR1_ADDR, &stat, sizeof(u32)) < 0) { + ILI_ERR("Read 0x%x error\n", INTR1_ADDR); + retry--; + continue; + } + + ILI_DBG("fw dma stat = %x\n", stat); + + if ((stat & BIT(16)) == BIT(16)) + break; + + retry--; + usleep_range(1000, 1000); + } + + if (retry <= 0) + ILI_ERR("DMA fail: Regsiter = 0x%x Flash = 0x%x, Size = %d\n", + reg_dest_addr, flash_start_addr, copy_size); + + /* CS High */ + if (ili_ice_mode_write(FLASH0_reg_flash_csb, 0x1, 1) < 0) + ILI_ERR("Pull CS High failed\n"); + /* waiting for CS status done */ + mdelay(10); +} + +int ili_move_mp_code_flash(void) +{ + int ret = 0, retry = 10; + u32 mp_text_size = 0, mp_andes_init_size = 0, stat = 0; + u32 mp_flash_addr, mp_size, overlay_start_addr, overlay_end_addr; + bool dma_trigger_enable = 0; + u8 cmd[2] = {0}; + u8 data[16] = {0}; + + cmd[0] = P5_X_MP_TEST_MODE_INFO; + ret = ilits->wrapper(cmd, sizeof(u8), data, ilits->protocol->mp_info_len, ON, OFF); + ili_dump_data(data, 8, ilits->protocol->mp_info_len, 0, "MP overlay info"); + if (ret < 0) { + ILI_ERR("Failed to write info cmd\n"); + goto out; + } + + dma_trigger_enable = 0; + + mp_flash_addr = data[3] + (data[2] << 8) + (data[1] << 16); + mp_size = data[6] + (data[5] << 8) + (data[4] << 16); + overlay_start_addr = data[9] + (data[8] << 8) + (data[7] << 16); + overlay_end_addr = data[12] + (data[11] << 8) + (data[10] << 16); + + if (overlay_start_addr != 0x0 && overlay_end_addr != 0x0 + && data[0] == P5_X_MP_TEST_MODE_INFO) + dma_trigger_enable = 1; + + ILI_INFO("MP info Overlay: Enable = %d, addr = 0x%x ~ 0x%x, flash addr = 0x%x, mp size = 0x%x\n", + dma_trigger_enable, overlay_start_addr, + overlay_end_addr, mp_flash_addr, mp_size); + + if (ilits->chip->core_ver >= CORE_VER_1700) { + cmd[0] = P5_X_NEW_CONTROL_FORMAT; + } else { + cmd[0] = P5_X_MODE_CONTROL; + } + cmd[1] = P5_X_FW_TEST_MODE; + ret = ilits->wrapper(cmd, 2, NULL, 0, ON, OFF); + if (ret < 0) + goto out; + + /* Check if ic is ready switching test mode from demo mode */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + ret = ili_ic_check_busy(20, 50, ON); /* Set busy as 0x41 */ + if (ret < 0) + goto out; + + ilits->ice_flash_cs_ctrl = DISABLE; + if (ilits->cascade_info_block.nNum != 0) { + ret = ili_ice_mode_ctrl_by_mode(ENABLE, OFF, BOTH); + } else { + ret = ili_ice_mode_ctrl(ENABLE, OFF); + } + ilits->ice_flash_cs_ctrl = ENABLE; + + if (ret < 0) + goto out; + + if (dma_trigger_enable) { + mp_andes_init_size = overlay_start_addr; + mp_text_size = (mp_size - overlay_end_addr) + 1; + ILI_INFO("MP andes init size = %d , MP text size = %d\n", mp_andes_init_size, mp_text_size); + + dma_clear_reg_setting(); + + ILI_INFO("[Move ANDES.INIT to DRAM]\n"); + dma_trigger_reg_setting(0, mp_flash_addr, mp_andes_init_size); /* DMA ANDES.INIT */ + + dma_clear_reg_setting(); + + ILI_INFO("[Move MP.TEXT to DRAM]\n"); + dma_trigger_reg_setting(overlay_end_addr, (mp_flash_addr + overlay_start_addr), mp_text_size); + + dma_clear_reg_setting(); + } else { + if (ilits->cascade_info_block.nNum != 0) { + /* DMA Trigger */ + if (ili_ice_mode_write_by_mode(FLASH4_reg_rcv_data, 0xFF, 1, BOTH) < 0) + ILI_ERR("Trigger DMA failed\n"); + + /* waiting for fw reload code completed. */ + while (retry > 0) { + if (ili_ice_mode_read_by_mode(INTR1_FLASH_INT_FLAG, &stat, sizeof(u32), MASTER) < 0) { + ILI_ERR("Read 0x%x error\n", INTR1_FLASH_INT_FLAG); + retry--; + continue; + } + + ILI_DBG("fw dma stat = 0x%x\n", stat); + + if ((stat & BIT(1)) == BIT(1)) + break; + + retry--; + usleep_range(10000, 10000); + } + + if (retry <= 0) + ILI_ERR("Polling DMA fail: Regsiter = 0x%X state = 0x%X\n", INTR1_FLASH_INT_FLAG, stat); + + /* CS High */ + if (ili_ice_mode_write_by_mode(FLASH0_reg_flash_csb, 0x1, 1, BOTH) < 0) + ILI_ERR("Pull CS High failed\n"); + + /* waiting for CS status done */ + mdelay(10); + } else { + if (ili_ice_mode_write(FLASH4_reg_rcv_data, 0xFF, 1) < 0) + ILI_ERR("Trigger DMA failed\n"); + + /* waiting for fw reload code completed. */ + while (retry > 0) { + if (ili_ice_mode_read(INTR1_FLASH_INT_FLAG, &stat, sizeof(u32)) < 0) { + ILI_ERR("Read 0x%x error\n", INTR1_FLASH_INT_FLAG); + retry--; + continue; + } + + ILI_DBG("fw dma stat = 0x%x\n", stat); + + if ((stat & BIT(1)) == BIT(1)) + break; + + retry--; + usleep_range(10000, 10000); + } + + if (retry <= 0) + ILI_ERR("Polling DMA fail: Regsiter = 0x%X state = 0x%X\n", INTR1_FLASH_INT_FLAG, stat); + + /* CS High */ + if (ili_ice_mode_write(FLASH0_reg_flash_csb, 0x1, 1) < 0) + ILI_ERR("Pull CS High failed\n"); + + /* waiting for CS status done */ + mdelay(10); + } + } + + if (ilits->cascade_info_block.nNum != 0) { + if (ili_cascade_reset_ctrl(TP_IC_WHOLE_RST_WITHOUT_FLASH, false) < 0) + ILI_ERR("IC whole reset without flash failed during moving mp code\n"); + } else { + if (ili_reset_ctrl(TP_IC_WHOLE_RST_WITHOUT_FLASH) < 0) + ILI_ERR("IC whole reset without flash failed during moving mp code\n"); + } + + ret = ili_ice_mode_ctrl(DISABLE, OFF); + if (ret < 0) + goto out; + + /* Check if ic is already in test mode */ + ilits->actual_tp_mode = P5_X_FW_TEST_MODE; /* set busy as 0x51 */ + ret = ili_ic_check_busy(20, 50, ON); + if (ret < 0) + ILI_ERR("Check cdc timeout failed after moved mp code\n"); + +out: + return ret; +} + +int ili_move_mp_code_iram(void) +{ + ILI_INFO("Download MP code to iram\n"); + return ili_fw_upgrade_handler(NULL); +} + +int ili_proximity_near(int mode) +{ + int ret = 0; + + ilits->prox_near = true; + + switch (mode) { + case DDI_POWER_ON: + /* + * If the power of VSP and VSN keeps alive when proximity near event + * occures, TP can just go to sleep in. + */ + ret = ili_ic_func_ctrl("sleep", SLEEP_IN); + if (ret < 0) + ILI_ERR("Write sleep in cmd failed\n"); + break; + case DDI_POWER_OFF: + ILI_INFO("DDI POWER OFF, do nothing\n"); + break; + default: + ILI_ERR("Unknown mode (%d)\n", mode); + ret = -EINVAL; + break; + } + return ret; +} + +int ili_proximity_far(int mode) +{ + int ret = 0; + u8 cmd[2] = {0}; + u8 data_type = P5_X_FW_SIGNAL_DATA_MODE; + + if (!ilits->prox_near) { + ILI_INFO("No proximity near event, break\n"); + return 0; + } + + switch (mode) { + case WAKE_UP_GESTURE_RECOVERY: + /* + * If the power of VSP and VSN has been shut down previsouly, + * TP should go through gesture recovery to get back. + */ + ili_gesture_recovery(); + break; + case WAKE_UP_SWITCH_GESTURE_MODE: + /* + * If the power of VSP and VSN keeps alive in the event of proximity near, + * TP can be just recovered by switching gesture mode to get back. + */ + cmd[0] = 0xF6; + cmd[1] = 0x0A; + + ILI_INFO("write prepare gesture command 0xF6 0x0A\n"); + ret = ilits->wrapper(cmd, 2, NULL, 0, ON, OFF); + if (ret < 0) { + ILI_INFO("write prepare gesture command error\n"); + break; + } + + ret = ili_set_tp_data_len(ilits->gesture_mode, true, &data_type); + if (ret < 0) + ILI_ERR("Switch to gesture mode failed during proximity far\n"); + break; + default: + ILI_ERR("Unknown mode (%d)\n", mode); + ret = -EINVAL; + break; + } + + ilits->prox_near = false; + + return ret; +} + +int ili_move_gesture_code_flash(int mode) +{ + int ret = 0; + u8 data_type = P5_X_FW_SIGNAL_DATA_MODE; + + /* + * NOTE: If functions need to be added during suspend, + * they must be called before gesture cmd reaches to FW. + */ + + ILI_INFO("Gesture mode = %d\n", mode); + ret = ili_set_tp_data_len(mode, true, &data_type); + + return ret; +} + +void ili_set_gesture_symbol(void) +{ + u8 cmd[7] = {0}; + struct gesture_symbol *ptr_sym = &ilits->ges_sym; + u8 *ptr; + + ptr = (u8 *) ptr_sym; + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = 0x01; + cmd[2] = 0x0A; + cmd[3] = 0x08; + cmd[4] = ptr[0]; + cmd[5] = ptr[1]; + cmd[6] = ptr[2]; + + ili_dump_data(cmd, 8, sizeof(cmd), 0, "Gesture symbol"); + + if (ilits->wrapper(cmd, 2, NULL, 0, ON, OFF) < 0) { + ILI_ERR("Write pre cmd failed\n"); + return; + + } + + if (ilits->wrapper(&cmd[1], (sizeof(cmd) - 1), NULL, 0, ON, OFF)) { + ILI_ERR("Write gesture symbol fail\n"); + return; + } + + ILI_INFO(" single_tap = %d\n", ilits->ges_sym.single_tap); + ILI_INFO(" double_tap = %d\n", ilits->ges_sym.double_tap); + ILI_DBG(" alphabet_line_2_top = %d\n", ilits->ges_sym.alphabet_line_2_top); + ILI_DBG(" alphabet_line_2_bottom = %d\n", ilits->ges_sym.alphabet_line_2_bottom); + ILI_DBG(" alphabet_line_2_left = %d\n", ilits->ges_sym.alphabet_line_2_left); + ILI_DBG(" alphabet_line_2_right = %d\n", ilits->ges_sym.alphabet_line_2_right); + ILI_DBG(" alphabet_w = %d\n", ilits->ges_sym.alphabet_w); + ILI_DBG(" alphabet_c = %d\n", ilits->ges_sym.alphabet_c); + ILI_DBG(" alphabet_E = %d\n", ilits->ges_sym.alphabet_E); + ILI_DBG(" alphabet_V = %d\n", ilits->ges_sym.alphabet_V); + ILI_DBG(" alphabet_O = %d\n", ilits->ges_sym.alphabet_O); + ILI_DBG(" alphabet_S = %d\n", ilits->ges_sym.alphabet_S); + ILI_DBG(" alphabet_Z = %d\n", ilits->ges_sym.alphabet_Z); + ILI_DBG(" alphabet_V_down = %d\n", ilits->ges_sym.alphabet_V_down); + ILI_DBG(" alphabet_V_left = %d\n", ilits->ges_sym.alphabet_V_left); + ILI_DBG(" alphabet_V_right = %d\n", ilits->ges_sym.alphabet_V_right); + ILI_DBG(" alphabet_two_line_2_bottom= %d\n", ilits->ges_sym.alphabet_two_line_2_bottom); + ILI_DBG(" alphabet_F= %d\n", ilits->ges_sym.alphabet_F); + ILI_DBG(" alphabet_AT= %d\n", ilits->ges_sym.alphabet_AT); + +} + +int ili_move_gesture_code_iram(int mode) +{ + int i, ret = 0, timeout = 100; + u8 cmd[2] = {0}; + u8 cmd_write[3] = {0x01, 0x0A, 0x05}; + u8 data_type = P5_X_FW_SIGNAL_DATA_MODE; + u8 backup_rec_state = 0; + + /* + * NOTE: If functions need to be added during suspend, + * they must be called before gesture cmd reaches to FW. + */ + + ILI_INFO("Gesture code loaded by %s\n", ilits->gesture_load_code ? "driver" : "firmware"); + + if (!ilits->gesture_load_code) { + ret = ili_set_tp_data_len(mode, true, &data_type); + goto out; + } + + /*pre-command for ili_ic_func_ctrl("lpwg", 0x3)*/ + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = 0x1; + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write 0xF6,0x1 failed\n"); + goto out; + } + + ret = ili_ic_func_ctrl("lpwg", 0x3); + if (ret < 0) { + ILI_ERR("write gesture flag failed\n"); + goto out; + } + + ret = ili_set_tp_data_len(mode, true, &data_type); + if (ret < 0) { + ILI_ERR("Failed to set tp data length\n"); + goto out; + } + + ili_irq_enable(); + /* Prepare Check Ready */ + cmd[0] = P5_X_READ_DATA_CTRL; + cmd[1] = 0xA; + ret = ilits->wrapper(cmd, sizeof(cmd), NULL, 0, OFF, OFF); + if (ret < 0) { + ILI_ERR("Write 0xF6,0xA failed\n"); + goto out; + } + + for (i = 0; i < timeout; i++) { + /* Check ready for load code */ + ret = ilits->wrapper(cmd_write, sizeof(cmd_write), cmd, sizeof(u8), ON, OFF); + ILI_DBG("gesture ready byte = 0x%x\n", cmd[0]); + + if (cmd[0] == 0x91) { + ILI_INFO("Ready to load gesture code\n"); + break; + } + mdelay(2); + } + ili_irq_disable(); + + if (i >= timeout) { + ILI_ERR("Gesture is not ready (0x%x), try to run its recovery\n", cmd[0]); + return ili_gesture_recovery(); + } + + if (ilits->prox_face_mode == PROXIMITY_SUSPEND_RESUME) { + backup_rec_state = func_ctrl[FUNC_CTRL_PROXIMITY_IDX].rec_state; + func_ctrl[FUNC_CTRL_PROXIMITY_IDX].rec_state = DISABLE; + ILI_INFO("Doing gesture dynamic load code, Cannot reset %s CMD, rec_state %d \n", func_ctrl[FUNC_CTRL_PROXIMITY_IDX].name, func_ctrl[FUNC_CTRL_PROXIMITY_IDX].rec_state); + } + + ret = ili_fw_upgrade_handler(NULL); + if (ret < 0) { + ILI_ERR("FW upgrade failed during moving code\n"); + goto out; + } + + if (ilits->prox_face_mode == PROXIMITY_SUSPEND_RESUME) { + func_ctrl[FUNC_CTRL_PROXIMITY_IDX].rec_state = backup_rec_state; + ILI_INFO("Recovery %s rec_state %d \n", func_ctrl[FUNC_CTRL_PROXIMITY_IDX].name, func_ctrl[FUNC_CTRL_PROXIMITY_IDX].rec_state); + } + + /* Resume gesture loader */ + ret = ili_ic_func_ctrl("lpwg", 0x6); + if (ilits->prox_face_mode == PROXIMITY_SUSPEND_RESUME && ilits->proxmity_face == true) { + ret = ili_ic_func_ctrl("lpwg", 0x21); + } + + if (ret < 0) { + ILI_ERR("write resume loader error"); + goto out; + } + +out: + return ret; +} + +u8 ili_calc_packet_checksum(u8 *packet, int len) +{ + int i; + s32 sum = 0; + + for (i = 0; i < len; i++) + sum += packet[i]; + + return (u8) ((-sum) & 0xFF); +} + +int ili_touch_esd_gesture_flash(void) +{ + int ret = 0, retry = 100; + u32 answer = 0, slave_answer = 0; + int ges_pwd_addr = I2C_ESD_GESTURE_CORE146_PWD_ADDR; + int ges_pwd = ESD_GESTURE_CORE146_PWD; + int ges_run = I2C_ESD_GESTURE_CORE146_RUN; + int pwd_len = 2; + u8 data_type = P5_X_FW_SIGNAL_DATA_MODE; + + if (ilits->chip->core_ver < CORE_VER_1460) { + ges_pwd_addr = I2C_ESD_GESTURE_PWD_ADDR; + ges_pwd = ESD_GESTURE_PWD; + ges_run = I2C_ESD_GESTURE_RUN; + pwd_len = 4; + } + + if (ilits->cascade_info_block.nNum != 0) { + ret = ili_ice_mode_ctrl_by_mode(ENABLE, OFF, BOTH); + } else { + ret = ili_ice_mode_ctrl(ENABLE, OFF); + } + + if (ret < 0) { + ILI_ERR("Enable ice mode failed during gesture recovery\n"); + return ret; + } + + ILI_INFO("ESD Gesture PWD Addr = 0x%X, PWD = 0x%X, GES_RUN = 0%X, core_ver = 0x%X\n", + ges_pwd_addr, ges_pwd, ges_run, ilits->chip->core_ver); + + /* write a special password to inform FW go back into gesture mode */ + if (ilits->cascade_info_block.nNum != 0) { + ret = ili_ice_mode_write_by_mode(ges_pwd_addr, ges_pwd, pwd_len, BOTH); + } else { + ret = ili_ice_mode_write(ges_pwd_addr, ges_pwd, pwd_len); + } + + if (ret < 0) { + ILI_ERR("write password failed\n"); + goto out; + } + + /* HW reset gives effect to FW receives password successed */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + + if (ilits->cascade_info_block.nNum != 0) { + ret = ili_cascade_reset_ctrl(ilits->reset, false); + } else { + ret = ili_reset_ctrl(ilits->reset); + } + + if (ret < 0) { + ILI_ERR("TP Reset failed during gesture recovery\n"); + goto out; + } + + if (ilits->cascade_info_block.nNum != 0) { + ili_ice_mode_ctrl_by_mode(ENABLE, ON, BOTH); + } else { + ret = ili_ice_mode_ctrl(ENABLE, ON); + } + + if (ret < 0) { + ILI_ERR("Enable ice mode failed during gesture recovery\n"); + goto out; + } + + /* polling Cascade Slave another specific register to see if gesutre is enabled properly */ + if (ilits->cascade_info_block.nNum != 0) { + do { + ret = ili_ice_mode_read_by_mode(ges_pwd_addr, &slave_answer, pwd_len, SLAVE); + + if (ret < 0) { + ILI_ERR("Read Slave gesture answer error\n"); + goto out; + } + + if (slave_answer != ges_run) + ILI_INFO("ret = 0x%X, slave_answer = 0x%X\n", slave_answer, ges_run); + + mdelay(2); + } while (slave_answer != ges_run && --retry > 0); + } + + /* polling another specific register to see if gesutre is enabled properly */ + do { + ret = ili_ice_mode_read_by_mode(ges_pwd_addr, &answer, pwd_len, MASTER); + + if (ret < 0) { + ILI_ERR("Read Master gesture answer error\n"); + goto out; + } + + if (answer != ges_run) + ILI_INFO("ret = 0x%X, slave_answer = 0x%X\n", answer, ges_run); + + mdelay(2); + } while (answer != ges_run && --retry > 0); + + if (retry <= 0) { + ILI_ERR("Enter gesture failed\n"); + ret = -1; + goto out; + } + + ILI_INFO("0x%X Enter gesture successfully\n", answer); + +out: + if (ilits->cascade_info_block.nNum != 0) { + ili_ice_mode_ctrl_by_mode(DISABLE, ON, BOTH); + } else { + if (ili_ice_mode_ctrl(DISABLE, ON) < 0) { + ILI_ERR("Disable ice mode failed during gesture recovery\n"); + } + } + + if (ret >= 0) { + ilits->actual_tp_mode = P5_X_FW_GESTURE_MODE; + ili_set_tp_data_len(ilits->gesture_mode, false, &data_type); + } + + if (ilits->prox_face_mode == PROXIMITY_SUSPEND_RESUME && ilits->proxmity_face == true) { + ret = ili_ic_func_ctrl("proximity", 0x01); + ret = ili_ic_func_ctrl("lpwg", 0x21); + if (ret < 0) { + ILI_ERR("Write func ctrl cmd failed\n"); + } + } + + return ret; +} + +int ili_touch_esd_gesture_iram(void) +{ + int ret = 0, retry = 100; + u32 answer = 0, slave_answer = 0; + int ges_pwd_addr = SPI_ESD_GESTURE_CORE146_PWD_ADDR; + int slave_ges_pwd_addr = SPI_ESD_GESTURE_CORE146_PWD_ADDR; + int ges_pwd = ESD_GESTURE_CORE146_PWD; + int ges_run = SPI_ESD_GESTURE_CORE146_RUN; + int pwd_len = 2; + u8 data_type = P5_X_FW_SIGNAL_DATA_MODE; + u8 backup_rec_state = 0; + + if (!ilits->gesture_load_code) { + ges_run = I2C_ESD_GESTURE_CORE146_RUN; + } + + if (ilits->chip->core_ver < CORE_VER_1460) { + if (ilits->chip->core_ver >= CORE_VER_1420) + ges_pwd_addr = I2C_ESD_GESTURE_PWD_ADDR; + else + ges_pwd_addr = SPI_ESD_GESTURE_PWD_ADDR; + ges_pwd = ESD_GESTURE_PWD; + ges_run = SPI_ESD_GESTURE_RUN; + pwd_len = 4; + } + + ILI_INFO("ESD Gesture PWD Addr = 0x%X, PWD = 0x%X, GES_RUN = 0x%X, core_ver = 0x%X\n", + ges_pwd_addr, ges_pwd, ges_run, ilits->chip->core_ver); + + if (ilits->cascade_info_block.nNum != 0) { + ili_set_bypass_mode(OFF); + } else { + ret = ili_ice_mode_ctrl(ENABLE, OFF); + } + + if (ret < 0) { + ILI_ERR("Enable ice mode failed during gesture recovery\n"); + goto fail; + } + + /* write a special password to inform FW go back into gesture mode */ + ret = ili_ice_mode_write(ges_pwd_addr, ges_pwd, pwd_len); + if (ret < 0) { + ILI_ERR("write password failed\n"); + goto fail; + } + + if (ilits->prox_face_mode == PROXIMITY_SUSPEND_RESUME) { + backup_rec_state = func_ctrl[FUNC_CTRL_PROXIMITY_IDX].rec_state; + func_ctrl[FUNC_CTRL_PROXIMITY_IDX].rec_state = DISABLE; + ILI_INFO("Doing gesture dynamic load code, Cannot reset %s CMD, rec_state %d \n", func_ctrl[FUNC_CTRL_PROXIMITY_IDX].name, func_ctrl[FUNC_CTRL_PROXIMITY_IDX].rec_state); + } + + /* Host download gives effect to FW receives password successed */ + ilits->actual_tp_mode = P5_X_FW_AP_MODE; + ret = ili_fw_upgrade_handler(NULL); + if (ret < 0) { + ILI_ERR("FW upgrade failed during gesture recovery\n"); + goto fail; + } + + /* Wait for fw running code finished. */ + if (ilits->info_from_hex || (ilits->chip->core_ver >= CORE_VER_1410)) + msleep(50); + + if (ilits->cascade_info_block.nNum != 0) { + ili_set_bypass_mode(ON); + } else { + ret = ili_ice_mode_ctrl(ENABLE, ON); + } + + if (ret < 0) { + ILI_ERR("Enable ice mode failed during gesture recovery\n"); + goto fail; + } + + if (ilits->cascade_info_block.nNum != 0) { + /* polling Cascade Slave another specific register to see if gesutre is enabled properly */ + do { + if (ilits->cascade_info_block.nNum != 0) { + ili_mspi_read_slave_register(slave_ges_pwd_addr, &slave_answer, sizeof(u32)); + + if (slave_answer != ges_run) + ILI_INFO("ret = 0x%X, slave answer = 0x%X\n", slave_answer, ges_run); + } + + mdelay(2); + } while (slave_answer != ges_run && --retry > 0); + } + + /* polling another specific register to see if gesutre is enabled properly */ + do { + ret = ili_ice_mode_read(ges_pwd_addr, &answer, pwd_len); + if (ret < 0) { + ILI_ERR("Read gesture answer error\n"); + goto fail; + } + + if (answer != ges_run) + ILI_INFO("ret = 0x%X, answer = 0x%X\n", answer, ges_run); + + mdelay(2); + } while (answer != ges_run && --retry > 0); + + if (retry <= 0) { + ILI_ERR("Enter gesture failed\n"); + ret = -1; + goto fail; + } + + ILI_INFO("Enter gesture successfully\n"); + + ret = ili_ice_mode_ctrl(DISABLE, ON); + if (ret < 0) { + ILI_ERR("Disable ice mode failed during gesture recovery\n"); + goto fail; + } + + ILI_INFO("Gesture code loaded by %s\n", ilits->gesture_load_code ? "driver" : "firmware"); + + if (!ilits->gesture_load_code) { + ilits->actual_tp_mode = P5_X_FW_GESTURE_MODE; + ili_set_tp_data_len(ilits->gesture_mode, false, &data_type); + goto out; + } + + /* Load gesture code */ + ilits->actual_tp_mode = P5_X_FW_GESTURE_MODE; + ili_set_tp_data_len(ilits->gesture_mode, false, &data_type); + ret = ili_fw_upgrade_handler(NULL); + if (ret < 0) { + ILI_ERR("Failed to load code during gesture recovery\n"); + goto fail; + } + + if (ilits->prox_face_mode == PROXIMITY_SUSPEND_RESUME) { + func_ctrl[FUNC_CTRL_PROXIMITY_IDX].rec_state = backup_rec_state; + ILI_INFO("Recovery %s rec_state %d \n", func_ctrl[FUNC_CTRL_PROXIMITY_IDX].name, func_ctrl[FUNC_CTRL_PROXIMITY_IDX].rec_state); + + /* Resume gesture loader */ + ret = ili_ic_func_ctrl("lpwg", 0x6); + + if (ilits->proxmity_face == true) { + ret = ili_ic_func_ctrl("proximity", 0x01); + ret = ili_ic_func_ctrl("lpwg", 0x21); + } + } else { + /* Resume gesture loader */ + ret = ili_ic_func_ctrl("lpwg", 0x6); + } + + if (ret < 0) { + ILI_ERR("write resume loader error"); + goto fail; + } + +out: + return ret; + +fail: + ilits->actual_tp_mode = P5_X_FW_GESTURE_MODE; + ili_ice_mode_ctrl(DISABLE, ON); + + return ret; +} + +void ili_demo_debug_info_id0(u8 *buf, size_t len) +{ + struct ili_demo_debug_info_id0 id0; + + ipio_memcpy(&id0, buf, sizeof(id0), len); + ILI_INFO("id0 len = %d,strucy len = %d", (int)len, (int)sizeof(id0)); + + ILI_INFO("id = %d\n", id0.id); + ILI_INFO("app_sys_powr_state_e = %d\n", id0.sys_powr_state_e); + ILI_INFO("app_sys_state_e = %d\n", id0.sys_state_e); + ILI_INFO("tp_state_e = %d\n", id0.tp_state_e); + + ILI_INFO("touch_palm_state_e = %d\n", id0.touch_palm_state); + ILI_INFO("app_an_statu_e = %d\n", id0.app_an_statu_e); + ILI_INFO("app_sys_bg_err = %d\n", id0.app_sys_bg_err); + ILI_INFO("g_b_wrong_bg = %d\n", id0.g_b_wrong_bg); + ILI_INFO("reserved0 = %d\n", id0.reserved0); + + ILI_INFO("normal_mode = %d\n", id0.normal_mode); + ILI_INFO("charger_mode = %d\n", id0.charger_mode); + ILI_INFO("glove_mode = %d\n", id0.glove_mode); + ILI_INFO("stylus_mode = %d\n", id0.stylus_mode); + ILI_INFO("multi_mode = %d\n", id0.multi_mode); + ILI_INFO("noise_mode = %d\n", id0.noise_mode); + ILI_INFO("palm_plus_mode = %d\n", id0.palm_plus_mode); + ILI_INFO("floating_mode = %d\n", id0.floating_mode); + + ILI_INFO("algo_pt_status0 = %d\n", id0.algo_pt_status0); + ILI_INFO("algo_pt_status1 = %d\n", id0.algo_pt_status1); + ILI_INFO("algo_pt_status2 = %d\n", id0.algo_pt_status2); + ILI_INFO("algo_pt_status3 = %d\n", id0.algo_pt_status3); + ILI_INFO("algo_pt_status4 = %d\n", id0.algo_pt_status4); + ILI_INFO("algo_pt_status5 = %d\n", id0.algo_pt_status5); + ILI_INFO("algo_pt_status6 = %d\n", id0.algo_pt_status6); + ILI_INFO("algo_pt_status7 = %d\n", id0.algo_pt_status7); + ILI_INFO("algo_pt_status8 = %d\n", id0.algo_pt_status8); + ILI_INFO("algo_pt_status9 = %d\n", id0.algo_pt_status9); + ILI_INFO("reserved2 = %d\n", id0.reserved2); + + ILI_INFO("hopping_flg = %d\n", id0.hopping_flg); + ILI_INFO("hopping_index = %d\n", id0.hopping_index); + ILI_INFO("frequency = %d\n", (id0.frequency_h << 8 | id0.frequency_l)); + ILI_INFO("reserved3 = %d\n", id0.reserved3); + ILI_INFO("reserved4 = %d\n", id0.reserved4); + +} + +void ili_demo_debug_info_mode(u8 *buf, size_t len) +{ + u8 *info_ptr; + u8 info_id, info_len; + + ili_report_ap_mode(buf, len); + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + info_ptr = buf + P5_X_DEMO_MODE_PACKET_LEN; + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + info_ptr = buf + P5_X_DEMO_MODE_PACKET_LEN_HIGH_RESOLUTION; + } else { + info_ptr = buf + P5_X_DEMO_MODE_PACKET_LEN; + } + info_len = info_ptr[0]; + info_id = info_ptr[1]; + + ILI_INFO("info len = %d ,id = %d\n", info_len, info_id); + if (info_id == 0) { + ilits->demo_debug_info[info_id](&info_ptr[1], info_len); + } else { + ILI_INFO("Not support this id %d\n", info_id); + } +} + +static void ilitek_tddi_touch_send_debug_data(u8 *buf, int len) +{ + int index; + mutex_lock(&ilits->debug_mutex); + + if (!ilits->dnp && !ilits->dlnp) + goto out; + + /* Sending data to apk via the node of debug_message node */ + if (ilits->dnp) { + index = ilits->dbf; + + if (!ilits->dbl[ilits->dbf].mark) { + ilits->dbf = ((ilits->dbf + 1) % TR_BUF_LIST_SIZE); + } else { + /*keep the last frame buffer*/ + if (ilits->dbf == 0) + index = TR_BUF_LIST_SIZE - 1; + else + index = ilits->dbf -1; + + ilits->all_frame_full = true; + ILI_DBG("Frame buf is full. \n"); + } + + if (ilits->dbl[index].data == NULL) { + ILI_ERR("BUFFER %d error\n", index); + goto out; + } + ipio_memcpy(ilits->dbl[index].data, buf, len, TR_BUF_SIZE); + ilits->dbl[index].mark = true; + + wake_up(&(ilits->inq)); + goto out; + } + + if (ilits->dlnp) { + /* ping pong buffer */ + index = ilits->dbf_in_idx; + ILI_DBG("ping pong buffer index = %d, mark = %d \n", index, ilits->dbf_in[index].mark); + if (ilits->dbf_in[index].data == NULL) { + ILI_ERR("INPUT BUFFER %d error\n", index); + goto out; + } + + if (ilits->dbf_in_idx < TR_BUF_LIST_SIZE) { + ipio_memcpy(ilits->dbf_in[index].data, buf, len, TR_BUF_SIZE); + ilits->dbf_in_idx++; + ilits->dbf_in[index].mark = true; + } else { + ILI_DBG("input buffer is fully.\n"); + } + + wake_up(&(ilits->inq)); + goto out; + } + +out: + mutex_unlock(&ilits->debug_mutex); +} + +static void ilitek_tddi_touch_customer_data_parsing(u8 *buf) +{ + int i = 0; + const int others = 16; + int index = 0; + struct ilitek_axis_info axis_info[MAX_TOUCH_NUM]; + bool palmflag = false; + ilits->finger = 0; + if (ilits->rib.nCustomerType == POSITION_CUSTOMER_TYPE_ON && ilits->tp_data_format == DATA_FORMAT_DEMO) { + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + index = P5_X_5B_LOW_RESOLUTION_LENGTH - others - P5_X_INFO_CHECKSUM_LENGTH; + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if ((buf[(4 * i) + 1 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF) && (buf[(4 * i) + 2 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF) && (buf[(4 * i) + 3 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF)) { + continue; + } + axis_info[ilits->finger].degree = buf[(5 * i) + index]; + axis_info[ilits->finger].width_major = (((buf[(5 * i) + 1 + index]) << 8) | (buf[(5 * i) + 2 + index])); + axis_info[ilits->finger].width_minor = (((buf[(5 * i) + 3 + index]) << 8) | (buf[(5 * i) + 4 + index])); + ILI_DBG("finger = %d, degree = %d, width_major = %d, width_minor = %d\n", ilits->finger, axis_info[ilits->finger].degree, axis_info[ilits->finger].width_major, axis_info[ilits->finger].width_minor); + ilits->finger++; + } + palmflag = ((buf[index + P5_X_CUSTOMER_LENGTH + 2] & 0x08) >> 3); + ILI_DBG("palmflag = %d\n", palmflag); + + for (i = 0; i < others; i++) { + ILI_DBG("Other Information byte[%d]:%02x\n", i, buf[index + P5_X_CUSTOMER_LENGTH + i]); + } + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + index = P5_X_DEMO_MODE_PACKET_LEN_HIGH_RESOLUTION - others - P5_X_INFO_CHECKSUM_LENGTH; + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if ((buf[(5 * i) + 1 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF) && (buf[(5 * i) + 2 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF) && + (buf[(5 * i) + 3 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF) && (buf[(5 * i) + 4 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF)) { + continue; + } + axis_info[ilits->finger].degree = buf[(5 * i) + index]; + axis_info[ilits->finger].width_major = (((buf[(5 * i) + 1 + index]) << 8) | (buf[(5 * i) + 2 + index])); + axis_info[ilits->finger].width_minor = (((buf[(5 * i) + 3 + index]) << 8) | (buf[(5 * i) + 4 + index])); + ILI_DBG("finger = %d, degree = %d, width_major = %d, width_minor = %d\n", ilits->finger, axis_info[ilits->finger].degree, axis_info[ilits->finger].width_major, axis_info[ilits->finger].width_minor); + ilits->finger++; + } + palmflag = ((buf[index + P5_X_CUSTOMER_LENGTH + 2] & 0x08) >> 3); + ILI_DBG("palmflag = %d\n", palmflag); + + for (i = 0; i < others; i++) { + ILI_DBG("Other Information byte[%d]:%02x\n", i, buf[index + P5_X_CUSTOMER_LENGTH + i]); + } + } + } else if (ilits->rib.nCustomerType == POSITION_CUSTOMER_TYPE_ON && ilits->tp_data_format == DATA_FORMAT_DEBUG) { + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + index = P5_X_DEBUG_LOW_RESOLUTION_FINGER_DATA_LENGTH + (2 * ilits->xch_num * ilits->ych_num) + (ilits->stx * 2) + (ilits->srx * 2) + (2 * 2); + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if ((buf[(3 * i) + 1 + P5_X_DEBUG_MODE_PACKET_INFO_LEN] == 0xFF) && (buf[(3 * i) + 2 + P5_X_DEBUG_MODE_PACKET_INFO_LEN] == 0xFF) && (buf[(3 * i) + 3 + P5_X_DEBUG_MODE_PACKET_INFO_LEN] == 0xFF)) { + continue; + } + axis_info[ilits->finger].degree = buf[(5 * i) + index]; + axis_info[ilits->finger].width_major = (((buf[(5 * i) + 1 + index]) << 8) | (buf[(5 * i) + 2 + index])); + axis_info[ilits->finger].width_minor = (((buf[(5 * i) + 3 + index]) << 8) | (buf[(5 * i) + 4 + index])); + ILI_DBG("finger = %d, degree = %d, width_major = %d, width_minor = %d\n", ilits->finger, axis_info[ilits->finger].degree, axis_info[ilits->finger].width_major, axis_info[ilits->finger].width_minor); + ilits->finger++; + } + palmflag = ((buf[index + P5_X_CUSTOMER_LENGTH + 2] & 0x08) >> 3); + ILI_DBG("palmflag = %d\n", palmflag); + + for (i = 0; i < others; i++) { + ILI_DBG("Other Information byte[%d]:%02x\n", i, buf[index + P5_X_CUSTOMER_LENGTH + i]); + } + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + index = P5_X_DEBUG_HIGH_RESOLUTION_FINGER_DATA_LENGTH + (2 * ilits->xch_num * ilits->ych_num) + (ilits->stx * 2) + (ilits->srx * 2) + (2 * 2); + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if ((buf[(4 * i) + 1 + P5_X_DEBUG_MODE_PACKET_INFO_LEN] == 0xFF) && (buf[(4 * i) + 2 + P5_X_DEBUG_MODE_PACKET_INFO_LEN] == 0xFF) && + (buf[(4 * i) + 3 + P5_X_DEBUG_MODE_PACKET_INFO_LEN] == 0xFF) && (buf[(4 * i) + 4 + P5_X_DEBUG_MODE_PACKET_INFO_LEN] == 0xFF)) { + continue; + } + axis_info[ilits->finger].degree = buf[(5 * i) + index]; + axis_info[ilits->finger].width_major = (((buf[(5 * i) + 1 + index]) << 8) | (buf[(5 * i) + 2 + index])); + axis_info[ilits->finger].width_minor = (((buf[(5 * i) + 3 + index]) << 8) | (buf[(5 * i) + 4 + index])); + ILI_DBG("finger = %d, degree = %d, width_major = %d, width_minor = %d\n", ilits->finger, axis_info[ilits->finger].degree, axis_info[ilits->finger].width_major, axis_info[ilits->finger].width_minor); + ilits->finger++; + } + palmflag = ((buf[index + P5_X_CUSTOMER_LENGTH + 2] & 0x08) >> 3); + ILI_DBG("palmflag = %d\n", palmflag); + + for (i = 0; i < others; i++) { + ILI_DBG("Other Information byte[%d]:%02x\n", i, buf[index + P5_X_CUSTOMER_LENGTH + i]); + } + } + } + +} + +void ili_touch_press(u16 x, u16 y, u16 pressure, u16 id) +{ + ILI_DBG("Touch Press: id = %d, x = %d, y = %d, p = %d\n", id, x, y, pressure); + + if (MT_B_TYPE) { + input_mt_slot(ilits->input, id); + input_mt_report_slot_state(ilits->input, MT_TOOL_FINGER, true); + input_report_abs(ilits->input, ABS_MT_POSITION_X, x); + input_report_abs(ilits->input, ABS_MT_POSITION_Y, y); + if (MT_PRESSURE) + input_report_abs(ilits->input, ABS_MT_PRESSURE, pressure); + } else { + input_report_key(ilits->input, BTN_TOUCH, 1); + input_report_abs(ilits->input, ABS_MT_TRACKING_ID, id); + input_report_abs(ilits->input, ABS_MT_TOUCH_MAJOR, 1); + input_report_abs(ilits->input, ABS_MT_WIDTH_MAJOR, 1); + input_report_abs(ilits->input, ABS_MT_POSITION_X, x); + input_report_abs(ilits->input, ABS_MT_POSITION_Y, y); + if (MT_PRESSURE) + input_report_abs(ilits->input, ABS_MT_PRESSURE, pressure); + + input_mt_sync(ilits->input); + } +} + +void ili_touch_release(u16 x, u16 y, u16 id) +{ + ILI_DBG("Touch Release: id = %d, x = %d, y = %d\n", id, x, y); + + if (MT_B_TYPE) { + input_mt_slot(ilits->input, id); + input_mt_report_slot_state(ilits->input, MT_TOOL_FINGER, false); + } else { + input_report_key(ilits->input, BTN_TOUCH, 0); + input_mt_sync(ilits->input); + } +} + +void ili_touch_release_all_point(void) +{ + int i; + ILI_DBG("Touch Release All\n"); + if (MT_B_TYPE) { + for (i = 0 ; i < MAX_TOUCH_NUM; i++) + ili_touch_release(0, 0, i); + + input_report_key(ilits->input, BTN_TOUCH, 0); + input_report_key(ilits->input, BTN_TOOL_FINGER, 0); + } else { + ili_touch_release(0, 0, 0); + } + input_sync(ilits->input); +} + +static struct ilitek_touch_info touch_info[MAX_TOUCH_NUM + MAX_PEN_NUM]; + +void ili_report_ap_mode(u8 *buf, int len) +{ + int i = 0; + u32 xop = 0, yop = 0; + + memset(touch_info, 0x0, sizeof(touch_info)); + + ilits->finger = 0; + + for (i = 0; i < MAX_TOUCH_NUM; i++) { + + if (ilits->rib.nCustomerType == ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + if ((buf[(4 * i) + 1] == 0xFF) && (buf[(4 * i) + 2] == 0xFF) && (buf[(4 * i) + 3] == 0xFF)) { + if (MT_B_TYPE) + ilits->curt_touch[i] = 0; + continue; + } + xop = (((buf[(4 * i) + 1] & 0xF0) << 4) | (buf[(4 * i) + 2])); + yop = (((buf[(4 * i) + 1] & 0x0F) << 8) | (buf[(4 * i) + 3])); + } else if (ilits->rib.nCustomerType != ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + if ((buf[(4 * i) + 1 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF) && (buf[(4 * i) + 2 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF) && (buf[(4 * i) + 3 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF)) { + if (MT_B_TYPE) + ilits->curt_touch[i] = 0; + continue; + } + xop = (((buf[(4 * i) + 1 + P5_X_DEMO_MODE_PACKET_INFO_LEN] & 0xF0) << 4) | (buf[(4 * i) + 2 + P5_X_DEMO_MODE_PACKET_INFO_LEN])); + yop = (((buf[(4 * i) + 1 + P5_X_DEMO_MODE_PACKET_INFO_LEN] & 0x0F) << 8) | (buf[(4 * i) + 3 + P5_X_DEMO_MODE_PACKET_INFO_LEN])); + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + if ((buf[(5 * i) + 1 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF) && (buf[(5 * i) + 2 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF) && + (buf[(5 * i) + 3 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF) && (buf[(5 * i) + 4 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF)) { + if (MT_B_TYPE) + ilits->curt_touch[i] = 0; + continue; + } + xop = ((buf[(5 * i) + 1 + P5_X_DEMO_MODE_PACKET_INFO_LEN] << 8) | (buf[(5 * i) + 2 + P5_X_DEMO_MODE_PACKET_INFO_LEN])); + yop = ((buf[(5 * i) + 3 + P5_X_DEMO_MODE_PACKET_INFO_LEN] << 8) | (buf[(5 * i) + 4 + P5_X_DEMO_MODE_PACKET_INFO_LEN])); + } + + if (ilits->trans_xy) { + touch_info[ilits->finger].x = xop; + touch_info[ilits->finger].y = yop; + } else { + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + touch_info[ilits->finger].x = xop * ilits->panel_wid / TPD_WIDTH; + touch_info[ilits->finger].y = yop * ilits->panel_hei / TPD_HEIGHT; + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + touch_info[ilits->finger].x = xop * ilits->panel_wid / ilits->max_x; + touch_info[ilits->finger].y = yop * ilits->panel_hei / ilits->max_y; + } + } + + touch_info[ilits->finger].id = i; + + if (MT_PRESSURE) { + if (ilits->rib.nCustomerType == ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + touch_info[ilits->finger].pressure = buf[(4 * i) + 4]; + } else if (ilits->rib.nCustomerType != ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + touch_info[ilits->finger].pressure = buf[(4 * i) + 4 + P5_X_DEMO_MODE_PACKET_INFO_LEN]; + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + touch_info[ilits->finger].pressure = buf[(5 * i) + 5 + P5_X_DEMO_MODE_PACKET_INFO_LEN]; + } + } else { + touch_info[ilits->finger].pressure = 1; + } + + ILI_DBG("original x = %d, y = %d\n", xop, yop); + ilits->finger++; + if (MT_B_TYPE) + ilits->curt_touch[i] = 1; + } + +#ifdef ROI + ili_read_knuckle_roi_data(); +#endif + ILI_DBG("figner number = %d, LastTouch = %d\n", ilits->finger, ilits->last_touch); + + if (ilits->finger) { + if (MT_B_TYPE) { + for (i = 0; i < ilits->finger; i++) { + input_report_key(ilits->input, BTN_TOUCH, 1); + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + input_report_key(ilits->input, BTN_TOOL_FINGER, 1); + } + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) + ili_touch_release(0, 0, i); + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + } else { + for (i = 0; i < ilits->finger; i++) + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + } + input_sync(ilits->input); + ilits->last_touch = ilits->finger; + } else { + if (ilits->last_touch) { + if (MT_B_TYPE) { + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) + ili_touch_release(0, 0, i); + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + input_report_key(ilits->input, BTN_TOUCH, 0); + input_report_key(ilits->input, BTN_TOOL_FINGER, 0); + } else { + ili_touch_release(0, 0, 0); + } + input_sync(ilits->input); + ilits->last_touch = 0; +#ifdef ILI_TOUCH_LAST_TIME + //save last timestamp after last touch reported to avoid impacting multi and swipe touch + ilits->last_event_time = ktime_get_boottime(); + ILI_DBG("last touch, save last_event_time\n"); +#endif + } + } + ilitek_tddi_touch_customer_data_parsing(buf); + ilitek_tddi_touch_send_debug_data(buf, len); +} + +static void ili_pen_down(u16 x, u16 y, u16 pressure) +{ + ILI_DBG("Pen Down: x = %d, y = %d, pressure = %d, TipSwitch = %d, InRange = %d\n", x, y, pressure, ilits->pen_info.TipSwitch, ilits->pen_info.InRange); + input_report_abs(ilits->input_pen, ABS_X, x); + input_report_abs(ilits->input_pen, ABS_Y, y); + input_report_abs(ilits->input_pen, ABS_PRESSURE, pressure); + input_report_key(ilits->input_pen, BTN_TOUCH, ilits->pen_info.TipSwitch); + input_report_key(ilits->input_pen, BTN_TOOL_PEN, ilits->pen_info.InRange); + input_report_key(ilits->input_pen, BTN_STYLUS, ilits->pen_info.BarelSwitch); + input_report_key(ilits->input_pen, BTN_STYLUS2, ilits->pen_info.Eraser); + input_report_abs(ilits->input_pen, ABS_TILT_X, ilits->pen_info.tilt_x); + input_report_abs(ilits->input_pen, ABS_TILT_Y, ilits->pen_info.tilt_y); + + /* Hover Report Rate Test */ + if (ilits->pen_info.TipSwitch) { + input_report_abs(ilits->input_pen, ABS_DISTANCE, 0); + } else if ( ilits->pen_info.TipSwitch == 0 && ilits->pen_info.InRange == 1 ) { + if (ilits->pen_info.distance_cnt == 1) { + ilits->pen_info.distance_cnt = 2; + } else { + ilits->pen_info.distance_cnt = 1; + } + input_report_abs(ilits->input_pen, ABS_DISTANCE, ilits->pen_info.distance_cnt); + } + + input_sync(ilits->input_pen); +} + +void ili_pen_release(void) +{ +#if ENABLE_PEN_MODE + ILI_DBG("Pen Release \n"); + ilits->pen_info.active = false; + input_report_key(ilits->input_pen, BTN_TOUCH, false); + input_report_key(ilits->input_pen, BTN_TOOL_PEN, false); + input_report_key(ilits->input_pen, BTN_STYLUS, false); + input_report_key(ilits->input_pen, BTN_STYLUS2, false); + input_report_abs(ilits->input_pen, ABS_DISTANCE, 1); + input_sync(ilits->input_pen); +#endif +} + +void ili_pen_demo_mode_report_point(u8 *buf, int len) +{ + int i = 0, pen_id_index = 0; + u32 xop = 0, yop = 0, PenStartIdx = 0; + + memset(touch_info, 0x0, sizeof(touch_info)); + + ilits->finger = 0; + ilits->pen = 0; + + if (ilits->rib.nCustomerType == ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + PenStartIdx = P5_X_DEMO_LOW_RESOLUTION_FINGER_DATA_LENGTH + P5_X_P_SENSOR_KEY_LENGTH; + } else if (ilits->rib.nCustomerType != ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + PenStartIdx = P5_X_DEMO_LOW_RESOLUTION_FINGER_DATA_LENGTH + P5_X_P_SENSOR_KEY_LENGTH + P5_X_CUSTOMER_LENGTH; + } else if (ilits->rib.nCustomerType != ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + PenStartIdx = P5_X_DEMO_HIGH_RESOLUTION_FINGER_DATA_LENGTH + P5_X_P_SENSOR_KEY_LENGTH + P5_X_CUSTOMER_LENGTH; + } else if (ilits->rib.nCustomerType == ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + PenStartIdx = P5_X_DEMO_HIGH_RESOLUTION_FINGER_DATA_LENGTH + P5_X_P_SENSOR_KEY_LENGTH; + } + + if (buf[len - P5_X_INFO_CHECKSUM_LENGTH - 1] != ONLY_REPORT_HAND) { + ILI_DBG("Pen Switch = 0x%X\n", buf[PenStartIdx]); + if ((buf[PenStartIdx] & TIPSWITCH_HOVER_BIT) == RELEASE_PEN) { + ilits->pen_info.active = false; + } else { + ilits->pen_info.active = true; + } + } else { + ilits->pen_info.active = false; + } + ILI_DBG("Pen is active : %s\n", ilits->pen_info.active ? "True" : "False"); + + for (i = 0; i < ilits->touch_num; i++) { + if (i >= MAX_TOUCH_NUM) { + if (ilits->pen_info.active) { + xop = ((buf[PenStartIdx + 2] << 8) | (buf[PenStartIdx + 3])); + yop = ((buf[PenStartIdx + 4] << 8) | (buf[PenStartIdx + 5])); + + ilits->pen_info.TipSwitch = buf[PenStartIdx] & BIT(0); + ilits->pen_info.BarelSwitch = (buf[PenStartIdx] & BIT(1)) >> 1; + ilits->pen_info.Eraser = (buf[PenStartIdx] & BIT(2)) >> 2; + ilits->pen_info.InRange = (buf[PenStartIdx] & BIT(4)) >> 4; + + if ( buf[PenStartIdx + TOUCH_TILT_OFFSET] & NEGITIVE_FLAG ) { + /* Transfer Tilt to Negitive */ + ilits->pen_info.tilt_x = buf[PenStartIdx + TOUCH_TILT_OFFSET] | 0xFFFFFF00; + } else { + ilits->pen_info.tilt_x = buf[PenStartIdx + TOUCH_TILT_OFFSET]; + } + + if ( buf[PenStartIdx + TOUCH_TILT_OFFSET + 1] & NEGITIVE_FLAG ) { + /* Transfer Tilt to Negitive */ + ilits->pen_info.tilt_y = buf[PenStartIdx + TOUCH_TILT_OFFSET + 1] | 0xFFFFFF00; + } else { + ilits->pen_info.tilt_y = buf[PenStartIdx + TOUCH_TILT_OFFSET + 1]; + } + + /* Pen ID */ + for (pen_id_index = 0; pen_id_index < P5_X_PEN_ID_LEN; pen_id_index++) { + ilits->pen_info.pen_id[pen_id_index] = buf[PenStartIdx + PEN_ID_OFFSET + pen_id_index]; + } + } else { + ilits->curt_touch[PEN_INDEX] = 0; + ILI_DBG("Report Switch is Only Report Hand\n"); + continue; + } + } else { + if (buf[len - P5_X_INFO_CHECKSUM_LENGTH - 1] != ONLY_REPORT_PEN) { + if (ilits->rib.nCustomerType == ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + if ((buf[(4 * i) + 1] == 0xFF) && (buf[(4 * i) + 2] == 0xFF) && (buf[(4 * i) + 3] == 0xFF)) { + if (MT_B_TYPE) + ilits->curt_touch[i] = 0; + continue; + } + xop = (((buf[(4 * i) + 1] & 0xF0) << 4) | (buf[(4 * i) + 2])); + yop = (((buf[(4 * i) + 1] & 0x0F) << 8) | (buf[(4 * i) + 3])); + } else if (ilits->rib.nCustomerType != ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + if ((buf[(4 * i) + 1 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF) && (buf[(4 * i) + 2 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF) && (buf[(4 * i) + 3 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF)) { + if (MT_B_TYPE) + ilits->curt_touch[i] = 0; + continue; + } + xop = (((buf[(4 * i) + 1 + P5_X_DEMO_MODE_PACKET_INFO_LEN] & 0xF0) << 4) | (buf[(4 * i) + 2 + P5_X_DEMO_MODE_PACKET_INFO_LEN])); + yop = (((buf[(4 * i) + 1 + P5_X_DEMO_MODE_PACKET_INFO_LEN] & 0x0F) << 8) | (buf[(4 * i) + 3 + P5_X_DEMO_MODE_PACKET_INFO_LEN])); + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + if ((buf[(5 * i) + 1 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF) && (buf[(5 * i) + 2 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF) && + (buf[(5 * i) + 3 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF) && (buf[(5 * i) + 4 + P5_X_DEMO_MODE_PACKET_INFO_LEN] == 0xFF)) { + if (MT_B_TYPE) + ilits->curt_touch[i] = 0; + continue; + } + xop = ((buf[(5 * i) + 1 + P5_X_DEMO_MODE_PACKET_INFO_LEN] << 8) | (buf[(5 * i) + 2 + P5_X_DEMO_MODE_PACKET_INFO_LEN])); + yop = ((buf[(5 * i) + 3 + P5_X_DEMO_MODE_PACKET_INFO_LEN] << 8) | (buf[(5 * i) + 4 + P5_X_DEMO_MODE_PACKET_INFO_LEN])); + } + } else { + continue; + } + } + + if (ilits->pen_info.active == true) { + if (ilits->trans_xy) { + touch_info[PEN_INDEX].x = xop; + touch_info[PEN_INDEX].y = yop; + } else { + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + touch_info[PEN_INDEX].x = xop * ilits->panel_wid / TPD_WIDTH; + touch_info[PEN_INDEX].y = yop * ilits->panel_hei / TPD_HEIGHT; + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + touch_info[PEN_INDEX].x = xop * ilits->panel_wid / ilits->max_x; + touch_info[PEN_INDEX].y = yop * ilits->panel_hei / ilits->max_y; + } + } + touch_info[PEN_INDEX].id = i; + touch_info[PEN_INDEX].pressure = ( buf[PenStartIdx + TOUCH_PRESS_OFFSET] << 8 ) + buf[PenStartIdx + TOUCH_PRESS_OFFSET + 1]; + + ilits->pen++; + ilits->curt_touch[PEN_INDEX] = 1; + ilits->prev_touch[PEN_INDEX] = 1; + } else { + if (ilits->trans_xy) { + touch_info[ilits->finger].x = xop; + touch_info[ilits->finger].y = yop; + } else { + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + touch_info[ilits->finger].x = xop * ilits->panel_wid / TPD_WIDTH; + touch_info[ilits->finger].y = yop * ilits->panel_hei / TPD_HEIGHT; + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + touch_info[ilits->finger].x = xop * ilits->panel_wid / ilits->max_x; + touch_info[ilits->finger].y = yop * ilits->panel_hei / ilits->max_y; + } + } + touch_info[ilits->finger].id = i; + + if (MT_PRESSURE) { + if (ilits->rib.nCustomerType == ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + touch_info[ilits->finger].pressure = buf[(4 * i) + 4]; + } else if (ilits->rib.nCustomerType != ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + touch_info[ilits->finger].pressure = buf[(4 * i) + 4 + P5_X_DEMO_MODE_PACKET_INFO_LEN]; + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + touch_info[ilits->finger].pressure = buf[(5 * i) + 5 + P5_X_DEMO_MODE_PACKET_INFO_LEN]; + } + } else { + touch_info[ilits->finger].pressure = 1; + } + + ilits->finger++; + if (MT_B_TYPE) + ilits->curt_touch[i] = 1; + } + + ILI_DBG("original x = %d, y = %d\n", xop, yop); + } + +#ifdef ROI + ili_read_knuckle_roi_data(); +#endif + ILI_DBG("figner number = %d, LastTouch = %d\n", ilits->finger, ilits->last_touch); + + if (ilits->pen) { + if (ilits->pen_info.finger_touch) { + ilits->pen_info.finger_touch = false; + ili_touch_release_all_point(); + + if (MT_PRESSURE) + input_report_abs(ilits->input, ABS_MT_PRESSURE, 0); + } + /* Pen Down */ + ili_pen_down(touch_info[PEN_INDEX].x, touch_info[PEN_INDEX].y, touch_info[PEN_INDEX].pressure); + } else { + /* Pen Up */ + if (ilits->curt_touch[PEN_INDEX] == 0 && ilits->prev_touch[PEN_INDEX] == 1) { + ili_pen_release(); + } + ilits->prev_touch[PEN_INDEX] = ilits->curt_touch[PEN_INDEX]; + } + + if (ilits->pen == 0) { + if (ilits->finger) { + if (MT_B_TYPE) { + /* Finger Touch */ + for (i = 0; i < ilits->finger; i++) { + if (touch_info[i].id < MAX_TOUCH_NUM) { + ilits->pen_info.finger_touch = true; + input_report_key(ilits->input, BTN_TOUCH, 1); + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + input_report_key(ilits->input, BTN_TOOL_FINGER, 1); + } + } + /* Finger Release */ + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) { + ili_touch_release(0, 0, i); + } + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + } else { + for (i = 0; i < ilits->finger; i++) { + if (touch_info[i].id < MAX_TOUCH_NUM) { + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + } + } + } + input_sync(ilits->input); + ilits->last_touch = ilits->finger; + } else { + if (ilits->last_touch) { + if (MT_B_TYPE) { + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) + ili_touch_release(0, 0, i); + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + + if (MT_PRESSURE) + input_report_abs(ilits->input, ABS_MT_PRESSURE, 0); + + input_report_key(ilits->input, BTN_TOUCH, 0); + input_report_key(ilits->input, BTN_TOOL_FINGER, 0); + } else { + ili_touch_release(0, 0, 0); + } + input_sync(ilits->input); + ilits->last_touch = 0; + ilits->pen_info.finger_touch = false; + } + } + } +} + +void ili_report_pen_ap_mode(u8 *buf, int len) +{ + ili_pen_demo_mode_report_point(buf, len); + ilitek_tddi_touch_customer_data_parsing(buf); + ilitek_tddi_touch_send_debug_data(buf, len); +} + +/*Pen report single event, Finger report Mulit event*/ +void ili_pen_debug_mode_report_point(u8 *buf, int len, u8 offset) +{ + int i = 0, pen_id_index = 0; + u32 xop = 0, yop = 0, PenStartIdx = 0; + static u8 p[MAX_TOUCH_NUM]; + + memset(touch_info, 0x0, sizeof(touch_info)); + + ilits->finger = 0; + ilits->pen = 0; + + for (i = 0; i < ilits->touch_num; i++) { + if (ilits->rib.nCustomerType == ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + PenStartIdx = P5_X_DEBUG_LOW_RESOLUTION_FINGER_DATA_LENGTH + ilits->cdc_data_len; + } else if (ilits->rib.nCustomerType != ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + PenStartIdx = P5_X_DEBUG_LOW_RESOLUTION_FINGER_DATA_LENGTH + ilits->cdc_data_len + P5_X_CUSTOMER_LENGTH; + } else if (ilits->rib.nCustomerType != ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + PenStartIdx = P5_X_DEBUG_HIGH_RESOLUTION_FINGER_DATA_LENGTH + ilits->cdc_data_len + P5_X_CUSTOMER_LENGTH; + } else if (ilits->rib.nCustomerType == ilits->customertype_off && ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + PenStartIdx = P5_X_DEBUG_HIGH_RESOLUTION_FINGER_DATA_LENGTH + ilits->cdc_data_len; + } + + /* cdc debug lite */ + if (ilits->tp_data_format == DATA_FORMAT_DEBUG_LITE_ROI || ilits->tp_data_format == DATA_FORMAT_DEBUG_LITE_WINDOW + || ilits->tp_data_format == DATA_FORMAT_DEBUG_LITE_AREA) { + /* PenStartIdx = P5_X_DEBUG_LITE_LENGTH - Pendata - other len - checksum */ + PenStartIdx = 284; + } else if (ilits->tp_data_format == DATA_FORMAT_DEBUG_LITE_PEN) { + /* PenStartIdx = P5_X_DEBUG_LITE_PEN_LENGTH - Pendata - PenCDCData - other len - checksum */ + PenStartIdx = 39; + /* debug lite pen only support pen and 1 finger */ + if (i > 0 && i < MAX_TOUCH_NUM) + continue; + } + + PenStartIdx -= offset; + + if (buf[len - offset - P5_X_INFO_CHECKSUM_LENGTH - 1] != ONLY_REPORT_HAND) { + ILI_DBG("Pen Switch = 0x%X\n", buf[PenStartIdx]); + if ((buf[PenStartIdx] & TIPSWITCH_HOVER_BIT) == RELEASE_PEN) { + ilits->pen_info.active = false; + } else { + ilits->pen_info.active = true; + } + } else { + ilits->pen_info.active = false; + } + ILI_DBG("Pen is active : %s\n", ilits->pen_info.active ? "True" : "False"); + + if (i >= MAX_TOUCH_NUM) { + if (ilits->pen_info.active) { + xop = ((buf[PenStartIdx + 2] << 8) | (buf[PenStartIdx + 3])); + yop = ((buf[PenStartIdx + 4] << 8) | (buf[PenStartIdx + 5])); + + ilits->pen_info.TipSwitch = buf[PenStartIdx] & BIT(0); + ilits->pen_info.BarelSwitch = (buf[PenStartIdx] & BIT(1)) >> 1; + ilits->pen_info.Eraser = (buf[PenStartIdx] & BIT(2)) >> 2; + ilits->pen_info.InRange = (buf[PenStartIdx] & BIT(4)) >> 4; + + if ( buf[PenStartIdx + TOUCH_TILT_OFFSET] & NEGITIVE_FLAG ) { + /* Transfer Tilt to Negitive */ + ilits->pen_info.tilt_x = buf[PenStartIdx + TOUCH_TILT_OFFSET] | 0xFFFFFF00; + } else { + ilits->pen_info.tilt_x = buf[PenStartIdx + TOUCH_TILT_OFFSET]; + } + + if ( buf[PenStartIdx + TOUCH_TILT_OFFSET + 1] & NEGITIVE_FLAG ) { + /* Transfer Tilt to Negitive */ + ilits->pen_info.tilt_y = buf[PenStartIdx + TOUCH_TILT_OFFSET + 1] | 0xFFFFFF00; + } else { + ilits->pen_info.tilt_y = buf[PenStartIdx + TOUCH_TILT_OFFSET + 1]; + } + + if ( ilits->pen_info.TipSwitch ) { + /* Pen ID */ + for (pen_id_index = 0; pen_id_index < P5_X_PEN_ID_LEN; pen_id_index++) { + ilits->pen_info.pen_id[pen_id_index] = buf[PenStartIdx + PEN_ID_OFFSET + pen_id_index]; + } + ILI_DBG("\n"); + } + } else { + ilits->curt_touch[PEN_INDEX] = 0; + ILI_DBG("Report Switch is Only Report Hand\n"); + continue; + } + } else { + if (buf[len - P5_X_INFO_CHECKSUM_LENGTH - 1] != ONLY_REPORT_PEN) { + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + if ((buf[(3 * i)] == 0xFF) && (buf[(3 * i) + 1] == 0xFF) && (buf[(3 * i) + 2] == 0xFF)) { + if (MT_B_TYPE) + ilits->curt_touch[i] = 0; + continue; + } + xop = (((buf[(3 * i)] & 0xF0) << 4) | (buf[(3 * i) + 1])); + yop = (((buf[(3 * i)] & 0x0F) << 8) | (buf[(3 * i) + 2])); + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + if ((buf[(4 * i)] == 0xFF) && (buf[(4 * i) + 1] == 0xFF) && (buf[(4 * i) + 2] == 0xFF) && (buf[(4 * i) + 3] == 0xFF)) { + if (MT_B_TYPE) + ilits->curt_touch[i] = 0; + continue; + } + xop = ((buf[(4 * i) + 0] << 8) | (buf[(4 * i) + 1])); + yop = ((buf[(4 * i) + 2] << 8) | (buf[(4 * i) + 3])); + } + } else { + continue; + } + } + + if (ilits->pen_info.active == true) { + if (ilits->trans_xy) { + touch_info[PEN_INDEX].x = xop; + touch_info[PEN_INDEX].y = yop; + } else { + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + touch_info[PEN_INDEX].x = xop * ilits->panel_wid / TPD_WIDTH; + touch_info[PEN_INDEX].y = yop * ilits->panel_hei / TPD_HEIGHT; + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + touch_info[PEN_INDEX].x = xop * ilits->panel_wid / ilits->max_x; + touch_info[PEN_INDEX].y = yop * ilits->panel_hei / ilits->max_y; + } + } + + touch_info[PEN_INDEX].id = i; + touch_info[PEN_INDEX].pressure = ( buf[PenStartIdx + TOUCH_PRESS_OFFSET] << 8 ) + buf[PenStartIdx + TOUCH_PRESS_OFFSET + 1]; + + ilits->pen++; + ilits->curt_touch[PEN_INDEX] = 1; + ilits->prev_touch[PEN_INDEX] = 1; + } else { + if (ilits->trans_xy) { + touch_info[ilits->finger].x = xop; + touch_info[ilits->finger].y = yop; + } else { + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + touch_info[ilits->finger].x = xop * ilits->panel_wid / TPD_WIDTH; + touch_info[ilits->finger].y = yop * ilits->panel_hei / TPD_HEIGHT; + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + touch_info[ilits->finger].x = xop * ilits->panel_wid / ilits->max_x; + touch_info[ilits->finger].y = yop * ilits->panel_hei / ilits->max_y; + } + } + touch_info[ilits->finger].id = i; + + if (MT_PRESSURE) { + /* + * Since there's no pressure data in debug mode, we make fake values + * for android system if pressure needs to be reported. + */ + if (p[ilits->finger] == 1) + touch_info[ilits->finger].pressure = p[ilits->finger] = 2; + else + touch_info[ilits->finger].pressure = p[ilits->finger] = 1; + } else { + touch_info[ilits->finger].pressure = 1; + } + + ilits->finger++; + if (MT_B_TYPE) + ilits->curt_touch[i] = 1; + } + + ILI_DBG("original x = %d, y = %d\n", xop, yop); + } + + ILI_DBG("figner number = %d, LastTouch = %d\n", ilits->finger, ilits->last_touch); + + if (ilits->pen) { + if (ilits->pen_info.finger_touch) { + ilits->pen_info.finger_touch = false; + ili_touch_release_all_point(); + + if (MT_PRESSURE) + input_report_abs(ilits->input, ABS_MT_PRESSURE, 0); + } + /* Pen Down */ + ili_pen_down(touch_info[PEN_INDEX].x, touch_info[PEN_INDEX].y, touch_info[PEN_INDEX].pressure); + } else { + /* Pen Up */ + if (ilits->curt_touch[PEN_INDEX] == 0 && ilits->prev_touch[PEN_INDEX] == 1) { + ili_pen_release(); + } + ilits->prev_touch[PEN_INDEX] = ilits->curt_touch[PEN_INDEX]; + } + + if (ilits->pen == 0) { + if (ilits->finger) { + if (MT_B_TYPE) { + /* Finger Touch */ + for (i = 0; i < ilits->finger; i++) { + if (touch_info[i].id < MAX_TOUCH_NUM) { + ilits->pen_info.finger_touch = true; + input_report_key(ilits->input, BTN_TOUCH, 1); + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + input_report_key(ilits->input, BTN_TOOL_FINGER, 1); + } + } + /* Finger Release */ + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) { + ili_touch_release(0, 0, i); + } + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + } else { + for (i = 0; i < ilits->finger; i++) { + if (touch_info[i].id < MAX_TOUCH_NUM) { + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + } + } + } + input_sync(ilits->input); + ilits->last_touch = ilits->finger; + } else { + if (ilits->last_touch) { + if (MT_B_TYPE) { + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) + ili_touch_release(0, 0, i); + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + + if (MT_PRESSURE) + input_report_abs(ilits->input, ABS_MT_PRESSURE, 0); + + input_report_key(ilits->input, BTN_TOUCH, 0); + input_report_key(ilits->input, BTN_TOOL_FINGER, 0); + } else { + ili_touch_release(0, 0, 0); + } + input_sync(ilits->input); + ilits->last_touch = 0; + ilits->pen_info.finger_touch = false; + } + } + } +} + +void ili_debug_mode_report_point(u8 *buf, int len) +{ + int i = 0; + u32 xop = 0, yop = 0; + static u8 p[MAX_TOUCH_NUM]; + + memset(touch_info, 0x0, sizeof(touch_info)); + + ilits->finger = 0; + + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + if ((buf[(3 * i)] == 0xFF) && (buf[(3 * i) + 1] == 0xFF) && (buf[(3 * i) + 2] == 0xFF)) { + if (MT_B_TYPE) + ilits->curt_touch[i] = 0; + continue; + } + xop = (((buf[(3 * i)] & 0xF0) << 4) | (buf[(3 * i) + 1])); + yop = (((buf[(3 * i)] & 0x0F) << 8) | (buf[(3 * i) + 2])); + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + if ((buf[(4 * i)] == 0xFF) && (buf[(4 * i) + 1] == 0xFF) && (buf[(4 * i) + 2] == 0xFF) && (buf[(4 * i) + 3] == 0xFF)) { + if (MT_B_TYPE) + ilits->curt_touch[i] = 0; + continue; + } + xop = ((buf[(4 * i) + 0] << 8) | (buf[(4 * i) + 1])); + yop = ((buf[(4 * i) + 2] << 8) | (buf[(4 * i) + 3])); + } + + if (ilits->trans_xy) { + touch_info[ilits->finger].x = xop; + touch_info[ilits->finger].y = yop; + } else { + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + touch_info[ilits->finger].x = xop * ilits->panel_wid / TPD_WIDTH; + touch_info[ilits->finger].y = yop * ilits->panel_hei / TPD_HEIGHT; + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + touch_info[ilits->finger].x = xop * ilits->panel_wid / ilits->max_x; + touch_info[ilits->finger].y = yop * ilits->panel_hei / ilits->max_y; + } + } + + touch_info[ilits->finger].id = i; + + if (MT_PRESSURE) { + /* + * Since there's no pressure data in debug mode, we make fake values + * for android system if pressure needs to be reported. + */ + if (p[ilits->finger] == 1) + touch_info[ilits->finger].pressure = p[ilits->finger] = 2; + else + touch_info[ilits->finger].pressure = p[ilits->finger] = 1; + } else { + touch_info[ilits->finger].pressure = 1; + } + + ILI_DBG("original x = %d, y = %d\n", xop, yop); + ilits->finger++; + if (MT_B_TYPE) + ilits->curt_touch[i] = 1; + } + + ILI_DBG("figner number = %d, LastTouch = %d\n", ilits->finger, ilits->last_touch); + + if (ilits->finger) { + if (MT_B_TYPE) { + for (i = 0; i < ilits->finger; i++) { + input_report_key(ilits->input, BTN_TOUCH, 1); + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + input_report_key(ilits->input, BTN_TOOL_FINGER, 1); + } + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) + ili_touch_release(0, 0, i); + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + } else { + for (i = 0; i < ilits->finger; i++) + ili_touch_press(touch_info[i].x, touch_info[i].y, touch_info[i].pressure, touch_info[i].id); + } + input_sync(ilits->input); + ilits->last_touch = ilits->finger; + } else { + if (ilits->last_touch) { + if (MT_B_TYPE) { + for (i = 0; i < MAX_TOUCH_NUM; i++) { + if (ilits->curt_touch[i] == 0 && ilits->prev_touch[i] == 1) + ili_touch_release(0, 0, i); + ilits->prev_touch[i] = ilits->curt_touch[i]; + } + input_report_key(ilits->input, BTN_TOUCH, 0); + input_report_key(ilits->input, BTN_TOOL_FINGER, 0); + } else { + ili_touch_release(0, 0, 0); + } + input_sync(ilits->input); + ilits->last_touch = 0; + } + } +} + +void ili_report_debug_mode(u8 *buf, int len) +{ + ili_debug_mode_report_point(buf + 5, len); + + ilitek_tddi_touch_customer_data_parsing(buf); + + ilitek_tddi_touch_send_debug_data(buf, len); +} + +void ili_report_pen_debug_mode(u8 *buf, int len) +{ + u8 offset = 5; + ilits->touch_num = MAX_TOUCH_NUM + MAX_PEN_NUM; + ILI_DBG("Pen Type = 0x%X, Max Touch Num = %d, Pen Data Mode = %d\n",ilits->pen_info.report_type, ilits->touch_num, ilits->pen_info.pen_data_mode); + + ili_pen_debug_mode_report_point(buf + offset, len, offset); + + if (ilits->pen_info.report_type != P5_X_ONLY_PEN_TYPE) { + ilitek_tddi_touch_customer_data_parsing(buf); + } + + ilitek_tddi_touch_send_debug_data(buf, len); +} + +void ili_report_debug_lite_mode(u8 *buf, int len) +{ + ili_debug_mode_report_point(buf + 4, len); + + ilitek_tddi_touch_send_debug_data(buf, len); +} + +void ili_report_proximity_mode(u8 *buf, int len) +{ + struct input_dev *input = ilits->input; + u8 proxmity_face_status = PROXIMITY_NONE_STATE_0; + + if (!ilits->prox_face_mode) { + ILI_ERR("proximity face mode Error\n"); + return; + } + + ili_irq_disable(); + + if (buf[1] == PROXIMITY_NEAR_STATE_1 || buf[1] == PROXIMITY_NEAR_STATE_2 || buf[1] == PROXIMITY_NEAR_STATE_3) { + proxmity_face_status = PROXIMITY_NEAR_STATE; + ILI_DBG("Set proximity State : Near, cmd : %X\n", buf[1]); + } else if(buf[1] == PROXIMITY_FAR_STATE_5) { + proxmity_face_status = PROXIMITY_FAR_STATE; + ILI_DBG("Set proximity State : Far, cmd : %X\n", buf[1]); + } else { + proxmity_face_status = PROXIMITY_IGNORE_STATE; + ILI_DBG("Set proximity State : Ignore, cmd : %X\n", buf[1]); + } + + if ((ilits->actual_tp_mode == P5_X_FW_AP_MODE) && (proxmity_face_status == PROXIMITY_NEAR_STATE) && (ilits->power_status == true) && (ilits->proxmity_face == false)) { + input_report_key(input, KEY_GESTURE_POWER, 1); + input_sync(input); + input_report_key(input, KEY_GESTURE_POWER, 0); + input_sync(input); + ilits->proxmity_face = true; + } else if ((ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) && (proxmity_face_status == PROXIMITY_FAR_STATE) && (ilits->power_status == false) && (ilits->proxmity_face == true)) { + input_report_key(input, KEY_GESTURE_POWER, 1); + input_sync(input); + input_report_key(input, KEY_GESTURE_POWER, 0); + input_sync(input); + ilits->proxmity_face = false; + } + + ILI_DBG("TP mode (0x%x)\n", ilits->actual_tp_mode); + ILI_DBG("power_status %s\n", ilits->power_status ? "True" : "False"); + ILI_DBG("proxmity face status : %s, proxmity face : %s\n", (proxmity_face_status == PROXIMITY_NEAR_STATE) ? "Near" : (proxmity_face_status == PROXIMITY_FAR_STATE) ? "Far" : "Ignore", ilits->proxmity_face ? "True" : "False"); + + ili_irq_enable(); +} + +void ili_report_gesture_mode(u8 *buf, int len) +{ + int lu_x = 0, lu_y = 0, rd_x = 0, rd_y = 0, score = 0; + struct gesture_coordinate *gc = ilits->gcoord; +#ifdef ILI_SENSOR_EN + static int report_cnt = 0; +#else + struct input_dev *input = ilits->input; +#endif + bool transfer = ilits->trans_xy; + u8 ges[P5_X_GESTURE_INFO_LENGTH_HIGH_RESOLUTION] = {0}; + + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + ipio_memcpy(ges, buf, len, P5_X_GESTURE_INFO_LENGTH); + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + ipio_memcpy(ges, buf, len, P5_X_GESTURE_INFO_LENGTH_HIGH_RESOLUTION); + } + + memset(gc, 0x0, sizeof(struct gesture_coordinate)); + + gc->code = ges[1]; + + /* Parsing gesture coordinate & Score*/ + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + score = ges[36]; + gc->pos_start.x = ((ges[4] & 0xF0) << 4) | ges[5]; + gc->pos_start.y = ((ges[4] & 0x0F) << 8) | ges[6]; + gc->pos_end.x = ((ges[7] & 0xF0) << 4) | ges[8]; + gc->pos_end.y = ((ges[7] & 0x0F) << 8) | ges[9]; + gc->pos_1st.x = ((ges[16] & 0xF0) << 4) | ges[17]; + gc->pos_1st.y = ((ges[16] & 0x0F) << 8) | ges[18]; + gc->pos_2nd.x = ((ges[19] & 0xF0) << 4) | ges[20]; + gc->pos_2nd.y = ((ges[19] & 0x0F) << 8) | ges[21]; + gc->pos_3rd.x = ((ges[22] & 0xF0) << 4) | ges[23]; + gc->pos_3rd.y = ((ges[22] & 0x0F) << 8) | ges[24]; + gc->pos_4th.x = ((ges[25] & 0xF0) << 4) | ges[26]; + gc->pos_4th.y = ((ges[25] & 0x0F) << 8) | ges[27]; + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + score = ges[46]; + gc->pos_start.x = (ges[4] << 8) | ges[5]; + gc->pos_start.y = (ges[6] << 8) | ges[7]; + gc->pos_end.x = (ges[8] << 8) | ges[9]; + gc->pos_end.y = (ges[10] << 8) | ges[11]; + gc->pos_1st.x = (ges[20] << 8) | ges[21]; + gc->pos_1st.y = (ges[22] << 8) | ges[23]; + gc->pos_2nd.x = (ges[24] << 4) | ges[25]; + gc->pos_2nd.y = (ges[26] << 8) | ges[27]; + gc->pos_3rd.x = (ges[28] << 8) | ges[29]; + gc->pos_3rd.y = (ges[30] << 8) | ges[31]; + gc->pos_4th.x = (ges[32] << 8) | ges[33]; + gc->pos_4th.y = (ges[34] << 8) | ges[35]; + } + ILI_INFO("gesture code = 0x%x, score = %d\n", gc->code, score); + + switch (gc->code) { + case GESTURE_SINGLECLICK: + case GESTURE_DOUBLECLICK: + ILI_INFO("Gesture Click key event 0x%x\n", gc->code); +#ifdef ILI_SENSOR_EN + if (!(ilits->wakeable && ilits->should_enable_gesture)) { + ILI_INFO("Gesture got but wakeable not set. Skip this gesture."); + return; + } + if (ilits->report_gesture_key) { + int key_code = BTN_TRIGGER_HAPPY3; +#ifdef ILI_DOUBLE_TAP_CTRL + if (GESTURE_DOUBLECLICK == gc->code) { + key_code = BTN_TRIGGER_HAPPY6; + } +#endif + input_report_key(ilits->sensor_pdata->input_sensor_dev, key_code, 1); + input_sync(ilits->sensor_pdata->input_sensor_dev); + input_report_key(ilits->sensor_pdata->input_sensor_dev, key_code, 0); + input_sync(ilits->sensor_pdata->input_sensor_dev); + ++report_cnt; + ILI_INFO("input report keycode %d, report_cnt: %d", key_code, report_cnt); + } else { + input_report_abs(ilits->sensor_pdata->input_sensor_dev, + ABS_DISTANCE, + ++report_cnt); + input_sync(ilits->sensor_pdata->input_sensor_dev); + } + if (report_cnt >= REPORT_MAX_COUNT) { + report_cnt = 0; + } +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&(ilits->gesture_wakelock), msecs_to_jiffies(5000)); +#else + PM_WAKEUP_EVENT(ilits->gesture_wakelock, 5000); +#endif +#else + input_report_key(input, KEY_GESTURE_POWER, 1); + input_sync(input); + input_report_key(input, KEY_GESTURE_POWER, 0); + input_sync(input); +#endif + gc->type = GESTURE_DOUBLECLICK; + gc->clockwise = 1; + gc->pos_end.x = gc->pos_start.x; + gc->pos_end.y = gc->pos_start.y; + break; + case GESTURE_LEFT: + gc->type = GESTURE_LEFT; + gc->clockwise = 1; + break; + case GESTURE_RIGHT: + gc->type = GESTURE_RIGHT; + gc->clockwise = 1; + break; + case GESTURE_UP: + gc->type = GESTURE_UP; + gc->clockwise = 1; + break; + case GESTURE_DOWN: + gc->type = GESTURE_DOWN; + gc->clockwise = 1; + break; + case GESTURE_O: + gc->type = GESTURE_O; + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + gc->clockwise = (ges[34] > 1) ? 0 : ges[34]; + lu_x = (((ges[28] & 0xF0) << 4) | (ges[29])); + lu_y = (((ges[28] & 0x0F) << 8) | (ges[30])); + rd_x = (((ges[31] & 0xF0) << 4) | (ges[32])); + rd_y = (((ges[31] & 0x0F) << 8) | (ges[33])); + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + gc->clockwise = (ges[44] > 1) ? 0 : ges[44]; + lu_x = ((ges[36] << 8) | (ges[37])); + lu_y = ((ges[38] << 8) | (ges[39])); + rd_x = ((ges[40] << 8) | (ges[41])); + rd_y = ((ges[42] << 8) | (ges[43])); + } + + gc->pos_1st.x = ((rd_x + lu_x) / 2); + gc->pos_1st.y = lu_y; + gc->pos_2nd.x = lu_x; + gc->pos_2nd.y = ((rd_y + lu_y) / 2); + gc->pos_3rd.x = ((rd_x + lu_x) / 2); + gc->pos_3rd.y = rd_y; + gc->pos_4th.x = rd_x; + gc->pos_4th.y = ((rd_y + lu_y) / 2); + break; + case GESTURE_W: + gc->type = GESTURE_W; + gc->clockwise = 1; + break; + case GESTURE_M: + gc->type = GESTURE_M; + gc->clockwise = 1; + break; + case GESTURE_V: + gc->type = GESTURE_V; + gc->clockwise = 1; + break; + case GESTURE_C: + gc->type = GESTURE_C; + gc->clockwise = 1; + break; + case GESTURE_E: + gc->type = GESTURE_E; + gc->clockwise = 1; + break; + case GESTURE_S: + gc->type = GESTURE_S; + gc->clockwise = 1; + break; + case GESTURE_Z: + gc->type = GESTURE_Z; + gc->clockwise = 1; + break; + case GESTURE_TWOLINE_DOWN: + gc->type = GESTURE_TWOLINE_DOWN; + gc->clockwise = 1; + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + gc->pos_1st.x = (((ges[10] & 0xF0) << 4) | (ges[11])); + gc->pos_1st.y = (((ges[10] & 0x0F) << 8) | (ges[12])); + gc->pos_2nd.x = (((ges[13] & 0xF0) << 4) | (ges[14])); + gc->pos_2nd.y = (((ges[13] & 0x0F) << 8) | (ges[15])); + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + gc->pos_1st.x = ((ges[12] << 8) | (ges[13])); + gc->pos_1st.y = ((ges[14] << 8) | (ges[15])); + gc->pos_2nd.x = ((ges[16] << 8) | (ges[17])); + gc->pos_2nd.y = ((ges[18] << 8) | (ges[19])); + } + break; + default: + ILI_ERR("Unknown gesture code\n"); + break; + } + + if (!transfer) { + if (ilits->rib.nReportResolutionMode == POSITION_LOW_RESOLUTION) { + gc->pos_start.x = gc->pos_start.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_start.y = gc->pos_start.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_end.x = gc->pos_end.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_end.y = gc->pos_end.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_1st.x = gc->pos_1st.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_1st.y = gc->pos_1st.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_2nd.x = gc->pos_2nd.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_2nd.y = gc->pos_2nd.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_3rd.x = gc->pos_3rd.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_3rd.y = gc->pos_3rd.y * ilits->panel_hei / TPD_HEIGHT; + gc->pos_4th.x = gc->pos_4th.x * ilits->panel_wid / TPD_WIDTH; + gc->pos_4th.y = gc->pos_4th.y * ilits->panel_hei / TPD_HEIGHT; + } else if (ilits->rib.nReportResolutionMode == POSITION_HIGH_RESOLUTION) { + gc->pos_start.x = gc->pos_start.x * ilits->panel_wid / ilits->max_x; + gc->pos_start.y = gc->pos_start.y * ilits->panel_hei / ilits->max_y; + gc->pos_end.x = gc->pos_end.x * ilits->panel_wid / ilits->max_x; + gc->pos_end.y = gc->pos_end.y * ilits->panel_hei / ilits->max_y; + gc->pos_1st.x = gc->pos_1st.x * ilits->panel_wid / ilits->max_x; + gc->pos_1st.y = gc->pos_1st.y * ilits->panel_hei / ilits->max_y; + gc->pos_2nd.x = gc->pos_2nd.x * ilits->panel_wid / ilits->max_x; + gc->pos_2nd.y = gc->pos_2nd.y * ilits->panel_hei / ilits->max_y; + gc->pos_3rd.x = gc->pos_3rd.x * ilits->panel_wid / ilits->max_x; + gc->pos_3rd.y = gc->pos_3rd.y * ilits->panel_hei / ilits->max_y; + gc->pos_4th.x = gc->pos_4th.x * ilits->panel_wid / ilits->max_x; + gc->pos_4th.y = gc->pos_4th.y * ilits->panel_hei / ilits->max_y; + } + + } + + ILI_INFO("Transfer = %d, Type = %d, clockwise = %d\n", transfer, gc->type, gc->clockwise); + ILI_INFO("Gesture Points: (%d, %d)(%d, %d)(%d, %d)(%d, %d)(%d, %d)(%d, %d)\n", + gc->pos_start.x, gc->pos_start.y, + gc->pos_end.x, gc->pos_end.y, + gc->pos_1st.x, gc->pos_1st.y, + gc->pos_2nd.x, gc->pos_2nd.y, + gc->pos_3rd.x, gc->pos_3rd.y, + gc->pos_4th.x, gc->pos_4th.y); + + ilitek_tddi_touch_send_debug_data(buf, len); +} + +void ili_report_i2cuart_mode(u8 *buf, int len) +{ + int type = buf[3] & 0x0F; + int need_read_len = 0, one_data_bytes = 0; + int actual_len = len - 5; + int uart_len; + u8 *uart_buf = NULL, *total_buf = NULL; + + ILI_DBG("data[3] = %x, type = %x, actual_len = %d\n", + buf[3], type, actual_len); + + need_read_len = buf[1] * buf[2]; + + if (type == 0 || type == 1 || type == 6) { + one_data_bytes = 1; + } else if (type == 2 || type == 3) { + one_data_bytes = 2; + } else if (type == 4 || type == 5) { + one_data_bytes = 4; + } + + need_read_len = need_read_len * one_data_bytes + 1; + ILI_DBG("need_read_len = %d one_data_bytes = %d\n", need_read_len, one_data_bytes); + + if (need_read_len <= actual_len) { + ilitek_tddi_touch_send_debug_data(buf, len); + goto out; + } + + uart_len = need_read_len - actual_len; + ILI_DBG("uart len = %d\n", uart_len); + + uart_buf = kcalloc(uart_len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(uart_buf)) { + ILI_ERR("Failed to allocate uart_buf memory %ld\n", PTR_ERR(uart_buf)); + goto out; + } + + if (ilits->wrapper(NULL, 0, uart_buf, uart_len, OFF, OFF) < 0) { + ILI_ERR("i2cuart read data failed\n"); + goto out; + } + + total_buf = kcalloc(len + uart_len, sizeof(u8), GFP_KERNEL); + if (ERR_ALLOC_MEM(total_buf)) { + ILI_ERR("Failed to allocate total_buf memory %ld\n", PTR_ERR(total_buf)); + goto out; + } + + memcpy(total_buf, buf, len); + memcpy(total_buf + len, uart_buf, uart_len); + ilitek_tddi_touch_send_debug_data(total_buf, len + uart_len); + +out: + ipio_kfree((void **)&uart_buf); + ipio_kfree((void **)&total_buf); + return; +} + +#ifdef ROI +int ili_read_knuckle_roi_data(void) +{ + int i = 0; + int ret = 0; + struct ts_kit_device_data *ts_dev_data = ilits->ts_dev_data; + + ILI_DBG("roi_switch = %d, roi_support =%d\n", + ts_dev_data->ts_platform_data->feature_info.roi_info.roi_switch, + ts_dev_data->ts_platform_data->feature_info.roi_info.roi_supported); + + ILI_INFO("last_fingers = %d, cur_fingers =%d\n", + ilits->last_touch, + ilits->finger); + + if (ts_dev_data->ts_platform_data->feature_info.roi_info.roi_switch && ts_dev_data->ts_platform_data->feature_info.roi_info.roi_supported) { + if (ilits->last_touch != ilits->finger && ilits->finger <= ILITEK_KNUCKLE_ROI_FINGERS) { + ret = ili_config_knuckle_roi_ctrl(CMD_ROI_DATA); + if (ret) { + ILI_ERR("write data failed, ret = %d\n", ret); + return ret; + } + mdelay(1); + ret = ilits->wrapper(NULL, 0, ilits->knuckle_roi_data, ROI_DATA_READ_LENGTH, OFF, OFF); + if (ret) { + ILI_ERR("read data failed, ret = %d\n", ret); + return ret; + } + + ILI_DBG("index = %d, fixed = %d, peak_row = %d, peak_colum = %d\n", + ilits->knuckle_roi_data[0], + ilits->knuckle_roi_data[1], + ilits->knuckle_roi_data[2], + ilits->knuckle_roi_data[3]); + if (debug_en) { + printk("=================== roi data in bytes ===================\n"); + for (i = 4; i < ROI_DATA_READ_LENGTH; i++) { + printk(KERN_CONT "%3d ", ilits->knuckle_roi_data[i]); + if ((i - 3) % 14 == 0) { /* 14 * 14 rawdata bytes */ + printk("\n"); + } + } + } + } + } + return 0; +} +#endif diff --git a/drivers/input/touchscreen/nova_0flash_mmi/Android.mk b/drivers/input/touchscreen/nova_0flash_mmi/Android.mk new file mode 100644 index 000000000000..b4e888ba71b7 --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/Android.mk @@ -0,0 +1,122 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +NOVA_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +NOVA_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +ifneq ($(BOARD_USES_DOUBLE_TAP),) + KERNEL_CFLAGS += CONFIG_INPUT_NOVA_0FLASH_MMI_ENABLE_DOUBLE_TAP=y + KBUILD_OPTIONS += CONFIG_INPUT_NOVA_0FLASH_MMI_ENABLE_DOUBLE_TAP=y +endif + +ifeq ($(NVT_WAKEUP_GESTURE_CTRL),true) + KERNEL_CFLAGS += CONFIG_NVT_WAKEUP_GESTURE_CTRL=y + KBUILD_OPTIONS += CONFIG_NVT_WAKEUP_GESTURE_CTRL=y +endif + +ifeq ($(TOUCHSCREEN_LAST_TIME),true) + KBUILD_OPTIONS += CONFIG_NOVA_TOUCH_LAST_TIME=y +endif + +ifeq ($(CONFIG_INPUT_HIGH_RESOLUTION_N),true) + KERNEL_CFLAGS += CONFIG_INPUT_HIGH_RESOLUTION_N=y +endif + +ifeq ($(CONFIG_STOWED_MODE_EN),true) + KERNEL_CFLAGS += CONFIG_STOWED_MODE_EN=y +endif + +ifneq ($(MOTO_PANEL_CHECK_TOUCH_STATE),) + KBUILD_OPTIONS += CONFIG_INPUT_NOVA_0FLASH_MMI_NOTIFY_TOUCH_STATE=y +endif + +ifneq ($(MOTO_PANEL_CHECK_TOUCH_STATE_V2),) + KBUILD_OPTIONS += CONFIG_INPUT_NOVA_0FLASH_MMI_NOTIFY_TOUCH_STATE=y +endif + +ifneq ($(BOARD_USES_PANEL_NOTIFICATIONS),) + KERNEL_CFLAGS += CONFIG_INPUT_NOVA_PANEL_NOTIFICATIONS=y +endif + +ifeq ($(CONFIG_INPUT_ENABLE_DRM_PANEL),true) + KERNEL_CFLAGS += CONFIG_INPUT_NOVA_DRM_PANEL=y + KBUILD_OPTIONS += CONFIG_INPUT_NOVA_DRM_PANEL=y +endif + +ifeq ($(CONFIG_INPUT_NOVA_USES_CHIP_VER_1),true) + KERNEL_CFLAGS += CONFIG_INPUT_NOVA_CHIP_VER_1=y + KBUILD_OPTIONS += CONFIG_INPUT_NOVA_CHIP_VER_1=y +endif + +ifeq ($(CONFIG_INPUT_NOVA_ESD_ENABLE),true) + KERNEL_CFLAGS += CONFIG_NOVA_ESD_ENABLE=y +endif + +ifeq ($(CONFIG_INPUT_NOVA_LCM_FAST_LIGHTUP),true) + KERNEL_CFLAGS += CONFIG_NOVA_LCM_FAST_LIGHTUP=y +endif + +ifneq ($(BOARD_USES_PEN_NOTIFIER),) + KBUILD_OPTIONS += CONFIG_INPUT_NOVA_0FLASH_MMI_PEN_NOTIFIER=y +endif + +ifneq ($(BOARD_USES_STYLUS_PALM),) + KBUILD_OPTIONS += CONFIG_INPUT_NOVA_0FLASH_MMI_STYLUS_PALM=y +endif + +ifneq ($(BOARD_USES_STYLUS_PALM_RANGE),) + KBUILD_OPTIONS += CONFIG_INPUT_NOVA_0FLASH_MMI_STYLUS_PALM_RANGE=y +endif +ifneq ($(BOARD_USES_EDGE_SUPPRESSION),) + KERNEL_CFLAGS += CONFIG_INPUT_NOVA_EDGE_SUPPRESSION=y +endif +ifeq ($(DRM_PANEL_NOTIFICATIONS),true) + KERNEL_CFLAGS += CONFIG_DRM_PANEL_NOTIFICATIONS=y +endif +ifeq ($(DRM_PANEL_EVENT_NOTIFICATIONS),true) + KBUILD_OPTIONS += CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS=y +endif +ifeq ($(TOUCH_PANEL_NOTIFICATIONS),true) + KERNEL_CFLAGS += CONFIG_TOUCH_PANEL_NOTIFICATIONS=y +endif + +ifeq ($(TOUCHCLASS_MMI_MULTIWAY_UPDATE_FW),true) + KERNEL_CFLAGS += CONFIG_TOUCHCLASS_MMI_MULTIWAY_UPDATE_FW=y +endif + +ifeq ($(BOARD_USES_DOUBLE_TAP_CTRL),true) + KBUILD_OPTIONS += CONFIG_BOARD_USES_DOUBLE_TAP_CTRL=y +endif + +ifeq ($(GTP_ENABLE_DDA_STYLUS),true) + KBUILD_OPTIONS += CONFIG_GTP_DDA_STYLUS=y +endif + +ifeq ($(NVT_SPI_TIMING_CTRL),true) + KBUILD_OPTIONS += CONFIG_SPI_SM8450=y +endif + +include $(CLEAR_VARS) +ifneq ($(BOARD_USES_DOUBLE_TAP),) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/sensors_class.ko +endif +ifneq ($(BOARD_USES_PEN_NOTIFIER),) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/bu520xx_pen.ko +endif +LOCAL_MODULE := nova_0flash_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/mmi_info.ko +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KBUILD_OPTIONS += CONFIG_INPUT_TOUCHSCREEN_MMI_NVT=y + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +endif +ifneq ($(MOTO_PANEL_CHECK_TOUCH_STATE_V2),) + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/msm_drm.ko +endif +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +KBUILD_OPTIONS_GKI += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/nova_0flash_mmi/Kbuild b/drivers/input/touchscreen/nova_0flash_mmi/Kbuild new file mode 100644 index 000000000000..5aac7d2ba3b2 --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/Kbuild @@ -0,0 +1,110 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/nova_0flash_mmi + +ifeq ($(filter m y, $(CONFIG_TOUCHSCREEN_NT36xxx_HOSTDL_SPI)),) + obj-m := nova_0flash_mmi.o +endif +ifneq ($(filter m y,$(CONFIG_INPUT_NOVA_0FLASH_MMI_ENABLE_DOUBLE_TAP)),) + EXTRA_CFLAGS += -DNVT_SENSOR_EN + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers +endif +ifneq ($(filter m y,$(CONFIG_INPUT_NOVA_0FLASH_MMI_NOTIFY_TOUCH_STATE)),) + EXTRA_CFLAGS += -DNVT_SET_TOUCH_STATE +endif + +ifneq ($(filter m y,$(CONFIG_NOVA_TOUCH_LAST_TIME)),) + EXTRA_CFLAGS += -DNVT_TOUCH_LAST_TIME +endif + +ifneq ($(filter m y, $(CONFIG_INPUT_HIGH_RESOLUTION_N)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_HIGH_RESOLUTION_N +endif + +ifneq ($(filter m y, $(CONFIG_STOWED_MODE_EN)),) + EXTRA_CFLAGS += -DNOVA_STOWED_MODE_EN +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_NOVA_PANEL_NOTIFICATIONS)),) +EXTRA_CFLAGS += -DNVT_CONFIG_PANEL_NOTIFICATIONS +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_NOVA_DRM_PANEL)),) + EXTRA_CFLAGS += -DNVT_CONFIG_DRM_PANEL +endif + +ifeq ($(TARGET_BUILD_VARIANT),user) + EXTRA_CFLAGS += -DCFG_CTS_FOR_GKI +endif + +ifneq ($(filter m y,$(CONFIG_TOUCH_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_PANEL_NOTIFICATIONS +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_NOVA_CHIP_VER_1)),) + EXTRA_CFLAGS += -DNVT_CONFIG_CHIP_VER_1 +endif + +ifneq ($(filter m y,$(CONFIG_NOVA_ESD_ENABLE)),) + EXTRA_CFLAGS += -DNVT_CONFIG_ESD_ENABLE +endif + +ifneq ($(filter m y,$(CONFIG_NOVA_LCM_FAST_LIGHTUP)),) + EXTRA_CFLAGS += -DLCM_FAST_LIGHTUP +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_NOVA_0FLASH_MMI_STYLUS_PALM)),) + EXTRA_CFLAGS += -DPALM_GESTURE +endif +ifneq ($(filter m y,$(CONFIG_INPUT_NOVA_0FLASH_MMI_STYLUS_PALM_RANGE)),) + EXTRA_CFLAGS += -DPALM_GESTURE_RANGE +endif +ifneq ($(filter m y,$(CONFIG_INPUT_NOVA_0FLASH_MMI_PEN_NOTIFIER)),) + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../misc/pen/$(GKI_OBJ_MODULE_DIR)/Module.symvers + EXTRA_CFLAGS += -DNOVATECH_PEN_NOTIFIER +endif +ifneq ($(filter m y,$(CONFIG_INPUT_NOVA_EDGE_SUPPRESSION)),) + EXTRA_CFLAGS += -DEDGE_SUPPRESSION +endif + +ifneq ($(filter m y, $(CONFIG_BOARD_USES_DOUBLE_TAP_CTRL)),) + EXTRA_CFLAGS += -DCONFIG_BOARD_USES_DOUBLE_TAP_CTRL +endif + +ifneq ($(filter m y, $(CONFIG_NVT_WAKEUP_GESTURE_CTRL)),) + EXTRA_CFLAGS += -DNVT_WAKEUP_GESTURE_CTRL +endif + +ifneq ($(filter m y, $(CONFIG_SPI_SM8450)),) + EXTRA_CFLAGS += -DCONFIG_SPI_SM8450 +endif + +nova_0flash_mmi-objs += nt36xxx.o +nova_0flash_mmi-objs += nt36xxx_ext_proc.o +nova_0flash_mmi-objs += nt36xxx_fw_update.o +nova_0flash_mmi-objs += nt36xxx_mp_ctrlram.o +ifneq ($(filter m y,$(CONFIG_DRM_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_NOTIFICATIONS +endif +ifneq ($(filter m y,$(CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_EVENT_NOTIFICATIONS +endif +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI_NVT)),) + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers + EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI + nova_0flash_mmi-objs += nt36xxx_mmi_class.o + + ifneq ($(filter m y, $(CONFIG_TOUCHCLASS_MMI_MULTIWAY_UPDATE_FW)),) + EXTRA_CFLAGS += -DTS_MMI_TOUCH_MULTIWAY_UPDATE_FW + endif +endif +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/../../kernel/msm-$(MODULE_KERNEL_VERSION)/Module.symvers + +ifneq ($(filter m y,$(CONFIG_GTP_DDA_STYLUS)),) + EXTRA_CFLAGS += -DCONFIG_MOTO_DDA_PASSIVESTYLUS + nova_0flash_mmi-objs += moto_ts_dda.o +endif + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers + diff --git a/drivers/input/touchscreen/nova_0flash_mmi/Makefile b/drivers/input/touchscreen/nova_0flash_mmi/Makefile new file mode 100644 index 000000000000..7f515f8e71c0 --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/Makefile @@ -0,0 +1,19 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(M)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(M)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(M)/../../../misc/pen/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(OUT_DIR)/../../vendor/qcom/opensource/display-drivers/msm/Module.symvers diff --git a/drivers/input/touchscreen/nova_0flash_mmi/moto_ts_dda.c b/drivers/input/touchscreen/nova_0flash_mmi/moto_ts_dda.c new file mode 100644 index 000000000000..2d123c564994 --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/moto_ts_dda.c @@ -0,0 +1,582 @@ +#include +#include +#include +#include +#include +#include "moto_ts_dda.h" + +//cdev registration +#define DRIVER_NAME "moto_dda_penraw_driver" +#define DEVICE_NAME "moto_penraw" +#define MINOR_NUMBER_START ((const unsigned int)0) /* the minor number starts at */ +#define NUMBER_MINOR_NUMBER ((const unsigned int)1) /* the number of minor numbers */ +#define DDA_MAX_BUFFER 32 +#define MAX_IO_CONTROL_REPORT 16 +#define DDA_TOUCH_ID_MAX 10 +#define DDA_DEVICE_NAME_MAXLEN 32 + +static bool debug_log_flag = false; +#define DDA_INFO(fmt, args...) pr_info("[MOTO_DDA_INFO] %s %d: " fmt, __func__, __LINE__, ##args) +#define DDA_ERR(fmt, args...) pr_err("[MOTO_DDA_ERR] %s %d: " fmt, __func__, __LINE__, ##args) +#define DDA_DBG(fmt, args...) {if(debug_log_flag)pr_debug("[MOTO_DDA_DBG] %s %d: " fmt, __func__, __LINE__, ##args);} + + +struct dda_io_device_name { + char device_name[DDA_DEVICE_NAME_MAXLEN]; +}; + +#ifdef MOTO_DDA_PASSIVE_STYLUS +enum { + DDA_FINGER_RELEASE = 0, + DDA_FINGER_ENTER = 1, + DDA_FINGER_MOVE = 2 +}; + +struct dda_finger_info { + unsigned char frame_no; + unsigned char reserve[3]; + struct dda_finger_coords coords; +}; + +struct dda_io_finger_reports { + unsigned char report_num; + unsigned char reserve[3]; + struct dda_finger_info finger_info[MAX_IO_CONTROL_REPORT]; +}; + +struct dda_finger_report_buffer{ + unsigned char finger_report_num; + unsigned char frame_no; + unsigned char buffer_wp; + unsigned char reserve[1]; + unsigned char last_status; + struct dda_finger_info finger_report_buffer[DDA_MAX_BUFFER]; +}; +#endif //MOTO_DDA_PASSIVE_STYLUS + + +#ifdef MOTO_DDA_ACTIVE_STYLUS +/* for DDA goodix_penraw */ +enum{ + DATA_TYPE_RAW = 0 +}; + +struct dda_pen_info { + unsigned char frame_no; + unsigned char data_type; + unsigned char reserve[2]; + struct dda_pen_coords coords; +}; + +struct dda_io_pen_reports { + unsigned char report_num; + unsigned char reserve[3]; + struct dda_pen_info pen_info[MAX_IO_CONTROL_REPORT]; +}; + +struct dda_pen_report_buffer{ + unsigned char pen_report_num; + unsigned char frame_no; + unsigned char buffer_wp; + unsigned char reserve[1]; + struct dda_pen_info pen_report_buffer[DDA_MAX_BUFFER]; +}; +#endif //MOTO_DDA_ACTIVE_STYLUS + +struct dda_cdev_control{ + unsigned int major_number; /* the major number of the device */ + struct cdev penraw_char_dev; /* character device */ + struct class* penraw_char_dev_class; /* class object */ +}; + + +//DDA cdev +static struct dda_cdev_control dda_cdev_ctrl; + +#ifdef MOTO_DDA_PASSIVE_STYLUS +//DDA raw finger report buffer +static struct dda_finger_report_buffer dda_finger_report_buf[DDA_TOUCH_ID_MAX]; +static int dda_finger_id_assign_table[DDA_TOUCH_ID_MAX]; +static int dda_finger_id_last_assign_table[DDA_TOUCH_ID_MAX]; +#endif //MOTO_DDA_PASSIVE_STYLUS + +#ifdef MOTO_DDA_ACTIVE_STYLUS +//DDA raw pen report buffer +static struct dda_pen_report_buffer dda_pen_report_buf; +#endif //MOTO_DDA_ACTIVE_STYLUS + +static spinlock_t lock; +static int open_count = 0; + +static char dda_device_name[DDA_DEVICE_NAME_MAXLEN]; + + +//prototype of private functions +#ifdef MOTO_DDA_PASSIVE_STYLUS +static int get_finger_report(int id, unsigned long arg); +#endif //MOTO_DDA_PASSIVE_STYLUS +#ifdef MOTO_DDA_ACTIVE_STYLUS +static int get_pen_report(unsigned long arg); +#endif //MOTO_DDA_ACTIVE_STYLUS + +static int get_device_name(unsigned long arg); + +// Linux 2.0/2.2 +static int penraw_open(struct inode * inode, struct file * file) +{ + spin_lock(&lock); + if (open_count) { + spin_unlock(&lock); + return -EBUSY; + } + open_count++; + spin_unlock(&lock); + + return 0; +} + +// Linux 2.1: int type +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 1, 0) +static int penraw_close(struct inode * inode, struct file * file) +{ + spin_lock(&lock); + open_count--; + spin_unlock(&lock); + + return 0; +} +#else +static void penraw_close(struct inode * inode, struct file * file) +{ + spinlock(&lock); + open_count--; + spin_unlock(&lock); +} +#endif + +#define PENRAW_IOC_TYPE 'P' +#ifdef MOTO_DDA_ACTIVE_STYLUS +#define PENRAW_GET_VALUES _IOR(PENRAW_IOC_TYPE, 0, struct dda_io_pen_reports) + +static struct dda_io_pen_reports pen_reports; // return report +#endif //MOTO_DDA_ACTIVE_STYLUS + +#ifdef MOTO_DDA_PASSIVE_STYLUS +#define PENRAW_GET_VALUES_ID0 _IOR(PENRAW_IOC_TYPE, 1, struct dda_io_finger_reports) +#define PENRAW_GET_VALUES_ID1 _IOR(PENRAW_IOC_TYPE, 2, struct dda_io_finger_reports) +#define PENRAW_GET_VALUES_ID2 _IOR(PENRAW_IOC_TYPE, 3, struct dda_io_finger_reports) +#define PENRAW_GET_VALUES_ID3 _IOR(PENRAW_IOC_TYPE, 4, struct dda_io_finger_reports) +#define PENRAW_GET_VALUES_ID4 _IOR(PENRAW_IOC_TYPE, 5, struct dda_io_finger_reports) +#define PENRAW_GET_VALUES_ID5 _IOR(PENRAW_IOC_TYPE, 6, struct dda_io_finger_reports) +#define PENRAW_GET_VALUES_ID6 _IOR(PENRAW_IOC_TYPE, 7, struct dda_io_finger_reports) +#define PENRAW_GET_VALUES_ID7 _IOR(PENRAW_IOC_TYPE, 8, struct dda_io_finger_reports) +#define PENRAW_GET_VALUES_ID8 _IOR(PENRAW_IOC_TYPE, 9, struct dda_io_finger_reports) +#define PENRAW_GET_VALUES_ID9 _IOR(PENRAW_IOC_TYPE, 10, struct dda_io_finger_reports) + +static struct dda_io_finger_reports finger_reports; // return report +#endif //MOTO_DDA_PASSIVE_STYLUS + +#define PENRAW_GET_DEVICE_NAME _IOR(PENRAW_IOC_TYPE, 11, struct dda_io_device_name) + +static long penraw_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret; + + switch (cmd) { +#ifdef MOTO_DDA_ACTIVE_STYLUS + case PENRAW_GET_VALUES: + ret = get_pen_report(arg); + break; +#endif +#ifdef MOTO_DDA_PASSIVE_STYLUS + case PENRAW_GET_VALUES_ID0: + ret = get_finger_report(0,arg); + break; + case PENRAW_GET_VALUES_ID1: + ret = get_finger_report(1,arg); + break; + case PENRAW_GET_VALUES_ID2: + ret = get_finger_report(2,arg); + break; + case PENRAW_GET_VALUES_ID3: + ret = get_finger_report(3,arg); + break; + case PENRAW_GET_VALUES_ID4: + ret = get_finger_report(4,arg); + break; + case PENRAW_GET_VALUES_ID5: + ret = get_finger_report(5,arg); + break; + case PENRAW_GET_VALUES_ID6: + ret = get_finger_report(6,arg); + break; + case PENRAW_GET_VALUES_ID7: + ret = get_finger_report(7,arg); + break; + case PENRAW_GET_VALUES_ID8: + ret = get_finger_report(8,arg); + break; + case PENRAW_GET_VALUES_ID9: + ret = get_finger_report(9,arg); + break; +#endif //MOTO_DDA_PASSIVE_STYLUS + case PENRAW_GET_DEVICE_NAME: + ret = get_device_name(arg); + break; + default: + DDA_ERR("unsupported command %d", cmd); + return -EFAULT; + } + return ret; +} + +#ifdef MOTO_DDA_PASSIVE_STYLUS +static int get_finger_report(int id, unsigned long arg){ + unsigned long flags; + struct dda_finger_info *pfinger_info; + unsigned char cnt; + unsigned char finger_buffer_rp; + unsigned char wp; + unsigned char num; + int touch_id; + //enter to critical session + local_irq_save(flags); + touch_id = dda_finger_id_assign_table[id]; + if(-1 == touch_id){ + touch_id = dda_finger_id_last_assign_table[id]; + if(-1 == touch_id){ + DDA_ERR("PassiveStylusDDA touch_id is not assignd for %d",id); + touch_id = id; + } + } + + wp = dda_finger_report_buf[touch_id].buffer_wp; + num = dda_finger_report_buf[touch_id].finger_report_num; + if(MAX_IO_CONTROL_REPORT <= num) { + finger_buffer_rp = (unsigned char)((wp + (DDA_MAX_BUFFER - + MAX_IO_CONTROL_REPORT)) % DDA_MAX_BUFFER); + } else { + num = MAX_IO_CONTROL_REPORT; //for safety implementation + finger_buffer_rp = 0; + } + memset(&finger_reports, 0, sizeof(finger_reports)); + finger_reports.report_num = num; + pfinger_info = (struct dda_finger_info *)&finger_reports.finger_info[0]; + for(cnt = 0; cnt < num; cnt++) { + memcpy(pfinger_info, &dda_finger_report_buf[touch_id].finger_report_buffer[finger_buffer_rp], + sizeof(struct dda_finger_info)); + pfinger_info++; + finger_buffer_rp++; + if(DDA_MAX_BUFFER == finger_buffer_rp) { + finger_buffer_rp = 0; + } + } + //leave from critical session + local_irq_restore(flags); + + + if (copy_to_user((void __user *)arg, &finger_reports, sizeof(finger_reports))) { + return -EFAULT; + } + return 0; +} +#endif //MOTO_DDA_PASSIVE_STYLUS + +#ifdef MOTO_DDA_ACTIVE_STYLUS +static int get_pen_report(unsigned long arg){ + unsigned long flags; + struct dda_pen_info *ppen_info; + unsigned char cnt; + unsigned char pen_buffer_rp; + unsigned char wp; + unsigned char num; + + local_irq_save(flags); + wp = dda_pen_report_buf.buffer_wp; + num = dda_pen_report_buf.pen_report_num; + local_irq_restore(flags); + if(MAX_IO_CONTROL_REPORT <= num) { + pen_buffer_rp = (unsigned char)((wp + (DDA_MAX_BUFFER - + MAX_IO_CONTROL_REPORT)) % DDA_MAX_BUFFER); + } else { + pen_buffer_rp = 0; + } + memset(&pen_reports, 0, sizeof(pen_reports)); + pen_reports.report_num = num; + ppen_info = (struct dda_pen_info *)&pen_reports.pen_info[0]; + for(cnt = 0; cnt < num; cnt++) { + memcpy(ppen_info, &dda_pen_report_buf.pen_report_buffer[pen_buffer_rp], + sizeof(struct dda_pen_info)); + ppen_info++; + pen_buffer_rp++; + if(DDA_MAX_BUFFER == pen_buffer_rp) { + pen_buffer_rp = 0; + } + } + if (copy_to_user((void __user *)arg, &pen_reports, sizeof(pen_reports))) { + return -EFAULT; + } + return 0; +} +#endif //MOTO_DDA_ACTIVE_STYLUS + +static int get_device_name(unsigned long arg){ + if (copy_to_user((void __user *)arg, &dda_device_name, sizeof(dda_device_name))) { + return -EFAULT; + } + return 0; +} + +static struct file_operations penraw_fops = { + .owner = THIS_MODULE, + .open = penraw_open, + .release = penraw_close, + .unlocked_ioctl = penraw_ioctl, +}; + +/* functions for DDA Features */ +#ifdef MOTO_DDA_PASSIVE_STYLUS +void moto_dda_process_finger_press(uint8_t touch_id, struct dda_finger_coords *finger_data) { + struct dda_finger_info *pfinger_info; + int i; + unsigned char id_is_found; + + if(touch_id >= DDA_TOUCH_ID_MAX){ + return; + } + + // ioctl-DAA Buffering finger raw data + pfinger_info = &dda_finger_report_buf[touch_id].finger_report_buffer[0]; + pfinger_info += dda_finger_report_buf[touch_id].buffer_wp; + memset(pfinger_info, 0, sizeof(struct dda_finger_info)); + pfinger_info->coords.x = finger_data->x; + pfinger_info->coords.y = finger_data->y; + pfinger_info->coords.p = finger_data->p; + pfinger_info->coords.minor = finger_data->minor; + pfinger_info->coords.major = finger_data->major; + pfinger_info->frame_no = dda_finger_report_buf[touch_id].frame_no; + DDA_DBG("P:[%d] %d %d %d (%d)\n",touch_id, finger_data->x, finger_data->y, finger_data->p, dda_finger_report_buf[touch_id].frame_no); + dda_finger_report_buf[touch_id].frame_no++; + if(DDA_FINGER_RELEASE == dda_finger_report_buf[touch_id].last_status){ + pfinger_info->coords.status = DDA_FINGER_ENTER; + //check registerd iDs to avoid duplicated registration. + id_is_found = 0; + for(i=0;icoords.status = DDA_FINGER_MOVE; + } + dda_finger_report_buf[touch_id].last_status = pfinger_info->coords.status; + if(MAX_IO_CONTROL_REPORT > dda_finger_report_buf[touch_id].finger_report_num) { + // Max count: MAX_IO_CONTROL_REPORT + dda_finger_report_buf[touch_id].finger_report_num++; + } + dda_finger_report_buf[touch_id].buffer_wp++; + if(DDA_MAX_BUFFER == dda_finger_report_buf[touch_id].buffer_wp) { + dda_finger_report_buf[touch_id].buffer_wp = 0; + } +} + +void moto_dda_process_finger_release(uint8_t touch_id) { + struct dda_finger_info *pfinger_info; + int i; + + if(touch_id >= DDA_TOUCH_ID_MAX){ + return; + } + + // ioctl-DAA Buffering finger raw data + pfinger_info = &dda_finger_report_buf[touch_id].finger_report_buffer[0]; + pfinger_info += dda_finger_report_buf[touch_id].buffer_wp; + memset(pfinger_info, 0, sizeof(struct dda_finger_info)); + pfinger_info->coords.x = 0xFFFFFFFF; + pfinger_info->coords.y = 0xFFFFFFFF; + pfinger_info->coords.p = 0; + pfinger_info->coords.minor = 0; + pfinger_info->coords.major = 0; + pfinger_info->frame_no = dda_finger_report_buf[touch_id].frame_no; + DDA_DBG("R:[%d] (%d)\n",touch_id, dda_finger_report_buf[touch_id].frame_no); + dda_finger_report_buf[touch_id].frame_no++; + pfinger_info->coords.status = DDA_FINGER_RELEASE; + dda_finger_report_buf[touch_id].last_status = pfinger_info->coords.status; + for(i=0;i dda_finger_report_buf[touch_id].finger_report_num) { + // Max count: MAX_IO_CONTROL_REPORT + dda_finger_report_buf[touch_id].finger_report_num++; + } + dda_finger_report_buf[touch_id].buffer_wp++; + if(DDA_MAX_BUFFER == dda_finger_report_buf[touch_id].buffer_wp) { + dda_finger_report_buf[touch_id].buffer_wp = 0; + } + +} +#endif //MOTO_DDA_PASSIVE_STYLUS + +#ifdef MOTO_DDA_ACTIVE_STYLUS +void moto_dda_process_pen_report(struct dda_pen_coords *pen_data) { + struct dda_pen_info *ppen_info; + + if (pen_data->status) { + // ioctl-DAA Buffering pen raw data + ppen_info = &dda_pen_report_buf.pen_report_buffer[0]; + ppen_info += dda_pen_report_buf.buffer_wp; + memset(ppen_info, 0, sizeof(struct dda_pen_info)); + ppen_info->coords.status = (signed char)pen_data->status; + ppen_info->coords.tool_type = (signed char)pen_data->tool_type; + ppen_info->coords.tilt_x = pen_data->tilt_x; + ppen_info->coords.tilt_y = pen_data->tilt_y; + ppen_info->coords.x = pen_data->x; + ppen_info->coords.y = pen_data->y; + ppen_info->coords.p = pen_data->p; + ppen_info->frame_no = dda_pen_report_buf.frame_no; + ppen_info->data_type = DATA_TYPE_RAW; + DDA_DBG("S:[%d] %d %d %d (%d)\n", pen_data->status, (int)pen_data->x, (int)pen_data->y, (int)pen_data->p, dda_pen_report_buf.frame_no); + dda_pen_report_buf.frame_no++; + if(MAX_IO_CONTROL_REPORT > dda_pen_report_buf.pen_report_num) { + // Max count: MAX_IO_CONTROL_REPORT + dda_pen_report_buf.pen_report_num++; + } + dda_pen_report_buf.buffer_wp++; + if(DDA_MAX_BUFFER == dda_pen_report_buf.buffer_wp) { + dda_pen_report_buf.buffer_wp = 0; + } + } +} +#endif //MOTO_DDA_ACTIVE_STYLUS + + +void moto_dda_init(char *device_name) { +#ifdef MOTO_DDA_PASSIVE_STYLUS + int cnt; +#endif //MOTO_DDA_PASSIVE_STYLUS + + memset(dda_device_name,0x00,sizeof(dda_device_name)); + strncpy(dda_device_name,device_name,sizeof(dda_device_name)-1); + +#ifdef MOTO_DDA_PASSIVE_STYLUS + for(cnt =0;cnt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_SPI_SM8450 +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +#include +#else +#include +#endif +#endif + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) || defined(NVT_CONFIG_DRM_PANEL)) +#if defined(CONFIG_DRM) +#include +#include +#include +#include +#endif +#else //vension code < 5.4.0 +#if defined(CONFIG_FB) +#ifdef CONFIG_DRM_MSM +#include +#endif +#include +#include +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#endif +#endif //end version code >= 5.4.0 + +#ifdef NVT_CONFIG_PANEL_NOTIFICATIONS +#define register_panel_notifier panel_register_notifier +#define unregister_panel_notifier panel_unregister_notifier +enum touch_state { + TOUCH_DEEP_SLEEP_STATE = 0, + TOUCH_LOW_POWER_STATE, +}; +#else +#define register_panel_notifier(...) rc +#define unregister_panel_notifier(...) rc +#endif +#endif //end touchscreen_mmi + +#include "nt36xxx.h" +#if NVT_TOUCH_ESD_PROTECT +#include +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS +#include "moto_ts_dda.h" +#endif + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +#include +extern int nvt_mmi_init(struct nvt_ts_data *ts_data, bool enable); +#endif + +#if defined (NVT_SENSOR_EN) || defined (CONFIG_INPUT_TOUCHSCREEN_MMI) +#ifdef CONFIG_HAS_WAKELOCK +static struct wake_lock gesture_wakelock; +#else +static struct wakeup_source *gesture_wakelock; +#endif +#endif + +#if defined (NVT_SENSOR_EN) +static struct sensors_classdev __maybe_unused sensors_touch_cdev = { + + .name = "dt-gesture", + .vendor = "Novatek", + .version = 1, + .type = SENSOR_TYPE_MOTO_DOUBLE_TAP, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + /* WAKE_UP & SPECIAL_REPORT */ + .flags = 1 | 6, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#define REPORT_MAX_COUNT 10000 +#endif + +#if NVT_TOUCH_ESD_PROTECT +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; + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + +#define MAX_ATTRS_ENTRIES 10 +#define UI_FEATURE_ENABLE 1 +#define UI_FEATURE_DISABLE 0 +#define CMD_ON 0x71 +#define CMD_OFF 0x72 +#define ROTATE_0 0 +#define ROTATE_90 1 +#define ROTATE_180 2 +#define ROTATE_270 3 +#define DEFAULT_MODE 0 +#define BIG_MODE 1 +#define SMALL_MODE 2 +#define CLOSE_MODE 3 +#define DEFAULT_EDGE 0 +#define BIG_EDGE 1 +#define SMALL_EDGE 2 +#define CLOSE_EDGE 3 + +static ssize_t nvt_mmi_interpolation_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t nvt_mmi_interpolation_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t nvt_mmi_first_filter_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t nvt_mmi_first_filter_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t nvt_mmi_jitter_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t nvt_mmi_jitter_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t nvt_mmi_edge_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t nvt_mmi_edge_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static DEVICE_ATTR(interpolation, (S_IRUGO | S_IWUSR | S_IWGRP), + nvt_mmi_interpolation_show, nvt_mmi_interpolation_store); +static DEVICE_ATTR(first_filter, (S_IRUGO | S_IWUSR | S_IWGRP), + nvt_mmi_first_filter_show, nvt_mmi_first_filter_store); +static DEVICE_ATTR(jitter, (S_IRUGO | S_IWUSR | S_IWGRP), + nvt_mmi_jitter_show, nvt_mmi_jitter_store); +static DEVICE_ATTR(edge, (S_IRUGO | S_IWUSR | S_IWGRP), + nvt_mmi_edge_show, nvt_mmi_edge_store); + +#define ADD_ATTR(name) { \ + if (idx < MAX_ATTRS_ENTRIES) { \ + dev_info(dev, "%s: [%d] adding %p\n", __func__, idx, &dev_attr_##name.attr); \ + ext_attributes[idx] = &dev_attr_##name.attr; \ + idx++; \ + } else { \ + dev_err(dev, "%s: cannot add attribute '%s'\n", __func__, #name); \ + } \ +} + +static struct attribute *ext_attributes[MAX_ATTRS_ENTRIES]; +static struct attribute_group ext_attr_group = { + .attrs = ext_attributes, +}; +#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) || defined(NVT_CONFIG_DRM_PANEL)) +#if defined(CONFIG_DRM) +static struct drm_panel *active_panel; +static const char *active_panel_name = NULL; +#endif +#endif +#endif + +#if BOOT_UPDATE_FIRMWARE +static struct workqueue_struct *nvt_fwu_wq; +extern void Boot_Update_Firmware(struct work_struct *work); +#endif + +#ifdef NOVATECH_PEN_NOTIFIER +int nvt_mcu_pen_detect_set(uint8_t pen_detect); +#endif + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if (((LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0))) || defined(NVT_CONFIG_DRM_PANEL)) +#if defined(CONFIG_DRM) +#ifdef NVT_DRM_PANEL_EVENT_NOTIFICATIONS +static void nvt_drm_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *client_data); +#else +static int nvt_drm_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#endif +#ifdef LCM_FAST_LIGHTUP +static struct work_struct ts_resume_work; +static void nova_resume_work_func(struct work_struct *work); +#endif //end LCM_FAST_LIGHTUP +#endif +#else //vension code < 5.4.0 +#if defined(CONFIG_FB) +#ifdef _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 +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void nvt_ts_early_suspend(struct early_suspend *h); +static void nvt_ts_late_resume(struct early_suspend *h); +#endif + +#ifdef NVT_CONFIG_PANEL_NOTIFICATIONS +static int nvt_panel_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#endif +#endif //end version code >= 5.4.0 +#endif //end touchscreen_mmi + +uint32_t ENG_RST_ADDR = 0x7FFF80; +uint32_t SWRST_N8_ADDR = 0; //read from dtsi +uint32_t SPI_RD_FAST_ADDR = 0; //read from dtsi +static int charger_notifier_callback(struct notifier_block *nb, unsigned long val, void *v); +static void nvt_charger_notify_work(struct work_struct *work); +static int usb_detect_flag = 0; + +char *nvt_boot_firmware_name = NULL; +char *nvt_mp_firmware_name = NULL; + +#if TOUCH_KEY_NUM > 0 +const uint16_t touch_key_array[TOUCH_KEY_NUM] = { + KEY_BACK, + KEY_HOME, + KEY_MENU +}; +#endif + +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL +#if WAKEUP_GESTURE +const uint16_t gesture_key_array[] = { + KEY_POWER, //GESTURE_WORD_C + KEY_POWER, //GESTURE_WORD_W + KEY_POWER, //GESTURE_WORD_V + BTN_TRIGGER_HAPPY6, //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 + BTN_TRIGGER_HAPPY3, //GESTURE_SINGLE_CLICK +}; +#endif +#else +#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, , GESTURE_SINGLE_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 +#endif + +#ifdef CONFIG_MTK_SPI +const struct mt_chip_conf spi_ctrdata = { + .setuptime = 25, + .holdtime = 25, + .high_time = 5, /* 10MHz (SPI_SPEED=100M / (high_time+low_time(10ns)))*/ + .low_time = 5, + .cs_idletime = 2, + .ulthgh_thrsh = 0, + .cpol = 0, + .cpha = 0, + .rx_mlsb = 1, + .tx_mlsb = 1, + .tx_endian = 0, + .rx_endian = 0, + .com_mod = DMA_TRANSFER, + .pause = 0, + .finish_intr = 1, + .deassert = 0, + .ulthigh = 0, + .tckdly = 0, +}; +#endif + +#ifdef CONFIG_SPI_MT65XX +const struct mtk_chip_config spi_ctrdata = { + .rx_mlsb = 1, + .tx_mlsb = 1, + .cs_pol = 0, +}; +#endif + +#ifdef NVT_TOUCH_LAST_TIME +static bool time_flag = 1; +#endif + +/******************************************************* +Description: + Novatek touchscreen irq enable/disable function. + +return: + n.a. +*******************************************************/ +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 spi read/write core function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static inline int32_t spi_read_write(struct spi_device *client, uint8_t *buf, size_t len , NVT_SPI_RW rw) +{ + struct spi_message m; + struct spi_transfer t = { + .len = len, + }; + + memset(ts->xbuf, 0, len + DUMMY_BYTES); + memcpy(ts->xbuf, buf, len); + + switch (rw) { + case NVTREAD: + t.tx_buf = ts->xbuf; + t.rx_buf = ts->rbuf; + t.len = (len + DUMMY_BYTES); + break; + + case NVTWRITE: + 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 CTP_SPI_READ(struct spi_device *client, uint8_t *buf, uint16_t len) +{ + int32_t ret = -1; + int32_t retries = 0; + + mutex_lock(&ts->xbuf_lock); + + buf[0] = SPI_READ_MASK(buf[0]); + + while (retries < 5) { + ret = spi_read_write(client, buf, len, NVTREAD); + 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 CTP_SPI_WRITE(struct spi_device *client, uint8_t *buf, uint16_t len) +{ + int32_t ret = -1; + int32_t retries = 0; + + mutex_lock(&ts->xbuf_lock); + + buf[0] = SPI_WRITE_MASK(buf[0]); + + while (retries < 5) { + ret = spi_read_write(client, buf, len, NVTWRITE); + 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_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 CTP_SPI_WRITE(ts->client, buf, 3); +} + +/******************************************************* +Description: + Novatek touchscreen write data to specify address. + +return: + Executive outcomes. 0---succeed. -5---access fail. +*******************************************************/ +int32_t nvt_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 = CTP_SPI_WRITE(ts->client, 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 = CTP_SPI_WRITE(ts->client, 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_bld_crc_enable(void) +{ + uint8_t buf[4] = {0}; + + //---set xdata index to BLD_CRC_EN_ADDR--- + nvt_set_page(ts->mmap->BLD_CRC_EN_ADDR); + + //---read data from index--- + buf[0] = ts->mmap->BLD_CRC_EN_ADDR & (0x7F); + buf[1] = 0xFF; + CTP_SPI_READ(ts->client, buf, 2); + + //---write data to index--- + buf[0] = ts->mmap->BLD_CRC_EN_ADDR & (0x7F); + buf[1] = buf[1] | (0x01 << 7); + CTP_SPI_WRITE(ts->client, buf, 2); +} + +/******************************************************* +Description: + Novatek touchscreen clear status & enable fw crc function. + +return: + N/A. +*******************************************************/ +void nvt_fw_crc_enable(void) +{ + uint8_t buf[4] = {0}; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(ts->mmap->EVENT_BUF_ADDR); + + //---clear fw reset status--- + buf[0] = EVENT_MAP_RESET_COMPLETE & (0x7F); + buf[1] = 0x00; + CTP_SPI_WRITE(ts->client, buf, 2); + + //---enable fw crc--- + buf[0] = EVENT_MAP_HOST_CMD & (0x7F); + buf[1] = 0xAE; //enable fw crc command + CTP_SPI_WRITE(ts->client, buf, 2); +} + +/******************************************************* +Description: + Novatek touchscreen set boot ready function. + +return: + N/A. +*******************************************************/ +void nvt_boot_ready(void) +{ + //---write BOOT_RDY status cmds--- + nvt_write_addr(ts->mmap->BOOT_RDY_ADDR, 1); + + mdelay(5); + + if (!ts->hw_crc) { + //---write BOOT_RDY status cmds--- + nvt_write_addr(ts->mmap->BOOT_RDY_ADDR, 0); + + //---write POR_CD cmds--- + nvt_write_addr(ts->mmap->POR_CD_ADDR, 0xA0); + } +} + +/******************************************************* +Description: + Novatek touchscreen eng reset cmd + function. + +return: + n.a. +*******************************************************/ +void nvt_eng_reset(void) +{ + //---eng reset cmds to ENG_RST_ADDR--- + nvt_write_addr(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_sw_reset(void) +{ + //---software reset cmds to SWRST_N8_ADDR--- + nvt_write_addr(SWRST_N8_ADDR, 0x55); + + msleep(10); +} + +/******************************************************* +Description: + Novatek touchscreen reset MCU then into idle mode + function. + +return: + n.a. +*******************************************************/ +void nvt_sw_reset_idle(void) +{ + //---MCU idle cmds to SWRST_N8_ADDR--- + nvt_write_addr(SWRST_N8_ADDR, 0xAA); + + msleep(15); +} + +/******************************************************* +Description: + Novatek touchscreen reset MCU (boot) function. + +return: + n.a. +*******************************************************/ +void nvt_bootloader_reset(void) +{ + //---reset cmds to SWRST_N8_ADDR--- + nvt_write_addr(SWRST_N8_ADDR, 0x69); + + mdelay(5); //wait tBRST2FR after Bootload RST + + if (SPI_RD_FAST_ADDR) { + /* disable SPI_RD_FAST */ + nvt_write_addr(SPI_RD_FAST_ADDR, 0x00); + } +} + +/******************************************************* +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(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_SPI_WRITE(ts->client, buf, 2); + + //---read fw status--- + buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = 0xFF; + CTP_SPI_READ(ts->client, 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(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_SPI_READ(ts->client, 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; + int32_t retry_max = (check_reset_state == RESET_STATE_INIT) ? 10 : 50; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_RESET_COMPLETE); + + while (1) { + //---read reset state--- + buf[0] = EVENT_MAP_RESET_COMPLETE; + buf[1] = 0x00; + CTP_SPI_READ(ts->client, buf, 6); + + if ((buf[1] >= check_reset_state) && (buf[1] <= 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, 10000); + } + + 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[4] = {0}; + int32_t ret = 0; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_PROJECTID); + + //---read project id--- + buf[0] = EVENT_MAP_PROJECTID; + buf[1] = 0x00; + buf[2] = 0x00; + CTP_SPI_READ(ts->client, buf, 3); + + ts->nvt_pid = (buf[2] << 8) + buf[1]; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(ts->mmap->EVENT_BUF_ADDR); + + NVT_LOG("PID=%04X\n", ts->nvt_pid); + + return ret; +} + +/******************************************************* +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(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_FWINFO); + + //---read fw info--- + buf[0] = EVENT_MAP_FWINFO; + CTP_SPI_READ(ts->client, buf, 17); + ts->fw_ver = buf[1]; + ts->x_num = buf[3]; + ts->y_num = buf[4]; +#ifndef CONFIG_INPUT_HIGH_RESOLUTION_N + ts->abs_x_max = (uint16_t)((buf[5] << 8) | buf[6]); + ts->abs_y_max = (uint16_t)((buf[7] << 8) | buf[8]); +#endif + ts->max_button_num = buf[11]; + ts->fw_type = buf[14]; + //---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; + ts->fw_type = 1; + + 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; + } + + NVT_LOG("fw_ver=%d, x_num=%d, y_num=%d, abs_xmax=%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); + NVT_LOG("FW type is 0x%02X\n", buf[14]); + + //---Get Novatek PID--- + nvt_read_pid(); + + return ret; +} + +int32_t nvt_cmd_ext_store(uint8_t cmd, uint8_t subcmd) +{ + int32_t i, retry = 5; + uint8_t buf[4] = {0}; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(ts->mmap->EVENT_BUF_ADDR); + + for (i = 0; i < retry; i++) { + if (buf[1] != cmd) { + //---set cmd status--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = cmd; + buf[2] = subcmd; + CTP_SPI_WRITE(ts->client, buf, 3); + } + + msleep(20); + + //---read cmd status--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0xFF; + CTP_SPI_READ(ts->client, buf, 2); + if (buf[1] == 0x00) + break; + } + + if (i == retry) { + NVT_ERR("send Cmd 0x%02X 0x%02X failed, buf[1]=0x%02X\n", cmd, subcmd, buf[1]); + return -1; + } else { + NVT_LOG("send Cmd 0x%02X 0x%02X success, tried %d times\n", cmd, subcmd, i); + } + + return 0; +} + +/******************************************************* + Create Device Node (Proc Entry) +*******************************************************/ +#if NVT_TOUCH_PROC +static struct proc_dir_entry *NVT_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_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_TRANSFER_LEN + 3) || (count < 3)) { + NVT_ERR("invalid transfer len!\n"); + return -EFAULT; + } + + /* allocate buffer for spi transfer */ + str = (uint8_t *)kzalloc((count), GFP_KERNEL); + if(str == NULL) { + NVT_ERR("kzalloc for buf failed!\n"); + ret = -ENOMEM; + goto kzalloc_failed; + } + + buf = (uint8_t *)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_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 */ + + spi_wr = str[0] >> 7; + memcpy(buf, str+2, ((str[0] & 0x7F) << 8) | str[1]); + + if (spi_wr == NVTWRITE) { //SPI write + while (retries < 20) { + ret = CTP_SPI_WRITE(ts->client, buf, ((str[0] & 0x7F) << 8) | str[1]); + if (!ret) + break; + else + 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 == NVTREAD) { //SPI read + while (retries < 20) { + ret = CTP_SPI_READ(ts->client, buf, ((str[0] & 0x7F) << 8) | str[1]); + if (!ret) + break; + else + 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_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/NVTSPI 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; + + if (dev) + kfree(dev); + + return 0; +} + +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +static const struct proc_ops nvt_flash_fops = { + .proc_open = nvt_flash_open, + .proc_read = nvt_flash_read, + .proc_lseek = seq_lseek, + .proc_release = nvt_flash_close, +}; +#else +static const struct file_operations nvt_flash_fops = { + .owner = THIS_MODULE, + .open = nvt_flash_open, + .release = nvt_flash_close, + .read = nvt_flash_read, +}; +#endif + +/******************************************************* +Description: + Novatek touchscreen /proc/NVTSPI 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/NVTSPI 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_SINGLE_CLICK 25 +#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]; +#ifdef NVT_SENSOR_EN + static int report_cnt = 0; +#endif + + /* 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_DBG("Gesture : Word-C.\n"); + keycode = gesture_key_array[0]; + break; + case GESTURE_WORD_W: + NVT_DBG("Gesture : Word-W.\n"); + keycode = gesture_key_array[1]; + break; + case GESTURE_WORD_V: + NVT_DBG("Gesture : Word-V.\n"); + keycode = gesture_key_array[2]; + break; +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + case GESTURE_DOUBLE_CLICK: + NVT_DBG("Gesture : Double Click.\n"); + keycode = gesture_key_array[3]; + break; +#else + case GESTURE_SINGLE_CLICK: + case GESTURE_DOUBLE_CLICK: + NVT_DBG("Gesture : Double Click.\n"); + keycode = gesture_key_array[3]; + break; +#endif + case GESTURE_WORD_Z: + NVT_DBG("Gesture : Word-Z.\n"); + keycode = gesture_key_array[4]; + break; + case GESTURE_WORD_M: + NVT_DBG("Gesture : Word-M.\n"); + keycode = gesture_key_array[5]; + break; + case GESTURE_WORD_O: + NVT_DBG("Gesture : Word-O.\n"); + keycode = gesture_key_array[6]; + break; + case GESTURE_WORD_e: + NVT_DBG("Gesture : Word-e.\n"); + keycode = gesture_key_array[7]; + break; + case GESTURE_WORD_S: + NVT_DBG("Gesture : Word-S.\n"); + keycode = gesture_key_array[8]; + break; + case GESTURE_SLIDE_UP: + NVT_DBG("Gesture : Slide UP.\n"); + keycode = gesture_key_array[9]; + break; + case GESTURE_SLIDE_DOWN: + NVT_DBG("Gesture : Slide DOWN.\n"); + keycode = gesture_key_array[10]; + break; + case GESTURE_SLIDE_LEFT: + NVT_DBG("Gesture : Slide LEFT.\n"); + keycode = gesture_key_array[11]; + break; + case GESTURE_SLIDE_RIGHT: + NVT_DBG("Gesture : Slide RIGHT.\n"); + keycode = gesture_key_array[12]; + break; +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + case GESTURE_SINGLE_CLICK: + NVT_LOG("Gesture : Single Click.\n"); + keycode = gesture_key_array[13]; + break; +#endif + default: + break; + } + + if (keycode > 0) { +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + if (ts->imports && ts->imports->report_gesture) { + struct gesture_event_data event; + int ret = 0; + + dev_dbg(&ts->client->dev, + "%s: invoke imported report gesture function\n", __func__); + /* extract X and Y coordinates */ +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + if(gesture_id == GESTURE_SINGLE_CLICK) { + event.evcode = 1; + } else if(gesture_id == GESTURE_DOUBLE_CLICK) { + event.evcode =4; + } +#else + event.evcode = 1; +#endif + /* call class method */ + ret = ts->imports->report_gesture(&event); + if (!ret) + PM_WAKEUP_EVENT(gesture_wakelock, 5000); + } +#elif NVT_SENSOR_EN + if (!(ts->wakeable && ts->should_enable_gesture)) { + NVT_LOG("Gesture got but wakeable not set. Skip this gesture."); + return; + } + if (ts->report_gesture_key) { +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + input_report_key(ts->sensor_pdata->input_sensor_dev, keycode, 1); + input_sync(ts->sensor_pdata->input_sensor_dev); + input_report_key(ts->sensor_pdata->input_sensor_dev, keycode, 0); + input_sync(ts->sensor_pdata->input_sensor_dev); +#else + input_report_key(ts->sensor_pdata->input_sensor_dev, KEY_F1, 1); + input_sync(ts->sensor_pdata->input_sensor_dev); + input_report_key(ts->sensor_pdata->input_sensor_dev, KEY_F1, 0); + input_sync(ts->sensor_pdata->input_sensor_dev); +#endif + ++report_cnt; + } else { + input_report_abs(ts->sensor_pdata->input_sensor_dev, + ABS_DISTANCE, + ++report_cnt); + input_sync(ts->sensor_pdata->input_sensor_dev); + } + NVT_LOG("input report: %d", report_cnt); + if (report_cnt >= REPORT_MAX_COUNT) { + report_cnt = 0; + } +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&gesture_wakelock, msecs_to_jiffies(5000)); +#else + PM_WAKEUP_EVENT(gesture_wakelock, 5000); +#endif +#else + 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 + } +} +#endif + +/******************************************************* +Description: + Novatek touchscreen parse device tree function. + +return: + n.a. +*******************************************************/ +#ifdef CONFIG_OF +static int nvt_get_dt_def_coords(struct device *dev, char *name) +{ + u32 coords[TOUCH_COORDS_ARR_SIZE]; + struct property *prop; + struct device_node *np = dev->of_node; + int coords_size, rc; + + 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 != TOUCH_COORDS_ARR_SIZE) { + dev_err(dev, "invalid %s\n", name); + return -EINVAL; + } + + rc = of_property_read_u32_array(np, name, coords, coords_size); + if (rc && (rc != -EINVAL)) { + NVT_LOG("Unable to read novatek,def-max-resolution\n"); + return rc; + } + + ts->abs_x_max = coords[0]; + ts->abs_y_max = coords[1]; + return rc; +} + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) || defined(NVT_CONFIG_DRM_PANEL)) +#if defined(CONFIG_DRM) +static int nova_check_dt(struct device_node *np) +{ + int i; + int count; + int ret = -ENODEV; + bool dts_using_dummy = false; + + struct device_node *node; + struct drm_panel *panel; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) + return 0; + + NVT_LOG("nova_check_dt, count=%d\n", count); + for (i = 0; i < count; i++) { + node = of_parse_phandle(np, "panel", i); + panel = of_drm_find_panel(node); + NVT_LOG("node->name %s !\n", node->name); + if(strstr(node->name, "dummy")) { + dts_using_dummy = true; + } + of_node_put(node); + if (!IS_ERR(panel)) { + active_panel = panel; + active_panel_name = node->name; + NVT_LOG("nova_check_dt, active_panel: %s !\n", active_panel_name); + ret = 0; + } + } + + if(dts_using_dummy && ret) + ret = -EPROBE_DEFER; + if(active_panel_name != NULL) { + if(strstr(active_panel_name, "dummy")) { + NVT_LOG("Using dummy panel! Return!\n"); + ret = -ENODEV; + } + } + return ret; +} +#endif +#endif +#endif + +static int32_t nvt_parse_dt(struct device *dev) +{ + struct device_node *np = dev->of_node; + int32_t ret = 0; +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) || defined(NVT_CONFIG_DRM_PANEL)) + int num_of_panel_supplier; + int j; + const char *panel_supplier; +#endif + +#endif + + +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + uint32_t value; + if (!of_property_read_u32(np, "novatek,supported_gesture_type", &value)){ + ts->supported_gesture_type = (uint8_t)value; + } +#endif + +#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); + + ret = of_property_read_u32(np, "novatek,swrst-n8-addr", &SWRST_N8_ADDR); + if (ret) { + NVT_ERR("error reading novatek,swrst-n8-addr. ret=%d\n", ret); + return ret; + } else { + NVT_LOG("SWRST_N8_ADDR=0x%06X\n", SWRST_N8_ADDR); + } + +#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) || defined(NVT_CONFIG_DRM_PANEL)) &&\ + (!defined CONFIG_INPUT_TOUCHSCREEN_MMI) + num_of_panel_supplier = of_property_count_strings(np, "novatek,panel-supplier"); + NVT_LOG("%s: get novatek,panel-supplier count=%d", __func__, num_of_panel_supplier); + if (active_panel_name && num_of_panel_supplier > 1) { + for (j = 0; j < num_of_panel_supplier; j++) { + ret = of_property_read_string_index(np, "novatek,panel-supplier", j, &panel_supplier); + if (ret < 0) { + NVT_LOG("%s: cannot parse panel-supplier: %d\n", __func__, ret); + break; + } else if (panel_supplier && strstr(active_panel_name, panel_supplier)) { + ts->panel_supplier = panel_supplier; + NVT_LOG("%s: matched panel_supplier: %s", __func__, panel_supplier); + break; + } + } + } else { + //in case the panel-supplier info does not completely contained in panel name. + ret = of_property_read_string(np, "novatek,panel-supplier", + &ts->panel_supplier); + } +#else + ret = of_property_read_string(np, "novatek,panel-supplier", + &ts->panel_supplier); +#endif + if (ret < 0) { + NVT_LOG("Unable to read panel supplier\n"); + } else { + NVT_LOG("panel supplier is %s", (char *)ts->panel_supplier); + nvt_boot_firmware_name = kzalloc(NVT_FILE_NAME_LENGTH, GFP_KERNEL); + if (!nvt_boot_firmware_name) { + NVT_LOG("%s: alloc nvt_boot_firmware_name failed\n", __func__); + goto nvt_boot_firmware_name_alloc_failed; + } + nvt_mp_firmware_name = kzalloc(NVT_FILE_NAME_LENGTH, GFP_KERNEL); + if (!nvt_boot_firmware_name) { + NVT_LOG("%s: alloc nvt_boot_firmware_name failed\n", __func__); + goto nvt_mp_firmware_name_alloc_failed; + } + snprintf(nvt_boot_firmware_name, NVT_FILE_NAME_LENGTH, "%s_novatek_ts_fw.bin", + ts->panel_supplier); + snprintf(nvt_mp_firmware_name, NVT_FILE_NAME_LENGTH, "%s_novatek_ts_mp.bin", + ts->panel_supplier); + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + //Support FW name with panel & IC info +#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) || defined(NVT_CONFIG_DRM_PANEL)) + if (active_panel_name) { + const char *ic_name; + int num_of_ic = of_property_count_strings(np, "novatek,fw_ic_info"); + if (num_of_ic > 0) { + int i; + NVT_LOG("%s: get novatek,fw_ic_info count=%d", __func__, num_of_ic); + for (i = 0; i < num_of_ic; i++) { + ret = of_property_read_string_index(np, "novatek,fw_ic_info", i, &ic_name); + if (ret < 0) { + NVT_LOG("%s: cannot parse fw_ic_info: %d\n", __func__, ret); + break; + } else if (ic_name && strstr(active_panel_name, ic_name)) { + NVT_LOG("%s: matched FW IC: %s", __func__, ic_name); + snprintf(nvt_boot_firmware_name, NVT_FILE_NAME_LENGTH, "%s_%s_novatek_ts_fw.bin", + ts->panel_supplier, ic_name); + snprintf(nvt_mp_firmware_name, NVT_FILE_NAME_LENGTH, "%s_%s_novatek_ts_mp.bin", + ts->panel_supplier, ic_name); + break; + } + } + } + } +#endif +#endif + } + NVT_LOG("boot firmware %s, mp firmware %s", nvt_boot_firmware_name, nvt_mp_firmware_name); + + ret = nvt_get_dt_def_coords(dev, "novatek,def-max-resolution"); + if (ret) { + ts->abs_x_max = TOUCH_DEFAULT_MAX_WIDTH; + ts->abs_y_max = TOUCH_DEFAULT_MAX_HEIGHT; + } + NVT_LOG("novatek,def-max-resolution=%d,%d\n", ts->abs_x_max, ts->abs_y_max); + + ret = of_property_read_u32(np, "novatek,def-build-id", &ts->build_id); + if (ret) { + ts->build_id = 0; + NVT_LOG("novatek,build_id undefined.\n"); + } else { + NVT_LOG("novatek,build_id=0x%04X\n", ts->build_id); + } + + ret = of_property_read_u32(np, "novatek,def-config-id", &ts->config_id); + if (ret) { + ts->config_id = 0; + NVT_LOG("novatek,config_id undefined.\n"); + } else { + NVT_LOG("novatek,config_id=0x%04X\n", ts->config_id); + } + + ret = of_property_read_u32(np, "novatek,spi-rd-fast-addr", &SPI_RD_FAST_ADDR); + if (ret) { + NVT_LOG("not support novatek,spi-rd-fast-addr\n"); + SPI_RD_FAST_ADDR = 0; + ret = 0; + } else { + NVT_LOG("SPI_RD_FAST_ADDR=0x%06X\n", SPI_RD_FAST_ADDR); + } + if (of_property_read_bool(np, "novatek,usb_charger")) { + NVT_LOG("novatek,usb_charger set"); + ts->charger_detection_enable = 1; + } else { + ts->charger_detection_enable = 0; + } + + if (of_property_read_bool(np, "novatek,gesture-wait-pm")) { + NVT_LOG("novatek,gesture-wait-pm set"); + ts->gesture_wait_pm = true; + } else { + ts->gesture_wait_pm = false; + } + + if (of_property_read_bool(np, "novatek,report_gesture_key")) { + NVT_LOG("novatek,report_gesture_key set"); + ts->report_gesture_key = 1; + } else { + ts->report_gesture_key = 0; + } + + if (of_property_read_u8_array(np, "novatek,interpolation_cmd", + ts->interpolation_cmd, 2) == 0) { + ts->interpolation_ctrl = true; + NVT_LOG("Support report rate interpolation.\n"); + } + + if (of_property_read_u8_array(np, "novatek,jitter_cmd", + ts->jitter_cmd, 2) == 0) { + ts->jitter_ctrl = true; + NVT_LOG("Support set jitter.\n"); + } + + if (of_property_read_u8_array(np, "novatek,first_filter_cmd", + ts->first_filter_cmd, 2) == 0) { + ts->first_filter_ctrl = true; + NVT_LOG("Support set first_filter.\n"); + } + + if (of_property_read_u8_array(np, "novatek,edge_cmd", + ts->edge_cmd, 3) == 0) { + ts->rotate_cmd = EDGE_REJECT_VERTICLE_CMD; + ts->edge_ctrl = true; + NVT_LOG("Support edge switching.\n"); + } + + return ret; + +nvt_mp_firmware_name_alloc_failed: + kfree(nvt_boot_firmware_name); + nvt_boot_firmware_name = NULL; +nvt_boot_firmware_name_alloc_failed: + return ret; +} +#else +static int32_t 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; + return 0; +} +#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_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_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_LOG("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, reload fw */ + if(nvt_boot_firmware_name) + nvt_update_firmware(nvt_boot_firmware_name); + else + nvt_update_firmware(BOOT_UPDATE_FIRMWARE_NAME); + 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 */ + +#if NVT_TOUCH_WDT_RECOVERY +static uint8_t recovery_cnt = 0; +static uint8_t nvt_wdt_fw_recovery(uint8_t *point_data) +{ + uint32_t recovery_cnt_max = 10; + uint8_t recovery_enable = false; + uint8_t i = 0; + + recovery_cnt++; + + /* check pattern */ + for (i=1 ; i<7 ; i++) { + if ((point_data[i] != 0xFD) && (point_data[i] != 0xFE)) { + recovery_cnt = 0; + break; + } + } + + if (recovery_cnt > recovery_cnt_max){ + recovery_enable = true; + recovery_cnt = 0; + } + + return recovery_enable; +} +#endif /* #if NVT_TOUCH_WDT_RECOVERY */ + +#if POINT_DATA_CHECKSUM +static int32_t nvt_ts_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("i2c/spi 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 -1; + } + + return 0; +} +#endif /* POINT_DATA_CHECKSUM */ + +#define FINGER_ENTER 0x01 +#define FINGER_MOVING 0x02 +#ifdef PALM_GESTURE +#define PALM_TOUCH 0x05 +#define POINT_DATA_LEN 120 +#define MINOR_DATA_OFFSET 70 +#define ORIENT_DATA_OFFSET 110 +#elif defined CONFIG_INPUT_HIGH_RESOLUTION_N +#define POINT_DATA_LEN 108 +#else +#define POINT_DATA_LEN 65 +#endif + +/******************************************************* +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 + DUMMY_BYTES] = {0}; + uint32_t position = 0; + uint32_t input_x = 0; + uint32_t input_y = 0; + uint32_t input_major = 0; +#ifdef PALM_GESTURE + uint32_t input_minor = 0; + int32_t input_orient = 0; +#endif + 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 (ts->bTouchIsAwake == 0) { + pm_wakeup_event(&ts->input_dev->dev, 5000); + + if ((ts->gesture_wait_pm) && (ts->gesture_enabled)) { + /* Waiting for pm resume completed */ + ret = wait_event_interruptible_timeout(ts->pm_wq, atomic_read(&ts->pm_resume), msecs_to_jiffies(700)); + if (!ret) { + NVT_ERR("system(spi) can't finished resuming procedure."); + return IRQ_HANDLED; + } + } + } +#endif + + mutex_lock(&ts->lock); + + ret = CTP_SPI_READ(ts->client, point_data, POINT_DATA_LEN + 1); + if (ret < 0) { + NVT_ERR("CTP_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_TOUCH_WDT_RECOVERY + /* ESD protect by WDT */ + if (nvt_wdt_fw_recovery(point_data)) { + NVT_ERR("Recover for fw reset, %02X\n", point_data[1]); + if(nvt_boot_firmware_name) + nvt_update_firmware(nvt_boot_firmware_name); + else + nvt_update_firmware(BOOT_UPDATE_FIRMWARE_NAME); + + mutex_unlock(&ts->lock); + + if (ts->charger_detection) { + queue_work(ts->charger_detection->nvt_charger_notify_wq, &ts->charger_detection->charger_notify_work); + } + +#ifdef NOVATECH_PEN_NOTIFIER + if(!ts->fw_ready_flag) + ts->fw_ready_flag = true; + nvt_mcu_pen_detect_set(ts->nvt_pen_detect_flag); +#endif +#ifdef PALM_GESTURE + nvt_palm_set(ts->palm_enabled); +#endif +#ifdef EDGE_SUPPRESSION + nvt_edge_reject_set(ts->edge_reject_state); +#endif + return IRQ_HANDLED; + } +#endif /* #if NVT_TOUCH_WDT_RECOVERY */ + + /* ESD protect by FW handshake */ + 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 POINT_DATA_CHECKSUM + if (POINT_DATA_LEN >= POINT_DATA_CHECKSUM_LEN) { + ret = nvt_ts_point_data_checksum(point_data, POINT_DATA_CHECKSUM_LEN); + if (ret) { + goto XFER_ERROR; + } + } +#endif /* POINT_DATA_CHECKSUM */ + +#if WAKEUP_GESTURE + if (ts->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; +#ifdef PALM_GESTURE + if (((point_data[position] & 0x07) == FINGER_ENTER) + || ((point_data[position] & 0x07) == FINGER_MOVING) + || ((point_data[position] & 0x07) == PALM_TOUCH)) { //finger down (enter & moving) or palm +#else + if (((point_data[position] & 0x07) == FINGER_ENTER) || ((point_data[position] & 0x07) == FINGER_MOVING)) { //finger down (enter & moving) +#endif +#if NVT_TOUCH_ESD_PROTECT + /* update interrupt timer */ + irq_timer = jiffies; +#endif /* #if NVT_TOUCH_ESD_PROTECT */ +#ifdef CONFIG_INPUT_HIGH_RESOLUTION_N + input_x = (uint32_t)(point_data[position + 1] << 8) + (uint32_t) (point_data[position + 2]); + input_y = (uint32_t)(point_data[position + 3] << 8) + (uint32_t) (point_data[position + 4]); + if ((input_x < 0) || (input_y < 0)) + continue; + if ((input_x > ts->abs_x_max) || (input_y > ts->abs_y_max)) + continue; + input_major = (uint32_t)(point_data[position + 5]); + if (input_major == 0) + input_major = 1; +#else + 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_major = (uint32_t)(point_data[position + 4]); + if (input_major == 0) + input_major = 1; +#endif /* CONFIG_INPUT_HIGH_RESOLUTION_N */ +#ifdef PALM_GESTURE_RANGE + if((point_data[position] & 0x07) == PALM_TOUCH) { //palm + input_minor = (uint32_t)(point_data[1 + MINOR_DATA_OFFSET + i]); + if (input_minor == 0) + input_minor = 1; + + input_orient = (int8_t)(point_data[1 + ORIENT_DATA_OFFSET + i]); + } +#endif + + +#ifdef PALM_GESTURE + if ((point_data[position] & 0x07) == PALM_TOUCH) { //palm +#ifdef CONFIG_INPUT_HIGH_RESOLUTION_N + input_p = (uint32_t)(point_data[1 + 98 + i]); +#else + input_p = (uint32_t)(point_data[position + 5]); +#endif + if(input_p == 255) + input_p = PALM_HANG; + else if (input_p == 254) + input_p = PALM_HAND_HOLDE; + +#ifdef PALM_GESTURE_RANGE + //dimension-to-pixel + input_minor = input_minor * TOUCH_DEFAULT_MAX_WIDTH / PANEL_REAL_WIDTH * 100; + input_major = input_major * TOUCH_DEFAULT_MAX_HEIGHT / PANEL_REAL_HEIGHT * 100; +#endif + } else +#endif + { +#ifdef CONFIG_INPUT_HIGH_RESOLUTION_N + input_p = (uint32_t)(point_data[position + 98 + i]); +#else + 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]); + } +#endif + } + 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 */ + +#ifdef PALM_GESTURE + if((point_data[position] & 0x07) == PALM_TOUCH) { //palm + dev_dbg(&ts->client->dev, "id=%d, pressure=%d, (%d,%d), major=%d, minor=%d, orient=%d\n", + input_id, input_p, input_x, input_y, input_major, input_minor, input_orient); + 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_major); +#ifdef PALM_GESTURE_RANGE + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MINOR, input_minor); + input_report_abs(ts->input_dev, ABS_MT_ORIENTATION, input_orient); +#endif + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, input_p); +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS + { + struct dda_finger_coords finger_data; + finger_data.x = input_x; + finger_data.y = input_y; + finger_data.p = input_p; + finger_data.minor = input_major; + finger_data.major = input_major; + moto_dda_process_finger_press(input_id - 1, &finger_data); + } +#endif + } else +#endif + { + dev_dbg(&ts->client->dev, "id=%d, pressure=%d, (%d,%d), major=%d\n", + input_id, input_p, input_x, input_y, input_major); + 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_major); + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, input_p); +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS + { + struct dda_finger_coords finger_data; + finger_data.x = input_x; + finger_data.y = input_y; + finger_data.p = input_p; + finger_data.minor = input_major; + finger_data.major = input_major; + moto_dda_process_finger_press(input_id - 1, &finger_data); + +#ifdef MOTO_DDA_ACTIVE_STYLUS //ACTIVE_PEN_FEATURE TEST + if(0 == (input_id - 1)){ + struct dda_pen_coords pen_data; + pen_data.status = 1; + pen_data.tool_type = 0; + pen_data.tilt_x = input_p / 10; + pen_data.tilt_y = input_p / 20; + pen_data.x = input_x; + pen_data.y = input_y; + pen_data.p = input_p; + moto_dda_process_pen_report(&pen_data); + } +#endif //#ifdef MOTO_DDA_ACTIVE_STYLUS + } +#endif + + } +#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); +#ifdef PALM_GESTURE_RANGE + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MINOR, 0); + input_report_abs(ts->input_dev, ABS_MT_ORIENTATION, 0); +#endif + 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); +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS + moto_dda_process_finger_release(i); +#ifdef MOTO_DDA_ACTIVE_STYLUS //ACTIVE_PEN_FEATURE TEST + if(0 == i){ + struct dda_pen_coords pen_data; + pen_data.status = 0; + pen_data.tool_type = 0; + pen_data.tilt_x = 0; + pen_data.tilt_y = 0; + pen_data.x = 0; + pen_data.y = 0; + pen_data.p = 0; + moto_dda_process_pen_report(&pen_data); + } +#endif //#ifdef MOTO_DDA_ACTIVE_STYLUS +#endif + + + } + } + + input_report_key(ts->input_dev, BTN_TOUCH, (finger_cnt > 0)); + +#ifdef NVT_TOUCH_LAST_TIME + if (finger_cnt > 0) { + //touch down/moving + if(time_flag) { + //check time_flag to get boot time only once in one touch session, + //to avoid impact touch performance for multi touch & moving cases + ts->last_event_time = ktime_get_boottime(); + time_flag = 0; + NVT_DBG("set last_event_time\n"); + } + } else { + //touch UP, enable time flag for next touch + time_flag = 1; + NVT_DBG("finger_cnt 0, reset time_flag\n"); + } +#endif + +#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 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; + + //---Check for 5 times--- + for (retry = 5; retry > 0; retry--) { + + nvt_bootloader_reset(); + + nvt_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; + CTP_SPI_READ(ts->client, buf, 7); + NVT_LOG("nvt_ts_check_chip_ver_trim: buf[0]=0x%02X\n", buf[0]); + 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]); + + // 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; + ts->hw_crc = trim_id_table[list].hwinfo->hw_crc; + strncpy(ts->product_id, trim_id_table[list].trim_id, 10); + ret = 0; + goto out; + } else { + ts->mmap = NULL; + ret = -1; + } + } + + msleep(10); + } + +out: + return ret; +} + +#ifdef NVT_SENSOR_EN +static int nvt_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ +#ifdef NVT_WAKEUP_GESTURE_CTRL + NVT_LOG("wakeup gesture ctrl, do nothing"); +#else + NVT_LOG("Gesture set enable %d!", enable); + mutex_lock(&ts->state_mutex); + if (enable == 1) { + ts->should_enable_gesture = true; + } else if (enable == 0) { + ts->should_enable_gesture = false; + } else { + NVT_LOG("unknown enable symbol\n"); + } + mutex_unlock(&ts->state_mutex); +#endif + return 0; +} + +static int nvt_sensor_init(struct nvt_ts_data *data) +{ + struct nvt_sensor_platform_data *sensor_pdata; + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + NVT_ERR("Failed to allocate device"); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct nvt_sensor_platform_data), + GFP_KERNEL); + if (!sensor_pdata) { + NVT_ERR("Failed to allocate memory"); + goto free_sensor_pdata; + } + data->sensor_pdata = sensor_pdata; + +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + __set_bit(EV_KEY, sensor_input_dev->evbit); + __set_bit(BTN_TRIGGER_HAPPY3, sensor_input_dev->keybit); + __set_bit(BTN_TRIGGER_HAPPY6, sensor_input_dev->keybit); +#else + if (data->report_gesture_key) { + __set_bit(EV_KEY, sensor_input_dev->evbit); + __set_bit(KEY_F1, sensor_input_dev->keybit); + } else { + __set_bit(EV_ABS, sensor_input_dev->evbit); + input_set_abs_params(sensor_input_dev, ABS_DISTANCE, + 0, REPORT_MAX_COUNT, 0, 0); + } +#endif + __set_bit(EV_SYN, sensor_input_dev->evbit); + + sensor_input_dev->name = "double-tap"; + data->sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + NVT_ERR("Unable to register device, err=%d", err); + goto free_sensor_input_dev; + } + + sensor_pdata->ps_cdev = sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = nvt_sensor_set_enable; + sensor_pdata->data = data; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(data->sensor_pdata->input_sensor_dev); +free_sensor_input_dev: + input_free_device(data->sensor_pdata->input_sensor_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, sensor_pdata); + data->sensor_pdata = NULL; +exit: + return 1; +} + +int nvt_sensor_remove(struct nvt_ts_data *data) +{ + sensors_classdev_unregister(&data->sensor_pdata->ps_cdev); + input_unregister_device(data->sensor_pdata->input_sensor_dev); + devm_kfree(&data->sensor_pdata->input_sensor_dev->dev, + data->sensor_pdata); + data->sensor_pdata = NULL; + data->wakeable = false; + data->should_enable_gesture = false; + if (!nvt_boot_firmware_name) { + kfree(nvt_boot_firmware_name); + nvt_boot_firmware_name = NULL; + } + if (!nvt_mp_firmware_name) { + kfree(nvt_mp_firmware_name); + nvt_mp_firmware_name = NULL; + } + return 0; +} +#endif + +#include +#include +#include + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t blen; + const char *path; + + path = kobject_get_path(&ts->client->dev.kobj, GFP_KERNEL); + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "novatek_ts"); +} + +static ssize_t ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int buildid; + + buildid = ts->fw_ver << 8 | ts->fw_type; + return scnprintf(buf, PAGE_SIZE, "%s%s\n%s%04x\n%s%04x\n", + "Product ID: ", ts->product_id, + "Build ID: ", buildid ? buildid : ts->build_id, + "Config ID: ", ts->nvt_pid ? ts->nvt_pid : ts->config_id); +} + +#ifdef PALM_GESTURE +static ssize_t nvt_palm_settings_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d", ts->palm_enabled); +} + +static ssize_t nvt_palm_settings_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) + +{ + int value; + int err = 0; + + if (count > 2) + return -EINVAL; + + if (sscanf(buf, "%d", &value) != 1) + return -EINVAL; + + err = count; + + switch (value) { + case 0: + ts->palm_enabled = false; + break; + case 1: + ts->palm_enabled = true; + break; + default: + err = -EINVAL; + ts->palm_enabled = false; + NVT_ERR("Invalid Value! %d\n", value); + break; + } + + nvt_palm_set(ts->palm_enabled); + + return err; +} +#endif + +#ifdef EDGE_SUPPRESSION +static ssize_t nvt_edge_reject_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { + int res = 0; + uint8_t state; + + res = kstrtou8(buf, 0, &state); + if (res < 0) + return res; + + ts->edge_reject_state = state; + + NVT_LOG("edge_reject_state %d!\n", state); + + if(nvt_edge_reject_read() != state) { + nvt_edge_reject_set(ts->edge_reject_state); + } + + return count; +} + +static ssize_t nvt_edge_reject_show(struct device *dev, + struct device_attribute *attr, char *buf) { + + uint8_t ret; + + ret = nvt_edge_reject_read(); + + if(ret == VERTICAL) + return scnprintf(buf, PAGE_SIZE, "VERTICAL\n"); + else if(ret == LEFT_UP) + return scnprintf(buf, PAGE_SIZE, "LEFT_UP\n"); + else if(ret == RIGHT_UP) + return scnprintf(buf, PAGE_SIZE, "RIGHT_UP\n"); + else + return scnprintf(buf, PAGE_SIZE, "Not Support!\n"); +} +#endif + +static int32_t nvt_cmd_write(uint8_t u8Cmd, uint8_t u8subCmd, uint8_t u8subCmd2, uint16_t len) +{ + int i, retry = 5; + uint8_t buf[8] = {0}; + int32_t ret = 0; + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + mutex_unlock(&ts->lock); + return ret; + } + + for (i = 0; i < retry; i++) { + //---set cmd status--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = u8Cmd; + buf[2] = u8subCmd; + buf[3] = u8subCmd2; + CTP_SPI_WRITE(ts->client, buf, len+1); + msleep(20); + + //---read cmd status--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0xFF; + CTP_SPI_READ(ts->client, buf, 2); + if (buf[1] == 0x00) + break; + } + + if (unlikely(i == retry)) { + NVT_LOG("send Cmd 0x%02X 0x%02X 0x%02X failed, buf[1]=0x%02X\n", u8Cmd, u8subCmd, u8subCmd2, buf[1]); + ret = -1; + } else { + NVT_LOG("send Cmd 0x%02X 0x%02X 0x%02X success, tried %d times\n", u8Cmd, u8subCmd, u8subCmd2, i); + } + + mutex_unlock(&ts->lock); + + return ret; +} + +static ssize_t nvt_mmi_interpolation_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + uint8_t mode = 0; + uint8_t cmd = 0; + + ret = kstrtou8(buf, 0, &mode); + if (ret < 0) { + NVT_ERR("Failed to convert value.\n"); + return -EINVAL; + } + NVT_LOG("mode=%d\n", mode); + + if (UI_FEATURE_ENABLE == mode) + cmd = CMD_ON; + else + cmd = CMD_OFF; + + if (ts->interpolation_cmd[0] != cmd) { + ts->interpolation_cmd[0] = cmd; + nvt_cmd_write(ts->interpolation_cmd[0], ts->interpolation_cmd[1], 0, 2); + } + + return size; +} + +static ssize_t nvt_mmi_interpolation_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "0x%02x\n", ts->interpolation_cmd[0]); +} + +static ssize_t nvt_mmi_jitter_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + uint8_t mode = 0; + uint8_t cmd = 0; + + ret = kstrtou8(buf, 0, &mode); + if (ret < 0) { + NVT_ERR("Failed to convert value.\n"); + return -EINVAL; + } + NVT_LOG("mode=%d\n", mode); + + if (UI_FEATURE_ENABLE == mode) + cmd = CMD_ON; + else + cmd = CMD_OFF; + + if (ts->jitter_cmd[0] != cmd) { + ts->jitter_cmd[0] = cmd; + nvt_cmd_write(ts->jitter_cmd[0], ts->jitter_cmd[1], 0, 2); + } + + return size; +} + +static ssize_t nvt_mmi_jitter_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "0x%02x\n", ts->jitter_cmd[0]); +} + +static ssize_t nvt_mmi_first_filter_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + uint8_t mode = 0; + uint8_t cmd = 0; + + ret = kstrtou8(buf, 0, &mode); + if (ret < 0) { + NVT_ERR("Failed to convert value.\n"); + return -EINVAL; + } + NVT_LOG("mode=%d\n", mode); + + if (UI_FEATURE_ENABLE == mode) + cmd = CMD_ON; + else + cmd = CMD_OFF; + + if (ts->first_filter_cmd[0] != cmd) { + ts->first_filter_cmd[0] = cmd; + nvt_cmd_write(ts->first_filter_cmd[0], ts->first_filter_cmd[1], 0, 2); + } + + return size; +} + +static ssize_t nvt_mmi_first_filter_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "0x%02x\n", ts->first_filter_cmd[0]); +} + +static ssize_t nvt_mmi_edge_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + unsigned int args[2] = { 0 }; + uint8_t mode = 0; + uint8_t rotate = 0; + + ret = sscanf(buf, "%d %d", &args[0], &args[1]); + if (ret < 2) + return -EINVAL; + NVT_LOG("mode=%d, rotate=%d\n", args[0], args[1]); + + if (DEFAULT_MODE == args[0]) { + ts->edge_cmd[0] = CMD_OFF; + ts->edge_cmd[2] = DEFAULT_EDGE; + nvt_cmd_write(ts->edge_cmd[0], ts->edge_cmd[1], 0, 2); + return size; + } else if (BIG_MODE == args[0]) + mode = BIG_EDGE; + else if (SMALL_MODE == args[0]) + mode = SMALL_EDGE; + else if (CLOSE_MODE == args[0]) + mode = CLOSE_EDGE; + else { + NVT_ERR("Invalid mode %d!\n", args[0]); + return size; + } + + if (ts->edge_cmd[2] != mode) { + ts->edge_cmd[0] = CMD_ON; + ts->edge_cmd[2] = mode; + nvt_cmd_write(ts->edge_cmd[0], ts->edge_cmd[1], ts->edge_cmd[2], 3); + } + + if (ROTATE_0 == args[1]) + rotate = EDGE_REJECT_VERTICLE_CMD; + else if (ROTATE_90 == args[1]) + rotate = EDGE_REJECT_LEFT_UP; + else if (ROTATE_270 == args[1]) + rotate = EDGE_REJECT_RIGHT_UP; + else { + NVT_ERR("Invalid rotate %d!\n", args[1]); + return size; + } + + if (ts->rotate_cmd!= rotate) { + ts->rotate_cmd = rotate; + nvt_cmd_write(ts->rotate_cmd, 0, 0, 1); + } + + return size; +} + +static ssize_t nvt_mmi_edge_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "0x%02x 0x%02x\n", ts->edge_cmd[2], ts->rotate_cmd); +} + +static int nvt_extend_attribute_group(struct device *dev, struct attribute_group **group) +{ + int idx = 0; + + if (ts->interpolation_ctrl) + ADD_ATTR(interpolation); + + if (ts->jitter_ctrl) + ADD_ATTR(jitter); + + if (ts->first_filter_ctrl) + ADD_ATTR(first_filter); + + if (ts->edge_ctrl) + ADD_ATTR(edge); + + if (idx) { + ext_attributes[idx] = NULL; + *group = &ext_attr_group; + } else + *group = NULL; + + return 0; +} + +#ifdef NVT_TOUCH_LAST_TIME +static ssize_t timestamp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ktime_t last_ktime; + struct timespec64 last_ts; + + last_ktime = ts->last_event_time; + ts->last_event_time = 0; + + last_ts = ktime_to_timespec64(last_ktime); + return scnprintf(buf, PAGE_SIZE, "%lld.%ld\n", last_ts.tv_sec, last_ts.tv_nsec); +} +#endif + +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL +#ifdef NVT_WAKEUP_GESTURE_CTRL +static void nvt_gesture_state_switch(void) +{ + if (ts->s_tap_flag || ts->d_tap_flag) { + //gesture enable + if (!ts->should_enable_gesture) { + ts->should_enable_gesture = true; +#ifdef NVT_SET_TOUCH_STATE + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); +#endif + NVT_LOG("gesture switch to enable"); + } + } else { + //gesture disable + if (ts->should_enable_gesture) { + ts->should_enable_gesture = false; +#ifdef NVT_SET_TOUCH_STATE + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); +#endif + NVT_LOG("gesture switch to disable"); + } + } +} +#endif +static ssize_t gesture_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvt_ts_data *nvt_data = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%02x\n", nvt_data->supported_gesture_type); +} + +static ssize_t gesture_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + + unsigned int value = 0; + int err = 0; + //struct nvt_ts_data *nvt_data = dev_get_drvdata(dev); + + err = sscanf(buf, "%d", &value); + if (err < 0) { + NVT_LOG("error: Failed to convert value"); + return -EINVAL; + } + + switch (value) { + case 0x10: + nvt_cmd_ext_store(0x7B,0x01); + NVT_LOG("zero tap disable..."); + ts->s_tap_flag = false; + ts->d_tap_flag = false; + break; + case 0x11: + break; + case 0x20: + NVT_LOG("single tap disable..."); + ts->s_tap_flag = false; + break; + case 0x21: + nvt_cmd_ext_store(0x7B,0x02); + NVT_LOG("single tap enable..."); + ts->s_tap_flag = true; + break; + case 0x30: + NVT_LOG("double tap disable..."); + ts->d_tap_flag = false; + break; + case 0x31: + nvt_cmd_ext_store(0x7B,0x04); + NVT_LOG("double tap enable..."); + ts->d_tap_flag = true; + break; + default: + NVT_LOG("unsupport gesture mode type"); + } +#ifdef NVT_WAKEUP_GESTURE_CTRL + nvt_gesture_state_switch(); +#endif + + return count; + +} + +#endif + +#if defined(NVT_SENSOR_EN) && defined(NOVA_STOWED_MODE_EN) +static ssize_t stowed_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + unsigned long mode = 0; + + ret = kstrtoul(buf, 0, &mode); + if (ret < 0) { + NVT_LOG("Failed to convert value.\n"); + return -EINVAL; + } + ts->get_stowed = mode; + if (ts->set_stowed == mode) { + NVT_LOG("The value = %lu is same, so not to write", mode); + ret = size; + return ret; + } + + mutex_lock(&ts->lock); + if (!ts->bTouchIsAwake && ts->should_enable_gesture) { + if (mode) { + nvt_cmd_ext_store(NVT_STOWED_MODE_CMD, NVT_STOWED_MODE_EN); + } else { + nvt_cmd_ext_store(NVT_STOWED_MODE_CMD, NVT_STOWED_MODE_DIS); + } + } else { + NVT_LOG("Skip stowed mode setting suspended:%d,should_enable_gesture:%d", ts->bTouchIsAwake,ts->should_enable_gesture); + ret = size; + mutex_unlock(&ts->lock); + return ret; + } + + ts->set_stowed = ts->get_stowed ; + ret = size; + NVT_LOG("Success to set stowed mode %lu\n", mode); + mutex_unlock(&ts->lock); + return ret; +} + +static ssize_t stowed_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + NVT_LOG("Stowed state = %d.\n", ts->set_stowed); + return scnprintf(buf, PAGE_SIZE, "0x%02x", ts->get_stowed); +} +#endif + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_RO(ic_ver), +#ifdef NVT_TOUCH_LAST_TIME + __ATTR_RO(timestamp), +#endif +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + __ATTR_RW(gesture), +#endif +#if defined(NVT_SENSOR_EN) && defined(NOVA_STOWED_MODE_EN) + __ATTR_RW(stowed), +#endif + +#ifdef PALM_GESTURE + __ATTR(palm_settings, S_IRUGO | S_IWUSR | S_IWGRP, nvt_palm_settings_show, nvt_palm_settings_store), +#endif +#ifdef EDGE_SUPPRESSION + __ATTR(rotate, S_IRUGO | S_IWUSR | S_IWGRP, nvt_edge_reject_show, nvt_edge_reject_store), +#endif + __ATTR_NULL +}; + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +/******************************************************* +Description: + Novatek touchscreen FW function class. file node + initial function. + +return: + Executive outcomes. 0---succeed. -1---failed. +*******************************************************/ +int32_t nvt_fw_class_init(bool create) +{ + struct device_attribute *attrs = touchscreen_attributes; + static struct attribute_group *extern_group; + int i, error = 0; + s32 ret = 0; + static struct class *touchscreen_class; + static struct device *ts_class_dev; + dev_t devno; + static int minor; + + if (create) { +#if defined (PALM_GESTURE) || defined (EDGE_SUPPRESSION) || defined(NVT_TOUCH_LAST_TIME) + ret = alloc_chrdev_region(&devno, 0, 1, NVT_PRIMARY_NAME); +#else + ret = alloc_chrdev_region(&devno, 0, 1, NVT_SPI_NAME); +#endif + + if (ret) { + NVT_ERR("cant`t allocate chrdev\n"); + return ret; + } + + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + + ts_class_dev = device_create(touchscreen_class, NULL, + devno, +#if defined (PALM_GESTURE) || defined (EDGE_SUPPRESSION) || defined(NVT_TOUCH_LAST_TIME) + ts, NVT_PRIMARY_NAME); +#else + ts, NVT_SPI_NAME); +#endif + if (IS_ERR(ts_class_dev)) { + error = PTR_ERR(ts_class_dev); + ts_class_dev = NULL; + return error; + } + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + error = device_create_file(ts_class_dev, &attrs[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + else +#if defined (PALM_GESTURE) || defined (EDGE_SUPPRESSION) || defined(NVT_TOUCH_LAST_TIME) + NVT_LOG("create /sys/class/touchscreen/%s Succeeded!\n", NVT_PRIMARY_NAME); +#else + NVT_LOG("create /sys/class/touchscreen/%s Succeeded!\n", NVT_SPI_NAME); +#endif + nvt_extend_attribute_group(ts_class_dev, &extern_group); + if (extern_group) { + error = sysfs_create_group(&ts_class_dev->kobj,extern_group); + if (error) + goto ext_attr_destroy; + else + NVT_LOG("Create extend attribute group Succeeded!\n"); + } + } else { + if (!touchscreen_class || !ts_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(ts_class_dev, &attrs[i]); + + if (extern_group) { + sysfs_remove_group(&ts_class_dev->kobj, extern_group); + } else { + NVT_LOG("%s: extern_group is NULL!\n", __func__); + } + + device_unregister(ts_class_dev); + class_unregister(touchscreen_class); + } + + return ret; + +ext_attr_destroy: + if (extern_group) { + sysfs_remove_group(&ts_class_dev->kobj, extern_group); + } else { + NVT_LOG("ext_attr_destroy: extern_group is NULL!\n"); + } +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor)); + ts_class_dev = NULL; + class_unregister(touchscreen_class); + NVT_ERR("error creating touchscreen class\n"); + + return -ENODEV; +} +#endif //#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + +#ifdef NOVATECH_PEN_NOTIFIER +static int pen_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + int ret = 0; + NVT_LOG("Received event(%lu) for pen detection\n", event); + + if (event == PEN_DETECTION_INSERT) + ts->nvt_pen_detect_flag = PEN_DETECTION_INSERT; + else if (event == PEN_DETECTION_PULL) + ts->nvt_pen_detect_flag = PEN_DETECTION_PULL; + + if (!ts->bTouchIsAwake || !ts->fw_ready_flag) { + NVT_LOG("touch in suspend or no firmware, so store."); + } else { + ret = nvt_mcu_pen_detect_set(ts->nvt_pen_detect_flag); + if (ret < 0) { + NVT_ERR("write pen state fail"); + } + } + + return 0; +} +#endif + +/******************************************************* +Description: + Novatek touchscreen driver probe function. + +return: + Executive outcomes. 0---succeed. negative---failed +*******************************************************/ +static int32_t nvt_ts_probe(struct spi_device *client) +{ + int32_t ret = 0; +#ifdef CONFIG_SPI_SM8450 + struct spi_geni_qcom_ctrl_data *spi_param = NULL; +#endif +#if ((TOUCH_KEY_NUM > 0) || WAKEUP_GESTURE) + int32_t retry = 0; +#endif +#ifdef NVT_SENSOR_EN + static bool initialized_sensor; +#endif +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) || defined(NVT_CONFIG_DRM_PANEL)) + struct device_node *dp = client->dev.of_node; + + ret = nova_check_dt(dp); + if (ret) { + NVT_LOG("panel error\n"); + return ret; + } +#endif +#else + if (client->dev.of_node && !mmi_device_is_available(client->dev.of_node)) { + NVT_ERR("mmi: device not supported\n"); + return -ENODEV; + } +#endif + + NVT_LOG("start\n"); + + ts = kzalloc(sizeof(struct nvt_ts_data), GFP_KERNEL); + if (ts == NULL) { + NVT_ERR("failed to allocated memory for nvt ts data\n"); + return -ENOMEM; + } + + ts->xbuf = (uint8_t *)kzalloc((NVT_TRANSFER_LEN+2), GFP_KERNEL); + if(ts->xbuf == NULL) { + NVT_ERR("kzalloc for xbuf failed!\n"); + ret = -ENOMEM; + goto err_malloc_xbuf; + } + + ts->rbuf = (uint8_t *)kzalloc(NVT_READ_LEN, GFP_KERNEL); + if(ts->rbuf == NULL) { + NVT_ERR("kzalloc for rbuf failed!\n"); + ret = -ENOMEM; + goto err_malloc_rbuf; + } + +#ifdef CONFIG_SPI_SM8450 + spi_param = devm_kzalloc(&client->dev, sizeof(spi_param), GFP_KERNEL); + if(spi_param == NULL) { + NVT_ERR("devm_kzalloc for spi_param failed!\n"); + ret = -ENOMEM; + goto err_malloc_spi_param; + } +#endif + + 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; + ts->client->chip_select = 0; + + ret = spi_setup(ts->client); + if (ret < 0) { + NVT_ERR("Failed to perform SPI setup\n"); + goto err_spi_setup; + } + +#ifdef CONFIG_MTK_SPI + /* old usage of MTK spi API */ + memcpy(&ts->spi_ctrl, &spi_ctrdata, sizeof(struct mt_chip_conf)); + ts->client->controller_data = (void *)&ts->spi_ctrl; +#endif + +#ifdef CONFIG_SPI_MT65XX + /* new usage of MTK spi API */ + memcpy(&ts->spi_ctrl, &spi_ctrdata, sizeof(struct mtk_chip_config)); + ts->client->controller_data = (void *)&ts->spi_ctrl; +#endif + +#ifdef CONFIG_SPI_SM8450 + /* Initialize the driver data */ + spi_param->spi_cs_clk_delay = 2; + client->controller_data = spi_param; +#endif + + NVT_LOG("mode=%d, max_speed_hz=%d\n", ts->client->mode, ts->client->max_speed_hz); + + //---parse dts--- + ret = nvt_parse_dt(&client->dev); + if (ret) { + NVT_ERR("parse dt error\n"); + goto err_spi_setup; + } + + //---request and config GPIOs--- + ret = nvt_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); + +#ifdef NVT_SENSOR_EN + mutex_init(&ts->state_mutex); + //unknown screen state + ts->screen_state = SCREEN_UNKNOWN; +#endif + + //---eng reset before TP_RESX high + nvt_eng_reset(); + +#if NVT_TOUCH_SUPPORT_HW_RST + gpio_set_value(ts->reset_gpio, 1); +#endif + + // need 10ms delay after POR(power on reset) + msleep(10); + + //---check chip version trim--- +#ifdef NVT_CONFIG_CHIP_VER_1 + ret = nvt_ts_check_chip_ver_trim(CHIP_VER_TRIM_ADDR_V1); + if (ret) { + NVT_LOG("try to check from old chip ver trim address\n"); + ret = nvt_ts_check_chip_ver_trim(CHIP_VER_TRIM_ADDR_V0); + } +#else + ret = nvt_ts_check_chip_ver_trim(CHIP_VER_TRIM_ADDR_V0); +#endif + if (ret) { + NVT_ERR("chip is not identified\n"); + ret = -EINVAL; + goto err_chipvertrim_failed; + } + + //---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 +#ifdef PALM_GESTURE_RANGE + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, TOUCH_DEFAULT_MAX_HEIGHT, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MINOR, 0, TOUCH_DEFAULT_MAX_HEIGHT, 0, 0); + input_set_abs_params(ts->input_dev, ABS_MT_ORIENTATION, TOUCH_ORIENTATION_MIN, TOUCH_ORIENTATION_MAX, 0, 0); +#else + input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); //area = 255 +#endif + 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 < (sizeof(gesture_key_array) / sizeof(gesture_key_array[0])); retry++) { + input_set_capability(ts->input_dev, EV_KEY, gesture_key_array[retry]); + } +#endif + +#ifdef PALM_GESTURE + ts->palm_enabled = false; +#endif +#ifdef EDGE_SUPPRESSION + ts->edge_reject_state = VERTICAL; +#endif + +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS + moto_dda_init("novatek-ts for Geneva5G"); + ret = moto_dda_register_cdevice(); + if (ret) + NVT_ERR("Failed register stylus dda device, %d", ret); +#endif + + sprintf(ts->phys, "input/ts"); + ts->input_dev->name = NVT_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; + } + + //---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_SPI_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 (ts->gesture_wait_pm) + init_waitqueue_head(&ts->pm_wq); + atomic_set(&ts->pm_resume, 1); + +#if WAKEUP_GESTURE + device_init_wakeup(&ts->input_dev->dev, 1); +#endif +#ifdef NVT_SENSOR_EN + if (!initialized_sensor) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&gesture_wakelock, WAKE_LOCK_SUSPEND, "dt-wake-lock"); +#else + PM_WAKEUP_REGISTER(&client->dev, gesture_wakelock, "dt-wake-lock"); + if (!gesture_wakelock) { + NVT_ERR("failed to allocate wakeup source\n"); + goto err_wakeup_source_register_failed; + } +#endif + if (!nvt_sensor_init(ts)) + initialized_sensor = true; + } +#endif + + if (ts->charger_detection_enable) { + ts->charger_detection = kzalloc(sizeof(struct usb_charger_detection), GFP_KERNEL); + if (ts->charger_detection == NULL) { + NVT_ERR("failed to allocated memory for usb_charger_detection\n"); + goto err_charger_detection_alloc_failed; + } + } + + if (ts->charger_detection) { + struct power_supply *psy = NULL; + union power_supply_propval prop = {0}; + + NVT_LOG("charger_detection on"); + ts->charger_detection->usb_connected = 0; + ts->charger_detection->nvt_charger_notify_wq = create_singlethread_workqueue("nvt_charger_wq"); + if (!ts->charger_detection->nvt_charger_notify_wq) { + NVT_ERR("allocate nvt_charger_notify_wq failed\n"); + goto err_charger_notify_wq_failed; + } + INIT_WORK(&ts->charger_detection->charger_notify_work, nvt_charger_notify_work); + + ts->charger_detection->charger_notif.notifier_call = charger_notifier_callback; + ret = power_supply_reg_notifier(&ts->charger_detection->charger_notif); + if (ret) { + NVT_ERR("Unable to register charger_notifier:%d\n", ret); + goto err_register_charger_notify_failed; + } + + /* if power supply supplier registered brfore TP + * ps_notify_callback will not receive PSY_EVENT_PROP_ADDED + * event, and will cause miss to set TP into charger state. + * So check PS state in probe. + */ + psy = power_supply_get_by_name("usb"); + if (psy) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,&prop); + if (ret < 0) { + NVT_ERR("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret); + goto err_register_charger_notify_failed; + } else { + usb_detect_flag = prop.intval; + if (usb_detect_flag != ts->charger_detection->usb_connected) { + if (USB_DETECT_IN == usb_detect_flag) { + ts->charger_detection->usb_connected = USB_DETECT_IN; + } else { + ts->charger_detection->usb_connected = USB_DETECT_OUT; + } + nvt_set_charger(ts->charger_detection->usb_connected); + } + } + } + } + +#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); +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + // 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 +#endif +#ifdef LCM_FAST_LIGHTUP + INIT_WORK(&ts_resume_work, nova_resume_work_func); +#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 + +#ifdef NOVATECH_PEN_NOTIFIER + ts->fw_ready_flag = false; + ts->nvt_pen_detect_flag = PEN_DETECTION_INSERT; + ts->pen_notif.notifier_call = pen_notifier_callback; + ret = pen_detection_register_client(&ts->pen_notif); + if (ret) { + NVT_ERR("[PEN]Unable to register pen_notifier: %d\n", ret); + goto err_register_pen_notif_failed; + } +#endif + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if (((LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0))) || defined(NVT_CONFIG_DRM_PANEL)) +#if defined(CONFIG_DRM) +#ifdef NVT_DRM_PANEL_EVENT_NOTIFICATIONS + if (active_panel){ + ts->notifier_cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, active_panel, + &nvt_drm_notifier_callback, ts); + } + if (!ts->notifier_cookie) { + NVT_ERR("Failed to register for panel events\n"); + goto err_register_drm_notif_failed; + } + NVT_LOG("registered for panel notifications panel\n"); +#else + ts->drm_notif.notifier_call = nvt_drm_notifier_callback; + if (active_panel && + drm_panel_notifier_register(active_panel, + &ts->drm_notif) < 0) { + NVT_LOG("register notifier failed!\n"); + goto err_register_drm_notif_failed; + } +#endif +#endif +#else //vension code < 5.4.0 +#if defined(CONFIG_FB) +#ifdef _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 + +#ifdef NVT_CONFIG_PANEL_NOTIFICATIONS + ts->panel_notif.notifier_call = nvt_panel_notifier_callback; + ret = register_panel_notifier(&ts->panel_notif); + if(ret) { + NVT_ERR("register panel_notifier failed. ret=%d\n", ret); + goto err_register_fb_notif_failed; + } +#endif + +#elif defined(CONFIG_HAS_EARLYSUSPEND) + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->early_suspend.suspend = nvt_ts_early_suspend; + ts->early_suspend.resume = nvt_ts_late_resume; + ret = register_early_suspend(&ts->early_suspend); + if(ret) { + NVT_ERR("register early suspend failed. ret=%d\n", ret); + goto err_register_early_suspend_failed; + } +#endif +#endif //end version code >= 5.4.0 +#endif //end touchscreen_mmi + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + ret = nvt_mmi_init(ts, true); +#else + ret = nvt_fw_class_init(true); +#endif + if (ret) { + NVT_ERR("Create touchscreen class failed. ret=%d\n", ret); + goto err_create_touchscreen_class_failed; + } + + ts->bTouchIsAwake = 1; + NVT_LOG("end\n"); + + nvt_irq_enable(true); + + return 0; + +err_create_touchscreen_class_failed: +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + nvt_mmi_init(ts, false); +#endif + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if (((LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0))) || defined(NVT_CONFIG_DRM_PANEL)) +#if defined(CONFIG_DRM) + if (active_panel) { +#ifdef NVT_DRM_PANEL_EVENT_NOTIFICATIONS + if (ts->notifier_cookie) + panel_event_notifier_unregister(ts->notifier_cookie); +#else + if (drm_panel_notifier_unregister(active_panel, &ts->drm_notif)) + NVT_ERR("Error occurred while unregistering drm_notifier.\n"); +#endif + } +err_register_drm_notif_failed: +#endif +#else //vension code < 5.4.0 +#if defined(CONFIG_FB) +#ifdef _MSM_DRM_NOTIFY_H_ + if (msm_drm_unregister_client(&ts->drm_notif)) + NVT_ERR("Error occurred while unregistering drm_notifier.\n"); +err_register_drm_notif_failed: +#else + if (fb_unregister_client(&ts->fb_notif)) + NVT_ERR("Error occurred while unregistering fb_notifier.\n"); +err_register_fb_notif_failed: +#endif +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts->early_suspend); +err_register_early_suspend_failed: +#endif +#endif //end version code >= 5.4.0 +#endif //end touchscreen_mmi +#ifdef NOVATECH_PEN_NOTIFIER + if (pen_detection_unregister_client(&ts->pen_notif)) + NVT_ERR("Error occurred while unregistering pen_notifier.\n"); +err_register_pen_notif_failed: +#endif +#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 +err_register_charger_notify_failed: + if (ts->charger_detection) { + if (ts->charger_detection->charger_notif.notifier_call) + power_supply_unreg_notifier(&ts->charger_detection->charger_notif); + destroy_workqueue(ts->charger_detection->nvt_charger_notify_wq); + ts->charger_detection->nvt_charger_notify_wq = NULL; + kfree(ts->charger_detection); + } +err_charger_detection_alloc_failed: +err_charger_notify_wq_failed: + free_irq(client->irq, ts); +#ifdef NVT_SENSOR_EN +#ifndef CONFIG_HAS_WAKELOCK +err_wakeup_source_register_failed: +#endif +#endif +err_int_request_failed: + input_unregister_device(ts->input_dev); + ts->input_dev = NULL; +err_input_register_device_failed: +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS + moto_dda_exit(); +#endif + if (ts->input_dev) { + input_free_device(ts->input_dev); + ts->input_dev = NULL; + } +err_input_dev_alloc_failed: +err_chipvertrim_failed: + mutex_destroy(&ts->xbuf_lock); + mutex_destroy(&ts->lock); + nvt_gpio_deconfig(ts); +err_gpio_config_failed: +err_spi_setup: +err_ckeck_full_duplex: + spi_set_drvdata(client, NULL); +#ifdef CONFIG_SPI_SM8450 + if (spi_param) { + devm_kfree(&client->dev ,spi_param); + spi_param = NULL; + } +err_malloc_spi_param: +#endif + if (ts->rbuf) { + kfree(ts->rbuf); + ts->rbuf = NULL; + } +err_malloc_rbuf: + if (ts->xbuf) { + kfree(ts->xbuf); + ts->xbuf = NULL; + } +err_malloc_xbuf: + if (ts) { + kfree(ts); + ts = NULL; + } + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen driver release function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int32_t nvt_ts_remove(struct spi_device *client) +{ + NVT_LOG("Removing driver...\n"); + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + nvt_mmi_init(ts, false); +#else + nvt_fw_class_init(false); +#endif + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if (((LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0))) || defined(NVT_CONFIG_DRM_PANEL)) +#if defined(CONFIG_DRM) + if (active_panel) { +#ifdef NVT_DRM_PANEL_EVENT_NOTIFICATIONS + if (ts->notifier_cookie) + panel_event_notifier_unregister(ts->notifier_cookie); +#else + drm_panel_notifier_unregister(active_panel, &ts->drm_notif); + NVT_ERR("Error occurred while unregistering drm_notifier.\n"); +#endif + } +#endif +#else //vension code < 5.4.0 +#if defined(CONFIG_FB) +#ifdef _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 +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts->early_suspend); +#endif + +#ifdef NVT_CONFIG_PANEL_NOTIFICATIONS + if (unregister_panel_notifier(&ts->panel_notif)) + NVT_ERR("Error occurred while unregistering panel_notifier.\n"); +#endif +#endif //end version code >= 5.4.0 +#endif //end touchscren_mmi +#ifdef NOVATECH_PEN_NOTIFIER + if (pen_detection_unregister_client(&ts->pen_notif)) + NVT_ERR("Error occurred while unregistering pen_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(); + if (ts->charger_detection) { + if (ts->charger_detection->charger_notif.notifier_call) + power_supply_unreg_notifier(&ts->charger_detection->charger_notif); + destroy_workqueue(ts->charger_detection->nvt_charger_notify_wq); + kfree(ts->charger_detection); + } +#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 + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if WAKEUP_GESTURE + device_init_wakeup(&ts->input_dev->dev, 0); +#if defined(NVT_CONFIG_PANEL_NOTIFICATIONS) || defined(NVT_SET_TOUCH_STATE) + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); +#endif +#endif +#endif + + nvt_irq_enable(false); + free_irq(client->irq, ts); + + mutex_destroy(&ts->xbuf_lock); + mutex_destroy(&ts->lock); + + nvt_gpio_deconfig(ts); + +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS + moto_dda_exit(); +#endif + + if (ts->input_dev) { + input_unregister_device(ts->input_dev); + ts->input_dev = NULL; + } + + spi_set_drvdata(client, NULL); + + if (ts) { + kfree(ts); + ts = NULL; + } + + return 0; +} + +static void nvt_ts_shutdown(struct spi_device *client) +{ + NVT_LOG("Shutdown driver...\n"); + + nvt_irq_enable(false); + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if (((LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0))) || defined(NVT_CONFIG_DRM_PANEL)) +#if defined(CONFIG_DRM) + if (active_panel) { +#ifdef NVT_DRM_PANEL_EVENT_NOTIFICATIONS + if (ts->notifier_cookie) + panel_event_notifier_unregister(ts->notifier_cookie); +#else + drm_panel_notifier_unregister(active_panel, &ts->drm_notif); + NVT_ERR("Error occurred while unregistering drm_notifier.\n"); +#endif + } +#endif +#else //vension code < 5.4.0 +#if defined(CONFIG_FB) +#ifdef _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 +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts->early_suspend); +#endif + +#ifdef NVT_CONFIG_PANEL_NOTIFICATIONS + if (unregister_panel_notifier(&ts->panel_notif)) + NVT_ERR("Error occurred while unregistering panel_notifier.\n"); +#endif +#endif //end version code >= 5.4.0 +#endif //end touchscreen_mmi +#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 + device_init_wakeup(&ts->input_dev->dev, 0); +#endif +} + +/** + * Release all the touches in the linux input subsystem + * @param info pointer to fts_ts_info which contains info about the device and + * its hw setup + */ + +void release_all_touches(void) +{ +#if MT_PROTOCOL_B + uint32_t i = 0; + for (i = 0; i < ts->max_touch_num; i++) { + input_mt_slot(ts->input_dev, i); +#ifdef PALM_GESTURE_RANGE + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MINOR, 0); + input_report_abs(ts->input_dev, ABS_MT_ORIENTATION, 0); +#endif + 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); +#ifdef CONFIG_MOTO_DDA_PASSIVESTYLUS + moto_dda_process_finger_release(i); +#ifdef MOTO_DDA_ACTIVE_STYLUS //ACTIVE_PEN_FEATURE TEST + if(0 == i){ + struct dda_pen_coords pen_data; + pen_data.status = 0; + pen_data.tool_type = 0; + pen_data.tilt_x = 0; + pen_data.tilt_y = 0; + pen_data.x = 0; + pen_data.y = 0; + pen_data.p = 0; + moto_dda_process_pen_report(&pen_data); + } +#endif //#ifdef MOTO_DDA_ACTIVE_STYLUS +#endif + + } +#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); +} + +/******************************************************* +Description: + Novatek touchscreen driver suspend function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +int32_t nvt_ts_suspend(struct device *dev) +{ + uint8_t buf[4] = {0}; + +#ifdef NVT_SENSOR_EN + mutex_lock(&ts->state_mutex); +#endif + + if (!ts->bTouchIsAwake) { +#ifdef NVT_SENSOR_EN + mutex_unlock(&ts->state_mutex); +#endif + NVT_LOG("Touch is already suspend\n"); + return 0; + } + +#if !WAKEUP_GESTURE + nvt_irq_enable(false); +#else +#ifdef NVT_SENSOR_EN + if (!ts->should_enable_gesture) + nvt_irq_enable(false); +#endif +#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"); + + ts->bTouchIsAwake = 0; + +#if defined(NVT_SENSOR_EN) && defined(NOVA_STOWED_MODE_EN) + if (ts->should_enable_gesture && ts->get_stowed && (!ts->bTouchIsAwake)) { + nvt_cmd_ext_store(NVT_STOWED_MODE_CMD, NVT_STOWED_MODE_EN); + NVT_LOG("Enable stowed mode suspend\n"); + ts->set_stowed = ts->get_stowed; + } +#endif + +#if WAKEUP_GESTURE +#ifdef NVT_SENSOR_EN + if (ts->should_enable_gesture) { +#endif + //---write command to enter "wakeup gesture mode"--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x13; + CTP_SPI_WRITE(ts->client, buf, 2); + + enable_irq_wake(ts->client->irq); + ts->gesture_enabled = true; + ts->wakeable = true; + + NVT_LOG("Enabled touch wakeup gesture\n"); +#ifdef NVT_SENSOR_EN + } else { + //---write command to enter "deep sleep mode"--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x11; + CTP_SPI_WRITE(ts->client, buf, 2); + ts->gesture_enabled = false; + ts->wakeable = false; + } +#endif +#else // WAKEUP_GESTURE + //---write command to enter "deep sleep mode"--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x11; + CTP_SPI_WRITE(ts->client, buf, 2); +#endif // WAKEUP_GESTURE + + + + mutex_unlock(&ts->lock); + + release_all_touches(); + msleep(50); + + NVT_LOG("end\n"); +#ifdef NVT_SENSOR_EN + ts->screen_state = SCREEN_OFF; + mutex_unlock(&ts->state_mutex); +#endif + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen driver resume function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +int32_t nvt_ts_resume(struct device *dev) +{ + +#ifdef NVT_SENSOR_EN + mutex_lock(&ts->state_mutex); +#endif + if (ts->bTouchIsAwake) { +#ifdef NVT_SENSOR_EN + mutex_unlock(&ts->state_mutex); +#endif + NVT_LOG("Touch is already resume\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_TOUCH_SUPPORT_HW_RST + gpio_set_value(ts->reset_gpio, 1); +#endif + queue_delayed_work(nvt_fwu_wq, &ts->nvt_fwu_work, msecs_to_jiffies(0)); + +#if !WAKEUP_GESTURE + nvt_irq_enable(true); +#else +#ifdef NVT_SENSOR_EN + if (!ts->gesture_enabled) + nvt_irq_enable(true); +#endif +#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 */ + + if (ts->charger_detection) { + queue_work(ts->charger_detection->nvt_charger_notify_wq, &ts->charger_detection->charger_notify_work); + } + +#if WAKEUP_GESTURE +#ifdef NVT_SENSOR_EN + if (ts->wakeable) { + disable_irq_wake(ts->client->irq); + ts->gesture_enabled = false; + ts->wakeable = false; + } +#endif +#endif + + ts->bTouchIsAwake = 1; + + mutex_unlock(&ts->lock); + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + /* restore data */ + if (ts->interpolation_ctrl) { + nvt_cmd_write(ts->interpolation_cmd[0], ts->interpolation_cmd[1], 0, 2); + } + + if (ts->jitter_ctrl) { + nvt_cmd_write(ts->jitter_cmd[0], ts->jitter_cmd[1], 0, 2); + } + + if (ts->first_filter_ctrl) { + nvt_cmd_write(ts->first_filter_cmd[0], ts->first_filter_cmd[1], 0, 2); + } + + if (ts->edge_ctrl) { + if (ts->edge_cmd[0] == CMD_ON) + nvt_cmd_write(ts->edge_cmd[0], ts->edge_cmd[1], ts->edge_cmd[2], 3); + else + nvt_cmd_write(ts->edge_cmd[0], ts->edge_cmd[1], 0, 2); + + nvt_cmd_write(ts->rotate_cmd, 0, 0, 1); + } +#endif +#if defined(NVT_SENSOR_EN) && defined(NOVA_STOWED_MODE_EN) + ts->set_stowed = 0; +#endif + + NVT_LOG("end\n"); + +#ifdef NVT_SENSOR_EN + ts->screen_state = SCREEN_ON; + mutex_unlock(&ts->state_mutex); +#endif + return 0; +} + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if (((LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0))) || defined(NVT_CONFIG_DRM_PANEL)) +#if defined(CONFIG_DRM) +#ifdef LCM_FAST_LIGHTUP +static void nova_resume_work_func(struct work_struct *work) +{ + NVT_LOG("%s\n", __func__); + nvt_ts_resume(&ts->client->dev); +} +#endif //end LCM_FAST_LIGHTUP +#ifdef NVT_DRM_PANEL_EVENT_NOTIFICATIONS +static void nvt_drm_notifier_callback(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *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) { +#ifdef LCM_FAST_LIGHTUP + if (nvt_fwu_wq) { + queue_work(nvt_fwu_wq, &ts_resume_work); + NVT_LOG("LCM_FAST_LIGHTUP, queue_work\n"); + } else { + NVT_LOG("nvt_fwu_wq null"); + nvt_ts_resume(&ts->client->dev); + } +#else + nvt_ts_resume(&ts->client->dev); +#endif //LCM_FAST_LIGHTUP + } + + break; + + case DRM_PANEL_EVENT_BLANK: + if (notification->notif_data.early_trigger) { + NVT_LOG("event=DRM_PANEL_EVENT_BLANK"); +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + + if((ts->s_tap_flag == true)&&(ts->d_tap_flag == false)){ + nvt_cmd_ext_store(0x7B,0x02); + } + else if((ts->s_tap_flag == false)&&(ts->d_tap_flag == true)){ + nvt_cmd_ext_store(0x7B,0x04); + } + else if((ts->s_tap_flag == true)&&(ts->d_tap_flag == true)){ + nvt_cmd_ext_store(0x7B,0x03); + } + + if (ts->s_tap_flag || ts->d_tap_flag){ + ts->should_enable_gesture = true; + }else{ + ts->should_enable_gesture = false; + } +#endif + nvt_ts_suspend(&ts->client->dev); +#if defined(NVT_SENSOR_EN) && (defined(NVT_SET_TOUCH_STATE) || defined(NVT_CONFIG_PANEL_NOTIFICATIONS)) + if (ts->should_enable_gesture) { + NVT_LOG("double tap gesture suspend\n"); + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); + } else { + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); + } +#endif + } + 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; + } +} +#else +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); + + NVT_LOG("nvt_drm_notifier_callback start\n"); + if (!evdata) + return 0; + + if (!(event == MSM_DRM_EARLY_EVENT_BLANK || + event == MSM_DRM_EVENT_BLANK)) { + NVT_LOG("event(%lu) do not need process\n", event); + return 0; + } + + blank = evdata->data; + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); + if (event == MSM_DRM_EARLY_EVENT_BLANK) { + if (*blank == MSM_DRM_BLANK_POWERDOWN) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); +#ifdef NVT_WAKEUP_GESTURE_CTRL + if ((ts->s_tap_flag == true) && (ts->d_tap_flag == false)) { + nvt_cmd_ext_store(0x7B,0x02); + NVT_LOG("Support single tap feature \n"); + } else if ((ts->s_tap_flag == false) && (ts->d_tap_flag == true)) { + nvt_cmd_ext_store(0x7B,0x04); + NVT_LOG("Support double tap feature \n"); + } else if ((ts->s_tap_flag == true) && (ts->d_tap_flag == true)) { + nvt_cmd_ext_store(0x7B,0x03); + NVT_LOG("Support single tap and double tap feature \n"); + } + + if (ts->s_tap_flag || ts->d_tap_flag) { + ts->should_enable_gesture = true; + } else { + ts->should_enable_gesture = false; + } +#endif + nvt_ts_suspend(&ts->client->dev); +#if defined(NVT_SENSOR_EN) && (defined(NVT_SET_TOUCH_STATE) || defined(NVT_CONFIG_PANEL_NOTIFICATIONS)) + if (ts->should_enable_gesture) { + NVT_LOG("double tap gesture suspend\n"); + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); + } else { + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); + } +#endif + } + } else if (event == MSM_DRM_EVENT_BLANK) { + if (*blank == MSM_DRM_BLANK_UNBLANK) { + NVT_LOG("event=%lu, *blank=%d\n", event, *blank); +#ifdef LCM_FAST_LIGHTUP + if (nvt_fwu_wq) { + queue_work(nvt_fwu_wq, &ts_resume_work); + NVT_LOG("LCM_FAST_LIGHTUP, queue_work\n"); + } else { + NVT_LOG("nvt_fwu_wq null"); + nvt_ts_resume(&ts->client->dev); + } +#else + nvt_ts_resume(&ts->client->dev); +#endif //LCM_FAST_LIGHTUP + } + } + + return 0; +} +#endif +#endif + +#else //vension code < 5.4.0 +#ifdef NVT_CONFIG_PANEL_NOTIFICATIONS +static int nvt_panel_notifier_callback(struct notifier_block *self, unsigned long event, void *data) +{ + struct nvt_ts_data *ts = + container_of(self, struct nvt_ts_data, panel_notif); + + switch (event) { + case PANEL_EVENT_PRE_DISPLAY_OFF: + NVT_LOG("event=%lu\n", event); + nvt_ts_suspend(&ts->client->dev); +#ifdef NVT_SENSOR_EN + if (ts->should_enable_gesture) { + NVT_LOG("double tap gesture suspend\n"); + touch_set_state(TOUCH_LOW_POWER_STATE, TOUCH_PANEL_IDX_PRIMARY); + } else { + touch_set_state(TOUCH_DEEP_SLEEP_STATE, TOUCH_PANEL_IDX_PRIMARY); + } +#endif + break; + + case PANEL_EVENT_DISPLAY_ON: + NVT_LOG("event=%lu\n", event); + nvt_ts_resume(&ts->client->dev); + break; + default: /* use DEV_TS here to avoid unused variable */ + NVT_LOG("%s: function not implemented event %lu\n", __func__, event); + break; + } + + return 0; +} +#endif + +#if defined(CONFIG_FB) +#ifdef _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); +#ifdef NVT_SENSOR_EN + if (ts->should_enable_gesture) { + NVT_LOG("double tap gesture suspend\n"); + return 1; + } +#endif + } + } 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 +#elif defined(CONFIG_HAS_EARLYSUSPEND) +/******************************************************* +Description: + Novatek touchscreen driver early suspend function. + +return: + n.a. +*******************************************************/ +static void nvt_ts_early_suspend(struct early_suspend *h) +{ + nvt_ts_suspend(ts->client, PMSG_SUSPEND); +} + +/******************************************************* +Description: + Novatek touchscreen driver late resume function. + +return: + n.a. +*******************************************************/ +static void nvt_ts_late_resume(struct early_suspend *h) +{ + nvt_ts_resume(ts->client); +} +#endif +#endif //version code >= 5.4.0 +#endif //end touchscreen_mmi + +static const struct spi_device_id nvt_ts_id[] = { + { NVT_SPI_NAME, 0 }, + { } +}; + +int nvt_set_charger(uint8_t charger_on_off) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + NVT_LOG("set charger: %d\n", charger_on_off); + + msleep(20); + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto nvt_set_charger_out; + } + + if (charger_on_off == USB_DETECT_IN) { + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = CMD_CHARGER_ON; + ret = CTP_SPI_WRITE(ts->client, buf, 2); + if (ret < 0) { + NVT_ERR("Write set charger command fail!\n"); + goto nvt_set_charger_out; + } else { + NVT_LOG("set charger on cmd succeeded\n"); + } + } else if (charger_on_off == USB_DETECT_OUT) { + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = CMD_CHARGER_OFF; + ret = CTP_SPI_WRITE(ts->client, buf, 2); + if (ret < 0) { + NVT_ERR("Write set charger command fail!\n"); + goto nvt_set_charger_out; + } else { + NVT_LOG("set charger off cmd succeeded\n"); + } + } else { + NVT_ERR("Invalid charger parameter!\n"); + ret = -EINVAL; + } + + nvt_set_charger_out: + + return ret; +} + +static void nvt_charger_notify_work(struct work_struct *work) +{ + if (NULL == work) { + NVT_ERR("%s: parameter work are null!\n", __func__); + return; + } + NVT_LOG("enter\n"); + if (USB_DETECT_IN == usb_detect_flag) { + if (mutex_lock_interruptible(&ts->lock)) { + NVT_ERR("Failed to lock in mutex_lock_interruptible(&ts->lock).\n"); + return; + } + nvt_set_charger(USB_DETECT_IN); + mutex_unlock(&ts->lock); + } else if (USB_DETECT_OUT == usb_detect_flag) { + if (mutex_lock_interruptible(&ts->lock)) { + NVT_ERR("Failed to lock in mutex_lock_interruptible(&ts->lock).\n"); + return; + } + nvt_set_charger(USB_DETECT_OUT); + mutex_unlock(&ts->lock); + }else{ + NVT_LOG("Charger flag:%d not currently required!\n",usb_detect_flag); + } +} + +static int charger_notifier_callback(struct notifier_block *nb, + unsigned long val, void *v) +{ + int ret = 0; + struct power_supply *psy = NULL; + struct usb_charger_detection *charger_detection = + container_of(nb, struct usb_charger_detection, charger_notif); + union power_supply_propval prop; + + psy= power_supply_get_by_name("usb"); + if (!psy){ + return -EINVAL; + NVT_ERR("Couldn't get usbpsy\n"); + } + + if (!strcmp(psy->desc->name, "usb")){ + if (psy && charger_detection && val == POWER_SUPPLY_PROP_STATUS) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,&prop); + if (ret < 0) { + NVT_ERR("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret); + return ret; + }else{ + usb_detect_flag = prop.intval; + if(usb_detect_flag != charger_detection->usb_connected) { + if (USB_DETECT_IN == usb_detect_flag) { + charger_detection->usb_connected = USB_DETECT_IN; + }else{ + charger_detection->usb_connected = USB_DETECT_OUT; + } + if (ts->bTouchIsAwake){ + queue_work(charger_detection->nvt_charger_notify_wq, + &charger_detection->charger_notify_work); + } + } + } + } + } + return 0; +} + +#ifdef CONFIG_OF +static struct of_device_id nvt_match_table[] = { + { .compatible = "novatek,NVT-ts-spi",}, + { }, +}; +#endif + +static int nvt_pm_suspend(struct device *dev) +{ + struct nvt_ts_data *ts = dev_get_drvdata(dev); + + NVT_LOG("CALL BACK TP PM SUSPEND"); + + atomic_set(&ts->pm_resume, 0); + + return 0; +} + +static int nvt_pm_resume(struct device *dev) +{ + struct nvt_ts_data *ts = dev_get_drvdata(dev); + + NVT_LOG("CALL BACK TP PM RESUME"); + + atomic_set(&ts->pm_resume, 1); + + if ((ts->gesture_wait_pm) && (ts->gesture_enabled)) + wake_up_interruptible(&ts->pm_wq); + + return 0; +} + +static const struct dev_pm_ops nvt_dev_pm_ops = { + .suspend = nvt_pm_suspend, + .resume = nvt_pm_resume, +}; + +static struct spi_driver nvt_spi_driver = { + .probe = nvt_ts_probe, + .remove = nvt_ts_remove, + .shutdown = nvt_ts_shutdown, + .id_table = nvt_ts_id, + .driver = { + .name = NVT_SPI_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_OF + .of_match_table = nvt_match_table, +#endif +#ifdef CONFIG_PM + .pm = &nvt_dev_pm_ops, +#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 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_driver_exit(void) +{ + spi_unregister_driver(&nvt_spi_driver); +} + +//late_initcall(nvt_driver_init); +module_init(nvt_driver_init); +module_exit(nvt_driver_exit); + +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +MODULE_DESCRIPTION("Novatek Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx.h b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx.h new file mode 100644 index 000000000000..f1591c1ddab8 --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx.h @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 46000 $ + * $Date: 2019-06-12 14:25:52 +0800 (週三, 12 六月 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 + +#include +#include +#include +#include +#include +#include +#include +#ifdef NVT_TOUCH_LAST_TIME +#include +#endif + +#ifdef NVT_SENSOR_EN +#include +#endif + +#ifdef NVT_CONFIG_PANEL_NOTIFICATIONS +#include +#endif +#ifdef NOVATECH_PEN_NOTIFIER +#include +#endif +#if (!defined(CONFIG_INPUT_TOUCHSCREEN_MMI) && defined(NVT_CONFIG_DRM_PANEL)) +#if IS_ENABLED(CONFIG_QCOM_PANEL_EVENT_NOTIFIER) +#define NVT_DRM_PANEL_EVENT_NOTIFICATIONS +#include +#endif +#endif +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + +#include "nt36xxx_mem_map.h" + +#ifdef CONFIG_MTK_SPI +/* Please copy mt_spi.h file under mtk spi driver folder */ +#include "mt_spi.h" +#endif + +#ifdef CONFIG_SPI_MT65XX +#include +#endif + +#include + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +#include +#endif + +#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 + + +//---SPI driver info.--- +#define NVT_SPI_NAME "NVT-ts" +#define NVT_PRIMARY_NAME "primary" + +#if NVT_DEBUG +#define NVT_LOG(fmt, args...) pr_err("[%s] %s %d: " fmt, NVT_SPI_NAME, __func__, __LINE__, ##args) +#else +#define NVT_LOG(fmt, args...) pr_info("[%s] %s %d: " fmt, NVT_SPI_NAME, __func__, __LINE__, ##args) +#endif +#define NVT_ERR(fmt, args...) pr_err("[%s] %s %d: " fmt, NVT_SPI_NAME, __func__, __LINE__, ##args) +#define NVT_DBG(fmt, args...) pr_debug("[%s] %s %d: " fmt, NVT_SPI_NAME, __func__, __LINE__, ##args) + +//---Input device info.--- +#define NVT_TS_NAME "NVTCapacitiveTouchScreen" + + +//---Touch info.--- +#define TOUCH_COORDS_ARR_SIZE 2 +#define TOUCH_DEFAULT_MAX_WIDTH 720 +#define TOUCH_DEFAULT_MAX_HEIGHT 1600 +#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 +#ifdef PALM_GESTURE +#define PALM_HAND_HOLDE 900 +#define PALM_HANG 1000 +#ifdef PALM_GESTURE_RANGE +#define TOUCH_ORIENTATION_MIN 0 +#define TOUCH_ORIENTATION_MAX 90 +#define PANEL_REAL_WIDTH 7096 +#define PANEL_REAL_HEIGHT 15768 +#endif +#endif +#define EDGE_REJECT_VERTICLE_CMD 0xBA +#define EDGE_REJECT_LEFT_UP 0xBB +#define EDGE_REJECT_RIGHT_UP 0xBC +#define VERTICAL 1 +#define LEFT_UP 2 +#define RIGHT_UP 3 +#define NVT_STOWED_MODE_CMD 0x7C +#define NVT_STOWED_MODE_EN 0x01 +#define NVT_STOWED_MODE_DIS 0x00 + +/* Enable only when module have tp reset pin and connected to host */ +#define NVT_TOUCH_SUPPORT_HW_RST 0 + +//---Customerized func.--- +#define NVT_TOUCH_PROC 1 +#define NVT_TOUCH_EXT_PROC 1 +#define NVT_TOUCH_MP 1 +#define MT_PROTOCOL_B 1 +#if defined (NVT_SENSOR_EN) || defined (CONFIG_INPUT_TOUCHSCREEN_MMI) +#define WAKEUP_GESTURE 1 +#else +#define WAKEUP_GESTURE 0 +#endif +#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" +#define MP_UPDATE_FIRMWARE_NAME "novatek_ts_mp.bin" +#define POINT_DATA_CHECKSUM 1 +#define POINT_DATA_CHECKSUM_LEN 65 +#define NVT_FILE_NAME_LENGTH 128 +//---ESD Protect.--- +#ifdef NVT_CONFIG_ESD_ENABLE +#define NVT_TOUCH_ESD_PROTECT 1 +#else +#define NVT_TOUCH_ESD_PROTECT 0 +#endif +#define NVT_TOUCH_ESD_CHECK_PERIOD 1500 /* ms */ +#define NVT_TOUCH_WDT_RECOVERY 1 + +#define DOUBLE_TAP_GESTURE_MODE_CMD 0x7B + +#if NVT_TOUCH_ESD_PROTECT +extern struct delayed_work nvt_esd_check_work; +#endif + +#ifdef NVT_SENSOR_EN +/* display state */ +enum display_state { + SCREEN_UNKNOWN, + SCREEN_OFF, + SCREEN_ON, +}; +struct nvt_sensor_platform_data { + struct input_dev *input_sensor_dev; + struct sensors_classdev ps_cdev; + int sensor_opened; + char sensor_data; /* 0 near, 1 far */ + struct nvt_ts_data *data; +}; +#endif + +/* charger detect */ +#define USB_DETECT_IN 1 +#define USB_DETECT_OUT 0 +#define CMD_CHARGER_ON (0x53) +#define CMD_CHARGER_OFF (0x51) + +struct usb_charger_detection { + struct notifier_block charger_notif; + uint8_t usb_connected; + struct workqueue_struct *nvt_charger_notify_wq; + struct work_struct charger_notify_work; +}; + +struct nvt_ts_data { + struct spi_device *client; + struct input_dev *input_dev; + struct delayed_work nvt_fwu_work; + uint16_t addr; + int8_t phys[32]; + uint8_t bTouchIsAwake; + 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; + uint8_t hw_crc; + uint16_t nvt_pid; + uint8_t *rbuf; + uint8_t *xbuf; + struct mutex xbuf_lock; + bool irq_enabled; + atomic_t pm_resume; + wait_queue_head_t pm_wq; + bool gesture_wait_pm; + const char *panel_supplier; +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + int supported_gesture_type; + bool d_tap_flag; + bool s_tap_flag; +#endif +#ifdef CONFIG_MTK_SPI + struct mt_chip_conf spi_ctrl; +#endif + uint32_t charger_detection_enable; + uint32_t report_gesture_key; + struct usb_charger_detection *charger_detection; +#ifdef CONFIG_SPI_MT65XX + struct mtk_chip_config spi_ctrl; +#endif +#ifdef WAKEUP_GESTURE + bool gesture_enabled; + bool wakeable; +#endif +#ifdef NVT_TOUCH_LAST_TIME + ktime_t last_event_time; +#endif +#ifdef NOVATECH_PEN_NOTIFIER + bool fw_ready_flag; + int nvt_pen_detect_flag; + struct notifier_block pen_notif; +#endif +#ifdef NVT_SENSOR_EN + bool should_enable_gesture; + enum display_state screen_state; + struct mutex state_mutex; + struct nvt_sensor_platform_data *sensor_pdata; +#endif +#ifdef PALM_GESTURE + bool palm_enabled; +#endif +#ifdef EDGE_SUPPRESSION + uint32_t edge_reject_state; +#endif + char product_id[10]; + uint8_t fw_type; + atomic_t loading_fw; + uint32_t build_id; + uint32_t config_id; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +#if defined(CONFIG_DRM) + struct notifier_block drm_notif; +#ifdef NVT_DRM_PANEL_EVENT_NOTIFICATIONS + void* notifier_cookie; +#endif +#endif +#else //vension code < 5.4.0 +#if defined(CONFIG_FB) +#ifdef _MSM_DRM_NOTIFY_H_ + struct notifier_block drm_notif; +#else + struct notifier_block fb_notif; +#endif +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif +#ifdef NVT_CONFIG_PANEL_NOTIFICATIONS + struct notifier_block panel_notif; +#endif +#endif //version code >= 5.4.0 + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + struct ts_mmi_class_methods *imports; +#endif +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW + int flash_mode; +#endif + uint8_t jitter_cmd[2]; /* /< support report rate switching */ + bool jitter_ctrl; /* /< support report rate switching */ + uint8_t first_filter_cmd[2]; /* /< support report rate switching */ + bool first_filter_ctrl; /* /< support report rate switching */ + uint8_t interpolation_cmd[2]; /* /< report rate interpolation command */ + bool interpolation_ctrl; /* /< support report rate interpolation */ + uint8_t edge_cmd[3]; /* /< edge switching command */ + uint8_t rotate_cmd; /* /< rotate switching command */ + bool edge_ctrl; /* /< edge rate switching */ + int set_stowed; + int get_stowed; +}; + +#if NVT_TOUCH_PROC +struct nvt_flash_data{ + rwlock_t lock; +}; +#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_HOST_CMD_CHECK = 0x5C, + EVENT_MAP_RESET_COMPLETE = 0x60, + EVENT_MAP_FWINFO = 0x78, + EVENT_MAP_PROJECTID = 0x9A, +} SPI_EVENT_MAP; + +#ifdef NVT_SET_TOUCH_STATE +#define MAX_PANEL_IDX 2 +enum touch_panel_id { + TOUCH_PANEL_IDX_PRIMARY = 0, + TOUCH_PANEL_MAX_IDX, +}; + +enum touch_state { + TOUCH_DEEP_SLEEP_STATE = 0, + TOUCH_LOW_POWER_STATE, +}; +#endif + +//---SPI READ/WRITE--- +#define SPI_WRITE_MASK(a) (a | 0x80) +#define SPI_READ_MASK(a) (a & 0x7F) + +#define DUMMY_BYTES (1) +#define NVT_TRANSFER_LEN (63*1024) +#define NVT_READ_LEN (2*1024) + +typedef enum { + NVTWRITE = 0, + NVTREAD = 1 +} NVT_SPI_RW; + +//---extern structures--- +extern struct nvt_ts_data *ts; + +extern char *nvt_boot_firmware_name; +extern char *nvt_mp_firmware_name; + +//---extern functions--- +int32_t CTP_SPI_READ(struct spi_device *client, uint8_t *buf, uint16_t len); +int32_t CTP_SPI_WRITE(struct spi_device *client, uint8_t *buf, uint16_t len); +void nvt_bootloader_reset(void); +void nvt_eng_reset(void); +void nvt_sw_reset(void); +void nvt_sw_reset_idle(void); +void nvt_boot_ready(void); +void nvt_bld_crc_enable(void); +void nvt_fw_crc_enable(void); +void nvt_irq_enable(bool enable); +int32_t nvt_update_firmware(char *firmware_name); +int32_t nvt_check_fw_reset_state(RST_COMPLETE_STATE check_reset_state); +int32_t nvt_get_fw_info(void); +int32_t nvt_clear_fw_status(void); +int32_t nvt_check_fw_status(void); +int32_t nvt_set_page(uint32_t addr); +int32_t nvt_write_addr(uint32_t addr, uint8_t data); +int32_t nvt_ts_suspend(struct device *dev); +int32_t nvt_ts_resume(struct device *dev); +int nvt_set_charger(uint8_t charger_on_off); +int32_t nvt_cmd_ext_store(uint8_t cmd, uint8_t subcmd); +#ifdef NVT_SET_TOUCH_STATE +int touch_set_state(int state, int panel_idx); +int check_touch_state(int *state, int panel_idx); +#endif +#if NVT_TOUCH_ESD_PROTECT +extern void nvt_esd_check_enable(uint8_t enable); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ +#ifdef NOVATECH_PEN_NOTIFIER +extern int nvt_mcu_pen_detect_set(uint8_t pen_detect); +#endif +#ifdef PALM_GESTURE +extern int nvt_palm_set(bool enabled); +#endif +#ifdef EDGE_SUPPRESSION +extern int32_t nvt_edge_reject_set(uint32_t status); +extern uint8_t nvt_edge_reject_read(void); +#endif +void release_all_touches(void); +#endif /* _LINUX_NVT_TOUCH_H */ diff --git a/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_ext_proc.c b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_ext_proc.c new file mode 100644 index 000000000000..e17bc9b141b2 --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_ext_proc.c @@ -0,0 +1,975 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 47376 $ + * $Date: 2019-07-12 09:06:29 +0800 (週五, 12 七月 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 +#include + +#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 NVT_UPDATE "nvt_update" +#define NVT_MONITOR_CTRL_FLAG "nvt_monitor_control" + +#define BUS_TRANSFER_LENGTH 256 + +#define NORMAL_MODE 0x00 +#define TEST_MODE_1 0x21 +#define TEST_MODE_2 0x22 +#define HANDSHAKING_HOST_READY 0xBB + +#define XDATA_SECTOR_SIZE 256 + +#define ENABLE_PASSIVE_PEN_MODE_CMD 0x73 +#define DISABLE_PASSIVE_PEN_MODE_CMD 0x74 +#define ENABLE_PALM_GESTURE_MODE_CMD 0x75 +#define DISABLE_PALM_GESTURE_MODE_CMD 0x76 + +#define ENABLE_DOZE_MODE_CMD 0xB7 +#define DISABLE_DOZE_MODE_CMD 0xB8 + +static uint8_t monitor_control_status = 1; + +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; +static struct proc_dir_entry *NVT_proc_fwupdate_entry; +static struct proc_dir_entry *NVT_proc_monitor_control_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(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + + //---set mode--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = mode; + CTP_SPI_WRITE(ts->client, buf, 2); + + if (mode == NORMAL_MODE) { + buf[0] = EVENT_MAP_HANDSHAKING_or_SUB_CMD_BYTE; + buf[1] = HANDSHAKING_HOST_READY; + CTP_SPI_WRITE(ts->client, 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(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_SPI_READ(ts->client, 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 + 2] = {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(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_SPI_READ(ts->client, 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(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_SPI_READ(ts->client, 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(xdata_btn_addr); + + //---read data--- + buf[0] = (xdata_btn_addr & 0xFF); + CTP_SPI_READ(ts->client, 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(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); +} +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +static const struct proc_ops nvt_fw_version_fops = { + .proc_open = nvt_fw_version_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, +#if KERNEL_VERSION(5, 15, 0) <= LINUX_VERSION_CODE + .proc_release = seq_release, +#else + .proc_release = single_release, +#endif +}; +#else +static const struct file_operations nvt_fw_version_fops = { + .owner = THIS_MODULE, + .open = nvt_fw_version_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif + +/******************************************************* +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); +} + +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +static const struct proc_ops nvt_baseline_fops = { + .proc_open = nvt_baseline_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, +#if KERNEL_VERSION(5, 15, 0) <= LINUX_VERSION_CODE + .proc_release = seq_release, +#else + .proc_release = single_release, +#endif +}; +#else +static const struct file_operations nvt_baseline_fops = { + .owner = THIS_MODULE, + .open = nvt_baseline_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif + +/******************************************************* +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); +} + +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +static const struct proc_ops nvt_raw_fops = { + .proc_open = nvt_raw_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, +#if KERNEL_VERSION(5, 15, 0) <= LINUX_VERSION_CODE + .proc_release = seq_release, +#else + .proc_release = single_release, +#endif +}; +#else +static const struct file_operations nvt_raw_fops = { + .owner = THIS_MODULE, + .open = nvt_raw_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif + +/******************************************************* +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); +} + +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +static const struct proc_ops nvt_diff_fops = { + .proc_open = nvt_diff_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, +#if KERNEL_VERSION(5, 15, 0) <= LINUX_VERSION_CODE + .proc_release = seq_release, +#else + .proc_release = single_release, +#endif +}; +#else +static const struct file_operations nvt_diff_fops = { + .owner = THIS_MODULE, + .open = nvt_diff_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif + + +/******************************************************* +Description: + Novatek touchscreen /proc/nvt_update read function. +return: + Executive outcomes. 0---succeed. +*******************************************************/ +#define FWTYPE_MP (0) +#define FWTYPE_Normal (1) +static ssize_t nvt_fwupdate_read(struct file *file, char __user *buff, size_t count, loff_t *offp) +{ + uint8_t *str = NULL; + uint8_t fwtype = FWTYPE_Normal; + int32_t ret = 0; + + NVT_LOG("++\n"); + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + + /* allocate buffer */ + str = (uint8_t *)kzalloc((count), GFP_KERNEL); + if(str == NULL) { + NVT_ERR("kzalloc for buf failed!\n"); + ret = -ENOMEM; + goto kzalloc_failed; + } + + if (copy_from_user(str, buff, count)) { + NVT_ERR("copy from user error\n"); + ret = -EFAULT; + goto out; + } + +#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 */ + + fwtype = str[0]; + + NVT_LOG("fwtype is %d\n", fwtype); + + + switch (fwtype) { + case FWTYPE_Normal: + if(nvt_boot_firmware_name) + nvt_update_firmware(nvt_boot_firmware_name); + else + nvt_update_firmware(BOOT_UPDATE_FIRMWARE_NAME); + break; + case FWTYPE_MP: + if(nvt_mp_firmware_name) + nvt_update_firmware(nvt_mp_firmware_name); + else + nvt_update_firmware(MP_UPDATE_FIRMWARE_NAME); + break; + default: + NVT_ERR("fwtype error\n"); + } + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + +out: + kfree(str); +kzalloc_failed: + return ret; +} + +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +static const struct proc_ops nvt_fwupdate_fops = { + .proc_read = nvt_fwupdate_read, + .proc_write = NULL, +}; +#else +static const struct file_operations nvt_fwupdate_fops = { + .owner = THIS_MODULE, + .read = nvt_fwupdate_read, +}; +#endif + +#ifdef EDGE_SUPPRESSION +typedef enum{ + EDGE_REJECT=5, +}CMD_OFFSET; + +static uint8_t nvt_cmd_show(int offset) +{ + uint8_t buf[3] = {0}; + int32_t ret = 0; + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + mutex_unlock(&ts->lock); + return ret; + } + + //---read cmd status--- + buf[0] = EVENT_MAP_HOST_CMD_CHECK; + buf[1] = 0xFF; + CTP_SPI_READ(ts->client, buf, 2); + + return ((buf[1]>> offset) & 0x03); + +} +#endif + +int32_t nvt_cmd_store(uint8_t u8Cmd) +{ + int i, retry = 5; + uint8_t buf[3] = {0}; + int32_t ret = 0; + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + mutex_unlock(&ts->lock); + return ret; + } + + for (i = 0; i < retry; i++) { + //---set cmd status--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = u8Cmd; + CTP_SPI_WRITE(ts->client, buf, 2); + msleep(20); + + //---read cmd status--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0xFF; + CTP_SPI_READ(ts->client, buf, 2); + if (buf[1] == 0x00) + break; + } + + if (unlikely(i == retry)) { + NVT_LOG("send Cmd 0x%02X failed, buf[1]=0x%02X\n", u8Cmd, buf[1]); + ret = -1; + } else { + NVT_LOG("send Cmd 0x%02X success, tried %d times\n", u8Cmd, i); + } + + mutex_unlock(&ts->lock); + + return ret; +} + +#ifdef NOVATECH_PEN_NOTIFIER +int nvt_mcu_pen_detect_set(uint8_t pen_detect) { + int ret = 0; + + if (pen_detect == PEN_DETECTION_PULL) { + ret = nvt_cmd_store(ENABLE_PASSIVE_PEN_MODE_CMD); + } else if (pen_detect == PEN_DETECTION_INSERT) { + ret = nvt_cmd_store(DISABLE_PASSIVE_PEN_MODE_CMD); + } + + return ret; +} +#endif + +#ifdef PALM_GESTURE +int nvt_palm_set(bool enabled) { + int ret = 0; + + if (enabled) { + ret = nvt_cmd_store(ENABLE_PALM_GESTURE_MODE_CMD); + } else if (!enabled) { + ret = nvt_cmd_store(DISABLE_PALM_GESTURE_MODE_CMD); + } + + return ret; +} +#endif + +#ifdef EDGE_SUPPRESSION +int32_t nvt_edge_reject_set(uint32_t status) { + int ret = 0; + + if(status == VERTICAL)// 0 180 + ret = nvt_cmd_store(EDGE_REJECT_VERTICLE_CMD); + else if(status == LEFT_UP) //90 + ret = nvt_cmd_store(EDGE_REJECT_LEFT_UP); + else if(status == RIGHT_UP) //270 + ret = nvt_cmd_store(EDGE_REJECT_RIGHT_UP); + else { + NVT_ERR("Invalid parameter %d!\n", status); + ret = -1; + } + + return ret; +} + +uint8_t nvt_edge_reject_read(void) { + return nvt_cmd_show(EDGE_REJECT); +} +#endif + +static int nvt_monitor_control_show(struct seq_file *sfile, void *v) +{ + if(monitor_control_status == 1) + seq_printf(sfile, "Enable Doze MODE!\n"); + else if (monitor_control_status == 0) + seq_printf(sfile, "Disable Doze MODE!\n"); + else + seq_printf(sfile, "UNKNOW MODE!\n"); + return 0; +} + +static ssize_t nvt_monitor_control_store(struct file *file, const char *buffer, size_t count, loff_t *pos) +{ + int input; + uint8_t *str = NULL; + + /* allocate buffer */ + str = (uint8_t *)kzalloc((count), GFP_KERNEL); + if(str == NULL) { + NVT_ERR("kzalloc for buf failed!\n"); + return -ENOMEM; + } + + if (copy_from_user(str, buffer, count)) { + NVT_ERR("copy from user error\n"); + kfree(str); + return -EFAULT; + } + + if (sscanf(str, "%d", &input) != 1) { + kfree(str); + return -EINVAL; + } + + monitor_control_status = input; + NVT_LOG("set monitor control: %d\n", monitor_control_status); + + if (monitor_control_status == 1) { + nvt_cmd_store(ENABLE_DOZE_MODE_CMD); + } else if (monitor_control_status == 0) { + nvt_cmd_store(DISABLE_DOZE_MODE_CMD); + } else { + NVT_ERR("Invalid parameter %d!\n", monitor_control_status); + } + + kfree(str); + + return count; +} +static int32_t nvt_monitor_control_open(struct inode *inode, struct file *file) +{ + return single_open(file, nvt_monitor_control_show, NULL); +} +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +static const struct proc_ops monitor_control_fops = { + .proc_open = nvt_monitor_control_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, +#if KERNEL_VERSION(5, 15, 0) <= LINUX_VERSION_CODE + .proc_release = seq_release, +#else + .proc_release = single_release, +#endif + .proc_write = nvt_monitor_control_store, +}; +#else +static const struct file_operations monitor_control_fops = { + .owner = THIS_MODULE, + .open = nvt_monitor_control_open, + .read = seq_read, + .write = nvt_monitor_control_store, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif + + +/******************************************************* +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); + } + + NVT_proc_fwupdate_entry = proc_create(NVT_UPDATE, 0444, NULL,&nvt_fwupdate_fops); + if (NVT_proc_fwupdate_entry == NULL) { + NVT_ERR("create proc/nvt_update Failed!\n"); + return -ENOMEM; + } else { + NVT_LOG("create proc/nvt_update Succeeded!\n"); + } + + NVT_proc_monitor_control_entry = proc_create(NVT_MONITOR_CTRL_FLAG, 0644, NULL,&monitor_control_fops); + if (NVT_proc_monitor_control_entry == NULL) { + NVT_ERR("create proc/nvt_monitor_control Failed!\n"); + return -ENOMEM; + } else { + NVT_LOG("create proc/nvt_monitor_control Succeeded!\n"); + } + 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); + } + + if (NVT_proc_fwupdate_entry != NULL) { + remove_proc_entry(NVT_UPDATE, NULL); + NVT_proc_fwupdate_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_UPDATE); + } + + if (NVT_proc_monitor_control_entry != NULL) { + remove_proc_entry(NVT_MONITOR_CTRL_FLAG, NULL); + NVT_proc_monitor_control_entry = NULL; + NVT_LOG("Removed /proc/%s\n", NVT_MONITOR_CTRL_FLAG); + } +} +#endif diff --git a/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_fw_update.c b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_fw_update.c new file mode 100644 index 000000000000..e0e5b6eab98d --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_fw_update.c @@ -0,0 +1,1109 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 43459 $ + * $Date: 2019-04-17 15:28:41 +0800 (週三, 17 四月 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 +#include + +#include "nt36xxx.h" +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +#include +#endif + +#if BOOT_UPDATE_FIRMWARE + +#define SIZE_4KB 4096 +#define FLASH_SECTOR_SIZE SIZE_4KB +#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) + +#define NVT_DUMP_PARTITION (0) +#define NVT_DUMP_PARTITION_LEN (1024) +#define NVT_DUMP_PARTITION_PATH "/data/local/tmp" + +struct TIME_TYPE start, end; +const struct firmware *fw_entry = NULL; +static size_t fw_need_write_size = 0; +static uint8_t *fwbuf = NULL; + +struct nvt_ts_bin_map { + char name[12]; + uint32_t BIN_addr; + uint32_t SRAM_addr; + uint32_t size; + uint32_t crc; +}; + +static struct nvt_ts_bin_map *bin_map; + +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 (strncmp(&fw_entry->data[i * FLASH_SECTOR_SIZE - NVT_FLASH_END_FLAG_LEN], "NVT", NVT_FLASH_END_FLAG_LEN) == 0) { + fw_need_write_size = i * FLASH_SECTOR_SIZE; + NVT_LOG("fw_need_write_size = %zu(0x%zx), NVT end flag\n", fw_need_write_size, fw_need_write_size); + return 0; + } + + /* check if there is end flag "MOD" at the end of this sector */ + if (strncmp(&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), MOD end flag\n", fw_need_write_size, fw_need_write_size); + return 0; + } + } + + NVT_ERR("end flag \"NVT\" \"MOD\" not found!\n"); + return -1; +} + +/******************************************************* +Description: + Novatek touchscreen init variable and allocate buffer +for download firmware function. + +return: + n.a. +*******************************************************/ +static int32_t nvt_download_init(void) +{ + /* allocate buffer for transfer firmware */ + //NVT_LOG("NVT_TRANSFER_LEN = 0x%06X\n", NVT_TRANSFER_LEN); + + if (fwbuf == NULL) { + fwbuf = (uint8_t *)kzalloc((NVT_TRANSFER_LEN+2), GFP_KERNEL); + if(fwbuf == NULL) { + NVT_ERR("kzalloc for fwbuf failed!\n"); + return -ENOMEM; + } + } + + 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 partition = 0; +static uint8_t ilm_dlm_num = 2; +static int32_t nvt_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; + + /* Find the header size */ + end = fwdata[0] + (fwdata[1] << 8) + (fwdata[2] << 16) + (fwdata[3] << 24); + pos = 0x30; // info section start at 0x30 offset + while (pos < end) { + info_sec_num ++; + pos += 0x10; /* each header info is 16 bytes */ + } + + /* + * Find the DLM OVLY section + * [0:3] Overlay Section Number + * [4] Overlay Info + */ + ovly_info = (fwdata[0x28] & 0x10) >> 4; + ovly_sec_num = (ovly_info) ? (fwdata[0x28] & 0x0F) : 0; + + /* + * calculate all partition number + * ilm_dlm_num (ILM & DLM) + ovly_sec_num + info_sec_num + */ + partition = 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, ilm_dlm_num, ovly_sec_num, info_sec_num, partition); + + /* allocated memory for header info */ + bin_map = (struct nvt_ts_bin_map *)kzalloc((partition+1) * sizeof(struct nvt_ts_bin_map), GFP_KERNEL); + if(bin_map == NULL) { + NVT_ERR("kzalloc for bin_map failed!\n"); + return -ENOMEM; + } + + for (list = 0; list < partition; list++) { + /* + * [1] parsing ILM & DLM header info + * BIN_addr : SRAM_addr : size (12-bytes) + * crc located at 0x18 & 0x1C + */ + if (list < ilm_dlm_num) { + 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 larger than bin size!\n", + bin_map[list].BIN_addr, bin_map[list].BIN_addr + bin_map[list].size); + return -EINVAL; + } + } //ts->hw_crc + if (list == 0) + sprintf(bin_map[list].name, "ILM"); + else if (list == 1) + sprintf(bin_map[list].name, "DLM"); + } + + /* + * [2] parsing others header info + * SRAM_addr : size : BIN_addr : crc (16-bytes) + */ + if ((list >= ilm_dlm_num) && (list < (ilm_dlm_num + info_sec_num))) { + /* others partition located at 0x30 offset */ + pos = 0x30 + (0x10 * (list - ilm_dlm_num)); + + 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 larger than bin size!\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 == 0) && (bin_map[list].size != 0)) { + sprintf(bin_map[list].name, "Header"); + } else { + sprintf(bin_map[list].name, "Info-%d", (list - ilm_dlm_num)); + } + } + + /* + * [3] parsing overlay section header info + * SRAM_addr : size : BIN_addr : crc (16-bytes) + */ + if (list >= (ilm_dlm_num + info_sec_num)) { + /* overlay info located at DLM (list = 1) start addr */ + pos = bin_map[1].BIN_addr + (0x10 * (list- ilm_dlm_num - info_sec_num)); + + 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 larger than bin size!\n", + bin_map[list].BIN_addr, bin_map[list].BIN_addr + bin_map[list].size); + return -EINVAL; + } + } //ts->hw_crc + sprintf(bin_map[list].name, "Overlay-%d", (list- ilm_dlm_num - info_sec_num)); + } + + /* BIN size error detect */ + if ((bin_map[list].BIN_addr + bin_map[list].size) > fwsize) { + 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 update_firmware_release(void) +{ + if (fw_entry) { +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW + if (ts->flash_mode == FW_PARAM_MODE) { + vfree(fw_entry->data); + fw_entry = NULL; + return; + } +#endif + release_firmware(fw_entry); + } + + fw_entry = NULL; +} + +/******************************************************* +Description: + Novatek touchscreen request update firmware function. + +return: + Executive outcomes. 0---succeed. -1,-22---failed. +*******************************************************/ +static int32_t update_firmware_request(char *filename) +{ + uint8_t retry = 0; + int32_t ret = 0; +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW + char path[TS_MMI_MAX_FULL_FW_PATH] = { 0 }; + void *data = NULL; + struct firmware *fw_tmp; + struct file *filp = NULL; + struct inode *inode; + loff_t pos = 0; + loff_t file_len = 0; +#endif + + if (NULL == filename) { + return -ENOENT; + } + + while (1) { + NVT_LOG("filename is %s\n", filename); + +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW + if (ts->flash_mode == FW_PARAM_MODE) { + NVT_LOG("Read FW data from param path: %s\n", filename); + snprintf(path, TS_MMI_MAX_FULL_FW_PATH, "%s%s", TS_MMI_FW_PARAM_PATH, filename); + filp = filp_open(path, O_RDONLY, 0); + if (IS_ERR(filp)) { + NVT_ERR("Open %s file fail!\n", path); + return -ENOENT; + } + + fw_tmp = vzalloc(sizeof(struct firmware)); + if (!fw_tmp) { + NVT_ERR("Failed to malloc (struct) firmware!\n"); + return -ENOMEM; + } + + inode = filp->f_inode; + file_len = inode->i_size; + data = (u8 *)vzalloc(file_len); + if (NULL == data) { + NVT_ERR("Failed to malloc param firmware memory!\n"); + filp_close(filp, NULL); + return -ENOMEM; + } + + ret = kernel_read(filp, data, file_len, &pos); + if (ret < 0) { + NVT_ERR("Failed to read param firmware data, ret = %d\n", ret); + filp_close(filp, NULL); + vfree(data); + vfree(fw_tmp); + fw_entry = NULL; + return -EIO; + } + fw_tmp->data = data; + fw_tmp->size = file_len; + NVT_ERR("fw file len:%d pos:%d!\n", (u32)file_len, (u32)pos); + filp_close(filp, NULL); + fw_entry = fw_tmp; + } else +#endif + { + ret = request_firmware(&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_get_fw_need_write_size(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 + 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)); + ret = -ENOEXEC; + goto invalid; + } + + /* BIN Header Parser */ + ret = nvt_bin_header_parser(fw_entry->data, fw_entry->size); + if (ret) { + NVT_ERR("bin header parser failed\n"); + goto invalid; + } else { + break; + } + +invalid: + update_firmware_release(); + if (!IS_ERR_OR_NULL(bin_map)) { + kfree(bin_map); + bin_map = NULL; + } + +request_fail: + retry++; + msleep(100); + if(unlikely(retry > 2)) { + NVT_ERR("error, retry=%d\n", retry); + break; + } + } + + return ret; +} + +#if NVT_DUMP_PARTITION +/******************************************************* +Description: + Novatek touchscreen dump flash partition function. + +return: + n.a. +*******************************************************/ +loff_t file_offset = 0; +static int32_t nvt_read_ram_and_save_file(uint32_t addr, uint16_t len, char *name) +{ + char file[256] = ""; + uint8_t *fbufp = NULL; + int32_t ret = 0; + struct file *fp = NULL; + mm_segment_t org_fs; + + sprintf(file, "%s/dump_%s.bin", NVT_DUMP_PARTITION_PATH, name); + NVT_LOG("Dump [%s] from 0x%08X to 0x%08X\n", file, addr, addr+len); + + fbufp = (uint8_t *)kzalloc(len+1, GFP_KERNEL); + if(fbufp == NULL) { + NVT_ERR("kzalloc for fbufp failed!\n"); + ret = -ENOMEM; + goto alloc_buf_fail; + } + + org_fs = get_fs(); + set_fs(KERNEL_DS); + fp = filp_open(file, O_RDWR | O_CREAT, 0644); + if (fp == NULL || IS_ERR(fp)) { + ret = -ENOMEM; + NVT_ERR("open file failed\n"); + goto open_file_fail; + } + + /* SPI read */ + //---set xdata index to addr--- + nvt_set_page(addr); + + fbufp[0] = addr & 0x7F; //offset + CTP_SPI_READ(ts->client, fbufp, len+1); + + /* Write to file */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + ret = kernel_write(fp, (char __user *)fbufp+1, len, &file_offset); +#else + ret = vfs_write(fp, (char __user *)fbufp+1, len, &file_offset); +#endif + if (ret != len) { + NVT_ERR("write file failed\n"); + goto open_file_fail; + } else { + ret = 0; + } + +open_file_fail: + set_fs(org_fs); + if (!IS_ERR_OR_NULL(fp)) { + filp_close(fp, NULL); + fp = NULL; + } + + if (!IS_ERR_OR_NULL(fbufp)) { + kfree(fbufp); + fbufp = NULL; + } +alloc_buf_fail: + + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen nvt_dump_partition function to dump + each partition for debug. + +return: + n.a. +*******************************************************/ +static int32_t nvt_dump_partition(void) +{ + uint32_t list = 0; + char *name; + uint32_t SRAM_addr, size; + uint32_t i = 0; + uint16_t len = 0; + int32_t count = 0; + int32_t ret = 0; + + if (NVT_DUMP_PARTITION_LEN >= sizeof(ts->rbuf)) { + NVT_ERR("dump len %d is larger than buffer size %ld\n", + NVT_DUMP_PARTITION_LEN, sizeof(ts->rbuf)); + return -EINVAL; + } else if (NVT_DUMP_PARTITION_LEN >= NVT_TRANSFER_LEN) { + NVT_ERR("dump len %d is larger than NVT_TRANSFER_LEN\n", NVT_DUMP_PARTITION_LEN); + return -EINVAL; + } + + if (bin_map == NULL) { + NVT_ERR("bin_map is NULL\n"); + return -ENOMEM; + } + + memset(fwbuf, 0, (NVT_DUMP_PARTITION_LEN+1)); + + for (list = 0; list < partition; list++) { + /* initialize variable */ + SRAM_addr = bin_map[list].SRAM_addr; + size = bin_map[list].size; + name = bin_map[list].name; + + /* ignore reserved partition (Reserved Partition size is zero) */ + if (!size) + continue; + else + size = size +1; + + /* write data to SRAM */ + if (size % NVT_DUMP_PARTITION_LEN) + count = (size / NVT_DUMP_PARTITION_LEN) + 1; + else + count = (size / NVT_DUMP_PARTITION_LEN); + + for (i = 0 ; i < count ; i++) { + len = (size < NVT_DUMP_PARTITION_LEN) ? size : NVT_DUMP_PARTITION_LEN; + + /* dump for debug download firmware */ + ret = nvt_read_ram_and_save_file(SRAM_addr, len, name); + if (ret < 0) { + NVT_ERR("nvt_read_ram_and_save_file failed, ret = %d\n", ret); + goto out; + } + + SRAM_addr += NVT_DUMP_PARTITION_LEN; + size -= NVT_DUMP_PARTITION_LEN; + } + + file_offset = 0; + } + +out: + return ret; +} +#endif /* NVT_DUMP_PARTITION */ + +/******************************************************* +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_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_TRANSFER_LEN) + count = (size / NVT_TRANSFER_LEN) + 1; + else + count = (size / NVT_TRANSFER_LEN); + + for (i = 0 ; i < count ; i++) { + len = (size < NVT_TRANSFER_LEN) ? size : NVT_TRANSFER_LEN; + + //---set xdata index to start address of SRAM--- + ret = nvt_set_page(SRAM_addr); + if (ret) { + NVT_ERR("set page failed, ret = %d\n", ret); + return ret; + } + + //---write data into SRAM--- + fwbuf[0] = SRAM_addr & 0x7F; //offset + memcpy(fwbuf+1, &fwdata[BIN_addr], len); //payload + ret = CTP_SPI_WRITE(ts->client, fwbuf, len+1); + if (ret) { + NVT_ERR("write to sram failed, ret = %d\n", ret); + return ret; + } + + SRAM_addr += NVT_TRANSFER_LEN; + BIN_addr += NVT_TRANSFER_LEN; + size -= NVT_TRANSFER_LEN; + } + + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen nvt_write_firmware function to write +firmware into each partition. + +return: + n.a. +*******************************************************/ +static int32_t nvt_write_firmware(const u8 *fwdata, size_t fwsize) +{ + uint32_t list = 0; + uint32_t BIN_addr, SRAM_addr, size; + int32_t ret = 0; + + memset(fwbuf, 0, (NVT_TRANSFER_LEN+2)); + + for (list = 0; list < partition; list++) { + /* initialize variable */ + SRAM_addr = bin_map[list].SRAM_addr; + size = bin_map[list].size; + BIN_addr = bin_map[list].BIN_addr; + +// 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_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_check_fw_checksum(void) +{ + uint32_t fw_checksum = 0; + uint32_t len = partition*4; + uint32_t list = 0; + int32_t ret = 0; + + memset(fwbuf, 0, (len+1)); + + //---set xdata index to checksum--- + nvt_set_page(ts->mmap->R_ILM_CHECKSUM_ADDR); + + /* read checksum */ + fwbuf[0] = (ts->mmap->R_ILM_CHECKSUM_ADDR) & 0x7F; + ret = CTP_SPI_READ(ts->client, fwbuf, len+1); + if (ret) { + NVT_ERR("Read fw checksum failed\n"); + return ret; + } + + /* + * Compare each checksum from fw + * ILM + DLM + Overlay + Info + * ilm_dlm_num (ILM & DLM) + ovly_sec_num + info_sec_num + */ + for (list = 0; list < partition; list++) { + fw_checksum = byte_to_word(&fwbuf[1+list*4]); + + /* ignore reserved partition (Reserved Partition size is zero) */ + if(!bin_map[list].size) + continue; + + if (bin_map[list].crc != fw_checksum) { + NVT_ERR("[%d] BIN_checksum=0x%08X, FW_checksum=0x%08X\n", + list, 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_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) +{ + /* write destination address */ + nvt_set_page(DES_ADDR); + fwbuf[0] = DES_ADDR & 0x7F; + fwbuf[1] = (SRAM_ADDR) & 0xFF; + fwbuf[2] = (SRAM_ADDR >> 8) & 0xFF; + fwbuf[3] = (SRAM_ADDR >> 16) & 0xFF; + CTP_SPI_WRITE(ts->client, fwbuf, 4); + + /* write length */ + //nvt_set_page(LENGTH_ADDR); + fwbuf[0] = LENGTH_ADDR & 0x7F; + fwbuf[1] = (size) & 0xFF; + fwbuf[2] = (size >> 8) & 0xFF; + fwbuf[3] = (size >> 16) & 0x01; + if (ts->hw_crc == 1) { + CTP_SPI_WRITE(ts->client, fwbuf, 3); + } else if (ts->hw_crc > 1) { + CTP_SPI_WRITE(ts->client, fwbuf, 4); + } + + /* write golden dlm checksum */ + //nvt_set_page(G_CHECKSUM_ADDR); + fwbuf[0] = G_CHECKSUM_ADDR & 0x7F; + fwbuf[1] = (crc) & 0xFF; + fwbuf[2] = (crc >> 8) & 0xFF; + fwbuf[3] = (crc >> 16) & 0xFF; + fwbuf[4] = (crc >> 24) & 0xFF; + CTP_SPI_WRITE(ts->client, fwbuf, 5); + + return; +} + +/******************************************************* +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_set_bld_hw_crc(void) +{ + /* [0] ILM */ + /* write register bank */ + nvt_set_bld_crc_bank(ts->mmap->ILM_DES_ADDR, bin_map[0].SRAM_addr, + ts->mmap->ILM_LENGTH_ADDR, bin_map[0].size, + ts->mmap->G_ILM_CHECKSUM_ADDR, bin_map[0].crc); + + /* [1] DLM */ + /* write register bank */ + nvt_set_bld_crc_bank(ts->mmap->DLM_DES_ADDR, bin_map[1].SRAM_addr, + ts->mmap->DLM_LENGTH_ADDR, bin_map[1].size, + ts->mmap->G_DLM_CHECKSUM_ADDR, 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_read_bld_hw_crc(void) +{ + uint8_t buf[8] = {0}; + uint32_t g_crc = 0, r_crc = 0; + + /* CRC Flag */ + nvt_set_page(ts->mmap->BLD_ILM_DLM_CRC_ADDR); + buf[0] = ts->mmap->BLD_ILM_DLM_CRC_ADDR & 0x7F; + buf[1] = 0x00; + CTP_SPI_READ(ts->client, 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_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; + CTP_SPI_READ(ts->client, buf, 5); + g_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24); + + nvt_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; + CTP_SPI_READ(ts->client, 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", + bin_map[0].crc, g_crc, r_crc); + + /* DLM CRC */ + nvt_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; + CTP_SPI_READ(ts->client, buf, 5); + g_crc = buf[1] | (buf[2] << 8) | (buf[3] << 16) | (buf[4] << 24); + + nvt_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; + CTP_SPI_READ(ts->client, 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", + bin_map[1].crc, g_crc, r_crc); + + return; +} + +/******************************************************* +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_download_firmware_hw_crc(void) +{ + uint8_t retry = 0; + int32_t ret = 0; + + GET_TIME_OF_DAY(&start); + + while (1) { + /* bootloader reset to reset MCU */ + nvt_bootloader_reset(); + + /* Start to write firmware process */ + ret = nvt_write_firmware(fw_entry->data, 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 ilm & dlm reg bank */ + nvt_set_bld_hw_crc(); + + /* enable hw bld crc function */ + nvt_bld_crc_enable(); + + /* clear fw reset status & enable fw crc check */ + nvt_fw_crc_enable(); + + /* Set Boot Ready Bit */ + nvt_boot_ready(); + + ret = nvt_check_fw_reset_state(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_read_bld_hw_crc(); + break; + } + } + + GET_TIME_OF_DAY(&end); + + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen Download_Firmware function. It's +complete download firmware flow. + +return: + n.a. +*******************************************************/ +static int32_t nvt_download_firmware(void) +{ + uint8_t retry = 0; + int32_t ret = 0; + + GET_TIME_OF_DAY(&start); + + while (1) { + /* + * Send eng reset cmd before download FW + * Keep TP_RESX low when send eng reset cmd + */ +#if NVT_TOUCH_SUPPORT_HW_RST + gpio_set_value(ts->reset_gpio, 0); + mdelay(1); //wait 1ms +#endif + nvt_eng_reset(); +#if NVT_TOUCH_SUPPORT_HW_RST + gpio_set_value(ts->reset_gpio, 1); + mdelay(10); //wait tRT2BRST after TP_RST +#endif + nvt_bootloader_reset(); + + /* clear fw reset status */ + nvt_write_addr(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_RESET_COMPLETE, 0x00); + + /* Start to write firmware process */ + ret = nvt_write_firmware(fw_entry->data, 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_boot_ready(); + + ret = nvt_check_fw_reset_state(RESET_STATE_INIT); + if (ret) { + NVT_ERR("nvt_check_fw_reset_state failed. (%d)\n", ret); + goto fail; + } + + /* check fw checksum result */ + ret = nvt_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; + } + } + + GET_TIME_OF_DAY(&end); + + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen update firmware main function. + +return: + n.a. +*******************************************************/ +int32_t nvt_update_firmware(char *firmware_name) +{ + int32_t ret = 0; + + atomic_set(&ts->loading_fw, 1); + // request bin file in "/etc/firmware" + ret = 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_download_init(); + if (ret) { + NVT_ERR("Download Init failed. (%d)\n", ret); + goto download_fail; + } + + /* download firmware process */ + if (ts->hw_crc) + ret = nvt_download_firmware_hw_crc(); + else + ret = nvt_download_firmware(); + if (ret) { + NVT_ERR("Download Firmware failed. (%d)\n", ret); + goto download_fail; + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + NVT_LOG("Update firmware success! <%lld us>\n", + (end.tv_sec - start.tv_sec)*1000000L + (end.tv_nsec - start.tv_nsec)/1000); +#else + NVT_LOG("Update firmware success! <%ld us>\n", + (end.tv_sec - start.tv_sec)*1000000L + (end.tv_usec - start.tv_usec)); +#endif + + /* Get FW Info */ + ret = nvt_get_fw_info(); + if (ret) { + NVT_ERR("nvt_get_fw_info failed. (%d)\n", ret); + } + +download_fail: + if (!IS_ERR_OR_NULL(bin_map)) { + kfree(bin_map); + bin_map = NULL; + } + + update_firmware_release(); +request_firmware_fail: + + atomic_set(&ts->loading_fw, 0); + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen update firmware when booting + function. + +return: + n.a. +*******************************************************/ +void Boot_Update_Firmware(struct work_struct *work) +{ + int32_t ret = 0; + + mutex_lock(&ts->lock); + + if(nvt_boot_firmware_name) + ret = nvt_update_firmware(nvt_boot_firmware_name); + else + ret = nvt_update_firmware(BOOT_UPDATE_FIRMWARE_NAME); + if (ret) { + NVT_ERR("download firmware failed, ignore check fw state\n"); + } else { + nvt_check_fw_reset_state(RESET_STATE_REK); + } + + mutex_unlock(&ts->lock); + + if (ts->charger_detection) { + queue_work(ts->charger_detection->nvt_charger_notify_wq, &ts->charger_detection->charger_notify_work); + } + +#ifdef NOVATECH_PEN_NOTIFIER + if(!ts->fw_ready_flag) + ts->fw_ready_flag = true; + nvt_mcu_pen_detect_set(ts->nvt_pen_detect_flag); +#endif +#ifdef PALM_GESTURE + nvt_palm_set(ts->palm_enabled); +#endif +#ifdef EDGE_SUPPRESSION + ts->edge_reject_state = VERTICAL; +#endif +} +#endif /* BOOT_UPDATE_FIRMWARE */ diff --git a/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mem_map.h b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mem_map.h new file mode 100644 index 000000000000..1c2e0a164d30 --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mem_map.h @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 43248 $ + * $Date: 2019-04-12 11:06:27 +0800 (週五, 12 四月 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_V1 0x3F004 +#define CHIP_VER_TRIM_ADDR_V0 0x1F64E + +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; + /* Phase 2 Host Download */ + uint32_t BOOT_RDY_ADDR; + uint32_t POR_CD_ADDR; + /* 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_ts_hw_info { + uint8_t carrier_system; + uint8_t hw_crc; +}; + +static const struct nvt_ts_mem_map NT36528_memory_map = { + .EVENT_BUF_ADDR = 0x22D00, + .RAW_PIPE0_ADDR = 0x24000, + .RAW_PIPE1_ADDR = 0x24000, + .BASELINE_ADDR = 0x21880, + .BASELINE_BTN_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x20B00, + .DIFF_PIPE1_ADDR = 0x24B00, + .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_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, + /* 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_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, + /* 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_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, + /* 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_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, + /* Phase 2 Host Download */ + .BOOT_RDY_ADDR = 0x1F141, + .POR_CD_ADDR = 0x1F61C, + /* BLD CRC */ + .R_ILM_CHECKSUM_ADDR = 0x1BF00, +}; + +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, + /* Phase 2 Host Download */ + .BOOT_RDY_ADDR = 0x1F141, + .POR_CD_ADDR = 0x1F61C, + /* BLD CRC */ + .R_ILM_CHECKSUM_ADDR = 0x1BF00, +}; + +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 NT36528_hw_info = { + .carrier_system = 0, + .hw_crc = 2, +}; + +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 { + char trim_id[10]; + 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[] = { + {.trim_id = "NT36528",.id = {0xFF, 0xFF, 0xFF, 0x28, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36528_memory_map, .hwinfo = &NT36528_hw_info}, + {.trim_id = "NT36675",.id = {0x0D, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.trim_id = "NT36675",.id = {0x20, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.trim_id = "NT36675",.id = {0x00, 0xFF, 0xFF, 0x80, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.trim_id = "NT36525C",.id = {0x0C, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.trim_id = "NT36675",.id = {0x0E, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.trim_id = "NT36672C",.id = {0x0C, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.trim_id = "NT36526",.id = {0xFF, 0xFF, 0xFF, 0x26, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36526_memory_map, .hwinfo = &NT36526_hw_info}, + {.trim_id = "NT36675",.id = {0xFF, 0xFF, 0xFF, 0x75, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36675_memory_map, .hwinfo = &NT36675_hw_info}, + {.trim_id = "NT36672A",.id = {0x0B, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.trim_id = "NT36672A",.id = {0x0B, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.trim_id = "NT36525B",.id = {0x0B, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.trim_id = "NT36672A",.id = {0x0A, 0xFF, 0xFF, 0x72, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.trim_id = "NT36672A",.id = {0x0A, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.trim_id = "NT36672A",.id = {0x0A, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.trim_id = "NT36672A",.id = {0x0A, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.trim_id = "NT36672A",.id = {0x0B, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.trim_id = "NT36672A",.id = {0x0A, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .hwinfo = &NT36672A_hw_info}, + {.trim_id = "NT36772",.id = {0x55, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.trim_id = "NT36772",.id = {0x55, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.trim_id = "NT36772",.id = {0xAA, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.trim_id = "NT36772",.id = {0xAA, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.trim_id = "NT36772",.id = {0xFF, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.trim_id = "NT36772",.id = {0xFF, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.trim_id = "NT36772",.id = {0xFF, 0xFF, 0xFF, 0x70, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.trim_id = "NT36772",.id = {0xFF, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .hwinfo = &NT36772_hw_info}, + {.trim_id = "NT36525",.id = {0xFF, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36525_memory_map, .hwinfo = &NT36525_hw_info}, + {.trim_id = "NT36676F",.id = {0xFF, 0xFF, 0xFF, 0x76, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36676F_memory_map, .hwinfo = &NT36676F_hw_info} +}; diff --git a/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mmi_class.c b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mmi_class.c new file mode 100644 index 000000000000..174ea289b0bf --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mmi_class.c @@ -0,0 +1,790 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include +#include "nt36xxx.h" + +#define GET_TS_DATA(dev) { \ + ts_data = dev_get_drvdata(dev); \ + if (!ts_data) { \ + NVT_ERR("Failed to get driver data"); \ + return -ENODEV; \ + } \ +} + +static ssize_t nvt_mmi_interpolation_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t nvt_mmi_interpolation_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t nvt_mmi_first_filter_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t nvt_mmi_first_filter_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t nvt_mmi_jitter_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t nvt_mmi_jitter_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t nvt_mmi_edge_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t nvt_mmi_edge_show(struct device *dev, + struct device_attribute *attr, char *buf); +#ifdef NVT_TOUCH_LAST_TIME +static ssize_t nvt_mmi_timestamp_show(struct device *dev, + struct device_attribute *attr, char *buf); +#endif + +static DEVICE_ATTR(interpolation, (S_IRUGO | S_IWUSR | S_IWGRP), + nvt_mmi_interpolation_show, nvt_mmi_interpolation_store); +static DEVICE_ATTR(first_filter, (S_IRUGO | S_IWUSR | S_IWGRP), + nvt_mmi_first_filter_show, nvt_mmi_first_filter_store); +static DEVICE_ATTR(jitter, (S_IRUGO | S_IWUSR | S_IWGRP), + nvt_mmi_jitter_show, nvt_mmi_jitter_store); +static DEVICE_ATTR(edge, (S_IRUGO | S_IWUSR | S_IWGRP), + nvt_mmi_edge_show, nvt_mmi_edge_store); +#ifdef NVT_TOUCH_LAST_TIME +static DEVICE_ATTR(timestamp, S_IRUGO, nvt_mmi_timestamp_show, NULL); +#endif + +#define MAX_ATTRS_ENTRIES 10 +#define UI_FEATURE_ENABLE 1 +#define UI_FEATURE_DISABLE 0 +#define CMD_ON 0x71 +#define CMD_OFF 0x72 +#define ROTATE_0 0 +#define ROTATE_90 1 +#define ROTATE_180 2 +#define ROTATE_270 3 +#define DEFAULT_MODE 0 +#define BIG_MODE 1 +#define SMALL_MODE 2 +#define CLOSE_MODE 3 +#define DEFAULT_EDGE 0 +#define BIG_EDGE 1 +#define SMALL_EDGE 2 +#define CLOSE_EDGE 3 + +#define ADD_ATTR(name) { \ + if (idx < MAX_ATTRS_ENTRIES) { \ + dev_info(dev, "%s: [%d] adding %p\n", __func__, idx, &dev_attr_##name.attr); \ + ext_attributes[idx] = &dev_attr_##name.attr; \ + idx++; \ + } else { \ + dev_err(dev, "%s: cannot add attribute '%s'\n", __func__, #name); \ + } \ +} + +static struct attribute *ext_attributes[MAX_ATTRS_ENTRIES]; +static struct attribute_group ext_attr_group = { + .attrs = ext_attributes, +}; + +#ifdef PALM_GESTURE +static ssize_t nvt_palm_settings_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d", ts->palm_enabled); +} + +static ssize_t nvt_palm_settings_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) + +{ + int value; + int err = 0; + + if (count > 2) + return -EINVAL; + + if (sscanf(buf, "%d", &value) != 1) + return -EINVAL; + + err = count; + + switch (value) { + case 0: + ts->palm_enabled = false; + break; + case 1: + ts->palm_enabled = true; + break; + default: + err = -EINVAL; + ts->palm_enabled = false; + NVT_ERR("Invalid Value! %d\n", value); + break; + } + + nvt_palm_set(ts->palm_enabled); + + return err; +} + +static DEVICE_ATTR(palm_settings, S_IRUGO | S_IWUSR | S_IWGRP, nvt_palm_settings_show, nvt_palm_settings_store); +#endif + +static int nvt_mmi_extend_attribute_group(struct device *dev, struct attribute_group **group) +{ + int idx = 0; + + if (ts->interpolation_ctrl) + ADD_ATTR(interpolation); + + if (ts->jitter_ctrl) + ADD_ATTR(jitter); + + if (ts->first_filter_ctrl) + ADD_ATTR(first_filter); + + if (ts->edge_ctrl) + ADD_ATTR(edge); + +#ifdef PALM_GESTURE + ADD_ATTR(palm_settings); +#endif + +#ifdef NVT_TOUCH_LAST_TIME + ADD_ATTR(timestamp); +#endif + + if (idx) { + ext_attributes[idx] = NULL; + *group = &ext_attr_group; + } else + *group = NULL; + + return 0; +} + +int32_t nvt_cmd_write(uint8_t u8Cmd, uint8_t u8subCmd, uint8_t u8subCmd2, uint16_t len) +{ + int i, retry = 5; + uint8_t buf[8] = {0}; + int32_t ret = 0; + + if (mutex_lock_interruptible(&ts->lock)) { + return -ERESTARTSYS; + } + + //---set xdata index to EVENT BUF ADDR--- + ret = nvt_set_page(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + mutex_unlock(&ts->lock); + return ret; + } + + for (i = 0; i < retry; i++) { + //---set cmd status--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = u8Cmd; + buf[2] = u8subCmd; + buf[3] = u8subCmd2; + CTP_SPI_WRITE(ts->client, buf, len+1); + msleep(20); + + //---read cmd status--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0xFF; + CTP_SPI_READ(ts->client, buf, 2); + if (buf[1] == 0x00) + break; + } + + if (unlikely(i == retry)) { + NVT_LOG("send Cmd 0x%02X 0x%02X 0x%02X failed, buf[1]=0x%02X\n", u8Cmd, u8subCmd, u8subCmd2, buf[1]); + ret = -1; + } else { + NVT_LOG("send Cmd 0x%02X 0x%02X 0x%02X success, tried %d times\n", u8Cmd, u8subCmd, u8subCmd2, i); + } + + mutex_unlock(&ts->lock); + + return ret; +} + +static ssize_t nvt_mmi_interpolation_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + uint8_t mode = 0; + uint8_t cmd = 0; + + ret = kstrtou8(buf, 0, &mode); + if (ret < 0) { + NVT_ERR("Failed to convert value.\n"); + return -EINVAL; + } + NVT_LOG("mode=%d\n", mode); + + if (UI_FEATURE_ENABLE == mode) + cmd = CMD_ON; + else + cmd = CMD_OFF; + + if (ts->interpolation_cmd[0] != cmd) { + ts->interpolation_cmd[0] = cmd; + nvt_cmd_write(ts->interpolation_cmd[0], ts->interpolation_cmd[1], 0, 2); + } + + return size; +} + +static ssize_t nvt_mmi_interpolation_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "0x%02x\n", ts->interpolation_cmd[0]); +} + +static ssize_t nvt_mmi_jitter_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + uint8_t mode = 0; + uint8_t cmd = 0; + + ret = kstrtou8(buf, 0, &mode); + if (ret < 0) { + NVT_ERR("Failed to convert value.\n"); + return -EINVAL; + } + NVT_LOG("mode=%d\n", mode); + + if (UI_FEATURE_ENABLE == mode) + cmd = CMD_ON; + else + cmd = CMD_OFF; + + if (ts->jitter_cmd[0] != cmd) { + ts->jitter_cmd[0] = cmd; + nvt_cmd_write(ts->jitter_cmd[0], ts->jitter_cmd[1], 0, 2); + } + + return size; +} + +static ssize_t nvt_mmi_jitter_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "0x%02x\n", ts->jitter_cmd[0]); +} + +static ssize_t nvt_mmi_first_filter_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + uint8_t mode = 0; + uint8_t cmd = 0; + + ret = kstrtou8(buf, 0, &mode); + if (ret < 0) { + NVT_ERR("Failed to convert value.\n"); + return -EINVAL; + } + NVT_LOG("mode=%d\n", mode); + + if (UI_FEATURE_ENABLE == mode) + cmd = CMD_ON; + else + cmd = CMD_OFF; + + if (ts->first_filter_cmd[0] != cmd) { + ts->first_filter_cmd[0] = cmd; + nvt_cmd_write(ts->first_filter_cmd[0], ts->first_filter_cmd[1], 0, 2); + } + + return size; +} + +static ssize_t nvt_mmi_first_filter_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "0x%02x\n", ts->first_filter_cmd[0]); +} + +static ssize_t nvt_mmi_edge_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret = 0; + unsigned int args[2] = { 0 }; + uint8_t mode = 0; + uint8_t rotate = 0; + + ret = sscanf(buf, "%d %d", &args[0], &args[1]); + if (ret < 2) + return -EINVAL; + NVT_LOG("mode=%d, rotate=%d\n", args[0], args[1]); + + if (DEFAULT_MODE == args[0]) { + ts->edge_cmd[0] = CMD_OFF; + ts->edge_cmd[2] = DEFAULT_EDGE; + nvt_cmd_write(ts->edge_cmd[0], ts->edge_cmd[1], 0, 2); + return size; + } else if (BIG_MODE == args[0]) + mode = BIG_EDGE; + else if (SMALL_MODE == args[0]) + mode = SMALL_EDGE; + else if (CLOSE_MODE == args[0]) + mode = CLOSE_EDGE; + else { + NVT_ERR("Invalid mode %d!\n", args[0]); + return size; + } + + if (ts->edge_cmd[2] != mode) { + ts->edge_cmd[0] = CMD_ON; + ts->edge_cmd[2] = mode; + nvt_cmd_write(ts->edge_cmd[0], ts->edge_cmd[1], ts->edge_cmd[2], 3); + } + + if (ROTATE_0 == args[1]) + rotate = EDGE_REJECT_VERTICLE_CMD; + else if (ROTATE_90 == args[1]) + rotate = EDGE_REJECT_LEFT_UP; + else if (ROTATE_270 == args[1]) + rotate = EDGE_REJECT_RIGHT_UP; + else { + NVT_ERR("Invalid rotate %d!\n", args[1]); + return size; + } + + if (ts->rotate_cmd!= rotate) { + ts->rotate_cmd = rotate; + nvt_cmd_write(ts->rotate_cmd, 0, 0, 1); + } + + return size; +} + +static ssize_t nvt_mmi_edge_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "0x%02x 0x%02x\n", ts->edge_cmd[2], ts->rotate_cmd); +} + +#ifdef NVT_TOUCH_LAST_TIME +static ssize_t nvt_mmi_timestamp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ktime_t last_ktime; + struct timespec64 last_ts; + + mutex_lock(&ts->lock); + last_ktime = ts->last_event_time; + ts->last_event_time = 0; + mutex_unlock(&ts->lock); + + last_ts = ktime_to_timespec64(last_ktime); + + return scnprintf(buf, PAGE_SIZE, "%lld.%ld\n", last_ts.tv_sec, last_ts.tv_nsec); +} +#endif + +static int nvt_mmi_methods_get_vendor(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", "novatek_ts"); +} + +static int nvt_mmi_methods_get_productinfo(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_INFO_LEN, "%s", ts->product_id); +} + +static int nvt_mmi_methods_get_build_id(struct device *dev, void *cdata) +{ + struct nvt_ts_data *ts_data; + int buildid; + + GET_TS_DATA(dev); + buildid = (ts->nvt_pid ? ts->nvt_pid : ts->config_id); + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%04x", buildid); +} + +/* Read fw_version, date_Y, date_M, date_D */ +static int nvt_mmi_methods_get_config_id(struct device *dev, void *cdata) +{ + struct nvt_ts_data *ts_data; + uint8_t tmp_buf[8] = {0}; + uint8_t fw_version = 0; + uint8_t date_Y = 0; + uint8_t date_M = 0; + uint8_t date_D = 0; + + GET_TS_DATA(dev); + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(ts_data->mmap->EVENT_BUF_ADDR | EVENT_MAP_FWINFO); + + + tmp_buf[0] = EVENT_MAP_FWINFO; + CTP_SPI_READ(ts_data->client, tmp_buf, 2); + fw_version = tmp_buf[1]; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(ts_data->mmap->EVENT_BUF_ADDR | EVENT_MAP_PROJECTID); + tmp_buf[0] = EVENT_MAP_PROJECTID; + CTP_SPI_READ(ts_data->client, tmp_buf, 6); + date_Y = tmp_buf[3]; + date_M = tmp_buf[4]; + date_D = tmp_buf[5]; + nvt_set_page(ts_data->mmap->EVENT_BUF_ADDR); + + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%02d%02d%02d%02x", + date_Y, date_M, date_D, fw_version); +} + +static int nvt_mmi_methods_get_bus_type(struct device *dev, void *idata) +{ + struct nvt_ts_data *ts_data; + + + GET_TS_DATA(dev); + + TO_INT(idata) = ts->input_dev->id.bustype == BUS_SPI ? + TOUCHSCREEN_MMI_BUS_TYPE_SPI : TOUCHSCREEN_MMI_BUS_TYPE_I2C; + return 0; +} + +static int nvt_mmi_methods_get_irq_status(struct device *dev, void *idata) +{ + struct nvt_ts_data *ts_data; + + GET_TS_DATA(dev); + TO_INT(idata) = gpio_get_value(ts_data->irq_gpio); + return 0; +} + +static int nvt_mmi_methods_get_drv_irq(struct device *dev, void *idata) +{ + struct nvt_ts_data *ts_data; + + GET_TS_DATA(dev); + TO_INT(idata) = ts_data->irq_enabled ? 1 : 0; + return 0; +} + +static int nvt_mmi_methods_get_poweron(struct device *dev, void *idata) +{ + TO_INT(idata) = 1; + + return 0; +} + +static int nvt_mmi_methods_get_flashprog(struct device *dev, void *idata) +{ + struct nvt_ts_data *ts_data; + + GET_TS_DATA(dev); + TO_INT(idata) = atomic_read(&ts_data->loading_fw); + + return 0; +} + +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW +static int nvt_mmi_get_flash_mode(struct device *dev, void *idata) +{ + struct nvt_ts_data *ts_data; + + GET_TS_DATA(dev); + TO_INT(idata) = ts_data->flash_mode; + + return 0; +} + +static int nvt_mmi_flash_mode(struct device *dev, int mode) +{ + struct nvt_ts_data *ts_data; + + GET_TS_DATA(dev); + ts_data->flash_mode = mode; + + return 0; +} +#endif + +static int nvt_mmi_methods_drv_irq(struct device *dev, int state) +{ + int ret = 0; + + if (state == 1) + nvt_irq_enable(true); + else if (state == 0) { + nvt_irq_enable(false); + } else { + dev_err(dev, "%s: invalid value\n", __func__); + ret = -EINVAL; + } + + return ret; +} + +static int nvt_mmi_charger_mode(struct device *dev, int mode) +{ + if (mutex_lock_interruptible(&ts->lock)) { + NVT_ERR("Failed to lock in mutex_lock_interruptible(&ts->lock).\n"); + return -EINVAL; + } + + nvt_set_charger(mode); + NVT_LOG("Charger mode %s!\n", !!mode ? "Enabled" : "Disabled"); + mutex_unlock(&ts->lock); + + return 0; +} + +static int nvt_mmi_methods_reset(struct device *dev, int type) +{ + nvt_sw_reset(); + + return 0; +} + +static int nvt_mmi_firmware_update(struct device *dev, char *fwname) +{ + struct nvt_ts_data *ts_data; + + GET_TS_DATA(dev); + + mutex_lock(&ts->lock); + if (strncmp(fwname, "Default", sizeof("Default"))) { + snprintf(nvt_boot_firmware_name, NVT_FILE_NAME_LENGTH, "%s", fwname); + snprintf(nvt_mp_firmware_name, NVT_FILE_NAME_LENGTH, "mp-%s", fwname); + } + nvt_update_firmware(nvt_boot_firmware_name); + mutex_unlock(&ts->lock); + +#ifdef NOVATECH_PEN_NOTIFIER + if(!ts->fw_ready_flag) + ts->fw_ready_flag = true; + nvt_mcu_pen_detect_set(ts->nvt_pen_detect_flag); +#endif + + return 0; +} + +static int nvt_mmi_panel_state(struct device *dev, + enum ts_mmi_pm_mode from, enum ts_mmi_pm_mode to) +{ + struct nvt_ts_data *ts_data; +#if defined(CONFIG_BOARD_USES_DOUBLE_TAP_CTRL) + static uint8_t gesture_cmd = 0x00; + unsigned char gesture_type = 0; +#endif + + GET_TS_DATA(dev); + dev_dbg(dev, "%s: panel state change: %d->%d\n", __func__, from, to); + NVT_LOG("panel state change: %d->%d\n", from, to); + mutex_lock(&ts->lock); + switch (to) { + case TS_MMI_PM_GESTURE: +#ifdef NVT_SET_TOUCH_STATE + ts->gesture_enabled = true; + touch_set_state(TS_MMI_PM_GESTURE, TOUCH_PANEL_IDX_PRIMARY); +#else + ts->gesture_enabled = false; +#endif + +#if defined(CONFIG_BOARD_USES_DOUBLE_TAP_CTRL) + if (ts_data->imports && ts_data->imports->get_gesture_type) { + ts_data->imports->get_gesture_type(dev, &gesture_type); + } + + if (gesture_type & TS_MMI_GESTURE_SINGLE) { + gesture_cmd |= 0x02; + NVT_LOG("enable single gesture mode cmd 0x%04x\n", gesture_cmd); + } + + if (gesture_type & TS_MMI_GESTURE_DOUBLE) { + gesture_cmd |= 0x04; + NVT_LOG("enable double gesture mode cmd 0x%04x\n", gesture_cmd); + } + + //---write command to enter "wakeup gesture mode"--- + nvt_cmd_ext_store(DOUBLE_TAP_GESTURE_MODE_CMD, gesture_cmd); + gesture_cmd = 0x00; +#endif + break; + + case TS_MMI_PM_DEEPSLEEP: + ts->gesture_enabled = false; +#ifdef NVT_SET_TOUCH_STATE + touch_set_state(TS_MMI_PM_DEEPSLEEP, TOUCH_PANEL_IDX_PRIMARY); +#endif + break; + + case TS_MMI_PM_ACTIVE: + break; + default: + mutex_unlock(&ts->lock); + dev_warn(dev, "panel mode %d is invalid.\n", to); + return -EINVAL; + } + mutex_unlock(&ts->lock); + NVT_LOG("IRQ is %s\n", ts_data->irq_enabled ? "EN" : "DIS"); + return 0; +} + +int32_t nvt_mmi_post_suspend(struct device *dev) +{ + uint8_t buf[4] = {0}; + + mutex_lock(&ts->lock); + NVT_LOG("enter\n"); + + if (ts->gesture_enabled) { + //---write command to enter "wakeup gesture mode"--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x13; + CTP_SPI_WRITE(ts->client, buf, 2); + enable_irq_wake(ts->client->irq); + NVT_LOG("Enabled touch wakeup gesture\n"); + } else { + //---write command to enter "deep sleep mode"--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x11; + CTP_SPI_WRITE(ts->client, buf, 2); + } + ts->bTouchIsAwake = 0; + + mutex_unlock(&ts->lock); + + release_all_touches(); + msleep(50); + + return 0; +} + +/******************************************************* +Description: + Novatek touchscreen driver resume function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int nvt_mmi_pre_resume(struct device *dev) +{ + struct nvt_ts_data *ts_data; + + GET_TS_DATA(dev); + NVT_LOG("enter\n"); + +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW + if (ts_data->flash_mode == FW_PARAM_MODE) + { + NVT_LOG("update firmware through touchUpg service\n"); + return 0; + } +#endif + + mutex_lock(&ts->lock); + NVT_LOG("update firmware: %s\n", nvt_boot_firmware_name); + nvt_update_firmware(nvt_boot_firmware_name); + + mutex_unlock(&ts->lock); + +#ifdef NOVATECH_PEN_NOTIFIER + if(!ts->fw_ready_flag) + ts->fw_ready_flag = true; + nvt_mcu_pen_detect_set(ts->nvt_pen_detect_flag); +#endif + + return 0; +} + +static int nvt_mmi_post_resume(struct device *dev) +{ + struct nvt_ts_data *ts_data; + + GET_TS_DATA(dev); + NVT_LOG("enter\n"); + + mutex_lock(&ts->lock); + if (ts->gesture_enabled) + disable_irq_wake(ts_data->client->irq); + + ts->bTouchIsAwake = 1; + mutex_unlock(&ts->lock); + + /* restore data */ + if (ts->interpolation_ctrl) { + nvt_cmd_write(ts->interpolation_cmd[0], ts->interpolation_cmd[1], 0, 2); + } + + if (ts->jitter_ctrl) { + nvt_cmd_write(ts->jitter_cmd[0], ts->jitter_cmd[1], 0, 2); + } + + if (ts->first_filter_ctrl) { + nvt_cmd_write(ts->first_filter_cmd[0], ts->first_filter_cmd[1], 0, 2); + } + + if (ts->edge_ctrl) { + if (ts->edge_cmd[0] == CMD_ON) + nvt_cmd_write(ts->edge_cmd[0], ts->edge_cmd[1], ts->edge_cmd[2], 3); + else + nvt_cmd_write(ts->edge_cmd[0], ts->edge_cmd[1], 0, 2); + + nvt_cmd_write(ts->rotate_cmd, 0, 0, 1); + } + + NVT_LOG("IRQ is %s\n", ts_data->irq_enabled ? "EN" : "DIS"); + return 0; +} + +static struct ts_mmi_methods nvt_mmi_methods = { + .get_vendor = nvt_mmi_methods_get_vendor, + .get_productinfo = nvt_mmi_methods_get_productinfo, + .get_build_id = nvt_mmi_methods_get_build_id, + .get_config_id = nvt_mmi_methods_get_config_id, + .get_bus_type = nvt_mmi_methods_get_bus_type, + .get_irq_status = nvt_mmi_methods_get_irq_status, + .get_drv_irq = nvt_mmi_methods_get_drv_irq, + .get_flashprog = nvt_mmi_methods_get_flashprog, +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW + .get_flash_mode = nvt_mmi_get_flash_mode, +#endif + .get_poweron = nvt_mmi_methods_get_poweron, + /* SET methods */ + .reset = nvt_mmi_methods_reset, + .drv_irq = nvt_mmi_methods_drv_irq, + .charger_mode = nvt_mmi_charger_mode, + /* Firmware */ +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW + .flash_mode = nvt_mmi_flash_mode, +#endif + .firmware_update = nvt_mmi_firmware_update, + .extend_attribute_group = nvt_mmi_extend_attribute_group, + /* PM callback */ + .panel_state = nvt_mmi_panel_state, + .pre_resume = nvt_mmi_pre_resume, + .post_resume = nvt_mmi_post_resume, + .post_suspend = nvt_mmi_post_suspend, + +}; + +int nvt_mmi_init(struct nvt_ts_data *ts_data, bool enable) { + int ret = 0; + + if (enable) { + ret = ts_mmi_dev_register(&ts_data->client->dev, &nvt_mmi_methods); + if (ret) + dev_err(&ts_data->client->dev, "Failed to register ts mmi\n"); + /* initialize class imported methods */ + ts_data->imports = &nvt_mmi_methods.exports; + } else + ts_mmi_dev_unregister(&ts_data->client->dev); + + return ret; +} diff --git a/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mp_ctrlram.c b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mp_ctrlram.c new file mode 100644 index 000000000000..519f4ecdb2df --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mp_ctrlram.c @@ -0,0 +1,1684 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 47376 $ + * $Date: 2019-07-12 09:06:29 +0800 (週五, 12 七月 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 +#include +#include + +#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; + +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 = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_Short) { + NVT_ERR("kzalloc for RecordResult_Short failed!\n"); + return -ENOMEM; + } + + RecordResult_Open = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_Open) { + NVT_ERR("kzalloc for RecordResult_Open failed!\n"); + return -ENOMEM; + } + + RecordResult_FWMutual = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FWMutual) { + NVT_ERR("kzalloc for RecordResult_FWMutual failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_CC = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_CC) { + NVT_ERR("kzalloc for RecordResult_FW_CC failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_DiffMax = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_DiffMax) { + NVT_ERR("kzalloc for RecordResult_FW_DiffMax failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_DiffMin = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_DiffMin) { + NVT_ERR("kzalloc for RecordResult_FW_DiffMin failed!\n"); + return -ENOMEM; + } + + RawData_Short = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Short) { + NVT_ERR("kzalloc for RawData_Short failed!\n"); + return -ENOMEM; + } + + RawData_Open = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Open) { + NVT_ERR("kzalloc for RawData_Open failed!\n"); + return -ENOMEM; + } + + RawData_Diff = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Diff) { + NVT_ERR("kzalloc for RawData_Diff failed!\n"); + return -ENOMEM; + } + + RawData_Diff_Min = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Diff_Min) { + NVT_ERR("kzalloc for RawData_Diff_Min failed!\n"); + return -ENOMEM; + } + + RawData_Diff_Max = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Diff_Max) { + NVT_ERR("kzalloc for RawData_Diff_Max failed!\n"); + return -ENOMEM; + } + + RawData_FWMutual = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_FWMutual) { + NVT_ERR("kzalloc for RawData_FWMutual failed!\n"); + return -ENOMEM; + } + + RawData_FW_CC = (int32_t *)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; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + int32_t count = data_num * 7 + 1; + + tmp_log = kzalloc(count, GFP_KERNEL); +#else + tmp_log = (char *)kzalloc(data_num * 7 + 1, GFP_KERNEL); +#endif + if (!tmp_log) { + NVT_ERR("kzalloc for tmp_log failed!\n "); + return; + } + + for (i = 0; i < data_num; i++) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + snprintf(tmp_log + i * 7, count - i * 7, "%5d, ", data[i]); +#else + sprintf(tmp_log + i * 7, "%5d, ", data[i]); +#endif + } + 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; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + int32_t count = 0; + + count = result_num * 6 + 1; + tmp_log = kzalloc(count, GFP_KERNEL); +#else + tmp_log = (char *)kzalloc(result_num * 6 + 1, GFP_KERNEL); +#endif + if (!tmp_log) { + NVT_ERR("kzalloc for tmp_log failed!\n "); + return; + } + + for (i = 0; i < result_num; i++) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + snprintf(tmp_log + i * 6, count - i * 6, "0x%02X, ", result[i]); +#else + sprintf(tmp_log + i * 6, "0x%02X, ", result[i]); +#endif + } + 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_save_rawdata_to_csv(int32_t *rawdata, uint8_t x_ch, uint8_t y_ch, const char *file_path, uint32_t offset) +{ +#ifndef CFG_CTS_FOR_GKI + int32_t x = 0; + int32_t y = 0; + int32_t iArrayIndex = 0; + struct file *fp = NULL; + char *fbufp = NULL; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + mm_segment_t org_fs; +#endif + int32_t write_ret = 0; + uint32_t output_len= 0; + loff_t pos = 0; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; + int32_t keydata_output_offset = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + printk("%s:++\n", __func__); + fbufp = (char *)kzalloc(8192, GFP_KERNEL); + if (!fbufp) { + NVT_ERR("kzalloc for fbufp failed!\n"); + return -ENOMEM; + } + + for (y = 0; y < y_ch; y++) { + for (x = 0; x < x_ch; x++) { + iArrayIndex = y * x_ch + x; + sprintf(fbufp + iArrayIndex * 7 + y * 2, "%5d, ", rawdata[iArrayIndex]); + } + nvt_print_data_log_in_one_line(rawdata + y * x_ch, x_ch); + printk("\n"); + sprintf(fbufp + (iArrayIndex + 1) * 7 + y * 2,"\r\n"); + } +#if TOUCH_KEY_NUM > 0 + keydata_output_offset = y_ch * x_ch * 7 + y_ch * 2; + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = y_ch * x_ch + k; + sprintf(fbufp + keydata_output_offset + k * 7, "%5d, ", rawdata[iArrayIndex]); + } + nvt_print_data_log_in_one_line(rawdata + y_ch * x_ch, Key_Channel); + printk("\n"); + sprintf(fbufp + y_ch * x_ch * 7 + y_ch * 2 + Key_Channel * 7, "\r\n"); +#endif /* #if TOUCH_KEY_NUM > 0 */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + if (offset == 0) + fp = filp_open(file_path, O_RDWR | O_CREAT | O_TRUNC, 0666); + else + fp = filp_open(file_path, O_RDWR | O_CREAT, 0666); +#else + org_fs = get_fs(); + set_fs(KERNEL_DS); + fp = filp_open(file_path, O_RDWR | O_CREAT, 0644); +#endif + if (fp == NULL || IS_ERR(fp)) { + NVT_ERR("open %s failed\n", file_path); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + set_fs(org_fs); +#endif + if (fbufp) { + kfree(fbufp); + fbufp = NULL; + } + return -1; + } + +#if TOUCH_KEY_NUM > 0 + output_len = y_ch * x_ch * 7 + y_ch * 2 + Key_Channel * 7 + 2; +#else + output_len = y_ch * x_ch * 7 + y_ch * 2; +#endif /* #if TOUCH_KEY_NUM > 0 */ + pos = offset; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + write_ret = vfs_write(fp, (char __user *)fbufp, output_len, &pos); +#elif (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) + write_ret = kernel_write(fp, (char __user *)fbufp, output_len, &pos); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + write_ret = kernel_write(fp, (char __user *)fbufp, output_len, &pos); +#endif + + if (write_ret <= 0) { + NVT_ERR("write %s failed\n", file_path); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + set_fs(org_fs); +#endif + if (fp) { + filp_close(fp, NULL); + fp = NULL; + } + if (fbufp) { + kfree(fbufp); + fbufp = NULL; + } + return -1; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + set_fs(org_fs); +#endif + if (fp) { + filp_close(fp, NULL); + fp = NULL; + } + if (fbufp) { + kfree(fbufp); + fbufp = NULL; + } + + printk("%s:--\n", __func__); +#endif + return 0; +} + +static int32_t nvt_polling_hand_shake_status(void) +{ + uint8_t buf[8] = {0}; + int32_t i = 0; + const int32_t retry = 250; + + for (i = 0; i < retry; i++) { + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(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_SPI_READ(ts->client, 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(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_SPI_READ(ts->client, 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(ts->mmap->EVENT_BUF_ADDR | EVENT_MAP_HOST_CMD); + + //---switch FreqHopEnDis--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = FreqHopEnDis; + CTP_SPI_WRITE(ts->client, buf, 2); + + msleep(35); + + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0xFF; + CTP_SPI_READ(ts->client, 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 */ + + printk("%s:\n", __func__); + // Save Rawdata to CSV file + if (nvt_save_rawdata_to_csv(xdata, X_Channel, Y_Channel, FW_RAWDATA_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + + 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 */ + + printk("%s:\n", __func__); + // Save Rawdata to CSV file + if (nvt_save_rawdata_to_csv(xdata, X_Channel, Y_Channel, FW_CC_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + + 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(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_SPI_WRITE(ts->client, 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; + uint32_t rawdata_diff_min_offset = 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); + + printk("%s:RawData_Diff_Max:\n", __func__); + // Save Rawdata to CSV file + if (nvt_save_rawdata_to_csv(RawData_Diff_Max, X_Channel, Y_Channel, NOISE_TEST_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + +#if TOUCH_KEY_NUM > 0 + rawdata_diff_min_offset = Y_Channel * X_Channel * 7 + Y_Channel * 2 + Key_Channel * 7 + 2; +#else + rawdata_diff_min_offset = Y_Channel * X_Channel * 7 + Y_Channel * 2; +#endif /* #if TOUCH_KEY_NUM > 0 */ + printk("%s:RawData_Diff_Min:\n", __func__); + // Save Rawdata to CSV file + if (nvt_save_rawdata_to_csv(RawData_Diff_Min, X_Channel, Y_Channel, NOISE_TEST_CSV_FILE, rawdata_diff_min_offset) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + + 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(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_SPI_WRITE(ts->client, buf, 5); +} + +static void nvt_enable_short_test(void) +{ + uint8_t buf[8] = {0}; + + //---set xdata index to EVENT BUF ADDR--- + nvt_set_page(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_SPI_WRITE(ts->client, 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 = (uint8_t *)kzalloc((IC_X_CFG_SIZE * IC_Y_CFG_SIZE + IC_KEY_CFG_SIZE) * 2, GFP_KERNEL); +#else + rawdata_buf = (uint8_t *)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(raw_pipe_addr + y * IC_X_CFG_SIZE * 2); + buf[0] = (uint8_t)((raw_pipe_addr + y * IC_X_CFG_SIZE * 2) & 0xFF); + CTP_SPI_READ(ts->client, 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(raw_btn_pipe_addr); + buf[0] = (uint8_t)(raw_btn_pipe_addr & 0xFF); + CTP_SPI_READ(ts->client, 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); + + + printk("%s:RawData_Open\n", __func__); + // Save RawData to CSV file + if (nvt_save_rawdata_to_csv(xdata, X_Channel, Y_Channel, OPEN_TEST_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + + 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 = (uint8_t *)kzalloc((X_Channel * Y_Channel + Key_Channel) * 2, GFP_KERNEL); +#else + rawdata_buf = (uint8_t *)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(raw_pipe_addr + y * X_Channel * 2); + buf[0] = (uint8_t)((raw_pipe_addr + y * X_Channel * 2) & 0xFF); + CTP_SPI_READ(ts->client, 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(raw_btn_pipe_addr); + buf[0] = (uint8_t)(raw_btn_pipe_addr & 0xFF); + CTP_SPI_READ(ts->client, 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); + + printk("%s:RawData_Short\n", __func__); + // Save Rawdata to CSV file + if (nvt_save_rawdata_to_csv(xdata, X_Channel, Y_Channel, SHORT_TEST_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + + 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 == false) { + 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; + unsigned char mpcriteria[32] = {0}; //novatek-mp-criteria-default + + 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 */ + + //---Download MP FW--- + if(nvt_mp_firmware_name) + nvt_update_firmware(nvt_mp_firmware_name); + else + nvt_update_firmware(MP_UPDATE_FIRMWARE_NAME); + + 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 is 2 bytes (show hex). + * + * Ex. nvt_pid = 500A + * mpcriteria = "novatek-mp-criteria-500A" + */ + scnprintf(mpcriteria, sizeof(mpcriteria), "novatek-mp-criteria-%04X", ts->nvt_pid); + + if (nvt_mp_parse_dt(np, mpcriteria)) { + //---Download Normal FW--- + if(nvt_boot_firmware_name) + nvt_update_firmware(nvt_boot_firmware_name); + else + nvt_update_firmware(BOOT_UPDATE_FIRMWARE_NAME); + 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); + } + + //---Download Normal FW--- + if(nvt_boot_firmware_name) + nvt_update_firmware(nvt_boot_firmware_name); + else + nvt_update_firmware(BOOT_UPDATE_FIRMWARE_NAME); + + mutex_unlock(&ts->lock); + + NVT_LOG("--\n"); + + nvt_mp_test_result_printed = 0; + + return seq_open(file, &nvt_selftest_seq_ops); +} + +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +static const struct proc_ops nvt_selftest_fops = { + .proc_open = nvt_selftest_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, +#if KERNEL_VERSION(5, 15, 0) <= LINUX_VERSION_CODE + .proc_release = seq_release, +#else + .proc_release = single_release, +#endif +}; +#else +static const struct file_operations nvt_selftest_fops = { + .owner = THIS_MODULE, + .open = nvt_selftest_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif + +#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 */ diff --git a/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mp_ctrlram.h b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mp_ctrlram.h new file mode 100644 index 000000000000..924a4c4a0fb1 --- /dev/null +++ b/drivers/input/touchscreen/nova_0flash_mmi/nt36xxx_mp_ctrlram.h @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2010 - 2018 Novatek, Inc. + * + * $Revision: 46242 $ + * $Date: 2019-06-17 10:29:24 +0800 (週一, 17 六月 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 */ diff --git a/drivers/input/touchscreen/nova_36525_mmi/Android.mk b/drivers/input/touchscreen/nova_36525_mmi/Android.mk new file mode 100644 index 000000000000..edc91035fb5e --- /dev/null +++ b/drivers/input/touchscreen/nova_36525_mmi/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +NOVA_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +NOVA_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := nova_36525_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(NOVA_MMI_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/nova_36525_mmi/Kbuild b/drivers/input/touchscreen/nova_36525_mmi/Kbuild new file mode 100644 index 000000000000..01a63f95fff6 --- /dev/null +++ b/drivers/input/touchscreen/nova_36525_mmi/Kbuild @@ -0,0 +1,13 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/nova_36525_mmi + +ifeq ($(filter m y, $(CONFIG_TOUCHSCREEN_NOVATEK_CHIPSET)),) + obj-m := nova_36525_mmi.o +endif + +nova_36525_mmi-objs += nt36xxx.o +nova_36525_mmi-objs += nt36xxx_ext_proc.o +nova_36525_mmi-objs += nt36xxx_fw_update.o +nova_36525_mmi-objs += nt36xxx_mp_ctrlram.o diff --git a/drivers/input/touchscreen/nova_36525_mmi/Makefile b/drivers/input/touchscreen/nova_36525_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/nova_36525_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/nova_36525_mmi/nt36xxx.c b/drivers/input/touchscreen/nova_36525_mmi/nt36xxx.c new file mode 100644 index 000000000000..4ab083d1c891 --- /dev/null +++ b/drivers/input/touchscreen/nova_36525_mmi/nt36xxx.c @@ -0,0 +1,1976 @@ +/* + * Copyright (C) 2010 - 2017 Novatek, Inc. + * + * $Revision: 23175 $ + * $Date: 2018-02-12 16:26:21 +0800 (Mon, 12 Feb 2018) $ + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if defined(CONFIG_FB) +#include +#include +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#endif + +#include "nt36xxx.h" +#if NVT_TOUCH_ESD_PROTECT +#include +#endif /* #if NVT_TOUCH_ESD_PROTECT */ +#if defined(CONFIG_CHARGER_NOTIFY) +#include +#endif + +#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; +uint8_t esd_check; +uint8_t esd_retry; +uint8_t esd_retry_max = 5; +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + +#if NVT_TOUCH_EXT_PROC +extern int32_t nvt_extra_proc_init(void); +extern void nvt_extra_proc_remove(void); +#endif + +#if NVT_TOUCH_FW +extern int32_t nvt_fw_sysfs_init(void); +extern int32_t nvt_fw_sysfs_deinit(void); +#endif + +#if NVT_TOUCH_MP +extern int32_t nvt_mp_proc_init(void); +extern void nvt_mp_proc_remove(void); +#endif + +struct nvt_ts_data *ts; + +static struct workqueue_struct *nvt_wq; + +#if BOOT_UPDATE_FIRMWARE +static struct workqueue_struct *nvt_fwu_wq; +extern void Boot_Update_Firmware(struct work_struct *work); +#endif + +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void nvt_ts_early_suspend(struct early_suspend *h); +static void nvt_ts_late_resume(struct early_suspend *h); +#endif +#if defined(CONFIG_CHARGER_NOTIFY) +static int charger_notifier_callback(struct notifier_block *nb, + unsigned long val, void *v); +int32_t nvt_set_charger(uint8_t charger_on_off); +static void nvt_charger_notify_work(struct work_struct *work); +int usb_detect_flag = 0; +#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; + +/******************************************************* +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; + + 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 = &buf[1]; + + 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; + } + + 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; + + msg.flags = !I2C_M_RD; + msg.addr = address; + msg.len = len; + msg.buf = buf; + + 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; + } + + return ret; +} + + +/******************************************************* +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("enter %s start\n", __func__); + //---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("exit %s end\n", __func__); +} + +/******************************************************* +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--- + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + //---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; + + msleep(10); + } + + if (i >= retry) { + NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); + return -EPERM; + } 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--- + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + //---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; + + msleep(10); + } + + if (i >= retry) { + NVT_ERR("failed, i=%d, buf[1]=0x%02X\n", i, buf[1]); + return -EPERM; + } 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; + NVT_LOG("enter %s start\n", __func__); + + while (1) { + msleep(10); + + //---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 (check_reset_state <= RESET_STATE_REK) { + if (unlikely(retry > 50)) { + 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; + } + } else { + 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; + } + } + } + NVT_LOG("exit %s end\n", __func__); + + 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}; + int32_t ret = 0; + + //---set xdata index to EVENT BUF ADDR--- + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + //---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 ret; +} + +/******************************************************* +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; + NVT_LOG("enter %s start\n", __func__); + +info_retry: + //---set xdata index to EVENT BUF ADDR--- + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + //---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(); + NVT_LOG("exit %s end\n", __func__); + + 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; + + if (dev) + kfree(dev); + + return 0; +} + +static const struct file_operations nvt_flash_fops = { + .owner = THIS_MODULE, + .open = nvt_flash_open, + .release = nvt_flash_close, + .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/NVTflash\n"); + NVT_LOG("============================================================\n"); + + return 0; +} +#endif + +static void nvt_flash_proc_remove(void) +{ + if (NVT_proc_entry != NULL) { + remove_proc_entry(DEVICE_NAME, NULL); + NVT_LOG("Removed %s under /proc\n", + DEVICE_NAME); + } +} + +#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 + +#define NVT_TS_HOLD_TIME 5000 +static struct wakeup_source gestrue_wakelock; + + +/******************************************************* +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; + NVT_LOG("enter %s start\n", __func__); + +#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); + NVT_LOG("exit %s end\n", __func__); +} +#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; + NVT_LOG("enter %s start\n", __func__); +#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; + } + } + NVT_LOG("exit %s end\n", __func__); + + return ret; + +err_request_irq_gpio: +#if NVT_TOUCH_SUPPORT_HW_RST + if (gpio_is_valid(ts->reset_gpio)) + gpio_free(ts->reset_gpio); +err_request_reset_gpio: +#endif + NVT_LOG("exit %s abnormal end\n", __func__); + + return ret; +} + +#if NVT_TOUCH_ESD_PROTECT +void nvt_esd_check_enable(uint8_t enable) +{ + /* enable/disable esd check flag */ + esd_check = enable; + /* update interrupt timer */ + irq_timer = jiffies; + /* clear esd_retry counter, if protect function is enabled */ + esd_retry = enable ? 0 : esd_retry; +} + +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; +} + +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/%d)\n", esd_check, esd_retry, esd_retry_max); //DEBUG + + if (esd_retry >= esd_retry_max) + nvt_esd_check_enable(false); + + if ((timer > NVT_TOUCH_ESD_CHECK_PERIOD) && esd_check) { + NVT_ERR("do ESD recovery, timer = %d, retry = %d\n", timer, esd_retry); + /* do esd recovery, bootloader reset */ + nvt_bootloader_reset(); + /* 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 void nvt_ts_work_func(struct work_struct *work) +{ + 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; + + 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; + } +/* + //--- dump I2C 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_TOUCH_ESD_PROTECT + if (nvt_fw_recovery(point_data)) { + nvt_esd_check_enable(true); + goto XFER_ERROR; + } +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + +#if WAKEUP_GESTURE + if (bTouchIsAwake == 0) { + input_id = (uint8_t)(point_data[1] >> 3); + nvt_ts_wakeup_gesture_report(input_id, point_data); + enable_irq(ts->client->irq); + mutex_unlock(&ts->lock); + return; + } +#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) { + 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: + enable_irq(ts->client->irq); + + mutex_unlock(&ts->lock); +} + +/******************************************************* +Description: + External interrupt service routine. + +return: + irq execute status. +*******************************************************/ +static irqreturn_t nvt_ts_irq_handler(int32_t irq, void *dev_id) +{ + disable_irq_nosync(ts->client->irq); + +#if WAKEUP_GESTURE + if (bTouchIsAwake == 0) { + __pm_wakeup_event(&gestrue_wakelock, NVT_TS_HOLD_TIME); + } +#endif + + queue_work(nvt_wq, &ts->nvt_work); + + 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--- + buf[0] = 0xFF; + buf[1] = 0x01; + buf[2] = 0xF6; + CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 3); + + //---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[2] == 0xFC) && (buf[3] == 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--- + buf[0] = 0xFF; + buf[1] = 0x03; + buf[2] = 0xF1; + CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 3); + + buf[0] = 0x35; + buf[1] = 0xA5; + CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 2); + + //---check CRC_ERR_FLAG--- + buf[0] = 0xFF; + buf[1] = 0x03; + buf[2] = 0xF1; + CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 3); + + 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! buf[1]=0x%02X\n", buf[1]); + } + + 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(void) +{ + 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_LOG("enter %s start\n", __func__); + + 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); + + buf[0] = 0xFF; + buf[1] = 0x01; + buf[2] = 0xF6; + CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 3); + + buf[0] = 0x4E; + 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]); + + // 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"); +#if NVT_TOUCH_FW + if (list == 0) { + snprintf(ts->product_id, sizeof(ts->product_id), "NT36525B"); + } else if (list == 1) { + snprintf(ts->product_id, sizeof(ts->product_id), "NT36672A"); + } else if (list == 10) { + snprintf(ts->product_id, sizeof(ts->product_id), "NT36525"); + } else if (list == 11) { + snprintf(ts->product_id, sizeof(ts->product_id), "NT36870"); + } else if (list == 12) { + snprintf(ts->product_id, sizeof(ts->product_id), "NT36676F"); + } else { + snprintf(ts->product_id, sizeof(ts->product_id), "NT36772"); + } +#endif + ts->mmap = trim_id_table[list].mmap; + ts->carrier_system = trim_id_table[list].carrier_system; + ret = 0; + goto out; + } else { + ts->mmap = NULL; + ret = -1; + } + } + + //---Stop CRC check to prevent IC auto reboot--- + if (((buf[1] == 0xFC) && (buf[2] == 0xFC) && (buf[3] == 0xFC)) || + ((buf[1] == 0xFF) && (buf[2] == 0xFF) && (buf[3] == 0xFF))) { + nvt_stop_crc_reboot(); + } + + msleep(10); + } + +out: + NVT_LOG("exit %s end\n", __func__); + 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 ((TOUCH_KEY_NUM > 0) || WAKEUP_GESTURE) + int32_t retry = 0; +#endif + + NVT_LOG("enter %s start\n", __func__); + //---check i2c func.--- + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + NVT_ERR("i2c_check_functionality failed. (no I2C_FUNC_I2C)\n"); + ret = -ENODEV; + goto err_check_functionality_failed; + } + + ts = kmalloc(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); + + + //---request and config GPIOs--- + ret = nvt_gpio_config(ts); + if (ret) { + NVT_ERR("gpio config error!\n"); + goto err_gpio_config_failed; + } + + // need 10ms delay after POR(power on reset) + msleep(10); + //---check chip version trim--- + ret = nvt_ts_check_chip_ver_trim(); + if (ret) { + NVT_ERR("chip is not identified\n"); + ret = -EINVAL; + goto err_chipvertrim_failed; + } + + nvt_read_pid(); + + mutex_init(&ts->lock); + + mutex_lock(&ts->lock); + nvt_bootloader_reset(); + nvt_check_fw_reset_state(RESET_STATE_INIT); + nvt_get_fw_info(); + mutex_unlock(&ts->lock); + + //---create workqueue--- + nvt_wq = create_workqueue("nvt_wq"); + if (!nvt_wq) { + NVT_ERR("nvt_wq create workqueue failed\n"); + ret = -ENOMEM; + goto err_create_nvt_wq_failed; + } + INIT_WORK(&ts->nvt_work, nvt_ts_work_func); + + + //---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 < (sizeof(gesture_key_array) / sizeof(gesture_key_array[0])); retry++) { + input_set_capability(ts->input_dev, EV_KEY, gesture_key_array[retry]); + } + wakeup_source_init(&gestrue_wakelock, "gestrue-wake-lock"); +#endif + + snprintf(ts->phys, 9, "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); + +#if WAKEUP_GESTURE + ret = request_irq(client->irq, nvt_ts_irq_handler, ts->int_trigger_type | IRQF_NO_SUSPEND, client->name, ts); +#else + ret = request_irq(client->irq, nvt_ts_irq_handler, ts->int_trigger_type, client->name, ts); +#endif + if (ret != 0) { + NVT_ERR("request irq failed. ret=%d\n", ret); + goto err_int_request_failed; + } else { + disable_irq(client->irq); + NVT_LOG("request irq %d succeed\n", client->irq); + } + } + +#if defined(CONFIG_CHARGER_NOTIFY) + ts->usb_connected = 0; + ts->nvt_charger_notify_wq = create_singlethread_workqueue("nvt_charger_notify_wq"); + if (!ts->nvt_charger_notify_wq) { + NVT_ERR(" allocate nvt_charger_notify_wq failed\n"); + goto err_charger_notify_wq_failed; + } + INIT_WORK(&ts->charger_notify_work, nvt_charger_notify_work); + + ts->charger_notif.notifier_call = charger_notifier_callback; + ret = power_supply_reg_notifier(&ts->charger_notif); + if (ret) { + NVT_ERR("Unable to register charger_notifier: %d\n",ret); + goto err_register_charger_notify_failed; + } +#endif + +#if BOOT_UPDATE_FIRMWARE + nvt_fwu_wq = create_singlethread_workqueue("nvt_fwu_wq"); + 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 + +#if NVT_TOUCH_ESD_PROTECT + INIT_DELAYED_WORK(&nvt_esd_check_work, nvt_esd_check_func); + nvt_esd_check_wq = create_workqueue("nvt_esd_check_wq"); + 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_init_NVT_ts; + } +#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_init_NVT_ts; + } +#endif + +#if NVT_TOUCH_FW + ts->suspended = 0; + ts->force_reflash = 0; + ts->loading_fw = 0; + + ret = nvt_fw_sysfs_init(); + if (ret != 0) { + NVT_ERR("nvt fw class init failed. ret=%d\n", ret); + goto err_init_NVT_ts; + } +#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_init_NVT_ts; + } +#endif + +#if defined(CONFIG_FB) + ts->fb_notif.notifier_call = 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; + } +#elif defined(CONFIG_HAS_EARLYSUSPEND) + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->early_suspend.suspend = nvt_ts_early_suspend; + ts->early_suspend.resume = nvt_ts_late_resume; + ret = register_early_suspend(&ts->early_suspend); + if (ret) { + NVT_ERR("register early suspend failed. ret=%d\n", ret); + goto err_register_early_suspend_failed; + } +#endif + + bTouchIsAwake = 1; + + NVT_LOG("exit %s normal end\n", __func__); + + enable_irq(client->irq); + + return 0; + +#if defined(CONFIG_FB) +err_register_fb_notif_failed: +#elif defined(CONFIG_HAS_EARLYSUSPEND) +err_register_early_suspend_failed: +#endif +#if (NVT_TOUCH_PROC || NVT_TOUCH_EXT_PROC || NVT_TOUCH_MP) +err_init_NVT_ts: +#endif +#if BOOT_UPDATE_FIRMWARE + err_create_nvt_fwu_wq_failed: +#endif +#if defined(CONFIG_CHARGER_NOTIFY) +err_register_charger_notify_failed: + if (ts->charger_notif.notifier_call) + power_supply_unreg_notifier(&ts->charger_notif); + destroy_workqueue(ts->nvt_charger_notify_wq); + ts->nvt_charger_notify_wq = NULL; +err_charger_notify_wq_failed: +#endif + free_irq(client->irq, ts); +err_int_request_failed: +err_input_register_device_failed: + input_free_device(ts->input_dev); +err_input_dev_alloc_failed: +err_create_nvt_wq_failed: + mutex_destroy(&ts->lock); +err_chipvertrim_failed: +#if NVT_TOUCH_SUPPORT_HW_RST + if (gpio_is_valid(ts->reset_gpio)) + gpio_free(ts->reset_gpio); +#endif + if (gpio_is_valid(ts->irq_gpio)) + gpio_free(ts->irq_gpio); +err_gpio_config_failed: + i2c_set_clientdata(client, NULL); + kfree(ts); +err_check_functionality_failed: + + NVT_LOG("exit %s abnormal end\n", __func__); + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen driver release function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int32_t nvt_ts_remove(struct i2c_client *client) +{ + //struct nvt_ts_data *ts = i2c_get_clientdata(client); + +#if defined(CONFIG_FB) + if (fb_unregister_client(&ts->fb_notif)) + NVT_ERR("Error occurred while unregistering fb_notifier.\n"); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts->early_suspend); +#endif +#if defined(CONFIG_CHARGER_NOTIFY) + if (ts->charger_notif.notifier_call) + power_supply_unreg_notifier(&ts->charger_notif); + + destroy_workqueue(ts->nvt_charger_notify_wq); +#endif + +#if NVT_TOUCH_FW + nvt_fw_sysfs_deinit(); +#endif + + mutex_destroy(&ts->lock); + + NVT_LOG("Removing driver...\n"); + + free_irq(client->irq, 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 + +#if NVT_TOUCH_PROC + nvt_flash_proc_remove(); +#endif +#if NVT_TOUCH_MP + nvt_mp_proc_remove(); +#endif +#if NVT_TOUCH_EXT_PROC + nvt_extra_proc_remove(); +#endif + + input_unregister_device(ts->input_dev); + i2c_set_clientdata(client, NULL); + kfree(ts); + + return 0; +} + +/******************************************************* +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; + } + + mutex_lock(&ts->lock); + + NVT_LOG("start\n"); +#if NVT_TOUCH_FW + ts->suspended = 1; +#endif + bTouchIsAwake = 0; + +#if NVT_TOUCH_ESD_PROTECT + cancel_delayed_work_sync(&nvt_esd_check_work); + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + +#if WAKEUP_GESTURE + //---write i2c command to enter "wakeup gesture mode"--- + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x13; +#if 0 // Do not set 0xFF first, ToDo + buf[2] = 0xFF; + buf[3] = 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 4); +#else + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); +#endif + + enable_irq_wake(ts->client->irq); + + NVT_LOG("Enabled touch wakeup gesture\n"); + +#else // WAKEUP_GESTURE + disable_irq(ts->client->irq); + + //---write i2c 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 + + /* 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); + + mutex_unlock(&ts->lock); + + 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) +{ + if (bTouchIsAwake) { + NVT_LOG("Touch is already resume\n"); + return 0; + } + + msleep(10); + mutex_lock(&ts->lock); + + NVT_LOG("start\n"); + + // please make sure display reset(RESX) sequence and mipi dsi cmds sent before this +#if NVT_TOUCH_SUPPORT_HW_RST + if (gpio_is_valid(ts->reset_gpio)) + gpio_set_value(ts->reset_gpio, 1); +#endif + nvt_bootloader_reset(); + nvt_check_fw_reset_state(RESET_STATE_REK); + +#if !WAKEUP_GESTURE + enable_irq(ts->client->irq); +#endif + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(true); + + 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 */ +#if defined(CONFIG_CHARGER_NOTIFY) + queue_work(ts->nvt_charger_notify_wq, &ts->charger_notify_work); +#endif + + bTouchIsAwake = 1; +#if NVT_TOUCH_FW + ts->suspended = 0; +#endif + + mutex_unlock(&ts->lock); + + NVT_LOG("end\n"); + + return 0; +} + + +#if defined(CONFIG_FB) +static int 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_ts_suspend(&ts->client->dev); + } + } else if (evdata && evdata->data && event == FB_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK || + (*blank == FB_BLANK_NORMAL && 0 == bTouchIsAwake)) { + nvt_ts_resume(&ts->client->dev); + } + } + + return 0; +} +#elif defined(CONFIG_HAS_EARLYSUSPEND) +/******************************************************* +Description: + Novatek touchscreen driver early suspend function. + +return: + n.a. +*******************************************************/ +static void nvt_ts_early_suspend(struct early_suspend *h) +{ + nvt_ts_suspend(ts->client, PMSG_SUSPEND); +} + +/******************************************************* +Description: + Novatek touchscreen driver late resume function. + +return: + n.a. +*******************************************************/ +static void nvt_ts_late_resume(struct early_suspend *h) +{ + nvt_ts_resume(ts->client); +} +#endif + +#if defined(CONFIG_CHARGER_NOTIFY) +int32_t nvt_set_charger(uint8_t charger_on_off) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + NVT_LOG("set charger: %d\n", charger_on_off); + + msleep(20); + //---set xdata index to EVENT BUF ADDR--- + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + + ret = CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto nvt_set_charger_out; + } + + if (charger_on_off == USB_DETECT_IN) { + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = CMD_CHARGER_ON; + ret = CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Write set charger command fail!\n"); + goto nvt_set_charger_out; + } + } else if (charger_on_off == USB_DETECT_OUT) { + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = CMD_CHARGER_OFF; + ret = CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Write set charger command fail!\n"); + goto nvt_set_charger_out; + } + } else { + NVT_ERR("Invalid charger parameter!\n"); + ret = -EINVAL; + } + +nvt_set_charger_out: + + return ret; +} + +static void nvt_charger_notify_work(struct work_struct *work) +{ + if (NULL == work) { + NVT_ERR("%s: parameter work are null!\n", __func__); + return; + } + NVT_LOG("enter\n"); + if (USB_DETECT_IN == usb_detect_flag) { + if (mutex_lock_interruptible(&ts->lock)) { + NVT_ERR("Failed to lock in mutex_lock_interruptible(&ts->lock).\n"); + return; + } + nvt_set_charger(USB_DETECT_IN); + mutex_unlock(&ts->lock); + } else if (USB_DETECT_OUT == usb_detect_flag) { + if (mutex_lock_interruptible(&ts->lock)) { + NVT_ERR("Failed to lock in mutex_lock_interruptible(&ts->lock).\n"); + return; + } + nvt_set_charger(USB_DETECT_OUT); + mutex_unlock(&ts->lock); + }else{ + NVT_LOG("Charger flag:%d not currently required!\n",usb_detect_flag); + } +} + +static int charger_notifier_callback(struct notifier_block *nb, + unsigned long val, void *v) +{ + int ret = 0; + struct power_supply *psy = NULL; + struct nvt_ts_data *ts = container_of(nb, struct nvt_ts_data, charger_notif); + union power_supply_propval prop; + + psy= power_supply_get_by_name("usb"); + if (!psy){ + return -EINVAL; + NVT_ERR("Couldn't get usbpsy\n"); + } + if (!strcmp(psy->desc->name, "usb")){ + if (psy && ts && val == POWER_SUPPLY_PROP_STATUS) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,&prop); + if (ret < 0) { + NVT_ERR("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret); + return ret; + }else{ + usb_detect_flag = prop.intval; + if(usb_detect_flag != ts->usb_connected) { + if (USB_DETECT_IN == usb_detect_flag) { + ts->usb_connected = USB_DETECT_IN; + }else{ + ts->usb_connected = USB_DETECT_OUT; + } + if (bTouchIsAwake){ + queue_work(ts->nvt_charger_notify_wq, &ts->charger_notify_work); + } + } + } + } + } + return 0; +} +#endif + +#if 0 +static const struct dev_pm_ops nvt_ts_dev_pm_ops = { + .suspend = nvt_ts_suspend, + .resume = nvt_ts_resume, +}; +#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_board_info __initdata nvt_i2c_boardinfo[] = { + { + I2C_BOARD_INFO(NVT_I2C_NAME, I2C_FW_Address), + }, +}; +*/ + +static struct i2c_driver nvt_i2c_driver = { + .probe = nvt_ts_probe, + .remove = nvt_ts_remove, +// .suspend = nvt_ts_suspend, +// .resume = nvt_ts_resume, + .id_table = nvt_ts_id, + .driver = { + .name = NVT_I2C_NAME, + .owner = THIS_MODULE, +#if 0 +#ifdef CONFIG_PM + .pm = &nvt_ts_dev_pm_ops, +#endif +#endif +#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) { + pr_err("%s: failed to add i2c driver", __func__); + goto err_driver; + } + + pr_info("%s: finished\n", __func__); + +err_driver: + return ret; +} + +/******************************************************* +Description: + Driver uninstall function. + +return: + n.a. +********************************************************/ +static void __exit nvt_driver_exit(void) +{ + i2c_del_driver(&nvt_i2c_driver); + + if (nvt_wq) + destroy_workqueue(nvt_wq); + +#if BOOT_UPDATE_FIRMWARE + if (nvt_fwu_wq) + destroy_workqueue(nvt_fwu_wq); +#endif + +#if NVT_TOUCH_ESD_PROTECT + if (nvt_esd_check_wq) { + cancel_delayed_work_sync(&nvt_esd_check_work); + flush_workqueue(nvt_esd_check_wq); + destroy_workqueue(nvt_esd_check_wq); + } +#endif /* #if NVT_TOUCH_ESD_PROTECT */ +} + +//late_initcall(nvt_driver_init); +module_init(nvt_driver_init); +module_exit(nvt_driver_exit); + +MODULE_DESCRIPTION("Novatek Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/nova_36525_mmi/nt36xxx.h b/drivers/input/touchscreen/nova_36525_mmi/nt36xxx.h new file mode 100644 index 000000000000..13ea757502cf --- /dev/null +++ b/drivers/input/touchscreen/nova_36525_mmi/nt36xxx.h @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2010 - 2017 Novatek, Inc. + * + * $Revision: 22429 $ + * $Date: 2018-01-30 19:42:59 +0800 (Tue, 30 Jan 2018) $ + * + * 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 + +#include +#include + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + +#include "nt36xxx_mem_map.h" + +#define NVT_DEBUG 0 + +//---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 +#define PANNEL_ID 0x6013 + +#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 720 +#define TOUCH_DEFAULT_MAX_HEIGHT 1512 +#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_FW 1 +#define NVT_TOUCH_MP 1 +#define NVT_TOUCH_MP_LENOVO 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 0 +#define BOOT_UPDATE_FIRMWARE_NAME "novatek_ts_fw.bin" +#define NT36525B_FIRMWARE_NAME "novatek_ts_fw_pid6013.bin" +/* ---ESD Protect.--- */ +#define NVT_TOUCH_ESD_PROTECT 1 +#define NVT_TOUCH_ESD_CHECK_PERIOD 2000 /* ms */ + +#define CONFIG_CHARGER_NOTIFY +#if defined(CONFIG_CHARGER_NOTIFY) + #define USB_DETECT_IN 1 + #define USB_DETECT_OUT 0 + #define CMD_CHARGER_ON (0x53) + #define CMD_CHARGER_OFF (0x51) +#endif + +struct nvt_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct work_struct nvt_work; + struct delayed_work nvt_fwu_work; + uint16_t addr; + int8_t phys[32]; +#if defined(CONFIG_FB) + struct notifier_block fb_notif; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#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; +#if NVT_TOUCH_FW + int8_t product_id[10]; + uint8_t suspended; + uint8_t force_reflash; + uint8_t loading_fw; +#endif +#if defined(CONFIG_CHARGER_NOTIFY) + struct notifier_block charger_notif; + uint8_t usb_connected; + struct workqueue_struct *nvt_charger_notify_wq; + struct work_struct charger_notify_work; +#endif +}; + +#if NVT_TOUCH_FW +#define FW_NAME_MAX_LEN 80 +#define VENDOR_NAME "novatek_ts" +#endif + +#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, + EVENT_MAP_FWDATE = 0x9C, +} I2C_EVENT_MAP; + +#if NVT_TOUCH_MP_LENOVO +typedef enum { + MP_RESULT_SHIFT_SHORT = 0x00, + MP_RESULT_SHIFT_SHORT_DIFF, + MP_RESULT_SHIFT_SHORT_BASE, + MP_RESULT_SHIFT_OPEN, + MP_RESULT_SHIFT_RAWDATA, + MP_RESULT_SHIFT_CC, + MP_RESULT_SHIFT_CC_I, + MP_RESULT_SHIFT_CC_Q, + MP_RESULT_SHIFT_NOISE, + MP_RESULT_SHIFT_DIFF_MAX, + MP_RESULT_SHIFT_DIFF_MIN, +/* MP_RESULT_SHIFT_READFAIL,*/ +} MP_TEST_RESULT; +#endif + +//---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); +#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); + +#endif /* _LINUX_NVT_TOUCH_H */ diff --git a/drivers/input/touchscreen/nova_36525_mmi/nt36xxx_ext_proc.c b/drivers/input/touchscreen/nova_36525_mmi/nt36xxx_ext_proc.c new file mode 100644 index 000000000000..12057a884967 --- /dev/null +++ b/drivers/input/touchscreen/nova_36525_mmi/nt36xxx_ext_proc.c @@ -0,0 +1,721 @@ +/* + * Copyright (C) 2010 - 2017 Novatek, Inc. + * + * $Revision: 20251 $ + * $Date: 2017-12-13 17:41:29 +0800 (Wed, 13 Dec 2017) $ + * + * 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 +#include +#include + +#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 I2C_TANSFER_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 +#define XDATA_BUF_SIZE 2048 + +static uint8_t xdata_tmp[XDATA_BUF_SIZE] = {0}; +static int32_t xdata[XDATA_BUF_SIZE] = {0}; +static int32_t xdata_i[XDATA_BUF_SIZE] = {0}; +static int32_t xdata_q[XDATA_BUF_SIZE] = {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--- + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + //---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--- + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + //---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 mass data function. + +return: + n.a. +*******************************************************/ +int32_t nvt_read_mass_data(uint8_t i2c_addr, uint8_t *temp_buf, uint32_t count) +{ + int32_t i = 0; + int32_t j = 0; + int32_t k = 0; + uint8_t i2c_buf[I2C_TANSFER_LENGTH + 1] = {0}; + uint32_t xdata_addr = 0; + uint32_t head_addr = 0; + int32_t dummy_len = 0; + int32_t data_len = 0; + int32_t residual_len = 0; + + xdata_addr = (temp_buf[3] << 24) + (temp_buf[4] << 16) + (temp_buf[5] << 8) + temp_buf[6]; + head_addr = xdata_addr - (xdata_addr % XDATA_SECTOR_SIZE); + dummy_len = xdata_addr - head_addr; + data_len = (int32_t)((temp_buf[1] << 8) + temp_buf[2]); + residual_len = (head_addr + dummy_len + data_len) % XDATA_SECTOR_SIZE; + + if (data_len > XDATA_BUF_SIZE) + return -EINVAL; + + for (i = 0; i < ((dummy_len + data_len) / XDATA_SECTOR_SIZE); i++) { + i2c_buf[0] = 0xFF; + i2c_buf[1] = ((head_addr + XDATA_SECTOR_SIZE * i) >> 16) & 0xFF; + i2c_buf[2] = ((head_addr + XDATA_SECTOR_SIZE * i) >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, (uint16_t)i2c_addr, i2c_buf, 3); + for (j = 0; j < (XDATA_SECTOR_SIZE / I2C_TANSFER_LENGTH); j++) { + i2c_buf[0] = I2C_TANSFER_LENGTH * j; + CTP_I2C_READ(ts->client, (uint16_t)i2c_addr, i2c_buf, I2C_TANSFER_LENGTH + 1); + for (k = 0; k < I2C_TANSFER_LENGTH; k++) { + xdata_tmp[XDATA_SECTOR_SIZE * i + I2C_TANSFER_LENGTH * j + k] = i2c_buf[k + 1]; + } + } + } + + if (residual_len != 0) { + i2c_buf[0] = 0xFF; + i2c_buf[1] = ((xdata_addr + data_len - residual_len) >> 16) & 0xFF; + i2c_buf[2] = ((xdata_addr + data_len - residual_len) >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, (uint16_t)i2c_addr, i2c_buf, 3); + for (j = 0; j < (residual_len / I2C_TANSFER_LENGTH + 1); j++) { + i2c_buf[0] = I2C_TANSFER_LENGTH * j; + CTP_I2C_READ(ts->client, (uint16_t)i2c_addr, i2c_buf, I2C_TANSFER_LENGTH + 1); + for (k = 0; k < I2C_TANSFER_LENGTH; k++) { + xdata_tmp[(dummy_len + data_len - residual_len) + I2C_TANSFER_LENGTH * j + k] = i2c_buf[k + 1]; + } + } + } + + i2c_buf[0] = 0xFF; + i2c_buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + i2c_buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, (uint16_t)i2c_addr, i2c_buf, 3); + + for (i = 0; i < data_len; i++) { + *(temp_buf + i) = xdata_tmp[dummy_len + i]; + } + + return data_len; +} + +/******************************************************* +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[I2C_TANSFER_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--- + buf[0] = 0xFF; + buf[1] = ((head_addr + XDATA_SECTOR_SIZE * i) >> 16) & 0xFF; + buf[2] = ((head_addr + XDATA_SECTOR_SIZE * i) >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + //---read xdata by I2C_TANSFER_LENGTH + for (j = 0; j < (XDATA_SECTOR_SIZE / I2C_TANSFER_LENGTH); j++) { + //---read data--- + buf[0] = I2C_TANSFER_LENGTH * j; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, I2C_TANSFER_LENGTH + 1); + + //---copy buf to xdata_tmp--- + for (k = 0; k < I2C_TANSFER_LENGTH; k++) { + xdata_tmp[XDATA_SECTOR_SIZE * i + I2C_TANSFER_LENGTH * j + k] = buf[k + 1]; + //printk("0x%02X, 0x%04X\n", buf[k+1], (XDATA_SECTOR_SIZE*i + I2C_TANSFER_LENGTH*j + k)); + } + } + //printk("addr=0x%05X\n", (head_addr+XDATA_SECTOR_SIZE*i)); + } + + //read xdata : step2 + if (residual_len != 0) { + //---change xdata index--- + buf[0] = 0xFF; + buf[1] = ((xdata_addr + data_len - residual_len) >> 16) & 0xFF; + buf[2] = ((xdata_addr + data_len - residual_len) >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + //---read xdata by I2C_TANSFER_LENGTH + for (j = 0; j < (residual_len / I2C_TANSFER_LENGTH + 1); j++) { + //---read data--- + buf[0] = I2C_TANSFER_LENGTH * j; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, I2C_TANSFER_LENGTH + 1); + + //---copy buf to xdata_tmp--- + for (k = 0; k < I2C_TANSFER_LENGTH; k++) { + xdata_tmp[(dummy_len + data_len - residual_len) + I2C_TANSFER_LENGTH * j + k] = buf[k + 1]; + //printk("0x%02X, 0x%04x\n", buf[k+1], ((dummy_len+data_len-residual_len) + I2C_TANSFER_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--- + buf[0] = 0xFF; + buf[1] = (xdata_btn_addr >> 16) & 0xFF; + buf[2] = ((xdata_btn_addr >> 8) & 0xFF); + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + //---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--- + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); +} + +/******************************************************* +Description: + Novatek touchscreen read meta data from IQ to rss function. + +return: + n.a. +*******************************************************/ +void nvt_read_mdata_rss(uint32_t xdata_i_addr, uint32_t xdata_q_addr, uint32_t xdata_btn_i_addr, uint32_t xdata_btn_q_addr) +{ + int i = 0; + + nvt_read_mdata(xdata_i_addr, xdata_btn_i_addr); + memcpy(xdata_i, xdata, ((ts->x_num * ts->y_num + TOUCH_KEY_NUM) * sizeof(int32_t))); + + nvt_read_mdata(xdata_q_addr, xdata_btn_q_addr); + memcpy(xdata_q, xdata, ((ts->x_num * ts->y_num + TOUCH_KEY_NUM) * sizeof(int32_t))); + + for (i = 0; i < (ts->x_num * ts->y_num + TOUCH_KEY_NUM); i++) { + xdata[i] = (int32_t)int_sqrt((unsigned long)(xdata_i[i] * xdata_i[i]) + (unsigned long)(xdata_q[i] * xdata_q[i])); + } +} + +/******************************************************* +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 file_operations nvt_fw_version_fops = { + .owner = THIS_MODULE, + .open = nvt_fw_version_open, + .read = seq_read, + .llseek = seq_lseek, + .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; + } + + if (ts->carrier_system) { + nvt_read_mdata_rss(ts->mmap->BASELINE_ADDR, ts->mmap->BASELINE_Q_ADDR, + ts->mmap->BASELINE_BTN_ADDR, ts->mmap->BASELINE_BTN_Q_ADDR); + } else { + 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 file_operations nvt_baseline_fops = { + .owner = THIS_MODULE, + .open = nvt_baseline_open, + .read = seq_read, + .llseek = seq_lseek, + .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 (ts->carrier_system) { + if (nvt_get_fw_pipe() == 0) + nvt_read_mdata_rss(ts->mmap->RAW_PIPE0_ADDR, ts->mmap->RAW_PIPE0_Q_ADDR, + ts->mmap->RAW_BTN_PIPE0_ADDR, ts->mmap->RAW_BTN_PIPE0_Q_ADDR); + else + nvt_read_mdata_rss(ts->mmap->RAW_PIPE1_ADDR, ts->mmap->RAW_PIPE1_Q_ADDR, + ts->mmap->RAW_BTN_PIPE1_ADDR, ts->mmap->RAW_BTN_PIPE1_Q_ADDR); + } else { + 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 file_operations nvt_raw_fops = { + .owner = THIS_MODULE, + .open = nvt_raw_open, + .read = seq_read, + .llseek = seq_lseek, + .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 (ts->carrier_system) { + if (nvt_get_fw_pipe() == 0) + nvt_read_mdata_rss(ts->mmap->DIFF_PIPE0_ADDR, ts->mmap->DIFF_PIPE0_Q_ADDR, + ts->mmap->DIFF_BTN_PIPE0_ADDR, ts->mmap->DIFF_BTN_PIPE0_Q_ADDR); + else + nvt_read_mdata_rss(ts->mmap->DIFF_PIPE1_ADDR, ts->mmap->DIFF_PIPE1_Q_ADDR, + ts->mmap->DIFF_BTN_PIPE1_ADDR, ts->mmap->DIFF_BTN_PIPE1_Q_ADDR); + } else { + 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 file_operations nvt_diff_fops = { + .owner = THIS_MODULE, + .open = nvt_diff_open, + .read = seq_read, + .llseek = seq_lseek, + .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/nvt_fw_version Failed!\n"); + return -ENOMEM; + } else { + NVT_LOG("create proc/nvt_fw_version Succeeded!\n"); + } + + NVT_proc_baseline_entry = proc_create(NVT_BASELINE, 0444, NULL, &nvt_baseline_fops); + if (NVT_proc_baseline_entry == NULL) { + NVT_ERR("create proc/nvt_baseline Failed!\n"); + return -ENOMEM; + } else { + NVT_LOG("create proc/nvt_baseline Succeeded!\n"); + } + + NVT_proc_raw_entry = proc_create(NVT_RAW, 0444, NULL, &nvt_raw_fops); + if (NVT_proc_raw_entry == NULL) { + NVT_ERR("create proc/nvt_raw Failed!\n"); + return -ENOMEM; + } else { + NVT_LOG("create proc/nvt_raw Succeeded!\n"); + } + + NVT_proc_diff_entry = proc_create(NVT_DIFF, 0444, NULL, &nvt_diff_fops); + if (NVT_proc_diff_entry == NULL) { + NVT_ERR("create proc/nvt_diff Failed!\n"); + return -ENOMEM; + } else { + NVT_LOG("create proc/nvt_diff Succeeded!\n"); + } + + return 0; +} + +void nvt_extra_proc_remove(void) +{ + if (NVT_proc_fw_version_entry != NULL) { + remove_proc_entry(NVT_FW_VERSION, NULL); + NVT_LOG("Removed %s under /proc\n", + NVT_FW_VERSION); + } + + if (NVT_proc_baseline_entry != NULL) { + remove_proc_entry(NVT_BASELINE, NULL); + NVT_LOG("Removed %s under /proc\n", + NVT_BASELINE); + } + + if (NVT_proc_raw_entry != NULL) { + remove_proc_entry(NVT_RAW, NULL); + NVT_LOG("Removed %s under /proc\n", + NVT_RAW); + } + + if (NVT_proc_diff_entry != NULL) { + remove_proc_entry(NVT_DIFF, NULL); + NVT_LOG("Removed %s under /proc\n", + NVT_DIFF); + } +} +#endif diff --git a/drivers/input/touchscreen/nova_36525_mmi/nt36xxx_fw_update.c b/drivers/input/touchscreen/nova_36525_mmi/nt36xxx_fw_update.c new file mode 100644 index 000000000000..4907a2412e99 --- /dev/null +++ b/drivers/input/touchscreen/nova_36525_mmi/nt36xxx_fw_update.c @@ -0,0 +1,1392 @@ +/* + * Copyright (C) 2010 - 2017 Novatek, Inc. + * + * $Revision: 20544 $ + * $Date: 2017-12-20 11:08:15 +0800 (Wed, 20 Dec 2017) $ + * + * 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 +#include + +#include "nt36xxx.h" + +#if (BOOT_UPDATE_FIRMWARE || NVT_TOUCH_FW) + +#define FW_BIN_SIZE_116KB 118784 +#define FW_BIN_SIZE FW_BIN_SIZE_116KB +#define FW_BIN_VER_OFFSET 0x1A000 +#define FW_BIN_VER_BAR_OFFSET 0x1A001 +#define FLASH_SECTOR_SIZE 4096 +#define SIZE_64KB 65536 +#define BLOCK_64KB_NUM 4 + +const struct firmware *fw_entry; + +/******************************************************* +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 -EPERM; + } + + 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 bin file size (116kb) + if (fw_entry->size != FW_BIN_SIZE) { + NVT_ERR("bin file size not match. (%zu)\n", fw_entry->size); + 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 + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + ret = CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 3); + 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 -EPERM; + } + } + 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 fw_bin_size = 0; + size_t len_in_blk = 0; + int32_t retry = 0; + + if (Resume_PD()) { + NVT_ERR("Resume PD error!!\n"); + return -EPERM; + } + + fw_bin_size = fw_entry->size; + + for (i = 0; i < BLOCK_64KB_NUM; i++) { + if (fw_bin_size > (i * SIZE_64KB)) { + // Calculate WR_Filechksum of each 64KB block + len_in_blk = min(fw_bin_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 -EPERM; + } + } + // Read Checksum (write addr high byte & middle byte) + buf[0] = 0xFF; + buf[1] = XDATA_Addr >> 16; + buf[2] = (XDATA_Addr >> 8) & 0xFF; + ret = CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 3); + 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 -EPERM; + } + } + + 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) { + mdelay(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 -EPERM; + } + } + + // 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) { + mdelay(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 -EPERM; + } + } + + // Read Status + retry = 0; + while (1) { + mdelay(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 -EPERM; + } + } + + if (fw_entry->size % FLASH_SECTOR_SIZE) + count = fw_entry->size / FLASH_SECTOR_SIZE + 1; + else + count = fw_entry->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) { + mdelay(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 -EPERM; + } + } + + 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) { + mdelay(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 -EPERM; + } + } + + // Read Status + retry = 0; + while (1) { + mdelay(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 -EPERM; + } + } + } + + 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; + + // change I2C buffer index + buf[0] = 0xFF; + buf[1] = XDATA_Addr >> 16; + buf[2] = (XDATA_Addr >> 8) & 0xFF; + ret = CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 3); + if (ret < 0) { + NVT_ERR("change I2C buffer index error!!(%d)\n", ret); + return ret; + } + + if (fw_entry->size % 256) + count = fw_entry->size / 256 + 1; + else + count = fw_entry->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 -EPERM; + } + } + + // Write Page : 256 bytes + for (j = 0; j < min(fw_entry->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_entry->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_entry->size - Flash_Address - 1); + + for (k = 0; k < min(fw_entry->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_entry->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) { + mdelay(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 -EPERM; + } + } + if (buf[1] == 0xEA) { + NVT_ERR("Page Program error!! i=%d\n", i); + return -ESRCH; + } + + // Read Status + retry = 0; + while (1) { + mdelay(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 -EPERM; + } + } + if (buf[1] == 0xEA) { + NVT_ERR("Page Program error!! i=%d\n", i); + return -EINTR; + } + + NVT_LOG("Programming...%2d%%\r", ((i * 100) / count)); + } + + NVT_LOG("Programming...%2d%%\r", 100); + 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 fw_bin_size = 0; + size_t len_in_blk = 0; + int32_t retry = 0; + + fw_bin_size = fw_entry->size; + + for (i = 0; i < BLOCK_64KB_NUM; i++) { + if (fw_bin_size > (i * SIZE_64KB)) { + // Calculate WR_Filechksum of each 64KB block + len_in_blk = min(fw_bin_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 -EPERM; + } + } + // Read Checksum (write addr high byte & middle byte) + buf[0] = 0xFF; + buf[1] = XDATA_Addr >> 16; + buf[2] = (XDATA_Addr >> 8) & 0xFF; + ret = CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 3); + 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 -EPERM; + } + } + } + + 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; + NVT_LOG("enter %s start\n", __func__); + //---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_LOG("exit %s end\n", __func__); + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen check flash end flag function. + +return: + Executive outcomes. 0---succeed. 1,negative---failed. +*******************************************************/ +#define NVT_FLASH_END_FLAG_LEN 3 +#define NVT_FLASH_END_FLAG_ADDR 0x1AFFD +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 -EPERM; + } + + msleep(10); + + //Step 5 : Read Flash Data + buf[0] = 0xFF; + buf[1] = (ts->mmap->READ_FLASH_CHECKSUM_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->READ_FLASH_CHECKSUM_ADDR >> 8) & 0xFF; + ret = CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 3); + 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 (strncmp(nvt_end_flag, "NVT", 3) == 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] = ""; + NVT_LOG("enter %s start\n",__func__); + if (ts->nvt_pid == PANNEL_ID) { + snprintf(firmware_name, sizeof(firmware_name), NT36525B_FIRMWARE_NAME); + } else { + 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; + } + NVT_LOG("firmware name :%s\n", firmware_name); + mutex_lock(&ts->lock); + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif /* #if NVT_TOUCH_ESD_PROTECT */ +#if NVT_TOUCH_FW + ts->loading_fw = 1; +#endif + + 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(); + nvt_check_fw_reset_state(RESET_STATE_INIT); + } + +#if NVT_TOUCH_FW + ts->loading_fw = 0; +#endif + mutex_unlock(&ts->lock); + + update_firmware_release(); +} +#endif /* BOOT_UPDATE_FIRMWARE */ + +#if NVT_TOUCH_FW +static ssize_t nvt_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + bool val; + + mutex_lock(&ts->lock); + val = ts->suspended; + mutex_unlock(&ts->lock); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + val == 0); +} + +static DEVICE_ATTR(poweron, 0444, nvt_poweron_show, NULL); + +static ssize_t nvt_ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "[FW]%02x,[IC]NT36525\n", + ts->fw_ver); +} + +static DEVICE_ATTR(ic_ver, 0444, nvt_ic_ver_show, NULL); + +static ssize_t nvt_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +/* Update script use as $touch_product_id in fw name ==> (chip id from Lenovo&script) OR (vendor name from Focaltech) OR (project ID from Longcheer&Himax) + Project ID ?? ( - Jeter - Hannah) + Chip ID ?? (NVT-ts or NVTCapacitiveTouchScreen or NT36xxx) */ + return scnprintf(buf, PAGE_SIZE, "%s\n", + ts->product_id); +} + +static DEVICE_ATTR(productinfo, 0444, nvt_productinfo_show, NULL); + +static ssize_t nvt_force_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + ts->force_reflash = (input == 0) ? false : true; + + return count; +} + +static DEVICE_ATTR(forcereflash, 0220, NULL, nvt_force_reflash_store); + +static ssize_t nvt_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", + (ts->loading_fw) ? 1 : 0); +} + +static DEVICE_ATTR(flashprog, 0444, nvt_flashprog_show, NULL); + +static ssize_t nvt_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + char prefix[FW_NAME_MAX_LEN] = VENDOR_NAME; + char template[FW_NAME_MAX_LEN]; + char firmware_name[256] = ""; + + if (count > FW_NAME_MAX_LEN) { + NVT_ERR("FW filename is too long\n"); + retval = -EINVAL; + goto exit; + } + + if (ts->suspended) { + NVT_ERR("In suspend state, try again later\n"); + retval = -EINVAL; + goto exit; + } + + if (ts->loading_fw) { + NVT_ERR("In FW flashing state, try again later\n"); + retval = -EINVAL; + goto exit; + } + + if (!ts->force_reflash) { + if (strncmp(buf, prefix, + strnlen(prefix, sizeof(prefix)))) { + NVT_ERR("FW does not belong to Novatek\n"); + retval = -EINVAL; + goto exit; + } + + snprintf(template, sizeof(template), "-%s-", + ts->product_id); + if (!strnstr(buf + strnlen(prefix, sizeof(prefix)), template, + count)) { + NVT_ERR("FW does not belong to %s\n", ts->product_id); + retval = -EINVAL; + goto exit; + } + } + + strlcpy(firmware_name, buf, count); + NVT_LOG("FW filename: %s\n", firmware_name); + + retval = update_firmware_request(firmware_name); + if (retval) { + NVT_ERR("update_firmware_request failed. (%d)\n", retval); + goto exit; + } + + mutex_lock(&ts->lock); +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif + ts->loading_fw = 1; + nvt_sw_reset_idle(); + + retval = Check_CheckSum(); + + if (retval < 0) { + NVT_ERR("read firmware checksum failed\n"); + Update_Firmware(); + } else if ((retval == 0) && (Check_FW_Ver() == 0)) { + 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(); + nvt_check_fw_reset_state(RESET_STATE_INIT); + } + + ts->loading_fw = 0; + mutex_unlock(&ts->lock); + + update_firmware_release(); + + retval = count; +exit: + return retval; +} + +static DEVICE_ATTR(doreflash, 0220, NULL, nvt_do_reflash_store); + +static ssize_t nvt_build_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +/* Update script use for comparing $str_cfg_id_latest in fw name */ + uint8_t tmp_buf[4] = {0}; + uint8_t fw_version = 0; + uint8_t date_Y = 0; + uint8_t date_M = 0; + uint8_t date_D = 0; + + tmp_buf[0] = 0xFF; + tmp_buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + tmp_buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, tmp_buf, 3); + + tmp_buf[0] = EVENT_MAP_FWINFO; + CTP_I2C_READ(ts->client, I2C_FW_Address, tmp_buf, 2); + fw_version = tmp_buf[1]; + + tmp_buf[0] = EVENT_MAP_FWDATE; + CTP_I2C_READ(ts->client, I2C_FW_Address, tmp_buf, 4); + date_Y = tmp_buf[1]; + date_M = tmp_buf[2]; + date_D = tmp_buf[3]; + + return scnprintf(buf, PAGE_SIZE, "%02x-%02d%02d%02d\n", + fw_version, date_Y, date_M, date_D); +} + +static DEVICE_ATTR(buildid, 0444, nvt_build_id_show, NULL); + + +#include +#include +#include + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t blen; + const char *path; + + path = kobject_get_path(&ts->client->dev.kobj, GFP_KERNEL); + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, VENDOR_NAME); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_NULL +}; + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +/******************************************************* +Description: + Novatek touchscreen FW function class. file node + initial function. + +return: + Executive outcomes. 0---succeed. -1---failed. +*******************************************************/ +int32_t nvt_fw_class_init(bool create) +{ + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + static struct class *touchscreen_class; + static struct device *ts_class_dev; + static int minor; + + if (create) { + minor = input_get_new_minor(ts->client->addr, + 1, false); + if (minor < 0) + minor = input_get_new_minor(TSDEV_MINOR_BASE, + TSDEV_MINOR_MAX, true); + NVT_LOG("assigned minor %d\n", minor); + + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + + ts_class_dev = device_create(touchscreen_class, NULL, + MKDEV(INPUT_MAJOR, minor), + ts, NVT_I2C_NAME); + if (IS_ERR(ts_class_dev)) { + error = PTR_ERR(ts_class_dev); + ts_class_dev = NULL; + return error; + } + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + error = device_create_file(ts_class_dev, &attrs[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + else + NVT_LOG("create /sys/class/touchscreen/%s Succeeded!\n", NVT_I2C_NAME); + } else { + if (!touchscreen_class || !ts_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(ts_class_dev, &attrs[i]); + + device_unregister(ts_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor)); + ts_class_dev = NULL; + class_unregister(touchscreen_class); + NVT_ERR("error creating touchscreen class\n"); + + return -ENODEV; +} + +int nvt_fw_sysfs_init(void) +{ + int ret; + struct i2c_client *client = ts->client; + + ret = nvt_fw_class_init(true); + if (ret != 0) { + NVT_ERR("fw class init failed. ret=%d\n", ret); + return ret; + } + + ret = device_create_file(&client->dev, &dev_attr_forcereflash); + if (ret) { + NVT_ERR("create_file dev_attr_forcereflash failed\n"); + return ret; + } + + ret = device_create_file(&client->dev, &dev_attr_flashprog); + if (ret) { + NVT_ERR("create_file dev_attr_flashprog failed\n"); + return ret; + } + ret = device_create_file(&client->dev, &dev_attr_doreflash); + if (ret) { + NVT_ERR("create_file dev_attr_doreflash failed\n"); + return ret; + } + + ret = device_create_file(&client->dev, &dev_attr_buildid); + if (ret) { + NVT_ERR("create_file dev_attr_buildid failed\n"); + return ret; + } + + ret = device_create_file(&client->dev, &dev_attr_productinfo); + if (ret) { + NVT_ERR("create_file dev_attr_productinfo failed\n"); + return ret; + } + + ret = device_create_file(&client->dev, &dev_attr_poweron); + if (ret) { + NVT_ERR("create_file dev_attr_poweron failed\n"); + return ret; + } + + ret = device_create_file(&client->dev, &dev_attr_ic_ver); + if (ret) { + NVT_ERR("create_file dev_attr_ic_ver failed\n"); + return ret; + } + + return 0; +} + +void nvt_fw_sysfs_deinit(void) +{ + struct i2c_client *client = ts->client; + device_remove_file(&client->dev, &dev_attr_forcereflash); + device_remove_file(&client->dev, &dev_attr_flashprog); + device_remove_file(&client->dev, &dev_attr_doreflash); + device_remove_file(&client->dev, &dev_attr_buildid); + device_remove_file(&client->dev, &dev_attr_productinfo); + device_remove_file(&client->dev, &dev_attr_poweron); + device_remove_file(&client->dev, &dev_attr_ic_ver); + nvt_fw_class_init(false); +} +#endif diff --git a/drivers/input/touchscreen/nova_36525_mmi/nt36xxx_mem_map.h b/drivers/input/touchscreen/nova_36525_mmi/nt36xxx_mem_map.h new file mode 100644 index 000000000000..678a266d8d2d --- /dev/null +++ b/drivers/input/touchscreen/nova_36525_mmi/nt36xxx_mem_map.h @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2010 - 2017 Novatek, Inc. + * + * $Revision: 20544 $ + * $Date: 2017-12-20 11:08:15 +0800 (Wed, 20 Dec 2017) $ + * + * 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. + * + */ + +struct nvt_ts_mem_map { + uint32_t EVENT_BUF_ADDR; + uint32_t RAW_PIPE0_ADDR; + uint32_t RAW_PIPE0_Q_ADDR; + uint32_t RAW_PIPE1_ADDR; + uint32_t RAW_PIPE1_Q_ADDR; + uint32_t BASELINE_ADDR; + uint32_t BASELINE_Q_ADDR; + uint32_t BASELINE_BTN_ADDR; + uint32_t BASELINE_BTN_Q_ADDR; + uint32_t DIFF_PIPE0_ADDR; + uint32_t DIFF_PIPE0_Q_ADDR; + uint32_t DIFF_PIPE1_ADDR; + uint32_t DIFF_PIPE1_Q_ADDR; + uint32_t RAW_BTN_PIPE0_ADDR; + uint32_t RAW_BTN_PIPE0_Q_ADDR; + uint32_t RAW_BTN_PIPE1_ADDR; + uint32_t RAW_BTN_PIPE1_Q_ADDR; + uint32_t DIFF_BTN_PIPE0_ADDR; + uint32_t DIFF_BTN_PIPE0_Q_ADDR; + uint32_t DIFF_BTN_PIPE1_ADDR; + uint32_t DIFF_BTN_PIPE1_Q_ADDR; + uint32_t READ_FLASH_CHECKSUM_ADDR; + uint32_t RW_FLASH_DATA_ADDR; +}; + +static const struct nvt_ts_mem_map NT36525B_memory_map = { + .EVENT_BUF_ADDR = 0x21C00, + .RAW_PIPE0_ADDR = 0x20000, + .RAW_PIPE0_Q_ADDR = 0, + .RAW_PIPE1_ADDR = 0x23000, + .RAW_PIPE1_Q_ADDR = 0, + .BASELINE_ADDR = 0x20BFC, + .BASELINE_Q_ADDR = 0, + .BASELINE_BTN_ADDR = 0x23BFC, + .BASELINE_BTN_Q_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x206DC, + .DIFF_PIPE0_Q_ADDR = 0, + .DIFF_PIPE1_ADDR = 0x236DC, + .DIFF_PIPE1_Q_ADDR = 0, + .RAW_BTN_PIPE0_ADDR = 0x20510, + .RAW_BTN_PIPE0_Q_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0x23510, + .RAW_BTN_PIPE1_Q_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0x20BF0, + .DIFF_BTN_PIPE0_Q_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0x23BF0, + .DIFF_BTN_PIPE1_Q_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_PIPE0_Q_ADDR = 0, + .RAW_PIPE1_ADDR = 0x23000, + .RAW_PIPE1_Q_ADDR = 0, + .BASELINE_ADDR = 0x20BFC, + .BASELINE_Q_ADDR = 0, + .BASELINE_BTN_ADDR = 0x23BFC, + .BASELINE_BTN_Q_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x206DC, + .DIFF_PIPE0_Q_ADDR = 0, + .DIFF_PIPE1_ADDR = 0x236DC, + .DIFF_PIPE1_Q_ADDR = 0, + .RAW_BTN_PIPE0_ADDR = 0x20510, + .RAW_BTN_PIPE0_Q_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0x23510, + .RAW_BTN_PIPE1_Q_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0x20BF0, + .DIFF_BTN_PIPE0_Q_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0x23BF0, + .DIFF_BTN_PIPE1_Q_ADDR = 0, + .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_PIPE0_Q_ADDR = 0, + .RAW_PIPE1_ADDR = 0x12000, + .RAW_PIPE1_Q_ADDR = 0, + .BASELINE_ADDR = 0x10E70, + .BASELINE_Q_ADDR = 0, + .BASELINE_BTN_ADDR = 0x12E70, + .BASELINE_BTN_Q_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x10830, + .DIFF_PIPE0_Q_ADDR = 0, + .DIFF_PIPE1_ADDR = 0x12830, + .DIFF_PIPE1_Q_ADDR = 0, + .RAW_BTN_PIPE0_ADDR = 0x10E60, + .RAW_BTN_PIPE0_Q_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0x12E60, + .RAW_BTN_PIPE1_Q_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0x10E68, + .DIFF_BTN_PIPE0_Q_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0x12E68, + .DIFF_BTN_PIPE1_Q_ADDR = 0, + .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_PIPE0_Q_ADDR = 0, + .RAW_PIPE1_ADDR = 0x12000, + .RAW_PIPE1_Q_ADDR = 0, + .BASELINE_ADDR = 0x10B08, + .BASELINE_Q_ADDR = 0, + .BASELINE_BTN_ADDR = 0x12B08, + .BASELINE_BTN_Q_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x1064C, + .DIFF_PIPE0_Q_ADDR = 0, + .DIFF_PIPE1_ADDR = 0x1264C, + .DIFF_PIPE1_Q_ADDR = 0, + .RAW_BTN_PIPE0_ADDR = 0x10634, + .RAW_BTN_PIPE0_Q_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0x12634, + .RAW_BTN_PIPE1_Q_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0x10AFC, + .DIFF_BTN_PIPE0_Q_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0x12AFC, + .DIFF_BTN_PIPE1_Q_ADDR = 0, + .READ_FLASH_CHECKSUM_ADDR = 0x14000, + .RW_FLASH_DATA_ADDR = 0x14002, +}; + +static const struct nvt_ts_mem_map NT36870_memory_map = { + .EVENT_BUF_ADDR = 0x25000, + .RAW_PIPE0_ADDR = 0x20000, + .RAW_PIPE0_Q_ADDR = 0x204C8, + .RAW_PIPE1_ADDR = 0x23000, + .RAW_PIPE1_Q_ADDR = 0x234C8, + .BASELINE_ADDR = 0x21350, + .BASELINE_Q_ADDR = 0x21818, + .BASELINE_BTN_ADDR = 0x24350, + .BASELINE_BTN_Q_ADDR = 0x24358, + .DIFF_PIPE0_ADDR = 0x209B0, + .DIFF_PIPE0_Q_ADDR = 0x20E78, + .DIFF_PIPE1_ADDR = 0x239B0, + .DIFF_PIPE1_Q_ADDR = 0x23E78, + .RAW_BTN_PIPE0_ADDR = 0x20990, + .RAW_BTN_PIPE0_Q_ADDR = 0x20998, + .RAW_BTN_PIPE1_ADDR = 0x23990, + .RAW_BTN_PIPE1_Q_ADDR = 0x23998, + .DIFF_BTN_PIPE0_ADDR = 0x21340, + .DIFF_BTN_PIPE0_Q_ADDR = 0x21348, + .DIFF_BTN_PIPE1_ADDR = 0x24340, + .DIFF_BTN_PIPE1_Q_ADDR = 0x24348, + .READ_FLASH_CHECKSUM_ADDR = 0x24000, + .RW_FLASH_DATA_ADDR = 0x24002, +}; + +static const struct nvt_ts_mem_map NT36676F_memory_map = { + .EVENT_BUF_ADDR = 0x11A00, + .RAW_PIPE0_ADDR = 0x10000, + .RAW_PIPE0_Q_ADDR = 0, + .RAW_PIPE1_ADDR = 0x12000, + .RAW_PIPE1_Q_ADDR = 0, + .BASELINE_ADDR = 0x10B08, + .BASELINE_Q_ADDR = 0, + .BASELINE_BTN_ADDR = 0x12B08, + .BASELINE_BTN_Q_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x1064C, + .DIFF_PIPE0_Q_ADDR = 0, + .DIFF_PIPE1_ADDR = 0x1264C, + .DIFF_PIPE1_Q_ADDR = 0, + .RAW_BTN_PIPE0_ADDR = 0x10634, + .RAW_BTN_PIPE0_Q_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0x12634, + .RAW_BTN_PIPE1_Q_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0x10AFC, + .DIFF_BTN_PIPE0_Q_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0x12AFC, + .DIFF_BTN_PIPE1_Q_ADDR = 0, + .READ_FLASH_CHECKSUM_ADDR = 0x14000, + .RW_FLASH_DATA_ADDR = 0x14002, +}; + +#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; + uint8_t carrier_system; +}; + +static const struct nvt_ts_trim_id_table trim_id_table[] = { + {.id = {0x0B, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36525B_memory_map, .carrier_system = 0}, + {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .carrier_system = 0}, + {.id = {0x55, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .carrier_system = 0}, + {.id = {0x55, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .carrier_system = 0}, + {.id = {0xAA, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .carrier_system = 0}, + {.id = {0xAA, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .carrier_system = 0}, + {.id = {0xFF, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .carrier_system = 0}, + {.id = {0xFF, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .carrier_system = 0}, + {.id = {0xFF, 0xFF, 0xFF, 0x70, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .carrier_system = 0}, + {.id = {0xFF, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .carrier_system = 0}, + {.id = {0xFF, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36525_memory_map, .carrier_system = 0}, + {.id = {0xFF, 0xFF, 0xFF, 0x70, 0x68, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36870_memory_map, .carrier_system = 1}, + {.id = {0xFF, 0xFF, 0xFF, 0x76, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36676F_memory_map, .carrier_system = 0} +}; diff --git a/drivers/input/touchscreen/nova_36525_mmi/nt36xxx_mp_ctrlram.c b/drivers/input/touchscreen/nova_36525_mmi/nt36xxx_mp_ctrlram.c new file mode 100644 index 000000000000..118e42795bff --- /dev/null +++ b/drivers/input/touchscreen/nova_36525_mmi/nt36xxx_mp_ctrlram.c @@ -0,0 +1,1916 @@ +/* + * Copyright (C) 2010 - 2017 Novatek, Inc. + * + * $Revision: 21288 $ + * $Date: 2018-01-05 11:38:47 +0800 (Fri, 05 Jan 2018) $ + * + * 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 +#include +#include +#include +#include + +#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; +static uint8_t *RecordResult_Short_Diff; +static uint8_t *RecordResult_Short_Base; +static uint8_t *RecordResult_Open; +static uint8_t *RecordResult_FWMutual; +static uint8_t *RecordResult_FW_CC; +static uint8_t *RecordResult_FW_CC_I; +static uint8_t *RecordResult_FW_CC_Q; +static uint8_t *RecordResult_FW_DiffMax; +static uint8_t *RecordResult_FW_DiffMin; + +static int32_t TestResult_Short; +static int32_t TestResult_Short_Diff; +static int32_t TestResult_Short_Base; +static int32_t TestResult_Open; +static int32_t TestResult_FW_Rawdata; +static int32_t TestResult_FWMutual; +static int32_t TestResult_FW_CC; +static int32_t TestResult_FW_CC_I; +static int32_t TestResult_FW_CC_Q; +static int32_t TestResult_Noise; +static int32_t TestResult_FW_DiffMax; +static int32_t TestResult_FW_DiffMin; + +static int32_t *RawData_Short; +static int32_t *RawData_Short_Diff; +static int32_t *RawData_Short_Base; +static int32_t *RawData_Open; +static int32_t *RawData_Diff; +static int32_t *RawData_Diff_Min; +static int32_t *RawData_Diff_Max; +static int32_t *RawData_FWMutual; +static int32_t *RawData_FW_CC; +static int32_t *RawData_FW_CC_I; +static int32_t *RawData_FW_CC_Q; + +static struct proc_dir_entry *NVT_proc_selftest_entry; +#if NVT_TOUCH_MP_LENOVO +static struct proc_dir_entry *NVT_proc_selftest_read_data; +#endif +static int8_t nvt_mp_test_result_printed; + +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); +void nvt_mp_parse_dt(struct device_node *root, const char *node_compatible); +extern int32_t nvt_read_mass_data(uint32_t xdata_addr, uint8_t *temp_buf, uint32_t len); + +/******************************************************* +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 = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_Short) { + NVT_ERR("kzalloc for RecordResult_Short failed!\n"); + return -ENOMEM; + } + + RecordResult_Short_Diff = RecordResult_Short; + + RecordResult_Short_Base = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_Short_Base) { + NVT_ERR("kzalloc for RecordResult_Short_Base failed!\n"); + return -ENOMEM; + } + + RecordResult_Open = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_Open) { + NVT_ERR("kzalloc for RecordResult_Open failed!\n"); + return -ENOMEM; + } + + RecordResult_FWMutual = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FWMutual) { + NVT_ERR("kzalloc for RecordResult_FWMutual failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_CC = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_CC) { + NVT_ERR("kzalloc for RecordResult_FW_CC failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_CC_I = RecordResult_FW_CC; + + RecordResult_FW_CC_Q = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_CC_Q) { + NVT_ERR("kzalloc for RecordResult_FW_CC_Q failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_DiffMax = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_DiffMax) { + NVT_ERR("kzalloc for RecordResult_FW_DiffMax failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_DiffMin = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_DiffMin) { + NVT_ERR("kzalloc for RecordResult_FW_DiffMin failed!\n"); + return -ENOMEM; + } + + RawData_Short = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Short) { + NVT_ERR("kzalloc for RawData_Short failed!\n"); + return -ENOMEM; + } + + RawData_Short_Diff = RawData_Short; + + RawData_Short_Base = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Short_Base) { + NVT_ERR("kzalloc for RawData_Short_Base failed!\n"); + return -ENOMEM; + } + + RawData_Open = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Open) { + NVT_ERR("kzalloc for RawData_Open failed!\n"); + return -ENOMEM; + } + + RawData_Diff = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Diff) { + NVT_ERR("kzalloc for RawData_Diff failed!\n"); + return -ENOMEM; + } + + RawData_Diff_Min = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Diff_Min) { + NVT_ERR("kzalloc for RawData_Diff_Min failed!\n"); + return -ENOMEM; + } + + RawData_Diff_Max = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Diff_Max) { + NVT_ERR("kzalloc for RawData_Diff_Max failed!\n"); + return -ENOMEM; + } + + RawData_FWMutual = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_FWMutual) { + NVT_ERR("kzalloc for RawData_FWMutual failed!\n"); + return -ENOMEM; + } + + RawData_FW_CC = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_FW_CC) { + NVT_ERR("kzalloc for RawData_FW_CC failed!\n"); + return -ENOMEM; + } + + RawData_FW_CC_I = RawData_FW_CC; + + RawData_FW_CC_Q = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_FW_CC_Q) { + NVT_ERR("kzalloc for RawData_FW_CC_Q failed!\n"); + return -ENOMEM; + } + + return 0; +} + +/******************************************************* +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 i = 0; + int32_t j = 0; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + for (j = 0; j < y_ch; j++) { + for (i = 0; i < x_ch; i++) { + printk("%5d, ", array[j * x_ch + i]); + } + printk("\n"); + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + printk("%5d, ", array[y_ch * x_ch + k]); + } + printk("\n"); +#endif /* #if TOUCH_KEY_NUM > 0 */ +} + +static void nvt_print_criteria(void) +{ + NVT_LOG("++\n"); + + if (ts->carrier_system) { + //---PS_Config_Lmt_Short_Diff--- + printk("PS_Config_Lmt_Short_Diff_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Short_Diff_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_Short_Diff_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Short_Diff_N, X_Channel, Y_Channel); + //---PS_Config_Lmt_Short_Base--- + printk("PS_Config_Lmt_Short_Base_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Short_Base_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_Short_Base_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Short_Base_N, X_Channel, Y_Channel); + } else { + //---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); + + if (ts->carrier_system) { + //---PS_Config_Lmt_FW_CC_I--- + printk("PS_Config_Lmt_FW_CC_I_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_CC_I_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_FW_CC_I_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_CC_I_N, X_Channel, Y_Channel); + //---PS_Config_Lmt_FW_CC_Q--- + printk("PS_Config_Lmt_FW_CC_Q_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_CC_Q_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_FW_CC_Q_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_CC_Q_N, X_Channel, Y_Channel); + } else { + //---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_save_rawdata_to_csv(int32_t *rawdata, uint8_t x_ch, uint8_t y_ch, const char *file_path, uint32_t offset) +{ + int32_t x = 0; + int32_t y = 0; + int32_t iArrayIndex = 0; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + printk("%s:++%s\n", __func__, file_path); + + for (y = 0; y < y_ch; y++) { + for (x = 0; x < x_ch; x++) { + iArrayIndex = y * x_ch + x; + printk("%5d, ", rawdata[iArrayIndex]); + } + printk("\n"); + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = y_ch * x_ch + k; + printk("%5d, ", rawdata[iArrayIndex]); + } + printk("\n"); +#endif /* #if TOUCH_KEY_NUM > 0 */ + printk("%s:--\n", __func__); + return 0; +} + +static int32_t nvt_polling_hand_shake_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--- + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + //---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; + + msleep(10); + } + + 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 + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + 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 -EPERM; + } 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--- + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + //---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; + if (ts->carrier_system) { + xdata[iArrayIndex] = (uint16_t)xdata[iArrayIndex]; + } else { + xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex]; + } + } + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = Y_Channel * X_Channel + k; + if (ts->carrier_system) { + xdata[iArrayIndex] = (uint16_t)xdata[iArrayIndex]; + } else { + xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex]; + } + } +#endif /* #if TOUCH_KEY_NUM > 0 */ + + printk("%s:\n", __func__); + // Save Rawdata to CSV file + if (nvt_save_rawdata_to_csv(xdata, X_Channel, Y_Channel, FW_RAWDATA_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + + 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; + int32_t xdata_tmp = 0; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + uint32_t rawdata_cc_q_offset = 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; + if (ts->carrier_system) { + xdata_tmp = xdata[iArrayIndex]; + RawData_FW_CC_I[iArrayIndex] = (uint8_t)(xdata_tmp & 0xFF); + RawData_FW_CC_Q[iArrayIndex] = (uint8_t)((xdata_tmp >> 8) & 0xFF); + } else { + xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex]; + } + } + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = Y_Channel * X_Channel + k; + if (ts->carrier_system) { + xdata_tmp = xdata[iArrayIndex]; + RawData_FW_CC_I[iArrayIndex] = (uint8_t)(xdata_tmp & 0xFF); + RawData_FW_CC_Q[iArrayIndex] = (uint8_t)((xdata_tmp >> 8) & 0xFF); + } else { + xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex]; + } + } +#endif /* #if TOUCH_KEY_NUM > 0 */ + + printk("%s:\n", __func__); + if (ts->carrier_system) { + printk("%s:RawData_CC_I:\n", __func__); + // Save Rawdata to CSV file + if (nvt_save_rawdata_to_csv(RawData_FW_CC_I, X_Channel, Y_Channel, FW_CC_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } +#if TOUCH_KEY_NUM > 0 + rawdata_cc_q_offset = Y_Channel * X_Channel * 7 + Y_Channel * 2 + Key_Channel * 7 + 2; +#else + rawdata_cc_q_offset = Y_Channel * X_Channel * 7 + Y_Channel * 2; +#endif /* #if TOUCH_KEY_NUM > 0 */ + printk("%s:RawData_CC_Q:\n", __func__); + // Save Rawdata to CSV file + if (nvt_save_rawdata_to_csv(RawData_FW_CC_Q, X_Channel, Y_Channel, FW_CC_CSV_FILE, rawdata_cc_q_offset) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + } else { + // Save Rawdata to CSV file + if (nvt_save_rawdata_to_csv(xdata, X_Channel, Y_Channel, FW_CC_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + } + + 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--- + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + //---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; + uint32_t rawdata_diff_min_offset = 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; + if (ts->carrier_system) { + RawData_Diff_Max[iArrayIndex] = (uint16_t)xdata[iArrayIndex]; + RawData_Diff_Min[iArrayIndex] = 0; + } else { + 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; + if (ts->carrier_system) { + RawData_Diff_Max[iArrayIndex] = (uint16_t)xdata[iArrayIndex]; + RawData_Diff_Min[iArrayIndex] = 0; + } else { + 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); + + printk("%s:RawData_Diff_Max:\n", __func__); + // Save Rawdata to CSV file + if (nvt_save_rawdata_to_csv(RawData_Diff_Max, X_Channel, Y_Channel, NOISE_TEST_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + + if (!ts->carrier_system) { +#if TOUCH_KEY_NUM > 0 + rawdata_diff_min_offset = Y_Channel * X_Channel * 7 + Y_Channel * 2 + Key_Channel * 7 + 2; +#else + rawdata_diff_min_offset = Y_Channel * X_Channel * 7 + Y_Channel * 2; +#endif /* #if TOUCH_KEY_NUM > 0 */ + printk("%s:RawData_Diff_Min:\n", __func__); + // Save Rawdata to CSV file + if (nvt_save_rawdata_to_csv(RawData_Diff_Min, X_Channel, Y_Channel, NOISE_TEST_CSV_FILE, rawdata_diff_min_offset) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + } + + NVT_LOG("--\n"); + + return 0; +} + +static void nvt_enable_open_test(void) +{ + uint8_t buf[8] = {0}; + + //---set xdata index to EVENT BUF ADDR--- + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + //---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--- + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + //---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 = (uint8_t *)kzalloc((IC_X_CFG_SIZE * IC_Y_CFG_SIZE + IC_KEY_CFG_SIZE) * 2, GFP_KERNEL); +#else + rawdata_buf = (uint8_t *)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--- + buf[0] = 0xFF; + buf[1] = (uint8_t)(((raw_pipe_addr + y * IC_X_CFG_SIZE * 2) >> 16) & 0xFF); + buf[2] = (uint8_t)(((raw_pipe_addr + y * IC_X_CFG_SIZE * 2) >> 8) & 0xFF); + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + 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--- + buf[0] = 0xFF; + buf[1] = (uint8_t)((raw_btn_pipe_addr >> 16) & 0xFF); + buf[2] = (uint8_t)((raw_btn_pipe_addr >> 8) & 0xFF); + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + 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); + + + printk("%s:RawData_Open\n", __func__); + // Save RawData to CSV file + if (nvt_save_rawdata_to_csv(xdata, X_Channel, Y_Channel, OPEN_TEST_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + + 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 */ + uint32_t rawdata_short_base_offset = 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 = (uint8_t *)kzalloc((X_Channel * Y_Channel + Key_Channel) * 2, GFP_KERNEL); +#else + rawdata_buf = (uint8_t *)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 (ts->carrier_system) { + // to get short diff rawdata at pipe0 + raw_pipe_addr = ts->mmap->RAW_PIPE0_ADDR; + } else { + 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--- + buf[0] = 0xFF; + buf[1] = (uint8_t)(((raw_pipe_addr + y * X_Channel * 2) >> 16) & 0xFF); + buf[2] = (uint8_t)(((raw_pipe_addr + y * X_Channel * 2) >> 8) & 0xFF); + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + 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 (ts->carrier_system) { + // to get button short diff rawdata at pipe0 + raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE0_ADDR; + } else { + 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--- + buf[0] = 0xFF; + buf[1] = (uint8_t)((raw_btn_pipe_addr >> 16) & 0xFF); + buf[2] = (uint8_t)((raw_btn_pipe_addr >> 8) & 0xFF); + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + 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 */ + + // for carrier sensing system to get short baseline rawdata + if (ts->carrier_system) { + // to get short baseline rawdata at pipe1 + raw_pipe_addr = ts->mmap->RAW_PIPE1_ADDR; + + for (y = 0; y < Y_Channel; y++) { + //---change xdata index--- + buf[0] = 0xFF; + buf[1] = (uint8_t)(((raw_pipe_addr + y * X_Channel * 2) >> 16) & 0xFF); + buf[2] = (uint8_t)(((raw_pipe_addr + y * X_Channel * 2) >> 8) & 0xFF); + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + 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 + // to get button short baseline rawdata at pipe1 + raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE1_ADDR; + + //---change xdata index--- + buf[0] = 0xFF; + buf[1] = (uint8_t)((raw_btn_pipe_addr >> 16) & 0xFF); + buf[2] = (uint8_t)((raw_btn_pipe_addr >> 8) & 0xFF); + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + 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; + RawData_Short_Base[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; + RawData_Short_Base[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); + + if (ts->carrier_system) + printk("%s:RawData_Short_Diff:\n", __func__); + else + printk("%s:RawData_Short\n", __func__); + // Save Rawdata to CSV file + if (nvt_save_rawdata_to_csv(xdata, X_Channel, Y_Channel, SHORT_TEST_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + if (ts->carrier_system) { +#if TOUCH_KEY_NUM > 0 + rawdata_short_base_offset = Y_Channel * X_Channel * 7 + Y_Channel * 2 + Key_Channel * 7 + 2; +#else + rawdata_short_base_offset = Y_Channel * X_Channel * 7 + Y_Channel * 2; +#endif /* #if TOUCH_KEY_NUM > 0 */ + printk("%s:RawData_Short_Base:\n", __func__); + // Save Rawdata to CSV file + if (nvt_save_rawdata_to_csv(RawData_Short_Base, X_Channel, Y_Channel, SHORT_TEST_CSV_FILE, rawdata_short_base_offset) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + } + + 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 == false) { + return -EPERM; + } else { + return 0; + } +} + +/******************************************************* +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; + nvt_mp_seq_printf(m, "0x%02X, ", RecordResult[iArrayIndex]); + } + nvt_mp_seq_printf(m, "\n"); + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = y_len * x_len + k; + nvt_mp_seq_printf(m, "0x%02X, ", RecordResult[iArrayIndex]); + } + 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; + nvt_mp_seq_printf(m, "%5d, ", rawdata[iArrayIndex]); + } + nvt_mp_seq_printf(m, "\n"); + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = y_len * x_len + k; + nvt_mp_seq_printf(m, "%5d, ", rawdata[iArrayIndex]); + } + 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) +{ +#if NVT_TOUCH_MP_LENOVO + MP_TEST_RESULT mp_result = 0; +#endif + + NVT_LOG("++\n"); +#if !NVT_TOUCH_MP_LENOVO + nvt_mp_seq_printf(m, "FW Version: %d\n\n", ts->fw_ver); + + nvt_mp_seq_printf(m, "Short Test"); + if ((TestResult_Short == 0) || (TestResult_Short == 1)) { + print_selftest_result(m, TestResult_Short, RecordResult_Short, RawData_Short, X_Channel, Y_Channel); + } else { // TestResult_Short is -1 + if (ts->carrier_system) { + nvt_mp_seq_printf(m, " FAIL!\n"); + if (TestResult_Short_Diff == -1) { + nvt_mp_seq_printf(m, "Short Diff"); + print_selftest_result(m, TestResult_Short_Diff, RecordResult_Short_Diff, RawData_Short_Diff, X_Channel, Y_Channel); + } + if (TestResult_Short_Base == -1) { + nvt_mp_seq_printf(m, "Short Base"); + print_selftest_result(m, TestResult_Short_Base, RecordResult_Short_Base, RawData_Short_Base, X_Channel, Y_Channel); + } + } else { + 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) { + if (ts->carrier_system) { + if (TestResult_FW_CC_I == -1) { + nvt_mp_seq_printf(m, "FW CC_I"); + print_selftest_result(m, TestResult_FW_CC_I, RecordResult_FW_CC_I, RawData_FW_CC_I, X_Channel, Y_Channel); + } + if (TestResult_FW_CC_Q == -1) { + nvt_mp_seq_printf(m, "FW CC_Q"); + print_selftest_result(m, TestResult_FW_CC_Q, RecordResult_FW_CC_Q, RawData_FW_CC_Q, X_Channel, Y_Channel); + } + } else { + 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); + } + } +#else + /* short */ + if (TestResult_Short >= 0) + mp_result |= (!!TestResult_Short << MP_RESULT_SHIFT_SHORT); + else { + if (ts->carrier_system) { + mp_result |= (!!TestResult_Short_Diff << MP_RESULT_SHIFT_SHORT_DIFF); + mp_result |= (!!TestResult_Short_Base << MP_RESULT_SHIFT_SHORT_BASE); + } else { + mp_result |= (!!TestResult_Short << MP_RESULT_SHIFT_SHORT); + } + } + + /* open */ + mp_result |= (!!TestResult_Open << MP_RESULT_SHIFT_OPEN); + + /* raw data & cc */ + if (TestResult_FW_Rawdata >= 0) + mp_result |= (!!TestResult_FWMutual << MP_RESULT_SHIFT_RAWDATA); + else { + if (TestResult_FWMutual == -1) { + mp_result |= (!!TestResult_FWMutual << MP_RESULT_SHIFT_RAWDATA); + } + if (TestResult_FW_CC == -1) { + if (ts->carrier_system) { + mp_result |= (!!TestResult_FW_CC_I << MP_RESULT_SHIFT_CC_I); + mp_result |= (!!TestResult_FW_CC_Q << MP_RESULT_SHIFT_CC_Q); + } else { + mp_result |= (!!TestResult_FW_CC << MP_RESULT_SHIFT_CC); + } + } + } + + /* noise */ + if (TestResult_Noise >= 0) + mp_result |= (!!TestResult_FW_DiffMax << MP_RESULT_SHIFT_DIFF_MAX); + else { + if (TestResult_FW_DiffMax == -1) { + mp_result |= (!!TestResult_FW_DiffMax << MP_RESULT_SHIFT_DIFF_MAX); + } + if (TestResult_FW_DiffMin == -1) { + mp_result |= (!!TestResult_FW_DiffMin << MP_RESULT_SHIFT_DIFF_MIN); + } + } + + nvt_mp_seq_printf(m, "%d", mp_result); +#endif + 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; + unsigned char mpcriteria[32] = {0}; //novatek-mp-criteria-default + + TestResult_Short = 0; + TestResult_Short_Diff = 0; + TestResult_Short_Base = 0; + TestResult_Open = 0; + TestResult_FW_Rawdata = 0; + TestResult_FWMutual = 0; + TestResult_FW_CC = 0; + TestResult_FW_CC_I = 0; + TestResult_FW_CC_Q = 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; + } + + /* 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 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); + + nvt_mp_parse_dt(np, mpcriteria); + } 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; + if (ts->carrier_system) { + TestResult_FW_CC_I = 1; + TestResult_FW_CC_Q = 1; + } + } else { + if (ts->carrier_system) { + TestResult_FW_CC_I = RawDataTest_SinglePoint_Sub(RawData_FW_CC_I, RecordResult_FW_CC_I, X_Channel, Y_Channel, + PS_Config_Lmt_FW_CC_I_P, PS_Config_Lmt_FW_CC_I_N); + TestResult_FW_CC_Q = RawDataTest_SinglePoint_Sub(RawData_FW_CC_Q, RecordResult_FW_CC_Q, X_Channel, Y_Channel, + PS_Config_Lmt_FW_CC_Q_P, PS_Config_Lmt_FW_CC_Q_N); + if ((TestResult_FW_CC_I == -1) || (TestResult_FW_CC_Q == -1)) + TestResult_FW_CC = -1; + else + TestResult_FW_CC = 0; + } 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); + + // for carrier sensing system, only positive noise data + if (ts->carrier_system) { + TestResult_FW_DiffMin = 0; + } else { + 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 + if (ts->carrier_system) { + TestResult_Short_Diff = 1; + TestResult_Short_Base = 1; + } + } else { + //---Self Test Check --- // 0:PASS, -1:FAIL + if (ts->carrier_system) { + TestResult_Short_Diff = RawDataTest_SinglePoint_Sub(RawData_Short_Diff, RecordResult_Short_Diff, X_Channel, Y_Channel, + PS_Config_Lmt_Short_Diff_P, PS_Config_Lmt_Short_Diff_N); + TestResult_Short_Base = RawDataTest_SinglePoint_Sub(RawData_Short_Base, RecordResult_Short_Base, X_Channel, Y_Channel, + PS_Config_Lmt_Short_Base_P, PS_Config_Lmt_Short_Base_N); + + if ((TestResult_Short_Diff == -1) || (TestResult_Short_Base == -1)) + TestResult_Short = -1; + else + TestResult_Short = 0; + } else { + 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 file_operations nvt_selftest_fops = { + .owner = THIS_MODULE, + .open = nvt_selftest_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +#if NVT_TOUCH_MP_LENOVO +/******************************************************* +Description: + Novatek touchscreen /proc/NVTflash read function. + +return: + Executive outcomes. 2---succeed. -5,-14---failed. +*******************************************************/ +static ssize_t nvt_data_read(struct file *file, char __user *buff, size_t count, loff_t *offp) +{ + uint8_t *str; + int32_t ret = -1; + int32_t retries = 0; + int8_t i2c_wr = 0; + uint8_t i2c_addr = 0; + uint32_t data_len = 0; + + NVT_LOG("++\n"); + + if (count > (40 * (40 + 4) * 2 + 7)) { + NVT_ERR("error count=%zu\n", count); + return -EFAULT; + } + + str = kmalloc(40 * (40 + 4) * 2 + 7, GFP_KERNEL); + if (str == NULL) { + NVT_ERR("failed to allocated memory for input data\n"); + return -ENOMEM; + } + + if (copy_from_user(str, buff, count)) { + NVT_ERR("copy from user error\n"); + return -EFAULT; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif + + i2c_wr = str[0] >> 7; + i2c_addr = str[0] & 0x7F; + data_len = (int32_t)((str[1] << 8) + str[2]); + + if (i2c_wr == 0) { + return -ENOTSUPP; + } else if (i2c_wr == 1) { + while (retries < 20) { + ret = nvt_read_mass_data(i2c_addr, str, data_len); + if (ret == data_len) + break; + else + NVT_ERR("error, retries=%d, ret=%d, data_len=%d\n", retries, ret, data_len); + + retries++; + } + + if (retries < 20) { + ret = copy_to_user(buff, str, data_len); + if (ret) { + NVT_ERR("error, copy_to_user, ret=%d, data_len=%d\n", ret, data_len); + return -EFAULT; + } + } + + if (unlikely(retries == 20)) { + NVT_ERR("error, ret = %d\n", ret); + return -EIO; + } + } else { + NVT_ERR("Call error, str[0]=%d\n", str[0]); + return -EFAULT; + } + + kfree(str); + NVT_LOG("--\n"); + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen /proc/NVTflash open function. + +return: + Executive outcomes. 0---succeed. -12---failed. +*******************************************************/ +static int32_t nvt_data_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_data_close(struct inode *inode, struct file *file) +{ + struct nvt_flash_data *dev = file->private_data; + + if (dev) + kfree(dev); + + return 0; +} + +static const struct file_operations nvt_read_data_fops = { + .owner = THIS_MODULE, + .open = nvt_data_open, + .release = nvt_data_close, + .read = nvt_data_read, +}; +#endif + +#ifdef CONFIG_OF +/******************************************************* +Description: + Novatek touchscreen parse AIN setting for array type. + +return: + n.a. +*******************************************************/ +void 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); + } 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; + } + + for (i = 0; i < len; i++) + array[i] = tmp[i]; + +#if NVT_DEBUG + printk("[NVT-ts] %s = ", name); + for (i = 0; i < len; i++) { + printk("%02d ", array[i]); + } + printk("\n"); +#endif + } +} + +/******************************************************* +Description: + Novatek touchscreen parse criterion for u32 type. + +return: + n.a. +*******************************************************/ +void 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); + else { +#if NVT_DEBUG + NVT_LOG("%s=%d\n", name, *para); +#endif + } +} + +/******************************************************* +Description: + Novatek touchscreen parse criterion for array type. + +return: + n.a. +*******************************************************/ +void 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 i, j, iArrayIndex = 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); + } 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; + } + +#if NVT_DEBUG + NVT_LOG("%s =\n", name); + for (j = 0; j < Y_Channel; j++) { + printk("[NVT-ts] "); + for (i = 0; i < X_Channel; i++) { + iArrayIndex = j * X_Channel + i; + printk("%5d, ", array[iArrayIndex]); + } + printk("\n"); + } +#if TOUCH_KEY_NUM > 0 + printk("[NVT-ts] "); + for (i = 0; i < Key_Channel; i++) { + iArrayIndex++; + printk("%5d, ", array[iArrayIndex]); + } + printk("\n"); +#endif +#endif + } +} + +/******************************************************* +Description: + Novatek touchscreen parse device tree mp function. + +return: + n.a. +*******************************************************/ +void 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, use default setting!\n", node_compatible); + return; + } + + /* MP Config*/ + nvt_mp_parse_u32(np, "IC_X_CFG_SIZE", &IC_X_CFG_SIZE); + + nvt_mp_parse_u32(np, "IC_Y_CFG_SIZE", &IC_Y_CFG_SIZE); + +#if TOUCH_KEY_NUM > 0 + nvt_mp_parse_u32(np, "IC_KEY_CFG_SIZE", &IC_KEY_CFG_SIZE); +#endif + + nvt_mp_parse_u32(np, "X_Channel", &X_Channel); + + nvt_mp_parse_u32(np, "Y_Channel", &Y_Channel); + + nvt_mp_parse_ain(np, "AIN_X", AIN_X, IC_X_CFG_SIZE); + + nvt_mp_parse_ain(np, "AIN_Y", AIN_Y, IC_Y_CFG_SIZE); + +#if TOUCH_KEY_NUM > 0 + nvt_mp_parse_ain(np, "AIN_KEY", AIN_KEY, IC_KEY_CFG_SIZE); +#endif + + /* MP Criteria */ + if (ts->carrier_system) { + nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Diff_P", PS_Config_Lmt_Short_Diff_P, + X_Channel * Y_Channel + Key_Channel); + + nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Diff_N", PS_Config_Lmt_Short_Diff_N, + X_Channel * Y_Channel + Key_Channel); + + nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Base_P", PS_Config_Lmt_Short_Base_P, + X_Channel * Y_Channel + Key_Channel); + + nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Base_N", PS_Config_Lmt_Short_Base_N, + X_Channel * Y_Channel + Key_Channel); + } else { + nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Rawdata_P", PS_Config_Lmt_Short_Rawdata_P, + X_Channel * Y_Channel + Key_Channel); + + nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Rawdata_N", PS_Config_Lmt_Short_Rawdata_N, + X_Channel * Y_Channel + Key_Channel); + } + + nvt_mp_parse_array(np, "PS_Config_Lmt_Open_Rawdata_P", PS_Config_Lmt_Open_Rawdata_P, + X_Channel * Y_Channel + Key_Channel); + + nvt_mp_parse_array(np, "PS_Config_Lmt_Open_Rawdata_N", PS_Config_Lmt_Open_Rawdata_N, + X_Channel * Y_Channel + Key_Channel); + + nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Rawdata_P", PS_Config_Lmt_FW_Rawdata_P, + X_Channel * Y_Channel + Key_Channel); + + nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Rawdata_N", PS_Config_Lmt_FW_Rawdata_N, + X_Channel * Y_Channel + Key_Channel); + + nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_P", PS_Config_Lmt_FW_CC_P, + X_Channel * Y_Channel + Key_Channel); + + nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_N", PS_Config_Lmt_FW_CC_N, + X_Channel * Y_Channel + Key_Channel); + + if (ts->carrier_system) { + nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_I_P", PS_Config_Lmt_FW_CC_I_P, + X_Channel * Y_Channel + Key_Channel); + + nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_I_N", PS_Config_Lmt_FW_CC_I_N, + X_Channel * Y_Channel + Key_Channel); + + nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_Q_P", PS_Config_Lmt_FW_CC_Q_P, + X_Channel * Y_Channel + Key_Channel); + + nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_Q_N", PS_Config_Lmt_FW_CC_Q_N, + X_Channel * Y_Channel + Key_Channel); + } + + nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Diff_P", PS_Config_Lmt_FW_Diff_P, + X_Channel * Y_Channel + Key_Channel); + + nvt_mp_parse_array(np, "PS_Config_Lmt_FW_Diff_N", PS_Config_Lmt_FW_Diff_N, + X_Channel * Y_Channel + Key_Channel); + + nvt_mp_parse_u32(np, "PS_Config_Diff_Test_Frame", &PS_Config_Diff_Test_Frame); + + NVT_LOG("Parse mp criteria done!\n"); +} +#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) +{ + int32_t ret = 0; + + 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"); + ret = -1; + } else { + if (nvt_mp_buffer_init()) { + NVT_ERR("Allocate mp memory failed\n"); + ret = -1; + } else { + NVT_LOG("create /proc/nvt_selftest Succeeded!\n"); + } + ret = 0; + } +#if NVT_TOUCH_MP_LENOVO + if (ret == 0) { + NVT_proc_selftest_read_data = proc_create("nvt_read_data", 0444, NULL, &nvt_read_data_fops); + if (NVT_proc_selftest_read_data == NULL) { + NVT_ERR("create /proc/nvt_read_data Failed!\n"); + ret = -1; + } else { + NVT_LOG("create /proc/nvt_read_data Succeeded!\n"); + } + } +#endif + + return ret; +} + +void nvt_mp_proc_remove(void) +{ + if (NVT_proc_selftest_entry != NULL) { + remove_proc_entry("nvt_selftest", NULL); + NVT_LOG("Removed %s under /proc\n", + "nvt_selftest"); + } +#if NVT_TOUCH_MP_LENOVO + if (NVT_proc_selftest_read_data != NULL) { + remove_proc_entry("nvt_read_data", NULL); + NVT_LOG("Removed %s under /proc\n", + "nvt_read_data"); + } +#endif +} +#endif /* #if NVT_TOUCH_MP */ diff --git a/drivers/input/touchscreen/nova_36525_mmi/nt36xxx_mp_ctrlram.h b/drivers/input/touchscreen/nova_36525_mmi/nt36xxx_mp_ctrlram.h new file mode 100644 index 000000000000..ed068d741d48 --- /dev/null +++ b/drivers/input/touchscreen/nova_36525_mmi/nt36xxx_mp_ctrlram.h @@ -0,0 +1,719 @@ +/* + * Copyright (C) 2010 - 2017 Novatek, Inc. + * + * $Revision: 19587 $ + * $Date: 2017-11-28 21:00:30 +0800 (Tue, 28 Nov 2017) $ + * + * 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 = 32; +static uint32_t IC_KEY_CFG_SIZE = 4; +static uint32_t X_Channel = 18; +static uint32_t Y_Channel = 32; +static uint32_t Key_Channel = TOUCH_KEY_NUM; +static uint8_t AIN_X[40] = {17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; +static uint8_t AIN_Y[40] = {31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; +#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] = { + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, + 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, +#if TOUCH_KEY_NUM > 0 + 20000, 20000, 20000, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Short_Rawdata_N[40 * 40] = { + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, + 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, 11550, +#if TOUCH_KEY_NUM > 0 + 11550, 11550, 11550, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Short_Diff_P[40 * 40] = { + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, +#if TOUCH_KEY_NUM > 0 + 6300, 6300, 6300, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Short_Diff_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, +#if TOUCH_KEY_NUM > 0 + 0, 0, 0, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Short_Base_P[40 * 40] = { + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, +#if TOUCH_KEY_NUM > 0 + 2000, 2000, 2000, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Short_Base_N[40 * 40] = { + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, +#if TOUCH_KEY_NUM > 0 + -2000, -2000, -2000, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Open_Rawdata_P[40 * 40] = { + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, + 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, 13000, +#if TOUCH_KEY_NUM > 0 + 13000, 13000, 13000, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Open_Rawdata_N[40 * 40] = { + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, + 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, 6500, +#if TOUCH_KEY_NUM > 0 + 6500, 6500, 6500, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_Rawdata_P[40 * 40] = { + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, +#if TOUCH_KEY_NUM > 0 + 2000, 2000, 2000, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_Rawdata_N[40 * 40] = { + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, + 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, 400, +#if TOUCH_KEY_NUM > 0 + 400, 400, 400, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_CC_P[40 * 40] = { + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, + 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, +#if TOUCH_KEY_NUM > 0 + 38, 38, 38, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_CC_N[40 * 40] = { + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, +#if TOUCH_KEY_NUM > 0 + 9, 9, 9, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_CC_I_P[40 * 40] = { + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +#if TOUCH_KEY_NUM > 0 + 25, 25, 25, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_CC_I_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, +#if TOUCH_KEY_NUM > 0 + 0, 0, 0, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_CC_Q_P[40 * 40] = { + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +#if TOUCH_KEY_NUM > 0 + 25, 25, 25, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_CC_Q_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, +#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] = { + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, +#if TOUCH_KEY_NUM > 0 + 35, 35, 35, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_Diff_N[40 * 40] = { + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, + -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, -35, +#if TOUCH_KEY_NUM > 0 + -35, -35, -35, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Diff_Test_Frame = 50; + +#endif /* #if NVT_TOUCH_MP */ diff --git a/drivers/input/touchscreen/nova_mmi/Android.mk b/drivers/input/touchscreen/nova_mmi/Android.mk new file mode 100644 index 000000000000..4c6c38cdd1e1 --- /dev/null +++ b/drivers/input/touchscreen/nova_mmi/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +NOVA_MMI_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +NOVA_MMI_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := nova_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(NOVA_MMI_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/nova_mmi/Kbuild b/drivers/input/touchscreen/nova_mmi/Kbuild new file mode 100644 index 000000000000..1c3d4d161b3f --- /dev/null +++ b/drivers/input/touchscreen/nova_mmi/Kbuild @@ -0,0 +1,13 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/nova_mmi + +ifeq ($(filter m y, $(CONFIG_TOUCHSCREEN_NOVATEK_CHIPSET)),) + obj-m := nova_mmi.o +endif + +nova_mmi-objs += nt36xxx.o +nova_mmi-objs += nt36xxx_ext_proc.o +nova_mmi-objs += nt36xxx_fw_update.o +nova_mmi-objs += nt36xxx_mp_ctrlram.o diff --git a/drivers/input/touchscreen/nova_mmi/Makefile b/drivers/input/touchscreen/nova_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/nova_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/nova_mmi/nt36xxx.c b/drivers/input/touchscreen/nova_mmi/nt36xxx.c new file mode 100644 index 000000000000..80b034446fc2 --- /dev/null +++ b/drivers/input/touchscreen/nova_mmi/nt36xxx.c @@ -0,0 +1,2019 @@ +/* + * Copyright (C) 2010 - 2017 Novatek, Inc. + * + * $Revision: 14651 $ + * $Date: 2017-07-21 14:58:50 +0800 (周五, 21 七月 2017) $ + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_HQ_DEV_INFO) +#include +#endif + +#if defined(CONFIG_FB) +#include +#include +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#endif + +#include "nt36xxx.h" + +#if NVT_TOUCH_ESD_PROTECT +#include + +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; +uint8_t esd_retry_max = 5; +#endif /* #if NVT_TOUCH_ESD_PROTECT */ + +#if NVT_TOUCH_EXT_PROC +extern int32_t nvt_extra_proc_init(void); +extern void nvt_extra_proc_remove(void); +#endif + +#if NVT_TOUCH_FW +extern int32_t nvt_fw_sysfs_init(void); +extern int32_t nvt_fw_sysfs_deinit(void); +#endif + +#if NVT_TOUCH_MP +extern int32_t nvt_mp_proc_init(void); +extern void nvt_mp_proc_remove(void); +#endif + +struct nvt_ts_data *ts; + +static struct workqueue_struct *nvt_wq; + +#if BOOT_UPDATE_FIRMWARE +static struct workqueue_struct *nvt_fwu_wq; +extern void Boot_Update_Firmware(struct work_struct *work); +#endif + +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, unsigned long event, void *data); +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void nvt_ts_early_suspend(struct early_suspend *h); +static void nvt_ts_late_resume(struct early_suspend *h); +#endif + +static int charger_notifier_callback(struct notifier_block *nb, unsigned long val, void *v); +int32_t nvt_set_charger(uint8_t charger_on_off); +static void nvt_charger_notify_work(struct work_struct *work); +int usb_detect_flag = 0; + +#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; + +/******************************************************* +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; + + 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 = &buf[1]; + + 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; + } + + 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; + + msg.flags = !I2C_M_RD; + msg.addr = address; + msg.len = len; + msg.buf = buf; + + 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; + } + + return ret; +} + + +/******************************************************* +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("enter-%s\n", __func__); + /*---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); +} + +/******************************************************* +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---*/ + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + /*---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; + + msleep(10); + } + + 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---*/ + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + /*---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; + + msleep(10); + } + + 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; + NVT_LOG("enter-%s\n", __func__); + while (1) { + /*---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; + } + msleep(10); + } + + 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}; + int32_t ret = 0; + + /*---set xdata index to EVENT BUF ADDR---*/ + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + /*---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 ret; +} + +/******************************************************* +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---*/ + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + /*---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 = ts->def_x_max; + ts->abs_y_max = ts->def_y_max; + 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 + + 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; + + if (dev) + kfree(dev); + + return 0; +} + +static const struct file_operations nvt_flash_fops = { + .owner = THIS_MODULE, + .open = nvt_flash_open, + .release = nvt_flash_close, + .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/NVTflash\n"); + NVT_LOG("============================================================\n"); + + return 0; +} +#endif + +static void nvt_flash_proc_remove(void) +{ + if (NVT_proc_entry != NULL) { + remove_proc_entry(DEVICE_NAME, NULL); + NVT_LOG("Removed %s under /proc\n", + DEVICE_NAME); + } +} + +#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 + +static struct wake_lock gestrue_wakelock; + +/******************************************************* +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 int nvt_get_dt_def_coords(struct device *dev, char *name) +{ + u32 coords[TOUCH_COORDS_ARR_SIZE]; + struct property *prop; + struct device_node *np = dev->of_node; + int coords_size, rc; + + 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 != TOUCH_COORDS_ARR_SIZE) { + dev_err(dev, "invalid %s\n", name); + return -EINVAL; + } + + rc = of_property_read_u32_array(np, name, coords, coords_size); + if (rc && (rc != -EINVAL)) { + NVT_LOG("Unable to read novatek,def-max-resolution\n"); + return rc; + } + + ts->def_x_max = coords[0]; + ts->def_y_max = coords[1]; + return rc; +} + +static void nvt_parse_dt(struct device *dev) +{ + struct device_node *np = dev->of_node; + int rc; + 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); + + rc = nvt_get_dt_def_coords(dev, "novatek,def-max-resolution"); + if (rc) { + ts->def_x_max = TOUCH_DEFAULT_MAX_WIDTH; + ts->def_y_max = TOUCH_DEFAULT_MAX_HEIGHT; + } + NVT_LOG("novatek,def-max-resolution=%d,%d\n", ts->def_x_max, ts->def_y_max); + + if (of_property_read_bool(np, "novatek,usb_charger")) { + NVT_LOG("novatek,usb_charger set"); + ts->charger_detection_enable = 1; + } + else { + ts->charger_detection_enable = 0; + } +} +#else +static void nvt_parse_dt(struct device *dev) +{ + ts->irq_gpio = NVTTOUCH_INT_PIN; + ts->def_x_max = TOUCH_DEFAULT_MAX_WIDTH; + ts->def_y_max = TOUCH_DEFAULT_MAX_HEIGHT; +} +#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; + + /* 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: + return ret; +} + +#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 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; +} + +static void nvt_esd_check_func(struct work_struct *work) +{ + unsigned int timer = jiffies_to_msecs(jiffies - irq_timer); + + if (esd_retry >= esd_retry_max) + nvt_esd_check_enable(false); + + if ((timer > NVT_TOUCH_ESD_CHECK_PERIOD) && esd_check) { + NVT_ERR("do ESD recovery, timer = %d, retry = %d\n", timer, esd_retry); + printk("do ESD recovery, timer = %d, retry = %d\n", timer, esd_retry); + /* do esd recovery, bootloader reset */ + nvt_bootloader_reset(); + /* 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 void nvt_ts_work_func(struct work_struct *work) +{ + 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; + + 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_TOUCH_ESD_PROTECT + if (nvt_fw_recovery(point_data)) { + nvt_esd_check_enable(true); + goto XFER_ERROR; + } +#endif + +#if WAKEUP_GESTURE + if (bTouchIsAwake == 0) { + input_id = (uint8_t)(point_data[1] >> 3); + nvt_ts_wakeup_gesture_report(input_id, point_data); + enable_irq(ts->client->irq); + mutex_unlock(&ts->lock); + return; + } +#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)) { +#if NVT_TOUCH_ESD_PROTECT + /* update interrupt timer */ + irq_timer = jiffies; +#endif + 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: + enable_irq(ts->client->irq); + + mutex_unlock(&ts->lock); +} + +/******************************************************* +Description: + External interrupt service routine. + +return: + irq execute status. +*******************************************************/ +static irqreturn_t nvt_ts_irq_handler(int32_t irq, void *dev_id) +{ + disable_irq_nosync(ts->client->irq); + +#if WAKEUP_GESTURE + if (bTouchIsAwake == 0) { + wake_lock_timeout(&gestrue_wakelock, msecs_to_jiffies(5000)); + } +#endif + + queue_work(nvt_wq, &ts->nvt_work); + + 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---*/ + buf[0] = 0xFF; + buf[1] = 0x01; + buf[2] = 0xF6; + CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 3); + + /* ---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---*/ + buf[0] = 0xFF; + buf[1] = 0x03; + buf[2] = 0xF1; + CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 3); + + buf[0] = 0x35; + buf[1] = 0xA5; + CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 2); + + /* ---check CRC_ERR_FLAG---*/ + buf[0] = 0xFF; + buf[1] = 0x03; + buf[2] = 0xF1; + CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 3); + + 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! buf[1]=0x%02X\n", buf[1]); + } + + 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(void) +{ + 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; +#if NVT_TOUCH_FW + const char* product_id_table[] = { + "NT36572A", /* 0*/ + "NT36672A", + "NT36682A", + "NT36670A", + "NT36670B", + "NT36772A", /* 5*/ + "NT36772", + "NT36772", + "NT36772", + "NT36772", + "NT36772", /* 10*/ + "NT36670", + "NT36770", + "NT36672", + "NT36525", + "NT36870", /* 15*/ + "NT36676" + }; +#endif + 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); + + buf[0] = 0xFF; + buf[1] = 0x01; + buf[2] = 0xF6; + CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 3); + + buf[0] = 0x4E; + 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"); +#if NVT_TOUCH_FW + snprintf(ts->product_id, sizeof(ts->product_id), product_id_table[list]); +#endif + ts->mmap = trim_id_table[list].mmap; + ts->carrier_system = trim_id_table[list].carrier_system; + if (list == 1) /* NT36672A */ + /* + This kind of IC dose not need trigger + reset in resume. + Because TP core is reset with LCD. + */ + ts->skip_reset_in_resume = true; + ret = 0; + goto out; + } else { + ts->mmap = NULL; + ret = -1; + } + } + + msleep(10); + } + +#if NVT_TOUCH_FW + if (ret == -1) { + snprintf(ts->product_id, sizeof(ts->product_id), "NA"); + } +#endif +out: + 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 ((TOUCH_KEY_NUM > 0) || WAKEUP_GESTURE) + int32_t retry = 0; +#endif + struct pinctrl *pinctrl; + + NVT_LOG("start\n"); + + ts = kzalloc(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); + + /*---request and config GPIOs---*/ + ret = nvt_gpio_config(ts); + if (ret) { + NVT_ERR("gpio config error!\n"); + goto err_gpio_config_failed; + } + + pinctrl = devm_pinctrl_get_select(&client->dev, "active"); + if (IS_ERR(pinctrl)) { + long int error = PTR_ERR(pinctrl); + NVT_LOG("pinctrl failed err %ld\n", error); + } + + /*---check i2c func.---*/ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + NVT_ERR("i2c_check_functionality failed. (no I2C_FUNC_I2C)\n"); + ret = -ENODEV; + goto err_check_functionality_failed; + } + + /* need 10ms delay after POR(power on reset)*/ + msleep(10); + + /*---check chip version trim---*/ + ret = nvt_ts_check_chip_ver_trim(); + if (ret) { + NVT_ERR("chip is not identified\n"); + ret = -EINVAL; + goto err_chipvertrim_failed; + } + + mutex_init(&ts->lock); + + mutex_lock(&ts->lock); + nvt_bootloader_reset(); + nvt_check_fw_reset_state(RESET_STATE_INIT); + nvt_get_fw_info(); + mutex_unlock(&ts->lock); + + /*---create workqueue---*/ + nvt_wq = create_workqueue("nvt_wq"); + if (!nvt_wq) { + NVT_ERR("nvt_wq create workqueue failed\n"); + ret = -ENOMEM; + goto err_create_nvt_wq_failed; + } + INIT_WORK(&ts->nvt_work, nvt_ts_work_func); + + /*---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 < (sizeof(gesture_key_array) / sizeof(gesture_key_array[0])); retry++) { + input_set_capability(ts->input_dev, EV_KEY, gesture_key_array[retry]); + } + wake_lock_init(&gestrue_wakelock, WAKE_LOCK_SUSPEND, "poll-wake-lock"); +#endif + + sprintf(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); + +#if WAKEUP_GESTURE + ret = request_irq(client->irq, nvt_ts_irq_handler, ts->int_trigger_type | IRQF_NO_SUSPEND, client->name, ts); +#else + ret = request_irq(client->irq, nvt_ts_irq_handler, ts->int_trigger_type, client->name, ts); +#endif + if (ret != 0) { + NVT_ERR("request irq failed. ret=%d\n", ret); + goto err_int_request_failed; + } else { + disable_irq(client->irq); + NVT_LOG("request irq %d succeed\n", client->irq); + } + } + + if (ts->charger_detection_enable){ + ts->charger_detection = kzalloc(sizeof(struct usb_charger_detection), GFP_KERNEL); + if (ts->charger_detection == NULL) { + NVT_ERR("failed to allocated memory for usb_charger_detection\n"); + goto err_charger_detection_alloc_failed; + } + } + + if (ts->charger_detection) { + struct power_supply *psy = NULL; + union power_supply_propval prop = {0}; + + NVT_LOG("charger_detection on"); + ts->charger_detection->usb_connected = 0; + ts->charger_detection->nvt_charger_notify_wq = create_singlethread_workqueue("nvt_charger_wq"); + if (!ts->charger_detection->nvt_charger_notify_wq){ + NVT_ERR("allocate nvt_charger_notify_wq failed\n"); + goto err_charger_notify_wq_failed; + } + INIT_WORK(&ts->charger_detection->charger_notify_work, nvt_charger_notify_work); + + ts->charger_detection->charger_notif.notifier_call = charger_notifier_callback; + ret = power_supply_reg_notifier(&ts->charger_detection->charger_notif); + if (ret) { + NVT_ERR("Unable to register charger_notifier:%d\n", ret); + goto err_register_charger_notify_failed; + } + + /* if power supply supplier registered brfore TP + * ps_notify_callback will not receive PSY_EVENT_PROP_ADDED + * event, and will cause miss to set TP into charger state. + * So check PS state in probe. + */ + psy = power_supply_get_by_name("usb"); + if (psy) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,&prop); + if (ret < 0) { + NVT_ERR("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret); + goto err_register_charger_notify_failed; + } + else{ + usb_detect_flag = prop.intval; + if(usb_detect_flag != ts->charger_detection->usb_connected) { + if (USB_DETECT_IN == usb_detect_flag) { + ts->charger_detection->usb_connected = USB_DETECT_IN; + }else{ + ts->charger_detection->usb_connected = USB_DETECT_OUT; + } + nvt_set_charger(ts->charger_detection->usb_connected); + } + } + } + } + +#if BOOT_UPDATE_FIRMWARE + nvt_fwu_wq = create_singlethread_workqueue("nvt_fwu_wq"); + 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 + +#if NVT_TOUCH_ESD_PROTECT + INIT_DELAYED_WORK(&nvt_esd_check_work, nvt_esd_check_func); + nvt_esd_check_wq = create_workqueue("nvt_esd_check_wq"); + queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work, + msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD)); +#endif + /*---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_init_NVT_ts; + } +#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_init_NVT_ts; + } +#endif + +#if NVT_TOUCH_FW + ts->suspended = 0; + ts->force_reflash = 0; + ts->loading_fw = 0; + + ret = nvt_fw_sysfs_init(); + if (ret != 0) { + NVT_ERR("nvt fw class init failed. ret=%d\n", ret); + goto err_init_NVT_ts; + } +#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_init_NVT_ts; + } +#endif + +#if defined(CONFIG_FB) + ts->fb_notif.notifier_call = 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; + } +#elif defined(CONFIG_HAS_EARLYSUSPEND) + ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + ts->early_suspend.suspend = nvt_ts_early_suspend; + ts->early_suspend.resume = nvt_ts_late_resume; + ret = register_early_suspend(&ts->early_suspend); + if (ret) { + NVT_ERR("register early suspend failed. ret=%d\n", ret); + goto err_register_early_suspend_failed; + } +#endif + + bTouchIsAwake = 1; + NVT_LOG("end\n"); + + enable_irq(client->irq); + + return 0; + +#if defined(CONFIG_FB) +err_register_fb_notif_failed: +#elif defined(CONFIG_HAS_EARLYSUSPEND) +err_register_early_suspend_failed: +#endif +#if (NVT_TOUCH_PROC || NVT_TOUCH_EXT_PROC || NVT_TOUCH_MP) +err_init_NVT_ts: +#endif + free_irq(client->irq, ts); +#if BOOT_UPDATE_FIRMWARE +err_create_nvt_fwu_wq_failed: +#endif +err_register_charger_notify_failed: + if (ts->charger_detection) { + if (ts->charger_detection->charger_notif.notifier_call) + power_supply_unreg_notifier(&ts->charger_detection->charger_notif); + destroy_workqueue(ts->charger_detection->nvt_charger_notify_wq); + ts->charger_detection->nvt_charger_notify_wq = NULL; + kfree(ts->charger_detection); + } +err_charger_detection_alloc_failed: +err_charger_notify_wq_failed: +err_int_request_failed: +err_input_register_device_failed: + input_free_device(ts->input_dev); +err_input_dev_alloc_failed: +err_create_nvt_wq_failed: + mutex_destroy(&ts->lock); +err_chipvertrim_failed: +err_check_functionality_failed: + gpio_free(ts->irq_gpio); +err_gpio_config_failed: + i2c_set_clientdata(client, NULL); + kfree(ts); + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen driver release function. + +return: + Executive outcomes. 0---succeed. +*******************************************************/ +static int32_t nvt_ts_remove(struct i2c_client *client) +{ +#if defined(CONFIG_FB) + if (fb_unregister_client(&ts->fb_notif)) + NVT_ERR("Error occurred while unregistering fb_notifier.\n"); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&ts->early_suspend); +#endif + if (ts->charger_detection) { + if (ts->charger_detection->charger_notif.notifier_call) + power_supply_unreg_notifier(&ts->charger_detection->charger_notif); + destroy_workqueue(ts->charger_detection->nvt_charger_notify_wq); + kfree(ts->charger_detection); + } +#if NVT_TOUCH_FW + nvt_fw_sysfs_deinit(); +#endif + + mutex_destroy(&ts->lock); + + NVT_LOG("Removing driver...\n"); + + free_irq(client->irq, ts); + if (gpio_is_valid(ts->irq_gpio)) { + gpio_free(ts->irq_gpio); + } + +#if NVT_TOUCH_PROC + nvt_flash_proc_remove(); +#endif +#if NVT_TOUCH_MP + nvt_mp_proc_remove(); +#endif +#if NVT_TOUCH_EXT_PROC + nvt_extra_proc_remove(); +#endif + + input_unregister_device(ts->input_dev); + i2c_set_clientdata(client, NULL); + kfree(ts); + + return 0; +} + +/******************************************************* +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; + } + + mutex_lock(&ts->lock); + + NVT_LOG("start\n"); +#if NVT_TOUCH_FW + ts->suspended = 1; +#endif + bTouchIsAwake = 0; + +#if NVT_TOUCH_ESD_PROTECT + cancel_delayed_work_sync(&nvt_esd_check_work); + nvt_esd_check_enable(false); +#endif + +#if WAKEUP_GESTURE + /*---write i2c command to enter "wakeup gesture mode"---*/ + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = 0x13; +#if 0 /* Do not set 0xFF first, ToDo*/ + buf[2] = 0xFF; + buf[3] = 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 4); +#else + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); +#endif + + enable_irq_wake(ts->client->irq); + + NVT_LOG("Enabled touch wakeup gesture\n"); + +#else /* WAKEUP_GESTURE*/ + disable_irq(ts->client->irq); + + /*---write i2c 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*/ + + /* 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); + + mutex_unlock(&ts->lock); + + 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) +{ + int res = 0; + + if (bTouchIsAwake) { + NVT_LOG("Touch is already resume\n"); + return 0; + } + + if (!ts->skip_reset_in_resume) + msleep(10); + + mutex_lock(&ts->lock); + + NVT_LOG("start\n"); + + /* please make sure display reset(RESX) sequence and mipi dsi cmds sent before this*/ + if (!ts->skip_reset_in_resume) + nvt_bootloader_reset(); + res = nvt_check_fw_reset_state(RESET_STATE_REK); + + NVT_LOG("enter-%s-----res=%d\n", __func__, res); + +#if !WAKEUP_GESTURE + enable_irq(ts->client->irq); +#endif + +#if NVT_TOUCH_ESD_PROTECT + queue_delayed_work(nvt_esd_check_wq, &nvt_esd_check_work, + msecs_to_jiffies(NVT_TOUCH_ESD_CHECK_PERIOD)); +#endif + if (ts->charger_detection) { + queue_work(ts->charger_detection->nvt_charger_notify_wq, &ts->charger_detection->charger_notify_work); + } + + bTouchIsAwake = 1; +#if NVT_TOUCH_FW + ts->suspended = 0; +#endif + + mutex_unlock(&ts->lock); + + NVT_LOG("end\n"); + + return 0; +} + + +#if defined(CONFIG_FB) +static int 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_ts_suspend(&ts->client->dev); + } + } else if (evdata && evdata->data && event == FB_EVENT_BLANK) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK || + (*blank == FB_BLANK_NORMAL && 0 == bTouchIsAwake)) { + nvt_ts_resume(&ts->client->dev); + } + } + + return 0; +} +#elif defined(CONFIG_HAS_EARLYSUSPEND) +/******************************************************* +Description: + Novatek touchscreen driver early suspend function. + +return: + n.a. +*******************************************************/ +static void nvt_ts_early_suspend(struct early_suspend *h) +{ + nvt_ts_suspend(ts->client, PMSG_SUSPEND); +} + +/******************************************************* +Description: + Novatek touchscreen driver late resume function. + +return: + n.a. +*******************************************************/ +static void nvt_ts_late_resume(struct early_suspend *h) +{ + nvt_ts_resume(ts->client); +} +#endif + +int32_t nvt_set_charger(uint8_t charger_on_off) +{ + uint8_t buf[8] = {0}; + int32_t ret = 0; + + NVT_LOG("set charger: %d\n", charger_on_off); + + msleep(20); + //---set xdata index to EVENT BUF ADDR--- + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + + ret = CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + if (ret < 0) { + NVT_ERR("Set event buffer index fail!\n"); + goto nvt_set_charger_out; + } + + if (charger_on_off == USB_DETECT_IN) { + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = CMD_CHARGER_ON; + ret = CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Write set charger command fail!\n"); + goto nvt_set_charger_out; + } + } else if (charger_on_off == USB_DETECT_OUT) { + buf[0] = EVENT_MAP_HOST_CMD; + buf[1] = CMD_CHARGER_OFF; + ret = CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 2); + if (ret < 0) { + NVT_ERR("Write set charger command fail!\n"); + goto nvt_set_charger_out; + } + } else { + NVT_ERR("Invalid charger parameter!\n"); + ret = -EINVAL; + } + +nvt_set_charger_out: + + return ret; +} + +static void nvt_charger_notify_work(struct work_struct *work) +{ + if (NULL == work) { + NVT_ERR("%s: parameter work are null!\n", __func__); + return; + } + NVT_LOG("enter\n"); + if (USB_DETECT_IN == usb_detect_flag) { + if (mutex_lock_interruptible(&ts->lock)) { + NVT_ERR("Failed to lock in mutex_lock_interruptible(&ts->lock).\n"); + return; + } + nvt_set_charger(USB_DETECT_IN); + mutex_unlock(&ts->lock); + } else if (USB_DETECT_OUT == usb_detect_flag) { + if (mutex_lock_interruptible(&ts->lock)) { + NVT_ERR("Failed to lock in mutex_lock_interruptible(&ts->lock).\n"); + return; + } + nvt_set_charger(USB_DETECT_OUT); + mutex_unlock(&ts->lock); + }else{ + NVT_LOG("Charger flag:%d not currently required!\n",usb_detect_flag); + } +} + +static int charger_notifier_callback(struct notifier_block *nb, + unsigned long val, void *v) +{ + int ret = 0; + struct power_supply *psy = NULL; + struct usb_charger_detection *charger_detection = + container_of(nb, struct usb_charger_detection, charger_notif); + union power_supply_propval prop; + + psy= power_supply_get_by_name("usb"); + if (!psy){ + return -EINVAL; + NVT_ERR("Couldn't get usbpsy\n"); + } + + if (!strcmp(psy->desc->name, "usb")){ + if (psy && charger_detection && val == POWER_SUPPLY_PROP_STATUS) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT,&prop); + if (ret < 0) { + NVT_ERR("Couldn't get POWER_SUPPLY_PROP_ONLINE rc=%d\n", ret); + return ret; + }else{ + usb_detect_flag = prop.intval; + if(usb_detect_flag != charger_detection->usb_connected) { + if (USB_DETECT_IN == usb_detect_flag) { + charger_detection->usb_connected = USB_DETECT_IN; + }else{ + charger_detection->usb_connected = USB_DETECT_OUT; + } + if (bTouchIsAwake){ + queue_work(charger_detection->nvt_charger_notify_wq, + &charger_detection->charger_notify_work); + } + } + } + } + } + return 0; +} + +#if 0 +static const struct dev_pm_ops nvt_ts_dev_pm_ops = { + .suspend = nvt_ts_suspend, + .resume = nvt_ts_resume, +}; +#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_board_info __initdata nvt_i2c_boardinfo[] = { + { + I2C_BOARD_INFO(NVT_I2C_NAME, I2C_FW_Address), + }, +}; +*/ + +static struct i2c_driver nvt_i2c_driver = { + .probe = nvt_ts_probe, + .remove = nvt_ts_remove, + .id_table = nvt_ts_id, + .driver = { + .name = NVT_I2C_NAME, + .owner = THIS_MODULE, +#if 0 +#ifdef CONFIG_PM + .pm = &nvt_ts_dev_pm_ops, +#endif +#endif +#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) { + pr_err("%s: failed to add i2c driver", __func__); + goto err_driver; + } + + NVT_LOG("%s: finished\n", __func__); + +err_driver: + return ret; +} + +/******************************************************* +Description: + Driver uninstall function. + +return: + n.a. +********************************************************/ +static void __exit nvt_driver_exit(void) +{ + i2c_del_driver(&nvt_i2c_driver); + + if (nvt_wq) + destroy_workqueue(nvt_wq); + +#if BOOT_UPDATE_FIRMWARE + if (nvt_fwu_wq) + destroy_workqueue(nvt_fwu_wq); +#endif + +#if NVT_TOUCH_ESD_PROTECT + if (nvt_esd_check_wq) { + cancel_delayed_work_sync(&nvt_esd_check_work); + flush_workqueue(nvt_esd_check_wq); + destroy_workqueue(nvt_esd_check_wq); + } +#endif + +} + +module_init(nvt_driver_init); +module_exit(nvt_driver_exit); + +MODULE_DESCRIPTION("Novatek Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/nova_mmi/nt36xxx.h b/drivers/input/touchscreen/nova_mmi/nt36xxx.h new file mode 100644 index 000000000000..aedbd59487ae --- /dev/null +++ b/drivers/input/touchscreen/nova_mmi/nt36xxx.h @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2010 - 2017 Novatek, Inc. + * + * $Revision: 17989 $ + * $Date: 2017-10-31 21:25:50 +0800 (週二, 31 十月 2017) $ + * + * 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 + +#include +#include + +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#endif + +#include "nt36xxx_mem_map.h" + +#define NVT_DEBUG 0 + +/*---GPIO number---*/ +#define NVTTOUCH_INT_PIN 943 + + +/*---INT trigger mode---*/ +#define INT_TRIGGER_TYPE IRQ_TYPE_EDGE_RISING + + +/*---I2C driver info.---*/ +#define NVT_I2C_NAME "NVT-ts" +#define I2C_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_debug("[%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_COORDS_ARR_SIZE 2 +#define TOUCH_DEFAULT_MAX_WIDTH 1080 +#define TOUCH_DEFAULT_MAX_HEIGHT 2246 +#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 + +/*---Customerized func.---*/ +#define NVT_TOUCH_PROC 1 +#define NVT_TOUCH_EXT_PROC 1 +#define NVT_TOUCH_FW 1 +#define NVT_TOUCH_MP 1 +#define NVT_TOUCH_MP_LENOVO 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 0 +#define BOOT_UPDATE_FIRMWARE_NAME "novatek_ts_fw.bin" +/* ---ESD Protect.--- */ +#define NVT_TOUCH_ESD_PROTECT 1 +#define NVT_TOUCH_ESD_CHECK_PERIOD 2000 /* ms */ + +/* charger detect */ +#define USB_DETECT_IN 1 +#define USB_DETECT_OUT 0 +#define CMD_CHARGER_ON (0x53) +#define CMD_CHARGER_OFF (0x51) + +struct usb_charger_detection { + struct notifier_block charger_notif; + uint8_t usb_connected; + struct workqueue_struct *nvt_charger_notify_wq; + struct work_struct charger_notify_work; +}; + +struct nvt_ts_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct work_struct nvt_work; + struct delayed_work nvt_fwu_work; + uint16_t addr; + int8_t phys[32]; +#if defined(CONFIG_FB) + struct notifier_block fb_notif; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif + uint8_t fw_ver; + uint8_t x_num; + uint8_t y_num; + uint16_t abs_x_max; + uint16_t abs_y_max; + uint16_t def_x_max; + uint16_t def_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; + bool skip_reset_in_resume; + uint32_t charger_detection_enable; + struct usb_charger_detection *charger_detection; +#if NVT_TOUCH_FW + int8_t product_id[10]; + uint8_t suspended; + uint8_t force_reflash; + uint8_t loading_fw; +#endif +}; + +#if NVT_TOUCH_FW +#define FW_NAME_MAX_LEN 80 +#define VENDOR_NAME "novatek_ts" +#endif + +#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, + 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, + EVENT_MAP_FWDATE = 0x9C, +} I2C_EVENT_MAP; + +#if NVT_TOUCH_MP_LENOVO +typedef enum { + MP_RESULT_SHIFT_SHORT = 0x00, + MP_RESULT_SHIFT_SHORT_DIFF, + MP_RESULT_SHIFT_SHORT_BASE, + MP_RESULT_SHIFT_OPEN, + MP_RESULT_SHIFT_RAWDATA, + MP_RESULT_SHIFT_CC, + MP_RESULT_SHIFT_CC_I, + MP_RESULT_SHIFT_CC_Q, + MP_RESULT_SHIFT_NOISE, + MP_RESULT_SHIFT_DIFF_MAX, + MP_RESULT_SHIFT_DIFF_MIN, +/* MP_RESULT_SHIFT_READFAIL,*/ +} MP_TEST_RESULT; +#endif + +/*---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); +#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); + +#endif /* _LINUX_NVT_TOUCH_H */ diff --git a/drivers/input/touchscreen/nova_mmi/nt36xxx_ext_proc.c b/drivers/input/touchscreen/nova_mmi/nt36xxx_ext_proc.c new file mode 100644 index 000000000000..abcb76683038 --- /dev/null +++ b/drivers/input/touchscreen/nova_mmi/nt36xxx_ext_proc.c @@ -0,0 +1,766 @@ +/* + * Copyright (C) 2010 - 2017 Novatek, Inc. + * + * $Revision: 15382 $ + * $Date: 2017-08-15 09:19:01 +0800 (周二, 15 八月 2017) $ + * + * 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 +#include +#include + +#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 I2C_TANSFER_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 +#define XDATA_BUF_SIZE 2048 + +static uint8_t xdata_tmp[XDATA_BUF_SIZE] = {0}; +static int32_t xdata[XDATA_BUF_SIZE] = {0}; +static int32_t xdata_i[XDATA_BUF_SIZE] = {0}; +static int32_t xdata_q[XDATA_BUF_SIZE] = {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---*/ + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + /*---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---*/ + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + /*---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 mass data function. + +return: + n.a. +*******************************************************/ +int32_t nvt_read_mass_data(uint8_t i2c_addr, uint8_t *temp_buf, uint32_t count) +{ + int32_t i = 0; + int32_t j = 0; + int32_t k = 0; + uint8_t i2c_buf[I2C_TANSFER_LENGTH + 1] = {0}; + uint32_t xdata_addr = 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---*/ + xdata_addr = (temp_buf[3] << 24) + (temp_buf[4] << 16) + (temp_buf[5] << 8) + temp_buf[6]; + head_addr = xdata_addr - (xdata_addr % XDATA_SECTOR_SIZE); + dummy_len = xdata_addr - head_addr; + data_len = (int32_t)((temp_buf[1] << 8) + temp_buf[2]); + residual_len = (head_addr + dummy_len + data_len) % XDATA_SECTOR_SIZE; + + if (data_len > XDATA_BUF_SIZE) + return -EINVAL; + + /*NVT_LOG("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---*/ + i2c_buf[0] = 0xFF; + i2c_buf[1] = ((head_addr + XDATA_SECTOR_SIZE * i) >> 16) & 0xFF; + i2c_buf[2] = ((head_addr + XDATA_SECTOR_SIZE * i) >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, (uint16_t)i2c_addr, i2c_buf, 3); + + /*---read xdata by I2C_TANSFER_LENGTH*/ + for (j = 0; j < (XDATA_SECTOR_SIZE / I2C_TANSFER_LENGTH); j++) { + /*---read data---*/ + i2c_buf[0] = I2C_TANSFER_LENGTH * j; + CTP_I2C_READ(ts->client, (uint16_t)i2c_addr, i2c_buf, I2C_TANSFER_LENGTH + 1); + + /*---copy buf to xdata_tmp---*/ + for (k = 0; k < I2C_TANSFER_LENGTH; k++) { + xdata_tmp[XDATA_SECTOR_SIZE * i + I2C_TANSFER_LENGTH * j + k] = i2c_buf[k + 1]; + //NVT_LOG("0x%02X, 0x%04X\n", i2c_buf[k+1], (XDATA_SECTOR_SIZE*i + I2C_TANSFER_LENGTH*j + k)); + } + } + /*NVT_LOG("addr=0x%05X\n", (head_addr+XDATA_SECTOR_SIZE*i));*/ + } + + /*read xdata : step2*/ + if (residual_len != 0) { + /*---change xdata index---*/ + i2c_buf[0] = 0xFF; + i2c_buf[1] = ((xdata_addr + data_len - residual_len) >> 16) & 0xFF; + i2c_buf[2] = ((xdata_addr + data_len - residual_len) >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, (uint16_t)i2c_addr, i2c_buf, 3); + + /*---read xdata by I2C_TANSFER_LENGTH*/ + for (j = 0; j < (residual_len / I2C_TANSFER_LENGTH + 1); j++) { + /*---read data---*/ + i2c_buf[0] = I2C_TANSFER_LENGTH * j; + CTP_I2C_READ(ts->client, (uint16_t)i2c_addr, i2c_buf, I2C_TANSFER_LENGTH + 1); + + /*---copy buf to xdata_tmp---*/ + for (k = 0; k < I2C_TANSFER_LENGTH; k++) { + xdata_tmp[(dummy_len + data_len - residual_len) + I2C_TANSFER_LENGTH * j + k] = i2c_buf[k + 1]; + /*printk("0x%02X, 0x%04x\n", buf[k+1], ((dummy_len+data_len-residual_len) + I2C_TANSFER_LENGTH*j + k));*/ + } + } + /*printk("addr=0x%05X\n", (xdata_addr+data_len-residual_len));*/ + } + + /*---set xdata index to EVENT BUF ADDR---*/ + i2c_buf[0] = 0xFF; + i2c_buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + i2c_buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, (uint16_t)i2c_addr, i2c_buf, 3); + + /*---remove dummy data and copy to caller buffer---*/ + for (i = 0; i < data_len; i++) { + *(temp_buf + i) = xdata_tmp[dummy_len + i]; + } + + return data_len; +} + +/******************************************************* +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[I2C_TANSFER_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---*/ + buf[0] = 0xFF; + buf[1] = ((head_addr + XDATA_SECTOR_SIZE * i) >> 16) & 0xFF; + buf[2] = ((head_addr + XDATA_SECTOR_SIZE * i) >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + /*---read xdata by I2C_TANSFER_LENGTH*/ + for (j = 0; j < (XDATA_SECTOR_SIZE / I2C_TANSFER_LENGTH); j++) { + /*---read data---*/ + buf[0] = I2C_TANSFER_LENGTH * j; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, I2C_TANSFER_LENGTH + 1); + + /*---copy buf to xdata_tmp---*/ + for (k = 0; k < I2C_TANSFER_LENGTH; k++) { + xdata_tmp[XDATA_SECTOR_SIZE * i + I2C_TANSFER_LENGTH * j + k] = buf[k + 1]; + /*printk("0x%02X, 0x%04X\n", buf[k+1], (XDATA_SECTOR_SIZE*i + I2C_TANSFER_LENGTH*j + k));*/ + } + } + /*printk("addr=0x%05X\n", (head_addr+XDATA_SECTOR_SIZE*i));*/ + } + + /*read xdata : step2*/ + if (residual_len != 0) { + /*---change xdata index---*/ + buf[0] = 0xFF; + buf[1] = ((xdata_addr + data_len - residual_len) >> 16) & 0xFF; + buf[2] = ((xdata_addr + data_len - residual_len) >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + /*---read xdata by I2C_TANSFER_LENGTH*/ + for (j = 0; j < (residual_len / I2C_TANSFER_LENGTH + 1); j++) { + /*---read data---*/ + buf[0] = I2C_TANSFER_LENGTH * j; + CTP_I2C_READ(ts->client, I2C_FW_Address, buf, I2C_TANSFER_LENGTH + 1); + + /*---copy buf to xdata_tmp---*/ + for (k = 0; k < I2C_TANSFER_LENGTH; k++) { + xdata_tmp[(dummy_len + data_len - residual_len) + I2C_TANSFER_LENGTH * j + k] = buf[k + 1]; + /*printk("0x%02X, 0x%04x\n", buf[k+1], ((dummy_len+data_len-residual_len) + I2C_TANSFER_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---*/ + buf[0] = 0xFF; + buf[1] = (xdata_btn_addr >> 16) & 0xFF; + buf[2] = ((xdata_btn_addr >> 8) & 0xFF); + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + /*---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---*/ + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); +} + +/******************************************************* +Description: + Novatek touchscreen read meta data from IQ to rss function. + +return: + n.a. +*******************************************************/ +void nvt_read_mdata_rss(uint32_t xdata_i_addr, uint32_t xdata_q_addr, uint32_t xdata_btn_i_addr, uint32_t xdata_btn_q_addr) +{ + int i = 0; + + nvt_read_mdata(xdata_i_addr, xdata_btn_i_addr); + memcpy(xdata_i, xdata, ((ts->x_num * ts->y_num + TOUCH_KEY_NUM) * sizeof(int32_t))); + + nvt_read_mdata(xdata_q_addr, xdata_btn_q_addr); + memcpy(xdata_q, xdata, ((ts->x_num * ts->y_num + TOUCH_KEY_NUM) * sizeof(int32_t))); + + for (i = 0; i < (ts->x_num * ts->y_num + TOUCH_KEY_NUM); i++) { + xdata[i] = (int32_t)int_sqrt((unsigned long)(xdata_i[i] * xdata_i[i]) + (unsigned long)(xdata_q[i] * xdata_q[i])); + } +} + +/******************************************************* +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_info_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_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 file_operations nvt_info_proc_fops = { + .owner = THIS_MODULE, + .open = nvt_info_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +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_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 file_operations nvt_fw_version_fops = { + .owner = THIS_MODULE, + .open = nvt_fw_version_open, + .read = seq_read, + .llseek = seq_lseek, + .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_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 (ts->carrier_system) { + nvt_read_mdata_rss(ts->mmap->BASELINE_ADDR, ts->mmap->BASELINE_Q_ADDR, + ts->mmap->BASELINE_BTN_ADDR, ts->mmap->BASELINE_BTN_Q_ADDR); + } else { + 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 file_operations nvt_baseline_fops = { + .owner = THIS_MODULE, + .open = nvt_baseline_open, + .read = seq_read, + .llseek = seq_lseek, + .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_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 (ts->carrier_system) { + if (nvt_get_fw_pipe() == 0) + nvt_read_mdata_rss(ts->mmap->RAW_PIPE0_ADDR, ts->mmap->RAW_PIPE0_Q_ADDR, + ts->mmap->RAW_BTN_PIPE0_ADDR, ts->mmap->RAW_BTN_PIPE0_Q_ADDR); + else + nvt_read_mdata_rss(ts->mmap->RAW_PIPE1_ADDR, ts->mmap->RAW_PIPE1_Q_ADDR, + ts->mmap->RAW_BTN_PIPE1_ADDR, ts->mmap->RAW_BTN_PIPE1_Q_ADDR); + } else { + 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 file_operations nvt_raw_fops = { + .owner = THIS_MODULE, + .open = nvt_raw_open, + .read = seq_read, + .llseek = seq_lseek, + .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_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 (ts->carrier_system) { + if (nvt_get_fw_pipe() == 0) + nvt_read_mdata_rss(ts->mmap->DIFF_PIPE0_ADDR, ts->mmap->DIFF_PIPE0_Q_ADDR, + ts->mmap->DIFF_BTN_PIPE0_ADDR, ts->mmap->DIFF_BTN_PIPE0_Q_ADDR); + else + nvt_read_mdata_rss(ts->mmap->DIFF_PIPE1_ADDR, ts->mmap->DIFF_PIPE1_Q_ADDR, + ts->mmap->DIFF_BTN_PIPE1_ADDR, ts->mmap->DIFF_BTN_PIPE1_Q_ADDR); + } else { + 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 file_operations nvt_diff_fops = { + .owner = THIS_MODULE, + .open = nvt_diff_open, + .read = seq_read, + .llseek = seq_lseek, + .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/nvt_fw_version Failed!\n"); + return -ENOMEM; + } else { + NVT_LOG("create proc/nvt_fw_version Succeeded!\n"); + } + + NVT_proc_baseline_entry = proc_create(NVT_BASELINE, 0444, NULL, &nvt_baseline_fops); + if (NVT_proc_baseline_entry == NULL) { + NVT_ERR("create proc/nvt_baseline Failed!\n"); + return -ENOMEM; + } else { + NVT_LOG("create proc/nvt_baseline Succeeded!\n"); + } + + NVT_proc_raw_entry = proc_create(NVT_RAW, 0444, NULL, &nvt_raw_fops); + if (NVT_proc_raw_entry == NULL) { + NVT_ERR("create proc/nvt_raw Failed!\n"); + return -ENOMEM; + } else { + NVT_LOG("create proc/nvt_raw Succeeded!\n"); + } + + NVT_proc_diff_entry = proc_create(NVT_DIFF, 0444, NULL, &nvt_diff_fops); + if (NVT_proc_diff_entry == NULL) { + NVT_ERR("create proc/nvt_diff Failed!\n"); + return -ENOMEM; + } else { + NVT_LOG("create proc/nvt_diff Succeeded!\n"); + } + + return 0; +} + +void nvt_extra_proc_remove(void) +{ + if (NVT_proc_fw_version_entry != NULL) { + remove_proc_entry(NVT_FW_VERSION, NULL); + NVT_LOG("Removed %s under /proc\n", + NVT_FW_VERSION); + } + + if (NVT_proc_baseline_entry != NULL) { + remove_proc_entry(NVT_BASELINE, NULL); + NVT_LOG("Removed %s under /proc\n", + NVT_BASELINE); + } + + if (NVT_proc_raw_entry != NULL) { + remove_proc_entry(NVT_RAW, NULL); + NVT_LOG("Removed %s under /proc\n", + NVT_RAW); + } + + if (NVT_proc_diff_entry != NULL) { + remove_proc_entry(NVT_DIFF, NULL); + NVT_LOG("Removed %s under /proc\n", + NVT_DIFF); + } +} +#endif diff --git a/drivers/input/touchscreen/nova_mmi/nt36xxx_fw_update.c b/drivers/input/touchscreen/nova_mmi/nt36xxx_fw_update.c new file mode 100644 index 000000000000..7d08169c6ba6 --- /dev/null +++ b/drivers/input/touchscreen/nova_mmi/nt36xxx_fw_update.c @@ -0,0 +1,1395 @@ +/* + * Copyright (C) 2010 - 2017 Novatek, Inc. + * + * $Revision: 12034 $ + * $Date: 2017-05-04 17:49:58 +0800 (周四, 04 五月 2017) $ + * + * 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 +#include + +#include "nt36xxx.h" + +#if (BOOT_UPDATE_FIRMWARE || NVT_TOUCH_FW) + +#define FW_BIN_SIZE_116KB 118784 +#define FW_BIN_SIZE FW_BIN_SIZE_116KB +#define FW_BIN_VER_OFFSET 0x1A000 +#define FW_BIN_VER_BAR_OFFSET 0x1A001 +#define FLASH_SECTOR_SIZE 4096 +#define SIZE_64KB 65536 +#define BLOCK_64KB_NUM 4 + +const struct firmware *fw_entry = NULL; + +/******************************************************* +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 bin file size (116kb)*/ + if (fw_entry->size != FW_BIN_SIZE) { + NVT_ERR("bin file size not match. (%zu)\n", fw_entry->size); + 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*/ + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + ret = CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 3); + 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 fw_bin_size = 0; + size_t len_in_blk = 0; + int32_t retry = 0; + + if (Resume_PD()) { + NVT_ERR("Resume PD error!!\n"); + return -1; + } + + fw_bin_size = fw_entry->size; + + for (i = 0; i < BLOCK_64KB_NUM; i++) { + if (fw_bin_size > (i * SIZE_64KB)) { + /* Calculate WR_Filechksum of each 64KB block*/ + len_in_blk = min(fw_bin_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)*/ + buf[0] = 0xFF; + buf[1] = XDATA_Addr >> 16; + buf[2] = (XDATA_Addr >> 8) & 0xFF; + ret = CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 3); + 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) { + mdelay(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) { + mdelay(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) { + mdelay(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_entry->size % FLASH_SECTOR_SIZE) + count = fw_entry->size / FLASH_SECTOR_SIZE + 1; + else + count = fw_entry->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) { + mdelay(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) { + mdelay(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) { + mdelay(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; + + /* change I2C buffer index*/ + buf[0] = 0xFF; + buf[1] = XDATA_Addr >> 16; + buf[2] = (XDATA_Addr >> 8) & 0xFF; + ret = CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 3); + if (ret < 0) { + NVT_ERR("change I2C buffer index error!!(%d)\n", ret); + return ret; + } + + if (fw_entry->size % 256) + count = fw_entry->size / 256 + 1; + else + count = fw_entry->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_entry->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_entry->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_entry->size - Flash_Address - 1); + + for (k = 0; k < min(fw_entry->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_entry->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) { + mdelay(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) { + mdelay(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; + } + + NVT_LOG("Programming...%2d%%\r", ((i * 100) / count)); + } + + NVT_LOG("Programming...%2d%%\r", 100); + 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 fw_bin_size = 0; + size_t len_in_blk = 0; + int32_t retry = 0; + + fw_bin_size = fw_entry->size; + + for (i = 0; i < BLOCK_64KB_NUM; i++) { + if (fw_bin_size > (i * SIZE_64KB)) { + /* Calculate WR_Filechksum of each 64KB block*/ + len_in_blk = min(fw_bin_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)*/ + buf[0] = 0xFF; + buf[1] = XDATA_Addr >> 16; + buf[2] = (XDATA_Addr >> 8) & 0xFF; + ret = CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 3); + 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); + + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen check flash end flag function. + +return: + Executive outcomes. 0---succeed. 1,negative---failed. +*******************************************************/ +#define NVT_FLASH_END_FLAG_LEN 3 +#define NVT_FLASH_END_FLAG_ADDR 0x1AFFD +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*/ + buf[0] = 0xFF; + buf[1] = (ts->mmap->READ_FLASH_CHECKSUM_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->READ_FLASH_CHECKSUM_ADDR >> 8) & 0xFF; + ret = CTP_I2C_WRITE(ts->client, I2C_BLDR_Address, buf, 3); + 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*/ + strncpy(nvt_end_flag, &buf[3], NVT_FLASH_END_FLAG_LEN); + NVT_LOG("nvt_end_flag=%s (%02X %02X %02X)\n", nvt_end_flag, buf[3], buf[4], buf[5]); + + if (strncmp(nvt_end_flag, "NVT", 3) == 0) { + return 0; + } else { + NVT_ERR("\"NVT\" end flag not found!\n"); + return 1; + } +} +#endif + +#if BOOT_UPDATE_FIRMWARE +/******************************************************* +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] = ""; + sprintf(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_FW + ts->loading_fw = 1; +#endif + 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(); + } + } + + +#if NVT_TOUCH_FW + ts->loading_fw = 0; +#endif + mutex_unlock(&ts->lock); + + update_firmware_release(); +} +#endif /* BOOT_UPDATE_FIRMWARE */ + +#if NVT_TOUCH_FW +static ssize_t nvt_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + bool val; + + mutex_lock(&ts->lock); + val = ts->suspended; + mutex_unlock(&ts->lock); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + val == 0); +} + +static DEVICE_ATTR(poweron, 0444, nvt_poweron_show, NULL); + +static ssize_t nvt_ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "[FW]%02x,[IC]%s\n", + ts->fw_ver, ts->product_id); +} + +static DEVICE_ATTR(ic_ver, 0444, nvt_ic_ver_show, NULL); + +static ssize_t nvt_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +/* Update script use as $touch_product_id in fw name ==> (chip id from Lenovo&script) OR (vendor name from Focaltech) OR (project ID from Longcheer&Himax) + Project ID ?? ( - Jeter - Hannah) + Chip ID ?? (NVT-ts or NVTCapacitiveTouchScreen or NT36xxx) */ + return scnprintf(buf, PAGE_SIZE, "%s\n", + ts->product_id); +} + +static DEVICE_ATTR(productinfo, 0444, nvt_productinfo_show, NULL); + +static ssize_t nvt_force_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + ts->force_reflash = (input == 0) ? false : true; + + return count; +} + +static DEVICE_ATTR(forcereflash, 0220, NULL, nvt_force_reflash_store); + +static ssize_t nvt_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", + (ts->loading_fw) ? 1 : 0); +} + +static DEVICE_ATTR(flashprog, 0444, nvt_flashprog_show, NULL); + +static ssize_t nvt_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + char prefix[FW_NAME_MAX_LEN] = VENDOR_NAME; + char template[FW_NAME_MAX_LEN]; + char firmware_name[256] = ""; + + if (count > FW_NAME_MAX_LEN) { + NVT_ERR("FW filename is too long\n"); + retval = -EINVAL; + goto exit; + } + + if (ts->suspended) { + NVT_ERR("In suspend state, try again later\n"); + retval = -EINVAL; + goto exit; + } + + if (ts->loading_fw) { + NVT_ERR("In FW flashing state, try again later\n"); + retval = -EINVAL; + goto exit; + } + + if (!ts->force_reflash) { + if (strncmp(buf, prefix, + strnlen(prefix, sizeof(prefix)))) { + NVT_ERR("FW does not belong to Novatek\n"); + retval = -EINVAL; + goto exit; + } + + snprintf(template, sizeof(template), "-%s-", + ts->product_id); + if (!strnstr(buf + strnlen(prefix, sizeof(prefix)), template, + count)) { + NVT_ERR("FW does not belong to %s\n", ts->product_id); + retval = -EINVAL; + goto exit; + } + } + + strlcpy(firmware_name, buf, count); + NVT_LOG("FW filename: %s\n", firmware_name); + + retval = update_firmware_request(firmware_name); + if (retval) { + NVT_ERR("update_firmware_request failed. (%d)\n", retval); + goto exit; + } + + mutex_lock(&ts->lock); +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif + ts->loading_fw = 1; + nvt_sw_reset_idle(); + + retval = Check_CheckSum(); + + if (retval < 0) { + NVT_ERR("read firmware checksum failed\n"); + Update_Firmware(); + } else if ((retval == 0) && (Check_FW_Ver() == 0)) { + 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(); + retval = nvt_check_fw_reset_state(RESET_STATE_INIT); + if (retval) { + NVT_LOG("check fw reset state failed\n"); + Update_Firmware(); + } + } + + ts->loading_fw = 0; + mutex_unlock(&ts->lock); + + update_firmware_release(); + + retval = count; +exit: + return retval; +} + +static DEVICE_ATTR(doreflash, 0220, NULL, nvt_do_reflash_store); + +static ssize_t nvt_build_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +/* Update script use for comparing $str_cfg_id_latest in fw name */ + uint8_t tmp_buf[4] = {0}; + uint8_t fw_version = 0; + uint8_t date_Y = 0; + uint8_t date_M = 0; + uint8_t date_D = 0; + + tmp_buf[0] = 0xFF; + tmp_buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + tmp_buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, tmp_buf, 3); + + tmp_buf[0] = EVENT_MAP_FWINFO; + CTP_I2C_READ(ts->client, I2C_FW_Address, tmp_buf, 2); + fw_version = tmp_buf[1]; + + tmp_buf[0] = EVENT_MAP_FWDATE; + CTP_I2C_READ(ts->client, I2C_FW_Address, tmp_buf, 4); + date_Y = tmp_buf[1]; + date_M = tmp_buf[2]; + date_D = tmp_buf[3]; + + return scnprintf(buf, PAGE_SIZE, "%02x-%02d%02d%02d\n", + fw_version, date_Y, date_M, date_D); +} + +static DEVICE_ATTR(buildid, 0444, nvt_build_id_show, NULL); + + +#include +#include +#include + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t blen; + const char *path; + + path = kobject_get_path(&ts->client->dev.kobj, GFP_KERNEL); + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, VENDOR_NAME); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_NULL +}; + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +/******************************************************* +Description: + Novatek touchscreen FW function class. file node + initial function. + +return: + Executive outcomes. 0---succeed. -1---failed. +*******************************************************/ +int32_t nvt_fw_class_init(bool create) +{ + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + static struct class *touchscreen_class; + static struct device *ts_class_dev; + static int minor; + + if (create) { + minor = input_get_new_minor(ts->client->addr, + 1, false); + if (minor < 0) + minor = input_get_new_minor(TSDEV_MINOR_BASE, + TSDEV_MINOR_MAX, true); + NVT_LOG("assigned minor %d\n", minor); + + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + + ts_class_dev = device_create(touchscreen_class, NULL, + MKDEV(INPUT_MAJOR, minor), + ts, NVT_I2C_NAME); + if (IS_ERR(ts_class_dev)) { + error = PTR_ERR(ts_class_dev); + ts_class_dev = NULL; + return error; + } + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + error = device_create_file(ts_class_dev, &attrs[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + else + NVT_LOG("create /sys/class/touchscreen/%s Succeeded!\n", NVT_I2C_NAME); + } else { + if (!touchscreen_class || !ts_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(ts_class_dev, &attrs[i]); + + device_unregister(ts_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor)); + ts_class_dev = NULL; + class_unregister(touchscreen_class); + NVT_ERR("error creating touchscreen class\n"); + + return -ENODEV; +} + +int nvt_fw_sysfs_init(void) +{ + int ret; + struct i2c_client *client = ts->client; + + ret = nvt_fw_class_init(true); + if (ret != 0) { + NVT_ERR("fw class init failed. ret=%d\n", ret); + return ret; + } + + ret = device_create_file(&client->dev, &dev_attr_forcereflash); + if (ret) { + NVT_ERR("create_file dev_attr_forcereflash failed\n"); + return ret; + } + + ret = device_create_file(&client->dev, &dev_attr_flashprog); + if (ret) { + NVT_ERR("create_file dev_attr_flashprog failed\n"); + return ret; + } + ret = device_create_file(&client->dev, &dev_attr_doreflash); + if (ret) { + NVT_ERR("create_file dev_attr_doreflash failed\n"); + return ret; + } + + ret = device_create_file(&client->dev, &dev_attr_buildid); + if (ret) { + NVT_ERR("create_file dev_attr_buildid failed\n"); + return ret; + } + + ret = device_create_file(&client->dev, &dev_attr_productinfo); + if (ret) { + NVT_ERR("create_file dev_attr_productinfo failed\n"); + return ret; + } + + ret = device_create_file(&client->dev, &dev_attr_poweron); + if (ret) { + NVT_ERR("create_file dev_attr_poweron failed\n"); + return ret; + } + + ret = device_create_file(&client->dev, &dev_attr_ic_ver); + if (ret) { + NVT_ERR("create_file dev_attr_ic_ver failed\n"); + return ret; + } + + return 0; +} +void nvt_fw_sysfs_deinit(void) +{ + struct i2c_client *client = ts->client; + device_remove_file(&client->dev, &dev_attr_forcereflash); + device_remove_file(&client->dev, &dev_attr_flashprog); + device_remove_file(&client->dev, &dev_attr_doreflash); + device_remove_file(&client->dev, &dev_attr_buildid); + device_remove_file(&client->dev, &dev_attr_productinfo); + device_remove_file(&client->dev, &dev_attr_poweron); + device_remove_file(&client->dev, &dev_attr_ic_ver); + nvt_fw_class_init(false); +} +#endif diff --git a/drivers/input/touchscreen/nova_mmi/nt36xxx_mem_map.h b/drivers/input/touchscreen/nova_mmi/nt36xxx_mem_map.h new file mode 100644 index 000000000000..b5292406ac51 --- /dev/null +++ b/drivers/input/touchscreen/nova_mmi/nt36xxx_mem_map.h @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2010 - 2017 Novatek, Inc. + * + * $Revision: 26970 $ + * $Date: 2018-04-25 09:34:01 +0800 (週三, 25 四月 2018) $ + * + * 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. + * + */ + +struct nvt_ts_mem_map { + uint32_t EVENT_BUF_ADDR; + uint32_t RAW_PIPE0_ADDR; + uint32_t RAW_PIPE0_Q_ADDR; + uint32_t RAW_PIPE1_ADDR; + uint32_t RAW_PIPE1_Q_ADDR; + uint32_t BASELINE_ADDR; + uint32_t BASELINE_Q_ADDR; + uint32_t BASELINE_BTN_ADDR; + uint32_t BASELINE_BTN_Q_ADDR; + uint32_t DIFF_PIPE0_ADDR; + uint32_t DIFF_PIPE0_Q_ADDR; + uint32_t DIFF_PIPE1_ADDR; + uint32_t DIFF_PIPE1_Q_ADDR; + uint32_t RAW_BTN_PIPE0_ADDR; + uint32_t RAW_BTN_PIPE0_Q_ADDR; + uint32_t RAW_BTN_PIPE1_ADDR; + uint32_t RAW_BTN_PIPE1_Q_ADDR; + uint32_t DIFF_BTN_PIPE0_ADDR; + uint32_t DIFF_BTN_PIPE0_Q_ADDR; + uint32_t DIFF_BTN_PIPE1_ADDR; + uint32_t DIFF_BTN_PIPE1_Q_ADDR; + uint32_t READ_FLASH_CHECKSUM_ADDR; + uint32_t RW_FLASH_DATA_ADDR; +}; + +static const struct nvt_ts_mem_map NT36672A_memory_map = { + .EVENT_BUF_ADDR = 0x21C00, + .RAW_PIPE0_ADDR = 0x20000, + .RAW_PIPE0_Q_ADDR = 0, + .RAW_PIPE1_ADDR = 0x23000, + .RAW_PIPE1_Q_ADDR = 0, + .BASELINE_ADDR = 0x20BFC, + .BASELINE_Q_ADDR = 0, + .BASELINE_BTN_ADDR = 0x23BFC, + .BASELINE_BTN_Q_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x206DC, + .DIFF_PIPE0_Q_ADDR = 0, + .DIFF_PIPE1_ADDR = 0x236DC, + .DIFF_PIPE1_Q_ADDR = 0, + .RAW_BTN_PIPE0_ADDR = 0x20510, + .RAW_BTN_PIPE0_Q_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0x23510, + .RAW_BTN_PIPE1_Q_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0x20BF0, + .DIFF_BTN_PIPE0_Q_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0x23BF0, + .DIFF_BTN_PIPE1_Q_ADDR = 0, + .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_PIPE0_Q_ADDR = 0, + .RAW_PIPE1_ADDR = 0x12000, + .RAW_PIPE1_Q_ADDR = 0, + .BASELINE_ADDR = 0x10E70, + .BASELINE_Q_ADDR = 0, + .BASELINE_BTN_ADDR = 0x12E70, + .BASELINE_BTN_Q_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x10830, + .DIFF_PIPE0_Q_ADDR = 0, + .DIFF_PIPE1_ADDR = 0x12830, + .DIFF_PIPE1_Q_ADDR = 0, + .RAW_BTN_PIPE0_ADDR = 0x10E60, + .RAW_BTN_PIPE0_Q_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0x12E60, + .RAW_BTN_PIPE1_Q_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0x10E68, + .DIFF_BTN_PIPE0_Q_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0x12E68, + .DIFF_BTN_PIPE1_Q_ADDR = 0, + .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_PIPE0_Q_ADDR = 0, + .RAW_PIPE1_ADDR = 0x12000, + .RAW_PIPE1_Q_ADDR = 0, + .BASELINE_ADDR = 0x10B08, + .BASELINE_Q_ADDR = 0, + .BASELINE_BTN_ADDR = 0x12B08, + .BASELINE_BTN_Q_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x1064C, + .DIFF_PIPE0_Q_ADDR = 0, + .DIFF_PIPE1_ADDR = 0x1264C, + .DIFF_PIPE1_Q_ADDR = 0, + .RAW_BTN_PIPE0_ADDR = 0x10634, + .RAW_BTN_PIPE0_Q_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0x12634, + .RAW_BTN_PIPE1_Q_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0x10AFC, + .DIFF_BTN_PIPE0_Q_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0x12AFC, + .DIFF_BTN_PIPE1_Q_ADDR = 0, + .READ_FLASH_CHECKSUM_ADDR = 0x14000, + .RW_FLASH_DATA_ADDR = 0x14002, +}; + +static const struct nvt_ts_mem_map NT36870_memory_map = { + .EVENT_BUF_ADDR = 0x25000, + .RAW_PIPE0_ADDR = 0x20000, + .RAW_PIPE0_Q_ADDR = 0x204C8, + .RAW_PIPE1_ADDR = 0x23000, + .RAW_PIPE1_Q_ADDR = 0x234C8, + .BASELINE_ADDR = 0x21350, + .BASELINE_Q_ADDR = 0x21818, + .BASELINE_BTN_ADDR = 0x24350, + .BASELINE_BTN_Q_ADDR = 0x24358, + .DIFF_PIPE0_ADDR = 0x209B0, + .DIFF_PIPE0_Q_ADDR = 0x20E78, + .DIFF_PIPE1_ADDR = 0x239B0, + .DIFF_PIPE1_Q_ADDR = 0x23E78, + .RAW_BTN_PIPE0_ADDR = 0x20990, + .RAW_BTN_PIPE0_Q_ADDR = 0x20998, + .RAW_BTN_PIPE1_ADDR = 0x23990, + .RAW_BTN_PIPE1_Q_ADDR = 0x23998, + .DIFF_BTN_PIPE0_ADDR = 0x21340, + .DIFF_BTN_PIPE0_Q_ADDR = 0x21348, + .DIFF_BTN_PIPE1_ADDR = 0x24340, + .DIFF_BTN_PIPE1_Q_ADDR = 0x24348, + .READ_FLASH_CHECKSUM_ADDR = 0x24000, + .RW_FLASH_DATA_ADDR = 0x24002, +}; + +static const struct nvt_ts_mem_map NT36676F_memory_map = { + .EVENT_BUF_ADDR = 0x11A00, + .RAW_PIPE0_ADDR = 0x10000, + .RAW_PIPE0_Q_ADDR = 0, + .RAW_PIPE1_ADDR = 0x12000, + .RAW_PIPE1_Q_ADDR = 0, + .BASELINE_ADDR = 0x10B08, + .BASELINE_Q_ADDR = 0, + .BASELINE_BTN_ADDR = 0x12B08, + .BASELINE_BTN_Q_ADDR = 0, + .DIFF_PIPE0_ADDR = 0x1064C, + .DIFF_PIPE0_Q_ADDR = 0, + .DIFF_PIPE1_ADDR = 0x1264C, + .DIFF_PIPE1_Q_ADDR = 0, + .RAW_BTN_PIPE0_ADDR = 0x10634, + .RAW_BTN_PIPE0_Q_ADDR = 0, + .RAW_BTN_PIPE1_ADDR = 0x12634, + .RAW_BTN_PIPE1_Q_ADDR = 0, + .DIFF_BTN_PIPE0_ADDR = 0x10AFC, + .DIFF_BTN_PIPE0_Q_ADDR = 0, + .DIFF_BTN_PIPE1_ADDR = 0x12AFC, + .DIFF_BTN_PIPE1_Q_ADDR = 0, + .READ_FLASH_CHECKSUM_ADDR = 0x14000, + .RW_FLASH_DATA_ADDR = 0x14002, +}; + +#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; + uint8_t carrier_system; +}; + +static const struct nvt_ts_trim_id_table trim_id_table[] = { + {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x65, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .carrier_system = 0}, + {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .carrier_system = 0}, + {.id = {0x0A, 0xFF, 0xFF, 0x82, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .carrier_system = 0}, + {.id = {0x0A, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .carrier_system = 0}, + {.id = {0x0B, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .carrier_system = 0}, + {.id = {0x0A, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {1, 0, 0, 1, 1, 1}, + .mmap = &NT36672A_memory_map, .carrier_system = 0}, + {.id = {0x55, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .carrier_system = 0}, + {.id = {0x55, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .carrier_system = 0}, + {.id = {0xAA, 0x00, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .carrier_system = 0}, + {.id = {0xAA, 0x72, 0xFF, 0x00, 0x00, 0x00}, .mask = {1, 1, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .carrier_system = 0}, + {.id = {0xFF, 0xFF, 0xFF, 0x72, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .carrier_system = 0}, + {.id = {0xFF, 0xFF, 0xFF, 0x70, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .carrier_system = 0}, + {.id = {0xFF, 0xFF, 0xFF, 0x70, 0x67, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .carrier_system = 0}, + {.id = {0xFF, 0xFF, 0xFF, 0x72, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36772_memory_map, .carrier_system = 0}, + {.id = {0xFF, 0xFF, 0xFF, 0x25, 0x65, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36525_memory_map, .carrier_system = 0}, + {.id = {0xFF, 0xFF, 0xFF, 0x70, 0x68, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36870_memory_map, .carrier_system = 1}, + {.id = {0xFF, 0xFF, 0xFF, 0x76, 0x66, 0x03}, .mask = {0, 0, 0, 1, 1, 1}, + .mmap = &NT36676F_memory_map, .carrier_system = 0} +}; diff --git a/drivers/input/touchscreen/nova_mmi/nt36xxx_mp_ctrlram.c b/drivers/input/touchscreen/nova_mmi/nt36xxx_mp_ctrlram.c new file mode 100644 index 000000000000..0059674f9bcb --- /dev/null +++ b/drivers/input/touchscreen/nova_mmi/nt36xxx_mp_ctrlram.c @@ -0,0 +1,1998 @@ +/* + * Copyright (C) 2010 - 2017 Novatek, Inc. + * + * $Revision: 17989 $ + * $Date: 2017-10-31 21:25:50 +0800 (週二, 31 十月 2017) $ + * + * 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 +#include +#include +#include +#include + +#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_Short_Diff = NULL; +static uint8_t *RecordResult_Short_Base = 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_CC_I = NULL; +static uint8_t *RecordResult_FW_CC_Q = 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_Short_Diff = 0; +static int32_t TestResult_Short_Base = 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_FW_CC_I = 0; +static int32_t TestResult_FW_CC_Q = 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_Short_Diff = NULL; +static int32_t *RawData_Short_Base = 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 int32_t *RawData_FW_CC_I = NULL; +static int32_t *RawData_FW_CC_Q = NULL; + +static struct proc_dir_entry *NVT_proc_selftest_entry = NULL; +#if NVT_TOUCH_MP_LENOVO +static struct proc_dir_entry *NVT_proc_selftest_read_data = NULL; +#endif +static int8_t nvt_mp_test_result_printed = 0; + +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); +extern int32_t nvt_read_mass_data(uint32_t xdata_addr, uint8_t *temp_buf, uint32_t len); +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 = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_Short) { + NVT_ERR("kzalloc for RecordResult_Short failed!\n"); + return -ENOMEM; + } + + RecordResult_Short_Diff = RecordResult_Short; + + RecordResult_Short_Base = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_Short_Base) { + NVT_ERR("kzalloc for RecordResult_Short_Base failed!\n"); + return -ENOMEM; + } + + RecordResult_Open = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_Open) { + NVT_ERR("kzalloc for RecordResult_Open failed!\n"); + return -ENOMEM; + } + + RecordResult_FWMutual = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FWMutual) { + NVT_ERR("kzalloc for RecordResult_FWMutual failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_CC = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_CC) { + NVT_ERR("kzalloc for RecordResult_FW_CC failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_CC_I = RecordResult_FW_CC; + + RecordResult_FW_CC_Q = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_CC_Q) { + NVT_ERR("kzalloc for RecordResult_FW_CC_Q failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_DiffMax = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_DiffMax) { + NVT_ERR("kzalloc for RecordResult_FW_DiffMax failed!\n"); + return -ENOMEM; + } + + RecordResult_FW_DiffMin = (uint8_t *)kzalloc(RecordResult_BufSize, GFP_KERNEL); + if (!RecordResult_FW_DiffMin) { + NVT_ERR("kzalloc for RecordResult_FW_DiffMin failed!\n"); + return -ENOMEM; + } + + RawData_Short = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Short) { + NVT_ERR("kzalloc for RawData_Short failed!\n"); + return -ENOMEM; + } + + RawData_Short_Diff = RawData_Short; + + RawData_Short_Base = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Short_Base) { + NVT_ERR("kzalloc for RawData_Short_Base failed!\n"); + return -ENOMEM; + } + + RawData_Open = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Open) { + NVT_ERR("kzalloc for RawData_Open failed!\n"); + return -ENOMEM; + } + + RawData_Diff = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Diff) { + NVT_ERR("kzalloc for RawData_Diff failed!\n"); + return -ENOMEM; + } + + RawData_Diff_Min = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Diff_Min) { + NVT_ERR("kzalloc for RawData_Diff_Min failed!\n"); + return -ENOMEM; + } + + RawData_Diff_Max = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_Diff_Max) { + NVT_ERR("kzalloc for RawData_Diff_Max failed!\n"); + return -ENOMEM; + } + + RawData_FWMutual = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_FWMutual) { + NVT_ERR("kzalloc for RawData_FWMutual failed!\n"); + return -ENOMEM; + } + + RawData_FW_CC = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_FW_CC) { + NVT_ERR("kzalloc for RawData_FW_CC failed!\n"); + return -ENOMEM; + } + + RawData_FW_CC_I = RawData_FW_CC; + + RawData_FW_CC_Q = (int32_t *)kzalloc(RawData_BufSize, GFP_KERNEL); + if (!RawData_FW_CC_Q) { + NVT_ERR("kzalloc for RawData_FW_CC_Q failed!\n"); + return -ENOMEM; + } + + return 0; +} + +static void nvt_print_data_log_in_one_line(int32_t *data, int32_t data_num) +{ + char *tmp_log = NULL; + int32_t i = 0; + + tmp_log = (char *)kzalloc(data_num * 7 + 1, GFP_KERNEL); + if (!tmp_log) { + NVT_ERR("kzalloc for tmp_log failed!\n "); + return; + } + + for (i = 0; i < data_num; i++) { + sprintf(tmp_log + 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; + + tmp_log = (char *)kzalloc(result_num * 6 + 1, GFP_KERNEL); + if (!tmp_log) { + NVT_ERR("kzalloc for tmp_log failed!\n "); + return; + } + + for (i = 0; i < result_num; i++) { + sprintf(tmp_log + 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"); + + if (ts->carrier_system) { + /*---PS_Config_Lmt_Short_Diff---*/ + printk("PS_Config_Lmt_Short_Diff_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Short_Diff_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_Short_Diff_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Short_Diff_N, X_Channel, Y_Channel); + /*---PS_Config_Lmt_Short_Base---*/ + printk("PS_Config_Lmt_Short_Base_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Short_Base_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_Short_Base_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_Short_Base_N, X_Channel, Y_Channel); + } else { + /*---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); + + if (ts->carrier_system) { + /*---PS_Config_Lmt_FW_CC_I---*/ + printk("PS_Config_Lmt_FW_CC_I_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_CC_I_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_FW_CC_I_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_CC_I_N, X_Channel, Y_Channel); + /*---PS_Config_Lmt_FW_CC_Q---*/ + printk("PS_Config_Lmt_FW_CC_Q_P:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_CC_Q_P, X_Channel, Y_Channel); + printk("PS_Config_Lmt_FW_CC_Q_N:\n"); + nvt_print_lmt_array(PS_Config_Lmt_FW_CC_Q_N, X_Channel, Y_Channel); + } else { + /*---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_save_rawdata_to_csv(int32_t *rawdata, uint8_t x_ch, uint8_t y_ch, const char *file_path, uint32_t offset) +{ + int32_t x = 0; + int32_t y = 0; + int32_t iArrayIndex = 0; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + + printk("%s:++%s\n", __func__, file_path); + + for (y = 0; y < y_ch; y++) { + for (x = 0; x < x_ch; x++) { + iArrayIndex = y * x_ch + x; + } + nvt_print_data_log_in_one_line(rawdata + y * x_ch, x_ch); + printk("\n"); + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = y_ch * x_ch + k; + } + nvt_print_data_log_in_one_line(rawdata + y_ch * x_ch, Key_Channel); + printk("\n"); +#endif /* #if TOUCH_KEY_NUM > 0 */ + + printk("%s:--\n", __func__); + + return 0; +} + +static int32_t nvt_polling_hand_shake_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---*/ + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + /*---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; + + msleep(10); + } + + 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*/ + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + 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---*/ + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + /*---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; + if (ts->carrier_system) { + xdata[iArrayIndex] = (uint16_t)xdata[iArrayIndex]; + } else { + xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex]; + } + } + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = Y_Channel * X_Channel + k; + if (ts->carrier_system) { + xdata[iArrayIndex] = (uint16_t)xdata[iArrayIndex]; + } else { + xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex]; + } + } +#endif /* #if TOUCH_KEY_NUM > 0 */ + + printk("%s:\n", __func__); + /* Save Rawdata to CSV file*/ + if (nvt_save_rawdata_to_csv(xdata, X_Channel, Y_Channel, FW_RAWDATA_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + + 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; + int32_t xdata_tmp = 0; +#if TOUCH_KEY_NUM > 0 + int32_t k = 0; +#endif /* #if TOUCH_KEY_NUM > 0 */ + uint32_t rawdata_cc_q_offset = 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; + if (ts->carrier_system) { + xdata_tmp = xdata[iArrayIndex]; + RawData_FW_CC_I[iArrayIndex] = (uint8_t)(xdata_tmp & 0xFF); + RawData_FW_CC_Q[iArrayIndex] = (uint8_t)((xdata_tmp >> 8) & 0xFF); + } else { + xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex]; + } + } + } +#if TOUCH_KEY_NUM > 0 + for (k = 0; k < Key_Channel; k++) { + iArrayIndex = Y_Channel * X_Channel + k; + if (ts->carrier_system) { + xdata_tmp = xdata[iArrayIndex]; + RawData_FW_CC_I[iArrayIndex] = (uint8_t)(xdata_tmp & 0xFF); + RawData_FW_CC_Q[iArrayIndex] = (uint8_t)((xdata_tmp >> 8) & 0xFF); + } else { + xdata[iArrayIndex] = (int16_t)xdata[iArrayIndex]; + } + } +#endif /* #if TOUCH_KEY_NUM > 0 */ + + printk("%s:\n", __func__); + if (ts->carrier_system) { + printk("%s:RawData_CC_I:\n", __func__); + /* Save Rawdata to CSV file*/ + if (nvt_save_rawdata_to_csv(RawData_FW_CC_I, X_Channel, Y_Channel, FW_CC_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } +#if TOUCH_KEY_NUM > 0 + rawdata_cc_q_offset = Y_Channel * X_Channel * 7 + Y_Channel * 2 + Key_Channel * 7 + 2; +#else + rawdata_cc_q_offset = Y_Channel * X_Channel * 7 + Y_Channel * 2; +#endif /* #if TOUCH_KEY_NUM > 0 */ + printk("%s:RawData_CC_Q:\n", __func__); + /* Save Rawdata to CSV file*/ + if (nvt_save_rawdata_to_csv(RawData_FW_CC_Q, X_Channel, Y_Channel, FW_CC_CSV_FILE, rawdata_cc_q_offset) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + } else { + /* Save Rawdata to CSV file*/ + if (nvt_save_rawdata_to_csv(xdata, X_Channel, Y_Channel, FW_CC_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + } + + 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---*/ + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + /*---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; + uint32_t rawdata_diff_min_offset = 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; + if (ts->carrier_system) { + RawData_Diff_Max[iArrayIndex] = (uint16_t)xdata[iArrayIndex]; + RawData_Diff_Min[iArrayIndex] = 0; + } else { + 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; + if (ts->carrier_system) { + RawData_Diff_Max[iArrayIndex] = (uint16_t)xdata[iArrayIndex]; + RawData_Diff_Min[iArrayIndex] = 0; + } else { + 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); + + printk("%s:RawData_Diff_Max:\n", __func__); + /* Save Rawdata to CSV file*/ + if (nvt_save_rawdata_to_csv(RawData_Diff_Max, X_Channel, Y_Channel, NOISE_TEST_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + + if (!ts->carrier_system) { +#if TOUCH_KEY_NUM > 0 + rawdata_diff_min_offset = Y_Channel * X_Channel * 7 + Y_Channel * 2 + Key_Channel * 7 + 2; +#else + rawdata_diff_min_offset = Y_Channel * X_Channel * 7 + Y_Channel * 2; +#endif /* #if TOUCH_KEY_NUM > 0 */ + printk("%s:RawData_Diff_Min:\n", __func__); + /* Save Rawdata to CSV file*/ + if (nvt_save_rawdata_to_csv(RawData_Diff_Min, X_Channel, Y_Channel, NOISE_TEST_CSV_FILE, rawdata_diff_min_offset) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + } + + NVT_LOG("--\n"); + + return 0; +} + +static void nvt_enable_open_test(void) +{ + uint8_t buf[8] = {0}; + + /*---set xdata index to EVENT BUF ADDR---*/ + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + /*---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---*/ + buf[0] = 0xFF; + buf[1] = (ts->mmap->EVENT_BUF_ADDR >> 16) & 0xFF; + buf[2] = (ts->mmap->EVENT_BUF_ADDR >> 8) & 0xFF; + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + + /*---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 = (uint8_t *)kzalloc((IC_X_CFG_SIZE * IC_Y_CFG_SIZE + IC_KEY_CFG_SIZE) * 2, GFP_KERNEL); +#else + rawdata_buf = (uint8_t *)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---*/ + buf[0] = 0xFF; + buf[1] = (uint8_t)(((raw_pipe_addr + y * IC_X_CFG_SIZE * 2) >> 16) & 0xFF); + buf[2] = (uint8_t)(((raw_pipe_addr + y * IC_X_CFG_SIZE * 2) >> 8) & 0xFF); + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + 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---*/ + buf[0] = 0xFF; + buf[1] = (uint8_t)((raw_btn_pipe_addr >> 16) & 0xFF); + buf[2] = (uint8_t)((raw_btn_pipe_addr >> 8) & 0xFF); + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + 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); + + + printk("%s:RawData_Open\n", __func__); + /* Save RawData to CSV file*/ + if (nvt_save_rawdata_to_csv(xdata, X_Channel, Y_Channel, OPEN_TEST_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + + 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 */ + uint32_t rawdata_short_base_offset = 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 = (uint8_t *)kzalloc((X_Channel * Y_Channel + Key_Channel) * 2, GFP_KERNEL); +#else + rawdata_buf = (uint8_t *)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 (ts->carrier_system) { + /* to get short diff rawdata at pipe0*/ + raw_pipe_addr = ts->mmap->RAW_PIPE0_ADDR; + } else { + 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---*/ + buf[0] = 0xFF; + buf[1] = (uint8_t)(((raw_pipe_addr + y * X_Channel * 2) >> 16) & 0xFF); + buf[2] = (uint8_t)(((raw_pipe_addr + y * X_Channel * 2) >> 8) & 0xFF); + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + 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 (ts->carrier_system) { + /* to get button short diff rawdata at pipe0*/ + raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE0_ADDR; + } else { + 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---*/ + buf[0] = 0xFF; + buf[1] = (uint8_t)((raw_btn_pipe_addr >> 16) & 0xFF); + buf[2] = (uint8_t)((raw_btn_pipe_addr >> 8) & 0xFF); + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + 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 */ + + /* for carrier sensing system to get short baseline rawdata*/ + if (ts->carrier_system) { + /* to get short baseline rawdata at pipe1*/ + raw_pipe_addr = ts->mmap->RAW_PIPE1_ADDR; + + for (y = 0; y < Y_Channel; y++) { + /*---change xdata index---*/ + buf[0] = 0xFF; + buf[1] = (uint8_t)(((raw_pipe_addr + y * X_Channel * 2) >> 16) & 0xFF); + buf[2] = (uint8_t)(((raw_pipe_addr + y * X_Channel * 2) >> 8) & 0xFF); + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + 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 + /* to get button short baseline rawdata at pipe1*/ + raw_btn_pipe_addr = ts->mmap->RAW_BTN_PIPE1_ADDR; + + /*---change xdata index---*/ + buf[0] = 0xFF; + buf[1] = (uint8_t)((raw_btn_pipe_addr >> 16) & 0xFF); + buf[2] = (uint8_t)((raw_btn_pipe_addr >> 8) & 0xFF); + CTP_I2C_WRITE(ts->client, I2C_FW_Address, buf, 3); + 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; + RawData_Short_Base[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; + RawData_Short_Base[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); + + if (ts->carrier_system) + printk("%s:RawData_Short_Diff:\n", __func__); + else + printk("%s:RawData_Short\n", __func__); + /* Save Rawdata to CSV file*/ + if (nvt_save_rawdata_to_csv(xdata, X_Channel, Y_Channel, SHORT_TEST_CSV_FILE, 0) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + if (ts->carrier_system) { +#if TOUCH_KEY_NUM > 0 + rawdata_short_base_offset = Y_Channel * X_Channel * 7 + Y_Channel * 2 + Key_Channel * 7 + 2; +#else + rawdata_short_base_offset = Y_Channel * X_Channel * 7 + Y_Channel * 2; +#endif /* #if TOUCH_KEY_NUM > 0 */ + printk("%s:RawData_Short_Base:\n", __func__); + /* Save Rawdata to CSV file*/ + if (nvt_save_rawdata_to_csv(RawData_Short_Base, X_Channel, Y_Channel, SHORT_TEST_CSV_FILE, rawdata_short_base_offset) < 0) { + NVT_ERR("save rawdata to CSV file failed\n"); + return -EAGAIN; + } + } + + 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 == false) { + 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) +{ +#if NVT_TOUCH_MP_LENOVO + MP_TEST_RESULT mp_result = 0; +#endif + + NVT_LOG("++\n"); +#if !NVT_TOUCH_MP_LENOVO + nvt_mp_seq_printf(m, "FW Version: %d\n\n", ts->fw_ver); + + nvt_mp_seq_printf(m, "Short Test"); + if ((TestResult_Short == 0) || (TestResult_Short == 1)) { + print_selftest_result(m, TestResult_Short, RecordResult_Short, RawData_Short, X_Channel, Y_Channel); + } else { /* TestResult_Short is -1*/ + if (ts->carrier_system) { + nvt_mp_seq_printf(m, " FAIL!\n"); + if (TestResult_Short_Diff == -1) { + nvt_mp_seq_printf(m, "Short Diff"); + print_selftest_result(m, TestResult_Short_Diff, RecordResult_Short_Diff, RawData_Short_Diff, X_Channel, Y_Channel); + } + if (TestResult_Short_Base == -1) { + nvt_mp_seq_printf(m, "Short Base"); + print_selftest_result(m, TestResult_Short_Base, RecordResult_Short_Base, RawData_Short_Base, X_Channel, Y_Channel); + } + } else { + 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) { + if (ts->carrier_system) { + if (TestResult_FW_CC_I == -1) { + nvt_mp_seq_printf(m, "FW CC_I"); + print_selftest_result(m, TestResult_FW_CC_I, RecordResult_FW_CC_I, RawData_FW_CC_I, X_Channel, Y_Channel); + } + if (TestResult_FW_CC_Q == -1) { + nvt_mp_seq_printf(m, "FW CC_Q"); + print_selftest_result(m, TestResult_FW_CC_Q, RecordResult_FW_CC_Q, RawData_FW_CC_Q, X_Channel, Y_Channel); + } + } else { + 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); + } + } +#else + /* short */ + if (TestResult_Short >= 0) + mp_result |= (!!TestResult_Short << MP_RESULT_SHIFT_SHORT); + else { + if (ts->carrier_system) { + mp_result |= (!!TestResult_Short_Diff << MP_RESULT_SHIFT_SHORT_DIFF); + mp_result |= (!!TestResult_Short_Base << MP_RESULT_SHIFT_SHORT_BASE); + } else { + mp_result |= (!!TestResult_Short << MP_RESULT_SHIFT_SHORT); + } + } + + /* open */ + mp_result |= (!!TestResult_Open << MP_RESULT_SHIFT_OPEN); + + /* raw data & cc */ + if (TestResult_FW_Rawdata >= 0) + mp_result |= (!!TestResult_FWMutual << MP_RESULT_SHIFT_RAWDATA); + else { + if (TestResult_FWMutual == -1) { + mp_result |= (!!TestResult_FWMutual << MP_RESULT_SHIFT_RAWDATA); + } + if (TestResult_FW_CC == -1) { + if (ts->carrier_system) { + mp_result |= (!!TestResult_FW_CC_I << MP_RESULT_SHIFT_CC_I); + mp_result |= (!!TestResult_FW_CC_Q << MP_RESULT_SHIFT_CC_Q); + } else { + mp_result |= (!!TestResult_FW_CC << MP_RESULT_SHIFT_CC); + } + } + } + + /* noise */ + if (TestResult_Noise >= 0) + mp_result |= (!!TestResult_FW_DiffMax << MP_RESULT_SHIFT_DIFF_MAX); + else { + if (TestResult_FW_DiffMax == -1) { + mp_result |= (!!TestResult_FW_DiffMax << MP_RESULT_SHIFT_DIFF_MAX); + } + if (TestResult_FW_DiffMin == -1) { + mp_result |= (!!TestResult_FW_DiffMin << MP_RESULT_SHIFT_DIFF_MIN); + } + } + + nvt_mp_seq_printf(m, "%d", mp_result); +#endif + 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; + unsigned char mpcriteria[32] = {0}; /*novatek-mp-criteria-default*/ + + TestResult_Short = 0; + TestResult_Short_Diff = 0; + TestResult_Short_Base = 0; + TestResult_Open = 0; + TestResult_FW_Rawdata = 0; + TestResult_FWMutual = 0; + TestResult_FW_CC = 0; + TestResult_FW_CC_I = 0; + TestResult_FW_CC_Q = 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 + +#ifdef CONFIG_OF + if (nvt_get_fw_info()) { + mutex_unlock(&ts->lock); + NVT_ERR("get fw info failed!\n"); + return -EAGAIN; + } + + /* 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 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"); + } +#endif + /*---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; + if (ts->carrier_system) { + TestResult_FW_CC_I = 1; + TestResult_FW_CC_Q = 1; + } + } else { + if (ts->carrier_system) { + TestResult_FW_CC_I = RawDataTest_SinglePoint_Sub(RawData_FW_CC_I, RecordResult_FW_CC_I, X_Channel, Y_Channel, + PS_Config_Lmt_FW_CC_I_P, PS_Config_Lmt_FW_CC_I_N); + TestResult_FW_CC_Q = RawDataTest_SinglePoint_Sub(RawData_FW_CC_Q, RecordResult_FW_CC_Q, X_Channel, Y_Channel, + PS_Config_Lmt_FW_CC_Q_P, PS_Config_Lmt_FW_CC_Q_N); + if ((TestResult_FW_CC_I == -1) || (TestResult_FW_CC_Q == -1)) + TestResult_FW_CC = -1; + else + TestResult_FW_CC = 0; + } 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); + + /* for carrier sensing system, only positive noise data*/ + if (ts->carrier_system) { + TestResult_FW_DiffMin = 0; + } else { + 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*/ + if (ts->carrier_system) { + TestResult_Short_Diff = 1; + TestResult_Short_Base = 1; + } + } else { + /*---Self Test Check --- 0:PASS, -1:FAIL*/ + if (ts->carrier_system) { + TestResult_Short_Diff = RawDataTest_SinglePoint_Sub(RawData_Short_Diff, RecordResult_Short_Diff, X_Channel, Y_Channel, + PS_Config_Lmt_Short_Diff_P, PS_Config_Lmt_Short_Diff_N); + TestResult_Short_Base = RawDataTest_SinglePoint_Sub(RawData_Short_Base, RecordResult_Short_Base, X_Channel, Y_Channel, + PS_Config_Lmt_Short_Base_P, PS_Config_Lmt_Short_Base_N); + + if ((TestResult_Short_Diff == -1) || (TestResult_Short_Base == -1)) + TestResult_Short = -1; + else + TestResult_Short = 0; + } else { + 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 file_operations nvt_selftest_fops = { + .owner = THIS_MODULE, + .open = nvt_selftest_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +#if NVT_TOUCH_MP_LENOVO +/******************************************************* +Description: + Novatek touchscreen /proc/NVTflash read function. + +return: + Executive outcomes. 2---succeed. -5,-14---failed. +*******************************************************/ +static ssize_t nvt_data_read(struct file *file, char __user *buff, size_t count, loff_t *offp) +{ + uint8_t *str; + int32_t ret = -1; + int32_t retries = 0; + int8_t i2c_wr = 0; + uint8_t i2c_addr = 0; + uint32_t data_len = 0; + + NVT_LOG("++\n"); + + if (count > (X_Channel * (Y_Channel + 4) * 2 + 7)) { + NVT_ERR("error count=%zu\n", count); + return -EFAULT; + } + + str = kmalloc(X_Channel * (Y_Channel + 4) * 2 + 7, GFP_KERNEL); + if (str == NULL) { + NVT_ERR("failed to allocated memory for input data\n"); + return -ENOMEM; + } + + if (copy_from_user(str, buff, count)) { + NVT_ERR("copy from user error\n"); + return -EFAULT; + } + +#if NVT_TOUCH_ESD_PROTECT + nvt_esd_check_enable(false); +#endif + + i2c_wr = str[0] >> 7; + i2c_addr = str[0] & 0x7F; + data_len = (int32_t)((str[1] << 8) + str[2]); + + if (i2c_wr == 0) { + return -ENOTSUPP; + } else if (i2c_wr == 1) { + while (retries < 20) { + ret = nvt_read_mass_data(i2c_addr, str, data_len); + if (ret == data_len) + break; + else + NVT_ERR("error, retries=%d, ret=%d, data_len=%d\n", retries, ret, data_len); + + retries++; + } + + if (retries < 20) { + ret = copy_to_user(buff, str, data_len); + if (ret) { + NVT_ERR("error, copy_to_user, ret=%d, data_len=%d\n", ret, data_len); + return -EFAULT; + } + } + + if (unlikely(retries == 20)) { + NVT_ERR("error, ret = %d\n", ret); + return -EIO; + } + } else { + NVT_ERR("Call error, str[0]=%d\n", str[0]); + return -EFAULT; + } + + kfree(str); + NVT_LOG("--\n"); + return ret; +} + +/******************************************************* +Description: + Novatek touchscreen /proc/NVTflash open function. + +return: + Executive outcomes. 0---succeed. -12---failed. +*******************************************************/ +static int32_t nvt_data_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_data_close(struct inode *inode, struct file *file) +{ + struct nvt_flash_data *dev = file->private_data; + + if (dev) + kfree(dev); + + return 0; +} + +static const struct file_operations nvt_read_data_fops = { + .owner = THIS_MODULE, + .open = nvt_data_open, + .release = nvt_data_close, + .read = nvt_data_read, +}; +#endif + +#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 (ts->carrier_system) { + if (nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Diff_P", PS_Config_Lmt_Short_Diff_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Diff_N", PS_Config_Lmt_Short_Diff_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Base_P", PS_Config_Lmt_Short_Base_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_Short_Base_N", PS_Config_Lmt_Short_Base_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + } else { + 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 (ts->carrier_system) { + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_I_P", PS_Config_Lmt_FW_CC_I_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_I_N", PS_Config_Lmt_FW_CC_I_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_Q_P", PS_Config_Lmt_FW_CC_Q_P, + X_Channel * Y_Channel + Key_Channel)) + return -1; + + if (nvt_mp_parse_array(np, "PS_Config_Lmt_FW_CC_Q_N", PS_Config_Lmt_FW_CC_Q_N, + X_Channel * Y_Channel + Key_Channel)) + return -1; + } else { + 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) +{ + int32_t ret = 0; + 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"); + ret = -1; + } else { + if (nvt_mp_buffer_init()) { + NVT_ERR("Allocate mp memory failed\n"); + ret = -1; + } else { + NVT_LOG("create /proc/nvt_selftest Succeeded!\n"); + } + } +#if NVT_TOUCH_MP_LENOVO + if (ret == 0) { + NVT_proc_selftest_read_data = proc_create("nvt_read_data", 0444, NULL, &nvt_read_data_fops); + if (NVT_proc_selftest_read_data == NULL) { + NVT_ERR("create /proc/nvt_read_data Failed!\n"); + ret = -1; + } else { + NVT_LOG("create /proc/nvt_read_data Succeeded!\n"); + } +} +#endif + return ret; +} + +void nvt_mp_proc_remove(void) +{ + if (NVT_proc_selftest_entry != NULL) { + remove_proc_entry("nvt_selftest", NULL); + NVT_LOG("Removed %s under /proc\n", + "nvt_selftest"); + } +#if NVT_TOUCH_MP_LENOVO + if (NVT_proc_selftest_read_data != NULL) { + remove_proc_entry("nvt_read_data", NULL); + NVT_LOG("Removed %s under /proc\n", + "nvt_read_data"); + } +#endif +} + +#endif /* #if NVT_TOUCH_MP */ diff --git a/drivers/input/touchscreen/nova_mmi/nt36xxx_mp_ctrlram.h b/drivers/input/touchscreen/nova_mmi/nt36xxx_mp_ctrlram.h new file mode 100644 index 000000000000..6c915cca2739 --- /dev/null +++ b/drivers/input/touchscreen/nova_mmi/nt36xxx_mp_ctrlram.h @@ -0,0 +1,791 @@ +/* + * Copyright (C) 2010 - 2017 Novatek, Inc. + * + * $Revision: 14590 $ + * $Date: 2017-07-20 11:14:42 +0800 (周四, 20 七月 2017) $ + * + * 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] = { + 65535, 14008, 14008, 14008, 14008, 14008, 65535, 65535, 65535, 65535, 65535, 65535, 14008, 14008, 14008, 14008, 14008, 65535, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, + 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 14008, 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 + 20000, 20000, 20000, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Short_Rawdata_N[40 * 40] = { + -65535, 11376, 11376, 11376, 11376, 11376, -65535, -65535, -65535, -65535, -65535, -65535, 11376, 11376, 11376, 11376, 11376, -65535, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, + 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, 11376, +#if TOUCH_KEY_NUM > 0 + 11550, 11550, 11550, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Short_Diff_P[40 * 40] = { + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, + 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, 6300, +#if TOUCH_KEY_NUM > 0 + 6300, 6300, 6300, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Short_Diff_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_Short_Base_P[40 * 40] = { + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, + 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, +#if TOUCH_KEY_NUM > 0 + 2000, 2000, 2000, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Short_Base_N[40 * 40] = { + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, + -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, -2000, +#if TOUCH_KEY_NUM > 0 + -2000, -2000, -2000, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Open_Rawdata_P[40 * 40] = { + 65535, 5073, 5699, 4902, 4158, 3372, 65535, 65535, 65535, 65535, 65535, 65535, 3252, 4097, 4856, 5603, 5211, 65535, + 2067, 1988, 1973, 1943, 1928, 1889, 1887, 1788, 1748, 1742, 1779, 1871, 1892, 1923, 1944, 1986, 2018, 2258, + 1811, 1848, 1842, 1826, 1820, 1797, 1850, 1874, 1835, 1874, 1908, 1886, 1836, 1859, 1875, 1905, 1920, 1967, + 1838, 1869, 1863, 1844, 1844, 1824, 1875, 1898, 1860, 1826, 1863, 1844, 1797, 1812, 1827, 1854, 1869, 1889, + 1803, 1833, 1829, 1818, 1814, 1793, 1847, 1865, 1826, 1809, 1844, 1827, 1778, 1799, 1811, 1835, 1845, 1866, + 1844, 1866, 1854, 1845, 1836, 1821, 1874, 1890, 1856, 1826, 1862, 1853, 1788, 1814, 1826, 1848, 1865, 1890, + 1829, 1851, 1838, 1826, 1820, 1805, 1859, 1872, 1838, 1872, 1904, 1898, 1841, 1863, 1874, 1895, 1914, 1947, + 1821, 1832, 1826, 1824, 1817, 1800, 1853, 1871, 1833, 1830, 1865, 1865, 1802, 1820, 1841, 1857, 1866, 1899, + 1824, 1833, 1833, 1827, 1821, 1800, 1863, 1874, 1833, 1829, 1856, 1859, 1799, 1814, 1826, 1848, 1854, 1896, + 1803, 1809, 1806, 1805, 1796, 1770, 1842, 1848, 1806, 1817, 1847, 1847, 1785, 1805, 1818, 1836, 1848, 1887, + 1818, 1829, 1820, 1817, 1812, 1790, 1862, 1862, 1823, 1802, 1832, 1830, 1770, 1793, 1802, 1823, 1824, 1871, + 1805, 1814, 1800, 1800, 1793, 1776, 1844, 1844, 1805, 1796, 1818, 1821, 1755, 1778, 1793, 1809, 1823, 1865, + 1805, 1812, 1802, 1793, 1788, 1772, 1844, 1844, 1797, 1805, 1829, 1829, 1755, 1776, 1791, 1809, 1820, 1865, + 1790, 1802, 1793, 1791, 1785, 1766, 1836, 1838, 1791, 1769, 1793, 1802, 1730, 1746, 1758, 1770, 1790, 1824, + 1763, 1790, 1766, 1755, 1752, 1736, 1806, 1806, 1760, 1770, 1769, 1793, 1721, 1733, 1745, 1763, 1809, 1811, + 1757, 1809, 1751, 1748, 1742, 1731, 1800, 1796, 1751, 1737, 1754, 1770, 1695, 1713, 1730, 1742, 1800, 1784, + 1749, 1802, 1746, 1740, 1736, 1721, 1796, 1802, 1742, 1748, 1776, 1781, 1707, 1721, 1736, 1748, 1809, 1793, + 1746, 1797, 1740, 1737, 1730, 1715, 1794, 1782, 1737, 1703, 1716, 1737, 1665, 1679, 1698, 1706, 1761, 1755, + 1713, 1760, 1710, 1691, 1677, 1656, 1731, 1716, 1667, 1715, 1725, 1743, 1661, 1674, 1686, 1731, 1752, 1749, + 1688, 1734, 1725, 1673, 1659, 1637, 1719, 1697, 1662, 1698, 1704, 1725, 1644, 1661, 1673, 1730, 1734, 1725, + 1695, 1736, 1727, 1677, 1665, 1643, 1722, 1697, 1670, 1685, 1689, 1718, 1628, 1643, 1656, 1710, 1725, 1715, + 1677, 1710, 1704, 1653, 1644, 1625, 1704, 1674, 1640, 1664, 1659, 1697, 1613, 1623, 1635, 1695, 1697, 1691, + 1671, 1706, 1701, 1643, 1632, 1614, 1694, 1668, 1626, 1664, 1667, 1701, 1611, 1623, 1641, 1689, 1698, 1692, + 1640, 1668, 1656, 1616, 1595, 1578, 1658, 1634, 1589, 1653, 1655, 1694, 1601, 1617, 1664, 1683, 1688, 1682, + 1623, 1650, 1643, 1631, 1575, 1559, 1641, 1617, 1566, 1619, 1628, 1670, 1571, 1584, 1641, 1650, 1653, 1656, + 1625, 1647, 1641, 1634, 1578, 1557, 1646, 1620, 1566, 1586, 1610, 1638, 1551, 1563, 1620, 1626, 1626, 1629, + 1605, 1629, 1628, 1619, 1560, 1542, 1628, 1607, 1542, 1584, 1613, 1640, 1556, 1563, 1622, 1628, 1628, 1631, + 1611, 1631, 1625, 1611, 1566, 1535, 1623, 1607, 1539, 1548, 1589, 1614, 1524, 1577, 1602, 1611, 1610, 1607, + 1563, 1596, 1584, 1578, 1568, 1497, 1592, 1547, 1494, 1487, 1532, 1565, 1466, 1535, 1544, 1551, 1553, 1548, + 1568, 1593, 1587, 1578, 1568, 1497, 1592, 1551, 1494, 1502, 1550, 1587, 1487, 1556, 1565, 1571, 1571, 1571, + 1542, 1563, 1554, 1551, 1538, 1469, 1565, 1526, 1473, 1487, 1529, 1569, 1466, 1535, 1544, 1550, 1559, 1548, + 1526, 1551, 1547, 1535, 1526, 1455, 1551, 1511, 1455, 1446, 1488, 1529, 1430, 1497, 1508, 1512, 1515, 1508, + 1521, 1550, 1542, 1533, 1524, 1451, 1547, 1508, 1448, 1422, 1458, 1500, 1400, 1470, 1478, 1487, 1487, 1481, + 1491, 1511, 1505, 1497, 1485, 1409, 1511, 1469, 1415, 1401, 1437, 1475, 1383, 1454, 1461, 1467, 1475, 1466, + 1497, 1518, 1503, 1494, 1484, 1406, 1508, 1461, 1404, 1424, 1469, 1512, 1413, 1484, 1493, 1500, 1512, 1514, + 1800, 1806, 1758, 1733, 1709, 1620, 1721, 1680, 1620, 1599, 1643, 1683, 1586, 1661, 1688, 1718, 1754, 1710, +#if TOUCH_KEY_NUM > 0 + 13000, 13000, 13000, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_Open_Rawdata_N[40 * 40] = { + -65535, 1691, 1900, 1634, 1386, 1124, -65535, -65535, -65535, -65535, -65535, -65535, 1084, 1366, 1619, 1868, 1737, -65535, + 689, 663, 658, 648, 643, 630, 629, 596, 583, 581, 593, 624, 631, 641, 648, 662, 673, 753, + 604, 616, 614, 609, 607, 599, 617, 625, 612, 625, 636, 629, 612, 620, 625, 635, 640, 656, + 613, 623, 621, 615, 615, 608, 625, 633, 620, 609, 621, 615, 599, 604, 609, 618, 623, 630, + 601, 611, 610, 606, 605, 598, 616, 622, 609, 603, 615, 609, 593, 600, 604, 612, 615, 622, + 615, 622, 618, 615, 612, 607, 625, 630, 619, 609, 621, 618, 596, 605, 609, 616, 622, 630, + 610, 617, 613, 609, 607, 602, 620, 624, 613, 624, 635, 633, 614, 621, 625, 632, 638, 649, + 607, 611, 609, 608, 606, 600, 618, 624, 611, 610, 622, 622, 601, 607, 614, 619, 622, 633, + 608, 611, 611, 609, 607, 600, 621, 625, 611, 610, 619, 620, 600, 605, 609, 616, 618, 632, + 601, 603, 602, 602, 599, 590, 614, 616, 602, 606, 616, 616, 595, 602, 606, 612, 616, 629, + 606, 610, 607, 606, 604, 597, 621, 621, 608, 601, 611, 610, 590, 598, 601, 608, 608, 624, + 602, 605, 600, 600, 598, 592, 615, 615, 602, 599, 606, 607, 585, 593, 598, 603, 608, 622, + 602, 604, 601, 598, 596, 591, 615, 615, 599, 602, 610, 610, 585, 592, 597, 603, 607, 622, + 597, 601, 598, 597, 595, 589, 612, 613, 597, 590, 598, 601, 577, 582, 586, 590, 597, 608, + 588, 597, 589, 585, 584, 579, 602, 602, 587, 590, 590, 598, 574, 578, 582, 588, 603, 604, + 586, 603, 584, 583, 581, 577, 600, 599, 584, 579, 585, 590, 565, 571, 577, 581, 600, 595, + 583, 601, 582, 580, 579, 574, 599, 601, 581, 583, 592, 594, 569, 574, 579, 583, 603, 598, + 582, 599, 580, 579, 577, 572, 598, 594, 579, 568, 572, 579, 555, 560, 566, 569, 587, 585, + 571, 587, 570, 564, 559, 552, 577, 572, 556, 572, 575, 581, 554, 558, 562, 577, 584, 583, + 563, 578, 575, 558, 553, 546, 573, 566, 554, 566, 568, 575, 548, 554, 558, 577, 578, 575, + 565, 579, 576, 559, 555, 548, 574, 566, 557, 562, 563, 573, 543, 548, 552, 570, 575, 572, + 559, 570, 568, 551, 548, 542, 568, 558, 547, 555, 553, 566, 538, 541, 545, 565, 566, 564, + 557, 569, 567, 548, 544, 538, 565, 556, 542, 555, 556, 567, 537, 541, 547, 563, 566, 564, + 547, 556, 552, 539, 532, 526, 553, 545, 530, 551, 552, 565, 534, 539, 555, 561, 563, 561, + 541, 550, 548, 544, 525, 520, 547, 539, 522, 540, 543, 557, 524, 528, 547, 550, 551, 552, + 542, 549, 547, 545, 526, 519, 549, 540, 522, 529, 537, 546, 517, 521, 540, 542, 542, 543, + 535, 543, 543, 540, 520, 514, 543, 536, 514, 528, 538, 547, 519, 521, 541, 543, 543, 544, + 537, 544, 542, 537, 522, 512, 541, 536, 513, 516, 530, 538, 508, 526, 534, 537, 537, 536, + 521, 532, 528, 526, 523, 499, 531, 516, 498, 496, 511, 522, 489, 512, 515, 517, 518, 516, + 523, 531, 529, 526, 523, 499, 531, 517, 498, 501, 517, 529, 496, 519, 522, 524, 524, 524, + 514, 521, 518, 517, 513, 490, 522, 509, 491, 496, 510, 523, 489, 512, 515, 517, 520, 516, + 509, 517, 516, 512, 509, 485, 517, 504, 485, 482, 496, 510, 477, 499, 503, 504, 505, 503, + 507, 517, 514, 511, 508, 484, 516, 503, 483, 474, 486, 500, 467, 490, 493, 496, 496, 494, + 497, 504, 502, 499, 495, 470, 504, 490, 472, 467, 479, 492, 461, 485, 487, 489, 492, 489, + 499, 506, 501, 498, 495, 469, 503, 487, 468, 475, 490, 504, 471, 495, 498, 500, 504, 505, + 600, 602, 586, 578, 570, 540, 574, 560, 540, 533, 548, 561, 529, 554, 563, 573, 585, 570, +#if TOUCH_KEY_NUM > 0 + 6500, 6500, 6500, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_Rawdata_P[40 * 40] = { + 65535, 1799, 1581, 1847, 1749, 1634, 65535, 65535, 65535, 65535, 65535, 65535, 1744, 1708, 1596, 1659, 1588, 65535, + 1819, 1726, 1831, 1721, 1715, 1715, 1795, 1761, 1704, 1821, 1819, 1679, 1763, 1761, 1827, 1840, 1830, 1709, + 1597, 1638, 1652, 1574, 1515, 1610, 1625, 1546, 1674, 1530, 1648, 1540, 1554, 1506, 1520, 1572, 1606, 1670, + 1589, 1665, 1639, 1585, 1616, 1737, 1565, 1630, 1609, 1746, 1736, 1632, 1641, 1644, 1674, 1571, 1585, 1595, + 1694, 1680, 1686, 1700, 1638, 1786, 1662, 1618, 1751, 1660, 1705, 1733, 1831, 1634, 1760, 1729, 1715, 1775, + 1541, 1613, 1726, 1760, 1568, 1813, 1651, 1761, 1726, 1610, 1562, 1659, 1560, 1653, 1548, 1653, 1691, 1529, + 1786, 1817, 1767, 1781, 1641, 1777, 1683, 1621, 1593, 1697, 1774, 1721, 1775, 1736, 1781, 1750, 1599, 1861, + 1708, 1674, 1516, 1558, 1747, 1670, 1758, 1663, 1708, 1590, 1809, 1691, 1812, 1646, 1739, 1680, 1834, 1795, + 1568, 1589, 1623, 1662, 1688, 1593, 1582, 1569, 1679, 1618, 1620, 1525, 1655, 1547, 1567, 1637, 1632, 1576, + 1603, 1620, 1607, 1673, 1658, 1669, 1589, 1659, 1651, 1607, 1635, 1606, 1652, 1621, 1632, 1541, 1557, 1645, + 1736, 1630, 1856, 1704, 1655, 1800, 1849, 1624, 1751, 1842, 1809, 1635, 1809, 1813, 1711, 1659, 1765, 1739, + 1690, 1716, 1595, 1677, 1679, 1656, 1597, 1719, 1653, 1744, 1840, 1659, 1781, 1781, 1807, 1719, 1823, 1774, + 1672, 1588, 1589, 1623, 1663, 1564, 1511, 1646, 1646, 1513, 1617, 1674, 1540, 1529, 1649, 1644, 1548, 1680, + 1597, 1651, 1557, 1697, 1729, 1793, 1716, 1632, 1672, 1550, 1779, 1743, 1803, 1732, 1679, 1586, 1644, 1842, + 1579, 1540, 1665, 1480, 1553, 1644, 1652, 1553, 1627, 1572, 1602, 1578, 1512, 1606, 1718, 1712, 1567, 1597, + 1582, 1632, 1578, 1618, 1625, 1715, 1670, 1701, 1651, 1621, 1824, 1831, 1834, 1758, 1788, 1712, 1779, 1637, + 1607, 1693, 1659, 1683, 1770, 1656, 1824, 1648, 1721, 1596, 1742, 1751, 1739, 1630, 1560, 1513, 1571, 1830, + 1686, 1763, 1756, 1673, 1698, 1749, 1803, 1694, 1711, 1728, 1725, 1697, 1824, 1723, 1730, 1848, 1837, 1831, + 1716, 1525, 1660, 1645, 1511, 1536, 1795, 1620, 1749, 1785, 1767, 1513, 1637, 1663, 1637, 1638, 1666, 1737, + 1558, 1669, 1649, 1704, 1735, 1574, 1564, 1568, 1627, 1537, 1621, 1651, 1700, 1662, 1630, 1571, 1527, 1715, + 1732, 1557, 1520, 1574, 1628, 1684, 1652, 1653, 1665, 1810, 1627, 1852, 1604, 1554, 1807, 1778, 1823, 1679, + 1684, 1568, 1569, 1618, 1655, 1652, 1614, 1592, 1662, 1683, 1595, 1597, 1623, 1511, 1662, 1645, 1642, 1613, + 1770, 1588, 1754, 1655, 1733, 1858, 1792, 1802, 1620, 1855, 1635, 1624, 1646, 1690, 1788, 1701, 1718, 1562, + 1870, 1649, 1665, 1691, 1704, 1847, 1770, 1866, 1593, 1656, 1873, 1750, 1611, 1828, 1796, 1782, 1744, 1704, + 1732, 1492, 1658, 1485, 1523, 1655, 1672, 1572, 1611, 1715, 1558, 1735, 1687, 1550, 1532, 1677, 1518, 1567, + 1788, 1870, 1610, 1607, 1705, 1837, 1863, 1658, 1609, 1854, 1879, 1785, 1718, 1551, 1852, 1861, 1793, 1610, + 1793, 1606, 1651, 1681, 1792, 1876, 1709, 1763, 1646, 1644, 1809, 1585, 1620, 1795, 1676, 1618, 1593, 1798, + 1550, 1513, 1554, 1539, 1628, 1716, 1739, 1690, 1617, 1597, 1733, 1585, 1548, 1543, 1625, 1645, 1597, 1571, + 1742, 1715, 1858, 1732, 1708, 1806, 1700, 1781, 1676, 1831, 1693, 1824, 1712, 1831, 1709, 1827, 1863, 1820, + 1632, 1803, 1830, 1851, 1534, 1632, 1674, 1777, 1680, 1823, 1865, 1536, 1726, 1660, 1579, 1562, 1562, 1700, + 1666, 1632, 1740, 1639, 1576, 1712, 1728, 1609, 1590, 1653, 1550, 1529, 1751, 1645, 1546, 1628, 1593, 1733, + 1715, 1870, 1870, 1761, 1715, 1844, 1721, 1729, 1744, 1729, 1637, 1628, 1827, 1723, 1817, 1735, 1733, 1642, + 1708, 1764, 1694, 1781, 1866, 1575, 1736, 1700, 1611, 1812, 1557, 1684, 1730, 1669, 1578, 1792, 1686, 1634, + 1651, 1595, 1597, 1656, 1578, 1628, 1611, 1670, 1609, 1551, 1585, 1562, 1568, 1494, 1637, 1725, 1562, 1523, + 1700, 1610, 1880, 1670, 1763, 1795, 1582, 1623, 1609, 1576, 1721, 1739, 1816, 1744, 1655, 1842, 1603, 1722, + 1653, 1600, 1723, 1728, 1754, 1827, 1617, 1667, 1592, 1847, 1810, 1653, 1712, 1800, 1735, 1679, 1744, 1596, +#if TOUCH_KEY_NUM > 0 + 1500, 1500, 1500, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_Rawdata_N[40 * 40] = { + -65535, 771, 677, 791, 749, 700, -65535, -65535, -65535, -65535, -65535, -65535, 748, 732, 684, 711, 680, -65535, + 779, 740, 785, 737, 735, 735, 769, 755, 730, 781, 779, 719, 755, 755, 783, 788, 784, 733, + 685, 702, 708, 674, 649, 690, 697, 662, 718, 656, 706, 660, 666, 646, 652, 674, 688, 716, + 681, 713, 703, 679, 692, 745, 671, 698, 689, 748, 744, 700, 703, 704, 718, 673, 679, 683, + 726, 720, 722, 728, 702, 766, 712, 694, 751, 712, 731, 743, 785, 700, 754, 741, 735, 761, + 661, 691, 740, 754, 672, 777, 707, 755, 740, 690, 670, 711, 668, 709, 664, 709, 725, 655, + 766, 779, 757, 763, 703, 761, 721, 695, 683, 727, 760, 737, 761, 744, 763, 750, 685, 797, + 732, 718, 650, 668, 749, 716, 754, 713, 732, 682, 775, 725, 776, 706, 745, 720, 786, 769, + 672, 681, 695, 712, 724, 683, 678, 673, 719, 694, 694, 653, 709, 663, 671, 701, 700, 676, + 687, 694, 689, 717, 710, 715, 681, 711, 707, 689, 701, 688, 708, 695, 700, 661, 667, 705, + 744, 698, 796, 730, 709, 772, 793, 696, 751, 790, 775, 701, 775, 777, 733, 711, 757, 745, + 724, 736, 683, 719, 719, 710, 685, 737, 709, 748, 788, 711, 763, 763, 775, 737, 781, 760, + 716, 680, 681, 695, 713, 670, 647, 706, 706, 649, 693, 718, 660, 655, 707, 704, 664, 720, + 685, 707, 667, 727, 741, 769, 736, 700, 716, 664, 763, 747, 773, 742, 719, 680, 704, 790, + 677, 660, 713, 634, 665, 704, 708, 665, 697, 674, 686, 676, 648, 688, 736, 734, 671, 685, + 678, 700, 676, 694, 697, 735, 716, 729, 707, 695, 782, 785, 786, 754, 766, 734, 763, 701, + 689, 725, 711, 721, 758, 710, 782, 706, 737, 684, 746, 751, 745, 698, 668, 649, 673, 784, + 722, 755, 752, 717, 728, 749, 773, 726, 733, 740, 739, 727, 782, 739, 742, 792, 787, 785, + 736, 653, 712, 705, 647, 658, 769, 694, 749, 765, 757, 649, 701, 713, 701, 702, 714, 745, + 668, 715, 707, 730, 743, 674, 670, 672, 697, 659, 695, 707, 728, 712, 698, 673, 655, 735, + 742, 667, 652, 674, 698, 722, 708, 709, 713, 776, 697, 794, 688, 666, 775, 762, 781, 719, + 722, 672, 673, 694, 709, 708, 692, 682, 712, 721, 683, 685, 695, 647, 712, 705, 704, 691, + 758, 680, 752, 709, 743, 796, 768, 772, 694, 795, 701, 696, 706, 724, 766, 729, 736, 670, + 802, 707, 713, 725, 730, 791, 758, 800, 683, 710, 803, 750, 691, 784, 770, 764, 748, 730, + 742, 640, 710, 637, 653, 709, 716, 674, 691, 735, 668, 743, 723, 664, 656, 719, 650, 671, + 766, 802, 690, 689, 731, 787, 799, 710, 689, 794, 805, 765, 736, 665, 794, 797, 769, 690, + 769, 688, 707, 721, 768, 804, 733, 755, 706, 704, 775, 679, 694, 769, 718, 694, 683, 770, + 664, 649, 666, 659, 698, 736, 745, 724, 693, 685, 743, 679, 664, 661, 697, 705, 685, 673, + 746, 735, 796, 742, 732, 774, 728, 763, 718, 785, 725, 782, 734, 785, 733, 783, 799, 780, + 700, 773, 784, 793, 658, 700, 718, 761, 720, 781, 799, 658, 740, 712, 677, 670, 670, 728, + 714, 700, 746, 703, 676, 734, 740, 689, 682, 709, 664, 655, 751, 705, 662, 698, 683, 743, + 735, 802, 802, 755, 735, 790, 737, 741, 748, 741, 701, 698, 783, 739, 779, 743, 743, 704, + 732, 756, 726, 763, 800, 675, 744, 728, 691, 776, 667, 722, 742, 715, 676, 768, 722, 700, + 707, 683, 685, 710, 676, 698, 691, 716, 689, 665, 679, 670, 672, 640, 701, 739, 670, 653, + 728, 690, 806, 716, 755, 769, 678, 695, 689, 676, 737, 745, 778, 748, 709, 790, 687, 738, + 709, 686, 739, 740, 752, 783, 693, 715, 682, 791, 776, 709, 734, 772, 743, 719, 748, 684, +#if TOUCH_KEY_NUM > 0 + 800, 800, 800, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_CC_P[40 * 40] = { + 65535, 116, 116, 113, 115, 116, 65535, 65535, 65535, 65535, 65535, 65535, 116, 116, 116, 116, 119, 65535, + 115, 119, 116, 116, 117, 117, 116, 117, 115, 113, 116, 116, 116, 116, 115, 115, 116, 117, + 116, 116, 115, 115, 116, 115, 115, 116, 112, 120, 122, 122, 122, 123, 122, 122, 123, 124, + 120, 117, 116, 116, 117, 116, 117, 117, 115, 116, 119, 119, 119, 119, 117, 119, 120, 122, + 117, 116, 115, 115, 116, 115, 116, 116, 112, 116, 117, 117, 116, 119, 116, 116, 117, 119, + 122, 119, 116, 116, 119, 116, 117, 116, 113, 116, 119, 119, 119, 119, 119, 117, 119, 122, + 119, 116, 115, 115, 116, 115, 116, 116, 113, 117, 119, 120, 119, 120, 119, 119, 122, 122, + 119, 116, 116, 116, 116, 116, 116, 116, 113, 116, 116, 119, 116, 117, 116, 116, 116, 119, + 122, 119, 117, 117, 119, 119, 120, 119, 115, 117, 119, 122, 119, 120, 119, 117, 119, 122, + 122, 119, 117, 117, 119, 117, 120, 117, 115, 117, 117, 120, 117, 117, 117, 117, 119, 120, + 117, 116, 113, 115, 116, 115, 116, 116, 112, 119, 119, 122, 119, 119, 119, 119, 119, 122, + 119, 116, 116, 116, 116, 116, 119, 116, 113, 119, 116, 119, 116, 116, 116, 116, 116, 119, + 120, 119, 117, 117, 117, 119, 122, 119, 115, 122, 119, 119, 117, 117, 116, 116, 117, 119, + 119, 116, 116, 116, 116, 116, 119, 119, 113, 122, 119, 119, 116, 116, 116, 116, 116, 116, + 117, 116, 115, 116, 116, 116, 117, 119, 112, 124, 122, 123, 122, 120, 119, 119, 120, 122, + 119, 116, 116, 116, 116, 116, 119, 119, 113, 122, 119, 119, 116, 116, 116, 116, 116, 119, + 119, 116, 116, 116, 116, 117, 119, 120, 113, 122, 119, 119, 116, 116, 116, 116, 116, 116, + 120, 117, 117, 119, 119, 119, 120, 120, 115, 119, 116, 117, 115, 115, 115, 113, 113, 115, + 116, 116, 115, 115, 116, 116, 116, 116, 111, 122, 119, 122, 119, 117, 117, 117, 117, 119, + 119, 116, 116, 116, 116, 117, 120, 119, 115, 122, 117, 119, 116, 116, 116, 116, 116, 116, + 116, 116, 116, 116, 116, 116, 119, 116, 113, 122, 119, 119, 119, 119, 116, 116, 116, 119, + 119, 117, 117, 117, 117, 119, 122, 119, 115, 120, 116, 119, 116, 116, 115, 115, 115, 116, + 116, 116, 115, 116, 116, 116, 119, 116, 113, 122, 119, 122, 119, 117, 116, 116, 116, 119, + 116, 116, 116, 116, 116, 116, 119, 116, 113, 122, 116, 120, 119, 116, 116, 116, 116, 117, + 116, 116, 115, 116, 116, 116, 119, 117, 112, 119, 117, 119, 116, 116, 116, 115, 116, 117, + 119, 116, 119, 119, 119, 119, 122, 122, 116, 116, 116, 119, 116, 116, 113, 113, 113, 116, + 116, 116, 116, 116, 116, 116, 120, 119, 113, 116, 119, 122, 119, 116, 116, 116, 116, 116, + 123, 122, 122, 122, 122, 122, 124, 124, 117, 113, 119, 122, 119, 117, 116, 116, 116, 117, + 119, 117, 116, 117, 119, 119, 123, 120, 115, 111, 117, 119, 116, 115, 115, 113, 113, 115, + 122, 119, 119, 119, 122, 122, 124, 122, 116, 111, 116, 122, 116, 116, 116, 116, 116, 116, + 119, 117, 116, 117, 119, 119, 122, 120, 115, 115, 122, 124, 119, 119, 119, 117, 119, 119, + 119, 116, 116, 117, 119, 119, 123, 120, 115, 112, 119, 122, 116, 116, 115, 115, 115, 116, + 120, 119, 119, 119, 119, 122, 124, 122, 116, 113, 122, 123, 119, 119, 119, 116, 117, 119, + 122, 120, 120, 120, 122, 122, 127, 123, 117, 112, 117, 122, 116, 116, 115, 113, 115, 116, + 119, 119, 116, 119, 119, 119, 127, 122, 116, 116, 122, 126, 119, 119, 119, 116, 119, 119, + 116, 116, 115, 116, 116, 116, 124, 119, 113, 111, 119, 124, 117, 116, 116, 115, 115, 116, +#if TOUCH_KEY_NUM > 0 + 38, 38, 38, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_CC_N[40 * 40] = { + -65535, 56, 56, 55, 55, 56, -65535, -65535, -65535, -65535, -65535, -65535, 56, 56, 56, 56, 57, -65535, + 55, 57, 56, 56, 57, 57, 56, 57, 55, 55, 56, 56, 56, 56, 55, 55, 56, 57, + 56, 56, 55, 55, 56, 55, 55, 56, 54, 58, 59, 59, 59, 59, 59, 59, 59, 60, + 58, 57, 56, 56, 57, 56, 57, 57, 55, 56, 57, 57, 57, 57, 57, 57, 58, 59, + 57, 56, 55, 55, 56, 55, 56, 56, 54, 56, 57, 57, 56, 57, 56, 56, 57, 57, + 59, 57, 56, 56, 57, 56, 57, 56, 55, 56, 57, 57, 57, 57, 57, 57, 57, 59, + 57, 56, 55, 55, 56, 55, 56, 56, 55, 57, 57, 58, 57, 58, 57, 57, 59, 59, + 57, 56, 56, 56, 56, 56, 56, 56, 55, 56, 56, 57, 56, 57, 56, 56, 56, 57, + 59, 57, 57, 57, 57, 57, 58, 57, 55, 57, 57, 59, 57, 58, 57, 57, 57, 59, + 59, 57, 57, 57, 57, 57, 58, 57, 55, 57, 57, 58, 57, 57, 57, 57, 57, 58, + 57, 56, 55, 55, 56, 55, 56, 56, 54, 57, 57, 59, 57, 57, 57, 57, 57, 59, + 57, 56, 56, 56, 56, 56, 57, 56, 55, 57, 56, 57, 56, 56, 56, 56, 56, 57, + 58, 57, 57, 57, 57, 57, 59, 57, 55, 59, 57, 57, 57, 57, 56, 56, 57, 57, + 57, 56, 56, 56, 56, 56, 57, 57, 55, 59, 57, 57, 56, 56, 56, 56, 56, 56, + 57, 56, 55, 56, 56, 56, 57, 57, 54, 60, 59, 59, 59, 58, 57, 57, 58, 59, + 57, 56, 56, 56, 56, 56, 57, 57, 55, 59, 57, 57, 56, 56, 56, 56, 56, 57, + 57, 56, 56, 56, 56, 57, 57, 58, 55, 59, 57, 57, 56, 56, 56, 56, 56, 56, + 58, 57, 57, 57, 57, 57, 58, 58, 55, 57, 56, 57, 55, 55, 55, 55, 55, 55, + 56, 56, 55, 55, 56, 56, 56, 56, 53, 59, 57, 59, 57, 57, 57, 57, 57, 57, + 57, 56, 56, 56, 56, 57, 58, 57, 55, 59, 57, 57, 56, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 56, 56, 57, 56, 55, 59, 57, 57, 57, 57, 56, 56, 56, 57, + 57, 57, 57, 57, 57, 57, 59, 57, 55, 58, 56, 57, 56, 56, 55, 55, 55, 56, + 56, 56, 55, 56, 56, 56, 57, 56, 55, 59, 57, 59, 57, 57, 56, 56, 56, 57, + 56, 56, 56, 56, 56, 56, 57, 56, 55, 59, 56, 58, 57, 56, 56, 56, 56, 57, + 56, 56, 55, 56, 56, 56, 57, 57, 54, 57, 57, 57, 56, 56, 56, 55, 56, 57, + 57, 56, 57, 57, 57, 57, 59, 59, 56, 56, 56, 57, 56, 56, 55, 55, 55, 56, + 56, 56, 56, 56, 56, 56, 58, 57, 55, 56, 57, 59, 57, 56, 56, 56, 56, 56, + 59, 59, 59, 59, 59, 59, 60, 60, 57, 55, 57, 59, 57, 57, 56, 56, 56, 57, + 57, 57, 56, 57, 57, 57, 59, 58, 55, 53, 57, 57, 56, 55, 55, 55, 55, 55, + 59, 57, 57, 57, 59, 59, 60, 59, 56, 53, 56, 59, 56, 56, 56, 56, 56, 56, + 57, 57, 56, 57, 57, 57, 59, 58, 55, 55, 59, 60, 57, 57, 57, 57, 57, 57, + 57, 56, 56, 57, 57, 57, 59, 58, 55, 54, 57, 59, 56, 56, 55, 55, 55, 56, + 58, 57, 57, 57, 57, 59, 60, 59, 56, 55, 59, 59, 57, 57, 57, 56, 57, 57, + 59, 58, 58, 58, 59, 59, 61, 59, 57, 54, 57, 59, 56, 56, 55, 55, 55, 56, + 57, 57, 56, 57, 57, 57, 61, 59, 56, 56, 59, 60, 57, 57, 57, 56, 57, 57, + 56, 56, 55, 56, 56, 56, 60, 57, 55, 53, 57, 60, 57, 56, 56, 55, 55, 56, +#if TOUCH_KEY_NUM > 0 + 9, 9, 9, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_CC_I_P[40 * 40] = { + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +#if TOUCH_KEY_NUM > 0 + 25, 25, 25, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_CC_I_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_CC_Q_P[40 * 40] = { + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, + 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +#if TOUCH_KEY_NUM > 0 + 25, 25, 25, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_CC_Q_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] = { + 65535, 50, 50, 50, 50, 50, 65535, 65535, 65535, 65535, 65535, 65535, 50, 50, 50, 50, 50, 65535, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, + 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 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 + 35, 35, 35, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Lmt_FW_Diff_N[40 *40] = { + -65535, -50, -50, -50, -50, -50, -65535, -65535, -65535, -65535, -65535, -65535, -50, -50, -50, -50, -50, -65535, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, + -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -50, -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 + -35, -35, -35, +#endif /* #if TOUCH_KEY_NUM > 0 */ +}; + +static int32_t PS_Config_Diff_Test_Frame = 50; + +#endif /* #if NVT_TOUCH_MP */ diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/Android.mk b/drivers/input/touchscreen/parade_tma5xx_mmi/Android.mk new file mode 100644 index 000000000000..c1572736eab8 --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/Android.mk @@ -0,0 +1,83 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5=y + +KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT=y + +ifeq ($(TOUCHSCREEN_CYPRESS_CYTTSP5_I2C),true) + KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_I2C=y +endif + +ifeq ($(TOUCHSCREEN_CYPRESS_CYTTSP5_SPI),true) + KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_SPI=y +endif + + +ifeq ($(TOUCHSCREEN_CYPRESS_CYTTSP5_MT_A),true) + KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MT_A=y +endif + +ifeq ($(TOUCHSCREEN_CYPRESS_CYTTSP5_MT_B),true) + KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MT_B=y +endif + +ifeq ($(TOUCHSCREEN_CYPRESS_CYTTSP5_BUTTON),true) + KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BUTTON=y +endif + +ifeq ($(TOUCHSCREEN_CYPRESS_CYTTSP5_PROXIMITY),true) + KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PROXIMITY=y +endif + +ifeq ($(DRM_PANEL_EVENT_NOTIFICATIONS),true) + KBUILD_OPTIONS += CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS=y +endif + +ifeq ($(DRM_PANEL_NOTIFICATIONS),true) + KBUILD_OPTIONS += CONFIG_DRM_PANEL_NOTIFICATIONS=y +endif + +ifeq ($(GTP_LIMIT_USE_SUPPLIER),true) + KBUILD_OPTIONS += CONFIG_GTP_LIMIT_USE_SUPPLIER=y +endif + +ifneq ($(BOARD_USES_DOUBLE_TAP),) + KBUILD_OPTIONS += CONFIG_INPUT_CYTTSP5_MMI_ENABLE_DOUBLE_TAP=y +endif + +include $(CLEAR_VARS) +ifneq ($(BOARD_USES_DOUBLE_TAP),) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/sensors_class.ko +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := parade_tma5xx_mmi_core.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KBUILD_OPTIONS += CONFIG_INPUT_TOUCHSCREEN_MMI=y + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +endif +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := parade_tma5xx_mmi_loader.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := parade_tma5xx_mmi_debug.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := parade_tma5xx_mmi_access.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICE_ACCESS_API=y +include $(DLKM_DIR)/AndroidKernelModule.mk + +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/Kbuild b/drivers/input/touchscreen/parade_tma5xx_mmi/Kbuild new file mode 100644 index 000000000000..832817219c15 --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/Kbuild @@ -0,0 +1,85 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m := parade_tma5xx_mmi_core.o +parade_tma5xx_mmi_core-objs := \ + cyttsp5_core.o \ + cyttsp5_mt_common.o \ + cyttsp5_platform.o \ + cyttsp5_devtree.o + + +obj-m += parade_tma5xx_mmi_loader.o +parade_tma5xx_mmi_loader-objs := cyttsp5_loader.o + +obj-m += parade_tma5xx_mmi_debug.o +parade_tma5xx_mmi_debug-objs := cyttsp5_debug.o + +obj-m += parade_tma5xx_mmi_access.o +parade_tma5xx_mmi_access-objs := cyttsp5_device_access.o + + +ifneq ($(filter m y,$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_I2C)),) + parade_tma5xx_mmi_core-objs += cyttsp5_i2c.o +endif + +ifneq ($(filter m y,$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_SPI)),) + parade_tma5xx_mmi_core-objs += cyttsp5_spi.o +endif + +ifneq ($(filter m y,$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MT_A)),) + parade_tma5xx_mmi_core-objs += cyttsp5_mta.o +endif + +ifneq ($(filter m y,$(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MT_B)),) + parade_tma5xx_mmi_core-objs += cyttsp5_mtb.o +endif + +ifneq ($(filter m y, $(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5)),) + EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5 +endif + +ifneq ($(filter m y, $(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT)),) + EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT +endif + +ifneq ($(filter m y, $(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BUTTON)),) + EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BUTTON + parade_tma5xx_mmi_core-objs += cyttsp5_btn.o +endif + +ifneq ($(filter m y, $(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PROXIMITY)),) + EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PROXIMITY + parade_tma5xx_mmi_core-objs += cyttsp5_proximity.o +endif + + +ifneq ($(filter m y, $(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICE_ACCESS_API)),) + EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICE_ACCESS_API +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI + parade_tma5xx_mmi_core-objs += cyttsp5_ts_mmi.o +endif + +ifneq ($(filter m y, $(CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_EVENT_NOTIFICATIONS +endif + +ifneq ($(filter m y,$(CONFIG_DRM_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_NOTIFICATIONS +endif + +ifneq ($(filter m y,$(CONFIG_GTP_LIMIT_USE_SUPPLIER)),) + EXTRA_CFLAGS += -DCONFIG_GTP_LIMIT_USE_SUPPLIER +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_CYTTSP5_MMI_ENABLE_DOUBLE_TAP)),) + EXTRA_CFLAGS += -DCYTTSP5_SENSOR_EN + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers +endif + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/Kconfig b/drivers/input/touchscreen/parade_tma5xx_mmi/Kconfig new file mode 100644 index 000000000000..1d95731d41e6 --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/Kconfig @@ -0,0 +1,202 @@ +# +# Parade touchscreen driver configuration +# +config TOUCHSCREEN_CYPRESS_CYTTSP5 + tristate "Parade TrueTouch Gen5 Touchscreen Driver" + help + Core driver for Parade TrueTouch(tm) Standard Product + Geneartion5 touchscreen controllers. + + Say Y here if you have a Parade Gen5 touchscreen. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called cyttsp5. + +config TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT + bool "Enable Device Tree support" + depends on TOUCHSCREEN_CYPRESS_CYTTSP5 && OF + help + Say Y here to enable support for device tree. + + If unsure, say N. + +config TOUCHSCREEN_CYPRESS_CYTTSP5_I2C + tristate "Parade TrueTouch Gen5 I2C" + depends on TOUCHSCREEN_CYPRESS_CYTTSP5 + select I2C + help + Say Y here to enable I2C bus interface to Parade TrueTouch(tm) + Standard Product Generation5 touchscreen controller. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called cyttsp5_i2c. + +config TOUCHSCREEN_CYPRESS_CYTTSP5_SPI + tristate "Parade TrueTouch Gen5 SPI" + depends on TOUCHSCREEN_CYPRESS_CYTTSP5 + select SPI + help + Say Y here to enable SPI bus interface to Parade TrueTouch(tm) + Standard Product Generation5 touchscreen controller. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called cyttsp5_spi. + +choice + bool "Parade TrueTouch Gen5 MultiTouch Protocol" + depends on TOUCHSCREEN_CYPRESS_CYTTSP5 + default TOUCHSCREEN_CYPRESS_CYTTSP5_MT_B + help + This option controls which MultiTouch protocol will be used to + report the touch events. + +config TOUCHSCREEN_CYPRESS_CYTTSP5_MT_A + bool "Protocol A" + help + Select to enable MultiTouch touch reporting using protocol A + on Parade TrueTouch(tm) Standard Product Generation4 touchscreen + controller. + +config TOUCHSCREEN_CYPRESS_CYTTSP5_MT_B + bool "Protocol B" + help + Select to enable MultiTouch touch reporting using protocol B + on Parade TrueTouch(tm) Standard Product Generation4 touchscreen + controller. + +endchoice + +config TOUCHSCREEN_CYPRESS_CYTTSP5_BUTTON + bool "Parade TrueTouch Gen5 MultiTouch CapSense Button" + depends on TOUCHSCREEN_CYPRESS_CYTTSP5 + help + Say Y here to enable CapSense reporting on Parade TrueTouch(tm) + Standard Product Generation5 touchscreen controller. + + If unsure, say N. + +config TOUCHSCREEN_CYPRESS_CYTTSP5_PROXIMITY + bool "Parade TrueTouch Gen5 Proximity" + depends on TOUCHSCREEN_CYPRESS_CYTTSP5 + help + Say Y here to enable proximity reporting on Parade TrueTouch(tm) + Standard Product Generation5 touchscreen controller. + + If unsure, say N. + +config TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICE_ACCESS + tristate "Parade TrueTouch Gen5 MultiTouch Device Access" + depends on TOUCHSCREEN_CYPRESS_CYTTSP5 + help + Say Y here to enable Parade TrueTouch(tm) Standard Product + Generation5 touchscreen controller device access module. + + This modules adds an interface to access touchscreen + controller using driver sysfs nodes. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called cyttsp5_device_access. + +config TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICE_ACCESS_API + bool "Enable Device Access kernel API" + depends on TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICE_ACCESS + help + Say Y here to enable Device access kernel API which provides + access to Parade TrueTouch(tm) Standard Product Generation5 + touchscreen controller for other modules. + + If unsure, say N. + +config TOUCHSCREEN_CYPRESS_CYTTSP5_TEST_DEVICE_ACCESS_API + tristate "Simple Test module for Device Access kernel API" + depends on TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICE_ACCESS + depends on TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICE_ACCESS_API + help + Say Y here to enable test module for Device access kernel API. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called cyttsp5_test_device_access_api. + +config TOUCHSCREEN_CYPRESS_CYTTSP5_LOADER + tristate "Parade TrueTouch Gen5 MultiTouch Loader" + depends on TOUCHSCREEN_CYPRESS_CYTTSP5 + help + Say Y here to enable Parade TrueTouch(tm) Standard Product + Generation5 touchscreen controller FW Loader module. + + This module enables support for Firmware upgrade. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called cyttsp5_loader. + +config TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE + bool "FW upgrade from header file" + depends on TOUCHSCREEN_CYPRESS_CYTTSP5_LOADER + help + Say Y here to include Parade TrueTouch(tm) Standard Product + Generation5 device Firmware into driver. + + Need proper header file for this. + + If unsure, say N. + +config TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE + bool "FW upgrade from binary file" + depends on TOUCHSCREEN_CYPRESS_CYTTSP5_LOADER + help + Say Y here to include Parade TrueTouch(tm) Standard Product + Generation5 device Firmware into kernel as binary blob. + + This should be enabled for manual FW upgrade support. + + If unsure, say Y. + +config TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE + bool "TT Configuration upgrade from header file" + depends on TOUCHSCREEN_CYPRESS_CYTTSP5_LOADER + help + Say Y here to include Parade TrueTouch(tm) Standard Product + Generation5 device TrueTouch Configuration into kernel itself. + + Need proper header file for this. + + If unsure, say N. + +config TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE + bool "TT Configuration upgrade via SysFs" + depends on TOUCHSCREEN_CYPRESS_CYTTSP5_LOADER + help + Say Y here to provide a SysFs interface to upgrade TrueTouch + Configuration with a binary configuration file. + + Need proper binary version of config file for this + feature. + + If unsure, say Y. + +config TOUCHSCREEN_CYPRESS_CYTTSP5_DEBUG_MDL + tristate "Parade TrueTouch Gen5 MultiTouch Debug Module" + depends on TOUCHSCREEN_CYPRESS_CYTTSP5 + help + Say Y here to enable Parade TrueTouch(tm) Standard Product + Generation5 Debug module. + + This module adds support for verbose printing touch + information. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called cyttsp5_debug. diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/Makefile b/drivers/input/touchscreen/parade_tma5xx_mmi/Makefile new file mode 100644 index 000000000000..d20ee691484d --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/Makefile @@ -0,0 +1,17 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(M)/../../../sensors/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(M)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers \ No newline at end of file diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_btn.c b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_btn.c new file mode 100644 index 000000000000..52c0eb85d29e --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_btn.c @@ -0,0 +1,369 @@ +/* + * cyttsp5_btn.c + * Parade TrueTouch(TM) Standard Product V5 CapSense Reports Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * CYTMA5XX + * CYTMA448 + * CYTMA445A + * CYTT21XXX + * CYTT31XXX + * + * Copyright (C) 2015 Parade Technologies + * Copyright (C) 2012-2015 Cypress Semiconductor + * + * 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 + * + */ + +#include "cyttsp5_regs.h" + +#define CYTTSP5_BTN_NAME "cyttsp5_btn" + +static inline void cyttsp5_btn_key_action(struct cyttsp5_btn_data *bd, + int btn_no, int btn_state) +{ + struct device *dev = bd->dev; + struct cyttsp5_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); + + parade_debug(dev, DEBUG_LEVEL_1, "%s: btn=%d key_code=%d %s\n", + __func__, btn_no, si->btn[btn_no].key_code, + btn_state == CY_BTN_PRESSED ? + "PRESSED" : "RELEASED"); +} + +static void cyttsp5_get_btn_touches(struct cyttsp5_btn_data *bd) +{ + struct cyttsp5_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 * CY_BITS_PER_BTN)) + & CY_NUM_BTN_EVENT_ID; + + cyttsp5_btn_key_action(bd, cur_btn, cur_btn_state); + } +} + +static void cyttsp5_btn_lift_all(struct cyttsp5_btn_data *bd) +{ + struct cyttsp5_sysinfo *si = bd->si; + int i; + + if (!si || si->num_btns == 0) + return; + + for (i = 0; i < si->num_btns; i++) + cyttsp5_btn_key_action(bd, i, CY_BTN_RELEASED); +} + +#ifdef VERBOSE_DEBUG +static void cyttsp5_log_btn_data(struct cyttsp5_btn_data *bd) +{ + struct device *dev = bd->dev; + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + u8 *pr_buf = cd->pr_buf; + struct cyttsp5_sysinfo *si = bd->si; + int cur; + int value; + + for (cur = 0; cur < si->num_btns; cur++) { + pr_buf[0] = 0; + if (si->xy_data[0] & (1 << cur)) + value = 1; + else + value = 0; + snprintf(pr_buf, CY_MAX_PRBUF_SIZE, "btn_rec[%d]=0x", cur); + snprintf(pr_buf, CY_MAX_PRBUF_SIZE, "%s%X (%02X)", + pr_buf, value, + le16_to_cpu(si->xy_data[1 + cur * 2])); + + parade_debug(dev, DEBUG_LEVEL_2, "%s: %s\n", __func__, pr_buf); + } +} +#endif + +/* read xy_data for all current CapSense button touches */ +static int cyttsp5_xy_worker(struct cyttsp5_btn_data *bd) +{ + struct cyttsp5_sysinfo *si = bd->si; + + /* extract button press/release touch information */ + if (si->num_btns > 0) { + cyttsp5_get_btn_touches(bd); +#ifdef VERBOSE_DEBUG + /* log button press/release touch information */ + cyttsp5_log_btn_data(bd); +#endif + } + + return 0; +} + +static int cyttsp5_btn_attention(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_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 = cyttsp5_xy_worker(bd); + mutex_unlock(&bd->btn_lock); + if (rc < 0) + dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc); + + return rc; +} + +static int cyttsp5_startup_attention(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_btn_data *bd = &cd->bd; + + mutex_lock(&bd->btn_lock); + cyttsp5_btn_lift_all(bd); + mutex_unlock(&bd->btn_lock); + + return 0; +} + +static int cyttsp5_btn_suspend_attention(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_btn_data *bd = &cd->bd; + + mutex_lock(&bd->btn_lock); + cyttsp5_btn_lift_all(bd); + bd->is_suspended = true; + mutex_unlock(&bd->btn_lock); + + pm_runtime_put(dev); + + return 0; +} + +static int cyttsp5_btn_resume_attention(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_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; +} + +static int cyttsp5_btn_open(struct input_dev *input) +{ + struct device *dev = input->dev.parent; + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_btn_data *bd = &cd->bd; + + pm_runtime_get_sync(dev); + + mutex_lock(&bd->btn_lock); + bd->is_suspended = false; + mutex_unlock(&bd->btn_lock); + + parade_debug(dev, DEBUG_LEVEL_2, "%s: setup subscriptions\n", __func__); + + /* set up touch call back */ + _cyttsp5_subscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_BTN_NAME, + cyttsp5_btn_attention, CY_MODE_OPERATIONAL); + + /* set up startup call back */ + _cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_BTN_NAME, + cyttsp5_startup_attention, 0); + + /* set up suspend call back */ + _cyttsp5_subscribe_attention(dev, CY_ATTEN_SUSPEND, CYTTSP5_BTN_NAME, + cyttsp5_btn_suspend_attention, 0); + + /* set up resume call back */ + _cyttsp5_subscribe_attention(dev, CY_ATTEN_RESUME, CYTTSP5_BTN_NAME, + cyttsp5_btn_resume_attention, 0); + + return 0; +} + +static void cyttsp5_btn_close(struct input_dev *input) +{ + struct device *dev = input->dev.parent; + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_btn_data *bd = &cd->bd; + + _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_BTN_NAME, + cyttsp5_btn_attention, CY_MODE_OPERATIONAL); + + _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_BTN_NAME, + cyttsp5_startup_attention, 0); + + _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_SUSPEND, CYTTSP5_BTN_NAME, + cyttsp5_btn_suspend_attention, 0); + + _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_RESUME, CYTTSP5_BTN_NAME, + cyttsp5_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); +} + +static int cyttsp5_setup_input_device(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_btn_data *bd = &cd->bd; + int i; + int rc; + + parade_debug(dev, DEBUG_LEVEL_2, "%s: Initialize event signals\n", + __func__); + __set_bit(EV_KEY, bd->input->evbit); + parade_debug(dev, DEBUG_LEVEL_2, "%s: Number of buttons %d\n", + __func__, bd->si->num_btns); + for (i = 0; i < bd->si->num_btns; i++) { + parade_debug(dev, DEBUG_LEVEL_2, "%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) + dev_err(dev, "%s: Error, failed register input device r=%d\n", + __func__, rc); + else + bd->input_device_registered = true; + + return rc; +} + +static int cyttsp5_setup_input_attention(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_btn_data *bd = &cd->bd; + int rc; + + bd->si = _cyttsp5_request_sysinfo(dev); + if (!bd->si) + return -1; + + rc = cyttsp5_setup_input_device(dev); + + _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_BTN_NAME, + cyttsp5_setup_input_attention, 0); + + return rc; +} + +int cyttsp5_btn_probe(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_btn_data *bd = &cd->bd; + struct cyttsp5_platform_data *pdata = dev_get_platdata(dev); + struct cyttsp5_btn_platform_data *btn_pdata; + int rc = 0; + + if (!pdata || !pdata->btn_pdata) { + dev_err(dev, "%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. */ + parade_debug(dev, DEBUG_LEVEL_2, "%s: Create the input device and register it\n", + __func__); + bd->input = input_allocate_device(); + if (!bd->input) { + dev_err(dev, "%s: Error, failed to allocate input device\n", + __func__); + rc = -ENODEV; + goto error_alloc_failed; + } + + if (bd->pdata->inp_dev_name) + bd->input->name = bd->pdata->inp_dev_name; + else + bd->input->name = CYTTSP5_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 = cyttsp5_btn_open; + bd->input->close = cyttsp5_btn_close; + input_set_drvdata(bd->input, bd); + + /* get sysinfo */ + bd->si = _cyttsp5_request_sysinfo(dev); + + if (bd->si) { + rc = cyttsp5_setup_input_device(dev); + if (rc) + goto error_init_input; + } else { + dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n", + __func__, bd->si); + _cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP, + CYTTSP5_BTN_NAME, cyttsp5_setup_input_attention, 0); + } + + return 0; + +error_init_input: + input_free_device(bd->input); +error_alloc_failed: +error_no_pdata: + dev_err(dev, "%s failed.\n", __func__); + return rc; +} + +int cyttsp5_btn_release(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_btn_data *bd = &cd->bd; + + if (bd->input_device_registered) { + input_unregister_device(bd->input); + } else { + input_free_device(bd->input); + _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, + CYTTSP5_BTN_NAME, cyttsp5_setup_input_attention, 0); + } + + return 0; +} diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_core.c b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_core.c new file mode 100644 index 000000000000..c153273da1ab --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_core.c @@ -0,0 +1,7156 @@ +/* + * cyttsp5_core.c + * Parade TrueTouch(TM) Standard Product V5 Core Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * CYTMA5XX + * CYTMA448 + * CYTMA445A + * CYTT21XXX + * CYTT31XXX + * + * Copyright (C) 2015 Parade Technologies + * Copyright (C) 2012-2015 Cypress Semiconductor + * + * 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 + * + */ + +#include "cyttsp5_regs.h" +#include "cyttsp5_ts_mmi.h" +#include +#include + +#define CY_CORE_STARTUP_RETRY_COUNT 3 + +MODULE_FIRMWARE(CY_FW_FILE_NAME); + +static const char *cy_driver_core_name = CYTTSP5_CORE_NAME; +static const char *cy_driver_core_version = CY_DRIVER_VERSION; +static const char *cy_driver_core_date = CY_DRIVER_DATE; +static bool cyttsp5_first_probe = true; +static bool is_cyttsp5_probe_success; +static const struct cyttsp5_bus_ops *cyttsp5_bus_ops_save; + +struct cyttsp5_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[CY_HID_MAX_COLLECTIONS]; + struct cyttsp5_hid_report *report; + bool record_field; +}; + +struct cyttsp5_hid_report { + u8 id; + u8 type; + int size; + struct cyttsp5_hid_field *fields[CY_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 *); + int mode; +}; + +struct param_node { + struct list_head node; + u8 id; + u32 value; + u8 size; +}; + +struct module_node { + struct list_head node; + struct cyttsp5_module *module; + void *data; +}; + +struct cyttsp5_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 cyttsp5_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; +}; + +#if defined (CYTTSP5_SENSOR_EN) +static struct sensors_classdev __maybe_unused sensors_touch_cdev = { + .name = "s-dt-gesture", + .vendor = "Parade", + .version = 1, + .type = SENSOR_TYPE_MOTO_DOUBLE_TAP, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + /* WAKE_UP & SPECIAL_REPORT */ + .flags = 1 | 6, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#endif + +#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 HID_OUTPUT_APP_COMMAND(command) \ + .cmd_type = HID_OUTPUT_CMD_APP, \ + .command_code = command + +#define HID_OUTPUT_BL_COMMAND(command) \ + .cmd_type = HID_OUTPUT_CMD_BL, \ + .command_code = command + +#ifdef VERBOSE_DEBUG +void cyttsp5_pr_buf(struct device *dev, u8 *dptr, int size, + const char *data_name) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + u8 *pr_buf = cd->pr_buf; + int i, k; + const char fmt[] = "%02X "; + int max; + + if (!size) + return; + + max = (CY_MAX_PRBUF_SIZE - 1) - sizeof(CY_PR_TRUNCATED); + + pr_buf[0] = 0; + for (i = k = 0; i < size && k < max; i++, k += 3) + scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, dptr[i]); + + if (size) { + parade_debug(dev, DEBUG_LEVEL_2, "%s: %s[0..%d]=%s%s\n", + __func__, data_name, + size - 1, pr_buf, size <= max ? "" : CY_PR_TRUNCATED); + } else { + parade_debug(dev, DEBUG_LEVEL_2, "%s: %s[]\n", + __func__, data_name); + } +} +EXPORT_SYMBOL_GPL(cyttsp5_pr_buf); +#endif + +#ifdef TTHE_TUNER_SUPPORT +static int tthe_print(struct cyttsp5_core_data *cd, u8 *buf, int buf_len, + const u8 *data_name) +{ + int len = strlen(data_name); + int i, n; + u8 *p; + int remain; + u8 data_name_with_time_stamp[100]; + + if (cd->show_timestamp) { + sprintf(data_name_with_time_stamp, "[%u] %s", + jiffies_to_msecs(jiffies), data_name); + data_name = data_name_with_time_stamp; + len = strlen(data_name); + } + + mutex_lock(&cd->tthe_lock); + if (!cd->tthe_buf) + goto exit; + + if (cd->tthe_buf_len + (len + buf_len) > CY_MAX_PRBUF_SIZE) + goto exit; + + if (len + buf_len == 0) + goto exit; + + remain = CY_MAX_PRBUF_SIZE - cd->tthe_buf_len; + if (remain < len) + len = remain; + + p = cd->tthe_buf + cd->tthe_buf_len; + memcpy(p, data_name, len); + cd->tthe_buf_len += len; + p += len; + remain -= len; + + *p = 0; + for (i = 0; i < buf_len; i++) { + n = scnprintf(p, remain, "%02X ", buf[i]); + if (!n) + break; + p += n; + remain -= n; + cd->tthe_buf_len += n; + } + + n = scnprintf(p, remain, "\n"); + if (!n) + cd->tthe_buf[cd->tthe_buf_len] = 0; + cd->tthe_buf_len += n; + wake_up(&cd->wait_q); +exit: + mutex_unlock(&cd->tthe_lock); + return 0; +} + +static int _cyttsp5_request_tthe_print(struct device *dev, u8 *buf, + int buf_len, const u8 *data_name) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + return tthe_print(cd, buf, buf_len, data_name); +} +#endif + +/* + * cyttsp5_platform_detect_read() + * + * This function is passed to platform detect + * function to perform a read operation + */ +static int cyttsp5_platform_detect_read(struct device *dev, void *buf, int size) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + return cyttsp5_adap_read_default(cd, buf, size); +} + +/* Must be called with cd->hid_report_lock acquired */ +static struct cyttsp5_hid_report *cyttsp5_get_hid_report_( + struct cyttsp5_core_data *cd, u8 report_type, u8 report_id, + bool create) +{ + struct cyttsp5_hid_report *report = NULL; + int i; + + /* Look for created reports */ + for (i = 0; i < cd->num_hid_reports; i++) { + if (cd->hid_reports[i]->type == report_type + && cd->hid_reports[i]->id == report_id) { + return cd->hid_reports[i]; + } + } + + /* Create a new report */ + if (create && cd->num_hid_reports < CY_HID_MAX_REPORTS) { + report = kzalloc(sizeof(struct cyttsp5_hid_report), + GFP_KERNEL); + if (!report) + return NULL; + + report->type = report_type; + report->id = report_id; + cd->hid_reports[cd->num_hid_reports++] = report; + } + + return report; +} + +/* Must be called with cd->hid_report_lock acquired */ +static void cyttsp5_free_hid_reports_(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_hid_report *report; + int i, j; + + for (i = 0; i < cd->num_hid_reports; i++) { + report = cd->hid_reports[i]; + for (j = 0; j < report->num_fields; j++) + kfree(report->fields[j]); + kfree(report); + cd->hid_reports[i] = NULL; + } + + cd->num_hid_reports = 0; +} + +static void cyttsp5_free_hid_reports(struct cyttsp5_core_data *cd) +{ + mutex_lock(&cd->hid_report_lock); + cyttsp5_free_hid_reports_(cd); + mutex_unlock(&cd->hid_report_lock); +} + +/* Must be called with cd->hid_report_lock acquired */ +static struct cyttsp5_hid_field *cyttsp5_create_hid_field_( + struct cyttsp5_hid_report *report) +{ + struct cyttsp5_hid_field *field; + + if (!report) + return NULL; + + if (report->num_fields == CY_HID_MAX_FIELDS) + return NULL; + + field = kzalloc(sizeof(struct cyttsp5_hid_field), GFP_KERNEL); + if (!field) + return NULL; + + field->report = report; + + report->fields[report->num_fields++] = field; + + return field; +} + +static int cyttsp5_add_parameter(struct cyttsp5_core_data *cd, + u8 param_id, u32 param_value, u8 param_size) +{ + struct param_node *param, *param_new; + + /* Check if parameter exists */ + spin_lock(&cd->spinlock); + list_for_each_entry(param, &cd->param_list, node) { + if (param->id == param_id) { + /* Update parameter */ + param->value = param_value; + parade_debug(cd->dev, DEBUG_LEVEL_2, "%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; + + parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Add parameter id:%d value:%d size:%d\n", + __func__, param_id, param_value, param_size); + + spin_lock(&cd->spinlock); + list_add(¶m_new->node, &cd->param_list); +exit_unlock: + spin_unlock(&cd->spinlock); + + return 0; +} + +int request_exclusive(struct cyttsp5_core_data *cd, void *ownptr, + int timeout_ms) +{ + int t = msecs_to_jiffies(timeout_ms); + bool with_timeout = (timeout_ms != 0); + + 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)) { + dev_err(cd->dev, "%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); + parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: request_exclusive ok=%p\n", + __func__, ownptr); + + return 0; +} + +static int release_exclusive_(struct cyttsp5_core_data *cd, void *ownptr) +{ + if (cd->exclusive_dev != ownptr) + return -EINVAL; + + parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: exclusive_dev %p freed\n", + __func__, cd->exclusive_dev); + cd->exclusive_dev = NULL; + wake_up(&cd->wait_q); + return 0; +} + +/* + * returns error if was not owned + */ +int release_exclusive(struct cyttsp5_core_data *cd, void *ownptr) +{ + int rc; + + mutex_lock(&cd->system_lock); + rc = release_exclusive_(cd, ownptr); + mutex_unlock(&cd->system_lock); + + return rc; +} + +static int cyttsp5_hid_exec_cmd_(struct cyttsp5_core_data *cd, + struct cyttsp5_hid_cmd *hid_cmd) +{ + int rc; + u8 *cmd; + u8 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 */ + + 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; + } + + rc = cyttsp5_adap_write_read_specific(cd, cmd_length, cmd, + hid_cmd->read_buf); + if (rc) + dev_err(cd->dev, "%s: Fail cyttsp5_adap_transfer\n", __func__); + + kfree(cmd); + return rc; +} + +static int cyttsp5_hid_exec_cmd_and_wait_(struct cyttsp5_core_data *cd, + struct cyttsp5_hid_cmd *hid_cmd) +{ + int rc; + 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 = cyttsp5_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 = CY_HID_RESET_TIMEOUT; + + t = wait_event_timeout(cd->wait_q, (*cmd_state == 0), + msecs_to_jiffies(timeout_ms)); + if (IS_TMO(t)) { + dev_err(cd->dev, "%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; +} + +static int cyttsp5_hid_cmd_reset_(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_hid_cmd hid_cmd = { + .opcode = HID_CMD_RESET, + .wait_interrupt = 1, + .reset_cmd = 1, + .timeout_ms = CY_HID_RESET_TIMEOUT, + }; + + return cyttsp5_hid_exec_cmd_and_wait_(cd, &hid_cmd); +} + +static int cyttsp5_hid_cmd_reset(struct cyttsp5_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_cmd_reset_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int cyttsp5_hid_cmd_set_power_(struct cyttsp5_core_data *cd, + u8 power_state) +{ + int rc; + struct cyttsp5_hid_cmd hid_cmd = { + .opcode = HID_CMD_SET_POWER, + .wait_interrupt = 1, + .timeout_ms = CY_HID_SET_POWER_TIMEOUT, + }; + hid_cmd.power_state = power_state; + + rc = cyttsp5_hid_exec_cmd_and_wait_(cd, &hid_cmd); + if (rc) { + dev_err(cd->dev, "%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; +} + +static int cyttsp5_hid_cmd_set_power(struct cyttsp5_core_data *cd, + u8 power_state) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_cmd_set_power_(cd, power_state); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%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, +}; + +static u16 _cyttsp5_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; +} + +static int cyttsp5_hid_output_validate_bl_response( + struct cyttsp5_core_data *cd, + struct cyttsp5_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[HID_OUTPUT_RESPONSE_REPORT_OFFSET] + != HID_BL_RESPONSE_REPORT_ID) { + dev_err(cd->dev, "%s: HID output response, wrong report_id\n", + __func__); + return -EPROTO; + } + + if (cd->response_buf[4] != HID_OUTPUT_BL_SOP) { + dev_err(cd->dev, "%s: HID output response, wrong SOP\n", + __func__); + return -EPROTO; + } + + if (cd->response_buf[size - 1] != HID_OUTPUT_BL_EOP) { + dev_err(cd->dev, "%s: HID output response, wrong EOP\n", + __func__); + return -EPROTO; + } + + crc = _cyttsp5_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)) { + dev_err(cd->dev, "%s: HID output response, wrong CRC 0x%X\n", + __func__, crc); + return -EPROTO; + } + + status = cd->response_buf[5]; + if (status) { + dev_err(cd->dev, "%s: HID output response, ERROR:%d\n", + __func__, status); + return -EPROTO; + } + + return 0; +} + +static int cyttsp5_hid_output_validate_app_response( + struct cyttsp5_core_data *cd, + struct cyttsp5_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[HID_OUTPUT_RESPONSE_REPORT_OFFSET] + != HID_APP_RESPONSE_REPORT_ID) { + dev_err(cd->dev, "%s: HID output response, wrong report_id\n", + __func__); + return -EPROTO; + } + + command_code = cd->response_buf[HID_OUTPUT_RESPONSE_CMD_OFFSET] + & HID_OUTPUT_RESPONSE_CMD_MASK; + if (command_code != hid_output->command_code) { + dev_err(cd->dev, + "%s: HID output response, wrong command_code:%X\n", + __func__, command_code); + return -EPROTO; + } + + return 0; +} + +static void cyttsp5_check_set_parameter(struct cyttsp5_core_data *cd, + struct cyttsp5_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 & CY_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] == + HID_APP_OUTPUT_REPORT_ID + && hid_output->write_buf[6] == + HID_OUTPUT_SET_PARAM) + param_buf = &hid_output->write_buf[7]; + else if (!raw && hid_output->cmd_type == HID_OUTPUT_CMD_APP + && hid_output->command_code == HID_OUTPUT_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) { + dev_err(cd->dev, "%s: Invalid parameter size\n", __func__); + return; + } + + param_buf = ¶m_buf[2]; + while (i < param_size) + param_value += *(param_buf++) << (8 * i++); + + /* Check command response for Set Parameter command */ + if (cd->response_buf[2] != HID_APP_RESPONSE_REPORT_ID + || (cd->response_buf[4] & HID_OUTPUT_CMD_MASK) != + HID_OUTPUT_SET_PARAM + || cd->response_buf[5] != param_id + || cd->response_buf[6] != param_size) { + dev_err(cd->dev, "%s: Set Parameter command not successful\n", + __func__); + return; + } + + cyttsp5_add_parameter(cd, param_id, param_value, param_size); +} + +static void cyttsp5_check_command(struct cyttsp5_core_data *cd, + struct cyttsp5_hid_output *hid_output, bool raw) +{ + cyttsp5_check_set_parameter(cd, hid_output, raw); +} + +static int cyttsp5_hid_output_validate_response(struct cyttsp5_core_data *cd, + struct cyttsp5_hid_output *hid_output) +{ + if (hid_output->cmd_type == HID_OUTPUT_CMD_BL) + return cyttsp5_hid_output_validate_bl_response(cd, hid_output); + + return cyttsp5_hid_output_validate_app_response(cd, hid_output); + +} + +static int cyttsp5_hid_send_output_user_(struct cyttsp5_core_data *cd, + struct cyttsp5_hid_output *hid_output) +{ + int rc; + + if (!hid_output->length || !hid_output->write_buf) + return -EINVAL; + + rc = cyttsp5_adap_write_read_specific(cd, hid_output->length, + hid_output->write_buf, NULL); + if (rc) + dev_err(cd->dev, "%s: Fail cyttsp5_adap_transfer\n", __func__); + + return rc; +} + +static int cyttsp5_hid_send_output_user_and_wait_(struct cyttsp5_core_data *cd, + struct cyttsp5_hid_output *hid_output) +{ + int rc; + int t; + + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = HID_OUTPUT_USER_CMD + 1; + mutex_unlock(&cd->system_lock); + + rc = cyttsp5_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(CY_HID_OUTPUT_USER_TIMEOUT)); + if (IS_TMO(t)) { + dev_err(cd->dev, "%s: HID output cmd execution timed out\n", + __func__); + rc = -ETIME; + goto error; + } + + cyttsp5_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; +} + +static int cyttsp5_hid_send_output_(struct cyttsp5_core_data *cd, + struct cyttsp5_hid_output *hid_output) +{ + int rc; + u8 *cmd; + u16 length; + u8 report_id; + u8 cmd_offset = 0; + u16 crc; + u8 cmd_allocated = 0; + + switch (hid_output->cmd_type) { + case HID_OUTPUT_CMD_APP: + report_id = HID_APP_OUTPUT_REPORT_ID; + length = 5; + break; + case HID_OUTPUT_CMD_BL: + report_id = HID_BL_OUTPUT_REPORT_ID; + length = 11 /* 5 + SOP + LEN(2) + CRC(2) + EOP */; + break; + default: + return -EINVAL; + } + + length += hid_output->write_length; + + if (length + 2 > CYTTSP5_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 == HID_OUTPUT_CMD_BL) + cmd[cmd_offset++] = HID_OUTPUT_BL_SOP; + cmd[cmd_offset++] = hid_output->command_code; + + /* Set Data Length for bootloader */ + if (hid_output->cmd_type == HID_OUTPUT_CMD_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 == HID_OUTPUT_CMD_BL) { + crc = _cyttsp5_compute_crc(&cmd[6], + hid_output->write_length + 4); + cmd[cmd_offset++] = LOW_BYTE(crc); + cmd[cmd_offset++] = HI_BYTE(crc); + cmd[cmd_offset++] = HID_OUTPUT_BL_EOP; + } + + cyttsp5_pr_buf(cd->dev, cmd, length + 2, "command"); + rc = cyttsp5_adap_write_read_specific(cd, length + 2, cmd, NULL); + if (rc) + dev_err(cd->dev, "%s: Fail cyttsp5_adap_transfer\n", __func__); + + if (cmd_allocated) + kfree(cmd); + return rc; +} + +static int cyttsp5_hid_send_output_and_wait_(struct cyttsp5_core_data *cd, + struct cyttsp5_hid_output *hid_output) +{ + int rc; + int t; +#ifdef VERBOSE_DEBUG + u16 size; +#endif + 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 = CY_HID_OUTPUT_TIMEOUT; + + rc = cyttsp5_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)) { + dev_err(cd->dev, "%s: HID output cmd execution timed out\n", + __func__); + rc = -ETIME; + goto error; + } + + if (!hid_output->novalidate) + rc = cyttsp5_hid_output_validate_response(cd, hid_output); + + cyttsp5_check_command(cd, hid_output, false); + +#ifdef VERBOSE_DEBUG + size = get_unaligned_le16(&cd->response_buf[0]); + cyttsp5_pr_buf(cd->dev, cd->response_buf, size, "return_buf"); +#endif + + goto exit; + +error: + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = 0; + mutex_unlock(&cd->system_lock); +exit: + return rc; +} + +static int cyttsp5_hid_output_null_(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_NULL), + }; + + return cyttsp5_hid_send_output_and_wait_(cd, &hid_output); +} + +static int cyttsp5_hid_output_null(struct cyttsp5_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_null_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int cyttsp5_hid_output_start_bootloader_(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_START_BOOTLOADER), + .timeout_ms = CY_HID_OUTPUT_START_BOOTLOADER_TIMEOUT, + .reset_expected = 1, + }; + + return cyttsp5_hid_send_output_and_wait_(cd, &hid_output); +} + +static int cyttsp5_hid_output_start_bootloader(struct cyttsp5_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_start_bootloader_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_start_bl(struct device *dev, int protect) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_start_bootloader(cd); + + return cyttsp5_hid_output_start_bootloader_(cd); +} + +static void cyttsp5_si_get_cydata(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_cydata *cydata = &cd->sysinfo.cydata; + struct cyttsp5_cydata_dev *cydata_dev = + (struct cyttsp5_cydata_dev *) + &cd->response_buf[HID_SYSINFO_CYDATA_OFFSET]; + + cydata->pip_ver_major = cydata_dev->pip_ver_major; + cydata->pip_ver_minor = cydata_dev->pip_ver_minor; + cydata->bl_ver_major = cydata_dev->bl_ver_major; + cydata->bl_ver_minor = cydata_dev->bl_ver_minor; + cydata->fw_ver_major = cydata_dev->fw_ver_major; + cydata->fw_ver_minor = cydata_dev->fw_ver_minor; + + cydata->fw_pid = get_unaligned_le16(&cydata_dev->fw_pid); + cydata->fw_ver_conf = get_unaligned_le16(&cydata_dev->fw_ver_conf); + cydata->post_code = get_unaligned_le16(&cydata_dev->post_code); + cydata->revctrl = get_unaligned_le32(&cydata_dev->revctrl); + cydata->jtag_id_l = get_unaligned_le16(&cydata_dev->jtag_si_id_l); + cydata->jtag_id_h = get_unaligned_le16(&cydata_dev->jtag_si_id_h); + + memcpy(cydata->mfg_id, cydata_dev->mfg_id, CY_NUM_MFGID); + + cyttsp5_pr_buf(cd->dev, (u8 *)cydata_dev, + sizeof(struct cyttsp5_cydata_dev), "sysinfo_cydata"); +} + +static void cyttsp5_si_get_sensing_conf_data(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_sensing_conf_data *scd = &cd->sysinfo.sensing_conf_data; + struct cyttsp5_sensing_conf_data_dev *scd_dev = + (struct cyttsp5_sensing_conf_data_dev *) + &cd->response_buf[HID_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; + } + +#ifndef PT_PANEL_ID_FROM_MFG + scd->panel_id = scd_dev->panel_id; +#endif + 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); + + cyttsp5_pr_buf(cd->dev, (u8 *)scd_dev, + sizeof(struct cyttsp5_sensing_conf_data_dev), + "sensing_conf_data"); +} + +static int cyttsp5_si_setup(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_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; +} + +static int cyttsp5_si_get_btn_data(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_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[HID_SYSINFO_BTN_OFFSET] + & HID_SYSINFO_BTN_MASK; + size_t btn_keys_size; + + parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: get btn data\n", __func__); + + for (i = 0; i < HID_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 cyttsp5_btn); + if (!si->btn) + si->btn = kzalloc(btn_keys_size, GFP_KERNEL); + if (!si->btn) + return -ENOMEM; + + if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS] == NULL) + num_defined_keys = 0; + else if (cd->cpdata->sett[CY_IC_GRPNUM_BTN_KEYS]->data == NULL) + num_defined_keys = 0; + else + num_defined_keys = cd->cpdata->sett + [CY_IC_GRPNUM_BTN_KEYS]->size; + + for (btn = 0; btn < num_btns && btn < num_defined_keys; btn++) { + key_table = (u16 *)cd->cpdata->sett + [CY_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; +} + +static void cyttsp5_si_put_log_data(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_sysinfo *si = &cd->sysinfo; + struct cyttsp5_cydata *cydata = &si->cydata; + struct cyttsp5_sensing_conf_data *scd = &si->sensing_conf_data; + int i; + + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: pip_ver_major =0x%02X (%d)\n", + __func__, cydata->pip_ver_major, cydata->pip_ver_major); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: pip_ver_minor =0x%02X (%d)\n", + __func__, cydata->pip_ver_minor, cydata->pip_ver_minor); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: fw_pid =0x%04X (%d)\n", + __func__, cydata->fw_pid, cydata->fw_pid); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: fw_ver_major =0x%02X (%d)\n", + __func__, cydata->fw_ver_major, cydata->fw_ver_major); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: fw_ver_minor =0x%02X (%d)\n", + __func__, cydata->fw_ver_minor, cydata->fw_ver_minor); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: revctrl =0x%08X (%d)\n", + __func__, cydata->revctrl, cydata->revctrl); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: fw_ver_conf =0x%04X (%d)\n", + __func__, cydata->fw_ver_conf, cydata->fw_ver_conf); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: bl_ver_major =0x%02X (%d)\n", + __func__, cydata->bl_ver_major, cydata->bl_ver_major); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: bl_ver_minor =0x%02X (%d)\n", + __func__, cydata->bl_ver_minor, cydata->bl_ver_minor); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: jtag_id_h =0x%04X (%d)\n", + __func__, cydata->jtag_id_h, cydata->jtag_id_h); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: jtag_id_l =0x%04X (%d)\n", + __func__, cydata->jtag_id_l, cydata->jtag_id_l); + for (i = 0; i < CY_NUM_MFGID; i++) + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: mfg_id[%d] =0x%02X (%d)\n", + __func__, i, cydata->mfg_id[i], cydata->mfg_id[i]); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: post_code =0x%04X (%d)\n", + __func__, cydata->post_code, cydata->post_code); + + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: electrodes_x =0x%02X (%d)\n", + __func__, scd->electrodes_x, scd->electrodes_x); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: electrodes_y =0x%02X (%d)\n", + __func__, scd->electrodes_y, scd->electrodes_y); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: len_x =0x%04X (%d)\n", + __func__, scd->len_x, scd->len_x); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: len_y =0x%04X (%d)\n", + __func__, scd->len_y, scd->len_y); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: res_x =0x%04X (%d)\n", + __func__, scd->res_x, scd->res_x); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: res_y =0x%04X (%d)\n", + __func__, scd->res_y, scd->res_y); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: max_z =0x%04X (%d)\n", + __func__, scd->max_z, scd->max_z); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: origin_x =0x%02X (%d)\n", + __func__, scd->origin_x, scd->origin_x); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: origin_y =0x%02X (%d)\n", + __func__, scd->origin_y, scd->origin_y); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: panel_id =0x%02X (%d)\n", + __func__, scd->panel_id, scd->panel_id); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: btn =0x%02X (%d)\n", + __func__, scd->btn, scd->btn); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: scan_mode =0x%02X (%d)\n", + __func__, scd->scan_mode, scd->scan_mode); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: max_num_of_tch_per_refresh_cycle =0x%02X (%d)\n", + __func__, scd->max_tch, scd->max_tch); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: xy_mode =%p\n", + __func__, si->xy_mode); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: xy_data =%p\n", + __func__, si->xy_data); +} + +static int cyttsp5_get_sysinfo_regs(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_sysinfo *si = &cd->sysinfo; + int rc; + + rc = cyttsp5_si_get_btn_data(cd); + if (rc < 0) + return rc; + + cyttsp5_si_get_cydata(cd); + + cyttsp5_si_get_sensing_conf_data(cd); + + cyttsp5_si_setup(cd); + + cyttsp5_si_put_log_data(cd); + + si->ready = true; + return rc; +} + +static void cyttsp5_free_si_ptrs(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_sysinfo *si = &cd->sysinfo; + + kfree(si->btn); + kfree(si->xy_mode); + kfree(si->xy_data); +} + +static int cyttsp5_hid_output_get_sysinfo_(struct cyttsp5_core_data *cd) +{ + int rc; + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_GET_SYSINFO), + .timeout_ms = CY_HID_OUTPUT_GET_SYSINFO_TIMEOUT, + }; + + rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + rc = cyttsp5_get_sysinfo_regs(cd); + if (rc) + cyttsp5_free_si_ptrs(cd); + + return rc; +} + +static int cyttsp5_hid_output_get_sysinfo(struct cyttsp5_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_get_sysinfo_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int cyttsp5_hid_output_suspend_scanning_(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_SUSPEND_SCANNING), + }; + + return cyttsp5_hid_send_output_and_wait_(cd, &hid_output); +} + +static int cyttsp5_hid_output_suspend_scanning(struct cyttsp5_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_suspend_scanning_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_suspend_scanning(struct device *dev, + int protect) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_suspend_scanning(cd); + + return cyttsp5_hid_output_suspend_scanning_(cd); +} + +static int cyttsp5_hid_output_resume_scanning_(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_RESUME_SCANNING), + }; + + return cyttsp5_hid_send_output_and_wait_(cd, &hid_output); +} + +static int cyttsp5_hid_output_resume_scanning(struct cyttsp5_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_resume_scanning_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_resume_scanning(struct device *dev, + int protect) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_resume_scanning(cd); + + return cyttsp5_hid_output_resume_scanning_(cd); +} + +static int cyttsp5_hid_output_get_param_(struct cyttsp5_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; + int i; + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_GET_PARAM), + .write_length = write_length, + .write_buf = param, + }; + + rc = cyttsp5_hid_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; +} + +static int cyttsp5_hid_output_get_param(struct cyttsp5_core_data *cd, + u8 param_id, u32 *value) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_get_param_(cd, param_id, value); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +int _cyttsp5_request_hid_output_get_param(struct device *dev, + int protect, u8 param_id, u32 *value) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_get_param(cd, param_id, value); + + return cyttsp5_hid_output_get_param_(cd, param_id, value); +} + +static int cyttsp5_hid_output_set_param_(struct cyttsp5_core_data *cd, + u8 param_id, u32 value, u8 size) +{ + u8 write_buf[6]; + u8 *ptr = &write_buf[2]; + int rc; + int i; + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_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 = cyttsp5_hid_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; +} + +static int cyttsp5_hid_output_set_param(struct cyttsp5_core_data *cd, + u8 param_id, u32 value, u8 size) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_set_param_(cd, param_id, value, size); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +int _cyttsp5_request_hid_output_set_param(struct device *dev, + int protect, u8 param_id, u32 value, u8 size) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_set_param(cd, param_id, value, size); + + return cyttsp5_hid_output_set_param_(cd, param_id, value, size); +} + +static int cyttsp5_hid_output_enter_easywake_state_( + struct cyttsp5_core_data *cd, u8 data, u8 *return_data) +{ + int write_length = 1; + u8 param[1] = { data }; + int rc; + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_ENTER_EASYWAKE_STATE), + .write_length = write_length, + .write_buf = param, + }; + + rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + *return_data = cd->response_buf[5]; + return rc; +} + +static int cyttsp5_hid_output_verify_config_block_crc_( + struct cyttsp5_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; + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_VERIFY_CONFIG_BLOCK_CRC), + .write_length = write_length, + .write_buf = param, + }; + + rc = cyttsp5_hid_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; +} + +static int cyttsp5_hid_output_verify_config_block_crc( + struct cyttsp5_core_data *cd, u8 ebid, u8 *status, + u16 *calculated_crc, u16 *stored_crc) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_verify_config_block_crc_(cd, ebid, status, + calculated_crc, stored_crc); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_verify_config_block_crc( + struct device *dev, int protect, u8 ebid, u8 *status, + u16 *calculated_crc, u16 *stored_crc) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_verify_config_block_crc(cd, ebid, + status, calculated_crc, stored_crc); + + return cyttsp5_hid_output_verify_config_block_crc_(cd, ebid, + status, calculated_crc, stored_crc); +} + +static int cyttsp5_hid_output_get_config_row_size_(struct cyttsp5_core_data *cd, + u16 *row_size) +{ + int rc; + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_GET_CONFIG_ROW_SIZE), + }; + + rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + *row_size = get_unaligned_le16(&cd->response_buf[5]); + return 0; +} + +static int cyttsp5_hid_output_get_config_row_size(struct cyttsp5_core_data *cd, + u16 *row_size) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_get_config_row_size_(cd, row_size); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_get_config_row_size(struct device *dev, + int protect, u16 *row_size) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_get_config_row_size(cd, row_size); + + return cyttsp5_hid_output_get_config_row_size_(cd, row_size); +} + +static int cyttsp5_hid_output_read_conf_block_(struct cyttsp5_core_data *cd, + u16 row_number, u16 length, u8 ebid, u8 *read_buf, u16 *crc) +{ + int read_ebid; + int read_length; + int status; + int rc; + int write_length = 5; + u8 write_buf[5]; + u8 cmd_offset = 0; + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_READ_CONF_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 = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + status = cd->response_buf[5]; + if (status) + return -EINVAL; + + read_ebid = cd->response_buf[6]; + if ((read_ebid != ebid) || (cd->response_buf[9] != 0)) + return -EPROTO; + + read_length = get_unaligned_le16(&cd->response_buf[7]); + if (length > read_length) + length = read_length; + + memcpy(read_buf, &cd->response_buf[10], length); + *crc = get_unaligned_le16(&cd->response_buf[read_length + 10]); + + return 0; +} + +static int cyttsp5_hid_output_read_conf_ver_(struct cyttsp5_core_data *cd, + u16 *config_ver) +{ + int rc; + u8 read_buf[CY_TTCONFIG_VERSION_OFFSET + CY_TTCONFIG_VERSION_SIZE]; + u16 crc; + + rc = cyttsp5_hid_output_read_conf_block_(cd, CY_TTCONFIG_VERSION_ROW, + CY_TTCONFIG_VERSION_OFFSET + CY_TTCONFIG_VERSION_SIZE, + CY_TCH_PARM_EBID, read_buf, &crc); + if (rc) + return rc; + + *config_ver = get_unaligned_le16( + &read_buf[CY_TTCONFIG_VERSION_OFFSET]); + + return 0; +} + +static int cyttsp5_hid_output_write_conf_block_(struct cyttsp5_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 */ + int full_write_length = 2 + 2 + 1 + write_length + 8 + 2; + u8 *full_write_buf; + u8 cmd_offset = 0; + u16 crc; + int status; + int rc; + int read_ebid; + u8 *data; + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_WRITE_CONF_BLOCK), + .write_length = full_write_length, + .timeout_ms = CY_HID_OUTPUT_WRITE_CONF_BLOCK_TIMEOUT, + }; + + 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 = _cyttsp5_compute_crc(data, write_length); + full_write_buf[cmd_offset++] = LOW_BYTE(crc); + full_write_buf[cmd_offset++] = HI_BYTE(crc); + + rc = cyttsp5_hid_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; +} + +static int cyttsp5_hid_output_write_conf_block(struct cyttsp5_core_data *cd, + u16 row_number, u16 write_length, u8 ebid, u8 *write_buf, + u8 *security_key, u16 *actual_write_len) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_write_conf_block_(cd, row_number, write_length, + ebid, write_buf, security_key, actual_write_len); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_write_conf_block(struct device *dev, + int protect, u16 row_number, u16 write_length, u8 ebid, + u8 *write_buf, u8 *security_key, u16 *actual_write_len) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_write_conf_block(cd, row_number, + write_length, ebid, write_buf, security_key, + actual_write_len); + + return cyttsp5_hid_output_write_conf_block_(cd, row_number, + write_length, ebid, write_buf, security_key, + actual_write_len); +} + +#define MFG_WRITE_LENGTH 128 +int cyttsp5_write_mfg_val_(struct cyttsp5_core_data *cd, u8 *data, u8 data_len) +{ + int rc; + u16 actual_write_len; + u16 mfg_data_size; + u16 crc; + u8 write_buf[MFG_WRITE_LENGTH]; + u8 cyttsp5_security_key[] = {0xA5, 0x01, 0x02, 0x03, + 0xFF, 0xFE, 0xFD, 0x5A}; + + if (data_len > MFG_WRITE_LENGTH) + return -EINVAL; + + memset(write_buf, 0, MFG_WRITE_LENGTH); + rc = cyttsp5_hid_output_suspend_scanning_(cd); + if (rc) { + dev_err(cd->dev, "%s: fail to suspend scan\n", __func__); + goto exit; + } + + rc = cyttsp5_hid_output_read_conf_block_(cd, 0, 4, CY_MDATA_EBID, + write_buf, &crc); + if (rc) { + dev_err(cd->dev, "%s: fail to suspend scan\n", __func__); + goto exit; + } + + memcpy(&write_buf[4], data, data_len); + dev_info(cd->dev, "%s: Writing %x %x %x %x %x\n", __func__, + write_buf[0], write_buf[1], write_buf[2], write_buf[3], write_buf[4]); + + mfg_data_size = get_unaligned_le16(&write_buf[0]); + if (mfg_data_size > MFG_WRITE_LENGTH) { + dev_err(cd->dev, "%s: MFG data size %d is not available\n", __func__, mfg_data_size); + rc = -EINVAL; + goto exit; + } + dev_info(cd->dev, "%s: MFG data size %d\n", __func__, mfg_data_size); + crc = crc_ccitt_calculate(&write_buf[0], mfg_data_size); + dev_info(cd->dev, "%s: MFG data size %d, 0x%04x\n", __func__, mfg_data_size, crc); + write_buf[MFG_WRITE_LENGTH-2] = LOW_BYTE(crc); + write_buf[MFG_WRITE_LENGTH-1] = HI_BYTE(crc); + + rc = cyttsp5_hid_output_write_conf_block_( + cd, 0, MFG_WRITE_LENGTH, CY_MDATA_EBID, write_buf, + &cyttsp5_security_key[0], &actual_write_len); + if (rc) { + dev_err(cd->dev, "%s: Fail to write MFG value r=%d\n", __func__, + rc); + goto exit; + } + + if (actual_write_len != MFG_WRITE_LENGTH) { + dev_err(cd->dev, "%s: Fail to write size=%d, actual_write=%d\n", + __func__, MFG_WRITE_LENGTH, actual_write_len); + rc = -EINVAL; + } + +exit: + rc = cyttsp5_hid_output_resume_scanning_(cd); + if (rc) + dev_err(cd->dev, "%s: fail to resume scann\n", __func__); + + return rc; +} + +static int _cyttsp5_mfg_value_(struct cyttsp5_core_data *cd, u8 *mfg_buffer, u8 mfg_num) +{ + int rc; + u8* read_buf; + u16 crc; + + read_buf = kzalloc(mfg_num + 4, GFP_KERNEL); + + rc = cyttsp5_hid_output_read_conf_block_(cd, 0, 4 + mfg_num, CY_MDATA_EBID, + read_buf, &crc); + if (rc) + goto exit; + + memcpy(mfg_buffer, &read_buf[4], mfg_num); + +exit: + kfree(read_buf); + return 0; +} + +static __maybe_unused int cyttsp5_get_mfg_value_(struct cyttsp5_core_data *cd, u8 *mfg_buffer, u8 mfg_num) +{ + int rc; + + rc = cyttsp5_hid_output_suspend_scanning_(cd); + if (rc) + goto error; + + rc = _cyttsp5_mfg_value_(cd, mfg_buffer, mfg_num); + if (rc) + goto exit; + +exit: + cyttsp5_hid_output_resume_scanning_(cd); +error: + dev_info(cd->dev, "%s: MFG_0:%02X\n", __func__, mfg_buffer[0]); + return rc; +} + +static int cyttsp5_hid_output_get_data_structure_( + struct cyttsp5_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; + u16 total_read_len = 0; + u16 read_len; + u16 off_buf = 0; + u8 write_buf[5]; + u8 read_data_id; + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_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 = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + if (cd->response_buf[5] != CY_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; +} + +static int cyttsp5_hid_output_get_data_structure( + struct cyttsp5_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, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_get_data_structure_(cd, read_offset, + read_length, data_id, status, data_format, + actual_read_len, data); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_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 cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_get_data_structure(cd, + read_offset, read_length, data_id, status, + data_format, actual_read_len, data); + + return cyttsp5_hid_output_get_data_structure_(cd, + read_offset, read_length, data_id, status, + data_format, actual_read_len, data); +} + +static int cyttsp5_hid_output_run_selftest_( + struct cyttsp5_core_data *cd, u8 test_id, + u8 write_idacs_to_flash, u8 *status, u8 *summary_result, + u8 *results_available) +{ + int rc; + u8 write_buf[2]; + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_RUN_SELF_TEST), + .write_length = 2, + .write_buf = write_buf, + .timeout_ms = CY_HID_OUTPUT_RUN_SELF_TEST_TIMEOUT, + }; + + write_buf[0] = test_id; + write_buf[1] = write_idacs_to_flash; + + rc = cyttsp5_hid_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]; + if (results_available) + *results_available = cd->response_buf[7]; + + return rc; +} + +static int cyttsp5_hid_output_run_selftest( + struct cyttsp5_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, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_run_selftest_(cd, test_id, + write_idacs_to_flash, status, summary_result, + results_available); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_run_selftest(struct device *dev, + int protect, u8 test_id, u8 write_idacs_to_flash, u8 *status, + u8 *summary_result, u8 *results_available) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_run_selftest(cd, test_id, + write_idacs_to_flash, status, summary_result, + results_available); + + return cyttsp5_hid_output_run_selftest_(cd, test_id, + write_idacs_to_flash, status, summary_result, + results_available); +} + +static int cyttsp5_hid_output_get_selftest_result_( + struct cyttsp5_core_data *cd, u16 read_offset, u16 read_length, + u8 test_id, u8 *status, u16 *actual_read_len, u8 *data) +{ + int rc; + u16 total_read_len = 0; + u16 read_len; + u16 off_buf = 0; + u8 write_buf[5]; + u8 read_test_id; + bool repeat; + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_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 != CY_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 = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + if (cd->response_buf[5] != CY_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; +} + +static int cyttsp5_hid_output_get_selftest_result( + struct cyttsp5_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, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_get_selftest_result_(cd, read_offset, + read_length, test_id, status, actual_read_len, data); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_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 cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_get_selftest_result(cd, read_offset, + read_length, test_id, status, actual_read_len, + data); + + return cyttsp5_hid_output_get_selftest_result_(cd, read_offset, + read_length, test_id, status, actual_read_len, + data); +} + +static int cyttsp5_hid_output_calibrate_idacs_(struct cyttsp5_core_data *cd, + u8 mode, u8 *status) +{ + int rc; + int write_length = 1; + u8 write_buf[1]; + u8 cmd_offset = 0; + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_CALIBRATE_IDACS), + .write_length = write_length, + .write_buf = write_buf, + .timeout_ms = CY_HID_OUTPUT_CALIBRATE_IDAC_TIMEOUT, + }; + + write_buf[cmd_offset++] = mode; + rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + *status = cd->response_buf[5]; + if (*status) + return -EINVAL; + + return 0; +} + +static int cyttsp5_hid_output_calibrate_idacs(struct cyttsp5_core_data *cd, + u8 mode, u8 *status) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_calibrate_idacs_(cd, mode, status); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_calibrate_idacs(struct device *dev, + int protect, u8 mode, u8 *status) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_calibrate_idacs(cd, mode, status); + + return cyttsp5_hid_output_calibrate_idacs_(cd, mode, status); +} + +static int cyttsp5_hid_output_initialize_baselines_( + struct cyttsp5_core_data *cd, u8 test_id, u8 *status) +{ + int rc; + int write_length = 1; + u8 write_buf[1]; + u8 cmd_offset = 0; + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_INITIALIZE_BASELINES), + .write_length = write_length, + .write_buf = write_buf, + }; + + write_buf[cmd_offset++] = test_id; + + rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); + if (rc) + return rc; + + *status = cd->response_buf[5]; + if (*status) + return -EINVAL; + + return rc; +} + +static int cyttsp5_hid_output_initialize_baselines(struct cyttsp5_core_data *cd, + u8 test_id, u8 *status) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_initialize_baselines_(cd, test_id, status); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_initialize_baselines(struct device *dev, + int protect, u8 test_id, u8 *status) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_initialize_baselines(cd, test_id, + status); + + return cyttsp5_hid_output_initialize_baselines_(cd, test_id, status); +} + +static int cyttsp5_hid_output_exec_panel_scan_(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_EXEC_PANEL_SCAN), + }; + + return cyttsp5_hid_send_output_and_wait_(cd, &hid_output); +} + +static int cyttsp5_hid_output_exec_panel_scan(struct cyttsp5_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_exec_panel_scan_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_exec_panel_scan(struct device *dev, + int protect) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_exec_panel_scan(cd); + + return cyttsp5_hid_output_exec_panel_scan_(cd); +} + +/* @response: set none NULL only if all response required including header */ +static int cyttsp5_hid_output_retrieve_panel_scan_( + struct cyttsp5_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; + int write_length = 5; + u8 write_buf[5]; + u8 cmd_offset = 0; + u8 data_elem_size; + int size; + int data_size; + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_APP_COMMAND(HID_OUTPUT_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 = cyttsp5_hid_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; +} + +static int cyttsp5_hid_output_retrieve_panel_scan( + struct cyttsp5_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, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_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) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_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 cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_retrieve_panel_scan(cd, + read_offset, read_count, data_id, response, + config, actual_read_len, read_buf); + + return cyttsp5_hid_output_retrieve_panel_scan_(cd, + read_offset, read_count, data_id, response, + config, actual_read_len, read_buf); +} + +static int cyttsp5_hid_output_user_cmd_(struct cyttsp5_core_data *cd, + u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf, + u16 *actual_read_len) +{ + int rc; + u16 size; +#ifdef TTHE_TUNER_SUPPORT + int command_code = 0; + int len; +#endif + struct cyttsp5_hid_output hid_output = { + .length = write_len, + .write_buf = write_buf, + }; + + rc = cyttsp5_hid_send_output_user_and_wait_(cd, &hid_output); + if (rc) + return rc; + + size = get_unaligned_le16(&cd->response_buf[0]); + if (size == 0) + size = 2; + + if (size > read_len) { + *actual_read_len = 0; + return -EINVAL; + } + + memcpy(read_buf, cd->response_buf, size); + *actual_read_len = size; + +#ifdef TTHE_TUNER_SUPPORT + /* print up to cmd code */ + len = HID_OUTPUT_CMD_OFFSET + 1; + if (write_len < len) + len = write_len; + else + command_code = write_buf[HID_OUTPUT_CMD_OFFSET] + & HID_OUTPUT_CMD_MASK; + + /* Do not print for EXEC_PANEL_SCAN & RETRIEVE_PANEL_SCAN commands */ + if (command_code != HID_OUTPUT_EXEC_PANEL_SCAN + && command_code != HID_OUTPUT_RETRIEVE_PANEL_SCAN) + tthe_print(cd, write_buf, len, "CMD="); +#endif + + return 0; +} + +static int cyttsp5_get_config_ver_(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_sysinfo *si = &cd->sysinfo; + int rc; + u16 config_ver = 0; + + rc = cyttsp5_hid_output_suspend_scanning_(cd); + if (rc) + goto error; + + rc = cyttsp5_hid_output_read_conf_ver_(cd, &config_ver); + if (rc) + goto exit; + + si->cydata.fw_ver_conf = config_ver; + +exit: + cyttsp5_hid_output_resume_scanning_(cd); +error: + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: CONFIG_VER:%04X\n", + __func__, config_ver); + return rc; +} + +static int cyttsp5_hid_output_user_cmd(struct cyttsp5_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, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_user_cmd_(cd, read_len, read_buf, + write_len, write_buf, actual_read_len); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_user_cmd(struct device *dev, + int protect, u16 read_len, u8 *read_buf, u16 write_len, + u8 *write_buf, u16 *actual_read_len) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_user_cmd(cd, read_len, read_buf, + write_len, write_buf, actual_read_len); + + return cyttsp5_hid_output_user_cmd_(cd, read_len, read_buf, + write_len, write_buf, actual_read_len); +} + +static int cyttsp5_hid_output_bl_get_information_(struct cyttsp5_core_data *cd, + u8 *return_data) +{ + int rc; + int data_len; + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_BL_COMMAND(HID_OUTPUT_BL_GET_INFO), + }; + + rc = cyttsp5_hid_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; +} + +static int cyttsp5_hid_output_bl_get_information(struct cyttsp5_core_data *cd, + u8 *return_data) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_bl_get_information_(cd, return_data); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_bl_get_information(struct device *dev, + int protect, u8 *return_data) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_bl_get_information(cd, return_data); + + return cyttsp5_hid_output_bl_get_information_(cd, return_data); +} + +static int cyttsp5_hid_output_bl_initiate_bl_(struct cyttsp5_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; + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_BL_COMMAND(HID_OUTPUT_BL_INITIATE_BL), + .write_length = write_length, + .timeout_ms = CY_HID_OUTPUT_BL_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 = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); + + kfree(write_buf); + return rc; +} + +static int cyttsp5_hid_output_bl_initiate_bl(struct cyttsp5_core_data *cd, + u16 key_size, u8 *key_buf, u16 row_size, u8 *metadata_row_buf) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_bl_initiate_bl_(cd, key_size, key_buf, + row_size, metadata_row_buf); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_bl_initiate_bl(struct device *dev, + int protect, u16 key_size, u8 *key_buf, u16 row_size, + u8 *metadata_row_buf) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_bl_initiate_bl(cd, key_size, key_buf, + row_size, metadata_row_buf); + + return cyttsp5_hid_output_bl_initiate_bl_(cd, key_size, key_buf, + row_size, metadata_row_buf); +} + +static int cyttsp5_hid_output_bl_program_and_verify_( + struct cyttsp5_core_data *cd, u16 data_len, u8 *data_buf) +{ + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_BL_COMMAND(HID_OUTPUT_BL_PROGRAM_AND_VERIFY), + .write_length = data_len, + .write_buf = data_buf, + .timeout_ms = CY_HID_OUTPUT_BL_PROGRAM_AND_VERIFY_TIMEOUT, + }; + + return cyttsp5_hid_send_output_and_wait_(cd, &hid_output); +} + +static int cyttsp5_hid_output_bl_program_and_verify( + struct cyttsp5_core_data *cd, u16 data_len, u8 *data_buf) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_bl_program_and_verify_(cd, data_len, data_buf); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_bl_program_and_verify( + struct device *dev, int protect, u16 data_len, u8 *data_buf) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_bl_program_and_verify(cd, data_len, + data_buf); + + return cyttsp5_hid_output_bl_program_and_verify_(cd, data_len, + data_buf); +} + +static int cyttsp5_hid_output_bl_verify_app_integrity_( + struct cyttsp5_core_data *cd, u8 *result) +{ + int rc; + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_BL_COMMAND(HID_OUTPUT_BL_VERIFY_APP_INTEGRITY), + }; + + rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); + if (rc) { + *result = 0; + return rc; + } + + *result = cd->response_buf[8]; + return 0; +} + +static int cyttsp5_hid_output_bl_verify_app_integrity( + struct cyttsp5_core_data *cd, u8 *result) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_bl_verify_app_integrity_(cd, result); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_bl_verify_app_integrity( + struct device *dev, int protect, u8 *result) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_bl_verify_app_integrity(cd, result); + + return cyttsp5_hid_output_bl_verify_app_integrity_(cd, result); +} + +static int cyttsp5_hid_output_bl_launch_app_(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_BL_COMMAND(HID_OUTPUT_BL_LAUNCH_APP), + .reset_expected = 1, + }; + + return cyttsp5_hid_send_output_and_wait_(cd, &hid_output); +} + +static int cyttsp5_hid_output_bl_launch_app(struct cyttsp5_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_bl_launch_app_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_launch_app(struct device *dev, + int protect) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_bl_launch_app(cd); + + return cyttsp5_hid_output_bl_launch_app_(cd); +} + +static int cyttsp5_hid_output_bl_get_panel_id_( + struct cyttsp5_core_data *cd, u8 *panel_id) +{ + int rc; + struct cyttsp5_hid_output hid_output = { + HID_OUTPUT_BL_COMMAND(HID_OUTPUT_BL_GET_PANEL_ID), + }; + + rc = cyttsp5_hid_send_output_and_wait_(cd, &hid_output); + if (rc == -EPROTO && cd->response_buf[5] == ERROR_COMMAND) { + parade_debug(cd->dev, DEBUG_LEVEL_1, + "%s: Get Panel ID command not supported\n", + __func__); + *panel_id = PANEL_ID_NOT_ENABLED; + return 0; + } else if (rc < 0) { + dev_err(cd->dev, "%s: Error on Get Panel ID command\n", + __func__); + return rc; + } + + *panel_id = cd->response_buf[8]; + return 0; +} + +static int cyttsp5_hid_output_bl_get_panel_id( + struct cyttsp5_core_data *cd, u8 *panel_id) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_hid_output_bl_get_panel_id_(cd, panel_id); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_hid_output_bl_get_panel_id( + struct device *dev, int protect, u8 *panel_id) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_hid_output_bl_get_panel_id(cd, panel_id); + + return cyttsp5_hid_output_bl_get_panel_id_(cd, panel_id); +} + +static int cyttsp5_get_hid_descriptor_(struct cyttsp5_core_data *cd, + struct cyttsp5_hid_desc *desc) +{ + struct device *dev = cd->dev; + int rc; + int t; + u8 cmd[2]; + + /* 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)); + + rc = cyttsp5_adap_write_read_specific(cd, 2, cmd, NULL); + if (rc) { + dev_err(dev, "%s: failed to get HID descriptor length and version, rc=%d\n", + __func__, rc); + goto error; + } + + t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0), + msecs_to_jiffies(CY_HID_GET_HID_DESCRIPTOR_TIMEOUT)); + if (IS_TMO(t)) { + dev_err(cd->dev, "%s: HID get descriptor timed out\n", + __func__); + rc = -ETIME; + goto error; + } + + memcpy((u8 *)desc, cd->response_buf, sizeof(struct cyttsp5_hid_desc)); + cyttsp5_pr_buf(cd->dev, cd->response_buf, sizeof(struct cyttsp5_hid_desc), "HID Desc"); + + + /* Check HID descriptor length and version */ + parade_debug(dev, DEBUG_LEVEL_2, "%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) != CY_HID_VERSION) { + dev_err(dev, "%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; +} + +static int cyttsp5_get_hid_descriptor(struct cyttsp5_core_data *cd, + struct cyttsp5_hid_desc *desc) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_get_hid_descriptor_(cd, desc); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + + return rc; +} + +static int _cyttsp5_request_get_hid_desc(struct device *dev, int protect) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (protect) + return cyttsp5_get_hid_descriptor(cd, &cd->hid_desc); + + return cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc); +} + +static int cyttsp5_hw_soft_reset(struct cyttsp5_core_data *cd) +{ + int rc; + + if (cd->hid_desc.hid_desc_len == 0) { + rc = cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc); + if (rc < 0) + return rc; + } + + rc = cyttsp5_hid_cmd_reset_(cd); + if (rc < 0) { + dev_err(cd->dev, "%s: FAILED to execute SOFT reset\n", + __func__); + return rc; + } + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: execute SOFT reset\n", + __func__); + return 0; +} + +static int cyttsp5_hw_hard_reset(struct cyttsp5_core_data *cd) +{ + if (cd->cpdata->xres) { + cd->cpdata->xres(cd->cpdata, cd->dev); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: execute HARD reset\n", + __func__); + return 0; + } + dev_err(cd->dev, "%s: FAILED to execute HARD reset\n", __func__); + return -ENODEV; +} + +static int cyttsp5_hw_reset(struct cyttsp5_core_data *cd) +{ + int rc; + + mutex_lock(&cd->system_lock); + rc = cyttsp5_hw_hard_reset(cd); + mutex_unlock(&cd->system_lock); + if (rc == -ENODEV) + rc = cyttsp5_hw_soft_reset(cd); + return rc; +} + +static inline int get_hid_item_data(u8 *data, int item_size) +{ + if (item_size == 1) + return (int)*data; + else if (item_size == 2) + return (int)get_unaligned_le16(data); + else if (item_size == 4) + return (int)get_unaligned_le32(data); + return 0; +} + +static int parse_report_descriptor(struct cyttsp5_core_data *cd, + u8 *report_desc, size_t len) +{ + struct cyttsp5_hid_report *report; + struct cyttsp5_hid_field *field; + u8 *buf = report_desc; + u8 *end = buf + len; + int rc = 0; + int offset = 0; + int i; + u8 report_type; + u32 up_usage; + /* Global items */ + u8 report_id = 0; + u16 usage_page = 0; + int report_count = 0; + int report_size = 0; + int logical_min = 0; + int logical_max = 0; + /* Local items */ + u16 usage = 0; + /* Main items - Collection stack */ + u32 collection_usages[CY_HID_MAX_NESTED_COLLECTIONS] = {0}; + u8 collection_types[CY_HID_MAX_NESTED_COLLECTIONS] = {0}; + /* First collection for header, second for report */ + int logical_collection_count = 0; + int collection_nest = 0; + + parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Report descriptor length: %u\n", + __func__, (u32)len); + + mutex_lock(&cd->hid_report_lock); + cyttsp5_free_hid_reports_(cd); + + while (buf < end) { + int item_type; + int item_size; + int item_tag; + u8 *data; + + /* Get Item */ + item_size = HID_GET_ITEM_SIZE(buf[0]); + if (item_size == 3) + item_size = 4; + item_type = HID_GET_ITEM_TYPE(buf[0]); + item_tag = HID_GET_ITEM_TAG(buf[0]); + + data = ++buf; + buf += item_size; + + /* Process current item */ + switch (item_type) { + case HID_ITEM_TYPE_GLOBAL: + switch (item_tag) { + case HID_GLOBAL_ITEM_TAG_REPORT_ID: + if (item_size != 1) { + rc = -EINVAL; + goto exit; + } + report_id = get_hid_item_data(data, item_size); + offset = 0; + logical_collection_count = 0; + break; + case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: + if (item_size == 0 || item_size == 4) { + rc = -EINVAL; + goto exit; + } + usage_page = (u16)get_hid_item_data(data, + item_size); + break; + case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: + if (item_size == 0) { + rc = -EINVAL; + goto exit; + } + logical_min = get_hid_item_data(data, + item_size); + break; + case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: + if (item_size == 0) { + rc = -EINVAL; + goto exit; + } + logical_max = get_hid_item_data(data, + item_size); + break; + case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: + if (item_size == 0) { + rc = -EINVAL; + goto exit; + } + report_count = get_hid_item_data(data, + item_size); + break; + case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: + if (item_size == 0) { + rc = -EINVAL; + goto exit; + } + report_size = get_hid_item_data(data, + item_size); + break; + default: + dev_info(cd->dev, + "%s: Unrecognized Global tag %d\n", + __func__, item_tag); + } + break; + case HID_ITEM_TYPE_LOCAL: + switch (item_tag) { + case HID_LOCAL_ITEM_TAG_USAGE: + if (item_size == 0 || item_size == 4) { + rc = -EINVAL; + goto exit; + } + usage = (u16)get_hid_item_data(data, + item_size); + break; + case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: + if (item_size == 0) { + rc = -EINVAL; + goto exit; + } + /* usage_min = */ + get_hid_item_data(data, item_size); + break; + case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: + if (item_size == 0) { + rc = -EINVAL; + goto exit; + } + /* usage_max = */ + get_hid_item_data(data, item_size); + break; + default: + dev_info(cd->dev, + "%s: Unrecognized Local tag %d\n", + __func__, item_tag); + } + break; + case HID_ITEM_TYPE_MAIN: + switch (item_tag) { + case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: + if (item_size != 1) { + rc = -EINVAL; + goto exit; + } + if (CY_HID_MAX_NESTED_COLLECTIONS == + collection_nest) { + rc = -EINVAL; + goto exit; + } + + up_usage = usage_page << 16 | usage; + + /* Update collection stack */ + collection_usages[collection_nest] = up_usage; + collection_types[collection_nest] = + get_hid_item_data(data, item_size); + + if (collection_types[collection_nest] == + HID_COLLECTION_LOGICAL) + logical_collection_count++; + + collection_nest++; + break; + case HID_MAIN_ITEM_TAG_END_COLLECTION: + if (item_size != 0) { + rc = -EINVAL; + goto exit; + } + if (--collection_nest < 0) { + rc = -EINVAL; + goto exit; + } + break; + case HID_MAIN_ITEM_TAG_INPUT: + report_type = HID_INPUT_REPORT; + goto continue_main_item; + case HID_MAIN_ITEM_TAG_OUTPUT: + report_type = HID_OUTPUT_REPORT; + goto continue_main_item; + case HID_MAIN_ITEM_TAG_FEATURE: + report_type = HID_FEATURE_REPORT; +continue_main_item: + if (item_size != 1) { + rc = -EINVAL; + goto exit; + } + + up_usage = usage_page << 16 | usage; + + /* Get or create report */ + report = cyttsp5_get_hid_report_(cd, + report_type, report_id, true); + if (!report) { + rc = -ENOMEM; + goto exit; + } + if (!report->usage_page && collection_nest > 0) + report->usage_page = + collection_usages + [collection_nest - 1]; + + /* Create field */ + field = cyttsp5_create_hid_field_(report); + if (!field) { + rc = -ENOMEM; + goto exit; + } + + field->report_count = report_count; + field->report_size = report_size; + field->size = report_count * report_size; + field->offset = offset; + field->data_type = + get_hid_item_data(data, item_size); + field->logical_min = logical_min; + field->logical_max = logical_max; + field->usage_page = up_usage; + + for (i = 0; i < collection_nest; i++) { + field->collection_usage_pages + [collection_types[i]] = + collection_usages[i]; + } + + /* Update report's header or record size */ + if (logical_collection_count == 1) { + report->header_size += field->size; + } else if (logical_collection_count == 2) { + field->record_field = true; + field->offset -= report->header_size; + /* Set record field index */ + if (report->record_field_index == 0) + report->record_field_index = + report->num_fields - 1; + report->record_size += field->size; + } + + report->size += field->size; + + offset += field->size; + break; + default: + dev_info(cd->dev, "%s: Unrecognized Main tag %d\n", + __func__, item_tag); + } + + /* Reset all local items */ + usage = 0; + break; + } + } + + if (buf != end) { + dev_err(cd->dev, "%s: Report descriptor length invalid\n", + __func__); + rc = -EINVAL; + goto exit; + } + + if (collection_nest) { + dev_err(cd->dev, "%s: Unbalanced collection items (%d)\n", + __func__, collection_nest); + rc = -EINVAL; + goto exit; + } + +exit: + if (rc) + cyttsp5_free_hid_reports_(cd); + mutex_unlock(&cd->hid_report_lock); + return rc; +} + +static struct cyttsp5_hid_field *find_report_desc_field( + struct cyttsp5_core_data *cd, u32 usage_page, + u32 collection_usage_page) +{ + struct cyttsp5_hid_report *report = NULL; + struct cyttsp5_hid_field *field = NULL; + int i; + int j; + u32 field_cup; + u32 field_up; + + for (i = 0; i < cd->num_hid_reports; i++) { + report = cd->hid_reports[i]; + for (j = 0; j < report->num_fields; j++) { + field = report->fields[j]; + field_cup = field->collection_usage_pages + [HID_COLLECTION_LOGICAL]; + field_up = field->usage_page; + if (field_cup == collection_usage_page + && field_up == usage_page) { + return field; + } + } + } + + return NULL; +} + +static int fill_tch_abs(struct cyttsp5_tch_abs_params *tch_abs, + struct cyttsp5_hid_field *field) +{ + tch_abs->ofs = field->offset / 8; + tch_abs->size = field->report_size / 8; + if (field->report_size % 8) + tch_abs->size += 1; + tch_abs->min = 0; + tch_abs->max = 1 << field->report_size; + tch_abs->bofs = field->offset - (tch_abs->ofs << 3); + + return 0; +} + +static struct cyttsp5_hid_report *find_report_desc(struct cyttsp5_core_data *cd, + u32 usage_page) +{ + struct cyttsp5_hid_report *report = NULL; + int i; + + for (i = 0; i < cd->num_hid_reports; i++) { + if (cd->hid_reports[i]->usage_page == usage_page) { + report = cd->hid_reports[i]; + break; + } + } + + return report; +} + +static int setup_report_descriptor(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_sysinfo *si = &cd->sysinfo; + struct cyttsp5_hid_report *report; + struct cyttsp5_hid_field *field; + int i; + u32 tch_collection_usage_page = HID_CY_TCH_COL_USAGE_PG; + u32 btn_collection_usage_page = HID_CY_BTN_COL_USAGE_PG; + + for (i = CY_TCH_X; i < CY_TCH_NUM_ABS; i++) { + field = find_report_desc_field(cd, + cyttsp5_tch_abs_field_map[i], + tch_collection_usage_page); + if (field) { + parade_debug(cd->dev, DEBUG_LEVEL_2, + " Field %p: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n", + field, field->report_count, field->report_size, + field->offset, field->data_type, + field->logical_min, field->logical_max, + field->usage_page); + fill_tch_abs(&si->tch_abs[i], field); + si->tch_abs[i].report = 1; + parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: ofs:%u size:%u min:%u max:%u bofs:%u report:%d", + cyttsp5_tch_abs_string[i], + (u32)si->tch_abs[i].ofs, + (u32)si->tch_abs[i].size, + (u32)si->tch_abs[i].min, + (u32)si->tch_abs[i].max, + (u32)si->tch_abs[i].bofs, + si->tch_abs[i].report); + + } else { + si->tch_abs[i].report = 0; + } + } + for (i = CY_TCH_TIME; i < CY_TCH_NUM_HDR; i++) { + field = find_report_desc_field(cd, + cyttsp5_tch_hdr_field_map[i], + tch_collection_usage_page); + if (field) { + parade_debug(cd->dev, DEBUG_LEVEL_2, + " Field %p: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n", + field, field->report_count, field->report_size, + field->offset, field->data_type, + field->logical_min, field->logical_max, + field->usage_page); + fill_tch_abs(&si->tch_hdr[i], field); + si->tch_hdr[i].report = 1; + parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: ofs:%u size:%u min:%u max:%u bofs:%u report:%d", + cyttsp5_tch_hdr_string[i], + (u32)si->tch_hdr[i].ofs, + (u32)si->tch_hdr[i].size, + (u32)si->tch_hdr[i].min, + (u32)si->tch_hdr[i].max, + (u32)si->tch_hdr[i].bofs, + si->tch_hdr[i].report); + + } else { + si->tch_hdr[i].report = 0; + } + } + + report = find_report_desc(cd, tch_collection_usage_page); + if (report) { + si->desc.tch_report_id = report->id; + si->desc.tch_record_size = report->record_size / 8; + si->desc.tch_header_size = (report->header_size / 8) + 3; + } else { + si->desc.tch_report_id = HID_TOUCH_REPORT_ID; + si->desc.tch_record_size = TOUCH_REPORT_SIZE; + si->desc.tch_header_size = TOUCH_INPUT_HEADER_SIZE; + } + + report = find_report_desc(cd, btn_collection_usage_page); + if (report) + si->desc.btn_report_id = report->id; + else + si->desc.btn_report_id = HID_BTN_REPORT_ID; + + for (i = 0; i < cd->num_hid_reports; i++) { + struct cyttsp5_hid_report *report = cd->hid_reports[i]; + + switch (report->id) { + case HID_WAKEUP_REPORT_ID: + cd->features.easywake = 1; + break; + case HID_NOISE_METRIC_REPORT_ID: + cd->features.noise_metric = 1; + break; + case HID_TRACKING_HEATMAP_REPOR_ID: + cd->features.tracking_heatmap = 1; + break; + case HID_SENSOR_DATA_REPORT_ID: + cd->features.sensor_data = 1; + break; + default: + break; + } + } + + parade_debug(cd->dev, DEBUG_LEVEL_1, "Features: easywake:%d noise_metric:%d tracking_heatmap:%d sensor_data:%d\n", + cd->features.easywake, cd->features.noise_metric, + cd->features.tracking_heatmap, + cd->features.sensor_data); + + return 0; +} + +int cyttsp5_get_report_descriptor_(struct cyttsp5_core_data *cd) +{ + struct device *dev = cd->dev; + u8 cmd[2]; + int rc; + int t; + + /* Read report descriptor length and version */ + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = 1; + mutex_unlock(&cd->system_lock); + + /* Set report descriptor register */ + memcpy(cmd, &cd->hid_desc.report_desc_register, + sizeof(cd->hid_desc.report_desc_register)); + + rc = cyttsp5_adap_write_read_specific(cd, 2, cmd, NULL); + if (rc) { + dev_err(dev, "%s: failed to get HID descriptor length and version, rc=%d\n", + __func__, rc); + goto error; + } + + t = wait_event_timeout(cd->wait_q, (cd->hid_cmd_state == 0), + msecs_to_jiffies(CY_HID_GET_REPORT_DESCRIPTOR_TIMEOUT)); + if (IS_TMO(t)) { + dev_err(cd->dev, "%s: HID get descriptor timed out\n", + __func__); + rc = -ETIME; + goto error; + } + + cyttsp5_pr_buf(cd->dev, cd->response_buf, + cd->hid_core.hid_report_desc_len, "Report Desc"); + + rc = parse_report_descriptor(cd, cd->response_buf + 3, + get_unaligned_le16(&cd->response_buf[0]) - 3); + if (rc) { + dev_err(cd->dev, "%s: Error parsing report descriptor r=%d\n", + __func__, rc); + } + + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: %d reports found in descriptor\n", + __func__, cd->num_hid_reports); + + for (t = 0; t < cd->num_hid_reports; t++) { + struct cyttsp5_hid_report *report = cd->hid_reports[t]; + int j; + + parade_debug(cd->dev, DEBUG_LEVEL_2, + "Report %d: type:%d id:%02X size:%d fields:%d rec_fld_index:%d hdr_sz:%d rec_sz:%d usage_page:%08X\n", + t, report->type, report->id, + report->size, report->num_fields, + report->record_field_index, report->header_size, + report->record_size, report->usage_page); + + for (j = 0; j < report->num_fields; j++) { + struct cyttsp5_hid_field *field = report->fields[j]; + + parade_debug(cd->dev, DEBUG_LEVEL_2, + " Field %d: rep_cnt:%d rep_sz:%d off:%d data:%02X min:%d max:%d usage_page:%08X\n", + j, field->report_count, field->report_size, + field->offset, field->data_type, + field->logical_min, field->logical_max, + field->usage_page); + + parade_debug(cd->dev, DEBUG_LEVEL_2, " Collections Phys:%08X App:%08X Log:%08X\n", + field->collection_usage_pages + [HID_COLLECTION_PHYSICAL], + field->collection_usage_pages + [HID_COLLECTION_APPLICATION], + field->collection_usage_pages + [HID_COLLECTION_LOGICAL]); + } + } + + rc = setup_report_descriptor(cd); + + /* Free it for now */ + cyttsp5_free_hid_reports_(cd); + + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: %d reports found in descriptor\n", + __func__, cd->num_hid_reports); + + goto exit; + +error: + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = 0; + mutex_unlock(&cd->system_lock); +exit: + return rc; +} + +/* + * touch default parameters (from report descriptor) to resolve protocol for + * touch report + */ +const struct cyttsp5_tch_abs_params tch_hdr_default[CY_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 cyttsp5_tch_abs_params tch_abs_default[CY_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 cyttsp5_core_data *cd) +{ + struct cyttsp5_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 = HID_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 = HID_BTN_REPORT_ID; + + cd->features.easywake = 1; + cd->features.noise_metric = 1; + cd->features.tracking_heatmap = 1; + cd->features.sensor_data = 1; +} + +static int cyttsp5_get_mode(struct cyttsp5_core_data *cd, + struct cyttsp5_hid_desc *desc) +{ + if (desc->packet_id == CY_HID_APP_REPORT_ID) + return CY_MODE_OPERATIONAL; + else if (desc->packet_id == CY_HID_BL_REPORT_ID) + return CY_MODE_BOOTLOADER; + + return CY_MODE_UNKNOWN; +} + +static int _cyttsp5_request_get_mode(struct device *dev, int protect, u8 *mode) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + int rc; + + if (protect) + rc = cyttsp5_get_hid_descriptor(cd, &cd->hid_desc); + else + rc = cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc); + + if (rc) + *mode = CY_MODE_UNKNOWN; + else + *mode = cyttsp5_get_mode(cd, &cd->hid_desc); + + return rc; +} + +static void cyttsp5_queue_startup_(struct cyttsp5_core_data *cd) +{ + if (cd->startup_state == STARTUP_NONE) { + cd->startup_state = STARTUP_QUEUED; + schedule_work(&cd->startup_work); + dev_info(cd->dev, "%s: cyttsp5_startup queued\n", __func__); + } else { + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: startup_state = %d\n", + __func__, cd->startup_state); + } +} + +static void cyttsp5_queue_startup(struct cyttsp5_core_data *cd) +{ + mutex_lock(&cd->system_lock); + cyttsp5_queue_startup_(cd); + mutex_unlock(&cd->system_lock); +} + +static void cyttsp5_queue_ez_recovery_(struct cyttsp5_core_data *cd) +{ + if (!work_pending(&cd->ez_recovery_work)) { + schedule_work(&cd->ez_recovery_work); + dev_info(cd->dev, "%s: ez_recovery_work queued\n", __func__); + } else { + dev_info(cd->dev, "%s: ez_recovery_work pending\n", __func__); + } +} + +static void cyttsp5_queue_ez_recovery(struct cyttsp5_core_data *cd) +{ + mutex_lock(&cd->system_lock); + cyttsp5_queue_ez_recovery_(cd); + mutex_unlock(&cd->system_lock); +} + +static void call_atten_cb(struct cyttsp5_core_data *cd, + enum cyttsp5_atten_type type, int mode) +{ + struct atten_node *atten, *atten_n; + + parade_debug(cd->dev, DEBUG_LEVEL_2, "%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); + parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: attention for '%s'", + __func__, dev_name(atten->dev)); + atten->func(atten->dev); + spin_lock(&cd->spinlock); + } + } + spin_unlock(&cd->spinlock); +} + +static void cyttsp5_start_wd_timer(struct cyttsp5_core_data *cd) +{ + if (!cd->watchdog_interval) + return; + + mod_timer(&cd->watchdog_timer, jiffies + + msecs_to_jiffies(cd->watchdog_interval)); +} + +static void cyttsp5_stop_wd_timer(struct cyttsp5_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); +} + +static int start_fw_upgrade(void *data) +{ + struct cyttsp5_core_data *cd = (struct cyttsp5_core_data *)data; + + call_atten_cb(cd, CY_ATTEN_LOADER, 0); + return 0; +} + +static void cyttsp5_watchdog_work(struct work_struct *work) +{ + struct cyttsp5_core_data *cd = + container_of(work, struct cyttsp5_core_data, + watchdog_work); + int rc; + /*fix CDT207254 + *if found the current sleep_state is SS_SLEEPING + *then no need to request_exclusive, directly return + */ + if (cd->sleep_state == SS_SLEEPING || cd->sleep_state == SS_SLEEP_ON) + return; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + goto queue_startup; + } + + rc = cyttsp5_hid_output_null_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + +queue_startup: + if (rc) { + dev_err(cd->dev, + "%s: failed to access device in watchdog timer r=%d\n", + __func__, rc); + + /* Already tried FW upgrade because of watchdog but failed */ + if (cd->startup_retry_count > CY_WATCHDOG_RETRY_COUNT) + return; + + if (cd->startup_retry_count++ < CY_WATCHDOG_RETRY_COUNT) + cyttsp5_queue_startup(cd); + else + kthread_run(start_fw_upgrade, cd, "cyttp5_loader"); + + return; + } + + cyttsp5_start_wd_timer(cd); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0) +static void cyttsp5_watchdog_timer(struct timer_list *t) +{ + struct cyttsp5_core_data *cd = from_timer(cd, t, watchdog_timer); + + if (!cd) + return; + + parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Watchdog timer triggered\n", + __func__); + + if (!work_pending(&cd->watchdog_work)) + schedule_work(&cd->watchdog_work); +} +#else +static void cyttsp5_watchdog_timer(unsigned long handle) +{ + struct cyttsp5_core_data *cd = (struct cyttsp5_core_data *)handle; + + if (!cd) + return; + + parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Watchdog timer triggered\n", + __func__); + + if (!work_pending(&cd->watchdog_work)) + schedule_work(&cd->watchdog_work); +} +#endif + +static int cyttsp5_put_device_into_easy_wakeup_(struct cyttsp5_core_data *cd) +{ + int rc; + u8 status = 0; + + dev_info(cd->dev, "%s: put device into easy wakeup state\n", __func__); + + mutex_lock(&cd->system_lock); + cd->wait_until_wake = 0; + mutex_unlock(&cd->system_lock); + + rc = cyttsp5_hid_output_enter_easywake_state_(cd, + cd->easy_wakeup_gesture, &status); + if (rc || status == 0) + return -EBUSY; + + mutex_lock(&cd->system_lock); + cd->pm_mode = CYTTSP5_PM_EASY_WAKEUP; + mutex_unlock(&cd->system_lock); + + return rc; +} + +static int cyttsp5_put_device_into_deep_sleep_(struct cyttsp5_core_data *cd) +{ + int rc; + + rc = cyttsp5_hid_cmd_set_power_(cd, HID_POWER_SLEEP); + if (rc) + rc = -EBUSY; + else { + mutex_lock(&cd->system_lock); + cd->pm_mode = CYTTSP5_PM_DEEPSLEEP; + mutex_unlock(&cd->system_lock); + } + + return rc; +} + +static int cyttsp5_put_device_into_sleep_(struct cyttsp5_core_data *cd) +{ + int rc; + + if (IS_DEEP_SLEEP_CONFIGURED(cd->easy_wakeup_gesture)) + rc = cyttsp5_put_device_into_deep_sleep_(cd); + else + rc = cyttsp5_put_device_into_easy_wakeup_(cd); + + return rc; +} + +static int cyttsp5_core_poweroff_device_(struct cyttsp5_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) + dev_err(cd->dev, "%s: HW Power down fails r=%d\n", + __func__, rc); + else { + mutex_lock(&cd->system_lock); + cd->pm_mode = CYTTSP5_PM_POWEROFF; + mutex_unlock(&cd->system_lock); + } + + return rc; +} + +static int cyttsp5_core_sleep_(struct cyttsp5_core_data *cd) +{ + int rc; + + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_OFF) { + cd->sleep_state = SS_SLEEPING; + } else { + mutex_unlock(&cd->system_lock); + return 1; + } + mutex_unlock(&cd->system_lock); + + /* Ensure watchdog and startup works stopped */ + cyttsp5_stop_wd_timer(cd); + cancel_work_sync(&cd->startup_work); + cancel_work_sync(&cd->ez_recovery_work); + cyttsp5_stop_wd_timer(cd); + + if (!IS_DEEP_SLEEP_CONFIGURED(cd->easy_wakeup_gesture) && + (cd->should_enable_gesture ==1)) { + rc = cyttsp5_put_device_into_sleep_(cd); + } else if (cd->cpdata->flags & CY_CORE_FLAG_POWEROFF_ON_SLEEP) + rc = cyttsp5_core_poweroff_device_(cd); + else + rc = cyttsp5_put_device_into_sleep_(cd); + + mutex_lock(&cd->system_lock); + cd->sleep_state = SS_SLEEP_ON; + mutex_unlock(&cd->system_lock); + + return rc; +} + +static int cyttsp5_core_sleep(struct cyttsp5_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_core_sleep_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + else + parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: pass release exclusive\n", + __func__); + + return rc; +} + +static int cyttsp5_wakeup_host(struct cyttsp5_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; + + cd->wake_initiated_by_device = 1; + event_id = cd->input_buf[3]; + + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: e=%d, rc=%d\n", + __func__, event_id, rc); + + if (rc) { + cyttsp5_core_sleep_(cd); + goto exit; + } + + /* attention WAKE */ + call_atten_cb(cd, CY_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] != 4) + rc = -EINVAL; + +#if 0 + cd->wake_initiated_by_device = 1; +#endif + + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: rc=%d\n", __func__, rc); + + if (rc) { + cyttsp5_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; + + parade_debug(cd->dev, DEBUG_LEVEL_1, "%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, CY_ATTEN_WAKE, 0); +exit: + return rc; +#endif +} + +static void cyttsp5_get_touch_axis(struct cyttsp5_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; +} + +static int move_tracking_hetmap_data(struct cyttsp5_core_data *cd, + struct cyttsp5_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; +} + +static int move_sensor_data(struct cyttsp5_core_data *cd, + struct cyttsp5_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; +} + +static int move_button_data(struct cyttsp5_core_data *cd, + struct cyttsp5_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); + cyttsp5_pr_buf(cd->dev, (u8 *)si->xy_mode, BTN_INPUT_HEADER_SIZE, + "xy_mode"); + + memcpy(si->xy_data, &cd->input_buf[BTN_INPUT_HEADER_SIZE], + BTN_REPORT_SIZE); + cyttsp5_pr_buf(cd->dev, (u8 *)si->xy_data, BTN_REPORT_SIZE, "xy_data"); + return 0; +} + +static int move_touch_data(struct cyttsp5_core_data *cd, + struct cyttsp5_sysinfo *si) +{ + int max_tch = si->sensing_conf_data.max_tch; + int num_cur_tch; + int length; + struct cyttsp5_tch_abs_params *tch = &si->tch_hdr[CY_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); + cyttsp5_pr_buf(cd->dev, (u8 *)si->xy_mode, si->desc.tch_header_size, + "xy_mode"); + + cyttsp5_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); + cyttsp5_pr_buf(cd->dev, (u8 *)si->xy_data, length, "xy_data"); + return 0; +} + +static int parse_touch_input(struct cyttsp5_core_data *cd, int size) +{ + struct cyttsp5_sysinfo *si = &cd->sysinfo; + int report_id = cd->input_buf[2]; + int rc = -EINVAL; + + parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Received touch report\n", + __func__); + if (!si->ready) { + dev_err(cd->dev, + "%s: Need system information to parse touches\n", + __func__); + return 0; + } + + if (!si->xy_mode || !si->xy_data) + return rc; + + if (report_id == si->desc.tch_report_id) + rc = move_touch_data(cd, si); + else if (report_id == si->desc.btn_report_id) + rc = move_button_data(cd, si); + else if (report_id == HID_SENSOR_DATA_REPORT_ID) + rc = move_sensor_data(cd, si); + else if (report_id == HID_TRACKING_HEATMAP_REPOR_ID) + rc = move_tracking_hetmap_data(cd, si); + + if (rc) + return rc; + + /* attention IRQ */ + call_atten_cb(cd, CY_ATTEN_IRQ, cd->mode); + + return 0; +} + +static int parse_command_input(struct cyttsp5_core_data *cd, int size) +{ + parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Received cmd interrupt\n", + __func__); + + memcpy(cd->response_buf, cd->input_buf, size); + + mutex_lock(&cd->system_lock); + cd->hid_cmd_state = 0; + mutex_unlock(&cd->system_lock); + wake_up(&cd->wait_q); + + return 0; +} + +static int cyttsp5_parse_input(struct cyttsp5_core_data *cd) +{ + int report_id; + int is_command = 0; + int size; + + size = get_unaligned_le16(&cd->input_buf[0]); + + /* check reset */ + if (size == 0) { + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Reset complete\n", + __func__); + memcpy(cd->response_buf, cd->input_buf, 2); + mutex_lock(&cd->system_lock); + if (!cd->hid_reset_cmd_state && !cd->hid_cmd_state) { + mutex_unlock(&cd->system_lock); + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Device Initiated Reset\n", + __func__); + return 0; + } + + cd->hid_reset_cmd_state = 0; + if (cd->hid_cmd_state == HID_OUTPUT_START_BOOTLOADER + 1 + || cd->hid_cmd_state == + HID_OUTPUT_BL_LAUNCH_APP + 1 + || cd->hid_cmd_state == + HID_OUTPUT_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 >= CY_PIP_1P7_EMPTY_BUF) + /* + * Before PIP 1.7, empty buffer is 0x0002; + * From PIP 1.7, empty buffer is 0xFFXX + */ + return 0; + + report_id = cd->input_buf[2]; + parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: report_id:%X\n", + __func__, report_id); + + /* Check wake-up report */ + if (report_id == HID_WAKEUP_REPORT_ID) { + cyttsp5_wakeup_host(cd); + return 0; + } + + /* update watchdog expire time */ + mod_timer_pending(&cd->watchdog_timer, jiffies + + msecs_to_jiffies(cd->watchdog_interval)); + + if (report_id != cd->sysinfo.desc.tch_report_id + && report_id != cd->sysinfo.desc.btn_report_id + && report_id != HID_SENSOR_DATA_REPORT_ID + && report_id != HID_TRACKING_HEATMAP_REPOR_ID) + is_command = 1; + + if (unlikely(is_command)) { + parse_command_input(cd, size); + return 0; + } + parse_touch_input(cd, size); + return 0; +} + +static int cyttsp5_read_input(struct cyttsp5_core_data *cd) +{ + struct device *dev = cd->dev; + int rc; + int t; + + /* added as workaround to CDT170960: easywake failure */ + /* Interrupt for easywake, wait for bus controller to wake */ + mutex_lock(&cd->system_lock); + if (!IS_DEEP_SLEEP_CONFIGURED(cd->easy_wakeup_gesture)) { + if (cd->sleep_state == SS_SLEEP_ON) { + mutex_unlock(&cd->system_lock); + + dev_info(dev, "%s: is_suspended = %d, wait_until_wake = %d", __func__, + dev->power.is_suspended, cd->wait_until_wake); + + PM_WAKEUP_EVENT(cd->gesture_wakelock, 500); + if (!dev->power.is_suspended) + goto read; + t = wait_event_timeout(cd->wait_q, + (cd->wait_until_wake == 1), + msecs_to_jiffies(4000)); + if (IS_TMO(t)) { + dev_info(dev, "%s: wait event timeout", __func__); + cyttsp5_queue_ez_recovery(cd); + } + goto read; + } + } + mutex_unlock(&cd->system_lock); + +read: + rc = cyttsp5_adap_read_default_nosize(cd, cd->input_buf, CY_MAX_INPUT); + if (rc) { + dev_err(dev, "%s: Error getting report, r=%d\n", + __func__, rc); + return rc; + } + parade_debug(dev, DEBUG_LEVEL_2, "%s: Read input successfully\n", + __func__); + return rc; +} + +static bool cyttsp5_check_irq_asserted(struct cyttsp5_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 == HID_OUTPUT_BL_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); + if (cd->cpdata->irq_stat(cd->cpdata, cd->dev) + != CY_IRQ_ASSERTED_VALUE) + return false; + } +#endif + return true; +} + + +static irqreturn_t cyttsp5_irq(int irq, void *handle) +{ + struct cyttsp5_core_data *cd = handle; + int rc; + + if (!cyttsp5_check_irq_asserted(cd)) + return IRQ_HANDLED; + + rc = cyttsp5_read_input(cd); + if (!rc) + cyttsp5_parse_input(cd); + + return IRQ_HANDLED; +} + +int _cyttsp5_subscribe_attention(struct device *dev, + enum cyttsp5_atten_type type, char *id, int (*func)(struct device *), + int mode) +{ + struct cyttsp5_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; + + parade_debug(cd->dev, DEBUG_LEVEL_1, "%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); + parade_debug(cd->dev, DEBUG_LEVEL_2, "%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; +} + +int _cyttsp5_unsubscribe_attention(struct device *dev, + enum cyttsp5_atten_type type, char *id, int (*func)(struct device *), + int mode) +{ + struct cyttsp5_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); + kfree(atten); + parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: %s=%p %s=%d\n", + __func__, + "unsub for atten->dev", atten->dev, + "atten->mode", atten->mode); + return 0; + } + } + spin_unlock(&cd->spinlock); + + return -ENODEV; +} + +static int _cyttsp5_request_exclusive(struct device *dev, + int timeout_ms) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + return request_exclusive(cd, (void *)dev, timeout_ms); +} + +static int _cyttsp5_release_exclusive(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + return release_exclusive(cd, (void *)dev); +} + +static int cyttsp5_reset(struct cyttsp5_core_data *cd) +{ + int rc; + + /* reset hardware */ + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: reset hw...\n", __func__); + rc = cyttsp5_hw_reset(cd); + if (rc < 0) + dev_err(cd->dev, "%s: %s dev='%s' r=%d\n", __func__, + "Fail hw reset", dev_name(cd->dev), rc); + return rc; +} + +static int cyttsp5_reset_and_wait(struct cyttsp5_core_data *cd) +{ + int rc; + int t; + + mutex_lock(&cd->system_lock); + cd->hid_reset_cmd_state = 1; + mutex_unlock(&cd->system_lock); + + rc = cyttsp5_reset(cd); + if (rc < 0) + goto error; + + t = wait_event_timeout(cd->wait_q, (cd->hid_reset_cmd_state == 0), + msecs_to_jiffies(CY_HID_RESET_TIMEOUT)); + if (IS_TMO(t)) { + dev_err(cd->dev, "%s: reset timed out\n", + __func__); + rc = -ETIME; + goto error; + } + + goto exit; + +error: + mutex_lock(&cd->system_lock); + cd->hid_reset_cmd_state = 0; + mutex_unlock(&cd->system_lock); +exit: + return rc; +} + +/* + * returns err if refused or timeout(core uses fixed timeout period) occurs; + * blocks until ISR occurs + */ +static int _cyttsp5_request_reset(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + int rc; + + mutex_lock(&cd->system_lock); + cd->hid_reset_cmd_state = 1; + mutex_unlock(&cd->system_lock); + + rc = cyttsp5_reset(cd); + if (rc < 0) { + dev_err(cd->dev, "%s: Error on h/w reset r=%d\n", + __func__, rc); + mutex_lock(&cd->system_lock); + cd->hid_reset_cmd_state = 0; + mutex_unlock(&cd->system_lock); + } + + return rc; +} + +/* + * returns err if refused ; if no error then restart has completed + * and system is in normal operating mode + */ +static int _cyttsp5_request_restart(struct device *dev, bool wait) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + cyttsp5_queue_startup(cd); + + if (wait) + wait_event(cd->wait_q, cd->startup_state == STARTUP_NONE); + + return 0; +} + +/* + * returns NULL if sysinfo has not been acquired from the device yet + */ +struct cyttsp5_sysinfo *_cyttsp5_request_sysinfo(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); +#ifdef PT_PANEL_ID_FROM_MFG + int rc = 0; + +/* Attempt to get sysinfo if not ready and panel_id is from XY pin */ + if (!cd->sysinfo.ready) { + rc = cyttsp5_get_mfg_value_(cd, &cd->panel_id, 1); + if (rc) { + dev_err(dev, + "%s: Error getting panel ID from MFG rc=%d\n", + __func__, rc); + } + + rc = cyttsp5_hid_output_get_sysinfo_(cd); + if (rc) { + dev_err(dev, + "%s: Error getting sysinfo rc=%d\n", + __func__, rc); + } + } +#endif + if (cd->sysinfo.ready) + return &cd->sysinfo; + + return NULL; +} + +static struct cyttsp5_loader_platform_data *_cyttsp5_request_loader_pdata( + struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + return cd->pdata->loader_pdata; +} + +static int _cyttsp5_request_start_wd(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + cyttsp5_start_wd_timer(cd); + return 0; +} + +static int _cyttsp5_request_stop_wd(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + cyttsp5_stop_wd_timer(cd); + return 0; +} + +static int cyttsp5_core_wake_device_from_deep_sleep_( + struct cyttsp5_core_data *cd) +{ + int rc; + + dev_info(cd->dev, "%s: wake device from deep sleep state\n", __func__); + + rc = cyttsp5_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; +} + +static int cyttsp5_core_wake_device_(struct cyttsp5_core_data *cd) +{ + if (!IS_DEEP_SLEEP_CONFIGURED(cd->easy_wakeup_gesture)) { + mutex_lock(&cd->system_lock); + cd->wait_until_wake = 1; + mutex_unlock(&cd->system_lock); + wake_up(&cd->wait_q); + msleep(20); + +#if 0 + if (cd->wake_initiated_by_device) { + cd->wake_initiated_by_device = 0; + return 0; + } +#endif + } + + return cyttsp5_core_wake_device_from_deep_sleep_(cd); +} + +static int cyttsp5_restore_parameters_(struct cyttsp5_core_data *cd) +{ + struct param_node *param; + int rc = 0; + + if (!(cd->cpdata->flags & CY_CORE_FLAG_RESTORE_PARAMETERS)) + goto exit; + + spin_lock(&cd->spinlock); + list_for_each_entry(param, &cd->param_list, node) { + spin_unlock(&cd->spinlock); + parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: Parameter id:%d value:%d\n", + __func__, param->id, param->value); + rc = cyttsp5_hid_output_set_param_(cd, param->id, + param->value, param->size); + if (rc) + goto exit; + spin_lock(&cd->spinlock); + } + spin_unlock(&cd->spinlock); +exit: + return rc; +} + +static int cyttsp5_check_and_deassert_int(struct cyttsp5_core_data *cd); + +static int _fast_startup(struct cyttsp5_core_data *cd) +{ + int retry = CY_CORE_STARTUP_RETRY_COUNT; + int rc; + +reset: + if (retry != CY_CORE_STARTUP_RETRY_COUNT) + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Retry %d\n", + __func__, CY_CORE_STARTUP_RETRY_COUNT - retry); + + rc = cyttsp5_check_and_deassert_int(cd); + if (rc < 0) + dev_err(cd->dev, "%s: Error on deassert INT r=%d\n", + __func__, rc); + + rc = cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc); + if (rc < 0) { + dev_err(cd->dev, "%s: Error on getting HID descriptor r=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + cd->mode = cyttsp5_get_mode(cd, &cd->hid_desc); + + if (cd->mode == CY_MODE_BOOTLOADER) { + dev_info(cd->dev, "%s: Bootloader mode\n", __func__); + rc = cyttsp5_hid_output_bl_launch_app_(cd); + if (rc < 0) { + dev_err(cd->dev, "%s: Error on launch app r=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + rc = cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc); + if (rc < 0) { + dev_err(cd->dev, + "%s: Error on getting HID descriptor r=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + cd->mode = cyttsp5_get_mode(cd, &cd->hid_desc); + if (cd->mode == CY_MODE_BOOTLOADER) { + if (retry--) + goto reset; + goto exit; + } + } + + rc = cyttsp5_restore_parameters_(cd); + if (rc) + dev_err(cd->dev, "%s: failed to restore parameters rc=%d\n", + __func__, rc); + +exit: + return rc; +} + +static int cyttsp5_core_poweron_device_(struct cyttsp5_core_data *cd) +{ + struct device *dev = cd->dev; + int rc; + + rc = cd->cpdata->power(cd->cpdata, 1, dev, 0); + if (rc < 0) { + dev_err(dev, "%s: HW Power up fails r=%d\n", __func__, rc); + goto exit; + } + + if (!cd->irq_enabled) { + cd->irq_enabled = true; + enable_irq(cd->irq); + } + + rc = _fast_startup(cd); +exit: + return rc; +} + +static int cyttsp5_core_wake_(struct cyttsp5_core_data *cd) +{ + int rc; + + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_ON) { + cd->sleep_state = SS_WAKING; + } else { + mutex_unlock(&cd->system_lock); + return 1; + } + mutex_unlock(&cd->system_lock); + + if (!IS_DEEP_SLEEP_CONFIGURED(cd->easy_wakeup_gesture) && + (cd->should_enable_gesture ==1) && + (cd->pm_mode == CYTTSP5_PM_DEEPSLEEP || + cd->pm_mode == CYTTSP5_PM_EASY_WAKEUP)) { + rc = cyttsp5_core_wake_device_(cd); + } else if ((cd->cpdata->flags & CY_CORE_FLAG_POWEROFF_ON_SLEEP) && + (cd->pm_mode == CYTTSP5_PM_POWEROFF)) + rc = cyttsp5_core_poweron_device_(cd); + else + rc = cyttsp5_core_wake_device_(cd); + + mutex_lock(&cd->system_lock); + cd->sleep_state = SS_SLEEP_OFF; + cd->pm_mode = CYTTSP5_PM_ACTIVE; + mutex_unlock(&cd->system_lock); + + cyttsp5_start_wd_timer(cd); + return rc; +} + +static int cyttsp5_core_wake(struct cyttsp5_core_data *cd) +{ + int rc; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return rc; + } + + rc = cyttsp5_core_wake_(cd); + + if (release_exclusive(cd, cd->dev) < 0) + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + else + parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: pass release exclusive\n", + __func__); + + return rc; +} + +static int cyttsp5_get_ic_crc_(struct cyttsp5_core_data *cd, u8 ebid) +{ + struct cyttsp5_sysinfo *si = &cd->sysinfo; + int rc; + u8 status; + u16 calculated_crc = 0; + u16 stored_crc = 0; + + rc = cyttsp5_hid_output_suspend_scanning_(cd); + if (rc) + goto error; + + rc = cyttsp5_hid_output_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: + cyttsp5_hid_output_resume_scanning_(cd); +error: + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: CRC: ebid:%d, crc:0x%04X\n", + __func__, ebid, si->ttconfig.crc); + return rc; +} + +static int cyttsp5_check_and_deassert_int(struct cyttsp5_core_data *cd) +{ + u16 size; + u8 buf[2]; + u8 *p; + u8 retry = 3; + int rc; + + do { + rc = cyttsp5_adap_read_default(cd, buf, 2); + if (rc < 0) + return rc; + size = get_unaligned_le16(&buf[0]); + + if (size == 2 || size == 0 || size >= CY_PIP_1P7_EMPTY_BUF) + return 0; + + p = kzalloc(size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + rc = cyttsp5_adap_read_default(cd, p, size); + kfree(p); + if (rc < 0) + return rc; + } while (retry--); + + return -EINVAL; +} + +static int cyttsp5_startup_(struct cyttsp5_core_data *cd, bool reset) +{ + int retry = CY_CORE_STARTUP_RETRY_COUNT; + int rc; + bool detected = false; + +#ifdef TTHE_TUNER_SUPPORT + tthe_print(cd, NULL, 0, "enter startup"); +#endif + + cyttsp5_stop_wd_timer(cd); + +reset: + if (retry != CY_CORE_STARTUP_RETRY_COUNT) + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Retry %d\n", + __func__, CY_CORE_STARTUP_RETRY_COUNT - retry); + + rc = cyttsp5_check_and_deassert_int(cd); + + if (reset || retry != CY_CORE_STARTUP_RETRY_COUNT) { + /* reset hardware */ + rc = cyttsp5_reset_and_wait(cd); + if (rc < 0) { + dev_err(cd->dev, "%s: Error on h/w reset r=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + } + +#ifndef GET_PANEL_ID_FROM_BOOTLOADER + /* Wait to skip bootloader mode */ + msleep(120); +#endif + + rc = cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc); + if (rc < 0) { + dev_err(cd->dev, "%s: Error on getting HID descriptor r=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + cd->mode = cyttsp5_get_mode(cd, &cd->hid_desc); + + detected = true; + +#ifdef GET_PANEL_ID_FROM_BOOTLOADER + /* Switch to bootloader mode to get Panel ID */ + if (cd->mode == CY_MODE_OPERATIONAL) { + rc = cyttsp5_hid_output_start_bootloader_(cd); + if (rc < 0) { + dev_err(cd->dev, "%s: Error on start bootloader r=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + dev_info(cd->dev, "%s: Bootloader mode\n", __func__); + } +#ifndef PT_PANEL_ID_FROM_MFG + cyttsp5_hid_output_bl_get_panel_id_(cd, &cd->panel_id); + + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Panel ID: 0x%02X\n", + __func__, cd->panel_id); +#endif + + rc = cyttsp5_hid_output_bl_launch_app_(cd); + if (rc < 0) { + dev_err(cd->dev, "%s: Error on launch app r=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } +#else + if (cd->mode == CY_MODE_BOOTLOADER) { + rc = cyttsp5_hid_output_bl_launch_app_(cd); + if (rc < 0) { + dev_err(cd->dev, "%s: Error on launch app r=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + } +#endif + rc = cyttsp5_get_hid_descriptor_(cd, &cd->hid_desc); + if (rc < 0) { + dev_err(cd->dev, + "%s: Error on getting HID descriptor r=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + cd->mode = cyttsp5_get_mode(cd, &cd->hid_desc); + if (cd->mode == CY_MODE_BOOTLOADER) { + if (retry--) + goto reset; + goto exit; + } + + mutex_lock(&cd->system_lock); + /* Read 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); + + cd->mode = cyttsp5_get_mode(cd, &cd->hid_desc); + if (cd->mode == CY_MODE_OPERATIONAL) + dev_info(cd->dev, "%s: Operational mode\n", __func__); + else if (cd->mode == CY_MODE_BOOTLOADER) + dev_info(cd->dev, "%s: Bootloader mode\n", __func__); + else if (cd->mode == CY_MODE_UNKNOWN) { + dev_err(cd->dev, "%s: Unknown mode\n", __func__); + rc = -ENODEV; + mutex_unlock(&cd->system_lock); + if (retry--) + goto reset; + goto exit; + } + mutex_unlock(&cd->system_lock); + +#if 0 + dev_info(cd->dev, "%s: Reading report descriptor\n", __func__); + rc = cyttsp5_get_report_descriptor_(cd); + if (rc < 0) { + dev_err(cd->dev, "%s: Error on getting report descriptor r=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } +#else + dev_info(cd->dev, "%s: Init report descriptor\n", __func__); + pt_init_pip_report_fields(cd); +#endif + + if (!cd->features.easywake) + cd->easy_wakeup_gesture = CY_CORE_EWG_NONE; + + rc = cyttsp5_hid_output_get_sysinfo_(cd); + if (rc) { + dev_err(cd->dev, "%s: Error on getting sysinfo r=%d\n", + __func__, rc); + if (retry--) + goto reset; + goto exit; + } + + dev_info(cd->dev, "cyttsp5 Protocol Version: %d.%d\n", + cd->sysinfo.cydata.pip_ver_major, + cd->sysinfo.cydata.pip_ver_minor); + + /* Read config version directly if PIP version < 1.2 */ + if (!IS_PIP_VER_GE(&cd->sysinfo, 1, 2)) { + rc = cyttsp5_get_config_ver_(cd); + if (rc) + dev_err(cd->dev, "%s: failed to read config version rc=%d\n", + __func__, rc); + } + +#ifdef PT_PANEL_ID_FROM_MFG + rc = cyttsp5_get_mfg_value_(cd, &cd->mfg_data[0], MFG_DATA_NUM); + if (rc) + dev_err(cd->dev, "%s: failed to read MFG data rc=%d\n", + __func__, rc); +#endif + + dev_info(cd->dev, "%s: Panel ID=0x%02x\n", __func__, cd->panel_id); + + rc = cyttsp5_get_ic_crc_(cd, CY_TCH_PARM_EBID); + if (rc) + dev_err(cd->dev, "%s: failed to crc data rc=%d\n", + __func__, rc); + + rc = cyttsp5_restore_parameters_(cd); + if (rc) + dev_err(cd->dev, "%s: failed to restore parameters rc=%d\n", + __func__, rc); + + /* attention startup */ + call_atten_cb(cd, CY_ATTEN_STARTUP, 0); + +exit: + if (!rc) + cd->startup_retry_count = 0; + + cyttsp5_start_wd_timer(cd); + + if (!detected) + rc = -ENODEV; + +#ifdef TTHE_TUNER_SUPPORT + tthe_print(cd, NULL, 0, "exit startup"); +#endif + + return rc; +} + +static int cyttsp5_startup(struct cyttsp5_core_data *cd, bool reset) +{ + int rc; + + mutex_lock(&cd->system_lock); + cd->startup_state = STARTUP_RUNNING; + mutex_unlock(&cd->system_lock); + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + goto exit; + } + + rc = cyttsp5_startup_(cd, reset); + + if (release_exclusive(cd, cd->dev) < 0) + /* Don't return fail code, mode is already changed. */ + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + else + parade_debug(cd->dev, DEBUG_LEVEL_2, "%s: pass release exclusive\n", + __func__); + +exit: + mutex_lock(&cd->system_lock); + cd->startup_state = STARTUP_NONE; + mutex_unlock(&cd->system_lock); + + /* Wake the waiters for end of startup */ + wake_up(&cd->wait_q); + + return rc; +} + +static void cyttsp5_startup_work_function(struct work_struct *work) +{ + struct cyttsp5_core_data *cd = container_of(work, + struct cyttsp5_core_data, startup_work); + int rc; + + rc = cyttsp5_startup(cd, true); + if (rc < 0) + dev_err(cd->dev, "%s: Fail queued startup r=%d\n", + __func__, rc); +} + +static void cyttsp5_ez_work_function(struct work_struct *work) +{ + struct cyttsp5_core_data *cd = + container_of(work, struct cyttsp5_core_data, ez_recovery_work); + int rc, t; + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(cd->dev, "%s: fail get exclusive ex=%p own=%p\n", + __func__, cd->exclusive_dev, cd->dev); + return; + } + + t = wait_event_timeout(cd->wait_q, (cd->wait_until_wake == 1), + msecs_to_jiffies(2000)); + if (IS_TMO(t)) { + dev_err(cd->dev, "%s: tmo waiting I2C master resume\n", + __func__); + } else { + rc = cyttsp5_check_and_deassert_int(cd); + if (rc < 0) + dev_err(cd->dev, "%s: Error on deassert INT r=%d\n", + __func__, rc); + } + + if (release_exclusive(cd, cd->dev) < 0) + /* Don't return fail code, mode is already changed. */ + dev_err(cd->dev, "%s: fail to release exclusive\n", __func__); + else + parade_debug(cd->dev, DEBUG_LEVEL_2, + "%s: pass release exclusive\n", __func__); +} + +/* + * CONFIG_PM_RUNTIME option is removed in 3.19.0. + */ +#if defined(CONFIG_PM_RUNTIME) || \ + (KERNEL_VERSION(3, 19, 0) <= LINUX_VERSION_CODE) +static __maybe_unused int cyttsp5_core_rt_suspend(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + int rc; + + rc = cyttsp5_core_sleep(cd); + if (rc < 0) { + dev_err(dev, "%s: Error on sleep\n", __func__); + return -EAGAIN; + } + return 0; +} + +static __maybe_unused int cyttsp5_core_rt_resume(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + int rc; + + rc = cyttsp5_core_wake(cd); + if (rc < 0) { + dev_err(dev, "%s: Error on wake\n", __func__); + return -EAGAIN; + } + + return 0; +} +#endif + +#if defined(CONFIG_PM_SLEEP) || defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +int cyttsp5_core_mmi_suspend(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + dev_dbg(dev, "%s: system PM suspend\n", __func__); + mutex_lock(&cd->system_lock); + cd->wait_until_wake = 0; + mutex_unlock(&cd->system_lock); + return 0; +} + +int cyttsp5_core_mmi_resume(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + dev_dbg(dev, "%s: system PM resume\n", __func__); + if (!IS_DEEP_SLEEP_CONFIGURED(cd->easy_wakeup_gesture)) { + mutex_lock(&cd->system_lock); + cd->wait_until_wake = 1; + mutex_unlock(&cd->system_lock); + wake_up(&cd->wait_q); + msleep(20); + } + return 0; +} + +int cyttsp5_core_suspend(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + cyttsp5_core_sleep(cd); + call_atten_cb(cd, CY_ATTEN_SUSPEND, 0); + + if (IS_DEEP_SLEEP_CONFIGURED(cd->easy_wakeup_gesture)) + return 0; + + /* + * This will not prevent resume + * Required to prevent interrupts before i2c awake + */ + if (!cd->should_enable_gesture) { + disable_irq(cd->irq); + cd->irq_disabled = 1; + } else { + if (device_may_wakeup(dev)) { + parade_debug(dev, DEBUG_LEVEL_2, "%s Device MAY wakeup\n", + __func__); + if (!enable_irq_wake(cd->irq)) + cd->irq_wake = 1; + } else { + parade_debug(dev, DEBUG_LEVEL_1, "%s Device MAY NOT wakeup\n", + __func__); + } + } + + dev_info(dev, "%s: suspend power state %s \n", __func__, + cd->pm_mode == CYTTSP5_PM_POWEROFF ? "CYTTSP5_PM_POWEROFF" : + (cd->pm_mode == CYTTSP5_PM_DEEPSLEEP ? "CYTTSP5_PM_DEEPSLEEP" : + (cd->pm_mode == CYTTSP5_PM_EASY_WAKEUP ? "CYTTSP5_PM_EASY_WAKEUP" : + (cd->pm_mode == CYTTSP5_PM_ACTIVE ? "CYTTSP5_PM_ACTIVE" : "Unknown")))); + + return 0; +} + +int cyttsp5_core_resume(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (IS_DEEP_SLEEP_CONFIGURED(cd->easy_wakeup_gesture)) + goto exit; + + /* + * I2C bus pm does not call suspend if device runtime suspended + * This flag is cover that case + */ + if (cd->irq_disabled) { + enable_irq(cd->irq); + cd->irq_disabled = 0; + } + + if (cd->should_enable_gesture) { + if (device_may_wakeup(dev)) { + parade_debug(dev, DEBUG_LEVEL_2, "%s Device MAY wakeup\n", + __func__); + if (cd->irq_wake) { + disable_irq_wake(cd->irq); + cd->irq_wake = 0; + } + } else { + parade_debug(dev, DEBUG_LEVEL_1, "%s Device MAY NOT wakeup\n", + __func__); + } + } +exit: + call_atten_cb(cd, CY_ATTEN_RESUME, 0); + cyttsp5_core_wake(cd); + + dev_info(dev, "%s: resume power state %s \n", __func__, + cd->pm_mode == CYTTSP5_PM_POWEROFF ? "CYTTSP5_PM_POWEROFF" : + (cd->pm_mode == CYTTSP5_PM_DEEPSLEEP ? "CYTTSP5_PM_DEEPSLEEP" : + (cd->pm_mode == CYTTSP5_PM_EASY_WAKEUP ? "CYTTSP5_PM_EASY_WAKEUP" : + (cd->pm_mode == CYTTSP5_PM_ACTIVE ? "CYTTSP5_PM_ACTIVE" : "Unknown")))); + + return 0; +} +#endif + +#if NEED_SUSPEND_NOTIFIER +static int cyttsp5_pm_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct cyttsp5_core_data *cd = container_of(nb, + struct cyttsp5_core_data, pm_notifier); + + if (action == PM_SUSPEND_PREPARE) { + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Suspend prepare\n", + __func__); + + /* + * If not runtime PM suspended, either call runtime + * PM suspend callback or wait until it finishes + */ + if (!pm_runtime_suspended(cd->dev)) + pm_runtime_suspend(cd->dev); + + (void) cyttsp5_core_suspend(cd->dev); + } + + return NOTIFY_DONE; +} +#endif + +const struct dev_pm_ops cyttsp5_pm_ops = { +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + SET_SYSTEM_SLEEP_PM_OPS(cyttsp5_core_mmi_suspend, cyttsp5_core_mmi_resume) +#else + SET_SYSTEM_SLEEP_PM_OPS(cyttsp5_core_suspend, cyttsp5_core_resume) + SET_RUNTIME_PM_OPS(cyttsp5_core_rt_suspend, cyttsp5_core_rt_resume, + NULL) +#endif +}; +EXPORT_SYMBOL_GPL(cyttsp5_pm_ops); + +/* + * Show Firmware version via sysfs + */ +static ssize_t cyttsp5_ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_cydata *cydata = &cd->sysinfo.cydata; + + return sprintf(buf, + "%s: 0x%02X\n" + "%s: 0x%02X\n" + "%s: 0x%08X\n" + "%s: 0x%04X\n" + "%s: 0x%02X\n" + "%s: 0x%02X\n" + "%s: 0x%02X\n" + "%s: 0x%02X\n", + "Firmware Major Version", cydata->fw_ver_major, + "Firmware Minor Version", cydata->fw_ver_minor, + "Revision Control Number", cydata->revctrl, + "Firmware Configuration Version", cydata->fw_ver_conf, + "Bootloader Major Version", cydata->bl_ver_major, + "Bootloader Minor Version", cydata->bl_ver_minor, + "Protocol Major Version", cydata->pip_ver_major, + "Protocol Minor Version", cydata->pip_ver_minor); +} + +/* + * Show Driver version via sysfs + */ +static ssize_t cyttsp5_drv_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, CY_MAX_PRBUF_SIZE, + "Driver: %s\nVersion: %s\nDate: %s\n", + cy_driver_core_name, cy_driver_core_version, + cy_driver_core_date); +} + +/* + * HW reset via sysfs + */ +static ssize_t cyttsp5_hw_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + int rc; + + rc = cyttsp5_startup(cd, true); + if (rc < 0) + dev_err(dev, "%s: HW reset failed r=%d\n", + __func__, rc); + + return size; +} + +/* + * Show IRQ status via sysfs + */ +static ssize_t cyttsp5_hw_irq_stat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + int retval; + + if (cd->cpdata->irq_stat) { + retval = cd->cpdata->irq_stat(cd->cpdata, dev); + switch (retval) { + case 0: + return snprintf(buf, CY_MAX_PRBUF_SIZE, + "Interrupt line is LOW.\n"); + case 1: + return snprintf(buf, CY_MAX_PRBUF_SIZE, + "Interrupt line is HIGH.\n"); + default: + return snprintf(buf, CY_MAX_PRBUF_SIZE, + "Function irq_stat() returned %d.\n", retval); + } + } + + return snprintf(buf, CY_MAX_PRBUF_SIZE, + "Function irq_stat() undefined.\n"); +} + +/* + * Show IRQ enable/disable status via sysfs + */ +static ssize_t cyttsp5_drv_irq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + ssize_t ret; + + mutex_lock(&cd->system_lock); + if (cd->irq_enabled) + ret = snprintf(buf, CY_MAX_PRBUF_SIZE, + "Driver interrupt is ENABLED\n"); + else + ret = snprintf(buf, CY_MAX_PRBUF_SIZE, + "Driver interrupt is DISABLED\n"); + mutex_unlock(&cd->system_lock); + + return ret; +} + +/* + * Enable/disable IRQ via sysfs + */ +static ssize_t cyttsp5_drv_irq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + unsigned long value; + int retval = 0; + + retval = kstrtoul(buf, 10, &value); + if (retval < 0) { + dev_err(dev, "%s: Invalid value\n", __func__); + goto cyttsp5_drv_irq_store_error_exit; + } + + mutex_lock(&cd->system_lock); + switch (value) { + case 0: + if (cd->irq_enabled) { + cd->irq_enabled = false; + /* Disable IRQ */ + disable_irq_nosync(cd->irq); + dev_info(dev, "%s: Driver IRQ now disabled\n", + __func__); + } else + dev_info(dev, "%s: Driver IRQ already disabled\n", + __func__); + break; + + case 1: + if (cd->irq_enabled == false) { + cd->irq_enabled = true; + /* Enable IRQ */ + enable_irq(cd->irq); + dev_info(dev, "%s: Driver IRQ now enabled\n", + __func__); + } else + dev_info(dev, "%s: Driver IRQ already enabled\n", + __func__); + break; + + default: + dev_err(dev, "%s: Invalid value\n", __func__); + } + mutex_unlock(&(cd->system_lock)); + +cyttsp5_drv_irq_store_error_exit: + + return size; +} + +/* + * Gets user input from sysfs and parse it + * return size of parsed output buffer + */ + +#define CY_MAX_CONFIG_BYTES_DEC 256 +#define CYTTSP5_INPUT_ELEM_SZ_DEC 10 + +static int cyttsp5_ic_parse_input_dec(struct device *dev, const char *buf, + size_t buf_size, u32 *ic_buf, size_t ic_buf_size) +{ + const char *pbuf = buf; + unsigned long value; + char scan_buf[CYTTSP5_INPUT_ELEM_SZ_DEC]; + u32 i = 0; + u32 j; + int last = 0; + int ret; + + parade_debug(dev, DEBUG_LEVEL_1, "%s: pbuf=%p buf=%p size=%zu %s=%zu buf=%s\n", + __func__, pbuf, buf, buf_size, "scan buf size", + (size_t)CYTTSP5_INPUT_ELEM_SZ_DEC, buf); + + while (pbuf <= (buf + buf_size)) { + if (i >= CY_MAX_CONFIG_BYTES_DEC) { + dev_err(dev, "%s: %s size=%d max=%d\n", __func__, + "Max cmd size exceeded", i, + CY_MAX_CONFIG_BYTES_DEC); + return -EINVAL; + } + if (i >= ic_buf_size) { + dev_err(dev, "%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, CYTTSP5_INPUT_ELEM_SZ_DEC); + if ((last == ',') && (*pbuf == ',')) { + dev_err(dev, "%s: %s \",,\" not allowed.\n", __func__, + "Invalid data format."); + return -EINVAL; + } + for (j = 0; j < (CYTTSP5_INPUT_ELEM_SZ_DEC - 1) + && (pbuf < (buf + buf_size)) + && (*pbuf != ' ') + && (*pbuf != ','); j++) { + last = *pbuf; + scan_buf[j] = *pbuf++; + } + ret = kstrtoul(scan_buf, 10, &value); + if (ret < 0) { + dev_err(dev, "%s: Invalid data format.\n", __func__); + return ret; + } + + ic_buf[i] = value; + i++; + } + + return i; +} + +/* + * Debugging options via sysfs + */ +static ssize_t cyttsp5_drv_debug_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + unsigned long value; + int rc; + u8 return_data[8]; + static u8 wd_disabled; + u32 input_data[2]; + int length; + + /*maximal input two data*/ + length = cyttsp5_ic_parse_input_dec(dev, buf, size, input_data, + 3); + if (length <= 0) { + dev_err(dev, "%s: %s failed\n", __func__, + "cyttsp5_ic_parse_input_dec"); + goto cyttsp5_drv_debug_store_exit; + } + value = input_data[0]; + + + /* Start watchdog timer command */ + if (value == CY_DBG_HID_START_WD) { + dev_info(dev, "%s: start watchdog (cd=%p)\n", __func__, cd); + wd_disabled = 0; + cyttsp5_start_wd_timer(cd); + goto cyttsp5_drv_debug_store_exit; + } + + /* Stop watchdog timer temporarily */ + cyttsp5_stop_wd_timer(cd); + + if (value == CY_DBG_HID_STOP_WD) { + dev_info(dev, "%s: stop watchdog (cd=%p)\n", __func__, cd); + wd_disabled = 1; + goto cyttsp5_drv_debug_store_exit; + } + + switch (value) { + case CY_DBG_SUSPEND: + dev_info(dev, "%s: SUSPEND (cd=%p)\n", __func__, cd); + rc = cyttsp5_core_sleep(cd); + if (rc) + dev_err(dev, "%s: Suspend failed rc=%d\n", + __func__, rc); + else + dev_info(dev, "%s: Suspend succeeded\n", __func__); + break; + + case CY_DBG_RESUME: + dev_info(dev, "%s: RESUME (cd=%p)\n", __func__, cd); + rc = cyttsp5_core_wake(cd); + if (rc) + dev_err(dev, "%s: Resume failed rc=%d\n", + __func__, rc); + else + dev_info(dev, "%s: Resume succeeded\n", __func__); + break; + case CY_DBG_SOFT_RESET: + dev_info(dev, "%s: SOFT RESET (cd=%p)\n", __func__, cd); + rc = cyttsp5_hw_soft_reset(cd); + break; + case CY_DBG_RESET: + dev_info(dev, "%s: HARD RESET (cd=%p)\n", __func__, cd); + rc = cyttsp5_hw_hard_reset(cd); + break; + case CY_DBG_HID_RESET: + dev_info(dev, "%s: hid_reset (cd=%p)\n", __func__, cd); + cyttsp5_hid_cmd_reset(cd); + break; + case CY_DBG_HID_SET_POWER_ON: + dev_info(dev, "%s: hid_set_power_on (cd=%p)\n", __func__, cd); + cyttsp5_hid_cmd_set_power(cd, HID_POWER_ON); + wd_disabled = 0; + break; + case CY_DBG_HID_SET_POWER_SLEEP: + dev_info(dev, "%s: hid_set_power_off (cd=%p)\n", __func__, cd); + wd_disabled = 1; + cyttsp5_hid_cmd_set_power(cd, HID_POWER_SLEEP); + break; + case CY_DBG_HID_NULL: + dev_info(dev, "%s: hid_null (cd=%p)\n", __func__, cd); + cyttsp5_hid_output_null(cd); + break; + case CY_DBG_HID_ENTER_BL: + dev_info(dev, "%s: start_bootloader (cd=%p)\n", __func__, cd); + cyttsp5_hid_output_start_bootloader(cd); + break; + case CY_DBG_HID_SYSINFO: + dev_info(dev, "%s: get_sysinfo (cd=%p)\n", __func__, cd); + cyttsp5_hid_output_get_sysinfo(cd); + break; + case CY_DBG_HID_SUSPEND_SCAN: + dev_info(dev, "%s: suspend_scanning (cd=%p)\n", __func__, cd); + cyttsp5_hid_output_suspend_scanning(cd); + break; + case CY_DBG_HID_RESUME_SCAN: + dev_info(dev, "%s: resume_scanning (cd=%p)\n", __func__, cd); + cyttsp5_hid_output_resume_scanning(cd); + break; + case HID_OUTPUT_BL_VERIFY_APP_INTEGRITY: + dev_info(dev, "%s: verify app integ (cd=%p)\n", __func__, cd); + cyttsp5_hid_output_bl_verify_app_integrity(cd, &return_data[0]); + break; + case HID_OUTPUT_BL_GET_INFO: + dev_info(dev, "%s: bl get info (cd=%p)\n", __func__, cd); + cyttsp5_hid_output_bl_get_information(cd, return_data); + break; + case HID_OUTPUT_BL_PROGRAM_AND_VERIFY: + dev_info(dev, "%s: program and verify (cd=%p)\n", __func__, cd); + cyttsp5_hid_output_bl_program_and_verify(cd, 0, NULL); + break; + case HID_OUTPUT_BL_LAUNCH_APP: + dev_info(dev, "%s: launch app (cd=%p)\n", __func__, cd); + cyttsp5_hid_output_bl_launch_app(cd); + break; + case HID_OUTPUT_BL_INITIATE_BL: + dev_info(dev, "%s: initiate bl (cd=%p)\n", __func__, cd); + cyttsp5_hid_output_bl_initiate_bl(cd, 0, NULL, 0, NULL); + break; +#ifdef TTHE_TUNER_SUPPORT + case CY_TTHE_TUNER_EXIT: + cd->tthe_exit = 1; + wake_up(&cd->wait_q); + kfree(cd->tthe_buf); + cd->tthe_buf = NULL; + cd->tthe_exit = 0; + break; + case CY_TTHE_BUF_CLEAN: + if (cd->tthe_buf) + memset(cd->tthe_buf, 0, CY_MAX_PRBUF_SIZE); + else + dev_info(dev, "%s : tthe_buf not existed\n", __func__); + break; +#endif + case CY_DBG_REPORT_LEVEL: + mutex_lock(&cd->system_lock); + cd->debug_level = input_data[1]; + dev_info(dev, "%s: Set debug_level: %d\n", + __func__, cd->debug_level); + mutex_unlock(&(cd->system_lock)); + break; + case CY_DBG_WATCHDOG_INTERVAL: + mutex_lock(&cd->system_lock); + if (input_data[1] > 0) + cd->watchdog_interval = input_data[1]; + dev_info(dev, "%s: Set watchdog_interval: %d\n", + __func__, cd->watchdog_interval); + mutex_unlock(&(cd->system_lock)); + break; + case CY_DBG_SHOW_TIMESTAMP: + mutex_lock(&cd->system_lock); + cd->show_timestamp = input_data[1]; + dev_info(dev, "%s: Set show_timestamp: %d\n", + __func__, cd->show_timestamp); + mutex_unlock(&(cd->system_lock)); + break; + + default: + dev_err(dev, "%s: Invalid value\n", __func__); + } + + /* Enable watchdog timer */ + if (!wd_disabled) + cyttsp5_start_wd_timer(cd); + +cyttsp5_drv_debug_store_exit: + return size; +} + +/* + * Show system status on deep sleep status via sysfs + */ +static ssize_t cyttsp5_sleep_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + ssize_t ret; + + mutex_lock(&cd->system_lock); + if (cd->sleep_state == SS_SLEEP_ON) + ret = snprintf(buf, CY_MAX_PRBUF_SIZE, "off\n"); + else + ret = snprintf(buf, CY_MAX_PRBUF_SIZE, "on\n"); + mutex_unlock(&cd->system_lock); + + return ret; +} + +static ssize_t cyttsp5_easy_wakeup_gesture_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + ssize_t ret; + + mutex_lock(&cd->system_lock); + ret = snprintf(buf, CY_MAX_PRBUF_SIZE, "0x%02X\n", + cd->easy_wakeup_gesture); + mutex_unlock(&cd->system_lock); + return ret; +} + +static ssize_t cyttsp5_easy_wakeup_gesture_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + unsigned long value; + int ret; + + if (!cd->features.easywake) + return -EINVAL; + + ret = kstrtoul(buf, 10, &value); + if (ret < 0) + return ret; + + if (value > 0xFF) + return -EINVAL; + + 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)value; + else + ret = -ENODEV; + mutex_unlock(&cd->system_lock); + + pm_runtime_put(dev); + + if (ret) + return ret; + + return size; +} + +#ifdef EASYWAKE_TSG6 +/* + * Show easywake gesture id via sysfs + */ +static ssize_t cyttsp5_easy_wakeup_gesture_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + ssize_t ret; + + mutex_lock(&cd->system_lock); + ret = snprintf(buf, CY_MAX_PRBUF_SIZE, "0x%02X\n", + cd->gesture_id); + mutex_unlock(&cd->system_lock); + return ret; +} + +/* + * Show easywake gesture data via sysfs + * The format: + * x1(LSB), x1(MSB),y1(LSB), y1(MSB),x2(LSB), x2(MSB),y2(LSB), y2(MSB),... + */ +static ssize_t cyttsp5_easy_wakeup_gesture_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + ssize_t ret = 0; + int i; + + mutex_lock(&cd->system_lock); + + for (i = 0; i < cd->gesture_data_length; i++) + ret += snprintf(buf + ret, CY_MAX_PRBUF_SIZE - ret, + "0x%02X\n", cd->gesture_data[i]); + + ret += snprintf(buf + ret, CY_MAX_PRBUF_SIZE - ret, + "(%d bytes)\n", cd->gesture_data_length); + + mutex_unlock(&cd->system_lock); + return ret; +} +#endif + +/* Show Panel ID via sysfs */ +static ssize_t cyttsp5_panel_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + ssize_t ret; + + ret = snprintf(buf, CY_MAX_PRBUF_SIZE, "0x%02X\n", + cd->panel_id); + return ret; +} + +/* Show platform data via sysfs */ +static ssize_t cyttsp5_platform_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_platform_data *pdata = dev_get_platdata(dev); + ssize_t ret; + + ret = sprintf(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", + "Interrupt GPIO", pdata->core_pdata->irq_gpio, + "Reset GPIO", pdata->core_pdata->rst_gpio, + "Level trigger delay (us)", pdata->core_pdata->level_irq_udelay, + "HID descriptor register", pdata->core_pdata->hid_desc_register, + "Vendor ID", pdata->core_pdata->vendor_id, + "Product ID", pdata->core_pdata->product_id, + "Easy wakeup gesture", pdata->core_pdata->easy_wakeup_gesture, + "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); + return ret; +} + +/*********************************************************************** + * Show MFG test by sysfs + ***********************************************************************/ +#define PIP_CMD_MAX_LENGTH ((1 << 16) - 1) +#define STATUS_SUCCESS 0 +#define STATUS_FAIL 1 + +static int prepare_print_buffer(u8 status, u8 *in_buf, int length, + u8 *out_buf, size_t out_buf_size) +{ + int index = 0; + int i; + + index += scnprintf(out_buf, out_buf_size, "%02X\n", status); + + for (i = 0; i < length; i++) { + index += scnprintf(&out_buf[index], out_buf_size - index, + "%02X\n", in_buf[i]); + } + + return index; +} + +static ssize_t cyttsp5_calibrate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + u32 input_data[2]; + int length; + + /*maximal input two data*/ + length = cyttsp5_ic_parse_input_dec(dev, buf, size, input_data, + 3); + if (length <= 0) { + dev_err(dev, "%s: %s failed\n", __func__, + "cyttsp5_ic_parse_input_dec"); + return -EINVAL; + } + + mutex_lock(&cd->system_lock); + cd->calibrate_sensing_mode = input_data[0]; + cd->calibrate_initialize_baselines = input_data[1]; + mutex_unlock(&cd->system_lock); + + return size; +} + +static ssize_t cyttsp5_calibrate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + ssize_t cnt = 0; + u8 status_cal = 0; + u8 status_baseline = 0; + int rc = 0; + + pm_runtime_get_sync(dev); + + rc = request_exclusive(cd, cd->dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(dev, "%s: Error on request exclusive r=%d\n", + __func__, rc); + goto put_pm_runtime; + } + + rc = cyttsp5_hid_output_suspend_scanning_(cd); + if (rc < 0) { + dev_err(dev, "%s: Error on suspend scan r=%d\n", + __func__, rc); + goto release_exclusive; + } + + rc = cyttsp5_hid_output_calibrate_idacs_( + cd, cd->calibrate_sensing_mode, &status_cal); + if (rc < 0) { + dev_err(dev, "%s: Error on calibrate idacs r=%d\n", + __func__, rc); + goto resume_scan; + } + + /* Check if baseline initialization is requested */ + if (cd->calibrate_initialize_baselines) { + /* Perform baseline initialization for all modes */ + rc = cyttsp5_hid_output_initialize_baselines_( + cd, CY_IB_SM_MUTCAP | CY_IB_SM_SELFCAP | CY_IB_SM_BUTTON, + &status_baseline); + if (rc < 0) { + dev_err(dev, "%s: Error on initialize baselines r=%d\n", + __func__, rc); + goto resume_scan; + } + } + +resume_scan: + cyttsp5_hid_output_resume_scanning_(cd); + +release_exclusive: + release_exclusive(cd, cd->dev); + +put_pm_runtime: + pm_runtime_put(dev); + + if (rc) { + cnt = sprintf(buf, "command_result:%d\n", rc); + } else if (!cd->calibrate_initialize_baselines) { + cnt = sprintf(buf, "command_result:%d\nstatus_calibration:%d\n", rc, status_cal); + } else { + cnt = sprintf(buf, "command_result:%d\nstatus_calibration:%d\nstatus_baseline:%d\n", + rc, status_cal, status_baseline); + } + + return cnt; +} + +static ssize_t cyttsp5_run_and_get_selftest_result(struct device *dev, + char *buf, size_t buf_len, u8 test_id, u16 read_length, + bool get_result_on_pass) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + u8 status = STATUS_FAIL; + u8 cmd_status = 0; + u8 summary_result = 0; + u16 act_length = 0; + int length = 0; + int size; + int rc; + + pm_runtime_get_sync(dev); + + rc = request_exclusive(cd, dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(dev, "%s: Error on request exclusive r=%d\n", + __func__, rc); + goto put_pm_runtime; + } + + rc = cyttsp5_hid_output_suspend_scanning_(cd); + if (rc < 0) { + dev_err(dev, "%s: Error on suspend scan r=%d\n", + __func__, rc); + goto release_exclusive; + } + + rc = cyttsp5_hid_output_run_selftest_(cd, test_id, 0, + &cmd_status, &summary_result, NULL); + if (rc < 0) { + dev_err(dev, "%s: Error on run self test for test_id:%d r=%d\n", + __func__, test_id, rc); + goto resume_scan; + } + + /* Form response buffer */ + cd->ic_buf[0] = cmd_status; + cd->ic_buf[1] = summary_result; + + length = 2; + + /* Get data if command status is success */ + if (cmd_status != CY_CMD_STATUS_SUCCESS) + goto status_success; + + /* Get data unless test result is pass */ + if (summary_result == CY_ST_RESULT_PASS && !get_result_on_pass) + goto status_success; + + rc = cyttsp5_hid_output_get_selftest_result_(cd, 0, read_length, + test_id, &cmd_status, &act_length, &cd->ic_buf[6]); + if (rc < 0) { + dev_err(dev, "%s: Error on get self test result r=%d\n", + __func__, rc); + goto resume_scan; + } + + cd->ic_buf[2] = cmd_status; + cd->ic_buf[3] = test_id; + cd->ic_buf[4] = LOW_BYTE(act_length); + cd->ic_buf[5] = HI_BYTE(act_length); + + length = 6 + act_length; + +status_success: + status = STATUS_SUCCESS; + +resume_scan: + cyttsp5_hid_output_resume_scanning_(cd); + +release_exclusive: + release_exclusive(cd, dev); + +put_pm_runtime: + pm_runtime_put(dev); + + if (status == STATUS_FAIL) + length = 0; + + size = prepare_print_buffer(status, cd->ic_buf, length, buf, buf_len); + + return size; +} + +static ssize_t auto_shorts_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + /* Set length to PIP_CMD_MAX_LENGTH to read all */ + cyttsp5_run_and_get_selftest_result( + dev, cd->pr_buf, sizeof(cd->pr_buf), + CY_ST_ID_AUTOSHORTS, PIP_CMD_MAX_LENGTH, true); + + return snprintf(buf, CY_MAX_PRBUF_SIZE, "%s", cd->pr_buf); +} + +static ssize_t cm_panel_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + /* Set length to PIP_CMD_MAX_LENGTH to read all */ + cyttsp5_run_and_get_selftest_result( + dev, cd->pr_buf, sizeof(cd->pr_buf), + CY_ST_ID_CM_PANEL, PIP_CMD_MAX_LENGTH, true); + + return snprintf(buf, CY_MAX_PRBUF_SIZE, "%s", cd->pr_buf); +} + +static ssize_t cp_panel_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + /* Set length to PIP_CMD_MAX_LENGTH to read all */ + cyttsp5_run_and_get_selftest_result( + dev, cd->pr_buf, sizeof(cd->pr_buf), + CY_ST_ID_CP_PANEL, PIP_CMD_MAX_LENGTH, true); + + return snprintf(buf, CY_MAX_PRBUF_SIZE, "%s", cd->pr_buf); +} + +/* End MFG test support by sysfs */ + +static struct device_attribute attributes[] = { + __ATTR(ic_ver, S_IRUGO, cyttsp5_ic_ver_show, NULL), + __ATTR(drv_ver, S_IRUGO, cyttsp5_drv_ver_show, NULL), + __ATTR(hw_reset, S_IWUSR, NULL, cyttsp5_hw_reset_store), + __ATTR(hw_irq_stat, S_IRUSR, cyttsp5_hw_irq_stat_show, NULL), + __ATTR(drv_irq, S_IRUSR | S_IWUSR, cyttsp5_drv_irq_show, + cyttsp5_drv_irq_store), + __ATTR(drv_debug, S_IWUSR, NULL, cyttsp5_drv_debug_store), + __ATTR(sleep_status, S_IRUSR, cyttsp5_sleep_status_show, NULL), + __ATTR(easy_wakeup_gesture, S_IRUSR | S_IWUSR, + cyttsp5_easy_wakeup_gesture_show, + cyttsp5_easy_wakeup_gesture_store), +#ifdef EASYWAKE_TSG6 + __ATTR(easy_wakeup_gesture_id, S_IRUSR, + cyttsp5_easy_wakeup_gesture_id_show, NULL), + __ATTR(easy_wakeup_gesture_data, S_IRUSR, + cyttsp5_easy_wakeup_gesture_data_show, NULL), +#endif + __ATTR(panel_id, S_IRUGO, cyttsp5_panel_id_show, NULL), + __ATTR(platform_data, S_IRUGO, cyttsp5_platform_data_show, NULL), + __ATTR(calibrate, S_IRUSR | S_IWUSR, cyttsp5_calibrate_show, + cyttsp5_calibrate_store), + __ATTR(cm_panel, S_IRUGO, cm_panel_show, NULL), + __ATTR(cp_panel, S_IRUGO, cp_panel_show, NULL), + __ATTR(auto_shorts, S_IRUGO, auto_shorts_show, NULL), +}; + +static int add_sysfs_interfaces(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(attributes); i++) + if (device_create_file(dev, attributes + i)) + goto undo; + return 0; +undo: + for (i--; i >= 0; i--) + device_remove_file(dev, attributes + i); + dev_err(dev, "%s: failed to create sysfs interface\n", __func__); + return -ENODEV; +} + +static void remove_sysfs_interfaces(struct device *dev) +{ + u32 i; + + for (i = 0; i < ARRAY_SIZE(attributes); i++) + device_remove_file(dev, attributes + i); +} + +/* + * ttdl_restart via sysfs + */ +static ssize_t cyttsp5_ttdl_restart_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int rc; + struct i2c_client *client = to_i2c_client(dev); + + if (is_cyttsp5_probe_success) { + dev_err(dev, "%s: previous cyttsp5_probe is successful, do nothing\n", + __func__); + return size; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(dev, "%s I2C functionality not Supported\n", __func__); + return -EIO; + } + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT + rc = cyttsp5_devtree_create_and_get_pdata(dev); + if (rc < 0) + return rc; +#endif + + rc = cyttsp5_probe(cyttsp5_bus_ops_save, &client->dev, client->irq, + 512); + + if (!rc) { + is_cyttsp5_probe_success = true; + dev_err(dev, "%s restart successful\n", __func__); + } else { + is_cyttsp5_probe_success = false; + dev_err(dev, "%s: ttdl restart failed r=%d\n", + __func__, rc); + } + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT + if (rc) + cyttsp5_devtree_clean_pdata(dev); +#endif + + return size; + +} +static DEVICE_ATTR(ttdl_restart, S_IWUSR, NULL, cyttsp5_ttdl_restart_store); + + +#ifdef TTHE_TUNER_SUPPORT +static int tthe_debugfs_open(struct inode *inode, struct file *filp) +{ + struct cyttsp5_core_data *cd = inode->i_private; + + filp->private_data = inode->i_private; + + if (cd->tthe_buf) + return -EBUSY; + + cd->tthe_buf = kzalloc(CY_MAX_PRBUF_SIZE, GFP_KERNEL); + if (!cd->tthe_buf) + return -ENOMEM; + + return 0; +} + +static int tthe_debugfs_close(struct inode *inode, struct file *filp) +{ + struct cyttsp5_core_data *cd = filp->private_data; + + filp->private_data = NULL; + + kfree(cd->tthe_buf); + cd->tthe_buf = NULL; + + return 0; +} + +static ssize_t tthe_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct cyttsp5_core_data *cd = filp->private_data; + int size; + int ret; + + 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; + } + + ret = copy_to_user(buf, cd->tthe_buf, cd->tthe_buf_len); + 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 + +#ifdef CONFIG_CYTTSP5_CREATE_PT_BL_FROM_FILE +/******************************************************************************* + * 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; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + mm_segment_t oldfs; +#endif + int rc = 0; + + if (file_path == NULL || buf == NULL) { + dev_err(dev, "%s: path || buf is NULL.\n", __func__); + return -EINVAL; + } + + dev_info(dev, "%s: path = %s\n", __func__, file_path); + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + oldfs = get_fs(); + set_fs(KERNEL_DS); +#endif + filp = filp_open(file_path, O_RDONLY, S_IRUSR); + if (IS_ERR(filp)) { + dev_err(dev, "%s: Failed to open %s\n", __func__, + file_path); + rc = -EIO; + goto err; + } + + if (filp->f_op == NULL) { + dev_err(dev, "%s: File Operation Method Error\n", + __func__); + rc = -EINVAL; + goto exit; + } + inode = filp->f_path.dentry->d_inode; + if (inode == NULL) { + dev_err(dev, "%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) { + dev_err(dev, "%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) { + dev_err(dev, "%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, 4, 0)) + if (vfs_read(filp, buf, read_len, &(filp->f_pos)) != read_len) { +#else + if (kernel_read(filp, buf, read_len, &(filp->f_pos)) != read_len) { +#endif + dev_err(dev, "%s: file read error.\n", __func__); + rc = -EINVAL; + goto exit; + } + *size = read_len; + +exit: + if (filp_close(filp, NULL) != 0) + dev_err(dev, "%s: file close error.\n", __func__); +err: +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + set_fs(oldfs); +#endif + return rc; +} +#endif + +static struct cyttsp5_core_nonhid_cmd _cyttsp5_core_nonhid_cmd = { + .start_bl = _cyttsp5_request_hid_output_start_bl, + .suspend_scanning = _cyttsp5_request_hid_output_suspend_scanning, + .resume_scanning = _cyttsp5_request_hid_output_resume_scanning, + .get_param = _cyttsp5_request_hid_output_get_param, + .set_param = _cyttsp5_request_hid_output_set_param, + .verify_config_block_crc = + _cyttsp5_request_hid_output_verify_config_block_crc, + .get_config_row_size = _cyttsp5_request_hid_output_get_config_row_size, + .get_data_structure = _cyttsp5_request_hid_output_get_data_structure, + .run_selftest = _cyttsp5_request_hid_output_run_selftest, + .get_selftest_result = _cyttsp5_request_hid_output_get_selftest_result, + .calibrate_idacs = _cyttsp5_request_hid_output_calibrate_idacs, + .initialize_baselines = + _cyttsp5_request_hid_output_initialize_baselines, + .exec_panel_scan = _cyttsp5_request_hid_output_exec_panel_scan, + .retrieve_panel_scan = _cyttsp5_request_hid_output_retrieve_panel_scan, + .write_conf_block = _cyttsp5_request_hid_output_write_conf_block, + .user_cmd = _cyttsp5_request_hid_output_user_cmd, + .get_bl_info = _cyttsp5_request_hid_output_bl_get_information, + .initiate_bl = _cyttsp5_request_hid_output_bl_initiate_bl, + .launch_app = _cyttsp5_request_hid_output_launch_app, + .prog_and_verify = _cyttsp5_request_hid_output_bl_program_and_verify, + .verify_app_integrity = + _cyttsp5_request_hid_output_bl_verify_app_integrity, + .get_panel_id = _cyttsp5_request_hid_output_bl_get_panel_id, +#ifdef CONFIG_CYTTSP5_CREATE_PT_BL_FROM_FILE + .read_us_file = _pt_read_us_file, +#endif +}; + +static struct cyttsp5_core_commands _cyttsp5_core_commands = { + .subscribe_attention = _cyttsp5_subscribe_attention, + .unsubscribe_attention = _cyttsp5_unsubscribe_attention, + .request_exclusive = _cyttsp5_request_exclusive, + .release_exclusive = _cyttsp5_release_exclusive, + .request_reset = _cyttsp5_request_reset, + .request_restart = _cyttsp5_request_restart, + .request_sysinfo = _cyttsp5_request_sysinfo, + .request_loader_pdata = _cyttsp5_request_loader_pdata, + .request_stop_wd = _cyttsp5_request_stop_wd, + .request_start_wd = _cyttsp5_request_start_wd, + .request_get_hid_desc = _cyttsp5_request_get_hid_desc, + .request_get_mode = _cyttsp5_request_get_mode, +#ifdef TTHE_TUNER_SUPPORT + .request_tthe_print = _cyttsp5_request_tthe_print, +#endif + .nonhid_cmd = &_cyttsp5_core_nonhid_cmd, +}; + +struct cyttsp5_core_commands *cyttsp5_get_commands(void) +{ + return &_cyttsp5_core_commands; +} +EXPORT_SYMBOL_GPL(cyttsp5_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; + +static int cyttsp5_probe_module(struct cyttsp5_core_data *cd, + struct cyttsp5_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; +} + +static void cyttsp5_release_module(struct cyttsp5_core_data *cd, + struct cyttsp5_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); +} + +static void cyttsp5_probe_modules(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_module *m; + int rc = 0; + + mutex_lock(&module_list_lock); + list_for_each_entry(m, &module_list, node) { + rc = cyttsp5_probe_module(cd, m); + if (rc) + dev_err(cd->dev, "%s: Probe fails for module %s\n", + __func__, m->name); + } + mutex_unlock(&module_list_lock); +} + +static void cyttsp5_release_modules(struct cyttsp5_core_data *cd) +{ + struct cyttsp5_module *m; + + mutex_lock(&module_list_lock); + list_for_each_entry(m, &module_list, node) + cyttsp5_release_module(cd, m); + mutex_unlock(&module_list_lock); +} + +struct cyttsp5_core_data *cyttsp5_get_core_data(char *id) +{ + struct cyttsp5_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(cyttsp5_get_core_data); + +static void cyttsp5_add_core(struct device *dev) +{ + struct cyttsp5_core_data *d; + struct cyttsp5_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); +} + +static void cyttsp5_del_core(struct device *dev) +{ + struct cyttsp5_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); +} + +int cyttsp5_register_module(struct cyttsp5_module *module) +{ + struct cyttsp5_module *m; + struct cyttsp5_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) + cyttsp5_probe_module(cd, module); + mutex_unlock(&core_list_lock); + +unlock: + mutex_unlock(&module_list_lock); + return rc; +} +EXPORT_SYMBOL_GPL(cyttsp5_register_module); + +void cyttsp5_unregister_module(struct cyttsp5_module *module) +{ + struct cyttsp5_module *m, *m_n; + struct cyttsp5_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) + cyttsp5_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(cyttsp5_unregister_module); + +void *cyttsp5_get_module_data(struct device *dev, struct cyttsp5_module *module) +{ + struct cyttsp5_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(cyttsp5_get_module_data); + +#ifdef CONFIG_HAS_EARLYSUSPEND +static void cyttsp5_early_suspend(struct early_suspend *h) +{ + struct cyttsp5_core_data *cd = + container_of(h, struct cyttsp5_core_data, es); + + call_atten_cb(cd, CY_ATTEN_SUSPEND, 0); +} + +static void cyttsp5_late_resume(struct early_suspend *h) +{ + struct cyttsp5_core_data *cd = + container_of(h, struct cyttsp5_core_data, es); + + call_atten_cb(cd, CY_ATTEN_RESUME, 0); +} + +static void cyttsp5_setup_early_suspend(struct cyttsp5_core_data *cd) +{ + cd->es.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + cd->es.suspend = cyttsp5_early_suspend; + cd->es.resume = cyttsp5_late_resume; + + register_early_suspend(&cd->es); +} +#elif defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct cyttsp5_core_data *cd = + container_of(self, struct cyttsp5_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) { + dev_info(cd->dev, "%s: UNBLANK!\n", __func__); + if (cd->fb_state != FB_ON) { + call_atten_cb(cd, CY_ATTEN_RESUME, 0); + cd->fb_state = FB_ON; + } + } else if (*blank == FB_BLANK_POWERDOWN) { + dev_info(cd->dev, "%s: POWERDOWN!\n", __func__); + if (cd->fb_state != FB_OFF) { + call_atten_cb(cd, CY_ATTEN_SUSPEND, 0); + cd->fb_state = FB_OFF; + } + } + +exit: + return 0; +} + +static void cyttsp5_setup_fb_notifier(struct cyttsp5_core_data *cd) +{ + int rc; + + cd->fb_state = FB_ON; + + cd->fb_notifier.notifier_call = fb_notifier_callback; + + rc = fb_register_client(&cd->fb_notifier); + if (rc) + dev_err(cd->dev, "Unable to register fb_notifier: %d\n", rc); +} +#endif + +static int cyttsp5_setup_irq_gpio(struct cyttsp5_core_data *cd) +{ + struct device *dev = cd->dev; + unsigned long irq_flags; + int rc; + + /* Initialize IRQ */ + cd->irq = gpio_to_irq(cd->cpdata->irq_gpio); + if (cd->irq < 0) + return -EINVAL; + + cd->irq_enabled = true; + + parade_debug(dev, DEBUG_LEVEL_1, "%s: initialize threaded irq=%d\n", + __func__, cd->irq); + if (cd->cpdata->level_irq_udelay > 0) + /* use level triggered interrupts */ + irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT; + else + /* use edge triggered interrupts */ + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + + rc = request_threaded_irq(cd->irq, NULL, cyttsp5_irq, irq_flags, + dev_name(dev), cd); + if (rc < 0) + dev_err(dev, "%s: Error, could not request irq\n", __func__); + + return rc; +} + +#ifdef CYTTSP5_SENSOR_EN +static int cyttsp5_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + return 0; +} + +static int cyttsp5_sensor_init(struct cyttsp5_core_data *data) +{ + struct cyttsp5_sensor_platform_data *sensor_pdata; + struct input_dev *sensor_input_dev; + int err; + + mutex_init(&data->state_mutex); + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + pr_err("%s: Failed to allocate device", __func__); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct cyttsp5_sensor_platform_data), + GFP_KERNEL); + if (!sensor_pdata) { + pr_err("%s: Failed to allocate memory", __func__); + goto free_sensor_pdata; + } + data->sensor_pdata = sensor_pdata; + + __set_bit(EV_KEY, sensor_input_dev->evbit); + __set_bit(KEY_F1, sensor_input_dev->keybit); + __set_bit(EV_SYN, sensor_input_dev->evbit); + + sensor_input_dev->name = "s-double-tap"; + data->sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + pr_err("%s: Unable to register device, err=%d", __func__, err); + goto free_sensor_input_dev; + } + + sensor_pdata->ps_cdev = sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = cyttsp5_sensor_set_enable; + sensor_pdata->data = data; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(data->sensor_pdata->input_sensor_dev); +free_sensor_input_dev: + input_free_device(data->sensor_pdata->input_sensor_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, sensor_pdata); + data->sensor_pdata = NULL; +exit: + return 1; +} + +int cyttsp5_sensor_remove(struct cyttsp5_core_data *data) +{ + sensors_classdev_unregister(&data->sensor_pdata->ps_cdev); + input_unregister_device(data->sensor_pdata->input_sensor_dev); + devm_kfree(&data->sensor_pdata->input_sensor_dev->dev, + data->sensor_pdata); + data->sensor_pdata = NULL; + data->should_enable_gesture = false; + return 0; +} +#endif + +int cyttsp5_probe(const struct cyttsp5_bus_ops *ops, struct device *dev, + u16 irq, size_t xfer_buf_size) +{ + struct cyttsp5_core_data *cd; + struct cyttsp5_platform_data *pdata = dev_get_platdata(dev); + enum cyttsp5_atten_type type; + int rc = 0; +#ifdef CYTTSP5_SENSOR_EN + static bool initialized_sensor; +#endif + + /* Set default values on first probe */ + if (cyttsp5_first_probe) { + cyttsp5_first_probe = false; + is_cyttsp5_probe_success = false; + cyttsp5_bus_ops_save = NULL; + } + + if (!pdata || !pdata->core_pdata || !pdata->mt_pdata) { + dev_err(dev, "%s: Missing platform data\n", __func__); + rc = -ENODEV; + goto error_no_pdata; + } + + if (pdata->core_pdata->flags & CY_CORE_FLAG_POWEROFF_ON_SLEEP) { + if (!pdata->core_pdata->power) { + dev_err(dev, "%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_alloc_data; + } + + /* Initialize device info */ + cd->dev = dev; + cd->pdata = pdata; + cd->cpdata = pdata->core_pdata; + cd->bus_ops = ops; + cd->debug_level = CY_INITIAL_DEBUG_LEVEL; + cd->watchdog_interval = CY_WATCHDOG_TIMEOUT; + cd->show_timestamp = CY_INITIAL_SHOW_TIME_STAMP; + scnprintf(cd->core_id, 20, "%s%d", CYTTSP5_CORE_NAME, core_number++); + memset(cd->pip2_us_file_path, 0, 128); + memcpy(cd->pip2_us_file_path, "/data/ttdl/pt_fw.bin", + sizeof("/data/ttdl/pt_fw.bin")); + + /* Initialize mutexes and spinlocks */ + mutex_init(&cd->module_list_lock); + mutex_init(&cd->system_lock); + mutex_init(&cd->adap_lock); + mutex_init(&cd->hid_report_lock); + spin_lock_init(&cd->spinlock); + + /* Initialize module list */ + INIT_LIST_HEAD(&cd->module_list); + + /* Initialize attention lists */ + for (type = 0; type < CY_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); + + /* Initialize works */ + INIT_WORK(&cd->startup_work, cyttsp5_startup_work_function); + INIT_WORK(&cd->ez_recovery_work, cyttsp5_ez_work_function); + INIT_WORK(&cd->watchdog_work, cyttsp5_watchdog_work); + + /* Initialize HID specific data */ + cd->hid_core.hid_vendor_id = (cd->cpdata->vendor_id) ? + cd->cpdata->vendor_id : CY_HID_VENDOR_ID; + cd->hid_core.hid_product_id = (cd->cpdata->product_id) ? + cd->cpdata->product_id : CY_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; + +#ifdef PT_PANEL_ID_FROM_MFG + /* Set Panel ID to default to 0 */ + cd->panel_id = PANEL_ID_DEFAULT; +#else + /* Set Panel ID to Not Enabled */ + cd->panel_id = PANEL_ID_NOT_ENABLED; +#endif + dev_set_drvdata(dev, cd); + cyttsp5_add_core(dev); + + /*create ttdl_restart sysfs node is probe failed*/ + if (!is_cyttsp5_probe_success) + device_create_file(dev, &dev_attr_ttdl_restart); + + /* + * Save the pointer to a global value, which will be used + * in ttdl_restart function + */ + cyttsp5_bus_ops_save = ops; + + /* Call platform init function */ + if (cd->cpdata->init) { + parade_debug(cd->dev, DEBUG_LEVEL_1, "%s: Init HW\n", __func__); + rc = cd->cpdata->init(cd->cpdata, 1, cd->dev); + } else { + dev_info(cd->dev, "%s: No HW INIT function\n", __func__); + rc = 0; + } + if (rc < 0) + dev_err(cd->dev, "%s: HW Init fail r=%d\n", __func__, rc); + else { + mutex_lock(&cd->system_lock); + cd->pm_mode = CYTTSP5_PM_ACTIVE; + mutex_unlock(&cd->system_lock); + } + + /* Call platform detect function */ + if (cd->cpdata->detect) { + dev_info(cd->dev, "%s: Detect HW\n", __func__); + rc = cd->cpdata->detect(cd->cpdata, cd->dev, + cyttsp5_platform_detect_read); + if (rc) { + dev_info(cd->dev, "%s: No HW detected\n", __func__); + rc = -ENODEV; + goto error_detect; + } + } + + /* Setup watchdog timer */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0) + timer_setup(&cd->watchdog_timer, cyttsp5_watchdog_timer, + 0); +#else + setup_timer(&cd->watchdog_timer, cyttsp5_watchdog_timer, + (unsigned long)cd); +#endif + + rc = cyttsp5_setup_irq_gpio(cd); + if (rc < 0) { + dev_err(dev, "%s: Error, could not setup IRQ\n", __func__); + goto error_setup_irq; + } + + parade_debug(dev, DEBUG_LEVEL_1, "%s: add sysfs interfaces\n", + __func__); + rc = add_sysfs_interfaces(dev); + if (rc < 0) { + dev_err(dev, "%s: Error, fail sysfs init\n", __func__); + goto error_attr_create; + } + +#ifdef TTHE_TUNER_SUPPORT + mutex_init(&cd->tthe_lock); + cd->tthe_debugfs = debugfs_create_file(CYTTSP5_TTHE_TUNER_FILE_NAME, + 0644, NULL, cd, &tthe_debugfs_fops); +#endif + rc = device_init_wakeup(dev, 1); + if (rc < 0) + dev_err(dev, "%s: Error, device_init_wakeup rc:%d\n", + __func__, rc); + + pm_runtime_get_noresume(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + /* + * call startup directly to ensure that the device + * is tested before leaving the probe + */ + parade_debug(dev, DEBUG_LEVEL_1, "%s: call startup\n", __func__); + rc = cyttsp5_startup(cd, false); + + pm_runtime_put_sync(dev); + + /* Do not fail probe if startup fails but the device is detected */ + if (rc == -ENODEV) { + dev_err(cd->dev, "%s: Fail initial startup r=%d\n", + __func__, rc); + goto error_startup; + } + + rc = cyttsp5_mt_probe(dev); + if (rc < 0) { + dev_err(dev, "%s: Error, fail mt probe\n", __func__); + goto error_startup; + } + + rc = cyttsp5_btn_probe(dev); + if (rc < 0) { + dev_err(dev, "%s: Error, fail btn probe\n", __func__); + goto error_startup_mt; + } + + rc = cyttsp5_proximity_probe(dev); + if (rc < 0) { + dev_err(dev, "%s: Error, fail proximity probe\n", __func__); + goto error_startup_btn; + } + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + dev_info(dev, "%s:cyttsp5_ts_mmi_dev_register",__func__); + rc = cyttsp5_ts_mmi_dev_register(dev); + if (rc) { + dev_info(dev, "Failed register touchscreen mmi."); + goto error_startup_btn; + } +#endif + + PM_WAKEUP_REGISTER(dev, cd->gesture_wakelock, + "cyttsp5_gesture_wakelock"); + if (!cd->gesture_wakelock) { + dev_info(dev, "%s: allocate gesture wakeup source err!\n", __func__); + rc = -ENOMEM; + goto err_register_gesture_wakelock; + } + + /* Probe registered modules */ + cyttsp5_probe_modules(cd); + +#if defined (CYTTSP5_SENSOR_EN) + if (!initialized_sensor) { + if (!cyttsp5_sensor_init(cd)) + initialized_sensor = true; + else + dev_err(dev, "%s: cyttsp5 init sensor fail.\n", __func__); + } +#endif + +#ifdef CONFIG_HAS_EARLYSUSPEND + cyttsp5_setup_early_suspend(cd); +#elif defined(CONFIG_FB) + cyttsp5_setup_fb_notifier(cd); +#endif + +#if NEED_SUSPEND_NOTIFIER + cd->pm_notifier.notifier_call = cyttsp5_pm_notifier; + register_pm_notifier(&cd->pm_notifier); +#endif + is_cyttsp5_probe_success = true; + return 0; + +err_register_gesture_wakelock: + PM_WAKEUP_UNREGISTER(cd->gesture_wakelock); +error_startup_btn: + cyttsp5_btn_release(dev); +error_startup_mt: + cyttsp5_mt_release(dev); +error_startup: + 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->startup_work); + cancel_work_sync(&cd->ez_recovery_work); + cyttsp5_stop_wd_timer(cd); + cyttsp5_free_si_ptrs(cd); + remove_sysfs_interfaces(dev); +error_attr_create: + free_irq(cd->irq, cd); + del_timer(&cd->watchdog_timer); +error_setup_irq: +error_detect: + if (cd->cpdata->init) + cd->cpdata->init(cd->cpdata, 0, dev); + cyttsp5_del_core(dev); + dev_set_drvdata(dev, NULL); + kfree(cd); +error_alloc_data: +error_no_pdata: + dev_err(dev, "%s failed.\n", __func__); + is_cyttsp5_probe_success = false; + return rc; +} +EXPORT_SYMBOL_GPL(cyttsp5_probe); + +int cyttsp5_release(struct cyttsp5_core_data *cd) +{ + struct device *dev = cd->dev; + + /* Release successfully probed modules */ + cyttsp5_release_modules(cd); + + cyttsp5_proximity_release(dev); + cyttsp5_btn_release(dev); + cyttsp5_mt_release(dev); + +#ifdef CONFIG_HAS_EARLYSUSPEND + unregister_early_suspend(&cd->es); +#elif defined(CONFIG_FB) + fb_unregister_client(&cd->fb_notifier); +#endif + +#if NEED_SUSPEND_NOTIFIER + unregister_pm_notifier(&cd->pm_notifier); +#endif + + /* + * 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); + + cancel_work_sync(&cd->startup_work); + cancel_work_sync(&cd->ez_recovery_work); + + cyttsp5_stop_wd_timer(cd); + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + dev_info(dev, "%s:cyttsp5_ts_mmi_dev_register", __func__); + cyttsp5_ts_mmi_dev_unregister(dev); +#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 + remove_sysfs_interfaces(dev); + free_irq(cd->irq, cd); + if (cd->cpdata->init) + cd->cpdata->init(cd->cpdata, 0, dev); + dev_set_drvdata(dev, NULL); + cyttsp5_del_core(dev); + cyttsp5_free_si_ptrs(cd); + cyttsp5_free_hid_reports(cd); + kfree(cd); + return 0; +} +EXPORT_SYMBOL_GPL(cyttsp5_release); + +MODULE_LICENSE("GPL"); +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Core Driver"); +MODULE_AUTHOR("Parade Technologies "); diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_core.h b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_core.h new file mode 100644 index 000000000000..9c9fe09dbc31 --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_core.h @@ -0,0 +1,189 @@ +/* + * cyttsp5_core.h + * Parade TrueTouch(TM) Standard Product V5 Core Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * CYTMA5XX + * CYTMA448 + * CYTMA445A + * CYTT21XXX + * CYTT31XXX + * + * Copyright (C) 2015 Parade Semiconductor + * Copyright (C) 2012-2015 Cypress Semiconductor + * + * 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 Semiconductor at www.parade.com + * + */ + +#ifndef _LINUX_CYTTSP5_CORE_H +#define _LINUX_CYTTSP5_CORE_H + +#include + +#define CYTTSP5_I2C_NAME "cyttsp5_i2c_adapter" +#define CYTTSP5_SPI_NAME "cyttsp5_spi_adapter" + +#define CYTTSP5_CORE_NAME "cyttsp5_core" +#define CYTTSP5_MT_NAME "cyttsp5_mt" +#define CYTTSP5_BTN_NAME "cyttsp5_btn" +#define CYTTSP5_PROXIMITY_NAME "cyttsp5_proximity" + +#define CY_DRIVER_NAME TTDA +#define CY_DRIVER_MAJOR 03 +#define CY_DRIVER_MINOR 08 + +#define CY_DRIVER_REVCTRL 874312 + +#define CY_DRIVER_VERSION \ +__stringify(CY_DRIVER_NAME) \ +"." __stringify(CY_DRIVER_MAJOR) \ +"." __stringify(CY_DRIVER_MINOR) \ +"." __stringify(CY_DRIVER_REVCTRL) + +#define CY_DRIVER_DATE "20170125" + +/* abs settings */ +#define CY_IGNORE_VALUE -1 + +#define CYTTSP5_MAX_STR_LABLE_LEN 64 + +#define CYTTSP5_FIRMWARE_NAME_MAX_LEN 64 + +enum cyttsp5_core_platform_flags { + CY_CORE_FLAG_NONE, + CY_CORE_FLAG_POWEROFF_ON_SLEEP = 0x02, + CY_CORE_FLAG_RESTORE_PARAMETERS = 0x04, +}; + +enum cyttsp5_core_platform_easy_wakeup_gesture { + CY_CORE_EWG_NONE, + CY_CORE_EWG_TAP_TAP, + CY_CORE_EWG_TWO_FINGER_SLIDE, + CY_CORE_EWG_RESERVED, + CY_CORE_EWG_WAKE_ON_INT_FROM_HOST = 0xFF, +}; + +enum cyttsp5_loader_platform_flags { + CY_LOADER_FLAG_NONE, + CY_LOADER_FLAG_CALIBRATE_AFTER_FW_UPGRADE, + /* Use CONFIG_VER field in TT_CFG to decide TT_CFG update */ + CY_LOADER_FLAG_CHECK_TTCONFIG_VERSION, + CY_LOADER_FLAG_CALIBRATE_AFTER_TTCONFIG_UPGRADE, +}; + +struct touch_settings { + const uint8_t *data; + uint32_t size; + uint8_t tag; +}; + +struct cyttsp5_touch_firmware { + const uint8_t *img; + uint32_t size; + const uint8_t *ver; + uint8_t vsize; + uint8_t panel_id; +}; + +struct cyttsp5_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 cyttsp5_loader_platform_data { + struct cyttsp5_touch_firmware *fw; + struct cyttsp5_touch_config *ttconfig; + struct cyttsp5_touch_firmware **fws; + struct cyttsp5_touch_config **ttconfigs; + u32 flags; +}; + +typedef int (*cyttsp5_platform_read) (struct device *dev, void *buf, int size); + +#define CY_TOUCH_SETTINGS_MAX 32 + +struct cyttsp5_core_platform_data { + int irq_gpio; + int rst_gpio; + int level_irq_udelay; + u16 hid_desc_register; + u16 vendor_id; + u16 product_id; + const char *class_entry_name; + char avdd_name[CYTTSP5_MAX_STR_LABLE_LEN]; + char iovdd_name[CYTTSP5_MAX_STR_LABLE_LEN]; + struct regulator *avdd; + struct regulator *iovdd; + + int (*xres)(struct cyttsp5_core_platform_data *pdata, + struct device *dev); + int (*init)(struct cyttsp5_core_platform_data *pdata, + int on, struct device *dev); + int (*power)(struct cyttsp5_core_platform_data *pdata, + int on, struct device *dev, atomic_t *ignore_irq); + int (*detect)(struct cyttsp5_core_platform_data *pdata, + struct device *dev, cyttsp5_platform_read read); + int (*irq_stat)(struct cyttsp5_core_platform_data *pdata, + struct device *dev); + struct touch_settings *sett[CY_TOUCH_SETTINGS_MAX]; + u32 flags; + u8 easy_wakeup_gesture; +}; + +struct touch_framework { + const int16_t *abs; + uint8_t size; + uint8_t enable_vkeys; +} __packed; + +enum cyttsp5_mt_platform_flags { + CY_MT_FLAG_NONE, + CY_MT_FLAG_HOVER = 0x04, + CY_MT_FLAG_FLIP = 0x08, + CY_MT_FLAG_INV_X = 0x10, + CY_MT_FLAG_INV_Y = 0x20, + CY_MT_FLAG_VKEYS = 0x40, + CY_MT_FLAG_NO_TOUCH_ON_LO = 0x80, +}; + +struct cyttsp5_mt_platform_data { + struct touch_framework *frmwrk; + unsigned short flags; + char const *inp_dev_name; + int vkeys_x; + int vkeys_y; +}; + +struct cyttsp5_btn_platform_data { + char const *inp_dev_name; +}; + +struct cyttsp5_proximity_platform_data { + struct touch_framework *frmwrk; + char const *inp_dev_name; +}; + +struct cyttsp5_platform_data { + char ic_name[CYTTSP5_MAX_STR_LABLE_LEN]; + struct cyttsp5_core_platform_data *core_pdata; + struct cyttsp5_mt_platform_data *mt_pdata; + struct cyttsp5_btn_platform_data *btn_pdata; + struct cyttsp5_proximity_platform_data *prox_pdata; + struct cyttsp5_loader_platform_data *loader_pdata; +}; + +#endif /* _LINUX_CYTTSP5_CORE_H */ diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_debug.c b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_debug.c new file mode 100644 index 000000000000..f4ebba784936 --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_debug.c @@ -0,0 +1,393 @@ +/* + * cyttsp5_debug.c + * Parade TrueTouch(TM) Standard Product V5 Debug Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * CYTMA5XX + * CYTMA448 + * CYTMA445A + * CYTT21XXX + * CYTT31XXX + * + * Copyright (C) 2015 Parade Technologies + * Copyright (C) 2012-2015 Cypress Semiconductor + * + * 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 + * + */ + +#include "cyttsp5_regs.h" + +#define CYTTSP5_DEBUG_NAME "cyttsp5_debug" + +struct cyttsp5_debug_data { + struct device *dev; + struct cyttsp5_sysinfo *si; + uint32_t interrupt_count; + uint32_t formated_output; + struct mutex sysfs_lock; + u8 pr_buf[CY_MAX_PRBUF_SIZE]; +}; + +static struct cyttsp5_core_commands *cmd; + +static struct cyttsp5_module debug_module; + +static inline struct cyttsp5_debug_data *cyttsp5_get_debug_data( + struct device *dev) +{ + return cyttsp5_get_module_data(dev, &debug_module); +} + +/* + * This function provide output of combined xy_mode and xy_data. + * Required by TTHE. + */ +static void cyttsp5_pr_buf_op_mode(struct cyttsp5_debug_data *dd, u8 *pr_buf, + struct cyttsp5_sysinfo *si, u8 cur_touch) +{ + const char fmt[] = "%02X "; + int max = (CY_MAX_PRBUF_SIZE - 1) - sizeof(CY_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, CY_MAX_PRBUF_SIZE, fmt, si->xy_mode[i]); + + for (i = 0; i < report_size && i < max; i++, k += 3) + scnprintf(pr_buf + k, CY_MAX_PRBUF_SIZE, fmt, si->xy_data[i]); + + pr_info("%s=%s%s\n", "cyttsp5_OpModeData", pr_buf, + total_size <= max ? "" : CY_PR_TRUNCATED); +} + +static void cyttsp5_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 = (CY_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, CY_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 ? "" : CY_PR_TRUNCATED); + else + pr_info("%s[]\n", data_name); +} + +static void cyttsp5_debug_formated(struct device *dev, u8 *pr_buf, + struct cyttsp5_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 */ + cyttsp5_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) { + cyttsp5_debug_print(dev, pr_buf, si->xy_data + i, + max_print_length, " "); + } + if (report_size - i) + cyttsp5_debug_print(dev, pr_buf, si->xy_data + i, + report_size - i, " "); + } else { + cyttsp5_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); + cyttsp5_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) + cyttsp5_debug_print(dev, pr_buf, si->xy_data, report_size, + "button"); +} + +/* read xy_data for all touches for debug */ +static int cyttsp5_xy_worker(struct cyttsp5_debug_data *dd) +{ + struct device *dev = dd->dev; + struct cyttsp5_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) + cyttsp5_debug_formated(dev, dd->pr_buf, si, num_cur_tch); + else + /* print data for TTHE */ + cyttsp5_pr_buf_op_mode(dd, dd->pr_buf, si, num_cur_tch); + + pr_info("\n"); + + return 0; +} + +static int cyttsp5_debug_attention(struct device *dev) +{ + struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev); + struct cyttsp5_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 = cyttsp5_xy_worker(dd); + if (rc < 0) + dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc); + + return rc; +} + +static ssize_t cyttsp5_interrupt_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev); + int val; + + mutex_lock(&dd->sysfs_lock); + val = dd->interrupt_count; + mutex_unlock(&dd->sysfs_lock); + + return scnprintf(buf, CY_MAX_PRBUF_SIZE, "Interrupt Count: %d\n", val); +} + +static ssize_t cyttsp5_interrupt_count_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct cyttsp5_debug_data *dd = cyttsp5_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, S_IRUSR | S_IWUSR, + cyttsp5_interrupt_count_show, cyttsp5_interrupt_count_store); + +static ssize_t cyttsp5_formated_output_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev); + int val; + + mutex_lock(&dd->sysfs_lock); + val = dd->formated_output; + mutex_unlock(&dd->sysfs_lock); + + return scnprintf(buf, CY_MAX_PRBUF_SIZE, + "Formated debug output: %x\n", val); +} + +static ssize_t cyttsp5_formated_output_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct cyttsp5_debug_data *dd = cyttsp5_get_debug_data(dev); + unsigned long value; + int rc; + + rc = kstrtoul(buf, 10, &value); + if (rc < 0) { + dev_err(dev, "%s: Invalid value\n", __func__); + return size; + } + + /* Expecting only 0 or 1 */ + if (value != 0 && value != 1) { + dev_err(dev, "%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, S_IRUSR | S_IWUSR, + cyttsp5_formated_output_show, cyttsp5_formated_output_store); + +static int cyttsp5_debug_probe(struct device *dev, void **data) +{ + struct cyttsp5_debug_data *dd; + int rc; + + /* get context and debug print buffers */ + dd = kzalloc(sizeof(*dd), GFP_KERNEL); + if (!dd) { + rc = -ENOMEM; + goto cyttsp5_debug_probe_alloc_failed; + } + + rc = device_create_file(dev, &dev_attr_int_count); + if (rc) { + dev_err(dev, "%s: Error, could not create int_count\n", + __func__); + goto cyttsp5_debug_probe_create_int_count_failed; + } + + rc = device_create_file(dev, &dev_attr_formated_output); + if (rc) { + dev_err(dev, "%s: Error, could not create formated_output\n", + __func__); + goto cyttsp5_debug_probe_create_formated_failed; + } + + mutex_init(&dd->sysfs_lock); + dd->dev = dev; + *data = dd; + + dd->si = cmd->request_sysinfo(dev); + if (!dd->si) { + dev_err(dev, "%s: Fail get sysinfo pointer from core\n", + __func__); + rc = -ENODEV; + goto cyttsp5_debug_probe_sysinfo_failed; + } + + rc = cmd->subscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_DEBUG_NAME, + cyttsp5_debug_attention, CY_MODE_OPERATIONAL); + if (rc < 0) { + dev_err(dev, "%s: Error, could not subscribe attention cb\n", + __func__); + goto cyttsp5_debug_probe_subscribe_failed; + } + + return 0; + +cyttsp5_debug_probe_subscribe_failed: +cyttsp5_debug_probe_sysinfo_failed: + device_remove_file(dev, &dev_attr_formated_output); +cyttsp5_debug_probe_create_formated_failed: + device_remove_file(dev, &dev_attr_int_count); +cyttsp5_debug_probe_create_int_count_failed: + kfree(dd); +cyttsp5_debug_probe_alloc_failed: + dev_err(dev, "%s failed.\n", __func__); + return rc; +} + +static void cyttsp5_debug_release(struct device *dev, void *data) +{ + struct cyttsp5_debug_data *dd = data; + int rc; + + rc = cmd->unsubscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_DEBUG_NAME, + cyttsp5_debug_attention, CY_MODE_OPERATIONAL); + if (rc < 0) { + dev_err(dev, "%s: Error, could not un-subscribe attention\n", + __func__); + goto cyttsp5_debug_release_exit; + } + +cyttsp5_debug_release_exit: + device_remove_file(dev, &dev_attr_formated_output); + device_remove_file(dev, &dev_attr_int_count); + kfree(dd); +} + +static struct cyttsp5_module debug_module = { + .name = CYTTSP5_DEBUG_NAME, + .probe = cyttsp5_debug_probe, + .release = cyttsp5_debug_release, +}; + +static int __init cyttsp5_debug_init(void) +{ + int rc; + + cmd = cyttsp5_get_commands(); + if (!cmd) + return -EINVAL; + + rc = cyttsp5_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__, CY_DRIVER_VERSION, rc); + return 0; +} +module_init(cyttsp5_debug_init); + +static void __exit cyttsp5_debug_exit(void) +{ + cyttsp5_unregister_module(&debug_module); +} +module_exit(cyttsp5_debug_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Debug Driver"); +MODULE_AUTHOR("Parade Technologies "); diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_device_access-api.h b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_device_access-api.h new file mode 100644 index 000000000000..c071e522550d --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_device_access-api.h @@ -0,0 +1,44 @@ +/* + * cyttsp5_device_access-api.h + * Parade TrueTouch(TM) Standard Product V5 Device Access API module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * CYTMA5XX + * CYTMA448 + * CYTMA445A + * CYTT21XXX + * CYTT31XXX + * + * Copyright (C) 2015 Parade Semiconductor + * Copyright (C) 2012-2015 Cypress Semiconductor + * + * 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 Semiconductor at www.parade.com + * + */ + +#ifndef _LINUX_CYTTSP5_DEVICE_ACCESS_API_H +#define _LINUX_CYTTSP5_DEVICE_ACCESS_API_H + +#include +#include + +int cyttsp5_device_access_user_command(const char *core_name, u16 read_len, + u8 *read_buf, u16 write_len, u8 *write_buf, + u16 *actual_read_len); + +int cyttsp5_device_access_user_command_async(const char *core_name, + u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf, + void (*cont)(const char *core_name, u16 read_len, u8 *read_buf, + u16 write_len, u8 *write_buf, u16 actual_read_length, + int rc)); +#endif /* _LINUX_CYTTSP5_DEVICE_ACCESS_API_H */ diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_device_access.c b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_device_access.c new file mode 100644 index 000000000000..c01134de7e10 --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_device_access.c @@ -0,0 +1,5467 @@ +/* + * cyttsp5_device_access.c + * Parade TrueTouch(TM) Standard Product V5 Device Access Module. + * Configuration and Test command/status user interface. + * For use with Parade touchscreen controllers. + * Supported parts include: + * CYTMA5XX + * CYTMA448 + * CYTMA445A + * CYTT21XXX + * CYTT31XXX + * + * Copyright (C) 2015 Parade Technologies + * Copyright (C) 2012-2015 Cypress Semiconductor + * + * 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 + * + */ + +#include "cyttsp5_regs.h" +#include + +#include +#include +#include + +#define CY_CMCP_THRESHOLD_FILE_NAME "cyttsp5_thresholdfile.csv" +#define CMCP_THRESHOLD_FILE_NAME "ttdl_cmcp_thresholdfile.csv" +#define PARADE_TEST_FILE_NAME "parade" + +/* 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 (HID_SYSINFO_MAX_BTN) +#define MAX_SENSORS (1024) +#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 (50000) + +/* 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_sensor_rx; + u32 max_sensor_rx; + u32 min_sensor_tx; + u32 max_sensor_tx; + u32 min_button; + u32 max_button; + u32 max_delta_sensor; + u32 cp_max_delta_sensor_rx_percent; + u32 cp_max_delta_sensor_tx_percent; + u32 cm_min_max_table_button[TABLE_BUTTON_MAX_SIZE]; + u32 cp_min_max_table_button[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_button_size; + u32 cp_min_max_table_button_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 CYTTSP5_DEVICE_ACCESS_NAME "cyttsp5_device_access" +#define CYTTSP5_INPUT_ELEM_SZ (sizeof("0xHH") + 1) + +#define STATUS_SUCCESS 0 +#define STATUS_FAIL -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)) + +#define CY_MAX_CONFIG_BYTES 256 +#define CYTTSP5_TTHE_TUNER_GET_PANEL_DATA_FILE_NAME "get_panel_data" +#define TTHE_TUNER_MAX_BUF (CY_MAX_PRBUF_SIZE * 3) + +struct cyttsp5_device_access_data { + struct device *dev; + struct cyttsp5_sysinfo *si; + struct mutex sysfs_lock; + u8 status; + u16 response_length; + bool sysfs_nodes_created; + struct kobject mfg_test; + u8 panel_scan_data_id; + u8 get_idac_data_id; + u8 calibrate_sensing_mode; + u8 calibrate_initialize_baselines; + u8 baseline_sensing_mode; +#ifdef TTHE_TUNER_SUPPORT + struct heatmap_param heatmap; + struct dentry *tthe_get_panel_data_debugfs; + struct mutex debugfs_lock; + u8 tthe_get_panel_data_buf[TTHE_TUNER_MAX_BUF]; + 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[CY_MAX_PRBUF_SIZE]; + u8 response_buf[CY_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; + struct completion builtin_cmcp_threshold_complete; + 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 sensor_assignment; + int32_t config_ver; + int32_t revision_ctrl; + int32_t device_id_high; + int32_t device_id_low; + bool cm_test_run; + bool cp_test_run; + /* 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; + uint8_t *cm_open_pwc; +}; + +static struct cyttsp5_core_commands *cmd; + +static struct cyttsp5_module device_access_module; + +static ssize_t cyttsp5_run_and_get_selftest_result_noprint(struct device *dev, + char *buf, size_t buf_len, u8 test_id, u16 read_length, + bool get_result_on_pass); + +static int _cyttsp5_calibrate_idacs_cmd(struct device *dev, + u8 sensing_mode, u8 *status); + +static inline struct cyttsp5_device_access_data *cyttsp5_get_device_access_data( + struct device *dev) +{ + return cyttsp5_get_module_data(dev, &device_access_module); +} + +static ssize_t cyttsp5_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_device_access_data *dad + = cyttsp5_get_device_access_data(dev); + u8 val; + + mutex_lock(&dad->sysfs_lock); + val = dad->status; + mutex_unlock(&dad->sysfs_lock); + + return scnprintf(buf, CY_MAX_PRBUF_SIZE, "%d\n", val); +} + +static DEVICE_ATTR(status, S_IRUSR, cyttsp5_status_show, NULL); + +static ssize_t cyttsp5_response_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_device_access_data *dad + = cyttsp5_get_device_access_data(dev); + int i; + ssize_t num_read; + int index; + + mutex_lock(&dad->sysfs_lock); + index = scnprintf(buf, CY_MAX_PRBUF_SIZE, + "Status %d\n", dad->status); + if (!dad->status) + goto error; + + num_read = dad->response_length; + + for (i = 0; i < num_read; i++) + index += scnprintf(buf + index, CY_MAX_PRBUF_SIZE - index, + "0x%02X\n", dad->response_buf[i]); + + index += scnprintf(buf + index, CY_MAX_PRBUF_SIZE - index, + "(%zd bytes)\n", num_read); + +error: + mutex_unlock(&dad->sysfs_lock); + return index; +} + +static DEVICE_ATTR(response, S_IRUSR, cyttsp5_response_show, NULL); + +/* + * Gets user input from sysfs and parse it + * return size of parsed output buffer + */ +static int cyttsp5_ic_parse_input(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[CYTTSP5_INPUT_ELEM_SZ]; + u32 i = 0; + u32 j; + int last = 0; + int ret; + + parade_debug(dev, DEBUG_LEVEL_1, + "%s: pbuf=%p buf=%p size=%zu %s=%zu buf=%s\n", + __func__, pbuf, buf, buf_size, "scan buf size", + CYTTSP5_INPUT_ELEM_SZ, buf); + + while (pbuf <= (buf + buf_size)) { + if (i >= CY_MAX_CONFIG_BYTES) { + dev_err(dev, "%s: %s size=%d max=%d\n", __func__, + "Max cmd size exceeded", i, + CY_MAX_CONFIG_BYTES); + return -EINVAL; + } + if (i >= ic_buf_size) { + dev_err(dev, "%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, CYTTSP5_INPUT_ELEM_SZ); + if ((last == ',') && (*pbuf == ',')) { + dev_err(dev, "%s: %s \",,\" not allowed.\n", __func__, + "Invalid data format."); + return -EINVAL; + } + for (j = 0; j < (CYTTSP5_INPUT_ELEM_SZ - 1) + && (pbuf < (buf + buf_size)) + && (*pbuf != ' ') + && (*pbuf != ','); j++) { + last = *pbuf; + scan_buf[j] = *pbuf++; + } + + ret = kstrtoul(scan_buf, 16, &value); + if (ret < 0) { + dev_err(dev, "%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; + i++; + } + + return i; +} + +static ssize_t cyttsp5_command_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct cyttsp5_device_access_data *dad + = cyttsp5_get_device_access_data(dev); + ssize_t length; + int rc; + + mutex_lock(&dad->sysfs_lock); + dad->status = 0; + dad->response_length = 0; + length = cyttsp5_ic_parse_input(dev, buf, size, dad->ic_buf, + CY_MAX_PRBUF_SIZE); + if (length <= 0) { + dev_err(dev, "%s: %s Group Data store\n", __func__, + "Malformed input for"); + goto exit; + } + + /* write ic_buf to log */ + cyttsp5_pr_buf(dev, dad->ic_buf, length, "ic_buf"); + + pm_runtime_get_sync(dev); + rc = cmd->nonhid_cmd->user_cmd(dev, 1, CY_MAX_PRBUF_SIZE, + dad->response_buf, length, dad->ic_buf, + &dad->response_length); + pm_runtime_put(dev); + if (rc) { + dad->response_length = 0; + dev_err(dev, "%s: Failed to store command\n", __func__); + } else { + dad->status = 1; + } + +exit: + mutex_unlock(&dad->sysfs_lock); + parade_debug(dev, DEBUG_LEVEL_2, "%s: return size=%zu\n", + __func__, size); + return size; +} + +static DEVICE_ATTR(command, S_IWUSR, NULL, cyttsp5_command_store); + +static int cmcp_check_config_fw_match(struct device *dev, + struct configuration *configuration) +{ + struct cyttsp5_device_access_data *dad + = cyttsp5_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) { + dev_err(dev, "%s: TX number mismatch,%d,%d!\n", __func__, + tx_num,dad->si->sensing_conf_data.tx_num); + ret = -EINVAL; + } + + if (rx_num != dad->si->sensing_conf_data.rx_num) { + dev_err(dev, "%s: RX number mismatch,%d,%d!\n", __func__, + rx_num,dad->si->sensing_conf_data.rx_num); + ret = -EINVAL; + } + + if (button_num != dad->si->num_btns) { + dev_err(dev, "%s: Button number mismatch!\n", __func__); + ret = -EINVAL; + } + + return ret; +} + +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; + + parade_debug(dev, DEBUG_LEVEL_2, "%s: start\n", __func__); + + if ((test_item & CM_PANEL) == CM_PANEL) { + parade_debug(dev, DEBUG_LEVEL_2, "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)) { + dev_err(dev, "%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]){ + dev_err(dev, + "%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]) { + dev_err(dev, + "%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) { + dev_err(dev, + "%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) { + dev_err(dev, + "%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) { + dev_err(dev, "%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) { + dev_err(dev, + "%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_button[i * 2]; + int32_t cm_button_max = + configuration->cm_min_max_table_button[i * 2 + 1]; + if ((cm_button_data[i] <= cm_button_min) + || (cm_button_data[i] >= cm_button_max)) { + dev_err(dev, + "%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) { + dev_err(dev, + "%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; +} +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; + + parade_debug(dev, DEBUG_LEVEL_2, "%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) { + dev_err(dev, + "%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) { + dev_err(dev, + "%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)) { + dev_err(dev, + "%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)) { + dev_err(dev, + "%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) { + dev_err(dev, + "%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) { + dev_err(dev, + "%s: Button Cp average fails min/max test\n", + __func__); + dev_err(dev, + "%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_button[i * 2]; + int32_t cp_button_max = + configuration->cp_min_max_table_button[i * 2 + 1]; + if ((cmcp_info->cp_btn_data[i] <= cp_button_min) + || (cmcp_info->cp_btn_data[i] >= cp_button_max)) { + dev_err(dev, + "%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; +} + +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; + } + } +} + +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; + } + } +} + +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; +} + +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); +} + +static int cyttsp5_get_cmcp_info(struct cyttsp5_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 cm_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 cm_delta_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; + parade_debug(dev, DEBUG_LEVEL_2, "%s tx_num=%d", __func__, tx_num); + parade_debug(dev, DEBUG_LEVEL_2, "%s rx_num=%d", __func__, rx_num); + parade_debug(dev, DEBUG_LEVEL_2, "%s btn_num=%d", __func__, btn_num); + + /*short test*/ + result->short_test_pass = true; + rc = cyttsp5_run_and_get_selftest_result_noprint( + dev, tmp_buf, sizeof(tmp_buf), + CY_ST_ID_AUTOSHORTS, PIP_CMD_MAX_LENGTH, false); + if (rc) { + dev_err(dev, "short test not supported"); + goto exit; + } + if (dad->ic_buf[1] != 0) + result->short_test_pass = false; + + /*Get cm_panel data*/ + rc = cyttsp5_run_and_get_selftest_result_noprint( + dev, tmp_buf, sizeof(tmp_buf), + CY_ST_ID_CM_PANEL, PIP_CMD_MAX_LENGTH, true); + if (rc) { + dev_err(dev, "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]); + parade_debug(dev, DEBUG_LEVEL_2, + "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++) { + parade_debug(dev, DEBUG_LEVEL_2, + "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++) { + parade_debug(dev, DEBUG_LEVEL_2, + "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 = cyttsp5_run_and_get_selftest_result_noprint( + dev, tmp_buf, sizeof(tmp_buf), + CY_ST_ID_CP_PANEL, PIP_CMD_MAX_LENGTH, true); + if (rc) { + dev_err(dev, "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]); + parade_debug(dev, DEBUG_LEVEL_2, + "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]); + parade_debug(dev, DEBUG_LEVEL_2, " 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]); + parade_debug(dev, DEBUG_LEVEL_2, + "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]); + parade_debug(dev, DEBUG_LEVEL_2, + "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) + goto skip_button_test; + + /*get cm btn data*/ + rc = cyttsp5_run_and_get_selftest_result_noprint( + dev, tmp_buf, sizeof(tmp_buf), + CY_ST_ID_CM_BUTTON, PIP_CMD_MAX_LENGTH, true); + if (rc) { + dev_err(dev, "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]); + parade_debug(dev, DEBUG_LEVEL_2, + " 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 = cyttsp5_run_and_get_selftest_result_noprint( + dev, tmp_buf, sizeof(tmp_buf), + CY_ST_ID_CP_BUTTON, PIP_CMD_MAX_LENGTH, true); + if (rc) { + dev_err(dev, "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]; + parade_debug(dev, DEBUG_LEVEL_2, + "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); + parade_debug(dev, DEBUG_LEVEL_2, " cp_btn_cal=%d\n", + cp_btn_cal); + parade_debug(dev, DEBUG_LEVEL_2, " cp_btn_ave=%d\n", + cp_btn_ave); + } +skip_button_test: +exit: + return rc; +} + +static int cyttsp5_get_cm_cal(struct cyttsp5_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 = cyttsp5_run_and_get_selftest_result_noprint( + dev, tmp_buf, sizeof(tmp_buf), + CY_ST_ID_CM_PANEL, PIP_CMD_MAX_LENGTH, true); + if (rc) { + dev_err(dev, "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 increate 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) + goto skip_button_test; + + /*get cm_btn_cal data*/ + rc = cyttsp5_run_and_get_selftest_result_noprint( + dev, tmp_buf, sizeof(tmp_buf), + CY_ST_ID_CM_BUTTON, PIP_CMD_MAX_LENGTH, true); + if (rc) { + dev_err(dev, "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); + parade_debug(dev, DEBUG_LEVEL_2, " cm_btn_cal=%d\n", + cmcp_info->cm_cal_data_btn); + } + +skip_button_test: +exit: + return rc; +} + +static void cyttsp5_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); +} + +static int cyttsp5_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; +} + +#define PT_PARAM_FORCE_SINGLE_TX 0x1F +#define PT_PARAM_ACT_LFT_EN 0x1A +#define PT_PARAM_BL_H2O_RJCT 0x05 +#define PT_PARAM_LOW_POWER_ENABLE 0x06 + +static ssize_t cyttsp5_cmcp_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_device_access_data *dad + = cyttsp5_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; + u8 status; + int self_test_id_supported = 0; + u32 ram_reg_lft; + u32 ram_reg_h2o; + u32 ram_reg_lpwg; + + 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 = cyttsp5_cmcp_get_test_item(dad->cmcp_test_items); + + if (dad->builtin_cmcp_threshold_status < 0) { + dev_err(dev, "%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) { + parade_debug(dev, DEBUG_LEVEL_2, + "%s: Invalid test item! Should be 0~4!\n", __func__); + mutex_unlock(&dad->sysfs_lock); + goto invalid_item; + } + + parade_debug(dev, DEBUG_LEVEL_2, "%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))) { + parade_debug(dev, DEBUG_LEVEL_2, + "%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: + parade_debug(dev, DEBUG_LEVEL_2, "%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) + dev_err(dev, "stop watchdog failed"); + /*force single tx*/ + rc = cmd->nonhid_cmd->set_param(dev, 0, 0x1F, 1, 1); + if (rc) + dev_err(dev, "force single tx failed"); + + /* Save self CP Param */ + rc = cmd->nonhid_cmd->get_param(dev, 0, PT_PARAM_ACT_LFT_EN, &ram_reg_lft); + if (rc) + dev_err(dev, "Save ram_reg_lft failed"); + rc = cmd->nonhid_cmd->get_param(dev, 0, PT_PARAM_BL_H2O_RJCT, &ram_reg_h2o); + if (rc) + dev_err(dev, "Save ram_reg_h2o failed"); + rc = cmd->nonhid_cmd->get_param(dev, 0, PT_PARAM_LOW_POWER_ENABLE, &ram_reg_lpwg); + if (rc) + dev_err(dev, "Save ram_reg_lpwg failed"); + + /* Disable self CP scanning */ + rc = cmd->nonhid_cmd->set_param(dev, 0, PT_PARAM_ACT_LFT_EN, 0, 1); + if (rc) + dev_err(dev, "Disable ram_reg_lft failed"); + rc = cmd->nonhid_cmd->set_param(dev, 0, PT_PARAM_BL_H2O_RJCT, 0, 1); + if (rc) + dev_err(dev, "Disable ram_reg_h2o failed"); + rc = cmd->nonhid_cmd->set_param(dev, 0, PT_PARAM_LOW_POWER_ENABLE, 0, 1); + if (rc) + dev_err(dev, "Disable ram_reg_lpwg failed"); + + /*suspend_scanning */ + rc = cmd->nonhid_cmd->suspend_scanning(dev, 0); + if (rc) + dev_err(dev, "suspend_scanning failed"); + /*do calibration*/ + if (!dad->cmcp_force_calibrate) { + parade_debug(dev, DEBUG_LEVEL_2, "do calibration in single tx mode"); + rc = _cyttsp5_calibrate_idacs_cmd(dev, 0, &status); + if (rc < 0) { + dev_err(dev, "%s: Error on calibrate idacs for mutual r=%d\n", + __func__, rc); + } + rc = _cyttsp5_calibrate_idacs_cmd(dev, 1, &status); + if (rc < 0) { + dev_err(dev, "%s: Error on calibrate idacs for buttons r=%d\n", + __func__, rc); + } + rc = _cyttsp5_calibrate_idacs_cmd(dev, 2, &status); + if (rc < 0) { + dev_err(dev, "%s: Error on calibrate idacs for self r=%d\n", + __func__, rc); + } + } + /*resume_scanning */ + rc = cmd->nonhid_cmd->resume_scanning(dev, 0); + if (rc) + dev_err(dev, "resume_scanning failed"); + + /* Delay 300ms to for IIR filter */ + msleep(300); + + /*get all cmcp data from FW*/ + self_test_id_supported = + cyttsp5_get_cmcp_info(dad, cmcp_info); + if (self_test_id_supported) + dev_err(dev, "cyttsp5_get_cmcp_info failed"); + + /*restore to multi tx*/ + rc = cmd->nonhid_cmd->set_param(dev, 0, 0x1F, 0, 1); + if (rc) + dev_err(dev, "restore multi tx failed"); + + /*suspend_scanning */ + rc = cmd->nonhid_cmd->suspend_scanning(dev, 0); + if (rc) + dev_err(dev, "suspend_scanning failed"); + /*do calibration*/ + if (!dad->cmcp_force_calibrate) { + parade_debug(dev, DEBUG_LEVEL_2, "do calibration in multi tx mode"); + rc = _cyttsp5_calibrate_idacs_cmd(dev, 0, &status); + if (rc < 0) { + dev_err(dev, "%s: Error on calibrate idacs for mutual r=%d\n", + __func__, rc); + } + rc = _cyttsp5_calibrate_idacs_cmd(dev, 1, &status); + if (rc < 0) { + dev_err(dev, "%s: Error on calibrate idacs for buttons r=%d\n", + __func__, rc); + } + rc = _cyttsp5_calibrate_idacs_cmd(dev, 2, &status); + if (rc < 0) { + dev_err(dev, "%s: Error on calibrate idacs for self r=%d\n", + __func__, rc); + } + + } + /*resume_scanning */ + rc = cmd->nonhid_cmd->resume_scanning(dev, 0); + if (rc) + dev_err(dev, "resume_scanning failed"); + + /* Delay 300ms to for IIR filter */ + msleep(300); + + self_test_id_supported = + cyttsp5_get_cm_cal(dad, cmcp_info); + if (self_test_id_supported) + dev_err(dev, "cyttsp5_get_cmcp_info failed"); + + /* Restore Param */ + rc = cmd->nonhid_cmd->set_param(dev, 0, PT_PARAM_ACT_LFT_EN, ram_reg_lft, 1); + if (rc) + dev_err(dev, "Restore ram_reg_lft failed"); + rc = cmd->nonhid_cmd->set_param(dev, 0, PT_PARAM_BL_H2O_RJCT, ram_reg_h2o, 1); + if (rc) + dev_err(dev, "Restore ram_reg_h2o failed"); + rc = cmd->nonhid_cmd->set_param(dev, 0, PT_PARAM_LOW_POWER_ENABLE, ram_reg_lpwg, 1); + if (rc) + dev_err(dev, "Restore ram_reg_lpwg failed"); + + /*start watchdog*/ + rc = cmd->request_start_wd(dev); + if (rc) + dev_err(dev, "start watchdog failed"); + + if (self_test_id_supported) + goto self_test_id_failed; + + if (no_builtin_file) + goto no_builtin; + + if (test_item) + validate_cm_test_results(dev, configuration, cmcp_info, + result, &final_pass, test_item); + + if (test_item) + 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; + } + mutex_lock(&dad->sysfs_lock); + dad->test_executed = 1; + mutex_unlock(&dad->sysfs_lock); + + if (result->test_summary) { + dev_vdbg(dev, "%s: Finish Cm/Cp test! All Test Passed\n", + __func__); + index = snprintf(buf, CY_MAX_PRBUF_SIZE, + "Status 1\n"); + } else { + dev_vdbg(dev, "%s: Finish Cm/Cp test! Range Check Failure\n", + __func__); + index = snprintf(buf, CY_MAX_PRBUF_SIZE, + "Status 6\n"); + } + + goto cmcp_ready; +mismatch: + index = snprintf(buf, CY_MAX_PRBUF_SIZE, + "Status 2\nInput cmcp threshold file mismatches with FW\n"); + goto cmcp_ready; +invalid_item_btn: + index = snprintf(buf, CY_MAX_PRBUF_SIZE, + "Status 3\nFW doesn't support button!\n"); + goto cmcp_ready; +invalid_item: + index = snprintf(buf, CY_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_id_failed: + index = snprintf(buf, CY_MAX_PRBUF_SIZE, + "Status 5\nget self test ID not supported!"); + goto cmcp_ready; +cmcp_not_ready: + index = snprintf(buf, CY_MAX_PRBUF_SIZE, + "Status 0\n"); + goto cmcp_ready; +no_builtin: + mutex_lock(&dad->sysfs_lock); + dad->test_executed = 1; + mutex_unlock(&dad->sysfs_lock); + index = snprintf(buf, CY_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); + dev_info(dev, "%s: cmcp test show %s", __func__, buf); +exit: + return index; +} + +static ssize_t cyttsp5_cmcp_test_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct cyttsp5_device_access_data *dad + = cyttsp5_get_device_access_data(dev); + u8 test_item = 0; + u8 range_check = 0; + u8 force_calibrate = 0; + 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 = cyttsp5_ic_parse_input(dev, buf, size, dad->ic_buf, + CY_MAX_PRBUF_SIZE); + if (length <= 0 || length > 3) { + dev_err(dev, "%s: Input format error!\n", __func__); + dad->cmcp_test_items = -EINVAL; + ret = -EINVAL; + goto error; + } + + /* Get test item */ + test_item = dad->ic_buf[0]; + /* Get range check */ + if (length >= 2) + range_check = dad->ic_buf[1]; + /* Get force calibration */ + if (length == 3) + force_calibrate = dad->ic_buf[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)) { + dev_err(dev, + "%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; + parade_debug(dev, DEBUG_LEVEL_2, + "%s: Test item is %s; Range check is %s; Force calibrate is %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, S_IRUSR | S_IWUSR, + cyttsp5_cmcp_test_show, cyttsp5_cmcp_test_store); + +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; +} + +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; +} + +int save_header(char *out_buf, int index, struct result *result) +{ + struct timespec64 txc; + struct rtc_time tm; + char time_buf[100] = {0}; + + ktime_get_real_ts64(&txc); + rtc_time64_to_tm(txc.tv_sec, &tm); + scnprintf(time_buf, 100, "%d/%d/%d,TIME,%d:%d:%d,", tm.tm_year+1900, + tm.tm_mon + 1, 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, CY_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; +} + +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; +} + +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 = cmcp_info->tx_num; + int rx_num = cmcp_info->rx_num; + int btn_num = cmcp_info->btn_num; + int tmp = 0; + uint32_t fw_revision_control; + uint32_t fw_config_ver; + char device_id[20] = {0}; + struct cyttsp5_device_access_data *dad + = cyttsp5_get_device_access_data(dev); + + fw_revision_control = dad->si->cydata.revctrl; + fw_config_ver = dad->si->cydata.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->cydata.mfg_id[i]; + + for (i = 4; i < 8; i++) + result->device_id_high = + (result->device_id_high << 8) + dad->si->cydata.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); + + /*short 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_button[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_button[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_button[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_button[i*2], + index, 1); + index = prepare_print_string(out_buf, + "\n", index); + } + } + } + return index; +} + +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) + dev_err(dev, "config is NULL"); + if (result == NULL) + dev_err(dev, "result is NULL"); + if (cmcp_info == NULL) + dev_err(dev, "cmcp_info is NULL"); + + 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; +} + +static int cmcp_results_debugfs_open(struct inode *inode, + struct file *filp) +{ + filp->private_data = inode->i_private; + return 0; +} + +static int cmcp_results_debugfs_close(struct inode *inode, + struct file *filp) +{ + filp->private_data = NULL; + return 0; +} + +static ssize_t cmcp_results_debugfs_read(struct file *filp, + char __user *buf, size_t count, loff_t *ppos) +{ + struct cyttsp5_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 = cyttsp5_cmcp_get_test_item(dad->cmcp_test_items); + if (dad->builtin_cmcp_threshold_status < 0) { + dev_err(dev, "%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"; + dev_err(dev, "%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, +}; + +static ssize_t cmcp_results_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 cyttsp5_device_access_data *dad + = cyttsp5_get_device_access_data(dev); + struct cmcp_data *cmcp_info = dad->cmcp_info; + struct result *result = dad->result; + struct configuration *configuration = dad->configs; + static int pr_left; + static int pr_index; + static u8 *pr_buf; + int index = 0; + int rc = 0; + int test_item; + int no_builtin_file = 0; + int test_executed = 0; + + if (pr_left) { + if (count < pr_left) { + index = count; + memcpy(buf, pr_buf + pr_index, index); + pr_left -= count; + pr_index += count; + } else { + index = pr_left; + memcpy(buf, pr_buf + pr_index, index); + pr_left = 0; + pr_index = 0; + kfree(pr_buf); + } + return index; + } + + if (offset == 0) { + mutex_lock(&dad->sysfs_lock); + test_executed = dad->test_executed; + test_item = cyttsp5_cmcp_get_test_item(dad->cmcp_test_items); + if (dad->builtin_cmcp_threshold_status < 0) { + dev_err(dev, "%s: No cmcp threshold file.\n", __func__); + no_builtin_file = 1; + } + mutex_unlock(&dad->sysfs_lock); + if (test_executed) { + pr_buf = kzalloc(MAX_BUF_LEN, GFP_KERNEL); + if (configuration == NULL) + dev_err(dev, "config is NULL"); + if (result == NULL) + dev_err(dev, "result is NULL"); + if (cmcp_info == NULL) + dev_err(dev, "cmcp_info is NULL"); + + index = save_header(pr_buf, index, result); + index = save_engineering_data(dev, pr_buf, index, + cmcp_info, configuration, result, + test_item, no_builtin_file); + + } else { + char warning_info[] = + "No test result available!\n"; + dev_err(dev, "%s: No test result available!\n", __func__); + index += scnprintf(&pr_buf[index], MAX_BUF_LEN - index, + "%s", warning_info); + } + + if (count < index) { + memcpy(buf, pr_buf, count); + pr_left = index - count; + pr_index = count; + index = count; + } else { + memcpy(buf, pr_buf, index); + pr_left = 0; + pr_index = 0; + kfree(pr_buf); + } + return index; + } + + return rc; +} + +static struct bin_attribute bin_attr_cmcp_results = { + .attr = { + .name = "cmcp_results", + .mode = (0444), + }, + .read = cmcp_results_show, +}; + +static ssize_t cyttsp5_cmcp_threshold_loading_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_device_access_data *dad + = cyttsp5_get_device_access_data(dev); + bool cmcp_threshold_loading; + + mutex_lock(&dad->cmcp_threshold_lock); + cmcp_threshold_loading = dad->cmcp_threshold_loading; + mutex_unlock(&dad->cmcp_threshold_lock); + + return sprintf(buf, "%d\n", cmcp_threshold_loading); +} + +#if 0 +/* Return the buffer offset of new test case */ +u32 cmcp_return_offset_of_new_case(const char *bufPtr, u32 first_time) +{ + 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++ != ASCII_LF) + offset++; + + offset++; + /* + * Single line: end loop + * Multiple lines: continue loop + */ + if (*bufPtr != ASCII_COMMA) + break; + } + } else + first_search = 1; + + return offset; +} +#else +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; +} +#endif + +/* Get test case information from cmcp threshold file */ +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; + + parade_debug(dev, DEBUG_LEVEL_2, "%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; + /* + * parade_debug(dev, DEBUG_LEVEL_2, "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; +} + +/* Compose one value based on data of each bit */ +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; +} + +/* Return one value */ +int cmcp_return_one_value(struct device *dev, + const char *buf, u32 *offset, u32 *line_num) +{ + 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] == ASCII_COMMA + && buf[tmp_offset + 1] == ASCII_COMMA) + tmp_offset++; + + /* Windows and Linux difference at the end of one line */ + if (buf[tmp_offset] == ASCII_COMMA + && buf[tmp_offset + 1] == ASCII_CR + && buf[tmp_offset + 2] == ASCII_LF) + tmp_offset += 2; + 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; + + /* New line for multiple lines */ + if (buf[tmp_offset] == ASCII_LF && buf[tmp_offset + 1] == ASCII_COMMA) { + tmp_offset++; + line_count++; + /*parade_debug(dev, DEBUG_LEVEL_2, "\n");*/ + } + + /* Beginning */ + if (buf[tmp_offset] == ASCII_COMMA) { + tmp_offset++; + for (;;) { + 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); + /*parade_debug(dev, DEBUG_LEVEL_2, */ + /* ",%d", value);*/ + } else { + /* 0 indicates no data available */ + value = -1; + } + break; + } + } + } else { + /* Multiple line: line count */ + *line_num = line_count; + /* Reset for next case */ + line_count = 1; + } + + *offset = tmp_offset; + + return value; +} + +/* Get configuration information */ +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 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; + + parade_debug(dev, DEBUG_LEVEL_2, + "%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; + + /* 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); + 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); + 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; + } + parade_debug(dev, DEBUG_LEVEL_2, + "%s: %s: Data number is %d, line number is %d\n", + __func__, + field_array[count].name, + field_array[count].data_num, + field_array[count].line_num); + } else + parade_debug(dev, DEBUG_LEVEL_2, "%s: !!! %s doesn't exist\n", + __func__, field_array[count].name); + } +} + +/* Get basic information, like tx, rx, button number */ +void cmcp_get_basic_info(struct device *dev, + struct test_case_field *field_array, struct configuration *config) +{ +#define CMCP_DEBUG 0 + u32 tx_num = 0; +#if CMCP_DEBUG + u32 index = 0; +#endif + + 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) { + parade_debug(dev, DEBUG_LEVEL_2, + "%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_button_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_button_size = + field_array[CP_PER_ELEMENT_MIN_MAX_BUTTON].data_num; + +#if CMCP_DEBUG + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->cm_excluding_col_edge); + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->cm_excluding_row_edge); + for (index = 0; + index < config->cm_max_table_gradient_cols_percent_size; + index++) + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->cm_max_table_gradient_cols_percent[index]); + for (index = 0; + index < config->cm_max_table_gradient_rows_percent_size; + index++) + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->cm_max_table_gradient_rows_percent[index]); + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->cm_range_limit_row); + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->cm_range_limit_col); + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->cm_min_limit_cal); + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->cm_max_limit_cal); + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->cm_max_delta_sensor_percent); + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->cm_max_delta_button_percent); + for (index = 0; + index < config->cm_min_max_table_button_size; index++) + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->cm_min_max_table_button[index]); + for (index = 0; + index < config->cm_min_max_table_sensor_size; index++) + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->cm_min_max_table_sensor[index]); + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->cp_max_delta_sensor_rx_percent); + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->cp_max_delta_sensor_tx_percent); + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->cp_max_delta_button_percent); + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->min_button); + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->max_button); + + for (index = 0; + index < config->cp_min_max_table_button_size; index++) + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->cp_min_max_table_button[index]); + for (index = 0; + index < config->cp_min_max_table_rx_size; index++) + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->cp_min_max_table_rx[index]); + for (index = 0; + index < config->cp_min_max_table_tx_size; index++) + parade_debug(dev, DEBUG_LEVEL_2, "%d\n", + config->cp_min_max_table_tx[index]); +#endif + /* 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; + parade_debug(dev, DEBUG_LEVEL_2, "Invalid mutual data length\n"); + } + } else { + if (!config->cm_enabled) + parade_debug(dev, DEBUG_LEVEL_2, + "%s: Miss CM thresholds or CM data format is wrong!\n", + __func__); + + if (!config->cp_enabled) + parade_debug(dev, DEBUG_LEVEL_2, + "%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; + } + + parade_debug(dev, DEBUG_LEVEL_2, + "%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); +} + +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_button[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_button[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); +} + +static ssize_t cyttsp5_parse_cmcp_threshold_file_common( + struct device *dev, const char *buf, u32 file_size) +{ + struct cyttsp5_device_access_data *dad + = cyttsp5_get_device_access_data(dev); + ssize_t rc = 0; + u32 case_count = 0; + + parade_debug(dev, DEBUG_LEVEL_2, + "%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); + + /* Search cases */ + cmcp_get_configuration_info(dev, + buf, + dad->test_search_array, case_count, dad->test_field_array, + dad->configs); + + /* Get basic information */ + cmcp_get_basic_info(dev, dad->test_field_array, dad->configs); + + return rc; +} + +static ssize_t cyttsp5_cmcp_threshold_loading_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct cyttsp5_device_access_data *dad + = cyttsp5_get_device_access_data(dev); + long value; + int rc; + + rc = kstrtol(buf, 10, &value); + if (rc < 0 || value < -1 || value > 1) { + dev_err(dev, "%s: Invalid value\n", __func__); + return size; + } + + mutex_lock(&dad->cmcp_threshold_lock); + + if (value == 1) + dad->cmcp_threshold_loading = true; + else if (value == -1) + dad->cmcp_threshold_loading = false; + else if (value == 0 && dad->cmcp_threshold_loading) { + dad->cmcp_threshold_loading = false; + + if (dad->cmcp_threshold_size == 0) { + dev_err(dev, "%s: No cmcp threshold data\n", __func__); + goto exit_free; + } + + /* Clear test executed flag */ + dad->test_executed = 0; + + cyttsp5_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; + } + +exit_free: + kfree(dad->cmcp_threshold_data); + dad->cmcp_threshold_data = NULL; + dad->cmcp_threshold_size = 0; + + mutex_unlock(&dad->cmcp_threshold_lock); + + if (rc) + return rc; + + return size; +} + +static DEVICE_ATTR(cmcp_threshold_loading, S_IRUSR | S_IWUSR, + cyttsp5_cmcp_threshold_loading_show, + cyttsp5_cmcp_threshold_loading_store); + +/* +* cmcp threshold data write +*/ +static ssize_t cyttsp5_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 cyttsp5_device_access_data *dad + = cyttsp5_get_device_access_data(dev); + u8 *p; + + parade_debug(dev, DEBUG_LEVEL_2, "%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 = S_IWUSR, + }, + .size = 0, + .write = cyttsp5_cmcp_threshold_data_write, +}; + +/* + * Suspend scan command + */ +static int cyttsp5_suspend_scan_cmd_(struct device *dev) +{ + int rc; + + rc = cmd->nonhid_cmd->suspend_scanning(dev, 0); + if (rc < 0) + dev_err(dev, "%s: Suspend scan failed r=%d\n", + __func__, rc); + return rc; +} + +/* + * Resume scan command + */ +static int cyttsp5_resume_scan_cmd_(struct device *dev) +{ + int rc; + + rc = cmd->nonhid_cmd->resume_scanning(dev, 0); + if (rc < 0) + dev_err(dev, "%s: Resume scan failed r=%d\n", + __func__, rc); + return rc; +} + +/* + * Execute scan command + */ +static int cyttsp5_exec_scan_cmd_(struct device *dev) +{ + int rc; + + rc = cmd->nonhid_cmd->exec_panel_scan(dev, 0); + if (rc < 0) + dev_err(dev, "%s: Heatmap start scan failed r=%d\n", + __func__, rc); + return rc; +} + +/* + * Retrieve panel data command + */ +static int cyttsp5_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 < 0) + dev_err(dev, "%s: Retrieve scan data failed r=%d\n", + __func__, rc); + return rc; +} + +/* + * Get data structure command + */ +static int cyttsp5_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 < 0) + dev_err(dev, "%s: Get data structure failed r=%d\n", + __func__, rc); + return rc; +} + +/* + * Run self test command + */ +static int cyttsp5_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 < 0) + dev_err(dev, "%s: Run self test failed r=%d\n", + __func__, rc); + return rc; +} + +/* + * Get self test result command + */ +static int cyttsp5_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 < 0) + dev_err(dev, "%s: Get self test result failed r=%d\n", + __func__, rc); + return rc; +} + +/* + * Calibrate IDACs command + */ +static int _cyttsp5_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; +} + +/* + * Initialize Baselines command + */ +static int _cyttsp5_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; +} + +static int prepare_print_buffer(int status, u8 *in_buf, int length, + u8 *out_buf, size_t out_buf_size) +{ + int index = 0; + int i; + + index += scnprintf(out_buf, out_buf_size, "status %d\n", status); + + for (i = 0; i < length; i++) { + index += scnprintf(&out_buf[index], out_buf_size - index, + "%02X\n", in_buf[i]); + } + + return index; +} +static ssize_t cyttsp5_run_and_get_selftest_result_noprint(struct device *dev, + char *buf, size_t buf_len, u8 test_id, u16 read_length, + bool get_result_on_pass) +{ + struct cyttsp5_device_access_data *dad + = cyttsp5_get_device_access_data(dev); + int status = STATUS_FAIL; + u8 cmd_status = 0; + u8 summary_result = 0; + u16 act_length = 0; + int length = 0; + int rc; + + mutex_lock(&dad->sysfs_lock); + + pm_runtime_get_sync(dev); + + rc = cmd->request_exclusive(dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(dev, "%s: Error on request exclusive r=%d\n", + __func__, rc); + goto put_pm_runtime; + } + + rc = cyttsp5_suspend_scan_cmd_(dev); + if (rc < 0) { + dev_err(dev, "%s: Error on suspend scan r=%d\n", + __func__, rc); + goto release_exclusive; + } + + rc = cyttsp5_run_selftest_cmd_(dev, test_id, 0, + &cmd_status, &summary_result, NULL); + if (rc < 0) { + dev_err(dev, "%s: Error on run self test for test_id:%d r=%d\n", + __func__, test_id, rc); + goto resume_scan; + } + + /* Form response buffer */ + dad->ic_buf[0] = cmd_status; + dad->ic_buf[1] = summary_result; + + length = 2; + + /* Get data if command status is success */ + if (cmd_status != CY_CMD_STATUS_SUCCESS) + goto status_success; + + /* Get data unless test result is pass */ + if (summary_result == CY_ST_RESULT_PASS && !get_result_on_pass) + goto status_success; + + rc = cyttsp5_get_selftest_result_cmd_(dev, 0, read_length, + test_id, &cmd_status, &act_length, &dad->ic_buf[6]); + if (rc < 0) { + dev_err(dev, "%s: Error on get self test result r=%d\n", + __func__, rc); + goto resume_scan; + } + + 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; + +status_success: + status = STATUS_SUCCESS; + +resume_scan: + cyttsp5_resume_scan_cmd_(dev); + +release_exclusive: + cmd->release_exclusive(dev); + +put_pm_runtime: + pm_runtime_put(dev); + mutex_unlock(&dad->sysfs_lock); + + return status; +} + +static ssize_t cyttsp5_run_and_get_selftest_result(struct device *dev, + char *buf, size_t buf_len, u8 test_id, u16 read_length, + bool get_result_on_pass) +{ + struct cyttsp5_device_access_data *dad + = cyttsp5_get_device_access_data(dev); + int status = STATUS_FAIL; + u8 cmd_status = 0; + u8 summary_result = 0; + u16 act_length = 0; + int length = 0; + int size; + int rc; + + mutex_lock(&dad->sysfs_lock); + + pm_runtime_get_sync(dev); + + rc = cmd->request_exclusive(dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(dev, "%s: Error on request exclusive r=%d\n", + __func__, rc); + goto put_pm_runtime; + } + + rc = cyttsp5_suspend_scan_cmd_(dev); + if (rc < 0) { + dev_err(dev, "%s: Error on suspend scan r=%d\n", + __func__, rc); + goto release_exclusive; + } + + rc = cyttsp5_run_selftest_cmd_(dev, test_id, 0, + &cmd_status, &summary_result, NULL); + if (rc < 0) { + dev_err(dev, "%s: Error on run self test for test_id:%d r=%d\n", + __func__, test_id, rc); + goto resume_scan; + } + + /* Form response buffer */ + dad->ic_buf[0] = cmd_status; + dad->ic_buf[1] = summary_result; + + length = 2; + + /* Get data if command status is success */ + if (cmd_status != CY_CMD_STATUS_SUCCESS) + goto status_success; + + /* Get data unless test result is pass */ + if (summary_result == CY_ST_RESULT_PASS && !get_result_on_pass) + goto status_success; + + rc = cyttsp5_get_selftest_result_cmd_(dev, 0, read_length, + test_id, &cmd_status, &act_length, &dad->ic_buf[6]); + if (rc < 0) { + dev_err(dev, "%s: Error on get self test result r=%d\n", + __func__, rc); + goto resume_scan; + } + + 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; + +status_success: + status = STATUS_SUCCESS; + +resume_scan: + cyttsp5_resume_scan_cmd_(dev); + +release_exclusive: + cmd->release_exclusive(dev); + +put_pm_runtime: + pm_runtime_put(dev); + + if (status == STATUS_FAIL) + length = 0; + + size = prepare_print_buffer(status, dad->ic_buf, length, buf, buf_len); + + mutex_unlock(&dad->sysfs_lock); + + return size; +} + +struct cyttsp5_device_access_debugfs_data { + struct cyttsp5_device_access_data *dad; + ssize_t pr_buf_len; + u8 pr_buf[3 * CY_MAX_PRBUF_SIZE]; +}; + +static int cyttsp5_device_access_debugfs_open(struct inode *inode, + struct file *filp) +{ + struct cyttsp5_device_access_data *dad = inode->i_private; + struct cyttsp5_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); +} + +static int cyttsp5_device_access_debugfs_release(struct inode *inode, + struct file *filp) +{ + kfree(filp->private_data); + + return 0; +} + +#define CY_DEBUGFS_FOPS(_name, _read, _write) \ +static const struct file_operations _name##_debugfs_fops = { \ + .open = cyttsp5_device_access_debugfs_open, \ + .release = cyttsp5_device_access_debugfs_release, \ + .read = _read, \ + .write = _write, \ +} + +static ssize_t panel_scan_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct cyttsp5_device_access_debugfs_data *data = filp->private_data; + struct cyttsp5_device_access_data *dad = data->dad; + struct device *dev = dad->dev; + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + int status = STATUS_FAIL; + u8 config; + u16 actual_read_len; + int length = 0; + u8 element_size = 0; + u8 *buf_offset; + int elem_offset = 0; + int rc; + + if (*ppos) + goto exit; + + + mutex_lock(&dad->sysfs_lock); + + pm_runtime_get_sync(dev); + + rc = cmd->request_exclusive(dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(dev, "%s: Error on request exclusive r=%d\n", + __func__, rc); + goto put_pm_runtime; + } + + rc = cyttsp5_suspend_scan_cmd_(dev); + if (rc < 0) { + dev_err(dev, "%s: Error on suspend scan r=%d\n", + __func__, rc); + goto release_exclusive; + } + + rc = cyttsp5_exec_scan_cmd_(dev); + if (rc < 0) { + dev_err(dev, "%s: Error on execute panel scan r=%d\n", + __func__, rc); + goto resume_scan; + } + + /* Set length to max to read all */ + rc = cyttsp5_ret_scan_data_cmd_(dev, 0, 0xFFFF, + dad->panel_scan_data_id, dad->ic_buf, &config, + &actual_read_len, NULL); + if (rc < 0) { + dev_err(dev, "%s: Error on retrieve panel scan r=%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 = actual_read_len; + while (actual_read_len > 0) { + rc = cyttsp5_ret_scan_data_cmd_(dev, elem_offset, 0xFFFF, + dad->panel_scan_data_id, NULL, &config, + &actual_read_len, buf_offset); + if (rc < 0) + goto resume_scan; + + length += actual_read_len * element_size; + buf_offset = dad->ic_buf + length; + 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]); + + /* Do not print command header */ + length -= 5; + + status = STATUS_SUCCESS; + +resume_scan: + cyttsp5_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) { + int index = 0; + + index += scnprintf(data->pr_buf, sizeof(data->pr_buf), + "[%u] ", jiffies_to_msecs(jiffies)); + data->pr_buf_len = prepare_print_buffer(status, + &dad->ic_buf[5], length, &data->pr_buf[index], + sizeof(data->pr_buf)-index); + } else { + data->pr_buf_len = prepare_print_buffer(status, + &dad->ic_buf[5], length, data->pr_buf, + sizeof(data->pr_buf)); + } + + mutex_unlock(&dad->sysfs_lock); + +exit: + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +static ssize_t panel_scan_debugfs_write(struct file *filp, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct cyttsp5_device_access_debugfs_data *data = filp->private_data; + struct cyttsp5_device_access_data *dad = data->dad; + ssize_t length; + 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 = cyttsp5_ic_parse_input(dad->dev, data->pr_buf, count, + dad->ic_buf, CY_MAX_PRBUF_SIZE); + + if (length != 1) { + dev_err(dad->dev, "%s: Malformed input\n", __func__); + rc = -EINVAL; + goto exit_unlock; + } + + dad->panel_scan_data_id = dad->ic_buf[0]; + +exit_unlock: + mutex_unlock(&dad->sysfs_lock); + + if (rc) + return rc; + + return count; +} + +CY_DEBUGFS_FOPS(panel_scan, panel_scan_debugfs_read, panel_scan_debugfs_write); + +static ssize_t get_idac_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct cyttsp5_device_access_debugfs_data *data = filp->private_data; + struct cyttsp5_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, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(dev, "%s: Error on request exclusive r=%d\n", + __func__, rc); + goto put_pm_runtime; + } + + rc = cyttsp5_suspend_scan_cmd_(dev); + if (rc < 0) { + dev_err(dev, "%s: Error on suspend scan r=%d\n", + __func__, rc); + goto release_exclusive; + } + + rc = cyttsp5_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 < 0) { + dev_err(dev, "%s: Error on get data structure r=%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 = STATUS_SUCCESS; + +resume_scan: + cyttsp5_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)); + + mutex_unlock(&dad->sysfs_lock); + +exit: + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +static ssize_t get_idac_debugfs_write(struct file *filp, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct cyttsp5_device_access_debugfs_data *data = filp->private_data; + struct cyttsp5_device_access_data *dad = data->dad; + ssize_t length; + 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 = cyttsp5_ic_parse_input(dad->dev, data->pr_buf, count, + dad->ic_buf, CY_MAX_PRBUF_SIZE); + if (length != 1) { + dev_err(dad->dev, "%s: Malformed input\n", __func__); + rc = -EINVAL; + goto exit_unlock; + } + + dad->get_idac_data_id = dad->ic_buf[0]; + +exit_unlock: + mutex_unlock(&dad->sysfs_lock); + + if (rc) + return rc; + + return count; +} + +CY_DEBUGFS_FOPS(get_idac, get_idac_debugfs_read, get_idac_debugfs_write); + +static ssize_t calibrate_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct cyttsp5_device_access_debugfs_data *data = filp->private_data; + struct cyttsp5_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, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(dev, "%s: Error on request exclusive r=%d\n", + __func__, rc); + goto put_pm_runtime; + } + + rc = cyttsp5_suspend_scan_cmd_(dev); + if (rc < 0) { + dev_err(dev, "%s: Error on suspend scan r=%d\n", + __func__, rc); + goto release_exclusive; + } + + rc = _cyttsp5_calibrate_idacs_cmd(dev, dad->calibrate_sensing_mode, + &dad->ic_buf[0]); + if (rc < 0) { + dev_err(dev, "%s: Error on calibrate idacs r=%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 = _cyttsp5_initialize_baselines_cmd(dev, CY_IB_SM_MUTCAP | + CY_IB_SM_SELFCAP | CY_IB_SM_BUTTON, + &dad->ic_buf[length]); + if (rc < 0) { + dev_err(dev, "%s: Error on initialize baselines r=%d\n", + __func__, rc); + goto resume_scan; + } + + length++; + } + + status = STATUS_SUCCESS; + +resume_scan: + cyttsp5_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)); + + mutex_unlock(&dad->sysfs_lock); + +exit: + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +static ssize_t calibrate_debugfs_write(struct file *filp, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct cyttsp5_device_access_debugfs_data *data = filp->private_data; + struct cyttsp5_device_access_data *dad = data->dad; + ssize_t length; + 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 = cyttsp5_ic_parse_input(dad->dev, data->pr_buf, count, + dad->ic_buf, CY_MAX_PRBUF_SIZE); + if (length != 2) { + dev_err(dad->dev, "%s: Malformed input\n", __func__); + rc = -EINVAL; + goto exit_unlock; + } + + dad->calibrate_sensing_mode = dad->ic_buf[0]; + dad->calibrate_initialize_baselines = dad->ic_buf[1]; + +exit_unlock: + mutex_unlock(&dad->sysfs_lock); + + if (rc) + return rc; + + return count; +} + +CY_DEBUGFS_FOPS(calibrate, calibrate_debugfs_read, calibrate_debugfs_write); + +static ssize_t baseline_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct cyttsp5_device_access_debugfs_data *data = filp->private_data; + struct cyttsp5_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, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(dev, "%s: Error on request exclusive r=%d\n", + __func__, rc); + goto put_pm_runtime; + } + + rc = cyttsp5_suspend_scan_cmd_(dev); + if (rc < 0) { + dev_err(dev, "%s: Error on suspend scan r=%d\n", + __func__, rc); + goto release_exclusive; + } + + rc = _cyttsp5_initialize_baselines_cmd(dev, dad->baseline_sensing_mode, + &dad->ic_buf[0]); + if (rc < 0) { + dev_err(dev, "%s: Error on initialize baselines r=%d\n", + __func__, rc); + goto resume_scan; + } + + length = 1; + + status = STATUS_SUCCESS; + +resume_scan: + cyttsp5_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)); + + mutex_unlock(&dad->sysfs_lock); + +exit: + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +static ssize_t baseline_debugfs_write(struct file *filp, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct cyttsp5_device_access_debugfs_data *data = filp->private_data; + struct cyttsp5_device_access_data *dad = data->dad; + ssize_t length; + 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 = cyttsp5_ic_parse_input(dad->dev, buf, count, dad->ic_buf, + CY_MAX_PRBUF_SIZE); + if (length != 1) { + dev_err(dad->dev, "%s: Malformed input\n", __func__); + rc = -EINVAL; + goto exit_unlock; + } + + dad->baseline_sensing_mode = dad->ic_buf[0]; + +exit_unlock: + mutex_unlock(&dad->sysfs_lock); + + if (rc) + return rc; + + return count; +} + +CY_DEBUGFS_FOPS(baseline, baseline_debugfs_read, baseline_debugfs_write); + +static ssize_t auto_shorts_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct cyttsp5_device_access_debugfs_data *data = filp->private_data; + + if (!*ppos) + /* Set length to PIP_CMD_MAX_LENGTH to read all */ + data->pr_buf_len = cyttsp5_run_and_get_selftest_result( + data->dad->dev, data->pr_buf, sizeof(data->pr_buf), + CY_ST_ID_AUTOSHORTS, PIP_CMD_MAX_LENGTH, false); + + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +CY_DEBUGFS_FOPS(auto_shorts, auto_shorts_debugfs_read, NULL); + +static ssize_t opens_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct cyttsp5_device_access_debugfs_data *data = filp->private_data; + + if (!*ppos) + /* Set length to PIP_CMD_MAX_LENGTH to read all */ + data->pr_buf_len = cyttsp5_run_and_get_selftest_result( + data->dad->dev, data->pr_buf, sizeof(data->pr_buf), + CY_ST_ID_OPENS, PIP_CMD_MAX_LENGTH, false); + + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +CY_DEBUGFS_FOPS(opens, opens_debugfs_read, NULL); + +static ssize_t cm_panel_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct cyttsp5_device_access_debugfs_data *data = filp->private_data; + + if (!*ppos) + /* Set length to PIP_CMD_MAX_LENGTH to read all */ + data->pr_buf_len = cyttsp5_run_and_get_selftest_result( + data->dad->dev, data->pr_buf, sizeof(data->pr_buf), + CY_ST_ID_CM_PANEL, PIP_CMD_MAX_LENGTH, true); + + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +CY_DEBUGFS_FOPS(cm_panel, cm_panel_debugfs_read, NULL); + +static ssize_t cp_panel_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct cyttsp5_device_access_debugfs_data *data = filp->private_data; + + if (!*ppos) + /* Set length to PIP_CMD_MAX_LENGTH to read all */ + data->pr_buf_len = cyttsp5_run_and_get_selftest_result( + data->dad->dev, data->pr_buf, sizeof(data->pr_buf), + CY_ST_ID_CP_PANEL, PIP_CMD_MAX_LENGTH, true); + + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +CY_DEBUGFS_FOPS(cp_panel, cp_panel_debugfs_read, NULL); + +static ssize_t cm_button_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct cyttsp5_device_access_debugfs_data *data = filp->private_data; + + if (!*ppos) + /* Set length to PIP_CMD_MAX_LENGTH to read all */ + data->pr_buf_len = cyttsp5_run_and_get_selftest_result( + data->dad->dev, data->pr_buf, sizeof(data->pr_buf), + CY_ST_ID_CM_BUTTON, PIP_CMD_MAX_LENGTH, true); + + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +CY_DEBUGFS_FOPS(cm_button, cm_button_debugfs_read, NULL); + +static ssize_t cp_button_debugfs_read(struct file *filp, char __user *buf, + size_t count, loff_t *ppos) +{ + struct cyttsp5_device_access_debugfs_data *data = filp->private_data; + + if (!*ppos) + /* Set length to PIP_CMD_MAX_LENGTH to read all */ + data->pr_buf_len = cyttsp5_run_and_get_selftest_result( + data->dad->dev, data->pr_buf, sizeof(data->pr_buf), + CY_ST_ID_CP_BUTTON, PIP_CMD_MAX_LENGTH, true); + + return simple_read_from_buffer(buf, count, ppos, data->pr_buf, + data->pr_buf_len); +} + +CY_DEBUGFS_FOPS(cp_button, cp_button_debugfs_read, NULL); + +#ifdef TTHE_TUNER_SUPPORT +static ssize_t tthe_get_panel_data_debugfs_read(struct file *filp, + char __user *buf, size_t count, loff_t *ppos) +{ + struct cyttsp5_device_access_data *dad = filp->private_data; + struct device *dev; + struct cyttsp5_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->tthe_get_panel_data_buf; + if (!buf_out) + goto release_mutex; + + pm_runtime_get_sync(dev); + + rc = cmd->request_exclusive(dev, CY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) + 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 = cyttsp5_exec_scan_cmd_(dev); + if (rc < 0) + goto release_exclusive; + } + + elem = dad->heatmap.num_element; + +#if defined(CY_ENABLE_MAX_ELEN) + if (elem > CY_MAX_ELEN) { + rc = cyttsp5_ret_scan_data_cmd_(dev, elem_offset, + CY_MAX_ELEN, dad->heatmap.data_type, dad->ic_buf, + &config, &actual_read_len, NULL); + } else{ + rc = cyttsp5_ret_scan_data_cmd_(dev, elem_offset, elem, + dad->heatmap.data_type, dad->ic_buf, &config, + &actual_read_len, NULL); + } +#else + rc = cyttsp5_ret_scan_data_cmd_(dev, elem_offset, elem, + dad->heatmap.data_type, dad->ic_buf, &config, + &actual_read_len, NULL); +#endif + if (rc < 0) + goto release_exclusive; + + length = get_unaligned_le16(&dad->ic_buf[0]); + buf_offset = dad->ic_buf + length; + + element_size = config & CY_CMD_RET_PANEL_ELMNT_SZ_MASK; + + elem -= actual_read_len; + elem_offset = actual_read_len; + while (elem > 0) { +#ifdef CY_ENABLE_MAX_ELEN + if (elem > CY_MAX_ELEN) { + rc = cyttsp5_ret_scan_data_cmd_(dev, elem_offset, + CY_MAX_ELEN, dad->heatmap.data_type, NULL, &config, + &actual_read_len, buf_offset); + } else{ + rc = cyttsp5_ret_scan_data_cmd_(dev, elem_offset, elem, + dad->heatmap.data_type, NULL, &config, + &actual_read_len, buf_offset); + } +#else + + rc = cyttsp5_ret_scan_data_cmd_(dev, elem_offset, elem, + dad->heatmap.data_type, NULL, &config, + &actual_read_len, buf_offset); +#endif + if (rc < 0) + 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 < 0) + goto release_mutex; + if (cd->show_timestamp) + print_idx += scnprintf(buf_out, TTHE_TUNER_MAX_BUF, + "[%u] CY_DATA:", jiffies_to_msecs(jiffies)); + else + print_idx += scnprintf(buf_out, TTHE_TUNER_MAX_BUF, + "CY_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; +} + +static ssize_t tthe_get_panel_data_debugfs_write(struct file *filp, + const char __user *buf, size_t count, loff_t *ppos) +{ + struct cyttsp5_device_access_data *dad = filp->private_data; + struct device *dev = dad->dev; + ssize_t length; + int max_read; + u8 *buf_in = dad->tthe_get_panel_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 = cyttsp5_ic_parse_input(dev, buf_in, count, dad->ic_buf, + CY_MAX_PRBUF_SIZE); + if (length <= 0) { + dev_err(dev, "%s: %s Group Data store\n", __func__, + "Malformed input for"); + goto exit; + } + + /* update parameter value */ + dad->heatmap.num_element = get_unaligned_le16(&dad->ic_buf[3]); + dad->heatmap.data_type = dad->ic_buf[5]; + + if (dad->ic_buf[6] > 0) + dad->heatmap.scan_start = true; + else + dad->heatmap.scan_start = false; + + /* elem can not be bigger then buffer size */ + max_read = CY_CMD_RET_PANEL_HDR; + max_read += dad->heatmap.num_element * CY_CMD_RET_PANEL_ELMNT_SZ_MAX; + + if (max_read >= CY_MAX_PRBUF_SIZE) { + dad->heatmap.num_element = + (CY_MAX_PRBUF_SIZE - CY_CMD_RET_PANEL_HDR) + / CY_CMD_RET_PANEL_ELMNT_SZ_MAX; + parade_debug(dev, DEBUG_LEVEL_2, "%s: Will get %d element\n", + __func__, dad->heatmap.num_element); + } + +exit: + mutex_unlock(&dad->debugfs_lock); + parade_debug(dev, DEBUG_LEVEL_2, "%s: return count=%zu\n", + __func__, count); + return count; +} + +static int tthe_get_panel_data_debugfs_open(struct inode *inode, + struct file *filp) +{ + struct cyttsp5_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; +} + +static int tthe_get_panel_data_debugfs_close(struct inode *inode, + struct file *filp) +{ + struct cyttsp5_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 + +static int cyttsp5_setup_sysfs(struct device *dev) +{ + struct cyttsp5_device_access_data *dad + = cyttsp5_get_device_access_data(dev); + int rc; + + rc = device_create_file(dev, &dev_attr_command); + if (rc) { + dev_err(dev, "%s: Error, could not create command\n", + __func__); + goto exit; + } + + rc = device_create_file(dev, &dev_attr_status); + if (rc) { + dev_err(dev, "%s: Error, could not create status\n", + __func__); + goto unregister_command; + } + + rc = device_create_file(dev, &dev_attr_response); + if (rc) { + dev_err(dev, "%s: Error, could not create response\n", + __func__); + goto unregister_status; + } + + rc = device_create_bin_file(dev, &bin_attr_cmcp_results); + if (rc) { + dev_err(dev, + "%s: Error, could not create node cmcp_results\n", + __func__); + goto unregister_response; + } + + dad->base_dentry = debugfs_create_dir(dev_name(dev), NULL); + if (IS_ERR_OR_NULL(dad->base_dentry)) { + dev_err(dev, "%s: Error, could not create base directory\n", + __func__); + goto unregister_cmcp_results; + } + + dad->mfg_test_dentry = debugfs_create_dir("mfg_test", + dad->base_dentry); + if (IS_ERR_OR_NULL(dad->mfg_test_dentry)) { + dev_err(dev, "%s: Error, could not create mfg_test directory\n", + __func__); + goto unregister_base_dir; + } + + if (IS_ERR_OR_NULL(debugfs_create_file("panel_scan", 0600, + dad->mfg_test_dentry, dad, + &panel_scan_debugfs_fops))) { + dev_err(dev, "%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))) { + dev_err(dev, "%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))) { + dev_err(dev, "%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))) { + dev_err(dev, "%s: Error, could not create opens\n", + __func__); + goto unregister_base_dir; + } + + if (IS_ERR_OR_NULL(debugfs_create_file("calibrate", 0600, + dad->mfg_test_dentry, dad, &calibrate_debugfs_fops))) { + dev_err(dev, "%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))) { + dev_err(dev, "%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))) { + dev_err(dev, "%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))) { + dev_err(dev, "%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))) { + dev_err(dev, "%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))) { + dev_err(dev, "%s: Error, could not create cp_button\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)) { + dev_err(dev, "%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( + CYTTSP5_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)) { + dev_err(dev, "%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) { + dev_err(dev, "%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) { + dev_err(dev, "%s: Error, could not create cmcp_thresold_loading\n", + __func__); + goto unregister_cmcp_test; + } + + rc = device_create_bin_file(dev, &bin_attr_cmcp_threshold_data); + if (rc) { + dev_err(dev, "%s: Error, could not create cmcp_thresold_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); +unregister_cmcp_results: + device_remove_bin_file(dev, &bin_attr_cmcp_results); +unregister_response: + device_remove_file(dev, &dev_attr_response); +unregister_status: + device_remove_file(dev, &dev_attr_status); +unregister_command: + device_remove_file(dev, &dev_attr_command); +exit: + return rc; +} + +static void cyttsp5_parse_cmcp_threshold_builtin( + struct work_struct *cmcp_threshold_update); +static int cyttsp5_setup_sysfs_attention(struct device *dev) +{ + struct cyttsp5_device_access_data *dad + = cyttsp5_get_device_access_data(dev); + int rc = 0; + + dad->si = cmd->request_sysinfo(dev); + if (!dad->si) + return -EINVAL; + + rc = cyttsp5_setup_sysfs(dev); + + INIT_WORK(&dad->cmcp_threshold_update, + cyttsp5_parse_cmcp_threshold_builtin); + schedule_work(&dad->cmcp_threshold_update); + + cmd->unsubscribe_attention(dev, CY_ATTEN_STARTUP, + CYTTSP5_DEVICE_ACCESS_NAME, cyttsp5_setup_sysfs_attention, + 0); + + return rc; +} + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICE_ACCESS_API +int cyttsp5_device_access_user_command(const char *core_name, u16 read_len, + u8 *read_buf, u16 write_len, u8 *write_buf, + u16 *actual_read_len) +{ + struct cyttsp5_core_data *cd; + int rc; + + might_sleep(); + + /* Check parameters */ + if (!read_buf || !write_buf || !actual_read_len) + return -EINVAL; + + if (!core_name) + core_name = CY_DEFAULT_CORE_ID; + + /* Find device */ + cd = cyttsp5_get_core_data((char *)core_name); + if (!cd) { + pr_err("%s: No device.\n", __func__); + return -ENODEV; + } + + pm_runtime_get_sync(cd->dev); + rc = cmd->nonhid_cmd->user_cmd(cd->dev, 1, read_len, read_buf, + write_len, write_buf, actual_read_len); + pm_runtime_put(cd->dev); + + return rc; +} +EXPORT_SYMBOL_GPL(cyttsp5_device_access_user_command); + +struct command_work { + struct work_struct work; + const char *core_name; + u16 read_len; + u8 *read_buf; + u16 write_len; + u8 *write_buf; + + void (*cont)(const char *core_name, u16 read_len, u8 *read_buf, + u16 write_len, u8 *write_buf, u16 actual_read_length, + int rc); +}; + +static void cyttsp5_device_access_user_command_work_func( + struct work_struct *work) +{ + struct command_work *cmd_work = + container_of(work, struct command_work, work); + u16 actual_read_length; + int rc; + + rc = cyttsp5_device_access_user_command(cmd_work->core_name, + cmd_work->read_len, cmd_work->read_buf, + cmd_work->write_len, cmd_work->write_buf, + &actual_read_length); + + if (cmd_work->cont) + cmd_work->cont(cmd_work->core_name, + cmd_work->read_len, cmd_work->read_buf, + cmd_work->write_len, cmd_work->write_buf, + actual_read_length, rc); + + kfree(cmd_work); +} + +int cyttsp5_device_access_user_command_async(const char *core_name, + u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf, + void (*cont)(const char *core_name, u16 read_len, u8 *read_buf, + u16 write_len, u8 *write_buf, u16 actual_read_length, + int rc)) +{ + struct command_work *cmd_work; + + cmd_work = kzalloc(sizeof(*cmd_work), GFP_ATOMIC); + if (!cmd_work) + return -ENOMEM; + + cmd_work->core_name = core_name; + cmd_work->read_len = read_len; + cmd_work->read_buf = read_buf; + cmd_work->write_len = write_len; + cmd_work->write_buf = write_buf; + cmd_work->cont = cont; + + INIT_WORK(&cmd_work->work, + cyttsp5_device_access_user_command_work_func); + schedule_work(&cmd_work->work); + + return 0; +} +EXPORT_SYMBOL_GPL(cyttsp5_device_access_user_command_async); +#endif + +static void cyttsp5_cmcp_parse_threshold_file(const struct firmware *fw, + void *context) +{ + struct device *dev = context; + struct cyttsp5_device_access_data *dad = + cyttsp5_get_device_access_data(dev); + + if (!fw) { + dev_info(dev, "%s: No builtin cmcp threshold file\n", __func__); + goto exit; + } + + if (!fw->data || !fw->size) { + dev_err(dev, "%s: Invalid builtin cmcp threshold file\n", + __func__); + goto exit; + } + + parade_debug(dev, DEBUG_LEVEL_1, "%s: Found cmcp threshold file.\n", + __func__); + + cyttsp5_parse_cmcp_threshold_file_common(dev, &fw->data[0], fw->size); + + dad->builtin_cmcp_threshold_status = 0; + complete(&dad->builtin_cmcp_threshold_complete); + return; + +exit: + release_firmware(fw); + + dad->builtin_cmcp_threshold_status = -EINVAL; + complete(&dad->builtin_cmcp_threshold_complete); +} + +static void cyttsp5_parse_cmcp_threshold_builtin( + struct work_struct *cmcp_threshold_update) +{ + struct cyttsp5_device_access_data *dad = + container_of(cmcp_threshold_update, + struct cyttsp5_device_access_data, + cmcp_threshold_update); + struct device *dev = dad->dev; + int retval; + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + char limit_file[100] = {0}; + + dad->si = cmd->request_sysinfo(dev); + if (!dad->si) { + dev_err(dev, "%s: Fail get sysinfo pointer from core\n", + __func__); + return; + } + + parade_debug(dev, DEBUG_LEVEL_2, + "%s: Enabling cmcp threshold class loader built-in\n", + __func__); + + if (cd->supplier) + snprintf(limit_file, sizeof(limit_file), "%s_%s_test_limits.csv", + cd->supplier, PARADE_TEST_FILE_NAME); + else + snprintf(limit_file, sizeof(limit_file), "%s", + CMCP_THRESHOLD_FILE_NAME); + dev_info(dev, "limit_file_name:%s", limit_file); + + /* Open threshold file */ + retval = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + limit_file, dev, GFP_KERNEL, dev, + cyttsp5_cmcp_parse_threshold_file); + if (retval < 0) { + dev_err(dev, "%s: Failed loading cmcp threshold file, attempting legacy file\n", + __func__); + /* Try legacy file name */ + retval = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + CY_CMCP_THRESHOLD_FILE_NAME, dev, GFP_KERNEL, + dev, cyttsp5_cmcp_parse_threshold_file); + if (retval < 0) { + dev_err(dev, "%s: Fail request cmcp threshold class file load\n", + __func__); + goto exit; + } + } + + /* wait until cmcp threshold upgrade finishes */ + wait_for_completion(&dad->builtin_cmcp_threshold_complete); + + retval = dad->builtin_cmcp_threshold_status; + +exit: + return; +} + +static int cyttsp5_device_access_probe(struct device *dev, void **data) +{ + struct cyttsp5_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 cyttsp5_device_access_probe_data_failed; + } + + configurations = + kzalloc(sizeof(*configurations), GFP_KERNEL); + if (!configurations) { + rc = -ENOMEM; + goto cyttsp5_device_access_probe_configs_failed; + } + dad->configs = configurations; + + cmcp_info = kzalloc(sizeof(*cmcp_info), GFP_KERNEL); + if (!cmcp_info) { + rc = -ENOMEM; + goto cyttsp5_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 cyttsp5_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 cyttsp5_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 cyttsp5_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 cyttsp5_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 cyttsp5_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 cyttsp5_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 cyttsp5_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 cyttsp5_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 cyttsp5_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 cyttsp5_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 cyttsp5_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 cyttsp5_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 cyttsp5_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 cyttsp5_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; + + init_completion(&dad->builtin_cmcp_threshold_complete); + + /* get sysinfo */ + dad->si = cmd->request_sysinfo(dev); + if (dad->si) { + rc = cyttsp5_setup_sysfs(dev); + if (rc) + goto cyttsp5_device_access_setup_sysfs_failed; + } else { + dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n", + __func__, dad->si); + cmd->subscribe_attention(dev, CY_ATTEN_STARTUP, + CYTTSP5_DEVICE_ACCESS_NAME, + cyttsp5_setup_sysfs_attention, 0); + } + + INIT_WORK(&dad->cmcp_threshold_update, + cyttsp5_parse_cmcp_threshold_builtin); + schedule_work(&dad->cmcp_threshold_update); + + return 0; + +cyttsp5_device_access_setup_sysfs_failed: + kfree(cmcp_info->cm_sensor_row_delta); +cyttsp5_device_access_probe_cm_sensor_row_delta_failed: + kfree(cmcp_info->cm_sensor_column_delta); +cyttsp5_device_access_probe_cm_sensor_column_delta_failed: + kfree(cmcp_info->cp_btn_data); +cyttsp5_device_access_probe_cp_btn_data_failed: + kfree(cmcp_info->cm_btn_data); +cyttsp5_device_access_probe_cm_btn_data_failed: + kfree(cmcp_info->cp_rx_cal_data_panel); +cyttsp5_device_access_probe_cp_rx_cal_data_panel_failed: + kfree(cmcp_info->cp_rx_data_panel); +cyttsp5_device_access_probe_cp_rx_data_panel_failed: + kfree(cmcp_info->cp_tx_cal_data_panel); +cyttsp5_device_access_probe_cp_tx_cal_data_panel_failed: + kfree(cmcp_info->cp_tx_data_panel); +cyttsp5_device_access_probe_cp_tx_data_panel_failed: + kfree(cmcp_info->cm_data_panel); +cyttsp5_device_access_probe_cm_data_panel_failed: + kfree(cmcp_info->gd_sensor_row); +cyttsp5_device_access_probe_gd_sensor_row_failed: + kfree(cmcp_info->gd_sensor_col); +cyttsp5_device_access_probe_gd_sensor_col_failed: + kfree(test_case_search_array); +cyttsp5_device_access_probe_search_array_failed: + kfree(test_case_field_array); +cyttsp5_device_access_probe_field_array_failed: + kfree(result); +cyttsp5_device_access_probe_result_failed: + kfree(cmcp_info); +cyttsp5_device_access_probe_cmcp_info_failed: + kfree(configurations); +cyttsp5_device_access_probe_configs_failed: + kfree(dad); +cyttsp5_device_access_probe_data_failed: + dev_err(dev, "%s failed.\n", __func__); + return rc; +} + +static void cyttsp5_device_access_release(struct device *dev, void *data) +{ + struct cyttsp5_device_access_data *dad = data; + + if (dad->sysfs_nodes_created) { + device_remove_file(dev, &dev_attr_command); + device_remove_file(dev, &dev_attr_status); + device_remove_file(dev, &dev_attr_response); + device_remove_bin_file(dev, &bin_attr_cmcp_results); + 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, CY_ATTEN_STARTUP, + CYTTSP5_DEVICE_ACCESS_NAME, + cyttsp5_setup_sysfs_attention, 0); + } + + kfree(dad->test_search_array); + kfree(dad->test_field_array); + kfree(dad->configs); + cyttsp5_free_cmcp_buf(dad->cmcp_info); + kfree(dad->cmcp_info); + kfree(dad->result); + kfree(dad); +} + +static struct cyttsp5_module device_access_module = { + .name = CYTTSP5_DEVICE_ACCESS_NAME, + .probe = cyttsp5_device_access_probe, + .release = cyttsp5_device_access_release, +}; + +static int __init cyttsp5_device_access_init(void) +{ + int rc; + + cmd = cyttsp5_get_commands(); + if (!cmd) + return -EINVAL; + + rc = cyttsp5_register_module(&device_access_module); + if (rc < 0) { + 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__, CY_DRIVER_VERSION, rc); + return 0; +} +module_init(cyttsp5_device_access_init); + +static void __exit cyttsp5_device_access_exit(void) +{ + cyttsp5_unregister_module(&device_access_module); +} +module_exit(cyttsp5_device_access_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Device Access Driver"); +MODULE_AUTHOR("Parade Technologies "); + diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_devtree.c b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_devtree.c new file mode 100644 index 000000000000..a739b64fdd46 --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_devtree.c @@ -0,0 +1,800 @@ +/* + * cyttsp5_devtree.c + * Parade TrueTouch(TM) Standard Product V5 Device Tree Support Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * CYTMA5XX + * CYTMA448 + * CYTMA445A + * CYTT21XXX + * CYTT31XXX + * + * Copyright (C) 2015 Parade Technologies + * Copyright (C) 2013-2015 Cypress Semiconductor + * + * 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 + * + */ + +#include +#include +#include +#include +#include + +/* cyttsp */ +#include "cyttsp5_regs.h" +#include "cyttsp5_platform.h" + +#define ENABLE_VIRTUAL_KEYS + +#define MAX_NAME_LENGTH 64 + +enum cyttsp5_device_type { + DEVICE_MT, + DEVICE_BTN, + DEVICE_PROXIMITY, + DEVICE_TYPE_MAX, +}; + +struct cyttsp5_device_pdata_func { + void * (*create_and_get_pdata)(struct device_node *); + void (*free_pdata)(void *); +}; + +struct cyttsp5_pdata_ptr { + void **pdata; +}; + +#ifdef ENABLE_VIRTUAL_KEYS +static struct kobject *board_properties_kobj; + +struct cyttsp5_virtual_keys { + struct kobj_attribute kobj_attr; + u16 *data; + int size; +}; +#endif + +struct cyttsp5_extended_mt_platform_data { + struct cyttsp5_mt_platform_data pdata; +#ifdef ENABLE_VIRTUAL_KEYS + struct cyttsp5_virtual_keys vkeys; +#endif +}; + +static inline int get_inp_dev_name(struct device_node *dev_node, + const char **inp_dev_name) +{ + return of_property_read_string(dev_node, "cy,inp_dev_name", + inp_dev_name); +} + +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); +} + +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, "cy,abs", &size); + if (IS_ERR_OR_NULL(abs)) + return (void *)abs; + + /* Check for valid abs size */ + if (size % CY_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); +} + +static void free_touch_framework(struct touch_framework *frmwrk) +{ + kfree(frmwrk->abs); + kfree(frmwrk); +} + +#ifdef ENABLE_VIRTUAL_KEYS +#define VIRTUAL_KEY_ELEMENT_SIZE 5 +static ssize_t virtual_keys_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct cyttsp5_virtual_keys *vkeys = container_of(attr, + struct cyttsp5_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, CY_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; +} + +static int setup_virtual_keys(struct device_node *dev_node, + const char *inp_dev_name, struct cyttsp5_virtual_keys *vkeys) +{ + char *name; + u16 *data; + int size; + int rc; + + data = create_and_get_u16_array(dev_node, "cy,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 = S_IRUGO; + 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; +} + +static void free_virtual_keys(struct cyttsp5_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 + +static void *create_and_get_mt_pdata(struct device_node *dev_node) +{ + struct cyttsp5_extended_mt_platform_data *ext_pdata; + struct cyttsp5_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, "cy,flags", &value); + if (!rc) + pdata->flags = value; + + rc = of_property_read_u32(dev_node, "cy,vkeys_x", &value); + if (!rc) + pdata->vkeys_x = value; + + rc = of_property_read_u32(dev_node, "cy,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); +} + +static void free_mt_pdata(void *pdata) +{ + struct cyttsp5_mt_platform_data *mt_pdata = + (struct cyttsp5_mt_platform_data *)pdata; + struct cyttsp5_extended_mt_platform_data *ext_mt_pdata = + container_of(mt_pdata, + struct cyttsp5_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); +} + +static void *create_and_get_btn_pdata(struct device_node *dev_node) +{ + struct cyttsp5_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); +} + +static void free_btn_pdata(void *pdata) +{ + struct cyttsp5_btn_platform_data *btn_pdata = + (struct cyttsp5_btn_platform_data *)pdata; + + kfree(btn_pdata); +} + +static void *create_and_get_proximity_pdata(struct device_node *dev_node) +{ + struct cyttsp5_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); +} + +static void free_proximity_pdata(void *pdata) +{ + struct cyttsp5_proximity_platform_data *proximity_pdata = + (struct cyttsp5_proximity_platform_data *)pdata; + + free_touch_framework(proximity_pdata->frmwrk); + + kfree(proximity_pdata); +} + +static struct cyttsp5_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 cyttsp5_pdata_ptr pdata_ptr[DEVICE_TYPE_MAX]; + +static const char *device_names[DEVICE_TYPE_MAX] = { + [DEVICE_MT] = "cy,mt", + [DEVICE_BTN] = "cy,btn", + [DEVICE_PROXIMITY] = "cy,proximity", +}; + +static void set_pdata_ptr(struct cyttsp5_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; +} + +static int get_device_type(struct device_node *dev_node, + enum cyttsp5_device_type *type) +{ + const char *name; + enum cyttsp5_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; +} + +static inline void *create_and_get_device_pdata(struct device_node *dev_node, + enum cyttsp5_device_type type) +{ + return device_pdata_funcs[type].create_and_get_pdata(dev_node); +} + +static inline void free_device_pdata(enum cyttsp5_device_type type) +{ + device_pdata_funcs[type].free_pdata(*pdata_ptr[type].pdata); +} + +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); +} + +static void free_touch_setting(struct touch_settings *setting) +{ + if (setting) { + kfree(setting->data); + kfree(setting); + } +} + +static char *touch_setting_names[CY_IC_GRPNUM_NUM] = { + NULL, /* CY_IC_GRPNUM_RESERVED */ + "cy,cmd_regs", /* CY_IC_GRPNUM_CMD_REGS */ + "cy,tch_rep", /* CY_IC_GRPNUM_TCH_REP */ + "cy,data_rec", /* CY_IC_GRPNUM_DATA_REC */ + "cy,test_rec", /* CY_IC_GRPNUM_TEST_REC */ + "cy,pcfg_rec", /* CY_IC_GRPNUM_PCFG_REC */ + "cy,tch_parm_val", /* CY_IC_GRPNUM_TCH_PARM_VAL */ + "cy,tch_parm_size", /* CY_IC_GRPNUM_TCH_PARM_SIZE */ + NULL, /* CY_IC_GRPNUM_RESERVED1 */ + NULL, /* CY_IC_GRPNUM_RESERVED2 */ + "cy,opcfg_rec", /* CY_IC_GRPNUM_OPCFG_REC */ + "cy,ddata_rec", /* CY_IC_GRPNUM_DDATA_REC */ + "cy,mdata_rec", /* CY_IC_GRPNUM_MDATA_REC */ + "cy,test_regs", /* CY_IC_GRPNUM_TEST_REGS */ + "cy,btn_keys", /* CY_IC_GRPNUM_BTN_KEYS */ + NULL, /* CY_IC_GRPNUM_TTHE_REGS */ +}; + +static struct cyttsp5_core_platform_data *create_and_get_core_pdata( + struct device_node *core_node) +{ + struct cyttsp5_core_platform_data *pdata; + u32 value; + int rc; + int i; + const char *name_tmp; + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + rc = -ENOMEM; + goto fail; + } + + /* get avdd power */ + memset(pdata->avdd_name, 0, sizeof(pdata->avdd_name)); + rc = of_property_read_string(core_node, "cyttsp5,avdd-name", &name_tmp); + if (!rc) { + pr_info("%s, avdd name from dt: %s", __func__, name_tmp); + if (strlen(name_tmp) < sizeof(pdata->avdd_name)) + strncpy(pdata->avdd_name, + name_tmp, sizeof(pdata->avdd_name)); + else + pr_info("%s, invalied avdd name length: %ld > %ld", __func__, + strlen(name_tmp), + sizeof(pdata->avdd_name)); + } + + /* get iovdd power */ + memset(pdata->iovdd_name, 0, sizeof(pdata->iovdd_name)); + rc = of_property_read_string(core_node, "cyttsp5,iovdd-name", &name_tmp); + if (!rc) { + pr_info("%s, iovdd name from dt: %s", __func__, name_tmp); + if (strlen(name_tmp) < sizeof(pdata->iovdd_name)) + strncpy(pdata->iovdd_name, + name_tmp, sizeof(pdata->iovdd_name)); + else + pr_info("%s, invalied iovdd name length: %ld > %ld", __func__, + strlen(name_tmp), + sizeof(pdata->iovdd_name)); + } + + /* Required fields */ + rc = of_get_named_gpio(core_node, "cy,irq_gpio", 0); + if (!gpio_is_valid(rc)) { + pdata->irq_gpio = -EINVAL; + pr_err("%s, invalid irq-gpio in dt: %d", __func__, rc); + goto fail_free; + } + pdata->irq_gpio = rc; + + rc = of_property_read_u32(core_node, "cy,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 + */ + rc = of_get_named_gpio(core_node, "cy,rst_gpio", 0); + if (!gpio_is_valid(rc)) { + pdata->rst_gpio = -EINVAL; + pr_err("%s, invalid rst-gpio in dt: %d", __func__, rc); + goto fail_free; + } + pdata->rst_gpio = rc; + + rc = of_property_read_u32(core_node, "cy,level_irq_udelay", &value); + if (!rc) + pdata->level_irq_udelay = value; + + rc = of_property_read_u32(core_node, "cy,vendor_id", &value); + if (!rc) + pdata->vendor_id = value; + + rc = of_property_read_u32(core_node, "cy,product_id", &value); + if (!rc) + pdata->product_id = value; + + rc = of_property_read_u32(core_node, "cy,flags", &value); + if (!rc) + pdata->flags = value; + + rc = of_property_read_u32(core_node, "cy,easy_wakeup_gesture", &value); + if (!rc) + pdata->easy_wakeup_gesture = (u8)value; + + rc = of_property_read_string(core_node, "cy,class-entry-name", + &pdata->class_entry_name); + if (!rc) + pr_notice("%s, class entry name '%s'\n", __func__, pdata->class_entry_name); + + 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_register:%d level_irq_udelay:%d vendor_id:%d product_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 = cyttsp5_xres; + pdata->init = cyttsp5_init; + pdata->power = cyttsp5_power; + pdata->detect = cyttsp5_detect; + pdata->irq_stat = cyttsp5_irq_stat; + + 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); +} + +static void free_core_pdata(void *pdata) +{ + struct cyttsp5_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); +} + +int cyttsp5_devtree_create_and_get_pdata(struct device *adap_dev) +{ + struct cyttsp5_platform_data *pdata; + struct device_node *core_node, *dev_node, *dev_node_fail; + enum cyttsp5_device_type type; + int count = 0; + int rc = 0; + const char *name_tmp; + + 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); + + /* get ic name */ + rc = of_property_read_string(adap_dev->of_node, "ic_name", &name_tmp); + if (rc) { + pr_err("%s, get ic_name failed", __func__); + return rc; + } else { + pr_info("%s, ic_name form dt: %s", __func__, name_tmp); + strncpy(pdata->ic_name, name_tmp, sizeof(pdata->ic_name)); + } + + /* 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); + + pdata->core_pdata = create_and_get_core_pdata(core_node); + if (IS_ERR(pdata->core_pdata)) { + rc = PTR_ERR(pdata->core_pdata); + break; + } + + /* 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 = &_cyttsp5_loader_platform_data; + } + + pr_debug("%s: %d child node(s) found\n", __func__, count); + + return rc; +} +EXPORT_SYMBOL_GPL(cyttsp5_devtree_create_and_get_pdata); + +int cyttsp5_devtree_clean_pdata(struct device *adap_dev) +{ + struct cyttsp5_platform_data *pdata; + struct device_node *core_node, *dev_node; + enum cyttsp5_device_type type; + int rc = 0; + + if (!adap_dev->of_node) + return 0; + + pdata = dev_get_platdata(adap_dev); + set_pdata_ptr(pdata); + for_each_child_of_node(adap_dev->of_node, core_node) { + free_core_pdata(pdata->core_pdata); + 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); + } + } + + return rc; +} +EXPORT_SYMBOL_GPL(cyttsp5_devtree_clean_pdata); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product DeviceTree Driver"); +MODULE_AUTHOR("Parade Technologies "); diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_i2c.c b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_i2c.c new file mode 100644 index 000000000000..7f12671468e5 --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_i2c.c @@ -0,0 +1,223 @@ +/* + * cyttsp5_i2c.c + * Parade TrueTouch(TM) Standard Product V5 I2C Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * CYTMA5XX + * CYTMA448 + * CYTMA445A + * CYTT21XXX + * CYTT31XXX + * + * Copyright (C) 2015 Parade Technologies + * Copyright (C) 2012-2015 Cypress Semiconductor + * + * 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 + * + */ + +#include "cyttsp5_regs.h" + +#include +#include + +#define CY_I2C_DATA_SIZE (2 * 256) + +static int cyttsp5_i2c_read_default(struct device *dev, void *buf, int size) +{ + struct i2c_client *client = to_i2c_client(dev); + int rc; + + if (!buf || !size || size > CY_I2C_DATA_SIZE) + return -EINVAL; + + rc = i2c_master_recv(client, buf, size); + + return (rc < 0) ? rc : rc != size ? -EIO : 0; +} + +static int cyttsp5_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 >= CY_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; +} + +static int cyttsp5_i2c_write_read_specific(struct device *dev, u8 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; + + if (!write_buf || !write_len) + 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 = cyttsp5_i2c_read_default_nosize(dev, read_buf, + CY_I2C_DATA_SIZE); + + return rc; +} + +static struct cyttsp5_bus_ops cyttsp5_i2c_bus_ops = { + .bustype = BUS_I2C, + .read_default = cyttsp5_i2c_read_default, + .read_default_nosize = cyttsp5_i2c_read_default_nosize, + .write_read_specific = cyttsp5_i2c_write_read_specific, +}; + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT +static const struct of_device_id cyttsp5_i2c_of_match[] = { + { .compatible = "cy,cyttsp5_i2c_adapter", }, + { } +}; +MODULE_DEVICE_TABLE(of, cyttsp5_i2c_of_match); +#endif + +static int cyttsp5_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *i2c_id) +{ + struct device *dev = &client->dev; +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT + const struct of_device_id *match; +#endif + int rc; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(dev, "I2C functionality not Supported\n"); + return -EIO; + } + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT + match = of_match_device(of_match_ptr(cyttsp5_i2c_of_match), dev); + if (match) { + rc = cyttsp5_devtree_create_and_get_pdata(dev); + if (rc < 0) + return rc; + } +#endif + + rc = cyttsp5_probe(&cyttsp5_i2c_bus_ops, &client->dev, client->irq, + CY_I2C_DATA_SIZE); + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT + if (rc && match) + cyttsp5_devtree_clean_pdata(dev); +#endif + + return rc; +} + +static int cyttsp5_i2c_remove(struct i2c_client *client) +{ +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT + struct device *dev = &client->dev; + const struct of_device_id *match; +#endif + struct cyttsp5_core_data *cd = i2c_get_clientdata(client); + + cyttsp5_release(cd); + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT + match = of_match_device(of_match_ptr(cyttsp5_i2c_of_match), dev); + if (match) + cyttsp5_devtree_clean_pdata(dev); +#endif + + return 0; +} + +static const struct i2c_device_id cyttsp5_i2c_id[] = { + { CYTTSP5_I2C_NAME, 0, }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cyttsp5_i2c_id); + +static struct i2c_driver cyttsp5_i2c_driver = { + .driver = { + .name = CYTTSP5_I2C_NAME, + .owner = THIS_MODULE, + .pm = &cyttsp5_pm_ops, +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT + .of_match_table = cyttsp5_i2c_of_match, +#endif + }, + .probe = cyttsp5_i2c_probe, + .remove = cyttsp5_i2c_remove, + .id_table = cyttsp5_i2c_id, +}; + +#if (KERNEL_VERSION(3, 3, 0) <= LINUX_VERSION_CODE) +module_i2c_driver(cyttsp5_i2c_driver); +#else +static int __init cyttsp5_i2c_init(void) +{ + int rc = i2c_add_driver(&cyttsp5_i2c_driver); + + pr_info("%s: Parade TTSP I2C Driver (Built %s) rc=%d\n", + __func__, CY_DRIVER_VERSION, rc); + return rc; +} +module_init(cyttsp5_i2c_init); + +static void __exit cyttsp5_i2c_exit(void) +{ + i2c_del_driver(&cyttsp5_i2c_driver); +} +module_exit(cyttsp5_i2c_exit); +#endif + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product I2C driver"); +MODULE_AUTHOR("Parade Technologies "); diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_loader.c b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_loader.c new file mode 100644 index 000000000000..643d15462c5c --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_loader.c @@ -0,0 +1,1962 @@ + /* + * cyttsp5_loader.c + * Parade TrueTouch(TM) Standard Product V5 FW Loader Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * CYTMA5XX + * CYTMA448 + * CYTMA445A + * CYTT21XXX + * CYTT31XXX + * + * Copyright (C) 2015 Parade Technologies + * Copyright (C) 2012-2015 Cypress Semiconductor, Inc. + * + * 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 + * + */ + +#include "cyttsp5_regs.h" +#include + +#define CYTTSP5_LOADER_NAME "cyttsp5_loader" +#define CY_FW_MANUAL_UPGRADE_FILE_NAME "cyttsp5_fw_manual_upgrade" + +/* Enable UPGRADE_FW_AND_CONFIG_IN_PROBE definition + * to perform FW and config upgrade during probe + * instead of scheduling a work for it + */ +/* #define UPGRADE_FW_AND_CONFIG_IN_PROBE */ + +#define CYTTSP5_AUTO_LOAD_FOR_CORRUPTED_FW 1 +#define CYTTSP5_LOADER_FW_UPGRADE_RETRY_COUNT 3 + +#ifndef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE +#define CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE +#endif + + +#define CYTTSP5_FW_UPGRADE 1 + +#define CYTTSP5_TTCONFIG_UPGRADE 0 + +static const u8 cyttsp5_security_key[] = { + 0xA5, 0x01, 0x02, 0x03, 0xFF, 0xFE, 0xFD, 0x5A +}; + +/* Timeout values in ms. */ +#define CY_LDR_REQUEST_EXCLUSIVE_TIMEOUT 500 +#define CY_LDR_SWITCH_TO_APP_MODE_TIMEOUT 300 + +#define CY_MAX_STATUS_SIZE 32 + +#define CY_DATA_MAX_ROW_SIZE 256 +#define CY_DATA_ROW_SIZE 128 + +#define CY_ARRAY_ID_OFFSET 0 +#define CY_ROW_NUM_OFFSET 1 +#define CY_ROW_SIZE_OFFSET 3 +#define CY_ROW_DATA_OFFSET 5 + +#define CY_POST_TT_CFG_CRC_MASK 0x2 + +struct cyttsp5_loader_data { + struct device *dev; + struct cyttsp5_sysinfo *si; + u8 status_buf[CY_MAX_STATUS_SIZE]; + struct completion int_running; + struct completion calibration_complete; +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE + struct completion builtin_bin_fw_complete; + int builtin_bin_fw_status; + bool is_manual_upgrade_enabled; +#endif + struct work_struct fw_and_config_upgrade; + struct work_struct calibration_work; + struct work_struct bl_from_file; + struct cyttsp5_loader_platform_data *loader_pdata; +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE + struct mutex config_lock; + u8 *config_data; + int config_size; + bool config_loading; +#endif +}; + +struct cyttsp5_dev_id { + u32 silicon_id; + u8 rev_id; + u32 bl_ver; +}; + +struct cyttsp5_hex_image { + u8 array_id; + u16 row_num; + u16 row_size; + u8 row_data[CY_DATA_ROW_SIZE]; +} __packed; + +static struct cyttsp5_core_commands *cmd; + +static struct cyttsp5_module loader_module; + +static inline struct cyttsp5_loader_data *cyttsp5_get_loader_data( + struct device *dev) +{ + return cyttsp5_get_module_data(dev, &loader_module); +} + +#if CYTTSP5_FW_UPGRADE \ + || defined(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE) +static u8 cyttsp5_get_panel_id(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + return cd->panel_id; +} +#endif + +#if CYTTSP5_FW_UPGRADE || CYTTSP5_TTCONFIG_UPGRADE +/* + * return code: + * -1: Do not upgrade firmware + * 0: Version info same, let caller decide + * 1: Do a firmware upgrade + */ +static int cyttsp5_check_firmware_version(struct device *dev, + u32 fw_ver_new, u32 fw_revctrl_new) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + u32 fw_ver_img; + u32 fw_revctrl_img; + + fw_ver_img = ld->si->cydata.fw_ver_major << 8; + fw_ver_img += ld->si->cydata.fw_ver_minor; + + parade_debug(dev, DEBUG_LEVEL_1, + "%s: img vers:0x%04X new vers:0x%04X\n", __func__, + fw_ver_img, fw_ver_new); + + if (fw_ver_new > fw_ver_img) { + parade_debug(dev, DEBUG_LEVEL_1, + "%s: Image is newer, will upgrade\n", __func__); + return 1; + } + + if (fw_ver_new < fw_ver_img) { + parade_debug(dev, DEBUG_LEVEL_1, + "%s: Image is older, will NOT upgrade\n", __func__); + return -1; + } + + fw_revctrl_img = ld->si->cydata.revctrl; + + parade_debug(dev, DEBUG_LEVEL_1, + "%s: img revctrl:0x%04X new revctrl:0x%04X\n", + __func__, fw_revctrl_img, fw_revctrl_new); + + if (fw_revctrl_new > fw_revctrl_img) { + parade_debug(dev, DEBUG_LEVEL_1, + "%s: Image is newer, will upgrade\n", __func__); + return 1; + } + + if (fw_revctrl_new < fw_revctrl_img) { + parade_debug(dev, DEBUG_LEVEL_1, + "%s: Image is older, will NOT upgrade\n", __func__); + return -1; + } + + return 0; +} + +static void cyttsp5_calibrate_idacs(struct work_struct *calibration_work) +{ + struct cyttsp5_loader_data *ld = container_of(calibration_work, + struct cyttsp5_loader_data, calibration_work); + struct device *dev = ld->dev; + u8 mode; + u8 status; + int rc; + + rc = cmd->request_exclusive(dev, CY_LDR_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) + goto exit; + + rc = cmd->nonhid_cmd->suspend_scanning(dev, 0); + if (rc < 0) + goto release; + + for (mode = 0; mode < 3; mode++) { + rc = cmd->nonhid_cmd->calibrate_idacs(dev, 0, mode, &status); + if (rc < 0) + goto release; + } + + rc = cmd->nonhid_cmd->resume_scanning(dev, 0); + if (rc < 0) + goto release; + + parade_debug(dev, DEBUG_LEVEL_1, "%s: Calibration Done\n", __func__); + +release: + cmd->release_exclusive(dev); +exit: + complete(&ld->calibration_complete); +} + +static int cyttsp5_calibration_attention(struct device *dev) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + int rc = 0; + + schedule_work(&ld->calibration_work); + + cmd->unsubscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_LOADER_NAME, + cyttsp5_calibration_attention, 0); + + return rc; +} + + +#endif /* CYTTSP5_FW_UPGRADE || CYTTSP5_TTCONFIG_UPGRADE */ + +#if CYTTSP5_FW_UPGRADE +static u8 *cyttsp5_get_row_(struct device *dev, u8 *row_buf, + u8 *image_buf, int size) +{ + memcpy(row_buf, image_buf, size); + return image_buf + size; +} + +static int cyttsp5_ldr_enter_(struct device *dev, struct cyttsp5_dev_id *dev_id) +{ + int rc; + u8 return_data[8]; + u8 mode; + + dev_id->silicon_id = 0; + dev_id->rev_id = 0; + dev_id->bl_ver = 0; + + cmd->request_reset(dev); + + rc = cmd->request_get_mode(dev, 0, &mode); + if (rc < 0) + return rc; + + if (mode == CY_MODE_UNKNOWN) + return -EINVAL; + + if (mode == CY_MODE_OPERATIONAL) { + rc = cmd->nonhid_cmd->start_bl(dev, 0); + if (rc < 0) + return rc; + } + + rc = cmd->nonhid_cmd->get_bl_info(dev, 0, return_data); + if (rc < 0) + 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; +} + +static int cyttsp5_ldr_init_(struct device *dev, + struct cyttsp5_hex_image *row_image) +{ + return cmd->nonhid_cmd->initiate_bl(dev, 0, 8, + (u8 *)cyttsp5_security_key, row_image->row_size, + row_image->row_data); +} + +static int cyttsp5_ldr_parse_row_(struct device *dev, u8 *row_buf, + struct cyttsp5_hex_image *row_image) +{ + int rc = 0; + + row_image->array_id = row_buf[CY_ARRAY_ID_OFFSET]; + row_image->row_num = get_unaligned_be16(&row_buf[CY_ROW_NUM_OFFSET]); + row_image->row_size = get_unaligned_be16(&row_buf[CY_ROW_SIZE_OFFSET]); + + if (row_image->row_size > ARRAY_SIZE(row_image->row_data)) { + dev_err(dev, "%s: row data buffer overflow\n", __func__); + rc = -EOVERFLOW; + goto cyttsp5_ldr_parse_row_exit; + } + + memcpy(row_image->row_data, &row_buf[CY_ROW_DATA_OFFSET], + row_image->row_size); +cyttsp5_ldr_parse_row_exit: + return rc; +} + +static int cyttsp5_ldr_prog_row_(struct device *dev, + struct cyttsp5_hex_image *row_image) +{ + u16 length = row_image->row_size + 3; + int ret = 0; + u8 *data = NULL; + u8 offset = 0; + + data = (u8 *) kmalloc(length, GFP_KERNEL); + if(!data) { + dev_err(dev, "%s: failed to kmalloc\n", __func__); + 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); + ret = cmd->nonhid_cmd->prog_and_verify(dev, 0, length, data); + + kfree(data); + return ret; +} + +static int cyttsp5_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; +} + +static int cyttsp5_ldr_exit_(struct device *dev) +{ + return cmd->nonhid_cmd->launch_app(dev, 0); +} + +static int cyttsp5_load_app_(struct device *dev, const u8 *fw, int fw_size) +{ + struct cyttsp5_dev_id *dev_id; + struct cyttsp5_hex_image *row_image; + u8 *row_buf; + size_t image_rec_size; + size_t row_buf_size = CY_DATA_MAX_ROW_SIZE; + int row_count = 0; + u8 *p; + u8 *last_row; + int rc; + int rc_tmp; + + image_rec_size = sizeof(struct cyttsp5_hex_image); + if (fw_size % image_rec_size != 0) { + dev_err(dev, "%s: Firmware image is misaligned\n", __func__); + rc = -EINVAL; + goto _cyttsp5_load_app_error; + } + + dev_info(dev, "%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 cyttsp5_hex_image), GFP_KERNEL); + dev_id = kzalloc(sizeof(struct cyttsp5_dev_id), GFP_KERNEL); + if (!row_buf || !row_image || !dev_id) { + rc = -ENOMEM; + goto _cyttsp5_load_app_exit; + } + + cmd->request_stop_wd(dev); + + dev_info(dev, "%s: Send BL Loader Enter\n", __func__); +#ifdef TTHE_TUNER_SUPPORT + cmd->request_tthe_print(dev, NULL, 0, "Send BL Loader Enter"); +#endif + rc = cyttsp5_ldr_enter_(dev, dev_id); + if (rc) { + dev_err(dev, "%s: Error cannot start Loader (ret=%d)\n", + __func__, rc); + goto _cyttsp5_load_app_exit; + } + parade_debug(dev, DEBUG_LEVEL_2, "%s: dev: silicon id=%08X rev=%02X bl=%08X\n", + __func__, dev_id->silicon_id, + dev_id->rev_id, dev_id->bl_ver); + + /* get last row */ + last_row = (u8 *)fw + fw_size - image_rec_size; + cyttsp5_get_row_(dev, row_buf, last_row, image_rec_size); + cyttsp5_ldr_parse_row_(dev, row_buf, row_image); + + /* initialise bootloader */ + rc = cyttsp5_ldr_init_(dev, row_image); + if (rc) { + dev_err(dev, "%s: Error cannot init Loader (ret=%d)\n", + __func__, rc); + goto _cyttsp5_load_app_exit; + } + + dev_info(dev, "%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 */ + parade_debug(dev, DEBUG_LEVEL_1, "%s: read row=%d\n", + __func__, ++row_count); + memset(row_buf, 0, row_buf_size); + p = cyttsp5_get_row_(dev, row_buf, p, image_rec_size); + + /* Parse row */ + parade_debug(dev, DEBUG_LEVEL_2, "%s: p=%p buf=%p buf[0]=%02X\n", + __func__, p, row_buf, row_buf[0]); + rc = cyttsp5_ldr_parse_row_(dev, row_buf, row_image); + parade_debug(dev, DEBUG_LEVEL_2, "%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) { + dev_err(dev, "%s: Parse Row Error (a=%d r=%d ret=%d\n", + __func__, row_image->array_id, + row_image->row_num, rc); + goto _cyttsp5_load_app_exit; + } else { + parade_debug(dev, DEBUG_LEVEL_2, "%s: Parse Row (a=%d r=%d ret=%d\n", + __func__, row_image->array_id, + row_image->row_num, rc); + } + + /* program row */ + rc = cyttsp5_ldr_prog_row_(dev, row_image); + if (rc) { + dev_err(dev, "%s: Program Row Error (array=%d row=%d ret=%d)\n", + __func__, row_image->array_id, + row_image->row_num, rc); + goto _cyttsp5_load_app_exit; + } + + parade_debug(dev, DEBUG_LEVEL_2, "%s: array=%d row_cnt=%d row_num=%04X\n", + __func__, row_image->array_id, row_count, + row_image->row_num); + } + + /* exit loader */ + dev_info(dev, "%s: Send BL Loader Terminate\n", __func__); +#ifdef TTHE_TUNER_SUPPORT + cmd->request_tthe_print(dev, NULL, 0, "Send BL Loader Terminate"); +#endif + rc = cyttsp5_ldr_exit_(dev); + if (rc) { + dev_err(dev, "%s: Error on exit Loader (ret=%d)\n", + __func__, rc); + + /* verify app checksum */ + rc_tmp = cyttsp5_ldr_verify_chksum_(dev); + if (rc_tmp) + dev_err(dev, "%s: ldr_verify_chksum fail r=%d\n", + __func__, rc_tmp); + else + dev_info(dev, "%s: APP Checksum Verified\n", __func__); + } + +_cyttsp5_load_app_exit: + kfree(row_buf); + kfree(row_image); + kfree(dev_id); +_cyttsp5_load_app_error: + return rc; +} + +static int cyttsp5_upgrade_firmware(struct device *dev, const u8 *fw_img, + int fw_size) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + int retry = CYTTSP5_LOADER_FW_UPGRADE_RETRY_COUNT; + bool wait_for_calibration_complete = false; + int rc; + + pm_runtime_get_sync(dev); + + rc = cmd->request_exclusive(dev, CY_LDR_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) + goto exit; + + while (retry--) { + rc = cyttsp5_load_app_(dev, fw_img, fw_size); + if (rc < 0) + dev_err(dev, "%s: Firmware update failed rc=%d, retry:%d\n", + __func__, rc, retry); + else + break; + msleep(20); + } + if (rc < 0) { + dev_err(dev, "%s: Firmware update failed with error code %d\n", + __func__, rc); + } else if (ld->loader_pdata && + (ld->loader_pdata->flags + & CY_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 */ + parade_debug(dev, DEBUG_LEVEL_2, "%s: Adding callback for calibration\n", + __func__); + rc = cmd->subscribe_attention(dev, CY_ATTEN_STARTUP, + CYTTSP5_LOADER_NAME, cyttsp5_calibration_attention, 0); + if (rc) { + dev_err(dev, "%s: Failed adding callback for calibration\n", + __func__); + dev_err(dev, "%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_restart(dev, true); + + pm_runtime_put_sync(dev); + + if (wait_for_calibration_complete) + wait_for_completion(&ld->calibration_complete); + + return rc; +} + +static int cyttsp5_loader_attention(struct device *dev) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + + complete(&ld->int_running); + return 0; +} +#endif /* CYTTSP5_FW_UPGRADE */ + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE +#if 0 +static int cyttsp5_check_firmware_version_platform(struct device *dev, + struct cyttsp5_touch_firmware *fw) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + u32 fw_ver_new; + u32 fw_revctrl_new; + int upgrade; + + if (!ld->si) { + dev_info(dev, "%s: No firmware infomation found, device FW may be corrupted\n", + __func__); + return CYTTSP5_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); + + upgrade = cyttsp5_check_firmware_version(dev, fw_ver_new, + fw_revctrl_new); + + if (upgrade > 0) + return 1; + + return 0; +} +#endif +static int cyttsp5_check_firmware_config_version(struct device *dev, u16 fw_config_ver_new) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + u16 fw_config_ver_img; + + fw_config_ver_img = ld->si->cydata.fw_ver_conf; + + dev_dbg(dev, "%s: img config vers:0x%04X new config vers:0x%04X\n", + __func__, fw_config_ver_img, fw_config_ver_new); + + if (fw_config_ver_new != fw_config_ver_img) { + dev_info(dev, "%s: Image config is different, will upgrade\n", __func__); + return 1; + } + + if (fw_config_ver_new < fw_config_ver_img) { + dev_dbg(dev, "%s: Image is config older, will NOT upgrade\n", + __func__); + return -1; + } + + return 0; +} + +static int cyttsp5_check_firmware_version_platform(struct device *dev, + struct cyttsp5_touch_firmware *fw) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + u32 fw_ver_new; + u32 fw_revctrl_new; + u16 fw_config_ver_new; + int upgrade; + + if (!ld->si) { + dev_info(dev, "%s: No firmware infomation found, device FW may be corrupted\n", + __func__); + return CYTTSP5_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); + + fw_config_ver_new = get_unaligned_be16(fw->ver + 16); + + upgrade = cyttsp5_check_firmware_version(dev, fw_ver_new, + fw_revctrl_new); + + if (upgrade <= 0) + upgrade = cyttsp5_check_firmware_config_version(dev, fw_config_ver_new); + + if (upgrade > 0) + return 1; + + return 0; +} + +static struct cyttsp5_touch_firmware *cyttsp5_get_platform_firmware( + struct device *dev) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + struct cyttsp5_touch_firmware **fws; + struct cyttsp5_touch_firmware *fw; + u8 panel_id; + + panel_id = cyttsp5_get_panel_id(dev); + if (panel_id == PANEL_ID_NOT_ENABLED) { + parade_debug(dev, DEBUG_LEVEL_1, "%s: Panel ID not enabled, using legacy firmware\n", + __func__); + return ld->loader_pdata->fw; + } + + fws = ld->loader_pdata->fws; + if (!fws) { + dev_err(dev, "%s: No firmwares provided\n", __func__); + return NULL; + } + + /* Find FW according to the Panel ID */ + while ((fw = *fws++)) { + if (fw->panel_id == panel_id) { + parade_debug(dev, DEBUG_LEVEL_1, "%s: Found matching fw:%p with Panel ID: 0x%02X\n", + __func__, fw, fw->panel_id); + return fw; + } + parade_debug(dev, DEBUG_LEVEL_2, "%s: Found mismatching fw:%p with Panel ID: 0x%02X\n", + __func__, fw, fw->panel_id); + } + + return NULL; +} + +static int upgrade_firmware_from_platform(struct device *dev, + bool forced) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + struct cyttsp5_touch_firmware *fw; + int rc = -ENODEV; + int upgrade; +#ifdef PT_PANEL_ID_FROM_MFG + int retry = 3; + +retry_bl: +#endif + if (!ld->loader_pdata) { + dev_err(dev, "%s: No loader platform data\n", __func__); + return rc; + } + + fw = cyttsp5_get_platform_firmware(dev); + if (!fw || !fw->img || !fw->size) { + dev_err(dev, "%s: No platform firmware\n", __func__); + return rc; + } + + if (!fw->ver || !fw->vsize) { + dev_err(dev, "%s: No platform firmware version\n", + __func__); + return rc; + } + + if (forced) + upgrade = forced; + else + upgrade = cyttsp5_check_firmware_version_platform(dev, fw); + + if (upgrade) { + rc = cyttsp5_upgrade_firmware(dev, fw->img, fw->size); +#ifdef PT_PANEL_ID_FROM_MFG + /* An extra BL may be needed if default PID was wrong choice */ + if (!ld->si && retry--) { + dev_warn(dev, "%s: 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; + } +#endif + } + + return rc; +} +#endif /* CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE */ + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE +static void _cyttsp5_firmware_cont(const struct firmware *fw, void *context) +{ + struct device *dev = context; + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + u8 header_size = 0; + + if (!fw) + goto cyttsp5_firmware_cont_exit; + + if (!fw->data || !fw->size) { + dev_err(dev, "%s: No firmware received\n", __func__); + goto cyttsp5_firmware_cont_release_exit; + } + + header_size = fw->data[0]; + if (header_size >= (fw->size + 1)) { + dev_err(dev, "%s: Firmware format is invalid\n", __func__); + goto cyttsp5_firmware_cont_release_exit; + } + + cyttsp5_upgrade_firmware(dev, &(fw->data[header_size + 1]), + fw->size - (header_size + 1)); + +cyttsp5_firmware_cont_release_exit: + release_firmware(fw); + +cyttsp5_firmware_cont_exit: + ld->is_manual_upgrade_enabled = 0; +} + +#if 0 +static int cyttsp5_check_firmware_version_builtin(struct device *dev, + const struct firmware *fw) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + u32 fw_ver_new; + u32 fw_revctrl_new; + int upgrade; + + if (!ld->si) { + dev_info(dev, "%s: No firmware infomation found, device FW may be corrupted\n", + __func__); + return CYTTSP5_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); + + upgrade = cyttsp5_check_firmware_version(dev, fw_ver_new, + fw_revctrl_new); + + if (upgrade > 0) + return 1; + + return 0; +} + +#else + +static int cyttsp5_check_firmware_config_version(struct device *dev, u16 fw_config_ver_new) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + u16 fw_config_ver_img; + + fw_config_ver_img = ld->si->cydata.fw_ver_conf; + + dev_dbg(dev, "%s: img config vers:0x%04X new config vers:0x%04X\n", + __func__, fw_config_ver_img, fw_config_ver_new); + + if (fw_config_ver_new != fw_config_ver_img) { + dev_info(dev, "%s: Image config is different, will upgrade\n", __func__); + return 1; + } + + if (fw_config_ver_new < fw_config_ver_img) { + dev_dbg(dev, "%s: Image is config older, will NOT upgrade\n", + __func__); + return -1; + } + + return 0; +} + +static int cyttsp5_check_firmware_version_builtin(struct device *dev, const struct firmware *fw) +{ +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); +#endif + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + u32 fw_ver_new; + u32 fw_revctrl_new; + u16 fw_config_ver_new; + int upgrade; + + if (!ld->si) { + dev_info(dev, + "%s: No firmware infomation found, device FW may be " + "corrupted\n", + __func__); + return CYTTSP5_AUTO_LOAD_FOR_CORRUPTED_FW; + } + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + if(cd->force_fw_upgrade == 1) { + cd->force_fw_upgrade = 0; + return 1; + } else { + return 0; + } +#endif + + fw_ver_new = get_unaligned_be16(fw->data + 3); + /* 4 middle bytes are not used */ + fw_revctrl_new = get_unaligned_be32(fw->data + 9); + + fw_config_ver_new = get_unaligned_be16(fw->data + 17); + + upgrade = cyttsp5_check_firmware_version(dev, fw_ver_new, fw_revctrl_new); + + if (upgrade <= 0) + upgrade = cyttsp5_check_firmware_config_version(dev, fw_config_ver_new); + + if (upgrade > 0) + return 1; + + return 0; +} + +#endif + +static void _cyttsp5_firmware_cont_builtin(const struct firmware *fw, + void *context) +{ + struct device *dev = context; + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + int upgrade; + + if (!fw) { + dev_info(dev, "%s: No builtin firmware\n", __func__); + goto _cyttsp5_firmware_cont_builtin_exit; + } + + if (!fw->data || !fw->size) { + dev_err(dev, "%s: Invalid builtin firmware\n", __func__); + goto _cyttsp5_firmware_cont_builtin_exit; + } + + parade_debug(dev, DEBUG_LEVEL_1, "%s: Found firmware\n", __func__); + + upgrade = cyttsp5_check_firmware_version_builtin(dev, fw); + if (upgrade) { + _cyttsp5_firmware_cont(fw, dev); + ld->builtin_bin_fw_status = 0; + complete(&ld->builtin_bin_fw_complete); + return; + } + +_cyttsp5_firmware_cont_builtin_exit: + release_firmware(fw); + + ld->builtin_bin_fw_status = -EINVAL; + complete(&ld->builtin_bin_fw_complete); +} + +static int upgrade_firmware_from_class(struct device *dev) +{ + int retval; + + parade_debug(dev, DEBUG_LEVEL_2, + "%s: Enabling firmware class loader\n", __func__); + + retval = request_firmware_nowait(THIS_MODULE, FW_ACTION_NOHOTPLUG, + CY_FW_MANUAL_UPGRADE_FILE_NAME, dev, GFP_KERNEL, dev, + _cyttsp5_firmware_cont); + if (retval < 0) { + dev_err(dev, "%s: Fail request firmware class file load\n", + __func__); + return retval; + } + + return 0; +} + +#ifdef CONFIG_CYTTSP5_CREATE_PT_BL_FROM_FILE +/******************************************************************************* + * 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 cyttsp5_core_data *cd = dev_get_drvdata(dev); + int fw_size = 0; + int rc = 0; + u8 *fw_img = NULL; + u8 header_size = 0; + + + fw_img = kzalloc(PT_PIP2_MAX_FILE_SIZE, GFP_KERNEL); + if (!fw_img) { + rc = -ENOMEM; + goto exit; + } + + rc = cmd->nonhid_cmd->read_us_file(dev, cd->pip2_us_file_path, + fw_img, &fw_size); + if (rc) { + dev_err(dev, "%s: No firmware provided to load\n", + __func__); + goto exit; + } + + if (!fw_img || !fw_size) { + dev_err(dev, "%s: No firmware received\n", __func__); + goto exit; + } + + header_size = fw_img[0]; + if (header_size >= (fw_size + 1)) { + dev_err(dev, "%s: Firmware format is invalid\n", __func__); + goto exit; + } + + cyttsp5_upgrade_firmware(dev, &(fw_img[header_size + 1]), + fw_size - (header_size + 1)); +exit: + kfree(fw_img); + return rc; +} + +/******************************************************************************* + * 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 cyttsp5_loader_data *ld = container_of(bl_from_file, + struct cyttsp5_loader_data, bl_from_file); + struct device *dev = ld->dev; + + _pt_pip1_bl_from_file(dev); +} + +/******************************************************************************* + * 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 cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + int rc = 0; + int read_size = 2; + u8 image[2]; + + ld->builtin_bin_fw_status = false; + + /* 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); + + schedule_work(&ld->bl_from_file); + return snprintf(buf, CY_MAX_PRBUF_SIZE, + "Status: %d\n" + "BL File: %s\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 > 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_pip_bl_from_file_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + const char *deli_space = " ", *deli_comma = ","; + char name_space[128]; + char *ptr_left = NULL, *ptr_right = name_space; + + memset(name_space, 0, 128); + memset(cd->pip2_us_file_path, 0, 128); + if (size <= 128) { + 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); + } else + dev_err(dev, "%s:Invalid input\n", __func__); + + + + dev_info(dev, "%s:Path=%s, ptr_right=%s\n", __func__, + ptr_left, ptr_right); + + 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(pt_bl_from_file, S_IRUGO | S_IWUSR, + pt_bl_from_file_show, pt_pip_bl_from_file_store); +#endif +/* + * Generates binary FW filename as following: + * - Panel ID not enabled: cyttsp5_fw.bin + * - Panel ID enabled: cyttsp5_fw_pidXX.bin + */ +static __maybe_unused char *generate_firmware_filename(struct device *dev) +{ + char *filename; + u8 panel_id; + +#define FILENAME_LEN_MAX 64 + filename = kzalloc(FILENAME_LEN_MAX, GFP_KERNEL); + if (!filename) + return NULL; + + panel_id = cyttsp5_get_panel_id(dev); + if (panel_id == PANEL_ID_NOT_ENABLED) + snprintf(filename, FILENAME_LEN_MAX, "%s", CY_FW_FILE_NAME); + else + snprintf(filename, FILENAME_LEN_MAX, "%s_pid%02X%s", + CY_FW_FILE_PREFIX, panel_id, CY_FW_FILE_SUFFIX); + + parade_debug(dev, DEBUG_LEVEL_1, "%s: Filename: %s\n", + __func__, filename); + + return filename; +} + +static int upgrade_firmware_from_builtin(struct device *dev) +{ +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); +#endif + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + char *filename; + int retval; +#ifdef PT_PANEL_ID_FROM_MFG + int retry = 3; + +retry_bl: +#endif + + parade_debug(dev, DEBUG_LEVEL_2, + "%s: Enabling firmware class loader built-in\n", + __func__); + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + filename = kzalloc(CYTTSP5_FIRMWARE_NAME_MAX_LEN, GFP_KERNEL); + if (!filename) + return -ENOMEM; + + snprintf(filename, CYTTSP5_FIRMWARE_NAME_MAX_LEN, "%s", cd->firmware_name); +#else + filename = generate_firmware_filename(dev); +#endif + + if (!filename) + return -ENOMEM; + + retval = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + filename, dev, GFP_KERNEL, dev, + _cyttsp5_firmware_cont_builtin); + if (retval < 0) { + dev_err(dev, "%s: Fail request firmware class file load\n", + __func__); + goto exit; + } + + /* wait until FW binary upgrade finishes */ + wait_for_completion(&ld->builtin_bin_fw_complete); +#ifdef PT_PANEL_ID_FROM_MFG + /* An extra BL may be needed if default PID was wrong choice */ + if (!ld->si && retry--) { + dev_warn(dev, "%s: 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; + } +#endif + retval = ld->builtin_bin_fw_status; + +exit: + kfree(filename); + + return retval; +} +#endif /* CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE */ + +#if CYTTSP5_TTCONFIG_UPGRADE +static int cyttsp5_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_conf_block(dev, 0, row_number, + row_size, ebid, data, (u8 *)cyttsp5_security_key, + &actual_write_len); + if (rc) { + dev_err(dev, "%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) { + dev_err(dev, "%s: Fail Put EBID=%d row=%d wrong write size=%d\n", + __func__, ebid, row_number, actual_write_len); + rc = -EINVAL; + } + + return rc; +} + +static int cyttsp5_upgrade_ttconfig(struct device *dev, + const u8 *ttconfig_data, int ttconfig_size) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + bool wait_for_calibration_complete = false; + u8 ebid = CY_TCH_PARM_EBID; + u16 row_size = CY_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; + parade_debug(dev, DEBUG_LEVEL_1, "%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, CY_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++) { + parade_debug(dev, DEBUG_LEVEL_1, "%s: row=%d size=%d\n", + __func__, i, row_size); + rc = cyttsp5_write_config_row_(dev, ebid, i, row_size, + row_buf); + if (rc) { + dev_err(dev, "%s: Fail put row=%d r=%d\n", + __func__, i, rc); + break; + } + row_buf += row_size; + } + if (!rc) { + residue = table_size % row_size; + parade_debug(dev, DEBUG_LEVEL_1, "%s: row=%d size=%d\n", + __func__, i, residue); + rc = cyttsp5_write_config_row_(dev, ebid, i, residue, + row_buf); + row_count++; + if (rc) + dev_err(dev, "%s: Fail put row=%d r=%d\n", + __func__, i, rc); + } + + if (!rc) + parade_debug(dev, DEBUG_LEVEL_1, + "%s: TT_CFG updated: rows:%d bytes:%d\n", + __func__, row_count, table_size); + + rc = cmd->nonhid_cmd->verify_config_block_crc(dev, 0, ebid, + &verify_crc_status, &calculated_crc, &stored_crc); + if (rc || verify_crc_status) + dev_err(dev, "%s: CRC Failed, ebid=%d, status=%d, scrc=%X ccrc=%X\n", + __func__, ebid, verify_crc_status, + calculated_crc, stored_crc); + else + parade_debug(dev, DEBUG_LEVEL_1, + "%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 + & CY_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 */ + parade_debug(dev, DEBUG_LEVEL_2, "%s: Adding callback for calibration\n", + __func__); + rc = cmd->subscribe_attention(dev, CY_ATTEN_STARTUP, + CYTTSP5_LOADER_NAME, cyttsp5_calibration_attention, 0); + if (rc) { + dev_err(dev, "%s: Failed adding callback for calibration\n", + __func__); + dev_err(dev, "%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_restart(dev, true); + + pm_runtime_put_sync(dev); + + if (wait_for_calibration_complete) + wait_for_completion(&ld->calibration_complete); + + return rc; +} +#endif /* CYTTSP5_TTCONFIG_UPGRADE */ + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE +static int cyttsp5_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; +} + +static int cyttsp5_get_ttconfig_version(struct device *dev, + const u8 *ttconfig_data, int ttconfig_size, u16 *version) +{ + if (ttconfig_size < CY_TTCONFIG_VERSION_OFFSET + + CY_TTCONFIG_VERSION_SIZE) + return -EINVAL; + + *version = get_unaligned_le16( + &ttconfig_data[CY_TTCONFIG_VERSION_OFFSET]); + + return 0; +} + +static int cyttsp5_check_ttconfig_version(struct device *dev, + const u8 *ttconfig_data, int ttconfig_size) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + u16 cfg_crc_new; + int rc; + + if (!ld->si) + return 0; + + /* Check for config version */ + if (ld->loader_pdata->flags & + CY_LOADER_FLAG_CHECK_TTCONFIG_VERSION) { + u16 cfg_ver_new; + + rc = cyttsp5_get_ttconfig_version(dev, ttconfig_data, + ttconfig_size, &cfg_ver_new); + if (rc) + return 0; + + parade_debug(dev, DEBUG_LEVEL_1, "%s: img_ver:0x%04X new_ver:0x%04X\n", + __func__, ld->si->cydata.fw_ver_conf, cfg_ver_new); + + /* Check if config version is newer */ + if (cfg_ver_new > ld->si->cydata.fw_ver_conf) { + parade_debug(dev, DEBUG_LEVEL_1, "%s: Config version newer, will upgrade\n", + __func__); + return 1; + } + + parade_debug(dev, DEBUG_LEVEL_1, "%s: Config version is identical or older, will NOT upgrade\n", + __func__); + /* Check for config CRC */ + } else { + rc = cyttsp5_get_ttconfig_crc(dev, ttconfig_data, + ttconfig_size, &cfg_crc_new); + if (rc) + return 0; + + parade_debug(dev, DEBUG_LEVEL_1, "%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) { + parade_debug(dev, DEBUG_LEVEL_1, "%s: Config CRC different, will upgrade\n", + __func__); + return 1; + } + + parade_debug(dev, DEBUG_LEVEL_1, "%s: Config CRC equal, will NOT upgrade\n", + __func__); + } + + return 0; +} + +static int cyttsp5_check_ttconfig_version_platform(struct device *dev, + struct cyttsp5_touch_config *ttconfig) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + u32 fw_ver_config; + u32 fw_revctrl_config; + + if (!ld->si) { + dev_info(dev, "%s: No firmware infomation found, device 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 (cyttsp5_check_firmware_version(dev, fw_ver_config, + fw_revctrl_config)) { + dev_err(dev, "%s: FW versions mismatch\n", __func__); + return 0; + } + + /* Check PowerOn Self Test, TT_CFG CRC bit */ + if ((ld->si->cydata.post_code & CY_POST_TT_CFG_CRC_MASK) == 0) { + parade_debug(dev, DEBUG_LEVEL_1, "%s: POST, TT_CFG failed (%X), will upgrade\n", + __func__, ld->si->cydata.post_code); + return 1; + } + + return cyttsp5_check_ttconfig_version(dev, ttconfig->param_regs->data, + ttconfig->param_regs->size); +} + +static struct cyttsp5_touch_config *cyttsp5_get_platform_ttconfig( + struct device *dev) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + struct cyttsp5_touch_config **ttconfigs; + struct cyttsp5_touch_config *ttconfig; + u8 panel_id; + + panel_id = cyttsp5_get_panel_id(dev); + if (panel_id == PANEL_ID_NOT_ENABLED) { + /* TODO: Make debug message */ + dev_info(dev, "%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 */ + dev_info(dev, "%s: Found matching ttconfig:%p with Panel ID: 0x%02X\n", + __func__, ttconfig, ttconfig->panel_id); + return ttconfig; + } + parade_debug(dev, DEBUG_LEVEL_2, "%s: Found mismatching ttconfig:%p with Panel ID: 0x%02X\n", + __func__, ttconfig, ttconfig->panel_id); + } + + return NULL; +} + +static int upgrade_ttconfig_from_platform(struct device *dev) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + struct cyttsp5_touch_config *ttconfig; + struct touch_settings *param_regs; + struct cyttsp5_touch_fw; + int rc = -ENODEV; + int upgrade; + + if (!ld->loader_pdata) { + dev_info(dev, "%s: No loader platform data\n", __func__); + return rc; + } + + ttconfig = cyttsp5_get_platform_ttconfig(dev); + if (!ttconfig) { + dev_info(dev, "%s: No ttconfig data\n", __func__); + return rc; + } + + param_regs = ttconfig->param_regs; + if (!param_regs) { + dev_info(dev, "%s: No touch parameters\n", __func__); + return rc; + } + + if (!param_regs->data || !param_regs->size) { + dev_info(dev, "%s: Invalid touch parameters\n", __func__); + return rc; + } + + if (!ttconfig->fw_ver || !ttconfig->fw_vsize) { + dev_info(dev, "%s: Invalid FW version for touch parameters\n", + __func__); + return rc; + } + + upgrade = cyttsp5_check_ttconfig_version_platform(dev, ttconfig); + if (upgrade) + return cyttsp5_upgrade_ttconfig(dev, param_regs->data, + param_regs->size); + + return rc; +} +#endif /* CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE */ + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE +static ssize_t cyttsp5_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 cyttsp5_loader_data *data = cyttsp5_get_loader_data(dev); + u8 *p; + + parade_debug(dev, DEBUG_LEVEL_2, "%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 = S_IWUSR, + }, + .size = 0, + .write = cyttsp5_config_data_write, +}; + +static ssize_t cyttsp5_config_loading_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + bool config_loading; + + mutex_lock(&ld->config_lock); + config_loading = ld->config_loading; + mutex_unlock(&ld->config_lock); + + return sprintf(buf, "%d\n", config_loading); +} + +static int cyttsp5_verify_ttconfig_binary(struct device *dev, + u8 *bin_config_data, int bin_config_size, u8 **start, int *len) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + int header_size; + u16 config_size; + u32 fw_ver_config; + u32 fw_revctrl_config; + + if (!ld->si) { + dev_err(dev, "%s: No firmware infomation found, device 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) { + dev_err(dev, "%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 (cyttsp5_check_firmware_version(dev, fw_ver_config, + fw_revctrl_config)) { + dev_err(dev, "%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) { + dev_err(dev, "%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 + */ +static ssize_t cyttsp5_config_loading_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + long value; + u8 *start; + int length; + int rc; + + rc = kstrtol(buf, 10, &value); + if (rc < 0 || value < -1 || value > 1) { + dev_err(dev, "%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) { + dev_err(dev, "%s: No config data\n", __func__); + goto exit_free; + } + + rc = cyttsp5_verify_ttconfig_binary(dev, + ld->config_data, ld->config_size, + &start, &length); + if (rc) + goto exit_free; + + rc = cyttsp5_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, S_IRUSR | S_IWUSR, + cyttsp5_config_loading_show, cyttsp5_config_loading_store); +#endif /* CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE */ + +static void cyttsp5_fw_and_config_upgrade( + struct work_struct *fw_and_config_upgrade) +{ + struct cyttsp5_loader_data *ld = container_of(fw_and_config_upgrade, + struct cyttsp5_loader_data, fw_and_config_upgrade); + struct device *dev = ld->dev; + + ld->si = cmd->request_sysinfo(dev); + if (!ld->si) + dev_err(dev, "%s: Fail get sysinfo pointer from core\n", + __func__); +#if !CYTTSP5_FW_UPGRADE + dev_info(dev, "%s: No FW upgrade method selected!\n", __func__); +#endif + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE + if (!upgrade_firmware_from_platform(dev, false)) + return; +#endif + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE + if (!upgrade_firmware_from_builtin(dev)) + return; +#endif + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE + if (!upgrade_ttconfig_from_platform(dev)) + return; +#endif +} + +#if CYTTSP5_FW_UPGRADE +static int cyttsp5_fw_upgrade_cb(struct device *dev) +{ +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE + if (!upgrade_firmware_from_platform(dev, false)) + return 1; +#endif + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE + if (!upgrade_firmware_from_builtin(dev)) + return 1; +#endif + return 0; +} +#endif + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE +static ssize_t cyttsp5_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, S_IWUSR, + NULL, cyttsp5_forced_upgrade_store); +#endif + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE +static ssize_t cyttsp5_manual_upgrade_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + int rc; + + if (ld->is_manual_upgrade_enabled) + return -EBUSY; + + ld->is_manual_upgrade_enabled = 1; + + rc = upgrade_firmware_from_class(ld->dev); + + if (rc < 0) + ld->is_manual_upgrade_enabled = 0; + + return size; +} + +static DEVICE_ATTR(manual_upgrade, S_IWUSR, + NULL, cyttsp5_manual_upgrade_store); +#endif + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +static int cyttsp5_loader_firmware_update(struct device *dev, + char *fwname) +{ + struct cyttsp5_loader_data *ld = cyttsp5_get_loader_data(dev); + cyttsp5_fw_and_config_upgrade(&ld->fw_and_config_upgrade); + + return 0; +} +#endif + +static int cyttsp5_loader_probe(struct device *dev, void **data) +{ +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); +#endif + struct cyttsp5_loader_data *ld; + struct cyttsp5_platform_data *pdata = dev_get_platdata(dev); + int rc; + + if (!pdata || !pdata->loader_pdata) { + dev_err(dev, "%s: Missing platform data\n", __func__); + rc = -ENODEV; + goto error_no_pdata; + } + + ld = kzalloc(sizeof(*ld), GFP_KERNEL); + if (!ld) { + rc = -ENOMEM; + goto error_alloc_data_failed; + } + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE + rc = device_create_file(dev, &dev_attr_forced_upgrade); + if (rc) { + dev_err(dev, "%s: Error, could not create forced_upgrade\n", + __func__); + goto error_create_forced_upgrade; + } +#endif + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE + rc = device_create_file(dev, &dev_attr_manual_upgrade); + if (rc) { + dev_err(dev, "%s: Error, could not create manual_upgrade\n", + __func__); + goto error_create_manual_upgrade; + } +#endif + +#ifdef CONFIG_CYTTSP5_CREATE_PT_BL_FROM_FILE + rc = device_create_file(dev, &dev_attr_pt_bl_from_file); + if (rc) { + dev_err(dev, "%s: Error creating pt_bl_from_file\n", + __func__); + goto error_create_pt_bl_from_file; + } +#endif + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE + rc = device_create_file(dev, &dev_attr_config_loading); + if (rc) { + dev_err(dev, "%s: Error, could not create config_loading\n", + __func__); + goto error_create_config_loading; + } + + rc = device_create_bin_file(dev, &bin_attr_config_data); + if (rc) { + dev_err(dev, "%s: Error, could not create config_data\n", + __func__); + goto error_create_config_data; + } +#endif + + ld->loader_pdata = pdata->loader_pdata; + ld->dev = dev; + *data = ld; + +#ifdef CONFIG_CYTTSP5_CREATE_PT_BL_FROM_FILE + INIT_WORK(&ld->bl_from_file, pt_bl_from_file_work); +#endif + +#if CYTTSP5_FW_UPGRADE + init_completion(&ld->int_running); +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE + init_completion(&ld->builtin_bin_fw_complete); +#endif + cmd->subscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_LOADER_NAME, + cyttsp5_loader_attention, CY_MODE_BOOTLOADER); + + cmd->subscribe_attention(dev, CY_ATTEN_LOADER, CYTTSP5_LOADER_NAME, + cyttsp5_fw_upgrade_cb, CY_MODE_UNKNOWN); +#endif +#if CYTTSP5_FW_UPGRADE || CYTTSP5_TTCONFIG_UPGRADE + init_completion(&ld->calibration_complete); + INIT_WORK(&ld->calibration_work, cyttsp5_calibrate_idacs); +#endif +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE + mutex_init(&ld->config_lock); +#endif + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + cd->firmware_update = cyttsp5_loader_firmware_update; +#else +#ifdef UPGRADE_FW_AND_CONFIG_IN_PROBE + /* Call FW and config upgrade directly in probe */ + cyttsp5_fw_and_config_upgrade(&ld->fw_and_config_upgrade); +#else + INIT_WORK(&ld->fw_and_config_upgrade, cyttsp5_fw_and_config_upgrade); + schedule_work(&ld->fw_and_config_upgrade); +#endif +#endif + + dev_info(dev, "%s: Successful probe %s\n", __func__, dev_name(dev)); + return 0; + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE +error_create_config_data: + device_remove_file(dev, &dev_attr_config_loading); +error_create_config_loading: +#endif +#ifdef CONFIG_CYTTSP5_CREATE_PT_BL_FROM_FILE +error_create_pt_bl_from_file: + device_remove_file(dev, &dev_attr_pt_bl_from_file); +#endif +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE + device_remove_file(dev, &dev_attr_manual_upgrade); +error_create_manual_upgrade: +#endif +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE + device_remove_file(dev, &dev_attr_forced_upgrade); +error_create_forced_upgrade: +#endif + kfree(ld); +error_alloc_data_failed: +error_no_pdata: + dev_err(dev, "%s failed.\n", __func__); + return rc; +} + +static void cyttsp5_loader_release(struct device *dev, void *data) +{ + struct cyttsp5_loader_data *ld = (struct cyttsp5_loader_data *)data; + +#if CYTTSP5_FW_UPGRADE + cmd->unsubscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_LOADER_NAME, + cyttsp5_loader_attention, CY_MODE_BOOTLOADER); + + cmd->unsubscribe_attention(dev, CY_ATTEN_LOADER, CYTTSP5_LOADER_NAME, + cyttsp5_fw_upgrade_cb, CY_MODE_UNKNOWN); +#endif +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MANUAL_TTCONFIG_UPGRADE + device_remove_bin_file(dev, &bin_attr_config_data); + device_remove_file(dev, &dev_attr_config_loading); + kfree(ld->config_data); +#endif +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BINARY_FW_UPGRADE + device_remove_file(dev, &dev_attr_manual_upgrade); +#endif +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE + device_remove_file(dev, &dev_attr_forced_upgrade); +#endif + kfree(ld); +} + +static struct cyttsp5_module loader_module = { + .name = CYTTSP5_LOADER_NAME, + .probe = cyttsp5_loader_probe, + .release = cyttsp5_loader_release, +}; + +static int __init cyttsp5_loader_init(void) +{ + int rc; + + cmd = cyttsp5_get_commands(); + if (!cmd) + return -EINVAL; + + rc = cyttsp5_register_module(&loader_module); + if (rc < 0) { + pr_err("%s: Error, failed registering module\n", + __func__); + return rc; + } + + pr_info("%s: Parade TTSP FW Loader Driver (Built %s) rc=%d\n", + __func__, CY_DRIVER_VERSION, rc); + return 0; +} +module_init(cyttsp5_loader_init); + +static void __exit cyttsp5_loader_exit(void) +{ + cyttsp5_unregister_module(&loader_module); +} +module_exit(cyttsp5_loader_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product FW Loader Driver"); +MODULE_AUTHOR("Parade Technologies "); diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_mt_common.c b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_mt_common.c new file mode 100644 index 000000000000..dd14ce8edf9a --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_mt_common.c @@ -0,0 +1,751 @@ +/* + * cyttsp5_mt_common.c + * Parade TrueTouch(TM) Standard Product V5 Multi-Touch Reports Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * CYTMA5XX + * CYTMA448 + * CYTMA445A + * CYTT21XXX + * CYTT31XXX + * + * Copyright (C) 2015 Parade Technologies + * Copyright (C) 2012-2015 Cypress Semiconductor + * + * 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 + * + */ + +#include "cyttsp5_regs.h" + +#define CYTTSP5_MT_NAME "cyttsp5_mt" + +#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) + +static void cyttsp5_mt_lift_all(struct cyttsp5_mt_data *md) +{ + int max = md->si->tch_abs[CY_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; + } +} + +static void cyttsp5_get_touch_axis(struct cyttsp5_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++) { + parade_debug(md->dev, DEBUG_LEVEL_2, + "%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; + + parade_debug(md->dev, DEBUG_LEVEL_2, + "%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]); +} + +static void cyttsp5_get_touch_hdr(struct cyttsp5_mt_data *md, + struct cyttsp5_touch *touch, u8 *xy_mode) +{ + struct device *dev = md->dev; + struct cyttsp5_sysinfo *si = md->si; + enum cyttsp5_tch_hdr hdr; + + for (hdr = CY_TCH_TIME; hdr < CY_TCH_NUM_HDR; hdr++) { + if (!si->tch_hdr[hdr].report) + continue; + cyttsp5_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); + parade_debug(dev, DEBUG_LEVEL_2, "%s: get %s=%04X(%d)\n", + __func__, cyttsp5_tch_hdr_string[hdr], + touch->hdr[hdr], touch->hdr[hdr]); + } + + parade_debug(dev, DEBUG_LEVEL_1, + "%s: time=%X tch_num=%d lo=%d noise=%d counter=%d\n", + __func__, + touch->hdr[CY_TCH_TIME], + touch->hdr[CY_TCH_NUM], + touch->hdr[CY_TCH_LO], + touch->hdr[CY_TCH_NOISE], + touch->hdr[CY_TCH_COUNTER]); +} + +static void cyttsp5_get_touch_record(struct cyttsp5_mt_data *md, + struct cyttsp5_touch *touch, u8 *xy_data) +{ + struct device *dev = md->dev; + struct cyttsp5_sysinfo *si = md->si; + enum cyttsp5_tch_abs abs; + + for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) { + if (!si->tch_abs[abs].report) + continue; + cyttsp5_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); + parade_debug(dev, DEBUG_LEVEL_2, "%s: get %s=%04X(%d)\n", + __func__, cyttsp5_tch_abs_string[abs], + touch->abs[abs], touch->abs[abs]); + } +} + +static void cyttsp5_mt_process_touch(struct cyttsp5_mt_data *md, + struct cyttsp5_touch *touch) +{ + struct device *dev = md->dev; + struct cyttsp5_sysinfo *si = md->si; + int tmp; + bool flipped; + + + /* Orientation is signed */ + touch->abs[CY_TCH_OR] = (int8_t)touch->abs[CY_TCH_OR]; + + if (md->pdata->flags & CY_MT_FLAG_FLIP) { + tmp = touch->abs[CY_TCH_X]; + touch->abs[CY_TCH_X] = touch->abs[CY_TCH_Y]; + touch->abs[CY_TCH_Y] = tmp; + if (touch->abs[CY_TCH_OR] > 0) + touch->abs[CY_TCH_OR] = + md->or_max - touch->abs[CY_TCH_OR]; + else + touch->abs[CY_TCH_OR] = + md->or_min - touch->abs[CY_TCH_OR]; + flipped = true; + } else + flipped = false; + + if (md->pdata->flags & CY_MT_FLAG_INV_X) { + if (flipped) + touch->abs[CY_TCH_X] = si->sensing_conf_data.res_y - + touch->abs[CY_TCH_X]; + else + touch->abs[CY_TCH_X] = si->sensing_conf_data.res_x - + touch->abs[CY_TCH_X]; + touch->abs[CY_TCH_OR] *= -1; + } + if (md->pdata->flags & CY_MT_FLAG_INV_Y) { + if (flipped) + touch->abs[CY_TCH_Y] = si->sensing_conf_data.res_x - + touch->abs[CY_TCH_Y]; + else + touch->abs[CY_TCH_Y] = si->sensing_conf_data.res_y - + touch->abs[CY_TCH_Y]; + touch->abs[CY_TCH_OR] *= -1; + } + + /* Convert MAJOR/MINOR from mm to resolution */ + tmp = touch->abs[CY_TCH_MAJ] * 100 * si->sensing_conf_data.res_x; + touch->abs[CY_TCH_MAJ] = tmp / si->sensing_conf_data.len_x; + tmp = touch->abs[CY_TCH_MIN] * 100 * si->sensing_conf_data.res_x; + touch->abs[CY_TCH_MIN] = tmp / si->sensing_conf_data.len_x; + + parade_debug(dev, DEBUG_LEVEL_2, "%s: flip=%s inv-x=%s inv-y=%s x=%04X(%d) y=%04X(%d)\n", + __func__, flipped ? "true" : "false", + md->pdata->flags & CY_MT_FLAG_INV_X ? "true" : "false", + md->pdata->flags & CY_MT_FLAG_INV_Y ? "true" : "false", + touch->abs[CY_TCH_X], touch->abs[CY_TCH_X], + touch->abs[CY_TCH_Y], touch->abs[CY_TCH_Y]); +} + +static void cyttsp5_report_event(struct cyttsp5_mt_data *md, int event, + int value) +{ + int sig = MT_PARAM_SIGNAL(md, event); + + if (sig != CY_IGNORE_VALUE) + input_report_abs(md->input, sig, value); +} + +#define CY_MAX_ID 32 +static void cyttsp5_get_mt_touches(struct cyttsp5_mt_data *md, + struct cyttsp5_touch *tch, int num_cur_tch) +{ + struct device *dev = md->dev; + struct cyttsp5_sysinfo *si = md->si; + int sig; + int i, j, t = 0; + DECLARE_BITMAP(ids, CY_MAX_ID); + int mt_sync_count = 0; + u8 *tch_addr; + + bitmap_zero(ids, si->tch_abs[CY_TCH_T].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); + cyttsp5_get_touch_record(md, tch, tch_addr); + + /* Discard proximity event */ + if (tch->abs[CY_TCH_O] == CY_OBJ_PROXIMITY) { + parade_debug(dev, DEBUG_LEVEL_2, "%s: Discarding proximity event\n", + __func__); + continue; + } + + /* Validate track_id */ + t = tch->abs[CY_TCH_T]; + if (t < md->t_min || t > md->t_max) { + dev_err(dev, "%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[CY_TCH_E] == CY_EV_LIFTOFF) { + parade_debug(dev, DEBUG_LEVEL_1, "%s: t=%d e=%d lift-off\n", + __func__, t, tch->abs[CY_TCH_E]); + goto cyttsp5_get_mt_touches_pr_tch; + } + + /* Process touch */ + cyttsp5_mt_process_touch(md, tch); + + /* use 0 based track id's */ + t -= md->t_min; + + sig = MT_PARAM_SIGNAL(md, CY_ABS_ID_OST); + if (sig != CY_IGNORE_VALUE) { + if (md->mt_function.input_report) + md->mt_function.input_report(md->input, sig, + t, tch->abs[CY_TCH_O]); + __set_bit(t, ids); + } + + /* If touch type is hover, send P as distance, reset P */ + if (tch->abs[CY_TCH_O] == CY_OBJ_HOVER) { + /* CY_ABS_D_OST signal must be in touch framework */ + cyttsp5_report_event(md, CY_ABS_D_OST, + tch->abs[CY_TCH_P]); + tch->abs[CY_TCH_P] = 0; + } else + cyttsp5_report_event(md, CY_ABS_D_OST, 0); + + + /* all devices: position and pressure fields */ + for (j = 0; j <= CY_ABS_W_OST; j++) { + if (!si->tch_abs[j].report) + continue; + cyttsp5_report_event(md, CY_ABS_X_OST + j, + tch->abs[CY_TCH_X + j]); + } + + /* Get the extended touch fields */ + for (j = 0; j < CY_NUM_EXT_TCH_FIELDS; j++) { + if (!si->tch_abs[CY_ABS_MAJ_OST + j].report) + continue; + cyttsp5_report_event(md, CY_ABS_MAJ_OST + j, + tch->abs[CY_TCH_MAJ + j]); + } + if (md->mt_function.input_sync) + md->mt_function.input_sync(md->input); + mt_sync_count++; + +cyttsp5_get_mt_touches_pr_tch: + parade_debug(dev, DEBUG_LEVEL_1, + "%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[CY_TCH_X], + tch->abs[CY_TCH_Y], + tch->abs[CY_TCH_P], + tch->abs[CY_TCH_MAJ], + tch->abs[CY_TCH_MIN], + tch->abs[CY_TCH_OR], + tch->abs[CY_TCH_E], + tch->abs[CY_TCH_O], + tch->abs[CY_TCH_TIP]); + } + + if (md->mt_function.final_sync) + md->mt_function.final_sync(md->input, + si->tch_abs[CY_TCH_T].max, mt_sync_count, ids); + + md->num_prv_rec = num_cur_tch; +} + +/* read xy_data for all current touches */ +static int cyttsp5_xy_worker(struct cyttsp5_mt_data *md) +{ + struct device *dev = md->dev; + struct cyttsp5_sysinfo *si = md->si; + int max_tch = si->sensing_conf_data.max_tch; + struct cyttsp5_touch tch; + u8 num_cur_tch; + int rc = 0; + + cyttsp5_get_touch_hdr(md, &tch, si->xy_mode + 3); + + num_cur_tch = tch.hdr[CY_TCH_NUM]; + if (num_cur_tch > max_tch) { + dev_err(dev, "%s: Num touch err detected (n=%d)\n", + __func__, num_cur_tch); + num_cur_tch = max_tch; + } + + if (tch.hdr[CY_TCH_LO]) { + parade_debug(dev, DEBUG_LEVEL_1, "%s: Large area detected\n", + __func__); + if (md->pdata->flags & CY_MT_FLAG_NO_TOUCH_ON_LO) + num_cur_tch = 0; + } + + if (num_cur_tch == 0 && md->num_prv_rec == 0) + goto cyttsp5_xy_worker_exit; + + /* extract xy_data for all currently reported touches */ + parade_debug(dev, DEBUG_LEVEL_2, "%s: extract data num_cur_tch=%d\n", + __func__, num_cur_tch); + if (num_cur_tch) + cyttsp5_get_mt_touches(md, &tch, num_cur_tch); + else + cyttsp5_mt_lift_all(md); + + rc = 0; + +cyttsp5_xy_worker_exit: + return rc; +} + +static void cyttsp5_mt_send_dummy_event(struct cyttsp5_core_data *cd, + struct cyttsp5_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, CY_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_F1; + break; + case GESTURE_TWO_FINGERS_SLIDE: + key_value = KEY_F2; + break; + case GESTURE_TOUCH_DETECTED: +#ifdef CYTTSP5_SENSOR_EN + key_value = KEY_F1; +#else + key_value = KEY_F3; +#endif + 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; + } + +#ifdef CYTTSP5_SENSOR_EN + if (!(cd->easy_wakeup_gesture && cd->should_enable_gesture)) { + dev_info(cd->dev, "Gesture got but wakeable not set. Skip this gesture."); + return; + } + input_report_key(cd->sensor_pdata->input_sensor_dev, key_value, 1); + input_sync(cd->sensor_pdata->input_sensor_dev); + input_report_key(cd->sensor_pdata->input_sensor_dev, key_value, 0); + input_sync(cd->sensor_pdata->input_sensor_dev); + dev_info(cd->dev, "%s: report single tap, cd->gesture_id = %d, key_value = %d\n", + __func__, cd->gesture_id, key_value); +#else + input_report_key(md->input, key_value, 1); + mdelay(10); + input_report_key(md->input, key_value, 0); + input_sync(md->input); +#endif +#endif +} + +static int cyttsp5_mt_attention(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_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 = cyttsp5_xy_worker(md); + mutex_unlock(&md->mt_lock); + if (rc < 0) + dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc); + + return rc; +} + +static int cyttsp5_mt_wake_attention(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_mt_data *md = &cd->md; + + mutex_lock(&md->mt_lock); + cyttsp5_mt_send_dummy_event(cd, md); + mutex_unlock(&md->mt_lock); + return 0; +} + +static int cyttsp5_startup_attention(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_mt_data *md = &cd->md; + + mutex_lock(&md->mt_lock); + cyttsp5_mt_lift_all(md); + mutex_unlock(&md->mt_lock); + + return 0; +} + +static int cyttsp5_mt_suspend_attention(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_mt_data *md = &cd->md; + + mutex_lock(&md->mt_lock); + cyttsp5_mt_lift_all(md); + md->is_suspended = true; + mutex_unlock(&md->mt_lock); + + pm_runtime_put(dev); + + return 0; +} + +static int cyttsp5_mt_resume_attention(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_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; +} + +static int cyttsp5_mt_open(struct input_dev *input) +{ + struct device *dev = input->dev.parent; + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_mt_data *md = &cd->md; + + pm_runtime_get_sync(dev); + + mutex_lock(&md->mt_lock); + md->is_suspended = false; + mutex_unlock(&md->mt_lock); + + parade_debug(dev, DEBUG_LEVEL_2, "%s: setup subscriptions\n", __func__); + + /* set up touch call back */ + _cyttsp5_subscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_MT_NAME, + cyttsp5_mt_attention, CY_MODE_OPERATIONAL); + + /* set up startup call back */ + _cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_MT_NAME, + cyttsp5_startup_attention, 0); + + /* set up wakeup call back */ + _cyttsp5_subscribe_attention(dev, CY_ATTEN_WAKE, CYTTSP5_MT_NAME, + cyttsp5_mt_wake_attention, 0); + + /* set up suspend call back */ + _cyttsp5_subscribe_attention(dev, CY_ATTEN_SUSPEND, CYTTSP5_MT_NAME, + cyttsp5_mt_suspend_attention, 0); + + /* set up resume call back */ + _cyttsp5_subscribe_attention(dev, CY_ATTEN_RESUME, CYTTSP5_MT_NAME, + cyttsp5_mt_resume_attention, 0); + + return 0; +} + +static void cyttsp5_mt_close(struct input_dev *input) +{ + struct device *dev = input->dev.parent; + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_mt_data *md = &cd->md; + + _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_MT_NAME, + cyttsp5_mt_attention, CY_MODE_OPERATIONAL); + + _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_MT_NAME, + cyttsp5_startup_attention, 0); + + _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_WAKE, CYTTSP5_MT_NAME, + cyttsp5_mt_wake_attention, 0); + + _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_SUSPEND, CYTTSP5_MT_NAME, + cyttsp5_mt_suspend_attention, 0); + + _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_RESUME, CYTTSP5_MT_NAME, + cyttsp5_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); +} + +static int cyttsp5_setup_input_device(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_mt_data *md = &cd->md; + int signal = CY_IGNORE_VALUE; + int max_x, max_y, max_p, min, max; + int max_x_tmp, max_y_tmp; + int i; + int rc; + + parade_debug(dev, DEBUG_LEVEL_2, "%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 & CY_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 & CY_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 != CY_IGNORE_VALUE) { + __set_bit(signal, md->input->absbit); + + min = MT_PARAM_MIN(md, i); + max = MT_PARAM_MAX(md, i); + if (i == CY_ABS_ID_OST) { + /* shift track ids down to start at 0 */ + max = max - min; + min = min - min; + } else if (i == CY_ABS_X_OST) + max = max_x; + else if (i == CY_ABS_Y_OST) + max = max_y; + else if (i == CY_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)); + parade_debug(dev, DEBUG_LEVEL_1, + "%s: register signal=%02X min=%d max=%d\n", + __func__, signal, min, max); + } + } + + md->or_min = MT_PARAM_MIN(md, CY_ABS_OR_OST); + md->or_max = MT_PARAM_MAX(md, CY_ABS_OR_OST); + + md->t_min = MT_PARAM_MIN(md, CY_ABS_ID_OST); + md->t_max = MT_PARAM_MAX(md, CY_ABS_ID_OST); + + rc = md->mt_function.input_register_device(md->input, + md->si->tch_abs[CY_TCH_T].max); + if (rc < 0) + dev_err(dev, "%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); +#endif + return rc; +} + +static int cyttsp5_setup_input_attention(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_mt_data *md = &cd->md; + int rc; + + md->si = _cyttsp5_request_sysinfo(dev); + if (!md->si) + return -EINVAL; + + rc = cyttsp5_setup_input_device(dev); + + _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, CYTTSP5_MT_NAME, + cyttsp5_setup_input_attention, 0); + + return rc; +} + +int cyttsp5_mt_probe(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_mt_data *md = &cd->md; + struct cyttsp5_platform_data *pdata = dev_get_platdata(dev); + struct cyttsp5_mt_platform_data *mt_pdata; + int rc = 0; + + if (!pdata || !pdata->mt_pdata) { + dev_err(dev, "%s: Missing platform data\n", __func__); + rc = -ENODEV; + goto error_no_pdata; + } + mt_pdata = pdata->mt_pdata; + + cyttsp5_init_function_ptrs(md); + + mutex_init(&md->mt_lock); + md->dev = dev; + md->pdata = mt_pdata; + + /* Create the input device and register it. */ + parade_debug(dev, DEBUG_LEVEL_2, + "%s: Create the input device and register it\n", __func__); + md->input = input_allocate_device(); + if (!md->input) { + dev_err(dev, "%s: Error, failed to allocate input device\n", + __func__); + rc = -ENODEV; + goto error_alloc_failed; + } + + if (md->pdata->inp_dev_name) + md->input->name = md->pdata->inp_dev_name; + else + md->input->name = CYTTSP5_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 = cyttsp5_mt_open; + md->input->close = cyttsp5_mt_close; + input_set_drvdata(md->input, md); + + /* get sysinfo */ + md->si = _cyttsp5_request_sysinfo(dev); + + if (md->si) { + rc = cyttsp5_setup_input_device(dev); + if (rc) + goto error_init_input; + } else { + dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n", + __func__, md->si); + _cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP, + CYTTSP5_MT_NAME, cyttsp5_setup_input_attention, 0); + } + + return 0; + +error_init_input: + input_free_device(md->input); +error_alloc_failed: +error_no_pdata: + dev_err(dev, "%s failed.\n", __func__); + return rc; +} + +int cyttsp5_mt_release(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_mt_data *md = &cd->md; + + if (md->input_device_registered) { + input_unregister_device(md->input); + } else { + input_free_device(md->input); + _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, + CYTTSP5_MT_NAME, cyttsp5_setup_input_attention, 0); + } + + return 0; +} diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_mta.c b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_mta.c new file mode 100644 index 000000000000..d457bb66d360 --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_mta.c @@ -0,0 +1,85 @@ +/* + * cyttsp5_mta.c + * Parade TrueTouch(TM) Standard Product V5 Multi-Touch Protocol A Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * CYTMA5XX + * CYTMA448 + * CYTMA445A + * CYTT21XXX + * CYTT31XXX + * + * Copyright (C) 2015 Parade Technologies + * Copyright (C) 2012-2015 Cypress Semiconductor + * + * 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 + * + */ + +#include "cyttsp5_regs.h" + +static void cyttsp5_final_sync(struct input_dev *input, int max_slots, + int mt_sync_count, unsigned long *ids) +{ + if (mt_sync_count) + input_sync(input); +} + +static void cyttsp5_input_sync(struct input_dev *input) +{ + input_mt_sync(input); +} + +static void cyttsp5_input_report(struct input_dev *input, int sig, + int t, int type) +{ + if (type == CY_OBJ_STANDARD_FINGER || type == CY_OBJ_GLOVE + || type == CY_OBJ_HOVER) { + input_report_key(input, BTN_TOOL_FINGER, CY_BTN_PRESSED); + input_report_key(input, BTN_TOOL_PEN, CY_BTN_RELEASED); + } else if (type == CY_OBJ_STYLUS) { + input_report_key(input, BTN_TOOL_PEN, CY_BTN_PRESSED); + input_report_key(input, BTN_TOOL_FINGER, CY_BTN_RELEASED); + } + + if (type != CY_OBJ_HOVER) + input_report_key(input, BTN_TOUCH, CY_BTN_PRESSED); + + input_report_abs(input, sig, t); +} + +static void cyttsp5_report_slot_liftoff(struct cyttsp5_mt_data *md, + int max_slots) +{ + input_report_key(md->input, BTN_TOUCH, CY_BTN_RELEASED); + input_report_key(md->input, BTN_TOOL_FINGER, CY_BTN_RELEASED); + input_report_key(md->input, BTN_TOOL_PEN, CY_BTN_RELEASED); + +} + +static int cyttsp5_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); +} + +void cyttsp5_init_function_ptrs(struct cyttsp5_mt_data *md) +{ + md->mt_function.report_slot_liftoff = cyttsp5_report_slot_liftoff; + md->mt_function.final_sync = cyttsp5_final_sync; + md->mt_function.input_sync = cyttsp5_input_sync; + md->mt_function.input_report = cyttsp5_input_report; + md->mt_function.input_register_device = cyttsp5_input_register_device; +} diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_mtb.c b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_mtb.c new file mode 100644 index 000000000000..d5f51e9c836f --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_mtb.c @@ -0,0 +1,93 @@ +/* + * cyttsp5_mtb.c + * Parade TrueTouch(TM) Standard Product V5 Multi-Touch Protocol B Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * CYTMA5XX + * CYTMA448 + * CYTMA445A + * CYTT21XXX + * CYTT31XXX + * + * Copyright (C) 2015 Parade Technologies + * Copyright (C) 2012-2015 Cypress Semiconductor + * + * 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 + * + */ + +#include "cyttsp5_regs.h" +#include +#include + +static void cyttsp5_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); +} + +static void cyttsp5_input_report(struct input_dev *input, int sig, + int t, int type) +{ + input_mt_slot(input, t); + + if (type == CY_OBJ_STANDARD_FINGER || type == CY_OBJ_GLOVE + || type == CY_OBJ_HOVER) + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + else if (type == CY_OBJ_STYLUS) + input_mt_report_slot_state(input, MT_TOOL_PEN, true); +} + +static void cyttsp5_report_slot_liftoff(struct cyttsp5_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); + } +} + +static int cyttsp5_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); +} + +void cyttsp5_init_function_ptrs(struct cyttsp5_mt_data *md) +{ + md->mt_function.report_slot_liftoff = cyttsp5_report_slot_liftoff; + md->mt_function.final_sync = cyttsp5_final_sync; + md->mt_function.input_sync = NULL; + md->mt_function.input_report = cyttsp5_input_report; + md->mt_function.input_register_device = cyttsp5_input_register_device; +} + diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_platform.c b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_platform.c new file mode 100644 index 000000000000..6fb0d6a5ff2b --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_platform.c @@ -0,0 +1,409 @@ +/* + * cyttsp5_platform.c + * Parade TrueTouch(TM) Standard Product V5 Platform Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * CYTMA5XX + * CYTMA448 + * CYTMA445A + * CYTT21XXX + * CYTT31XXX + * + * Copyright (C) 2015 Parade Technologies + * Copyright (C) 2013-2015 Cypress Semiconductor + * + * 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 + * + */ + +#include "cyttsp5_regs.h" +#include "cyttsp5_platform.h" + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE +/* FW for Panel ID = 0x00 */ +#include "cyttsp5_fw_pid00.h" +static struct cyttsp5_touch_firmware cyttsp5_firmware_pid00 = { + .img = cyttsp4_img_pid00, + .size = ARRAY_SIZE(cyttsp4_img_pid00), + .ver = cyttsp4_ver_pid00, + .vsize = ARRAY_SIZE(cyttsp4_ver_pid00), + .panel_id = 0x00, +}; + +/* FW for Panel ID = 0x01 */ +#include "cyttsp5_fw_pid01.h" +static struct cyttsp5_touch_firmware cyttsp5_firmware_pid01 = { + .img = cyttsp4_img_pid01, + .size = ARRAY_SIZE(cyttsp4_img_pid01), + .ver = cyttsp4_ver_pid01, + .vsize = ARRAY_SIZE(cyttsp4_ver_pid01), + .panel_id = 0x01, +}; + +/* FW for Panel ID not enabled (legacy) */ +#include "cyttsp5_fw.h" +static struct cyttsp5_touch_firmware cyttsp5_firmware = { + .img = cyttsp4_img, + .size = ARRAY_SIZE(cyttsp4_img), + .ver = cyttsp4_ver, + .vsize = ARRAY_SIZE(cyttsp4_ver), +}; +#else +/* FW for Panel ID not enabled (legacy) */ +static struct cyttsp5_touch_firmware cyttsp5_firmware = { + .img = NULL, + .size = 0, + .ver = NULL, + .vsize = 0, +}; +#endif + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE +/* TT Config for Panel ID = 0x00 */ +#include "cyttsp5_params_pid00.h" +static struct touch_settings cyttsp5_sett_param_regs_pid00 = { + .data = (uint8_t *)&cyttsp4_param_regs_pid00[0], + .size = ARRAY_SIZE(cyttsp4_param_regs_pid00), + .tag = 0, +}; + +static struct touch_settings cyttsp5_sett_param_size_pid00 = { + .data = (uint8_t *)&cyttsp4_param_size_pid00[0], + .size = ARRAY_SIZE(cyttsp4_param_size_pid00), + .tag = 0, +}; + +static struct cyttsp5_touch_config cyttsp5_ttconfig_pid00 = { + .param_regs = &cyttsp5_sett_param_regs_pid00, + .param_size = &cyttsp5_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 "cyttsp5_params_pid01.h" +static struct touch_settings cyttsp5_sett_param_regs_pid01 = { + .data = (uint8_t *)&cyttsp4_param_regs_pid01[0], + .size = ARRAY_SIZE(cyttsp4_param_regs_pid01), + .tag = 0, +}; + +static struct touch_settings cyttsp5_sett_param_size_pid01 = { + .data = (uint8_t *)&cyttsp4_param_size_pid01[0], + .size = ARRAY_SIZE(cyttsp4_param_size_pid01), + .tag = 0, +}; + +static struct cyttsp5_touch_config cyttsp5_ttconfig_pid01 = { + .param_regs = &cyttsp5_sett_param_regs_pid01, + .param_size = &cyttsp5_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 "cyttsp5_params.h" +static struct touch_settings cyttsp5_sett_param_regs = { + .data = (uint8_t *)&cyttsp4_param_regs[0], + .size = ARRAY_SIZE(cyttsp4_param_regs), + .tag = 0, +}; + +static struct touch_settings cyttsp5_sett_param_size = { + .data = (uint8_t *)&cyttsp4_param_size[0], + .size = ARRAY_SIZE(cyttsp4_param_size), + .tag = 0, +}; + +static struct cyttsp5_touch_config cyttsp5_ttconfig = { + .param_regs = &cyttsp5_sett_param_regs, + .param_size = &cyttsp5_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 cyttsp5_touch_config cyttsp5_ttconfig = { + .param_regs = NULL, + .param_size = NULL, + .fw_ver = NULL, + .fw_vsize = 0, +}; +#endif + +static struct cyttsp5_touch_firmware *cyttsp5_firmwares[] = { +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_FW_UPGRADE + &cyttsp5_firmware_pid00, + &cyttsp5_firmware_pid01, +#endif + NULL, /* Last item should always be NULL */ +}; + +static struct cyttsp5_touch_config *cyttsp5_ttconfigs[] = { +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PLATFORM_TTCONFIG_UPGRADE + &cyttsp5_ttconfig_pid00, + &cyttsp5_ttconfig_pid01, +#endif + NULL, /* Last item should always be NULL */ +}; + +struct cyttsp5_loader_platform_data _cyttsp5_loader_platform_data = { + .fw = &cyttsp5_firmware, + .ttconfig = &cyttsp5_ttconfig, + .fws = cyttsp5_firmwares, + .ttconfigs = cyttsp5_ttconfigs, + .flags = CY_LOADER_FLAG_NONE, +}; + +int cyttsp5_xres(struct cyttsp5_core_platform_data *pdata, + struct device *dev) +{ + int rst_gpio = pdata->rst_gpio; + int rc = 0; + + gpio_set_value(rst_gpio, 1); + msleep(20); + gpio_set_value(rst_gpio, 0); + msleep(40); + gpio_set_value(rst_gpio, 1); + msleep(20); + dev_info(dev, + "%s: RESET CYTTSP gpio=%d r=%d\n", __func__, + pdata->rst_gpio, rc); + return rc; +} + +/** + * cyttsp5_ts_power_init - Get regulator for touch device + * @pdata: pointer to touch core platform data + * @dev: pointer to touch device + * return: 0 ok, <0 failed + */ +static int cyttsp5_ts_power_init(struct cyttsp5_core_platform_data *pdata, struct device *dev) +{ + int ret = 0; + + dev_info(dev, "%s:Get regulator for touch device", __func__); + if (strlen(pdata->avdd_name)) { + pdata->avdd = devm_regulator_get(dev, + pdata->avdd_name); + if (IS_ERR_OR_NULL(pdata->avdd)) { + ret = PTR_ERR(pdata->avdd); + pr_err("%s, Failed to get regulator avdd:%d", __func__, ret); + pdata->avdd = NULL; + return ret; + } + } else { + dev_info(dev, "%s: Avdd name is NULL", __func__); + return -1; + } + + if (strlen(pdata->iovdd_name)) { + pdata->iovdd = devm_regulator_get(dev, + pdata->iovdd_name); + if (IS_ERR_OR_NULL(pdata->iovdd)) { + ret = PTR_ERR(pdata->iovdd); + pr_err("%s: Failed to get regulator iovdd:%d", __func__, ret); + pdata->iovdd = NULL; + return ret; + } + } else { + dev_info(dev, "%s: iovdd name is NULL", __func__); + return -1; + } + + return ret; +} + +/** + * cyttsp5_ts_power_control - Set regulator power on or off + * @dev: pointer to touch device + * @on: indicating the on/off the power + * return: 0 ok, <0 failed + */ +static int cyttsp5_ts_power_control(struct cyttsp5_core_platform_data *pdata, + struct device *dev, int on) +{ + int ret =0; + if(on) { + pr_info("%s: Device power on", __func__); + + if (!IS_ERR_OR_NULL(pdata->iovdd)) { + ret = regulator_enable(pdata->iovdd); + if (ret < 0) { + pr_err("%s: Failed to enable iovdd:%d", __func__, ret); + goto power_off; + } + } + + if (!IS_ERR_OR_NULL(pdata->avdd)) { + ret = regulator_enable(pdata->avdd); + if (ret < 0) { + pr_err("%s: Failed to enable avdd:%d", __func__, ret); + goto power_off; + } + } + return 0; + } + +power_off: + pr_info("%s: Device power off", __func__); + if (!IS_ERR_OR_NULL(pdata->iovdd)) { + if (regulator_is_enabled(pdata->iovdd)) + regulator_disable(pdata->iovdd); + } + + if (!IS_ERR_OR_NULL(pdata->avdd)) { + if (regulator_is_enabled(pdata->avdd)) + regulator_disable(pdata->avdd); + } + + return ret; +} + +int cyttsp5_init(struct cyttsp5_core_platform_data *pdata, + int on, struct device *dev) +{ + int rst_gpio = pdata->rst_gpio; + int irq_gpio = pdata->irq_gpio; + int rc = 0; + + if (on) { + rc = cyttsp5_ts_power_init(pdata, dev); + if(rc < 0) { + dev_err(dev, + "%s: Fail init cyttsp5 power regulator =%d\n", __func__, rc); + return rc; + } + + rc = cyttsp5_ts_power_control(pdata, dev, 1); + if(rc < 0) { + dev_err(dev, + "%s: Power on cyttsp5 failed = %d\n", __func__, rc); + return rc; + } + + rc = gpio_request(rst_gpio, NULL); + if (rc < 0) { + gpio_free(rst_gpio); + rc = gpio_request(rst_gpio, NULL); + } + if (rc < 0) { + dev_err(dev, + "%s: Fail request gpio=%d\n", __func__, + rst_gpio); + } else { + rc = gpio_direction_output(rst_gpio, 1); + if (rc < 0) { + pr_err("%s: Fail set output gpio=%d\n", + __func__, rst_gpio); + gpio_free(rst_gpio); + } else { + rc = gpio_request(irq_gpio, NULL); + if (rc < 0) { + gpio_free(irq_gpio); + rc = gpio_request(irq_gpio, + NULL); + } + if (rc < 0) { + dev_err(dev, + "%s: Fail request gpio=%d\n", + __func__, irq_gpio); + gpio_free(rst_gpio); + } else { + gpio_direction_input(irq_gpio); + } + } + } + } else { + gpio_free(rst_gpio); + gpio_free(irq_gpio); + } + + dev_info(dev, "%s: INIT CYTTSP RST gpio=%d and IRQ gpio=%d r=%d\n", + __func__, rst_gpio, irq_gpio, rc); + return rc; +} + +static int cyttsp5_wakeup(struct cyttsp5_core_platform_data *pdata, + struct device *dev, atomic_t *ignore_irq) +{ + int ret =0; + + ret = cyttsp5_ts_power_control(pdata, dev, 1); + if(ret < 0) { + dev_err(dev, + "%s: Power on cyttsp5 failed = %d\n", __func__, ret); + return ret; + } + + return ret; +} + +static int cyttsp5_sleep(struct cyttsp5_core_platform_data *pdata, + struct device *dev, atomic_t *ignore_irq) +{ + int ret =0; + + ret = cyttsp5_ts_power_control(pdata, dev, 0); + if(ret < 0) { + dev_err(dev, + "%s: Power off cyttsp5 failed = %d\n", __func__, ret); + return ret; + } + + return ret; +} + +int cyttsp5_power(struct cyttsp5_core_platform_data *pdata, + int on, struct device *dev, atomic_t *ignore_irq) +{ + if (on) + return cyttsp5_wakeup(pdata, dev, ignore_irq); + + return cyttsp5_sleep(pdata, dev, ignore_irq); +} + +int cyttsp5_irq_stat(struct cyttsp5_core_platform_data *pdata, + struct device *dev) +{ + return gpio_get_value(pdata->irq_gpio); +} + +#ifdef CYTTSP5_DETECT_HW +int cyttsp5_detect(struct cyttsp5_core_platform_data *pdata, + struct device *dev, cyttsp5_platform_read read) +{ + int retry = 3; + int rc; + char buf[1]; + + while (retry--) { + /* Perform reset, wait for 100 ms and perform read */ + parade_debug(dev, DEBUG_LEVEL_2, "%s: Performing a reset\n", + __func__); + pdata->xres(pdata, dev); + msleep(100); + rc = read(dev, buf, 1); + if (!rc) + return 0; + + parade_debug(dev, DEBUG_LEVEL_2, "%s: Read unsuccessful, try=%d\n", + __func__, 3 - retry); + } + + return rc; +} +#endif diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_platform.h b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_platform.h new file mode 100644 index 000000000000..289b014775c0 --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_platform.h @@ -0,0 +1,60 @@ +/* + * cyttsp5_platform.h + * Parade TrueTouch(TM) Standard Product V5 Platform Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * CYTMA5XX + * CYTMA448 + * CYTMA445A + * CYTT21XXX + * CYTT31XXX + * + * Copyright (C) 2015 Parade Semiconductor + * Copyright (C) 2013-2015 Cypress Semiconductor + * + * 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 Semiconductor at www.parade.com + * + */ + +#ifndef _LINUX_CYTTSP5_PLATFORM_H +#define _LINUX_CYTTSP5_PLATFORM_H + +#include "cyttsp5_core.h" + +#if defined(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5) \ + || defined(CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_MODULE) +extern struct cyttsp5_loader_platform_data _cyttsp5_loader_platform_data; + +int cyttsp5_xres(struct cyttsp5_core_platform_data *pdata, struct device *dev); +int cyttsp5_init(struct cyttsp5_core_platform_data *pdata, int on, + struct device *dev); +int cyttsp5_power(struct cyttsp5_core_platform_data *pdata, int on, + struct device *dev, atomic_t *ignore_irq); +#ifdef CYTTSP5_DETECT_HW +int cyttsp5_detect(struct cyttsp5_core_platform_data *pdata, + struct device *dev, cyttsp5_platform_read read); +#else +#define cyttsp5_detect NULL +#endif +int cyttsp5_irq_stat(struct cyttsp5_core_platform_data *pdata, + struct device *dev); +#else /* !CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5 */ +static struct cyttsp5_loader_platform_data _cyttsp5_loader_platform_data; +#define cyttsp5_xres NULL +#define cyttsp5_init NULL +#define cyttsp5_power NULL +#define cyttsp5_irq_stat NULL +#define cyttsp5_detect NULL +#endif /* CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5 */ + +#endif /* _LINUX_CYTTSP5_PLATFORM_H */ diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_proximity.c b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_proximity.c new file mode 100644 index 000000000000..0dba2c817387 --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_proximity.c @@ -0,0 +1,555 @@ +/* + * cyttsp5_proximity.c + * Parade TrueTouch(TM) Standard Product V5 Proximity Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * CYTMA5XX + * CYTMA448 + * CYTMA445A + * CYTT21XXX + * CYTT31XXX + * + * Copyright (C) 2015 Parade Technologies + * Copyright (C) 2013-2015 Cypress Semiconductor + * + * 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 + * + */ + +#include "cyttsp5_regs.h" + +#define CYTTSP5_PROXIMITY_NAME "cyttsp5_proximity" + +/* Timeout value in ms. */ +#define CYTTSP5_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT 1000 + +#define CYTTSP5_PROXIMITY_ON 0 +#define CYTTSP5_PROXIMITY_OFF 1 + +static inline struct cyttsp5_proximity_data *get_prox_data(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + return &cd->pd; +} + +static void cyttsp5_report_proximity(struct cyttsp5_proximity_data *pd, + bool on) +{ + int val = on ? CYTTSP5_PROXIMITY_ON : CYTTSP5_PROXIMITY_OFF; + + input_report_abs(pd->input, ABS_DISTANCE, val); + input_sync(pd->input); +} + +static void cyttsp5_get_touch_axis(struct cyttsp5_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++) { + parade_debug(pd->dev, DEBUG_LEVEL_2, + "%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; + + parade_debug(pd->dev, DEBUG_LEVEL_2, + "%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]); +} + +static void cyttsp5_get_touch_hdr(struct cyttsp5_proximity_data *pd, + struct cyttsp5_touch *touch, u8 *xy_mode) +{ + struct device *dev = pd->dev; + struct cyttsp5_sysinfo *si = pd->si; + enum cyttsp5_tch_hdr hdr; + + for (hdr = CY_TCH_TIME; hdr < CY_TCH_NUM_HDR; hdr++) { + if (!si->tch_hdr[hdr].report) + continue; + cyttsp5_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); + parade_debug(dev, DEBUG_LEVEL_2, "%s: get %s=%04X(%d)\n", + __func__, cyttsp5_tch_hdr_string[hdr], + touch->hdr[hdr], touch->hdr[hdr]); + } +} + +static void cyttsp5_get_touch(struct cyttsp5_proximity_data *pd, + struct cyttsp5_touch *touch, u8 *xy_data) +{ + struct device *dev = pd->dev; + struct cyttsp5_sysinfo *si = pd->si; + enum cyttsp5_tch_abs abs; + + for (abs = CY_TCH_X; abs < CY_TCH_NUM_ABS; abs++) { + if (!si->tch_abs[abs].report) + continue; + cyttsp5_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); + parade_debug(dev, DEBUG_LEVEL_2, "%s: get %s=%04X(%d)\n", + __func__, cyttsp5_tch_abs_string[abs], + touch->abs[abs], touch->abs[abs]); + } + + parade_debug(dev, DEBUG_LEVEL_2, "%s: x=%04X(%d) y=%04X(%d)\n", + __func__, touch->abs[CY_TCH_X], touch->abs[CY_TCH_X], + touch->abs[CY_TCH_Y], touch->abs[CY_TCH_Y]); +} + +static void cyttsp5_get_proximity_touch(struct cyttsp5_proximity_data *pd, + struct cyttsp5_touch *tch, int num_cur_tch) +{ + struct cyttsp5_sysinfo *si = pd->si; + int i; + + for (i = 0; i < num_cur_tch; i++) { + cyttsp5_get_touch(pd, tch, si->xy_data + + (i * si->desc.tch_record_size)); + + /* Check for proximity event */ + if (tch->abs[CY_TCH_O] == CY_OBJ_PROXIMITY) { + if (tch->abs[CY_TCH_E] == CY_EV_TOUCHDOWN) + cyttsp5_report_proximity(pd, true); + else if (tch->abs[CY_TCH_E] == CY_EV_LIFTOFF) + cyttsp5_report_proximity(pd, false); + break; + } + } +} + +/* read xy_data for all current touches */ +static int cyttsp5_xy_worker(struct cyttsp5_proximity_data *pd) +{ + struct device *dev = pd->dev; + struct cyttsp5_sysinfo *si = pd->si; + struct cyttsp5_touch tch; + u8 num_cur_tch; + + cyttsp5_get_touch_hdr(pd, &tch, si->xy_mode + 3); + + num_cur_tch = tch.hdr[CY_TCH_NUM]; + if (num_cur_tch > si->sensing_conf_data.max_tch) { + dev_err(dev, "%s: Num touch err detected (n=%d)\n", + __func__, num_cur_tch); + num_cur_tch = si->sensing_conf_data.max_tch; + } + + if (tch.hdr[CY_TCH_LO]) + parade_debug(dev, DEBUG_LEVEL_1, "%s: Large area detected\n", + __func__); + + /* extract xy_data for all currently reported touches */ + parade_debug(dev, DEBUG_LEVEL_2, "%s: extract data num_cur_rec=%d\n", + __func__, num_cur_tch); + if (num_cur_tch) + cyttsp5_get_proximity_touch(pd, &tch, num_cur_tch); + else + cyttsp5_report_proximity(pd, false); + + return 0; +} + +static int cyttsp5_proximity_attention(struct device *dev) +{ + struct cyttsp5_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 = cyttsp5_xy_worker(pd); + mutex_unlock(&pd->prox_lock); + if (rc < 0) + dev_err(dev, "%s: xy_worker error r=%d\n", __func__, rc); + + return rc; +} + +static int cyttsp5_startup_attention(struct device *dev) +{ + struct cyttsp5_proximity_data *pd = get_prox_data(dev); + + mutex_lock(&pd->prox_lock); + cyttsp5_report_proximity(pd, false); + mutex_unlock(&pd->prox_lock); + + return 0; +} + +static int _cyttsp5_set_proximity_via_touchmode_enabled( + struct cyttsp5_proximity_data *pd, bool enable) +{ + struct device *dev = pd->dev; + u32 touchmode_enabled; + int rc; + + rc = cyttsp5_request_nonhid_get_param(dev, 0, + CY_RAM_ID_TOUCHMODE_ENABLED, &touchmode_enabled); + if (rc) + return rc; + + if (enable) + touchmode_enabled |= 0x80; + else + touchmode_enabled &= 0x7F; + + rc = cyttsp5_request_nonhid_set_param(dev, 0, + CY_RAM_ID_TOUCHMODE_ENABLED, touchmode_enabled, + CY_RAM_ID_TOUCHMODE_ENABLED_SIZE); + + return rc; +} + +static int _cyttsp5_set_proximity_via_proximity_enable( + struct cyttsp5_proximity_data *pd, bool enable) +{ + struct device *dev = pd->dev; + u32 proximity_enable; + int rc; + + rc = cyttsp5_request_nonhid_get_param(dev, 0, + CY_RAM_ID_PROXIMITY_ENABLE, &proximity_enable); + if (rc) + return rc; + + if (enable) + proximity_enable |= 0x01; + else + proximity_enable &= 0xFE; + + rc = cyttsp5_request_nonhid_set_param(dev, 0, + CY_RAM_ID_PROXIMITY_ENABLE, proximity_enable, + CY_RAM_ID_PROXIMITY_ENABLE_SIZE); + + return rc; +} + +static int _cyttsp5_set_proximity(struct cyttsp5_proximity_data *pd, + bool enable) +{ + if (!IS_PIP_VER_GE(pd->si, 1, 4)) + return _cyttsp5_set_proximity_via_touchmode_enabled(pd, + enable); + + return _cyttsp5_set_proximity_via_proximity_enable(pd, enable); +} + +static int _cyttsp5_proximity_enable(struct cyttsp5_proximity_data *pd) +{ + struct device *dev = pd->dev; + int rc = 0; + + pm_runtime_get_sync(dev); + + rc = cyttsp5_request_exclusive(dev, + CYTTSP5_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(dev, "%s: Error on request exclusive r=%d\n", + __func__, rc); + goto exit; + } + + rc = _cyttsp5_set_proximity(pd, true); + if (rc < 0) { + dev_err(dev, "%s: Error on request enable proximity scantype r=%d\n", + __func__, rc); + goto exit_release; + } + + parade_debug(dev, DEBUG_LEVEL_2, "%s: setup subscriptions\n", __func__); + + /* set up touch call back */ + _cyttsp5_subscribe_attention(dev, CY_ATTEN_IRQ, CYTTSP5_PROXIMITY_NAME, + cyttsp5_proximity_attention, CY_MODE_OPERATIONAL); + + /* set up startup call back */ + _cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP, + CYTTSP5_PROXIMITY_NAME, cyttsp5_startup_attention, 0); + +exit_release: + cyttsp5_release_exclusive(dev); +exit: + return rc; +} + +static int _cyttsp5_proximity_disable(struct cyttsp5_proximity_data *pd, + bool force) +{ + struct device *dev = pd->dev; + int rc = 0; + + rc = cyttsp5_request_exclusive(dev, + CYTTSP5_PROXIMITY_REQUEST_EXCLUSIVE_TIMEOUT); + if (rc < 0) { + dev_err(dev, "%s: Error on request exclusive r=%d\n", + __func__, rc); + goto exit; + } + + rc = _cyttsp5_set_proximity(pd, false); + if (rc < 0) { + dev_err(dev, "%s: Error on request disable proximity scan r=%d\n", + __func__, rc); + goto exit_release; + } + +exit_release: + cyttsp5_release_exclusive(dev); + +exit: + if (!rc || force) { + _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_IRQ, + CYTTSP5_PROXIMITY_NAME, cyttsp5_proximity_attention, + CY_MODE_OPERATIONAL); + + _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, + CYTTSP5_PROXIMITY_NAME, cyttsp5_startup_attention, 0); + } + + pm_runtime_put(dev); + + return rc; +} + +static ssize_t cyttsp5_proximity_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cyttsp5_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, CY_MAX_PRBUF_SIZE, "%d\n", val); +} + +static ssize_t cyttsp5_proximity_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct cyttsp5_proximity_data *pd = get_prox_data(dev); + unsigned long value; + int rc; + + rc = kstrtoul(buf, 10, &value); + if (rc < 0 || (value != 0 && value != 1)) { + dev_err(dev, "%s: Invalid value\n", __func__); + return -EINVAL; + } + + mutex_lock(&pd->sysfs_lock); + if (value) { + if (pd->enable_count++) { + parade_debug(dev, DEBUG_LEVEL_2, "%s: '%s' already enabled\n", + __func__, pd->input->name); + } else { + rc = _cyttsp5_proximity_enable(pd); + if (rc) + pd->enable_count--; + } + } else { + if (--pd->enable_count) { + if (pd->enable_count < 0) { + dev_err(dev, "%s: '%s' unbalanced disable\n", + __func__, pd->input->name); + pd->enable_count = 0; + } + } else { + rc = _cyttsp5_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, S_IRUSR | S_IWUSR, + cyttsp5_proximity_enable_show, + cyttsp5_proximity_enable_store); + +static int cyttsp5_setup_input_device_and_sysfs(struct device *dev) +{ + struct cyttsp5_proximity_data *pd = get_prox_data(dev); + int signal = CY_IGNORE_VALUE; + int i; + int rc; + + rc = device_create_file(dev, &dev_attr_prox_enable); + if (rc) { + dev_err(dev, "%s: Error, could not create enable\n", + __func__); + goto exit; + } + + parade_debug(dev, DEBUG_LEVEL_2, "%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 != CY_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) { + dev_err(dev, "%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; +} + +static int cyttsp5_setup_input_attention(struct device *dev) +{ + struct cyttsp5_proximity_data *pd = get_prox_data(dev); + int rc; + + pd->si = _cyttsp5_request_sysinfo(dev); + if (!pd->si) + return -EINVAL; + + rc = cyttsp5_setup_input_device_and_sysfs(dev); + if (!rc) + rc = _cyttsp5_set_proximity(pd, false); + + _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, + CYTTSP5_PROXIMITY_NAME, cyttsp5_setup_input_attention, 0); + + return rc; +} + +int cyttsp5_proximity_probe(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + struct cyttsp5_proximity_data *pd = &cd->pd; + struct cyttsp5_platform_data *pdata = dev_get_platdata(dev); + struct cyttsp5_proximity_platform_data *prox_pdata; + int rc = 0; + + if (!pdata || !pdata->prox_pdata) { + dev_err(dev, "%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. */ + parade_debug(dev, DEBUG_LEVEL_2, + "%s: Create the input device and register it\n", __func__); + pd->input = input_allocate_device(); + if (!pd->input) { + dev_err(dev, "%s: Error, failed to allocate input device\n", + __func__); + rc = -ENODEV; + goto error_alloc_failed; + } + + if (pd->pdata->inp_dev_name) + pd->input->name = pd->pdata->inp_dev_name; + else + pd->input->name = CYTTSP5_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 = _cyttsp5_request_sysinfo(dev); + + if (pd->si) { + rc = cyttsp5_setup_input_device_and_sysfs(dev); + if (rc) + goto error_init_input; + + rc = _cyttsp5_set_proximity(pd, false); + } else { + dev_err(dev, "%s: Fail get sysinfo pointer from core p=%p\n", + __func__, pd->si); + _cyttsp5_subscribe_attention(dev, CY_ATTEN_STARTUP, + CYTTSP5_PROXIMITY_NAME, cyttsp5_setup_input_attention, + 0); + } + + return 0; + +error_init_input: + input_free_device(pd->input); +error_alloc_failed: +error_no_pdata: + dev_err(dev, "%s failed.\n", __func__); + return rc; +} + +int cyttsp5_proximity_release(struct device *dev) +{ + struct cyttsp5_proximity_data *pd = get_prox_data(dev); + + if (pd->input_device_registered) { + /* Disable proximity sensing */ + mutex_lock(&pd->sysfs_lock); + if (pd->enable_count) + _cyttsp5_proximity_disable(pd, true); + mutex_unlock(&pd->sysfs_lock); + device_remove_file(dev, &dev_attr_prox_enable); + input_unregister_device(pd->input); + } else { + input_free_device(pd->input); + _cyttsp5_unsubscribe_attention(dev, CY_ATTEN_STARTUP, + CYTTSP5_PROXIMITY_NAME, cyttsp5_setup_input_attention, + 0); + } + + return 0; +} diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_regs.h b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_regs.h new file mode 100644 index 000000000000..b447dc7607fd --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_regs.h @@ -0,0 +1,1235 @@ +/* + * cyttsp5_regs.h + * Parade TrueTouch(TM) Standard Product V5 Registers. + * For use with Parade touchscreen controllers. + * Supported parts include: + * CYTMA5XX + * CYTMA448 + * CYTMA445A + * CYTT21XXX + * CYTT31XXX + * + * Copyright (C) 2015 Parade Technologies + * Copyright (C) 2012-2015 Cypress Semiconductor + * + * 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 + * + */ + +#ifndef _CYTTSP5_REGS_H +#define _CYTTSP5_REGS_H + +#include +#ifdef CONFIG_HAS_EARLYSUSPEND +#include +#elif defined(CONFIG_FB) +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cyttsp5_core.h" + +#include +#include +#include +#include + +#ifdef CYTTSP5_SENSOR_EN +#include +#endif + +//#define PT_PANEL_ID_FROM_MFG + +#define EASYWAKE_TSG6 +#define PT_PIP2_MAX_FILE_SIZE 0x18000 +#define CY_FW_FILE_PREFIX "cyttsp5_fw" +#define CY_FW_FILE_SUFFIX ".bin" +#define CY_FW_FILE_NAME "cyttsp5_fw.bin" + +#define TTHE_TUNER_SUPPORT + +#ifdef TTHE_TUNER_SUPPORT +#define CYTTSP5_TTHE_TUNER_FILE_NAME "tthe_tuner" +#endif + +#define CY_MAX_PRBUF_SIZE PIPE_BUF +#define CY_PR_TRUNCATED " truncated..." + +#define CY_DEFAULT_CORE_ID "cyttsp5_core0" +#define CY_MAX_NUM_CORE_DEVS 5 +#define CY_IRQ_ASSERTED_VALUE 0 +#define MFG_DATA_NUM 5 + +#ifdef CY_ENABLE_MAX_ELEN +#define CY_MAX_ELEN 100 +#endif + +enum PARADE_DEBUG_LEVEL { + DEBUG_LEVEL_0,/*no debug info*/ + DEBUG_LEVEL_1,/*only print dev_dbg info*/ + DEBUG_LEVEL_2,/*print dev_dbg and dev_vdbg info*/ + DEBUG_LEVEL_NUM +}; + +#define CY_INITIAL_DEBUG_LEVEL 0 +#define CY_INITIAL_SHOW_TIME_STAMP 0 + +//#define VERBOSE_DEBUG +/* +* those current debug level smaller than setted debug level +* will be printed out +*/ + +#define parade_debug(dev, dlevel, format, arg...) \ + do { \ + struct cyttsp5_core_data *cd_tmp = dev_get_drvdata(dev);\ + if (cd_tmp->debug_level >= dlevel) {\ + dev_dbg(dev, format, ##arg);\ + } \ + } while (0) + + +/* HID */ +#define HID_CYVENDOR 0xff010000 +#define CY_HID_VENDOR_ID 0x04B4 +#define CY_HID_BL_PRODUCT_ID 0xC100 +#define CY_HID_APP_PRODUCT_ID 0xC101 +#define CY_HID_VERSION 0x0100 +#define CY_HID_APP_REPORT_ID 0xF7 +#define CY_HID_BL_REPORT_ID 0xFF + +#define HID_INVALID_REPORT_ID 0x0 +#define HID_TOUCH_REPORT_ID 0x1 +#define HID_BTN_REPORT_ID 0x3 +#define HID_WAKEUP_REPORT_ID 0x4 +#define HID_NOISE_METRIC_REPORT_ID 0x5 +#define HID_TRACKING_HEATMAP_REPOR_ID 0xE +#define HID_SENSOR_DATA_REPORT_ID 0xF +#define HID_APP_RESPONSE_REPORT_ID 0x1F +#define HID_APP_OUTPUT_REPORT_ID 0x2F +#define HID_BL_RESPONSE_REPORT_ID 0x30 +#define HID_BL_OUTPUT_REPORT_ID 0x40 +#define HID_RESPONSE_REPORT_ID 0xF0 + +#define HID_OUTPUT_RESPONSE_REPORT_OFFSET 2 +#define HID_OUTPUT_RESPONSE_CMD_OFFSET 4 +#define HID_OUTPUT_RESPONSE_CMD_MASK 0x7F +#define HID_OUTPUT_CMD_OFFSET 6 +#define HID_OUTPUT_CMD_MASK 0x7F + +#define HID_SYSINFO_CYDATA_OFFSET 5 +#define HID_SYSINFO_SENSING_OFFSET 33 +#define HID_SYSINFO_BTN_OFFSET 48 +#define HID_SYSINFO_BTN_MASK 0xFF +#define HID_SYSINFO_MAX_BTN 8 + +#define HID_POWER_ON 0x0 +#define HID_POWER_SLEEP 0x1 +#define HID_LENGTH_BYTES 2 +#define HID_LENGTH_AND_REPORT_ID_BYTES 3 + +/* Timeout in ms */ +#define CY_REQUEST_EXCLUSIVE_TIMEOUT 8000 +#define CY_WATCHDOG_TIMEOUT 1000 +#define CY_HID_RESET_TIMEOUT 5000 +#define CY_HID_AUTO_CALI_CPLT_TIMEOUT 2500 +/* HID_DESCRIPTOR_TIMEOUT value based on FW spec (CAL_OS) */ +#define CY_HID_GET_HID_DESCRIPTOR_TIMEOUT 4000 +#define CY_HID_GET_REPORT_DESCRIPTOR_TIMEOUT 500 +#define CY_HID_SET_POWER_TIMEOUT 500 +#ifdef VERBOSE_DEBUG +#define CY_HID_OUTPUT_TIMEOUT 2000 +#else +#define CY_HID_OUTPUT_TIMEOUT 200 +#endif +#define CY_HID_OUTPUT_START_BOOTLOADER_TIMEOUT 2000 +#define CY_HID_OUTPUT_USER_TIMEOUT 8000 +#define CY_HID_OUTPUT_GET_SYSINFO_TIMEOUT 3000 +#define CY_HID_OUTPUT_CALIBRATE_IDAC_TIMEOUT 5000 +#define CY_HID_OUTPUT_WRITE_CONF_BLOCK_TIMEOUT 400 +#define CY_HID_OUTPUT_RUN_SELF_TEST_TIMEOUT 10000 +#define CY_HID_OUTPUT_BL_INITIATE_BL_TIMEOUT 20000 +#define CY_HID_OUTPUT_BL_PROGRAM_AND_VERIFY_TIMEOUT 400 + +#define CY_WATCHDOG_RETRY_COUNT 60 + +/* 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 HID_ITEM_SIZE_MASK 0x03 +#define HID_ITEM_TYPE_MASK 0x0C +#define HID_ITEM_TAG_MASK 0xF0 + +#define HID_ITEM_SIZE_SHIFT 0 +#define HID_ITEM_TYPE_SHIFT 2 +#define HID_ITEM_TAG_SHIFT 4 + +#define HID_GET_ITEM_SIZE(x) \ + ((x & HID_ITEM_SIZE_MASK) >> HID_ITEM_SIZE_SHIFT) +#define HID_GET_ITEM_TYPE(x) \ + ((x & HID_ITEM_TYPE_MASK) >> HID_ITEM_TYPE_SHIFT) +#define HID_GET_ITEM_TAG(x) \ + ((x & HID_ITEM_TAG_MASK) >> HID_ITEM_TAG_SHIFT) + +#define IS_DEEP_SLEEP_CONFIGURED(x) \ + ((x) == 0 || (x) == 0xFF) + +#define IS_PIP_VER_GE(p, maj, min) \ + ((p)->cydata.pip_ver_major < (maj) ? \ + 0 : \ + ((p)->cydata.pip_ver_minor < (min) ? \ + 0 : \ + 1)) + +/* drv_debug commands */ +#define CY_DBG_SUSPEND 4 +#define CY_DBG_RESUME 5 +#define CY_DBG_SOFT_RESET 97 +#define CY_DBG_RESET 98 +#define CY_DBG_HID_RESET 50 +#define CY_DBG_HID_GET_REPORT 51 +#define CY_DBG_HID_SET_REPORT 52 +#define CY_DBG_HID_SET_POWER_ON 53 +#define CY_DBG_HID_SET_POWER_SLEEP 54 +#define CY_DBG_HID_NULL 100 +#define CY_DBG_HID_ENTER_BL 101 +#define CY_DBG_HID_SYSINFO 102 +#define CY_DBG_HID_SUSPEND_SCAN 103 +#define CY_DBG_HID_RESUME_SCAN 104 +#define CY_DBG_HID_STOP_WD 105 +#define CY_DBG_HID_START_WD 106 + +#define CY_TTHE_TUNER_EXIT 107 +#define CY_TTHE_BUF_CLEAN 108 + +/* + * Commands that require additional parameters + * will be in the 200 range. Commands that do not + * require additional parameters remain below 200. + */ +#define CY_DBG_REPORT_LEVEL 200 +#define CY_DBG_WATCHDOG_INTERVAL 201 +#define CY_DBG_SHOW_TIMESTAMP 202 + +/* 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_CY_UNDEFINED 0xff010000 +#define HID_CY_BOOTLOADER 0xff010001 +#define HID_CY_TOUCHAPPLICATION 0xff010002 +#define HID_CY_BUTTONS 0xff010020 +#define HID_CY_GENERICITEM 0xff010030 +#define HID_CY_LARGEOBJECT 0xff010040 +#define HID_CY_NOISEEFFECTS 0xff010041 +#define HID_CY_REPORTCOUNTER 0xff010042 +#define HID_CY_TOUCHTYPE 0xff010060 +#define HID_CY_EVENTID 0xff010061 +#define HID_CY_MAJORAXISLENGTH 0xff010062 +#define HID_CY_MINORAXISLENGTH 0xff010063 +#define HID_CY_ORIENTATION 0xff010064 +#define HID_CY_BUTTONSIGNAL 0xff010065 +#define HID_CY_MAJOR_CONTACT_AXIS_LENGTH 0xff010066 +#define HID_CY_MINOR_CONTACT_AXIS_LENGTH 0xff010067 +#define HID_CY_TCH_COL_USAGE_PG 0x000D0022 +#define HID_CY_BTN_COL_USAGE_PG 0xFF010020 + +#define PANEL_ID_NOT_ENABLED (0xFF) +#define PANEL_ID_DEFAULT (0x00) + +#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 CY_RAM_ID_TOUCHMODE_ENABLED 0x02 +#define CY_RAM_ID_PROXIMITY_ENABLE 0x20 +#define CY_RAM_ID_TOUCHMODE_ENABLED_SIZE 1 +#define CY_RAM_ID_PROXIMITY_ENABLE_SIZE 1 + +/* abs signal capabilities offsets in the frameworks array */ +enum cyttsp5_sig_caps { + CY_SIGNAL_OST, + CY_MIN_OST, + CY_MAX_OST, + CY_FUZZ_OST, + CY_FLAT_OST, + CY_NUM_ABS_SET /* number of signal capability fields */ +}; + +/* helpers */ +#define NUM_SIGNALS(frmwrk) ((frmwrk)->size / CY_NUM_ABS_SET) +#define PARAM(frmwrk, sig_ost, cap_ost) \ + ((frmwrk)->abs[((sig_ost) * CY_NUM_ABS_SET) + (cap_ost)]) + +#define PARAM_SIGNAL(frmwrk, sig_ost) PARAM(frmwrk, sig_ost, CY_SIGNAL_OST) +#define PARAM_MIN(frmwrk, sig_ost) PARAM(frmwrk, sig_ost, CY_MIN_OST) +#define PARAM_MAX(frmwrk, sig_ost) PARAM(frmwrk, sig_ost, CY_MAX_OST) +#define PARAM_FUZZ(frmwrk, sig_ost) PARAM(frmwrk, sig_ost, CY_FUZZ_OST) +#define PARAM_FLAT(frmwrk, sig_ost) PARAM(frmwrk, sig_ost, CY_FLAT_OST) + +/* abs axis signal offsets in the framworks array */ +enum cyttsp5_sig_ost { + CY_ABS_X_OST, + CY_ABS_Y_OST, + CY_ABS_P_OST, + CY_ABS_W_OST, + CY_ABS_ID_OST, + CY_ABS_MAJ_OST, + CY_ABS_MIN_OST, + CY_ABS_OR_OST, + CY_ABS_TOOL_OST, + CY_ABS_D_OST, + CY_NUM_ABS_OST /* number of abs signals */ +}; + +enum hid_command { + HID_CMD_RESERVED, + HID_CMD_RESET, + HID_CMD_GET_REPORT, + HID_CMD_SET_REPORT, + HID_CMD_GET_IDLE, + HID_CMD_SET_IDLE, + HID_CMD_GET_PROTOCOL, + HID_CMD_SET_PROTOCOL, + HID_CMD_SET_POWER, + HID_CMD_VENDOR = 0xE, +}; + +enum hid_output_cmd_type { + HID_OUTPUT_CMD_APP, + HID_OUTPUT_CMD_BL, +}; + +enum hid_output { + HID_OUTPUT_NULL, + HID_OUTPUT_START_BOOTLOADER, + HID_OUTPUT_GET_SYSINFO, + HID_OUTPUT_SUSPEND_SCANNING, + HID_OUTPUT_RESUME_SCANNING, + HID_OUTPUT_GET_PARAM, + HID_OUTPUT_SET_PARAM, + HID_OUTPUT_GET_NOISE_METRICS, + HID_OUTPUT_RESERVED, + HID_OUTPUT_ENTER_EASYWAKE_STATE, + HID_OUTPUT_VERIFY_CONFIG_BLOCK_CRC = 0x20, + HID_OUTPUT_GET_CONFIG_ROW_SIZE, + HID_OUTPUT_READ_CONF_BLOCK, + HID_OUTPUT_WRITE_CONF_BLOCK, + HID_OUTPUT_GET_DATA_STRUCTURE, + HID_OUTPUT_LOAD_SELF_TEST_PARAM, + HID_OUTPUT_RUN_SELF_TEST, + HID_OUTPUT_GET_SELF_TEST_RESULT, + HID_OUTPUT_CALIBRATE_IDACS, + HID_OUTPUT_INITIALIZE_BASELINES, + HID_OUTPUT_EXEC_PANEL_SCAN, + HID_OUTPUT_RETRIEVE_PANEL_SCAN, + HID_OUTPUT_START_SENSOR_DATA_MODE, + HID_OUTPUT_STOP_SENSOR_DATA_MODE, + HID_OUTPUT_START_TRACKING_HEATMAP_MODE, + HID_OUTPUT_INT_PIN_OVERRIDE = 0x40, + HID_OUTPUT_STORE_PANEL_SCAN = 0x60, + HID_OUTPUT_PROCESS_PANEL_SCAN, + HID_OUTPUT_DISCARD_INPUT_REPORT, + HID_OUTPUT_LAST, + HID_OUTPUT_USER_CMD, +}; + +enum hid_output_bl { + HID_OUTPUT_BL_VERIFY_APP_INTEGRITY = 0x31, + HID_OUTPUT_BL_GET_INFO = 0x38, + HID_OUTPUT_BL_PROGRAM_AND_VERIFY, + HID_OUTPUT_BL_LAUNCH_APP = 0x3B, + HID_OUTPUT_BL_GET_PANEL_ID = 0x3E, + HID_OUTPUT_BL_INITIATE_BL = 0x48, + HID_OUTPUT_BL_LAST, +}; + +#define HID_OUTPUT_BL_SOP 0x1 +#define HID_OUTPUT_BL_EOP 0x17 + +enum hid_output_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_UKNOWN = 15, + ERROR_INVALID, +}; + +enum cyttsp5_mode { + CY_MODE_UNKNOWN, + CY_MODE_BOOTLOADER, + CY_MODE_OPERATIONAL, +}; + +enum cyttsp5_cmd_status { + CY_CMD_STATUS_SUCCESS, + CY_CMD_STATUS_FAILURE, +}; + +enum { + CY_IC_GRPNUM_RESERVED, + CY_IC_GRPNUM_CMD_REGS, + CY_IC_GRPNUM_TCH_REP, + CY_IC_GRPNUM_DATA_REC, + CY_IC_GRPNUM_TEST_REC, + CY_IC_GRPNUM_PCFG_REC, + CY_IC_GRPNUM_TCH_PARM_VAL, + CY_IC_GRPNUM_TCH_PARM_SIZE, + CY_IC_GRPNUM_RESERVED1, + CY_IC_GRPNUM_RESERVED2, + CY_IC_GRPNUM_OPCFG_REC, + CY_IC_GRPNUM_DDATA_REC, + CY_IC_GRPNUM_MDATA_REC, + CY_IC_GRPNUM_TEST_REGS, + CY_IC_GRPNUM_BTN_KEYS, + CY_IC_GRPNUM_TTHE_REGS, + CY_IC_GRPNUM_SENSING_CONF, + CY_IC_GRPNUM_NUM, +}; + +enum cyttsp5_event_id { + CY_EV_NO_EVENT, + CY_EV_TOUCHDOWN, + CY_EV_MOVE, /* significant displacement (> act dist) */ + CY_EV_LIFTOFF, /* record reports last position */ +}; + +enum cyttsp5_object_id { + CY_OBJ_STANDARD_FINGER, + CY_OBJ_PROXIMITY, + CY_OBJ_STYLUS, + CY_OBJ_HOVER, + CY_OBJ_GLOVE, +}; + +enum cyttsp5_self_test_result { + CY_ST_RESULT_PASS, + CY_ST_RESULT_FAIL, + CY_ST_RESULT_HOST_MUST_INTERPRET = 0xFF, +}; + +enum cyttsp5_self_test_id { + CY_ST_ID_NULL, + CY_ST_ID_BIST, + CY_ST_ID_SHORTS, + CY_ST_ID_OPENS, + CY_ST_ID_AUTOSHORTS, + CY_ST_ID_CM_PANEL, + CY_ST_ID_CP_PANEL, + CY_ST_ID_CM_BUTTON, + CY_ST_ID_CP_BUTTON, +}; + +#define CY_NUM_MFGID 8 + +/* System Information interface definitions */ +struct cyttsp5_cydata_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[CY_NUM_MFGID]; + __le16 post_code; +} __packed; + +struct cyttsp5_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 cyttsp5_cydata { + 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[CY_NUM_MFGID]; +}; + +struct cyttsp5_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 cyttsp5_tch_abs { /* for ordering within the extracted touch data array */ + CY_TCH_X, /* X */ + CY_TCH_Y, /* Y */ + CY_TCH_P, /* P (Z) */ + CY_TCH_T, /* TOUCH ID */ + CY_TCH_E, /* EVENT ID */ + CY_TCH_O, /* OBJECT ID */ + CY_TCH_TIP, /* OBJECT ID */ + CY_TCH_MAJ, /* TOUCH_MAJOR */ + CY_TCH_MIN, /* TOUCH_MINOR */ + CY_TCH_OR, /* ORIENTATION */ + CY_TCH_NUM_ABS, +}; + +enum cyttsp5_tch_hdr { + CY_TCH_TIME, /* SCAN TIME */ + CY_TCH_NUM, /* NUMBER OF RECORDS */ + CY_TCH_LO, /* LARGE OBJECT */ + CY_TCH_NOISE, /* NOISE EFFECT */ + CY_TCH_COUNTER, /* REPORT_COUNTER */ + CY_TCH_NUM_HDR, +}; + +static const char * const cyttsp5_tch_abs_string[] = { + [CY_TCH_X] = "X", + [CY_TCH_Y] = "Y", + [CY_TCH_P] = "P", + [CY_TCH_T] = "T", + [CY_TCH_E] = "E", + [CY_TCH_O] = "O", + [CY_TCH_TIP] = "TIP", + [CY_TCH_MAJ] = "MAJ", + [CY_TCH_MIN] = "MIN", + [CY_TCH_OR] = "OR", + [CY_TCH_NUM_ABS] = "INVALID", +}; + +static const char * const cyttsp5_tch_hdr_string[] = { + [CY_TCH_TIME] = "SCAN TIME", + [CY_TCH_NUM] = "NUMBER OF RECORDS", + [CY_TCH_LO] = "LARGE OBJECT", + [CY_TCH_NOISE] = "NOISE EFFECT", + [CY_TCH_COUNTER] = "REPORT_COUNTER", + [CY_TCH_NUM_HDR] = "INVALID", +}; + +static const int cyttsp5_tch_abs_field_map[] = { + [CY_TCH_X] = 0x00010030 /* HID_GD_X */, + [CY_TCH_Y] = 0x00010031 /* HID_GD_Y */, + [CY_TCH_P] = HID_DI_PRESSURE, + [CY_TCH_T] = HID_DI_CONTACTID, + [CY_TCH_E] = HID_CY_EVENTID, + [CY_TCH_O] = HID_CY_TOUCHTYPE, + [CY_TCH_TIP] = HID_DI_TIP, + [CY_TCH_MAJ] = HID_CY_MAJORAXISLENGTH, + [CY_TCH_MIN] = HID_CY_MINORAXISLENGTH, + [CY_TCH_OR] = HID_CY_ORIENTATION, + [CY_TCH_NUM_ABS] = 0, +}; + +static const int cyttsp5_tch_hdr_field_map[] = { + [CY_TCH_TIME] = HID_DI_SCANTIME, + [CY_TCH_NUM] = HID_DI_CONTACTCOUNT, + [CY_TCH_LO] = HID_CY_LARGEOBJECT, + [CY_TCH_NOISE] = HID_CY_NOISEEFFECTS, + [CY_TCH_COUNTER] = HID_CY_REPORTCOUNTER, + [CY_TCH_NUM_HDR] = 0, +}; + +#define CY_NUM_EXT_TCH_FIELDS 3 + +struct cyttsp5_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 cyttsp5_touch { + int hdr[CY_TCH_NUM_HDR]; + int abs[CY_TCH_NUM_ABS]; +}; + +/* button to keycode support */ +#define CY_BITS_PER_BTN 1 +#define CY_NUM_BTN_EVENT_ID ((1 << CY_BITS_PER_BTN) - 1) + +enum cyttsp5_btn_state { + CY_BTN_RELEASED = 0, + CY_BTN_PRESSED = 1, + CY_BTN_NUM_STATE +}; + +struct cyttsp5_btn { + bool enabled; + int state; /* CY_BTN_PRESSED, CY_BTN_RELEASED */ + int key_code; +}; + +enum cyttsp5_ic_ebid { + CY_TCH_PARM_EBID, + CY_MDATA_EBID, + CY_DDATA_EBID, +}; + +/* ttconfig block */ +#define CY_TTCONFIG_VERSION_OFFSET 8 +#define CY_TTCONFIG_VERSION_SIZE 2 +#define CY_TTCONFIG_VERSION_ROW 0 + +struct cyttsp5_ttconfig { + u16 version; + u16 crc; +}; + +struct cyttsp5_report_desc_data { + u16 tch_report_id; + u16 tch_record_size; + u16 tch_header_size; + u16 btn_report_id; +}; + +struct cyttsp5_sysinfo { + bool ready; + struct cyttsp5_cydata cydata; + struct cyttsp5_sensing_conf_data sensing_conf_data; + struct cyttsp5_report_desc_data desc; + int num_btns; + struct cyttsp5_btn *btn; + struct cyttsp5_ttconfig ttconfig; + struct cyttsp5_tch_abs_params tch_hdr[CY_TCH_NUM_HDR]; + struct cyttsp5_tch_abs_params tch_abs[CY_TCH_NUM_ABS]; + u8 *xy_mode; + u8 *xy_data; +}; + +enum cyttsp5_atten_type { + CY_ATTEN_IRQ, + CY_ATTEN_STARTUP, + CY_ATTEN_EXCLUSIVE, + CY_ATTEN_WAKE, + CY_ATTEN_LOADER, + CY_ATTEN_SUSPEND, + CY_ATTEN_RESUME, + CY_ATTEN_NUM_ATTEN, +}; + +enum cyttsp5_sleep_state { + SS_SLEEP_OFF, + SS_SLEEP_ON, + SS_SLEEPING, + SS_WAKING, +}; + +enum cyttsp5_fb_state { + FB_ON, + FB_OFF, +}; + +enum cyttsp5_startup_state { + STARTUP_NONE, + STARTUP_QUEUED, + STARTUP_RUNNING, + STARTUP_ILLEGAL, +}; + +struct cyttsp5_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 cyttsp5_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 CY_HID_MAX_REPORTS 8 +#define CY_HID_MAX_FIELDS 128 +#define CY_HID_MAX_COLLECTIONS 3 +#define CY_HID_MAX_NESTED_COLLECTIONS CY_HID_MAX_COLLECTIONS + +#define CY_MAX_INPUT 512 +#define CY_PIP_1P7_EMPTY_BUF 0xFF00 + +enum cyttsp5_module_id { + CY_MODULE_MT, + CY_MODULE_BTN, + CY_MODULE_PROX, + CY_MODULE_LAST, +}; + +struct cyttsp5_mt_data; +struct cyttsp5_mt_function { + int (*mt_release)(struct device *dev); + int (*mt_probe)(struct device *dev, struct cyttsp5_mt_data *md); + void (*report_slot_liftoff)(struct cyttsp5_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 cyttsp5_mt_data { + struct device *dev; + struct cyttsp5_mt_platform_data *pdata; + struct cyttsp5_sysinfo *si; + struct input_dev *input; + struct cyttsp5_mt_function mt_function; + struct mutex mt_lock; + bool is_suspended; + bool input_device_registered; + char phys[NAME_MAX]; + int num_prv_rec; + int or_min; + int or_max; + int t_min; + int t_max; +}; + +struct cyttsp5_btn_data { + struct device *dev; + struct cyttsp5_btn_platform_data *pdata; + struct cyttsp5_sysinfo *si; + struct input_dev *input; + struct mutex btn_lock; + bool is_suspended; + bool input_device_registered; + char phys[NAME_MAX]; +}; + +struct cyttsp5_proximity_data { + struct device *dev; + struct cyttsp5_proximity_platform_data *pdata; + struct cyttsp5_sysinfo *si; + struct input_dev *input; + struct mutex prox_lock; + struct mutex sysfs_lock; + int enable_count; + bool input_device_registered; + char phys[NAME_MAX]; +}; + +enum cyttsp5_calibrate_idacs_sensing_mode { + CY_CI_SM_MUTCAP_FINE, + CY_CI_SM_MUTCAP_BUTTON, + CY_CI_SM_SELFCAP, +}; + +enum cyttsp5_initialize_baselines_sensing_mode { + CY_IB_SM_MUTCAP = 1, + CY_IB_SM_BUTTON = 2, + CY_IB_SM_SELFCAP = 4, + CY_IB_SM_BALANCED = 8, +}; + +struct cyttsp5_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_config_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 (*calibrate_idacs)(struct device *dev, int protect, u8 mode, + u8 *status); + int (*initialize_baselines)(struct device *dev, int protect, + u8 test_id, u8 *status); + int (*exec_panel_scan)(struct device *dev, int protect); + 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 (*write_conf_block)(struct device *dev, int protect, + 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 (*read_us_file)(struct device *dev, u8 *file_path, u8 *buf, + int *size); +}; + +typedef int (*cyttsp5_atten_func) (struct device *); + +struct cyttsp5_core_commands { + int (*subscribe_attention)(struct device *dev, + enum cyttsp5_atten_type type, char *id, + cyttsp5_atten_func func, int flags); + int (*unsubscribe_attention)(struct device *dev, + enum cyttsp5_atten_type type, char *id, + cyttsp5_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 (*request_restart)(struct device *dev, bool wait); + struct cyttsp5_sysinfo * (*request_sysinfo)(struct device *dev); + struct cyttsp5_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_hid_desc)(struct device *dev, int protect); + int (*request_get_mode)(struct device *dev, int protect, u8 *mode); + int (*request_enable_scan_type)(struct device *dev, u8 scan_type); + int (*request_disable_scan_type)(struct device *dev, u8 scan_type); + struct cyttsp5_core_nonhid_cmd *nonhid_cmd; +#ifdef TTHE_TUNER_SUPPORT + int (*request_tthe_print)(struct device *dev, u8 *buf, int buf_len, + const u8 *data_name); +#endif +}; + +struct cyttsp5_features { + uint8_t easywake; + uint8_t noise_metric; + uint8_t tracking_heatmap; + uint8_t sensor_data; +}; + +#if ((LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)) \ + && defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_RUNTIME)) +#define NEED_SUSPEND_NOTIFIER 1 +#else +#define NEED_SUSPEND_NOTIFIER 0 +#endif + +struct cyttsp5_module { + struct list_head node; + char *name; + int (*probe)(struct device *dev, void **data); + void (*release)(struct device *dev, void *data); +}; + +#ifdef CYTTSP5_SENSOR_EN +struct cyttsp5_sensor_platform_data { + struct input_dev *input_sensor_dev; + struct sensors_classdev ps_cdev; + int sensor_opened; + char sensor_data; /* 0 near, 1 far */ + struct cyttsp5_core_data *data; +}; +#endif + +enum cyttsp5_pm_mode { + CYTTSP5_PM_POWEROFF = 0, + CYTTSP5_PM_DEEPSLEEP, + CYTTSP5_PM_EASY_WAKEUP, + CYTTSP5_PM_ACTIVE, +}; + +struct cyttsp5_core_data { + struct list_head node; + struct list_head module_list; /* List of probed modules */ + char core_id[20]; + struct device *dev; + struct list_head atten_list[CY_ATTEN_NUM_ATTEN]; + struct list_head param_list; + struct mutex module_list_lock; + struct mutex system_lock; + struct mutex adap_lock; + struct mutex hid_report_lock; + enum cyttsp5_mode mode; + spinlock_t spinlock; + struct cyttsp5_mt_data md; + struct cyttsp5_btn_data bd; + struct cyttsp5_proximity_data pd; + int phys_num; + int number_of_open_input_device; + int pm_runtime_usage_count; + void *cyttsp5_dynamic_data[CY_MODULE_LAST]; + struct cyttsp5_platform_data *pdata; + struct cyttsp5_core_platform_data *cpdata; + const struct cyttsp5_bus_ops *bus_ops; + wait_queue_head_t wait_q; + enum cyttsp5_sleep_state sleep_state; + enum cyttsp5_startup_state startup_state; + int irq; + bool irq_enabled; + bool irq_wake; + bool irq_disabled; + u8 easy_wakeup_gesture; +#ifdef EASYWAKE_TSG6 + u8 gesture_id; + u8 gesture_data_length; + u8 gesture_data[80]; +#endif + u8 pip2_us_file_path[128]; + bool wake_initiated_by_device; + bool wait_until_wake; + u8 panel_id; +#if 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 work_struct startup_work; + struct work_struct ez_recovery_work; + struct cyttsp5_sysinfo sysinfo; + void *exclusive_dev; + int exclusive_waits; + struct work_struct watchdog_work; + struct timer_list watchdog_timer; + u16 startup_retry_count; + struct cyttsp5_hid_core hid_core; + int hid_cmd_state; + int hid_reset_cmd_state; /* reset can happen any time */ + struct cyttsp5_hid_desc hid_desc; + struct cyttsp5_hid_report *hid_reports[CY_HID_MAX_REPORTS]; + int num_hid_reports; + struct cyttsp5_features features; +#define CYTTSP5_PREALLOCATED_CMD_BUFFER 32 + u8 cmd_buf[CYTTSP5_PREALLOCATED_CMD_BUFFER]; + u8 input_buf[CY_MAX_INPUT]; + u8 response_buf[CY_MAX_INPUT]; +#ifdef CONFIG_HAS_EARLYSUSPEND + struct early_suspend es; +#elif defined(CONFIG_FB) + struct notifier_block fb_notifier; + enum cyttsp5_fb_state fb_state; +#endif +#ifdef TTHE_TUNER_SUPPORT + struct dentry *tthe_debugfs; + u8 *tthe_buf; + u32 tthe_buf_len; + struct mutex tthe_lock; + u8 tthe_exit; +#endif + struct wakeup_source *gesture_wakelock; + enum cyttsp5_pm_mode pm_mode; + bool should_enable_gesture; +#ifdef CYTTSP5_SENSOR_EN + struct mutex state_mutex; + struct cyttsp5_sensor_platform_data *sensor_pdata; + #endif + u8 pr_buf[CY_MAX_PRBUF_SIZE]; + u8 debug_level; + u32 watchdog_interval; + u8 show_timestamp; + u8 mfg_data[MFG_DATA_NUM]; + u8 calibrate_sensing_mode; + u8 calibrate_initialize_baselines; + u8 ic_buf[CY_MAX_PRBUF_SIZE]; + + /* touchscreen_mmi */ + struct ts_mmi_class_methods *imports; + int (*firmware_update)(struct device *dev, char *fwname); + char firmware_name[CYTTSP5_FIRMWARE_NAME_MAX_LEN]; + bool force_fw_upgrade; + const char *supplier; +}; +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 CY_CMD_RET_PANEL_IN_DATA_OFFSET 0 +#define CY_CMD_RET_PANEL_ELMNT_SZ_MASK 0x07 +#define CY_CMD_RET_PANEL_HDR 0x0A +#define CY_CMD_RET_PANEL_ELMNT_SZ_MAX 0x2 + +enum scan_data_type_list { + CY_MUT_RAW, + CY_MUT_BASE, + CY_MUT_DIFF, + CY_SELF_RAW, + CY_SELF_BASE, + CY_SELF_DIFF, + CY_BAL_RAW, + CY_BAL_BASE, + CY_BAL_DIFF, +}; +#endif + +struct cyttsp5_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, u8 write_len, + u8 *write_buf, u8 *read_buf); +}; + +static inline int cyttsp5_adap_read_default(struct cyttsp5_core_data *cd, + void *buf, int size) +{ + return cd->bus_ops->read_default(cd->dev, buf, size); +} + +static inline int cyttsp5_adap_read_default_nosize(struct cyttsp5_core_data *cd, + void *buf, int max) +{ + return cd->bus_ops->read_default_nosize(cd->dev, buf, max); +} + +static inline int cyttsp5_adap_write_read_specific(struct cyttsp5_core_data *cd, + u8 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 *cyttsp5_get_dynamic_data(struct device *dev, int id) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + return cd->cyttsp5_dynamic_data[id]; +} + +int request_exclusive(struct cyttsp5_core_data *cd, void *ownptr, + int timeout_ms); +int release_exclusive(struct cyttsp5_core_data *cd, void *ownptr); +int _cyttsp5_request_hid_output_get_param(struct device *dev, + int protect, u8 param_id, u32 *value); +int _cyttsp5_request_hid_output_set_param(struct device *dev, + int protect, u8 param_id, u32 value, u8 size); + +static inline int cyttsp5_request_exclusive(struct device *dev, int timeout_ms) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + return request_exclusive(cd, dev, timeout_ms); +} + +static inline int cyttsp5_release_exclusive(struct device *dev) +{ + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + return release_exclusive(cd, dev); +} + +static inline int cyttsp5_request_nonhid_get_param(struct device *dev, + int protect, u8 param_id, u32 *value) +{ + return _cyttsp5_request_hid_output_get_param(dev, protect, param_id, + value); +} + +static inline int cyttsp5_request_nonhid_set_param(struct device *dev, + int protect, u8 param_id, u32 value, u8 size) +{ + return _cyttsp5_request_hid_output_set_param(dev, protect, param_id, + value, size); +} + +#ifdef VERBOSE_DEBUG +void cyttsp5_pr_buf(struct device *dev, u8 *dptr, int size, + const char *data_name); +#else +#define cyttsp5_pr_buf(a, b, c, d) do { } while (0) +#endif + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT +int cyttsp5_devtree_create_and_get_pdata(struct device *adap_dev); +int cyttsp5_devtree_clean_pdata(struct device *adap_dev); +#else +static inline int cyttsp5_devtree_create_and_get_pdata(struct device *adap_dev) +{ + return 0; +} + +static inline int cyttsp5_devtree_clean_pdata(struct device *adap_dev) +{ + return 0; +} +#endif + +int cyttsp5_probe(const struct cyttsp5_bus_ops *ops, struct device *dev, + u16 irq, size_t xfer_buf_size); +int cyttsp5_release(struct cyttsp5_core_data *cd); + +struct cyttsp5_core_commands *cyttsp5_get_commands(void); +struct cyttsp5_core_data *cyttsp5_get_core_data(char *id); + +int cyttsp5_mt_release(struct device *dev); +int cyttsp5_mt_probe(struct device *dev); + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_BUTTON +int cyttsp5_btn_probe(struct device *dev); +int cyttsp5_btn_release(struct device *dev); +#else +static inline int cyttsp5_btn_probe(struct device *dev) { return 0; } +static inline int cyttsp5_btn_release(struct device *dev) { return 0; } +#endif + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_PROXIMITY +int cyttsp5_proximity_probe(struct device *dev); +int cyttsp5_proximity_release(struct device *dev); +#else +static inline int cyttsp5_proximity_probe(struct device *dev) { return 0; } +static inline int cyttsp5_proximity_release(struct device *dev) { return 0; } +#endif + +void cyttsp5_init_function_ptrs(struct cyttsp5_mt_data *md); +int _cyttsp5_subscribe_attention(struct device *dev, + enum cyttsp5_atten_type type, char *id, int (*func)(struct device *), + int mode); +int _cyttsp5_unsubscribe_attention(struct device *dev, + enum cyttsp5_atten_type type, char *id, int (*func)(struct device *), + int mode); +struct cyttsp5_sysinfo *_cyttsp5_request_sysinfo(struct device *dev); + +extern const struct dev_pm_ops cyttsp5_pm_ops; + +int cyttsp5_register_module(struct cyttsp5_module *module); +void cyttsp5_unregister_module(struct cyttsp5_module *module); + +void *cyttsp5_get_module_data(struct device *dev, + struct cyttsp5_module *module); + +#endif /* _CYTTSP5_REGS_H */ diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_spi.c b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_spi.c new file mode 100644 index 000000000000..a01b88b0c8a9 --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_spi.c @@ -0,0 +1,255 @@ +/* + * cyttsp5_spi.c + * Parade TrueTouch(TM) Standard Product V5 SPI Module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * CYTMA5XX + * CYTMA448 + * CYTMA445A + * CYTT21XXX + * CYTT31XXX + * + * Copyright (C) 2015 Parade Technologies + * Copyright (C) 2012-2015 Cypress Semiconductor + * + * 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 + * + */ + +#include "cyttsp5_regs.h" + +#include +#include + +#define CY_SPI_WR_OP 0x00 /* r/~w */ +#define CY_SPI_RD_OP 0x01 +#define CY_SPI_BITS_PER_WORD 8 +#define CY_SPI_SYNC_ACK 0x62 + +#define CY_SPI_CMD_BYTES 0 +#define CY_SPI_DATA_SIZE (2 * 256) +#define CY_SPI_DATA_BUF_SIZE (CY_SPI_CMD_BYTES + CY_SPI_DATA_SIZE) + +static void cyttsp5_spi_add_rw_msg(struct spi_message *msg, + struct spi_transfer *xfer, u8 *w_header, u8 *r_header, u8 op) +{ + xfer->tx_buf = w_header; + xfer->rx_buf = r_header; + w_header[0] = op; + xfer->len = 1; + spi_message_add_tail(xfer, msg); +} + +static int cyttsp5_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[2]; + u8 w_header[2]; + u8 r_header[2]; + int rc; + + memset(xfer, 0, sizeof(xfer)); + + spi_message_init(&msg); + cyttsp5_spi_add_rw_msg(&msg, &xfer[0], w_header, r_header, op); + + switch (op) { + case CY_SPI_RD_OP: + xfer[1].rx_buf = buf; + xfer[1].len = length; + spi_message_add_tail(&xfer[1], &msg); + break; + case CY_SPI_WR_OP: + xfer[1].tx_buf = buf; + xfer[1].len = length; + spi_message_add_tail(&xfer[1], &msg); + break; + default: + rc = -EIO; + goto exit; + } + + rc = spi_sync(spi, &msg); +exit: + if (rc < 0) + parade_debug(dev, DEBUG_LEVEL_2, "%s: spi_sync() error %d\n", + __func__, rc); + + if (r_header[0] != CY_SPI_SYNC_ACK) + return -EIO; + + return rc; +} + +static int cyttsp5_spi_read_default(struct device *dev, void *buf, int size) +{ + if (!buf || !size) + return 0; + + return cyttsp5_spi_xfer(dev, CY_SPI_RD_OP, buf, size); +} + +static int cyttsp5_spi_read_default_nosize(struct device *dev, u8 *buf, u32 max) +{ + u32 size; + int rc; + + if (!buf) + return 0; + + rc = cyttsp5_spi_xfer(dev, CY_SPI_RD_OP, buf, 2); + if (rc < 0) + return rc; + + size = get_unaligned_le16(&buf[0]); + if (!size) + return rc; + + if (size > max) + return -EINVAL; + + return cyttsp5_spi_read_default(dev, buf, size); +} + +static int cyttsp5_spi_write_read_specific(struct device *dev, u8 write_len, + u8 *write_buf, u8 *read_buf) +{ + int rc; + + rc = cyttsp5_spi_xfer(dev, CY_SPI_WR_OP, write_buf, write_len); + if (rc < 0) + return rc; + + if (read_buf) + rc = cyttsp5_spi_read_default_nosize(dev, read_buf, + CY_SPI_DATA_SIZE); + + return rc; +} + +static struct cyttsp5_bus_ops cyttsp5_spi_bus_ops = { + .bustype = BUS_SPI, + .read_default = cyttsp5_spi_read_default, + .read_default_nosize = cyttsp5_spi_read_default_nosize, + .write_read_specific = cyttsp5_spi_write_read_specific, +}; + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT +static const struct of_device_id cyttsp5_spi_of_match[] = { + { .compatible = "cy,cyttsp5_spi_adapter", }, + { } +}; +MODULE_DEVICE_TABLE(of, cyttsp5_spi_of_match); +#endif + +static int cyttsp5_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT + const struct of_device_id *match; +#endif + int rc; + + /* Set up SPI*/ + spi->bits_per_word = CY_SPI_BITS_PER_WORD; + spi->mode = SPI_MODE_0; + rc = spi_setup(spi); + if (rc < 0) { + dev_err(dev, "%s: SPI setup error %d\n", __func__, rc); + return rc; + } + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT + match = of_match_device(of_match_ptr(cyttsp5_spi_of_match), dev); + if (match) { + rc = cyttsp5_devtree_create_and_get_pdata(dev); + if (rc < 0) + return rc; + } +#endif + + rc = cyttsp5_probe(&cyttsp5_spi_bus_ops, &spi->dev, spi->irq, + CY_SPI_DATA_BUF_SIZE); + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT + if (rc && match) + cyttsp5_devtree_clean_pdata(dev); +#endif + + return rc; +} + +static int cyttsp5_spi_remove(struct spi_device *spi) +{ +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT + struct device *dev = &spi->dev; + const struct of_device_id *match; +#endif + struct cyttsp5_core_data *cd = dev_get_drvdata(&spi->dev); + + cyttsp5_release(cd); + +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT + match = of_match_device(of_match_ptr(cyttsp5_spi_of_match), dev); + if (match) + cyttsp5_devtree_clean_pdata(dev); +#endif + + return 0; +} + +static const struct spi_device_id cyttsp5_spi_id[] = { + { CYTTSP5_SPI_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, cyttsp5_spi_id); + +static struct spi_driver cyttsp5_spi_driver = { + .driver = { + .name = CYTTSP5_SPI_NAME, + .bus = &spi_bus_type, + .owner = THIS_MODULE, + .pm = &cyttsp5_pm_ops, +#ifdef CONFIG_TOUCHSCREEN_CYPRESS_CYTTSP5_DEVICETREE_SUPPORT + .of_match_table = cyttsp5_spi_of_match, +#endif + }, + .probe = cyttsp5_spi_probe, + .remove = (cyttsp5_spi_remove), + .id_table = cyttsp5_spi_id, +}; + +#if (KERNEL_VERSION(3, 3, 0) <= LINUX_VERSION_CODE) +module_spi_driver(cyttsp5_spi_driver); +#else +static int __init cyttsp5_spi_init(void) +{ + int err = spi_register_driver(&cyttsp5_spi_driver); + + pr_info("%s: Parade TTSP SPI Driver (Built %s) rc=%d\n", + __func__, CY_DRIVER_VERSION, err); + return err; +} +module_init(cyttsp5_spi_init); + +static void __exit cyttsp5_spi_exit(void) +{ + spi_unregister_driver(&cyttsp5_spi_driver); +} +module_exit(cyttsp5_spi_exit); +#endif + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product SPI Driver"); +MODULE_AUTHOR("Parade Technologies "); diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_test_device_access_api.c b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_test_device_access_api.c new file mode 100644 index 000000000000..d874821fa920 --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_test_device_access_api.c @@ -0,0 +1,442 @@ +/* + * cyttsp5_test_device_access_api.c + * Parade TrueTouch(TM) Standard Product V5 Device Access API test module. + * For use with Parade touchscreen controllers. + * Supported parts include: + * CYTMA5XX + * CYTMA448 + * CYTMA445A + * CYTT21XXX + * CYTT31XXX + * + * Copyright (C) 2015 Parade Technologies + * Copyright (C) 2012-2015 Cypress Semiconductor + * + * 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 + * + */ + +#include +#include "cyttsp5_device_access-api.h" +#include + +#define BUFFER_SIZE 256 + +#define COMMAND_GET_SYSTEM_INFO 2 +#define COMMAND_SUSPEND_SCANNING 3 +#define COMMAND_RESUME_SCANNING 4 +#define COMMAND_GET_PARAMETER 5 +#define COMMAND_SET_PARAMETER 6 + +#define PARAMETER_ACTIVE_DISTANCE_2 0x0B + +struct tt_output_report { + __le16 reg_address; + __le16 length; + u8 report_id; + u8 reserved; + u8 command; + u8 parameters[0]; +} __packed; + +struct tt_input_report { + __le16 length; + u8 report_id; + u8 reserved; + u8 command; + u8 return_data[0]; +} __packed; + +static int prepare_tt_output_report(struct tt_output_report *out, + u16 length, u8 command) +{ + put_unaligned_le16(0x04, &out->reg_address); + put_unaligned_le16(5 + length, &out->length); + + out->report_id = 0x2f; + out->reserved = 0x00; + out->command = command; + + return 7 + length; +} + +static int check_and_parse_tt_input_report(struct tt_input_report *in, + u16 *length, u8 *command) +{ + if (in->report_id != 0x1f) + return -EINVAL; + + *length = get_unaligned_le16(&in->length); + *command = in->command & 0x7f; + + return 0; +} + +static int prepare_get_system_info_report(u8 *buf) +{ + struct tt_output_report *out = (struct tt_output_report *)buf; + + return prepare_tt_output_report(out, 0, COMMAND_GET_SYSTEM_INFO); +} + +static int check_get_system_info_response(u8 *buf, u16 read_length) +{ + struct tt_input_report *in = (struct tt_input_report *)buf; + u16 length = 0; + u8 command = 0; + + if (read_length != 51) + return -EINVAL; + + if (check_and_parse_tt_input_report(in, &length, &command) + || command != COMMAND_GET_SYSTEM_INFO + || length != 51) + return -EINVAL; + + pr_info("PIP Major Version: %d\n", in->return_data[0]); + pr_info("PIP Minor Version: %d\n", in->return_data[1]); + pr_info("Touch Firmware Product Id: %d\n", + get_unaligned_le16(&in->return_data[2])); + pr_info("Touch Firmware Major Version: %d\n", in->return_data[4]); + pr_info("Touch Firmware Minor Version: %d\n", in->return_data[5]); + pr_info("Touch Firmware Internal Revision Control Number: %d\n", + get_unaligned_le32(&in->return_data[6])); + pr_info("Customer Specified Firmware/Configuration Version: %d\n", + get_unaligned_le16(&in->return_data[10])); + pr_info("Bootloader Major Version: %d\n", in->return_data[12]); + pr_info("Bootloader Minor Version: %d\n", in->return_data[13]); + pr_info("Family ID: 0x%02x\n", in->return_data[14]); + pr_info("Revision ID: 0x%02x\n", in->return_data[15]); + pr_info("Silicon ID: 0x%02x\n", + get_unaligned_le16(&in->return_data[16])); + pr_info("Parade Manufacturing ID[0]: 0x%02x\n", in->return_data[18]); + pr_info("Parade Manufacturing ID[1]: 0x%02x\n", in->return_data[19]); + pr_info("Parade Manufacturing ID[2]: 0x%02x\n", in->return_data[20]); + pr_info("Parade Manufacturing ID[3]: 0x%02x\n", in->return_data[21]); + pr_info("Parade Manufacturing ID[4]: 0x%02x\n", in->return_data[22]); + pr_info("Parade Manufacturing ID[5]: 0x%02x\n", in->return_data[23]); + pr_info("Parade Manufacturing ID[6]: 0x%02x\n", in->return_data[24]); + pr_info("Parade Manufacturing ID[7]: 0x%02x\n", in->return_data[25]); + pr_info("POST Result Code: 0x%02x\n", + get_unaligned_le16(&in->return_data[26])); + + pr_info("Number of X Electrodes: %d\n", in->return_data[28]); + pr_info("Number of Y Electrodes: %d\n", in->return_data[29]); + pr_info("Panel X Axis Length: %d\n", + get_unaligned_le16(&in->return_data[30])); + pr_info("Panel Y Axis Length: %d\n", + get_unaligned_le16(&in->return_data[32])); + pr_info("Panel X Axis Resolution: %d\n", + get_unaligned_le16(&in->return_data[34])); + pr_info("Panel Y Axis Resolution: %d\n", + get_unaligned_le16(&in->return_data[36])); + pr_info("Panel Pressure Resolution: %d\n", + get_unaligned_le16(&in->return_data[38])); + pr_info("X_ORG: %d\n", in->return_data[40]); + pr_info("Y_ORG: %d\n", in->return_data[41]); + pr_info("Panel ID: %d\n", in->return_data[42]); + pr_info("Buttons: 0x%02x\n", in->return_data[43]); + pr_info("BAL SELF MC: 0x%02x\n", in->return_data[44]); + pr_info("Max Number of Touch Records per Refresh Cycle: %d\n", + in->return_data[45]); + + return 0; +} + +static int prepare_get_parameter_report(u8 *buf, u8 parameter_id) +{ + struct tt_output_report *out = (struct tt_output_report *)buf; + + out->parameters[0] = parameter_id; + + return prepare_tt_output_report(out, 1, COMMAND_GET_PARAMETER); +} + +static int check_get_parameter_response(u8 *buf, u16 read_length, + u32 *parameter_value) +{ + struct tt_input_report *in = (struct tt_input_report *)buf; + u16 length = 0; + u8 command = 0; + u32 param_value = 0; + u8 param_id; + u8 param_size = 0; + + if (read_length != 8 && read_length != 9 && read_length != 11) + return -EINVAL; + + if (check_and_parse_tt_input_report(in, &length, &command) + || command != COMMAND_GET_PARAMETER + || (length != 8 && length != 9 && length != 11)) + return -EINVAL; + + param_id = in->return_data[0]; + + param_size = in->return_data[1]; + + if (param_size == 1) + param_value = in->return_data[2]; + else if (param_size == 2) + param_value = get_unaligned_le16(&in->return_data[2]); + else if (param_size == 4) + param_value = get_unaligned_le32(&in->return_data[2]); + else + return -EINVAL; + + pr_info("%s: Parameter ID: 0x%02x Value: 0x%02x\n", + __func__, param_id, param_value); + + if (parameter_value) + *parameter_value = param_value; + + return 0; +} + +static int prepare_set_parameter_report(u8 *buf, u8 parameter_id, + u8 parameter_size, u32 parameter_value) +{ + struct tt_output_report *out = (struct tt_output_report *)buf; + + out->parameters[0] = parameter_id; + out->parameters[1] = parameter_size; + + if (parameter_size == 1) + out->parameters[2] = (u8)parameter_value; + else if (parameter_size == 2) + put_unaligned_le16(parameter_value, &out->parameters[2]); + else if (parameter_size == 4) + put_unaligned_le32(parameter_value, &out->parameters[2]); + else + return -EINVAL; + + return prepare_tt_output_report(out, 2 + parameter_size, + COMMAND_SET_PARAMETER); +} + +static int check_set_parameter_response(u8 *buf, u16 read_length) +{ + struct tt_input_report *in = (struct tt_input_report *)buf; + u16 length = 0; + u8 command = 0; + u8 param_id; + u8 param_size = 0; + + if (read_length != 7) + return -EINVAL; + + if (check_and_parse_tt_input_report(in, &length, &command) + || command != COMMAND_SET_PARAMETER + || length != 7) + return -EINVAL; + + param_id = in->return_data[0]; + param_size = in->return_data[1]; + + pr_info("%s: Parameter ID: 0x%02x Size: 0x%02x\n", + __func__, param_id, param_size); + + return 0; +} + +static int prepare_suspend_scanning_report(u8 *buf) +{ + struct tt_output_report *out = (struct tt_output_report *)buf; + + return prepare_tt_output_report(out, 0, COMMAND_SUSPEND_SCANNING); +} + +static int check_suspend_scanning_response(u8 *buf, u16 read_length) +{ + struct tt_input_report *in = (struct tt_input_report *)buf; + u16 length = 0; + u8 command = 0; + + if (read_length != 5) + return -EINVAL; + + if (check_and_parse_tt_input_report(in, &length, &command) + || command != COMMAND_SUSPEND_SCANNING + || length != 5) + return -EINVAL; + + return 0; +} + +static int prepare_resume_scanning_report(u8 *buf) +{ + struct tt_output_report *out = (struct tt_output_report *)buf; + + return prepare_tt_output_report(out, 0, COMMAND_RESUME_SCANNING); +} + +static int check_resume_scanning_response(u8 *buf, u16 read_length) +{ + struct tt_input_report *in = (struct tt_input_report *)buf; + u16 length = 0; + u8 command = 0; + + if (read_length != 5) + return -EINVAL; + + if (check_and_parse_tt_input_report(in, &length, &command) + || command != COMMAND_RESUME_SCANNING + || length != 5) + return -EINVAL; + + return 0; +} + +void cyttsp5_user_command_async_cont(const char *core_name, + u16 read_len, u8 *read_buf, u16 write_len, u8 *write_buf, + u16 actual_read_length, int rc) +{ + if (rc) { + pr_err("%s: suspend scan fails\n", __func__); + goto exit; + } + + rc = check_suspend_scanning_response(read_buf, actual_read_length); + if (rc) { + pr_err("%s: check suspend scanning response fails\n", __func__); + goto exit; + } + + pr_info("%s: suspend scanning succeeds\n", __func__); +exit: + return; +} + +/* Read and write buffers */ +static u8 write_buf[BUFFER_SIZE]; +static u8 read_buf[BUFFER_SIZE]; + +static uint active_distance; +module_param(active_distance, uint, 0); + +static int __init cyttsp5_test_device_access_api_init(void) +{ + u32 initial_active_distance; + u16 actual_read_len; + int write_len; + int rc; + + pr_info("%s: Enter\n", __func__); + + /* CASE 1: Run get system information */ + write_len = prepare_get_system_info_report(write_buf); + + rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf), + read_buf, write_len, write_buf, + &actual_read_len); + if (rc) + goto exit; + + rc = check_get_system_info_response(read_buf, actual_read_len); + if (rc) + goto exit; + + /* CASE 2: Run get parameter (Active distance) */ + write_len = prepare_get_parameter_report(write_buf, + PARAMETER_ACTIVE_DISTANCE_2); + + rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf), + read_buf, write_len, write_buf, + &actual_read_len); + if (rc) + goto exit; + + rc = check_get_parameter_response(read_buf, actual_read_len, + &initial_active_distance); + if (rc) + goto exit; + + pr_info("%s: Initial Active Distance: %d\n", + __func__, initial_active_distance); + + /* CASE 3: Run set parameter (Active distance) */ + write_len = prepare_set_parameter_report(write_buf, + PARAMETER_ACTIVE_DISTANCE_2, 1, + active_distance); + + rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf), + read_buf, write_len, write_buf, + &actual_read_len); + if (rc) + goto exit; + + rc = check_set_parameter_response(read_buf, actual_read_len); + if (rc) + goto exit; + + pr_info("%s: Active Distance set to %d\n", __func__, active_distance); + + /* CASE 4: Run get parameter (Active distance) */ + write_len = prepare_get_parameter_report(write_buf, + PARAMETER_ACTIVE_DISTANCE_2); + + rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf), + read_buf, write_len, write_buf, + &actual_read_len); + if (rc) + goto exit; + + rc = check_get_parameter_response(read_buf, actual_read_len, + &active_distance); + if (rc) + goto exit; + + pr_info("%s: New Active Distance: %d\n", __func__, active_distance); + + /* CASE 5: Run suspend scanning asynchronously */ + write_len = prepare_suspend_scanning_report(write_buf); + + preempt_disable(); + rc = cyttsp5_device_access_user_command_async(NULL, + sizeof(read_buf), read_buf, write_len, write_buf, + cyttsp5_user_command_async_cont); + preempt_enable(); + if (rc) + goto exit; +exit: + return rc; +} +module_init(cyttsp5_test_device_access_api_init); + +static void __exit cyttsp5_test_device_access_api_exit(void) +{ + u16 actual_read_len; + int write_len; + int rc; + + /* CASE 6: Run resume scanning */ + write_len = prepare_resume_scanning_report(write_buf); + + rc = cyttsp5_device_access_user_command(NULL, sizeof(read_buf), + read_buf, write_len, write_buf, + &actual_read_len); + if (rc) + goto exit; + + rc = check_resume_scanning_response(read_buf, actual_read_len); + if (rc) + goto exit; + + pr_info("%s: resume scanning succeeds\n", __func__); +exit: + return; +} +module_exit(cyttsp5_test_device_access_api_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Parade TrueTouch(R) Standard Product Device Access Driver API Tester"); +MODULE_AUTHOR("Parade Technologies "); diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_ts_mmi.c b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_ts_mmi.c new file mode 100644 index 000000000000..e8839d29b436 --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_ts_mmi.c @@ -0,0 +1,386 @@ +/* + * Parade tma5xx touchscreen driver MMI class extention + * + * Copyright (C) 2022 Motorola Mobility + * + * 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 pr_fmt(fmt) "%s: " fmt, __func__ + +#include "cyttsp5_ts_mmi.h" +#include "cyttsp5_regs.h" + +#define ASSERT_PTR(p) if (p == NULL) { \ + dev_err(dev, "Failed to get driver data"); \ + return -ENODEV; \ + } +#define MAX_ATTRS_ENTRIES 10 + +#define ADD_ATTR(name) { \ + if (idx < MAX_ATTRS_ENTRIES) { \ + dev_info(dev, "%s: [%d] adding %p\n", __func__, idx, &dev_attr_##name.attr); \ + ext_attributes[idx] = &dev_attr_##name.attr; \ + idx++; \ + } else { \ + dev_err(dev, "%s: cannot add attribute '%s'\n", __func__, #name); \ + } \ +} + +static struct attribute *ext_attributes[MAX_ATTRS_ENTRIES]; +static struct attribute_group ext_attr_group = { + .attrs = ext_attributes, +}; + +extern int cyttsp5_core_suspend(struct device *dev); +extern int cyttsp5_core_resume(struct device *dev); + +static int cyttsp5_ts_mmi_methods_get_vendor(struct device *dev, void *cdata) { + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", "parade"); +} + +static int cyttsp5_mmi_get_class_entry_name(struct device *dev, void *cdata) { + struct cyttsp5_platform_data *pdata = dev_get_platdata(dev); + ASSERT_PTR(pdata); + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", pdata->core_pdata->class_entry_name); +} + +static int cyttsp5_ts_mmi_methods_get_productinfo(struct device *dev, void *cdata) { + char* ic_info; + struct cyttsp5_platform_data *pdata = dev_get_platdata(dev); + ASSERT_PTR(pdata); + + ic_info = strstr(pdata->ic_name, ","); + ic_info++; + + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", ic_info); +} + +static int cyttsp5_ts_mmi_methods_get_build_id(struct device *dev, void *cdata) { + u32 fw_ver_img; + u32 fw_revctrl_img; + u16 fw_config_ver_img; + struct cyttsp5_sysinfo *si; + + struct cyttsp5_core_commands *cmd = cyttsp5_get_commands(); + if (!cmd) + return -EINVAL; + + si = cmd->request_sysinfo(dev); + if (!si) { + dev_err(dev, "%s: Fail get sysinfo pointer from core\n", __func__); + return -EINVAL; + } + + fw_ver_img = si->cydata.fw_ver_major << 8; + fw_ver_img += si->cydata.fw_ver_minor; + fw_revctrl_img = si->cydata.revctrl; + fw_config_ver_img = si->cydata.fw_ver_conf; + + parade_debug(dev, DEBUG_LEVEL_1, + "%s: img vers:0x%04X ,revctrl vers:0x%04X, fw_config vers:0x%04X\n", __func__, + fw_ver_img, fw_revctrl_img, fw_config_ver_img); + + return snprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%04x", le32_to_cpu(fw_config_ver_img)); +} + +/*return firmware version*/ +static int cyttsp5_ts_mmi_methods_get_config_id(struct device *dev, void *cdata) { + u32 fw_ver_img; + u32 fw_revctrl_img; + struct cyttsp5_sysinfo *si; + + struct cyttsp5_core_commands *cmd = cyttsp5_get_commands(); + if (!cmd) + return -EINVAL; + + si = cmd->request_sysinfo(dev); + if (!si) { + dev_err(dev, "%s: Fail get sysinfo pointer from core\n", __func__); + return -EINVAL; + } + + fw_ver_img = si->cydata.fw_ver_major << 8; + fw_ver_img += si->cydata.fw_ver_minor; + fw_revctrl_img = si->cydata.revctrl; + + parade_debug(dev, DEBUG_LEVEL_1, + "%s: img vers:0x%04X ,revctrl vers:0x%04X\n", __func__, + fw_ver_img, fw_revctrl_img); + + return snprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%02d%02d%02d%02x", + (fw_revctrl_img>>24)&0xff, + (fw_revctrl_img>>16)&0xff, + (fw_revctrl_img>>8)&0xff, + (fw_revctrl_img)&0xff); +} + +static int cyttsp5_ts_mmi_methods_get_bus_type(struct device *dev, void *idata) { + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + ASSERT_PTR(cd); + + TO_INT(idata) = cd->bus_ops->bustype == BUS_I2C ? + TOUCHSCREEN_MMI_BUS_TYPE_I2C : TOUCHSCREEN_MMI_BUS_TYPE_SPI; + return 0; +} + +static int cyttsp5_ts_mmi_methods_get_irq_status(struct device *dev, void *idata) { + struct cyttsp5_platform_data *pdata = dev_get_platdata(dev); + ASSERT_PTR(pdata); + + TO_INT(idata) = gpio_get_value(pdata->core_pdata->irq_gpio); + return 0; +} + +static int cyttsp5_ts_mmi_methods_get_drv_irq(struct device *dev, void *idata) { + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + ASSERT_PTR(cd); + + TO_INT(idata) = cd ->irq_enabled; + return 0; +} + +static int cyttsp5_ts_mmi_methods_get_poweron(struct device *dev, void *idata) +{ + struct cyttsp5_platform_data *pdata = dev_get_platdata(dev); + ASSERT_PTR(pdata); + + TO_INT(idata) = (regulator_is_enabled(pdata->core_pdata->avdd) && + regulator_is_enabled(pdata->core_pdata->iovdd)) ? 1 : 0; + + return 0; +} + +static int cyttsp5_ts_mmi_methods_get_flashprog(struct device *dev, void *idata) { + int rc; + u8 mode; + struct cyttsp5_core_commands *cmd = cyttsp5_get_commands(); + if (!cmd) + return -EINVAL; + + rc = cmd->request_get_mode(dev, 0, &mode); + if (rc < 0) + return rc; + + TO_INT(idata) = mode == CY_MODE_BOOTLOADER ? 1 : 0; + return 0; +} + +static int cyttsp5_ts_mmi_methods_reset(struct device *dev, int type) { + struct cyttsp5_core_commands *cmd = cyttsp5_get_commands(); + if (!cmd) + return -EINVAL; + + cmd->request_reset(dev); + return 0; +} + +static int cyttsp5_ts_firmware_update(struct device *dev, char *fwname) { + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + ASSERT_PTR(cd); + + dev_info(dev, "%s, HW request update fw, %s", __func__, fwname); + + if (!fwname) { + dev_err(dev, "%s: Error, firmware name is null\n", + __func__); + return -EINVAL; + } + + if ( !cd->firmware_update) + return -ENOSYS; + + cd->force_fw_upgrade = 1; + snprintf(cd->firmware_name, CYTTSP5_FIRMWARE_NAME_MAX_LEN, "%s", fwname); + + if(cd->sleep_state != SS_SLEEP_OFF) { + dev_err(dev, "%s: Error, touch IC is on sleep state, can not do upgrade\n", + __func__); + return -EINVAL; + } + + return cd->firmware_update(dev, fwname); +} + +static __maybe_unused int cyttsp5_ts_mmi_methods_drv_irq(struct device *dev, int state) { + int ret = -ENODEV; + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + ASSERT_PTR(cd); + + mutex_lock(&cd->system_lock); + switch (state) { + case 0: + if (cd->irq_enabled) { + cd->irq_enabled = false; + /* Disable IRQ */ + disable_irq_nosync(cd->irq); + dev_info(dev, "%s: Driver IRQ now disabled\n", + __func__); + } else + dev_info(dev, "%s: Driver IRQ already disabled\n", + __func__); + ret = 0; + break; + + case 1: + if (cd->irq_enabled == false) { + cd->irq_enabled = true; + /* Enable IRQ */ + enable_irq(cd->irq); + dev_info(dev, "%s: Driver IRQ now enabled\n", + __func__); + } else + dev_info(dev, "%s: Driver IRQ already enabled\n", + __func__); + ret = 0; + break; + + default: + dev_err(dev, "%s: Invalid value\n", __func__); + } + mutex_unlock(&(cd->system_lock)); + + return ret; +} + +static int cyttsp5_ts_mmi_methods_power(struct device *dev, int on) { + int rc = 0; + atomic_t is_ignore; + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + ASSERT_PTR(cd); + + atomic_set(&is_ignore, 1); + /* Call platform power function */ + if (cd->cpdata->power) { + dev_info(cd->dev, "%s: Touch IC power on/off \n", __func__); + if(on == TS_MMI_POWER_ON) + { + rc = cd->cpdata->power(cd->cpdata, TS_MMI_POWER_ON, cd->dev, &is_ignore); + if (rc) { + dev_info(cd->dev, "%s: Power on touch IC success\n", __func__); + rc = -ENODEV; + } + } + else if(on == TS_MMI_POWER_OFF) + { + rc = cd->cpdata->power(cd->cpdata, TS_MMI_POWER_OFF, cd->dev, &is_ignore); + if (rc) { + dev_info(cd->dev, "%s: Power on touch IC failed \n", __func__); + rc = -ENODEV; + } + } + } + else { + dev_info(cd->dev, "%s: No Power operation func\n", __func__); + rc = -ENODEV; + } + + return rc; +} + +static int cyttsp5_ts_mmi_post_suspend(struct device *dev) { +#ifdef CYTTSP5_SENSOR_EN + unsigned char gesture_type = 0; + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + ASSERT_PTR(cd); +#endif + + pr_info("%s: Post suspend end", __func__); + +#ifdef CYTTSP5_SENSOR_EN + if (cd->imports && cd->imports->get_gesture_type) { + cd->imports->get_gesture_type(cd->dev, &gesture_type); + } + + if (gesture_type & TS_MMI_GESTURE_SINGLE) { + dev_info(dev, "%s: cli single tap enable\n", __func__); + cd->should_enable_gesture = 1; + } else { + dev_info(dev, "%s: cli single tap disable\n", __func__); + cd->should_enable_gesture = 0; + } +#endif + + cyttsp5_core_suspend(dev); + return 0; +} + +static int cyttsp5_ts_mmi_post_resume(struct device *dev) { + pr_info("%s: Post resume end", __func__); + cyttsp5_core_resume(dev); + return 0; +} + +static int cyttsp5_ts_mmi_extend_attribute_group(struct device *dev, struct attribute_group **group) +{ + int idx = 0; + + if (idx) { + ext_attributes[idx] = NULL; + *group = &ext_attr_group; + } else + *group = NULL; + + return 0; +} + +static struct ts_mmi_methods cyttsp5_ts_mmi_methods = { + .get_vendor = cyttsp5_ts_mmi_methods_get_vendor, + .get_class_entry_name = cyttsp5_mmi_get_class_entry_name, + .get_productinfo = cyttsp5_ts_mmi_methods_get_productinfo, + .get_build_id = cyttsp5_ts_mmi_methods_get_build_id, + .get_config_id = cyttsp5_ts_mmi_methods_get_config_id, + .get_bus_type = cyttsp5_ts_mmi_methods_get_bus_type, + .get_irq_status = cyttsp5_ts_mmi_methods_get_irq_status, + .get_drv_irq = cyttsp5_ts_mmi_methods_get_drv_irq, + .get_poweron = cyttsp5_ts_mmi_methods_get_poweron, + .get_flashprog = cyttsp5_ts_mmi_methods_get_flashprog, + /* SET methods */ + .reset = cyttsp5_ts_mmi_methods_reset, + .power = cyttsp5_ts_mmi_methods_power, + /* Firmware */ + .firmware_update = cyttsp5_ts_firmware_update, + /* vendor specific attribute group */ + .extend_attribute_group = cyttsp5_ts_mmi_extend_attribute_group, + /* PM callback */ + .post_resume = cyttsp5_ts_mmi_post_resume, + .post_suspend = cyttsp5_ts_mmi_post_suspend, +}; + +int cyttsp5_ts_mmi_dev_register(struct device *dev) { + int ret; + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + ret = ts_mmi_dev_register(dev, &cyttsp5_ts_mmi_methods); + if (ret) { + dev_err(dev, "%s, Failed to register ts mmi\n", __func__); + return ret; + } + dev_info(dev, "%s, Register ts mmi success\n", __func__); + + cd->imports = &cyttsp5_ts_mmi_methods.exports; + +#if defined(CONFIG_GTP_LIMIT_USE_SUPPLIER) + if (cd->imports && cd->imports->get_supplier) { + ret = cd->imports->get_supplier(cd->dev, &cd->supplier); + } +#endif + return 0; +} + +void cyttsp5_ts_mmi_dev_unregister(struct device *dev) { + struct cyttsp5_core_data *cd = dev_get_drvdata(dev); + + if (!cd) + dev_err(dev ,"Failed to get driver data"); + ts_mmi_dev_unregister(dev); +} + diff --git a/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_ts_mmi.h b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_ts_mmi.h new file mode 100644 index 000000000000..0f81d3e52ad5 --- /dev/null +++ b/drivers/input/touchscreen/parade_tma5xx_mmi/cyttsp5_ts_mmi.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2022 Motorola Mobility LLC + * + * 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. + */ +#ifndef __CYTTSP5_TS_MMI_H__ +#define __CYTTSP5_TS_MMI_H__ + +#include +#include + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +int cyttsp5_ts_mmi_dev_register(struct device *dev); +void cyttsp5_ts_mmi_dev_unregister(struct device *dev); +#else +static int inline cyttsp5_ts_mmi_dev_register(struct device *dev) { + return -ENOSYS; +} +static void inline cyttsp5_ts_mmi_dev_unregister(struct device *dev) { + return ; +} +#endif + +#endif diff --git a/drivers/input/touchscreen/pixart_mmi/Android.mk b/drivers/input/touchscreen/pixart_mmi/Android.mk new file mode 100644 index 000000000000..dcfcda829293 --- /dev/null +++ b/drivers/input/touchscreen/pixart_mmi/Android.mk @@ -0,0 +1,21 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(BUILD_PIXART_TOUCHPAD),true) +KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_PIXART_TS=y +ifeq ($(DRM_PANEL_EVENT_NOTIFICATIONS),true) + KBUILD_OPTIONS += CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := pct1812_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +#LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/mmi_info.ko +#ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) +# KBUILD_OPTIONS += CONFIG_INPUT_TOUCHSCREEN_MMI=y +# LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +#endif +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk +endif diff --git a/drivers/input/touchscreen/pixart_mmi/Kbuild b/drivers/input/touchscreen/pixart_mmi/Kbuild new file mode 100644 index 000000000000..de6944798f46 --- /dev/null +++ b/drivers/input/touchscreen/pixart_mmi/Kbuild @@ -0,0 +1,11 @@ +# add -Wall to try to catch everything we canss +# EXTRA_CFLAGS += -Wallss +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/pixart_mmi + +obj-m := pct1812_mmi.o +pct1812_mmi-objs := pct1812_captouch.o + +ifneq ($(filter m y, $(CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_EVENT_NOTIFICATIONS +endif diff --git a/drivers/input/touchscreen/pixart_mmi/Kconfig b/drivers/input/touchscreen/pixart_mmi/Kconfig new file mode 100644 index 000000000000..c25eaa4dccca --- /dev/null +++ b/drivers/input/touchscreen/pixart_mmi/Kconfig @@ -0,0 +1,13 @@ +# +# PixArt TOUCH driver configuration +# + +config TOUCHSCREEN_PIXART_TS + tristate "PixArt Touchscreen" + depends on I2C + help + Say Y here if you want support for PCT1812 touchscreen controller. + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called pct1812_mmi. diff --git a/drivers/input/touchscreen/pixart_mmi/Makefile b/drivers/input/touchscreen/pixart_mmi/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/input/touchscreen/pixart_mmi/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/input/touchscreen/pixart_mmi/pct1812_captouch.c b/drivers/input/touchscreen/pixart_mmi/pct1812_captouch.c new file mode 100644 index 000000000000..c93bc062c2ad --- /dev/null +++ b/drivers/input/touchscreen/pixart_mmi/pct1812_captouch.c @@ -0,0 +1,3002 @@ +// +// Description: I2C Adapter for PixArt Imaging PCT1812FF Capacitance Touch Controller Driver on Linux Kernel +// +// Copyright (c) 2021 Lenovo, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +#define MYNAME "pct1812_mmi" +#define pr_fmt(fmt) "pct1812_mmi: %s: " fmt, __func__ + +#define CALM_WDOG +#undef STATIC_PLATFORM_DATA +#undef DRY_RUN_UPDATE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_POINTS 5 +#define DFLT_X_RES 1280 +#define DFLT_Y_RES 768 + +#define AREAD (1) +#define AWRITE (0) + +#define INVALID_BYTE2 0xebeb +#define INVALID_BYTE 0xeb + +#define PCT1812_SECTOR_SIZE (0x1000) // 4K +#define CHUNK_SZ 256 + +#define SUPPORTED_FW_FILE_SIZE 0x0000F000 +#define FW_SECTION_SIZE 0x0000D000 +#define CUSTOM_SECTION_SIZE (0x00000000 + PCT1812_SECTOR_SIZE) +#define PARAM_SECTION_SIZE CUSTOM_SECTION_SIZE + +#define ADDRESS_FW 0x00000000 +#define ADDRESS_CUSTOM FW_SECTION_SIZE +#define ADDRESS_PARAM (SUPPORTED_FW_FILE_SIZE - PARAM_SECTION_SIZE) + +#define POWERUP_CODE 0x02 +#define RESET_CODE 0xaa +#define RESUME_CODE 0xbb +#define ENGINEERING_CODE 0xcc +#define SUSPEND_CODE 0x99 + +#define FRAME_NUM_REG 0x00 + +#define NUM_OBJ_REG 0x01 +#define OBJ_X0L_REG 0x04 +#define OBJ_X0H_REG 0x05 +#define OBJ_Y0L_REG 0x06 +#define OBJ_Y0H_REG 0x07 +#define OBJ_0_BASE OBJ_X0L_REG + +#define OBJ_ADDR(o) (OBJ_0_BASE + (o) * 4) + +#define GEST_TYPE_REG 0x60 +#define GEST_X0L_REG 0x62 +#define GEST_X0H_REG 0x63 +#define GEST_Y0L_REG 0x64 +#define GEST_Y0H_REG 0x65 +#define GEST_0_BASE GEST_X0L_REG + +#define GEST_ADDR(g) (GEST_0_BASE + (g) * 4) + +#define BOOT_STATUS_REG 0x70 +#define STATUS_REG 0x71 + +#define USER_BANK_REG 0x73 +#define USER_ADDR_REG 0x74 +#define USER_DATA_REG 0x75 + +#define SW_RESET_REG 0x7a +#define DEEP_SLEEP_REG 0x7c +#define ACTIVE_BANK_REG 0x7f + +#define BANK(a) (a) + +// USER BANK 0 +#define DEBUG_REG 0x00 +#define FW_REV_REG 0x01 +#define FW_VMIN_L_REG 0x02 +#define FW_MAJOR_REG 0x03 +#define MODEL_REG 0x04 +#define FW_PATCH_REG 0x06 +#define INTR_MASK_REG 0x07 +#define START_REG 0x09 +#define FLASH_BANK_REG 0x0f +#define REPORT_RATE_L_REG 0x10 +#define REPORT_RATE_H_REG 0x11 +#define MAX_RATE_L_REG 0x12 +#define MAX_RATE_H_REG 0x13 +#define OP_MODE_REG 0x15 +#define FORCE_OP_MODE_REG 0x16 +#define REST1_L_REG 0x18 +#define REST1_H_REG 0x19 +#define REST2_L_REG 0x1a +#define REST2_H_REG 0x1b +#define REST1_FRAME_RATE_REG 0x1c +#define REST2_FRAME_RATE_REG 0x1d +#define TX_REG 0x5a +#define RX_REG 0x59 +#define VER_LOW_REG 0x7e +#define VER_HIGH_REG 0x7f + +// USER BANK 1 +#define FLASH_PUP_REG 0x0d +#define KEY1_REG 0x2c +#define KEY2_REG 0x2d +#define ENG_MODE_REG 0x2e + +// USER BANK 2 +#define XRES_L_REG 0x00 +#define XRES_H_REG 0x01 +#define YRES_L_REG 0x02 +#define YRES_H_REG 0x03 +#define MAX_POINTS_REG 0x05 +#define SRAM_SELECT_REG 0x09 +#define SRAM_NSC_REG 0x0a +#define SRAM_PORT_REG 0x0b +#define ORIENTATION_REG 0x0e + +// USER BANK 3 +#define FUNCT_CTRL_REG 0x02 +#define ABS_TRIG_DIST_REG 0x0b +#define V_SCROLL_RES_REG 0x26 +#define H_SCROLL_RES_REG 0x2e +#define CUST_INFO_BASE 0x6a +#define CUST_INFO_RNUM 8 + +// USER BANK 4 +#define FLASH_STATUS_REG 0x1c +#define FLASH_FLASH_CMD_REG 0x20 +#define FLASH_DCOUNT0_REG 0x22 +#define FLASH_DCOUNT1_REG 0x23 +#define FLASH_ADDR0_REG 0x24 +#define FLASH_ADDR1_REG 0x25 +#define FLASH_ADDR2_REG 0x26 +#define FLASH_COMMAND_REG 0x2c +#define FLASH_SRAM_OFF0_REG 0x2e +#define FLASH_SRAM_OFF1_REG 0x2f + +#define STAT_BIT_ERR (1 << 0) +#define STAT_BIT_TOUCH (1 << 1) +#define STAT_BIT_GESTURE (1 << 3) +#define STAT_BIT_WDOG (1 << 7) +#define STAT_BIT_EABS (STAT_BIT_WDOG | STAT_BIT_TOUCH) +#define STAT_BIT_ALL 0xff + +#define SLEEP_BIT_DISABLE (1 << 0) +#define SLEEP_BIT_STAT (1 << 4) + +#define BOOT_COMPLETE 1 +#define MODE_COMPLETE (BOOT_COMPLETE | (1 << 7)) +#define TOUCH_RESET_DELAY 10 +#define POWER_UP_DELAY 50 +#define CMD_WAIT_DELAY 10 +#define WDOG_INTERVAL 1000 +#define SELFTEST_SHORT_INTERVAL 3000 +#define SELFTEST_LONG_INTERVAL 5000 +#define SELFTEST_EXTRA_LONG_INTERVAL 10000 +#define PON_DELAY 200 + +#define CNT_I2C_RETRY 3 +#define CNT_WAIT_RETRY 50 + +#define SNR_LOOP_COUNT 50 + +#define I2C_WRITE_BUFFER_SIZE 256 +#define I2C_READ_BUFFER_SIZE 4 + +#define PINCTRL_STATE_ACTIVE "touchpad_active" +#define PINCTRL_STATE_SUSPEND "touchpad_suspend" + +enum pwr_modes { + PWR_MODE_AUTO, //controlled by FW + PWR_MODE_RUN, + PWR_MODE_LPM, // REST1 + PWR_MODE_DEEP_SLEEP, // REST2 + PWR_MODE_SUSPEND, + PWR_MODE_NO_DEEP_SLEEP, + PWR_MODE_MAX +}; + +enum cmds { + CMD_INIT, + CMD_RESET, + CMD_SUSPEND, + CMD_RESUME, + CMD_RECOVER, + CMD_WDOG, + CMD_MODE_RESET, + CMD_CLEANUP, + CMD_CONFIG, + CMD_INFO +}; + +enum selftest_ids { + PCT1812_SELFTEST_NONE, + PCT1812_SELFTEST_FULL = 1, + PCT1812_SELFTEST_SNR, + PCT1812_SELFTEST_DRAW_LINE, + PCT1812_SELFTEST_IN_PROGRESS, + PCT1812_SELFTEST_MAX +}; + +enum gestures { + GEST_TAP = 2, + GEST_DBL_TAP, + GEST_VERT_SCROLL = 7, + GEST_HORIZ_SCROLL, + GEST_UP_SWIPE = 17, + GEST_DWN_SWIPE, + GEST_LFT_SWIPE, + GEST_RGHT_SWIPE +}; + +struct bank_reg { + unsigned char bank; + unsigned char reg; + unsigned char data; +}; + +struct pct1812_custom_cfg { + unsigned int max_x, max_y; + unsigned int scroll_min_distance; + unsigned int ty_offset; + bool flip_x; + bool flip_y; + bool report_gesture; + bool report_mt; + int reg_num; + struct bank_reg *patch; +}; + +struct pct1812_platform { + unsigned rst_gpio; + unsigned irq_gpio; + + struct i2c_client *client; + + const char *vdd; + const char *vio; + + struct pct1812_custom_cfg *config; +}; + +#if defined(STATIC_PLATFORM_DATA) +#define VDD_PS_NAME "pm8350c_l3" +#define VIO_PS_NAME "pm8350_s10" + +static struct pct1812_platform pct1812_pd = { + 0, 0, + 0, 84, + NULL, VDD_PS_NAME, VIO_PS_NAME +}; +#endif + +#define DBG_BIT_PM 0x00000001 +#define DBG_BIT_IRQ 0x00000002 +#define DBG_BIT_ISR 0x00000004 +#define DBG_BIT_I2C 0x00000008 +#define DBG_BIT_WORK 0x00000010 +#define DBG_BIT_TIME 0x00000020 +#define DBG_BIT_TEST 0x00000040 +#define DBG_BIT_FW 0x00000080 +#define DBG_BIT_UACC 0x00000100 +#define DBG_BIT_DTS 0x00000200 +#define DBG_BIT_DROP 0x00000400 +#define DBG_BIT_ALL 0x00010000 + +#define DBG_PRINT(s, fmt, ...) { \ + if (ts->debug & s) \ + pr_info(fmt, ##__VA_ARGS__); \ +} + +struct pct1812_data { + struct device *dev; + struct i2c_client *client; + struct input_dev *input_dev; + struct pct1812_platform *plat_data; + struct input_dev *idev; + + struct regulator *reg_vdd; + struct regulator *reg_vio; + + int tx_count; + int rx_count; + + int selftest; + int frame_count; + int frame_sample; + int test_type; + ktime_t test_start_time; + unsigned int frame_size; + unsigned int test_hdr_size; + unsigned int test_data_size; + unsigned char **test_data; + unsigned char test_intr_mask; + + struct mutex i2c_mutex; + struct mutex eventlock; + struct mutex cmdlock; + struct delayed_work worker; + + void (*transform)(struct pct1812_data *, unsigned int *); + bool irq_enabled; + struct kfifo cmd_pipe; + atomic_t touch_stopped; + + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + + unsigned char drop_mask; + unsigned short ver7e; + unsigned short ver7f; + unsigned char func_ctrl; + unsigned char model; + unsigned char major; + unsigned short minor; + unsigned char revision; + unsigned char patch; + unsigned short x_res; + unsigned short y_res; + unsigned char max_points; + unsigned char boot_block; + unsigned char custom_block[CUST_INFO_RNUM]; + unsigned short debug; +}; + +static ssize_t selftest_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t selftest_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t doreflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t forcereflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t drv_irq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t drv_irq_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t irq_status_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t info_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t mask_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t mask_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t debug_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t debug_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t selftest_bin_show(struct file *filp, struct kobject *kobp, + struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count); + +static DEVICE_ATTR_WO(doreflash); +static DEVICE_ATTR_WO(forcereflash); +static DEVICE_ATTR_WO(reset); +static DEVICE_ATTR_RO(ic_ver); +static DEVICE_ATTR_RO(info); +static DEVICE_ATTR_RO(vendor); +static DEVICE_ATTR_RO(irq_status); +static DEVICE_ATTR_RW(selftest); +static DEVICE_ATTR_RW(drv_irq); +static DEVICE_ATTR_RW(mask); +static DEVICE_ATTR_RW(debug); + +static struct attribute *pct1812_attrs[] = { + &dev_attr_doreflash.attr, + &dev_attr_forcereflash.attr, + &dev_attr_reset.attr, + &dev_attr_ic_ver.attr, + &dev_attr_vendor.attr, + &dev_attr_drv_irq.attr, + &dev_attr_irq_status.attr, + &dev_attr_selftest.attr, + &dev_attr_info.attr, + &dev_attr_mask.attr, + &dev_attr_debug.attr, + NULL, +}; + +static struct attribute_group pct1812_attrs_group = { + .attrs = pct1812_attrs, +}; + +static struct bin_attribute selftest_bin_attr = { + .attr = {.name = "selftest_bin", .mode = 0440}, + .read = selftest_bin_show, +}; + +static int inline pct1812_selftest_get(struct pct1812_data *ts) +{ + int cur = PCT1812_SELFTEST_NONE; + + mutex_lock(&ts->cmdlock); + cur = ts->selftest; + mutex_unlock(&ts->cmdlock); + + return cur; +} + +static void inline pct1812_selftest_set(struct pct1812_data *ts, enum selftest_ids mode) +{ + mutex_lock(&ts->cmdlock); + if (ts->selftest != mode) + ts->selftest = mode; + mutex_unlock(&ts->cmdlock); +} + +static void inline pct1812_drop_mask_set(struct pct1812_data *ts, unsigned char mask) +{ + mutex_lock(&ts->eventlock); + ts->drop_mask = mask; + mutex_unlock(&ts->eventlock); +} + +static void inline pct1812_fifo_cmd_add(struct pct1812_data *ts, enum cmds command, unsigned int delay) +{ + kfifo_put(&ts->cmd_pipe, command); + schedule_delayed_work(&ts->worker, msecs_to_jiffies(delay)); +} + +static void inline pct1812_delay_ms(unsigned int ms) +{ + if (ms < 20) + usleep_range(ms * 1000, ms * 1000); + else + msleep(ms); +} + +static int pct1812_i2c_read(struct pct1812_data *ts, unsigned char reg, unsigned char *data, int len) +{ + unsigned char retry, buf[I2C_READ_BUFFER_SIZE]; + int ret; + struct i2c_msg msg[2]; +#if 0 + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + dev_err(&ts->client->dev, "%s: POWER_STATUS : OFF\n", __func__); + return -EIO; + } +#endif + buf[0] = reg; + + msg[0].addr = ts->client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = buf; + + msg[1].addr = ts->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = len; + msg[1].buf = data; + + mutex_lock(&ts->i2c_mutex); + for (retry = 0; retry < CNT_I2C_RETRY; retry++) { + ret = i2c_transfer(ts->client->adapter, msg, 2); + if (ret == 2) { + ret = 0; /* indicate a success */ + break; + } +#if 0 + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + dev_err(&ts->client->dev, "%s: POWER_STATUS : OFF, retry:%d\n", __func__, retry); + mutex_unlock(&ts->i2c_mutex); + return -EIO; + } +#endif + pct1812_delay_ms(1); + if (retry > 1) { + dev_err(&ts->client->dev, "%s: I2C retry %d, ret:%d\n", __func__, retry + 1, ret); + //ts->comm_err_count++; + } + } + mutex_unlock(&ts->i2c_mutex); + + if (retry == CNT_I2C_RETRY) { + dev_err(&ts->client->dev, "%s: I2C read over retry limit\n", __func__); + ret = -EIO; + } + + if (ts->debug & DBG_BIT_I2C) { + int i; + pr_info("R: %02X | ", reg); + for (i = 0; i < len; i++) + pr_cont("%02X ", data[i]); + pr_cont("\n"); + } + + return ret; +} + +static int pct1812_i2c_write(struct pct1812_data *ts, unsigned char reg, unsigned char *data, int len) +{ + unsigned char retry, buf[I2C_WRITE_BUFFER_SIZE + 1]; + int ret; + struct i2c_msg msg; + + if (len > I2C_WRITE_BUFFER_SIZE) { + dev_err(&ts->client->dev, "%s: len is larger than buffer size\n", __func__); + return -EINVAL; + } +#if 0 + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + dev_err(&ts->client->dev, "%s: POWER_STATUS : OFF\n", __func__); + return -EIO; + } +#endif + buf[0] = reg; + memcpy(buf + 1, data, len); + + msg.addr = ts->client->addr; + msg.flags = 0; + msg.len = len + 1; + msg.buf = buf; + + mutex_lock(&ts->i2c_mutex); + for (retry = 0; retry < CNT_I2C_RETRY; retry++) { + ret = i2c_transfer(ts->client->adapter, &msg, 1); + if (ret == 1) { + ret = 0; /* indicate a success */ + break; + } +#if 0 + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + dev_err(&ts->client->dev, "%s: POWER_STATUS : OFF, retry:%d\n", __func__, retry); + mutex_unlock(&ts->i2c_mutex); + return -EIO; + } +#endif + pct1812_delay_ms(1); + if (retry > 1) { + dev_err(&ts->client->dev, "%s: I2C retry %d, ret:%d\n", __func__, retry + 1, ret); + //ts->comm_err_count++; + } + } + mutex_unlock(&ts->i2c_mutex); + + if (retry == CNT_I2C_RETRY) { + dev_err(&ts->client->dev, "%s: I2C write over retry limit\n", __func__); + ret = -EIO; + } + + if (ts->debug & DBG_BIT_I2C) { + int i; + pr_info("W: %02X | ", reg); + for (i = 0; i < len; i++) + pr_cont("%02X ", data[i]); + pr_cont("\n"); + } + + return ret; +} + +static void inline pct1812_set_irq(struct pct1812_data *ts, bool on) +{ + if (on) { + if (!ts->irq_enabled) { + ts->irq_enabled = true; + enable_irq(ts->plat_data->client->irq); + DBG_PRINT(DBG_BIT_IRQ,"IRQ enabled\n"); + } + } else { + if (ts->irq_enabled) { + ts->irq_enabled = false; + disable_irq(ts->plat_data->client->irq); + DBG_PRINT(DBG_BIT_IRQ,"IRQ disabled\n"); + } + } +} + +static void inline pct1812_transform_coords(struct pct1812_data *ts, unsigned int *y) +{ + *y += ts->plat_data->config->ty_offset; +} + +#define PATCH_SZ 3 + +#ifndef STATIC_PLATFORM_DATA +static int pct1812_parse_dt(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct pct1812_platform *pdata = dev->platform_data; + struct pct1812_data *ts = i2c_get_clientdata(client); + struct device_node *cfg, *np = dev->of_node; + int r, ret = 0; + + if (of_property_read_string(np, "pct1812,regulator_vdd", &pdata->vdd)) { + dev_err(dev, "%s: Failed to get VDD name property\n", __func__); + return -EINVAL; + } + + if (of_property_read_string(np, "pct1812,regulator_vio", &pdata->vio)) { + dev_warn(dev, "%s: Failed to get VIO name property\n", __func__); + //return -EINVAL; + } + + pdata->irq_gpio = of_get_named_gpio(np, "pct1812,irq-gpio", 0); + if (!gpio_is_valid(pdata->irq_gpio)) { + dev_err(&client->dev, "%s: Failed to get irq gpio\n", __func__); + return -EINVAL; + } + + pdata->rst_gpio = of_get_named_gpio(np, "pct1812,reset-gpio", 0); + if (!gpio_is_valid(pdata->rst_gpio)) + pdata->rst_gpio = -EINVAL; + + cfg = of_find_node_by_name(np, "pct1812-custom-config"); + if (cfg) { + unsigned int byte_len, reg_num; + unsigned int coords[2], *adata; + + pdata->config = devm_kzalloc(dev, sizeof(struct pct1812_custom_cfg), GFP_KERNEL); + if (!pdata->config) { + ret = -ENOMEM; + goto put_node_exit; + } + + if (!of_property_read_u32_array(cfg, "max-coords", coords, 2)) { + pdata->config->max_x = coords[0]; + pdata->config->max_y = coords[1]; + dev_info(dev, "%s: max_coords:(%d,%d)\n", + __func__, pdata->config->max_x, pdata->config->max_y); + } + + if (of_property_read_bool(cfg, "flip-x")) { + pdata->config->flip_x = true; + dev_info(dev, "%s: flip X\n", __func__); + } + + if (of_property_read_bool(cfg, "flip-y")) { + pdata->config->flip_y = true; + dev_info(dev, "%s: flip Y\n", __func__); + } + + if (of_property_read_bool(cfg, "report-mt")) { + pdata->config->report_mt = true; + dev_info(dev, "%s: report MT\n", __func__); + } + + if (of_property_read_bool(cfg, "report-gesture")) { + pdata->config->report_gesture = true; + dev_info(dev, "%s: report GESTURE\n", __func__); + } + + if (!of_property_read_u32(cfg, "scroll-min-distance", &pdata->config->scroll_min_distance)) + dev_info(dev, "%s: scroll_min_disctance:(%u)\n", __func__, pdata->config->scroll_min_distance); + + if (!of_property_read_u32(cfg, "ty-offset", &pdata->config->ty_offset)) { + ts->transform = pct1812_transform_coords; + dev_info(dev, "%s: ty_offset:(%u)\n", __func__, pdata->config->ty_offset); + } + + if (of_find_property(cfg, "bank-register-data", NULL)) { + of_find_property(cfg, "bank-register-data", &byte_len); + pr_err("bank-register-data property len %u\n", byte_len); + if ((byte_len / sizeof(u32)) < PATCH_SZ || + (byte_len / sizeof(u32)) % PATCH_SZ) { + dev_err(dev, "%s: Invalid patch data alignment\n", __func__); + ret = -ENODEV; + goto put_node_exit; + } + + adata = kzalloc(byte_len, GFP_KERNEL); + if (!adata) { + ret = -ENOMEM; + goto put_node_exit; + } + + reg_num = byte_len / (sizeof(u32) * PATCH_SZ); + pdata->config->patch = devm_kzalloc(dev, sizeof(struct bank_reg) * reg_num, GFP_KERNEL); + if (!pdata->config->patch) { + ret = -ENOMEM; + goto kfree_exit; + } + + ret = of_property_read_u32_array(cfg, "bank-register-data", adata, byte_len / sizeof(u32)); + if (ret < 0) { + dev_err(dev, "%s: Error reading patch data; rc = %d\n", __func__, ret); + goto kfree_exit; + } + + pdata->config->reg_num = reg_num; + pr_err("%d registers in the patch set\n", reg_num); + for (r = 0; r < reg_num; r++) { + struct bank_reg *brd = &pdata->config->patch[r]; + + brd->bank = (unsigned char)adata[r * PATCH_SZ]; + brd->reg = (unsigned char)adata[r * PATCH_SZ + 1]; + brd->data = (unsigned char)adata[r * PATCH_SZ + 2]; + pr_err("[%d(off=%d)]: bank=%d, reg0x%02x, data=0x%02x\n", + r, r * PATCH_SZ, brd->bank, brd->reg, brd->data); + } +kfree_exit: + kfree(adata); + } else + pr_err("has no run time patches\n"); +put_node_exit: + of_node_put(cfg); + } + + return ret; +} +#endif + +static int pct1812_platform_init(struct pct1812_platform *pdata) +{ + struct device *dev = &pdata->client->dev; + int ret; + + if (gpio_is_valid(pdata->irq_gpio)) { + ret = gpio_request_one(pdata->irq_gpio, GPIOF_DIR_IN, "pct1812,irq"); + if (ret) { + dev_err(dev, "%s: Unable to request gpio [%d]\n", __func__, pdata->irq_gpio); + return -EINVAL; + } + dev_info(dev, "%s: using irq gpio %d\n", __func__, pdata->irq_gpio); + } + + if (gpio_is_valid(pdata->rst_gpio)) { + ret = gpio_request_one(pdata->rst_gpio, GPIOF_DIR_OUT, "pct1812,reset"); + if (ret) { + dev_err(dev, "%s: Unable to request gpio [%d]\n", __func__, pdata->rst_gpio); + return -EINVAL; + } + dev_info(dev, "%s: using reset gpio %d\n", __func__, pdata->rst_gpio); + } + + pdata->client->irq = gpio_to_irq(pdata->irq_gpio); + + return 0; +} + +static int inline comp2_16b(unsigned char *data) +{ + int result; + /* 16bits 2's compliment data */ + if (data[1] & 0x80) + result = (data[0] | (data[1] << 8) | 0xFFFF0000); + else + result = (data[0] | (data[1] << 8)); + + return result; +} + +#define X0L 0 +#define X0H 1 +#define Y0L 2 +#define Y0H 3 + +#define X_GET(d) (&d[X0L]) +#define Y_GET(d) (&d[Y0L]) + +#define UINT16_X(d) ((d[1] << 8) | d[0] | 0x00000000) +#define UINT16_Y(d) ((d[3] << 8) | d[2] | 0x00000000) + +static int pct1812_read_xy_coords(struct pct1812_data *ts, + unsigned char start_addr, unsigned char *tdata) +{ + int ret; + + ret = pct1812_i2c_read(ts, start_addr, &tdata[X0L], sizeof(tdata[X0L])); + ret = !ret && pct1812_i2c_read(ts, start_addr + 1, &tdata[X0H], sizeof(tdata[X0H])); + ret = !ret && pct1812_i2c_read(ts, start_addr + 2, &tdata[Y0L], sizeof(tdata[Y0L])); + ret = !ret && pct1812_i2c_read(ts, start_addr + 3, &tdata[Y0H], sizeof(tdata[Y0H])); + + return ret ? -EIO : 0; +} + +static int inline pct1812_selftest_push(struct pct1812_data *ts, + unsigned char *coords, unsigned int length) +{ + int ret = -ERANGE; + + if ((ts->frame_sample + 1) <= ts->frame_count) { + memcpy(ts->test_data[ts->frame_sample], coords, length); + ts->frame_sample++; + ts->test_data_size += length; + DBG_PRINT(DBG_BIT_TEST,"push test sample to slot %d; data size %u\n", ts->frame_sample, ts->test_data_size); + ret = 0; + } else + dev_warn(ts->dev, "%s: Buffer is full\n", __func__); + + return ret; +} + +static int pct1812_process_touch_event(struct pct1812_data *ts) +{ + unsigned char nt, coords[4] = {0}; + unsigned int x, y; + int i, mode, ret; + int pressed; + static int finger[MAX_POINTS]; + + ret = pct1812_i2c_read(ts, NUM_OBJ_REG, &nt, sizeof(nt)); + if (ret) + return -EIO; + DBG_PRINT(DBG_BIT_ISR,"num objects %d\n", (int)nt); + for (i = 0; i < MAX_POINTS; i++) { + if (i < nt) { + ret = pct1812_read_xy_coords(ts, OBJ_ADDR(i), coords); + if (ret) { + dev_err(ts->dev, "%s: error reading obj %d\n", __func__, i); + return -EIO; + } + x = UINT16_X(coords); + y = UINT16_Y(coords); + + if (ts->plat_data->config) { + if (ts->plat_data->config->flip_x) + x = ts->plat_data->config->max_x - x; + if (ts->plat_data->config->flip_y) + y = ts->plat_data->config->max_y - y; + } + + if (ts->transform) + ts->transform(ts, &y); + + DBG_PRINT(DBG_BIT_ISR,"[@%02x]: x=%d, y=%d\n", OBJ_ADDR(i), x, y); + + if (i == 0) { // draw line self-test supports one finger only + mode = pct1812_selftest_get(ts); + if (mode == PCT1812_SELFTEST_DRAW_LINE) + pct1812_selftest_push(ts, coords, sizeof(coords)); + DBG_PRINT(DBG_BIT_TEST,"[@%02x]: x=%d, y=%d\n", OBJ_ADDR(i), x, y); + } + pressed = 1; + if (finger[i] != 1) { + if (ts->plat_data->config && ts->plat_data->config->report_mt) { + input_mt_slot(ts->idev, i); + input_mt_report_slot_state(ts->idev, MT_TOOL_FINGER, 1); + } else { + DBG_PRINT(DBG_BIT_DROP,"dropped MT DOWN\n"); + } + input_report_key(ts->idev, BTN_TOUCH, 1); + input_report_key(ts->idev, BTN_TOOL_FINGER, 1); + DBG_PRINT(DBG_BIT_ISR,"[%d]: PRESS\n", i); + } + + if (ts->plat_data->config && ts->plat_data->config->report_mt) { + input_report_abs(ts->idev, ABS_MT_POSITION_X, x); + input_report_abs(ts->idev, ABS_MT_POSITION_Y, y); + } else { + DBG_PRINT(DBG_BIT_DROP,"dropped MT X/Y\n"); + } + input_report_abs(ts->idev, ABS_X, x); + input_report_abs(ts->idev, ABS_Y, y); + input_sync(ts->idev); + } else + pressed = 0; + + if (finger[i] && !pressed) { + if (ts->plat_data->config && ts->plat_data->config->report_mt) { + input_mt_slot(ts->idev, i); + input_mt_report_slot_state(ts->idev, MT_TOOL_FINGER, 0); + } else { + DBG_PRINT(DBG_BIT_DROP,"dropped MT UP\n"); + } + input_report_key(ts->idev, BTN_TOUCH, 0); + input_report_key(ts->idev, BTN_TOOL_FINGER, 0); + input_sync(ts->idev); + DBG_PRINT(DBG_BIT_ISR,"[%d]: RELEASE\n", i); + } + + finger[i] = pressed; + DBG_PRINT(DBG_BIT_ALL,"finger [%d]: %d\n", i, finger[i]); + } + + return 0; +} + +#define MIN_DIST 2 + +static int pct1812_process_gesture_event(struct pct1812_data *ts) +{ + unsigned char gest, coords[4] = {0}; + bool send_button = false; + int ret; + + ret = pct1812_i2c_read(ts, GEST_TYPE_REG, &gest, sizeof(gest)); + if (ret) + return -EIO; + + DBG_PRINT(DBG_BIT_ISR,"gesture 0x%02x\n", gest); + switch(gest & 0x1f) { + case GEST_HORIZ_SCROLL: + case GEST_VERT_SCROLL: + break; + case GEST_DBL_TAP: + send_button = true; + break; + case GEST_TAP: + case GEST_UP_SWIPE: + case GEST_DWN_SWIPE: + case GEST_LFT_SWIPE: + case GEST_RGHT_SWIPE: + return 0; + default: return -EINVAL; + } + + if (send_button) { + if (ts->plat_data->config && !ts->plat_data->config->report_gesture) { + DBG_PRINT(DBG_BIT_DROP,"dropped DOUBLETAP\n"); + goto drop_exit; + } + /* press */ + input_report_key(ts->idev, BTN_TOOL_DOUBLETAP, 1); + input_report_key(ts->idev, BTN_TOUCH, 1); + input_sync(ts->idev); + /* release */ + input_report_key(ts->idev, BTN_TOOL_DOUBLETAP, 0); + input_report_key(ts->idev, BTN_TOUCH, 0); + input_sync(ts->idev); + } else { + ret = pct1812_read_xy_coords(ts, GEST_ADDR(0), coords); + if (ret) { + dev_err(ts->dev, "%s: error reading gesture\n", __func__); + } else { + int x = comp2_16b(X_GET(coords)); + int y = comp2_16b(Y_GET(coords)); + + if (ts->plat_data->config && !ts->plat_data->config->report_gesture) { + DBG_PRINT(DBG_BIT_DROP,"dropped SCROLL\n"); + goto drop_exit; + } + + /* suppress unsubstantial movement events */ + if ((gest & 0x1f) == GEST_HORIZ_SCROLL) { + if (ts->plat_data->config) { + if (abs(x) >= ts->plat_data->config->scroll_min_distance) { + DBG_PRINT(DBG_BIT_DROP,"dropped HORIZ_SCROLL: x=%d, y=%d\n", x, y); + goto drop_exit; + } + if (ts->plat_data->config->flip_x) + x = -x; + } + input_report_rel(ts->idev, REL_HWHEEL, x); + input_sync(ts->idev); + } else if ((gest & 0x1f) == GEST_VERT_SCROLL) { + if (ts->plat_data->config) { + if (abs(y) >= ts->plat_data->config->scroll_min_distance) { + DBG_PRINT(DBG_BIT_DROP,"dropped VERT_SCROLL: x=%d, y=%d\n", x, y); + goto drop_exit; + } + if (ts->plat_data->config->flip_y) + y = -y; + } + input_report_rel(ts->idev, REL_WHEEL, y); + input_sync(ts->idev); + } else + goto drop_exit; + + DBG_PRINT(DBG_BIT_ISR,"[@%02x]: x=%d, y=%d\n", GEST_ADDR(0), x, y); + } + } +drop_exit: + return ret; +} + +static irqreturn_t pct1812_irq_handler(int irq, void *ptr) +{ + struct pct1812_data *ts = (struct pct1812_data *)ptr; + unsigned char dropped, status = 0; + bool ack = true; + int ret; + + mutex_lock(&ts->eventlock); + ret = pct1812_i2c_read(ts, STATUS_REG, &status, sizeof(status)); + if (ret || ((status & STAT_BIT_ERR) || (status & STAT_BIT_WDOG))) { + pct1812_fifo_cmd_add(ts, CMD_RECOVER, TOUCH_RESET_DELAY); + goto failure; + } + dropped = status; + dropped &= ~ts->drop_mask; + if (status != dropped) + DBG_PRINT(DBG_BIT_ISR,"drop out %02x -> %02x\n", status, dropped); + if (dropped & STAT_BIT_GESTURE) { + pct1812_process_gesture_event(ts); + } + if (dropped & STAT_BIT_TOUCH) { + pct1812_process_touch_event(ts); + } + if (ack) { + status = 0; // ACK interrupt + ret = pct1812_i2c_write(ts, STATUS_REG, &status, sizeof(status)); + } +failure: + mutex_unlock(&ts->eventlock); + + return IRQ_HANDLED; +} + +static int pct1812_user_access(struct pct1812_data *ts, + unsigned char bank, unsigned char addr, + unsigned char *value, unsigned int size, bool R) +{ + unsigned char vArray[16]; + int ret; + + if (R && size > sizeof(vArray)) { + dev_err(ts->dev, "%s: Requested size %d bigger than buffer size %u\n", __func__, size, sizeof(vArray)); + return -EINVAL; + } + + memset(vArray, INVALID_BYTE, sizeof(vArray)); + ret = pct1812_i2c_write(ts, USER_BANK_REG, &bank, sizeof(bank)); + if (ret) + return -EIO; + + ret = pct1812_i2c_write(ts, USER_ADDR_REG, &addr, sizeof(addr)); + if (ret) + return -EIO; + + if (R) { // read access + if (value) + memset(value, 0, size); + ret = pct1812_i2c_read(ts, USER_DATA_REG, vArray, size); + if (!ret && value) + memcpy(value, vArray, size); + } else { + ret = pct1812_i2c_write(ts, USER_DATA_REG, value, size); + } + + if (ts->debug & DBG_BIT_UACC) { + int i; + pr_info("%c: b%d:0x%02x |", R ? 'R' : 'W', bank, addr); + for (i = 0; i < size; i++) + pr_cont(" %02X", value ? value[i] : vArray[i]); + pr_cont("\n"); + } + + return ret; +} + +#define QUERY_PARAM_IF(p, b, r, v) { \ + p = INVALID_BYTE; \ + ret = pct1812_user_access(ts, b, r, &(v), sizeof(v), AREAD); \ + if (!ret) { \ + p = v; \ + } else { \ + dev_err(ts->dev, "%s: Error reading b(%d):0x%02x\n", __func__, b, r); \ + goto failure; \ + } \ +} + +#define QUERY_PARAM(p, b, r, v) { \ + p = INVALID_BYTE; \ + ret = pct1812_user_access(ts, b, r, &(v), sizeof(v), AREAD); \ + if (!ret) { \ + p = v; \ + } else { \ + dev_err(ts->dev, "%s: Error reading b(%d):0x%02x\n", __func__, b, r); \ + } \ +} + +#define SET_PARAM_IF(b, r, v) { \ + ret = pct1812_user_access(ts, b, r, &(v), sizeof(v), AWRITE); \ + if (ret) { \ + dev_err(ts->dev, "%s: Error writing b(%d):0x%02x\n", __func__, b, r); \ + goto failure; \ + } \ +} + +#define GET_REG(r, v) { \ + v = INVALID_BYTE; \ + ret = pct1812_i2c_read(ts, r, &(v), sizeof(v)); \ + if (ret) { \ + dev_err(ts->dev, "%s: Error reading Reg0x%02x\n", __func__, r); \ + } \ +} + +#define SET_REG_IF(r, v) { \ + ret = pct1812_i2c_write(ts, r, &(v), sizeof(v)); \ + if (ret) { \ + dev_err(ts->dev, "%s: Error writing Reg0x%02x val=0x%02x\n", __func__, r, v); \ + goto failure; \ + } \ +} + +#define SET_REG_CHK_IF(r, v) if (1) { \ + unsigned char checkout; \ + ret = pct1812_i2c_write(ts, r, &(v), sizeof(v)); \ + if (ret) { \ + dev_err(ts->dev, "%s: Error writing Reg0x%02x val=0x%02x\n", __func__, r, v); \ + goto failure; \ + } else { \ + ret = pct1812_i2c_read(ts, r, &checkout, sizeof(checkout)); \ + if (ret) { \ + dev_err(ts->dev, "%s: Error reading back Reg0x%02x\n", __func__, r); \ + goto failure; \ + } else if (v != checkout) { \ + dev_warn(ts->dev, "%s: Mismatch %02x != %02x\n", __func__, v, checkout); \ + } else { \ + DBG_PRINT(DBG_BIT_FW,"active BANK(%d)\n", checkout); \ + } \ + } \ +} + +static int inline pct1812_set_bank(struct pct1812_data *ts, + unsigned char bank) { + int ret; + SET_REG_CHK_IF(ACTIVE_BANK_REG, bank); +failure: + return ret; +} + +static int inline pct1812_sensing_on(struct pct1812_data *ts) { + unsigned char curmode, value; + int ret; + QUERY_PARAM_IF(curmode, BANK(0), START_REG, value); + curmode |= 0x01; + SET_PARAM_IF(BANK(0), START_REG, curmode); + DBG_PRINT(DBG_BIT_ALL,"start reg b0:0x02x value %02x\n", START_REG, curmode); +failure: + return ret; +} + +static int pct1812_get_extinfo(struct pct1812_data *ts) +{ + unsigned char bs, status, val, v1, v2; + int i, ret; + + pct1812_set_bank(ts, BANK(6)); + GET_REG(BOOT_STATUS_REG, bs); + GET_REG(STATUS_REG, status); + /* read boot info first */ + QUERY_PARAM(v1, BANK(0), VER_LOW_REG, val); + ts->ver7e = (VER_LOW_REG << 8) | v1; + QUERY_PARAM(v2, BANK(0), VER_HIGH_REG, val); + ts->ver7f = (VER_HIGH_REG << 8) | v2; + QUERY_PARAM(ts->boot_block, BANK(0), FLASH_BANK_REG, val); + QUERY_PARAM(v1, BANK(0), DEBUG_REG, val); + + dev_info(ts->dev, "%s: ver=%04x/%04x, boot_blk=%d, dbg=%02x, boot_status=%02x, status=%02x\n", + __func__, ts->ver7e, ts->ver7f, ts->boot_block, v1, bs, status); + + QUERY_PARAM(ts->tx_count, BANK(0), TX_REG, val); + QUERY_PARAM(ts->rx_count, BANK(0), RX_REG, val); + QUERY_PARAM(ts->model, BANK(0), MODEL_REG, val); + QUERY_PARAM(v1, BANK(0), FW_MAJOR_REG, val); + ts->major = v1 >> 4; + QUERY_PARAM(v2, BANK(0), FW_VMIN_L_REG, val); + // 4bits in high byte of MINOR version come from MAJOR register + ts->minor = v2 | ((v1 & 0x0f) << 8); + QUERY_PARAM(ts->revision, BANK(0), FW_REV_REG, val); + QUERY_PARAM(ts->patch, BANK(0), FW_PATCH_REG, val); + QUERY_PARAM(v1, BANK(2), XRES_L_REG, val); + QUERY_PARAM(v2, BANK(2), XRES_H_REG, val); + ts->x_res = v1 | (v2 << 8); + QUERY_PARAM(v1, BANK(2), YRES_L_REG, val); + QUERY_PARAM(v2, BANK(2), YRES_H_REG, val); + ts->y_res = v1 | (v2 << 8); + QUERY_PARAM(v1, BANK(2), MAX_POINTS_REG, val); + ts->max_points = v1 & 0x0f; + for (i = 0; i < CUST_INFO_RNUM; i++) { + QUERY_PARAM(ts->custom_block[i], BANK(3), CUST_INFO_BASE + i, val); + } + + return ret; +} + +static int pct1812_power_mode(struct pct1812_data *ts, enum pwr_modes mode) +{ + unsigned char val, cur_mode; + int ret = 0; + + switch (mode) { + case PWR_MODE_NO_DEEP_SLEEP: + val = INVALID_BYTE; + pct1812_i2c_read(ts, DEEP_SLEEP_REG, &val, sizeof(val)); + DBG_PRINT(DBG_BIT_PM,"%s: SLEEP_STATUS before %02x\n", __func__, val); + val = 0x01; // NO_DEEP_SLEEP + ret = pct1812_i2c_write(ts, DEEP_SLEEP_REG, &val, sizeof(val)); + if (ret) { + dev_err(ts->dev, "%s: error setting NO_DEEP_SLEEP\n", __func__); + goto failure; + } +do_again: + pct1812_delay_ms(10); + ret = pct1812_i2c_read(ts, DEEP_SLEEP_REG, &val, sizeof(val)); + if (ret) + goto failure; + if ((val & SLEEP_BIT_STAT)) + goto do_again; + DBG_PRINT(DBG_BIT_PM,"%s: SLEEP_STATUS %02x\n", __func__, val); + break; + case PWR_MODE_AUTO: + case PWR_MODE_RUN: + case PWR_MODE_LPM: // REST1 + case PWR_MODE_DEEP_SLEEP: // REST2 + case PWR_MODE_SUSPEND: + QUERY_PARAM(cur_mode, BANK(3), OP_MODE_REG, val); + if (cur_mode != mode) { + val = mode; + SET_PARAM_IF(BANK(3), FORCE_OP_MODE_REG, val); + DBG_PRINT(DBG_BIT_PM,"%s: PM mode %02x\n", __func__, val); + } + break; + default: + dev_err(ts->dev, "%s: invalid power mode %d\n", __func__, mode); + ret = -EINVAL; + } +failure: + return ret; +} +// vok - value compared bit wise +static int pct1812_wait4ready(struct pct1812_data *ts, unsigned char reg, unsigned char vok) +{ + unsigned char status = 0; + int rlimit = CNT_WAIT_RETRY; + int dwait = CMD_WAIT_DELAY; + int retry, ret; + + // MODE_COMPLETE takes much longer + if (vok == MODE_COMPLETE) { + rlimit = 250; + dwait = 20; + } + + for (retry = 0; retry < rlimit; retry++) { + ret = pct1812_i2c_read(ts, reg, &status, sizeof(status)); + if (ret || ((status & vok) == vok)) + break; + pct1812_delay_ms(dwait); + } + if (retry == rlimit) + ret = -ETIME; + else if (!ret) { + DBG_PRINT(DBG_BIT_TIME,"success waiting for %d\n", vok); + } + + return (status & vok) ? 0 : ret; +} +/* This function assumes active page is set to BANK(1) already */ +static int inline pct1812_engmode(struct pct1812_data *ts, bool on, + unsigned char *vok) +{ + unsigned char curmode, value; + int ret; + + value = RESET_CODE; + ret = pct1812_i2c_write(ts, KEY1_REG, &value, sizeof(value)); + if (ret) + goto failure; + value = on ? ENGINEERING_CODE : RESUME_CODE; + ret = pct1812_i2c_write(ts, KEY2_REG, &value, sizeof(value)); + if (ret) + goto failure; + + pct1812_delay_ms(5); + + /* verify effective mode */ + curmode = 0; + ret = pct1812_i2c_read(ts, ENG_MODE_REG, &curmode, sizeof(curmode)); + if (ret) + goto failure; + pr_info("engineering mode %02x\n", curmode); + + return vok && *vok != curmode ? -EINVAL : 0; +failure: + return -EIO; +} +/* This function assumes active page is set to BANK(4) already */ +static int pct1812_flash_exec(struct pct1812_data *ts, + unsigned char cmd, unsigned char flash_cmd, int cnt) +{ + unsigned char command, lval; + int repetition = 0; + int ret, step = 0; + + DBG_PRINT(DBG_BIT_FW,"cmd 0x%02x, flash_cmd 0x%02x, cnt %d\n", cmd, flash_cmd, cnt); +#ifndef DRY_RUN_UPDATE + // clean status + lval = 0; + ret = pct1812_i2c_write(ts, FLASH_COMMAND_REG, &lval, sizeof(lval)); + if (ret) + goto failure; + step++; + // write flash cmd + lval = flash_cmd; + ret = pct1812_i2c_write(ts, FLASH_FLASH_CMD_REG, &lval, sizeof(lval)); + if (ret) + goto failure; + step++; + // write data count LSB + lval = cnt & 0xff; + ret = pct1812_i2c_write(ts, FLASH_DCOUNT0_REG, &lval, sizeof(lval)); + if (ret) + goto failure; + step++; + // write data count MSB + lval = (cnt >> 8) & 0xff; + ret = pct1812_i2c_write(ts, FLASH_DCOUNT1_REG, &lval, sizeof(lval)); + if (ret) + goto failure; + step++; + // write cmd + lval = cmd; + ret = pct1812_i2c_write(ts, FLASH_COMMAND_REG, &lval, sizeof(lval)); + if (ret) + goto failure; + step++; + // wait for completion +do_again: + command = 0; + ret = pct1812_i2c_read(ts, FLASH_COMMAND_REG, &command, sizeof(command)); + if (ret) + goto failure; + + if ((command & cmd) != 0) { + repetition++; + if (repetition%11) + DBG_PRINT(DBG_BIT_FW,"Still waiting ... %d\n", repetition); + goto do_again; + } +#else + command = 0, lval = 0, ret = 0, step = 0, repetition = 0; +#endif + return 0; +#ifndef DRY_RUN_UPDATE +failure: + dev_err(ts->dev, + "Error exec cmd 0x%02x, flash_cmd 0x%02x, cnt %d (stage %d, reps %d)\n", + __func__, cmd, flash_cmd, cnt, step, repetition); + + return -EIO; +#endif +} +/* This function assumes active page is set to BANK(4) already */ +static int pct1812_flash_status(struct pct1812_data *ts, int bit_idx, int vok) +{ + unsigned char status; + int ret; +#ifndef DRY_RUN_UPDATE +do_again: +#endif + ret = pct1812_flash_exec(ts, 0x08, 0x05, 1); + if (ret) { + dev_err(ts->dev, "Error flash cmd\n", __func__); + return -EIO; + } +#ifndef DRY_RUN_UPDATE + ret = pct1812_i2c_read(ts, FLASH_STATUS_REG, &status, sizeof(status)); + if (ret) { + dev_err(ts->dev, "Error reading flash status\n", __func__); + return -EIO; + } + + if ((status & (1 << bit_idx)) != vok) { + pct1812_delay_ms(2); // adjust delay if necessary + goto do_again; + } +#else + ret = 0, status = 0; +#endif + DBG_PRINT(DBG_BIT_FW,"Flash status (bit(%d)=%d) OK\n", bit_idx, vok); + + return 0; +} +/* This function assumes active page is set to BANK(4) already */ +static int pct1812_flash_chunk(struct pct1812_data *ts, unsigned int address) +{ + int ret, step = 0; + unsigned char value; + + // Flash WriteEnable + ret = pct1812_flash_exec(ts, 0x02, 0x06, 0); + if (ret) + goto failure; + step++; + // check status + ret = pct1812_flash_status(ts, 1, 2); + if (ret) + goto failure; + step++; +#ifndef DRY_RUN_UPDATE + value = (unsigned char)(address & 0xff); + ret = pct1812_i2c_write(ts, FLASH_ADDR0_REG, &value, sizeof(value)); + if (ret) + goto failure; + step++; + + value = (unsigned char)((address >> 8) & 0xff); + ret = pct1812_i2c_write(ts, FLASH_ADDR1_REG, &value, sizeof(value)); + if (ret) + goto failure; + step++; + + value = (unsigned char)((address >> 16) & 0xff); + ret = pct1812_i2c_write(ts, FLASH_ADDR2_REG, &value, sizeof(value)); + if (ret) + goto failure; + step++; + + // set SRAM access offset + value = 0; + ret = pct1812_i2c_write(ts, FLASH_SRAM_OFF0_REG, &value, sizeof(value)); + if (ret) + goto failure; + ret = !ret && pct1812_i2c_write(ts, FLASH_SRAM_OFF1_REG, &value, sizeof(value)); + if (ret) { + dev_err(ts->dev, "%s: Error setting SRAM offset\n", __func__); + goto failure; + } + step++; +#else + value = 0; +#endif + ret = pct1812_flash_exec(ts, 0x81, 0x02, CHUNK_SZ); + if (ret) + goto failure; + step++; + // wait for completion + ret = pct1812_flash_status(ts, 0, 0); + if (ret) + goto failure; + + return 0; + +failure: + dev_err(ts->dev, "Error erasing address 0x%06x (stage %d)\n", + __func__, address, step); + return -EIO; +} + +static int pct1812_erase_sector(struct pct1812_data *ts, unsigned int address) +{ + int ret, step = 0; + unsigned char value; + + DBG_PRINT(DBG_BIT_FW,"Erasing sector @0x%06x\n", address); + + // Flash WriteEnable + ret = pct1812_flash_exec(ts, 0x02, 0x06, 0); + if (ret) + goto failure; + step++; + // check status + ret = pct1812_flash_status(ts, 1, 2); + if (ret) + goto failure; + step++; +#ifndef DRY_RUN_UPDATE + value = (unsigned char)(address & 0xff); + ret = pct1812_i2c_write(ts, FLASH_ADDR0_REG, &value, sizeof(value)); + if (ret) + goto failure; + step++; + + value = (unsigned char)((address >> 8) & 0xff); + ret = pct1812_i2c_write(ts, FLASH_ADDR1_REG, &value, sizeof(value)); + if (ret) + goto failure; + step++; + + value = (unsigned char)((address >> 16) & 0xff); + ret = pct1812_i2c_write(ts, FLASH_ADDR2_REG, &value, sizeof(value)); + if (ret) + goto failure; + step++; +#else + value = 0; +#endif + ret = pct1812_flash_exec(ts, 0x02, 0x20, 3); + if (ret) + goto failure; + step++; + // wait for completion + ret = pct1812_flash_status(ts, 0, 0); + if (ret) + goto failure; + + return 0; + +failure: + dev_err(ts->dev, "Error erasing address 0x%06x (stage %d)\n", + __func__, address, step); + return -EIO; +} + +static int pct1812_flash_section(struct pct1812_data *ts, unsigned char *data, + unsigned int size, unsigned int address) +{ + int m = 0, ret; + unsigned int num_of_sectors = size / PCT1812_SECTOR_SIZE; + unsigned int target_num = size / CHUNK_SZ; + unsigned char value; + unsigned char *ptr = data; + + ret = pct1812_set_bank(ts, BANK(4)); + if (ret) + goto failure; + + DBG_PRINT(DBG_BIT_FW,"Start addr = %06x, size = %04x\n", address, size); + DBG_PRINT(DBG_BIT_FW,"4K sectors = %u, 256b chunks = %u\n", num_of_sectors, target_num); + for (m = 0; m < num_of_sectors; m++) { + ret = pct1812_erase_sector(ts, address + PCT1812_SECTOR_SIZE * m); + if (ret) + goto failure; + DBG_PRINT(DBG_BIT_FW,"Erased sector %d of %d\n", m + 1, num_of_sectors); + } + + for (m = 0; m < target_num; m++) { + ret = pct1812_set_bank(ts, BANK(2)); + if (ret) + goto failure; +#ifndef DRY_RUN_UPDATE + // Set SRAM select + value = 0x08; + ret = pct1812_i2c_write(ts, SRAM_SELECT_REG, &value, sizeof(value)); + if (ret) { + dev_err(ts->dev, "%s: error setting SRAM_SELECT\n", __func__); + goto failure; + } + // Set SRAM NSC to 0 + value = 0x00; + ret = pct1812_i2c_write(ts, SRAM_NSC_REG, &value, sizeof(value)); + if (ret) { + dev_err(ts->dev, "%s: error setting SRAM_NSC\n", __func__); + goto failure; + } + // Write data to SRAM port + ret = pct1812_i2c_write(ts, SRAM_PORT_REG, ptr, CHUNK_SZ); + if (ret) { + dev_err(ts->dev, "%s: error writing to SRAM\n", __func__); + goto failure; + } + // Set SRAM NSC to 1 + value = 0x01; + ret = pct1812_i2c_write(ts, SRAM_NSC_REG, &value, sizeof(value)); + if (ret) { + dev_err(ts->dev, "%s: error setting SRAM_NSC\n", __func__); + goto failure; + } +#else + value = 0; +#endif + // advance data pointer + ptr += CHUNK_SZ; + DBG_PRINT(DBG_BIT_FW,"Flashing chunk @0x%06x\n", address); + + ret = pct1812_set_bank(ts, BANK(4)); + if (ret) + goto failure; + + // Program Flash from SRAM + ret = pct1812_flash_chunk(ts, address); + if (ret) + goto failure; + address += CHUNK_SZ; + } + + return 0; +failure: + dev_err(ts->dev, "Error flashing chunk %d\n", __func__, m); + return -EIO; +} + +/*static*/ +int pct1812_fw_update(struct pct1812_data *ts, char *fname) +{ + int ret; + unsigned char value; + unsigned char *fwdata_ptr, *fwparam_ptr, *fwcust_ptr; + unsigned int fwdata_size, fwparam_size, fwcust_size; + const struct firmware *fw_entry = NULL; + + //__pm_stay_awake(&ts->wake_src); + mutex_lock(&ts->cmdlock); + dev_info(ts->dev, "%s: Start FW update from file %s\n", __func__, fname); + //fwu_irq_enable(fwu, true); + ret = request_firmware(&fw_entry, fname, ts->dev); + if (ret) { + dev_err(ts->dev, "%s: Error loading firmware %s\n", __func__, fname); + ret = -EINVAL; + goto failure; + } + // FW file size check + if (fw_entry->size != SUPPORTED_FW_FILE_SIZE) { + dev_err(ts->dev, "%s: Firmware %s file size is WRONG!!!\n", __func__, fname); + ret = -EINVAL; + goto failure; + } + + /* 0000 ---- + * FW BLK (size 0xd000) + * d000 ---- + * CUSTOM BLK (size 0x1000) + * e000 ---- + * PARAM BLK (size 0x1000) + * efff ---- + */ + fwdata_size = FW_SECTION_SIZE; + fwdata_ptr = (unsigned char *)fw_entry->data; + fwcust_size = CUSTOM_SECTION_SIZE; + fwcust_ptr = fwdata_ptr + FW_SECTION_SIZE; + fwparam_size = PARAM_SECTION_SIZE; + fwparam_ptr = fwdata_ptr + SUPPORTED_FW_FILE_SIZE - PARAM_SECTION_SIZE; + + pct1812_set_bank(ts, BANK(6)); + ret = pct1812_power_mode(ts, PWR_MODE_NO_DEEP_SLEEP); + if (ret) { + dev_err(ts->dev, "%s: error setting no deep sleep power mode\n", __func__); + } + + pct1812_set_bank(ts, BANK(1)); + /* check value */ + value = 0x01; + ret = pct1812_engmode(ts, true, &value); + if (ret) { + dev_err(ts->dev, "%s: Error entering flash mode\n", __func__); + goto failure; + } + + value = POWERUP_CODE; + ret = pct1812_i2c_write(ts, FLASH_PUP_REG, &value, sizeof(value)); + if (ret) { + dev_err(ts->dev, "%s: error setting FLASH_PWRUP\n", __func__); + goto failure; + } + + ret = pct1812_flash_section(ts, fwdata_ptr, fwdata_size, ADDRESS_FW); + if (ret) + dev_err(ts->dev, "%s: Flash FW error!!!\n", __func__); + ret = !ret && pct1812_flash_section(ts, fwcust_ptr, fwcust_size, ADDRESS_CUSTOM); + if (ret) + dev_err(ts->dev, "%s: Flash customer error!!!\n", __func__); + ret = !ret && pct1812_flash_section(ts, fwparam_ptr, fwparam_size, ADDRESS_PARAM); + if (ret) + dev_err(ts->dev, "%s: Flash params error!!!\n", __func__); + + if (!ret) + dev_info(ts->dev, "%s: FW update completed successfully\n", __func__); +failure: + /* reset active page explicitly */ + pct1812_set_bank(ts, BANK(1)); + ret = pct1812_engmode(ts, false, NULL); + if (ret) { + dev_err(ts->dev, "%s: Error leaving flash mode\n", __func__); + } + + pct1812_set_bank(ts, BANK(6)); + ret = pct1812_wait4ready(ts, BOOT_STATUS_REG, BOOT_COMPLETE); + if (ret) { + dev_err(ts->dev, "%s: Failed to init\n", __func__); + } + mutex_unlock(&ts->cmdlock); + //__pm_relax(&ts->wake_src); + + pct1812_fifo_cmd_add(ts, CMD_INFO, TOUCH_RESET_DELAY); + /* delete runtime patch, since it might not apply over new firmware */ + if (ts->plat_data->config && ts->plat_data->config->reg_num && ts->plat_data->config->patch) { + devm_kfree(ts->dev, ts->plat_data->config->patch); + ts->plat_data->config->patch = NULL; + ts->plat_data->config->reg_num = 0; + dev_info(ts->dev, "%s: Run time config dropped\n", __func__); + } + + return ret; +} + +static int pct1812_regulator(struct pct1812_data *ts, bool get) +{ + struct pct1812_platform *pdata = ts->plat_data; + struct regulator *rvdd = NULL; + struct regulator *rvio = NULL; + + if (get) { + rvdd = regulator_get(ts->dev, pdata->vdd); + if (IS_ERR_OR_NULL(rvdd)) { + dev_err(ts->dev, "%s: Failed to get %s regulator\n", + __func__, pdata->vdd); + rvdd = NULL; + } + if (pdata->vio) + rvio = regulator_get(ts->dev, pdata->vio); + if (IS_ERR_OR_NULL(rvio)) { + dev_err(ts->dev, "%s: Failed to get %s regulator.\n", + __func__, pdata->vio); + rvio = NULL; + } + } else { + regulator_put(ts->reg_vdd); + if (pdata->vio) + regulator_put(ts->reg_vio); + } + + ts->reg_vdd = rvdd; + ts->reg_vio = rvio; + + DBG_PRINT(DBG_BIT_PM,"vdd=%p, vio=%p\n", rvdd, rvio); + + return 0; +} + +static int pct1812_pinctrl_state(struct pct1812_data *ts, bool on) +{ + struct pinctrl_state *state_ptr; + const char *state_name; + int error = 0; + + if (!ts->ts_pinctrl) + return 0; + + if (on) { + state_name = PINCTRL_STATE_ACTIVE; + state_ptr = ts->pinctrl_state_active; + } else { + state_name = PINCTRL_STATE_SUSPEND; + state_ptr = ts->pinctrl_state_suspend; + } + + error = pinctrl_select_state(ts->ts_pinctrl, state_ptr); + if (error < 0) + dev_err(ts->dev, "%s: Failed to select %s\n",__func__, state_name); + else + DBG_PRINT(DBG_BIT_PM,"set pinctrl state %s\n", state_name); + + return error; +} + +static int pct1812_pinctrl_init(struct pct1812_data *info) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + info->ts_pinctrl = devm_pinctrl_get(info->dev); + if (IS_ERR_OR_NULL(info->ts_pinctrl)) { + retval = PTR_ERR(info->ts_pinctrl); + dev_err(info->dev, "%s Target not using pinctrl %d\n", + __func__, retval); + goto err_pinctrl_get; + } + + info->pinctrl_state_active = pinctrl_lookup_state( + info->ts_pinctrl, PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(info->pinctrl_state_active)) { + retval = PTR_ERR(info->pinctrl_state_active); + dev_err(info->dev, "%s Can not lookup %s pinstate %d\n", + __func__, PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + info->pinctrl_state_suspend = pinctrl_lookup_state( + info->ts_pinctrl, PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(info->pinctrl_state_suspend)) { + retval = PTR_ERR(info->pinctrl_state_suspend); + dev_err(info->dev, "%s Can not lookup %s pinstate %d\n", + __func__, PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(info->ts_pinctrl); +err_pinctrl_get: + info->ts_pinctrl = NULL; + return retval; +} + +static int pct1812_power(struct pct1812_data *ts, bool on) +{ + struct pct1812_platform *pdata = ts->plat_data; + + if (on) { + if (pdata->vio) + regulator_enable(ts->reg_vio); + regulator_enable(ts->reg_vdd); + pct1812_delay_ms(PON_DELAY); + } else { + regulator_disable(ts->reg_vdd); + if (pdata->vio) + regulator_disable(ts->reg_vio); + } + + dev_info(ts->dev,"%s: power %s: %s:%s\n", __func__, on ? "ON" : "OFF", + pdata->vdd, regulator_is_enabled(ts->reg_vdd) ? "on" : "off"); + if (pdata->vio) + dev_info(ts->dev,"%s: %s: %s:%s\n", __func__, on ? "on" : "off", + pdata->vio, + regulator_is_enabled(ts->reg_vio) ? "on" : "off"); + + return 0; +} + +static int pct1812_run_cmd(struct pct1812_data *ts, enum cmds command, unsigned char vok) +{ + unsigned char code; + const char *action; + int ret, counter = 0; + + switch (command) { + case CMD_RESET: + action = "RESET"; + code = RESET_CODE; + break; + case CMD_RESUME: + action = "RESUME"; + code = RESUME_CODE; + break; + case CMD_SUSPEND: + action = "SUSPEND"; + code = SUSPEND_CODE; + break; + case CMD_MODE_RESET: + ret = pct1812_set_bank(ts, BANK(6)); + if (ret) { + dev_err(ts->dev, "%s: Bank(6) set error\n", __func__); + return ret; + } + pct1812_delay_ms(1); + action = "MODE_RESET"; + code = RESET_CODE; + break; + default: return -EINVAL; + } + +run_once_again: + DBG_PRINT(DBG_BIT_PM,"command: %s\n", action); + ret = pct1812_i2c_write(ts, SW_RESET_REG, &code, sizeof(code)); + if (ret) { + dev_err(ts->dev, "%s: Error sending cmd: %d (%d)\n", __func__, command, ret); + return ret; + } + + if (!counter++) + pct1812_delay_ms(10); + + if (code == RESET_CODE) { + action = "RESUME"; + code = RESUME_CODE; + goto run_once_again; + } + + return vok ? pct1812_wait4ready(ts, BOOT_STATUS_REG, vok) : 0; +} + +static int pct1812_queued_resume(struct pct1812_data *ts) +{ + int ret; + + DBG_PRINT(DBG_BIT_PM,"enter\n"); + + if (atomic_cmpxchg(&ts->touch_stopped, 1, 0) == 0) + return 0; + + ret = pct1812_run_cmd(ts, CMD_RESUME, BOOT_COMPLETE); + if (ret) { // set active flag and irq + } + + return ret; +} + +static int pct1812_report_mode(struct pct1812_data *ts, + unsigned char set_mask, unsigned char clear_mask, + unsigned char *stored_mask) +{ + unsigned char MASK, val; + int i, ret; + + QUERY_PARAM_IF(MASK, BANK(0), INTR_MASK_REG, val); + if (stored_mask) + *stored_mask = MASK; + DBG_PRINT(DBG_BIT_ALL,"Current mask 0x%02x\n", MASK); + for (i = 0; i < 8; i++) { + if (clear_mask & (1 << i)) { + MASK &= ~(1 << i); // clear bit + DBG_PRINT(DBG_BIT_ALL,"mask w/bit cleared %02x\n", MASK); + } + if (set_mask & (1 << i)) { + MASK |= (1 << i); + DBG_PRINT(DBG_BIT_ALL,"mask w/bit set %02x\n", MASK); + } + } + SET_PARAM_IF(BANK(0), INTR_MASK_REG, MASK); + DBG_PRINT(DBG_BIT_ALL,"New interrupt mask 0x%02x\n", MASK); +failure: + return 0; +} + +// overall size of test data followed by test type +// Full Panel & SNR have Tx/Rx followed by nodes data +// Draw Line contains only data +#define SELFTEST_HDR_SZ (sizeof(unsigned int) + sizeof(unsigned char)) + +static int pct1812_selftest_memory(struct pct1812_data *ts, bool allocate) +{ + int ff, ret = 0; + + if (!allocate) { + ff = ts->frame_count - 1; + goto dealloc; + } + + ts->test_data = kzalloc(sizeof(void *) * ts->frame_count, GFP_KERNEL); + if (!ts->test_data) + return -ENOMEM; + + for (ff = 0; ff < ts->frame_count; ff++) { + ts->test_data[ff] = kzalloc(ts->frame_size, GFP_KERNEL); + if (!ts->test_data[ff]) { + ret = -ENOMEM; + goto dealloc; + } + } + DBG_PRINT(DBG_BIT_TEST,"allocated %d frames %d bytes each\n", ts->frame_count, ts->frame_size); + + return 0; + +dealloc: + for (; ff >= 0; ff--) + kfree(ts->test_data[ff]); + kfree(ts->test_data); + ts->test_type = PCT1812_SELFTEST_NONE; + ts->frame_count = ts->frame_size = ts->test_data_size = ts->frame_sample = 0; + DBG_PRINT(DBG_BIT_TEST,"free-ed selftest memory\n"); + + return ret; +} + +static int pct1812_selftest_cleanup(struct pct1812_data *ts) +{ + int ret = 0; + + switch (ts->test_type) { + case PCT1812_SELFTEST_DRAW_LINE: + ret = pct1812_run_cmd(ts, CMD_RESET, BOOT_COMPLETE); + if (ret) { + dev_err(ts->dev, "%s: Reset failed\n", __func__); + } + // no need to restore intr mask; reset done it already!!! + //pct1812_report_mode(ts, ts->test_intr_mask, 0xff, NULL); + pct1812_drop_mask_set(ts, 0); // STAT_BIT_TOUCH + default: + pct1812_selftest_set(ts, PCT1812_SELFTEST_NONE); + pct1812_selftest_memory(ts, false); + } + + ret = pct1812_power_mode(ts, PWR_MODE_AUTO); + if (ret) { + dev_err(ts->dev, "%s: error setting auto power mode\n", __func__); + } + + return ret; +} + +static int pct1812_config_apply(struct pct1812_data *ts) +{ + int r, ret = 0; + + if (!ts->plat_data->config || !ts->plat_data->config->patch) + return 0; + + for (r = 0; r < ts->plat_data->config->reg_num; r++) { + struct bank_reg *brd = &ts->plat_data->config->patch[r]; + + SET_PARAM_IF(BANK(brd->bank), brd->reg, brd->data); + } +failure: + return ret; +} + +#define NANO_SEC 1000000000 +#define SEC_TO_MSEC 1000 +#define NANO_TO_MSEC 1000000 + +static inline unsigned int timediff_ms(struct pct1812_data *ts) +{ + struct timespec64 start = ktime_to_timespec64(ts->test_start_time); + struct timespec64 end = ktime_to_timespec64(ktime_get()); + struct timespec64 temp; + unsigned int diff_ms; + + if ((end.tv_nsec - start.tv_nsec) < 0) { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = NANO_SEC + end.tv_nsec - start.tv_nsec; + } else { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + diff_ms = (unsigned int)((temp.tv_sec * SEC_TO_MSEC) + (temp.tv_nsec / NANO_TO_MSEC)); + DBG_PRINT(DBG_BIT_TIME,"timediff %ums\n", diff_ms); + + return diff_ms; +} + +static void pct1812_work(struct work_struct *work) +{ + struct pct1812_data *ts = container_of(work, struct pct1812_data, worker.work); + unsigned char status = 0; + static unsigned char prev = 1; + const char *action = NULL; + int mode, cmd, ret = 0; + int rearm_cmd = CMD_WDOG; + unsigned int duration; + unsigned int interval = WDOG_INTERVAL; + + while (kfifo_get(&ts->cmd_pipe, &cmd)) { + mode = pct1812_selftest_get(ts); + //DBG_PRINT(DBG_BIT_ALL,"cmd = %d, mode = %d\n", cmd, mode); + switch (cmd) { + case CMD_CLEANUP: + duration = timediff_ms(ts); + if (mode == PCT1812_SELFTEST_IN_PROGRESS || + duration < SELFTEST_EXTRA_LONG_INTERVAL) { + rearm_cmd = CMD_CLEANUP; + action = "CLEANUP_POSTPONED"; + } else if (mode != PCT1812_SELFTEST_NONE) { + action = "SELFTEST_CLEANUP"; + pct1812_selftest_cleanup(ts); + } + break; + case CMD_WDOG: + if (mode == PCT1812_SELFTEST_NONE) { + action = "WDOG"; +#ifndef CALM_WDOG + ret = pct1812_i2c_read(ts, FRAME_NUM_REG, &status, sizeof(status)); +#endif + if (ret || (prev == status)) { + dev_warn(ts->dev, "%s: Possible lockup\n", __func__); + } +#ifndef CALM_WDOG + /* update frame counter */ + prev = status; +#endif + } else { + action = "WDOG_POSTPONED"; + } + break; + case CMD_RECOVER: + action = "RECOVER"; + ret = pct1812_run_cmd(ts, CMD_RESET, BOOT_COMPLETE); + if (ret) { + dev_err(ts->dev, "%s: Reset failed\n", __func__); + } else if (ts->plat_data->config) { + rearm_cmd = CMD_CONFIG; + interval = TOUCH_RESET_DELAY; + } + break; + case CMD_CONFIG: + action = "CONFIG"; + ret = pct1812_config_apply(ts); + if (ret) { + dev_err(ts->dev, "%s: Failed to apply config\n", __func__); + } else { + rearm_cmd = CMD_INFO; + interval = TOUCH_RESET_DELAY; + } + break; + case CMD_INFO: + action = "INFO"; + ret = pct1812_get_extinfo(ts); + if (ret) + dev_err(ts->dev, "%s: Failed to get extinfo\n", __func__); + break; + case CMD_RESUME: + action = "RESUME"; + ret = pct1812_queued_resume(ts); + if (ret) + dev_err(ts->dev, "%s: Failed to resume\n", __func__); + break; + } + } + + if (action && strcmp(action, "WDOG")) + DBG_PRINT(DBG_BIT_WORK,"action: %s\n", action); + + pct1812_fifo_cmd_add(ts, rearm_cmd, interval); +} +#if 0 +static int pct1812_suspend(struct device *dev) +{ + struct pct1812_data *ts = dev_get_drvdata(dev); + int ret; + + DBG_PRINT(DBG_BIT_PM,"enter\n"); + + if (atomic_cmpxchg(&ts->touch_stopped, 0, 1) == 1) + return 0; + + ret = pct1812_run_cmd(ts, CMD_SUSPEND, 0); + if (!ret) { // set active flag and irq + } + + return ret; +} + +static int pct1812_resume(struct device *dev) +{ + struct pct1812_data *ts = dev_get_drvdata(dev); + + //atomic_set(&ts->resume_should_stop, 0); + kfifo_put(&ts->cmd_pipe, CMD_RESUME); + /* schedule_delayed_work returns true if work has been scheduled */ + /* and false otherwise, thus return 0 on success to comply POSIX */ + return schedule_delayed_work(&ts->worker, 0) == false; +} +#endif + +static int pct1812_input_dev(struct pct1812_data *ts, bool alloc) +{ + static char ts_phys[64] = {0}; + unsigned int xRes = DFLT_X_RES; + unsigned int yRes = DFLT_Y_RES; + struct pct1812_custom_cfg *config = ts->plat_data->config; + int ret = 0; + + if (!alloc) + goto cleanup; + + ts->idev = input_allocate_device(); + if (!ts->idev) + return -ENOMEM; + + snprintf(ts_phys, sizeof(ts_phys), "pct1812_touchpad/input0"); + ts->idev->phys = ts_phys; + ts->idev->name = "PixArt PCT1812FF Touchpad"; + ts->idev->id.bustype = BUS_I2C; + ts->idev->dev.parent = ts->dev; + + set_bit(EV_SYN, ts->idev->evbit); + set_bit(EV_KEY, ts->idev->evbit); + set_bit(EV_ABS, ts->idev->evbit); + set_bit(BTN_TOUCH, ts->idev->keybit); + set_bit(BTN_TOOL_FINGER, ts->idev->keybit); + set_bit(INPUT_PROP_DIRECT, ts->idev->propbit); + + if (config) { + if (config->max_x) + xRes = config->max_x -1; + if (config->max_y) + yRes = config->max_y -1; + if (config->ty_offset) { + yRes += 2 * config->ty_offset; + } + } + + dev_info(ts->dev, "%s: Input dev resolution: %ux%u", __func__, xRes, yRes); + + /* x_res/y_res is not available, since INFO work has not run yet */ + input_set_abs_params(ts->idev, ABS_X, 0, xRes, 0, 0); + input_set_abs_params(ts->idev, ABS_Y, 0, yRes, 0, 0); + + if (ts->plat_data->config && ts->plat_data->config->report_mt) { + set_bit(MT_TOOL_FINGER, ts->idev->keybit); + set_bit(INPUT_PROP_DIRECT, ts->idev->propbit); + input_set_abs_params(ts->idev, ABS_MT_POSITION_X, 0, xRes, 0, 0); + input_set_abs_params(ts->idev, ABS_MT_POSITION_Y, 0, yRes, 0, 0); + input_mt_init_slots(ts->idev, ts->max_points, INPUT_MT_DIRECT); + } + + if (ts->plat_data->config && ts->plat_data->config->report_gesture) { + set_bit(EV_REL, ts->idev->evbit); + set_bit(REL_WHEEL, ts->idev->relbit); + set_bit(REL_HWHEEL, ts->idev->relbit); + set_bit(BTN_TOOL_DOUBLETAP, ts->idev->keybit); + } + + ret = input_register_device(ts->idev); + if (ret) { + dev_err(ts->dev, "%s: failed to register input device: %d\n", __func__, ret); + input_free_device(ts->idev); + } + + return ret; + +cleanup: + input_unregister_device(ts->idev); + + return ret; +} + +static int pct1812_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct pct1812_data *ts; + struct pct1812_platform *pdata; + int ret = 0; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "%s: EIO err!!!\n", __func__); + return -EIO; + } + + ts = kzalloc(sizeof(struct pct1812_data), GFP_KERNEL); + if (!ts) + goto error_allocate_mem; + + if (!client->dev.of_node) { + dev_err(&client->dev, "%s: Failed to locate dt\n", __func__); + goto error_allocate_mem; + } + + pdata = devm_kzalloc(&client->dev, sizeof(struct pct1812_platform), GFP_KERNEL); + if (!pdata) { + dev_err(&client->dev, "%s: Failed to allocate platform data\n", __func__); + goto error_allocate_pdata; + } + + i2c_set_clientdata(client, ts); + client->dev.platform_data = pdata; + pdata->client = client; + ts->client = client; + ts->dev = &client->dev; + ts->plat_data = pdata; + ts->debug = DBG_BIT_PM | DBG_BIT_DTS | DBG_BIT_ISR | DBG_BIT_DROP; // DBG_BIT_I2C | DBG_BIT_UACC | + +#if defined(STATIC_PLATFORM_DATA) + memcpy(pdata, (const void *)&pct1812_pd, sizeof(struct pct1812_platform)); +#else + ret = pct1812_parse_dt(client); + if (ret) { + dev_err(&client->dev, "%s: Failed to parse dt\n", __func__); + goto error_allocate_mem; + } +#endif + + ret = pct1812_platform_init(pdata); + if (ret) { + dev_err(&client->dev, "%s: Failed to init platform\n", __func__); + goto error_allocate_mem; + } + + dev_set_drvdata(&client->dev, ts); + + mutex_init(&ts->i2c_mutex); + mutex_init(&ts->cmdlock); + mutex_init(&ts->eventlock); + + INIT_DELAYED_WORK(&ts->worker, pct1812_work); + + pct1812_pinctrl_init(ts); + pct1812_pinctrl_state(ts, true); + + pct1812_regulator(ts, true); + pct1812_power(ts, true); + + pct1812_delay_ms(POWER_UP_DELAY); + pct1812_set_bank(ts, BANK(6)); + ret = pct1812_wait4ready(ts, BOOT_STATUS_REG, BOOT_COMPLETE); + if (ret) { + dev_err(&client->dev, "%s: Failed to init\n", __func__); + goto error_init; + } + + ret = kfifo_alloc(&ts->cmd_pipe, sizeof(unsigned int)* 10, GFP_KERNEL); + if (ret) + goto error_init; + + pct1812_drop_mask_set(ts, 0); + ret = request_threaded_irq(client->irq, NULL, pct1812_irq_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, MYNAME, ts); + if (ret < 0) { + dev_err(&client->dev, "%s: Unable to request threaded irq\n", __func__); + goto error_fifo_alloc; + } + + /* prevent unbalanced irq enable */ + ts->irq_enabled = true; + ret = pct1812_input_dev(ts, true); + if (ret < 0) { + dev_err(&client->dev, "%s: Unable register input\n", __func__); + goto error_input_alloc; + } + + /* sensing on */ + pct1812_sensing_on(ts); + /* enqueue CMD_CONFIG and CMD_INFO */ + pct1812_fifo_cmd_add(ts, CMD_CONFIG, TOUCH_RESET_DELAY); + pct1812_fifo_cmd_add(ts, CMD_WDOG, WDOG_INTERVAL); + + ret = sysfs_create_group(&client->dev.kobj, &pct1812_attrs_group); + if (ret) + dev_warn(&client->dev, "%s: Error creating sysfs entries %d\n", __func__, ret); + + sysfs_create_bin_file(&client->dev.kobj, &selftest_bin_attr); + + return 0; + +error_input_alloc: + free_irq(client->irq, ts); + +error_fifo_alloc: + kfifo_free(&ts->cmd_pipe); + +error_init: + pct1812_power(ts, false); + pct1812_regulator(ts, false); + +error_allocate_mem: +error_allocate_pdata: + + dev_err(&client->dev, "%s: failed(%d)\n", __func__, ret); + + return ret; +} + +static ssize_t ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pct1812_data *ts = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%s%s\n%s%04x%04x\n%s%02x%02x%02x%02x\n", + "Product_ID: ", ts->model == 0x47 ? "pct1812ff" : "unknown", + "Build ID: ", ts->ver7e, ts->ver7f, + "Config ID: ", ts->custom_block[0], ts->custom_block[1], + ts->custom_block[2], ts->custom_block[3]); +} + +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "pixart"); +} + +static ssize_t irq_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pct1812_data *ts = dev_get_drvdata(dev); + int value = gpio_get_value(ts->plat_data->irq_gpio); + + return scnprintf(buf, PAGE_SIZE, "%s", value ? "High" : "Low"); +} + +static ssize_t drv_irq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pct1812_data *ts = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d", ts->irq_enabled ? 1 : 0); +} + +static ssize_t drv_irq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pct1812_data *ts = dev_get_drvdata(dev); + unsigned long value = 0; + int ret = 0; + + ret = kstrtoul(buf, 10, &value); + if (ret < 0) { + dev_err(ts->dev, "%s: Conversion failed\n", __func__); + return -EINVAL; + } + + pct1812_set_irq(ts, !!(value)); + + return size; +} + +static int pct1812_raw_data_frame(struct pct1812_data *ts, + unsigned char *buffer) +{ + unsigned char cmd, tx, rx; + int ret = 0; + + ret = pct1812_set_bank(ts, BANK(6)); + if (ret) + goto failure; + + QUERY_PARAM_IF(tx, BANK(0), 0x5a, cmd); + QUERY_PARAM_IF(rx, BANK(0), 0x59, cmd); + dev_dbg(ts->dev, "%s: Tx/Rx=%d/%d\n", __func__, tx, rx); + + ret = pct1812_set_bank(ts, BANK(1)); + if (ret) + goto failure; + cmd = 0x40; + ret = pct1812_i2c_write(ts, 0x0d, &cmd, sizeof(cmd)); + if (ret) + goto failure; + cmd = 0x06; + ret = pct1812_i2c_write(ts, 0x0e, &cmd, sizeof(cmd)); + if (ret) + goto failure; + + ret = pct1812_set_bank(ts, BANK(2)); + if (ret) + goto failure; + cmd = 0x05; + ret = pct1812_i2c_write(ts, SRAM_SELECT_REG, &cmd, sizeof(cmd)); + if (ret) + goto failure; + cmd = 0x0; + ret = pct1812_i2c_write(ts, SRAM_NSC_REG, &cmd, sizeof(cmd)); + if (ret) + goto failure; + // read data from SRAM port + ret = pct1812_i2c_read(ts, SRAM_PORT_REG, buffer, ts->frame_size); + if (ret) + goto failure; + cmd = 0x1; + ret = pct1812_i2c_write(ts, SRAM_NSC_REG, &cmd, sizeof(cmd)); + if (ret) + goto failure; + + ret = pct1812_set_bank(ts, BANK(1)); + if (ret) + goto failure; + cmd = 0x0; + ret = pct1812_i2c_write(ts, 0x0d, &cmd, sizeof(cmd)); + if (ret) + goto failure; + ret = pct1812_i2c_write(ts, 0x0e, &cmd, sizeof(cmd)); + if (ret) + goto failure; + + ret = pct1812_set_bank(ts, BANK(6)); + if (ret) + goto failure; + cmd = 0x00; // clear interrupt + ret = pct1812_i2c_write(ts, STATUS_REG, &cmd, sizeof(cmd)); + if (ret) + goto failure; + + return 0; + +failure: + dev_err(ts->dev, "%s: Read error\n", __func__); + + return -EIO; +} + +static unsigned char *selftest_data_ptr(struct pct1812_data *ts, + unsigned int offset, unsigned int need2read, unsigned int *avail) +{ + int ff; + unsigned char *bptr = NULL; + + for (ff = 0; ff < ts->frame_count; ff++) { + if (offset >= ts->frame_size) { + offset -= ts->frame_size; + continue; + } + bptr = ts->test_data[ff]; + bptr += offset; + if (need2read < (ts->frame_size - offset)) + *avail = need2read; + else + *avail = ts->frame_size - offset; + break; + } + + return bptr; +} + +static ssize_t selftest_bin_show(struct file *filp, struct kobject *kobp, + struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) +{ + int mode; + unsigned char *bptr; + unsigned int remain, available; + unsigned int bOff = 0; + unsigned int dOffset = 0; + struct i2c_client *client = kobj_to_i2c_client(kobp); + struct pct1812_data *ts = i2c_get_clientdata(client); + + if (!ts || pos < 0) + return -EINVAL; + + mode = pct1812_selftest_get(ts); + if (mode == PCT1812_SELFTEST_NONE || ts->test_data_size == 0) { + dev_warn(ts->dev, "%s: No selftest results available\n", __func__); + return 0; + } + + if (pos >= ts->test_data_size) { + dev_warn(ts->dev, "%s: Position %lu beyond data boundary of %u\n", + __func__, pos, ts->test_data_size); + return 0; + } + + if (count > ts->test_data_size - pos) + count = ts->test_data_size - pos; + /* available data size */ + remain = count; + + if (pos == 0) { + DBG_PRINT(DBG_BIT_TEST,"sending selftest data header\n"); + memcpy(buf, &ts->test_data_size, sizeof(unsigned int)); + bOff += sizeof(unsigned int); + memcpy(buf + bOff, &ts->test_type, sizeof(unsigned char)); + bOff += sizeof(unsigned char); + if (ts->test_type != PCT1812_SELFTEST_DRAW_LINE) { + memcpy(buf + bOff, &ts->tx_count, sizeof(unsigned char)); + bOff += sizeof(unsigned char); + memcpy(buf + bOff, &ts->rx_count, sizeof(unsigned char)); + bOff += sizeof(unsigned char); + } + /* available data size after sending header */ + remain -= bOff; + } else if (pos >= ts->test_hdr_size) { + /* offset within data buffer (excludes header) */ + dOffset = pos - ts->test_hdr_size; + } + + DBG_PRINT(DBG_BIT_TEST,"start at offset %u, remaining %u\n", dOffset, remain); + while ((bptr = selftest_data_ptr(ts, dOffset, remain, &available)) && + available > 0) { + DBG_PRINT(DBG_BIT_TEST,"available for copying %u bytes at offset %u\n", available, dOffset); + memcpy(buf + bOff, bptr, available); + bOff += available; + dOffset += available; + remain -= available; + } + + if ((pos + count) >= ts->test_data_size) { + pct1812_selftest_cleanup(ts); + } + + return count; +} + +static ssize_t pct1812_print_raw_data(struct pct1812_data *ts, char *buf) +{ + unsigned char *bptr; + int ival, ll, rr, cc; + ssize_t blen = 0; + + blen = scnprintf(buf, PAGE_SIZE, "%08x;%02x;%02x,%02x\n", + ts->test_data_size, ts->test_type, ts->tx_count, ts->rx_count); + for (ll = 0; ll < ts->frame_count; ll++) { + blen += scnprintf(buf + blen, PAGE_SIZE - blen, + " Tx0 Tx1 Tx2 Tx3 Tx4 Tx5\n"); + bptr = ts->test_data[ll]; + for (rr = 0; rr < ts->rx_count; rr++) { + blen += scnprintf(buf + blen, PAGE_SIZE - blen, "Rx%d: ", rr); + for (cc = 0; cc < ts->tx_count; cc++) { + DBG_PRINT(DBG_BIT_TEST,"offset %d\n", (int)(bptr - ts->test_data[ll])); + ival = comp2_16b(bptr); + bptr += 2; + blen += scnprintf(buf + blen, PAGE_SIZE - blen, "%5d ", ival); + } + blen += scnprintf(buf + blen, PAGE_SIZE - blen, "\n"); + } + } + + return blen; +} + +static ssize_t pct1812_print_coords(struct pct1812_data *ts, char *buf) +{ + int ss; + ssize_t blen = 0; + + blen = scnprintf(buf, PAGE_SIZE, "%08x;%02x\n", ts->test_data_size, ts->test_type); + for (ss = 0; ss < ts->frame_sample; ss++) { + blen += scnprintf(buf + blen, PAGE_SIZE - blen, + "%5d,%5d;\n", UINT16_X(ts->test_data[ss]), UINT16_Y(ts->test_data[ss])); + } + + return blen; +} + +static ssize_t selftest_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pct1812_data *ts = dev_get_drvdata(dev); + int mode; + ssize_t blen = 0; + + mode = pct1812_selftest_get(ts); + if (mode == PCT1812_SELFTEST_IN_PROGRESS || + ts->test_type == PCT1812_SELFTEST_NONE || + ts->test_data_size == 0) { + dev_warn(ts->dev, "%s: No selftest results available\n", __func__); + return blen; + } + + if (ts->test_type == PCT1812_SELFTEST_FULL || + ts->test_type == PCT1812_SELFTEST_SNR) { + DBG_PRINT(DBG_BIT_TEST,"%d frame(s) available\n", ts->frame_count); + blen = pct1812_print_raw_data(ts, buf); + } else { + DBG_PRINT(DBG_BIT_TEST,"%d sample(s) available\n", ts->frame_sample); + blen = pct1812_print_coords(ts, buf); + } + + pct1812_selftest_cleanup(ts); + + return blen; +} + +static ssize_t selftest_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pct1812_data *ts = dev_get_drvdata(dev); + unsigned long value = 0; + unsigned char *test, cmd; + int mode, ret = 0; + int ll; + + mode = pct1812_selftest_get(ts); + if (mode != PCT1812_SELFTEST_NONE) { + dev_err(ts->dev, "%s: Selftest in progress\n", __func__); + return -EBUSY; + } + + ret = kstrtoul(buf, 10, &value); + if (ret < 0) { + dev_err(ts->dev, "%s: Conversion failed\n", __func__); + return -EINVAL; + } else if (value <= PCT1812_SELFTEST_NONE || value >= PCT1812_SELFTEST_MAX) { + dev_err(ts->dev, "%s: Invalid selftest id %lu\n", __func__, value); + return -EINVAL; + } + + ts->test_type = mode = value & 0xff; + switch (mode) { + case PCT1812_SELFTEST_FULL: + ts->frame_size = ts->tx_count * ts->rx_count * sizeof(unsigned char) * 2; + ts->frame_count = 1; + ts->test_hdr_size = SELFTEST_HDR_SZ + sizeof(unsigned char) * 2; + ts->test_data_size = ts->frame_count * ts->frame_size + ts->test_hdr_size; + break; + case PCT1812_SELFTEST_SNR: + ts->frame_size = ts->tx_count * ts->rx_count * sizeof(unsigned char) * 2; + ts->frame_count = SNR_LOOP_COUNT; + ts->test_hdr_size = SELFTEST_HDR_SZ + sizeof(unsigned char) * 2; + ts->test_data_size = ts->frame_count * ts->frame_size + ts->test_hdr_size; + break; + case PCT1812_SELFTEST_DRAW_LINE: + ts->frame_size = sizeof(unsigned char) * 4; + ts->frame_count = 100; // alloc memory for 100 samples + ts->test_hdr_size = SELFTEST_HDR_SZ; + ts->test_data_size = ts->test_hdr_size; + break; + } + + ret = pct1812_power_mode(ts, PWR_MODE_NO_DEEP_SLEEP); + if (ret) { + dev_err(ts->dev, "%s: error setting no deep sleep power mode\n", __func__); + } + + ret = pct1812_selftest_memory(ts, true); + if (ret) + return -ENOMEM; + + switch (mode) { + case PCT1812_SELFTEST_FULL: + ts->test_type = PCT1812_SELFTEST_FULL; + pct1812_selftest_set(ts, PCT1812_SELFTEST_FULL); + test = "SELFTEST_FULL"; + break; + case PCT1812_SELFTEST_SNR: + ts->test_type = PCT1812_SELFTEST_SNR; + pct1812_selftest_set(ts, PCT1812_SELFTEST_SNR); + test = "SELFTEST_SNR"; + break; + case PCT1812_SELFTEST_DRAW_LINE: + ts->test_type = PCT1812_SELFTEST_DRAW_LINE; + pct1812_selftest_set(ts, PCT1812_SELFTEST_DRAW_LINE); + ts->test_start_time = ktime_get(); + ret = pct1812_run_cmd(ts, CMD_RESET, BOOT_COMPLETE); + if (ret) { + dev_err(ts->dev, "%s: Failed to reset\n", __func__); + goto failure; + } + pct1812_report_mode(ts, STAT_BIT_EABS, 0xff, &ts->test_intr_mask); + pct1812_drop_mask_set(ts, STAT_BIT_GESTURE); + // arm timed out cleanup + pct1812_fifo_cmd_add(ts, CMD_CLEANUP, SELFTEST_EXTRA_LONG_INTERVAL); + return size; + } + + // disable IRQ + pct1812_set_irq(ts, false); + // RESET + ret = pct1812_run_cmd(ts, CMD_MODE_RESET, MODE_COMPLETE); + if (ret) { + dev_err(ts->dev, "%s: Failed to reset\n", __func__); + goto failure; + } + // INITIAL + ret = pct1812_set_bank(ts, BANK(6)); + if (ret) { + dev_err(ts->dev, "%s: Pre-initial error\n", __func__); + goto failure; + } + cmd = 0x00; + ret = pct1812_user_access(ts, BANK(0), 0x08, &cmd, sizeof(cmd), AWRITE); + if (ret) { + dev_err(ts->dev, "%s: Write error: bank=0, addr=0x08, value=0x00\n", __func__); + goto failure; + } + cmd = 0x01; + ret = pct1812_user_access(ts, BANK(0), 0x15, &cmd, sizeof(cmd), AWRITE); + if (ret) { + dev_err(ts->dev, "%s: Write error: bank=0, addr=0x15, value=0x01\n", __func__); + goto failure; + } + cmd = 0x00; + ret = pct1812_user_access(ts, BANK(1), 0x90, &cmd, sizeof(cmd), AWRITE); + if (ret) { + dev_err(ts->dev, "%s: Write error: bank=1, addr=0x90, value=0x00\n", __func__); + goto failure; + } + // SNR selftest requires an extra step + if ((value & 0xff) == PCT1812_SELFTEST_SNR) { + cmd = 0x0f; + ret = pct1812_user_access(ts, BANK(1), 0x91, &cmd, sizeof(cmd), AWRITE); + if (ret) { + dev_err(ts->dev, "%s: Write error: bank=1, addr=0x91, value=0x0f\n", __func__); + goto failure; + } + } + cmd = 0x00; + ret = pct1812_user_access(ts, BANK(0), 0x2d, &cmd, sizeof(cmd), AWRITE); + if (ret) { + dev_err(ts->dev, "%s: Write error: bank=0, addr=0x08, value=0x00\n", __func__); + goto failure; + } + cmd = 0x01; + ret = pct1812_user_access(ts, BANK(0), 0x08, &cmd, sizeof(cmd), AWRITE); + if (ret) { + dev_err(ts->dev, "%s: Write error: bank=0, addr=0x08, value=0x01\n", __func__); + goto failure; + } + + pct1812_selftest_set(ts, PCT1812_SELFTEST_IN_PROGRESS); + dev_info(ts->dev, "%s: Performing %s\n", __func__, test); + for (ll = 0; ll < ts->frame_count; ll++) { + ret = pct1812_raw_data_frame(ts, ts->test_data[ll]); + if (ret) { + dev_err(ts->dev, "%s: Error reading frame %d\n", __func__, ll); + break; + } + } + /* reset in case of failed selftest data collection */ + if (ret) + goto failure; + + /* restore selftest type */ + pct1812_selftest_set(ts, mode); + ret = pct1812_run_cmd(ts, CMD_RESET, BOOT_COMPLETE); + if (ret) + dev_err(ts->dev, "%s: Reset error\n", __func__); + // enable IRQ + pct1812_set_irq(ts, true); + + return size; + +failure: + ret = pct1812_run_cmd(ts, CMD_RESET, BOOT_COMPLETE); + if (ret) + dev_err(ts->dev, "%s: Reset error\n", __func__); + pct1812_selftest_set(ts, PCT1812_SELFTEST_NONE); + // enable IRQ + pct1812_set_irq(ts, true); + + return size; +} + +static ssize_t doreflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pct1812_data *ts = dev_get_drvdata(dev); + char fname[64]; + int ret = 0; + + if (size >= sizeof(fname)) { + dev_err(ts->dev, "%s: File name %d too long (max %d)\n", __func__, size, sizeof(fname)); + return -EINVAL; + } + + strncpy(fname, buf, sizeof(fname)); + if (fname[size - 1] == '\n') + fname[size - 1] = 0; + + ret = pct1812_fw_update(ts, fname); + if (ret) + dev_err(ts->dev, "%s: FW update failed\n", __func__); + + return ret ? -EIO : size; +} + +static ssize_t forcereflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + return size; +} + +static ssize_t reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pct1812_data *ts = dev_get_drvdata(dev); + unsigned long value = 0; + enum cmds cmd; + unsigned char vok; + int ret = 0; + + ret = kstrtoul(buf, 10, &value); + if (ret < 0) { + dev_err(ts->dev, "%s: Conversion failed\n", __func__); + return -EINVAL; + }else if (value == 0) { + cmd = CMD_MODE_RESET; + vok = MODE_COMPLETE; + } else { + cmd = CMD_RESET; + vok = BOOT_COMPLETE; + } + + dev_info(ts->dev, "%s: Performing reset %s\n", __func__, cmd == CMD_MODE_RESET ? "MODE" : "HW"); + + ret = pct1812_run_cmd(ts, cmd, vok); + if (ret) + dev_err(ts->dev, "%s: Incomplete reset\n", __func__); + + return size; +} + +#define USER_INFO \ + ENTRY(X_RES_L, 2, 0x00),\ + ENTRY(X_RES_H, 2, 0x01),\ + ENTRY(Y_RES_L, 2, 0x02),\ + ENTRY(Y_RES_H, 2, 0x03),\ + ENTRY(V_SCROLL_RES, 3, 0x26),\ + ENTRY(H_SCROLL_RES, 3, 0x2e),\ + ENTRY(ABS_DISTANCE, 3, 0x0b), + +#undef ENTRY +#define ENTRY(a, b, c) #a +static char *ui_title[] = { USER_INFO }; + +#undef ENTRY +#define ENTRY(a, b, c) {b, c} +static struct bank_reg ui_addr[] = { USER_INFO }; + +static ssize_t info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pct1812_data *ts = dev_get_drvdata(dev); + int i, ret; + ssize_t blen; + + blen = scnprintf(buf, PAGE_SIZE, " Product: PixArt %s\n", + ts->model == 0x47 ? "PCT1812FF" : "unknown"); + blen += scnprintf(buf + blen, PAGE_SIZE - blen, " FW ver: %d.%d\n", ts->major, ts->minor); + blen += scnprintf(buf + blen, PAGE_SIZE - blen, " FW rev: %d patch %d\n", ts->revision, ts->patch); + blen += scnprintf(buf + blen, PAGE_SIZE - blen, " Tx/Rx: %dx%d\n", ts->tx_count, ts->rx_count); + blen += scnprintf(buf + blen, PAGE_SIZE - blen, "Resolution: %dx%d\n", ts->x_res, ts->y_res); + blen += scnprintf(buf + blen, PAGE_SIZE - blen, "Max points: %d\n", ts->max_points); + blen += scnprintf(buf + blen, PAGE_SIZE - blen, "Boot block: %d\n", ts->boot_block); + blen += scnprintf(buf + blen, PAGE_SIZE - blen, " Version: %04x/%04x\n", ts->ver7e, ts->ver7f); + + for (i = 0; i < ARRAY_SIZE(ui_title); i++) { + unsigned char value, tmp; + + QUERY_PARAM_IF(value, BANK(ui_addr[i].bank), ui_addr[i].reg, tmp); + blen += scnprintf(buf + blen, PAGE_SIZE - blen, "%12s: 0x%02x\n", ui_title[i], value); + } +failure: + return blen; +} + +static ssize_t mask_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pct1812_data *ts = dev_get_drvdata(dev); + long value = 0; + unsigned char set_mask, clear_mask; + int ret; + + ret = kstrtol(buf, 10, &value); + if (ret < 0) { + dev_err(ts->dev, "%s: Conversion failed\n", __func__); + return -EINVAL; + } else if (value < -255 || value > 255) { + dev_err(ts->dev, "%s: Value %ld is out of range [-255,255]\n", __func__); + return -EINVAL; + } + + if (value < 0) { + value = -value; + clear_mask = value & 0xff; + set_mask = 0; + } else { + set_mask = value & 0xff; + clear_mask = 0; + } + + ret = pct1812_report_mode(ts, set_mask, clear_mask, NULL); + if (ret) + dev_err(ts->dev, "%s: Intr mask set error\n", __func__); + + return ret ? -EIO: size; +} + +static ssize_t mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pct1812_data *ts = dev_get_drvdata(dev); + unsigned char mask = 0; + unsigned char val; + int ret; + QUERY_PARAM(mask, BANK(0), INTR_MASK_REG, val); + return scnprintf(buf, PAGE_SIZE, "%02x\n", mask); +} + +static ssize_t debug_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct pct1812_data *ts = dev_get_drvdata(dev); + long value = 0; + unsigned char mask; + int ret; + + ret = kstrtol(buf, 10, &value); + if (ret < 0) { + dev_err(ts->dev, "%s: Conversion failed\n", __func__); + return -EINVAL; + } else if (value < -255 || value > 255) { + dev_err(ts->dev, "%s: Value %ld is out of range [-255,255]\n", __func__); + return -EINVAL; + } + + if (value < 0) { + value = -value; + mask = value & 0xff; + ts->debug &= ~mask; + } else { + mask = value & 0xff; + ts->debug |= mask; + } + + return ret ? -EINVAL: size; +} + +static ssize_t debug_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pct1812_data *ts = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%02x\n", ts->debug); +} + +static const struct i2c_device_id pct1812_id[] = { + { MYNAME, 0 }, + { }, +}; + +static const struct of_device_id pct1812_match_table[] = { + { .compatible = "pixart,pct1812_ts",}, + { }, +}; + +static struct i2c_driver pct1812_driver = { + .driver = { + .owner = THIS_MODULE, + .name = MYNAME, + .of_match_table = pct1812_match_table, + }, + .probe = pct1812_probe, + .id_table = pct1812_id, +}; + +module_i2c_driver(pct1812_driver); + +MODULE_AUTHOR("Konstantin Makariev "); +MODULE_DESCRIPTION("I2C Driver for PixArt Imaging PCT1812FF"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/raydium_mmi/Android.mk b/drivers/input/touchscreen/raydium_mmi/Android.mk new file mode 100644 index 000000000000..98b0e5c5ce4c --- /dev/null +++ b/drivers/input/touchscreen/raydium_mmi/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := raydium_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/raydium_mmi/Kbuild b/drivers/input/touchscreen/raydium_mmi/Kbuild new file mode 100644 index 000000000000..7a980ecb5062 --- /dev/null +++ b/drivers/input/touchscreen/raydium_mmi/Kbuild @@ -0,0 +1,7 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/raydium_mmi + +obj-m := raydium_mmi.o +raydium_mmi-objs += raydium_i2c_ts.o diff --git a/drivers/input/touchscreen/raydium_mmi/Makefile b/drivers/input/touchscreen/raydium_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/raydium_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/raydium_mmi/raydium_i2c_ts.c b/drivers/input/touchscreen/raydium_mmi/raydium_i2c_ts.c new file mode 100644 index 000000000000..d6506cc40705 --- /dev/null +++ b/drivers/input/touchscreen/raydium_mmi/raydium_i2c_ts.c @@ -0,0 +1,5463 @@ +/* drivers/input/touchscreen/raydium_ts.c + * + * Raydium TouchScreen driver. + * + * Copyright (c) 2010 Raydium tech Ltd. + * + * 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. + * + */ +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_DRM) +#include +#include +#define RAYDIUM_UNBLANK MSM_DRM_BLANK_UNBLANK +#define RAYDIUM_BLANK MSM_DRM_BLANK_POWERDOWN +#define RAYDIUM_VSYNC_SUSPEND 0x0fffffff +#elif defined(CONFIG_FB) +#include +#include +#define RAYDIUM_UNBLANK FB_BLANK_UNBLANK +#define RAYDIUM_BLANK FB_BLANK_POWERDOWN +#define RAYDIUM_VSYNC_SUSPEND FB_BLANK_VSYNC_SUSPEND +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#endif + +#include "raydium_i2c_ts.h" +//#include "RADFWImage.h" +static const char *RadInitImage; +static const char *RadBootImage; +static const char *RadFWImage; +static const char *RadTestFWImage; + +// TODO: Using struct+memcpy instead of array+offset +enum raydium_pt_report_idx { + POS_SEQ = 0,/*1:touch, 0:no touch*/ + POS_PT_AMOUNT, + POS_PT_ID = 0, + POS_X_L, + POS_X_H, + POS_Y_L, + POS_Y_H, + POS_PRESSURE_L, + POS_PRESSURE_H, + POS_WX_L, + POS_WX_H, + POS_WY_L, + POS_WY_H, + LENGTH_PT = 11 +}; + +struct raydium_slot_status { + unsigned char occupied_pt_id; //Occupied point ID + unsigned char need_update; //Mark as info need to be updated + unsigned char pt_report_offset; //point info offset in report +}; + +struct raydium_slot_status gst_slot_status[MAX_TOUCH_NUM * 2];// The first 3 elements are currently occupied. therest is new coming points +struct raydium_slot_status gst_slot_init_status = {0xFF, 0, 0}; + +struct raydium_ts_platform_data { + const char *name; + u32 irqflags; + u32 irq_gpio; + u32 irq_gpio_flags; + u32 reset_gpio; + u32 reset_gpio_flags; + u32 x_max; + u32 y_max; + u32 x_min; + u32 y_min; + u32 hard_rst_dly; + u32 soft_rst_dly; + u32 num_max_touches; + u32 ctrl_dsi; + const char *bound_display; +}; + +struct raydium_ts_data { + unsigned int irq; + unsigned int rst; + unsigned int x_max; + unsigned int y_max; + unsigned int x_pos; + unsigned int y_pos; + unsigned int pressure; + unsigned int is_suspend; + unsigned int key_flag; + unsigned int is_sleep; + unsigned int is_close; + #ifdef GESTURE_EN + unsigned int is_palm; + #endif + + struct i2c_client *client; + struct input_dev *input_dev; + struct raydium_ts_platform_data *pdata; + struct mutex lock; + struct work_struct work; + struct delayed_work ready_work; + struct workqueue_struct *workqueue; + struct irq_desc *irq_desc; + bool irq_enabled; + bool irq_wake; + + #if (defined(CONFIG_FB) || defined(CONFIG_DRM)) + struct notifier_block panel_cb; + volatile int blank; + #elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; + #endif //end of CONFIG_FB + + //struct regulator *vdd; + struct regulator *vcc_i2c; + unsigned int fw_version; + + #ifdef MSM_NEW_VER + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; + #endif //end of MSM_NEW_VER +}; + +#if (defined(CONFIG_RM_SYSFS_DEBUG)) +static const struct attribute_group raydium_attr_group; +#endif //end of CONFIG_RM_SYSFS_DEBUG + +volatile unsigned char g_uc_raydium_flag = 0; +volatile unsigned char g_uc_gesture_status = 0; +volatile unsigned char g_uc_raydium_int_flag = 0; +volatile unsigned char g_uc_raydium_selftest_flag = 0; +volatile unsigned char g_uc_pre_palm_status = 0; +unsigned char u8_i2c_mode = PDA2_MODE; +unsigned char upgrade_type = 0; +unsigned char g_uc_raw_data_type = RAYDIUM_FT_UPDATE; +unsigned int g_ui_raw_data_length = 36 * 2; // 72 bytes +unsigned long g_ul_addr = RAYDIUM_CHECK_I2C_CMD; +unsigned int g_ui_length = 1; + +/******************************************************************************* +* Name: raydium_gpio_configure +* Brief: +* Input: +* Output: +* Return: +*******************************************************************************/ +static int raydium_gpio_configure(struct raydium_ts_data *data, bool on) +{ + int err = 0; + + if (on) + { + if (gpio_is_valid(data->pdata->irq_gpio)) + { + err = gpio_request(data->pdata->irq_gpio, "raydium_irq_gpio"); + if (err) + { + dev_err(&data->client->dev, "[touch]irq gpio request failed"); + goto err_irq_gpio_req; + } + + err = gpio_direction_input(data->pdata->irq_gpio); + if (err) + { + dev_err(&data->client->dev, "[touch]set_direction for irq gpio failed\n"); + goto err_irq_gpio_dir; + } + } + return 0; + } else + { + if (gpio_is_valid(data->pdata->irq_gpio)) + { + gpio_free(data->pdata->irq_gpio); + } + return 0; + } + +err_irq_gpio_dir: + if (gpio_is_valid(data->pdata->irq_gpio)) + { + gpio_free(data->pdata->irq_gpio); + } +err_irq_gpio_req: + return err; +} + +/******************************************************************************* +* Name: raydium_power_on +* Brief: +* Input: +* Output: +* Return: +*******************************************************************************/ +static int raydium_power_on(struct raydium_ts_data *data, bool on) +{ + int rc = 0; + + if (!on) + { + goto power_off; + } + + regulator_set_load(data->vcc_i2c,100000); + rc = regulator_enable(data->vcc_i2c); + if (rc) + { + dev_err(&data->client->dev, "[touch]Regulator vcc_i2c enable failed rc=%d\n", rc); + } + return rc; + +power_off: + regulator_set_load(data->vcc_i2c, 100); + rc = regulator_disable(data->vcc_i2c); + if (rc) + { + dev_err(&data->client->dev, "[touch]Regulator vcc_i2c disable failed rc=%d\n", rc); + } + return rc; +} + +/******************************************************************************* +* Name: raydium_power_init +* Brief: +* Input: +* Output: +* Return: +*******************************************************************************/ +static int raydium_power_init(struct raydium_ts_data *data, bool on) +{ + int rc; + + if (!on) + { + dev_err(&data->client->dev, "[touch]power_init false \n"); + goto pwr_deinit; + } + + data->vcc_i2c = regulator_get(&data->client->dev, "vcc_i2c"); + if (IS_ERR(data->vcc_i2c)) + { + rc = PTR_ERR(data->vcc_i2c); + dev_err(&data->client->dev, "[touch]regulator get failed vcc_i2c rc=%d\n", rc); + return rc; + } + + if (regulator_count_voltages(data->vcc_i2c) > 0) + { + rc = regulator_set_voltage(data->vcc_i2c, I2C_VTG_MIN_UV, I2C_VTG_MAX_UV); + if (rc) + { + dev_err(&data->client->dev, "[touch]regulator set_vtg failed vcc_i2c rc=%d\n", rc); + goto reg_vcc_i2c_put; + } + } + return 0; + +reg_vcc_i2c_put: + regulator_put(data->vcc_i2c); + return rc; + +pwr_deinit: + if (regulator_count_voltages(data->vcc_i2c) > 0) + { + regulator_set_voltage(data->vcc_i2c, 0, I2C_VTG_MAX_UV); + } + regulator_put(data->vcc_i2c); + return 0; +} + +/******************************************************************************* +* Name: raydium_ts_pinctrl_init +* Brief: +* Input: +* Output: +* Return: +*******************************************************************************/ +#ifdef MSM_NEW_VER +static int raydium_ts_pinctrl_init(struct raydium_ts_data *data) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + data->ts_pinctrl = devm_pinctrl_get(&(data->client->dev)); + if (IS_ERR_OR_NULL(data->ts_pinctrl)) + { + retval = PTR_ERR(data->ts_pinctrl); + dev_err(&data->client->dev, "[touch]target does not use pinctrl %d\n", retval); + goto err_pinctrl_get; + } + + data->pinctrl_state_active + = pinctrl_lookup_state(data->ts_pinctrl, PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(data->pinctrl_state_active)) + { + retval = PTR_ERR(data->pinctrl_state_active); + dev_err(&data->client->dev, + "[touch]Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + data->pinctrl_state_suspend + = pinctrl_lookup_state(data->ts_pinctrl, + PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(data->pinctrl_state_suspend)) + { + retval = PTR_ERR(data->pinctrl_state_suspend); + dev_err(&data->client->dev, + "[touch]Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + data->pinctrl_state_release + = pinctrl_lookup_state(data->ts_pinctrl, + PINCTRL_STATE_RELEASE); + if (IS_ERR_OR_NULL(data->pinctrl_state_release)) + { + retval = PTR_ERR(data->pinctrl_state_release); + dev_err(&data->client->dev, + "[touch]Can not lookup %s pinstate %d\n", + PINCTRL_STATE_RELEASE, retval); + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(data->ts_pinctrl); +err_pinctrl_get: + data->ts_pinctrl = NULL; + return retval; +} +#endif //end of MSM_NEW_VER + +static int raydium_i2c_pda_set_address(struct raydium_ts_data *data, + unsigned long address, unsigned char mode) +{ + int retval = 0; + unsigned char retry; + unsigned char buf[RAYDIUM_I2C_PDA_ADDRESS_LENGTH]; + struct i2c_client *i2c = data->client; + + u8_i2c_mode = PDA_MODE; + + i2c->addr = RAYDIUM_I2C_EID; + buf[0] = (address & 0x0000FF00) >> 8; + buf[1] = (address & 0x00FF0000) >> 16; + buf[2] = (address & 0xFF000000) >> 24; + buf[3] = mode; + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) + { + retval = i2c_master_send(i2c, buf, RAYDIUM_I2C_PDA_ADDRESS_LENGTH); + if (retval != RAYDIUM_I2C_PDA_ADDRESS_LENGTH) + { + dev_err(&i2c->dev,"[touch]%s: I2C retry %d\n",__func__, retry + 1); + msleep(1); + } else + { + break; + } + } + + return (retval == RAYDIUM_I2C_PDA_ADDRESS_LENGTH) ? retval : -EIO; +} + +//device attribute raydium_i2c_pda2_mode used +static int raydium_i2c_pda_read(struct i2c_client *client, + unsigned long addr, unsigned char *r_data, unsigned short length) +{ + int retval; + unsigned char retry; + unsigned char mode = 0x00; + unsigned char buf; + struct raydium_ts_data *data = i2c_get_clientdata(client); + + struct i2c_msg msg[] = { + { + .addr = RAYDIUM_I2C_NID, + .flags = RAYDIUM_I2C_WRITE, + .len = 1, + .buf = &buf, + }, + { + .addr = RAYDIUM_I2C_NID, + .flags = RAYDIUM_I2C_READ, + .len = length, + .buf = r_data, + }, + }; + + if (length == 4) + { + mode |= RAYDIUM_I2C_PDA_MODE_ENABLE | RAYDIUM_I2C_PDA_2_MODE_DISABLE | RAYDIUM_I2C_PDA_MODE_WORD_MODE; + } else + { + mode |= RAYDIUM_I2C_PDA_MODE_ENABLE | RAYDIUM_I2C_PDA_2_MODE_DISABLE; + } + buf = addr & MASK_8BIT; + + retval = raydium_i2c_pda_set_address(data, addr, mode); + if (retval != RAYDIUM_I2C_PDA_ADDRESS_LENGTH) + { + goto exit; + } + usleep_range(50, 80); + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) + { + if (i2c_transfer(data->client->adapter, msg, 2) == 2) + { + retval = length; + break; + } + dev_err(&data->client->dev,"%s: I2C retry %d\n",__func__, retry + 1); + msleep(1); + } + + if (retry == SYN_I2C_RETRY_TIMES) + { + dev_err(&data->client->dev,"%s: I2C read over retry limit\n",__func__); + retval = -EIO; + } +exit: + return retval; +} + +static int raydium_i2c_pda_write(struct i2c_client *client, + unsigned long addr, unsigned char *w_data, unsigned short length) +{ + int retval; + unsigned char retry; + unsigned char mode = 0x00; + unsigned char buf[MAX_WRITE_PACKET_SIZE + 1]; + struct raydium_ts_data *data = i2c_get_clientdata(client); + + struct i2c_msg msg[] = { + { + .addr = RAYDIUM_I2C_NID, + .flags = RAYDIUM_I2C_WRITE, + .len = length + 1, + .buf = buf, + }, + }; + + + if (length > MAX_WRITE_PACKET_SIZE) + { + return -EINVAL; + } + + if (length == 4) + { + mode |= RAYDIUM_I2C_PDA_MODE_ENABLE | RAYDIUM_I2C_PDA_2_MODE_DISABLE | RAYDIUM_I2C_PDA_MODE_WORD_MODE; + } + else + { + mode |= RAYDIUM_I2C_PDA_MODE_ENABLE | RAYDIUM_I2C_PDA_2_MODE_DISABLE; + } + + buf[0] = addr & MASK_8BIT; + memcpy(&buf[1], w_data, length); + + retval = raydium_i2c_pda_set_address(data, addr, mode); + if (retval != RAYDIUM_I2C_PDA_ADDRESS_LENGTH) + { + goto exit; + } + usleep_range(50, 80); + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) + { + if (i2c_transfer(client->adapter, msg, 1) == 1) + { + retval = length; + break; + } + dev_err(&data->client->dev,"[touch]%s: I2C retry %d\n",__func__, retry + 1); + msleep(1); + } + + if (retry == SYN_I2C_RETRY_TIMES) + { + dev_err(&data->client->dev,"[touch]%s: I2C write over retry limit\n",__func__); + retval = -EIO; + } +exit: + return retval; +} + +static int raydium_i2c_pda2_set_page(struct i2c_client *client, unsigned char page) +{ + int retval = -1; + unsigned char retry; + unsigned char buf[RAYDIUM_I2C_PDA2_PAGE_LENGTH]; + struct raydium_ts_data *data = i2c_get_clientdata(client); + + struct i2c_msg msg[] = { + { + .addr = RAYDIUM_I2C_NID, + .flags = RAYDIUM_I2C_WRITE, + .len = RAYDIUM_I2C_PDA2_PAGE_LENGTH, + .buf = buf, + }, + }; + + buf[0] = RAYDIUM_PDA2_PAGE_ADDR; + buf[1] = page; + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) + { + if (i2c_transfer(client->adapter, msg, 1) == 1) + { + retval = RAYDIUM_I2C_PDA2_PAGE_LENGTH; + break; + } + msleep(1); + } + + if (retry == SYN_I2C_RETRY_TIMES) + { + dev_err(&data->client->dev,"[touch]%s: I2C write over retry limit\n",__func__); + retval = -EIO; + } + + return retval; +} + +static int raydium_i2c_pda2_read(struct i2c_client *client, + unsigned char addr, unsigned char *r_data, unsigned short length) +{ + int retval = -1; + unsigned char retry; + struct raydium_ts_data *data = i2c_get_clientdata(client); + + struct i2c_msg msg[] = { + { + .addr = RAYDIUM_I2C_NID, + .flags = RAYDIUM_I2C_WRITE, + .len = 1, + .buf = &addr, + }, + { + .addr = RAYDIUM_I2C_NID, + .flags = RAYDIUM_I2C_READ, + .len = length, + .buf = r_data, + }, + }; + u8_i2c_mode = PDA2_MODE; + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) + { + if (i2c_transfer(data->client->adapter, msg, 2) == 2) + { + retval = length; + break; + } + msleep(1); + } + + if (retry == SYN_I2C_RETRY_TIMES) + { + dev_err(&data->client->dev,"[touch]%s: I2C read over retry limit\n",__func__); + retval = -EIO; + } + + return retval; +} + +static int raydium_i2c_pda2_write(struct i2c_client *client, + unsigned char addr, unsigned char *w_data, unsigned short length) +{ + int retval = -1; + unsigned char retry; + unsigned char buf[MAX_WRITE_PACKET_SIZE + 1]; + struct raydium_ts_data *data = i2c_get_clientdata(client); + + struct i2c_msg msg[] = { + { + .addr = RAYDIUM_I2C_NID, + .flags = RAYDIUM_I2C_WRITE, + .len = length + 1, + .buf = buf, + }, + }; + + + if (length > MAX_WRITE_PACKET_SIZE) + { + return -EINVAL; + } + u8_i2c_mode = PDA2_MODE; + buf[0] = addr; + memcpy(&buf[1], w_data, length); + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) + { + if (i2c_transfer(client->adapter, msg, 1) == 1) + { + retval = length; + break; + } + msleep(1); + } + + if (retry == SYN_I2C_RETRY_TIMES) + { + dev_err(&data->client->dev,"[touch]%s: I2C write over retry limit\n",__func__); + retval = -EIO; + } + + return retval; +} + +static void raydium_irq_control(struct raydium_ts_data *ts, bool enable) +{ + pr_debug("[touch]%d, disable depth : %u\n", enable, ts->irq_desc->depth); + if (enable) + { + if (ts->irq_enabled) + { + pr_debug("[touch]Already enable irq\n"); + return; + } + + /* Clear interrupts first */ + if (ts->blank != RAYDIUM_BLANK) { + mutex_lock(&ts->lock); + if (raydium_i2c_pda2_set_page(ts->client, RAYDIUM_PDA2_PAGE_0) < 0) + { + dev_err(&ts->client->dev, "[touch]failed to set page %s\n", __func__); + } + mutex_unlock(&ts->lock); + mdelay(1); + } + while (ts->irq_desc->depth > 0) + { + pr_debug("[touch]irq enable\n"); + ts->irq_enabled = true; + enable_irq(ts->irq); + } + } else + { + if (ts->irq_enabled) + { + if (ts->irq_desc->depth == 0) + { + disable_irq(ts->irq); + ts->irq_enabled = false; + pr_debug("[touch]irq disable\n"); + } + } + } +} + +#ifdef CONFIG_RM_SYSFS_DEBUG +static int raydium_get_palm_state(struct raydium_ts_data *raydium_ts, unsigned char pre_palm_status) +{ + unsigned char rbuffer[1]; + unsigned int ui_palm_status = 0; + unsigned char tp_status[MAX_TCH_STATUS_PAKAGE_SIZE]; + int ret = -1; + + mutex_lock(&raydium_ts->lock); + // Read palm state + ret = raydium_i2c_pda2_set_page(raydium_ts->client, RAYDIUM_PDA2_PAGE_0); + if (ret < 0) + { + dev_err(&raydium_ts->client->dev, "[touch]%s: failed to set page: %d\n", __func__, ret); + goto exit_error; + } + ret = raydium_i2c_pda2_read(raydium_ts->client, RAYDIUM_PDA2_PALM_STATUS_ADDR, rbuffer, 1); + if (ret < 0) + { + dev_err(&raydium_ts->client->dev, "[touch]%s: failed to read data: %d\n", __func__, ret); + goto exit_error; + } + ui_palm_status = rbuffer[0]; + if (ui_palm_status == RAYDIUM_PALM_MODE_ENABLE || (ui_palm_status == RAYDIUM_PALM_MODE_DISABLE && pre_palm_status == RAYDIUM_PALM_MODE_ENABLE)) + { + //clear seq num + tp_status[POS_SEQ] = 0; + ret = raydium_i2c_pda2_write(raydium_ts->client, RAYDIUM_PDA2_TCH_RPT_STATUS_ADDR, tp_status, 1); + if (ret < 0) + { + dev_err(&raydium_ts->client->dev, "[touch]%s: failed to write data: %d\n",__func__, ret); + goto exit_error; + } + } + mutex_unlock(&raydium_ts->lock); + return ui_palm_status; + +exit_error: + mutex_unlock(&raydium_ts->lock); + return ret; +} + +static int raydium_set_palm_state(struct raydium_ts_data *raydium_ts, unsigned char set_palm) +{ + unsigned char wbuffer[1]; + unsigned int ui_palm_status = 0; + int ret = -1; + + mutex_lock(&raydium_ts->lock); + // Read palm state + ret = raydium_i2c_pda2_set_page(raydium_ts->client, RAYDIUM_PDA2_PAGE_0); + if (ret < 0) + { + goto exit_error; + } + wbuffer[0] = set_palm; + ret = raydium_i2c_pda2_write(raydium_ts->client, RAYDIUM_PDA2_PALM_STATUS_ADDR, wbuffer, 1); + if (ret < 0) + { + goto exit_error; + } + mutex_unlock(&raydium_ts->lock); + return ui_palm_status; + +exit_error: + mutex_unlock(&raydium_ts->lock); + return ret; +} + +static ssize_t raydium_touch_calibration_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned char rbuffer[1]; + int buf_len = 0; + int ret = -1; + unsigned char retry = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + if (ts->is_suspend) + { + pr_debug("[touch]Raydium IC is_suspend\n"); + } + raydium_irq_control(ts, DISABLE); + mutex_lock(&ts->lock); + + ret = raydium_i2c_pda2_set_page(client, RAYDIUM_PDA2_PAGE_0); + if (ret < 0) + { + goto exit_i2c_error; + } + rbuffer[0] = RAYDIUM_HOST_CMD_CALIBRATION; + ret = raydium_i2c_pda2_write(client, RAYDIUM_PDA2_HOST_CMD_ADDR, rbuffer, 1); + if (ret < 0) + { + goto exit_i2c_error; + } + + do { + if (rbuffer[0] == RAYDIUM_HOST_CMD_NO_OP) + { + break; + } else + { + msleep(1000); + } + ret = raydium_i2c_pda2_read(client, RAYDIUM_PDA2_HOST_CMD_ADDR, rbuffer, 1); + if (ret < 0) + { + goto exit_i2c_error; + } + pr_debug("[touch]Raydium return 0x%02x!!\n", rbuffer[0]); + } while (retry++ < (SYN_I2C_RETRY_TIMES * 2)); + + memcpy(buf, rbuffer, 1); + + buf_len = strlen(buf); + ret = buf_len + 1; + +exit_i2c_error: + mutex_unlock(&ts->lock); + raydium_irq_control(ts, ENABLE); + + return ret; +} + +static ssize_t raydium_check_i2c_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned char rbuffer[4]; + int buf_len = 0; + int ret = -1; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + if (ts->is_suspend) + { + pr_debug("[touch]Raydium IC is_suspend\n"); + } + raydium_irq_control(ts, DISABLE); + mutex_lock(&ts->lock); + + if (u8_i2c_mode == PDA2_MODE) + { + ret = raydium_i2c_pda2_set_page(client, RAYDIUM_PDA2_PAGE_0); + if (ret < 0) + { + goto exit_i2c_error; + } + *(unsigned long *)rbuffer = (RAYDIUM_I2C_PDA_MODE_ENABLE << 24) | ((RAYDIUM_CHECK_I2C_CMD & (~MASK_8BIT)) >> 8); //using byte mode to read 4 bytes + + ret = raydium_i2c_pda2_write(client, RAYDIUM_PDA2_PDA_CFG_ADDR, rbuffer, 4); + if (ret < 0) + { + goto exit_i2c_error; + } + ret = raydium_i2c_pda2_set_page(client, RAYDIUM_PDA2_ENABLE_PDA); + if (ret < 0) + { + goto exit_i2c_error; + } + ret = raydium_i2c_pda2_read(client, (unsigned char)(RAYDIUM_CHECK_I2C_CMD& MASK_8BIT), rbuffer, 4); + if (ret < 0) + { + goto exit_i2c_error; + } + } else + { + ret = raydium_i2c_pda_read(client, RAYDIUM_CHECK_I2C_CMD, rbuffer, 4); + if (ret < 0) + { + goto exit_i2c_error; + } + } + + sprintf(buf, "[touch]Raydium Touch check i2c : %02X%02X\n", rbuffer[3], rbuffer[2]); + buf_len = strlen(buf); + ret = buf_len + 1; + +exit_i2c_error: + mutex_unlock(&ts->lock); + raydium_irq_control(ts, ENABLE); + return ret; +} + +static int raydium_i2c_mode_control(struct i2c_client *client, unsigned char mode) +{ + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + switch (mode) + { + case 0: /* Disable INT flag */ + pr_debug("[touch]Raydium INT flag : %d\n", ts->irq_enabled); + disable_irq(ts->irq); + ts->irq_enabled = false; + pr_debug("[touch]Raydium irq disable\n"); + break; + case 1: /* Enable INT flag */ + pr_debug("[touch]Raydium INT flag : %d\n", ts->irq_enabled); + enable_irq(ts->irq); + ts->irq_enabled = true; + pr_debug("[touch]Raydium irq enable\n"); + break; + case 2: /* Disable INT */ + raydium_irq_control(ts, DISABLE); + pr_debug("[touch]Raydium disable INT\n"); + break; + case 3: /* Enable INT */ + raydium_irq_control(ts, ENABLE); + pr_debug("[touch]Raydium enable INT\n"); + break; + case 4: + pr_debug("[touch]Raydium INT depth : %d\n", ts->irq_desc->depth); + } + return 0; +} + +/***************************************************************************** +** Function name: bits_reverse +** Descriptions: MSB/LSB exchange. +** parameters: num:input data, bit_num:data bit number +** Returned value: reverse value +*****************************************************************************/ +static unsigned int bits_reverse(unsigned int num, unsigned int bit_num) +{ + unsigned int reverse = 0, i; + + for (i = 0; i < bit_num; i++) + { + if (num & (1 << i)) + { + reverse |= 1<< ((bit_num - 1) -i); + } + } + return reverse; +} +/***************************************************************************** +** Function name: rc_crc32 +** Descriptions: 32 bits checksum calculation. +** parameters: buf:start address, len:length +** Returned value: checksum +*****************************************************************************/ +static unsigned int rc_crc32(const char *buf, unsigned int len, unsigned int crc) +{ + unsigned int i; + unsigned char flash_byte, uc_current, j; + + for (i = 0; i < len; i++) + { + flash_byte = buf[i]; + uc_current = (unsigned char)bits_reverse(flash_byte, 8); + for (j = 0; j <8; j++) + { + if ((crc ^ uc_current ) & 0x01) + { + crc = (crc >> 1) ^ 0xedb88320; + } else + { + crc >>= 1; + } + uc_current >>= 1; + } + } + return crc; +} + +/* upgrade firmware with image file */ +static int raydium_fw_upgrade_with_image(struct i2c_client *client, unsigned long ul_fw_addr, unsigned char type) +{ + int ret = -1; + unsigned long ul_fw_size = 0; + unsigned char *firmware_data; + unsigned long ul_write_offset = 0; + unsigned short us_write_length = 0; + unsigned int ui_checksum = 0xFFFFFFFF; + + firmware_data = kzalloc(RAYDIUM_FW_MAX_SIZE, GFP_KERNEL); + if (firmware_data == NULL ) + { + pr_err("kzalloc firmware_data failed\n"); + return -ENOMEM; + } + + memset(firmware_data, 0, ul_fw_size); + + switch (type) + { + case RAYDIUM_INIT: + memcpy(firmware_data, RadInitImage, 0x1FC); + ul_fw_size = 0x1FC; + break; + case RAYDIUM_BASELINE: + memcpy(firmware_data, (RadFWImage + 0xA04), 0x2CC); + ul_fw_size = 0x2CC; + break; + case RAYDIUM_COMP: + memcpy(firmware_data, (RadFWImage + 0xCD4), 0x244); + ul_fw_size = 0x244; + break; + case RAYDIUM_PARA: + memcpy(firmware_data, (RadFWImage + 0xF1C), 0xE0); + ul_fw_size = 0xE0; + break; + case RAYDIUM_FIRMWARE: + memcpy(firmware_data, (RadFWImage + 0x1000), 0x4FFC); + ul_fw_size = 0x4FFC; + break; + case RAYDIUM_BOOTLOADER: + memcpy(firmware_data, RadBootImage, 0x7FC); + memcpy((firmware_data + 0x800), RadInitImage, 0x1FC); + ul_fw_size = 0x9FC; + break; + case RAYDIUM_TEST_FW: + memcpy((firmware_data + 4), (RadTestFWImage + 0xA04), 0x55FC); + ul_fw_size = 0x55FC; + break; + } + + if (type == RAYDIUM_TEST_FW) + { + ui_checksum = rc_crc32((firmware_data + 4) , ul_fw_size, ui_checksum); + pr_debug("[touch]Raydium ui_checksum : 0x%08X\n", ui_checksum); + ui_checksum = bits_reverse(ui_checksum, 32); + memcpy(firmware_data, &ui_checksum, 4); + } else + { + ui_checksum = rc_crc32(firmware_data, ul_fw_size, ui_checksum); + pr_debug("[touch]Raydium ui_checksum : 0x%08X\n", ui_checksum); + ui_checksum = bits_reverse(ui_checksum, 32); + memcpy((firmware_data + ul_fw_size), &ui_checksum, 4); + } + pr_info("[touch]Raydium ui_checksum after bits reverse: 0x%08X\n", ui_checksum); + pr_info("[touch]Raydium (firmware_data + ul_fw_size): 0x%08X\n", *(unsigned int*)(firmware_data + ul_fw_size)); + ul_fw_size += 4; + + ul_write_offset = 0; + while (ul_write_offset < ul_fw_size) + { + if ((ul_write_offset + MAX_WRITE_PACKET_SIZE) < ul_fw_size) + { + us_write_length = MAX_WRITE_PACKET_SIZE; + } else + { + us_write_length = (unsigned short)(ul_fw_size - ul_write_offset); + } + + ret = raydium_i2c_pda_write(client, (ul_fw_addr + ul_write_offset), (firmware_data + ul_write_offset), us_write_length); + if (ret < 0) + { + goto exit_upgrate; + } + ul_write_offset += (unsigned long)us_write_length; + } + ul_fw_addr += ul_write_offset; + +exit_upgrate: + kfree(firmware_data); + if (ret < 0) + { + pr_err("[touch]upgrade firmware Failed\n"); + return ret; + } else + { + pr_err("[touch]upgrade firmware Successfully\n"); + return 0; + } +} + +static int raydium_do_software_reset(struct i2c_client * client) +{ + int ret = SUCCESS; + unsigned int retry, retry_time = 1000; + unsigned int irq_value; + unsigned char buf[4]; + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + //#enable INT + g_uc_raydium_flag = RAYDIUM_BOOTLOADER_FLAG; + pr_debug("[touch]ready to software reset => enable INT\n"); + raydium_irq_control(ts, ENABLE); + + //SW reset + memset(buf, 0, sizeof(buf)); + buf[0]=0x01; + pr_debug("[touch]Raydium SW reset\n"); + ret = raydium_i2c_pda_write(client, 0x40000004, buf, 4); + if (ret < 0) + { + goto exit; + } + retry = retry_time; + while (retry != 0 && g_uc_raydium_flag != RAYDIUM_INTERRUPT_FLAG) + { + msleep(2); + retry--; + pr_debug("[touch]wait software reset finish => confirm interrupt_flag result %d\n", retry); + } + + //#disable INT + if (retry == 0 && g_uc_raydium_flag != RAYDIUM_INTERRUPT_FLAG) + { + pr_debug("[touch]no interrupt => disable INT\n"); + raydium_irq_control(ts, DISABLE); + g_uc_raydium_flag = 0; + } + + if (g_uc_raydium_flag == RAYDIUM_INTERRUPT_FLAG) + { + retry = retry_time; + irq_value = 0; + while (retry != 0 && irq_value != 1) + { + irq_value = gpio_get_value(ts->pdata->irq_gpio); + pr_debug("[touch]irq_value is %d\n", irq_value); + msleep(2); + retry--; + } + } + + if (retry == 0) + { + pr_err("[touch] FW not ready, retry error!\n"); + ret = ERROR; + } + +exit: + return ret; +} + +static int raydium_wait_ft_int(struct i2c_client * client) +{ + int ret = SUCCESS; + unsigned int retry, retry_time = 1000; + unsigned int irq_value; + + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + //#enable INT + g_uc_raydium_flag = RAYDIUM_BOOTLOADER_FLAG; + + retry = retry_time; + while (retry != 0 && g_uc_raydium_flag != RAYDIUM_INTERRUPT_FLAG) + { + msleep(2); + retry--; + } + + //#disable INT + if (retry == 0 && g_uc_raydium_flag != RAYDIUM_INTERRUPT_FLAG) + { + g_uc_raydium_flag = 0; + } + + if (g_uc_raydium_flag == RAYDIUM_INTERRUPT_FLAG) + { + retry = retry_time; + irq_value = 0; + while (retry != 0 && irq_value != 1) + { + irq_value = gpio_get_value(ts->pdata->irq_gpio); + pr_debug("[touch]irq_value is %d\n", irq_value); + msleep(2); + retry--; + } + } + + if (retry == 0) + { + pr_err("[touch] FT Test not ready, retry error!\n"); + ret = ERROR; + } else + { + raydium_irq_control(ts, ENABLE); + } + return ret; +} + +static int raydium_check_fw_ready(struct i2c_client * client) +{ + int ret = SUCCESS; + unsigned int retry, retry_time = 400; + unsigned char buf[4]; + + buf[1] = 0; + retry = retry_time; + while (buf[1] != 0x40 && retry != 0) + { + pr_debug("[touch]confirm boot is finish 0x%x\n", buf[1]); + ret = raydium_i2c_pda_read(client, 0x50000918, buf, 4); + if (ret < 0) + { + goto exit; + } + retry --; + msleep(5); + } + if (retry == 0) + { + pr_err("[touch]Error, confirm boot is finish 0x%x\n", buf[1]); + ret = -1; + goto exit; + } + + if (retry == 0) + { + pr_err("[touch] FW not ready, retry error!\n"); + ret = ERROR; + } else + { + pr_debug("[touch] FW is ready!!\n"); + msleep(5); + } + +exit: + return ret; +} + +static int set_skip_load(struct i2c_client * client) +{ + int ret = SUCCESS; + unsigned char buf[4]; + + /*Return to Bootloader in PRAM*/ + //Skip load + memset(buf, 0, sizeof(buf)); + buf[0]=0x10; + buf[1]=0x08; + pr_info("[touch]Raydium skip load\n"); + ret = raydium_i2c_pda_write(client, 0x50000918, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + ret = raydium_do_software_reset(client); + +exit_upgrade: + return ret; +} + +static int raydium_load_test_fw(struct i2c_client * client) +{ + int ret = SUCCESS; + unsigned char buf[4]; + unsigned int retry, retry_time = 200; + unsigned char is_MCU_HOLD; + unsigned int irq_value; + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + ret = raydium_i2c_pda_read(client, 0x50000918, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + is_MCU_HOLD = buf[1] & 0x20; + pr_debug("[touch]is_MCU_HOLD = %d\n", is_MCU_HOLD); + + //#enable INT + g_uc_raydium_flag = RAYDIUM_BOOTLOADER_FLAG; + pr_debug("[touch]ready to software reset => enable INT\n"); + raydium_irq_control(ts, ENABLE); + + //Skip load + memset(buf, 0, sizeof(buf)); + buf[0]=0x10; + buf[1]=0x08; + pr_debug("[touch]Raydium skip load\n"); + ret = raydium_i2c_pda_write(client, 0x50000918, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + memset(buf, 0, sizeof(buf)); + buf[0]=0x01; + ret = raydium_i2c_pda_write(client, 0x40000004, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + if (is_MCU_HOLD == 0) + { + retry = retry_time; + while (retry != 0 && g_uc_raydium_flag != RAYDIUM_INTERRUPT_FLAG) + { + msleep(10); + retry--; + pr_debug("[touch]wait software reset finish => confirm interrupt_flag result %d\n", retry); + } + + //#disable INT + if (retry == 0 && g_uc_raydium_flag != RAYDIUM_INTERRUPT_FLAG) + { + pr_debug("[touch]no interrupt => disable INT\n"); + raydium_irq_control(ts, DISABLE); + g_uc_raydium_flag = 0; + } + + if (g_uc_raydium_flag == RAYDIUM_INTERRUPT_FLAG) + { + retry = retry_time; + irq_value = 0; + while (retry!=0 && irq_value!=1) + { + irq_value = gpio_get_value(ts->pdata->irq_gpio); + pr_debug("[touch]irq_value is %d\n", irq_value); + msleep(10); + retry--; + } + } + + if (retry == 0) + { + pr_err("[touch] FW not ready, retry error!\n"); + ret = ERROR; + goto exit_upgrade; + } + } else + { + msleep(100); + raydium_irq_control(ts, DISABLE); + } + + //set mcu hold + raydium_i2c_pda_read(client, 0x50000918, buf, 4); + buf[0] |= 0x20; + raydium_i2c_pda_write(client, 0x50000918, buf, 4); + raydium_i2c_pda_read(client, 0x40000004, buf, 4); + buf[0] |= 0x01; + raydium_i2c_pda_write(client, 0x40000004, buf, 4); + + /*WRT boot-loader to PRAM first*/ + memset(buf, 0, sizeof(buf)); + printk("[touch]Raydium WRT boot-loader to PRAM first\n"); + ret = raydium_i2c_pda_write(client, 0x50000900, buf, 4); + + /*Sending bootloader*/ + ret = raydium_fw_upgrade_with_image(client, 0x0000, RAYDIUM_BOOTLOADER); + if (ret < 0) + { + goto exit_upgrade; + } + + //release mcu hold + raydium_i2c_pda_read(client, 0x50000918, buf, 4); + buf[0] &= ~0x20; + buf[0] |= 0x10; + raydium_i2c_pda_write(client, 0x50000918, buf, 4); + raydium_i2c_pda_read(client, 0x40000004, buf, 4); + buf[0] |= 0x01; + raydium_i2c_pda_write(client, 0x40000004, buf, 4); + msleep(50); + + ret = raydium_i2c_pda_read(client, 0x40000000, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + buf[3] |= 0x40; + ret = raydium_i2c_pda_write(client, 0x40000000, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + ret = raydium_i2c_pda_read(client, 0x40000014, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + buf[0] |= 0x04; + buf[1] |= 0x04; + ret = raydium_i2c_pda_write(client, 0x40000014, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + + memset(buf, 0, sizeof(buf)); + pr_debug("[touch]Raydium WRT test_fw to PRAM\n"); + + ret = raydium_i2c_pda_write(client, 0x50000900, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + /*Sending test fw*/ + ret = raydium_fw_upgrade_with_image(client, 0xA00, RAYDIUM_TEST_FW); + if (ret < 0) + { + goto exit_upgrade; + } + //Skip load + memset(buf, 0, sizeof(buf)); + buf[0]=0x10; + pr_debug("[touch]Raydium skip load\n"); + ret = raydium_i2c_pda_write(client, 0x50000918, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + ret = raydium_do_software_reset(client); + if (ret < 0) + { + goto exit_upgrade; + } + ret = raydium_check_fw_ready(client); + +exit_upgrade: + return ret; +} + +static int raydium_boot_upgrade(struct i2c_client * client) +{ + int ret = SUCCESS; + unsigned char buf[4]; + unsigned int retry, retry_time = 200; + unsigned char is_MCU_HOLD; + unsigned int irq_value; + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + ret = raydium_i2c_pda_read(client, 0x50000918, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + is_MCU_HOLD = buf[1] & 0x20; + pr_debug("[touch]is_MCU_HOLD = %d\n", is_MCU_HOLD); + + //#enable INT + g_uc_raydium_flag = RAYDIUM_BOOTLOADER_FLAG; + pr_debug("[touch]ready to software reset => enable INT\n"); + raydium_irq_control(ts, ENABLE); + + /* Return to Bootloader */ + memset(buf, 0, sizeof(buf)); + buf[0] = 0x00; + buf[1] = 0x08; + raydium_i2c_pda_write(client, 0x50000918, buf, 4); + memset(buf, 0, sizeof(buf)); + buf[0] |= 0x01; + raydium_i2c_pda_write(client, 0x40000004, buf, 4); + + if (is_MCU_HOLD == 0) + { + retry = retry_time; + while (retry != 0 && g_uc_raydium_flag != RAYDIUM_INTERRUPT_FLAG) + { + msleep(10); + retry--; + pr_debug("[touch]wait software reset finish => confirm interrupt_flag result %d\n", retry); + } + + //#disable INT + if (retry == 0 && g_uc_raydium_flag != RAYDIUM_INTERRUPT_FLAG) + { + pr_debug("[touch]no interrupt => disable INT\n"); + raydium_irq_control(ts, DISABLE); + g_uc_raydium_flag = 0; + } + + if (g_uc_raydium_flag == RAYDIUM_INTERRUPT_FLAG) + { + retry = retry_time; + irq_value = 0; + while (retry!=0 && irq_value!=1) + { + irq_value = gpio_get_value(ts->pdata->irq_gpio); + pr_debug("[touch]irq_value is %d\n", irq_value); + msleep(10); + retry--; + } + } + + if (retry == 0) + { + pr_err("[touch] FW not ready, retry error!\n"); + ret = ERROR; + goto exit_upgrade; + } + } else + { + raydium_irq_control(ts, DISABLE); + msleep(100); + } + + //set mcu hold + raydium_i2c_pda_read(client, 0x50000918, buf, 4); + buf[0] |= 0x20; + raydium_i2c_pda_write(client, 0x50000918, buf, 4); + raydium_i2c_pda_read(client, 0x40000004, buf, 4); + buf[0] |= 0x01; + raydium_i2c_pda_write(client, 0x40000004, buf, 4); + + /*WRT boot-loader to PRAM first*/ + memset(buf, 0, sizeof(buf)); + pr_debug("[touch]WRT boot-loader to PRAM first\n"); + ret = raydium_i2c_pda_write(client, 0x50000900, buf, 4); + + /*Sending bootloader*/ + ret = raydium_fw_upgrade_with_image(client, 0x0000, RAYDIUM_BOOTLOADER); + if (ret < 0) + { + goto exit_upgrade; + } + + //release mcu hold + raydium_i2c_pda_read(client, 0x50000918, buf, 4); + buf[0] &= ~0x20; + buf[0] |= 0x10; + raydium_i2c_pda_write(client, 0x50000918, buf, 4); + raydium_i2c_pda_read(client, 0x40000004, buf, 4); + buf[0] |= 0x01; + raydium_i2c_pda_write(client, 0x40000004, buf, 4); + msleep(50); + + ret = raydium_i2c_pda_read(client, 0x40000000, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + buf[3] |= 0x40; + ret = raydium_i2c_pda_write(client, 0x40000000, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + ret = raydium_i2c_pda_read(client, 0x40000014, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + buf[0] |= 0x04; + buf[1] |= 0x04; + ret = raydium_i2c_pda_write(client, 0x40000014, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + //Skip load + memset(buf, 0, sizeof(buf)); + buf[0]=0x10; + buf[1]=0x08; + printk("Raydium skip load\n"); + ret = raydium_i2c_pda_write(client, 0x50000918, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + ret = raydium_do_software_reset(client); + +exit_upgrade: + return ret; +} + +/* Raydium fireware upgrade flow */ +static int raydium_fw_upgrade(struct i2c_client * client, unsigned char type, unsigned char isSkipLoad, unsigned char isCheckCRCError) +{ + int ret = 0; + unsigned char buf[4]; + unsigned int retry, retry_time = 200; + unsigned int irq_value; + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + if (!gpio_is_valid(ts->rst)) + return -ENOSYS; + + //##### wait for boot-loader start ##### + pr_debug("[touch]type is %d\n", type); + //#set main state as burning mode, normal init state + //#sync_data:200h, main_state:204h, normal_state:208h, burning_state:20Ch + //#sync_data:210h, cmd_type:210h; ret_data:214h; test_mode:218h + memset(buf, 0, sizeof(buf)); + buf[0]=0x01; + ret = raydium_i2c_pda_write(client, 0x20000204, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + memset(buf, 0, sizeof(buf)); + ret = raydium_i2c_pda_write(client, 0x20000208, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + memset(buf, 0, sizeof(buf)); + ret = raydium_i2c_pda_write(client, 0x2000020C, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + memset(buf, 0, sizeof(buf)); + buf[0]=0x01; + ret = raydium_i2c_pda_write(client, 0x20000218, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + //#confirm in burn mode + buf[0] = 0; + retry = retry_time; + while (buf[0] != 63 && retry != 0) + { + pr_debug("[touch]confirm in burn mode 0x%x\n", buf[0]); + ret = raydium_i2c_pda_read(client, 0x50000900, buf, 1); + if (ret < 0) + { + goto exit_upgrade; + } + retry --; + msleep(10); + } + if (retry == 0) + { + pr_err("[touch]Error, confirm in burn mode 0x%x\n", buf[0]); + ret = -1; + goto exit_upgrade; + } + + pr_debug("[touch]VVVVVVVVVVVVVVV Type : %d ==> Start VVVVVVVVVVVVVVV\n", type); + + //Clear BL_CRC + memset(buf, 0, sizeof(buf)); + buf[0]=0x10; + ret = raydium_i2c_pda_write(client, 0x50000918, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + + //#write PRAM relative data + //#set PRAM type (at 0x50000904), wrt param code + //#init_code:0x01, baseline:0x02, COMP:0x04, param:0x08, FW:0x10, bootloader:0x20 + memset(buf, 0, sizeof(buf)); + buf[0]=type; + ret = raydium_i2c_pda_write(client, 0x50000904, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + //#set PRAM addr (at 'h5000_0908) + //#init_code:0x800, Baseline:0xA00, COMP:0xCD4, para:0xF1C, FW:0x1000, BOOT:0x5000 + //#set PRAM length (at 'h5000_090C) + if (type == RAYDIUM_INIT) + { + pr_debug("[touch]Set PRAM addr & length type=0x01\n"); + memset(buf, 0, sizeof(buf)); + buf[0]=0x00; + buf[1]=0x08; + ret = raydium_i2c_pda_write(client, 0x50000908, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + memset(buf, 0, sizeof(buf)); + buf[0]=0x00; + buf[1]=0x02; + ret = raydium_i2c_pda_write(client, 0x5000090C, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + } else if (type == RAYDIUM_BASELINE) + { + pr_debug("[touch]Set PRAM addr & length type=0x02\n"); + memset(buf, 0, sizeof(buf)); + buf[0]=0x04; + buf[1]=0x0a; + ret = raydium_i2c_pda_write(client, 0x50000908, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + memset(buf, 0, sizeof(buf)); + buf[0]=0xd0; + buf[1]=0x02; + ret = raydium_i2c_pda_write(client, 0x5000090C, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + } else if (type == RAYDIUM_COMP) + { + pr_debug("[touch]Set PRAM addr & length type=0x04\n"); + memset(buf, 0, sizeof(buf)); + buf[0]=0xd4; + buf[1]=0x0c; + ret = raydium_i2c_pda_write(client, 0x50000908, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + memset(buf, 0, sizeof(buf)); + buf[0]=0x48; + buf[1]=0x02; + ret = raydium_i2c_pda_write(client, 0x5000090C, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + } else if (type == RAYDIUM_PARA) + { + pr_debug("[touch]Set PRAM addr & length type=0x08\n"); + memset(buf, 0, sizeof(buf)); + buf[0]=0x1c; + buf[1]=0x0f; + ret = raydium_i2c_pda_write(client, 0x50000908, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + memset(buf, 0, sizeof(buf)); + buf[0]=0xe4; + ret = raydium_i2c_pda_write(client, 0x5000090C, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + } else if (type == RAYDIUM_FIRMWARE) + { + pr_debug("[touch]Set PRAM addr & length type=0x10\n"); + memset(buf, 0, sizeof(buf)); + buf[0]=0x1C; + buf[1]=0x0F; + ret = raydium_i2c_pda_write(client, 0x50000908, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + memset(buf, 0, sizeof(buf)); + buf[0]=0xE4; + buf[1]=0x50; + ret = raydium_i2c_pda_write(client, 0x5000090C, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + } else if (type == RAYDIUM_BOOTLOADER) + { + pr_debug("[touch]Set PRAM addr & length type=0x20\n"); + memset(buf, 0, sizeof(buf)); + buf[0]=0x00; + buf[1]=0x40; + ret = raydium_i2c_pda_write(client, 0x50000908, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + memset(buf, 0, sizeof(buf)); + buf[0]=0x00; + buf[1]=0x0A; + ret = raydium_i2c_pda_write(client, 0x5000090C, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + } + + //#set sync_data(0x20000200) = 0 as WRT data finish + memset(buf, 0, sizeof(buf)); + ret = raydium_i2c_pda_write(client, 0x20000200, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + //#Wait bootloader check addr and PRAM unlock + //#Confirm g_u8_sync_data.ret_data at 0x20000214 is SET_ADDR_READY + buf[0] = 0; + retry = retry_time; + while (buf[0] != 161 && retry != 0) + { + msleep(10); + ret = raydium_i2c_pda_read(client, 0x20000214, buf, 1); + if (ret < 0) + { + goto exit_upgrade; + } + retry --; + pr_debug("[touch]Confirm g_u8_sync_data.ret_data is SET_ADDR_READY %d\n", buf[0]); + } + if (retry == 0) + { + pr_err("[touch]Error, Confirm g_u8_sync_data.ret_data is SET_ADDR_READY %d\n", buf[0]); + ret = -1; + goto exit_upgrade; + } + + //#Confirm g_u8_sync_data.cmd_type at 0x20000210 is WRT_PRAM_DATA + buf[0] = 0; + retry = retry_time; + while (buf[0] != 163 && retry != 0) + { + msleep(10); + ret = raydium_i2c_pda_read(client, 0x20000210, buf, 1); + if (ret < 0) + { + goto exit_upgrade; + } + retry --; + pr_debug("[touch]Confirm g_u8_sync_data.cmd_type is WRT_PRAM_DATA %d\n", buf[0]); + } + if (retry == 0) + { + pr_err("[touch]Error, Confirm g_u8_sync_data.cmd_type is WRT_PRAM_DATA %d\n", buf[0]); + ret = -1; + goto exit_upgrade; + } + + //#start write data to PRAM + if (type == RAYDIUM_INIT) + { + ret = raydium_fw_upgrade_with_image(client, 0x800, RAYDIUM_INIT); + if (ret < 0) + { + goto exit_upgrade; + } + } else if (type == RAYDIUM_PARA) + { + ret = raydium_fw_upgrade_with_image(client, 0xF1C, RAYDIUM_PARA); + if (ret < 0) + { + goto exit_upgrade; + } + } else if (type == RAYDIUM_FIRMWARE) + { + ret = raydium_fw_upgrade_with_image(client, 0xF1C, RAYDIUM_PARA); + if (ret < 0) + { + goto exit_upgrade; + } + ret = raydium_fw_upgrade_with_image(client, 0x1000, RAYDIUM_FIRMWARE); + if (ret < 0) + { + goto exit_upgrade; + } + } else if (type == RAYDIUM_BOOTLOADER) + { + ret = raydium_fw_upgrade_with_image(client, 0x4000, RAYDIUM_BOOTLOADER); + if (ret < 0) + { + goto exit_upgrade; + } + } + + //#set sync_data(0x20000200) = 0 as WRT data finish, bootloader check checksum + memset(buf, 0, sizeof(buf)); + ret = raydium_i2c_pda_write(client, 0x20000200, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + //#wait(checksum okay) ACK cmd(gu8I2CSyncData.cmd_type=0xa5 at 0x20000210) + buf[0] = 0; + retry = retry_time; + while (buf[0] != 165 && retry != 0) + { + msleep(10); + ret = raydium_i2c_pda_read(client, 0x20000210, buf, 1); + if (ret < 0) + { + goto exit_upgrade; + } + retry --; + pr_debug("[touch]wait(checksum okay) ACK cmd 0x%x\n", buf[0]); + } + if (retry == 0) + { + pr_err("[touch]Error, wait(checksum okay) ACK cmd 0x%x\n", buf[0]); + ret = -1; + goto exit_upgrade; + } + + //#confirm ACK cmd result(gu8I2CSyncData.ret_data=0xa5 at 0x20000214) + buf[0] = 0; + retry = retry_time; + while (buf[0] != 165 && retry != 0) + { + msleep(10); + ret = raydium_i2c_pda_read(client, 0x20000214, buf, 1); + if (ret < 0) + { + goto exit_upgrade; + } + retry --; + pr_debug("[touch]confirm ACK cmd result 0x%x\n", buf[0]); + } + if (retry == 0) + { + pr_err("[touch]Error, confirm ACK cmd result 0x%x\n", buf[0]); + ret = -1; + goto exit_upgrade; + } + + //#set ACK return data = 0x5A + //#adb shell "echo 20000210 1 A5 > /sys/bus/i2c/drivers/raydium_ts/1-0039/raydium_i2c_pda_access" + //#above command can be ignored, due to previous while loop has check its value. + memset(buf, 0, sizeof(buf)); + buf[0] = 0x5a; + ret = raydium_i2c_pda_write(client, 0x20000214, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + //#clr sync_data(0x20000200) = 0 as finish + memset(buf, 0, sizeof(buf)); + ret = raydium_i2c_pda_write(client, 0x20000200, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + //#wait for input unlock key + buf[0] = 0; + retry = retry_time; + while (buf[0] != 168 && retry != 0) + { + msleep(10); + ret = raydium_i2c_pda_read(client, 0x20000210, buf, 1); + if (ret < 0) + { + goto exit_upgrade; + } + retry --; + pr_debug("[touch]wait for input unlock key 0x%x\n", buf[0]); + } + if (retry == 0) + { + pr_err("[touch]Error, wait for input unlock key 0x%x\n", buf[0]); + ret = -1; + goto exit_upgrade; + } + + //#unlock key + memset(buf, 0, sizeof(buf)); + buf[0] = 0xd7; + ret = raydium_i2c_pda_write(client, 0x50000938, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + memset(buf, 0, sizeof(buf)); + buf[0] = 0xa5; + ret = raydium_i2c_pda_write(client, 0x50000934, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + memset(buf, 0, sizeof(buf)); + ret = raydium_i2c_pda_write(client, 0x50000934, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + memset(buf, 0, sizeof(buf)); + buf[0] = 0xa5; + ret = raydium_i2c_pda_write(client, 0x50000934, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + memset(buf, 0, sizeof(buf)); + ret = raydium_i2c_pda_write(client, 0x50000938, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + //#wrt return data as unlock value + memset(buf, 0, sizeof(buf)); + buf[0] = 0xa8; + ret = raydium_i2c_pda_write(client, 0x20000214, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + //#clr sync_data(0x20000200) = 0 as finish + memset(buf, 0, sizeof(buf)); + ret = raydium_i2c_pda_write(client, 0x20000200, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + msleep(500); + + //#wait erase/wrt finish + //#confirm burning_state result(gu8I2CSyncData.burning_state=BURNING_WRT_FLASH_FINISH at 0x2000020C) + buf[0] = 0; + retry = retry_time; + while (buf[0] != 6 && retry != 0) + { + msleep(10); + ret = raydium_i2c_pda_read(client, 0x2000020c, buf, 1); + if (ret < 0) + { + goto exit_upgrade; + } + retry --; + pr_debug("[touch]confirm burning_state result %d\n", buf[0]); + } + if (retry == 0) + { + pr_err("[touch]Error, confirm burning_state result %d\n", buf[0]); + ret = -1; + goto exit_upgrade; + } + + //############## Use Interrupt to wait sofware reset ############## + //#clr sync_data(0x20000200) = 0 as finish + //#Set bootloader flag + //#RAYDIUM_INTERRUPT_FLAG = 0x01 + //#RAYDIUM_BOOTLOADER_FLAG = 0x02 + g_uc_raydium_flag = RAYDIUM_BOOTLOADER_FLAG; + //#enable INT + pr_debug("[touch]ready to software reset => enable INT\n"); + raydium_irq_control(ts, ENABLE); + + if (type == RAYDIUM_BOOTLOADER) + { + ret = raydium_i2c_pda_read(client, 0x50000918, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + buf[0] |= 0x10; + ret = raydium_i2c_pda_write(client, 0x50000918, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + } + + memset(buf, 0, sizeof(buf)); + ret = raydium_i2c_pda_write(client, 0x20000200, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + + //#wait software reset finish + //#confirm RAYDIUM_INTERRUPT_FLAG result (if raydium_flag = 1 => pass) + retry = retry_time; + while (retry != 0 && g_uc_raydium_flag != RAYDIUM_INTERRUPT_FLAG) + { + msleep(10); + retry--; + pr_debug("[touch]wait software reset finish => confirm interrupt_flag result %d\n", g_uc_raydium_flag); + } + + //#disable INT + if (retry == 0 && g_uc_raydium_flag != RAYDIUM_INTERRUPT_FLAG) + { + pr_debug("[touch]no interrupt => disable INT\n"); + raydium_irq_control(ts, DISABLE); + g_uc_raydium_flag = 0; + } + + if (g_uc_raydium_flag == RAYDIUM_INTERRUPT_FLAG) + { + retry = retry_time; + irq_value = 0; + while (retry != 0 && irq_value != 1) + { + irq_value = gpio_get_value(ts->pdata->irq_gpio); + pr_debug("[touch]irq_value is %d\n", irq_value); + msleep(10); + retry--; + } + } + + if (retry == 0) + { + pr_err("[touch] FW not ready, retry error!\n"); + ret = ERROR; + goto exit_upgrade; + } + //################################################################# + + buf[0] = 0; + retry = retry_time; + while (buf[0] != 130 && retry != 0) + { + msleep(10); + ret = raydium_i2c_pda_read(client, 0x20000214, buf, 1); + if (ret < 0) + { + goto exit_upgrade; + } + retry --; + pr_debug("[touch]wait software reset finished %d\n", buf[0]); + } + if (retry == 0) + { + pr_err("[touch]Error, wait software reset finished %d\n", buf[0]); + ret = -1; + goto exit_upgrade; + } + + if (type == RAYDIUM_BASELINE || type == RAYDIUM_INIT || isCheckCRCError == 1) + { + //#set test_mode = 1 + memset(buf, 0, sizeof(buf)); + buf[0] = 0x01; + ret = raydium_i2c_pda_write(client, 0x20000218, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + //#wait crc check finish + //#confirm gu8I2CSyncData.normal_state=NORMAL_CRC_NOTIFY at 0x20000208 + buf[0] = 0; + retry = retry_time; + while (buf[0] != 2 && retry != 0) + { + msleep(10); + ret = raydium_i2c_pda_read(client, 0x20000208, buf, 1); + if (ret < 0) + { + goto exit_upgrade; + } + retry --; + pr_debug("[touch]confirm gu8I2CSyncData.normal_state=NORMAL_CRC_NOTIFY %d\n", buf[0]); + } + if (retry == 0) + { + pr_err("[touch]Error, confirm gu8I2CSyncData.normal_state=NORMAL_CRC_NOTIFY %d\n", buf[0]); + ret = -1; + goto exit_upgrade; + } + + //#crc check pass 0x20000214 = 0x81 + buf[0] = 0; + retry = retry_time; + while (buf[0] != 0x81 && retry != 0) + { + msleep(10); + ret = raydium_i2c_pda_read(client, 0x20000214, buf, 1); + if (ret < 0) + { + goto exit_upgrade; + } + retry --; + pr_debug("[touch]confirm crc result 0x20000214 = %d\n", buf[0]); + } + if (retry == 0) + { + pr_err("[touch]Error, confirm crc result 0x20000214 = %d\n", buf[0]); + ret = -1; + goto exit_upgrade; + } + } + + //#run to next step + pr_debug("[touch]^^^^^^^^^^^^^^^ Type: 0x%x ==> Pass ^^^^^^^^^^^^^^^\n", type); + + if (isCheckCRCError) + { + buf[0] = 0; + retry = retry_time; + while (buf[0] != 2 && retry != 0) + { + ret = raydium_i2c_pda_read(client, 0x50000918, buf, 1); + if (ret < 0) + { + goto exit_upgrade; + } + msleep(10); + buf[0] = buf[0] & 47; + retry --; + pr_debug("[touch]BL1_CRC_ERR %d\n", buf[0]); + } + if (retry == 0) + { + pr_err("[touch]Error, BL1_CRC_ERR %d\n", buf[0]); + ret = -1; + goto exit_upgrade; + } + + //#clr sync para + memset(buf, 0, sizeof(buf)); + ret = raydium_i2c_pda_write(client, 0x20000210, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + ret = raydium_i2c_pda_write(client, 0x20000214, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + ret = raydium_i2c_pda_write(client, 0x20000218, buf, 4); + if (ret < 0) + { + goto exit_upgrade; + } + + pr_debug("[touch]Raydium HW reset\n"); + gpio_direction_output(ts->rst, 1); + gpio_direction_output(ts->rst, 0); + msleep(RAYDIUM_RESET_INTERVAL_MSEC); + gpio_direction_output(ts->rst, 1); + msleep(500); + + u8_i2c_mode = PDA2_MODE; + + pr_debug("[touch]Burn FW finish!\n"); + } + +exit_upgrade: + return ret; +} + + +static int raydium_burn_fw(struct i2c_client *client) +{ + int ret = 0; + + ret = raydium_boot_upgrade(client); + + if (ret < 0) + { + goto exit_upgrade; + } + ret = raydium_fw_upgrade(client, RAYDIUM_BOOTLOADER, 0, 0); + if (ret < 0) + { + goto exit_upgrade; + } + ret = raydium_fw_upgrade(client, RAYDIUM_FIRMWARE, 0, 1); + if (ret < 0) + { + goto exit_upgrade; + } +exit_upgrade: + return ret; +} + +static ssize_t raydium_fw_upgrade_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + + /* receive command line arguments string */ + if (count > 2) + { + return -EINVAL; + } + ret = kstrtou8(buf, 16, &upgrade_type); + if (ret < 0) + { + return ret; + } + return count; +} + +static ssize_t raydium_fw_upgrade_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret = 0, result = 0; + int buf_len = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + raydium_irq_control(ts, DISABLE); + pr_debug("[touch]Raydium burn type is %d!!\n", upgrade_type); + + if (upgrade_type == 1) + { + ret = raydium_burn_fw(client); + if (ret < 0) + { + result = FAIL; + goto exit_upgrade; + } + result = SUCCESS; + } else if (upgrade_type == 2) + { + ret = set_skip_load(client); + if (ret < 0) + { + result = FAIL; + goto exit_upgrade; + } + ret = raydium_fw_upgrade(client, RAYDIUM_COMP, 0, 1); + if (ret < 0) + { + result = FAIL; + goto exit_upgrade; + } + result = SUCCESS; + } else if (upgrade_type == 3) + { + ret = set_skip_load(client); + if (ret < 0) + { + result = FAIL; + goto exit_upgrade; + } + ret = raydium_fw_upgrade(client, RAYDIUM_BASELINE, 0, 1); + if (ret < 0) + { + result = FAIL; + goto exit_upgrade; + } + result = SUCCESS; + } else if (upgrade_type == 4) + { + ret = raydium_load_test_fw(client); + if (ret < 0) + { + result = FAIL; + goto exit_upgrade; + } + result = SUCCESS; + } + +exit_upgrade: + raydium_irq_control(ts, ENABLE); + upgrade_type = 0; + sprintf(buf, "FW Upgrade result : %d\n", result); + buf_len = strlen(buf); + return buf_len+1; +} + + +/* upgrade firmware with *.bin file */ +static int raydium_fw_upgrade_with_bin_file(struct i2c_client *client, char *arguments, size_t count) +{ + int ret = 0; + char *temp_buf, *token, *free_temp_buf, *free_token; + const char *delim = " ,"; + unsigned long ul_fw_offset = 0; + unsigned char uc_fw_amount = 0; + mm_segment_t old_fs; + struct file *fw_fd = NULL; + unsigned long ul_fw_size = 0, ul_fw_read_size = 0; + char *firmware_data; + unsigned long ul_write_offset = 0; + unsigned short us_write_length = 0; + unsigned int ui_checksum = 0xFFFFFFFF; + + /* receive command line arguments string */ + if (count < 2) + { + return -EINVAL; + } + firmware_data = kzalloc(RAYDIUM_FW_MAX_SIZE, GFP_KERNEL); + if (firmware_data == NULL) + { + pr_err("[touch]kzalloc firmware_data failed\n"); + return -ENOMEM; + } + + temp_buf = kzalloc(RAYDIUM_FW_BIN_PATH_LENGTH+1, GFP_KERNEL); + if (temp_buf == NULL) + { + pr_err("[touch]kzalloc temp_buf failed\n"); + kfree(firmware_data); + return -ENOMEM; + } + + token = kzalloc(RAYDIUM_FW_BIN_PATH_LENGTH+1, GFP_KERNEL); + if (token == NULL ) + { + pr_err("[touch]kzalloc token failed\n"); + kfree(firmware_data); + kfree(temp_buf); + return -ENOMEM; + } + + free_temp_buf = temp_buf; + free_token = token; + strncpy(temp_buf, arguments, count); + token = strsep(&temp_buf, delim); + + ret = kstrtoul(token, 16, &ul_fw_offset); + if (ret < 0) + { + goto exit_upgrate; + } + + token = strsep(&temp_buf, delim); + ret = kstrtou8(token, 16, &uc_fw_amount); + if (ret < 0) + { + goto exit_upgrate; + } + + while (uc_fw_amount--) + { + if (!temp_buf) + { + break; + } + + token = strsep(&temp_buf, delim); + ret = kstrtoul(token, 16, &ul_fw_size); + if (ret < 0) + { + goto exit_upgrate; + } + + token = strsep(&temp_buf, delim); + + old_fs = get_fs(); + set_fs(get_ds()); + + fw_fd = filp_open(token, O_RDONLY, 0); + if (IS_ERR(fw_fd)) + { + pr_err("[touch]error occured while opening file %s.\n", token); + set_fs(old_fs); + kfree(firmware_data); + kfree(free_token); + kfree(free_temp_buf); + return -ENOENT; + } + + memset(firmware_data, 0, ul_fw_size); + ul_fw_read_size = fw_fd->f_op->read(fw_fd, firmware_data, ul_fw_size, &fw_fd->f_pos); + ui_checksum = rc_crc32(firmware_data, ul_fw_size, ui_checksum); + + if (uc_fw_amount == 0) + { + ui_checksum = bits_reverse(ui_checksum, 32); + memcpy((firmware_data + ul_fw_size), &ui_checksum, 4); + ul_fw_size += 4; + } + + set_fs(old_fs); + filp_close(fw_fd,NULL); + + ul_write_offset = 0; + while (ul_write_offset < ul_fw_size) + { + if ((ul_write_offset + MAX_WRITE_PACKET_SIZE) < ul_fw_size) + { + us_write_length = MAX_WRITE_PACKET_SIZE; + } + else + { + us_write_length = (unsigned short)(ul_fw_size - ul_write_offset); + } + ret = raydium_i2c_pda_write(client, (ul_fw_offset + ul_write_offset), (firmware_data + ul_write_offset), us_write_length); + if (ret < 0) + { + goto exit_upgrate; + } + ul_write_offset += (unsigned long)us_write_length; + } + ul_fw_offset += ul_write_offset; + } + +exit_upgrate: + kfree(firmware_data); + kfree(free_token); + kfree(free_temp_buf); + if (ret < 0) + { + pr_err("[touch]Upgrage firmware Failed\n"); + return ret; + } else { + pr_err("[touch]Upgrage firmware Successfully\n"); + return 0; + } +} + +static ssize_t raydium_upgrade_firmware_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + char fwname[RAYDIUM_FW_BIN_PATH_LENGTH]; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + if (count > RAYDIUM_FW_BIN_PATH_LENGTH) + { + return -EINVAL; + } + memset(fwname, 0, sizeof(fwname)); + sprintf(fwname, "%s", buf); + fwname[count - 1] = '\0'; + + if (ts->is_suspend) + { + pr_debug("[touch]Raydium IC is_suspend\n"); + } + + /* start to upgrade binary file*/ + mutex_lock(&ts->lock); + ret = raydium_fw_upgrade_with_bin_file(client, fwname, count); + mutex_unlock(&ts->lock); + if (ret < 0) + { + return ret; + } + return count; +} + +static ssize_t raydium_i2c_pda2_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + unsigned char mode; + + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + if (ts->is_suspend) + { + pr_debug("[touch]Raydium IC is_suspend\n"); + } + + /* receive command line arguments string */ + if (count > 2) + { + return -EINVAL; + } + + ret = kstrtou8(buf, 16, &mode); + if (ret < 0) + { + return ret; + } + ret = raydium_i2c_mode_control(client, mode); + if (ret < 0) + { + return ret; + } + + return count; +} + +static ssize_t raydium_i2c_pda_access_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned char rbuffer[4]; + int buf_len = 0; + int ret = -1; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + if (g_ui_length > 4) + { + return -EINVAL; + } + memset(rbuffer, 0x00, 4); + mutex_lock(&ts->lock); + ret = raydium_i2c_pda_read(client, g_ul_addr, rbuffer, g_ui_length); + mutex_unlock(&ts->lock); + if (ret < 0) + { + return ret; + } + + sprintf(buf, "0x%08lX : 0x%02X%02X%02X%02X\n", g_ul_addr, + rbuffer[3], rbuffer[2], rbuffer[1], rbuffer[0]); + buf_len = strlen(buf); + + return buf_len+1; +} + +static ssize_t raydium_i2c_pda_access_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0, result = 0; + char *temp_buf, *token, *free_temp_buf, *free_token; + const char *delim = " ,"; + unsigned char w_data[MAX_WRITE_PACKET_SIZE]; + unsigned int data_count = 0; + unsigned int data_index = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + /* receive command line arguments string */ + if (count < 2) + { + return -EINVAL; + } + + temp_buf = kzalloc(count+1, GFP_KERNEL); + if (temp_buf == NULL) + { + pr_debug("[touch]kzalloc temp_buf failed\n"); + return -ENOMEM; + } + + token = kzalloc(count+1, GFP_KERNEL); + if (token == NULL) + { + pr_debug("[touch]kzalloc token failed\n"); + kfree(temp_buf); + return -ENOMEM; + } + + free_temp_buf = temp_buf; + free_token = token; + + ret = (ssize_t) count; + + strncpy(temp_buf, buf, count); + + token = strsep(&temp_buf, delim); + + result = kstrtoul(token, 16, &g_ul_addr); + + token = strsep(&temp_buf, delim); + if (token) + { + result = kstrtouint(token, 16, &data_count); + } else + { + goto exit_error; + } + if (g_ui_length > MAX_WRITE_PACKET_SIZE) + { + return -EINVAL; + } + g_ui_length = data_count; + + memset(w_data, 0x00, MAX_WRITE_PACKET_SIZE); + + if (temp_buf && data_count) + { + data_index = 0; + while (data_count) + { + token = strsep(&temp_buf, delim); + result = kstrtou8(token, 16, &w_data[data_index++]); + if (result < 0) + { + ret = result; + goto exit_error; + } + data_count--; + } + mutex_lock(&ts->lock); + result = raydium_i2c_pda_write(client, g_ul_addr, w_data, g_ui_length); + mutex_unlock(&ts->lock); + if (result < 0) + { + ret = result; + } + } + +exit_error: + kfree(free_token); + kfree(free_temp_buf); + return ret; +} + +unsigned char g_uc_addr = RAYDIUM_PDA2_PDA_CFG_ADDR; + +static ssize_t raydium_i2c_pda2_access_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned char rbuffer[4]; + int buf_len = 0; + int ret = -1; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + if (g_ui_length > 4) + { + return -EINVAL; + } + memset(rbuffer, 0x00, 4); + + mutex_lock(&ts->lock); + ret = raydium_i2c_pda2_read(client, g_uc_addr, rbuffer, g_ui_length); + mutex_unlock(&ts->lock); + if (ret < 0) + { + return ret; + } + + sprintf(buf, "0x%04X : 0x%02X%02X%02X%02X\n", g_uc_addr, + rbuffer[3], rbuffer[2], rbuffer[1], rbuffer[0]); + buf_len = strlen(buf); + + return buf_len+1; +} + +static ssize_t raydium_i2c_pda2_access_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0, result = 0; + char *temp_buf, *token, *free_temp_buf, *free_token; + const char *delim = " ,"; + unsigned char w_data[MAX_WRITE_PACKET_SIZE]; + unsigned int data_count = 0; + unsigned int data_index = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + /* receive command line arguments string */ + if (count < 2) + { + return -EINVAL; + } + temp_buf = kzalloc(count+1, GFP_KERNEL); + if (temp_buf == NULL ) + { + pr_err("[touch]kzalloc temp_buf failed\n"); + return -ENOMEM; + } + + token = kzalloc(count+1, GFP_KERNEL); + if (token == NULL ) + { + pr_err("[touch]kzalloc token failed\n"); + kfree(temp_buf); + return -ENOMEM; + } + + free_temp_buf = temp_buf; + free_token = token; + + ret = (ssize_t) count; + + strncpy(temp_buf, buf, count); + + token = strsep(&temp_buf, delim); + + result = kstrtou8(token, 16, &g_uc_addr); + + token = strsep(&temp_buf, delim); + if (token) + { + result = kstrtouint(token, 16, &data_count); + } else + { + ret = -EINVAL; + goto exit_error; + } + + if (data_count > MAX_WRITE_PACKET_SIZE) + { + ret = -EINVAL; + goto exit_error; + } + + memset(w_data, 0x00, MAX_WRITE_PACKET_SIZE); + + g_ui_length = data_count; + + if (temp_buf && data_count) + { + data_index = 0; + while (data_count) + { + token = strsep(&temp_buf, delim); + result = kstrtou8(token, 16, &w_data[data_index++]); + if (result < 0) + { + ret = result; + goto exit_error; + } + data_count--; + } + + mutex_lock(&ts->lock); + result = raydium_i2c_pda2_write(client, g_uc_addr, w_data, g_ui_length); + mutex_unlock(&ts->lock); + if (result < 0) + { + ret = result; + goto exit_error; + } + } + +exit_error: + kfree(free_token); + kfree(free_temp_buf); + return ret; +} + +static ssize_t raydium_i2c_pda2_page_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0, result = 0; + unsigned char page = 0; + char *temp_buf, *token, *free_temp_buf, *free_token; + const char *delim = " ,"; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + /* receive command line arguments string */ + if (count < 2) + { + return -EINVAL; + } + temp_buf = kzalloc(count+1, GFP_KERNEL); + if (temp_buf == NULL) + { + pr_err("[touch]kzalloc temp_buf failed\n"); + return -ENOMEM; + } + + token = kzalloc(count+1, GFP_KERNEL); + if (token == NULL) + { + pr_err("[touch]kzalloc token failed\n"); + kfree(temp_buf); + return -ENOMEM; + } + + free_temp_buf = temp_buf; + free_token = token; + + ret = (ssize_t) count; + + strncpy(temp_buf, buf, count); + + token = strsep(&temp_buf, delim); + + if (temp_buf) + { + pr_err("[touch]Raydium input error, extra auguments\n"); + ret = -EINVAL; + goto exit_error; + } + result = kstrtou8(token, 16, &page); + + if (result < 0) + { + ret = result; + goto exit_error; + } + + raydium_irq_control(ts, DISABLE); + mutex_lock(&ts->lock); + + result = raydium_i2c_pda2_set_page(client, page); + if (result < 0) + { + ret = result; + goto exit_set_error; + } +// TODO: Page check, Due to ISR will change page back to Page_0. Or disable IRQ during PDA2 access period + +exit_set_error: + mutex_unlock(&ts->lock); + raydium_irq_control(ts, ENABLE); + +exit_error: + kfree(free_token); + kfree(free_temp_buf); + return ret; +} + +static ssize_t raydium_i2c_raw_data_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned char rbuffer[MAX_READ_PACKET_SIZE]; + unsigned int ui_read_target_addr; + unsigned int ui_read_offset; + unsigned short us_read_length; + int ret = -1; + int retry = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + memset(rbuffer, 0x00, MAX_READ_PACKET_SIZE); + + // make sure update flag was set + for (retry = 0 ; retry < SYN_I2C_RETRY_TIMES ; retry++) + { + mutex_lock(&ts->lock); + ret = raydium_i2c_pda2_set_page(client, RAYDIUM_PDA2_PAGE_0); + if (ret < 0) + { + goto exit_i2c_error; + } + ret = raydium_i2c_pda2_read(client, RAYDIUM_PDA2_HOST_CMD_ADDR, rbuffer, RAYDIUM_FT_CMD_LENGTH); + mutex_unlock(&ts->lock); + if (ret < 0) + { + goto exit_flag_error; + } + if ((rbuffer[RAYDIUM_FT_CMD_POS] & RAYDIUM_FT_UPDATE) == RAYDIUM_FT_UPDATE) + { + break; + } + msleep(5); + } + + if (retry == SYN_I2C_RETRY_TIMES) + { + ret = -EAGAIN; + goto exit_flag_error; + } + + ui_read_offset = 0; + us_read_length = 0; + while (ui_read_offset < g_ui_raw_data_length) + { + if ((ui_read_offset + MAX_READ_PACKET_SIZE) < g_ui_raw_data_length) + { + us_read_length = MAX_READ_PACKET_SIZE; + } + else + { + us_read_length = (unsigned short)(g_ui_raw_data_length - ui_read_offset); + } + ui_read_target_addr = RAYDIUM_READ_FT_DATA_CMD + ui_read_offset; + + mutex_lock(&(ts->lock)); + ret = raydium_i2c_pda2_set_page(client, RAYDIUM_PDA2_PAGE_0); + if (ret < 0) + goto exit_i2c_error; + + *(unsigned long *)rbuffer = (RAYDIUM_I2C_PDA_MODE_ENABLE << 24) | ((ui_read_target_addr & (~MASK_8BIT)) >> 8); //using byte mode to read 4 bytes + + ret = raydium_i2c_pda2_write(client, RAYDIUM_PDA2_PDA_CFG_ADDR, rbuffer, 4); + if (ret < 0) + { + goto exit_i2c_error; + } + ret = raydium_i2c_pda2_set_page(client, RAYDIUM_PDA2_ENABLE_PDA); + if (ret < 0) + { + goto exit_i2c_error; + } + ret = raydium_i2c_pda2_read(client, (unsigned char)(ui_read_target_addr& MASK_8BIT), rbuffer, us_read_length); + mutex_unlock(&(ts->lock)); + if (ret < 0) + { + goto exit_flag_error; + } + memcpy((buf + ui_read_offset), rbuffer, us_read_length); + + ui_read_offset += us_read_length; + } + + // clear update flag to get next one + rbuffer[RAYDIUM_HOST_CMD_POS] = RAYDIUM_HOST_CMD_NO_OP; + rbuffer[RAYDIUM_FT_CMD_POS] = g_uc_raw_data_type; + mutex_lock(&ts->lock); + ret = raydium_i2c_pda2_write(client, RAYDIUM_PDA2_HOST_CMD_ADDR, rbuffer, RAYDIUM_FT_CMD_LENGTH); + mutex_unlock(&ts->lock); + if (ret < 0) + { + goto exit_flag_error; + } + + return g_ui_raw_data_length; +exit_i2c_error: + mutex_unlock(&(ts->lock)); +exit_flag_error: + return ret; +} + +static ssize_t raydium_i2c_raw_data_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + int result = 0; + char *temp_buf, *token, *free_temp_buf, *free_token; + const char *delim = " ,"; + unsigned char w_data[RAYDIUM_FT_CMD_LENGTH]; + + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + /* receive command line arguments string */ + if (count < 2) + { + return -EINVAL; + } + temp_buf = kzalloc(count+1, GFP_KERNEL); + if (temp_buf == NULL ) + { + pr_err("[touch]kzalloc temp_buf failed\n"); + return -ENOMEM; + } + + token = kzalloc(count+1, GFP_KERNEL); + if (token == NULL ) + { + pr_err("[touch]kzalloc token failed\n"); + kfree(temp_buf); + return -ENOMEM; + } + + free_temp_buf = temp_buf; + free_token = token; + + ret = (ssize_t) count; + + strncpy(temp_buf, buf, count); + + token = strsep(&temp_buf, delim); + + result = kstrtou8(token, 16, &g_uc_raw_data_type); + + token = strsep(&temp_buf, delim); + if (token) + { + result = kstrtouint(token, 16, &g_ui_raw_data_length); + if (result < 0) + { + ret = result; + goto exit_error; + } + + } else // without length info + { + ret = -EINVAL; + goto exit_error; + } + + if (temp_buf) // too much arguments + { + ret = -E2BIG; + goto exit_error; + } + + memset(w_data, 0x00, RAYDIUM_FT_CMD_LENGTH); + + mutex_lock(&ts->lock); + result = raydium_i2c_pda2_set_page(client, RAYDIUM_PDA2_PAGE_0); + if (result < 0) + { + mutex_unlock(&ts->lock); + ret = result; + goto exit_error; + } + + if (g_uc_raw_data_type > 1) + { + w_data[RAYDIUM_HOST_CMD_POS]=RAYDIUM_HOST_CMD_FT_MODE; + } else + { + w_data[RAYDIUM_HOST_CMD_POS]=RAYDIUM_HOST_CMD_TP_MODE; + } + result = raydium_i2c_pda2_write(client, RAYDIUM_PDA2_HOST_CMD_ADDR, w_data, 1); + if (result < 0) + { + mutex_unlock(&ts->lock); + ret = result; + goto exit_error; + } + + w_data[RAYDIUM_HOST_CMD_POS] = RAYDIUM_HOST_CMD_NO_OP; + w_data[RAYDIUM_FT_CMD_POS] = g_uc_raw_data_type; + + result = raydium_i2c_pda2_write(client, RAYDIUM_PDA2_HOST_CMD_ADDR, w_data, RAYDIUM_FT_CMD_LENGTH); + mutex_unlock(&ts->lock); + if (result < 0) + { + ret = result; + goto exit_error; + } + +exit_error: + kfree(free_token); + kfree(free_temp_buf); + + return ret; +} +static ssize_t raydium_flag_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int buf_len = 0; + + + sprintf(buf, "[touch]Raydium flag : %d\n", g_uc_raydium_flag); + g_uc_raydium_flag &= ~RAYDIUM_INTERRUPT_FLAG; + buf_len = strlen(buf); + + return buf_len+1; +} + +static ssize_t raydium_flag_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + unsigned char flag = 0; + + /* receive command line arguments string */ + if (count > 2) + { + return -EINVAL; + } + ret = kstrtou8(buf, 16, &flag); + if (ret < 0) + { + return ret; + } + g_uc_raydium_flag = flag; + return count; +} + +static ssize_t raydium_wait_ft_int_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + unsigned int ret; + ret = raydium_wait_ft_int(client); + if (ret < 0) + { + return ret; + } + sprintf(buf, "Raydium raydium ftint : %d\n", ret); + return strlen(buf) + 1; +} + +static ssize_t raydium_wait_ft_int_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static int raydium_hw_reset_fun(struct i2c_client *client) +{ + int ret = SUCCESS; + unsigned int retry, retry_time = 1000; + unsigned int irq_value; + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + //#enable INT + g_uc_raydium_flag = RAYDIUM_BOOTLOADER_FLAG; + + //HW reset + pr_debug("[touch]Raydium HW reset\n"); + gpio_direction_output(ts->rst, 1); + gpio_direction_output(ts->rst, 0); + msleep(RAYDIUM_RESET_INTERVAL_MSEC); + gpio_direction_output(ts->rst, 1); + + u8_i2c_mode = PDA2_MODE; + + retry = retry_time; + while (retry != 0 && g_uc_raydium_flag != RAYDIUM_INTERRUPT_FLAG) + { + msleep(2); + retry--; + pr_debug("[touch]wait software reset finish => confirm interrupt_flag result %d\n", retry); + } + + //#disable INT + if (retry == 0 && g_uc_raydium_flag != RAYDIUM_INTERRUPT_FLAG) + { + g_uc_raydium_flag = 0; + } + + if (g_uc_raydium_flag == RAYDIUM_INTERRUPT_FLAG) + { + retry = retry_time; + irq_value = 0; + while (retry != 0 && irq_value != 1) + { + irq_value = gpio_get_value(ts->pdata->irq_gpio); + pr_debug("[touch]irq_value is %d\n", irq_value); + msleep(2); + retry--; + } + } + + if (retry == 0) + { + pr_err("[touch] FW not ready, retry error!\n"); + ret = ERROR; + } else + { + msleep(500); + raydium_irq_control(ts, ENABLE); + } + + pr_debug("[touch]Raydium HW reset : %d\n", ret); + return ret; +} + +static ssize_t raydium_hw_reset_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + int ret = SUCCESS; + unsigned int retry, retry_time = 1000; + unsigned int irq_value; + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + //#enable INT + g_uc_raydium_flag = RAYDIUM_BOOTLOADER_FLAG; + + //HW reset + pr_debug("[touch]Raydium HW reset\n"); + gpio_direction_output(ts->rst, 1); + gpio_direction_output(ts->rst, 0); + msleep(RAYDIUM_RESET_INTERVAL_MSEC); + gpio_direction_output(ts->rst, 1); + + u8_i2c_mode = PDA2_MODE; + + retry = retry_time; + while (retry != 0 && g_uc_raydium_flag != RAYDIUM_INTERRUPT_FLAG) + { + msleep(2); + retry--; + pr_debug("[touch]wait software reset finish => confirm interrupt_flag result %d\n", retry); + } + + //#disable INT + if (retry == 0 && g_uc_raydium_flag != RAYDIUM_INTERRUPT_FLAG) + { + g_uc_raydium_flag = 0; + } + + if (g_uc_raydium_flag == RAYDIUM_INTERRUPT_FLAG) + { + retry = retry_time; + irq_value = 0; + while (retry != 0 && irq_value != 1) + { + irq_value = gpio_get_value(ts->pdata->irq_gpio); + pr_debug("[touch]irq_value is %d\n", irq_value); + msleep(2); + retry--; + } + } + + if (retry == 0) + { + pr_err("[touch] FW not ready, retry error!\n"); + ret = ERROR; + } else + { + if (g_uc_raydium_selftest_flag == 0) + { + msleep(500); + } + raydium_irq_control(ts, ENABLE); + } + + sprintf(buf, "Raydium HW Reset : %d\n", ret); + printk("%s\n", buf); + return strlen(buf) + 1; +} + +static ssize_t raydium_palm_status_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int buf_len = 0; + unsigned int palm_status; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + palm_status = raydium_get_palm_state(ts,0); + sprintf(buf, "[Raydium] palm_status : %d\n", palm_status); + buf_len = strlen(buf); + return buf_len+1; +} + +static ssize_t raydium_palm_status_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + unsigned char palm_value = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + /* receive command line arguments string */ + if (count > 2) + { + return -EINVAL; + } + + ret = kstrtou8(buf, 16, &palm_value); + raydium_set_palm_state(ts, palm_value); + return count; +} + +static ssize_t raydium_palm_area_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + unsigned char w_data[1]; + unsigned char palm_area = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + /* receive command line arguments string */ + if (count > 2) + { + return -EINVAL; + } + + ret = kstrtou8(buf, 16, &palm_area); + + mutex_lock(&ts->lock); + ret = raydium_i2c_pda2_set_page(client, RAYDIUM_PDA2_PAGE_0); + if (ret < 0) + { + mutex_unlock(&ts->lock); + goto exit_error; + } + w_data[0] = palm_area; + ret = raydium_i2c_pda2_write(client, RAYDIUM_PDA2_PALM_AREA_ADDR, w_data, 1); + if (ret < 0) + { + mutex_unlock(&ts->lock); + goto exit_error; + } + mutex_unlock(&ts->lock); + +exit_error: + return count; +} + +static ssize_t raydium_int_flag_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int buf_len = 0; + + sprintf(buf, "%d", g_uc_raydium_int_flag); + pr_debug("[touch]Raydium int flag = %s!!\n", buf); + buf_len = strlen(buf); + + return buf_len+1; +} + +static ssize_t raydium_int_flag_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + unsigned char flag = 0; + + /* receive command line arguments string */ + if (count > 2) + { + return -EINVAL; + } + + ret = kstrtou8(buf, 16, &flag); + if (ret < 0) + { + return ret; + } + g_uc_raydium_int_flag = flag; + pr_debug("[touch]Raydium int flag store = %d!!\n", g_uc_raydium_int_flag); + return count; +} + +static ssize_t raydium_selftest_flag_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int buf_len = 0; + + sprintf(buf, "%d", g_uc_raydium_selftest_flag); + pr_debug("[touch]Raydium selftest flag = %s!!\n", buf); + buf_len = strlen(buf); + return buf_len+1; +} + +static ssize_t raydium_selftest_flag_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + unsigned char flag = 0; + pr_debug("[touch]Raydium %s!!\n", __func__); + + /* receive command line arguments string */ + if (count > 2) + { + return -EINVAL; + } + + ret = kstrtou8(buf, 16, &flag); + if (ret < 0) + { + return ret; + } + g_uc_raydium_selftest_flag = flag; + + pr_debug("[touch]Raydium selftest flag store = %d!!\n", g_uc_raydium_selftest_flag); + + return count; +} + +static ssize_t raydium_touch_lock_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0; + unsigned char mode; + unsigned char wbuffer[1]; + + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + if (ts->is_suspend) + { + pr_debug("[touch]Raydium IC is_suspend\n"); + } + + /* receive command line arguments string */ + if (count > 2) + { + return -EINVAL; + } + + ret = kstrtou8(buf, 16, &mode); + if (ret < 0) + { + return ret; + } + raydium_irq_control(ts, DISABLE); + mutex_lock(&ts->lock); + + switch (mode) + { + case 0: /* Disable Touch lock */ + + if (ts->is_sleep != 1) + { + break; + } + + if (gpio_is_valid(ts->rst)) + { + ret = gpio_request(ts->rst, "raydium_reset_gpio"); + if (ret < 0) + { + dev_err(&ts->client->dev, "[touch]reset gpio request failed\n"); + break; + } + gpio_direction_output(ts->rst, 1); + gpio_direction_output(ts->rst, 0); + msleep(RAYDIUM_RESET_INTERVAL_MSEC);//5ms + gpio_direction_output(ts->rst, 1); + msleep(RAYDIUM_RESET_DELAY_MSEC);//100ms + gpio_free(ts->rst); + } + pr_debug("[touch]Raydium disable touch lock!!\n"); + ts->is_sleep = 0; + break; + + case 1: /* Enable Touch lock */ + if (ts->is_sleep == 1) + { + break; + } + ret = raydium_i2c_pda2_set_page(client, RAYDIUM_PDA2_PAGE_0); + if (ret < 0) + { + goto exit_i2c_error; + } + wbuffer[0] = RAYDIUM_HOST_CMD_PWR_SLEEP; //fw enter sleep mode + ret = raydium_i2c_pda2_write(client, RAYDIUM_PDA2_HOST_CMD_ADDR, wbuffer, 1); + if (ret < 0) + { + goto exit_i2c_error; + } + pr_debug("[touch]Raydium enable touch lock!!\n"); + ts->is_sleep = 1; + break; + } + + ret = (ssize_t) count; + +exit_i2c_error: + mutex_unlock(&ts->lock); + raydium_irq_control(ts, ENABLE); + return ret; +} + +static ssize_t raydium_check_driver_version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + //unsigned char rbuffer[4]; + int buf_len = 0; + int ret = -1; + + sprintf(buf, "Raydium Driver Version : 0x%X\n", RAYDIUM_VER); + + buf_len = strlen(buf); + ret = buf_len + 1; + return ret; +} + +static ssize_t raydium_check_fw_version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned char rbuffer[4]; + int buf_len = 0; + int ret = -1; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + unsigned int fw_version, image_version; + + if (ts->is_suspend) + { + printk("[touch]Raydium IC is_suspend at %s\n", __func__); + } + + raydium_irq_control(ts, DISABLE); + mutex_lock(&ts->lock); + + ret = raydium_i2c_pda2_set_page(client, RAYDIUM_PDA2_PAGE_0); + if (ret < 0) + { + goto exit_i2c_error; + } + ret = raydium_i2c_pda2_read(client, RAYDIUM_PDA2_FW_VERSION_ADDR, rbuffer, 4); + if (ret < 0) + { + goto exit_i2c_error; + } + sprintf(buf, "Raydium Touch FW Version : %02X%02X%02X%02X\n",rbuffer[0], rbuffer[1], rbuffer[2], rbuffer[3]); + + fw_version = (rbuffer[0] << 24) | (rbuffer[1] << 16) | (rbuffer[2] << 8) | rbuffer[3]; + pr_info("[touch]Raydium IC FW version is 0x%x\n", fw_version); + + image_version = (RadFWImage[0xF21] << 24) | (RadFWImage[0xF22] << 16) | (RadFWImage[0xF23] << 8) | RadFWImage[0xF24]; + pr_info("[touch]Raydium Image FW version is 0x%x\n", image_version); + + mutex_unlock(&ts->lock); + raydium_irq_control(ts, ENABLE); + + if (fw_version != image_version) + { + pr_debug("[touch] FW need upgrade.\n"); + } + + buf_len = strlen(buf); + ret = buf_len + 1; + goto exit; + +exit_i2c_error: + mutex_unlock(&ts->lock); + raydium_irq_control(ts, ENABLE); +exit: + return ret; +} + +static ssize_t raydium_check_panel_version_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + unsigned char rbuffer[8]; + int buf_len = 0; + int ret = -1; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + if (ts->is_suspend) + { + pr_debug("[touch]Raydium IC is_suspend\n"); + } + raydium_irq_control(ts, DISABLE); + mutex_lock(&ts->lock); + + ret = raydium_i2c_pda2_set_page(client, RAYDIUM_PDA2_PAGE_0); + if (ret < 0) + { + goto exit_i2c_error; + } + ret = raydium_i2c_pda2_read(client, RAYDIUM_PDA2_PANEL_VERSION_ADDR, rbuffer, 8); + if (ret < 0) + { + goto exit_i2c_error; + } + sprintf(buf, "Raydium Touch Panel Version : %02X%02X%02X%02X%02X%02X\n", + rbuffer[0], rbuffer[1], rbuffer[2], rbuffer[3], rbuffer[4], rbuffer[5]); + mutex_unlock(&ts->lock); + raydium_irq_control(ts, ENABLE); + + buf_len = strlen(buf); + ret = buf_len + 1; + goto exit; + +exit_i2c_error: + mutex_unlock(&ts->lock); + raydium_irq_control(ts, ENABLE); +exit: + return ret; +} + +static ssize_t fw_check_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int count = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct raydium_ts_data *ts = (struct raydium_ts_data *)i2c_get_clientdata(client); + + if (ts->fw_version == 0) + { + count += sprintf(buf + count, "FAIL\n"); + } else + { + count += sprintf(buf + count, "Pass, (fw_ver : 0x%x)\n", ts->fw_version); + } + return count; +} + +static unsigned char i2c_burst_read(struct i2c_client *client, int int_addr, unsigned short u16ReadLen, unsigned char* p_u8_read_data) +{ + unsigned char result = ERROR; + unsigned short u16_already_read_len = 0; + char u8_read = 0; + unsigned char rbuffer[4]; + int ret = 0; + + if (u16ReadLen > 4) + { + u8_read = 4; + } else + { + u8_read = u16ReadLen; + } + while (u16_already_read_len < u16ReadLen) + { + ret = raydium_i2c_pda_read(client, int_addr, rbuffer, u8_read); + if (ret < 0) + { + return result; + } + memcpy(p_u8_read_data + u16_already_read_len, rbuffer, u8_read); + u16_already_read_len += u8_read; + } + result = SUCCESS; + return result; +} + +static unsigned char i2c_burst_write(struct i2c_client *client, int int_addr, unsigned short bWriteLen, unsigned char* bValue) +{ + unsigned char result = ERROR; + int ret = 0; + unsigned short u16_len = 0; + unsigned char u8_w_len = 4; + + while (u16_len < bWriteLen) + { + ret = raydium_i2c_pda_write(client, int_addr, bValue + u16_len, u8_w_len); + if (ret < 0) + { + return result; + } + u16_len += u8_w_len; + if (u16_len + u8_w_len > bWriteLen) + { + u8_w_len = bWriteLen - u16_len; + } + } + result = SUCCESS; + return result; +} + +static unsigned int i2c_read_4byte(struct i2c_client *client, unsigned int addr) +{ + unsigned int u32Data = 0, u16ii; + unsigned char u8_buf[4]; + + if (i2c_burst_read(client, addr, 4, u8_buf) == SUCCESS) + { + for (u16ii = 0; u16ii < 4 && u16ii < sizeof(u8_buf); u16ii++) + { + u32Data += (u8_buf[u16ii] << (8 * u16ii)); + } + } + return u32Data; +} + +void i2c_write(struct i2c_client *client, uint32_t addr, uint32_t data) +{ + unsigned char u8_data[4]; + u8_data[0] = (uint8_t)(data); + u8_data[1] = (uint8_t)(data >> 8); + u8_data[2] = (uint8_t)(data >> 16); + u8_data[3] = (uint8_t)(data >> 24); + i2c_burst_write(client, addr, 4, u8_data); +} + +static int raydium_selftest_stop_mcu(struct i2c_client *client) +{ + unsigned char wData[4]; + unsigned int u32_read; + unsigned short u16_time_out = 10; + unsigned int MCU_HOLD_STATUS = (0x00000001<<13); + + pr_debug("[touch]Stop MCU\r\n"); + i2c_burst_read(client, 0x50000918, 4, wData); + wData[0] |= 0x20 | 0x10; + i2c_burst_write(client, 0x50000918, 4, wData); + memset(wData, 0, sizeof(wData)); + wData[0] = 0x01; + i2c_burst_write(client, 0x40000004, 4, wData); + + u32_read = i2c_read_4byte(client, 0x50000918); + while ((u32_read & MCU_HOLD_STATUS) == 0 && u16_time_out-- > 0) + { + msleep(10); + u32_read = i2c_read_4byte(client, 0x50000918); + } + if ((u32_read & MCU_HOLD_STATUS) == 0) + { + pr_err("[touch]Not Enter MCU HOLD\r\n"); + return ERROR; + } else + { + pr_debug("[touch]Enter MCU HOLD\r\n"); + } + + u32_read = i2c_read_4byte(client, 0x50000610); + u32_read &= ~(0x7f << 8); + u32_read |= (1 << 7); + u32_read &= ~((1 << 7) | (1 << 6)); + u32_read |= (0xF << 8); + i2c_write(client, 0x50000610, u32_read); + + return SUCCESS; +} + +static ssize_t raydium_i2c_test_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret = SUCCESS; + + unsigned char wData[64], readData[64]; + unsigned int writeIndex = 0, i2cWriteLen = 0, i2cReadLength = 0, readLength = 0; + int addr = 0x20000000; + unsigned int u32_test_length = 256; + short i16_time_out = 3; + unsigned char u8_is_pass = 0; + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + + pr_debug("[touch]##RM6D010 I2C Test##\r\n"); + g_ui_length = 4; + g_ul_addr = 0x50000918; + raydium_i2c_pda_access_show(dev, attr, buf); + //Stop MCU + if (raydium_selftest_stop_mcu(client) == ERROR) + { + pr_err("[touch]Stop MCU NG\r\n"); + goto NG_CASE; + } + pr_debug("[touch]After STOP MCU!\n"); + for (writeIndex = 0; writeIndex < 64; writeIndex++) + { + if (writeIndex % 4 == 0) + { + wData[writeIndex] = 0x55; + } else if (writeIndex % 4 == 1) + { + wData[writeIndex] = 0xaa; + } else if (writeIndex % 4 == 2) + { + wData[writeIndex] = 0xa5; + } else if (writeIndex % 4 == 3) + { + wData[writeIndex] = 0x5a; + } + } + +RETRY_I2C: + while (i16_time_out > 0 && u8_is_pass != 1) + { + i2cWriteLen = 64; + writeIndex = 0; + pr_debug("[touch]I2C Test Write!!!\r\n"); + + while (writeIndex < u32_test_length) + { + if (i2c_burst_write(client, addr + writeIndex, i2cWriteLen, wData) == 0) + { + pr_err("[touch]i2c_test Write NG(ADDR=0x%x)!!\r\n", addr + writeIndex); + i16_time_out--; + if (raydium_hw_reset_fun(client) == ERROR) + { + goto NG_CASE; + } + if (raydium_selftest_stop_mcu(client) < 0) + { + goto NG_CASE; + } + goto RETRY_I2C; + } + + writeIndex += i2cWriteLen; + + if ((u32_test_length - writeIndex) < i2cWriteLen) + { + i2cWriteLen = (u32_test_length - writeIndex); + } + } + + pr_debug("[touch]I2C Test Read!!!\r\n"); + i2cReadLength = 64; + + while (readLength < u32_test_length) + { + if (i2c_burst_read(client, addr + readLength, i2cReadLength, readData) == 0) + { + i16_time_out--; + pr_err("[touch]i2c_test Read NG(ADDR=0x%x)!!\r\n", addr + writeIndex); + if (raydium_hw_reset_fun(client) == ERROR) + { + goto NG_CASE; + } + if (raydium_selftest_stop_mcu(client) < 0) + { + goto NG_CASE; + } + goto RETRY_I2C; + } + + for (writeIndex = 0; writeIndex < i2cReadLength; writeIndex++) + { + if (wData[writeIndex] != readData[writeIndex]) + { + pr_err("[touch]i2c_test Read NG [%d], W=0x%x,R=0x%x\r\n", writeIndex, wData[writeIndex], readData[writeIndex]); + i16_time_out--; + if (raydium_hw_reset_fun(client) == ERROR) + { + goto NG_CASE; + } + if (raydium_selftest_stop_mcu(client) < 0) + { + goto NG_CASE; + } + goto RETRY_I2C; + } + } + + readLength += i2cReadLength; + + if (u32_test_length - readLength < i2cReadLength) + { + i2cReadLength = u32_test_length - readLength; + } + } + + u8_is_pass = 1; + } + + if (!u8_is_pass) + { + goto NG_CASE; + } + ret = u8_is_pass; + pr_debug("[touch]I2C Test Pass!!!\r\n"); + sprintf(buf, "Raydium I2C Test : %d\n", ret); + printk("%s\n", buf); + return strlen(buf) + 1; + +NG_CASE: + ret = 0; + sprintf(buf, "Raydium I2C Test : %d\n", ret); + printk("%s\n", buf); + return strlen(buf) + 1; +} + +/* panel calibration cmd (R) + * example:cat raydium_ic_verion + */ +static DEVICE_ATTR(raydium_touch_calibration, S_IRUGO|S_IWUSR, + raydium_touch_calibration_show, + NULL); + +/* check the i2c (R) + * example:cat raydium_check_i2c + */ +static DEVICE_ATTR(raydium_check_i2c, S_IRUGO|S_IWUSR, + raydium_check_i2c_show, + NULL); + +/* upgrade configurate and algo firmware from app.bin (W) + * example:echo "offset num_of_bin length *_app.bin [length *_app.bin]" > raydium_fw_upgrade_mode + */ +static DEVICE_ATTR(raydium_fw_upgrade, S_IRUGO|S_IWUSR, + raydium_fw_upgrade_show, + raydium_fw_upgrade_store); + +/* upgrade configurate and algo firmware from app.bin (W) + * example:echo "offset num_of_bin length *_app.bin [length *_app.bin]" > raydium_fw_upgrade_mode + */ +static DEVICE_ATTR(raydium_upgrade_firmware, S_IRUGO|S_IWUSR, + NULL, + raydium_upgrade_firmware_store); + +/* change I2C comunication mode (W) + * example: echo 1 > raydium_i2c_pda2_mode ==> enable pda2 mode + * echo 0 > raydium_i2c_pda2_mode ==> disable pda2 mode + */ +static DEVICE_ATTR(raydium_i2c_pda2_mode, S_IRUGO|S_IWUSR, + NULL, + raydium_i2c_pda2_mode_store); + +/* I2C pda mode (R/W) + * example: cat raydium_i2c_pda_access ==> read pda address provided by the following cmd + * echo ADDRinHEX [DATAinHEX] > raydium_i2c_pda_access ==> write pda address [data] + */ +static DEVICE_ATTR(raydium_i2c_pda_access, S_IRUGO|S_IWUSR, + raydium_i2c_pda_access_show, + raydium_i2c_pda_access_store); + +/* I2C pda2 mode (R/W) + * example: cat raydium_i2c_pda2_access ==> read pda2 address provided by the following cmd + * echo ADDRinHEX [DATAinHEX] > raydium_i2c_pda2_access ==> write pda2 address [data] + */ +static DEVICE_ATTR(raydium_i2c_pda2_access, S_IRUGO|S_IWUSR, + raydium_i2c_pda2_access_show, + raydium_i2c_pda2_access_store); + +/* I2C pda2 mode page (W) + * example: echo PAGEinHEX > raydium_i2c_pda2_page ==> write pda2 page + */ +static DEVICE_ATTR(raydium_i2c_pda2_page, S_IRUGO|S_IWUSR, + NULL, + raydium_i2c_pda2_page_store); + +/* I2C read/set FT raw data (R/W) + * example: cat raydium_i2c_raw_data ==> read raw data with specific length of corresponding type provided by the following cmd + * echo DataTypeinHEX RawDataLengthinHEX > raydium_i2c_raw_data ==> set raw data type and its length + */ +static DEVICE_ATTR(raydium_i2c_raw_data, S_IRUGO|S_IWUSR, + raydium_i2c_raw_data_show, + raydium_i2c_raw_data_store); + +/* Read interrupt flag cmd (R) + * example:cat raydium_flag + */ +static DEVICE_ATTR(raydium_flag, S_IRUGO|S_IWUSR, + raydium_flag_show, + raydium_flag_store); + +/* Read interrupt flag cmd (R) + * example:cat raydium_int_flag + */ +static DEVICE_ATTR(raydium_int_flag, S_IRUGO|S_IWUSR, + raydium_int_flag_show, + raydium_int_flag_store); + +/* Read selftest flag cmd (R) + * example:cat raydium_int_flag + */ +static DEVICE_ATTR(raydium_selftest_flag, S_IRUGO|S_IWUSR, + raydium_selftest_flag_show, + raydium_selftest_flag_store); + +/* Touch lock (W) + * example: echo 1 > raydium_i2c_touch_lock ==> enable touch lock + * echo 0 > raydium_i2c_touch_lock ==> disable touch lock + */ +static DEVICE_ATTR(raydium_i2c_touch_lock, S_IRUGO|S_IWUSR, + NULL, + raydium_touch_lock_store); + +/* show the fw version (R) + * example:cat raydium_fw_version + */ +static DEVICE_ATTR(raydium_check_fw_version, S_IRUGO|S_IWUSR, + raydium_check_fw_version_show, + NULL); + +static DEVICE_ATTR(raydium_check_driver_version, S_IRUGO | S_IWUSR, + raydium_check_driver_version_show, + NULL); + +static DEVICE_ATTR(fw_check, S_IRUGO, fw_check_show, NULL); +/* show the panel version (R) + * example:cat raydium_panel_version + */ +static DEVICE_ATTR(raydium_check_panel_version, S_IRUGO | S_IWUSR, + raydium_check_panel_version_show, + NULL); + +static DEVICE_ATTR(raydium_hw_reset, S_IRUGO|S_IWUSR, + raydium_hw_reset_show, + NULL); +static DEVICE_ATTR(raydium_wait_ft_int, S_IRUGO|S_IWUSR, + raydium_wait_ft_int_show, + raydium_wait_ft_int_store); +static DEVICE_ATTR(raydium_palm_status, S_IRUGO|S_IWUSR, + raydium_palm_status_show, + raydium_palm_status_store); + +static DEVICE_ATTR(raydium_palm_area, S_IRUGO|S_IWUSR, + NULL, + raydium_palm_area_store); +static DEVICE_ATTR(raydium_i2c_test, S_IRUGO | S_IWUSR, + raydium_i2c_test_show, + NULL); + +/*add your attr in here*/ +static struct attribute *raydium_attributes[] = { + &dev_attr_raydium_touch_calibration.attr, + &dev_attr_raydium_check_i2c.attr, + &dev_attr_raydium_upgrade_firmware.attr, + &dev_attr_raydium_i2c_pda2_mode.attr, + &dev_attr_raydium_i2c_pda_access.attr, + &dev_attr_raydium_i2c_pda2_access.attr, + &dev_attr_raydium_i2c_pda2_page.attr, + &dev_attr_raydium_i2c_raw_data.attr, + &dev_attr_raydium_flag.attr, + &dev_attr_raydium_i2c_touch_lock.attr, + &dev_attr_raydium_fw_upgrade.attr, + &dev_attr_raydium_check_fw_version.attr, + &dev_attr_raydium_check_panel_version.attr, + &dev_attr_raydium_hw_reset.attr, + &dev_attr_raydium_palm_status.attr, + &dev_attr_raydium_int_flag.attr, + &dev_attr_raydium_selftest_flag.attr, + &dev_attr_raydium_wait_ft_int.attr, + &dev_attr_raydium_palm_area.attr, + &dev_attr_fw_check.attr, + &dev_attr_raydium_i2c_test.attr, + &dev_attr_raydium_check_driver_version.attr, + NULL +}; + +static const struct attribute_group raydium_attr_group = { + .attrs = raydium_attributes +}; + +/*create sysfs for debug update firmware*/ +static int raydium_create_sysfs(struct i2c_client *client) +{ + int ret = -1; + + ret = sysfs_create_group(&(client->dev.kobj), &raydium_attr_group); + if (ret) + { + dev_err(&client->dev, "[touch]failed to register sysfs\n"); + sysfs_remove_group(&client->dev.kobj, &raydium_attr_group); + return -EIO; + } else + { + pr_debug("[touch]create raydium sysfs attr_group succesful\n"); + } + + return ret; +} + +static void raydium_release_sysfs(struct i2c_client *client) +{ + sysfs_remove_group(&client->dev.kobj, &raydium_attr_group); +} +#endif //end of CONFIG_RM_SYSFS_DEBUG + +static int raydium_read_touchdata(struct raydium_ts_data *data) +{ + unsigned char buf[MAX_REPORT_PAKAGE_SIZE]; + unsigned char tp_status[MAX_TCH_STATUS_PAKAGE_SIZE]; + int ret = 0; + bool do_sync = false; + unsigned char i, j, offset = 0; + unsigned char u8_seq_no = 0, u8_points_amount, u8_point_id; + signed short s16_x, s16_y, s16_pressure, s16_wx, s16_wy; + +#ifdef GESTURE_EN + //display idle mode, touch idle mode + if (data->blank == RAYDIUM_BLANK || data->blank == RAYDIUM_VSYNC_SUSPEND) + { + //need check small area + input_mt_slot(data->input_dev, 0); + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, 1); + input_report_abs(data->input_dev, ABS_MT_POSITION_X, 100); + input_report_abs(data->input_dev, ABS_MT_POSITION_Y, 100); + input_sync(data->input_dev); + mdelay(1); + input_mt_slot(data->input_dev, 0); + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, 0); + input_mt_report_pointer_emulation(data->input_dev, false); + input_sync(data->input_dev); + g_uc_gesture_status = RAYDIUM_GESTURE_DISABLE; + pr_debug("[touch]display wake up\n"); + return 0; + } + else + { +#endif + mutex_lock(&data->lock); + + memset(buf, 0, MAX_REPORT_PAKAGE_SIZE); + memset(tp_status, 0, MAX_TCH_STATUS_PAKAGE_SIZE); + //read touch point information + ret = raydium_i2c_pda2_read(data->client, RAYDIUM_PDA2_TCH_RPT_STATUS_ADDR, tp_status, MAX_TCH_STATUS_PAKAGE_SIZE); + if (ret < 0) + { + dev_err(&data->client->dev, "[touch]%s: failed to read data: %d\n",__func__, ret); + mutex_unlock(&data->lock); + return ret; + } + + // inform IC to prepare next report + if (u8_seq_no == tp_status[POS_SEQ]) + { //report not updated + mutex_unlock(&data->lock); + printk("%s -> report not updated.\n", __func__); + return 0; + } + u8_points_amount = tp_status[POS_PT_AMOUNT]; + if (u8_points_amount > MAX_TOUCH_NUM) + { + mutex_unlock(&data->lock); + printk("%s -> u8_points_amount > MAX_TOUCH_NUM.\n", __func__); + return 0; + } + + //read touch point report + //PDA2 only support word mode + if (u8_points_amount != 0) + { + ret = raydium_i2c_pda2_read(data->client, RAYDIUM_PDA2_TCH_RPT_ADDR, buf, u8_points_amount * LENGTH_PT); + } + + if (ret < 0) + { + dev_err(&data->client->dev, "[touch]%s: failed to read data: %d\n", __func__, ret); + mutex_unlock(&data->lock); + return ret; + } + u8_seq_no = tp_status[POS_SEQ]; + tp_status[POS_SEQ] = 0; + ret = raydium_i2c_pda2_write(data->client, RAYDIUM_PDA2_TCH_RPT_STATUS_ADDR, tp_status, 1); + if (ret < 0) + { + dev_err(&data->client->dev, "[touch]%s: failed to write data: %d\n",__func__, ret); + mutex_unlock(&data->lock); + return ret; + } + mutex_unlock(&data->lock); + //Report + // TODO: Using struct+memcpy instead of array+offset + //initialize slot update info + for (i = 0; i < (MAX_TOUCH_NUM * 2); i++) + { + gst_slot_status[i].need_update = 0; + gst_slot_status[i].pt_report_offset = 0; + } + + //Check incoming point info + for (i = 0; i < u8_points_amount; i++) + { + u8_point_id = buf[POS_PT_ID + i * LENGTH_PT]; + // Current + for (j = 0; j < MAX_TOUCH_NUM; j++) + { + if (u8_point_id == gst_slot_status[j].occupied_pt_id) + { + gst_slot_status[j].need_update = 1; + gst_slot_status[j].pt_report_offset = i; + break; + } + } + // New coming + if (j == MAX_TOUCH_NUM) + { + for (j = MAX_TOUCH_NUM; j < (MAX_TOUCH_NUM * 2); j++) + { + if (!gst_slot_status[j].need_update) + { + gst_slot_status[j].occupied_pt_id = u8_point_id; + gst_slot_status[j].need_update = 1; + gst_slot_status[j].pt_report_offset = i; + pr_debug("[touch]x:%d,y:%d\n", + buf[POS_X_L + offset] | buf[POS_X_H + offset] << SHORT_HIGH_BYTE_SHIFT, + buf[POS_Y_L + offset] | buf[POS_Y_H + offset] << SHORT_HIGH_BYTE_SHIFT); + break; + } + } + } + } + + //Release slot with non-occupied point + for (i = 0; i < MAX_TOUCH_NUM; i++) + { + if (!gst_slot_status[i].need_update) + { + input_mt_slot(data->input_dev, i); + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, false); + gst_slot_status[i].occupied_pt_id = 0xFF; + gst_slot_status[i].pt_report_offset = 0; + gst_slot_status[i].need_update = 0; + do_sync = true; + } + } + //Assign new one to non-occupied slot + for (i = MAX_TOUCH_NUM; i < (MAX_TOUCH_NUM * 2); i++) + { + if (gst_slot_status[i].need_update) + { + for (j = 0; j < MAX_TOUCH_NUM; j++) + { + if (!gst_slot_status[j].need_update) + { + gst_slot_status[j] = gst_slot_status[i]; + gst_slot_status[i] = gst_slot_init_status; + break; + } + } + } + else + { + break; + } + } + + for (i = 0; i < MAX_TOUCH_NUM; i++) + { + if (gst_slot_status[i].need_update) + { + offset = gst_slot_status[i].pt_report_offset * LENGTH_PT; + s16_x = buf[POS_X_L + offset] | buf[POS_X_H + offset] << SHORT_HIGH_BYTE_SHIFT; + s16_y = buf[POS_Y_L + offset] | buf[POS_Y_H + offset] << SHORT_HIGH_BYTE_SHIFT; + s16_pressure = buf[POS_PRESSURE_L + offset] | buf[POS_PRESSURE_H + offset] << SHORT_HIGH_BYTE_SHIFT; + s16_wx = buf[POS_WX_L + offset] | buf[POS_WX_H + offset] << SHORT_HIGH_BYTE_SHIFT; + s16_wy = buf[POS_WY_L + offset] | buf[POS_WY_H + offset] << SHORT_HIGH_BYTE_SHIFT; + + input_mt_slot(data->input_dev, i); + input_mt_report_slot_state(data->input_dev, MT_TOOL_FINGER, true); + + input_report_abs(data->input_dev, ABS_MT_POSITION_X, s16_x); + input_report_abs(data->input_dev, ABS_MT_POSITION_Y, s16_y); + input_report_abs(data->input_dev, ABS_MT_PRESSURE, s16_pressure); + input_report_abs(data->input_dev, ABS_MT_TOUCH_MAJOR, max(s16_wx, s16_wy)); + input_report_abs(data->input_dev, ABS_MT_TOUCH_MINOR, min(s16_wx, s16_wy)); + //pr_debug("[touch]x:%d, y:%d\n", s16_x,s16_y); + do_sync = true; + } + } + + if (do_sync) { + input_mt_sync_frame(data->input_dev); + input_sync(data->input_dev); + } + + /* + printk("Raydium Slot status #: %d %d ; %d %d ; %d %d ; %d %d\n", + gst_slot_status[0].need_update, gst_slot_status[0].occupied_pt_id, + gst_slot_status[1].need_update, gst_slot_status[1].occupied_pt_id, + gst_slot_status[2].need_update, gst_slot_status[2].occupied_pt_id, + gst_slot_status[3].need_update, gst_slot_status[3].occupied_pt_id); + */ +#ifdef GESTURE_EN + } +#endif + + return 0; +} + +static void raydium_work_handler(struct work_struct *work) +{ + struct raydium_ts_data *raydium_ts = \ + container_of(work, struct raydium_ts_data, work); + int ret = 0; + +#ifdef GESTURE_EN + unsigned int palm_status = 0; + int i = 0; + + //when display on can use palm to suspend + if (raydium_ts->blank == RAYDIUM_UNBLANK) + { + if (u8_i2c_mode == PDA2_MODE) + { + palm_status = raydium_get_palm_state(raydium_ts, g_uc_pre_palm_status); + } + if (palm_status == RAYDIUM_PALM_MODE_ENABLE) + { + if (raydium_ts->is_palm == 0) + { + // release all touches + for (i = 0; i < raydium_ts->pdata->num_max_touches; i++) + { + input_mt_slot(raydium_ts->input_dev, i); + input_mt_report_slot_state(raydium_ts->input_dev, MT_TOOL_FINGER, false); + } + + input_mt_report_pointer_emulation(raydium_ts->input_dev, false); + input_report_key(raydium_ts->input_dev, KEY_SLEEP, true); //press sleep key + input_sync(raydium_ts->input_dev); + + pr_debug("[touch]palm_status = %d.\n", palm_status); + g_uc_pre_palm_status = RAYDIUM_PALM_MODE_ENABLE; + raydium_ts->is_palm = 1; + goto exit; + } + } else if ((palm_status == RAYDIUM_PALM_MODE_DISABLE) && (g_uc_pre_palm_status == RAYDIUM_PALM_MODE_ENABLE)) + { + pr_debug("[touch]leave palm mode.\n"); + input_report_key(raydium_ts->input_dev, KEY_SLEEP, false); //release sleep key + input_sync(raydium_ts->input_dev); + g_uc_pre_palm_status = RAYDIUM_PALM_MODE_DISABLE; + raydium_ts->is_palm = 0; + goto exit; + } + } +#endif + + if (u8_i2c_mode == PDA2_MODE) + { + ret = raydium_read_touchdata(raydium_ts); + if (ret < 0) + { + printk(KERN_ERR "[touch]raydium_read_touchdata error, ret:%d\n", ret); + } + } + +#ifdef GESTURE_EN +exit: +#endif + return; +} + +/*The raydium device will signal the host about TRIGGER_FALLING. + *Processed when the interrupt is asserted. + */ +static irqreturn_t raydium_ts_interrupt(int irq, void *dev_id) +{ + struct raydium_ts_data *raydium_ts = dev_id; + bool result = false; + + if (g_uc_raydium_selftest_flag == 1) + { + disable_irq_nosync(raydium_ts->irq); + raydium_ts->irq_enabled = false; + pr_debug("[touch]g_uc_raydium_selftest_flag = 1\n"); + g_uc_raydium_int_flag = 1; + } + //For bootloader wrt/erase flash and software reset interrupt + else if ((g_uc_raydium_flag & RAYDIUM_BOOTLOADER_FLAG) != 0) + { + disable_irq_nosync(raydium_ts->irq); + raydium_ts->irq_enabled = false; + pr_debug("[touch]g_uc_raydium_flag = %d\n", g_uc_raydium_flag); + g_uc_raydium_flag = RAYDIUM_INTERRUPT_FLAG; + } else + { + if (!work_pending(&raydium_ts->work)) + { + result = queue_work(raydium_ts->workqueue, &raydium_ts->work); + if (result == false) //queue_work fail + { + pr_err("[touch]queue_work fail.\n"); + } else + { + if (raydium_ts->blank == RAYDIUM_BLANK) + { + pr_debug("[touch] disable depth : %u\n", raydium_ts->irq_desc->depth); + disable_irq_nosync(raydium_ts->irq); + raydium_ts->irq_enabled = false; + pr_debug("[touch]BLANK_POWERDOWN disable irq\n"); + } else + { + /* Clear interrupts*/ + mutex_lock(&raydium_ts->lock); + if (raydium_i2c_pda2_set_page(raydium_ts->client, RAYDIUM_PDA2_PAGE_0) < 0) + { + disable_irq_nosync(raydium_ts->irq); + raydium_ts->irq_enabled = false; + dev_err(&raydium_ts->client->dev, "[touch]%s: failed to set page\n",__func__); + } + mutex_unlock(&raydium_ts->lock); + } + } + } else //work pending + { + /* Clear interrupts*/ + mutex_lock(&raydium_ts->lock); + if (raydium_i2c_pda2_set_page(raydium_ts->client, RAYDIUM_PDA2_PAGE_0) < 0) + { + disable_irq_nosync(raydium_ts->irq); + raydium_ts->irq_enabled = false; + dev_err(&raydium_ts->client->dev, "[touch]%s: failed to set page in work_pending\n", __func__); + } + mutex_unlock(&raydium_ts->lock); + pr_debug("[touch]work_pending\n"); + } + } + + return IRQ_HANDLED; +} + +static int raydium_check_i2c_ready(struct raydium_ts_data *raydium_ts) +{ + unsigned char temp_buf[4]; + int ret = -1; + + mutex_lock(&raydium_ts->lock); + + if (u8_i2c_mode == PDA2_MODE) + { + ret = raydium_i2c_pda2_set_page(raydium_ts->client, RAYDIUM_PDA2_PAGE_0); + if (ret < 0) + { + goto exit_error; + } + + *(unsigned long *)temp_buf = (RAYDIUM_I2C_PDA_MODE_ENABLE << 24) | ((RAYDIUM_CHECK_I2C_CMD & (~MASK_8BIT)) >> 8); //using byte mode to read 4 bytes + ret = raydium_i2c_pda2_write(raydium_ts->client, RAYDIUM_PDA2_PDA_CFG_ADDR, temp_buf, 4); + if (ret < 0) + { + goto exit_error; + } + + ret = raydium_i2c_pda2_set_page(raydium_ts->client, RAYDIUM_PDA2_ENABLE_PDA); + if (ret < 0) + { + goto exit_error; + } + + ret = raydium_i2c_pda2_read(raydium_ts->client, (unsigned char)(RAYDIUM_CHECK_I2C_CMD& MASK_8BIT), temp_buf, 4); + if (ret < 0) + { + goto exit_error; + } + } else + { + ret = raydium_i2c_pda_read(raydium_ts->client, RAYDIUM_CHECK_I2C_CMD, temp_buf, 4); + if (ret < 0) + { + goto exit_error; + } + } + + pr_info("[touch]Raydium Touch check I2C:0x%02X%02X\n", temp_buf[3], temp_buf[2]); + +exit_error: + mutex_unlock(&raydium_ts->lock); + return ret; +} + +static int raydium_fw_update_check(struct raydium_ts_data *raydium_ts) +{ + + unsigned char rbuffer[4]; + unsigned int fw_version, image_version; + int ret = -1; + + mutex_lock(&raydium_ts->lock); + ret = raydium_i2c_pda2_set_page(raydium_ts->client, RAYDIUM_PDA2_PAGE_0); + if (ret < 0) + { + goto exit_error; + } + ret = raydium_i2c_pda2_read(raydium_ts->client, RAYDIUM_PDA2_FW_VERSION_ADDR, rbuffer, 4); + if (ret < 0) + { + goto exit_error; + } + mutex_unlock(&raydium_ts->lock); + + fw_version = (rbuffer[0] << 24) | (rbuffer[1] << 16) | (rbuffer[2] << 8) | rbuffer[3]; + pr_info("[touch]Raydium IC FW version is 0x%x\n", fw_version); + + raydium_ts->fw_version = fw_version; + + /* TODO: re-enable FW check */ + pr_warn("skip fw image version check\n"); + return 0; + + image_version = (RadFWImage[0xF21] << 24) | (RadFWImage[0xF22] << 16) | (RadFWImage[0xF23] << 8) | RadFWImage[0xF24]; + pr_info("[touch]Raydium Image FW version is 0x%x\n", image_version); + + if (fw_version != image_version) + { + pr_debug("[touch]FW need update.\n"); + raydium_irq_control(raydium_ts, DISABLE); + ret = raydium_burn_fw(raydium_ts->client); + if (ret < 0) + { + pr_err("[touch]FW update fail:%d\n", ret); + } + raydium_irq_control(raydium_ts, ENABLE); + mutex_lock(&raydium_ts->lock); + ret = raydium_i2c_pda2_set_page(raydium_ts->client, RAYDIUM_PDA2_PAGE_0); + if (ret < 0) + { + goto exit_error; + } + ret = raydium_i2c_pda2_read(raydium_ts->client, RAYDIUM_PDA2_FW_VERSION_ADDR, rbuffer, 4); + if (ret < 0) + { + goto exit_error; + } + mutex_unlock(&raydium_ts->lock); + fw_version = (rbuffer[0] << 24) | (rbuffer[1] << 16) | (rbuffer[2] << 8) | rbuffer[3]; + pr_info("[touch]Raydium IC FW version is 0x%x\n", fw_version); + raydium_ts->fw_version = fw_version; + } else + { + pr_debug("[touch]FW is the latest version.\n"); + } + + return ret; + +exit_error: + mutex_unlock(&raydium_ts->lock); + return ret; +} + +#if defined(CONFIG_PM) +static void raydium_ts_do_suspend(struct raydium_ts_data *ts) +{ + int i = 0; + + if (ts->is_suspend == 1) + { + pr_debug("[touch]Already in suspend state\n"); + return; + } + + //#ifndef GESTURE_EN + raydium_irq_control(ts, DISABLE); + //#endif + + //clear workqueue + if (!cancel_work_sync(&ts->work)) + pr_debug("[touch]workqueue is empty!\n"); + + pr_debug("[touch]\n"); + + /* release all touches */ + for (i = 0; i < ts->pdata->num_max_touches; i++) + { + input_mt_slot(ts->input_dev, i); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); + } + input_mt_report_pointer_emulation(ts->input_dev, false); + input_sync(ts->input_dev); + + #ifdef GESTURE_EN + if (device_may_wakeup(&ts->client->dev)) + { + pr_debug("[touch]Device may wakeup\n"); + if (!enable_irq_wake(ts->irq)) + { + ts->irq_wake = 1; + } + } else + { + printk(KERN_INFO "[touch]Device not wakeup\n"); + } + raydium_irq_control(ts, ENABLE); + #endif + + ts->is_suspend = 1; + pr_info("state change ACTIVE -> SUSPEND\n"); +} +static void raydium_ts_do_resume(struct raydium_ts_data *ts) +{ + pr_debug("[touch] %d\n", ts->is_suspend); + if (ts->is_suspend == 0) + { + pr_debug("[touch]Already in resume state\n"); + return; + } + + //clear workqueue + if (!cancel_work_sync(&ts->work)) + pr_debug("[touch]workqueue is empty!\n"); + + if (ts->is_close != 1) + raydium_irq_control(ts, ENABLE); + +#ifdef GESTURE_EN + if (device_may_wakeup(&ts->client->dev)) + { + pr_debug("[touch]Device may wakeup\n"); + if (ts->irq_wake) + { + disable_irq_wake(ts->irq); + ts->irq_wake = 0; + } + } else + { + pr_debug("[touch]Device not wakeup\n"); + } +#endif + + ts->is_suspend = 0; + pr_info("state change SUSPEND -> ACTIVE\n"); +} + +static int raydium_ts_suspend(struct device *dev) +{ + struct raydium_ts_data *ts = dev_get_drvdata(dev); + raydium_ts_do_suspend(ts); + return 0; +} + +static int raydium_ts_resume(struct device *dev) +{ + struct raydium_ts_data *ts = dev_get_drvdata(dev); + raydium_ts_do_resume(ts); + return 0; +} + +static const struct dev_pm_ops raydium_ts_pm_ops = { +#if (!defined(CONFIG_DRM) && !defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND)) + .suspend = raydium_ts_suspend, + .resume = raydium_ts_resume, +#endif //end of CONFIG_PM +}; + +//used for touch lock feature +static int raydium_ts_open(struct input_dev *input_dev) +{ + struct raydium_ts_data *ts; + int ret = 0; + + pr_debug("[touch]+\n"); + ts = input_get_drvdata(input_dev); + + pr_debug("[touch]ts->blank:%x\n",ts->blank); + + ts->is_close = 0; + if (ts->is_sleep == 1) + { + //turn on i2c pull-up power + ret = raydium_power_on(ts, true); + if (ret) + { + dev_err(&ts->client->dev, "[touch]%s:power on failed",__func__); + } + udelay(RAYDIUM_POWERON_DELAY_USEC);//500us + + mutex_lock(&ts->lock); + if (gpio_is_valid(ts->rst)) + { + //udelay(RAYDIUM_POWERON_DELAY_USEC);//500us + + ret = gpio_request(ts->rst, "raydium_reset_gpio"); + if (ret < 0) + { + dev_err(&ts->client->dev, "[touch]reset gpio request failed"); + } + gpio_direction_output(ts->rst, 1); + gpio_direction_output(ts->rst, 0); + msleep(RAYDIUM_RESET_INTERVAL_MSEC);//5ms + gpio_direction_output(ts->rst, 1); + msleep(RAYDIUM_RESET_DELAY_MSEC);//100ms + u8_i2c_mode = PDA2_MODE; + gpio_free(ts->rst); + } + mutex_unlock(&ts->lock); + raydium_irq_control(ts, ENABLE); + ts->is_sleep = 0; + pr_debug("[touch]disable touch lock.\n"); + } + + return ret; +} + +static void raydium_ts_close(struct input_dev *input_dev) +{ + struct raydium_ts_data *ts; + int ret = 0; + int i = 0; + unsigned char wbuffer[1]; + unsigned char buf[4]; + + pr_debug("[touch]+\n"); + ts = input_get_drvdata(input_dev); + + if (ts->is_sleep == 1) + { + pr_debug("[touch]touch lock already enabled.\n"); + return; + } + + ts->is_close = 1; + raydium_irq_control(ts, DISABLE); + + /* release all touches */ + for (i = 0; i < ts->pdata->num_max_touches; i++) + { + input_mt_slot(ts->input_dev, i); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); + } + input_mt_report_pointer_emulation(ts->input_dev, false); + input_sync(ts->input_dev); + + mutex_lock(&ts->lock); + ret = raydium_i2c_pda2_set_page(ts->client, RAYDIUM_PDA2_PAGE_0); + if (ret < 0) + { + pr_err("[touch]ret:%d\n", ret); + goto exit_i2c_error; + } + + wbuffer[0] = RAYDIUM_HOST_CMD_PWR_SLEEP;//fw enter sleep mode + ret = raydium_i2c_pda2_write(ts->client, RAYDIUM_PDA2_HOST_CMD_ADDR, wbuffer, 1); + if (ret < 0) + { + pr_err("[touch]ret:%d\n", ret); + goto exit_i2c_error; + } + + mdelay(750); + memset(buf, 0, sizeof(buf)); + ret = raydium_i2c_pda_write(ts->client, 0x5000000C, buf, 4); + if (ret < 0) + { + pr_err("[touch]Disable M0 NG ret:%d\n",ret); + goto exit_i2c_error; + } + + //turn off i2c pull-up power + ret = raydium_power_on(ts, false); + if (ret) + { + dev_err(&ts->client->dev, "[touch]%s:power off failed",__func__); + } + mutex_unlock(&ts->lock); + + ts->is_sleep = 1; + pr_debug("[touch]enable touch lock.\n"); + return; + +exit_i2c_error: + mutex_unlock(&ts->lock); + ts->is_close = 0; + raydium_irq_control(ts, ENABLE); + return; +} + +#else +static int raydium_ts_suspend(struct device *dev) +{ + return 0; +} + +static int raydium_ts_resume(struct device *dev) +{ + return 0; +} +#endif //end of CONFIG_FB + +#if defined(CONFIG_DRM) +static int drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) + { + struct msm_drm_notifier *evdata = data; + struct raydium_ts_data *raydium_ts = + container_of(self, struct raydium_ts_data, panel_cb); + + if (!evdata || (evdata->id != raydium_ts->pdata->ctrl_dsi)) { + pr_debug("[touch]drm notification: id(%d) != ctrl_dsi(%d)\n", + evdata->id, raydium_ts->pdata->ctrl_dsi); + return 0; + } + + if ((event == MSM_DRM_EARLY_EVENT_BLANK || event == MSM_DRM_EVENT_BLANK) && + evdata && evdata->data && raydium_ts) { + int *blank = evdata->data; + + raydium_ts->blank = (*blank); + pr_debug("[touch]drm notification: event = %lu blank = %d\n", event, *blank); + /* entering suspend upon early blank event */ + /* to ensure shared power supply is still on */ + /* for in-cell design touch solutions */ + if (event == MSM_DRM_EARLY_EVENT_BLANK) { + if (*blank != MSM_DRM_BLANK_POWERDOWN) + return 0; + pr_debug("[touch]MSM_DRM_BLANK_POWERDOWN\n"); + #ifdef GESTURE_EN + /* clear palm status */ + g_uc_pre_palm_status = 0; + raydium_ts->is_palm = 0; + #endif + raydium_ts_suspend(&raydium_ts->client->dev); + } else if (*blank == MSM_DRM_BLANK_UNBLANK) { + pr_debug("[touch]MSM_DRM_BLANK_UNBLANK\n"); + #ifdef GESTURE_EN + /* clear palm status */ + g_uc_pre_palm_status = 0; + raydium_ts->is_palm = 0; + #endif + raydium_ts_resume(&raydium_ts->client->dev); + } + } + + return 0; +} +#elif defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + struct raydium_ts_data *raydium_ts = + container_of(self, struct raydium_ts_data, panel_cb); + + if (evdata && evdata->data && event == FB_EVENT_BLANK && + raydium_ts && raydium_ts->client) + { + blank = evdata->data; + raydium_ts->blank = (*blank); + + switch (*blank) + { + case FB_BLANK_UNBLANK: //screen on + pr_debug("[touch]FB_BLANK_UNBLANK\n"); + #ifdef GESTURE_EN + /* clear palm status */ + g_uc_pre_palm_status = 0; + raydium_ts->is_palm = 0; + #endif + raydium_ts_resume(&raydium_ts->client->dev); + break; + + case FB_BLANK_POWERDOWN://screen off + pr_debug("[touch]FB_BLANK_POWERDOWN\n"); + #ifdef GESTURE_EN + /* clear palm status */ + g_uc_pre_palm_status = 0; + raydium_ts->is_palm = 0; + #endif + raydium_ts_suspend(&raydium_ts->client->dev); + break; + + case FB_BLANK_VSYNC_SUSPEND://ambient mode + pr_debug("[touch]FB_BLANK_VSYNC_SUSPEND\n"); + #ifdef GESTURE_EN + /* clear palm status */ + g_uc_pre_palm_status = 0; + raydium_ts->is_palm = 0; + #endif + raydium_ts_suspend(&raydium_ts->client->dev); + break; + + default: + break; + } + } + return 0; +} +#endif + +#if (defined(CONFIG_DRM) || defined(CONFIG_FB)) +static void raydium_register_notifier(struct raydium_ts_data *raydium_ts) +{ + memset(&raydium_ts->panel_cb, 0, sizeof(raydium_ts->panel_cb)); +#if defined(CONFIG_DRM) + raydium_ts->panel_cb.notifier_call = drm_notifier_callback; + /* register DRM notifier */ + msm_drm_register_client(&raydium_ts->panel_cb); +#elif defined(CONFIG_FB) + raydium_ts->panel_cb.notifier_call = fb_notifier_callback; + /* register on the fb notifier and work with fb*/ + fb_register_client(&raydium_ts->panel_cb); +#endif +} + +static void raydium_unregister_notifier(struct raydium_ts_data *raydium_ts) +{ +#if defined(CONFIG_DRM) + msm_drm_unregister_client(&raydium_ts->panel_cb); +#elif defined(CONFIG_FB) + fb_unregister_client(&raydium_ts->panel_cb); +#endif +} + +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void raydium_ts_early_suspend(struct early_suspend *handler) +{ + struct raydium_ts_data *raydium_ts = container_of(handler, + struct raydium_ts_data, + early_suspend); + + raydium_ts_do_suspend(raydium_ts); +} + +static void raydium_ts_late_resume(struct early_suspend *handler) +{ + struct raydium_ts_data *raydium_ts = container_of(handler, + struct raydium_ts_data, + early_suspend); + + raydium_ts_do_resume(raydium_ts); +} +#endif //end of CONFIG_FB + +#ifdef CONFIG_OF +static int raydium_get_dt_coords(struct device *dev, char *name, + struct raydium_ts_platform_data *pdata) +{ + u32 coords[COORDS_ARR_SIZE]; + struct property *prop; + struct device_node *np = dev->of_node; + int coords_size, rc; + + 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 != COORDS_ARR_SIZE) + { + dev_err(dev, "[touch]invalid %s\n", name); + return -EINVAL; + } + + rc = of_property_read_u32_array(np, name, coords, coords_size); + if (rc && (rc != -EINVAL)) + { + dev_err(dev, "[touch]unable to read %s\n", name); + return rc; + } + + if (!strcmp(name, "raydium,display-coords")) + { + pdata->x_min = coords[0]; + pdata->y_min = coords[1]; + pdata->x_max = coords[2]; + pdata->y_max = coords[3]; + } else { + dev_err(dev, "[touch]unsupported property %s\n", name); + return -EINVAL; + } + + return 0; +} + +static int raydium_parse_dt(struct device *dev, struct raydium_ts_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + int rc = 0; + const char *display_name = NULL; + u32 temp_val = 0; + + pdata->name = RAYDIUM_NAME; + + rc = raydium_get_dt_coords(dev, "raydium,display-coords", pdata); + if (rc) + { + pr_err("[touch]parse dt coodrds fail.\n"); + return rc; + } + + /* reset, irq gpio info */ + pdata->reset_gpio = of_get_named_gpio(np, "raydium,reset-gpio", 0); + if (pdata->reset_gpio < 0) + { + pr_info("[touch]parse dt no reset_gpio; assume in-cell\n"); + } + + pdata->irq_gpio = of_get_named_gpio(np, "raydium,irq-gpio", 0); + if (pdata->irq_gpio < 0) + { + pr_err("[touch]parse dt irq_gpio fail.\n"); + return pdata->irq_gpio; + } + + rc = of_property_read_u32(np, "raydium,hard-reset-delay-ms", &temp_val); + if (!rc) + { + pdata->hard_rst_dly = temp_val; + } else + { + pr_info("[touch]parse dt no hard-reset-delay-ms\n"); + } + + rc = of_property_read_u32(np, "raydium,soft-reset-delay-ms", &temp_val); + if (!rc) + { + pdata->soft_rst_dly = temp_val; + } else + { + pr_info("[touch]parse dt no soft-reset-delay-ms\n"); + } + + rc = of_property_read_u32(np, "raydium,num-max-touches", &temp_val); + if (!rc) + { + pdata->num_max_touches = temp_val; + } else + { + pr_err("[touch]parse dtnum-max-touches fail, return:%d.\n", rc); + return rc; + } + + rc = of_property_read_u32(np, "raydium,control-dsi", &temp_val); + if (!rc) + { + pdata->ctrl_dsi = temp_val; + } + + rc = of_property_read_string(np, "raydium,bound-display", &display_name); + if (!rc) + { + pdata->bound_display = display_name; + } + + return 0; +} +#else +static int raydium_parse_dt(struct device *dev, struct raydium_ts_platform_data *pdata) +{ + return -ENODEV; +} +#endif //end of CONFIG_OF + +static void raydium_input_set(struct input_dev *input_dev, struct raydium_ts_data *ts) +{ + int ret = 0; + unsigned char i; + input_dev->name = "raydium_ts";//name need same with .idc + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &ts->client->dev; + input_dev->open = raydium_ts_open;//touch lock + input_dev->close = raydium_ts_close; + input_set_drvdata(input_dev, ts); + +#ifdef GESTURE_EN + set_bit(EV_KEY, input_dev->evbit); +#endif + set_bit(EV_SYN, input_dev->evbit); + set_bit(EV_ABS, input_dev->evbit); +// set_bit(BTN_TOUCH, input_dev->keybit); + set_bit(INPUT_PROP_DIRECT, input_dev->propbit); + + /* Multitouch input params setup */ + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, ts->x_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, ts->y_max, 0, 0); + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, PRESS_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, WIDTH_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0, WIDTH_MAX, 0, 0); + + ret = input_mt_init_slots(input_dev, MAX_TOUCH_NUM, 0); + if (ret) + { + dev_err(&ts->client->dev, "[touch]failed to initialize MT slots: %d\n", ret); + } + + for (i = 0; i < (MAX_TOUCH_NUM * 2); i++) + { + gst_slot_status[i]=gst_slot_init_status; + } + +} + +static int raydium_ts_remove(struct i2c_client *client); +static int raydium_ts_lateinit(struct raydium_ts_data *raydium_ts) +{ + struct raydium_ts_platform_data *pdata = raydium_ts->pdata; + struct i2c_client *client = raydium_ts->client; + struct input_dev *input_dev; + unsigned long irqflags = pdata->irqflags; + int ret = 0; + + /* print touch i2c ready */ + ret = raydium_check_i2c_ready(raydium_ts); + if (ret < 0) { + dev_err(&client->dev, "[touch]Check I2C failed\n"); + ret = -ENODEV; + goto exit_check_i2c; + } + + /* input device initialization */ + input_dev = input_allocate_device(); + if (!input_dev) { + ret = -ENOMEM; + dev_err(&client->dev, "[touch]failed to allocate input device\n"); + goto exit_input_dev_alloc_failed; + } + + raydium_input_set(input_dev, raydium_ts); + + ret = input_register_device(input_dev); + if (ret) { + dev_err(&client->dev, "[touch]failed to register input device: %s\n", + dev_name(&client->dev)); + goto exit_input_register_device_failed; + } + raydium_ts->input_dev = input_dev; + +#ifdef GESTURE_EN + input_set_capability(input_dev, EV_KEY, KEY_SLEEP); +#endif + + /* suspend/resume routine */ +#if (defined(CONFIG_FB) || defined(CONFIG_DRM)) + raydium_register_notifier(raydium_ts); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + /* 1: Early-suspend level */ + raydium_ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + raydium_ts->early_suspend.suspend = raydium_ts_early_suspend; + raydium_ts->early_suspend.resume = raydium_ts_late_resume; + register_early_suspend(&raydium_ts->early_suspend); +#endif /* end of CONFIG_FB */ + +#ifdef CONFIG_RM_SYSFS_DEBUG + raydium_create_sysfs(client); +#endif /* end of CONFIG_RM_SYSFS_DEBUG */ + + pr_debug("[touch]pdata irq: %d\n", raydium_ts->pdata->irq_gpio); + pr_debug("[touch]client irq: %d, pdata flags: %d\n", + client->irq, pdata->irqflags); + + irqflags |= (IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_NO_SUSPEND); + ret = request_threaded_irq(gpio_to_irq(pdata->irq_gpio), NULL, + raydium_ts_interrupt, irqflags, + client->dev.driver->name, raydium_ts); + if (ret < 0) { + dev_err(&client->dev, "[touch]raydium_probe: request irq failed\n"); + goto exit_irq_request_failed; + } + + client->irq = gpio_to_irq(pdata->irq_gpio); + raydium_ts->irq = client->irq; + raydium_ts->irq_desc = irq_to_desc(client->irq); + + /* disable then enable irq to avoid Unbalanced enable warning */ + raydium_irq_control(raydium_ts, DISABLE); + raydium_irq_control(raydium_ts, ENABLE); + + pr_info("[touch]Raydium Touch driver version :0x%04X\n", RAYDIUM_VER); + + /* fw update check */ + ret = raydium_fw_update_check(raydium_ts); + if (ret < 0) { + dev_err(&raydium_ts->client->dev, + "[touch]FW update check failed\n"); + ret = -ENODEV; + goto exit_irq_request_failed; + } + + return 0; + +exit_irq_request_failed: +#ifdef CONFIG_RM_SYSFS_DEBUG + raydium_release_sysfs(client); +#endif /* end of CONFIG_RM_SYSFS_DEBUG */ + +#if defined(CONFIG_DRM) || defined(CONFIG_FB) + raydium_unregister_notifier(raydium_ts); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&raydium_ts->early_suspend); +#endif /* end of CONFIG_FB */ + + cancel_work_sync(&raydium_ts->work); + input_unregister_device(raydium_ts->input_dev); + +exit_input_register_device_failed: + if (!raydium_ts->input_dev) + input_free_device(input_dev); + +exit_input_dev_alloc_failed: +exit_check_i2c: + if (gpio_is_valid(pdata->reset_gpio)) { + gpio_free(pdata->reset_gpio); + pdata->reset_gpio = 0; + } + if (gpio_is_valid(pdata->irq_gpio)) { + gpio_free(pdata->irq_gpio); + pdata->irq_gpio = 0; + } + + destroy_workqueue(raydium_ts->workqueue); + raydium_ts->workqueue = NULL; + + return ret; +} + +extern bool dsi_display_is_panel_enable(int id, int *probe_status, char **pname); + +static void raydium_ready_handler(struct work_struct *work) +{ + struct delayed_work *dw = + container_of(work, struct delayed_work, work); + struct raydium_ts_data *raydium_ts = + container_of(dw, struct raydium_ts_data, ready_work); + struct raydium_ts_platform_data *pdata = raydium_ts->pdata; + int ret, probe_status; + /* CLI display ID is 1 */ + /* 3rd parameter is NULL now, but we might need to start */ + /* retrieving it later if found necessary */ + bool status = dsi_display_is_panel_enable(pdata->ctrl_dsi, &probe_status, NULL); + + if (!status && probe_status == -ENODEV) { + dev_err(&raydium_ts->client->dev, "[touch]touch ic not present\n"); + goto exit_no_panel; + } + + if (!status) { + queue_delayed_work(raydium_ts->workqueue, + &raydium_ts->ready_work, msecs_to_jiffies(500)); + dev_dbg(&raydium_ts->client->dev, "[touch]re-scheduled ready work\n"); + return; + } + + ret = raydium_ts_lateinit(raydium_ts); + + if (!ret) + return; + + dev_err(&raydium_ts->client->dev, "[touch]failed to init ic!!!\n"); + +exit_no_panel: + raydium_ts_remove(raydium_ts->client); +} + +static int raydium_ts_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct raydium_ts_platform_data *pdata; + struct raydium_ts_data *raydium_ts; + int ret = 0; + + if (client->dev.of_node) + { + pdata = devm_kzalloc(&client->dev, sizeof(struct raydium_ts_platform_data), GFP_KERNEL); + if (!pdata) + { + dev_err(&client->dev, "[touch]failed to allocate memory\n"); + return -ENOMEM; + } + + ret = raydium_parse_dt(&client->dev, pdata); + if (ret) + { + dev_err(&client->dev, "[touch]device tree parsing failed\n"); + return ret; + } + } else + { + pdata = client->dev.platform_data; + } + + /* Touch can be bound to display. If that's the case, + * we need to check if control dsi associated with the + * same display name and fail probe otherwise */ + if (pdata->bound_display) { + int probe_status; + char *display_name = NULL; + + dsi_display_is_panel_enable(pdata->ctrl_dsi, &probe_status, &display_name); + if (probe_status == -ENODEV || (display_name && + strncmp(display_name, pdata->bound_display, strlen(pdata->bound_display)))) { + dev_err(&client->dev, "[touch]display binding failed\n"); + return -ENODEV; + } + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + raydium_ts = devm_kzalloc(&client->dev, sizeof(struct raydium_ts_data), GFP_KERNEL); + if (!raydium_ts) + { + dev_err(&client->dev, "[touch]failed to allocate input driver data\n"); + return -ENOMEM; + } + + mutex_init(&raydium_ts->lock); + + i2c_set_clientdata(client, raydium_ts); + raydium_ts->irq_enabled = true; + raydium_ts->irq_wake = false; + + raydium_ts->rst = pdata->reset_gpio; + raydium_ts->client = client; + raydium_ts->pdata = pdata; + raydium_ts->x_max = pdata->x_max - 1; + raydium_ts->y_max = pdata->y_max - 1; + raydium_ts->is_suspend = 0; + raydium_ts->is_sleep = 0; + raydium_ts->is_close = 0; +#ifdef GESTURE_EN + raydium_ts->is_palm = 0; +#endif + raydium_ts->fw_version = 0; + + device_init_wakeup(&client->dev,1); + + ret = raydium_power_init(raydium_ts, true); + if (ret) + { + dev_err(&client->dev, "[touch]raydium_power_init failed\n"); + goto exit_regulator_failed; + } + + ret = raydium_power_on(raydium_ts, true); + if (ret) + { + dev_err(&client->dev, "[touch]raydium_power_on failed\n"); + goto pwr_deinit; + } + +#ifdef MSM_NEW_VER + ret = raydium_ts_pinctrl_init(raydium_ts); + if (!ret && raydium_ts->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. + */ + ret = pinctrl_select_state(raydium_ts->ts_pinctrl, + raydium_ts->pinctrl_state_active); + if (ret < 0) + { + dev_err(&client->dev, "[touch]failed to select pin to active state\n"); + } + } +#endif //end of MSM_NEW_VER + + ret = raydium_gpio_configure(raydium_ts, true); + if (ret < 0) + { + dev_err(&client->dev,"[touch]failed to configure the gpios\n"); + goto err_gpio_req; + } + + INIT_DELAYED_WORK(&raydium_ts->ready_work, raydium_ready_handler); + INIT_WORK(&raydium_ts->work, raydium_work_handler); + raydium_ts->workqueue = create_singlethread_workqueue("raydium_ts"); + + if (!gpio_is_valid(raydium_ts->rst)) { + dev_warn(&client->dev, "[touch]in-cell mode: wait for out of reset\n"); + queue_delayed_work(raydium_ts->workqueue, + &raydium_ts->ready_work, 0); + return 0; + } else + msleep(raydium_ts->pdata->soft_rst_dly); + + ret = raydium_ts_lateinit(raydium_ts); + + if (!ret) + return 0; + +err_gpio_req: +#ifdef MSM_NEW_VER + if (raydium_ts->ts_pinctrl) + { + if (IS_ERR_OR_NULL(raydium_ts->pinctrl_state_release)) + { + devm_pinctrl_put(raydium_ts->ts_pinctrl); + raydium_ts->ts_pinctrl = NULL; + } else + { + ret = pinctrl_select_state(raydium_ts->ts_pinctrl, + raydium_ts->pinctrl_state_release); + if (ret) + { + pr_err("[touch]failed to select relase pinctrl state\n"); + } + } + } +#endif //end of MSM_NEW_VER + raydium_power_on(raydium_ts, false); + +pwr_deinit: + raydium_power_init(raydium_ts, false); + raydium_ts->vcc_i2c = ERR_PTR(-ENODEV); + +exit_regulator_failed: + i2c_set_clientdata(client, NULL); + + return ret; +} + +static int raydium_ts_remove(struct i2c_client *client) +{ + struct raydium_ts_data *raydium_ts; + + raydium_ts = i2c_get_clientdata(client); + if (!raydium_ts) + return 0; + +#if (defined(CONFIG_DRM) || defined(CONFIG_FB)) + raydium_unregister_notifier(raydium_ts); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + unregister_early_suspend(&raydium_ts->early_suspend); +#endif //end of CONFIG_FB + + if (raydium_ts->input_dev) + input_unregister_device(raydium_ts->input_dev); + +#ifdef CONFIG_RM_SYSFS_DEBUG + raydium_release_sysfs(client); +#endif //end of CONFIG_RM_SYSFS_DEBUG + + if (raydium_ts->irq) + free_irq(raydium_ts->irq, raydium_ts); + + if (gpio_is_valid(raydium_ts->pdata->reset_gpio)) + { + gpio_free(raydium_ts->pdata->reset_gpio); + } + if (gpio_is_valid(raydium_ts->pdata->irq_gpio)) + { + gpio_free(raydium_ts->pdata->irq_gpio); + } + + if (raydium_ts->workqueue) + destroy_workqueue(raydium_ts->workqueue); + + if (!IS_ERR(raydium_ts->vcc_i2c)) { + raydium_power_on(raydium_ts, false); + raydium_power_init(raydium_ts, false); + } + + i2c_set_clientdata(client, NULL); + return 0; +} + +static const struct i2c_device_id raydium_ts_id[] = { + {RAYDIUM_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, raydium_ts_id); + +#ifdef CONFIG_OF +static struct of_device_id raydium_match_table[] = { + { .compatible = "raydium,raydium-ts",}, + { }, +}; +#else +#define raydium_match_table NULL +#endif //end of CONFIG_OF + +static struct i2c_driver raydium_ts_driver = { + .probe = raydium_ts_probe, + .remove = raydium_ts_remove, + .id_table = raydium_ts_id, + .driver = { + .name = RAYDIUM_NAME, + .owner = THIS_MODULE, + .of_match_table = raydium_match_table, +#if defined(CONFIG_PM) + .pm = &raydium_ts_pm_ops, +#endif //end of CONFIG_PM + }, +}; + +static int __init raydium_ts_init(void) +{ + int ret; + ret = i2c_add_driver(&raydium_ts_driver); + return ret; +} + +static void __exit raydium_ts_exit(void) +{ + i2c_del_driver(&raydium_ts_driver); +} + +module_init(raydium_ts_init); +module_exit(raydium_ts_exit); + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("Raydium TouchScreen driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/raydium_mmi/raydium_i2c_ts.h b/drivers/input/touchscreen/raydium_mmi/raydium_i2c_ts.h new file mode 100644 index 000000000000..39fb75bc3cdc --- /dev/null +++ b/drivers/input/touchscreen/raydium_mmi/raydium_i2c_ts.h @@ -0,0 +1,141 @@ +#ifndef __LINUX_RAYDIUM_H +#define __LINUX_RAYDIUM_H + +#define RAYDIUM_NAME "raydium_ts" + +#define COORDS_ARR_SIZE 4 +#define I2C_VTG_MIN_UV 1800000 +#define I2C_VTG_MAX_UV 1800000 + +#define RAYDIUM_VER 0x000F + +/* IC timing control arguments */ +#define RAYDIUM_POWERON_DELAY_USEC 500 +#define RAYDIUM_RESET_INTERVAL_MSEC 5 +#define RAYDIUM_RESET_RESTORE_USEC 200 +#define RAYDIUM_RESET_DELAY_MSEC 100 + +/* I2C bus slave address(ID) */ +#define RAYDIUM_I2C_EID (0x5A) +#define RAYDIUM_I2C_NID (0x39) + +/* I2C R/W configuration literal */ +#define RAYDIUM_I2C_WRITE I2C_SMBUS_WRITE +#define RAYDIUM_I2C_READ I2C_SMBUS_READ +#define SYN_I2C_RETRY_TIMES 2 +#define MAX_WRITE_PACKET_SIZE 64 +#define MAX_READ_PACKET_SIZE 64 + +/* PDA address and bit definition*/ +#define RAYDIUM_READ_FT_DATA_CMD 0x2000019C +#define RAYDIUM_GESTURE_STATE_CMD 0x200005F4 // 1byte, disable:0x00 ; enable:0x20 +#define RAYDIUM_GESTURE_DISABLE 0x00 +#define RAYDIUM_GESTURE_ENABLE 0x20 +#define RAYDIUM_GESTURE_RESULT_CMD 0x200005F0 // 4bytes, [0]:ready ; [1]:type ; [2]:direction +#define RAYDIUM_CHECK_I2C_CMD 0x500009BC +#define RAYDIUM_PDA2_CTRL_CMD 0x50000628 +#define RAYDIM_ENABLE_PDA2 0x04 + +/* PDA literal */ +#define MASK_8BIT 0xFF +#define RAYDIUM_I2C_PDA_ADDRESS_LENGTH 4 + +#define PDA_MODE 1 +#define PDA2_MODE 2 + +#define RAYDIUM_I2C_PDA_MODE_DISABLE 0x00 +#define RAYDIUM_I2C_PDA_MODE_ENABLE 0x80 +#define RAYDIUM_I2C_PDA_MODE_WORD_MODE 0x40 // Using byte mode due to data might be not word-aligment +#define RAYDIUM_I2C_PDA_2_MODE_DISABLE 0x20 + +#define RAYDIUM_PALM_MODE_DISABLE 0x00 +#define RAYDIUM_PALM_MODE_ENABLE 0x01 + +#define RAYDIUM_TEST_FW 0x80 +#define RAYDIUM_EXTREME_BURN 0x40 +#define RAYDIUM_BOOTLOADER 0x20 +#define RAYDIUM_FIRMWARE 0x10 +#define RAYDIUM_PARA 0x08 +#define RAYDIUM_COMP 0x04 +#define RAYDIUM_BASELINE 0x02 +#define RAYDIUM_INIT 0x01 + +#define FAIL 0 +#define ERROR -1 +#define SUCCESS 1 + +#define DISABLE 0 +#define ENABLE 1 + +/* PDA2 setting */ +#define MAX_PAGE_AMOUNT 11 // Page 0 ~ Page A + +/* PDA2 address and setting definition*/ +#define RAYDIUM_PDA2_TCH_RPT_STATUS_ADDR 0x00 // only in Page 0 +#define RAYDIUM_PDA2_TCH_RPT_ADDR 0x01 // only in Page 0 +#define RAYDIUM_PDA2_HOST_CMD_ADDR 0x02 // only in Page 0 +#define RAYDIUM_PDA2_PALM_AREA_ADDR 0x03 // only in Page 0 +#define RAYDIUM_PDA2_GESTURE_RPT_ADDR 0x04 // only in Page 0 +#define RAYDIUM_PDA2_PALM_STATUS_ADDR 0x05 // only in Page 0 +#define RAYDIUM_PDA2_FW_VERSION_ADDR 0x06 // only in Page 0 +#define RAYDIUM_PDA2_PANEL_VERSION_ADDR 0x07 // only in Page 0 +#define RAYDIUM_PDA2_DISPLAY_MODE_ADDR 0x08 // only in Page 0 +#define RAYDIUM_PDA2_PDA_CFG_ADDR 0x09 // only in Page 0 +#define RAYDIUM_PDA2_PAGE_ADDR 0x0A // Page 0 ~ Page 9 will be directed to Page 0 +#define RAYDIUM_PDA2_PAGE_0 0x00 +#define RAYDIUM_PDA2_ENABLE_PDA 0x0A // temporary switch to PDA once +#define RAYDIUM_PDA2_2_PDA (MAX_PAGE_AMOUNT + 2) // permanently switch to PDA mode + +/* Raydium host cmd */ +#define RAYDIUM_HOST_CMD_NO_OP 0x00 +#define RAYDIUM_HOST_CMD_PWR_SLEEP 0x30 +#define RAYDIUM_HOST_CMD_CALIBRATION 0x5C +#define RAYDIUM_HOST_CMD_TP_MODE 0x60 +#define RAYDIUM_HOST_CMD_FT_MODE 0x61 + +/* PDA2 literal */ +#define RAYDIUM_I2C_PDA2_PAGE_LENGTH 2 // entry byte + target page byte + +/* Touch report */ +#define MAX_TOUCH_NUM 2 +#define MAX_REPORT_PAKAGE_SIZE 35 +#define MAX_TCH_STATUS_PAKAGE_SIZE 2 +#define MAX_GESTURERESULT_SIZE 4 +#define PRESS_MAX 0xFFFF +#define WIDTH_MAX 0xFFFF +#define SHORT_HIGH_BYTE_SHIFT 8 + +/* FW update literal */ +#define RAYDIUM_FW_BIN_PATH_LENGTH 256 +#define RAYDIUM_FW_MAX_SIZE 0x5600 //20480 //0x5000 +/* FT APK literal */ +#define RAYDIUM_HOST_CMD_POS 0x00 +#define RAYDIUM_FT_CMD_POS 0x01 +#define RAYDIUM_FT_CMD_LENGTH 0x02 + +/* FT APK data type */ +#define RAYDIUM_FT_UPDATE 0x01 + +/*Raydium system flag*/ +#define RAYDIUM_INTERRUPT_FLAG 0x01 +#define RAYDIUM_BOOTLOADER_FLAG 0x02 + +/* define display mode */ +#define ACTIVE_MODE 0x00 +#define AMBIENT_MODE 0x01 +#define SLEEP_MODE 0x02 + +/* Enable sysfs */ +#define CONFIG_RM_SYSFS_DEBUG + +/* Gesture switch */ +//#define GESTURE_EN + +#define MSM_NEW_VER + +#define PINCTRL_STATE_ACTIVE "ts_sub_int_active" +#define PINCTRL_STATE_SUSPEND "ts_sub_int_suspend" +#define PINCTRL_STATE_RELEASE "ts_sub_int_release" + +#endif /*__LINUX_RAYDIUM_H*/ + diff --git a/drivers/input/touchscreen/sec_mmi/Android.mk b/drivers/input/touchscreen/sec_mmi/Android.mk new file mode 100644 index 000000000000..e74d98023635 --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := sec_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_info.ko +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_MMI=y + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +endif +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/sec_mmi/Kbuild b/drivers/input/touchscreen/sec_mmi/Kbuild new file mode 100644 index 000000000000..27d0c327fcf4 --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/Kbuild @@ -0,0 +1,29 @@ +# add -Wall to try to catch everything we can. +# EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/sec_mmi + +ifneq (,$(filter racer% burton%,$(TARGET_PRODUCT))) + EXTRA_CFLAGS += -DCONFIG_DRM_DYNAMIC_REFRESH_RATE +endif + +obj-m := sec_mmi.o +sec_mmi-objs = sec_ts.o sec_ts_fn.o sec_ts_fw.o sec_cmd.o sec_ts_only_vendor.o + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI -DCONFIG_PM + + ifneq ($(filter m y, $(CONFIG_DRM_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_NOTIFICATIONS + endif + ifneq ($(filter m y, $(CONFIG_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_PANEL_NOTIFICATIONS + endif + + sec_mmi-objs += sec_ts_mmi_class.o + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers +else + sec_mmi-objs += sec_ts_mmi.o +endif + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/sec_mmi/Kconfig b/drivers/input/touchscreen/sec_mmi/Kconfig new file mode 100644 index 000000000000..e2598b2e10c9 --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/Kconfig @@ -0,0 +1,22 @@ +# +# Samsung Electronics TOUCH driver configuration +# + +config TOUCHSCREEN_SEC_TS + tristate "Samsung Electronics Touchscreen" + depends on I2C + help + Say Y here if you want support for SEC touchscreen controllers. + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called sec_ts. + +config TOUCHSCREEN_SEC_TS_GLOVEMODE + tristate "Samsung Electronics Touchscreen" + depends on I2C + help + Say Y here if you have a Samsung Electronics Touchscreen and want to enable + support for the built-in touchscreen. + + If unsure, say N. diff --git a/drivers/input/touchscreen/sec_mmi/Makefile b/drivers/input/touchscreen/sec_mmi/Makefile new file mode 100644 index 000000000000..34b09dd815a7 --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean \ No newline at end of file diff --git a/drivers/input/touchscreen/sec_mmi/sec_cmd.c b/drivers/input/touchscreen/sec_mmi/sec_cmd.c new file mode 100644 index 000000000000..9e7c6a9e1781 --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/sec_cmd.c @@ -0,0 +1,559 @@ +/* + * sec_cmd.c - samsung factory command driver + * + * Copyright (C) 2014 Samsung Electronics + * + * 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. + * + */ + +#include "sec_ts.h" + +#if defined USE_SEC_CMD_QUEUE +static void sec_cmd_store_function(struct sec_cmd_data *data); +#endif + +void sec_cmd_set_cmd_exit(struct sec_cmd_data *data) +{ + mutex_lock(&data->cmd_lock); + data->cmd_is_running = false; + mutex_unlock(&data->cmd_lock); + +#ifdef USE_SEC_CMD_QUEUE + mutex_lock(&data->fifo_lock); + if (kfifo_len(&data->cmd_queue)) { + pr_info("%s: do next cmd, left cmd[%d]\n", __func__, + (int)(kfifo_len(&data->cmd_queue) / sizeof(struct command))); + mutex_unlock(&data->fifo_lock); + + /* check lock */ + mutex_lock(&data->cmd_lock); + data->cmd_is_running = true; + mutex_unlock(&data->cmd_lock); + + data->cmd_state = SEC_CMD_STATUS_RUNNING; + sec_cmd_store_function(data); + + } else { + mutex_unlock(&data->fifo_lock); + } +#endif +} + +void sec_cmd_set_default_result(struct sec_cmd_data *data) +{ + char delim = ':'; + memset(data->cmd_result, 0x00, SEC_CMD_RESULT_STR_LEN); + memcpy(data->cmd_result, data->cmd, SEC_CMD_STR_LEN); + strncat(data->cmd_result, &delim, 1); +} + +void sec_cmd_set_cmd_result(struct sec_cmd_data *data, char *buff, int len) +{ + strlcat(data->cmd_result, buff, SEC_CMD_RESULT_STR_LEN); +} + +#ifndef USE_SEC_CMD_QUEUE +static ssize_t sec_cmd_store(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count) +{ + struct sec_cmd_data *data = dev_get_drvdata(dev); + char *cur, *start, *end; + char buff[SEC_CMD_STR_LEN] = { 0 }; + int len, i; + struct sec_cmd *sec_cmd_ptr = NULL; + char delim = ','; + bool cmd_found = false; + int param_cnt = 0; + + if (!data) { + pr_err("%s: No platform data found\n", __func__); + return -EINVAL; + } + + if (strlen(buf) >= SEC_CMD_STR_LEN) { + pr_err("%s: cmd length is over (%s,%d)!!\n", __func__, buf, (int)strlen(buf)); + return -EINVAL; + } + + if (data->cmd_is_running == true) { + pr_err("%s: other cmd is running.\n", __func__); + return -EBUSY; + } + + /* check lock */ + mutex_lock(&data->cmd_lock); + data->cmd_is_running = true; + mutex_unlock(&data->cmd_lock); + + data->cmd_state = SEC_CMD_STATUS_RUNNING; + for (i = 0; i < ARRAY_SIZE(data->cmd_param); i++) + data->cmd_param[i] = 0; + + len = (int)count; + if (*(buf + len - 1) == '\n') + len--; + + memset(data->cmd, 0x00, ARRAY_SIZE(data->cmd)); + memcpy(data->cmd, buf, len); + + cur = strchr(buf, (int)delim); + if (cur) + memcpy(buff, buf, cur - buf); + else + memcpy(buff, buf, len); + + pr_debug("%s: COMMAND = %s\n", __func__, buff); + + /* find command */ + list_for_each_entry(sec_cmd_ptr, &data->cmd_list_head, list) { + if (!strncmp(buff, sec_cmd_ptr->cmd_name, SEC_CMD_STR_LEN)) { + cmd_found = true; + break; + } + } + + /* set not_support_cmd */ + if (!cmd_found) { + list_for_each_entry(sec_cmd_ptr, &data->cmd_list_head, list) { + if (!strncmp("not_support_cmd", sec_cmd_ptr->cmd_name, + SEC_CMD_STR_LEN)) + break; + } + } + + /* parsing parameters */ + if (cur && cmd_found) { + cur++; + start = cur; + memset(buff, 0x00, ARRAY_SIZE(buff)); + + do { + if (*cur == delim || cur - buf == len) { + end = cur; + memcpy(buff, start, end - start); + *(buff + strnlen(buff, ARRAY_SIZE(buff))) = '\0'; + if (kstrtoint(buff, 10, data->cmd_param + param_cnt) < 0) + goto err_out; + start = cur + 1; + memset(buff, 0x00, ARRAY_SIZE(buff)); + param_cnt++; + } + cur++; + } while ((cur - buf <= len) && (param_cnt < SEC_CMD_PARAM_NUM)); + } + + if (cmd_found) { + pr_info("%s: cmd = %s", __func__, sec_cmd_ptr->cmd_name); + for (i = 0; i < param_cnt; i++) { + if (i == 0) + pr_cont(" param ="); + pr_cont(" %d", data->cmd_param[i]); + } + pr_cont("\n"); + } else { + pr_info("%s: cmd = %s(%s)\n", __func__, buff, sec_cmd_ptr->cmd_name); + } + + sec_cmd_ptr->cmd_func(data); + sec_cmd_set_cmd_exit(data); + +err_out: + return count; +} + +#else /* defined USE_SEC_CMD_QUEUE */ +static void sec_cmd_store_function(struct sec_cmd_data *data) +{ + char *cur, *start, *end; + char buff[SEC_CMD_STR_LEN] = { 0 }; + int len, i; + struct sec_cmd *sec_cmd_ptr = NULL; + char delim = ','; + bool cmd_found = false; + int param_cnt = 0; + int ret; + const char *buf; + size_t count; + struct command cmd = {{0}}; + + if (!data) { + pr_err("%s: No platform data found\n", __func__); + return; + } + + mutex_lock(&data->fifo_lock); + if (kfifo_len(&data->cmd_queue)) { + ret = kfifo_out(&data->cmd_queue, &cmd, sizeof(struct command)); + if (!ret) { + pr_err("%s: kfifo_out failed, it seems empty, ret=%d\n", __func__, ret); + mutex_unlock(&data->fifo_lock); + return; + } + } else { + pr_err("%s: left cmd is nothing\n", __func__); + mutex_unlock(&data->fifo_lock); + return; + } + mutex_unlock(&data->fifo_lock); + + buf = cmd.cmd; + count = strlen(buf); + + for (i = 0; i < (int)ARRAY_SIZE(data->cmd_param); i++) + data->cmd_param[i] = 0; + + len = (int)count; + if (*(buf + len - 1) == '\n') + len--; + + memset(data->cmd, 0x00, ARRAY_SIZE(data->cmd)); + memcpy(data->cmd, buf, len); + + cur = strchr(buf, (int)delim); + if (cur) + memcpy(buff, buf, cur - buf); + else + memcpy(buff, buf, len); + + pr_debug("%s: COMMAND : %s\n", __func__, buff); + + /* find command */ + list_for_each_entry(sec_cmd_ptr, &data->cmd_list_head, list) { + if (!strncmp(buff, sec_cmd_ptr->cmd_name, SEC_CMD_STR_LEN)) { + cmd_found = true; + break; + } + } + + /* set not_support_cmd */ + if (!cmd_found) { + list_for_each_entry(sec_cmd_ptr, &data->cmd_list_head, list) { + if (!strncmp("not_support_cmd", sec_cmd_ptr->cmd_name, + SEC_CMD_STR_LEN)) + break; + } + } + + /* parsing parameters */ + if (cur && cmd_found) { + cur++; + start = cur; + memset(buff, 0x00, ARRAY_SIZE(buff)); + + do { + if (*cur == delim || cur - buf == len) { + end = cur; + memcpy(buff, start, end - start); + *(buff + strnlen(buff, ARRAY_SIZE(buff))) = '\0'; + if (kstrtoint(buff, 10, data->cmd_param + param_cnt) < 0) + return; + start = cur + 1; + memset(buff, 0x00, ARRAY_SIZE(buff)); + param_cnt++; + } + cur++; + } while ((cur - buf <= len) && (param_cnt < SEC_CMD_PARAM_NUM)); + } + + if (cmd_found) { + pr_info("%s: cmd = %s", __func__, sec_cmd_ptr->cmd_name); + for (i = 0; i < param_cnt; i++) { + if (i == 0) + pr_cont(" param ="); + pr_cont(" %d", data->cmd_param[i]); + } + pr_cont("\n"); + } else { + pr_info("%s: cmd = %s(%s)\n", __func__, buff, sec_cmd_ptr->cmd_name); + } + + sec_cmd_ptr->cmd_func(data); + +} + +static ssize_t sec_cmd_store(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sec_cmd_data *data = dev_get_drvdata(dev); + struct command cmd = {{0}}; + int queue_size; + + if (!data) { + pr_err("%s: No platform data found\n", __func__); + return -EINVAL; + } + + if (strlen(buf) >= SEC_CMD_STR_LEN) { + pr_err("%s: cmd length is over (%s,%d)!!\n", __func__, buf, (int)strlen(buf)); + return -EINVAL; + } + + strncpy(cmd.cmd, buf, count); + + mutex_lock(&data->fifo_lock); + queue_size = (kfifo_len(&data->cmd_queue) / sizeof(struct command)); + + if (kfifo_avail(&data->cmd_queue) && (queue_size < SEC_CMD_MAX_QUEUE)) { + kfifo_in(&data->cmd_queue, &cmd, sizeof(struct command)); + pr_info("%s: push cmd: %s\n", __func__, cmd.cmd); + } else { + pr_err("%s: cmd_queue is full!!\n", __func__); + + kfifo_reset(&data->cmd_queue); + pr_err("%s: cmd_queue is reset!!\n", __func__); + mutex_unlock(&data->fifo_lock); + + mutex_lock(&data->cmd_lock); + data->cmd_is_running = false; + mutex_unlock(&data->cmd_lock); + + return -ENOSPC; + } + + if (data->cmd_is_running == true) { + pr_err("%s: other cmd is running. Wait until previous cmd is done[%d]\n", + __func__, (int)(kfifo_len(&data->cmd_queue) / sizeof(struct command))); + mutex_unlock(&data->fifo_lock); + return -EBUSY; + } + mutex_unlock(&data->fifo_lock); + + /* check lock */ + mutex_lock(&data->cmd_lock); + data->cmd_is_running = true; + mutex_unlock(&data->cmd_lock); + + data->cmd_state = SEC_CMD_STATUS_RUNNING; + sec_cmd_store_function(data); + + return count; +} +#endif + +static ssize_t sec_cmd_show_status(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sec_cmd_data *data = dev_get_drvdata(dev); + char buff[16] = { 0 }; + + if (!data) { + pr_err("%s: No platform data found\n", __func__); + return -EINVAL; + } + + if (data->cmd_state == SEC_CMD_STATUS_WAITING) + snprintf(buff, sizeof(buff), "WAITING"); + + else if (data->cmd_state == SEC_CMD_STATUS_RUNNING) + snprintf(buff, sizeof(buff), "RUNNING"); + + else if (data->cmd_state == SEC_CMD_STATUS_OK) + snprintf(buff, sizeof(buff), "OK"); + + else if (data->cmd_state == SEC_CMD_STATUS_FAIL) + snprintf(buff, sizeof(buff), "FAIL"); + + else if (data->cmd_state == SEC_CMD_STATUS_NOT_APPLICABLE) + snprintf(buff, sizeof(buff), "NOT_APPLICABLE"); + + pr_debug("%s: %d, %s\n", __func__, data->cmd_state, buff); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); +} + +static ssize_t sec_cmd_show_result(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct sec_cmd_data *data = dev_get_drvdata(dev); + int size; + + if (!data) { + pr_err("%s: No platform data found\n", __func__); + return -EINVAL; + } + + data->cmd_state = SEC_CMD_STATUS_WAITING; + pr_info("%s: %s\n", __func__, data->cmd_result); + size = snprintf(buf, SEC_CMD_RESULT_STR_LEN, "%s\n", data->cmd_result); + + sec_cmd_set_cmd_exit(data); + + return size; +} + +static ssize_t sec_cmd_list_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *data = dev_get_drvdata(dev); + struct sec_cmd *sec_cmd_ptr = NULL; + char *buffer; + char buffer_name[SEC_CMD_STR_LEN]; + ssize_t len; + + buffer = kzalloc(data->cmd_buffer_size + 30, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + snprintf(buffer, 30, "++factory command list++\n"); + + list_for_each_entry(sec_cmd_ptr, &data->cmd_list_head, list) { + if (strncmp(sec_cmd_ptr->cmd_name, "not_support_cmd", 15)) { + snprintf(buffer_name, SEC_CMD_STR_LEN, "%s\n", sec_cmd_ptr->cmd_name); + strncat(buffer, buffer_name, SEC_CMD_STR_LEN); + } + } + + len = snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buffer); + kfree(buffer); + + return len; +} + +static DEVICE_ATTR(cmd, S_IWUSR | S_IWGRP, NULL, sec_cmd_store); +static DEVICE_ATTR(cmd_status, S_IRUGO, sec_cmd_show_status, NULL); +static DEVICE_ATTR(cmd_result, S_IRUGO, sec_cmd_show_result, NULL); +static DEVICE_ATTR(cmd_list, S_IRUGO, sec_cmd_list_show, NULL); + +static struct attribute *sec_fac_attrs[] = { + &dev_attr_cmd.attr, + &dev_attr_cmd_status.attr, + &dev_attr_cmd_result.attr, + &dev_attr_cmd_list.attr, + NULL, +}; + +static struct attribute_group sec_fac_attr_group = { + .attrs = sec_fac_attrs, +}; + +#ifdef CONFIG_SEC_SYSFS +static inline struct device *sec_device_create(struct sec_cmd_data *data, char *dev_name) { + return NULL; +} + +static inline void sec_device_destroy(int devt) { +} +#endif + +int sec_cmd_init(struct sec_cmd_data *data, struct sec_cmd *cmds, + int len, int devt) +{ + const char *dev_name; + int ret, i; + + INIT_LIST_HEAD(&data->cmd_list_head); + + data->cmd_buffer_size = 0; + for (i = 0; i < len; i++) { + list_add_tail(&cmds[i].list, &data->cmd_list_head); + if (cmds[i].cmd_name) + data->cmd_buffer_size += strlen(cmds[i].cmd_name) + 1; + } + + mutex_init(&data->cmd_lock); + + mutex_lock(&data->cmd_lock); + data->cmd_is_running = false; + mutex_unlock(&data->cmd_lock); + +#ifdef USE_SEC_CMD_QUEUE + if (kfifo_alloc(&data->cmd_queue, + SEC_CMD_MAX_QUEUE * sizeof(struct command), GFP_KERNEL)) { + pr_err("%s: failed to alloc queue for cmd\n", __func__); + goto err_alloc_queue; + } + mutex_init(&data->fifo_lock); +#endif + + if (devt == SEC_CLASS_DEVT_TSP) { + dev_name = SEC_CLASS_DEV_NAME_TSP; + + } else if (devt == SEC_CLASS_DEVT_TKEY) { + dev_name = SEC_CLASS_DEV_NAME_TKEY; + + } else if (devt == SEC_CLASS_DEVT_WACOM) { + dev_name = SEC_CLASS_DEV_NAME_WACOM; + + } else { + pr_err("%s: not defined devt=%d\n", __func__, devt); + goto err_get_dev_name; + } + +#ifdef CONFIG_SEC_SYSFS + data->fac_dev = sec_device_create(data, dev_name); +#else + data->fac_dev = device_create(data->sec_class, NULL, devt, data, dev_name); +#endif + if (IS_ERR(data->fac_dev)) { + pr_err("%s: failed to create device for the sysfs\n", __func__); + goto err_sysfs_device; + } + + dev_set_drvdata(data->fac_dev, data); + + ret = sysfs_create_group(&data->fac_dev->kobj, &sec_fac_attr_group); + if (ret < 0) { + pr_err("%s: failed to create sysfs group\n", __func__); + goto err_sysfs_group; + } + + return 0; + + sysfs_remove_group(&data->fac_dev->kobj, &sec_fac_attr_group); +err_sysfs_group: +#ifdef CONFIG_SEC_SYSFS + sec_device_destroy(data->fac_dev->devt); +#else + device_destroy(data->sec_class, devt); +#endif +err_sysfs_device: +err_get_dev_name: +#ifdef USE_SEC_CMD_QUEUE + mutex_destroy(&data->fifo_lock); + kfifo_free(&data->cmd_queue); +err_alloc_queue: +#endif + mutex_destroy(&data->cmd_lock); + list_del(&data->cmd_list_head); + return -ENODEV; +} + +void sec_cmd_exit(struct sec_cmd_data *data, int devt) +{ +#ifdef USE_SEC_CMD_QUEUE + struct command cmd = {{0}}; + int ret; +#endif + + pr_info("%s", __func__); + sysfs_remove_group(&data->fac_dev->kobj, &sec_fac_attr_group); + dev_set_drvdata(data->fac_dev, NULL); +#ifdef CONFIG_SEC_SYSFS + sec_device_destroy(data->fac_dev->devt); +#else + device_destroy(data->sec_class, devt); +#endif +#ifdef USE_SEC_CMD_QUEUE + mutex_lock(&data->fifo_lock); + while (kfifo_len(&data->cmd_queue)) { + ret = kfifo_out(&data->cmd_queue, &cmd, sizeof(struct command)); + if (!ret) { + pr_err("%s: kfifo_out failed, it seems empty, ret=%d\n", __func__, ret); + } + pr_info("%s: remove pending commands: %s", __func__, cmd.cmd); + } + mutex_unlock(&data->fifo_lock); + mutex_destroy(&data->fifo_lock); + kfifo_free(&data->cmd_queue); +#endif + mutex_destroy(&data->cmd_lock); + list_del(&data->cmd_list_head); +} + +MODULE_DESCRIPTION("Samsung factory command"); +MODULE_LICENSE("GPL"); + + diff --git a/drivers/input/touchscreen/sec_mmi/sec_cmd.h b/drivers/input/touchscreen/sec_mmi/sec_cmd.h new file mode 100644 index 000000000000..08132df634ea --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/sec_cmd.h @@ -0,0 +1,92 @@ +#ifndef _SEC_CMD_H_ +#define _SEC_CMD_H_ + +#undef CONFIG_SEC_SYSFS +#undef CONFIG_SEC_FACTORY +#undef USE_SEC_CMD_QUEUE + +#include +#include +#include +#include +#include +#include +#include +#include +#if 0 +#ifdef CONFIG_SEC_SYSFS +//#include +#endif + +#ifndef CONFIG_SEC_FACTORY +#include +#endif + +#ifndef CONFIG_SEC_SYSFS +extern struct class *sec_class; +#endif +#endif +#define SEC_CLASS_DEVT_TSP 10 +#define SEC_CLASS_DEVT_TKEY 11 +#define SEC_CLASS_DEVT_WACOM 12 + +#define SEC_CLASS_DEV_NAME_TSP "tsp" +#define SEC_CLASS_DEV_NAME_TKEY "sec_touchkey" +#define SEC_CLASS_DEV_NAME_WACOM "sec_epen" + +#define SEC_CMD(name, func) .cmd_name = name, .cmd_func = func + +#define SEC_CMD_BUF_SIZE (4096 - 1) +#define SEC_CMD_STR_LEN 256 +#define SEC_CMD_RESULT_STR_LEN (4096 - 1) +#define SEC_CMD_PARAM_NUM 8 + +struct sec_cmd { + struct list_head list; + const char *cmd_name; + void (*cmd_func)(void *device_data); +}; + +enum SEC_CMD_STATUS { + SEC_CMD_STATUS_WAITING = 0, + SEC_CMD_STATUS_RUNNING, // = 1 + SEC_CMD_STATUS_OK, // = 2 + SEC_CMD_STATUS_FAIL, // = 3 + SEC_CMD_STATUS_NOT_APPLICABLE, // = 4 +}; + +#ifdef USE_SEC_CMD_QUEUE +#define SEC_CMD_MAX_QUEUE 10 + +struct command { + char cmd[SEC_CMD_STR_LEN]; +}; +#endif + +struct sec_cmd_data { + struct class *sec_class; + struct device *fac_dev; + struct list_head cmd_list_head; + u8 cmd_state; + char cmd[SEC_CMD_STR_LEN]; + int cmd_param[SEC_CMD_PARAM_NUM]; + char cmd_result[SEC_CMD_RESULT_STR_LEN]; + int cmd_buffer_size; + bool cmd_is_running; + struct mutex cmd_lock; +#ifdef USE_SEC_CMD_QUEUE + struct kfifo cmd_queue; + struct mutex fifo_lock; +#endif +}; + +extern void sec_cmd_set_cmd_exit(struct sec_cmd_data *data); +extern void sec_cmd_set_default_result(struct sec_cmd_data *data); +extern void sec_cmd_set_cmd_result(struct sec_cmd_data *data, char *buff, int len); +extern int sec_cmd_init(struct sec_cmd_data *data, + struct sec_cmd *cmds, int len, int devt); +extern void sec_cmd_exit(struct sec_cmd_data *data, int devt); + +#endif /* _SEC_CMD_H_ */ + + diff --git a/drivers/input/touchscreen/sec_mmi/sec_custom.h b/drivers/input/touchscreen/sec_mmi/sec_custom.h new file mode 100644 index 000000000000..9b12c82e8de6 --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/sec_custom.h @@ -0,0 +1,14 @@ +#ifndef _SEC_CUSTOM_H_ +#define _SEC_CUSTOM_H_ +/* ABS_MT */ +#define ABS_MT_CUSTOM 0x3e +/* SW_ */ +#define SW_GLOVE 0x0f +/* KEY_ */ +#define KEY_SIDE_GESTURE 0x1c6 +#define KEY_BLACK_UI_GESTURE 0x1c7 +#define KEY_SIDE_GESTURE_RIGHT 0x1ca +#define KEY_SIDE_GESTURE_LEFT 0x1cb +#endif /* _SEC_CUSTOM_H_ */ + + diff --git a/drivers/input/touchscreen/sec_mmi/sec_mmi.h b/drivers/input/touchscreen/sec_mmi/sec_mmi.h new file mode 100644 index 000000000000..3b5b99e91a21 --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/sec_mmi.h @@ -0,0 +1,84 @@ +#ifndef _SEC_MMI_H_ +#define _SEC_MMI_H_ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_PANEL_NOTIFICATIONS) +#include +#endif +#if defined(CONFIG_DRM) +#include +extern bool dsi_display_is_panel_enable(int id, int *probe_status, char **pname); +#endif + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +struct sec_ts_data; + +enum sec_mmi_task { + MMI_TASK_INIT = 0, + MMI_TASK_SET_RATE, +}; + +struct sec_mmi_data { + struct sec_ts_data *ts_ptr; + struct i2c_client *i2c_client; + struct device *ts_class_dev; + struct notifier_block panel_nb; + + bool update_refresh_rate; + struct notifier_block freq_nb; + unsigned char refresh_rate; + enum sec_mmi_task task; + + struct delayed_work resume_work; + struct delayed_work work; + + atomic_t touch_stopped; + + bool gpio_config; + bool gestures_enabled; + bool power_off_suspend; + unsigned int reset; + bool usb_detection; + struct work_struct ps_notify_work; + struct notifier_block ps_notif; + bool ps_is_present; + + bool force_calibration; + int ctrl_dsi; + unsigned int build_id; + unsigned int config_id; + char product_id[10]; + char panel_supplier[16]; + + char *image_name; + const char *class_entry_name; + const char *bound_display; + + void (*reset_func)(struct sec_mmi_data *data, int mode); +}; + +int sec_mmi_data_init(struct sec_ts_data *ts, bool enable); + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +int sec_mmi_sysfs_notify(struct sec_ts_data *ts, unsigned char state); +#else +#define DEV_MMI (&data->i2c_client->dev) +#define DEV_TS (&ts->client->dev) +static int inline sec_mmi_sysfs_notify(struct sec_ts_data *ts, unsigned char state) { + return -ENOSYS; +} +#endif + +extern struct class *get_touchscreen_class_ptr(void); +extern void set_touchscreen_class_ptr(struct class *ptr); + +#endif diff --git a/drivers/input/touchscreen/sec_mmi/sec_ts.c b/drivers/input/touchscreen/sec_mmi/sec_ts.c new file mode 100644 index 000000000000..17071e130917 --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/sec_ts.c @@ -0,0 +1,2730 @@ +/* drivers/input/touchscreen/sec_ts.c + * + * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * http://www.samsungsemi.com/ + * + * Core file for Samsung TSC driver + * + * 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. + */ + +struct sec_ts_data *tsp_info; + +#include "sec_ts.h" +#include "sec_mmi.h" +#include + +struct sec_ts_data *ts_dup; + +#ifdef USE_POWER_RESET_WORK +static void sec_ts_reset_work(struct work_struct *work); +#endif +static void sec_ts_read_info_work(struct work_struct *work); + +#ifdef USE_OPEN_CLOSE +static int sec_ts_input_open(struct input_dev *dev); +static void sec_ts_input_close(struct input_dev *dev); +#endif + +void sec_ts_irq_enable(struct sec_ts_data *ts, bool on) +{ + if (on) { + if (ts->irq_enabled) + return; + ts->irq_enabled = true; + enable_irq(ts->client->irq); + } else if (ts->irq_enabled) { + ts->irq_enabled = false; + disable_irq(ts->client->irq); + } +} + +int sec_ts_i2c_write(struct sec_ts_data *ts, u8 reg, u8 *data, int len) +{ + u8 buf[I2C_WRITE_BUFFER_SIZE + 1]; + int ret; + unsigned char retry; + struct i2c_msg msg; + int i; + + if (len > I2C_WRITE_BUFFER_SIZE) { + input_err(true, &ts->client->dev, "%s: len is larger than buffer size\n", __func__); + return -EINVAL; + } + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: POWER_STATUS : OFF\n", __func__); + goto err; + } + + buf[0] = reg; + memcpy(buf + 1, data, len); + + msg.addr = ts->client->addr; + msg.flags = 0; + msg.len = len + 1; + msg.buf = buf; + + mutex_lock(&ts->i2c_mutex); + for (retry = 0; retry < SEC_TS_I2C_RETRY_CNT; retry++) { + ret = i2c_transfer(ts->client->adapter, &msg, 1); + if (ret == 1) + break; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: POWER_STATUS : OFF, retry:%d\n", __func__, retry); + mutex_unlock(&ts->i2c_mutex); + goto err; + } + + usleep_range(1 * 1000, 1 * 1000); + + if (retry > 1) { + input_err(true, &ts->client->dev, "%s: I2C retry %d, ret:%d\n", __func__, retry + 1, ret); + ts->comm_err_count++; + } + } + + mutex_unlock(&ts->i2c_mutex); + + if (retry == SEC_TS_I2C_RETRY_CNT) { + input_err(true, &ts->client->dev, "%s: I2C write over retry limit\n", __func__); + ret = -EIO; +#ifdef USE_POR_AFTER_I2C_RETRY + if (ts->probe_done && !ts->reset_is_on_going) + schedule_delayed_work(&ts->reset_work, msecs_to_jiffies(TOUCH_RESET_DWORK_TIME)); +#endif + } + + if (ts->debug_flag & SEC_TS_DEBUG_PRINT_I2C_WRITE_CMD) { + pr_info("sec_input:i2c_cmd: W: %02X | ", reg); + for (i = 0; i < len; i++) + pr_cont("%02X ", data[i]); + pr_cont("\n"); + } + + if (ret == 1) + return 0; +err: + return -EIO; +} + +int sec_ts_i2c_read(struct sec_ts_data *ts, u8 reg, u8 *data, int len) +{ + u8 buf[4]; + int ret; + unsigned char retry; + struct i2c_msg msg[2]; + int remain = len; + int i; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: POWER_STATUS : OFF\n", __func__); + goto err; + } + + buf[0] = reg; + + msg[0].addr = ts->client->addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = buf; + + msg[1].addr = ts->client->addr; + msg[1].flags = I2C_M_RD; + msg[1].len = len; + msg[1].buf = data; + + mutex_lock(&ts->i2c_mutex); + + if (len <= ts->i2c_burstmax) { + + for (retry = 0; retry < SEC_TS_I2C_RETRY_CNT; retry++) { + ret = i2c_transfer(ts->client->adapter, msg, 2); + if (ret == 2) + break; + usleep_range(1 * 1000, 1 * 1000); + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: POWER_STATUS : OFF, retry:%d\n", __func__, retry); + mutex_unlock(&ts->i2c_mutex); + goto err; + } + + if (retry > 1) { + input_err(true, &ts->client->dev, "%s: I2C retry %d, ret:%d\n", + __func__, retry + 1, ret); + ts->comm_err_count++; + } + } + + } else { + /* + * I2C read buffer is 256 byte. do not support long buffer over than 256. + * So, try to seperate reading data about 256 bytes. + */ + + for (retry = 0; retry < SEC_TS_I2C_RETRY_CNT; retry++) { + ret = i2c_transfer(ts->client->adapter, msg, 1); + if (ret == 1) + break; + usleep_range(1 * 1000, 1 * 1000); + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: POWER_STATUS : OFF, retry:%d\n", __func__, retry); + mutex_unlock(&ts->i2c_mutex); + goto err; + } + + if (retry > 1) { + input_err(true, &ts->client->dev, "%s: I2C retry %d, ret:%d\n", + __func__, retry + 1, ret); + ts->comm_err_count++; + } + } + + do { + if (remain > ts->i2c_burstmax) + msg[1].len = ts->i2c_burstmax; + else + msg[1].len = remain; + + remain -= ts->i2c_burstmax; + + for (retry = 0; retry < SEC_TS_I2C_RETRY_CNT; retry++) { + ret = i2c_transfer(ts->client->adapter, &msg[1], 1); + if (ret == 1) + break; + usleep_range(1 * 1000, 1 * 1000); + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: POWER_STATUS : OFF, retry:%d\n", __func__, retry); + mutex_unlock(&ts->i2c_mutex); + goto err; + } + + if (retry > 1) { + input_err(true, &ts->client->dev, "%s: I2C retry %d, ret:%d\n", + __func__, retry + 1, ret); + ts->comm_err_count++; + } + } + + msg[1].buf += msg[1].len; + + } while (remain > 0); + + } + + mutex_unlock(&ts->i2c_mutex); + + if (retry == SEC_TS_I2C_RETRY_CNT) { + input_err(true, &ts->client->dev, "%s: I2C read over retry limit\n", __func__); + ret = -EIO; +#ifdef USE_POR_AFTER_I2C_RETRY + if (ts->probe_done && !ts->reset_is_on_going) + schedule_delayed_work(&ts->reset_work, msecs_to_jiffies(TOUCH_RESET_DWORK_TIME)); +#endif + + } + + if (ts->debug_flag & SEC_TS_DEBUG_PRINT_I2C_READ_CMD) { + pr_info("sec_input:i2c_cmd: R: %02X | ", reg); + for (i = 0; i < len; i++) + pr_cont("%02X ", data[i]); + pr_cont("\n"); + } + + return ret; + +err: + return -EIO; +} + +static int sec_ts_i2c_write_burst(struct sec_ts_data *ts, u8 *data, int len) +{ + int ret; + int retry; + + mutex_lock(&ts->i2c_mutex); + for (retry = 0; retry < SEC_TS_I2C_RETRY_CNT; retry++) { + ret = i2c_master_send(ts->client, data, len); + if (ret == len) + break; + + usleep_range(1 * 1000, 1 * 1000); + + if (retry > 1) { + input_err(true, &ts->client->dev, "%s: I2C retry %d, ret:%d\n", __func__, retry + 1, ret); + ts->comm_err_count++; + } + } + + mutex_unlock(&ts->i2c_mutex); + if (retry == SEC_TS_I2C_RETRY_CNT) { + input_err(true, &ts->client->dev, "%s: I2C write over retry limit\n", __func__); + ret = -EIO; + } + + return ret; +} + +static int sec_ts_i2c_read_bulk(struct sec_ts_data *ts, u8 *data, int len) +{ + int ret; + unsigned char retry; + int remain = len; + struct i2c_msg msg; + + msg.addr = ts->client->addr; + msg.flags = I2C_M_RD; + msg.len = len; + msg.buf = data; + + mutex_lock(&ts->i2c_mutex); + + do { + if (remain > ts->i2c_burstmax) + msg.len = ts->i2c_burstmax; + else + msg.len = remain; + + remain -= ts->i2c_burstmax; + + for (retry = 0; retry < SEC_TS_I2C_RETRY_CNT; retry++) { + ret = i2c_transfer(ts->client->adapter, &msg, 1); + if (ret == 1) + break; + usleep_range(1 * 1000, 1 * 1000); + + if (retry > 1) { + input_err(true, &ts->client->dev, "%s: I2C retry %d, ret:%d\n", + __func__, retry + 1, ret); + ts->comm_err_count++; + } + } + + if (retry == SEC_TS_I2C_RETRY_CNT) { + input_err(true, &ts->client->dev, "%s: I2C read over retry limit\n", __func__); + ret = -EIO; + + break; + } + msg.buf += msg.len; + + } while (remain > 0); + + mutex_unlock(&ts->i2c_mutex); + + if (ret == 1) + return 0; + + return -EIO; +} +static int sec_ts_read_from_customlib(struct sec_ts_data *ts, u8 *data, int len) +{ + int ret; + + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_CUSTOMLIB_READ_PARAM, data, 2); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to read custom library command\n", __func__); + + ret = sec_ts_i2c_read(ts, SEC_TS_CMD_CUSTOMLIB_READ_PARAM, (u8 *)data, len); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to read custom library command\n", __func__); + + return ret; +} + +#if defined(CONFIG_TOUCHSCREEN_DUMP_MODE) +#include +extern struct tsp_dump_callbacks dump_callbacks; +static struct delayed_work *p_ghost_check; + +static void sec_ts_check_rawdata(struct work_struct *work) +{ + struct sec_ts_data *ts = container_of(work, struct sec_ts_data, ghost_check.work); + + if (ts->tsp_dump_lock == 1) { + input_err(true, &ts->client->dev, "%s: ignored ## already checking..\n", __func__); + return; + } + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: ignored ## IC is power off\n", __func__); + return; + } + + input_info(true, &ts->client->dev, "%s: start ##\n", __func__); + sec_ts_run_rawdata_all(ts, true); + msleep(100); + + input_info(true, &ts->client->dev, "%s: done ##\n", __func__); +} + +static void dump_tsp_log(void) +{ + pr_info("%s: %s: start\n", SEC_TS_I2C_NAME, __func__); + +#ifdef CONFIG_BATTERY_SAMSUNG + if (lpcharge == 1) { + pr_err("%s: %s: ignored ## lpm charging Mode!!\n", SEC_TS_I2C_NAME, __func__); + return; + } +#endif + + if (p_ghost_check == NULL) { + pr_err("%s: %s: ignored ## tsp probe fail!!\n", SEC_TS_I2C_NAME, __func__); + return; + } + schedule_delayed_work(p_ghost_check, msecs_to_jiffies(100)); +} +#endif + + +void sec_ts_delay(unsigned int ms) +{ + if (ms < 20) + usleep_range(ms * 1000, ms * 1000); + else + msleep(ms); +} + +int sec_ts_wait_for_ready(struct sec_ts_data *ts, unsigned int ack) +{ + int rc = -1; + int retry = 0; + u8 tBuff[SEC_TS_EVENT_BUFF_SIZE] = {0,}; + + while (sec_ts_i2c_read(ts, SEC_TS_READ_ONE_EVENT, tBuff, SEC_TS_EVENT_BUFF_SIZE) > 0) { + if (((tBuff[0] >> 2) & 0xF) == TYPE_STATUS_EVENT_INFO) { + if (tBuff[1] == ack) { + rc = 0; + break; + } + } else if (((tBuff[0] >> 2) & 0xF) == TYPE_STATUS_EVENT_VENDOR_INFO) { + if (tBuff[1] == ack) { + rc = 0; + break; + } + } + + if (retry++ > SEC_TS_WAIT_RETRY_CNT) { + input_err(true, &ts->client->dev, "%s: Time Over\n", __func__); + break; + } + sec_ts_delay(20); + } + + input_info(true, &ts->client->dev, + "%s: %02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X [%d]\n", + __func__, tBuff[0], tBuff[1], tBuff[2], tBuff[3], + tBuff[4], tBuff[5], tBuff[6], tBuff[7], retry); + + return rc; +} + +int sec_ts_read_calibration_report(struct sec_ts_data *ts) +{ + int ret; + u8 buf[5] = { 0 }; + + buf[0] = SEC_TS_READ_CALIBRATION_REPORT; + + ret = sec_ts_i2c_read(ts, buf[0], &buf[1], 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to read, %d\n", __func__, ret); + return ret; + } + + input_info(true, &ts->client->dev, "%s: count:%d, pass count:%d, fail count:%d, status:0x%X\n", + __func__, buf[1], buf[2], buf[3], buf[4]); + + return buf[4]; +} + +void sec_ts_reinit(struct sec_ts_data *ts) +{ + u8 w_data[2] = {0x00, 0x00}; + int ret = 0; + + input_info(true, &ts->client->dev, + "%s : charger=0x%x, touch_functions=0x%x, Power mode=0x%x, noise_mode=%d\n", + __func__, ts->charger_mode, ts->touch_functions, + ts->power_status, ts->touch_noise_status); + + ts->touch_noise_status = 0; + + /* charger mode */ + if (ts->charger_mode != SEC_TS_BIT_CHARGER_MODE_NO) { + w_data[0] = ts->charger_mode; + ret = ts->sec_ts_i2c_write(ts, SET_TS_CMD_SET_CHARGER_MODE, (u8 *)&w_data[0], 1); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to send command(0x%x)", + __func__, SET_TS_CMD_SET_CHARGER_MODE); + } + + /* Cover mode */ + if (ts->touch_functions & SEC_TS_BIT_SETFUNC_COVER) { + w_data[0] = ts->cover_cmd; + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_COVERTYPE, (u8 *)&w_data[0], 1); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to send command(0x%x)", + __func__, SEC_TS_CMD_SET_COVERTYPE); + } + + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_TOUCHFUNCTION, (u8 *)&(ts->touch_functions), 2); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to send command(0x%x)", + __func__, SEC_TS_CMD_SET_TOUCHFUNCTION); +#ifdef SEC_TS_SUPPORT_CUSTOMLIB + if (ts->use_customlib) + sec_ts_set_custom_library(ts); +#endif + /* Power mode */ + if (ts->power_status == SEC_TS_STATE_LPM) { + w_data[0] = (ts->lowpower_mode & SEC_TS_MODE_LOWPOWER_FLAG) >> 1; + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_WAKEUP_GESTURE_MODE, (u8 *)&w_data[0], 1); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to send command(0x%x)", + __func__, SEC_TS_CMD_WAKEUP_GESTURE_MODE); + + w_data[0] = TO_LOWPOWER_MODE; + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_POWER_MODE, (u8 *)&w_data[0], 1); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to send command(0x%x)", + __func__, SEC_TS_CMD_SET_POWER_MODE); + + sec_ts_delay(50); + + if (ts->lowpower_mode & SEC_TS_MODE_CUSTOMLIB_AOD) { + int i, ret; + u8 data[10] = {0x02, 0}; + + for (i = 0; i < 4; i++) { + data[i * 2 + 2] = ts->rect_data[i] & 0xFF; + data[i * 2 + 3] = (ts->rect_data[i] >> 8) & 0xFF; + } + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CUSTOMLIB_WRITE_PARAM, &data[0], 10); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to write offset\n", __func__); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CUSTOMLIB_NOTIFY_PACKET, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to send notify\n", __func__); + + } + + } else { + + sec_ts_set_grip_type(ts, ONLY_EDGE_HANDLER); + + if (ts->dex_mode) { + input_info(true, &ts->client->dev, "%s: set dex mode\n", __func__); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_DEX_MODE, &ts->dex_mode, 1); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: failed to set dex mode %x\n", __func__, ts->dex_mode); + } + + if (ts->brush_mode) { + input_info(true, &ts->client->dev, "%s: set brush mode\n", __func__); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_BRUSH_MODE, &ts->brush_mode, 1); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: failed to set brush mode\n", __func__); + } + + if (ts->touchable_area) { + input_info(true, &ts->client->dev, "%s: set 16:9 mode\n", __func__); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_TOUCHABLE_AREA, &ts->touchable_area, 1); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: failed to set 16:9 mode\n", __func__); + } + + } + return; +} + +#define MIN_X 0 +#define MAX_X 1 +#define MIN_Y 2 +#define MAX_Y 3 + +#define STATUS_ID_HOLD_GRIP 0x69 +#define MAX_EVENT_COUNT 32 +static void sec_ts_read_event(struct sec_ts_data *ts) +{ + int ret; + u8 t_id; + u8 event_id; + u8 left_event_count; + u8 read_event_buff[MAX_EVENT_COUNT][SEC_TS_EVENT_BUFF_SIZE] = { { 0 } }; + u8 *event_buff; + struct sec_ts_event_coordinate *p_event_coord; + struct sec_ts_gesture_status *gs; + struct sec_ts_event_status *p_event_status; + int curr_pos; + int remain_event_count = 0; + int pre_ttype = 0; + + if (ts->power_status == SEC_TS_STATE_LPM) { + //wake_lock_timeout(ts->wakelock, msecs_to_jiffies(500)); + + /* waiting for blsp block resuming, if not occurs i2c error */ + ret = wait_for_completion_interruptible_timeout(&ts->resume_done, msecs_to_jiffies(500)); + if (ret == 0) { + input_err(true, &ts->client->dev, "%s: LPM: pm resume is not handled\n", __func__); + return; + } + + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: LPM: -ERESTARTSYS if interrupted, %d\n", __func__, ret); + return; + } + + input_info(true, &ts->client->dev, "%s: run LPM interrupt handler, %d\n", __func__, ret); + /* run lpm interrupt handler */ + } + + ret = t_id = event_id = curr_pos = remain_event_count = 0; + /* repeat READ_ONE_EVENT until buffer is empty(No event) */ + ret = sec_ts_i2c_read(ts, SEC_TS_READ_ONE_EVENT, (u8 *)read_event_buff[0], SEC_TS_EVENT_BUFF_SIZE); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: i2c read one event failed\n", __func__); + return; + } + + if (ts->debug_flag & SEC_TS_DEBUG_PRINT_ONEEVENT) + input_info(true, &ts->client->dev, "ONE: %02X %02X %02X %02X %02X %02X %02X %02X\n", + read_event_buff[0][0], read_event_buff[0][1], + read_event_buff[0][2], read_event_buff[0][3], + read_event_buff[0][4], read_event_buff[0][5], + read_event_buff[0][6], read_event_buff[0][7]); + + if (read_event_buff[0][0] == 0) { + input_info(true, &ts->client->dev, "%s: event buffer is empty\n", __func__); + return; + } + + left_event_count = read_event_buff[0][7] & 0x3F; + remain_event_count = left_event_count; + + if (left_event_count > MAX_EVENT_COUNT - 1) { + input_err(true, &ts->client->dev, "%s: event buffer overflow\n", __func__); + + /* write clear event stack command when read_event_count > MAX_EVENT_COUNT */ + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_CLEAR_EVENT_STACK, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: i2c write clear event failed\n", __func__); + + sec_ts_unlocked_release_all_finger(ts); + + return; + } + + if (left_event_count > 0) { + ret = sec_ts_i2c_read(ts, SEC_TS_READ_ALL_EVENT, (u8 *)read_event_buff[1], + sizeof(u8) * (SEC_TS_EVENT_BUFF_SIZE) * (left_event_count)); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: i2c read one event failed\n", __func__); + return; + } + } + + do { + event_buff = read_event_buff[curr_pos]; + event_id = event_buff[0] & 0x3; + + if (ts->debug_flag & SEC_TS_DEBUG_PRINT_ALLEVENT) + input_info(true, &ts->client->dev, "ALL: %02X %02X %02X %02X %02X %02X %02X %02X\n", + event_buff[0], event_buff[1], event_buff[2], event_buff[3], + event_buff[4], event_buff[5], event_buff[6], event_buff[7]); + + switch (event_id) { + case SEC_TS_STATUS_EVENT: + p_event_status = (struct sec_ts_event_status *)event_buff; + + /* tchsta == 0 && ttype == 0 && eid == 0 : buffer empty */ + if (p_event_status->stype > 0) + input_info(true, &ts->client->dev, "%s: STATUS %x %x %x %x %x %x %x %x\n", __func__, + event_buff[0], event_buff[1], event_buff[2], + event_buff[3], event_buff[4], event_buff[5], + event_buff[6], event_buff[7]); + + /* hold grip change status event */ + if ((p_event_status->status_id == STATUS_ID_HOLD_GRIP) && + ts->plat_data->hold_grip_ctrl) { + sec_mmi_sysfs_notify(ts, p_event_status->status_data_1); + /* no further processing needed */ + break; + } + + /* watchdog reset -> send SENSEON command */ + if ((p_event_status->stype == TYPE_STATUS_EVENT_INFO) && + (p_event_status->status_id == SEC_TS_ACK_BOOT_COMPLETE) && + (p_event_status->status_data_1 == 0x20)) { + + sec_ts_unlocked_release_all_finger(ts); + + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to write Sense_on\n", __func__); + + sec_ts_reinit(ts); + } + + /* event queue full-> all finger release */ + if ((p_event_status->stype == TYPE_STATUS_EVENT_ERR) && + (p_event_status->status_id == SEC_TS_ERR_EVENT_QUEUE_FULL)) { + input_err(true, &ts->client->dev, "%s: IC Event Queue is full\n", __func__); + sec_ts_unlocked_release_all_finger(ts); + } + + if ((p_event_status->stype == TYPE_STATUS_EVENT_ERR) && + (p_event_status->status_id == SEC_TS_ERR_EVENT_ESD)) { + input_err(true, &ts->client->dev, "%s: ESD detected. run reset\n", __func__); +#ifdef USE_RESET_DURING_POWER_ON + schedule_work(&ts->reset_work.work); +#endif + } + + if ((p_event_status->stype == TYPE_STATUS_EVENT_INFO) && + (p_event_status->status_id == SEC_TS_ACK_WET_MODE)) { + ts->wet_mode = p_event_status->status_data_1; + input_info(true, &ts->client->dev, "%s: water wet mode %d\n", + __func__, ts->wet_mode); + if (ts->wet_mode) + ts->wet_count++; + } + + if ((p_event_status->stype == TYPE_STATUS_EVENT_VENDOR_INFO) && + (p_event_status->status_id == SEC_TS_VENDOR_ACK_NOISE_STATUS_NOTI)) { + + ts->touch_noise_status = !!p_event_status->status_data_1; + input_info(true, &ts->client->dev, "%s: TSP NOISE MODE %s[%d]\n", + __func__, ts->touch_noise_status == 0 ? "OFF" : "ON", + p_event_status->status_data_1); + + if (ts->touch_noise_status) + ts->noise_count++; + } + + break; + + case SEC_TS_COORDINATE_EVENT: + if (ts->power_status != SEC_TS_STATE_POWER_ON) { + input_err(true, &ts->client->dev, "%s: device is closed\n", __func__); + break; + } + p_event_coord = (struct sec_ts_event_coordinate *)event_buff; + + t_id = (p_event_coord->tid - 1); + + if (t_id < MAX_SUPPORT_TOUCH_COUNT + MAX_SUPPORT_HOVER_COUNT) { + pre_ttype = ts->coord[t_id].ttype; + ts->coord[t_id].id = t_id; + ts->coord[t_id].action = p_event_coord->tchsta; + ts->coord[t_id].x = (p_event_coord->x_11_4 << 4) | (p_event_coord->x_3_0); + ts->coord[t_id].y = (p_event_coord->y_11_4 << 4) | (p_event_coord->y_3_0); + ts->coord[t_id].z = p_event_coord->z & 0x3F; + ts->coord[t_id].ttype = p_event_coord->ttype_3_2 << 2 | p_event_coord->ttype_1_0 << 0; + ts->coord[t_id].major = p_event_coord->major; + ts->coord[t_id].minor = p_event_coord->minor; + + if (!ts->coord[t_id].palm && (ts->coord[t_id].ttype == SEC_TS_TOUCHTYPE_PALM)) + ts->coord[t_id].palm_count++; + + ts->coord[t_id].palm = (ts->coord[t_id].ttype == SEC_TS_TOUCHTYPE_PALM); + ts->coord[t_id].left_event = p_event_coord->left_event; + + if (ts->coord[t_id].z <= 0) + ts->coord[t_id].z = 1; + + if (ts->plat_data->clip_area) { + if (unlikely(!((ts->coord[t_id].x >= ts->plat_data->clip_area[MIN_X]) + && (ts->coord[t_id].x <= ts->plat_data->clip_area[MAX_X]) + && (ts->coord[t_id].y >= ts->plat_data->clip_area[MIN_Y]) + && (ts->coord[t_id].y <= ts->plat_data->clip_area[MAX_Y])))) { + + input_dbg(true, &ts->client->dev, + "CLIP: tid[%d] type (%02X %02X), action %02X, xy (%02X %02X)\n", + t_id, ts->coord[t_id].ttype, pre_ttype, + ts->coord[t_id].action, ts->coord[t_id].x, ts->coord[t_id].y); + + if (ts->coord[t_id].action == SEC_TS_COORDINATE_ACTION_MOVE) { + /* send release event for the touch entering clip area */ + input_mt_slot(ts->input_dev, t_id); + if (ts->plat_data->support_mt_pressure) + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); + if (ts->touch_count > 0) + ts->touch_count--; + if (ts->touch_count == 0) { + input_report_key(ts->input_dev, BTN_TOUCH, 0); + input_report_key(ts->input_dev, BTN_TOOL_FINGER, 0); + ts->check_multi = 0; + } + } + + /* cancel further processing */ + ts->coord[t_id].action = SEC_TS_COORDINATE_ACTION_NONE; + ts->coord[t_id].mcount = 0; + ts->coord[t_id].palm_count = 0; + break; + } + } + + if ((ts->coord[t_id].ttype == SEC_TS_TOUCHTYPE_NORMAL) + || (ts->coord[t_id].ttype == SEC_TS_TOUCHTYPE_PALM) + || (ts->coord[t_id].ttype == SEC_TS_TOUCHTYPE_WET) + || (ts->coord[t_id].ttype == SEC_TS_TOUCHTYPE_GLOVE)) { + + if (ts->coord[t_id].action == SEC_TS_COORDINATE_ACTION_RELEASE) { + + GET_TIME_OF_DAY(&ts->time_released[t_id]); + + if (ts->time_longest < (ts->time_released[t_id].tv_sec - ts->time_pressed[t_id].tv_sec)) + ts->time_longest = (ts->time_released[t_id].tv_sec - ts->time_pressed[t_id].tv_sec); + + input_mt_slot(ts->input_dev, t_id); + if (ts->plat_data->support_mt_pressure) + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, 0); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 0); + + if (ts->touch_count > 0) + ts->touch_count--; + if (ts->touch_count == 0) { + input_report_key(ts->input_dev, BTN_TOUCH, 0); + input_report_key(ts->input_dev, BTN_TOOL_FINGER, 0); + ts->check_multi = 0; + } + +#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP) + input_info(true, &ts->client->dev, + "%s[R] tID:%d mc:%d tc:%d lx:%d ly:%d v:%02X%02X cal:%02X id(%d,%d) p:%d noise:%x lp:(%x)\n", + ts->dex_name, + t_id, ts->coord[t_id].mcount, ts->touch_count, + ts->coord[t_id].x, ts->coord[t_id].y, + ts->plat_data->img_version_of_ic[2], + ts->plat_data->img_version_of_ic[3], + ts->cal_status, ts->tspid_val, + ts->tspicid_val, ts->coord[t_id].palm_count, + ts->touch_noise_status, ts->lowpower_mode); +#else + input_info(true, &ts->client->dev, + "%s[R] tID:%d mc:%d tc:%d v:%02X%02X cal:%02X id(%d,%d) p:%d noise:%x lp:(%x)\n", + ts->dex_name, + t_id, ts->coord[t_id].mcount, ts->touch_count, + ts->plat_data->img_version_of_ic[2], + ts->plat_data->img_version_of_ic[3], + ts->cal_status, ts->tspid_val, + ts->tspicid_val, ts->coord[t_id].palm_count, + ts->touch_noise_status, ts->lowpower_mode); +#endif + ts->coord[t_id].action = SEC_TS_COORDINATE_ACTION_NONE; + ts->coord[t_id].mcount = 0; + ts->coord[t_id].palm_count = 0; + + + } else if (ts->coord[t_id].action == SEC_TS_COORDINATE_ACTION_PRESS) { + GET_TIME_OF_DAY(&ts->time_pressed[t_id]); + + ts->touch_count++; + ts->all_finger_count++; + + ts->max_z_value = max((unsigned int)ts->coord[t_id].z, ts->max_z_value); + ts->min_z_value = min((unsigned int)ts->coord[t_id].z, ts->min_z_value); + ts->sum_z_value += (unsigned int)ts->coord[t_id].z; + + input_mt_slot(ts->input_dev, t_id); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + input_report_key(ts->input_dev, BTN_TOUCH, 1); + input_report_key(ts->input_dev, BTN_TOOL_FINGER, 1); + + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, ts->coord[t_id].x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, ts->coord[t_id].y); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, + (ts->coord[t_id].major * ts->plat_data->size_to_pixel_multiple_factor) >> 7); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MINOR, + (ts->coord[t_id].minor * ts->plat_data->size_to_pixel_multiple_factor) >> 7); + if (ts->brush_mode) + input_report_abs(ts->input_dev, ABS_MT_CUSTOM, (ts->coord[t_id].z << 1) | ts->coord[t_id].palm); + else + input_report_abs(ts->input_dev, ABS_MT_CUSTOM, (BRUSH_Z_DATA << 1) | ts->coord[t_id].palm); + if (ts->plat_data->support_mt_pressure) + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, ts->coord[t_id].z); + + if ((ts->touch_count > 4) && (ts->check_multi == 0)) { + ts->check_multi = 1; + ts->multi_count++; + } + +#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP) + input_info(true, &ts->client->dev, + "%s[P] tID:%d x:%d y:%d z:%d major:%d minor:%d tc:%d type:%X noise:%x\n", + ts->dex_name, t_id, ts->coord[t_id].x, + ts->coord[t_id].y, ts->coord[t_id].z, + ts->coord[t_id].major, ts->coord[t_id].minor, + ts->touch_count, + ts->coord[t_id].ttype, ts->touch_noise_status); +#else + input_info(true, &ts->client->dev, + "%s[P] tID:%d z:%d major:%d minor:%d tc:%d type:%X noise:%x\n", + ts->dex_name, + t_id, ts->coord[t_id].z, ts->coord[t_id].major, + ts->coord[t_id].minor, ts->touch_count, + ts->coord[t_id].ttype, ts->touch_noise_status); +#endif + } else if (ts->coord[t_id].action == SEC_TS_COORDINATE_ACTION_MOVE) { + if ((ts->coord[t_id].ttype == SEC_TS_TOUCHTYPE_GLOVE) && !ts->touchkey_glove_mode_status) { + ts->touchkey_glove_mode_status = true; + input_report_switch(ts->input_dev, SW_GLOVE, 1); + } else if ((ts->coord[t_id].ttype != SEC_TS_TOUCHTYPE_GLOVE) && ts->touchkey_glove_mode_status) { + ts->touchkey_glove_mode_status = false; + input_report_switch(ts->input_dev, SW_GLOVE, 0); + } + + input_mt_slot(ts->input_dev, t_id); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, 1); + input_report_key(ts->input_dev, BTN_TOUCH, 1); + input_report_key(ts->input_dev, BTN_TOOL_FINGER, 1); + + input_report_abs(ts->input_dev, ABS_MT_POSITION_X, ts->coord[t_id].x); + input_report_abs(ts->input_dev, ABS_MT_POSITION_Y, ts->coord[t_id].y); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, + (ts->coord[t_id].major * ts->plat_data->size_to_pixel_multiple_factor) >> 7); + input_report_abs(ts->input_dev, ABS_MT_TOUCH_MINOR, + (ts->coord[t_id].minor * ts->plat_data->size_to_pixel_multiple_factor) >> 7); + if (ts->brush_mode) + input_report_abs(ts->input_dev, ABS_MT_CUSTOM, (ts->coord[t_id].z << 1) | ts->coord[t_id].palm); + else + input_report_abs(ts->input_dev, ABS_MT_CUSTOM, (BRUSH_Z_DATA << 1) | ts->coord[t_id].palm); + + if (ts->plat_data->support_mt_pressure) + input_report_abs(ts->input_dev, ABS_MT_PRESSURE, ts->coord[t_id].z); + ts->coord[t_id].mcount++; + } else { + input_dbg(true, &ts->client->dev, + "%s: do not support coordinate action(%d)\n", __func__, ts->coord[t_id].action); + } + + if ((ts->coord[t_id].action == SEC_TS_COORDINATE_ACTION_PRESS) + || (ts->coord[t_id].action == SEC_TS_COORDINATE_ACTION_MOVE)) { + + if (ts->coord[t_id].ttype != pre_ttype) { + input_info(true, &ts->client->dev, "%s : tID:%d ttype(%x->%x)\n", + __func__, ts->coord[t_id].id, + pre_ttype, ts->coord[t_id].ttype); + } + } + + } else { + input_dbg(true, &ts->client->dev, + "%s: do not support coordinate type(%d)\n", __func__, ts->coord[t_id].ttype); + } + } else { + input_err(true, &ts->client->dev, "%s: tid(%d) is out of range\n", __func__, t_id); + } + break; + + case SEC_TS_GESTURE_EVENT: + gs = (struct sec_ts_gesture_status *)event_buff; + input_info(true, &ts->client->dev, "%s: GESTURE %x %x %x %x %x %x %x %x\n", __func__, + gs->eid | (gs->stype << 2) | (gs->sf << 6), + gs->gesture_id, gs->gesture_data_1, gs->gesture_data_2, + gs->gesture_data_3, gs->gesture_data_4, gs->reserved_1, + gs->left_event_5_0 | (gs->reserved_2 << 6)); + switch (gs->stype) { + case SEC_TS_GESTURE_CODE_VENDOR: +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + if (ts->imports && ts->imports->report_gesture) { + struct gesture_event_data event; + + input_dbg(true, &ts->client->dev, + "%s: invoke imported report gesture function\n", __func__); + /* extract X and Y coordinates */ + event.evcode = gs->gesture_id; + event.evdata.x = gs->gesture_data_1 + ((gs->gesture_data_3 & 0xf0) << 4); + event.evdata.y = gs->gesture_data_2 + ((gs->gesture_data_3 & 0x0f) << 8); + /* call class method */ + ret = ts->imports->report_gesture(&event); + if (!ret) + PM_WAKEUP_EVENT(ts->gesture_wakelock, 3000); + } +#endif + break; + case SEC_TS_GESTURE_CODE_SPAY: + /* */ + /* Gesture event handling */ + /* */ + break; + case SEC_TS_GESTURE_CODE_DOUBLE_TAP: + /* */ + /* Gesture event handling */ + /* */ + break; + } + +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + input_sync(ts->input_dev); + input_report_key(ts->input_dev, KEY_BLACK_UI_GESTURE, 0); +#endif + break; + default: + input_err(true, &ts->client->dev, "%s: unknown event %x %x %x %x %x %x\n", __func__, + event_buff[0], event_buff[1], event_buff[2], + event_buff[3], event_buff[4], event_buff[5]); + break; + } + + curr_pos++; + remain_event_count--; + } while (remain_event_count >= 0); + + input_sync(ts->input_dev); +} + +static irqreturn_t sec_ts_irq_thread(int irq, void *ptr) +{ + struct sec_ts_data *ts = (struct sec_ts_data *)ptr; + + mutex_lock(&ts->eventlock); + + sec_ts_read_event(ts); + + mutex_unlock(&ts->eventlock); + + return IRQ_HANDLED; +} + +int get_tsp_status(void) +{ + return 0; +} +EXPORT_SYMBOL(get_tsp_status); + +void sec_ts_set_charger(struct sec_ts_data *ts, bool enable) +{ + int ret; + u8 charger_in[] = {0x02}; + u8 charger_out[] = {0x01}; + + if (enable) { + input_info(true, &ts->client->dev, + "sec_ts_set_charger : charger CONNECTED!!\n"); + ret = sec_ts_i2c_write(ts, SET_TS_CMD_SET_CHARGER_MODE, + charger_in, sizeof(charger_in)); + } else { + input_info(true, &ts->client->dev, + "sec_ts_set_charger : charger DISCONNECTED!!\n"); + ret = sec_ts_i2c_write(ts, SET_TS_CMD_SET_CHARGER_MODE, + charger_out, sizeof(charger_out)); + } + if (ret < 0) + input_err(true, &ts->client->dev, + "sec_ts_set_charger: fail to write\n"); +} +EXPORT_SYMBOL(sec_ts_set_charger); + +int sec_ts_glove_mode_enables(struct sec_ts_data *ts, int mode) +{ + int ret; + + if (mode) + ts->touch_functions = (ts->touch_functions | SEC_TS_BIT_SETFUNC_GLOVE | SEC_TS_DEFAULT_ENABLE_BIT_SETFUNC); + else + ts->touch_functions = ((ts->touch_functions & (~SEC_TS_BIT_SETFUNC_GLOVE)) | SEC_TS_DEFAULT_ENABLE_BIT_SETFUNC); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: pwr off, glove:%d, status:%x\n", __func__, + mode, ts->touch_functions); + goto glove_enable_err; + } + + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_TOUCHFUNCTION, (u8 *)&ts->touch_functions, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Failed to send command", __func__); + goto glove_enable_err; + } + + input_info(true, &ts->client->dev, "%s: glove:%d, status:%x\n", __func__, + mode, ts->touch_functions); + + return 0; + +glove_enable_err: + return -EIO; +} +EXPORT_SYMBOL(sec_ts_glove_mode_enables); + +int sec_ts_set_cover_type(struct sec_ts_data *ts, bool enable) +{ + int ret; + + input_info(true, &ts->client->dev, "%s: %d\n", __func__, ts->cover_type); + + + switch (ts->cover_type) { + case SEC_TS_VIEW_WIRELESS: + case SEC_TS_VIEW_COVER: + case SEC_TS_VIEW_WALLET: + case SEC_TS_FLIP_WALLET: + case SEC_TS_LED_COVER: + case SEC_TS_MONTBLANC_COVER: + case SEC_TS_CLEAR_FLIP_COVER: + case SEC_TS_QWERTY_KEYBOARD_EUR: + case SEC_TS_QWERTY_KEYBOARD_KOR: + ts->cover_cmd = (u8)ts->cover_type; + break; + case SEC_TS_CHARGER_COVER: + case SEC_TS_COVER_NOTHING1: + case SEC_TS_COVER_NOTHING2: + default: + ts->cover_cmd = 0; + input_err(true, &ts->client->dev, "%s: not chage touch state, %d\n", + __func__, ts->cover_type); + break; + } + + if (enable) + ts->touch_functions = (ts->touch_functions | SEC_TS_BIT_SETFUNC_COVER | SEC_TS_DEFAULT_ENABLE_BIT_SETFUNC); + else + ts->touch_functions = ((ts->touch_functions & (~SEC_TS_BIT_SETFUNC_COVER)) | SEC_TS_DEFAULT_ENABLE_BIT_SETFUNC); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: pwr off, close:%d, status:%x\n", __func__, + enable, ts->touch_functions); + goto cover_enable_err; + } + + if (enable) { + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_COVERTYPE, &ts->cover_cmd, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Failed to send covertype command: %d", __func__, ts->cover_cmd); + goto cover_enable_err; + } + } + + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_TOUCHFUNCTION, (u8 *)&(ts->touch_functions), 2); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Failed to send command", __func__); + goto cover_enable_err; + } + + input_info(true, &ts->client->dev, "%s: close:%d, status:%x\n", __func__, + enable, ts->touch_functions); + + return 0; + +cover_enable_err: + return -EIO; + + +} +EXPORT_SYMBOL(sec_ts_set_cover_type); + +void sec_ts_set_grip_type(struct sec_ts_data *ts, u8 set_type) +{ + u8 mode = G_NONE; + + input_info(true, &ts->client->dev, "%s: re-init grip(%d), edh:%d, edg:%d, lan:%d\n", __func__, + set_type, ts->grip_edgehandler_direction, ts->grip_edge_range, ts->grip_landscape_mode); + + /* edge handler */ + if (ts->grip_edgehandler_direction != 0) + mode |= G_SET_EDGE_HANDLER; + + if (set_type == GRIP_ALL_DATA) { + /* edge */ + if (ts->grip_edge_range != 60) + mode |= G_SET_EDGE_ZONE; + + /* dead zone */ + if (ts->grip_landscape_mode == 1) /* default 0 mode, 32 */ + mode |= G_SET_LANDSCAPE_MODE; + else + mode |= G_SET_NORMAL_MODE; + } + + if (mode) + set_grip_data_to_ic(ts, mode); + +} + +/* for debugging--------------------------------------------------------------------------------------*/ + +int sec_ts_pinctrl_configure(struct sec_ts_data *ts, bool enable) +{ + int rc = 0; + struct pinctrl_state *state; + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, enable ? "ACTIVE" : "SUSPEND"); + + if (enable) { + state = pinctrl_lookup_state(ts->plat_data->pinctrl, "on_state"); + if (IS_ERR(ts->plat_data->pinctrl)) + input_err(true, &ts->client->dev, "%s: could not get active pinstate\n", __func__); + } else { + state = pinctrl_lookup_state(ts->plat_data->pinctrl, "off_state"); + if (IS_ERR(ts->plat_data->pinctrl)) + input_err(true, &ts->client->dev, "%s: could not get suspend pinstate\n", __func__); + } + + if (!IS_ERR_OR_NULL(state)) { + rc = pinctrl_select_state(ts->plat_data->pinctrl, state); + if (enable && gpio_is_valid(ts->plat_data->rst_gpio)) { + /* pinctrl is not setting RESET gpio high by default */ + /* due to compatibility issues with Synaptics driver */ + gpio_set_value(ts->plat_data->rst_gpio, 1); + input_dbg(true, &ts->client->dev, "%s: rst gpio is %d\n", __func__, gpio_get_value(ts->plat_data->rst_gpio)); + } + } + + return rc; + +} + +static int sec_ts_regulator(struct sec_ts_data *ts, bool get) +{ + const struct sec_ts_plat_data *pdata = ts->plat_data; + struct regulator *regulator_dvdd = NULL; + struct regulator *regulator_avdd = NULL; + int ret = 0; + + if (get) { + regulator_dvdd = regulator_get( + &ts->client->dev, pdata->regulator_dvdd); + if (IS_ERR_OR_NULL(regulator_dvdd)) { + input_err(true, &ts->client->dev, + "%s: Failed to get %s regulator.\n", + __func__, pdata->regulator_dvdd); + ret = PTR_ERR(regulator_dvdd); + goto error; + } + + regulator_avdd = regulator_get( + &ts->client->dev, pdata->regulator_avdd); + if (IS_ERR_OR_NULL(regulator_avdd)) { + input_err(true, &ts->client->dev, + "%s: Failed to get %s regulator.\n", + __func__, pdata->regulator_avdd); + ret = PTR_ERR(regulator_avdd); + goto error; + } + + } else { + regulator_put(ts->regulator_dvdd); + regulator_put(ts->regulator_avdd); + } + +error: + ts->regulator_dvdd = regulator_dvdd; + ts->regulator_avdd = regulator_avdd; + + return ret; +} + +int sec_ts_power(void *data, bool on) +{ + struct sec_ts_data *ts = (struct sec_ts_data *)data; + const struct sec_ts_plat_data *pdata = ts->plat_data; + static bool enabled; + int ret = 0; + + if (enabled == on) + return ret; + + if (on) { + ret = regulator_enable(ts->regulator_dvdd); + if (ret) { + input_err(true, &ts->client->dev, + "%s: Failed to enable dvdd: %d\n", __func__, ret); + goto out; + } + + sec_ts_delay(1); + + ret = regulator_enable(ts->regulator_avdd); + if (ret) { + input_err(true, &ts->client->dev, + "%s: Failed to enable avdd: %d\n", __func__, ret); + goto out; + } + } else { + regulator_disable(ts->regulator_avdd); + sec_ts_delay(4); + regulator_disable(ts->regulator_dvdd); + } + + enabled = on; + +out: + input_err(true, &ts->client->dev, + "%s: %s: %s:%s, %s:%s\n", __func__, on ? "on" : "off", + pdata->regulator_avdd, + regulator_is_enabled(ts->regulator_avdd) ? "on" : "off", + pdata->regulator_dvdd, + regulator_is_enabled(ts->regulator_dvdd) ? "on" : "off"); + + return ret; +} + +static int sec_ts_parse_dt(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct sec_ts_plat_data *pdata = dev->platform_data; + struct device_node *np = dev->of_node; + u32 coords[2]; + u32 finger_size[2]; + u32 clip_area[4]; + int ret = 0; + int count = 0; + u32 ic_match_value; + int lcdtype = 0; + + if (of_property_read_string(np, "sec,regulator_dvdd", &pdata->regulator_dvdd)) { + input_err(true, dev, "%s: Failed to get regulator_dvdd name property\n", __func__); + return -EINVAL; + } + + if (of_property_read_string(np, "sec,regulator_avdd", &pdata->regulator_avdd)) { + input_err(true, dev, "%s: Failed to get regulator_avdd name property\n", __func__); + return -EINVAL; + } + + pdata->tsp_icid = of_get_named_gpio(np, "sec,tsp-icid_gpio", 0); + if (gpio_is_valid(pdata->tsp_icid)) { + input_info(true, dev, "%s: TSP_ICID : %d\n", __func__, gpio_get_value(pdata->tsp_icid)); + if (of_property_read_u32(np, "sec,icid_match_value", &ic_match_value)) { + input_err(true, dev, "%s: Failed to get icid match value\n", __func__); + return -EINVAL; + } + + if (gpio_get_value(pdata->tsp_icid) != ic_match_value) { + input_err(true, dev, "%s: Do not match TSP_ICID\n", __func__); + return -EINVAL; + } + input_info(true, dev, "%s: using tsp-icid gpio %d\n", __func__, pdata->tsp_icid); + } + + pdata->rst_gpio = of_get_named_gpio(np, "sec,rst_gpio", 0); + if (gpio_is_valid(pdata->rst_gpio)) { + ret = gpio_request_one(pdata->rst_gpio, GPIOF_DIR_OUT, "sec,tsp_rst"); + if (ret) { + input_err(true, &client->dev, "%s: Unable to request tsp_rst [%d]\n", __func__, pdata->rst_gpio); + return -EINVAL; + } + if (gpio_is_valid(pdata->rst_gpio)) { + /* pinctrl is not setting RESET gpio high by default */ + /* due to compatibility issues with Synaptics driver */ + gpio_set_value(pdata->rst_gpio, 1); + input_dbg(true, &client->dev, "%s: rst gpio is %d\n", __func__, gpio_get_value(pdata->rst_gpio)); + } + input_info(true, &client->dev, "%s: using rst gpio %d\n", __func__, pdata->rst_gpio); + } + + pdata->tsp_vsync = of_get_named_gpio(np, "sec,tsp_vsync_gpio", 0); + if (gpio_is_valid(pdata->tsp_vsync)) + input_info(true, &client->dev, "%s: vsync %s\n", __func__, + gpio_get_value(pdata->tsp_vsync) ? "disable" : "enable"); + + pdata->irq_gpio = of_get_named_gpio(np, "sec,irq_gpio", 0); + if (gpio_is_valid(pdata->irq_gpio)) { + ret = gpio_request_one(pdata->irq_gpio, GPIOF_DIR_IN, "sec,tsp_int"); + if (ret) { + input_err(true, &client->dev, "%s: Unable to request tsp_int [%d]\n", __func__, pdata->irq_gpio); + return -EINVAL; + } + input_info(true, &client->dev, "%s: using irq gpio %d\n", __func__, pdata->irq_gpio); + } else { + input_err(true, &client->dev, "%s: Failed to get irq gpio\n", __func__); + return -EINVAL; + } + + client->irq = gpio_to_irq(pdata->irq_gpio); + + if (of_property_read_u32(np, "sec,irq_type", &pdata->irq_type)) { + input_err(true, dev, "%s: Failed to get irq_type property\n", __func__); + pdata->irq_type = IRQF_TRIGGER_LOW | IRQF_ONESHOT; + } else { + input_info(true, dev, "%s: irq_type property:%X, %d\n", __func__, + pdata->irq_type, pdata->irq_type); + } + + if (of_property_read_u32(np, "sec,i2c-burstmax", &pdata->i2c_burstmax)) { + input_dbg(false, &client->dev, "%s: Failed to get i2c_burstmax property\n", __func__); + pdata->i2c_burstmax = 256; + } + + if (of_property_read_u32_array(np, "sec,max_coords", coords, 2)) { + input_err(true, &client->dev, "%s: Failed to get max_coords property\n", __func__); + } else { + pdata->max_x = coords[0] - 1; + pdata->max_y = coords[1] - 1; + } + + if (!of_property_read_u32_array(np, "sec,clip-area", clip_area, 4)) { + pdata->clip_area = devm_kzalloc(&client->dev, sizeof(clip_area), GFP_KERNEL); + if (!pdata->clip_area) + return -ENOMEM; + memcpy(pdata->clip_area, clip_area, sizeof(clip_area)); + input_info(true, dev, "%s: clip (%d,%d) (%d,%d)\n", __func__, + clip_area[0], clip_area[1], clip_area[2], clip_area[3]); + } + + if (of_property_read_u32_array(np, "sec,max_finger_size", finger_size, 2)) { + pdata->max_major = 255; + pdata->max_minor = 255; + } else { + pdata->max_major = finger_size[0] - 1; + pdata->max_minor = finger_size[1] - 1; + } + + if (of_property_read_u32(np, "sec,size_to_pixel_multiple_factor", &pdata->size_to_pixel_multiple_factor)) + /* multiple_factor not set in device_tree. Set as default value. + * report major and minor value = raw value * size_to_pixel_multiple_factor / 128 + */ + pdata->size_to_pixel_multiple_factor = 128; + + pdata->tsp_id = of_get_named_gpio(np, "sec,tsp-id_gpio", 0); + if (gpio_is_valid(pdata->tsp_id)) + input_info(true, dev, "%s: TSP_ID : %d\n", __func__, gpio_get_value(pdata->tsp_id)); + + count = of_property_count_strings(np, "sec,firmware_name"); + if (count <= 0) { + pdata->firmware_name = NULL; + } else { + if (gpio_is_valid(pdata->tsp_id)) + of_property_read_string_index(np, "sec,firmware_name", gpio_get_value(pdata->tsp_id), &pdata->firmware_name); + else + of_property_read_string_index(np, "sec,firmware_name", 0, &pdata->firmware_name); + } + + if (!of_property_read_string_index(np, "sec,project_name", 0, &pdata->project_name)) + input_info(true, &client->dev, "%s: using project_name %s\n", __func__, pdata->project_name); + + if (!of_property_read_string_index(np, "sec,project_name", 1, &pdata->model_name)) + input_info(true, &client->dev, "%s: using model_name %s\n", __func__, pdata->model_name); + + pdata->power = sec_ts_power; + + if (of_property_read_u32(np, "sec,always_lpmode", &pdata->always_lpmode) < 0) + pdata->always_lpmode = 0; + + if (of_property_read_u32(np, "sec,bringup", &pdata->bringup) < 0) + pdata->bringup = 0; + + if (of_property_read_u32(np, "sec,mis_cal_check", &pdata->mis_cal_check) < 0) + pdata->mis_cal_check = 0; + + pdata->regulator_boot_on = of_property_read_bool(np, "sec,regulator_boot_on"); + pdata->support_sidegesture = of_property_read_bool(np, "sec,support_sidegesture"); + pdata->support_dex = of_property_read_bool(np, "support_dex_mode"); + pdata->suppression_ctrl = of_property_read_bool(np, "sec,suppression-control"); + pdata->pill_region_ctrl = of_property_read_bool(np, "sec,pill-region-control"); + pdata->hold_distance_ctrl = of_property_read_bool(np, "sec,hold-distance-control"); + pdata->gs_distance_ctrl = of_property_read_bool(np, "sec,gs-distance-control"); + pdata->hold_grip_ctrl = of_property_read_bool(np, "sec,hold-grip-control"); + pdata->poweron_calibration = of_property_read_bool(np, "sec,poweron-calibration"); + +#ifdef CONFIG_SEC_FACTORY + pdata->support_mt_pressure = true; +#endif + + input_err(true, &client->dev, "%s: i2c buffer limit: %d, lcd_id:%06X, bringup:%d, FW:%s(%d), id:%d,%d, mis_cal:%d dex:%d, gesture:%d\n", + __func__, pdata->i2c_burstmax, lcdtype, pdata->bringup, pdata->firmware_name, + count, pdata->tsp_id, pdata->tsp_icid, pdata->mis_cal_check, + pdata->support_dex, pdata->support_sidegesture); + return ret; +} + +#ifdef SEC_TS_SUPPORT_CUSTOMLIB +int sec_ts_set_custom_library(struct sec_ts_data *ts) +{ + u8 data[3] = { 0 }; + int ret; + + input_err(true, &ts->client->dev, "%s: Custom Library (0x%02x)\n", + __func__, ts->lowpower_mode); + + data[2] = ts->lowpower_mode; + + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_CUSTOMLIB_WRITE_PARAM, &data[0], 3); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to Custom Library\n", __func__); + + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_CUSTOMLIB_NOTIFY_PACKET, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to send NOTIFY Custom Library\n", __func__); + + return ret; +} + +int sec_ts_check_custom_library(struct sec_ts_data *ts) +{ + u8 data[10] = { 0 }; + int ret = -1; + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_CUSTOMLIB_GET_INFO, &data[0], 10); + + input_info(true, &ts->client->dev, + "%s: (%d) %c%c%c%c, || %02X, %02X, %02X, %02X, || %02X, %02X\n", + __func__, ret, data[0], data[1], data[2], data[3], data[4], + data[5], data[6], data[7], data[8], data[9]); + + /* compare model name with device tree */ + if (ts->plat_data->model_name) + ret = strncmp(data, ts->plat_data->model_name, 4); + + if (ret == 0) + ts->use_customlib = true; + else + ts->use_customlib = false; + + input_err(true, &ts->client->dev, "%s: use %s\n", + __func__, ts->use_customlib? "CUSTOMLIB" : "VENDOR"); + + return ret; +} +#endif + +static void sec_ts_set_input_prop(struct sec_ts_data *ts, struct input_dev *dev, u8 propbit) +{ + static char sec_ts_phys[64] = { 0 }; + + snprintf(sec_ts_phys, sizeof(sec_ts_phys), "%s/input1", + dev->name); + dev->phys = sec_ts_phys; + dev->id.bustype = BUS_I2C; + dev->dev.parent = &ts->client->dev; + + set_bit(EV_SYN, dev->evbit); + set_bit(EV_KEY, dev->evbit); + set_bit(EV_ABS, dev->evbit); + set_bit(EV_SW, dev->evbit); + set_bit(BTN_TOUCH, dev->keybit); + set_bit(BTN_TOOL_FINGER, dev->keybit); + set_bit(KEY_BLACK_UI_GESTURE, dev->keybit); +#ifdef SEC_TS_SUPPORT_TOUCH_KEY + if (ts->plat_data->support_mskey) { + int i; + + for (i = 0 ; i < ts->plat_data->num_touchkey ; i++) + set_bit(ts->plat_data->touchkey[i].keycode, dev->keybit); + + set_bit(EV_LED, dev->evbit); + set_bit(LED_MISC, dev->ledbit); + } +#endif + if (ts->plat_data->support_sidegesture) { + set_bit(KEY_SIDE_GESTURE, dev->keybit); + set_bit(KEY_SIDE_GESTURE_RIGHT, dev->keybit); + set_bit(KEY_SIDE_GESTURE_LEFT, dev->keybit); + } + set_bit(propbit, dev->propbit); + set_bit(KEY_HOMEPAGE, dev->keybit); + + input_set_capability(dev, EV_SW, SW_GLOVE); + + input_set_abs_params(dev, ABS_MT_POSITION_X, 0, ts->plat_data->max_x, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, ts->plat_data->max_y, 0, 0); + input_set_abs_params(dev, ABS_MT_TOUCH_MAJOR, 0, ts->plat_data->max_major, 0, 0); + input_set_abs_params(dev, ABS_MT_TOUCH_MINOR, 0, ts->plat_data->max_minor, 0, 0); + input_set_abs_params(dev, ABS_MT_CUSTOM, 0, 0xFFFFFFFF, 0, 0); + if (ts->plat_data->support_mt_pressure) + input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); + + if (propbit == INPUT_PROP_POINTER) + input_mt_init_slots(dev, MAX_SUPPORT_TOUCH_COUNT, INPUT_MT_POINTER); + else + input_mt_init_slots(dev, MAX_SUPPORT_TOUCH_COUNT, INPUT_MT_DIRECT); + + input_set_drvdata(dev, ts); +} + +void sec_ts_sense_on(struct sec_ts_data *ts) +{ + int ret; + + ts->touch_functions |= SEC_TS_DEFAULT_ENABLE_BIT_SETFUNC; + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_TOUCHFUNCTION, + (u8 *)&ts->touch_functions, 2); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: Failed to send touch func_mode command", __func__); + /* Sense_on */ + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: fail to write Sense_on\n", __func__); +} + +static int sec_ts_read_fw_info(struct sec_ts_data *ts) +{ + unsigned char data[13] = { 0 }; + int ret; + + memset(data, 0x0, 3); + ret = sec_ts_i2c_read(ts, SEC_TS_READ_ID, data, 3); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read device id(%d)\n", + __func__, ret); + return ret; + } + + input_info(true, &ts->client->dev, + "%s: %X, %X, %X\n", + __func__, data[0], data[1], data[2]); + memset(data, 0x0, 11); + ret = sec_ts_i2c_read(ts, SEC_TS_READ_PANEL_INFO, data, 11); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read sub id(%d)\n", + __func__, ret); + return ret; + } + + input_info(true, &ts->client->dev, + "%s: nTX:%X, nRX:%X, rY:%d, rX:%d\n", + __func__, data[8], data[9], + (data[2] << 8) | data[3], (data[0] << 8) | data[1]); + + /* Set X,Y Resolution from IC information. */ + if (((data[0] << 8) | data[1]) > 0) + ts->plat_data->max_x = ((data[0] << 8) | data[1]) - 1; + + if (((data[2] << 8) | data[3]) > 0) + ts->plat_data->max_y = ((data[2] << 8) | data[3]) - 1; + + ts->tx_count = data[8]; + ts->rx_count = data[9]; + + if (ts->pFrame) + kfree(ts->pFrame); + + ts->pFrame = kzalloc(ts->tx_count * ts->rx_count * 2, GFP_KERNEL); + if (!ts->pFrame) { + input_err(true, &ts->client->dev, + "%s: failed to alloc frame\n", __func__); + return -ENOMEM; + } + + data[0] = 0; + ret = sec_ts_i2c_read(ts, SEC_TS_READ_BOOT_STATUS, data, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read sub id(%d)\n", + __func__, ret); + return ret; + } + + input_info(true, &ts->client->dev, + "%s: STATUS : %X\n", + __func__, data[0]); + + memset(data, 0x0, 4); + ret = sec_ts_i2c_read(ts, SEC_TS_READ_TS_STATUS, data, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read sub id(%d)\n", + __func__, ret); + return ret; + } + + input_info(true, &ts->client->dev, + "%s: TOUCH STATUS : %02X, %02X, %02X, %02X\n", + __func__, data[0], data[1], data[2], data[3]); + ret = sec_ts_i2c_read(ts, SEC_TS_CMD_SET_TOUCHFUNCTION, (u8 *)&(ts->touch_functions), 2); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read touch functions(%d)\n", + __func__, ret); + return ret; + } + + input_info(true, &ts->client->dev, + "%s: Functions : %02X\n", + __func__, ts->touch_functions); + + return ret; +} + +static void sec_ts_read_hw_info(struct sec_ts_data *ts) +{ + unsigned char result; + unsigned char buffer[5] = { 0 }; + bool valid_firmware_integrity = false; + int ret; + + ret = sec_ts_i2c_read(ts, SEC_TS_READ_DEVICE_ID, buffer, 5); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: failed to read device ID(%d)\n", __func__, ret); + else { + memcpy(ts->device_id, buffer, sizeof(ts->device_id)); + input_info(true, &ts->client->dev, + "%s: TOUCH DEVICE ID : %02X, %02X, %02X, %02X, %02X\n", __func__, + buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]); + } + + ret = sec_ts_i2c_read(ts, SEC_TS_READ_FIRMWARE_INTEGRITY, &result, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to integrity check (%d)\n", __func__, ret); + } else { + if (result & 0x80) { + valid_firmware_integrity = true; + } else if (result & 0x40) { + valid_firmware_integrity = false; + input_err(true, &ts->client->dev, + "%s: invalid firmware (0x%x)\n", __func__, result); + } else { + valid_firmware_integrity = false; + input_err(true, &ts->client->dev, + "%s: invalid integrity result (0x%x)\n", __func__, result); + } + } + + memset(buffer, 0, sizeof(buffer)); + ret = sec_ts_i2c_read(ts, SEC_TS_READ_BOOT_STATUS, &buffer[0], 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read sub id(%d)\n", + __func__, ret); + } else { + ret = sec_ts_i2c_read(ts, SEC_TS_READ_TS_STATUS, &buffer[1], 4); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to touch status(%d)\n", + __func__, ret); + } + } + input_info(true, &ts->client->dev, + "%s: TOUCH STATUS : %02X || %02X, %02X, %02X, %02X\n", + __func__, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4]); + + if (buffer[0] == SEC_TS_STATUS_BOOT_MODE) { + ts->checksum_result = 1; + ts->fw_invalid = true; + } + + if ((buffer[0] == SEC_TS_STATUS_APP_MODE && + buffer[2] == TOUCH_SYSTEM_MODE_FLASH) || + (valid_firmware_integrity == false)) + ts->fw_invalid = true; +} + +int sec_ts_integrity_check(struct sec_ts_data *ts) +{ + int ret = 0; + + ret = sec_ts_read_fw_info(ts); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail to read information 0x%x\n", __func__, ret); + return -EIO; + } + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + if (ts->plat_data->poweron_calibration) { +#else + if (ts->force_calibration) { +#endif + sec_ts_irq_enable(ts, false); + ret = sec_ts_execute_force_calibration(ts, OFFSET_CAL_SET); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to calibrate", __func__); + sec_ts_irq_enable(ts, true); + } + + ts->touch_functions |= SEC_TS_DEFAULT_ENABLE_BIT_SETFUNC; + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_TOUCHFUNCTION, (u8 *)&ts->touch_functions, 2); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to send touch func_mode command", __func__); + + schedule_delayed_work(&ts->work_read_info, msecs_to_jiffies(50)); + + return 0; +} + +static int sec_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct sec_ts_data *ts; + struct sec_ts_plat_data *pdata; + int ret = 0; + + input_info(true, &client->dev, "%s\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + input_err(true, &client->dev, "%s: EIO err!\n", __func__); + return -EIO; + } + + if (client->dev.of_node && !mmi_device_is_available(client->dev.of_node)) { + input_err(true, &client->dev, "%s mmi: device not supported\n", __func__); + return -ENODEV; + } + + /* parse dt */ + if (client->dev.of_node) { + pdata = devm_kzalloc(&client->dev, + sizeof(struct sec_ts_plat_data), GFP_KERNEL); + + if (!pdata) { + input_err(true, &client->dev, "%s: Failed to allocate platform data\n", __func__); + goto error_allocate_pdata; + } + + client->dev.platform_data = pdata; + + ret = sec_ts_parse_dt(client); + if (ret) { + input_err(true, &client->dev, "%s: Failed to parse dt\n", __func__); + goto error_allocate_mem; + } + } else { + pdata = client->dev.platform_data; + if (!pdata) { + input_err(true, &client->dev, "%s: No platform data found\n", __func__); + goto error_allocate_pdata; + } + } + + /* init TS static elements */ + ts = kzalloc(sizeof(struct sec_ts_data), GFP_KERNEL); + if (!ts) + goto error_allocate_mem; + + ts->client = client; + ts->dev = &client->dev; + ts->plat_data = pdata; + ts->crc_addr = 0x0001FE00; + ts->fw_addr = 0x00002000; + ts->para_addr = 0x18000; + ts->flash_page_size = SEC_TS_FW_BLK_SIZE_DEFAULT; + ts->sec_ts_i2c_read = sec_ts_i2c_read; + ts->sec_ts_i2c_write = sec_ts_i2c_write; + ts->sec_ts_i2c_write_burst = sec_ts_i2c_write_burst; + ts->sec_ts_i2c_read_bulk = sec_ts_i2c_read_bulk; + ts->i2c_burstmax = pdata->i2c_burstmax; + + if (!pdata->power) { + input_err(true, &client->dev, "%s: No power contorl found\n", __func__); + goto error_allocate_mem; + } + + ret = sec_ts_regulator(ts, true); + if (ret) { + input_err(true, &ts->client->dev, "%s: regulator err!\n", __func__); + goto err_allocate_input_dev; + } + + pdata->pinctrl = devm_pinctrl_get(&client->dev); + if (IS_ERR(pdata->pinctrl)) + input_err(true, &client->dev, "%s: could not get pinctrl\n", __func__); + +#ifdef USE_POWER_RESET_WORK + INIT_DELAYED_WORK(&ts->reset_work, sec_ts_reset_work); +#endif + INIT_DELAYED_WORK(&ts->work_read_info, sec_ts_read_info_work); + + i2c_set_clientdata(client, ts); + + if (gpio_is_valid(ts->plat_data->tsp_id)) + ts->tspid_val = gpio_get_value(ts->plat_data->tsp_id); + + if (gpio_is_valid(ts->plat_data->tsp_icid)) + ts->tspicid_val = gpio_get_value(ts->plat_data->tsp_icid); + + ts->input_dev = input_allocate_device(); + if (!ts->input_dev) { + input_err(true, &ts->client->dev, "%s: allocate device err!\n", __func__); + ret = -ENOMEM; + goto err_allocate_input_dev; + } + + if (ts->plat_data->support_dex) { + ts->input_dev_pad = input_allocate_device(); + if (!ts->input_dev_pad) { + input_err(true, &ts->client->dev, "%s: allocate device err!\n", __func__); + ret = -ENOMEM; + goto err_allocate_input_dev_pad; + } + } + + ts->touch_count = 0; + ts->sec_ts_read_customlib = sec_ts_read_from_customlib; + + ts->max_z_value = 0; + ts->min_z_value = 0xFFFFFFFF; + ts->sum_z_value = 0; + + mutex_init(&ts->lock); + mutex_init(&ts->device_mutex); + mutex_init(&ts->i2c_mutex); + mutex_init(&ts->eventlock); + mutex_init(&ts->modechange); + + PM_WAKEUP_REGISTER(ts->dev, ts->wakelock, "tsp_wakelock"); + if (!ts->wakelock) { + input_err(true, &ts->client->dev, + "%s: allocate wakeup source err!\n", __func__); + ret = -ENOMEM; + goto err_register_wakelock; + } + PM_WAKEUP_REGISTER(ts->dev, ts->gesture_wakelock, "tsp_gesture_wakelock"); + if (!ts->gesture_wakelock) { + input_err(true, &ts->client->dev, + "%s: allocate gesture wakeup source err!\n", __func__); + ret = -ENOMEM; + goto err_register_gesture_wakelock; + } + init_completion(&ts->resume_done); + complete_all(&ts->resume_done); + + if (pdata->always_lpmode) + ts->lowpower_mode |= SEC_TS_MODE_CUSTOMLIB_FORCE_KEY; + else + ts->lowpower_mode &= ~SEC_TS_MODE_CUSTOMLIB_FORCE_KEY; + + input_info(true, &client->dev, "%s: init resource\n", __func__); + + sec_ts_pinctrl_configure(ts, true); + + /* power enable */ + sec_ts_power(ts, true); + if (!pdata->regulator_boot_on) + sec_ts_delay(70); + ts->power_status = SEC_TS_STATE_POWER_ON; + ts->external_factory = false; + + sec_ts_wait_for_ready(ts, SEC_TS_ACK_BOOT_COMPLETE); + + input_info(true, &client->dev, "%s: power enable\n", __func__); + + sec_ts_read_hw_info(ts); +#if 0 +#ifdef SEC_TS_FW_UPDATE_ON_PROBE + ret = sec_ts_firmware_update_on_probe(ts, force_update); + if (ret < 0) + goto err_init; +#else + input_info(true, &ts->client->dev, "%s: fw update on probe disabled!\n", __func__); +#endif +#endif + if (ts->plat_data->support_dex) { + ts->input_dev_pad->name = "sec_touchpad"; + sec_ts_set_input_prop(ts, ts->input_dev_pad, INPUT_PROP_POINTER); + + ret = input_register_device(ts->input_dev_pad); + if (ret) { + input_err(true, &ts->client->dev, "%s: Unable to register %s input device\n", __func__, ts->input_dev_pad->name); + goto err_input_pad_register_device; + } + } + ts->dex_name = ""; + + input_info(true, &ts->client->dev, "%s: request_irq = %d\n", __func__, client->irq); + + ret = request_threaded_irq(client->irq, NULL, sec_ts_irq_thread, + ts->plat_data->irq_type, SEC_TS_I2C_NAME, ts); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Unable to request threaded irq\n", __func__); + goto err_irq; + } + + /* prevent unbalanced irq enable */ + ts->irq_enabled = true; + + /* need remove below resource @ remove driver */ +#if 1 //!defined(CONFIG_SAMSUNG_PRODUCT_SHIP) + sec_ts_raw_device_init(ts); +#endif + sec_ts_fn_init(ts); + +#ifdef SEC_TS_SUPPORT_CUSTOMLIB + sec_ts_check_custom_library(ts); + if (ts->use_customlib) + sec_ts_set_custom_library(ts); + +#endif + + ret = sec_mmi_data_init(ts, true); + if (ret) + goto err_mmi_data; + + sec_ts_set_input_prop(ts, ts->input_dev, INPUT_PROP_DIRECT); +#ifdef USE_OPEN_CLOSE + ts->input_dev->open = sec_ts_input_open; + ts->input_dev->close = sec_ts_input_close; +#endif + ts->input_dev_touch = ts->input_dev; + + ret = input_register_device(ts->input_dev); + if (ret) { + input_err(true, &ts->client->dev, "%s: Unable to register %s input device\n", __func__, ts->input_dev->name); + goto err_input_register_device; + } + + device_init_wakeup(&client->dev, true); + +#if defined(CONFIG_TOUCHSCREEN_DUMP_MODE) + dump_callbacks.inform_dump = dump_tsp_log; + INIT_DELAYED_WORK(&ts->ghost_check, sec_ts_check_rawdata); + p_ghost_check = &ts->ghost_check; +#endif + + ts_dup = ts; + ts->probe_done = true; + + input_err(true, &ts->client->dev, "%s: done\n", __func__); + input_log_fix(); + + return 0; + +err_mmi_data: + sec_ts_fn_remove(ts); + free_irq(client->irq, ts); +err_irq: + if (ts->plat_data->support_dex) { + input_unregister_device(ts->input_dev_pad); + ts->input_dev_pad = NULL; + } +err_input_register_device: + kfree(ts->pFrame); + PM_WAKEUP_UNREGISTER(ts->gesture_wakelock); +err_register_gesture_wakelock: + PM_WAKEUP_UNREGISTER(ts->wakelock); +err_register_wakelock: + sec_ts_power(ts, false); + if (ts->plat_data->support_dex) { + if (ts->input_dev_pad) + input_free_device(ts->input_dev_pad); + } +err_input_pad_register_device: + ts->input_dev = NULL; + ts->input_dev_touch = NULL; +err_allocate_input_dev_pad: + if (ts->input_dev) + input_free_device(ts->input_dev); +err_allocate_input_dev: + kfree(ts); + +error_allocate_mem: + if (gpio_is_valid(pdata->irq_gpio)) + gpio_free(pdata->irq_gpio); + if (gpio_is_valid(pdata->tsp_id)) + gpio_free(pdata->tsp_id); + if (gpio_is_valid(pdata->tsp_icid)) + gpio_free(pdata->tsp_icid); + +error_allocate_pdata: + if (ret == -ECONNREFUSED) + sec_ts_delay(100); + ret = -ENODEV; +#ifdef CONFIG_TOUCHSCREEN_DUMP_MODE + p_ghost_check = NULL; +#endif + ts_dup = NULL; + input_err(true, &client->dev, "%s: failed(%d)\n", __func__, ret); + input_log_fix(); + return ret; +} + +void sec_ts_unlocked_release_all_finger(struct sec_ts_data *ts) +{ + int i; + + for (i = 0; i < MAX_SUPPORT_TOUCH_COUNT; i++) { + input_mt_slot(ts->input_dev, i); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); + + if ((ts->coord[i].action == SEC_TS_COORDINATE_ACTION_PRESS) || + (ts->coord[i].action == SEC_TS_COORDINATE_ACTION_MOVE)) { + + ts->coord[i].action = SEC_TS_COORDINATE_ACTION_RELEASE; + input_info(true, &ts->client->dev, + "%s: [RA] tID:%d mc:%d tc:%d v:%02X%02X cal:%02X id(%d,%d) p:%d\n", + __func__, i, ts->coord[i].mcount, ts->touch_count, + ts->plat_data->img_version_of_ic[2], + ts->plat_data->img_version_of_ic[3], + ts->cal_status, ts->tspid_val, + ts->tspicid_val, ts->coord[i].palm_count); + + GET_TIME_OF_DAY(&ts->time_released[i]); + + if (ts->time_longest < (ts->time_released[i].tv_sec - ts->time_pressed[i].tv_sec)) + ts->time_longest = (ts->time_released[i].tv_sec - ts->time_pressed[i].tv_sec); + } + + ts->coord[i].mcount = 0; + ts->coord[i].palm_count = 0; + + } + + input_mt_slot(ts->input_dev, 0); + + input_report_key(ts->input_dev, BTN_TOUCH, false); + input_report_key(ts->input_dev, BTN_TOOL_FINGER, false); + input_report_switch(ts->input_dev, SW_GLOVE, false); + ts->touchkey_glove_mode_status = false; + ts->touch_count = 0; + ts->check_multi = 0; + + if (ts->plat_data->support_sidegesture) { + input_report_key(ts->input_dev, KEY_SIDE_GESTURE, 0); + input_report_key(ts->input_dev, KEY_SIDE_GESTURE_LEFT, 0); + input_report_key(ts->input_dev, KEY_SIDE_GESTURE_RIGHT, 0); + } + + input_report_key(ts->input_dev, KEY_HOMEPAGE, 0); + input_sync(ts->input_dev); + +} + +void sec_ts_locked_release_all_finger(struct sec_ts_data *ts) +{ + int i; + + mutex_lock(&ts->eventlock); + + for (i = 0; i < MAX_SUPPORT_TOUCH_COUNT; i++) { + input_mt_slot(ts->input_dev, i); + input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false); + + if ((ts->coord[i].action == SEC_TS_COORDINATE_ACTION_PRESS) || + (ts->coord[i].action == SEC_TS_COORDINATE_ACTION_MOVE)) { + + ts->coord[i].action = SEC_TS_COORDINATE_ACTION_RELEASE; + input_info(true, &ts->client->dev, + "%s: [RA] tID:%d mc: %d tc:%d, v:%02X%02X, cal:%X id(%d,%d), p:%d\n", + __func__, i, ts->coord[i].mcount, ts->touch_count, + ts->plat_data->img_version_of_ic[2], + ts->plat_data->img_version_of_ic[3], + ts->cal_status, ts->tspid_val, ts->tspicid_val, ts->coord[i].palm_count); + + GET_TIME_OF_DAY(&ts->time_released[i]); + + if (ts->time_longest < (ts->time_released[i].tv_sec - ts->time_pressed[i].tv_sec)) + ts->time_longest = (ts->time_released[i].tv_sec - ts->time_pressed[i].tv_sec); + } + + ts->coord[i].mcount = 0; + ts->coord[i].palm_count = 0; + + } + + input_mt_slot(ts->input_dev, 0); + + input_report_key(ts->input_dev, BTN_TOUCH, false); + input_report_key(ts->input_dev, BTN_TOOL_FINGER, false); + input_report_switch(ts->input_dev, SW_GLOVE, false); + ts->touchkey_glove_mode_status = false; + ts->touch_count = 0; + ts->check_multi = 0; + + if (ts->plat_data->support_sidegesture) { + input_report_key(ts->input_dev, KEY_SIDE_GESTURE, 0); + input_report_key(ts->input_dev, KEY_SIDE_GESTURE_LEFT, 0); + input_report_key(ts->input_dev, KEY_SIDE_GESTURE_RIGHT, 0); + } + + input_report_key(ts->input_dev, KEY_HOMEPAGE, 0); + input_sync(ts->input_dev); + + mutex_unlock(&ts->eventlock); + +} + +#ifdef USE_POWER_RESET_WORK +static void sec_ts_reset_work(struct work_struct *work) +{ + struct sec_ts_data *ts = container_of(work, struct sec_ts_data, + reset_work.work); + int ret; + + if (ts->reset_is_on_going) { + input_err(true, &ts->client->dev, "%s: reset is ongoing\n", __func__); + return; + } + + mutex_lock(&ts->modechange); + PM_STAY_AWAKE(ts->wakelock); + + ts->reset_is_on_going = true; + input_info(true, &ts->client->dev, "%s\n", __func__); + + sec_ts_stop_device(ts); + + sec_ts_delay(30); + + ret = sec_ts_start_device(ts); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to reset, ret:%d\n", __func__, ret); + ts->reset_is_on_going = false; + cancel_delayed_work(&ts->reset_work); + schedule_delayed_work(&ts->reset_work, msecs_to_jiffies(TOUCH_RESET_DWORK_TIME)); + mutex_unlock(&ts->modechange); + + PM_RELAX(ts->wakelock); + + return; + } + // FIXME: + // if (ts->input_dev_touch->disabled) { + input_err(true, &ts->client->dev , "%s: call input_close\n", __func__); + + if (ts->lowpower_mode) { + ret = sec_ts_set_lowpowermode(ts, TO_LOWPOWER_MODE); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to reset, ret:%d\n", __func__, ret); + ts->reset_is_on_going = false; + cancel_delayed_work(&ts->reset_work); + schedule_delayed_work(&ts->reset_work, msecs_to_jiffies(TOUCH_RESET_DWORK_TIME)); + mutex_unlock(&ts->modechange); + PM_RELAX(ts->wakelock); + return; + } + } else { + sec_ts_stop_device(ts); + } + + if ((ts->lowpower_mode & SEC_TS_MODE_CUSTOMLIB_AOD) && ts->use_customlib) { + int i, ret; + u8 data[10] = {0x02, 0}; + + for (i = 0; i < 4; i++) { + data[i * 2 + 2] = ts->rect_data[i] & 0xFF; + data[i * 2 + 3] = (ts->rect_data[i] >> 8) & 0xFF; + } + + sec_ts_irq_enable(ts, false); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CUSTOMLIB_WRITE_PARAM, &data[0], 10); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to write offset\n", __func__); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CUSTOMLIB_NOTIFY_PACKET, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to send notify\n", __func__); + sec_ts_irq_enable(ts, true); + } + // FIXME: + // } + ts->reset_is_on_going = false; + mutex_unlock(&ts->modechange); + + PM_RELAX(ts->wakelock); +} +#endif + +static void sec_ts_read_info_work(struct work_struct *work) +{ + struct sec_ts_data *ts = container_of(work, struct sec_ts_data, + work_read_info.work); + char para = TO_TOUCH_MODE; + int ret; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + /* run self-test */ + sec_ts_irq_enable(ts, false); + execute_selftest(ts, false); + sec_ts_irq_enable(ts, true); + + input_info(true, &ts->client->dev, "%s: %02X %02X %02X %02X\n", + __func__, ts->ito_test[0], ts->ito_test[1] + , ts->ito_test[2], ts->ito_test[3]); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_POWER_MODE, ¶, 1); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to set\n", __func__); + + sec_ts_delay(350); + + input_log_fix(); + + sec_ts_run_rawdata_all(ts, false); + ts->info_work_done = true; +} + + +int sec_ts_set_lowpowermode(struct sec_ts_data *ts, u8 mode) +{ + int ret; + int retrycnt = 0; + u8 data; + char para = 0; + + input_err(true, &ts->client->dev, "%s: %s(%X)\n", __func__, + mode == TO_LOWPOWER_MODE ? "ENTER" : "EXIT", ts->lowpower_mode); + + if (mode) { + #ifdef SEC_TS_SUPPORT_CUSTOMLIB + if (ts->use_customlib) { + ret = sec_ts_set_custom_library(ts); + if (ret < 0) + goto i2c_error; + } + #endif + + //data = (ts->lowpower_mode & SEC_TS_MODE_LOWPOWER_FLAG) >> 1; + data = 0x1; + input_err(true, &ts->client->dev, "%s: setting gesture mode (%X)\n", + __func__, data); + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_WAKEUP_GESTURE_MODE, &data, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Failed to set\n", __func__); + goto i2c_error; + } + } + +retry_pmode: + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_POWER_MODE, &mode, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed\n", __func__); + goto i2c_error; + } + + sec_ts_delay(50); + + ret = sec_ts_i2c_read(ts, SEC_TS_CMD_SET_POWER_MODE, ¶, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read power mode failed!\n", __func__); + goto i2c_error; + } else { + input_info(true, &ts->client->dev, "%s: power mode - write(%d) read(%d)\n", __func__, mode, para); + } + + if (mode != para) { + retrycnt++; + if (retrycnt < 5) + goto retry_pmode; + } + + if (mode) { + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_CLEAR_EVENT_STACK, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: i2c write clear event failed\n", __func__); + goto i2c_error; + } + } + + sec_ts_locked_release_all_finger(ts); + + if (device_may_wakeup(&ts->client->dev)) { + if (mode) + enable_irq_wake(ts->client->irq); + else + disable_irq_wake(ts->client->irq); + } + + if (mode == TO_LOWPOWER_MODE) + ts->power_status = SEC_TS_STATE_LPM; + else + ts->power_status = SEC_TS_STATE_POWER_ON; + +i2c_error: + input_info(true, &ts->client->dev, "%s: end %d\n", __func__, ret); + + return ret; +} + +#ifdef USE_OPEN_CLOSE +static int sec_ts_input_open(struct input_dev *dev) +{ + struct sec_ts_data *ts = input_get_drvdata(dev); + int ret; + + if (!ts->info_work_done) { + input_err(true, &ts->client->dev, "%s not finished info work\n", __func__); + return 0; + } + + mutex_lock(&ts->modechange); + + ts->input_closed = false; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + if (ts->power_status == SEC_TS_STATE_LPM) { +#ifdef USE_RESET_EXIT_LPM + schedule_delayed_work(&ts->reset_work, msecs_to_jiffies(TOUCH_RESET_DWORK_TIME)); +#else + sec_ts_set_lowpowermode(ts, TO_TOUCH_MODE); +#endif + } else { + ret = sec_ts_start_device(ts); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Failed to start device\n", __func__); + } + + /* because edge and dead zone will recover soon */ + sec_ts_set_grip_type(ts, ONLY_EDGE_HANDLER); + + mutex_unlock(&ts->modechange); + + return 0; +} + +static void sec_ts_input_close(struct input_dev *dev) +{ + struct sec_ts_data *ts = input_get_drvdata(dev); + + if (!ts->info_work_done) { + input_err(true, &ts->client->dev, "%s not finished info work\n", __func__); + return; + } + + mutex_lock(&ts->modechange); + + ts->input_closed = true; + + input_info(true, &ts->client->dev, "%s\n", __func__); + +#ifdef USE_POWER_RESET_WORK + cancel_delayed_work(&ts->reset_work); +#endif + +#ifndef CONFIG_SEC_FACTORY + ts->lowpower_mode |= SEC_TS_MODE_CUSTOMLIB_FORCE_KEY; +#endif + if (ts->lowpower_mode) { + sec_ts_set_lowpowermode(ts, TO_LOWPOWER_MODE); + } else { + sec_ts_stop_device(ts); + } + + mutex_unlock(&ts->modechange); +} +#endif + +static int sec_ts_remove(struct i2c_client *client) +{ + struct sec_ts_data *ts = i2c_get_clientdata(client); + + input_info(true, &ts->client->dev, "%s\n", __func__); + + cancel_delayed_work_sync(&ts->work_read_info); + flush_delayed_work(&ts->work_read_info); + + disable_irq_nosync(ts->client->irq); + free_irq(ts->client->irq, ts); + input_info(true, &ts->client->dev, "%s: irq disabled\n", __func__); + +#ifdef USE_POWER_RESET_WORK + cancel_delayed_work_sync(&ts->reset_work); + flush_delayed_work(&ts->reset_work); + + input_info(true, &ts->client->dev, "%s: flush queue\n", __func__); + +#endif + + sec_ts_fn_remove(ts); + +#ifdef CONFIG_TOUCHSCREEN_DUMP_MODE + p_ghost_check = NULL; +#endif + device_init_wakeup(&client->dev, false); + PM_WAKEUP_UNREGISTER(ts->gesture_wakelock); + PM_WAKEUP_UNREGISTER(ts->wakelock); + + ts->lowpower_mode = false; + ts->probe_done = false; + + if (ts->plat_data->support_dex) { + input_mt_destroy_slots(ts->input_dev_pad); + input_unregister_device(ts->input_dev_pad); + } + + ts->input_dev = ts->input_dev_touch; + input_mt_destroy_slots(ts->input_dev); + input_unregister_device(ts->input_dev); + + ts->input_dev_pad = NULL; + ts->input_dev = NULL; + ts->input_dev_touch = NULL; + ts_dup = NULL; + ts->plat_data->power(ts, false); + + sec_ts_pinctrl_configure(ts, false); + sec_ts_regulator(ts, false); + + kfree(ts); + return 0; +} + +static void sec_ts_shutdown(struct i2c_client *client) +{ + struct sec_ts_data *ts = i2c_get_clientdata(client); + + input_info(true, &ts->client->dev, "%s\n", __func__); + + sec_ts_remove(client); +} + +int sec_ts_stop_device(struct sec_ts_data *ts) +{ + input_info(true, &ts->client->dev, "%s\n", __func__); + + mutex_lock(&ts->device_mutex); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: already power off\n", __func__); + goto out; + } + + disable_irq(ts->client->irq); + + ts->power_status = SEC_TS_STATE_POWER_OFF; + + sec_ts_locked_release_all_finger(ts); + + ts->plat_data->power(ts, false); + + if (ts->plat_data->enable_sync) + ts->plat_data->enable_sync(false); + + sec_ts_pinctrl_configure(ts, false); + +out: + mutex_unlock(&ts->device_mutex); + return 0; +} + +int sec_ts_start_device(struct sec_ts_data *ts) +{ + int ret = -1; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + sec_ts_pinctrl_configure(ts, true); + + mutex_lock(&ts->device_mutex); + + if (ts->power_status == SEC_TS_STATE_POWER_ON) { + input_err(true, &ts->client->dev, "%s: already power on\n", __func__); + goto out; + } + + sec_ts_locked_release_all_finger(ts); + + ts->plat_data->power(ts, true); + sec_ts_delay(70); + ts->power_status = SEC_TS_STATE_POWER_ON; + ts->touch_noise_status = 0; + + ret = sec_ts_wait_for_ready(ts, SEC_TS_ACK_BOOT_COMPLETE); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: Failed to wait_for_ready\n", __func__); + goto err; + } + + if (ts->plat_data->enable_sync) + ts->plat_data->enable_sync(true); + + if (ts->flip_enable) { + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_COVERTYPE, &ts->cover_cmd, 1); + if (ret < 0) + goto err; + + ts->touch_functions = ts->touch_functions | SEC_TS_BIT_SETFUNC_COVER; + input_info(true, &ts->client->dev, + "%s: cover cmd write type:%d, mode:%x, ret:%d\n", + __func__, ts->touch_functions, ts->cover_cmd, ret); + } else { + ts->touch_functions = (ts->touch_functions & (~SEC_TS_BIT_SETFUNC_COVER)); + input_info(true, &ts->client->dev, + "%s: cover open, not send cmd\n", __func__); + } + + ts->touch_functions = ts->touch_functions | SEC_TS_DEFAULT_ENABLE_BIT_SETFUNC; + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SET_TOUCHFUNCTION, (u8 *)&ts->touch_functions, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: Failed to send touch function command\n", __func__); + goto err; + } + + #ifdef SEC_TS_SUPPORT_CUSTOMLIB + if (ts->use_customlib) { + ret = sec_ts_set_custom_library(ts); + if (ret < 0) + goto err; + } + #endif + + sec_ts_set_grip_type(ts, ONLY_EDGE_HANDLER); + + if (ts->dex_mode) { + input_info(true, &ts->client->dev, "%s: set dex mode\n", __func__); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_DEX_MODE, &ts->dex_mode, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to set dex mode %x\n", __func__, ts->dex_mode); + goto err; + } + } + + if (ts->brush_mode) { + input_info(true, &ts->client->dev, "%s: set brush mode\n", __func__); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_BRUSH_MODE, &ts->brush_mode, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to set brush mode\n", __func__); + goto err; + } + } + + if (ts->touchable_area) { + input_info(true, &ts->client->dev, "%s: set 16:9 mode\n", __func__); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_TOUCHABLE_AREA, &ts->touchable_area, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to set 16:9 mode\n", __func__); + goto err; + } + } + +err: + /* Sense_on */ + ret = sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to write Sense_on\n", __func__); + + sec_ts_irq_enable(ts, true); + +out: + mutex_unlock(&ts->device_mutex); + return ret; +} + +#if defined(CONFIG_PM) +static int sec_ts_pm_suspend(struct device *dev) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + input_dbg(true, &ts->client->dev, "%s:Into PM suspend\n", __func__); + +#if !defined(CONFIG_DRM) + if (ts->lowpower_mode) + reinit_completion(&ts->resume_done); +#endif + + if (ts->power_status == SEC_TS_STATE_LPM) { + input_dbg(true, &ts->client->dev, "%s: Disable-irq\n", __func__); + sec_ts_irq_enable(ts, false); + } + return 0; +} + +static int sec_ts_pm_suspend_noirq(struct device *dev) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + input_dbg(true, &ts->client->dev, "%s: Into PM suspend_noirq\n", __func__); + if (ts->power_status == SEC_TS_STATE_LPM) { + input_dbg(true, &ts->client->dev, "%s: Enable-irq\n", __func__); + sec_ts_irq_enable(ts, true); + } + return 0; +} + +static int sec_ts_pm_resume(struct device *dev) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + input_dbg(true, &ts->client->dev, "%s: Into PM resume\n", __func__); + if (ts->power_status == SEC_TS_STATE_LPM) { + input_dbg(true, &ts->client->dev, "%s: Enable-irq\n", __func__); + sec_ts_irq_enable(ts, true); + } + +#if !defined(CONFIG_DRM) + if (ts->lowpower_mode) + complete_all(&ts->resume_done); +#endif + return 0; +} + +static int sec_ts_pm_resume_noirq(struct device *dev) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + input_dbg(true, &ts->client->dev, "%s: Into PM resume_noirq\n", __func__); + if (ts->power_status == SEC_TS_STATE_LPM) { + input_dbg(true, &ts->client->dev, "%s: Disable-irq\n", __func__); + sec_ts_irq_enable(ts, false); + } + return 0; +} +#endif + +static const struct i2c_device_id sec_ts_id[] = { + { SEC_TS_I2C_NAME, 0 }, + { }, +}; + +#if defined(CONFIG_PM) +static const struct dev_pm_ops sec_ts_dev_pm_ops = { + .suspend = sec_ts_pm_suspend, + .suspend_noirq = sec_ts_pm_suspend_noirq, + .resume = sec_ts_pm_resume, + .resume_noirq = sec_ts_pm_resume_noirq, +}; +#endif + +#ifdef CONFIG_OF +static const struct of_device_id sec_ts_match_table[] = { + { .compatible = "sec,sec_ts",}, + { }, +}; +#else +#define sec_ts_match_table NULL +#endif + +static struct i2c_driver sec_ts_driver = { + .probe = sec_ts_probe, + .remove = sec_ts_remove, + .shutdown = sec_ts_shutdown, + .id_table = sec_ts_id, + .driver = { + .owner = THIS_MODULE, + .name = SEC_TS_I2C_NAME, +#ifdef CONFIG_OF + .of_match_table = sec_ts_match_table, +#endif +#if defined(CONFIG_PM) + .pm = &sec_ts_dev_pm_ops, +#endif + }, +}; + +static int __init sec_ts_init(void) +{ +#ifdef CONFIG_BATTERY_SAMSUNG + if (lpcharge == 1) { + pr_err("%s: Do not load driver due to : lpm %d\n", __func__, lpcharge); + return -ENODEV; + } +#endif + pr_err("%s\n", __func__); + + return i2c_add_driver(&sec_ts_driver); +} + +static void __exit sec_ts_exit(void) +{ + i2c_del_driver(&sec_ts_driver); +} + +MODULE_AUTHOR("Hyobae, Ahn"); +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +MODULE_DESCRIPTION("Samsung Electronics TouchScreen driver"); +MODULE_LICENSE("GPL"); + +module_init(sec_ts_init); +module_exit(sec_ts_exit); diff --git a/drivers/input/touchscreen/sec_mmi/sec_ts.h b/drivers/input/touchscreen/sec_mmi/sec_ts.h new file mode 100644 index 000000000000..4f11e95dea70 --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/sec_ts.h @@ -0,0 +1,910 @@ +/* drivers/input/touchscreen/sec_ts.h + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * http://www.samsungsemi.com/ + * + * Core file for Samsung TSC driver + * + * 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. + */ + +#ifndef __SEC_TS_H__ +#define __SEC_TS_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +#include +#endif + +#include "sec_cmd.h" + +#ifdef CONFIG_INPUT_BOOSTER +#include +#endif + +#ifndef ABS_MT_CUSTOM +#include "sec_custom.h" +#endif + +#ifndef input_info +#define input_info(f, d, fmt, args...) dev_info(d, fmt, ##args) +#endif + +#ifndef input_err +#define input_err(f, d, fmt, args...) dev_err(d, fmt, ##args) +#endif + +#ifndef input_dbg +#define input_dbg(f, d, fmt, args...) dev_dbg(d, fmt, ##args) +#endif + +#ifndef input_raw_info +#define input_raw_info(f, d, fmt, args...) dev_info(d, fmt, ##args) +#endif + +#ifndef input_raw_frame_info +#ifdef INPUT_RAW_FRAME_INFO +#define input_raw_frame_info(f, d, fmt, args...) dev_info(d, fmt, ##args) +#else +#define input_raw_frame_info(f, d, fmt, args...) dev_dbg(d, fmt, ##args) +#endif +#endif + +#define SEC_TS_I2C_NAME "sec_ts" +#define SEC_TS_DEVICE_NAME "SEC_TS" + +#undef USE_EXTRA_INPUT_FEATURES +#undef USE_OPEN_CLOSE +#undef USE_RESET_DURING_POWER_ON +#undef USE_RESET_EXIT_LPM +#undef USE_POR_AFTER_I2C_RETRY +#undef USER_OPEN_DWORK +#undef USE_PRESSURE_SENSOR +#undef PAT_CONTROL + +#if defined(USE_RESET_DURING_POWER_ON) || defined(USE_POR_AFTER_I2C_RETRY) || defined(USE_RESET_EXIT_LPM) +#define USE_POWER_RESET_WORK +#endif + +#define TOUCH_RESET_DWORK_TIME 10 +#define BRUSH_Z_DATA 63 /* for ArtCanvas */ + +#define MASK_1_BITS 0x0001 +#define MASK_2_BITS 0x0003 +#define MASK_3_BITS 0x0007 +#define MASK_4_BITS 0x000F +#define MASK_5_BITS 0x001F +#define MASK_6_BITS 0x003F +#define MASK_7_BITS 0x007F +#define MASK_8_BITS 0x00FF + +/* support feature */ +//#define SEC_TS_SUPPORT_CUSTOMLIB /* support user defined library */ + +#define TYPE_STATUS_EVENT_CMD_DRIVEN 0 +#define TYPE_STATUS_EVENT_ERR 1 +#define TYPE_STATUS_EVENT_INFO 2 +#define TYPE_STATUS_EVENT_USER_INPUT 3 +#define TYPE_STATUS_EVENT_CUSTOMLIB_INFO 6 +#define TYPE_STATUS_EVENT_VENDOR_INFO 7 +#define TYPE_STATUS_CODE_SAR 0x28 + +#define BIT_STATUS_EVENT_CMD_DRIVEN(a) (a << TYPE_STATUS_EVENT_CMD_DRIVEN) +#define BIT_STATUS_EVENT_ERR(a) (a << TYPE_STATUS_EVENT_ERR) +#define BIT_STATUS_EVENT_INFO(a) (a << TYPE_STATUS_EVENT_INFO) +#define BIT_STATUS_EVENT_USER_INPUT(a) (a << TYPE_STATUS_EVENT_USER_INPUT) +#define BIT_STATUS_EVENT_VENDOR_INFO(a) (a << TYPE_STATUS_EVENT_VENDOR_INFO) + +#define DO_FW_CHECKSUM (1 << 0) +#define DO_PARA_CHECKSUM (1 << 1) +#define MAX_SUPPORT_TOUCH_COUNT 10 +#define MAX_SUPPORT_HOVER_COUNT 1 + +#define SEC_TS_EVENTID_HOVER 10 + +#define SEC_TS_DEFAULT_FW_NAME "tsp_sec/sec_hero.fw" +#define SEC_TS_DEFAULT_BL_NAME "tsp_sec/s6smc41_blupdate_img_REL.bin" +#define SEC_TS_DEFAULT_PARA_NAME "tsp_sec/s6smc41_para_REL_DGA0_V0106_150114_193317.bin" +#define SEC_TS_DEFAULT_UMS_FW "/sdcard/Firmware/TSP/lsi.bin" +#define SEC_TS_DEFAULT_FFU_FW "ffu_tsp.bin" +#define SEC_TS_MAX_FW_PATH 64 +#define SEC_TS_FW_BLK_SIZE_MAX (512) +#define SEC_TS_FW_BLK_SIZE_DEFAULT (512) +#define SEC_TS_SELFTEST_REPORT_SIZE 80 + +#define I2C_WRITE_BUFFER_SIZE (256 - 1)//10 + +#define SEC_TS_FW_HEADER_SIGN 0x53494654 +#define SEC_TS_FW_CHUNK_SIGN 0x53434654 + +#define SEC_TS_FW_UPDATE_ON_PROBE + +#define AMBIENT_CAL 0 +#define OFFSET_CAL_SDC 1 +#define OFFSET_CAL_SET 2 +#define PRESSURE_CAL 3 + +#define SEC_TS_SKIPTSP_DUTY 100 + +#define SEC_TS_NVM_OFFSET_FAC_RESULT 0 +#define SEC_TS_NVM_OFFSET_CAL_COUNT 1 +#define SEC_TS_NVM_OFFSET_DISASSEMBLE_COUNT 2 +#define SEC_TS_NVM_OFFSET_TUNE_VERSION 3 +#define SEC_TS_NVM_OFFSET_TUNE_VERSION_LENGTH 2 + +#define SEC_TS_NVM_OFFSET_LENGTH (SEC_TS_NVM_LAST_BLOCK_OFFSET + SEC_TS_NVM_LAST_BLOCK_SIZE + 1) + +/* SEC_TS READ REGISTER ADDRESS */ +#define SEC_TS_CMD_SENSE_ON 0x10 +#define SEC_TS_CMD_SENSE_OFF 0x11 +#define SEC_TS_CMD_SW_RESET 0x12 +#define SEC_TS_CMD_CALIBRATION_SEC 0x13 // send it to touch ic, but toucu ic works nothing. +#define SEC_TS_CMD_FACTORY_PANELCALIBRATION 0x14 + +#define SEC_TS_READ_GPIO_STATUS 0x20 // not support +#define SEC_TS_READ_FIRMWARE_INTEGRITY 0x21 +#define SEC_TS_READ_DEVICE_ID 0x22 +#define SEC_TS_READ_PANEL_INFO 0x23 +#define SEC_TS_READ_CORE_CONFIG_VERSION 0x24 + +#define SEC_TS_CMD_SET_TOUCHFUNCTION 0x30 +#define SEC_TS_CMD_SET_TSC_MODE 0x31 +#define SET_TS_CMD_SET_CHARGER_MODE 0x32 +#define SET_TS_CMD_SET_NOISE_MODE 0x33 +#define SET_TS_CMD_SET_REPORT_RATE 0x34 +#define SEC_TS_CMD_TOUCH_MODE_FOR_THRESHOLD 0x35 +#define SEC_TS_CMD_TOUCH_THRESHOLD 0x36 +#define SET_TS_CMD_KEY_THRESHOLD 0x37 +#define SEC_TS_CMD_SET_COVERTYPE 0x38 +#define SEC_TS_CMD_WAKEUP_GESTURE_MODE 0x39 +#define SEC_TS_WRITE_POSITION_FILTER 0x3A +#define SEC_TS_CMD_WET_MODE 0x3B +#define SEC_TS_CMD_ERASE_FLASH 0x45 +#define SEC_TS_READ_ID 0x52 +#define SEC_TS_READ_BOOT_STATUS 0x55 +#define SEC_TS_CMD_ENTER_FW_MODE 0x57 +#define SEC_TS_READ_ONE_EVENT 0x60 +#define SEC_TS_READ_ALL_EVENT 0x61 +#define SEC_TS_CMD_CLEAR_EVENT_STACK 0x62 +#define SEC_TS_CMD_MUTU_RAW_TYPE 0x70 +#define SEC_TS_CMD_SELF_RAW_TYPE 0x71 +#define SEC_TS_READ_TOUCH_RAWDATA 0x72 +#define SEC_TS_READ_TOUCH_SELF_RAWDATA 0x73 +#define SEC_TS_READ_SELFTEST_RESULT 0x80 +#define SEC_TS_CMD_CALIBRATION_AMBIENT 0x81 +#define SEC_TS_CMD_P2P_TEST 0x82 +#define SEC_TS_CMD_P2P_MODE 0x83 +#define SEC_TS_CMD_NVM 0x85 +#define SEC_TS_CMD_STATEMANAGE_ON 0x8E +#define SEC_TS_CMD_CALIBRATION_OFFSET_SDC 0x8F + +/* SEC_TS CUSTOMLIB OPCODE COMMAND */ +#define SEC_TS_CMD_CUSTOMLIB_GET_INFO 0x90 +#define SEC_TS_CMD_CUSTOMLIB_WRITE_PARAM 0x91 +#define SEC_TS_CMD_CUSTOMLIB_READ_PARAM 0x92 +#define SEC_TS_CMD_CUSTOMLIB_NOTIFY_PACKET 0x93 + +#define SEC_TS_CMD_STATUS_EVENT_TYPE 0xA0 +#define SEC_TS_READ_FW_INFO 0xA2 +#define SEC_TS_READ_FW_VERSION 0xA3 +#define SEC_TS_READ_PARA_VERSION 0xA4 +#define SEC_TS_READ_IMG_VERSION 0xA5 +#define SEC_TS_CMD_GET_CHECKSUM 0xA6 +#define SEC_TS_CMD_MIS_CAL_CHECK 0xA7 +#define SEC_TS_CMD_MIS_CAL_READ 0xA8 +#define SEC_TS_CMD_MIS_CAL_SPEC 0xA9 +/* obsolete +#define SEC_TS_CMD_DEADZONE_RANGE 0xAA +#define SEC_TS_CMD_LONGPRESSZONE_RANGE 0xAB +#define SEC_TS_CMD_LONGPRESS_DROP_AREA 0xAC +#define SEC_TS_CMD_LONGPRESS_DROP_DIFF 0xAD +*/ +#define SEC_TS_READ_TS_STATUS 0xAF +#define SEC_TS_CMD_SELFTEST 0xAE +#define SEC_TS_READ_FORCE_RECAL_COUNT 0xB0 +#define SEC_TS_READ_FORCE_SIG_MAX_VAL 0xB1 +#define SEC_TS_GRIP_SUPPRESSION_INFO 0xBC + +/* SEC_TS FLASH COMMAND */ +#define SEC_TS_CMD_FLASH_READ_ADDR 0xD0 +#define SEC_TS_CMD_FLASH_READ_SIZE 0xD1 +#define SEC_TS_CMD_FLASH_READ_DATA 0xD2 +#define SEC_TS_CMD_CHG_SYSMODE 0xD7 +#define SEC_TS_CMD_FLASH_ERASE 0xD8 +#define SEC_TS_CMD_FLASH_WRITE 0xD9 +#define SEC_TS_CMD_FLASH_PADDING 0xDA + +#define SEC_TS_READ_BL_UPDATE_STATUS 0xDB +#define SEC_TS_CMD_SET_POWER_MODE 0xE4 +#define SEC_TS_CMD_EDGE_DEADZONE 0xE5 +#define SEC_TS_CMD_SET_DEX_MODE 0xE7 +#define SEC_TS_CMD_CALIBRATION_PRESSURE 0xE9 +/* Have to need delay 30msec after writing 0xEA command */ +/* Do not write Zero with 0xEA command */ +#define SEC_TS_CMD_SET_GET_PRESSURE 0xEA +#define SEC_TS_CMD_SET_USER_PRESSURE 0xEB +#define SEC_TS_CMD_SET_TEMPERATURE_COMP_MODE 0xEC +#define SEC_TS_CMD_SET_TOUCHABLE_AREA 0xED +#define SEC_TS_CMD_SET_BRUSH_MODE 0xEF + +#define SEC_TS_READ_CALIBRATION_REPORT 0xF1 +#define SEC_TS_CMD_SET_VENDOR_EVENT_LEVEL 0xF2 +#define SEC_TS_CMD_SET_SPENMODE 0xF3 +#define SEC_TS_CMD_SELECT_PRESSURE_TYPE 0xF5 +#define SEC_TS_CMD_READ_PRESSURE_DATA 0xF6 + +#define SEC_TS_FLASH_SIZE_64 64 +#define SEC_TS_FLASH_SIZE_128 128 +#define SEC_TS_FLASH_SIZE_256 256 + +#define SEC_TS_FLASH_SIZE_CMD 1 +#define SEC_TS_FLASH_SIZE_ADDR 2 +#define SEC_TS_FLASH_SIZE_CHECKSUM 1 + +#define SEC_TS_STATUS_BOOT_MODE 0x10 +#define SEC_TS_STATUS_APP_MODE 0x20 + +#define SEC_TS_FIRMWARE_PAGE_SIZE_256 256 +#define SEC_TS_FIRMWARE_PAGE_SIZE_128 128 + +/* SEC status event id */ +#define SEC_TS_COORDINATE_EVENT 0 +#define SEC_TS_STATUS_EVENT 1 +#define SEC_TS_GESTURE_EVENT 2 +#define SEC_TS_EMPTY_EVENT 3 + +#define SEC_TS_EVENT_BUFF_SIZE 8 +#define SEC_TS_SID_GESTURE 0x14 +#define SEC_TS_GESTURE_CODE_SPAY 0x00 +#define SEC_TS_GESTURE_CODE_DOUBLE_TAP 0x01 +#define SEC_TS_GESTURE_CODE_VENDOR 0x08 + +#define SEC_TS_COORDINATE_ACTION_NONE 0 +#define SEC_TS_COORDINATE_ACTION_PRESS 1 +#define SEC_TS_COORDINATE_ACTION_MOVE 2 +#define SEC_TS_COORDINATE_ACTION_RELEASE 3 + +#define SEC_TS_TOUCHTYPE_NORMAL 0 +#define SEC_TS_TOUCHTYPE_HOVER 1 +#define SEC_TS_TOUCHTYPE_FLIPCOVER 2 +#define SEC_TS_TOUCHTYPE_GLOVE 3 +#define SEC_TS_TOUCHTYPE_STYLUS 4 +#define SEC_TS_TOUCHTYPE_PALM 5 +#define SEC_TS_TOUCHTYPE_WET 6 +#define SEC_TS_TOUCHTYPE_PROXIMITY 7 +#define SEC_TS_TOUCHTYPE_JIG 8 + +/* SEC_TS_INFO : Info acknowledge event */ +#define SEC_TS_ACK_BOOT_COMPLETE 0x00 +#define SEC_TS_ACK_WET_MODE 0x1 + +/* SEC_TS_VENDOR_INFO : Vendor acknowledge event */ +#define SEC_TS_VENDOR_ACK_OFFSET_CAL_DONE 0x40 +#define SEC_TS_VENDOR_ACK_SELF_TEST_DONE 0x41 +#define SEC_TS_VENDOR_ACK_CMR_TEST_DONE 0x42 /* mutual */ + +#define SEC_TS_VENDOR_ACK_NOISE_STATUS_NOTI 0x64 +/* SEC_TS_STATUS_EVENT_USER_INPUT */ +#define SEC_TS_EVENT_FORCE_KEY 0x1 + +/* SEC_TS_STATUS_EVENT_CUSTOMLIB_INFO */ +#define SEC_TS_EVENT_CUSTOMLIB_FORCE_KEY 0x00 + +/* SEC_TS_ERROR : Error event */ +#define SEC_TS_ERR_EVNET_CORE_ERR 0x0 +#define SEC_TS_ERR_EVENT_QUEUE_FULL 0x01 +#define SEC_TS_ERR_EVENT_ESD 0x2 + +/* SEC_TS_DEBUG : Print event contents */ +#define SEC_TS_DEBUG_PRINT_ALLEVENT 0x01 +#define SEC_TS_DEBUG_PRINT_ONEEVENT 0x02 +#define SEC_TS_DEBUG_PRINT_I2C_READ_CMD 0x04 +#define SEC_TS_DEBUG_PRINT_I2C_WRITE_CMD 0x08 +#define SEC_TS_DEBUG_SEND_UEVENT 0x80 + +#define SEC_TS_BIT_SETFUNC_TOUCH (1 << 0) +#define SEC_TS_BIT_SETFUNC_MUTUAL (1 << 0) +#define SEC_TS_BIT_SETFUNC_HOVER (1 << 1) +#define SEC_TS_BIT_SETFUNC_COVER (1 << 2) +#define SEC_TS_BIT_SETFUNC_GLOVE (1 << 3) +#define SEC_TS_BIT_SETFUNC_STYLUS (1 << 4) +#define SEC_TS_BIT_SETFUNC_PALM (1 << 5) +#define SEC_TS_BIT_SETFUNC_WET (1 << 6) +#define SEC_TS_BIT_SETFUNC_PROXIMITY (1 << 7) + +#define SEC_TS_DEFAULT_ENABLE_BIT_SETFUNC (SEC_TS_BIT_SETFUNC_TOUCH | SEC_TS_BIT_SETFUNC_PALM | SEC_TS_BIT_SETFUNC_WET) + +#define SEC_TS_BIT_CHARGER_MODE_NO (0x1 << 0) +#define SEC_TS_BIT_CHARGER_MODE_WIRE_CHARGER (0x1 << 1) +#define SEC_TS_BIT_CHARGER_MODE_WIRELESS_CHARGER (0x1 << 2) +#define SEC_TS_BIT_CHARGER_MODE_WIRELESS_BATTERY_PACK (0x1 << 3) + +#define STATE_MANAGE_ON 1 +#define STATE_MANAGE_OFF 0 + +#define SEC_TS_STATUS_NOT_CALIBRATION 0x50 +#define SEC_TS_STATUS_CALIBRATION_SDC 0xA1 +#define SEC_TS_STATUS_CALIBRATION_SEC 0xA2 + +#define SEC_TS_CMD_EDGE_HANDLER 0xAA +#define SEC_TS_CMD_EDGE_AREA 0xAB /* compiled off in our FW */ +#define SEC_TS_CMD_HOLD_DISTANCE 0xAB +#define SEC_TS_CMD_DEAD_ZONE 0xAC /* compiled off in our FW */ +#define SEC_TS_CMD_GRIP_SUPP_AREA 0xAC +#define SEC_TS_CMD_LANDSCAPE_MODE 0xAD /* compiled off in our FW */ +#define SEC_TS_CMD_PILL_REGION 0xAD + +enum grip_write_mode { + G_NONE = 0, + G_SET_EDGE_HANDLER = 1, + G_SET_EDGE_ZONE = 2, + G_SET_NORMAL_MODE = 4, + G_SET_LANDSCAPE_MODE = 8, + G_CLR_LANDSCAPE_MODE = 16, +}; +enum grip_set_data { + ONLY_EDGE_HANDLER = 0, + GRIP_ALL_DATA = 1, +}; + +typedef enum { + SEC_TS_STATE_POWER_OFF = 0, + SEC_TS_STATE_LPM, + SEC_TS_STATE_POWER_ON +} TOUCH_POWER_MODE; + +typedef enum { + TOUCH_SYSTEM_MODE_BOOT = 0, + TOUCH_SYSTEM_MODE_CALIBRATION = 1, + TOUCH_SYSTEM_MODE_TOUCH = 2, + TOUCH_SYSTEM_MODE_SELFTEST = 3, + TOUCH_SYSTEM_MODE_FLASH = 4, + TOUCH_SYSTEM_MODE_LOWPOWER = 5, + TOUCH_SYSTEM_MODE_LISTEN +} TOUCH_SYSTEM_MODE; + +typedef enum { + TOUCH_MODE_STATE_IDLE = 0, + TOUCH_MODE_STATE_HOVER = 1, + TOUCH_MODE_STATE_TOUCH = 2, + TOUCH_MODE_STATE_NOISY = 3, + TOUCH_MODE_STATE_CAL = 4, + TOUCH_MODE_STATE_CAL2 = 5, + TOUCH_MODE_STATE_WAKEUP = 10 +} TOUCH_MODE_STATE; + +enum switch_system_mode { + TO_TOUCH_MODE = 0, + TO_LOWPOWER_MODE = 1, + TO_SELFTEST_MODE = 2, + TO_FLASH_MODE = 3, + TO_SLEEP_MODE = 4, +}; + +enum { + TYPE_RAW_DATA = 0, /* Total - Offset : delta data */ + TYPE_SIGNAL_DATA = 1, /* Signal - Filtering & Normalization */ + TYPE_AMBIENT_BASELINE = 2, /* Cap Baseline */ + TYPE_AMBIENT_DATA = 3, /* Cap Ambient */ + TYPE_REMV_BASELINE_DATA = 4, + TYPE_DECODED_DATA = 5, /* Raw */ + TYPE_REMV_AMB_DATA = 6, /* TYPE_RAW_DATA - TYPE_AMBIENT_DATA */ + TYPE_OFFSET_DATA_SET = 19, /* Cap Offset in SET Manufacturing Line */ + TYPE_OFFSET_DATA_SDC = 29, /* Cap Offset in SDC Manufacturing Line */ + TYPE_RAW_DATA_P2P_MIN = 30, /* Raw min data for 100 frame */ + TYPE_RAW_DATA_P2P_MAX = 31, /* Raw max data for 100 frame */ + TYPE_RAWDATA_MAX, + TYPE_INVALID_DATA = 0xFF, /* Invalid data type for release factory mode */ +}; + +typedef enum { + CUSTOMLIB_EVENT_TYPE_SPAY = 0x04, + CUSTOMLIB_EVENT_TYPE_PRESSURE_TOUCHED = 0x05, + CUSTOMLIB_EVENT_TYPE_PRESSURE_RELEASED = 0x06, + CUSTOMLIB_EVENT_TYPE_AOD = 0x08, + CUSTOMLIB_EVENT_TYPE_AOD_PRESS = 0x09, + CUSTOMLIB_EVENT_TYPE_AOD_LONGPRESS = 0x0A, + CUSTOMLIB_EVENT_TYPE_AOD_DOUBLETAB = 0x0B, + CUSTOMLIB_EVENT_TYPE_AOD_HOMEKEY_PRESS = 0x0C, + CUSTOMLIB_EVENT_TYPE_AOD_HOMEKEY_RELEASE = 0x0D, + CUSTOMLIB_EVENT_TYPE_AOD_HOMEKEY_RELEASE_NO_HAPTIC = 0x0E +} CUSTOMLIB_EVENT_TYPE; + +#define CMD_RESULT_WORD_LEN 10 + +#define SEC_TS_I2C_RETRY_CNT 3 +#define SEC_TS_WAIT_RETRY_CNT 100 + +#define SEC_TS_MODE_CUSTOMLIB_SPAY (1 << 1) +#define SEC_TS_MODE_CUSTOMLIB_AOD (1 << 2) +#define SEC_TS_MODE_CUSTOMLIB_FORCE_KEY (1 << 6) + +#define SEC_TS_MODE_LOWPOWER_FLAG (SEC_TS_MODE_CUSTOMLIB_SPAY | SEC_TS_MODE_CUSTOMLIB_AOD \ + | SEC_TS_MODE_CUSTOMLIB_FORCE_KEY) + +#define SEC_TS_CUSTOMLIB_EVENT_PRESSURE_TOUCHED (1 << 6) +#define SEC_TS_CUSTOMLIB_EVENT_PRESSURE_RELEASED (1 << 7) + +enum sec_ts_cover_id { + SEC_TS_FLIP_WALLET = 0, + SEC_TS_VIEW_COVER, + SEC_TS_COVER_NOTHING1, + SEC_TS_VIEW_WIRELESS, + SEC_TS_COVER_NOTHING2, + SEC_TS_CHARGER_COVER, + SEC_TS_VIEW_WALLET, + SEC_TS_LED_COVER, + SEC_TS_CLEAR_FLIP_COVER, + SEC_TS_QWERTY_KEYBOARD_EUR, + SEC_TS_QWERTY_KEYBOARD_KOR, + SEC_TS_MONTBLANC_COVER = 100, +}; + +enum sec_fw_update_status { + SEC_NOT_UPDATE = 0, + SEC_NEED_FW_UPDATE, + SEC_NEED_CALIBRATION_ONLY, + SEC_NEED_FW_UPDATE_N_CALIBRATION, +}; + +enum tsp_hw_parameter { + TSP_ITO_CHECK = 1, + TSP_RAW_CHECK = 2, + TSP_MULTI_COUNT = 3, + TSP_WET_MODE = 4, + TSP_COMM_ERR_COUNT = 5, + TSP_MODULE_ID = 6, +}; + +enum hold_grip_states { + SEC_HOLD_GRIP_NOGRIP, + SEC_HOLD_GRIP_UNCERTAIN, + SEC_HOLD_GRIP_LEFT, + SEC_HOLD_GRIP_RUGHT, +}; + +#define TEST_MODE_MIN_MAX false +#define TEST_MODE_ALL_NODE true +#define TEST_MODE_READ_FRAME false +#define TEST_MODE_READ_CHANNEL true + +/* factory test mode */ +struct sec_ts_test_mode { + u8 type; + short min; + short max; + bool allnode; + bool frame_channel; +}; + +struct sec_ts_fw_file { + u8 *data; + u32 pos; + size_t size; +}; + +/* + * write 0xE4 [ 11 | 10 | 01 | 00 ] + * MSB <-------------------> LSB + * read 0xE4 + * mapping sequnce : LSB -> MSB + * struct sec_ts_test_result { + * * assy : front + OCTA assay + * * module : only OCTA + * union { + * struct { + * u8 assy_count:2; -> 00 + * u8 assy_result:2; -> 01 + * u8 module_count:2; -> 10 + * u8 module_result:2; -> 11 + * } __attribute__ ((packed)); + * unsigned char data[1]; + * }; + *}; + */ +struct sec_ts_test_result { + union { + struct { + u8 assy_count:2; + u8 assy_result:2; + u8 module_count:2; + u8 module_result:2; + } __attribute__ ((packed)); + unsigned char data[1]; + }; +}; + +/* 8 byte */ +struct sec_ts_gesture_status { + u8 eid:2; + u8 stype:4; + u8 sf:2; + u8 gesture_id; + u8 gesture_data_1; + u8 gesture_data_2; + u8 gesture_data_3; + u8 gesture_data_4; + u8 reserved_1; + u8 left_event_5_0:6; + u8 reserved_2:2; +} __attribute__ ((packed)); + +/* 8 byte */ +struct sec_ts_event_status { + u8 eid:2; + u8 stype:4; + u8 sf:2; + u8 status_id; + u8 status_data_1; + u8 status_data_2; + u8 status_data_3; + u8 status_data_4; + u8 status_data_5; + u8 left_event_5_0:6; + u8 reserved_2:2; +} __attribute__ ((packed)); + +/* 8 byte */ +struct sec_ts_event_coordinate { + u8 eid:2; + u8 tid:4; + u8 tchsta:2; + u8 x_11_4; + u8 y_11_4; + u8 y_3_0:4; + u8 x_3_0:4; + u8 major; + u8 minor; + u8 z:6; + u8 ttype_3_2:2; + u8 left_event:6; + u8 ttype_1_0:2; +} __attribute__ ((packed)); + +/* not fixed */ +struct sec_ts_coordinate { + u8 id; + u8 ttype; + u8 action; + u16 x; + u16 y; + u8 z; + u8 hover_flag; + u8 glove_flag; + u8 touch_height; + u16 mcount; + u8 major; + u8 minor; + bool palm; + int palm_count; + u8 left_event; +}; + +struct sec_mmi_data; + +struct sec_ts_data { + u32 isr_pin; + + u32 crc_addr; + u32 fw_addr; + u32 para_addr; + u32 flash_page_size; + u8 boot_ver[3]; + u8 device_id[5]; + + struct device *dev; + struct i2c_client *client; + struct input_dev *input_dev; + struct input_dev *input_dev_pad; + struct input_dev *input_dev_touch; + struct sec_ts_plat_data *plat_data; + struct sec_ts_coordinate coord[MAX_SUPPORT_TOUCH_COUNT + MAX_SUPPORT_HOVER_COUNT]; + + struct TIME_TYPE time_pressed[MAX_SUPPORT_TOUCH_COUNT + MAX_SUPPORT_HOVER_COUNT]; + struct TIME_TYPE time_released[MAX_SUPPORT_TOUCH_COUNT + MAX_SUPPORT_HOVER_COUNT]; + long time_longest; + + struct regulator *regulator_dvdd; + struct regulator *regulator_avdd; + + u8 lowpower_mode; + u8 lowpower_status; + u8 dex_mode; + char *dex_name; + u8 brush_mode; + u8 touchable_area; + volatile u8 touch_noise_status; + volatile bool input_closed; + + int touch_count; + int tx_count; + int rx_count; + int i2c_burstmax; + int ta_status; + volatile int power_status; + int raw_status; + int touchkey_glove_mode_status; + u16 touch_functions; + u8 charger_mode; + struct sec_ts_event_coordinate touchtype; + bool touched[11]; + u8 gesture_status[6]; + u8 cal_status; + struct mutex lock; + struct mutex device_mutex; + struct mutex i2c_mutex; + struct mutex eventlock; + struct mutex modechange; + + struct delayed_work work_read_info; +#ifdef USE_POWER_RESET_WORK + struct delayed_work reset_work; + volatile bool reset_is_on_going; +#endif + struct completion resume_done; + struct wakeup_source *wakelock; + struct wakeup_source *gesture_wakelock; + struct sec_cmd_data sec; + short *pFrame; + + bool force_calibration; + bool probe_done; + bool reinit_done; + bool flip_enable; + bool info_work_done; + int cover_type; + u8 cover_cmd; + u16 rect_data[4]; + + int tspid_val; + int tspicid_val; + + bool use_customlib; + unsigned int scrub_id; + unsigned int scrub_x; + unsigned int scrub_y; + + u8 grip_edgehandler_direction; + int grip_edgehandler_start_y; + int grip_edgehandler_end_y; + u16 grip_edge_range; + u8 grip_deadzone_up_x; + u8 grip_deadzone_dn_x; + int grip_deadzone_y; + u8 grip_landscape_mode; + int grip_landscape_edge; + u16 grip_landscape_deadzone; + + struct delayed_work ghost_check; + u8 tsp_dump_lock; + + int nv; + int cal_count; + int tune_fix_ver; + bool external_factory; + + int wet_mode; + + unsigned char ito_test[4]; /* ito panel tx/rx chanel */ + unsigned char check_multi; + unsigned int multi_count; /* multi touch count */ + unsigned int wet_count; /* wet mode count */ + unsigned int noise_count; /* noise mode count */ + unsigned int dive_count; /* dive mode count */ + unsigned int comm_err_count; /* i2c comm error count */ + unsigned int checksum_result; /* checksum result */ + unsigned char module_id[4]; + unsigned int all_finger_count; + unsigned int all_force_count; + unsigned int all_aod_tap_count; + unsigned int all_spay_count; + unsigned int max_z_value; + unsigned int min_z_value; + unsigned int sum_z_value; + unsigned char pressure_cal_base; + unsigned char pressure_cal_delta; + + unsigned char suppression_data; + unsigned char pill_region_data[5]; + unsigned char hold_distance_data; + unsigned char gs_distance_data; + unsigned char hold_grip_data; + + /* for factory - factory_cmd_result_all() */ + short cm_raw_set_p2p_min; //CM_RAW_SET_P2P + short cm_raw_set_p2p_max; //CM_RAW_SET_P2P + short cm_raw_set_p2p_diff; //CM_RAW_SET_P2P_DIFF + +#ifdef USE_PRESSURE_SENSOR + short pressure_left; + short pressure_center; + short pressure_right; + u8 pressure_user_level; +#endif + int debug_flag; + + int (*sec_ts_i2c_write)(struct sec_ts_data *ts, u8 reg, u8 *data, int len); + int (*sec_ts_i2c_read)(struct sec_ts_data *ts, u8 reg, u8 *data, int len); + int (*sec_ts_i2c_write_burst)(struct sec_ts_data *ts, u8 *data, int len); + int (*sec_ts_i2c_read_bulk)(struct sec_ts_data *ts, u8 *data, int len); + int (*sec_ts_read_customlib)(struct sec_ts_data *ts, u8 *data, int len); + + struct sec_mmi_data *mmi_ptr; + bool fw_invalid; + bool irq_enabled; + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + struct ts_mmi_class_methods *imports; +#endif +}; + +struct sec_ts_plat_data { + int max_x; + int max_y; + int max_major; + int max_minor; + int size_to_pixel_multiple_factor; + unsigned rst_gpio; + unsigned irq_gpio; + int irq_type; + int i2c_burstmax; + int always_lpmode; + int bringup; + int mis_cal_check; + + const char *firmware_name; + const char *model_name; + const char *project_name; + const char *regulator_dvdd; + const char *regulator_avdd; + + u32 panel_revision; + u8 core_version_of_ic[4]; + u8 core_version_of_bin[4]; + u8 config_version_of_ic[4]; + u8 config_version_of_bin[4]; + u8 img_version_of_ic[4]; + u8 img_version_of_bin[4]; + + struct pinctrl *pinctrl; + + int (*power)(void *data, bool on); + void (*enable_sync)(bool on); + int tsp_icid; + int tsp_id; + int tsp_vsync; + + bool regulator_boot_on; + bool support_mt_pressure; + bool support_dex; + bool support_sidegesture; + bool poweron_calibration; + bool suppression_ctrl; + bool pill_region_ctrl; + bool hold_distance_ctrl; + bool gs_distance_ctrl; + bool hold_grip_ctrl; + + unsigned *clip_area; +}; + +typedef struct { + u32 signature; /* signature */ + u32 version; /* version */ + u32 totalsize; /* total size */ + u32 checksum; /* checksum */ + u32 img_ver; /* image file version */ + u32 img_date; /* image file date */ + u32 img_description; /* image file description */ + u32 fw_ver; /* firmware version */ + u32 fw_date; /* firmware date */ + u32 fw_description; /* firmware description */ + u32 para_ver; /* parameter version */ + u32 para_date; /* parameter date */ + u32 para_description; /* parameter description */ + u32 num_chunk; /* number of chunk */ + u32 reserved1; + u32 reserved2; +} fw_header; + +typedef struct { + u32 signature; + u32 addr; + u32 size; + u32 reserved; +} fw_chunk; + +int sec_ts_pinctrl_configure(struct sec_ts_data *ts, bool enable); +int sec_ts_power(void *data, bool on); +int sec_ts_stop_device(struct sec_ts_data *ts); +int sec_ts_start_device(struct sec_ts_data *ts); +int sec_ts_set_lowpowermode(struct sec_ts_data *ts, u8 mode); +int sec_ts_firmware_update(struct sec_ts_data *ts, const u8 *data, size_t size, int bl_update, int restore_cal, int retry); +int sec_ts_firmware_update_on_probe(struct sec_ts_data *ts, bool force_update); +int sec_ts_firmware_update_on_hidden_menu(struct sec_ts_data *ts, int update_type); +int sec_ts_glove_mode_enables(struct sec_ts_data *ts, int mode); +int sec_ts_set_cover_type(struct sec_ts_data *ts, bool enable); +int sec_ts_wait_for_ready(struct sec_ts_data *ts, unsigned int ack); +int sec_ts_function(int (*func_init)(void *device_data), void (*func_remove)(void)); +int sec_ts_fn_init(struct sec_ts_data *ts); +int sec_ts_read_calibration_report(struct sec_ts_data *ts); +int sec_ts_execute_force_calibration(struct sec_ts_data *ts, int cal_mode); +int sec_ts_fix_tmode(struct sec_ts_data *ts, u8 mode, u8 state); +int sec_ts_release_tmode(struct sec_ts_data *ts); +int get_tsp_nvm_data(struct sec_ts_data *ts, u8 offset); +int get_tsp_nvm_data_by_size(struct sec_ts_data *ts, u8 offset, int length, u8 *data); +void set_tsp_nvm_data_clear(struct sec_ts_data *ts, u8 offset); +#ifdef SEC_TS_SUPPORT_CUSTOMLIB +int sec_ts_set_custom_library(struct sec_ts_data *ts); +int sec_ts_check_custom_library(struct sec_ts_data *ts); +#endif +void sec_ts_unlocked_release_all_finger(struct sec_ts_data *ts); +void sec_ts_locked_release_all_finger(struct sec_ts_data *ts); +void sec_ts_fn_remove(struct sec_ts_data *ts); +void sec_ts_delay(unsigned int ms); +int sec_ts_read_information(struct sec_ts_data *ts); +int sec_ts_integrity_check(struct sec_ts_data *ts); +int execute_selftest(struct sec_ts_data *ts, bool save_result); + +void sec_ts_run_rawdata_all(struct sec_ts_data *ts, bool full_read); + +void sec_ts_reinit(struct sec_ts_data *ts); +void sec_ts_sense_on(struct sec_ts_data *ts); +void sec_ts_irq_enable(struct sec_ts_data *ts, bool on); +void sec_ts_set_charger(struct sec_ts_data *ts, bool enable); + +void sec_ts_lv1_params(u8 cmd, int size); +ssize_t sec_ts_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +ssize_t sec_ts_regread_show(struct device *dev, + struct device_attribute *attr, char *buf); + +#if (1)//!defined(CONFIG_SAMSUNG_PRODUCT_SHIP) + +int sec_ts_raw_device_init(struct sec_ts_data *ts); +#endif + +extern bool tsp_init_done; + +extern struct sec_ts_data *ts_dup; + +#ifdef CONFIG_BATTERY_SAMSUNG +extern unsigned int lpcharge; +#endif + +extern void set_grip_data_to_ic(struct sec_ts_data *ts, u8 flag); +extern void sec_ts_set_grip_type(struct sec_ts_data *ts, u8 set_type); + +#ifndef input_log_fix +#define input_log_fix() +#endif +#ifndef input_raw_data_clear +#define input_raw_data_clear() +#endif +#endif diff --git a/drivers/input/touchscreen/sec_mmi/sec_ts_fn.c b/drivers/input/touchscreen/sec_mmi/sec_ts_fn.c new file mode 100644 index 000000000000..851eb577914a --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/sec_ts_fn.c @@ -0,0 +1,3833 @@ +/* drivers/input/touchscreen/sec_ts_fn.c + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * http://www.samsungsemi.com/ + * + * Core file for Samsung TSC driver + * + * 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. + */ + +#include "sec_ts.h" + +static void fw_update(void *device_data); +static void get_fw_ver_bin(void *device_data); +static void get_fw_ver_ic(void *device_data); +static void get_config_ver(void *device_data); +static void get_threshold(void *device_data); +static void module_off_master(void *device_data); +static void module_on_master(void *device_data); +static void get_chip_vendor(void *device_data); +static void get_chip_name(void *device_data); +static void set_mis_cal_spec(void *device_data); +static void get_mis_cal_info(void *device_data); +static void get_wet_mode(void *device_data); +static void get_x_num(void *device_data); +static void get_y_num(void *device_data); +static void get_checksum_data(void *device_data); +static void run_reference_read(void *device_data); +static void run_reference_read_all(void *device_data); +static void get_reference(void *device_data); +static void run_rawcap_read(void *device_data); +static void run_rawcap_read_all(void *device_data); +static void get_rawcap(void *device_data); +static void run_delta_read(void *device_data); +static void run_delta_read_all(void *device_data); +static void get_delta(void *device_data); +static void run_raw_p2p_min_read_all(void *device_data); +static void run_raw_p2p_max_read_all(void *device_data); +static void run_self_reference_read(void *device_data); +static void run_self_reference_read_all(void *device_data); +static void run_self_rawcap_read(void *device_data); +static void run_self_rawcap_read_all(void *device_data); +static void run_self_delta_read(void *device_data); +static void run_self_delta_read_all(void *device_data); +static void run_rawdata_read_all(void *device_data); +static void run_force_calibration(void *device_data); +static void get_force_calibration(void *device_data); +static void get_gap_data_x_all(void *device_data); +static void get_gap_data_y_all(void *device_data); +static void run_trx_short_test(void *device_data); +static void glove_mode(void *device_data); +static void clear_cover_mode(void *device_data); +static void dead_zone_enable(void *device_data); +static void drawing_test_enable(void *device_data); +static void set_lowpower_mode(void *device_data); +static void set_wirelesscharger_mode(void *device_data); +static void spay_enable(void *device_data); +static void aod_enable(void *device_data); +static void set_grip_data(void *device_data); +static void dex_enable(void *device_data); +static void brush_enable(void *device_data); +static void set_touchable_area(void *device_data); +static void set_log_level(void *device_data); +static void debug(void *device_data); +static void not_support_cmd(void *device_data); + +static void sec_ts_print_frame(struct sec_ts_data *ts, short *min, short *max); + +static struct sec_cmd sec_cmds[] = { + {SEC_CMD("fw_update", fw_update),}, + {SEC_CMD("get_fw_ver_bin", get_fw_ver_bin),}, + {SEC_CMD("get_fw_ver_ic", get_fw_ver_ic),}, + {SEC_CMD("get_config_ver", get_config_ver),}, + + {SEC_CMD("get_threshold", get_threshold),}, + {SEC_CMD("module_off_master", module_off_master),}, + {SEC_CMD("module_on_master", module_on_master),}, + {SEC_CMD("get_chip_vendor", get_chip_vendor),}, + {SEC_CMD("get_chip_name", get_chip_name),}, + {SEC_CMD("set_mis_cal_spec", set_mis_cal_spec),}, + {SEC_CMD("get_mis_cal_info", get_mis_cal_info),}, + {SEC_CMD("get_wet_mode", get_wet_mode),}, + {SEC_CMD("get_x_num", get_x_num),}, + {SEC_CMD("get_y_num", get_y_num),}, + {SEC_CMD("get_checksum_data", get_checksum_data),}, + {SEC_CMD("run_reference_read", run_reference_read),}, + {SEC_CMD("run_reference_read_all", run_reference_read_all),}, + {SEC_CMD("get_reference", get_reference),}, + {SEC_CMD("run_rawcap_read", run_rawcap_read),}, + {SEC_CMD("run_rawcap_read_all", run_rawcap_read_all),}, + {SEC_CMD("get_rawcap", get_rawcap),}, + {SEC_CMD("run_delta_read", run_delta_read),}, + {SEC_CMD("run_delta_read_all", run_delta_read_all),}, + {SEC_CMD("get_delta", get_delta),}, + {SEC_CMD("run_raw_p2p_min_read_all", run_raw_p2p_min_read_all),}, + {SEC_CMD("run_raw_p2p_max_read_all", run_raw_p2p_max_read_all),}, + {SEC_CMD("run_self_reference_read", run_self_reference_read),}, + {SEC_CMD("run_self_reference_read_all", run_self_reference_read_all),}, + {SEC_CMD("run_self_rawcap_read", run_self_rawcap_read),}, + {SEC_CMD("run_self_rawcap_read_all", run_self_rawcap_read_all),}, + {SEC_CMD("run_self_delta_read", run_self_delta_read),}, + {SEC_CMD("run_self_delta_read_all", run_self_delta_read_all),}, + {SEC_CMD("run_rawdata_read_all", run_rawdata_read_all),}, + {SEC_CMD("run_force_calibration", run_force_calibration),}, + {SEC_CMD("get_force_calibration", get_force_calibration),}, + {SEC_CMD("get_gap_data_x_all", get_gap_data_x_all),}, + {SEC_CMD("get_gap_data_y_all", get_gap_data_y_all),}, + {SEC_CMD("run_trx_short_test", run_trx_short_test),}, + {SEC_CMD("glove_mode", glove_mode),}, + {SEC_CMD("clear_cover_mode", clear_cover_mode),}, + {SEC_CMD("dead_zone_enable", dead_zone_enable),}, + {SEC_CMD("drawing_test_enable", drawing_test_enable),}, + {SEC_CMD("set_lowpower_mode", set_lowpower_mode),}, + {SEC_CMD("set_wirelesscharger_mode", set_wirelesscharger_mode),}, + {SEC_CMD("spay_enable", spay_enable),}, + {SEC_CMD("aod_enable", aod_enable),}, + {SEC_CMD("set_grip_data", set_grip_data),}, + {SEC_CMD("dex_enable", dex_enable),}, + {SEC_CMD("brush_enable", brush_enable),}, + {SEC_CMD("set_touchable_area", set_touchable_area),}, + {SEC_CMD("set_log_level", set_log_level),}, + {SEC_CMD("debug", debug),}, + {SEC_CMD("not_support_cmd", not_support_cmd),}, +}; + +static ssize_t scrub_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[256] = { 0 }; + +#ifdef CONFIG_SAMSUNG_PRODUCT_SHIP + input_info(true, &ts->client->dev, + "%s: scrub_id: %d\n", __func__, ts->scrub_id); +#else + input_info(true, &ts->client->dev, + "%s: scrub_id: %d, X:%d, Y:%d\n", __func__, + ts->scrub_id, ts->scrub_x, ts->scrub_y); +#endif + snprintf(buff, sizeof(buff), "%d %d %d", ts->scrub_id, ts->scrub_x, ts->scrub_y); + + ts->scrub_x = 0; + ts->scrub_y = 0; + + return snprintf(buf, PAGE_SIZE, "%s", buff); +} + +static DEVICE_ATTR(scrub_pos, 0444, scrub_position_show, NULL); + +static ssize_t read_ito_check_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[256] = { 0 }; + + input_info(true, &ts->client->dev, "%s: %02X%02X%02X%02X\n", __func__, + ts->ito_test[0], ts->ito_test[1], + ts->ito_test[2], ts->ito_test[3]); + + snprintf(buff, sizeof(buff), "%02X%02X%02X%02X", + ts->ito_test[0], ts->ito_test[1], + ts->ito_test[2], ts->ito_test[3]); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s", buff); +} + +static ssize_t read_raw_check_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + int ii, ret = 0; + char *buffer = NULL; + char temp[CMD_RESULT_WORD_LEN] = { 0 }; + + buffer = vzalloc(ts->rx_count * ts->tx_count * 6); + if (!buffer) + return -ENOMEM; + + memset(buffer, 0x00, ts->rx_count * ts->tx_count * 6); + + for (ii = 0; ii < (ts->rx_count * ts->tx_count - 1); ii++) { + snprintf(temp, CMD_RESULT_WORD_LEN, "%d ", ts->pFrame[ii]); + strncat(buffer, temp, CMD_RESULT_WORD_LEN); + + memset(temp, 0x00, CMD_RESULT_WORD_LEN); + } + + snprintf(temp, CMD_RESULT_WORD_LEN, "%d", ts->pFrame[ii]); + strncat(buffer, temp, CMD_RESULT_WORD_LEN); + + ret = snprintf(buf, ts->rx_count * ts->tx_count * 6, buffer); + vfree(buffer); + + return ret; +} + +static ssize_t read_multi_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + input_info(true, &ts->client->dev, "%s: %d\n", __func__, + ts->multi_count); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", ts->multi_count); +} + +static ssize_t clear_multi_count_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + ts->multi_count = 0; + + input_info(true, &ts->client->dev, "%s: clear\n", __func__); + + return count; +} + +static ssize_t read_wet_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + input_info(true, &ts->client->dev, "%s: %d, %d\n", __func__, + ts->wet_count, ts->dive_count); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", ts->wet_count); +} + +static ssize_t clear_wet_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + ts->wet_count = 0; + ts->dive_count= 0; + + input_info(true, &ts->client->dev, "%s: clear\n", __func__); + + return count; +} + +static ssize_t read_noise_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + input_info(true, &ts->client->dev, "%s: %d\n", __func__, ts->noise_count); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", ts->noise_count); +} + +static ssize_t clear_noise_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + ts->noise_count = 0; + + input_info(true, &ts->client->dev, "%s: clear\n", __func__); + + return count; +} + +static ssize_t read_comm_err_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + input_info(true, &ts->client->dev, "%s: %d\n", __func__, + ts->multi_count); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", ts->comm_err_count); +} + +static ssize_t clear_comm_err_count_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + ts->comm_err_count = 0; + + input_info(true, &ts->client->dev, "%s: clear\n", __func__); + + return count; +} + +static ssize_t read_module_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[256] = { 0 }; + + snprintf(buff, sizeof(buff), "SE%02X%02X%02X", + ts->plat_data->panel_revision, ts->plat_data->img_version_of_bin[2], + ts->plat_data->img_version_of_bin[3]); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s", buff); +} + +static ssize_t read_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + unsigned char buffer[10] = { 0 }; + + if (ts->plat_data->firmware_name) + snprintf(buffer, 5, ts->plat_data->firmware_name + 8); + else + strlcpy(buffer, "undef", 5); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "LSI_%s", buffer); +} + +static ssize_t clear_checksum_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + ts->checksum_result = 0; + + input_info(true, &ts->client->dev, "%s: clear\n", __func__); + + return count; +} + +static ssize_t read_checksum_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + input_info(true, &ts->client->dev, "%s: %d\n", __func__, + ts->checksum_result); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", ts->checksum_result); +} + +static ssize_t clear_holding_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + ts->time_longest = 0; + + input_info(true, &ts->client->dev, "%s: clear\n", __func__); + + return count; +} + +static ssize_t read_holding_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + input_info(true, &ts->client->dev, "%s: %ld\n", __func__, + ts->time_longest); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%ld", ts->time_longest); +} + +static ssize_t read_all_touch_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + input_info(true, &ts->client->dev, "%s: touch:%d, force:%d, aod:%d, spay:%d\n", __func__, + ts->all_finger_count, ts->all_force_count, + ts->all_aod_tap_count, ts->all_spay_count); + + return snprintf(buf, SEC_CMD_BUF_SIZE, + "\"TTCN\":\"%d\",\"TFCN\":\"%d\",\"TACN\":\"%d\",\"TSCN\":\"%d\"", + ts->all_finger_count, ts->all_force_count, + ts->all_aod_tap_count, ts->all_spay_count); +} + +static ssize_t clear_all_touch_count_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + ts->all_force_count = 0; + ts->all_aod_tap_count = 0; + ts->all_spay_count = 0; + + input_info(true, &ts->client->dev, "%s: clear\n", __func__); + + return count; +} + +static ssize_t read_z_value_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + input_info(true, &ts->client->dev, "%s: max:%d, min:%d, avg:%d\n", __func__, + ts->max_z_value, ts->min_z_value, + ts->sum_z_value); + + if (ts->all_finger_count) + return snprintf(buf, SEC_CMD_BUF_SIZE, + "\"TMXZ\":\"%d\",\"TMNZ\":\"%d\",\"TAVZ\":\"%d\"", + ts->max_z_value, ts->min_z_value, + ts->sum_z_value / ts->all_finger_count); + else + return snprintf(buf, SEC_CMD_BUF_SIZE, + "\"TMXZ\":\"%d\",\"TMNZ\":\"%d\"", + ts->max_z_value, ts->min_z_value); + +} + +static ssize_t clear_z_value_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + + ts->max_z_value= 0; + ts->min_z_value= 0xFFFFFFFF; + ts->sum_z_value= 0; + ts->all_finger_count = 0; + + input_info(true, &ts->client->dev, "%s: clear\n", __func__); + + return count; +} + +static ssize_t get_force_recal_count(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + u8 rbuf[4] = {0, }; + u32 recal_count; + int ret; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Touch is stopped!\n", __func__); + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", -ENODEV); + } +#ifdef USE_POWER_RESET_WORK + if (ts->reset_is_on_going) { + input_err(true, &ts->client->dev, "%s: Reset is ongoing!\n", __func__); + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", -EBUSY); + } +#endif + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_FORCE_RECAL_COUNT, rbuf, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: Failed to read\n", __func__); + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", -EIO); + } + + recal_count = (rbuf[0] & 0xFF) << 24 | (rbuf[1] & 0xFF) << 16 | + (rbuf[2] & 0xFF) << 8 | (rbuf[3] & 0xFF); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%d", recal_count); +} + +static ssize_t ic_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_cmd_data *sec = dev_get_drvdata(dev); + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[512] = { 0 }; + char temp[128] = { 0 }; + u8 data[2] = { 0 }; + int ret; + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_SET_TOUCHFUNCTION, data, 2); + if (ret < 0) + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); + + snprintf(temp, sizeof(temp), "mutual,%d,", data[0] & 0x01 ? 1 : 0); + strncat(buff, temp, strlen(temp)); + snprintf(temp, sizeof(temp), "hover,%d,", data[0] & 0x02 ? 1 : 0); + strncat(buff, temp, strlen(temp)); + snprintf(temp, sizeof(temp), "cover,%d,", data[0] & 0x04 ? 1 : 0); + strncat(buff, temp, strlen(temp)); + snprintf(temp, sizeof(temp), "glove,%d,", data[0] & 0x08 ? 1 : 0); + strncat(buff, temp, strlen(temp)); + snprintf(temp, sizeof(temp), "stylus,%d,", data[0] & 0x10 ? 1 : 0); + strncat(buff, temp, strlen(temp)); + snprintf(temp, sizeof(temp), "palm,%d,", data[0] & 0x20 ? 1 : 0); + strncat(buff, temp, strlen(temp)); + snprintf(temp, sizeof(temp), "wet,%d,", data[0] & 0x40 ? 1 : 0); + strncat(buff, temp, strlen(temp)); + snprintf(temp, sizeof(temp), "prox,%d,", data[0] & 0x80 ? 1 : 0); + strncat(buff, temp, strlen(temp)); + + memset(data, 0x00, 2); + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_SET_POWER_MODE, data, 1); + if (ret < 0) + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); + + snprintf(temp, sizeof(temp), "npm,%d,", data[0] == 0 ? 1 : 0); + strncat(buff, temp, strlen(temp)); + snprintf(temp, sizeof(temp), "lpm,%d,", data[0] == 1 ? 1 : 0); + strncat(buff, temp, strlen(temp)); + snprintf(temp, sizeof(temp), "test,%d,", data[0] == 2 ? 1 : 0); + strncat(buff, temp, strlen(temp)); + snprintf(temp, sizeof(temp), "flash,%d,", data[0] == 3 ? 1 : 0); + strncat(buff, temp, strlen(temp)); + + memset(data, 0x00, 2); + ret = ts->sec_ts_i2c_read(ts, SET_TS_CMD_SET_CHARGER_MODE, data, 1); + if (ret < 0) + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); + + snprintf(temp, sizeof(temp), "no_charge,%d,", data[0] == 0 ? 1 : 0); + strncat(buff, temp, strlen(temp)); + snprintf(temp, sizeof(temp), "wire_charge,%d,", data[0] == 1 ? 1 : 0); + strncat(buff, temp, strlen(temp)); + snprintf(temp, sizeof(temp), "wireless_charge,%d,", data[0] == 2 ? 1 : 0); + strncat(buff, temp, strlen(temp)); + + memset(data, 0x00, 2); + ret = ts->sec_ts_i2c_read(ts, SET_TS_CMD_SET_NOISE_MODE, data, 1); + if (ret < 0) + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); + + snprintf(temp, sizeof(temp), "noise,%d,", data[0]); + strncat(buff, temp, strlen(temp)); + + memset(data, 0x00, 2); + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_SET_COVERTYPE, data, 1); + if (ret < 0) + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); + + snprintf(temp, sizeof(temp), "cover_type,%d,", data[0]); + strncat(buff, temp, strlen(temp)); + + data[0] = 0; + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_SET_DEX_MODE, data, 1); + if (ret < 0) + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); + + snprintf(temp, sizeof(temp), "dex,%d,", data[0]); + strncat(buff, temp, strlen(temp)); + + data[0] = 0; + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_SET_BRUSH_MODE, data, 1); + if (ret < 0) + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); + + snprintf(temp, sizeof(temp), "artcanvas,%d,", data[0]); + strncat(buff, temp, strlen(temp)); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + + return snprintf(buf, SEC_CMD_BUF_SIZE, "%s\n", buff); +} + +static DEVICE_ATTR(ito_check, 0444, read_ito_check_show, NULL); +static DEVICE_ATTR(raw_check, 0444, read_raw_check_show, NULL); +static DEVICE_ATTR(multi_count, 0664, read_multi_count_show, clear_multi_count_store); +static DEVICE_ATTR(wet_mode, 0664, read_wet_mode_show, clear_wet_mode_store); +static DEVICE_ATTR(noise_mode, 0664, read_noise_mode_show, clear_noise_mode_store); +static DEVICE_ATTR(comm_err_count, 0664, read_comm_err_count_show, clear_comm_err_count_store); +static DEVICE_ATTR(checksum, 0664, read_checksum_show, clear_checksum_store); +static DEVICE_ATTR(holding_time, 0664, read_holding_time_show, clear_holding_time_store); +static DEVICE_ATTR(all_touch_count, 0664, read_all_touch_count_show, clear_all_touch_count_store); +static DEVICE_ATTR(z_value, 0664, read_z_value_show, clear_z_value_store); +static DEVICE_ATTR(module_id, 0444, read_module_id_show, NULL); +static DEVICE_ATTR(vendor, 0444, read_vendor_show, NULL); +static DEVICE_ATTR(force_recal_count, 0444, get_force_recal_count, NULL); +static DEVICE_ATTR(status, 0444, ic_status_show, NULL); + +static struct attribute *cmd_attributes[] = { + &dev_attr_scrub_pos.attr, + &dev_attr_ito_check.attr, + &dev_attr_raw_check.attr, + &dev_attr_multi_count.attr, + &dev_attr_wet_mode.attr, + &dev_attr_noise_mode.attr, + &dev_attr_comm_err_count.attr, + &dev_attr_checksum.attr, + &dev_attr_holding_time.attr, + &dev_attr_all_touch_count.attr, + &dev_attr_z_value.attr, + &dev_attr_module_id.attr, + &dev_attr_vendor.attr, + &dev_attr_force_recal_count.attr, + &dev_attr_status.attr, + NULL, +}; + +static struct attribute_group cmd_attr_group = { + .attrs = cmd_attributes, +}; + +static int sec_ts_check_index(struct sec_ts_data *ts) +{ + struct sec_cmd_data *sec = &ts->sec; + char buff[SEC_CMD_STR_LEN] = { 0 }; + int node; + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > ts->tx_count + || sec->cmd_param[1] < 0 || sec->cmd_param[1] > ts->rx_count) { + + snprintf(buff, sizeof(buff), "%s", "NG"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + input_info(true, &ts->client->dev, "%s: parameter error: %u, %u\n", + __func__, sec->cmd_param[0], sec->cmd_param[0]); + node = -1; + return node; + } + node = sec->cmd_param[1] * ts->tx_count + sec->cmd_param[0]; + input_info(true, &ts->client->dev, "%s: node = %d\n", __func__, node); + return node; +} +static void fw_update(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[64] = { 0 }; + int retval = 0; + + sec_cmd_set_default_result(sec); + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", + __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + return; + } + + retval = sec_ts_firmware_update_on_hidden_menu(ts, sec->cmd_param[0]); + if (retval < 0) { + snprintf(buff, sizeof(buff), "%s", "NA"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + input_err(true, &ts->client->dev, "%s: failed [%d]\n", __func__, retval); + } else { + snprintf(buff, sizeof(buff), "%s", "OK"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: success [%d]\n", __func__, retval); + } +} + +int sec_ts_fix_tmode(struct sec_ts_data *ts, u8 mode, u8 state) +{ + int ret; + u8 onoff[1] = {STATE_MANAGE_OFF}; + u8 tBuff[2] = { mode, state }; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_STATEMANAGE_ON, onoff, 1); + sec_ts_delay(20); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CHG_SYSMODE, tBuff, sizeof(tBuff)); + sec_ts_delay(20); + + return ret; +} + +int sec_ts_p2p_tmode(struct sec_ts_data *ts) +{ + int ret; + u8 mode[1] = {0x0C}; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CLEAR_EVENT_STACK, NULL, 0); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_P2P_MODE, mode, sizeof(mode)); + sec_ts_delay(30); + + return ret; +} + +static int execute_p2ptest(struct sec_ts_data *ts) +{ + int ret; + u8 test[2] = {0x00, 0x64}; + u8 tBuff[10] = {0}; + int retry = 0; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_P2P_TEST, test, sizeof(test)); + sec_ts_delay(2000); + + ret = -1; + + while (ts->sec_ts_i2c_read(ts, SEC_TS_READ_ONE_EVENT, tBuff, 8)) { + if (((tBuff[0] >> 2) & 0xF) == TYPE_STATUS_EVENT_VENDOR_INFO) { + if (tBuff[1] == SEC_TS_VENDOR_ACK_CMR_TEST_DONE) { + ts->cm_raw_set_p2p_max = (tBuff[2] & 0xFF) << 8 | (tBuff[3] & 0xFF); + ts->cm_raw_set_p2p_min = (tBuff[4] & 0xFF) << 8 | (tBuff[5] & 0xFF); + ts->cm_raw_set_p2p_diff = (tBuff[7] & 0xC0) << 2 | (tBuff[6] & 0xFF); + } + } + + if ((tBuff[7] & 0x3F) == 0x00) { + input_info(true, &ts->client->dev, "%s: left event is 0\n", __func__); + ret = 0; + break; + } + + if (retry++ > SEC_TS_WAIT_RETRY_CNT) { + input_err(true, &ts->client->dev, "%s: Time Over\n", __func__); + break; + } + sec_ts_delay(20); + } + return ret; +} + +int sec_ts_release_tmode(struct sec_ts_data *ts) +{ + int ret; + u8 onoff[1] = {STATE_MANAGE_ON}; + + input_info(true, &ts->client->dev, "%s\n", __func__); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_STATEMANAGE_ON, onoff, 1); + sec_ts_delay(20); + + return ret; +} + +static void sec_ts_print_frame(struct sec_ts_data *ts, short *min, short *max) +{ + int i = 0; + int j = 0; + unsigned char *pStr = NULL; + unsigned char pTmp[16] = { 0 }; + int lsize = 7 * (ts->tx_count + 1); + + input_raw_info(true, &ts->client->dev, "%s\n", __func__); + + pStr = kzalloc(lsize, GFP_KERNEL); + if (pStr == NULL) + return; + + memset(pStr, 0x0, lsize); + snprintf(pTmp, sizeof(pTmp), " TX"); + strncat(pStr, pTmp, lsize); + + for (i = 0; i < ts->tx_count; i++) { + snprintf(pTmp, sizeof(pTmp), " %02d ", i); + strncat(pStr, pTmp, lsize); + } + + input_raw_frame_info(true, &ts->client->dev, "%s\n", pStr); + memset(pStr, 0x0, lsize); + snprintf(pTmp, sizeof(pTmp), " +"); + strncat(pStr, pTmp, lsize); + + for (i = 0; i < ts->tx_count; i++) { + snprintf(pTmp, sizeof(pTmp), "----"); + strncat(pStr, pTmp, lsize); + } + + input_raw_frame_info(true, &ts->client->dev, "%s\n", pStr); + + for (i = 0; i < ts->rx_count; i++) { + memset(pStr, 0x0, lsize); + snprintf(pTmp, sizeof(pTmp), "Rx%02d | ", i); + strncat(pStr, pTmp, lsize); + + for (j = 0; j < ts->tx_count; j++) { + snprintf(pTmp, sizeof(pTmp), " %3d", ts->pFrame[(j * ts->rx_count) + i]); + + if (ts->pFrame[(j * ts->rx_count) + i] < *min) + *min = ts->pFrame[(j * ts->rx_count) + i]; + + if (ts->pFrame[(j * ts->rx_count) + i] > *max) + *max = ts->pFrame[(j * ts->rx_count) + i]; + + strncat(pStr, pTmp, lsize); + } + input_raw_frame_info(true, &ts->client->dev, "%s\n", pStr); + } + kfree(pStr); +} + +static int sec_ts_read_frame(struct sec_ts_data *ts, u8 type, short *min, + short *max, bool save_result) +{ + unsigned int readbytes = 0xFF; + unsigned char *pRead = NULL; + u8 mode = TYPE_INVALID_DATA; + int ret = 0; + int i = 0; + int j = 0; + short *temp = NULL; + + input_raw_info(true, &ts->client->dev, "%s: type %d\n", __func__, type); + + /* set data length, allocation buffer memory */ + readbytes = ts->rx_count * ts->tx_count * 2; + + pRead = kzalloc(readbytes, GFP_KERNEL); + if (!pRead) + return -ENOMEM; + + /* set OPCODE and data type */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_MUTU_RAW_TYPE, &type, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Set rawdata type failed\n", __func__); + goto ErrorExit; + } + + sec_ts_delay(50); + + if (type == TYPE_OFFSET_DATA_SDC) { + /* excute selftest for real cap offset data, because real cap data is not memory data in normal touch. */ + char para = TO_TOUCH_MODE; + + disable_irq(ts->client->irq); + + ret = execute_selftest(ts, save_result); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: execute_selftest failed\n", __func__); + enable_irq(ts->client->irq); + goto ErrorRelease; + } + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_POWER_MODE, ¶, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Set rawdata type failed\n", __func__); + enable_irq(ts->client->irq); + goto ErrorRelease; + } + + enable_irq(ts->client->irq); + } + + /* read data */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_TOUCH_RAWDATA, pRead, readbytes); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read rawdata failed!\n", __func__); + goto ErrorRelease; + } + + memset(ts->pFrame, 0x00, readbytes); + + for (i = 0; i < readbytes; i += 2) + ts->pFrame[i / 2] = pRead[i + 1] + (pRead[i] << 8); + + *min = *max = ts->pFrame[0]; + +#ifdef DEBUG_MSG + input_info(true, &ts->client->dev, "%s: 02X%02X%02X readbytes=%d\n", __func__, + pRead[0], pRead[1], pRead[2], readbytes); +#endif + sec_ts_print_frame(ts, min, max); + + temp = kzalloc(readbytes, GFP_KERNEL); + if (!temp) + goto ErrorRelease; + + memcpy(temp, ts->pFrame, ts->tx_count * ts->rx_count * 2); + memset(ts->pFrame, 0x00, ts->tx_count * ts->rx_count * 2); + + for (i = 0; i < ts->tx_count; i++) { + for (j = 0; j < ts->rx_count; j++) + ts->pFrame[(j * ts->tx_count) + i] = temp[(i * ts->rx_count) + j]; + } + + kfree(temp); + +ErrorRelease: + /* release data monitory (unprepare AFE data memory) */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_MUTU_RAW_TYPE, &mode, 1); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Set rawdata type failed\n", __func__); + +ErrorExit: + kfree(pRead); + + return ret; +} + +static void sec_ts_print_channel(struct sec_ts_data *ts) +{ + unsigned char *pStr = NULL; + unsigned char pTmp[16] = { 0 }; + int i = 0, j = 0, k = 0; + + if (!ts->tx_count) + return; + + pStr = vzalloc(7 * (ts->tx_count + 1)); + if (!pStr) + return; + + memset(pStr, 0x0, 7 * (ts->tx_count + 1)); + snprintf(pTmp, sizeof(pTmp), " TX"); + strncat(pStr, pTmp, 7 * ts->tx_count); + + for (k = 0; k < ts->tx_count; k++) { + snprintf(pTmp, sizeof(pTmp), " %02d", k); + strncat(pStr, pTmp, 7 * ts->tx_count); + } + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + + memset(pStr, 0x0, 7 * (ts->tx_count + 1)); + snprintf(pTmp, sizeof(pTmp), " +"); + strncat(pStr, pTmp, 7 * ts->tx_count); + + for (k = 0; k < ts->tx_count; k++) { + snprintf(pTmp, sizeof(pTmp), "------"); + strncat(pStr, pTmp, 7 * ts->tx_count); + } + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + + memset(pStr, 0x0, 7 * (ts->tx_count + 1)); + snprintf(pTmp, sizeof(pTmp), " | "); + strncat(pStr, pTmp, 7 * ts->tx_count); + + for (i = 0; i < (ts->tx_count + ts->rx_count) * 2; i += 2) { + if (j == ts->tx_count) { + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + input_raw_info(true, &ts->client->dev, "\n"); + memset(pStr, 0x0, 7 * (ts->tx_count + 1)); + snprintf(pTmp, sizeof(pTmp), " RX"); + strncat(pStr, pTmp, 7 * ts->tx_count); + + for (k = 0; k < ts->tx_count; k++) { + snprintf(pTmp, sizeof(pTmp), " %02d", k); + strncat(pStr, pTmp, 7 * ts->tx_count); + } + + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + + memset(pStr, 0x0, 7 * (ts->tx_count + 1)); + snprintf(pTmp, sizeof(pTmp), " +"); + strncat(pStr, pTmp, 7 * ts->tx_count); + + for (k = 0; k < ts->tx_count; k++) { + snprintf(pTmp, sizeof(pTmp), "------"); + strncat(pStr, pTmp, 7 * ts->tx_count); + } + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + + memset(pStr, 0x0, 7 * (ts->tx_count + 1)); + snprintf(pTmp, sizeof(pTmp), " | "); + strncat(pStr, pTmp, 7 * ts->tx_count); + } else if (j && !(j % ts->tx_count)) { + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + memset(pStr, 0x0, 7 * (ts->tx_count + 1)); + snprintf(pTmp, sizeof(pTmp), " | "); + strncat(pStr, pTmp, 7 * ts->tx_count); + } + + snprintf(pTmp, sizeof(pTmp), " %5d", ts->pFrame[j]); + strncat(pStr, pTmp, 7 * ts->tx_count); + + j++; + } + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + vfree(pStr); +} + +static int sec_ts_read_channel(struct sec_ts_data *ts, u8 type, + short *min, short *max, bool save_result) +{ + unsigned char *pRead = NULL; + u8 mode = TYPE_INVALID_DATA; + int ret = 0; + int ii = 0; + int jj = 0; + unsigned int data_length = (ts->tx_count + ts->rx_count) * 2; + u8 w_data; + + input_raw_info(true, &ts->client->dev, "%s: type %d\n", __func__, type); + + pRead = kzalloc(data_length, GFP_KERNEL); + if (!pRead) + return -ENOMEM; + + /* set OPCODE and data type */ + w_data = type; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SELF_RAW_TYPE, &w_data, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Set rawdata type failed\n", __func__); + goto out_read_channel; + } + + sec_ts_delay(50); + + if (type == TYPE_OFFSET_DATA_SDC) { + /* execute selftest for real cap offset data, + * because real cap data is not memory data in normal touch. + */ + char para = TO_TOUCH_MODE; + disable_irq(ts->client->irq); + ret = execute_selftest(ts, save_result); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: execute_selftest failed!\n", __func__); + enable_irq(ts->client->irq); + goto err_read_data; + } + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_POWER_MODE, ¶, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: set rawdata type failed!\n", __func__); + enable_irq(ts->client->irq); + goto err_read_data; + } + enable_irq(ts->client->irq); + /* end */ + } + /* read data */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_TOUCH_SELF_RAWDATA, pRead, data_length); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read rawdata failed!\n", __func__); + goto err_read_data; + } + + /* clear all pFrame data */ + memset(ts->pFrame, 0x00, data_length); + + for (ii = 0; ii < data_length; ii += 2) { + ts->pFrame[jj] = ((pRead[ii] << 8) | pRead[ii + 1]); + + if (ii == 0) + *min = *max = ts->pFrame[jj]; + + *min = min(*min, ts->pFrame[jj]); + *max = max(*max, ts->pFrame[jj]); + + jj++; + } + + sec_ts_print_channel(ts); + +err_read_data: + /* release data monitory (unprepare AFE data memory) */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SELF_RAW_TYPE, &mode, 1); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Set rawdata type failed\n", __func__); + +out_read_channel: + kfree(pRead); + + return ret; +} + +static void get_gap_data_x_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char *buff = NULL; + int ii; + int node_gap = 0; + char temp[SEC_CMD_STR_LEN] = { 0 }; + + sec_cmd_set_default_result(sec); + + buff = kzalloc(ts->tx_count * ts->rx_count * CMD_RESULT_WORD_LEN, GFP_KERNEL); + if (!buff) + return; + + for (ii = 0; ii < (ts->rx_count * ts->tx_count); ii++) { + if ((ii + 1) % (ts->tx_count) != 0) { + if (ts->pFrame[ii] > ts->pFrame[ii + 1]) + node_gap = 100 - (ts->pFrame[ii + 1] * 100 / ts->pFrame[ii]); + else + node_gap = 100 - (ts->pFrame[ii] * 100 / ts->pFrame[ii + 1]); + + snprintf(temp, CMD_RESULT_WORD_LEN, "%d,", node_gap); + strncat(buff, temp, CMD_RESULT_WORD_LEN); + memset(temp, 0x00, SEC_CMD_STR_LEN); + } + } + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, ts->tx_count * ts->rx_count * CMD_RESULT_WORD_LEN)); + sec->cmd_state = SEC_CMD_STATUS_OK; + kfree(buff); +} + +static void get_gap_data_y_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char *buff = NULL; + int ii; + int node_gap = 0; + char temp[SEC_CMD_STR_LEN] = { 0 }; + + sec_cmd_set_default_result(sec); + + buff = kzalloc(ts->tx_count * ts->rx_count * CMD_RESULT_WORD_LEN, GFP_KERNEL); + if (!buff) + return; + + for (ii = 0; ii < (ts->rx_count * ts->tx_count); ii++) { + if (ii < (ts->rx_count - 1) * ts->tx_count) { + if (ts->pFrame[ii] > ts->pFrame[ii + ts->tx_count]) + node_gap = 100 - (ts->pFrame[ii + ts->tx_count] * 100 / ts->pFrame[ii]); + else + node_gap = 100 - (ts->pFrame[ii] * 100 / ts->pFrame[ii + ts->tx_count]); + + snprintf(temp, CMD_RESULT_WORD_LEN, "%d,", node_gap); + strncat(buff, temp, CMD_RESULT_WORD_LEN); + memset(temp, 0x00, SEC_CMD_STR_LEN); + } + } + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, ts->tx_count * ts->rx_count * CMD_RESULT_WORD_LEN)); + sec->cmd_state = SEC_CMD_STATUS_OK; + kfree(buff); +} + +static int sec_ts_read_raw_data(struct sec_ts_data *ts, + struct sec_cmd_data *sec, struct sec_ts_test_mode *mode) +{ + int ii; + int ret = 0; + char temp[SEC_CMD_STR_LEN] = { 0 }; + char *buff; + + buff = kzalloc(ts->tx_count * ts->rx_count * CMD_RESULT_WORD_LEN, GFP_KERNEL); + if (!buff) + goto error_alloc_mem; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", + __func__); + goto error_power_state; + } + + input_raw_info(true, &ts->client->dev, "%s: %d, %s\n", + __func__, mode->type, mode->allnode ? "ALL" : ""); + + ret = sec_ts_fix_tmode(ts, TOUCH_SYSTEM_MODE_TOUCH, TOUCH_MODE_STATE_TOUCH); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to fix tmode\n", + __func__); + goto error_test_fail; + } + + if (mode->frame_channel) + ret = sec_ts_read_channel(ts, mode->type, &mode->min, &mode->max, true); + else + ret = sec_ts_read_frame(ts, mode->type, &mode->min, &mode->max, true); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to read frame\n", + __func__); + goto error_test_fail; + } + + if (mode->allnode) { + if (mode->frame_channel) { + for (ii = 0; ii < (ts->rx_count + ts->tx_count); ii++) { + snprintf(temp, CMD_RESULT_WORD_LEN, "%d,", ts->pFrame[ii]); + strncat(buff, temp, CMD_RESULT_WORD_LEN); + + memset(temp, 0x00, SEC_CMD_STR_LEN); + } + } else { + for (ii = 0; ii < (ts->rx_count * ts->tx_count); ii++) { + snprintf(temp, CMD_RESULT_WORD_LEN, "%d,", ts->pFrame[ii]); + strncat(buff, temp, CMD_RESULT_WORD_LEN); + + memset(temp, 0x00, SEC_CMD_STR_LEN); + } + } + } else { + snprintf(buff, SEC_CMD_STR_LEN, "%d,%d", mode->min, mode->max); + } + + ret = sec_ts_release_tmode(ts); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to release tmode\n", + __func__); + goto error_test_fail; + } + + if (!sec) + goto out_rawdata; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, ts->tx_count * ts->rx_count * CMD_RESULT_WORD_LEN)); + sec->cmd_state = SEC_CMD_STATUS_OK; + +out_rawdata: + kfree(buff); + + sec_ts_locked_release_all_finger(ts); + + return ret; + +error_test_fail: +error_power_state: + kfree(buff); +error_alloc_mem: + if (!sec) + return ret; + + snprintf(temp, SEC_CMD_STR_LEN, "FAIL"); + sec_cmd_set_cmd_result(sec, temp, SEC_CMD_STR_LEN); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + + sec_ts_locked_release_all_finger(ts); + + return ret; +} + +static int sec_ts_read_rawp2p_data(struct sec_ts_data *ts, + struct sec_cmd_data *sec, struct sec_ts_test_mode *mode) +{ + int ii; + int ret = 0; + char temp[SEC_CMD_STR_LEN] = { 0 }; + char *buff; + char para = TO_TOUCH_MODE; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", + __func__); + goto error_power_state; + } + + buff = kzalloc(ts->tx_count * ts->rx_count * CMD_RESULT_WORD_LEN, GFP_KERNEL); + if (!buff) + goto error_alloc_mem; + + input_info(true, &ts->client->dev, "%s: %d, %s\n", + __func__, mode->type, mode->allnode ? "ALL" : ""); + + disable_irq(ts->client->irq); + + ret = sec_ts_p2p_tmode(ts); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to fix p2p tmode\n", + __func__); + goto error_tmode_fail; + } + + ret = execute_p2ptest(ts); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to fix p2p test\n", + __func__); + goto error_test_fail; + } + + if (mode->frame_channel) + ret = sec_ts_read_channel(ts, mode->type, &mode->min, &mode->max, true); + else + ret = sec_ts_read_frame(ts, mode->type, &mode->min, &mode->max, true); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to read frame\n", + __func__); + goto error_test_fail; + } + + if (mode->allnode) { + if (mode->frame_channel) { + for (ii = 0; ii < (ts->rx_count + ts->tx_count); ii++) { + snprintf(temp, CMD_RESULT_WORD_LEN, "%d,", ts->pFrame[ii]); + strncat(buff, temp, CMD_RESULT_WORD_LEN); + + memset(temp, 0x00, SEC_CMD_STR_LEN); + } + } else { + for (ii = 0; ii < (ts->rx_count * ts->tx_count); ii++) { + snprintf(temp, CMD_RESULT_WORD_LEN, "%d,", ts->pFrame[ii]); + strncat(buff, temp, CMD_RESULT_WORD_LEN); + + memset(temp, 0x00, SEC_CMD_STR_LEN); + } + } + } else { + snprintf(buff, SEC_CMD_STR_LEN, "%d,%d", mode->min, mode->max); + } + + if (!sec) + goto out_rawdata; + + sec_ts_locked_release_all_finger(ts); + sec_ts_delay(30); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_POWER_MODE, ¶, 1); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: set rawdata type failed!\n", __func__); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, ts->tx_count * ts->rx_count * CMD_RESULT_WORD_LEN)); + sec->cmd_state = SEC_CMD_STATUS_OK; + +out_rawdata: + enable_irq(ts->client->irq); + kfree(buff); + + return ret; +error_test_fail: +error_tmode_fail: + kfree(buff); + enable_irq(ts->client->irq); +error_alloc_mem: +error_power_state: + if (!sec) + return ret; + + snprintf(temp, SEC_CMD_STR_LEN, "FAIL"); + sec_cmd_set_cmd_result(sec, temp, SEC_CMD_STR_LEN); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + + return ret; +} + + +static void get_fw_ver_bin(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + + sec_cmd_set_default_result(sec); + + snprintf(buff, sizeof(buff), "SE%02X%02X%02X", + ts->plat_data->panel_revision, ts->plat_data->img_version_of_bin[2], + ts->plat_data->img_version_of_bin[3]); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void get_fw_ver_ic(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + int ret; + u8 fw_ver[4]; + + sec_cmd_set_default_result(sec); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", + __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + return; + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_IMG_VERSION, fw_ver, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: firmware version read error\n", __func__); + snprintf(buff, sizeof(buff), "%s", "NG"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + return; + } + + snprintf(buff, sizeof(buff), "SE%02X%02X%02X", + ts->plat_data->panel_revision, fw_ver[2], fw_ver[3]); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void get_config_ver(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[22] = { 0 }; + + sec_cmd_set_default_result(sec); + + snprintf(buff, sizeof(buff), "%s_SE_%02X%02X", + ts->plat_data->model_name, + ts->plat_data->config_version_of_ic[2], ts->plat_data->config_version_of_ic[3]); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void get_threshold(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[20] = { 0 }; + char threshold[2] = { 0 }; + int ret; + + sec_cmd_set_default_result(sec); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + goto err; + } + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_TOUCH_MODE_FOR_THRESHOLD, threshold, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: threshold write type failed. ret: %d\n", __func__, ret); + snprintf(buff, sizeof(buff), "%s", "NG"); + goto err; + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_TOUCH_THRESHOLD, threshold, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read threshold fail!\n", __func__); + snprintf(buff, sizeof(buff), "%s", "NG"); + goto err; + } + + input_info(true, &ts->client->dev, "0x%02X, 0x%02X\n", + threshold[0], threshold[1]); + + snprintf(buff, sizeof(buff), "%d", (threshold[0] << 8) | threshold[1]); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + + return; +err: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + return; +} + +static void module_off_master(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[3] = { 0 }; + int ret = 0; + + ret = sec_ts_stop_device(ts); + + if (ret == 0) + snprintf(buff, sizeof(buff), "%s", "OK"); + else + snprintf(buff, sizeof(buff), "%s", "NG"); + + sec_cmd_set_default_result(sec); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + if (strncmp(buff, "OK", 2) == 0) + sec->cmd_state = SEC_CMD_STATUS_OK; + else + sec->cmd_state = SEC_CMD_STATUS_FAIL; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void module_on_master(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[3] = { 0 }; + int ret = 0; + + ret = sec_ts_start_device(ts); + // FIXME + // if (ts->input_dev->disabled) { + sec_ts_set_lowpowermode(ts, TO_LOWPOWER_MODE); + ts->power_status = SEC_TS_STATE_LPM; + // } + + if (ret == 0) + snprintf(buff, sizeof(buff), "%s", "OK"); + else + snprintf(buff, sizeof(buff), "%s", "NG"); + + sec_cmd_set_default_result(sec); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + if (strncmp(buff, "OK", 2) == 0) + sec->cmd_state = SEC_CMD_STATUS_OK; + else + sec->cmd_state = SEC_CMD_STATUS_FAIL; + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void get_chip_vendor(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + + strncpy(buff, "SEC", sizeof(buff)); + sec_cmd_set_default_result(sec); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void get_chip_name(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + + if (ts->plat_data->img_version_of_ic[0] == 0x02) + strncpy(buff, "MC44", sizeof(buff)); + else if (ts->plat_data->img_version_of_ic[0] == 0x05) + strncpy(buff, "A552", sizeof(buff)); + else if (ts->plat_data->img_version_of_ic[0] == 0x09) + strncpy(buff, "Y661", sizeof(buff)); + else if (ts->plat_data->img_version_of_ic[0] == 0x10) + strncpy(buff, "Y761", sizeof(buff)); + else if (ts->plat_data->img_version_of_ic[0] == 0x17) + strncpy(buff, "Y771", sizeof(buff)); + else + strncpy(buff, "N/A", sizeof(buff)); + + sec_cmd_set_default_result(sec); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void set_mis_cal_spec(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + char wreg[5] = { 0 }; + int ret; + + sec_cmd_set_default_result(sec); + + if (ts->plat_data->mis_cal_check == 0) { + input_err(true, &ts->client->dev, "%s: [ERROR] not support\n", __func__); + goto NG; + } else if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", __func__); + goto NG; + } else { + if ((sec->cmd_param[0] < 0 || sec->cmd_param[0] > 255) || + (sec->cmd_param[1] < 0 || sec->cmd_param[1] > 255) || + (sec->cmd_param[2] < 0 || sec->cmd_param[2] > 255)) { + snprintf(buff, sizeof(buff), "%s", "NG"); + goto NG; + } else { + wreg[0] = sec->cmd_param[0]; + wreg[1] = sec->cmd_param[1]; + wreg[2] = sec->cmd_param[2]; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_MIS_CAL_SPEC, wreg, 3); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Misscal spec write failed. ret: %d\n", __func__, ret); + goto NG; + } else { + input_info(true, &ts->client->dev, "%s: tx gap=%d, rx gap=%d, peak=%d\n", + __func__, wreg[0], wreg[1], wreg[2]); + sec_ts_delay(20); + } + } + } + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + return; + +NG: + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + return; +} + +/* + * ## Mis Cal result ## + * FF : initial value in Firmware. + * FD : Cal fail case + * F4 : i2c fail case (5F) + * F3 : i2c fail case (5E) + * F2 : power off state + * F1 : not support mis cal concept + * F0 : initial value in fucntion + * 08 : Ambient Ambient condition check(PEAK) result 0 (PASS), 1(FAIL) + * 04 : Ambient Ambient condition check(DIFF MAX TX) result 0 (PASS), 1(FAIL) + * 02 : Ambient Ambient condition check(DIFF MAX RX) result 0 (PASS), 1(FAIL) + * 01 : Wet Wet mode result 0 (PASS), 1(FAIL) + * 00 : Pass + */ +static void get_mis_cal_info(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + char mis_cal_data = 0xF0; + char wreg[5] = { 0 }; + int ret; + char buff_all[16] = { 0 }; + + sec_cmd_set_default_result(sec); + + if (ts->plat_data->mis_cal_check == 0) { + input_err(true, &ts->client->dev, "%s: [ERROR] not support\n", __func__); + mis_cal_data = 0xF1; + goto NG; + } else if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", __func__); + mis_cal_data = 0xF2; + goto NG; + } else { + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_MIS_CAL_READ, &mis_cal_data, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: i2c fail!, %d\n", __func__, ret); + mis_cal_data = 0xF3; + goto NG; + } else { + input_info(true, &ts->client->dev, "%s: miss cal data : %d\n", __func__, mis_cal_data); + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_MIS_CAL_SPEC, wreg, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: i2c fail!, %d\n", __func__, ret); + mis_cal_data = 0xF4; + goto NG; + } else { + input_info(true, &ts->client->dev, "%s: miss cal spec : %d,%d,%d,%d\n", + __func__, wreg[0], wreg[1], wreg[2], wreg[3]); + } + } + + snprintf(buff, sizeof(buff), "%d", mis_cal_data); + snprintf(buff_all, sizeof(buff_all), "%d,%d,%d,%d,%d", mis_cal_data, wreg[0], wreg[1], wreg[2], wreg[3]); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff_all); + return; + +NG: + snprintf(buff, sizeof(buff), "%d", mis_cal_data); + snprintf(buff_all, sizeof(buff_all), "%d,%d,%d,%d", mis_cal_data, 0, 0, 0); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff_all); +} + +static void get_wet_mode(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + char wet_mode_info = 0; + int ret; + + sec_cmd_set_default_result(sec); + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_WET_MODE, &wet_mode_info, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: i2c fail!, %d\n", __func__, ret); + goto NG; + } + + snprintf(buff, sizeof(buff), "%d", wet_mode_info); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + return; + +NG: + snprintf(buff, sizeof(buff), "NG"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + +} + +static void get_x_num(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + + sec_cmd_set_default_result(sec); + snprintf(buff, sizeof(buff), "%d", ts->tx_count); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void get_y_num(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + + sec_cmd_set_default_result(sec); + snprintf(buff, sizeof(buff), "%d", ts->rx_count); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static u32 checksum_sum(struct sec_ts_data *ts, u8 *chunk_data, u32 size) +{ + u32 data_32 = 0; + u32 checksum = 0; + u8 *fd = chunk_data; + int i; + + for (i = 0; i < size/4; i++) { + data_32 = (((fd[(i*4)+0]&0xFF)<<0) | ((fd[(i*4)+1]&0xFF)<<8) | + ((fd[(i*4)+2]&0xFF)<<16) | ((fd[(i*4)+3]&0xFF)<<24)); + checksum += data_32; + } + + checksum &= 0xFFFFFFFF; + + input_info(true, &ts->client->dev, "%s: checksum = [%x]\n", __func__, checksum); + + return checksum; +} + +static u32 get_bin_checksum(struct sec_ts_data *ts, const u8 *data, size_t size) +{ + + int i; + fw_header *fw_hd; + fw_chunk *fw_ch; + u8 *fd = (u8 *)data; + u32 data_32; + u32 checksum = 0; + u32 chunk_checksum[3]; + u32 img_checksum; + + fw_hd = (fw_header *)fd; + fd += sizeof(fw_header); + + if (fw_hd->signature != SEC_TS_FW_HEADER_SIGN) { + input_err(true, &ts->client->dev, "%s: firmware header error = %08X\n", __func__, fw_hd->signature); + return 0; + } + + for (i = 0; i < (fw_hd->totalsize/4); i++) { + data_32 = (((data[(i*4)+0]&0xFF)<<0) | ((data[(i*4)+1]&0xFF)<<8) | + ((data[(i*4)+2]&0xFF)<<16) | ((data[(i*4)+3]&0xFF)<<24)); + checksum += data_32; + } + + input_info(true, &ts->client->dev, "%s: fw_hd->totalsize = [%d] checksum = [%x]\n", + __func__, fw_hd->totalsize, checksum); + + checksum &= 0xFFFFFFFF; + if (checksum != 0) { + input_err(true, &ts->client->dev, "%s: Checksum fail! = %08X\n", __func__, checksum); + return 0; + } + + for (i = 0; i < fw_hd->num_chunk; i++) { + fw_ch = (fw_chunk *)fd; + + input_info(true, &ts->client->dev, "%s: [%d] 0x%08X, 0x%08X, 0x%08X, 0x%08X\n", __func__, i, + fw_ch->signature, fw_ch->addr, fw_ch->size, fw_ch->reserved); + + if (fw_ch->signature != SEC_TS_FW_CHUNK_SIGN) { + input_err(true, &ts->client->dev, "%s: firmware chunk error = %08X\n", + __func__, fw_ch->signature); + return 0; + } + fd += sizeof(fw_chunk); + + checksum = checksum_sum(ts, fd, fw_ch->size); + chunk_checksum[i] = checksum; + fd += fw_ch->size; + } + + img_checksum = chunk_checksum[0] + chunk_checksum[1]; + + return img_checksum; +} + +static void get_checksum_data(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + char csum_result[4] = { 0 }; + char data[5] = { 0 }; + u8 cal_result; + u8 nv_result; + u8 temp; + u8 csum = 0; + int ret, i; + + u32 ic_checksum; + u32 img_checksum; + const struct firmware *fw_entry; + char fw_path[SEC_TS_MAX_FW_PATH]; + u8 fw_ver[4]; + + sec_cmd_set_default_result(sec); + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + goto err; + } + + disable_irq(ts->client->irq); + + ts->plat_data->power(ts, false); + ts->power_status = SEC_TS_STATE_POWER_OFF; + sec_ts_delay(50); + + ts->plat_data->power(ts, true); + ts->power_status = SEC_TS_STATE_POWER_ON; + sec_ts_delay(70); + + ret = sec_ts_wait_for_ready(ts, SEC_TS_ACK_BOOT_COMPLETE); + if (ret < 0) { + enable_irq(ts->client->irq); + input_err(true, &ts->client->dev, "%s: boot complete failed\n", __func__); + snprintf(buff, sizeof(buff), "%s", "NG"); + goto err; + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_FIRMWARE_INTEGRITY, &data[0], 1); + if (ret < 0 || (data[0] != 0x80)) { + enable_irq(ts->client->irq); + input_err(true, &ts->client->dev, "%s: firmware integrity failed, ret:%d, data:%X\n", + __func__, ret, data[0]); + snprintf(buff, sizeof(buff), "%s", "NG"); + goto err; + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_BOOT_STATUS, &data[1], 1); + if (ret < 0 || (data[1] != SEC_TS_STATUS_APP_MODE)) { + enable_irq(ts->client->irq); + input_err(true, &ts->client->dev, "%s: boot status failed, ret:%d, data:%X\n", __func__, ret, data[0]); + snprintf(buff, sizeof(buff), "%s", "NG"); + goto err; + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_TS_STATUS, &data[2], 4); + if (ret < 0 || (data[3] == TOUCH_SYSTEM_MODE_FLASH)) { + enable_irq(ts->client->irq); + input_err(true, &ts->client->dev, "%s: touch status failed, ret:%d, data:%X\n", __func__, ret, data[3]); + snprintf(buff, sizeof(buff), "%s", "NG"); + goto err; + } + + sec_ts_reinit(ts); + + enable_irq(ts->client->irq); + + input_err(true, &ts->client->dev, "%s: data[0]:%X, data[1]:%X, data[3]:%X\n", __func__, data[0], data[1], data[3]); + + temp = DO_FW_CHECKSUM | DO_PARA_CHECKSUM; + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_GET_CHECKSUM, &temp, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: send get_checksum_cmd fail!\n", __func__); + snprintf(buff, sizeof(buff), "%s", "SendCMDfail"); + goto err; + } + + sec_ts_delay(20); + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_GET_CHECKSUM, csum_result, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read get_checksum result fail!\n", __func__); + snprintf(buff, sizeof(buff), "%s", "ReadCSUMfail"); + goto err; + } + + nv_result = get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_FAC_RESULT); + nv_result += get_tsp_nvm_data(ts, SEC_TS_NVM_OFFSET_CAL_COUNT); + + cal_result = sec_ts_read_calibration_report(ts); + + for (i = 0; i < 4; i++) + csum += csum_result[i]; + + csum += nv_result; + csum += cal_result; + + csum = ~csum; + + if (ts->plat_data->firmware_name != NULL) { + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_IMG_VERSION, fw_ver, 4); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: firmware version read error\n", __func__); + + for (i = 0; i < 4; i++) { + if (ts->plat_data->img_version_of_bin[i] != fw_ver[i]) { + input_err(true, &ts->client->dev, "%s: do not matched version info [%d]:[%x]!=[%x] and skip!\n", + __func__, i, ts->plat_data->img_version_of_bin[i], fw_ver[i]); + goto out; + } + } + + ic_checksum = (((csum_result[0]&0xFF)<<24) | ((csum_result[1]&0xFF)<<16) | + ((csum_result[2]&0xFF)<<8) | ((csum_result[3]&0xFF)<<0)); + + snprintf(fw_path, SEC_TS_MAX_FW_PATH, "%s", ts->plat_data->firmware_name); + + if (request_firmware(&fw_entry, fw_path, &ts->client->dev) != 0) { + input_err(true, &ts->client->dev, "%s: firmware is not available\n", __func__); + snprintf(buff, sizeof(buff), "%s", "NG"); + goto err; + } + + img_checksum = get_bin_checksum(ts, fw_entry->data, fw_entry->size); + + release_firmware(fw_entry); + + input_info(true, &ts->client->dev, "%s: img_checksum=[0x%X], ic_checksum=[0x%X]\n", + __func__, img_checksum, ic_checksum); + + if (img_checksum != ic_checksum) { + input_err(true, &ts->client->dev, "%s: img_checksum=[0x%X] != ic_checksum=[0x%X]!!!\n", + __func__, img_checksum, ic_checksum); + snprintf(buff, sizeof(buff), "%s", "NG"); + goto err; + } + } +out: + + input_info(true, &ts->client->dev, "%s: checksum = %02X\n", __func__, csum); + snprintf(buff, sizeof(buff), "%02X", csum); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + return; + +err: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; +} + +static void run_reference_read(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SET; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void run_reference_read_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SET; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void get_reference(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + short val = 0; + int node = 0; + + sec_cmd_set_default_result(sec); + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", + __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + return; + } + + node = sec_ts_check_index(ts); + if (node < 0) + return; + + val = ts->pFrame[node]; + snprintf(buff, sizeof(buff), "%d", val); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void run_rawcap_read(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SDC; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void run_rawcap_read_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SDC; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void get_rawcap(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + short val = 0; + int node = 0; + + sec_cmd_set_default_result(sec); + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", + __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + return; + } + + node = sec_ts_check_index(ts); + if (node < 0) + return; + + val = ts->pFrame[node]; + snprintf(buff, sizeof(buff), "%d", val); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void run_delta_read(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_RAW_DATA; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void run_delta_read_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_RAW_DATA; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void get_delta(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + short val = 0; + int node = 0; + + sec_cmd_set_default_result(sec); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: [ERROR] Touch is stopped\n", + __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + return; + } + + node = sec_ts_check_index(ts); + if (node < 0) + return; + + val = ts->pFrame[node]; + snprintf(buff, sizeof(buff), "%d", val); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void run_raw_p2p_min_read_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_RAW_DATA_P2P_MIN; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_rawp2p_data(ts, sec, &mode); +} + +static void run_raw_p2p_max_read_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_RAW_DATA_P2P_MAX; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_rawp2p_data(ts, sec, &mode); +} + +/* self reference : send TX power in TX channel, receive in TX channel */ +static void run_self_reference_read(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SET; + mode.frame_channel = TEST_MODE_READ_CHANNEL; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void run_self_reference_read_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SET; + mode.frame_channel = TEST_MODE_READ_CHANNEL; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void run_self_rawcap_read(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SDC; + mode.frame_channel = TEST_MODE_READ_CHANNEL; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void run_self_rawcap_read_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_OFFSET_DATA_SDC; + mode.frame_channel = TEST_MODE_READ_CHANNEL; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void run_self_delta_read(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_RAW_DATA; + mode.frame_channel = TEST_MODE_READ_CHANNEL; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +static void run_self_delta_read_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + struct sec_ts_test_mode mode; + + sec_cmd_set_default_result(sec); + + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_RAW_DATA; + mode.frame_channel = TEST_MODE_READ_CHANNEL; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, sec, &mode); +} + +/* + * sec_ts_run_rawdata_all : read all raw data + * + * when you want to read full raw data (full_read : true) + * "mutual/self 3, 5, 29, 1, 19" data will be saved in log + * + * otherwise, (full_read : false, especially on boot time) + * only "mutual 3, 5, 29" data will be saved in log + */ +void sec_ts_run_rawdata_all(struct sec_ts_data *ts, bool full_read) +{ + short min, max; + int ret, i, read_num; + u8 test_type[5] = {TYPE_AMBIENT_DATA, TYPE_DECODED_DATA, + TYPE_SIGNAL_DATA, TYPE_OFFSET_DATA_SET, TYPE_OFFSET_DATA_SDC}; + + ts->tsp_dump_lock = 1; + input_raw_data_clear(); + input_raw_info(true, &ts->client->dev, + "%s: start (noise:%d, wet:%d)##\n", + __func__, ts->touch_noise_status, ts->wet_mode); + + ret = sec_ts_fix_tmode(ts, TOUCH_SYSTEM_MODE_TOUCH, TOUCH_MODE_STATE_TOUCH); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: failed to fix tmode\n", + __func__); + goto out; + } + + if (full_read) { + read_num = 5; + } else { + read_num = 3; + test_type[read_num - 1] = TYPE_OFFSET_DATA_SDC; + } + + for (i = 0; i < read_num; i++) { + ret = sec_ts_read_frame(ts, test_type[i], &min, &max, false); + if (ret < 0) { + input_raw_info(true, &ts->client->dev, + "%s: mutual %d : error ## ret:%d\n", + __func__, test_type[i], ret); + goto out; + } else { + input_raw_info(true, &ts->client->dev, + "%s: mutual %d : Max/Min %d,%d ##\n", + __func__, test_type[i], max, min); + } + sec_ts_delay(20); + + if (full_read) { + ret = sec_ts_read_channel(ts, test_type[i], &min, &max, false); + if (ret < 0) { + input_raw_info(true, &ts->client->dev, + "%s: self %d : error ## ret:%d\n", + __func__, test_type[i], ret); + goto out; + } else { + input_raw_info(true, &ts->client->dev, + "%s: self %d : Max/Min %d,%d ##\n", + __func__, test_type[i], max, min); + } + sec_ts_delay(20); + } + } + + sec_ts_release_tmode(ts); + +out: + input_raw_info(true, &ts->client->dev, "%s: ito : %02X %02X %02X %02X\n", + __func__, ts->ito_test[0], ts->ito_test[1] + , ts->ito_test[2], ts->ito_test[3]); + + input_raw_info(true, &ts->client->dev, "%s: done (noise:%d, wet:%d)##\n", + __func__, ts->touch_noise_status, ts->wet_mode); + ts->tsp_dump_lock = 0; + + sec_ts_locked_release_all_finger(ts); +} + +static void run_rawdata_read_all(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[16] = { 0 }; + + sec_cmd_set_default_result(sec); + + if (ts->tsp_dump_lock == 1) { + input_err(true, &ts->client->dev, "%s: already checking now\n", __func__); + snprintf(buff, sizeof(buff), "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out; + } + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: IC is power off\n", __func__); + snprintf(buff, sizeof(buff), "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out; + } + + sec_ts_run_rawdata_all(ts, true); + + snprintf(buff, sizeof(buff), "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; +out: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +/* Use TSP NV area + * buff[0] : offset from user NVM storage + * buff[1] : length of stroed data - 1 (ex. using 1byte, value is 1 - 1 = 0) + * buff[2] : write data + * buff[..] : cont. + */ +void set_tsp_nvm_data_clear(struct sec_ts_data *ts, u8 offset) +{ + char buff[4] = { 0 }; + int ret; + + input_dbg(true, &ts->client->dev, "%s\n", __func__); + + buff[0] = offset; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_NVM, buff, 3); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: nvm write failed. ret: %d\n", __func__, ret); + + sec_ts_delay(20); +} + +int get_tsp_nvm_data(struct sec_ts_data *ts, u8 offset) +{ + char buff[2] = { 0 }; + int ret; + + /* SENSE OFF -> CELAR EVENT STACK -> READ NV -> SENSE ON */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_OFF, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail to write Sense_off\n", __func__); + goto out_nvm; + } + + input_dbg(false, &ts->client->dev, "%s: SENSE OFF\n", __func__); + + sec_ts_delay(100); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CLEAR_EVENT_STACK, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: i2c write clear event failed\n", __func__); + goto out_nvm; + } + + input_dbg(false, &ts->client->dev, "%s: CLEAR EVENT STACK\n", __func__); + + sec_ts_delay(100); + + sec_ts_locked_release_all_finger(ts); + + /* send NV data using command + * Use TSP NV area : in this model, use only one byte + * buff[0] : offset from user NVM storage + * buff[1] : length of stroed data - 1 (ex. using 1byte, value is 1 - 1 = 0) + */ + memset(buff, 0x00, 2); + buff[0] = offset; + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_NVM, buff, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: nvm send command failed. ret: %d\n", __func__, ret); + goto out_nvm; + } + + sec_ts_delay(20); + + /* read NV data + * Use TSP NV area : in this model, use only one byte + */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_NVM, buff, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: nvm send command failed. ret: %d\n", __func__, ret); + goto out_nvm; + } + + input_info(true, &ts->client->dev, "%s: offset:%u data:%02X\n", __func__, offset, buff[0]); + +out_nvm: + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to write Sense_on\n", __func__); + + sec_ts_delay(300); + + input_dbg(false, &ts->client->dev, "%s: SENSE ON\n", __func__); + + return buff[0]; +} + +int get_tsp_nvm_data_by_size(struct sec_ts_data *ts, u8 offset, int length, u8 *data) +{ + char *buff = NULL; + int ret; + + buff = kzalloc(length, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + input_info(true, &ts->client->dev, "%s: offset:%u, length:%d, size:%zu\n", __func__, offset, length, sizeof(data)); + + /* SENSE OFF -> CELAR EVENT STACK -> READ NV -> SENSE ON */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_OFF, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail to write Sense_off\n", __func__); + goto out_nvm; + } + + input_dbg(true, &ts->client->dev, "%s: SENSE OFF\n", __func__); + + sec_ts_delay(100); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CLEAR_EVENT_STACK, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: i2c write clear event failed\n", __func__); + goto out_nvm; + } + + input_dbg(true, &ts->client->dev, "%s: CLEAR EVENT STACK\n", __func__); + + sec_ts_delay(100); + + sec_ts_locked_release_all_finger(ts); + + /* send NV data using command + * Use TSP NV area : in this model, use only one byte + * buff[0] : offset from user NVM storage + * buff[1] : length of stroed data - 1 (ex. using 1byte, value is 1 - 1 = 0) + */ + memset(buff, 0x00, 2); + buff[0] = offset; + buff[1] = length - 1; + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_NVM, buff, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: nvm send command failed. ret: %d\n", __func__, ret); + goto out_nvm; + } + + sec_ts_delay(20); + + /* read NV data + * Use TSP NV area : in this model, use only one byte + */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_NVM, buff, length); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: nvm send command failed. ret: %d\n", __func__, ret); + goto out_nvm; + } + + memcpy(data, buff, length); + +out_nvm: + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to write Sense_on\n", __func__); + + sec_ts_delay(300); + + input_dbg(true, &ts->client->dev, "%s: SENSE ON\n", __func__); + + kfree(buff); + + return ret; +} + +#define GLOVE_MODE_EN (1 << 0) +#define CLEAR_COVER_EN (1 << 1) +#define FAST_GLOVE_MODE_EN (1 << 2) + +static void glove_mode(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + int glove_mode_enables = 0; + + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { + snprintf(buff, sizeof(buff), "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + int retval; + + if (sec->cmd_param[0]) + glove_mode_enables |= GLOVE_MODE_EN; + else + glove_mode_enables &= ~(GLOVE_MODE_EN); + + retval = sec_ts_glove_mode_enables(ts, glove_mode_enables); + + if (retval < 0) { + input_err(true, &ts->client->dev, "%s: failed, retval = %d\n", __func__, retval); + snprintf(buff, sizeof(buff), "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + snprintf(buff, sizeof(buff), "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + } + } + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_exit(sec); +} + +static void clear_cover_mode(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + + input_info(true, &ts->client->dev, "%s: start clear_cover_mode %s\n", __func__, buff); + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 3) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + if (sec->cmd_param[0] > 1) { + ts->flip_enable = true; + ts->cover_type = sec->cmd_param[1]; + ts->cover_cmd = (u8)ts->cover_type; + } else { + ts->flip_enable = false; + } + + /* initial if condition ambiguous */ + if (!(ts->power_status == SEC_TS_STATE_POWER_OFF && ts->reinit_done)) { + if (ts->flip_enable) + sec_ts_set_cover_type(ts, true); + else + sec_ts_set_cover_type(ts, false); + } + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + } + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_exit(sec); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +}; + +static void dead_zone_enable(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + int ret; + char data = 0; + + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + data = sec->cmd_param[0]; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_EDGE_DEADZONE, &data, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to set deadzone\n", __func__); + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto err_set_dead_zone; + } + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + } + +err_set_dead_zone: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +}; + +static void drawing_test_enable(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; +#ifdef SEC_TS_SUPPORT_CUSTOMLIB + int ret; +#endif + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + if (ts->use_customlib) { + if (sec->cmd_param[0]) + ts->lowpower_mode &= ~SEC_TS_MODE_CUSTOMLIB_FORCE_KEY; + else + ts->lowpower_mode |= SEC_TS_MODE_CUSTOMLIB_FORCE_KEY; + + #ifdef SEC_TS_SUPPORT_CUSTOMLIB + ret = sec_ts_set_custom_library(ts); + if (ret < 0) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + } + #endif + + } else { + snprintf(buff, sizeof(buff), "%s", "NA"); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + } + } + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +}; + +static void sec_ts_swap(u8 *a, u8 *b) +{ + u8 temp = *a; + + *a = *b; + *b = temp; +} + +static void rearrange_sft_result(u8 *data, int length) +{ + int i, nlength; + + nlength = length - (length % 4); + + for (i = 0; i < nlength; i += 4) { + sec_ts_swap(&data[i], &data[i + 3]); + sec_ts_swap(&data[i + 1], &data[i + 2]); + } +} + +int execute_selftest(struct sec_ts_data *ts, bool save_result) +{ + u8 pStr[50] = {0}; + u8 pTmp[20]; + int rc = 0; + u8 tpara[2] = {0x23, 0x40}; + u8 *rBuff; + int i; + int result_size = SEC_TS_SELFTEST_REPORT_SIZE + ts->tx_count * ts->rx_count * 2; + + /* save selftest result in flash */ + if (save_result) + tpara[0] = 0x23; + else + tpara[0] = 0xA3; + + rBuff = kzalloc(result_size, GFP_KERNEL); + if (!rBuff) + return -ENOMEM; + + input_info(true, &ts->client->dev, "%s: Self test start!\n", __func__); + rc = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SELFTEST, tpara, 2); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: Send selftest cmd failed!\n", __func__); + goto err_exit; + } + + sec_ts_delay(350); + + rc = sec_ts_wait_for_ready(ts, SEC_TS_VENDOR_ACK_SELF_TEST_DONE); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: Selftest execution time out!\n", __func__); + goto err_exit; + } + + input_raw_info(true, &ts->client->dev, "%s: Self test done!\n", __func__); + + rc = ts->sec_ts_i2c_read(ts, SEC_TS_READ_SELFTEST_RESULT, rBuff, result_size); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: Selftest execution time out!\n", __func__); + goto err_exit; + } + + rearrange_sft_result(rBuff, SEC_TS_SELFTEST_REPORT_SIZE); + + for (i = 0; i < 80; i += 4) { + if (i / 4 == 0) + strncat(pStr, "SIG ", 5); + else if (i / 4 == 1) + strncat(pStr, "VER ", 5); + else if (i / 4 == 2) + strncat(pStr, "SIZ ", 5); + else if (i / 4 == 3) + strncat(pStr, "CRC ", 5); + else if (i / 4 == 4) + strncat(pStr, "RES ", 5); + else if (i / 4 == 5) + strncat(pStr, "COU ", 5); + else if (i / 4 == 6) + strncat(pStr, "PAS ", 5); + else if (i / 4 == 7) + strncat(pStr, "FAI ", 5); + else if (i / 4 == 8) + strncat(pStr, "CHA ", 5); + else if (i / 4 == 9) + strncat(pStr, "AMB ", 5); + else if (i / 4 == 10) + strncat(pStr, "RXS ", 5); + else if (i / 4 == 11) + strncat(pStr, "TXS ", 5); + else if (i / 4 == 12) + strncat(pStr, "RXO ", 5); + else if (i / 4 == 13) + strncat(pStr, "TXO ", 5); + else if (i / 4 == 14) + strncat(pStr, "RXG ", 5); + else if (i / 4 == 15) + strncat(pStr, "TXG ", 5); + else if (i / 4 == 16) + strncat(pStr, "RXR ", 5); + else if (i / 4 == 17) + strncat(pStr, "TXT ", 5); + else if (i / 4 == 18) + strncat(pStr, "RXT ", 5); + else if (i / 4 == 19) + strncat(pStr, "TXR ", 5); + + snprintf(pTmp, sizeof(pTmp), "%2X, %2X, %2X, %2X", + rBuff[i], rBuff[i + 1], rBuff[i + 2], rBuff[i + 3]); + strncat(pStr, pTmp, strnlen(pTmp, sizeof(pTmp))); + + if (i / 4 == 4) { + if ((rBuff[i + 3] & 0x30) != 0)// RX, RX open check. + rc = 0; + else + rc = 1; + + ts->ito_test[0] = rBuff[i]; + ts->ito_test[1] = rBuff[i + 1]; + ts->ito_test[2] = rBuff[i + 2]; + ts->ito_test[3] = rBuff[i + 3]; + } + if (i % 8 == 4) { + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + memset(pStr, 0x00, sizeof(pStr)); + } else { + strncat(pStr, " ", 3); + } + } + +err_exit: + kfree(rBuff); + return rc; +} + +static void run_trx_short_test(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[1024 + 256] = {0}; + int rc; + int size = SEC_TS_SELFTEST_REPORT_SIZE + ts->tx_count * ts->rx_count * 2; + char para = TO_TOUCH_MODE; + u8 *rBuff = NULL; + int ii; + u8 data[32] = { 0 }; + int sum = 0; + u8 pStr[50] = {0}; + u8 pTmp[20]; + + sec_cmd_set_default_result(sec); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Touch is stopped!\n", __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + return; + } + + rBuff = kzalloc(size, GFP_KERNEL); + if (!rBuff) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + return; + } + + memset(rBuff, 0x00, size); + memset(data, 0x00, 32); + + disable_irq(ts->client->irq); + + input_info(true, &ts->client->dev, "%s: set power mode to test mode\n", __func__); + data[0] = 0x02; + rc = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_POWER_MODE, data, 1); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: set test mode failed\n", __func__); + goto err_trx_short; + } + + input_info(true, &ts->client->dev, "%s: clear event stack\n", __func__); + rc = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CLEAR_EVENT_STACK, NULL, 0); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: clear event stack failed\n", __func__); + goto err_trx_short; + } + + input_info(true, &ts->client->dev, "%s: self test start\n", __func__); + + data[0] = 0x04; + + rc = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SELFTEST, data, 1); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: Send selftest cmd failed!\n", __func__); + goto err_trx_short; + } + + sec_ts_delay(700); + + rc = sec_ts_wait_for_ready(ts, SEC_TS_VENDOR_ACK_SELF_TEST_DONE); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: Selftest execution time out!\n", __func__); + goto err_trx_short; + } + + input_info(true, &ts->client->dev, "%s: self test done\n", __func__); + + rc = ts->sec_ts_i2c_read(ts, SEC_TS_READ_SELFTEST_RESULT, rBuff, size); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: Selftest execution time out!\n", __func__); + goto err_trx_short; + } + + rc = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_POWER_MODE, ¶, 1); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: mode changed failed!\n", __func__); + goto err_trx_short; + } + + sec_ts_reinit(ts); + + input_info(true, &ts->client->dev, "%s: Test Result %02X, %02X, %02X, %02X\n", + __func__, rBuff[16], rBuff[17], rBuff[18], rBuff[19]); + + enable_irq(ts->client->irq); + + memcpy(data, &rBuff[48], 32); + + for (ii = 0; ii < 32; ii++) + sum += data[ii]; + + if (!sum) + goto test_ok; + + for (ii = 0; ii < 8; ii += 4) { + if (ii / 4 == 0) + strncat(pStr, "RXO", 5); + if (ii / 4 == 1) + strncat(pStr, "TXO", 5); + if (ii / 4 == 2) + strncat(pStr, "RXG", 5); + if (ii / 4 == 3) + strncat(pStr, "TXG", 5); + if (ii / 4 == 4) + strncat(pStr, "RXR", 5); + if (ii / 4 == 5) + strncat(pStr, "TXT", 5); + if (ii / 4 == 6) + strncat(pStr, "RXT", 5); + if (ii / 4 == 7) + strncat(pStr, "TXR", 5); + + snprintf(pTmp, sizeof(pTmp), "%2X, %2X, %2X, %2X", + data[ii], data[ii + 1], data[ii + 2], data[ii + 3]); + strncat(pStr, pTmp, strnlen(pTmp, sizeof(pTmp))); + if (ii % 8 == 4) { + input_raw_info(true, &ts->client->dev, "%s\n", pStr); + strncat(buff, pStr, strlen(pStr)); + + memset(pStr, 0x00, sizeof(pStr)); + } else { + strncat(pStr, " ", 3); + } + } + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + + kfree(rBuff); + return; + +test_ok: + snprintf(buff, sizeof(buff), "%s", "OK"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + + kfree(rBuff); + return; + +err_trx_short: + + ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_POWER_MODE, ¶, 1); + enable_irq(ts->client->irq); + + snprintf(buff, sizeof(buff), "%s", "NG"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + + kfree(rBuff); + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + return; +} + +int sec_ts_execute_force_calibration(struct sec_ts_data *ts, int cal_mode) +{ + int rc = -1; + u8 cmd; + + input_info(true, &ts->client->dev, "%s: %d\n", __func__, cal_mode); + + if (cal_mode == OFFSET_CAL_SET) + cmd = SEC_TS_CMD_FACTORY_PANELCALIBRATION; + else if (cal_mode == AMBIENT_CAL) + cmd = SEC_TS_CMD_CALIBRATION_AMBIENT; +#ifdef USE_PRESSURE_SENSOR + else if (cal_mode == PRESSURE_CAL) + cmd = SEC_TS_CMD_CALIBRATION_PRESSURE; +#endif + else + return rc; + + if (ts->sec_ts_i2c_write(ts, cmd, NULL, 0) < 0) { + input_err(true, &ts->client->dev, "%s: Write Cal commend failed!\n", __func__); + return rc; + } + + sec_ts_delay(1000); + + rc = sec_ts_wait_for_ready(ts, SEC_TS_VENDOR_ACK_OFFSET_CAL_DONE); + + return rc; +} + +static void run_force_calibration(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = {0}; + int rc; + struct sec_ts_test_mode mode; + char mis_cal_data = 0xF0; + + sec_cmd_set_default_result(sec); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Touch is stopped!\n", __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + goto out_force_cal_before_irq_ctrl; + } + + if (ts->touch_count > 0) { + snprintf(buff, sizeof(buff), "%s", "NG_FINGER_ON"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out_force_cal_before_irq_ctrl; + } + + disable_irq(ts->client->irq); + + rc = sec_ts_execute_force_calibration(ts, OFFSET_CAL_SET); + if (rc < 0) { + snprintf(buff, sizeof(buff), "%s", "FAIL"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out_force_cal; + } + + if (ts->plat_data->mis_cal_check) { + sec_ts_delay(50); + + buff[0] = 0; + rc = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_STATEMANAGE_ON, buff, 1); + if (rc < 0) { + input_err(true, &ts->client->dev, + "%s: mis_cal_check error[1] ret: %d\n", __func__, rc); + } + + buff[0] = 0x2; + buff[1] = 0x2; + rc = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CHG_SYSMODE, buff, 2); + if (rc < 0) { + input_err(true, &ts->client->dev, + "%s: mis_cal_check error[2] ret: %d\n", __func__, rc); + } + + input_err(true, &ts->client->dev, "%s: try mis Cal. check\n", __func__); + rc = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_MIS_CAL_CHECK, NULL, 0); + if (rc < 0) { + input_err(true, &ts->client->dev, + "%s: mis_cal_check error[3] ret: %d\n", __func__, rc); + } + sec_ts_delay(200); + + rc = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_MIS_CAL_READ, &mis_cal_data, 1); + if (rc < 0) { + input_err(true, &ts->client->dev, "%s: i2c fail!, %d\n", __func__, rc); + mis_cal_data = 0xF3; + } else { + input_info(true, &ts->client->dev, "%s: miss cal data : %d\n", __func__, mis_cal_data); + } + + buff[0] = 1; + rc = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_STATEMANAGE_ON, buff, 1); + if (rc < 0) { + input_err(true, &ts->client->dev, + "%s: mis_cal_check error[4] ret: %d\n", __func__, rc); + } + + if (mis_cal_data) { + memset(&mode, 0x00, sizeof(struct sec_ts_test_mode)); + mode.type = TYPE_AMBIENT_DATA; + mode.allnode = TEST_MODE_ALL_NODE; + + sec_ts_read_raw_data(ts, NULL, &mode); + snprintf(buff, sizeof(buff), "%s", "MIS CAL"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out_force_cal; + } + } + + ts->cal_status = sec_ts_read_calibration_report(ts); + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + +out_force_cal: + enable_irq(ts->client->irq); + +out_force_cal_before_irq_ctrl: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + +} + +static void get_force_calibration(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = {0}; + int rc; + + sec_cmd_set_default_result(sec); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Touch is stopped!\n", __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + return; + } + + rc = sec_ts_read_calibration_report(ts); + if (rc < 0) { + snprintf(buff, sizeof(buff), "%s", "FAIL"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else if (rc == SEC_TS_STATUS_CALIBRATION_SEC) { + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + } else { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); + +} + +static void set_lowpower_mode(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + char buff[SEC_CMD_STR_LEN] = { 0 }; + + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + } else { + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + } + +#if 0 + /* set lowpower mode by spay, edge_swipe function. */ + ts->lowpower_mode = sec->cmd_param[0]; +#endif + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + + sec_cmd_set_cmd_exit(sec); + + return; + +} + +static void set_wirelesscharger_mode(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + int ret; + bool mode; + u8 w_data[1] = {0x00}; + + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 3) + goto OUT; + + if (sec->cmd_param[0] == 0) { + ts->charger_mode |= SEC_TS_BIT_CHARGER_MODE_NO; + mode = false; + } else { + ts->charger_mode &= (~SEC_TS_BIT_CHARGER_MODE_NO); + mode = true; + } + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: fail to enable w-charger status, POWER_STATUS=OFF\n", __func__); + goto NG; + } + + if (sec->cmd_param[0] == 1) + ts->charger_mode = ts->charger_mode | SEC_TS_BIT_CHARGER_MODE_WIRELESS_CHARGER; + else if (sec->cmd_param[0] == 3) + ts->charger_mode = ts->charger_mode | SEC_TS_BIT_CHARGER_MODE_WIRELESS_BATTERY_PACK; + else if (mode == false) + ts->charger_mode = ts->charger_mode & (~SEC_TS_BIT_CHARGER_MODE_WIRELESS_CHARGER) & (~SEC_TS_BIT_CHARGER_MODE_WIRELESS_BATTERY_PACK); + + w_data[0] = ts->charger_mode; + ret = ts->sec_ts_i2c_write(ts, SET_TS_CMD_SET_CHARGER_MODE, w_data, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Failed to send command 74\n", __func__); + goto NG; + } + + input_err(true, &ts->client->dev, "%s: %s, status =%x\n", + __func__, (mode) ? "wireless enable" : "wireless disable", ts->charger_mode); + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + return; + +NG: + input_err(true, &ts->client->dev, "%s: %s, status =%x\n", + __func__, (mode) ? "wireless enable" : "wireless disable", ts->charger_mode); + +OUT: + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); +} + +static void spay_enable(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) + goto NG; + + if (sec->cmd_param[0]) { + if (ts->use_customlib) + ts->lowpower_mode |= SEC_TS_MODE_CUSTOMLIB_SPAY; + } else { + if (ts->use_customlib) + ts->lowpower_mode &= ~SEC_TS_MODE_CUSTOMLIB_SPAY; + } + + input_info(true, &ts->client->dev, "%s: %02X\n", __func__, ts->lowpower_mode); + + #ifdef SEC_TS_SUPPORT_CUSTOMLIB + if (ts->use_customlib) + sec_ts_set_custom_library(ts); + #endif + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + return; + +NG: + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); +} + +static void aod_enable(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) + goto NG; + + if (sec->cmd_param[0]) { + if (ts->use_customlib) + ts->lowpower_mode |= SEC_TS_MODE_CUSTOMLIB_AOD; + } else { + if (ts->use_customlib) + ts->lowpower_mode &= ~SEC_TS_MODE_CUSTOMLIB_AOD; + } + + input_info(true, &ts->client->dev, "%s: %02X\n", __func__, ts->lowpower_mode); + + #ifdef SEC_TS_SUPPORT_CUSTOMLIB + if (ts->use_customlib) + sec_ts_set_custom_library(ts); + #endif + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + return; + +NG: + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); +} + +/* + * flag 1 : set edge handler + * 2 : set (portrait, normal) edge zone data + * 4 : set (portrait, normal) dead zone data + * 8 : set landscape mode data + * 16 : mode clear + * data + * 0x30, FFF (y start), FFF (y end), FF(direction) + * 0x31, FFFF (edge zone) + * 0x32, FF (up x), FF (down x), FFFF (y) + * 0x33, FF (mode), FFF (edge), FFF (dead zone) + * case + * edge handler set : 0x30.... + * booting time : 0x30... + 0x31... + * normal mode : 0x32... (+0x31...) + * landscape mode : 0x33... + * landscape -> normal (if same with old data) : 0x33, 0 + * landscape -> normal (etc) : 0x32.... + 0x33, 0 + */ + +void set_grip_data_to_ic(struct sec_ts_data *ts, u8 flag) +{ + u8 data[8] = { 0 }; + + input_info(true, &ts->client->dev, "%s: flag: %02X (clr,lan,nor,edg,han)\n", __func__, flag); + + if (flag & G_SET_EDGE_HANDLER) { + if (ts->grip_edgehandler_direction == 0) { + data[0] = 0x0; + data[1] = 0x0; + data[2] = 0x0; + data[3] = 0x0; + } else { + data[0] = (ts->grip_edgehandler_start_y >> 4) & 0xFF; + data[1] = (ts->grip_edgehandler_start_y << 4 & 0xF0) | ((ts->grip_edgehandler_end_y >> 8) & 0xF); + data[2] = ts->grip_edgehandler_end_y & 0xFF; + data[3] = ts->grip_edgehandler_direction & 0x3; + } + ts->sec_ts_i2c_write(ts, SEC_TS_CMD_EDGE_HANDLER, data, 4); + input_info(true, &ts->client->dev, "%s: 0x%02X %02X,%02X,%02X,%02X\n", + __func__, SEC_TS_CMD_EDGE_HANDLER, data[0], data[1], data[2], data[3]); + } + + if (flag & G_SET_EDGE_ZONE) { + data[0] = (ts->grip_edge_range >> 8) & 0xFF; + data[1] = ts->grip_edge_range & 0xFF; + ts->sec_ts_i2c_write(ts, SEC_TS_CMD_EDGE_AREA, data, 2); + input_info(true, &ts->client->dev, "%s: 0x%02X %02X,%02X\n", + __func__, SEC_TS_CMD_EDGE_AREA, data[0], data[1]); + } + + if (flag & G_SET_NORMAL_MODE) { + data[0] = ts->grip_deadzone_up_x & 0xFF; + data[1] = ts->grip_deadzone_dn_x & 0xFF; + data[2] = (ts->grip_deadzone_y >> 8) & 0xFF; + data[3] = ts->grip_deadzone_y & 0xFF; + ts->sec_ts_i2c_write(ts, SEC_TS_CMD_DEAD_ZONE, data, 4); + input_info(true, &ts->client->dev, "%s: 0x%02X %02X,%02X,%02X,%02X\n", + __func__, SEC_TS_CMD_DEAD_ZONE, data[0], data[1], data[2], data[3]); + } + + if (flag & G_SET_LANDSCAPE_MODE) { + data[0] = ts->grip_landscape_mode & 0x1; + data[1] = (ts->grip_landscape_edge >> 4) & 0xFF; + data[2] = (ts->grip_landscape_edge << 4 & 0xF0) | ((ts->grip_landscape_deadzone >> 8) & 0xF); + data[3] = ts->grip_landscape_deadzone & 0xFF; + ts->sec_ts_i2c_write(ts, SEC_TS_CMD_LANDSCAPE_MODE, data, 4); + input_info(true, &ts->client->dev, "%s: 0x%02X %02X,%02X,%02X,%02X\n", + __func__, SEC_TS_CMD_LANDSCAPE_MODE, data[0], data[1], data[2], data[3]); + } + + if (flag & G_CLR_LANDSCAPE_MODE) { + data[0] = ts->grip_landscape_mode; + ts->sec_ts_i2c_write(ts, SEC_TS_CMD_LANDSCAPE_MODE, data, 1); + input_info(true, &ts->client->dev, "%s: 0x%02X %02X\n", + __func__, SEC_TS_CMD_LANDSCAPE_MODE, data[0]); + } +} + +/* + * index 0 : set edge handler + * 1 : portrait (normal) mode + * 2 : landscape mode + * + * data + * 0, X (direction), X (y start), X (y end) + * direction : 0 (off), 1 (left), 2 (right) + * ex) echo set_grip_data,0,2,600,900 > cmd + * + * 1, X (edge zone), X (dead zone up x), X (dead zone down x), X (dead zone y) + * ex) echo set_grip_data,1,200,10,50,1500 > cmd + * + * 2, 1 (landscape mode), X (edge zone), X (dead zone) + * ex) echo set_grip_data,2,1,200,100 > cmd + * + * 2, 0 (portrait mode) + * ex) echo set_grip_data,2,0 > cmd + */ + +static void set_grip_data(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + u8 mode = G_NONE; + + sec_cmd_set_default_result(sec); + + memset(buff, 0, sizeof(buff)); + + mutex_lock(&ts->device_mutex); + + if (sec->cmd_param[0] == 0) { // edge handler + if (sec->cmd_param[1] == 0) { // clear + ts->grip_edgehandler_direction = 0; + } else if (sec->cmd_param[1] < 3) { + ts->grip_edgehandler_direction = sec->cmd_param[1]; + ts->grip_edgehandler_start_y = sec->cmd_param[2]; + ts->grip_edgehandler_end_y = sec->cmd_param[3]; + } else { + input_err(true, &ts->client->dev, "%s: cmd1 is abnormal, %d (%d)\n", + __func__, sec->cmd_param[1], __LINE__); + goto err_grip_data; + } + + mode = mode | G_SET_EDGE_HANDLER; + set_grip_data_to_ic(ts, mode); + + } else if (sec->cmd_param[0] == 1) { // normal mode + if (ts->grip_edge_range != sec->cmd_param[1]) + mode = mode | G_SET_EDGE_ZONE; + + ts->grip_edge_range = sec->cmd_param[1]; + ts->grip_deadzone_up_x = sec->cmd_param[2]; + ts->grip_deadzone_dn_x = sec->cmd_param[3]; + ts->grip_deadzone_y = sec->cmd_param[4]; + mode = mode | G_SET_NORMAL_MODE; + + if (ts->grip_landscape_mode == 1) { + ts->grip_landscape_mode = 0; + mode = mode | G_CLR_LANDSCAPE_MODE; + } + set_grip_data_to_ic(ts, mode); + } else if (sec->cmd_param[0] == 2) { // landscape mode + if (sec->cmd_param[1] == 0) { // normal mode + ts->grip_landscape_mode = 0; + mode = mode | G_CLR_LANDSCAPE_MODE; + } else if (sec->cmd_param[1] == 1) { + ts->grip_landscape_mode = 1; + ts->grip_landscape_edge = sec->cmd_param[2]; + ts->grip_landscape_deadzone = sec->cmd_param[3]; + mode = mode | G_SET_LANDSCAPE_MODE; + } else { + input_err(true, &ts->client->dev, "%s: cmd1 is abnormal, %d (%d)\n", + __func__, sec->cmd_param[1], __LINE__); + goto err_grip_data; + } + set_grip_data_to_ic(ts, mode); + } else { + input_err(true, &ts->client->dev, "%s: cmd0 is abnormal, %d", __func__, sec->cmd_param[0]); + goto err_grip_data; + } + + mutex_unlock(&ts->device_mutex); + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + return; + +err_grip_data: + mutex_unlock(&ts->device_mutex); + + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); +} + +/* + * Set/Get Dex Mode 0xE7 + * 0: Disable dex mode + * 1: Full screen mode + * 2: Iris mode + */ +static void dex_enable(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + int ret; + + sec_cmd_set_default_result(sec); + + if (!ts->plat_data->support_dex) { + input_err(true, &ts->client->dev, "%s: not support DeX mode\n", __func__); + goto NG; + } + + if ((sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) && + (sec->cmd_param[1] < 0 || sec->cmd_param[1] > 1)) { + input_err(true, &ts->client->dev, "%s: not support param\n", __func__); + goto NG; + } + + sec_ts_unlocked_release_all_finger(ts); + + ts->dex_mode = sec->cmd_param[0]; + if (ts->dex_mode) { + input_err(true, &ts->client->dev, "%s: set DeX touch_pad mode%s\n", + __func__, sec->cmd_param[1] ? " & Iris mode" : ""); + ts->input_dev = ts->input_dev_pad; + if (sec->cmd_param[1]) { + /* Iris mode */ + ts->dex_mode = 0x02; + ts->dex_name = "[DeXI]"; + } else { + ts->dex_name = "[DeX]"; + } + } else { + input_err(true, &ts->client->dev, "%s: set touch mode\n", __func__); + ts->input_dev = ts->input_dev_touch; + ts->dex_name = ""; + } + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Touch is stopped!\n", __func__); + goto NG; + } + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_DEX_MODE, &ts->dex_mode, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to set dex %smode\n", __func__, + sec->cmd_param[1] ? "iris " : ""); + goto NG; + } + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + return; + +NG: + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); +} + +static void brush_enable(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + int ret; + + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out; + } + + ts->brush_mode = sec->cmd_param[0]; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Touch is stopped!\n", __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + goto out; + } + + input_info(true, &ts->client->dev, + "%s: set brush mode %s\n", __func__, ts->brush_mode ? "enable" : "disable"); + + /* - 0: Disable Artcanvas min phi mode + * - 1: Enable Artcanvas min phi mode + */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_BRUSH_MODE, &ts->brush_mode, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to set brush mode\n", __func__); + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out; + } + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; +out: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void set_touchable_area(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + int ret; + + sec_cmd_set_default_result(sec); + + if (sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) { + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out; + } + + ts->touchable_area = sec->cmd_param[0]; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Touch is stopped!\n", __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + goto out; + } + + input_info(true, &ts->client->dev, + "%s: set 16:9 mode %s\n", __func__, ts->touchable_area ? "enable" : "disable"); + + /* - 0: Disable 16:9 mode + * - 1: Enable 16:9 mode + */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_TOUCHABLE_AREA, &ts->touchable_area, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to set 16:9 mode\n", __func__); + snprintf(buff, sizeof(buff), "%s", "NG"); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + goto out; + } + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; +out: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec_cmd_set_cmd_exit(sec); + + input_info(true, &ts->client->dev, "%s: %s\n", __func__, buff); +} + +static void set_log_level(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + char tBuff[2] = { 0 }; + u8 w_data[1] = {0x00}; + int ret; + + sec_cmd_set_default_result(sec); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Touch is stopped!\n", __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + return; + } + + if ((sec->cmd_param[0] < 0 || sec->cmd_param[0] > 1) || + (sec->cmd_param[1] < 0 || sec->cmd_param[1] > 1) || + (sec->cmd_param[2] < 0 || sec->cmd_param[2] > 1) || + (sec->cmd_param[3] < 0 || sec->cmd_param[3] > 1) || + (sec->cmd_param[4] < 0 || sec->cmd_param[4] > 1) || + (sec->cmd_param[5] < 0 || sec->cmd_param[5] > 1) || + (sec->cmd_param[6] < 0 || sec->cmd_param[6] > 1) || + (sec->cmd_param[7] < 0 || sec->cmd_param[7] > 1)) { + input_err(true, &ts->client->dev, "%s: para out of range\n", __func__); + snprintf(buff, sizeof(buff), "%s", "Para out of range"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_FAIL; + return; + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_STATUS_EVENT_TYPE, tBuff, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Read Event type enable status fail\n", __func__); + snprintf(buff, sizeof(buff), "%s", "Read Stat Fail"); + goto err; + } + + input_info(true, &ts->client->dev, "%s: STATUS_EVENT enable = 0x%02X, 0x%02X\n", + __func__, tBuff[0], tBuff[1]); + + tBuff[0] = BIT_STATUS_EVENT_VENDOR_INFO(sec->cmd_param[6]); + tBuff[1] = BIT_STATUS_EVENT_ERR(sec->cmd_param[0]) | + BIT_STATUS_EVENT_INFO(sec->cmd_param[1]) | + BIT_STATUS_EVENT_USER_INPUT(sec->cmd_param[2]); + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_STATUS_EVENT_TYPE, tBuff, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Write Event type enable status fail\n", __func__); + snprintf(buff, sizeof(buff), "%s", "Write Stat Fail"); + goto err; + } + + if (sec->cmd_param[0] == 1 && sec->cmd_param[1] == 1 && + sec->cmd_param[2] == 1 && sec->cmd_param[3] == 1 && + sec->cmd_param[4] == 1 && sec->cmd_param[5] == 1 && + sec->cmd_param[6] == 1 && sec->cmd_param[7] == 1) { + w_data[0] = 0x1; + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_VENDOR_EVENT_LEVEL, w_data, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Write Vendor Event Level fail\n", __func__); + snprintf(buff, sizeof(buff), "%s", "Write Stat Fail"); + goto err; + } + } else { + w_data[0] = 0x0; + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SET_VENDOR_EVENT_LEVEL, w_data, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Write Vendor Event Level fail\n", __func__); + snprintf(buff, sizeof(buff), "%s", "Write Stat Fail"); + goto err; + } + } + + input_info(true, &ts->client->dev, "%s: ERROR : %d, INFO : %d, USER_INPUT : %d, INFO_CUSTOMLIB : %d, VENDOR_INFO : %d, VENDOR_EVENT_LEVEL : %d\n", + __func__, sec->cmd_param[0], sec->cmd_param[1], sec->cmd_param[2], sec->cmd_param[5], sec->cmd_param[6], w_data[0]); + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_OK; + return; +err: + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; +} + +static void debug(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + struct sec_ts_data *ts = container_of(sec, struct sec_ts_data, sec); + char buff[SEC_CMD_STR_LEN] = { 0 }; + + sec_cmd_set_default_result(sec); + + ts->debug_flag = sec->cmd_param[0]; + + snprintf(buff, sizeof(buff), "%s", "OK"); + sec->cmd_state = SEC_CMD_STATUS_OK; + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); +} + +static void not_support_cmd(void *device_data) +{ + struct sec_cmd_data *sec = (struct sec_cmd_data *)device_data; + char buff[SEC_CMD_STR_LEN] = { 0 }; + + sec_cmd_set_default_result(sec); + snprintf(buff, sizeof(buff), "%s", "NA"); + + sec_cmd_set_cmd_result(sec, buff, strnlen(buff, sizeof(buff))); + sec->cmd_state = SEC_CMD_STATUS_NOT_APPLICABLE; + sec_cmd_set_cmd_exit(sec); +} + +int sec_ts_fn_init(struct sec_ts_data *ts) +{ + int retval; + + retval = sec_cmd_init(&ts->sec, sec_cmds, + ARRAY_SIZE(sec_cmds), SEC_CLASS_DEVT_TSP); + if (retval < 0) { + input_err(true, &ts->client->dev, + "%s: Failed to sec_cmd_init\n", __func__); + goto exit; + } + + retval = sysfs_create_group(&ts->sec.fac_dev->kobj, + &cmd_attr_group); + if (retval < 0) { + input_err(true, &ts->client->dev, + "%s: Failed to create sysfs attributes\n", __func__); + goto exit; + } + + retval = sysfs_create_link(&ts->sec.fac_dev->kobj, + &ts->input_dev->dev.kobj, "input"); + if (retval < 0) { + input_err(true, &ts->client->dev, + "%s: Failed to create input symbolic link\n", + __func__); + goto exit; + } + + ts->reinit_done = true; + + return 0; + +exit: + return retval; +} + +void sec_ts_fn_remove(struct sec_ts_data *ts) +{ + input_err(true, &ts->client->dev, "%s\n", __func__); + + sysfs_remove_link(&ts->sec.fac_dev->kobj, "input"); + + sysfs_remove_group(&ts->sec.fac_dev->kobj, + &cmd_attr_group); + + sec_cmd_exit(&ts->sec, SEC_CLASS_DEVT_TSP); + +} diff --git a/drivers/input/touchscreen/sec_mmi/sec_ts_fw.c b/drivers/input/touchscreen/sec_mmi/sec_ts_fw.c new file mode 100644 index 000000000000..7dce0f0dcaea --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/sec_ts_fw.c @@ -0,0 +1,1034 @@ +/* drivers/input/touchscreen/sec_ts_fw.c + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * http://www.samsungsemi.com/ + * + * Core file for Samsung TSC driver + * + * 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. + */ + +#include "sec_ts.h" + +#define SEC_TS_FW_BLK_SIZE 256 + +enum { + BUILT_IN = 0, + UMS, + BL, + FFU, +}; + +static int sec_ts_enter_fw_mode(struct sec_ts_data *ts) +{ + int ret; + u8 fw_update_mode_passwd[] = {0x55, 0xAC}; + u8 fw_status; + u8 id[3]; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_ENTER_FW_MODE, fw_update_mode_passwd, sizeof(fw_update_mode_passwd)); + sec_ts_delay(20); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: write fail, enter_fw_mode\n", __func__); + return 0; + } + + input_info(true, &ts->client->dev, "%s: write ok, enter_fw_mode - 0x%x 0x%x 0x%x\n", + __func__, SEC_TS_CMD_ENTER_FW_MODE, fw_update_mode_passwd[0], fw_update_mode_passwd[1]); + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_BOOT_STATUS, &fw_status, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read fail, read_boot_status\n", __func__); + return 0; + } + if (fw_status != SEC_TS_STATUS_BOOT_MODE) { + input_err(true, &ts->client->dev, "%s: enter fail! read_boot_status = 0x%x\n", __func__, fw_status); + return 0; + } + + input_info(true, &ts->client->dev, "%s: Success! read_boot_status = 0x%x\n", __func__, fw_status); + + sec_ts_delay(10); + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_ID, id, 3); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read id fail\n", __func__); + return 0; + } + + ts->boot_ver[0] = id[0]; + ts->boot_ver[1] = id[1]; + ts->boot_ver[2] = id[2]; + + ts->flash_page_size = SEC_TS_FW_BLK_SIZE_DEFAULT; + + input_info(true, &ts->client->dev, "%s: read_boot_id = %02X%02X%02X\n", __func__, id[0], id[1], id[2]); + + return 1; +} + +static int sec_ts_sw_reset(struct sec_ts_data *ts) +{ + int ret; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SW_RESET, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: write fail, sw_reset\n", __func__); + return 0; + } + + sec_ts_delay(100); + + ret = sec_ts_wait_for_ready(ts, SEC_TS_ACK_BOOT_COMPLETE); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: time out\n", __func__); + return 0; + } + + input_info(true, &ts->client->dev, "%s: sw_reset\n", __func__); + + /* Sense_on */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: write fail, Sense_on\n", __func__); + return 0; + } + + return ret; +} + +static void sec_ts_save_version_of_bin(struct sec_ts_data *ts, const fw_header *fw_hd) +{ + ts->plat_data->img_version_of_bin[3] = ((fw_hd->img_ver >> 24) & 0xff); + ts->plat_data->img_version_of_bin[2] = ((fw_hd->img_ver >> 16) & 0xff); + ts->plat_data->img_version_of_bin[1] = ((fw_hd->img_ver >> 8) & 0xff); + ts->plat_data->img_version_of_bin[0] = ((fw_hd->img_ver >> 0) & 0xff); + + ts->plat_data->core_version_of_bin[3] = ((fw_hd->fw_ver >> 24) & 0xff); + ts->plat_data->core_version_of_bin[2] = ((fw_hd->fw_ver >> 16) & 0xff); + ts->plat_data->core_version_of_bin[1] = ((fw_hd->fw_ver >> 8) & 0xff); + ts->plat_data->core_version_of_bin[0] = ((fw_hd->fw_ver >> 0) & 0xff); + + ts->plat_data->config_version_of_bin[3] = ((fw_hd->para_ver >> 24) & 0xff); + ts->plat_data->config_version_of_bin[2] = ((fw_hd->para_ver >> 16) & 0xff); + ts->plat_data->config_version_of_bin[1] = ((fw_hd->para_ver >> 8) & 0xff); + ts->plat_data->config_version_of_bin[0] = ((fw_hd->para_ver >> 0) & 0xff); + + input_info(true, &ts->client->dev, "%s: img_ver of bin = %x.%x.%x.%x\n", __func__, + ts->plat_data->img_version_of_bin[0], + ts->plat_data->img_version_of_bin[1], + ts->plat_data->img_version_of_bin[2], + ts->plat_data->img_version_of_bin[3]); + + input_info(true, &ts->client->dev, "%s: core_ver of bin = %x.%x.%x.%x\n", __func__, + ts->plat_data->core_version_of_bin[0], + ts->plat_data->core_version_of_bin[1], + ts->plat_data->core_version_of_bin[2], + ts->plat_data->core_version_of_bin[3]); + + input_info(true, &ts->client->dev, "%s: config_ver of bin = %x.%x.%x.%x\n", __func__, + ts->plat_data->config_version_of_bin[0], + ts->plat_data->config_version_of_bin[1], + ts->plat_data->config_version_of_bin[2], + ts->plat_data->config_version_of_bin[3]); +} + +static int sec_ts_save_version_of_ic(struct sec_ts_data *ts) +{ + u8 img_ver[4] = {0,}; + u8 core_ver[4] = {0,}; + u8 config_ver[4] = {0,}; + int ret; + + /* Image ver */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_IMG_VERSION, img_ver, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: Image version read error\n", __func__); + return -EIO; + } + input_info(true, &ts->client->dev, "%s: IC Image version info : %x.%x.%x.%x\n", + __func__, img_ver[0], img_ver[1], img_ver[2], img_ver[3]); + + ts->plat_data->img_version_of_ic[0] = img_ver[0]; + ts->plat_data->img_version_of_ic[1] = img_ver[1]; + ts->plat_data->img_version_of_ic[2] = img_ver[2]; + ts->plat_data->img_version_of_ic[3] = img_ver[3]; + + /* Core ver */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_FW_VERSION, core_ver, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: core version read error\n", __func__); + return -EIO; + } + input_info(true, &ts->client->dev, "%s: IC Core version info : %x.%x.%x.%x,\n", + __func__, core_ver[0], core_ver[1], core_ver[2], core_ver[3]); + + ts->plat_data->core_version_of_ic[0] = core_ver[0]; + ts->plat_data->core_version_of_ic[1] = core_ver[1]; + ts->plat_data->core_version_of_ic[2] = core_ver[2]; + ts->plat_data->core_version_of_ic[3] = core_ver[3]; + + /* Config ver */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_PARA_VERSION, config_ver, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: config version read error\n", __func__); + return -EIO; + } + input_info(true, &ts->client->dev, "%s: IC config version info : %x.%x.%x.%x\n", + __func__, config_ver[0], config_ver[1], config_ver[2], config_ver[3]); + + ts->plat_data->config_version_of_ic[0] = config_ver[0]; + ts->plat_data->config_version_of_ic[1] = config_ver[1]; + ts->plat_data->config_version_of_ic[2] = config_ver[2]; + ts->plat_data->config_version_of_ic[3] = config_ver[3]; + + return 1; +} + +int sec_ts_check_firmware_version(struct sec_ts_data *ts, const u8 *fw_info) +{ + fw_header *fw_hd; + u8 buff[1]; + int i; + int ret; + /* + * sec_ts_check_firmware_version + * return value = 1 : firmware download needed, + * return value = 0 : skip firmware download + */ + + fw_hd = (fw_header *)fw_info; + + sec_ts_save_version_of_bin(ts, fw_hd); + + /* firmware download if READ_BOOT_STATUS = 0x10 */ + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_BOOT_STATUS, buff, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: fail to read BootStatus\n", __func__); + return -EIO; + } + + if (buff[0] == SEC_TS_STATUS_BOOT_MODE) { + input_err(true, &ts->client->dev, + "%s: ReadBootStatus = 0x%x, Firmware download Start!\n", + __func__, buff[0]); + return 1; + } + + ret = sec_ts_save_version_of_ic(ts); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail to read ic version\n", __func__); + return -EIO; + } + + /* check f/w version + * ver[0] : IC version + * ver[1] : Project version + */ + for (i = 0; i < 2; i++) { + if (ts->plat_data->img_version_of_ic[i] != ts->plat_data->img_version_of_bin[i]) { + if (ts->plat_data->bringup == 3) { + input_err(true, &ts->client->dev, "%s: bringup. force update\n", __func__); + return 1; + } + input_err(true, &ts->client->dev, "%s: do not matched version info\n", __func__); + return 0; + } + } + + if (ts->plat_data->img_version_of_ic[2] != ts->plat_data->img_version_of_bin[2]) + return 1; + + if (ts->plat_data->img_version_of_ic[3] < ts->plat_data->img_version_of_bin[3]) + return 1; + + return 0; +} + +static u8 sec_ts_checksum(u8 *data, int offset, int size) +{ + int i; + u8 checksum = 0; + + for (i = 0; i < size; i++) + checksum += data[i + offset]; + + return checksum; +} + +static int sec_ts_flashpageerase(struct sec_ts_data *ts, u32 page_idx, u32 page_num) +{ + int ret; + u8 tCmd[6]; + + tCmd[0] = SEC_TS_CMD_FLASH_ERASE; + tCmd[1] = (u8)((page_idx >> 8) & 0xFF); + tCmd[2] = (u8)((page_idx >> 0) & 0xFF); + tCmd[3] = (u8)((page_num >> 8) & 0xFF); + tCmd[4] = (u8)((page_num >> 0) & 0xFF); + tCmd[5] = sec_ts_checksum(tCmd, 1, 4); + + ret = ts->sec_ts_i2c_write_burst(ts, tCmd, 6); + + return ret; +} + +static int sec_ts_flashpagewrite(struct sec_ts_data *ts, u32 page_idx, u8 *page_data) +{ + int ret; + u8 tCmd[1 + 2 + SEC_TS_FW_BLK_SIZE_MAX + 1]; + int flash_page_size = (int)ts->flash_page_size; + + tCmd[0] = 0xD9; + tCmd[1] = (u8)((page_idx >> 8) & 0xFF); + tCmd[2] = (u8)((page_idx >> 0) & 0xFF); + + memcpy(&tCmd[3], page_data, flash_page_size); + tCmd[1 + 2 + flash_page_size] = sec_ts_checksum(tCmd, 1, 2 + flash_page_size); + + ret = ts->sec_ts_i2c_write_burst(ts, tCmd, 1 + 2 + flash_page_size + 1); + return ret; +} + +static int sec_ts_limited_flashpagewrite(struct sec_ts_data *ts, u32 page_idx, u8 *page_data) +{ + int ret = 0; + u8 *tCmd; + u8 copy_data[3 + SEC_TS_FW_BLK_SIZE_MAX]; + int copy_left = (int)ts->flash_page_size + 3; + int copy_size = 0; + int copy_max = ts->i2c_burstmax - 1; + int flash_page_size = (int)ts->flash_page_size; + + copy_data[0] = (u8)((page_idx >> 8) & 0xFF); /* addH */ + copy_data[1] = (u8)((page_idx >> 0) & 0xFF); /* addL */ + + memcpy(©_data[2], page_data, flash_page_size); /* DATA */ + copy_data[2 + flash_page_size] = sec_ts_checksum(copy_data, 0, 2 + flash_page_size); /* CS */ + + while (copy_left > 0) { + int copy_cur = (copy_left > copy_max) ? copy_max : copy_left; + + tCmd = kzalloc(copy_cur + 1, GFP_KERNEL); + if (!tCmd) + goto err_write; + + if (copy_size == 0) + tCmd[0] = SEC_TS_CMD_FLASH_WRITE; + else + tCmd[0] = SEC_TS_CMD_FLASH_PADDING; + + memcpy(&tCmd[1], ©_data[copy_size], copy_cur); + + ret = ts->sec_ts_i2c_write_burst(ts, tCmd, 1 + copy_cur); + if (ret < 0) + input_err(true, &ts->client->dev, + "%s: failed, ret:%d\n", __func__, ret); + + copy_size += copy_cur; + copy_left -= copy_cur; + kfree(tCmd); + } + return ret; + +err_write: + input_err(true, &ts->client->dev, + "%s: failed to alloc.\n", __func__); + return -ENOMEM; + +} + +static int sec_ts_flashwrite(struct sec_ts_data *ts, u32 mem_addr, u8 *mem_data, u32 mem_size, int retry) +{ + int ret; + u32 page_idx; + u32 size_copy; + u32 flash_page_size; + u32 page_idx_start; + u32 page_idx_end; + u32 page_num; + u8 page_buf[SEC_TS_FW_BLK_SIZE_MAX]; + + if (mem_size == 0) + return 0; + + flash_page_size = ts->flash_page_size; + page_idx_start = mem_addr / flash_page_size; + page_idx_end = (mem_addr + mem_size - 1) / flash_page_size; + page_num = page_idx_end - page_idx_start + 1; + + ret = sec_ts_flashpageerase(ts, page_idx_start, page_num); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: fw erase failed, mem_addr= %08X, pagenum = %d\n", + __func__, mem_addr, page_num); + return -EIO; + } + + sec_ts_delay(page_num + 10); + + size_copy = mem_size % flash_page_size; + if (size_copy == 0) + size_copy = flash_page_size; + + memset(page_buf, 0, flash_page_size); + + for (page_idx = page_num - 1;; page_idx--) { + memcpy(page_buf, mem_data + (page_idx * flash_page_size), size_copy); + if (ts->boot_ver[0] == 0xB2) { + ret = sec_ts_flashpagewrite(ts, (page_idx + page_idx_start), page_buf); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fw write failed, page_idx = %u\n", __func__, page_idx); + goto err; + } + + if (retry) { + sec_ts_delay(50); + ret = sec_ts_flashpagewrite(ts, (page_idx + page_idx_start), page_buf); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fw write failed, page_idx = %u\n", __func__, page_idx); + goto err; + } + } + } else { + ret = sec_ts_limited_flashpagewrite(ts, (page_idx + page_idx_start), page_buf); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fw write failed, page_idx = %u\n", __func__, page_idx); + goto err; + } + + if (retry) { + sec_ts_delay(50); + ret = sec_ts_limited_flashpagewrite(ts, (page_idx + page_idx_start), page_buf); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fw write failed, page_idx = %u\n", __func__, page_idx); + goto err; + } + } + + } + + size_copy = flash_page_size; + sec_ts_delay(5); + + if (page_idx == 0) /* end condition (page_idx >= 0) page_idx type unsinged int */ + break; + } + + return mem_size; +err: + return -EIO; +} + +static int sec_ts_memoryblockread(struct sec_ts_data *ts, u32 mem_addr, int mem_size, u8 *buf) +{ + int ret; + u8 cmd[5]; + u8 *data; + + if (mem_size >= 64 * 1024) { + input_err(true, &ts->client->dev, + "%s: mem size over 64K\n", __func__); + return -EIO; + } + + cmd[0] = (u8)SEC_TS_CMD_FLASH_READ_ADDR; + cmd[1] = (u8)((mem_addr >> 24) & 0xff); + cmd[2] = (u8)((mem_addr >> 16) & 0xff); + cmd[3] = (u8)((mem_addr >> 8) & 0xff); + cmd[4] = (u8)((mem_addr >> 0) & 0xff); + + ret = ts->sec_ts_i2c_write_burst(ts, cmd, 5); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: send command failed, %02X\n", __func__, cmd[0]); + return -EIO; + } + + udelay(10); + cmd[0] = (u8)SEC_TS_CMD_FLASH_READ_SIZE; + cmd[1] = (u8)((mem_size >> 8) & 0xff); + cmd[2] = (u8)((mem_size >> 0) & 0xff); + + ret = ts->sec_ts_i2c_write_burst(ts, cmd, 3); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: send command failed, %02X\n", __func__, cmd[0]); + return -EIO; + } + + udelay(10); + cmd[0] = (u8)SEC_TS_CMD_FLASH_READ_DATA; + + data = buf; + + + ret = ts->sec_ts_i2c_read(ts, cmd[0], data, mem_size); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: memory read failed\n", __func__); + return -EIO; + } +#if 0 + ret = ts->sec_ts_i2c_write(ts, cmd[0], NULL, 0); + ret = ts->sec_ts_i2c_read_bulk(ts, data, mem_size); +#endif + return 0; +} + +static int sec_ts_memoryread(struct sec_ts_data *ts, u32 mem_addr, u8 *mem_data, u32 mem_size) +{ + int ret; + int retry = 3; + int read_size = 0; + int unit_size; + int max_size = 1024; + int read_left = (int)mem_size; + u8 *tmp_data; + + tmp_data = kmalloc(max_size, GFP_KERNEL); + if (!tmp_data) { + input_err(true, &ts->client->dev, + "%s: failed to kmalloc\n", __func__); + return -ENOMEM; + } + + while (read_left > 0) { + unit_size = (read_left > max_size) ? max_size : read_left; + retry = 3; + do { + ret = sec_ts_memoryblockread(ts, mem_addr, unit_size, tmp_data); + if (retry-- == 0) { + input_err(true, &ts->client->dev, + "%s: fw read fail mem_addr=%08X,unit_size=%d\n", + __func__, mem_addr, unit_size); + kfree(tmp_data); + return -1; + } + + memcpy(mem_data + read_size, tmp_data, unit_size); + } while (ret < 0); + + mem_addr += unit_size; + read_size += unit_size; + read_left -= unit_size; + } + + kfree(tmp_data); + + return read_size; +} + +static int sec_ts_chunk_update(struct sec_ts_data *ts, u32 addr, u32 size, u8 *data, int retry) +{ + u32 fw_size; + u32 write_size; + u8 *mem_rb; + int ret = 0; + + fw_size = size; + + write_size = sec_ts_flashwrite(ts, addr, data, fw_size, retry); + if (write_size != fw_size) { + input_err(true, &ts->client->dev, "%s: fw write failed\n", __func__); + ret = -1; + goto err_write_fail; + } + + mem_rb = vzalloc(fw_size); + if (!mem_rb) { + input_err(true, &ts->client->dev, "%s: vzalloc failed\n", __func__); + ret = -1; + goto err_write_fail; + } + + if (sec_ts_memoryread(ts, addr, mem_rb, fw_size) >= 0) { + u32 ii; + + for (ii = 0; ii < fw_size; ii++) { + if (data[ii] != mem_rb[ii]) + break; + } + + if (fw_size != ii) { + input_err(true, &ts->client->dev, "%s: fw verify fail\n", __func__); + ret = -1; + goto out; + } + } else { + ret = -1; + goto out; + } + + input_info(true, &ts->client->dev, "%s: verify done(%d)\n", __func__, ret); + +out: + vfree(mem_rb); +err_write_fail: + sec_ts_delay(10); + + return ret; +} + +int sec_ts_firmware_update(struct sec_ts_data *ts, const u8 *data, + size_t size, int bl_update, int restore_cal, int retry) +{ + int i; + int ret; + fw_header *fw_hd; + fw_chunk *fw_ch; + u8 fw_status = 0; + u8 *fd = (u8 *)data; + u8 tBuff[3]; + + /* Check whether CRC is appended or not. + * Enter Firmware Update Mode + */ + if (!sec_ts_enter_fw_mode(ts)) { + input_err(true, &ts->client->dev, "%s: firmware mode failed\n", __func__); + return -1; + } + + if (bl_update && (ts->boot_ver[0] == 0xB4)) { + input_info(true, &ts->client->dev, "%s: bootloader is up to date\n", __func__); + return 0; + } + + input_info(true, &ts->client->dev, "%s: firmware update retry :%d\n", __func__, retry); + + fw_hd = (fw_header *)fd; + fd += sizeof(fw_header); + + if (fw_hd->signature != SEC_TS_FW_HEADER_SIGN) { + input_err(true, &ts->client->dev, "%s: firmware header error = %08X\n", __func__, fw_hd->signature); + return -1; + } + + input_err(true, &ts->client->dev, "%s: num_chunk : %d\n", __func__, fw_hd->num_chunk); + + for (i = 0; i < fw_hd->num_chunk; i++) { + fw_ch = (fw_chunk *)fd; + + input_err(true, &ts->client->dev, "%s: [%d] 0x%08X, 0x%08X, 0x%08X, 0x%08X\n", __func__, i, + fw_ch->signature, fw_ch->addr, fw_ch->size, fw_ch->reserved); + + if (fw_ch->signature != SEC_TS_FW_CHUNK_SIGN) { + input_err(true, &ts->client->dev, "%s: firmware chunk error = %08X\n", __func__, fw_ch->signature); + return -1; + } + fd += sizeof(fw_chunk); + ret = sec_ts_chunk_update(ts, fw_ch->addr, fw_ch->size, fd, retry); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: firmware chunk write failed, addr=%08X, size = %d\n", __func__, fw_ch->addr, fw_ch->size); + return -1; + } + fd += fw_ch->size; + } + + sec_ts_sw_reset(ts); + ts->fw_invalid = true; + + if (!bl_update) { + if (restore_cal) { + input_err(true, &ts->client->dev, "%s: RUN OFFSET CALIBRATION\n", __func__); + + ret = sec_ts_execute_force_calibration(ts, OFFSET_CAL_SET); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to write OFFSET CAL SET!\n", __func__); + } + + /* Sense_on */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: write fail, Sense_on\n", __func__); + return -EIO; + } + + if (ts->sec_ts_i2c_read(ts, SEC_TS_READ_BOOT_STATUS, &fw_status, 1) < 0) { + input_err(true, &ts->client->dev, "%s: read fail, read_boot_status = 0x%x\n", __func__, fw_status); + return -EIO; + } + + if (fw_status != SEC_TS_STATUS_APP_MODE) { + input_err(true, &ts->client->dev, "%s: fw update sequence done, BUT read_boot_status = 0x%x\n", __func__, fw_status); + return -EIO; + } + + ts->fw_invalid = false; + input_info(true, &ts->client->dev, "%s: fw update Success! read_boot_status = 0x%x\n", __func__, fw_status); + + return 1; + } else { + + if (ts->sec_ts_i2c_read(ts, SEC_TS_READ_ID, tBuff, 3) < 0) { + input_err(true, &ts->client->dev, "%s: read device id fail after bl fw download\n", __func__); + return -EIO; + } + + if (tBuff[0] == 0xA0) { + input_info(true, &ts->client->dev, "%s: bl fw download success - device id = %02X\n", __func__, tBuff[0]); + return -EIO; + } else { + input_err(true, &ts->client->dev, "%s: bl fw id does not match - device id = %02X\n", __func__, tBuff[0]); + return -EIO; + } + } + +} + +int sec_ts_firmware_update_bl(struct sec_ts_data *ts) +{ + const struct firmware *fw_entry; + char fw_path[SEC_TS_MAX_FW_PATH]; + int result = -1; + + disable_irq(ts->client->irq); + + snprintf(fw_path, SEC_TS_MAX_FW_PATH, "%s", SEC_TS_DEFAULT_BL_NAME); + + input_info(true, &ts->client->dev, "%s: initial bl update %s\n", __func__, fw_path); + + /* Loading Firmware------------------------------------------ */ + if (request_firmware(&fw_entry, fw_path, &ts->client->dev) != 0) { + input_err(true, &ts->client->dev, "%s: bt is not available\n", __func__); + goto err_request_fw; + } + input_info(true, &ts->client->dev, "%s: request bt done! size = %d\n", __func__, (int)fw_entry->size); + + result = sec_ts_firmware_update(ts, fw_entry->data, fw_entry->size, 1, false, 0); + +err_request_fw: + release_firmware(fw_entry); + enable_irq(ts->client->irq); + + return result; +} + +int sec_ts_bl_update(struct sec_ts_data *ts) +{ + int ret; + u8 tCmd[5] = { 0xDE, 0xAD, 0xBE, 0xEF }; + u8 tBuff[3]; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_READ_BL_UPDATE_STATUS, tCmd, 4); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: bl update command send fail!\n", __func__); + goto err; + } + sec_ts_delay(10); + + do { + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_BL_UPDATE_STATUS, tBuff, 1); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: read bl update status fail!\n", __func__); + goto err; + } + sec_ts_delay(2); + + } while (tBuff[0] == 0x1); + + tCmd[0] = 0x55; + tCmd[1] = 0xAC; + ret = ts->sec_ts_i2c_write(ts, 0x57, tCmd, 2); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: write passwd fail!\n", __func__); + goto err; + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_ID, tBuff, 3); + + if (tBuff[0] == 0xB4) { + input_info(true, &ts->client->dev, "%s: bl update completed!\n", __func__); + ret = 1; + } else { + input_info(true, &ts->client->dev, "%s: bl updated but bl version not matching, ver=%02X\n", __func__, tBuff[0]); + goto err; + } + + return ret; +err: + return -EIO; +} + +int sec_ts_firmware_update_on_probe(struct sec_ts_data *ts, bool force_update) +{ + const struct firmware *fw_entry; + char fw_path[SEC_TS_MAX_FW_PATH]; + int result = -1, restore_cal = 0; + int ii = 0; + int ret = 0; + int skip_firmup = 0; + + if (ts->plat_data->bringup == 1) { + input_err(true, &ts->client->dev, "%s: bringup. do not update\n", __func__); + return 0; + } + + if (ts->plat_data->firmware_name) + snprintf(fw_path, SEC_TS_MAX_FW_PATH, "%s", ts->plat_data->firmware_name); + else + return 0; + + disable_irq(ts->client->irq); + + /* read cal status */ + ts->cal_status = sec_ts_read_calibration_report(ts); + + input_info(true, &ts->client->dev, "%s: initial firmware update %s, cal:%X\n", + __func__, fw_path, ts->cal_status); + + /* Loading Firmware */ + if (request_firmware(&fw_entry, fw_path, &ts->client->dev) != 0) { + input_err(true, &ts->client->dev, "%s: firmware is not available\n", __func__); + goto err_request_fw; + } + input_info(true, &ts->client->dev, "%s: request firmware done! size = %d\n", __func__, (int)fw_entry->size); + + result = sec_ts_check_firmware_version(ts, fw_entry->data); + if (ts->plat_data->bringup == 2) { + input_err(true, &ts->client->dev, "%s: bringup. do not update\n", __func__); + result = 0; + goto err_request_fw; + } + + /* don't firmup case */ + if ((result <= 0) && (!force_update)) { + input_info(true, &ts->client->dev, "%s: skip - fw update\n", __func__); + /* goto err_request_fw; */ + skip_firmup = 1; + } + + if ((skip_firmup == 1) && (restore_cal == 0)) + goto err_request_fw; + + for (ii = 0; ii < 3; ii++) { + ret = sec_ts_firmware_update(ts, fw_entry->data, fw_entry->size, 0, restore_cal, ii); + if (ret >= 0) + break; + } + + if (ret < 0) + result = -1; + else + result = 0; + + sec_ts_save_version_of_ic(ts); + +err_request_fw: + release_firmware(fw_entry); + enable_irq(ts->client->irq); + return result; +} + +static int sec_ts_load_fw_from_bin(struct sec_ts_data *ts) +{ + const struct firmware *fw_entry; + char fw_path[SEC_TS_MAX_FW_PATH]; + int error = 0; + int restore_cal = 0; + + if (ts->client->irq) + disable_irq(ts->client->irq); + + if (!ts->plat_data->firmware_name) + snprintf(fw_path, SEC_TS_MAX_FW_PATH, "%s", SEC_TS_DEFAULT_FW_NAME); + else + snprintf(fw_path, SEC_TS_MAX_FW_PATH, "%s", ts->plat_data->firmware_name); + + input_info(true, &ts->client->dev, "%s: initial firmware update %s\n", __func__, fw_path); + + /* Loading Firmware */ + if (request_firmware(&fw_entry, fw_path, &ts->client->dev) != 0) { + input_err(true, &ts->client->dev, "%s: firmware is not available\n", __func__); + error = -1; + goto err_request_fw; + } + input_info(true, &ts->client->dev, "%s: request firmware done! size = %d\n", __func__, (int)fw_entry->size); + + /* use virtual pat_control - magic cal 1 */ + if (sec_ts_firmware_update(ts, fw_entry->data, fw_entry->size, 0, restore_cal, 0) < 0) + error = -1; + else + error = 0; + + sec_ts_save_version_of_ic(ts); + +err_request_fw: + release_firmware(fw_entry); + if (ts->client->irq) + enable_irq(ts->client->irq); + + return error; +} + +static int sec_ts_load_fw_from_ums(struct sec_ts_data *ts) +{ + fw_header *fw_hd; + struct file *fp; + mm_segment_t old_fs; + long fw_size, nread; + int error = 0; + int restore_cal = 0; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + + fp = filp_open(SEC_TS_DEFAULT_UMS_FW, O_RDONLY, S_IRUSR); + if (IS_ERR(fp)) { + input_err(true, ts->dev, "%s: failed to open %s.\n", __func__, + SEC_TS_DEFAULT_UMS_FW); + error = -ENOENT; + goto open_err; + } + + fw_size = fp->f_path.dentry->d_inode->i_size; + + if (fw_size > 0) { + unsigned char *fw_data; + + fw_data = vzalloc(fw_size); + if (!fw_data) { + input_err(true, ts->dev, "%s: failed to alloc mem\n", __func__); + error = -ENOMEM; + filp_close(fp, NULL); + goto open_err; + } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + nread = kernel_read(fp, (char __user *)fw_data, + fw_size, &fp->f_pos); +#else + nread = vfs_read(fp, (char __user *)fw_data, + fw_size, &fp->f_pos); +#endif + input_info(true, ts->dev, + "%s: start, file path %s, size %ld Bytes\n", + __func__, SEC_TS_DEFAULT_UMS_FW, fw_size); + + if (nread != fw_size) { + input_err(true, ts->dev, + "%s: failed to read firmware file, nread %ld Bytes\n", + __func__, nread); + error = -EIO; + } else { + fw_hd = (fw_header *)fw_data; +#if 0 + sec_ts_check_firmware_version(ts, fw_data); +#endif + input_info(true, &ts->client->dev, "%s: firmware version %08X\n", __func__, fw_hd->fw_ver); + input_info(true, &ts->client->dev, "%s: parameter version %08X\n", __func__, fw_hd->para_ver); + + if (ts->client->irq) + disable_irq(ts->client->irq); + /* use virtual pat_control - magic cal 1 */ + if (sec_ts_firmware_update(ts, fw_data, fw_size, 0, restore_cal, 0) < 0) { + error = -1; /* firmware failed */ + goto done; + } + + sec_ts_save_version_of_ic(ts); + } + + if (error < 0) + input_err(true, ts->dev, "%s: failed update firmware\n", + __func__); + +done: + if (ts->client->irq) + enable_irq(ts->client->irq); + vfree(fw_data); + } + + filp_close(fp, NULL); + +open_err: + set_fs(old_fs); + return error; +} + +static int sec_ts_load_fw_from_ffu(struct sec_ts_data *ts) +{ + const struct firmware *fw_entry; + const char *fw_path = SEC_TS_DEFAULT_FFU_FW; + int result = -1; + + disable_irq(ts->client->irq); + + input_info(true, ts->dev, "%s: Load firmware : %s\n", __func__, fw_path); + + /* Loading Firmware */ + if (request_firmware(&fw_entry, fw_path, &ts->client->dev) != 0) { + input_err(true, &ts->client->dev, "%s: firmware is not available\n", __func__); + goto err_request_fw; + } + input_info(true, &ts->client->dev, "%s: request firmware done! size = %d\n", __func__, (int)fw_entry->size); + + sec_ts_check_firmware_version(ts, fw_entry->data); + + if (sec_ts_firmware_update(ts, fw_entry->data, fw_entry->size, 0, false, 0) < 0) + result = -1; + else + result = 0; + + sec_ts_save_version_of_ic(ts); + +err_request_fw: + release_firmware(fw_entry); + enable_irq(ts->client->irq); + return result; +} + +int sec_ts_firmware_update_on_hidden_menu(struct sec_ts_data *ts, int update_type) +{ + int ret = 0; + + /* Factory cmd for firmware update + * argument represent what is source of firmware like below. + * + * 0 : [BUILT_IN] Getting firmware which is for user. + * 1 : [UMS] Getting firmware from sd card. + * 2 : none + * 3 : [FFU] Getting firmware from air. + */ + + switch (update_type) { + case BUILT_IN: + ret = sec_ts_load_fw_from_bin(ts); + break; + case UMS: + ret = sec_ts_load_fw_from_ums(ts); + break; + case FFU: + ret = sec_ts_load_fw_from_ffu(ts); + break; + case BL: + ret = sec_ts_firmware_update_bl(ts); + if (ret < 0) { + break; + } else if (!ret) { + ret = sec_ts_firmware_update_on_probe(ts, false); + break; + } else { + ret = sec_ts_bl_update(ts); + if (ret < 0) + break; + ret = sec_ts_firmware_update_on_probe(ts, false); + if (ret < 0) + break; + } + break; + default: + input_err(true, ts->dev, "%s: Not support command[%d]\n", + __func__, update_type); + break; + } + +#ifdef SEC_TS_SUPPORT_CUSTOMLIB + sec_ts_check_custom_library(ts); + if (ts->use_customlib) + sec_ts_set_custom_library(ts); +#endif + + return ret; +} +EXPORT_SYMBOL(sec_ts_firmware_update_on_hidden_menu); diff --git a/drivers/input/touchscreen/sec_mmi/sec_ts_mmi.c b/drivers/input/touchscreen/sec_mmi/sec_ts_mmi.c new file mode 100644 index 000000000000..ffc8fd3de245 --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/sec_ts_mmi.c @@ -0,0 +1,1336 @@ +/* + * Copyright (C) 2018 Motorola Mobility LLC + * + * 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sec_ts.h" +#include "sec_mmi.h" + +#ifdef CONFIG_DRM_DYNAMIC_REFRESH_RATE +extern struct blocking_notifier_head dsi_freq_head; +#define register_dynamic_refresh_rate_notifier(...) \ + blocking_notifier_chain_register(&dsi_freq_head, &data->freq_nb) +#define unregister_dynamic_refresh_rate_notifier(...) \ + blocking_notifier_chain_unregister(&dsi_freq_head, &data->freq_nb) +#else +#define register_dynamic_refresh_rate_notifier(...) rc +#define unregister_dynamic_refresh_rate_notifier(...) rc +#endif + +#if defined(CONFIG_PANEL_NOTIFICATIONS) +#define register_panel_notifier panel_register_notifier +#define unregister_panel_notifier panel_unregister_notifier +#elif defined(CONFIG_DRM) +#define register_panel_notifier msm_drm_register_client +#define unregister_panel_notifier msm_drm_unregister_client +#else +#define register_panel_notifier(...) rc +#define unregister_panel_notifier(...) rc +#endif + +#if defined(USE_STUBS) +static struct class *local_touchscreen_class; +struct class *get_touchscreen_class_ptr(void) +{ + pr_info("sec_mmi: stub %s\n", __func__); + return local_touchscreen_class; +} +void set_touchscreen_class_ptr(struct class *ptr) +{ + pr_info("sec_mmi: stub %s\n", __func__); + local_touchscreen_class = ptr; +} +#endif + +/* MMI specific sysfs entries and API */ +static ssize_t sec_mmi_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_forcereflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_doreflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_drv_irq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_erase_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_drv_irq_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t sec_mmi_hw_irqstat_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t sec_mmi_ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t sec_mmi_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t sec_mmi_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t sec_mmi_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t sec_mmi_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t sec_mmi_panel_supplier_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t sec_mmi_address_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_write_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_data_show(struct device *dev, + struct device_attribute *attr, char *buf); + + +static DEVICE_ATTR(forcereflash, (S_IWUSR | S_IWGRP), NULL, sec_mmi_forcereflash_store); +static DEVICE_ATTR(doreflash, (S_IWUSR | S_IWGRP), NULL, sec_mmi_doreflash_store); +static DEVICE_ATTR(drv_irq, (S_IWUSR | S_IWGRP | S_IRUGO), sec_mmi_drv_irq_show, sec_mmi_drv_irq_store); +static DEVICE_ATTR(hw_irqstat, S_IRUGO, sec_mmi_hw_irqstat_show, NULL); +static DEVICE_ATTR(ic_ver, S_IRUGO, sec_mmi_ic_ver_show, NULL); +static DEVICE_ATTR(productinfo, S_IRUGO, sec_mmi_productinfo_show, NULL); +static DEVICE_ATTR(buildid, S_IRUGO, sec_mmi_buildid_show, NULL); +static DEVICE_ATTR(flashprog, S_IRUGO, sec_mmi_flashprog_show, NULL); +static DEVICE_ATTR(poweron, S_IRUGO, sec_mmi_poweron_show, NULL); +static DEVICE_ATTR(reset, (S_IWUSR | S_IWGRP), NULL, sec_mmi_reset_store); +static DEVICE_ATTR(panel_supplier, S_IRUGO, sec_mmi_panel_supplier_show, NULL); +static DEVICE_ATTR(address, (S_IWUSR | S_IWGRP), NULL, sec_mmi_address_store); +static DEVICE_ATTR(size, (S_IWUSR | S_IWGRP), NULL, sec_mmi_size_store); +static DEVICE_ATTR(data, S_IRUGO, sec_mmi_data_show, NULL); + +static struct attribute *mmi_attributes[] = { + &dev_attr_forcereflash.attr, + &dev_attr_doreflash.attr, + &dev_attr_drv_irq.attr, + &dev_attr_hw_irqstat.attr, + &dev_attr_ic_ver.attr, + &dev_attr_productinfo.attr, + &dev_attr_buildid.attr, + &dev_attr_flashprog.attr, + &dev_attr_poweron.attr, + &dev_attr_reset.attr, + &dev_attr_panel_supplier.attr, + &dev_attr_address.attr, + &dev_attr_size.attr, + &dev_attr_data.attr, + NULL, +}; + +static struct attribute_group mmi_attr_group = { + .attrs = mmi_attributes, +}; + +static DEVICE_ATTR(write, (S_IWUSR | S_IWGRP), NULL, sec_mmi_write_store); +static DEVICE_ATTR(erase_all, (S_IWUSR | S_IWGRP), NULL, sec_mmi_erase_store); + +static struct attribute *factory_attributes[] = { + &dev_attr_write.attr, + &dev_attr_erase_all.attr, + NULL, +}; + +static struct attribute_group factory_attr_group = { + .attrs = factory_attributes, +}; + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_mmi_data *data = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!data) { + dev_err(DEV_MMI, "data pointer is NULL\n"); + return (ssize_t)0; + } + path = kobject_get_path(&data->i2c_client->dev.kobj, GFP_KERNEL); + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "samsung"); +} + +static struct device_attribute class_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_NULL +}; + +static int sec_mmi_touchscreen_class( + struct sec_mmi_data *data, bool create) +{ + struct device_attribute *attrs = class_attributes; + bool unreg_class = false; + int i, error = 0; + char buffer[128]; + struct sec_ts_data *ts = data->ts_ptr; + ssize_t namelen; + const char *class_fname = "primary"; + static char *input_dev_name; + static struct class *touchscreen_class; + static int minor; + + if (create) { + minor = input_get_new_minor(data->i2c_client->addr, + 1, false); + if (minor < 0) + minor = input_get_new_minor(TSDEV_MINOR_BASE, + TSDEV_MINOR_MAX, true); + dev_dbg(DEV_MMI, "assigned minor %d\n", minor); + + touchscreen_class = get_touchscreen_class_ptr(); + dev_dbg(DEV_MMI, "class ptr %p\n", touchscreen_class); + if (!touchscreen_class) { + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + set_touchscreen_class_ptr(touchscreen_class); + unreg_class = true; + } + + if (data->class_entry_name) + class_fname = data->class_entry_name; + + dev_dbg(DEV_MMI, "class entry name %s\n", class_fname); + data->ts_class_dev = device_create(touchscreen_class, + NULL, MKDEV(INPUT_MAJOR, minor), data, class_fname); + if (IS_ERR(data->ts_class_dev)) { + error = PTR_ERR(data->ts_class_dev); + data->ts_class_dev = NULL; + return error; + } + + dev_set_drvdata(data->ts_class_dev, data); + + /* construct input device name */ + namelen = scnprintf(buffer, sizeof(buffer), "samsung_mmi.%s", class_fname); + if (namelen) { + input_dev_name = kstrdup(buffer, GFP_KERNEL); + ts->input_dev->name = input_dev_name; + } + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + error = device_create_file(data->ts_class_dev, &attrs[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + } else { + if (!touchscreen_class || !data->ts_class_dev) + return -ENODEV; + + dev_set_drvdata(data->ts_class_dev, NULL); + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(data->ts_class_dev, &attrs[i]); + + device_unregister(data->ts_class_dev); + data->ts_class_dev = NULL; + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(data->ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor)); + data->ts_class_dev = NULL; + if (unreg_class) + class_unregister(touchscreen_class); + touchscreen_class = NULL; + dev_err(DEV_MMI, "%s: error creating touchscreen class attrs\n", __func__); + + return -ENODEV; +} + +static int sec_mmi_ps_get_state(struct power_supply *psy, bool *present) +{ + union power_supply_propval pval = {0}; + int ret; + + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &pval); + if (ret) + return ret; + *present = !pval.intval ? false : true; + return 0; +} + +static void sec_mmi_ps_work(struct work_struct *work) +{ + struct sec_mmi_data *data = container_of(work, + struct sec_mmi_data, ps_notify_work); + struct sec_ts_data *ts = data->ts_ptr; + u8 cval = 0; + u8 mode = 1; + int ret; + + /* plugged in USB should change value to 2 */ + if (data->ps_is_present) + mode++; + ret = ts->sec_ts_i2c_read(ts, SET_TS_CMD_SET_CHARGER_MODE, + &cval, sizeof(cval)); + if (ret < 0) { + dev_err(DEV_TS, "%s: failed to read charger mode\n", __func__); + } + + if (cval != mode) + sec_ts_set_charger(ts, data->ps_is_present); + else + dev_dbg(DEV_TS, "%s: charger mode already %d\n", __func__, cval); +} + +static int sec_mmi_ps_notify_callback(struct notifier_block *self, + unsigned long event, void *ptr) +{ + struct sec_mmi_data *data = container_of( + self, struct sec_mmi_data, ps_notif); + struct power_supply *psy = ptr; + int ret; + bool present; + + if (!((event == PSY_EVENT_PROP_CHANGED) && psy && + psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, "usb", sizeof("usb")))) + return 0; + + ret = sec_mmi_ps_get_state(psy, &present); + if (ret) { + dev_err(DEV_MMI, "%s: failed to get usb status: %d\n", + __func__, ret); + return ret; + } + + dev_dbg(DEV_MMI, "%s: event=%lu, usb status: cur=%d, prev=%d\n", + __func__, event, present, data->ps_is_present); + + if (data->ps_is_present != present) { + data->ps_is_present = present; + schedule_work(&data->ps_notify_work); + } + + return 0; +} + +static int sec_mmi_dt(struct sec_mmi_data *data) +{ + struct device *dev = &data->i2c_client->dev; + struct device_node *chosen, *np = dev->of_node; + + if (!of_property_read_string(np, "sec,class-entry-name", &data->class_entry_name)) + dev_info(DEV_MMI, "%s: class-entry-name property %s\n", + __func__, data->class_entry_name); + + if (!of_property_read_string(np, "sec,bound-display", &data->bound_display)) + dev_info(DEV_MMI, "%s: bound-display property %s\n", + __func__, data->bound_display); + + if (!of_property_read_u32(np, "sec,control-dsi", &data->ctrl_dsi)) + dev_info(DEV_MMI, "%s: ctrl-dsi property %d\n", + __func__, data->ctrl_dsi); + + if (of_property_read_bool(np, "sec,usb-charger-detection")) { + dev_info(DEV_MMI, "%s: using usb detection\n", __func__); + data->usb_detection = true; + } + + if (of_property_read_bool(np, "sec,force-calibration")) { + struct sec_ts_data *ts = data->ts_ptr; + dev_info(DEV_MMI, "%s: using force calibration\n", __func__); + ts->force_calibration = true; + } + + if (of_property_read_bool(np, "sec,enable-gestures")) { + dev_info(DEV_MMI, "%s: using enable gestures\n", __func__); + data->gestures_enabled = true; + } + + if (of_property_read_bool(np, "sec,gpio-config")) { + dev_info(DEV_MMI, "%s: using gpio config\n", __func__); + data->gpio_config = true; + } + + if (!of_property_read_u32(np, "sec,reset-on-resume", &data->reset)) + dev_info(DEV_MMI, "%s: using %u reset on resume\n", + __func__, data->reset); + + if (of_property_read_bool(np, "sec,power-off-suspend")) { + dev_info(DEV_MMI, "%s: using power off in suspend\n", __func__); + data->power_off_suspend = true; + if (data->reset) { + data->reset = 0; + dev_info(DEV_MMI, "%s: unset reset on resume!!!\n", __func__); + } + } + + if (of_property_read_bool(np, "sec,refresh-rate-update")) { + dev_info(DEV_MMI, "%s: using refresh rate update\n", __func__); + data->update_refresh_rate = true; + } + + chosen = of_find_node_by_name(NULL, "chosen"); + if (chosen) { + struct device_node *child; + const char *supplier; + char *s, *d; + int rc; + u64 panel_ver, panel_id; + + rc = of_property_read_string(chosen, "mmi,panel_name", + (const char **)&supplier); + if (rc) { + dev_err(DEV_MMI, "%s: cannot read mmi,panel_name %d\n", + __func__, rc); + goto done; + } + dev_dbg(DEV_MMI, "%s: mmi,panel_name %s\n", __func__, supplier); + + /* skip dsi_ part */ + s = (char *)supplier + 4; + d = data->panel_supplier; + while (*s != '_') *d++ = *s++; + + rc = of_property_read_u64(chosen, "mmi,panel_id", &panel_id); + if (rc) { + dev_err(DEV_MMI, "%s: cannot read mmi,panel_id %d\n", __func__, rc); + goto done; + } + rc = of_property_read_u64(chosen, "mmi,panel_ver", &panel_ver); + if (rc) { + dev_err(DEV_MMI, "%s: cannot read mmi,panel_ver %d\n", __func__, rc); + goto done; + } + of_node_put(chosen); + dev_dbg(DEV_MMI, "%s: [%s] id=%llx ver=%llx\n", + __func__, chosen->name, panel_id, panel_ver); + + np = of_find_node_by_name(dev->of_node, "mmi,panel-mappings"); + if (!np) { + dev_err(DEV_MMI, "%s: cannot read mmi,panel-mappings\n", __func__); + goto done; + } + + for_each_child_of_node(np, child) { + u64 id, ver; + + rc = of_property_read_u64(child, "panel-id", &id); + if (rc) { + dev_err(DEV_MMI, "%s: cannot read panel-id %d\n", __func__, rc); + goto done; + } + rc = of_property_read_u64(child, "panel-ver", &ver); + if (rc) { + dev_err(DEV_MMI, "%s: cannot read panel-ver %d\n", __func__, rc); + goto done; + } + + dev_dbg(DEV_MMI, "%s: [%s] id=%llx ver=%llx\n", + __func__, child->name, id, ver); + of_node_put(child); + + if (id == panel_id && ver == panel_ver) { + strlcpy(data->panel_supplier, child->name, + sizeof(data->panel_supplier)); + break; + } + } + + of_node_put(np); + dev_info(DEV_MMI, "%s: panel-supplier %s\n", + __func__, data->panel_supplier); + } + +done: + return 0; +} + +static inline void bdc2ui(unsigned int *dest, unsigned char *src, size_t size) +{ + int i, shift = 0; + *dest = 0; + for (i = 0; i < size; i++, shift += 8) + *dest += src[i] << (24 - shift); +} + +static void sec_mmi_fw_read_id(struct sec_mmi_data *data) +{ + struct sec_ts_data *ts = data->ts_ptr; + unsigned char buffer[8]; + int ret; + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_FW_VERSION, buffer, sizeof(buffer)); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read fw version (%d)\n", + __func__, ret); + } else { + dev_dbg(DEV_MMI, "%s: FW VERSION: " \ + "%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", + __func__, buffer[0], buffer[1], buffer[2], buffer[3], + buffer[4], buffer[5], buffer[6], buffer[7]); + } + + memset(buffer, 0, sizeof(buffer)); + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_PARA_VERSION, buffer, sizeof(buffer)); + if (ret < 0) { + dev_err(DEV_MMI, "%s: failed to read fw version (%d)\n", + __func__, ret); + } else { + bdc2ui(&data->config_id, buffer+4, sizeof(data->config_id)); + dev_dbg(DEV_MMI, "%s: CONFIG VER: " \ + "%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", + __func__, buffer[0], buffer[1], buffer[2], buffer[3], + buffer[4], buffer[5], buffer[6], buffer[7]); + } + + memset(buffer, 0, sizeof(buffer)); + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_IMG_VERSION, buffer, sizeof(buffer)); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read fw version (%d)\n", + __func__, ret); + } else { + if (buffer[0] == 0x17) + ts->device_id[3] = 0x7C; + bdc2ui(&data->build_id, buffer, sizeof(data->build_id)); + dev_dbg(DEV_MMI, "%s: IMAGE VER: " \ + "%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", + __func__, buffer[0], buffer[1], buffer[2], buffer[3], + buffer[4], buffer[5], buffer[6], buffer[7]); + } +} + +static void sec_mmi_ic_reset(struct sec_mmi_data *data, int mode) +{ + struct sec_ts_data *ts = data->ts_ptr; + + if (!gpio_is_valid(ts->plat_data->rst_gpio) || + !gpio_get_value(ts->plat_data->rst_gpio)) + return; + + PM_STAY_AWAKE(ts->wakelock); + mutex_lock(&ts->modechange); + /* disable irq to ensure getting boot complete */ + sec_ts_irq_enable(ts, false); + + if (mode == 1) { + gpio_set_value(ts->plat_data->rst_gpio, 0); + usleep_range(10, 10); + gpio_set_value(ts->plat_data->rst_gpio, 1); + dev_dbg(DEV_MMI, "%s: reset line toggled\n", __func__); + } else { + int ret; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SW_RESET, NULL, 0); + if (ret < 0) + dev_err(DEV_MMI, "%s: error sending sw reset cmd\n", __func__); + } + + sec_ts_wait_for_ready(ts, SEC_TS_ACK_BOOT_COMPLETE); + sec_ts_irq_enable(ts, true); + + mutex_unlock(&ts->modechange); + PM_RELAX(ts->wakelock); + + dev_dbg(DEV_MMI, "%s: hw reset done\n", __func__); +} + +static void sec_mmi_wait4idle(struct sec_mmi_data *data) +{ + struct sec_ts_data *ts = data->ts_ptr; + unsigned long start_wait_jiffies = jiffies; + + do { + if (!ts->wakelock->active) + break; + usleep_range(1000, 1000); + } while (1); + + if ((jiffies - start_wait_jiffies)) + dev_info(DEV_MMI, "%s: entering suspend delayed for %ums\n", + __func__, jiffies_to_msecs(jiffies - start_wait_jiffies)); +} + +static void sec_mmi_enable_touch(struct sec_mmi_data *data) +{ + struct sec_ts_data *ts = data->ts_ptr; + + sec_mmi_fw_read_id(data); + if (data->usb_detection) { + struct power_supply *psy; + + psy = power_supply_get_by_name("usb"); + if (psy) { + int rc; + rc = sec_mmi_ps_get_state(psy, &data->ps_is_present); + if (rc) { + dev_err(DEV_MMI, + "%s: failed to get usb status\n", __func__); + } + if (data->ps_is_present) + sec_ts_set_charger(ts, data->ps_is_present); + } + } + + sec_ts_integrity_check(ts); + sec_ts_sense_on(ts); + dev_dbg(DEV_MMI, "%s: touch sensing ready\n", __func__); +} + +static void sec_mmi_work(struct work_struct *w) +{ + struct delayed_work *dw = + container_of(w, struct delayed_work, work); + struct sec_mmi_data *data = + container_of(dw, struct sec_mmi_data, work); + struct sec_ts_data *ts = data->ts_ptr; + int rc, probe_status; + bool panel_ready = true; + char *pname = NULL; + + switch (data->task) { + case MMI_TASK_INIT: + /* DRM panel status */ + panel_ready = dsi_display_is_panel_enable( + data->ctrl_dsi, &probe_status, &pname); + dev_dbg(DEV_MMI, "%s: drm: probe=%d, enable=%d, panel'%s'\n", + __func__, probe_status, panel_ready, pname); + /* check if bound panel is not present */ + if (pname && strstr(pname, "dummy")) { + dev_info(DEV_MMI, "%s: dummy panel detected\n", __func__); + //rmi4_data->drm_state = DRM_ST_TERM; + } else if (panel_ready) { + dev_dbg(DEV_MMI, "%s: panel ready\n", __func__); + //rmi4_data->drm_state = DRM_ST_READY; + } + + if (ts->fw_invalid == false) { + sec_mmi_enable_touch(data); + } else /* stuck in BL mode, update productinfo to report 'se77c' */ + ts->device_id[3] = 0x7C; + + snprintf(data->product_id, sizeof(data->product_id), "%c%c%c%02x", + tolower(ts->device_id[0]), tolower(ts->device_id[1]), + ts->device_id[2], ts->device_id[3]); + break; + + case MMI_TASK_SET_RATE: + /* Switch refresh rate */ + dev_info(DEV_MMI, "%s: writing refresh rate %dhz\n", + __func__, data->refresh_rate); + rc = ts->sec_ts_i2c_write(ts, 0x4C, &data->refresh_rate, 1); + if (rc < 0) + dev_err(DEV_MMI, "%s: failed refresh_rate (%d)\n", __func__, rc); + break; + } +} + +static void sec_mmi_queued_resume(struct work_struct *w) +{ + struct delayed_work *dw = + container_of(w, struct delayed_work, work); + struct sec_mmi_data *data = + container_of(dw, struct sec_mmi_data, resume_work); + struct sec_ts_data *ts = data->ts_ptr; + unsigned char buffer = 0; + bool wait4_boot_complete = true; + bool update_charger = true; + int ret; + + if (atomic_cmpxchg(&data->touch_stopped, 1, 0) == 0) + return; + + if (data->power_off_suspend) { + sec_ts_wait_for_ready(ts, SEC_TS_ACK_BOOT_COMPLETE); + } else if (!data->reset) { + sec_ts_set_lowpowermode(ts, TO_TOUCH_MODE); + wait4_boot_complete = false; + update_charger = false; + } + + if (wait4_boot_complete) { + if (data->power_off_suspend) + sec_ts_irq_enable(ts, true); + + /* Sense_on */ + dev_dbg(DEV_MMI, "%s: sending sense_on...\n", __func__); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) + dev_err(DEV_MMI, + "%s: failed sense_on (%d)\n", __func__, ret); + } + + /* make sure charger mode is properly set after reset */ + if (data->usb_detection && update_charger) + schedule_work(&data->ps_notify_work); + + if (ts->lowpower_mode) + complete_all(&ts->resume_done); + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_BOOT_STATUS, + &buffer, sizeof(buffer)); + if (ret < 0) + dev_err(DEV_MMI, + "%s: failed to read boot status (%d)\n", + __func__, ret); + + ts->fw_invalid = buffer == SEC_TS_STATUS_BOOT_MODE; + + dev_info(DEV_MMI, "%s: done\n", __func__); +} + +static int inline sec_mmi_display_on(struct sec_mmi_data *data) +{ + /* schedule_delayed_work returns true if work has been scheduled */ + /* and false otherwise, thus return 0 on success to comply POSIX */ + return schedule_delayed_work(&data->resume_work, 0) == false; +} + +static int inline sec_mmi_display_off(struct sec_mmi_data *data) +{ + struct sec_ts_data *ts = data->ts_ptr; + + if (atomic_cmpxchg(&data->touch_stopped, 0, 1) == 1) + return 0; + /* complete critical work */ + sec_mmi_wait4idle(data); + + if (data->power_off_suspend) + sec_ts_irq_enable(ts, false); + else + sec_ts_set_lowpowermode(ts, + data->gestures_enabled ? + TO_LOWPOWER_MODE : TO_SLEEP_MODE); + cancel_delayed_work_sync(&data->resume_work); + + if (ts->lowpower_mode) + reinit_completion(&ts->resume_done); + + dev_info(DEV_MMI, "%s: done\n", __func__); + + return 0; +} + +static int sec_mmi_panel_cb(struct notifier_block *nb, + unsigned long event, void *evd) +{ + struct sec_mmi_data *data = + container_of(nb, struct sec_mmi_data, panel_nb); + struct sec_ts_data *ts = data->ts_ptr; + int idx = -1; +#if defined(CONFIG_PANEL_NOTIFICATIONS) + if (evd) + idx = *(int *)evd; +#elif defined (CONFIG_DRM) + struct msm_drm_notifier *evdata = evd; + int *blank; + + if (!(evdata && evdata->data && data)) { + dev_dbg(DEV_MMI, "%s: Invalid evdata \n", + __func__); + return 0; + } + + idx = evdata->id; + blank = (int *)evdata->data; + dev_dbg(DEV_MMI, "%s: drm notification: event = %lu blank = %d\n", + __func__, event, *blank); +#endif + if ((idx != data->ctrl_dsi)) { + dev_dbg(DEV_MMI, "%s: ctrl_dsi notification: id(%d) != ctrl_dsi(%d)\n", + __func__, idx, data->ctrl_dsi); + return 0; + } + + /* entering suspend upon early blank event */ + /* to ensure shared power supply is still on */ + /* for in-cell design touch solutions */ + switch (event) { +#if defined(CONFIG_PANEL_NOTIFICATIONS) + case PANEL_EVENT_PRE_DISPLAY_OFF: + /* put in reset first */ + if (data->power_off_suspend) + sec_ts_pinctrl_configure(ts, false); + sec_mmi_display_off(data); + break; + + case PANEL_EVENT_DISPLAY_OFF: + if (data->power_off_suspend) { + /* then proceed with de-powering */ + sec_ts_power((void *)ts, false); + dev_dbg(DEV_MMI, "%s: touch powered off\n", __func__); + } + break; + + case PANEL_EVENT_PRE_DISPLAY_ON: + if (data->power_off_suspend) { + /* powering on early */ + sec_ts_power((void *)ts, true); + dev_dbg(DEV_MMI, "%s: touch powered on\n", __func__); + } else if (data->reset) { + dev_dbg(DEV_MMI, "%s: resetting...\n", __func__); + data->reset_func(ts->mmi_ptr, data->reset); + } + break; + + case PANEL_EVENT_DISPLAY_ON: + /* out of reset to allow wait for boot complete */ + if (data->power_off_suspend) + sec_ts_pinctrl_configure(ts, true); + sec_mmi_display_on(data); + break; +#elif defined(CONFIG_DRM) + case MSM_DRM_EARLY_EVENT_BLANK: + if (*blank == MSM_DRM_BLANK_POWERDOWN) { + /* put in reset first */ + if (data->power_off_suspend) + sec_ts_pinctrl_configure(ts, false); + + sec_mmi_display_off(data); + + } else if (data->power_off_suspend) { + /* powering on early */ + sec_ts_power((void *)ts, true); + dev_dbg(DEV_MMI, "%s: touch powered on\n", __func__); + } else if (data->reset) { + dev_dbg(DEV_MMI, "%s: resetting...\n", __func__); + data->reset_func(ts->mmi_ptr, data->reset); + } + break; + + case MSM_DRM_EVENT_BLANK: + if (*blank == MSM_DRM_BLANK_UNBLANK) { + /* out of reset to allow wait for boot complete */ + if (data->power_off_suspend) + sec_ts_pinctrl_configure(ts, true); + + sec_mmi_display_on(data); + + } else if (data->power_off_suspend) { + /* then proceed with de-powering */ + sec_ts_power((void *)ts, false); + dev_dbg(DEV_MMI, "%s: touch powered off\n", __func__); + } + break; +#endif + default: /* use DEV_TS here to avoid unused variable */ + dev_dbg(DEV_TS, "%s: function not implemented\n", __func__); + break; + } + + return 0; +} + +static int sec_mmi_refresh_rate(struct notifier_block *nb, + unsigned long refresh_rate, void *ptr) +{ + struct sec_mmi_data *data = + container_of(nb, struct sec_mmi_data, freq_nb); + bool do_calibration = false; + + if (!data) + return 0; + + if (!data->refresh_rate || + (data->refresh_rate != (refresh_rate & 0xFF))) { + data->refresh_rate = refresh_rate & 0xFF; + do_calibration = true; + } + + if (do_calibration) { + data->task = MMI_TASK_SET_RATE; + schedule_delayed_work(&data->work, 0); + } + + return 0; +} + +static int sec_mmi_register_notifiers( + struct sec_mmi_data *data, bool enable) +{ + int rc = 0; + + if (enable) { + data->panel_nb.notifier_call = sec_mmi_panel_cb; + rc = register_panel_notifier(&data->panel_nb); + + if (data->usb_detection) { + data->ps_notif.notifier_call = sec_mmi_ps_notify_callback; + rc = power_supply_reg_notifier(&data->ps_notif); + } + + if (data->update_refresh_rate) { + data->freq_nb.notifier_call = sec_mmi_refresh_rate; + rc = register_dynamic_refresh_rate_notifier( + &dsi_freq_head, &data->freq_nb); + } + } else { + rc = unregister_panel_notifier(&data->panel_nb); + + if (data->usb_detection) + power_supply_unreg_notifier(&data->ps_notif); + + if (data->update_refresh_rate) { + rc = unregister_dynamic_refresh_rate_notifier( + &dsi_freq_head, &data->freq_nb); + } + } + + if (!rc) + dev_dbg(DEV_MMI, "%s: %sregistered notifier\n", + __func__, enable ? "" : "un"); + else + dev_err(DEV_MMI, "%s: failed to %sregister notifiers\n", + __func__, enable ? "" : "un"); + + return rc; +} + +static ssize_t sec_mmi_panel_supplier_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + struct sec_mmi_data *data = ts->mmi_ptr; + return scnprintf(buf, PAGE_SIZE, "%s\n", data->panel_supplier); +} + +static ssize_t sec_mmi_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%d\n", + ts->power_status != SEC_TS_STATE_POWER_OFF); +} + +static ssize_t sec_mmi_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + struct sec_mmi_data *data = ts->mmi_ptr; + return scnprintf(buf, PAGE_SIZE, "%s\n", data->product_id); +} + +static ssize_t sec_mmi_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + struct sec_mmi_data *data = ts->mmi_ptr; + return scnprintf(buf, PAGE_SIZE, "%08x-%08x\n", + data->build_id, data->config_id); +} + +static ssize_t sec_mmi_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%d\n", ts->fw_invalid ? 1 : 0); +} + +static ssize_t sec_mmi_hw_irqstat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + switch (gpio_get_value(ts->plat_data->irq_gpio)) { + case 0: + return scnprintf(buf, PAGE_SIZE, "Low\n"); + case 1: + return scnprintf(buf, PAGE_SIZE, "High\n"); + default: + dev_err(DEV_TS, "%s: Failed to get irq gpio %d state\n", + __func__, ts->plat_data->irq_gpio); + return scnprintf(buf, PAGE_SIZE, "Unknown\n"); + } +} + +static ssize_t sec_mmi_drv_irq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%s\n", + ts->irq_enabled ? "ENABLED" : "DISABLED"); +} + +static ssize_t sec_mmi_erase_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + unsigned long value = 0; + int err = 0; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + dev_err(DEV_TS, "%s: Power off state\n", __func__); + return -EIO; + } + + err = kstrtoul(buf, 10, &value); + if (err < 0) { + dev_err(DEV_TS, "%s: Failed to convert value\n", __func__); + return -EINVAL; + } + + if (value != 1) + return -EINVAL; + + mutex_lock(&ts->modechange); + sec_ts_irq_enable(ts, false); + PM_STAY_AWAKE(ts->wakelock); + + ts->fw_invalid = true; + + mutex_unlock(&ts->modechange); + PM_RELAX(ts->wakelock); + + return size; +} + +static ssize_t sec_mmi_drv_irq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + unsigned long value = 0; + int err = 0; + + err = kstrtoul(buf, 10, &value); + if (err < 0) { + dev_err(DEV_TS, "%s: Failed to convert value\n", __func__); + return -EINVAL; + } + switch (value) { + case 0: /* Disable irq */ + sec_ts_irq_enable(ts, false); + break; + case 1: /* Enable irq */ + sec_ts_irq_enable(ts, true); + break; + default: + dev_err(DEV_TS, "%s: invalid value\n", __func__); + return -EINVAL; + } + return size; +} + +static ssize_t sec_mmi_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + struct sec_mmi_data *data = ts->mmi_ptr; + data->reset_func(ts->mmi_ptr, 1); + return size; +} + +static ssize_t sec_mmi_ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + struct sec_mmi_data *data = ts->mmi_ptr; + return scnprintf(buf, PAGE_SIZE, "%s%s\n%s%08x\n%s%08x\n", + "Product ID: ", data->product_id, + "Build ID: ", data->build_id, + "Config ID: ", data->config_id); +} + +static ssize_t sec_mmi_forcereflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + struct sec_mmi_data *data = ts->mmi_ptr; + data->force_calibration = true; + return size; +} + +static ssize_t sec_mmi_doreflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + struct sec_mmi_data *data = ts->mmi_ptr; + const struct firmware *fw_entry; + char fw_path[SEC_TS_MAX_FW_PATH]; + int result = -EFAULT; + + if (size > SEC_TS_MAX_FW_PATH) { + dev_err(DEV_TS, "%s: FW filename is too long\n", __func__); + return -EINVAL; + } + + strlcpy(fw_path, buf, size); + + /* Loading Firmware------------------------------------------ */ + if (request_firmware(&fw_entry, fw_path, &ts->client->dev) != 0) { + dev_err(DEV_TS, "%s: fw not available\n", __func__); + goto err_request_fw; + } + dev_info(DEV_TS, "%s: update fw from %s, size = %d\n", + __func__, fw_path, (int)fw_entry->size); + + mutex_lock(&ts->modechange); + sec_ts_irq_enable(ts, false); + PM_STAY_AWAKE(ts->wakelock); + + result = sec_ts_firmware_update(ts, fw_entry->data, fw_entry->size, + 0, data->force_calibration, 0); + if (ts->fw_invalid == false) + sec_mmi_enable_touch(data); + + data->force_calibration = false; + + mutex_unlock(&ts->modechange); + sec_ts_irq_enable(ts, true); + PM_RELAX(ts->wakelock); + +err_request_fw: + release_firmware(fw_entry); + + return result; +} + +#define MAX_DATA_SZ 1024 + +static bool factory_mode; +static u8 reg_address; +static int data_size; + +static ssize_t sec_mmi_address_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + int error; + long value; + + error = kstrtol(buf, 0, &value); + if (error || value > MAX_DATA_SZ) + return -EINVAL; + + reg_address = (u8)value; + dev_info(DEV_TS, "%s: read address 0x%02X\n", __func__, reg_address); + + return size; +} + +static ssize_t sec_mmi_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + int error; + long value; + + error = kstrtol(buf, 0, &value); + if (error) + return -EINVAL; + + data_size = (unsigned int)value; + dev_info(DEV_TS, "%s: read size %u\n", __func__, data_size); + + return size; +} + +static ssize_t sec_mmi_write_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + char ascii[3] = {0}; + char *sptr, *eptr; + size_t data_sz = size; + u8 hex[MAX_DATA_SZ]; + int byte, error; + long value; + + if (*(buf + size - 1) == '\n') + data_sz--; + + if ((data_sz%2 != 0) || (data_sz/2 > MAX_DATA_SZ)) { + dev_err(DEV_TS, "%s: odd input\n", __func__); + return -EINVAL; + } + + sptr = (char *)buf; + eptr = (char *)buf + data_sz; + pr_debug("%s: data to write: ", __func__); + for (byte = 0; sptr < eptr; sptr += 2, byte++) { + memcpy(ascii, sptr, 2); + error = kstrtol(ascii, 16, &value); + if (error) + break; + hex[byte] = (u8)value; + pr_cont("0x%02x ", hex[byte]); + } + pr_cont("; total=%d\n", byte); + + if (error) { + dev_err(DEV_TS, "%s: input conversion failed\n", __func__); + return -EINVAL; + } + + error = sec_ts_reg_store(dev, attr, hex, byte); + if (error != byte) { + dev_err(DEV_TS, "%s: write error\n", __func__); + return -EIO; + } + + dev_info(DEV_TS, "%s: written %d bytes to address 0x%02X\n", + __func__, byte, hex[0]); + return size; +} + +static ssize_t sec_mmi_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + u8 buffer[MAX_DATA_SZ]; + ssize_t length, blen = 0; + int i; + + /* init parameters required for sec_ts_regread_show() */ + sec_ts_lv1_params(reg_address, data_size); + length = sec_ts_regread_show(dev, attr, buffer); + if (length != data_size) + return sprintf(buf, + "error %zu reading %u bytes from reg addr=0x%02X\n", + length, data_size, reg_address); + + dev_info(DEV_TS, "%s: read %u bytes from reg addr=0x%02X\n", + __func__, data_size, reg_address); + for (i = 0; i < length; i++) + blen += scnprintf(buf + blen, PAGE_SIZE - blen, "%02X ", buffer[i]); + blen += scnprintf(buf + blen, PAGE_SIZE - blen, "\n"); + + return blen; +} + +static void sec_mmi_sysfs(struct sec_mmi_data *data, bool enable) +{ + struct sec_ts_data *ts = data->ts_ptr; + + if (enable) { + int ret; + + ret = sysfs_create_group(&data->i2c_client->dev.kobj, &mmi_attr_group); + if (ret < 0) { + dev_err(DEV_TS, "%s: Failed to create MMI sysfs attrs\n", __func__); + } + + if (strncmp(bi_bootmode(), "mot-factory", strlen("mot-factory")) == 0) { + ret = sysfs_create_group(&data->i2c_client->dev.kobj, + &factory_attr_group); + if (ret < 0) + dev_err(DEV_TS, "%s: Failed to create factory sysfs\n", __func__); + else + factory_mode = true; + } + } else { + sysfs_remove_group(&data->i2c_client->dev.kobj, &mmi_attr_group); + if (factory_mode) + sysfs_remove_group(&data->i2c_client->dev.kobj, &factory_attr_group); + } +} + +int sec_mmi_data_init(struct sec_ts_data *ts, bool enable) +{ + struct sec_mmi_data *data; + + if (enable) + data = kzalloc(sizeof(*data), GFP_KERNEL); + else + data = ts->mmi_ptr; + + if (!data) { + dev_err(DEV_TS, "%s: MMI data is NULL\n", __func__); + return 0; + } + + if (enable) { +#if defined(CONFIG_DRM) + int probe_status; + bool panel_ready = true; + char *pname = NULL; +#endif + + ts->mmi_ptr = (void *)data; + data->ts_ptr = ts; + data->i2c_client = ts->client; + data->reset_func = sec_mmi_ic_reset; + + sec_mmi_dt(data); + +#if defined(CONFIG_DRM) + /* DRM panel status */ + panel_ready = dsi_display_is_panel_enable(data->ctrl_dsi, + &probe_status, &pname); + dev_info(DEV_TS, "%s: drm: probe=%d, enable=%d, panel'%s'\n", + __func__, probe_status, panel_ready, pname); + /* check panel binding */ + if (data->bound_display && (probe_status == -ENODEV || + (pname && strncmp(pname, data->bound_display, + strlen(data->bound_display))))) { + dev_err(DEV_TS, "%s: panel binding failed\n", __func__); + return -ENODEV; + } + + if (pname && strstr(pname, "dummy")) { + dev_info(DEV_TS, "%s: dummy panel; exiting...\n", __func__); + return -ENODEV; + } +#endif + INIT_DELAYED_WORK(&data->resume_work, sec_mmi_queued_resume); + INIT_DELAYED_WORK(&data->work, sec_mmi_work); + /* register charging detection before sec_mmi_enable_touch enabling touch */ + if (data->usb_detection) + INIT_WORK(&data->ps_notify_work, sec_mmi_ps_work); + + if (data->gpio_config) + sec_mmi_ic_reset(data, 1); + + data->task = MMI_TASK_INIT; + schedule_delayed_work(&data->work, msecs_to_jiffies(50)); + } + + sec_mmi_touchscreen_class(data, enable); + sec_mmi_register_notifiers(data, enable); + sec_mmi_sysfs(data, enable); + + if (!enable) { + sec_mmi_sysfs(data, true); + cancel_delayed_work(&data->resume_work); + cancel_delayed_work(&data->work); + if (data->usb_detection) + cancel_work_sync(&data->ps_notify_work); + kfree(data); + ts->mmi_ptr = NULL; + } + + return 0; +} diff --git a/drivers/input/touchscreen/sec_mmi/sec_ts_mmi_class.c b/drivers/input/touchscreen/sec_mmi/sec_ts_mmi_class.c new file mode 100644 index 000000000000..771270714da6 --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/sec_ts_mmi_class.c @@ -0,0 +1,1359 @@ +/* + * Copyright (C) 2018 Motorola Mobility LLC + * + * 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sec_ts.h" +#include "sec_mmi.h" + +#define GET_TS_DATA(dev) { \ + ts = dev_get_drvdata(dev); \ + if (!ts) { \ + dev_err(dev, "%s: Failed to get drv data\n", __func__); \ + return -ENODEV; \ + } \ +} + +static ssize_t sec_mmi_suppression_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_suppression_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t sec_mmi_pill_region_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_pill_region_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t sec_mmi_hold_distance_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_hold_distance_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t sec_mmi_address_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_write_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_data_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t sec_mmi_gs_distance_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_gs_distance_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t sec_mmi_hold_grip_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_mmi_hold_grip_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t sec_mmi_mutual_range_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static DEVICE_ATTR(address, (S_IWUSR | S_IWGRP), NULL, sec_mmi_address_store); +static DEVICE_ATTR(size, (S_IWUSR | S_IWGRP), NULL, sec_mmi_size_store); +static DEVICE_ATTR(write, (S_IWUSR | S_IWGRP), NULL, sec_mmi_write_store); +static DEVICE_ATTR(data, S_IRUGO, sec_mmi_data_show, NULL); +static DEVICE_ATTR(mutual_range, S_IRUGO, sec_mmi_mutual_range_show, NULL); +static DEVICE_ATTR(suppression, (S_IRUGO | S_IWUSR | S_IWGRP), + sec_mmi_suppression_show, sec_mmi_suppression_store); +static DEVICE_ATTR(pill_region, (S_IRUGO | S_IWUSR | S_IWGRP), + sec_mmi_pill_region_show, sec_mmi_pill_region_store); +static DEVICE_ATTR(hold_distance, (S_IRUGO | S_IWUSR | S_IWGRP), + sec_mmi_hold_distance_show, sec_mmi_hold_distance_store); +static DEVICE_ATTR(gs_distance, (S_IRUGO | S_IWUSR | S_IWGRP), + sec_mmi_gs_distance_show, sec_mmi_gs_distance_store); +static DEVICE_ATTR(hold_grip, (S_IRUGO | S_IWUSR | S_IWGRP), + sec_mmi_hold_grip_show, sec_mmi_hold_grip_store); + +#define MAX_ATTRS_ENTRIES 10 +#define ADD_ATTR(name) { \ + if (idx < MAX_ATTRS_ENTRIES) { \ + dev_info(dev, "%s: [%d] adding %p\n", __func__, idx, &dev_attr_##name.attr); \ + ext_attributes[idx] = &dev_attr_##name.attr; \ + idx++; \ + } else { \ + dev_err(dev, "%s: cannot add attribute '%s'\n", __func__, #name); \ + } \ +} + +static struct attribute *ext_attributes[MAX_ATTRS_ENTRIES]; +static struct attribute_group ext_attr_group = { + .attrs = ext_attributes, +}; + +static int sec_mmi_extend_attribute_group(struct device *dev, struct attribute_group **group) +{ + struct sec_ts_data *ts; + int idx = 0; + + GET_TS_DATA(dev); + + if (ts->plat_data->suppression_ctrl) + ADD_ATTR(suppression); + + if (ts->plat_data->pill_region_ctrl) + ADD_ATTR(pill_region); + + if (ts->plat_data->hold_distance_ctrl) + ADD_ATTR(hold_distance); + + if (ts->plat_data->gs_distance_ctrl) + ADD_ATTR(gs_distance); + + if (ts->plat_data->hold_grip_ctrl) + ADD_ATTR(hold_grip); + + if (strncmp(bi_bootmode(), "mot-factory", strlen("mot-factory")) == 0) { + ADD_ATTR(address); + ADD_ATTR(size); + ADD_ATTR(data); + ADD_ATTR(write); + ADD_ATTR(mutual_range); + } + + if (idx) { + ext_attributes[idx] = NULL; + *group = &ext_attr_group; + } else + *group = NULL; + + return 0; +} + +void sec_mmi_gesture_handler(void *data) { + struct sec_ts_gesture_status *gs = + (struct sec_ts_gesture_status *)data; + + if (gs->eid != SEC_TS_GESTURE_EVENT) { + pr_info("%s: invalid gesture ID\n", __func__); + return; + } + + pr_info("%s: GESTURE %x %x %x %x %x %x %x %x\n", __func__, + gs->eid | (gs->stype << 4) | (gs->sf << 6), + gs->gesture_id, + gs->gesture_data_1, + gs->gesture_data_2, + gs->gesture_data_3, + gs->gesture_data_4, + gs->reserved_1, + gs->left_event_5_0 | (gs->reserved_2 << 6)); + + switch (gs->gesture_id) { + case 1: + pr_info("%s: single tap\n", __func__); + break; + case 2: + pr_info("%s: zero tap; x=%x, y=%x, w=%x, p=%x\n", __func__, + gs->gesture_data_1+((gs->gesture_data_3 & 0x0f) << 8), + gs->gesture_data_2+((gs->gesture_data_3 & 0xf0) << 4), + gs->gesture_data_4, + gs->reserved_1); + break; + default: + pr_info("%s: unknown id=%x\n", __func__, gs->gesture_id); + } +} + +static void sec_mmi_ic_reset(struct sec_ts_data *ts, int mode) +{ + if (!gpio_is_valid(ts->plat_data->rst_gpio) || + !gpio_get_value(ts->plat_data->rst_gpio)) + return; + + PM_STAY_AWAKE(ts->wakelock); + mutex_lock(&ts->modechange); + /* disable irq to ensure getting boot complete */ + sec_ts_irq_enable(ts, false); + + if (mode == 1) { + gpio_set_value(ts->plat_data->rst_gpio, 0); + usleep_range(10, 10); + gpio_set_value(ts->plat_data->rst_gpio, 1); + dev_dbg(&ts->client->dev, "%s: reset line toggled\n", __func__); + } else { + int ret; + + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SW_RESET, NULL, 0); + if (ret < 0) + dev_err(&ts->client->dev, "%s: error sending sw reset cmd\n", __func__); + } + + sec_ts_wait_for_ready(ts, SEC_TS_ACK_BOOT_COMPLETE); + sec_ts_irq_enable(ts, true); + + mutex_unlock(&ts->modechange); + PM_RELAX(ts->wakelock); + + dev_dbg(&ts->client->dev, "%s: hw reset done\n", __func__); +} + +static void sec_mmi_enable_touch(struct sec_ts_data *ts) +{ + int ret; + unsigned char buffer[8]; + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_IMG_VERSION, buffer, sizeof(buffer)); + if (ret < 0) { + input_err(true, &ts->client->dev, + "%s: failed to read fw version (%d)\n", + __func__, ret); + } else { + if (buffer[0] == 0x17) { + ts->device_id[3] = 0x7C; + input_info(true, &ts->client->dev, + "%s: set device_id[3] to 0x7C\n", __func__); + } + } + sec_ts_integrity_check(ts); + sec_ts_sense_on(ts); + dev_dbg(&ts->client->dev, "%s: touch sensing ready\n", __func__); +} + +static ssize_t sec_mmi_suppression_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts; + unsigned char buffer; + int error; + unsigned long value; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + error = kstrtoul(buf, 0, &value); + if (error) + return -EINVAL; + + buffer = (unsigned char)value; + dev_dbg(dev, "%s: program value 0x%02x\n", __func__, (unsigned int)buffer); + + if (ts->suppression_data == buffer) { + dev_dbg(dev, "%s: value is same,so not write.\n", __func__); + return size; + } + ts->suppression_data = buffer; + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + dev_dbg(dev, "%s: power off state\n", __func__); + return size; + } + + error = ts->sec_ts_i2c_write(ts, SEC_TS_GRIP_SUPPRESSION_INFO, &buffer, sizeof(buffer)); + if (error < 0) + dev_err(dev, "%s: failed to write suppression info (%d)\n", + __func__, error); + + error = ts->sec_ts_i2c_read(ts, SEC_TS_GRIP_SUPPRESSION_INFO, &buffer, sizeof(buffer)); + if (error < 0) + dev_err(dev, "%s: failed to read suppression info (%d)\n", + __func__, error); + else + dev_dbg(dev, "%s: suppression info 0x%02x\n", __func__, (unsigned int)buffer); + + return size; +} + +static ssize_t sec_mmi_suppression_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts; + unsigned char buffer; + ssize_t blen = 0; + int error; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + dev_dbg(dev, "%s: power off state\n", __func__); + blen += scnprintf(buf, PAGE_SIZE, "0x%02x", (unsigned int)(ts->suppression_data)); + return blen; + } + error = ts->sec_ts_i2c_read(ts, SEC_TS_GRIP_SUPPRESSION_INFO, &buffer, sizeof(buffer)); + if (error < 0) + dev_err(dev, "%s: failed to read suppression info (%d)\n", + __func__, error); + else { + blen += scnprintf(buf, PAGE_SIZE, "0x%02x", (unsigned int)buffer); + } + + return blen; +} + +int sec_mmi_sysfs_notify(struct sec_ts_data *ts, unsigned char state) +{ + if (state != 0xff) + ts->hold_grip_data = state; + + sysfs_notify(ts->imports->kobj_notify, NULL, "hold_grip"); + dev_dbg(&ts->client->dev, "%s: triggered HAL with 0x%02x\n", __func__, state); + + return 0; +} + +static ssize_t sec_mmi_hold_grip_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + sec_mmi_sysfs_notify(ts, 0xff); + + return size; +} + +static ssize_t sec_mmi_hold_grip_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + return scnprintf(buf, PAGE_SIZE, "0x%02x", (unsigned int)(ts->hold_grip_data)); +} + +#define REQ_ARGS_NUM 3 + +static ssize_t sec_mmi_pill_region_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts; + unsigned int args[3] = {0}; + unsigned char buffer[5]; + int error; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + error = sscanf(buf, "0x%x 0x%x 0x%x", &args[0], &args[1], &args[2]); + if (error < REQ_ARGS_NUM) + return -EINVAL; + + buffer[0] = (unsigned char)args[0]; + buffer[1] = (unsigned char)((args[1] >> 8) & 0xff); + buffer[2] = (unsigned char)(args[1] & 0xff); + buffer[3] = (unsigned char)((args[2] >> 8) & 0xff); + buffer[4] = (unsigned char)(args[2] & 0xff); + dev_dbg(dev, "%s: program pill region 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", __func__, + (unsigned int)buffer[0], (unsigned int)buffer[1], + (unsigned int)buffer[2], (unsigned int)buffer[3], + (unsigned int)buffer[4]); + + if (!memcmp(buffer, ts->pill_region_data, sizeof(buffer))) { + dev_dbg(dev, "%s: value is same,so not write.\n", __func__); + return size; + } + + memcpy(ts->pill_region_data, buffer, sizeof(ts->pill_region_data)); + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + dev_dbg(dev, "%s: power off state\n", __func__); + return size; + } + + error = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_PILL_REGION, buffer, sizeof(buffer)); + if (error < 0) + dev_err(dev, "%s: failed to write pill region (%d)\n", + __func__, error); + + error = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_PILL_REGION, buffer, sizeof(buffer)); + if (error < 0) + dev_err(dev, "%s: failed to read pill region (%d)\n", + __func__, error); + else { + dev_dbg(dev, "%s: pill region 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", __func__, + (unsigned int)buffer[0], (unsigned int)buffer[1], + (unsigned int)buffer[2], (unsigned int)buffer[3], + (unsigned int)buffer[4]); + } + + return size; +} + +static ssize_t sec_mmi_pill_region_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts; + unsigned char buffer[5] = {0}; + ssize_t blen = 0; + int error; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + dev_dbg(dev, "%s: power off state\n", __func__); + memcpy(buffer, ts->pill_region_data, sizeof(buffer)); + blen += scnprintf(buf, PAGE_SIZE, "0x%02x 0x%x 0x%x", (unsigned int)buffer[0], + (unsigned int)buffer[2] | (buffer[1] << 8), + (unsigned int)buffer[4] | (buffer[3] << 8)); + return blen; + } + error = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_PILL_REGION, buffer, sizeof(buffer)); + if (error < 0) + dev_err(dev, "%s: failed to read pill region (%d)\n", + __func__, error); + else { + dev_dbg(dev, "%s: pill region 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x\n", __func__, + (unsigned int)buffer[0], (unsigned int)buffer[1], + (unsigned int)buffer[2], (unsigned int)buffer[3], + (unsigned int)buffer[4]); + + blen += scnprintf(buf, PAGE_SIZE, "0x%02x 0x%x 0x%x", (unsigned int)buffer[0], + (unsigned int)buffer[2] | (buffer[1] << 8), + (unsigned int)buffer[4] | (buffer[3] << 8)); + } + + return blen; +} + +static ssize_t sec_mmi_hold_distance_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts; + unsigned char buffer; + unsigned long value; + int error; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + error = kstrtoul(buf, 0, &value); + if (error) + return -EINVAL; + + buffer = (unsigned char)value; + dev_dbg(dev, "%s: program value 0x%02x\n", __func__, (unsigned int)buffer); + + if (ts->hold_distance_data == buffer) { + dev_dbg(dev, "%s: value is same,so not write.\n", __func__); + return size; + } + ts->hold_distance_data = buffer; + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + dev_dbg(dev, "%s: power off state\n", __func__); + return size; + } + + error = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_HOLD_DISTANCE, &buffer, sizeof(buffer)); + if (error < 0) + dev_err(dev, "%s: failed to write hold distance (%d)\n", + __func__, error); + + return size; +} + +static ssize_t sec_mmi_hold_distance_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts; + unsigned char buffer; + ssize_t blen = 0; + int error; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + dev_dbg(dev, "%s: power off state\n", __func__); + blen += scnprintf(buf, PAGE_SIZE, "0x%02x", (unsigned int)(ts->hold_distance_data)); + return blen; + } + + error = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_HOLD_DISTANCE, &buffer, sizeof(buffer)); + if (error < 0) + dev_err(dev, "%s: failed to read hold distance info (%d)\n", + __func__, error); + else { + blen += scnprintf(buf, PAGE_SIZE, "0x%02x", (unsigned int)buffer); + } + + return blen; +} + +static ssize_t sec_mmi_gs_distance_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts; + unsigned char buffer; + unsigned long value; + int error; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + error = kstrtoul(buf, 0, &value); + if (error) + return -EINVAL; + + buffer = (unsigned char)value; + dev_dbg(dev, "%s: program value 0x%02x\n", __func__, (unsigned int)buffer); + + if (ts->gs_distance_data == buffer) { + dev_dbg(dev, "%s: value is same,so not write.\n", __func__); + return size; + } + ts->gs_distance_data = buffer; + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + dev_dbg(dev, "%s: power off state\n", __func__); + return size; + } + + error = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_GRIP_SUPP_AREA, &buffer, sizeof(buffer)); + if (error < 0) + dev_err(dev, "%s: failed to write grip suppression distance (%d)\n", + __func__, error); + + return size; +} + +static ssize_t sec_mmi_gs_distance_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts; + unsigned char buffer; + ssize_t blen = 0; + int error; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + dev_dbg(dev, "%s: power off state\n", __func__); + blen += scnprintf(buf, PAGE_SIZE, "0x%02x", (unsigned int)(ts->gs_distance_data)); + return blen; + } + + error = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_GRIP_SUPP_AREA, &buffer, sizeof(buffer)); + if (error < 0) + dev_err(dev, "%s: failed to read grip suppression distance info (%d)\n", + __func__, error); + else { + blen += scnprintf(buf, PAGE_SIZE, "0x%02x", (unsigned int)buffer); + } + + return blen; +} + +static ssize_t sec_mmi_mutual_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char ostate = STATE_MANAGE_OFF; + unsigned char tmode[2] = { TOUCH_SYSTEM_MODE_TOUCH, TOUCH_MODE_STATE_TOUCH }; + int i, error; + short p, min = 1, max = -1; + struct sec_ts_data *ts; + unsigned int readbytes; + unsigned char *pRead; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) + goto not_ready; + + readbytes = ts->rx_count * ts->rx_count * 2; + pRead = kzalloc(readbytes, GFP_KERNEL); + if (!pRead) + return -ENOMEM; + + error = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_STATEMANAGE_ON, &ostate, sizeof(ostate)); + if (error < 0) { + dev_err(dev, "%s: cannot set manage state: %d\n", __func__, error); + goto free_and_leave; + } + sec_ts_delay(20); + + error = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CHG_SYSMODE, tmode, sizeof(tmode)); + if (error < 0) { + dev_err(dev, "%s: cannot set sysmode: %d\n", __func__, error); + goto restore_mode; + } + sec_ts_delay(20); + + ostate = TYPE_AMBIENT_DATA; + error = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_MUTU_RAW_TYPE, &ostate, sizeof(ostate)); + if (error < 0) { + dev_err(dev, "%s: cannot set rawdata type: %d\n", __func__, error); + goto restore_mode; + } + sec_ts_delay(50); + + error = ts->sec_ts_i2c_read(ts, SEC_TS_READ_TOUCH_RAWDATA, pRead, readbytes); + if (error < 0) { + dev_err(dev, "%s: cannot set rawdata type: %d\n", __func__, error); + goto release_afe; + } + sec_ts_delay(50); + + for (i = 0; i < readbytes; i+=2) { + p = pRead[i + 1] + (pRead[i] << 8); + if (!i) { + min = max = p; + continue; + } + if (p < min) + min = p; + else if (p > max) + max = p; + } + +release_afe: + ostate = TYPE_INVALID_DATA; + error = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_MUTU_RAW_TYPE, &ostate, sizeof(ostate)); + if (error < 0) + dev_err(dev, "%s: cannot restore rawdata type: %d\n", __func__, error); +restore_mode: + ostate = STATE_MANAGE_ON; + error = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_STATEMANAGE_ON, &ostate, sizeof(ostate)); + if (error < 0) + dev_err(dev, "%s: cannot restore manage state: %d\n", __func__, error); + sec_ts_delay(20); +free_and_leave: + kfree(pRead); +not_ready: + return scnprintf(buf, PAGE_SIZE, "%d,%d", min, max); +} + +#define MAX_DATA_SZ 1024 +static u8 reg_address; +static int data_size; + +static ssize_t sec_mmi_address_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int error; + long value; + + error = kstrtol(buf, 0, &value); + if (error || value > MAX_DATA_SZ) + return -EINVAL; + + reg_address = (u8)value; + dev_info(dev, "%s: read address 0x%02X\n", __func__, reg_address); + + return size; +} + +static ssize_t sec_mmi_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int error; + long value; + + error = kstrtol(buf, 0, &value); + if (error) + return -EINVAL; + + data_size = (unsigned int)value; + dev_info(dev, "%s: read size %u\n", __func__, data_size); + + return size; +} + +static ssize_t sec_mmi_write_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + char ascii[3] = {0}; + char *sptr, *eptr; + size_t data_sz = size; + u8 hex[MAX_DATA_SZ]; + int byte, error; + long value; + + dev = MMI_DEV_TO_TS_DEV(dev); + + if (*(buf + size - 1) == '\n') + data_sz--; + + if ((data_sz%2 != 0) || (data_sz/2 > MAX_DATA_SZ)) { + dev_err(dev, "%s: odd input\n", __func__); + return -EINVAL; + } + + sptr = (char *)buf; + eptr = (char *)buf + data_sz; + pr_debug("%s: data to write: ", __func__); + for (byte = 0; sptr < eptr; sptr += 2, byte++) { + memcpy(ascii, sptr, 2); + error = kstrtol(ascii, 16, &value); + if (error) + break; + hex[byte] = (u8)value; + pr_cont("0x%02x ", hex[byte]); + } + pr_cont("; total=%d\n", byte); + + if (error) { + dev_err(dev, "%s: input conversion failed\n", __func__); + return -EINVAL; + } + + error = sec_ts_reg_store(dev, attr, hex, byte); + if (error != byte) { + dev_err(dev, "%s: write error\n", __func__); + return -EIO; + } + + dev_info(dev, "%s: written %d bytes to address 0x%02X\n", + __func__, byte, hex[0]); + return size; +} + +static ssize_t sec_mmi_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 buffer[MAX_DATA_SZ]; + ssize_t length, blen = 0; + int i; + + dev = MMI_DEV_TO_TS_DEV(dev); + + /* init parameters required for sec_ts_regread_show() */ + sec_ts_lv1_params(reg_address, data_size); + length = sec_ts_regread_show(dev, attr, buffer); + if (length != data_size) + return sprintf(buf, + "error %zu reading %u bytes from reg addr=0x%02X\n", + length, data_size, reg_address); + + dev_info(dev, "%s: read %u bytes from reg addr=0x%02X\n", + __func__, data_size, reg_address); + for (i = 0; i < length; i++) + blen += scnprintf(buf + blen, PAGE_SIZE - blen, "%02X ", buffer[i]); + blen += scnprintf(buf + blen, PAGE_SIZE - blen, "\n"); + + return blen; +} + + +static int sec_mmi_methods_get_vendor(struct device *dev, void *cdata) { + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", "samsung"); +} + +static int sec_mmi_methods_get_productinfo(struct device *dev, void *cdata) { + struct sec_ts_data *ts = dev_get_drvdata(dev); + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_INFO_LEN, "%c%c%c%02x", + tolower(ts->device_id[0]), tolower(ts->device_id[1]), + ts->device_id[2], ts->device_id[3]); +} + + +static inline void bdc2ui(unsigned int *dest, unsigned char *src, size_t size) +{ + int i, shift = 0; + *dest = 0; + for (i = 0; i < size; i++, shift += 8) + *dest += src[i] << (24 - shift); +} + +static int sec_mmi_methods_get_build_id(struct device *dev, void *cdata) { + struct sec_ts_data *ts = dev_get_drvdata(dev); + unsigned char buffer[8]; + unsigned int build_id = 0; + int ret; + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_IMG_VERSION, buffer, sizeof(buffer)); + if (ret < 0) + dev_err(dev, "%s: failed to read fw version (%d)\n", + __func__, ret); + else { + if (buffer[0] == 0x17) + ts->device_id[3] = 0x7C; + bdc2ui(&build_id, buffer, sizeof(build_id)); + dev_dbg(dev, "%s: IMAGE VER: " \ + "%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", + __func__, buffer[0], buffer[1], buffer[2], buffer[3], + buffer[4], buffer[5], buffer[6], buffer[7]); + } + + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%08x", build_id); +} + +static int sec_mmi_methods_get_config_id(struct device *dev, void *cdata) { + struct sec_ts_data *ts = dev_get_drvdata(dev); + unsigned char buffer[8]; + unsigned int config_id = 0; + int ret; + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_PARA_VERSION, buffer, sizeof(buffer)); + if (ret < 0) + dev_err(dev, "%s: failed to read fw version (%d)\n", + __func__, ret); + else { + bdc2ui(&config_id, buffer+4, sizeof(config_id)); + dev_dbg(dev, "%s: CONFIG VER: " \ + "%02X, %02X, %02X, %02X, %02X, %02X, %02X, %02X\n", + __func__, buffer[0], buffer[1], buffer[2], buffer[3], + buffer[4], buffer[5], buffer[6], buffer[7]); + } + + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%08x", config_id); +} + +static int sec_mmi_methods_get_bus_type(struct device *dev, void *idata) { + TO_INT(idata) = TOUCHSCREEN_MMI_BUS_TYPE_I2C; + return 0; +} + +static int sec_mmi_methods_get_irq_status(struct device *dev, void *idata) { + struct sec_ts_data *ts = dev_get_drvdata(dev); + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + TO_INT(idata) = gpio_get_value(ts->plat_data->irq_gpio); + + return 0; +} + +static int sec_mmi_methods_get_drv_irq(struct device *dev, void *idata) { + struct sec_ts_data *ts = dev_get_drvdata(dev); + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + TO_INT(idata) = ts->irq_enabled ? 1 : 0; + + return 0; +} + +static int sec_mmi_methods_get_poweron(struct device *dev, void *idata) { + + struct sec_ts_data *ts = dev_get_drvdata(dev); + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + TO_INT(idata) = (ts->power_status != SEC_TS_STATE_POWER_OFF) ? 1 : 0; + + return 0; +} + +static void sec_mmi_print_version(struct sec_ts_data *ts) +{ + char prod_info[TS_MMI_MAX_INFO_LEN]; + char build_id[TS_MMI_MAX_ID_LEN]; + char config_id[TS_MMI_MAX_ID_LEN]; + + sec_mmi_methods_get_productinfo(&ts->client->dev, prod_info); + if (ts->fw_invalid == true) { + dev_info(&ts->client->dev, "Product %s in bootloader mode\n", + prod_info); + return; + } + + sec_mmi_methods_get_build_id(&ts->client->dev, build_id); + sec_mmi_methods_get_config_id(&ts->client->dev, config_id); + dev_info(&ts->client->dev, "Product %s, firmware id: %s, config_id: %s\n", + prod_info, build_id, config_id); +} + +static int sec_mmi_methods_get_flashprog(struct device *dev, void *idata) { + struct sec_ts_data *ts = dev_get_drvdata(dev); + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + TO_INT(idata) = ts->fw_invalid ? 1 : 0; + + return 0; +} + + +static int sec_mmi_methods_drv_irq(struct device *dev, int state) { + struct sec_ts_data *ts = dev_get_drvdata(dev); + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + switch (state) { + case 0: /* Disable irq */ + sec_ts_irq_enable(ts, false); + break; + case 1: /* Enable irq */ + sec_ts_irq_enable(ts, true); + break; + default: + dev_err(dev, "%s: invalid value\n", __func__); + return -EINVAL; + } + return 0; + +} + +static int sec_mmi_methods_reset(struct device *dev, int type) { + struct sec_ts_data *ts = dev_get_drvdata(dev); + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + sec_mmi_ic_reset(ts, type); + + return 0; +} + +static int sec_mmi_methods_power(struct device *dev, int on) { + struct sec_ts_data *ts = dev_get_drvdata(dev); + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + if (on) + ts->power_status = SEC_TS_STATE_POWER_ON; + else + ts->power_status = SEC_TS_STATE_POWER_OFF; + + return sec_ts_power((void *)ts, on == TS_MMI_POWER_ON); +} + +static int sec_mmi_methods_refresh_rate(struct device *dev, int freq) { + struct sec_ts_data *ts = dev_get_drvdata(dev); + int ret; + unsigned char f = freq & 0xff; + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + dev_info(dev, "%s: writing refresh rate %dhz\n", __func__, freq); + ret = ts->sec_ts_i2c_write(ts, 0x4C, &f, 1); + if (ret < 0) { + dev_err(dev, "%s: failed refresh_rate (%d)\n", __func__, ret); + return ret; + } + return 0; +} + +static int sec_mmi_update_baseline(struct device *dev, int mode) { + struct sec_ts_data *ts = dev_get_drvdata(dev); + int ret; + unsigned char d = (mode == TS_MMI_UPDATE_BASELINE_ON) ? 0x01 : 0x00; + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + dev_info(dev, "%s: update baseline %s\n", __func__, + (mode == TS_MMI_UPDATE_BASELINE_ON) ? "enable" : "disable"); + ret = ts->sec_ts_i2c_write(ts, 0x5f, &d, 1); + if (ret < 0) { + dev_err(dev, "%s: failed set baseline update (%d)\n", __func__, ret); + return ret; + } + return 0; +} + +static int sec_mmi_methods_pinctrl(struct device *dev, int on) { + struct sec_ts_data *ts = dev_get_drvdata(dev); + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + return sec_ts_pinctrl_configure(ts, on == TS_MMI_PINCTL_ON); +} + +static int sec_mmi_firmware_update(struct device *dev, char *fwname) { + struct sec_ts_data *ts = dev_get_drvdata(dev); + const struct firmware *fw_entry; + int result = -EFAULT; + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + /* Loading Firmware------------------------------------------ */ + if (request_firmware(&fw_entry, fwname, &ts->client->dev) != 0) { + dev_err(dev, "%s: fw not available\n", __func__); + goto err_request_fw; + } + dev_info(dev, "%s: update fw from %s, size = %d\n", + __func__, fwname, (int)fw_entry->size); + + mutex_lock(&ts->modechange); + sec_ts_irq_enable(ts, false); + PM_STAY_AWAKE(ts->wakelock); + + result = sec_ts_firmware_update(ts, fw_entry->data, fw_entry->size, + 0, false, 0); /* do not run calibration!!! */ + if (ts->fw_invalid == false) + sec_mmi_enable_touch(ts); + + sec_mmi_print_version(ts); + + mutex_unlock(&ts->modechange); + sec_ts_irq_enable(ts, true); + PM_RELAX(ts->wakelock); + +err_request_fw: + release_firmware(fw_entry); + + return result; +} + +static int sec_mmi_firmware_erase(struct device *dev) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + dev_err(dev, "%s: Power off state\n", __func__); + return -EIO; + } + + mutex_lock(&ts->modechange); + sec_ts_irq_enable(ts, false); + PM_STAY_AWAKE(ts->wakelock); + + ts->fw_invalid = true; + + mutex_unlock(&ts->modechange); + PM_RELAX(ts->wakelock); + + return 0; +} + +static int sec_mmi_panel_state(struct device *dev, + enum ts_mmi_pm_mode from, enum ts_mmi_pm_mode to) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + switch (to) { + case TS_MMI_PM_DEEPSLEEP: + return sec_ts_set_lowpowermode(ts, TO_SLEEP_MODE); + case TS_MMI_PM_GESTURE: + return sec_ts_set_lowpowermode(ts, TO_LOWPOWER_MODE); + case TS_MMI_PM_ACTIVE: + return sec_ts_set_lowpowermode(ts, TO_TOUCH_MODE); + default: + dev_warn(dev, "panel mode %d is invalid.\n", to); + return -EINVAL; + } +} + +static int sec_mmi_wait_for_ready(struct device *dev) { + struct sec_ts_data *ts = dev_get_drvdata(dev); + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + if (!ts->plat_data->regulator_boot_on) + sec_ts_delay(70); + + return sec_ts_wait_for_ready(ts, SEC_TS_ACK_BOOT_COMPLETE); +} + +static int sec_mmi_pre_suspend(struct device *dev) { + struct sec_ts_data *ts = dev_get_drvdata(dev); + unsigned long start_wait_jiffies = jiffies; + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + do { + if (!ts->wakelock->active) + break; + usleep_range(1000, 1000); + } while (1); + + if ((jiffies - start_wait_jiffies)) + dev_info(dev, "%s: entering suspend delayed for %ums\n", + __func__, jiffies_to_msecs(jiffies - start_wait_jiffies)); + + return 0; +} + +static int sec_mmi_post_suspend(struct device *dev) { + struct sec_ts_data *ts = dev_get_drvdata(dev); + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + if (ts->lowpower_mode) + reinit_completion(&ts->resume_done); + + return 0; +} + +static int sec_mmi_post_resume(struct device *dev) { + struct sec_ts_data *ts = dev_get_drvdata(dev); + int ret; + unsigned char buffer = 0; + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + dev_dbg(dev, "%s: sending sense_on...\n", __func__); + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_SENSE_ON, NULL, 0); + if (ret < 0) + dev_err(dev, "%s: failed sense_on (%d)\n", __func__, ret); + + if (ts->lowpower_mode) + complete_all(&ts->resume_done); + + if (ts->plat_data->suppression_ctrl) { + ret = ts->sec_ts_i2c_write(ts, SEC_TS_GRIP_SUPPRESSION_INFO, + &ts->suppression_data, sizeof(ts->suppression_data)); + if (ret < 0) + dev_err(dev, "%s: failed to write suppression info (%d)\n", + __func__, ret); + } + + if(ts->plat_data->pill_region_ctrl) { + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_PILL_REGION, + ts->pill_region_data, sizeof(ts->pill_region_data)); + if (ret < 0) + dev_err(dev, "%s: failed to write pill region (%d)\n", + __func__, ret); + } + + if(ts->plat_data->hold_distance_ctrl) { + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_HOLD_DISTANCE, + &ts->hold_distance_data, sizeof(ts->hold_distance_data)); + if (ret < 0) + dev_err(dev, "%s: failed to write hold distance (%d)\n", + __func__, ret); + } + + if(ts->plat_data->gs_distance_ctrl) { + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_GRIP_SUPP_AREA, + &ts->gs_distance_data, sizeof(ts->gs_distance_data)); + if (ret < 0) + dev_err(dev, "%s: failed to write grip supp distance (%d)\n", + __func__, ret); + } + + ret = ts->sec_ts_i2c_read(ts, SEC_TS_READ_BOOT_STATUS, + &buffer, sizeof(buffer)); + if (ret < 0) + dev_err(dev, "%s: failed to read boot status (%d)\n", __func__, ret); + + ts->fw_invalid = buffer == SEC_TS_STATUS_BOOT_MODE; + + return 0; +} + +static int sec_mmi_charger_mode(struct device *dev, int mode) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + int ret; + unsigned char cval = 0; + unsigned char bitval = 1; + + if (!ts) { + dev_err(dev, "Failed to get driver data"); + return -ENODEV; + } + + /* plugged in USB should change value to 2 */ + if (mode) + bitval++; + + ret = ts->sec_ts_i2c_read(ts, SET_TS_CMD_SET_CHARGER_MODE, + &cval, sizeof(cval)); + if (ret < 0) { + dev_err(dev, "%s: failed to read charger mode\n", __func__); + } + + if (cval != bitval) + sec_ts_set_charger(ts, mode); + else + dev_dbg(dev, "%s: charger mode already %d\n", __func__, cval); + return 0; +} + +static struct ts_mmi_methods sec_ts_mmi_methods = { + .get_vendor = sec_mmi_methods_get_vendor, + .get_productinfo = sec_mmi_methods_get_productinfo, + .get_build_id = sec_mmi_methods_get_build_id, + .get_config_id = sec_mmi_methods_get_config_id, + .get_bus_type = sec_mmi_methods_get_bus_type, + .get_irq_status = sec_mmi_methods_get_irq_status, + .get_drv_irq = sec_mmi_methods_get_drv_irq, + .get_poweron = sec_mmi_methods_get_poweron, + .get_flashprog = sec_mmi_methods_get_flashprog, + /* SET methods */ + .reset = sec_mmi_methods_reset, + .drv_irq = sec_mmi_methods_drv_irq, + .power = sec_mmi_methods_power, + .pinctrl = sec_mmi_methods_pinctrl, + .refresh_rate = sec_mmi_methods_refresh_rate, + .charger_mode = sec_mmi_charger_mode, + .update_baseline = sec_mmi_update_baseline, + /* Firmware */ + .firmware_update = sec_mmi_firmware_update, + .firmware_erase = sec_mmi_firmware_erase, + /* vendor specific attribute group */ + .extend_attribute_group = sec_mmi_extend_attribute_group, + /* PM callback */ + .panel_state = sec_mmi_panel_state, + .wait_for_ready = sec_mmi_wait_for_ready, + .post_resume = sec_mmi_post_resume, + .pre_suspend = sec_mmi_pre_suspend, + .post_suspend = sec_mmi_post_suspend, +}; + +static int sec_mmi_class_fname(struct sec_ts_data *ts) +{ + int ret; + char buffer[128] = "samsung_mmi"; + const char* fname = NULL; + static char *input_dev_name; + + if (ts->imports && ts->imports->get_class_fname) { + ret = ts->imports->get_class_fname(&ts->client->dev, &fname); + if (!ret) + scnprintf(buffer, sizeof(buffer), "samsung_mmi.%s", fname); + } + + input_dev_name = kstrdup(buffer, GFP_KERNEL); + ts->input_dev->name = input_dev_name; + return 0; +} + +int sec_mmi_data_init(struct sec_ts_data *ts, bool enable) +{ + int ret; + + if (enable) { + ret = ts_mmi_dev_register(&ts->client->dev, &sec_ts_mmi_methods); + if (ret) { + dev_err(&ts->client->dev, "Failed to register ts mmi\n"); + return ret; + } + + /* initialize class imported methods */ + ts->imports = &sec_ts_mmi_methods.exports; + + if (ts->fw_invalid == false) { + sec_mmi_enable_touch(ts); + } else /* stuck in BL mode, update productinfo to report 'se77c' */ + ts->device_id[3] = 0x7C; + + sec_mmi_print_version(ts); + sec_mmi_class_fname(ts); + + /*initialize value*/ + if (ts->plat_data->suppression_ctrl) { + ret = ts->sec_ts_i2c_read(ts, SEC_TS_GRIP_SUPPRESSION_INFO, + &ts->suppression_data, sizeof(ts->suppression_data)); + if (ret < 0) + dev_err(&ts->client->dev, "%s: failed to write suppression info (%d)\n", + __func__, ret); + } + + if(ts->plat_data->pill_region_ctrl) { + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_PILL_REGION, + ts->pill_region_data, sizeof(ts->pill_region_data)); + if (ret < 0) + dev_err(&ts->client->dev, "%s: failed to write pill region (%d)\n", + __func__, ret); + } + + if(ts->plat_data->hold_distance_ctrl) { + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_HOLD_DISTANCE, + &ts->hold_distance_data, sizeof(ts->hold_distance_data)); + if (ret < 0) + dev_err(&ts->client->dev, "%s: failed to write hold distance (%d)\n", + __func__, ret); + } + + if(ts->plat_data->gs_distance_ctrl) { + ret = ts->sec_ts_i2c_read(ts, SEC_TS_CMD_GRIP_SUPP_AREA, + &ts->gs_distance_data, sizeof(ts->gs_distance_data)); + if (ret < 0) + dev_err(&ts->client->dev, "%s: failed to write grip supp distance (%d)\n", + __func__, ret); + + if (ts->gs_distance_data == 0xFF || ret < 0) { + dev_err(&ts->client->dev, "%s: previous firmware dose not support gs-distance, set default value\n", + __func__); + ts->gs_distance_data = 0x1E; + } + } + + if (ts->plat_data->hold_grip_ctrl) + ts->hold_grip_data = SEC_HOLD_GRIP_NOGRIP; + } else + ts_mmi_dev_unregister(&ts->client->dev); + + return 0; +} diff --git a/drivers/input/touchscreen/sec_mmi/sec_ts_only_vendor.c b/drivers/input/touchscreen/sec_mmi/sec_ts_only_vendor.c new file mode 100644 index 000000000000..7a5d53fa54f5 --- /dev/null +++ b/drivers/input/touchscreen/sec_mmi/sec_ts_only_vendor.c @@ -0,0 +1,300 @@ +/* drivers/input/touchscreen/sec_ts_fw.c + * + * Copyright (C) 2015 Samsung Electronics Co., Ltd. + * http://www.samsungsemi.com/ + * + * Core file for Samsung TSC driver + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +/*#include */ + +#include "sec_ts.h" + +u8 lv1cmd; +u8 *read_lv1_buff; +static int lv1_readsize; +static int lv1_readremain; +static int lv1_readoffset; + +static ssize_t sec_ts_regreadsize_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static inline ssize_t sec_ts_store_error(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +static ssize_t sec_ts_enter_recovery_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t sec_ts_gesture_status_show(struct device *dev, + struct device_attribute *attr, char *buf); +static inline ssize_t sec_ts_show_error(struct device *dev, + struct device_attribute *attr, char *buf); + +static DEVICE_ATTR(sec_ts_reg, (S_IWUSR | S_IWGRP), NULL, sec_ts_reg_store); +static DEVICE_ATTR(sec_ts_regreadsize, (S_IWUSR | S_IWGRP), NULL, sec_ts_regreadsize_store); +static DEVICE_ATTR(sec_ts_enter_recovery, (S_IWUSR | S_IWGRP), NULL, sec_ts_enter_recovery_store); +static DEVICE_ATTR(sec_ts_regread, S_IRUGO, sec_ts_regread_show, NULL); +static DEVICE_ATTR(sec_ts_gesture_status, S_IRUGO, sec_ts_gesture_status_show, NULL); + +static struct attribute *cmd_attributes[] = { + &dev_attr_sec_ts_reg.attr, + &dev_attr_sec_ts_regreadsize.attr, + &dev_attr_sec_ts_enter_recovery.attr, + &dev_attr_sec_ts_regread.attr, + &dev_attr_sec_ts_gesture_status.attr, + NULL, +}; + +static struct attribute_group cmd_attr_group = { + .attrs = cmd_attributes, +}; + +/* for debugging--------------------------------------------------------------------------------------*/ +void sec_ts_lv1_params(u8 cmd, int size) +{ + lv1cmd = cmd; + lv1_readsize = size; + lv1_readoffset = 0; + lv1_readremain = 0; +} + +ssize_t sec_ts_reg_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_info(true, &ts->client->dev, "%s: Power off state\n", __func__); + return -EIO; + } + + if (size > 0) + ts->sec_ts_i2c_write_burst(ts, (u8 *)buf, size); + + input_info(true, &ts->client->dev, "%s: 0x%x, 0x%x, size %d\n", __func__, buf[0], buf[1], (int)size); + return size; +} + +ssize_t sec_ts_regread_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + int ret; + int length; + int remain; + int offset; + + if (ts->power_status == SEC_TS_STATE_POWER_OFF) { + input_err(true, &ts->client->dev, "%s: Power off state\n", __func__); + return -EIO; + } + + disable_irq(ts->client->irq); + + mutex_lock(&ts->device_mutex); + + read_lv1_buff = kzalloc(lv1_readsize, GFP_KERNEL); + if (!read_lv1_buff) + goto malloc_err; + + remain = lv1_readsize; + offset = 0; + do { + if (remain >= ts->i2c_burstmax) + length = ts->i2c_burstmax; + else + length = remain; + + if (offset == 0) + ret = ts->sec_ts_i2c_read(ts, lv1cmd, &read_lv1_buff[offset], length); + else + ret = ts->sec_ts_i2c_read_bulk (ts, &read_lv1_buff[offset], length); + + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: i2c read %x command, remain =%d\n", __func__, lv1cmd, remain); + goto i2c_err; + } + + remain -= length; + offset += length; + } while (remain > 0); + + input_info(true, &ts->client->dev, "%s: lv1_readsize = %d\n", __func__, lv1_readsize); + memcpy(buf, read_lv1_buff + lv1_readoffset, lv1_readsize); + +i2c_err: + kfree(read_lv1_buff); +malloc_err: + mutex_unlock(&ts->device_mutex); + lv1_readremain = 0; + enable_irq(ts->client->irq); + + return lv1_readsize; +} + +static ssize_t sec_ts_gesture_status_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + mutex_lock(&ts->device_mutex); + memcpy(buf, ts->gesture_status, sizeof(ts->gesture_status)); + input_info(true, &ts->client->dev, + "%s: GESTURE STATUS %x %x %x %x %x %x\n", __func__, + ts->gesture_status[0], ts->gesture_status[1], ts->gesture_status[2], + ts->gesture_status[3], ts->gesture_status[4], ts->gesture_status[5]); + mutex_unlock(&ts->device_mutex); + + return sizeof(ts->gesture_status); +} + +static ssize_t sec_ts_regreadsize_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + mutex_lock(&ts->device_mutex); + + lv1cmd = buf[0]; + lv1_readsize = ((unsigned int)buf[4] << 24) | + ((unsigned int)buf[3] << 16) | ((unsigned int) buf[2] << 8) | ((unsigned int)buf[1] << 0); + lv1_readoffset = 0; + lv1_readremain = 0; + + mutex_unlock(&ts->device_mutex); + + return size; +} + +static ssize_t sec_ts_enter_recovery_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + struct sec_ts_plat_data *pdata = ts->plat_data; + int ret; + unsigned long on; + + ret = kstrtoul(buf, 10, &on); + if (ret != 0) { + input_err(true, &ts->client->dev, "%s: failed to read:%d\n", + __func__, ret); + return -EINVAL; + } + + if (on == 1) { + disable_irq(ts->client->irq); + gpio_free(pdata->irq_gpio); + + input_info(true, &ts->client->dev, "%s: gpio free\n", __func__); + if (gpio_is_valid(pdata->irq_gpio)) { + ret = gpio_request_one(pdata->irq_gpio, GPIOF_OUT_INIT_LOW, "sec,tsp_int"); + input_info(true, &ts->client->dev, "%s: gpio request one\n", __func__); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: Unable to request tsp_int [%d]: %d\n", __func__, pdata->irq_gpio, ret); + } else { + input_err(true, &ts->client->dev, "%s: Failed to get irq gpio\n", __func__); + return -EINVAL; + } + + pdata->power(ts, false); + sec_ts_delay(100); + pdata->power(ts, true); + } else { + gpio_free(pdata->irq_gpio); + + if (gpio_is_valid(pdata->irq_gpio)) { + ret = gpio_request_one(pdata->irq_gpio, GPIOF_DIR_IN, "sec,tsp_int"); + if (ret) { + input_err(true, &ts->client->dev, "%s: Unable to request tsp_int [%d]\n", __func__, pdata->irq_gpio); + return -EINVAL; + } + } else { + input_err(true, &ts->client->dev, "%s: Failed to get irq gpio\n", __func__); + return -EINVAL; + } + + pdata->power(ts, false); + sec_ts_delay(500); + pdata->power(ts, true); + sec_ts_delay(500); + + /* AFE Calibration */ + ret = ts->sec_ts_i2c_write(ts, SEC_TS_CMD_CALIBRATION_AMBIENT, NULL, 0); + if (ret < 0) + input_err(true, &ts->client->dev, "%s: fail to write AFE_CAL\n", __func__); + + sec_ts_delay(1000); + enable_irq(ts->client->irq); + } + + sec_ts_integrity_check(ts); + + return size; +} + +static inline ssize_t sec_ts_show_error(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + input_err(true, &ts->client->dev, "%s: read only function, %s\n", __func__, attr->attr.name); + return -EPERM; +} + +static inline ssize_t sec_ts_store_error(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sec_ts_data *ts = dev_get_drvdata(dev); + + input_err(true, &ts->client->dev, "%s: write only function, %s\n", __func__, attr->attr.name); + return -EPERM; +} + +int sec_ts_raw_device_init(struct sec_ts_data *ts) +{ + int ret; + + struct class *sec_class = NULL; + + sec_class = class_create(THIS_MODULE, "sec"); + ret = IS_ERR_OR_NULL(sec_class); + if (ret) { + input_err(true, &ts->client->dev, "%s: fail - class_create\n", __func__); + return ret; + } + + ts->sec.sec_class = sec_class; + ts->dev = device_create(sec_class, NULL, 0, ts, "sec_ts"); + + ret = IS_ERR(ts->dev); + if (ret) { + input_err(true, &ts->client->dev, "%s: fail - device_create\n", __func__); + return ret; + } + + ret = sysfs_create_group(&ts->dev->kobj, &cmd_attr_group); + if (ret < 0) { + input_err(true, &ts->client->dev, "%s: fail - sysfs_create_group\n", __func__); + goto err_sysfs; + } + + return ret; +err_sysfs: + input_err(true, &ts->client->dev, "%s: fail\n", __func__); + return ret; +} diff --git a/drivers/input/touchscreen/stmicro_mmi/Android.mk b/drivers/input/touchscreen/stmicro_mmi/Android.mk new file mode 100644 index 000000000000..dc83cba172eb --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/Android.mk @@ -0,0 +1,29 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DRM_PANEL_NOTIFICATIONS),true) + KBUILD_OPTIONS += CONFIG_DRM_PANEL_NOTIFICATIONS=y +endif + +ifeq ($(DRM_PANEL_EVENT_NOTIFICATIONS),true) + KBUILD_OPTIONS += CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS=y +endif + +ifeq ($(ST_LIMIT_USE_SUPPLIER),true) + KBUILD_OPTIONS += CONFIG_ST_LIMIT_USE_SUPPLIER=y + ifeq ($(TOUCHCLASS_MMI_MULTIWAY_UPDATE_FW),true) + KBUILD_OPTIONS += CONFIG_TOUCHCLASS_MMI_MULTIWAY_UPDATE_FW=y + endif +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := stmicro_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/mmi_info.ko +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KBUILD_OPTIONS += CONFIG_INPUT_TOUCHSCREEN_MMI=y + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +endif +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/stmicro_mmi/Kbuild b/drivers/input/touchscreen/stmicro_mmi/Kbuild new file mode 100644 index 000000000000..409745f6c512 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/Kbuild @@ -0,0 +1,41 @@ +# add -Wall to try to catch everything we canss +# EXTRA_CFLAGS += -Wallss +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/stmicro_mmi + +obj-m := stmicro_mmi.o +stmicro_mmi-objs := fts.o fts_proc.o +stmicro_mmi-objs += fts_lib/ftsCompensation.o +stmicro_mmi-objs += fts_lib/ftsCore.o +stmicro_mmi-objs += fts_lib/ftsError.o +stmicro_mmi-objs += fts_lib/ftsFlash.o +stmicro_mmi-objs += fts_lib/ftsFrame.o +stmicro_mmi-objs += fts_lib/ftsGesture.o +stmicro_mmi-objs += fts_lib/ftsIO.o +stmicro_mmi-objs += fts_lib/ftsTest.o +stmicro_mmi-objs += fts_lib/ftsTime.o +stmicro_mmi-objs += fts_lib/ftsTool.o + +ifneq ($(filter m y,$(CONFIG_DRM_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_NOTIFICATIONS +endif + +ifneq ($(filter m y, $(CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_EVENT_NOTIFICATIONS +endif + +ifneq ($(filter m y,$(CONFIG_ST_LIMIT_USE_SUPPLIER)),) + EXTRA_CFLAGS += -DCONFIG_ST_LIMIT_USE_SUPPLIER +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI + stmicro_mmi-objs += fts_mmi_class.o + + ifneq ($(filter m y, $(CONFIG_TOUCHCLASS_MMI_MULTIWAY_UPDATE_FW)),) + EXTRA_CFLAGS += -DTS_MMI_TOUCH_MULTIWAY_UPDATE_FW + endif +endif + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/stmicro_mmi/Kconfig b/drivers/input/touchscreen/stmicro_mmi/Kconfig new file mode 100644 index 000000000000..296bbc65a819 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/Kconfig @@ -0,0 +1,13 @@ +# +# STmicro TOUCH driver configuration +# + +config TOUCHSCREEN_FTS_TS + tristate "STmicro Touchscreen" + depends on I2C + help + Say Y here if you want support for FTS touchscreen controllers. + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called stmicro_mmi. diff --git a/drivers/input/touchscreen/stmicro_mmi/Makefile b/drivers/input/touchscreen/stmicro_mmi/Makefile new file mode 100644 index 000000000000..527323a74e8b --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/Makefile @@ -0,0 +1,17 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(M)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(M)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/stmicro_mmi/fts.c b/drivers/input/touchscreen/stmicro_mmi/fts.c new file mode 100644 index 000000000000..55c00309035b --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts.c @@ -0,0 +1,4581 @@ +/* + * fts.c + * + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2016, STMicroelectronics Limited. + * Authors: AMG(Analog Mems Group) + * + * marco.cali@st.com + * + * 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. + * + * THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE + * PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT. + * AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, + * INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM + * THE + * CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING + * INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + * THIS SOFTWARE IS SPECIFICALLY DESIGNED FOR EXCLUSIVE USE WITH ST PARTS. + */ + + +/*! + * \file fts.c + * \brief It is the main file which contains all the most important functions + * generally used by a device driver the driver + */ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef KERNEL_ABOVE_2_6_38 +#include +#endif + + +#include "fts.h" +#include "fts_lib/ftsCompensation.h" +#include "fts_lib/ftsCore.h" +#include "fts_lib/ftsIO.h" +#include "fts_lib/ftsError.h" +#include "fts_lib/ftsFlash.h" +#include "fts_lib/ftsFrame.h" +#include "fts_lib/ftsGesture.h" +#include "fts_lib/ftsTest.h" +#include "fts_lib/ftsTime.h" +#include "fts_lib/ftsTool.h" + +#include "fts_mmi.h" +#include + +/** + * Event handler installer helpers + */ +#define event_id(_e) (EVT_ID_##_e >> 4) +#define handler_name(_h) fts_##_h##_event_handler + +#define install_handler(_i, _evt, _hnd) \ + (_i->event_dispatch_table[event_id(_evt)] = handler_name(_hnd)) + + + +#ifdef KERNEL_ABOVE_2_6_38 +#define TYPE_B_PROTOCOL +#endif + + +extern SysInfo systemInfo; +extern TestToDo tests; +#ifdef GESTURE_MODE +extern struct mutex gestureMask_mutex; +#endif + +char tag[8] = "[ FTS ]\0"; +char fts_ts_phys[64]; /* /< buffer which store the input device name assigned + * by the kernel */ + +static u32 typeOfComand[CMD_STR_LEN] = { 0 }; /* /< buffer used to store the + * command sent from the MP + * device file node */ +static int numberParameters; /* /< number of parameter passed through the MP + * device file node */ +#ifdef USE_ONE_FILE_NODE +static int feature_feasibility = ERROR_OP_NOT_ALLOW; +#endif +#ifdef GESTURE_MODE +static u8 mask[GESTURE_MASK_SIZE + 2]; +extern u16 gesture_coordinates_x[GESTURE_MAX_COORDS_PAIRS_REPORT]; +extern u16 gesture_coordinates_y[GESTURE_MAX_COORDS_PAIRS_REPORT]; +extern int gesture_coords_reported; +extern struct mutex gestureMask_mutex; +#endif + +#ifdef PHONE_KEY +static u8 key_mask = 0x00; /* /< store the last update of the key mask + * published by the IC */ +#endif + + + + +static int fts_mode_handler(struct fts_ts_info *info, int force); + + +/** + * Release all the touches in the linux input subsystem + * @param info pointer to fts_ts_info which contains info about the device and + * its hw setup + */ +void release_all_touches(struct fts_ts_info *info) +{ + unsigned int type = MT_TOOL_FINGER; + int i; + + for (i = 0; i < TOUCH_ID_MAX; i++) { +#ifdef STYLUS_MODE + if (test_bit(i, &info->stylus_id)) + type = MT_TOOL_PEN; + else + type = MT_TOOL_FINGER; +#endif + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, type, 0); + /* input_report_abs(info->input_dev, ABS_MT_TRACKING_ID, -1); */ + } + input_report_key(info->input_dev, BTN_TOUCH, 0); + input_sync(info->input_dev); + info->touch_id = 0; + info->touch_count = 0; +#ifdef STYLUS_MODE + info->stylus_id = 0; +#endif +} + + +/** + * @defgroup file_nodes Driver File Nodes + * Driver publish a series of file nodes used to provide several utilities + * to the host and give him access to different API. + * @{ + */ + +/** + * @defgroup device_file_nodes Device File Nodes + * @ingroup file_nodes + * Device File Nodes \n + * There are several file nodes that are associated to the device and which + * are designed to be used by the host to enable/disable features or trigger + * some system specific actions \n + * Usually their final path depend on the definition of device tree node of + * the IC (e.g /sys/devices/soc.0/f9928000.i2c/i2c-6/6-0049) + * @{ + */ +/***************************************** FW UPGGRADE + * ***************************************************/ + +/** + * File node function to Update firmware from shell \n + * echo path_to_fw X Y > fwupdate perform a fw update \n + * where: \n + * path_to_fw = file name or path of the the FW to burn, if "NULL" the default + * approach selected in the driver will be used\n + * X = 0/1 to force the FW update whichever fw_version and config_id; + * 0=perform a fw update only if the fw in the file is newer than the fw in the + * chip \n + * Y = 0/1 keep the initialization data; 0 = will erase the initialization data + * from flash, 1 = will keep the initialization data + * the string returned in the shell is made up as follow: \n + * { = start byte \n + * X1X2X3X4 = 4 bytes in HEX format which represent an error code (00000000 no + * error) \n + * } = end byte + */ +static ssize_t fts_fwupdate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret, mode[2]; + char path[100]; + struct fts_ts_info *info = dev_get_drvdata(dev); + + /* default(if not specified by user) set force = 0 and keep_cx to 1 */ + mode[0] = 0; + mode[1] = 1; + + /* reading out firmware upgrade parameters */ + if (sscanf(buf, "%100s %d %d", path, &mode[0], &mode[1]) >= 1) { + logError(1, "%s fts_fwupdate_store: file = %s, force = %d, keep_cx = %d\n", + tag, path, mode[0], mode[1]); + + + ret = flashProcedure(path, mode[0], mode[1]); + + info->fwupdate_stat = ret; + + if (ret < OK) + logError(1, "%s %s Unable to upgrade firmware! ERROR %08X\n", + tag, __func__, ret); + } else + logError(1, "%s %s Wrong number of parameters! ERROR %08X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + + return count; +} + +static ssize_t fts_fwupdate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_info *info = dev_get_drvdata(dev); + + /* fwupdate_stat: ERROR code Returned by flashProcedure. */ + return snprintf(buf, 14, "{ %08X }\n", info->fwupdate_stat); +} + + +/***************************************** UTILITIES + * (current fw_ver/conf_id, active mode, file fw_ver/conf_id) + ***************************************************/ +/** + * File node to show on terminal external release version in Little Endian \n + * (first the less significant byte) \n + * cat appid show the external release version of the FW running in the IC + */ +static ssize_t fts_appid_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int error; + char temp[100]; + + error = snprintf(buf, PAGE_SIZE, "%s\n", printHex("EXT Release = ", + systemInfo. + u8_releaseInfo, + EXTERNAL_RELEASE_INFO_SIZE, + temp)); + + return error; +} + +/** + * File node to show on terminal the mode that is active on the IC \n + * cat mode_active to show the bitmask which indicate + * the modes/features which are running on the IC in a specific instant of time + * the string returned in the shell is made up as follow: \n + * { = start byte \n + * X1 = 1 byte in HEX format which represent the actual running scan mode + * (@link scan_opt Scan Mode Options @endlink) \n + * X2 = 1 byte in HEX format which represent the bitmask on which is running + * the actual scan mode \n + * X3X4 = 2 bytes in HEX format which represent a bitmask of the features that + * are enabled at this moment (@link feat_opt Feature Selection Options + * @endlink) \n + * } = end byte + * @see fts_mode_handler() + */ +static ssize_t fts_mode_active_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_info *info = dev_get_drvdata(dev); + + logError(1, "%s Current mode active = %08X\n", tag, info->mode); + return snprintf(buf, 14, "{ %08X }\n", info->mode); +} + +/** + * File node to show the fw_ver and config_id of the FW file + * cat fw_file_test show on the kernel log external release + * of the FW stored in the fw file/header file + */ +static ssize_t fts_fw_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + Firmware fw; + int ret; + char temp[100] = { 0 }; + + fw.data = NULL; + ret = readFwFile(PATH_FILE_FW, &fw, 0); + + if (ret < OK) + logError(1, "%s Error during reading FW file! ERROR %08X\n", + tag, ret); + else + logError(1, "%s %s, size = %d bytes\n", tag, printHex( + "EXT Release = ", systemInfo.u8_releaseInfo, + EXTERNAL_RELEASE_INFO_SIZE, temp), + fw.data_size); + + kfree(fw.data); + return 0; +} + + +#if 0 +/** + * File node to obtain and show strength frame + * cat strength_frame to obtain strength data \n + * the string returned in the shell is made up as follow: \n + * { = start byte \n + * X1X2X3X4 = 4 bytes in HEX format which represent an error code (00000000 no + *error) \n + * **** if error code is all 0s **** \n + * FF = 1 byte in HEX format number of rows \n + * SS = 1 byte in HEX format number of columns \n + * N1, ... = the decimal value of each node separated by a coma \n + * ********************************* \n + * } = end byte + */ +static ssize_t fts_strength_frame_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + MutualSenseFrame frame; + int res, count, j, size = (6 * 2) + 1, index = 0; + char *all_strbuff = NULL; + /* char buff[CMD_STR_LEN] = {0}; */ + /* struct i2c_client *client = to_i2c_client(dev); */ + struct fts_ts_info *info = dev_get_drvdata(dev); + + frame.node_data = NULL; + + res = fts_disableInterrupt(); + if (res < OK) + goto END; + + res = senseOn(); + if (res < OK) { + logError(1, "%s %s: could not start scanning! ERROR %08X\n", + tag, __func__, res); + goto END; + } + msleep(WAIT_FOR_FRESH_FRAMES); + res = senseOff(); + if (res < OK) { + logError(1, "%s %s: could not finish scanning! ERROR %08X\n", + tag, __func__, res); + goto END; + } + + msleep(WAIT_AFTER_SENSEOFF); + flushFIFO(); + + res = getMSFrame3(MS_STRENGTH, &frame); + if (res < OK) { + logError(1, "%s %s: could not get the frame! ERROR %08X\n", + tag, __func__, res); + goto END; + } else { + size += (res * 6); + logError(0, "%s The frame size is %d words\n", tag, res); + res = OK; + print_frame_short("MS Strength frame =", array1dTo2d_short( + frame.node_data, frame.node_data_size, + frame.header.sense_node), + frame.header.force_node, + frame.header.sense_node); + } + +END: + flushFIFO(); + release_all_touches(info); + fts_mode_handler(info, 1); + + all_strbuff = (char *)kzalloc(size * sizeof(char), GFP_KERNEL); + + if (all_strbuff != NULL) { + snprintf(&all_strbuff[index], 11, "{ %08X", res); + + index += 10; + + if (res >= OK) { + snprintf(&all_strbuff[index], 3, "%02X", + (u8)frame.header.force_node); + index += 2; + snprintf(&all_strbuff[index], 3, "%02X", + (u8)frame.header.sense_node); + + index += 2; + + for (j = 0; j < frame.node_data_size; j++) { + snprintf(&all_strbuff[index], 10, "%d,%n", + frame.node_data[j], &count); + index += count; + } + + kfree(frame.node_data); + } + + snprintf(&all_strbuff[index], 3, " }"); + index += 2; + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, + ERROR_ALLOC); + + fts_enableInterrupt(); + return count; +} +#endif + +/***************************************** FEATURES + * ***************************************************/ + +/* TODO: edit this function according to the features policy to allow during + * the screen on/off, following is shown an example but check always with ST + * for more details */ +/** + * Check if there is any conflict in enable/disable a particular feature + * considering the features already enabled and running + * @param info pointer to fts_ts_info which contains info about the device + * and its hw setup + * @param feature code of the feature that want to be tested + * @return OK if is possible to enable/disable feature, ERROR_OP_NOT_ALLOW + * in case of any other conflict + */ +int check_feature_feasibility(struct fts_ts_info *info, unsigned int feature) +{ + int res = OK; + +/* Example based on the status of the screen and on the feature + * that is trying to enable */ + /*res=ERROR_OP_NOT_ALLOW; + * if(info->resume_bit ==0){ + * switch(feature){ + #ifdef GESTURE_MODE + * case FEAT_SEL_GESTURE: + * res = OK; + * break; + #endif + * default: + * logError(1,"%s %s: Feature not allowed in this + * operating mode! ERROR %08X\n", + * tag,__func__,res); + * break; + * + * } + * }else{ + * switch(feature){ + #ifdef GESTURE_MODE + * case FEAT_SEL_GESTURE: + #endif + * case FEAT__SEL_GLOVE: // glove mode can only activate + *during sense on + * res = OK; + * break; + * + * default: + * logError(1,"%s %s: Feature not allowed in this + *operating mode! ERROR %08X\n",tag,__func__,res); + * break; + * + * } + * }*/ + + +/* Example based only on the feature that is going to be activated */ + switch (feature) { + case FEAT_SEL_GESTURE: + if (info->cover_enabled == 1) { + res = ERROR_OP_NOT_ALLOW; + logError(1, + "%s %s: Feature not allowed when in Cover mode! ERROR %08X\n", + tag, __func__, res); + /* for example here can be placed a code for disabling + * the cover mode when gesture is activated */ + } + break; + + case FEAT_SEL_GLOVE: + if (info->gesture_enabled == 1) { + res = ERROR_OP_NOT_ALLOW; + logError(1, + "%s %s: Feature not allowed when Gestures enabled! ERROR %08X\n", + tag, __func__, res); + /* for example here can be placed a code for disabling + * the gesture mode when cover is activated + * (that means that cover mode has + * an higher priority on gesture mode) */ + } + break; + + default: + logError(1, "%s %s: Feature Allowed!\n", tag, __func__); + } + + return res; +} + +#ifdef USE_ONE_FILE_NODE +/** + * File node to enable some feature + * echo XX 00/01 > feature_enable to enable/disable XX + * (possible values @link feat_opt Feature Selection Options @endlink) feature + * cat feature_enable to show the result of enabling/disabling process + * echo XX 01/00 > feature_enable; cat feature_enable + * to perform both actions stated before in just one call \n + * the string returned in the shell is made up as follow: \n + * { = start byte \n + * X1X2X3X4 = 4 bytes in HEX format which represent an error code (00000000 = + * no error) \n + * } = end byte + */ +static ssize_t fts_feature_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct fts_ts_info *info = dev_get_drvdata(dev); + char *p = (char *)buf; + unsigned int temp, temp2; + int res = OK; + + if ((count - 2 + 1) / 3 != 1) + logError(1, + "%s fts_feature_enable: Number of parameter wrong! %d > %d\n", + tag, (count - 2 + 1) / 3, 1); + else { + if (sscanf(p, "%02X %02X ", &temp, &temp2) == 2) { + p += 3; + res = check_feature_feasibility(info, temp); + if (res >= OK) { + switch (temp) { + #ifdef GESTURE_MODE + case FEAT_SEL_GESTURE: + info->gesture_enabled = temp2; + logError(1, + "%s fts_feature_enable: Gesture Enabled = %d\n", + tag, info->gesture_enabled); + break; + #endif + + #ifdef GLOVE_MODE + case FEAT_SEL_GLOVE: + info->glove_enabled = temp2; + logError(1, + "%s fts_feature_enable: Glove Enabled = %d\n", + tag, info->glove_enabled); + + break; + #endif + + #ifdef STYLUS_MODE + case FEAT_SEL_STYLUS: + info->stylus_enabled = temp2; + logError(1, + "%s fts_feature_enable: Stylus Enabled = %d\n", + tag, info->stylus_enabled); + + break; + #endif + + #ifdef COVER_MODE + case FEAT_SEL_COVER: + info->cover_enabled = temp2; + logError(1, + "%s fts_feature_enable: Cover Enabled = %d\n", + tag, info->cover_enabled); + + break; + #endif + + #ifdef CHARGER_MODE + case FEAT_SEL_CHARGER: + info->charger_enabled = temp2; + logError(1, + "%s fts_feature_enable: Charger Enabled = %d\n", + tag, info->charger_enabled); + + break; + #endif + + #ifdef GRIP_MODE + case FEAT_SEL_GRIP: + info->grip_enabled = temp2; + logError(1, + "%s fts_feature_enable: Grip Enabled = %d\n", + tag, info->grip_enabled); + + break; + #endif + + + + default: + logError(1, + "%s fts_feature_enable: Feature %08X not valid! ERROR %08X\n", + tag, temp, ERROR_OP_NOT_ALLOW); + res = ERROR_OP_NOT_ALLOW; + } + feature_feasibility = res; + } + + if (feature_feasibility >= OK) + feature_feasibility = fts_mode_handler(info, 1); + else + logError(1, + "%s %s: Call echo XX 00/01 > feature_enable with a correct feature value (XX)! ERROR %08X\n", + tag, __func__, res); + } else + logError(1, "%s %s: Error when reading with sscanf!\n", + tag, __func__); + + + } + return count; +} + + + +static ssize_t fts_feature_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int size = (6 * 2) + 1, index = 0; + u8 *all_strbuff = NULL; + int count = 0; + + + if (feature_feasibility < OK) + logError(1, + "%s %s: Call before echo XX 00/01 > feature_enable with a correct feature value (XX)! ERROR %08X\n", + tag, __func__, feature_feasibility); + + + all_strbuff = (u8 *)kzalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + index += snprintf(&all_strbuff[index], 13, "{ %08X }", + feature_feasibility); + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else + logError(1, + "%s fts_feature_enable_show: Unable to allocate all_strbuff! ERROR %08X\n", + tag, ERROR_ALLOC); + + feature_feasibility = ERROR_OP_NOT_ALLOW; + return count; +} + +#else + + +#ifdef GRIP_MODE +/** + * File node to set the grip mode + * echo 01/00 > grip_mode to enable/disable glove mode \n + * cat grip_mode to show the status of the grip_enabled switch \n + * echo 01/00 > grip_mode; cat grip_mode to enable/disable grip + *mode + * and see the switch status in just one call \n + * the string returned in the shell is made up as follow: \n + * { = start byte \n + * X1X2X3X4 = 4 bytes in HEX format which represent the value + * info->grip_enabled (1 = enabled; 0= disabled) \n + * } = end byte + */ +static ssize_t fts_grip_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int size = (6 * 2) + 1, index = 0; + u8 *all_strbuff = NULL; + int count = 0; + struct fts_ts_info *info = dev_get_drvdata(dev); + + logError(0, "%s %s: grip_enabled = %d\n", tag, __func__, + info->grip_enabled); + + all_strbuff = (u8 *)kzalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + index += snprintf(&all_strbuff[index], 13, "{ %08X }", + info->grip_enabled); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, + __func__, ERROR_ALLOC); + + return count; +} + + +static ssize_t fts_grip_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct fts_ts_info *info = dev_get_drvdata(dev); + + + /* in case of a different elaboration of the input, just modify this + * initial part of the code according to customer needs */ + if ((count + 1) / 3 != 1) + logError(1, + "%s %s: Number of bytes of parameter wrong! %d != %d byte\n", + tag, __func__, (int)((count + 1) / 3), 1); + else { + if (sscanf(p, "%02X ", &temp) == 1) { + p += 3; + +/* standard code that should be always used when a feature is enabled! */ +/* first step : check if the wanted feature can be enabled */ +/* second step: call fts_mode_handler to actually enable it */ +/* NOTE: Disabling a feature is always allowed by default */ + res = check_feature_feasibility(info, FEAT_SEL_GRIP); + if (res >= OK || temp == FEAT_DISABLE) { + info->grip_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) + logError(1, + "%s %s: Error during fts_mode_handler! ERROR %08X\n", + tag, __func__, res); + } + } else + logError(1, "%s %s: Error when reading with sscanf!\n", + tag, __func__); + } + + return count; +} +#endif + +#ifdef CHARGER_MODE +/** + * File node to set the glove mode + * echo XX/00 > charger_mode to value >0 to enable + * (possible values: @link charger_opt Charger Options @endlink), + * 00 to disable charger mode \n + * cat charger_mode to show the status of the charger_enabled switch \n + * echo 01/00 > charger_mode; cat charger_mode to enable/disable + * charger mode and see the switch status in just one call \n + * the string returned in the shell is made up as follow: \n + * { = start byte \n + * X1X2X3X4 = 4 bytes in HEX format which represent the value + * info->charger_enabled (>0 = enabled; 0= disabled) \n + * } = end byte + */ +static ssize_t fts_charger_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int size = (6 * 2) + 1, index = 0; + u8 *all_strbuff = NULL; + int count = 0; + struct fts_ts_info *info = dev_get_drvdata(dev); + + logError(0, "%s %s: charger_enabled = %d\n", tag, __func__, + info->charger_enabled); + + all_strbuff = (u8 *)kzalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + index += snprintf(&all_strbuff[index], 13, "{ %08X }", + info->charger_enabled); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, + __func__, ERROR_ALLOC); + + return count; +} + + +static ssize_t fts_charger_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct fts_ts_info *info = dev_get_drvdata(dev); + + +/* in case of a different elaboration of the input, just modify this + * initial part of the code according to customer needs */ + if ((count + 1) / 3 != 1) + logError(1, + "%s %s: Number of bytes of parameter wrong! %d != %d byte\n", + tag, __func__, (int)((count + 1) / 3), 1); + else { + if (sscanf(p, "%02X ", &temp) == 1) { + p += 3; + +/* standard code that should be always used when a feature is enabled! */ +/* first step : check if the wanted feature can be enabled */ +/* second step: call fts_mode_handler to actually enable it */ +/* NOTE: Disabling a feature is always allowed by default */ + res = check_feature_feasibility(info, FEAT_SEL_CHARGER); + if (res >= OK || temp == FEAT_DISABLE) { + info->charger_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) + logError(1, + "%s %s: Error during fts_mode_handler! ERROR %08X\n", + tag, __func__, res); + } + } else + logError(1, "%s %s: Error when reading with sscanf!\n", + tag, __func__); + + } + + return count; +} +#endif + +#ifdef GLOVE_MODE +/** + * File node to set the glove mode + * echo 01/00 > glove_mode to enable/disable glove mode \n + * cat glove_mode to show the status of the glove_enabled switch \n + * echo 01/00 > glove_mode; cat glove_mode to enable/disable glove mode and + * see the switch status in just one call \n + * the string returned in the shell is made up as follow: \n + * { = start byte \n + * X1X2X3X4 = 4 bytes in HEX format which represent the of value + * info->glove_enabled (1 = enabled; 0= disabled) \n + * } = end byte + */ +static ssize_t fts_glove_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int size = (6 * 2) + 1, index = 0; + u8 *all_strbuff = NULL; + int count = 0; + struct fts_ts_info *info = dev_get_drvdata(dev); + + logError(0, "%s %s: glove_enabled = %d\n", tag, __func__, + info->glove_enabled); + + all_strbuff = (u8 *)kzalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + index += snprintf(&all_strbuff[index], 13, "{ %08X }", + info->glove_enabled); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, + __func__, ERROR_ALLOC); + + return count; +} + + +static ssize_t fts_glove_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct fts_ts_info *info = dev_get_drvdata(dev); + + +/* in case of a different elaboration of the input, just modify this + * initial part of the code according to customer needs */ + if ((count + 1) / 3 != 1) + logError(1, + "%s %s: Number of bytes of parameter wrong! %d != %d byte\n", + tag, __func__, (int)((count + 1) / 3), 1); + else { + if (sscanf(p, "%02X ", &temp) == 1) { + p += 3; + +/* standard code that should be always used when a feature is enabled! */ +/* first step : check if the wanted feature can be enabled */ +/* second step: call fts_mode_handler to actually enable it */ +/* NOTE: Disabling a feature is always allowed by default */ + res = check_feature_feasibility(info, FEAT_SEL_GLOVE); + if (res >= OK || temp == FEAT_DISABLE) { + info->glove_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) + logError(1, + "%s %s: Error during fts_mode_handler! ERROR %08X\n", + tag, __func__, res); + } + } else + logError(1, "%s %s: Error when reading with sscanf!\n", + tag, __func__); + } + + return count; +} +#endif + + +#ifdef COVER_MODE +/* echo 01/00 > cover_mode to enable/disable cover mode */ +/* cat cover_mode to show the status of the cover_enabled switch + * (example output in the terminal = "AA00000001BB" if the switch is enabled) */ +/* echo 01/00 > cover_mode; cat cover_mode to enable/disable cover mode and + * see the switch status in just one call */ +/* NOTE: the cover can be handled also using a notifier, in this case the body + * of these functions should be copied in the notifier callback */ +/** + * File node to set the cover mode + * echo 01/00 > cover_mode to enable/disable cover mode \n + * cat cover_mode to show the status of the cover_enabled switch \n + * echo 01/00 > cover_mode; cat cover_mode to enable/disable cover mode + * and see the switch status in just one call \n + * the string returned in the shell is made up as follow: \n + * { = start byte \n + * X1X2X3X4 = 4 bytes in HEX format which is the value of info->cover_enabled + * (1 = enabled; 0= disabled)\n + * } = end byte \n + * NOTE: \n + * the cover can be handled also using a notifier, in this case the body of + * these functions should be copied in the notifier callback + */ +static ssize_t fts_cover_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int size = (6 * 2) + 1, index = 0; + u8 *all_strbuff = NULL; + int count = 0; + struct fts_ts_info *info = dev_get_drvdata(dev); + + logError(0, "%s %s: cover_enabled = %d\n", tag, __func__, + info->cover_enabled); + + all_strbuff = (u8 *)kzalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + index += snprintf(&all_strbuff[index], 13, "{ %08X }", + info->cover_enabled); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, + __func__, ERROR_ALLOC); + + return count; +} + + +static ssize_t fts_cover_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + int res; + struct fts_ts_info *info = dev_get_drvdata(dev); + + +/* in case of a different elaboration of the input, just modify this + * initial part of the code according to customer needs */ + if ((count + 1) / 3 != 1) + logError(1, + "%s %s: Number of bytes of parameter wrong! %d != %d byte\n", + tag, __func__, (int)((count + 1) / 3), 1); + else { + if (sscanf(p, "%02X ", &temp) == 1) { + p += 3; + +/* standard code that should be always used when a feature is enabled! */ +/* first step : check if the wanted feature can be enabled */ +/* second step: call fts_mode_handler to actually enable it */ +/* NOTE: Disabling a feature is always allowed by default */ + res = check_feature_feasibility(info, FEAT_SEL_COVER); + if (res >= OK || temp == FEAT_DISABLE) { + info->cover_enabled = temp; + res = fts_mode_handler(info, 1); + if (res < OK) + logError(1, + "%s %s: Error during fts_mode_handler! ERROR %08X\n", + tag, __func__, res); + } + } else + logError(1, "%s %s: Error when reading with sscanf!\n", + tag, __func__); + } + + return count; +} +#endif + +#ifdef STYLUS_MODE +/** + * File node to enable the stylus report + * echo 01/00 > stylus_mode to enable/disable stylus mode \n + * cat stylus_mode to show the status of the stylus_enabled switch \n + * echo 01/00 > stylus_mode; cat stylus_mode to enable/disable stylus mode + * and see the switch status in just one call \n + * the string returned in the shell is made up as follow: \n + * { = start byte \n + * X1X2X3X4 = 4 bytes in HEX format which is the value of info->stylus_enabled + * (1 = enabled; 0= disabled)\n + * } = end byte + */ +static ssize_t fts_stylus_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int size = (6 * 2) + 1, index = 0; + u8 *all_strbuff = NULL; + int count = 0; + struct fts_ts_info *info = dev_get_drvdata(dev); + + logError(0, "%s %s: stylus_enabled = %d\n", tag, __func__, + info->stylus_enabled); + + all_strbuff = (u8 *)kzalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + index += snprintf(&all_strbuff[index], 13, "{ %08X }", + info->stylus_enabled); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, + __func__, ERROR_ALLOC); + + return count; +} + + +static ssize_t fts_stylus_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + char *p = (char *)buf; + unsigned int temp; + struct fts_ts_info *info = dev_get_drvdata(dev); + + +/* in case of a different elaboration of the input, just modify this + * initial part of the code according to customer needs */ + if ((count + 1) / 3 != 1) + logError(1, + "%s %s: Number of bytes of parameter wrong! %d != %d byte\n", + tag, __func__, (int)((count + 1) / 3), 1); + else { + if (sscanf(p, "%02X ", &temp) == 1) { + p += 3; + info->stylus_enabled = temp; + } else + logError(1, "%s %s: Error when reading with sscanf!\n", + tag, __func__); + + } + + return count; +} +#endif + +#endif + +/***************************************** GESTURES + * ***************************************************/ +#ifdef GESTURE_MODE +#ifdef USE_GESTURE_MASK /* if this define is used, a gesture bit mask is used as + * method to select the gestures to enable/disable */ + +/** + * File node used by the host to set the gesture mask to enable or disable + * echo EE X1 X2 ~~ > gesture_mask set the gesture to disable/enable; + * EE = 00(disable) or 01(enable) \n + * X1 ~~ = gesture mask (example 06 00 ~~ 00 this gesture mask represents + * the gestures with ID = 1 and 2) can be specified + * from 1 to GESTURE_MASK_SIZE bytes, \n + * if less than GESTURE_MASK_SIZE bytes are passed as arguments, + * the omit bytes of the mask maintain the previous settings \n + * if one or more gestures is enabled the driver will automatically + * enable the gesture mode, If all the gestures are disabled the driver + * automatically will disable the gesture mode \n + * cat gesture_mask set inside the specified mask and return an error code + * for the operation \n + * the string returned in the shell is made up as follow: \n + * { = start byte \n + * X1X2X3X4 = 4 bytes in HEX format which represent an error code for enabling + * the mask (00000000 = no error)\n + * } = end byte \n\n + * if USE_GESTURE_MASK is not define the usage of the function become: \n\n + * echo EE X1 X2 ~~ > gesture_mask set the gesture to disable/enable; + * EE = 00(disable) or 01(enable) \n + * X1 ~~ = gesture IDs (example 01 02 05 represent the gestures with ID = 1, 2 + * and 5) + * there is no limit of the IDs passed as arguments, (@link gesture_opt Gesture + * IDs @endlink) \n + * if one or more gestures is enabled the driver will automatically enable + * the gesture mode. If all the gestures are disabled the driver automatically + * will disable the gesture mode. \n + * cat gesture_mask to show the status of the gesture enabled switch \n + * the string returned in the shell is made up as follow: \n + * { = start byte \n + * X1X2X3X4 = 4 bytes in HEX format which is the value of info->gesture_enabled + * (1 = enabled; 0= disabled)\n + * } = end byte + */ +static ssize_t fts_gesture_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int size = (6 * 2) + 1, index = 0; + u8 *all_strbuff = NULL; + int count = 0, res, temp; + struct fts_ts_info *info = dev_get_drvdata(dev); + + if (mask[0] == 0) { + res = ERROR_OP_NOT_ALLOW; + logError(1, + "%s %s: Call before echo enable/disable xx xx .... > gesture_mask with a correct number of parameters! ERROR %08X\n", + tag, __func__, res); + } else { + if (mask[1] == FEAT_ENABLE || mask[1] == FEAT_DISABLE) + res = updateGestureMask(&mask[2], mask[0], mask[1]); + else + res = ERROR_OP_NOT_ALLOW; + + if (res < OK) + logError(1, "%s fts_gesture_mask_store: ERROR %08X\n", + tag, res); + } + res |= check_feature_feasibility(info, FEAT_SEL_GESTURE); + temp = isAnyGestureActive(); + if (res >= OK || temp == FEAT_DISABLE) + info->gesture_enabled = temp; + + logError(1, "%s fts_gesture_mask_store: Gesture Enabled = %d\n", tag, + info->gesture_enabled); + + all_strbuff = (u8 *)kzalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + index += snprintf(&all_strbuff[index], 13, "{ %08X }", res); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else + logError(1, + "%s fts_gesture_mask_show: Unable to allocate all_strbuff! ERROR %08X\n", + tag, ERROR_ALLOC); + + mask[0] = 0; + return count; +} + + +static ssize_t fts_gesture_mask_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + char *p = (char *)buf; + int n; + unsigned int temp; + + if ((count + 1) / 3 > GESTURE_MASK_SIZE + 1) { + logError(1, + "%s fts_gesture_mask_store: Number of bytes of parameter wrong! %d > (enable/disable + %d )\n", + tag, (int)((count + 1) / 3), GESTURE_MASK_SIZE); + mask[0] = 0; + } else { + mask[0] = ((count + 1) / 3) - 1; + for (n = 1; n <= (count + 1) / 3; n++) { + if (sscanf(p, "%02X ", &temp) == 1) { + p += 3; + mask[n] = (u8)temp; + logError(1, "%s mask[%d] = %02X\n", + tag, n, mask[n]); + } else + logError(1, "%s %s: Error when reading with sscanf!\n", + tag, __func__); + + } + } + + return count; +} + +#else /* if this define is not used, to select the gestures to enable/disable + * are used the IDs of the gestures */ +/* echo EE X1 X2 ... > gesture_mask set the gesture to disable/enable; + * EE = 00(disable) or 01(enable); X1 ... = gesture IDs + * (example 01 02 05... represent the gestures with ID = 1, 2 and 5) + * there is no limit of the parameters that can be passed, + * of course the gesture IDs should be valid (all the valid IDs are listed in + * ftsGesture.h) */ +/* cat gesture_mask enable/disable the given gestures, if one or more + * gestures is enabled the driver will automatically enable the gesture mode. + * If all the gestures are disabled the driver automatically will disable the + * gesture mode. + * At the end an error code will be printed + * (example output in the terminal = "AA00000000BB" if there are no errors) */ +/* echo EE X1 X2 ... > gesture_mask; cat gesture_mask perform in one command + * both actions stated before */ +/** + * File node used by the host to set the gesture mask to enable or disable + * echo EE X1 X2 ~~ > gesture_mask set the gesture to disable/enable; + * EE = 00(disable) or 01(enable) \n + * X1 ~ = gesture IDs (example 01 02 05 represent the gestures with ID = 1, 2 + * and 5) + * there is no limit of the IDs passed as arguments, (@link gesture_opt Gesture + * IDs @endlink) \n + * if one or more gestures is enabled the driver will automatically enable + * the gesture mode, If all the gestures are disabled the driver automatically + * will disable the gesture mode \n + * cat gesture_mask to show the status of the gesture enabled switch \n + * the string returned in the shell is made up as follow: \n + * { = start byte \n + * X1X2X3X4 = 4 bytes in HEX format which is the value of info->gesture_enabled + * (1 = enabled; 0= disabled)\n + * } = end byte + */ +static ssize_t fts_gesture_mask_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int size = (6 * 2) + 1, index = 0; + u8 *all_strbuff = NULL; + int count = 0; + struct fts_ts_info *info = dev_get_drvdata(dev); + + logError(0, "%s fts_gesture_mask_show: gesture_enabled = %d\n", tag, + info->gesture_enabled); + + all_strbuff = (u8 *)kzalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + index += snprintf(&all_strbuff[index], 13, "{ %08X }", + info->gesture_enabled); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + } else + logError(1, + "%s fts_gesture_mask_show: Unable to allocate all_strbuff! ERROR %08X\n", + tag, ERROR_ALLOC); + + return count; +} + + +static ssize_t fts_gesture_mask_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + char *p = (char *)buf; + int n; + unsigned int temp; + int res; + struct fts_ts_info *info = dev_get_drvdata(dev); + + if ((count + 1) / 3 < 2 || (count + 1) / 3 > GESTURE_MASK_SIZE + 1) { + logError(1, + "%s fts_gesture_mask_store: Number of bytes of parameter wrong! %d < or > (enable/disable + at least one gestureID or max %d bytes)\n", + tag, (count + 1) / 3, GESTURE_MASK_SIZE); + mask[0] = 0; + } else { + memset(mask, 0, GESTURE_MASK_SIZE + 2); + mask[0] = ((count + 1) / 3) - 1; + if (sscanf(p, "%02X ", &temp) == 1) { + p += 3; + mask[1] = (u8)temp; + for (n = 1; n < (count + 1) / 3; n++) { + if (sscanf(p, "%02X ", &temp) == 1) { + p += 3; + fromIDtoMask((u8)temp, &mask[2], + GESTURE_MASK_SIZE); + } else { + logError(1, "%s %s: Error when reading the mask with sscanf!\n", + tag, __func__); + mask[0] = 0; + goto END; + } + } + + for (n = 0; n < GESTURE_MASK_SIZE + 2; n++) + logError(1, "%s mask[%d] = %02X\n", tag, n, + mask[n]); + } else { + logError(1, "%s %s: Error when reading with sscanf!\n", + tag, __func__); + mask[0] = 0; + } + + } + +END: + if (mask[0] == 0) { + res = ERROR_OP_NOT_ALLOW; + logError(1, + "%s %s: Call before echo enable/disable xx xx .... > gesture_mask with a correct number of parameters! ERROR %08X\n", + tag, __func__, res); + } else { + if (mask[1] == FEAT_ENABLE || mask[1] == FEAT_DISABLE) + res = updateGestureMask(&mask[2], mask[0], mask[1]); + else + res = ERROR_OP_NOT_ALLOW; + + if (res < OK) + logError(1, "%s fts_gesture_mask_store: ERROR %08X\n", + tag, res); + } + + res = check_feature_feasibility(info, FEAT_SEL_GESTURE); + temp = isAnyGestureActive(); + if (res >= OK || temp == FEAT_DISABLE) + info->gesture_enabled = temp; + res = fts_mode_handler(info, 0); + + return count; +} + + +#endif + + +/** + * File node to read the coordinates of the last gesture drawn by the user \n + * cat gesture_coordinates to obtain the gesture coordinates \n + * the string returned in the shell follow this up as follow: \n + * { = start byte \n + * X1X2X3X4 = 4 bytes in HEX format which represent an error code (00000000 = + *OK) \n + * \n if error code = 00000000 \n + * CC = 1 byte in HEX format number of coords (pair of x,y) returned \n + * XXiYYi ... = XXi 2 bytes in HEX format for x[i] and + * YYi 2 bytes in HEX format for y[i] (big endian) \n + * \n + * } = end byte + */ +static ssize_t fts_gesture_coordinates_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int size = (6 * 2) + 1, index = 0; + u8 *all_strbuff = NULL; + int count = 0, res, i = 0; + + logError(0, "%s %s: Getting gestures coordinates...\n", tag, __func__); + + if (gesture_coords_reported < OK) { + logError(1, "%s %s: invalid coordinates! ERROR %08X\n", tag, + __func__, gesture_coords_reported); + res = gesture_coords_reported; + } else { + size += gesture_coords_reported * 2 * 4 + 2; + /* coords are pairs of x,y (*2) where each coord is + * short(2bytes=4char)(*4) + 1 byte(2char) num of coords (+2) + **/ + res = OK; /* set error code to OK */ + } + + all_strbuff = (u8 *)kzalloc(size, GFP_KERNEL); + if (all_strbuff != NULL) { + snprintf(&all_strbuff[index], 11, "{ %08X", res); + index += 10; + + + if (res >= OK) { + snprintf(&all_strbuff[index], 3, "%02X", + gesture_coords_reported); + index += 2; + + + for (i = 0; i < gesture_coords_reported; i++) { + snprintf(&all_strbuff[index], 5, "%04X", + gesture_coordinates_x[i]); + index += 4; + snprintf(&all_strbuff[index], 5, "%04X", + gesture_coordinates_y[i]); + index += 4; + } + } + + index += snprintf(&all_strbuff[index], 3, " }"); + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + kfree(all_strbuff); + logError(0, "%s %s: Getting gestures coordinates FINISHED!\n", + tag, __func__); + } else + logError(1, + "%s %s: Unable to allocate all_strbuff! ERROR %08X\n", + tag, + __func__, ERROR_ALLOC); + + return count; +} +#endif + + + +/***************************************** PRODUCTION TEST + * ***************************************************/ + +/** + * File node to execute the Mass Production Test or to get data from the IC + * (raw or ms/ss init data) + * echo cmd > stm_fts_cmd to execute a command \n + * cat stm_fts_cmd to show the result of the command \n + * echo cmd > stm_fts_cmd; cat stm_fts_cmd to execute and show the result + * in just one call \n + * the string returned in the shell is made up as follow: \n + * { = start byte \n + * X1X2X3X4 = 4 bytes in HEX format which represent an error_code (00000000 = + * OK)\n + * (optional) data = data coming from the command executed represented as HEX + * string \n + * Not all the command return additional data \n + * } = end byte \n + * \n + * Possible commands (cmd): \n + * - 00 = MP Test -> return error_code \n + * - 01 = ITO Test -> return error_code \n + * - 03 = MS Raw Test -> return error_code \n + * - 04 = MS Init Data Test -> return error_code \n + * - 05 = SS Raw Test -> return error_code \n + * - 06 = SS Init Data Test -> return error_code \n + * - 13 xx(optional) = Read 1 MS Raw Frame -> return additional data: + * MS frame row after row. if xx = 1, will read LP frame \n + * - 14 = Read MS Init Data -> return additional data: MS init data row after + * row \n + * - 15 xx(optional) = Read 1 SS Raw Frame -> return additional data: SS frame, + * force channels followed by sense channels. If xx = 1, will read LP frame \n + * - 16 = Read SS Init Data -> return additional data: SS Init data, + * first IX for force and sense channels and then CX for force and sense + * channels \n + * - F0 = Perform a system reset -> return error_code \n + * - F1 = Perform a system reset and reenable the sensing and the interrupt + */ +static ssize_t stm_fts_cmd_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + int n; + char *p = (char *)buf; + + memset(typeOfComand, 0, CMD_STR_LEN * sizeof(u32)); + numberParameters = 0; + logError(1, "%s\n", tag); + for (n = 0; n < (count + 1) / 3; n++) { + if (sscanf(p, "%02X ", &typeOfComand[n]) == 1) { + p += 3; + logError(1, "%s typeOfComand[%d] = %02X\n", tag, n, + typeOfComand[n]); + numberParameters++; + } + } + + /* numberParameters = n; */ + logError(1, "%s Number of Parameters = %d\n", tag, numberParameters); + return count; +} + +static ssize_t stm_fts_cmd_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int res, j, doClean = 0, count = 0, index = 0; + + int size = (6 * 2) + 1; + int init_type = SPECIAL_PANEL_INIT; + u8 *all_strbuff = NULL; + struct fts_ts_info *info = dev_get_drvdata(dev); + + MutualSenseData compData; + SelfSenseData comData; + MutualSenseFrame frameMS; + SelfSenseFrame frameSS; + + + if (numberParameters >= 1) { + res = fts_disableInterrupt(); + if (res < 0) { + logError(0, "%s fts_disableInterrupt: ERROR %08X\n", + tag, res); + res = (res | ERROR_DISABLE_INTER); + goto END; + } + +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + res = fb_unregister_client(&info->notifier); + if (res < 0) { + logError(1, "%s ERROR: unregister notifier failed!\n", + tag); + goto END; + } +#endif + + switch (typeOfComand[0]) { + /*ITO TEST*/ + case 0x01: + res = production_test_ito(info->limit_path, &tests); + break; + /*PRODUCTION TEST*/ + case 0x00: +#ifndef COMPUTE_INIT_METHOD + if (systemInfo.u8_cfgAfeVer != systemInfo.u8_cxAfeVer) { + res = ERROR_OP_NOT_ALLOW; + logError(0, + "%s Miss match in CX version! MP test not allowed with wrong CX memory! ERROR %08X\n", + tag, res); + break; + } +#else + if (systemInfo.u8_mpFlag != MP_FLAG_FACTORY) { + init_type = SPECIAL_FULL_PANEL_INIT; + logError(0, + "%s Select Full Panel Init!\n", tag); + } else { + init_type = NO_INIT; + logError(0, + "%s Skip Full Panel Init!\n", tag); + } +#endif + + res = production_test_main(info->limit_path, 1, init_type, + &tests, MP_FLAG_FACTORY); + break; + /*read mutual raw*/ + case 0x13: + logError(0, "%s Get 1 MS Frame\n", tag); + if (numberParameters >= 2 && typeOfComand[1] == 1) + setScanMode(SCAN_MODE_LOCKED, LOCKED_LP_ACTIVE); + else + setScanMode(SCAN_MODE_LOCKED, LOCKED_ACTIVE); + msleep(WAIT_FOR_FRESH_FRAMES); + setScanMode(SCAN_MODE_ACTIVE, 0x00); + msleep(WAIT_AFTER_SENSEOFF); + flushFIFO(); /* delete the events related to some + * touch (allow to call this function + * while touching the screen without + * having a flooding of the FIFO) */ + res = getMSFrame3(MS_RAW, &frameMS); + if (res < 0) + logError(0, + "%s Error while taking the MS frame... ERROR %08X\n", + tag, res); + + else { + logError(0, "%s The frame size is %d words\n", + tag, res); + size += (res * sizeof(short) + 2) * 2; + /* set res to OK because if getMSFrame is */ + /* successful res = number of words read */ + res = OK; + print_frame_short( + "MS frame =", + array1dTo2d_short( + frameMS.node_data, + frameMS.node_data_size, + frameMS.header. + sense_node), + frameMS.header.force_node, + frameMS.header.sense_node); + } + break; + /*read self raw*/ + case 0x15: + logError(0, "%s Get 1 SS Frame\n", tag); + if (numberParameters >= 2 && typeOfComand[1] == 1) + setScanMode(SCAN_MODE_LOCKED, LOCKED_LP_DETECT); + else + setScanMode(SCAN_MODE_LOCKED, LOCKED_ACTIVE); + msleep(WAIT_FOR_FRESH_FRAMES); + setScanMode(SCAN_MODE_ACTIVE, 0x00); + msleep(WAIT_AFTER_SENSEOFF); + flushFIFO(); /* delete the events related to some + * touch (allow to call this function + * while touching the screen without + * having a flooding of the FIFO) */ + if (numberParameters >= 2 && typeOfComand[1] == 1) + res = getSSFrame3(SS_DETECT_RAW, &frameSS); + else + res = getSSFrame3(SS_RAW, &frameSS); + + if (res < OK) + logError(0, + "%s Error while taking the SS frame... ERROR %08X\n", + tag, res); + + else { + logError(0, "%s The frame size is %d words\n", + tag, res); + size += (res * sizeof(short) + 2) * 2; + /* set res to OK because if getMSFrame is */ + /* successful res = number of words read */ + res = OK; + print_frame_short("SS force frame =", + array1dTo2d_short( + frameSS.force_data, + frameSS. + header.force_node, 1), + frameSS.header.force_node, 1); + print_frame_short("SS sense frame =", + array1dTo2d_short( + frameSS.sense_data, + frameSS. + header.sense_node, + frameSS. + header.sense_node), 1, + frameSS.header.sense_node); + } + + break; + + case 0x14: /* read mutual comp data */ + logError(0, "%s Get MS Compensation Data\n", tag); + res = readMutualSenseCompensationData(LOAD_CX_MS_TOUCH, + &compData); + + if (res < 0) + logError(0, + "%s Error reading MS compensation data ERROR %08X\n", + tag, res); + else { + logError(0, + "%s MS Compensation Data Reading Finished!\n", + tag); + size += ((compData.node_data_size + 3) * + sizeof(u8)) * 2; + print_frame_i8("MS Data (Cx2) =", + array1dTo2d_i8( + compData.node_data, + compData. + node_data_size, + compData.header. + sense_node), + compData.header.force_node, + compData.header.sense_node); + } + break; + + case 0x16: /* read self comp data */ + logError(0, "%s Get SS Compensation Data...\n", tag); + res = readSelfSenseCompensationData(LOAD_CX_SS_TOUCH, + &comData); + if (res < 0) + logError(0, + "%s Error reading SS compensation data ERROR %08X\n", + tag, res); + else { + logError(0, + "%s SS Compensation Data Reading Finished!\n", + tag); + size += ((comData.header.force_node + + comData.header.sense_node) * 2 + 8) * + sizeof(u8) * 2; + print_frame_u8("SS Data Ix2_fm = ", + array1dTo2d_u8(comData.ix2_fm, + comData.header. + force_node, 1), + comData.header.force_node, 1); + print_frame_i8("SS Data Cx2_fm = ", + array1dTo2d_i8(comData.cx2_fm, + comData.header. + force_node, 1), + comData.header.force_node, 1); + print_frame_u8("SS Data Ix2_sn = ", + array1dTo2d_u8(comData.ix2_sn, + comData.header. + sense_node, + comData.header. + sense_node), 1, + comData.header.sense_node); + print_frame_i8("SS Data Cx2_sn = ", + array1dTo2d_i8(comData.cx2_sn, + comData.header. + sense_node, + comData.header. + sense_node), 1, + comData.header.sense_node); + } + break; + + case 0x03: /* MS Raw DATA TEST */ + res = fts_system_reset(); + if (res >= OK) + res = production_test_ms_raw(info->limit_path, 1, + &tests); + break; + + case 0x04: /* MS CX DATA TEST */ + res = fts_system_reset(); + if (res >= OK) + res = production_test_ms_cx(info->limit_path, 1, + &tests); + break; + + case 0x05: /* SS RAW DATA TEST */ + res = fts_system_reset(); + if (res >= OK) + res = production_test_ss_raw(info->limit_path, 1, + &tests); + break; + + case 0x06: /* SS IX CX DATA TEST */ + res = fts_system_reset(); + if (res >= OK) + res = production_test_ss_ix_cx(info->limit_path, 1, + &tests); + break; + + + case 0xF0: + case 0xF1: /* TOUCH ENABLE/DISABLE */ + doClean = (int)(typeOfComand[0] & 0x01); + res = cleanUp(doClean); + break; + + default: + logError(1, + "%s COMMAND NOT VALID!! Insert a proper value ...\n", + tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + + doClean = fts_mode_handler(info, 1); + if (typeOfComand[0] != 0xF0) + doClean |= fts_enableInterrupt(); + if (doClean < 0) + logError(0, "%s %s: ERROR %08X\n", tag, __func__, + (doClean | ERROR_ENABLE_INTER)); + } else { + logError(1, + "%s NO COMMAND SPECIFIED!!! do: 'echo [cmd_code] [args] > stm_fts_cmd' before looking for result!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + + +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + if (fb_register_client(&info->notifier) < 0) + logError(1, "%s ERROR: register notifier failed!\n", tag); +#endif + +END: + /* here start the reporting phase, assembling the data + * to send in the file node */ + all_strbuff = (u8 *)kzalloc(size, GFP_KERNEL); + + snprintf(&all_strbuff[index], 11, "{ %08X", res); + index += 10; + + if (res >= OK) { + /*all the other cases are already fine printing only the res.*/ + switch (typeOfComand[0]) { + case 0x13: + snprintf(&all_strbuff[index], 3, "%02X", + (u8)frameMS.header.force_node); + index += 2; + + snprintf(&all_strbuff[index], 3, "%02X", + (u8)frameMS.header.sense_node); + index += 2; + + for (j = 0; j < frameMS.node_data_size; j++) { + snprintf(&all_strbuff[index], 5, "%02X%02X", + (frameMS.node_data[j] & 0xFF00) >> 8, + frameMS.node_data[j] & 0xFF); + index += 4; + } + + kfree(frameMS.node_data); + break; + + case 0x15: + snprintf(&all_strbuff[index], 3, "%02X", + (u8)frameSS.header.force_node); + index += 2; + + snprintf(&all_strbuff[index], 3, "%02X", + (u8)frameSS.header.sense_node); + index += 2; + + /* Copying self raw data Force */ + for (j = 0; j < frameSS.header.force_node; j++) { + snprintf(&all_strbuff[index], 5, "%02X%02X", + (frameSS.force_data[j] & 0xFF00) >> 8, + frameSS.force_data[j] & 0xFF); + index += 4; + } + + + /* Copying self raw data Sense */ + for (j = 0; j < frameSS.header.sense_node; j++) { + snprintf(&all_strbuff[index], 5, "%02X%02X", + (frameSS.sense_data[j] & 0xFF00) >> 8, + frameSS.sense_data[j] & 0xFF); + index += 4; + } + + kfree(frameSS.force_data); + kfree(frameSS.sense_data); + break; + + case 0x14: + snprintf(&all_strbuff[index], 3, "%02X", + (u8)compData.header.force_node); + index += 2; + + snprintf(&all_strbuff[index], 3, "%02X", + (u8)compData.header.sense_node); + index += 2; + + /* Cpying CX1 value */ + snprintf(&all_strbuff[index], 3, "%02X", + (compData.cx1) & 0xFF); + index += 2; + + /* Copying CX2 values */ + for (j = 0; j < compData.node_data_size; j++) { + snprintf(&all_strbuff[index], 3, "%02X", + (compData.node_data[j]) & 0xFF); + index += 2; + } + + kfree(compData.node_data); + break; + + case 0x16: + snprintf(&all_strbuff[index], 3, "%02X", + comData.header.force_node); + index += 2; + + snprintf(&all_strbuff[index], 3, "%02X", + comData.header.sense_node); + index += 2; + + snprintf(&all_strbuff[index], 3, "%02X", + (comData.f_ix1) & 0xFF); + index += 2; + + snprintf(&all_strbuff[index], 3, "%02X", + (comData.s_ix1) & 0xFF); + index += 2; + + snprintf(&all_strbuff[index], 3, "%02X", + (comData.f_cx1) & 0xFF); + index += 2; + + snprintf(&all_strbuff[index], 3, "%02X", + (comData.s_cx1) & 0xFF); + index += 2; + + snprintf(&all_strbuff[index], 3, "%02X", + (comData.f_ix0) & 0xFF); + index += 2; + + snprintf(&all_strbuff[index], 3, "%02X", + (comData.s_ix0) & 0xFF); + index += 2; + + /* Copying IX2 Force */ + for (j = 0; j < comData.header.force_node; j++) { + snprintf(&all_strbuff[index], 3, "%02X", + comData.ix2_fm[j] & 0xFF); + index += 2; + } + + /* Copying IX2 Sense */ + for (j = 0; j < comData.header.sense_node; j++) { + snprintf(&all_strbuff[index], 3, "%02X", + comData.ix2_sn[j] & 0xFF); + index += 2; + } + + /* Copying CX2 Force */ + for (j = 0; j < comData.header.force_node; j++) { + snprintf(&all_strbuff[index], 3, "%02X", + comData.cx2_fm[j] & 0xFF); + index += 2; + } + + /* Copying CX2 Sense */ + for (j = 0; j < comData.header.sense_node; j++) { + snprintf(&all_strbuff[index], 3, "%02X", + comData.cx2_sn[j] & 0xFF); + index += 2; + } + + kfree(comData.ix2_fm); + kfree(comData.ix2_sn); + kfree(comData.cx2_fm); + kfree(comData.cx2_sn); + break; + + default: + break; + } + } + + snprintf(&all_strbuff[index], 3, " }"); + index += 2; + + + count = snprintf(buf, TSP_BUF_SIZE, "%s\n", all_strbuff); + numberParameters = 0; + /* need to reset the number of parameters in order to wait the + * next command, comment if you want to repeat the last command sent + * just doing a cat */ + /* logError(0,"%s numberParameters = %d\n",tag, numberParameters); */ + kfree(all_strbuff); + + return count; +} + +static DEVICE_ATTR(fwupdate, (S_IRUGO | S_IWUSR | S_IWGRP), fts_fwupdate_show, + fts_fwupdate_store); +static DEVICE_ATTR(appid, (S_IRUGO), fts_appid_show, NULL); +static DEVICE_ATTR(mode_active, (S_IRUGO), fts_mode_active_show, NULL); +static DEVICE_ATTR(fw_file_test, (S_IRUGO), fts_fw_test_show, NULL); +static DEVICE_ATTR(stm_fts_cmd, (S_IRUGO | S_IWUSR | S_IWGRP), stm_fts_cmd_show, + stm_fts_cmd_store); +#ifdef USE_ONE_FILE_NODE +static DEVICE_ATTR(feature_enable, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_feature_enable_show, fts_feature_enable_store); +#else + + +#ifdef GRIP_MODE +static DEVICE_ATTR(grip_mode, (S_IRUGO | S_IWUSR | S_IWGRP), fts_grip_mode_show, + fts_grip_mode_store); +#endif + +#ifdef CHARGER_MODE +static DEVICE_ATTR(charger_mode, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_charger_mode_show, fts_charger_mode_store); +#endif + +#ifdef GLOVE_MODE +static DEVICE_ATTR(glove_mode, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_glove_mode_show, fts_glove_mode_store); +#endif + +#ifdef COVER_MODE +static DEVICE_ATTR(cover_mode, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_cover_mode_show, fts_cover_mode_store); +#endif + +#ifdef STYLUS_MODE +static DEVICE_ATTR(stylus_mode, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_stylus_mode_show, fts_stylus_mode_store); +#endif + +#endif + +#ifdef GESTURE_MODE +static DEVICE_ATTR(gesture_mask, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_gesture_mask_show, fts_gesture_mask_store); +static DEVICE_ATTR(gesture_coordinates, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_gesture_coordinates_show, NULL); +#endif + +/* /sys/devices/soc.0/f9928000.i2c/i2c-6/6-0049 */ +static struct attribute *fts_attr_group[] = { + &dev_attr_fwupdate.attr, + &dev_attr_appid.attr, + &dev_attr_mode_active.attr, + &dev_attr_fw_file_test.attr, + &dev_attr_stm_fts_cmd.attr, +#ifdef USE_ONE_FILE_NODE + &dev_attr_feature_enable.attr, +#else + +#ifdef GRIP_MODE + &dev_attr_grip_mode.attr, +#endif +#ifdef CHARGER_MODE + &dev_attr_charger_mode.attr, +#endif +#ifdef GLOVE_MODE + &dev_attr_glove_mode.attr, +#endif +#ifdef COVER_MODE + &dev_attr_cover_mode.attr, +#endif +#ifdef STYLUS_MODE + &dev_attr_stylus_mode.attr, +#endif + +#endif + +#ifdef GESTURE_MODE + &dev_attr_gesture_mask.attr, + &dev_attr_gesture_coordinates.attr, +#endif + NULL, +}; + +/** @}*/ +/** @}*/ + + +/** + * @defgroup isr Interrupt Service Routine (Event Handler) + * The most important part of the driver is the ISR (Interrupt Service Routine) + * called also as Event Handler \n + * As soon as the interrupt pin goes low, fts_interrupt_handler() is called and + * the chain to read and parse the event read from the FIFO start.\n + * For any different kind of EVT_ID there is a specific event handler + * which will take the correct action to report the proper info to the host. \n + * The most important events are the one related to touch informations, status + * update or user report. + * @{ + */ + +/** + * Report to the linux input system the pressure and release of a button + * handling concurrency + * @param info pointer to fts_ts_info which contains info about the device + * and its hw setup + * @param key_code button value + */ +void fts_input_report_key(struct fts_ts_info *info, int key_code) +{ + mutex_lock(&info->input_report_mutex); + input_report_key(info->input_dev, key_code, 1); + input_sync(info->input_dev); + input_report_key(info->input_dev, key_code, 0); + input_sync(info->input_dev); + mutex_unlock(&info->input_report_mutex); +} + + + +/** + * Event Handler for no events (EVT_ID_NOEVENT) + */ +static void fts_nop_event_handler(struct fts_ts_info *info, unsigned + char *event) +{ + logError(1, + "%s %s Doing nothing for event = %02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[0], event[1], event[2], event[3], + event[4], + event[5], event[6], event[7]); +} + +/** + * Event handler for enter and motion events (EVT_ID_ENTER_POINT, + * EVT_ID_MOTION_POINT ) + * report to the linux input system touches with their coordinated and + * additional informations + */ +static void fts_enter_pointer_event_handler(struct fts_ts_info *info, unsigned + char *event) +{ + unsigned char touchId; + unsigned int touch_condition = 1, tool = MT_TOOL_FINGER; + int x, y, z, distance; + struct fts_hw_platform_data *board = info->board; + u8 touchType; + + if (!info->resume_bit) + goto no_report; + + touchType = event[1] & 0x0F; + touchId = (event[1] & 0xF0) >> 4; + + x = (((int)event[3] & 0x0F) << 8) | (event[2]); + y = ((int)event[4] << 4) | ((event[3] & 0xF0) >> 4); + /* TODO: check with fw how they will report distance and pressure */ + z = PRESSURE_MAX; + distance = 0; /* if the tool is touching the display the distance + * should be 0 */ + + if (x == board->x_max) + x--; + + if (y == board->y_max) + y--; + + if (board->y_flip) + y = board->y_max - y; + + if (board->x_flip) + x = board->x_max - x; + + input_mt_slot(info->input_dev, touchId); + switch (touchType) { +#ifdef STYLUS_MODE + case TOUCH_TYPE_STYLUS: + logError(0, "%s %s : It is a stylus!\n", tag, __func__); + if (info->stylus_enabled == 1) { /* if stylus_enabled is + * not ==1 it will be + * reported as normal + * touch */ + tool = MT_TOOL_PEN; + touch_condition = 1; + __set_bit(touchId, &info->stylus_id); + break; + } +#endif + /* TODO: customer can implement a different strategy for each kind of + * touch */ + case TOUCH_TYPE_FINGER: + /* logError(0, "%s %s : It is a finger!\n",tag,__func__); */ + case TOUCH_TYPE_GLOVE: + /* logError(0, "%s %s : It is a glove!\n",tag,__func__); */ + case TOUCH_TYPE_PALM: + /* logError(0, "%s %s : It is a palm!\n",tag,__func__); */ + tool = MT_TOOL_FINGER; + touch_condition = 1; + __set_bit(touchId, &info->touch_id); + break; + + + case TOUCH_TYPE_HOVER: + tool = MT_TOOL_FINGER; + touch_condition = 0; /* need to hover */ + z = 0; /* no pressure */ + __set_bit(touchId, &info->touch_id); + distance = DISTANCE_MAX; /* check with fw report the + * hovering distance */ + break; + + case TOUCH_TYPE_INVALID: + default: + logError(1, "%s %s : Invalid touch type = %d ! No Report...\n", + tag, __func__, touchType); + goto no_report; + } + + input_mt_report_slot_state(info->input_dev, tool, 1); + + /* logError(0, "%s %s : TouchID = %d,Touchcount = %d + *\n",tag,__func__,touchId,touchcount); */ + if (event[0] == EVT_ID_ENTER_POINT) { + if (info->touch_count == 0) + input_report_key(info->input_dev, BTN_TOUCH, touch_condition); + info->touch_count++; + } + + /* input_report_abs(info->input_dev, ABS_MT_TRACKING_ID, touchId); */ + input_report_abs(info->input_dev, ABS_MT_POSITION_X, x); + input_report_abs(info->input_dev, ABS_MT_POSITION_Y, y); + input_report_abs(info->input_dev, ABS_MT_TOUCH_MAJOR, z); + input_report_abs(info->input_dev, ABS_MT_TOUCH_MINOR, z); + /* + input_report_abs(info->input_dev, ABS_MT_PRESSURE, z); + input_report_abs(info->input_dev, ABS_MT_DISTANCE, distance); + */ + + /* logError(0, "%s %s : Event 0x%02x - ID[%d], (x, y) = (%3d, %3d) + * Size = %d\n", tag, __func__, *event, touchId, x, y, touchType); */ + +no_report: + return; +} + +/** + * Event handler for leave event (EVT_ID_LEAVE_POINT ) + * Report to the linux input system that one touch left the display + */ +static void fts_leave_pointer_event_handler(struct fts_ts_info *info, unsigned + char *event) +{ + unsigned char touchId; + unsigned int tool = MT_TOOL_FINGER; + u8 touchType; + + touchType = event[1] & 0x0F; + touchId = (event[1] & 0xF0) >> 4; + + + + input_mt_slot(info->input_dev, touchId); + switch (touchType) { +#ifdef STYLUS_MODE + case TOUCH_TYPE_STYLUS: + logError(0, "%s %s : It is a stylus!\n", tag, __func__); + if (info->stylus_enabled == 1) { + /* if stylus_enabled is not ==1 it will be reported as + * normal touch */ + tool = MT_TOOL_PEN; + __clear_bit(touchId, &info->stylus_id); + break; + } +#endif + + case TOUCH_TYPE_FINGER: + /* logError(0, "%s %s : It is a finger!\n",tag,__func__); */ + case TOUCH_TYPE_GLOVE: + /* logError(0, "%s %s : It is a glove!\n",tag,__func__); */ + case TOUCH_TYPE_PALM: + /* logError(0, "%s %s : It is a palm!\n",tag,__func__); */ + case TOUCH_TYPE_HOVER: + tool = MT_TOOL_FINGER; + __clear_bit(touchId, &info->touch_id); + break; + + case TOUCH_TYPE_INVALID: + default: + logError(1, "%s %s : Invalid touch type = %d ! No Report...\n", + tag, __func__, touchType); + return; + } + + input_mt_report_slot_state(info->input_dev, tool, 0); + + /* logError(0, "%s %s : TouchID = %d, Touchcount = %d\n",tag,__func__, + * touchId,touchcount); */ + + /* no need to set ABS_MT_TRACKING_ID, input_mt_init_slots() already set it */ + /* input_report_abs(info->input_dev, ABS_MT_TRACKING_ID, -1); */ + if (info->touch_count > 0) + info->touch_count--; + + if (info->touch_count == 0) + input_report_key(info->input_dev, BTN_TOUCH, 0); + /* logError(0, "%s %s : Event 0x%02x - release ID[%d]\n", tag, + * __func__, event[0], touchId); */ +} + +/* EventId : EVT_ID_MOTION_POINT */ +#define fts_motion_pointer_event_handler fts_enter_pointer_event_handler +/* remap the motion event handler to the same function which handle the enter + * event */ + +/** + * Event handler for error events (EVT_ID_ERROR) + * Handle unexpected error events implementing recovery strategy and + * restoring the sensing status that the IC had before the error occured + */ +static void fts_error_event_handler(struct fts_ts_info *info, unsigned + char *event) +{ + int error = 0; + + logError(0, + "%s %s Received event %02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, + __func__, event[0], event[1], event[2], event[3], event[4], + event[5], + event[6], event[7]); + + switch (event[1]) { + case EVT_TYPE_ERROR_ESD:/* esd */ + {/* before reset clear all slot */ + release_all_touches(info); + + fts_chip_powercycle(info); + + error = fts_system_reset(); + error |= fts_mode_handler(info, 0); + error |= fts_enableInterrupt(); + if (error < OK) + logError(1, + "%s %s Cannot restore the device ERROR %08X\n", + tag, __func__, error); + } + break; + + case EVT_TYPE_ERROR_HARD_FAULT: /* hard fault */ + case EVT_TYPE_ERROR_WATCHDOG: /* watch dog timer */ + { + dumpErrorInfo(NULL, 0); + /* before reset clear all slots */ + release_all_touches(info); + error = fts_system_reset(); + error |= fts_mode_handler(info, 0); + error |= fts_enableInterrupt(); + if (error < OK) + logError(1, + "%s %s Cannot reset the device ERROR %08X\n", + tag, __func__, error); + } + break; + } +} + +/** + * Event handler for controller ready event (EVT_ID_CONTROLLER_READY) + * Handle controller events received after unexpected reset of the IC updating + * the resets flag and restoring the proper sensing status + */ +static void fts_controller_ready_event_handler(struct fts_ts_info *info, + unsigned char *event) +{ + int error; + + logError(0, + "%s %s Received event %02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, + __func__, event[0], event[1], event[2], event[3], event[4], + event[5], + event[6], event[7]); + + logError(0, "%s %s NB: skip calling fts_mode_handler\n", tag, __func__); + return; + + release_all_touches(info); + setSystemResetedUp(1); + setSystemResetedDown(1); + error = fts_mode_handler(info, 0); + if (error < OK) + logError(1, + "%s %s Cannot restore the device status ERROR %08X\n", + tag, + __func__, error); +} + +/** + * Event handler for status events (EVT_ID_STATUS_UPDATE) + * Handle status update events + */ +static void fts_status_event_handler(struct fts_ts_info *info, unsigned + char *event) +{ + switch (event[1]) { + case EVT_TYPE_STATUS_ECHO: + logError(1, + "%s %s Echo event of command = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], event[5], + event[6], + event[7]); + break; + + case EVT_TYPE_STATUS_FORCE_CAL: + switch (event[2]) { + case 0x00: + logError(1, + "%s %s Continuous frame drop Force cal = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + break; + + case 0x01: + logError(1, + "%s %s Mutual negative detect Force cal = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + break; + + case 0x02: + logError(1, + "%s %s Mutual calib deviation Force cal = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + break; + + case 0x11: + logError(1, + "%s %s SS negative detect Force cal = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + break; + + case 0x12: + logError(1, + "%s %s SS negative detect Force cal in Low Power mode = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + break; + + case 0x13: + logError(1, + "%s %s SS negative detect Force cal in Idle mode = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + break; + + case 0x20: + logError(1, + "%s %s SS invalid Mutual Strength soft Force cal = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + break; + + case 0x21: + logError(1, + "%s %s SS invalid Self Strength soft Force cal = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + break; + + case 0x22: + logError(1, + "%s %s SS invalid Self Island soft Force cal = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + break; + + case 0x30: + logError(1, + "%s %s MS invalid Mutual Strength soft Force cal = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + break; + + case 0x31: + logError(1, + "%s %s MS invalid Self Strength soft Force cal = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + break; + + default: + logError(1, + "%s %s Force cal = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + } + break; + + case EVT_TYPE_STATUS_FRAME_DROP: + switch (event[2]) { + case 0x01: + logError(1, + "%s %s Frame drop noisy frame = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + break; + + case 0x02: + logError(1, + "%s %s Frame drop bad R = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + break; + + case 0x03: + logError(1, + "%s %s Frame drop invalid processing state = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + break; + + default: + /*logError(1, + "%s %s Frame drop = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]);*/ + break; + } + break; + + case EVT_TYPE_STATUS_SS_RAW_SAT: + if (event[2] == 1) + logError(1, + "%s %s SS Raw Saturated = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + else + logError(1, + "%s %s SS Raw No more Saturated = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + break; + + case EVT_TYPE_STATUS_WATER: + if (event[2] == 1) + logError(1, + "%s %s Enter Water mode = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + else + logError(1, + "%s %s Exit Water mode = %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[2], event[3], event[4], + event[5], + event[6], event[7]); + break; + + default: + logError(1, + "%s %s Received unhandled status event = %02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[0], event[1], event[2], event[3], + event[4], + event[5], event[6], event[7]); + break; + } +} + + +/* key events reported in the user report */ +#ifdef PHONE_KEY +/* TODO: the customer should handle the events coming from the keys according + * his needs (this is just an sample code that report the click of a botton + * after a press->release action) */ +/** + * Event handler for status events (EVT_TYPE_USER_KEY) + * Handle keys update events, the third byte of the event is a bitmask where if + *the bit set means that the corresponding key is pressed. + */ +static void fts_key_event_handler(struct fts_ts_info *info, unsigned + char *event) +{ + /* int value; */ + logError(0, + "%s %s Received event %02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, + __func__, event[0], event[1], event[2], event[3], event[4], + event[5], + event[6], event[7]); + + if (event[0] == EVT_ID_USER_REPORT && event[1] == EVT_TYPE_USER_KEY) { + /* event[2] contain the bitmask of the keys that are actually + * pressed */ + + if ((event[2] & FTS_KEY_0) == 0 && (key_mask & FTS_KEY_0) > 0) { + logError(0, + "%s %s: Button HOME pressed and released!\n", + tag, + __func__); + fts_input_report_key(info, KEY_HOMEPAGE); + } + + if ((event[2] & FTS_KEY_1) == 0 && (key_mask & FTS_KEY_1) > 0) { + logError(0, + "%s %s: Button Back pressed and released!\n", + tag, + __func__); + fts_input_report_key(info, KEY_BACK); + } + + if ((event[2] & FTS_KEY_2) == 0 && (key_mask & FTS_KEY_2) > 0) { + logError(0, "%s %s: Button Menu pressed!\n", tag, + __func__); + fts_input_report_key(info, KEY_MENU); + } + + key_mask = event[2]; + } else + logError(1, "%s %s: Invalid event passed as argument!\n", tag, + __func__); +} +#endif + +/* gesture event must be handled in the user event handler */ +#ifdef GESTURE_MODE +/* TODO: Customer should implement their own actions in respond of a gesture + * event. This is an example that simply print the gesture received and simulate + * the click on a different button for each gesture. */ +/** + * Event handler for gesture events (EVT_TYPE_USER_GESTURE) + * Handle gesture events and simulate the click on a different button for any + *gesture detected (@link gesture_opt Gesture IDs @endlink) + */ +static void fts_gesture_event_handler(struct fts_ts_info *info, unsigned + char *event) +{ + int value = 0; + int needCoords = 0; + + logError(0, + "%s gesture event data: %02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, event[0], event[1], event[2], event[3], event[4], + event[5], + event[6], event[7]); + + + + if (event[0] == EVT_ID_USER_REPORT && event[1] == + EVT_TYPE_USER_GESTURE) { + needCoords = 1; + /* default read the coordinates for all gestures excluding + * double tap */ + + switch (event[2]) { + case GEST_ID_DBLTAP: + value = KEY_WAKEUP; + logError(0, "%s %s: double tap !\n", tag, __func__); + needCoords = 0; + break; + + case GEST_ID_AT: + value = KEY_WWW; + logError(0, "%s %s: @ !\n", tag, __func__); + break; + + case GEST_ID_C: + value = KEY_C; + logError(0, "%s %s: C !\n", tag, __func__); + break; + + case GEST_ID_E: + value = KEY_E; + logError(0, "%s %s: e !\n", tag, __func__); + break; + + case GEST_ID_F: + value = KEY_F; + logError(0, "%s %s: F !\n", tag, __func__); + break; + + case GEST_ID_L: + value = KEY_L; + logError(0, "%s %s: L !\n", tag, __func__); + break; + + case GEST_ID_M: + value = KEY_M; + logError(0, "%s %s: M !\n", tag, __func__); + break; + + case GEST_ID_O: + value = KEY_O; + logError(0, "%s %s: O !\n", tag, __func__); + break; + + case GEST_ID_S: + value = KEY_S; + logError(0, "%s %s: S !\n", tag, __func__); + break; + + case GEST_ID_V: + value = KEY_V; + logError(0, "%s %s: V !\n", tag, __func__); + break; + + case GEST_ID_W: + value = KEY_W; + logError(0, "%s %s: W !\n", tag, __func__); + break; + + case GEST_ID_Z: + value = KEY_Z; + logError(0, "%s %s: Z !\n", tag, __func__); + break; + + case GEST_ID_RIGHT_1F: + value = KEY_RIGHT; + logError(0, "%s %s: -> !\n", tag, __func__); + break; + + case GEST_ID_LEFT_1F: + value = KEY_LEFT; + logError(0, "%s %s: <- !\n", tag, __func__); + break; + + case GEST_ID_UP_1F: + value = KEY_UP; + logError(0, "%s %s: UP !\n", tag, __func__); + break; + + case GEST_ID_DOWN_1F: + value = KEY_DOWN; + logError(0, "%s %s: DOWN !\n", tag, __func__); + break; + + case GEST_ID_CARET: + value = KEY_APOSTROPHE; + logError(0, "%s %s: ^ !\n", tag, __func__); + break; + + case GEST_ID_LEFTBRACE: + value = KEY_LEFTBRACE; + logError(0, "%s %s: < !\n", tag, __func__); + break; + + case GEST_ID_RIGHTBRACE: + value = KEY_RIGHTBRACE; + logError(0, "%s %s: > !\n", tag, __func__); + break; + + case GEST_ID_SIGTAP: +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + if (info->imports && info->imports->report_gesture) { + int ret = 0; + struct gesture_event_data mmi_event; + + logError(1, "%s %s: invoke imported report gesture function\n", tag, __func__); + /* extract X and Y coordinates */ + mmi_event.evcode = 1; + mmi_event.evdata.x = (event[4] << 8) | event[3]; + mmi_event.evdata.y = (event[6] << 8) | event[5]; + /* call class method */ + ret = info->imports->report_gesture(&mmi_event); + if (!ret) + PM_WAKEUP_EVENT(info->wakesrc, 3000); + + goto gesture_done; + } +#endif + break; + + default: + logError(0, "%s %s: No valid GestureID!\n", tag, + __func__); + goto gesture_done; + } + + if (needCoords == 1) + readGestureCoords(event); + + fts_input_report_key(info, value); + +gesture_done: + return; + } else + logError(1, "%s %s: Invalid event passed as argument!\n", tag, + __func__); +} +#endif + + +/** + * Event handler for user report events (EVT_ID_USER_REPORT) + * Handle user events reported by the FW due to some interaction triggered + * by an external user (press keys, perform gestures, etc.) + */ +static void fts_user_report_event_handler(struct fts_ts_info *info, unsigned + char *event) +{ + switch (event[1]) { +#ifdef PHONE_KEY + case EVT_TYPE_USER_KEY: + fts_key_event_handler(info, event); + break; +#endif + + case EVT_TYPE_USER_PROXIMITY: + if (event[2] == 0) + logError(1, "%s %s No proximity!\n", tag, __func__); + else + logError(1, "%s %s Proximity Detected!\n", tag, + __func__); + break; + +#ifdef GESTURE_MODE + case EVT_TYPE_USER_GESTURE: + fts_gesture_event_handler(info, event); + break; +#endif + default: + logError(1, + "%s %s Received unhandled user report event = %02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, __func__, event[0], event[1], event[2], event[3], + event[4], + event[5], event[6], event[7]); + break; + } +} + +/** + * Bottom Half Interrupt Handler function + * This handler is called each time there is at least one new event in the FIFO + * and the interrupt pin of the IC goes low. It will read all the events from + * the FIFO and dispatch them to the proper event handler according the event + * ID + */ +static irqreturn_t fts_event_handler(int irq, void *ptr) +{ + struct fts_ts_info *info = (struct fts_ts_info *)ptr; + int error = 0, count = 0; + unsigned char regAdd; + unsigned char data[FIFO_EVENT_SIZE] = { 0 }; + unsigned char eventId; + + event_dispatch_handler_t event_handler; + + /* read the FIFO and parsing events */ + regAdd = FIFO_CMD_READONE; + + for (count = 0; count < FIFO_DEPTH; count++) { + error = fts_writeReadU8UX(regAdd, 0, 0, data, FIFO_EVENT_SIZE, + DUMMY_FIFO); + + /*logError(0, "%s %s event = %02X %02X %02X %02X %02X %02X %02X %02X\n", + tag, __func__, data[0], + data[1], data[2], data[3], data[4], data[5], data[6], + data[7]);*/ + if (error == OK && data[0] != EVT_ID_NOEVENT) + eventId = data[0] >> 4; + else + break; + /* if(data[7]&0x20) */ + /* logError(1, "%s %s overflow ID = %02X Last = %02X\n", tag, + * __func__, data[0], data[7]); */ + + + + + if (eventId < NUM_EVT_ID) { /* this check prevent array out + * of index in case of no sense + * event ID */ + event_handler = info->event_dispatch_table[eventId]; + event_handler(info, (data)); + } + } + input_sync(info->input_dev); + + return IRQ_HANDLED; +} +/** @}*/ + + + +/** + * Implement the fw update and initialization flow of the IC that should be + *executed at every boot up. + * The function perform a fw update of the IC in case of crc error or a new + *fw version and then understand if the IC need to be re-initialized again. + * @return OK if success or an error code which specify the type of error + * encountered + */ +int fts_fw_update(struct fts_ts_info *info) +{ + u8 error_to_search[4] = { EVT_TYPE_ERROR_CRC_CX_HEAD, + EVT_TYPE_ERROR_CRC_CX, + EVT_TYPE_ERROR_CRC_CX_SUB_HEAD, + EVT_TYPE_ERROR_CRC_CX_SUB }; + int retval = 0; + int retval1 = 0; + int ret; + int crc_status = 0; + int error = 0; + int init_type = NO_INIT; + const char *firmware_name; + const struct fts_hw_platform_data *bdata = info->board; + +#if defined(PRE_SAVED_METHOD) || defined (COMPUTE_INIT_METHOD) + int keep_cx = 1; +#else + int keep_cx = 0; +#endif + + + logError(1, "%s Fw Auto Update is starting...\n", tag); + + /* check CRC status */ + ret = fts_crc_check(); + if (ret > OK) { + logError(1, "%s %s: CRC Error or NO FW!\n", tag, __func__); + crc_status = ret; + } else { + crc_status = 0; + logError(1, + "%s %s: NO CRC Error or Impossible to read CRC register!\n", + tag, __func__); + if (info->force_reflash) + crc_status = CRC_FLASH; + } + /* support arbitrary firmware file */ + if (info->fw_file) + firmware_name = info->fw_file; + else + firmware_name = PATH_FILE_FW; + + retval = flashProcedure(firmware_name, crc_status, keep_cx); + if ((retval & 0xFF000000) == ERROR_FLASH_PROCEDURE) { + logError(1, + "%s %s: firmware update failed and retry! ERROR %08X\n", + tag, + __func__, retval); + fts_chip_powercycle(info); /* power reset */ + retval1 = flashProcedure(firmware_name, crc_status, keep_cx); + if ((retval1 & 0xFF000000) == ERROR_FLASH_PROCEDURE) { + logError(1, + "%s %s: firmware update failed again! ERROR %08X\n", + tag, __func__, retval1); + logError(1, "%s Fw Auto Update Failed!\n", tag); + } + } + + + logError(1, "%s %s: Verifying if CX CRC Error...\n", tag, __func__); + ret = fts_system_reset(); + if (ret >= OK) { + ret = pollForErrorType(error_to_search, 4); + if (ret < OK) { + logError(1, "%s %s: No Cx CRC Error Found!\n", tag, + __func__); + logError(1, "%s %s: Verifying if Panel CRC Error...\n", + tag, __func__); + error_to_search[0] = EVT_TYPE_ERROR_CRC_PANEL_HEAD; + error_to_search[1] = EVT_TYPE_ERROR_CRC_PANEL; + ret = pollForErrorType(error_to_search, 2); + if (ret < OK) { + logError(1, + "%s %s: No Panel CRC Error Found!\n", + tag, + __func__); + init_type = NO_INIT; + } else { + logError(1, + "%s %s: Panel CRC Error FOUND! CRC ERROR = %02X\n", + tag, __func__, ret); + init_type = SPECIAL_PANEL_INIT; + } + } else { + logError(1, + "%s %s: Cx CRC Error FOUND! CRC ERROR = %02X\n", + tag, + __func__, ret); + /* this path of the code is used only in case there is a + * CRC error in code or config which not allow the fw to + * compute the CRC in the CX before */ + /* the only way to recover is to have CX in fw file... + * */ +#ifndef COMPUTE_INIT_METHOD + logError(1, + "%s %s: Try to recovery with CX in fw file...\n", + tag, + __func__); + flashProcedure(PATH_FILE_FW, CRC_CX, 0); + logError(1, "%s %s: Refresh panel init data...\n", tag, + __func__); +#else + logError(1, + "%s %s: Select Full Panel Init...\n", tag, + __func__); + init_type = SPECIAL_FULL_PANEL_INIT; +#endif + } + } else + logError(1, + "%s %s: Error while executing system reset! ERROR %08X\n", + tag, + __func__, ret); /* better skip initialization + * because the real state is + * unknown */ + + + if (init_type != SPECIAL_FULL_PANEL_INIT) { +#if defined(PRE_SAVED_METHOD) || defined(COMPUTE_INIT_METHOD) + if ((systemInfo.u8_cfgAfeVer != systemInfo.u8_cxAfeVer) +#ifdef COMPUTE_INIT_METHOD + || ((systemInfo.u8_mpFlag != MP_FLAG_BOOT) && + (systemInfo.u8_mpFlag != MP_FLAG_FACTORY)) +#endif + ) { + init_type = SPECIAL_FULL_PANEL_INIT; + logError(0, + "%s %s: Different CX AFE Ver: %02X != %02X or invalid MpFlag = %02X... Execute FULL Panel Init!\n", + tag, __func__, systemInfo.u8_cfgAfeVer, + systemInfo.u8_cxAfeVer, systemInfo.u8_mpFlag); + } else +#endif + if (systemInfo.u8_cfgAfeVer != systemInfo.u8_panelCfgAfeVer) { + init_type = SPECIAL_PANEL_INIT; + logError(0, + "%s %s: Different Panel AFE Ver: %02X != %02X... Execute Panel Init!\n", + tag, __func__, systemInfo.u8_cfgAfeVer, + systemInfo.u8_panelCfgAfeVer); + } else + init_type = NO_INIT; + } + +/** + * Note: The fts_chip_initialization function contains the touchscreen calibration + * operation. We close the operation by default, plese be very careful to + * open it if necessary. +*/ + if (bdata->need_tp_cal) { + if (init_type != NO_INIT) { /* initialization status not correct or + * after FW complete update, do + * initialization. */ + logError(1, "%s Note: Do touch calibration...!\n", tag); + error = fts_chip_initialization(info, init_type); + if (error < OK) + logError(1, + "%s %s Cannot initialize the chip ERROR %08X\n", + tag, + __func__, error); + } + } + + error = fts_init_sensing(info); + if (error < OK) + logError(1, + "%s Cannot initialize the hardware device ERROR %08X\n", + tag, + error); + + logError(1, "%s Fw Update Finished! error = %08X\n", tag, error); + return error; +} + +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +#ifndef FW_UPDATE_ON_PROBE +/** + * Function called by the delayed workthread executed after the probe in + * order to perform the fw update flow + * @see fts_fw_update() + */ +static void fts_fw_update_auto(struct work_struct *work) +{ + struct delayed_work *fwu_work = container_of(work, struct delayed_work, + work); + struct fts_ts_info *info = container_of(fwu_work, struct fts_ts_info, + fwu_work); + + fts_fw_update(info); +} +#endif +#endif + +/* TODO: define if need to do the full mp at the boot */ +/** + * Execute the initialization of the IC (supporting a retry mechanism), + * checking also the resulting data + * @see production_test_main() + */ +int fts_chip_initialization(struct fts_ts_info *info, int init_type) +{ + int ret2 = 0; + int retry; + int initretrycnt = 0; + + /* initialization error, retry initialization */ + for (retry = 0; retry < RETRY_INIT_BOOT; retry++) { +#ifndef COMPUTE_INIT_METHOD + ret2 = production_test_initialization(init_type); +#else + ret2 = production_test_main(info->limit_path, 1, init_type, &tests, + MP_FLAG_BOOT); +#endif + if (ret2 == OK) + break; + initretrycnt++; + logError(1, + "%s initialization cycle count = %04d - ERROR %08X\n", + tag, + initretrycnt, ret2); + fts_chip_powercycle(info); + } + if (ret2 < OK) /* initialization error */ + + logError(1, "%s fts initialization failed %d times\n", tag, + RETRY_INIT_BOOT); + + + return ret2; +} + +/** + * Initialize the dispatch table with the event handlers for any possible event + * ID + * Set IRQ pin behavior (level triggered low) + * Register top half interrupt handler function. + * @see fts_interrupt_handler() + */ +static int fts_interrupt_install(struct fts_ts_info *info) +{ + int i, error = 0; + + info->event_dispatch_table = kzalloc(sizeof(event_dispatch_handler_t) * + NUM_EVT_ID, GFP_KERNEL); + + if (!info->event_dispatch_table) { + logError(1, "%s OOM allocating event dispatch table\n", tag); + return -ENOMEM; + } + + for (i = 0; i < NUM_EVT_ID; i++) + info->event_dispatch_table[i] = fts_nop_event_handler; + + install_handler(info, ENTER_POINT, enter_pointer); + install_handler(info, LEAVE_POINT, leave_pointer); + install_handler(info, MOTION_POINT, motion_pointer); + install_handler(info, ERROR, error); + install_handler(info, CONTROLLER_READY, controller_ready); + install_handler(info, STATUS_UPDATE, status); + install_handler(info, USER_REPORT, user_report); + + error = request_threaded_irq(info->client->irq, NULL, fts_event_handler, + IRQF_TRIGGER_LOW|IRQF_ONESHOT, FTS_TS_DRV_NAME, info); + if (error < 0) { + logError(1, "%s Request threaded irq failed\n", tag); + kfree(info->event_dispatch_table); + error = -EBUSY; + } + logError(1, "%s Interrupt Mode Set\n", tag); + + return error; +} + +/** + * Clean the dispatch table and the free the IRQ. + * This function is called when the driver need to be removed + */ +void fts_interrupt_uninstall(struct fts_ts_info *info) +{ + fts_disableInterrupt(); + + kfree(info->event_dispatch_table); + + free_irq(info->client->irq, info); +} + +/**@}*/ + +/** + * This function try to attempt to communicate with the IC for the first time + * during the boot up process in order to read the necessary info for the + * following stages. + * The function execute a system reset, read fundamental info (system info) + * @return OK if success or an error code which specify the type of error + */ +static int fts_init(struct fts_ts_info *info) +{ + int error; + u8 readData[3]; + +#if !defined(I2C_INTERFACE) && defined(SPI4_WIRE) + /* configure manually SPI4 because when no fw is running the chip use + * SPI3 by default */ + u8 cmd[1] = {0x00}; + + logError(0, "%s Setting SPI4 mode...\n", tag); + cmd[0] = 0x10; + error = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG, + ADDR_GPIO_DIRECTION, cmd, 1); + if (error < OK) { + logError(1, "%s can not set gpio dir ERROR %08X\n", + tag, error); + return error; + } + + cmd[0] = 0x02; + error = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG, + ADDR_GPIO_PULLUP, cmd, 1); + if (error < OK) { + logError(1, "%s can not set gpio pull-up ERROR %08X\n", + tag, error); + return error; + } + +#if defined(ALIX) || defined (SALIXP) +#if defined(ALIX) + cmd[0] = 0x70; +#else + cmd[0] = 0x07; +#endif + error = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG, + ADDR_GPIO_CONFIG_REG3, cmd, 1); + if (error < OK) { + logError(1, "%s can not set gpio config ERROR %08X\n", + tag, error); + return error; + } + +#else + cmd[0] = 0x07; + error = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG, + ADDR_GPIO_CONFIG_REG2, cmd, 1); + if (error < OK) { + logError(1, "%s can not set gpio config ERROR %08X\n", + tag, error); + return error; + } +#endif + + cmd[0] = 0x30; + error = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG, + ADDR_GPIO_CONFIG_REG0, cmd, 1); + if (error < OK) { + logError(1, "%s can not set gpio config ERROR %08X\n", + tag, error); + return error; + } + + cmd[0] = SPI4_MASK; + error = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG, ADDR_ICR, cmd, + 1); + if (error < OK) { + logError(1, "%s can not set spi4 mode ERROR %08X\n", + tag, error); + return error; + } + msleep(1); /* wait for the GPIO to stabilize */ +#endif + logError(1, "%s Reading chip id\n", tag); + error = fts_writeReadU8UX(FTS_CMD_HW_REG_R, ADDR_SIZE_HW_REG, + ADDR_DCHIP_ID, readData, 2, DUMMY_FIFO); + logError(1, "%s chip id: %02X %02X!\n",tag, readData[0], readData[1]); + + if((readData[0] != DCHIP_ID_0) || (readData[1] != DCHIP_ID_1)) + { + logError(1, "%s wrong chip id detected!\n", tag); + return ERROR_OP_NOT_ALLOW; + } + else + logError(1, "%s chip id read successful!\n", tag); + + + error = fts_system_reset(); + if (error < OK && isI2cError(error)) { + logError(1, "%s Cannot reset the device! ERROR %08X\n", tag, + error); + return error; + } else { + if (error == (ERROR_TIMEOUT | ERROR_SYSTEM_RESET_FAIL)) { + logError(1, "%s Setting default Sys INFO!\n", tag); + error = defaultSysInfo(0); + } else { + error = readSysInfo(0); /* system reset OK */ + if (error < OK) { + if (!isI2cError(error)) + error = OK; + logError(1, + "%s Cannot read Sys Info! ERROR %08X\n", + tag, + error); + } + } + } + + return error; +} + +static inline void checkRegulatorState(struct fts_ts_info *info) +{ + logError(1, "%s %s: %s, %s:%s\n", tag, info->board->avdd_reg_name, + regulator_is_enabled(info->avdd_reg) ? "on" : "off", + info->board->vdd_reg_name, + regulator_is_enabled(info->vdd_reg) ? "on" : "off"); +} + + +/** + * Execute a power cycle in the IC, toggling the power lines (AVDD and DVDD) + * @param info pointer to fts_ts_info struct which contain information of the + * regulators + * @return 0 if success or another value if fail + */ +int fts_chip_powercycle(struct fts_ts_info *info) +{ + int error = 0; + + logError(1, "%s %s: Power Cycle Starting...\n", tag, __func__); + logError(1, "%s %s: Disabling IRQ...\n", tag, __func__); + /* if IRQ pin is short with DVDD a call to the ISR will triggered when + * the regulator is turned off if IRQ not disabled */ + fts_disableInterruptNoSync(); + + if (info->vdd_reg) { + error = regulator_disable(info->vdd_reg); + if (error < 0) + logError(1, "%s %s: Failed to disable DVDD regulator\n", + tag, __func__); + } + + if (info->avdd_reg) { + error = regulator_disable(info->avdd_reg); + if (error < 0) + logError(1, "%s %s: Failed to disable AVDD regulator\n", + tag, __func__); + } + + if (info->board->reset_gpio != GPIO_NOT_DEFINED) + gpio_set_value(info->board->reset_gpio, 0); + else + msleep(300); + + /* in FTI power up first the digital and then the analog */ + if (info->vdd_reg) { + error = regulator_enable(info->vdd_reg); + if (error < 0) + logError(1, "%s %s: Failed to enable DVDD regulator\n", + tag, __func__); + } + + msleep(1); + + if (info->avdd_reg) { + error = regulator_enable(info->avdd_reg); + if (error < 0) + logError(1, "%s %s: Failed to enable AVDD regulator\n", + tag, __func__); + } + + msleep(5); /* time needed by the regulators for reaching the regime + * values */ + + + if (info->board->reset_gpio != GPIO_NOT_DEFINED) { + msleep(10); /* time to wait before bring up the reset gpio + * after the power up of the regulators */ + gpio_set_value(info->board->reset_gpio, 1); + } + + release_all_touches(info); + + logError(1, "%s %s: Power Cycle Finished! ERROR CODE = %08x\n", tag, + __func__, error); + setSystemResetedUp(1); + setSystemResetedDown(1); + checkRegulatorState(info); + return error; +} + +/** + * Complete the boot up process, initializing the sensing of the IC according + * to the current setting chosen by the host + * Register the notifier for the suspend/resume actions and the event handler + * @return OK if success or an error code which specify the type of error + */ +int fts_init_sensing(struct fts_ts_info *info) +{ + int error = 0; +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + error |= fb_register_client(&info->notifier); /* register the + * suspend/resume + * function */ +#endif + error |= fts_interrupt_install(info); /* register event handler */ + error |= fts_mode_handler(info, 0); + fts_resetDisableIrqCount(); + + if (error < OK) + logError(1, "%s %s Init after Probe error (ERROR = %08X)\n", + tag, __func__, error); + + + return error; +} + +/* TODO: change this function according with the needs of customer in terms of + * feature to enable/disable */ + +/** + * @ingroup mode_section + * @{ + */ +/** + * The function handle the switching of the mode in the IC enabling/disabling + * the sensing and the features set from the host + * @param info pointer to fts_ts_info which contains info about the device and + * its hw setup + * @param force if 1, the enabling/disabling command will be send even + * if the feature was already enabled/disabled otherwise it will judge if + * the feature changed status or the IC had a system reset + * @return OK if success or an error code which specify the type of error + *encountered + */ +static int fts_mode_handler(struct fts_ts_info *info, int force) +{ + int res = OK; + int ret = OK; + u8 settings[4] = { 0 }; + + /* disable irq wake because resuming from gesture mode */ + if (IS_POWER_MODE(info->mode, SCAN_MODE_LOW_POWER) && + (info->resume_bit == 1)) + disable_irq_wake(info->client->irq); + + info->mode = MODE_NOTHING; /* initialize the mode to nothing in + * order to be updated depending on the + * features enabled */ + + logError(0, "%s %s: Mode Handler starting...\n", tag, __func__); + switch (info->resume_bit) { + case 0: /* screen down */ + logError(0, "%s %s: Screen OFF...\n", tag, __func__); + /* do sense off in order to avoid the flooding of the fifo with + * touch events if someone is touching the panel during suspend + */ + logError(0, "%s %s: Sense OFF!\n", tag, __func__); + /* for speed reason (no need to check echo in this case and + * interrupt can be enabled) */ + ret = setScanMode(SCAN_MODE_ACTIVE, 0x00); + res |= ret; /* to avoid warning unsused ret variable when + * all the features are disabled */ + +#ifdef GESTURE_MODE + if (info->gesture_enabled == 1) { + logError(0, "%s %s: enter in gesture mode !\n", tag, + __func__); + res = enterGestureMode(isSystemResettedDown()); + if (res >= OK) { + enable_irq_wake(info->client->irq); + fromIDtoMask(FEAT_SEL_GESTURE, + (u8 *)&info->mode, + sizeof(info->mode)); + MODE_LOW_POWER(info->mode, 0); + } else + logError(1, + "%s %s: enterGestureMode failed! ERROR %08X recovery in senseOff...\n", + tag, __func__, res); + } +#endif + + setSystemResetedDown(0); + break; + + case 1: /* screen up */ + logError(0, "%s %s: Screen ON...\n", tag, __func__); +#ifdef GLOVE_MODE + if ((info->glove_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Glove Mode setting...\n", tag, + __func__); + settings[0] = info->glove_enabled; + /* required to satisfy also the disable case */ + ret = setFeatures(FEAT_SEL_GLOVE, settings, 1); + if (ret < OK) + logError(1, + "%s %s: error during setting GLOVE_MODE! ERROR %08X\n", + tag, __func__, ret); + res |= ret; + + if (ret >= OK && info->glove_enabled == FEAT_ENABLE) { + fromIDtoMask(FEAT_SEL_GLOVE, (u8 *)&info->mode, + sizeof(info->mode)); + logError(1, "%s %s: GLOVE_MODE Enabled!\n", tag, + __func__); + } else + logError(1, "%s %s: GLOVE_MODE Disabled!\n", + tag, __func__); + } + +#endif + +#ifdef COVER_MODE + if ((info->cover_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Cover Mode setting...\n", tag, + __func__); + settings[0] = info->cover_enabled; + ret = setFeatures(FEAT_SEL_COVER, settings, 1); + if (ret < OK) + logError(1, + "%s %s: error during setting COVER_MODE! ERROR %08X\n", + tag, __func__, ret); + res |= ret; + + if (ret >= OK && info->cover_enabled == FEAT_ENABLE) { + fromIDtoMask(FEAT_SEL_COVER, (u8 *)&info->mode, + sizeof(info->mode)); + logError(1, "%s %s: COVER_MODE Enabled!\n", tag, + __func__); + } else + logError(1, "%s %s: COVER_MODE Disabled!\n", + tag, __func__); + } +#endif +#ifdef CHARGER_MODE + if ((info->charger_enabled > 0 && isSystemResettedUp()) || + force == 1) { + logError(0, "%s %s: Charger Mode setting...\n", tag, + __func__); + + settings[0] = info->charger_enabled; + ret = setFeatures(FEAT_SEL_CHARGER, settings, 1); + if (ret < OK) + logError(1, + "%s %s: error during setting CHARGER_MODE! ERROR %08X\n", + tag, __func__, ret); + res |= ret; + + if (ret >= OK && info->charger_enabled == FEAT_ENABLE) { + fromIDtoMask(FEAT_SEL_CHARGER, + (u8 *)&info->mode, + sizeof(info->mode)); + logError(1, "%s %s: CHARGER_MODE Enabled!\n", + tag, __func__); + } else + logError(1, "%s %s: CHARGER_MODE Disabled!\n", + tag, __func__); + } +#endif + + +#ifdef GRIP_MODE + if ((info->grip_enabled == FEAT_ENABLE && + isSystemResettedUp()) || force == 1) { + logError(0, "%s %s: Grip Mode setting...\n", tag, + __func__); + settings[0] = info->grip_enabled; + ret = setFeatures(FEAT_SEL_GRIP, settings, 1); + if (ret < OK) + logError(1, + "%s %s: error during setting GRIP_MODE! ERROR %08X\n", + tag, __func__, ret); + res |= ret; + + if (ret >= OK && info->grip_enabled == FEAT_ENABLE) { + fromIDtoMask(FEAT_SEL_GRIP, (u8 *)&info->mode, + sizeof(info->mode)); + logError(1, "%s %s: GRIP_MODE Enabled!\n", tag, + __func__); + } else + logError(1, "%s %s: GRIP_MODE Disabled!\n", tag, + __func__); + } +#endif + + logError(1, "%s %s: Sense OFF!\n", tag, __func__); + res |= setScanMode(SCAN_MODE_ACTIVE, 0x00); + + /* if some selective scan want to be enabled can be done an or + * of the following options */ + /* settings[0] = ACTIVE_MULTI_TOUCH | ACTIVE_KEY | ACTIVE_HOVER + * | ACTIVE_PROXIMITY | ACTIVE_FORCE; */ + settings[0] = 0xFF; /* enable all the possible scans mode + * supported by the config */ + logError(0, "%s %s: Sense ON!\n", tag, __func__); + res |= setScanMode(SCAN_MODE_ACTIVE, settings[0]); + info->mode |= (SCAN_MODE_ACTIVE << 24); + MODE_ACTIVE(info->mode, settings[0]); + + + setSystemResetedUp(0); + break; + + default: + logError(1, + "%s %s: invalid resume_bit value = %d! ERROR %08X\n", + tag, + __func__, info->resume_bit, ERROR_OP_NOT_ALLOW); + res = ERROR_OP_NOT_ALLOW; + } + + + logError(0, "%s %s: Mode Handler finished! res = %08X mode = %08X\n", + tag, __func__, res, info->mode); + return res; +} + +int fts_pinctrl_state(struct fts_ts_info *info, bool on) +{ + struct pinctrl_state *state_ptr; + const char *state_name; + int error = 0; + + if (info->board->power_on_suspend || !info->ts_pinctrl) + return 0; + + if (on) { + state_name = PINCTRL_STATE_ACTIVE; + state_ptr =info->pinctrl_state_active; + } else { + state_name = PINCTRL_STATE_SUSPEND; + state_ptr =info->pinctrl_state_suspend; + } + + error = pinctrl_select_state(info->ts_pinctrl, state_ptr); + if (error < 0) + logError(1, "%s: Failed to select %s\n", __func__, state_name); + + return error; +} + +int fts_chip_power_switch(struct fts_ts_info *info, bool on) +{ + int error = 0; + + if (on) { + error = regulator_enable(info->avdd_reg); + if (error < 0) + logError(1, "%s %s: Failed to enable AVDD\n", tag, __func__); + + error = regulator_enable(info->vdd_reg); + if (error < 0) + logError(1, "%s %s: Failed to enable DVDD\n", tag, __func__); + } else { + error = regulator_disable(info->vdd_reg); + if (error < 0) + logError(1, "%s %s: Failed to disable DVDD\n", tag, __func__); + + error = regulator_disable(info->avdd_reg); + if (error < 0) + logError(1, "%s %s: Failed to disable AVDD\n", tag, __func__); + } + + return error; +} + +void fts_resume_func(struct fts_ts_info *info) +{ + logError(1, "%s %s: enter\n", tag, __func__); + PM_WAKEUP_EVENT(info->wakesrc, jiffies_to_msecs(HZ)); + if (info->board->power_on_suspend || !info->ts_pinctrl) + fts_system_reset(); + release_all_touches(info); + info->resume_bit = 1; + fts_mode_handler(info, 0); + info->sensor_sleep = false; + fts_enableInterrupt(); +#if 0 + u8 cmd[3] = { FTS_CMD_SYSTEM, SYS_CMD_INT, 1 }; + int res; + + res = fts_write(cmd, sizeof(cmd)); + if (res < OK) + logError(1, "%s %s failed to enable FW INT %d\n", tag , __func__, res); +#endif +} + +/** + * Resume work function which perform a system reset, clean all the touches + *from the linux input system and prepare the ground for enabling the sensing + */ +static void inline fts_resume_work(struct work_struct *work) +{ + fts_resume_func(container_of(work, struct fts_ts_info, resume_work)); +} + +void fts_suspend_func(struct fts_ts_info *info) +{ + logError(1, "%s %s: enter\n", tag, __func__); + PM_STAY_AWAKE(info->wakesrc); + info->resume_bit = 0; + fts_mode_handler(info, 0); + release_all_touches(info); + info->sensor_sleep = true; + PM_RELAX(info->wakesrc); +} + +/** + * Suspend work function which clean all the touches from Linux input system + *and prepare the ground to disabling the sensing or enter in gesture mode + */ +static void inline fts_suspend_work(struct work_struct *work) +{ + fts_suspend_func(container_of(work, struct fts_ts_info, suspend_work)); +} + +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +/** @}*/ + +/** + * Callback function used to detect the suspend/resume events generated by + * clicking the power button. + * This function schedule a suspend or resume work according to the event + * received. + */ +static int fts_fb_state_chg_callback(struct notifier_block *nb, unsigned long + val, void *data) +{ + struct fts_ts_info *info = container_of(nb, struct fts_ts_info, + notifier); + struct fb_event *evdata = data; + unsigned int blank; + + + if (val != FB_EVENT_BLANK) + return 0; + + logError(0, "%s %s: fts notifier begin!\n", tag, __func__); + + if (evdata && evdata->data && val == FB_EVENT_BLANK && info) { + blank = *(int *)(evdata->data); + + + switch (blank) { + case FB_BLANK_POWERDOWN: + if (info->sensor_sleep) + break; + + logError(0, "%s %s: FB_BLANK_POWERDOWN\n", tag, + __func__); + + queue_work(info->event_wq, &info->suspend_work); + + break; + + case FB_BLANK_UNBLANK: + if (!info->sensor_sleep) + break; + + logError(0, "%s %s: FB_BLANK_UNBLANK\n", tag, __func__); + + queue_work(info->event_wq, &info->resume_work); + break; + default: + break; + } + } + return NOTIFY_OK; +} + +static struct notifier_block fts_noti_block = { + .notifier_call = fts_fb_state_chg_callback, +}; +#endif + +/** + * From the name of the power regulator get/put the actual regulator structs + * (copying their references into fts_ts_info variable) + * @param info pointer to fts_ts_info which contains info about the device and + * its hw setup + * @param get if 1, the regulators are get otherwise they are put (released) + * back to the system + * @return OK if success or an error code which specify the type of error + */ +static int fts_get_reg(struct fts_ts_info *info, bool get) +{ + int retval; + const struct fts_hw_platform_data *bdata = info->board; + + if (!get) { + retval = 0; + goto regulator_put; + } + + if ((bdata->vdd_reg_name != NULL) && (*bdata->vdd_reg_name != 0)) { + info->vdd_reg = regulator_get(info->dev, bdata->vdd_reg_name); + if (IS_ERR(info->vdd_reg)) { + logError(1, "%s %s: Failed to get power regulator\n", + tag, + __func__); + retval = PTR_ERR(info->vdd_reg); + goto regulator_put; + } + } + + if ((bdata->avdd_reg_name != NULL) && (*bdata->avdd_reg_name != 0)) { + info->avdd_reg = regulator_get(info->dev, bdata->avdd_reg_name); + if (IS_ERR(info->avdd_reg)) { + logError(1, + "%s %s: Failed to get bus pullup regulator\n", + tag, + __func__); + retval = PTR_ERR(info->avdd_reg); + goto regulator_put; + } + } + + return OK; + +regulator_put: + if (info->vdd_reg) { + regulator_put(info->vdd_reg); + info->vdd_reg = NULL; + } + + if (info->avdd_reg) { + regulator_put(info->avdd_reg); + info->avdd_reg = NULL; + } + + return retval; +} + + +/** + * Enable or disable the power regulators + * @param info pointer to fts_ts_info which contains info about the device and + * its hw setup + * @param enable if 1, the power regulators are turned on otherwise they are + * turned off + * @return OK if success or an error code which specify the type of error + */ +static int fts_enable_reg(struct fts_ts_info *info, bool enable) +{ + int retval; + + if (!enable) { + retval = 0; + goto disable_pwr_reg; + } + + if (info->vdd_reg) { + retval = regulator_enable(info->vdd_reg); + if (retval < 0) { + logError(1, "%s %s: Failed to enable bus regulator\n", + tag, + __func__); + goto exit; + } + } + + if (info->avdd_reg) { + retval = regulator_enable(info->avdd_reg); + if (retval < 0) { + logError(1, "%s %s: Failed to enable power regulator\n", + tag, + __func__); + goto disable_bus_reg; + } + } + + checkRegulatorState(info); + return OK; + +disable_pwr_reg: + if (info->avdd_reg) + regulator_disable(info->avdd_reg); + +disable_bus_reg: + if (info->vdd_reg) + regulator_disable(info->vdd_reg); +exit: + checkRegulatorState(info); + return retval; +} + +/** + * Configure a GPIO according to the parameters + * @param gpio gpio number + * @param config if true, the gpio is set up otherwise it is free + * @param dir direction of the gpio, 0 = in, 1 = out + * @param state initial value (if the direction is in, this parameter is + * ignored) + * return error code + */ +static int fts_gpio_setup(int gpio, bool config, int dir, int state) +{ + int retval = 0; + unsigned char buf[16]; + + if (config) { + snprintf(buf, 16, "fts_gpio_%u\n", gpio); + + retval = gpio_request(gpio, buf); + if (retval) { + logError(1, "%s %s: Failed to get gpio %d (code: %d)", + tag, + __func__, gpio, retval); + return retval; + } + + if (dir == 0) + retval = gpio_direction_input(gpio); + else + retval = gpio_direction_output(gpio, state); + if (retval) { + logError(1, "%s %s: Failed to set gpio %d direction", + tag, + __func__, gpio); + return retval; + } + } else + gpio_free(gpio); + + return retval; +} + +/** + * Setup the IRQ and RESET (if present) gpios. + * If the Reset Gpio is present it will perform a cycle HIGH-LOW-HIGH in order + *to assure that the IC has been reset properly + */ +static int fts_set_gpio(struct fts_ts_info *info) +{ + int retval; + struct fts_hw_platform_data *bdata = info->board; + + retval = fts_gpio_setup(bdata->irq_gpio, true, 0, 0); + if (retval < 0) { + logError(1, "%s %s: Failed to configure irq GPIO\n", tag, + __func__); + goto err_gpio_irq; + } + + if (bdata->reset_gpio >= 0) { + retval = fts_gpio_setup(bdata->reset_gpio, true, 1, 0); + if (retval < 0) { + logError(1, "%s %s: Failed to configure reset GPIO\n", + tag, __func__); + goto err_gpio_reset; + } + } + if (bdata->reset_gpio >= 0) { + gpio_set_value(bdata->reset_gpio, 0); + msleep(10); + gpio_set_value(bdata->reset_gpio, 1); + } + + return OK; + +err_gpio_reset: + fts_gpio_setup(bdata->irq_gpio, false, 0, 0); + bdata->reset_gpio = GPIO_NOT_DEFINED; +err_gpio_irq: + return retval; +} + +/** + * Retrieve and parse the hw information from the device tree node defined in + * the system. + * the most important information to obtain are: IRQ and RESET gpio numbers, + * power regulator names + * In the device file node is possible to define additional optional + *information that can be parsed here. + */ +static int parse_dt(struct device *dev, struct fts_hw_platform_data *bdata) +{ + int retval; + const char *name; + struct device_node *np = dev->of_node; + u32 temp_val; + + bdata->irq_gpio = of_get_named_gpio_flags(np, "st,irq-gpio", 0, NULL); + + logError(0, "%s irq_gpio = %d\n", tag, bdata->irq_gpio); + + + retval = of_property_read_string(np, "st,regulator_dvdd", &name); + if (retval == -EINVAL) + bdata->vdd_reg_name = NULL; + else if (retval < 0) + return retval; + else { + bdata->vdd_reg_name = name; + logError(0, "%s pwr_reg_name = %s\n", tag, name); + } + + retval = of_property_read_string(np, "st,regulator_avdd", &name); + if (retval == -EINVAL) + bdata->avdd_reg_name = NULL; + else if (retval < 0) + return retval; + else { + bdata->avdd_reg_name = name; + logError(0, "%s bus_reg_name = %s\n", tag, name); + } + + if (of_property_read_bool(np, "st,reset-gpio")) { + bdata->reset_gpio = of_get_named_gpio_flags(np, + "st,reset-gpio", 0, + NULL); + logError(0, "%s reset_gpio =%d\n", tag, bdata->reset_gpio); + } else + bdata->reset_gpio = GPIO_NOT_DEFINED; + + retval = of_property_read_u32(np, "st,x-max", &temp_val); + if (retval < 0) + bdata->x_max = X_AXIS_MAX; + else + bdata->x_max = temp_val; + + retval = of_property_read_u32(np, "st,y-max", &temp_val); + if (retval < 0) + bdata->y_max = Y_AXIS_MAX; + else + bdata->y_max = temp_val; + logError(0, "%s x_max =%d y_max =%d\n", tag, bdata->x_max, bdata->y_max); + + if (of_property_read_bool(np, "st,y-flip")) { + bdata->y_flip = true; + logError(0, "%s flip Y\n", tag); + } + + if (of_property_read_bool(np, "st,x-flip")) { + bdata->x_flip = true; + logError(0, "%s flip X\n", tag); + } + + if (of_property_read_bool(np, "st,need_tp_cal")) { + bdata->need_tp_cal = true; + logError(0, "%s Need to do calibraion after fw upgrade"); + } + + if (of_property_read_u8_array(np, "st,interpolation_cmd", + bdata->interpolation_cmd, 10) == 0) { + bdata->interpolation_ctrl = true; + logError(1, "%s Support report rate interpolation.\n", tag); + } + + if (of_property_read_u8_array(np, "st,jitter_cmd", + bdata->jitter_cmd, 8) == 0) { + bdata->jitter_ctrl = true; + logError(1, "%s Support set jitter.\n", tag); + } + + if (of_property_read_u8_array(np, "st,linearity_cmd", + bdata->linearity_cmd, 3) == 0) { + bdata->linearity_ctrl = true; + logError(1, "%s Support set linearity.\n", tag); + } + + if (of_property_read_u8_array(np, "st,first_filter_cmd", + bdata->first_filter_cmd, 4*2) == 0) { + bdata->first_filter_ctrl = true; + logError(1, "%s Support set first_filter.\n", tag); + } + + if (of_property_read_u8_array(np, "st,report_rate_cmd", + bdata->report_rate_cmd, 3) == 0) { + bdata->report_rate_ctrl = true; + logError(1, "%s Support report rate switching.\n", tag); + } + + if (of_property_read_u8_array(np, "st,edge_cmd", + bdata->edge_cmd, 4) == 0) { + bdata->edge_ctrl = true; + logError(1, "%s Support edge switching.\n", tag); + } + + if (of_property_read_u8_array(np, "st,sensitivity_cmd", + bdata->sensitivity_cmd, 3) == 0) { + bdata->sensitivity_ctrl = true; + logError(1, "%s Support film sensitivity switching.\n", tag); + } + + return OK; +} + +static int fts_pinctrl_init(struct fts_ts_info *info) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + info->ts_pinctrl = devm_pinctrl_get(info->dev); + if (IS_ERR_OR_NULL(info->ts_pinctrl)) { + retval = PTR_ERR(info->ts_pinctrl); + logError(1, "%s Target does not use pinctrl %d\n", tag, retval); + goto err_pinctrl_get; + } + + info->pinctrl_state_active + = pinctrl_lookup_state(info->ts_pinctrl, PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(info->pinctrl_state_active)) { + retval = PTR_ERR(info->pinctrl_state_active); + logError(1, "%s Can not lookup %s pinstate %d\n", tag, + PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + info->pinctrl_state_suspend + = pinctrl_lookup_state(info->ts_pinctrl, PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(info->pinctrl_state_suspend)) { + retval = PTR_ERR(info->pinctrl_state_suspend); + logError(1, "%s Can not lookup %s pinstate %d\n", tag, + PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } +#if 0 + info->pinctrl_state_release + = pinctrl_lookup_state(info->ts_pinctrl, PINCTRL_STATE_RELEASE); + if (IS_ERR_OR_NULL(info->pinctrl_state_release)) { + retval = PTR_ERR(info->pinctrl_state_release); + logError(1, "%s Can not lookup %s pinstate %d\n", tag, + PINCTRL_STATE_RELEASE, retval); + } +#endif + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(info->ts_pinctrl); +err_pinctrl_get: + info->ts_pinctrl = NULL; + return retval; +} + +static int fts_mmi_set_limit_name(struct fts_ts_info *ts) +{ +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) && defined(CONFIG_ST_LIMIT_USE_SUPPLIER) + int ret; + const char* supplier = NULL; + if (ts->imports && ts->imports->get_supplier) { + ret = ts->imports->get_supplier(ts->dev, &supplier); + if (!ret) + snprintf(ts->limit_path, MAX_LIMIT_FILE_NAME, "%s_%s", + supplier, LIMITS_FILE); + } else + snprintf(ts->limit_path, MAX_LIMIT_FILE_NAME, "%s", LIMITS_FILE); +#else + snprintf(ts->limit_path, MAX_LIMIT_FILE_NAME, "%s", LIMITS_FILE); +#endif + + logError(1, "%s limit file path: %s\n", tag, ts->limit_path); + return 0; +} + +/** + * Probe function, called when the driver it is matched with a device with the + *same name compatible name + * This function allocate, initialize and define all the most important + *function and flow that are used by the driver to operate with the IC. + * It allocates device variables, initialize queues and schedule works, + *registers the IRQ handler, suspend/resume callbacks, registers the device to + *the linux input subsystem etc. + */ +#ifdef I2C_INTERFACE +static int fts_probe(struct i2c_client *client, const struct i2c_device_id *idp) +{ +#else +static int fts_probe(struct spi_device *client) +{ +#endif + + struct fts_ts_info *info = NULL; + int error = 0; + struct device_node *dp = client->dev.of_node; + int retval; + int skip_5_1 = 0; + u16 bus_type; + + logError(1, "%s %s: driver probe begin!\n", tag, __func__); + if (client->dev.of_node && !mmi_device_is_available(client->dev.of_node)) { + logError(1, "%s mmi: device not supported\n", tag); + return -ENODEV; + } + + logError(1, "%s driver ver. %s\n", tag, FTS_TS_DRV_VERSION); + logError(1, "%s SET Bus Functionality :\n", tag); +#ifdef I2C_INTERFACE + logError(1, "%s I2C interface...\n", tag); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + logError(1, "%s Unsupported I2C functionality\n", tag); + error = -EIO; + goto ProbeErrorExit_0; + } + + logError(1, "%s i2c address: %x\n", tag, client->addr); + bus_type = BUS_I2C; +#else + logError(1, "%s SPI interface...\n", tag); + client->mode = SPI_MODE_0; +#ifndef SPI4_WIRE + client->mode |= SPI_3WIRE; +#endif + client->max_speed_hz = SPI_CLOCK_FREQ; + client->bits_per_word = 8; + if (spi_setup(client) < 0) { + logError(1, "%s Unsupported SPI functionality\n", tag); + error = -EIO; + goto ProbeErrorExit_0; + } + bus_type = BUS_SPI; +#endif + + + logError(1, "%s SET Device driver INFO:\n", tag); + + + info = kzalloc(sizeof(struct fts_ts_info), GFP_KERNEL); + if (!info) { + logError(1, + "%s Out of memory... Impossible to allocate struct info!\n", + tag); + error = -ENOMEM; + goto ProbeErrorExit_0; + } + + info->client = client; + info->dev = &info->client->dev; + + dev_set_drvdata(info->dev, info); + + if (dp) { + info->board = devm_kzalloc(&client->dev, + sizeof(struct fts_hw_platform_data), + GFP_KERNEL); + if (!info->board) { + logError(1, "%s ERROR:info.board kzalloc failed\n", + tag); + goto ProbeErrorExit_1; + } + parse_dt(&client->dev, info->board); + } + + logError(1, "%s SET Regulators:\n", tag); + retval = fts_get_reg(info, true); + if (retval < 0) { + logError(1, "%s ERROR: %s: Failed to get regulators\n", tag, + __func__); + goto ProbeErrorExit_1; + } + + retval = fts_enable_reg(info, true); + if (retval < 0) { + logError(1, "%s %s: ERROR Failed to enable regulators\n", tag, + __func__); + goto ProbeErrorExit_2; + } + + logError(1, "%s SET GPIOS:\n", tag); + retval = fts_set_gpio(info); + if (retval < 0) { + logError(1, "%s %s: ERROR Failed to set up GPIO's\n", tag, + __func__); + goto ProbeErrorExit_2; + } + info->client->irq = gpio_to_irq(info->board->irq_gpio); + + retval = fts_pinctrl_init(info); + if (!retval && info->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 */ + retval = pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_active); + if (retval < 0) + logError(1, "%s %s: Failed to select %s\n", tag, + __func__, PINCTRL_STATE_ACTIVE); + } + + logError(1, "%s SET Event Handler:\n", tag); + + PM_WAKEUP_REGISTER(info->dev, info->wakesrc, "fts_tp"); + if (!info->wakesrc) { + logError(1, "%s ERROR: Cannot allocate wake lock\n", tag); + error = -ENOMEM; + goto ProbeErrorExit_2; + } + + info->event_wq = alloc_workqueue("fts-event-queue", WQ_UNBOUND | + WQ_HIGHPRI | WQ_CPU_INTENSIVE, 1); + if (!info->event_wq) { + logError(1, "%s ERROR: Cannot create work thread\n", tag); + error = -ENOMEM; + goto ProbeErrorExit_4; + } + + INIT_WORK(&info->resume_work, fts_resume_work); + INIT_WORK(&info->suspend_work, fts_suspend_work); + + info->dev = &info->client->dev; + + logError(1, "%s SET Input Device Property:\n", tag); + info->input_dev = input_allocate_device(); + if (!info->input_dev) { + logError(1, "%s ERROR: No such input device defined!\n", tag); + error = -ENODEV; + goto ProbeErrorExit_5; + } + info->input_dev->dev.parent = &client->dev; + info->input_dev->name = FTS_TS_DRV_NAME; + snprintf(fts_ts_phys, sizeof(fts_ts_phys), "%s/input0", + info->input_dev->name); + info->input_dev->phys = fts_ts_phys; + info->input_dev->id.bustype = bus_type; + info->input_dev->id.vendor = 0x0001; + info->input_dev->id.product = 0x0002; + info->input_dev->id.version = 0x0100; + + __set_bit(EV_SYN, info->input_dev->evbit); + __set_bit(EV_KEY, info->input_dev->evbit); + __set_bit(EV_ABS, info->input_dev->evbit); + __set_bit(BTN_TOUCH, info->input_dev->keybit); + /* __set_bit(BTN_TOOL_FINGER, info->input_dev->keybit); */ + /* __set_bit(BTN_TOOL_PEN, info->input_dev->keybit); */ + + input_mt_init_slots(info->input_dev, TOUCH_ID_MAX, INPUT_MT_DIRECT); + + /* input_mt_init_slots(info->input_dev, TOUCH_ID_MAX); */ + + input_set_abs_params(info->input_dev, ABS_MT_POSITION_X, X_AXIS_MIN, + info->board->x_max - 1, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_POSITION_Y, Y_AXIS_MIN, + info->board->y_max - 1, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_TOUCH_MAJOR, AREA_MIN, + AREA_MAX, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_TOUCH_MINOR, AREA_MIN, + AREA_MAX, 0, 0); + /* + input_set_abs_params(info->input_dev, ABS_MT_PRESSURE, PRESSURE_MIN, + PRESSURE_MAX, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_DISTANCE, DISTANCE_MIN, + DISTANCE_MAX, 0, 0); + */ + +#ifdef GESTURE_MODE + input_set_capability(info->input_dev, EV_KEY, KEY_WAKEUP); + + input_set_capability(info->input_dev, EV_KEY, KEY_M); + input_set_capability(info->input_dev, EV_KEY, KEY_O); + input_set_capability(info->input_dev, EV_KEY, KEY_E); + input_set_capability(info->input_dev, EV_KEY, KEY_W); + input_set_capability(info->input_dev, EV_KEY, KEY_C); + input_set_capability(info->input_dev, EV_KEY, KEY_L); + input_set_capability(info->input_dev, EV_KEY, KEY_F); + input_set_capability(info->input_dev, EV_KEY, KEY_V); + input_set_capability(info->input_dev, EV_KEY, KEY_S); + input_set_capability(info->input_dev, EV_KEY, KEY_Z); + input_set_capability(info->input_dev, EV_KEY, KEY_WWW); + + input_set_capability(info->input_dev, EV_KEY, KEY_LEFT); + input_set_capability(info->input_dev, EV_KEY, KEY_RIGHT); + input_set_capability(info->input_dev, EV_KEY, KEY_UP); + input_set_capability(info->input_dev, EV_KEY, KEY_DOWN); + + input_set_capability(info->input_dev, EV_KEY, KEY_F1); + input_set_capability(info->input_dev, EV_KEY, KEY_F2); + input_set_capability(info->input_dev, EV_KEY, KEY_F3); + input_set_capability(info->input_dev, EV_KEY, KEY_F4); + input_set_capability(info->input_dev, EV_KEY, KEY_F5); + + input_set_capability(info->input_dev, EV_KEY, KEY_LEFTBRACE); + input_set_capability(info->input_dev, EV_KEY, KEY_RIGHTBRACE); +#endif + +#ifdef PHONE_KEY + /* KEY associated to the touch screen buttons */ + input_set_capability(info->input_dev, EV_KEY, KEY_HOMEPAGE); + input_set_capability(info->input_dev, EV_KEY, KEY_BACK); + input_set_capability(info->input_dev, EV_KEY, KEY_MENU); +#endif + + mutex_init(&(info->input_report_mutex)); + + +#ifdef GESTURE_MODE + mutex_init(&gestureMask_mutex); +#endif + + /* register the multi-touch input device */ + error = input_register_device(info->input_dev); + if (error) { + logError(1, "%s ERROR: No such input device\n", tag); + error = -ENODEV; + goto ProbeErrorExit_5_1; + } + + skip_5_1 = 1; + /* track slots */ + info->touch_id = 0; +#ifdef STYLUS_MODE + info->stylus_id = 0; +#endif + + + /* init feature switches (by default all the features are disable, + * if one feature want to be enabled from the start, + * set the corresponding value to 1)*/ + info->gesture_enabled = 0; + info->glove_enabled = 0; + info->charger_enabled = 0; + info->cover_enabled = 0; + info->grip_enabled = 0; + + info->resume_bit = 1; +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + info->notifier = fts_noti_block; +#endif + logError(1, "%s Init Core Lib:\n", tag); + initCore(info); + /* init hardware device */ + logError(1, "%s Device Initialization:\n", tag); + error = fts_init(info); + if (error < OK) { + logError(1, "%s Cannot initialize the device ERROR %08X\n", tag, + error); + error = -ENODEV; + goto ProbeErrorExit_6; + } + +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +#if defined(FW_UPDATE_ON_PROBE) && defined(FW_H_FILE) + logError(1, "%s FW Update and Sensing Initialization:\n", tag); + error = fts_fw_update(info); + if (error < OK) { + logError(1, + "%s Cannot execute fw upgrade the device ERROR %08X\n", + tag, + error); + error = -ENODEV; + goto ProbeErrorExit_7; + } + +#else + logError(1, "%s SET Auto Fw Update:\n", tag); + info->fwu_workqueue = alloc_workqueue("fts-fwu-queue", WQ_UNBOUND | + WQ_HIGHPRI | WQ_CPU_INTENSIVE, 1); + if (!info->fwu_workqueue) { + logError(1, "%s ERROR: Cannot create fwu work thread\n", tag); + goto ProbeErrorExit_7; + } + INIT_DELAYED_WORK(&info->fwu_work, fts_fw_update_auto); +#endif +#endif + logError(1, "%s SET Device File Nodes:\n", tag); + /* sysfs stuff */ + info->attrs.attrs = fts_attr_group; + error = sysfs_create_group(&client->dev.kobj, &info->attrs); + if (error) { + logError(1, "%s ERROR: Cannot create sysfs structure!\n", tag); + error = -ENODEV; + goto ProbeErrorExit_7; + } + + error = fts_proc_init(); + if (error < OK) + logError(1, "%s Error: can not create /proc file!\n", tag); + +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +#ifdef FW_UPDATE_ON_PROBE + queue_delayed_work(info->fwu_workqueue, &info->fwu_work, + msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); +#endif +#endif + /* register touchscreen class if provisioned */ + fts_mmi_init(info, true); + fts_mmi_set_limit_name(info); + + logError(1, "%s Probe Finished!\n", tag); + return OK; + + +ProbeErrorExit_7: + fts_mmi_init(info, false); + +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +#ifdef FW_UPDATE_ON_PROBE + fb_unregister_client(&info->notifier); +#endif +#endif + +ProbeErrorExit_6: + input_unregister_device(info->input_dev); + +ProbeErrorExit_5_1: + if (skip_5_1 != 1) + input_free_device(info->input_dev); + +ProbeErrorExit_5: + destroy_workqueue(info->event_wq); + +ProbeErrorExit_4: + /* destroy_workqueue(info->fwu_workqueue); */ + PM_WAKEUP_UNREGISTER(info->wakesrc); + + fts_enable_reg(info, false); + +ProbeErrorExit_2: + fts_get_reg(info, false); + +ProbeErrorExit_1: + kfree(info); + +ProbeErrorExit_0: + logError(1, "%s Probe Failed!\n", tag); + + return error; +} + + +/** + * Clear and free all the resources associated to the driver. + * This function is called when the driver need to be removed. + */ +#ifdef I2C_INTERFACE +static int fts_remove(struct i2c_client *client) +{ +#else +static int fts_remove(struct spi_device *client) +{ +#endif + + struct fts_ts_info *info = dev_get_drvdata(&(client->dev)); + + fts_proc_remove(); + + /* sysfs stuff */ + sysfs_remove_group(&client->dev.kobj, &info->attrs); + + /* remove interrupt and event handlers */ + fts_interrupt_uninstall(info); + +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + fb_unregister_client(&info->notifier); +#endif + /* unregister the device */ + input_unregister_device(info->input_dev); + + /* input_free_device(info->input_dev ); */ + + /* Remove the work thread */ + destroy_workqueue(info->event_wq); + PM_WAKEUP_UNREGISTER(info->wakesrc); +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +#ifndef FW_UPDATE_ON_PROBE + destroy_workqueue(info->fwu_workqueue); +#endif +#endif + fts_enable_reg(info, false); + fts_get_reg(info, false); + + fts_mmi_init(info, false); + + /* free all */ + kfree(info); + + return OK; +} + +/** + * Struct which contains the compatible names that need to match with + * the definition of the device in the device tree node + */ +static struct of_device_id fts_of_match_table[] = { + { + .compatible = "st,fts", + }, + {}, +}; + +#ifdef I2C_INTERFACE +static const struct i2c_device_id fts_device_id[] = { + { FTS_TS_DRV_NAME, 0 }, + {} +}; + +static struct i2c_driver fts_i2c_driver = { + .driver = { + .name = FTS_TS_DRV_NAME, + .of_match_table = fts_of_match_table, + }, + .probe = fts_probe, + .remove = fts_remove, + .id_table = fts_device_id, +}; +#else +static struct spi_driver fts_spi_driver = { + .driver = { + .name = FTS_TS_DRV_NAME, + .of_match_table = fts_of_match_table, + .owner = THIS_MODULE, + }, + .probe = fts_probe, + .remove = fts_remove, +}; +#endif + + + + +static int __init fts_driver_init(void) +{ +#ifdef I2C_INTERFACE + if (mmi_check_dynamic_device_node("st_fts")) + return i2c_add_driver(&fts_i2c_driver); + return -ENODEV; +#else + return spi_register_driver(&fts_spi_driver); +#endif +} + +static void __exit fts_driver_exit(void) +{ +#ifdef I2C_INTERFACE + i2c_del_driver(&fts_i2c_driver); +#else + spi_unregister_driver(&fts_spi_driver); +#endif +} + + +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +MODULE_DESCRIPTION("STMicroelectronics MultiTouch IC Driver"); +MODULE_AUTHOR("STMicroelectronics"); +MODULE_LICENSE("GPL"); + +late_initcall(fts_driver_init); +module_exit(fts_driver_exit); diff --git a/drivers/input/touchscreen/stmicro_mmi/fts.h b/drivers/input/touchscreen/stmicro_mmi/fts.h new file mode 100644 index 000000000000..dff0cc880c15 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts.h @@ -0,0 +1,403 @@ +/* + * fts.c + * + * FTS Capacitive touch screen controller (FingerTipS) + * + * Copyright (C) 2017, STMicroelectronics + * Authors: AMG(Analog Mems Group) + * + * marco.cali@st.com + * + * 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. + * + * THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE + * PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT. + * AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT, + * INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM + *THE + * CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING + * INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + * + * THIS SOFTWARE IS SPECIFICALLY DESIGNED FOR EXCLUSIVE USE WITH ST PARTS. + */ + +/*! + * \file fts.h + * \brief Contains all the definitions and structs used generally by the driver + */ + +#ifndef _LINUX_FTS_I2C_H_ +#define _LINUX_FTS_I2C_H_ + +#include +#include "fts_lib/ftsSoftware.h" +#include "fts_lib/ftsHardware.h" +#include "fts_lib/ftsTest.h" + +/****************** CONFIGURATION SECTION ******************/ +/** @defgroup conf_section Driver Configuration Section + * Settings of the driver code in order to suit the HW set up and the + *application behavior + * @{ + */ +/* **** CODE CONFIGURATION **** */ +#define FTS_TS_DRV_NAME "fts" /* /< driver name */ +#define FTS_TS_DRV_VERSION "5.2.19" /* /< driver version string format */ +#define FTS_TS_DRV_VER 0x05021300 /* driver version u32 format */ + +#define DEBUG /* /< define to print more logs in the kernel log and better + * follow the code flow */ + +#define DRIVER_TEST /* /< if defined allow to use and test special functions + * of the driver and fts_lib from comand shell (usefull + * for enginering/debug operations) */ + + +/* If both COMPUTE_INIT_METHOD and PRE_SAVED_METHOD are not defined, + * driver will be automatically configured as GOLDEN_VALUE_METHOD */ +/*#define COMPUTE_INIT_METHOD*/ /* Allow to compute init data on phone during + production */ +#ifndef COMPUTE_INIT_METHOD + #define PRE_SAVED_METHOD /* Pre-Saved Method used + * during production */ +#endif + +/* #define FW_H_FILE */ /* include the FW data as header file */ +#ifdef FW_H_FILE + #define FW_SIZE_NAME myArray_size /* /< name of the variable in + * the FW header file which + * specified the dimension of + * the FW data array */ + #define FW_ARRAY_NAME myArray /* /< name of the variable in the FW + * header file which specified the FW + * data array */ +/* #define FW_UPDATE_ON_PROBE */ + /* if defined the FW update will be execute on the probe, if not it will be + * executed EXP_FN_WORK_DELAY_MS ms after the probe is completed */ +#endif + +#ifndef FW_UPDATE_ON_PROBE +/* #define LIMITS_H_FILE */ +/* include the Production Limit File as header file, can be commented to use a + * .csv file instead */ +#ifdef LIMITS_H_FILE + #define LIMITS_SIZE_NAME myArray2_size /* /< name of the + * variable in the + * limits header file + * which specified the + * dimension of the + * limits data array */ + #define LIMITS_ARRAY_NAME myArray2 /* /< name of the + * variable in the + * limits header file + * which specified the + * limits data array */ +#endif +#else +/* if execute fw update in the probe the limit file must be a .h */ +#define LIMITS_H_FILE /* /< include the Production Limit File as header file, + * DO NOT COMMENT! */ +#define LIMITS_SIZE_NAME myArray2_size /* /< name of the + * variable in the + * limits header file + * which specified the + * dimension of the + * limits data array */ +#define LIMITS_ARRAY_NAME myArray2 /* /< name of the + * variable in the + * limits header file + * which specified the + * limits data array */ +#endif + +#define USE_ONE_FILE_NODE +/* allow to enable/disable all the features just using one file node */ + +#ifndef FW_UPDATE_ON_PROBE +#define EXP_FN_WORK_DELAY_MS 1000 /* /< time in ms elapsed after the probe + * to start the work which execute FW + * update and the Initialization of the + * IC */ +#endif + +/* **** END **** */ + + +/* **** FEATURES USED IN THE IC **** */ +/* Enable the support of keys */ +/* #define PHONE_KEY */ + +#define GESTURE_MODE /* /< enable the support of the gestures */ +#ifdef GESTURE_MODE + #define USE_GESTURE_MASK /* /< the gestures to select are + * referred using a gesture bitmask + * instead of their gesture IDs */ +#endif + + +#define CHARGER_MODE /* /< enable the support to charger mode feature + * (comment to disable) */ + +#define GLOVE_MODE /* /< enable the support to glove mode feature (comment + * to disable) */ + +#define COVER_MODE /* /< enable the support to cover mode feature (comment + * to disable) */ + +#define STYLUS_MODE /* /< enable the support to stylus mode feature (comment + * to disable) */ + +#define GRIP_MODE /* /< enable the support to grip mode feature (comment + * to disable) */ + + +/* **** END **** */ + + +/* **** PANEL SPECIFICATION **** */ +#define X_AXIS_MAX 1440 /* /< Max X coordinate of the display */ +#define X_AXIS_MIN 0 /* /< min X coordinate of the display */ +#define Y_AXIS_MAX 2959 /* /< Max Y coordinate of the display */ +#define Y_AXIS_MIN 0 /* /< min Y coordinate of the display */ + +#define PRESSURE_MIN 0 /* /< min value of pressure reported */ +#define PRESSURE_MAX 127 /* /< Max value of pressure reported */ + +#define DISTANCE_MIN 0 /* /< min distance between the tool and the + * display */ +#define DISTANCE_MAX 127 /* /< Max distance between the tool and the + * display */ + +#define TOUCH_ID_MAX 10 /* /< Max number of simoultaneous touches + * reported */ + +#define AREA_MIN PRESSURE_MIN /* /< min value of Major/minor axis + * reported */ +#define AREA_MAX PRESSURE_MAX /* /< Man value of Major/minor axis + * reported */ +/* **** END **** */ +/**@}*/ +/*********************************************************/ + + +/* + * Configuration mode + * + * bitmask which can assume the value defined as features in ftsSoftware.h or + * the following values + */ + +/** @defgroup mode_section IC Status Mode + * Bitmask which keeps track of the features and working mode enabled in the + * IC. + * The meaning of the the LSB of the bitmask must be interpreted considering + * that the value defined in @link feat_opt Feature Selection Option @endlink + * correspond to the position of the corresponding bit in the mask + * @{ + */ +#define MODE_NOTHING 0x00000000 /* /< nothing enabled (sense off) */ +#define MODE_ACTIVE(_mask, _sett) (_mask |= (SCAN_MODE_ACTIVE << 24) | \ + (_sett << 16)) +/* /< store the status of scan mode active and its setting */ +#define MODE_LOW_POWER(_mask, _sett) (_mask |= (SCAN_MODE_LOW_POWER << 24) | \ + (_sett << 16)) +/* /< store the status of scan mode low power and its setting */ +#define IS_POWER_MODE(_mask, _mode) ((_mask&(_mode<<24)) != 0x00) +/* /< check the current mode of the IC */ + +/** @}*/ + +#define CMD_STR_LEN 32 /* /< max number of parameters that can accept + * the MP file node (stm_fts_cmd) */ + +#define TSP_BUF_SIZE PAGE_SIZE /* /< max number of bytes printable on + * the shell in the normal file nodes */ + +#define PINCTRL_STATE_ACTIVE "pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "pmx_ts_suspend" +#define PINCTRL_STATE_RELEASE "pmx_ts_release" + +/** + * Struct which contains information about the HW platform and set up + */ +struct fts_hw_platform_data { + int (*power)(bool on); + int irq_gpio; /* /< number of the gpio associated to the interrupt pin + * */ + int reset_gpio; /* /< number of the gpio associated to the reset pin */ + const char *vdd_reg_name; /* /< name of the VDD regulator */ + const char *avdd_reg_name; /* /< name of the AVDD regulator */ + unsigned int x_max; + unsigned int y_max; + bool y_flip, x_flip; + bool power_on_suspend; + bool need_tp_cal; + u8 jitter_cmd[8]; /* /< support report rate switching */ + bool jitter_ctrl; /* /< support report rate switching */ + u8 linearity_cmd[3]; /* /< support report rate switching */ + bool linearity_ctrl; /* /< support report rate switching */ + u8 first_filter_cmd[4*2]; /* /< support report rate switching */ + bool first_filter_ctrl; /* /< support report rate switching */ + u8 interpolation_cmd[10]; /* /< report rate interpolation command */ + bool interpolation_ctrl; /* /< support report rate interpolation */ + u8 report_rate_cmd[3]; /* /< report rate switching command */ + bool report_rate_ctrl; /* /< support report rate switching */ + u8 edge_cmd[4]; /* /< edge switching command */ + bool edge_ctrl; /* /< edge rate switching */ + u8 sensitivity_cmd[3]; /* /< sensitivity switching command */ + bool sensitivity_ctrl; /* /< sensitivity switching */ +}; + +/* + * Forward declaration + */ +struct fts_ts_info; +struct fts_sys_info; +extern char tag[8]; /* /< forward the definition of the label used + * to print the log in the kernel log */ + +/* + * Dispatch event handler + */ +typedef void (*event_dispatch_handler_t) + (struct fts_ts_info *info, unsigned char *data); + +/** + * FTS capacitive touch screen device information + * - dev Pointer to the structure device \n + * - client client structure \n + * - input_dev Input device structure \n + * - work Work thread \n + * - event_wq Event queue for work thread \n + * - event_dispatch_table Event dispatch table handlers \n + * - attrs SysFS attributes \n + * - mode Device operating mode (bitmask) \n + * - touch_id Bitmask for touch id (mapped to input slots) \n + * - stylus_id Bitmask for tracking the stylus touches (mapped using the + * touchId) \n + * - timer Timer when operating in polling mode \n + * - power Power on/off routine \n + * - board HW info retrieved from device tree \n + * - vdd_reg DVDD power regulator \n + * - avdd_reg AVDD power regulator \n + * - resume_bit Indicate if screen off/on \n + * - fwupdate_stat Store the result of a fw update triggered by the host \n + * - notifier Used for be notified from a suspend/resume event \n + * - sensor_sleep true suspend was called, false resume was called \n + * - wakesrc Wakeup Source struct \n + * - input_report_mutex mutex for handling the pressure of keys \n + * - series_of_switches to store the enabling status of a particular feature + * from the host \n + */ +struct fts_ts_info { + struct device *dev; /* /< Pointer to the structure device */ +#ifdef I2C_INTERFACE + struct i2c_client *client; /* /< I2C client structure */ +#else + struct spi_device *client; /* /< SPI client structure */ +#endif + struct input_dev *input_dev; /* /< Input device structure */ + + struct work_struct work; /* /< Event work thread */ + struct work_struct suspend_work; /* /< Suspend work thread */ + struct work_struct resume_work; /* /< Resume work thread */ + struct workqueue_struct *event_wq; /* /< Workqueue used for event + * handler, suspend and resume + * work threads */ + +#ifndef FW_UPDATE_ON_PROBE + struct delayed_work fwu_work; /* /< Delayed work thread for fw update + * process */ + struct workqueue_struct *fwu_workqueue; /* /< Fw update work + * queue */ +#endif + event_dispatch_handler_t *event_dispatch_table; /* /< Event dispatch + * table handlers */ + + struct attribute_group attrs; /* /< SysFS attributes */ + + unsigned int mode; /* /< Device operating mode (bitmask: msb + * indicate if active or lpm) */ + unsigned long touch_id; /* /< Bitmask for touch id (mapped to input + * slots) */ + unsigned int touch_count; +#ifdef STYLUS_MODE + unsigned long stylus_id; /* /< Bitmask for tracking the stylus + * touches (mapped using the touchId) */ +#endif + + + struct fts_hw_platform_data *board; /* /< HW info retrieved from + * device tree */ + struct regulator *vdd_reg; /* /< DVDD power regulator */ + struct regulator *avdd_reg; /* /< AVDD power regulator */ + + + int resume_bit; /* /< Indicate if screen off/on */ + int fwupdate_stat; /* /< Store the result of a fw update triggered + * by the host */ + + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + /*struct pinctrl_state *pinctrl_state_release;*/ + + struct notifier_block notifier; /* /< Used for be notified from a + * suspend/resume event */ + bool sensor_sleep; /* /< if true suspend was called while if false + * resume was called */ + struct wakeup_source *wakesrc; /* Wake Lock struct */ + + /* input lock */ + struct mutex input_report_mutex; /* /< mutex for handling the report + * of the pressure of keys */ + + /* switches for features */ + int gesture_enabled; /* /< if set, the gesture mode will be enabled + * during the suspend */ + int glove_enabled; /* /< if set, the glove mode will be enabled + * when allowed */ + int charger_enabled; /* /< if set, the charger mode will be enabled + * when allowed */ + int stylus_enabled; /* /< if set, the stylus mode will be enabled + * when allowed */ + int cover_enabled; /* /< if set, the cover mode will be enabled + * when allowed */ + int grip_enabled; /* /< if set, the grip mode mode will be enabled + * when allowed */ + + struct fts_sys_info *sysinfo; + /* touchscreen class*/ + struct ts_mmi_class_methods *imports; + char limit_path[MAX_LIMIT_FILE_NAME]; + const char *fw_file; + bool force_reflash; + + unsigned int interpolation_val; + unsigned int report_rate; + unsigned int refresh_rate; + unsigned int first_filter_val; + unsigned int sensitivity_val; + u8 jitter_val[8]; + u8 linearity_val[3]; + u8 edge_val[4]; +}; + + +int fts_chip_powercycle(struct fts_ts_info *info); +int fts_init_sensing(struct fts_ts_info *info); +int fts_chip_power_switch(struct fts_ts_info *info, bool on); +int fts_pinctrl_state(struct fts_ts_info *info, bool on); +int fts_chip_initialization(struct fts_ts_info *info, int type); +void fts_interrupt_uninstall(struct fts_ts_info*info); +extern int input_register_notifier_client(struct notifier_block *nb); +extern int input_unregister_notifier_client(struct notifier_block *nb); + +/* export declaration of functions in fts_proc.c */ +extern int fts_proc_init(void); +extern int fts_proc_remove(void); + +#endif diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_fw.h b/drivers/input/touchscreen/stmicro_mmi/fts_fw.h new file mode 100644 index 000000000000..eee021d00100 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_fw.h @@ -0,0 +1,12574 @@ +/*! + * \file fts_fw.h + * \brief Contains the size and the byte array of the fw file to burn into the + *IC + */ +#ifndef FTS_FW_H +#define FTS_FW_H +/* This is an auto generated header file */ +/* --->Remember to change the name of the two variables!<--- */ +const uint32_t myArray_size = 100476; + +const uint8_t myArray[] = { + 0x55, 0xAA, 0x55, 0xAA, 0x01, 0x00, 0x00, 0x00, 0x36, 0x39, 0x00, 0x00, + 0x33, 0xA2, 0x00, 0x00, + 0x33, 0xA2, 0x00, 0x00, 0xA1, 0x00, 0xA1, 0x00, 0xA1, 0x00, 0xA1, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xE8, 0x7D, 0x01, 0x00, + 0x50, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xF3, 0xB0, 0x4F, 0xDA, + 0x69, 0xDA, 0x63, 0x91, 0x79, 0x5F, 0x00, 0x00, 0x96, 0x25, 0x9C, 0x6E, + 0x86, 0xA0, 0xFF, 0xFF, + 0x00, 0x04, 0x10, 0x00, 0xB5, 0x02, 0x00, 0x00, 0x0D, 0x03, 0x00, 0x00, + 0x27, 0x03, 0x00, 0x00, + 0x41, 0x03, 0x00, 0x00, 0x5B, 0x03, 0x00, 0x00, 0x75, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x6E, 0x01, 0x00, + 0x8D, 0x6E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8F, 0x03, 0x00, 0x00, + 0x95, 0x6E, 0x01, 0x00, + 0x9D, 0x6E, 0x01, 0x00, 0xAB, 0x6E, 0x01, 0x00, 0xB9, 0x6E, 0x01, 0x00, + 0xC7, 0x6E, 0x01, 0x00, + 0xD5, 0x6E, 0x01, 0x00, 0xE3, 0x6E, 0x01, 0x00, 0xF1, 0x6E, 0x01, 0x00, + 0xFF, 0x6E, 0x01, 0x00, + 0x0D, 0x6F, 0x01, 0x00, 0x9D, 0x03, 0x00, 0x00, 0x1B, 0x6F, 0x01, 0x00, + 0x29, 0x6F, 0x01, 0x00, + 0x37, 0x6F, 0x01, 0x00, 0x3F, 0x6F, 0x01, 0x00, 0x47, 0x6F, 0x01, 0x00, + 0x4F, 0x6F, 0x01, 0x00, + 0x5D, 0x6F, 0x01, 0x00, 0x65, 0x6F, 0x01, 0x00, 0x6D, 0x6F, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x75, 0x6F, 0x01, 0x00, + 0x7D, 0x6F, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x6F, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x33, 0xA2, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0xF0, 0x02, 0xF8, 0x00, 0xF0, 0x68, 0xF8, 0x0A, 0xA0, 0x90, 0xE8, + 0x00, 0x0C, 0x82, 0x44, + 0x83, 0x44, 0xAA, 0xF1, 0x01, 0x07, 0xDA, 0x45, 0x01, 0xD1, 0x00, 0xF0, + 0x5D, 0xF8, 0xAF, 0xF2, + 0x09, 0x0E, 0xBA, 0xE8, 0x0F, 0x00, 0x13, 0xF0, 0x01, 0x0F, 0x18, 0xBF, + 0xFB, 0x1A, 0x43, 0xF0, + 0x01, 0x03, 0x18, 0x47, 0x1C, 0x7C, 0x01, 0x00, 0x4C, 0x7C, 0x01, 0x00, + 0x0A, 0x44, 0x4F, 0xF0, + 0x00, 0x0C, 0x10, 0xF8, 0x01, 0x3B, 0x13, 0xF0, 0x07, 0x04, 0x08, 0xBF, + 0x10, 0xF8, 0x01, 0x4B, + 0x1D, 0x11, 0x08, 0xBF, 0x10, 0xF8, 0x01, 0x5B, 0x64, 0x1E, 0x05, 0xD0, + 0x10, 0xF8, 0x01, 0x6B, + 0x64, 0x1E, 0x01, 0xF8, 0x01, 0x6B, 0xF9, 0xD1, 0x13, 0xF0, 0x08, 0x0F, + 0x1E, 0xBF, 0x10, 0xF8, + 0x01, 0x4B, 0xAD, 0x1C, 0x0C, 0x1B, 0x09, 0xD1, 0x6D, 0x1E, 0x58, 0xBF, + 0x01, 0xF8, 0x01, 0xCB, + 0xFA, 0xD5, 0x05, 0xE0, 0x14, 0xF8, 0x01, 0x6B, 0x01, 0xF8, 0x01, 0x6B, + 0x6D, 0x1E, 0xF9, 0xD5, + 0x91, 0x42, 0xD6, 0xD3, 0x70, 0x47, 0x00, 0x00, 0x10, 0x3A, 0x24, 0xBF, + 0x78, 0xC8, 0x78, 0xC1, + 0xFA, 0xD8, 0x52, 0x07, 0x24, 0xBF, 0x30, 0xC8, 0x30, 0xC1, 0x44, 0xBF, + 0x04, 0x68, 0x0C, 0x60, + 0x70, 0x47, 0x00, 0x00, 0x00, 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, + 0x10, 0x3A, 0x28, 0xBF, + 0x78, 0xC1, 0xFB, 0xD8, 0x52, 0x07, 0x28, 0xBF, 0x30, 0xC1, 0x48, 0xBF, + 0x0B, 0x60, 0x70, 0x47, + 0x1F, 0xB5, 0x1F, 0xBD, 0x10, 0xB5, 0x10, 0xBD, 0xDF, 0xF8, 0x0C, 0xD0, + 0xFF, 0xF7, 0xF8, 0xFF, + 0x16, 0xF0, 0xE4, 0xFE, 0x17, 0xF0, 0x69, 0xF8, 0x00, 0x04, 0x10, 0x00, + 0x03, 0xB4, 0xFF, 0xF7, + 0xF1, 0xFF, 0x03, 0xBC, 0x17, 0xF0, 0x68, 0xF8, 0x0A, 0x48, 0x4F, 0xF0, + 0xFF, 0x01, 0x01, 0x70, + 0x4F, 0xF0, 0x00, 0x00, 0x80, 0xF3, 0x11, 0x88, 0x4F, 0xF0, 0x00, 0x00, + 0x80, 0xF3, 0x09, 0x88, + 0x05, 0x48, 0x4F, 0xF0, 0x02, 0x01, 0x01, 0x70, 0x62, 0xB6, 0x04, 0x48, + 0x4F, 0xF0, 0x80, 0x51, + 0x01, 0x60, 0x70, 0x47, 0x22, 0xED, 0x00, 0xE0, 0x00, 0x04, 0x10, 0x00, + 0x04, 0xED, 0x00, 0xE0, + 0x02, 0x48, 0x4F, 0xF0, 0x80, 0x51, 0x01, 0x60, 0x70, 0x47, 0x00, 0x00, + 0x04, 0xED, 0x00, 0xE0, + 0x4F, 0xF0, 0x20, 0x00, 0x80, 0xF3, 0x11, 0x88, 0xEF, 0xF3, 0x09, 0x80, + 0x98, 0xB1, 0x12, 0x48, + 0x00, 0x68, 0x12, 0x49, 0x09, 0x68, 0x88, 0x42, 0x17, 0xD0, 0xEF, 0xF3, + 0x09, 0x80, 0x0E, 0x49, + 0x09, 0x68, 0x20, 0xE9, 0xF0, 0x0F, 0x88, 0x60, 0x00, 0xB5, 0x0B, 0x48, + 0x00, 0x68, 0x00, 0xF0, + 0x2F, 0xF9, 0x5D, 0xF8, 0x04, 0xEB, 0x08, 0x48, 0x08, 0x49, 0x0A, 0x68, + 0x02, 0x60, 0x00, 0x68, + 0x81, 0x68, 0xB1, 0xE8, 0xF0, 0x0F, 0x81, 0xF3, 0x09, 0x88, 0x4E, 0xF0, + 0x04, 0x0E, 0x4F, 0xF0, + 0x00, 0x00, 0x80, 0xF3, 0x11, 0x88, 0x70, 0x47, 0x04, 0x04, 0x10, 0x00, + 0x08, 0x04, 0x10, 0x00, + 0xEF, 0xF3, 0x11, 0x80, 0x4F, 0xF0, 0x20, 0x01, 0x81, 0xF3, 0x11, 0x88, + 0x70, 0x47, 0x00, 0x00, + 0x4F, 0xF0, 0x00, 0x00, 0x80, 0xF3, 0x11, 0x88, 0x70, 0x47, 0x00, 0x00, + 0x62, 0xB6, 0x70, 0x47, + 0x72, 0xB6, 0x70, 0x47, 0x40, 0x1E, 0xFD, 0xD1, 0x70, 0x47, 0x00, 0x00, + 0x40, 0xBA, 0x70, 0x47, + 0xC0, 0xBA, 0x70, 0x47, 0x12, 0x48, 0x13, 0x49, 0xA1, 0xEB, 0x00, 0x01, + 0x4F, 0xF0, 0x20, 0x03, + 0x91, 0xFB, 0xF3, 0xF2, 0x02, 0xFB, 0x13, 0x11, 0x4F, 0xF0, 0xCC, 0x33, + 0x4F, 0xF0, 0xCC, 0x34, + 0x4F, 0xF0, 0xCC, 0x35, 0x4F, 0xF0, 0xCC, 0x36, 0x4F, 0xF0, 0xCC, 0x37, + 0x4F, 0xF0, 0xCC, 0x38, + 0x4F, 0xF0, 0xCC, 0x39, 0x4F, 0xF0, 0xCC, 0x3A, 0xA0, 0xE8, 0xF8, 0x07, + 0x01, 0x3A, 0xFB, 0xD1, + 0x19, 0xB1, 0x40, 0xF8, 0x04, 0xAB, 0x04, 0x39, 0xFB, 0xD1, 0x03, 0x48, + 0x00, 0x47, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x04, 0x10, 0x00, 0xE1, 0x00, 0x00, 0x00, + 0x1E, 0xF0, 0x04, 0x0F, + 0x0C, 0xBF, 0xEF, 0xF3, 0x08, 0x80, 0xEF, 0xF3, 0x09, 0x80, 0x4F, 0xF0, + 0x01, 0x01, 0x4F, 0xF0, + 0x00, 0x02, 0x12, 0xF0, 0xBB, 0xB8, 0x1E, 0xF0, 0x04, 0x0F, 0x0C, 0xBF, + 0xEF, 0xF3, 0x08, 0x80, + 0xEF, 0xF3, 0x09, 0x80, 0x4F, 0xF0, 0x02, 0x01, 0x4F, 0xF0, 0x00, 0x02, + 0x12, 0xF0, 0xAE, 0xB8, + 0x1E, 0xF0, 0x04, 0x0F, 0x0C, 0xBF, 0xEF, 0xF3, 0x08, 0x80, 0xEF, 0xF3, + 0x09, 0x80, 0x4F, 0xF0, + 0x03, 0x01, 0x4F, 0xF0, 0x00, 0x02, 0x12, 0xF0, 0xA1, 0xB8, 0x1E, 0xF0, + 0x04, 0x0F, 0x0C, 0xBF, + 0xEF, 0xF3, 0x08, 0x80, 0xEF, 0xF3, 0x09, 0x80, 0x4F, 0xF0, 0x04, 0x01, + 0x4F, 0xF0, 0x00, 0x02, + 0x12, 0xF0, 0x94, 0xB8, 0x1E, 0xF0, 0x04, 0x0F, 0x0C, 0xBF, 0xEF, 0xF3, + 0x08, 0x80, 0xEF, 0xF3, + 0x09, 0x80, 0x4F, 0xF0, 0x05, 0x01, 0x4F, 0xF0, 0x00, 0x02, 0x12, 0xF0, + 0x87, 0xB8, 0x00, 0xB5, + 0x16, 0xF0, 0x72, 0xFD, 0x5D, 0xF8, 0x04, 0xEB, 0xFF, 0xF7, 0x42, 0xBF, + 0x1E, 0xF0, 0x04, 0x0F, + 0x0C, 0xBF, 0xEF, 0xF3, 0x08, 0x80, 0xEF, 0xF3, 0x09, 0x80, 0x4F, 0xF0, + 0x06, 0x01, 0x4F, 0xF0, + 0x00, 0x02, 0x12, 0xF0, 0x73, 0xB8, 0x00, 0x00, 0x19, 0x49, 0x10, 0xB5, + 0x01, 0x20, 0x08, 0x70, + 0x18, 0x49, 0x00, 0x20, 0x08, 0x60, 0x18, 0x49, 0x08, 0x60, 0x18, 0x49, + 0x08, 0x60, 0x18, 0x49, + 0x08, 0x60, 0x00, 0xF0, 0xBF, 0xF9, 0x00, 0xF0, 0x59, 0xF9, 0x00, 0xF0, + 0x7D, 0xF8, 0x00, 0x20, + 0x10, 0xBD, 0x10, 0xB5, 0x11, 0x48, 0x00, 0xF0, 0x38, 0xF9, 0x0F, 0x49, + 0x08, 0x60, 0x0D, 0x49, + 0x09, 0x68, 0x88, 0x42, 0x07, 0xD0, 0x0A, 0x48, 0x00, 0x78, 0x02, 0x28, + 0x03, 0xD1, 0xBD, 0xE8, + 0x10, 0x40, 0xFF, 0xF7, 0x05, 0xBF, 0x10, 0xBD, 0x10, 0xB5, 0xFF, 0xF7, + 0xEA, 0xFF, 0x04, 0x49, + 0x02, 0x20, 0x08, 0x70, 0xFF, 0xF7, 0xE0, 0xFE, 0x4F, 0xF0, 0xFF, 0x30, + 0x10, 0xBD, 0x00, 0x00, + 0x00, 0x04, 0x10, 0x00, 0x04, 0x04, 0x10, 0x00, 0x08, 0x04, 0x10, 0x00, + 0x0C, 0x04, 0x10, 0x00, + 0x10, 0x04, 0x10, 0x00, 0x70, 0xB5, 0x0D, 0x46, 0x04, 0x46, 0xC1, 0x69, + 0x80, 0x68, 0xCC, 0x22, + 0x16, 0xF0, 0xDA, 0xFE, 0xE1, 0x69, 0xA0, 0x68, 0x08, 0x44, 0x4F, 0xF0, + 0x80, 0x71, 0x40, 0xF8, + 0x04, 0x1C, 0x21, 0x6A, 0x40, 0xF8, 0x08, 0x1C, 0x00, 0x21, 0x40, 0xF8, + 0x0C, 0x1C, 0x40, 0xF8, + 0x10, 0x1C, 0x40, 0xF8, 0x14, 0x1C, 0x40, 0xF8, 0x18, 0x1C, 0x40, 0xE9, + 0x08, 0x51, 0x4F, 0xF0, + 0x0B, 0x31, 0x40, 0xF8, 0x24, 0x1C, 0x4F, 0xF0, 0x0A, 0x31, 0x40, 0xF8, + 0x28, 0x1C, 0x4F, 0xF0, + 0x09, 0x31, 0x40, 0xF8, 0x2C, 0x1C, 0x4F, 0xF0, 0x08, 0x31, 0x40, 0xF8, + 0x30, 0x1C, 0x4F, 0xF0, + 0x07, 0x31, 0x40, 0xF8, 0x34, 0x1C, 0x4F, 0xF0, 0x06, 0x31, 0x40, 0xF8, + 0x38, 0x1C, 0x4F, 0xF0, + 0x05, 0x31, 0x40, 0xF8, 0x3C, 0x1C, 0x4F, 0xF0, 0x04, 0x31, 0x40, 0xF8, + 0x40, 0x1D, 0x70, 0xBD, + 0x81, 0x69, 0x4F, 0xF0, 0xCC, 0x32, 0x0B, 0x68, 0x93, 0x42, 0x08, 0xD1, + 0x4B, 0x68, 0x93, 0x42, + 0x05, 0xD1, 0x8B, 0x68, 0x93, 0x42, 0x02, 0xD1, 0xC9, 0x68, 0x91, 0x42, + 0x02, 0xD0, 0x40, 0x68, + 0x12, 0xF0, 0xD9, 0xBB, 0x70, 0x47, 0x00, 0x00, 0x10, 0xB5, 0xA0, 0x21, + 0x49, 0x48, 0x16, 0xF0, + 0xB5, 0xFE, 0x00, 0x20, 0x10, 0xBD, 0x70, 0xB5, 0x05, 0x46, 0x46, 0x48, + 0x00, 0xEB, 0x05, 0x14, + 0xFF, 0xF7, 0xC6, 0xFE, 0x06, 0x46, 0x20, 0x78, 0x28, 0xB1, 0x30, 0x46, + 0xFF, 0xF7, 0xC8, 0xFE, + 0x6F, 0xF0, 0x01, 0x00, 0x70, 0xBD, 0x03, 0x20, 0x20, 0x70, 0x00, 0x20, + 0x65, 0x60, 0xE0, 0x60, + 0x04, 0xF1, 0x08, 0x00, 0x00, 0xF0, 0x7A, 0xF8, 0x30, 0x46, 0xFF, 0xF7, + 0xB9, 0xFE, 0x00, 0x20, + 0x70, 0xBD, 0x2D, 0xE9, 0xF0, 0x41, 0x0F, 0x46, 0x36, 0x49, 0x00, 0x25, + 0x01, 0xEB, 0x00, 0x14, + 0x2E, 0x46, 0xFF, 0xF7, 0xA5, 0xFE, 0x80, 0x46, 0xE0, 0x68, 0x38, 0x43, + 0xE0, 0x60, 0x04, 0xF1, + 0x08, 0x00, 0x07, 0x46, 0x00, 0xF0, 0x89, 0xF8, 0x0C, 0xE0, 0xE1, 0x68, + 0xC2, 0x6A, 0x11, 0x42, + 0x03, 0xD0, 0x00, 0xF0, 0x4F, 0xF9, 0x01, 0x26, 0x00, 0xE0, 0x6D, 0x1C, + 0x29, 0x46, 0x38, 0x46, + 0x00, 0xF0, 0x71, 0xF8, 0x00, 0x28, 0xF0, 0xD1, 0x0E, 0xB1, 0xFF, 0xF7, + 0x3A, 0xFF, 0x40, 0x46, + 0xFF, 0xF7, 0x8E, 0xFE, 0x00, 0x20, 0xBD, 0xE8, 0xF0, 0x81, 0x70, 0xB5, + 0x0D, 0x46, 0x21, 0x49, + 0x01, 0xEB, 0x00, 0x14, 0xFF, 0xF7, 0x7C, 0xFE, 0xE1, 0x68, 0xA9, 0x43, + 0xE1, 0x60, 0xFF, 0xF7, + 0x7F, 0xFE, 0x00, 0x20, 0x70, 0xBD, 0x70, 0xB5, 0x0C, 0x46, 0x1A, 0x49, + 0x01, 0xEB, 0x00, 0x15, + 0x00, 0x20, 0x20, 0x60, 0xFF, 0xF7, 0x6C, 0xFE, 0xE9, 0x68, 0x21, 0x60, + 0xFF, 0xF7, 0x70, 0xFE, + 0x00, 0x20, 0x70, 0xBD, 0x2D, 0xE9, 0xF0, 0x41, 0x0D, 0x46, 0x12, 0x49, + 0x1E, 0x46, 0x90, 0x46, + 0x01, 0xEB, 0x00, 0x14, 0xFF, 0xF7, 0x5C, 0xFE, 0x07, 0x46, 0x0F, 0x48, + 0x00, 0x68, 0xC5, 0x62, + 0xE1, 0x68, 0x29, 0x42, 0x03, 0xD1, 0x04, 0xF1, 0x08, 0x01, 0x00, 0xF0, + 0x1D, 0xF9, 0x38, 0x46, + 0xFF, 0xF7, 0x56, 0xFE, 0xFF, 0xF7, 0x4C, 0xFE, 0xE1, 0x68, 0x29, 0x40, + 0x31, 0x60, 0xB8, 0xF1, + 0x01, 0x0F, 0x02, 0xD1, 0xE1, 0x68, 0xA9, 0x43, 0xE1, 0x60, 0xFF, 0xF7, + 0x49, 0xFE, 0x00, 0x20, + 0xB9, 0xE7, 0x00, 0x00, 0x0C, 0x08, 0x10, 0x00, 0x04, 0x04, 0x10, 0x00, + 0x00, 0x21, 0x01, 0x60, + 0x08, 0x46, 0x70, 0x47, 0x30, 0xB5, 0x0A, 0x68, 0x12, 0xB1, 0x0B, 0x46, + 0x84, 0x6A, 0x09, 0xE0, + 0x00, 0x22, 0x08, 0x60, 0xC0, 0xE9, 0x03, 0x21, 0x0B, 0xE0, 0x00, 0xBF, + 0x02, 0xF1, 0x0C, 0x03, + 0xD2, 0x68, 0x12, 0xB1, 0x95, 0x6A, 0xA5, 0x42, 0xF8, 0xDC, 0x1A, 0x68, + 0xC0, 0xE9, 0x03, 0x21, + 0x18, 0x60, 0x00, 0x20, 0x30, 0xBD, 0x00, 0x68, 0x00, 0x22, 0x01, 0xE0, + 0xC0, 0x68, 0x52, 0x1C, + 0x8A, 0x42, 0x01, 0xD0, 0x00, 0x28, 0xF9, 0xD1, 0x70, 0x47, 0x00, 0x68, + 0x70, 0x47, 0x02, 0x69, + 0x0A, 0xB1, 0x11, 0x68, 0x31, 0xB9, 0x6F, 0xF0, 0x04, 0x00, 0x70, 0x47, + 0x01, 0xF1, 0x0C, 0x02, + 0xC9, 0x68, 0x39, 0xB1, 0x81, 0x42, 0xF9, 0xD1, 0xC9, 0x68, 0x11, 0x60, + 0x00, 0x21, 0x01, 0x61, + 0x08, 0x46, 0x70, 0x47, 0x6F, 0xF0, 0x03, 0x00, 0x70, 0x47, 0x00, 0x00, + 0x10, 0xB5, 0xC8, 0x21, + 0x2E, 0x48, 0x16, 0xF0, 0xDB, 0xFD, 0x00, 0x20, 0x10, 0xBD, 0x2D, 0xE9, + 0xF0, 0x41, 0x05, 0x46, + 0x2A, 0x49, 0x00, 0xEB, 0x85, 0x00, 0x17, 0x46, 0x01, 0xEB, 0x80, 0x04, + 0xFF, 0xF7, 0xE8, 0xFD, + 0x06, 0x46, 0x20, 0x78, 0x30, 0xB1, 0x30, 0x46, 0xFF, 0xF7, 0xEA, 0xFD, + 0x6F, 0xF0, 0x01, 0x00, + 0xBD, 0xE8, 0xF0, 0x81, 0x02, 0x20, 0x20, 0x70, 0x00, 0x20, 0x65, 0x60, + 0xC4, 0xE9, 0x02, 0x70, + 0x04, 0xF1, 0x10, 0x00, 0xFF, 0xF7, 0x9A, 0xFF, 0x30, 0x46, 0xFF, 0xF7, + 0xD9, 0xFD, 0x00, 0x20, + 0xEE, 0xE7, 0x70, 0xB5, 0x19, 0x49, 0x00, 0xEB, 0x80, 0x00, 0x01, 0xEB, + 0x80, 0x04, 0xFF, 0xF7, + 0xC7, 0xFD, 0x05, 0x46, 0xD4, 0xE9, 0x02, 0x01, 0x81, 0x42, 0x05, 0xDB, + 0x14, 0x48, 0x04, 0xF1, + 0x10, 0x01, 0x00, 0x68, 0x00, 0xF0, 0x88, 0xF8, 0xE0, 0x68, 0x40, 0x1C, + 0xE0, 0x60, 0x28, 0x46, + 0xFF, 0xF7, 0xBE, 0xFD, 0x00, 0x20, 0x70, 0xBD, 0x70, 0xB5, 0x0C, 0x49, + 0x00, 0xEB, 0x80, 0x00, + 0x01, 0xEB, 0x80, 0x04, 0xFF, 0xF7, 0xAC, 0xFD, 0x05, 0x46, 0xE0, 0x68, + 0x08, 0xB1, 0x40, 0x1E, + 0xE0, 0x60, 0x04, 0xF1, 0x10, 0x00, 0xFF, 0xF7, 0x90, 0xFF, 0x10, 0xB1, + 0x20, 0x69, 0x00, 0xF0, + 0x11, 0xF8, 0x28, 0x46, 0xFF, 0xF7, 0xA4, 0xFD, 0x00, 0x20, 0x70, 0xBD, + 0xAC, 0x08, 0x10, 0x00, + 0x04, 0x04, 0x10, 0x00, 0x10, 0xB5, 0x4F, 0xF4, 0xF0, 0x71, 0x3C, 0x48, + 0x16, 0xF0, 0x76, 0xFD, + 0x00, 0x20, 0x10, 0xBD, 0x10, 0xB5, 0x04, 0x46, 0x90, 0xF8, 0x24, 0x00, + 0x02, 0x28, 0x0B, 0xD0, + 0x20, 0x46, 0xFF, 0xF7, 0x74, 0xFF, 0x02, 0x20, 0x84, 0xF8, 0x24, 0x00, + 0x34, 0x49, 0x20, 0x46, + 0xFF, 0xF7, 0x48, 0xFF, 0xFF, 0xF7, 0x2D, 0xFE, 0x00, 0x20, 0x10, 0xBD, + 0x2D, 0xE9, 0xF0, 0x5F, + 0x06, 0x46, 0x0A, 0xA8, 0x06, 0xEB, 0x46, 0x01, 0x90, 0xE8, 0x00, 0x0E, + 0x2B, 0x48, 0x1D, 0x46, + 0x00, 0xEB, 0x01, 0x14, 0x90, 0x46, 0x20, 0x78, 0x18, 0xB1, 0x6F, 0xF0, + 0x01, 0x00, 0xBD, 0xE8, + 0xF0, 0x9F, 0xFF, 0xF7, 0x65, 0xFD, 0x07, 0x46, 0x01, 0x20, 0x20, 0x70, + 0xC4, 0xF8, 0x14, 0x80, + 0xC4, 0xE9, 0x01, 0x65, 0x04, 0xF1, 0x18, 0x01, 0x00, 0x20, 0x81, 0xE8, + 0x20, 0x06, 0x84, 0xF8, + 0x24, 0x00, 0xE0, 0x60, 0x20, 0x61, 0xC4, 0xE9, 0x0A, 0x80, 0x59, 0x46, + 0x20, 0x46, 0xFF, 0xF7, + 0x29, 0xFE, 0xA0, 0x60, 0x20, 0x46, 0xFF, 0xF7, 0xBD, 0xFF, 0x38, 0x46, + 0xFF, 0xF7, 0x50, 0xFD, + 0x00, 0x20, 0xDC, 0xE7, 0x10, 0xB5, 0x04, 0x46, 0x90, 0xF8, 0x24, 0x00, + 0x02, 0x28, 0x09, 0xD0, + 0x20, 0x46, 0xFF, 0xF7, 0x2C, 0xFF, 0x02, 0x20, 0x84, 0xF8, 0x24, 0x00, + 0x10, 0x49, 0x20, 0x46, + 0xFF, 0xF7, 0x00, 0xFF, 0x00, 0x20, 0x10, 0xBD, 0x70, 0xB5, 0x04, 0x46, + 0x90, 0xF8, 0x24, 0x00, + 0x0D, 0x46, 0x02, 0x28, 0x0B, 0xD1, 0x20, 0x46, 0xFF, 0xF7, 0x19, 0xFF, + 0x03, 0x20, 0x84, 0xF8, + 0x24, 0x00, 0x29, 0x46, 0x20, 0x46, 0xFF, 0xF7, 0xED, 0xFE, 0xFF, 0xF7, + 0xD2, 0xFD, 0x00, 0x20, + 0x70, 0xBD, 0x04, 0x48, 0x00, 0x68, 0x40, 0x68, 0x70, 0x47, 0x00, 0x00, + 0x74, 0x09, 0x10, 0x00, + 0x0C, 0x04, 0x10, 0x00, 0x04, 0x04, 0x10, 0x00, 0xA4, 0x22, 0x4F, 0x49, + 0x16, 0xF0, 0x55, 0xBC, + 0x70, 0x47, 0x10, 0xB5, 0x04, 0x46, 0x80, 0x07, 0x02, 0xD0, 0x02, 0x20, + 0x16, 0xF0, 0xA3, 0xF8, + 0x4F, 0xF4, 0x80, 0x30, 0x20, 0x44, 0xC0, 0xF3, 0x4F, 0x00, 0x10, 0xBD, + 0x01, 0x46, 0xAC, 0x22, + 0x46, 0x48, 0x16, 0xF0, 0x87, 0xBC, 0x45, 0x4A, 0x5F, 0xF0, 0x00, 0x01, + 0x42, 0xF8, 0x21, 0x00, + 0x49, 0x1C, 0x26, 0x29, 0xFA, 0xDB, 0x70, 0x47, 0x00, 0xF0, 0xF6, 0xBE, + 0x00, 0xF0, 0x1B, 0xBF, + 0x1D, 0x20, 0x70, 0x47, 0x26, 0x20, 0x70, 0x47, 0x3D, 0x48, 0x10, 0xB5, + 0x00, 0x68, 0x10, 0xB1, + 0xBD, 0xE8, 0x10, 0x40, 0x00, 0x47, 0x00, 0x20, 0x16, 0xF0, 0x7D, 0xF8, + 0x10, 0xBD, 0x01, 0x29, + 0x12, 0xD0, 0x02, 0x29, 0x24, 0xD0, 0x03, 0x29, 0x0E, 0xD0, 0x04, 0x29, + 0x1D, 0xD1, 0x01, 0x78, + 0x21, 0xF0, 0x12, 0x01, 0x00, 0xF8, 0x35, 0x1B, 0x01, 0x7E, 0x21, 0xF0, + 0x03, 0x01, 0x01, 0x76, + 0xC1, 0x7F, 0x21, 0xF0, 0x80, 0x01, 0x22, 0xE0, 0x02, 0x78, 0x01, 0x29, + 0x42, 0xF0, 0x12, 0x02, + 0x02, 0x70, 0x0B, 0xD0, 0x5F, 0xF0, 0x03, 0x02, 0x10, 0xF8, 0x4D, 0x1F, + 0x62, 0xF3, 0x01, 0x01, + 0x01, 0x70, 0xC1, 0x79, 0x41, 0xF0, 0x80, 0x01, 0xC1, 0x71, 0x70, 0x47, + 0x01, 0x22, 0xF3, 0xE7, + 0x01, 0x78, 0x21, 0xF0, 0x02, 0x01, 0x41, 0xF0, 0x10, 0x01, 0x00, 0xF8, + 0x35, 0x1B, 0x01, 0x7E, + 0x21, 0xF0, 0x03, 0x01, 0x89, 0x1C, 0x01, 0x76, 0xC1, 0x7F, 0x41, 0xF0, + 0x80, 0x01, 0xC1, 0x77, + 0x70, 0x47, 0x90, 0xF8, 0x29, 0x20, 0x02, 0x29, 0x04, 0xD0, 0x03, 0x29, + 0x22, 0xF0, 0x40, 0x01, + 0x02, 0xD1, 0x04, 0xE0, 0x42, 0xF0, 0x40, 0x01, 0x21, 0xF0, 0x80, 0x01, + 0x01, 0xE0, 0x41, 0xF0, + 0x80, 0x01, 0x80, 0xF8, 0x29, 0x10, 0x70, 0x47, 0x90, 0xF8, 0x29, 0x20, + 0x01, 0x29, 0x04, 0xD0, + 0x02, 0x29, 0x07, 0xD0, 0x22, 0xF0, 0x10, 0x01, 0x01, 0xE0, 0x42, 0xF0, + 0x10, 0x01, 0x21, 0xF0, + 0x20, 0x01, 0x01, 0xE0, 0x42, 0xF0, 0x30, 0x01, 0x80, 0xF8, 0x29, 0x10, + 0x70, 0x47, 0xC2, 0x78, + 0x00, 0x29, 0x61, 0xF3, 0x03, 0x02, 0xC2, 0x70, 0x04, 0xD0, 0x10, 0xF8, + 0x5E, 0x1F, 0x41, 0xF0, + 0x04, 0x01, 0x01, 0x70, 0x70, 0x47, 0x00, 0x00, 0x48, 0x74, 0x01, 0x00, + 0xA4, 0x00, 0x03, 0x20, + 0x18, 0x04, 0x10, 0x00, 0x01, 0xEB, 0x81, 0x01, 0x02, 0xEB, 0xC1, 0x01, + 0x40, 0x5C, 0x01, 0x06, + 0x02, 0xD5, 0x00, 0xF0, 0x7F, 0x00, 0x40, 0x42, 0x70, 0x47, 0x00, 0x2B, + 0x04, 0xDA, 0x5B, 0x42, + 0xDB, 0xB2, 0x43, 0xF0, 0x80, 0x03, 0x01, 0xE0, 0x03, 0xF0, 0x7F, 0x03, + 0x01, 0xEB, 0x81, 0x01, + 0x02, 0xEB, 0xC1, 0x01, 0x43, 0x54, 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x5F, + 0x00, 0x25, 0x17, 0x46, + 0x98, 0x46, 0x89, 0x46, 0x83, 0x46, 0x2E, 0x46, 0x4F, 0xF0, 0x01, 0x0A, + 0x0A, 0xFA, 0x05, 0xF0, + 0x10, 0xEA, 0x09, 0x0F, 0x14, 0xD0, 0x00, 0x24, 0x22, 0x46, 0x01, 0x20, + 0x00, 0x21, 0x16, 0xF0, + 0x4D, 0xFB, 0x38, 0x40, 0x01, 0xEA, 0x08, 0x01, 0x08, 0x43, 0x05, 0xD0, + 0x00, 0x23, 0x22, 0x46, + 0x31, 0x46, 0x58, 0x46, 0xFF, 0xF7, 0xD1, 0xFF, 0x64, 0x1C, 0x26, 0x2C, + 0xEC, 0xDB, 0x76, 0x1C, + 0x6D, 0x1C, 0x1D, 0x2D, 0xE2, 0xDB, 0xBD, 0xE8, 0xF0, 0x9F, 0x40, 0x5C, + 0x01, 0x06, 0x02, 0xD5, + 0x00, 0xF0, 0x7F, 0x00, 0x40, 0x42, 0x70, 0x47, 0x00, 0x2A, 0x04, 0xDA, + 0x52, 0x42, 0xD2, 0xB2, + 0x42, 0xF0, 0x80, 0x02, 0x01, 0xE0, 0x02, 0xF0, 0x7F, 0x02, 0x42, 0x54, + 0x70, 0x47, 0xF0, 0xB5, + 0x00, 0x24, 0x16, 0x46, 0x0F, 0x46, 0x84, 0x46, 0x23, 0x46, 0x01, 0x25, + 0x10, 0xE0, 0x00, 0xBF, + 0x07, 0xEB, 0xE3, 0x00, 0x03, 0xF0, 0x07, 0x02, 0x01, 0x78, 0x05, 0xFA, + 0x02, 0xF0, 0x08, 0x42, + 0x05, 0xD0, 0x00, 0x22, 0x19, 0x46, 0x60, 0x46, 0xFF, 0xF7, 0xDE, 0xFF, + 0x64, 0x1C, 0x5B, 0x1C, + 0xB4, 0x42, 0xED, 0xDB, 0xF0, 0xBD, 0x40, 0x5C, 0x70, 0x47, 0x42, 0x54, + 0x70, 0x47, 0x00, 0x00, + 0x2D, 0xE9, 0xF0, 0x47, 0x34, 0x4C, 0x54, 0xF8, 0x3C, 0x8F, 0x67, 0x68, + 0xE6, 0x68, 0x25, 0x7C, + 0xDF, 0xF8, 0xC8, 0x90, 0x01, 0x21, 0xC9, 0xF8, 0x00, 0x10, 0x4F, 0xF4, + 0x20, 0x70, 0xFF, 0xF7, + 0xF9, 0xFB, 0x00, 0x21, 0xC9, 0xF8, 0x00, 0x10, 0xC4, 0xF8, 0x00, 0x80, + 0x67, 0x60, 0xE6, 0x60, + 0x25, 0x74, 0xBD, 0xE8, 0xF0, 0x87, 0x28, 0x48, 0x00, 0x21, 0xC1, 0x63, + 0x01, 0x64, 0x81, 0x64, + 0x80, 0xF8, 0x4C, 0x10, 0x70, 0x47, 0x10, 0xB5, 0x02, 0x46, 0x00, 0x24, + 0x4F, 0xF0, 0x01, 0x00, + 0x49, 0xB3, 0x00, 0x21, 0x16, 0xF0, 0xDA, 0xFA, 0x1F, 0x4A, 0x48, 0x32, + 0x10, 0x60, 0x11, 0x71, + 0xFF, 0xF7, 0xCE, 0xFF, 0x00, 0x20, 0x00, 0xF0, 0x25, 0xF8, 0x00, 0xB1, + 0x01, 0x24, 0xFF, 0xF7, + 0xC7, 0xFF, 0x01, 0x20, 0x00, 0xF0, 0x1E, 0xF8, 0x08, 0xB1, 0x44, 0xF0, + 0x02, 0x04, 0xFF, 0xF7, + 0xBF, 0xFF, 0x02, 0x20, 0x00, 0xF0, 0x16, 0xF8, 0x08, 0xB1, 0x44, 0xF0, + 0x04, 0x04, 0xFF, 0xF7, + 0xB7, 0xFF, 0x03, 0x20, 0x00, 0xF0, 0x0E, 0xF8, 0x08, 0xB1, 0x44, 0xF0, + 0x08, 0x04, 0xFF, 0xF7, + 0xAF, 0xFF, 0x20, 0x46, 0x10, 0xBD, 0x90, 0x40, 0x0B, 0x4A, 0x00, 0x21, + 0x3C, 0x32, 0xC2, 0xE9, + 0x00, 0x01, 0xD5, 0xE7, 0x40, 0xF0, 0x20, 0x01, 0x07, 0x48, 0x10, 0xB5, + 0x80, 0xF8, 0xA3, 0x10, + 0xE5, 0x21, 0x81, 0x71, 0x4F, 0xF4, 0x20, 0x70, 0xFF, 0xF7, 0xA4, 0xFB, + 0x04, 0x48, 0xD0, 0xF8, + 0xC0, 0x02, 0xC0, 0xB2, 0x10, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x03, 0x20, + 0x68, 0x14, 0x60, 0x22, + 0x00, 0x10, 0x60, 0x22, 0x01, 0x49, 0x01, 0x20, 0x09, 0x68, 0x08, 0x47, + 0x1C, 0x04, 0x10, 0x00, + 0x10, 0xB5, 0xFF, 0xF7, 0x8B, 0xFE, 0x05, 0x49, 0x02, 0x20, 0x08, 0x70, + 0x04, 0x49, 0x00, 0x20, + 0x08, 0x60, 0x04, 0x49, 0x09, 0x68, 0xBD, 0xE8, 0x10, 0x40, 0x08, 0x47, + 0x14, 0x04, 0x10, 0x00, + 0x18, 0x04, 0x10, 0x00, 0x1C, 0x04, 0x10, 0x00, 0x37, 0xB5, 0x04, 0x46, + 0x37, 0x49, 0x01, 0x98, + 0x15, 0x46, 0x08, 0x40, 0x01, 0x90, 0x04, 0x21, 0x01, 0xA8, 0x00, 0xF0, + 0xFC, 0xFA, 0x01, 0x2D, + 0x02, 0xD0, 0x02, 0x2D, 0x04, 0xD0, 0x06, 0xE0, 0xC0, 0x1C, 0x20, 0xF0, + 0x01, 0x00, 0x02, 0xE0, + 0xC0, 0x1D, 0x20, 0xF0, 0x03, 0x00, 0x01, 0x99, 0x44, 0xF8, 0x3C, 0x1F, + 0x00, 0x21, 0x61, 0x60, + 0x21, 0x7A, 0x60, 0xF3, 0x06, 0x01, 0x21, 0x72, 0x3E, 0xBD, 0x01, 0x23, + 0x40, 0xF8, 0x3C, 0x3F, + 0x00, 0x23, 0x43, 0x60, 0x51, 0x43, 0x02, 0x7A, 0x61, 0xF3, 0x06, 0x02, + 0x02, 0x72, 0x70, 0x47, + 0x1F, 0xB5, 0x04, 0x46, 0x03, 0x99, 0x02, 0xA8, 0x01, 0xF0, 0x3F, 0x01, + 0x03, 0x91, 0x08, 0x21, + 0x00, 0xF0, 0xD1, 0xFA, 0xDD, 0xE9, 0x02, 0x12, 0xA1, 0x64, 0x84, 0xF8, + 0x4C, 0x20, 0x14, 0xF8, + 0x70, 0x1F, 0x60, 0xF3, 0x05, 0x01, 0x04, 0xF8, 0x1E, 0x19, 0xE1, 0x7F, + 0x60, 0xF3, 0x05, 0x01, + 0xE1, 0x77, 0x1F, 0xBD, 0x08, 0xB5, 0x16, 0xA2, 0x12, 0x68, 0x00, 0x92, + 0x1D, 0xF8, 0x01, 0x20, + 0x10, 0xF8, 0x5D, 0x1F, 0x62, 0xF3, 0x01, 0x01, 0x01, 0x70, 0x08, 0xBD, + 0x10, 0xB5, 0x82, 0x79, + 0x22, 0xF0, 0x0D, 0x02, 0x42, 0xF0, 0xE0, 0x02, 0x82, 0x71, 0x0E, 0x4A, + 0x11, 0x60, 0x0E, 0x4A, + 0x03, 0x21, 0x11, 0x70, 0x0E, 0x4A, 0x0D, 0x49, 0x11, 0x60, 0x01, 0x46, + 0xA4, 0x22, 0x0D, 0x48, + 0x16, 0xF0, 0x4B, 0xFA, 0x0C, 0x49, 0x01, 0x20, 0xC1, 0xF8, 0xC0, 0x00, + 0xBD, 0xE8, 0x10, 0x40, + 0x0A, 0x49, 0x4F, 0xF4, 0xFA, 0x70, 0xFF, 0xF7, 0x07, 0xBE, 0x00, 0x00, + 0xFF, 0xFF, 0x03, 0x1C, + 0x00, 0x01, 0x02, 0x00, 0x1C, 0x04, 0x10, 0x00, 0x14, 0x04, 0x10, 0x00, + 0x81, 0x0B, 0x00, 0x00, + 0x18, 0x04, 0x10, 0x00, 0x00, 0x00, 0x03, 0x20, 0x00, 0x00, 0x60, 0x22, + 0x75, 0x0B, 0x00, 0x00, + 0x16, 0x48, 0x01, 0x78, 0x01, 0x29, 0x08, 0xD1, 0x02, 0x21, 0x01, 0x70, + 0x14, 0x48, 0x01, 0x68, + 0x19, 0xB1, 0x00, 0x22, 0x02, 0x60, 0x10, 0x46, 0x08, 0x47, 0x01, 0x20, + 0x15, 0xF0, 0x73, 0xBE, + 0x0E, 0x4A, 0x01, 0x21, 0x11, 0x70, 0x0E, 0x49, 0x08, 0x60, 0x0E, 0x49, + 0xE8, 0x20, 0x88, 0x71, + 0x0D, 0x49, 0x02, 0x20, 0xFF, 0xF7, 0xD8, 0xBD, 0x0A, 0x49, 0x08, 0x20, + 0x88, 0x71, 0x0B, 0x49, + 0x00, 0x20, 0x08, 0x62, 0x05, 0x49, 0x08, 0x70, 0x05, 0x49, 0x08, 0x60, + 0x70, 0x47, 0x03, 0x48, + 0x00, 0x78, 0x00, 0x28, 0x00, 0xD0, 0x01, 0x20, 0x70, 0x47, 0x00, 0x00, + 0x14, 0x04, 0x10, 0x00, + 0x20, 0x04, 0x10, 0x00, 0x00, 0x00, 0x03, 0x20, 0xB1, 0x0C, 0x00, 0x00, + 0x00, 0x00, 0x60, 0x22, + 0x53, 0x49, 0x01, 0x20, 0x09, 0x68, 0x08, 0x47, 0x10, 0xB5, 0xFF, 0xF7, + 0xB7, 0xFD, 0x51, 0x49, + 0x02, 0x20, 0x08, 0x70, 0x50, 0x49, 0x00, 0x20, 0x08, 0x60, 0x4D, 0x49, + 0x09, 0x68, 0xBD, 0xE8, + 0x10, 0x40, 0x08, 0x47, 0x13, 0xB5, 0x04, 0x46, 0x4C, 0x49, 0x01, 0x98, + 0x08, 0x40, 0x01, 0x90, + 0x04, 0x21, 0x01, 0xA8, 0x00, 0xF0, 0x2F, 0xFA, 0x01, 0x99, 0x44, 0xF8, + 0x3C, 0x1F, 0x00, 0x21, + 0x61, 0x60, 0x14, 0xF8, 0x08, 0x1F, 0x60, 0xF3, 0x06, 0x01, 0x04, 0xF8, + 0x11, 0x1B, 0x61, 0x7F, + 0x60, 0xF3, 0x05, 0x01, 0x61, 0x77, 0xA1, 0x7F, 0x60, 0xF3, 0x05, 0x01, + 0xA1, 0x77, 0xE0, 0x7F, + 0x20, 0xF0, 0x08, 0x00, 0xE0, 0x77, 0x1C, 0xBD, 0x1F, 0xB5, 0x04, 0x46, + 0x03, 0x99, 0x02, 0xA8, + 0x01, 0xF0, 0x3F, 0x01, 0x03, 0x91, 0x08, 0x21, 0x00, 0xF0, 0x0D, 0xFA, + 0xDD, 0xE9, 0x02, 0x12, + 0xA1, 0x64, 0x84, 0xF8, 0x4C, 0x20, 0x14, 0xF8, 0x70, 0x1F, 0x60, 0xF3, + 0x05, 0x01, 0x04, 0xF8, + 0x1E, 0x19, 0xE1, 0x7F, 0x60, 0xF3, 0x05, 0x01, 0xE1, 0x77, 0x1F, 0xBD, + 0x10, 0xB5, 0x30, 0xA3, + 0x89, 0xB0, 0x1C, 0xCB, 0x8D, 0xE8, 0x1C, 0x00, 0x30, 0xA4, 0x0D, 0xF1, + 0x0C, 0x0C, 0x1C, 0xCC, + 0x8C, 0xE8, 0x1C, 0x00, 0x30, 0xA4, 0x0D, 0xF1, 0x18, 0x0C, 0x1C, 0xCC, + 0x8C, 0xE8, 0x1C, 0x00, + 0x03, 0xAA, 0x53, 0x5C, 0x42, 0x7E, 0x63, 0xF3, 0x05, 0x02, 0x42, 0x76, + 0x1D, 0xF8, 0x01, 0x30, + 0x02, 0x7E, 0x63, 0xF3, 0x42, 0x02, 0x02, 0x76, 0x06, 0xAA, 0x52, 0x5C, + 0x81, 0x79, 0x62, 0xF3, + 0x83, 0x01, 0x81, 0x71, 0x09, 0xB0, 0x10, 0xBD, 0x1A, 0x49, 0x04, 0x20, + 0x08, 0x70, 0x1A, 0x49, + 0x24, 0x48, 0x08, 0x60, 0x24, 0x49, 0x01, 0x20, 0xC1, 0xF8, 0xC0, 0x00, + 0x23, 0x49, 0x4F, 0xF4, + 0xFA, 0x70, 0xFF, 0xF7, 0x39, 0xBD, 0x10, 0xB5, 0xB0, 0xF8, 0x56, 0x20, + 0x42, 0xF0, 0x01, 0x02, + 0xA0, 0xF8, 0x56, 0x20, 0x82, 0x79, 0x22, 0xF0, 0x01, 0x02, 0x42, 0xF0, + 0xE0, 0x02, 0x82, 0x71, + 0x0B, 0x4A, 0x11, 0x60, 0x0B, 0x4A, 0x04, 0x21, 0x11, 0x70, 0x0B, 0x4A, + 0x15, 0x49, 0x11, 0x60, + 0x01, 0x46, 0xA4, 0x22, 0x16, 0x48, 0x16, 0xF0, 0x58, 0xF9, 0x13, 0x49, + 0x01, 0x20, 0xC1, 0xF8, + 0xC0, 0x00, 0xBD, 0xE8, 0x10, 0x40, 0x11, 0x49, 0x4F, 0xF4, 0xFA, 0x70, + 0xFF, 0xF7, 0x14, 0xBD, + 0x24, 0x04, 0x10, 0x00, 0x14, 0x04, 0x10, 0x00, 0x18, 0x04, 0x10, 0x00, + 0xFF, 0xFF, 0x03, 0x1C, + 0x00, 0x00, 0x02, 0x02, 0x01, 0x01, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00, + 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x01, 0x01, 0x00, 0x00, 0x02, 0x03, 0x02, 0x03, + 0x02, 0x03, 0x02, 0x03, + 0x02, 0x03, 0x00, 0x00, 0x29, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x60, 0x22, + 0x21, 0x0D, 0x00, 0x00, + 0x00, 0x00, 0x03, 0x20, 0x35, 0x49, 0x01, 0x20, 0x08, 0x60, 0x17, 0x22, + 0x41, 0x07, 0x01, 0xF8, + 0x27, 0x2F, 0x0B, 0x22, 0x81, 0xF8, 0x41, 0x20, 0x30, 0x49, 0x18, 0x39, + 0x08, 0x60, 0x4F, 0xF0, + 0x08, 0x51, 0xC1, 0xF8, 0xC4, 0x03, 0x2D, 0x4A, 0x00, 0x21, 0xB0, 0x32, + 0x11, 0x60, 0x2C, 0x49, + 0x08, 0x60, 0x09, 0x1D, 0x08, 0x60, 0x29, 0x49, 0x94, 0x39, 0x08, 0x60, + 0x70, 0x47, 0x4F, 0xF0, + 0x00, 0x50, 0x00, 0x88, 0x70, 0x47, 0x4F, 0xF0, 0x00, 0x50, 0x80, 0x78, + 0x70, 0x47, 0x4F, 0xF0, + 0x00, 0x51, 0x88, 0x80, 0x70, 0x47, 0x21, 0x49, 0x01, 0x20, 0x30, 0x39, + 0x08, 0x60, 0x70, 0x47, + 0x4F, 0xF0, 0x00, 0x50, 0x10, 0xF8, 0x22, 0x1F, 0x21, 0xF0, 0x03, 0x01, + 0x01, 0x70, 0x70, 0x47, + 0x4F, 0xF0, 0x00, 0x53, 0x03, 0xF8, 0x1F, 0x0F, 0x59, 0x70, 0x98, 0x78, + 0x62, 0xF3, 0x03, 0x00, + 0x98, 0x70, 0x70, 0x47, 0x17, 0x48, 0x00, 0x68, 0x70, 0x47, 0x16, 0x49, + 0x08, 0x60, 0x70, 0x47, + 0x10, 0xB5, 0x4F, 0xF0, 0x00, 0x54, 0xE2, 0x7E, 0x22, 0xF0, 0xAF, 0x03, + 0x20, 0x22, 0x02, 0xEA, + 0x40, 0x12, 0x1A, 0x43, 0x04, 0x23, 0x03, 0xEA, 0x80, 0x00, 0x02, 0x43, + 0x01, 0xF0, 0x03, 0x00, + 0x02, 0x43, 0xE2, 0x76, 0x10, 0xBD, 0x4F, 0xF0, 0x00, 0x50, 0x90, 0xF8, + 0x22, 0x00, 0x10, 0xF0, + 0x03, 0x00, 0x05, 0xD0, 0x01, 0x28, 0x05, 0xD0, 0x02, 0x28, 0x05, 0xD0, + 0x06, 0x48, 0x70, 0x47, + 0x06, 0x48, 0x70, 0x47, 0x06, 0x48, 0x70, 0x47, 0x06, 0x48, 0x70, 0x47, + 0xCC, 0x04, 0x00, 0x22, + 0x80, 0x1E, 0x00, 0x22, 0x28, 0x04, 0x10, 0x00, 0x00, 0x12, 0x7A, 0x00, + 0x00, 0x90, 0xD0, 0x03, + 0x00, 0x48, 0xE8, 0x01, 0x00, 0x24, 0xF4, 0x00, 0x30, 0xB5, 0x6C, 0x4C, + 0x01, 0x25, 0x25, 0x60, + 0xE5, 0x06, 0x03, 0xF0, 0x1F, 0x03, 0x85, 0xF8, 0x6B, 0x30, 0x00, 0x23, + 0x05, 0xE0, 0x00, 0xBF, + 0x51, 0xF8, 0x23, 0x50, 0x40, 0xF8, 0x23, 0x50, 0x5B, 0x1C, 0x93, 0x42, + 0xF8, 0xDB, 0x00, 0x20, + 0x20, 0x60, 0x30, 0xBD, 0xF0, 0xB5, 0x06, 0x46, 0x00, 0x20, 0x04, 0x46, + 0xDF, 0xF8, 0x80, 0xC1, + 0x14, 0xE0, 0x56, 0xF8, 0x24, 0x70, 0x03, 0x23, 0xDD, 0x00, 0x47, 0xFA, + 0x05, 0xF2, 0x80, 0xEA, + 0x02, 0x60, 0x08, 0x25, 0x00, 0x28, 0x02, 0xDA, 0x8C, 0xEA, 0x40, 0x00, + 0x00, 0xE0, 0x40, 0x00, + 0x6D, 0x1E, 0x00, 0x2D, 0xF6, 0xDC, 0x5B, 0x1E, 0xEE, 0xD5, 0x64, 0x1C, + 0x8C, 0x42, 0xE8, 0xDB, + 0xF0, 0xBD, 0xF0, 0xB5, 0xB1, 0xB0, 0x07, 0x46, 0x6D, 0x46, 0x52, 0x4E, + 0x01, 0x24, 0xC0, 0x21, + 0x28, 0x46, 0x16, 0xF0, 0x13, 0xF9, 0x00, 0x20, 0x56, 0xF8, 0x20, 0x10, + 0x45, 0xF8, 0x20, 0x10, + 0x40, 0x1C, 0x30, 0x28, 0xF8, 0xD3, 0x4C, 0x49, 0x00, 0x98, 0x88, 0x42, + 0x10, 0xD1, 0x02, 0x98, + 0x81, 0xB2, 0x05, 0xA8, 0x04, 0x46, 0xFF, 0xF7, 0xC5, 0xFF, 0x01, 0x99, + 0x81, 0x42, 0x06, 0xD1, + 0xAC, 0x22, 0x21, 0x46, 0x38, 0x46, 0x16, 0xF0, 0x9D, 0xF8, 0x00, 0x24, + 0x00, 0xE0, 0x03, 0x24, + 0x31, 0xB0, 0x20, 0x46, 0xF0, 0xBD, 0x2D, 0xE9, 0xF0, 0x41, 0x41, 0x4D, + 0x07, 0x46, 0x3F, 0x4E, + 0x01, 0x24, 0x4F, 0xF4, 0x58, 0x71, 0x28, 0x46, 0x16, 0xF0, 0xE8, 0xF8, + 0x5F, 0xF0, 0x00, 0x00, + 0x56, 0xF8, 0x20, 0x10, 0x45, 0xF8, 0x20, 0x10, 0x40, 0x1C, 0xD8, 0x28, + 0xF8, 0xD3, 0x38, 0x4D, + 0x38, 0x49, 0x28, 0x68, 0x88, 0x42, 0x12, 0xD1, 0x28, 0x89, 0x81, 0xB2, + 0x05, 0xF1, 0x14, 0x00, + 0xFF, 0xF7, 0x98, 0xFF, 0x69, 0x68, 0x81, 0x42, 0x08, 0xD1, 0x4F, 0xF4, + 0x52, 0x72, 0x05, 0xF1, + 0x18, 0x01, 0x38, 0x46, 0x16, 0xF0, 0x29, 0xF8, 0x00, 0x24, 0x00, 0xE0, + 0x03, 0x24, 0x20, 0x46, + 0xBD, 0xE8, 0xF0, 0x81, 0x70, 0xB5, 0x94, 0xB0, 0x04, 0x46, 0x6D, 0x46, + 0x2A, 0x4E, 0x50, 0x21, + 0x28, 0x46, 0x16, 0xF0, 0xBB, 0xF8, 0x00, 0x20, 0x56, 0xF8, 0x20, 0x10, + 0x45, 0xF8, 0x20, 0x10, + 0x40, 0x1C, 0x14, 0x28, 0xF8, 0xD3, 0x25, 0x49, 0x00, 0x98, 0x6F, 0xF0, + 0x02, 0x05, 0x88, 0x42, + 0x2E, 0xD1, 0xA8, 0x10, 0x20, 0x60, 0x60, 0x60, 0xA0, 0x60, 0xE0, 0x60, + 0x20, 0x61, 0x02, 0x98, + 0x81, 0xB2, 0x05, 0xA8, 0xFF, 0xF7, 0x66, 0xFF, 0x01, 0x99, 0x81, 0x42, + 0x20, 0xD1, 0xBD, 0xF8, + 0x38, 0x10, 0x45, 0xF2, 0xAA, 0x50, 0x81, 0x42, 0x0C, 0xD1, 0x9D, 0xF8, + 0x1D, 0x10, 0x21, 0x60, + 0x9D, 0xF8, 0x1C, 0x10, 0x01, 0xF0, 0x0F, 0x01, 0x61, 0x60, 0x9D, 0xF8, + 0x1E, 0x10, 0x01, 0xF0, + 0x0F, 0x01, 0xA1, 0x60, 0xBD, 0xF8, 0x3C, 0x10, 0x81, 0x42, 0x01, 0xD1, + 0x08, 0x99, 0xE1, 0x60, + 0xBD, 0xF8, 0x40, 0x10, 0x81, 0x42, 0x01, 0xD1, 0x09, 0x98, 0x20, 0x61, + 0x14, 0xB0, 0x70, 0xBD, + 0x25, 0x60, 0x65, 0x60, 0xA5, 0x60, 0xE5, 0x60, 0x25, 0x61, 0xF7, 0xE7, + 0xC4, 0x1B, 0x00, 0x22, + 0x1B, 0x00, 0x00, 0x80, 0x48, 0x1D, 0x04, 0x00, 0xB2, 0x57, 0x0E, 0x70, + 0xE8, 0x19, 0x04, 0x00, + 0x54, 0x0B, 0x10, 0x00, 0xA1, 0x46, 0x0D, 0x60, 0xAC, 0x1E, 0x04, 0x00, + 0x5C, 0x00, 0x0D, 0x60, + 0xF0, 0xB5, 0x07, 0x46, 0x00, 0x20, 0x03, 0x46, 0x2F, 0x4E, 0x11, 0xE0, + 0x57, 0xF8, 0x23, 0x50, + 0x03, 0x22, 0x00, 0xBF, 0x4F, 0xEA, 0xC2, 0x0C, 0x45, 0xFA, 0x0C, 0xF4, + 0x84, 0xEA, 0x20, 0x64, + 0xE4, 0xB2, 0x52, 0x1E, 0x56, 0xF8, 0x24, 0x40, 0x84, 0xEA, 0x00, 0x20, + 0xF2, 0xD5, 0x5B, 0x1C, + 0x8B, 0x42, 0xEB, 0xDB, 0xF0, 0xBD, 0x30, 0xB5, 0x02, 0x46, 0x23, 0x4B, + 0x00, 0x20, 0x10, 0x3B, + 0x08, 0xE0, 0x54, 0x5C, 0x04, 0xF0, 0x0F, 0x05, 0x03, 0xEB, 0x14, 0x14, + 0x5D, 0x5D, 0x24, 0x78, + 0x28, 0x44, 0x20, 0x44, 0x49, 0x1E, 0xF4, 0xD2, 0x30, 0xBD, 0x10, 0xB5, + 0x84, 0x68, 0x03, 0x68, + 0xE4, 0x43, 0xA3, 0x42, 0x0A, 0xD1, 0xC4, 0x68, 0x43, 0x68, 0xE4, 0x43, + 0xA3, 0x42, 0x05, 0xD1, + 0x5B, 0x1C, 0x0B, 0x60, 0x00, 0x68, 0x10, 0x60, 0x01, 0x20, 0x10, 0xBD, + 0x00, 0x20, 0x10, 0xBD, + 0x3C, 0xB5, 0x04, 0x46, 0x00, 0x25, 0x01, 0xAA, 0x69, 0x46, 0xFF, 0xF7, + 0xE6, 0xFF, 0x01, 0x28, + 0x09, 0xD1, 0x00, 0x99, 0x04, 0xF1, 0x10, 0x00, 0x09, 0x1F, 0xFF, 0xF7, + 0xB1, 0xFF, 0x01, 0x99, + 0x88, 0x42, 0x00, 0xD1, 0x01, 0x25, 0x28, 0x46, 0x3C, 0xBD, 0x30, 0xB5, + 0x0D, 0x46, 0x04, 0x46, + 0x09, 0x1F, 0x10, 0x30, 0xFF, 0xF7, 0xA4, 0xFF, 0x6D, 0x1E, 0x65, 0x60, + 0xE9, 0x43, 0xE1, 0x60, + 0x20, 0x60, 0xC0, 0x43, 0xA0, 0x60, 0x30, 0xBD, 0xFC, 0x74, 0x01, 0x00, + 0x4F, 0xF0, 0x00, 0x50, + 0x00, 0x21, 0x80, 0xF8, 0x3D, 0x10, 0x80, 0xF8, 0x3E, 0x10, 0x80, 0xF8, + 0x3F, 0x10, 0x3F, 0x22, + 0x80, 0xF8, 0x32, 0x20, 0x00, 0xF8, 0x30, 0x1F, 0x02, 0x71, 0x41, 0x71, + 0x42, 0x70, 0x01, 0x72, + 0x41, 0x72, 0x81, 0x72, 0xC1, 0x72, 0xC1, 0x71, 0x70, 0x47, 0x28, 0x4A, + 0x10, 0x60, 0x27, 0x48, + 0x0C, 0x30, 0x01, 0x60, 0x70, 0x47, 0x30, 0xB5, 0x01, 0x24, 0x84, 0x40, + 0x4F, 0xF0, 0x00, 0x50, + 0x00, 0x29, 0x03, 0x9D, 0x90, 0xF8, 0x32, 0x10, 0xE4, 0xB2, 0x01, 0xD0, + 0x21, 0x43, 0x00, 0xE0, + 0xA1, 0x43, 0x80, 0xF8, 0x32, 0x10, 0x90, 0xF8, 0x30, 0x10, 0x0A, 0xB1, + 0x21, 0x43, 0x00, 0xE0, + 0xA1, 0x43, 0x80, 0xF8, 0x30, 0x10, 0x90, 0xF8, 0x34, 0x10, 0x0B, 0xB1, + 0x21, 0x43, 0x00, 0xE0, + 0xA1, 0x43, 0x80, 0xF8, 0x34, 0x10, 0x10, 0xF8, 0x35, 0x1F, 0x0D, 0xB1, + 0x21, 0x43, 0x00, 0xE0, + 0xA1, 0x43, 0x01, 0x70, 0x30, 0xBD, 0x4F, 0xF0, 0x00, 0x52, 0x06, 0x28, + 0x17, 0xD2, 0xDF, 0xE8, + 0x00, 0xF0, 0x03, 0x06, 0x09, 0x0F, 0x17, 0x1A, 0x12, 0xF8, 0x3D, 0x0F, + 0x04, 0xE0, 0x12, 0xF8, + 0x3D, 0x0F, 0x07, 0xE0, 0x12, 0xF8, 0x3E, 0x0F, 0x20, 0xF0, 0x0F, 0x00, + 0x08, 0x43, 0x05, 0xE0, + 0x12, 0xF8, 0x3E, 0x0F, 0x20, 0xF0, 0xF0, 0x00, 0x40, 0xEA, 0x01, 0x10, + 0x10, 0x70, 0x70, 0x47, + 0x12, 0xF8, 0x3F, 0x0F, 0xF0, 0xE7, 0x12, 0xF8, 0x3F, 0x0F, 0xF3, 0xE7, + 0x38, 0x05, 0x00, 0x22, + 0xF0, 0xB5, 0x63, 0x4C, 0xD0, 0xE9, 0x00, 0x25, 0x01, 0x21, 0xE3, 0x1D, + 0x06, 0x7A, 0xFF, 0x27, + 0x48, 0x07, 0x80, 0xF8, 0xE5, 0x70, 0x80, 0xF8, 0xE4, 0x70, 0x00, 0x27, + 0xC0, 0xF8, 0xC0, 0x70, + 0xC0, 0xF8, 0xE8, 0x70, 0x80, 0xF8, 0xE1, 0x70, 0x80, 0xF8, 0xE3, 0x70, + 0x8E, 0x46, 0x58, 0xE0, + 0x17, 0x78, 0x04, 0xF8, 0x01, 0x7B, 0x2F, 0x88, 0x23, 0xF8, 0x02, 0x7B, + 0xD7, 0x78, 0x01, 0x2F, + 0x02, 0xD0, 0x02, 0x2F, 0x09, 0xD0, 0x10, 0xE0, 0x90, 0xF8, 0xE4, 0x70, + 0x0E, 0xFA, 0x01, 0xFC, + 0x27, 0xEA, 0x0C, 0x07, 0x80, 0xF8, 0xE4, 0x70, 0x07, 0xE0, 0x90, 0xF8, + 0xE5, 0x70, 0x0E, 0xFA, + 0x01, 0xFC, 0x27, 0xEA, 0x0C, 0x07, 0x80, 0xF8, 0xE5, 0x70, 0x4F, 0x1E, + 0x07, 0xEB, 0x47, 0x0C, + 0x27, 0xF0, 0x01, 0x07, 0xBC, 0x44, 0x57, 0x78, 0x07, 0xF0, 0x07, 0x07, + 0x07, 0xFA, 0x0C, 0xF7, + 0xD0, 0xF8, 0xC0, 0xC0, 0x47, 0xEA, 0x0C, 0x07, 0xC0, 0xF8, 0xC0, 0x70, + 0x97, 0x78, 0x4F, 0xEA, + 0x81, 0x0C, 0x07, 0xF0, 0x0F, 0x07, 0xAC, 0xF1, 0x04, 0x0C, 0x07, 0xFA, + 0x0C, 0xF7, 0xD0, 0xF8, + 0xE8, 0xC0, 0x47, 0xEA, 0x0C, 0x07, 0xC0, 0xF8, 0xE8, 0x70, 0x17, 0x79, + 0xFF, 0x07, 0x07, 0xD0, + 0x90, 0xF8, 0xE1, 0xC0, 0x0E, 0xFA, 0x01, 0xF7, 0x4C, 0xEA, 0x07, 0x0C, + 0x80, 0xF8, 0xE1, 0xC0, + 0x17, 0x79, 0xFF, 0x06, 0x07, 0xD5, 0x90, 0xF8, 0xE3, 0x70, 0x0E, 0xFA, + 0x01, 0xFC, 0x47, 0xEA, + 0x0C, 0x07, 0x80, 0xF8, 0xE3, 0x70, 0x49, 0x1C, 0x08, 0x29, 0x04, 0xD2, + 0x52, 0x1D, 0xAD, 0x1C, + 0x76, 0x1E, 0x00, 0x2E, 0xA4, 0xD1, 0xD0, 0xF8, 0xE8, 0x10, 0x41, 0x64, + 0xF0, 0xBD, 0xF0, 0xB5, + 0x27, 0x4D, 0x28, 0x49, 0x01, 0x26, 0xEC, 0x1D, 0xC3, 0x79, 0x00, 0x22, + 0x07, 0x68, 0x4A, 0x60, + 0x09, 0x04, 0x4F, 0xF0, 0xFF, 0x0E, 0x81, 0xF8, 0xE5, 0xE0, 0x81, 0xF8, + 0xE4, 0x60, 0xC1, 0xF8, + 0xC0, 0x20, 0x81, 0xF8, 0xE1, 0x20, 0x81, 0xF8, 0xE3, 0x20, 0x18, 0xE0, + 0x97, 0xF8, 0x00, 0xC0, + 0x05, 0xF8, 0x01, 0xCB, 0xB0, 0xF8, 0x04, 0xC0, 0x24, 0xF8, 0x02, 0xCB, + 0x90, 0xF8, 0x06, 0xC0, + 0x4F, 0xEA, 0x86, 0x0E, 0x0C, 0xF0, 0x0F, 0x0C, 0xAE, 0xF1, 0x04, 0x0E, + 0x0C, 0xFA, 0x0E, 0xFC, + 0x76, 0x1C, 0x4C, 0xEA, 0x02, 0x02, 0x08, 0x2E, 0x03, 0xD2, 0x5B, 0x1E, + 0x7F, 0x1C, 0x00, 0x2B, + 0xE4, 0xD1, 0xC1, 0xF8, 0xE8, 0x20, 0x4A, 0x64, 0xF0, 0xBD, 0x0D, 0x4A, + 0xD2, 0x1D, 0x22, 0xF8, + 0x10, 0x10, 0x70, 0x47, 0x10, 0xB5, 0x04, 0x46, 0x0B, 0x48, 0x00, 0x68, + 0x38, 0xB1, 0xA0, 0x68, + 0xFF, 0xF7, 0x46, 0xFF, 0x20, 0x68, 0xBD, 0xE8, 0x10, 0x40, 0x00, 0xF0, + 0x0F, 0xB8, 0xE0, 0x68, + 0xFF, 0xF7, 0x3E, 0xFF, 0x60, 0x68, 0xBD, 0xE8, 0x10, 0x40, 0x00, 0xF0, + 0xAB, 0xB8, 0x00, 0x00, + 0xAB, 0x00, 0x00, 0x20, 0x00, 0x20, 0x00, 0x22, 0xBC, 0x05, 0x00, 0x22, + 0x10, 0xB5, 0x4F, 0x49, + 0x02, 0x68, 0x00, 0x24, 0x8A, 0x60, 0x82, 0x88, 0x4A, 0x80, 0x03, 0x89, + 0x8B, 0x80, 0x0C, 0x70, + 0x4F, 0xF0, 0x00, 0x51, 0xA1, 0xF8, 0x1A, 0x31, 0xA1, 0xF8, 0x1E, 0x21, + 0xC0, 0x88, 0xA1, 0xF8, + 0x20, 0x01, 0x47, 0x48, 0x01, 0x21, 0xC0, 0xF8, 0xD8, 0x11, 0xC0, 0xF8, + 0xDC, 0x11, 0xC0, 0xF8, + 0xC4, 0x11, 0xC0, 0xF8, 0xCC, 0x11, 0xC0, 0xF8, 0xD4, 0x11, 0xC0, 0xF8, + 0x80, 0x11, 0x72, 0xB6, + 0xD0, 0xF8, 0x08, 0x22, 0x00, 0x2A, 0xFB, 0xD1, 0x41, 0x60, 0x62, 0xB6, + 0x10, 0xBD, 0x2D, 0xE9, + 0xF0, 0x41, 0x3B, 0x4C, 0xD4, 0xF8, 0x5C, 0x01, 0x38, 0x4E, 0x25, 0x04, + 0x01, 0x27, 0xB2, 0x68, + 0x50, 0xB3, 0xB5, 0xF8, 0x1C, 0x81, 0x5F, 0xEA, 0x08, 0x00, 0x0F, 0xD0, + 0xD4, 0xF8, 0x54, 0x01, + 0x10, 0xB9, 0xD4, 0xF8, 0x00, 0x01, 0x48, 0xB1, 0x41, 0x46, 0x01, 0x20, + 0x90, 0x47, 0xC4, 0xF8, + 0xD8, 0x72, 0xD4, 0xF8, 0x54, 0x01, 0x08, 0xB1, 0xC4, 0xF8, 0xD4, 0x72, + 0xB2, 0x68, 0x41, 0x46, + 0x02, 0x20, 0x90, 0x47, 0x01, 0x78, 0x02, 0x29, 0x02, 0xD0, 0x03, 0x29, + 0x02, 0xD0, 0x08, 0xE0, + 0x41, 0x88, 0x00, 0xE0, 0x71, 0x88, 0xA5, 0xF8, 0x1E, 0x11, 0x80, 0x88, + 0xA5, 0xF8, 0x1A, 0x01, + 0x37, 0x70, 0xC4, 0xF8, 0xDC, 0x72, 0x38, 0xE0, 0xD4, 0xF8, 0x44, 0x01, + 0x80, 0xB1, 0xB5, 0xF8, + 0x1C, 0x11, 0x03, 0x20, 0x90, 0x47, 0x30, 0x78, 0x38, 0xB1, 0x70, 0x88, + 0xA5, 0xF8, 0x1E, 0x01, + 0xB0, 0x88, 0xA5, 0xF8, 0x1A, 0x01, 0x00, 0x20, 0x30, 0x70, 0xC4, 0xF8, + 0xC4, 0x72, 0x24, 0xE0, + 0xD4, 0xF8, 0x58, 0x01, 0x30, 0xB1, 0xB5, 0xF8, 0x1C, 0x11, 0x01, 0x20, + 0x90, 0x47, 0xC4, 0xF8, + 0xD8, 0x72, 0x1A, 0xE0, 0xD4, 0xF8, 0x4C, 0x01, 0x28, 0xB9, 0xD4, 0xF8, + 0x54, 0x01, 0x10, 0xB9, + 0xD4, 0xF8, 0x00, 0x01, 0x88, 0xB1, 0xB5, 0xF8, 0x1C, 0x11, 0x19, 0xB1, + 0x01, 0x20, 0x90, 0x47, + 0xC4, 0xF8, 0xD8, 0x72, 0xD4, 0xF8, 0x4C, 0x01, 0x10, 0xB1, 0xC4, 0xF8, + 0xCC, 0x72, 0x04, 0xE0, + 0xD4, 0xF8, 0x54, 0x01, 0x08, 0xB1, 0xC4, 0xF8, 0xD4, 0x72, 0xD4, 0xF8, + 0x00, 0x01, 0x00, 0x28, + 0x01, 0xD0, 0xC4, 0xF8, 0x80, 0x72, 0xBD, 0xE8, 0xF0, 0x81, 0x00, 0x00, + 0x2C, 0x04, 0x10, 0x00, + 0x00, 0x20, 0x00, 0x22, 0xC1, 0x7D, 0x01, 0x29, 0x05, 0xD1, 0x49, 0x07, + 0x11, 0xF8, 0x2D, 0x2F, + 0x42, 0xF0, 0x02, 0x02, 0x0A, 0x70, 0x04, 0x49, 0x02, 0x68, 0x4A, 0x60, + 0x42, 0x68, 0x8A, 0x60, + 0x80, 0x7D, 0x08, 0x70, 0x70, 0x47, 0x00, 0x00, 0x38, 0x04, 0x10, 0x00, + 0xE9, 0x49, 0x00, 0x20, + 0x08, 0x60, 0x09, 0x1D, 0x08, 0x60, 0x09, 0x1D, 0x08, 0x60, 0x09, 0x1D, + 0x08, 0x60, 0x09, 0x1D, + 0x08, 0x60, 0x09, 0x1D, 0x08, 0x60, 0x09, 0x1D, 0x08, 0x60, 0x09, 0x1D, + 0x08, 0x60, 0xE2, 0x49, + 0x01, 0x22, 0x0A, 0x60, 0xE0, 0x49, 0x14, 0x31, 0x08, 0x60, 0x51, 0x07, + 0x01, 0xF8, 0x50, 0x0F, + 0xF0, 0x23, 0x4B, 0x70, 0x0F, 0x23, 0x01, 0xF8, 0x05, 0x3C, 0x10, 0x23, + 0x4B, 0x80, 0x8A, 0x80, + 0xDA, 0x49, 0x08, 0x60, 0x48, 0x60, 0x88, 0x60, 0xC8, 0x60, 0x08, 0x61, + 0x48, 0x61, 0x88, 0x61, + 0xC8, 0x61, 0x08, 0x62, 0x70, 0x47, 0x70, 0x47, 0xD4, 0x4A, 0x11, 0x60, + 0xD1, 0x49, 0x00, 0x22, + 0x0A, 0x60, 0x00, 0xEB, 0xC0, 0x02, 0x02, 0xEB, 0x00, 0x10, 0x4F, 0xF6, + 0xFF, 0x72, 0x02, 0xEA, + 0x80, 0x00, 0x4F, 0xF0, 0x00, 0x52, 0xA2, 0xF8, 0x58, 0x00, 0x01, 0x20, + 0x08, 0x60, 0x70, 0x47, + 0x4F, 0xF0, 0x00, 0x50, 0x90, 0xF8, 0x22, 0x00, 0xC0, 0xF3, 0x81, 0x00, + 0x28, 0xB1, 0x01, 0x28, + 0x05, 0xD0, 0x02, 0x28, 0x05, 0xD0, 0xC6, 0x48, 0x70, 0x47, 0xC6, 0x48, + 0x70, 0x47, 0xC6, 0x48, + 0x70, 0x47, 0xC6, 0x48, 0x70, 0x47, 0x70, 0xB5, 0x4F, 0xF0, 0x00, 0x54, + 0x72, 0xB6, 0x65, 0x69, + 0x60, 0x69, 0x40, 0xF0, 0x04, 0x00, 0x60, 0x61, 0xBA, 0x49, 0x00, 0x20, + 0x08, 0x60, 0xB4, 0xF8, + 0x58, 0x10, 0x09, 0xB1, 0xA4, 0xF8, 0x58, 0x00, 0xFF, 0xF7, 0x35, 0xFC, + 0x01, 0x46, 0xFF, 0xF7, + 0xD7, 0xFF, 0xB1, 0xFB, 0xF0, 0xF0, 0xFE, 0xF7, 0xCD, 0xFD, 0x65, 0x61, + 0x02, 0x20, 0x00, 0xF0, + 0x56, 0xF9, 0x62, 0xB6, 0x70, 0xBD, 0xB1, 0x48, 0x10, 0xB5, 0x00, 0x21, + 0x04, 0x68, 0x01, 0x60, + 0xFF, 0xF7, 0xD9, 0xFF, 0x1C, 0xB1, 0xA4, 0x46, 0xBD, 0xE8, 0x10, 0x40, + 0x60, 0x47, 0x00, 0x20, + 0xFF, 0xF7, 0xA9, 0xFF, 0x10, 0xBD, 0xA9, 0x4A, 0x51, 0x60, 0xA6, 0x49, + 0x00, 0x22, 0x09, 0x1D, + 0x0A, 0x60, 0x00, 0xEB, 0xC0, 0x02, 0x02, 0xEB, 0x00, 0x10, 0x4F, 0xF6, + 0xFF, 0x72, 0x02, 0xEA, + 0x80, 0x00, 0xCA, 0x06, 0xA2, 0xF8, 0x5A, 0x00, 0x01, 0x20, 0x08, 0x60, + 0x70, 0x47, 0x70, 0xB5, + 0x4F, 0xF0, 0x00, 0x54, 0x72, 0xB6, 0x65, 0x69, 0x60, 0x69, 0x40, 0xF0, + 0x08, 0x00, 0x60, 0x61, + 0x98, 0x49, 0x00, 0x20, 0x09, 0x1D, 0x08, 0x60, 0xB4, 0xF8, 0x5A, 0x10, + 0x09, 0xB1, 0xA4, 0xF8, + 0x5A, 0x00, 0xFF, 0xF7, 0xF0, 0xFB, 0x01, 0x46, 0xFF, 0xF7, 0x92, 0xFF, + 0xB1, 0xFB, 0xF0, 0xF0, + 0xFE, 0xF7, 0x88, 0xFD, 0x65, 0x61, 0x03, 0x20, 0x00, 0xF0, 0x11, 0xF9, + 0x62, 0xB6, 0x70, 0xBD, + 0x8E, 0x48, 0x10, 0xB5, 0x00, 0x21, 0x44, 0x68, 0x41, 0x60, 0xFF, 0xF7, + 0xD8, 0xFF, 0x1C, 0xB1, + 0xA4, 0x46, 0xBD, 0xE8, 0x10, 0x40, 0x60, 0x47, 0x01, 0x20, 0xFF, 0xF7, + 0x64, 0xFF, 0x10, 0xBD, + 0x70, 0xB5, 0x4F, 0xF0, 0x00, 0x54, 0x72, 0xB6, 0x65, 0x69, 0x60, 0x69, + 0x40, 0xF0, 0x10, 0x00, + 0x60, 0x61, 0x80, 0x49, 0x00, 0x20, 0x08, 0x31, 0x08, 0x60, 0xB4, 0xF8, + 0x5C, 0x10, 0x09, 0xB1, + 0xA4, 0xF8, 0x5C, 0x00, 0xFF, 0xF7, 0xBF, 0xFB, 0x01, 0x46, 0xFF, 0xF7, + 0x61, 0xFF, 0xB1, 0xFB, + 0xF0, 0xF0, 0xFE, 0xF7, 0x57, 0xFD, 0x65, 0x61, 0x04, 0x20, 0x00, 0xF0, + 0xE0, 0xF8, 0x62, 0xB6, + 0x70, 0xBD, 0x76, 0x48, 0x10, 0xB5, 0x00, 0x21, 0x84, 0x68, 0x81, 0x60, + 0xFF, 0xF7, 0xD8, 0xFF, + 0x1C, 0xB1, 0xA4, 0x46, 0xBD, 0xE8, 0x10, 0x40, 0x60, 0x47, 0x02, 0x20, + 0xFF, 0xF7, 0x33, 0xFF, + 0x10, 0xBD, 0x70, 0xB5, 0x4F, 0xF0, 0x00, 0x54, 0x72, 0xB6, 0x65, 0x69, + 0x60, 0x69, 0x40, 0xF0, + 0x20, 0x00, 0x60, 0x61, 0x67, 0x49, 0x00, 0x20, 0x0C, 0x31, 0x08, 0x60, + 0xB4, 0xF8, 0x5E, 0x10, + 0x09, 0xB1, 0xA4, 0xF8, 0x5E, 0x00, 0xFF, 0xF7, 0x8E, 0xFB, 0x01, 0x46, + 0xFF, 0xF7, 0x30, 0xFF, + 0xB1, 0xFB, 0xF0, 0xF0, 0xFE, 0xF7, 0x26, 0xFD, 0x65, 0x61, 0x05, 0x20, + 0x00, 0xF0, 0xAF, 0xF8, + 0x62, 0xB6, 0x70, 0xBD, 0x5D, 0x48, 0x10, 0xB5, 0x00, 0x21, 0xC4, 0x68, + 0xC1, 0x60, 0xFF, 0xF7, + 0xD8, 0xFF, 0x1C, 0xB1, 0xA4, 0x46, 0xBD, 0xE8, 0x10, 0x40, 0x60, 0x47, + 0x03, 0x20, 0xFF, 0xF7, + 0x02, 0xFF, 0x10, 0xBD, 0x4F, 0xF0, 0x00, 0x50, 0xB0, 0xF8, 0x60, 0x00, + 0x70, 0x47, 0x53, 0x49, + 0x10, 0xB5, 0x00, 0x22, 0x08, 0x69, 0x0A, 0x61, 0x4E, 0x49, 0x10, 0x31, + 0x0A, 0x60, 0x10, 0xB1, + 0xBD, 0xE8, 0x10, 0x40, 0x00, 0x47, 0x04, 0x20, 0xFF, 0xF7, 0xED, 0xFE, + 0x10, 0xBD, 0x4B, 0x4A, + 0x00, 0x23, 0x51, 0x61, 0x47, 0x49, 0x14, 0x31, 0x0B, 0x60, 0x12, 0x6A, + 0x50, 0x43, 0x4F, 0xF0, + 0x00, 0x52, 0x80, 0x09, 0xA2, 0xF8, 0x62, 0x00, 0x01, 0x20, 0x08, 0x60, + 0x70, 0x47, 0x41, 0x49, + 0x00, 0x20, 0x14, 0x31, 0x08, 0x60, 0x70, 0x47, 0x40, 0x49, 0x10, 0xB5, + 0x00, 0x22, 0x48, 0x69, + 0x4A, 0x61, 0x3C, 0x49, 0x14, 0x31, 0x0A, 0x60, 0x10, 0xB1, 0xBD, 0xE8, + 0x10, 0x40, 0x00, 0x47, + 0x05, 0x20, 0xFF, 0xF7, 0xC8, 0xFE, 0x10, 0xBD, 0x38, 0x49, 0x10, 0xB5, + 0x00, 0x22, 0x88, 0x69, + 0x8A, 0x61, 0x34, 0x49, 0x18, 0x31, 0x0A, 0x60, 0x10, 0xB1, 0xBD, 0xE8, + 0x10, 0x40, 0x00, 0x47, + 0x06, 0x20, 0xFF, 0xF7, 0xB8, 0xFE, 0x10, 0xBD, 0x10, 0xB5, 0xFF, 0xF7, + 0x0B, 0xFB, 0x4F, 0xF0, + 0x00, 0x51, 0x91, 0xF8, 0x22, 0x20, 0x42, 0xF2, 0x10, 0x73, 0xB0, 0xFB, + 0xF3, 0xF0, 0xC2, 0xF3, + 0x81, 0x02, 0x52, 0x1C, 0xD0, 0x40, 0xB1, 0xF8, 0x54, 0x20, 0xC9, 0x6C, + 0x51, 0x43, 0x89, 0x09, + 0xB0, 0xFB, 0xF1, 0xF0, 0x10, 0xBD, 0x25, 0x49, 0xC8, 0x61, 0x22, 0x49, + 0x01, 0x20, 0xB8, 0x31, + 0x08, 0x60, 0x09, 0x1D, 0x08, 0x60, 0x70, 0x47, 0x20, 0x48, 0x10, 0xB5, + 0x00, 0x22, 0xC1, 0x69, + 0xC2, 0x61, 0x51, 0xB1, 0x1B, 0x48, 0xB8, 0x30, 0x02, 0x60, 0x4F, 0xF0, + 0x00, 0x50, 0xC0, 0x6C, + 0xBD, 0xE8, 0x10, 0x40, 0x20, 0xF0, 0x7F, 0x40, 0x08, 0x47, 0xFF, 0x20, + 0xFF, 0xF7, 0x83, 0xFE, + 0x10, 0xBD, 0x10, 0xB5, 0x04, 0x46, 0xFF, 0xF7, 0xD5, 0xFA, 0x4F, 0xF0, + 0x00, 0x51, 0x91, 0xF8, + 0x22, 0x10, 0x42, 0xF2, 0x10, 0x72, 0xB0, 0xFB, 0xF2, 0xF0, 0xC1, 0xF3, + 0x81, 0x01, 0x49, 0x1C, + 0xC8, 0x40, 0x00, 0x03, 0xB0, 0xFB, 0xF4, 0xF0, 0x0C, 0x49, 0x08, 0x62, + 0x10, 0xBD, 0x4F, 0xF0, + 0x00, 0x50, 0x90, 0xF8, 0x4A, 0x00, 0x00, 0x07, 0x00, 0xD0, 0x01, 0x20, + 0x70, 0x47, 0x00, 0xF0, + 0x1F, 0x02, 0x01, 0x21, 0x91, 0x40, 0x40, 0x09, 0x80, 0x00, 0x00, 0xF1, + 0xE0, 0x20, 0xC0, 0xF8, + 0x80, 0x12, 0x70, 0x47, 0x40, 0x09, 0x00, 0x22, 0xC0, 0x04, 0x00, 0x22, + 0x44, 0x04, 0x10, 0x00, + 0x00, 0x09, 0x3D, 0x00, 0x00, 0x48, 0xE8, 0x01, 0x00, 0x24, 0xF4, 0x00, + 0x00, 0x12, 0x7A, 0x00, + 0x10, 0xB5, 0x01, 0x46, 0x34, 0x22, 0xF6, 0x48, 0x15, 0xF0, 0xCC, 0xFB, + 0x01, 0x21, 0xBD, 0xE8, + 0x10, 0x40, 0x00, 0x20, 0xFE, 0xF7, 0x8D, 0xBD, 0x38, 0xB5, 0x04, 0x46, + 0xF1, 0x48, 0xF0, 0x4D, + 0x20, 0x60, 0x20, 0x46, 0x15, 0xF0, 0x03, 0xF8, 0x00, 0x22, 0x6B, 0x46, + 0x01, 0x21, 0x10, 0x46, + 0xFE, 0xF7, 0xC8, 0xFD, 0x01, 0x21, 0x00, 0x20, 0xFE, 0xF7, 0xA7, 0xFD, + 0x28, 0x78, 0x00, 0x28, + 0x02, 0xD0, 0x10, 0xF0, 0x8A, 0xFD, 0xEC, 0xE7, 0x38, 0xBD, 0xE7, 0x48, + 0x02, 0x68, 0x92, 0xF8, + 0xB0, 0x00, 0xC1, 0x07, 0x0E, 0xD0, 0xE2, 0x49, 0x01, 0x23, 0x34, 0x31, + 0x4B, 0x73, 0xC0, 0xF3, + 0xC1, 0x03, 0x02, 0x2B, 0x01, 0xD1, 0x00, 0x23, 0x4B, 0x73, 0x80, 0x07, + 0x02, 0xD5, 0x92, 0xF8, + 0xBB, 0x00, 0x08, 0x73, 0x70, 0x47, 0xDC, 0x48, 0x10, 0xB5, 0x03, 0x68, + 0x93, 0xF8, 0xB0, 0x20, + 0xD0, 0x07, 0x16, 0xD0, 0x90, 0x07, 0x14, 0xD5, 0xD8, 0x48, 0x40, 0x78, + 0x00, 0x28, 0x10, 0xD1, + 0xD3, 0x48, 0x34, 0x30, 0x00, 0x21, 0x04, 0x7B, 0x0C, 0xB1, 0x41, 0x73, + 0x08, 0xE0, 0x01, 0x24, + 0xC2, 0xF3, 0xC1, 0x02, 0x44, 0x73, 0x02, 0x2A, 0x00, 0xD1, 0x41, 0x73, + 0x93, 0xF8, 0xBB, 0x10, + 0x01, 0x73, 0x10, 0xBD, 0x2D, 0xE9, 0xF0, 0x47, 0xDF, 0xF8, 0x2C, 0x83, + 0x82, 0x46, 0x01, 0x27, + 0xD8, 0xF8, 0x00, 0x00, 0xCA, 0x4D, 0x90, 0xF8, 0x31, 0x40, 0x10, 0xF8, + 0x30, 0x1F, 0x90, 0xF8, + 0x31, 0x00, 0x4C, 0x43, 0x00, 0xF0, 0x03, 0x00, 0x87, 0x40, 0xA1, 0x00, + 0x28, 0x46, 0x15, 0xF0, + 0xBD, 0xFB, 0xFF, 0xF7, 0xB2, 0xFF, 0x00, 0x26, 0xDF, 0xF8, 0xF4, 0x92, + 0x15, 0xE0, 0xBC, 0x48, + 0x34, 0x30, 0xFF, 0xF7, 0x91, 0xFF, 0x00, 0x20, 0xD9, 0xF8, 0x08, 0x10, + 0x08, 0xE0, 0x00, 0xBF, + 0x55, 0xF8, 0x20, 0x20, 0x31, 0xF9, 0x10, 0x30, 0x1A, 0x44, 0x45, 0xF8, + 0x20, 0x20, 0x40, 0x1C, + 0xA0, 0x42, 0xF5, 0xDB, 0xFF, 0xF7, 0xAF, 0xFF, 0x76, 0x1C, 0xBE, 0x42, + 0xE7, 0xDB, 0x40, 0x46, + 0x0A, 0xE0, 0x00, 0xBF, 0x02, 0x68, 0x55, 0xF8, 0x24, 0x10, 0x92, 0xF8, + 0x61, 0x20, 0x02, 0xF0, + 0x03, 0x02, 0x11, 0x41, 0x2A, 0xF8, 0x14, 0x10, 0x64, 0x1E, 0xF3, 0xD2, + 0xBD, 0xE8, 0xF0, 0x87, + 0x2D, 0xE9, 0xF0, 0x4F, 0xDF, 0xF8, 0xA0, 0xB2, 0x4F, 0xF0, 0x01, 0x09, + 0xA8, 0x4E, 0xDB, 0xF8, + 0x00, 0x00, 0x9F, 0xB0, 0x90, 0xF8, 0x61, 0x10, 0x90, 0xF8, 0x31, 0x40, + 0xC1, 0xF3, 0x81, 0x08, + 0x90, 0xF8, 0x30, 0x50, 0x09, 0xFA, 0x08, 0xFA, 0x60, 0x19, 0x81, 0x00, + 0x06, 0xEB, 0x84, 0x07, + 0x30, 0x46, 0x15, 0xF0, 0x73, 0xFB, 0x9A, 0x48, 0x34, 0x30, 0x14, 0xF0, + 0x33, 0xFF, 0x98, 0x48, + 0x34, 0x30, 0x80, 0xF8, 0x06, 0x90, 0xDB, 0xF8, 0x00, 0x10, 0x91, 0xF8, + 0x60, 0x20, 0x92, 0x07, + 0x01, 0xD5, 0x80, 0xF8, 0x04, 0x90, 0x91, 0xF8, 0x62, 0x10, 0x01, 0x73, + 0xFF, 0xF7, 0x55, 0xFF, + 0xDB, 0xF8, 0x00, 0x00, 0x90, 0xF8, 0x60, 0x00, 0xC0, 0x07, 0x05, 0xD0, + 0x8C, 0x48, 0x34, 0x30, + 0xFF, 0xF7, 0x32, 0xFF, 0xFF, 0xF7, 0x5F, 0xFF, 0x00, 0x20, 0x2E, 0xE0, + 0x88, 0x48, 0x34, 0x30, + 0xFF, 0xF7, 0x2A, 0xFF, 0xDF, 0xF8, 0x18, 0xB2, 0x62, 0x00, 0x13, 0xA8, + 0xDB, 0xF8, 0x18, 0x10, + 0x15, 0xF0, 0xA3, 0xFA, 0x6A, 0x00, 0x68, 0x46, 0xDB, 0xF8, 0x1C, 0x10, + 0x15, 0xF0, 0x9D, 0xFA, + 0x20, 0x46, 0x13, 0xA9, 0x07, 0xE0, 0x00, 0xBF, 0x56, 0xF8, 0x20, 0x20, + 0x31, 0xF9, 0x10, 0x30, + 0x1A, 0x44, 0x46, 0xF8, 0x20, 0x20, 0x40, 0x1E, 0xF6, 0xD2, 0x28, 0x46, + 0x69, 0x46, 0x06, 0xE0, + 0x57, 0xF8, 0x20, 0x20, 0x31, 0xF9, 0x10, 0x30, 0x1A, 0x44, 0x47, 0xF8, + 0x20, 0x20, 0x40, 0x1E, + 0xF6, 0xD2, 0xFF, 0xF7, 0x30, 0xFF, 0x09, 0xF1, 0x01, 0x00, 0x81, 0x46, + 0x50, 0x45, 0xCD, 0xDB, + 0x74, 0x49, 0x05, 0xE0, 0x56, 0xF8, 0x24, 0x00, 0x40, 0xFA, 0x08, 0xF0, + 0x21, 0xF8, 0x14, 0x00, + 0x64, 0x1E, 0xF7, 0xD2, 0x70, 0x49, 0x05, 0xE0, 0x57, 0xF8, 0x25, 0x00, + 0x40, 0xFA, 0x08, 0xF0, + 0x21, 0xF8, 0x15, 0x00, 0x6D, 0x1E, 0xF7, 0xD2, 0x1F, 0xB0, 0xBD, 0xE8, + 0xF0, 0x8F, 0x2D, 0xE9, + 0xFF, 0x5F, 0xDF, 0xF8, 0x94, 0x81, 0x66, 0x4F, 0xD8, 0xF8, 0x00, 0x00, + 0x90, 0xF8, 0x31, 0x40, + 0x90, 0xF8, 0x30, 0x50, 0x07, 0xEB, 0x84, 0x0A, 0x60, 0x19, 0x81, 0x00, + 0x04, 0xFB, 0x05, 0xFB, + 0x0A, 0xEB, 0x85, 0x06, 0x38, 0x46, 0x15, 0xF0, 0xF1, 0xFA, 0x4F, 0xEA, + 0x8B, 0x01, 0x30, 0x46, + 0x15, 0xF0, 0xEC, 0xFA, 0x56, 0x48, 0x34, 0x30, 0x14, 0xF0, 0xAC, 0xFE, + 0x54, 0x48, 0x4F, 0xF0, + 0x01, 0x09, 0x34, 0x30, 0x80, 0xF8, 0x06, 0x90, 0x80, 0xF8, 0x04, 0x90, + 0xD8, 0xF8, 0x00, 0x10, + 0x91, 0xF8, 0x62, 0x10, 0x01, 0x73, 0xFF, 0xF7, 0xD0, 0xFE, 0xD8, 0xF8, + 0x00, 0x00, 0x90, 0xF8, + 0x60, 0x00, 0xC0, 0x07, 0x05, 0xD0, 0x4A, 0x48, 0x34, 0x30, 0xFF, 0xF7, + 0xAD, 0xFE, 0xFF, 0xF7, + 0xDA, 0xFE, 0xD8, 0xF8, 0x00, 0x00, 0x90, 0xF8, 0x61, 0x00, 0xC0, 0xF3, + 0x81, 0x01, 0x02, 0x91, + 0x00, 0xF0, 0x03, 0x01, 0x03, 0x91, 0x02, 0x99, 0x48, 0x46, 0x09, 0xFA, + 0x01, 0xF9, 0x03, 0x99, + 0x88, 0x40, 0x01, 0x90, 0x00, 0x20, 0x32, 0xE0, 0x3D, 0x48, 0x34, 0x30, + 0xFF, 0xF7, 0x94, 0xFE, + 0x3B, 0x48, 0xD0, 0xE9, 0x06, 0x32, 0x81, 0x68, 0x20, 0x46, 0x06, 0xE0, + 0x57, 0xF8, 0x20, 0xC0, + 0x33, 0xF9, 0x10, 0x80, 0xC4, 0x44, 0x47, 0xF8, 0x20, 0xC0, 0x40, 0x1E, + 0xF6, 0xD2, 0x28, 0x46, + 0x07, 0xE0, 0x00, 0xBF, 0x5A, 0xF8, 0x20, 0x30, 0x32, 0xF9, 0x10, 0xC0, + 0x9C, 0x44, 0x4A, 0xF8, + 0x20, 0xC0, 0x40, 0x1E, 0xF6, 0xD2, 0xDD, 0xE9, 0x00, 0x02, 0x90, 0x42, + 0x0B, 0xDA, 0x58, 0x46, + 0x07, 0xE0, 0x00, 0xBF, 0x56, 0xF8, 0x20, 0x20, 0x31, 0xF9, 0x10, 0x30, + 0x1A, 0x44, 0x46, 0xF8, + 0x20, 0x20, 0x40, 0x1E, 0xF6, 0xD2, 0xFF, 0xF7, 0x96, 0xFE, 0x00, 0x98, + 0x40, 0x1C, 0x00, 0x90, + 0x48, 0x45, 0xC9, 0xDB, 0x01, 0x98, 0x48, 0x45, 0x1F, 0xDD, 0x21, 0x48, + 0x00, 0x21, 0x34, 0x30, + 0x88, 0x46, 0x81, 0x71, 0x01, 0x99, 0xA1, 0xEB, 0x09, 0x09, 0x14, 0xE0, + 0x1C, 0x48, 0x34, 0x30, + 0xFF, 0xF7, 0x52, 0xFE, 0x1A, 0x48, 0x81, 0x68, 0x58, 0x46, 0x06, 0xE0, + 0x56, 0xF8, 0x20, 0x20, + 0x31, 0xF9, 0x10, 0x30, 0x1A, 0x44, 0x46, 0xF8, 0x20, 0x20, 0x40, 0x1E, + 0xF6, 0xD2, 0xFF, 0xF7, + 0x72, 0xFE, 0x08, 0xF1, 0x01, 0x08, 0xC1, 0x45, 0xE8, 0xDC, 0x16, 0x49, + 0x06, 0xE0, 0x00, 0xBF, + 0x57, 0xF8, 0x24, 0x00, 0x02, 0x9A, 0x10, 0x41, 0x21, 0xF8, 0x14, 0x00, + 0x64, 0x1E, 0xF7, 0xD2, + 0x11, 0x49, 0x05, 0xE0, 0x5A, 0xF8, 0x25, 0x00, 0x02, 0x9A, 0x10, 0x41, + 0x21, 0xF8, 0x15, 0x00, + 0x6D, 0x1E, 0xF7, 0xD2, 0x58, 0x46, 0x0D, 0x4A, 0x06, 0xE0, 0x00, 0xBF, + 0x56, 0xF8, 0x20, 0x10, + 0x03, 0x9B, 0x19, 0x41, 0x22, 0xF8, 0x10, 0x10, 0x40, 0x1E, 0xF7, 0xD2, + 0xBD, 0xE8, 0xFF, 0x9F, + 0xF4, 0x0E, 0x10, 0x00, 0xF1, 0x19, 0x00, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x74, 0x07, 0x10, 0x00, + 0xD4, 0x56, 0x10, 0x00, 0x7E, 0x4A, 0x01, 0x20, 0xA8, 0x4A, 0x01, 0x20, + 0x42, 0x44, 0x01, 0x20, + 0x2D, 0xE9, 0xF8, 0x43, 0x14, 0xF0, 0x53, 0xFE, 0x00, 0x90, 0x04, 0x21, + 0x68, 0x46, 0xFF, 0xF7, + 0xDA, 0xF9, 0xDF, 0xF8, 0xA0, 0x82, 0xC6, 0xB2, 0x01, 0x27, 0xD8, 0xF8, + 0x00, 0x00, 0xA6, 0x4C, + 0x90, 0xF8, 0x61, 0x00, 0x80, 0x09, 0x07, 0xFA, 0x00, 0xF5, 0xB1, 0x00, + 0x20, 0x46, 0x15, 0xF0, + 0x15, 0xFA, 0xA2, 0x48, 0x14, 0xF0, 0xD6, 0xFD, 0xA0, 0x48, 0xA0, 0xF1, + 0x34, 0x09, 0x87, 0x72, + 0x00, 0x27, 0x12, 0xE0, 0x9D, 0x48, 0xFF, 0xF7, 0xE7, 0xFD, 0x00, 0x20, + 0xD9, 0xF8, 0x2C, 0x10, + 0x08, 0xE0, 0x00, 0xBF, 0x54, 0xF8, 0x20, 0x20, 0x31, 0xF9, 0x10, 0x30, + 0x1A, 0x44, 0x44, 0xF8, + 0x20, 0x20, 0x40, 0x1C, 0xB0, 0x42, 0xF5, 0xDB, 0x7F, 0x1C, 0xAF, 0x42, + 0xEA, 0xDB, 0x00, 0x20, + 0x93, 0x4B, 0xD8, 0xF8, 0x00, 0x10, 0x08, 0xE0, 0x91, 0xF8, 0x61, 0x50, + 0x54, 0xF8, 0x20, 0x20, + 0xAD, 0x09, 0x2A, 0x41, 0x23, 0xF8, 0x10, 0x20, 0x40, 0x1C, 0xB0, 0x42, + 0xF4, 0xDB, 0xBD, 0xE8, + 0xF8, 0x83, 0x2D, 0xE9, 0xF0, 0x47, 0x05, 0x46, 0x00, 0x20, 0xFE, 0xF7, + 0x2A, 0xFC, 0x02, 0x20, + 0xFE, 0xF7, 0x27, 0xFC, 0x03, 0x20, 0xFE, 0xF7, 0x24, 0xFC, 0x86, 0x4E, + 0x00, 0x20, 0x30, 0x70, + 0x82, 0x48, 0x14, 0xF0, 0x97, 0xFD, 0x84, 0x48, 0x01, 0x24, 0x04, 0x70, + 0x7D, 0x4F, 0xDF, 0xF8, + 0x0C, 0x82, 0xDF, 0xF8, 0x0C, 0x92, 0x38, 0x68, 0x90, 0xF8, 0x60, 0x00, + 0x80, 0x07, 0x4F, 0xEA, + 0x05, 0x70, 0x0B, 0xD5, 0x00, 0x28, 0x11, 0xDA, 0xA8, 0x07, 0x09, 0xD5, + 0x34, 0x70, 0xFF, 0xF7, + 0xB6, 0xFE, 0x88, 0xF8, 0x00, 0x40, 0x89, 0xF8, 0x00, 0x40, 0x01, 0xE0, + 0x00, 0x28, 0x05, 0xDA, + 0x30, 0x78, 0x18, 0xB9, 0xFF, 0xF7, 0x24, 0xFE, 0x88, 0xF8, 0x00, 0x40, + 0x28, 0x06, 0x08, 0xD5, + 0x38, 0x68, 0x90, 0xF8, 0x2F, 0x0A, 0xC0, 0x07, 0x03, 0xD0, 0x72, 0x48, + 0x04, 0x70, 0xFF, 0xF7, + 0x77, 0xFF, 0xDF, 0xF8, 0xA8, 0x81, 0xA8, 0x07, 0x11, 0xD5, 0x30, 0x78, + 0xD0, 0xB9, 0x40, 0x46, + 0x14, 0xF0, 0x60, 0xFD, 0x88, 0xF8, 0x04, 0x40, 0x6B, 0x48, 0xFF, 0xF7, + 0xC3, 0xFD, 0x6A, 0x48, + 0x00, 0xF0, 0x23, 0xF8, 0x69, 0x49, 0x08, 0x70, 0x89, 0xF8, 0x00, 0x40, + 0x0A, 0xE0, 0x68, 0x07, + 0x08, 0xD5, 0x5E, 0x48, 0x14, 0xF0, 0x4E, 0xFD, 0x04, 0x21, 0x88, 0xF8, + 0x04, 0x10, 0x62, 0x48, + 0xFF, 0xF7, 0xB0, 0xFD, 0x38, 0x68, 0x90, 0xF8, 0x61, 0x04, 0x18, 0xB1, + 0xE8, 0x06, 0x01, 0xD5, + 0x00, 0xF0, 0x5E, 0xF8, 0x03, 0x20, 0xFE, 0xF7, 0xDF, 0xFB, 0x02, 0x20, + 0xFE, 0xF7, 0xDC, 0xFB, + 0xBD, 0xE8, 0xF0, 0x47, 0x00, 0x20, 0xFE, 0xF7, 0xD7, 0xBB, 0x2D, 0xE9, + 0xF0, 0x43, 0x4D, 0x49, + 0x4F, 0xF0, 0x00, 0x09, 0xCC, 0x46, 0x0F, 0x68, 0x4B, 0x46, 0x97, 0xF8, + 0x31, 0x20, 0x97, 0xF8, + 0x30, 0x60, 0x97, 0xF8, 0x60, 0x40, 0x02, 0xFB, 0x06, 0xF1, 0x09, 0xB2, + 0x64, 0x07, 0x37, 0xD5, + 0x4F, 0x4C, 0x24, 0x68, 0x25, 0x8E, 0x8D, 0x42, 0x32, 0xD1, 0x32, 0x34, + 0x28, 0xE0, 0x35, 0x46, + 0x20, 0xE0, 0x00, 0xBF, 0x34, 0xF8, 0x15, 0x10, 0x30, 0xF8, 0x15, 0x80, + 0xA1, 0xEB, 0x08, 0x01, + 0x09, 0xB2, 0x00, 0x29, 0x01, 0xDB, 0x88, 0x46, 0x01, 0xE0, 0xC1, 0xF1, + 0x00, 0x08, 0xE0, 0x45, + 0x07, 0xDD, 0x00, 0x29, 0x01, 0xDB, 0x8C, 0x46, 0x01, 0xE0, 0xC1, 0xF1, + 0x00, 0x0C, 0x0F, 0xFA, + 0x8C, 0xFC, 0x00, 0x29, 0x00, 0xDA, 0x49, 0x42, 0xB7, 0xF8, 0x63, 0x80, + 0x41, 0x45, 0x01, 0xDD, + 0x5B, 0x1C, 0x9B, 0xB2, 0x6D, 0x1E, 0xDD, 0xD1, 0x04, 0xEB, 0x46, 0x04, + 0x00, 0xEB, 0x46, 0x00, + 0x52, 0x1E, 0xD4, 0xD2, 0x97, 0xF8, 0x65, 0x00, 0x98, 0x42, 0x01, 0xD2, + 0x4F, 0xF0, 0x01, 0x09, + 0x34, 0x48, 0xA0, 0xF8, 0x00, 0xC0, 0x34, 0x48, 0x03, 0x80, 0x48, 0x46, + 0xBD, 0xE8, 0xF0, 0x83, + 0x2D, 0xE9, 0xF0, 0x47, 0xDF, 0xF8, 0x8C, 0x80, 0x01, 0x21, 0x45, 0x46, + 0xD8, 0xF8, 0x00, 0x00, + 0x00, 0xF2, 0x61, 0x40, 0xFF, 0xF7, 0xC7, 0xF8, 0x04, 0x46, 0x28, 0x68, + 0x01, 0x27, 0x1E, 0x4D, + 0x90, 0xF8, 0x61, 0x00, 0xC0, 0xF3, 0x01, 0x10, 0x07, 0xFA, 0x00, 0xF6, + 0xA1, 0x00, 0x28, 0x46, + 0x15, 0xF0, 0x04, 0xF9, 0x19, 0x48, 0x14, 0xF0, 0xC5, 0xFC, 0x18, 0x48, + 0xA0, 0xF1, 0x34, 0x09, + 0x47, 0x72, 0x00, 0x27, 0x11, 0xE0, 0x15, 0x48, 0xFF, 0xF7, 0xD6, 0xFC, + 0x00, 0x20, 0xD9, 0xF8, + 0x14, 0x10, 0x07, 0xE0, 0x55, 0xF8, 0x20, 0x20, 0x31, 0xF9, 0x10, 0x30, + 0x1A, 0x44, 0x45, 0xF8, + 0x20, 0x20, 0x40, 0x1C, 0xA0, 0x42, 0xF5, 0xDB, 0x7F, 0x1C, 0xB7, 0x42, + 0xEB, 0xDB, 0x00, 0x20, + 0x16, 0x4B, 0xD8, 0xF8, 0x00, 0x10, 0x09, 0xE0, 0x91, 0xF8, 0x61, 0x60, + 0x55, 0xF8, 0x20, 0x20, + 0xC6, 0xF3, 0x01, 0x16, 0x32, 0x41, 0x23, 0xF8, 0x10, 0x20, 0x40, 0x1C, + 0xA0, 0x42, 0xF3, 0xDB, + 0x44, 0xE5, 0x00, 0x00, 0x4C, 0x07, 0x10, 0x00, 0xD4, 0x56, 0x10, 0x00, + 0x28, 0x0F, 0x10, 0x00, + 0xFC, 0x4A, 0x01, 0x20, 0x68, 0x04, 0x10, 0x00, 0x78, 0x07, 0x10, 0x00, + 0x7A, 0x07, 0x10, 0x00, + 0x79, 0x07, 0x10, 0x00, 0x7C, 0x07, 0x10, 0x00, 0x42, 0x44, 0x01, 0x20, + 0x7D, 0x07, 0x10, 0x00, + 0x50, 0x07, 0x10, 0x00, 0x7E, 0x07, 0x10, 0x00, 0x80, 0x07, 0x10, 0x00, + 0xF4, 0x4A, 0x01, 0x20, + 0x10, 0xB5, 0x01, 0x46, 0x34, 0x22, 0xFE, 0x48, 0x15, 0xF0, 0x54, 0xF8, + 0x01, 0x21, 0xBD, 0xE8, + 0x10, 0x40, 0x00, 0x20, 0xFE, 0xF7, 0x15, 0xBA, 0x38, 0xB5, 0x04, 0x46, + 0xF9, 0x48, 0xF8, 0x4D, + 0x20, 0x60, 0x20, 0x46, 0x14, 0xF0, 0x8B, 0xFC, 0x00, 0x22, 0x6B, 0x46, + 0x01, 0x21, 0x10, 0x46, + 0xFE, 0xF7, 0x50, 0xFA, 0x01, 0x21, 0x00, 0x20, 0xFE, 0xF7, 0x2F, 0xFA, + 0x28, 0x78, 0x00, 0x28, + 0x02, 0xD0, 0x10, 0xF0, 0x12, 0xFA, 0xEC, 0xE7, 0x38, 0xBD, 0x2D, 0xE9, + 0xFF, 0x41, 0x00, 0x24, + 0xDD, 0xE9, 0x0A, 0x76, 0x04, 0x21, 0x68, 0x46, 0xFF, 0xF7, 0x3D, 0xF8, + 0x05, 0x46, 0x08, 0x21, + 0x02, 0xA8, 0xFF, 0xF7, 0x38, 0xF8, 0x02, 0x46, 0x00, 0x20, 0x6F, 0xF0, + 0x7E, 0x0E, 0x14, 0xE0, + 0x00, 0x21, 0x00, 0xFB, 0x02, 0xF3, 0x0D, 0xE0, 0x07, 0xEB, 0x01, 0x0C, + 0x13, 0xF9, 0x0C, 0xC0, + 0xB4, 0x44, 0xF4, 0x45, 0x01, 0xDA, 0x01, 0x24, 0x03, 0xE0, 0xBC, 0xF1, + 0x7F, 0x0F, 0x00, 0xDD, + 0x02, 0x24, 0x49, 0x1C, 0x91, 0x42, 0xEF, 0xDB, 0x40, 0x1C, 0xA8, 0x42, + 0xE8, 0xDB, 0x20, 0x46, + 0x04, 0xB0, 0xBD, 0xE8, 0xF0, 0x81, 0x2D, 0xE9, 0xFF, 0x41, 0x0A, 0xA9, + 0x00, 0x24, 0x91, 0xE8, + 0xC0, 0x01, 0x04, 0x21, 0x68, 0x46, 0xFF, 0xF7, 0x0E, 0xF8, 0x05, 0x46, + 0x08, 0x21, 0x02, 0xA8, + 0xFF, 0xF7, 0x09, 0xF8, 0x01, 0x46, 0x00, 0x20, 0x08, 0xE0, 0x32, 0x56, + 0xD2, 0x19, 0x01, 0xD5, + 0x01, 0x24, 0x02, 0xE0, 0xFF, 0x2A, 0x00, 0xDD, 0x02, 0x24, 0x40, 0x1C, + 0xA8, 0x42, 0xF4, 0xDB, + 0x00, 0x20, 0x0A, 0xE0, 0x2A, 0x18, 0xB2, 0x56, 0x12, 0xEB, 0x08, 0x02, + 0x01, 0xD5, 0x01, 0x24, + 0x02, 0xE0, 0xFF, 0x2A, 0x00, 0xDD, 0x02, 0x24, 0x40, 0x1C, 0x88, 0x42, + 0xF2, 0xDB, 0x20, 0x46, + 0xCE, 0xE7, 0x2D, 0xE9, 0xF0, 0x4F, 0x8D, 0xB0, 0x14, 0xF0, 0x51, 0xFC, + 0x0C, 0x90, 0x14, 0xF0, + 0x50, 0xFC, 0xBD, 0x4E, 0x04, 0x46, 0x0D, 0x46, 0x30, 0x68, 0x90, 0xF8, + 0xE7, 0x00, 0x10, 0xF0, + 0x60, 0x0F, 0x03, 0xD0, 0x0C, 0x98, 0x11, 0xF0, 0x67, 0xF9, 0x0C, 0x90, + 0xDF, 0xF8, 0xD8, 0xA2, + 0x01, 0x21, 0xDA, 0xF8, 0x00, 0x00, 0x90, 0xF8, 0x31, 0x60, 0x90, 0xF8, + 0x30, 0x80, 0x06, 0xFB, + 0x08, 0xF0, 0x0B, 0x90, 0x06, 0xA8, 0x0E, 0xF0, 0x42, 0xFC, 0xB0, 0x4F, + 0x4F, 0xF0, 0x00, 0x0B, + 0xDF, 0xF8, 0xBC, 0x92, 0x39, 0x68, 0x06, 0x9B, 0x22, 0x46, 0x91, 0xF9, + 0x1B, 0x10, 0xCD, 0xF8, + 0x0C, 0xB0, 0xCD, 0xF8, 0x00, 0x90, 0xCD, 0xE9, 0x01, 0x31, 0x2B, 0x46, + 0x0C, 0x98, 0x11, 0xF0, + 0xC5, 0xF9, 0x06, 0x9B, 0x01, 0x20, 0xCD, 0xE9, 0x00, 0x93, 0xCD, 0xE9, + 0x02, 0xB0, 0x22, 0x46, + 0x2B, 0x46, 0x0C, 0x98, 0x11, 0xF0, 0xBA, 0xF9, 0x9D, 0x48, 0x34, 0x30, + 0x14, 0xF0, 0xB2, 0xFB, + 0x9B, 0x49, 0x05, 0x20, 0x34, 0x31, 0x08, 0x71, 0x08, 0x46, 0xFF, 0xF7, + 0x3D, 0xFF, 0x98, 0x48, + 0x02, 0x21, 0x80, 0x68, 0x00, 0x90, 0x0A, 0xA8, 0x0E, 0xF0, 0x11, 0xFC, + 0xDA, 0xF8, 0x00, 0x00, + 0xB0, 0xF9, 0xC2, 0xA1, 0xB0, 0xF9, 0xC4, 0x91, 0x0A, 0xEB, 0x09, 0x01, + 0x49, 0x10, 0x02, 0x91, + 0x90, 0xF8, 0xE8, 0x00, 0x80, 0x07, 0x09, 0xD4, 0xA8, 0xF1, 0x01, 0x00, + 0x70, 0x43, 0x00, 0x99, + 0x0B, 0x90, 0x43, 0x46, 0x32, 0x46, 0x08, 0x46, 0x00, 0xF0, 0x76, 0xFB, + 0xDD, 0xE9, 0x0A, 0x12, + 0x00, 0x98, 0x11, 0xF0, 0x8C, 0xFF, 0x80, 0x46, 0x02, 0x21, 0x0A, 0xA8, + 0x0E, 0xF0, 0x82, 0xFC, + 0x00, 0x26, 0xD0, 0x45, 0x01, 0xDC, 0xC8, 0x45, 0x09, 0xDA, 0x83, 0x48, + 0x02, 0x99, 0x00, 0x68, + 0xA8, 0xEB, 0x01, 0x01, 0xB0, 0xF8, 0xC6, 0x01, 0x91, 0xFB, 0xF0, 0xF0, + 0x46, 0xB2, 0x06, 0x98, + 0xCD, 0xE9, 0x00, 0x06, 0x22, 0x46, 0x2B, 0x46, 0x0C, 0x98, 0xFF, 0xF7, + 0x16, 0xFF, 0x01, 0x28, + 0x18, 0xD0, 0x02, 0x28, 0x1C, 0xD0, 0x38, 0x68, 0x79, 0x49, 0x2B, 0x46, + 0xC6, 0x76, 0x01, 0x20, + 0x06, 0x9A, 0x03, 0x90, 0x8D, 0xE8, 0x46, 0x00, 0x22, 0x46, 0x0C, 0x98, + 0x11, 0xF0, 0x5E, 0xF9, + 0x39, 0x68, 0xA5, 0x20, 0x88, 0x76, 0x01, 0x21, 0x06, 0xA8, 0x0E, 0xF0, + 0x53, 0xFC, 0x0D, 0xB0, + 0xBD, 0xE8, 0xF0, 0x8F, 0x39, 0x68, 0x81, 0xF8, 0x1B, 0xB0, 0x01, 0x21, + 0xB0, 0x20, 0x04, 0xE0, + 0x39, 0x68, 0xB1, 0x20, 0x81, 0xF8, 0x1B, 0xB0, 0x01, 0x21, 0x10, 0xF0, + 0xEF, 0xF8, 0xE7, 0xE7, + 0x2D, 0xE9, 0xF0, 0x4F, 0x64, 0x4F, 0x8D, 0xB0, 0x38, 0x68, 0x90, 0xF8, + 0x31, 0x60, 0x90, 0xF8, + 0x30, 0x80, 0x14, 0xF0, 0x94, 0xFB, 0x0B, 0x90, 0x14, 0xF0, 0x93, 0xFB, + 0x04, 0x46, 0x38, 0x68, + 0x0D, 0x46, 0x90, 0xF8, 0xE7, 0x00, 0x10, 0xF0, 0x60, 0x0F, 0x03, 0xD0, + 0x0B, 0x98, 0x11, 0xF0, + 0xAB, 0xF8, 0x0B, 0x90, 0x01, 0x21, 0x04, 0xA8, 0x0E, 0xF0, 0x91, 0xFB, + 0x57, 0x4F, 0x4F, 0xF0, + 0x00, 0x0A, 0xDF, 0xF8, 0x60, 0x91, 0x38, 0x68, 0x04, 0x9B, 0x90, 0xF9, + 0x1D, 0x20, 0xCD, 0xF8, + 0x0C, 0xA0, 0xCD, 0xF8, 0x00, 0x90, 0xCD, 0xE9, 0x01, 0x32, 0x22, 0x46, + 0x2B, 0x46, 0x0B, 0x98, + 0x11, 0xF0, 0x14, 0xF9, 0x04, 0x9B, 0x01, 0x21, 0xCD, 0xE9, 0x00, 0x93, + 0xCD, 0xE9, 0x02, 0xA1, + 0x22, 0x46, 0x2B, 0x46, 0x0B, 0x98, 0x11, 0xF0, 0x09, 0xF9, 0x45, 0x48, + 0x34, 0x30, 0x14, 0xF0, + 0x01, 0xFB, 0x43, 0x49, 0x06, 0x20, 0x34, 0x31, 0x08, 0x71, 0x08, 0x46, + 0xFF, 0xF7, 0x8C, 0xFE, + 0x3F, 0x48, 0x02, 0x21, 0x80, 0x68, 0x00, 0x90, 0x0A, 0xA8, 0x0E, 0xF0, + 0x60, 0xFB, 0x3E, 0x48, + 0x06, 0xFB, 0x08, 0xFB, 0x00, 0x68, 0xB0, 0xF9, 0xC8, 0x11, 0xB0, 0xF9, + 0xCA, 0x21, 0x11, 0x44, + 0x49, 0x10, 0x01, 0x91, 0x90, 0xF8, 0xE8, 0x00, 0x80, 0x07, 0x09, 0xD4, + 0xA8, 0xF1, 0x01, 0x00, + 0x00, 0x99, 0x06, 0xFB, 0x00, 0xFB, 0x43, 0x46, 0x32, 0x46, 0x08, 0x46, + 0x00, 0xF0, 0xC4, 0xFA, + 0x5A, 0x46, 0x0A, 0x99, 0x00, 0x98, 0x11, 0xF0, 0xDA, 0xFE, 0x80, 0x46, + 0x02, 0x21, 0x0A, 0xA8, + 0x0E, 0xF0, 0xD0, 0xFB, 0x2C, 0x48, 0x00, 0x26, 0x00, 0x68, 0xB0, 0xF9, + 0xC8, 0x21, 0x42, 0x45, + 0x03, 0xDB, 0xB0, 0xF9, 0xCA, 0x21, 0x42, 0x45, 0x07, 0xDD, 0x01, 0x9A, + 0xB0, 0xF8, 0xCC, 0x01, + 0xA8, 0xEB, 0x02, 0x01, 0x91, 0xFB, 0xF0, 0xF0, 0x46, 0xB2, 0x04, 0x98, + 0xCD, 0xE9, 0x00, 0x06, + 0x22, 0x46, 0x2B, 0x46, 0x0B, 0x98, 0xFF, 0xF7, 0x60, 0xFE, 0x4F, 0xF4, + 0x80, 0x71, 0x01, 0x28, + 0x17, 0xD0, 0x02, 0x28, 0x1A, 0xD0, 0x38, 0x68, 0x2B, 0x46, 0x46, 0x77, + 0x01, 0x20, 0x04, 0x9A, + 0xCD, 0xF8, 0x00, 0x90, 0x03, 0x90, 0xCD, 0xE9, 0x01, 0x26, 0x22, 0x46, + 0x0B, 0x98, 0x11, 0xF0, + 0xA5, 0xF8, 0x39, 0x68, 0xA5, 0x20, 0x08, 0x77, 0x01, 0x21, 0x04, 0xA8, + 0x0E, 0xF0, 0x9A, 0xFB, + 0x45, 0xE7, 0x3A, 0x68, 0xB0, 0x20, 0x82, 0xF8, 0x1D, 0xA0, 0x03, 0xE0, + 0x3A, 0x68, 0xB1, 0x20, + 0x82, 0xF8, 0x1D, 0xA0, 0x10, 0xF0, 0x3A, 0xF8, 0xEB, 0xE7, 0x2D, 0xE9, + 0xF0, 0x4F, 0x87, 0xB0, + 0x14, 0xF0, 0xE9, 0xFA, 0x05, 0x90, 0x14, 0xF0, 0xE8, 0xFA, 0x04, 0x46, + 0x0D, 0x46, 0x01, 0x21, + 0x04, 0xA8, 0x0E, 0xF0, 0xEC, 0xFA, 0x4F, 0xF0, 0x00, 0x0A, 0x56, 0x46, + 0x03, 0x4F, 0x0B, 0xE0, + 0x38, 0x0F, 0x10, 0x00, 0xE1, 0x20, 0x00, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x50, 0x07, 0x10, 0x00, + 0x78, 0x8B, 0x01, 0x20, 0x08, 0x92, 0x01, 0x20, 0x38, 0x68, 0xDF, 0xF8, + 0x2C, 0x94, 0x04, 0x9A, + 0x90, 0xF9, 0x25, 0x10, 0xCD, 0xF8, 0x0C, 0xA0, 0xCD, 0xF8, 0x00, 0x90, + 0xCD, 0xE9, 0x01, 0x21, + 0x22, 0x46, 0x2B, 0x46, 0x05, 0x98, 0x11, 0xF0, 0x61, 0xF8, 0x01, 0x21, + 0x04, 0x9A, 0xCD, 0xF8, + 0x00, 0x90, 0x03, 0x91, 0xCD, 0xE9, 0x01, 0x26, 0x22, 0x46, 0x2B, 0x46, + 0x05, 0x98, 0x11, 0xF0, + 0x55, 0xF8, 0xFE, 0x48, 0x14, 0xF0, 0x4E, 0xFA, 0xFC, 0x48, 0x02, 0x21, + 0x41, 0x72, 0xFF, 0xF7, + 0xDB, 0xFD, 0xFA, 0x48, 0x02, 0x21, 0x34, 0x38, 0x46, 0x69, 0x06, 0xA8, + 0x0E, 0xF0, 0xAF, 0xFA, + 0xDF, 0xF8, 0xDC, 0xB3, 0x01, 0x21, 0xDB, 0xF8, 0x00, 0x00, 0x00, 0xF2, + 0x61, 0x40, 0xFE, 0xF7, + 0x2A, 0xFE, 0x02, 0x46, 0x30, 0x46, 0x06, 0x99, 0x11, 0xF0, 0x39, 0xFE, + 0x80, 0x46, 0x02, 0x21, + 0x06, 0xA8, 0x0E, 0xF0, 0x2F, 0xFB, 0xDB, 0xF8, 0x00, 0x10, 0x00, 0x26, + 0xB1, 0xF9, 0xDC, 0x21, + 0xB1, 0xF9, 0xDE, 0x31, 0xD0, 0x18, 0x4F, 0xEA, 0x60, 0x0C, 0x90, 0x45, + 0x01, 0xDC, 0x98, 0x45, + 0x06, 0xDA, 0xB1, 0xF8, 0xE0, 0x11, 0xA8, 0xEB, 0x0C, 0x00, 0x90, 0xFB, + 0xF1, 0xF0, 0x46, 0xB2, + 0x04, 0x98, 0xCD, 0xE9, 0x00, 0x06, 0x22, 0x46, 0x2B, 0x46, 0x05, 0x98, + 0xFF, 0xF7, 0xBD, 0xFD, + 0x01, 0x28, 0x1A, 0xD0, 0x02, 0x28, 0x1E, 0xD0, 0x38, 0x68, 0x2B, 0x46, + 0x80, 0xF8, 0x25, 0x60, + 0x01, 0x20, 0x04, 0x9A, 0xCD, 0xF8, 0x00, 0x90, 0x03, 0x90, 0xCD, 0xE9, + 0x01, 0x26, 0x22, 0x46, + 0x05, 0x98, 0x11, 0xF0, 0x03, 0xF8, 0x39, 0x68, 0xA5, 0x20, 0x81, 0xF8, + 0x24, 0x00, 0x01, 0x21, + 0x04, 0xA8, 0x0E, 0xF0, 0xF7, 0xFA, 0x07, 0xB0, 0xA2, 0xE6, 0x39, 0x68, + 0x81, 0xF8, 0x25, 0xA0, + 0x02, 0x21, 0xB0, 0x20, 0x04, 0xE0, 0x39, 0x68, 0xB1, 0x20, 0x81, 0xF8, + 0x25, 0xA0, 0x02, 0x21, + 0x0F, 0xF0, 0x94, 0xFF, 0xE7, 0xE7, 0x2D, 0xE9, 0xF0, 0x4F, 0xC9, 0x48, + 0x89, 0xB0, 0x00, 0x68, + 0x90, 0xF8, 0x31, 0x60, 0x90, 0xF8, 0x30, 0xB0, 0x14, 0xF0, 0x39, 0xFA, + 0x07, 0x90, 0x14, 0xF0, + 0x38, 0xFA, 0x04, 0x46, 0x0D, 0x46, 0x01, 0x21, 0x06, 0xA8, 0x0E, 0xF0, + 0x40, 0xFA, 0xDF, 0xF8, + 0x04, 0xA3, 0x02, 0xAB, 0x00, 0x27, 0xDA, 0xF8, 0x00, 0x00, 0x06, 0x9A, + 0x90, 0xF9, 0x20, 0x10, + 0x90, 0xF9, 0x1F, 0x00, 0x83, 0xE8, 0x83, 0x00, 0xB7, 0x48, 0x2B, 0x46, + 0xA0, 0x30, 0xCD, 0xE9, + 0x00, 0x02, 0x08, 0x90, 0x22, 0x46, 0x07, 0x98, 0x11, 0xF0, 0x0C, 0xF8, + 0x06, 0x99, 0xCD, 0xE9, + 0x01, 0x17, 0x01, 0x20, 0xCD, 0xE9, 0x03, 0x70, 0x08, 0x98, 0x00, 0x90, + 0x22, 0x46, 0x2B, 0x46, + 0x07, 0x98, 0x10, 0xF0, 0xFF, 0xFF, 0xAD, 0x48, 0x14, 0xF0, 0xAC, 0xF9, + 0xAB, 0x48, 0x03, 0x21, + 0x81, 0x71, 0xFF, 0xF7, 0x39, 0xFD, 0xA9, 0x48, 0x02, 0x21, 0x34, 0x38, + 0xD0, 0xE9, 0x06, 0x70, + 0x00, 0x90, 0xA7, 0x48, 0x00, 0x68, 0xB0, 0xF9, 0xCE, 0x91, 0xB0, 0xF9, + 0xD0, 0x81, 0x05, 0xA8, + 0x0E, 0xF0, 0x05, 0xFA, 0x09, 0xEB, 0x08, 0x00, 0x40, 0x10, 0x01, 0x90, + 0xA0, 0x48, 0x00, 0x68, + 0x90, 0xF8, 0x34, 0x01, 0x80, 0x07, 0x05, 0xD4, 0x39, 0x46, 0x32, 0x46, + 0x08, 0x46, 0x00, 0xF0, + 0x87, 0xF9, 0x76, 0x1E, 0x32, 0x46, 0x38, 0x46, 0x05, 0x99, 0x11, 0xF0, + 0x88, 0xFD, 0x00, 0x27, + 0x48, 0x45, 0x01, 0xDC, 0x40, 0x45, 0x08, 0xDA, 0x01, 0x99, 0x09, 0x1A, + 0x94, 0x48, 0x00, 0x68, + 0xB0, 0xF8, 0xD2, 0x01, 0x91, 0xFB, 0xF0, 0xF0, 0x47, 0xB2, 0x91, 0x48, + 0x00, 0x68, 0x90, 0xF8, + 0x34, 0x01, 0x80, 0x07, 0x06, 0xD4, 0x00, 0x99, 0x5A, 0x46, 0x08, 0x46, + 0x00, 0xF0, 0x68, 0xF9, + 0xAB, 0xF1, 0x01, 0x0B, 0x5A, 0x46, 0x05, 0x99, 0x00, 0x98, 0x11, 0xF0, + 0x68, 0xFD, 0x00, 0x26, + 0x48, 0x45, 0x01, 0xDC, 0x40, 0x45, 0x08, 0xDA, 0x01, 0x99, 0x09, 0x1A, + 0x84, 0x48, 0x00, 0x68, + 0xB0, 0xF8, 0xD4, 0x01, 0x91, 0xFB, 0xF0, 0xF0, 0x46, 0xB2, 0x02, 0x21, + 0x05, 0xA8, 0x0E, 0xF0, + 0x51, 0xFA, 0x06, 0x98, 0xCD, 0xE9, 0x00, 0x07, 0x02, 0x96, 0x22, 0x46, + 0x2B, 0x46, 0x07, 0x98, + 0xFF, 0xF7, 0x21, 0xFD, 0x01, 0x28, 0x1D, 0xD0, 0x02, 0x28, 0x24, 0xD0, + 0xDA, 0xF8, 0x00, 0x00, + 0x22, 0x46, 0x2B, 0x46, 0xC7, 0x77, 0x80, 0xF8, 0x20, 0x60, 0x01, 0x20, + 0x06, 0x99, 0xCD, 0xE9, + 0x03, 0x60, 0xCD, 0xE9, 0x01, 0x17, 0x08, 0x98, 0x00, 0x90, 0x07, 0x98, + 0x10, 0xF0, 0x82, 0xFF, + 0xDA, 0xF8, 0x00, 0x00, 0xA5, 0x21, 0x81, 0x77, 0x01, 0x21, 0x06, 0xA8, + 0x0E, 0xF0, 0x2A, 0xFA, + 0x09, 0xB0, 0xD5, 0xE5, 0xDA, 0xF8, 0x00, 0x10, 0x00, 0x20, 0xC8, 0x77, + 0x81, 0xF8, 0x20, 0x00, + 0x10, 0x21, 0xB2, 0x20, 0x07, 0xE0, 0xDA, 0xF8, 0x00, 0x10, 0x00, 0x20, + 0xC8, 0x77, 0x81, 0xF8, + 0x20, 0x00, 0x10, 0x21, 0xB3, 0x20, 0x0F, 0xF0, 0xC1, 0xFE, 0xE1, 0xE7, + 0x2D, 0xE9, 0xF0, 0x4F, + 0x87, 0xB0, 0x14, 0xF0, 0x6C, 0xF9, 0x83, 0x46, 0x14, 0xF0, 0x6B, 0xF9, + 0xDF, 0xF8, 0x70, 0x91, + 0x01, 0x21, 0xC8, 0x46, 0xD9, 0xF8, 0x00, 0x00, 0x90, 0xF8, 0x31, 0x60, + 0x05, 0xA8, 0x0E, 0xF0, + 0x6E, 0xF9, 0x58, 0x4D, 0x58, 0x4F, 0x05, 0x9A, 0x28, 0x68, 0x00, 0x24, + 0x23, 0x46, 0x90, 0xF9, + 0x23, 0x10, 0x90, 0xF9, 0x22, 0x00, 0x00, 0x97, 0xCD, 0xE9, 0x01, 0x20, + 0xCD, 0xE9, 0x03, 0x14, + 0x22, 0x46, 0x58, 0x46, 0x10, 0xF0, 0x3E, 0xFF, 0x05, 0x99, 0xCD, 0xE9, + 0x01, 0x14, 0x4F, 0xF0, + 0x01, 0x0A, 0x00, 0x97, 0xCD, 0xE9, 0x03, 0x4A, 0x00, 0x22, 0x13, 0x46, + 0x58, 0x46, 0x10, 0xF0, + 0x31, 0xFF, 0x46, 0x48, 0x14, 0xF0, 0xDE, 0xF8, 0x44, 0x49, 0x03, 0x20, + 0x08, 0x72, 0x08, 0x46, + 0xFF, 0xF7, 0x6A, 0xFC, 0x41, 0x48, 0x02, 0x21, 0x34, 0x38, 0x87, 0x6A, + 0x06, 0xA8, 0x0E, 0xF0, + 0x3E, 0xF9, 0xD8, 0xF8, 0x00, 0x00, 0xB0, 0xF9, 0xD6, 0x11, 0xB0, 0xF9, + 0xD8, 0x21, 0x90, 0xF8, + 0x84, 0x09, 0x11, 0x44, 0x01, 0xEB, 0xD1, 0x71, 0x4F, 0xEA, 0x61, 0x08, + 0x80, 0x07, 0x05, 0xD4, + 0x76, 0x1E, 0x39, 0x46, 0x32, 0x46, 0x08, 0x46, 0x00, 0xF0, 0xBA, 0xF8, + 0x32, 0x46, 0x38, 0x46, + 0x06, 0x99, 0x11, 0xF0, 0xBC, 0xFC, 0xD9, 0xF8, 0x00, 0x10, 0x00, 0x26, + 0x37, 0x46, 0xB1, 0xF9, + 0xD6, 0x21, 0x82, 0x42, 0x03, 0xDB, 0xB1, 0xF9, 0xD8, 0x21, 0x82, 0x42, + 0x06, 0xDD, 0xB1, 0xF8, + 0xDA, 0x11, 0xA8, 0xEB, 0x00, 0x00, 0x90, 0xFB, 0xF1, 0xF0, 0x46, 0xB2, + 0x02, 0x21, 0x06, 0xA8, + 0x0E, 0xF0, 0xA0, 0xF9, 0x05, 0x98, 0x00, 0x22, 0x8D, 0xE8, 0xC1, 0x00, + 0x13, 0x46, 0x58, 0x46, + 0xFF, 0xF7, 0x71, 0xFC, 0x01, 0x28, 0x1A, 0xD0, 0x02, 0x28, 0x28, 0x68, + 0x1E, 0xD0, 0x00, 0xF8, + 0x22, 0x6F, 0x00, 0x22, 0x44, 0x70, 0x20, 0x48, 0x05, 0x99, 0x98, 0x38, + 0x8D, 0xE8, 0x43, 0x00, + 0xCD, 0xE9, 0x03, 0x4A, 0x13, 0x46, 0x58, 0x46, 0x10, 0xF0, 0xD4, 0xFE, + 0x29, 0x68, 0xA5, 0x20, + 0x81, 0xF8, 0x21, 0x00, 0x01, 0x21, 0x05, 0xA8, 0x0E, 0xF0, 0x7C, 0xF9, + 0x83, 0xE6, 0x28, 0x68, + 0x40, 0x21, 0x00, 0xF8, 0x22, 0x4F, 0x44, 0x70, 0xB2, 0x20, 0x04, 0xE0, + 0x00, 0xF8, 0x22, 0x4F, + 0x40, 0x21, 0x44, 0x70, 0xB3, 0x20, 0x0F, 0xF0, 0x19, 0xFE, 0xE7, 0xE7, + 0x70, 0xB5, 0x04, 0x46, + 0x00, 0x20, 0xFD, 0xF7, 0xEE, 0xFE, 0x02, 0x20, 0xFD, 0xF7, 0xEB, 0xFE, + 0x0B, 0x49, 0x01, 0x20, + 0x08, 0x70, 0x07, 0x4D, 0x28, 0x68, 0x90, 0xF8, 0xC0, 0x01, 0x20, 0x40, + 0xC0, 0x07, 0x01, 0xD0, + 0xFF, 0xF7, 0x5F, 0xFC, 0x28, 0x68, 0x0B, 0xE0, 0x98, 0x98, 0x01, 0x20, + 0x6C, 0x0F, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, 0x50, 0x07, 0x10, 0x00, 0xD0, 0x99, 0x01, 0x20, + 0x78, 0x07, 0x10, 0x00, + 0x90, 0xF8, 0xC0, 0x01, 0x80, 0x07, 0x03, 0xD5, 0xA0, 0x07, 0x01, 0xD5, + 0xFF, 0xF7, 0x00, 0xFD, + 0x28, 0x68, 0x90, 0xF8, 0xC0, 0x11, 0xC9, 0x06, 0x06, 0xD5, 0x90, 0xF8, + 0x61, 0x04, 0x18, 0xB1, + 0xE0, 0x06, 0x01, 0xD5, 0xFF, 0xF7, 0xA9, 0xFD, 0x28, 0x68, 0x90, 0xF8, + 0xC0, 0x01, 0x40, 0x07, + 0x03, 0xD5, 0x60, 0x07, 0x01, 0xD5, 0xFF, 0xF7, 0x46, 0xFE, 0x28, 0x68, + 0x90, 0xF8, 0xC0, 0x01, + 0x00, 0x07, 0x03, 0xD5, 0x20, 0x07, 0x01, 0xD5, 0xFF, 0xF7, 0x10, 0xFF, + 0x00, 0x20, 0xFD, 0xF7, + 0xC3, 0xFE, 0x02, 0x20, 0xFD, 0xF7, 0xC0, 0xFE, 0xE0, 0x07, 0x04, 0xD0, + 0xBD, 0xE8, 0x70, 0x40, + 0x01, 0x20, 0x01, 0xF0, 0x75, 0xBA, 0x70, 0xBD, 0xF0, 0xB5, 0x00, 0x26, + 0x35, 0x46, 0x0C, 0xE0, + 0x01, 0x24, 0x07, 0xE0, 0x05, 0xFB, 0x03, 0x47, 0x64, 0x1C, 0x31, 0xF8, + 0x17, 0x70, 0x20, 0xF8, + 0x16, 0x70, 0x76, 0x1C, 0x9C, 0x42, 0xF5, 0xDB, 0x6D, 0x1C, 0x95, 0x42, + 0xF0, 0xDB, 0xF0, 0xBD, + 0x30, 0xB5, 0x00, 0x24, 0x01, 0x23, 0x05, 0xE0, 0x31, 0xF8, 0x13, 0x50, + 0x20, 0xF8, 0x14, 0x50, + 0x5B, 0x1C, 0x64, 0x1C, 0x93, 0x42, 0xF7, 0xDB, 0x30, 0xBD, 0x00, 0x00, + 0x70, 0xB5, 0x7E, 0x4D, + 0x4F, 0xF4, 0x25, 0x74, 0x21, 0x46, 0x28, 0x68, 0xFE, 0xF7, 0x17, 0xFC, + 0x00, 0x21, 0x1F, 0x20, + 0x0D, 0xF1, 0xFE, 0xF8, 0x22, 0x46, 0x29, 0x68, 0xBD, 0xE8, 0x70, 0x40, + 0x1F, 0x23, 0x77, 0x48, + 0xFE, 0xF7, 0xCA, 0xBA, 0x70, 0xB5, 0x76, 0x4D, 0x40, 0xF2, 0xAF, 0x14, + 0x21, 0x46, 0x28, 0x68, + 0xFE, 0xF7, 0x03, 0xFC, 0x00, 0x21, 0x1A, 0x20, 0x0D, 0xF1, 0xEA, 0xF8, + 0x22, 0x46, 0x29, 0x68, + 0xBD, 0xE8, 0x70, 0x40, 0x1A, 0x23, 0x6F, 0x48, 0xFE, 0xF7, 0xB6, 0xBA, + 0x2D, 0xE9, 0xF0, 0x41, + 0xDF, 0xF8, 0xB4, 0x81, 0x05, 0x1D, 0x0E, 0x46, 0x29, 0x46, 0x40, 0x46, + 0xFE, 0xF7, 0xED, 0xFB, + 0x30, 0x68, 0x6A, 0x4A, 0x00, 0xEB, 0x85, 0x03, 0x93, 0x42, 0x01, 0xD2, + 0x1B, 0x23, 0x13, 0xE0, + 0x67, 0x49, 0x8B, 0x42, 0x13, 0xD2, 0x90, 0x42, 0x0D, 0xD2, 0x44, 0x46, + 0x11, 0x1A, 0x8F, 0x08, + 0x90, 0x46, 0x1B, 0x23, 0x3A, 0x46, 0x21, 0x46, 0xFE, 0xF7, 0x96, 0xFA, + 0xEA, 0x1B, 0x04, 0xEB, + 0x87, 0x01, 0x1C, 0x23, 0x15, 0xE0, 0x1C, 0x23, 0x2A, 0x46, 0x41, 0x46, + 0x12, 0xE0, 0x5D, 0x4A, + 0x93, 0x42, 0x19, 0xD2, 0x88, 0x42, 0x15, 0xD2, 0x44, 0x46, 0x88, 0x46, + 0x09, 0x1A, 0x8F, 0x08, + 0x1C, 0x23, 0x3A, 0x46, 0x21, 0x46, 0xFE, 0xF7, 0x7F, 0xFA, 0xEA, 0x1B, + 0x04, 0xEB, 0x87, 0x01, + 0x1D, 0x23, 0x40, 0x46, 0xFE, 0xF7, 0x78, 0xFA, 0x30, 0x68, 0x00, 0xEB, + 0x85, 0x00, 0x30, 0x60, + 0xBD, 0xE8, 0xF0, 0x81, 0x1D, 0x23, 0xDF, 0xE7, 0x90, 0x42, 0x0D, 0xD2, + 0x47, 0x46, 0x11, 0x1A, + 0x8C, 0x08, 0x90, 0x46, 0x1D, 0x23, 0x22, 0x46, 0x39, 0x46, 0xFE, 0xF7, + 0x65, 0xFA, 0x2A, 0x1B, + 0x07, 0xEB, 0x84, 0x01, 0x1E, 0x23, 0xE4, 0xE7, 0x1E, 0x23, 0xCD, 0xE7, + 0xF8, 0xB5, 0x42, 0x4C, + 0x45, 0x48, 0x00, 0x90, 0x10, 0x34, 0x02, 0x20, 0xFD, 0xF7, 0xF3, 0xFD, + 0x00, 0x21, 0x1B, 0x20, + 0x0D, 0xF1, 0x7E, 0xF8, 0x00, 0x21, 0x1C, 0x20, 0x0D, 0xF1, 0x7A, 0xF8, + 0x00, 0x21, 0x1D, 0x20, + 0x0D, 0xF1, 0x76, 0xF8, 0x00, 0x21, 0x1E, 0x20, 0x0D, 0xF1, 0x72, 0xF8, + 0x10, 0x21, 0x20, 0x46, + 0x14, 0xF0, 0x72, 0xFB, 0x00, 0x20, 0x20, 0x80, 0xA0, 0x70, 0x2F, 0x4D, + 0x01, 0x20, 0xE0, 0x70, + 0x28, 0x68, 0xC1, 0x8A, 0xA1, 0x80, 0x80, 0x8A, 0xE0, 0x80, 0x04, 0x20, + 0x69, 0x46, 0xFF, 0xF7, + 0x7D, 0xFF, 0x01, 0x23, 0x00, 0x22, 0x21, 0x46, 0x10, 0x20, 0x11, 0xF0, + 0x37, 0xF8, 0x06, 0x46, + 0x69, 0x46, 0xFF, 0xF7, 0x73, 0xFF, 0x01, 0x23, 0x00, 0x22, 0x21, 0x46, + 0x11, 0x20, 0x11, 0xF0, + 0x2D, 0xF8, 0x69, 0x46, 0x30, 0x46, 0xFF, 0xF7, 0x69, 0xFF, 0x28, 0x68, + 0x90, 0xF8, 0x61, 0x04, + 0x48, 0xB1, 0x01, 0x23, 0x00, 0x22, 0x21, 0x46, 0x14, 0x20, 0x11, 0xF0, + 0x1F, 0xF8, 0x69, 0x46, + 0x30, 0x46, 0xFF, 0xF7, 0x5B, 0xFF, 0x28, 0x68, 0x90, 0xF8, 0x50, 0x12, + 0xC9, 0x07, 0x03, 0xD1, + 0x90, 0xF8, 0x80, 0x02, 0xC0, 0x07, 0x09, 0xD0, 0x01, 0x23, 0x00, 0x22, + 0x21, 0x46, 0x12, 0x20, + 0x11, 0xF0, 0x0C, 0xF8, 0x69, 0x46, 0x30, 0x46, 0xFF, 0xF7, 0x48, 0xFF, + 0x01, 0x23, 0x00, 0x22, + 0x21, 0x46, 0x13, 0x20, 0x11, 0xF0, 0x02, 0xF8, 0x69, 0x46, 0x30, 0x46, + 0xFF, 0xF7, 0x3E, 0xFF, + 0x28, 0x68, 0x90, 0xF8, 0x2F, 0x0A, 0xC0, 0x07, 0x09, 0xD0, 0x01, 0x23, + 0x00, 0x22, 0x21, 0x46, + 0x17, 0x20, 0x10, 0xF0, 0xF3, 0xFF, 0x69, 0x46, 0x30, 0x46, 0xFF, 0xF7, + 0x2F, 0xFF, 0x02, 0x20, + 0xFD, 0xF7, 0x9A, 0xFD, 0xF8, 0xBD, 0x00, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0xF0, 0xEF, 0x01, 0x00, + 0x50, 0x07, 0x10, 0x00, 0xF0, 0x9F, 0x01, 0x00, 0xD4, 0x56, 0x10, 0x00, + 0xF0, 0xBF, 0x01, 0x00, + 0xF0, 0xCF, 0x01, 0x00, 0xF0, 0xDF, 0x01, 0x00, 0xF0, 0xAF, 0x01, 0x00, + 0x10, 0xB5, 0x01, 0x46, + 0x34, 0x22, 0xFE, 0x48, 0x14, 0xF0, 0xBE, 0xFA, 0x01, 0x21, 0xBD, 0xE8, + 0x10, 0x40, 0x00, 0x20, + 0xFD, 0xF7, 0x7F, 0xBC, 0x70, 0xB5, 0x86, 0xB0, 0x06, 0x46, 0x68, 0x46, + 0x13, 0xF0, 0xD2, 0xFE, + 0xF7, 0x48, 0x00, 0x90, 0x02, 0x2E, 0x15, 0xD0, 0x07, 0x20, 0x8D, 0xF8, + 0x04, 0x00, 0xF3, 0x4C, + 0x68, 0x46, 0x13, 0xF0, 0xEC, 0xFE, 0x00, 0x22, 0x04, 0xAB, 0x01, 0x21, + 0x10, 0x46, 0xFD, 0xF7, + 0xB1, 0xFC, 0x01, 0x21, 0x00, 0x20, 0xFD, 0xF7, 0x90, 0xFC, 0x20, 0x78, + 0x30, 0xB1, 0x0F, 0xF0, + 0x74, 0xFC, 0xED, 0xE7, 0x03, 0x20, 0x8D, 0xF8, 0x09, 0x00, 0xE8, 0xE7, + 0xE9, 0x4D, 0x02, 0x2E, + 0x09, 0xD0, 0x13, 0xF0, 0x0C, 0xFF, 0xA8, 0x61, 0x13, 0xF0, 0x0B, 0xFF, + 0xC5, 0xE9, 0x10, 0x01, + 0xA0, 0x68, 0x06, 0xB0, 0x70, 0xBD, 0x13, 0xF0, 0x06, 0xFF, 0xA8, 0x61, + 0x13, 0xF0, 0x05, 0xFF, + 0xC5, 0xE9, 0x10, 0x01, 0x60, 0x69, 0xF4, 0xE7, 0x2D, 0xE9, 0xF0, 0x41, + 0xDD, 0x4C, 0x01, 0x25, + 0x60, 0x38, 0xD4, 0xE9, 0x10, 0x23, 0xA6, 0x69, 0x09, 0x28, 0x6F, 0xD2, + 0xDF, 0xE8, 0x00, 0xF0, + 0x05, 0x0F, 0x1E, 0x28, 0x37, 0x41, 0x50, 0x5A, 0x6A, 0x00, 0x32, 0x46, + 0x00, 0x23, 0x08, 0x46, + 0x00, 0xF0, 0x51, 0xFA, 0x85, 0x40, 0x60, 0x68, 0x05, 0x43, 0x65, 0x60, + 0x5E, 0xE0, 0x08, 0x46, + 0x00, 0xF0, 0x49, 0xFA, 0x02, 0x46, 0x01, 0x20, 0x00, 0x21, 0x14, 0xF0, + 0xDF, 0xF9, 0xD4, 0xE9, + 0x08, 0x23, 0x10, 0x43, 0x19, 0x43, 0xC4, 0xE9, 0x08, 0x01, 0x4F, 0xE0, + 0x32, 0x46, 0x00, 0x23, + 0x08, 0x46, 0x00, 0xF0, 0x38, 0xFA, 0x85, 0x40, 0xE0, 0x68, 0x05, 0x43, + 0xE5, 0x60, 0x45, 0xE0, + 0x08, 0x46, 0x00, 0xF0, 0x30, 0xFA, 0x02, 0x46, 0x01, 0x20, 0x00, 0x21, + 0x14, 0xF0, 0xC6, 0xF9, + 0xD4, 0xE9, 0x0C, 0x23, 0x10, 0x43, 0x19, 0x43, 0xC4, 0xE9, 0x0C, 0x01, + 0x36, 0xE0, 0x32, 0x46, + 0x00, 0x23, 0x08, 0x46, 0x00, 0xF0, 0x1F, 0xFA, 0x85, 0x40, 0xA0, 0x68, + 0x05, 0x43, 0xA5, 0x60, + 0x2C, 0xE0, 0x08, 0x46, 0x00, 0xF0, 0x17, 0xFA, 0x02, 0x46, 0x01, 0x20, + 0x00, 0x21, 0x14, 0xF0, + 0xAD, 0xF9, 0xD4, 0xE9, 0x0A, 0x23, 0x10, 0x43, 0x19, 0x43, 0xC4, 0xE9, + 0x0A, 0x01, 0x1D, 0xE0, + 0x32, 0x46, 0x00, 0x23, 0x08, 0x46, 0x00, 0xF0, 0x06, 0xFA, 0x85, 0x40, + 0x20, 0x69, 0x05, 0x43, + 0x25, 0x61, 0x13, 0xE0, 0x08, 0x46, 0x00, 0xF0, 0xFE, 0xF9, 0x01, 0x23, + 0x02, 0x46, 0x00, 0x21, + 0x18, 0x46, 0x14, 0xF0, 0x93, 0xF9, 0xD4, 0xE9, 0x0E, 0x23, 0x10, 0x43, + 0x19, 0x43, 0xC4, 0xE9, + 0x0E, 0x01, 0x03, 0xE0, 0x60, 0x69, 0x8D, 0x40, 0x05, 0x43, 0x65, 0x61, + 0x20, 0x68, 0x40, 0x1C, + 0x20, 0x60, 0xBD, 0xE8, 0xF0, 0x81, 0x2D, 0xE9, 0xF0, 0x41, 0x17, 0x46, + 0x04, 0x46, 0x0D, 0x46, + 0x00, 0x26, 0x09, 0xE0, 0xE0, 0x07, 0x03, 0xD0, 0x31, 0x46, 0x38, 0x46, + 0x0F, 0xF0, 0xBE, 0xFB, + 0x76, 0x1C, 0x6D, 0x08, 0x4F, 0xEA, 0x34, 0x04, 0x54, 0xEA, 0x05, 0x00, + 0xF2, 0xD1, 0xE8, 0xE7, + 0x70, 0xB5, 0x94, 0x4C, 0xE1, 0x68, 0x60, 0x68, 0xD4, 0xE9, 0x0C, 0x65, + 0x40, 0xEA, 0x01, 0x03, + 0xD4, 0xE9, 0x08, 0x21, 0x29, 0x43, 0x25, 0x69, 0x32, 0x43, 0x9D, 0x43, + 0x25, 0x61, 0xD4, 0xE9, + 0x0E, 0x35, 0x93, 0x43, 0x8D, 0x43, 0xC4, 0xE9, 0x0E, 0x35, 0x21, 0x68, + 0x00, 0x29, 0x2C, 0xD0, + 0x60, 0x22, 0x00, 0x21, 0xFF, 0xF7, 0xCF, 0xFF, 0xD4, 0xE9, 0x08, 0x01, + 0x61, 0x22, 0xFF, 0xF7, + 0xCA, 0xFF, 0x62, 0x22, 0x00, 0x21, 0xE0, 0x68, 0xFF, 0xF7, 0xC5, 0xFF, + 0xD4, 0xE9, 0x0C, 0x01, + 0x63, 0x22, 0xFF, 0xF7, 0xC0, 0xFF, 0x64, 0x22, 0x00, 0x21, 0xA0, 0x68, + 0xFF, 0xF7, 0xBB, 0xFF, + 0xD4, 0xE9, 0x0A, 0x01, 0x65, 0x22, 0xFF, 0xF7, 0xB6, 0xFF, 0x66, 0x22, + 0x00, 0x21, 0x20, 0x69, + 0xFF, 0xF7, 0xB1, 0xFF, 0xD4, 0xE9, 0x0E, 0x01, 0x67, 0x22, 0xFF, 0xF7, + 0xAC, 0xFF, 0x60, 0x69, + 0xBD, 0xE8, 0x70, 0x40, 0x68, 0x22, 0x00, 0x21, 0xA5, 0xE7, 0x70, 0xBD, + 0x2D, 0xE9, 0xF0, 0x5F, + 0x83, 0x46, 0x4F, 0xF0, 0x00, 0x0A, 0x13, 0xF0, 0x1A, 0xFE, 0x04, 0x46, + 0x13, 0xF0, 0x1B, 0xFE, + 0x40, 0xEA, 0x04, 0x06, 0x13, 0xF0, 0x15, 0xFE, 0x07, 0x46, 0x88, 0x46, + 0x13, 0xF0, 0x15, 0xFE, + 0x41, 0xEA, 0x08, 0x05, 0x67, 0x49, 0x40, 0xEA, 0x07, 0x04, 0x8E, 0x61, + 0xC1, 0xE9, 0x10, 0x45, + 0xFD, 0xF7, 0x0E, 0xFD, 0x00, 0xF0, 0xFF, 0x08, 0xFD, 0xF7, 0x0C, 0xFD, + 0x00, 0xF0, 0xFF, 0x09, + 0xFD, 0xF7, 0x19, 0xFE, 0x00, 0x27, 0x26, 0xE0, 0x01, 0x20, 0xB8, 0x40, + 0x30, 0x42, 0x21, 0xD0, + 0xF8, 0xB2, 0x00, 0x21, 0xFD, 0xF7, 0x17, 0xFE, 0x41, 0x07, 0x05, 0xD5, + 0x5F, 0xEA, 0xCB, 0x61, + 0x02, 0xD5, 0x51, 0x46, 0x62, 0x20, 0x0F, 0xE0, 0xC1, 0x07, 0x05, 0xD0, + 0x5F, 0xEA, 0x4B, 0x71, + 0x02, 0xD5, 0x51, 0x46, 0x60, 0x20, 0x07, 0xE0, 0x1B, 0xF0, 0xC0, 0x0F, + 0x06, 0xD0, 0x10, 0xF0, + 0x0A, 0x0F, 0x03, 0xD0, 0x51, 0x46, 0x64, 0x20, 0xFF, 0xF7, 0xDE, 0xFE, + 0x0A, 0xF1, 0x01, 0x00, + 0x00, 0xF0, 0xFF, 0x0A, 0x7F, 0x1C, 0x47, 0x45, 0xD6, 0xDB, 0xFD, 0xF7, + 0xEC, 0xFD, 0x00, 0x27, + 0x3E, 0x46, 0x29, 0xE0, 0x32, 0x46, 0x01, 0x20, 0x00, 0x21, 0x14, 0xF0, + 0xCF, 0xF8, 0x20, 0x40, + 0x29, 0x40, 0x08, 0x43, 0x1F, 0xD0, 0xF0, 0xB2, 0x01, 0x21, 0xFD, 0xF7, + 0xE4, 0xFD, 0x41, 0x07, + 0x05, 0xD5, 0x5F, 0xEA, 0x8B, 0x61, 0x02, 0xD5, 0x39, 0x46, 0x63, 0x20, + 0x0F, 0xE0, 0xC1, 0x07, + 0x05, 0xD0, 0x5F, 0xEA, 0x0B, 0x71, 0x02, 0xD5, 0x39, 0x46, 0x61, 0x20, + 0x07, 0xE0, 0x1B, 0xF4, + 0xC0, 0x7F, 0x06, 0xD0, 0x10, 0xF0, 0x0A, 0x0F, 0x03, 0xD0, 0x39, 0x46, + 0x65, 0x20, 0xFF, 0xF7, + 0xAB, 0xFE, 0x7F, 0x1C, 0xFF, 0xB2, 0x76, 0x1C, 0x4E, 0x45, 0xD3, 0xDD, + 0xBD, 0xE8, 0xF0, 0x5F, + 0xFD, 0xF7, 0xB9, 0xBD, 0x30, 0x48, 0x02, 0x68, 0x12, 0xF8, 0xD3, 0x1F, + 0xD0, 0x78, 0x00, 0xF0, + 0x1F, 0x00, 0x40, 0x1C, 0x41, 0x43, 0x48, 0x00, 0xC0, 0xEB, 0x40, 0x20, + 0x40, 0x10, 0x70, 0x47, + 0x2D, 0xE9, 0xFC, 0x5F, 0x07, 0x46, 0x00, 0x20, 0xCD, 0xE9, 0x00, 0x00, + 0x1C, 0x46, 0x92, 0x46, + 0x0E, 0x46, 0xFF, 0xF7, 0xE7, 0xFF, 0x00, 0xEB, 0x04, 0x08, 0x0C, 0x98, + 0x80, 0x07, 0x50, 0xD5, + 0x00, 0x24, 0x4C, 0xE0, 0x4F, 0xF0, 0x00, 0x09, 0x4F, 0xF0, 0xFF, 0x3B, + 0x4D, 0x46, 0x27, 0xE0, + 0x05, 0xFB, 0x06, 0x40, 0x3A, 0xF9, 0x10, 0x20, 0x42, 0x45, 0x20, 0xD2, + 0x09, 0xF1, 0x01, 0x09, + 0xBB, 0xF1, 0xFF, 0x3F, 0x00, 0xD1, 0xAB, 0x46, 0x79, 0x1E, 0x8D, 0x42, + 0x17, 0xD1, 0xB9, 0xF1, + 0x01, 0x0F, 0x14, 0xD1, 0x71, 0x1E, 0x8C, 0x42, 0x04, 0xD1, 0x29, 0x46, + 0x66, 0x20, 0xFF, 0xF7, + 0x63, 0xFE, 0x0C, 0xE0, 0x0A, 0xEB, 0x40, 0x00, 0xB0, 0xF9, 0x02, 0x20, + 0x42, 0x45, 0x04, 0xD3, + 0x2C, 0xB1, 0x30, 0xF9, 0x02, 0x1C, 0x41, 0x45, 0x01, 0xD2, 0x4F, 0xF0, + 0x00, 0x09, 0x6D, 0x1C, + 0xBD, 0x42, 0xD5, 0xD3, 0xA7, 0xEB, 0x0B, 0x01, 0x49, 0x45, 0x17, 0xD8, + 0x21, 0x46, 0x67, 0x20, + 0xFF, 0xF7, 0x4A, 0xFE, 0x01, 0x20, 0x22, 0x46, 0x00, 0x21, 0x07, 0xE0, + 0x7C, 0x0F, 0x10, 0x00, + 0x0D, 0x2C, 0x00, 0x00, 0x70, 0x04, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x14, 0xF0, 0x3E, 0xF8, + 0xDD, 0xE9, 0x00, 0x23, 0x10, 0x43, 0x19, 0x43, 0xCD, 0xE9, 0x00, 0x01, + 0x64, 0x1C, 0xB4, 0x42, + 0xB0, 0xD3, 0x0C, 0x98, 0xC0, 0x07, 0x25, 0xD0, 0x00, 0x25, 0x21, 0xE0, + 0x4F, 0xF0, 0x00, 0x09, + 0x4C, 0x46, 0x13, 0xE0, 0x05, 0xFB, 0x06, 0x41, 0x3A, 0xF9, 0x11, 0x10, + 0x41, 0x45, 0x0C, 0xD2, + 0x22, 0x46, 0x01, 0x20, 0x00, 0x21, 0x14, 0xF0, 0x21, 0xF8, 0xDD, 0xE9, + 0x00, 0x23, 0x10, 0x40, + 0x19, 0x40, 0x08, 0x43, 0x01, 0xD1, 0x09, 0xF1, 0x01, 0x09, 0x64, 0x1C, + 0xB4, 0x42, 0xE9, 0xD3, + 0xB9, 0xF1, 0x00, 0x0F, 0x03, 0xD0, 0x29, 0x46, 0x66, 0x20, 0xFF, 0xF7, + 0x0D, 0xFE, 0x6D, 0x1C, + 0xBD, 0x42, 0xDB, 0xD3, 0xBD, 0xE8, 0xFC, 0x9F, 0x2D, 0xE9, 0xFF, 0x47, + 0x04, 0x46, 0x00, 0x20, + 0xFD, 0xF7, 0x27, 0xFB, 0x4C, 0x49, 0x4F, 0xF0, 0x01, 0x09, 0x81, 0xF8, + 0x00, 0x90, 0x00, 0xF0, + 0x7D, 0xF8, 0x14, 0xF0, 0x0C, 0x0F, 0x09, 0xD1, 0xE0, 0x06, 0x07, 0xD4, + 0xA0, 0x06, 0x05, 0xD4, + 0x60, 0x06, 0x03, 0xD4, 0xE0, 0x05, 0x01, 0xD4, 0x20, 0x06, 0x02, 0xD5, + 0x20, 0x46, 0xFF, 0xF7, + 0xC5, 0xFE, 0x42, 0x4E, 0xA0, 0x07, 0x1C, 0xD0, 0x01, 0x20, 0xFF, 0xF7, + 0xAB, 0xFD, 0x05, 0x46, + 0x13, 0xF0, 0xDD, 0xFC, 0x01, 0x90, 0x13, 0xF0, 0xDC, 0xFC, 0xCD, 0xE9, + 0x02, 0x01, 0x04, 0x21, + 0x01, 0xA8, 0xFE, 0xF7, 0x68, 0xF8, 0x07, 0x46, 0x08, 0x21, 0x02, 0xA8, + 0xFE, 0xF7, 0x63, 0xF8, + 0x01, 0x46, 0x00, 0x94, 0x30, 0x68, 0x2A, 0x46, 0x90, 0xF8, 0xDA, 0x30, + 0x38, 0x46, 0xFF, 0xF7, + 0x37, 0xFF, 0x14, 0xF4, 0xC0, 0x6F, 0x26, 0xD0, 0x30, 0x68, 0x90, 0xF8, + 0x61, 0x04, 0x10, 0xB3, + 0x00, 0x25, 0xFF, 0xF7, 0x1F, 0xFF, 0x31, 0x68, 0xB0, 0x46, 0x91, 0xF8, + 0xDB, 0x10, 0x0F, 0x18, + 0x02, 0x20, 0xFF, 0xF7, 0x7F, 0xFD, 0x06, 0x46, 0x5F, 0xF0, 0x00, 0x04, + 0xD8, 0xF8, 0x00, 0x00, + 0x90, 0xF8, 0x61, 0x14, 0x09, 0xFA, 0x04, 0xF0, 0x01, 0x42, 0x09, 0xD0, + 0x6D, 0x1C, 0x36, 0xF9, + 0x14, 0x00, 0xED, 0xB2, 0xB8, 0x42, 0x03, 0xD2, 0x29, 0x46, 0x68, 0x20, + 0xFF, 0xF7, 0xA4, 0xFD, + 0x64, 0x1C, 0x04, 0x2C, 0xEA, 0xDB, 0xFF, 0xF7, 0x33, 0xFE, 0x04, 0xB0, + 0x00, 0x20, 0xBD, 0xE8, + 0xF0, 0x47, 0xFD, 0xF7, 0xD9, 0xBA, 0x2D, 0xE9, 0xF0, 0x41, 0x16, 0x46, + 0x1F, 0x46, 0x80, 0x46, + 0x4F, 0xF0, 0xFF, 0x35, 0x00, 0x24, 0x22, 0x46, 0x01, 0x20, 0x00, 0x21, + 0x13, 0xF0, 0x8E, 0xFF, + 0x30, 0x40, 0x39, 0x40, 0x08, 0x43, 0x04, 0xD0, 0x6D, 0x1C, 0x45, 0x45, + 0x01, 0xD1, 0x20, 0x46, + 0xFF, 0xE5, 0x64, 0x1C, 0x40, 0x2C, 0xEE, 0xD3, 0x00, 0x20, 0xFA, 0xE5, + 0x0C, 0x48, 0x00, 0x21, + 0x01, 0x61, 0x41, 0x60, 0x81, 0x60, 0xC1, 0x60, 0xC0, 0xE9, 0x0E, 0x11, + 0xC0, 0xE9, 0x08, 0x11, + 0xC0, 0xE9, 0x0A, 0x11, 0x41, 0x61, 0x01, 0x60, 0xC0, 0xE9, 0x0C, 0x11, + 0x41, 0xF2, 0xC8, 0x11, + 0x04, 0x48, 0x14, 0xF0, 0x43, 0xB8, 0x00, 0x00, 0x78, 0x07, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, + 0x70, 0x04, 0x10, 0x00, 0x78, 0x8B, 0x01, 0x20, 0x11, 0x49, 0x08, 0x60, + 0x4F, 0xF4, 0x00, 0x51, + 0x00, 0x20, 0xFD, 0xF7, 0x9E, 0xB9, 0x38, 0xB5, 0xAF, 0xF2, 0x13, 0x00, + 0xFE, 0xF7, 0xAB, 0xFB, + 0x00, 0x22, 0x4F, 0xF4, 0x00, 0x54, 0x6B, 0x46, 0x21, 0x46, 0x10, 0x46, + 0xFD, 0xF7, 0xDA, 0xF9, + 0x21, 0x46, 0x00, 0x20, 0xFD, 0xF7, 0xB9, 0xF9, 0x06, 0x48, 0xA5, 0x22, + 0x01, 0x68, 0x04, 0x48, + 0x0A, 0x74, 0x00, 0x68, 0x20, 0xF0, 0x7F, 0x42, 0x4A, 0x61, 0xFE, 0xF7, + 0xB2, 0xFB, 0x38, 0xBD, + 0xB8, 0x04, 0x10, 0x00, 0x50, 0x07, 0x10, 0x00, 0x02, 0x00, 0x04, 0xD0, + 0x01, 0x2A, 0x04, 0xD0, + 0x02, 0x28, 0x03, 0xD1, 0x04, 0xE0, 0x01, 0x20, 0x00, 0xE0, 0x04, 0x20, + 0x13, 0xF0, 0x32, 0xBB, + 0x01, 0x20, 0x12, 0xF0, 0xD1, 0xBC, 0x10, 0xB5, 0x01, 0x46, 0x34, 0x22, + 0xFA, 0x48, 0x13, 0xF0, + 0xA1, 0xFF, 0x01, 0x21, 0xBD, 0xE8, 0x10, 0x40, 0x00, 0x20, 0xFD, 0xF7, + 0x62, 0xB9, 0x30, 0xB5, + 0x85, 0xB0, 0x04, 0x46, 0x68, 0x46, 0x13, 0xF0, 0xB5, 0xFB, 0xF4, 0x48, + 0x00, 0x90, 0xAC, 0xB1, + 0x01, 0x2C, 0x15, 0xD0, 0x02, 0x2C, 0x15, 0xD0, 0x03, 0x2C, 0x31, 0xD1, + 0x5F, 0xF0, 0x08, 0x00, + 0x8D, 0xF8, 0x04, 0x00, 0x01, 0x20, 0x8D, 0xF8, 0x0D, 0x00, 0xED, 0x48, + 0x00, 0x68, 0x90, 0xF8, + 0xB0, 0x00, 0xC0, 0xF3, 0xC1, 0x00, 0x02, 0x28, 0x08, 0xD0, 0x0A, 0xE0, + 0x05, 0x20, 0xEF, 0xE7, + 0x06, 0x20, 0xED, 0xE7, 0x02, 0x20, 0x8D, 0xF8, 0x09, 0x00, 0xEB, 0xE7, + 0x00, 0x20, 0x8D, 0xF8, + 0x0D, 0x00, 0xE1, 0x4D, 0x03, 0x24, 0x68, 0x46, 0x13, 0xF0, 0xB1, 0xFB, + 0x00, 0x22, 0x04, 0xAB, + 0x01, 0x21, 0x10, 0x46, 0xFD, 0xF7, 0x76, 0xF9, 0x01, 0x21, 0x00, 0x20, + 0xFD, 0xF7, 0x55, 0xF9, + 0x28, 0x78, 0x00, 0x28, 0x04, 0xD0, 0x0F, 0xF0, 0x38, 0xF9, 0x64, 0x1E, + 0x00, 0x2C, 0xEA, 0xDC, + 0x05, 0xB0, 0x30, 0xBD, 0x2D, 0xE9, 0xF1, 0x4F, 0x88, 0xB0, 0x13, 0xF0, + 0xD0, 0xFB, 0x01, 0x90, + 0x13, 0xF0, 0xCF, 0xFB, 0x06, 0x46, 0xD2, 0x4C, 0x08, 0x98, 0x88, 0x46, + 0x00, 0x28, 0x20, 0x68, + 0x07, 0xD0, 0xB0, 0xF8, 0x7A, 0x11, 0x08, 0x98, 0xFF, 0xF7, 0x8E, 0xFF, + 0x03, 0x90, 0xCD, 0x48, + 0x10, 0xE0, 0x90, 0xF8, 0xE7, 0x00, 0x10, 0xF0, 0x60, 0x0F, 0x03, 0xD0, + 0x01, 0x98, 0x10, 0xF0, + 0xDB, 0xF8, 0x01, 0x90, 0x20, 0x68, 0xB0, 0xF8, 0x72, 0x11, 0x08, 0x98, + 0xFF, 0xF7, 0x7C, 0xFF, + 0x03, 0x90, 0xC5, 0x48, 0x02, 0x90, 0x20, 0x20, 0x06, 0x90, 0xBF, 0x48, + 0x4F, 0xF0, 0x40, 0x0A, + 0xB8, 0x21, 0xD0, 0x38, 0x13, 0xF0, 0x82, 0xFF, 0x00, 0x20, 0x00, 0x90, + 0x08, 0x98, 0xFF, 0xF7, + 0x86, 0xFF, 0xB9, 0x48, 0x00, 0x27, 0xB9, 0x46, 0x80, 0x68, 0x4F, 0xF0, + 0x01, 0x0B, 0x3C, 0x46, + 0x05, 0x90, 0x01, 0x20, 0x01, 0x99, 0xA0, 0x40, 0x08, 0x42, 0x40, 0xD0, + 0x00, 0x25, 0x2A, 0x46, + 0x01, 0x20, 0x00, 0x21, 0x13, 0xF0, 0x92, 0xFE, 0x02, 0x46, 0x0B, 0x46, + 0x30, 0x40, 0x01, 0xEA, + 0x08, 0x01, 0x08, 0x43, 0x21, 0xD0, 0xAC, 0x48, 0xD0, 0x38, 0x00, 0xEB, + 0xC4, 0x01, 0x04, 0x91, + 0xD1, 0xE9, 0x00, 0x01, 0x8E, 0x46, 0x00, 0xEA, 0x02, 0x0C, 0x19, 0x40, + 0x5C, 0xEA, 0x01, 0x0C, + 0x12, 0xD1, 0x05, 0x99, 0x31, 0xF9, 0x17, 0xC0, 0x03, 0x99, 0x8C, 0x45, + 0x06, 0xDC, 0x10, 0x43, + 0x04, 0x9A, 0x4E, 0xEA, 0x03, 0x01, 0xC2, 0xE9, 0x00, 0x01, 0x05, 0xE0, + 0x2A, 0x46, 0x49, 0x46, + 0x00, 0x9B, 0x02, 0x98, 0xFD, 0xF7, 0xF9, 0xFA, 0x7F, 0x1C, 0x6D, 0x1C, + 0x55, 0x45, 0xCE, 0xDB, + 0x99, 0x48, 0xD0, 0x38, 0x00, 0xEB, 0xC4, 0x01, 0xD1, 0xE9, 0x00, 0x01, + 0x70, 0x40, 0x81, 0xEA, + 0x08, 0x01, 0x08, 0x43, 0x01, 0xD0, 0x4F, 0xF0, 0x00, 0x0B, 0x09, 0xF1, + 0x01, 0x09, 0x06, 0x98, + 0x64, 0x1C, 0x84, 0x42, 0xB5, 0xDB, 0xBB, 0xF1, 0x01, 0x0F, 0x04, 0xD0, + 0x00, 0x98, 0x40, 0x1C, + 0x00, 0x90, 0x7F, 0x28, 0xA2, 0xDD, 0x09, 0xB0, 0x58, 0x46, 0xBD, 0xE8, + 0xF0, 0x8F, 0x2D, 0xE9, + 0xF1, 0x4F, 0x8A, 0xB0, 0x13, 0xF0, 0x3B, 0xFB, 0x06, 0x90, 0x13, 0xF0, + 0x38, 0xFB, 0x00, 0x21, + 0xCD, 0xE9, 0x02, 0x01, 0x0A, 0x98, 0x86, 0x4C, 0x00, 0x28, 0x56, 0xD0, + 0x20, 0x68, 0xB0, 0xF8, + 0x7A, 0x11, 0x0A, 0x98, 0xFF, 0xF7, 0xF8, 0xFE, 0x01, 0x90, 0x82, 0x48, + 0x05, 0x90, 0x20, 0x20, + 0x09, 0x90, 0x4F, 0xF0, 0x40, 0x0A, 0x04, 0x21, 0x06, 0xA8, 0xFD, 0xF7, + 0xB4, 0xFE, 0x04, 0x46, + 0x08, 0x21, 0x02, 0xA8, 0xFD, 0xF7, 0xAF, 0xFE, 0x44, 0x43, 0x7C, 0x48, + 0x00, 0x90, 0x00, 0xF5, + 0xE6, 0x60, 0x21, 0x46, 0x04, 0x90, 0x13, 0xF0, 0xCF, 0xFE, 0x61, 0x00, + 0x00, 0x98, 0x13, 0xF0, + 0xCB, 0xFE, 0x71, 0x48, 0xB8, 0x21, 0xD0, 0x38, 0x13, 0xF0, 0xE8, 0xFE, + 0x00, 0x25, 0x0A, 0x98, + 0xFF, 0xF7, 0xED, 0xFE, 0x6C, 0x48, 0x4F, 0xF0, 0x00, 0x09, 0x4C, 0x46, + 0x80, 0x68, 0x07, 0x90, + 0x68, 0x42, 0x4F, 0xF0, 0x01, 0x0B, 0x4E, 0x46, 0x08, 0x90, 0x01, 0x20, + 0x06, 0x99, 0xB0, 0x40, + 0x08, 0x42, 0x79, 0xD0, 0x00, 0x27, 0x3A, 0x46, 0x01, 0x20, 0x00, 0x21, + 0x13, 0xF0, 0xF6, 0xFD, + 0xDD, 0xE9, 0x02, 0x8C, 0x02, 0x46, 0x0B, 0x46, 0x00, 0xEA, 0x08, 0x00, + 0x01, 0xEA, 0x0C, 0x01, + 0x08, 0x43, 0x6A, 0xD0, 0x5C, 0x48, 0xD0, 0x38, 0x00, 0xEB, 0xC6, 0x0C, + 0xDC, 0xE9, 0x00, 0x01, + 0x10, 0x40, 0x19, 0x40, 0x08, 0x43, 0x0D, 0xD0, 0x89, 0xE0, 0x06, 0x98, + 0x0F, 0xF0, 0xFC, 0xFF, + 0x06, 0x90, 0x20, 0x68, 0xB0, 0xF8, 0x72, 0x11, 0x0A, 0x98, 0xFF, 0xF7, + 0x9D, 0xFE, 0x01, 0x90, + 0x55, 0x48, 0xA3, 0xE7, 0x00, 0x2D, 0x0A, 0xDA, 0x07, 0x98, 0x30, 0xF9, + 0x14, 0x10, 0x01, 0x98, + 0x81, 0x42, 0x01, 0xDB, 0x00, 0x21, 0x00, 0xE0, 0x01, 0x21, 0x04, 0x98, + 0x01, 0x55, 0x07, 0x98, + 0x01, 0x99, 0x30, 0xF9, 0x14, 0x00, 0x88, 0x42, 0x2A, 0xDD, 0x04, 0x99, + 0x09, 0x5D, 0x11, 0xB1, + 0x01, 0x29, 0x07, 0xD0, 0x63, 0xE0, 0x7F, 0x2D, 0x61, 0xDA, 0x00, 0x99, + 0x6B, 0x1C, 0x21, 0xF8, + 0x14, 0x00, 0x4C, 0xE0, 0xDC, 0xE9, 0x00, 0x10, 0x11, 0x43, 0x18, 0x43, + 0xCC, 0xE9, 0x00, 0x10, + 0x00, 0x98, 0x30, 0xF9, 0x14, 0x10, 0x01, 0x98, 0x08, 0x1A, 0x07, 0x99, + 0x31, 0xF9, 0x14, 0x20, + 0x01, 0x99, 0xA2, 0xEB, 0x01, 0x01, 0x00, 0xD5, 0x40, 0x42, 0x00, 0x29, + 0x00, 0xDA, 0x49, 0x42, + 0x88, 0x42, 0x3A, 0xDC, 0x08, 0x9B, 0x3A, 0x46, 0x49, 0x46, 0x5B, 0x1C, + 0x05, 0x98, 0x31, 0xE0, + 0x37, 0xDA, 0x04, 0x99, 0x09, 0x5D, 0x01, 0x29, 0x01, 0xD0, 0x79, 0xB1, + 0x37, 0xE0, 0x08, 0x99, + 0x11, 0xF1, 0x7F, 0x0F, 0x33, 0xDD, 0x00, 0x99, 0x3A, 0x46, 0x21, 0xF8, + 0x14, 0x00, 0x08, 0x9B, + 0x49, 0x46, 0x5B, 0x1E, 0x05, 0x98, 0x1D, 0xE0, 0x3E, 0xE0, 0x29, 0xE0, + 0xDC, 0xE9, 0x00, 0x10, + 0x11, 0x43, 0x18, 0x43, 0xCC, 0xE9, 0x00, 0x10, 0x00, 0x98, 0x30, 0xF9, + 0x14, 0x10, 0x01, 0x98, + 0x08, 0x1A, 0x07, 0x99, 0x31, 0xF9, 0x14, 0x20, 0x01, 0x99, 0xA2, 0xEB, + 0x01, 0x01, 0x00, 0xD5, + 0x40, 0x42, 0x00, 0x29, 0x00, 0xDA, 0x49, 0x42, 0x88, 0x42, 0x06, 0xDC, + 0x6B, 0x1E, 0x3A, 0x46, + 0x49, 0x46, 0x05, 0x98, 0xFD, 0xF7, 0xF1, 0xF9, 0x09, 0xE0, 0x00, 0x98, + 0x20, 0xF8, 0x14, 0x20, + 0x05, 0xE0, 0xDC, 0xE9, 0x00, 0x10, 0x11, 0x43, 0x18, 0x43, 0xCC, 0xE9, + 0x00, 0x10, 0x64, 0x1C, + 0x7F, 0x1C, 0x57, 0x45, 0xFF, 0xF6, 0x57, 0xAF, 0x0F, 0x48, 0xD0, 0x38, + 0x00, 0xEB, 0xC6, 0x01, + 0xD1, 0xE9, 0x00, 0x01, 0xDD, 0xE9, 0x02, 0x23, 0x50, 0x40, 0x59, 0x40, + 0x08, 0x43, 0x01, 0xD0, + 0x4F, 0xF0, 0x00, 0x0B, 0x09, 0xF1, 0x01, 0x09, 0x09, 0x98, 0x76, 0x1C, + 0x86, 0x42, 0xFF, 0xF6, + 0x3C, 0xAF, 0x58, 0x46, 0xBB, 0xF1, 0x01, 0x0F, 0x03, 0xD0, 0x6D, 0x1C, + 0x7F, 0x2D, 0x7F, 0xF7, + 0x26, 0xAF, 0x0B, 0xB0, 0xE9, 0xE6, 0x00, 0x00, 0x80, 0x10, 0x10, 0x00, + 0x47, 0x32, 0x00, 0x00, + 0x4C, 0x07, 0x10, 0x00, 0x08, 0x92, 0x01, 0x20, 0x78, 0x8B, 0x01, 0x20, + 0xD4, 0x56, 0x10, 0x00, + 0x2D, 0xE9, 0xF1, 0x4F, 0x96, 0xB0, 0x4F, 0xF0, 0x00, 0x0A, 0x13, 0xF0, + 0x18, 0xFA, 0x0D, 0x90, + 0x13, 0xF0, 0x17, 0xFA, 0xCD, 0xE9, 0x0E, 0x01, 0x16, 0x98, 0x48, 0xB1, + 0xFE, 0x48, 0x00, 0x68, + 0xB0, 0xF8, 0x7A, 0x11, 0x16, 0x98, 0xFF, 0xF7, 0xD7, 0xFD, 0x11, 0x90, + 0xFB, 0x48, 0x12, 0xE0, + 0xF9, 0x4C, 0x20, 0x68, 0x90, 0xF8, 0xE7, 0x00, 0x10, 0xF0, 0x60, 0x0F, + 0x03, 0xD0, 0x0D, 0x98, + 0x0F, 0xF0, 0x22, 0xFF, 0x0D, 0x90, 0x20, 0x68, 0xB0, 0xF8, 0x72, 0x11, + 0x16, 0x98, 0xFF, 0xF7, + 0xC3, 0xFD, 0x11, 0x90, 0xF2, 0x48, 0x01, 0x90, 0x40, 0x20, 0x0A, 0x90, + 0x20, 0x26, 0x04, 0x21, + 0x0D, 0xA8, 0xFD, 0xF7, 0x80, 0xFD, 0x0C, 0x90, 0x08, 0x21, 0x0E, 0xA8, + 0xFD, 0xF7, 0x7B, 0xFD, + 0x07, 0x90, 0xB8, 0x21, 0xEB, 0x48, 0x13, 0xF0, 0xC1, 0xFD, 0x01, 0x20, + 0x10, 0x90, 0x00, 0x20, + 0x04, 0x46, 0x05, 0x90, 0x22, 0x46, 0x01, 0x20, 0x00, 0x21, 0x13, 0xF0, + 0xDF, 0xFC, 0xDD, 0xE9, + 0x0E, 0x23, 0x81, 0x46, 0x8B, 0x46, 0x10, 0x40, 0x19, 0x40, 0x08, 0x43, + 0x78, 0xD0, 0xDE, 0x48, + 0x00, 0x68, 0x90, 0xF8, 0x70, 0x01, 0x40, 0x07, 0x13, 0xD5, 0x05, 0x98, + 0x80, 0xB1, 0x00, 0x25, + 0x0B, 0xE0, 0xDD, 0xE9, 0x01, 0x02, 0x29, 0x46, 0xFD, 0xF7, 0x4C, 0xF9, + 0x03, 0x46, 0x22, 0x46, + 0x29, 0x46, 0x01, 0x98, 0xFD, 0xF7, 0x51, 0xF9, 0x6D, 0x1C, 0x0C, 0x98, + 0x85, 0x42, 0xF0, 0xDB, + 0x02, 0x94, 0x16, 0x98, 0xFF, 0xF7, 0x9B, 0xFD, 0xD2, 0x48, 0x4F, 0xF0, + 0x01, 0x08, 0xD0, 0x30, + 0x00, 0x25, 0x80, 0x68, 0x2F, 0x46, 0x14, 0x90, 0x01, 0x20, 0x0D, 0x99, + 0xB8, 0x40, 0x08, 0x42, + 0x45, 0xD0, 0xCC, 0x48, 0x00, 0xEB, 0xC7, 0x01, 0x15, 0x91, 0xD1, 0xE9, + 0x00, 0x01, 0x00, 0xEA, + 0x09, 0x00, 0x01, 0xEA, 0x0B, 0x01, 0x08, 0x43, 0x2E, 0xD1, 0x22, 0x46, + 0x29, 0x46, 0x01, 0x98, + 0xFD, 0xF7, 0x20, 0xF9, 0x07, 0x9A, 0x05, 0x99, 0x05, 0xFB, 0x02, 0x12, + 0x14, 0x99, 0x31, 0xF9, + 0x12, 0x20, 0x11, 0x99, 0x8A, 0x42, 0x0A, 0xDA, 0x15, 0x98, 0x15, 0x99, + 0xD0, 0xE9, 0x00, 0x20, + 0x42, 0xEA, 0x09, 0x02, 0x40, 0xEA, 0x0B, 0x00, 0xC1, 0xE9, 0x00, 0x20, + 0x14, 0xE0, 0x7F, 0x28, + 0x06, 0xDA, 0x43, 0x1C, 0x22, 0x46, 0x29, 0x46, 0x01, 0x98, 0xFD, 0xF7, + 0x0E, 0xF9, 0x0B, 0xE0, + 0x15, 0x98, 0x15, 0x9A, 0xD0, 0xE9, 0x00, 0x10, 0x41, 0xEA, 0x09, 0x01, + 0x40, 0xEA, 0x0B, 0x00, + 0xC2, 0xE9, 0x00, 0x10, 0x00, 0x20, 0x10, 0x90, 0x15, 0x99, 0x6D, 0x1C, + 0xD1, 0xE9, 0x00, 0x01, + 0x00, 0xEA, 0x09, 0x00, 0x01, 0xEA, 0x0B, 0x01, 0x08, 0x43, 0x00, 0xD1, + 0x80, 0x46, 0x7F, 0x1C, + 0xB7, 0x42, 0xB1, 0xDB, 0xB8, 0xF1, 0x00, 0x0F, 0xA3, 0xD0, 0x05, 0x98, + 0x40, 0x1C, 0x05, 0x90, + 0x0A, 0x98, 0x64, 0x1C, 0x84, 0x42, 0xFF, 0xF6, 0x75, 0xAF, 0x00, 0x20, + 0xB4, 0xE0, 0x00, 0xBF, + 0x4F, 0xF0, 0x00, 0x08, 0x45, 0x46, 0x2A, 0x46, 0x01, 0x20, 0x00, 0x21, + 0x13, 0xF0, 0x4E, 0xFC, + 0xDD, 0xE9, 0x0E, 0x23, 0x83, 0x46, 0x06, 0x91, 0x10, 0x40, 0x19, 0x40, + 0x08, 0x43, 0x6E, 0xD0, + 0xB8, 0xF1, 0x00, 0x0F, 0x01, 0xD1, 0x4F, 0xF0, 0x01, 0x0A, 0x16, 0x98, + 0xFF, 0xF7, 0x1F, 0xFD, + 0x94, 0x48, 0x4F, 0xF0, 0x01, 0x09, 0xD0, 0x30, 0x00, 0x27, 0x80, 0x68, + 0x3C, 0x46, 0x14, 0x90, + 0x01, 0x20, 0x0D, 0x99, 0xA0, 0x40, 0x08, 0x42, 0x78, 0xD0, 0xB8, 0xF1, + 0x00, 0x0F, 0x0E, 0xDD, + 0x2A, 0x46, 0x39, 0x46, 0x01, 0x98, 0xFD, 0xF7, 0xAD, 0xF8, 0x8A, 0x49, + 0x2A, 0x46, 0xB8, 0x31, + 0x09, 0x57, 0x0B, 0x18, 0x39, 0x46, 0x01, 0x98, 0xFD, 0xF7, 0xAF, 0xF8, + 0x65, 0xE0, 0xBA, 0xF1, + 0x01, 0x0F, 0x12, 0xD1, 0x2A, 0x46, 0x39, 0x46, 0x01, 0x98, 0xFD, 0xF7, + 0x9B, 0xF8, 0x81, 0x49, + 0xB8, 0x31, 0x08, 0x55, 0x7F, 0x48, 0x00, 0xEB, 0xC4, 0x03, 0xD3, 0xE9, + 0x00, 0x01, 0x06, 0x9A, + 0x20, 0xEA, 0x0B, 0x00, 0x91, 0x43, 0xC3, 0xE9, 0x00, 0x01, 0x7A, 0x48, + 0x06, 0x9B, 0x00, 0xEB, + 0xC4, 0x00, 0x0C, 0x90, 0xD0, 0xE9, 0x00, 0x10, 0x01, 0xEA, 0x0B, 0x01, + 0x18, 0x40, 0x01, 0x43, + 0x39, 0xD1, 0x2A, 0x46, 0x39, 0x46, 0x01, 0x98, 0xFD, 0xF7, 0x7C, 0xF8, + 0x07, 0x9A, 0x14, 0x99, + 0x07, 0xFB, 0x02, 0x82, 0x31, 0xF9, 0x12, 0x20, 0x11, 0x99, 0x8A, 0x42, + 0x0E, 0xDA, 0x0C, 0x99, + 0x06, 0x9B, 0xD1, 0xE9, 0x00, 0xC2, 0x4C, 0xEA, 0x0B, 0x0C, 0x1A, 0x43, + 0xC1, 0xE9, 0x00, 0xC2, + 0x68, 0x49, 0xB8, 0x31, 0x0A, 0x5D, 0x80, 0x1A, 0x08, 0x55, 0x1C, 0xE0, + 0x7F, 0x28, 0x07, 0xDA, + 0x43, 0x1C, 0x2A, 0x46, 0x39, 0x46, 0x01, 0x98, 0xFD, 0xF7, 0x67, 0xF8, + 0x13, 0xE0, 0x29, 0xE0, + 0x0C, 0x99, 0xDD, 0xF8, 0x18, 0xC0, 0x0C, 0x9B, 0xD1, 0xE9, 0x00, 0x21, + 0x42, 0xEA, 0x0B, 0x02, + 0x41, 0xEA, 0x0C, 0x01, 0xC3, 0xE9, 0x00, 0x21, 0x5A, 0x49, 0xB8, 0x31, + 0x0A, 0x5D, 0x80, 0x1A, + 0x08, 0x55, 0x00, 0x20, 0x10, 0x90, 0x0C, 0x98, 0x06, 0x9A, 0xD0, 0xE9, + 0x00, 0x10, 0x01, 0xEA, + 0x0B, 0x01, 0x10, 0x40, 0x01, 0x43, 0x00, 0xD1, 0x89, 0x46, 0x7F, 0x1C, + 0x64, 0x1C, 0xB4, 0x42, + 0xFF, 0xF6, 0x7E, 0xAF, 0x4F, 0xF0, 0x00, 0x0A, 0xB9, 0xF1, 0x00, 0x0F, + 0x3F, 0xF4, 0x6D, 0xAF, + 0x08, 0xF1, 0x01, 0x08, 0x0A, 0x98, 0x6D, 0x1C, 0x85, 0x42, 0xFF, 0xF6, + 0x54, 0xAF, 0x16, 0x98, + 0xFF, 0xF7, 0x85, 0xFC, 0x0B, 0x98, 0x40, 0x1C, 0x0B, 0x90, 0x43, 0x48, + 0x00, 0x68, 0x90, 0xF8, + 0x70, 0x11, 0x0B, 0x98, 0xB0, 0xEB, 0x11, 0x1F, 0xFF, 0xF6, 0x42, 0xAF, + 0x10, 0x98, 0x17, 0xB0, + 0x4B, 0xE5, 0x2D, 0xE9, 0xF0, 0x4F, 0x8B, 0xB0, 0x13, 0xF0, 0x8D, 0xF8, + 0x09, 0x90, 0x13, 0xF0, + 0x8C, 0xF8, 0x8A, 0x46, 0x38, 0x49, 0x81, 0x46, 0x00, 0x20, 0x0A, 0x68, + 0x01, 0x46, 0x16, 0x46, + 0x92, 0xF8, 0x61, 0x54, 0x01, 0x24, 0x02, 0xF2, 0x72, 0x42, 0x00, 0xBF, + 0x04, 0xFA, 0x01, 0xF3, + 0x1D, 0x42, 0x03, 0xD0, 0x57, 0x5C, 0x04, 0xFA, 0x07, 0xF3, 0x18, 0x43, + 0x49, 0x1C, 0x04, 0x29, + 0xF4, 0xDB, 0x01, 0x90, 0x00, 0x20, 0x00, 0x90, 0x20, 0x20, 0x08, 0x90, + 0x40, 0x20, 0x07, 0x90, + 0xB6, 0xF8, 0x82, 0x11, 0x02, 0x20, 0xFF, 0xF7, 0x2F, 0xFC, 0x03, 0x90, + 0x2A, 0x48, 0x05, 0x90, + 0x2A, 0x48, 0x00, 0x21, 0x02, 0x90, 0x01, 0x60, 0x0D, 0x46, 0x41, 0x60, + 0x02, 0x20, 0xFF, 0xF7, + 0x3E, 0xFC, 0x24, 0x48, 0x00, 0x24, 0xD0, 0x30, 0xA3, 0x46, 0x40, 0x69, + 0x06, 0x90, 0x01, 0x20, + 0x27, 0x46, 0x04, 0x90, 0x01, 0x20, 0x09, 0x99, 0xB8, 0x40, 0x08, 0x42, + 0x63, 0xD0, 0x00, 0x26, + 0x32, 0x46, 0x01, 0x20, 0x00, 0x21, 0x13, 0xF0, 0x49, 0xFB, 0x80, 0x46, + 0x0A, 0x46, 0x00, 0xEA, + 0x09, 0x00, 0x01, 0xEA, 0x0A, 0x01, 0x08, 0x43, 0x4F, 0xD0, 0x16, 0x48, + 0x00, 0xEB, 0xC7, 0x01, + 0xD1, 0xE9, 0x00, 0x01, 0x00, 0xEA, 0x08, 0x00, 0x11, 0x40, 0x08, 0x43, + 0x44, 0xD1, 0x01, 0x20, + 0x01, 0x99, 0x00, 0xFA, 0x04, 0xF8, 0x18, 0xEA, 0x01, 0x0F, 0x3D, 0xD0, + 0x06, 0x98, 0x30, 0xF9, + 0x14, 0x20, 0x03, 0x98, 0x82, 0x42, 0x2C, 0xDC, 0x00, 0x2D, 0x2A, 0xDD, + 0x02, 0x98, 0x30, 0xF9, + 0x14, 0x10, 0x03, 0x98, 0x08, 0x1A, 0x03, 0x99, 0xA2, 0xEB, 0x01, 0x01, + 0x00, 0xD5, 0x40, 0x42, + 0x00, 0x29, 0x00, 0xDA, 0x49, 0x42, 0x0B, 0xE0, 0x4C, 0x07, 0x10, 0x00, + 0x08, 0x92, 0x01, 0x20, + 0x78, 0x8B, 0x01, 0x20, 0xB0, 0x0F, 0x10, 0x00, 0x98, 0x98, 0x01, 0x20, + 0xD4, 0x56, 0x10, 0x00, + 0x88, 0x42, 0x06, 0xDC, 0x6B, 0x1E, 0x32, 0x46, 0x59, 0x46, 0x05, 0x98, + 0xFC, 0xF7, 0x95, 0xFF, + 0x02, 0xE0, 0x02, 0x98, 0x20, 0xF8, 0x14, 0x20, 0x00, 0x99, 0x48, 0xEA, + 0x01, 0x00, 0x00, 0x90, + 0x0A, 0xE0, 0x7F, 0x2D, 0x08, 0xDA, 0x02, 0x98, 0x6B, 0x1C, 0x59, 0x46, + 0x20, 0xF8, 0x14, 0x20, + 0x32, 0x46, 0x05, 0x98, 0xFC, 0xF7, 0x81, 0xFF, 0x64, 0x1C, 0x07, 0x98, + 0x76, 0x1C, 0x86, 0x42, + 0x9E, 0xDB, 0x0B, 0xF1, 0x01, 0x0B, 0x08, 0x98, 0x7F, 0x1C, 0x87, 0x42, + 0x92, 0xDB, 0xDD, 0xE9, + 0x00, 0x01, 0x88, 0x42, 0x05, 0xD0, 0x00, 0x20, 0x6D, 0x1C, 0x04, 0x90, + 0x7F, 0x2D, 0x7F, 0xF7, + 0x7D, 0xAF, 0x04, 0x98, 0xA5, 0xE5, 0x2D, 0xE9, 0xFF, 0x5F, 0x04, 0x46, + 0x01, 0x25, 0x12, 0xF0, + 0xD0, 0xFF, 0xCD, 0xE9, 0x02, 0x01, 0xC5, 0x48, 0x40, 0x27, 0x08, 0x21, + 0x00, 0x68, 0x00, 0xF2, + 0x02, 0x60, 0x01, 0x90, 0xC2, 0x48, 0x00, 0x90, 0x02, 0xA8, 0xFD, 0xF7, + 0x54, 0xFB, 0x06, 0x46, + 0x01, 0x2C, 0x6E, 0xD1, 0xDD, 0xE9, 0x02, 0x23, 0x29, 0x46, 0x00, 0x98, + 0xFC, 0xF7, 0x5C, 0xFF, + 0xDF, 0xF8, 0xF0, 0x92, 0x00, 0x20, 0x09, 0xEB, 0x46, 0x0B, 0x47, 0xF6, + 0xFF, 0x72, 0x6F, 0xF0, + 0x7E, 0x01, 0x04, 0xE0, 0x29, 0xF8, 0x10, 0x20, 0x0B, 0xF8, 0x00, 0x10, + 0x40, 0x1C, 0xB0, 0x42, + 0xF8, 0xDB, 0x6F, 0xF0, 0x7F, 0x08, 0x03, 0x20, 0xFF, 0xF7, 0x89, 0xFB, + 0xB2, 0x48, 0x00, 0x24, + 0x25, 0x46, 0xD0, 0xF8, 0x08, 0xA0, 0x2A, 0x46, 0x01, 0x20, 0x00, 0x21, + 0x13, 0xF0, 0x9E, 0xFA, + 0xDD, 0xE9, 0x02, 0x23, 0x10, 0x40, 0x19, 0x40, 0x08, 0x43, 0x1D, 0xD0, + 0x3A, 0xF9, 0x14, 0x10, + 0x39, 0xF9, 0x14, 0x00, 0x00, 0x29, 0x00, 0xDA, 0x49, 0x42, 0x00, 0x28, + 0x00, 0xDA, 0x40, 0x42, + 0x81, 0x42, 0x0A, 0xDA, 0x2A, 0x46, 0x00, 0x21, 0x00, 0x98, 0xFC, 0xF7, + 0x0B, 0xFF, 0x0B, 0xF8, + 0x04, 0x00, 0x3A, 0xF8, 0x14, 0x10, 0x29, 0xF8, 0x14, 0x10, 0x43, 0x46, + 0x2A, 0x46, 0x00, 0x21, + 0x00, 0x98, 0xFC, 0xF7, 0x0A, 0xFF, 0x64, 0x1C, 0x6D, 0x1C, 0xBD, 0x42, + 0xD3, 0xDB, 0x08, 0xF1, + 0x01, 0x08, 0xB8, 0xF1, 0x7F, 0x0F, 0xC6, 0xDD, 0x00, 0x25, 0x2C, 0x46, + 0x22, 0x46, 0x01, 0x20, + 0x00, 0x21, 0x13, 0xF0, 0x6B, 0xFA, 0xDD, 0xE9, 0x02, 0x23, 0x10, 0x40, + 0x19, 0x40, 0x08, 0x43, + 0x07, 0xD0, 0x1B, 0xF9, 0x05, 0x30, 0x22, 0x46, 0x00, 0x21, 0x00, 0x98, + 0xFC, 0xF7, 0xED, 0xFE, + 0x6D, 0x1C, 0x64, 0x1C, 0xBC, 0x42, 0xE9, 0xDB, 0xDD, 0xE9, 0x00, 0x10, + 0x26, 0x22, 0x13, 0xF0, + 0x8C, 0xFA, 0x00, 0x25, 0xDF, 0xF8, 0x24, 0x82, 0x2C, 0x46, 0x22, 0x46, + 0x01, 0x20, 0x00, 0x21, + 0x13, 0xF0, 0x4C, 0xFA, 0xDD, 0xE9, 0x02, 0x23, 0x10, 0x40, 0x19, 0x40, + 0x08, 0x43, 0x07, 0xD0, + 0x22, 0x46, 0x00, 0x21, 0x00, 0x98, 0xFC, 0xF7, 0xC5, 0xFE, 0x28, 0xF8, + 0x15, 0x00, 0x6D, 0x1C, + 0x64, 0x1C, 0xBC, 0x42, 0xE9, 0xDB, 0x03, 0x20, 0xFF, 0xF7, 0x19, 0xFB, + 0x7A, 0x48, 0x42, 0x46, + 0x81, 0x68, 0x00, 0x20, 0x06, 0xE0, 0x00, 0xBF, 0x31, 0xF8, 0x10, 0x30, + 0x34, 0x18, 0x40, 0x1C, + 0x22, 0xF8, 0x14, 0x30, 0xB0, 0x42, 0xF7, 0xDB, 0xBD, 0xE8, 0xFF, 0x9F, + 0x2D, 0xE9, 0xF0, 0x5F, + 0x07, 0x46, 0x01, 0x24, 0x00, 0x20, 0xFC, 0xF7, 0x44, 0xFD, 0x02, 0x20, + 0xFC, 0xF7, 0x41, 0xFD, + 0x6F, 0x49, 0x01, 0x20, 0x08, 0x70, 0x69, 0x4E, 0xF8, 0x07, 0x4F, 0xF0, + 0x00, 0x08, 0x4F, 0xF0, + 0xA5, 0x09, 0x09, 0xD0, 0x00, 0xF0, 0x55, 0xF8, 0x10, 0xF0, 0x01, 0x04, + 0x04, 0xD0, 0x30, 0x68, + 0x80, 0xF8, 0x1B, 0x80, 0x80, 0xF8, 0x1A, 0x90, 0xB8, 0x07, 0x09, 0xD5, + 0x00, 0xF0, 0x8F, 0xF8, + 0x20, 0x40, 0x04, 0x00, 0x04, 0xD0, 0x30, 0x68, 0x80, 0xF8, 0x1D, 0x80, + 0x80, 0xF8, 0x1C, 0x90, + 0xDF, 0xF8, 0x80, 0xA1, 0xDA, 0xF8, 0x00, 0x00, 0x90, 0xF8, 0x61, 0x04, + 0x58, 0xB3, 0xF8, 0x06, + 0x29, 0xD5, 0x01, 0x25, 0x12, 0xF0, 0xEF, 0xFE, 0x83, 0x46, 0x12, 0xF0, + 0xEE, 0xFE, 0xDA, 0xF8, + 0x00, 0x20, 0x92, 0xF8, 0x80, 0x21, 0xD2, 0x07, 0x13, 0xD0, 0x02, 0x46, + 0x0B, 0x46, 0x59, 0x46, + 0x55, 0x48, 0xFC, 0xF7, 0x81, 0xFE, 0xDA, 0xF8, 0x00, 0x00, 0x90, 0xF8, + 0x80, 0x01, 0xC0, 0x07, + 0x07, 0xD0, 0xFF, 0xF7, 0x46, 0xFE, 0x05, 0x00, 0x03, 0xD1, 0x02, 0x21, + 0x74, 0x20, 0x0E, 0xF0, + 0x1D, 0xFC, 0x02, 0x20, 0xFF, 0xF7, 0xB3, 0xFA, 0x2C, 0x40, 0x04, 0xD0, + 0x30, 0x68, 0x80, 0xF8, + 0x25, 0x80, 0x80, 0xF8, 0x24, 0x90, 0x00, 0x20, 0xFC, 0xF7, 0x06, 0xFD, + 0x02, 0x20, 0xFC, 0xF7, + 0x03, 0xFD, 0xF8, 0x07, 0x02, 0xD0, 0x01, 0x20, 0x00, 0xF0, 0xBA, 0xF8, + 0x20, 0x46, 0xBD, 0xE8, + 0xF0, 0x9F, 0x2D, 0xE9, 0xF0, 0x41, 0x01, 0x24, 0x12, 0xF0, 0xB1, 0xFE, + 0x80, 0x46, 0x12, 0xF0, + 0xB0, 0xFE, 0x3C, 0x4D, 0x06, 0x46, 0x0F, 0x46, 0x28, 0x68, 0x90, 0xF8, + 0xE7, 0x00, 0x10, 0xF0, + 0x60, 0x0F, 0x03, 0xD0, 0x40, 0x46, 0x0F, 0xF0, 0xC7, 0xFB, 0x80, 0x46, + 0x28, 0x68, 0x90, 0xF8, + 0x70, 0x01, 0xC0, 0x07, 0x25, 0xD0, 0x32, 0x46, 0x3B, 0x46, 0x41, 0x46, + 0x33, 0x48, 0xFC, 0xF7, + 0x3B, 0xFE, 0x28, 0x68, 0x90, 0xF8, 0x70, 0x11, 0xC9, 0x07, 0x1A, 0xD0, + 0x90, 0xF8, 0xE8, 0x10, + 0x89, 0x07, 0x03, 0xD4, 0x00, 0x20, 0xFF, 0xF7, 0x6B, 0xFC, 0x0C, 0xE0, + 0x90, 0xF8, 0xE7, 0x00, + 0xC0, 0xF3, 0x41, 0x10, 0x02, 0x28, 0x4F, 0xF0, 0x00, 0x00, 0x02, 0xD3, + 0xFF, 0xF7, 0x3F, 0xFB, + 0x01, 0xE0, 0xFF, 0xF7, 0xA7, 0xFA, 0x04, 0x00, 0x03, 0xD1, 0x01, 0x21, + 0x74, 0x20, 0x0E, 0xF0, + 0xC5, 0xFB, 0x00, 0x20, 0xFF, 0xF7, 0x5B, 0xFA, 0x20, 0x46, 0xBD, 0xE8, + 0xF0, 0x81, 0x70, 0xB5, + 0x01, 0x24, 0x12, 0xF0, 0x6C, 0xFE, 0x06, 0x46, 0x12, 0xF0, 0x6B, 0xFE, + 0x19, 0x4D, 0x2A, 0x68, + 0x92, 0xF8, 0x78, 0x21, 0xD2, 0x07, 0x1B, 0xD0, 0x02, 0x46, 0x0B, 0x46, + 0x31, 0x46, 0x18, 0x48, + 0xFC, 0xF7, 0x02, 0xFE, 0x28, 0x68, 0x90, 0xF8, 0x78, 0x11, 0xC9, 0x07, + 0x10, 0xD0, 0x90, 0xF8, + 0xE8, 0x00, 0x80, 0x07, 0x20, 0x46, 0x02, 0xD4, 0xFF, 0xF7, 0x32, 0xFC, + 0x01, 0xE0, 0xFF, 0xF7, + 0x79, 0xFA, 0x04, 0x00, 0x04, 0xD1, 0x4F, 0xF4, 0x80, 0x71, 0x74, 0x20, + 0x0E, 0xF0, 0x96, 0xFB, + 0x01, 0x20, 0xFF, 0xF7, 0x2C, 0xFA, 0x20, 0x46, 0x70, 0xBD, 0x00, 0x00, + 0x50, 0x07, 0x10, 0x00, + 0xE4, 0x9F, 0x01, 0x20, 0xD4, 0x56, 0x10, 0x00, 0x80, 0x10, 0x10, 0x00, + 0x04, 0x4B, 0x01, 0x20, + 0x78, 0x07, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, 0x98, 0x98, 0x01, 0x20, + 0x78, 0x8B, 0x01, 0x20, + 0x08, 0x92, 0x01, 0x20, 0x10, 0xB5, 0xFF, 0xF7, 0xD6, 0xF9, 0x53, 0x20, + 0xFF, 0xF7, 0x06, 0xFF, + 0x00, 0x28, 0x12, 0xD0, 0x4F, 0xF4, 0xD6, 0x70, 0x00, 0xF0, 0x7B, 0xFF, + 0x00, 0x28, 0x0C, 0xD0, + 0x24, 0x48, 0x25, 0x49, 0x00, 0x68, 0x09, 0x68, 0x90, 0xF8, 0xDE, 0x00, + 0x08, 0x76, 0xFE, 0xF7, + 0x45, 0xFE, 0xBD, 0xE8, 0x10, 0x40, 0xFE, 0xF7, 0xD5, 0xBD, 0x10, 0xBD, + 0x10, 0xB5, 0xFF, 0xF7, + 0xBA, 0xF9, 0x40, 0xF2, 0xFF, 0x10, 0xFE, 0xF7, 0x41, 0xFD, 0x1A, 0x48, + 0x1A, 0x49, 0x00, 0x68, + 0x09, 0x68, 0x90, 0xF8, 0xDE, 0x00, 0x08, 0x76, 0xBD, 0xE8, 0x10, 0x40, + 0xFE, 0xF7, 0xC2, 0xBD, + 0x2D, 0xE9, 0xF0, 0x41, 0x13, 0x49, 0x09, 0x68, 0x91, 0xF8, 0xC1, 0x21, + 0xD2, 0x07, 0x1F, 0xD0, + 0x01, 0x28, 0x1D, 0xD1, 0x10, 0x4F, 0x91, 0xF8, 0x31, 0x20, 0x91, 0xF8, + 0x30, 0x10, 0x3D, 0x68, + 0x4A, 0x43, 0x02, 0x20, 0x0D, 0x4E, 0x14, 0xB2, 0x32, 0x35, 0xFD, 0xF7, + 0xE2, 0xFF, 0x03, 0x20, + 0xFC, 0xF7, 0x0F, 0xFC, 0x38, 0x68, 0x62, 0x00, 0x31, 0x46, 0x04, 0x86, + 0x28, 0x46, 0x13, 0xF0, + 0x1C, 0xF9, 0x03, 0x20, 0xFC, 0xF7, 0x20, 0xFC, 0xBD, 0xE8, 0xF0, 0x41, + 0xFE, 0xF7, 0x9A, 0xBD, + 0xBD, 0xE8, 0xF0, 0x81, 0x4C, 0x07, 0x10, 0x00, 0x50, 0x07, 0x10, 0x00, + 0x42, 0x44, 0x01, 0x20, + 0x10, 0xB5, 0x01, 0x46, 0x34, 0x22, 0xFB, 0x48, 0x13, 0xF0, 0x4C, 0xF9, + 0x01, 0x21, 0xBD, 0xE8, + 0x10, 0x40, 0x00, 0x20, 0xFC, 0xF7, 0x0D, 0xBB, 0x30, 0xB5, 0x85, 0xB0, + 0x68, 0x46, 0x12, 0xF0, + 0x61, 0xFD, 0xF5, 0x48, 0x02, 0x22, 0x00, 0x78, 0xE0, 0xB1, 0x03, 0x21, + 0x01, 0x28, 0x0D, 0xD0, + 0x03, 0x28, 0x0E, 0xD0, 0x02, 0x28, 0x18, 0xD0, 0x04, 0x22, 0x05, 0x28, + 0x0C, 0xD0, 0x04, 0x28, + 0x0D, 0xD0, 0x06, 0x28, 0x0E, 0xD0, 0x07, 0x28, 0x0F, 0xD0, 0x10, 0xE0, + 0x8D, 0xF8, 0x06, 0x10, + 0x0D, 0xE0, 0x8D, 0xF8, 0x08, 0x10, 0x0A, 0xE0, 0x8D, 0xF8, 0x0A, 0x20, + 0x07, 0xE0, 0x8D, 0xF8, + 0x0A, 0x10, 0x04, 0xE0, 0x8D, 0xF8, 0x06, 0x20, 0x01, 0xE0, 0x8D, 0xF8, + 0x08, 0x20, 0xE3, 0x48, + 0x00, 0x90, 0xE3, 0x48, 0x00, 0x68, 0x90, 0xF8, 0xB0, 0x10, 0xC9, 0x07, + 0x0B, 0xD0, 0x01, 0x21, + 0x8D, 0xF8, 0x0D, 0x10, 0x90, 0xF8, 0xB0, 0x00, 0xC0, 0xF3, 0xC1, 0x00, + 0x02, 0x28, 0x02, 0xD1, + 0x00, 0x20, 0x8D, 0xF8, 0x0D, 0x00, 0xD7, 0x4D, 0x03, 0x24, 0x68, 0x46, + 0x12, 0xF0, 0x47, 0xFD, + 0x00, 0x22, 0x04, 0xAB, 0x01, 0x21, 0x10, 0x46, 0xFC, 0xF7, 0x0C, 0xFB, + 0x01, 0x21, 0x00, 0x20, + 0xFC, 0xF7, 0xEB, 0xFA, 0x28, 0x78, 0x00, 0x28, 0x04, 0xD0, 0x0E, 0xF0, + 0xCE, 0xFA, 0x64, 0x1E, + 0x00, 0x2C, 0xEA, 0xDC, 0x05, 0xB0, 0x30, 0xBD, 0x2D, 0xE9, 0xF0, 0x4F, + 0xCA, 0x4E, 0x04, 0x46, + 0x87, 0xB0, 0x06, 0x20, 0x89, 0x46, 0x30, 0x70, 0x12, 0xF0, 0x61, 0xFD, + 0x00, 0x21, 0x20, 0x27, + 0xCD, 0xE9, 0x04, 0x01, 0x1D, 0x20, 0xC7, 0x4D, 0x02, 0x90, 0x24, 0xB1, + 0x01, 0x2C, 0x0E, 0xD0, + 0x02, 0x2C, 0x21, 0xD0, 0x2F, 0xE0, 0xC4, 0x48, 0x00, 0x90, 0xDF, 0xF8, + 0xF8, 0x82, 0x28, 0x68, + 0x08, 0xF1, 0x18, 0x08, 0xDF, 0xF8, 0x04, 0xA3, 0x00, 0xF5, 0xC5, 0x60, + 0x22, 0xE0, 0x12, 0xF0, + 0x48, 0xFD, 0xCD, 0xE9, 0x04, 0x01, 0xBC, 0x48, 0x40, 0x27, 0x20, 0x30, + 0x00, 0x90, 0x28, 0x68, + 0xDF, 0xF8, 0xD0, 0x82, 0x00, 0xF2, 0x5E, 0x60, 0x01, 0x90, 0x26, 0x20, + 0x08, 0xF1, 0x1C, 0x08, + 0xDF, 0xF8, 0xDC, 0xA2, 0x02, 0x90, 0x0E, 0xE0, 0x07, 0x20, 0x30, 0x70, + 0xB2, 0x48, 0xDF, 0xF8, + 0xB4, 0x82, 0x48, 0x30, 0x00, 0x90, 0x28, 0x68, 0xDF, 0xF8, 0xC8, 0xA2, + 0x08, 0xF1, 0x28, 0x08, + 0x00, 0xF2, 0x84, 0x60, 0x01, 0x90, 0x08, 0x21, 0x04, 0xA8, 0xFD, 0xF7, + 0xB4, 0xF8, 0x04, 0x46, + 0xB9, 0xF1, 0x01, 0x0F, 0x74, 0xD1, 0x22, 0x46, 0x04, 0xA9, 0x00, 0x98, + 0xFC, 0xF7, 0xF7, 0xFC, + 0xDF, 0xF8, 0xA4, 0xB2, 0x47, 0xF6, 0xFF, 0x71, 0x0B, 0xEB, 0x44, 0x00, + 0x03, 0x90, 0x00, 0x20, + 0x6F, 0xF0, 0x7E, 0x02, 0x05, 0xE0, 0x00, 0xBF, 0x2B, 0xF8, 0x10, 0x10, + 0x03, 0x9B, 0x1A, 0x54, + 0x40, 0x1C, 0xA0, 0x42, 0xF8, 0xDB, 0x6F, 0xF0, 0x7F, 0x09, 0x00, 0xBF, + 0xFF, 0xF7, 0x3C, 0xFF, + 0xD8, 0xF8, 0x00, 0x00, 0x00, 0x25, 0x2E, 0x46, 0x06, 0x90, 0x2B, 0xE0, + 0x32, 0x46, 0x01, 0x20, + 0x00, 0x21, 0x12, 0xF0, 0xFB, 0xFF, 0xDD, 0xE9, 0x04, 0x23, 0x10, 0x40, + 0x19, 0x40, 0x08, 0x43, + 0x1D, 0xD0, 0x06, 0x98, 0x30, 0xF9, 0x15, 0x10, 0x3B, 0xF9, 0x15, 0x00, + 0x00, 0x29, 0x00, 0xDA, + 0x49, 0x42, 0x00, 0x28, 0x00, 0xDA, 0x40, 0x42, 0x81, 0x42, 0x0A, 0xDA, + 0x31, 0x46, 0x00, 0x98, + 0xFC, 0xF7, 0xAB, 0xFC, 0x03, 0x99, 0x48, 0x55, 0x06, 0x98, 0x30, 0xF8, + 0x15, 0x10, 0x2B, 0xF8, + 0x15, 0x10, 0x4A, 0x46, 0x31, 0x46, 0x00, 0x98, 0xFC, 0xF7, 0xA6, 0xFC, + 0x6D, 0x1C, 0xA5, 0x42, + 0x02, 0xD0, 0x76, 0x1C, 0xBE, 0x42, 0xD1, 0xDB, 0x09, 0xF1, 0x01, 0x09, + 0xB9, 0xF1, 0x7F, 0x0F, + 0xC4, 0xDD, 0x00, 0x26, 0x35, 0x46, 0x14, 0xE0, 0x2A, 0x46, 0x01, 0x20, + 0x00, 0x21, 0x12, 0xF0, + 0xC5, 0xFF, 0xDD, 0xE9, 0x04, 0x23, 0x10, 0x40, 0x19, 0x40, 0x08, 0x43, + 0x06, 0xD0, 0x03, 0x98, + 0x29, 0x46, 0x82, 0x57, 0x00, 0x98, 0xFC, 0xF7, 0x87, 0xFC, 0x76, 0x1C, + 0xA6, 0x42, 0x02, 0xD0, + 0x6D, 0x1C, 0xBD, 0x42, 0xE8, 0xDB, 0xDD, 0xE9, 0x00, 0x10, 0x02, 0x9A, + 0x12, 0xF0, 0xE5, 0xFF, + 0x00, 0x26, 0x35, 0x46, 0x14, 0xE0, 0x2A, 0x46, 0x01, 0x20, 0x00, 0x21, + 0x12, 0xF0, 0xA6, 0xFF, + 0xDD, 0xE9, 0x04, 0x23, 0x10, 0x40, 0x19, 0x40, 0x08, 0x43, 0x06, 0xD0, + 0x29, 0x46, 0x00, 0x98, + 0xFC, 0xF7, 0x63, 0xFC, 0x2A, 0xF8, 0x16, 0x00, 0x76, 0x1C, 0xA6, 0x42, + 0x02, 0xD0, 0x6D, 0x1C, + 0xBD, 0x42, 0xE8, 0xDB, 0xFF, 0xF7, 0xC8, 0xFE, 0x00, 0x20, 0xD8, 0xF8, + 0x00, 0x10, 0x05, 0xE0, + 0x31, 0xF8, 0x10, 0x30, 0x25, 0x18, 0x40, 0x1C, 0x2A, 0xF8, 0x15, 0x30, + 0xA0, 0x42, 0xF7, 0xDB, + 0x07, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, 0x70, 0xB5, 0x55, 0x4C, 0x05, 0x46, + 0x20, 0x68, 0x90, 0xF8, + 0x88, 0x01, 0xC0, 0x06, 0x07, 0xD5, 0x29, 0x46, 0x00, 0x20, 0xFF, 0xF7, + 0x05, 0xFF, 0x29, 0x46, + 0x01, 0x20, 0xFF, 0xF7, 0x01, 0xFF, 0x20, 0x68, 0x90, 0xF8, 0xA0, 0x01, + 0xC0, 0x06, 0x04, 0xD5, + 0x29, 0x46, 0xBD, 0xE8, 0x70, 0x40, 0x02, 0x20, 0xF6, 0xE6, 0x70, 0xBD, + 0x2D, 0xE9, 0xF0, 0x47, + 0x45, 0x49, 0x00, 0x25, 0x2C, 0x46, 0x0E, 0x69, 0xD1, 0xF8, 0x38, 0x30, + 0x28, 0x46, 0x01, 0x27, + 0x07, 0xFA, 0x00, 0xF2, 0x32, 0x42, 0x03, 0xD0, 0x1A, 0x5C, 0xAA, 0x42, + 0x00, 0xD9, 0x15, 0x46, + 0x40, 0x1C, 0x20, 0x28, 0xF4, 0xD3, 0xD1, 0xE9, 0x14, 0x78, 0xD1, 0xF8, + 0x3C, 0x90, 0x00, 0x26, + 0x8A, 0x46, 0x32, 0x46, 0x01, 0x20, 0x00, 0x21, 0x12, 0xF0, 0x48, 0xFF, + 0x38, 0x40, 0x01, 0xEA, + 0x08, 0x01, 0x08, 0x43, 0x04, 0xD0, 0x19, 0xF8, 0x06, 0x00, 0xA0, 0x42, + 0x00, 0xD9, 0x04, 0x46, + 0x76, 0x1C, 0x40, 0x2E, 0xED, 0xD3, 0xDA, 0xF8, 0x48, 0x20, 0x50, 0x46, + 0x15, 0x70, 0xDA, 0xF8, + 0x4C, 0x10, 0x0C, 0x70, 0xDA, 0xF8, 0x30, 0x30, 0xDA, 0xF8, 0x28, 0x70, + 0xDA, 0xF8, 0x34, 0x60, + 0x1B, 0x78, 0x3F, 0x78, 0x2B, 0x44, 0x3B, 0x44, 0xDA, 0xF8, 0x2C, 0x70, + 0x36, 0x78, 0x3F, 0x78, + 0x26, 0x44, 0x3E, 0x44, 0x07, 0x69, 0x6F, 0xB1, 0xD0, 0xE9, 0x14, 0x7C, + 0x57, 0xEA, 0x0C, 0x07, + 0x0A, 0xD0, 0xFF, 0x27, 0xB3, 0x42, 0x13, 0xD2, 0xF3, 0x1A, 0x2B, 0x44, + 0xFF, 0x2B, 0x07, 0xD8, + 0x13, 0x70, 0x0B, 0xE0, 0x14, 0x70, 0x09, 0xE0, 0x15, 0x70, 0x0D, 0x70, + 0xBD, 0xE8, 0xF0, 0x87, + 0x17, 0x70, 0xC2, 0x69, 0x42, 0xF4, 0x00, 0x72, 0xCA, 0xF8, 0x1C, 0x20, + 0x0C, 0x70, 0xF5, 0xE7, + 0x9E, 0x42, 0x0D, 0xD2, 0x9B, 0x1B, 0x23, 0x44, 0xFF, 0x2B, 0x01, 0xD8, + 0x0B, 0x70, 0x05, 0xE0, + 0x0F, 0x70, 0xC1, 0x69, 0x41, 0xF4, 0x00, 0x71, 0xCA, 0xF8, 0x1C, 0x10, + 0x15, 0x70, 0xE5, 0xE7, + 0x15, 0x70, 0xEB, 0xE7, 0x2D, 0xE9, 0xF0, 0x41, 0x1F, 0x46, 0x0D, 0x46, + 0x00, 0x2A, 0x34, 0xDD, + 0x09, 0x4C, 0x77, 0xB1, 0xB4, 0xF9, 0x0A, 0x60, 0x11, 0x46, 0x0F, 0xF0, + 0xD6, 0xFF, 0xB0, 0x42, + 0x28, 0x78, 0x27, 0xDA, 0xFF, 0x28, 0x1C, 0xD2, 0x40, 0x1C, 0x28, 0x70, + 0x00, 0x20, 0xBD, 0xE8, + 0xF0, 0x81, 0x13, 0xE0, 0xB4, 0x10, 0x10, 0x00, 0xC0, 0x04, 0x10, 0x00, + 0xF1, 0x3E, 0x00, 0x00, + 0x4C, 0x07, 0x10, 0x00, 0x50, 0x07, 0x10, 0x00, 0x0C, 0xA0, 0x01, 0x20, + 0x9C, 0x4B, 0x01, 0x20, + 0xF0, 0x4B, 0x01, 0x20, 0x88, 0x4C, 0x01, 0x20, 0xD4, 0x56, 0x10, 0x00, + 0xB4, 0xF9, 0x08, 0x60, + 0xDA, 0xE7, 0xE0, 0x69, 0x1F, 0xB1, 0x40, 0xF0, 0x02, 0x00, 0xE0, 0x61, + 0x05, 0xE0, 0x40, 0xF4, + 0x00, 0x60, 0xFA, 0xE7, 0x08, 0xB1, 0x40, 0x1E, 0x28, 0x70, 0x01, 0x20, + 0xD7, 0xE7, 0x2D, 0xE9, + 0xF0, 0x41, 0x1E, 0x46, 0x0D, 0x46, 0x00, 0x2A, 0x1A, 0xDD, 0xFE, 0x4C, + 0x66, 0xB1, 0xB4, 0xF9, + 0x0A, 0x70, 0x11, 0x46, 0x0F, 0xF0, 0x99, 0xFF, 0xB8, 0x42, 0x11, 0xDD, + 0x28, 0x78, 0x50, 0xB1, + 0x10, 0x28, 0x04, 0xD3, 0x10, 0x38, 0x03, 0xE0, 0xB4, 0xF9, 0x08, 0x70, + 0xF1, 0xE7, 0x00, 0x20, + 0x28, 0x70, 0x00, 0x20, 0xBB, 0xE7, 0xE0, 0x69, 0x26, 0xB1, 0x40, 0xF0, + 0x01, 0x00, 0xE0, 0x61, + 0x01, 0x20, 0xB4, 0xE7, 0x40, 0xF4, 0x80, 0x60, 0xF9, 0xE7, 0x2D, 0xE9, + 0xF1, 0x4F, 0x82, 0xB0, + 0x00, 0x25, 0xEC, 0x48, 0x02, 0x9A, 0x2C, 0x46, 0xFF, 0x21, 0x02, 0xB3, + 0xD0, 0xF8, 0x30, 0x90, + 0x02, 0x26, 0x4F, 0xF0, 0x18, 0x0B, 0x89, 0xF8, 0x00, 0x10, 0xD0, 0xF8, + 0x34, 0x80, 0x88, 0xF8, + 0x00, 0x10, 0xB0, 0xF9, 0x0A, 0x70, 0x31, 0x46, 0x00, 0x91, 0x41, 0x69, + 0x00, 0x29, 0x00, 0xDC, + 0x01, 0x25, 0xE0, 0x48, 0x80, 0x69, 0x00, 0x28, 0x00, 0xDC, 0x01, 0x24, + 0xFF, 0xF7, 0x06, 0xFF, + 0xFF, 0xF7, 0xA2, 0xFD, 0x00, 0x98, 0x00, 0xF0, 0xFD, 0xFC, 0x85, 0xB1, + 0x21, 0xE0, 0xD0, 0xF8, + 0x28, 0x90, 0x4F, 0xF4, 0x00, 0x66, 0x4F, 0xF0, 0x18, 0x0B, 0x89, 0xF8, + 0x00, 0x10, 0xD0, 0xF8, + 0x2C, 0x80, 0x88, 0xF8, 0x00, 0x10, 0xB0, 0xF9, 0x08, 0x70, 0x01, 0x21, + 0xDC, 0xE7, 0xDF, 0xF8, + 0x44, 0xA3, 0x3A, 0x46, 0xDA, 0xF8, 0x20, 0x00, 0xDA, 0xF8, 0x14, 0x10, + 0x00, 0x68, 0x00, 0xF0, + 0xF6, 0xFC, 0x01, 0x28, 0x05, 0xD1, 0xDA, 0xF8, 0x1C, 0x10, 0x31, 0x43, + 0x01, 0x25, 0xCA, 0xF8, + 0x1C, 0x10, 0x8C, 0xB9, 0xDF, 0xF8, 0x1C, 0xA3, 0x3A, 0x46, 0xDA, 0xF8, + 0x24, 0x00, 0xDA, 0xF8, + 0x18, 0x10, 0x00, 0x68, 0x00, 0xF0, 0xE3, 0xFC, 0x01, 0x28, 0x05, 0xD1, + 0xDA, 0xF8, 0x1C, 0x10, + 0x31, 0x43, 0x01, 0x24, 0xCA, 0xF8, 0x1C, 0x10, 0x0D, 0xB1, 0x00, 0x2C, + 0x4E, 0xD1, 0x2F, 0x46, + 0x26, 0x46, 0x4F, 0xF0, 0x17, 0x0A, 0x4F, 0xB9, 0xBA, 0x48, 0x02, 0x9B, + 0x01, 0x6A, 0x42, 0x69, + 0x0F, 0x68, 0x49, 0x46, 0x38, 0x46, 0xFF, 0xF7, 0x6A, 0xFF, 0x07, 0x46, + 0x4E, 0xB9, 0xB5, 0x48, + 0x02, 0x9B, 0x41, 0x6A, 0x82, 0x69, 0x0E, 0x68, 0x41, 0x46, 0x30, 0x46, + 0xFF, 0xF7, 0x5F, 0xFF, + 0x06, 0x46, 0xFF, 0xF7, 0xAB, 0xFE, 0xFF, 0xF7, 0x47, 0xFD, 0x00, 0x98, + 0x00, 0xF0, 0xA2, 0xFC, + 0x07, 0xB1, 0x16, 0xB9, 0xBA, 0xF1, 0x01, 0x0A, 0xDD, 0xD2, 0x2F, 0x46, + 0xA9, 0x4D, 0xAB, 0xF1, + 0x01, 0x06, 0x3F, 0xB9, 0x28, 0x6A, 0x02, 0x9B, 0x49, 0x46, 0x00, 0x68, + 0x6A, 0x69, 0xFF, 0xF7, + 0x09, 0xFF, 0x07, 0x46, 0x3C, 0xB9, 0x68, 0x6A, 0x02, 0x9B, 0x41, 0x46, + 0x00, 0x68, 0xAA, 0x69, + 0xFF, 0xF7, 0x00, 0xFF, 0x04, 0x46, 0xFF, 0xF7, 0x89, 0xFE, 0xFF, 0xF7, + 0x25, 0xFD, 0x00, 0x98, + 0x00, 0xF0, 0x80, 0xFC, 0x07, 0xB1, 0x0C, 0xB9, 0x76, 0x1E, 0xE2, 0xD2, + 0x01, 0x2F, 0x05, 0xD1, + 0x01, 0x2C, 0x03, 0xD1, 0xE8, 0x69, 0x20, 0xF4, 0x00, 0x70, 0xE8, 0x61, + 0xBD, 0xE8, 0xFE, 0x8F, + 0x2D, 0xE9, 0xF0, 0x4F, 0x9A, 0x46, 0x91, 0x46, 0x00, 0x22, 0x92, 0x4B, + 0x83, 0x46, 0x14, 0x46, + 0xB3, 0xF9, 0x0C, 0x70, 0x10, 0x46, 0x01, 0x23, 0x90, 0x46, 0x14, 0xE0, + 0x09, 0xEB, 0xE4, 0x05, + 0x04, 0xF0, 0x07, 0x0C, 0x2E, 0x78, 0x03, 0xFA, 0x0C, 0xF5, 0x35, 0x42, + 0x0A, 0xD0, 0x3B, 0xF9, + 0x12, 0x50, 0xBD, 0x42, 0x03, 0xDC, 0x0A, 0xF8, 0x02, 0x30, 0x01, 0x20, + 0x01, 0xE0, 0x0A, 0xF8, + 0x02, 0x80, 0x52, 0x1C, 0x64, 0x1C, 0x8A, 0x42, 0xE8, 0xDB, 0x32, 0xE6, + 0x2D, 0xE9, 0xFF, 0x5F, + 0x00, 0x24, 0x4F, 0xF0, 0x01, 0x08, 0x9A, 0x46, 0x17, 0x46, 0x25, 0x46, + 0xDF, 0xF8, 0xF4, 0x91, + 0x46, 0x46, 0xDD, 0xF8, 0x3C, 0xB0, 0x4E, 0xE0, 0x0A, 0xEB, 0xE5, 0x00, + 0x05, 0xF0, 0x07, 0x01, + 0x00, 0x78, 0x06, 0xFA, 0x01, 0xF2, 0x02, 0x42, 0x44, 0xD0, 0x1B, 0xF8, + 0x04, 0x00, 0x01, 0x28, + 0x3F, 0xD0, 0x00, 0x98, 0xB9, 0xF9, 0x0C, 0x10, 0x30, 0xF9, 0x14, 0x20, + 0x8A, 0x42, 0x1D, 0xDA, + 0x29, 0x46, 0x01, 0x98, 0xFC, 0xF7, 0x87, 0xFA, 0xFF, 0x28, 0x0E, 0xDA, + 0x40, 0x1C, 0xC2, 0xB2, + 0x4F, 0xF0, 0x00, 0x08, 0x29, 0x46, 0x01, 0x98, 0xFC, 0xF7, 0x7F, 0xFA, + 0x00, 0x98, 0x30, 0xF8, + 0x14, 0x10, 0x0E, 0x98, 0x20, 0xF8, 0x14, 0x10, 0x23, 0xE0, 0x0B, 0xF8, + 0x04, 0x60, 0xD9, 0xF8, + 0x1C, 0x10, 0x41, 0xF0, 0x08, 0x01, 0xC9, 0xF8, 0x1C, 0x10, 0x1A, 0xE0, + 0x0E, 0x98, 0x30, 0xF9, + 0x14, 0x00, 0x00, 0x28, 0x13, 0xDD, 0x40, 0x1A, 0xA2, 0xEB, 0x01, 0x01, + 0x00, 0xD5, 0x40, 0x42, + 0x00, 0x29, 0x00, 0xDA, 0x49, 0x42, 0x88, 0x42, 0x09, 0xDC, 0x29, 0x46, + 0x01, 0x98, 0xFC, 0xF7, + 0x5A, 0xFA, 0x40, 0x1E, 0xC2, 0xB2, 0x29, 0x46, 0x01, 0x98, 0xFC, 0xF7, + 0x56, 0xFA, 0x0B, 0xF8, + 0x04, 0x60, 0x64, 0x1C, 0x6D, 0x1C, 0xBC, 0x42, 0xAE, 0xDB, 0x40, 0x46, + 0x04, 0xB0, 0xBD, 0xE8, + 0xF0, 0x9F, 0x2D, 0xE9, 0xFF, 0x5F, 0x00, 0x24, 0x4F, 0xF0, 0x01, 0x08, + 0x9A, 0x46, 0x17, 0x46, + 0x25, 0x46, 0x46, 0x46, 0xDF, 0xF8, 0x2C, 0x91, 0xDD, 0xF8, 0x38, 0xB0, + 0x31, 0xE0, 0x00, 0xBF, + 0x0A, 0xEB, 0xE5, 0x00, 0x05, 0xF0, 0x07, 0x02, 0x01, 0x78, 0x06, 0xFA, + 0x02, 0xF0, 0x08, 0x42, + 0x26, 0xD0, 0x1B, 0xF8, 0x04, 0x00, 0x01, 0x28, 0x21, 0xD0, 0x00, 0x98, + 0x30, 0xF9, 0x14, 0x10, + 0xB9, 0xF9, 0x0C, 0x00, 0x81, 0x42, 0x1A, 0xDD, 0x29, 0x46, 0x01, 0x98, + 0xFC, 0xF7, 0x23, 0xFA, + 0x00, 0x28, 0x0C, 0xDD, 0x4F, 0xF0, 0x00, 0x08, 0x04, 0x28, 0x01, 0xDB, + 0x00, 0x1F, 0x00, 0xE0, + 0x00, 0x20, 0xC2, 0xB2, 0x29, 0x46, 0x01, 0x98, 0xFC, 0xF7, 0x17, 0xFA, + 0x07, 0xE0, 0x0B, 0xF8, + 0x04, 0x60, 0xD9, 0xF8, 0x1C, 0x10, 0x41, 0xF0, 0x04, 0x01, 0xC9, 0xF8, + 0x1C, 0x10, 0x64, 0x1C, + 0x6D, 0x1C, 0xBC, 0x42, 0xCC, 0xDB, 0x40, 0x46, 0xB8, 0xE7, 0x2D, 0xE9, + 0xF0, 0x4F, 0x93, 0xB0, + 0x00, 0x24, 0x25, 0x46, 0x18, 0x21, 0x0C, 0xA8, 0x12, 0xF0, 0xE0, 0xFD, + 0x28, 0x21, 0x02, 0xA8, + 0x12, 0xF0, 0xDC, 0xFD, 0x27, 0x48, 0x41, 0x69, 0x00, 0x29, 0x00, 0xDC, + 0x01, 0x24, 0x82, 0x69, + 0x00, 0x2A, 0x00, 0xDC, 0x01, 0x25, 0x23, 0x4A, 0x81, 0x46, 0x06, 0x46, + 0xFF, 0x23, 0x10, 0x32, + 0x80, 0x6B, 0x00, 0xF0, 0xA6, 0xFB, 0x1F, 0x4A, 0xFF, 0x23, 0x50, 0x32, + 0xB1, 0x69, 0xF0, 0x6B, + 0x00, 0xF0, 0x9F, 0xFB, 0xFF, 0xF7, 0x82, 0xFD, 0xFF, 0xF7, 0x1E, 0xFC, + 0x04, 0x20, 0x00, 0xF0, + 0x79, 0xFB, 0x8C, 0xB1, 0xD9, 0xF8, 0x20, 0x00, 0x16, 0x4A, 0x4E, 0x46, + 0x00, 0x68, 0x0C, 0xAB, + 0x10, 0x32, 0xD9, 0xF8, 0x14, 0x10, 0xFF, 0xF7, 0xFB, 0xFE, 0x01, 0x28, + 0x04, 0xD1, 0xF0, 0x69, + 0x01, 0x24, 0x40, 0xF0, 0x08, 0x00, 0xF0, 0x61, 0x8D, 0xB1, 0xD9, 0xF8, + 0x24, 0x00, 0x0D, 0x4A, + 0x4E, 0x46, 0x00, 0x68, 0x02, 0xAB, 0x50, 0x32, 0xD9, 0xF8, 0x18, 0x10, + 0xFF, 0xF7, 0xE8, 0xFE, + 0x01, 0x28, 0x04, 0xD1, 0xF0, 0x69, 0x01, 0x25, 0x40, 0xF0, 0x08, 0x00, + 0xF0, 0x61, 0x0C, 0xB1, + 0x00, 0x2D, 0x7A, 0xD1, 0x26, 0x46, 0xA8, 0x46, 0x00, 0x27, 0x0D, 0xF1, + 0x30, 0x0B, 0x86, 0xB9, + 0x02, 0xE0, 0x00, 0x00, 0xC0, 0x04, 0x10, 0x00, 0xCD, 0xF8, 0x00, 0xB0, + 0xD9, 0xF8, 0x20, 0x00, + 0xFE, 0x4B, 0xD9, 0xF8, 0x14, 0x20, 0x00, 0x68, 0xD9, 0xF8, 0x38, 0x10, + 0xFF, 0xF7, 0x51, 0xFF, + 0x06, 0x46, 0xB8, 0xF1, 0x00, 0x0F, 0x0D, 0xD1, 0x02, 0xA8, 0x00, 0x90, + 0xD9, 0xF8, 0x24, 0x10, + 0xF6, 0x4B, 0xD9, 0xF8, 0x18, 0x20, 0x08, 0x68, 0x40, 0x33, 0xD9, 0xF8, + 0x3C, 0x10, 0xFF, 0xF7, + 0x40, 0xFF, 0x80, 0x46, 0xFF, 0xF7, 0x2A, 0xFD, 0xFF, 0xF7, 0xC6, 0xFB, + 0x04, 0x20, 0x00, 0xF0, + 0x21, 0xFB, 0x16, 0xB1, 0xB8, 0xF1, 0x00, 0x0F, 0x02, 0xD1, 0x7F, 0x1C, + 0x60, 0x2F, 0xCE, 0xDB, + 0xDF, 0xF8, 0xAC, 0xA3, 0xD9, 0xF8, 0x14, 0x00, 0x26, 0x46, 0x41, 0x00, + 0x0A, 0xF5, 0xE6, 0x68, + 0x4C, 0x46, 0x50, 0x46, 0x12, 0xF0, 0x30, 0xFD, 0xA0, 0x69, 0x41, 0x00, + 0x40, 0x46, 0x12, 0xF0, + 0x2B, 0xFD, 0x00, 0x27, 0xFF, 0xF7, 0xA8, 0xFB, 0x04, 0x20, 0x00, 0xF0, + 0x03, 0xFB, 0x4E, 0xB9, + 0xCD, 0xE9, 0x00, 0xAB, 0x20, 0x6A, 0xDD, 0x4B, 0x62, 0x69, 0x00, 0x68, + 0xA1, 0x6B, 0xFF, 0xF7, + 0xAD, 0xFE, 0x06, 0x46, 0x5D, 0xB9, 0x02, 0xA8, 0xCD, 0xE9, 0x00, 0x80, + 0x60, 0x6A, 0xD7, 0x4B, + 0xA2, 0x69, 0x00, 0x68, 0x40, 0x33, 0xE1, 0x6B, 0xFF, 0xF7, 0xA0, 0xFE, + 0x05, 0x46, 0xFF, 0xF7, + 0xED, 0xFC, 0xFF, 0xF7, 0x89, 0xFB, 0x04, 0x20, 0x00, 0xF0, 0xE4, 0xFA, + 0x06, 0xB1, 0x15, 0xB9, + 0x7F, 0x1C, 0x60, 0x2F, 0xD6, 0xDB, 0x01, 0x2E, 0x07, 0xD1, 0x01, 0x2D, + 0x05, 0xD1, 0xE0, 0x69, + 0x81, 0x05, 0x02, 0xD5, 0x20, 0xF4, 0x00, 0x70, 0xE0, 0x61, 0x13, 0xB0, + 0xB9, 0xE4, 0x2D, 0xE9, + 0xF0, 0x41, 0x0F, 0x46, 0xC5, 0x49, 0x16, 0x46, 0x02, 0x46, 0x10, 0x39, + 0xDD, 0xE9, 0x06, 0x05, + 0xB1, 0xF9, 0x0E, 0xC0, 0x01, 0x24, 0x62, 0x45, 0x11, 0xDD, 0x1D, 0x68, + 0xA0, 0xF1, 0x7F, 0x02, + 0x95, 0x42, 0x07, 0xDD, 0x2A, 0x1A, 0x31, 0x46, 0x38, 0x46, 0x1A, 0x60, + 0xFC, 0xF7, 0xF4, 0xF8, + 0x00, 0x24, 0x11, 0xE0, 0xC8, 0x69, 0x40, 0xF0, 0x40, 0x00, 0xC8, 0x61, + 0x0C, 0xE0, 0x01, 0x21, + 0x29, 0x70, 0x19, 0x68, 0xC0, 0xF1, 0x7F, 0x02, 0x91, 0x42, 0x05, 0xDA, + 0x0A, 0x18, 0x31, 0x46, + 0x38, 0x46, 0x1A, 0x60, 0xFC, 0xF7, 0xE0, 0xF8, 0x20, 0x46, 0x30, 0xE5, + 0x2D, 0xE9, 0xFF, 0x4F, + 0x00, 0x24, 0x85, 0xB0, 0x91, 0x46, 0x06, 0x46, 0x25, 0x46, 0xA2, 0x46, + 0x78, 0xE0, 0x08, 0x98, + 0x05, 0xF0, 0x07, 0x03, 0x00, 0xEB, 0xE5, 0x00, 0x7F, 0x22, 0x01, 0x78, + 0x01, 0x20, 0x98, 0x40, + 0x08, 0x42, 0x03, 0x92, 0x6B, 0xD0, 0x29, 0x46, 0x06, 0x98, 0xFC, 0xF7, + 0xC5, 0xF8, 0x00, 0x20, + 0x8D, 0xF8, 0x08, 0x00, 0x80, 0x46, 0x4F, 0xF0, 0x10, 0x0B, 0x00, 0xBF, + 0xFF, 0xF7, 0x24, 0xFB, + 0x08, 0x20, 0x00, 0xF0, 0x7F, 0xFA, 0x02, 0xAF, 0xCD, 0xE9, 0x00, 0xB7, + 0x36, 0xF9, 0x14, 0x00, + 0x03, 0xAB, 0x2A, 0x46, 0x06, 0x99, 0xFF, 0xF7, 0xA2, 0xFF, 0x20, 0xB9, + 0x08, 0xF1, 0x01, 0x08, + 0xB8, 0xF1, 0x18, 0x0F, 0xEA, 0xDB, 0x9D, 0xF8, 0x08, 0x00, 0xE0, 0xB3, + 0x00, 0x20, 0x8D, 0xF8, + 0x08, 0x00, 0x80, 0x46, 0x4F, 0xF0, 0x04, 0x0B, 0xFF, 0xF7, 0x06, 0xFB, + 0x08, 0x20, 0x00, 0xF0, + 0x61, 0xFA, 0xCD, 0xE9, 0x00, 0xB7, 0x36, 0xF9, 0x14, 0x00, 0x03, 0xAB, + 0x2A, 0x46, 0x06, 0x99, + 0xFF, 0xF7, 0x85, 0xFF, 0x20, 0xB9, 0x08, 0xF1, 0x01, 0x08, 0xB8, 0xF1, + 0x18, 0x0F, 0xEB, 0xDB, + 0x9D, 0xF8, 0x08, 0x00, 0xF8, 0xB1, 0x00, 0x20, 0x8D, 0xF8, 0x08, 0x00, + 0x80, 0x46, 0x4F, 0xF0, + 0x01, 0x0B, 0x00, 0xBF, 0xFF, 0xF7, 0xE8, 0xFA, 0x08, 0x20, 0x00, 0xF0, + 0x43, 0xFA, 0xCD, 0xE9, + 0x00, 0xB7, 0x36, 0xF9, 0x14, 0x00, 0x03, 0xAB, 0x2A, 0x46, 0x06, 0x99, + 0xFF, 0xF7, 0x67, 0xFF, + 0x78, 0xB1, 0x9D, 0xF8, 0x08, 0x00, 0x01, 0x28, 0x10, 0xD0, 0x03, 0x9A, + 0x29, 0x46, 0x52, 0x1E, + 0x03, 0x92, 0x06, 0x98, 0x00, 0xE0, 0x09, 0xE0, 0xFC, 0xF7, 0x66, 0xF8, + 0x0A, 0xF1, 0x01, 0x0A, + 0x04, 0xE0, 0x08, 0xF1, 0x01, 0x08, 0xB8, 0xF1, 0x18, 0x0F, 0xDB, 0xDB, + 0x64, 0x1C, 0x6D, 0x1C, + 0x4C, 0x45, 0x84, 0xDB, 0xCA, 0x45, 0x02, 0xD0, 0x00, 0x20, 0x09, 0xB0, + 0xCD, 0xE5, 0x01, 0x20, + 0xFB, 0xE7, 0x70, 0xB5, 0x69, 0x4C, 0x01, 0x25, 0x10, 0x3C, 0xA2, 0x69, + 0x00, 0x2A, 0x08, 0xDD, + 0x60, 0x6A, 0x04, 0xF1, 0x50, 0x03, 0x61, 0x6C, 0x00, 0x68, 0xFF, 0xF7, + 0x67, 0xFF, 0x00, 0xF0, + 0x01, 0x05, 0x62, 0x69, 0x00, 0x2A, 0x06, 0xDD, 0x20, 0x6A, 0x60, 0x4B, + 0x21, 0x6C, 0x00, 0x68, + 0xFF, 0xF7, 0x5C, 0xFF, 0x05, 0x40, 0x28, 0x46, 0x70, 0xBD, 0x70, 0xB5, + 0x5B, 0x4C, 0x00, 0x26, + 0x10, 0x3C, 0x05, 0x46, 0xE6, 0x61, 0x01, 0x28, 0x5B, 0xD0, 0x02, 0x2D, + 0x5B, 0xD0, 0x01, 0x20, + 0x20, 0x70, 0x60, 0x79, 0x60, 0xB1, 0x00, 0x23, 0x54, 0x4A, 0x61, 0x69, + 0xA0, 0x6B, 0x00, 0xF0, + 0x08, 0xFA, 0x52, 0x4A, 0x00, 0x23, 0x40, 0x32, 0xA1, 0x69, 0xE0, 0x6B, + 0x00, 0xF0, 0x01, 0xFA, + 0xA0, 0x79, 0x50, 0xB1, 0x4D, 0x49, 0x62, 0x69, 0x20, 0x6C, 0xFC, 0xF7, + 0x20, 0xF8, 0x4B, 0x49, + 0xA2, 0x69, 0x40, 0x31, 0x60, 0x6C, 0xFC, 0xF7, 0x1A, 0xF8, 0xE0, 0x78, + 0x10, 0xB1, 0x00, 0x20, + 0xFF, 0xF7, 0xAB, 0xFC, 0x20, 0x79, 0x60, 0xB1, 0xE0, 0x78, 0x38, 0xB1, + 0x61, 0x78, 0xA0, 0x6A, + 0x00, 0xF0, 0xD5, 0xF9, 0x61, 0x78, 0xE0, 0x6A, 0x00, 0xF0, 0xD1, 0xF9, + 0x01, 0x20, 0xFF, 0xF7, + 0x9C, 0xFC, 0x60, 0x79, 0x08, 0xB1, 0xFF, 0xF7, 0x18, 0xFE, 0x01, 0x2D, + 0x25, 0xD0, 0x02, 0x2D, + 0x25, 0xD0, 0x26, 0x70, 0xFF, 0xF7, 0x58, 0xFA, 0xA0, 0x79, 0x60, 0xB1, + 0x20, 0x79, 0x38, 0xB1, + 0xA1, 0x78, 0x20, 0x6B, 0x00, 0xF0, 0xBB, 0xF9, 0xA1, 0x78, 0x60, 0x6B, + 0x00, 0xF0, 0xB7, 0xF9, + 0xE0, 0x79, 0xFF, 0xF7, 0x8E, 0xFF, 0xFF, 0xF7, 0x47, 0xFA, 0x01, 0x2D, + 0x12, 0xD0, 0x02, 0x2D, + 0x12, 0xD0, 0x10, 0x21, 0xE0, 0x69, 0x00, 0xF0, 0xD1, 0xF9, 0xE0, 0x69, + 0x78, 0xB1, 0x00, 0x20, + 0x70, 0xBD, 0x03, 0x20, 0xA4, 0xE7, 0x05, 0x20, 0xA2, 0xE7, 0x02, 0x20, + 0x00, 0xE0, 0x04, 0x20, + 0x20, 0x70, 0xD7, 0xE7, 0x40, 0x21, 0xED, 0xE7, 0x4F, 0xF4, 0x00, 0x71, + 0xEA, 0xE7, 0x01, 0x20, + 0x70, 0xBD, 0x70, 0xB5, 0x21, 0x4D, 0x0C, 0x46, 0x10, 0x3D, 0x02, 0x28, + 0x26, 0xD0, 0x11, 0xF0, + 0xE6, 0xFF, 0x28, 0x61, 0x11, 0xF0, 0xE5, 0xFF, 0xC5, 0xE9, 0x14, 0x01, + 0x04, 0x21, 0x1B, 0x48, + 0xFC, 0xF7, 0x71, 0xFB, 0x68, 0x61, 0x19, 0x48, 0x08, 0x21, 0x40, 0x30, + 0xFC, 0xF7, 0x6B, 0xFB, + 0xA8, 0x61, 0x04, 0xF1, 0x90, 0x00, 0xA8, 0x62, 0x40, 0x1C, 0xE8, 0x62, + 0x40, 0x1C, 0x28, 0x63, + 0x40, 0x1C, 0x68, 0x63, 0x4B, 0x38, 0xA8, 0x63, 0x20, 0x30, 0xC5, 0xE9, + 0x0F, 0x04, 0x48, 0x38, + 0x68, 0x64, 0x74, 0x30, 0x95, 0x34, 0xC5, 0xE9, 0x12, 0x04, 0x70, 0xBD, + 0x11, 0xF0, 0xC7, 0xFF, + 0x28, 0x61, 0x11, 0xF0, 0xC6, 0xFF, 0xD7, 0xE7, 0x10, 0xB5, 0x0A, 0x49, + 0x00, 0x20, 0xFF, 0xF7, + 0xC8, 0xFF, 0x06, 0x48, 0x08, 0x49, 0x10, 0x38, 0x08, 0x4A, 0x01, 0x62, + 0x09, 0x1D, 0x41, 0x62, + 0x13, 0x68, 0x93, 0xF8, 0x88, 0x11, 0x11, 0xF0, 0x01, 0x04, 0x09, 0xE0, + 0xD0, 0x04, 0x10, 0x00, + 0xD4, 0x56, 0x10, 0x00, 0x38, 0x99, 0x01, 0x20, 0xCC, 0x10, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, + 0xC4, 0x70, 0x4F, 0xF0, 0x00, 0x01, 0x04, 0xD0, 0x83, 0x6A, 0x19, 0x70, + 0xC3, 0x6A, 0x19, 0x70, + 0x08, 0xE0, 0x84, 0x6A, 0x93, 0xF8, 0x92, 0x31, 0x23, 0x70, 0x13, 0x68, + 0xC4, 0x6A, 0x93, 0xF8, + 0x93, 0x31, 0x23, 0x70, 0x13, 0x68, 0x93, 0xF8, 0x88, 0x41, 0xC4, 0xF3, + 0x40, 0x04, 0x04, 0x71, + 0x14, 0xB1, 0x03, 0x6B, 0x19, 0x70, 0x06, 0xE0, 0x93, 0xF8, 0x94, 0x11, + 0x03, 0x6B, 0x19, 0x70, + 0x11, 0x68, 0x91, 0xF8, 0x95, 0x11, 0x43, 0x6B, 0x19, 0x70, 0x11, 0x68, + 0x91, 0xF8, 0x88, 0x21, + 0xC2, 0xF3, 0x80, 0x03, 0x43, 0x71, 0xC2, 0xF3, 0xC0, 0x02, 0x82, 0x71, + 0xB1, 0xF8, 0x8A, 0x21, + 0x02, 0x81, 0xB1, 0xF8, 0x8C, 0x21, 0x42, 0x81, 0xB1, 0xF8, 0x8E, 0x21, + 0x82, 0x81, 0xB1, 0xF8, + 0x90, 0x21, 0xC2, 0x81, 0x91, 0xF8, 0x96, 0x21, 0x42, 0x70, 0x91, 0xF8, + 0x97, 0x21, 0x82, 0x70, + 0x91, 0xF8, 0x34, 0x11, 0xC1, 0xF3, 0x40, 0x01, 0xC1, 0x71, 0xBD, 0xE8, + 0x10, 0x40, 0x00, 0x20, + 0xF3, 0xE6, 0x10, 0xB5, 0xAC, 0x49, 0x01, 0x20, 0xFF, 0xF7, 0x63, 0xFF, + 0xAC, 0x48, 0xAB, 0x49, + 0x01, 0x62, 0x00, 0x21, 0xC0, 0xE9, 0x14, 0x11, 0x0A, 0x46, 0x81, 0x61, + 0xA9, 0x49, 0x0B, 0x68, + 0x93, 0xF8, 0xA0, 0x41, 0x14, 0xF0, 0x01, 0x04, 0xC4, 0x70, 0x04, 0xD0, + 0x83, 0x6A, 0x1A, 0x70, + 0xC3, 0x6A, 0x1A, 0x70, 0x08, 0xE0, 0x84, 0x6A, 0x93, 0xF8, 0xAA, 0x31, + 0x23, 0x70, 0x0B, 0x68, + 0xC4, 0x6A, 0x93, 0xF8, 0xAB, 0x31, 0x23, 0x70, 0x0B, 0x68, 0x93, 0xF8, + 0xA0, 0x41, 0xC4, 0xF3, + 0x40, 0x04, 0x04, 0x71, 0x14, 0xB1, 0x03, 0x6B, 0x1A, 0x70, 0x06, 0xE0, + 0x93, 0xF8, 0xAC, 0x21, + 0x03, 0x6B, 0x1A, 0x70, 0x0A, 0x68, 0x92, 0xF8, 0xAD, 0x21, 0x43, 0x6B, + 0x1A, 0x70, 0x09, 0x68, + 0x91, 0xF8, 0xA0, 0x21, 0xC2, 0xF3, 0x80, 0x03, 0x43, 0x71, 0xC2, 0xF3, + 0xC0, 0x02, 0x82, 0x71, + 0xB1, 0xF8, 0xA2, 0x21, 0x02, 0x81, 0xB1, 0xF8, 0xA4, 0x21, 0x42, 0x81, + 0xB1, 0xF8, 0xA6, 0x21, + 0x82, 0x81, 0xB1, 0xF8, 0xA8, 0x21, 0xC2, 0x81, 0x91, 0xF8, 0xAE, 0x21, + 0x42, 0x70, 0x91, 0xF8, + 0xAF, 0x21, 0x82, 0x70, 0x91, 0xF8, 0x84, 0x19, 0xC1, 0xF3, 0x40, 0x01, + 0xC1, 0x71, 0xBD, 0xE8, + 0x10, 0x40, 0x01, 0x20, 0x99, 0xE6, 0x80, 0x49, 0x10, 0xB5, 0x98, 0x31, + 0x02, 0x20, 0xFF, 0xF7, + 0x08, 0xFF, 0x7E, 0x49, 0x7E, 0x48, 0x09, 0x1D, 0x01, 0x62, 0x7E, 0x49, + 0x0B, 0x68, 0x93, 0xF8, + 0xB0, 0x21, 0x12, 0xF0, 0x01, 0x04, 0xC4, 0x70, 0x4F, 0xF0, 0x00, 0x02, + 0x04, 0xD0, 0x83, 0x6A, + 0x1A, 0x70, 0xC3, 0x6A, 0x1A, 0x70, 0x08, 0xE0, 0x84, 0x6A, 0x93, 0xF8, + 0xBA, 0x31, 0x23, 0x70, + 0x0B, 0x68, 0xC4, 0x6A, 0x93, 0xF8, 0xBB, 0x31, 0x23, 0x70, 0x0C, 0x68, + 0x94, 0xF8, 0xB0, 0x31, + 0xC3, 0xF3, 0x40, 0x03, 0x03, 0x71, 0x13, 0xB1, 0x03, 0x6B, 0x1A, 0x70, + 0x06, 0xE0, 0x03, 0x6B, + 0x94, 0xF8, 0xBC, 0x21, 0x1A, 0x70, 0x0A, 0x68, 0x92, 0xF8, 0xBD, 0x21, + 0x43, 0x6B, 0x1A, 0x70, + 0x09, 0x68, 0x91, 0xF8, 0xB0, 0x21, 0xC2, 0xF3, 0x80, 0x03, 0x43, 0x71, + 0xC2, 0xF3, 0xC0, 0x02, + 0x82, 0x71, 0xB1, 0xF8, 0xB2, 0x21, 0x02, 0x81, 0xB1, 0xF8, 0xB4, 0x21, + 0x42, 0x81, 0xB1, 0xF8, + 0xB6, 0x21, 0x82, 0x81, 0xB1, 0xF8, 0xB8, 0x21, 0xC2, 0x81, 0x91, 0xF8, + 0xBE, 0x21, 0x42, 0x70, + 0x91, 0xF8, 0xBF, 0x11, 0x81, 0x70, 0x01, 0x21, 0xC1, 0x71, 0xBD, 0xE8, + 0x10, 0x40, 0x02, 0x20, + 0x43, 0xE6, 0x2D, 0xE9, 0xF0, 0x41, 0x07, 0x46, 0x01, 0x24, 0x00, 0x20, + 0xFB, 0xF7, 0xC9, 0xFC, + 0x02, 0x20, 0xFB, 0xF7, 0xC6, 0xFC, 0x54, 0x49, 0x01, 0x20, 0x08, 0x70, + 0x53, 0x4E, 0x78, 0x07, + 0x4F, 0xF0, 0x00, 0x05, 0x4F, 0xF0, 0xA5, 0x08, 0x0A, 0xD5, 0xFF, 0xF7, + 0xD5, 0xFE, 0x10, 0xF0, + 0x01, 0x04, 0x05, 0xD0, 0x30, 0x68, 0xC5, 0x77, 0x80, 0xF8, 0x20, 0x50, + 0x80, 0xF8, 0x1E, 0x80, + 0x38, 0x07, 0x0A, 0xD5, 0xFF, 0xF7, 0x2D, 0xFF, 0x20, 0x40, 0x04, 0x00, + 0x05, 0xD0, 0x30, 0x68, + 0x00, 0xF8, 0x22, 0x5F, 0x45, 0x70, 0x00, 0xF8, 0x01, 0x8C, 0x38, 0x06, + 0x0A, 0xD5, 0xFF, 0xF7, + 0x7A, 0xFF, 0x20, 0x40, 0x04, 0x00, 0x05, 0xD0, 0x30, 0x68, 0x00, 0xF8, + 0x27, 0x5F, 0x45, 0x70, + 0x00, 0xF8, 0x01, 0x8C, 0x02, 0x20, 0xFB, 0xF7, 0xAF, 0xFC, 0x00, 0x20, + 0xFB, 0xF7, 0xAC, 0xFC, + 0x20, 0x46, 0x5A, 0xE5, 0x3A, 0x49, 0x4A, 0x78, 0x02, 0x42, 0x07, 0xD0, + 0x08, 0x78, 0xAA, 0x28, + 0x04, 0xD1, 0x08, 0x78, 0xAA, 0x28, 0xFC, 0xD0, 0xAA, 0x20, 0x08, 0x70, + 0x70, 0x47, 0x02, 0x78, + 0x8A, 0x42, 0x01, 0xD9, 0x51, 0x1A, 0x00, 0xE0, 0x00, 0x21, 0x01, 0x70, + 0x70, 0x47, 0x10, 0xB5, + 0x14, 0x46, 0x0F, 0xF0, 0x42, 0xFA, 0xA0, 0x42, 0x01, 0xDC, 0x01, 0x20, + 0x10, 0xBD, 0x00, 0x20, + 0x10, 0xBD, 0xF0, 0xB5, 0x00, 0x25, 0x2C, 0x46, 0x01, 0x26, 0x0C, 0xE0, + 0x02, 0xEB, 0xE4, 0x07, + 0x04, 0xF0, 0x07, 0x0C, 0x3F, 0x78, 0x06, 0xFA, 0x0C, 0xFE, 0x1E, 0xEA, + 0x07, 0x0F, 0x01, 0xD0, + 0x03, 0x55, 0x6D, 0x1C, 0x64, 0x1C, 0x8D, 0x42, 0xF0, 0xDB, 0xF0, 0xBD, + 0x70, 0xB5, 0x04, 0x46, + 0x0D, 0x46, 0xC0, 0x07, 0x02, 0xD0, 0x83, 0x20, 0x0D, 0xF0, 0x78, 0xFB, + 0xA0, 0x07, 0x03, 0xD5, + 0x29, 0x46, 0x84, 0x20, 0x0D, 0xF0, 0x72, 0xFB, 0x60, 0x07, 0x03, 0xD5, + 0x29, 0x46, 0x85, 0x20, + 0x0D, 0xF0, 0x6C, 0xFB, 0x20, 0x07, 0x03, 0xD5, 0x29, 0x46, 0x86, 0x20, + 0x0D, 0xF0, 0x66, 0xFB, + 0x60, 0x06, 0x03, 0xD5, 0x29, 0x46, 0x8B, 0x20, 0x0D, 0xF0, 0x60, 0xFB, + 0x20, 0x06, 0x03, 0xD5, + 0x29, 0x46, 0x8C, 0x20, 0x0D, 0xF0, 0x5A, 0xFB, 0xE0, 0x05, 0x03, 0xD5, + 0x29, 0x46, 0x87, 0x20, + 0x0D, 0xF0, 0x54, 0xFB, 0xA0, 0x05, 0x05, 0xD5, 0x29, 0x46, 0xBD, 0xE8, + 0x70, 0x40, 0x88, 0x20, + 0x0D, 0xF0, 0x4C, 0xBB, 0x70, 0xBD, 0x00, 0x00, 0xD0, 0x99, 0x01, 0x20, + 0xDC, 0x10, 0x10, 0x00, + 0xC0, 0x04, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, 0x78, 0x07, 0x10, 0x00, + 0x50, 0x07, 0x10, 0x00, + 0x08, 0x00, 0x00, 0x20, 0x08, 0xB5, 0x4E, 0x4E, 0x4E, 0x4D, 0x00, 0x24, + 0x00, 0x22, 0x6B, 0x46, + 0x31, 0x46, 0x10, 0x46, 0xFB, 0xF7, 0x76, 0xFB, 0x00, 0x98, 0x81, 0x07, + 0x32, 0xD5, 0xFB, 0xF7, + 0xE7, 0xF9, 0x49, 0x48, 0x07, 0x68, 0x04, 0x60, 0xFB, 0xF7, 0xE0, 0xF9, + 0x17, 0xF0, 0x53, 0x0F, + 0x02, 0xD0, 0x38, 0x46, 0xFE, 0xF7, 0xB2, 0xFE, 0x17, 0xF4, 0xD6, 0x7F, + 0x02, 0xD0, 0x38, 0x46, + 0xFF, 0xF7, 0x27, 0xFF, 0xB8, 0x05, 0x13, 0xD5, 0x28, 0x68, 0x90, 0xF8, + 0x70, 0x01, 0x80, 0x07, + 0x02, 0xD5, 0x01, 0x20, 0xFE, 0xF7, 0xEF, 0xFD, 0x28, 0x68, 0x90, 0xF8, + 0x88, 0x11, 0xC9, 0x06, + 0x03, 0xD4, 0x90, 0xF8, 0xA0, 0x01, 0xC0, 0x06, 0x02, 0xD5, 0x01, 0x20, + 0xFF, 0xF7, 0x3B, 0xF9, + 0x78, 0x05, 0x05, 0xD5, 0x00, 0x20, 0xFE, 0xF7, 0xDE, 0xFD, 0x00, 0x20, + 0xFF, 0xF7, 0x33, 0xF9, + 0x02, 0x21, 0x58, 0xE0, 0xC1, 0x05, 0x0C, 0xD5, 0xFB, 0xF7, 0xB2, 0xF9, + 0x2F, 0x48, 0x07, 0x68, + 0x04, 0x60, 0xFB, 0xF7, 0xAB, 0xF9, 0x38, 0x46, 0xFC, 0xF7, 0x9B, 0xFF, + 0x4F, 0xF4, 0x80, 0x71, + 0x49, 0xE0, 0x81, 0x05, 0x04, 0xD5, 0xFE, 0xF7, 0x46, 0xF9, 0x4F, 0xF4, + 0x00, 0x71, 0x42, 0xE0, + 0x41, 0x05, 0x15, 0xD5, 0xFB, 0xF7, 0x9C, 0xF9, 0x25, 0x48, 0x07, 0x68, + 0x04, 0x60, 0xFB, 0xF7, + 0x95, 0xF9, 0xF8, 0x07, 0x01, 0xD0, 0xFD, 0xF7, 0x39, 0xFD, 0xB8, 0x07, + 0x01, 0xD5, 0xFD, 0xF7, + 0xB5, 0xFD, 0x78, 0x07, 0x01, 0xD5, 0xFD, 0xF7, 0x45, 0xFD, 0x4F, 0xF4, + 0x80, 0x61, 0x2A, 0xE0, + 0x01, 0x06, 0x0B, 0xD5, 0xFB, 0xF7, 0x84, 0xF9, 0x1A, 0x48, 0x07, 0x68, + 0x04, 0x60, 0xFB, 0xF7, + 0x7D, 0xF9, 0x38, 0x46, 0xFE, 0xF7, 0x70, 0xF8, 0x80, 0x21, 0x1C, 0xE0, + 0x81, 0x02, 0x0C, 0xD5, + 0xFB, 0xF7, 0x76, 0xF9, 0x14, 0x48, 0x07, 0x68, 0x04, 0x60, 0xFB, 0xF7, + 0x6F, 0xF9, 0x38, 0x46, + 0xFD, 0xF7, 0x9C, 0xFC, 0x4F, 0xF4, 0x00, 0x11, 0x0D, 0xE0, 0xC1, 0x04, + 0x04, 0xD5, 0xFE, 0xF7, + 0x31, 0xFF, 0x4F, 0xF4, 0x80, 0x51, 0x06, 0xE0, 0x00, 0x05, 0x7F, 0xF5, + 0x6F, 0xAF, 0xFE, 0xF7, + 0x45, 0xFF, 0x4F, 0xF4, 0x00, 0x61, 0x00, 0x20, 0xFB, 0xF7, 0xC7, 0xFA, + 0x66, 0xE7, 0x00, 0x00, + 0x83, 0x9F, 0x30, 0x00, 0x4C, 0x07, 0x10, 0x00, 0x58, 0x07, 0x10, 0x00, + 0x54, 0x07, 0x10, 0x00, + 0x60, 0x07, 0x10, 0x00, 0x64, 0x07, 0x10, 0x00, 0x5C, 0x07, 0x10, 0x00, + 0x20, 0x48, 0x10, 0xB5, + 0x01, 0x68, 0x20, 0x48, 0x02, 0x68, 0x13, 0x46, 0x92, 0xEA, 0x01, 0x0F, + 0x28, 0xD0, 0x1E, 0x4A, + 0xCC, 0x07, 0x12, 0x68, 0x25, 0xD0, 0x92, 0xF8, 0x21, 0x43, 0xE4, 0x07, + 0x02, 0xD0, 0x43, 0xF0, + 0x01, 0x03, 0x03, 0x60, 0x8B, 0x07, 0x1F, 0xD5, 0x92, 0xF8, 0x40, 0x33, + 0xDB, 0x07, 0x03, 0xD0, + 0x03, 0x68, 0x43, 0xF0, 0x02, 0x03, 0x03, 0x60, 0x0B, 0x07, 0x19, 0xD5, + 0x92, 0xF8, 0x60, 0x33, + 0xDB, 0x07, 0x03, 0xD0, 0x03, 0x68, 0x43, 0xF0, 0x08, 0x03, 0x03, 0x60, + 0x49, 0x06, 0x13, 0xD5, + 0x92, 0xF8, 0x60, 0x13, 0xC9, 0x07, 0x03, 0xD0, 0x01, 0x68, 0x41, 0xF0, + 0x40, 0x01, 0x01, 0x60, + 0x10, 0xBD, 0x23, 0xF0, 0x01, 0x03, 0xDC, 0xE7, 0x03, 0x68, 0x23, 0xF0, + 0x02, 0x03, 0xE2, 0xE7, + 0x03, 0x68, 0x23, 0xF0, 0x08, 0x03, 0xE8, 0xE7, 0x01, 0x68, 0x21, 0xF0, + 0x40, 0x01, 0xEE, 0xE7, + 0x70, 0x07, 0x10, 0x00, 0x48, 0x05, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x2D, 0xE9, 0xF0, 0x47, + 0xDF, 0xF8, 0x60, 0x92, 0x98, 0x4C, 0x00, 0x25, 0xD9, 0xF8, 0x00, 0x10, + 0x91, 0xF8, 0x31, 0x00, + 0x84, 0xF8, 0x30, 0x00, 0x91, 0xF8, 0x30, 0x20, 0x94, 0x49, 0x84, 0xF8, + 0x31, 0x20, 0x50, 0x43, + 0x09, 0x78, 0x84, 0xF8, 0x33, 0x10, 0x92, 0x49, 0x21, 0x60, 0x34, 0x39, + 0x06, 0xB2, 0x8B, 0x7B, + 0x84, 0xF8, 0x2D, 0x30, 0x88, 0x46, 0x77, 0x00, 0x0F, 0xE0, 0x2A, 0x46, + 0x31, 0x46, 0xD8, 0xF8, + 0x08, 0x00, 0x0E, 0xF0, 0x32, 0xF9, 0x01, 0x46, 0x22, 0x68, 0x06, 0xFB, + 0x05, 0xF0, 0x02, 0xEB, + 0x40, 0x00, 0x3A, 0x46, 0x12, 0xF0, 0x09, 0xF8, 0x6D, 0x1C, 0x94, 0xF8, + 0x2D, 0x00, 0xA8, 0x42, + 0xEB, 0xDC, 0x84, 0x48, 0x84, 0x49, 0x00, 0x78, 0x09, 0x78, 0x08, 0x43, + 0x40, 0x07, 0x11, 0xD5, + 0x82, 0x48, 0x60, 0x60, 0xD9, 0xF8, 0x00, 0x10, 0x91, 0xF8, 0xF1, 0x11, + 0x89, 0x07, 0x4A, 0x0F, + 0x94, 0xF8, 0x31, 0x10, 0x92, 0x1C, 0x4A, 0x43, 0xD8, 0xF8, 0x10, 0x10, + 0xBD, 0xE8, 0xF0, 0x47, + 0x11, 0xF0, 0xEB, 0xBF, 0xBD, 0xE8, 0xF0, 0x87, 0x2D, 0xE9, 0xF8, 0x43, + 0x75, 0x48, 0xDF, 0xF8, + 0xD8, 0x81, 0x00, 0x27, 0x05, 0x68, 0xD8, 0xF8, 0x00, 0x00, 0x05, 0x43, + 0x01, 0x20, 0xFB, 0xF7, + 0xC8, 0xFA, 0x38, 0x21, 0x6C, 0x48, 0x12, 0xF0, 0x79, 0xF8, 0xE8, 0x07, + 0x06, 0xD0, 0xFF, 0xF7, + 0x9D, 0xFF, 0xE8, 0x06, 0x01, 0xD5, 0x00, 0xF0, 0x9B, 0xF8, 0x01, 0x27, + 0x68, 0x4E, 0x66, 0x4C, + 0xA8, 0x07, 0xA6, 0xF1, 0x34, 0x06, 0x0A, 0xD5, 0x69, 0x48, 0xA0, 0x60, + 0x68, 0x49, 0x70, 0x69, + 0x89, 0x1E, 0x01, 0x27, 0x02, 0x68, 0x41, 0xF8, 0x02, 0x2F, 0x40, 0x68, + 0x48, 0x60, 0xA8, 0x05, + 0x11, 0xD5, 0x62, 0x4F, 0xCA, 0x3F, 0xA7, 0x61, 0x11, 0xF0, 0x81, 0xFC, + 0x00, 0x90, 0x04, 0x21, + 0x68, 0x46, 0xFC, 0xF7, 0x08, 0xF8, 0x84, 0xF8, 0x32, 0x00, 0xF0, 0x6A, + 0x01, 0x68, 0x39, 0x60, + 0x40, 0x68, 0x78, 0x60, 0x01, 0x27, 0x5B, 0x48, 0x00, 0x68, 0xC4, 0xE9, + 0x07, 0x05, 0xD8, 0xF8, + 0x00, 0x00, 0x60, 0x62, 0x70, 0x68, 0xA0, 0x62, 0xF0, 0x7B, 0x84, 0xF8, + 0x35, 0x00, 0x96, 0xF8, + 0x30, 0x00, 0x55, 0x4E, 0x84, 0xF8, 0x36, 0x00, 0x30, 0x78, 0x84, 0xF8, + 0x2C, 0x00, 0xE8, 0x07, + 0x1A, 0xD0, 0xE8, 0x06, 0x18, 0xD5, 0x47, 0x4D, 0x28, 0x68, 0x90, 0xF8, + 0x50, 0x02, 0xC0, 0x07, + 0x06, 0xD0, 0x00, 0xF0, 0xF4, 0xFD, 0x18, 0xB9, 0xE0, 0x69, 0x40, 0xF0, + 0x10, 0x00, 0xE0, 0x61, + 0x28, 0x68, 0x90, 0xF8, 0x80, 0x02, 0xC0, 0x07, 0x06, 0xD0, 0x00, 0xF0, + 0xE8, 0xFD, 0x18, 0xB9, + 0xE0, 0x69, 0x40, 0xF0, 0x20, 0x00, 0xE0, 0x61, 0x01, 0x20, 0xFB, 0xF7, + 0x7D, 0xFA, 0x00, 0x2F, + 0x0C, 0xD0, 0x30, 0x78, 0x05, 0x28, 0x07, 0xD0, 0x06, 0x28, 0x05, 0xD0, + 0x01, 0x21, 0xBD, 0xE8, + 0xF8, 0x43, 0x02, 0x20, 0xFB, 0xF7, 0x75, 0xB9, 0x02, 0x21, 0xF8, 0xE7, + 0xBD, 0xE8, 0xF8, 0x83, + 0x3A, 0x48, 0x10, 0xB5, 0x00, 0x78, 0x00, 0x28, 0x29, 0xD1, 0x31, 0x4C, + 0x34, 0x3C, 0xD4, 0xE9, + 0x06, 0x01, 0x00, 0xF0, 0xC7, 0xFD, 0x94, 0xF8, 0x31, 0x10, 0xA0, 0x6A, + 0x00, 0xF0, 0xC3, 0xFC, + 0xFF, 0xF7, 0x6A, 0xFF, 0x27, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x60, 0x03, + 0xC1, 0x07, 0x16, 0xD0, + 0x81, 0x07, 0x14, 0xD5, 0x00, 0x24, 0x40, 0x09, 0x12, 0xF0, 0x6C, 0xF8, + 0x00, 0xB1, 0x01, 0x24, + 0x27, 0x48, 0x80, 0x1E, 0x01, 0x78, 0x8C, 0x42, 0x09, 0xD0, 0x04, 0x70, + 0x0C, 0xB1, 0x08, 0x21, + 0x00, 0xE0, 0x10, 0x21, 0xBD, 0xE8, 0x10, 0x40, 0x03, 0x20, 0xFB, 0xF7, + 0x42, 0xB9, 0x10, 0xBD, + 0x2D, 0xE9, 0xF0, 0x41, 0x17, 0x48, 0x1A, 0x4E, 0x17, 0x4C, 0x00, 0x68, + 0x34, 0x3E, 0x90, 0xF8, + 0x31, 0x50, 0x90, 0xF8, 0x30, 0x70, 0xB1, 0x69, 0x39, 0xB1, 0x18, 0x48, + 0x84, 0xF8, 0x30, 0x50, + 0xC2, 0x38, 0x6A, 0x00, 0x20, 0x61, 0x11, 0xF0, 0x20, 0xFF, 0xF1, 0x69, + 0x39, 0xB1, 0x13, 0x48, + 0x84, 0xF8, 0x31, 0x70, 0x98, 0x38, 0x7A, 0x00, 0x60, 0x61, 0x11, 0xF0, + 0x16, 0xFF, 0x0D, 0x48, + 0x0D, 0x49, 0x00, 0x78, 0x09, 0x78, 0x08, 0x43, 0x80, 0x06, 0x08, 0xD5, + 0x0B, 0x48, 0x4C, 0x38, + 0xE0, 0x60, 0x6A, 0x00, 0x71, 0x6A, 0xBD, 0xE8, 0xF0, 0x41, 0x11, 0xF0, + 0x06, 0xBF, 0xBD, 0xE8, + 0xF0, 0x81, 0x00, 0x00, 0x4C, 0x07, 0x10, 0x00, 0xCC, 0x68, 0x10, 0x00, + 0x54, 0x05, 0x10, 0x00, + 0x1C, 0x11, 0x10, 0x00, 0x4C, 0x05, 0x10, 0x00, 0x50, 0x05, 0x10, 0x00, + 0xCC, 0x07, 0x01, 0x20, + 0x1A, 0x05, 0x10, 0x00, 0x48, 0x05, 0x10, 0x00, 0x40, 0x05, 0x10, 0x00, + 0x41, 0x05, 0x10, 0x00, + 0x10, 0xB5, 0x00, 0x24, 0xC1, 0x07, 0x0B, 0xD0, 0x3F, 0x49, 0x03, 0x24, + 0x09, 0x68, 0x91, 0xF8, + 0x50, 0x22, 0xD2, 0x07, 0x03, 0xD1, 0x91, 0xF8, 0x80, 0x12, 0xC9, 0x07, + 0x00, 0xD0, 0x0B, 0x24, + 0x82, 0x07, 0x4F, 0xF0, 0x01, 0x01, 0x03, 0xD5, 0x38, 0x4A, 0x44, 0xF0, + 0x10, 0x04, 0x11, 0x70, + 0x40, 0x06, 0x03, 0xD5, 0x36, 0x48, 0x44, 0xF0, 0x80, 0x04, 0x01, 0x70, + 0x00, 0x2C, 0x12, 0xD0, + 0xFA, 0xF7, 0x96, 0xFF, 0x33, 0x48, 0x01, 0x68, 0x21, 0x43, 0x01, 0x60, + 0xFA, 0xF7, 0x8E, 0xFF, + 0x01, 0x21, 0x05, 0x20, 0x0D, 0xF0, 0xCF, 0xFA, 0xBD, 0xE8, 0x10, 0x40, + 0x4F, 0xF4, 0x80, 0x71, + 0x00, 0x20, 0xFB, 0xF7, 0xC6, 0xB8, 0x10, 0xBD, 0x10, 0xB5, 0x00, 0xF0, + 0xDE, 0xFD, 0x02, 0x21, + 0x01, 0x20, 0xFB, 0xF7, 0xEA, 0xF8, 0x01, 0x20, 0xFB, 0xF7, 0x9B, 0xF9, + 0x38, 0x21, 0x26, 0x48, + 0x11, 0xF0, 0x4C, 0xFF, 0x24, 0x49, 0x01, 0x20, 0x81, 0xF8, 0x2C, 0x00, + 0xFB, 0xF7, 0xAC, 0xF9, + 0x01, 0x21, 0x02, 0x20, 0xFB, 0xF7, 0xAD, 0xF8, 0xBD, 0xE8, 0x10, 0x40, + 0x00, 0xF0, 0x2D, 0xBB, + 0x70, 0xB5, 0x1E, 0x48, 0x1E, 0x4E, 0x05, 0x68, 0x30, 0x68, 0x90, 0xEA, + 0x05, 0x04, 0x1F, 0xD0, + 0x35, 0xB1, 0x40, 0xB1, 0x04, 0xEA, 0x05, 0x00, 0xFF, 0xF7, 0xA2, 0xFF, + 0x35, 0x60, 0x70, 0xBD, + 0xFF, 0xF7, 0xD2, 0xFF, 0xFA, 0xE7, 0x00, 0xF0, 0xB0, 0xFD, 0x02, 0x21, + 0x01, 0x20, 0xFB, 0xF7, + 0xBC, 0xF8, 0x00, 0xF0, 0xD8, 0xF8, 0x00, 0xF0, 0x08, 0xFB, 0x04, 0xEA, + 0x05, 0x00, 0xFF, 0xF7, + 0x8F, 0xFF, 0x00, 0xF0, 0xF1, 0xFA, 0x02, 0x21, 0x01, 0x20, 0xFB, 0xF7, + 0x82, 0xF8, 0xE5, 0xE7, + 0x00, 0x2D, 0xE4, 0xD1, 0x0B, 0x48, 0x00, 0x78, 0x05, 0x28, 0x01, 0xD0, + 0x06, 0x28, 0xDE, 0xD1, + 0x35, 0x60, 0xBD, 0xE8, 0x70, 0x40, 0xAF, 0xE7, 0x4C, 0x07, 0x10, 0x00, + 0x7B, 0x07, 0x10, 0x00, + 0x7C, 0x07, 0x10, 0x00, 0x54, 0x07, 0x10, 0x00, 0xCC, 0x68, 0x10, 0x00, + 0x6C, 0x07, 0x10, 0x00, + 0x44, 0x05, 0x10, 0x00, 0x40, 0x05, 0x10, 0x00, 0x70, 0xB5, 0xFF, 0x49, + 0x00, 0x20, 0x03, 0x46, + 0x09, 0x68, 0x91, 0xF8, 0x40, 0x20, 0xD2, 0x07, 0x38, 0xD0, 0xFC, 0x4A, + 0x15, 0x78, 0x02, 0x2D, + 0x16, 0xD0, 0x04, 0x2D, 0x03, 0xD0, 0x05, 0x2D, 0x0F, 0xD0, 0x06, 0x2D, + 0x0D, 0xD0, 0x91, 0xF8, + 0x41, 0x20, 0xF7, 0x49, 0x0C, 0x68, 0x64, 0x1C, 0x0C, 0x60, 0x94, 0x42, + 0x26, 0xDD, 0xF5, 0x4A, + 0x00, 0x20, 0x14, 0x78, 0x64, 0xB1, 0x08, 0x60, 0x0C, 0xE0, 0x91, 0xF8, + 0x43, 0x20, 0xF0, 0xE7, + 0x91, 0xF8, 0x42, 0x20, 0x91, 0xF8, 0x90, 0x10, 0xC9, 0x06, 0xEA, 0xD5, + 0x01, 0x23, 0xE8, 0xE7, + 0x13, 0xB1, 0x01, 0x20, 0x10, 0x70, 0x00, 0xE0, 0x08, 0x60, 0x05, 0x2D, + 0x0F, 0xD0, 0x00, 0xF0, + 0x11, 0xF9, 0x04, 0x46, 0x00, 0xF0, 0x14, 0xF9, 0x04, 0x43, 0x00, 0xF0, + 0x1F, 0xF9, 0x04, 0x43, + 0x00, 0xF0, 0x2B, 0xF9, 0x04, 0x43, 0x00, 0xF0, 0x35, 0xF9, 0x20, 0x43, + 0x70, 0xBD, 0x01, 0x20, + 0x70, 0xBD, 0x10, 0xB5, 0x01, 0x46, 0x34, 0x22, 0xDF, 0x48, 0x11, 0xF0, + 0x53, 0xFE, 0x01, 0x21, + 0xBD, 0xE8, 0x10, 0x40, 0x08, 0x46, 0xFB, 0xF7, 0x14, 0xB8, 0x2D, 0xE9, + 0xFF, 0x41, 0x80, 0x46, + 0xD6, 0x48, 0x00, 0x78, 0x02, 0x28, 0x24, 0xD0, 0x03, 0x28, 0x1E, 0xD0, + 0x04, 0x24, 0xD7, 0x4D, + 0xD7, 0x4E, 0x01, 0x27, 0x05, 0x28, 0x20, 0xD0, 0x06, 0x28, 0x68, 0x46, + 0x30, 0xD0, 0x00, 0xF0, + 0xB5, 0xF9, 0xD4, 0x48, 0x00, 0x90, 0xCC, 0x48, 0x00, 0x68, 0x90, 0xF8, + 0xB0, 0x00, 0xC0, 0x07, + 0x01, 0xD0, 0x8D, 0xF8, 0x0D, 0x80, 0x68, 0x46, 0x11, 0xF0, 0x71, 0xFA, + 0xCE, 0x49, 0xCF, 0x4A, + 0xC9, 0x78, 0x11, 0x70, 0x04, 0xB0, 0xBD, 0xE8, 0xF0, 0x81, 0x68, 0x46, + 0x00, 0xF0, 0x4A, 0xF9, + 0xE7, 0xE7, 0x68, 0x46, 0x00, 0xF0, 0x05, 0xF9, 0xE3, 0xE7, 0x68, 0x46, + 0x11, 0xF0, 0x3A, 0xFA, + 0xFF, 0xF7, 0x7A, 0xFF, 0x40, 0x21, 0xC2, 0x07, 0x01, 0xD0, 0x8D, 0xF8, + 0x04, 0x40, 0xC2, 0x06, + 0x01, 0xD5, 0x8D, 0xF8, 0x06, 0x70, 0x8D, 0xF8, 0x08, 0x70, 0x28, 0x60, + 0x31, 0x60, 0xD0, 0xE7, + 0x00, 0x27, 0x11, 0xF0, 0x27, 0xFA, 0x8D, 0xF8, 0x04, 0x40, 0x01, 0x20, + 0x2F, 0x60, 0x30, 0x60, + 0xC7, 0xE7, 0x10, 0xB5, 0x00, 0xF0, 0xE1, 0xFC, 0x02, 0x21, 0xBD, 0xE8, + 0x10, 0x40, 0x01, 0x20, + 0xFA, 0xF7, 0xBF, 0xBF, 0x10, 0xB5, 0x00, 0xF0, 0xDD, 0xFC, 0x20, 0x21, + 0xBD, 0xE8, 0x10, 0x40, + 0x01, 0x20, 0xFA, 0xF7, 0xB6, 0xBF, 0xAA, 0x49, 0x00, 0x20, 0x08, 0x60, + 0x70, 0x47, 0xA8, 0x49, + 0x00, 0x20, 0x08, 0x60, 0x70, 0x47, 0xF8, 0xB5, 0x00, 0x24, 0x20, 0x46, + 0xFB, 0xF7, 0x89, 0xF8, + 0x00, 0xF0, 0x32, 0xFA, 0xA1, 0x4E, 0x30, 0x78, 0x00, 0xF0, 0x93, 0xF9, + 0xAF, 0xF2, 0x4D, 0x01, + 0x00, 0xF0, 0xB3, 0xFC, 0x9C, 0x4F, 0xA6, 0x4D, 0x38, 0x68, 0x90, 0xF8, + 0xB0, 0x00, 0xC1, 0x07, + 0x09, 0xD0, 0x01, 0x24, 0xC0, 0xF3, 0xC1, 0x01, 0x02, 0x29, 0x00, 0xD1, + 0x00, 0x24, 0x80, 0x07, + 0x01, 0xD5, 0x00, 0x20, 0x28, 0x70, 0x20, 0x46, 0xFF, 0xF7, 0x77, 0xFF, + 0x96, 0x4C, 0x01, 0x28, + 0x0D, 0xD1, 0x01, 0x21, 0x6B, 0x46, 0x00, 0x22, 0x08, 0x46, 0xFA, 0xF7, + 0xCB, 0xFF, 0x01, 0x21, + 0x08, 0x46, 0xFA, 0xF7, 0xAA, 0xFF, 0x20, 0x78, 0x90, 0xB1, 0x0C, 0xF0, + 0x8E, 0xFF, 0x00, 0x20, + 0xFB, 0xF7, 0x72, 0xF8, 0x38, 0x68, 0x90, 0xF8, 0xB0, 0x10, 0xCA, 0x07, + 0x51, 0xD0, 0x89, 0x07, + 0x4F, 0xD5, 0x8D, 0x49, 0x49, 0x78, 0x00, 0x29, 0x4B, 0xD1, 0x29, 0x78, + 0x19, 0xB1, 0x28, 0xE0, + 0xFF, 0xF7, 0xF6, 0xFD, 0xEB, 0xE7, 0x32, 0x78, 0x8A, 0x49, 0x02, 0x2A, + 0x02, 0xD1, 0x83, 0x4A, + 0x12, 0x68, 0x2A, 0xB1, 0x09, 0x78, 0x49, 0x06, 0x0B, 0xD5, 0x90, 0xF8, + 0xC1, 0x00, 0x0A, 0xE0, + 0x09, 0x78, 0x49, 0x06, 0x02, 0xD5, 0x90, 0xF8, 0xC2, 0x00, 0x04, 0xE0, + 0x90, 0xF8, 0xBC, 0x00, + 0x01, 0xE0, 0x90, 0xF8, 0xBB, 0x00, 0xAF, 0xF2, 0xE3, 0x01, 0x00, 0xF0, + 0x68, 0xFC, 0x6B, 0x46, + 0x00, 0x22, 0x20, 0x21, 0x01, 0x20, 0xFA, 0xF7, 0x8D, 0xFF, 0x20, 0x21, + 0x01, 0x20, 0xFA, 0xF7, + 0x6C, 0xFF, 0x28, 0x78, 0x00, 0x28, 0x1C, 0xD1, 0xFB, 0xF7, 0x1B, 0xF8, + 0x00, 0x20, 0xFF, 0xF7, + 0x24, 0xFF, 0x01, 0x28, 0x0D, 0xD1, 0x01, 0x21, 0x6B, 0x46, 0x00, 0x22, + 0x08, 0x46, 0xFA, 0xF7, + 0x79, 0xFF, 0x01, 0x21, 0x08, 0x46, 0xFA, 0xF7, 0x58, 0xFF, 0x20, 0x78, + 0x30, 0xB1, 0x0C, 0xF0, + 0x3C, 0xFF, 0xBD, 0xE8, 0xF8, 0x40, 0x00, 0x20, 0xFB, 0xF7, 0x1E, 0xB8, + 0xFF, 0xF7, 0xB0, 0xFD, + 0xF7, 0xE7, 0xF8, 0xBD, 0x67, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x00, 0xD0, + 0x01, 0x20, 0x70, 0x47, + 0x64, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x07, 0xD0, 0x57, 0x48, 0x00, 0x68, + 0x90, 0xF8, 0x50, 0x02, + 0xC0, 0x07, 0x01, 0xD0, 0x10, 0x20, 0x70, 0x47, 0x00, 0x20, 0x70, 0x47, + 0x5D, 0x48, 0x00, 0x78, + 0xC1, 0x07, 0x51, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x80, 0x02, 0x03, 0xD0, + 0xC1, 0x07, 0x01, 0xD0, + 0x10, 0x20, 0x70, 0x47, 0x00, 0xF0, 0x01, 0x00, 0x70, 0x47, 0x56, 0x48, + 0x00, 0x78, 0x80, 0x07, + 0x06, 0xD5, 0x49, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x61, 0x04, 0x08, 0xB1, + 0x02, 0x20, 0x70, 0x47, + 0x00, 0x20, 0x70, 0x47, 0x4F, 0x48, 0x00, 0x78, 0x40, 0x06, 0x08, 0xD5, + 0x42, 0x48, 0x00, 0x68, + 0x90, 0xF8, 0x2F, 0x0A, 0xC0, 0x07, 0x02, 0xD0, 0x4F, 0xF4, 0x00, 0x70, + 0x70, 0x47, 0x00, 0x20, + 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x41, 0x06, 0x46, 0x00, 0x27, 0x11, 0xF0, + 0x33, 0xF9, 0xFF, 0xF7, + 0x73, 0xFE, 0x05, 0x46, 0x43, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x00, 0xD0, + 0x40, 0x27, 0xFF, 0xF7, + 0xD4, 0xFF, 0x40, 0xEA, 0x07, 0x04, 0x6D, 0xB1, 0x33, 0x48, 0x00, 0x68, + 0x90, 0xF8, 0x90, 0x00, + 0x00, 0x07, 0x07, 0xD5, 0x00, 0xF0, 0xE4, 0xF8, 0x40, 0xEA, 0x04, 0x07, + 0x00, 0xF0, 0xEE, 0xF8, + 0x40, 0xEA, 0x07, 0x04, 0x45, 0xEA, 0x04, 0x07, 0xF8, 0x07, 0x02, 0xD0, + 0x00, 0xF0, 0xF4, 0xF8, + 0x30, 0x71, 0x61, 0x07, 0x4F, 0xF0, 0x01, 0x00, 0x00, 0xD5, 0x70, 0x71, + 0xE9, 0x06, 0x00, 0xD5, + 0xB0, 0x71, 0x61, 0x06, 0x00, 0xD5, 0x30, 0x72, 0xA1, 0x06, 0x00, 0xD5, + 0xF0, 0x71, 0xB9, 0x07, + 0x00, 0xD5, 0x70, 0x72, 0xA9, 0x05, 0x00, 0xD5, 0xB0, 0x72, 0x24, 0x48, + 0x05, 0x60, 0x24, 0x48, + 0x04, 0x60, 0xB0, 0xE6, 0x2D, 0xE9, 0xF0, 0x41, 0x05, 0x46, 0x11, 0xF0, + 0xF3, 0xF8, 0xFF, 0xF7, + 0x33, 0xFE, 0x07, 0x46, 0xFF, 0xF7, 0x76, 0xFF, 0x04, 0x46, 0xFF, 0xF7, + 0x79, 0xFF, 0x04, 0x43, + 0xFF, 0xF7, 0x84, 0xFF, 0x04, 0x43, 0x00, 0xF0, 0xAB, 0xF8, 0x04, 0x43, + 0xFF, 0xF7, 0x8D, 0xFF, + 0x04, 0x43, 0xFF, 0xF7, 0x97, 0xFF, 0x04, 0x43, 0x00, 0xF0, 0xB0, 0xF8, + 0x40, 0xEA, 0x04, 0x06, + 0x47, 0xEA, 0x06, 0x04, 0xE0, 0x07, 0x02, 0xD0, 0x00, 0xF0, 0xB6, 0xF8, + 0x28, 0x71, 0x71, 0x07, + 0x4F, 0xF0, 0x01, 0x00, 0x00, 0xD5, 0x68, 0x71, 0xE1, 0x06, 0x00, 0xD5, + 0xA8, 0x71, 0xB1, 0x06, + 0x00, 0xD5, 0xE8, 0x71, 0xA1, 0x07, 0x00, 0xD5, 0x68, 0x72, 0xA1, 0x05, + 0x00, 0xD5, 0xA8, 0x72, + 0x06, 0x48, 0x07, 0x60, 0x06, 0x48, 0x17, 0xE0, 0x4C, 0x07, 0x10, 0x00, + 0x40, 0x05, 0x10, 0x00, + 0x24, 0x05, 0x10, 0x00, 0x41, 0x05, 0x10, 0x00, 0xE8, 0x10, 0x10, 0x00, + 0x50, 0x05, 0x10, 0x00, + 0x4C, 0x05, 0x10, 0x00, 0xE3, 0x54, 0x00, 0x00, 0x74, 0x07, 0x10, 0x00, + 0x54, 0x05, 0x10, 0x00, + 0x78, 0x07, 0x10, 0x00, 0x44, 0x05, 0x10, 0x00, 0x06, 0x60, 0x5C, 0xE6, + 0x2D, 0xE9, 0xF0, 0x41, + 0x05, 0x46, 0x11, 0xF0, 0x9F, 0xF8, 0xFF, 0xF7, 0xDF, 0xFD, 0x06, 0x46, + 0xFF, 0xF7, 0x22, 0xFF, + 0x04, 0x46, 0xFF, 0xF7, 0x25, 0xFF, 0x04, 0x43, 0xFF, 0xF7, 0x30, 0xFF, + 0x04, 0x43, 0x00, 0xF0, + 0x57, 0xF8, 0x04, 0x43, 0xFF, 0xF7, 0x39, 0xFF, 0x04, 0x43, 0xFF, 0xF7, + 0x43, 0xFF, 0x04, 0x43, + 0x00, 0xF0, 0x5C, 0xF8, 0x40, 0xEA, 0x04, 0x07, 0x46, 0xEA, 0x07, 0x04, + 0xE0, 0x07, 0x02, 0xD0, + 0x00, 0xF0, 0x62, 0xF8, 0x28, 0x71, 0x61, 0x07, 0x4F, 0xF0, 0x01, 0x00, + 0x00, 0xD5, 0x68, 0x71, + 0xE1, 0x06, 0x00, 0xD5, 0xA8, 0x71, 0xB9, 0x06, 0x00, 0xD5, 0xE8, 0x71, + 0xA1, 0x07, 0x00, 0xD5, + 0x68, 0x72, 0xA1, 0x05, 0x00, 0xD5, 0xA8, 0x72, 0x2E, 0x48, 0x06, 0x60, + 0x2E, 0x48, 0x07, 0x60, + 0x21, 0xE6, 0x38, 0xB5, 0x2D, 0x49, 0x80, 0x1E, 0x05, 0x28, 0x09, 0x68, + 0x13, 0xD2, 0xDF, 0xE8, + 0x00, 0xF0, 0x03, 0x06, 0x09, 0x0C, 0x0F, 0x00, 0x91, 0xF8, 0x73, 0x40, + 0x0C, 0xE0, 0x91, 0xF8, + 0x72, 0x40, 0x09, 0xE0, 0x91, 0xF8, 0x71, 0x40, 0x06, 0xE0, 0x91, 0xF8, + 0x74, 0x40, 0x03, 0xE0, + 0x91, 0xF8, 0x75, 0x40, 0x00, 0xE0, 0x01, 0x24, 0x00, 0x25, 0x0C, 0xF0, + 0x53, 0xFA, 0x00, 0xB1, + 0x01, 0x25, 0x69, 0x46, 0x03, 0x20, 0xFA, 0xF7, 0x26, 0xFE, 0x00, 0x98, + 0x00, 0xB9, 0x1D, 0xB1, + 0x14, 0x2C, 0x01, 0xD2, 0x14, 0x24, 0x01, 0xE0, 0x04, 0xB9, 0x01, 0x24, + 0x20, 0x46, 0x38, 0xBD, + 0x17, 0x48, 0x00, 0x78, 0xC0, 0x07, 0x07, 0xD0, 0x14, 0x48, 0x00, 0x68, + 0x90, 0xF8, 0xF0, 0x01, + 0xC0, 0x07, 0x01, 0xD0, 0x04, 0x20, 0x70, 0x47, 0x00, 0x20, 0x70, 0x47, + 0x10, 0x48, 0x00, 0x78, + 0xC0, 0x07, 0x07, 0xD0, 0x0D, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x10, 0x02, + 0xC0, 0x07, 0x01, 0xD0, + 0x20, 0x20, 0x70, 0x47, 0x00, 0x20, 0x70, 0x47, 0x08, 0x49, 0x01, 0x20, + 0x09, 0x68, 0x91, 0xF8, + 0xE7, 0x10, 0x09, 0x06, 0x03, 0xD5, 0x07, 0x48, 0x40, 0x78, 0x08, 0xB1, + 0x03, 0x20, 0x70, 0x47, + 0x02, 0x20, 0x70, 0x47, 0x50, 0x05, 0x10, 0x00, 0x4C, 0x05, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, + 0x44, 0x05, 0x10, 0x00, 0x74, 0x07, 0x10, 0x00, 0x10, 0xB5, 0x00, 0xF0, + 0x8B, 0xF9, 0x00, 0xF0, + 0x00, 0xFA, 0x5B, 0x49, 0x00, 0x20, 0x48, 0x60, 0x5A, 0x49, 0x02, 0x20, + 0x08, 0x70, 0x10, 0xBD, + 0x10, 0xB5, 0x00, 0xF0, 0x7F, 0xF9, 0x00, 0xF0, 0xF4, 0xF9, 0x55, 0x49, + 0x00, 0x20, 0x48, 0x60, + 0x54, 0x49, 0x03, 0x20, 0x08, 0x70, 0x10, 0xBD, 0x10, 0xB5, 0x00, 0xF0, + 0x73, 0xF9, 0x00, 0xF0, + 0xE8, 0xF9, 0x4F, 0x49, 0x00, 0x20, 0x48, 0x60, 0x4E, 0x49, 0x04, 0x20, + 0x08, 0x70, 0x4E, 0x49, + 0x01, 0x20, 0x08, 0x70, 0x4D, 0x49, 0x08, 0x70, 0x10, 0xBD, 0x49, 0x49, + 0x00, 0x20, 0x48, 0x60, + 0x70, 0x47, 0x47, 0x49, 0x00, 0x20, 0x48, 0x60, 0x70, 0x47, 0x45, 0x49, + 0x00, 0x20, 0x48, 0x60, + 0x44, 0x49, 0x01, 0x20, 0x08, 0x70, 0x70, 0x47, 0x70, 0xB5, 0x42, 0x4D, + 0x00, 0x26, 0x40, 0x4C, + 0x28, 0x78, 0x80, 0x1E, 0x04, 0x28, 0x10, 0xD2, 0xDF, 0xE8, 0x00, 0xF0, + 0x30, 0x1F, 0x02, 0x3E, + 0x00, 0xF0, 0x45, 0xF8, 0x01, 0x28, 0x07, 0xD0, 0x60, 0x68, 0x40, 0x1C, + 0x60, 0x60, 0x00, 0xF0, + 0x46, 0xF8, 0x01, 0x28, 0x02, 0xD0, 0x0B, 0xE0, 0x66, 0x60, 0x70, 0xBD, + 0x38, 0x48, 0x61, 0x68, + 0x00, 0x68, 0x90, 0xF8, 0x77, 0x00, 0x88, 0x42, 0x02, 0xD2, 0xBD, 0xE8, + 0x70, 0x40, 0xAF, 0xE7, + 0x00, 0xF0, 0x47, 0xF8, 0x01, 0x28, 0x0E, 0xD0, 0x70, 0xBD, 0x00, 0xF0, + 0x28, 0xF8, 0x01, 0x28, + 0x17, 0xD0, 0x60, 0x68, 0x40, 0x1C, 0x60, 0x60, 0x00, 0xF0, 0x3B, 0xF8, + 0x01, 0x28, 0x02, 0xD0, + 0x00, 0xF0, 0x25, 0xF8, 0x0B, 0xE0, 0xBD, 0xE8, 0x70, 0x40, 0x8D, 0xE7, + 0x00, 0xF0, 0x17, 0xF8, + 0x01, 0x28, 0x06, 0xD0, 0x60, 0x68, 0x40, 0x1C, 0x60, 0x60, 0x00, 0xF0, + 0x22, 0xF8, 0x01, 0x28, + 0xE2, 0xD0, 0xBD, 0xE8, 0x70, 0x40, 0x97, 0xE7, 0x00, 0xF0, 0x09, 0xF9, + 0x08, 0xB1, 0x21, 0x78, + 0x09, 0xB1, 0x20, 0x70, 0x70, 0xBD, 0x20, 0x70, 0x06, 0x20, 0x28, 0x70, + 0x70, 0xBD, 0x10, 0xB5, + 0x00, 0xF0, 0xFD, 0xF8, 0x04, 0x46, 0x0B, 0xF0, 0x43, 0xFD, 0x20, 0x43, + 0x10, 0xBD, 0x18, 0x48, + 0x00, 0x68, 0x90, 0xF8, 0x70, 0x00, 0x80, 0x07, 0x01, 0xD5, 0x01, 0x20, + 0x70, 0x47, 0x00, 0x20, + 0x70, 0x47, 0x13, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x70, 0x00, 0xC0, 0x07, + 0x00, 0xD0, 0x01, 0x20, + 0x70, 0x47, 0x10, 0xB5, 0xFF, 0xF7, 0xF5, 0xFF, 0x01, 0x28, 0x0F, 0xD1, + 0x00, 0xF0, 0x5F, 0xF9, + 0x60, 0xB9, 0x0B, 0x48, 0x06, 0x49, 0x00, 0x68, 0x49, 0x68, 0x90, 0xF8, + 0x78, 0x00, 0x00, 0xEB, + 0x80, 0x00, 0xB1, 0xEB, 0x40, 0x0F, 0x01, 0xD9, 0x01, 0x20, 0x10, 0xBD, + 0x00, 0x20, 0x10, 0xBD, + 0x28, 0x05, 0x10, 0x00, 0x40, 0x05, 0x10, 0x00, 0x79, 0x07, 0x10, 0x00, + 0x7A, 0x07, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, 0x2D, 0xE9, 0xF0, 0x41, 0x8E, 0x4C, 0x07, 0x46, + 0x0E, 0x46, 0x20, 0x68, + 0x00, 0x25, 0x90, 0xF8, 0x98, 0x20, 0x8C, 0x48, 0x0E, 0xF0, 0xBD, 0xFA, + 0x02, 0x00, 0x8B, 0x48, + 0x29, 0x46, 0x34, 0xD0, 0x02, 0x78, 0x01, 0x25, 0x52, 0x1C, 0xD2, 0xB2, + 0x02, 0x70, 0x23, 0x68, + 0x93, 0xF8, 0x99, 0x30, 0x93, 0x42, 0x2B, 0xD2, 0x01, 0x70, 0x83, 0x48, + 0x76, 0x00, 0x32, 0x46, + 0x39, 0x46, 0x54, 0x38, 0x11, 0xF0, 0xB9, 0xFA, 0x7F, 0x48, 0x32, 0x46, + 0x39, 0x46, 0x2A, 0x38, + 0x11, 0xF0, 0xB3, 0xFA, 0x20, 0x68, 0x03, 0x26, 0x90, 0xF8, 0x50, 0x12, + 0xC9, 0x07, 0x03, 0xD1, + 0x90, 0xF8, 0x80, 0x02, 0xC0, 0x07, 0x00, 0xD0, 0x0B, 0x26, 0xFA, 0xF7, + 0x71, 0xFB, 0x78, 0x48, + 0x01, 0x68, 0x31, 0x43, 0x01, 0x60, 0xFA, 0xF7, 0x69, 0xFB, 0x20, 0x21, + 0x05, 0x20, 0x0C, 0xF0, + 0xAA, 0xFE, 0x4F, 0xF4, 0x80, 0x71, 0x00, 0x20, 0xFA, 0xF7, 0xA3, 0xFC, + 0x00, 0xE0, 0x01, 0x70, + 0x28, 0x46, 0xBD, 0xE8, 0xF0, 0x81, 0x2D, 0xE9, 0xF0, 0x47, 0x88, 0x46, + 0x00, 0x28, 0x6B, 0xD0, + 0xDF, 0xF8, 0xB0, 0xA1, 0x00, 0x21, 0x69, 0x4C, 0x9A, 0xF8, 0x00, 0x20, + 0x89, 0x46, 0x02, 0x2A, + 0x03, 0xD0, 0x03, 0x2A, 0x01, 0xD0, 0x05, 0x2A, 0x5D, 0xD1, 0x00, 0x21, + 0xB8, 0xF1, 0x00, 0x0F, + 0x00, 0xD0, 0x01, 0x21, 0xA2, 0x88, 0x00, 0xEB, 0x41, 0x00, 0x52, 0x1A, + 0x15, 0xB2, 0x07, 0x46, + 0xA2, 0x78, 0x29, 0x46, 0x00, 0xF0, 0x71, 0xF8, 0x5A, 0x4E, 0xA0, 0x70, + 0x30, 0x68, 0x90, 0xF8, + 0x90, 0x00, 0x80, 0x07, 0x04, 0xD5, 0x38, 0x46, 0x29, 0x46, 0xFF, 0xF7, + 0x8B, 0xFF, 0x81, 0x46, + 0x00, 0x22, 0x01, 0x21, 0xB8, 0xF1, 0x00, 0x0F, 0x1A, 0xD0, 0x00, 0x27, + 0xDF, 0xF8, 0x48, 0xC1, + 0x38, 0x46, 0x36, 0x68, 0x10, 0xE0, 0x00, 0xBF, 0x3C, 0xF9, 0x10, 0x30, + 0x00, 0x2B, 0x00, 0xDA, + 0x5B, 0x42, 0xB6, 0xF8, 0x92, 0x80, 0x43, 0x45, 0x04, 0xDD, 0x96, 0xF8, + 0x91, 0x30, 0x7F, 0x1C, + 0xBB, 0x42, 0x1B, 0xDD, 0x40, 0x1C, 0xC0, 0xB2, 0xA8, 0x42, 0xED, 0xDB, + 0x11, 0x46, 0x22, 0xE0, + 0x00, 0x23, 0x45, 0x4F, 0x18, 0x46, 0x36, 0x68, 0x0D, 0xE0, 0x00, 0xBF, + 0x37, 0xF9, 0x10, 0xC0, + 0xB6, 0xF8, 0x92, 0x80, 0xC4, 0x45, 0x04, 0xDD, 0x96, 0xF8, 0x91, 0xC0, + 0x5B, 0x1C, 0x9C, 0x45, + 0x04, 0xDD, 0x40, 0x1C, 0xC0, 0xB2, 0xA8, 0x42, 0xF0, 0xDB, 0xE7, 0xE7, + 0x9A, 0xF8, 0x00, 0x00, + 0x02, 0x28, 0x0B, 0xD0, 0x03, 0x28, 0x09, 0xD0, 0x05, 0x28, 0x04, 0xD1, + 0x60, 0x78, 0x10, 0xB9, + 0xB9, 0xF1, 0x00, 0x0F, 0x02, 0xD0, 0x61, 0x70, 0xBD, 0xE8, 0xF0, 0x87, + 0x61, 0x70, 0x00, 0xF0, + 0x44, 0xF9, 0xBD, 0xE8, 0xF0, 0x47, 0x02, 0x21, 0x01, 0x20, 0xFA, 0xF7, + 0x22, 0xBC, 0x2F, 0x48, + 0x40, 0x78, 0x70, 0x47, 0x2D, 0x48, 0x00, 0x21, 0x01, 0x22, 0x01, 0x70, + 0x82, 0x70, 0x29, 0x4A, + 0x12, 0x68, 0x92, 0xF8, 0x31, 0x20, 0x82, 0x80, 0x2A, 0x4A, 0x12, 0x78, + 0x05, 0x2A, 0x02, 0xD0, + 0x06, 0x2A, 0x00, 0xD0, 0x41, 0x70, 0x70, 0x47, 0xEC, 0xE7, 0x2D, 0xE9, + 0xFC, 0x41, 0x80, 0x46, + 0x22, 0x48, 0x14, 0x46, 0x0D, 0x46, 0x40, 0x78, 0x16, 0x46, 0x40, 0xB9, + 0x1E, 0x48, 0x0A, 0x46, + 0x7F, 0x23, 0x41, 0x46, 0x54, 0x38, 0x00, 0x94, 0x0E, 0xF0, 0x22, 0xFA, + 0x00, 0x26, 0x19, 0x4F, + 0x38, 0x68, 0x90, 0xF8, 0x90, 0x10, 0xC9, 0x07, 0x00, 0xD0, 0xEC, 0xB1, + 0x16, 0x48, 0x6A, 0x00, + 0x41, 0x46, 0x2A, 0x38, 0x11, 0xF0, 0xE1, 0xF9, 0x13, 0x4A, 0x2B, 0x46, + 0x54, 0x3A, 0x02, 0xF1, + 0x2A, 0x01, 0x11, 0x48, 0x0E, 0xF0, 0xE5, 0xF9, 0x38, 0x68, 0x90, 0xF8, + 0x90, 0x10, 0x49, 0x07, + 0x07, 0xD5, 0x90, 0xF8, 0x9A, 0x30, 0x23, 0xB1, 0x0B, 0x49, 0x2A, 0x46, + 0x08, 0x46, 0x0E, 0xF0, + 0x18, 0xFA, 0x30, 0x46, 0xBD, 0xE8, 0xFC, 0x81, 0xB0, 0xF9, 0x96, 0x10, + 0xCD, 0xE9, 0x00, 0x15, + 0x05, 0x4A, 0xB0, 0xF9, 0x94, 0x30, 0x54, 0x3A, 0x41, 0x46, 0x02, 0xF1, + 0x2A, 0x00, 0x0E, 0xF0, + 0x4F, 0xF9, 0xD9, 0xE7, 0x4C, 0x07, 0x10, 0x00, 0x50, 0x09, 0x01, 0x20, + 0x30, 0x05, 0x10, 0x00, + 0x54, 0x07, 0x10, 0x00, 0x40, 0x05, 0x10, 0x00, 0x70, 0xB5, 0x00, 0x28, + 0x04, 0x9E, 0x0D, 0xD0, + 0x00, 0x24, 0x06, 0xE0, 0x30, 0xF9, 0x11, 0x50, 0x9D, 0x42, 0x01, 0xDD, + 0x95, 0x42, 0x00, 0xDB, + 0x64, 0x1C, 0x49, 0x1E, 0xF6, 0xD2, 0xB4, 0x42, 0x01, 0xDD, 0x01, 0x20, + 0x70, 0xBD, 0x00, 0x20, + 0x70, 0xBD, 0x3F, 0x48, 0x00, 0x21, 0x01, 0x70, 0x41, 0x60, 0x70, 0x47, + 0xF9, 0xE7, 0x3C, 0x48, + 0x00, 0x78, 0x70, 0x47, 0x2D, 0xE9, 0xF3, 0x4F, 0xDF, 0xF8, 0xE8, 0xA0, + 0x84, 0x46, 0x4F, 0xF0, + 0x00, 0x08, 0xDA, 0xF8, 0x00, 0x00, 0xDF, 0xF8, 0xD8, 0xB0, 0x81, 0xB0, + 0x90, 0xF8, 0x80, 0x10, + 0x47, 0x46, 0xC9, 0x07, 0xC1, 0x46, 0x2B, 0xD0, 0x33, 0x49, 0x09, 0x78, + 0x03, 0x29, 0x2D, 0xD0, + 0x04, 0x29, 0x2B, 0xD0, 0x05, 0x29, 0x36, 0xD1, 0xB0, 0xF8, 0x88, 0x60, + 0xB0, 0xF8, 0x8A, 0x50, + 0x90, 0xF8, 0x87, 0x40, 0xBC, 0xF1, 0x00, 0x0F, 0x08, 0xD0, 0x00, 0x94, + 0x90, 0xF8, 0x31, 0x10, + 0x2B, 0xB2, 0x32, 0xB2, 0x60, 0x46, 0xFF, 0xF7, 0xB7, 0xFF, 0x80, 0x46, + 0x02, 0x98, 0x50, 0xB1, + 0x00, 0x94, 0xDA, 0xF8, 0x00, 0x00, 0x2B, 0xB2, 0x32, 0xB2, 0x90, 0xF8, + 0x30, 0x10, 0x02, 0x98, + 0xFF, 0xF7, 0xAA, 0xFF, 0x07, 0x46, 0xB8, 0xF1, 0x01, 0x0F, 0x0E, 0xD0, + 0x01, 0x2F, 0x0C, 0xD0, + 0x9B, 0xF8, 0x00, 0x10, 0x58, 0x46, 0x01, 0x29, 0x1D, 0xD0, 0x0A, 0xE0, + 0xB0, 0xF8, 0x82, 0x60, + 0xB0, 0xF8, 0x84, 0x50, 0x90, 0xF8, 0x86, 0x40, 0xD4, 0xE7, 0x9B, 0xF8, + 0x00, 0x10, 0x58, 0x46, + 0x19, 0xB1, 0xCB, 0xF8, 0x04, 0x90, 0xBD, 0xE8, 0xFE, 0x8F, 0x41, 0x68, + 0x49, 0x1C, 0x41, 0x60, + 0xDA, 0xF8, 0x00, 0x20, 0x92, 0xF8, 0x81, 0x20, 0x8A, 0x42, 0xF4, 0xDA, + 0x01, 0x21, 0x01, 0x70, + 0xCB, 0xF8, 0x04, 0x90, 0x0D, 0xE0, 0x41, 0x68, 0x49, 0x1C, 0x41, 0x60, + 0xDA, 0xF8, 0x00, 0x20, + 0x92, 0xF8, 0x81, 0x20, 0x8A, 0x42, 0xE6, 0xDA, 0x80, 0xF8, 0x00, 0x90, + 0x00, 0x21, 0xCB, 0xF8, + 0x04, 0x90, 0x03, 0xB0, 0x07, 0x20, 0xBD, 0xE8, 0xF0, 0x4F, 0x0C, 0xF0, + 0x2C, 0xBD, 0x00, 0x00, + 0x38, 0x05, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, 0x40, 0x05, 0x10, 0x00, + 0x22, 0x49, 0x08, 0xB5, + 0x00, 0x20, 0x48, 0x60, 0x01, 0x22, 0x88, 0x60, 0x0A, 0x70, 0x48, 0x70, + 0x1F, 0x49, 0x08, 0x70, + 0x1F, 0x49, 0x08, 0x70, 0xFF, 0xF7, 0x08, 0xFF, 0xFF, 0xF7, 0x70, 0xFF, + 0xFF, 0xF7, 0x5F, 0xFB, + 0xFF, 0xF7, 0x8F, 0xFD, 0x6B, 0x46, 0x00, 0x22, 0x1E, 0x21, 0x01, 0x20, + 0xFA, 0xF7, 0x52, 0xFB, + 0x00, 0x98, 0x41, 0x07, 0x03, 0xD5, 0xFF, 0xF7, 0x5B, 0xFA, 0x04, 0x21, + 0x08, 0xE0, 0x01, 0x07, + 0x03, 0xD5, 0xFF, 0xF7, 0x73, 0xF8, 0x08, 0x21, 0x02, 0xE0, 0xC1, 0x06, + 0x04, 0xD5, 0x10, 0x21, + 0x01, 0x20, 0xFA, 0xF7, 0x22, 0xFB, 0xE5, 0xE7, 0x80, 0x07, 0xE3, 0xD5, + 0x02, 0x21, 0x01, 0x20, + 0xFA, 0xF7, 0x1B, 0xFB, 0xFF, 0xF7, 0x3F, 0xFB, 0xDC, 0xE7, 0x00, 0xEB, + 0x80, 0x00, 0x4F, 0xF6, + 0xFF, 0x72, 0x02, 0xEA, 0x40, 0x00, 0xFB, 0xF7, 0xAA, 0xBC, 0xFB, 0xF7, + 0xB8, 0xBC, 0x80, 0xB2, + 0xFB, 0xF7, 0xE9, 0xBB, 0xFB, 0xF7, 0xFB, 0xBB, 0x40, 0x05, 0x10, 0x00, + 0x78, 0x07, 0x10, 0x00, + 0x7D, 0x07, 0x10, 0x00, 0x01, 0xF0, 0x41, 0xBA, 0x5E, 0x48, 0x5F, 0x49, + 0x00, 0x7E, 0x09, 0x7E, + 0xC8, 0x42, 0x09, 0xD1, 0x5D, 0x48, 0x00, 0x78, 0x80, 0x06, 0x05, 0xD5, + 0x01, 0x23, 0x5C, 0x4A, + 0x59, 0x49, 0x58, 0x48, 0x00, 0xF0, 0x00, 0xBD, 0x70, 0x47, 0x3C, 0xB5, + 0x00, 0x23, 0x00, 0x93, + 0x01, 0x93, 0x6B, 0x46, 0x07, 0xE0, 0x44, 0x18, 0x64, 0x78, 0x14, 0xB1, + 0x1D, 0x5D, 0x6D, 0x1C, + 0x1D, 0x55, 0x49, 0x1C, 0xC9, 0xB2, 0x91, 0x42, 0xF5, 0xD9, 0x00, 0x20, + 0x01, 0x21, 0x1C, 0x5C, + 0x5A, 0x5C, 0xA2, 0x42, 0x00, 0xD9, 0x08, 0x46, 0x49, 0x1C, 0xC9, 0xB2, + 0x06, 0x29, 0xF6, 0xD3, + 0x3C, 0xBD, 0x70, 0xB5, 0x04, 0x46, 0x00, 0x20, 0x08, 0x70, 0x0E, 0x46, + 0x10, 0x70, 0x45, 0x48, + 0x45, 0x49, 0x15, 0x46, 0x00, 0x7E, 0x09, 0x7E, 0xC8, 0x42, 0x0B, 0xD0, + 0xE2, 0x78, 0xA1, 0x78, + 0x44, 0x48, 0xFF, 0xF7, 0xD2, 0xFF, 0x30, 0x70, 0x62, 0x78, 0x21, 0x78, + 0x42, 0x48, 0xFF, 0xF7, + 0xCC, 0xFF, 0x28, 0x70, 0x70, 0xBD, 0x70, 0xB5, 0x00, 0x24, 0x01, 0x23, + 0x05, 0x7E, 0x08, 0xE0, + 0xC6, 0x18, 0x96, 0xF9, 0x12, 0x60, 0x8E, 0x42, 0x02, 0xDB, 0x96, 0x42, + 0x00, 0xDC, 0x64, 0x1C, + 0x5B, 0x1C, 0x9D, 0x42, 0xF4, 0xD2, 0x01, 0x2C, 0x01, 0xDC, 0x01, 0x20, + 0x70, 0xBD, 0x00, 0x20, + 0x70, 0xBD, 0x2D, 0xE9, 0xF0, 0x43, 0x99, 0x46, 0x00, 0x23, 0x1F, 0x46, + 0x47, 0xF6, 0xFF, 0x76, + 0x4F, 0xF0, 0xFF, 0x3C, 0x01, 0x25, 0x04, 0x7E, 0x0D, 0xE0, 0x00, 0xBF, + 0x00, 0xEB, 0x05, 0x08, + 0x98, 0xF9, 0x12, 0x80, 0x88, 0x45, 0x05, 0xDB, 0x90, 0x45, 0x03, 0xDC, + 0x43, 0x19, 0x93, 0xF9, + 0x12, 0x30, 0x02, 0xE0, 0x6D, 0x1C, 0xAC, 0x42, 0xF0, 0xD2, 0x64, 0x1C, + 0x0A, 0xE0, 0x05, 0x19, + 0x95, 0xF9, 0x12, 0x50, 0x8D, 0x42, 0x05, 0xDB, 0x95, 0x42, 0x03, 0xDC, + 0x20, 0x44, 0x90, 0xF9, + 0x12, 0x70, 0x01, 0xE0, 0x64, 0x1E, 0xF2, 0xD1, 0xBB, 0x42, 0x09, 0xD1, + 0x0A, 0xE0, 0x00, 0xBF, + 0x39, 0xF9, 0x13, 0x00, 0xB0, 0x42, 0x02, 0xDA, 0x06, 0x46, 0x4F, 0xFA, + 0x83, 0xFC, 0x5B, 0x1C, + 0xBB, 0x42, 0xF5, 0xDD, 0x60, 0x46, 0xBD, 0xE8, 0xF0, 0x83, 0x2D, 0xE9, + 0xF0, 0x41, 0x17, 0x46, + 0x0E, 0x46, 0x04, 0x46, 0xC2, 0x78, 0x81, 0x78, 0x0E, 0x48, 0xFF, 0xF7, + 0xA4, 0xFF, 0x05, 0x00, + 0x06, 0xD1, 0xE2, 0x78, 0xA1, 0x78, 0x11, 0x4B, 0x0A, 0x48, 0xFF, 0xF7, + 0xB2, 0xFF, 0x30, 0x70, + 0x62, 0x78, 0x21, 0x78, 0x08, 0x48, 0xFF, 0xF7, 0x96, 0xFF, 0x06, 0x00, + 0x06, 0xD1, 0x62, 0x78, + 0x21, 0x78, 0x0B, 0x4B, 0x04, 0x48, 0xFF, 0xF7, 0xA4, 0xFF, 0x38, 0x70, + 0x05, 0xEA, 0x06, 0x00, + 0xBD, 0xE8, 0xF0, 0x81, 0x18, 0x2A, 0x10, 0x00, 0x58, 0x2A, 0x10, 0x00, + 0x04, 0x07, 0x10, 0x00, + 0x40, 0x2B, 0x10, 0x00, 0x0A, 0x0C, 0x01, 0x20, 0x21, 0x0C, 0x01, 0x20, + 0x94, 0x0B, 0x01, 0x20, + 0xBE, 0x0B, 0x01, 0x20, 0x24, 0x48, 0x00, 0x21, 0x01, 0x61, 0x41, 0x61, + 0x23, 0x48, 0x01, 0x70, + 0x01, 0x21, 0x41, 0x70, 0x70, 0x47, 0x2D, 0xE9, 0xF8, 0x4F, 0x20, 0x4D, + 0x82, 0x46, 0x0E, 0x46, + 0x1D, 0x48, 0x69, 0x78, 0x10, 0x30, 0x01, 0x29, 0x0F, 0xD1, 0x1D, 0x49, + 0x09, 0x68, 0x91, 0xF8, + 0x40, 0x2A, 0x02, 0xF0, 0x0F, 0x03, 0x03, 0x80, 0x12, 0x09, 0x42, 0x80, + 0x91, 0xF8, 0x41, 0x1A, + 0x01, 0xF0, 0x0F, 0x02, 0x82, 0x80, 0x09, 0x09, 0xC1, 0x80, 0x13, 0x49, + 0x00, 0x27, 0x3C, 0x46, + 0x8F, 0x60, 0xCF, 0x60, 0x80, 0x46, 0x89, 0x46, 0x0B, 0xE0, 0x68, 0x78, + 0x00, 0x90, 0x38, 0xF9, + 0x14, 0x30, 0x0A, 0xEB, 0x44, 0x01, 0x09, 0xEB, 0x44, 0x00, 0x01, 0x22, + 0x0D, 0xF0, 0xDE, 0xFF, + 0x64, 0x1C, 0xB4, 0x42, 0xF1, 0xDB, 0x6F, 0x70, 0x03, 0x20, 0xFA, 0xF7, + 0xAA, 0xFA, 0x06, 0x49, + 0x33, 0x46, 0x08, 0x4A, 0x01, 0xF1, 0x08, 0x00, 0x0D, 0xF0, 0xC3, 0xFF, + 0xBD, 0xE8, 0xF8, 0x4F, + 0x03, 0x20, 0xFA, 0xF7, 0xB9, 0xBA, 0x00, 0x00, 0x7A, 0x09, 0x01, 0x20, + 0x55, 0x05, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, 0xFC, 0x4A, 0x01, 0x20, 0x2D, 0xE9, 0xF0, 0x5F, + 0x82, 0x46, 0x5E, 0x48, + 0x99, 0x46, 0x3A, 0xF9, 0x12, 0x30, 0x00, 0x68, 0x01, 0x26, 0x0A, 0x9C, + 0xB0, 0xF9, 0x3E, 0xB2, + 0x03, 0xFB, 0x02, 0xF0, 0x35, 0x46, 0x29, 0xE0, 0x57, 0x1B, 0x7F, 0xB2, + 0x00, 0x2F, 0x0F, 0xDB, + 0x76, 0xB1, 0x3A, 0xF9, 0x17, 0xC0, 0xDC, 0x45, 0x09, 0xDD, 0x0A, 0xEB, + 0x47, 0x08, 0xB8, 0xF9, + 0x02, 0x80, 0xC4, 0x45, 0x03, 0xDC, 0x0C, 0xFB, 0x07, 0x00, 0x63, 0x44, + 0x00, 0xE0, 0x00, 0x26, + 0x57, 0x19, 0x7F, 0xB2, 0x8F, 0x42, 0x0F, 0xDA, 0x76, 0xB1, 0x3A, 0xF9, + 0x17, 0xC0, 0xDC, 0x45, + 0x09, 0xDD, 0x0A, 0xEB, 0x47, 0x08, 0x38, 0xF9, 0x02, 0x8C, 0xC4, 0x45, + 0x03, 0xDC, 0x0C, 0xFB, + 0x07, 0x00, 0x63, 0x44, 0x00, 0xE0, 0x00, 0x26, 0x6D, 0x1C, 0xED, 0xB2, + 0x4D, 0x45, 0xD3, 0xD9, + 0x49, 0x1E, 0x83, 0xFB, 0x01, 0x23, 0x80, 0xFB, 0x04, 0x01, 0x10, 0xF0, + 0x42, 0xFF, 0x00, 0x28, + 0x00, 0xDA, 0x00, 0x20, 0x61, 0x1E, 0x88, 0x42, 0x00, 0xDD, 0x08, 0x46, + 0xBD, 0xE8, 0xF0, 0x9F, + 0x70, 0xB5, 0x00, 0x20, 0x96, 0xB0, 0x04, 0x46, 0x38, 0x4D, 0x34, 0xE0, + 0x22, 0x46, 0x37, 0x49, + 0x01, 0xA8, 0x00, 0xF0, 0x35, 0xF8, 0x34, 0x4E, 0x30, 0x68, 0xB0, 0xF9, + 0x34, 0x10, 0x00, 0x91, + 0x90, 0xF8, 0x40, 0x32, 0x32, 0x48, 0x2A, 0x57, 0x01, 0x78, 0x32, 0x48, + 0xFF, 0xF7, 0x9C, 0xFF, + 0x13, 0x90, 0x30, 0x68, 0xB0, 0xF9, 0x32, 0x10, 0x00, 0x91, 0x90, 0xF8, + 0x40, 0x32, 0x28, 0x19, + 0x90, 0xF9, 0x02, 0x20, 0x2C, 0x48, 0x01, 0x78, 0x2C, 0x48, 0xFF, 0xF7, + 0x8D, 0xFF, 0x14, 0x90, + 0x13, 0xA8, 0x07, 0xF0, 0xB5, 0xF8, 0x14, 0x99, 0xAD, 0xF8, 0x12, 0x10, + 0x13, 0x98, 0xAD, 0xF8, + 0x16, 0x10, 0xAD, 0xF8, 0x10, 0x00, 0xAD, 0xF8, 0x14, 0x00, 0x01, 0xA8, + 0x05, 0xF0, 0xB5, 0xFE, + 0x01, 0x20, 0x64, 0x1C, 0xE4, 0xB2, 0x29, 0x7A, 0xA1, 0x42, 0xC7, 0xD8, + 0x16, 0xB0, 0x70, 0xBD, + 0x70, 0xB5, 0x0E, 0x46, 0x15, 0x46, 0x04, 0x46, 0x48, 0x21, 0x10, 0xF0, + 0xB7, 0xFF, 0x71, 0x19, + 0x08, 0x79, 0x8A, 0x79, 0x50, 0x43, 0x03, 0x22, 0x84, 0xF8, 0x40, 0x20, + 0x18, 0x4A, 0x12, 0x88, + 0x52, 0x1C, 0x62, 0x86, 0xA2, 0x8C, 0x42, 0xF0, 0x20, 0x02, 0xA2, 0x84, + 0xE0, 0x84, 0x60, 0x85, + 0x42, 0x1C, 0x22, 0x85, 0x41, 0xF2, 0x88, 0x32, 0xE2, 0x61, 0xA2, 0x61, + 0x0A, 0x79, 0x8B, 0x79, + 0xD1, 0x18, 0x00, 0xD1, 0x01, 0x21, 0x42, 0x43, 0x58, 0x43, 0x52, 0x00, + 0x40, 0x00, 0xB2, 0xFB, + 0xF1, 0xF2, 0xB0, 0xFB, 0xF1, 0xF0, 0x82, 0x42, 0x02, 0xD9, 0xE2, 0x85, + 0x20, 0x86, 0x70, 0xBD, + 0xE0, 0x85, 0x22, 0x86, 0x70, 0xBD, 0x00, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x0C, 0x2A, 0x10, 0x00, + 0xED, 0x06, 0x10, 0x00, 0x94, 0x0B, 0x01, 0x20, 0xEE, 0x06, 0x10, 0x00, + 0xBE, 0x0B, 0x01, 0x20, + 0x8C, 0x06, 0x10, 0x00, 0x2D, 0xE9, 0xF0, 0x47, 0x00, 0x25, 0x82, 0x46, + 0xA6, 0xB0, 0x50, 0x1E, + 0xC7, 0xB2, 0x2C, 0x46, 0x28, 0x46, 0x6B, 0x46, 0x0D, 0xF1, 0x4C, 0x09, + 0x12, 0xE0, 0x00, 0xBF, + 0x31, 0xF9, 0x10, 0x60, 0x35, 0x44, 0x46, 0x1C, 0xB4, 0x46, 0x95, 0xFB, + 0xF6, 0xF6, 0x23, 0xF8, + 0x10, 0x60, 0x3E, 0x1A, 0x40, 0x1C, 0x31, 0xF9, 0x16, 0x80, 0x44, 0x44, + 0x94, 0xFB, 0xFC, 0xFC, + 0x29, 0xF8, 0x16, 0xC0, 0x90, 0x42, 0xEB, 0xDB, 0x48, 0x46, 0x0C, 0xE0, + 0x33, 0xF9, 0x12, 0x40, + 0x30, 0xF9, 0x12, 0x50, 0x2C, 0x44, 0x31, 0xF8, 0x12, 0x50, 0x04, 0xEB, + 0xD4, 0x74, 0xA5, 0xEB, + 0x64, 0x04, 0x2A, 0xF8, 0x12, 0x40, 0x52, 0x1E, 0xF0, 0xD2, 0x26, 0xB0, + 0xBD, 0xE8, 0xF0, 0x87, + 0x2D, 0xE9, 0xFF, 0x4F, 0x4F, 0xF0, 0x00, 0x0B, 0xA7, 0xB0, 0xC0, 0xF8, + 0x00, 0xB0, 0x81, 0x46, + 0xC0, 0xF8, 0x04, 0xB0, 0x98, 0x46, 0x14, 0x46, 0x5D, 0x46, 0x5E, 0x46, + 0xDA, 0x46, 0x5F, 0x46, + 0x28, 0x21, 0x01, 0xA8, 0x10, 0xF0, 0x32, 0xFF, 0x4C, 0x21, 0x0B, 0xA8, + 0x10, 0xF0, 0x2E, 0xFF, + 0xCD, 0xF8, 0x00, 0x80, 0x02, 0x23, 0x22, 0x46, 0x0B, 0xA8, 0x28, 0x99, + 0x0D, 0xF0, 0x9E, 0xFE, + 0x8E, 0x49, 0x01, 0x20, 0x02, 0x46, 0x00, 0x23, 0x0D, 0xF1, 0x04, 0x0E, + 0xD1, 0xF8, 0x00, 0x80, + 0x17, 0xE0, 0x0B, 0xA9, 0x31, 0xF9, 0x10, 0xC0, 0xB8, 0xF9, 0x4C, 0x12, + 0x8C, 0x45, 0x04, 0xDD, + 0x4F, 0xF0, 0x01, 0x0B, 0x0E, 0xF8, 0x00, 0x20, 0x0A, 0xE0, 0x49, 0x42, + 0x8C, 0x45, 0x05, 0xDA, + 0xFF, 0x21, 0x4F, 0xF0, 0x01, 0x0B, 0x0E, 0xF8, 0x00, 0x10, 0x01, 0xE0, + 0x0E, 0xF8, 0x00, 0x30, + 0x40, 0x1C, 0xA0, 0x42, 0xE5, 0xDB, 0xBB, 0xF1, 0x01, 0x0F, 0x42, 0xD1, + 0x01, 0x20, 0x71, 0x46, + 0x09, 0xE0, 0x0A, 0x5C, 0x32, 0xB1, 0xC5, 0xB2, 0x08, 0x56, 0x00, 0x28, + 0x05, 0xDD, 0x4F, 0xF0, + 0x01, 0x0A, 0x02, 0xE0, 0x40, 0x1C, 0xA0, 0x42, 0xF3, 0xDB, 0x60, 0x1E, + 0x07, 0xE0, 0x0A, 0x5C, + 0x2A, 0xB1, 0xC6, 0xB2, 0x08, 0x56, 0x00, 0x28, 0x03, 0xDA, 0x01, 0x27, + 0x01, 0xE0, 0x40, 0x1E, + 0xF5, 0xD2, 0xA4, 0xF1, 0x01, 0x08, 0xBA, 0xF1, 0x00, 0x0F, 0x01, 0xD0, + 0x97, 0xB9, 0x02, 0xE0, + 0x4F, 0xF0, 0x01, 0x05, 0x77, 0xB9, 0x08, 0xF0, 0xFF, 0x06, 0x0B, 0xE0, + 0xD9, 0xE9, 0x00, 0x47, + 0x2A, 0x46, 0x01, 0x20, 0x00, 0x21, 0x10, 0xF0, 0xF9, 0xFD, 0x04, 0x43, + 0x0F, 0x43, 0xC9, 0xE9, + 0x00, 0x47, 0x6D, 0x1C, 0xB5, 0x42, 0xF1, 0xDD, 0xD9, 0xE9, 0x00, 0x04, + 0x40, 0xF0, 0x01, 0x05, + 0x42, 0x46, 0x01, 0x20, 0x00, 0x21, 0x10, 0xF0, 0xE9, 0xFD, 0x05, 0x43, + 0x0C, 0x43, 0xC9, 0xE9, + 0x00, 0x54, 0x2B, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, 0x2D, 0xE9, 0xF8, 0x43, + 0x05, 0x46, 0x16, 0x46, + 0x0F, 0x46, 0x03, 0x20, 0xFA, 0xF7, 0xFD, 0xF8, 0x55, 0x4C, 0x56, 0x4A, + 0x29, 0x46, 0x23, 0x78, + 0x55, 0x48, 0x0D, 0xF0, 0x16, 0xFE, 0x55, 0x4D, 0x53, 0x48, 0x55, 0x4A, + 0x2B, 0x78, 0x39, 0x46, + 0x2A, 0x30, 0x0D, 0xF0, 0x0E, 0xFE, 0x03, 0x20, 0xFA, 0xF7, 0x06, 0xF9, + 0xDF, 0xF8, 0x2C, 0x81, + 0xD8, 0xF8, 0x00, 0x00, 0x90, 0xF8, 0xE0, 0x13, 0x89, 0x07, 0x12, 0xD5, + 0x90, 0xF8, 0xF4, 0x03, + 0x00, 0x96, 0x00, 0xF0, 0x0F, 0x07, 0x3B, 0x46, 0x22, 0x78, 0x47, 0x49, + 0x49, 0x48, 0x0D, 0xF0, + 0x05, 0xFE, 0x45, 0x49, 0x00, 0x96, 0x3B, 0x46, 0x2A, 0x78, 0x2A, 0x31, + 0x46, 0x48, 0x0D, 0xF0, + 0xFD, 0xFD, 0x22, 0x78, 0x40, 0x49, 0x45, 0x48, 0x00, 0xF0, 0x5B, 0xF8, + 0x3E, 0x49, 0x2A, 0x78, + 0x2A, 0x31, 0x43, 0x48, 0x00, 0xF0, 0x55, 0xF8, 0x42, 0x4F, 0x00, 0x96, + 0x22, 0x78, 0x3F, 0x49, + 0x41, 0x48, 0x3B, 0x68, 0x0D, 0xF0, 0xEA, 0xFD, 0x00, 0x96, 0x2A, 0x78, + 0x3C, 0x49, 0x3F, 0x48, + 0x3B, 0x68, 0x0D, 0xF0, 0xE3, 0xFD, 0xD8, 0xF8, 0x00, 0x00, 0x47, 0x46, + 0x90, 0xF8, 0x30, 0x02, + 0xC0, 0x06, 0x0A, 0xD5, 0x22, 0x78, 0x38, 0x49, 0x39, 0x48, 0xFF, 0xF7, + 0xE3, 0xFE, 0x2A, 0x78, + 0x36, 0x49, 0x38, 0x48, 0xFF, 0xF7, 0xDE, 0xFE, 0x0B, 0xE0, 0x20, 0x78, + 0x32, 0x49, 0x42, 0x00, + 0x33, 0x48, 0x10, 0xF0, 0xB2, 0xFD, 0x28, 0x78, 0x30, 0x49, 0x42, 0x00, + 0x31, 0x48, 0x10, 0xF0, + 0xAC, 0xFD, 0x38, 0x68, 0x90, 0xF8, 0x30, 0x02, 0x80, 0x06, 0x04, 0xD5, + 0x2C, 0x49, 0x22, 0x78, + 0x08, 0x46, 0x00, 0xF0, 0x30, 0xF8, 0x38, 0x68, 0x90, 0xF8, 0x30, 0x02, + 0x40, 0x06, 0x04, 0xD5, + 0x28, 0x49, 0x2A, 0x78, 0x08, 0x46, 0x00, 0xF0, 0x26, 0xF8, 0x33, 0x46, + 0x22, 0x78, 0x1A, 0x49, + 0x25, 0x48, 0xFF, 0xF7, 0xED, 0xFE, 0x2A, 0x78, 0x01, 0xB0, 0x33, 0x46, + 0xBD, 0xE8, 0xF0, 0x43, + 0x15, 0x49, 0x21, 0x48, 0x2A, 0x31, 0x08, 0x30, 0xE2, 0xE6, 0x20, 0x49, + 0x00, 0x20, 0x08, 0x70, + 0x70, 0x47, 0x30, 0xB5, 0x00, 0x23, 0x03, 0x80, 0x01, 0x23, 0x09, 0xE0, + 0x00, 0xEB, 0x43, 0x04, + 0x31, 0xF8, 0x13, 0x50, 0x34, 0xF8, 0x02, 0x4C, 0x2C, 0x44, 0x20, 0xF8, + 0x13, 0x40, 0x5B, 0x1C, + 0x93, 0x42, 0xF3, 0xDB, 0x30, 0xBD, 0x08, 0xB5, 0x00, 0x92, 0x04, 0x4A, + 0x12, 0x68, 0xB2, 0xF9, + 0x4A, 0x32, 0xB2, 0xF9, 0x48, 0x22, 0x02, 0xF0, 0x78, 0xFA, 0x08, 0xBD, + 0x4C, 0x07, 0x10, 0x00, + 0xED, 0x06, 0x10, 0x00, 0x7E, 0x4A, 0x01, 0x20, 0x92, 0x09, 0x01, 0x20, + 0xEE, 0x06, 0x10, 0x00, + 0xA8, 0x4A, 0x01, 0x20, 0xC8, 0x2A, 0x10, 0x00, 0xF2, 0x2A, 0x10, 0x00, + 0xA0, 0x0A, 0x01, 0x20, + 0xCE, 0x0A, 0x01, 0x20, 0x9C, 0x06, 0x10, 0x00, 0x1E, 0x0B, 0x01, 0x20, + 0x48, 0x0B, 0x01, 0x20, + 0x94, 0x0B, 0x01, 0x20, 0xBE, 0x0B, 0x01, 0x20, 0x50, 0x2B, 0x10, 0x00, + 0xF4, 0x06, 0x10, 0x00, + 0x10, 0xB5, 0x40, 0x21, 0xFE, 0x48, 0x10, 0xF0, 0xE1, 0xFD, 0xFD, 0x48, + 0x40, 0x21, 0x40, 0x30, + 0x10, 0xF0, 0xDC, 0xFD, 0xFA, 0x48, 0x00, 0x21, 0x80, 0x30, 0xC0, 0xE9, + 0x00, 0x11, 0xC0, 0xE9, + 0x02, 0x11, 0x10, 0x30, 0xC0, 0xE9, 0x00, 0x11, 0xC0, 0xE9, 0x02, 0x11, + 0x10, 0x30, 0xC0, 0xE9, + 0x00, 0x11, 0xC0, 0xE9, 0x02, 0x11, 0x08, 0x46, 0xF2, 0x49, 0x08, 0x60, + 0x10, 0xBD, 0x2D, 0xE9, + 0xFF, 0x5F, 0x06, 0x46, 0x89, 0x46, 0xF0, 0x48, 0xF0, 0x49, 0x98, 0x46, + 0x00, 0x78, 0x09, 0x68, + 0x14, 0x46, 0x01, 0x28, 0x50, 0xD0, 0xB1, 0xF9, 0x86, 0xA2, 0x91, 0xF8, + 0x88, 0x52, 0xB1, 0xF9, + 0x82, 0x72, 0x11, 0x46, 0x48, 0x46, 0x0D, 0xF0, 0xAE, 0xFD, 0x83, 0x46, + 0x21, 0x46, 0x48, 0x46, + 0x0D, 0xF0, 0xB4, 0xFD, 0x00, 0x90, 0x39, 0x46, 0x58, 0x46, 0x0C, 0xF0, + 0x91, 0xFD, 0x0F, 0xFA, + 0x80, 0xFB, 0x39, 0x46, 0x00, 0x98, 0x0C, 0xF0, 0x8B, 0xFD, 0x00, 0xB2, + 0xAB, 0xEB, 0x00, 0x00, + 0x68, 0x43, 0x64, 0x21, 0x90, 0xFB, 0xF1, 0xF0, 0x38, 0x44, 0x05, 0xB2, + 0xA8, 0xF8, 0x08, 0x50, + 0x70, 0x1C, 0x00, 0x21, 0x03, 0x90, 0xC8, 0xF8, 0x00, 0x10, 0xC8, 0xF8, + 0x04, 0x10, 0xB9, 0xF9, + 0x00, 0x00, 0xA8, 0x42, 0x02, 0xDD, 0x03, 0x99, 0x01, 0x20, 0x08, 0x70, + 0x09, 0xEB, 0x44, 0x00, + 0x76, 0x1C, 0x30, 0xF9, 0x02, 0x2C, 0x31, 0x19, 0xAA, 0x42, 0x02, 0xDD, + 0x01, 0x22, 0x01, 0xF8, + 0x01, 0x2C, 0xB9, 0xF9, 0x02, 0x30, 0x03, 0xEB, 0x0A, 0x02, 0x12, 0xB2, + 0xAB, 0x42, 0x18, 0xDD, + 0xB9, 0xF9, 0x00, 0x30, 0x93, 0x42, 0x03, 0xDD, 0xB9, 0xF9, 0x04, 0x30, + 0x93, 0x42, 0x0A, 0xDC, + 0x03, 0x9B, 0x01, 0x22, 0x5A, 0x70, 0x0C, 0xE0, 0xB1, 0xF9, 0x69, 0xA2, + 0x91, 0xF8, 0x64, 0x52, + 0xB1, 0xF9, 0x65, 0x72, 0xAD, 0xE7, 0xD8, 0xF8, 0x00, 0x60, 0x46, 0xF0, + 0x02, 0x06, 0xC8, 0xF8, + 0x00, 0x60, 0x30, 0xF9, 0x04, 0x3C, 0xA4, 0xF1, 0x02, 0x0B, 0x03, 0xEB, + 0x0A, 0x02, 0x12, 0xB2, + 0xAB, 0x42, 0x16, 0xDD, 0x30, 0xF9, 0x06, 0x3C, 0x93, 0x42, 0x03, 0xDD, + 0x30, 0xF9, 0x02, 0x0C, + 0x90, 0x42, 0x03, 0xDC, 0x01, 0x20, 0x01, 0xF8, 0x02, 0x0C, 0x0A, 0xE0, + 0xD8, 0xE9, 0x00, 0x64, + 0x5A, 0x46, 0x01, 0x20, 0x00, 0x21, 0x10, 0xF0, 0x69, 0xFC, 0x06, 0x43, + 0x0C, 0x43, 0xC8, 0xE9, + 0x00, 0x64, 0x02, 0x24, 0x58, 0xE0, 0x00, 0xBF, 0x39, 0xF9, 0x14, 0x00, + 0xA8, 0x42, 0x52, 0xDD, + 0x09, 0xEB, 0x44, 0x01, 0x02, 0x91, 0x50, 0x44, 0x31, 0xF9, 0x02, 0x1C, + 0x00, 0xB2, 0x81, 0x42, + 0x04, 0xDD, 0x02, 0x9A, 0xB2, 0xF9, 0x02, 0x20, 0x82, 0x42, 0x10, 0xDC, + 0x02, 0x9A, 0x32, 0xF9, + 0x04, 0x2C, 0x82, 0x42, 0x04, 0xDD, 0x02, 0x9A, 0xB2, 0xF9, 0x02, 0x20, + 0x82, 0x42, 0x06, 0xDC, + 0x81, 0x42, 0x35, 0xDD, 0x02, 0x99, 0xB1, 0xF9, 0x04, 0x10, 0x81, 0x42, + 0x30, 0xDD, 0xD8, 0xE9, + 0x00, 0x67, 0x22, 0x46, 0x01, 0x20, 0x00, 0x21, 0x10, 0xF0, 0x38, 0xFC, + 0xCD, 0xE9, 0x00, 0x01, + 0x06, 0x43, 0x0F, 0x43, 0x62, 0x1E, 0x01, 0x20, 0x00, 0x21, 0xC8, 0xE9, + 0x00, 0x67, 0x10, 0xF0, + 0x2D, 0xFC, 0x02, 0x46, 0x0B, 0x46, 0x06, 0xEA, 0x02, 0x00, 0x07, 0xEA, + 0x03, 0x01, 0x08, 0x43, + 0x19, 0xD0, 0x02, 0x98, 0x39, 0xF9, 0x14, 0x10, 0x30, 0xF9, 0x02, 0x0C, + 0x81, 0x42, 0x09, 0xDA, + 0x96, 0x43, 0x9F, 0x43, 0xC8, 0xE9, 0x00, 0x67, 0x03, 0x99, 0x01, 0x20, + 0x21, 0x44, 0x01, 0xF8, + 0x01, 0x0C, 0x08, 0xE0, 0xDD, 0xE9, 0x00, 0x10, 0x8E, 0x43, 0x87, 0x43, + 0xC8, 0xE9, 0x00, 0x67, + 0x03, 0x99, 0x01, 0x20, 0x08, 0x55, 0x64, 0x1C, 0x5C, 0x45, 0xA5, 0xD3, + 0xBD, 0xE8, 0xFF, 0x9F, + 0x2D, 0xE9, 0xFF, 0x4F, 0x81, 0xB0, 0x82, 0x46, 0x00, 0x21, 0x1D, 0x46, + 0x19, 0x76, 0x06, 0x20, + 0x90, 0x46, 0xC3, 0x1F, 0x7A, 0x4C, 0x07, 0xE0, 0x00, 0x22, 0x29, 0x18, + 0x0A, 0x73, 0x8B, 0x74, + 0x05, 0xEB, 0x40, 0x01, 0x22, 0x88, 0x0A, 0x80, 0x40, 0x1E, 0xF5, 0xD2, + 0x72, 0x48, 0x01, 0x78, + 0x72, 0x48, 0x01, 0x29, 0x00, 0x68, 0x06, 0xD0, 0xB0, 0xF9, 0x84, 0x22, + 0x4F, 0xF0, 0x00, 0x0B, + 0x59, 0x46, 0x58, 0x46, 0x16, 0xE0, 0xB0, 0xF9, 0x67, 0x22, 0xF7, 0xE7, + 0x0A, 0xEB, 0x00, 0x03, + 0x5C, 0x78, 0x74, 0xB1, 0x1A, 0xF8, 0x00, 0x40, 0x0C, 0xB9, 0x49, 0x1C, + 0xC9, 0xB2, 0x59, 0x70, + 0x02, 0x9B, 0x33, 0xF9, 0x10, 0x30, 0x93, 0x42, 0x03, 0xDD, 0x01, 0x23, + 0x8B, 0x40, 0x43, 0xEA, + 0x0B, 0x0B, 0x40, 0x1C, 0x40, 0x45, 0xE9, 0xD3, 0x62, 0x48, 0x00, 0x90, + 0x0E, 0x98, 0x4F, 0xF0, + 0x00, 0x09, 0x4C, 0x46, 0xC0, 0xE9, 0x00, 0x99, 0x37, 0xE0, 0x00, 0xBF, + 0x0A, 0xEB, 0x04, 0x00, + 0x42, 0x78, 0x8A, 0xB3, 0x01, 0x21, 0x91, 0x40, 0x11, 0xEA, 0x0B, 0x0F, + 0x02, 0xD1, 0x00, 0x21, + 0x41, 0x70, 0x29, 0xE0, 0x1A, 0xF8, 0x04, 0x10, 0x39, 0xB9, 0x09, 0xF1, + 0x01, 0x01, 0x01, 0xF0, + 0xFF, 0x09, 0x54, 0x49, 0x00, 0x91, 0x05, 0xF8, 0x19, 0x40, 0x80, 0xF8, + 0x01, 0x90, 0x0E, 0x98, + 0x22, 0x46, 0x00, 0x21, 0xD0, 0xE9, 0x00, 0x67, 0x01, 0x20, 0x10, 0xF0, + 0x9F, 0xFB, 0x06, 0x43, + 0x0E, 0x98, 0x0F, 0x43, 0xC0, 0xE9, 0x00, 0x67, 0x05, 0xEB, 0x09, 0x00, + 0x01, 0x7B, 0x49, 0x1C, + 0x01, 0x73, 0x05, 0xEB, 0x49, 0x01, 0x4C, 0x70, 0x02, 0x99, 0x00, 0x9A, + 0x31, 0xF9, 0x14, 0x10, + 0x91, 0x42, 0x01, 0xDD, 0x00, 0x91, 0x84, 0x74, 0x64, 0x1C, 0x44, 0x45, + 0xC6, 0xD3, 0x85, 0xF8, + 0x18, 0x90, 0x05, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, 0x2D, 0xE9, 0xF8, 0x4F, + 0x0C, 0x46, 0x05, 0x46, + 0x1E, 0x46, 0x91, 0x46, 0x17, 0x21, 0x3C, 0x48, 0x10, 0xF0, 0x2E, 0xFC, + 0x28, 0x21, 0x3B, 0x48, + 0x10, 0xF0, 0x2A, 0xFC, 0xDF, 0xF8, 0xE8, 0xA0, 0x3A, 0x4F, 0x09, 0xF1, + 0x08, 0x08, 0x9A, 0xF8, + 0x00, 0x10, 0x76, 0xB1, 0x0A, 0x46, 0x05, 0xF1, 0x20, 0x03, 0x37, 0x49, + 0x32, 0x48, 0xFF, 0xF7, + 0x76, 0xFE, 0x04, 0xF1, 0x20, 0x03, 0x3A, 0x78, 0x34, 0x49, 0x30, 0x48, + 0xFF, 0xF7, 0x6F, 0xFE, + 0x11, 0xE0, 0x4A, 0x46, 0x2C, 0x48, 0x00, 0xF0, 0x3D, 0xFA, 0x42, 0x46, + 0x39, 0x78, 0x2B, 0x48, + 0x00, 0xF0, 0x38, 0xFA, 0x00, 0x20, 0xC5, 0xE9, 0x08, 0x00, 0xC4, 0xE9, + 0x08, 0x00, 0x2C, 0x48, + 0x00, 0x88, 0x28, 0x85, 0x20, 0x85, 0x2B, 0x4E, 0x28, 0x8D, 0x2B, 0x46, + 0x26, 0xF8, 0x11, 0x0F, + 0x20, 0x8D, 0x70, 0x80, 0xCD, 0xF8, 0x00, 0x90, 0x9A, 0xF8, 0x00, 0x20, + 0x22, 0x49, 0x1E, 0x48, + 0xFF, 0xF7, 0x36, 0xFF, 0xCD, 0xF8, 0x00, 0x80, 0x23, 0x46, 0x3A, 0x78, + 0x1F, 0x49, 0x1B, 0x48, + 0xFF, 0xF7, 0x2E, 0xFF, 0x29, 0x7E, 0x30, 0x79, 0x61, 0xF3, 0x03, 0x00, + 0x30, 0x71, 0x21, 0x7E, + 0x61, 0xF3, 0x07, 0x10, 0x30, 0x71, 0xBD, 0xE8, 0xF8, 0x8F, 0x2D, 0xE9, + 0xFF, 0x4F, 0x15, 0x46, + 0x04, 0x46, 0xD1, 0xE9, 0x02, 0x70, 0xD1, 0xE9, 0x00, 0x92, 0x80, 0x46, + 0x81, 0xB0, 0x92, 0x46, + 0x4F, 0xF4, 0x66, 0x71, 0x28, 0x46, 0x10, 0xF0, 0xCF, 0xFB, 0x00, 0x20, + 0x01, 0x46, 0x00, 0x90, + 0x0E, 0x98, 0x01, 0x60, 0x00, 0x21, 0x28, 0x46, 0x10, 0xF0, 0xD8, 0xFC, + 0x83, 0x46, 0x1B, 0xE0, + 0x18, 0x2A, 0x10, 0x00, 0x58, 0x05, 0x10, 0x00, 0x66, 0x05, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, + 0xFC, 0x78, 0x01, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0x0A, 0x0C, 0x01, 0x20, + 0x21, 0x0C, 0x01, 0x20, + 0xED, 0x06, 0x10, 0x00, 0xEE, 0x06, 0x10, 0x00, 0x94, 0x0B, 0x01, 0x20, + 0xBE, 0x0B, 0x01, 0x20, + 0x68, 0x05, 0x10, 0x00, 0x0A, 0x44, 0x01, 0x20, 0x00, 0x21, 0x20, 0x46, + 0x10, 0xF0, 0xAE, 0xFC, + 0x06, 0x46, 0x00, 0x25, 0x34, 0xE0, 0x2A, 0x46, 0x01, 0x20, 0x00, 0x21, + 0x10, 0xF0, 0xE6, 0xFA, + 0x00, 0xEA, 0x09, 0x00, 0x01, 0xEA, 0x0A, 0x01, 0x08, 0x43, 0x22, 0xD0, + 0x00, 0x24, 0x1C, 0xE0, + 0x22, 0x46, 0x01, 0x20, 0x00, 0x21, 0x10, 0xF0, 0xD9, 0xFA, 0x38, 0x40, + 0x01, 0xEA, 0x08, 0x01, + 0x08, 0x43, 0x11, 0xD0, 0x36, 0xF9, 0x14, 0x10, 0x04, 0x98, 0x81, 0x42, + 0x0C, 0xDD, 0x01, 0x20, + 0x0B, 0xF8, 0x04, 0x00, 0x00, 0x98, 0x40, 0x1C, 0x00, 0x90, 0x0E, 0x98, + 0x36, 0xF9, 0x14, 0x10, + 0x00, 0x68, 0x01, 0x44, 0x0E, 0x98, 0x01, 0x60, 0x64, 0x1C, 0xFE, 0x48, + 0x00, 0x78, 0x84, 0x42, + 0xDE, 0xDB, 0xFC, 0x48, 0x0B, 0xF1, 0x28, 0x0B, 0x6D, 0x1C, 0x00, 0x78, + 0x06, 0xEB, 0x40, 0x06, + 0xF9, 0x48, 0x00, 0x78, 0x85, 0x42, 0xC6, 0xDB, 0x00, 0x98, 0x2A, 0xE7, + 0x2D, 0xE9, 0xF0, 0x47, + 0x0F, 0x46, 0x1C, 0x46, 0x16, 0x46, 0x99, 0x78, 0x10, 0xF0, 0x62, 0xFC, + 0x05, 0x46, 0xA1, 0x78, + 0x38, 0x46, 0x10, 0xF0, 0x63, 0xFC, 0x02, 0x46, 0xEE, 0x48, 0x00, 0x23, + 0xA1, 0x78, 0x90, 0xF8, + 0x00, 0x90, 0x94, 0xF8, 0x03, 0xC0, 0x13, 0xE0, 0x20, 0x78, 0x67, 0x78, + 0x09, 0xE0, 0x00, 0xBF, + 0x15, 0xF8, 0x00, 0x80, 0xB0, 0x45, 0x02, 0xD1, 0x32, 0xF9, 0x10, 0x80, + 0x43, 0x44, 0x40, 0x1C, + 0xC0, 0xB2, 0x87, 0x42, 0xF4, 0xD2, 0x49, 0x1C, 0x02, 0xEB, 0x49, 0x02, + 0xC9, 0xB2, 0x28, 0x35, + 0x8C, 0x45, 0xE9, 0xD2, 0x18, 0x46, 0xBD, 0xE8, 0xF0, 0x87, 0x2D, 0xE9, + 0xF0, 0x4F, 0xDF, 0x48, + 0x87, 0xB0, 0xDF, 0xF8, 0x80, 0x83, 0x06, 0x68, 0x00, 0x20, 0x06, 0xF5, + 0x88, 0x74, 0x86, 0xF8, + 0x0E, 0x01, 0xDB, 0x48, 0x01, 0x23, 0x00, 0x22, 0x00, 0x68, 0x30, 0x60, + 0x31, 0x46, 0xD8, 0xF8, + 0x00, 0x00, 0x08, 0xF0, 0x85, 0xF8, 0xDF, 0xF8, 0x60, 0xA3, 0xDF, 0xF8, + 0x60, 0x93, 0x4F, 0xF0, + 0xFF, 0x37, 0x9A, 0xF8, 0x00, 0x00, 0x01, 0x28, 0x10, 0xD1, 0xD5, 0x48, + 0x00, 0x68, 0x90, 0xF8, + 0x51, 0x02, 0xC0, 0x07, 0x01, 0xD0, 0x02, 0x23, 0x00, 0xE0, 0x01, 0x23, + 0x32, 0x46, 0xD9, 0xF8, + 0x00, 0x10, 0xD8, 0xF8, 0x00, 0x00, 0x00, 0xF0, 0x4D, 0xF9, 0x07, 0x46, + 0xCD, 0x49, 0x96, 0xF8, + 0x0E, 0x01, 0x48, 0x71, 0x00, 0x20, 0x01, 0x25, 0x05, 0x90, 0x51, 0xE0, + 0x01, 0x20, 0xA8, 0x40, + 0x38, 0x42, 0x4C, 0xD0, 0x06, 0xEB, 0x85, 0x00, 0x03, 0x90, 0x06, 0xEB, + 0x45, 0x00, 0x48, 0x21, + 0xB0, 0xF8, 0xB4, 0xB0, 0x20, 0x46, 0x10, 0xF0, 0x11, 0xFB, 0x03, 0x20, + 0x84, 0xF8, 0x40, 0x00, + 0xD9, 0xF8, 0x00, 0x00, 0x60, 0x60, 0xA0, 0x60, 0x84, 0xF8, 0x42, 0x50, + 0xA4, 0xF8, 0x2A, 0xB0, + 0xA4, 0xF8, 0x26, 0xB0, 0xE9, 0xB2, 0x23, 0x46, 0x03, 0x9A, 0xD8, 0xF8, + 0x00, 0x00, 0x07, 0xF0, + 0x40, 0xF8, 0x9A, 0xF8, 0x00, 0x00, 0x02, 0x28, 0x10, 0xD1, 0xB7, 0x48, + 0x04, 0xA9, 0xEA, 0xB2, + 0xB0, 0xF9, 0x00, 0x00, 0x02, 0x94, 0xCD, 0xE9, 0x00, 0x10, 0xD8, 0xF8, + 0x00, 0x10, 0x03, 0x9B, + 0x08, 0x46, 0x07, 0xF0, 0x68, 0xF8, 0xDD, 0xE9, 0x03, 0x01, 0x01, 0x60, + 0xE0, 0x8C, 0xB0, 0xB1, + 0xAD, 0x48, 0xB4, 0xF9, 0x32, 0x10, 0xB0, 0xF9, 0x00, 0x00, 0x81, 0x42, + 0x0F, 0xDD, 0x01, 0x20, + 0x05, 0x90, 0xE9, 0xB2, 0x23, 0x46, 0x03, 0x9A, 0xD8, 0xF8, 0x00, 0x00, + 0x08, 0xF0, 0x4A, 0xFA, + 0xE9, 0xB2, 0x23, 0x46, 0x03, 0x9A, 0xD8, 0xF8, 0x00, 0x00, 0x06, 0xF0, + 0x91, 0xFF, 0x6D, 0x1C, + 0x96, 0xF8, 0x0E, 0x01, 0xA8, 0x42, 0xA9, 0xD2, 0x05, 0x98, 0x07, 0xB0, + 0x6A, 0xE6, 0x9F, 0x49, + 0x10, 0xB5, 0x09, 0x78, 0x00, 0x20, 0x11, 0xB1, 0xFF, 0xF7, 0x67, 0xFF, + 0x04, 0xE0, 0x9C, 0x49, + 0x09, 0x7A, 0x09, 0xB1, 0xFF, 0xF7, 0xBC, 0xFA, 0x9A, 0x4A, 0x00, 0x21, + 0x11, 0x70, 0x9A, 0x4A, + 0x10, 0x70, 0x9A, 0x48, 0x01, 0x70, 0x10, 0xBD, 0x10, 0xB5, 0x91, 0x4C, + 0x20, 0x68, 0x90, 0xF8, + 0x30, 0x12, 0x09, 0x07, 0x1C, 0xD5, 0x96, 0x49, 0x09, 0x78, 0x11, 0xF0, + 0x30, 0x0F, 0x17, 0xD0, + 0xB0, 0xF9, 0x44, 0x12, 0x01, 0x22, 0x93, 0x48, 0x00, 0xF0, 0xEB, 0xFA, + 0x20, 0x68, 0x01, 0x22, + 0xB0, 0xF9, 0x46, 0x12, 0x8F, 0x48, 0x10, 0x30, 0x00, 0xF0, 0xE3, 0xFA, + 0x20, 0x68, 0x01, 0x22, + 0xB0, 0xF9, 0x42, 0x12, 0x8B, 0x48, 0xBD, 0xE8, 0x10, 0x40, 0x10, 0x38, + 0x00, 0xF0, 0xD9, 0xBA, + 0x88, 0x48, 0x4F, 0xF0, 0xFF, 0x31, 0xC0, 0xE9, 0x00, 0x11, 0xC0, 0xE9, + 0x02, 0x11, 0x10, 0x38, + 0xC0, 0xE9, 0x00, 0x11, 0xC0, 0xE9, 0x02, 0x11, 0x20, 0x30, 0xC0, 0xE9, + 0x00, 0x11, 0xC0, 0xE9, + 0x02, 0x11, 0x10, 0xBD, 0x2D, 0xE9, 0xFF, 0x5F, 0x15, 0x46, 0x03, 0x99, + 0x91, 0xF8, 0x40, 0x10, + 0x02, 0x29, 0x0C, 0xD0, 0x01, 0x29, 0x0C, 0xD0, 0x7A, 0x49, 0x10, 0x39, + 0x8A, 0x46, 0x4F, 0xF0, + 0x00, 0x0B, 0xA9, 0x78, 0x10, 0xF0, 0x5A, 0xFB, 0x81, 0x46, 0xA8, 0x78, + 0x2A, 0xE0, 0x75, 0x49, + 0xF4, 0xE7, 0x74, 0x49, 0x10, 0x31, 0xF1, 0xE7, 0x2C, 0x78, 0x1C, 0xE0, + 0x19, 0xF8, 0x04, 0x10, + 0x01, 0x98, 0x81, 0x42, 0x16, 0xD1, 0x01, 0x22, 0xDA, 0xF8, 0x00, 0x00, + 0x02, 0xFA, 0x08, 0xF2, + 0x10, 0x40, 0x00, 0x00, 0x24, 0xD0, 0xDA, 0xE9, 0x02, 0x76, 0x22, 0x46, + 0x01, 0x20, 0x00, 0x21, + 0x10, 0xF0, 0x74, 0xF9, 0x07, 0x40, 0x0E, 0x40, 0x37, 0x43, 0x19, 0xD0, + 0x0B, 0xF1, 0x01, 0x00, + 0x1F, 0xFA, 0x80, 0xFB, 0x64, 0x1C, 0x68, 0x78, 0xA0, 0x42, 0xDF, 0xDA, + 0x09, 0xF1, 0x28, 0x09, + 0x08, 0xF1, 0x01, 0x00, 0xE9, 0x78, 0x80, 0x46, 0x41, 0x45, 0xD5, 0xDA, + 0x03, 0x99, 0x5F, 0xEA, + 0x0B, 0x00, 0xA1, 0xF8, 0x26, 0xB0, 0x00, 0xD0, 0x01, 0x20, 0x04, 0xB0, + 0xBD, 0xE8, 0xF0, 0x9F, + 0x00, 0x21, 0x09, 0xF8, 0x04, 0x10, 0xE5, 0xE7, 0x2D, 0xE9, 0xF8, 0x4F, + 0x80, 0x46, 0x4F, 0xF0, + 0x00, 0x0B, 0x87, 0x78, 0x90, 0xF8, 0x03, 0xA0, 0x23, 0xE0, 0x00, 0xBF, + 0x52, 0x49, 0x01, 0x22, + 0xBA, 0x40, 0x08, 0x68, 0x98, 0xF8, 0x00, 0x40, 0x10, 0x40, 0x00, 0x90, + 0xD1, 0xE9, 0x02, 0x56, + 0x98, 0xF8, 0x01, 0x90, 0x12, 0xE0, 0x00, 0x99, 0x09, 0x00, 0x0E, 0xD0, + 0x22, 0x46, 0x01, 0x20, + 0x00, 0x21, 0x10, 0xF0, 0x33, 0xF9, 0x05, 0xEA, 0x00, 0x02, 0x06, 0xEA, + 0x01, 0x00, 0x02, 0x43, + 0x03, 0xD0, 0x0B, 0xF1, 0x01, 0x00, 0x1F, 0xFA, 0x80, 0xFB, 0x64, 0x1C, + 0xA1, 0x45, 0xEA, 0xDA, + 0x7F, 0x1C, 0xBA, 0x45, 0xDA, 0xDA, 0xBB, 0xF1, 0x00, 0x0F, 0x01, 0xD0, + 0x01, 0x20, 0xF2, 0xE5, + 0x00, 0x20, 0xF0, 0xE5, 0x2D, 0xE9, 0xF0, 0x47, 0x91, 0x46, 0x45, 0x1C, + 0x0C, 0x46, 0x4F, 0xF0, + 0x01, 0x08, 0x0C, 0xE0, 0xD9, 0xE9, 0x00, 0x67, 0x22, 0x46, 0x01, 0x20, + 0x00, 0x21, 0x10, 0xF0, + 0x0D, 0xF9, 0x06, 0x40, 0x0F, 0x40, 0x3E, 0x43, 0x01, 0xD0, 0x05, 0xF8, + 0x04, 0x80, 0x64, 0x1E, + 0xF0, 0xD2, 0x80, 0xE6, 0x2D, 0xE9, 0xF0, 0x47, 0x92, 0xF8, 0x0E, 0x41, + 0x86, 0xB0, 0x1D, 0x46, + 0x17, 0x46, 0x89, 0x46, 0x82, 0x46, 0xE8, 0x46, 0x05, 0x2C, 0x02, 0xD9, + 0x05, 0x24, 0x01, 0x26, + 0x01, 0xE0, 0x01, 0x26, 0x0C, 0xE0, 0x07, 0xEB, 0x86, 0x03, 0x32, 0x46, + 0x49, 0x46, 0x50, 0x46, + 0xFF, 0xF7, 0x3C, 0xFE, 0x08, 0xEB, 0x86, 0x01, 0x76, 0x1C, 0x41, 0xF8, + 0x04, 0x0C, 0xF6, 0xB2, + 0xA6, 0x42, 0xF0, 0xD9, 0x00, 0x20, 0x03, 0x46, 0x01, 0x22, 0x40, 0xE0, + 0x00, 0x27, 0x4F, 0xF0, + 0x00, 0x46, 0x01, 0x21, 0x0F, 0xE0, 0x00, 0xBF, 0x02, 0xFA, 0x01, 0xFC, + 0x1C, 0xEA, 0x00, 0x0F, + 0x07, 0xD1, 0x08, 0xEB, 0x81, 0x0C, 0x5C, 0xF8, 0x04, 0xCC, 0xB4, 0x45, + 0x01, 0xDD, 0x66, 0x46, + 0x0F, 0x46, 0x49, 0x1C, 0xC9, 0xB2, 0xA1, 0x42, 0xEE, 0xD9, 0x02, 0xFA, + 0x07, 0xF1, 0x08, 0x43, + 0x5B, 0x1C, 0x23, 0xE0, 0xEE, 0x06, 0x10, 0x00, 0xED, 0x06, 0x10, 0x00, + 0x58, 0x05, 0x10, 0x00, + 0x88, 0x06, 0x10, 0x00, 0x50, 0x06, 0x10, 0x00, 0x66, 0x05, 0x10, 0x00, + 0x14, 0x07, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, 0x0A, 0x44, 0x01, 0x20, 0x92, 0x06, 0x10, 0x00, + 0x57, 0x05, 0x10, 0x00, + 0x0C, 0x2A, 0x10, 0x00, 0xF6, 0x06, 0x10, 0x00, 0xF7, 0x06, 0x10, 0x00, + 0xF8, 0x06, 0x10, 0x00, + 0x04, 0x07, 0x10, 0x00, 0xA8, 0x2A, 0x10, 0x00, 0x50, 0x2B, 0x10, 0x00, + 0xDB, 0xB2, 0xAB, 0x42, + 0xBC, 0xD3, 0x06, 0xB0, 0x17, 0xE6, 0x00, 0x00, 0xA1, 0x4A, 0x10, 0xB5, + 0x13, 0x68, 0x00, 0x21, + 0xB3, 0xF8, 0x14, 0x22, 0x82, 0x42, 0x05, 0xDA, 0x93, 0xF8, 0x10, 0x22, + 0x52, 0x07, 0x01, 0xD5, + 0x02, 0x21, 0x08, 0xE0, 0xB3, 0xF8, 0x12, 0x22, 0x82, 0x42, 0x04, 0xDA, + 0x93, 0xF8, 0x10, 0x02, + 0x80, 0x07, 0x00, 0xD5, 0x01, 0x21, 0x97, 0x48, 0x97, 0x4C, 0x42, 0x78, + 0x91, 0x42, 0x14, 0xD1, + 0xA1, 0x78, 0x91, 0x42, 0x0D, 0xD0, 0x95, 0x4B, 0x1B, 0x78, 0x1A, 0x43, + 0x94, 0x4B, 0x1B, 0x78, + 0x1A, 0x43, 0x07, 0xD0, 0x41, 0x68, 0x49, 0x1E, 0x41, 0x60, 0x00, 0x29, + 0x01, 0xDC, 0x40, 0x78, + 0xA0, 0x70, 0x10, 0xBD, 0x41, 0x70, 0x41, 0x68, 0xF7, 0xE7, 0x59, 0xB1, + 0x02, 0x29, 0x01, 0xD1, + 0x01, 0x2A, 0x07, 0xD0, 0x93, 0xF8, 0x18, 0x22, 0x42, 0x60, 0x41, 0x70, + 0x00, 0x2A, 0xF0, 0xDC, + 0xA1, 0x70, 0x10, 0xBD, 0x93, 0xF8, 0x19, 0x22, 0xF6, 0xE7, 0x70, 0xB5, + 0x85, 0x4A, 0x86, 0x4B, + 0x81, 0x4D, 0x16, 0x78, 0x7E, 0x4A, 0x1C, 0x78, 0x00, 0x21, 0x12, 0x68, + 0xAB, 0x78, 0x01, 0x2E, + 0x02, 0xD1, 0x82, 0x4E, 0x36, 0x78, 0x4E, 0xB1, 0xB2, 0xF8, 0x14, 0x62, + 0x86, 0x42, 0x17, 0xDA, + 0x92, 0xF8, 0x10, 0x62, 0x76, 0x07, 0x13, 0xD5, 0x02, 0x21, 0x25, 0xE0, + 0xB2, 0xF8, 0x1C, 0x62, + 0x86, 0x42, 0x03, 0xDA, 0x92, 0xF8, 0x10, 0x62, 0x76, 0x07, 0xF5, 0xD4, + 0xB2, 0xF8, 0x1A, 0x62, + 0x08, 0xE0, 0x00, 0xBF, 0x92, 0xF8, 0x10, 0x02, 0x80, 0x07, 0x05, 0xD5, + 0x01, 0x21, 0x13, 0xE0, + 0xB2, 0xF8, 0x12, 0x62, 0x86, 0x42, 0xF5, 0xDB, 0x73, 0xB1, 0x6C, 0x48, + 0x00, 0x78, 0x01, 0x28, + 0x03, 0xD0, 0x6B, 0x48, 0x00, 0x78, 0x01, 0x28, 0x03, 0xD1, 0x92, 0xF8, + 0x10, 0x02, 0x00, 0x07, + 0x01, 0xD5, 0x01, 0x2C, 0x00, 0xD1, 0x19, 0x46, 0x62, 0x48, 0x34, 0xB1, + 0x19, 0xB1, 0x00, 0xBF, + 0x92, 0xF8, 0x19, 0x22, 0x42, 0x60, 0xA9, 0x70, 0x70, 0xBD, 0x00, 0x29, + 0xF8, 0xD1, 0x00, 0x2B, + 0xF6, 0xD0, 0x44, 0x68, 0x00, 0x2C, 0xF6, 0xDD, 0x92, 0xF8, 0x10, 0x12, + 0x0A, 0x07, 0x59, 0x49, + 0x49, 0x68, 0xA1, 0xF1, 0x01, 0x01, 0x02, 0xD5, 0x5D, 0x4A, 0x12, 0x78, + 0x02, 0xB1, 0x41, 0x60, + 0x19, 0x46, 0xE8, 0xE7, 0x52, 0x48, 0x53, 0x49, 0x00, 0x68, 0x90, 0xF8, + 0x10, 0x02, 0xC2, 0x06, + 0x4F, 0xF0, 0x00, 0x00, 0x03, 0xD4, 0x50, 0x4A, 0x90, 0x70, 0x48, 0x70, + 0x48, 0x60, 0x08, 0x70, + 0x70, 0x47, 0x10, 0xB5, 0x04, 0x46, 0x4A, 0x48, 0x00, 0x68, 0x90, 0xF8, + 0x10, 0x02, 0xC0, 0x06, + 0x20, 0x46, 0x02, 0xD5, 0xFF, 0xF7, 0x89, 0xFF, 0x01, 0xE0, 0xFF, 0xF7, + 0x45, 0xFF, 0x46, 0x48, + 0x82, 0x78, 0x4C, 0x48, 0x41, 0x7B, 0x62, 0xF3, 0x07, 0x11, 0x41, 0x73, + 0xA0, 0xF8, 0x2F, 0x40, + 0x10, 0xBD, 0x2D, 0xE9, 0xFC, 0x5F, 0x83, 0x46, 0x47, 0x48, 0x00, 0x25, + 0x0A, 0x46, 0x00, 0x78, + 0x2F, 0x46, 0x2C, 0x46, 0x01, 0x28, 0x00, 0xD1, 0x01, 0x24, 0x44, 0x48, + 0x06, 0x78, 0x00, 0x78, + 0x41, 0x00, 0x01, 0x2A, 0x1B, 0xD0, 0xDF, 0xF8, 0xD8, 0x80, 0xDF, 0xF8, + 0x04, 0xA1, 0xDF, 0xF8, + 0x04, 0x91, 0xD8, 0xF8, 0x00, 0x00, 0x90, 0xF8, 0x10, 0x22, 0x92, 0x06, + 0x14, 0xD5, 0x32, 0x1B, + 0xB0, 0xF9, 0x20, 0x12, 0xCD, 0xE9, 0x00, 0x12, 0xB0, 0xF9, 0x1E, 0x32, + 0x0A, 0xEB, 0x44, 0x02, + 0x0B, 0xEB, 0x44, 0x01, 0x09, 0xEB, 0x44, 0x00, 0x0C, 0xF0, 0x72, 0xFF, + 0x09, 0xE0, 0x34, 0x48, + 0x4C, 0x38, 0x10, 0xF0, 0x59, 0xF8, 0x41, 0xE0, 0x0A, 0x46, 0x59, 0x46, + 0x31, 0x48, 0x0F, 0xF0, + 0xD4, 0xFF, 0x2F, 0x48, 0x4C, 0x38, 0x34, 0xE0, 0x3A, 0xF9, 0x14, 0x20, + 0x39, 0xF9, 0x14, 0x10, + 0x51, 0x1A, 0x00, 0xD5, 0x49, 0x42, 0xD8, 0xF8, 0x00, 0x30, 0x09, 0xB2, + 0x93, 0xF8, 0x10, 0x22, + 0x52, 0x06, 0x0B, 0xD5, 0x20, 0x4A, 0x12, 0x78, 0x01, 0x2A, 0x07, 0xD1, + 0x26, 0x4A, 0x12, 0x78, + 0x22, 0xB9, 0xB3, 0xF8, 0x22, 0x22, 0x8A, 0x42, 0x00, 0xDA, 0x01, 0x27, + 0x30, 0xF9, 0x14, 0x20, + 0x8A, 0x42, 0x02, 0xDA, 0x93, 0xF8, 0x16, 0x32, 0x01, 0xE0, 0x93, 0xF8, + 0x17, 0x32, 0x5A, 0x43, + 0xC3, 0xF5, 0x80, 0x73, 0x01, 0xFB, 0x03, 0x21, 0xCA, 0x17, 0x01, 0xEB, + 0x12, 0x61, 0x09, 0x12, + 0x20, 0xF8, 0x14, 0x10, 0x30, 0xF9, 0x14, 0x10, 0xA9, 0x42, 0x00, 0xDD, + 0x0D, 0x46, 0x64, 0x1C, + 0x24, 0xB2, 0xB4, 0x42, 0xC8, 0xDB, 0x28, 0x46, 0xFF, 0xF7, 0x73, 0xFF, + 0x72, 0x00, 0x59, 0x46, + 0x0F, 0x48, 0x0F, 0xF0, 0x92, 0xFF, 0x03, 0x48, 0x07, 0x70, 0xBD, 0xE8, + 0xFC, 0x9F, 0x00, 0x00, + 0x4C, 0x07, 0x10, 0x00, 0x5C, 0x05, 0x10, 0x00, 0x74, 0x07, 0x10, 0x00, + 0xF5, 0x06, 0x10, 0x00, + 0x64, 0x05, 0x10, 0x00, 0xF6, 0x06, 0x10, 0x00, 0xFD, 0x06, 0x10, 0x00, + 0xF7, 0x06, 0x10, 0x00, + 0xFA, 0x06, 0x10, 0x00, 0x0A, 0x44, 0x01, 0x20, 0xF4, 0x06, 0x10, 0x00, + 0xED, 0x06, 0x10, 0x00, + 0x54, 0x0A, 0x01, 0x20, 0xA0, 0x0A, 0x01, 0x20, 0x74, 0x05, 0x10, 0x00, + 0x2D, 0xE9, 0xF0, 0x4F, + 0x81, 0x46, 0xDD, 0xE9, 0x09, 0xB5, 0x01, 0x20, 0x47, 0xF6, 0xFF, 0x77, + 0x9A, 0x46, 0x84, 0x46, + 0xFE, 0x43, 0x2C, 0x46, 0x15, 0xE0, 0x00, 0xBF, 0x32, 0xF8, 0x14, 0x30, + 0x31, 0xF8, 0x14, 0x80, + 0xA3, 0xEB, 0x08, 0x03, 0x1B, 0xB2, 0xBB, 0x42, 0x00, 0xDA, 0x1F, 0x46, + 0xB3, 0x42, 0x00, 0xDD, + 0x1E, 0x46, 0x00, 0x2B, 0x02, 0xDD, 0x4F, 0xF0, 0x00, 0x0C, 0x02, 0xE0, + 0x01, 0xDA, 0x4F, 0xF0, + 0x00, 0x00, 0x64, 0x1E, 0xE8, 0xD2, 0x20, 0xB1, 0xBC, 0xF1, 0x00, 0x0F, + 0x01, 0xD0, 0x00, 0x23, + 0x09, 0xE0, 0x50, 0xEA, 0x0C, 0x03, 0x06, 0xD0, 0x20, 0xB9, 0xBC, 0xF1, + 0x00, 0x0F, 0x01, 0xD0, + 0x33, 0x46, 0x00, 0xE0, 0x3B, 0x46, 0x00, 0x27, 0x3C, 0x46, 0x28, 0x46, + 0xCB, 0xF1, 0x00, 0x0C, + 0x0E, 0xE0, 0x00, 0xBF, 0x32, 0xF8, 0x10, 0x60, 0x31, 0xF8, 0x10, 0x80, + 0xA6, 0xEB, 0x08, 0x06, + 0xF6, 0x1A, 0x36, 0xB2, 0x56, 0x45, 0x03, 0xDA, 0x66, 0x45, 0x01, 0xDD, + 0x37, 0x44, 0x64, 0x1C, + 0x40, 0x1E, 0xEF, 0xD2, 0x04, 0xB9, 0x01, 0x24, 0x97, 0xFB, 0xF4, 0xF0, + 0x18, 0x44, 0x04, 0xE0, + 0x31, 0xF8, 0x15, 0x20, 0x02, 0x44, 0x29, 0xF8, 0x15, 0x20, 0x6D, 0x1E, + 0xF8, 0xD2, 0xBD, 0xE8, + 0xF0, 0x8F, 0x2D, 0xE9, 0xF0, 0x41, 0x04, 0x46, 0x03, 0x46, 0xFC, 0x48, + 0x15, 0x46, 0x0E, 0x46, + 0x0A, 0x46, 0x01, 0x78, 0xFA, 0x48, 0x00, 0xF0, 0x51, 0xFA, 0xFA, 0x48, + 0x04, 0xF1, 0x08, 0x03, + 0x1F, 0x46, 0x01, 0x78, 0xF6, 0x48, 0x32, 0x46, 0x2A, 0x30, 0x00, 0xF0, + 0x47, 0xFA, 0x00, 0x2D, + 0x07, 0xD0, 0x20, 0x46, 0x10, 0xF0, 0x6C, 0xF8, 0x38, 0x46, 0xBD, 0xE8, + 0xF0, 0x41, 0x10, 0xF0, + 0x67, 0xB8, 0xBD, 0xE8, 0xF0, 0x81, 0x2D, 0xE9, 0xFC, 0x41, 0x07, 0x46, + 0x14, 0x46, 0x88, 0x46, + 0x03, 0x20, 0xF9, 0xF7, 0xD6, 0xF9, 0xEC, 0x4D, 0xE8, 0x4E, 0x00, 0x94, + 0xE8, 0x48, 0x32, 0x78, + 0x39, 0x46, 0x76, 0x38, 0x2B, 0x68, 0x0C, 0xF0, 0xF9, 0xFE, 0x00, 0x94, + 0xE5, 0x4C, 0xE4, 0x48, + 0x41, 0x46, 0x22, 0x78, 0x4C, 0x38, 0x2B, 0x68, 0x0C, 0xF0, 0xF0, 0xFE, + 0xE3, 0x4D, 0x28, 0x68, + 0x90, 0xF8, 0x30, 0x12, 0xC9, 0x07, 0x1A, 0xD0, 0x32, 0x78, 0xB0, 0xF9, + 0x34, 0x12, 0xCD, 0xE9, + 0x00, 0x12, 0xDB, 0x49, 0xB0, 0xF9, 0x32, 0x32, 0x76, 0x39, 0xDD, 0x4A, + 0x08, 0x46, 0xFF, 0xF7, + 0x55, 0xFF, 0x28, 0x68, 0x22, 0x78, 0xB0, 0xF9, 0x38, 0x12, 0xCD, 0xE9, + 0x00, 0x12, 0xD4, 0x49, + 0xB0, 0xF9, 0x36, 0x32, 0x4C, 0x39, 0xD7, 0x4A, 0x08, 0x46, 0xFF, 0xF7, + 0x47, 0xFF, 0xD0, 0x49, + 0x33, 0x78, 0xD3, 0x4A, 0x76, 0x39, 0xCE, 0x48, 0x0C, 0xF0, 0xBB, 0xFE, + 0xCC, 0x49, 0x23, 0x78, + 0x4C, 0x39, 0xD0, 0x4A, 0x01, 0xF1, 0x76, 0x00, 0x0C, 0xF0, 0xB3, 0xFE, + 0x28, 0x68, 0x90, 0xF8, + 0x30, 0x12, 0x8A, 0x07, 0x04, 0xD4, 0xCC, 0x4A, 0x12, 0x78, 0x8A, 0xB1, + 0x49, 0x07, 0x0F, 0xD5, + 0xC3, 0x49, 0x90, 0xF8, 0x41, 0x32, 0x32, 0x78, 0x08, 0x46, 0x0C, 0xF0, + 0xE2, 0xFE, 0x28, 0x68, + 0xBF, 0x49, 0x22, 0x78, 0x90, 0xF8, 0x41, 0x32, 0x2A, 0x31, 0x08, 0x46, + 0x0C, 0xF0, 0xD9, 0xFE, + 0xBD, 0xE8, 0xFC, 0x41, 0x03, 0x20, 0xF9, 0xF7, 0x8F, 0xB9, 0x70, 0xB5, + 0xBF, 0x4C, 0xC0, 0x49, + 0x00, 0x25, 0xA5, 0x70, 0x01, 0x20, 0x08, 0x70, 0xB8, 0x48, 0x25, 0x70, + 0x65, 0x70, 0x00, 0x68, + 0xB0, 0xF8, 0x3A, 0x02, 0xA0, 0x80, 0x00, 0xF0, 0xE3, 0xF9, 0xFF, 0xF7, + 0xEE, 0xF8, 0xA5, 0x60, + 0xE5, 0x60, 0x70, 0xBD, 0x2D, 0xE9, 0xF0, 0x41, 0x0E, 0x46, 0xB5, 0x49, + 0x07, 0x46, 0xB3, 0x4C, + 0x0D, 0x78, 0x00, 0x20, 0x08, 0x70, 0x60, 0x70, 0x00, 0xF0, 0xD2, 0xF9, + 0xB1, 0x48, 0x2A, 0x46, + 0x31, 0x46, 0x00, 0x78, 0x00, 0x28, 0x38, 0x46, 0x02, 0xD0, 0xFF, 0xF7, + 0x45, 0xF8, 0x01, 0xE0, + 0xFF, 0xF7, 0x69, 0xFF, 0xAC, 0x48, 0x41, 0x78, 0xA4, 0x48, 0xA9, 0xB1, + 0xA1, 0x78, 0x01, 0x29, + 0x12, 0xD0, 0x00, 0x68, 0xB0, 0xF9, 0x3C, 0x12, 0xA8, 0x4D, 0xA1, 0x80, + 0x01, 0x22, 0x28, 0x46, + 0xFF, 0xF7, 0x37, 0xFF, 0xD5, 0xE9, 0x00, 0x01, 0x08, 0x43, 0x09, 0xD0, + 0xD5, 0xE9, 0x02, 0x01, + 0x08, 0x43, 0x05, 0xD0, 0x01, 0x20, 0x04, 0xE0, 0x00, 0x68, 0xB0, 0xF9, + 0x3A, 0x12, 0xEB, 0xE7, + 0x00, 0x20, 0x20, 0x70, 0x45, 0xE7, 0x2D, 0xE9, 0xF0, 0x4F, 0x04, 0x46, + 0x9C, 0x48, 0xA7, 0xB0, + 0x4F, 0xF0, 0x01, 0x09, 0x01, 0x78, 0x91, 0x48, 0x01, 0x29, 0x00, 0x68, + 0x15, 0xD0, 0x92, 0x49, + 0x09, 0x78, 0xA9, 0xB1, 0xB0, 0xF9, 0x54, 0x62, 0x88, 0x48, 0x00, 0x21, + 0x4F, 0xF0, 0x01, 0x0A, + 0x00, 0x78, 0x94, 0x4D, 0x40, 0x1E, 0x00, 0xF0, 0xFF, 0x08, 0x86, 0x48, + 0x83, 0x46, 0x03, 0x78, + 0x9A, 0x1E, 0x02, 0xF0, 0xFF, 0x0C, 0x22, 0x46, 0x13, 0xE0, 0xB0, 0xF9, + 0x59, 0x62, 0xEB, 0xE7, + 0xB0, 0xF9, 0x52, 0x62, 0xE8, 0xE7, 0x50, 0x46, 0x06, 0xE0, 0x00, 0xBF, + 0x32, 0xF9, 0x10, 0x70, + 0xAF, 0x42, 0x00, 0xDD, 0x3D, 0x46, 0x40, 0x1C, 0x60, 0x45, 0xF7, 0xDD, + 0x02, 0xEB, 0x43, 0x02, + 0x49, 0x1C, 0x41, 0x45, 0xEF, 0xDD, 0x5F, 0x46, 0x5A, 0x00, 0x04, 0xEB, + 0x43, 0x01, 0x68, 0x46, + 0x0F, 0xF0, 0xFB, 0xFD, 0x80, 0x4A, 0x38, 0x78, 0x6B, 0x46, 0x12, 0x88, + 0x41, 0x42, 0x02, 0xEB, + 0x41, 0x01, 0x09, 0xB2, 0x42, 0x00, 0x04, 0xEB, 0x41, 0x01, 0x03, 0xEB, + 0x40, 0x00, 0x0F, 0xF0, + 0xEC, 0xFD, 0x38, 0x78, 0x69, 0x46, 0x42, 0x00, 0x08, 0x46, 0x0C, 0xF0, + 0x50, 0xFE, 0x28, 0x1A, + 0x00, 0xB2, 0xB0, 0x42, 0x01, 0xDD, 0x4F, 0xF0, 0x00, 0x09, 0x48, 0x46, + 0x27, 0xB0, 0xC6, 0xE6, + 0x2D, 0xE9, 0xF0, 0x4F, 0x71, 0x49, 0x70, 0x4A, 0x00, 0x27, 0x0C, 0x68, + 0xB2, 0xF9, 0x00, 0x20, + 0x4F, 0xF0, 0x01, 0x0A, 0x21, 0x8E, 0x3B, 0x46, 0x91, 0x42, 0x49, 0xD1, + 0x62, 0x49, 0x0A, 0x78, + 0x5E, 0x49, 0x0E, 0x68, 0x8A, 0xB1, 0xB6, 0xF9, 0x54, 0x12, 0x00, 0x25, + 0x89, 0x46, 0x57, 0x49, + 0x4F, 0xF0, 0x02, 0x0E, 0x09, 0x78, 0x49, 0x1E, 0x01, 0xF0, 0xFF, 0x0B, + 0x55, 0x49, 0x09, 0x78, + 0xC9, 0x1E, 0x01, 0xF0, 0xFF, 0x0C, 0x32, 0x34, 0x2A, 0xE0, 0xB6, 0xF9, + 0x52, 0x12, 0xEC, 0xE7, + 0x72, 0x46, 0x1C, 0xE0, 0x34, 0xF8, 0x12, 0x10, 0x30, 0xF8, 0x12, 0x80, + 0xA1, 0xEB, 0x08, 0x01, + 0x09, 0xB2, 0x00, 0x29, 0x01, 0xDB, 0x88, 0x46, 0x01, 0xE0, 0xC1, 0xF1, + 0x00, 0x08, 0xB8, 0x45, + 0x05, 0xDD, 0x00, 0x29, 0x01, 0xDB, 0x0F, 0x46, 0x00, 0xE0, 0x4F, 0x42, + 0x3F, 0xB2, 0x00, 0x29, + 0x00, 0xDA, 0x49, 0x42, 0x49, 0x45, 0x01, 0xDD, 0x5B, 0x1C, 0x9B, 0xB2, + 0x52, 0x1C, 0x62, 0x45, + 0xE0, 0xDD, 0x96, 0xF8, 0x30, 0x10, 0x6D, 0x1C, 0x04, 0xEB, 0x41, 0x04, + 0x00, 0xEB, 0x41, 0x00, + 0x5D, 0x45, 0xD5, 0xDD, 0x96, 0xF8, 0x56, 0x02, 0x98, 0x42, 0x01, 0xD2, + 0x4F, 0xF0, 0x00, 0x0A, + 0x47, 0x48, 0x20, 0xF8, 0x33, 0x7F, 0x83, 0x70, 0x50, 0x46, 0x68, 0xE6, + 0x2D, 0xE9, 0xF0, 0x41, + 0x06, 0x46, 0x44, 0x48, 0x00, 0x24, 0x00, 0x78, 0xC0, 0x06, 0x5E, 0xD5, + 0x3C, 0x48, 0x36, 0x4D, + 0x00, 0x78, 0x88, 0xB1, 0x40, 0x48, 0x31, 0x4B, 0x34, 0x49, 0x02, 0x68, + 0x18, 0x68, 0x08, 0x31, + 0xB0, 0xF8, 0x5D, 0x02, 0x40, 0x42, 0x07, 0xB2, 0x00, 0x20, 0xC8, 0x80, + 0x88, 0x80, 0x08, 0x60, + 0x35, 0x48, 0xB0, 0xF9, 0x00, 0x00, 0x29, 0xE0, 0x2C, 0x48, 0x2C, 0x78, + 0x00, 0x78, 0x08, 0xB1, + 0x01, 0x29, 0x02, 0xD0, 0x08, 0x43, 0x02, 0xD0, 0x3B, 0xE0, 0x01, 0x24, + 0x3A, 0xE0, 0x00, 0x24, + 0x67, 0xE0, 0x00, 0xBF, 0x32, 0xF9, 0x10, 0xC0, 0xBC, 0xF1, 0x00, 0x0F, + 0x16, 0xDA, 0xB1, 0xF8, + 0x04, 0xC0, 0x0C, 0xF1, 0x01, 0x0C, 0xA1, 0xF8, 0x04, 0xC0, 0x32, 0xF9, + 0x10, 0xE0, 0xD1, 0xF8, + 0x00, 0xC0, 0xF4, 0x44, 0xC1, 0xF8, 0x00, 0xC0, 0x32, 0xF9, 0x10, 0xC0, + 0xBC, 0x45, 0x05, 0xDA, + 0xB1, 0xF8, 0x06, 0xC0, 0x0C, 0xF1, 0x01, 0x0C, 0xA1, 0xF8, 0x06, 0xC0, + 0x40, 0x1E, 0xE1, 0xD2, + 0x08, 0x68, 0x40, 0x42, 0x08, 0x60, 0x15, 0x48, 0x19, 0x68, 0x08, 0x30, + 0x02, 0x68, 0xB1, 0xF8, + 0x5F, 0x32, 0x9A, 0x42, 0x3D, 0xDD, 0xB0, 0xF9, 0x06, 0x00, 0x91, 0xF8, + 0x61, 0x12, 0x88, 0x42, + 0x37, 0xDD, 0x1A, 0x48, 0x11, 0x4A, 0xB0, 0xF9, 0x00, 0x10, 0x30, 0x46, + 0x00, 0xF0, 0x20, 0xF9, + 0x04, 0x46, 0x6C, 0xB3, 0x00, 0xF0, 0xF2, 0xF8, 0x2D, 0xE0, 0x3D, 0xE0, + 0xED, 0x06, 0x10, 0x00, + 0x94, 0x0B, 0x01, 0x20, 0xEE, 0x06, 0x10, 0x00, 0x9C, 0x06, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, + 0x7E, 0x4A, 0x01, 0x20, 0xA8, 0x4A, 0x01, 0x20, 0x74, 0x05, 0x10, 0x00, + 0x64, 0x05, 0x10, 0x00, + 0x7A, 0x07, 0x10, 0x00, 0xF4, 0x06, 0x10, 0x00, 0x74, 0x07, 0x10, 0x00, + 0x40, 0x2B, 0x10, 0x00, + 0x79, 0x05, 0x10, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0xFE, 0x06, 0x10, 0x00, + 0x50, 0x07, 0x10, 0x00, + 0x0A, 0x44, 0x01, 0x20, 0x04, 0x07, 0x10, 0x00, 0x14, 0x07, 0x10, 0x00, + 0x98, 0x06, 0x10, 0x00, + 0xFF, 0xE7, 0x00, 0xF0, 0xD9, 0xF8, 0x51, 0x4E, 0x28, 0x78, 0x31, 0x78, + 0x88, 0x42, 0x06, 0xD0, + 0x08, 0xB1, 0x01, 0x21, 0x00, 0xE0, 0x00, 0x21, 0x06, 0x20, 0x0B, 0xF0, + 0xEC, 0xF8, 0x28, 0x78, + 0x30, 0x70, 0x0C, 0xB1, 0x01, 0x20, 0xE4, 0xE5, 0x00, 0x20, 0xE2, 0xE5, + 0x70, 0xB5, 0x03, 0x20, + 0xF8, 0xF7, 0xBF, 0xFF, 0x00, 0xF0, 0x48, 0xF8, 0x04, 0x46, 0x03, 0x20, + 0xF8, 0xF7, 0xD4, 0xFF, + 0x43, 0x49, 0xC8, 0x7B, 0x64, 0xF3, 0x05, 0x10, 0xC8, 0x73, 0x42, 0x48, + 0x01, 0x78, 0x21, 0xB1, + 0x00, 0x21, 0x01, 0x70, 0x20, 0x46, 0x00, 0xF0, 0x30, 0xF9, 0x3F, 0x4D, + 0x3F, 0x48, 0x21, 0x46, + 0x2A, 0x78, 0x00, 0x68, 0xFF, 0xF7, 0x3A, 0xFF, 0xA8, 0x70, 0x70, 0xBD, + 0x2D, 0xE9, 0xF0, 0x47, + 0x1D, 0x46, 0x4F, 0xF0, 0x00, 0x08, 0xC5, 0xE9, 0x00, 0x88, 0x91, 0x46, + 0x82, 0x46, 0x0C, 0x46, + 0x11, 0xE0, 0x00, 0xBF, 0x3A, 0xF9, 0x14, 0x10, 0x49, 0x45, 0x0C, 0xDD, + 0xD5, 0xE9, 0x00, 0x67, + 0x4F, 0xF0, 0x01, 0x08, 0x22, 0x46, 0x40, 0x46, 0x00, 0x21, 0x0F, 0xF0, + 0x67, 0xFC, 0x06, 0x43, + 0x0F, 0x43, 0xC5, 0xE9, 0x00, 0x67, 0x64, 0x1E, 0xEC, 0xD2, 0x40, 0x46, + 0xBD, 0xE8, 0xF0, 0x87, + 0x2B, 0x48, 0x00, 0x21, 0xC0, 0xE9, 0x00, 0x11, 0xC0, 0xE9, 0x02, 0x11, + 0x08, 0x46, 0x29, 0x49, + 0x08, 0x76, 0x29, 0x49, 0x08, 0x76, 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x41, + 0x27, 0x48, 0x00, 0x78, + 0x02, 0x28, 0x35, 0xD9, 0x26, 0x4E, 0x30, 0x68, 0x90, 0xF8, 0x50, 0x02, + 0x41, 0x07, 0x25, 0x48, + 0x00, 0x68, 0x02, 0xD5, 0x24, 0x49, 0x09, 0x78, 0x81, 0xB1, 0xFF, 0xF7, + 0x3C, 0xFE, 0x05, 0x46, + 0x30, 0x68, 0x90, 0xF8, 0x50, 0x02, 0x00, 0x07, 0x21, 0xD5, 0x22, 0x48, + 0x1F, 0x4B, 0x20, 0x49, + 0x02, 0x78, 0x02, 0x2A, 0x1B, 0xDB, 0x20, 0x4C, 0x50, 0x1E, 0x08, 0xE0, + 0xFF, 0xF7, 0x88, 0xFE, + 0xED, 0xE7, 0x00, 0xBF, 0x33, 0xF9, 0x10, 0x70, 0xA7, 0x42, 0x00, 0xDD, + 0x3C, 0x46, 0x40, 0x1E, + 0xF8, 0xD1, 0x98, 0x1C, 0x92, 0x1E, 0x0C, 0xF0, 0xC2, 0xFC, 0x31, 0x68, + 0x20, 0x1A, 0x00, 0xB2, + 0xB1, 0xF9, 0x57, 0x12, 0x88, 0x42, 0x02, 0xDD, 0x35, 0xB1, 0x02, 0x20, + 0x59, 0xE5, 0x0D, 0xB1, + 0x00, 0x20, 0x56, 0xE5, 0x01, 0x20, 0x54, 0xE5, 0x03, 0x20, 0x52, 0xE5, + 0x75, 0x05, 0x10, 0x00, + 0x0A, 0x44, 0x01, 0x20, 0x78, 0x05, 0x10, 0x00, 0x64, 0x05, 0x10, 0x00, + 0x14, 0x07, 0x10, 0x00, + 0x40, 0x2B, 0x10, 0x00, 0x18, 0x2A, 0x10, 0x00, 0x58, 0x2A, 0x10, 0x00, + 0xED, 0x06, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, 0x1C, 0x07, 0x10, 0x00, 0x79, 0x05, 0x10, 0x00, + 0xA8, 0x4A, 0x01, 0x20, + 0xCE, 0x0A, 0x01, 0x20, 0xEE, 0x06, 0x10, 0x00, 0x00, 0x80, 0xFF, 0xFF, + 0xFE, 0x48, 0x00, 0x21, + 0xC1, 0x70, 0x02, 0x78, 0x00, 0x2A, 0x0E, 0xD1, 0x82, 0x78, 0xFC, 0x4B, + 0x52, 0x1C, 0xD2, 0xB2, + 0x82, 0x70, 0x1B, 0x68, 0x93, 0xF8, 0x62, 0x32, 0x03, 0xF0, 0x0F, 0x03, + 0x93, 0x42, 0x02, 0xD2, + 0x01, 0x22, 0x02, 0x70, 0x81, 0x70, 0x70, 0x47, 0xF3, 0x48, 0x00, 0x21, + 0x81, 0x70, 0x02, 0x78, + 0x00, 0x2A, 0x0C, 0xD0, 0xC2, 0x78, 0xF1, 0x4B, 0x52, 0x1C, 0xD2, 0xB2, + 0xC2, 0x70, 0x1B, 0x68, + 0x93, 0xF8, 0x62, 0x32, 0xB2, 0xEB, 0x13, 0x1F, 0x01, 0xD9, 0x01, 0x70, + 0xC1, 0x70, 0x70, 0x47, + 0x2D, 0xE9, 0xF7, 0x4F, 0x86, 0xB0, 0x8A, 0x46, 0xD2, 0xE9, 0x00, 0x01, + 0xCD, 0xE9, 0x00, 0x01, + 0xD2, 0xE9, 0x02, 0x01, 0xCD, 0xE9, 0x04, 0x01, 0x68, 0x46, 0x0F, 0xF0, + 0x61, 0xFD, 0x04, 0xA8, + 0x0F, 0xF0, 0x5E, 0xFD, 0xE2, 0x48, 0x00, 0x27, 0xB9, 0x46, 0x06, 0x78, + 0x28, 0xE0, 0xF1, 0xB2, + 0x06, 0x98, 0x0F, 0xF0, 0x6B, 0xFD, 0x80, 0x46, 0xDE, 0x48, 0xDD, 0xE9, + 0x04, 0xB1, 0x05, 0x78, + 0x01, 0x24, 0x00, 0x98, 0xB4, 0x40, 0x04, 0x40, 0x02, 0x91, 0x17, 0xE0, + 0x38, 0xF9, 0x15, 0x10, + 0x51, 0x45, 0x13, 0xDD, 0x20, 0x00, 0x0F, 0xD0, 0x2A, 0x46, 0x01, 0x20, + 0x00, 0x21, 0x0F, 0xF0, + 0x95, 0xFB, 0x02, 0x9A, 0x00, 0xEA, 0x0B, 0x00, 0x11, 0x40, 0x08, 0x43, + 0x04, 0xD0, 0x09, 0xF1, + 0x01, 0x00, 0x1F, 0xFA, 0x80, 0xF9, 0x01, 0xE0, 0x7F, 0x1C, 0xBF, 0xB2, + 0x6D, 0x1E, 0xE5, 0xD2, + 0x76, 0x1E, 0xD4, 0xD2, 0xC9, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x5B, 0x12, + 0xB9, 0x42, 0x07, 0xD8, + 0x90, 0xF8, 0x5C, 0x12, 0x49, 0x45, 0x03, 0xD3, 0x01, 0x20, 0x09, 0xB0, + 0xBD, 0xE8, 0xF0, 0x8F, + 0x00, 0x20, 0xFA, 0xE7, 0xC0, 0x48, 0x10, 0xB5, 0x41, 0x78, 0x01, 0x70, + 0x00, 0x21, 0x81, 0x70, + 0xC1, 0x70, 0xFF, 0xF7, 0x1A, 0xFD, 0xBD, 0xE8, 0x10, 0x40, 0xFE, 0xF7, + 0x59, 0xBE, 0xBA, 0x48, + 0x00, 0x21, 0xBA, 0x4A, 0x01, 0x70, 0x41, 0x70, 0x12, 0x68, 0x92, 0xF8, + 0x50, 0x22, 0x92, 0x07, + 0x00, 0xD5, 0x01, 0x21, 0x41, 0x71, 0x01, 0x71, 0x70, 0x47, 0xB3, 0x49, + 0x4A, 0x79, 0x01, 0x2A, + 0x02, 0xD1, 0x00, 0x28, 0x00, 0xD1, 0x48, 0x71, 0x70, 0x47, 0xF0, 0xB5, + 0x00, 0x24, 0x1C, 0x72, + 0x04, 0x7E, 0x0D, 0x7E, 0x6C, 0x43, 0x04, 0xF0, 0xFF, 0x0C, 0x4F, 0xF0, + 0xFF, 0x34, 0xE7, 0x07, + 0x26, 0x46, 0x00, 0x25, 0x08, 0xE0, 0x00, 0xBF, 0x52, 0xF8, 0x25, 0xE0, + 0xBE, 0x45, 0x01, 0xDD, + 0x77, 0x46, 0x6E, 0xB2, 0x6D, 0x1C, 0xED, 0xB2, 0x65, 0x45, 0xF5, 0xD3, + 0x00, 0x2E, 0x17, 0xDB, + 0x01, 0x25, 0x1D, 0x72, 0x0D, 0x7E, 0x96, 0xFB, 0xF5, 0xF5, 0x6D, 0x1C, + 0xED, 0xB2, 0x05, 0x44, + 0xAF, 0x7C, 0x1F, 0x70, 0x2D, 0x7B, 0x1D, 0x71, 0x0D, 0x7E, 0x96, 0xFB, + 0xF5, 0xF7, 0x05, 0xFB, + 0x17, 0x65, 0x6D, 0x1C, 0xED, 0xB2, 0x0D, 0x44, 0xAF, 0x7C, 0x9F, 0x70, + 0x2D, 0x7B, 0x9D, 0x71, + 0xBC, 0xF1, 0x01, 0x0F, 0x30, 0xD9, 0x95, 0x4D, 0x2D, 0x68, 0x95, 0xF8, + 0x51, 0x52, 0xED, 0x07, + 0x2A, 0xD0, 0x25, 0x46, 0x00, 0x27, 0x3C, 0x46, 0x0A, 0xE0, 0x00, 0xBF, + 0x52, 0xF8, 0x24, 0xE0, + 0xBE, 0x45, 0x03, 0xDD, 0xA6, 0x42, 0x01, 0xD0, 0x77, 0x46, 0x65, 0xB2, + 0x64, 0x1C, 0xE4, 0xB2, + 0x64, 0x45, 0xF3, 0xD3, 0x00, 0x2D, 0x17, 0xDB, 0x02, 0x22, 0x1A, 0x72, + 0x0A, 0x7E, 0x95, 0xFB, + 0xF2, 0xF2, 0x52, 0x1C, 0xD2, 0xB2, 0x10, 0x44, 0x82, 0x7C, 0x5A, 0x70, + 0x00, 0x7B, 0x58, 0x71, + 0x08, 0x7E, 0x95, 0xFB, 0xF0, 0xF2, 0x00, 0xFB, 0x12, 0x50, 0x40, 0x1C, + 0xC0, 0xB2, 0x08, 0x44, + 0x81, 0x7C, 0xD9, 0x70, 0x00, 0x7B, 0xD8, 0x71, 0xF0, 0xBD, 0x2D, 0xE9, + 0xFF, 0x47, 0x0F, 0x46, + 0x06, 0x46, 0xD2, 0xE9, 0x00, 0x01, 0xCD, 0xE9, 0x00, 0x01, 0xD2, 0xE9, + 0x02, 0x01, 0xCD, 0xE9, + 0x02, 0x01, 0x68, 0x46, 0x0F, 0xF0, 0x84, 0xFC, 0x02, 0xA8, 0x0F, 0xF0, + 0x81, 0xFC, 0x00, 0x25, + 0xDF, 0xF8, 0xD0, 0x91, 0xDF, 0xF8, 0xC8, 0x81, 0x2F, 0xE0, 0x2A, 0x46, + 0x01, 0x20, 0x00, 0x21, + 0x0F, 0xF0, 0xCC, 0xFA, 0xDD, 0xE9, 0x00, 0x23, 0x10, 0x40, 0x19, 0x40, + 0x08, 0x43, 0x01, 0xD0, + 0x00, 0x24, 0x17, 0xE0, 0x99, 0xF8, 0x00, 0x00, 0x39, 0x46, 0x42, 0x00, + 0x30, 0x46, 0x0F, 0xF0, + 0xF4, 0xFA, 0x13, 0xE0, 0x22, 0x46, 0x01, 0x20, 0x00, 0x21, 0x0F, 0xF0, + 0xB7, 0xFA, 0xDD, 0xE9, + 0x02, 0x23, 0x10, 0x40, 0x19, 0x40, 0x08, 0x43, 0x03, 0xD1, 0x37, 0xF8, + 0x14, 0x00, 0x26, 0xF8, + 0x14, 0x00, 0x64, 0x1C, 0x99, 0xF8, 0x00, 0x00, 0x84, 0x42, 0xEB, 0xDB, + 0x99, 0xF8, 0x00, 0x00, + 0x6D, 0x1C, 0x07, 0xEB, 0x40, 0x07, 0x06, 0xEB, 0x40, 0x06, 0x98, 0xF8, + 0x00, 0x00, 0x85, 0x42, + 0xCB, 0xDB, 0xBD, 0xE8, 0xFF, 0x87, 0x2D, 0xE9, 0xFF, 0x4F, 0x81, 0x46, + 0x84, 0x1C, 0x80, 0x78, + 0x81, 0xB0, 0x9A, 0x46, 0x15, 0x46, 0x60, 0xB1, 0x40, 0x1E, 0xC2, 0xB2, + 0x22, 0x70, 0xD5, 0xE9, + 0x00, 0x67, 0x01, 0x20, 0x00, 0x21, 0x0F, 0xF0, 0x89, 0xFA, 0x06, 0x43, + 0x0F, 0x43, 0xC5, 0xE9, + 0x00, 0x67, 0x4F, 0xF0, 0x01, 0x08, 0xAA, 0xF1, 0x01, 0x0A, 0x2D, 0xE0, + 0x09, 0xEB, 0x48, 0x00, + 0x00, 0xF1, 0x02, 0x0B, 0x82, 0x78, 0x60, 0x78, 0xC1, 0x1C, 0x8A, 0x42, + 0x1F, 0xD9, 0x50, 0x45, + 0x0C, 0xDA, 0x40, 0x1C, 0xC2, 0xB2, 0x62, 0x70, 0xD5, 0xE9, 0x00, 0x76, + 0x01, 0x20, 0x00, 0x21, + 0x0F, 0xF0, 0x6C, 0xFA, 0x07, 0x43, 0x0E, 0x43, 0xC5, 0xE9, 0x00, 0x76, + 0x9B, 0xF8, 0x00, 0x00, + 0x68, 0xB1, 0x40, 0x1E, 0x8B, 0xF8, 0x00, 0x00, 0x22, 0x78, 0xD5, 0xE9, + 0x00, 0x67, 0x01, 0x20, + 0x00, 0x21, 0x0F, 0xF0, 0x5B, 0xFA, 0x06, 0x43, 0x0F, 0x43, 0xC5, 0xE9, + 0x00, 0x67, 0x08, 0xF1, + 0x01, 0x00, 0x5C, 0x46, 0x00, 0xF0, 0xFF, 0x08, 0x99, 0xF8, 0x18, 0x00, + 0x40, 0x45, 0xCD, 0xD8, + 0x60, 0x78, 0x50, 0x45, 0x0C, 0xDA, 0x40, 0x1C, 0xC2, 0xB2, 0x62, 0x70, + 0xD5, 0xE9, 0x00, 0x46, + 0x01, 0x20, 0x00, 0x21, 0x0F, 0xF0, 0x42, 0xFA, 0x04, 0x43, 0x0E, 0x43, + 0xC5, 0xE9, 0x00, 0x46, + 0x01, 0x20, 0x04, 0x46, 0x00, 0x90, 0x66, 0xE0, 0x02, 0x98, 0x22, 0x46, + 0x00, 0x21, 0xD0, 0xE9, + 0x00, 0x67, 0x01, 0x20, 0x0F, 0xF0, 0x32, 0xFA, 0x80, 0x46, 0x8B, 0x46, + 0x06, 0xEA, 0x08, 0x06, + 0x07, 0xEA, 0x0B, 0x07, 0x3E, 0x43, 0x27, 0xD0, 0xD5, 0xE9, 0x00, 0x67, + 0x46, 0xEA, 0x08, 0x06, + 0x47, 0xEA, 0x0B, 0x07, 0x62, 0x1E, 0x01, 0x20, 0x00, 0x21, 0xC5, 0xE9, + 0x00, 0x67, 0x0F, 0xF0, + 0x1D, 0xFA, 0x06, 0x40, 0x0F, 0x40, 0x3E, 0x43, 0x05, 0xD0, 0x00, 0x99, + 0x09, 0xEB, 0x41, 0x00, + 0x41, 0x78, 0x49, 0x1C, 0x41, 0x70, 0xD5, 0xE9, 0x00, 0x67, 0x62, 0x1C, + 0x01, 0x20, 0x00, 0x21, + 0x0F, 0xF0, 0x0C, 0xFA, 0x06, 0x40, 0x0F, 0x40, 0x3E, 0x43, 0x05, 0xD0, + 0x00, 0x99, 0x09, 0xEB, + 0x41, 0x01, 0x88, 0x78, 0x40, 0x1E, 0x88, 0x70, 0xD5, 0xE9, 0x00, 0x67, + 0x62, 0x1E, 0x01, 0x20, + 0x00, 0x21, 0x0F, 0xF0, 0xFB, 0xF9, 0x30, 0x40, 0x39, 0x40, 0x08, 0x43, + 0x21, 0xD0, 0x58, 0xEA, + 0x0B, 0x01, 0x0F, 0xD0, 0x00, 0x20, 0x07, 0xE0, 0x74, 0x05, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, + 0xED, 0x06, 0x10, 0x00, 0xEE, 0x06, 0x10, 0x00, 0x06, 0x40, 0x07, 0xEA, + 0xE0, 0x77, 0x3E, 0x43, + 0x0B, 0xD1, 0x01, 0xE0, 0x01, 0x20, 0xEE, 0xE7, 0x02, 0x99, 0xD1, 0xE9, + 0x00, 0x01, 0x00, 0xEA, + 0x08, 0x00, 0x01, 0xEA, 0x0B, 0x01, 0x08, 0x43, 0x03, 0xD0, 0x00, 0x98, + 0x40, 0x1C, 0xC0, 0xB2, + 0x00, 0x90, 0x64, 0x1C, 0xE4, 0xB2, 0x54, 0x45, 0x96, 0xDB, 0x05, 0xB0, + 0x56, 0xE6, 0x2D, 0xE9, + 0xFF, 0x5F, 0x05, 0x46, 0xFE, 0x49, 0xDD, 0xF8, 0x38, 0xB0, 0x00, 0x20, + 0x05, 0xEB, 0x40, 0x03, + 0x40, 0x1C, 0x0C, 0x88, 0xC0, 0xB2, 0x1C, 0x80, 0x06, 0x28, 0xF7, 0xD3, + 0x00, 0x20, 0x28, 0x73, + 0xF8, 0x48, 0x00, 0x68, 0xB0, 0xF9, 0x6E, 0x82, 0xB2, 0xF9, 0x08, 0x00, + 0x40, 0x45, 0x02, 0xDB, + 0xB8, 0xF1, 0x00, 0x0F, 0x00, 0xD1, 0x80, 0x46, 0x4F, 0xF0, 0x01, 0x0A, + 0x00, 0x24, 0x28, 0xE0, + 0x03, 0x98, 0x05, 0xEB, 0x4A, 0x09, 0x22, 0x46, 0xD0, 0xE9, 0x00, 0x67, + 0x01, 0x20, 0x00, 0x21, + 0x0F, 0xF0, 0xA4, 0xF9, 0x06, 0x40, 0x0F, 0x40, 0x3E, 0x43, 0x04, 0xD1, + 0x01, 0x98, 0x30, 0xF9, + 0x14, 0x10, 0x41, 0x45, 0x0B, 0xDD, 0x99, 0xF8, 0x00, 0x00, 0xFF, 0x28, + 0x04, 0xD1, 0x89, 0xF8, + 0x00, 0x40, 0x28, 0x7B, 0x40, 0x1C, 0x28, 0x73, 0x89, 0xF8, 0x01, 0x40, + 0x07, 0xE0, 0x99, 0xF8, + 0x00, 0x00, 0xFF, 0x28, 0x03, 0xD0, 0x0A, 0xF1, 0x01, 0x00, 0x00, 0xF0, + 0xFF, 0x0A, 0x64, 0x1C, + 0xE4, 0xB2, 0x5C, 0x45, 0xD4, 0xD3, 0xBD, 0xE8, 0xFF, 0x9F, 0x2D, 0xE9, + 0xFF, 0x4F, 0x85, 0xB0, + 0x00, 0x20, 0x05, 0x46, 0x02, 0x90, 0x04, 0x90, 0xD7, 0x48, 0x9B, 0x46, + 0x8A, 0x46, 0x06, 0x68, + 0x01, 0x20, 0x81, 0x46, 0x05, 0x98, 0x01, 0x7E, 0x49, 0x45, 0x7D, 0xD3, + 0x05, 0x98, 0x01, 0x27, + 0x10, 0xF8, 0x19, 0x10, 0x40, 0x46, 0x61, 0xF3, 0x17, 0x40, 0x05, 0x99, + 0x01, 0xEB, 0x49, 0x01, + 0x49, 0x78, 0x61, 0xF3, 0x1F, 0x60, 0x80, 0x46, 0x69, 0xE0, 0x00, 0xBF, + 0x1A, 0xF8, 0x17, 0x10, + 0x40, 0x46, 0x61, 0xF3, 0x07, 0x00, 0x0A, 0xEB, 0x47, 0x01, 0x01, 0x23, + 0x49, 0x78, 0x61, 0xF3, + 0x0F, 0x20, 0x80, 0x46, 0x00, 0x20, 0x86, 0xF8, 0x0E, 0x01, 0x02, 0x46, + 0xC3, 0x48, 0xC6, 0xF8, + 0x00, 0x80, 0x44, 0x46, 0x31, 0x46, 0x00, 0x68, 0x06, 0xF0, 0x62, 0xFF, + 0x0B, 0xEB, 0x85, 0x00, + 0x44, 0x66, 0x00, 0x20, 0x4B, 0xF8, 0x25, 0x00, 0x4F, 0xF0, 0x00, 0x40, + 0x01, 0x90, 0x00, 0x20, + 0x01, 0x24, 0x03, 0x90, 0x3C, 0xE0, 0x00, 0xBF, 0x06, 0xEB, 0x44, 0x00, + 0xB0, 0xF8, 0xB4, 0x10, + 0x07, 0x98, 0x81, 0x42, 0x2D, 0xD9, 0xB6, 0x48, 0x06, 0xEB, 0x84, 0x03, + 0xE2, 0xB2, 0x01, 0x68, + 0xB2, 0x48, 0x00, 0x68, 0xFE, 0xF7, 0x7A, 0xFE, 0x01, 0x99, 0x00, 0x90, + 0x88, 0x42, 0x0F, 0xDD, + 0x03, 0x98, 0x48, 0xB1, 0xC1, 0xB2, 0xAD, 0x48, 0x32, 0x46, 0x00, 0x68, + 0x07, 0xF0, 0x2D, 0xF8, + 0x02, 0x98, 0x40, 0x1E, 0xC0, 0xB2, 0x02, 0x90, 0x00, 0x98, 0x01, 0x90, + 0x60, 0xB2, 0x03, 0x90, + 0x5B, 0xF8, 0x25, 0x10, 0x00, 0x98, 0x01, 0x44, 0x4B, 0xF8, 0x25, 0x10, + 0x02, 0x98, 0x04, 0x99, + 0x40, 0x1C, 0xC0, 0xB2, 0x02, 0x90, 0x01, 0x20, 0xA8, 0x40, 0x08, 0x43, + 0x80, 0xB2, 0x04, 0x90, + 0x05, 0xE0, 0x9E, 0x48, 0xE1, 0xB2, 0x32, 0x46, 0x00, 0x68, 0x07, 0xF0, + 0x0E, 0xF8, 0x64, 0x1C, + 0x96, 0xF8, 0x0E, 0x01, 0xA0, 0x42, 0xBF, 0xD2, 0x6D, 0x1C, 0xED, 0xB2, + 0x7F, 0x1C, 0x9A, 0xF8, + 0x18, 0x00, 0xB8, 0x42, 0x92, 0xD2, 0x00, 0xE0, 0x02, 0xE0, 0x09, 0xF1, + 0x01, 0x00, 0x78, 0xE7, + 0x94, 0x48, 0x04, 0x99, 0xA0, 0xF8, 0x19, 0x10, 0x02, 0x98, 0x6E, 0xE5, + 0x2D, 0xE9, 0xFF, 0x5F, + 0xDF, 0xF8, 0x44, 0x92, 0x91, 0x4E, 0x09, 0xF1, 0x20, 0x00, 0x03, 0x90, + 0x06, 0xF1, 0x20, 0x00, + 0x02, 0x90, 0x90, 0x49, 0x8E, 0x48, 0xDF, 0xF8, 0x40, 0xB2, 0x1E, 0xC9, + 0x80, 0xE8, 0x1E, 0x00, + 0x8E, 0x49, 0x00, 0x20, 0x8B, 0xF8, 0x00, 0x00, 0x08, 0x72, 0x82, 0x48, + 0x31, 0x7E, 0x00, 0x68, + 0x90, 0xF8, 0x51, 0x02, 0x00, 0xF0, 0x01, 0x08, 0x99, 0xF8, 0x18, 0x00, + 0xC8, 0x42, 0x05, 0xD1, + 0x43, 0x46, 0x83, 0x4A, 0x31, 0x46, 0x48, 0x46, 0xFE, 0xF7, 0x3E, 0xFD, + 0x99, 0xF8, 0x18, 0x40, + 0x37, 0x7E, 0x04, 0xFB, 0x07, 0xF0, 0x10, 0xF0, 0xFF, 0x05, 0x77, 0xD0, + 0x19, 0x2D, 0x75, 0xD2, + 0x05, 0x2C, 0x73, 0xD8, 0x05, 0x2F, 0x71, 0xD8, 0x79, 0x48, 0xFC, 0x21, + 0x10, 0x30, 0x0F, 0xF0, + 0x85, 0xF9, 0x77, 0x48, 0x39, 0x46, 0x10, 0x30, 0x80, 0xF8, 0xFA, 0x50, + 0x20, 0x46, 0x0B, 0xF0, + 0x67, 0xF9, 0x00, 0xF0, 0xFF, 0x0A, 0x72, 0x48, 0x08, 0x30, 0x01, 0x90, + 0xB8, 0xF1, 0x00, 0x0F, + 0x0D, 0xD0, 0x73, 0x48, 0x6E, 0x4A, 0x03, 0x99, 0x03, 0x78, 0x48, 0x46, + 0xFF, 0xF7, 0xFB, 0xFD, + 0x70, 0x48, 0xDD, 0xE9, 0x01, 0x21, 0x03, 0x78, 0x30, 0x46, 0xFF, 0xF7, + 0xF4, 0xFD, 0x6E, 0x48, + 0x00, 0x90, 0x6E, 0x48, 0x66, 0x49, 0xB0, 0xF9, 0x00, 0x30, 0x60, 0x48, + 0x02, 0x68, 0x60, 0x48, + 0x00, 0x68, 0xFE, 0xF7, 0x5A, 0xFD, 0x6A, 0x49, 0x5A, 0x4F, 0x61, 0x4B, + 0x08, 0x60, 0x38, 0x68, + 0x10, 0x33, 0x31, 0x46, 0x90, 0xF8, 0x6D, 0x22, 0x48, 0x46, 0xFF, 0xF7, + 0xF6, 0xFE, 0x04, 0x46, + 0x38, 0x68, 0x90, 0xF8, 0x51, 0x02, 0x80, 0x07, 0x28, 0xD5, 0x5D, 0x48, + 0xDF, 0xF8, 0x58, 0x81, + 0x56, 0x4F, 0x00, 0x78, 0x08, 0xF1, 0x30, 0x08, 0x00, 0x90, 0x30, 0x37, + 0x54, 0x4B, 0x5D, 0x49, + 0x40, 0x46, 0x03, 0x9A, 0xFF, 0xF7, 0x93, 0xFE, 0x56, 0x48, 0x5B, 0x49, + 0x00, 0x78, 0x00, 0x90, + 0xDD, 0xE9, 0x01, 0x32, 0x38, 0x46, 0xFF, 0xF7, 0x8A, 0xFE, 0x4A, 0x48, + 0x98, 0xF8, 0x0C, 0x20, + 0x81, 0x7D, 0x62, 0xF3, 0x03, 0x01, 0x81, 0x75, 0x3A, 0x7B, 0x62, 0xF3, + 0x07, 0x11, 0x81, 0x75, + 0x47, 0x4A, 0x39, 0x46, 0x10, 0x32, 0x40, 0x46, 0x00, 0xF0, 0x3A, 0xF8, + 0xA2, 0x45, 0x06, 0xD8, + 0xAC, 0x42, 0x04, 0xD8, 0x01, 0x21, 0x8B, 0xF8, 0x00, 0x10, 0x0A, 0xE0, + 0x10, 0xE0, 0x00, 0x20, + 0x3F, 0x4A, 0x8B, 0xF8, 0x00, 0x00, 0x41, 0x4B, 0x10, 0x32, 0x31, 0x46, + 0x48, 0x46, 0xFF, 0xF7, + 0xE4, 0xFC, 0x38, 0x48, 0x01, 0x7E, 0x6A, 0xF3, 0x03, 0x01, 0x64, 0xF3, + 0x07, 0x11, 0x01, 0x76, + 0xA9, 0xE6, 0x10, 0xB5, 0x01, 0x21, 0x31, 0x48, 0x08, 0xF0, 0x31, 0xFD, + 0x02, 0x21, 0x3F, 0x48, + 0x08, 0xF0, 0xD5, 0xFC, 0x04, 0x21, 0x2C, 0x48, 0x08, 0xF0, 0xD1, 0xFC, + 0xFF, 0xF7, 0x36, 0xFF, + 0xFE, 0xF7, 0x2D, 0xFE, 0x02, 0x21, 0x39, 0x48, 0x08, 0xF0, 0xE6, 0xFC, + 0x04, 0x21, 0x26, 0x48, + 0x08, 0xF0, 0xE2, 0xFC, 0xBD, 0xE8, 0x10, 0x40, 0x01, 0x21, 0x24, 0x48, + 0x08, 0xF0, 0xAA, 0xBD, + 0x2D, 0xE9, 0xF0, 0x47, 0x14, 0x46, 0x0D, 0x46, 0x06, 0x46, 0x00, 0xF0, + 0x8B, 0xF8, 0x30, 0x7B, + 0x29, 0x7B, 0x1C, 0x4F, 0x48, 0x43, 0xC5, 0xB2, 0x01, 0x20, 0x30, 0xE0, + 0x00, 0x21, 0x0A, 0x46, + 0x94, 0xF8, 0xFA, 0x60, 0x0C, 0xE0, 0x00, 0xBF, 0x54, 0xF8, 0x22, 0x30, + 0x8B, 0x42, 0x06, 0xDD, + 0x04, 0xEB, 0x02, 0x0C, 0x9C, 0xF8, 0xE1, 0xC0, 0x84, 0x45, 0x00, 0xD1, + 0x19, 0x46, 0x52, 0x1C, + 0x96, 0x42, 0xF1, 0xD8, 0x3A, 0x68, 0xB2, 0xF8, 0x70, 0x22, 0x8A, 0x42, + 0x15, 0xDA, 0x00, 0x22, + 0x0F, 0xE0, 0xA3, 0x18, 0x93, 0xF8, 0xE1, 0x60, 0x86, 0x42, 0x09, 0xD1, + 0x54, 0xF8, 0x22, 0x60, + 0x4F, 0xF0, 0x64, 0x0C, 0x06, 0xFB, 0x0C, 0xF6, 0x96, 0xFB, 0xF1, 0xF6, + 0x83, 0xF8, 0xC8, 0x60, + 0x52, 0x1C, 0x94, 0xF8, 0xFA, 0x30, 0x93, 0x42, 0xEB, 0xD8, 0x40, 0x1C, + 0xC0, 0xB2, 0x27, 0xE0, + 0xFC, 0x78, 0x01, 0x00, 0x4C, 0x07, 0x10, 0x00, 0x58, 0x05, 0x10, 0x00, + 0x50, 0x06, 0x10, 0x00, + 0x14, 0x07, 0x10, 0x00, 0x0A, 0x44, 0x01, 0x20, 0x18, 0x2A, 0x10, 0x00, + 0x58, 0x2A, 0x10, 0x00, + 0x60, 0x2B, 0x10, 0x00, 0x40, 0x2B, 0x10, 0x00, 0x57, 0x05, 0x10, 0x00, + 0x0C, 0x2A, 0x10, 0x00, + 0xED, 0x06, 0x10, 0x00, 0xEE, 0x06, 0x10, 0x00, 0x84, 0x06, 0x10, 0x00, + 0x92, 0x06, 0x10, 0x00, + 0x7C, 0x06, 0x10, 0x00, 0x94, 0x0B, 0x01, 0x20, 0xBE, 0x0B, 0x01, 0x20, + 0x60, 0x06, 0x10, 0x00, + 0xA8, 0x42, 0xA3, 0xD9, 0x00, 0x26, 0x35, 0x46, 0xB8, 0x46, 0xB1, 0x46, + 0xDF, 0xF8, 0xD4, 0xA0, + 0x18, 0xE0, 0xD8, 0xF8, 0x00, 0x00, 0x67, 0x19, 0x97, 0xF8, 0xC8, 0x10, + 0x90, 0xF8, 0x72, 0x02, + 0x81, 0x42, 0x0E, 0xD2, 0x04, 0xEB, 0x85, 0x01, 0x00, 0x22, 0x64, 0x31, + 0xDA, 0xF8, 0x00, 0x00, + 0x06, 0xF0, 0xCE, 0xFC, 0x44, 0xF8, 0x25, 0x90, 0x97, 0xF8, 0xC8, 0x00, + 0xB0, 0x42, 0x00, 0xD2, + 0x06, 0x46, 0x6D, 0x1C, 0x94, 0xF8, 0xFA, 0x00, 0xA8, 0x42, 0xE2, 0xD8, + 0x26, 0x48, 0xC6, 0x75, + 0xBD, 0xE8, 0xF0, 0x87, 0x2D, 0xE9, 0xF8, 0x4F, 0x82, 0x46, 0x01, 0x24, + 0x16, 0x46, 0x0F, 0x46, + 0x20, 0x46, 0x9A, 0xF8, 0x0C, 0x10, 0x81, 0x46, 0x49, 0x45, 0x38, 0xD3, + 0x1A, 0xF8, 0x14, 0x00, + 0x8D, 0xF8, 0x02, 0x00, 0x0A, 0xEB, 0x44, 0x00, 0x40, 0x78, 0x8D, 0xF8, + 0x03, 0x00, 0x01, 0x20, + 0x26, 0xE0, 0x00, 0xBF, 0x17, 0xF8, 0x14, 0x00, 0x8D, 0xF8, 0x00, 0x00, + 0x07, 0xEB, 0x44, 0x00, + 0x00, 0x25, 0x40, 0x78, 0x8D, 0xF8, 0x01, 0x00, 0x12, 0xE0, 0x00, 0xBF, + 0x06, 0xEB, 0x05, 0x0B, + 0x9B, 0xF8, 0xE1, 0x00, 0x58, 0xB9, 0x06, 0xEB, 0x85, 0x01, 0x68, 0x46, + 0x64, 0x31, 0x06, 0xF0, + 0x7B, 0xFC, 0x20, 0xB1, 0x8B, 0xF8, 0xE1, 0x40, 0x64, 0x20, 0x8B, 0xF8, + 0xC8, 0x00, 0x6D, 0x1C, + 0x96, 0xF8, 0xFA, 0x00, 0xA8, 0x42, 0xE9, 0xD8, 0x64, 0x1C, 0xE4, 0xB2, + 0x08, 0xF1, 0x01, 0x00, + 0x39, 0x7B, 0x80, 0x46, 0x41, 0x45, 0xD5, 0xD2, 0x09, 0xF1, 0x01, 0x00, + 0xC1, 0xE7, 0xBD, 0xE8, + 0xF8, 0x8F, 0x00, 0x00, 0x50, 0x06, 0x10, 0x00, 0x0A, 0x44, 0x01, 0x20, + 0x2D, 0xE9, 0xFF, 0x4F, + 0xCE, 0x4B, 0x83, 0xB0, 0x1B, 0x68, 0xB3, 0xF9, 0xC2, 0x42, 0xB3, 0xF9, + 0xC4, 0xE2, 0x99, 0xE0, + 0x47, 0xF6, 0xFF, 0x76, 0xF7, 0x43, 0x06, 0x9D, 0x08, 0xE0, 0x00, 0xBF, + 0x31, 0xF9, 0x15, 0x30, + 0xB3, 0x42, 0x00, 0xDA, 0x1E, 0x46, 0xBB, 0x42, 0x00, 0xDD, 0x1F, 0x46, + 0x6D, 0x1E, 0xF5, 0xD1, + 0xAB, 0x46, 0x00, 0x2F, 0x01, 0xDA, 0xBB, 0x46, 0x02, 0xE0, 0x00, 0x2E, + 0x00, 0xDD, 0xB3, 0x46, + 0x00, 0x26, 0x37, 0x46, 0xB2, 0x46, 0x35, 0x46, 0xB1, 0x46, 0x06, 0x9B, + 0x13, 0xE0, 0x00, 0xBF, + 0x31, 0xF9, 0x13, 0x80, 0xA8, 0xEB, 0x0B, 0x0C, 0xA4, 0x45, 0x0C, 0xDA, + 0x0C, 0xEB, 0x04, 0x08, + 0xB8, 0xF1, 0x00, 0x0F, 0x07, 0xDD, 0x03, 0xFB, 0x03, 0x77, 0x03, 0xFB, + 0x0C, 0x55, 0x1E, 0x44, + 0xE2, 0x44, 0x09, 0xF1, 0x01, 0x09, 0x5B, 0x1E, 0xEA, 0xD1, 0x07, 0xFB, + 0x09, 0xF3, 0x06, 0xFB, + 0x16, 0x33, 0x02, 0x93, 0x05, 0xFB, 0x09, 0xF3, 0x06, 0xFB, 0x1A, 0x33, + 0x07, 0xFB, 0x0A, 0xF7, + 0x01, 0x93, 0x05, 0xFB, 0x16, 0x73, 0x4F, 0xF0, 0x00, 0x0C, 0x00, 0x93, + 0x67, 0x46, 0x66, 0x46, + 0xE2, 0x46, 0x06, 0x9B, 0x28, 0xE0, 0xDD, 0xF8, 0x08, 0x80, 0x00, 0x25, + 0xB8, 0xF1, 0x00, 0x0F, + 0x0D, 0xD0, 0xDD, 0xE9, 0x00, 0x58, 0x08, 0xFB, 0x03, 0x58, 0x02, 0x9D, + 0x98, 0xFB, 0xF5, 0xF5, + 0xA5, 0x42, 0x01, 0xDD, 0x25, 0x46, 0x02, 0xE0, 0xE5, 0x42, 0x00, 0xD5, + 0x65, 0x42, 0x31, 0xF8, + 0x13, 0x80, 0x5D, 0x44, 0xA8, 0xEB, 0x05, 0x05, 0x2D, 0xB2, 0x7F, 0x1C, + 0xA9, 0x46, 0xAC, 0x44, + 0x75, 0x45, 0x07, 0xDA, 0x05, 0xEB, 0x0E, 0x08, 0xB8, 0xF1, 0x00, 0x0F, + 0x02, 0xDD, 0x4E, 0x44, + 0x0A, 0xF1, 0x01, 0x0A, 0x20, 0xF8, 0x13, 0x90, 0x5B, 0x1E, 0xD4, 0xD1, + 0x9C, 0xFB, 0xF7, 0xF5, + 0xBA, 0xF1, 0x00, 0x0F, 0x01, 0xD1, 0x4F, 0xF0, 0x01, 0x0A, 0x96, 0xFB, + 0xFA, 0xF3, 0x00, 0x2D, + 0x01, 0xDB, 0x2F, 0x46, 0x00, 0xE0, 0x6F, 0x42, 0x1E, 0x1E, 0x00, 0xDA, + 0x5E, 0x42, 0xB7, 0xEB, + 0x46, 0x0F, 0x00, 0xDD, 0x1D, 0x46, 0x06, 0x9B, 0x05, 0xE0, 0x00, 0xBF, + 0x30, 0xF8, 0x13, 0x60, + 0x76, 0x1B, 0x20, 0xF8, 0x13, 0x60, 0x5B, 0x1E, 0xF8, 0xD1, 0x06, 0x9B, + 0x01, 0xEB, 0x43, 0x01, + 0x00, 0xEB, 0x43, 0x00, 0x52, 0x1E, 0xBF, 0xF4, 0x63, 0xAF, 0x07, 0xB0, + 0xBD, 0xE8, 0xF0, 0x8F, + 0x2D, 0xE9, 0xF7, 0x4F, 0x79, 0x4D, 0x7A, 0x4C, 0x7A, 0x4F, 0x28, 0x68, + 0x0E, 0x46, 0x90, 0xF8, + 0xC0, 0x02, 0xC0, 0x07, 0x00, 0xD0, 0x3C, 0x68, 0x03, 0x20, 0xF8, 0xF7, + 0x9A, 0xF9, 0x77, 0x48, + 0xDF, 0xF8, 0xD4, 0xB1, 0x31, 0x46, 0x02, 0x68, 0xBB, 0xF9, 0x00, 0x30, + 0x20, 0x46, 0x0B, 0xF0, + 0xB0, 0xFE, 0x03, 0x20, 0xF8, 0xF7, 0xA8, 0xF9, 0x28, 0x68, 0xDF, 0xF8, + 0xC4, 0xA1, 0x71, 0x4E, + 0x90, 0xF8, 0xC0, 0x02, 0xC0, 0x07, 0x06, 0xD0, 0x9A, 0xF8, 0x00, 0x30, + 0x32, 0x78, 0x21, 0x46, + 0x67, 0x48, 0xFF, 0xF7, 0x2B, 0xFF, 0xB9, 0x46, 0x65, 0x4A, 0x33, 0x78, + 0x4F, 0xF0, 0x00, 0x08, + 0x54, 0x46, 0x38, 0x68, 0x16, 0xE0, 0x00, 0xBF, 0xA0, 0xF8, 0x00, 0x80, + 0x01, 0x21, 0x09, 0xE0, + 0x00, 0xEB, 0x41, 0x07, 0x32, 0xF8, 0x11, 0xC0, 0x37, 0xF8, 0x02, 0x7C, + 0x67, 0x44, 0x20, 0xF8, + 0x11, 0x70, 0x49, 0x1C, 0x27, 0x78, 0xB9, 0x42, 0xF2, 0xDB, 0xF9, 0xB2, + 0x02, 0xEB, 0x41, 0x02, + 0x00, 0xEB, 0x41, 0x00, 0x5B, 0x1E, 0xE7, 0xD2, 0x5B, 0x48, 0x5C, 0x4F, + 0x02, 0x9B, 0x02, 0x68, + 0xD9, 0xF8, 0x00, 0x10, 0x38, 0x68, 0x00, 0xF0, 0x73, 0xFA, 0x28, 0x68, + 0x90, 0xF8, 0xC0, 0x02, + 0x80, 0x07, 0x06, 0xD5, 0x23, 0x78, 0x32, 0x78, 0x39, 0x68, 0x00, 0x98, + 0x00, 0xF0, 0x1C, 0xF8, + 0x06, 0xE0, 0xBB, 0xF9, 0x00, 0x00, 0x39, 0x68, 0x42, 0x00, 0x00, 0x98, + 0x0E, 0xF0, 0x55, 0xFE, + 0x28, 0x68, 0x90, 0xF8, 0xC0, 0x02, 0x00, 0x07, 0x08, 0xD5, 0x00, 0x99, + 0x23, 0x78, 0x32, 0x78, + 0x03, 0xB0, 0x08, 0x46, 0xBD, 0xE8, 0xF0, 0x4F, 0x00, 0xF0, 0x05, 0xBC, + 0xBD, 0xE8, 0xFE, 0x8F, + 0x47, 0x49, 0x00, 0x20, 0x08, 0x70, 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x4F, + 0xA7, 0xB0, 0x1C, 0x46, + 0x0D, 0x46, 0x06, 0x46, 0x17, 0x46, 0xEA, 0x46, 0x0D, 0xF1, 0x4C, 0x0B, + 0x3C, 0xE0, 0x00, 0x22, + 0x60, 0x1E, 0x00, 0xF0, 0xFF, 0x09, 0x11, 0x46, 0x10, 0x46, 0x11, 0xE0, + 0x35, 0xF9, 0x10, 0x30, + 0x1A, 0x44, 0x43, 0x1C, 0x92, 0xFB, 0xF3, 0xFC, 0x2A, 0xF8, 0x10, 0xC0, + 0xA9, 0xEB, 0x00, 0x0C, + 0x40, 0x1C, 0x35, 0xF9, 0x1C, 0x80, 0x41, 0x44, 0x91, 0xFB, 0xF3, 0xF3, + 0x2B, 0xF8, 0x1C, 0x30, + 0xA0, 0x42, 0xEB, 0xDB, 0x20, 0x46, 0x52, 0x46, 0x5B, 0x46, 0x0C, 0xE0, + 0x32, 0xF9, 0x10, 0x10, + 0x33, 0xF9, 0x10, 0xC0, 0x61, 0x44, 0x35, 0xF8, 0x10, 0xC0, 0x01, 0xEB, + 0xD1, 0x71, 0xAC, 0xEB, + 0x61, 0x01, 0x26, 0xF8, 0x10, 0x10, 0x40, 0x1E, 0xF0, 0xD2, 0x20, 0x48, + 0x00, 0x68, 0x90, 0xF8, + 0xC0, 0x02, 0x40, 0x07, 0x04, 0xD5, 0x31, 0x46, 0x22, 0x46, 0x08, 0x46, + 0x00, 0xF0, 0x08, 0xF8, + 0x05, 0xEB, 0x44, 0x05, 0x06, 0xEB, 0x44, 0x06, 0x7F, 0x1E, 0xC0, 0xD2, + 0x27, 0xB0, 0x35, 0xE7, + 0xF0, 0xB5, 0x16, 0x4B, 0x00, 0x26, 0x35, 0x46, 0xD3, 0xF8, 0x00, 0xE0, + 0x34, 0x46, 0x13, 0x46, + 0xBE, 0xF9, 0xC6, 0x72, 0xC7, 0xF1, 0x00, 0x0C, 0x06, 0xE0, 0x00, 0xBF, + 0x31, 0xF9, 0x13, 0x70, + 0x67, 0x45, 0x01, 0xDA, 0x3E, 0x44, 0x6D, 0x1C, 0x5B, 0x1E, 0xF7, 0xD2, + 0x00, 0x2D, 0x01, 0xDD, + 0x96, 0xFB, 0xF5, 0xF4, 0xBE, 0xF8, 0xC8, 0x32, 0x5E, 0x42, 0xA6, 0x42, + 0x00, 0xDD, 0x1C, 0x44, + 0x53, 0x1B, 0x63, 0x43, 0x93, 0xFB, 0xF2, 0xF3, 0x05, 0xE0, 0x00, 0xBF, + 0x31, 0xF8, 0x12, 0x40, + 0xE4, 0x1A, 0x20, 0xF8, 0x12, 0x40, 0x52, 0x1E, 0xF8, 0xD2, 0xF0, 0xBD, + 0x4C, 0x07, 0x10, 0x00, + 0x4A, 0x0C, 0x01, 0x20, 0x80, 0x05, 0x10, 0x00, 0xFE, 0x06, 0x10, 0x00, + 0x1C, 0x07, 0x10, 0x00, + 0xEE, 0x06, 0x10, 0x00, 0xED, 0x06, 0x10, 0x00, 0x9C, 0x06, 0x10, 0x00, + 0x18, 0x07, 0x10, 0x00, + 0xF3, 0x06, 0x10, 0x00, 0x2D, 0xE9, 0xFF, 0x4F, 0x83, 0xB0, 0x9A, 0x46, + 0x17, 0x46, 0x89, 0x46, + 0x00, 0x25, 0x4F, 0xF0, 0xAA, 0x0B, 0xDD, 0xF8, 0x40, 0x80, 0x5B, 0xE0, + 0x11, 0x98, 0x05, 0xFB, + 0x00, 0xF6, 0x05, 0xEB, 0x85, 0x00, 0xC4, 0x00, 0x1A, 0xF8, 0x04, 0x00, + 0x08, 0xB9, 0x38, 0x5D, + 0xE8, 0xB3, 0x0A, 0xF8, 0x04, 0xB0, 0x39, 0xF9, 0x16, 0x00, 0x00, 0xF0, + 0xC9, 0xF8, 0x29, 0xF8, + 0x16, 0x00, 0x38, 0x5D, 0x98, 0xB3, 0x39, 0x19, 0x01, 0x91, 0x11, 0xF8, + 0x28, 0x1C, 0x81, 0x42, + 0x1E, 0xD0, 0xAA, 0x28, 0x1C, 0xD0, 0x01, 0x98, 0x00, 0xF8, 0x28, 0xBC, + 0x11, 0x98, 0x31, 0x1A, + 0x03, 0x98, 0x00, 0x91, 0x30, 0xF9, 0x11, 0x00, 0x00, 0xF0, 0xB2, 0xF8, + 0x03, 0x9A, 0x00, 0x99, + 0x22, 0xF8, 0x11, 0x00, 0x0A, 0xEB, 0x04, 0x01, 0x01, 0xF8, 0x28, 0xBC, + 0x00, 0x99, 0x39, 0xF9, + 0x11, 0x00, 0x00, 0xF0, 0xA5, 0xF8, 0x00, 0x99, 0x29, 0xF8, 0x11, 0x00, + 0x38, 0x5D, 0x70, 0xB1, + 0x01, 0x98, 0x90, 0xF8, 0x28, 0x10, 0x38, 0x5D, 0x81, 0x42, 0x1A, 0xD0, + 0xAA, 0x28, 0x18, 0xD0, + 0x01, 0x98, 0x80, 0xF8, 0x28, 0xB0, 0x11, 0x98, 0x06, 0x44, 0x03, 0x98, + 0x00, 0xE0, 0x10, 0xE0, + 0x30, 0xF9, 0x16, 0x00, 0x00, 0xF0, 0x8C, 0xF8, 0x03, 0x99, 0x21, 0xF8, + 0x16, 0x00, 0x0A, 0xEB, + 0x04, 0x01, 0x81, 0xF8, 0x28, 0xB0, 0x39, 0xF9, 0x16, 0x00, 0x00, 0xF0, + 0x81, 0xF8, 0x29, 0xF8, + 0x16, 0x00, 0x6D, 0x1C, 0x45, 0x45, 0xA1, 0xD3, 0x07, 0xB0, 0xBD, 0xE8, + 0xF0, 0x8F, 0x44, 0x49, + 0x00, 0x20, 0x08, 0x60, 0x70, 0x47, 0x42, 0x49, 0x08, 0x60, 0x70, 0x47, + 0x2D, 0xE9, 0xF0, 0x4F, + 0x05, 0x46, 0x40, 0x48, 0x8B, 0xB0, 0x17, 0x46, 0x00, 0x78, 0x8A, 0x46, + 0x00, 0x28, 0x65, 0xD0, + 0x3D, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x00, 0x04, 0x80, 0x07, 0x5F, 0xD5, + 0x3B, 0x48, 0x04, 0x90, + 0x3B, 0x48, 0xDF, 0xF8, 0xDC, 0xB0, 0x01, 0x24, 0x90, 0xF8, 0x00, 0x80, + 0xDB, 0xF8, 0x00, 0x90, + 0x22, 0xE0, 0x01, 0x20, 0xA0, 0x40, 0x10, 0xEA, 0x09, 0x0F, 0x1C, 0xD1, + 0x35, 0x48, 0x00, 0xEB, + 0x84, 0x06, 0x04, 0x98, 0xB1, 0x78, 0x0E, 0xF0, 0xA9, 0xFE, 0x01, 0x46, + 0xB2, 0x78, 0x00, 0x23, + 0x0E, 0xE0, 0x30, 0x78, 0x06, 0xE0, 0x00, 0xBF, 0x11, 0xF8, 0x00, 0xC0, + 0xA4, 0x45, 0x00, 0xD1, + 0x0B, 0x54, 0x40, 0x1C, 0x96, 0xF8, 0x01, 0xC0, 0x84, 0x45, 0xF5, 0xDA, + 0x28, 0x31, 0x52, 0x1C, + 0xF0, 0x78, 0x90, 0x42, 0xED, 0xDA, 0x64, 0x1C, 0x44, 0x45, 0xDA, 0xD9, + 0xA8, 0x46, 0x05, 0xF1, + 0x02, 0x09, 0x00, 0x21, 0x21, 0x48, 0x0E, 0xF0, 0x89, 0xFE, 0x20, 0x4E, + 0x07, 0x90, 0x00, 0x21, + 0x70, 0x1C, 0x0E, 0xF0, 0x83, 0xFE, 0x05, 0xEB, 0x47, 0x04, 0xA5, 0x1E, + 0x3E, 0x44, 0x06, 0x90, + 0x70, 0x1E, 0x00, 0x21, 0x24, 0x1F, 0x0E, 0xF0, 0x79, 0xFE, 0x03, 0x90, + 0xB0, 0x1E, 0x00, 0x21, + 0x0E, 0xF0, 0x74, 0xFE, 0xCD, 0xE9, 0x00, 0xA7, 0xDD, 0xE9, 0x06, 0x32, + 0x06, 0x46, 0x49, 0x46, + 0x40, 0x46, 0xFF, 0xF7, 0x27, 0xFF, 0xCD, 0xE9, 0x00, 0xA7, 0x33, 0x46, + 0x21, 0x46, 0x28, 0x46, + 0x03, 0x9A, 0xFF, 0xF7, 0x1F, 0xFF, 0x00, 0x20, 0xCB, 0xF8, 0x00, 0x00, + 0x0B, 0xB0, 0x84, 0xE7, + 0x09, 0x49, 0x09, 0x68, 0x91, 0xF8, 0x08, 0x24, 0x42, 0x43, 0x64, 0x20, + 0x92, 0xFB, 0xF0, 0xF0, + 0xB1, 0xF8, 0x06, 0x24, 0x00, 0xB2, 0x82, 0x42, 0x01, 0xDA, 0xB1, 0xF9, + 0x06, 0x04, 0x70, 0x47, + 0x7C, 0x05, 0x10, 0x00, 0xA4, 0x06, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0xC2, 0x18, 0x01, 0x20, + 0x39, 0x06, 0x10, 0x00, 0x5A, 0x1C, 0x01, 0x20, 0x2D, 0xE9, 0xF0, 0x4F, + 0xFE, 0x4C, 0x80, 0x25, + 0xFE, 0x4E, 0x23, 0x68, 0x93, 0xF8, 0x31, 0x10, 0x93, 0xF8, 0x30, 0x00, + 0x01, 0xFB, 0x00, 0xF2, + 0x02, 0xE0, 0x00, 0xBF, 0x26, 0xF8, 0x12, 0x50, 0x52, 0x1E, 0xFB, 0xD2, + 0x93, 0xF8, 0xA0, 0x22, + 0xA6, 0x46, 0x12, 0x07, 0x4F, 0xF0, 0x64, 0x08, 0x31, 0xD5, 0x4A, 0x1E, + 0x02, 0xFB, 0x00, 0xFA, + 0xDF, 0xF8, 0xC8, 0xB3, 0x03, 0xF5, 0x94, 0x69, 0x02, 0x46, 0x26, 0xE0, + 0x19, 0xF8, 0x02, 0x30, + 0x0A, 0xEB, 0x02, 0x04, 0xDB, 0x01, 0xB3, 0xFB, 0xF8, 0xF3, 0x0B, 0xEB, + 0x44, 0x05, 0x0C, 0x46, + 0x19, 0xE0, 0x80, 0x2B, 0x05, 0xDD, 0xC3, 0xF1, 0x80, 0x06, 0x66, 0x43, + 0x96, 0xFB, 0xF1, 0xF6, + 0x06, 0xE0, 0xA3, 0xF1, 0x80, 0x06, 0x06, 0xFB, 0x04, 0xF7, 0x97, 0xFB, + 0xF1, 0xF7, 0xF6, 0x1B, + 0xB5, 0xF9, 0x00, 0x70, 0x80, 0x36, 0x77, 0x43, 0xFE, 0x17, 0x07, 0xEB, + 0x56, 0x66, 0xF6, 0x11, + 0x2E, 0x80, 0xA5, 0xEB, 0x40, 0x05, 0x64, 0x1E, 0xE3, 0xD2, 0x52, 0x1E, + 0xD6, 0xD2, 0xDE, 0xF8, + 0x00, 0x20, 0xF2, 0x46, 0x92, 0xF8, 0xA0, 0x32, 0xDB, 0x06, 0x2F, 0xD5, + 0x02, 0xF5, 0x98, 0x69, + 0xD6, 0x4B, 0x4A, 0x1E, 0x42, 0x43, 0x03, 0xEB, 0x42, 0x05, 0x0A, 0x46, + 0x24, 0xE0, 0x00, 0xBF, + 0x19, 0xF8, 0x02, 0x30, 0xDB, 0x01, 0xB3, 0xFB, 0xF8, 0xF6, 0x03, 0x46, + 0x18, 0xE0, 0x80, 0x2E, + 0x05, 0xDD, 0xC6, 0xF1, 0x80, 0x04, 0x5C, 0x43, 0x94, 0xFB, 0xF0, 0xF4, + 0x06, 0xE0, 0xA6, 0xF1, + 0x80, 0x04, 0x04, 0xFB, 0x03, 0xF7, 0x97, 0xFB, 0xF0, 0xF7, 0xE4, 0x1B, + 0x35, 0xF9, 0x13, 0x70, + 0x80, 0x34, 0x67, 0x43, 0xFC, 0x17, 0x07, 0xEB, 0x54, 0x64, 0xE4, 0x11, + 0x25, 0xF8, 0x13, 0x40, + 0x5B, 0x1E, 0xE4, 0xD2, 0xA5, 0xEB, 0x40, 0x05, 0x52, 0x1E, 0xD9, 0xD2, + 0xDA, 0xF8, 0x00, 0x40, + 0x94, 0xF8, 0xA1, 0x22, 0xD2, 0x07, 0x20, 0xD0, 0xBC, 0x4E, 0x00, 0x23, + 0x04, 0xF5, 0x9C, 0x64, + 0x19, 0xE0, 0x00, 0xBF, 0x03, 0xFB, 0x00, 0xF2, 0x06, 0xEB, 0x42, 0x07, + 0x00, 0x22, 0x0F, 0xE0, + 0xA5, 0x5C, 0xED, 0x01, 0xB5, 0xFB, 0xF8, 0xFC, 0x37, 0xF9, 0x12, 0x50, + 0x05, 0xFB, 0x0C, 0xF5, + 0x4F, 0xEA, 0xE5, 0x7C, 0x05, 0xEB, 0x5C, 0x65, 0xED, 0x11, 0x27, 0xF8, + 0x12, 0x50, 0x52, 0x1C, + 0x82, 0x42, 0xED, 0xD3, 0x5B, 0x1C, 0x8B, 0x42, 0xE4, 0xD3, 0xBD, 0xE8, + 0xF0, 0x8F, 0x30, 0xB5, + 0x0B, 0xE0, 0x00, 0xBF, 0x31, 0xF9, 0x13, 0x40, 0x32, 0xF9, 0x13, 0x50, + 0x6C, 0x43, 0xE5, 0x17, + 0x04, 0xEB, 0x55, 0x64, 0xE4, 0x11, 0x20, 0xF8, 0x13, 0x40, 0x5B, 0x1E, + 0xF2, 0xD2, 0x30, 0xBD, + 0x08, 0xB5, 0x00, 0x93, 0x13, 0x46, 0xA2, 0x4A, 0xB2, 0xF9, 0x00, 0x20, + 0x0B, 0xF0, 0x06, 0xFC, + 0x08, 0xBD, 0x2D, 0xE9, 0xF0, 0x5F, 0xDF, 0xF8, 0x70, 0x92, 0xDF, 0xF8, + 0x78, 0xB2, 0xD9, 0xF8, + 0x00, 0x10, 0xDB, 0xF8, 0x00, 0xC0, 0xB1, 0xF9, 0x00, 0x23, 0xB1, 0xF9, + 0x22, 0x33, 0xB1, 0xF9, + 0x42, 0x63, 0x5F, 0xEA, 0x8C, 0x70, 0x07, 0xD5, 0x97, 0x48, 0x00, 0x78, + 0x01, 0x28, 0x03, 0xD1, + 0xB1, 0xF9, 0x4E, 0x33, 0xB1, 0xF9, 0x50, 0x23, 0x94, 0x48, 0x47, 0x78, + 0x01, 0x2F, 0x7C, 0xD0, + 0x02, 0x2F, 0x04, 0xD3, 0xB1, 0xF8, 0x04, 0x03, 0x08, 0xB1, 0xB1, 0xF9, + 0x04, 0x23, 0x90, 0x4D, + 0x28, 0x78, 0x98, 0xB1, 0x91, 0xF8, 0x69, 0x03, 0x64, 0x24, 0x00, 0xFB, + 0x02, 0xF8, 0x98, 0xFB, + 0xF4, 0xF8, 0x42, 0x44, 0x00, 0xFB, 0x03, 0xF8, 0x70, 0x43, 0x98, 0xFB, + 0xF4, 0xF8, 0x90, 0xFB, + 0xF4, 0xF0, 0x43, 0x44, 0x30, 0x44, 0x12, 0xB2, 0x1B, 0xB2, 0x06, 0xB2, + 0x85, 0x4C, 0xDF, 0xF8, + 0x18, 0xA2, 0xDF, 0xF8, 0x18, 0xE2, 0x22, 0x80, 0xAA, 0xF8, 0x00, 0x30, + 0xAE, 0xF8, 0x00, 0x60, + 0x91, 0xF8, 0x02, 0x03, 0xDF, 0xF8, 0x08, 0x92, 0x10, 0x1A, 0x00, 0xB2, + 0xA9, 0xF8, 0x00, 0x00, + 0x91, 0xF8, 0x24, 0x83, 0xA3, 0xEB, 0x08, 0x08, 0x7E, 0x4B, 0xA3, 0xF8, + 0x00, 0x80, 0x91, 0xF8, + 0x44, 0x13, 0x76, 0x1A, 0x7C, 0x49, 0x5F, 0xEA, 0xCC, 0x78, 0x0E, 0x80, + 0x01, 0xD0, 0x02, 0x2F, + 0x02, 0xD3, 0xAA, 0xF8, 0x00, 0x20, 0x18, 0x80, 0xDA, 0x46, 0x5F, 0xEA, + 0x8C, 0x76, 0x01, 0xD5, + 0x02, 0x2F, 0x02, 0xD3, 0xAE, 0xF8, 0x00, 0x20, 0x08, 0x80, 0xB1, 0xF9, + 0x00, 0x20, 0xB3, 0xF9, + 0x00, 0x10, 0xC8, 0x46, 0x4F, 0x46, 0x0A, 0xF0, 0xF1, 0xFB, 0x70, 0x4E, + 0x00, 0xB2, 0x70, 0x4A, + 0x30, 0x80, 0x21, 0x88, 0x6F, 0x4B, 0x11, 0x80, 0xDF, 0xF8, 0x7C, 0x91, + 0x39, 0x88, 0x19, 0x80, + 0xD9, 0xF8, 0x00, 0x10, 0x91, 0xF8, 0x00, 0x74, 0xFF, 0x07, 0x10, 0xD0, + 0x5F, 0x4F, 0x7F, 0x78, + 0x0F, 0xB1, 0x91, 0xF8, 0x05, 0x74, 0xB1, 0xF8, 0x02, 0xC4, 0x67, 0x44, + 0x17, 0x80, 0x91, 0xF8, + 0x04, 0x14, 0x79, 0x1A, 0x09, 0xB2, 0x19, 0x80, 0x0A, 0xF0, 0xDE, 0xFB, + 0x30, 0x80, 0x62, 0x4F, + 0x62, 0x4A, 0x20, 0x88, 0x38, 0x80, 0x10, 0x80, 0x00, 0xE0, 0x0C, 0xE0, + 0x60, 0x49, 0xB8, 0xF8, + 0x00, 0x00, 0x08, 0x80, 0xD9, 0xF8, 0x00, 0x00, 0x90, 0xF8, 0x40, 0x34, + 0xDB, 0x07, 0x26, 0xD0, + 0x2B, 0x78, 0x6B, 0xB1, 0x23, 0xE0, 0x91, 0xF8, 0x03, 0x03, 0x10, 0x44, + 0x02, 0xB2, 0x91, 0xF8, + 0x25, 0x03, 0x18, 0x44, 0x03, 0xB2, 0x91, 0xF8, 0x45, 0x03, 0x30, 0x44, + 0x06, 0xB2, 0x6E, 0xE7, + 0xB0, 0xF8, 0x42, 0x34, 0x13, 0xB1, 0xB0, 0xF8, 0x42, 0x34, 0x3B, 0x80, + 0xB0, 0xF8, 0x44, 0x34, + 0x33, 0xB1, 0xB0, 0xF8, 0x44, 0x34, 0x13, 0x80, 0x90, 0xF8, 0x46, 0x04, + 0x18, 0x1A, 0x08, 0x80, + 0xB1, 0xF9, 0x00, 0x10, 0xB6, 0xF9, 0x00, 0x00, 0x0A, 0xF0, 0xA6, 0xFB, + 0x30, 0x80, 0x49, 0x4E, + 0x20, 0x88, 0x30, 0x80, 0xD9, 0xF8, 0x00, 0x00, 0x90, 0xF8, 0x20, 0x14, + 0xC9, 0x07, 0x07, 0xD0, + 0x29, 0x78, 0x29, 0xB9, 0xB0, 0xF8, 0x21, 0x14, 0x11, 0xB1, 0xB0, 0xF8, + 0x21, 0x14, 0x31, 0x80, + 0x9A, 0xF8, 0x00, 0x10, 0xC9, 0x06, 0x07, 0xD5, 0x3F, 0x49, 0x09, 0x78, + 0x21, 0xB1, 0xB0, 0xF8, + 0x6B, 0x12, 0x21, 0x80, 0xA8, 0xF8, 0x00, 0x10, 0x3C, 0x4C, 0xB0, 0xF9, + 0xB2, 0x02, 0x20, 0x80, + 0xB7, 0xF9, 0x00, 0x10, 0x0A, 0xF0, 0x80, 0xFB, 0x00, 0xB2, 0x20, 0x80, + 0xB6, 0xF9, 0x00, 0x10, + 0x0A, 0xF0, 0x7A, 0xFB, 0x20, 0x80, 0xBD, 0xE8, 0xF0, 0x9F, 0x2D, 0xE9, + 0xFF, 0x4F, 0x8A, 0x46, + 0x81, 0x46, 0x33, 0x49, 0x0D, 0x98, 0x93, 0x46, 0x4A, 0x0C, 0x04, 0x46, + 0x08, 0xE0, 0x00, 0xBF, + 0x3A, 0xF9, 0x14, 0x30, 0x93, 0x42, 0x00, 0xDA, 0x1A, 0x46, 0x8B, 0x42, + 0x00, 0xDD, 0x19, 0x46, + 0x64, 0x1E, 0xF5, 0xD2, 0x00, 0x25, 0x00, 0x29, 0x01, 0xDA, 0x0D, 0x46, + 0x02, 0xE0, 0x00, 0x2A, + 0x00, 0xDD, 0x15, 0x46, 0x00, 0x23, 0x1C, 0x46, 0x1E, 0x46, 0x19, 0x46, + 0x1F, 0x46, 0x02, 0x46, + 0x12, 0xE0, 0x00, 0xBF, 0x3A, 0xF9, 0x12, 0xC0, 0xAC, 0xEB, 0x05, 0x0C, + 0xDC, 0x45, 0x0B, 0xDA, + 0x0C, 0xEB, 0x0B, 0x08, 0xB8, 0xF1, 0x00, 0x0F, 0x06, 0xDD, 0x02, 0xFB, + 0x02, 0x44, 0x02, 0xFB, + 0x0C, 0x11, 0x13, 0x44, 0x66, 0x44, 0x7F, 0x1C, 0x52, 0x1E, 0xEB, 0xD2, + 0x04, 0xFB, 0x07, 0xF2, + 0x03, 0xFB, 0x13, 0x2E, 0x2E, 0xE0, 0x00, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x86, 0x12, 0x01, 0x20, + 0xFE, 0x06, 0x10, 0x00, 0x04, 0x07, 0x10, 0x00, 0xF9, 0x06, 0x10, 0x00, + 0x74, 0x07, 0x10, 0x00, + 0xFD, 0x06, 0x10, 0x00, 0x8C, 0x06, 0x10, 0x00, 0x8E, 0x06, 0x10, 0x00, + 0x90, 0x06, 0x10, 0x00, + 0x92, 0x06, 0x10, 0x00, 0x94, 0x06, 0x10, 0x00, 0x96, 0x06, 0x10, 0x00, + 0x98, 0x06, 0x10, 0x00, + 0xA0, 0x06, 0x10, 0x00, 0xA2, 0x06, 0x10, 0x00, 0xA8, 0x06, 0x10, 0x00, + 0xAA, 0x06, 0x10, 0x00, + 0xAC, 0x06, 0x10, 0x00, 0xA6, 0x06, 0x10, 0x00, 0x74, 0x05, 0x10, 0x00, + 0x9A, 0x06, 0x10, 0x00, + 0x00, 0x80, 0xFF, 0xFF, 0x01, 0xFB, 0x07, 0xF2, 0x03, 0xFB, 0x16, 0x27, + 0x74, 0x43, 0x01, 0xFB, + 0x13, 0x46, 0x00, 0x24, 0x21, 0x46, 0x02, 0x46, 0x25, 0xE0, 0x00, 0x23, + 0xBE, 0xF1, 0x00, 0x0F, + 0x0F, 0xD0, 0x07, 0xFB, 0x02, 0x6C, 0x9C, 0xFB, 0xFE, 0xF3, 0xDC, 0x46, + 0x5B, 0x45, 0x01, 0xDD, + 0x63, 0x46, 0x06, 0xE0, 0xDD, 0xF8, 0x0C, 0xC0, 0x13, 0xEB, 0x0C, 0x0F, + 0x01, 0xD5, 0x03, 0x9B, + 0x5B, 0x42, 0x3A, 0xF8, 0x12, 0xC0, 0x2B, 0x44, 0xAC, 0xEB, 0x03, 0x03, + 0xDD, 0xF8, 0x0C, 0xC0, + 0x1B, 0xB2, 0x63, 0x45, 0x05, 0xDA, 0x9C, 0x44, 0xBC, 0xF1, 0x00, 0x0F, + 0x01, 0xDD, 0x1C, 0x44, + 0x49, 0x1C, 0x29, 0xF8, 0x12, 0x30, 0x52, 0x1E, 0xD7, 0xD2, 0x01, 0xB9, + 0x01, 0x21, 0x94, 0xFB, + 0xF1, 0xF1, 0x04, 0xE0, 0x39, 0xF8, 0x10, 0x20, 0x52, 0x1A, 0x29, 0xF8, + 0x10, 0x20, 0x40, 0x1E, + 0xF8, 0xD2, 0xBD, 0xE8, 0xFF, 0x8F, 0x2D, 0xE9, 0xF8, 0x43, 0x1F, 0x46, + 0xFE, 0x4B, 0x0E, 0x46, + 0x05, 0x46, 0x1B, 0x68, 0x14, 0x46, 0xB3, 0xF9, 0xCA, 0x92, 0xB3, 0xF9, + 0xCC, 0x82, 0x0A, 0xE0, + 0x43, 0x46, 0x4A, 0x46, 0x31, 0x46, 0x28, 0x46, 0x00, 0x97, 0xFF, 0xF7, + 0x3E, 0xFF, 0x06, 0xEB, + 0x47, 0x06, 0x05, 0xEB, 0x47, 0x05, 0x64, 0x1E, 0xF2, 0xD2, 0xBD, 0xE8, + 0xF8, 0x83, 0x08, 0xE0, + 0x30, 0xF9, 0x12, 0x30, 0x00, 0x2B, 0x00, 0xDA, 0x5B, 0x42, 0x8B, 0x42, + 0x01, 0xDD, 0x01, 0x20, + 0x70, 0x47, 0x52, 0x1E, 0xF4, 0xD2, 0x00, 0x20, 0x70, 0x47, 0x05, 0xE0, + 0x30, 0xF9, 0x12, 0x30, + 0x8B, 0x42, 0x01, 0xDD, 0x01, 0x20, 0x70, 0x47, 0x52, 0x1E, 0xF7, 0xD2, + 0x00, 0x20, 0x70, 0x47, + 0x2D, 0xE9, 0xFC, 0x47, 0x04, 0x46, 0xE5, 0x48, 0xE5, 0x4F, 0x13, 0x46, + 0x02, 0x68, 0x38, 0x68, + 0xFF, 0xF7, 0x0E, 0xFE, 0x03, 0x20, 0xF7, 0xF7, 0xEC, 0xFC, 0xDF, 0xF8, + 0x7C, 0x93, 0xDF, 0xF8, + 0x84, 0x83, 0xE1, 0x4D, 0xD9, 0xF8, 0x00, 0x00, 0xE0, 0x4E, 0x90, 0xF8, + 0xA0, 0x02, 0x80, 0x07, + 0x15, 0xD5, 0xDF, 0x48, 0xB0, 0xF9, 0x00, 0x20, 0xDE, 0x48, 0xB0, 0xF9, + 0x00, 0x10, 0xDE, 0x48, + 0xB0, 0xF9, 0x00, 0x00, 0x0A, 0xF0, 0x62, 0xFA, 0x03, 0xB2, 0x29, 0x78, + 0x30, 0x78, 0xCD, 0xE9, + 0x00, 0x01, 0x39, 0x68, 0xD8, 0xF8, 0x00, 0x20, 0x08, 0x46, 0x00, 0xF0, + 0x3F, 0xF9, 0xD7, 0x48, + 0xD8, 0xF8, 0x00, 0x20, 0x39, 0x68, 0xB0, 0xF9, 0x00, 0x30, 0x20, 0x46, + 0x0B, 0xF0, 0xE1, 0xF9, + 0x03, 0x20, 0xF7, 0xF7, 0xD9, 0xFC, 0xD9, 0xF8, 0x00, 0x00, 0x4F, 0x46, + 0x90, 0xF8, 0xA0, 0x02, + 0x00, 0x06, 0x05, 0xD5, 0x21, 0x46, 0x2B, 0x78, 0x32, 0x78, 0x08, 0x46, + 0xFF, 0xF7, 0x83, 0xFF, + 0x38, 0x68, 0x90, 0xF8, 0xA0, 0x02, 0x40, 0x07, 0x25, 0xD5, 0x30, 0x78, + 0x2E, 0x78, 0x32, 0x46, + 0x1F, 0xE0, 0x00, 0x21, 0x03, 0x46, 0x0C, 0xE0, 0x03, 0xFB, 0x06, 0x27, + 0x34, 0xF9, 0x17, 0x50, + 0x00, 0x2D, 0x04, 0xDD, 0x09, 0xB1, 0x8D, 0x42, 0x03, 0xDA, 0x29, 0x46, + 0x01, 0xE0, 0x00, 0x21, + 0x43, 0x1C, 0x5B, 0x1E, 0xF0, 0xD2, 0x00, 0x29, 0x0B, 0xDD, 0x03, 0x46, + 0x07, 0xE0, 0x00, 0xBF, + 0x03, 0xFB, 0x06, 0x25, 0x34, 0xF8, 0x15, 0x70, 0x7F, 0x1A, 0x24, 0xF8, + 0x15, 0x70, 0x5B, 0x1E, + 0xF6, 0xD2, 0x52, 0x1E, 0xDD, 0xD2, 0xBD, 0xE8, 0xFC, 0x87, 0x2D, 0xE9, + 0xF0, 0x5F, 0x92, 0x46, + 0xB3, 0x4A, 0x4F, 0xF0, 0x00, 0x08, 0x8B, 0x46, 0x15, 0x78, 0x06, 0x46, + 0x82, 0xF8, 0x00, 0x80, + 0xFF, 0xF7, 0x9F, 0xFD, 0x02, 0x21, 0xAF, 0x48, 0x07, 0xF0, 0x59, 0xFE, + 0xAE, 0x48, 0xAD, 0x4C, + 0x2A, 0x46, 0x00, 0x78, 0x31, 0x46, 0x01, 0x28, 0x20, 0x68, 0x5F, 0xD0, + 0xFF, 0xF7, 0x70, 0xFF, + 0x9D, 0x4D, 0xA1, 0x4F, 0xDF, 0xF8, 0x84, 0x92, 0x28, 0x68, 0x90, 0xF8, + 0xA1, 0x02, 0x80, 0x07, + 0x06, 0xD5, 0x21, 0x68, 0x3B, 0x78, 0x99, 0xF8, 0x00, 0x20, 0x08, 0x46, + 0x00, 0xF0, 0xDE, 0xF9, + 0x29, 0x68, 0x9E, 0x4E, 0x91, 0xF8, 0xA0, 0x02, 0x02, 0x07, 0x05, 0xD4, + 0xC0, 0x06, 0x03, 0xD4, + 0x91, 0xF8, 0xA1, 0x02, 0xC0, 0x07, 0x06, 0xD0, 0x21, 0x68, 0xB6, 0xF9, + 0x00, 0x30, 0x9B, 0x4A, + 0x08, 0x46, 0xFF, 0xF7, 0x54, 0xFD, 0xB6, 0xF9, 0x00, 0x00, 0x21, 0x68, + 0x42, 0x00, 0x98, 0x48, + 0x00, 0x68, 0x0E, 0xF0, 0x52, 0xF9, 0x3A, 0x78, 0x99, 0xF8, 0x00, 0x10, + 0x20, 0x68, 0xFF, 0xF7, + 0x0D, 0xFC, 0xDF, 0xF8, 0x50, 0x92, 0x99, 0xF8, 0x00, 0x00, 0x10, 0xF0, + 0x30, 0x0F, 0x04, 0xD0, + 0x51, 0x46, 0x58, 0x46, 0x22, 0x68, 0xFE, 0xF7, 0xCD, 0xFA, 0x8F, 0x48, + 0x8F, 0x4F, 0x40, 0x78, + 0x02, 0x28, 0x28, 0x68, 0x90, 0xF8, 0xA0, 0x12, 0x4F, 0xEA, 0x81, 0x61, + 0x19, 0xD3, 0x00, 0x29, + 0x07, 0xDA, 0x21, 0x68, 0xB0, 0xF9, 0xB0, 0x22, 0xB6, 0xF9, 0x00, 0x30, + 0x08, 0x46, 0x00, 0xF0, + 0x3E, 0xF9, 0x28, 0x68, 0x90, 0xF8, 0xA0, 0x12, 0x49, 0x06, 0x15, 0xD5, + 0xB0, 0xF9, 0xAE, 0x32, + 0x83, 0x4A, 0x21, 0x68, 0x38, 0x68, 0x06, 0xF0, 0xF9, 0xFA, 0x14, 0xE0, + 0xFF, 0xF7, 0x60, 0xFA, + 0x9E, 0xE7, 0x00, 0x29, 0x08, 0xDA, 0xB0, 0xF9, 0xB0, 0x22, 0xB6, 0xF9, + 0x00, 0x30, 0x21, 0x68, + 0x38, 0x68, 0x00, 0xF0, 0x24, 0xF9, 0x06, 0xE0, 0xB6, 0xF9, 0x00, 0x00, + 0x21, 0x68, 0x42, 0x00, + 0x38, 0x68, 0x0E, 0xF0, 0x0A, 0xF9, 0x02, 0x21, 0x6E, 0x48, 0x07, 0xF0, + 0x6B, 0xFE, 0x75, 0x48, + 0x00, 0x78, 0x60, 0xB1, 0xD9, 0xF8, 0x00, 0x00, 0x73, 0x49, 0xC2, 0x06, + 0x02, 0xD5, 0x0A, 0x78, + 0x01, 0x2A, 0x21, 0xD0, 0x80, 0x06, 0x02, 0xD5, 0x08, 0x78, 0x02, 0x28, + 0x1E, 0xD0, 0x6F, 0x48, + 0xB6, 0xF9, 0x00, 0x20, 0xB0, 0xF9, 0x00, 0x10, 0x38, 0x68, 0xFF, 0xF7, + 0xD6, 0xFE, 0x6C, 0x49, + 0x6C, 0x4D, 0xA8, 0xB1, 0x01, 0x20, 0x08, 0x70, 0x01, 0x46, 0x02, 0x24, + 0x6A, 0x48, 0x07, 0xF0, + 0xB6, 0xFD, 0x6B, 0x48, 0x69, 0x4B, 0xB0, 0xF9, 0x00, 0x20, 0x67, 0x48, + 0x01, 0x68, 0x38, 0x68, + 0x00, 0xF0, 0xFF, 0xF8, 0x28, 0x60, 0x0E, 0xE0, 0x03, 0x20, 0xE4, 0xE5, + 0x04, 0x20, 0xE2, 0xE5, + 0x64, 0x4A, 0xC5, 0xF8, 0x00, 0x80, 0x01, 0x24, 0xC2, 0xF8, 0x00, 0x80, + 0x5F, 0x4A, 0xC2, 0xF8, + 0x00, 0x80, 0x81, 0xF8, 0x00, 0x80, 0x20, 0x46, 0xD5, 0xE5, 0x4E, 0x49, + 0x10, 0xB5, 0x00, 0x20, + 0x08, 0x60, 0x57, 0x49, 0x08, 0x70, 0xFF, 0xF7, 0x7B, 0xFA, 0xFF, 0xF7, + 0x78, 0xFB, 0xBD, 0xE8, + 0x10, 0x40, 0x00, 0xF0, 0x01, 0xB8, 0x07, 0xE4, 0x3B, 0x48, 0x10, 0xB5, + 0x00, 0x68, 0x42, 0x49, + 0x40, 0x4B, 0xB0, 0xF8, 0x00, 0x23, 0x0A, 0x80, 0xB0, 0xF8, 0x22, 0x13, + 0x3C, 0x4C, 0x19, 0x80, + 0xB0, 0xF8, 0x42, 0x33, 0x23, 0x80, 0x50, 0x4C, 0x22, 0x80, 0x50, 0x4A, + 0x11, 0x80, 0x50, 0x49, + 0x0B, 0x80, 0x4B, 0x49, 0xB0, 0xF8, 0xB2, 0x02, 0x08, 0x80, 0x10, 0xBD, + 0x2D, 0xE9, 0xFF, 0x4F, + 0x9B, 0x46, 0xDD, 0xE9, 0x0D, 0x19, 0x86, 0x46, 0xA5, 0xE0, 0x00, 0xBF, + 0x01, 0x9A, 0x01, 0xFB, + 0x09, 0xF0, 0x02, 0xEB, 0x40, 0x04, 0x02, 0x9A, 0x46, 0x4F, 0x02, 0xEB, + 0x40, 0x06, 0x4F, 0xF0, + 0x01, 0x0C, 0x0E, 0xEB, 0x40, 0x05, 0x7B, 0x0C, 0xE2, 0x46, 0x4A, 0x46, + 0x15, 0xE0, 0x00, 0xBF, + 0x36, 0xF8, 0x12, 0x00, 0x34, 0xF8, 0x12, 0x80, 0xA0, 0xEB, 0x08, 0x00, + 0x00, 0xB2, 0x98, 0x42, + 0x00, 0xDA, 0x03, 0x46, 0xB8, 0x42, 0x00, 0xDD, 0x07, 0x46, 0x00, 0x28, + 0x02, 0xDD, 0x4F, 0xF0, + 0x00, 0x0A, 0x02, 0xE0, 0x01, 0xDA, 0x4F, 0xF0, 0x00, 0x0C, 0x52, 0x1E, + 0xE8, 0xD2, 0xBC, 0xF1, + 0x00, 0x0F, 0x04, 0xD0, 0xBA, 0xF1, 0x00, 0x0F, 0x01, 0xD0, 0x00, 0x20, + 0x0B, 0xE0, 0x5C, 0xEA, + 0x0A, 0x00, 0x08, 0xD0, 0xBC, 0xF1, 0x00, 0x0F, 0x04, 0xD1, 0xBA, 0xF1, + 0x00, 0x0F, 0x01, 0xD0, + 0x38, 0x46, 0x00, 0xE0, 0x18, 0x46, 0x00, 0x27, 0x3B, 0x46, 0x4A, 0x46, + 0xCB, 0xF1, 0x00, 0x0A, + 0x10, 0xE0, 0x00, 0xBF, 0x36, 0xF8, 0x12, 0xC0, 0x34, 0xF8, 0x12, 0x80, + 0xAC, 0xEB, 0x08, 0x0C, + 0xAC, 0xEB, 0x00, 0x0C, 0x0F, 0xFA, 0x8C, 0xFC, 0xDC, 0x45, 0x03, 0xDA, + 0xD4, 0x45, 0x01, 0xDD, + 0x67, 0x44, 0x5B, 0x1C, 0x52, 0x1E, 0x3F, 0xE0, 0x4C, 0x07, 0x10, 0x00, + 0x9C, 0x06, 0x10, 0x00, + 0x18, 0x07, 0x10, 0x00, 0x1C, 0x07, 0x10, 0x00, 0xEE, 0x06, 0x10, 0x00, + 0xED, 0x06, 0x10, 0x00, + 0x90, 0x06, 0x10, 0x00, 0x8E, 0x06, 0x10, 0x00, 0x8C, 0x06, 0x10, 0x00, + 0xFE, 0x06, 0x10, 0x00, + 0x79, 0x07, 0x10, 0x00, 0x80, 0x05, 0x10, 0x00, 0xF3, 0x06, 0x10, 0x00, + 0x86, 0x12, 0x01, 0x20, + 0x14, 0x07, 0x10, 0x00, 0x04, 0x07, 0x10, 0x00, 0x74, 0x07, 0x10, 0x00, + 0x10, 0x07, 0x10, 0x00, + 0x0E, 0x7A, 0x01, 0x00, 0x64, 0x05, 0x10, 0x00, 0x66, 0x05, 0x10, 0x00, + 0x98, 0x06, 0x10, 0x00, + 0xF5, 0x06, 0x10, 0x00, 0x7C, 0x06, 0x10, 0x00, 0x50, 0x06, 0x10, 0x00, + 0x84, 0x06, 0x10, 0x00, + 0x9A, 0x06, 0x10, 0x00, 0x80, 0x06, 0x10, 0x00, 0x92, 0x06, 0x10, 0x00, + 0x94, 0x06, 0x10, 0x00, + 0x96, 0x06, 0x10, 0x00, 0x00, 0x80, 0xFF, 0xFF, 0xAC, 0xD2, 0x03, 0xB9, + 0x01, 0x23, 0x97, 0xFB, + 0xF3, 0xF2, 0x02, 0x44, 0x48, 0x46, 0x04, 0xE0, 0x34, 0xF8, 0x10, 0x30, + 0x13, 0x44, 0x25, 0xF8, + 0x10, 0x30, 0x40, 0x1E, 0xF8, 0xD2, 0x49, 0x1E, 0xBF, 0xF4, 0x58, 0xAF, + 0xA9, 0xE5, 0x70, 0xB5, + 0x00, 0x24, 0x26, 0x46, 0x0A, 0xE0, 0x00, 0xBF, 0x31, 0xF9, 0x14, 0x50, + 0x95, 0x42, 0x02, 0xDA, + 0x20, 0xF8, 0x14, 0x60, 0x01, 0xE0, 0x20, 0xF8, 0x14, 0x50, 0x64, 0x1C, + 0x9C, 0x42, 0xF3, 0xDB, + 0x70, 0xBD, 0x2D, 0xE9, 0xFF, 0x5F, 0x45, 0x4C, 0x88, 0x46, 0x00, 0x26, + 0x07, 0x46, 0x92, 0x46, + 0x35, 0x46, 0x4F, 0xF4, 0x66, 0x71, 0x40, 0x46, 0x26, 0x60, 0x0E, 0xF0, + 0x4D, 0xF8, 0x00, 0x21, + 0x40, 0x46, 0x0E, 0xF0, 0x5B, 0xF9, 0x81, 0x46, 0x00, 0x21, 0x38, 0x46, + 0x0E, 0xF0, 0x4E, 0xF9, + 0x00, 0x22, 0x3B, 0x4B, 0x3B, 0x4F, 0x29, 0xE0, 0x00, 0x21, 0x4F, 0xF0, + 0x01, 0x0E, 0xDF, 0xF8, + 0xE8, 0xB0, 0x18, 0xE0, 0x30, 0xF9, 0x11, 0x80, 0xD0, 0x45, 0x05, 0xDD, + 0x09, 0xF8, 0x01, 0xE0, + 0x30, 0xF9, 0x11, 0xC0, 0x76, 0x1C, 0x65, 0x44, 0xDB, 0xF8, 0x00, 0xC0, + 0x30, 0xF9, 0x11, 0x80, + 0xBC, 0xF8, 0x55, 0xC3, 0xE0, 0x45, 0x05, 0xDD, 0xD4, 0xF8, 0x00, 0xC0, + 0x0C, 0xF1, 0x01, 0x0C, + 0xC4, 0xF8, 0x00, 0xC0, 0x49, 0x1C, 0x93, 0xF8, 0x00, 0xC0, 0x61, 0x45, + 0xE2, 0xDB, 0x0C, 0xF0, + 0xFF, 0x01, 0x09, 0xF1, 0x28, 0x09, 0x00, 0xEB, 0x41, 0x00, 0x52, 0x1C, + 0x39, 0x78, 0x8A, 0x42, + 0xD2, 0xDB, 0x03, 0x98, 0x05, 0x60, 0x30, 0x46, 0x04, 0xB0, 0x9C, 0xE4, + 0x2D, 0xE9, 0xF0, 0x43, + 0x91, 0x46, 0x1C, 0x46, 0x35, 0xE0, 0x21, 0x4D, 0x4F, 0xF0, 0x01, 0x0C, + 0x6A, 0x0C, 0xE0, 0x46, + 0x4F, 0x46, 0x11, 0xE0, 0x07, 0xFB, 0x03, 0x46, 0x31, 0xF9, 0x16, 0x60, + 0x96, 0x42, 0x00, 0xDA, + 0x32, 0x46, 0xAE, 0x42, 0x00, 0xDD, 0x35, 0x46, 0x00, 0x2E, 0x02, 0xDD, + 0x4F, 0xF0, 0x00, 0x08, + 0x02, 0xE0, 0x01, 0xDA, 0x4F, 0xF0, 0x00, 0x0C, 0x7F, 0x1E, 0xEB, 0xD2, + 0xBC, 0xF1, 0x00, 0x0F, + 0x02, 0xD0, 0xB8, 0xF1, 0x00, 0x0F, 0x14, 0xD1, 0x5C, 0xEA, 0x08, 0x06, + 0x11, 0xD0, 0xBC, 0xF1, + 0x00, 0x0F, 0x03, 0xD1, 0xB8, 0xF1, 0x00, 0x0F, 0x00, 0xD0, 0x2A, 0x46, + 0x4E, 0x46, 0x06, 0xE0, + 0x06, 0xFB, 0x03, 0x45, 0x31, 0xF8, 0x15, 0x70, 0xBF, 0x1A, 0x20, 0xF8, + 0x15, 0x70, 0x76, 0x1E, + 0xF6, 0xD2, 0x64, 0x1E, 0xC7, 0xD2, 0xBD, 0xE8, 0xF0, 0x83, 0x00, 0x00, + 0x80, 0x06, 0x10, 0x00, + 0xEE, 0x06, 0x10, 0x00, 0xED, 0x06, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x00, 0x80, 0xFF, 0xFF, + 0x70, 0x47, 0xA5, 0x4A, 0x30, 0xB4, 0x12, 0x68, 0x92, 0xF8, 0xA4, 0x23, + 0x12, 0x09, 0x80, 0xF8, + 0x28, 0x20, 0x0A, 0x68, 0x02, 0x62, 0x4A, 0x68, 0x42, 0x62, 0xB1, 0xF9, + 0x02, 0x20, 0xD3, 0x17, + 0x1B, 0x04, 0x43, 0xEA, 0x12, 0x43, 0x12, 0x04, 0x82, 0x60, 0xC3, 0x60, + 0xB1, 0xF9, 0x00, 0x10, + 0xCA, 0x17, 0x12, 0x04, 0x42, 0xEA, 0x11, 0x42, 0x09, 0x04, 0x01, 0x60, + 0x42, 0x60, 0x10, 0x22, + 0x01, 0x46, 0x30, 0xBC, 0x10, 0x30, 0x0D, 0xF0, 0x20, 0xBF, 0x2D, 0xE9, + 0xF0, 0x4F, 0x92, 0x4F, + 0x81, 0x46, 0x8A, 0x46, 0x38, 0x68, 0x8F, 0xB0, 0x16, 0x46, 0xB0, 0xF8, + 0xA6, 0x13, 0x90, 0xF8, + 0xAA, 0x53, 0x01, 0xFB, 0x01, 0xFB, 0xB0, 0xF8, 0xA8, 0x13, 0x01, 0xFB, + 0x01, 0xF8, 0x0E, 0xF0, + 0x5B, 0xF8, 0x00, 0x28, 0x38, 0x68, 0x02, 0xD0, 0x90, 0xF8, 0xAC, 0x43, + 0x01, 0xE0, 0x90, 0xF8, + 0xAB, 0x43, 0xAC, 0x42, 0x00, 0xD2, 0x25, 0x46, 0x00, 0x2E, 0x02, 0xDB, + 0x89, 0xF8, 0x28, 0x60, + 0x19, 0xE0, 0x09, 0xF1, 0x24, 0x01, 0x99, 0xF8, 0x28, 0x60, 0x08, 0x1F, + 0x0A, 0xF0, 0x5B, 0xF8, + 0x58, 0x45, 0x01, 0xDB, 0x26, 0x46, 0x02, 0xE0, 0x40, 0x45, 0x00, 0xDC, + 0x2E, 0x46, 0x38, 0x68, + 0x99, 0xF8, 0x28, 0x10, 0x90, 0xF8, 0xA4, 0x03, 0x00, 0xF0, 0x0F, 0x02, + 0x30, 0x46, 0x0A, 0xF0, + 0x6D, 0xF8, 0x89, 0xF8, 0x28, 0x00, 0x99, 0xF8, 0x28, 0x10, 0x74, 0x48, + 0x00, 0xEB, 0x81, 0x00, + 0x01, 0x68, 0xCA, 0x17, 0xCD, 0xE9, 0x07, 0x21, 0x41, 0x6C, 0xCA, 0x17, + 0xCD, 0xE9, 0x04, 0x12, + 0xD0, 0xF8, 0xCC, 0x20, 0xD0, 0xF8, 0x88, 0x50, 0xD4, 0x17, 0x01, 0x94, + 0x93, 0x46, 0xDD, 0xE9, + 0x07, 0x21, 0xD9, 0xF8, 0x08, 0x30, 0xD9, 0xF8, 0x0C, 0x40, 0xA3, 0xFB, + 0x01, 0x07, 0x04, 0xFB, + 0x01, 0x71, 0x03, 0xFB, 0x02, 0x11, 0xEE, 0x17, 0x4F, 0xF4, 0x80, 0x32, + 0x00, 0x23, 0x0D, 0xF0, + 0x98, 0xFE, 0xBA, 0xF9, 0x02, 0x30, 0xB9, 0xF9, 0x26, 0x20, 0x01, 0x9C, + 0x9F, 0x18, 0xA7, 0xFB, + 0x0B, 0x38, 0x4F, 0xEA, 0xE7, 0x7C, 0x0C, 0xFB, 0x0B, 0x82, 0x07, 0xFB, + 0x04, 0x24, 0xB9, 0xF9, + 0x22, 0x20, 0xA2, 0xFB, 0x05, 0xC8, 0xD7, 0x17, 0x07, 0xFB, 0x05, 0x87, + 0x13, 0xEB, 0x0C, 0x03, + 0x02, 0xFB, 0x06, 0x72, 0x54, 0x41, 0x1F, 0x1A, 0x64, 0xEB, 0x01, 0x04, + 0xDD, 0xE9, 0x04, 0x12, + 0xD9, 0xF8, 0x18, 0x30, 0xD9, 0xF8, 0x1C, 0xC0, 0xA3, 0xFB, 0x01, 0x08, + 0x0C, 0xFB, 0x01, 0x81, + 0x03, 0xFB, 0x02, 0x11, 0x4F, 0xF4, 0x80, 0x32, 0x00, 0x23, 0x0D, 0xF0, + 0x6A, 0xFE, 0x38, 0x1A, + 0x64, 0xEB, 0x01, 0x04, 0xCD, 0xE9, 0x0D, 0x04, 0x06, 0x94, 0x0A, 0x90, + 0xDD, 0xE9, 0x07, 0x21, + 0xD9, 0xF8, 0x00, 0x30, 0xD9, 0xF8, 0x04, 0x40, 0xA3, 0xFB, 0x01, 0x07, + 0x04, 0xFB, 0x01, 0x71, + 0x03, 0xFB, 0x02, 0x11, 0x4F, 0xF4, 0x80, 0x32, 0x00, 0x23, 0x0D, 0xF0, + 0x52, 0xFE, 0xBA, 0xF9, + 0x00, 0x30, 0xB9, 0xF9, 0x24, 0x20, 0x01, 0x9C, 0x13, 0x44, 0xA3, 0xFB, + 0x0B, 0xC8, 0xDF, 0x17, + 0x07, 0xFB, 0x0B, 0x82, 0x03, 0xFB, 0x04, 0x27, 0xB9, 0xF9, 0x20, 0x20, + 0xA2, 0xFB, 0x05, 0x48, + 0xD3, 0x17, 0x03, 0xFB, 0x05, 0x83, 0x02, 0xFB, 0x06, 0x32, 0x1C, 0xEB, + 0x04, 0x03, 0x57, 0x41, + 0x1C, 0x1A, 0x67, 0xEB, 0x01, 0x07, 0xDD, 0xE9, 0x04, 0x12, 0xD9, 0xF8, + 0x10, 0x30, 0xD9, 0xF8, + 0x14, 0x50, 0xA3, 0xFB, 0x01, 0x06, 0x05, 0xFB, 0x01, 0x61, 0x03, 0xFB, + 0x02, 0x11, 0x4F, 0xF4, + 0x80, 0x32, 0x00, 0x23, 0x0D, 0xF0, 0x25, 0xFE, 0x25, 0x1A, 0x67, 0xEB, + 0x01, 0x07, 0xCD, 0xE9, + 0x0B, 0x57, 0xD9, 0xF8, 0x20, 0x10, 0xC9, 0xF8, 0x24, 0x10, 0xDA, 0xF8, + 0x00, 0x10, 0xC9, 0xF8, + 0x20, 0x10, 0x10, 0x22, 0x49, 0x46, 0x09, 0xF1, 0x10, 0x00, 0x0D, 0xF0, + 0x36, 0xFE, 0x10, 0x22, + 0x0B, 0xA9, 0x48, 0x46, 0x0D, 0xF0, 0x31, 0xFE, 0x0A, 0x98, 0x4F, 0xF4, + 0x00, 0x41, 0x06, 0x9C, + 0x40, 0x18, 0x44, 0xF1, 0x00, 0x01, 0x4F, 0xF4, 0x80, 0x32, 0x00, 0x23, + 0x0D, 0xF0, 0x01, 0xFE, + 0xAA, 0xF8, 0x02, 0x00, 0x4F, 0xF4, 0x00, 0x40, 0x28, 0x18, 0x47, 0xF1, + 0x00, 0x01, 0x4F, 0xF4, + 0x80, 0x32, 0x00, 0x23, 0x0D, 0xF0, 0xF5, 0xFD, 0xAA, 0xF8, 0x00, 0x00, + 0x0F, 0xB0, 0xBD, 0xE8, + 0xF0, 0x8F, 0x70, 0xB5, 0x0D, 0x46, 0x04, 0x46, 0x01, 0x46, 0x10, 0x22, + 0x10, 0x30, 0x0D, 0xF0, + 0x0C, 0xFE, 0xB5, 0xF9, 0x02, 0x00, 0xC1, 0x17, 0x09, 0x04, 0x41, 0xEA, + 0x10, 0x41, 0x00, 0x04, + 0xA0, 0x60, 0xE1, 0x60, 0xB5, 0xF9, 0x00, 0x00, 0xC1, 0x17, 0x09, 0x04, + 0x41, 0xEA, 0x10, 0x41, + 0x00, 0x04, 0x20, 0x60, 0x61, 0x60, 0x70, 0xBD, 0x4C, 0x07, 0x10, 0x00, + 0xFE, 0x78, 0x01, 0x00, + 0x70, 0xB5, 0x04, 0x46, 0x00, 0x20, 0x84, 0xF8, 0x42, 0x01, 0x94, 0xF8, + 0x3F, 0x01, 0xF8, 0x4D, + 0x0E, 0x46, 0x01, 0x28, 0x0B, 0xD0, 0x0D, 0xF0, 0x37, 0xFF, 0x00, 0x28, + 0x94, 0xF9, 0x4C, 0x01, + 0x35, 0xD0, 0xB0, 0x42, 0x28, 0x68, 0x90, 0xF8, 0x9B, 0x03, 0x1A, 0xD1, + 0x11, 0xE0, 0xF1, 0x48, + 0x00, 0x78, 0x58, 0xB1, 0x0D, 0xF0, 0x28, 0xFF, 0x00, 0x28, 0x94, 0xF9, + 0x4C, 0x01, 0x0B, 0xD0, + 0xB0, 0x42, 0x28, 0x68, 0x90, 0xF8, 0x4D, 0x03, 0x03, 0xD0, 0x0A, 0xE0, + 0x28, 0x68, 0x90, 0xF8, + 0x4B, 0x03, 0x00, 0xF0, 0x0F, 0x00, 0x05, 0xE0, 0xB0, 0x42, 0x28, 0x68, + 0x90, 0xF8, 0x4C, 0x03, + 0xF7, 0xD0, 0x00, 0x09, 0x84, 0xF8, 0x42, 0x01, 0xE3, 0x48, 0x00, 0x78, + 0x48, 0xB1, 0x29, 0x68, + 0x94, 0xF8, 0x42, 0x01, 0x91, 0xF8, 0x63, 0x12, 0x01, 0xF0, 0x0F, 0x01, + 0x08, 0x44, 0x84, 0xF8, + 0x42, 0x01, 0x20, 0x46, 0x00, 0xF0, 0x4A, 0xF9, 0x01, 0x28, 0x06, 0xD0, + 0x0F, 0xE0, 0xB0, 0x42, + 0x28, 0x68, 0x90, 0xF8, 0x9A, 0x03, 0xE4, 0xD1, 0xDB, 0xE7, 0x29, 0x68, + 0x94, 0xF8, 0x42, 0x01, + 0x91, 0xF8, 0x9C, 0x13, 0x01, 0xF0, 0x0F, 0x01, 0x08, 0x44, 0x84, 0xF8, + 0x42, 0x01, 0xD3, 0x48, + 0xD3, 0x49, 0x00, 0x78, 0x09, 0x78, 0x08, 0x43, 0x07, 0xD0, 0x29, 0x68, + 0x94, 0xF8, 0x42, 0x01, + 0x91, 0xF8, 0x9D, 0x13, 0x08, 0x44, 0x84, 0xF8, 0x42, 0x01, 0x70, 0xBD, + 0xC9, 0x49, 0x10, 0xB5, + 0x0C, 0x78, 0xC7, 0x49, 0x90, 0xF8, 0x42, 0x21, 0x09, 0x68, 0x91, 0xF8, + 0x9A, 0x33, 0x03, 0xF0, + 0x0F, 0x03, 0x5C, 0xB1, 0x91, 0xF8, 0x4C, 0x13, 0x01, 0xF0, 0x0F, 0x01, + 0xC9, 0x1A, 0x51, 0x1A, + 0x00, 0x29, 0x00, 0xDC, 0x00, 0x21, 0x80, 0xF8, 0x42, 0x11, 0x10, 0xBD, + 0x91, 0xF8, 0x4B, 0x13, + 0xF2, 0xE7, 0xC0, 0x49, 0x00, 0x20, 0x08, 0x70, 0xBF, 0x49, 0x08, 0x70, + 0xB9, 0x49, 0x08, 0x70, + 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x4F, 0x8B, 0xB0, 0x4F, 0xF0, 0x00, 0x09, + 0x00, 0xF5, 0x80, 0x55, + 0x04, 0x46, 0x8D, 0xF8, 0x10, 0x90, 0x8D, 0xF8, 0x14, 0x90, 0xB5, 0xF8, + 0xEC, 0x03, 0xB5, 0x4A, + 0x00, 0x04, 0x00, 0x0C, 0x0A, 0xD0, 0x4F, 0xF0, 0xFF, 0x36, 0x10, 0x78, + 0x31, 0x46, 0x4F, 0xF0, + 0x00, 0x48, 0x93, 0x46, 0x4F, 0xF0, 0x01, 0x0A, 0xF8, 0xB9, 0x04, 0xE0, + 0x48, 0x46, 0x82, 0xF8, + 0x00, 0x90, 0xAD, 0x49, 0xDE, 0xE0, 0x04, 0xF5, 0x80, 0x52, 0x00, 0x20, + 0x92, 0xF9, 0xF4, 0x73, + 0x22, 0xE0, 0x00, 0xBF, 0xD2, 0xF8, 0xEC, 0xC3, 0x0A, 0xFA, 0x00, 0xF3, + 0x1C, 0xEA, 0x03, 0x0F, + 0x18, 0xD0, 0x00, 0xEB, 0x00, 0x13, 0x03, 0xEB, 0x83, 0x03, 0x04, 0xEB, + 0x83, 0x03, 0x93, 0xF8, + 0x3E, 0xC1, 0xBC, 0xF1, 0x01, 0x0F, 0x01, 0xD0, 0x31, 0x46, 0x0F, 0xE0, + 0xB3, 0xF8, 0x44, 0xC0, + 0xBC, 0xF1, 0x00, 0x0F, 0x06, 0xD0, 0x5B, 0x6B, 0x93, 0xFB, 0xFC, 0xF3, + 0x98, 0x45, 0x01, 0xDA, + 0x98, 0x46, 0x01, 0x46, 0x40, 0x1C, 0x40, 0xB2, 0x87, 0x42, 0xDB, 0xDA, + 0x00, 0x27, 0xDF, 0xF8, + 0x40, 0x82, 0x01, 0x91, 0x9D, 0xE0, 0x00, 0xBF, 0xD5, 0xF8, 0xEC, 0x13, + 0x0A, 0xFA, 0x07, 0xF0, + 0x01, 0x42, 0x60, 0xD0, 0x07, 0xEB, 0x07, 0x10, 0x00, 0xEB, 0x80, 0x00, + 0x04, 0xEB, 0x80, 0x06, + 0x96, 0xF8, 0x3E, 0x01, 0x01, 0x28, 0x02, 0xD0, 0x05, 0x28, 0x08, 0xD0, + 0x10, 0xE0, 0x30, 0x46, + 0x01, 0x99, 0xFF, 0xF7, 0x0D, 0xFF, 0x02, 0x20, 0x86, 0xF8, 0x3E, 0x01, + 0x11, 0xE0, 0x30, 0x46, + 0x00, 0xF0, 0xB0, 0xF8, 0x06, 0x20, 0x86, 0xF8, 0x3E, 0x01, 0x30, 0x46, + 0x00, 0xF0, 0x48, 0xF9, + 0x96, 0xF8, 0x3E, 0x01, 0x02, 0x28, 0x04, 0xD0, 0x06, 0x28, 0x32, 0xD0, + 0x07, 0x28, 0x3B, 0xD0, + 0x46, 0xE0, 0x96, 0xF8, 0x42, 0x00, 0xC0, 0x07, 0x13, 0xD0, 0x30, 0x46, + 0x00, 0xF0, 0x7E, 0xF8, + 0x78, 0xB9, 0xD8, 0xF8, 0x00, 0x10, 0x96, 0xF8, 0x42, 0x01, 0x91, 0xF8, + 0x9C, 0x13, 0x01, 0xF0, + 0x0F, 0x01, 0x88, 0x42, 0x03, 0xD9, 0x40, 0x1A, 0x86, 0xF8, 0x42, 0x01, + 0x01, 0xE0, 0x86, 0xF8, + 0x42, 0x91, 0x6E, 0x48, 0x00, 0x78, 0x40, 0xB1, 0x96, 0xF8, 0x42, 0x01, + 0x10, 0xBB, 0xD8, 0xF8, + 0x00, 0x00, 0x90, 0xF8, 0x9D, 0x03, 0x86, 0xF8, 0x42, 0x01, 0x96, 0xF8, + 0x42, 0x01, 0xC8, 0xB9, + 0x03, 0x20, 0x86, 0xF8, 0x3E, 0x01, 0x9B, 0xF8, 0x00, 0x00, 0x40, 0x1C, + 0x8B, 0xF8, 0x00, 0x00, + 0x2B, 0xE0, 0x96, 0xF8, 0x43, 0x01, 0x18, 0xBB, 0x96, 0xF8, 0x42, 0x00, + 0x80, 0x07, 0x03, 0xD5, + 0x07, 0x20, 0x86, 0xF8, 0x3E, 0x01, 0x32, 0xE0, 0x08, 0x20, 0x86, 0xF8, + 0x3E, 0x01, 0x30, 0x46, + 0x00, 0xF0, 0xA7, 0xF8, 0x96, 0xF8, 0x3E, 0x01, 0x02, 0x28, 0x07, 0xD0, + 0x06, 0x28, 0x0C, 0xD0, + 0xC0, 0xB2, 0x03, 0x28, 0x11, 0xD0, 0x04, 0x28, 0x0F, 0xD0, 0x20, 0xE0, + 0x96, 0xF8, 0x42, 0x01, + 0xE8, 0xB1, 0x40, 0x1E, 0x86, 0xF8, 0x42, 0x01, 0x19, 0xE0, 0x96, 0xF8, + 0x43, 0x01, 0xB0, 0xB1, + 0xC0, 0xB2, 0x40, 0x1E, 0x86, 0xF8, 0x43, 0x01, 0x11, 0xE0, 0x96, 0xF8, + 0x3F, 0x01, 0x01, 0x28, + 0x02, 0xD0, 0x03, 0x28, 0x06, 0xD0, 0x0A, 0xE0, 0x9D, 0xF8, 0x14, 0x00, + 0x40, 0x1C, 0x8D, 0xF8, + 0x14, 0x00, 0x04, 0xE0, 0x9D, 0xF8, 0x10, 0x00, 0x40, 0x1C, 0x8D, 0xF8, + 0x10, 0x00, 0x7F, 0x1C, + 0x7F, 0xB2, 0x95, 0xF9, 0xF4, 0x03, 0xB8, 0x42, 0xBF, 0xF6, 0x5E, 0xAF, + 0x05, 0xAA, 0x04, 0xA9, + 0x20, 0x46, 0x00, 0xF0, 0x87, 0xF8, 0x41, 0x49, 0x9D, 0xF8, 0x10, 0x00, + 0x08, 0x70, 0x9D, 0xF8, + 0x14, 0x00, 0x3F, 0x49, 0x08, 0x70, 0x0B, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, + 0x90, 0xF8, 0x5B, 0x10, + 0x90, 0xF8, 0x5A, 0x20, 0x8A, 0x1A, 0xB0, 0xF8, 0x42, 0x10, 0x52, 0x1C, + 0x02, 0x2A, 0x0B, 0xD9, + 0x90, 0xF8, 0x5D, 0x20, 0x90, 0xF8, 0x5C, 0x30, 0x9A, 0x42, 0x05, 0xD1, + 0x41, 0xF0, 0x01, 0x01, + 0xA0, 0xF8, 0x42, 0x10, 0x01, 0x20, 0x70, 0x47, 0x21, 0xF0, 0x01, 0x01, + 0xA0, 0xF8, 0x42, 0x10, + 0x00, 0x20, 0x70, 0x47, 0x70, 0xB5, 0x00, 0x25, 0x04, 0x46, 0x80, 0xF8, + 0x43, 0x51, 0x0D, 0xF0, + 0x93, 0xFD, 0x01, 0x00, 0x22, 0x48, 0x0C, 0xD0, 0x01, 0x68, 0x01, 0x25, + 0x91, 0xF8, 0x9E, 0x13, + 0x5F, 0xEA, 0x11, 0x11, 0x84, 0xF8, 0x43, 0x11, 0x94, 0xF8, 0x3F, 0x11, + 0x01, 0x29, 0x06, 0xD0, + 0x0E, 0xE0, 0x01, 0x68, 0x91, 0xF8, 0x9E, 0x13, 0x01, 0xF0, 0x0F, 0x01, + 0xF2, 0xE7, 0x02, 0x68, + 0x94, 0xF8, 0x43, 0x11, 0x92, 0xF8, 0x4B, 0x23, 0x01, 0xEB, 0x12, 0x11, + 0x84, 0xF8, 0x43, 0x11, + 0x1D, 0xB9, 0x94, 0xF8, 0x42, 0x10, 0x89, 0x07, 0x0D, 0xD4, 0x01, 0x68, + 0xE2, 0x6A, 0x91, 0xF8, + 0x9F, 0x13, 0x01, 0xF0, 0x0F, 0x03, 0x9A, 0x42, 0x05, 0xD9, 0x94, 0xF8, + 0x43, 0x21, 0x02, 0xEB, + 0x11, 0x11, 0x84, 0xF8, 0x43, 0x11, 0x0C, 0x49, 0x09, 0x78, 0x00, 0x29, + 0x08, 0xD0, 0x00, 0x68, + 0x94, 0xF8, 0x43, 0x11, 0x90, 0xF8, 0x63, 0x02, 0x01, 0xEB, 0x10, 0x10, + 0x84, 0xF8, 0x43, 0x01, + 0x70, 0xBD, 0x08, 0x49, 0x08, 0x78, 0x00, 0x28, 0x01, 0xD0, 0x40, 0x1E, + 0x08, 0x70, 0x70, 0x47, + 0x4C, 0x07, 0x10, 0x00, 0xF9, 0x06, 0x10, 0x00, 0x74, 0x05, 0x10, 0x00, + 0x76, 0x06, 0x10, 0x00, + 0x7D, 0x07, 0x10, 0x00, 0xFA, 0x06, 0x10, 0x00, 0x20, 0x07, 0x10, 0x00, + 0xFB, 0x06, 0x10, 0x00, + 0xFC, 0x06, 0x10, 0x00, 0x2D, 0xE9, 0xF0, 0x47, 0x16, 0x46, 0x07, 0x46, + 0x0B, 0x78, 0x34, 0x4A, + 0x01, 0x20, 0x23, 0xB9, 0x33, 0x78, 0x13, 0xB1, 0x10, 0x70, 0xBD, 0xE8, + 0xF0, 0x87, 0x00, 0x23, + 0x13, 0x70, 0x00, 0x29, 0xF9, 0xD0, 0x31, 0x78, 0x00, 0x29, 0xF6, 0xD0, + 0x00, 0x24, 0x07, 0xF5, + 0x80, 0x55, 0x80, 0x46, 0x4F, 0xF0, 0x02, 0x09, 0x1D, 0xE0, 0x00, 0xBF, + 0xD5, 0xF8, 0xEC, 0x13, + 0x08, 0xFA, 0x04, 0xF0, 0x01, 0x42, 0x14, 0xD0, 0x04, 0xEB, 0x04, 0x10, + 0x00, 0xEB, 0x80, 0x00, + 0x07, 0xEB, 0x80, 0x00, 0x90, 0xF8, 0x3E, 0x11, 0x03, 0x29, 0x0A, 0xD1, + 0x90, 0xF8, 0x3F, 0x11, + 0x01, 0x29, 0x06, 0xD1, 0x80, 0xF8, 0x3E, 0x91, 0xFF, 0xF7, 0xB3, 0xFF, + 0x30, 0x78, 0x40, 0x1E, + 0x30, 0x70, 0x64, 0x1C, 0x64, 0xB2, 0x95, 0xF9, 0xF4, 0x03, 0xA0, 0x42, + 0xDE, 0xDA, 0xCC, 0xE7, + 0x2D, 0xE9, 0xF0, 0x41, 0x17, 0x4D, 0x04, 0x46, 0x28, 0x68, 0x90, 0xF8, + 0x81, 0x03, 0xC0, 0x07, + 0x24, 0xD0, 0x21, 0x46, 0x20, 0x1D, 0x09, 0xF0, 0x06, 0xFD, 0x07, 0x46, + 0x21, 0x46, 0x04, 0xF1, + 0x20, 0x00, 0x09, 0xF0, 0x00, 0xFD, 0x06, 0x46, 0x0D, 0xF0, 0xE6, 0xFC, + 0x00, 0x28, 0x28, 0x68, + 0x05, 0xD0, 0xB0, 0xF8, 0x93, 0x13, 0xB0, 0xF8, 0x8F, 0x03, 0x49, 0x43, + 0x04, 0xE0, 0xB0, 0xF8, + 0x91, 0x13, 0xB0, 0xF8, 0x8D, 0x03, 0x49, 0x43, 0x40, 0x43, 0x86, 0x42, + 0x06, 0xDD, 0x8F, 0x42, + 0x04, 0xDD, 0x34, 0xF8, 0x42, 0x0F, 0x40, 0xF0, 0x02, 0x00, 0x20, 0x80, + 0xBD, 0xE8, 0xF0, 0x81, + 0xF9, 0x06, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, 0xF8, 0xB5, 0x15, 0x46, + 0x0E, 0x46, 0x04, 0x46, + 0x0D, 0xF0, 0xC2, 0xFC, 0x01, 0x00, 0xF9, 0x48, 0x0B, 0xD0, 0x00, 0x68, + 0x90, 0xF8, 0xB4, 0x13, + 0xB0, 0xF8, 0xB7, 0x23, 0x01, 0xF0, 0x0F, 0x03, 0x0C, 0x09, 0xB0, 0xF8, + 0xB5, 0x13, 0x49, 0x43, + 0x10, 0xE0, 0x94, 0xF8, 0x3F, 0x11, 0x02, 0x29, 0x13, 0xD0, 0x00, 0x68, + 0x01, 0x29, 0x14, 0xD0, + 0x90, 0xF8, 0xB9, 0x13, 0xB0, 0xF8, 0xC0, 0x23, 0x01, 0xF0, 0x0F, 0x03, + 0x0C, 0x09, 0xB0, 0xF8, + 0xBE, 0x13, 0x49, 0x43, 0x52, 0x43, 0x30, 0x46, 0x00, 0x94, 0x09, 0xF0, + 0xC2, 0xFC, 0x2C, 0x60, + 0xF8, 0xBD, 0x00, 0x68, 0x90, 0xF8, 0xBA, 0x13, 0xEC, 0xE7, 0x90, 0xF8, + 0xBB, 0x13, 0xB0, 0xF8, + 0xC4, 0x23, 0x01, 0xF0, 0x0F, 0x03, 0x0C, 0x09, 0xB0, 0xF8, 0xC2, 0x13, + 0x49, 0x43, 0xE9, 0xE7, + 0x2D, 0xE9, 0xF8, 0x43, 0x04, 0x46, 0x90, 0xF8, 0x3E, 0x01, 0x88, 0x46, + 0x04, 0xF1, 0x78, 0x07, + 0x03, 0x28, 0x0B, 0xD0, 0xD9, 0x4E, 0x04, 0x28, 0x0D, 0xD0, 0x07, 0x28, + 0x3B, 0xD0, 0x08, 0x28, + 0x37, 0xD1, 0x38, 0x46, 0xBD, 0xE8, 0xF8, 0x43, 0x00, 0xF0, 0xC5, 0xBB, + 0x38, 0x46, 0xBD, 0xE8, + 0xF8, 0x43, 0x00, 0xF0, 0x8E, 0xBB, 0x20, 0x46, 0x0D, 0xF0, 0x83, 0xFC, + 0x18, 0xB1, 0x20, 0x46, + 0x0D, 0xF0, 0x7A, 0xFC, 0x1D, 0xE0, 0x04, 0xF1, 0x0C, 0x01, 0x08, 0x1D, + 0x09, 0xF0, 0x7B, 0xFC, + 0x01, 0x46, 0x6A, 0x46, 0x20, 0x46, 0xFF, 0xF7, 0x97, 0xFF, 0x05, 0x46, + 0x94, 0xF8, 0x41, 0x01, + 0x80, 0xB9, 0x20, 0x46, 0x0D, 0xF0, 0x6A, 0xFC, 0x10, 0xB1, 0x00, 0x98, + 0x85, 0x42, 0x09, 0xDB, + 0x30, 0x68, 0xD4, 0xF8, 0x80, 0x10, 0x90, 0xF8, 0xBC, 0x03, 0x02, 0x09, + 0x28, 0x46, 0x09, 0xF0, + 0x85, 0xFC, 0x05, 0x46, 0x2A, 0x46, 0x41, 0x46, 0x38, 0x46, 0x00, 0xF0, + 0x64, 0xFB, 0xC4, 0xF8, + 0x80, 0x50, 0xBD, 0xE8, 0xF8, 0x83, 0x30, 0x68, 0x00, 0x24, 0x90, 0xF8, + 0x81, 0x13, 0x89, 0x07, + 0x0A, 0xD5, 0x90, 0xF8, 0xBD, 0x03, 0x00, 0xF0, 0x0F, 0x04, 0x0D, 0xF0, + 0x35, 0xFC, 0x18, 0xB1, + 0x30, 0x68, 0x90, 0xF8, 0xBD, 0x03, 0x04, 0x09, 0x22, 0x46, 0x41, 0x46, + 0x38, 0x46, 0xBD, 0xE8, + 0xF8, 0x43, 0x00, 0xF0, 0x60, 0xBB, 0x2D, 0xE9, 0xF0, 0x43, 0xDF, 0xF8, + 0xB0, 0x92, 0x4F, 0xF0, + 0x00, 0x0C, 0xB0, 0xF9, 0x02, 0x30, 0xD9, 0xF8, 0x00, 0x40, 0x67, 0x46, + 0x66, 0x46, 0x65, 0x46, + 0x00, 0x2B, 0x02, 0xDA, 0x4F, 0xF0, 0x01, 0x0C, 0x04, 0xE0, 0xB4, 0xF8, + 0x32, 0x80, 0x98, 0x45, + 0x00, 0xDA, 0x01, 0x27, 0xB0, 0xF9, 0x00, 0x30, 0x00, 0x2B, 0x01, 0xDA, + 0x01, 0x26, 0x04, 0xE0, + 0xB4, 0xF8, 0x34, 0x80, 0x98, 0x45, 0x00, 0xDA, 0x01, 0x25, 0x00, 0x23, + 0xBC, 0xF1, 0x00, 0x0F, + 0x02, 0xD0, 0x0E, 0xB1, 0x43, 0x80, 0x03, 0xE0, 0x4F, 0xB1, 0x26, 0xB1, + 0x61, 0x8E, 0x38, 0xE0, + 0x03, 0x80, 0xBD, 0xE8, 0xF0, 0x83, 0x15, 0xB1, 0x62, 0x8E, 0x42, 0x80, + 0x04, 0xE0, 0xBC, 0xF1, + 0x00, 0x0F, 0x30, 0xD0, 0x2D, 0xB1, 0x43, 0x80, 0xD9, 0xF8, 0x00, 0x10, + 0x89, 0x8E, 0x01, 0x80, + 0xEF, 0xE7, 0xB2, 0xF8, 0x02, 0xC0, 0xBC, 0xF1, 0x00, 0x0F, 0x24, 0xD0, + 0x43, 0x80, 0xB1, 0xF9, + 0x02, 0x40, 0x67, 0x42, 0x2C, 0xE0, 0x00, 0xBF, 0x0C, 0xFB, 0x07, 0xFC, + 0x00, 0x2C, 0x00, 0xDA, + 0x64, 0x42, 0x9C, 0xFB, 0xF4, 0xF4, 0x0F, 0x88, 0x3C, 0x44, 0x04, 0x80, + 0x46, 0xB3, 0xB2, 0xF9, + 0x00, 0x40, 0x2C, 0xB3, 0xB1, 0xF9, 0x00, 0x50, 0xB2, 0xF9, 0x02, 0x20, + 0x6D, 0x42, 0x00, 0xD5, + 0x6D, 0x42, 0x6A, 0x43, 0x00, 0x2C, 0x00, 0xDA, 0x64, 0x42, 0x92, 0xFB, + 0xF4, 0xF2, 0x49, 0x88, + 0x11, 0x44, 0x41, 0x80, 0xC4, 0xE7, 0x00, 0x2F, 0xE8, 0xD0, 0x57, 0x88, + 0x00, 0x2F, 0xE5, 0xD0, + 0x64, 0x8E, 0x44, 0x80, 0xD9, 0xF8, 0x00, 0x40, 0xB1, 0xF9, 0x02, 0x70, + 0x64, 0x8E, 0xE7, 0x1B, + 0xB2, 0xF9, 0x02, 0x40, 0xB2, 0xF9, 0x00, 0xC0, 0x00, 0x2F, 0xCD, 0xDA, + 0x7F, 0x42, 0xCB, 0xE7, + 0x00, 0x2D, 0xAE, 0xD0, 0xB2, 0xF9, 0x00, 0x30, 0x00, 0x2B, 0xAA, 0xD0, + 0xD9, 0xF8, 0x00, 0x40, + 0xB1, 0xF9, 0x00, 0x60, 0xB2, 0xF9, 0x02, 0x20, 0xA4, 0x8E, 0xA4, 0x1B, + 0x00, 0xD5, 0x64, 0x42, + 0x62, 0x43, 0x00, 0x2B, 0x00, 0xDA, 0x5B, 0x42, 0x92, 0xFB, 0xF3, 0xF2, + 0x49, 0x88, 0x11, 0x44, + 0x41, 0x80, 0xA1, 0xE7, 0x2D, 0xE9, 0xF0, 0x5F, 0x04, 0x46, 0x61, 0x48, + 0x0D, 0x46, 0x00, 0x21, + 0x41, 0x80, 0x01, 0x80, 0xA0, 0xF1, 0x10, 0x08, 0x28, 0x68, 0xC8, 0xF8, + 0x00, 0x00, 0x17, 0x46, + 0x4F, 0xF0, 0x01, 0x0B, 0x46, 0x46, 0x29, 0x46, 0x20, 0x46, 0x00, 0xF0, + 0x67, 0xFF, 0xDF, 0xF8, + 0x5C, 0x91, 0x28, 0x68, 0x70, 0x60, 0xD9, 0xF8, 0x00, 0x00, 0x04, 0xF1, + 0x0C, 0x0A, 0x90, 0xF8, + 0x80, 0x03, 0x41, 0x07, 0x22, 0xD5, 0x94, 0xF8, 0x3E, 0x01, 0x04, 0xF1, + 0x84, 0x06, 0x02, 0x28, + 0x0A, 0xD0, 0x03, 0x28, 0x08, 0xD0, 0x04, 0x28, 0x0B, 0xD0, 0x06, 0x28, + 0x12, 0xD0, 0x07, 0x28, + 0x10, 0xD0, 0x08, 0x28, 0x0E, 0xD0, 0x11, 0xE0, 0x51, 0x46, 0x30, 0x46, + 0xFF, 0xF7, 0xC9, 0xFA, + 0x0C, 0xE0, 0x20, 0x46, 0x00, 0xF0, 0xDD, 0xF9, 0x02, 0x46, 0x29, 0x46, + 0x30, 0x46, 0xFF, 0xF7, + 0xE4, 0xFA, 0x03, 0xE0, 0x29, 0x46, 0x30, 0x46, 0xFF, 0xF7, 0xEB, 0xFB, + 0x29, 0x68, 0xC8, 0xF8, + 0x08, 0x10, 0xD9, 0xF8, 0x00, 0x00, 0x4E, 0x46, 0x90, 0xF8, 0x80, 0x03, + 0x00, 0x07, 0x09, 0xD5, + 0x94, 0xF8, 0x42, 0x00, 0x10, 0xF0, 0xC8, 0x0F, 0x35, 0xD0, 0x51, 0x46, + 0x04, 0xF1, 0xB0, 0x00, + 0x00, 0xF0, 0xF3, 0xFD, 0x29, 0x68, 0xC8, 0xF8, 0x0C, 0x10, 0x30, 0x68, + 0x90, 0xF8, 0x80, 0x13, + 0x49, 0x06, 0x0F, 0xD4, 0x90, 0xF8, 0x40, 0x14, 0x49, 0x06, 0x03, 0xD5, + 0x94, 0xF8, 0x42, 0x10, + 0x09, 0x07, 0x07, 0xD4, 0x90, 0xF8, 0x20, 0x04, 0x80, 0x07, 0x07, 0xD5, + 0x94, 0xF8, 0x42, 0x00, + 0x00, 0x06, 0x03, 0xD5, 0x29, 0x46, 0x20, 0x46, 0x01, 0xF0, 0xBC, 0xFC, + 0x30, 0x68, 0x90, 0xF8, + 0x80, 0x03, 0x80, 0x06, 0x03, 0xD5, 0x29, 0x46, 0x20, 0x46, 0xFF, 0xF7, + 0x89, 0xFE, 0x30, 0x68, + 0x90, 0xF8, 0x80, 0x03, 0x01, 0x07, 0x11, 0xD5, 0xC0, 0x06, 0x0F, 0xD5, + 0x94, 0xF8, 0x3E, 0x01, + 0x07, 0x28, 0x05, 0xD0, 0x0A, 0xE0, 0x29, 0x46, 0x20, 0x46, 0x00, 0xF0, + 0xB2, 0xF9, 0xC9, 0xE7, + 0x1B, 0x4A, 0x04, 0xF1, 0xF4, 0x01, 0x28, 0x46, 0xFF, 0xF7, 0xD5, 0xFE, + 0x30, 0x68, 0xB0, 0xF9, + 0x32, 0x10, 0xB5, 0xF9, 0x02, 0x00, 0x09, 0xF0, 0xD7, 0xFA, 0x68, 0x80, + 0x30, 0x68, 0xB0, 0xF9, + 0x34, 0x10, 0xB5, 0xF9, 0x00, 0x00, 0x09, 0xF0, 0xCF, 0xFA, 0x28, 0x80, + 0x94, 0xF8, 0x3E, 0x01, + 0x03, 0x28, 0x06, 0xD0, 0x04, 0x28, 0x08, 0xD0, 0x07, 0x28, 0x1B, 0xD0, + 0x08, 0x28, 0x19, 0xD0, + 0x1B, 0xE0, 0x38, 0x88, 0xA4, 0xF8, 0x50, 0x00, 0x17, 0xE0, 0x30, 0x68, + 0x34, 0xF8, 0x50, 0x2F, + 0x90, 0xF8, 0xBC, 0x03, 0x00, 0xF0, 0x0F, 0x01, 0xC1, 0xF1, 0x10, 0x00, + 0x4A, 0x43, 0x39, 0x88, + 0x01, 0xFB, 0x00, 0x20, 0x00, 0x09, 0x20, 0x80, 0x06, 0xE0, 0x00, 0x00, + 0x4C, 0x07, 0x10, 0x00, + 0x94, 0x05, 0x10, 0x00, 0xB4, 0xF8, 0x50, 0x00, 0x38, 0x80, 0x58, 0x46, + 0xBD, 0xE8, 0xF0, 0x9F, + 0x2D, 0xE9, 0xF3, 0x4F, 0xE4, 0x49, 0x83, 0xB0, 0x4F, 0xF0, 0x00, 0x08, + 0xC1, 0xF8, 0x00, 0x80, + 0xE2, 0x49, 0xE3, 0x4D, 0x83, 0x46, 0xC1, 0xF8, 0x00, 0x80, 0x85, 0xF8, + 0x00, 0x80, 0x46, 0x46, + 0xDF, 0xF8, 0x80, 0xA3, 0x4F, 0xF0, 0x01, 0x09, 0x00, 0xF5, 0x80, 0x57, + 0x77, 0xE0, 0x00, 0xBF, + 0xD7, 0xF8, 0xEC, 0x13, 0x09, 0xFA, 0x06, 0xF0, 0x01, 0x42, 0x6F, 0xD0, + 0x06, 0xEB, 0x06, 0x10, + 0x00, 0xEB, 0x80, 0x01, 0x0B, 0xEB, 0x81, 0x04, 0x94, 0xF8, 0x3E, 0x01, + 0x03, 0x28, 0x05, 0xD0, + 0x04, 0x28, 0x03, 0xD0, 0x07, 0x28, 0x01, 0xD0, 0x08, 0x28, 0x5F, 0xD1, + 0xE0, 0x68, 0x00, 0x90, + 0xB4, 0xF8, 0x4E, 0x00, 0xAD, 0xF8, 0x04, 0x00, 0x04, 0xF1, 0x0C, 0x01, + 0xCA, 0x4A, 0x08, 0x1D, + 0x00, 0xF0, 0x06, 0xF9, 0x01, 0xAA, 0x69, 0x46, 0x20, 0x46, 0xFF, 0xF7, + 0xF3, 0xFE, 0x02, 0x90, + 0xC6, 0x4A, 0x69, 0x46, 0x04, 0xF1, 0x24, 0x00, 0x00, 0xF0, 0xFA, 0xF8, + 0x94, 0xF8, 0x3F, 0x01, + 0x01, 0x28, 0x08, 0xD1, 0xC4, 0x48, 0x00, 0x78, 0x28, 0xB9, 0x94, 0xF8, + 0x3E, 0x01, 0x08, 0x28, + 0x01, 0xD0, 0x00, 0x20, 0x02, 0x90, 0xDA, 0xF8, 0x00, 0x00, 0x90, 0xF8, + 0x00, 0x04, 0xC0, 0x07, + 0x0E, 0xD0, 0x94, 0xF8, 0x42, 0x00, 0x40, 0x07, 0x02, 0xD5, 0x00, 0x20, + 0x02, 0x90, 0x07, 0xE0, + 0x94, 0xF8, 0x34, 0x01, 0x01, 0x28, 0x03, 0xD1, 0x20, 0x6A, 0x00, 0x90, + 0x84, 0xF8, 0x34, 0x81, + 0x94, 0xF8, 0x3F, 0x01, 0x03, 0x28, 0x09, 0xD1, 0x28, 0x78, 0x40, 0x1C, + 0xC0, 0xB2, 0x28, 0x70, + 0x94, 0xF8, 0x42, 0x10, 0x49, 0x07, 0x01, 0xD5, 0x40, 0x1E, 0x28, 0x70, + 0x00, 0x98, 0x60, 0x62, + 0xBD, 0xF8, 0x04, 0x00, 0xA4, 0xF8, 0x50, 0x00, 0x02, 0x98, 0x01, 0x28, + 0x0E, 0xD1, 0x00, 0x98, + 0xA0, 0x61, 0xBD, 0xF8, 0x04, 0x00, 0xA4, 0xF8, 0x52, 0x00, 0x04, 0x98, + 0x94, 0xF8, 0x4C, 0x21, + 0x00, 0x68, 0x09, 0xFA, 0x02, 0xF1, 0x08, 0x43, 0x04, 0x99, 0x08, 0x60, + 0x76, 0x1C, 0x97, 0xF9, + 0xF4, 0x03, 0xB0, 0x42, 0x84, 0xDA, 0xDA, 0xF8, 0x00, 0x00, 0x90, 0xF8, + 0x00, 0x04, 0x00, 0x07, + 0x0E, 0xD5, 0x29, 0x78, 0x9D, 0x48, 0x29, 0xB1, 0x9D, 0x49, 0x09, 0x78, + 0x01, 0x29, 0x01, 0xD1, + 0x80, 0xF8, 0x00, 0x90, 0xD7, 0xF8, 0xF0, 0x13, 0x00, 0x29, 0x01, 0xD1, + 0x80, 0xF8, 0x00, 0x80, + 0x05, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, 0x2D, 0xE9, 0xF0, 0x47, 0x89, 0x46, + 0x06, 0x46, 0x00, 0x27, + 0x4F, 0xF0, 0x01, 0x08, 0x00, 0xF5, 0x80, 0x55, 0x3D, 0xE0, 0x00, 0xBF, + 0xD5, 0xF8, 0xEC, 0x13, + 0x08, 0xFA, 0x07, 0xF2, 0x11, 0x42, 0x35, 0xD0, 0x07, 0xEB, 0x07, 0x11, + 0x01, 0xEB, 0x81, 0x01, + 0x06, 0xEB, 0x81, 0x04, 0x94, 0xF8, 0x3E, 0x11, 0x03, 0x29, 0x05, 0xD0, + 0x04, 0x29, 0x03, 0xD0, + 0x07, 0x29, 0x01, 0xD0, 0x08, 0x29, 0x25, 0xD1, 0x94, 0xF8, 0x4C, 0x31, + 0xD9, 0xF8, 0x00, 0x20, + 0x08, 0xFA, 0x03, 0xF0, 0x02, 0x42, 0x1D, 0xD0, 0x03, 0x29, 0x03, 0xD1, + 0x21, 0x46, 0x30, 0x46, + 0x02, 0xF0, 0x7A, 0xF8, 0x94, 0xF9, 0x4D, 0x01, 0x00, 0x28, 0x13, 0xDB, + 0x94, 0xF8, 0x3F, 0x01, + 0x00, 0x21, 0x01, 0x28, 0x04, 0xD0, 0x02, 0x28, 0x04, 0xD0, 0x04, 0x28, + 0x04, 0xD0, 0x04, 0xE0, + 0x03, 0x21, 0x02, 0xE0, 0x04, 0x21, 0x00, 0xE0, 0x05, 0x21, 0x84, 0xF8, + 0x40, 0x11, 0x20, 0x46, + 0x08, 0xF0, 0x7D, 0xFC, 0x7F, 0x1C, 0x95, 0xF9, 0xF4, 0x03, 0xB8, 0x42, + 0xBE, 0xDA, 0x00, 0x24, + 0x47, 0x46, 0x24, 0xE0, 0xD5, 0xF8, 0xEC, 0x03, 0x07, 0xFA, 0x04, 0xF1, + 0x08, 0x42, 0x1D, 0xD0, + 0x04, 0xEB, 0x04, 0x10, 0x00, 0xEB, 0x80, 0x00, 0x06, 0xEB, 0x80, 0x08, + 0x98, 0xF8, 0x3E, 0x01, + 0x08, 0x28, 0x13, 0xD1, 0x98, 0xF8, 0x4C, 0x21, 0xD9, 0xF8, 0x00, 0x10, + 0x07, 0xFA, 0x02, 0xF0, + 0x01, 0x42, 0x07, 0xD0, 0x98, 0xF9, 0x4D, 0x01, 0x00, 0x28, 0x03, 0xDB, + 0x41, 0x46, 0x30, 0x46, + 0x02, 0xF0, 0x55, 0xF8, 0x41, 0x46, 0x30, 0x46, 0x02, 0xF0, 0x18, 0xF8, + 0x64, 0x1C, 0x95, 0xF9, + 0xF4, 0x03, 0xA0, 0x42, 0xD6, 0xDA, 0xBD, 0xE8, 0xF0, 0x87, 0x53, 0x49, + 0x10, 0xB5, 0x00, 0x20, + 0x08, 0x60, 0x52, 0x49, 0x08, 0x60, 0xFF, 0xF7, 0x03, 0xF9, 0x00, 0xF0, + 0x2F, 0xFC, 0xBD, 0xE8, + 0x10, 0x40, 0x00, 0xF0, 0xAD, 0xB8, 0x38, 0xB5, 0x04, 0x46, 0x00, 0x20, + 0x00, 0x90, 0x69, 0x46, + 0x20, 0x46, 0xFF, 0xF7, 0xC5, 0xFE, 0x69, 0x46, 0x20, 0x46, 0xFF, 0xF7, + 0x6C, 0xFF, 0x38, 0xBD, + 0x10, 0xB5, 0x14, 0x46, 0x09, 0xF0, 0x97, 0xF9, 0x21, 0x68, 0x81, 0x42, + 0x00, 0xDA, 0x20, 0x60, + 0x10, 0xBD, 0x10, 0xB5, 0x02, 0x46, 0x43, 0x49, 0xB2, 0xF8, 0x42, 0x30, + 0x4F, 0xF0, 0xFF, 0x30, + 0x09, 0x68, 0x5C, 0x06, 0x03, 0xD5, 0x91, 0xF8, 0xA5, 0x03, 0x00, 0x09, + 0x10, 0xBD, 0x1C, 0x07, + 0x02, 0xD5, 0x91, 0xF8, 0x4F, 0x04, 0x03, 0xE0, 0x1B, 0x06, 0x04, 0xD5, + 0x91, 0xF8, 0x2A, 0x04, + 0x00, 0xF0, 0x0F, 0x00, 0x10, 0xBD, 0x92, 0xF8, 0x3F, 0x21, 0x01, 0x2A, + 0x06, 0xD0, 0x39, 0x4A, + 0x12, 0x78, 0x01, 0x2A, 0xF6, 0xD9, 0x91, 0xF9, 0xAA, 0x03, 0x10, 0xBD, + 0x91, 0xF8, 0xA5, 0x03, + 0xEE, 0xE7, 0x30, 0xB4, 0x0C, 0x46, 0x90, 0xF8, 0x3E, 0x11, 0x00, 0xF1, + 0xB0, 0x03, 0x03, 0x29, + 0x23, 0xD0, 0x04, 0x29, 0x03, 0xD0, 0x06, 0x29, 0x01, 0xD0, 0x07, 0x29, + 0x41, 0xD1, 0x29, 0x4A, + 0x12, 0x68, 0x92, 0xF8, 0x80, 0x23, 0xD2, 0x06, 0x2D, 0xD5, 0x90, 0xF8, + 0x5B, 0x20, 0x90, 0xF8, + 0x5A, 0x50, 0x52, 0x1B, 0xC0, 0xF8, 0x24, 0x21, 0x90, 0xF8, 0x5D, 0x20, + 0x90, 0xF8, 0x5C, 0x50, + 0x52, 0x1B, 0xC0, 0xF8, 0x28, 0x21, 0x82, 0x6B, 0xC0, 0xF8, 0x20, 0x21, + 0x22, 0x4A, 0x07, 0x29, + 0x12, 0x88, 0xC0, 0xF8, 0x2C, 0x21, 0x06, 0xD0, 0x0F, 0xE0, 0x00, 0xF1, + 0x0C, 0x01, 0x30, 0xBC, + 0x18, 0x46, 0x00, 0xF0, 0xDA, 0xBB, 0x90, 0xF8, 0x31, 0x11, 0x03, 0x29, + 0x05, 0xD9, 0x21, 0x46, + 0x30, 0xBC, 0x1A, 0x4A, 0x18, 0x46, 0x00, 0xF0, 0x68, 0xBC, 0x90, 0xF8, + 0x31, 0x11, 0x49, 0x1C, + 0x80, 0xF8, 0x31, 0x11, 0x03, 0xE0, 0x90, 0xF8, 0x31, 0x21, 0x03, 0x2A, + 0x04, 0xD9, 0x21, 0x46, + 0x30, 0xBC, 0x18, 0x46, 0x00, 0xF0, 0x26, 0xBC, 0x04, 0x29, 0x04, 0xD0, + 0x00, 0x21, 0x80, 0xF8, + 0x31, 0x11, 0x30, 0xBC, 0x70, 0x47, 0x52, 0x1C, 0x80, 0xF8, 0x31, 0x21, + 0x21, 0x46, 0x30, 0xBC, + 0x18, 0x46, 0x00, 0xF0, 0x00, 0xBC, 0x00, 0x00, 0x24, 0x07, 0x10, 0x00, + 0x28, 0x07, 0x10, 0x00, + 0x20, 0x07, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, 0xF9, 0x06, 0x10, 0x00, + 0xA5, 0x06, 0x10, 0x00, + 0xA4, 0x06, 0x10, 0x00, 0xFA, 0x06, 0x10, 0x00, 0x78, 0x06, 0x10, 0x00, + 0x94, 0x05, 0x10, 0x00, + 0x70, 0x47, 0x00, 0xF0, 0x35, 0xB8, 0x7C, 0xB5, 0x04, 0x46, 0x15, 0x46, + 0x0E, 0x46, 0x68, 0x46, + 0x00, 0xF0, 0x2E, 0xF8, 0x01, 0x99, 0x2A, 0x46, 0x60, 0x68, 0x00, 0xF0, + 0x3B, 0xF8, 0x60, 0x60, + 0x00, 0x99, 0x2A, 0x46, 0x20, 0x68, 0x00, 0xF0, 0x35, 0xF8, 0x20, 0x60, + 0x21, 0x46, 0x30, 0x46, + 0x00, 0xF0, 0x27, 0xF8, 0x7C, 0xBD, 0x7C, 0xB5, 0x04, 0x46, 0x15, 0x46, + 0x0E, 0x46, 0x68, 0x46, + 0x00, 0xF0, 0x16, 0xF8, 0x01, 0x99, 0x2A, 0x46, 0x60, 0x68, 0x00, 0xF0, + 0x23, 0xF8, 0x60, 0x60, + 0x00, 0x99, 0x2A, 0x46, 0x20, 0x68, 0x00, 0xF0, 0x1D, 0xF8, 0x20, 0x60, + 0x21, 0x46, 0x30, 0x46, + 0x00, 0xF0, 0x0F, 0xF8, 0x7C, 0xBD, 0x0A, 0x46, 0x01, 0x46, 0x10, 0x46, + 0x00, 0xF0, 0x09, 0xB8, + 0xB1, 0xF9, 0x02, 0x20, 0x12, 0x02, 0x42, 0x60, 0xB1, 0xF9, 0x00, 0x10, + 0x09, 0x02, 0x01, 0x60, + 0x70, 0x47, 0x4A, 0x68, 0x80, 0x32, 0x12, 0x12, 0x42, 0x80, 0x09, 0x68, + 0x80, 0x31, 0x09, 0x12, + 0x01, 0x80, 0x70, 0x47, 0xC2, 0xF1, 0x10, 0x03, 0x50, 0x43, 0x01, 0xFB, + 0x03, 0x00, 0x00, 0x11, + 0x70, 0x47, 0x00, 0x00, 0x2D, 0xE9, 0xF0, 0x41, 0xFE, 0x4A, 0x12, 0x68, + 0x51, 0xB3, 0x51, 0x8E, + 0x92, 0xF8, 0x30, 0x30, 0x82, 0x6F, 0x04, 0x6F, 0xC0, 0x6F, 0x64, 0x26, + 0x40, 0x00, 0x00, 0xFB, + 0x02, 0x40, 0x48, 0x43, 0xA0, 0xFB, 0x00, 0x25, 0x00, 0x21, 0x01, 0xFB, + 0x00, 0x55, 0x00, 0xFB, + 0x01, 0x51, 0xA2, 0xFB, 0x06, 0x07, 0x00, 0x25, 0x01, 0xFB, 0x06, 0x71, + 0x02, 0xFB, 0x05, 0x11, + 0x5C, 0x43, 0x22, 0x46, 0x2B, 0x46, 0x0C, 0xF0, 0x72, 0xFE, 0x22, 0x46, + 0x00, 0x23, 0x0C, 0xF0, + 0x6E, 0xFE, 0xED, 0x4B, 0x00, 0x22, 0x1B, 0x1A, 0x8A, 0x41, 0x01, 0xD2, + 0xEA, 0x48, 0x00, 0x21, + 0xBD, 0xE8, 0xF0, 0x81, 0x91, 0x8E, 0x92, 0xF8, 0x31, 0x30, 0x42, 0x6F, + 0xD3, 0xE7, 0x7C, 0xB5, + 0x04, 0x46, 0xE6, 0x48, 0xB4, 0xF9, 0x02, 0x10, 0x00, 0x78, 0x01, 0x28, + 0x30, 0xD0, 0xB4, 0xF9, + 0x06, 0x00, 0xB4, 0xF9, 0x0A, 0x20, 0x09, 0x1A, 0x80, 0x1A, 0x08, 0x1A, + 0x01, 0x90, 0xB4, 0xF9, + 0x00, 0x10, 0xB4, 0xF9, 0x04, 0x00, 0xB4, 0xF9, 0x08, 0x20, 0x09, 0x1A, + 0x80, 0x1A, 0x08, 0x1A, + 0xD8, 0x49, 0x00, 0x90, 0x20, 0x6C, 0x09, 0x68, 0x01, 0x9B, 0x91, 0xF8, + 0xB3, 0x23, 0x50, 0x43, + 0xC2, 0xF1, 0x80, 0x02, 0x53, 0x43, 0xC2, 0x17, 0x00, 0xEB, 0x52, 0x60, + 0x03, 0xEB, 0xE0, 0x10, + 0x20, 0x64, 0x91, 0xF8, 0xB3, 0x13, 0xE0, 0x6B, 0x00, 0x9A, 0x48, 0x43, + 0xC1, 0xF1, 0x80, 0x01, + 0x4A, 0x43, 0xC1, 0x17, 0x00, 0xEB, 0x51, 0x60, 0x02, 0xEB, 0xE0, 0x10, + 0xE0, 0x63, 0x7C, 0xBD, + 0xB4, 0xF9, 0x46, 0x00, 0xB4, 0xF9, 0x4A, 0x20, 0x09, 0x1A, 0x80, 0x1A, + 0x08, 0x1A, 0x01, 0x90, + 0xB4, 0xF9, 0x00, 0x10, 0xB4, 0xF9, 0x44, 0x00, 0xB4, 0xF9, 0x48, 0x20, + 0x09, 0x1A, 0x80, 0x1A, + 0x08, 0x1A, 0x00, 0x90, 0x04, 0xF1, 0x50, 0x00, 0x03, 0x22, 0x69, 0x46, + 0x05, 0x46, 0x09, 0xF0, + 0x08, 0xF8, 0x94, 0xF8, 0x80, 0x00, 0x03, 0x28, 0x02, 0xD2, 0x40, 0x1C, + 0x84, 0xF8, 0x80, 0x00, + 0xC2, 0xB2, 0x29, 0x46, 0x04, 0xF1, 0x68, 0x00, 0x00, 0xF0, 0x98, 0xFB, + 0x7C, 0xBD, 0x2D, 0xE9, + 0xF0, 0x4F, 0x89, 0xB0, 0x00, 0x26, 0x05, 0x96, 0x06, 0x96, 0x07, 0x96, + 0x01, 0x24, 0x0D, 0xF1, + 0x14, 0x08, 0x08, 0x96, 0x02, 0x25, 0xCD, 0xE9, 0x01, 0x48, 0x07, 0x46, + 0xCD, 0xE9, 0x03, 0x54, + 0x00, 0xF1, 0x0C, 0x03, 0xAD, 0x48, 0x9B, 0x46, 0x2A, 0x46, 0x29, 0x46, + 0x20, 0x30, 0x00, 0x95, + 0x08, 0xF0, 0x70, 0xFF, 0xDF, 0xF8, 0xA4, 0x92, 0x4F, 0xF4, 0x80, 0x7A, + 0x99, 0xF8, 0x00, 0x00, + 0x01, 0x28, 0x48, 0xD0, 0x38, 0x6C, 0x00, 0x90, 0x01, 0x23, 0x02, 0x22, + 0x07, 0xA9, 0x09, 0xF1, + 0x08, 0x00, 0x08, 0xF0, 0x36, 0xFF, 0x07, 0xA9, 0x01, 0x23, 0x02, 0x22, + 0x08, 0x46, 0xCD, 0xF8, + 0x00, 0xA0, 0x08, 0xF0, 0x20, 0xFF, 0x02, 0x23, 0x5A, 0x46, 0x07, 0xA9, + 0x05, 0xA8, 0x00, 0x94, + 0x08, 0xF0, 0x34, 0xFF, 0x05, 0x96, 0x06, 0x96, 0x07, 0x96, 0x08, 0x96, + 0xCD, 0xE9, 0x01, 0x48, + 0xCD, 0xE9, 0x03, 0x54, 0x95, 0x48, 0x07, 0xF1, 0x14, 0x03, 0x00, 0x95, + 0x02, 0x22, 0x1D, 0x46, + 0x11, 0x46, 0x20, 0x30, 0x08, 0xF0, 0x3E, 0xFF, 0x99, 0xF8, 0x00, 0x00, + 0x01, 0x28, 0x1F, 0xD0, + 0xF8, 0x6B, 0x00, 0x90, 0x8D, 0x48, 0x01, 0x23, 0x02, 0x22, 0x07, 0xA9, + 0x08, 0x30, 0x08, 0xF0, + 0x08, 0xFF, 0x07, 0xA9, 0x01, 0x23, 0x02, 0x22, 0x08, 0x46, 0xCD, 0xF8, + 0x00, 0xA0, 0x08, 0xF0, + 0xF2, 0xFE, 0x02, 0x23, 0x2A, 0x46, 0x07, 0xA9, 0x05, 0xA8, 0x00, 0x94, + 0x08, 0xF0, 0x06, 0xFF, + 0x09, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, 0xDD, 0xE9, 0x05, 0x01, 0xC7, 0xE9, + 0x03, 0x01, 0xC9, 0xE7, + 0xDD, 0xE9, 0x05, 0x01, 0xC7, 0xE9, 0x05, 0x01, 0xF2, 0xE7, 0x2D, 0xE9, + 0xF0, 0x4F, 0x8D, 0xB0, + 0x00, 0x26, 0x02, 0x24, 0x05, 0x96, 0x06, 0x96, 0x07, 0x96, 0x08, 0x96, + 0x09, 0x96, 0x0A, 0x96, + 0x0B, 0x96, 0x05, 0xAF, 0x0C, 0x96, 0xCD, 0xE9, 0x01, 0x47, 0x05, 0x46, + 0x73, 0x48, 0x05, 0xF1, + 0x1C, 0x03, 0x03, 0x94, 0x04, 0x94, 0x9B, 0x46, 0x22, 0x46, 0x21, 0x46, + 0x20, 0x30, 0x00, 0x94, + 0x08, 0xF0, 0xF8, 0xFE, 0x0D, 0xF1, 0x24, 0x0A, 0xCD, 0xE9, 0x01, 0x4A, + 0x6B, 0x4B, 0x02, 0x22, + 0x03, 0x94, 0x04, 0x94, 0x30, 0x33, 0x11, 0x46, 0x05, 0xA8, 0x00, 0x94, + 0x08, 0xF0, 0xEA, 0xFE, + 0xDF, 0xF8, 0x98, 0x81, 0x4F, 0xF0, 0x04, 0x09, 0x98, 0xF8, 0x00, 0x00, + 0x01, 0x28, 0x12, 0xD1, + 0xE8, 0x6E, 0x02, 0x23, 0x08, 0xF1, 0x60, 0x01, 0x00, 0x90, 0x1A, 0x46, + 0xA1, 0xF1, 0x10, 0x00, + 0x08, 0xF0, 0xAF, 0xFE, 0x02, 0x23, 0x08, 0xF1, 0x60, 0x01, 0x1A, 0x46, + 0x08, 0x46, 0xCD, 0xF8, + 0x00, 0x90, 0x08, 0xF0, 0x98, 0xFE, 0x59, 0x49, 0x02, 0x23, 0x5A, 0x46, + 0x60, 0x31, 0x09, 0xA8, + 0x00, 0x94, 0x08, 0xF0, 0xAB, 0xFE, 0x05, 0x96, 0x06, 0x96, 0x07, 0x96, + 0x08, 0x96, 0x09, 0x96, + 0x0A, 0x96, 0x0B, 0x96, 0x0C, 0x96, 0xCD, 0xE9, 0x01, 0x47, 0x50, 0x48, + 0x05, 0xF1, 0x2C, 0x03, + 0x02, 0x22, 0x03, 0x94, 0x04, 0x94, 0x1E, 0x46, 0x11, 0x46, 0x20, 0x30, + 0x00, 0x94, 0x08, 0xF0, + 0xB1, 0xFE, 0xCD, 0xE9, 0x01, 0x4A, 0x49, 0x4B, 0x02, 0x22, 0x03, 0x94, + 0x04, 0x94, 0x30, 0x33, + 0x11, 0x46, 0x05, 0xA8, 0x00, 0x94, 0x08, 0xF0, 0xA5, 0xFE, 0x98, 0xF8, + 0x00, 0x00, 0x01, 0x28, + 0x12, 0xD1, 0x42, 0x49, 0xA8, 0x6E, 0x02, 0x23, 0x60, 0x31, 0x00, 0x90, + 0x1A, 0x46, 0xA1, 0xF1, + 0x10, 0x00, 0x08, 0xF0, 0x6E, 0xFE, 0x3D, 0x49, 0x02, 0x23, 0x60, 0x31, + 0x1A, 0x46, 0x08, 0x46, + 0xCD, 0xF8, 0x00, 0x90, 0x08, 0xF0, 0x57, 0xFE, 0x38, 0x49, 0x02, 0x23, + 0x32, 0x46, 0x60, 0x31, + 0x09, 0xA8, 0x00, 0x94, 0x08, 0xF0, 0x6A, 0xFE, 0x0D, 0xB0, 0x62, 0xE7, + 0x2D, 0xE9, 0xF0, 0x4F, + 0x89, 0xB0, 0x00, 0x26, 0x01, 0x24, 0x05, 0x96, 0x06, 0x96, 0x0D, 0xF1, + 0x14, 0x0A, 0x07, 0x96, + 0xCD, 0xE9, 0x01, 0x4A, 0x05, 0x46, 0x00, 0xF1, 0x0C, 0x03, 0x4F, 0xF0, + 0x02, 0x09, 0x2B, 0x48, + 0x93, 0x46, 0x0F, 0x46, 0x03, 0x94, 0x98, 0x46, 0x4A, 0x46, 0x21, 0x46, + 0x10, 0x30, 0xCD, 0xF8, + 0x00, 0x90, 0x04, 0x94, 0x08, 0xF0, 0x66, 0xFE, 0xB5, 0xF9, 0x02, 0x00, + 0x05, 0x99, 0x01, 0x23, + 0x40, 0x1A, 0x05, 0x90, 0x00, 0x90, 0x02, 0x22, 0x06, 0xA9, 0x38, 0x46, + 0x08, 0xF0, 0x31, 0xFE, + 0x64, 0x27, 0x41, 0x46, 0x01, 0x23, 0x02, 0x22, 0x08, 0x46, 0x00, 0x97, + 0x08, 0xF0, 0x29, 0xFE, + 0x42, 0x46, 0x02, 0x23, 0x06, 0xA9, 0x10, 0x46, 0x00, 0x94, 0x08, 0xF0, + 0x2F, 0xFE, 0x41, 0x46, + 0x01, 0x23, 0x02, 0x22, 0x08, 0x46, 0x00, 0x97, 0x08, 0xF0, 0x0D, 0xFE, + 0x05, 0x96, 0x06, 0x96, + 0x07, 0x96, 0xCD, 0xE9, 0x01, 0x4A, 0x11, 0x48, 0x05, 0xF1, 0x14, 0x03, + 0x03, 0x94, 0x1E, 0x46, + 0x02, 0x22, 0x01, 0x21, 0x10, 0x30, 0xCD, 0xF8, 0x00, 0x90, 0x04, 0x94, + 0x08, 0xF0, 0x32, 0xFE, + 0xB5, 0xF9, 0x00, 0x00, 0x05, 0x99, 0x01, 0x23, 0x40, 0x1A, 0x05, 0x90, + 0x00, 0x90, 0x02, 0x22, + 0x06, 0xA9, 0x58, 0x46, 0x08, 0xF0, 0xFD, 0xFD, 0x01, 0x23, 0x31, 0x46, + 0x02, 0x22, 0x08, 0x46, + 0x00, 0x97, 0x05, 0xE0, 0x4C, 0x07, 0x10, 0x00, 0x40, 0x42, 0x0F, 0x00, + 0x98, 0x05, 0x10, 0x00, + 0x08, 0xF0, 0xEF, 0xFD, 0x32, 0x46, 0x02, 0x23, 0x06, 0xA9, 0x10, 0x46, + 0x00, 0x94, 0x08, 0xF0, + 0xF5, 0xFD, 0x31, 0x46, 0x01, 0x23, 0x02, 0x22, 0x08, 0x46, 0x00, 0x97, + 0x08, 0xF0, 0xD3, 0xFD, + 0xE6, 0xE6, 0x2D, 0xE9, 0xF7, 0x4F, 0x8A, 0xB0, 0x00, 0x26, 0x05, 0x96, + 0x06, 0x96, 0x07, 0x96, + 0x08, 0x96, 0x01, 0x24, 0x4F, 0xF0, 0x02, 0x09, 0x0D, 0xF1, 0x14, 0x0A, + 0x09, 0x96, 0xCD, 0xE9, + 0x01, 0x4A, 0xCD, 0xE9, 0x03, 0x94, 0x07, 0x46, 0x07, 0xF1, 0x1C, 0x00, + 0x4D, 0x46, 0xFD, 0x4B, + 0x4A, 0x46, 0x49, 0x46, 0x80, 0x46, 0xCD, 0xF8, 0x00, 0x90, 0x08, 0xF0, + 0xEB, 0xFD, 0x07, 0xA8, + 0xCD, 0xE9, 0x01, 0x50, 0xF7, 0x48, 0xCD, 0xE9, 0x03, 0x45, 0x43, 0x46, + 0x02, 0x22, 0x01, 0x21, + 0x08, 0x38, 0x00, 0x95, 0x08, 0xF0, 0xDE, 0xFD, 0x0D, 0xF1, 0x24, 0x0B, + 0xCD, 0xE9, 0x01, 0x4B, + 0x03, 0x94, 0xF0, 0x4B, 0x02, 0x22, 0x01, 0x21, 0x07, 0xA8, 0x00, 0x95, + 0x04, 0x94, 0x08, 0xF0, + 0xD1, 0xFD, 0xEC, 0x4D, 0x18, 0x3D, 0x28, 0x78, 0x01, 0x28, 0x04, 0xD1, + 0x01, 0x21, 0x38, 0x46, + 0xFF, 0xF7, 0xA8, 0xFD, 0x68, 0x60, 0x09, 0x98, 0x69, 0x68, 0x4F, 0xF0, + 0x64, 0x08, 0x08, 0x44, + 0x05, 0xA9, 0x09, 0x90, 0x01, 0x23, 0x02, 0x22, 0xCD, 0xF8, 0x00, 0x80, + 0x08, 0x46, 0x08, 0xF0, + 0x90, 0xFD, 0x09, 0x98, 0x00, 0x90, 0x01, 0x23, 0x02, 0x22, 0x05, 0xA8, + 0x0B, 0x99, 0x08, 0xF0, + 0x7A, 0xFD, 0x07, 0x96, 0x08, 0x96, 0x05, 0x96, 0x06, 0x96, 0x09, 0x96, + 0xCD, 0xE9, 0x01, 0x4A, + 0xCD, 0xE9, 0x03, 0x94, 0x4E, 0x46, 0x02, 0x22, 0x07, 0xF1, 0x2C, 0x00, + 0xCD, 0xF8, 0x00, 0x90, + 0xD4, 0x4B, 0x11, 0x46, 0x81, 0x46, 0x08, 0xF0, 0x9D, 0xFD, 0x07, 0xA8, + 0xCD, 0xE9, 0x01, 0x60, + 0xCD, 0xE9, 0x03, 0x46, 0xCF, 0x48, 0x4B, 0x46, 0x02, 0x22, 0x01, 0x21, + 0x08, 0x38, 0x00, 0x96, + 0x08, 0xF0, 0x90, 0xFD, 0xCD, 0xE9, 0x01, 0x4B, 0x03, 0x94, 0xCA, 0x4B, + 0x02, 0x22, 0x01, 0x21, + 0x07, 0xA8, 0x00, 0x96, 0x04, 0x94, 0x08, 0xF0, 0x85, 0xFD, 0x28, 0x78, + 0x01, 0x28, 0x04, 0xD1, + 0x00, 0x21, 0x38, 0x46, 0xFF, 0xF7, 0x5E, 0xFD, 0x68, 0x60, 0x09, 0x98, + 0x69, 0x68, 0x01, 0x23, + 0x08, 0x44, 0x05, 0xA9, 0x09, 0x90, 0x02, 0x22, 0xCD, 0xF8, 0x00, 0x80, + 0x08, 0x46, 0x08, 0xF0, + 0x48, 0xFD, 0x09, 0x98, 0x00, 0x90, 0x01, 0x23, 0x02, 0x22, 0x05, 0xA8, + 0x0C, 0x99, 0x08, 0xF0, + 0x32, 0xFD, 0xE1, 0xE6, 0x2D, 0xE9, 0xF7, 0x4F, 0x8E, 0xB0, 0x00, 0x25, + 0x05, 0x95, 0x06, 0x95, + 0x07, 0x95, 0x02, 0x24, 0x08, 0x95, 0x09, 0x95, 0x0A, 0x95, 0x0B, 0x95, + 0x05, 0xAF, 0x0C, 0x95, + 0xCD, 0xE9, 0x01, 0x47, 0x8C, 0x46, 0x4F, 0xF0, 0x01, 0x0A, 0xAE, 0x4B, + 0x06, 0x46, 0x03, 0x94, + 0x08, 0x3B, 0x52, 0x46, 0x21, 0x46, 0x60, 0x46, 0xCD, 0xF8, 0x00, 0xA0, + 0x04, 0x94, 0x08, 0xF0, + 0x49, 0xFD, 0xA8, 0x48, 0x05, 0xAA, 0x02, 0x23, 0x11, 0x46, 0x28, 0x30, + 0x00, 0x94, 0x08, 0xF0, + 0x33, 0xFD, 0x0D, 0xF1, 0x24, 0x08, 0xCD, 0xE9, 0x01, 0x48, 0x06, 0xF1, + 0x1C, 0x03, 0x02, 0x22, + 0x03, 0x94, 0x04, 0x94, 0x9B, 0x46, 0x11, 0x46, 0x05, 0xA8, 0x00, 0x94, + 0x08, 0xF0, 0x32, 0xFD, + 0x4F, 0xF0, 0x64, 0x09, 0x02, 0x23, 0x1A, 0x46, 0x59, 0x46, 0x09, 0xA8, + 0xCD, 0xF8, 0x00, 0x90, + 0x08, 0xF0, 0xF1, 0xFC, 0x05, 0x95, 0x06, 0x95, 0x07, 0x95, 0x08, 0x95, + 0x09, 0x95, 0x0A, 0x95, + 0x0B, 0x95, 0x0C, 0x95, 0xCD, 0xE9, 0x01, 0x47, 0x92, 0x4B, 0x03, 0x94, + 0x08, 0x3B, 0x01, 0x22, + 0x02, 0x21, 0xCD, 0xF8, 0x00, 0xA0, 0x04, 0x94, 0x10, 0x98, 0x08, 0xF0, + 0x13, 0xFD, 0x8D, 0x48, + 0x05, 0xAA, 0x02, 0x23, 0x11, 0x46, 0x28, 0x30, 0x00, 0x94, 0x08, 0xF0, + 0xFD, 0xFC, 0xCD, 0xE9, + 0x01, 0x48, 0x03, 0x94, 0x04, 0x94, 0x06, 0xF1, 0x2C, 0x03, 0x00, 0x94, + 0x02, 0x22, 0x1C, 0x46, + 0x11, 0x46, 0x05, 0xA8, 0x08, 0xF0, 0xFE, 0xFC, 0x02, 0x23, 0x1A, 0x46, + 0x21, 0x46, 0x09, 0xA8, + 0xCD, 0xF8, 0x00, 0x90, 0x08, 0xF0, 0xBF, 0xFC, 0x11, 0xB0, 0xD2, 0xE5, + 0x38, 0xB5, 0x7E, 0x48, + 0x7C, 0x4C, 0x01, 0x68, 0x18, 0x3C, 0x91, 0xF8, 0x80, 0x03, 0xC0, 0xF3, + 0x00, 0x12, 0x22, 0x70, + 0xB1, 0xF8, 0xB1, 0x03, 0x1A, 0xB1, 0x64, 0x21, 0x48, 0x43, 0x0D, 0x46, + 0x01, 0xE0, 0xD1, 0xF8, + 0xAD, 0x53, 0x74, 0x49, 0x80, 0x08, 0x02, 0x23, 0x48, 0x31, 0x00, 0x90, + 0x1A, 0x46, 0xA1, 0xF1, + 0x10, 0x00, 0x08, 0xF0, 0xAE, 0xFC, 0x65, 0x60, 0x38, 0xBD, 0xF8, 0xB5, + 0x04, 0x46, 0x08, 0x68, + 0x20, 0x60, 0x48, 0x68, 0x60, 0x60, 0x88, 0x68, 0xA0, 0x60, 0x00, 0x25, + 0x25, 0x64, 0xE5, 0x63, + 0xB1, 0xF9, 0x02, 0x00, 0xC4, 0xE9, 0x03, 0x05, 0xB1, 0xF9, 0x00, 0x00, + 0xC4, 0xE9, 0x05, 0x05, + 0x64, 0x48, 0x18, 0x38, 0x01, 0x78, 0x01, 0x29, 0x09, 0xD0, 0x60, 0x30, + 0x1C, 0x34, 0x0F, 0xC8, + 0x0F, 0xC4, 0x84, 0xE8, 0x0F, 0x00, 0x2C, 0x3C, 0x84, 0xF8, 0x81, 0x50, + 0xF8, 0xBD, 0x50, 0x30, + 0x1C, 0x34, 0x0F, 0xC8, 0x0F, 0xC4, 0x5C, 0x4E, 0x0F, 0xC4, 0x30, 0x68, + 0x64, 0x21, 0x02, 0x23, + 0xB0, 0xF8, 0xB1, 0x03, 0x1A, 0x46, 0x48, 0x43, 0x80, 0x08, 0xA4, 0xF1, + 0x20, 0x01, 0x00, 0x90, + 0x08, 0x46, 0x3C, 0x3C, 0x08, 0xF0, 0x75, 0xFC, 0x30, 0x68, 0x64, 0x21, + 0x02, 0x23, 0xB0, 0xF8, + 0xB1, 0x03, 0x1A, 0x46, 0x48, 0x43, 0x80, 0x08, 0x04, 0xF1, 0x2C, 0x01, + 0x00, 0x90, 0x08, 0x46, + 0x08, 0xF0, 0x67, 0xFC, 0x94, 0xE8, 0x07, 0x00, 0x04, 0xF1, 0x44, 0x03, + 0x83, 0xE8, 0x07, 0x00, + 0x84, 0xF8, 0x80, 0x50, 0xD0, 0xE7, 0x10, 0xB5, 0x04, 0x46, 0x03, 0x22, + 0x08, 0xF0, 0xDC, 0xFC, + 0x20, 0x46, 0xFF, 0xF7, 0x94, 0xFC, 0xB4, 0xF9, 0x02, 0x00, 0xE0, 0x60, + 0xB4, 0xF9, 0x06, 0x10, + 0x40, 0x1A, 0x20, 0x61, 0xB4, 0xF9, 0x00, 0x00, 0x60, 0x61, 0xB4, 0xF9, + 0x04, 0x10, 0x40, 0x1A, + 0xA0, 0x61, 0x10, 0xBD, 0x7F, 0xB5, 0x0D, 0x46, 0x04, 0x46, 0x03, 0x22, + 0x08, 0xF0, 0xC4, 0xFC, + 0x20, 0x46, 0xFF, 0xF7, 0x7C, 0xFC, 0x20, 0x46, 0xFF, 0xF7, 0xD9, 0xFC, + 0x20, 0x46, 0xFF, 0xF7, + 0x4C, 0xFD, 0x00, 0x20, 0x00, 0x90, 0x01, 0x90, 0x02, 0x90, 0x03, 0x90, + 0x02, 0xAA, 0x69, 0x46, + 0x20, 0x46, 0xFF, 0xF7, 0x4E, 0xFE, 0x02, 0xAA, 0x69, 0x46, 0x20, 0x46, + 0xFF, 0xF7, 0xCE, 0xFD, + 0x02, 0xAA, 0x69, 0x46, 0x20, 0x46, 0xFF, 0xF7, 0xE5, 0xFE, 0xA0, 0x89, + 0x68, 0x80, 0xA0, 0x8A, + 0x28, 0x80, 0x28, 0x48, 0x18, 0x38, 0x00, 0x78, 0x01, 0x28, 0x05, 0xD1, + 0x03, 0x22, 0x29, 0x46, + 0x04, 0xF1, 0x44, 0x00, 0x08, 0xF0, 0x98, 0xFC, 0x7F, 0xBD, 0x2D, 0xE9, + 0xF0, 0x47, 0x06, 0x46, + 0x48, 0x88, 0xB6, 0xF8, 0x4A, 0x30, 0x89, 0x46, 0xC0, 0x1A, 0xB6, 0xF8, + 0x46, 0x40, 0x92, 0x46, + 0x01, 0xB2, 0xB9, 0xF8, 0x00, 0x00, 0xB6, 0xF8, 0x48, 0x20, 0xE3, 0x1A, + 0x1C, 0xB2, 0x80, 0x1A, + 0xB6, 0xF8, 0x44, 0x30, 0x00, 0xB2, 0x9A, 0x1A, 0x15, 0xB2, 0x0C, 0xB9, + 0x00, 0x2D, 0x27, 0xD0, + 0x49, 0x43, 0x00, 0xFB, 0x00, 0x10, 0x64, 0x21, 0x48, 0x43, 0x04, 0xFB, + 0x04, 0xF1, 0x05, 0xFB, + 0x05, 0x11, 0xB0, 0xFB, 0xF1, 0xF0, 0x08, 0xF0, 0xC7, 0xFB, 0x44, 0x43, + 0x0A, 0x22, 0x45, 0x43, + 0x21, 0xB2, 0x28, 0xB2, 0x91, 0xFB, 0xF2, 0xF1, 0x90, 0xFB, 0xF2, 0xF0, + 0xB6, 0xF8, 0x46, 0x20, + 0x61, 0xF3, 0x1F, 0x48, 0x11, 0x44, 0x61, 0xF3, 0x1F, 0x47, 0xB6, 0xF8, + 0x44, 0x10, 0x60, 0xF3, + 0x0F, 0x08, 0x08, 0x44, 0x60, 0xF3, 0x0F, 0x07, 0xC9, 0xF8, 0x00, 0x70, + 0xCA, 0xF8, 0x00, 0x80, + 0xBD, 0xE8, 0xF0, 0x87, 0xB0, 0x05, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0xF0, 0xB5, 0x4F, 0xF0, + 0x00, 0x0C, 0x63, 0x46, 0x65, 0x46, 0x14, 0x46, 0x07, 0xE0, 0x00, 0xBF, + 0x01, 0xEB, 0xC4, 0x06, + 0x76, 0x68, 0x35, 0x44, 0x51, 0xF8, 0x34, 0x60, 0x33, 0x44, 0x64, 0x1E, + 0xF6, 0xD2, 0x95, 0xFB, + 0xF2, 0xF6, 0x93, 0xFB, 0xF2, 0xF7, 0xC0, 0xF8, 0x04, 0xC0, 0x15, 0x46, + 0x42, 0xF2, 0x10, 0x74, + 0xC0, 0xF8, 0x00, 0xC0, 0x17, 0xE0, 0x00, 0xBF, 0x01, 0xEB, 0xC5, 0x03, + 0x5B, 0x68, 0x9B, 0x1B, + 0x5B, 0x43, 0xA3, 0x42, 0x00, 0xDD, 0x23, 0x46, 0xD0, 0xF8, 0x04, 0xC0, + 0x63, 0x44, 0x43, 0x60, + 0x51, 0xF8, 0x35, 0x30, 0xDB, 0x1B, 0x5B, 0x43, 0xA3, 0x42, 0x00, 0xDD, + 0x23, 0x46, 0xD0, 0xF8, + 0x00, 0xC0, 0x63, 0x44, 0x03, 0x60, 0x6D, 0x1E, 0xE6, 0xD2, 0x41, 0x68, + 0x64, 0x23, 0x59, 0x43, + 0x91, 0xFB, 0xF2, 0xF1, 0x41, 0x60, 0x01, 0x68, 0x59, 0x43, 0x91, 0xFB, + 0xF2, 0xF1, 0x01, 0x60, + 0xF0, 0xBD, 0x00, 0x00, 0x10, 0xB5, 0x14, 0x21, 0x4B, 0x48, 0x0C, 0xF0, + 0x6F, 0xFB, 0xF6, 0xF7, + 0xD3, 0xFE, 0x64, 0x21, 0x48, 0x43, 0x48, 0x49, 0x08, 0x60, 0x10, 0xBD, + 0x2D, 0xE9, 0xF0, 0x41, + 0xDF, 0xF8, 0x18, 0x81, 0x04, 0x46, 0x0D, 0x46, 0xD8, 0xF8, 0x00, 0x00, + 0x90, 0xF8, 0xA0, 0x13, + 0x90, 0xF8, 0x80, 0x03, 0x01, 0xF0, 0x0F, 0x06, 0xC6, 0xF1, 0x10, 0x07, + 0x80, 0x07, 0x0F, 0xD5, + 0xB4, 0xF8, 0x42, 0x00, 0x01, 0x07, 0x0B, 0xD4, 0x00, 0x06, 0x09, 0xD4, + 0x0C, 0xF0, 0xFC, 0xFB, + 0x30, 0xB9, 0x3B, 0x48, 0x00, 0x78, 0x18, 0xB9, 0x3A, 0x48, 0x00, 0x78, + 0x01, 0x28, 0x02, 0xD9, + 0x00, 0x20, 0x20, 0x63, 0x18, 0xE0, 0x20, 0x6B, 0x02, 0x28, 0x18, 0xD8, + 0x40, 0x1C, 0x20, 0x63, + 0x02, 0x28, 0x07, 0xD9, 0x94, 0xF8, 0x3E, 0x11, 0x04, 0x29, 0x10, 0xD0, + 0x06, 0x29, 0x0E, 0xD0, + 0x07, 0x29, 0x0C, 0xD0, 0x02, 0x28, 0x07, 0xD1, 0xE0, 0x89, 0x61, 0x8A, + 0x40, 0x1A, 0x60, 0x85, + 0xA0, 0x89, 0x21, 0x8A, 0x40, 0x1A, 0x20, 0x85, 0x00, 0x20, 0xBD, 0xE8, + 0xF0, 0x81, 0xE0, 0x89, + 0x61, 0x8A, 0x22, 0x8A, 0x40, 0x1A, 0x01, 0xB2, 0xA0, 0x89, 0x79, 0x43, + 0x80, 0x1A, 0xB4, 0xF9, + 0x2A, 0x20, 0x00, 0xB2, 0x02, 0xFB, 0x06, 0x11, 0x09, 0x11, 0x61, 0x85, + 0xB4, 0xF9, 0x28, 0x10, + 0x78, 0x43, 0x01, 0xFB, 0x06, 0x00, 0x00, 0x11, 0x20, 0x85, 0xF6, 0xF7, + 0x2B, 0xFE, 0x1A, 0x4A, + 0x91, 0x89, 0x08, 0x1A, 0x11, 0x7C, 0x01, 0x29, 0x01, 0xD1, 0x51, 0x68, + 0x08, 0x44, 0x00, 0x28, + 0x00, 0xDA, 0x00, 0x20, 0x00, 0xEB, 0x80, 0x01, 0x13, 0x68, 0x49, 0x00, + 0xB1, 0xFB, 0xF3, 0xF7, + 0xD8, 0xF8, 0x00, 0x60, 0xB6, 0xF8, 0xA1, 0x13, 0x8F, 0x42, 0x03, 0xD9, + 0x59, 0x43, 0x0A, 0x20, + 0xB1, 0xFB, 0xF0, 0xF0, 0xB4, 0xF9, 0x2A, 0x10, 0x96, 0xF8, 0xA3, 0x33, + 0x92, 0x68, 0x59, 0x43, + 0x64, 0x26, 0x41, 0x43, 0x72, 0x43, 0xB4, 0xF9, 0x28, 0x40, 0x91, 0xFB, + 0xF2, 0xF1, 0x5C, 0x43, + 0x44, 0x43, 0x94, 0xFB, 0xF2, 0xF0, 0x6A, 0x88, 0x11, 0x44, 0x69, 0x80, + 0x29, 0x88, 0x08, 0x44, + 0x28, 0x80, 0x01, 0x20, 0xB1, 0xE7, 0x00, 0x00, 0x6C, 0x2C, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, + 0x74, 0x05, 0x10, 0x00, 0x20, 0x07, 0x10, 0x00, 0xB0, 0xF8, 0x44, 0x20, + 0xB1, 0xF8, 0x44, 0x30, + 0x1A, 0x44, 0xA0, 0xF8, 0x44, 0x20, 0xB0, 0xF8, 0x46, 0x20, 0xB1, 0xF8, + 0x46, 0x30, 0x1A, 0x44, + 0xA0, 0xF8, 0x46, 0x20, 0x42, 0x6B, 0x4B, 0x6B, 0x1A, 0x44, 0x42, 0x63, + 0x82, 0x6B, 0x8B, 0x6B, + 0x1A, 0x44, 0x82, 0x63, 0xB1, 0xF9, 0x54, 0x20, 0xB0, 0xF9, 0x54, 0x30, + 0x9A, 0x42, 0x01, 0xDD, + 0xA0, 0xF8, 0x54, 0x20, 0xB1, 0xF9, 0x56, 0x20, 0xB0, 0xF9, 0x56, 0x30, + 0x9A, 0x42, 0x01, 0xDD, + 0xA0, 0xF8, 0x56, 0x20, 0x90, 0xF8, 0x46, 0x21, 0x91, 0xF8, 0x46, 0x31, + 0x9A, 0x42, 0x0A, 0xD0, + 0x30, 0xF8, 0x48, 0x2F, 0xB1, 0xF8, 0x48, 0x30, 0x1A, 0x44, 0x20, 0xF8, + 0x0C, 0x29, 0xC9, 0x6B, + 0x02, 0x68, 0x11, 0x44, 0x01, 0x60, 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x4F, + 0x97, 0xB0, 0x80, 0x46, + 0x00, 0xF5, 0x80, 0x50, 0x00, 0x26, 0x11, 0x90, 0xBF, 0xE0, 0x00, 0x20, + 0x08, 0x90, 0x05, 0x90, + 0x10, 0x90, 0x12, 0x90, 0x81, 0x46, 0x82, 0x46, 0x83, 0x46, 0x05, 0x46, + 0x07, 0x46, 0x0A, 0x90, + 0x0B, 0x90, 0x0C, 0x90, 0x0D, 0x90, 0x08, 0xEB, 0x86, 0x00, 0x00, 0xF5, + 0x80, 0x50, 0x14, 0x90, + 0xD0, 0xF8, 0x68, 0x04, 0x0A, 0xAA, 0x04, 0x46, 0x17, 0xE0, 0x00, 0xBF, + 0x90, 0xF8, 0x46, 0x11, + 0x08, 0xEB, 0x01, 0x0C, 0x53, 0x5C, 0x0C, 0xF5, 0x80, 0x5C, 0x5B, 0x1C, + 0xDB, 0xB2, 0x53, 0x54, + 0x9C, 0xF8, 0x56, 0xC4, 0x63, 0x45, 0x06, 0xD1, 0x01, 0x2B, 0x04, 0xD9, + 0x01, 0x23, 0x8B, 0x40, + 0x12, 0x99, 0x0B, 0x43, 0x12, 0x93, 0xD0, 0xF8, 0x50, 0x01, 0x00, 0x28, + 0xE6, 0xD1, 0x40, 0xE0, + 0x94, 0xF8, 0x46, 0x11, 0x01, 0x20, 0x00, 0xFA, 0x01, 0xFC, 0x12, 0x99, + 0x1C, 0xEA, 0x01, 0x0F, + 0x12, 0xD1, 0xA0, 0x6B, 0xB4, 0xF9, 0x0E, 0x30, 0x05, 0x9A, 0x49, 0x46, + 0xC0, 0xFB, 0x03, 0x12, + 0x05, 0x92, 0x89, 0x46, 0xB4, 0xF9, 0x0C, 0x30, 0x51, 0x46, 0x5A, 0x46, + 0xC0, 0xFB, 0x03, 0x12, + 0x8A, 0x46, 0x93, 0x46, 0x05, 0x44, 0x19, 0xE0, 0x08, 0x99, 0x1C, 0xEA, + 0x01, 0x0F, 0x15, 0xD1, + 0xE0, 0x6B, 0xB4, 0xF9, 0x1E, 0x30, 0x05, 0x9A, 0x49, 0x46, 0xC0, 0xFB, + 0x03, 0x12, 0x05, 0x92, + 0x89, 0x46, 0xB4, 0xF9, 0x1C, 0x30, 0x51, 0x46, 0x5A, 0x46, 0xC0, 0xFB, + 0x03, 0x12, 0x8A, 0x46, + 0x08, 0x99, 0x05, 0x44, 0x4C, 0xEA, 0x01, 0x00, 0x93, 0x46, 0x08, 0x90, + 0x20, 0x46, 0x00, 0xF0, + 0x98, 0xFB, 0x01, 0x28, 0x02, 0xD1, 0x10, 0x98, 0x40, 0x1C, 0x10, 0x90, + 0xD4, 0xF8, 0x50, 0x41, + 0x7F, 0x1C, 0x00, 0x2C, 0xBC, 0xD1, 0x00, 0x2F, 0x45, 0xDD, 0xE8, 0x17, + 0x04, 0x46, 0x03, 0x46, + 0x2A, 0x46, 0x48, 0x46, 0x05, 0x99, 0x0C, 0xF0, 0x4C, 0xF9, 0xAD, 0xF8, + 0x1E, 0x00, 0x2A, 0x46, + 0x23, 0x46, 0x50, 0x46, 0x59, 0x46, 0x0C, 0xF0, 0x44, 0xF9, 0xAD, 0xF8, + 0x1C, 0x00, 0x14, 0x98, + 0x4F, 0xF0, 0xFF, 0x37, 0x00, 0x25, 0xD0, 0xF8, 0x68, 0x44, 0x23, 0xE0, + 0x20, 0x46, 0x00, 0xF0, + 0x70, 0xFB, 0x01, 0x28, 0x01, 0xD0, 0x10, 0x98, 0x90, 0xB9, 0x04, 0xF1, + 0x10, 0x01, 0x07, 0xA8, + 0x08, 0xF0, 0xB9, 0xFA, 0xB8, 0x42, 0x0B, 0xD2, 0x07, 0x46, 0x3D, 0xB1, + 0x29, 0x46, 0x20, 0x46, + 0xFF, 0xF7, 0x1A, 0xFF, 0x29, 0x46, 0x40, 0x46, 0x00, 0xF0, 0x72, 0xFB, + 0x25, 0x46, 0x07, 0xE0, + 0x21, 0x46, 0x28, 0x46, 0xFF, 0xF7, 0x10, 0xFF, 0x21, 0x46, 0x40, 0x46, + 0x00, 0xF0, 0x68, 0xFB, + 0xD4, 0xF8, 0x50, 0x41, 0x00, 0x2C, 0xD9, 0xD1, 0x07, 0x98, 0x45, 0xF8, + 0x0C, 0x0F, 0xE8, 0x8E, + 0x40, 0xF0, 0x40, 0x00, 0xE8, 0x86, 0x11, 0x98, 0x76, 0x1C, 0x90, 0xF8, + 0xE0, 0x04, 0xB0, 0x42, + 0x3F, 0xF7, 0x3B, 0xAF, 0x17, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, 0x2D, 0xE9, + 0xF7, 0x4F, 0x05, 0x46, + 0x0E, 0x46, 0x01, 0xEB, 0x06, 0x10, 0x00, 0xEB, 0x80, 0x00, 0x82, 0xB0, + 0x05, 0xEB, 0x80, 0x0A, + 0x00, 0x24, 0x4F, 0xF0, 0x01, 0x08, 0xDF, 0xF8, 0x98, 0xB5, 0x05, 0xF5, + 0x80, 0x57, 0x7F, 0xE0, + 0xD7, 0xF8, 0xEC, 0x23, 0x08, 0xFA, 0x04, 0xF9, 0x12, 0xEA, 0x09, 0x0F, + 0x77, 0xD0, 0xB4, 0x42, + 0x75, 0xD0, 0xDB, 0xF8, 0x08, 0x10, 0x31, 0xF8, 0x16, 0x10, 0x11, 0xEA, + 0x09, 0x0F, 0x6E, 0xD1, + 0x04, 0xEB, 0x04, 0x10, 0x00, 0xEB, 0x80, 0x00, 0x05, 0xEB, 0x80, 0x00, + 0x00, 0x90, 0x00, 0xF0, + 0x0A, 0xFB, 0x01, 0x28, 0x04, 0xD0, 0x00, 0x98, 0x00, 0xF0, 0x0B, 0xFB, + 0x01, 0x28, 0x5E, 0xD1, + 0x00, 0x98, 0x00, 0xF0, 0xF8, 0xFA, 0xE8, 0xBB, 0x00, 0x99, 0x0A, 0xF1, + 0x0C, 0x00, 0x0C, 0x31, + 0x08, 0xF0, 0x51, 0xFA, 0x01, 0x90, 0xDB, 0xF8, 0x08, 0x00, 0x08, 0xFA, + 0x06, 0xF2, 0x30, 0xF8, + 0x16, 0x30, 0x43, 0xEA, 0x09, 0x03, 0x20, 0xF8, 0x16, 0x30, 0x30, 0xF8, + 0x14, 0x30, 0x13, 0x43, + 0x20, 0xF8, 0x14, 0x30, 0x50, 0x46, 0xDB, 0xF8, 0x00, 0x90, 0x00, 0xF0, + 0xF6, 0xFA, 0x01, 0x28, + 0x04, 0xD1, 0x00, 0x98, 0x00, 0xF0, 0xDF, 0xFA, 0x01, 0x28, 0x1D, 0xD0, + 0x50, 0x46, 0x00, 0xF0, + 0xDA, 0xFA, 0x01, 0x28, 0x04, 0xD1, 0x00, 0x98, 0x00, 0xF0, 0xE7, 0xFA, + 0x01, 0x28, 0x13, 0xD0, + 0x50, 0x46, 0x00, 0xF0, 0xD0, 0xFA, 0x01, 0x28, 0x1E, 0xD1, 0x00, 0x98, + 0x00, 0xF0, 0xCB, 0xFA, + 0x01, 0x28, 0x19, 0xD1, 0x00, 0x99, 0x9A, 0xF8, 0x42, 0x00, 0x91, 0xF8, + 0x42, 0x10, 0x08, 0x43, + 0x80, 0x06, 0x00, 0xE0, 0x1B, 0xE0, 0x0F, 0xD5, 0xBA, 0xF8, 0x42, 0x00, + 0xDB, 0xF8, 0x04, 0x90, + 0x40, 0xF0, 0x20, 0x01, 0xAA, 0xF8, 0x42, 0x10, 0x00, 0x98, 0xB0, 0xF8, + 0x42, 0x00, 0x40, 0xF0, + 0x20, 0x01, 0x00, 0x98, 0xA0, 0xF8, 0x42, 0x10, 0x01, 0x98, 0x48, 0x45, + 0x07, 0xD2, 0x41, 0xF2, + 0x68, 0x40, 0x51, 0x46, 0x28, 0x44, 0x04, 0x9B, 0x00, 0x9A, 0x00, 0xF0, + 0xD0, 0xFA, 0x64, 0x1C, + 0x97, 0xF9, 0xF4, 0x03, 0xA0, 0x42, 0xBF, 0xF6, 0x7B, 0xAF, 0x05, 0xB0, + 0x63, 0xE7, 0x2D, 0xE9, + 0xF8, 0x4F, 0x88, 0x46, 0x06, 0x46, 0x01, 0xEB, 0x08, 0x10, 0x00, 0xEB, + 0x80, 0x00, 0x06, 0xEB, + 0x80, 0x04, 0xDF, 0xF8, 0x6C, 0xA4, 0x94, 0xF8, 0x42, 0x00, 0x4F, 0xF0, + 0x04, 0x0B, 0x40, 0x06, + 0x4F, 0xF0, 0x01, 0x09, 0x06, 0xF5, 0x80, 0x55, 0x2E, 0xD5, 0x00, 0x27, + 0x04, 0xF1, 0x0C, 0x0B, + 0x24, 0xE0, 0x00, 0xBF, 0xD5, 0xF8, 0xEC, 0x13, 0x09, 0xFA, 0x07, 0xF0, + 0x01, 0x42, 0x1C, 0xD0, + 0x47, 0x45, 0x1A, 0xD0, 0x07, 0xEB, 0x07, 0x10, 0x00, 0xEB, 0x80, 0x00, + 0x06, 0xEB, 0x80, 0x00, + 0x00, 0x90, 0x00, 0xF0, 0x70, 0xFA, 0x01, 0x28, 0x0F, 0xD1, 0x00, 0x98, + 0x00, 0xF0, 0x63, 0xFA, + 0x58, 0xB9, 0x00, 0x99, 0x58, 0x46, 0x10, 0x31, 0x08, 0xF0, 0xBD, 0xF9, + 0xDA, 0xF8, 0x04, 0x10, + 0x88, 0x42, 0x02, 0xD2, 0x04, 0x20, 0x84, 0xF8, 0x3E, 0x01, 0x7F, 0x1C, + 0x95, 0xF9, 0xF4, 0x03, + 0xB8, 0x42, 0xD7, 0xDA, 0xBD, 0xE8, 0xF8, 0x8F, 0xFE, 0x48, 0xE1, 0x6A, + 0x00, 0x68, 0x90, 0xF8, + 0x95, 0x03, 0x81, 0x42, 0xF6, 0xD9, 0x00, 0x27, 0x31, 0xE0, 0x00, 0xBF, + 0xD5, 0xF8, 0xEC, 0x13, + 0x09, 0xFA, 0x07, 0xF0, 0x01, 0x42, 0x29, 0xD0, 0x47, 0x45, 0x27, 0xD0, + 0x07, 0xEB, 0x07, 0x10, + 0x00, 0xEB, 0x80, 0x00, 0x06, 0xEB, 0x80, 0x00, 0x00, 0x90, 0x00, 0xF0, + 0x3C, 0xFA, 0x01, 0x28, + 0x1C, 0xD1, 0x00, 0x98, 0x94, 0xF8, 0x46, 0x11, 0x90, 0xF8, 0x47, 0x01, + 0x81, 0x42, 0x15, 0xD1, + 0x00, 0x98, 0x00, 0xF0, 0x28, 0xFA, 0x88, 0xB9, 0x00, 0x99, 0x04, 0xF1, + 0x0C, 0x00, 0x10, 0x31, + 0x08, 0xF0, 0x81, 0xF9, 0xDA, 0xF8, 0x04, 0x10, 0x88, 0x42, 0x07, 0xD2, + 0x84, 0xF8, 0x3E, 0xB1, + 0xB4, 0xF8, 0x42, 0x00, 0x40, 0xF0, 0x40, 0x00, 0xA4, 0xF8, 0x42, 0x00, + 0x7F, 0x1C, 0x95, 0xF9, + 0xF4, 0x03, 0xB8, 0x42, 0xCA, 0xDA, 0x00, 0x27, 0x0C, 0x34, 0x26, 0xE0, + 0xD5, 0xF8, 0xEC, 0x13, + 0x09, 0xFA, 0x07, 0xF0, 0x01, 0x42, 0x1F, 0xD0, 0x47, 0x45, 0x1D, 0xD0, + 0x07, 0xEB, 0x07, 0x10, + 0x00, 0xEB, 0x80, 0x00, 0x06, 0xEB, 0x80, 0x00, 0x83, 0x46, 0x00, 0xF0, + 0x0A, 0xFA, 0x01, 0x28, + 0x12, 0xD1, 0x58, 0x46, 0x00, 0xF0, 0xF7, 0xF9, 0x70, 0xB9, 0x0B, 0xF1, + 0x10, 0x01, 0x20, 0x46, + 0x08, 0xF0, 0x51, 0xF9, 0xDA, 0xF8, 0x04, 0x10, 0x88, 0x42, 0x05, 0xD2, + 0xBB, 0xF8, 0x42, 0x00, + 0x40, 0xF0, 0x40, 0x01, 0xAB, 0xF8, 0x42, 0x10, 0x7F, 0x1C, 0x95, 0xF9, + 0xF4, 0x03, 0xB8, 0x42, + 0xD4, 0xDA, 0x8F, 0xE7, 0x2D, 0xE9, 0xF8, 0x43, 0x06, 0x46, 0xFF, 0x20, + 0x00, 0x25, 0x8D, 0xF8, + 0x00, 0x00, 0x01, 0x27, 0x06, 0xF5, 0x80, 0x54, 0x1A, 0xE0, 0x00, 0xBF, + 0x05, 0xEB, 0x05, 0x10, + 0x00, 0xEB, 0x80, 0x00, 0x06, 0xEB, 0x80, 0x08, 0xD4, 0xF8, 0xEC, 0x03, + 0x07, 0xFA, 0x05, 0xF1, + 0x08, 0x42, 0x0C, 0xD0, 0x40, 0x46, 0x00, 0xF0, 0xC6, 0xF9, 0x01, 0x28, + 0x07, 0xD0, 0x98, 0xF8, + 0x3E, 0x01, 0x05, 0x28, 0x03, 0xD1, 0x29, 0x46, 0x30, 0x46, 0xFF, 0xF7, + 0x28, 0xFF, 0x6D, 0x1C, + 0x94, 0xF9, 0xF4, 0x03, 0xA8, 0x42, 0xE1, 0xDA, 0x00, 0x25, 0x1B, 0xE0, + 0x05, 0xEB, 0x05, 0x10, + 0x00, 0xEB, 0x80, 0x00, 0x06, 0xEB, 0x80, 0x08, 0xD4, 0xF8, 0xEC, 0x03, + 0x07, 0xFA, 0x05, 0xF1, + 0x08, 0x42, 0x0E, 0xD0, 0x40, 0x46, 0x00, 0xF0, 0xA6, 0xF9, 0x01, 0x28, + 0x09, 0xD0, 0x40, 0x46, + 0x00, 0xF0, 0xAF, 0xF9, 0x01, 0x28, 0x04, 0xD1, 0x6A, 0x46, 0x29, 0x46, + 0x30, 0x46, 0xFF, 0xF7, + 0x6C, 0xFE, 0x6D, 0x1C, 0x94, 0xF9, 0xF4, 0x03, 0xA8, 0x42, 0xDF, 0xDA, + 0x00, 0x25, 0xB8, 0x46, + 0x21, 0xE0, 0x00, 0xBF, 0x05, 0xEB, 0x05, 0x10, 0x00, 0xEB, 0x80, 0x00, + 0x06, 0xEB, 0x80, 0x07, + 0xD4, 0xF8, 0xEC, 0x13, 0x08, 0xFA, 0x05, 0xF0, 0x01, 0x42, 0x13, 0xD0, + 0x38, 0x46, 0x00, 0xF0, + 0x82, 0xF9, 0x01, 0x28, 0x0E, 0xD0, 0x38, 0x46, 0x00, 0xF0, 0x85, 0xF9, + 0x01, 0x28, 0x04, 0xD1, + 0x6A, 0x46, 0x29, 0x46, 0x30, 0x46, 0xFF, 0xF7, 0x48, 0xFE, 0x37, 0xF8, + 0x42, 0x0F, 0x20, 0xF0, + 0x20, 0x00, 0x38, 0x80, 0x6D, 0x1C, 0x94, 0xF9, 0xF4, 0x03, 0xA8, 0x42, + 0xDA, 0xDA, 0x9D, 0xF8, + 0x00, 0x00, 0x40, 0x1C, 0x84, 0xF8, 0xE0, 0x04, 0xBD, 0xE8, 0xF8, 0x83, + 0x10, 0xB5, 0x04, 0x46, + 0x87, 0x48, 0x03, 0x21, 0x08, 0x30, 0x05, 0xF0, 0xD2, 0xFB, 0x85, 0x48, + 0x1E, 0x21, 0x80, 0x68, + 0x0B, 0xF0, 0xD2, 0xFF, 0x41, 0xF2, 0x68, 0x40, 0x7C, 0x21, 0x20, 0x44, + 0x0B, 0xF0, 0xEE, 0xFF, + 0x80, 0x48, 0x02, 0x68, 0x80, 0x48, 0xB2, 0xF8, 0x96, 0x13, 0x00, 0x78, + 0x49, 0x43, 0x10, 0xB1, + 0xB2, 0xF8, 0x6C, 0x13, 0x49, 0x43, 0xB2, 0xF8, 0x98, 0x23, 0x02, 0xFB, + 0x02, 0x10, 0x78, 0x4A, + 0x11, 0x60, 0x11, 0x1D, 0x08, 0x60, 0x20, 0x46, 0xFF, 0xF7, 0x5C, 0xFF, + 0x20, 0x46, 0xFF, 0xF7, + 0x3B, 0xFD, 0x73, 0x48, 0x03, 0x21, 0xBD, 0xE8, 0x10, 0x40, 0x08, 0x30, + 0x05, 0xF0, 0xC4, 0xBB, + 0xF0, 0xB4, 0x84, 0x6B, 0xC2, 0x68, 0x8B, 0x6B, 0xCE, 0x68, 0x17, 0x14, + 0x67, 0x43, 0x4F, 0xEA, + 0x26, 0x4C, 0x03, 0xFB, 0x0C, 0x77, 0xE5, 0x18, 0x97, 0xFB, 0xF5, 0xF7, + 0x67, 0xF3, 0x1F, 0x42, + 0x17, 0xB2, 0x67, 0x43, 0x34, 0xB2, 0x03, 0xFB, 0x04, 0x73, 0x93, 0xFB, + 0xF5, 0xF3, 0x63, 0xF3, + 0x0F, 0x02, 0xC2, 0x60, 0xF0, 0xBC, 0xDF, 0xE4, 0x62, 0x4A, 0x12, 0x68, + 0x92, 0xF8, 0x40, 0x24, + 0x92, 0x06, 0x11, 0xD5, 0x90, 0xF8, 0x49, 0x31, 0x23, 0xB1, 0x91, 0xF8, + 0x49, 0x21, 0x0A, 0xB1, + 0x93, 0x42, 0x07, 0xD1, 0x90, 0xF8, 0x4A, 0x01, 0x30, 0xB1, 0x91, 0xF8, + 0x4A, 0x11, 0x19, 0xB1, + 0x88, 0x42, 0x01, 0xD0, 0x00, 0x20, 0x70, 0x47, 0x01, 0x20, 0x70, 0x47, + 0x2D, 0xE9, 0xFE, 0x4F, + 0x00, 0xF5, 0x80, 0x54, 0x05, 0x46, 0xD4, 0xF8, 0xF8, 0x13, 0xD4, 0xF8, + 0xEC, 0x03, 0x01, 0x40, + 0x00, 0x28, 0xC4, 0xF8, 0xF8, 0x13, 0x4F, 0xF0, 0x00, 0x00, 0x0C, 0xD0, + 0x81, 0x46, 0x0F, 0x21, + 0x4E, 0x48, 0x0B, 0xF0, 0x61, 0xFF, 0x00, 0x26, 0xDF, 0xF8, 0x30, 0xA1, + 0x4F, 0xF0, 0x01, 0x08, + 0x41, 0xF2, 0xFC, 0x3B, 0x5A, 0xE0, 0xC4, 0xF8, 0xF8, 0x03, 0xBD, 0xE8, + 0xFE, 0x8F, 0xD4, 0xF8, + 0xEC, 0x23, 0x08, 0xFA, 0x06, 0xF1, 0x40, 0x46, 0x0A, 0x42, 0x4E, 0xD0, + 0x06, 0xEB, 0x06, 0x12, + 0x02, 0xEB, 0x82, 0x02, 0x05, 0xEB, 0x82, 0x07, 0x97, 0xF8, 0x40, 0x20, + 0x12, 0x06, 0xD4, 0xF8, + 0xF8, 0x23, 0x11, 0xD5, 0x0A, 0x43, 0x05, 0xEB, 0x86, 0x01, 0xC4, 0xF8, + 0xF8, 0x23, 0x01, 0xF5, + 0x9C, 0x51, 0xD7, 0xF8, 0x5A, 0x20, 0xCA, 0x67, 0x97, 0xF8, 0x46, 0x11, + 0x88, 0x40, 0x40, 0xEA, + 0x09, 0x09, 0x0A, 0xF8, 0x01, 0x60, 0x30, 0xE0, 0x0A, 0x42, 0x2E, 0xD0, + 0x97, 0xF8, 0x3E, 0x21, + 0x06, 0x2A, 0x14, 0xD0, 0x05, 0x2A, 0x12, 0xD0, 0x97, 0xF8, 0x46, 0x11, + 0x08, 0xFA, 0x01, 0xF0, + 0x40, 0xEA, 0x09, 0x09, 0x0A, 0xF8, 0x01, 0x60, 0x05, 0xEB, 0x86, 0x01, + 0x01, 0xEB, 0x0B, 0x00, + 0x5A, 0x37, 0x39, 0x46, 0x00, 0x90, 0x03, 0xF0, 0x8F, 0xFB, 0x98, 0xB1, + 0x15, 0xE0, 0xB7, 0xF8, + 0x42, 0x00, 0x20, 0xF0, 0x08, 0x00, 0xA7, 0xF8, 0x42, 0x00, 0xD4, 0xF8, + 0xF8, 0x03, 0x88, 0x43, + 0xC4, 0xF8, 0xF8, 0x03, 0x22, 0x49, 0x05, 0xEB, 0x86, 0x00, 0x00, 0xF5, + 0x9C, 0x50, 0x09, 0x68, + 0xC1, 0x67, 0xE1, 0xE7, 0x00, 0x98, 0x39, 0x68, 0x01, 0x60, 0x76, 0x1C, + 0x94, 0xF9, 0xF4, 0x03, + 0xB0, 0x42, 0xA4, 0xDA, 0x00, 0x27, 0x72, 0xE0, 0xD4, 0xF8, 0xEC, 0x23, + 0x08, 0xFA, 0x07, 0xF1, + 0x0A, 0x42, 0x6B, 0xD0, 0xD4, 0xF8, 0xF8, 0x23, 0x0A, 0x42, 0x67, 0xD1, + 0x07, 0xEB, 0x07, 0x11, + 0x01, 0xEB, 0x81, 0x01, 0x05, 0xEB, 0x81, 0x01, 0x00, 0x91, 0x91, 0xF8, + 0x3E, 0x11, 0x02, 0x29, + 0x01, 0xD0, 0x01, 0x29, 0x5A, 0xD1, 0x00, 0x99, 0x43, 0x46, 0x91, 0xF8, + 0x46, 0x11, 0x08, 0xFA, + 0x01, 0xF0, 0x10, 0xEA, 0x09, 0x0F, 0x23, 0xD0, 0x1A, 0xF8, 0x01, 0x00, + 0xB8, 0x42, 0x4D, 0xD0, + 0x00, 0xEB, 0x00, 0x10, 0x00, 0xEB, 0x80, 0x00, 0x05, 0xEB, 0x80, 0x00, + 0x06, 0x46, 0x09, 0xE0, + 0x08, 0x06, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, 0xFD, 0x06, 0x10, 0x00, + 0x80, 0x2C, 0x10, 0x00, + 0x36, 0x7A, 0x01, 0x00, 0x00, 0x99, 0xFF, 0xF7, 0x2F, 0xFF, 0x01, 0x28, + 0x36, 0xD1, 0x30, 0x46, + 0x00, 0x99, 0xFF, 0xF7, 0x0D, 0xFF, 0x28, 0x46, 0x00, 0x99, 0x00, 0xF0, + 0xF7, 0xFD, 0x2D, 0xE0, + 0x00, 0x98, 0x98, 0x46, 0x5A, 0x30, 0x00, 0x26, 0x02, 0x90, 0x23, 0xE0, + 0xD4, 0xF8, 0xF8, 0x13, + 0x08, 0xFA, 0x06, 0xF0, 0x01, 0x42, 0x1C, 0xD0, 0x06, 0xEB, 0x06, 0x10, + 0x00, 0xEB, 0x80, 0x00, + 0x05, 0xEB, 0x80, 0x00, 0x05, 0xEB, 0x86, 0x01, 0x01, 0x90, 0x59, 0x44, + 0x02, 0x98, 0x03, 0xF0, + 0x13, 0xFB, 0x01, 0x28, 0x0D, 0xD1, 0xDD, 0xE9, 0x00, 0x10, 0xFF, 0xF7, + 0x05, 0xFF, 0x01, 0x28, + 0x07, 0xD1, 0xDD, 0xE9, 0x00, 0x10, 0xFF, 0xF7, 0xE3, 0xFE, 0x28, 0x46, + 0x00, 0x99, 0x00, 0xF0, + 0xCD, 0xFD, 0x76, 0x1C, 0x94, 0xF9, 0xF4, 0x03, 0xB0, 0x42, 0xD7, 0xDA, + 0x7F, 0x1C, 0x94, 0xF9, + 0xF4, 0x03, 0xB8, 0x42, 0x88, 0xDA, 0x28, 0xE7, 0x33, 0x49, 0x00, 0x20, + 0x88, 0x60, 0x0F, 0x21, + 0x32, 0x48, 0x0B, 0xF0, 0x79, 0xBE, 0x90, 0xF8, 0x42, 0x00, 0x40, 0x07, + 0x01, 0xD5, 0x01, 0x20, + 0x70, 0x47, 0x00, 0x20, 0x70, 0x47, 0x90, 0xF8, 0x3E, 0x01, 0x01, 0x28, + 0x00, 0xD0, 0x00, 0x20, + 0x70, 0x47, 0x90, 0xF8, 0x3E, 0x01, 0x02, 0x28, 0x05, 0xD0, 0x03, 0x28, + 0x03, 0xD0, 0x04, 0x28, + 0x01, 0xD0, 0x00, 0x20, 0x70, 0x47, 0x01, 0x20, 0x70, 0x47, 0x10, 0xB5, + 0x90, 0xF8, 0x42, 0x10, + 0x49, 0x06, 0x03, 0xD5, 0xFF, 0xF7, 0xED, 0xFF, 0x01, 0x28, 0x00, 0xD0, + 0x00, 0x20, 0x10, 0xBD, + 0x91, 0xF8, 0x3E, 0x21, 0x01, 0x2A, 0x08, 0xD0, 0x02, 0x2A, 0x06, 0xD0, + 0x06, 0x20, 0x81, 0xF8, + 0x3E, 0x01, 0x00, 0x20, 0x81, 0xF8, 0x43, 0x01, 0x70, 0x47, 0x00, 0xF0, + 0x87, 0xBD, 0xF0, 0xB5, + 0x91, 0xF9, 0x4B, 0x61, 0x93, 0xF9, 0x00, 0x40, 0x92, 0xF9, 0x4B, 0x51, + 0x77, 0x1C, 0x02, 0xD0, + 0x6D, 0x1C, 0x15, 0xD0, 0x0B, 0xE0, 0x6E, 0x1C, 0x0B, 0xD0, 0x00, 0xEB, + 0x85, 0x02, 0x00, 0xEB, + 0x84, 0x00, 0xD2, 0x6B, 0xC2, 0xF8, 0x50, 0x11, 0xC1, 0x63, 0x81, 0xF8, + 0x4B, 0x41, 0x1C, 0x70, + 0xF0, 0xBD, 0x64, 0x1C, 0x64, 0xB2, 0x40, 0xF8, 0x24, 0x10, 0x81, 0xF8, + 0x4B, 0x41, 0x02, 0xE0, + 0x00, 0xEB, 0x86, 0x01, 0xC9, 0x6B, 0x00, 0xEB, 0x84, 0x00, 0xC1, 0xF8, + 0x50, 0x21, 0xC2, 0x63, + 0x82, 0xF8, 0x4B, 0x41, 0xEB, 0xE7, 0x00, 0x00, 0x08, 0x06, 0x10, 0x00, + 0x80, 0x2C, 0x10, 0x00, + 0xF0, 0xB5, 0x44, 0x88, 0x4E, 0x88, 0x01, 0x25, 0xA4, 0x1B, 0x24, 0xB2, + 0x00, 0x2C, 0x01, 0xDB, + 0x26, 0x46, 0x00, 0xE0, 0x66, 0x42, 0x5E, 0x80, 0x56, 0x42, 0x94, 0x42, + 0x02, 0xDD, 0x44, 0x88, + 0xA4, 0x1A, 0x03, 0xE0, 0xB4, 0x42, 0x03, 0xDA, 0x44, 0x88, 0x14, 0x44, + 0x4C, 0x80, 0x00, 0x25, + 0x04, 0x88, 0x0F, 0x88, 0xE4, 0x1B, 0x24, 0xB2, 0x00, 0x2C, 0x01, 0xDB, + 0x27, 0x46, 0x00, 0xE0, + 0x67, 0x42, 0x1F, 0x80, 0x94, 0x42, 0x02, 0xDD, 0x00, 0x88, 0x80, 0x1A, + 0x03, 0xE0, 0xB4, 0x42, + 0x03, 0xDA, 0x00, 0x88, 0x10, 0x44, 0x08, 0x80, 0x00, 0x25, 0x28, 0x46, + 0xF0, 0xBD, 0x2D, 0xE9, + 0xF0, 0x41, 0x96, 0x4D, 0x0F, 0x46, 0x06, 0x46, 0x81, 0x89, 0x28, 0x68, + 0x90, 0x46, 0xF9, 0xB1, + 0x90, 0xF8, 0xC7, 0x43, 0x0B, 0xF0, 0xA8, 0xFE, 0x18, 0xB1, 0x28, 0x68, + 0x90, 0xF8, 0xC9, 0x43, + 0x12, 0xE0, 0xB0, 0x7D, 0x02, 0x28, 0x04, 0xD0, 0x01, 0x28, 0x06, 0xD0, + 0x03, 0x28, 0x08, 0xD0, + 0x0A, 0xE0, 0x28, 0x68, 0x90, 0xF8, 0xCE, 0x43, 0x06, 0xE0, 0x28, 0x68, + 0x90, 0xF8, 0x51, 0x44, + 0x02, 0xE0, 0x28, 0x68, 0x90, 0xF8, 0x2C, 0x44, 0xB0, 0x89, 0x40, 0x1E, + 0xB0, 0x81, 0x11, 0xE0, + 0xB1, 0x7D, 0x90, 0xF8, 0xCB, 0x43, 0x02, 0x29, 0x04, 0xD0, 0x01, 0x29, + 0x05, 0xD0, 0x03, 0x29, + 0x06, 0xD0, 0x07, 0xE0, 0x90, 0xF8, 0xCE, 0x43, 0x04, 0xE0, 0x90, 0xF8, + 0x52, 0x44, 0x01, 0xE0, + 0x90, 0xF8, 0x2D, 0x44, 0x29, 0x68, 0xB2, 0x7D, 0x91, 0xF8, 0xCA, 0x03, + 0x02, 0x2A, 0x04, 0xD0, + 0x01, 0x2A, 0x05, 0xD0, 0x03, 0x2A, 0x06, 0xD0, 0x07, 0xE0, 0x91, 0xF8, + 0xCD, 0x03, 0x04, 0xE0, + 0x91, 0xF8, 0x53, 0x04, 0x01, 0xE0, 0x91, 0xF8, 0x2E, 0x04, 0x38, 0x80, + 0xA8, 0xF8, 0x00, 0x40, + 0xBD, 0xE8, 0xF0, 0x81, 0x2D, 0xE9, 0xF0, 0x4F, 0x00, 0xF1, 0x5E, 0x04, + 0x85, 0xB0, 0x4F, 0xF0, + 0x00, 0x0B, 0x84, 0xF8, 0x15, 0xB0, 0xDF, 0xF8, 0xA4, 0x91, 0x0E, 0x46, + 0x80, 0xF8, 0x74, 0xB0, + 0x05, 0x46, 0xB0, 0xF8, 0x42, 0x10, 0x4F, 0xF0, 0x01, 0x08, 0xD9, 0xF8, + 0x00, 0x00, 0x4A, 0x06, + 0xC2, 0x46, 0x01, 0xD5, 0x02, 0x21, 0x0F, 0xE0, 0x0A, 0x07, 0x06, 0xD5, + 0x90, 0xF8, 0x40, 0x24, + 0x52, 0x06, 0x02, 0xD5, 0x84, 0xF8, 0x16, 0xA0, 0x07, 0xE0, 0x90, 0xF8, + 0x20, 0x24, 0x92, 0x07, + 0x03, 0xD5, 0x09, 0x06, 0x01, 0xD5, 0x03, 0x21, 0xA1, 0x75, 0x95, 0xF8, + 0x3E, 0x11, 0x03, 0x29, + 0x09, 0xD0, 0xA2, 0x7C, 0x01, 0x2A, 0x06, 0xD0, 0x04, 0x29, 0x2D, 0xD0, + 0x07, 0x29, 0x2B, 0xD0, + 0x08, 0x29, 0x76, 0xD0, 0x9C, 0xE0, 0x90, 0xF8, 0xC6, 0x73, 0x0B, 0xF0, + 0x25, 0xFE, 0x18, 0xB1, + 0xD9, 0xF8, 0x00, 0x00, 0x90, 0xF8, 0xC8, 0x73, 0x95, 0xF8, 0x3E, 0x01, + 0x03, 0x28, 0x00, 0xD0, + 0x00, 0x27, 0xA0, 0x7D, 0x01, 0x28, 0x02, 0xD0, 0x03, 0x28, 0x05, 0xD0, + 0x08, 0xE0, 0xD9, 0xF8, + 0x00, 0x00, 0x90, 0xF8, 0x50, 0x74, 0x03, 0xE0, 0xD9, 0xF8, 0x00, 0x00, + 0x90, 0xF8, 0x2B, 0x74, + 0xA7, 0x81, 0x31, 0x68, 0x21, 0x60, 0x31, 0x68, 0x61, 0x60, 0x84, 0xF8, + 0x14, 0xA0, 0x84, 0xF8, + 0x13, 0xA0, 0x84, 0xF8, 0x12, 0xB0, 0x73, 0xE0, 0x02, 0xAA, 0x01, 0xA9, + 0x20, 0x46, 0xFF, 0xF7, + 0x46, 0xFF, 0x03, 0xAB, 0xBD, 0xF9, 0x08, 0x20, 0x21, 0x46, 0x30, 0x46, + 0xFF, 0xF7, 0x10, 0xFF, + 0x83, 0x46, 0x04, 0xAB, 0xBD, 0xF9, 0x04, 0x20, 0x21, 0x1D, 0x30, 0x46, + 0xFF, 0xF7, 0x08, 0xFF, + 0x07, 0x46, 0xBB, 0xF1, 0x01, 0x0F, 0x03, 0xD0, 0x20, 0x7D, 0x01, 0x28, + 0x4D, 0xD0, 0x4E, 0xE0, + 0xBD, 0xF9, 0x0C, 0x00, 0xBD, 0xF9, 0x0E, 0x10, 0x81, 0x42, 0x00, 0xDD, + 0x08, 0x46, 0xD9, 0xF8, + 0x00, 0x10, 0x91, 0xF8, 0xCC, 0x23, 0x02, 0xF0, 0x0F, 0x03, 0x4F, 0xEA, + 0x12, 0x1C, 0xA2, 0x7D, + 0x02, 0x2A, 0x04, 0xD0, 0x01, 0x2A, 0x05, 0xD0, 0x03, 0x2A, 0x06, 0xD0, + 0x0B, 0xE0, 0x91, 0xF8, + 0xCF, 0x13, 0x04, 0xE0, 0x91, 0xF8, 0x54, 0x14, 0x01, 0xE0, 0x91, 0xF8, + 0x2F, 0x14, 0x01, 0xF0, + 0x0F, 0x03, 0x4F, 0xEA, 0x11, 0x1C, 0x01, 0x2F, 0x0C, 0xD0, 0xCD, 0xF8, + 0x00, 0xC0, 0xBD, 0xF9, + 0x08, 0x20, 0xBD, 0xF9, 0x04, 0x10, 0x07, 0xF0, 0xDC, 0xFD, 0x21, 0x7D, + 0x01, 0x29, 0x0D, 0xD0, + 0x18, 0xE0, 0x23, 0xE0, 0xA0, 0x68, 0x30, 0x60, 0xD9, 0xF8, 0x00, 0x00, + 0x90, 0xF8, 0x80, 0x03, + 0x00, 0x06, 0x01, 0xD4, 0x4F, 0xF0, 0x00, 0x08, 0x60, 0x46, 0xEE, 0xE7, + 0x95, 0xF8, 0x41, 0x11, + 0x41, 0xB9, 0xD9, 0xF8, 0x00, 0x10, 0x91, 0xF8, 0xBC, 0x13, 0x0A, 0x09, + 0xD4, 0xF8, 0x0E, 0x10, + 0x07, 0xF0, 0xD4, 0xFD, 0xC4, 0xF8, 0x0E, 0x00, 0x01, 0xE0, 0x84, 0xF8, + 0x15, 0xA0, 0x84, 0xF8, + 0x14, 0xB0, 0xE7, 0x74, 0xB8, 0xF1, 0x00, 0x0F, 0x02, 0xD1, 0x03, 0xE0, + 0xA0, 0x68, 0x30, 0x60, + 0x30, 0x68, 0xA0, 0x60, 0x05, 0xB0, 0x40, 0x46, 0xBD, 0xE8, 0xF0, 0x8F, + 0x4C, 0x07, 0x10, 0x00, + 0x2D, 0xE9, 0xF0, 0x41, 0x17, 0x46, 0x06, 0x46, 0xDD, 0xE9, 0x06, 0xC2, + 0xC8, 0x78, 0x00, 0x25, + 0x0C, 0x46, 0x03, 0x28, 0x30, 0xD2, 0xFE, 0x48, 0xDF, 0xF8, 0xF8, 0xE3, + 0x01, 0x68, 0x91, 0xF8, + 0x0A, 0x14, 0xA1, 0x70, 0xDE, 0xF8, 0x04, 0xE0, 0xBE, 0xF1, 0x00, 0x0F, + 0x06, 0xDD, 0xD0, 0xF8, + 0x00, 0xE0, 0x9E, 0xF8, 0x0B, 0xE4, 0xA1, 0xEB, 0x0E, 0x01, 0xA1, 0x70, + 0x00, 0x68, 0x71, 0x6B, + 0xB0, 0xF8, 0x0C, 0x04, 0x81, 0x42, 0x15, 0xDD, 0xDB, 0xB2, 0x39, 0x46, + 0x60, 0x46, 0x00, 0xF0, + 0xE8, 0xFA, 0xE0, 0x78, 0x40, 0x1C, 0xC0, 0xB2, 0xE0, 0x70, 0x03, 0x28, + 0x06, 0xD0, 0xEE, 0x49, + 0xB6, 0xF9, 0x56, 0x00, 0xB1, 0xF9, 0x00, 0x10, 0x88, 0x42, 0x05, 0xDD, + 0x03, 0x20, 0xE0, 0x70, + 0x01, 0x25, 0x01, 0xE0, 0x00, 0x20, 0xE0, 0x70, 0x28, 0x46, 0xBD, 0xE8, + 0xF0, 0x81, 0x2D, 0xE9, + 0xFC, 0x5F, 0x4F, 0xF0, 0x00, 0x0B, 0x0C, 0x46, 0x81, 0x46, 0x5D, 0x46, + 0x4F, 0xF0, 0x02, 0x0A, + 0x01, 0x26, 0x00, 0xF5, 0x80, 0x57, 0x7C, 0xE0, 0xD7, 0xF8, 0xEC, 0x03, + 0x06, 0xFA, 0x05, 0xFE, + 0x10, 0xEA, 0x0E, 0x0F, 0x74, 0xD0, 0x05, 0xEB, 0x05, 0x10, 0x00, 0xEB, + 0x80, 0x01, 0x09, 0xEB, + 0x81, 0x00, 0x00, 0xF5, 0x9A, 0x71, 0x90, 0xF8, 0x3E, 0x81, 0xB0, 0xF9, + 0x0E, 0xC0, 0xB8, 0xF1, + 0x01, 0x0F, 0x0F, 0xD0, 0xB8, 0xF1, 0x02, 0x0F, 0x0C, 0xD0, 0x90, 0xF8, + 0x42, 0x00, 0x40, 0x07, + 0x51, 0xD5, 0xD2, 0x48, 0xEB, 0xB2, 0x61, 0x46, 0xB0, 0xF9, 0x08, 0x20, + 0x20, 0x46, 0x00, 0xF0, + 0xA0, 0xFA, 0x55, 0xE0, 0xCE, 0x4B, 0xB0, 0xF9, 0x54, 0x20, 0xB3, 0xF9, + 0x00, 0x30, 0x9A, 0x42, + 0x4E, 0xDB, 0xB8, 0xF1, 0x01, 0x0F, 0x03, 0xD1, 0x00, 0x22, 0x0A, 0x60, + 0x4A, 0x60, 0x0A, 0x81, + 0xC6, 0x4A, 0xB2, 0xF9, 0x00, 0x30, 0x63, 0x45, 0x03, 0xDC, 0xB2, 0xF9, + 0x02, 0x30, 0x63, 0x45, + 0x0C, 0xDA, 0xB0, 0xF8, 0x42, 0x30, 0x43, 0xF0, 0x04, 0x03, 0xA0, 0xF8, + 0x42, 0x30, 0xC3, 0x68, + 0x4B, 0x60, 0x81, 0xF8, 0x08, 0xA0, 0xB2, 0xF9, 0x00, 0x20, 0x19, 0xE0, + 0xB2, 0xF9, 0x08, 0x30, + 0x63, 0x45, 0x03, 0xDC, 0xB2, 0xF9, 0x0A, 0x30, 0x63, 0x45, 0x19, 0xDA, + 0xB0, 0xF8, 0x42, 0x30, + 0x43, 0xF0, 0x04, 0x03, 0xA0, 0xF8, 0x42, 0x30, 0x90, 0xF8, 0x3E, 0x31, + 0x01, 0x2B, 0x02, 0xD0, + 0x0B, 0x7A, 0x02, 0x2B, 0x01, 0xD1, 0xC3, 0x68, 0x4B, 0x60, 0x0E, 0x72, + 0xB2, 0xF9, 0x08, 0x20, + 0x2B, 0x46, 0xCD, 0xE9, 0x00, 0x42, 0x62, 0x46, 0xFF, 0xF7, 0x4A, 0xFF, + 0x83, 0x46, 0x0F, 0xE0, + 0x00, 0x20, 0x08, 0x72, 0x08, 0xE0, 0xA9, 0x48, 0xB0, 0xF9, 0x08, 0x10, + 0x61, 0x45, 0x07, 0xDA, + 0xB0, 0xF9, 0x0A, 0x00, 0x60, 0x45, 0x03, 0xDD, 0xA0, 0x68, 0x40, 0xEA, + 0x0E, 0x00, 0xA0, 0x60, + 0x6D, 0x1C, 0x97, 0xF9, 0xF4, 0x03, 0xA8, 0x42, 0xBF, 0xF4, 0x7E, 0xAF, + 0x20, 0x68, 0x61, 0x68, + 0x9C, 0x4A, 0x50, 0xEA, 0x01, 0x03, 0xA3, 0x68, 0x01, 0xD0, 0x23, 0xB1, + 0x04, 0xE0, 0x1B, 0xB1, + 0x82, 0xF8, 0x00, 0xA0, 0x00, 0xE0, 0x16, 0x70, 0x98, 0x4A, 0x18, 0x32, + 0x13, 0x68, 0x98, 0x43, + 0x20, 0x60, 0x50, 0x68, 0x81, 0x43, 0x61, 0x60, 0x96, 0x49, 0xA0, 0x68, + 0x09, 0x78, 0x08, 0x43, + 0x05, 0xD1, 0x92, 0x49, 0x18, 0x22, 0x18, 0x39, 0x90, 0x48, 0x0B, 0xF0, + 0x8B, 0xFB, 0x58, 0x46, + 0xBD, 0xE8, 0xFC, 0x9F, 0x8B, 0x4A, 0x10, 0xB5, 0x12, 0x78, 0x00, 0x21, + 0x01, 0x2A, 0x01, 0xD1, + 0x03, 0xB1, 0x01, 0x21, 0x90, 0xF8, 0x3E, 0x21, 0x04, 0x2A, 0x18, 0xD1, + 0x84, 0x4A, 0x12, 0x68, + 0x92, 0xF8, 0x00, 0x34, 0x5B, 0x07, 0x12, 0xD5, 0xB0, 0xF8, 0x4A, 0x30, + 0xB0, 0xF8, 0x4C, 0x40, + 0x1B, 0x1B, 0x82, 0x4C, 0xA4, 0x8A, 0xA3, 0x42, 0x09, 0xDD, 0x90, 0xF8, + 0x5B, 0x30, 0x90, 0xF8, + 0x5A, 0x00, 0x92, 0xF8, 0x09, 0x24, 0x18, 0x1A, 0x90, 0x42, 0x00, 0xDD, + 0x01, 0x21, 0x08, 0x46, + 0x10, 0xBD, 0x30, 0xB5, 0x79, 0x4D, 0x00, 0x24, 0x18, 0x35, 0x09, 0xB1, + 0x29, 0x68, 0x11, 0xB9, + 0x12, 0xB1, 0x69, 0x68, 0x01, 0xB1, 0x01, 0x24, 0x72, 0x49, 0x09, 0x78, + 0x01, 0x29, 0x01, 0xD1, + 0x03, 0xB1, 0x01, 0x24, 0x6E, 0x49, 0x09, 0x68, 0x91, 0xF8, 0x00, 0x24, + 0x52, 0x07, 0x16, 0xD5, + 0x90, 0xF8, 0x3E, 0x21, 0x04, 0x2A, 0x12, 0xD1, 0xB0, 0xF8, 0x4A, 0x20, + 0xB0, 0xF8, 0x4C, 0x30, + 0xD2, 0x1A, 0x6A, 0x4B, 0x9B, 0x8A, 0x9A, 0x42, 0x09, 0xDD, 0x90, 0xF8, + 0x5B, 0x20, 0x90, 0xF8, + 0x5A, 0x00, 0x91, 0xF8, 0x09, 0x14, 0x10, 0x1A, 0x88, 0x42, 0x00, 0xDD, + 0x01, 0x24, 0x20, 0x46, + 0x30, 0xBD, 0x2D, 0xE9, 0xF3, 0x4F, 0x8D, 0xB0, 0x5E, 0x48, 0x00, 0x25, + 0x8B, 0x46, 0x0A, 0x95, + 0x01, 0x78, 0x5E, 0x48, 0x18, 0x38, 0x01, 0x29, 0x06, 0xD1, 0xDB, 0xF8, + 0x08, 0x10, 0x19, 0xB1, + 0xD0, 0xF8, 0x1C, 0x80, 0x47, 0x6A, 0x02, 0xE0, 0xD0, 0xF8, 0x18, 0x80, + 0x07, 0x6A, 0x0D, 0x9E, + 0x00, 0x24, 0x4F, 0xF0, 0x01, 0x09, 0x06, 0xF5, 0x80, 0x56, 0x0F, 0xE1, + 0x09, 0xFA, 0x04, 0xF0, + 0xD6, 0xF8, 0xEC, 0x13, 0x0B, 0x90, 0x01, 0x42, 0x1A, 0xD0, 0x04, 0xEB, + 0x04, 0x10, 0x00, 0xEB, + 0x80, 0x01, 0x0D, 0x98, 0x00, 0xEB, 0x81, 0x0A, 0x9A, 0xF8, 0x42, 0x00, + 0x40, 0x07, 0x0F, 0xD5, + 0x0A, 0xF5, 0x9A, 0x70, 0x01, 0x90, 0xBA, 0xF9, 0x0E, 0x00, 0x02, 0x90, + 0x9A, 0xF8, 0x3E, 0x01, + 0x01, 0x28, 0x06, 0xD0, 0x02, 0x28, 0x04, 0xD0, 0x04, 0x28, 0x1F, 0xD0, + 0x05, 0x28, 0x7D, 0xD0, + 0xEB, 0xE0, 0xDB, 0xF8, 0x00, 0x10, 0xDB, 0xF8, 0x04, 0x20, 0xDB, 0xF8, + 0x08, 0x30, 0x50, 0x46, + 0xFF, 0xF7, 0x60, 0xFF, 0x01, 0x28, 0x02, 0xD1, 0x01, 0x99, 0x81, 0xF8, + 0x01, 0x90, 0xDB, 0xF8, + 0x00, 0x10, 0xDB, 0xF8, 0x04, 0x20, 0xDB, 0xF8, 0x08, 0x30, 0x50, 0x46, + 0xFF, 0xF7, 0x79, 0xFF, + 0x01, 0x28, 0xE5, 0xD1, 0x01, 0x99, 0x00, 0x20, 0x88, 0x70, 0xCE, 0xE0, + 0xDB, 0xF8, 0x00, 0x10, + 0xDB, 0xF8, 0x04, 0x20, 0xDB, 0xF8, 0x08, 0x30, 0x50, 0x46, 0xFF, 0xF7, + 0x6A, 0xFF, 0x01, 0x28, + 0x09, 0xD0, 0x01, 0x98, 0x80, 0x78, 0x48, 0xB1, 0x01, 0x99, 0x40, 0x1E, + 0x10, 0xF0, 0xFF, 0x00, + 0x88, 0x70, 0x03, 0xD0, 0x07, 0xE0, 0x01, 0x99, 0x00, 0x20, 0x88, 0x70, + 0x9A, 0xF8, 0x48, 0x11, + 0x09, 0xFA, 0x01, 0xF0, 0x05, 0x43, 0xDB, 0xF8, 0x00, 0x10, 0xDB, 0xF8, + 0x04, 0x20, 0xDB, 0xF8, + 0x08, 0x30, 0x50, 0x46, 0xFF, 0xF7, 0x26, 0xFF, 0x01, 0x28, 0x02, 0xD1, + 0x01, 0x99, 0x81, 0xF8, + 0x01, 0x90, 0x01, 0x98, 0xC0, 0x78, 0x03, 0x28, 0x1D, 0xD2, 0x01, 0x99, + 0x89, 0x78, 0xD1, 0xB1, + 0x17, 0x49, 0xDA, 0xF8, 0x34, 0x20, 0x09, 0x68, 0xB1, 0xF8, 0x0C, 0x14, + 0x8A, 0x42, 0x0F, 0xDD, + 0x40, 0x1C, 0x01, 0x99, 0xC0, 0xB2, 0x03, 0x28, 0xC8, 0x70, 0x06, 0xD0, + 0x12, 0x49, 0xBA, 0xF9, + 0x56, 0x00, 0xB1, 0xF9, 0x00, 0x10, 0x88, 0x42, 0x05, 0xDD, 0x01, 0x20, + 0x0A, 0x90, 0x02, 0xE0, + 0x01, 0x99, 0x00, 0x20, 0xC8, 0x70, 0x02, 0x98, 0x0F, 0xFA, 0x88, 0xF2, + 0x82, 0x42, 0x04, 0xDC, + 0x02, 0x98, 0x4F, 0xEA, 0x28, 0x41, 0x81, 0x42, 0x16, 0xDA, 0x01, 0x98, + 0x02, 0x21, 0xE3, 0xB2, + 0x01, 0x72, 0x01, 0x98, 0xDA, 0xF8, 0x0C, 0x10, 0x41, 0x60, 0x37, 0xE0, + 0x56, 0xE0, 0x00, 0x00, + 0x4C, 0x07, 0x10, 0x00, 0x14, 0x06, 0x10, 0x00, 0x8C, 0x06, 0x10, 0x00, + 0xA8, 0x2C, 0x10, 0x00, + 0xA0, 0x06, 0x10, 0x00, 0x20, 0x07, 0x10, 0x00, 0x38, 0xB2, 0x02, 0x99, + 0x04, 0x90, 0x88, 0x42, + 0x03, 0xDC, 0x02, 0x98, 0x39, 0x14, 0x81, 0x42, 0x25, 0xDA, 0x01, 0x98, + 0x00, 0x7A, 0x02, 0x28, + 0x06, 0xD1, 0x01, 0x99, 0x81, 0xF8, 0x08, 0x90, 0x01, 0x98, 0xDA, 0xF8, + 0x0C, 0x10, 0x41, 0x60, + 0x01, 0x98, 0x40, 0x78, 0x80, 0xB9, 0x01, 0x98, 0x53, 0x46, 0x80, 0x78, + 0x00, 0x28, 0x82, 0x48, + 0x02, 0x69, 0x04, 0xD0, 0x01, 0x98, 0x0A, 0xF1, 0x0C, 0x01, 0x00, 0x1D, + 0x02, 0xE0, 0x0A, 0xF1, + 0x10, 0x01, 0x08, 0x1F, 0x00, 0xF0, 0xE2, 0xF8, 0x04, 0x9A, 0xE3, 0xB2, + 0x58, 0x46, 0x02, 0x99, + 0x00, 0xF0, 0xCF, 0xF8, 0x31, 0xE0, 0xDB, 0xF8, 0x08, 0x00, 0x0B, 0x99, + 0x08, 0x43, 0xCB, 0xF8, + 0x08, 0x00, 0x01, 0x98, 0x80, 0x78, 0x20, 0xB1, 0x01, 0x99, 0x00, 0x22, + 0x50, 0x46, 0x09, 0x1D, + 0x03, 0xE0, 0x01, 0x22, 0x0A, 0xF1, 0x0C, 0x01, 0x50, 0x46, 0x00, 0xF0, + 0xA8, 0xF8, 0xBA, 0xF8, + 0x42, 0x00, 0x20, 0xF0, 0x04, 0x01, 0xAA, 0xF8, 0x42, 0x10, 0x16, 0xE0, + 0x01, 0x98, 0x00, 0x7A, + 0x01, 0x28, 0x12, 0xD1, 0x01, 0x98, 0x80, 0x78, 0x78, 0xB1, 0x01, 0x99, + 0x01, 0x22, 0x50, 0x46, + 0x09, 0x1D, 0x00, 0xF0, 0x94, 0xF8, 0xBA, 0xF8, 0x42, 0x00, 0x20, 0xF0, + 0x04, 0x01, 0xAA, 0xF8, + 0x42, 0x10, 0xBA, 0xF8, 0x56, 0x10, 0xAA, 0xF8, 0x54, 0x10, 0x64, 0x1C, + 0x96, 0xF9, 0xF4, 0x03, + 0xA0, 0x42, 0xBF, 0xF4, 0xEB, 0xAE, 0x5D, 0x48, 0x00, 0x68, 0x90, 0xF8, + 0x00, 0x04, 0x80, 0x07, + 0x02, 0xD5, 0x28, 0x46, 0xFC, 0xF7, 0x6F, 0xFC, 0x59, 0x48, 0x15, 0xB1, + 0x80, 0xF8, 0x00, 0x90, + 0x01, 0xE0, 0x00, 0x21, 0x01, 0x70, 0x0A, 0x98, 0x0F, 0xB0, 0xBD, 0xE8, + 0xF0, 0x8F, 0x53, 0x49, + 0x0B, 0x68, 0x51, 0x49, 0x5A, 0x8E, 0x18, 0x39, 0x12, 0x1A, 0x08, 0x80, + 0x4A, 0x80, 0x93, 0xF8, + 0x0F, 0x34, 0x18, 0x44, 0x88, 0x80, 0xD0, 0x1A, 0xC8, 0x80, 0x70, 0x47, + 0x4B, 0x49, 0x0B, 0x68, + 0x49, 0x49, 0x5A, 0x8E, 0x18, 0x39, 0x12, 0x1A, 0x08, 0x81, 0x4A, 0x81, + 0x93, 0xF8, 0x12, 0x34, + 0x18, 0x44, 0x88, 0x81, 0xD0, 0x1A, 0xC8, 0x81, 0x70, 0x47, 0x10, 0xB5, + 0x43, 0x4C, 0x20, 0x68, + 0x90, 0xF8, 0x0E, 0x04, 0xFF, 0xF7, 0xDB, 0xFF, 0x20, 0x68, 0x90, 0xF8, + 0x0E, 0x04, 0xFF, 0xF7, + 0xE5, 0xFF, 0x22, 0x68, 0x3C, 0x49, 0x92, 0xF8, 0x10, 0x04, 0x18, 0x39, + 0x40, 0x43, 0x08, 0x61, + 0x92, 0xF8, 0x11, 0x04, 0x88, 0x82, 0x18, 0x22, 0x37, 0x48, 0x0B, 0xF0, + 0xB3, 0xF9, 0x38, 0x49, + 0x00, 0x20, 0x35, 0x4A, 0x08, 0x70, 0x37, 0x49, 0x18, 0x3A, 0x48, 0x60, + 0x10, 0x63, 0x50, 0x63, + 0x90, 0x63, 0x35, 0x4A, 0x10, 0x70, 0x08, 0x70, 0x10, 0xBD, 0xFE, 0xB5, + 0x00, 0x24, 0x06, 0x46, + 0x00, 0x94, 0x01, 0x94, 0x69, 0x46, 0x02, 0x94, 0xFF, 0xF7, 0x61, 0xFD, + 0x05, 0x46, 0x69, 0x46, + 0x30, 0x46, 0xFF, 0xF7, 0x66, 0xFE, 0x40, 0xEA, 0x05, 0x01, 0x2A, 0x48, + 0x01, 0x29, 0x01, 0xD0, + 0x41, 0x68, 0x05, 0xE0, 0x25, 0x49, 0x09, 0x68, 0x91, 0xF8, 0x0A, 0x14, + 0x49, 0x1F, 0x41, 0x60, + 0x00, 0x29, 0x04, 0xDD, 0x49, 0x1E, 0x41, 0x60, 0x21, 0x49, 0x09, 0x78, + 0x01, 0xB9, 0x44, 0x60, + 0x1D, 0x48, 0x9D, 0xE8, 0x0E, 0x00, 0x18, 0x30, 0x80, 0xE8, 0x0E, 0x00, + 0xFE, 0xBD, 0x02, 0x23, + 0x80, 0xF8, 0x3E, 0x31, 0x00, 0x23, 0x80, 0xF8, 0x42, 0x31, 0x01, 0x2A, + 0x05, 0xD0, 0x09, 0x68, + 0x01, 0x62, 0x01, 0x21, 0x80, 0xF8, 0x34, 0x11, 0x70, 0x47, 0x03, 0x22, + 0x0C, 0x30, 0x07, 0xF0, + 0x67, 0xBA, 0x10, 0xB5, 0x01, 0x24, 0x9C, 0x40, 0x91, 0x42, 0x03, 0xDA, + 0x01, 0x68, 0x21, 0x43, + 0x01, 0x60, 0x10, 0xBD, 0x41, 0x68, 0x21, 0x43, 0x41, 0x60, 0x10, 0xBD, + 0x70, 0xB5, 0x15, 0x46, + 0x0A, 0x46, 0x06, 0x46, 0x01, 0x46, 0x1C, 0x46, 0x10, 0x46, 0x07, 0xF0, + 0x74, 0xFA, 0xA8, 0x42, + 0x09, 0xDD, 0x00, 0x22, 0x31, 0x46, 0x20, 0x46, 0xFF, 0xF7, 0xD1, 0xFF, + 0x34, 0xF8, 0x42, 0x0F, + 0x20, 0xF0, 0x04, 0x00, 0x20, 0x80, 0x70, 0xBD, 0xA8, 0x2C, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, + 0xA4, 0x06, 0x10, 0x00, 0x14, 0x06, 0x10, 0x00, 0xA5, 0x06, 0x10, 0x00, + 0x10, 0xB5, 0x41, 0xF2, + 0xE4, 0x41, 0x82, 0x48, 0x0B, 0xF0, 0x92, 0xF9, 0x40, 0xF2, 0x3C, 0x41, + 0x80, 0x48, 0x0B, 0xF0, + 0x8D, 0xF9, 0x80, 0x48, 0xFF, 0x21, 0x80, 0xF8, 0xF4, 0x13, 0x00, 0xF0, + 0x95, 0xFC, 0xFF, 0xF7, + 0xE3, 0xFA, 0xFF, 0xF7, 0x5A, 0xFF, 0x00, 0xF0, 0xFB, 0xF8, 0xFD, 0xF7, + 0x6A, 0xFB, 0xFE, 0xF7, + 0x8C, 0xF8, 0xBD, 0xE8, 0x10, 0x40, 0xFE, 0xF7, 0x05, 0xBE, 0x70, 0xB5, + 0x76, 0x4D, 0x00, 0x24, + 0x72, 0x48, 0xAC, 0x71, 0x00, 0xF0, 0x86, 0xFC, 0x74, 0x4E, 0x30, 0x68, + 0x90, 0xF8, 0x40, 0x04, + 0xC1, 0x06, 0x04, 0xD5, 0xC0, 0x07, 0x02, 0xD0, 0x6C, 0x48, 0xFF, 0xF7, + 0xCF, 0xF9, 0x30, 0x68, + 0x90, 0xF8, 0x00, 0x04, 0xC0, 0x07, 0x05, 0xD0, 0x68, 0x48, 0xFF, 0xF7, + 0x5E, 0xFF, 0x67, 0x48, + 0x00, 0xF0, 0xD7, 0xF8, 0x65, 0x48, 0xFD, 0xF7, 0x4C, 0xFB, 0x64, 0x48, + 0xFE, 0xF7, 0x73, 0xF8, + 0x63, 0x48, 0x80, 0xF8, 0x38, 0x44, 0x63, 0x48, 0xB0, 0xF8, 0xF8, 0x03, + 0xA5, 0xF8, 0x1F, 0x00, + 0x70, 0xBD, 0x70, 0xB5, 0x5E, 0x4D, 0x60, 0x4E, 0x95, 0xF8, 0x38, 0x04, + 0x88, 0xB3, 0xB0, 0x71, + 0x29, 0x46, 0x5A, 0x48, 0x00, 0xF0, 0x88, 0xFC, 0x5C, 0x4C, 0x20, 0x68, + 0x90, 0xF8, 0x40, 0x04, + 0xC1, 0x06, 0x04, 0xD5, 0xC0, 0x07, 0x02, 0xD0, 0x54, 0x48, 0xFF, 0xF7, + 0x9F, 0xF9, 0x20, 0x68, + 0x90, 0xF8, 0x00, 0x04, 0xC0, 0x07, 0x02, 0xD0, 0x50, 0x48, 0xFF, 0xF7, + 0x2E, 0xFF, 0x20, 0x68, + 0x90, 0xF8, 0x80, 0x03, 0xC0, 0x07, 0x02, 0xD0, 0x4C, 0x48, 0xFF, 0xF7, + 0x27, 0xF9, 0x4B, 0x48, + 0x00, 0xF0, 0x9F, 0xF8, 0x49, 0x48, 0xFD, 0xF7, 0x14, 0xFB, 0x48, 0x48, + 0xFE, 0xF7, 0x3B, 0xF8, + 0x00, 0x20, 0x85, 0xF8, 0x38, 0x04, 0x47, 0x48, 0xB0, 0xF8, 0xF8, 0x03, + 0xA6, 0xF8, 0x1F, 0x00, + 0x70, 0xBD, 0xFF, 0xE7, 0xFF, 0xF7, 0x99, 0xFF, 0xF5, 0xE7, 0x10, 0xB5, + 0x40, 0x4C, 0x01, 0x46, + 0x94, 0xF8, 0x38, 0x04, 0x0F, 0x28, 0x0B, 0xD2, 0x00, 0xEB, 0xC0, 0x00, + 0x04, 0xEB, 0xC0, 0x00, + 0x48, 0x22, 0x0B, 0xF0, 0xA7, 0xF8, 0x94, 0xF8, 0x38, 0x04, 0x40, 0x1C, + 0x84, 0xF8, 0x38, 0x04, + 0x10, 0xBD, 0x10, 0xB5, 0x00, 0xF5, 0x80, 0x50, 0x00, 0x21, 0xD0, 0xF8, + 0xEC, 0x33, 0x01, 0x22, + 0x02, 0xFA, 0x01, 0xF4, 0x23, 0x42, 0x0B, 0xD1, 0x8A, 0x40, 0x13, 0x43, + 0xC0, 0xF8, 0xEC, 0x33, + 0x90, 0xF9, 0xF4, 0x23, 0x8A, 0x42, 0x01, 0xDA, 0x80, 0xF8, 0xF4, 0x13, + 0x48, 0xB2, 0x10, 0xBD, + 0x49, 0x1C, 0x0F, 0x29, 0xEC, 0xDB, 0x4F, 0xF0, 0xFF, 0x30, 0x10, 0xBD, + 0x10, 0xB5, 0x00, 0xF5, + 0x80, 0x50, 0x91, 0xF8, 0x4C, 0x41, 0x01, 0x23, 0xD0, 0xF8, 0xEC, 0x23, + 0xA3, 0x40, 0x9A, 0x43, + 0xC0, 0xF8, 0xEC, 0x23, 0x00, 0x22, 0x81, 0xF8, 0x3E, 0x21, 0x90, 0xF9, + 0xF4, 0x23, 0x91, 0xF9, + 0x4C, 0x11, 0x8A, 0x42, 0x07, 0xD1, 0xD0, 0xF8, 0xEC, 0x13, 0xB1, 0xFA, + 0x81, 0xF1, 0xC1, 0xF1, + 0x1F, 0x01, 0x80, 0xF8, 0xF4, 0x13, 0x10, 0xBD, 0x30, 0xB5, 0x00, 0xF5, + 0x80, 0x50, 0x00, 0x22, + 0xD0, 0xF8, 0xF0, 0x43, 0x01, 0x23, 0x00, 0xBF, 0x03, 0xFA, 0x02, 0xF5, + 0x2C, 0x42, 0x06, 0xD1, + 0x93, 0x40, 0x1C, 0x43, 0xC0, 0xF8, 0xF0, 0x43, 0x81, 0xF8, 0x4D, 0x21, + 0x30, 0xBD, 0x52, 0x1C, + 0x52, 0xB2, 0x0A, 0x2A, 0xF0, 0xDB, 0xFF, 0x20, 0x81, 0xF8, 0x4D, 0x01, + 0x30, 0xBD, 0x10, 0xB5, + 0x00, 0xF5, 0x80, 0x50, 0x91, 0xF8, 0x4D, 0x41, 0x01, 0x23, 0xD0, 0xF8, + 0xF0, 0x23, 0xA3, 0x40, + 0x9A, 0x43, 0xC0, 0xF8, 0xF0, 0x23, 0xFF, 0x20, 0x81, 0xF8, 0x4D, 0x01, + 0x10, 0xBD, 0x04, 0x48, + 0x70, 0x47, 0x03, 0x48, 0x90, 0xF8, 0x38, 0x04, 0x70, 0x47, 0x00, 0x00, + 0xCC, 0x2C, 0x10, 0x00, + 0xB0, 0x41, 0x10, 0x00, 0xCC, 0x3C, 0x10, 0x00, 0x0A, 0x44, 0x01, 0x20, + 0x4C, 0x07, 0x10, 0x00, + 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x5F, 0x00, 0x24, 0x06, 0x46, 0x01, 0x27, + 0xDF, 0xF8, 0x0C, 0x81, + 0xDF, 0xF8, 0x0C, 0x91, 0xDF, 0xF8, 0x0C, 0xA1, 0x4F, 0xF0, 0x05, 0x0B, + 0x00, 0xF5, 0x80, 0x55, + 0x76, 0xE0, 0x00, 0xBF, 0xD5, 0xF8, 0xEC, 0x03, 0x07, 0xFA, 0x04, 0xF1, + 0x08, 0x42, 0x6D, 0xD0, + 0x04, 0xEB, 0x04, 0x10, 0x00, 0xEB, 0x80, 0x00, 0x06, 0xEB, 0x80, 0x01, + 0x91, 0xF8, 0x3E, 0x01, + 0x01, 0x28, 0x04, 0xD0, 0x02, 0x28, 0x02, 0xD0, 0x04, 0x28, 0x3B, 0xD0, + 0x5E, 0xE0, 0x91, 0xF8, + 0x3F, 0x01, 0x03, 0x28, 0x06, 0xD0, 0x04, 0x28, 0x04, 0xD0, 0x02, 0x28, + 0x21, 0xD0, 0x01, 0x28, + 0x29, 0xD0, 0x53, 0xE0, 0x30, 0x48, 0xB1, 0xF8, 0x42, 0x20, 0xB0, 0xF9, + 0x00, 0x00, 0x53, 0x07, + 0x01, 0xD5, 0xB8, 0xF9, 0x00, 0x00, 0x13, 0x07, 0x02, 0xD5, 0x2C, 0x48, + 0xB0, 0xF9, 0x00, 0x00, + 0xB1, 0xF9, 0x54, 0x30, 0x83, 0x42, 0x41, 0xDA, 0xD0, 0x05, 0x06, 0xD5, + 0xB9, 0xF9, 0x00, 0x00, + 0x83, 0x42, 0x02, 0xDD, 0x81, 0xF8, 0x3F, 0x71, 0x38, 0xE0, 0x30, 0x46, + 0xFF, 0xF7, 0x56, 0xFF, + 0x34, 0xE0, 0x91, 0xF8, 0x42, 0x20, 0x00, 0x20, 0x52, 0x07, 0x01, 0xD5, + 0xB8, 0xF9, 0x00, 0x00, + 0xB1, 0xF9, 0x54, 0x20, 0x03, 0xE0, 0xB1, 0xF9, 0x54, 0x20, 0xB9, 0xF9, + 0x00, 0x00, 0x82, 0x42, + 0xEB, 0xDB, 0x23, 0xE0, 0x91, 0xF8, 0x40, 0x00, 0x10, 0xF0, 0x06, 0x0F, + 0x1E, 0xD0, 0x91, 0xF8, + 0x42, 0x00, 0x40, 0x07, 0x1A, 0xD4, 0x17, 0x4A, 0x15, 0x48, 0x12, 0x78, + 0xB0, 0xF9, 0x00, 0x00, + 0x01, 0x2A, 0x01, 0xD1, 0xBA, 0xF9, 0x00, 0x00, 0x91, 0xF8, 0x3F, 0x21, + 0x03, 0x2A, 0x02, 0xD0, + 0x02, 0x2A, 0x05, 0xD0, 0x0A, 0xE0, 0xB1, 0xF9, 0x54, 0x20, 0xBA, 0xF9, + 0x00, 0x00, 0x01, 0xE0, + 0xB1, 0xF9, 0x54, 0x20, 0x82, 0x42, 0x01, 0xDA, 0x81, 0xF8, 0x3E, 0xB1, + 0x64, 0x1C, 0x64, 0xB2, + 0x95, 0xF9, 0xF4, 0x03, 0xA0, 0x42, 0x85, 0xDA, 0xBD, 0xE8, 0xF0, 0x9F, + 0xA0, 0x06, 0x10, 0x00, + 0x90, 0x06, 0x10, 0x00, 0x92, 0x06, 0x10, 0x00, 0x8C, 0x06, 0x10, 0x00, + 0xAA, 0x06, 0x10, 0x00, + 0x94, 0x06, 0x10, 0x00, 0xA5, 0x06, 0x10, 0x00, 0x90, 0xF8, 0x40, 0x10, + 0x09, 0x06, 0x14, 0xD5, + 0xB0, 0xF8, 0x42, 0x10, 0x0A, 0x07, 0x0C, 0xD4, 0xFE, 0x4A, 0x12, 0x68, + 0x92, 0xF8, 0x40, 0x34, + 0x5B, 0x06, 0x06, 0xD5, 0x92, 0xF8, 0x80, 0x23, 0x52, 0x06, 0x02, 0xD4, + 0x01, 0x22, 0x80, 0xF8, + 0x70, 0x20, 0x41, 0xF0, 0x08, 0x01, 0xA0, 0xF8, 0x42, 0x10, 0x70, 0x47, + 0xB0, 0xF8, 0x40, 0x10, + 0xC9, 0x05, 0x14, 0xD5, 0xB0, 0xF8, 0x42, 0x10, 0x0A, 0x06, 0x0C, 0xD4, + 0xF1, 0x4A, 0x12, 0x68, + 0x92, 0xF8, 0x20, 0x34, 0x9B, 0x07, 0x06, 0xD5, 0x92, 0xF8, 0x80, 0x23, + 0x52, 0x06, 0x02, 0xD4, + 0x01, 0x22, 0x80, 0xF8, 0x70, 0x20, 0x41, 0xF0, 0x80, 0x01, 0xA0, 0xF8, + 0x42, 0x10, 0x70, 0x47, + 0x2D, 0xE9, 0xF0, 0x47, 0x82, 0x46, 0x92, 0xF8, 0x40, 0x00, 0x15, 0x46, + 0x0C, 0x46, 0x02, 0x28, + 0x01, 0xD1, 0x28, 0x69, 0xE8, 0x60, 0x04, 0xF1, 0x0C, 0x07, 0x4F, 0xF0, + 0x00, 0x08, 0x4F, 0xF0, + 0xFF, 0x39, 0x05, 0xF1, 0x0C, 0x06, 0x63, 0xB3, 0x01, 0x20, 0x84, 0xF8, + 0x3E, 0x01, 0x95, 0xF8, + 0x40, 0x00, 0x84, 0xF8, 0x3F, 0x01, 0x84, 0xF8, 0x4D, 0x91, 0x84, 0xF8, + 0x42, 0x81, 0x84, 0xF8, + 0x43, 0x81, 0xA4, 0xF8, 0x42, 0x80, 0xC4, 0xF8, 0x2C, 0x80, 0x84, 0xF8, + 0x41, 0x81, 0x84, 0xF8, + 0x44, 0x81, 0x95, 0xF8, 0x42, 0x00, 0x84, 0xF8, 0x47, 0x01, 0x68, 0x8E, + 0xA4, 0xF8, 0x56, 0x00, + 0x95, 0xF8, 0x41, 0x00, 0x84, 0xF8, 0x48, 0x01, 0xE8, 0x68, 0x20, 0x62, + 0x03, 0x22, 0x31, 0x46, + 0x20, 0x46, 0x07, 0xF0, 0x1D, 0xF8, 0x03, 0x22, 0x31, 0x46, 0x38, 0x46, + 0x07, 0xF0, 0x18, 0xF8, + 0x14, 0xE0, 0x11, 0x46, 0x20, 0x46, 0x00, 0xF0, 0x5F, 0xFB, 0xE0, 0x6A, + 0x03, 0x22, 0x40, 0x1C, + 0xE0, 0x62, 0x31, 0x46, 0x20, 0x46, 0x06, 0xF0, 0xFF, 0xFF, 0x03, 0x22, + 0x31, 0x46, 0x38, 0x46, + 0x06, 0xF0, 0xFA, 0xFF, 0x94, 0xF8, 0x46, 0x01, 0x84, 0xF8, 0x47, 0x01, + 0x95, 0xF8, 0x40, 0x00, + 0x84, 0xF8, 0x3F, 0x01, 0x95, 0xF8, 0x42, 0x00, 0x84, 0xF8, 0x46, 0x01, + 0xE8, 0x8C, 0xA4, 0xF8, + 0x44, 0x00, 0x28, 0x8D, 0xA4, 0xF8, 0x46, 0x00, 0x68, 0x8D, 0xA4, 0xF8, + 0x48, 0x00, 0xE8, 0x8D, + 0xA4, 0xF8, 0x4A, 0x00, 0x28, 0x8E, 0xA4, 0xF8, 0x4C, 0x00, 0x28, 0x8B, + 0xA4, 0xF8, 0x4E, 0x00, + 0xB5, 0xF9, 0x32, 0x00, 0xA4, 0xF8, 0x54, 0x00, 0xA9, 0x69, 0x61, 0x63, + 0xE9, 0x69, 0xA1, 0x63, + 0x29, 0x6A, 0xE1, 0x63, 0xA9, 0x8E, 0xA4, 0xF8, 0x58, 0x10, 0xA9, 0x8C, + 0xA4, 0xF8, 0x40, 0x10, + 0xD5, 0xF8, 0x36, 0x10, 0xC4, 0xF8, 0x5A, 0x10, 0x69, 0x69, 0xE1, 0x61, + 0x95, 0xF8, 0x43, 0x10, + 0x84, 0xF8, 0x49, 0x11, 0x95, 0xF8, 0x44, 0x10, 0x84, 0xF8, 0x4A, 0x11, + 0xB4, 0xF9, 0x56, 0x10, + 0x81, 0x42, 0x01, 0xDA, 0xA4, 0xF8, 0x56, 0x00, 0xC4, 0xF8, 0x50, 0x81, + 0x84, 0xF8, 0x4B, 0x91, + 0xA8, 0x8C, 0x80, 0x05, 0xB4, 0xF8, 0x42, 0x00, 0x02, 0xD5, 0x40, 0xF4, + 0x80, 0x70, 0x01, 0xE0, + 0x20, 0xF4, 0x80, 0x70, 0xA4, 0xF8, 0x42, 0x00, 0x21, 0x46, 0x50, 0x46, + 0x00, 0xF0, 0xD3, 0xFA, + 0x20, 0x46, 0xFF, 0xF7, 0x21, 0xFF, 0x20, 0x46, 0xBD, 0xE8, 0xF0, 0x47, + 0x36, 0xE7, 0xF0, 0xB5, + 0xB1, 0xF9, 0x02, 0x60, 0xB2, 0xF9, 0x02, 0x50, 0x78, 0x24, 0x06, 0xFB, + 0x05, 0xF7, 0x00, 0x2F, + 0x2A, 0xDD, 0xB1, 0xF9, 0x00, 0x70, 0xB2, 0xF9, 0x00, 0xC0, 0x07, 0xFB, + 0x0C, 0xF7, 0x00, 0x2F, + 0x22, 0xDD, 0x76, 0x43, 0x5D, 0x43, 0x96, 0xFB, 0xF5, 0xF5, 0x2D, 0xB2, + 0x45, 0x80, 0xB1, 0xF9, + 0x00, 0x10, 0xB2, 0xF9, 0x00, 0x20, 0x49, 0x43, 0x5A, 0x43, 0x91, 0xFB, + 0xF2, 0xF1, 0x0A, 0xB2, + 0x02, 0x80, 0x78, 0x2D, 0x00, 0xDD, 0x44, 0x80, 0xB0, 0xF9, 0x02, 0x30, + 0x6F, 0xF0, 0x77, 0x01, + 0x8B, 0x42, 0x00, 0xDA, 0x41, 0x80, 0x78, 0x2A, 0x00, 0xDD, 0x04, 0x80, + 0xB0, 0xF9, 0x00, 0x20, + 0x8A, 0x42, 0x00, 0xDA, 0x01, 0x80, 0xF0, 0xBD, 0x00, 0x21, 0x41, 0x80, + 0xFA, 0xE7, 0x2D, 0xE9, + 0xFF, 0x4F, 0xDF, 0xF8, 0xD4, 0x81, 0x0F, 0x46, 0x72, 0x4E, 0xD8, 0xF8, + 0x08, 0x10, 0x8F, 0xB0, + 0x00, 0x20, 0x08, 0x60, 0x30, 0x68, 0xB0, 0xF8, 0x82, 0x53, 0x0A, 0xF0, + 0x77, 0xFF, 0x01, 0x28, + 0x04, 0xD1, 0x30, 0x68, 0xB0, 0xF8, 0x84, 0x03, 0x00, 0xB1, 0x05, 0x46, + 0x0A, 0xF0, 0x64, 0xFF, + 0x80, 0xB1, 0x30, 0x68, 0x69, 0x49, 0xB0, 0xF8, 0x86, 0x53, 0xB0, 0xF8, + 0x8A, 0x03, 0x09, 0x68, + 0x88, 0x42, 0x07, 0xDA, 0x0A, 0xF0, 0x62, 0xFF, 0x01, 0x28, 0x03, 0xD1, + 0x30, 0x68, 0xB0, 0xF8, + 0x88, 0x03, 0x05, 0x44, 0xDF, 0xF8, 0x88, 0xB1, 0x62, 0x48, 0x63, 0x4B, + 0xBB, 0xF9, 0x00, 0x20, + 0x62, 0x49, 0xDF, 0xF8, 0x8C, 0xA1, 0x12, 0xB1, 0x01, 0x2A, 0x3E, 0xD0, + 0x7B, 0xE0, 0xB0, 0xF9, + 0x00, 0x00, 0xBA, 0xF9, 0x00, 0x20, 0xD1, 0x46, 0x42, 0x43, 0x03, 0xEB, + 0x42, 0x04, 0x42, 0x00, + 0x09, 0x68, 0x20, 0x46, 0x0A, 0xF0, 0xE9, 0xFD, 0xD8, 0xF8, 0x04, 0x20, + 0x05, 0x21, 0x92, 0xFB, + 0xF1, 0xF3, 0x01, 0xFB, 0x13, 0x22, 0x41, 0xF6, 0x5A, 0x61, 0x11, 0x44, + 0x21, 0x80, 0x55, 0x49, + 0x40, 0x46, 0x49, 0x79, 0x61, 0x80, 0x54, 0x49, 0x09, 0x88, 0xA1, 0x80, + 0x53, 0x49, 0x09, 0x88, + 0xE1, 0x80, 0x53, 0x49, 0x09, 0x88, 0x21, 0x81, 0x52, 0x49, 0x09, 0x88, + 0x61, 0x81, 0x52, 0x49, + 0x09, 0x88, 0xA1, 0x81, 0x51, 0x49, 0x09, 0x88, 0xE1, 0x81, 0xB9, 0xF8, + 0x00, 0x10, 0x49, 0x1C, + 0x09, 0xB2, 0xA9, 0xF8, 0x00, 0x10, 0x42, 0x68, 0x52, 0x1C, 0xC8, 0xF8, + 0x04, 0x20, 0x05, 0x29, + 0x41, 0xDB, 0x00, 0x21, 0xA9, 0xF8, 0x00, 0x10, 0x3D, 0xE0, 0xBA, 0xF9, + 0x00, 0x40, 0x0A, 0x2C, + 0x36, 0xDA, 0xB0, 0xF9, 0x00, 0x00, 0xD1, 0x46, 0x00, 0xFB, 0x04, 0xF2, + 0x03, 0xEB, 0x42, 0x04, + 0x42, 0x00, 0x09, 0x68, 0x20, 0x46, 0x0A, 0xF0, 0xA8, 0xFD, 0xD8, 0xF8, + 0x04, 0x20, 0x05, 0x21, + 0x92, 0xFB, 0xF1, 0xF3, 0x01, 0xFB, 0x13, 0x22, 0x41, 0xF6, 0x5A, 0x61, + 0x11, 0x44, 0x21, 0x80, + 0x34, 0x49, 0x40, 0x46, 0x49, 0x79, 0x61, 0x80, 0x33, 0x49, 0x09, 0x88, + 0xA1, 0x80, 0x33, 0x49, + 0x09, 0x88, 0xE1, 0x80, 0x32, 0x49, 0x09, 0x88, 0x21, 0x81, 0x32, 0x49, + 0x09, 0x88, 0x61, 0x81, + 0x31, 0x49, 0x09, 0x88, 0xA1, 0x81, 0x31, 0x49, 0x09, 0x88, 0xE1, 0x81, + 0xB9, 0xF8, 0x00, 0x10, + 0x49, 0x1C, 0xA9, 0xF8, 0x00, 0x10, 0x41, 0x68, 0x49, 0x1C, 0xC8, 0xF8, + 0x04, 0x10, 0x02, 0xE0, + 0x02, 0x20, 0xAB, 0xF8, 0x00, 0x00, 0x6D, 0x43, 0x02, 0x95, 0x30, 0x68, + 0x0F, 0x9E, 0x00, 0x25, + 0x90, 0xF8, 0x8C, 0x03, 0x04, 0x90, 0x06, 0xF5, 0x80, 0x56, 0x96, 0xE0, + 0x4F, 0xF0, 0x01, 0x09, + 0xD6, 0xF8, 0xEC, 0x03, 0x09, 0xFA, 0x05, 0xF1, 0x08, 0x42, 0x7D, 0xD0, + 0x05, 0xEB, 0x05, 0x10, + 0x00, 0xEB, 0x80, 0x01, 0x0F, 0x98, 0x00, 0xEB, 0x81, 0x04, 0x94, 0xF8, + 0x3E, 0x01, 0x07, 0x28, + 0x72, 0xD0, 0x21, 0x46, 0x20, 0x1D, 0x06, 0xF0, 0xBE, 0xFE, 0x00, 0x90, + 0xB0, 0xF5, 0x80, 0x5F, + 0x08, 0xDD, 0x01, 0x46, 0xAD, 0x20, 0x06, 0xF0, 0x69, 0xF9, 0xAB, 0xF8, + 0x00, 0x90, 0x05, 0x20, + 0xAA, 0xF8, 0x00, 0x00, 0x04, 0x99, 0x00, 0x98, 0x48, 0x43, 0x00, 0xFB, + 0x01, 0xF9, 0x02, 0x99, + 0x89, 0x45, 0x1D, 0xE0, 0x4C, 0x07, 0x10, 0x00, 0x1C, 0x06, 0x10, 0x00, + 0x24, 0x07, 0x10, 0x00, + 0x84, 0x07, 0x10, 0x00, 0xFE, 0x06, 0x10, 0x00, 0x20, 0x4D, 0x01, 0x20, + 0x10, 0x07, 0x10, 0x00, + 0x82, 0x07, 0x10, 0x00, 0x0A, 0x44, 0x01, 0x20, 0x86, 0x07, 0x10, 0x00, + 0x88, 0x07, 0x10, 0x00, + 0x8A, 0x07, 0x10, 0x00, 0x8C, 0x07, 0x10, 0x00, 0x8E, 0x07, 0x10, 0x00, + 0x90, 0x07, 0x10, 0x00, + 0x00, 0xDA, 0x89, 0x46, 0x22, 0x46, 0x21, 0x1D, 0x08, 0xA8, 0x06, 0xF0, + 0x7A, 0xFE, 0x22, 0x1D, + 0x11, 0x1D, 0x0A, 0xA8, 0x06, 0xF0, 0x75, 0xFE, 0xBD, 0xF9, 0x20, 0x00, + 0x64, 0x30, 0xC8, 0x28, + 0x04, 0xD8, 0xBD, 0xF9, 0x22, 0x00, 0x64, 0x30, 0xC8, 0x28, 0x01, 0xD9, + 0x01, 0x23, 0x00, 0xE0, + 0x03, 0x23, 0x0A, 0xAA, 0x08, 0xA9, 0x09, 0xA8, 0xFF, 0xF7, 0xA1, 0xFE, + 0x60, 0x88, 0xBD, 0xF8, + 0x26, 0x10, 0x08, 0x44, 0xAD, 0xF8, 0x2E, 0x00, 0x20, 0x88, 0xBD, 0xF8, + 0x24, 0x10, 0x00, 0x24, + 0x08, 0x44, 0xAD, 0xF8, 0x2C, 0x00, 0x1A, 0xE0, 0x04, 0xEB, 0xC4, 0x00, + 0x07, 0xEB, 0xC0, 0x01, + 0x0B, 0xA8, 0x0C, 0x31, 0x06, 0xF0, 0x57, 0xFE, 0x48, 0x45, 0x0E, 0xDA, + 0xD8, 0xF8, 0x08, 0x10, + 0x0A, 0x68, 0x02, 0xEB, 0x42, 0x02, 0x00, 0xE0, 0x0D, 0xE0, 0x01, 0xEB, + 0x42, 0x02, 0x50, 0x60, + 0x54, 0x72, 0x15, 0x72, 0x08, 0x68, 0x40, 0x1C, 0x08, 0x60, 0x64, 0x1C, + 0xE4, 0xB2, 0x97, 0xF8, + 0x38, 0x04, 0xA0, 0x42, 0xE0, 0xD8, 0x6D, 0x1C, 0x6D, 0xB2, 0x96, 0xF9, + 0xF4, 0x03, 0xA8, 0x42, + 0xBF, 0xF6, 0x64, 0xAF, 0xD8, 0xF8, 0x08, 0x00, 0x45, 0x46, 0x01, 0x68, + 0x00, 0x29, 0x78, 0xD0, + 0x00, 0xF0, 0xC2, 0xF9, 0x00, 0x26, 0x4F, 0xF0, 0x04, 0x09, 0xDF, 0xF8, + 0xC4, 0xA3, 0x4F, 0xF0, + 0x06, 0x0B, 0x6A, 0xE0, 0x06, 0xEB, 0x46, 0x01, 0x00, 0xEB, 0x41, 0x00, + 0x90, 0xF9, 0x08, 0x40, + 0x90, 0xF9, 0x09, 0x30, 0x11, 0x98, 0x02, 0x68, 0x01, 0x20, 0x00, 0xFA, + 0x04, 0xF1, 0x0A, 0x42, + 0x5A, 0xD1, 0xDD, 0xF8, 0x48, 0xC0, 0x00, 0xFA, 0x03, 0xF8, 0xDC, 0xF8, + 0x00, 0xC0, 0x1C, 0xEA, + 0x08, 0x0F, 0x51, 0xD1, 0x11, 0x98, 0x0A, 0x43, 0x02, 0x60, 0x12, 0x98, + 0x12, 0x99, 0x00, 0x68, + 0x40, 0xEA, 0x08, 0x00, 0x08, 0x60, 0x04, 0xEB, 0x04, 0x10, 0x00, 0xEB, + 0x80, 0x01, 0x0F, 0x98, + 0x00, 0xEB, 0x81, 0x04, 0x03, 0xEB, 0xC3, 0x00, 0x07, 0xEB, 0xC0, 0x02, + 0x00, 0x23, 0x21, 0x46, + 0x0F, 0x98, 0xFF, 0xF7, 0x7D, 0xFD, 0x94, 0xF8, 0x3E, 0x01, 0x03, 0x28, + 0x04, 0xD0, 0x04, 0x28, + 0x04, 0xD0, 0x06, 0x28, 0x08, 0xD0, 0x2F, 0xE0, 0x01, 0x20, 0x00, 0xE0, + 0x00, 0x20, 0x84, 0xF8, + 0x41, 0x01, 0x84, 0xF8, 0x3E, 0x91, 0x27, 0xE0, 0x84, 0xF8, 0x3E, 0x91, + 0x94, 0xF8, 0x40, 0x00, + 0x10, 0xF0, 0x06, 0x0F, 0x20, 0xD0, 0x94, 0xF8, 0x42, 0x00, 0x40, 0x07, + 0x1C, 0xD4, 0xCA, 0x49, + 0xC8, 0x48, 0x09, 0x78, 0xB0, 0xF9, 0x00, 0x00, 0x01, 0x29, 0x01, 0xD1, + 0xBA, 0xF9, 0x00, 0x00, + 0x94, 0xF8, 0x3F, 0x11, 0x03, 0x29, 0x02, 0xD0, 0x02, 0x29, 0x09, 0xD0, + 0x0C, 0xE0, 0xB4, 0xF9, + 0x54, 0x10, 0xBA, 0xF9, 0x00, 0x00, 0x05, 0xE0, 0x84, 0xF8, 0x3E, 0xB1, + 0x04, 0xE0, 0x08, 0xE0, + 0xB4, 0xF9, 0x54, 0x10, 0x81, 0x42, 0xF7, 0xDB, 0x76, 0x1C, 0xA8, 0x68, + 0x01, 0x68, 0xB1, 0x42, + 0x90, 0xDC, 0x13, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, 0xB8, 0x48, 0x00, 0x21, + 0x01, 0x70, 0x81, 0x60, + 0xC1, 0x60, 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x41, 0x05, 0x46, 0x41, 0xF2, + 0x38, 0x40, 0x2E, 0x21, + 0x28, 0x44, 0x0A, 0xF0, 0xE3, 0xFC, 0x00, 0x24, 0x05, 0xF5, 0x80, 0x56, + 0x01, 0x27, 0x4F, 0xF0, + 0x05, 0x08, 0x1B, 0xE0, 0xD6, 0xF8, 0xEC, 0x03, 0x07, 0xFA, 0x04, 0xF1, + 0x08, 0x42, 0x14, 0xD0, + 0x04, 0xEB, 0x04, 0x10, 0x00, 0xEB, 0x80, 0x00, 0x05, 0xEB, 0x80, 0x01, + 0x91, 0xF8, 0x3E, 0x01, + 0x03, 0x28, 0x04, 0xD0, 0x04, 0x28, 0x02, 0xD0, 0x02, 0x28, 0x03, 0xD0, + 0x05, 0xE0, 0x81, 0xF8, + 0x3E, 0x81, 0x02, 0xE0, 0x28, 0x46, 0xFF, 0xF7, 0xE1, 0xFB, 0x64, 0x1C, + 0x96, 0xF9, 0xF4, 0x03, + 0xA0, 0x42, 0xDF, 0xDA, 0xBD, 0xE8, 0xF0, 0x81, 0x2D, 0xE9, 0xFC, 0x47, + 0x04, 0x46, 0x00, 0x25, + 0x9A, 0x48, 0x0E, 0x46, 0x00, 0x95, 0x01, 0x21, 0x08, 0x30, 0x01, 0x95, + 0x04, 0xF0, 0x87, 0xF8, + 0x96, 0x48, 0x05, 0x21, 0x0C, 0x30, 0x04, 0xF0, 0x82, 0xF8, 0x41, 0xF2, + 0x38, 0x40, 0x2E, 0x21, + 0x20, 0x44, 0x0A, 0xF0, 0xA3, 0xFC, 0x91, 0x4F, 0xFF, 0x22, 0x2D, 0x21, + 0xF8, 0x68, 0x0A, 0xF0, + 0x73, 0xFC, 0x3D, 0x70, 0x04, 0xF5, 0x80, 0x57, 0x4F, 0xF0, 0x01, 0x08, + 0xD7, 0xF8, 0xEC, 0x03, + 0x58, 0xB3, 0x6B, 0x46, 0x01, 0xAA, 0x31, 0x46, 0x20, 0x46, 0xFF, 0xF7, + 0xC0, 0xFD, 0x4F, 0xF0, + 0x05, 0x09, 0x1E, 0xE0, 0xD7, 0xF8, 0xEC, 0x13, 0x08, 0xFA, 0x05, 0xF0, + 0x01, 0x42, 0x17, 0xD0, + 0x01, 0x99, 0x08, 0x42, 0x14, 0xD1, 0x05, 0xEB, 0x05, 0x10, 0x00, 0xEB, + 0x80, 0x00, 0x04, 0xEB, + 0x80, 0x01, 0x91, 0xF8, 0x3E, 0x01, 0x03, 0x28, 0x04, 0xD0, 0x04, 0x28, + 0x02, 0xD0, 0x02, 0x28, + 0x03, 0xD0, 0x05, 0xE0, 0x81, 0xF8, 0x3E, 0x91, 0x02, 0xE0, 0x20, 0x46, + 0xFF, 0xF7, 0x8E, 0xFB, + 0x6D, 0x1C, 0x97, 0xF9, 0xF4, 0x03, 0xA8, 0x42, 0xDC, 0xDA, 0x00, 0x25, + 0x47, 0x46, 0x1A, 0xE0, + 0x00, 0x99, 0x07, 0xFA, 0x05, 0xF0, 0x08, 0x42, 0x14, 0xD1, 0x20, 0x46, + 0xFF, 0xF7, 0x61, 0xFB, + 0x00, 0x28, 0x0F, 0xDB, 0x00, 0xEB, 0x00, 0x11, 0x01, 0xEB, 0x81, 0x01, + 0x04, 0xEB, 0x81, 0x01, + 0x05, 0xEB, 0xC5, 0x02, 0x81, 0xF8, 0x4C, 0x01, 0x06, 0xEB, 0xC2, 0x02, + 0x01, 0x23, 0x20, 0x46, + 0xFF, 0xF7, 0x96, 0xFC, 0x6D, 0x1C, 0x96, 0xF8, 0x38, 0x04, 0xA8, 0x42, + 0xE0, 0xD8, 0x63, 0x48, + 0x01, 0x21, 0x08, 0x30, 0x04, 0xF0, 0x38, 0xF8, 0x60, 0x48, 0x05, 0x21, + 0x0C, 0x30, 0x04, 0xF0, + 0x33, 0xF8, 0xBD, 0xE8, 0xFC, 0x87, 0x30, 0xB5, 0x5C, 0x4A, 0x91, 0xF8, + 0x46, 0x51, 0xD4, 0x68, + 0x63, 0x5D, 0xFF, 0x2B, 0x05, 0xD1, 0x13, 0x78, 0x63, 0x55, 0x0F, 0x2B, + 0x01, 0xD2, 0x5B, 0x1C, + 0x13, 0x70, 0x91, 0xF8, 0x46, 0x21, 0xA2, 0x5C, 0x81, 0xF8, 0x46, 0x21, + 0x00, 0xEB, 0x42, 0x02, + 0x02, 0xF5, 0x80, 0x52, 0x91, 0xF8, 0x4C, 0x51, 0xB2, 0xF8, 0x38, 0x34, + 0x01, 0x24, 0xAC, 0x40, + 0x23, 0x43, 0xA2, 0xF8, 0x38, 0x34, 0x91, 0xF8, 0x46, 0x11, 0x41, 0xF2, + 0x56, 0x42, 0x10, 0x44, + 0x0A, 0x5C, 0x52, 0x1C, 0x0A, 0x54, 0x30, 0xBD, 0x2D, 0xE9, 0xF0, 0x41, + 0x04, 0x46, 0x91, 0xF8, + 0x40, 0x00, 0x00, 0x26, 0x0D, 0x46, 0x01, 0x28, 0x01, 0xD1, 0x84, 0xF8, + 0x44, 0x61, 0x94, 0xF8, + 0x3F, 0x01, 0x43, 0x4F, 0xDF, 0xF8, 0x0C, 0x81, 0x01, 0x28, 0x1F, 0xD1, + 0x95, 0xF8, 0x40, 0x00, + 0x03, 0x28, 0x4D, 0xD1, 0x94, 0xF8, 0x3E, 0x01, 0x02, 0x28, 0x04, 0xD0, + 0x03, 0x28, 0x06, 0xD0, + 0x04, 0x28, 0x04, 0xD0, 0x1E, 0xE0, 0x20, 0x46, 0xFC, 0xF7, 0xC0, 0xFD, + 0x0E, 0xE0, 0xD8, 0xF8, + 0x00, 0x10, 0x94, 0xF8, 0x44, 0x01, 0x91, 0xF8, 0x4B, 0x13, 0x01, 0xF0, + 0x0F, 0x01, 0x88, 0x42, + 0x02, 0xD8, 0x39, 0x78, 0x01, 0x29, 0x06, 0xD0, 0x84, 0xF8, 0x44, 0x61, + 0x95, 0xF8, 0x40, 0x00, + 0x03, 0x28, 0x07, 0xD0, 0x08, 0xE0, 0x40, 0x1C, 0x84, 0xF8, 0x44, 0x01, + 0x01, 0x20, 0x85, 0xF8, + 0x40, 0x00, 0x01, 0xE0, 0x84, 0xF8, 0x45, 0x61, 0x94, 0xF8, 0x3F, 0x01, + 0x03, 0x28, 0x1F, 0xD1, + 0x95, 0xF8, 0x40, 0x00, 0x01, 0x28, 0x1B, 0xD1, 0x94, 0xF8, 0x3E, 0x01, + 0x04, 0x28, 0x17, 0xD1, + 0x38, 0x78, 0xB0, 0xB9, 0xD8, 0xF8, 0x00, 0x00, 0xB4, 0xF9, 0x56, 0x10, + 0xB0, 0xF8, 0x52, 0x23, + 0x91, 0x42, 0x0E, 0xDA, 0x94, 0xF8, 0x45, 0x11, 0x90, 0xF8, 0x54, 0x03, + 0x81, 0x42, 0x07, 0xD2, + 0x03, 0x20, 0x85, 0xF8, 0x40, 0x00, 0x94, 0xF8, 0x45, 0x01, 0x40, 0x1C, + 0x84, 0xF8, 0x45, 0x01, + 0xF0, 0xE6, 0x84, 0xF8, 0x45, 0x61, 0xED, 0xE6, 0x7C, 0xB5, 0x00, 0x21, + 0x1C, 0xE0, 0x01, 0xEB, + 0x41, 0x03, 0x4A, 0x1C, 0x00, 0xEB, 0x43, 0x03, 0x12, 0xE0, 0x02, 0xEB, + 0x42, 0x04, 0x00, 0xEB, + 0x44, 0x04, 0x5E, 0x68, 0x65, 0x68, 0xAE, 0x42, 0x09, 0xDD, 0x00, 0x96, + 0x1E, 0x89, 0xAD, 0xF8, + 0x04, 0x60, 0x5D, 0x60, 0x25, 0x89, 0x1D, 0x81, 0x00, 0x9D, 0x65, 0x60, + 0x26, 0x81, 0x52, 0x1C, + 0x04, 0x68, 0x94, 0x42, 0xE9, 0xDC, 0x49, 0x1C, 0x02, 0x68, 0x8A, 0x42, + 0xDF, 0xDC, 0x7C, 0xBD, + 0x92, 0x06, 0x10, 0x00, 0x94, 0x06, 0x10, 0x00, 0xA5, 0x06, 0x10, 0x00, + 0x1C, 0x06, 0x10, 0x00, + 0xF9, 0x06, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, 0x2D, 0xE9, 0xF8, 0x4F, + 0x00, 0x22, 0x91, 0x46, + 0x15, 0x46, 0x14, 0x46, 0x00, 0x92, 0xFE, 0x4A, 0xFE, 0x4B, 0x12, 0x68, + 0xB3, 0xF9, 0x00, 0xC0, + 0xFD, 0x4B, 0x17, 0x8E, 0x1E, 0x68, 0x67, 0x45, 0x60, 0xD1, 0x86, 0x46, + 0xFB, 0x48, 0x02, 0xF1, + 0x32, 0x0A, 0x8B, 0x46, 0x90, 0xF8, 0x00, 0xC0, 0xF9, 0x48, 0x07, 0x78, + 0x4D, 0xE0, 0x3A, 0x46, + 0x43, 0xE0, 0x00, 0xBF, 0x3A, 0xF8, 0x12, 0x10, 0x3E, 0xF8, 0x12, 0x00, + 0x3B, 0xF8, 0x12, 0x30, + 0x08, 0x1A, 0xC9, 0x1A, 0x00, 0xB2, 0x0B, 0xB2, 0x00, 0x28, 0x01, 0xDB, + 0x01, 0x46, 0x00, 0xE0, + 0x41, 0x42, 0x49, 0x45, 0x06, 0xDD, 0x00, 0x28, 0x01, 0xDB, 0x01, 0x46, + 0x00, 0xE0, 0x41, 0x42, + 0x0F, 0xFA, 0x81, 0xF9, 0x19, 0x1A, 0x01, 0xD4, 0x88, 0x46, 0x01, 0xE0, + 0xC1, 0xF1, 0x00, 0x08, + 0xA8, 0x45, 0x05, 0xDD, 0x00, 0x29, 0x01, 0xDB, 0x0D, 0x46, 0x00, 0xE0, + 0x4D, 0x42, 0x2D, 0xB2, + 0x00, 0x29, 0x00, 0xDA, 0x49, 0x42, 0xB6, 0xF8, 0xE2, 0x83, 0x41, 0x45, + 0x15, 0xDD, 0x00, 0x2B, + 0x00, 0xDA, 0x5B, 0x42, 0x01, 0x1E, 0x00, 0xDA, 0x41, 0x42, 0x8B, 0x42, + 0x02, 0xDD, 0x64, 0x1C, + 0xA4, 0xB2, 0x0A, 0xE0, 0x00, 0x28, 0x00, 0xDA, 0x40, 0x42, 0xB6, 0xF8, + 0xE4, 0x13, 0x88, 0x42, + 0x03, 0xDD, 0x4F, 0xF0, 0x00, 0x0C, 0x64, 0x46, 0x01, 0xE0, 0x52, 0x1E, + 0xBA, 0xD1, 0x0A, 0xEB, + 0x47, 0x0A, 0x0E, 0xEB, 0x47, 0x0E, 0x0B, 0xEB, 0x47, 0x0B, 0xBC, 0xF1, + 0x01, 0x0C, 0xAE, 0xD2, + 0x96, 0xF8, 0xE6, 0x03, 0xA0, 0x42, 0x01, 0xD2, 0x01, 0x20, 0x00, 0x90, + 0x96, 0xF8, 0xE7, 0x13, + 0xCC, 0x48, 0x00, 0x9A, 0x06, 0xF0, 0x71, 0xFB, 0xCB, 0x49, 0x21, 0xF8, + 0x25, 0x5F, 0x21, 0xF8, + 0x02, 0x9C, 0x8C, 0x70, 0xBD, 0xE8, 0xF8, 0x8F, 0x10, 0xB5, 0x0C, 0x46, + 0xC3, 0x49, 0x09, 0x78, + 0x4A, 0x00, 0x01, 0x46, 0xC5, 0x48, 0x0A, 0xF0, 0x50, 0xFA, 0xC1, 0x48, + 0x21, 0x46, 0x00, 0x78, + 0x42, 0x00, 0xC3, 0x48, 0x0A, 0xF0, 0x49, 0xFA, 0xC2, 0x49, 0x01, 0x20, + 0x08, 0x70, 0x10, 0xBD, + 0xB8, 0x49, 0x10, 0xB5, 0xB1, 0xF9, 0x00, 0x10, 0x4A, 0x00, 0x01, 0x46, + 0xBE, 0x48, 0x00, 0x68, + 0x0A, 0xF0, 0x3B, 0xFA, 0xBD, 0x49, 0x01, 0x20, 0x08, 0x70, 0x10, 0xBD, + 0x10, 0xB5, 0x01, 0x23, + 0x0A, 0xE0, 0x00, 0xBF, 0x30, 0xF9, 0x13, 0x40, 0x00, 0x2C, 0x00, 0xDA, + 0x64, 0x42, 0x8C, 0x42, + 0x01, 0xDD, 0x01, 0x20, 0x10, 0xBD, 0x5B, 0x1C, 0x93, 0x42, 0xF3, 0xDB, + 0x00, 0x20, 0x10, 0xBD, + 0x2D, 0xE9, 0xFF, 0x5F, 0xDF, 0xF8, 0xAC, 0xB2, 0x00, 0x24, 0xAB, 0xF1, + 0x01, 0x0B, 0xDF, 0xF8, + 0x98, 0x92, 0xCB, 0xF8, 0x04, 0x40, 0x8B, 0xF8, 0x00, 0x40, 0xD9, 0xF8, + 0x00, 0x00, 0x1F, 0x46, + 0x26, 0x46, 0x90, 0xF8, 0xE0, 0x03, 0x25, 0x46, 0xA0, 0x46, 0xC0, 0x07, + 0x0E, 0xD0, 0x03, 0x20, + 0xF3, 0xF7, 0xF7, 0xFC, 0xA4, 0x48, 0x01, 0x68, 0x00, 0x98, 0xFF, 0xF7, + 0x2D, 0xFF, 0x04, 0x46, + 0x11, 0x25, 0x03, 0x20, 0xF3, 0xF7, 0x08, 0xFD, 0x00, 0x2C, 0x63, 0xD1, + 0xA0, 0x48, 0x00, 0x78, + 0x10, 0xF0, 0x30, 0x0F, 0x5E, 0xD0, 0xFA, 0xF7, 0x21, 0xFD, 0xD9, 0xF8, + 0x00, 0x00, 0xDF, 0xF8, + 0x74, 0xA2, 0x90, 0xF8, 0xE0, 0x03, 0x80, 0x07, 0x0B, 0xD5, 0x9B, 0x4A, + 0x9B, 0x49, 0xDA, 0xF8, + 0x00, 0x00, 0x00, 0xF0, 0xFB, 0xF8, 0x04, 0x00, 0x4F, 0xF0, 0x30, 0x05, + 0x4F, 0xF0, 0x01, 0x06, + 0x48, 0xD1, 0xD9, 0xF8, 0x00, 0x00, 0x90, 0xF8, 0xE1, 0x03, 0xC0, 0x07, + 0x0B, 0xD0, 0x94, 0x4A, + 0x94, 0x49, 0xDA, 0xF8, 0x00, 0x00, 0x00, 0xF0, 0x7B, 0xF8, 0x04, 0x00, + 0x4F, 0xF0, 0x30, 0x05, + 0x4F, 0xF0, 0x01, 0x06, 0x36, 0xD1, 0xD9, 0xF8, 0x00, 0x00, 0x90, 0xF8, + 0xE1, 0x03, 0x80, 0x07, + 0x0B, 0xD5, 0x8B, 0x4A, 0x8B, 0x49, 0xDA, 0xF8, 0x00, 0x00, 0x00, 0xF0, + 0xA4, 0xF8, 0x04, 0x00, + 0x4F, 0xF0, 0x31, 0x05, 0x4F, 0xF0, 0x01, 0x06, 0x24, 0xD1, 0x87, 0x48, + 0x00, 0x78, 0x01, 0x28, + 0x20, 0xD1, 0x86, 0x48, 0x01, 0x7E, 0x86, 0x48, 0x00, 0x7E, 0x01, 0xFB, + 0x00, 0xF2, 0x19, 0x2A, + 0x03, 0xD2, 0x05, 0x29, 0x01, 0xD8, 0x05, 0x28, 0x02, 0xD9, 0x32, 0x25, + 0x01, 0x26, 0x11, 0xE0, + 0xD9, 0xF8, 0x00, 0x00, 0x90, 0xF8, 0xE1, 0x03, 0x40, 0x07, 0x0B, 0xD5, + 0x03, 0x20, 0xF3, 0xF7, + 0x90, 0xFC, 0x71, 0x48, 0x7B, 0x4A, 0x00, 0x99, 0x00, 0x68, 0xFA, 0xF7, + 0x7E, 0xFE, 0x03, 0x20, + 0xF3, 0xF7, 0xA2, 0xFC, 0x0A, 0xF0, 0x00, 0xFB, 0x01, 0x46, 0x77, 0x48, + 0x01, 0x70, 0x54, 0xB3, + 0x6E, 0xB1, 0x87, 0xF8, 0x00, 0x80, 0x29, 0x46, 0x05, 0x20, 0x05, 0xF0, + 0x9C, 0xFD, 0x00, 0x98, + 0xFF, 0xF7, 0x46, 0xFF, 0xDD, 0xE9, 0x01, 0x01, 0xFF, 0xF7, 0x2E, 0xFF, + 0x17, 0xE0, 0x01, 0x20, + 0x38, 0x70, 0x8B, 0xF8, 0x00, 0x50, 0xDB, 0xF8, 0x04, 0x10, 0x41, 0xF0, + 0x02, 0x01, 0xCB, 0xF8, + 0x04, 0x10, 0xD9, 0xF8, 0x00, 0x20, 0x92, 0xF8, 0x50, 0x32, 0xDB, 0x07, + 0x03, 0xD1, 0x92, 0xF8, + 0x80, 0x22, 0xD2, 0x07, 0x03, 0xD0, 0x41, 0xF0, 0x08, 0x01, 0xCB, 0xF8, + 0x04, 0x10, 0x01, 0x20, + 0x04, 0xB0, 0xBD, 0xE8, 0xF0, 0x9F, 0x87, 0xF8, 0x00, 0x80, 0x00, 0x78, + 0xF8, 0xE7, 0x4D, 0x48, + 0x00, 0x21, 0x40, 0x1E, 0x41, 0x60, 0x01, 0x70, 0xA0, 0xF8, 0x01, 0x10, + 0xC1, 0x70, 0x70, 0x47, + 0x2D, 0xE9, 0xF7, 0x4F, 0xDF, 0xF8, 0x10, 0x81, 0x93, 0x46, 0x0B, 0x46, + 0xD8, 0xF8, 0x00, 0x00, + 0x00, 0x25, 0x2C, 0x46, 0x90, 0xF8, 0xEC, 0x73, 0xB0, 0xF9, 0xE8, 0x63, + 0xB0, 0xF9, 0xEA, 0xA3, + 0x3E, 0x48, 0x31, 0x46, 0x02, 0x78, 0x18, 0x46, 0xFB, 0xF7, 0x21, 0xFF, + 0x81, 0x46, 0x3C, 0x48, + 0x31, 0x46, 0x02, 0x78, 0x58, 0x46, 0xFB, 0xF7, 0x1A, 0xFF, 0x01, 0x46, + 0xB9, 0xF1, 0x00, 0x0F, + 0x00, 0xD0, 0x51, 0xB9, 0x33, 0x48, 0x51, 0x46, 0xB0, 0xF9, 0x00, 0x20, + 0x00, 0x98, 0x00, 0xF0, + 0xBE, 0xF8, 0x04, 0x46, 0xB8, 0x42, 0x00, 0xD9, 0x01, 0x25, 0xD8, 0xF8, + 0x00, 0x00, 0x2A, 0x46, + 0x90, 0xF8, 0xED, 0x13, 0x2F, 0x48, 0x40, 0x1C, 0x06, 0xF0, 0x37, 0xFA, + 0x2E, 0x49, 0x81, 0xF8, + 0x31, 0x40, 0xBD, 0xE8, 0xFE, 0x8F, 0x2D, 0xE9, 0xF0, 0x47, 0x27, 0x4E, + 0x89, 0x46, 0x92, 0x46, + 0x31, 0x68, 0x24, 0x4A, 0x00, 0x25, 0x91, 0xF8, 0xF2, 0x73, 0xB1, 0xF9, + 0xF0, 0x83, 0x2C, 0x46, + 0xB1, 0xF9, 0xEE, 0x13, 0xB2, 0xF9, 0x00, 0x20, 0xFB, 0xF7, 0xF7, 0xFE, + 0x88, 0xB9, 0x1F, 0x48, + 0x41, 0x46, 0x02, 0x78, 0x48, 0x46, 0x00, 0xF0, 0x92, 0xF8, 0x04, 0x46, + 0x1C, 0x48, 0x41, 0x46, + 0x02, 0x78, 0x50, 0x46, 0x00, 0xF0, 0x8B, 0xF8, 0x20, 0x44, 0xC4, 0xB2, + 0xBC, 0x42, 0x00, 0xD9, + 0x01, 0x25, 0x30, 0x68, 0x2A, 0x46, 0x90, 0xF8, 0xF3, 0x13, 0x16, 0x48, + 0x80, 0x1C, 0x06, 0xF0, + 0x04, 0xFA, 0x15, 0x49, 0x81, 0xF8, 0x32, 0x40, 0xBD, 0xE8, 0xF0, 0x87, + 0x2D, 0xE9, 0xF0, 0x5F, + 0x0D, 0x4E, 0x83, 0x46, 0x0B, 0x46, 0x30, 0x68, 0x20, 0x49, 0x00, 0x25, + 0x90, 0xF8, 0xF5, 0x73, + 0xB0, 0xF9, 0xF6, 0xC3, 0xB0, 0xF9, 0xFA, 0x93, 0x09, 0x78, 0x92, 0x46, + 0x2C, 0x46, 0x09, 0xB1, + 0xB0, 0xF9, 0xF8, 0xC3, 0x1A, 0x48, 0x00, 0x78, 0xB0, 0xB3, 0x1A, 0x48, + 0x00, 0x78, 0x34, 0xE0, + 0x50, 0x07, 0x10, 0x00, 0xFE, 0x06, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0xED, 0x06, 0x10, 0x00, + 0xEE, 0x06, 0x10, 0x00, 0x2D, 0x06, 0x10, 0x00, 0x0A, 0x44, 0x01, 0x20, + 0x7E, 0x4A, 0x01, 0x20, + 0xA8, 0x4A, 0x01, 0x20, 0x7A, 0x07, 0x10, 0x00, 0x1C, 0x07, 0x10, 0x00, + 0x79, 0x07, 0x10, 0x00, + 0x04, 0x07, 0x10, 0x00, 0x10, 0x07, 0x10, 0x00, 0xF2, 0x2A, 0x10, 0x00, + 0xC8, 0x2A, 0x10, 0x00, + 0xBE, 0x0B, 0x01, 0x20, 0x94, 0x0B, 0x01, 0x20, 0x66, 0x05, 0x10, 0x00, + 0x18, 0x2A, 0x10, 0x00, + 0x58, 0x2A, 0x10, 0x00, 0x60, 0x2B, 0x10, 0x00, 0x76, 0x06, 0x10, 0x00, + 0xF9, 0x06, 0x10, 0x00, + 0x74, 0x05, 0x10, 0x00, 0x64, 0x05, 0x10, 0x00, 0x01, 0xE0, 0x90, 0xB1, + 0x1C, 0xE0, 0x1C, 0x48, + 0x61, 0x46, 0x02, 0x78, 0x18, 0x46, 0xFF, 0xF7, 0x61, 0xFE, 0x80, 0x46, + 0x19, 0x48, 0x61, 0x46, + 0x02, 0x78, 0x50, 0x46, 0xFF, 0xF7, 0x5A, 0xFE, 0x01, 0x46, 0xB8, 0xF1, + 0x00, 0x0F, 0x00, 0xD0, + 0x51, 0xB9, 0x15, 0x48, 0x49, 0x46, 0xB0, 0xF9, 0x00, 0x20, 0x58, 0x46, + 0x00, 0xF0, 0x0F, 0xF8, + 0x04, 0x46, 0xB8, 0x42, 0x00, 0xD9, 0x01, 0x25, 0x30, 0x68, 0x2A, 0x46, + 0x90, 0xF8, 0xFC, 0x13, + 0x0E, 0x48, 0x06, 0xF0, 0x8A, 0xF9, 0x0E, 0x49, 0x81, 0xF8, 0x31, 0x40, + 0x09, 0xE7, 0x10, 0xB5, + 0x04, 0x46, 0x00, 0x20, 0x09, 0xE0, 0x00, 0xBF, 0x34, 0xF9, 0x12, 0x30, + 0x00, 0x2B, 0x00, 0xDA, + 0x5B, 0x42, 0x8B, 0x42, 0x01, 0xDD, 0x40, 0x1C, 0xC0, 0xB2, 0x52, 0x1E, + 0xF4, 0xD2, 0x10, 0xBD, + 0xED, 0x06, 0x10, 0x00, 0xEE, 0x06, 0x10, 0x00, 0xFE, 0x06, 0x10, 0x00, + 0x2E, 0x06, 0x10, 0x00, + 0x0A, 0x44, 0x01, 0x20, 0x2D, 0xE9, 0xFF, 0x4F, 0x87, 0xB0, 0x00, 0x20, + 0x81, 0x46, 0x03, 0x90, + 0x02, 0x90, 0x01, 0x90, 0xFF, 0x48, 0x88, 0x46, 0x93, 0x46, 0x00, 0x68, + 0xB0, 0xF8, 0xB0, 0x02, + 0x06, 0x90, 0x89, 0x78, 0x50, 0x68, 0x0A, 0xF0, 0xD1, 0xF9, 0x00, 0x90, + 0x98, 0xF8, 0x02, 0x50, + 0x98, 0xF8, 0x03, 0x00, 0x05, 0x90, 0x32, 0xE0, 0x07, 0x98, 0x98, 0xF8, + 0x00, 0x40, 0x00, 0xEB, + 0xC5, 0x00, 0x04, 0x90, 0x98, 0xF8, 0x01, 0xA0, 0x1F, 0xE0, 0x04, 0x98, + 0x22, 0x46, 0x00, 0x21, + 0xD0, 0xE9, 0x00, 0x67, 0x01, 0x20, 0x09, 0xF0, 0xF9, 0xFF, 0x06, 0x40, + 0x0F, 0x40, 0x3E, 0x43, + 0x12, 0xD0, 0x00, 0x98, 0x06, 0x99, 0x30, 0xF9, 0x14, 0x00, 0x88, 0x42, + 0x0C, 0xDD, 0x02, 0x99, + 0x81, 0x44, 0x04, 0xFB, 0x00, 0x11, 0x02, 0x91, 0x01, 0x99, 0x05, 0xFB, + 0x00, 0x10, 0x01, 0x90, + 0x03, 0x98, 0x40, 0x1C, 0x80, 0xB2, 0x03, 0x90, 0x64, 0x1C, 0xA2, 0x45, + 0xDD, 0xDA, 0xE2, 0x49, + 0x00, 0x98, 0x6D, 0x1C, 0x09, 0x78, 0x00, 0xEB, 0x41, 0x00, 0x00, 0x90, + 0x05, 0x98, 0xA8, 0x42, + 0xCA, 0xDA, 0xDC, 0x48, 0x00, 0x25, 0x04, 0x68, 0x01, 0x98, 0xA1, 0x8E, + 0xC2, 0x17, 0xA1, 0xFB, + 0x00, 0x36, 0x05, 0xFB, 0x00, 0x60, 0x01, 0xFB, 0x02, 0x02, 0x4F, 0xF6, + 0xFF, 0x76, 0xA3, 0xFB, + 0x06, 0x07, 0x02, 0xFB, 0x06, 0x72, 0x03, 0xFB, 0x05, 0x21, 0x94, 0xF8, + 0x31, 0x30, 0x30, 0x34, + 0x5B, 0x1E, 0x89, 0xFB, 0x03, 0x23, 0x09, 0xF0, 0xCC, 0xFF, 0x72, 0x10, + 0x80, 0x18, 0xB0, 0x46, + 0x41, 0xF1, 0x00, 0x01, 0x42, 0x46, 0x00, 0x23, 0x09, 0xF0, 0xC3, 0xFF, + 0x07, 0x46, 0x02, 0x98, + 0x61, 0x88, 0xC2, 0x17, 0xA1, 0xFB, 0x00, 0x36, 0x05, 0xFB, 0x00, 0x60, + 0x01, 0xFB, 0x02, 0x01, + 0x42, 0x46, 0xA3, 0xFB, 0x02, 0x06, 0x01, 0xFB, 0x02, 0x61, 0x03, 0xFB, + 0x05, 0x11, 0x23, 0x78, + 0x5B, 0x1E, 0x89, 0xFB, 0x03, 0x23, 0x09, 0xF0, 0xAC, 0xFF, 0x47, 0xF6, + 0xFF, 0x72, 0x80, 0x18, + 0x41, 0xF1, 0x00, 0x01, 0x42, 0x46, 0x00, 0x23, 0x09, 0xF0, 0xA3, 0xFF, + 0xCB, 0xF8, 0x1C, 0x90, + 0x03, 0x99, 0xAB, 0xF8, 0x28, 0x10, 0x0A, 0x99, 0x48, 0x60, 0x0A, 0x99, + 0x0F, 0x60, 0x0B, 0xB0, + 0xBD, 0xE8, 0xF0, 0x8F, 0x2D, 0xE9, 0xFF, 0x4F, 0x81, 0x46, 0xB2, 0x48, + 0x00, 0x26, 0x8A, 0x46, + 0x00, 0x68, 0x14, 0x46, 0x95, 0xB0, 0x90, 0xF8, 0x21, 0x03, 0x4F, 0xF6, + 0xFF, 0x75, 0x32, 0x46, + 0x31, 0x46, 0x43, 0x07, 0xAA, 0xF1, 0x01, 0x07, 0x4C, 0xD5, 0x00, 0x07, + 0x02, 0xD5, 0x8C, 0xB3, + 0xBC, 0x42, 0x48, 0xDA, 0x68, 0x46, 0xD4, 0x46, 0xA3, 0x46, 0x00, 0x23, + 0x08, 0xE0, 0x00, 0xBF, + 0x39, 0xF8, 0x13, 0x40, 0x00, 0xEB, 0x43, 0x08, 0x5B, 0x1C, 0xA8, 0xF8, + 0x02, 0x40, 0xDB, 0xB2, + 0x63, 0x45, 0xF5, 0xD3, 0x43, 0x88, 0x03, 0x80, 0x00, 0xEB, 0x4C, 0x04, + 0x30, 0xF8, 0x1C, 0x30, + 0x63, 0x80, 0x01, 0x23, 0x10, 0xE0, 0x00, 0xBF, 0x00, 0xEB, 0x43, 0x04, + 0x34, 0xF8, 0x02, 0x8C, + 0x64, 0x88, 0x44, 0x44, 0x30, 0xF8, 0x13, 0x80, 0x04, 0xEB, 0x48, 0x04, + 0x09, 0xEB, 0x43, 0x08, + 0x5B, 0x1C, 0x28, 0xF8, 0x02, 0x4C, 0xDB, 0xB2, 0x63, 0x45, 0xED, 0xD9, + 0x93, 0x4B, 0xD8, 0x46, + 0x00, 0x20, 0x09, 0xE0, 0x1B, 0xE0, 0x00, 0xBF, 0x39, 0xF9, 0x10, 0x40, + 0x9C, 0x42, 0x01, 0xDD, + 0x23, 0x46, 0x80, 0x46, 0x40, 0x1C, 0xC0, 0xB2, 0x60, 0x45, 0xF5, 0xD3, + 0x5F, 0xEA, 0x08, 0x04, + 0x01, 0xD1, 0xBD, 0xF9, 0x00, 0x20, 0xBC, 0x42, 0x04, 0xDB, 0x69, 0x46, + 0x01, 0xEB, 0x4A, 0x00, + 0x30, 0xF9, 0x02, 0x1C, 0x1C, 0xB1, 0x09, 0xEB, 0x44, 0x00, 0x30, 0xF9, + 0x02, 0x2C, 0x39, 0xF9, + 0x14, 0xA0, 0xBC, 0x42, 0x03, 0xDA, 0x09, 0xEB, 0x44, 0x00, 0xB0, 0xF9, + 0x02, 0x10, 0x10, 0x46, + 0x88, 0x46, 0x8A, 0x42, 0x01, 0xDD, 0x80, 0x46, 0x08, 0x46, 0x8A, 0x1A, + 0xA2, 0xFB, 0x05, 0x1C, + 0xD3, 0x17, 0x03, 0xFB, 0x05, 0xC3, 0x02, 0xFB, 0x06, 0x3C, 0x0A, 0xEB, + 0x08, 0x02, 0x40, 0x42, + 0x02, 0xEB, 0x40, 0x02, 0x08, 0x46, 0xD3, 0x17, 0x61, 0x46, 0x09, 0xF0, + 0x12, 0xFF, 0xA4, 0xFB, + 0x05, 0x3C, 0x00, 0x22, 0x02, 0xFB, 0x05, 0xC2, 0x04, 0xFB, 0x06, 0x22, + 0x1B, 0x18, 0x4A, 0x41, + 0x18, 0x99, 0xA3, 0xFB, 0x01, 0x0C, 0xCC, 0x17, 0x02, 0xFB, 0x01, 0xC1, + 0x03, 0xFB, 0x04, 0x11, + 0xFB, 0x17, 0x3A, 0x46, 0x09, 0xF0, 0xFD, 0xFE, 0x04, 0x46, 0x0F, 0x46, + 0x02, 0x22, 0x00, 0x23, + 0x28, 0x46, 0x31, 0x46, 0x09, 0xF0, 0xF5, 0xFE, 0x00, 0x19, 0x79, 0x41, + 0x2A, 0x46, 0x33, 0x46, + 0x09, 0xF0, 0xEF, 0xFE, 0x19, 0xB0, 0x53, 0xE7, 0x2D, 0xE9, 0xFF, 0x4F, + 0x9F, 0xB0, 0x00, 0x20, + 0x1B, 0x90, 0x16, 0x90, 0x1C, 0x90, 0x0C, 0x46, 0x90, 0x46, 0x89, 0x78, + 0x50, 0x68, 0x0A, 0xF0, + 0x8D, 0xF8, 0x13, 0x90, 0x94, 0xF8, 0x02, 0x90, 0x94, 0xF8, 0x03, 0xB0, + 0x2E, 0xE0, 0x1F, 0x98, + 0x25, 0x78, 0x00, 0xEB, 0xC9, 0x00, 0x1D, 0x90, 0x94, 0xF8, 0x01, 0xA0, + 0x1A, 0xE0, 0x1D, 0x98, + 0x2A, 0x46, 0x00, 0x21, 0xD0, 0xE9, 0x00, 0x67, 0x01, 0x20, 0x09, 0xF0, + 0xB7, 0xFE, 0x06, 0x40, + 0x0F, 0x40, 0x3E, 0x43, 0x0C, 0xD0, 0x13, 0x98, 0x1C, 0x99, 0x30, 0xF9, + 0x15, 0x00, 0x88, 0x42, + 0x06, 0xDD, 0x16, 0x99, 0x08, 0x44, 0x16, 0x90, 0x1B, 0x98, 0x40, 0x1C, + 0x80, 0xB2, 0x1B, 0x90, + 0x6D, 0x1C, 0xED, 0xB2, 0xAA, 0x45, 0xE2, 0xD2, 0x43, 0x49, 0x13, 0x98, + 0x09, 0x78, 0x00, 0xEB, + 0x41, 0x00, 0x13, 0x90, 0x09, 0xF1, 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x09, + 0xCB, 0x45, 0xCE, 0xD2, + 0x16, 0x98, 0xC8, 0xF8, 0x1C, 0x00, 0x1B, 0x98, 0xA8, 0xF8, 0x28, 0x00, + 0x98, 0xF8, 0x3E, 0x70, + 0x98, 0xF8, 0x3F, 0x90, 0x4C, 0x21, 0x68, 0x46, 0x09, 0xF0, 0x60, 0xFF, + 0xA1, 0x78, 0xD8, 0xF8, + 0x04, 0x00, 0x0A, 0xF0, 0x43, 0xF8, 0x34, 0x4A, 0xA1, 0x78, 0x6D, 0x46, + 0x12, 0x78, 0x07, 0xE0, + 0x30, 0xF8, 0x17, 0x30, 0x25, 0xF8, 0x11, 0x30, 0x49, 0x1C, 0x00, 0xEB, + 0x42, 0x00, 0xC9, 0xB2, + 0xE3, 0x78, 0x8B, 0x42, 0xF4, 0xD2, 0x2B, 0x4E, 0x4A, 0x46, 0x30, 0x68, + 0xB0, 0xF9, 0x34, 0x30, + 0x90, 0xF8, 0x31, 0x10, 0x28, 0x46, 0xFF, 0xF7, 0xE5, 0xFE, 0x22, 0x99, + 0x08, 0x60, 0x4C, 0x21, + 0x68, 0x46, 0x09, 0xF0, 0x3B, 0xFF, 0x49, 0x46, 0xD8, 0xF8, 0x04, 0x00, + 0x0A, 0xF0, 0x1E, 0xF8, + 0x21, 0x78, 0x05, 0xE0, 0x30, 0xF8, 0x11, 0x20, 0x25, 0xF8, 0x11, 0x20, + 0x49, 0x1C, 0xC9, 0xB2, + 0x62, 0x78, 0x8A, 0x42, 0xF6, 0xD2, 0x30, 0x68, 0x3A, 0x46, 0xB0, 0xF9, + 0x32, 0x30, 0x90, 0xF8, + 0x30, 0x10, 0x68, 0x46, 0xFF, 0xF7, 0xC6, 0xFE, 0x22, 0x99, 0x48, 0x60, + 0x23, 0xB0, 0xBF, 0xE6, + 0x10, 0xB5, 0x14, 0x49, 0x43, 0x68, 0x00, 0x22, 0x09, 0x68, 0x00, 0x2B, + 0x01, 0xDA, 0x42, 0x60, + 0x03, 0xE0, 0x4C, 0x8E, 0xA3, 0x42, 0x00, 0xDD, 0x44, 0x60, 0x03, 0x68, + 0x00, 0x2B, 0x01, 0xDA, + 0x02, 0x60, 0x10, 0xBD, 0x89, 0x8E, 0x8B, 0x42, 0xFB, 0xDD, 0x01, 0x60, + 0x10, 0xBD, 0x2D, 0xE9, + 0xFF, 0x47, 0x14, 0x46, 0x88, 0x46, 0x81, 0x46, 0x00, 0x27, 0x01, 0x26, + 0x6B, 0x46, 0xFF, 0xF7, + 0x01, 0xFE, 0x07, 0x48, 0x03, 0x4D, 0x00, 0x78, 0x78, 0xB1, 0x28, 0x68, + 0x90, 0xF8, 0x07, 0x05, + 0x00, 0x07, 0x07, 0xE0, 0x4C, 0x07, 0x10, 0x00, 0xEE, 0x06, 0x10, 0x00, + 0x00, 0x80, 0xFF, 0xFF, + 0xA4, 0x06, 0x10, 0x00, 0x01, 0xD5, 0x04, 0x27, 0x3E, 0x46, 0x94, 0xF8, + 0x40, 0x00, 0x03, 0x28, + 0x04, 0xD0, 0x02, 0x28, 0x06, 0xD0, 0x01, 0x28, 0x14, 0xD0, 0x1C, 0xE0, + 0x28, 0x68, 0x06, 0xE0, + 0x39, 0x46, 0x15, 0xE0, 0x28, 0x68, 0x90, 0xF8, 0x21, 0x13, 0x89, 0x07, + 0x04, 0xD5, 0x90, 0xF8, + 0x07, 0x05, 0xC0, 0x07, 0xF4, 0xD1, 0x0E, 0xE0, 0x90, 0xF8, 0x07, 0x05, + 0x80, 0x07, 0x0A, 0xD5, + 0x31, 0x46, 0x05, 0xE0, 0x28, 0x68, 0x90, 0xF8, 0x07, 0x05, 0x40, 0x07, + 0x03, 0xD5, 0x02, 0x21, + 0x68, 0x46, 0x01, 0xF0, 0xC1, 0xFD, 0x68, 0x46, 0xFF, 0xF7, 0xA2, 0xFF, + 0xDD, 0xE9, 0x00, 0x01, + 0xCD, 0xE9, 0x02, 0x01, 0x94, 0xF8, 0x40, 0x00, 0x02, 0x28, 0x41, 0xD1, + 0x28, 0x68, 0x90, 0xF8, + 0x21, 0x03, 0x80, 0x07, 0x3C, 0xD5, 0x02, 0xAB, 0x22, 0x46, 0x41, 0x46, + 0x48, 0x46, 0xFF, 0xF7, + 0xFB, 0xFE, 0x28, 0x68, 0x90, 0xF8, 0x07, 0x05, 0x80, 0x07, 0x03, 0xD5, + 0x31, 0x46, 0x02, 0xA8, + 0x01, 0xF0, 0xA2, 0xFD, 0x02, 0xA8, 0xFF, 0xF7, 0x83, 0xFF, 0x29, 0x68, + 0xE3, 0x69, 0x91, 0xF8, + 0x2D, 0x23, 0x02, 0xF0, 0x0F, 0x00, 0x15, 0x09, 0xB1, 0xF8, 0x2E, 0x23, + 0xB1, 0xF8, 0x30, 0x13, + 0x93, 0x42, 0x0D, 0xDD, 0x8B, 0x42, 0x01, 0xDB, 0x28, 0x46, 0x09, 0xE0, + 0x91, 0x42, 0x00, 0xD1, + 0x49, 0x1C, 0x9B, 0x1A, 0x2D, 0x1A, 0x6B, 0x43, 0x89, 0x1A, 0x93, 0xFB, + 0xF1, 0xF1, 0x08, 0x44, + 0x01, 0x9A, 0x03, 0x9B, 0xC0, 0xF1, 0x10, 0x01, 0x42, 0x43, 0x03, 0xFB, + 0x01, 0x22, 0x12, 0x11, + 0x03, 0x92, 0x00, 0x9A, 0x42, 0x43, 0x02, 0x98, 0x00, 0xFB, 0x01, 0x20, + 0x00, 0x11, 0x02, 0x90, + 0x01, 0x98, 0xE0, 0x81, 0x00, 0x98, 0xA0, 0x81, 0x03, 0x98, 0x60, 0x82, + 0x02, 0x98, 0x20, 0x82, + 0x94, 0xF8, 0x24, 0x00, 0xC0, 0x06, 0x01, 0xD4, 0xE0, 0x68, 0x60, 0x61, + 0xBD, 0xE8, 0xFF, 0x87, + 0x2D, 0xE9, 0xF0, 0x47, 0x05, 0x46, 0x90, 0xF8, 0x02, 0x80, 0x0F, 0x46, + 0x88, 0x68, 0x14, 0x46, + 0x01, 0x26, 0x41, 0x46, 0x09, 0xF0, 0x42, 0xFF, 0x81, 0x46, 0xA8, 0xF1, + 0x01, 0x00, 0xC1, 0xB2, + 0xB8, 0x68, 0x09, 0xF0, 0x3B, 0xFF, 0x29, 0x78, 0x09, 0xEB, 0x41, 0x02, + 0x32, 0xF9, 0x02, 0x2C, + 0xA2, 0x42, 0x03, 0xDB, 0x30, 0xF9, 0x11, 0x10, 0xA1, 0x42, 0x00, 0xDA, + 0x00, 0x26, 0x69, 0x78, + 0x09, 0xEB, 0x41, 0x02, 0xB2, 0xF9, 0x02, 0x20, 0xA2, 0x42, 0x03, 0xDB, + 0x30, 0xF9, 0x11, 0x00, + 0xA0, 0x42, 0x00, 0xDA, 0x00, 0x26, 0x95, 0xF8, 0x03, 0x80, 0xB8, 0x68, + 0x41, 0x46, 0x09, 0xF0, + 0x1D, 0xFF, 0x81, 0x46, 0x08, 0xF1, 0x01, 0x00, 0xC1, 0xB2, 0xB8, 0x68, + 0x09, 0xF0, 0x16, 0xFF, + 0x29, 0x78, 0x09, 0xEB, 0x41, 0x02, 0x32, 0xF9, 0x02, 0x2C, 0xA2, 0x42, + 0x03, 0xDB, 0x30, 0xF9, + 0x11, 0x10, 0xA1, 0x42, 0x00, 0xDA, 0x00, 0x26, 0x69, 0x78, 0x09, 0xEB, + 0x41, 0x02, 0xB2, 0xF9, + 0x02, 0x20, 0xA2, 0x42, 0x03, 0xDB, 0x30, 0xF9, 0x11, 0x00, 0xA0, 0x42, + 0x00, 0xDA, 0x00, 0x26, + 0x30, 0x46, 0xBD, 0xE8, 0xF0, 0x87, 0x2D, 0xE9, 0xFF, 0x4F, 0x97, 0xB0, + 0x99, 0x46, 0x92, 0x46, + 0xFE, 0x48, 0xDF, 0xF8, 0xFC, 0x83, 0x01, 0x22, 0x04, 0x68, 0x51, 0x46, + 0xD8, 0xF8, 0x00, 0x00, + 0x01, 0xF0, 0x6E, 0xFA, 0xFB, 0x48, 0x00, 0x68, 0xB0, 0xF9, 0x4B, 0x14, + 0x11, 0x91, 0x90, 0xF8, + 0x4A, 0x04, 0x0A, 0x90, 0xF8, 0x48, 0x9A, 0xF8, 0x02, 0x70, 0x9A, 0xF8, + 0x03, 0x60, 0x00, 0x78, + 0x9A, 0xF8, 0x00, 0xB0, 0x9A, 0xF8, 0x01, 0x50, 0x40, 0x1E, 0x87, 0x42, + 0x01, 0xDA, 0x7F, 0x1C, + 0xFF, 0xB2, 0x0E, 0xB1, 0x76, 0x1E, 0xF6, 0xB2, 0xF0, 0x49, 0x09, 0x78, + 0x49, 0x1E, 0x8B, 0x45, + 0x03, 0xDA, 0x0B, 0xF1, 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x0B, 0x0D, 0xB1, + 0x6D, 0x1E, 0xED, 0xB2, + 0xB8, 0x1E, 0xC1, 0xB2, 0xD9, 0xF8, 0x08, 0x00, 0x09, 0xF0, 0xC0, 0xFE, + 0x0B, 0x90, 0x78, 0x1E, + 0xC1, 0xB2, 0xD9, 0xF8, 0x08, 0x00, 0x09, 0xF0, 0xB9, 0xFE, 0x04, 0x90, + 0x39, 0x46, 0xD9, 0xF8, + 0x08, 0x00, 0x09, 0xF0, 0xB3, 0xFE, 0x01, 0x90, 0x78, 0x1C, 0xC1, 0xB2, + 0xD9, 0xF8, 0x08, 0x00, + 0x09, 0xF0, 0xAC, 0xFE, 0x02, 0x90, 0xB8, 0x1C, 0xC1, 0xB2, 0xD9, 0xF8, + 0x08, 0x00, 0x09, 0xF0, + 0xA5, 0xFE, 0x03, 0x90, 0x39, 0x46, 0x17, 0x98, 0x09, 0xF0, 0x9A, 0xFE, + 0x08, 0x90, 0xD4, 0x48, + 0x39, 0x46, 0x00, 0x68, 0x09, 0xF0, 0xA2, 0xFE, 0x86, 0x46, 0x00, 0x20, + 0x7A, 0xB2, 0xD4, 0x4F, + 0x0C, 0x90, 0xB7, 0xE0, 0x4F, 0xFA, 0x8B, 0xF0, 0x94, 0xE0, 0x00, 0x21, + 0x0E, 0xF8, 0x00, 0x10, + 0x08, 0x99, 0x18, 0x9B, 0x09, 0x5C, 0x99, 0x42, 0x75, 0xD1, 0xDF, 0xF8, + 0x28, 0xC3, 0x01, 0x9B, + 0xDC, 0xF8, 0x00, 0xC0, 0x33, 0xF9, 0x10, 0x30, 0xBC, 0xF8, 0x4D, 0xC4, + 0x63, 0x45, 0x6A, 0xDA, + 0x00, 0x21, 0x14, 0x91, 0x13, 0x91, 0x11, 0x99, 0x19, 0x44, 0x04, 0x9B, + 0x09, 0xB2, 0x33, 0xF9, + 0x10, 0x30, 0x10, 0x93, 0x8B, 0x42, 0x06, 0xDD, 0x02, 0x9B, 0x33, 0xF9, + 0x10, 0x30, 0x8B, 0x42, + 0x01, 0xDD, 0x01, 0x23, 0x13, 0x93, 0x01, 0x9B, 0x03, 0xEB, 0x40, 0x03, + 0x33, 0xF9, 0x02, 0x8C, + 0x88, 0x45, 0x07, 0xDD, 0xB3, 0xF9, 0x02, 0xC0, 0x8C, 0x45, 0x03, 0xDD, + 0x4F, 0xF0, 0x01, 0x0C, + 0xCD, 0xF8, 0x50, 0xC0, 0x01, 0x2A, 0x0F, 0xDD, 0xDD, 0xF8, 0x2C, 0xC0, + 0x3C, 0xF9, 0x10, 0xC0, + 0x8C, 0x45, 0x09, 0xDD, 0xDD, 0xF8, 0x08, 0xC0, 0x3C, 0xF9, 0x10, 0xC0, + 0x8C, 0x45, 0x03, 0xDD, + 0x4F, 0xF0, 0x01, 0x0C, 0xCD, 0xF8, 0x4C, 0xC0, 0xDF, 0xF8, 0xAC, 0xC2, + 0x9C, 0xF8, 0x00, 0xC0, + 0xAC, 0xF1, 0x02, 0x0C, 0x62, 0x45, 0x0D, 0xDA, 0xDD, 0xF8, 0x40, 0xC0, + 0x8C, 0x45, 0x09, 0xDD, + 0xDD, 0xF8, 0x0C, 0xC0, 0x3C, 0xF9, 0x10, 0xC0, 0x8C, 0x45, 0x03, 0xDD, + 0x4F, 0xF0, 0x01, 0x0C, + 0xCD, 0xF8, 0x4C, 0xC0, 0x01, 0x28, 0x0B, 0xDD, 0x33, 0xF9, 0x04, 0xCC, + 0x8C, 0x45, 0x07, 0xDD, + 0xB3, 0xF9, 0x02, 0xC0, 0x8C, 0x45, 0x03, 0xDD, 0x4F, 0xF0, 0x01, 0x0C, + 0xCD, 0xF8, 0x50, 0xC0, + 0xDF, 0xF8, 0x68, 0xC2, 0x9C, 0xF8, 0x00, 0xC0, 0xAC, 0xF1, 0x02, 0x0C, + 0x60, 0x45, 0x05, 0xDA, + 0x88, 0x45, 0x03, 0xDD, 0xB3, 0xF9, 0x04, 0x30, 0x8B, 0x42, 0x12, 0xDC, + 0x14, 0x99, 0x81, 0xB9, + 0x13, 0x99, 0x0D, 0xE0, 0xFF, 0xE7, 0x99, 0xB9, 0x01, 0x99, 0xB7, 0xF9, + 0x00, 0x30, 0x31, 0xF9, + 0x10, 0x10, 0x99, 0x42, 0x0C, 0xDA, 0x8B, 0x49, 0x09, 0x68, 0x91, 0xF8, + 0x41, 0x14, 0xC9, 0x07, + 0x31, 0xB1, 0x01, 0x21, 0x0E, 0xF8, 0x00, 0x10, 0x0C, 0x99, 0x49, 0x1C, + 0x89, 0xB2, 0x0C, 0x91, + 0x40, 0x1C, 0x40, 0xB2, 0xA8, 0x42, 0x7F, 0xF7, 0x68, 0xAF, 0x84, 0x48, + 0x0B, 0x99, 0x0E, 0xF1, + 0x28, 0x0E, 0x00, 0x78, 0x01, 0xEB, 0x40, 0x01, 0x0B, 0x91, 0x04, 0x99, + 0x01, 0xEB, 0x40, 0x01, + 0x04, 0x91, 0x01, 0x99, 0x01, 0xEB, 0x40, 0x01, 0x01, 0x91, 0x02, 0x99, + 0x01, 0xEB, 0x40, 0x01, + 0x02, 0x91, 0x03, 0x99, 0x01, 0xEB, 0x40, 0x00, 0x03, 0x90, 0x08, 0x98, + 0x28, 0x30, 0x52, 0x1C, + 0x52, 0xB2, 0x08, 0x90, 0xB2, 0x42, 0x7F, 0xF7, 0x45, 0xAF, 0x0A, 0x99, + 0x0C, 0x98, 0x88, 0x42, + 0x2A, 0xD9, 0x00, 0x20, 0x84, 0xF8, 0x0E, 0x01, 0xDA, 0xF8, 0x00, 0x00, + 0x20, 0x60, 0x6C, 0x48, + 0x00, 0x23, 0x1A, 0x46, 0x21, 0x46, 0x00, 0x68, 0x01, 0xF0, 0x22, 0xFA, + 0x01, 0x25, 0x17, 0xE0, + 0x04, 0xEB, 0x85, 0x00, 0x06, 0x46, 0x51, 0x46, 0x00, 0xF0, 0x53, 0xF9, + 0x70, 0xB1, 0x04, 0xEB, + 0x45, 0x00, 0xB0, 0xF8, 0xB4, 0x10, 0x0A, 0x98, 0x81, 0x42, 0x07, 0xD9, + 0x30, 0x46, 0xB7, 0xF9, + 0x00, 0x20, 0x49, 0x46, 0xFF, 0xF7, 0x64, 0xFE, 0x01, 0x28, 0x06, 0xD0, + 0x6D, 0x1C, 0xED, 0xB2, + 0x94, 0xF8, 0x0E, 0x01, 0xA8, 0x42, 0xE3, 0xD2, 0x00, 0x20, 0x1B, 0xB0, + 0xBD, 0xE8, 0xF0, 0x8F, + 0x2D, 0xE9, 0xF8, 0x4F, 0x04, 0x46, 0x55, 0x48, 0xDF, 0xF8, 0x54, 0x81, + 0x91, 0x46, 0x8B, 0x46, + 0x05, 0x68, 0x01, 0x22, 0x21, 0x46, 0xD8, 0xF8, 0x00, 0x00, 0x01, 0xF0, + 0x19, 0xF9, 0x00, 0x27, + 0xA6, 0x78, 0x4F, 0xF0, 0x01, 0x0A, 0x1D, 0xE0, 0x31, 0x46, 0xD8, 0xF8, + 0x00, 0x00, 0x09, 0xF0, + 0x95, 0xFD, 0x00, 0x90, 0x31, 0x46, 0xDB, 0xF8, 0x08, 0x00, 0x09, 0xF0, + 0x87, 0xFD, 0x01, 0x46, + 0x20, 0x78, 0x52, 0x46, 0x09, 0xE0, 0x00, 0xBF, 0x31, 0xF9, 0x10, 0xC0, + 0xCC, 0x45, 0x02, 0xDA, + 0x00, 0x9B, 0x01, 0x27, 0x1A, 0x54, 0x40, 0x1C, 0xC0, 0xB2, 0x63, 0x78, + 0x83, 0x42, 0xF3, 0xD2, + 0x76, 0x1C, 0xF6, 0xB2, 0xE0, 0x78, 0xB0, 0x42, 0xDE, 0xD2, 0x01, 0x2F, + 0x2E, 0xD1, 0x00, 0x20, + 0x85, 0xF8, 0x0E, 0x01, 0x20, 0x68, 0x28, 0x60, 0x00, 0x23, 0x1A, 0x46, + 0x29, 0x46, 0xD8, 0xF8, + 0x00, 0x00, 0x01, 0xF0, 0xBD, 0xF9, 0x01, 0x26, 0xDF, 0xF8, 0xD8, 0x80, + 0x1A, 0xE0, 0x00, 0xBF, + 0x05, 0xEB, 0x86, 0x07, 0x21, 0x46, 0x38, 0x46, 0x00, 0xF0, 0xEB, 0xF8, + 0x80, 0xB1, 0x05, 0xEB, + 0x46, 0x00, 0xB0, 0xF8, 0xB4, 0x10, 0xD8, 0xF8, 0x00, 0x00, 0x90, 0xF8, + 0x4A, 0x04, 0x81, 0x42, + 0x06, 0xD9, 0x38, 0x46, 0x4A, 0x46, 0x59, 0x46, 0xFF, 0xF7, 0xFA, 0xFD, + 0x01, 0x28, 0x06, 0xD0, + 0x76, 0x1C, 0xF6, 0xB2, 0x95, 0xF8, 0x0E, 0x01, 0xB0, 0x42, 0xE1, 0xD2, + 0x00, 0x20, 0xBD, 0xE8, + 0xF8, 0x8F, 0x2D, 0xE9, 0xF0, 0x41, 0x23, 0x4D, 0x0F, 0x46, 0x80, 0x46, + 0x29, 0x68, 0x14, 0x46, + 0x24, 0x48, 0x1E, 0x46, 0x9A, 0x69, 0xB1, 0xF8, 0x47, 0x34, 0x00, 0x78, + 0x9A, 0x42, 0x10, 0xDC, + 0xF2, 0x8C, 0x04, 0x2A, 0x0D, 0xD9, 0x62, 0x78, 0x23, 0x78, 0x91, 0xF8, + 0x49, 0x14, 0xD2, 0x1A, + 0x52, 0x1C, 0x8A, 0x42, 0x05, 0xDC, 0xE2, 0x78, 0xA3, 0x78, 0xD2, 0x1A, + 0x52, 0x1C, 0x8A, 0x42, + 0x02, 0xDD, 0x00, 0x20, 0xBD, 0xE8, 0xF0, 0x81, 0x00, 0x28, 0xFB, 0xD1, + 0x14, 0x48, 0x31, 0x46, + 0xB0, 0xF9, 0x00, 0x20, 0x20, 0x46, 0xFF, 0xF7, 0x6B, 0xFF, 0x00, 0x28, + 0xF2, 0xD1, 0x29, 0x68, + 0x91, 0xF8, 0x40, 0x24, 0x92, 0x07, 0xED, 0xD5, 0x91, 0xF8, 0xE0, 0x22, + 0x52, 0x06, 0x09, 0xD5, + 0x0D, 0x4A, 0x52, 0x78, 0x02, 0xB3, 0xB1, 0xF8, 0xF0, 0x12, 0x0C, 0x4A, + 0x49, 0x43, 0x12, 0x68, + 0x8A, 0x42, 0xDF, 0xDC, 0x33, 0x46, 0x22, 0x46, 0x39, 0x46, 0x11, 0xE0, + 0x6C, 0x06, 0x10, 0x00, + 0x54, 0x06, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, 0xED, 0x06, 0x10, 0x00, + 0xEE, 0x06, 0x10, 0x00, + 0xA8, 0x06, 0x10, 0x00, 0x34, 0x06, 0x10, 0x00, 0x74, 0x07, 0x10, 0x00, + 0x24, 0x07, 0x10, 0x00, + 0x40, 0x46, 0xBD, 0xE8, 0xF0, 0x41, 0xE6, 0xE5, 0xB1, 0xF8, 0xEE, 0x12, + 0xDD, 0xE7, 0x2D, 0xE9, + 0xFF, 0x4F, 0x81, 0xB0, 0xDF, 0xF8, 0x04, 0xB1, 0xDD, 0xF8, 0x38, 0x90, + 0x5F, 0xEA, 0x03, 0x08, + 0x15, 0x46, 0x1C, 0xD4, 0x2C, 0x78, 0x0B, 0xEB, 0xC8, 0x0A, 0x15, 0xE0, + 0x08, 0xF0, 0xFF, 0x01, + 0x01, 0x98, 0x09, 0xF0, 0xD3, 0xFC, 0x00, 0x90, 0xDA, 0xE9, 0x00, 0x67, + 0x22, 0x46, 0x01, 0x20, + 0x00, 0x21, 0x09, 0xF0, 0x03, 0xFB, 0x06, 0x40, 0x0F, 0x40, 0x3E, 0x43, + 0x02, 0xD1, 0x00, 0x98, + 0x00, 0x21, 0x01, 0x55, 0x64, 0x1C, 0xE4, 0xB2, 0x68, 0x78, 0xA0, 0x42, + 0xE6, 0xD2, 0xB9, 0xF1, + 0x00, 0x00, 0x1E, 0xDB, 0x02, 0x46, 0xAC, 0x78, 0x01, 0x20, 0x00, 0x21, + 0x09, 0xF0, 0xEE, 0xFA, + 0x06, 0x46, 0x0F, 0x46, 0x4F, 0xF0, 0x00, 0x08, 0x10, 0xE0, 0x21, 0x46, + 0x01, 0x98, 0x09, 0xF0, + 0xAD, 0xFC, 0x0B, 0xEB, 0xC4, 0x01, 0x02, 0x46, 0xD1, 0xE9, 0x00, 0x01, + 0x30, 0x40, 0x39, 0x40, + 0x08, 0x43, 0x01, 0xD1, 0x02, 0xF8, 0x09, 0x80, 0x64, 0x1C, 0xE4, 0xB2, + 0xE8, 0x78, 0xA0, 0x42, + 0xEB, 0xD2, 0x05, 0xB0, 0xEA, 0xE6, 0x70, 0xB5, 0x1E, 0x4B, 0x1D, 0x4A, + 0x8C, 0x69, 0x1B, 0x68, + 0x12, 0x78, 0xB3, 0xF8, 0x47, 0x54, 0xAC, 0x42, 0x10, 0xDC, 0xCC, 0x8C, + 0x04, 0x2C, 0x0D, 0xD9, + 0x44, 0x78, 0x05, 0x78, 0x93, 0xF8, 0x49, 0x34, 0x64, 0x1B, 0x64, 0x1C, + 0x9C, 0x42, 0x05, 0xDC, + 0xC4, 0x78, 0x85, 0x78, 0x64, 0x1B, 0x64, 0x1C, 0x9C, 0x42, 0x01, 0xDD, + 0x00, 0x22, 0x06, 0xE0, + 0x2A, 0xB9, 0x11, 0x4A, 0xB2, 0xF9, 0x00, 0x20, 0xFF, 0xF7, 0xCA, 0xFE, + 0x02, 0x46, 0x10, 0x46, + 0x70, 0xBD, 0x02, 0x78, 0x0B, 0x78, 0x9A, 0x42, 0x0B, 0xD9, 0x42, 0x78, + 0x4B, 0x78, 0x9A, 0x42, + 0x07, 0xD2, 0x82, 0x78, 0x8B, 0x78, 0x9A, 0x42, 0x03, 0xD9, 0xC0, 0x78, + 0xC9, 0x78, 0x88, 0x42, + 0x01, 0xD3, 0x00, 0x20, 0x70, 0x47, 0x01, 0x20, 0x70, 0x47, 0x00, 0x00, + 0x98, 0x46, 0x10, 0x00, + 0x34, 0x06, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, 0x92, 0x06, 0x10, 0x00, + 0x70, 0xB5, 0xFE, 0x4C, + 0x00, 0xEB, 0x43, 0x05, 0x04, 0x9E, 0x00, 0xEB, 0x83, 0x03, 0x86, 0xB1, + 0x8E, 0x69, 0xA6, 0x60, + 0xCE, 0x8C, 0x66, 0x80, 0x09, 0x68, 0x61, 0x61, 0xB5, 0xF8, 0xB4, 0x10, + 0xA1, 0x80, 0x19, 0x68, + 0xE1, 0x60, 0x90, 0xF8, 0x0E, 0x01, 0x20, 0x70, 0x10, 0x68, 0x20, 0x61, + 0x70, 0xBD, 0xA6, 0x68, + 0x8E, 0x61, 0x66, 0x88, 0xCE, 0x84, 0x66, 0x69, 0x0E, 0x60, 0xA1, 0x88, + 0xA5, 0xF8, 0xB4, 0x10, + 0xE1, 0x68, 0x19, 0x60, 0x21, 0x78, 0x80, 0xF8, 0x0E, 0x11, 0x20, 0x69, + 0x10, 0x60, 0x70, 0xBD, + 0x2D, 0xE9, 0xF0, 0x47, 0xE9, 0x4D, 0x81, 0x46, 0xE9, 0x4F, 0x28, 0x68, + 0x1C, 0x46, 0x16, 0x46, + 0x90, 0xF8, 0x30, 0x02, 0x88, 0x46, 0x00, 0x07, 0x07, 0xD5, 0x38, 0x78, + 0x01, 0x28, 0x04, 0xD0, + 0x48, 0x46, 0xF9, 0xF7, 0xAF, 0xF8, 0x00, 0x28, 0x40, 0xD0, 0x28, 0x68, + 0x90, 0xF8, 0x30, 0x02, + 0x00, 0x06, 0x0A, 0xD5, 0x38, 0x78, 0x01, 0x28, 0x07, 0xD0, 0xDE, 0x48, + 0x00, 0x78, 0x20, 0xB1, + 0x30, 0x46, 0xF9, 0xF7, 0xF1, 0xF8, 0x00, 0x28, 0x30, 0xD0, 0xDB, 0x4B, + 0x32, 0x46, 0x41, 0x46, + 0x48, 0x46, 0x00, 0xF0, 0x34, 0xFF, 0x00, 0x28, 0x28, 0xD0, 0x94, 0xF8, + 0x40, 0x00, 0x03, 0x28, + 0x08, 0xD1, 0x29, 0x68, 0xE0, 0x8C, 0x91, 0xF8, 0x07, 0x13, 0x88, 0x42, + 0x02, 0xD9, 0x04, 0x20, + 0x84, 0xF8, 0x40, 0x00, 0x30, 0x68, 0xC4, 0xF8, 0x36, 0x00, 0x04, 0xF1, + 0x44, 0x02, 0x51, 0x1E, + 0x30, 0x46, 0xF8, 0xF7, 0xBE, 0xF9, 0x22, 0x46, 0x31, 0x46, 0xCB, 0x48, + 0x00, 0xF0, 0x7A, 0xFD, + 0x31, 0x46, 0xC9, 0x48, 0x00, 0xF0, 0xAC, 0xFE, 0x22, 0x46, 0x31, 0x46, + 0xC6, 0x48, 0xFF, 0xF7, + 0xE6, 0xFB, 0x20, 0x46, 0xBD, 0xE8, 0xF0, 0x47, 0xFE, 0xF7, 0xD7, 0xB9, + 0xBD, 0xE8, 0xF0, 0x87, + 0x2D, 0xE9, 0xF0, 0x41, 0x0D, 0x46, 0x1F, 0x46, 0x16, 0x46, 0x04, 0x46, + 0x48, 0x21, 0x09, 0xF0, + 0xDD, 0xFA, 0x00, 0x20, 0x84, 0xF8, 0x40, 0x00, 0xBC, 0x48, 0x00, 0x68, + 0x60, 0x60, 0xA0, 0x60, + 0x06, 0xEB, 0x45, 0x00, 0x84, 0xF8, 0x42, 0x50, 0x30, 0xF8, 0xB4, 0x1F, + 0x61, 0x85, 0x00, 0x88, + 0xE0, 0x84, 0x06, 0xEB, 0x85, 0x00, 0x00, 0x68, 0xC4, 0xF8, 0x3A, 0x00, + 0xAE, 0x48, 0x81, 0x69, + 0x21, 0x60, 0x01, 0x21, 0xA9, 0x40, 0x39, 0x42, 0x01, 0xD0, 0x40, 0x6A, + 0x20, 0x60, 0xBD, 0xE8, + 0xF0, 0x81, 0x2D, 0xE9, 0xF0, 0x47, 0x89, 0x46, 0x1C, 0x46, 0x16, 0x46, + 0xAC, 0x4D, 0x03, 0xF1, + 0x3E, 0x08, 0x91, 0x78, 0x09, 0xF0, 0x94, 0xFB, 0x07, 0x46, 0xB1, 0x78, + 0x60, 0x68, 0x09, 0xF0, + 0x95, 0xFB, 0x02, 0x46, 0x00, 0x20, 0xA0, 0x61, 0xB1, 0x78, 0x1F, 0xE0, + 0x30, 0x78, 0x13, 0xE0, + 0x17, 0xF8, 0x00, 0xC0, 0xCC, 0x45, 0x0D, 0xD1, 0x32, 0xF9, 0x10, 0x30, + 0xD4, 0xF8, 0x18, 0xC0, + 0xAB, 0x42, 0x9C, 0x44, 0xC4, 0xF8, 0x18, 0xC0, 0x04, 0xDD, 0x88, 0xF8, + 0x00, 0x00, 0x1D, 0x46, + 0x88, 0xF8, 0x01, 0x10, 0x40, 0x1C, 0xC0, 0xB2, 0x73, 0x78, 0x83, 0x42, + 0xE8, 0xD2, 0x99, 0x48, + 0x49, 0x1C, 0xC9, 0xB2, 0x00, 0x78, 0x28, 0x37, 0x02, 0xEB, 0x40, 0x02, + 0xF0, 0x78, 0x88, 0x42, + 0xDC, 0xD2, 0x65, 0x86, 0x9A, 0xE7, 0x2D, 0xE9, 0xFF, 0x4F, 0x81, 0xB0, + 0x0C, 0x46, 0x1E, 0x46, + 0x99, 0x78, 0xDD, 0xF8, 0x40, 0x80, 0x0E, 0x9D, 0x09, 0xF0, 0x68, 0xFB, + 0x00, 0x90, 0xB1, 0x78, + 0x20, 0x46, 0x09, 0xF0, 0x55, 0xFB, 0x83, 0x46, 0xB1, 0x78, 0xD8, 0xF8, + 0x04, 0x00, 0x09, 0xF0, + 0x55, 0xFB, 0x81, 0x46, 0x88, 0x48, 0x4F, 0xF0, 0x00, 0x0A, 0xC8, 0xF8, + 0x18, 0xA0, 0x00, 0x68, + 0x28, 0x60, 0xB7, 0x78, 0x42, 0xE0, 0x34, 0x78, 0x32, 0xE0, 0x00, 0xBF, + 0x1B, 0xF8, 0x04, 0x10, + 0x03, 0x98, 0x81, 0x42, 0x2A, 0xD1, 0x39, 0xF9, 0x14, 0x10, 0x0F, 0x98, + 0x81, 0x42, 0x22, 0xDD, + 0x00, 0x99, 0x03, 0x98, 0x08, 0x55, 0x29, 0x78, 0x20, 0x46, 0x05, 0xF0, + 0x35, 0xFA, 0x28, 0x70, + 0x69, 0x78, 0x20, 0x46, 0x05, 0xF0, 0x34, 0xFA, 0x68, 0x70, 0xA9, 0x78, + 0x38, 0x46, 0x05, 0xF0, + 0x2B, 0xFA, 0xA8, 0x70, 0xE9, 0x78, 0x38, 0x46, 0x05, 0xF0, 0x2A, 0xFA, + 0xE8, 0x70, 0x0A, 0xF1, + 0x01, 0x00, 0x1F, 0xFA, 0x80, 0xFA, 0x39, 0xF9, 0x14, 0x00, 0xD8, 0xF8, + 0x18, 0x10, 0x01, 0x44, + 0xC8, 0xF8, 0x18, 0x10, 0x02, 0xE0, 0x00, 0x99, 0x00, 0x20, 0x08, 0x55, + 0x64, 0x1C, 0xE4, 0xB2, + 0x70, 0x78, 0xA0, 0x42, 0xCA, 0xD2, 0x00, 0x98, 0x66, 0x49, 0x28, 0x30, + 0x00, 0x90, 0x09, 0x78, + 0x7F, 0x1C, 0x0B, 0xF1, 0x28, 0x0B, 0x09, 0xEB, 0x41, 0x09, 0xFF, 0xB2, + 0xF0, 0x78, 0xB8, 0x42, + 0xB9, 0xD2, 0xA8, 0xF8, 0x26, 0xA0, 0x05, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, + 0x2D, 0xE9, 0xFF, 0x41, + 0x17, 0x46, 0x0A, 0xAA, 0x80, 0x46, 0x92, 0xE8, 0x31, 0x00, 0x5C, 0x4A, + 0x1E, 0x46, 0xB2, 0xF9, + 0x00, 0x20, 0x82, 0x42, 0x02, 0xDB, 0x00, 0x20, 0x04, 0xB0, 0x48, 0xE7, + 0x03, 0xAA, 0xCD, 0xE9, + 0x00, 0x20, 0x02, 0x94, 0x3A, 0x46, 0x40, 0x46, 0xFF, 0xF7, 0x7D, 0xFF, + 0x01, 0x21, 0x28, 0x68, + 0xB9, 0x40, 0x08, 0x43, 0x28, 0x60, 0xC4, 0xF8, 0x00, 0x80, 0xE0, 0x8C, + 0x10, 0xB1, 0x03, 0x98, + 0x30, 0x60, 0xE8, 0xE7, 0x01, 0x20, 0xE7, 0xE7, 0x2D, 0xE9, 0xFF, 0x4F, + 0x81, 0xB0, 0x90, 0x46, + 0x0E, 0x9C, 0x8B, 0x46, 0x04, 0xEB, 0x88, 0x00, 0x94, 0xF8, 0x0E, 0x71, + 0xDD, 0xF8, 0x40, 0xA0, + 0x00, 0x68, 0x1E, 0x46, 0x20, 0x60, 0x01, 0x23, 0x21, 0x46, 0x58, 0x46, + 0x00, 0xF0, 0x20, 0xFF, + 0x01, 0x98, 0x58, 0x45, 0x1F, 0xD0, 0xB5, 0x78, 0x1A, 0xE0, 0x29, 0x46, + 0x01, 0x98, 0x09, 0xF0, + 0xC5, 0xFA, 0x81, 0x46, 0x29, 0x46, 0x58, 0x46, 0x09, 0xF0, 0xC0, 0xFA, + 0x01, 0x46, 0x30, 0x78, + 0x09, 0xE0, 0x00, 0xBF, 0x19, 0xF8, 0x00, 0x30, 0x43, 0x45, 0x02, 0xD1, + 0x0B, 0x5C, 0x09, 0xF8, + 0x00, 0x30, 0x40, 0x1C, 0xC0, 0xB2, 0x72, 0x78, 0x82, 0x42, 0xF3, 0xD2, + 0x6D, 0x1C, 0xED, 0xB2, + 0xF0, 0x78, 0xA8, 0x42, 0xE1, 0xD2, 0x94, 0xF8, 0x0E, 0x01, 0x00, 0x25, + 0x01, 0x21, 0xB8, 0x42, + 0x12, 0xD0, 0x11, 0x98, 0x78, 0xB1, 0x7F, 0x1C, 0xF8, 0xB2, 0x08, 0xE0, + 0xDA, 0xF8, 0x00, 0x20, + 0x01, 0xFA, 0x00, 0xF3, 0x1A, 0x43, 0x40, 0x1C, 0xC0, 0xB2, 0xCA, 0xF8, + 0x00, 0x20, 0x94, 0xF8, + 0x0E, 0x21, 0x82, 0x42, 0xF2, 0xD2, 0x01, 0x25, 0x00, 0x91, 0x43, 0x46, + 0x52, 0x46, 0x20, 0x46, + 0x0F, 0x99, 0xFF, 0xF7, 0x33, 0xFE, 0x28, 0x46, 0x7D, 0xE7, 0x10, 0xB5, + 0x17, 0x4A, 0x20, 0x4C, + 0x13, 0x68, 0x64, 0x78, 0x93, 0xF8, 0x06, 0x23, 0x4C, 0xB1, 0x91, 0xF8, + 0x24, 0x10, 0x09, 0x07, + 0x02, 0xD5, 0x93, 0xF8, 0x09, 0x13, 0x01, 0xE0, 0x93, 0xF8, 0x08, 0x13, + 0x0A, 0x44, 0x90, 0x42, + 0x01, 0xD9, 0x01, 0x20, 0x10, 0xBD, 0x00, 0x20, 0x10, 0xBD, 0x2D, 0xE9, + 0xF0, 0x47, 0x1C, 0x46, + 0x17, 0x46, 0x88, 0x46, 0x81, 0x46, 0x01, 0xF0, 0xED, 0xF8, 0x08, 0x4E, + 0xE1, 0x8C, 0x30, 0x68, + 0x90, 0xF8, 0x0E, 0x23, 0x91, 0x42, 0x7C, 0xD8, 0x03, 0x21, 0x84, 0xF8, + 0x40, 0x10, 0x90, 0xF8, + 0xE0, 0x12, 0x00, 0x25, 0x8A, 0x07, 0x15, 0xE0, 0x38, 0x06, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, + 0x66, 0x05, 0x10, 0x00, 0xF4, 0x06, 0x10, 0x00, 0xF0, 0x45, 0x10, 0x00, + 0x10, 0x07, 0x10, 0x00, + 0x00, 0x80, 0xFF, 0xFF, 0xEE, 0x06, 0x10, 0x00, 0x36, 0x7A, 0x01, 0x00, + 0x9A, 0x06, 0x10, 0x00, + 0x74, 0x07, 0x10, 0x00, 0x01, 0xD4, 0x49, 0x07, 0x00, 0xD5, 0x01, 0x25, + 0xFF, 0x4A, 0xB0, 0xF8, + 0x0A, 0x13, 0x52, 0x78, 0x22, 0xB1, 0xFE, 0x4A, 0x12, 0x78, 0x0A, 0xB9, + 0xB0, 0xF8, 0x0C, 0x13, + 0xA2, 0x8D, 0x8A, 0x42, 0x02, 0xD2, 0xFB, 0x49, 0x09, 0x78, 0x21, 0xB1, + 0x61, 0x8D, 0x03, 0x29, + 0x06, 0xD2, 0x00, 0x25, 0x2C, 0xE0, 0x61, 0x8D, 0x00, 0x25, 0x03, 0x29, + 0xF9, 0xD3, 0x27, 0xE0, + 0x35, 0xB3, 0x90, 0xF8, 0x40, 0x04, 0xC1, 0x07, 0x0D, 0xD0, 0x40, 0x07, + 0x0B, 0xD5, 0x21, 0x46, + 0x38, 0x46, 0xFF, 0xF7, 0x80, 0xFD, 0xF0, 0x49, 0x08, 0x70, 0x20, 0xB1, + 0xA0, 0x8C, 0x40, 0xF0, + 0x80, 0x00, 0xA0, 0x84, 0xE5, 0xE7, 0x30, 0x68, 0x90, 0xF8, 0x20, 0x04, + 0xC0, 0x07, 0x0F, 0xD0, + 0xEA, 0x48, 0xEB, 0x49, 0xB0, 0xF9, 0x00, 0x00, 0xB1, 0xF9, 0x00, 0x10, + 0x88, 0x42, 0x07, 0xDB, + 0x23, 0x46, 0x3A, 0x46, 0x41, 0x46, 0x48, 0x46, 0x02, 0xF0, 0x4D, 0xF9, + 0x01, 0x28, 0x04, 0xD0, + 0xA0, 0x8C, 0x10, 0xF4, 0xC0, 0x7F, 0x19, 0xD1, 0x04, 0xE0, 0xA0, 0x8C, + 0x40, 0xF4, 0x80, 0x70, + 0xA0, 0x84, 0x13, 0xE0, 0x95, 0xB1, 0x30, 0x68, 0x90, 0xF8, 0xE0, 0x02, + 0xC0, 0x07, 0x03, 0xD0, + 0x21, 0x46, 0x38, 0x46, 0x01, 0xF0, 0x3B, 0xFA, 0x23, 0x46, 0x3A, 0x46, + 0x41, 0x46, 0x48, 0x46, + 0x00, 0xE0, 0x0A, 0xE0, 0xBD, 0xE8, 0xF0, 0x47, 0x01, 0xF0, 0xFD, 0xBF, + 0x23, 0x46, 0x3A, 0x46, + 0x41, 0x46, 0x48, 0x46, 0xBD, 0xE8, 0xF0, 0x47, 0xAA, 0xE5, 0x04, 0x20, + 0x84, 0xF8, 0x40, 0x00, + 0xF4, 0xE7, 0xD0, 0x48, 0x00, 0x78, 0x80, 0x07, 0x0B, 0xD5, 0xCF, 0x48, + 0xCF, 0x4A, 0x00, 0x68, + 0x12, 0x68, 0x90, 0xF8, 0x57, 0x13, 0x91, 0x42, 0x05, 0xD2, 0x90, 0xF8, + 0x40, 0x03, 0x80, 0x07, + 0x01, 0xD5, 0x00, 0x20, 0x70, 0x47, 0x01, 0x20, 0x70, 0x47, 0x91, 0xF8, + 0x24, 0x10, 0xC6, 0x4A, + 0xCB, 0x07, 0xC7, 0x49, 0x12, 0x68, 0x09, 0x78, 0x06, 0xD0, 0x11, 0xB1, + 0x92, 0xF8, 0x48, 0x13, + 0x05, 0xE0, 0x92, 0xF8, 0x49, 0x13, 0x02, 0xE0, 0x59, 0xB1, 0x92, 0xF8, + 0x46, 0x13, 0xB7, 0x4B, + 0x5B, 0x78, 0x13, 0xB1, 0x92, 0xF8, 0x4A, 0x23, 0x11, 0x44, 0x88, 0x42, + 0x04, 0xD9, 0x01, 0x20, + 0x70, 0x47, 0x92, 0xF8, 0x47, 0x13, 0xF2, 0xE7, 0x00, 0x20, 0x70, 0x47, + 0x2D, 0xE9, 0xFF, 0x4F, + 0xB8, 0x48, 0xDF, 0xF8, 0xD4, 0x92, 0x87, 0xB0, 0xB0, 0xF9, 0x00, 0x80, + 0xD9, 0xF8, 0x00, 0x00, + 0x0C, 0x46, 0x00, 0x26, 0x90, 0xF8, 0x40, 0x04, 0xDF, 0xF8, 0xCC, 0xB2, + 0xC1, 0x07, 0x14, 0x98, + 0x4F, 0xF0, 0x01, 0x07, 0x15, 0x46, 0x07, 0xFA, 0x00, 0xF7, 0xB2, 0x46, + 0x3A, 0xD0, 0xAF, 0x49, + 0xA7, 0x4A, 0xB1, 0xF9, 0x00, 0x00, 0xB2, 0xF9, 0x00, 0x20, 0x90, 0x42, + 0x32, 0xDA, 0x2A, 0x68, + 0x3A, 0x42, 0x2F, 0xD1, 0xE2, 0x8C, 0x04, 0x2A, 0x2C, 0xD9, 0x8D, 0xE8, + 0x31, 0x00, 0x0A, 0x9B, + 0x14, 0x9A, 0xDB, 0xF8, 0x18, 0x10, 0xDB, 0xF8, 0x24, 0x00, 0xFF, 0xF7, + 0x67, 0xFE, 0x06, 0x00, + 0x20, 0xD1, 0x23, 0x46, 0x20, 0x68, 0x0A, 0x9A, 0x14, 0x99, 0xFF, 0xF7, + 0x22, 0xFC, 0x96, 0x49, + 0x08, 0x70, 0xA0, 0xB1, 0x70, 0x1E, 0x8D, 0xF8, 0x10, 0x00, 0x8D, 0xF8, + 0x14, 0x00, 0xD9, 0xF8, + 0x00, 0x00, 0x90, 0xF8, 0x41, 0x04, 0x80, 0x07, 0x05, 0xD5, 0x05, 0xAA, + 0x04, 0xA9, 0x0A, 0x98, + 0xF7, 0xF7, 0x93, 0xFF, 0xA8, 0xB3, 0xA0, 0x8C, 0x40, 0xF0, 0x80, 0x00, + 0xA0, 0x84, 0x93, 0x48, + 0xB0, 0xF9, 0x00, 0x80, 0xD9, 0xF8, 0x00, 0x00, 0x90, 0xF8, 0x20, 0x04, + 0xC0, 0x07, 0x53, 0xD0, + 0x94, 0xF8, 0x24, 0x00, 0x00, 0x06, 0x4F, 0xD4, 0x84, 0x48, 0x85, 0x4A, + 0xB0, 0xF9, 0x00, 0x10, + 0xB2, 0xF9, 0x00, 0x20, 0x91, 0x42, 0x47, 0xDA, 0x81, 0x46, 0x41, 0x45, + 0x17, 0xD0, 0x28, 0x68, + 0x38, 0x42, 0x07, 0xD0, 0xCD, 0xF8, 0x00, 0xA0, 0x2A, 0x46, 0x21, 0x46, + 0x14, 0x9B, 0x07, 0x98, + 0xFF, 0xF7, 0xD4, 0xFC, 0xB9, 0xF9, 0x00, 0x00, 0x8D, 0xE8, 0x31, 0x00, + 0x0A, 0x9B, 0x14, 0x9A, + 0xDB, 0xF8, 0x18, 0x10, 0xDB, 0xF8, 0x24, 0x00, 0xFF, 0xF7, 0x18, 0xFE, + 0x06, 0x46, 0xFE, 0xB1, + 0x2A, 0xE0, 0xFF, 0xE7, 0x7A, 0x49, 0x9D, 0xF9, 0x10, 0x30, 0x01, 0xF8, + 0x36, 0x3F, 0x9D, 0xF9, + 0x14, 0x00, 0x48, 0x70, 0x00, 0x90, 0x20, 0x68, 0x0A, 0x9A, 0x07, 0x99, + 0xFF, 0xF7, 0x27, 0xFC, + 0x07, 0x98, 0x04, 0xF1, 0x3A, 0x03, 0x8D, 0xE8, 0x31, 0x04, 0x21, 0x68, + 0x14, 0x9A, 0xDB, 0xF8, + 0x18, 0x00, 0xFF, 0xF7, 0x21, 0xFE, 0x00, 0x28, 0xB1, 0xD0, 0x01, 0x20, + 0x0B, 0xB0, 0xF3, 0xE5, + 0x23, 0x46, 0x20, 0x68, 0x0A, 0x9A, 0x14, 0x99, 0x02, 0xF0, 0x45, 0xF8, + 0x01, 0x28, 0x03, 0xD1, + 0xA0, 0x8C, 0x40, 0xF4, 0x80, 0x70, 0xA0, 0x84, 0xA0, 0x8C, 0x10, 0xF4, + 0xC0, 0x7F, 0x0A, 0xD1, + 0x28, 0x68, 0x38, 0x42, 0x07, 0xD0, 0xCD, 0xF8, 0x00, 0xA0, 0x2A, 0x46, + 0x21, 0x46, 0x14, 0x9B, + 0x07, 0x98, 0xFF, 0xF7, 0x8B, 0xFC, 0x30, 0x46, 0xE0, 0xE7, 0x2D, 0xE9, + 0xF0, 0x4F, 0x5D, 0x49, + 0x53, 0x48, 0xE7, 0xB0, 0xB1, 0xF9, 0x00, 0x10, 0xB0, 0xF9, 0x00, 0x00, + 0x4F, 0xF0, 0x00, 0x08, + 0xCD, 0xE9, 0x62, 0x01, 0x54, 0x48, 0xC3, 0x46, 0xC1, 0x46, 0x47, 0x46, + 0x01, 0x21, 0x1C, 0x30, + 0x02, 0xF0, 0x0D, 0xFC, 0x50, 0x48, 0x01, 0x21, 0x24, 0x30, 0x02, 0xF0, + 0x08, 0xFC, 0x4E, 0x48, + 0x02, 0x21, 0x20, 0x30, 0x02, 0xF0, 0x03, 0xFC, 0x02, 0x21, 0x4F, 0x48, + 0x02, 0xF0, 0xA7, 0xFB, + 0x04, 0x21, 0x4E, 0x48, 0x02, 0xF0, 0xA3, 0xFB, 0x47, 0x4C, 0x4F, 0xF4, + 0x66, 0x75, 0x29, 0x46, + 0xE0, 0x69, 0x08, 0xF0, 0xA1, 0xFF, 0x29, 0x46, 0x60, 0x6A, 0x08, 0xF0, + 0x9D, 0xFF, 0x48, 0x48, + 0xB0, 0xF9, 0x00, 0x00, 0x41, 0x00, 0x20, 0x6A, 0x08, 0xF0, 0x96, 0xFF, + 0x29, 0x46, 0x45, 0x48, + 0x08, 0xF0, 0x92, 0xFF, 0x4F, 0xF0, 0x00, 0x0A, 0x55, 0x46, 0x84, 0xF8, + 0x01, 0xA0, 0xF8, 0xF7, + 0xFB, 0xFC, 0xF7, 0xF7, 0x41, 0xFE, 0x40, 0x48, 0x8D, 0xF8, 0x72, 0x51, + 0x01, 0x23, 0x00, 0x68, + 0x19, 0x90, 0x00, 0x22, 0x19, 0xA9, 0xA0, 0x69, 0x00, 0xF0, 0xE2, 0xFC, + 0x3B, 0x48, 0x5D, 0x95, + 0x00, 0x26, 0xB0, 0xF9, 0x00, 0x10, 0x2A, 0x48, 0xB0, 0xF9, 0x00, 0x00, + 0x04, 0xF0, 0x7C, 0xFF, + 0x00, 0xB2, 0x06, 0x90, 0x9D, 0xF8, 0x72, 0x01, 0x01, 0x28, 0x02, 0xD9, + 0x22, 0x49, 0x81, 0xF8, + 0x00, 0xA0, 0x0D, 0xF1, 0x56, 0x00, 0x01, 0x25, 0x60, 0x90, 0x9B, 0xE1, + 0x00, 0x20, 0x01, 0x90, + 0x19, 0xA8, 0x00, 0xEB, 0x85, 0x00, 0x05, 0x90, 0x19, 0xAA, 0x29, 0x46, + 0x07, 0xA8, 0x5D, 0x9B, + 0xFF, 0xF7, 0x96, 0xFC, 0x07, 0xAB, 0x29, 0x46, 0x05, 0x9A, 0x07, 0x98, + 0xFF, 0xF7, 0xB9, 0xFC, + 0x01, 0x24, 0x2B, 0x46, 0x5D, 0xAA, 0x07, 0xA9, 0x19, 0xA8, 0x00, 0x94, + 0xFF, 0xF7, 0x06, 0xFC, + 0x0D, 0xF1, 0x5A, 0x00, 0x00, 0xF0, 0xFE, 0xFA, 0x78, 0xB1, 0xBD, 0xF8, + 0x40, 0x00, 0x40, 0xF0, + 0x01, 0x00, 0xAD, 0xF8, 0x40, 0x00, 0x07, 0xA8, 0x00, 0xF0, 0x41, 0xFC, + 0x28, 0xB1, 0x1C, 0x48, + 0x14, 0x49, 0x00, 0x88, 0x08, 0x80, 0x0A, 0x49, 0x08, 0x80, 0x06, 0x48, + 0x00, 0x78, 0x88, 0xB3, + 0xBD, 0xF8, 0x40, 0x00, 0x10, 0xF0, 0x06, 0x0F, 0x30, 0xD1, 0x2C, 0xE0, + 0x74, 0x07, 0x10, 0x00, + 0xFD, 0x06, 0x10, 0x00, 0xA4, 0x06, 0x10, 0x00, 0x34, 0x06, 0x10, 0x00, + 0xA6, 0x06, 0x10, 0x00, + 0x92, 0x06, 0x10, 0x00, 0x04, 0x07, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x80, 0x06, 0x10, 0x00, + 0xF9, 0x06, 0x10, 0x00, 0x9A, 0x06, 0x10, 0x00, 0x38, 0x06, 0x10, 0x00, + 0xA8, 0x06, 0x10, 0x00, + 0x0A, 0x44, 0x01, 0x20, 0x94, 0x06, 0x10, 0x00, 0x60, 0x06, 0x10, 0x00, + 0x6C, 0x06, 0x10, 0x00, + 0xFE, 0x06, 0x10, 0x00, 0xC2, 0x18, 0x01, 0x20, 0x88, 0x06, 0x10, 0x00, + 0xAC, 0x06, 0x10, 0x00, + 0xA2, 0x06, 0x10, 0x00, 0x02, 0xE0, 0xFE, 0x48, 0x00, 0x68, 0x08, 0x90, + 0x05, 0x98, 0x00, 0xF0, + 0xC3, 0xFA, 0x28, 0xB1, 0xBD, 0xF8, 0x40, 0x00, 0x40, 0xF0, 0x08, 0x00, + 0xAD, 0xF8, 0x40, 0x00, + 0x01, 0x24, 0x5D, 0x98, 0xAC, 0x40, 0x04, 0x42, 0x41, 0xD1, 0xF6, 0x48, + 0x00, 0x78, 0xC0, 0x07, + 0x3D, 0xD0, 0xF5, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x28, 0x13, 0xF4, 0x48, + 0x00, 0x68, 0x81, 0x42, + 0xF3, 0x48, 0x40, 0x78, 0x01, 0xD2, 0x90, 0xBB, 0x01, 0xE0, 0x02, 0x28, + 0x2F, 0xD2, 0xF1, 0x48, + 0x5D, 0xAA, 0x07, 0xA9, 0xB0, 0xF9, 0x00, 0x00, 0x8D, 0xE8, 0x07, 0x00, + 0xEE, 0x48, 0x2A, 0x46, + 0x05, 0x9B, 0x81, 0x69, 0x40, 0x6A, 0xFF, 0xF7, 0xD9, 0xFC, 0x01, 0x90, + 0xB8, 0xB9, 0x07, 0xAB, + 0x29, 0x46, 0x05, 0x9A, 0x07, 0x98, 0x00, 0xF0, 0xA6, 0xF9, 0x80, 0xB1, + 0x4F, 0xF0, 0x02, 0x0C, + 0x8D, 0xF8, 0x5C, 0xC0, 0x29, 0x46, 0x07, 0xAB, 0x05, 0x9A, 0x07, 0x98, + 0xAD, 0xF8, 0x4A, 0xC0, + 0xAD, 0xF8, 0x4C, 0xC0, 0xFF, 0xF7, 0x9C, 0xFB, 0x4F, 0xF0, 0x01, 0x0B, + 0xCF, 0xE0, 0x2B, 0x46, + 0x5D, 0xAA, 0x07, 0xA9, 0x19, 0xA8, 0xCD, 0xF8, 0x00, 0xA0, 0xFF, 0xF7, + 0x67, 0xFB, 0xBD, 0xF9, + 0x4E, 0x00, 0x06, 0x99, 0x88, 0x42, 0x6A, 0xDD, 0x00, 0x95, 0x5D, 0xAA, + 0x07, 0xA9, 0x19, 0xA8, + 0x05, 0x9B, 0xFF, 0xF7, 0x13, 0xFE, 0x00, 0x28, 0x7E, 0xD1, 0x5D, 0x98, + 0x04, 0x42, 0x3A, 0xD1, + 0xBD, 0xF8, 0x40, 0x00, 0x10, 0xF4, 0xC0, 0x7F, 0x35, 0xD1, 0xD0, 0x48, + 0x5D, 0xAA, 0x07, 0xA9, + 0xB0, 0xF9, 0x00, 0x00, 0x8D, 0xE8, 0x07, 0x00, 0xCB, 0x48, 0x2A, 0x46, + 0x05, 0x9B, 0x81, 0x69, + 0x40, 0x6A, 0xFF, 0xF7, 0x93, 0xFC, 0x01, 0x90, 0x5D, 0x98, 0x04, 0x42, + 0x23, 0xD0, 0xC2, 0x48, + 0xBD, 0xF8, 0x42, 0x20, 0x00, 0x68, 0x90, 0xF8, 0x0E, 0x33, 0x9A, 0x42, + 0x05, 0xD8, 0x90, 0xF8, + 0xE0, 0x02, 0x82, 0x07, 0x17, 0xD4, 0x40, 0x07, 0x15, 0xD4, 0x01, 0x23, + 0x5D, 0xAA, 0x07, 0xA9, + 0x19, 0xA8, 0x8D, 0xE8, 0x0F, 0x00, 0xBC, 0x48, 0x2A, 0x46, 0x60, 0x9B, + 0x41, 0x6A, 0x80, 0x69, + 0xFF, 0xF7, 0x9A, 0xFC, 0x01, 0x90, 0x01, 0x20, 0x00, 0x90, 0x2B, 0x46, + 0x5D, 0xAA, 0x07, 0xA9, + 0x19, 0xA8, 0xFF, 0xF7, 0x1B, 0xFB, 0x01, 0x98, 0xC8, 0xB9, 0xBD, 0xF8, + 0x42, 0x00, 0x07, 0xA9, + 0xFF, 0xF7, 0xE3, 0xFC, 0x98, 0xB1, 0x07, 0xA8, 0x00, 0x90, 0xB1, 0x48, + 0x29, 0x46, 0x60, 0x9A, + 0xB0, 0xF9, 0x00, 0x30, 0xAC, 0x48, 0x80, 0x69, 0x00, 0xF0, 0x84, 0xF9, + 0x07, 0xAB, 0x29, 0x46, + 0x05, 0x9A, 0x07, 0x98, 0xFF, 0xF7, 0xE9, 0xFC, 0x4F, 0xF0, 0x01, 0x08, + 0x5F, 0xE0, 0x2B, 0x46, + 0x5D, 0xAA, 0x07, 0xA9, 0x19, 0xA8, 0xCD, 0xF8, 0x00, 0xA0, 0xFF, 0xF7, + 0xF7, 0xFA, 0xA4, 0x48, + 0xBD, 0xF9, 0x4E, 0x10, 0xB0, 0xF9, 0x00, 0x00, 0x81, 0x42, 0x50, 0xDD, + 0xFF, 0xF7, 0x71, 0xFD, + 0xF0, 0xB3, 0x5D, 0x98, 0x04, 0x42, 0x2F, 0xD1, 0x9D, 0x48, 0x5D, 0xAA, + 0x07, 0xA9, 0xB0, 0xF9, + 0x00, 0x00, 0x8D, 0xE8, 0x07, 0x00, 0x98, 0x48, 0x2A, 0x46, 0x05, 0x9B, + 0x81, 0x69, 0x40, 0x6A, + 0xFF, 0xF7, 0x2C, 0xFC, 0x01, 0x90, 0x00, 0xE0, 0x52, 0xE0, 0x8F, 0x48, + 0x00, 0x68, 0x90, 0xF8, + 0x40, 0x03, 0xC0, 0x06, 0x18, 0xD4, 0x5D, 0x98, 0x04, 0x42, 0x15, 0xD0, + 0x01, 0x23, 0x5D, 0xAA, + 0x07, 0xA9, 0x19, 0xA8, 0x8D, 0xE8, 0x0F, 0x00, 0x8B, 0x48, 0x2A, 0x46, + 0x60, 0x9B, 0x41, 0x6A, + 0x80, 0x69, 0xFF, 0xF7, 0x39, 0xFC, 0x01, 0x90, 0x01, 0x20, 0x00, 0x90, + 0x2B, 0x46, 0x5D, 0xAA, + 0x07, 0xA9, 0x19, 0xA8, 0xFF, 0xF7, 0xBA, 0xFA, 0x01, 0x98, 0x80, 0xB9, + 0xBD, 0xF8, 0x42, 0x00, + 0x07, 0xA9, 0xFF, 0xF7, 0x4A, 0xFD, 0x50, 0xB1, 0x07, 0xAB, 0x29, 0x46, + 0x05, 0x9A, 0x00, 0xE0, + 0x0D, 0xE0, 0x07, 0x98, 0x00, 0xF0, 0x6F, 0xF9, 0x4F, 0xF0, 0x01, 0x09, + 0x07, 0xE0, 0x2B, 0x46, + 0x5D, 0xAA, 0x07, 0xA9, 0x19, 0xA8, 0xCD, 0xF8, 0x00, 0xA0, 0xFF, 0xF7, + 0x9F, 0xFA, 0x5D, 0x99, + 0x44, 0xEA, 0x01, 0x00, 0x73, 0x49, 0x5D, 0x90, 0x63, 0x98, 0x08, 0x80, + 0x73, 0x49, 0x62, 0x98, + 0x08, 0x80, 0x74, 0x48, 0x01, 0x78, 0x31, 0xB1, 0x9D, 0xF8, 0x72, 0x11, + 0x26, 0x43, 0x01, 0x29, + 0x01, 0xD9, 0x80, 0xF8, 0x00, 0xA0, 0xBD, 0xF8, 0x40, 0x00, 0xC0, 0x05, + 0x00, 0xD5, 0x27, 0x43, + 0x6D, 0x1C, 0xED, 0xB2, 0x9D, 0xF8, 0x72, 0x01, 0xA8, 0x42, 0xBF, 0xF4, + 0x5F, 0xAE, 0x66, 0x48, + 0x6A, 0x4A, 0x69, 0x4B, 0x41, 0x78, 0xA2, 0xF5, 0x66, 0x70, 0x01, 0xF0, + 0x7A, 0xFC, 0x68, 0x48, + 0x9D, 0xF8, 0x72, 0x11, 0x00, 0xF8, 0x05, 0x1F, 0x68, 0x49, 0x06, 0x83, + 0x87, 0x83, 0x65, 0x48, + 0x80, 0xF8, 0x00, 0xB0, 0x64, 0x48, 0x80, 0xF8, 0x00, 0x80, 0x81, 0xF8, + 0x00, 0x90, 0x02, 0x21, + 0x63, 0x48, 0x02, 0xF0, 0xC1, 0xF9, 0x04, 0x21, 0x62, 0x48, 0x02, 0xF0, + 0xBD, 0xF9, 0x56, 0x48, + 0x01, 0x21, 0x18, 0x30, 0x02, 0xF0, 0x86, 0xFA, 0x53, 0x48, 0x01, 0x21, + 0x1C, 0x30, 0x02, 0xF0, + 0x81, 0xFA, 0x51, 0x48, 0x01, 0x21, 0x24, 0x30, 0x02, 0xF0, 0x7C, 0xFA, + 0x4E, 0x48, 0x02, 0x21, + 0x20, 0x30, 0x02, 0xF0, 0x77, 0xFA, 0x67, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, + 0x51, 0x49, 0x00, 0x20, + 0x08, 0x70, 0x51, 0x49, 0x08, 0x70, 0x51, 0x49, 0x08, 0x70, 0x4A, 0x49, + 0x08, 0x70, 0xA8, 0x21, + 0x49, 0x48, 0x08, 0xF0, 0xA3, 0xBD, 0x44, 0x49, 0x00, 0x20, 0x4D, 0x4A, + 0x08, 0x62, 0x88, 0x61, + 0xC8, 0x61, 0x10, 0x60, 0x4B, 0x4A, 0x10, 0x60, 0x46, 0x4A, 0x10, 0x70, + 0x46, 0x4A, 0x10, 0x70, + 0x46, 0x4A, 0x10, 0x70, 0x3F, 0x4A, 0x10, 0x70, 0x48, 0x70, 0x70, 0x47, + 0x3A, 0x49, 0x10, 0xB5, + 0x00, 0x20, 0x48, 0x70, 0x4F, 0xF4, 0x66, 0x71, 0x43, 0x48, 0x08, 0xF0, + 0x65, 0xFD, 0x3A, 0x48, + 0xA8, 0x21, 0xA8, 0x38, 0x08, 0xF0, 0x82, 0xFD, 0xBD, 0xE8, 0x10, 0x40, + 0xA8, 0x21, 0x36, 0x48, + 0x08, 0xF0, 0x7C, 0xBD, 0x2D, 0xE9, 0xF7, 0x4F, 0x2F, 0x48, 0x0D, 0x46, + 0x41, 0x78, 0x0F, 0x29, + 0x01, 0xD2, 0x49, 0x1C, 0x41, 0x70, 0x01, 0xF0, 0xFF, 0x09, 0x30, 0x49, + 0x82, 0xF8, 0x41, 0x90, + 0x01, 0xEB, 0x89, 0x00, 0x29, 0x68, 0x01, 0x60, 0xA9, 0x78, 0x33, 0x48, + 0x08, 0xF0, 0x56, 0xFE, + 0x82, 0x46, 0xA8, 0x78, 0x19, 0xE0, 0x00, 0x98, 0x2C, 0x78, 0x00, 0xEB, + 0xC8, 0x0B, 0x0D, 0xE0, + 0xDB, 0xE9, 0x00, 0x67, 0x22, 0x46, 0x01, 0x20, 0x00, 0x21, 0x08, 0xF0, + 0x7F, 0xFC, 0x06, 0x40, + 0x0F, 0x40, 0x3E, 0x43, 0x01, 0xD0, 0x0A, 0xF8, 0x04, 0x90, 0x64, 0x1C, + 0x68, 0x78, 0xA0, 0x42, + 0xEE, 0xDA, 0x0A, 0xF1, 0x28, 0x0A, 0x08, 0xF1, 0x01, 0x00, 0xE9, 0x78, + 0x80, 0x46, 0x41, 0x45, + 0xE1, 0xDA, 0xBD, 0xE8, 0xFE, 0x8F, 0x2D, 0xE9, 0xF0, 0x41, 0x0F, 0x4C, + 0x10, 0x4F, 0xB3, 0xF8, + 0x26, 0xC0, 0x24, 0x68, 0x7F, 0x78, 0x00, 0x26, 0x94, 0xF8, 0x29, 0x53, + 0x17, 0xB1, 0x94, 0xF8, + 0x2A, 0x73, 0x3D, 0x44, 0x93, 0xF8, 0x24, 0xE0, 0x94, 0xF8, 0x2B, 0x73, + 0x5F, 0xEA, 0xCE, 0x7E, + 0x01, 0xD0, 0x94, 0xF8, 0x2C, 0x73, 0xAC, 0x45, 0x39, 0xD9, 0xBC, 0x45, + 0x37, 0xD8, 0x25, 0xE0, + 0x14, 0x07, 0x10, 0x00, 0x04, 0x07, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x7C, 0x06, 0x10, 0x00, + 0x74, 0x07, 0x10, 0x00, 0x94, 0x06, 0x10, 0x00, 0x38, 0x06, 0x10, 0x00, + 0x92, 0x06, 0x10, 0x00, + 0x96, 0x06, 0x10, 0x00, 0x34, 0x06, 0x10, 0x00, 0x98, 0x46, 0x10, 0x00, + 0x5A, 0x1C, 0x01, 0x20, + 0x0A, 0x44, 0x01, 0x20, 0xF6, 0x06, 0x10, 0x00, 0xF7, 0x06, 0x10, 0x00, + 0xF8, 0x06, 0x10, 0x00, + 0x60, 0x06, 0x10, 0x00, 0x6C, 0x06, 0x10, 0x00, 0xC2, 0x18, 0x01, 0x20, + 0x45, 0x4F, 0xB3, 0xF9, + 0x32, 0x50, 0xB7, 0xF9, 0x00, 0x70, 0xBD, 0x42, 0x09, 0xDD, 0xB4, 0xF8, + 0x26, 0x43, 0x9D, 0x69, + 0xA5, 0x42, 0x04, 0xDA, 0x00, 0xF0, 0xDB, 0xFC, 0x01, 0x28, 0x00, 0xD1, + 0x01, 0x26, 0x30, 0x46, + 0xBD, 0xE8, 0xF0, 0x81, 0x2D, 0xE9, 0xFF, 0x5F, 0x8B, 0x46, 0x14, 0x46, + 0x91, 0x78, 0x0E, 0x9D, + 0x08, 0xF0, 0xC6, 0xFD, 0x06, 0x46, 0xA1, 0x78, 0x68, 0x68, 0x08, 0xF0, + 0xC7, 0xFD, 0x07, 0x46, + 0x4F, 0xF0, 0x00, 0x09, 0xFF, 0xF7, 0xF5, 0xFB, 0x00, 0x28, 0x32, 0xD0, + 0x32, 0x49, 0xB5, 0xF9, + 0x32, 0x00, 0xB1, 0xF9, 0x00, 0x10, 0x88, 0x42, 0x2B, 0xDC, 0x30, 0x48, + 0xA1, 0x78, 0xE3, 0x78, + 0x90, 0xF8, 0x00, 0xA0, 0x19, 0xE0, 0x20, 0x78, 0x62, 0x78, 0x0F, 0xE0, + 0x16, 0xF8, 0x00, 0x80, + 0xD8, 0x45, 0x09, 0xD1, 0x37, 0xF9, 0x10, 0x80, 0xDD, 0xF8, 0x0C, 0xC0, + 0xE0, 0x45, 0x03, 0xDD, + 0x09, 0xF1, 0x01, 0x0C, 0x1F, 0xFA, 0x8C, 0xF9, 0x40, 0x1C, 0xC0, 0xB2, + 0x82, 0x42, 0xED, 0xD2, + 0x49, 0x1C, 0x07, 0xEB, 0x4A, 0x07, 0xC9, 0xB2, 0x28, 0x36, 0x8B, 0x42, + 0xE3, 0xD2, 0x29, 0x46, + 0x48, 0x46, 0xFF, 0xF7, 0xDA, 0xFB, 0x00, 0x28, 0x03, 0xD0, 0xA8, 0x8C, + 0x40, 0xF4, 0x00, 0x70, + 0xA8, 0x84, 0xBD, 0xE8, 0xFF, 0x9F, 0x2D, 0xE9, 0xF0, 0x41, 0x80, 0x46, + 0x01, 0x20, 0x83, 0xF8, + 0x40, 0x00, 0x1C, 0x46, 0x16, 0x46, 0x0F, 0x46, 0x40, 0x46, 0x00, 0xF0, + 0x0B, 0xFC, 0x14, 0x4D, + 0x28, 0x68, 0x90, 0xF8, 0x40, 0x03, 0x41, 0x07, 0x04, 0xD5, 0x21, 0x46, + 0x30, 0x46, 0x00, 0xF0, + 0xDF, 0xFD, 0x05, 0xE0, 0x00, 0x07, 0x03, 0xD5, 0x21, 0x46, 0x30, 0x46, + 0x00, 0xF0, 0xC7, 0xFD, + 0x28, 0x68, 0x23, 0x46, 0x32, 0x46, 0x90, 0xF8, 0x40, 0x03, 0x39, 0x46, + 0xC0, 0x06, 0x40, 0x46, + 0x03, 0xD5, 0xBD, 0xE8, 0xF0, 0x41, 0x01, 0xF0, 0x86, 0xBB, 0xBD, 0xE8, + 0xF0, 0x41, 0xFF, 0xF7, + 0x37, 0xB9, 0x00, 0x00, 0x94, 0x06, 0x10, 0x00, 0x8C, 0x06, 0x10, 0x00, + 0xEE, 0x06, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, 0x01, 0x78, 0x59, 0xB1, 0x80, 0x4A, 0x12, 0x78, + 0x52, 0x1E, 0x91, 0x42, + 0x06, 0xD0, 0x40, 0x78, 0x20, 0xB1, 0x7E, 0x49, 0x09, 0x78, 0x49, 0x1E, + 0x88, 0x42, 0x01, 0xD1, + 0x01, 0x20, 0x70, 0x47, 0x00, 0x20, 0x70, 0x47, 0x01, 0x78, 0x29, 0xB1, + 0x77, 0x49, 0x42, 0x78, + 0x09, 0x78, 0x49, 0x1E, 0x8A, 0x42, 0x09, 0xD1, 0x81, 0x78, 0x29, 0xB1, + 0xC1, 0x78, 0x74, 0x48, + 0x00, 0x78, 0x40, 0x1E, 0x81, 0x42, 0x01, 0xD1, 0x01, 0x20, 0x70, 0x47, + 0x00, 0x20, 0x70, 0x47, + 0xF0, 0xB5, 0x70, 0x4D, 0x00, 0x22, 0x05, 0xEB, 0xC2, 0x06, 0x00, 0x23, + 0xC6, 0xE9, 0x00, 0x33, + 0x52, 0x1C, 0xD2, 0xB2, 0x17, 0x2A, 0xF6, 0xD3, 0x8A, 0x78, 0x2C, 0xE0, + 0x00, 0xEB, 0xC2, 0x03, + 0xD3, 0xE9, 0x00, 0x76, 0x57, 0xEA, 0x06, 0x03, 0x23, 0xD0, 0x74, 0x08, + 0x4F, 0xEA, 0x37, 0x03, + 0x17, 0xEB, 0x07, 0x0E, 0x46, 0xEB, 0x06, 0x0C, 0x44, 0xEA, 0x0C, 0x04, + 0x34, 0x43, 0x43, 0xEA, + 0x0E, 0x03, 0x05, 0xEB, 0xC2, 0x06, 0x3B, 0x43, 0xD6, 0xE9, 0x00, 0x7C, + 0x1F, 0x43, 0x4C, 0xEA, + 0x04, 0x0C, 0xC6, 0xE9, 0x00, 0x7C, 0xD6, 0xE9, 0x02, 0x7C, 0x1F, 0x43, + 0x4C, 0xEA, 0x04, 0x0C, + 0xC6, 0xE9, 0x02, 0x7C, 0xD6, 0xE9, 0x04, 0x7C, 0x1F, 0x43, 0x4C, 0xEA, + 0x04, 0x0C, 0xC6, 0xE9, + 0x04, 0x7C, 0x52, 0x1C, 0xD2, 0xB2, 0xCB, 0x78, 0x93, 0x42, 0xCF, 0xD2, + 0x00, 0x22, 0x50, 0x4B, + 0x0A, 0xE0, 0x00, 0xBF, 0x05, 0xEB, 0xC2, 0x04, 0x00, 0xEB, 0xC2, 0x07, + 0xD4, 0xE9, 0x02, 0x64, + 0xC7, 0xE9, 0x00, 0x64, 0x52, 0x1C, 0xD2, 0xB2, 0x1C, 0x78, 0xA2, 0x42, + 0xF2, 0xD3, 0x08, 0x78, + 0x08, 0xB1, 0x40, 0x1E, 0x08, 0x70, 0x45, 0x4A, 0x48, 0x78, 0x12, 0x78, + 0x52, 0x1E, 0x90, 0x42, + 0x01, 0xDA, 0x40, 0x1C, 0x48, 0x70, 0x88, 0x78, 0x08, 0xB1, 0x40, 0x1E, + 0x88, 0x70, 0x1A, 0x78, + 0xC8, 0x78, 0x52, 0x1E, 0x90, 0x42, 0x01, 0xDA, 0x40, 0x1C, 0xC8, 0x70, + 0xF0, 0xBD, 0x2D, 0xE9, + 0xFF, 0x5F, 0x94, 0x78, 0x16, 0x46, 0x04, 0xEB, 0x84, 0x02, 0x8B, 0x46, + 0x00, 0xEB, 0xC2, 0x00, + 0x00, 0x21, 0x00, 0xF1, 0x29, 0x08, 0x20, 0xE0, 0x00, 0x20, 0x81, 0x46, + 0x82, 0x46, 0x35, 0x78, + 0x77, 0x78, 0x0F, 0xE0, 0x18, 0xF8, 0x05, 0x20, 0x5A, 0x45, 0x09, 0xD1, + 0x2A, 0x46, 0x01, 0x20, + 0x00, 0x21, 0x08, 0xF0, 0xEB, 0xFA, 0x41, 0xEA, 0x0A, 0x0A, 0x40, 0xEA, + 0x09, 0x09, 0x01, 0x21, + 0x6D, 0x1C, 0xED, 0xB2, 0xAF, 0x42, 0xED, 0xD2, 0x03, 0x9B, 0x08, 0xF1, + 0x28, 0x08, 0x03, 0xEB, + 0xC4, 0x03, 0x64, 0x1C, 0xC3, 0xE9, 0x00, 0x9A, 0xE4, 0xB2, 0xF0, 0x78, + 0xA0, 0x42, 0xDB, 0xD2, + 0x04, 0xB0, 0x08, 0x46, 0xBD, 0xE8, 0xF0, 0x9F, 0x0A, 0x78, 0x43, 0x78, + 0x9A, 0x42, 0x0B, 0xD8, + 0x4A, 0x78, 0x03, 0x78, 0x9A, 0x42, 0x07, 0xD3, 0x8A, 0x78, 0xC3, 0x78, + 0x9A, 0x42, 0x03, 0xD8, + 0xC9, 0x78, 0x80, 0x78, 0x81, 0x42, 0x01, 0xD2, 0x00, 0x20, 0x70, 0x47, + 0x01, 0x20, 0x70, 0x47, + 0x70, 0xB5, 0x0C, 0x78, 0x8B, 0x78, 0x4D, 0x78, 0xC9, 0x78, 0x9A, 0xB1, + 0x0C, 0xB1, 0x64, 0x1E, + 0xE4, 0xB2, 0x12, 0x4A, 0x12, 0x78, 0x52, 0x1E, 0x95, 0x42, 0x01, 0xDA, + 0x6D, 0x1C, 0xED, 0xB2, + 0x0B, 0xB1, 0x5B, 0x1E, 0xDB, 0xB2, 0x0E, 0x4A, 0x12, 0x78, 0x52, 0x1E, + 0x91, 0x42, 0x01, 0xDA, + 0x49, 0x1C, 0xC9, 0xB2, 0x03, 0xEB, 0x83, 0x02, 0x00, 0xEB, 0xC2, 0x02, + 0x00, 0x26, 0x29, 0x32, + 0x09, 0xE0, 0x20, 0x46, 0x02, 0xE0, 0x16, 0x54, 0x40, 0x1C, 0xC0, 0xB2, + 0xA8, 0x42, 0xFA, 0xD9, + 0x5B, 0x1C, 0xDB, 0xB2, 0x28, 0x32, 0x8B, 0x42, 0xF3, 0xD9, 0x70, 0xBD, + 0xEE, 0x06, 0x10, 0x00, + 0xED, 0x06, 0x10, 0x00, 0x40, 0x47, 0x10, 0x00, 0x2D, 0xE9, 0xF0, 0x47, + 0xDF, 0xF8, 0xA0, 0x90, + 0x80, 0x46, 0x0C, 0x78, 0x99, 0xF8, 0x00, 0x00, 0x0D, 0x46, 0x80, 0x1E, + 0x01, 0x2C, 0x05, 0xD9, + 0x69, 0x78, 0x81, 0x42, 0x02, 0xDA, 0x00, 0x20, 0xBD, 0xE8, 0xF0, 0x87, + 0x02, 0x2C, 0x01, 0xD2, + 0x02, 0x27, 0x01, 0xE0, 0x6F, 0x78, 0xC4, 0xB2, 0x4F, 0xF0, 0x00, 0x0A, + 0xA9, 0x78, 0xD8, 0xF8, + 0x08, 0x00, 0x08, 0xF0, 0x2B, 0xFC, 0x06, 0x46, 0xA9, 0x78, 0xD8, 0xF8, + 0x04, 0x00, 0x08, 0xF0, + 0x25, 0xFC, 0xAA, 0x78, 0x01, 0x46, 0xED, 0x78, 0x99, 0xF8, 0x00, 0x30, + 0x13, 0xE0, 0x20, 0x46, + 0x0A, 0xE0, 0x00, 0xBF, 0x36, 0xF9, 0x10, 0xC0, 0x31, 0xF9, 0x10, 0x80, + 0xC4, 0x45, 0x02, 0xD0, + 0x4F, 0xF0, 0x01, 0x0A, 0x02, 0xE0, 0x40, 0x1C, 0xB8, 0x42, 0xF3, 0xD9, + 0x06, 0xEB, 0x43, 0x06, + 0x01, 0xEB, 0x43, 0x01, 0x52, 0x1C, 0x95, 0x42, 0xE9, 0xD2, 0x50, 0x46, + 0xCC, 0xE7, 0x08, 0x49, + 0x09, 0x78, 0x49, 0x1E, 0xCA, 0xB2, 0x90, 0xF8, 0x3E, 0x10, 0x09, 0xB1, + 0x91, 0x42, 0x05, 0xD1, + 0x81, 0x8C, 0x41, 0xF0, 0x04, 0x01, 0x81, 0x84, 0x01, 0x20, 0x70, 0x47, + 0x00, 0x20, 0x70, 0x47, + 0xEE, 0x06, 0x10, 0x00, 0x30, 0xB5, 0xB0, 0xB1, 0x88, 0x42, 0x14, 0xD0, + 0x02, 0xD2, 0x0A, 0x46, + 0x01, 0x46, 0x10, 0x46, 0xDE, 0x4A, 0x53, 0x68, 0x03, 0xEB, 0x40, 0x03, + 0x1C, 0x78, 0x8C, 0x42, + 0x09, 0xD0, 0x5C, 0x78, 0x8C, 0x42, 0x06, 0xD0, 0x95, 0x68, 0x2A, 0x5C, + 0x02, 0x2A, 0x02, 0xD2, + 0x54, 0x1C, 0x2C, 0x54, 0x99, 0x54, 0x08, 0x46, 0x30, 0xBD, 0xF0, 0xB5, + 0x03, 0xEB, 0x82, 0x04, + 0xA5, 0x78, 0x05, 0xEB, 0x85, 0x06, 0x00, 0xEB, 0xC6, 0x06, 0xE0, 0x78, + 0x28, 0x36, 0xA8, 0x42, + 0x0F, 0xD3, 0x20, 0x78, 0x07, 0xE0, 0x37, 0x18, 0x97, 0xF8, 0x01, 0xC0, + 0x94, 0x45, 0x00, 0xD1, + 0x79, 0x70, 0x40, 0x1C, 0xC0, 0xB2, 0x67, 0x78, 0x87, 0x42, 0xF4, 0xD2, + 0x6D, 0x1C, 0xED, 0xB2, + 0xEB, 0xE7, 0x03, 0xEB, 0x81, 0x00, 0x25, 0x68, 0x05, 0x60, 0x03, 0xEB, + 0x42, 0x00, 0x03, 0xEB, + 0x41, 0x01, 0x30, 0xF8, 0xB4, 0x2F, 0xA1, 0xF8, 0xB4, 0x20, 0xC2, 0x49, + 0x09, 0x68, 0x21, 0x60, + 0x00, 0x21, 0x01, 0x80, 0x93, 0xF8, 0x0E, 0x01, 0x40, 0x1E, 0x83, 0xF8, + 0x0E, 0x01, 0xF0, 0xBD, + 0x2D, 0xE9, 0xFF, 0x4F, 0x88, 0x46, 0x08, 0x46, 0x09, 0x78, 0x8F, 0xB0, + 0xFF, 0x29, 0x13, 0xD0, + 0x8A, 0x46, 0xB9, 0x49, 0x42, 0x78, 0x09, 0x78, 0x8A, 0x42, 0x00, 0xD2, + 0x51, 0x1C, 0xCE, 0xB2, + 0xB6, 0x49, 0x90, 0xF8, 0x02, 0x90, 0xC0, 0x78, 0x09, 0x78, 0x88, 0x42, + 0x01, 0xD2, 0x40, 0x1C, + 0x00, 0xE0, 0x08, 0x46, 0xC7, 0xB2, 0x04, 0xE0, 0x4F, 0xF0, 0x00, 0x0A, + 0x56, 0x46, 0xD1, 0x46, + 0x57, 0x46, 0x11, 0x98, 0x4F, 0xF0, 0x00, 0x0B, 0x08, 0xB1, 0x98, 0xF8, + 0x0E, 0xB1, 0xA8, 0x4A, + 0x11, 0x68, 0x01, 0xF1, 0x5A, 0x00, 0xC2, 0xE9, 0x01, 0x10, 0x02, 0x46, + 0x5F, 0xF0, 0x2C, 0x00, + 0x00, 0x23, 0x01, 0xF8, 0x10, 0x00, 0x13, 0x54, 0x40, 0x1E, 0xC0, 0xB2, + 0xF8, 0xD2, 0x0B, 0xF1, + 0x01, 0x00, 0x0D, 0x90, 0xC0, 0xB2, 0x06, 0x90, 0x0F, 0x98, 0x09, 0xEB, + 0x89, 0x01, 0x00, 0xEB, + 0xC1, 0x05, 0x09, 0xF1, 0x01, 0x00, 0x09, 0x95, 0x39, 0xE0, 0x00, 0xBF, + 0x0A, 0xF1, 0x01, 0x00, + 0x0A, 0x95, 0xC4, 0xB2, 0x28, 0x35, 0x2E, 0xE0, 0x29, 0x19, 0x08, 0x68, + 0xE0, 0xB1, 0x28, 0x5D, + 0x38, 0xB3, 0x11, 0xF8, 0x01, 0x1C, 0x00, 0x20, 0x01, 0xB1, 0x08, 0x46, + 0x0A, 0x99, 0x09, 0x5D, + 0x09, 0xB1, 0xFF, 0xF7, 0x57, 0xFF, 0x12, 0x99, 0x61, 0xB1, 0x0A, 0x99, + 0x21, 0x44, 0x0E, 0x91, + 0x11, 0xF8, 0x01, 0x1C, 0x09, 0xB1, 0xFF, 0xF7, 0x4D, 0xFF, 0x0E, 0x99, + 0x49, 0x78, 0x09, 0xB1, + 0xFF, 0xF7, 0x48, 0xFF, 0x18, 0xB1, 0x0B, 0xE0, 0xE4, 0x1C, 0xE4, 0xB2, + 0x09, 0xE0, 0x06, 0x98, + 0x2D, 0x28, 0x03, 0xD2, 0x41, 0x1C, 0xC9, 0xB2, 0x06, 0x91, 0x01, 0xE0, + 0x40, 0x1E, 0xC0, 0xB2, + 0x28, 0x55, 0x64, 0x1C, 0xE4, 0xB2, 0xB4, 0x42, 0xCE, 0xD9, 0x07, 0x98, + 0x40, 0x1C, 0xC0, 0xB2, + 0x07, 0x90, 0xB8, 0x42, 0xC2, 0xD9, 0x06, 0x98, 0x00, 0xF0, 0x99, 0xF8, + 0x06, 0x90, 0x0D, 0x98, + 0x00, 0x22, 0xC0, 0xB2, 0x77, 0x4C, 0x09, 0xE0, 0x08, 0xEB, 0x80, 0x03, + 0x21, 0x68, 0x19, 0x60, + 0x08, 0xEB, 0x40, 0x01, 0x40, 0x1C, 0xA1, 0xF8, 0xB4, 0x20, 0xC0, 0xB2, + 0x06, 0x99, 0x88, 0x42, + 0xF2, 0xD3, 0x09, 0x98, 0x4D, 0x46, 0x28, 0x30, 0x00, 0x90, 0x39, 0xE0, + 0x54, 0x46, 0x30, 0xE0, + 0x20, 0x44, 0x01, 0x21, 0x09, 0x58, 0xE9, 0xB3, 0x41, 0x78, 0x41, 0xB3, + 0x68, 0x4A, 0x52, 0x68, + 0x12, 0xF8, 0x11, 0x10, 0x41, 0x70, 0x08, 0xEB, 0x41, 0x00, 0x08, 0xEB, + 0x81, 0x09, 0x30, 0xF8, + 0xB4, 0x2F, 0x52, 0x1C, 0x02, 0x80, 0x99, 0xF8, 0x00, 0x10, 0x20, 0x46, + 0x04, 0xF0, 0xEC, 0xF9, + 0x89, 0xF8, 0x00, 0x00, 0x99, 0xF8, 0x01, 0x10, 0x20, 0x46, 0x04, 0xF0, + 0xE9, 0xF9, 0x89, 0xF8, + 0x01, 0x00, 0x99, 0xF8, 0x02, 0x10, 0x28, 0x46, 0x04, 0xF0, 0xDE, 0xF9, + 0x89, 0xF8, 0x02, 0x00, + 0x99, 0xF8, 0x03, 0x10, 0x28, 0x46, 0x04, 0xF0, 0xDB, 0xF9, 0x89, 0xF8, + 0x03, 0x00, 0x64, 0x1C, + 0xE4, 0xB2, 0x00, 0x98, 0xB4, 0x42, 0xCB, 0xD3, 0x28, 0x30, 0x6D, 0x1C, + 0xED, 0xB2, 0x00, 0x90, + 0xBD, 0x42, 0xC3, 0xD3, 0x06, 0x98, 0x40, 0x1E, 0xC2, 0xB2, 0x88, 0xF8, + 0x0E, 0x21, 0x11, 0x98, + 0x00, 0x28, 0x00, 0xE0, 0x0B, 0xE0, 0x0D, 0xD0, 0xA2, 0xEB, 0x0B, 0x00, + 0x01, 0x28, 0x09, 0xD1, + 0x11, 0x99, 0x0F, 0x98, 0x13, 0xB0, 0x43, 0x46, 0xBD, 0xE8, 0xF0, 0x4F, + 0xDD, 0xE6, 0xE4, 0x1C, + 0xE4, 0xB2, 0xDC, 0xE7, 0x13, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, 0xF0, 0xB5, + 0x02, 0xEB, 0x81, 0x03, + 0x00, 0x26, 0x9D, 0x78, 0x18, 0xE0, 0x00, 0xBF, 0x05, 0xEB, 0x85, 0x04, + 0x00, 0xEB, 0xC4, 0x07, + 0x1C, 0x78, 0x28, 0x37, 0x0A, 0xE0, 0x00, 0xBF, 0x07, 0xEB, 0x04, 0x0C, + 0x9C, 0xF8, 0x01, 0xE0, + 0x8E, 0x45, 0x01, 0xD1, 0x8C, 0xF8, 0x01, 0x60, 0x64, 0x1C, 0xE4, 0xB2, + 0x93, 0xF8, 0x01, 0xC0, + 0xA4, 0x45, 0xF1, 0xD2, 0x6D, 0x1C, 0xED, 0xB2, 0xDC, 0x78, 0xAC, 0x42, + 0xE4, 0xD2, 0x31, 0x48, + 0x00, 0x68, 0x18, 0x60, 0x02, 0xEB, 0x41, 0x00, 0xA0, 0xF8, 0xB4, 0x60, + 0xF0, 0xBD, 0x2D, 0xE9, + 0xF0, 0x5F, 0x07, 0x46, 0x00, 0x25, 0xDF, 0xF8, 0xA8, 0xB0, 0x2C, 0xE0, + 0xDB, 0xF8, 0x08, 0x10, + 0x4E, 0x5D, 0xDB, 0xF8, 0x04, 0x10, 0x01, 0xEB, 0x45, 0x0A, 0x1A, 0xE0, + 0x0A, 0xEB, 0x06, 0x00, + 0x80, 0x46, 0x10, 0xF8, 0x01, 0x1C, 0x10, 0xF8, 0x02, 0x9C, 0x08, 0x46, + 0x00, 0xF0, 0x35, 0xF8, + 0x04, 0x46, 0x48, 0x46, 0x00, 0xF0, 0x31, 0xF8, 0x84, 0x42, 0x02, 0xD2, + 0x01, 0x46, 0x20, 0x46, + 0x0C, 0x46, 0xDB, 0xF8, 0x04, 0x10, 0x76, 0x1E, 0xF6, 0xB2, 0x01, 0xF8, + 0x10, 0x40, 0x08, 0xF8, + 0x02, 0x4C, 0x01, 0x2E, 0xE2, 0xD8, 0x04, 0xD1, 0x28, 0x46, 0x00, 0xF0, + 0x1E, 0xF8, 0x8A, 0xF8, + 0x00, 0x00, 0x6D, 0x1C, 0xED, 0xB2, 0xBD, 0x42, 0xD0, 0xD3, 0x00, 0x21, + 0x08, 0x46, 0xDB, 0xF8, + 0x04, 0x40, 0x0D, 0xE0, 0x04, 0xEB, 0x40, 0x02, 0x13, 0x78, 0x83, 0x42, + 0x03, 0xD1, 0x11, 0x70, + 0x49, 0x1C, 0xC9, 0xB2, 0x02, 0xE0, 0x14, 0xF8, 0x13, 0x30, 0x13, 0x70, + 0x40, 0x1C, 0xC0, 0xB2, + 0xB8, 0x42, 0xEF, 0xD3, 0x08, 0x46, 0xBD, 0xE8, 0xF0, 0x9F, 0x05, 0x4A, + 0xD2, 0xE9, 0x01, 0x12, + 0x02, 0xE0, 0x00, 0xBF, 0x11, 0xF8, 0x10, 0x00, 0x13, 0x5C, 0x00, 0x2B, + 0xFA, 0xD1, 0x70, 0x47, + 0x60, 0x06, 0x10, 0x00, 0x36, 0x7A, 0x01, 0x00, 0xEE, 0x06, 0x10, 0x00, + 0xED, 0x06, 0x10, 0x00, + 0xF0, 0xB5, 0xB2, 0xF9, 0x00, 0x40, 0x06, 0x68, 0xB4, 0x42, 0x02, 0xDD, + 0xB3, 0xF9, 0x00, 0x10, + 0x30, 0xE0, 0x02, 0xEB, 0x41, 0x04, 0x34, 0xF9, 0x02, 0x5C, 0xB5, 0x42, + 0x04, 0xDA, 0x03, 0xEB, + 0x41, 0x01, 0x31, 0xF9, 0x02, 0x1C, 0x25, 0xE0, 0x00, 0x25, 0x2C, 0x46, + 0x4F, 0x1E, 0x0C, 0xE0, + 0x32, 0xF9, 0x14, 0xC0, 0xB4, 0x45, 0x07, 0xDC, 0x02, 0xEB, 0x44, 0x0C, + 0xBC, 0xF9, 0x02, 0xC0, + 0xB4, 0x45, 0x01, 0xDB, 0x25, 0x46, 0x0C, 0x46, 0x64, 0x1C, 0xBC, 0x42, + 0xF0, 0xDB, 0x32, 0xF9, + 0x15, 0x40, 0x03, 0xEB, 0x45, 0x01, 0x33, 0xF9, 0x15, 0x30, 0xB1, 0xF9, + 0x02, 0x10, 0x02, 0xEB, + 0x45, 0x02, 0x36, 0x1B, 0xC9, 0x1A, 0xB2, 0xF9, 0x02, 0x20, 0x71, 0x43, + 0x12, 0x1B, 0x91, 0xFB, + 0xF2, 0xF1, 0x19, 0x44, 0x01, 0x60, 0xF0, 0xBD, 0x2D, 0xE9, 0xF0, 0x41, + 0x07, 0x46, 0x2A, 0x48, + 0x00, 0x68, 0x00, 0xF2, 0x0F, 0x53, 0xE9, 0xB1, 0x01, 0x29, 0x22, 0xD0, + 0x02, 0x29, 0x2C, 0xD0, + 0x03, 0x29, 0x31, 0xD0, 0x04, 0x29, 0x36, 0xD0, 0x00, 0x21, 0x0C, 0x46, + 0x0E, 0xEB, 0x01, 0x00, + 0x20, 0x44, 0x64, 0x28, 0x3D, 0xDC, 0x01, 0x29, 0x03, 0xDD, 0x38, 0x1D, + 0x63, 0x46, 0xFF, 0xF7, + 0xA7, 0xFF, 0x01, 0x2C, 0x35, 0xDD, 0x38, 0x46, 0x33, 0x46, 0x2A, 0x46, + 0x21, 0x46, 0xBD, 0xE8, + 0xF0, 0x41, 0x9D, 0xE7, 0x90, 0xF8, 0x08, 0x15, 0x90, 0xF8, 0x09, 0x45, + 0x90, 0xF8, 0x0A, 0xE5, + 0x05, 0xE0, 0x90, 0xF8, 0x08, 0x15, 0x90, 0xF8, 0x09, 0x45, 0x90, 0xF8, + 0x0B, 0xE5, 0x9C, 0x46, + 0x03, 0xEB, 0x41, 0x06, 0x03, 0xEB, 0x4E, 0x02, 0x18, 0xE0, 0x90, 0xF8, + 0x08, 0x15, 0x90, 0xF8, + 0x09, 0x45, 0x90, 0xF8, 0x0C, 0xE5, 0xF2, 0xE7, 0x90, 0xF8, 0x08, 0x15, + 0x90, 0xF8, 0x09, 0x45, + 0x90, 0xF8, 0x0D, 0xE5, 0xEB, 0xE7, 0x90, 0xF8, 0x08, 0x15, 0x90, 0xF8, + 0x09, 0x45, 0x90, 0xF8, + 0x0E, 0x05, 0x9C, 0x46, 0x03, 0xEB, 0x41, 0x06, 0x03, 0xEB, 0x40, 0x02, + 0x02, 0xEB, 0x41, 0x05, + 0xBC, 0xE7, 0xBD, 0xE8, 0xF0, 0x81, 0x00, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x30, 0xB5, 0x42, 0x78, + 0x03, 0x78, 0xCD, 0x8C, 0xD2, 0x1A, 0xC3, 0x78, 0x80, 0x78, 0x52, 0x1C, + 0x1B, 0x1A, 0x5B, 0x1C, + 0x05, 0xFB, 0x02, 0xF0, 0xD4, 0x18, 0x5D, 0x43, 0x40, 0x00, 0x6A, 0x00, + 0xB0, 0xFB, 0xF4, 0xF0, + 0xB2, 0xFB, 0xF4, 0xF2, 0x90, 0x42, 0x02, 0xD9, 0xC8, 0x85, 0x0A, 0x86, + 0x30, 0xBD, 0xCA, 0x85, + 0x08, 0x86, 0x30, 0xBD, 0x2D, 0xE9, 0xFF, 0x4F, 0x00, 0x27, 0x83, 0xB0, + 0x15, 0x46, 0x01, 0x97, + 0x00, 0x97, 0x3E, 0x46, 0x3C, 0x46, 0xBB, 0x46, 0x91, 0x78, 0x08, 0xF0, + 0x61, 0xF9, 0x82, 0x46, + 0xAB, 0x78, 0x95, 0xF8, 0x03, 0x90, 0x28, 0xE0, 0x29, 0x78, 0x95, 0xF8, + 0x01, 0xC0, 0x8E, 0x46, + 0x1D, 0xE0, 0x00, 0xBF, 0x1A, 0xF8, 0x01, 0x20, 0x04, 0x98, 0x82, 0x42, + 0x15, 0xD1, 0xA1, 0xEB, + 0x0E, 0x02, 0x52, 0x1C, 0xDD, 0xF8, 0x00, 0x80, 0xA9, 0xEB, 0x03, 0x00, + 0x02, 0xFB, 0x02, 0x88, + 0x40, 0x1C, 0xCD, 0xF8, 0x00, 0x80, 0xDD, 0xF8, 0x04, 0x80, 0x16, 0x44, + 0x02, 0xFB, 0x00, 0x82, + 0x00, 0xFB, 0x00, 0xBB, 0x04, 0x44, 0x7F, 0x1C, 0x01, 0x92, 0x49, 0x1C, + 0xC9, 0xB2, 0x8C, 0x45, + 0xE0, 0xD2, 0x5B, 0x1C, 0x0A, 0xF1, 0x28, 0x0A, 0xDB, 0xB2, 0x99, 0x45, + 0xD4, 0xD2, 0x4F, 0xF4, + 0x7A, 0x70, 0x70, 0x43, 0x90, 0xFB, 0xF7, 0xF1, 0x4F, 0xF4, 0x7A, 0x70, + 0x60, 0x43, 0x90, 0xFB, + 0xF7, 0xF3, 0x4F, 0xF4, 0x7A, 0x72, 0x01, 0xFB, 0x04, 0xF0, 0x71, 0x43, + 0x90, 0xFB, 0xF2, 0xF7, + 0x91, 0xFB, 0xF2, 0xF6, 0x63, 0x43, 0x93, 0xFB, 0xF2, 0xF3, 0x01, 0x98, + 0x00, 0x99, 0xC0, 0x1B, + 0x89, 0x1B, 0xAB, 0xEB, 0x03, 0x02, 0x06, 0x9C, 0x8B, 0x1A, 0x00, 0xD5, + 0x5B, 0x42, 0x00, 0x28, + 0x00, 0xDA, 0x40, 0x42, 0x03, 0xEB, 0x40, 0x00, 0x11, 0x44, 0x0A, 0x18, + 0x08, 0x1A, 0x52, 0x10, + 0x40, 0x10, 0x4F, 0xF4, 0x7A, 0x71, 0x48, 0x43, 0x90, 0xFB, 0xF2, 0xF0, + 0xC0, 0xF5, 0x7A, 0x70, + 0xA0, 0x85, 0x06, 0x99, 0x07, 0xB0, 0x28, 0x46, 0xBD, 0xE8, 0xF0, 0x4F, + 0x6E, 0xE7, 0x01, 0x20, + 0x70, 0x47, 0x00, 0x00, 0x2D, 0xE9, 0xF0, 0x5F, 0x8A, 0x46, 0x0A, 0x9F, + 0x00, 0x25, 0x98, 0x46, + 0x14, 0x46, 0x83, 0x46, 0x2E, 0x46, 0x39, 0x46, 0x08, 0xF0, 0xF0, 0xF8, + 0x30, 0xF9, 0x18, 0x00, + 0xA0, 0x42, 0x2E, 0xDD, 0x78, 0x1E, 0x44, 0xB2, 0x7F, 0x1C, 0x27, 0xE0, + 0x00, 0x2C, 0x21, 0xDB, + 0xB0, 0x48, 0x00, 0x78, 0x84, 0x42, 0x1D, 0xDA, 0xE1, 0xB2, 0x58, 0x46, + 0x08, 0xF0, 0xDE, 0xF8, + 0xAD, 0x49, 0x81, 0x46, 0xA8, 0xF1, 0x01, 0x00, 0x0A, 0x78, 0x40, 0xB2, + 0x08, 0xF1, 0x01, 0x01, + 0x0D, 0xE0, 0x00, 0x28, 0x07, 0xDB, 0x90, 0x42, 0x05, 0xDA, 0x39, 0xF9, + 0x10, 0xC0, 0x3A, 0xF9, + 0x15, 0x30, 0x0C, 0xFB, 0x03, 0x66, 0x6D, 0x1C, 0x40, 0x1C, 0xED, 0xB2, + 0x40, 0xB2, 0x88, 0x42, + 0xEF, 0xDD, 0x01, 0xE0, 0xED, 0x1C, 0xED, 0xB2, 0x64, 0x1C, 0x64, 0xB2, + 0xBC, 0x42, 0xD5, 0xDD, + 0x00, 0xE0, 0x06, 0x46, 0x01, 0x20, 0x96, 0xFB, 0xF0, 0xF0, 0x00, 0xB2, + 0xBD, 0xE8, 0xF0, 0x9F, + 0x2D, 0xE9, 0xFF, 0x4F, 0x83, 0xB0, 0x91, 0x46, 0x10, 0x99, 0x08, 0x78, + 0x00, 0xB1, 0x40, 0x1E, + 0xC4, 0xB2, 0x88, 0x78, 0x00, 0xB1, 0x40, 0x1E, 0xDF, 0xF8, 0x4C, 0xA2, + 0xC5, 0xB2, 0x4A, 0x78, + 0x9A, 0xF8, 0x00, 0x00, 0x43, 0x1E, 0x9A, 0x42, 0x01, 0xDA, 0x50, 0x1C, + 0x00, 0xE0, 0x40, 0x1E, + 0xDF, 0xF8, 0x30, 0xB2, 0xC6, 0xB2, 0xC9, 0x78, 0x9B, 0xF8, 0x00, 0x00, + 0x42, 0x1E, 0x91, 0x42, + 0x01, 0xDA, 0x48, 0x1C, 0x00, 0xE0, 0x40, 0x1E, 0xC7, 0xB2, 0xCC, 0xB9, + 0xA8, 0x46, 0x12, 0xE0, + 0x01, 0x46, 0x03, 0x98, 0x08, 0xF0, 0x98, 0xF8, 0xCD, 0xE9, 0x00, 0x80, + 0x23, 0x46, 0x49, 0x46, + 0x06, 0x9A, 0x04, 0x98, 0xFF, 0xF7, 0x86, 0xFF, 0x01, 0x99, 0x21, 0xF8, + 0x14, 0x00, 0x08, 0xF1, + 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x08, 0x40, 0x46, 0xB8, 0x45, 0xE9, 0xD9, + 0x64, 0x1C, 0xE4, 0xB2, + 0x79, 0x48, 0x00, 0x78, 0x40, 0x1E, 0x86, 0x42, 0x19, 0xD1, 0xA8, 0x46, + 0x12, 0xE0, 0x01, 0x46, + 0x03, 0x98, 0x08, 0xF0, 0x79, 0xF8, 0x82, 0x46, 0x33, 0x46, 0xCD, 0xF8, + 0x00, 0x80, 0x49, 0x46, + 0x06, 0x9A, 0x04, 0x98, 0xFF, 0xF7, 0x66, 0xFF, 0x2A, 0xF8, 0x16, 0x00, + 0x08, 0xF1, 0x01, 0x00, + 0x00, 0xF0, 0xFF, 0x08, 0x40, 0x46, 0xB8, 0x45, 0xE9, 0xD9, 0x76, 0x1E, + 0xF6, 0xB2, 0xC5, 0xB9, + 0x29, 0x46, 0x03, 0x98, 0x08, 0xF0, 0x60, 0xF8, 0x82, 0x46, 0xA0, 0x46, + 0x0C, 0xE0, 0x03, 0x46, + 0x00, 0x95, 0x49, 0x46, 0x06, 0x9A, 0x04, 0x98, 0xFF, 0xF7, 0x4C, 0xFF, + 0x2A, 0xF8, 0x18, 0x00, + 0x08, 0xF1, 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x08, 0x40, 0x46, 0xB0, 0x45, + 0xEF, 0xD9, 0x6D, 0x1C, + 0xED, 0xB2, 0x9B, 0xF8, 0x00, 0x00, 0x40, 0x1E, 0x87, 0x42, 0x18, 0xD1, + 0x39, 0x46, 0x03, 0x98, + 0x08, 0xF0, 0x42, 0xF8, 0x82, 0x46, 0xA0, 0x46, 0x0C, 0xE0, 0x03, 0x46, + 0x00, 0x97, 0x49, 0x46, + 0x06, 0x9A, 0x04, 0x98, 0xFF, 0xF7, 0x2E, 0xFF, 0x2A, 0xF8, 0x18, 0x00, + 0x08, 0xF1, 0x01, 0x00, + 0x00, 0xF0, 0xFF, 0x08, 0x40, 0x46, 0xB0, 0x45, 0xEF, 0xD9, 0x7F, 0x1E, + 0xFF, 0xB2, 0xB9, 0xF9, + 0x12, 0x00, 0x5F, 0xEA, 0x00, 0x0A, 0x64, 0xD1, 0x4F, 0xF0, 0x01, 0x0A, + 0x61, 0xE0, 0x29, 0x46, + 0x03, 0x98, 0x08, 0xF0, 0x21, 0xF8, 0x01, 0x90, 0x68, 0x1E, 0xC1, 0xB2, + 0x04, 0x98, 0x08, 0xF0, + 0x0D, 0xF8, 0x00, 0x90, 0x29, 0x46, 0x04, 0x98, 0x08, 0xF0, 0x08, 0xF8, + 0x21, 0x46, 0x4C, 0xE0, + 0x30, 0xF9, 0x11, 0x20, 0x06, 0x9B, 0x9A, 0x42, 0x42, 0xDD, 0x00, 0x9A, + 0xB9, 0xF9, 0x00, 0x80, + 0x02, 0xEB, 0x41, 0x02, 0x00, 0x23, 0x32, 0xF9, 0x02, 0xCD, 0x0C, 0xFB, + 0x08, 0x33, 0xB2, 0xF9, + 0x02, 0xC0, 0xB9, 0xF9, 0x02, 0x80, 0x0C, 0xFB, 0x08, 0x33, 0xB2, 0xF9, + 0x04, 0xC0, 0xB9, 0xF9, + 0x04, 0x80, 0x0C, 0xFB, 0x08, 0x3B, 0x34, 0x4B, 0xB9, 0xF9, 0x06, 0x80, + 0x93, 0xF8, 0x00, 0xE0, + 0x02, 0xEB, 0x4E, 0x02, 0xB2, 0xF9, 0x00, 0xC0, 0x0C, 0xFB, 0x08, 0xB3, + 0xB2, 0xF9, 0x02, 0xC0, + 0xB9, 0xF9, 0x08, 0x80, 0x0C, 0xFB, 0x08, 0x33, 0xB2, 0xF9, 0x04, 0xC0, + 0xB9, 0xF9, 0x0A, 0x80, + 0x02, 0xEB, 0x4E, 0x02, 0x0C, 0xFB, 0x08, 0x3B, 0xB2, 0xF9, 0x00, 0xC0, + 0xB9, 0xF9, 0x0C, 0x80, + 0x0C, 0xFB, 0x08, 0xB3, 0xB2, 0xF9, 0x02, 0xC0, 0xB9, 0xF9, 0x0E, 0x80, + 0x0C, 0xFB, 0x08, 0x33, + 0xB2, 0xF9, 0x04, 0xC0, 0xB9, 0xF9, 0x10, 0x20, 0x0C, 0xFB, 0x02, 0x32, + 0x92, 0xFB, 0xFA, 0xF2, + 0x01, 0x9B, 0x23, 0xF8, 0x11, 0x20, 0x49, 0x1C, 0xC9, 0xB2, 0xB1, 0x42, + 0xB0, 0xD9, 0x6D, 0x1C, + 0xED, 0xB2, 0xBD, 0x42, 0x9B, 0xD9, 0x07, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, + 0x1C, 0xB5, 0x17, 0x4C, + 0x24, 0x68, 0x01, 0x94, 0x01, 0xAC, 0x00, 0x94, 0xFF, 0xF7, 0xF2, 0xFE, + 0x1C, 0xBD, 0x38, 0xB5, + 0x16, 0x4D, 0x00, 0x90, 0x12, 0x48, 0x0C, 0x46, 0x12, 0x4A, 0x00, 0x68, + 0xB0, 0xF9, 0xE2, 0x32, + 0x11, 0x48, 0x01, 0x68, 0x28, 0x68, 0xFF, 0xF7, 0xE3, 0xFE, 0x28, 0x68, + 0xA0, 0x60, 0x38, 0xBD, + 0x38, 0xB5, 0x0E, 0x4D, 0x00, 0x90, 0x0A, 0x48, 0x0C, 0x46, 0x0A, 0x4A, + 0x00, 0x68, 0x14, 0x3A, + 0xB0, 0xF9, 0xE2, 0x32, 0x08, 0x48, 0x01, 0x68, 0x28, 0x68, 0xFF, 0xF7, + 0xD1, 0xFE, 0x28, 0x68, + 0x60, 0x60, 0x38, 0xBD, 0xED, 0x06, 0x10, 0x00, 0xEE, 0x06, 0x10, 0x00, + 0x88, 0x06, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, 0x22, 0x7A, 0x01, 0x00, 0x10, 0x07, 0x10, 0x00, + 0x58, 0x06, 0x10, 0x00, + 0x2D, 0xE9, 0xFF, 0x5F, 0x89, 0x46, 0x14, 0x46, 0x91, 0x78, 0x0F, 0x9D, + 0x07, 0xF0, 0x68, 0xFF, + 0x82, 0x46, 0xA1, 0x78, 0xA8, 0x68, 0x07, 0xF0, 0x69, 0xFF, 0x47, 0xF6, + 0xFF, 0x76, 0xA7, 0x78, + 0xF5, 0x43, 0xFE, 0x49, 0x94, 0xF8, 0x03, 0x80, 0x91, 0xF8, 0x00, 0xB0, + 0x18, 0xE0, 0x22, 0x78, + 0x94, 0xF8, 0x01, 0xC0, 0x0D, 0xE0, 0x00, 0xBF, 0x1A, 0xF8, 0x02, 0x10, + 0x49, 0x45, 0x07, 0xD1, + 0x30, 0xF9, 0x12, 0x30, 0xB3, 0x42, 0x00, 0xDA, 0x1E, 0x46, 0xAB, 0x42, + 0x00, 0xDD, 0x1D, 0x46, + 0x52, 0x1C, 0x94, 0x45, 0xF0, 0xD2, 0x0A, 0xF1, 0x28, 0x0A, 0x00, 0xEB, + 0x4B, 0x00, 0x7F, 0x1C, + 0xB8, 0x45, 0xE4, 0xD2, 0x03, 0x98, 0x06, 0x80, 0x0E, 0x98, 0x05, 0x80, + 0xBD, 0xE8, 0xFF, 0x9F, + 0x2D, 0xE9, 0xFF, 0x4F, 0xEA, 0x48, 0x85, 0xB0, 0x9A, 0x46, 0x04, 0x68, + 0x03, 0xA9, 0xCD, 0xE9, + 0x00, 0x1A, 0xDD, 0xE9, 0x05, 0x01, 0x15, 0x46, 0x04, 0xF5, 0x88, 0x79, + 0x02, 0xAB, 0xFF, 0xF7, + 0xB7, 0xFF, 0xE4, 0x48, 0xBD, 0xF9, 0x0C, 0x20, 0xDF, 0xF8, 0x8C, 0xB3, + 0x00, 0x68, 0x5F, 0x46, + 0x90, 0xF8, 0xE4, 0x12, 0xBD, 0xF9, 0x08, 0x00, 0x12, 0x1A, 0x4A, 0x43, + 0x64, 0x21, 0x92, 0xFB, + 0xF1, 0xF1, 0x0E, 0x18, 0x01, 0x22, 0x29, 0x46, 0xDB, 0xF8, 0x00, 0x00, + 0xFF, 0xF7, 0x98, 0xFA, + 0xD7, 0xF8, 0x00, 0x80, 0xDD, 0xE9, 0x05, 0x07, 0x31, 0xB2, 0x01, 0x91, + 0xA9, 0x78, 0x07, 0xF0, + 0x07, 0xFF, 0x06, 0x46, 0xA9, 0x78, 0x40, 0x46, 0x07, 0xF0, 0x10, 0xFF, + 0x00, 0x90, 0xA9, 0x78, + 0xDA, 0xF8, 0x08, 0x00, 0x07, 0xF0, 0x02, 0xFF, 0x01, 0x46, 0xAA, 0x78, + 0x01, 0x23, 0xDF, 0xF8, + 0x2C, 0xE3, 0x1D, 0xE0, 0x28, 0x78, 0x0E, 0xE0, 0x16, 0xF8, 0x00, 0xC0, + 0xBC, 0x45, 0x09, 0xD1, + 0x31, 0xF9, 0x10, 0x80, 0xDD, 0xF8, 0x04, 0xC0, 0xE0, 0x45, 0x03, 0xDD, + 0xDD, 0xF8, 0x00, 0xC0, + 0x0C, 0xF8, 0x00, 0x30, 0x40, 0x1C, 0x95, 0xF8, 0x01, 0xC0, 0x84, 0x45, + 0xEC, 0xD2, 0x00, 0x98, + 0x52, 0x1C, 0x28, 0x30, 0x00, 0x90, 0x9E, 0xF8, 0x00, 0x00, 0x28, 0x36, + 0x01, 0xEB, 0x40, 0x01, + 0xE8, 0x78, 0x90, 0x42, 0xDE, 0xD2, 0x00, 0x20, 0x84, 0xF8, 0x0E, 0x01, + 0x28, 0x68, 0x20, 0x60, + 0x01, 0x23, 0x00, 0x22, 0x21, 0x46, 0x5E, 0x46, 0xDB, 0xF8, 0x00, 0x00, + 0xFF, 0xF7, 0x28, 0xFB, + 0x94, 0xF8, 0x0E, 0x01, 0x01, 0x28, 0x26, 0xD9, 0x01, 0x25, 0x1D, 0xE0, + 0x48, 0x22, 0x51, 0x46, + 0x48, 0x46, 0x07, 0xF0, 0x7F, 0xFD, 0x04, 0xEB, 0x45, 0x00, 0x04, 0xEB, + 0x85, 0x07, 0xB0, 0xF8, + 0xB4, 0x10, 0xA9, 0xF8, 0x26, 0x10, 0x49, 0x46, 0x38, 0x46, 0xFF, 0xF7, + 0x27, 0xFD, 0x3A, 0x46, + 0xE9, 0xB2, 0x4B, 0x46, 0x30, 0x68, 0xFE, 0xF7, 0x0C, 0xFB, 0x3A, 0x46, + 0xE9, 0xB2, 0x4B, 0x46, + 0x30, 0x68, 0xFE, 0xF7, 0x85, 0xFA, 0x6D, 0x1C, 0x94, 0xF8, 0x0E, 0x01, + 0xA8, 0x42, 0xDD, 0xD2, + 0x09, 0xB0, 0xBD, 0xE8, 0xF0, 0x8F, 0xDD, 0xE9, 0x05, 0x01, 0x53, 0x46, + 0x2A, 0x46, 0xFE, 0xF7, + 0x77, 0xFA, 0xF5, 0xE7, 0x2D, 0xE9, 0xFF, 0x5F, 0x06, 0x46, 0x9A, 0x48, + 0x1D, 0x46, 0x0B, 0x46, + 0x00, 0x68, 0xDD, 0xE9, 0x0F, 0xB4, 0x92, 0x46, 0x98, 0x49, 0xB0, 0xF9, + 0xE6, 0x22, 0x00, 0x92, + 0x09, 0x78, 0x11, 0xB1, 0xB0, 0xF9, 0x62, 0x03, 0x00, 0x90, 0x18, 0x46, + 0x0E, 0x99, 0x07, 0xF0, + 0x7F, 0xFE, 0x81, 0x46, 0x30, 0x46, 0x0E, 0x99, 0x07, 0xF0, 0x88, 0xFE, + 0x03, 0x90, 0xA0, 0x68, + 0x0E, 0x99, 0x07, 0xF0, 0x7B, 0xFE, 0x2C, 0x78, 0x6D, 0x78, 0x80, 0x46, + 0x5C, 0xB9, 0x99, 0xF8, + 0x00, 0x10, 0x51, 0x45, 0x06, 0xD1, 0xB8, 0xF9, 0x00, 0x10, 0x59, 0x45, + 0x02, 0xDD, 0x03, 0x99, + 0x01, 0x20, 0x08, 0x70, 0x01, 0x24, 0x81, 0x48, 0x00, 0x78, 0x40, 0x1E, + 0x85, 0x42, 0x54, 0xD1, + 0x19, 0xF8, 0x05, 0x10, 0x51, 0x45, 0x06, 0xD1, 0x38, 0xF9, 0x15, 0x10, + 0x59, 0x45, 0x02, 0xDD, + 0x03, 0x99, 0x01, 0x20, 0x48, 0x55, 0x6D, 0x1E, 0xED, 0xB2, 0x46, 0xE0, + 0x19, 0xF8, 0x04, 0x10, + 0x51, 0x45, 0x41, 0xD1, 0x38, 0xF9, 0x14, 0x00, 0x01, 0x90, 0x58, 0x45, + 0x3C, 0xDD, 0x00, 0x20, + 0x02, 0x90, 0x77, 0x49, 0x0E, 0x98, 0x22, 0x46, 0x01, 0xEB, 0xC0, 0x00, + 0x00, 0x21, 0xD0, 0xE9, + 0x00, 0x67, 0x01, 0x20, 0x07, 0xF0, 0x82, 0xFC, 0x06, 0x40, 0x0F, 0x40, + 0x3E, 0x43, 0x28, 0xD1, + 0xDD, 0xE9, 0x00, 0x01, 0x08, 0xEB, 0x44, 0x02, 0x08, 0x44, 0x32, 0xF9, + 0x02, 0x1C, 0x00, 0xB2, + 0x81, 0x42, 0x05, 0xDD, 0xB2, 0xF9, 0x02, 0x30, 0x83, 0x42, 0x01, 0xDD, + 0x01, 0x23, 0x02, 0x93, + 0x01, 0x2C, 0x09, 0xD9, 0x32, 0xF9, 0x04, 0x3C, 0x83, 0x42, 0x05, 0xDD, + 0xB2, 0xF9, 0x02, 0x30, + 0x83, 0x42, 0x01, 0xDD, 0x01, 0x23, 0x02, 0x93, 0x5C, 0x4B, 0x1B, 0x78, + 0x9B, 0x1E, 0x9C, 0x42, + 0x05, 0xD2, 0x81, 0x42, 0x03, 0xDD, 0xB2, 0xF9, 0x04, 0x10, 0x81, 0x42, + 0x04, 0xDC, 0x02, 0x98, + 0x10, 0xB9, 0x03, 0x99, 0x01, 0x20, 0x08, 0x55, 0x64, 0x1C, 0xAC, 0x42, + 0xB6, 0xD9, 0xCD, 0xE6, + 0x2D, 0xE9, 0xFF, 0x4F, 0xDF, 0xF8, 0x54, 0xA1, 0xDF, 0xF8, 0x48, 0x81, + 0x9B, 0xB0, 0x8B, 0x46, + 0x9A, 0xF8, 0x00, 0x10, 0x28, 0x9E, 0xD8, 0xF8, 0x00, 0x00, 0x1D, 0x46, + 0x11, 0xB1, 0x90, 0xF8, + 0x68, 0x43, 0x01, 0xE0, 0x90, 0xF8, 0xE5, 0x42, 0x4E, 0x4F, 0xAC, 0xB1, + 0x03, 0xA8, 0xCD, 0xE9, + 0x00, 0x06, 0x02, 0xAB, 0x2A, 0x46, 0x58, 0x46, 0x1D, 0x99, 0xFF, 0xF7, + 0x79, 0xFE, 0xBD, 0xF9, + 0x0C, 0x00, 0xBD, 0xF9, 0x08, 0x10, 0x40, 0x1A, 0x60, 0x43, 0x64, 0x21, + 0x90, 0xFB, 0xF1, 0xF0, + 0x39, 0x88, 0x08, 0x44, 0x00, 0xB2, 0x01, 0xE0, 0xB7, 0xF9, 0x00, 0x00, + 0x42, 0x49, 0x81, 0x46, + 0x38, 0x88, 0x08, 0x80, 0x41, 0x49, 0xA1, 0xF8, 0x00, 0x90, 0xD8, 0xF8, + 0x00, 0x00, 0xB0, 0xF9, + 0xE6, 0x12, 0x11, 0x91, 0xB0, 0xF9, 0xE8, 0x12, 0x0C, 0x91, 0x9A, 0xF8, + 0x00, 0x10, 0x29, 0xB1, + 0xB0, 0xF9, 0x62, 0x13, 0x11, 0x91, 0xB0, 0xF9, 0x64, 0x03, 0x0C, 0x90, + 0xAC, 0x78, 0x95, 0xF8, + 0x03, 0x80, 0x4C, 0xB9, 0xCD, 0xE9, 0x00, 0x49, 0x02, 0x96, 0x2B, 0x46, + 0x59, 0x46, 0x1D, 0x9A, + 0x1B, 0x98, 0xFF, 0xF7, 0x1F, 0xFF, 0x01, 0x24, 0x31, 0x49, 0x09, 0x78, + 0x49, 0x1E, 0x88, 0x45, + 0x0D, 0xD1, 0xCD, 0xF8, 0x00, 0x80, 0xCD, 0xE9, 0x01, 0x96, 0x2B, 0x46, + 0x59, 0x46, 0x1D, 0x9A, + 0x1B, 0x98, 0xFF, 0xF7, 0x0F, 0xFF, 0xA8, 0xF1, 0x01, 0x00, 0x00, 0xF0, + 0xFF, 0x08, 0xA0, 0x1E, + 0xC1, 0xB2, 0xB0, 0x68, 0x07, 0xF0, 0xA2, 0xFD, 0x0D, 0x90, 0x60, 0x1E, + 0xC1, 0xB2, 0xB0, 0x68, + 0x07, 0xF0, 0x9C, 0xFD, 0x03, 0x90, 0x21, 0x46, 0xB0, 0x68, 0x07, 0xF0, + 0x97, 0xFD, 0x02, 0x90, + 0x60, 0x1C, 0xC1, 0xB2, 0xB0, 0x68, 0x07, 0xF0, 0x91, 0xFD, 0x01, 0x90, + 0xA0, 0x1C, 0xC1, 0xB2, + 0xB0, 0x68, 0x07, 0xF0, 0x8B, 0xFD, 0x04, 0x90, 0x21, 0x46, 0x58, 0x46, + 0x07, 0xF0, 0x80, 0xFD, + 0x83, 0x46, 0x21, 0x46, 0x1B, 0x98, 0x07, 0xF0, 0x89, 0xFD, 0x05, 0x90, + 0x00, 0x20, 0x14, 0x90, + 0x01, 0x20, 0x13, 0x90, 0x00, 0x20, 0x12, 0x90, 0x0A, 0x48, 0x00, 0x68, + 0x90, 0xF8, 0x40, 0x14, + 0xCA, 0x07, 0x29, 0xD0, 0x09, 0x07, 0x27, 0xD5, 0x69, 0x78, 0x2A, 0x78, + 0x8A, 0x1A, 0x90, 0xF8, + 0x49, 0x14, 0x52, 0x1C, 0x8A, 0x42, 0x1F, 0xDC, 0xEA, 0x78, 0x13, 0xE0, + 0xEE, 0x06, 0x10, 0x00, + 0x6C, 0x06, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, 0x54, 0x06, 0x10, 0x00, + 0xFD, 0x06, 0x10, 0x00, + 0x98, 0x46, 0x10, 0x00, 0x92, 0x06, 0x10, 0x00, 0x8E, 0x07, 0x10, 0x00, + 0x90, 0x07, 0x10, 0x00, + 0xED, 0x06, 0x10, 0x00, 0xAB, 0x78, 0xD2, 0x1A, 0x52, 0x1C, 0x8A, 0x42, + 0x04, 0xDC, 0xB0, 0xF8, + 0x47, 0x04, 0xB1, 0x69, 0x81, 0x42, 0x01, 0xDD, 0x00, 0x20, 0x13, 0x90, + 0xA2, 0x46, 0xF1, 0xE0, + 0x2C, 0x78, 0xCE, 0xE0, 0x1B, 0xF8, 0x04, 0x10, 0x1D, 0x98, 0x81, 0x42, + 0x6B, 0xD1, 0x00, 0x20, + 0x10, 0x90, 0xFB, 0x49, 0x16, 0x90, 0x15, 0x90, 0x01, 0xEB, 0xCA, 0x00, + 0x22, 0x46, 0xD0, 0xE9, + 0x00, 0x67, 0x01, 0x20, 0x00, 0x21, 0x07, 0xF0, 0x71, 0xFB, 0x06, 0x40, + 0x0F, 0x40, 0x3E, 0x43, + 0x7E, 0xD1, 0xF4, 0x49, 0x40, 0xF2, 0xD2, 0x40, 0x08, 0x80, 0x02, 0x98, + 0x0C, 0x9A, 0x30, 0xF8, + 0x14, 0x10, 0x11, 0x98, 0x08, 0x44, 0x11, 0x44, 0x03, 0x9A, 0x00, 0xB2, + 0x09, 0xB2, 0x32, 0xF9, + 0x14, 0x20, 0x82, 0x42, 0x07, 0xDD, 0x01, 0x9B, 0x33, 0xF9, 0x14, 0x30, + 0x83, 0x42, 0x02, 0xDD, + 0x01, 0x23, 0x10, 0x93, 0x15, 0x93, 0xEC, 0xB3, 0xE7, 0x4B, 0x93, 0xF8, + 0x00, 0xE0, 0xAE, 0xF1, + 0x01, 0x03, 0x9C, 0x42, 0x68, 0xD0, 0x02, 0x9B, 0x03, 0xEB, 0x44, 0x0C, + 0x3C, 0xF9, 0x02, 0x6C, + 0x86, 0x42, 0x06, 0xDD, 0xBC, 0xF9, 0x02, 0x30, 0x83, 0x42, 0x02, 0xDD, + 0x01, 0x23, 0x10, 0x93, + 0x16, 0x93, 0x03, 0x9B, 0x03, 0xEB, 0x44, 0x03, 0x33, 0xF9, 0x02, 0x7C, + 0x8F, 0x42, 0x08, 0xDD, + 0x01, 0x9F, 0x07, 0xEB, 0x44, 0x07, 0xB7, 0xF9, 0x02, 0x70, 0x8F, 0x42, + 0x01, 0xDD, 0x01, 0x27, + 0x10, 0x97, 0xB3, 0xF9, 0x02, 0x30, 0x8B, 0x42, 0x06, 0xDD, 0x01, 0x9B, + 0x03, 0xEB, 0x44, 0x03, + 0x33, 0xF9, 0x02, 0x3C, 0x8B, 0x42, 0x41, 0xDC, 0x10, 0x99, 0x59, 0xB9, + 0xBA, 0xF1, 0x01, 0x0F, + 0x0F, 0xD9, 0x0D, 0x99, 0x31, 0xF9, 0x14, 0x10, 0x81, 0x42, 0x0A, 0xDD, + 0x01, 0x99, 0x31, 0xF9, + 0x14, 0x10, 0x01, 0xE0, 0x30, 0xE0, 0x5B, 0xE0, 0x81, 0x42, 0x02, 0xDD, + 0x01, 0x21, 0x10, 0x91, + 0x15, 0x91, 0xC6, 0x4B, 0x1B, 0x78, 0x9B, 0x1E, 0x9A, 0x45, 0x09, 0xD2, + 0x82, 0x42, 0x07, 0xDD, + 0x04, 0x99, 0x31, 0xF9, 0x14, 0x10, 0x81, 0x42, 0x02, 0xDD, 0x01, 0x21, + 0x10, 0x91, 0x15, 0x91, + 0x01, 0x2C, 0x0A, 0xD9, 0x3C, 0xF9, 0x04, 0x1C, 0x81, 0x42, 0x06, 0xDD, + 0xBC, 0xF9, 0x02, 0x10, + 0x81, 0x42, 0x02, 0xDD, 0x01, 0x21, 0x10, 0x91, 0x16, 0x91, 0xAE, 0xF1, + 0x02, 0x01, 0x00, 0xE0, + 0x17, 0xE0, 0x8C, 0x42, 0x08, 0xD2, 0x86, 0x42, 0x06, 0xDD, 0xBC, 0xF9, + 0x04, 0x10, 0x81, 0x42, + 0x02, 0xDD, 0x01, 0x20, 0x16, 0x90, 0x01, 0xE0, 0x10, 0x98, 0x48, 0xB1, + 0xB0, 0x49, 0x41, 0xF2, + 0xB3, 0x50, 0x08, 0x80, 0xAF, 0x48, 0xB0, 0x49, 0x04, 0x80, 0xA1, 0xF8, + 0x00, 0xA0, 0x09, 0xE0, + 0x40, 0xB9, 0x02, 0x98, 0x30, 0xF9, 0x14, 0x10, 0x49, 0x45, 0x03, 0xDD, + 0x05, 0x99, 0x01, 0x20, + 0x08, 0x55, 0x15, 0xE0, 0x16, 0x98, 0x98, 0xB1, 0x15, 0x98, 0x88, 0xB1, + 0x13, 0x98, 0x78, 0xB1, + 0x05, 0x98, 0x01, 0x21, 0x01, 0x55, 0x12, 0x98, 0x40, 0x1C, 0x80, 0xB2, + 0x12, 0x90, 0xA3, 0x48, + 0x00, 0x68, 0x90, 0xF8, 0x4A, 0x14, 0x12, 0x98, 0x81, 0x42, 0x01, 0xD2, + 0x01, 0x20, 0x14, 0x90, + 0x64, 0x1C, 0x68, 0x78, 0xA0, 0x42, 0xBF, 0xF4, 0x2D, 0xAF, 0x97, 0x48, + 0x0D, 0x99, 0x0B, 0xF1, + 0x28, 0x0B, 0x00, 0x78, 0x0A, 0xF1, 0x01, 0x0A, 0x01, 0xEB, 0x40, 0x01, + 0x0D, 0x91, 0x03, 0x99, + 0x01, 0xEB, 0x40, 0x01, 0x03, 0x91, 0x02, 0x99, 0x01, 0xEB, 0x40, 0x01, + 0x02, 0x91, 0x01, 0x99, + 0x01, 0xEB, 0x40, 0x01, 0x01, 0x91, 0x04, 0x99, 0x01, 0xEB, 0x40, 0x00, + 0x04, 0x90, 0x05, 0x98, + 0x28, 0x30, 0x05, 0x90, 0xC2, 0x45, 0x7F, 0xF6, 0x0B, 0xAF, 0x14, 0x98, + 0x1F, 0xB0, 0xA8, 0xE5, + 0x2D, 0xE9, 0xF0, 0x47, 0x04, 0x46, 0x91, 0x42, 0x5A, 0xD0, 0x07, 0xD9, + 0x01, 0xEB, 0x02, 0x00, + 0xA0, 0xEB, 0x02, 0x01, 0xCA, 0xB2, 0xA0, 0xEB, 0x02, 0x00, 0xC1, 0xB2, + 0x01, 0xEB, 0xC1, 0x00, + 0x04, 0xEB, 0xC0, 0x01, 0x02, 0xEB, 0xC2, 0x00, 0x04, 0xEB, 0xC0, 0x00, + 0xB1, 0xF9, 0x32, 0x50, + 0xB0, 0xF9, 0x32, 0x30, 0x81, 0x46, 0x9D, 0x42, 0x00, 0xDA, 0x4B, 0x86, + 0xCB, 0x8C, 0xC5, 0x8C, + 0x2B, 0x44, 0xCB, 0x84, 0x0B, 0x8D, 0x05, 0x8D, 0x2B, 0x44, 0x0B, 0x85, + 0x4B, 0x8D, 0x45, 0x8D, + 0x2B, 0x44, 0x4B, 0x85, 0x8B, 0x69, 0x85, 0x69, 0x2B, 0x44, 0x8B, 0x61, + 0xC7, 0x69, 0xCE, 0x69, + 0xCB, 0x68, 0xD9, 0xF8, 0x0C, 0x00, 0x4F, 0xEA, 0x23, 0x4C, 0x4F, 0xEA, + 0x20, 0x48, 0x0C, 0xFB, + 0x06, 0xFC, 0xF5, 0x19, 0x07, 0xFB, 0x08, 0xCC, 0x9C, 0xFB, 0xF5, 0xFC, + 0x6C, 0xF3, 0x1F, 0x43, + 0x0F, 0xFA, 0x83, 0xFC, 0x0C, 0xFB, 0x06, 0xFC, 0x00, 0xB2, 0x07, 0xFB, + 0x00, 0xC0, 0x90, 0xFB, + 0xF5, 0xF0, 0x60, 0xF3, 0x0F, 0x03, 0xCD, 0x61, 0xCB, 0x60, 0x94, 0xF8, + 0x38, 0x04, 0x41, 0x1E, + 0x91, 0x42, 0x08, 0xD0, 0x00, 0xEB, 0xC0, 0x00, 0x04, 0xEB, 0xC0, 0x01, + 0x48, 0x46, 0x48, 0x22, + 0x48, 0x39, 0x07, 0xF0, 0xAF, 0xFA, 0x94, 0xF8, 0x38, 0x04, 0x40, 0x1E, + 0x84, 0xF8, 0x38, 0x04, + 0xBD, 0xE8, 0xF0, 0x87, 0x2D, 0xE9, 0xFE, 0x4F, 0x07, 0x46, 0x58, 0x48, + 0x8A, 0x46, 0x00, 0x25, + 0x01, 0x68, 0xB1, 0xF8, 0xEA, 0x02, 0x00, 0xFB, 0x00, 0xF9, 0x55, 0x48, + 0x00, 0x78, 0x18, 0xB1, + 0xB1, 0xF8, 0x66, 0x03, 0x00, 0xFB, 0x00, 0xF9, 0x54, 0x46, 0x4F, 0xF0, + 0x01, 0x0B, 0xB0, 0xE0, + 0x0B, 0xFA, 0x04, 0xF0, 0x26, 0x46, 0x00, 0x90, 0x66, 0xE0, 0x00, 0xBF, + 0x00, 0x99, 0x0B, 0xFA, + 0x06, 0xF0, 0x08, 0x42, 0x5F, 0xD0, 0x06, 0xEB, 0xC6, 0x00, 0x07, 0xEB, + 0xC0, 0x00, 0xC2, 0x68, + 0x20, 0x46, 0x4D, 0xE0, 0xB0, 0x42, 0x4A, 0xD0, 0x00, 0xEB, 0xC0, 0x01, + 0x07, 0xEB, 0xC1, 0x01, + 0xC9, 0x68, 0x4F, 0xEA, 0x21, 0x4C, 0xAC, 0xEB, 0x22, 0x4C, 0x89, 0x1A, + 0x0F, 0xFA, 0x8C, 0xFC, + 0x09, 0xB2, 0x6C, 0xF3, 0x1F, 0x45, 0x61, 0xF3, 0x0F, 0x05, 0x8E, 0x46, + 0x3B, 0x49, 0xCD, 0xF8, + 0x04, 0xC0, 0x09, 0x68, 0x91, 0xF8, 0xE0, 0xC2, 0xCD, 0xF8, 0x08, 0xC0, + 0x5F, 0xEA, 0x8C, 0x6C, + 0x0B, 0xD5, 0x91, 0xF8, 0xED, 0x82, 0x0E, 0xFB, 0x08, 0xFC, 0x4F, 0xF0, + 0x64, 0x08, 0x9C, 0xFB, + 0xF8, 0xF8, 0x08, 0xEB, 0x0E, 0x0C, 0x6C, 0xF3, 0x0F, 0x05, 0xDD, 0xF8, + 0x08, 0xC0, 0x5F, 0xEA, + 0xCC, 0x6C, 0x0C, 0xD5, 0x91, 0xF8, 0xED, 0x12, 0xDD, 0xF8, 0x04, 0xC0, + 0x0C, 0xFB, 0x01, 0xFC, + 0x64, 0x21, 0x9C, 0xFB, 0xF1, 0xFC, 0x01, 0x99, 0x61, 0x44, 0x61, 0xF3, + 0x1F, 0x45, 0x4F, 0xEA, + 0x25, 0x4C, 0x0C, 0xFB, 0x0C, 0xFC, 0x29, 0xB2, 0x01, 0xFB, 0x01, 0xC1, + 0x49, 0x45, 0x06, 0xD2, + 0xDD, 0xF8, 0x00, 0xC0, 0x0B, 0xFA, 0x00, 0xF1, 0x41, 0xEA, 0x0C, 0x01, + 0x00, 0x91, 0x40, 0x1C, + 0x83, 0x42, 0xAF, 0xD8, 0x04, 0x21, 0x68, 0x46, 0xF1, 0xF7, 0x3D, 0xFA, + 0x01, 0x28, 0x02, 0xD1, + 0x00, 0x98, 0x40, 0x00, 0x00, 0x90, 0x76, 0x1C, 0x97, 0xF8, 0x38, 0x34, + 0xB3, 0x42, 0x95, 0xD8, + 0x04, 0x21, 0x68, 0x46, 0xF1, 0xF7, 0x2F, 0xFA, 0x01, 0x28, 0x0B, 0xD0, + 0x04, 0x21, 0x68, 0x46, + 0xF1, 0xF7, 0x29, 0xFA, 0x97, 0xF8, 0x38, 0x64, 0xA6, 0xEB, 0x0A, 0x01, + 0x88, 0x42, 0x06, 0xD1, + 0x87, 0xF8, 0x38, 0xA4, 0xBD, 0xE8, 0xFE, 0x8F, 0x64, 0x1C, 0xE4, 0xB2, + 0x01, 0xE0, 0x00, 0x99, + 0x58, 0x46, 0x00, 0xFA, 0x04, 0xF2, 0x0A, 0x42, 0xF6, 0xD0, 0xD8, 0x46, + 0x1C, 0xE0, 0x00, 0x00, + 0x98, 0x46, 0x10, 0x00, 0x86, 0x07, 0x10, 0x00, 0xEE, 0x06, 0x10, 0x00, + 0xED, 0x06, 0x10, 0x00, + 0x88, 0x07, 0x10, 0x00, 0x8A, 0x07, 0x10, 0x00, 0x8C, 0x07, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, + 0xFD, 0x06, 0x10, 0x00, 0x00, 0x99, 0x08, 0xFA, 0x06, 0xF0, 0x08, 0x42, + 0x04, 0xD0, 0xF2, 0xB2, + 0x21, 0x46, 0x38, 0x46, 0xFF, 0xF7, 0xDC, 0xFE, 0x76, 0x1E, 0xA6, 0x42, + 0xF2, 0xD8, 0x64, 0x1C, + 0xE4, 0xB2, 0x97, 0xF8, 0x38, 0x04, 0xA0, 0x42, 0x3F, 0xF6, 0x4A, 0xAF, + 0xCA, 0xE7, 0x2D, 0xE9, + 0xFF, 0x4F, 0xFB, 0x48, 0xFB, 0x4C, 0x93, 0x46, 0x06, 0x68, 0x81, 0xB0, + 0x1D, 0x46, 0x06, 0xF5, + 0x88, 0x7A, 0x01, 0x22, 0x59, 0x46, 0x20, 0x68, 0xFE, 0xF7, 0x92, 0xFE, + 0x00, 0x95, 0xDD, 0xE9, + 0x01, 0x12, 0x5B, 0x46, 0x20, 0x68, 0xFF, 0xF7, 0xFB, 0xFC, 0x03, 0x00, + 0x4F, 0xF0, 0x01, 0x01, + 0x05, 0xD0, 0xF1, 0x48, 0x01, 0x70, 0xA8, 0x8C, 0x40, 0xF0, 0x80, 0x00, + 0xA8, 0x84, 0xEF, 0x48, + 0x00, 0x78, 0x50, 0xB1, 0x4B, 0xB9, 0x95, 0xF8, 0x24, 0x00, 0x10, 0xF0, + 0x06, 0x0F, 0x04, 0xD1, + 0x59, 0x46, 0x28, 0x46, 0xFE, 0xF7, 0xA8, 0xFE, 0x03, 0x46, 0x00, 0x20, + 0x86, 0xF8, 0x0E, 0x01, + 0xDB, 0xF8, 0x00, 0x00, 0x30, 0x60, 0xE3, 0x48, 0x00, 0x22, 0x31, 0x46, + 0x00, 0x68, 0xFE, 0xF7, + 0x3F, 0xFF, 0x96, 0xF8, 0x0E, 0x01, 0x00, 0x24, 0x01, 0x28, 0x79, 0xD9, + 0xFC, 0xF7, 0x61, 0xF9, + 0x81, 0x46, 0xFC, 0xF7, 0x5C, 0xF9, 0x80, 0x46, 0xDB, 0xF8, 0x00, 0x20, + 0xDD, 0xE9, 0x01, 0x01, + 0x00, 0x92, 0xDB, 0x4B, 0x6A, 0x46, 0xFE, 0xF7, 0x0A, 0xFE, 0x60, 0xB1, + 0x69, 0x46, 0xD8, 0x48, + 0xFE, 0xF7, 0x9E, 0xFD, 0x2A, 0x46, 0x69, 0x46, 0xD5, 0x48, 0xFD, 0xF7, + 0xD8, 0xFA, 0xE9, 0x68, + 0x69, 0x61, 0xE9, 0x69, 0x29, 0x62, 0x01, 0x27, 0x2F, 0xE0, 0x48, 0x22, + 0x29, 0x46, 0x50, 0x46, + 0x07, 0xF0, 0x78, 0xF9, 0xCF, 0x49, 0x06, 0xEB, 0x47, 0x00, 0x09, 0x68, + 0xB0, 0xF8, 0xB4, 0x20, + 0x91, 0xF8, 0xEC, 0x12, 0x8A, 0x42, 0x1F, 0xD9, 0xBA, 0xF8, 0x24, 0x10, + 0x41, 0xF0, 0x10, 0x02, + 0xAA, 0xF8, 0x24, 0x20, 0xB0, 0xF8, 0xB4, 0x10, 0xAA, 0xF8, 0x26, 0x10, + 0x06, 0xEB, 0x87, 0x00, + 0x51, 0x46, 0x00, 0x90, 0xFF, 0xF7, 0x12, 0xF9, 0xBE, 0x48, 0xF9, 0xB2, + 0x53, 0x46, 0x00, 0x9A, + 0x00, 0x68, 0xFD, 0xF7, 0xF6, 0xFE, 0xBB, 0x48, 0xF9, 0xB2, 0x53, 0x46, + 0x00, 0x9A, 0x00, 0x68, + 0xFD, 0xF7, 0x6E, 0xFE, 0x64, 0x1C, 0xE4, 0xB2, 0x7F, 0x1C, 0x96, 0xF8, + 0x0E, 0x01, 0xB8, 0x42, + 0xCB, 0xD2, 0xB8, 0x4E, 0x30, 0x68, 0x90, 0xF8, 0xE0, 0x02, 0x00, 0x07, + 0x0A, 0xD5, 0x01, 0x2C, + 0x08, 0xD9, 0x49, 0x46, 0x40, 0x46, 0xFF, 0xF7, 0x95, 0xFE, 0xFC, 0xF7, + 0x02, 0xF9, 0xA0, 0xEB, + 0x09, 0x00, 0xC4, 0xB2, 0x31, 0x68, 0x91, 0xF8, 0xE0, 0x02, 0x00, 0x06, + 0x13, 0xD5, 0x20, 0x46, + 0x0F, 0xE0, 0x00, 0xBF, 0x09, 0xEB, 0x00, 0x02, 0xD2, 0xB2, 0x02, 0xEB, + 0xC2, 0x03, 0x08, 0xEB, + 0xC3, 0x02, 0x91, 0xF8, 0x07, 0x33, 0xD2, 0x8C, 0x9A, 0x42, 0x02, 0xD9, + 0x88, 0xF8, 0x38, 0x94, + 0x05, 0xE0, 0x40, 0x1E, 0xEE, 0xD2, 0x01, 0x2C, 0x0A, 0xD0, 0x00, 0x2C, + 0x0E, 0xD1, 0xDD, 0xE9, + 0x01, 0x01, 0x05, 0xB0, 0x2B, 0x46, 0x5A, 0x46, 0xBD, 0xE8, 0xF0, 0x4F, + 0xFD, 0xF7, 0x30, 0xBE, + 0x98, 0xF8, 0x38, 0x04, 0x40, 0x1E, 0x88, 0xF8, 0x38, 0x04, 0xF0, 0xE7, + 0x05, 0xB0, 0xBD, 0xE8, + 0xF0, 0x8F, 0x2D, 0xE9, 0xFF, 0x4F, 0x97, 0x48, 0x81, 0xB0, 0x00, 0x68, + 0x90, 0xF8, 0xE1, 0x02, + 0xC0, 0x07, 0xF3, 0xD0, 0xA8, 0x21, 0x04, 0x98, 0x07, 0xF0, 0x58, 0xF9, + 0x01, 0x25, 0x44, 0xE0, + 0x03, 0x98, 0x00, 0xEB, 0x85, 0x00, 0x82, 0x46, 0x84, 0x78, 0x90, 0xF8, + 0x03, 0x90, 0x8E, 0x48, + 0x00, 0x78, 0x40, 0x1E, 0x84, 0x42, 0x01, 0xDA, 0x64, 0x1C, 0xE4, 0xB2, + 0xB9, 0xF1, 0x00, 0x0F, + 0x03, 0xD0, 0xA9, 0xF1, 0x01, 0x00, 0x00, 0xF0, 0xFF, 0x09, 0x21, 0x46, + 0x01, 0x98, 0x07, 0xF0, + 0x1F, 0xFA, 0x83, 0x46, 0xA0, 0x46, 0x24, 0xE0, 0x03, 0x98, 0x04, 0x99, + 0x10, 0xF8, 0x25, 0x40, + 0x01, 0xEB, 0xC8, 0x00, 0x00, 0x90, 0x12, 0xE0, 0x1B, 0xF8, 0x04, 0x00, + 0xA8, 0x42, 0x0C, 0xD1, + 0x00, 0x98, 0x22, 0x46, 0x00, 0x21, 0xD0, 0xE9, 0x00, 0x67, 0x01, 0x20, + 0x07, 0xF0, 0x4E, 0xF8, + 0x06, 0x43, 0x00, 0x98, 0x0F, 0x43, 0xC0, 0xE9, 0x00, 0x67, 0x64, 0x1C, + 0xE4, 0xB2, 0x9A, 0xF8, + 0x01, 0x00, 0xA0, 0x42, 0xE8, 0xD2, 0x08, 0xF1, 0x01, 0x00, 0x0B, 0xF1, + 0x28, 0x0B, 0x00, 0xF0, + 0xFF, 0x08, 0xC8, 0x45, 0xD8, 0xD9, 0x6D, 0x1C, 0xED, 0xB2, 0x02, 0x98, + 0x85, 0x42, 0xB7, 0xD9, + 0x00, 0x21, 0x6D, 0x4E, 0x13, 0xE0, 0x04, 0x98, 0x00, 0xEB, 0xC1, 0x05, + 0x01, 0xF1, 0x01, 0x01, + 0xD5, 0xE9, 0x00, 0x32, 0xDC, 0x18, 0x42, 0xEB, 0x02, 0x00, 0x52, 0x08, + 0x4F, 0xEA, 0x33, 0x03, + 0x04, 0xEA, 0x03, 0x04, 0x00, 0xEA, 0x02, 0x00, 0xC5, 0xE9, 0x00, 0x40, + 0xC9, 0xB2, 0x30, 0x78, + 0x81, 0x42, 0xE8, 0xD3, 0x8A, 0xE7, 0xF0, 0xB4, 0x60, 0x4D, 0x00, 0x24, + 0x2C, 0x80, 0x60, 0x4D, + 0x2C, 0x80, 0x60, 0x4D, 0x2C, 0x80, 0x60, 0x4D, 0x2C, 0x80, 0x14, 0x68, + 0xC3, 0xF8, 0x3A, 0x40, + 0x58, 0x4C, 0x25, 0x68, 0x95, 0xF8, 0xE0, 0x42, 0xA6, 0x07, 0x13, 0xD4, + 0x93, 0xF8, 0x40, 0x60, + 0x01, 0x2E, 0x0F, 0xD0, 0x66, 0x07, 0x13, 0xD5, 0x64, 0x06, 0x09, 0xD5, + 0x57, 0x4C, 0x64, 0x78, + 0x5C, 0xB1, 0xB5, 0xF8, 0xF0, 0x42, 0x56, 0x4D, 0x64, 0x43, 0x2D, 0x68, + 0xA5, 0x42, 0x07, 0xDC, + 0xF0, 0xBC, 0x94, 0xE6, 0xF0, 0xBC, 0xFF, 0xF7, 0x73, 0xBA, 0xB5, 0xF8, + 0xEE, 0x42, 0xF2, 0xE7, + 0xF0, 0xBC, 0xFD, 0xF7, 0x85, 0xBD, 0x2D, 0xE9, 0xFF, 0x4F, 0x94, 0x78, + 0xD7, 0x78, 0x15, 0x78, + 0x56, 0x78, 0x81, 0xB0, 0x98, 0x46, 0x82, 0x46, 0x04, 0xB9, 0x01, 0x24, + 0x42, 0x48, 0x00, 0x78, + 0x40, 0x1E, 0x87, 0x42, 0x01, 0xD1, 0x7F, 0x1E, 0xFF, 0xB2, 0x05, 0xB9, + 0x01, 0x25, 0xDF, 0xF8, + 0x14, 0xB1, 0x9B, 0xF8, 0x00, 0x00, 0x40, 0x1E, 0x86, 0x42, 0x01, 0xD1, + 0x76, 0x1E, 0xF6, 0xB2, + 0x60, 0x1E, 0xC1, 0xB2, 0xD8, 0xF8, 0x08, 0x00, 0x07, 0xF0, 0x88, 0xF9, + 0x00, 0x90, 0x21, 0x46, + 0xD8, 0xF8, 0x08, 0x00, 0x07, 0xF0, 0x82, 0xF9, 0x81, 0x46, 0x60, 0x1C, + 0xC1, 0xB2, 0xD8, 0xF8, + 0x08, 0x00, 0x07, 0xF0, 0x7B, 0xF9, 0x80, 0x46, 0x21, 0x46, 0x50, 0x46, + 0x07, 0xF0, 0x70, 0xF9, + 0x35, 0xE0, 0x2C, 0x4A, 0x29, 0x46, 0xD2, 0xF8, 0x00, 0xA0, 0x24, 0xE0, + 0x43, 0x5C, 0x02, 0x9A, + 0x93, 0x42, 0x1E, 0xD1, 0x39, 0xF8, 0x11, 0x30, 0x9A, 0xF8, 0x27, 0x24, + 0x1A, 0x44, 0x00, 0x9B, + 0x12, 0xB2, 0x03, 0xEB, 0x41, 0x03, 0x33, 0xF9, 0x02, 0xCC, 0x94, 0x45, + 0x05, 0xDD, 0x08, 0xEB, + 0x41, 0x0C, 0xBC, 0xF9, 0x02, 0xC0, 0x94, 0x45, 0x09, 0xDC, 0xB3, 0xF9, + 0x02, 0x30, 0x93, 0x42, + 0x07, 0xDD, 0x08, 0xEB, 0x41, 0x03, 0x33, 0xF9, 0x02, 0x3C, 0x93, 0x42, + 0x01, 0xDD, 0x01, 0x20, + 0xFC, 0xE6, 0x49, 0x1C, 0xC9, 0xB2, 0xB1, 0x42, 0xD8, 0xD9, 0xCD, 0xF8, + 0x00, 0x90, 0x9B, 0xF8, + 0x00, 0x20, 0x64, 0x1C, 0xC1, 0x46, 0x08, 0xEB, 0x42, 0x08, 0xE4, 0xB2, + 0x28, 0x30, 0xBC, 0x42, + 0xC7, 0xD9, 0x00, 0x20, 0xEA, 0xE6, 0x2D, 0xE9, 0xF8, 0x4F, 0x4F, 0xF0, + 0x00, 0x0A, 0x89, 0x46, + 0x04, 0x46, 0x01, 0x26, 0x57, 0x46, 0xD0, 0x46, 0x85, 0x78, 0x46, 0xE0, + 0x29, 0x46, 0xD9, 0xF8, + 0x08, 0x00, 0x07, 0xF0, 0x2B, 0xF9, 0x02, 0x46, 0x00, 0x20, 0x21, 0x78, + 0x63, 0x78, 0x20, 0xE0, + 0x6C, 0x06, 0x10, 0x00, 0x54, 0x06, 0x10, 0x00, 0x34, 0x06, 0x10, 0x00, + 0xA4, 0x06, 0x10, 0x00, + 0xF0, 0x45, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, 0xED, 0x06, 0x10, 0x00, + 0x86, 0x07, 0x10, 0x00, + 0x88, 0x07, 0x10, 0x00, 0x8A, 0x07, 0x10, 0x00, 0x8C, 0x07, 0x10, 0x00, + 0x74, 0x07, 0x10, 0x00, + 0x24, 0x07, 0x10, 0x00, 0xEE, 0x06, 0x10, 0x00, 0x32, 0xF9, 0x11, 0xC0, + 0x49, 0x1C, 0x60, 0x44, + 0xC9, 0xB2, 0x8B, 0x42, 0xF8, 0xD2, 0xA1, 0x78, 0x4A, 0x1C, 0xAA, 0x42, + 0x10, 0xD2, 0x46, 0x4A, + 0x12, 0x68, 0xB2, 0xF8, 0x28, 0x34, 0x03, 0xEB, 0x08, 0x02, 0xBA, 0x42, + 0x01, 0xDA, 0x82, 0x42, + 0x33, 0xDB, 0x89, 0x1C, 0xA9, 0x42, 0x03, 0xD2, 0x52, 0x45, 0x01, 0xDA, + 0x82, 0x42, 0x2C, 0xDB, + 0xBA, 0x46, 0x47, 0x46, 0x6D, 0x1C, 0x80, 0x46, 0xED, 0xB2, 0xE0, 0x78, + 0xA8, 0x42, 0xB5, 0xD2, + 0x00, 0x20, 0x00, 0x90, 0x82, 0x46, 0x83, 0x46, 0x25, 0x78, 0x2E, 0xE0, + 0x4F, 0xF0, 0x00, 0x08, + 0xA7, 0x78, 0x09, 0xE0, 0x39, 0x46, 0xD9, 0xF8, 0x08, 0x00, 0x07, 0xF0, + 0xD7, 0xF8, 0x30, 0xF9, + 0x15, 0x10, 0x7F, 0x1C, 0x88, 0x44, 0xFF, 0xB2, 0xE0, 0x78, 0xB8, 0x42, + 0xF2, 0xD2, 0x20, 0x78, + 0x41, 0x1C, 0xA9, 0x42, 0x13, 0xD2, 0x2C, 0x49, 0x09, 0x68, 0xB1, 0xF8, + 0x28, 0x24, 0x02, 0xEB, + 0x0B, 0x01, 0x51, 0x45, 0x03, 0xDA, 0x41, 0x45, 0x01, 0xDA, 0x00, 0x26, + 0x10, 0xE0, 0x80, 0x1C, + 0xA8, 0x42, 0x04, 0xD2, 0x00, 0x98, 0x81, 0x42, 0x01, 0xDA, 0x41, 0x45, + 0xF5, 0xDB, 0xCD, 0xF8, + 0x00, 0xA0, 0xDA, 0x46, 0x6D, 0x1C, 0xC3, 0x46, 0xED, 0xB2, 0x60, 0x78, + 0xA8, 0x42, 0xCD, 0xD2, + 0x30, 0x46, 0xBD, 0xE8, 0xF8, 0x8F, 0x2D, 0xE9, 0xF0, 0x41, 0x07, 0x46, + 0x98, 0x8C, 0x14, 0x46, + 0x00, 0x26, 0x1D, 0x46, 0xC2, 0x05, 0x02, 0xD5, 0x01, 0x20, 0xBD, 0xE8, + 0xF0, 0x81, 0x00, 0x06, + 0x27, 0xD4, 0x15, 0x48, 0xEA, 0x8C, 0x00, 0x68, 0x90, 0xF8, 0x23, 0x34, + 0x9A, 0x42, 0x20, 0xD3, + 0xB0, 0xF8, 0x24, 0x34, 0xAA, 0x69, 0x9A, 0x42, 0x1B, 0xDC, 0x62, 0x78, + 0x23, 0x78, 0x90, 0xF8, + 0x26, 0x04, 0xD2, 0x1A, 0x52, 0x1C, 0x82, 0x42, 0x13, 0xDC, 0xE2, 0x78, + 0xA3, 0x78, 0xD2, 0x1A, + 0x52, 0x1C, 0x82, 0x42, 0x0D, 0xDC, 0x2B, 0x46, 0x22, 0x46, 0x38, 0x46, + 0xFF, 0xF7, 0xD3, 0xFE, + 0x01, 0x28, 0x06, 0xD1, 0x29, 0x46, 0x20, 0x46, 0xFF, 0xF7, 0x3D, 0xFF, + 0x01, 0x28, 0x00, 0xD1, + 0x01, 0x26, 0x30, 0x46, 0xD1, 0xE7, 0x00, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x70, 0x47, 0xF0, 0xB5, + 0x99, 0x4B, 0x02, 0x46, 0x0C, 0x46, 0x1F, 0x78, 0x98, 0x4B, 0x1E, 0x68, + 0x98, 0x4B, 0x1D, 0x78, + 0x19, 0xE0, 0x2B, 0x46, 0x11, 0xE0, 0x00, 0xBF, 0x34, 0xF8, 0x13, 0xC0, + 0x32, 0xF8, 0x13, 0xE0, + 0xAC, 0xEB, 0x0E, 0x0C, 0xB6, 0xF9, 0x46, 0xE0, 0x0F, 0xFA, 0x8C, 0xFC, + 0x1E, 0xEB, 0x0C, 0x0F, + 0x22, 0xD4, 0xB6, 0xF8, 0x48, 0xE0, 0xE6, 0x45, 0x1E, 0xDB, 0x5B, 0x1E, + 0xEC, 0xD1, 0x02, 0xEB, + 0x45, 0x02, 0x04, 0xEB, 0x45, 0x04, 0x7F, 0x1E, 0xE3, 0xD2, 0x96, 0xF8, + 0x44, 0x20, 0x89, 0x4B, + 0x02, 0xF0, 0x0F, 0x02, 0xC2, 0xF1, 0x10, 0x04, 0xB3, 0xF9, 0x00, 0x30, + 0x0A, 0xE0, 0x00, 0xBF, + 0x31, 0xF9, 0x13, 0x50, 0x30, 0xF9, 0x13, 0x60, 0x55, 0x43, 0x06, 0xFB, + 0x04, 0x55, 0x2D, 0x11, + 0x21, 0xF8, 0x13, 0x50, 0x5B, 0x1E, 0xF3, 0xD2, 0xF0, 0xBD, 0xF0, 0xB5, + 0x14, 0x46, 0x05, 0x9F, + 0x0B, 0xE0, 0x00, 0xBF, 0x31, 0xF9, 0x14, 0x50, 0x30, 0xF9, 0x14, 0x60, + 0x05, 0xEB, 0x03, 0x0C, + 0xB4, 0x45, 0x19, 0xDB, 0xED, 0x1B, 0xB5, 0x42, 0x16, 0xDC, 0x64, 0x1E, + 0xF2, 0xD1, 0x73, 0x4B, + 0x1B, 0x68, 0x93, 0xF8, 0x45, 0x30, 0x1B, 0x09, 0xC3, 0xF1, 0x10, 0x04, + 0x0A, 0xE0, 0x00, 0xBF, + 0x31, 0xF9, 0x12, 0x50, 0x30, 0xF9, 0x12, 0x60, 0x5D, 0x43, 0x06, 0xFB, + 0x04, 0x55, 0x2D, 0x11, + 0x21, 0xF8, 0x12, 0x50, 0x52, 0x1E, 0xF3, 0xD2, 0xF0, 0xBD, 0x2D, 0xE9, + 0xF0, 0x43, 0x81, 0x46, + 0x66, 0x48, 0x69, 0x4C, 0x00, 0x68, 0x30, 0xF9, 0x5B, 0x6F, 0xB0, 0xF9, + 0x02, 0x70, 0x10, 0xF8, + 0x01, 0x0C, 0x00, 0xF0, 0x0F, 0x03, 0xC3, 0xF1, 0x10, 0x08, 0x00, 0x20, + 0x17, 0xE0, 0x00, 0xBF, + 0x34, 0xF9, 0x10, 0x20, 0x39, 0xF9, 0x10, 0x50, 0x02, 0xEB, 0x06, 0x0C, + 0x65, 0x45, 0x0C, 0xDC, + 0xA2, 0xEB, 0x07, 0x0C, 0x65, 0x45, 0x08, 0xDB, 0x5A, 0x43, 0x05, 0xFB, + 0x08, 0x22, 0xD5, 0x17, + 0x02, 0xEB, 0x15, 0x72, 0x12, 0x11, 0x24, 0xF8, 0x10, 0x20, 0x40, 0x1C, + 0xC0, 0xB2, 0x88, 0x42, + 0xE6, 0xD3, 0xBD, 0xE8, 0xF0, 0x83, 0x2D, 0xE9, 0xF8, 0x4F, 0x0D, 0x46, + 0x04, 0x46, 0x01, 0xF0, + 0x7F, 0xF8, 0x00, 0x28, 0x5B, 0xD1, 0xE8, 0x07, 0x08, 0xD0, 0x50, 0x48, + 0x50, 0x49, 0x00, 0x78, + 0x09, 0x68, 0x01, 0x28, 0x20, 0x68, 0x2D, 0xD0, 0x00, 0xF0, 0x53, 0xF8, + 0x47, 0x4E, 0xE8, 0x06, + 0x17, 0xD5, 0x30, 0x68, 0xB0, 0xF9, 0x50, 0x10, 0x00, 0x91, 0x43, 0x49, + 0xB0, 0xF9, 0x4E, 0x30, + 0x20, 0x69, 0x0A, 0x78, 0x47, 0x49, 0xFF, 0xF7, 0x88, 0xFF, 0x30, 0x68, + 0xB0, 0xF9, 0x54, 0x10, + 0x00, 0x91, 0x3F, 0x49, 0xB0, 0xF9, 0x52, 0x30, 0x60, 0x69, 0x0A, 0x78, + 0x42, 0x49, 0xFF, 0xF7, + 0x7C, 0xFF, 0xA8, 0x07, 0x2B, 0xD5, 0x36, 0x68, 0x41, 0x49, 0xA7, 0x68, + 0x96, 0xF8, 0x45, 0x00, + 0xDF, 0xF8, 0xF8, 0x90, 0x00, 0xF0, 0x0F, 0x03, 0xC3, 0xF1, 0x10, 0x0A, + 0x00, 0x20, 0xB1, 0xF9, + 0x00, 0xC0, 0x1A, 0xE0, 0xFF, 0xF7, 0x2B, 0xFF, 0xD0, 0xE7, 0x00, 0xBF, + 0x39, 0xF9, 0x10, 0x10, + 0xB6, 0xF9, 0x56, 0x80, 0x37, 0xF9, 0x10, 0x20, 0x88, 0x44, 0x90, 0x45, + 0x0B, 0xDB, 0xB6, 0xF9, + 0x58, 0x80, 0xA1, 0xEB, 0x08, 0x08, 0x90, 0x45, 0x05, 0xDC, 0x59, 0x43, + 0x02, 0xFB, 0x0A, 0x11, + 0x0A, 0x11, 0x29, 0xF8, 0x10, 0x20, 0x40, 0x1C, 0x00, 0xB2, 0x60, 0x45, + 0xE6, 0xDB, 0xA8, 0x05, + 0x05, 0xD5, 0x94, 0xF8, 0x32, 0x10, 0xA0, 0x69, 0xBD, 0xE8, 0xF8, 0x4F, + 0x6D, 0xE7, 0xBD, 0xE8, + 0xF8, 0x8F, 0x2D, 0xE9, 0xF0, 0x43, 0x81, 0x46, 0x26, 0x48, 0x03, 0x78, + 0x1B, 0x48, 0x05, 0x2B, + 0x00, 0x68, 0x90, 0xF8, 0x44, 0x20, 0x0C, 0xD0, 0x30, 0xF9, 0x46, 0x5F, + 0x02, 0xF0, 0x0F, 0x03, + 0xB0, 0xF9, 0x02, 0x60, 0x17, 0x48, 0xC3, 0xF1, 0x10, 0x04, 0xB0, 0xF9, + 0x00, 0x00, 0x02, 0x46, + 0x11, 0xE0, 0x30, 0xF9, 0x4A, 0x5F, 0x13, 0x09, 0xB0, 0xF9, 0x02, 0x60, + 0xF2, 0xE7, 0x00, 0xBF, + 0x31, 0xF9, 0x12, 0x70, 0x39, 0xF9, 0x12, 0xC0, 0x07, 0xEB, 0x05, 0x08, + 0xE0, 0x45, 0x11, 0xDB, + 0xBF, 0x1B, 0x67, 0x45, 0x0E, 0xDC, 0x52, 0x1E, 0xF2, 0xD2, 0x09, 0xE0, + 0x31, 0xF9, 0x10, 0x20, + 0x39, 0xF9, 0x10, 0x50, 0x5A, 0x43, 0x05, 0xFB, 0x04, 0x22, 0x12, 0x11, + 0x21, 0xF8, 0x10, 0x20, + 0x40, 0x1E, 0xF3, 0xD2, 0x5D, 0xE7, 0x00, 0x00, 0xED, 0x06, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, + 0xEE, 0x06, 0x10, 0x00, 0xFE, 0x06, 0x10, 0x00, 0xFC, 0x4A, 0x01, 0x20, + 0xF3, 0x06, 0x10, 0x00, + 0x1C, 0x07, 0x10, 0x00, 0x7E, 0x4A, 0x01, 0x20, 0xA8, 0x4A, 0x01, 0x20, + 0xF4, 0x4A, 0x01, 0x20, + 0x00, 0x07, 0x10, 0x00, 0xEC, 0x06, 0x10, 0x00, 0x87, 0x48, 0x00, 0x68, + 0x90, 0xF8, 0x90, 0x02, + 0x40, 0x07, 0x07, 0xD5, 0x85, 0x48, 0x86, 0x49, 0xC0, 0x78, 0x09, 0x78, + 0x88, 0x42, 0x01, 0xD0, + 0x01, 0x20, 0x70, 0x47, 0x00, 0x20, 0x70, 0x47, 0x7F, 0x48, 0x10, 0xB5, + 0x00, 0x68, 0x90, 0xF8, + 0x90, 0x12, 0x89, 0x07, 0x27, 0xD5, 0x7F, 0x49, 0x0A, 0x78, 0x52, 0x1C, + 0xD2, 0xB2, 0x0A, 0x70, + 0x90, 0xF8, 0x92, 0x32, 0x93, 0x42, 0x1E, 0xD2, 0x00, 0x22, 0x0A, 0x70, + 0x90, 0xF8, 0x50, 0x12, + 0x03, 0x24, 0xC9, 0x07, 0x03, 0xD1, 0x90, 0xF8, 0x80, 0x02, 0xC0, 0x07, + 0x00, 0xD0, 0x0B, 0x24, + 0xEF, 0xF7, 0x36, 0xFE, 0x74, 0x48, 0x01, 0x68, 0x21, 0x43, 0x01, 0x60, + 0xEF, 0xF7, 0x2E, 0xFE, + 0x10, 0x21, 0x05, 0x20, 0x02, 0xF0, 0x6F, 0xF9, 0xBD, 0xE8, 0x10, 0x40, + 0x4F, 0xF4, 0x80, 0x71, + 0x00, 0x20, 0xEF, 0xF7, 0x66, 0xBF, 0x10, 0xBD, 0x68, 0x48, 0x6C, 0x49, + 0x4F, 0xF0, 0x00, 0x02, + 0x43, 0x78, 0x09, 0x78, 0x0B, 0x43, 0x67, 0x49, 0x08, 0xD1, 0x4B, 0x78, + 0x5B, 0x1C, 0xDB, 0xB2, + 0x4B, 0x70, 0x05, 0x2B, 0x03, 0xD9, 0x01, 0x23, 0x43, 0x70, 0x83, 0x70, + 0x4A, 0x70, 0x70, 0x47, + 0x2D, 0xE9, 0xF3, 0x4F, 0x62, 0x48, 0x00, 0x27, 0xDF, 0xF8, 0x88, 0xA1, + 0x90, 0xF8, 0x00, 0x80, + 0x59, 0x48, 0x81, 0xB0, 0x3C, 0x46, 0x00, 0x68, 0xB9, 0x46, 0x90, 0xF8, + 0x90, 0x02, 0xC0, 0x07, + 0x1A, 0xD0, 0x00, 0x26, 0x4F, 0xF0, 0x01, 0x0B, 0x01, 0x9D, 0x12, 0xE0, + 0x28, 0x46, 0x02, 0x99, + 0x00, 0xF0, 0x32, 0xF8, 0x10, 0xF0, 0x01, 0x0F, 0x05, 0xD0, 0x0B, 0xFA, + 0x06, 0xF0, 0x38, 0x43, + 0x64, 0x1C, 0xC7, 0xB2, 0xE4, 0xB2, 0xBA, 0xF9, 0x00, 0x00, 0x76, 0x1C, + 0x05, 0xEB, 0x40, 0x05, + 0x36, 0xB2, 0x46, 0x45, 0xEA, 0xDB, 0x01, 0xE0, 0x44, 0x46, 0xFF, 0x27, + 0x4E, 0x48, 0x07, 0x70, + 0x4C, 0xB1, 0x00, 0x94, 0xBA, 0xF8, 0x00, 0x00, 0x3B, 0x46, 0x82, 0xB2, + 0x41, 0x46, 0x01, 0x98, + 0x00, 0xF0, 0x45, 0xF8, 0x07, 0xE0, 0x49, 0x48, 0xB0, 0xF9, 0x00, 0x10, + 0x03, 0x20, 0x02, 0xF0, + 0x12, 0xF9, 0x4F, 0xF0, 0x01, 0x09, 0x48, 0x46, 0xBD, 0xE8, 0xFE, 0x8F, + 0x70, 0x47, 0x3D, 0x48, + 0x00, 0x21, 0x01, 0x70, 0x41, 0x70, 0x70, 0x47, 0x70, 0xB5, 0x37, 0x4A, + 0x3B, 0x4C, 0x12, 0x68, + 0x24, 0x78, 0xB2, 0xF9, 0x93, 0x32, 0x0C, 0xB1, 0xB2, 0xF9, 0x99, 0x32, + 0x33, 0x4C, 0x64, 0x78, + 0x01, 0x2C, 0x02, 0xD0, 0x02, 0x2C, 0x03, 0xD0, 0x04, 0xE0, 0xB2, 0xF9, + 0x95, 0x32, 0x01, 0xE0, + 0xB2, 0xF9, 0x97, 0x32, 0x33, 0x4A, 0x36, 0x4C, 0xB2, 0xF9, 0x00, 0x20, + 0x25, 0x78, 0x12, 0xE0, + 0x30, 0xF9, 0x12, 0x40, 0x31, 0xF9, 0x12, 0x60, 0xA4, 0x1B, 0x00, 0xD5, + 0x64, 0x42, 0x24, 0xB2, + 0x9C, 0x42, 0x08, 0xDD, 0x92, 0xFB, 0xF5, 0xF6, 0x05, 0xFB, 0x16, 0x26, + 0x1E, 0xB1, 0x2B, 0x48, + 0x04, 0x80, 0x00, 0x20, 0x70, 0xBD, 0x52, 0x1E, 0xEA, 0xD2, 0x01, 0x20, + 0x70, 0xBD, 0x2D, 0xE9, + 0xF0, 0x03, 0x06, 0x9F, 0x00, 0x2F, 0x19, 0xD0, 0x01, 0x25, 0x01, 0x2F, + 0x02, 0xD0, 0x14, 0x46, + 0xA9, 0x46, 0x2E, 0xE0, 0x00, 0x24, 0x0F, 0xE0, 0x05, 0xFA, 0x04, 0xF6, + 0x1E, 0x42, 0x0A, 0xD0, + 0x00, 0x2C, 0x0B, 0xD0, 0x53, 0x00, 0x62, 0x43, 0xBD, 0xE8, 0xF0, 0x03, + 0x00, 0xEB, 0x42, 0x01, + 0x1A, 0x46, 0x06, 0xF0, 0xAA, 0xBC, 0x64, 0x1C, 0x8C, 0x42, 0xED, 0xDB, + 0xBD, 0xE8, 0xF0, 0x03, + 0x70, 0x47, 0x00, 0xBF, 0x00, 0x26, 0x00, 0xEB, 0x44, 0x0C, 0x35, 0x46, + 0x0B, 0xE0, 0x00, 0xBF, + 0x09, 0xFA, 0x05, 0xF8, 0x18, 0xEA, 0x03, 0x0F, 0x02, 0xD0, 0xBC, 0xF9, + 0x00, 0x80, 0x46, 0x44, + 0x0C, 0xEB, 0x42, 0x0C, 0x6D, 0x1C, 0x8D, 0x42, 0xF2, 0xDB, 0x96, 0xFB, + 0xF7, 0xF5, 0x20, 0xF8, + 0x14, 0x50, 0x64, 0x1E, 0xE6, 0xD2, 0xE1, 0xE7, 0x4C, 0x07, 0x10, 0x00, + 0x74, 0x07, 0x10, 0x00, + 0xEF, 0x06, 0x10, 0x00, 0x70, 0x06, 0x10, 0x00, 0x54, 0x07, 0x10, 0x00, + 0x74, 0x05, 0x10, 0x00, + 0xF0, 0x06, 0x10, 0x00, 0xFE, 0x06, 0x10, 0x00, 0x75, 0x06, 0x10, 0x00, + 0x7A, 0x06, 0x10, 0x00, + 0xEE, 0x06, 0x10, 0x00, 0x10, 0xB5, 0xF6, 0xF7, 0x3D, 0xFC, 0xF7, 0xF7, + 0xC3, 0xF8, 0xBD, 0xE8, + 0x10, 0x40, 0xF5, 0xF7, 0x9F, 0xBB, 0x2D, 0xE9, 0xF0, 0x47, 0x81, 0x46, + 0x1F, 0x46, 0x90, 0x46, + 0x0E, 0x46, 0x03, 0x20, 0x08, 0x9D, 0xEF, 0xF7, 0x4C, 0xFF, 0x5C, 0x48, + 0x01, 0x68, 0x48, 0x46, + 0xFF, 0xF7, 0x16, 0xFF, 0x04, 0x46, 0x03, 0x20, 0xEF, 0xF7, 0x5E, 0xFF, + 0x64, 0xB1, 0xFF, 0xF7, + 0xFB, 0xFE, 0xFF, 0xF7, 0xC9, 0xFE, 0x31, 0x46, 0x48, 0x46, 0xFF, 0xF7, + 0x4F, 0xFF, 0x54, 0x48, + 0x04, 0x70, 0x20, 0x46, 0xBD, 0xE8, 0xF0, 0x87, 0xFF, 0xF7, 0x49, 0xFF, + 0x16, 0xB1, 0x30, 0x46, + 0x00, 0xF0, 0xE8, 0xFC, 0x65, 0xB1, 0x4F, 0x48, 0x01, 0x78, 0x28, 0x46, + 0xF6, 0xF7, 0x31, 0xFC, + 0x4D, 0x48, 0x80, 0x78, 0x20, 0xB1, 0x4D, 0x48, 0x01, 0x68, 0x21, 0xF0, + 0x30, 0x01, 0x01, 0x60, + 0x06, 0xF0, 0x6C, 0xFD, 0x05, 0x46, 0xFF, 0xF7, 0x97, 0xFE, 0x50, 0xEA, + 0x05, 0x04, 0xDA, 0xD1, + 0x3A, 0x46, 0x41, 0x46, 0x48, 0x46, 0x00, 0xF0, 0x32, 0xF8, 0x04, 0x46, + 0xD3, 0xE7, 0x10, 0xB5, + 0xF8, 0xF7, 0x5B, 0xFB, 0xFD, 0xF7, 0x1F, 0xFF, 0xFB, 0xF7, 0x28, 0xFB, + 0xFF, 0xF7, 0xAA, 0xFF, + 0xFF, 0xF7, 0x1D, 0xFF, 0xFC, 0xF7, 0xB3, 0xFA, 0x39, 0x49, 0x00, 0x20, + 0x08, 0x70, 0x48, 0x70, + 0x88, 0x80, 0xC8, 0x80, 0x88, 0x70, 0x88, 0x60, 0xC8, 0x60, 0x08, 0x61, + 0x14, 0x31, 0x08, 0x70, + 0x88, 0x70, 0x37, 0x48, 0x00, 0x68, 0x10, 0xF8, 0x30, 0x2F, 0x4A, 0x70, + 0x40, 0x78, 0xC8, 0x70, + 0x10, 0xBD, 0x10, 0xB5, 0xF8, 0xF7, 0x47, 0xFB, 0xFD, 0xF7, 0x10, 0xFF, + 0x00, 0xF0, 0x15, 0xFD, + 0xF7, 0xF7, 0x5D, 0xF8, 0x2C, 0x48, 0x00, 0x21, 0x41, 0x70, 0x81, 0x70, + 0x10, 0xBD, 0xF8, 0xB5, + 0x07, 0x46, 0x00, 0x24, 0x15, 0x46, 0x0E, 0x46, 0x8D, 0xF8, 0x00, 0x40, + 0x02, 0x20, 0xEF, 0xF7, + 0xD8, 0xFE, 0x2A, 0x46, 0x31, 0x46, 0x38, 0x46, 0xF8, 0xF7, 0x4F, 0xFA, + 0x25, 0x49, 0x01, 0x28, + 0xCA, 0x78, 0x60, 0xF3, 0x03, 0x02, 0xCA, 0x70, 0x2A, 0xD0, 0x02, 0x28, + 0x2D, 0xD0, 0x03, 0x28, + 0x2E, 0xD0, 0x04, 0x28, 0x00, 0xD0, 0x01, 0x24, 0x6B, 0x46, 0x2A, 0x46, + 0x31, 0x46, 0x38, 0x46, + 0xFC, 0xF7, 0xAE, 0xF9, 0x40, 0xEA, 0x04, 0x05, 0x02, 0x20, 0xEF, 0xF7, + 0xD5, 0xFE, 0x9D, 0xF8, + 0x00, 0x00, 0x98, 0xB1, 0xEF, 0xF7, 0x94, 0xFC, 0x17, 0x48, 0x18, 0x4A, + 0x01, 0x68, 0x12, 0x68, + 0x11, 0x43, 0x01, 0x60, 0xEF, 0xF7, 0x8A, 0xFC, 0x15, 0x48, 0x01, 0x78, + 0x05, 0x20, 0x01, 0xF0, + 0xCA, 0xFF, 0x4F, 0xF4, 0x80, 0x71, 0x00, 0x20, 0xEF, 0xF7, 0xC3, 0xFD, + 0x28, 0x46, 0xF8, 0xBD, + 0xFD, 0xF7, 0xA4, 0xFE, 0xFB, 0xF7, 0xD9, 0xFA, 0xD6, 0xE7, 0xFD, 0xF7, + 0x5E, 0xFC, 0x01, 0xE0, + 0xF7, 0xF7, 0x47, 0xFB, 0xFB, 0xF7, 0xFD, 0xFA, 0xCE, 0xE7, 0x00, 0x00, + 0x1C, 0x07, 0x10, 0x00, + 0x74, 0x06, 0x10, 0x00, 0x79, 0x07, 0x10, 0x00, 0x74, 0x07, 0x10, 0x00, + 0x04, 0x07, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, 0x0A, 0x44, 0x01, 0x20, 0x54, 0x07, 0x10, 0x00, + 0x30, 0x06, 0x10, 0x00, + 0x2C, 0x06, 0x10, 0x00, 0x06, 0x28, 0x08, 0xD2, 0xDF, 0xE8, 0x00, 0xF0, + 0x07, 0x03, 0x03, 0x03, + 0x05, 0x05, 0x01, 0x20, 0x70, 0x47, 0x02, 0x20, 0x70, 0x47, 0x00, 0x20, + 0x70, 0x47, 0x10, 0xB5, + 0x04, 0x46, 0x08, 0x46, 0xFF, 0xF7, 0xEE, 0xFF, 0xA8, 0x4A, 0x01, 0x21, + 0x81, 0x40, 0x12, 0x68, + 0x11, 0x42, 0x09, 0xD1, 0x02, 0x28, 0x07, 0xDC, 0xA5, 0x49, 0x51, 0xF8, + 0x20, 0x10, 0x21, 0x60, + 0xA4, 0x49, 0x41, 0xF8, 0x20, 0x40, 0x10, 0xBD, 0x00, 0x20, 0x20, 0x60, + 0x21, 0x46, 0xBD, 0xE8, + 0x10, 0x40, 0x48, 0x20, 0x01, 0xF0, 0x2D, 0xBD, 0x70, 0xB5, 0x04, 0x46, + 0x08, 0x46, 0xFF, 0xF7, + 0xD1, 0xFF, 0x00, 0x25, 0x88, 0xB1, 0x02, 0x28, 0x0F, 0xDC, 0x99, 0x4A, + 0x21, 0x68, 0x52, 0xF8, + 0x20, 0x20, 0x91, 0x42, 0x09, 0xD1, 0x95, 0x49, 0x01, 0x22, 0x82, 0x40, + 0x0B, 0x68, 0x93, 0x43, + 0x0B, 0x60, 0x94, 0x49, 0x41, 0xF8, 0x20, 0x50, 0x03, 0xE0, 0x21, 0x46, + 0x49, 0x20, 0x01, 0xF0, + 0x10, 0xFD, 0x25, 0x60, 0x70, 0xBD, 0x2D, 0xE9, 0xF0, 0x41, 0x01, 0x24, + 0x8B, 0x4E, 0x8D, 0x4F, + 0x4F, 0xF0, 0x00, 0x08, 0x25, 0x46, 0x00, 0xBF, 0x31, 0x68, 0x05, 0xFA, + 0x04, 0xF0, 0x08, 0x42, + 0x08, 0xD0, 0x81, 0x43, 0x31, 0x60, 0x57, 0xF8, 0x24, 0x10, 0x4A, 0x20, + 0x01, 0xF0, 0xF9, 0xFC, + 0x47, 0xF8, 0x24, 0x80, 0x64, 0x1C, 0x02, 0x2C, 0xEE, 0xDD, 0xBD, 0xE8, + 0xF0, 0x81, 0x2D, 0xE9, + 0xF0, 0x41, 0x80, 0x4D, 0x0A, 0x46, 0x00, 0x27, 0x0C, 0x35, 0x7C, 0x4B, + 0x7E, 0x4C, 0x05, 0xF1, + 0x14, 0x06, 0x03, 0xF1, 0x08, 0x01, 0x07, 0x60, 0x01, 0x2A, 0x63, 0xD0, + 0x02, 0x2A, 0x39, 0xD0, + 0x03, 0x2A, 0x6A, 0xD1, 0x5F, 0xF0, 0x00, 0x02, 0x11, 0xF8, 0x02, 0xC0, + 0xBC, 0xF1, 0x00, 0x0F, + 0x10, 0xD1, 0x01, 0xEB, 0x02, 0x0C, 0x9C, 0xF8, 0x01, 0xE0, 0xBE, 0xF1, + 0x00, 0x0F, 0x09, 0xD1, + 0x9C, 0xF8, 0x02, 0xE0, 0xBE, 0xF1, 0x00, 0x0F, 0x04, 0xD1, 0x9C, 0xF8, + 0x03, 0xE0, 0xBE, 0xF1, + 0x00, 0x0F, 0x0B, 0xD0, 0x02, 0xF1, 0x04, 0x0C, 0x52, 0x1C, 0xBC, 0xF1, + 0x05, 0x0F, 0xE3, 0xDB, + 0x59, 0x68, 0x04, 0x29, 0x47, 0xD3, 0x01, 0x46, 0x4C, 0x20, 0x48, 0xE0, + 0x73, 0x27, 0x57, 0x43, + 0x04, 0xEB, 0xC7, 0x04, 0x04, 0x60, 0x45, 0xF8, 0x22, 0x40, 0x46, 0xF8, + 0x22, 0x00, 0x03, 0x20, + 0x88, 0x54, 0x11, 0x44, 0x48, 0x70, 0x8C, 0xF8, 0x02, 0x00, 0x8C, 0xF8, + 0x03, 0x00, 0x58, 0x68, + 0x00, 0x1F, 0x45, 0xE0, 0x5F, 0xF0, 0x00, 0x02, 0x11, 0xF8, 0x02, 0xC0, + 0xBC, 0xF1, 0x00, 0x0F, + 0x06, 0xD1, 0x01, 0xEB, 0x02, 0x0C, 0x9C, 0xF8, 0x01, 0xC0, 0xBC, 0xF1, + 0x00, 0x0F, 0x09, 0xD0, + 0x02, 0xF1, 0x02, 0x0C, 0x52, 0x1C, 0xBC, 0xF1, 0x05, 0x0F, 0xED, 0xDB, + 0x59, 0x68, 0x02, 0x29, + 0xD1, 0xD2, 0x18, 0xE0, 0x73, 0x27, 0x57, 0x43, 0x04, 0xEB, 0xC7, 0x04, + 0x04, 0x60, 0x45, 0xF8, + 0x22, 0x40, 0x46, 0xF8, 0x22, 0x00, 0x02, 0x20, 0x88, 0x54, 0x11, 0x44, + 0x48, 0x70, 0x58, 0x68, + 0x80, 0x1E, 0x1D, 0xE0, 0x5F, 0xF0, 0x04, 0x02, 0x11, 0xF8, 0x02, 0xC0, + 0xBC, 0xF1, 0x00, 0x0F, + 0x09, 0xD0, 0x52, 0x1E, 0xF8, 0xD2, 0x00, 0x2F, 0x13, 0xD1, 0x01, 0x46, + 0x4B, 0x20, 0xBD, 0xE8, + 0xF0, 0x41, 0x01, 0xF0, 0x6E, 0xBC, 0x73, 0x27, 0x57, 0x43, 0x04, 0xEB, + 0xC7, 0x04, 0x04, 0x60, + 0x45, 0xF8, 0x22, 0x40, 0x46, 0xF8, 0x22, 0x00, 0x01, 0x20, 0x88, 0x54, + 0x58, 0x68, 0x40, 0x1E, + 0x58, 0x60, 0x6A, 0xE7, 0xF0, 0xB4, 0x37, 0x4D, 0x34, 0x4C, 0x00, 0x22, + 0x04, 0x23, 0x0C, 0x35, + 0x06, 0x68, 0x08, 0x34, 0x55, 0xF8, 0x23, 0x70, 0xB7, 0x42, 0x14, 0xD1, + 0xE7, 0x5C, 0x8F, 0x42, + 0x11, 0xD1, 0x5A, 0x1C, 0x13, 0xD0, 0x00, 0x22, 0x02, 0x60, 0x45, 0xF8, + 0x23, 0x20, 0x2D, 0x4D, + 0x20, 0x35, 0x01, 0x29, 0x45, 0xF8, 0x23, 0x20, 0xE2, 0x54, 0x11, 0xD0, + 0x02, 0x29, 0x11, 0xD0, + 0x03, 0x29, 0x04, 0xD1, 0x08, 0xE0, 0x5B, 0x1E, 0xE4, 0xD2, 0x00, 0x2A, + 0x08, 0xD1, 0xF0, 0xBC, + 0x01, 0x46, 0x4D, 0x20, 0x01, 0xF0, 0x35, 0xBC, 0xE0, 0x18, 0x42, 0x70, + 0x82, 0x70, 0xC2, 0x70, + 0xF0, 0xBC, 0x70, 0x47, 0xE0, 0x18, 0x42, 0x70, 0xFA, 0xE7, 0x2D, 0xE9, + 0xF0, 0x41, 0x1D, 0x4E, + 0x1A, 0x4F, 0x0C, 0x36, 0x05, 0x24, 0x06, 0xF1, 0x14, 0x05, 0x08, 0x37, + 0x0D, 0xE0, 0x00, 0xBF, + 0x56, 0xF8, 0x24, 0x00, 0x48, 0xB1, 0x55, 0xF8, 0x24, 0x10, 0x4E, 0x20, + 0x01, 0xF0, 0x19, 0xFC, + 0x39, 0x5D, 0x55, 0xF8, 0x24, 0x00, 0xFF, 0xF7, 0xB5, 0xFF, 0x64, 0x1E, + 0xF0, 0xD2, 0x1C, 0xE7, + 0x30, 0xB5, 0x0E, 0x4A, 0x01, 0x20, 0x01, 0x46, 0x0E, 0x4B, 0x10, 0x60, + 0x5F, 0xF0, 0x00, 0x00, + 0x43, 0xF8, 0x21, 0x00, 0x49, 0x1C, 0x02, 0x29, 0xFA, 0xDD, 0x05, 0x21, + 0x09, 0x4C, 0x51, 0x60, + 0x0C, 0x34, 0x06, 0x4A, 0x04, 0x21, 0x08, 0x32, 0x04, 0xF1, 0x14, 0x03, + 0x44, 0xF8, 0x21, 0x00, + 0x50, 0x54, 0x43, 0xF8, 0x21, 0x00, 0x49, 0x1E, 0xF8, 0xD2, 0x30, 0xBD, + 0xB0, 0x06, 0x10, 0x00, + 0x3C, 0x7A, 0x01, 0x00, 0x9C, 0x4E, 0x10, 0x00, 0xD4, 0x56, 0x10, 0x00, + 0x73, 0x49, 0x00, 0x20, + 0x74, 0x4B, 0x08, 0x70, 0x72, 0x49, 0x01, 0x22, 0x88, 0x60, 0xC8, 0x60, + 0x08, 0x70, 0x1A, 0x70, + 0xC1, 0xF8, 0x01, 0x00, 0x70, 0x47, 0xFE, 0xB5, 0x6F, 0x4C, 0x6E, 0x4D, + 0x70, 0x4F, 0x22, 0x68, + 0xB2, 0xF8, 0x7A, 0x14, 0xAD, 0xF8, 0x04, 0x10, 0xB2, 0xF8, 0x7C, 0x14, + 0xAD, 0xF8, 0x06, 0x10, + 0xB2, 0xF8, 0x7E, 0x14, 0xAD, 0xF8, 0x08, 0x10, 0xB2, 0xF8, 0x80, 0x14, + 0xAD, 0xF8, 0x0A, 0x10, + 0x66, 0x49, 0x03, 0x68, 0x0B, 0x60, 0x43, 0x68, 0x4B, 0x60, 0x29, 0x78, + 0x00, 0x91, 0x92, 0xF8, + 0x82, 0x14, 0xB7, 0xF9, 0x00, 0x20, 0x01, 0xF0, 0x0F, 0x03, 0x01, 0x46, + 0x5F, 0x48, 0x08, 0x30, + 0x03, 0xF0, 0x0C, 0xFA, 0x00, 0x26, 0x2E, 0x70, 0x03, 0x20, 0xEF, 0xF7, + 0xDA, 0xFC, 0x5B, 0x49, + 0xB7, 0xF9, 0x00, 0x30, 0x08, 0x31, 0x5B, 0x4A, 0x01, 0xF1, 0x08, 0x00, + 0x03, 0xF0, 0xF1, 0xF9, + 0x03, 0x20, 0xEF, 0xF7, 0xE9, 0xFC, 0x55, 0x48, 0x01, 0xA9, 0x10, 0x30, + 0x00, 0xF0, 0x27, 0xF8, + 0x4E, 0x49, 0x4F, 0x4D, 0xC0, 0xB1, 0x01, 0x26, 0x0E, 0x70, 0x21, 0x68, + 0x91, 0xF8, 0x60, 0x14, + 0x89, 0x07, 0x12, 0xD5, 0x04, 0x46, 0x00, 0x90, 0x04, 0x21, 0x68, 0x46, + 0xF0, 0xF7, 0x23, 0xFA, + 0xC0, 0xB2, 0x01, 0x28, 0x02, 0xD9, 0xA8, 0x68, 0x10, 0xB1, 0x04, 0x46, + 0x20, 0x46, 0x04, 0xE0, + 0x28, 0x78, 0x86, 0x40, 0x34, 0x43, 0xF9, 0xE7, 0x0E, 0x70, 0xA9, 0x68, + 0x00, 0xF0, 0x43, 0xF8, + 0xA9, 0x68, 0x88, 0x42, 0x02, 0xD0, 0xA8, 0x60, 0x01, 0xF0, 0xA4, 0xFD, + 0xFE, 0xBD, 0x2D, 0xE9, + 0xFC, 0x43, 0x04, 0x46, 0x00, 0x20, 0x89, 0x46, 0xDF, 0xF8, 0xEC, 0xC0, + 0x80, 0x46, 0x6E, 0x46, + 0x00, 0x90, 0x03, 0x46, 0x02, 0x46, 0x01, 0x21, 0xCD, 0xF8, 0x04, 0x00, + 0xDC, 0xF8, 0x00, 0x50, + 0x95, 0xF8, 0x61, 0x74, 0x01, 0xFA, 0x02, 0xF5, 0x2F, 0x42, 0x05, 0xD0, + 0x34, 0xF8, 0x13, 0x50, + 0x5B, 0x1C, 0x26, 0xF8, 0x12, 0x50, 0x1B, 0xB2, 0x52, 0x1C, 0x12, 0xB2, + 0x04, 0x2A, 0xED, 0xDB, + 0x2F, 0x4B, 0x34, 0x46, 0x00, 0x22, 0x2A, 0x4E, 0xB3, 0xF9, 0x00, 0x50, + 0x0F, 0xE0, 0x00, 0xBF, + 0x34, 0xF9, 0x12, 0x30, 0x39, 0xF9, 0x12, 0x70, 0xBB, 0x42, 0x06, 0xDD, + 0x01, 0xFA, 0x02, 0xF7, + 0x38, 0x43, 0x43, 0x45, 0x01, 0xDD, 0x98, 0x46, 0x32, 0x70, 0x52, 0x1C, + 0x12, 0xB2, 0xAA, 0x42, + 0xEE, 0xDB, 0xBD, 0xE8, 0xFC, 0x83, 0x2D, 0xE9, 0xF0, 0x43, 0x1D, 0x4F, + 0x89, 0x46, 0x00, 0x22, + 0xF9, 0x68, 0x4F, 0xF0, 0x01, 0x08, 0x80, 0xEA, 0x01, 0x04, 0x1B, 0x49, + 0x7B, 0x1C, 0x0D, 0x68, + 0x1B, 0x49, 0xB1, 0xF9, 0x00, 0x60, 0x1B, 0xE0, 0x08, 0xFA, 0x02, 0xF1, + 0x21, 0x42, 0x0A, 0xD0, + 0x95, 0xF8, 0x83, 0xC4, 0x01, 0x42, 0x02, 0xD0, 0x0C, 0xF0, 0x0F, 0x0C, + 0x01, 0xE0, 0x4F, 0xEA, + 0x1C, 0x1C, 0x03, 0xF8, 0x02, 0xC0, 0x13, 0xF9, 0x02, 0xC0, 0xBC, 0xF1, + 0x00, 0x0F, 0x0D, 0xD0, + 0x03, 0xDD, 0xAC, 0xF1, 0x01, 0x0C, 0x03, 0xF8, 0x02, 0xC0, 0x02, 0xF1, + 0x01, 0x02, 0x12, 0xB2, + 0xB2, 0x42, 0xE1, 0xDB, 0xF8, 0x60, 0x48, 0x46, 0xBD, 0xE8, 0xF0, 0x83, + 0x01, 0x42, 0x02, 0xD0, + 0x41, 0xEA, 0x09, 0x09, 0xF1, 0xE7, 0x29, 0xEA, 0x01, 0x09, 0xEE, 0xE7, + 0x2C, 0x07, 0x10, 0x00, + 0xC0, 0x06, 0x10, 0x00, 0x7B, 0x07, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x9A, 0x1C, 0x01, 0x20, + 0x00, 0x07, 0x10, 0x00, 0xF4, 0x4A, 0x01, 0x20, 0xF0, 0xB5, 0x06, 0x46, + 0xF8, 0x48, 0x00, 0x24, + 0xA7, 0xB0, 0x00, 0x78, 0x0D, 0x46, 0x01, 0x28, 0x00, 0xD1, 0x01, 0x24, + 0x98, 0x21, 0x68, 0x46, + 0x06, 0xF0, 0xC4, 0xF9, 0xF3, 0x4A, 0x00, 0x21, 0x68, 0x46, 0x13, 0x78, + 0x11, 0xE0, 0x00, 0xBF, + 0x01, 0xFB, 0x03, 0xF7, 0x22, 0x46, 0x09, 0xE0, 0x07, 0xEB, 0x02, 0x0E, + 0x50, 0xF8, 0x22, 0xC0, + 0x36, 0xF9, 0x1E, 0xE0, 0xF4, 0x44, 0x40, 0xF8, 0x22, 0xC0, 0x52, 0x1C, + 0x9A, 0x42, 0xF3, 0xDB, + 0x49, 0x1C, 0xA9, 0x42, 0xEC, 0xDB, 0x21, 0x46, 0x07, 0xE0, 0x00, 0xBF, + 0x50, 0xF8, 0x21, 0x20, + 0x92, 0xFB, 0xF5, 0xF2, 0x40, 0xF8, 0x21, 0x20, 0x49, 0x1C, 0x99, 0x42, + 0xF6, 0xDB, 0x00, 0x21, + 0x17, 0xE0, 0x00, 0x27, 0x3A, 0x46, 0x0C, 0xE0, 0x02, 0xFB, 0x03, 0x4C, + 0x50, 0xF8, 0x24, 0xE0, + 0x36, 0xF9, 0x1C, 0xC0, 0xBC, 0xEB, 0x0E, 0x0C, 0x01, 0xD5, 0xCC, 0xF1, + 0x00, 0x0C, 0x67, 0x44, + 0x52, 0x1C, 0xAA, 0x42, 0xF0, 0xDB, 0x97, 0xFB, 0xF5, 0xF2, 0x8A, 0x42, + 0x00, 0xDD, 0x11, 0x46, + 0x64, 0x1C, 0x9C, 0x42, 0xE5, 0xDB, 0xB1, 0xF5, 0x80, 0x3F, 0x03, 0xDB, + 0x4F, 0xF6, 0xFF, 0x70, + 0x27, 0xB0, 0xF0, 0xBD, 0x88, 0xB2, 0xFB, 0xE7, 0xF0, 0xB5, 0xCF, 0x49, + 0xCF, 0x4B, 0x09, 0x68, + 0x91, 0xF8, 0xA0, 0x22, 0xD2, 0x07, 0x34, 0xD0, 0xCD, 0x4A, 0x57, 0x78, + 0x4F, 0xB1, 0x91, 0xF8, + 0xAB, 0x42, 0xB1, 0xF8, 0xA8, 0x52, 0x04, 0xF0, 0x0F, 0x02, 0x26, 0x09, + 0xB1, 0xF8, 0xA6, 0x42, + 0x08, 0xE0, 0x91, 0xF8, 0xAA, 0x42, 0xB1, 0xF8, 0xA4, 0x52, 0x04, 0xF0, + 0x0F, 0x02, 0x26, 0x09, + 0xB1, 0xF8, 0xA2, 0x42, 0xDF, 0xF8, 0x0C, 0xC3, 0x9C, 0xF8, 0x00, 0xC0, + 0xBC, 0xF1, 0x03, 0x0F, + 0x06, 0xD1, 0x91, 0xF8, 0xAC, 0x12, 0x0F, 0xB1, 0x0A, 0x09, 0x01, 0xE0, + 0x01, 0xF0, 0x0F, 0x02, + 0xA0, 0x42, 0x01, 0xDC, 0x1A, 0x60, 0xF0, 0xBD, 0xA8, 0x42, 0x01, 0xDB, + 0x1E, 0x60, 0xF0, 0xBD, + 0x00, 0x1B, 0xB1, 0x1A, 0x48, 0x43, 0x29, 0x1B, 0x90, 0xFB, 0xF1, 0xF0, + 0x10, 0x44, 0x18, 0x60, + 0xF0, 0xBD, 0x00, 0x20, 0xFB, 0xE7, 0xB0, 0x48, 0xB1, 0x49, 0x00, 0x68, + 0x0A, 0x78, 0x00, 0xF5, + 0xF9, 0x70, 0x80, 0x5C, 0xC8, 0x70, 0x70, 0x47, 0x2D, 0xE9, 0xF0, 0x0F, + 0x83, 0x46, 0xAA, 0x48, + 0xDF, 0xF8, 0xAC, 0x92, 0xDF, 0xF8, 0xB0, 0xA2, 0x01, 0x68, 0x99, 0xF8, + 0x00, 0x70, 0x00, 0x20, + 0x91, 0xF8, 0x0A, 0x62, 0x91, 0xF8, 0x09, 0x12, 0x3A, 0xF8, 0x17, 0x40, + 0x6F, 0xF0, 0x00, 0x42, + 0x4F, 0xEA, 0x11, 0x18, 0x0B, 0x07, 0x1C, 0xD0, 0x01, 0xF0, 0x0F, 0x05, + 0x9E, 0x4B, 0x00, 0x21, + 0x8C, 0x46, 0x1B, 0x68, 0xB4, 0x42, 0x93, 0xF8, 0x08, 0x32, 0x05, 0xDB, + 0xA6, 0x1B, 0x06, 0xFB, + 0x08, 0xF6, 0x96, 0xFB, 0xF5, 0xF5, 0x2B, 0x44, 0x97, 0x4D, 0x99, 0xF8, + 0x01, 0x60, 0x2D, 0x68, + 0x01, 0x2E, 0x95, 0xF8, 0xF1, 0x51, 0x06, 0xD0, 0x02, 0x2E, 0x07, 0xD0, + 0x05, 0xF0, 0x03, 0x06, + 0x13, 0xE0, 0x01, 0x25, 0xE2, 0xE7, 0xC5, 0xF3, 0x81, 0x06, 0x0E, 0xE0, + 0xC5, 0xF3, 0x01, 0x16, + 0x0B, 0xE0, 0x00, 0xBF, 0x3A, 0xF8, 0x1C, 0x50, 0xAA, 0x42, 0x01, 0xDD, + 0x2A, 0x46, 0x60, 0x46, + 0xA9, 0x42, 0x00, 0xDA, 0x29, 0x46, 0x0C, 0xF1, 0x01, 0x0C, 0xB4, 0x45, + 0xF2, 0xDD, 0xAB, 0xF8, + 0x00, 0x10, 0xA1, 0x1A, 0x99, 0x42, 0x01, 0xDC, 0xB7, 0x42, 0x04, 0xDD, + 0x89, 0xF8, 0x00, 0x00, + 0xBD, 0xE8, 0xF0, 0x0F, 0x9F, 0xE7, 0xBD, 0xE8, 0xF0, 0x0F, 0x70, 0x47, + 0x83, 0x49, 0x10, 0xB5, + 0x0B, 0x78, 0x7D, 0x49, 0x00, 0x22, 0x01, 0x2B, 0x09, 0x68, 0x02, 0xD1, + 0x80, 0x4B, 0x1B, 0x78, + 0x4B, 0xB1, 0xB1, 0xF8, 0xFC, 0x31, 0x83, 0x42, 0x18, 0xD2, 0x91, 0xF8, + 0xF0, 0x31, 0x1B, 0x07, + 0x14, 0xD5, 0x03, 0x22, 0x25, 0xE0, 0xB1, 0xF8, 0x02, 0x32, 0x83, 0x42, + 0x03, 0xD2, 0x91, 0xF8, + 0xF0, 0x31, 0x1B, 0x07, 0xF5, 0xD4, 0xB1, 0xF8, 0x00, 0x32, 0x83, 0x42, + 0x03, 0xD2, 0x91, 0xF8, + 0xF0, 0x31, 0x5B, 0x07, 0x0A, 0xD4, 0xB1, 0xF8, 0xFE, 0x31, 0x0B, 0xE0, + 0xB1, 0xF8, 0xFA, 0x31, + 0x83, 0x42, 0x05, 0xD2, 0x91, 0xF8, 0xF0, 0x31, 0x5B, 0x07, 0x01, 0xD5, + 0x02, 0x22, 0x08, 0xE0, + 0xB1, 0xF8, 0xF8, 0x31, 0x83, 0x42, 0x04, 0xD2, 0x91, 0xF8, 0xF0, 0x01, + 0x80, 0x07, 0x00, 0xD5, + 0x01, 0x22, 0x65, 0x48, 0x62, 0x4C, 0x14, 0x38, 0x03, 0x78, 0x9A, 0x42, + 0x11, 0xD1, 0x61, 0x78, + 0x99, 0x42, 0x1A, 0xD0, 0x63, 0x4A, 0x12, 0x78, 0x13, 0x43, 0x07, 0xD0, + 0x81, 0x68, 0x49, 0x1E, + 0x81, 0x60, 0x00, 0x29, 0x11, 0xDC, 0x00, 0x78, 0x60, 0x70, 0x0E, 0xE0, + 0x01, 0x70, 0x81, 0x68, + 0xF7, 0xE7, 0x8A, 0xB1, 0x02, 0x2A, 0x01, 0xD1, 0x01, 0x2B, 0x0D, 0xD0, + 0x91, 0xF8, 0xF6, 0x11, + 0x81, 0x60, 0x02, 0x70, 0x00, 0x29, 0x00, 0xDC, 0x62, 0x70, 0x57, 0x48, + 0x62, 0x78, 0x41, 0x7B, + 0x62, 0xF3, 0x03, 0x01, 0x41, 0x73, 0x10, 0xBD, 0x91, 0xF8, 0xF7, 0x11, + 0xF0, 0xE7, 0x4A, 0x4A, + 0x12, 0x68, 0x92, 0xF8, 0xF0, 0x21, 0xD2, 0x06, 0x07, 0xD5, 0x42, 0x1A, + 0x00, 0x2A, 0x04, 0xDC, + 0xC1, 0xEB, 0xC1, 0x11, 0x08, 0x44, 0xC0, 0xF3, 0xCF, 0x10, 0x70, 0x47, + 0x44, 0x49, 0x00, 0x20, + 0x41, 0x4A, 0x48, 0x70, 0x44, 0x49, 0x14, 0x39, 0x48, 0x60, 0x88, 0x60, + 0x08, 0x70, 0x12, 0x68, + 0x92, 0xF8, 0xA0, 0x32, 0xDB, 0x07, 0x03, 0xD0, 0x92, 0xF8, 0xAB, 0x22, + 0x12, 0x09, 0x00, 0xE0, + 0x00, 0x22, 0x3A, 0x4B, 0x1A, 0x60, 0xC8, 0x60, 0x08, 0x61, 0x48, 0x61, + 0x88, 0x61, 0x70, 0x47, + 0x2D, 0xE9, 0xF8, 0x4F, 0x00, 0x21, 0xAD, 0xF8, 0x00, 0x10, 0x32, 0x49, + 0xDF, 0xF8, 0xD8, 0x90, + 0xDF, 0xF8, 0xD4, 0x80, 0x0D, 0x78, 0x4F, 0xF0, 0x10, 0x0B, 0x2F, 0x01, + 0x06, 0x46, 0x00, 0x24, + 0xA9, 0xF1, 0x08, 0x09, 0xA8, 0xF1, 0x14, 0x0A, 0x10, 0xE0, 0x2A, 0x46, + 0x59, 0x46, 0x30, 0x46, + 0xFF, 0xF7, 0x5A, 0xFE, 0x29, 0xF8, 0x14, 0x00, 0x43, 0x46, 0x38, 0xF8, + 0x14, 0x10, 0xFF, 0xF7, + 0xB6, 0xFF, 0x23, 0xF8, 0x14, 0x00, 0x06, 0xEB, 0x47, 0x06, 0x64, 0x1C, + 0x9A, 0xF8, 0x01, 0x00, + 0x84, 0x42, 0xEA, 0xDD, 0x68, 0x46, 0xFF, 0xF7, 0xE7, 0xFE, 0x1F, 0x48, + 0x20, 0x4D, 0x00, 0x68, + 0x90, 0xF8, 0xF0, 0x01, 0x40, 0x06, 0x02, 0xD5, 0xBD, 0xF8, 0x00, 0x40, + 0x02, 0xE0, 0x29, 0x78, + 0x38, 0xF8, 0x11, 0x40, 0x21, 0x48, 0x04, 0x80, 0x20, 0x46, 0xFF, 0xF7, + 0x2F, 0xFF, 0x20, 0x46, + 0xFF, 0xF7, 0x8A, 0xFE, 0x29, 0x78, 0x39, 0xF8, 0x11, 0x00, 0x1B, 0x49, + 0xA1, 0xF8, 0x1B, 0x00, + 0xBD, 0xE8, 0xF8, 0x8F, 0x2D, 0xE9, 0xFC, 0x5F, 0x0E, 0x49, 0x4F, 0xF0, + 0x00, 0x09, 0x0D, 0x78, + 0x0B, 0x49, 0x09, 0x78, 0x01, 0x29, 0x00, 0xD1, 0x89, 0x46, 0x0F, 0x49, + 0x10, 0x26, 0x14, 0x39, + 0x04, 0x46, 0x4A, 0x68, 0x12, 0x48, 0x92, 0xFB, 0xF6, 0xF3, 0x06, 0xFB, + 0x13, 0x22, 0x6A, 0x43, + 0x4F, 0x78, 0x4F, 0xEA, 0x05, 0x1A, 0x00, 0xEB, 0x42, 0x08, 0x83, 0x46, + 0x7F, 0x1C, 0x3D, 0xE0, + 0xF3, 0x06, 0x10, 0x00, 0xEE, 0x06, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x9C, 0x06, 0x10, 0x00, + 0x74, 0x07, 0x10, 0x00, 0xEC, 0x06, 0x10, 0x00, 0xE4, 0x06, 0x10, 0x00, + 0xF6, 0x06, 0x10, 0x00, + 0xF7, 0x06, 0x10, 0x00, 0xF5, 0x06, 0x10, 0x00, 0x0A, 0x44, 0x01, 0x20, + 0x78, 0x06, 0x10, 0x00, + 0xB2, 0x1C, 0x01, 0x20, 0x2A, 0x48, 0x00, 0x68, 0x90, 0xF8, 0xF0, 0x11, + 0x89, 0x06, 0x14, 0xD5, + 0x28, 0x49, 0x49, 0x68, 0x00, 0x29, 0x0E, 0xDD, 0xA5, 0xEB, 0x09, 0x02, + 0xB0, 0xF9, 0x06, 0x12, + 0xCD, 0xE9, 0x00, 0x12, 0xB0, 0xF9, 0x04, 0x32, 0x04, 0xEB, 0x49, 0x00, + 0x0B, 0xEB, 0x49, 0x02, + 0x01, 0x46, 0x02, 0xF0, 0x9D, 0xFE, 0x0B, 0xEB, 0x4A, 0x0B, 0x6A, 0x00, + 0x21, 0x46, 0x40, 0x46, + 0x05, 0xF0, 0x03, 0xFF, 0x04, 0xEB, 0x45, 0x04, 0x08, 0xEB, 0x4A, 0x08, + 0x7F, 0x1E, 0xD9, 0xD2, + 0x18, 0x48, 0x4F, 0xF0, 0xFF, 0x31, 0x01, 0xEB, 0x46, 0x01, 0x42, 0x68, + 0x52, 0x1C, 0x42, 0x60, + 0x91, 0x42, 0x00, 0xDA, 0x46, 0x60, 0x40, 0x68, 0xB0, 0x42, 0x03, 0xDB, + 0xBD, 0xE8, 0xFC, 0x5F, + 0x11, 0x48, 0x45, 0xE7, 0xBD, 0xE8, 0xFC, 0x9F, 0x28, 0xE7, 0x10, 0xB5, + 0x0C, 0x4C, 0x20, 0x68, + 0x90, 0xF8, 0xF1, 0x01, 0xC0, 0xF3, 0x01, 0x12, 0xC0, 0xF3, 0x81, 0x01, + 0x00, 0xF0, 0x03, 0x00, + 0x01, 0xF0, 0x5B, 0xFF, 0x07, 0x49, 0x48, 0x70, 0x20, 0x68, 0x08, 0x49, + 0x90, 0xF8, 0xEC, 0x00, + 0x00, 0xF0, 0x03, 0x00, 0x08, 0x70, 0xFF, 0xF7, 0x36, 0xFE, 0xBD, 0xE8, + 0x10, 0x40, 0x0D, 0xE7, + 0x4C, 0x07, 0x10, 0x00, 0xD0, 0x06, 0x10, 0x00, 0xB2, 0x1C, 0x01, 0x20, + 0x74, 0x07, 0x10, 0x00, + 0x10, 0xB5, 0xBA, 0x48, 0x00, 0x24, 0x84, 0x61, 0x04, 0x62, 0xC4, 0x61, + 0xFF, 0xF7, 0xB9, 0xFA, + 0xFF, 0xF7, 0x6E, 0xFC, 0xB6, 0x48, 0x04, 0x80, 0xBD, 0xE8, 0x10, 0x40, + 0x00, 0xF0, 0x11, 0xB9, + 0x10, 0xB5, 0xB4, 0x48, 0xB1, 0x4C, 0x90, 0xF8, 0x30, 0x10, 0x61, 0x70, + 0x90, 0xF8, 0x31, 0x20, + 0xA2, 0x70, 0x51, 0x43, 0x61, 0x82, 0x90, 0xF8, 0x33, 0x10, 0xE1, 0x70, + 0x10, 0xF8, 0x2C, 0x1F, + 0x21, 0x70, 0x41, 0x78, 0x21, 0x71, 0x81, 0x78, 0x61, 0x71, 0xC1, 0x78, + 0xA1, 0x71, 0x41, 0x7A, + 0xE1, 0x71, 0x80, 0x7A, 0x20, 0x72, 0xA8, 0x48, 0x60, 0x62, 0x00, 0xF2, + 0x3C, 0x60, 0xA0, 0x62, + 0xA0, 0xF6, 0x78, 0x40, 0xE0, 0x62, 0xA5, 0x48, 0x20, 0x63, 0xA5, 0x48, + 0x01, 0x21, 0x00, 0x68, + 0x00, 0xF2, 0x61, 0x40, 0xEF, 0xF7, 0xDF, 0xFE, 0xA0, 0x82, 0x10, 0xBD, + 0x9D, 0x48, 0x10, 0xB5, + 0x9B, 0x49, 0x90, 0xF8, 0x2C, 0x00, 0x99, 0x4A, 0x88, 0x70, 0x08, 0x79, + 0x53, 0x7A, 0x63, 0xF3, + 0x00, 0x00, 0xD3, 0x7A, 0x63, 0xF3, 0xC3, 0x00, 0x93, 0x7A, 0x63, 0xF3, + 0x82, 0x00, 0x13, 0x7B, + 0x63, 0xF3, 0x41, 0x00, 0x97, 0x4B, 0xDB, 0x78, 0x0B, 0x72, 0x93, 0x7B, + 0xCB, 0x71, 0x53, 0x7B, + 0xD4, 0x7B, 0x63, 0xF3, 0x04, 0x10, 0x11, 0xF8, 0x0E, 0x3F, 0x12, 0x7C, + 0x64, 0xF3, 0x03, 0x03, + 0x62, 0xF3, 0x07, 0x13, 0x90, 0x4A, 0x01, 0xF8, 0x0B, 0x39, 0x12, 0x78, + 0x81, 0xF8, 0x25, 0x20, + 0x8E, 0x4A, 0x12, 0x88, 0xCA, 0x84, 0x8E, 0x4A, 0x12, 0x78, 0x62, 0xF3, + 0xC7, 0x10, 0x48, 0x70, + 0x8C, 0x48, 0x00, 0x88, 0x08, 0x85, 0x8C, 0x48, 0x00, 0x88, 0x48, 0x85, + 0x8B, 0x48, 0x02, 0x78, + 0x08, 0x7B, 0x62, 0xF3, 0x03, 0x00, 0x08, 0x73, 0x89, 0x48, 0x02, 0x78, + 0x48, 0x7B, 0x62, 0xF3, + 0x00, 0x00, 0x88, 0x4A, 0x12, 0x78, 0x62, 0xF3, 0x41, 0x00, 0x20, 0xF0, + 0x04, 0x00, 0x48, 0x73, + 0x10, 0xBD, 0x70, 0xB5, 0x0D, 0x46, 0x80, 0xEA, 0x05, 0x04, 0xE0, 0x06, + 0x01, 0xD5, 0xF6, 0xF7, + 0x89, 0xFA, 0xA0, 0x06, 0x01, 0xD5, 0xF4, 0xF7, 0x65, 0xFD, 0x14, 0xF0, + 0x48, 0x0F, 0x05, 0xD0, + 0x6E, 0x48, 0x15, 0xF0, 0x48, 0x01, 0x00, 0xD0, 0x01, 0x21, 0x41, 0x74, + 0x70, 0xBD, 0x70, 0xB5, + 0x79, 0x4D, 0x2C, 0x68, 0x00, 0x2C, 0x0D, 0xD0, 0x68, 0x49, 0xA0, 0xB2, + 0x0A, 0x7A, 0xC9, 0x79, + 0x01, 0xF0, 0x44, 0xF9, 0x28, 0x68, 0xA0, 0x43, 0x28, 0x60, 0x03, 0xD1, + 0xBD, 0xE8, 0x70, 0x40, + 0x01, 0xF0, 0xB7, 0xB9, 0x70, 0xBD, 0xF8, 0xB5, 0x00, 0x26, 0x00, 0xF0, + 0xC1, 0xFF, 0x01, 0x20, + 0xEF, 0xF7, 0xF7, 0xF8, 0x00, 0xF0, 0x8F, 0xF8, 0x5C, 0x4D, 0x5E, 0x4C, + 0x95, 0xF8, 0x20, 0x00, + 0x80, 0x07, 0x02, 0xD5, 0xA0, 0x68, 0xFF, 0xF7, 0xE6, 0xFB, 0x95, 0xF8, + 0x20, 0x00, 0xC0, 0x07, + 0x09, 0xD0, 0xE0, 0x68, 0x00, 0x90, 0xD4, 0xE9, 0x00, 0x01, 0xD4, 0xE9, + 0x04, 0x23, 0xFF, 0xF7, + 0x8A, 0xF9, 0x06, 0x00, 0x0A, 0xD1, 0x28, 0x8C, 0x80, 0x05, 0x07, 0xD5, + 0x94, 0xF8, 0x32, 0x10, + 0xA0, 0x69, 0xF4, 0xF7, 0xF8, 0xFD, 0xE8, 0x69, 0x18, 0xB9, 0x0C, 0xE0, + 0xE8, 0x69, 0x50, 0xB1, + 0x4E, 0xB9, 0x03, 0x20, 0xEF, 0xF7, 0xCD, 0xF8, 0x4A, 0x48, 0xE9, 0x69, + 0xFE, 0xF7, 0x8B, 0xFF, + 0x03, 0x20, 0xEF, 0xF7, 0xE1, 0xF8, 0xFF, 0xF7, 0x51, 0xFF, 0xFF, 0xF7, + 0xB0, 0xFF, 0xFF, 0xF7, + 0x6C, 0xFB, 0xFF, 0xF7, 0x88, 0xFA, 0xBD, 0xE8, 0xF8, 0x40, 0x01, 0x20, + 0xEF, 0xF7, 0xD4, 0xB8, + 0x3E, 0x48, 0x81, 0x7A, 0x01, 0x29, 0x08, 0xD0, 0xC1, 0x7A, 0x01, 0x29, + 0x05, 0xD0, 0x01, 0x7B, + 0x01, 0x29, 0x02, 0xD0, 0x80, 0x7B, 0x00, 0x28, 0x00, 0xD0, 0x01, 0x20, + 0x70, 0x47, 0x08, 0xB5, + 0xFF, 0xF7, 0xF6, 0xFE, 0x6B, 0x46, 0x00, 0x22, 0x03, 0x21, 0x02, 0x20, + 0xEF, 0xF7, 0x0A, 0xF8, + 0x00, 0x98, 0xC0, 0x07, 0x05, 0xD0, 0x01, 0x21, 0x02, 0x20, 0xEE, 0xF7, + 0xE6, 0xFF, 0xFF, 0xF7, + 0x9A, 0xFF, 0x00, 0x98, 0x80, 0x07, 0xED, 0xD5, 0x02, 0x21, 0x08, 0x46, + 0xEE, 0xF7, 0xDD, 0xFF, + 0xE8, 0xE7, 0x2F, 0x48, 0x10, 0xB5, 0x02, 0x68, 0x28, 0x48, 0x00, 0x24, + 0x92, 0xF8, 0x31, 0x10, + 0x41, 0x70, 0x92, 0xF8, 0x30, 0x20, 0x82, 0x70, 0x51, 0x43, 0x41, 0x82, + 0x29, 0x49, 0xC9, 0x78, + 0xC1, 0x70, 0x01, 0x21, 0x01, 0x71, 0x41, 0x71, 0x81, 0x71, 0x01, 0x70, + 0x84, 0x82, 0xC4, 0x71, + 0x04, 0x72, 0xC4, 0x73, 0x04, 0x74, 0xFF, 0xF7, 0x62, 0xF9, 0xFF, 0xF7, + 0x5F, 0xFB, 0xFE, 0xF7, + 0x9D, 0xFE, 0x43, 0xF6, 0x58, 0x61, 0x29, 0x48, 0x05, 0xF0, 0xFE, 0xFD, + 0x28, 0x48, 0x04, 0x80, + 0x28, 0x48, 0x04, 0x80, 0x10, 0xBD, 0x70, 0xB5, 0x14, 0x4E, 0x16, 0x4D, + 0x31, 0x6A, 0x28, 0x6A, + 0x81, 0xEA, 0x00, 0x04, 0x19, 0xB9, 0x10, 0xB1, 0xFF, 0xF7, 0xAA, 0xFE, + 0x11, 0xE0, 0xA0, 0x07, + 0x01, 0xD5, 0xFF, 0xF7, 0x43, 0xFB, 0xE0, 0x07, 0x03, 0xD0, 0xFF, 0xF7, + 0x40, 0xF9, 0xFF, 0xF7, + 0x7B, 0xFE, 0xE0, 0x06, 0x01, 0xD5, 0xFF, 0xF7, 0xED, 0xF8, 0xA0, 0x05, + 0x01, 0xD5, 0xF4, 0xF7, + 0x61, 0xFD, 0xE9, 0x69, 0xB0, 0x69, 0xFF, 0xF7, 0x1C, 0xFF, 0xE8, 0x69, + 0xB0, 0x61, 0x28, 0x6A, + 0x30, 0x62, 0x68, 0x6A, 0xF0, 0x61, 0xBD, 0xE8, 0x70, 0x40, 0x99, 0xE6, + 0xEC, 0x06, 0x10, 0x00, + 0x0A, 0x44, 0x01, 0x20, 0xCC, 0x68, 0x10, 0x00, 0xEE, 0x35, 0x01, 0x20, + 0x42, 0x44, 0x01, 0x20, + 0x4C, 0x07, 0x10, 0x00, 0x74, 0x07, 0x10, 0x00, 0x75, 0x06, 0x10, 0x00, + 0x7A, 0x06, 0x10, 0x00, + 0x7D, 0x07, 0x10, 0x00, 0x7E, 0x07, 0x10, 0x00, 0x80, 0x07, 0x10, 0x00, + 0x66, 0x05, 0x10, 0x00, + 0x64, 0x05, 0x10, 0x00, 0x74, 0x05, 0x10, 0x00, 0x68, 0x07, 0x10, 0x00, + 0x20, 0x4D, 0x01, 0x20, + 0x82, 0x07, 0x10, 0x00, 0x84, 0x07, 0x10, 0x00, 0x0B, 0x4D, 0x00, 0x20, + 0x0B, 0x4C, 0x28, 0x60, + 0x28, 0x68, 0x40, 0x1C, 0x28, 0x60, 0xEE, 0xF7, 0xEB, 0xFD, 0xF0, 0xF7, + 0x70, 0xF9, 0x01, 0x28, + 0x20, 0x68, 0x06, 0xD0, 0x40, 0xF0, 0x04, 0x00, 0x20, 0x60, 0xEE, 0xF7, + 0xDF, 0xFD, 0x30, 0xBF, + 0xEE, 0xE7, 0x20, 0xF0, 0x04, 0x00, 0xF7, 0xE7, 0x30, 0x07, 0x10, 0x00, + 0x10, 0xED, 0x00, 0xE0, + 0x2D, 0xE9, 0xF0, 0x47, 0x1C, 0x4D, 0xDF, 0xF8, 0x74, 0x90, 0xA8, 0x46, + 0x20, 0x21, 0x1A, 0x48, + 0x05, 0xF0, 0x82, 0xFD, 0x18, 0x48, 0x00, 0xF0, 0x53, 0xFB, 0x47, 0x1E, + 0xD9, 0xF8, 0x00, 0x00, + 0x98, 0xF8, 0x00, 0x60, 0x08, 0xF1, 0x01, 0x04, 0x90, 0xF8, 0xA0, 0x00, + 0xC0, 0x07, 0x05, 0xD0, + 0x3A, 0x46, 0x21, 0x46, 0x30, 0x46, 0x00, 0xF0, 0x23, 0xF8, 0x28, 0xB9, + 0x3A, 0x46, 0x21, 0x46, + 0x30, 0x46, 0x00, 0xF0, 0xD3, 0xF8, 0x18, 0xB1, 0x01, 0x20, 0x29, 0x68, + 0x01, 0xF0, 0xF3, 0xF8, + 0x00, 0xF0, 0x50, 0xFB, 0x00, 0x28, 0xD9, 0xD1, 0xBD, 0xE8, 0xF0, 0x87, + 0x38, 0xB5, 0x0C, 0x46, + 0x05, 0x46, 0xEE, 0xF7, 0xE6, 0xFE, 0x69, 0x46, 0x28, 0x46, 0xEE, 0xF7, + 0x1C, 0xFF, 0x00, 0x99, + 0x21, 0x42, 0xF8, 0xD1, 0x38, 0xBD, 0x00, 0x00, 0xD0, 0x4E, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, + 0x70, 0xB5, 0x0D, 0x46, 0x00, 0x24, 0xA0, 0xF1, 0x10, 0x01, 0x03, 0x29, + 0x01, 0xD8, 0x52, 0x48, + 0x05, 0xE0, 0xA0, 0xF1, 0x30, 0x01, 0x0A, 0x29, 0x06, 0xD8, 0x4F, 0x48, + 0x20, 0x30, 0x50, 0xF8, + 0x31, 0x30, 0x00, 0xEB, 0xC1, 0x00, 0x0A, 0xE0, 0x62, 0x28, 0x10, 0xD1, + 0x4A, 0x49, 0xA0, 0xF1, + 0x62, 0x00, 0xA1, 0xF1, 0x08, 0x01, 0x51, 0xF8, 0x30, 0x30, 0x01, 0xEB, + 0xC0, 0x00, 0x4F, 0xF0, + 0x01, 0x04, 0x40, 0x68, 0x90, 0x42, 0x02, 0xD1, 0x11, 0x46, 0x28, 0x46, + 0x98, 0x47, 0x20, 0x46, + 0x70, 0xBD, 0x42, 0x49, 0x43, 0x20, 0x08, 0x60, 0x04, 0x21, 0x01, 0x20, + 0xFF, 0xF7, 0xBE, 0xBF, + 0x3E, 0x49, 0x00, 0x20, 0x08, 0x60, 0x04, 0x21, 0x01, 0x20, 0xFF, 0xF7, + 0xB7, 0xBF, 0xEF, 0xF7, + 0x92, 0xBB, 0x10, 0xB5, 0xEE, 0xF7, 0x5C, 0xFD, 0x39, 0x49, 0x1E, 0x20, + 0x08, 0x60, 0xEE, 0xF7, + 0x55, 0xFD, 0x02, 0x21, 0x05, 0x20, 0x01, 0xF0, 0x96, 0xF8, 0xBD, 0xE8, + 0x10, 0x40, 0x4F, 0xF4, + 0x80, 0x71, 0x00, 0x20, 0xFF, 0xF7, 0xA2, 0xBF, 0x32, 0x49, 0x02, 0x78, + 0x0A, 0x70, 0x42, 0x78, + 0x4A, 0x70, 0x01, 0x78, 0x4B, 0x07, 0x30, 0x49, 0x0A, 0x68, 0x02, 0xD5, + 0x42, 0xF0, 0x04, 0x02, + 0x01, 0xE0, 0x22, 0xF0, 0x04, 0x02, 0x0A, 0x60, 0x02, 0x78, 0x12, 0x07, + 0x0A, 0x68, 0x02, 0xD5, + 0x42, 0xF0, 0x02, 0x02, 0x01, 0xE0, 0x22, 0xF0, 0x02, 0x02, 0x0A, 0x60, + 0x02, 0x78, 0xD2, 0x06, + 0x0A, 0x68, 0x02, 0xD5, 0x42, 0xF0, 0x01, 0x02, 0x01, 0xE0, 0x22, 0xF0, + 0x01, 0x02, 0x0A, 0x60, + 0x00, 0x78, 0x40, 0x06, 0x02, 0xD5, 0x42, 0xF0, 0x10, 0x00, 0x01, 0xE0, + 0x22, 0xF0, 0x10, 0x00, + 0x08, 0x60, 0x08, 0x21, 0x01, 0x20, 0xFF, 0xF7, 0x71, 0xBF, 0x00, 0x78, + 0x28, 0xB1, 0x01, 0x28, + 0x05, 0xD1, 0x10, 0x21, 0x01, 0x20, 0xFF, 0xF7, 0x69, 0xBF, 0x04, 0x21, + 0xFA, 0xE7, 0x70, 0x47, + 0x02, 0x78, 0x15, 0x48, 0x01, 0x68, 0x22, 0xB1, 0x01, 0x2A, 0x05, 0xD0, + 0x02, 0x2A, 0x08, 0xD1, + 0x0B, 0xE0, 0x21, 0xF0, 0x08, 0x01, 0x01, 0xE0, 0x41, 0xF0, 0x08, 0x01, + 0x21, 0xF0, 0x40, 0x01, + 0x01, 0x60, 0x08, 0x21, 0x01, 0x20, 0xFF, 0xF7, 0x51, 0xBF, 0x21, 0xF0, + 0x08, 0x01, 0x41, 0xF0, + 0x40, 0x01, 0xF5, 0xE7, 0x70, 0x47, 0x70, 0x47, 0x70, 0x47, 0x70, 0x47, + 0x70, 0x47, 0x70, 0x47, + 0x70, 0x47, 0x70, 0x47, 0x00, 0xF0, 0x21, 0xBC, 0x50, 0x7A, 0x01, 0x00, + 0x6C, 0x07, 0x10, 0x00, + 0x54, 0x07, 0x10, 0x00, 0xF0, 0x4F, 0x10, 0x00, 0x70, 0x07, 0x10, 0x00, + 0x0B, 0x46, 0x10, 0xB5, + 0x00, 0x21, 0xA0, 0x38, 0x09, 0x28, 0x06, 0xD8, 0xF7, 0x49, 0x51, 0xF8, + 0x20, 0x40, 0x11, 0x46, + 0x18, 0x46, 0xA0, 0x47, 0x01, 0x46, 0x08, 0x46, 0x10, 0xBD, 0x00, 0x20, + 0x70, 0x47, 0x70, 0xB5, + 0x00, 0x23, 0x00, 0x29, 0x11, 0xDD, 0x02, 0x78, 0x49, 0x1E, 0x03, 0x2A, + 0x0D, 0xD2, 0xEE, 0x4D, + 0x40, 0x1C, 0x28, 0x35, 0x55, 0xF8, 0x32, 0x40, 0x05, 0xEB, 0xC2, 0x02, + 0x52, 0x68, 0x00, 0x2A, + 0x03, 0xDB, 0x91, 0x42, 0x01, 0xD1, 0xA0, 0x47, 0x03, 0x46, 0x18, 0x46, + 0x70, 0xBD, 0x10, 0xB5, + 0x00, 0x78, 0x00, 0x21, 0xC2, 0x07, 0x00, 0xD0, 0x01, 0x21, 0x82, 0x07, + 0x01, 0xD5, 0x41, 0xF0, + 0x02, 0x01, 0xC0, 0x06, 0x01, 0xD5, 0x41, 0xF0, 0x40, 0x01, 0xE0, 0x48, + 0x02, 0x68, 0x8A, 0x42, + 0x04, 0xD0, 0x01, 0x60, 0x04, 0x21, 0x01, 0x20, 0xFF, 0xF7, 0xF8, 0xFE, + 0x01, 0x20, 0x10, 0xBD, + 0x00, 0x20, 0x70, 0x47, 0x70, 0xB5, 0x00, 0x23, 0x00, 0x29, 0x0F, 0xDD, + 0x02, 0x78, 0x49, 0x1E, + 0x05, 0x2A, 0x0B, 0xD2, 0xD4, 0x4D, 0x40, 0x1C, 0x40, 0x35, 0x55, 0xF8, + 0x32, 0x40, 0x05, 0xEB, + 0xC2, 0x02, 0x52, 0x68, 0x91, 0x42, 0x01, 0xD1, 0xA0, 0x47, 0x03, 0x46, + 0x18, 0x46, 0x70, 0xBD, + 0x10, 0xB5, 0x00, 0x78, 0xC2, 0x07, 0xCE, 0x48, 0x01, 0x68, 0x02, 0xD0, + 0x41, 0xF0, 0x02, 0x01, + 0x01, 0xE0, 0x21, 0xF0, 0x02, 0x01, 0x01, 0x60, 0x08, 0x21, 0x01, 0x20, + 0xFF, 0xF7, 0xCE, 0xFE, + 0x01, 0x20, 0x10, 0xBD, 0x10, 0xB5, 0x00, 0x78, 0xC2, 0x07, 0xC5, 0x48, + 0x01, 0x68, 0x02, 0xD0, + 0x41, 0xF0, 0x04, 0x01, 0x01, 0xE0, 0x21, 0xF0, 0x04, 0x01, 0x01, 0x60, + 0x08, 0x21, 0x01, 0x20, + 0xFF, 0xF7, 0xBC, 0xFE, 0x01, 0x20, 0x10, 0xBD, 0x10, 0xB5, 0x01, 0x78, + 0xCB, 0x07, 0xBC, 0x49, + 0x0A, 0x68, 0x02, 0xD0, 0x42, 0xF0, 0x08, 0x02, 0x01, 0xE0, 0x22, 0xF0, + 0x08, 0x02, 0x0A, 0x60, + 0x00, 0x78, 0x80, 0x07, 0x02, 0xD5, 0x42, 0xF0, 0x40, 0x00, 0x01, 0xE0, + 0x22, 0xF0, 0x40, 0x00, + 0x08, 0x60, 0x08, 0x21, 0x01, 0x20, 0xFF, 0xF7, 0xA1, 0xFE, 0x01, 0x20, + 0x10, 0xBD, 0x00, 0x20, + 0x70, 0x47, 0x00, 0x20, 0x70, 0x47, 0x70, 0xB5, 0x00, 0x23, 0x00, 0x29, + 0x0F, 0xDD, 0x02, 0x78, + 0x49, 0x1E, 0x0A, 0x2A, 0x0B, 0xD2, 0xA8, 0x4D, 0x40, 0x1C, 0x68, 0x35, + 0x55, 0xF8, 0x32, 0x40, + 0x05, 0xEB, 0xC2, 0x02, 0x52, 0x68, 0x91, 0x42, 0x01, 0xD1, 0xA0, 0x47, + 0x03, 0x46, 0x18, 0x46, + 0x70, 0xBD, 0x01, 0x46, 0x00, 0x20, 0x09, 0x78, 0x04, 0x29, 0x06, 0xD2, + 0x9E, 0x48, 0xB8, 0x30, + 0x50, 0xF8, 0x21, 0x20, 0x00, 0x21, 0x08, 0x46, 0x10, 0x47, 0x70, 0x47, + 0x10, 0xB5, 0xEF, 0xF7, + 0x52, 0xFA, 0x01, 0x20, 0x10, 0xBD, 0x10, 0xB5, 0x00, 0xF0, 0x4F, 0xFB, + 0x01, 0x20, 0x10, 0xBD, + 0x10, 0xB5, 0x4F, 0xF4, 0x00, 0x61, 0x00, 0x20, 0xFF, 0xF7, 0x68, 0xFE, + 0x01, 0x20, 0x10, 0xBD, + 0x10, 0xB5, 0x4F, 0xF4, 0x80, 0x51, 0x00, 0x20, 0xFF, 0xF7, 0x60, 0xFE, + 0x01, 0x20, 0x10, 0xBD, + 0x10, 0xB5, 0x00, 0x78, 0xC0, 0x07, 0x02, 0xD0, 0x00, 0xF0, 0xD8, 0xFA, + 0x01, 0xE0, 0x00, 0xF0, + 0xCC, 0xFA, 0x01, 0x20, 0x10, 0xBD, 0x10, 0xB5, 0x00, 0x88, 0x00, 0x24, + 0xC1, 0x07, 0x00, 0xD0, + 0x02, 0x24, 0x81, 0x07, 0x01, 0xD5, 0x44, 0xF0, 0x04, 0x04, 0x41, 0x07, + 0x01, 0xD5, 0x44, 0xF0, + 0x08, 0x04, 0xC1, 0x06, 0x01, 0xD5, 0x44, 0xF0, 0x10, 0x04, 0x00, 0x06, + 0x01, 0xD5, 0x44, 0xF0, + 0x80, 0x04, 0x94, 0xB1, 0xEE, 0xF7, 0xE4, 0xFB, 0x7E, 0x48, 0x01, 0x68, + 0x21, 0x43, 0x01, 0x60, + 0xEE, 0xF7, 0xDC, 0xFB, 0x02, 0x21, 0x05, 0x20, 0x00, 0xF0, 0x1D, 0xFF, + 0x4F, 0xF4, 0x80, 0x71, + 0x00, 0x20, 0xFF, 0xF7, 0x2B, 0xFE, 0x01, 0x20, 0x10, 0xBD, 0x00, 0x20, + 0x10, 0xBD, 0x10, 0xB5, + 0x00, 0x88, 0x00, 0x24, 0xC1, 0x07, 0x00, 0xD0, 0x01, 0x24, 0x81, 0x07, + 0x01, 0xD5, 0x44, 0xF0, + 0x02, 0x04, 0x41, 0x07, 0x01, 0xD5, 0x44, 0xF0, 0x04, 0x04, 0x01, 0x07, + 0x01, 0xD5, 0x44, 0xF0, + 0x08, 0x04, 0xC1, 0x05, 0x01, 0xD5, 0x44, 0xF4, 0x00, 0x74, 0x81, 0x05, + 0x01, 0xD5, 0x44, 0xF4, + 0x80, 0x64, 0xC1, 0x06, 0x01, 0xD5, 0x44, 0xF0, 0x10, 0x04, 0x00, 0x06, + 0x01, 0xD5, 0x44, 0xF0, + 0x80, 0x04, 0x6C, 0xB1, 0xEE, 0xF7, 0xAC, 0xFB, 0x63, 0x48, 0x01, 0x68, + 0x21, 0x43, 0x01, 0x60, + 0xEE, 0xF7, 0xA4, 0xFB, 0x02, 0x21, 0x00, 0x20, 0xFF, 0xF7, 0xF8, 0xFD, + 0x01, 0x20, 0x10, 0xBD, + 0x00, 0x20, 0x10, 0xBD, 0x10, 0xB5, 0x00, 0x88, 0x00, 0x24, 0xC1, 0x07, + 0x00, 0xD0, 0x01, 0x24, + 0x81, 0x07, 0x01, 0xD5, 0x44, 0xF0, 0x02, 0x04, 0x41, 0x07, 0x01, 0xD5, + 0x44, 0xF0, 0x04, 0x04, + 0x01, 0x07, 0x01, 0xD5, 0x44, 0xF0, 0x08, 0x04, 0xC0, 0x06, 0x01, 0xD5, + 0x44, 0xF0, 0x10, 0x04, + 0x74, 0xB1, 0xEE, 0xF7, 0x85, 0xFB, 0x51, 0x48, 0x01, 0x68, 0x21, 0x43, + 0x01, 0x60, 0xEE, 0xF7, + 0x7D, 0xFB, 0x4F, 0xF4, 0x00, 0x11, 0x00, 0x20, 0xFF, 0xF7, 0xD0, 0xFD, + 0x01, 0x20, 0x10, 0xBD, + 0x00, 0x20, 0x10, 0xBD, 0x10, 0xB5, 0x00, 0x88, 0x00, 0x24, 0xC1, 0x07, + 0x00, 0xD0, 0x01, 0x24, + 0x81, 0x07, 0x01, 0xD5, 0x44, 0xF0, 0x02, 0x04, 0x41, 0x07, 0x01, 0xD5, + 0x44, 0xF0, 0x04, 0x04, + 0x01, 0x07, 0x01, 0xD5, 0x44, 0xF0, 0x08, 0x04, 0xC1, 0x06, 0x01, 0xD5, + 0x44, 0xF0, 0x10, 0x04, + 0x81, 0x06, 0x01, 0xD5, 0x44, 0xF0, 0x20, 0x04, 0x41, 0x06, 0x01, 0xD5, + 0x44, 0xF0, 0x40, 0x04, + 0x01, 0x06, 0x01, 0xD5, 0x44, 0xF0, 0x80, 0x04, 0xC1, 0x05, 0x01, 0xD5, + 0x44, 0xF4, 0x80, 0x74, + 0x81, 0x05, 0x01, 0xD5, 0x44, 0xF4, 0x00, 0x74, 0x40, 0x05, 0x01, 0xD5, + 0x44, 0xF4, 0x80, 0x64, + 0x6C, 0xB1, 0xEE, 0xF7, 0x45, 0xFB, 0x32, 0x48, 0x01, 0x68, 0x21, 0x43, + 0x01, 0x60, 0xEE, 0xF7, + 0x3D, 0xFB, 0x80, 0x21, 0x00, 0x20, 0xFF, 0xF7, 0x91, 0xFD, 0x01, 0x20, + 0x10, 0xBD, 0x00, 0x20, + 0x10, 0xBD, 0x10, 0xB5, 0x00, 0x78, 0x00, 0x24, 0xC1, 0x07, 0x00, 0xD0, + 0x01, 0x24, 0x81, 0x07, + 0x01, 0xD5, 0x44, 0xF0, 0x02, 0x04, 0x40, 0x07, 0x01, 0xD5, 0x44, 0xF0, + 0x04, 0x04, 0x74, 0xB1, + 0xEE, 0xF7, 0x26, 0xFB, 0x23, 0x48, 0x01, 0x68, 0x21, 0x43, 0x01, 0x60, + 0xEE, 0xF7, 0x1E, 0xFB, + 0x4F, 0xF4, 0x80, 0x61, 0x00, 0x20, 0xFF, 0xF7, 0x71, 0xFD, 0x01, 0x20, + 0x10, 0xBD, 0x00, 0x20, + 0x10, 0xBD, 0x10, 0xB5, 0x00, 0x78, 0x00, 0x24, 0x31, 0x28, 0x39, 0xD0, + 0x10, 0xDC, 0x12, 0x28, + 0x32, 0xD0, 0x06, 0xDC, 0x01, 0x28, 0x1C, 0xD0, 0x10, 0x28, 0x2D, 0xD0, + 0x11, 0x28, 0x31, 0xD1, + 0x2A, 0xE0, 0x13, 0x28, 0x28, 0xD0, 0x14, 0x28, 0x26, 0xD0, 0x30, 0x28, + 0x2A, 0xD1, 0x27, 0xE0, + 0x51, 0x28, 0x29, 0xD0, 0x06, 0xDC, 0x32, 0x28, 0x22, 0xD0, 0x33, 0x28, + 0x20, 0xD0, 0x50, 0x28, + 0x20, 0xD1, 0x21, 0xE0, 0x52, 0x28, 0x1F, 0xD0, 0x53, 0x28, 0x1D, 0xD0, + 0x54, 0x28, 0x19, 0xD1, + 0x1A, 0xE0, 0x00, 0xF0, 0x5B, 0xFC, 0x11, 0xE0, 0xC8, 0x7A, 0x01, 0x00, + 0x6C, 0x07, 0x10, 0x00, + 0x70, 0x07, 0x10, 0x00, 0x54, 0x07, 0x10, 0x00, 0x58, 0x07, 0x10, 0x00, + 0x5C, 0x07, 0x10, 0x00, + 0x64, 0x07, 0x10, 0x00, 0x60, 0x07, 0x10, 0x00, 0x00, 0xF0, 0xDC, 0xFC, + 0x01, 0x24, 0x01, 0xE0, + 0x00, 0xF0, 0xF8, 0xFC, 0x20, 0x46, 0x10, 0xBD, 0x00, 0xF0, 0xE2, 0xFC, + 0xF6, 0xE7, 0x00, 0x00, + 0x08, 0xB5, 0x00, 0xF0, 0x3B, 0xFC, 0x00, 0xF0, 0xE3, 0xFD, 0x17, 0x4C, + 0x6B, 0x46, 0x00, 0x22, + 0x19, 0x21, 0x03, 0x20, 0xEE, 0xF7, 0x4E, 0xFC, 0x00, 0x98, 0xC0, 0x07, + 0x05, 0xD0, 0x01, 0x21, + 0x03, 0x20, 0xEE, 0xF7, 0x2A, 0xFC, 0xFF, 0xF7, 0xE3, 0xFC, 0x00, 0x98, + 0x00, 0x07, 0x0B, 0xD5, + 0x08, 0x21, 0x03, 0x20, 0xEE, 0xF7, 0x21, 0xFC, 0x20, 0x68, 0x08, 0x21, + 0x40, 0xF0, 0x08, 0x00, + 0x20, 0x60, 0x01, 0x20, 0xFF, 0xF7, 0x02, 0xFD, 0x00, 0x98, 0xC0, 0x06, + 0xDE, 0xD5, 0x10, 0x21, + 0x03, 0x20, 0xEE, 0xF7, 0x12, 0xFC, 0x20, 0x68, 0x08, 0x21, 0x20, 0xF0, + 0x08, 0x00, 0x20, 0x60, + 0x01, 0x20, 0xFF, 0xF7, 0xF3, 0xFC, 0xD1, 0xE7, 0x70, 0x07, 0x10, 0x00, + 0x1F, 0x48, 0x00, 0x21, + 0x01, 0x70, 0x41, 0x70, 0x81, 0x70, 0x70, 0x47, 0x10, 0xB5, 0x1C, 0x4C, + 0x03, 0x46, 0xA0, 0x78, + 0x10, 0x28, 0x14, 0xD2, 0x0F, 0x29, 0x00, 0xD9, 0x0F, 0x21, 0x19, 0x48, + 0x22, 0x78, 0x00, 0xEB, + 0x02, 0x10, 0x0A, 0x46, 0x00, 0xF8, 0x01, 0x1B, 0x19, 0x46, 0x05, 0xF0, + 0xB6, 0xF9, 0x20, 0x78, + 0x40, 0x1C, 0x00, 0xF0, 0x0F, 0x00, 0x20, 0x70, 0xA0, 0x78, 0x40, 0x1C, + 0xA0, 0x70, 0x10, 0xBD, + 0x70, 0xB5, 0x0E, 0x4C, 0x00, 0x25, 0xA1, 0x78, 0x91, 0xB1, 0x0D, 0x49, + 0x62, 0x78, 0x01, 0xEB, + 0x02, 0x11, 0x11, 0xF8, 0x01, 0x5B, 0x2A, 0x46, 0x05, 0xF0, 0x9F, 0xF9, + 0x60, 0x78, 0x40, 0x1C, + 0x00, 0xF0, 0x0F, 0x00, 0x60, 0x70, 0x72, 0xB6, 0xA0, 0x78, 0x40, 0x1E, + 0xA0, 0x70, 0x62, 0xB6, + 0x28, 0x46, 0x70, 0xBD, 0x01, 0x48, 0x80, 0x78, 0x70, 0x47, 0x00, 0x00, + 0x34, 0x07, 0x10, 0x00, + 0xF0, 0x4E, 0x10, 0x00, 0x70, 0xB5, 0x14, 0x46, 0x00, 0x20, 0x05, 0xF0, + 0xAB, 0xFA, 0x00, 0xF0, + 0x01, 0x05, 0x01, 0x20, 0x05, 0xF0, 0xA6, 0xFA, 0x00, 0xF0, 0x01, 0x00, + 0x45, 0xEA, 0x00, 0x10, + 0x20, 0x70, 0x01, 0x20, 0x70, 0xBD, 0x80, 0x20, 0x10, 0x70, 0x01, 0x20, + 0x70, 0x47, 0x10, 0xB5, + 0x14, 0x46, 0xEF, 0xF7, 0x64, 0xF8, 0x53, 0x21, 0x21, 0x70, 0x54, 0x21, + 0x61, 0x70, 0x01, 0x0A, + 0xA1, 0x70, 0xE0, 0x70, 0xEF, 0xF7, 0x5F, 0xF8, 0x20, 0x71, 0x05, 0x20, + 0x10, 0xBD, 0x38, 0xB5, + 0x14, 0x46, 0x70, 0x4A, 0x13, 0x68, 0x59, 0x8E, 0x98, 0x8E, 0x93, 0xF8, + 0x36, 0x30, 0xDB, 0x07, + 0x0A, 0xD0, 0x03, 0x0A, 0x23, 0x70, 0x60, 0x70, 0x0D, 0x0A, 0xA5, 0x70, + 0xE1, 0x70, 0x23, 0x71, + 0x60, 0x71, 0xA5, 0x71, 0xE1, 0x71, 0x09, 0xE0, 0x0B, 0x0A, 0x23, 0x70, + 0x61, 0x70, 0x05, 0x0A, + 0xA5, 0x70, 0xE0, 0x70, 0x23, 0x71, 0x61, 0x71, 0xA5, 0x71, 0xE0, 0x71, + 0x10, 0x68, 0x90, 0xF8, + 0x31, 0x10, 0x21, 0x72, 0x10, 0xF8, 0x30, 0x1F, 0x61, 0x72, 0x90, 0xF8, + 0x31, 0x04, 0x8D, 0xF8, + 0x00, 0x00, 0x01, 0x21, 0x68, 0x46, 0xEF, 0xF7, 0x8E, 0xF9, 0xA0, 0x72, + 0x0B, 0x20, 0x38, 0xBD, + 0xA2, 0x20, 0x10, 0x70, 0x33, 0x20, 0x50, 0x70, 0x00, 0x20, 0x90, 0x70, + 0xD0, 0x70, 0x10, 0x71, + 0x50, 0x71, 0x06, 0x20, 0x70, 0x47, 0x08, 0xB5, 0x02, 0x23, 0x00, 0x93, + 0x52, 0x4B, 0x00, 0xF0, + 0x8D, 0xF8, 0x02, 0x20, 0x08, 0xBD, 0x08, 0xB5, 0x01, 0x23, 0x00, 0x93, + 0x4E, 0x4B, 0x9B, 0x1C, + 0x00, 0xF0, 0x84, 0xF8, 0x01, 0x20, 0x08, 0xBD, 0x08, 0xB5, 0x01, 0x23, + 0x00, 0x93, 0x4A, 0x4B, + 0xDB, 0x1C, 0x00, 0xF0, 0x7B, 0xF8, 0x01, 0x20, 0x08, 0xBD, 0x08, 0xB5, + 0x01, 0x23, 0x00, 0x93, + 0x45, 0x4B, 0x1B, 0x1D, 0x00, 0xF0, 0x72, 0xF8, 0x01, 0x20, 0x08, 0xBD, + 0x08, 0xB5, 0x01, 0x23, + 0x00, 0x93, 0x41, 0x4B, 0x5B, 0x1D, 0x00, 0xF0, 0x69, 0xF8, 0x01, 0x20, + 0x08, 0xBD, 0x08, 0xB5, + 0x01, 0x23, 0x00, 0x93, 0x3C, 0x4B, 0x9B, 0x1D, 0x00, 0xF0, 0x60, 0xF8, + 0x01, 0x20, 0x08, 0xBD, + 0x08, 0xB5, 0x02, 0x23, 0x00, 0x93, 0x38, 0x4B, 0xDB, 0x1D, 0x00, 0xF0, + 0x57, 0xF8, 0x02, 0x20, + 0x08, 0xBD, 0x08, 0xB5, 0x02, 0x23, 0x00, 0x93, 0x33, 0x4B, 0x09, 0x33, + 0x00, 0xF0, 0x4E, 0xF8, + 0x02, 0x20, 0x08, 0xBD, 0x08, 0xB5, 0x01, 0x23, 0x00, 0x93, 0x2F, 0x4B, + 0x0B, 0x33, 0x00, 0xF0, + 0x45, 0xF8, 0x01, 0x20, 0x08, 0xBD, 0x08, 0xB5, 0x01, 0x23, 0x00, 0x93, + 0x2A, 0x4B, 0x0C, 0x33, + 0x00, 0xF0, 0x3C, 0xF8, 0x01, 0x20, 0x08, 0xBD, 0x08, 0xB5, 0x01, 0x23, + 0x00, 0x93, 0x26, 0x4B, + 0x0D, 0x33, 0x00, 0xF0, 0x33, 0xF8, 0x01, 0x20, 0x08, 0xBD, 0x2D, 0xE9, + 0xF0, 0x41, 0x0F, 0x46, + 0x04, 0x46, 0x00, 0x78, 0x49, 0x1E, 0xCE, 0xB2, 0x00, 0x25, 0x63, 0x1C, + 0xA0, 0xF1, 0x20, 0x01, + 0x04, 0x29, 0x09, 0xD8, 0x1D, 0x49, 0x01, 0xEB, 0x80, 0x00, 0x31, 0x46, + 0x50, 0xF8, 0x80, 0x4C, + 0x18, 0x46, 0xA0, 0x47, 0x05, 0x46, 0x16, 0xE0, 0xA0, 0xF1, 0x30, 0x01, + 0x0B, 0x29, 0x12, 0xD8, + 0x16, 0x49, 0x14, 0x31, 0x01, 0xEB, 0x80, 0x00, 0x31, 0x46, 0x50, 0xF8, + 0xC0, 0x5C, 0x18, 0x46, + 0xA8, 0x47, 0x05, 0x46, 0x3E, 0xB1, 0x39, 0x46, 0x20, 0x46, 0xFF, 0xF7, + 0xD5, 0xFE, 0x01, 0x21, + 0x03, 0x20, 0xEE, 0xF7, 0xA6, 0xFA, 0x28, 0x46, 0xBD, 0xE8, 0xF0, 0x81, + 0x70, 0xB5, 0x1D, 0x46, + 0x04, 0x9C, 0x16, 0x46, 0x39, 0xB1, 0xA1, 0x42, 0x00, 0xD9, 0x21, 0x46, + 0x0A, 0x46, 0x01, 0x46, + 0x18, 0x46, 0x05, 0xF0, 0x8A, 0xF8, 0x22, 0x46, 0x29, 0x46, 0x30, 0x46, + 0xBD, 0xE8, 0x70, 0x40, + 0x05, 0xF0, 0x83, 0xB8, 0x4C, 0x07, 0x10, 0x00, 0xF0, 0x4F, 0x10, 0x00, + 0x90, 0x7B, 0x01, 0x00, + 0x10, 0xB5, 0x4F, 0xF4, 0x86, 0x71, 0x46, 0x48, 0x05, 0xF0, 0x18, 0xF9, + 0x44, 0x49, 0x01, 0x20, + 0xC1, 0xF8, 0x04, 0x01, 0x43, 0x49, 0x00, 0x20, 0x08, 0x70, 0x05, 0xF0, + 0xA1, 0xF9, 0x00, 0x21, + 0xBD, 0xE8, 0x10, 0x40, 0x01, 0x20, 0xEF, 0xF7, 0x20, 0xB9, 0x10, 0xB5, + 0x72, 0xB6, 0x00, 0x20, + 0x3C, 0x49, 0x08, 0x70, 0x05, 0xF0, 0x94, 0xF9, 0x62, 0xB6, 0x10, 0xBD, + 0x10, 0xB5, 0x72, 0xB6, + 0x01, 0x20, 0x38, 0x49, 0x08, 0x70, 0x36, 0x48, 0xD0, 0xF8, 0x08, 0x01, + 0x08, 0xB1, 0x05, 0xF0, + 0x2B, 0xF9, 0x62, 0xB6, 0x10, 0xBD, 0x10, 0xB5, 0x72, 0xB6, 0x31, 0x49, + 0xD1, 0xF8, 0x08, 0x31, + 0xD1, 0xF8, 0x00, 0x21, 0x20, 0x2B, 0x07, 0xD2, 0x5B, 0x1C, 0x52, 0x1C, + 0x22, 0xF0, 0x20, 0x02, + 0xC1, 0xF8, 0x08, 0x31, 0xC1, 0xF8, 0x00, 0x21, 0x01, 0xEB, 0xC2, 0x02, + 0x03, 0x68, 0x13, 0x60, + 0x40, 0x68, 0x50, 0x60, 0x27, 0x48, 0x00, 0x78, 0x01, 0x28, 0x05, 0xD1, + 0xD1, 0xF8, 0x08, 0x01, + 0x01, 0x28, 0x01, 0xD1, 0x05, 0xF0, 0x08, 0xF9, 0x62, 0xB6, 0x10, 0xBD, + 0x20, 0x4A, 0xD2, 0xF8, + 0x08, 0x01, 0x00, 0x28, 0x0E, 0xD0, 0xD2, 0xF8, 0x04, 0x01, 0x02, 0xEB, + 0xC0, 0x00, 0xC3, 0x79, + 0x23, 0xF0, 0x3F, 0x03, 0xC3, 0x71, 0x92, 0xF8, 0x08, 0x21, 0x52, 0x1E, + 0x02, 0xF0, 0x3F, 0x02, + 0x13, 0x43, 0xC3, 0x71, 0x70, 0x47, 0x10, 0xB5, 0x72, 0xB6, 0x15, 0x48, + 0xD0, 0xF8, 0x08, 0x11, + 0x59, 0xB1, 0x49, 0x1E, 0xC0, 0xF8, 0x08, 0x11, 0xD0, 0xF8, 0x04, 0x21, + 0x02, 0xF1, 0x01, 0x02, + 0x22, 0xF0, 0x20, 0x02, 0xC0, 0xF8, 0x04, 0x21, 0x05, 0xD1, 0x0E, 0x48, + 0x00, 0x78, 0x01, 0x28, + 0x01, 0xD1, 0x05, 0xF0, 0x35, 0xF9, 0x62, 0xB6, 0x10, 0xBD, 0x10, 0xB5, + 0x72, 0xB6, 0x00, 0x21, + 0x07, 0x48, 0xC0, 0xF8, 0x08, 0x11, 0xC0, 0xF8, 0x00, 0x11, 0x01, 0x21, + 0xC0, 0xF8, 0x04, 0x11, + 0x04, 0x48, 0x00, 0x78, 0x01, 0x28, 0x01, 0xD1, 0x05, 0xF0, 0x22, 0xF9, + 0x62, 0xB6, 0x10, 0xBD, + 0x68, 0x42, 0x01, 0x20, 0x37, 0x07, 0x10, 0x00, 0x18, 0x49, 0x00, 0x22, + 0x0A, 0x80, 0x4A, 0x80, + 0x4F, 0xF4, 0x70, 0x42, 0x8A, 0x80, 0x16, 0x4A, 0xCA, 0x80, 0x16, 0x4A, + 0x0A, 0x81, 0x16, 0x4A, + 0xC0, 0xE9, 0x00, 0x21, 0x05, 0x21, 0x01, 0x72, 0x70, 0x47, 0x1C, 0xB5, + 0x12, 0x49, 0xAD, 0xF8, + 0x04, 0x00, 0x02, 0x20, 0x8D, 0xF8, 0x06, 0x00, 0x04, 0x20, 0x09, 0x1F, + 0x8D, 0xF8, 0x07, 0x00, + 0x00, 0x91, 0x68, 0x46, 0xEF, 0xF7, 0x4B, 0xF9, 0x1C, 0xBD, 0x30, 0xB4, + 0x03, 0x46, 0x0A, 0x4A, + 0x00, 0x20, 0x14, 0x78, 0x9C, 0x42, 0x02, 0xD1, 0x30, 0xBC, 0xEF, 0xF7, + 0x76, 0xB9, 0x40, 0x1C, + 0xC0, 0xB2, 0x52, 0x1D, 0x05, 0x28, 0xF4, 0xD3, 0x30, 0xBC, 0x70, 0x47, + 0xFE, 0x4F, 0x10, 0x00, + 0x14, 0xA6, 0x01, 0x20, 0x6C, 0xA2, 0x01, 0x20, 0xD4, 0x7B, 0x01, 0x00, + 0x10, 0xB5, 0x88, 0xB3, + 0xA3, 0x49, 0xA4, 0x4A, 0x09, 0x78, 0x37, 0x29, 0x71, 0xD0, 0x11, 0xDC, + 0xA1, 0xF1, 0x20, 0x01, + 0x17, 0x29, 0x2C, 0xD2, 0xDF, 0xE8, 0x01, 0xF0, 0x6B, 0x6B, 0x6B, 0x6B, + 0x6B, 0x2B, 0x2B, 0x2B, + 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x2B, 0x6B, 0x6B, 0x6B, 0x6B, + 0x6B, 0x6B, 0x6B, 0x00, + 0x97, 0x4B, 0x70, 0x29, 0x5B, 0x78, 0x26, 0xD0, 0x0C, 0xDC, 0x3B, 0x29, + 0x57, 0xD0, 0x04, 0xDC, + 0x38, 0x29, 0x54, 0xD0, 0x3A, 0x29, 0x12, 0xD1, 0x51, 0xE0, 0x60, 0x29, + 0x19, 0xD0, 0x61, 0x29, + 0x0D, 0xD1, 0x16, 0xE0, 0x73, 0x29, 0x28, 0xD0, 0x05, 0xDC, 0x71, 0x29, + 0x15, 0xD0, 0x72, 0x29, + 0x05, 0xD1, 0x14, 0xE0, 0x0B, 0xE0, 0x85, 0x29, 0x0B, 0xD0, 0x86, 0x29, + 0x09, 0xD0, 0xC1, 0xB2, + 0x87, 0x48, 0xFF, 0xF7, 0xA9, 0xFD, 0x01, 0x21, 0x03, 0x20, 0xEE, 0xF7, + 0x7A, 0xF9, 0x00, 0x20, + 0x10, 0xBD, 0x02, 0x20, 0x10, 0xBD, 0x93, 0x70, 0xF9, 0xE7, 0xD3, 0x70, + 0xF7, 0xE7, 0x90, 0x78, + 0x01, 0x28, 0x08, 0xD0, 0x00, 0x28, 0xF2, 0xD1, 0x7F, 0x48, 0x81, 0xB2, + 0x5F, 0xF0, 0x72, 0x00, + 0xFF, 0xF7, 0x93, 0xFF, 0xEB, 0xE7, 0x7D, 0x48, 0xF7, 0xE7, 0xD0, 0x78, + 0x01, 0x28, 0x05, 0xD0, + 0x00, 0x28, 0xE4, 0xD1, 0x7A, 0x48, 0x81, 0xB2, 0x73, 0x20, 0xF1, 0xE7, + 0x79, 0x4C, 0x7A, 0x49, + 0x20, 0x68, 0x90, 0xF8, 0x31, 0x00, 0x42, 0x00, 0x71, 0x48, 0x10, 0x30, + 0x04, 0xF0, 0x45, 0xFF, + 0x20, 0x68, 0x10, 0xF8, 0x30, 0x1F, 0x4A, 0x00, 0x6D, 0x49, 0x40, 0x78, + 0x10, 0x31, 0x01, 0xEB, + 0x40, 0x00, 0x72, 0x49, 0x04, 0xF0, 0x39, 0xFF, 0x69, 0x48, 0x10, 0x30, + 0xE3, 0xE7, 0xC1, 0xB2, + 0x52, 0x68, 0x67, 0x48, 0xFF, 0xF7, 0x69, 0xFE, 0x6D, 0x49, 0x08, 0x70, + 0x01, 0x20, 0x10, 0xBD, + 0x2D, 0xE9, 0xF0, 0x5F, 0xDF, 0xF8, 0x88, 0xB1, 0x62, 0x4E, 0x62, 0x4D, + 0x89, 0x46, 0x07, 0x46, + 0x4F, 0xF0, 0x01, 0x0A, 0xAB, 0xF1, 0x10, 0x0B, 0x08, 0x36, 0x28, 0x78, + 0xA8, 0x46, 0x04, 0x46, + 0x07, 0x28, 0x76, 0xD2, 0xDF, 0xE8, 0x00, 0xF0, 0x04, 0x21, 0x25, 0x40, + 0x5C, 0x78, 0x8A, 0x00, + 0x01, 0x2F, 0x02, 0xD0, 0x02, 0x2F, 0x0C, 0xD0, 0x6B, 0xE0, 0x48, 0x46, + 0xFF, 0xF7, 0x5E, 0xFF, + 0x02, 0x28, 0x02, 0xD0, 0x01, 0x28, 0x02, 0xD0, 0x63, 0xE0, 0x03, 0x24, + 0x61, 0xE0, 0x02, 0x24, + 0x5F, 0xE0, 0x68, 0x78, 0x01, 0x28, 0x04, 0xD0, 0x4D, 0x48, 0x10, 0x21, + 0x10, 0x38, 0x04, 0xF0, + 0x7B, 0xFF, 0x54, 0x46, 0x86, 0xF8, 0x00, 0xA0, 0x53, 0xE0, 0x02, 0x2F, + 0x4F, 0xD0, 0x00, 0x24, + 0x39, 0xE0, 0x02, 0x2F, 0x1A, 0xD1, 0x4E, 0x4C, 0x45, 0x48, 0xD8, 0xF8, + 0x04, 0x10, 0x22, 0x78, + 0x10, 0x38, 0x04, 0xF0, 0xEA, 0xFE, 0x03, 0x20, 0x30, 0x70, 0x20, 0x78, + 0xB0, 0x80, 0x98, 0xF8, + 0x00, 0x00, 0x04, 0x24, 0x88, 0xF8, 0x01, 0x00, 0x88, 0xF8, 0x00, 0x40, + 0x27, 0xE0, 0x00, 0x24, + 0x88, 0xF8, 0x01, 0x00, 0x2C, 0x70, 0xB8, 0xE7, 0x02, 0x2F, 0x02, 0xD0, + 0x01, 0x2F, 0xF6, 0xD0, + 0x2F, 0xE0, 0xFF, 0xF7, 0xA3, 0xFE, 0x08, 0x21, 0x28, 0xB1, 0x02, 0x22, + 0x32, 0x70, 0x70, 0x80, + 0xB1, 0x80, 0x05, 0x24, 0x25, 0xE0, 0x68, 0x78, 0x06, 0x28, 0x04, 0xD0, + 0x00, 0x22, 0xCB, 0xF8, + 0x00, 0x20, 0xCB, 0xF8, 0x04, 0x20, 0x03, 0x20, 0x30, 0x70, 0xB1, 0x80, + 0x06, 0x24, 0x18, 0xE0, + 0x02, 0x2F, 0x08, 0xD0, 0x02, 0x24, 0xC0, 0xB2, 0x88, 0xF8, 0x01, 0x00, + 0x2C, 0x70, 0x29, 0x48, + 0x08, 0x30, 0xBD, 0xE8, 0xF0, 0x9F, 0x68, 0x78, 0x04, 0x28, 0x08, 0xD0, + 0x0B, 0xEB, 0x09, 0x00, + 0x49, 0x46, 0x10, 0xF8, 0x01, 0x2C, 0x22, 0x48, 0x10, 0x38, 0x04, 0xF0, + 0x1D, 0xFF, 0x86, 0xF8, + 0x00, 0xA0, 0xA8, 0x46, 0x28, 0x78, 0xE7, 0xE7, 0x02, 0x2F, 0x05, 0xD0, + 0xB9, 0xF1, 0x08, 0x0F, + 0x93, 0xD1, 0xFF, 0xF7, 0x80, 0xFE, 0x90, 0xE7, 0xFF, 0xF7, 0x7D, 0xFE, + 0x98, 0xF8, 0x00, 0x00, + 0x03, 0x24, 0x88, 0xF8, 0x01, 0x00, 0x88, 0xF8, 0x00, 0x40, 0x6E, 0xE7, + 0x02, 0x2F, 0x4F, 0xF0, + 0x03, 0x04, 0xAD, 0xD0, 0xCF, 0xE7, 0x13, 0x49, 0x00, 0x22, 0x0A, 0x70, + 0x4A, 0x70, 0x18, 0x4A, + 0x52, 0x1C, 0x4A, 0x60, 0x17, 0x49, 0x01, 0x60, 0x0D, 0x49, 0x10, 0x39, + 0x81, 0x80, 0x10, 0x31, + 0xC1, 0x80, 0x10, 0x21, 0x01, 0x81, 0x0E, 0x21, 0x13, 0x48, 0x04, 0xF0, + 0xF5, 0xBE, 0x10, 0xB5, + 0x04, 0x46, 0x04, 0xF0, 0xA5, 0xFF, 0x06, 0x48, 0x21, 0x68, 0x10, 0x38, + 0x01, 0x60, 0x61, 0x68, + 0x41, 0x60, 0x0E, 0x48, 0xFF, 0xF7, 0x91, 0xFE, 0xBD, 0xE8, 0x10, 0x40, + 0x04, 0xF0, 0x3C, 0xBF, + 0x84, 0x43, 0x01, 0x20, 0x38, 0x07, 0x10, 0x00, 0x14, 0xA6, 0x01, 0x20, + 0xEE, 0x35, 0x01, 0x20, + 0x6C, 0xA2, 0x01, 0x20, 0x4C, 0x07, 0x10, 0x00, 0x94, 0x0B, 0x01, 0x20, + 0xBE, 0x0B, 0x01, 0x20, + 0x08, 0x50, 0x10, 0x00, 0xB1, 0x22, 0x01, 0x00, 0xF0, 0x4F, 0x10, 0x00, + 0x74, 0x43, 0x00, 0x00, + 0x70, 0xB5, 0x04, 0x4D, 0x38, 0x21, 0x28, 0x46, 0x2C, 0x88, 0x04, 0xF0, + 0xC5, 0xFE, 0x64, 0x1C, + 0x2C, 0x80, 0x70, 0xBD, 0x0A, 0x44, 0x01, 0x20, 0x37, 0x48, 0x00, 0x21, + 0x01, 0x60, 0x41, 0x60, + 0x70, 0x47, 0xC0, 0xB2, 0x01, 0x22, 0x00, 0xF0, 0x48, 0xFA, 0xFE, 0xE7, + 0x34, 0x4C, 0x00, 0x25, + 0x32, 0x4F, 0xC4, 0xE9, 0x02, 0x25, 0xC4, 0xE9, 0x00, 0x71, 0x06, 0x46, + 0x00, 0x68, 0x20, 0x61, + 0x70, 0x68, 0x60, 0x61, 0xB0, 0x68, 0xA0, 0x61, 0xF0, 0x68, 0xE0, 0x61, + 0x30, 0x69, 0x20, 0x62, + 0x70, 0x69, 0x60, 0x62, 0xB0, 0x69, 0xA0, 0x62, 0xF0, 0x69, 0xE0, 0x62, + 0x29, 0x48, 0x90, 0x46, + 0x89, 0x46, 0x00, 0x68, 0x20, 0x63, 0x27, 0x48, 0x10, 0x38, 0x00, 0x68, + 0x60, 0x63, 0x25, 0x48, + 0x0C, 0x38, 0x00, 0x68, 0xA0, 0x63, 0x23, 0x48, 0x08, 0x38, 0x00, 0x68, + 0xE0, 0x63, 0x21, 0x48, + 0x00, 0x1D, 0x00, 0x68, 0x20, 0x64, 0x1F, 0x48, 0x34, 0x38, 0x00, 0x68, + 0x60, 0x64, 0x1D, 0x48, + 0x14, 0x38, 0x00, 0x68, 0xC4, 0xE9, 0x12, 0x05, 0xEE, 0xF7, 0x9B, 0xF9, + 0x20, 0x65, 0xC4, 0xE9, + 0x15, 0x65, 0xE5, 0x65, 0x25, 0x66, 0x65, 0x66, 0xA5, 0x66, 0xE5, 0x66, + 0x25, 0x67, 0x65, 0x67, + 0xC4, 0xE9, 0x1E, 0x57, 0x41, 0x46, 0x48, 0x46, 0xFF, 0xF7, 0xB3, 0xFF, + 0x10, 0xB5, 0x04, 0x46, + 0xC0, 0xB2, 0x00, 0x22, 0x00, 0xF0, 0xF9, 0xF9, 0x0F, 0x49, 0x08, 0x68, + 0x08, 0x28, 0x03, 0xDA, + 0x09, 0x4A, 0x14, 0x54, 0x40, 0x1C, 0x08, 0x60, 0x10, 0xBD, 0x01, 0x28, + 0x06, 0xD0, 0x02, 0x28, + 0x08, 0xD1, 0x00, 0x22, 0x11, 0x46, 0x13, 0x20, 0x00, 0xF0, 0xE7, 0xB9, + 0x00, 0x22, 0x11, 0x46, + 0x12, 0x20, 0xF9, 0xE7, 0x70, 0x47, 0x00, 0x00, 0x70, 0xEF, 0x01, 0x20, + 0xAF, 0x05, 0x50, 0xFA, + 0x80, 0xEF, 0x01, 0x20, 0x38, 0xED, 0x00, 0xE0, 0x48, 0x07, 0x10, 0x00, + 0x2D, 0xE9, 0xF0, 0x41, + 0x40, 0xF2, 0x02, 0x71, 0xB9, 0x48, 0x04, 0xF0, 0x3F, 0xFE, 0xB8, 0x4C, + 0x01, 0x26, 0x00, 0x25, + 0x66, 0x70, 0xA5, 0x80, 0xA5, 0x71, 0x02, 0x20, 0xE0, 0x71, 0xEE, 0xF7, + 0xAC, 0xFC, 0x20, 0x81, + 0xEE, 0xF7, 0xA5, 0xFC, 0x07, 0x02, 0xEE, 0xF7, 0xA2, 0xFC, 0x47, 0xEA, + 0x10, 0x20, 0x60, 0x81, + 0x4A, 0xF2, 0x33, 0x20, 0x20, 0x82, 0x30, 0x02, 0x60, 0x82, 0xAD, 0x48, + 0x00, 0x68, 0x41, 0x8A, + 0xA1, 0x82, 0x01, 0x8A, 0xE1, 0x82, 0xC1, 0x8A, 0x21, 0x83, 0x81, 0x8A, + 0x61, 0x83, 0x90, 0xF8, + 0xDE, 0x10, 0x21, 0x77, 0xA7, 0x49, 0x09, 0x68, 0x09, 0x7E, 0x61, 0x77, + 0x90, 0xF8, 0xA0, 0x10, + 0x01, 0xF0, 0x01, 0x01, 0xA1, 0x77, 0x81, 0x69, 0x21, 0x63, 0xC1, 0x69, + 0x61, 0x63, 0xA5, 0x63, + 0xE5, 0x63, 0xA4, 0xF8, 0x40, 0x50, 0x84, 0xF8, 0x42, 0x50, 0x84, 0xF8, + 0x43, 0x60, 0x41, 0x8E, + 0xA4, 0xF8, 0x50, 0x10, 0x81, 0x8E, 0xA4, 0xF8, 0x52, 0x10, 0x90, 0xF8, + 0x31, 0x10, 0x84, 0xF8, + 0x54, 0x10, 0x90, 0xF8, 0x30, 0x10, 0x84, 0xF8, 0x55, 0x10, 0x01, 0x21, + 0x00, 0xF2, 0x61, 0x40, + 0xEE, 0xF7, 0xC9, 0xFD, 0x84, 0xF8, 0x56, 0x00, 0x93, 0x48, 0xA4, 0xF8, + 0x80, 0x00, 0x93, 0x48, + 0xA4, 0xF8, 0x88, 0x00, 0x92, 0x48, 0xA4, 0xF8, 0x8A, 0x00, 0x92, 0x48, + 0xA4, 0xF8, 0x8C, 0x00, + 0x91, 0x48, 0xA4, 0xF8, 0x8E, 0x00, 0x91, 0x48, 0xA4, 0xF8, 0x90, 0x00, + 0x90, 0x48, 0xA4, 0xF8, + 0x92, 0x00, 0x90, 0x48, 0xA4, 0xF8, 0x94, 0x00, 0x8F, 0x48, 0xA4, 0xF8, + 0x96, 0x00, 0x8F, 0x48, + 0xA4, 0xF8, 0x98, 0x00, 0x8E, 0x48, 0xA4, 0xF8, 0x9A, 0x00, 0x8E, 0x48, + 0xA4, 0xF8, 0x9C, 0x00, + 0x8D, 0x48, 0xA4, 0xF8, 0x9E, 0x00, 0x8D, 0x48, 0xA4, 0xF8, 0xA0, 0x00, + 0x8C, 0x48, 0xA4, 0xF8, + 0xA2, 0x00, 0x8C, 0x48, 0xA4, 0xF8, 0xA4, 0x00, 0x8C, 0x49, 0x8B, 0x48, + 0xA4, 0xF8, 0xA6, 0x00, + 0x48, 0x88, 0x40, 0x1C, 0x48, 0x80, 0xA5, 0x22, 0x22, 0x70, 0x66, 0x70, + 0x60, 0x80, 0x0E, 0x70, + 0xBD, 0xE8, 0xF0, 0x81, 0x70, 0xB5, 0x85, 0x4C, 0x05, 0x46, 0x00, 0x23, + 0x60, 0x88, 0x6F, 0x49, + 0x40, 0x1C, 0x82, 0xB2, 0x62, 0x80, 0x28, 0x46, 0x01, 0xF0, 0x80, 0xFA, + 0x25, 0x70, 0x70, 0xBD, + 0x70, 0xB5, 0x05, 0x46, 0x40, 0xF2, 0x02, 0x71, 0x68, 0x48, 0x04, 0xF0, + 0x9D, 0xFD, 0x7B, 0x4C, + 0x66, 0x49, 0x60, 0x88, 0x40, 0x1C, 0x82, 0xB2, 0x62, 0x80, 0x28, 0x46, + 0x01, 0xF0, 0xA6, 0xFA, + 0x25, 0x70, 0x70, 0xBD, 0x2D, 0xE9, 0xF0, 0x41, 0x74, 0x4C, 0x05, 0x46, + 0x20, 0x78, 0xA8, 0x42, + 0x04, 0xD0, 0x40, 0xF2, 0x02, 0x71, 0x5D, 0x48, 0x04, 0xF0, 0x86, 0xFD, + 0x70, 0x49, 0x65, 0x70, + 0x0F, 0x20, 0x08, 0x60, 0x5A, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x61, 0x24, + 0x0A, 0xB1, 0x1F, 0x22, + 0x0A, 0x60, 0x56, 0x49, 0x55, 0x4D, 0x10, 0x31, 0x61, 0x60, 0x38, 0x21, + 0x29, 0x71, 0x05, 0xF1, + 0x48, 0x03, 0xA3, 0x60, 0x90, 0xF8, 0x31, 0x20, 0x90, 0xF8, 0x30, 0x60, + 0x6A, 0x71, 0x02, 0xFB, + 0x06, 0xF1, 0x03, 0xEB, 0x41, 0x07, 0xAE, 0x71, 0xE7, 0x60, 0xEA, 0x71, + 0x11, 0x44, 0x03, 0xEB, + 0x41, 0x02, 0x22, 0x61, 0x31, 0x44, 0x2E, 0x72, 0x03, 0xEB, 0x41, 0x01, + 0x61, 0x61, 0x01, 0x21, + 0x00, 0xF2, 0x61, 0x40, 0xEE, 0xF7, 0x2F, 0xFD, 0x68, 0x72, 0xA1, 0xE7, + 0x2D, 0xE9, 0xF0, 0x41, + 0x05, 0x46, 0x56, 0x4C, 0x17, 0x46, 0x88, 0x46, 0xC0, 0x07, 0x04, 0xD0, + 0x38, 0x22, 0x42, 0x49, + 0x60, 0x68, 0x04, 0xF0, 0xCA, 0xFC, 0x3E, 0x4E, 0xA8, 0x07, 0x12, 0xD5, + 0x31, 0x68, 0x91, 0xF8, + 0x31, 0x00, 0x91, 0xF8, 0x30, 0x10, 0x48, 0x43, 0x61, 0x78, 0x30, 0x29, + 0x3E, 0xD0, 0x31, 0x29, + 0x3E, 0xD0, 0x32, 0x29, 0x42, 0xD0, 0x3B, 0x49, 0xA3, 0x68, 0x42, 0x00, + 0x18, 0x46, 0x04, 0xF0, + 0xB4, 0xFC, 0x68, 0x07, 0x0E, 0xD5, 0x30, 0x68, 0x90, 0xF8, 0x31, 0x20, + 0x60, 0x78, 0x30, 0x28, + 0x36, 0xD0, 0x31, 0x28, 0x36, 0xD0, 0x32, 0x28, 0x39, 0xD0, 0x36, 0x49, + 0x52, 0x00, 0xE0, 0x68, + 0x04, 0xF0, 0xA3, 0xFC, 0x28, 0x07, 0x0F, 0xD5, 0x30, 0x68, 0x61, 0x78, + 0x90, 0xF8, 0x30, 0x00, + 0x30, 0x29, 0x2E, 0xD0, 0x31, 0x29, 0x2E, 0xD0, 0x32, 0x29, 0x31, 0xD0, + 0x31, 0x49, 0x23, 0x69, + 0x42, 0x00, 0x18, 0x46, 0x04, 0xF0, 0x91, 0xFC, 0xE8, 0x06, 0x31, 0xD5, + 0x1F, 0x48, 0x42, 0x7A, + 0x60, 0x78, 0x30, 0x28, 0x26, 0xD0, 0x31, 0x28, 0x26, 0xD0, 0x32, 0x28, + 0x26, 0xD0, 0x2D, 0x49, + 0x60, 0x69, 0xBD, 0xE8, 0xF0, 0x41, 0x52, 0x00, 0x04, 0xF0, 0x7F, 0xBC, + 0x1B, 0x49, 0xC3, 0xE7, + 0x1B, 0x49, 0xB8, 0xF1, 0x00, 0x0F, 0xBF, 0xD0, 0x2A, 0x49, 0xBD, 0xE7, + 0x1A, 0x49, 0xBB, 0xE7, + 0x1A, 0x49, 0xCB, 0xE7, 0x1A, 0x49, 0x00, 0x2F, 0xC8, 0xD0, 0x27, 0x49, + 0xC6, 0xE7, 0x1A, 0x49, + 0xC4, 0xE7, 0x1A, 0x49, 0xD3, 0xE7, 0x1A, 0x49, 0x00, 0x2F, 0xD0, 0xD0, + 0x23, 0x49, 0xCE, 0xE7, + 0x19, 0x49, 0xCC, 0xE7, 0x19, 0x49, 0xDB, 0xE7, 0x19, 0x49, 0xD9, 0xE7, + 0x1A, 0x49, 0xD7, 0xE7, + 0x26, 0xE7, 0x06, 0x4A, 0xA5, 0x20, 0x10, 0x70, 0x18, 0x48, 0x41, 0x78, + 0x51, 0x70, 0x01, 0x70, + 0x41, 0x88, 0x49, 0x1C, 0x41, 0x80, 0x51, 0x80, 0x70, 0x47, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x20, + 0x4C, 0x07, 0x10, 0x00, 0x50, 0x07, 0x10, 0x00, 0x0A, 0x44, 0x01, 0x20, + 0x14, 0xA6, 0x01, 0x20, + 0xB2, 0x2F, 0x01, 0x20, 0xEE, 0x35, 0x01, 0x20, 0x42, 0x44, 0x01, 0x20, + 0x04, 0xA3, 0x01, 0x20, + 0x1E, 0x0B, 0x01, 0x20, 0x94, 0x0B, 0x01, 0x20, 0x7E, 0x4A, 0x01, 0x20, + 0x2E, 0xA3, 0x01, 0x20, + 0x48, 0x0B, 0x01, 0x20, 0xBE, 0x0B, 0x01, 0x20, 0xA8, 0x4A, 0x01, 0x20, + 0xB8, 0xA4, 0x01, 0x20, + 0xA2, 0x1C, 0x01, 0x20, 0xAA, 0x1C, 0x01, 0x20, 0xF4, 0x4A, 0x01, 0x20, + 0x94, 0x07, 0x10, 0x00, + 0x68, 0x07, 0x10, 0x00, 0x4A, 0x0C, 0x01, 0x20, 0x92, 0x09, 0x01, 0x20, + 0xBC, 0x09, 0x01, 0x20, + 0x1F, 0x48, 0x00, 0x68, 0x90, 0xF8, 0xA0, 0x00, 0xC0, 0x07, 0x01, 0xD0, + 0x00, 0xF0, 0x3A, 0xB8, + 0x00, 0xF0, 0xDC, 0xB8, 0x1A, 0x49, 0x09, 0x68, 0x91, 0xF8, 0xA0, 0x10, + 0xC9, 0x07, 0x02, 0xD0, + 0xC0, 0xB2, 0x00, 0xF0, 0x39, 0xB8, 0x01, 0x46, 0x00, 0x20, 0x00, 0xF0, + 0x2C, 0xB9, 0x38, 0xB5, + 0x04, 0x46, 0x69, 0x46, 0x18, 0x30, 0x00, 0xF0, 0xA2, 0xFD, 0x11, 0x48, + 0x00, 0x99, 0x00, 0x68, + 0x90, 0xF8, 0xA0, 0x00, 0xC0, 0x07, 0x20, 0x46, 0x02, 0xD0, 0x00, 0xF0, + 0x44, 0xF8, 0x38, 0xBD, + 0x00, 0xF0, 0xC6, 0xF8, 0x38, 0xBD, 0x00, 0xF0, 0x24, 0xB9, 0x09, 0x4B, + 0x1B, 0x68, 0x93, 0xF8, + 0xA0, 0x30, 0xDB, 0x07, 0x01, 0xD1, 0x00, 0xF0, 0x3A, 0xB9, 0x70, 0x47, + 0x04, 0x4A, 0x12, 0x68, + 0x92, 0xF8, 0xA0, 0x20, 0x52, 0x07, 0x01, 0xD5, 0x00, 0xF0, 0x43, 0xB9, + 0x70, 0x47, 0x00, 0x00, + 0x4C, 0x07, 0x10, 0x00, 0x1C, 0xB5, 0x4F, 0x49, 0xD1, 0xE9, 0x00, 0x01, + 0xCD, 0xE9, 0x00, 0x01, + 0x68, 0x46, 0xFF, 0xF7, 0x80, 0xFB, 0x1C, 0xBD, 0x1C, 0xB5, 0x4A, 0x4A, + 0xD2, 0xE9, 0x02, 0x12, + 0xCD, 0xE9, 0x00, 0x12, 0x8D, 0xF8, 0x02, 0x00, 0x68, 0x46, 0xFF, 0xF7, + 0x74, 0xFB, 0x1C, 0xBD, + 0x88, 0x42, 0x0E, 0xD9, 0x90, 0x42, 0x01, 0xD3, 0xFF, 0x20, 0x70, 0x47, + 0x91, 0x42, 0x08, 0xD0, + 0x40, 0x1A, 0xC0, 0xEB, 0x00, 0x20, 0x51, 0x1A, 0xB0, 0xFB, 0xF1, 0xF0, + 0x10, 0xF0, 0xFF, 0x00, + 0xF3, 0xD1, 0x01, 0x20, 0x70, 0x47, 0x7C, 0xB5, 0x05, 0x46, 0x00, 0x20, + 0x00, 0x90, 0x01, 0x90, + 0x95, 0xF8, 0x4D, 0x21, 0x9D, 0xF8, 0x00, 0x00, 0x62, 0xF3, 0x85, 0x00, + 0x8D, 0xF8, 0x00, 0x00, + 0x95, 0xF8, 0x3E, 0x21, 0x03, 0x2A, 0x3E, 0xD0, 0x08, 0x2A, 0x40, 0xD0, + 0x20, 0xF0, 0xC0, 0x00, + 0x10, 0xF1, 0x80, 0x00, 0x8D, 0xF8, 0x00, 0x00, 0x0A, 0x14, 0x10, 0x09, + 0x8D, 0xF8, 0x01, 0x00, + 0x9D, 0xF8, 0x03, 0x00, 0x2C, 0x4E, 0x62, 0xF3, 0x07, 0x10, 0x0A, 0x09, + 0x8D, 0xF8, 0x02, 0x20, + 0x61, 0xF3, 0x03, 0x00, 0x8D, 0xF8, 0x03, 0x00, 0x30, 0x68, 0xB0, 0xF8, + 0xA3, 0x20, 0xB0, 0xF8, + 0xA1, 0x10, 0x35, 0xF8, 0x4A, 0x0F, 0xFF, 0xF7, 0xBB, 0xFF, 0x8D, 0xF8, + 0x04, 0x00, 0x30, 0x68, + 0xB0, 0xF8, 0xA7, 0x20, 0xB0, 0xF8, 0xA5, 0x10, 0x68, 0x88, 0xFF, 0xF7, + 0xB1, 0xFF, 0x8D, 0xF8, + 0x05, 0x00, 0x29, 0x7A, 0x9D, 0xF8, 0x06, 0x00, 0x61, 0xF3, 0x05, 0x00, + 0x8D, 0xF8, 0x06, 0x00, + 0x95, 0xF8, 0xF6, 0x10, 0x09, 0x29, 0x1E, 0xD2, 0xDF, 0xE8, 0x01, 0xF0, + 0x0C, 0x0E, 0x10, 0x12, + 0x14, 0x16, 0x18, 0x1A, 0x1C, 0x00, 0x20, 0xF0, 0xC0, 0x00, 0x40, 0x30, + 0xC2, 0xE7, 0x40, 0xF0, + 0xC0, 0x00, 0xBF, 0xE7, 0x00, 0x24, 0x0E, 0xE0, 0x01, 0x24, 0x0C, 0xE0, + 0x02, 0x24, 0x0A, 0xE0, + 0x03, 0x24, 0x08, 0xE0, 0x04, 0x24, 0x06, 0xE0, 0x05, 0x24, 0x04, 0xE0, + 0x06, 0x24, 0x02, 0xE0, + 0x07, 0x24, 0x00, 0xE0, 0x08, 0x24, 0xA1, 0x08, 0x61, 0xF3, 0x87, 0x10, + 0x8D, 0xF8, 0x06, 0x00, + 0x9D, 0xF8, 0x07, 0x00, 0x64, 0xF3, 0x87, 0x10, 0x8D, 0xF8, 0x07, 0x00, + 0x68, 0x46, 0xFF, 0xF7, + 0xEA, 0xFA, 0x7C, 0xBD, 0xF0, 0x7B, 0x01, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x1C, 0xB5, 0x54, 0x49, + 0xD1, 0xE9, 0x00, 0x01, 0xCD, 0xE9, 0x00, 0x01, 0x68, 0x46, 0xFF, 0xF7, + 0xDC, 0xFA, 0x1C, 0xBD, + 0x1C, 0xB5, 0x4F, 0x4B, 0xD3, 0xE9, 0x02, 0x23, 0xCD, 0xE9, 0x00, 0x23, + 0x90, 0xF8, 0x3E, 0x21, + 0x03, 0x2A, 0x19, 0xD0, 0x08, 0x2A, 0x9D, 0xF8, 0x00, 0x20, 0x22, 0xF0, + 0xF0, 0x02, 0x19, 0xD0, + 0x12, 0xF1, 0x20, 0x02, 0x8D, 0xF8, 0x00, 0x20, 0x90, 0xF8, 0x3F, 0x21, + 0x02, 0xF0, 0x0F, 0x02, + 0x01, 0x2A, 0x11, 0xD0, 0x02, 0x2A, 0x15, 0xD0, 0x04, 0x2A, 0x9D, 0xF8, + 0x01, 0x20, 0x22, 0xF0, + 0x0F, 0x02, 0x2E, 0xD0, 0x52, 0x1C, 0x12, 0xE0, 0x9D, 0xF8, 0x00, 0x20, + 0x22, 0xF0, 0xF0, 0x02, + 0x10, 0x32, 0xE7, 0xE7, 0x30, 0x32, 0xE5, 0xE7, 0x9D, 0xF8, 0x01, 0x20, + 0x22, 0xF0, 0x0F, 0x02, + 0x92, 0x1C, 0x04, 0xE0, 0x9D, 0xF8, 0x01, 0x20, 0x22, 0xF0, 0x0F, 0x02, + 0xD2, 0x1C, 0x8D, 0xF8, + 0x01, 0x20, 0x90, 0xF8, 0x4D, 0x01, 0xD2, 0xB2, 0x60, 0xF3, 0x07, 0x12, + 0x8D, 0xF8, 0x01, 0x20, + 0xBD, 0xF8, 0x02, 0x00, 0x0A, 0x0C, 0x62, 0xF3, 0x0B, 0x00, 0xAD, 0xF8, + 0x02, 0x00, 0xBD, 0xF8, + 0x03, 0x00, 0x61, 0xF3, 0x0F, 0x10, 0xAD, 0xF8, 0x03, 0x00, 0x68, 0x46, + 0xFF, 0xF7, 0x8B, 0xFA, + 0x1C, 0xBD, 0x12, 0x1D, 0xE3, 0xE7, 0x1C, 0xB5, 0x25, 0x4B, 0xD3, 0xE9, + 0x04, 0x23, 0xCD, 0xE9, + 0x00, 0x23, 0x8D, 0xF8, 0x01, 0x00, 0xCD, 0xF8, 0x02, 0x10, 0x68, 0x46, + 0xFF, 0xF7, 0x7B, 0xFA, + 0x1C, 0xBD, 0x1C, 0xB5, 0x1F, 0x4A, 0x12, 0x68, 0x92, 0xF8, 0xA0, 0x20, + 0x93, 0x07, 0x15, 0xD5, + 0x08, 0x28, 0x13, 0xD2, 0xDF, 0xE8, 0x00, 0xF0, 0x12, 0x04, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, + 0x12, 0x06, 0x0B, 0xD5, 0x16, 0x4B, 0xD3, 0xE9, 0x06, 0x23, 0xCD, 0xE9, + 0x00, 0x23, 0x8D, 0xF8, + 0x01, 0x00, 0xCD, 0xF8, 0x02, 0x10, 0x68, 0x46, 0xFF, 0xF7, 0x5D, 0xFA, + 0x1C, 0xBD, 0x1C, 0xB5, + 0x0F, 0x4C, 0xD4, 0xE9, 0x08, 0x34, 0xCD, 0xE9, 0x00, 0x34, 0x8D, 0xF8, + 0x01, 0x00, 0xCD, 0xF8, + 0x02, 0x10, 0x68, 0x46, 0x12, 0xB1, 0xFF, 0xF7, 0x2A, 0xFC, 0x1C, 0xBD, + 0xFF, 0xF7, 0x4B, 0xFA, + 0x1C, 0xBD, 0x1C, 0xB5, 0x06, 0x4B, 0xD3, 0xE9, 0x0A, 0x23, 0xCD, 0xE9, + 0x00, 0x23, 0x8D, 0xF8, + 0x01, 0x00, 0xCD, 0xF8, 0x02, 0x10, 0x68, 0x46, 0xFF, 0xF7, 0x3D, 0xFA, + 0x1C, 0xBD, 0x00, 0x00, + 0x08, 0x7C, 0x01, 0x00, 0x4C, 0x07, 0x10, 0x00, 0x2D, 0xE9, 0xFF, 0x41, + 0xED, 0xF7, 0xDC, 0xFB, + 0x23, 0x4D, 0x00, 0x24, 0xC4, 0xEB, 0xC4, 0x01, 0x05, 0xEB, 0x81, 0x00, + 0x00, 0xF1, 0x0C, 0x03, + 0x02, 0x7E, 0xC8, 0xCB, 0xCD, 0xE9, 0x02, 0x72, 0xCD, 0xE9, 0x00, 0x36, + 0xD0, 0xE9, 0x01, 0x23, + 0x55, 0xF8, 0x21, 0x00, 0x00, 0x21, 0xED, 0xF7, 0xB1, 0xFD, 0x18, 0xB1, + 0x21, 0x46, 0x43, 0x20, + 0xFF, 0xF7, 0x2F, 0xFC, 0x64, 0x1C, 0x05, 0x2C, 0xE4, 0xDB, 0x15, 0x4D, + 0x00, 0x24, 0x8C, 0x35, + 0x05, 0xEB, 0xC4, 0x00, 0x00, 0x21, 0x42, 0x68, 0x55, 0xF8, 0x34, 0x00, + 0xED, 0xF7, 0x25, 0xFD, + 0x18, 0xB1, 0x21, 0x46, 0x44, 0x20, 0xFF, 0xF7, 0x1C, 0xFC, 0x64, 0x1C, + 0x06, 0x2C, 0xEF, 0xDB, + 0x0B, 0x4D, 0x00, 0x24, 0x15, 0xF1, 0xBC, 0x05, 0x00, 0x21, 0x55, 0xF8, + 0x24, 0x00, 0xED, 0xF7, + 0x3A, 0xFC, 0x18, 0xB1, 0x21, 0x46, 0x45, 0x20, 0xFF, 0xF7, 0x0B, 0xFC, + 0x64, 0x1C, 0x04, 0x2C, + 0xF2, 0xDB, 0xBD, 0xE8, 0xFF, 0x81, 0x01, 0x46, 0x46, 0x20, 0xFF, 0xF7, + 0x02, 0xBC, 0x00, 0x00, + 0x38, 0x7C, 0x01, 0x00, 0x00, 0xF0, 0x1F, 0x03, 0x01, 0x22, 0x9A, 0x40, + 0x43, 0x09, 0x9B, 0x00, + 0x03, 0xF1, 0xE0, 0x23, 0xC3, 0xF8, 0x80, 0x22, 0x49, 0x07, 0x09, 0x0E, + 0x00, 0x28, 0x06, 0xDA, + 0x00, 0xF0, 0x0F, 0x00, 0x00, 0xF1, 0xE0, 0x20, 0x80, 0xF8, 0x14, 0x1D, + 0x03, 0xE0, 0x00, 0xF1, + 0xE0, 0x20, 0x80, 0xF8, 0x00, 0x14, 0xC3, 0xF8, 0x00, 0x21, 0x70, 0x47, + 0x2D, 0xE9, 0xF0, 0x41, + 0x64, 0x27, 0x07, 0x26, 0x00, 0x25, 0xFD, 0x4C, 0xFD, 0x48, 0xEE, 0xF7, + 0xF3, 0xF9, 0xFC, 0x48, + 0x03, 0x68, 0xD9, 0x1C, 0x3C, 0xD0, 0x42, 0x68, 0xD1, 0x1C, 0x39, 0xD0, + 0x81, 0x68, 0x11, 0xF1, + 0x03, 0x0F, 0x35, 0xD0, 0x00, 0x69, 0x10, 0xF1, 0x03, 0x0F, 0x31, 0xD0, + 0xB3, 0xF1, 0xFF, 0x3F, + 0x31, 0xD0, 0x13, 0xF1, 0x02, 0x0F, 0x2E, 0xD0, 0xB2, 0xF1, 0xFF, 0x3F, + 0x2B, 0xD0, 0x12, 0xF1, + 0x02, 0x0F, 0x28, 0xD0, 0xB1, 0xF1, 0xFF, 0x3F, 0x25, 0xD0, 0x11, 0xF1, + 0x02, 0x0F, 0x22, 0xD0, + 0xB0, 0xF1, 0xFF, 0x3F, 0x1F, 0xD0, 0x10, 0xF1, 0x02, 0x0F, 0x1C, 0xD0, + 0xDF, 0xB2, 0xD6, 0xB2, + 0xCD, 0xB2, 0x04, 0x46, 0x2A, 0x46, 0x31, 0x46, 0x38, 0x46, 0xEE, 0xF7, + 0xF1, 0xF8, 0x20, 0x46, + 0xEE, 0xF7, 0xFB, 0xF8, 0xE3, 0x48, 0xEE, 0xF7, 0x64, 0xF9, 0x03, 0x28, + 0x10, 0xD0, 0x01, 0x28, + 0x11, 0xD0, 0x02, 0x28, 0x0F, 0xD0, 0xDF, 0x48, 0x00, 0x68, 0x01, 0x28, + 0x10, 0xD0, 0x12, 0xE0, + 0x00, 0x21, 0x24, 0x20, 0x01, 0xE0, 0x00, 0x21, 0x25, 0x20, 0xFF, 0xF7, + 0xDF, 0xFB, 0xE1, 0xE7, + 0x00, 0x21, 0x29, 0x20, 0x01, 0xE0, 0x00, 0x21, 0x2A, 0x20, 0xFF, 0xF7, + 0xD7, 0xFB, 0x02, 0xE0, + 0xD4, 0x48, 0xED, 0xF7, 0x7B, 0xFD, 0xD2, 0x48, 0x14, 0x30, 0xEE, 0xF7, + 0x6C, 0xF9, 0x04, 0x46, + 0x03, 0x28, 0x04, 0xD0, 0x01, 0x2C, 0x05, 0xD0, 0x02, 0x2C, 0x03, 0xD0, + 0x06, 0xE0, 0x00, 0x21, + 0x2B, 0x20, 0x01, 0xE0, 0x00, 0x21, 0x2C, 0x20, 0xFF, 0xF7, 0xC0, 0xFB, + 0x21, 0x46, 0xBD, 0xE8, + 0xF0, 0x41, 0xC7, 0x48, 0x14, 0x30, 0x00, 0xF0, 0x81, 0xBF, 0x7C, 0xB5, + 0xC6, 0x4E, 0x01, 0xAA, + 0x69, 0x46, 0x30, 0x46, 0xEE, 0xF7, 0x09, 0xFA, 0xC4, 0x4C, 0x40, 0xF2, + 0xBC, 0x65, 0x01, 0x28, + 0x02, 0xD0, 0x00, 0x21, 0x22, 0x20, 0x10, 0xE0, 0x00, 0x98, 0xA0, 0xF5, + 0x80, 0x71, 0xAF, 0x39, + 0xF7, 0xD1, 0x2A, 0x46, 0x31, 0x46, 0x20, 0x68, 0x04, 0xF0, 0x8F, 0xF9, + 0x20, 0x68, 0xEE, 0xF7, + 0x07, 0xFA, 0x01, 0x28, 0x08, 0xD0, 0x00, 0x21, 0x23, 0x20, 0xFF, 0xF7, + 0x97, 0xFB, 0x29, 0x46, + 0x20, 0x68, 0x04, 0xF0, 0x01, 0xFA, 0x7C, 0xBD, 0xB5, 0x48, 0x00, 0x68, + 0x90, 0xF8, 0x70, 0x11, + 0x89, 0x07, 0x07, 0xD4, 0x90, 0xF8, 0x88, 0x11, 0xC9, 0x06, 0x03, 0xD4, + 0x90, 0xF8, 0xA0, 0x01, + 0xC0, 0x06, 0xF0, 0xD5, 0x24, 0x68, 0x26, 0x22, 0x04, 0xF2, 0x02, 0x61, + 0xAD, 0x48, 0x04, 0xF0, + 0x6C, 0xF9, 0xAC, 0x48, 0x1D, 0x22, 0x04, 0xF5, 0xC5, 0x61, 0x28, 0x30, + 0x04, 0xF0, 0x65, 0xF9, + 0xA8, 0x48, 0x26, 0x22, 0x04, 0xF2, 0x5E, 0x61, 0x48, 0x30, 0x04, 0xF0, + 0x5E, 0xF9, 0xA5, 0x48, + 0x1D, 0x22, 0x04, 0xF2, 0x84, 0x61, 0x70, 0x30, 0x04, 0xF0, 0x57, 0xF9, + 0x7C, 0xBD, 0xF8, 0xB5, + 0x0D, 0x46, 0x06, 0x46, 0x00, 0x24, 0x6A, 0x46, 0xEE, 0xF7, 0xB7, 0xF9, + 0x01, 0x28, 0x01, 0xD0, + 0x01, 0x24, 0x0B, 0xE0, 0x28, 0x68, 0x31, 0x46, 0x82, 0x00, 0x9B, 0x48, + 0x04, 0xF0, 0x8A, 0xF9, + 0x99, 0x48, 0xEE, 0xF7, 0xBD, 0xF9, 0x01, 0x28, 0x00, 0xD0, 0x02, 0x24, + 0x20, 0x46, 0xF8, 0xBD, + 0x2D, 0xE9, 0xF8, 0x43, 0x94, 0x4D, 0x95, 0x4F, 0x10, 0x35, 0x41, 0xF2, + 0xC8, 0x11, 0x94, 0x48, + 0x04, 0xF0, 0xD4, 0xF9, 0x69, 0x46, 0x38, 0x46, 0xFF, 0xF7, 0xD9, 0xFF, + 0x01, 0x28, 0x05, 0xD0, + 0x02, 0x28, 0x06, 0xD0, 0xE8, 0x78, 0x01, 0x28, 0x0A, 0xD1, 0x05, 0xE0, + 0x00, 0x21, 0xA0, 0x20, + 0x2E, 0xE0, 0x00, 0x21, 0xA1, 0x20, 0x2B, 0xE0, 0xA8, 0x78, 0x08, 0xB9, + 0x28, 0x88, 0x10, 0xB1, + 0x00, 0x21, 0xA2, 0x20, 0x24, 0xE0, 0x82, 0x4E, 0xE9, 0x88, 0x30, 0x68, + 0x82, 0x8A, 0x91, 0x42, + 0x02, 0xD0, 0x00, 0x21, 0xA3, 0x20, 0x1B, 0xE0, 0xA9, 0x88, 0xC0, 0x8A, + 0x81, 0x42, 0x02, 0xD0, + 0x00, 0x21, 0xA4, 0x20, 0x14, 0xE0, 0x00, 0x24, 0x00, 0x98, 0x69, 0x46, + 0x07, 0xEB, 0x80, 0x07, + 0x38, 0x46, 0xFF, 0xF7, 0xAC, 0xFF, 0x78, 0xB1, 0x01, 0x28, 0x03, 0xD0, + 0x00, 0x21, 0xA6, 0x20, + 0xFF, 0xF7, 0x0C, 0xFB, 0xE0, 0x07, 0x35, 0xD0, 0xA0, 0x07, 0x35, 0xD4, + 0x11, 0x21, 0xA7, 0x20, + 0xFF, 0xF7, 0x04, 0xFB, 0xBD, 0xE8, 0xF8, 0x83, 0x95, 0xF8, 0x01, 0x80, + 0x29, 0x46, 0x40, 0x46, + 0x00, 0xF0, 0xDE, 0xFD, 0xA0, 0xB1, 0xB8, 0xF1, 0x10, 0x0F, 0x14, 0xD0, + 0xB8, 0xF1, 0x11, 0x0F, + 0x14, 0xD0, 0xB8, 0xF1, 0x12, 0x0F, 0x14, 0xD0, 0xB8, 0xF1, 0x13, 0x0F, + 0x14, 0xD0, 0xB8, 0xF1, + 0x14, 0x0F, 0x14, 0xD0, 0xB8, 0xF1, 0x17, 0x0F, 0xCE, 0xD1, 0x44, 0xF0, + 0x20, 0x04, 0xCB, 0xE7, + 0x41, 0x46, 0xA8, 0x20, 0xD4, 0xE7, 0x44, 0xF0, 0x01, 0x04, 0xC5, 0xE7, + 0x44, 0xF0, 0x02, 0x04, + 0xC2, 0xE7, 0x44, 0xF0, 0x08, 0x04, 0xBF, 0xE7, 0x44, 0xF0, 0x10, 0x04, + 0xBC, 0xE7, 0x44, 0xF0, + 0x04, 0x04, 0xB9, 0xE7, 0x10, 0x21, 0xCA, 0xE7, 0x20, 0x07, 0x0A, 0xD4, + 0x30, 0x68, 0x90, 0xF8, + 0x50, 0x12, 0xC9, 0x07, 0x03, 0xD1, 0x90, 0xF8, 0x80, 0x02, 0xC0, 0x07, + 0x01, 0xD0, 0x12, 0x21, + 0xBD, 0xE7, 0xE0, 0x06, 0x01, 0xD4, 0x13, 0x21, 0xB9, 0xE7, 0x60, 0x07, + 0x05, 0xD4, 0x30, 0x68, + 0x90, 0xF8, 0x61, 0x04, 0x08, 0xB1, 0x14, 0x21, 0xB1, 0xE7, 0xA0, 0x06, + 0xB2, 0xD4, 0x30, 0x68, + 0x90, 0xF8, 0x2F, 0x0A, 0xC0, 0x07, 0xAD, 0xD0, 0x17, 0x21, 0xA8, 0xE7, + 0x10, 0xB5, 0x8E, 0xB0, + 0xED, 0xF7, 0x66, 0xF9, 0x00, 0x20, 0x01, 0x22, 0x41, 0xB2, 0x01, 0xF0, + 0x1F, 0x03, 0x02, 0xFA, + 0x03, 0xF3, 0x49, 0x09, 0x89, 0x00, 0x01, 0xF1, 0xE0, 0x21, 0xC1, 0xF8, + 0x80, 0x31, 0x40, 0x1C, + 0x1F, 0x28, 0xF1, 0xDB, 0xED, 0xF7, 0x52, 0xF9, 0xED, 0xF7, 0x5C, 0xFF, + 0xEE, 0xF7, 0x26, 0xF9, + 0x07, 0x21, 0x0A, 0x20, 0xFF, 0xF7, 0x46, 0xFE, 0x07, 0x21, 0x0B, 0x20, + 0xFF, 0xF7, 0x42, 0xFE, + 0xFE, 0xF7, 0xFE, 0xFF, 0xFE, 0xF7, 0xAA, 0xFE, 0x04, 0xA8, 0xFF, 0xF7, + 0xEC, 0xF9, 0x0A, 0xA9, + 0x07, 0xA8, 0xFF, 0xF7, 0x89, 0xF8, 0x04, 0xA8, 0x00, 0x90, 0x07, 0xA8, + 0x02, 0x90, 0x0A, 0xA8, + 0x03, 0x90, 0x68, 0x46, 0xEE, 0xF7, 0x26, 0xFA, 0x02, 0x21, 0x00, 0x20, + 0xFF, 0xF7, 0x2A, 0xFE, + 0xFF, 0xF7, 0x04, 0xF8, 0xFF, 0xF7, 0x20, 0xFA, 0xFF, 0xF7, 0x40, 0xFE, + 0xED, 0xF7, 0x60, 0xFF, + 0x28, 0x4C, 0x01, 0xAA, 0x69, 0x46, 0x20, 0x46, 0xEE, 0xF7, 0xBF, 0xF8, + 0x01, 0x28, 0x02, 0xD0, + 0x00, 0x21, 0x20, 0x20, 0x11, 0xE0, 0x00, 0x98, 0xB0, 0xF5, 0x25, 0x7F, + 0xF8, 0xD1, 0x21, 0x46, + 0x1B, 0x4C, 0x4F, 0xF4, 0x25, 0x62, 0x20, 0x68, 0x04, 0xF0, 0x47, 0xF8, + 0x20, 0x68, 0xEE, 0xF7, + 0xBF, 0xF8, 0x01, 0x28, 0x03, 0xD0, 0x00, 0x21, 0x21, 0x20, 0xFF, 0xF7, + 0x02, 0xFA, 0xFF, 0xF7, + 0x94, 0xFE, 0xFF, 0xF7, 0xFD, 0xFE, 0x00, 0xF0, 0x07, 0xF8, 0x00, 0xF0, + 0x55, 0xF8, 0x0E, 0xB0, + 0xBD, 0xE8, 0x10, 0x40, 0x00, 0xF0, 0x86, 0xB8, 0x10, 0xB5, 0xEE, 0xF7, + 0xBF, 0xFA, 0x0B, 0x48, + 0x00, 0x68, 0x01, 0x7C, 0xA5, 0x29, 0x1F, 0xD0, 0x4F, 0xF4, 0xFA, 0x40, + 0xEE, 0xF7, 0x61, 0xFC, + 0x00, 0x21, 0x28, 0x20, 0xFF, 0xF7, 0x32, 0xFA, 0x19, 0xE0, 0x00, 0x00, + 0x00, 0x90, 0xD0, 0x03, + 0x84, 0x7B, 0x10, 0x00, 0xE8, 0x84, 0x10, 0x00, 0xF0, 0x9F, 0x01, 0x00, + 0x50, 0x07, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, 0xE4, 0x9F, 0x01, 0x20, 0xD4, 0x56, 0x10, 0x00, + 0xF0, 0xAF, 0x01, 0x00, + 0x78, 0x8B, 0x01, 0x20, 0xF0, 0xEF, 0x01, 0x00, 0x40, 0x69, 0xEE, 0xF7, + 0x42, 0xFC, 0x04, 0x21, + 0x02, 0x20, 0xFF, 0xF7, 0xC7, 0xFD, 0x04, 0x21, 0x03, 0x20, 0xFF, 0xF7, + 0xC3, 0xFD, 0x04, 0x21, + 0x08, 0x46, 0xFF, 0xF7, 0xBF, 0xFD, 0x04, 0x21, 0x05, 0x20, 0xFF, 0xF7, + 0xBB, 0xFD, 0x04, 0x21, + 0x06, 0x20, 0xFF, 0xF7, 0xB7, 0xFD, 0x04, 0x21, 0x07, 0x20, 0xFF, 0xF7, + 0xB3, 0xFD, 0x04, 0x21, + 0x08, 0x20, 0xFF, 0xF7, 0xAF, 0xFD, 0x01, 0x21, 0x09, 0x20, 0xFF, 0xF7, + 0xAB, 0xFD, 0x04, 0x21, + 0xBD, 0xE8, 0x10, 0x40, 0x0F, 0x20, 0xA5, 0xE5, 0x38, 0xB5, 0x03, 0xF0, + 0xFB, 0xFB, 0x03, 0x21, + 0x01, 0x20, 0xFF, 0xF7, 0x9F, 0xFD, 0x28, 0x4C, 0x20, 0x68, 0x90, 0xF8, + 0xB0, 0x10, 0xC9, 0x07, + 0x27, 0xD0, 0x00, 0x25, 0x00, 0x95, 0x90, 0xF8, 0xC0, 0x00, 0x2B, 0x46, + 0xC0, 0xF3, 0x02, 0x10, + 0x01, 0x22, 0x29, 0x46, 0xEE, 0xF7, 0x87, 0xF8, 0x20, 0x68, 0x0A, 0x21, + 0x90, 0xF8, 0xC0, 0x00, + 0xC0, 0xF3, 0x02, 0x10, 0xEE, 0xF7, 0xA7, 0xF8, 0x00, 0x95, 0x20, 0x68, + 0x00, 0x23, 0x01, 0x22, + 0x90, 0xF8, 0xC0, 0x00, 0x19, 0x46, 0x00, 0xF0, 0x07, 0x00, 0xEE, 0xF7, + 0x74, 0xF8, 0x20, 0x68, + 0x01, 0x21, 0x90, 0xF8, 0xC0, 0x00, 0xBD, 0xE8, 0x38, 0x40, 0x00, 0xF0, + 0x07, 0x00, 0xEE, 0xF7, + 0x92, 0xB8, 0x38, 0xBD, 0x38, 0xB5, 0x10, 0x4C, 0x20, 0x68, 0x90, 0xF8, + 0x60, 0x03, 0xC1, 0x07, + 0x0F, 0xD0, 0x81, 0x07, 0x0D, 0xD5, 0x00, 0x21, 0x01, 0x23, 0x40, 0x09, + 0x1A, 0x46, 0x00, 0x91, + 0xEE, 0xF7, 0x59, 0xF8, 0x20, 0x68, 0x00, 0x21, 0x90, 0xF8, 0x60, 0x03, + 0x40, 0x09, 0xEE, 0xF7, + 0x7A, 0xF8, 0x20, 0x68, 0x90, 0xF8, 0x79, 0x00, 0xBD, 0xE8, 0x38, 0x40, + 0x00, 0xF0, 0x03, 0x01, + 0xC0, 0x09, 0xED, 0xF7, 0xA5, 0xBE, 0x00, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x88, 0x42, 0x00, 0xD3, + 0x08, 0x46, 0x90, 0x42, 0x00, 0xD3, 0x10, 0x46, 0x70, 0x47, 0x88, 0x42, + 0x00, 0xD8, 0x08, 0x46, + 0x90, 0x42, 0x00, 0xD8, 0x10, 0x46, 0x70, 0x47, 0x88, 0x42, 0x00, 0xD3, + 0x08, 0x46, 0x70, 0x47, + 0x88, 0x42, 0x00, 0xD8, 0x08, 0x46, 0x70, 0x47, 0x02, 0x46, 0x00, 0x20, + 0x4F, 0xF0, 0x80, 0x41, + 0x00, 0xE0, 0x89, 0x08, 0x91, 0x42, 0xFC, 0xD8, 0x07, 0xE0, 0x43, 0x18, + 0x93, 0x42, 0x02, 0xD8, + 0xD2, 0x1A, 0x00, 0xEB, 0x41, 0x00, 0x40, 0x08, 0x89, 0x08, 0x00, 0x29, + 0xF5, 0xD1, 0x82, 0x42, + 0x00, 0xD9, 0x40, 0x1C, 0x70, 0x47, 0x10, 0xB5, 0x5A, 0x43, 0x02, 0x9C, + 0x06, 0xE0, 0x00, 0xBF, + 0x50, 0xF8, 0x22, 0x30, 0x93, 0xFB, 0xF4, 0xF3, 0x41, 0xF8, 0x22, 0x30, + 0x52, 0x1E, 0xF7, 0xD2, + 0x10, 0xBD, 0x10, 0xB5, 0x5A, 0x43, 0x02, 0x9C, 0x05, 0xE0, 0x00, 0xBF, + 0x50, 0xF8, 0x22, 0x30, + 0x63, 0x43, 0x41, 0xF8, 0x22, 0x30, 0x52, 0x1E, 0xF8, 0xD2, 0x10, 0xBD, + 0x30, 0xB5, 0x03, 0x9C, + 0x63, 0x43, 0x06, 0xE0, 0x50, 0xF8, 0x23, 0x40, 0x51, 0xF8, 0x23, 0x50, + 0x2C, 0x44, 0x42, 0xF8, + 0x23, 0x40, 0x5B, 0x1E, 0xF6, 0xD2, 0x30, 0xBD, 0x30, 0xB5, 0x03, 0x9C, + 0x63, 0x43, 0x06, 0xE0, + 0x50, 0xF8, 0x23, 0x40, 0x51, 0xF8, 0x23, 0x50, 0x64, 0x1B, 0x42, 0xF8, + 0x23, 0x40, 0x5B, 0x1E, + 0xF6, 0xD2, 0x30, 0xBD, 0x2D, 0xE9, 0xF0, 0x4F, 0x83, 0x46, 0xDD, 0xE9, + 0x09, 0x06, 0xDD, 0xF8, + 0x34, 0xE0, 0x8A, 0x46, 0x82, 0x42, 0x26, 0xD1, 0x00, 0x20, 0x22, 0xE0, + 0x00, 0xFB, 0x02, 0xF1, + 0x0B, 0xEB, 0x81, 0x09, 0x0B, 0x9D, 0x00, 0xFB, 0x0E, 0xF1, 0x00, 0x24, + 0x05, 0xEB, 0x81, 0x05, + 0x14, 0xE0, 0x00, 0x21, 0x0F, 0xE0, 0x00, 0xBF, 0x01, 0xFB, 0x06, 0xF8, + 0x03, 0xEB, 0x88, 0x08, + 0x59, 0xF8, 0x21, 0xC0, 0x55, 0xF8, 0x24, 0x70, 0x58, 0xF8, 0x24, 0x80, + 0x49, 0x1C, 0x0C, 0xFB, + 0x08, 0x77, 0x45, 0xF8, 0x24, 0x70, 0x91, 0x42, 0xEE, 0xDB, 0x64, 0x1C, + 0xB4, 0x42, 0xE8, 0xDB, + 0x40, 0x1C, 0x50, 0x45, 0xDA, 0xDB, 0xBD, 0xE8, 0xF0, 0x8F, 0x10, 0xB5, + 0x00, 0x23, 0x1C, 0x46, + 0x42, 0xB1, 0x02, 0x78, 0x52, 0x1C, 0xD2, 0xB2, 0x02, 0x70, 0x8A, 0x42, + 0x03, 0xD9, 0x04, 0x70, + 0x01, 0x23, 0x00, 0xE0, 0x04, 0x70, 0x18, 0x46, 0x10, 0xBD, 0x30, 0xB5, + 0x00, 0x24, 0x01, 0x23, + 0x05, 0xE0, 0x0C, 0x44, 0xE4, 0x1C, 0x04, 0xF0, 0x03, 0x05, 0x64, 0x1B, + 0x5B, 0x1C, 0x93, 0x42, + 0xF7, 0xDD, 0x00, 0xEB, 0x44, 0x00, 0x30, 0xBD, 0x88, 0x42, 0x01, 0xDD, + 0x08, 0x46, 0x70, 0x47, + 0x00, 0x28, 0xFC, 0xDA, 0x00, 0x20, 0x70, 0x47, 0x10, 0xB5, 0x04, 0xE0, + 0x00, 0xEB, 0x82, 0x03, + 0x53, 0xF8, 0x04, 0x4C, 0x1C, 0x60, 0x52, 0x1E, 0xF8, 0xD1, 0x09, 0x68, + 0x01, 0x60, 0x10, 0xBD, + 0x10, 0xB5, 0x03, 0xE0, 0x00, 0xEB, 0x82, 0x03, 0x0C, 0x68, 0x1C, 0x60, + 0x52, 0x1E, 0xF9, 0xD2, + 0x10, 0xBD, 0x30, 0xB5, 0x06, 0xE0, 0x00, 0xBF, 0x00, 0xEB, 0xC2, 0x03, + 0x73, 0xE9, 0x02, 0x45, + 0xC3, 0xE9, 0x02, 0x45, 0x52, 0x1E, 0xF7, 0xD1, 0xD1, 0xE9, 0x00, 0x21, + 0xC0, 0xE9, 0x00, 0x21, + 0x30, 0xBD, 0x10, 0xB5, 0x53, 0x88, 0x4C, 0x88, 0x1B, 0x1B, 0x43, 0x80, + 0x12, 0x88, 0x09, 0x88, + 0x51, 0x1A, 0x01, 0x80, 0x10, 0xBD, 0x08, 0xB5, 0x0A, 0x46, 0x01, 0x46, + 0x68, 0x46, 0xFF, 0xF7, + 0xF0, 0xFF, 0xBD, 0xF9, 0x00, 0x10, 0xBD, 0xF9, 0x02, 0x00, 0x49, 0x43, + 0x00, 0xFB, 0x00, 0x10, + 0x08, 0xBD, 0x10, 0xB5, 0x88, 0x42, 0x02, 0x9C, 0x01, 0xDC, 0x20, 0x46, + 0x10, 0xBD, 0x90, 0x42, + 0x01, 0xDB, 0x18, 0x46, 0x10, 0xBD, 0x8A, 0x42, 0x00, 0xD1, 0x52, 0x1C, + 0x40, 0x1A, 0xE3, 0x1A, + 0x58, 0x43, 0x51, 0x1A, 0x90, 0xFB, 0xF1, 0xF0, 0x20, 0x1A, 0x10, 0xBD, + 0x10, 0xB5, 0x00, 0x2A, + 0x03, 0xD0, 0x43, 0x1A, 0x93, 0x42, 0x01, 0xDD, 0x88, 0x18, 0x10, 0xBD, + 0x54, 0x42, 0xA3, 0x42, + 0xFB, 0xDA, 0x88, 0x1A, 0x10, 0xBD, 0x91, 0x42, 0x01, 0xD1, 0x00, 0xB2, + 0x70, 0x47, 0x50, 0x43, + 0xB0, 0xFB, 0xF1, 0xF0, 0x00, 0xB2, 0x90, 0x42, 0xF7, 0xD9, 0x10, 0x46, + 0xF5, 0xE7, 0x70, 0xB5, + 0x0B, 0x00, 0x02, 0x46, 0x3D, 0xD0, 0x1F, 0x4C, 0x20, 0x68, 0x46, 0x8E, + 0x85, 0x8E, 0xB2, 0xF9, + 0x02, 0x00, 0x31, 0xB2, 0xFF, 0xF7, 0x80, 0xFF, 0x58, 0x80, 0xB2, 0xF9, + 0x00, 0x00, 0x29, 0xB2, + 0xFF, 0xF7, 0x7A, 0xFF, 0x18, 0x80, 0x21, 0x68, 0x91, 0xF8, 0x36, 0x10, + 0x49, 0x07, 0x02, 0xD5, + 0x59, 0x88, 0x71, 0x1A, 0x59, 0x80, 0x21, 0x68, 0x91, 0xF8, 0x36, 0x10, + 0x89, 0x07, 0x01, 0xD5, + 0x28, 0x1A, 0x18, 0x80, 0x20, 0x68, 0x90, 0xF8, 0x36, 0x10, 0x09, 0x07, + 0x10, 0xD5, 0xB0, 0xF8, + 0x37, 0x20, 0xB3, 0xF9, 0x02, 0x00, 0x31, 0x46, 0xFF, 0xF7, 0xC5, 0xFF, + 0x58, 0x80, 0x20, 0x68, + 0x29, 0x46, 0xB0, 0xF8, 0x39, 0x20, 0xB3, 0xF9, 0x00, 0x00, 0xFF, 0xF7, + 0xBC, 0xFF, 0x18, 0x80, + 0x20, 0x68, 0x90, 0xF8, 0x36, 0x00, 0xC0, 0x07, 0x03, 0xD0, 0x58, 0x88, + 0x19, 0x88, 0x59, 0x80, + 0x18, 0x80, 0x70, 0xBD, 0x4C, 0x07, 0x10, 0x00, 0x31, 0xB5, 0xED, 0xF7, + 0xE1, 0xF9, 0x04, 0x00, + 0x08, 0xD4, 0xDD, 0xF8, 0x00, 0x00, 0x01, 0x22, 0x02, 0xFA, 0x04, 0xF1, + 0x01, 0x42, 0x01, 0xD1, + 0x64, 0x1E, 0xF9, 0xD5, 0x04, 0x21, 0x68, 0x46, 0xED, 0xF7, 0x5D, 0xFE, + 0xFE, 0x49, 0xC0, 0xB2, + 0x09, 0x68, 0x91, 0xF8, 0xE7, 0x10, 0xC1, 0xF3, 0x41, 0x11, 0x02, 0x29, + 0x0E, 0xD3, 0x10, 0xF0, + 0x03, 0x00, 0x05, 0xD0, 0x01, 0x28, 0x05, 0xD0, 0x02, 0x28, 0x05, 0xD0, + 0x1F, 0x20, 0x0A, 0xE0, + 0x0F, 0x20, 0x08, 0xE0, 0x7F, 0x20, 0x06, 0xE0, 0x3F, 0x20, 0x04, 0xE0, + 0xC0, 0x07, 0x01, 0xD0, + 0x07, 0x20, 0x00, 0xE0, 0x03, 0x20, 0x64, 0x1C, 0x00, 0x99, 0xA0, 0x40, + 0x08, 0x43, 0x38, 0xBD, + 0x10, 0xB5, 0xEE, 0x4C, 0x50, 0x28, 0x13, 0xD0, 0x09, 0xDC, 0xA0, 0xF1, + 0x10, 0x00, 0x08, 0x28, + 0x40, 0xD2, 0xDF, 0xE8, 0x00, 0xF0, 0x0D, 0x20, 0x20, 0x20, 0x26, 0x3F, + 0x3F, 0x39, 0x51, 0x38, + 0x07, 0x28, 0x37, 0xD2, 0xDF, 0xE8, 0x00, 0xF0, 0x17, 0x17, 0x17, 0x1D, + 0x36, 0x36, 0x30, 0x00, + 0x03, 0xF0, 0x8D, 0xFA, 0x20, 0x60, 0x03, 0xF0, 0x8C, 0xFA, 0xC4, 0xE9, + 0x04, 0x01, 0xDE, 0x48, + 0x00, 0x68, 0x90, 0xF8, 0xE7, 0x00, 0x10, 0xF0, 0x60, 0x0F, 0x11, 0xD0, + 0x20, 0x68, 0xFF, 0xF7, + 0xA3, 0xFF, 0x20, 0x60, 0x0C, 0xE0, 0x03, 0xF0, 0x7A, 0xFA, 0x20, 0x60, + 0x03, 0xF0, 0x79, 0xFA, + 0x04, 0xE0, 0x03, 0xF0, 0x78, 0xFA, 0x20, 0x60, 0x03, 0xF0, 0x77, 0xFA, + 0xC4, 0xE9, 0x04, 0x01, + 0x04, 0x21, 0xD2, 0x48, 0xED, 0xF7, 0xFF, 0xFD, 0x60, 0x60, 0xD0, 0x48, + 0x08, 0x21, 0x10, 0x30, + 0xED, 0xF7, 0xF9, 0xFD, 0xA0, 0x60, 0x10, 0xBD, 0x03, 0xF0, 0x69, 0xFA, + 0x20, 0x60, 0x03, 0xF0, + 0x68, 0xFA, 0xEB, 0xE7, 0x00, 0x20, 0x01, 0x46, 0x20, 0x60, 0xE7, 0xE7, + 0x2D, 0xE9, 0xFF, 0x5F, + 0x04, 0x21, 0xDD, 0xE9, 0x0F, 0xAB, 0x68, 0x46, 0xED, 0xF7, 0xE5, 0xFD, + 0x81, 0x46, 0x08, 0x21, + 0x02, 0xA8, 0xED, 0xF7, 0xE0, 0xFD, 0x07, 0x46, 0x00, 0x24, 0x20, 0x46, + 0x35, 0xE0, 0x01, 0x20, + 0x00, 0x99, 0x00, 0xFA, 0x08, 0xF0, 0x08, 0x42, 0x2D, 0xD0, 0x00, 0x25, + 0x2E, 0x46, 0x27, 0xE0, + 0x32, 0x46, 0x01, 0x20, 0x00, 0x21, 0x03, 0xF0, 0x41, 0xFD, 0xDD, 0xE9, + 0x02, 0x23, 0x10, 0x40, + 0x19, 0x40, 0x08, 0x43, 0x1B, 0xD0, 0x11, 0x98, 0x60, 0xB1, 0x04, 0xFB, + 0x07, 0xF0, 0x0A, 0xEB, + 0x05, 0x01, 0x41, 0x56, 0x32, 0x46, 0x01, 0xEB, 0x0B, 0x03, 0x21, 0x46, + 0x0E, 0x98, 0xED, 0xF7, + 0xBC, 0xF9, 0x0B, 0xE0, 0x32, 0x46, 0x21, 0x46, 0x0E, 0x98, 0xED, 0xF7, + 0xAB, 0xF9, 0x04, 0xFB, + 0x07, 0xF1, 0x0A, 0xEB, 0x05, 0x02, 0xA0, 0xEB, 0x0B, 0x00, 0x88, 0x54, + 0x6D, 0x1C, 0x76, 0x1C, + 0xBD, 0x42, 0xD5, 0xDB, 0x64, 0x1C, 0x08, 0xF1, 0x01, 0x00, 0x80, 0x46, + 0x4C, 0x45, 0xC6, 0xDB, + 0xBD, 0xE8, 0xFF, 0x9F, 0x2D, 0xE9, 0xFF, 0x5F, 0x84, 0xB0, 0x04, 0x21, + 0xDD, 0xE9, 0x12, 0xAB, + 0x04, 0xA8, 0xED, 0xF7, 0x98, 0xFD, 0x81, 0x46, 0x08, 0x21, 0x06, 0xA8, + 0xED, 0xF7, 0x93, 0xFD, + 0x07, 0x46, 0x00, 0x25, 0x0A, 0xF1, 0x48, 0x00, 0x02, 0x90, 0x20, 0x30, + 0x01, 0x90, 0x48, 0x38, + 0x2C, 0x46, 0xA8, 0x46, 0x2E, 0x46, 0x00, 0x90, 0x31, 0xE0, 0x01, 0x20, + 0x04, 0x99, 0x00, 0xFA, + 0x08, 0xF0, 0x08, 0x42, 0x27, 0xD0, 0x16, 0x98, 0x90, 0xB1, 0x1B, 0xF8, + 0x05, 0x10, 0x14, 0x98, + 0x08, 0x44, 0xC2, 0xB2, 0x41, 0x46, 0x02, 0x98, 0xED, 0xF7, 0xDF, 0xF9, + 0x05, 0xEB, 0x09, 0x00, + 0xC1, 0x19, 0x1B, 0xF9, 0x01, 0x20, 0x41, 0x46, 0x50, 0x46, 0xED, 0xF7, + 0xAD, 0xF9, 0x10, 0xE0, + 0x41, 0x46, 0x02, 0x98, 0xED, 0xF7, 0xCF, 0xF9, 0x14, 0x99, 0x40, 0x1A, + 0x0B, 0xF8, 0x05, 0x00, + 0x41, 0x46, 0x50, 0x46, 0xED, 0xF7, 0x99, 0xF9, 0x05, 0xEB, 0x09, 0x01, + 0xCA, 0x19, 0x0B, 0xF8, + 0x02, 0x00, 0x6D, 0x1C, 0xED, 0xB2, 0x08, 0xF1, 0x01, 0x00, 0x00, 0xF0, + 0xFF, 0x08, 0x4D, 0x45, + 0xCB, 0xDB, 0x38, 0xE0, 0x32, 0x46, 0x01, 0x20, 0x00, 0x21, 0x03, 0xF0, + 0xBF, 0xFC, 0xDD, 0xE9, + 0x06, 0x23, 0x10, 0x40, 0x19, 0x40, 0x08, 0x43, 0x2B, 0xD0, 0x16, 0x98, + 0xA0, 0xB1, 0x04, 0xEB, + 0x09, 0x01, 0x15, 0x98, 0x1B, 0xF8, 0x01, 0x10, 0x08, 0x44, 0xC2, 0xB2, + 0x31, 0x46, 0x01, 0x98, + 0xED, 0xF7, 0xA3, 0xF9, 0x04, 0xEB, 0x49, 0x00, 0xC1, 0x19, 0x1B, 0xF9, + 0x01, 0x20, 0x31, 0x46, + 0x00, 0x98, 0xED, 0xF7, 0x71, 0xF9, 0x12, 0xE0, 0x31, 0x46, 0x01, 0x98, + 0xED, 0xF7, 0x93, 0xF9, + 0x15, 0x99, 0x04, 0xEB, 0x09, 0x02, 0x40, 0x1A, 0x0B, 0xF8, 0x02, 0x00, + 0x31, 0x46, 0x00, 0x98, + 0xED, 0xF7, 0x5B, 0xF9, 0x04, 0xEB, 0x49, 0x01, 0xCA, 0x19, 0x0B, 0xF8, + 0x02, 0x00, 0x64, 0x1C, + 0xE4, 0xB2, 0x76, 0x1C, 0xF6, 0xB2, 0xBC, 0x42, 0xC4, 0xDB, 0x08, 0xB0, + 0xBD, 0xE8, 0xF0, 0x9F, + 0x2D, 0xE9, 0xFF, 0x5F, 0x04, 0x21, 0xDD, 0xE9, 0x0E, 0xBA, 0x68, 0x46, + 0xED, 0xF7, 0x0B, 0xFD, + 0x81, 0x46, 0x08, 0x21, 0x02, 0xA8, 0xED, 0xF7, 0x06, 0xFD, 0x07, 0x46, + 0x00, 0x24, 0x20, 0x46, + 0x24, 0xE0, 0x01, 0x20, 0x00, 0x99, 0x00, 0xFA, 0x08, 0xF0, 0x08, 0x42, + 0x1C, 0xD0, 0x00, 0x25, + 0x2E, 0x46, 0x16, 0xE0, 0x32, 0x46, 0x01, 0x20, 0x00, 0x21, 0x03, 0xF0, + 0x67, 0xFC, 0xDD, 0xE9, + 0x02, 0x23, 0x10, 0x40, 0x19, 0x40, 0x08, 0x43, 0x0A, 0xD0, 0x32, 0x46, + 0x21, 0x46, 0x58, 0x46, + 0xED, 0xF7, 0xE0, 0xF8, 0x04, 0xFB, 0x07, 0x52, 0x80, 0x00, 0x2A, 0xF8, + 0x12, 0x00, 0x6D, 0x1C, + 0x76, 0x1C, 0xBD, 0x42, 0xE6, 0xDB, 0x64, 0x1C, 0x08, 0xF1, 0x01, 0x00, + 0x80, 0x46, 0x4C, 0x45, + 0xD7, 0xDB, 0x35, 0xE7, 0x2D, 0xE9, 0xFF, 0x5F, 0x04, 0x21, 0xDD, 0xE9, + 0x0E, 0x9B, 0x68, 0x46, + 0xED, 0xF7, 0xD1, 0xFC, 0x82, 0x46, 0x08, 0x21, 0x02, 0xA8, 0xED, 0xF7, + 0xCC, 0xFC, 0x00, 0x25, + 0x80, 0x46, 0x2C, 0x46, 0x2F, 0x46, 0x2E, 0x46, 0x36, 0xE0, 0x01, 0x20, + 0x00, 0x99, 0xB8, 0x40, + 0x08, 0x42, 0x2F, 0xD0, 0x39, 0x46, 0x09, 0xF1, 0x48, 0x00, 0xED, 0xF7, + 0x24, 0xF9, 0x2E, 0x49, + 0x99, 0xF8, 0x92, 0x30, 0x09, 0x68, 0x91, 0xF8, 0x43, 0xC1, 0x91, 0xF8, + 0x42, 0x11, 0x0C, 0xF0, + 0x0F, 0x02, 0x52, 0x1C, 0x53, 0x43, 0x01, 0x22, 0x02, 0xEB, 0x1C, 0x1C, + 0x00, 0xFB, 0x0C, 0x33, + 0x99, 0xF8, 0x90, 0x00, 0x02, 0xEB, 0x11, 0x11, 0x00, 0xFB, 0x01, 0x30, + 0x10, 0x99, 0x19, 0xB9, + 0x4F, 0xF6, 0xFF, 0x71, 0x01, 0xEA, 0x80, 0x00, 0x2B, 0xF8, 0x15, 0x00, + 0x39, 0x46, 0x48, 0x46, + 0xED, 0xF7, 0xD3, 0xF8, 0x05, 0xEB, 0x0A, 0x02, 0x80, 0x00, 0x42, 0x44, + 0x6D, 0x1C, 0x2B, 0xF8, + 0x12, 0x00, 0xED, 0xB2, 0x7F, 0x1C, 0xFF, 0xB2, 0x55, 0x45, 0xC6, 0xDB, + 0x45, 0xE0, 0x32, 0x46, + 0x01, 0x20, 0x00, 0x21, 0x03, 0xF0, 0xFA, 0xFB, 0xDD, 0xE9, 0x02, 0x23, + 0x10, 0x40, 0x19, 0x40, + 0x08, 0x43, 0x38, 0xD0, 0x31, 0x46, 0x09, 0xF1, 0x68, 0x00, 0xED, 0xF7, + 0xE4, 0xF8, 0x0E, 0x49, + 0x99, 0xF8, 0x93, 0x50, 0x09, 0x68, 0x91, 0xF8, 0x48, 0x21, 0x91, 0xF8, + 0x47, 0x11, 0x02, 0xF0, + 0x0F, 0x03, 0x5B, 0x1C, 0x5D, 0x43, 0x01, 0x23, 0x03, 0xEB, 0x12, 0x12, + 0x00, 0xFB, 0x02, 0x52, + 0x99, 0xF8, 0x91, 0x00, 0x03, 0xEB, 0x11, 0x11, 0x00, 0xFB, 0x01, 0x20, + 0x10, 0x99, 0x49, 0xB9, + 0x4F, 0xF6, 0xFF, 0x71, 0x04, 0xE0, 0x00, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0xB0, 0x07, 0x10, 0x00, + 0x01, 0xEA, 0x80, 0x00, 0x04, 0xEB, 0x0A, 0x02, 0x31, 0x46, 0x2B, 0xF8, + 0x12, 0x00, 0x09, 0xF1, + 0x20, 0x00, 0xED, 0xF7, 0x8A, 0xF8, 0x04, 0xEB, 0x4A, 0x02, 0x80, 0x00, + 0x42, 0x44, 0x64, 0x1C, + 0x2B, 0xF8, 0x12, 0x00, 0xE4, 0xB2, 0x76, 0x1C, 0xF6, 0xB2, 0x44, 0x45, + 0xB7, 0xDB, 0x9F, 0xE6, + 0x2D, 0xE9, 0xF0, 0x43, 0x89, 0x46, 0x00, 0x27, 0x85, 0xB0, 0x90, 0x46, + 0x04, 0x46, 0x3E, 0x46, + 0xFC, 0x21, 0x03, 0xF0, 0x61, 0xFC, 0xFE, 0x4D, 0xB8, 0xF1, 0x01, 0x0F, + 0x28, 0x79, 0x20, 0x71, + 0x28, 0x7A, 0x60, 0x71, 0xFB, 0x48, 0x90, 0xF8, 0x52, 0x1E, 0xA1, 0x71, + 0x90, 0xF8, 0x53, 0x1E, + 0xE1, 0x71, 0x90, 0xF8, 0x54, 0x1E, 0xA1, 0x72, 0x90, 0xF8, 0x55, 0x1E, + 0xE1, 0x72, 0x90, 0xF8, + 0x50, 0x1E, 0x21, 0x73, 0x90, 0xF8, 0x51, 0x0E, 0x60, 0x73, 0x08, 0xD1, + 0xF2, 0x48, 0x00, 0x68, + 0x81, 0x7F, 0xA5, 0x29, 0x03, 0xD1, 0x90, 0xF9, 0x1F, 0x70, 0x90, 0xF9, + 0x20, 0x60, 0x04, 0xF1, + 0x10, 0x00, 0xCD, 0xE9, 0x01, 0x07, 0xED, 0x48, 0x00, 0x21, 0x00, 0x90, + 0xCD, 0xE9, 0x03, 0x61, + 0xD5, 0xE9, 0x04, 0x23, 0x28, 0x68, 0xFF, 0xF7, 0x65, 0xFE, 0xA5, 0x20, + 0x20, 0x70, 0x12, 0x20, + 0x60, 0x70, 0xA4, 0xF8, 0x02, 0x90, 0xD5, 0xE9, 0x01, 0x01, 0x08, 0x44, + 0x13, 0x30, 0x80, 0x08, + 0x05, 0xB0, 0xBD, 0xE8, 0xF0, 0x83, 0x2D, 0xE9, 0xF0, 0x43, 0x89, 0x46, + 0x00, 0x27, 0x85, 0xB0, + 0x90, 0x46, 0x04, 0x46, 0x3E, 0x46, 0xFC, 0x21, 0x03, 0xF0, 0x16, 0xFC, + 0xD8, 0x4D, 0xB8, 0xF1, + 0x01, 0x0F, 0x28, 0x79, 0x20, 0x71, 0x28, 0x7A, 0x60, 0x71, 0xD6, 0x48, + 0x90, 0xF8, 0xEA, 0x1E, + 0xA1, 0x71, 0x90, 0xF8, 0xEB, 0x1E, 0xE1, 0x71, 0x90, 0xF8, 0xEC, 0x1E, + 0xA1, 0x72, 0x90, 0xF8, + 0xED, 0x1E, 0xE1, 0x72, 0x90, 0xF8, 0xE8, 0x1E, 0x21, 0x73, 0x90, 0xF8, + 0xE9, 0x0E, 0x60, 0x73, + 0x09, 0xD1, 0xCD, 0x48, 0x00, 0x68, 0x90, 0xF8, 0x21, 0x10, 0xA5, 0x29, + 0x03, 0xD1, 0x10, 0xF9, + 0x22, 0x7F, 0x90, 0xF9, 0x01, 0x60, 0x04, 0xF1, 0x10, 0x00, 0xCD, 0xE9, + 0x01, 0x07, 0xC7, 0x48, + 0x00, 0x21, 0x98, 0x30, 0x00, 0x90, 0xCD, 0xE9, 0x03, 0x61, 0xD5, 0xE9, + 0x04, 0x23, 0x28, 0x68, + 0xFF, 0xF7, 0x18, 0xFE, 0xA5, 0x20, 0x20, 0x70, 0x13, 0x20, 0x60, 0x70, + 0xA4, 0xF8, 0x02, 0x90, + 0xD5, 0xE9, 0x01, 0x01, 0x08, 0x44, 0x13, 0x30, 0x80, 0x08, 0xB1, 0xE7, + 0x2D, 0xE9, 0xF0, 0x43, + 0x89, 0x46, 0x00, 0x27, 0x85, 0xB0, 0x90, 0x46, 0x04, 0x46, 0x3E, 0x46, + 0xFC, 0x21, 0x03, 0xF0, + 0xCB, 0xFB, 0xB3, 0x4D, 0xB8, 0xF1, 0x01, 0x0F, 0x28, 0x79, 0x20, 0x71, + 0x28, 0x7A, 0x60, 0x71, + 0xB0, 0x48, 0x90, 0xF8, 0x82, 0x1F, 0xA1, 0x71, 0x90, 0xF8, 0x83, 0x1F, + 0xE1, 0x71, 0x90, 0xF8, + 0x84, 0x1F, 0xA1, 0x72, 0x90, 0xF8, 0x85, 0x1F, 0xE1, 0x72, 0x90, 0xF8, + 0x80, 0x1F, 0x21, 0x73, + 0x90, 0xF8, 0x81, 0x0F, 0x60, 0x73, 0x09, 0xD1, 0xA7, 0x48, 0x00, 0x68, + 0x90, 0xF8, 0x26, 0x10, + 0xA5, 0x29, 0x03, 0xD1, 0x10, 0xF9, 0x27, 0x7F, 0x90, 0xF9, 0x01, 0x60, + 0x04, 0xF1, 0x10, 0x00, + 0xCD, 0xE9, 0x01, 0x07, 0xA2, 0x48, 0x00, 0x21, 0x00, 0x90, 0xCD, 0xE9, + 0x03, 0x61, 0xD5, 0xE9, + 0x04, 0x23, 0x28, 0x68, 0xFF, 0xF7, 0xCE, 0xFD, 0xA5, 0x20, 0x20, 0x70, + 0x17, 0x20, 0x60, 0x70, + 0xA4, 0xF8, 0x02, 0x90, 0xD5, 0xE9, 0x01, 0x01, 0x08, 0x44, 0x13, 0x30, + 0x80, 0x08, 0x67, 0xE7, + 0x2D, 0xE9, 0xFF, 0x41, 0x0D, 0x46, 0x07, 0x46, 0x00, 0x26, 0xFF, 0xF7, + 0x21, 0xFD, 0x01, 0x22, + 0x8F, 0x4C, 0x91, 0x49, 0x10, 0x3F, 0x05, 0xF1, 0x10, 0x00, 0x08, 0x2F, + 0x4E, 0xD2, 0xDF, 0xE8, + 0x07, 0xF0, 0x04, 0x15, 0x26, 0x2A, 0x2F, 0x4D, 0x4D, 0x51, 0x2B, 0x79, + 0x66, 0x68, 0xB3, 0x42, + 0x2E, 0xD1, 0x6B, 0x79, 0xA5, 0x68, 0xAB, 0x42, 0x2A, 0xD1, 0x09, 0x68, + 0x00, 0x23, 0x8D, 0x7E, + 0xA5, 0x2D, 0x01, 0xD1, 0x91, 0xF9, 0x1B, 0x30, 0x82, 0x49, 0x2D, 0xE0, + 0x2B, 0x79, 0x66, 0x68, + 0xB3, 0x42, 0x1D, 0xD1, 0x6B, 0x79, 0xA5, 0x68, 0xAB, 0x42, 0x19, 0xD1, + 0x09, 0x68, 0x00, 0x23, + 0x0D, 0x7F, 0xA5, 0x2D, 0x01, 0xD1, 0x91, 0xF9, 0x1D, 0x30, 0x7E, 0x49, + 0x1C, 0xE0, 0x28, 0x46, + 0x00, 0xF0, 0x12, 0xF9, 0x02, 0xE0, 0x28, 0x46, 0x00, 0xF0, 0x49, 0xF9, + 0x06, 0x46, 0x1D, 0xE0, + 0x2B, 0x79, 0x66, 0x68, 0xB3, 0x42, 0x03, 0xD1, 0x6B, 0x79, 0xA5, 0x68, + 0xAB, 0x42, 0x01, 0xD0, + 0x00, 0x26, 0x13, 0xE0, 0x09, 0x68, 0x00, 0x23, 0x91, 0xF8, 0x24, 0x50, + 0xA5, 0x2D, 0x01, 0xD1, + 0x91, 0xF9, 0x25, 0x30, 0x6D, 0x49, 0xA0, 0x39, 0xCD, 0xE9, 0x00, 0x10, + 0xCD, 0xE9, 0x02, 0x32, + 0xD4, 0xE9, 0x04, 0x23, 0x20, 0x68, 0xFF, 0xF7, 0x19, 0xFD, 0x01, 0x26, + 0x30, 0x46, 0x04, 0xB0, + 0xBD, 0xE8, 0xF0, 0x81, 0x28, 0x46, 0x00, 0xF0, 0x5E, 0xF9, 0xD7, 0xE7, + 0x2D, 0xE9, 0xF0, 0x41, + 0x1E, 0x46, 0x17, 0x46, 0x88, 0x46, 0x05, 0x46, 0x00, 0x24, 0xFF, 0xF7, + 0xB9, 0xFC, 0x10, 0x3D, + 0x08, 0x2D, 0x11, 0xD2, 0xDF, 0xE8, 0x05, 0xF0, 0x04, 0x0A, 0x12, 0x18, + 0x1E, 0x10, 0x10, 0x24, + 0x32, 0x46, 0x39, 0x46, 0x40, 0x46, 0x00, 0xF0, 0x81, 0xF9, 0x04, 0xE0, + 0x32, 0x46, 0x39, 0x46, + 0x40, 0x46, 0x00, 0xF0, 0xAC, 0xF9, 0x04, 0x46, 0x20, 0x46, 0xD9, 0xE7, + 0x32, 0x46, 0x39, 0x46, + 0x40, 0x46, 0xFF, 0xF7, 0x95, 0xFE, 0xF6, 0xE7, 0x32, 0x46, 0x39, 0x46, + 0x40, 0x46, 0xFF, 0xF7, + 0xDA, 0xFE, 0xF0, 0xE7, 0x32, 0x46, 0x39, 0x46, 0x40, 0x46, 0x00, 0xF0, + 0xC9, 0xF9, 0xEA, 0xE7, + 0x32, 0x46, 0x39, 0x46, 0x40, 0x46, 0xFF, 0xF7, 0x19, 0xFF, 0xE4, 0xE7, + 0xFE, 0xB5, 0x17, 0x46, + 0x06, 0x46, 0x0D, 0x46, 0xFF, 0xF7, 0x84, 0xFC, 0x41, 0x48, 0x47, 0x4A, + 0x05, 0xF1, 0x10, 0x04, + 0x01, 0x79, 0x29, 0x71, 0x01, 0x7A, 0x69, 0x71, 0xA6, 0xF1, 0x50, 0x01, + 0x08, 0x29, 0x26, 0xD2, + 0xDF, 0xE8, 0x01, 0xF0, 0x04, 0x06, 0x0F, 0x16, 0x2A, 0x25, 0x25, 0x2D, + 0x39, 0x49, 0x00, 0xE0, + 0x3C, 0x49, 0xCD, 0xE9, 0x00, 0x14, 0xD0, 0xE9, 0x04, 0x23, 0x00, 0x68, + 0xFF, 0xF7, 0x90, 0xFD, + 0x15, 0xE0, 0x11, 0x68, 0x35, 0x4A, 0x91, 0xF8, 0x42, 0x11, 0x01, 0xF0, + 0x01, 0x01, 0x06, 0xE0, + 0x11, 0x68, 0x32, 0x4A, 0x91, 0xF8, 0x92, 0x19, 0x98, 0x32, 0x01, 0xF0, + 0x01, 0x01, 0x00, 0x92, + 0xCD, 0xE9, 0x01, 0x41, 0xD0, 0xE9, 0x04, 0x23, 0x00, 0x68, 0xFF, 0xF7, + 0xB3, 0xFD, 0xA5, 0x20, + 0x28, 0x70, 0x6E, 0x70, 0x6F, 0x80, 0xFE, 0xBD, 0x28, 0x49, 0xA0, 0x39, + 0xD9, 0xE7, 0x11, 0x68, + 0x27, 0x4A, 0x91, 0xF8, 0x12, 0x1A, 0x01, 0xF0, 0x01, 0x01, 0xE8, 0xE7, + 0x2D, 0xE9, 0xF0, 0x41, + 0x0C, 0x46, 0x05, 0x46, 0x4F, 0xF4, 0x29, 0x71, 0x24, 0x48, 0x03, 0xF0, + 0xBF, 0xFA, 0x23, 0x48, + 0x00, 0x2C, 0x04, 0x60, 0x57, 0xD1, 0x80, 0x46, 0x04, 0xEB, 0x84, 0x00, + 0xC4, 0xEB, 0xC4, 0x01, + 0x05, 0xEB, 0xC0, 0x06, 0x08, 0xEB, 0x01, 0x17, 0x38, 0x1D, 0x28, 0x22, + 0x31, 0x46, 0x03, 0xF0, + 0x0C, 0xFA, 0xC4, 0xEB, 0x04, 0x10, 0x05, 0xEB, 0x80, 0x01, 0x07, 0xF1, + 0x2C, 0x00, 0x20, 0x22, + 0x78, 0x31, 0x03, 0xF0, 0x02, 0xFA, 0x06, 0xF5, 0x96, 0x71, 0x07, 0xF1, + 0x4C, 0x00, 0x28, 0x22, + 0x03, 0xF0, 0xFB, 0xF9, 0x64, 0x1C, 0x03, 0x2C, 0xDE, 0xDB, 0x00, 0x24, + 0x04, 0xEB, 0x84, 0x00, + 0xC4, 0xEB, 0xC4, 0x02, 0x05, 0xEB, 0xC0, 0x06, 0x08, 0xEB, 0x02, 0x17, + 0x06, 0xF5, 0xD2, 0x71, + 0x07, 0xF5, 0xAA, 0x70, 0x28, 0x22, 0x03, 0xF0, 0xE8, 0xF9, 0xC4, 0xEB, + 0x04, 0x10, 0x0F, 0xE0, + 0xB0, 0x07, 0x10, 0x00, 0x78, 0x8B, 0x01, 0x20, 0x50, 0x07, 0x10, 0x00, + 0x38, 0x99, 0x01, 0x20, + 0x68, 0x9A, 0x01, 0x20, 0x08, 0x92, 0x01, 0x20, 0x4C, 0x07, 0x10, 0x00, + 0x40, 0x9D, 0x01, 0x20, + 0x05, 0xEB, 0x80, 0x01, 0x07, 0xF5, 0xBE, 0x70, 0x20, 0x22, 0x01, 0xF5, + 0x07, 0x71, 0x03, 0xF0, + 0xCC, 0xF9, 0x06, 0xF5, 0x34, 0x71, 0x07, 0xF5, 0xCE, 0x70, 0x28, 0x22, + 0x03, 0xF0, 0xC5, 0xF9, + 0x64, 0x1C, 0x03, 0x2C, 0xCA, 0xDB, 0x13, 0xE7, 0x30, 0xB5, 0xA3, 0x49, + 0x02, 0x79, 0x85, 0xB0, + 0x4B, 0x68, 0x9A, 0x42, 0x03, 0xD1, 0x42, 0x79, 0x8B, 0x68, 0x9A, 0x42, + 0x02, 0xD0, 0x00, 0x20, + 0x05, 0xB0, 0x30, 0xBD, 0x9D, 0x4A, 0x85, 0x79, 0x00, 0x24, 0x82, 0xF8, + 0x52, 0x5E, 0xC5, 0x79, + 0x82, 0xF8, 0x53, 0x5E, 0x85, 0x7A, 0x82, 0xF8, 0x54, 0x5E, 0xC5, 0x7A, + 0x82, 0xF8, 0x55, 0x5E, + 0x05, 0x7B, 0x82, 0xF8, 0x50, 0x5E, 0x45, 0x7B, 0x82, 0xF8, 0x51, 0x5E, + 0x94, 0x4A, 0x23, 0x46, + 0x12, 0x68, 0x95, 0x7F, 0xA5, 0x2D, 0x03, 0xD1, 0x92, 0xF9, 0x1F, 0x40, + 0x92, 0xF9, 0x20, 0x30, + 0x10, 0x30, 0xCD, 0xE9, 0x01, 0x04, 0x8F, 0x48, 0x01, 0x22, 0x00, 0x90, + 0xCD, 0xE9, 0x03, 0x32, + 0xD1, 0xE9, 0x04, 0x23, 0x08, 0x68, 0xFF, 0xF7, 0x3D, 0xFC, 0x01, 0x20, + 0xD0, 0xE7, 0x30, 0xB5, + 0x85, 0x49, 0x02, 0x79, 0x85, 0xB0, 0x4B, 0x68, 0x9A, 0x42, 0x03, 0xD1, + 0x42, 0x79, 0x8B, 0x68, + 0x9A, 0x42, 0x01, 0xD0, 0x00, 0x20, 0xC3, 0xE7, 0x80, 0x4A, 0x85, 0x79, + 0x00, 0x24, 0x82, 0xF8, + 0xEA, 0x5E, 0xC5, 0x79, 0x82, 0xF8, 0xEB, 0x5E, 0x85, 0x7A, 0x82, 0xF8, + 0xEC, 0x5E, 0xC5, 0x7A, + 0x82, 0xF8, 0xED, 0x5E, 0x05, 0x7B, 0x82, 0xF8, 0xE8, 0x5E, 0x45, 0x7B, + 0x82, 0xF8, 0xE9, 0x5E, + 0x77, 0x4A, 0x23, 0x46, 0x12, 0x68, 0x92, 0xF8, 0x21, 0x50, 0xA5, 0x2D, + 0x03, 0xD1, 0x12, 0xF9, + 0x22, 0x4F, 0x92, 0xF9, 0x01, 0x30, 0x10, 0x30, 0xCD, 0xE9, 0x01, 0x04, + 0x71, 0x48, 0x01, 0x22, + 0x98, 0x30, 0x00, 0x90, 0xCD, 0xE9, 0x03, 0x32, 0xD1, 0xE9, 0x04, 0x23, + 0x08, 0x68, 0xFF, 0xF7, + 0x01, 0xFC, 0x01, 0x20, 0x94, 0xE7, 0x30, 0xB5, 0x67, 0x49, 0x02, 0x79, + 0x85, 0xB0, 0x4B, 0x68, + 0x9A, 0x42, 0x03, 0xD1, 0x42, 0x79, 0x8B, 0x68, 0x9A, 0x42, 0x01, 0xD0, + 0x00, 0x20, 0x87, 0xE7, + 0x62, 0x4A, 0x85, 0x79, 0x00, 0x24, 0x82, 0xF8, 0x82, 0x5F, 0xC5, 0x79, + 0x82, 0xF8, 0x83, 0x5F, + 0x85, 0x7A, 0x82, 0xF8, 0x84, 0x5F, 0xC5, 0x7A, 0x82, 0xF8, 0x85, 0x5F, + 0x05, 0x7B, 0x82, 0xF8, + 0x80, 0x5F, 0x45, 0x7B, 0x82, 0xF8, 0x81, 0x5F, 0x59, 0x4A, 0x23, 0x46, + 0x12, 0x68, 0x92, 0xF8, + 0x26, 0x50, 0xA5, 0x2D, 0x03, 0xD1, 0x12, 0xF9, 0x27, 0x4F, 0x92, 0xF9, + 0x01, 0x30, 0x10, 0x30, + 0xCD, 0xE9, 0x01, 0x04, 0x54, 0x48, 0x01, 0x22, 0x00, 0x90, 0xCD, 0xE9, + 0x03, 0x32, 0xD1, 0xE9, + 0x04, 0x23, 0x08, 0x68, 0xFF, 0xF7, 0xC6, 0xFB, 0x01, 0x20, 0x59, 0xE7, + 0x2D, 0xE9, 0xFF, 0x41, + 0x0F, 0x46, 0x15, 0x46, 0x04, 0x46, 0x00, 0x26, 0x40, 0xF2, 0x7A, 0x31, + 0x03, 0xF0, 0x84, 0xF9, + 0x45, 0x48, 0x01, 0x2D, 0x01, 0x79, 0x21, 0x71, 0x01, 0x7A, 0x61, 0x71, + 0x06, 0xD1, 0x44, 0x49, + 0x09, 0x68, 0x8A, 0x7E, 0xA5, 0x2A, 0x01, 0xD1, 0x91, 0xF9, 0x1B, 0x60, + 0x00, 0x22, 0x04, 0xF1, + 0x10, 0x01, 0x3E, 0x4B, 0xCD, 0xE9, 0x00, 0x31, 0xCD, 0xE9, 0x02, 0x62, + 0x05, 0x46, 0x00, 0x68, + 0xD5, 0xE9, 0x04, 0x23, 0xFF, 0xF7, 0x52, 0xFB, 0xA5, 0x20, 0x20, 0x70, + 0x10, 0x20, 0x60, 0x70, + 0x67, 0x80, 0xD5, 0xE9, 0x01, 0x01, 0x48, 0x43, 0x13, 0x30, 0x80, 0x08, + 0x2F, 0xE6, 0x2D, 0xE9, + 0xFF, 0x41, 0x0F, 0x46, 0x15, 0x46, 0x04, 0x46, 0x00, 0x26, 0x40, 0xF2, + 0x7A, 0x31, 0x03, 0xF0, + 0x53, 0xF9, 0x2D, 0x48, 0x01, 0x2D, 0x01, 0x79, 0x21, 0x71, 0x01, 0x7A, + 0x61, 0x71, 0x06, 0xD1, + 0x2B, 0x49, 0x09, 0x68, 0x0A, 0x7F, 0xA5, 0x2A, 0x01, 0xD1, 0x91, 0xF9, + 0x1D, 0x60, 0x00, 0x23, + 0x04, 0xF1, 0x10, 0x01, 0x29, 0x4A, 0xCD, 0xE9, 0x00, 0x21, 0xCD, 0xE9, + 0x02, 0x63, 0x05, 0x46, + 0x00, 0x68, 0xD5, 0xE9, 0x04, 0x23, 0xFF, 0xF7, 0x21, 0xFB, 0xA5, 0x20, + 0x20, 0x70, 0x11, 0x20, + 0x60, 0x70, 0x67, 0x80, 0xD5, 0xE9, 0x01, 0x01, 0x48, 0x43, 0x13, 0x30, + 0x80, 0x08, 0xFE, 0xE5, + 0x2D, 0xE9, 0xFF, 0x41, 0x0F, 0x46, 0x15, 0x46, 0x04, 0x46, 0x00, 0x26, + 0x40, 0xF2, 0x7A, 0x31, + 0x03, 0xF0, 0x22, 0xF9, 0x14, 0x48, 0x01, 0x2D, 0x01, 0x79, 0x21, 0x71, + 0x01, 0x7A, 0x61, 0x71, + 0x07, 0xD1, 0x13, 0x49, 0x09, 0x68, 0x91, 0xF8, 0x24, 0x20, 0xA5, 0x2A, + 0x01, 0xD1, 0x91, 0xF9, + 0x25, 0x60, 0x10, 0x4A, 0x00, 0x23, 0x04, 0xF1, 0x10, 0x01, 0xA0, 0x3A, + 0xCD, 0xE9, 0x00, 0x21, + 0xCD, 0xE9, 0x02, 0x63, 0x05, 0x46, 0x00, 0x68, 0xD5, 0xE9, 0x04, 0x23, + 0xFF, 0xF7, 0xEE, 0xFA, + 0xA5, 0x20, 0x20, 0x70, 0x14, 0x20, 0x60, 0x70, 0x67, 0x80, 0xD5, 0xE9, + 0x01, 0x01, 0x48, 0x43, + 0x13, 0x30, 0x80, 0x08, 0xCB, 0xE5, 0x00, 0x00, 0xB0, 0x07, 0x10, 0x00, + 0x78, 0x8B, 0x01, 0x20, + 0x50, 0x07, 0x10, 0x00, 0x38, 0x99, 0x01, 0x20, 0x68, 0x9A, 0x01, 0x20, + 0x08, 0x92, 0x01, 0x20, + 0x2D, 0xE9, 0xF0, 0x4F, 0x47, 0xF6, 0xFF, 0x77, 0xDD, 0xE9, 0x09, 0xB5, + 0x81, 0x46, 0xFE, 0x43, + 0x01, 0x20, 0x9A, 0x46, 0x84, 0x46, 0x2C, 0x46, 0x15, 0xE0, 0x00, 0xBF, + 0x32, 0xF8, 0x14, 0x30, + 0x31, 0xF8, 0x14, 0x80, 0xA3, 0xEB, 0x08, 0x03, 0x1B, 0xB2, 0xBB, 0x42, + 0x00, 0xDA, 0x1F, 0x46, + 0xB3, 0x42, 0x00, 0xDD, 0x1E, 0x46, 0x00, 0x2B, 0x02, 0xDD, 0x4F, 0xF0, + 0x00, 0x0C, 0x02, 0xE0, + 0x01, 0xDA, 0x4F, 0xF0, 0x00, 0x00, 0x64, 0x1E, 0xE8, 0xD2, 0x20, 0xB1, + 0xBC, 0xF1, 0x00, 0x0F, + 0x01, 0xD0, 0x00, 0x23, 0x09, 0xE0, 0x50, 0xEA, 0x0C, 0x03, 0x06, 0xD0, + 0x20, 0xB9, 0xBC, 0xF1, + 0x00, 0x0F, 0x01, 0xD0, 0x33, 0x46, 0x00, 0xE0, 0x3B, 0x46, 0x00, 0x27, + 0x3C, 0x46, 0x28, 0x46, + 0xCB, 0xF1, 0x00, 0x0C, 0x0E, 0xE0, 0x00, 0xBF, 0x32, 0xF8, 0x10, 0x60, + 0x31, 0xF8, 0x10, 0x80, + 0xA6, 0xEB, 0x08, 0x06, 0xF6, 0x1A, 0x36, 0xB2, 0x56, 0x45, 0x03, 0xDA, + 0x66, 0x45, 0x01, 0xDD, + 0x37, 0x44, 0x64, 0x1C, 0x40, 0x1E, 0xEF, 0xD2, 0x04, 0xB9, 0x01, 0x24, + 0x97, 0xFB, 0xF4, 0xF0, + 0x18, 0x44, 0x04, 0xE0, 0x31, 0xF8, 0x15, 0x20, 0x02, 0x44, 0x29, 0xF8, + 0x15, 0x20, 0x6D, 0x1E, + 0xF8, 0xD2, 0xBD, 0xE8, 0xF0, 0x8F, 0xF0, 0xB5, 0x00, 0x23, 0x06, 0x46, + 0x1D, 0x46, 0x18, 0x46, + 0x0E, 0xE0, 0x00, 0xBF, 0x36, 0xF9, 0x11, 0x40, 0x00, 0x2C, 0x01, 0xDB, + 0x27, 0x46, 0x00, 0xE0, + 0x67, 0x42, 0x97, 0x42, 0x04, 0xDD, 0x00, 0x2C, 0x01, 0xDD, 0x25, 0x44, + 0x00, 0xE0, 0x23, 0x44, + 0x49, 0x1E, 0xEF, 0xD2, 0x00, 0x2B, 0x00, 0xDA, 0x5B, 0x42, 0xAB, 0x42, + 0x00, 0xDD, 0x01, 0x20, + 0xF0, 0xBD, 0x30, 0xB5, 0x07, 0xE0, 0x00, 0xBF, 0x32, 0xF8, 0x13, 0x40, + 0x31, 0xF8, 0x13, 0x50, + 0x64, 0x1B, 0x20, 0xF8, 0x13, 0x40, 0x5B, 0x1E, 0xF6, 0xD2, 0x30, 0xBD, + 0xF0, 0xB4, 0x04, 0x9C, + 0x1C, 0xB9, 0x13, 0xB1, 0xC3, 0xF1, 0x10, 0x04, 0x0E, 0xE0, 0xF0, 0xBC, + 0x52, 0x00, 0x02, 0xF0, + 0xDC, 0xBF, 0x00, 0xBF, 0x30, 0xF9, 0x12, 0x50, 0x31, 0xF9, 0x12, 0x60, + 0x5D, 0x43, 0x06, 0xFB, + 0x04, 0x55, 0x2D, 0x11, 0x20, 0xF8, 0x12, 0x50, 0x52, 0x1E, 0xF3, 0xD2, + 0xF0, 0xBC, 0x70, 0x47, + 0xF0, 0xB4, 0x04, 0x9C, 0x1C, 0xB9, 0x13, 0xB1, 0xC3, 0xF1, 0x80, 0x04, + 0x0E, 0xE0, 0xF0, 0xBC, + 0x52, 0x00, 0x02, 0xF0, 0xC2, 0xBF, 0x00, 0xBF, 0x30, 0xF9, 0x12, 0x50, + 0x31, 0xF9, 0x12, 0x60, + 0x5D, 0x43, 0x06, 0xFB, 0x04, 0x55, 0xED, 0x11, 0x20, 0xF8, 0x12, 0x50, + 0x52, 0x1E, 0xF3, 0xD2, + 0xE4, 0xE7, 0xF0, 0xB5, 0x00, 0x2B, 0x19, 0xD0, 0x14, 0x46, 0x15, 0xE0, + 0x00, 0x27, 0x3E, 0x46, + 0xE5, 0x1A, 0x04, 0xEB, 0x03, 0x0C, 0x08, 0xE0, 0x00, 0x2D, 0x05, 0xDB, + 0x95, 0x42, 0x03, 0xDA, + 0x31, 0xF9, 0x15, 0xE0, 0x76, 0x1C, 0x77, 0x44, 0x6D, 0x1C, 0xAC, 0x45, + 0xF4, 0xDA, 0x1E, 0xB1, + 0x97, 0xFB, 0xF6, 0xF5, 0x20, 0xF8, 0x14, 0x50, 0x64, 0x1E, 0xE7, 0xD2, + 0xF0, 0xBD, 0x70, 0xB5, + 0x0C, 0x46, 0x15, 0x46, 0xA0, 0x42, 0x04, 0xD0, 0x01, 0x46, 0x6A, 0x00, + 0x20, 0x46, 0x02, 0xF0, + 0x8C, 0xFF, 0x00, 0x20, 0x17, 0xE0, 0x01, 0x46, 0x42, 0x1C, 0x07, 0xE0, + 0x34, 0xF9, 0x12, 0x60, + 0x34, 0xF9, 0x11, 0x30, 0x9E, 0x42, 0x00, 0xDA, 0x11, 0x46, 0x52, 0x1C, + 0xAA, 0x42, 0xF5, 0xD3, + 0x88, 0x42, 0x07, 0xD0, 0x34, 0xF8, 0x10, 0x20, 0x34, 0xF8, 0x11, 0x30, + 0x24, 0xF8, 0x10, 0x30, + 0x24, 0xF8, 0x11, 0x20, 0x40, 0x1C, 0xB0, 0xEB, 0x55, 0x0F, 0xE4, 0xD9, + 0xE9, 0x07, 0x25, 0xF0, + 0x01, 0x00, 0x01, 0xD0, 0x20, 0x5E, 0x70, 0xBD, 0x21, 0x5E, 0x20, 0x44, + 0x30, 0xF9, 0x02, 0x0C, + 0x08, 0x44, 0x40, 0x10, 0x70, 0xBD, 0x03, 0x46, 0x00, 0x20, 0x04, 0xE0, + 0x33, 0xF9, 0x11, 0x20, + 0x82, 0x42, 0x00, 0xDD, 0x10, 0x46, 0x49, 0x1E, 0xF8, 0xD2, 0x70, 0x47, + 0x03, 0x46, 0x47, 0xF6, + 0xFF, 0x70, 0x04, 0xE0, 0x33, 0xF9, 0x11, 0x20, 0x82, 0x42, 0x00, 0xDA, + 0x10, 0x46, 0x49, 0x1E, + 0xF8, 0xD2, 0x00, 0x28, 0x00, 0xDA, 0x00, 0x20, 0x70, 0x47, 0x02, 0x46, + 0xB0, 0xF9, 0x00, 0x00, + 0x05, 0xE0, 0x00, 0xBF, 0x32, 0xF9, 0x11, 0x30, 0x83, 0x42, 0x00, 0xDD, + 0x18, 0x46, 0x49, 0x1E, + 0xF8, 0xD2, 0x70, 0x47, 0x70, 0xB5, 0x05, 0x00, 0x03, 0xD0, 0xBD, 0xE8, + 0x70, 0x40, 0x02, 0xF0, + 0xE4, 0xBB, 0xFE, 0x4C, 0x21, 0x46, 0x60, 0x78, 0x09, 0x79, 0x30, 0xB1, + 0x0A, 0x46, 0xFC, 0x49, + 0x01, 0xF1, 0x98, 0x00, 0x02, 0xF0, 0x04, 0xFC, 0x0B, 0xE0, 0x20, 0x78, + 0x02, 0xF0, 0x42, 0xFD, + 0x21, 0x79, 0x4A, 0x00, 0xF6, 0x49, 0x01, 0xEB, 0x40, 0x01, 0xF5, 0x48, + 0x98, 0x30, 0x02, 0xF0, + 0x1C, 0xFF, 0xA0, 0x78, 0xF3, 0x4E, 0x28, 0xB1, 0xF1, 0x48, 0x21, 0x79, + 0x98, 0x30, 0x02, 0xF0, + 0x2F, 0xFD, 0x01, 0xE0, 0xEE, 0x48, 0x98, 0x30, 0xB0, 0x62, 0xE0, 0x78, + 0x86, 0xF8, 0x31, 0x00, + 0x28, 0x46, 0xD2, 0xE7, 0x2D, 0xE9, 0xF0, 0x47, 0xDF, 0xF8, 0xAC, 0xA3, + 0x01, 0x27, 0xB8, 0x46, + 0xDA, 0xF8, 0x00, 0x00, 0x00, 0xF2, 0x5E, 0x64, 0x90, 0xF8, 0xA6, 0x19, + 0x90, 0xF8, 0xA2, 0x59, + 0x01, 0xF0, 0x1F, 0x06, 0x90, 0xF8, 0xA3, 0x19, 0x01, 0xF0, 0x0F, 0x09, + 0x90, 0xF8, 0x82, 0x19, + 0x89, 0x06, 0x09, 0xD5, 0x90, 0xF8, 0x58, 0x16, 0x90, 0xF8, 0x59, 0x06, + 0x0A, 0x22, 0x04, 0xEB, + 0x40, 0x00, 0x02, 0xF0, 0x09, 0xFC, 0x07, 0x46, 0xDA, 0xF8, 0x00, 0x00, + 0x90, 0xF8, 0x82, 0x19, + 0x49, 0x06, 0x09, 0xD5, 0x90, 0xF8, 0x68, 0x18, 0x90, 0xF8, 0x69, 0x08, + 0x0A, 0x22, 0x04, 0xEB, + 0x40, 0x00, 0x02, 0xF0, 0xF9, 0xFB, 0x80, 0x46, 0x4F, 0xF4, 0x80, 0x70, + 0x85, 0x42, 0x00, 0xD9, + 0x05, 0x46, 0x1F, 0x2E, 0x00, 0xD9, 0x1F, 0x26, 0x76, 0x1C, 0x75, 0x43, + 0x09, 0xF1, 0x01, 0x00, + 0x45, 0x43, 0xC5, 0xEB, 0x45, 0x20, 0x78, 0x43, 0x00, 0xFB, 0x08, 0xF0, + 0xC0, 0x13, 0xB0, 0xFA, + 0x80, 0xF0, 0xC0, 0xF1, 0x20, 0x00, 0xC0, 0xB2, 0xBD, 0xE8, 0xF0, 0x87, + 0x2D, 0xE9, 0xF0, 0x41, + 0x04, 0x46, 0xEC, 0xF7, 0x61, 0xFA, 0xC2, 0x48, 0xEC, 0xF7, 0x63, 0xFA, + 0xA4, 0xF8, 0x76, 0x00, + 0x02, 0xF0, 0xB2, 0xFB, 0x01, 0x46, 0x20, 0x46, 0xEC, 0xF7, 0xCC, 0xFC, + 0x02, 0xF0, 0xB0, 0xFB, + 0x02, 0x46, 0x0B, 0x46, 0x20, 0x46, 0xEC, 0xF7, 0xE7, 0xFC, 0x20, 0x46, + 0x02, 0xF0, 0xE2, 0xFC, + 0xB9, 0x4D, 0xB4, 0xF8, 0x56, 0x10, 0x28, 0x68, 0xB0, 0xF8, 0xBE, 0x20, + 0x62, 0xF3, 0x09, 0x01, + 0xA4, 0xF8, 0x56, 0x10, 0x90, 0xF8, 0xBD, 0x00, 0x94, 0xF8, 0x55, 0x10, + 0x60, 0xF3, 0x03, 0x01, + 0x84, 0xF8, 0x55, 0x10, 0x04, 0x21, 0x20, 0x46, 0xEC, 0xF7, 0x69, 0xFA, + 0x28, 0x68, 0x90, 0xF8, + 0xA2, 0x09, 0xA0, 0x70, 0x28, 0x68, 0x21, 0x8E, 0xB0, 0xF8, 0xA7, 0x29, + 0x62, 0xF3, 0x08, 0x01, + 0x21, 0x86, 0x90, 0xF8, 0xA3, 0x09, 0x00, 0xF0, 0x0F, 0x01, 0x20, 0x46, + 0xEC, 0xF7, 0xB7, 0xFA, + 0x28, 0x68, 0x34, 0xF8, 0x1A, 0x1F, 0xB0, 0xF8, 0x8E, 0x29, 0x62, 0xF3, + 0x0B, 0x01, 0x24, 0xF8, + 0x12, 0x19, 0xB0, 0xF8, 0x8C, 0x29, 0x21, 0x89, 0x62, 0xF3, 0x0B, 0x01, + 0x21, 0x81, 0xB0, 0xF8, + 0x8A, 0x29, 0xA1, 0x88, 0x62, 0xF3, 0x0B, 0x01, 0xA1, 0x80, 0x90, 0xF8, + 0xA4, 0x29, 0xA1, 0x7A, + 0x62, 0xF3, 0x01, 0x01, 0xA1, 0x72, 0x90, 0xF8, 0xA4, 0x29, 0x92, 0x08, + 0x62, 0xF3, 0x87, 0x11, + 0xA1, 0x72, 0x90, 0xF8, 0x82, 0x19, 0x14, 0xF8, 0x01, 0x2C, 0x61, 0xF3, + 0x00, 0x02, 0x04, 0xF8, + 0x01, 0x2C, 0x90, 0xF8, 0x85, 0x09, 0x60, 0x70, 0x90, 0x48, 0x90, 0xF8, + 0xE8, 0x1E, 0xA1, 0x75, + 0x90, 0xF8, 0xE9, 0x1E, 0x21, 0x77, 0x90, 0xF8, 0xEA, 0x1E, 0xE1, 0x75, + 0x90, 0xF8, 0xEB, 0x1E, + 0x61, 0x77, 0x90, 0xF8, 0xEC, 0x1E, 0x21, 0x76, 0x90, 0xF8, 0xED, 0x0E, + 0xA0, 0x77, 0x88, 0x48, + 0xEC, 0xF7, 0xE7, 0xF9, 0xA4, 0xF8, 0x78, 0x00, 0x85, 0x48, 0x20, 0x30, + 0xEC, 0xF7, 0xE1, 0xF9, + 0xA4, 0xF8, 0x7C, 0x00, 0x28, 0x68, 0x26, 0x7D, 0x90, 0xF8, 0x92, 0x19, + 0x09, 0x09, 0x61, 0xF3, + 0x07, 0x16, 0x26, 0x75, 0x90, 0xF8, 0x97, 0x19, 0x0A, 0x09, 0xA1, 0x7E, + 0x62, 0xF3, 0x07, 0x11, + 0xA1, 0x76, 0x90, 0xF8, 0x93, 0x29, 0x63, 0x7D, 0x62, 0xF3, 0x03, 0x03, + 0x63, 0x75, 0x90, 0xF8, + 0x98, 0x79, 0xE2, 0x7E, 0x67, 0xF3, 0x03, 0x02, 0xE2, 0x76, 0x90, 0xF8, + 0x93, 0x79, 0x3F, 0x09, + 0x67, 0xF3, 0x07, 0x13, 0x63, 0x75, 0x90, 0xF8, 0x98, 0x39, 0x1B, 0x09, + 0x63, 0xF3, 0x07, 0x12, + 0xE2, 0x76, 0x90, 0xF8, 0x92, 0x29, 0x62, 0xF3, 0x00, 0x06, 0x26, 0x75, + 0x90, 0xF8, 0x97, 0x29, + 0x62, 0xF3, 0x00, 0x01, 0xA1, 0x76, 0x90, 0xF8, 0x87, 0x09, 0x01, 0x09, + 0xE0, 0x78, 0x61, 0xF3, + 0x03, 0x00, 0xE0, 0x70, 0x66, 0x48, 0x48, 0x38, 0xEC, 0xF7, 0xA3, 0xF9, + 0xA4, 0xF8, 0x76, 0x00, + 0x63, 0x48, 0x28, 0x38, 0xEC, 0xF7, 0x9D, 0xF9, 0xA4, 0xF8, 0x7A, 0x00, + 0x29, 0x68, 0x91, 0xF8, + 0x84, 0x09, 0xC2, 0x08, 0x14, 0xF8, 0x1F, 0x0F, 0x62, 0xF3, 0x83, 0x00, + 0x20, 0x70, 0x91, 0xF8, + 0x84, 0x29, 0x52, 0x08, 0x62, 0xF3, 0x00, 0x00, 0x20, 0x70, 0x91, 0xF8, + 0x84, 0x29, 0x92, 0x08, + 0x62, 0xF3, 0x41, 0x00, 0x04, 0xF8, 0x27, 0x09, 0x91, 0xF8, 0x83, 0x09, + 0xC0, 0xF3, 0x81, 0x01, + 0x20, 0x46, 0xEC, 0xF7, 0xF9, 0xF9, 0x28, 0x68, 0x90, 0xF8, 0x83, 0x09, + 0x01, 0x09, 0x14, 0xF8, + 0x28, 0x0F, 0x61, 0xF3, 0x04, 0x10, 0x20, 0x70, 0x4E, 0x48, 0xEC, 0xF7, + 0x72, 0xF9, 0xA4, 0xF8, + 0x60, 0x00, 0x4C, 0x48, 0x20, 0x30, 0xEC, 0xF7, 0x6C, 0xF9, 0xA4, 0xF8, + 0x62, 0x00, 0x28, 0x68, + 0x90, 0xF8, 0x82, 0x19, 0xCA, 0x08, 0xE1, 0x7A, 0x62, 0xF3, 0x45, 0x11, + 0xE1, 0x72, 0x90, 0xF8, + 0x83, 0x09, 0x41, 0x08, 0x20, 0x78, 0x61, 0xF3, 0x45, 0x10, 0x04, 0xF8, + 0x28, 0x09, 0x20, 0x46, + 0x02, 0xF0, 0x8D, 0xFB, 0x28, 0x68, 0x90, 0xF8, 0x86, 0x09, 0x81, 0x09, + 0x20, 0x46, 0xEC, 0xF7, + 0xB8, 0xF9, 0x14, 0xF8, 0x32, 0x0F, 0x20, 0xF0, 0x0F, 0x00, 0x0A, 0x30, + 0x20, 0xF0, 0xF0, 0x00, + 0xA0, 0x30, 0x04, 0xF8, 0x1C, 0x09, 0x29, 0x68, 0x91, 0xF8, 0x82, 0x09, + 0x82, 0x09, 0x60, 0x7F, + 0x62, 0xF3, 0x00, 0x00, 0x60, 0x77, 0x91, 0xF8, 0x82, 0x29, 0x52, 0x09, + 0x62, 0xF3, 0x41, 0x00, + 0x60, 0x77, 0x91, 0xF8, 0x88, 0x29, 0x62, 0xF3, 0x82, 0x00, 0x60, 0x77, + 0x91, 0xF8, 0xA5, 0x09, + 0xE2, 0x7D, 0x60, 0xF3, 0x04, 0x02, 0xE2, 0x75, 0x91, 0xF8, 0xA6, 0x09, + 0xA1, 0x7D, 0x60, 0xF3, + 0x04, 0x01, 0x23, 0x48, 0xA1, 0x75, 0x08, 0x38, 0x94, 0xF8, 0x49, 0x10, + 0x40, 0x78, 0x60, 0xF3, + 0x04, 0x01, 0x23, 0x48, 0x84, 0xF8, 0x49, 0x10, 0xE8, 0x30, 0xEC, 0xF7, + 0x1A, 0xF9, 0xA4, 0xF8, + 0x64, 0x00, 0x28, 0x68, 0x21, 0x8B, 0xB0, 0xF8, 0x90, 0x29, 0x62, 0xF3, + 0x0B, 0x01, 0x21, 0x83, + 0x90, 0xF8, 0x86, 0x19, 0xCA, 0x08, 0x94, 0xF8, 0x22, 0x10, 0x62, 0xF3, + 0x02, 0x01, 0x84, 0xF8, + 0x22, 0x10, 0x10, 0xF8, 0xE0, 0x1F, 0x22, 0x7D, 0x61, 0xF3, 0x03, 0x02, + 0x22, 0x75, 0x10, 0xF8, + 0xE0, 0x1B, 0x0A, 0x09, 0x61, 0x7D, 0x62, 0xF3, 0x03, 0x01, 0x61, 0x75, + 0x90, 0xF8, 0xC7, 0x17, + 0xE2, 0x7C, 0x61, 0xF3, 0x02, 0x02, 0xE2, 0x74, 0x90, 0xF8, 0xC2, 0x17, + 0xCA, 0x09, 0xA1, 0x7F, + 0x62, 0xF3, 0x00, 0x01, 0xA1, 0x77, 0x94, 0xF8, 0x49, 0x10, 0x41, 0xF0, + 0x20, 0x01, 0x84, 0xF8, + 0x49, 0x10, 0x90, 0xF8, 0xC4, 0x27, 0xD2, 0x09, 0x0E, 0xE0, 0x00, 0x00, + 0xC8, 0x07, 0x10, 0x00, + 0x7C, 0xA0, 0x01, 0x20, 0xB4, 0x84, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x78, 0x8B, 0x01, 0x20, + 0x18, 0x9A, 0x01, 0x20, 0x48, 0xBF, 0x01, 0x20, 0x62, 0xF3, 0xC7, 0x11, + 0x84, 0xF8, 0x49, 0x10, + 0xB0, 0xF8, 0xDC, 0x27, 0xB4, 0xF8, 0x4A, 0x10, 0x62, 0xF3, 0x08, 0x01, + 0xA4, 0xF8, 0x4A, 0x10, + 0x00, 0x21, 0xE1, 0x64, 0x90, 0xF8, 0xC7, 0x17, 0xCA, 0x08, 0x61, 0x7F, + 0x62, 0xF3, 0xC3, 0x01, + 0x61, 0x77, 0x90, 0xF8, 0xC2, 0x27, 0x12, 0x09, 0x62, 0xF3, 0x04, 0x11, + 0x61, 0x77, 0x90, 0xF8, + 0xE5, 0x17, 0x4A, 0x09, 0x14, 0xF8, 0x0E, 0x1C, 0x62, 0xF3, 0x06, 0x11, + 0x04, 0xF8, 0x0E, 0x1C, + 0x90, 0xF8, 0xC3, 0x17, 0x4A, 0x09, 0xA1, 0x78, 0x62, 0xF3, 0xC3, 0x01, + 0xA1, 0x70, 0x90, 0xF8, + 0xC2, 0x27, 0x52, 0x08, 0x62, 0xF3, 0x04, 0x11, 0xA1, 0x70, 0x90, 0xF8, + 0xC4, 0x07, 0x80, 0x09, + 0x60, 0xF3, 0x86, 0x11, 0xA1, 0x70, 0xBD, 0xE8, 0xF0, 0x81, 0x2D, 0xE9, + 0xF0, 0x41, 0x04, 0x46, + 0x6A, 0x4D, 0x01, 0x20, 0x01, 0x29, 0xA8, 0x70, 0x08, 0xD0, 0x00, 0x26, + 0x68, 0x4F, 0x02, 0x29, + 0x08, 0xD0, 0x03, 0x29, 0x14, 0xD0, 0x04, 0x29, 0x2C, 0xD1, 0x3C, 0xE0, + 0x20, 0x46, 0x00, 0xF0, + 0xC0, 0xF8, 0x27, 0xE0, 0x63, 0x48, 0xFF, 0xF7, 0x19, 0xFE, 0x20, 0x46, + 0x00, 0xF0, 0xB9, 0xF8, + 0xB8, 0x78, 0x94, 0xF8, 0x5F, 0x10, 0x60, 0xF3, 0x04, 0x01, 0x84, 0xF8, + 0x5F, 0x10, 0x14, 0xE0, + 0x5C, 0x48, 0xFF, 0xF7, 0x0B, 0xFE, 0x20, 0x46, 0x00, 0xF0, 0xAB, 0xF8, + 0xF8, 0x78, 0x94, 0xF8, + 0x5F, 0x10, 0x60, 0xF3, 0x04, 0x01, 0x84, 0xF8, 0x5F, 0x10, 0x94, 0xF8, + 0x27, 0x00, 0x20, 0xF0, + 0x02, 0x00, 0x40, 0xF0, 0x01, 0x00, 0x84, 0xF8, 0x27, 0x00, 0x00, 0x21, + 0x20, 0x46, 0xEC, 0xF7, + 0xEE, 0xF8, 0xAE, 0x70, 0x20, 0x46, 0x02, 0xF0, 0xD9, 0xFA, 0x94, 0xF8, + 0x27, 0x00, 0x40, 0xF3, + 0x00, 0x00, 0x40, 0x1C, 0xE8, 0x70, 0x4C, 0x48, 0x00, 0x68, 0x90, 0xF8, + 0x83, 0x09, 0xC0, 0xF3, + 0x80, 0x10, 0x68, 0x70, 0xAF, 0xE7, 0x20, 0x46, 0x00, 0xF0, 0x83, 0xF8, + 0x14, 0xF8, 0x37, 0x0F, + 0x40, 0xF0, 0x10, 0x00, 0x04, 0xF8, 0x1F, 0x09, 0xE0, 0x7E, 0x20, 0xF0, + 0x23, 0x00, 0xE0, 0x76, + 0x34, 0xF8, 0x0C, 0x0C, 0x6F, 0xF3, 0x0B, 0x00, 0x10, 0x30, 0x24, 0xF8, + 0x0C, 0x0C, 0x34, 0xF8, + 0x08, 0x0C, 0x6F, 0xF3, 0x0B, 0x00, 0x40, 0x30, 0x24, 0xF8, 0x08, 0x0C, + 0x08, 0x20, 0x04, 0xF8, + 0x16, 0x0C, 0x94, 0xF8, 0x47, 0x00, 0x20, 0xF0, 0x1F, 0x00, 0x84, 0xF8, + 0x47, 0x00, 0x34, 0xF8, + 0x18, 0x0F, 0x6F, 0xF3, 0x08, 0x00, 0x24, 0xF8, 0x03, 0x0B, 0x20, 0x78, + 0x40, 0xF0, 0x0C, 0x00, + 0x04, 0xF8, 0x0B, 0x09, 0x20, 0x78, 0x40, 0xF0, 0x20, 0x00, 0x04, 0xF8, + 0x28, 0x09, 0xB8, 0xE7, + 0x38, 0xB5, 0x02, 0xF0, 0x69, 0xF9, 0x00, 0x90, 0x04, 0x21, 0x68, 0x46, + 0xEC, 0xF7, 0xBB, 0xFC, + 0x26, 0x49, 0x27, 0x4D, 0x08, 0x71, 0x00, 0x20, 0x28, 0x60, 0x28, 0x71, + 0x01, 0x24, 0xE0, 0xB2, + 0xFF, 0xF7, 0x50, 0xFD, 0x28, 0x55, 0x64, 0x1C, 0x05, 0x2C, 0xF8, 0xDB, + 0x21, 0x48, 0xFF, 0xF7, + 0x95, 0xFD, 0x38, 0xBD, 0x30, 0xB5, 0x01, 0x7A, 0xA9, 0xB0, 0x79, 0xB3, + 0x68, 0x46, 0xFF, 0xF7, + 0x5C, 0xFF, 0x9D, 0xF8, 0x12, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x02, 0xF0, + 0xB7, 0xF9, 0x1A, 0x4C, + 0x20, 0x68, 0x00, 0xF2, 0x5E, 0x63, 0x00, 0xF6, 0x6E, 0x05, 0x90, 0xF8, + 0x58, 0x16, 0x90, 0xF8, + 0x59, 0x06, 0x03, 0xEB, 0x40, 0x00, 0x02, 0xF0, 0x7A, 0xF9, 0x20, 0x68, + 0x90, 0xF8, 0x68, 0x18, + 0x90, 0xF8, 0x69, 0x08, 0x05, 0xEB, 0x40, 0x00, 0x02, 0xF0, 0x88, 0xF9, + 0x0B, 0x48, 0x41, 0x78, + 0x01, 0xB1, 0x02, 0x21, 0x01, 0x70, 0xC9, 0xB2, 0x68, 0x46, 0xEC, 0xF7, + 0x7F, 0xFA, 0x0B, 0x49, + 0x68, 0x46, 0xEC, 0xF7, 0xB0, 0xFA, 0x00, 0x20, 0x29, 0xB0, 0x30, 0xBD, + 0xFF, 0xE7, 0x01, 0x20, + 0xFA, 0xE7, 0xA4, 0x22, 0x03, 0x49, 0x02, 0xF0, 0x18, 0xBC, 0x00, 0x00, + 0xC8, 0x07, 0x10, 0x00, + 0x74, 0xA0, 0x01, 0x20, 0xE0, 0x7E, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x95, 0x42, 0x01, 0x00, + 0x10, 0xB5, 0x04, 0x00, 0x03, 0xD0, 0xBD, 0xE8, 0x10, 0x40, 0x02, 0xF0, + 0xB6, 0xB8, 0xFE, 0x48, + 0x02, 0x46, 0x81, 0x78, 0x52, 0x78, 0x29, 0xB1, 0xFC, 0x49, 0xA1, 0xF1, + 0x0A, 0x00, 0x02, 0xF0, + 0xD7, 0xF8, 0x0B, 0xE0, 0x03, 0x78, 0x00, 0x21, 0x01, 0x2B, 0x00, 0xD8, + 0x11, 0x46, 0xF7, 0x48, + 0x52, 0x00, 0x00, 0xEB, 0x41, 0x01, 0x0A, 0x38, 0x02, 0xF0, 0xEF, 0xFB, + 0xF3, 0x48, 0xF4, 0x49, + 0x0A, 0x38, 0xC8, 0x62, 0x20, 0x46, 0xDE, 0xE7, 0x2D, 0xE9, 0xF0, 0x47, + 0xDF, 0xF8, 0xC4, 0xA3, + 0x01, 0x27, 0xB8, 0x46, 0xDA, 0xF8, 0x00, 0x00, 0x00, 0xF2, 0x5E, 0x64, + 0x90, 0xF8, 0x22, 0x1A, + 0x90, 0xF8, 0x1E, 0x5A, 0x01, 0xF0, 0x1F, 0x06, 0x90, 0xF8, 0x1F, 0x1A, + 0x01, 0xF0, 0x0F, 0x09, + 0x90, 0xF8, 0x02, 0x1A, 0x89, 0x06, 0x09, 0xD5, 0x90, 0xF8, 0x5C, 0x16, + 0x90, 0xF8, 0x5D, 0x06, + 0x0A, 0x22, 0x04, 0xEB, 0x40, 0x00, 0x02, 0xF0, 0xE7, 0xF8, 0x07, 0x46, + 0xDA, 0xF8, 0x00, 0x00, + 0x90, 0xF8, 0x02, 0x1A, 0x49, 0x06, 0x09, 0xD5, 0x90, 0xF8, 0x6C, 0x18, + 0x90, 0xF8, 0x6D, 0x08, + 0x0A, 0x22, 0x04, 0xEB, 0x40, 0x00, 0x02, 0xF0, 0xD7, 0xF8, 0x80, 0x46, + 0x4F, 0xF4, 0x80, 0x70, + 0x85, 0x42, 0x00, 0xD9, 0x05, 0x46, 0x1F, 0x2E, 0x00, 0xD9, 0x1F, 0x26, + 0x76, 0x1C, 0x75, 0x43, + 0x09, 0xF1, 0x01, 0x00, 0x45, 0x43, 0xC5, 0xEB, 0x45, 0x20, 0x78, 0x43, + 0x00, 0xFB, 0x08, 0xF0, + 0xC0, 0x13, 0xB0, 0xFA, 0x80, 0xF0, 0xC0, 0xF1, 0x20, 0x00, 0xC0, 0xB2, + 0xBD, 0xE8, 0xF0, 0x87, + 0x2D, 0xE9, 0xF0, 0x41, 0x04, 0x46, 0xEB, 0xF7, 0x3F, 0xFF, 0xC8, 0x48, + 0xEB, 0xF7, 0x41, 0xFF, + 0xA4, 0xF8, 0x76, 0x00, 0x02, 0xF0, 0xA5, 0xF8, 0x01, 0x46, 0x20, 0x46, + 0xEC, 0xF7, 0xAA, 0xF9, + 0x02, 0xF0, 0xA4, 0xF8, 0x02, 0x46, 0x0B, 0x46, 0x20, 0x46, 0xEC, 0xF7, + 0xC5, 0xF9, 0x04, 0x21, + 0x20, 0x46, 0xEB, 0xF7, 0x5C, 0xFF, 0xBF, 0x4D, 0x28, 0x68, 0x90, 0xF8, + 0x1E, 0x0A, 0xA0, 0x70, + 0x28, 0x68, 0x21, 0x8E, 0xB0, 0xF8, 0x23, 0x2A, 0x62, 0xF3, 0x08, 0x01, + 0x21, 0x86, 0x90, 0xF8, + 0x1F, 0x0A, 0x00, 0xF0, 0x0F, 0x01, 0x20, 0x46, 0xEB, 0xF7, 0xA9, 0xFF, + 0x28, 0x68, 0x34, 0xF8, + 0x1A, 0x1F, 0xB0, 0xF8, 0x0E, 0x2A, 0x62, 0xF3, 0x0B, 0x01, 0x24, 0xF8, + 0x12, 0x19, 0xB0, 0xF8, + 0x0C, 0x2A, 0x21, 0x89, 0x62, 0xF3, 0x0B, 0x01, 0x21, 0x81, 0xB0, 0xF8, + 0x0A, 0x2A, 0xA1, 0x88, + 0x62, 0xF3, 0x0B, 0x01, 0xA1, 0x80, 0x90, 0xF8, 0x20, 0x2A, 0xA1, 0x7A, + 0x62, 0xF3, 0x01, 0x01, + 0xA1, 0x72, 0x90, 0xF8, 0x20, 0x2A, 0x92, 0x08, 0x62, 0xF3, 0x87, 0x11, + 0xA1, 0x72, 0x90, 0xF8, + 0x02, 0x1A, 0x14, 0xF8, 0x01, 0x2C, 0x61, 0xF3, 0x00, 0x02, 0x04, 0xF8, + 0x01, 0x2C, 0x90, 0xF8, + 0x05, 0x0A, 0x60, 0x70, 0xA0, 0x48, 0x90, 0xF8, 0x80, 0x1F, 0xA1, 0x75, + 0x90, 0xF8, 0x81, 0x1F, + 0x21, 0x77, 0x90, 0xF8, 0x82, 0x1F, 0xE1, 0x75, 0x90, 0xF8, 0x83, 0x1F, + 0x61, 0x77, 0x90, 0xF8, + 0x84, 0x1F, 0x21, 0x76, 0x90, 0xF8, 0x85, 0x0F, 0xA0, 0x77, 0x98, 0x48, + 0xEB, 0xF7, 0xD9, 0xFE, + 0xA4, 0xF8, 0x78, 0x00, 0x95, 0x48, 0x20, 0x30, 0xEB, 0xF7, 0xD3, 0xFE, + 0xA4, 0xF8, 0x7C, 0x00, + 0x28, 0x68, 0x23, 0x7D, 0x90, 0xF8, 0x12, 0x1A, 0x09, 0x09, 0x61, 0xF3, + 0x07, 0x13, 0x23, 0x75, + 0x90, 0xF8, 0x17, 0x1A, 0xA6, 0x7E, 0x09, 0x09, 0x61, 0xF3, 0x07, 0x16, + 0xA6, 0x76, 0x90, 0xF8, + 0x13, 0x1A, 0x62, 0x7D, 0x61, 0xF3, 0x03, 0x02, 0x62, 0x75, 0x90, 0xF8, + 0x18, 0x7A, 0xE1, 0x7E, + 0x67, 0xF3, 0x03, 0x01, 0xE1, 0x76, 0x90, 0xF8, 0x13, 0x7A, 0x3F, 0x09, + 0x67, 0xF3, 0x07, 0x12, + 0x62, 0x75, 0x90, 0xF8, 0x18, 0x2A, 0x12, 0x09, 0x62, 0xF3, 0x07, 0x11, + 0xE1, 0x76, 0x90, 0xF8, + 0x12, 0x1A, 0x61, 0xF3, 0x00, 0x03, 0x23, 0x75, 0x90, 0xF8, 0x17, 0x1A, + 0x61, 0xF3, 0x00, 0x06, + 0xA6, 0x76, 0x90, 0xF8, 0x07, 0x0A, 0x01, 0x09, 0xE0, 0x78, 0x61, 0xF3, + 0x03, 0x00, 0xE0, 0x70, + 0x76, 0x48, 0x48, 0x38, 0xEB, 0xF7, 0x95, 0xFE, 0xA4, 0xF8, 0x76, 0x00, + 0x73, 0x48, 0x28, 0x38, + 0xEB, 0xF7, 0x8F, 0xFE, 0xA4, 0xF8, 0x7A, 0x00, 0x28, 0x68, 0x90, 0xF8, + 0x04, 0x1A, 0xCA, 0x08, + 0x14, 0xF8, 0x1F, 0x1F, 0x62, 0xF3, 0x83, 0x01, 0x41, 0xF0, 0x01, 0x01, + 0x21, 0x70, 0x90, 0xF8, + 0x04, 0x2A, 0x92, 0x08, 0x62, 0xF3, 0x41, 0x01, 0x04, 0xF8, 0x27, 0x19, + 0x90, 0xF8, 0x03, 0x0A, + 0xC0, 0xF3, 0x81, 0x01, 0x20, 0x46, 0xEB, 0xF7, 0xEF, 0xFE, 0x28, 0x68, + 0x90, 0xF8, 0x03, 0x0A, + 0x01, 0x09, 0x14, 0xF8, 0x28, 0x0F, 0x61, 0xF3, 0x04, 0x10, 0x20, 0x70, + 0x60, 0x48, 0xEB, 0xF7, + 0x68, 0xFE, 0xA4, 0xF8, 0x60, 0x00, 0x5E, 0x48, 0x20, 0x30, 0xEB, 0xF7, + 0x62, 0xFE, 0xA4, 0xF8, + 0x62, 0x00, 0x28, 0x68, 0x90, 0xF8, 0x02, 0x1A, 0xCA, 0x08, 0xE1, 0x7A, + 0x62, 0xF3, 0x45, 0x11, + 0xE1, 0x72, 0x90, 0xF8, 0x03, 0x0A, 0x41, 0x08, 0x20, 0x78, 0x61, 0xF3, + 0x45, 0x10, 0x04, 0xF8, + 0x28, 0x09, 0x20, 0x46, 0x02, 0xF0, 0x4A, 0xF8, 0x28, 0x68, 0x90, 0xF8, + 0x06, 0x0A, 0x81, 0x09, + 0x20, 0x46, 0xEB, 0xF7, 0xAE, 0xFE, 0x14, 0xF8, 0x32, 0x0F, 0x20, 0xF0, + 0x0F, 0x00, 0x0A, 0x30, + 0x20, 0xF0, 0xF0, 0x00, 0xA0, 0x30, 0x04, 0xF8, 0x1C, 0x09, 0x29, 0x68, + 0x91, 0xF8, 0x02, 0x0A, + 0x82, 0x09, 0x60, 0x7F, 0x62, 0xF3, 0x00, 0x00, 0x60, 0x77, 0x91, 0xF8, + 0x02, 0x2A, 0x52, 0x09, + 0x62, 0xF3, 0x41, 0x00, 0x60, 0x77, 0x91, 0xF8, 0x08, 0x2A, 0x62, 0xF3, + 0x82, 0x00, 0x60, 0x77, + 0x91, 0xF8, 0x21, 0x0A, 0xE2, 0x7D, 0x60, 0xF3, 0x04, 0x02, 0xE2, 0x75, + 0x91, 0xF8, 0x22, 0x0A, + 0xA1, 0x7D, 0x60, 0xF3, 0x04, 0x01, 0x35, 0x48, 0xA1, 0x75, 0x10, 0x38, + 0x94, 0xF8, 0x49, 0x10, + 0x40, 0x78, 0x60, 0xF3, 0x04, 0x01, 0x35, 0x48, 0x84, 0xF8, 0x49, 0x10, + 0x50, 0x30, 0xEB, 0xF7, + 0x10, 0xFE, 0xA4, 0xF8, 0x64, 0x00, 0x28, 0x68, 0x34, 0xF8, 0x18, 0x1F, + 0xB0, 0xF8, 0x10, 0x2A, + 0x62, 0xF3, 0x0B, 0x01, 0x24, 0xF8, 0x0A, 0x1B, 0x90, 0xF8, 0x06, 0x1A, + 0xCA, 0x08, 0x21, 0x78, + 0x62, 0xF3, 0x02, 0x01, 0x04, 0xF8, 0x0E, 0x19, 0x10, 0xF8, 0xE0, 0x1F, + 0x22, 0x78, 0x61, 0xF3, + 0x03, 0x02, 0x04, 0xF8, 0x01, 0x2B, 0x01, 0x78, 0x0A, 0x09, 0x21, 0x78, + 0x62, 0xF3, 0x03, 0x01, + 0x04, 0xF8, 0x02, 0x19, 0x90, 0xF8, 0x27, 0x19, 0x22, 0x78, 0x61, 0xF3, + 0x02, 0x02, 0x04, 0xF8, + 0x0B, 0x2B, 0x90, 0xF8, 0x22, 0x19, 0xCA, 0x09, 0x21, 0x78, 0x62, 0xF3, + 0x00, 0x01, 0x04, 0xF8, + 0x0E, 0x1B, 0x14, 0xF8, 0x1D, 0x1F, 0x41, 0xF0, 0x20, 0x01, 0x21, 0x70, + 0x90, 0xF8, 0x24, 0x29, + 0xD2, 0x09, 0x62, 0xF3, 0xC7, 0x11, 0x04, 0xF8, 0x01, 0x1B, 0xB0, 0xF8, + 0x3C, 0x29, 0x21, 0x88, + 0x62, 0xF3, 0x08, 0x01, 0x24, 0xF8, 0x02, 0x1B, 0x00, 0x21, 0x44, 0xF8, + 0x2F, 0x19, 0x90, 0xF8, + 0x27, 0x19, 0xCA, 0x08, 0x21, 0x78, 0x62, 0xF3, 0xC3, 0x01, 0x21, 0x70, + 0x90, 0xF8, 0x22, 0x29, + 0x12, 0x09, 0x62, 0xF3, 0x04, 0x11, 0x04, 0xF8, 0x0C, 0x19, 0x14, 0xF8, + 0x1F, 0x19, 0x21, 0xF0, + 0x10, 0x01, 0xE1, 0x77, 0x0E, 0xE0, 0x00, 0x00, 0xCD, 0x07, 0x10, 0x00, + 0x70, 0xA1, 0x01, 0x20, + 0xB4, 0x84, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, 0x78, 0x8B, 0x01, 0x20, + 0xB0, 0x9A, 0x01, 0x20, + 0x48, 0xBF, 0x01, 0x20, 0x90, 0xF8, 0x41, 0x19, 0x4A, 0x09, 0x21, 0x78, + 0x62, 0xF3, 0x06, 0x11, + 0x21, 0x70, 0x90, 0xF8, 0x23, 0x19, 0x4A, 0x09, 0x21, 0x7C, 0x62, 0xF3, + 0xC3, 0x01, 0x21, 0x74, + 0x90, 0xF8, 0x22, 0x29, 0x52, 0x08, 0x62, 0xF3, 0x04, 0x11, 0x21, 0x74, + 0x90, 0xF8, 0x24, 0x09, + 0x80, 0x09, 0x60, 0xF3, 0x86, 0x11, 0x21, 0x74, 0xBD, 0xE8, 0xF0, 0x81, + 0xF8, 0xB5, 0x01, 0xF0, + 0xF0, 0xFE, 0x00, 0x90, 0x04, 0x21, 0x68, 0x46, 0xEC, 0xF7, 0x2D, 0xFA, + 0x45, 0x4E, 0x46, 0x4D, + 0x70, 0x70, 0x00, 0x20, 0x28, 0x60, 0x28, 0x71, 0x01, 0x24, 0xE0, 0xB2, + 0xFF, 0xF7, 0xE4, 0xFD, + 0x28, 0x55, 0x64, 0x1C, 0x05, 0x2C, 0xF8, 0xDB, 0x40, 0x48, 0xFF, 0xF7, + 0x29, 0xFE, 0x40, 0x48, + 0x00, 0x68, 0x90, 0xF8, 0x03, 0x0A, 0xC0, 0xF3, 0x80, 0x10, 0xB0, 0x70, + 0xF8, 0xBD, 0x30, 0xB5, + 0x81, 0x7A, 0xA9, 0xB0, 0x79, 0xB3, 0x68, 0x46, 0x00, 0xF0, 0x30, 0xF8, + 0x9D, 0xF8, 0x12, 0x00, + 0x00, 0xF0, 0x03, 0x00, 0x01, 0xF0, 0x22, 0xFF, 0x35, 0x4C, 0x20, 0x68, + 0x00, 0xF2, 0x5E, 0x63, + 0x00, 0xF6, 0x6E, 0x05, 0x90, 0xF8, 0x5C, 0x16, 0x90, 0xF8, 0x5D, 0x06, + 0x03, 0xEB, 0x40, 0x00, + 0x01, 0xF0, 0xE5, 0xFE, 0x20, 0x68, 0x90, 0xF8, 0x6C, 0x18, 0x90, 0xF8, + 0x6D, 0x08, 0x05, 0xEB, + 0x40, 0x00, 0x01, 0xF0, 0xF3, 0xFE, 0x27, 0x48, 0x81, 0x78, 0x01, 0xB1, + 0x02, 0x21, 0x01, 0x70, + 0xC9, 0xB2, 0x68, 0x46, 0xEB, 0xF7, 0xEA, 0xFF, 0x26, 0x49, 0x68, 0x46, + 0xEC, 0xF7, 0x1B, 0xF8, + 0x00, 0x20, 0x29, 0xB0, 0x30, 0xBD, 0xFF, 0xE7, 0x01, 0x20, 0xFA, 0xE7, + 0x70, 0xB5, 0x01, 0x29, + 0x20, 0xD0, 0x1D, 0x4D, 0x03, 0x29, 0x21, 0xD0, 0x04, 0x29, 0x2E, 0xD1, + 0x04, 0x46, 0x1B, 0x48, + 0xFF, 0xF7, 0xDE, 0xFD, 0x20, 0x46, 0x00, 0xF0, 0x29, 0xF8, 0x28, 0x79, + 0x94, 0xF8, 0x5F, 0x10, + 0x60, 0xF3, 0x04, 0x01, 0x84, 0xF8, 0x5F, 0x10, 0x94, 0xF8, 0x27, 0x00, + 0x20, 0xF0, 0x02, 0x00, + 0x40, 0xF0, 0x01, 0x00, 0x84, 0xF8, 0x27, 0x00, 0x20, 0x46, 0xBD, 0xE8, + 0x70, 0x40, 0x00, 0x21, + 0xEB, 0xF7, 0x9D, 0xBD, 0xBD, 0xE8, 0x70, 0x40, 0x00, 0xF0, 0x10, 0xB8, + 0x04, 0x46, 0x0B, 0x48, + 0xFF, 0xF7, 0xBE, 0xFD, 0x20, 0x46, 0x00, 0xF0, 0x09, 0xF8, 0xE8, 0x78, + 0x94, 0xF8, 0x5F, 0x10, + 0x60, 0xF3, 0x04, 0x01, 0x84, 0xF8, 0x5F, 0x10, 0xE6, 0xE7, 0x70, 0xBD, + 0xA4, 0x22, 0x03, 0x49, + 0x02, 0xF0, 0x4B, 0xB9, 0xCD, 0x07, 0x10, 0x00, 0x60, 0xA1, 0x01, 0x20, + 0x84, 0x7F, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, 0xF1, 0x48, 0x01, 0x00, 0x10, 0xB5, 0x04, 0x00, + 0x03, 0xD0, 0xBD, 0xE8, + 0x10, 0x40, 0x01, 0xF0, 0xEA, 0xBD, 0x23, 0x48, 0x23, 0x49, 0x00, 0x78, + 0x42, 0x00, 0x01, 0xEB, + 0x40, 0x01, 0x21, 0x48, 0x98, 0x30, 0x02, 0xF0, 0x30, 0xF9, 0x1F, 0x48, + 0x1F, 0x49, 0x98, 0x30, + 0x48, 0x62, 0x20, 0x46, 0xEB, 0xE7, 0x38, 0xB5, 0x01, 0xF0, 0x26, 0xFE, + 0x00, 0x90, 0x04, 0x21, + 0x68, 0x46, 0xEC, 0xF7, 0x78, 0xF9, 0x17, 0x49, 0x19, 0x4C, 0x08, 0x70, + 0x20, 0x46, 0x00, 0xF0, + 0x19, 0xFB, 0x18, 0x48, 0x01, 0x68, 0xA0, 0x7C, 0x91, 0xF8, 0x50, 0x21, + 0x62, 0xF3, 0x01, 0x00, + 0xA0, 0x74, 0x91, 0xF8, 0x50, 0x11, 0x89, 0x08, 0x61, 0xF3, 0x87, 0x10, + 0xA0, 0x74, 0x38, 0xBD, + 0x10, 0xB5, 0x0F, 0x4C, 0xC0, 0x79, 0x98, 0xB1, 0x20, 0x46, 0x01, 0xF0, + 0x2F, 0xFF, 0xA0, 0x7C, + 0x00, 0xF0, 0x03, 0x00, 0x01, 0xF0, 0x72, 0xFE, 0x00, 0xF0, 0x8D, 0xFB, + 0x04, 0x21, 0x20, 0x46, + 0xEB, 0xF7, 0x54, 0xFF, 0x08, 0x49, 0x20, 0x46, 0xEB, 0xF7, 0x85, 0xFF, + 0x00, 0x20, 0x10, 0xBD, + 0x01, 0x20, 0x10, 0xBD, 0xD0, 0x07, 0x10, 0x00, 0x80, 0xA1, 0x01, 0x20, + 0xB4, 0x84, 0x10, 0x00, + 0x28, 0x80, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, 0x89, 0x4E, 0x01, 0x00, + 0xFE, 0x49, 0xC8, 0x68, + 0x40, 0x1C, 0xC8, 0x60, 0x02, 0x28, 0x03, 0xD2, 0xFC, 0x49, 0x51, 0xF8, + 0x20, 0x00, 0x00, 0x47, + 0x00, 0x20, 0x01, 0xF0, 0x8A, 0xBD, 0x00, 0x28, 0x01, 0xD1, 0xEB, 0xF7, + 0x55, 0xBF, 0x70, 0x47, + 0xF5, 0x49, 0x10, 0xB5, 0x0A, 0x79, 0x01, 0x20, 0x52, 0x1C, 0xD2, 0xB2, + 0x0A, 0x71, 0x0B, 0x7A, + 0x9A, 0x42, 0x0A, 0xD8, 0x48, 0x7A, 0x48, 0xB1, 0xEB, 0xF7, 0xB6, 0xFE, + 0x03, 0x20, 0xEB, 0xF7, + 0x91, 0xF9, 0xEF, 0x48, 0xEB, 0xF7, 0xA4, 0xFE, 0x00, 0x20, 0x10, 0xBD, + 0xEB, 0xF7, 0x3C, 0xFF, + 0xFA, 0xE7, 0x70, 0xB5, 0x18, 0xB1, 0xBD, 0xE8, 0x70, 0x40, 0x01, 0xF0, + 0x66, 0xBD, 0xE6, 0x4C, + 0x20, 0x7A, 0x08, 0xB1, 0xE7, 0x4D, 0x01, 0xE0, 0xE6, 0x4D, 0x76, 0x3D, + 0xA0, 0x78, 0xE6, 0x49, + 0x82, 0x00, 0x01, 0xF5, 0xAE, 0x70, 0x02, 0xF0, 0xED, 0xF8, 0x60, 0x79, + 0x28, 0xB1, 0xA2, 0x78, + 0xE1, 0x49, 0x28, 0x46, 0x01, 0xF0, 0x7C, 0xFD, 0x0C, 0xE0, 0xA1, 0x78, + 0x60, 0x78, 0x01, 0xF0, + 0xB9, 0xFE, 0xA1, 0x78, 0xC0, 0xB2, 0x4A, 0x00, 0xDB, 0x49, 0x01, 0xEB, + 0x40, 0x01, 0x28, 0x46, + 0x02, 0xF0, 0x93, 0xF8, 0xD7, 0x48, 0x23, 0x79, 0xA2, 0x78, 0x29, 0x46, + 0x76, 0x38, 0x01, 0xF0, + 0x77, 0xFD, 0xFF, 0xF7, 0xB5, 0xFF, 0x00, 0x28, 0x15, 0xD0, 0xA0, 0x79, + 0xD3, 0x4D, 0x01, 0x28, + 0x0B, 0xD0, 0xD0, 0x48, 0x76, 0x38, 0xA8, 0x61, 0xE0, 0x79, 0x85, 0xF8, + 0x30, 0x00, 0x20, 0x79, + 0x85, 0xF8, 0x20, 0x00, 0xBD, 0xE8, 0x70, 0x40, 0x90, 0xE7, 0xCA, 0x48, + 0xA1, 0x78, 0x76, 0x38, + 0x01, 0xF0, 0x8E, 0xFE, 0xEF, 0xE7, 0x70, 0xBD, 0x70, 0xB5, 0x18, 0xB1, + 0xBD, 0xE8, 0x70, 0x40, + 0x01, 0xF0, 0x1B, 0xBD, 0xC0, 0x4C, 0x20, 0x7A, 0x08, 0xB1, 0xC2, 0x4D, + 0x01, 0xE0, 0xC1, 0x4D, + 0x4C, 0x3D, 0xE0, 0x78, 0xC0, 0x49, 0x82, 0x00, 0x01, 0xF5, 0xD8, 0x70, + 0x02, 0xF0, 0xA2, 0xF8, + 0x60, 0x79, 0x28, 0xB1, 0xE2, 0x78, 0xBC, 0x49, 0x28, 0x46, 0x01, 0xF0, + 0x31, 0xFD, 0x0C, 0xE0, + 0xE1, 0x78, 0x60, 0x78, 0x01, 0xF0, 0x74, 0xFE, 0xE1, 0x78, 0xC0, 0xB2, + 0x4A, 0x00, 0xB6, 0x49, + 0x01, 0xEB, 0x40, 0x01, 0x28, 0x46, 0x02, 0xF0, 0x48, 0xF8, 0xB2, 0x48, + 0x23, 0x79, 0xE2, 0x78, + 0x29, 0x46, 0x4C, 0x38, 0x01, 0xF0, 0x2C, 0xFD, 0xFF, 0xF7, 0x6A, 0xFF, + 0x00, 0x28, 0x15, 0xD0, + 0xA0, 0x79, 0xAE, 0x4D, 0x01, 0x28, 0x0B, 0xD0, 0xAA, 0x48, 0x4C, 0x38, + 0xE8, 0x61, 0xE0, 0x79, + 0x85, 0xF8, 0x30, 0x00, 0x20, 0x79, 0x85, 0xF8, 0x21, 0x00, 0xBD, 0xE8, + 0x70, 0x40, 0x45, 0xE7, + 0xA4, 0x48, 0xE1, 0x78, 0x4C, 0x38, 0x01, 0xF0, 0x44, 0xFE, 0xEF, 0xE7, + 0x70, 0xBD, 0x2D, 0xE9, + 0xF0, 0x5F, 0xA3, 0x48, 0x01, 0x27, 0xB8, 0x46, 0x00, 0x68, 0x00, 0xF2, + 0x5E, 0x64, 0x90, 0xF8, + 0x33, 0x11, 0x90, 0xF8, 0x4E, 0x51, 0xC1, 0xF3, 0x80, 0x1B, 0x90, 0xF8, + 0x55, 0x11, 0x01, 0xF0, + 0x03, 0x0A, 0x90, 0xF8, 0x52, 0x11, 0x01, 0xF0, 0x1F, 0x06, 0x90, 0xF8, + 0x4F, 0x11, 0x01, 0xF0, + 0x0F, 0x09, 0x90, 0xF8, 0x32, 0x11, 0x89, 0x06, 0x09, 0xD5, 0x90, 0xF8, + 0x56, 0x16, 0x90, 0xF8, + 0x57, 0x06, 0x0A, 0x22, 0x04, 0xEB, 0x40, 0x00, 0x01, 0xF0, 0x1E, 0xFD, + 0x07, 0x46, 0x90, 0x48, + 0x00, 0x68, 0x90, 0xF8, 0x32, 0x11, 0x49, 0x06, 0x09, 0xD5, 0x90, 0xF8, + 0x66, 0x18, 0x90, 0xF8, + 0x67, 0x08, 0x0A, 0x22, 0x04, 0xEB, 0x40, 0x00, 0x01, 0xF0, 0x0E, 0xFD, + 0x80, 0x46, 0x4F, 0xF4, + 0x80, 0x70, 0x85, 0x42, 0x00, 0xD9, 0x05, 0x46, 0x1F, 0x2E, 0x00, 0xD9, + 0x1F, 0x26, 0x76, 0x1C, + 0x75, 0x43, 0x09, 0xF1, 0x01, 0x00, 0x45, 0x43, 0xC5, 0xEB, 0x45, 0x20, + 0x78, 0x43, 0x00, 0xFB, + 0x08, 0xF0, 0x0A, 0xF1, 0x01, 0x01, 0x48, 0x43, 0x0B, 0xF1, 0x01, 0x01, + 0x48, 0x43, 0xC0, 0x13, + 0xB0, 0xFA, 0x80, 0xF0, 0xC0, 0xF1, 0x20, 0x00, 0xC0, 0xB2, 0xBD, 0xE8, + 0xF0, 0x9F, 0x2D, 0xE9, + 0xF0, 0x41, 0x04, 0x46, 0xEB, 0xF7, 0x70, 0xFB, 0x73, 0x48, 0xEB, 0xF7, + 0x72, 0xFB, 0xA4, 0xF8, + 0x76, 0x00, 0x01, 0xF0, 0xC1, 0xFC, 0x01, 0x46, 0x20, 0x46, 0xEB, 0xF7, + 0xDB, 0xFD, 0x01, 0xF0, + 0xBF, 0xFC, 0x02, 0x46, 0x0B, 0x46, 0x20, 0x46, 0xEB, 0xF7, 0xF6, 0xFD, + 0x20, 0x46, 0x01, 0xF0, + 0xF1, 0xFD, 0x6B, 0x4D, 0xB4, 0xF8, 0x56, 0x10, 0x28, 0x68, 0xB0, 0xF8, + 0xBE, 0x20, 0x62, 0xF3, + 0x09, 0x01, 0xA4, 0xF8, 0x56, 0x10, 0x90, 0xF8, 0xBD, 0x10, 0x94, 0xF8, + 0x55, 0x00, 0x61, 0xF3, + 0x03, 0x00, 0x84, 0xF8, 0x55, 0x00, 0x04, 0x21, 0x20, 0x46, 0xEB, 0xF7, + 0x78, 0xFB, 0x28, 0x68, + 0x90, 0xF8, 0x4E, 0x01, 0xA0, 0x70, 0x28, 0x68, 0x21, 0x8E, 0xB0, 0xF8, + 0x53, 0x21, 0x62, 0xF3, + 0x08, 0x01, 0x21, 0x86, 0x90, 0xF8, 0x4F, 0x01, 0x00, 0xF0, 0x0F, 0x01, + 0x20, 0x46, 0xEB, 0xF7, + 0xC6, 0xFB, 0x28, 0x68, 0x34, 0xF8, 0x1A, 0x1F, 0xB0, 0xF8, 0x3E, 0x21, + 0x62, 0xF3, 0x0B, 0x01, + 0x24, 0xF8, 0x12, 0x19, 0xB0, 0xF8, 0x3C, 0x21, 0x21, 0x89, 0x62, 0xF3, + 0x0B, 0x01, 0x21, 0x81, + 0xB0, 0xF8, 0x3A, 0x21, 0xA1, 0x88, 0x62, 0xF3, 0x0B, 0x01, 0xA1, 0x80, + 0x90, 0xF8, 0x50, 0x21, + 0xA1, 0x7A, 0x62, 0xF3, 0x01, 0x01, 0xA1, 0x72, 0x90, 0xF8, 0x50, 0x21, + 0x92, 0x08, 0x62, 0xF3, + 0x87, 0x11, 0xA1, 0x72, 0x90, 0xF8, 0x32, 0x11, 0x14, 0xF8, 0x01, 0x2C, + 0x61, 0xF3, 0x00, 0x02, + 0x04, 0xF8, 0x01, 0x2C, 0x90, 0xF8, 0x35, 0x01, 0x60, 0x70, 0x42, 0x48, + 0x90, 0xF8, 0x50, 0x1E, + 0xA1, 0x75, 0x90, 0xF8, 0x51, 0x1E, 0x21, 0x77, 0x90, 0xF8, 0x52, 0x1E, + 0xE1, 0x75, 0x90, 0xF8, + 0x53, 0x1E, 0x61, 0x77, 0x90, 0xF8, 0x54, 0x1E, 0x21, 0x76, 0x90, 0xF8, + 0x55, 0x0E, 0xA0, 0x77, + 0x39, 0x48, 0xEB, 0xF7, 0xF6, 0xFA, 0xA4, 0xF8, 0x78, 0x00, 0x37, 0x48, + 0x20, 0x30, 0xEB, 0xF7, + 0xF0, 0xFA, 0xA4, 0xF8, 0x7C, 0x00, 0x28, 0x68, 0x90, 0xF8, 0x42, 0x11, + 0x0A, 0x09, 0x21, 0x7D, + 0x62, 0xF3, 0x07, 0x11, 0x21, 0x75, 0x90, 0xF8, 0x47, 0x21, 0xA3, 0x7E, + 0x12, 0x09, 0x62, 0xF3, + 0x07, 0x13, 0xA3, 0x76, 0x90, 0xF8, 0x43, 0x61, 0x62, 0x7D, 0x66, 0xF3, + 0x03, 0x02, 0x62, 0x75, + 0x90, 0xF8, 0x48, 0x71, 0xE6, 0x7E, 0x67, 0xF3, 0x03, 0x06, 0xE6, 0x76, + 0x90, 0xF8, 0x43, 0x71, + 0x3F, 0x09, 0x67, 0xF3, 0x07, 0x12, 0x62, 0x75, 0x90, 0xF8, 0x48, 0x21, + 0x12, 0x09, 0x62, 0xF3, + 0x07, 0x16, 0xE6, 0x76, 0x90, 0xF8, 0x42, 0x21, 0x62, 0xF3, 0x00, 0x01, + 0x21, 0x75, 0x90, 0xF8, + 0x47, 0x11, 0x61, 0xF3, 0x00, 0x03, 0xA3, 0x76, 0x90, 0xF8, 0x37, 0x01, + 0x01, 0x09, 0xE0, 0x78, + 0x61, 0xF3, 0x03, 0x00, 0xE0, 0x70, 0x18, 0x48, 0x48, 0x38, 0xEB, 0xF7, + 0xB2, 0xFA, 0xA4, 0xF8, + 0x76, 0x00, 0x15, 0x48, 0x28, 0x38, 0xEB, 0xF7, 0xAC, 0xFA, 0xA4, 0xF8, + 0x7A, 0x00, 0x29, 0x68, + 0x91, 0xF8, 0x34, 0x01, 0xC2, 0x08, 0x14, 0xF8, 0x1F, 0x0F, 0x62, 0xF3, + 0x83, 0x00, 0x20, 0x70, + 0x91, 0xF8, 0x34, 0x21, 0x52, 0x08, 0x62, 0xF3, 0x00, 0x00, 0x20, 0x70, + 0x91, 0xF8, 0x34, 0x21, + 0x92, 0x08, 0x62, 0xF3, 0x41, 0x00, 0x11, 0xE0, 0xD4, 0x07, 0x10, 0x00, + 0x04, 0x7D, 0x01, 0x00, + 0x57, 0x4F, 0x01, 0x00, 0x7A, 0xA3, 0x01, 0x20, 0x6C, 0xA2, 0x01, 0x20, + 0xB4, 0x84, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, 0x78, 0x8B, 0x01, 0x20, 0x80, 0x99, 0x01, 0x20, + 0x04, 0xF8, 0x27, 0x09, + 0x91, 0xF8, 0x33, 0x01, 0xC0, 0xF3, 0x81, 0x01, 0x20, 0x46, 0xEB, 0xF7, + 0xF5, 0xFA, 0x28, 0x68, + 0x90, 0xF8, 0x33, 0x01, 0x01, 0x09, 0x14, 0xF8, 0x28, 0x0F, 0x61, 0xF3, + 0x04, 0x10, 0x20, 0x70, + 0xF8, 0x48, 0xEB, 0xF7, 0x6E, 0xFA, 0xA4, 0xF8, 0x60, 0x00, 0xF6, 0x48, + 0x20, 0x30, 0xEB, 0xF7, + 0x68, 0xFA, 0xA4, 0xF8, 0x62, 0x00, 0x28, 0x68, 0x90, 0xF8, 0x32, 0x11, + 0xCA, 0x08, 0xE1, 0x7A, + 0x62, 0xF3, 0x45, 0x11, 0xE1, 0x72, 0x90, 0xF8, 0x33, 0x01, 0x41, 0x08, + 0x20, 0x78, 0x61, 0xF3, + 0x45, 0x10, 0x04, 0xF8, 0x28, 0x09, 0x20, 0x46, 0x01, 0xF0, 0x50, 0xFC, + 0x28, 0x68, 0x90, 0xF8, + 0x36, 0x01, 0x81, 0x09, 0x20, 0x46, 0xEB, 0xF7, 0xB4, 0xFA, 0x14, 0xF8, + 0x32, 0x0F, 0x20, 0xF0, + 0x0F, 0x00, 0x0A, 0x30, 0x20, 0xF0, 0xF0, 0x00, 0xA0, 0x30, 0x04, 0xF8, + 0x1C, 0x09, 0x29, 0x68, + 0x91, 0xF8, 0x32, 0x01, 0x82, 0x09, 0x60, 0x7F, 0x62, 0xF3, 0x00, 0x00, + 0x60, 0x77, 0x91, 0xF8, + 0x32, 0x21, 0x52, 0x09, 0x62, 0xF3, 0x41, 0x00, 0x60, 0x77, 0x91, 0xF8, + 0x38, 0x21, 0x62, 0xF3, + 0x82, 0x00, 0x60, 0x77, 0x91, 0xF8, 0x51, 0x01, 0xE2, 0x7D, 0x60, 0xF3, + 0x04, 0x02, 0xE2, 0x75, + 0x91, 0xF8, 0x52, 0x01, 0xA1, 0x7D, 0x60, 0xF3, 0x04, 0x01, 0xD3, 0x48, + 0xA1, 0x75, 0x94, 0xF8, + 0x49, 0x10, 0x40, 0x78, 0x60, 0xF3, 0x04, 0x01, 0x84, 0xF8, 0x49, 0x10, + 0xCF, 0x48, 0xEB, 0xF7, + 0x18, 0xFA, 0xA4, 0xF8, 0x64, 0x00, 0x28, 0x68, 0x21, 0x8B, 0xB0, 0xF8, + 0x40, 0x21, 0x62, 0xF3, + 0x0B, 0x01, 0x21, 0x83, 0x90, 0xF8, 0x36, 0x11, 0xCA, 0x08, 0x94, 0xF8, + 0x22, 0x10, 0x62, 0xF3, + 0x02, 0x01, 0x84, 0xF8, 0x22, 0x10, 0x10, 0xF8, 0xE0, 0x1F, 0x22, 0x7D, + 0x61, 0xF3, 0x03, 0x02, + 0x22, 0x75, 0x10, 0xF8, 0x52, 0x1B, 0x0A, 0x09, 0x61, 0x7D, 0x62, 0xF3, + 0x03, 0x01, 0x61, 0x75, + 0x42, 0x79, 0xE1, 0x7C, 0x62, 0xF3, 0x02, 0x01, 0xE1, 0x74, 0x01, 0x78, + 0xCA, 0x09, 0xA1, 0x7F, + 0x62, 0xF3, 0x00, 0x01, 0xA1, 0x77, 0x94, 0xF8, 0x49, 0x10, 0x41, 0xF0, + 0x20, 0x01, 0x84, 0xF8, + 0x49, 0x10, 0x82, 0x78, 0xD2, 0x09, 0x62, 0xF3, 0xC7, 0x11, 0x84, 0xF8, + 0x49, 0x10, 0x42, 0x8B, + 0xB4, 0xF8, 0x4A, 0x10, 0x62, 0xF3, 0x08, 0x01, 0xA4, 0xF8, 0x4A, 0x10, + 0x00, 0x21, 0xE1, 0x64, + 0x41, 0x79, 0xCA, 0x08, 0x61, 0x7F, 0x62, 0xF3, 0xC3, 0x01, 0x61, 0x77, + 0x02, 0x78, 0x12, 0x09, + 0x62, 0xF3, 0x04, 0x11, 0x61, 0x77, 0xC1, 0x7F, 0x4A, 0x09, 0x14, 0xF8, + 0x0E, 0x1C, 0x62, 0xF3, + 0x06, 0x11, 0x04, 0xF8, 0x0E, 0x1C, 0x41, 0x78, 0x4A, 0x09, 0xA1, 0x78, + 0x62, 0xF3, 0xC3, 0x01, + 0xA1, 0x70, 0x02, 0x78, 0x52, 0x08, 0x62, 0xF3, 0x04, 0x11, 0xA1, 0x70, + 0x82, 0x78, 0x92, 0x09, + 0x62, 0xF3, 0x86, 0x11, 0xA1, 0x70, 0x40, 0x78, 0x9D, 0x49, 0xC0, 0xF3, + 0x80, 0x10, 0x48, 0x71, + 0xBD, 0xE8, 0xF0, 0x81, 0xA4, 0x22, 0x9B, 0x49, 0x01, 0xF0, 0xFF, 0xBD, + 0x2D, 0xE9, 0xF0, 0x41, + 0x97, 0x4D, 0x00, 0x26, 0x01, 0x27, 0x2E, 0x71, 0x2E, 0x72, 0xAF, 0x71, + 0x0C, 0x46, 0x6E, 0x72, + 0x01, 0x28, 0x07, 0xD0, 0x90, 0x4F, 0x02, 0x28, 0x2C, 0xD0, 0x03, 0x28, + 0x14, 0xD0, 0x04, 0x28, + 0x3A, 0xD1, 0x43, 0xE0, 0x08, 0x46, 0xFF, 0xF7, 0xE5, 0xFF, 0x8F, 0x48, + 0x01, 0x68, 0x91, 0xF8, + 0x38, 0x01, 0x42, 0x06, 0x30, 0xD5, 0x91, 0xF8, 0x55, 0x11, 0x00, 0x06, + 0x01, 0xF0, 0x03, 0x01, + 0x29, 0x72, 0x29, 0xD5, 0x6F, 0x72, 0x27, 0xE0, 0x86, 0x48, 0xFF, 0xF7, + 0x08, 0xFE, 0x20, 0x46, + 0xFF, 0xF7, 0xD0, 0xFF, 0xF8, 0x78, 0x94, 0xF8, 0x5F, 0x10, 0x60, 0xF3, + 0x04, 0x01, 0x84, 0xF8, + 0x5F, 0x10, 0x94, 0xF8, 0x27, 0x00, 0x20, 0xF0, 0x02, 0x00, 0x40, 0xF0, + 0x01, 0x00, 0x84, 0xF8, + 0x27, 0x00, 0x0C, 0xE0, 0x7B, 0x48, 0xFF, 0xF7, 0xF2, 0xFD, 0x20, 0x46, + 0xFF, 0xF7, 0xBA, 0xFF, + 0xB8, 0x78, 0x94, 0xF8, 0x5F, 0x10, 0x60, 0xF3, 0x04, 0x01, 0x84, 0xF8, + 0x5F, 0x10, 0x00, 0x21, + 0x20, 0x46, 0xEB, 0xF7, 0xEC, 0xF9, 0xAE, 0x71, 0x20, 0x46, 0x01, 0xF0, + 0xD7, 0xFB, 0x94, 0xF8, + 0x27, 0x00, 0x40, 0xF3, 0x00, 0x00, 0x40, 0x1C, 0xE8, 0x71, 0xA1, 0xE7, + 0x08, 0x46, 0xFF, 0xF7, + 0xA1, 0xFF, 0x14, 0xF8, 0x37, 0x0F, 0x40, 0xF0, 0x10, 0x00, 0x04, 0xF8, + 0x1F, 0x09, 0xE0, 0x7E, + 0x20, 0xF0, 0x23, 0x00, 0xE0, 0x76, 0x34, 0xF8, 0x0C, 0x0C, 0x6F, 0xF3, + 0x0B, 0x00, 0x10, 0x30, + 0x24, 0xF8, 0x0C, 0x0C, 0x34, 0xF8, 0x08, 0x0C, 0x6F, 0xF3, 0x0B, 0x00, + 0x40, 0x30, 0x24, 0xF8, + 0x08, 0x0C, 0x08, 0x20, 0x04, 0xF8, 0x16, 0x0C, 0x94, 0xF8, 0x47, 0x00, + 0x20, 0xF0, 0x1F, 0x00, + 0x84, 0xF8, 0x47, 0x00, 0x34, 0xF8, 0x18, 0x0F, 0x6F, 0xF3, 0x08, 0x00, + 0x24, 0xF8, 0x03, 0x0B, + 0x20, 0x78, 0x40, 0xF0, 0x0C, 0x00, 0x04, 0xF8, 0x0B, 0x09, 0x20, 0x78, + 0x40, 0xF0, 0x20, 0x00, + 0x04, 0xF8, 0x28, 0x09, 0xBF, 0xE7, 0x70, 0xB5, 0x53, 0x4D, 0x28, 0x68, + 0x00, 0xF2, 0x5E, 0x62, + 0x00, 0xF6, 0x6E, 0x04, 0x90, 0xF8, 0x56, 0x16, 0x90, 0xF8, 0x57, 0x06, + 0x02, 0xEB, 0x40, 0x00, + 0x01, 0xF0, 0xA5, 0xFA, 0x28, 0x68, 0x90, 0xF8, 0x66, 0x18, 0x90, 0xF8, + 0x67, 0x08, 0x04, 0xEB, + 0x40, 0x00, 0xBD, 0xE8, 0x70, 0x40, 0x01, 0xF0, 0xB1, 0xBA, 0x70, 0xB5, + 0x44, 0x4D, 0x45, 0x4C, + 0x68, 0x79, 0xA4, 0x34, 0x00, 0xB1, 0x02, 0x20, 0x68, 0x70, 0x21, 0x46, + 0x28, 0x78, 0xFF, 0xF7, + 0x4D, 0xFF, 0x41, 0x48, 0x01, 0x68, 0xA0, 0x7C, 0x91, 0xF8, 0x50, 0x21, + 0x62, 0xF3, 0x01, 0x00, + 0xA0, 0x74, 0x91, 0xF8, 0x50, 0x11, 0x89, 0x08, 0x61, 0xF3, 0x87, 0x10, + 0xA0, 0x74, 0x69, 0x78, + 0x20, 0x46, 0xEB, 0xF7, 0x93, 0xFB, 0xA0, 0x7C, 0x00, 0xF0, 0x03, 0x00, + 0x01, 0xF0, 0xA6, 0xFA, + 0xFF, 0xF7, 0xC1, 0xFF, 0x20, 0x46, 0xBD, 0xE8, 0x70, 0x40, 0x34, 0x49, + 0xEB, 0xF7, 0xBB, 0xBB, + 0x70, 0xB5, 0x2F, 0x4D, 0x2F, 0x4C, 0x68, 0x79, 0xA4, 0x34, 0x08, 0xB1, + 0x03, 0x20, 0x00, 0xE0, + 0x01, 0x20, 0x68, 0x70, 0x21, 0x46, 0x28, 0x78, 0xFF, 0xF7, 0x20, 0xFF, + 0x2A, 0x48, 0x01, 0x68, + 0x91, 0xF8, 0x50, 0x01, 0x02, 0x09, 0xA0, 0x7C, 0x62, 0xF3, 0x01, 0x00, + 0xA0, 0x74, 0x91, 0xF8, + 0x50, 0x11, 0x89, 0x09, 0x61, 0xF3, 0x87, 0x10, 0xA0, 0x74, 0x69, 0x78, + 0x20, 0x46, 0xEB, 0xF7, + 0x65, 0xFB, 0xA0, 0x7C, 0x00, 0xF0, 0x03, 0x00, 0x01, 0xF0, 0x78, 0xFA, + 0xFF, 0xF7, 0x93, 0xFF, + 0x20, 0x46, 0xBD, 0xE8, 0x70, 0x40, 0x1E, 0x49, 0xEB, 0xF7, 0x8D, 0xBB, + 0x7F, 0xB5, 0x01, 0xF0, + 0xFB, 0xF9, 0x01, 0x90, 0x01, 0xF0, 0xFC, 0xF9, 0xCD, 0xE9, 0x02, 0x01, + 0x04, 0x21, 0x01, 0xA8, + 0xEB, 0xF7, 0x49, 0xFD, 0x12, 0x4C, 0x08, 0x21, 0xA0, 0x70, 0x02, 0xA8, + 0xEB, 0xF7, 0x43, 0xFD, + 0xE0, 0x70, 0x0D, 0x4D, 0x01, 0x24, 0xE0, 0xB2, 0xFF, 0xF7, 0xC1, 0xFC, + 0x28, 0x55, 0x64, 0x1C, + 0x05, 0x2C, 0xF8, 0xDB, 0x0B, 0x48, 0xFF, 0xF7, 0x12, 0xFD, 0x7F, 0xBD, + 0x10, 0xB5, 0x80, 0x79, + 0xC0, 0xB1, 0x07, 0x49, 0x08, 0x70, 0x4F, 0xF0, 0xFF, 0x30, 0xC8, 0x60, + 0xFF, 0xF7, 0xEE, 0xFB, + 0x00, 0x20, 0x10, 0xBD, 0x48, 0xBF, 0x01, 0x20, 0x64, 0xA2, 0x01, 0x20, + 0x00, 0x9B, 0x01, 0x20, + 0xD4, 0x07, 0x10, 0x00, 0xCC, 0x80, 0x10, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x93, 0x4F, 0x01, 0x00, + 0x29, 0x50, 0x01, 0x00, 0x01, 0x20, 0xEC, 0xE7, 0x2D, 0xE9, 0xF0, 0x47, + 0xF9, 0x49, 0x02, 0x46, + 0x01, 0x27, 0x08, 0x68, 0xB8, 0x46, 0x00, 0xF2, 0x5E, 0x64, 0x02, 0x2A, + 0x01, 0xD0, 0x03, 0x2A, + 0x3C, 0xD0, 0x89, 0x46, 0x90, 0xF8, 0x23, 0x11, 0x90, 0xF8, 0x1F, 0x61, + 0x01, 0xF0, 0x1F, 0x05, + 0x90, 0xF8, 0x12, 0x11, 0x89, 0x06, 0x09, 0xD5, 0x90, 0xF8, 0x5A, 0x16, + 0x90, 0xF8, 0x5B, 0x06, + 0x0A, 0x22, 0x04, 0xEB, 0x40, 0x00, 0x01, 0xF0, 0xBF, 0xF9, 0x07, 0x46, + 0xD9, 0xF8, 0x00, 0x00, + 0x90, 0xF8, 0x12, 0x11, 0x49, 0x06, 0x09, 0xD5, 0x90, 0xF8, 0x6A, 0x18, + 0x90, 0xF8, 0x6B, 0x08, + 0x0A, 0x22, 0x04, 0xEB, 0x40, 0x00, 0x01, 0xF0, 0xAF, 0xF9, 0x80, 0x46, + 0x4F, 0xF4, 0x80, 0x70, + 0x86, 0x42, 0x00, 0xD9, 0x06, 0x46, 0x1F, 0x2D, 0x00, 0xD9, 0x1F, 0x25, + 0x6D, 0x1C, 0x6E, 0x43, + 0x70, 0x00, 0xC0, 0xEB, 0x40, 0x20, 0x78, 0x43, 0x00, 0xFB, 0x08, 0xF0, + 0xC0, 0x13, 0xB0, 0xFA, + 0x80, 0xF0, 0xC0, 0xF1, 0x20, 0x00, 0xC0, 0xB2, 0xBD, 0xE8, 0xF0, 0x87, + 0x10, 0xF8, 0xD3, 0x6F, + 0x00, 0x79, 0x00, 0xF0, 0x1F, 0x05, 0xE1, 0xE7, 0x70, 0xB5, 0x05, 0x00, + 0x03, 0xD0, 0xBD, 0xE8, + 0x70, 0x40, 0x01, 0xF0, 0x1A, 0xB9, 0xD0, 0x48, 0xD0, 0x4E, 0x00, 0x78, + 0x01, 0x28, 0x01, 0xD0, + 0xCF, 0x48, 0x16, 0xE0, 0xCB, 0x4C, 0xCE, 0x49, 0x22, 0x68, 0xA1, 0xF1, + 0x08, 0x00, 0x02, 0xF2, + 0x72, 0x42, 0x00, 0xF0, 0xD1, 0xF9, 0x22, 0x68, 0x92, 0xF8, 0x60, 0x04, + 0xC0, 0x07, 0x06, 0xD0, + 0xC7, 0x49, 0x02, 0xF2, 0x76, 0x42, 0xA1, 0xF1, 0x08, 0x00, 0x00, 0xF0, + 0xC5, 0xF9, 0xC4, 0x48, + 0x08, 0x38, 0x70, 0x61, 0x28, 0x46, 0xDA, 0xE7, 0x70, 0xB5, 0x04, 0x46, + 0xEA, 0xF7, 0xEC, 0xFF, + 0xBF, 0x48, 0xEA, 0xF7, 0xEE, 0xFF, 0xA4, 0xF8, 0x76, 0x00, 0x01, 0xF0, + 0x3D, 0xF9, 0x01, 0x46, + 0x00, 0x22, 0x20, 0x46, 0xEB, 0xF7, 0x88, 0xF9, 0x01, 0xF0, 0x3A, 0xF9, + 0x02, 0x46, 0x0B, 0x46, + 0x20, 0x46, 0xEB, 0xF7, 0xAD, 0xF9, 0xA0, 0x79, 0x00, 0x21, 0x20, 0xF0, + 0x02, 0x00, 0xA0, 0x71, + 0x94, 0xF8, 0x6F, 0x00, 0x20, 0xF0, 0x07, 0x00, 0x84, 0xF8, 0x6F, 0x00, + 0x94, 0xF8, 0x6E, 0x00, + 0x20, 0xF0, 0x0F, 0x00, 0x84, 0xF8, 0x6E, 0x00, 0x20, 0x46, 0xEB, 0xF7, + 0xB3, 0xF9, 0x94, 0xF8, + 0x5E, 0x00, 0x20, 0xF0, 0x01, 0x00, 0x84, 0xF8, 0x5E, 0x00, 0x20, 0x46, + 0x01, 0xF0, 0x52, 0xFA, + 0x04, 0x21, 0x20, 0x46, 0xEA, 0xF7, 0xEB, 0xFF, 0xA2, 0x4D, 0x28, 0x68, + 0x90, 0xF8, 0x1F, 0x01, + 0xA0, 0x70, 0x28, 0x68, 0x21, 0x8E, 0xB0, 0xF8, 0x24, 0x21, 0x62, 0xF3, + 0x08, 0x01, 0x21, 0x86, + 0x90, 0xF8, 0x20, 0x01, 0x00, 0xF0, 0x0F, 0x01, 0x20, 0x46, 0xEB, 0xF7, + 0x38, 0xF8, 0x28, 0x68, + 0x62, 0x7D, 0x90, 0xF8, 0x16, 0x11, 0x61, 0xF3, 0x03, 0x02, 0x62, 0x75, + 0xB0, 0xF8, 0x19, 0x21, + 0x21, 0x8A, 0x62, 0xF3, 0x0B, 0x01, 0x21, 0x82, 0x90, 0xF8, 0x21, 0x11, + 0xA2, 0x7D, 0x61, 0xF3, + 0x03, 0x02, 0xA2, 0x75, 0xB0, 0xF8, 0x1B, 0x21, 0xA1, 0x89, 0x62, 0xF3, + 0x0B, 0x01, 0xA1, 0x81, + 0x90, 0xF8, 0x20, 0x11, 0x0A, 0x09, 0xA1, 0x7C, 0x62, 0xF3, 0x01, 0x01, + 0xA1, 0x74, 0x90, 0xF8, + 0x20, 0x21, 0x92, 0x09, 0x62, 0xF3, 0x87, 0x11, 0xA1, 0x74, 0x90, 0xF8, + 0x12, 0x11, 0xE2, 0x79, + 0x61, 0xF3, 0x00, 0x02, 0xE2, 0x71, 0x90, 0xF8, 0x15, 0x11, 0x61, 0x72, + 0x90, 0xF8, 0x18, 0x01, + 0x01, 0x09, 0xE0, 0x7A, 0x61, 0xF3, 0x03, 0x00, 0xE0, 0x72, 0x82, 0x48, + 0xEA, 0xF7, 0x71, 0xFF, + 0xA4, 0xF8, 0x7C, 0x00, 0x29, 0x68, 0x91, 0xF8, 0x14, 0x01, 0xC2, 0x08, + 0x94, 0xF8, 0x27, 0x00, + 0x62, 0xF3, 0x83, 0x00, 0x20, 0xF0, 0x02, 0x00, 0x40, 0xF0, 0x01, 0x00, + 0x84, 0xF8, 0x27, 0x00, + 0x91, 0xF8, 0x13, 0x01, 0xC0, 0xF3, 0x81, 0x01, 0x20, 0x46, 0xEA, 0xF7, + 0xD5, 0xFF, 0x28, 0x68, + 0x90, 0xF8, 0x13, 0x01, 0x01, 0x09, 0x14, 0xF8, 0x28, 0x0F, 0x61, 0xF3, + 0x04, 0x10, 0x20, 0x70, + 0x71, 0x48, 0xEA, 0xF7, 0x4E, 0xFF, 0xA4, 0xF8, 0x5E, 0x00, 0x28, 0x68, + 0x90, 0xF8, 0x12, 0x11, + 0xCA, 0x08, 0xE1, 0x7A, 0x62, 0xF3, 0x45, 0x11, 0xE1, 0x72, 0x90, 0xF8, + 0x13, 0x01, 0x41, 0x08, + 0x20, 0x78, 0x61, 0xF3, 0x45, 0x10, 0x04, 0xF8, 0x28, 0x09, 0x20, 0x46, + 0x01, 0xF0, 0x10, 0xF9, + 0x28, 0x68, 0x90, 0xF8, 0x18, 0x01, 0xC0, 0xF3, 0x81, 0x01, 0x20, 0x46, + 0xEA, 0xF7, 0x99, 0xFF, + 0x14, 0xF8, 0x32, 0x0F, 0x20, 0xF0, 0x0F, 0x00, 0x0A, 0x30, 0x20, 0xF0, + 0xF0, 0x00, 0xA0, 0x30, + 0x04, 0xF8, 0x0A, 0x09, 0x29, 0x68, 0x91, 0xF8, 0x12, 0x01, 0x82, 0x09, + 0xE0, 0x7A, 0x62, 0xF3, + 0x00, 0x00, 0xE0, 0x72, 0x91, 0xF8, 0x12, 0x21, 0x52, 0x09, 0x62, 0xF3, + 0x41, 0x00, 0xE0, 0x72, + 0x91, 0xF8, 0x17, 0x21, 0xD2, 0x09, 0x62, 0xF3, 0x82, 0x00, 0xE0, 0x72, + 0x91, 0xF8, 0x22, 0x01, + 0x62, 0x79, 0x60, 0xF3, 0x04, 0x02, 0x62, 0x71, 0x91, 0xF8, 0x23, 0x01, + 0x21, 0x79, 0x60, 0xF3, + 0x04, 0x01, 0x4B, 0x48, 0x21, 0x71, 0x0C, 0x38, 0x41, 0x78, 0x94, 0xF8, + 0x37, 0x00, 0x61, 0xF3, + 0x04, 0x00, 0x84, 0xF8, 0x37, 0x00, 0x49, 0x48, 0xEA, 0xF7, 0xFB, 0xFE, + 0xA4, 0xF8, 0x52, 0x00, + 0x28, 0x68, 0xE1, 0x88, 0xB0, 0xF8, 0x1D, 0x21, 0x62, 0xF3, 0x0B, 0x01, + 0xE1, 0x80, 0x90, 0xF8, + 0x17, 0x11, 0xCA, 0x08, 0x21, 0x7C, 0x62, 0xF3, 0x02, 0x01, 0x21, 0x74, + 0x10, 0xF8, 0xDF, 0x2F, + 0xA1, 0x78, 0x62, 0xF3, 0x03, 0x01, 0xA1, 0x70, 0x10, 0xF8, 0x19, 0x1B, + 0x0A, 0x09, 0xE1, 0x78, + 0x62, 0xF3, 0x03, 0x01, 0xE1, 0x70, 0x81, 0x7F, 0x0A, 0x09, 0x61, 0x78, + 0x62, 0xF3, 0x02, 0x01, + 0x61, 0x70, 0x01, 0x7F, 0x8A, 0x09, 0x21, 0x78, 0x62, 0xF3, 0x00, 0x01, + 0x21, 0x70, 0x81, 0x7E, + 0xCA, 0x09, 0x21, 0x7B, 0x62, 0xF3, 0x00, 0x01, 0x21, 0x73, 0x94, 0xF8, + 0x37, 0x10, 0x41, 0xF0, + 0x20, 0x01, 0x84, 0xF8, 0x37, 0x10, 0x21, 0x8F, 0x6F, 0xF3, 0x08, 0x01, + 0x01, 0xF5, 0x80, 0x71, + 0x21, 0x87, 0x00, 0x21, 0xC4, 0xF8, 0x3A, 0x10, 0xC1, 0x7F, 0x8A, 0x09, + 0xE1, 0x7A, 0x62, 0xF3, + 0xC3, 0x01, 0xE1, 0x72, 0x80, 0x7E, 0x00, 0x09, 0x60, 0xF3, 0x04, 0x11, + 0xE1, 0x72, 0x70, 0xBD, + 0x70, 0xB5, 0x1F, 0x4D, 0x00, 0x20, 0x0C, 0x3D, 0x01, 0x24, 0x28, 0x60, + 0xE0, 0xB2, 0xFF, 0xF7, + 0x3B, 0xFE, 0x28, 0x55, 0x64, 0x1C, 0x04, 0x2C, 0xF8, 0xDB, 0xBD, 0xE8, + 0x70, 0x40, 0x1C, 0x48, + 0xAA, 0xE6, 0x70, 0xB5, 0x41, 0x7A, 0xAA, 0xB0, 0x59, 0xB1, 0x13, 0x4D, + 0x01, 0x20, 0x6C, 0x46, + 0x28, 0x70, 0x01, 0x29, 0x08, 0xD0, 0x00, 0x26, 0x02, 0x29, 0x09, 0xD0, + 0x03, 0x29, 0x2D, 0xD1, + 0x28, 0xE0, 0x01, 0x20, 0x2A, 0xB0, 0x70, 0xBD, 0x20, 0x46, 0x00, 0xF0, + 0x60, 0xF8, 0x25, 0xE0, + 0x20, 0x46, 0x00, 0xF0, 0x5C, 0xF8, 0x0A, 0x48, 0x9D, 0xF8, 0x5F, 0x10, + 0x0C, 0x38, 0x80, 0x78, + 0x60, 0xF3, 0x04, 0x01, 0x8D, 0xF8, 0x5F, 0x10, 0x00, 0x21, 0x20, 0x46, + 0xEA, 0xF7, 0x07, 0xFF, + 0x13, 0xE0, 0x00, 0x00, 0x4C, 0x07, 0x10, 0x00, 0xE4, 0x07, 0x10, 0x00, + 0xB4, 0x84, 0x10, 0x00, + 0xC0, 0xA4, 0x01, 0x20, 0x98, 0x98, 0x01, 0x20, 0x20, 0xBF, 0x01, 0x20, + 0x00, 0x9B, 0x01, 0x20, + 0x14, 0x82, 0x10, 0x00, 0x20, 0x46, 0x00, 0xF0, 0x3E, 0xF8, 0x2E, 0x70, + 0x20, 0x46, 0x01, 0xF0, + 0xDD, 0xF8, 0x9D, 0xF8, 0x12, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x01, 0xF0, + 0x1F, 0xF8, 0x37, 0x4C, + 0x20, 0x68, 0x00, 0xF2, 0x5E, 0x63, 0x00, 0xF6, 0x6E, 0x05, 0x90, 0xF8, + 0x5A, 0x16, 0x90, 0xF8, + 0x5B, 0x06, 0x03, 0xEB, 0x40, 0x00, 0x00, 0xF0, 0xE2, 0xFF, 0x20, 0x68, + 0x90, 0xF8, 0x6A, 0x18, + 0x90, 0xF8, 0x6B, 0x08, 0x05, 0xEB, 0x40, 0x00, 0x00, 0xF0, 0xF0, 0xFF, + 0x2C, 0x49, 0x68, 0x46, + 0xEB, 0xF7, 0x34, 0xF8, 0x00, 0x20, 0xAD, 0xE7, 0x40, 0xF2, 0xFF, 0x10, + 0xC1, 0xEB, 0x41, 0x21, + 0x91, 0xFB, 0xF0, 0xF0, 0x00, 0x20, 0x70, 0x47, 0x10, 0xB5, 0x00, 0x23, + 0xD4, 0x5C, 0x31, 0xF8, + 0x14, 0x40, 0x20, 0xF8, 0x13, 0x40, 0x5B, 0x1C, 0x04, 0x2B, 0xF7, 0xDB, + 0x10, 0xBD, 0xA4, 0x22, + 0x20, 0x49, 0x01, 0xF0, 0x7A, 0xBA, 0x70, 0xB5, 0x04, 0x46, 0x00, 0xF0, + 0x6A, 0xFB, 0x1E, 0x48, + 0xEA, 0xF7, 0x1F, 0xFE, 0xA4, 0xF8, 0x76, 0x00, 0x00, 0xF0, 0x77, 0xFF, + 0x17, 0x4D, 0x01, 0x46, + 0x28, 0x68, 0x90, 0xF8, 0xC9, 0x00, 0xC0, 0xF3, 0x41, 0x12, 0x20, 0x46, + 0xEA, 0xF7, 0xB4, 0xFF, + 0x00, 0xF0, 0x70, 0xFF, 0x02, 0x46, 0x0B, 0x46, 0x20, 0x46, 0xEA, 0xF7, + 0xD9, 0xFF, 0x13, 0x48, + 0xEA, 0xF7, 0x07, 0xFE, 0xA4, 0xF8, 0x7C, 0x00, 0x0F, 0x48, 0x0C, 0x38, + 0xC1, 0x78, 0x14, 0xF8, + 0x5F, 0x0F, 0x61, 0xF3, 0x04, 0x00, 0x20, 0x70, 0x29, 0x68, 0x00, 0xF0, + 0x1F, 0x00, 0x11, 0xF8, + 0xD3, 0x2F, 0xC9, 0x78, 0x01, 0xF0, 0x1F, 0x01, 0x49, 0x1C, 0xC1, 0xEB, + 0x41, 0x21, 0x49, 0x00, + 0x4A, 0x43, 0xC2, 0x40, 0x50, 0x08, 0xC4, 0xF8, 0x03, 0x00, 0x70, 0xBD, + 0x4C, 0x07, 0x10, 0x00, + 0x29, 0x58, 0x01, 0x00, 0x14, 0x82, 0x10, 0x00, 0xC0, 0xA4, 0x01, 0x20, + 0x98, 0x98, 0x01, 0x20, + 0x10, 0xB9, 0x66, 0x4A, 0x64, 0x49, 0x11, 0x61, 0x00, 0xF0, 0xDF, 0xBE, + 0x70, 0xB5, 0x64, 0x4D, + 0x04, 0x46, 0x28, 0x68, 0x90, 0xF8, 0xF1, 0x01, 0xC0, 0xF3, 0x01, 0x12, + 0xC0, 0xF3, 0x81, 0x01, + 0x00, 0xF0, 0x03, 0x00, 0xFD, 0xF7, 0xA1, 0xFA, 0x40, 0x1C, 0xC6, 0xB2, + 0x20, 0x46, 0x00, 0xF0, + 0x71, 0xFC, 0x59, 0x48, 0xEA, 0xF7, 0xC5, 0xFD, 0xA4, 0xF8, 0x76, 0x00, + 0x00, 0x22, 0x31, 0x46, + 0x20, 0x46, 0xEA, 0xF7, 0x82, 0xFF, 0xA0, 0x79, 0x40, 0xF0, 0x02, 0x00, + 0xA0, 0x71, 0x94, 0xF8, + 0x6F, 0x00, 0x66, 0xF3, 0x02, 0x00, 0x84, 0xF8, 0x6F, 0x00, 0x94, 0xF8, + 0x6E, 0x00, 0x20, 0xF0, + 0x0F, 0x00, 0x84, 0xF8, 0x6E, 0x00, 0x29, 0x68, 0xA0, 0x7C, 0x91, 0xF8, + 0x08, 0x21, 0x62, 0xF3, + 0x01, 0x00, 0xA0, 0x74, 0x91, 0xF8, 0x08, 0x21, 0x92, 0x08, 0x62, 0xF3, + 0x87, 0x10, 0xA0, 0x74, + 0xA0, 0x89, 0x6F, 0xF3, 0x0B, 0x00, 0xA0, 0x81, 0x91, 0xF8, 0x07, 0x01, + 0xA0, 0x70, 0x94, 0xF8, + 0x5E, 0x00, 0x00, 0x21, 0x20, 0xF0, 0x01, 0x00, 0x84, 0xF8, 0x5E, 0x00, + 0x20, 0x46, 0xEA, 0xF7, + 0x1E, 0xFE, 0x28, 0x68, 0x90, 0xF8, 0xF2, 0x11, 0x84, 0xF8, 0x66, 0x10, + 0x90, 0xF8, 0xF3, 0x11, + 0x84, 0xF8, 0x68, 0x10, 0x90, 0xF8, 0xF4, 0x11, 0x84, 0xF8, 0x6A, 0x10, + 0x90, 0xF8, 0xF5, 0x01, + 0x84, 0xF8, 0x6C, 0x00, 0x34, 0x48, 0x94, 0xF8, 0x5F, 0x10, 0x00, 0x1F, + 0x40, 0x78, 0x60, 0xF3, + 0x04, 0x01, 0x84, 0xF8, 0x5F, 0x10, 0x20, 0x46, 0xBD, 0xE8, 0x70, 0x40, + 0x00, 0xF0, 0x48, 0xBF, + 0x10, 0xB5, 0x2D, 0x4C, 0x00, 0x20, 0x24, 0x1F, 0x20, 0x80, 0x01, 0x20, + 0x00, 0xF0, 0x32, 0xF8, + 0x60, 0x70, 0xBD, 0xE8, 0x10, 0x40, 0x2B, 0x48, 0x88, 0xE7, 0x2D, 0xE9, + 0xF0, 0x41, 0x29, 0x4E, + 0x40, 0x79, 0x28, 0xB3, 0x30, 0x46, 0x00, 0xF0, 0xD9, 0xFF, 0xB0, 0x7C, + 0x00, 0xF0, 0x03, 0x00, + 0x00, 0xF0, 0x1C, 0xFF, 0x22, 0x4F, 0x3C, 0x68, 0x04, 0xF2, 0x5E, 0x60, + 0x94, 0xF8, 0x51, 0x26, + 0x04, 0xF6, 0x6E, 0x05, 0x94, 0xF8, 0x50, 0x16, 0x00, 0xEB, 0x42, 0x00, + 0x00, 0xF0, 0xDF, 0xFE, + 0x38, 0x68, 0x90, 0xF8, 0x60, 0x18, 0x90, 0xF8, 0x61, 0x08, 0x05, 0xEB, + 0x40, 0x00, 0x00, 0xF0, + 0xED, 0xFE, 0x19, 0x49, 0x30, 0x46, 0xEA, 0xF7, 0x31, 0xFF, 0x00, 0x20, + 0xBD, 0xE8, 0xF0, 0x81, + 0x01, 0x20, 0xFB, 0xE7, 0x12, 0x48, 0x4F, 0xF4, 0x80, 0x73, 0x02, 0x68, + 0x92, 0xF8, 0xF5, 0x10, + 0x92, 0xF8, 0x07, 0x01, 0x92, 0xF8, 0xF2, 0x20, 0x01, 0xF0, 0x1F, 0x01, + 0x02, 0xF0, 0x0F, 0x02, + 0x98, 0x42, 0x00, 0xD9, 0x18, 0x46, 0x1F, 0x29, 0x00, 0xD9, 0x1F, 0x21, + 0x49, 0x1C, 0x48, 0x43, + 0x40, 0x00, 0x52, 0x1C, 0x50, 0x43, 0xC0, 0xEB, 0x40, 0x20, 0xC0, 0x13, + 0xB0, 0xFA, 0x80, 0xF0, + 0xC0, 0xF1, 0x20, 0x00, 0xC0, 0xB2, 0x70, 0x47, 0xE4, 0xA4, 0x01, 0x20, + 0xB4, 0x84, 0x10, 0x00, + 0x4C, 0x07, 0x10, 0x00, 0xB8, 0x82, 0x10, 0x00, 0xA1, 0x5C, 0x01, 0x00, + 0x2D, 0xE9, 0xF0, 0x47, + 0xFE, 0x49, 0x4F, 0xF0, 0x01, 0x08, 0x00, 0x27, 0x0A, 0x68, 0x80, 0x1E, + 0x02, 0xF2, 0x5E, 0x64, + 0xC1, 0x46, 0x06, 0x28, 0x52, 0xD2, 0xDF, 0xE8, 0x00, 0xF0, 0x51, 0x09, + 0x2D, 0x51, 0x2D, 0x03, + 0x12, 0xF8, 0xD3, 0x6F, 0x10, 0x79, 0x00, 0xF0, 0x1F, 0x05, 0x70, 0xE0, + 0x92, 0xF8, 0xF5, 0x00, + 0x92, 0xF8, 0x01, 0x61, 0x00, 0xF0, 0x1F, 0x05, 0x92, 0xF8, 0xF2, 0x00, + 0x8A, 0x46, 0x00, 0xF0, + 0x0F, 0x07, 0x92, 0xF8, 0xE6, 0x00, 0x80, 0x06, 0x09, 0xD5, 0x92, 0xF8, + 0x53, 0x06, 0x92, 0xF8, + 0x52, 0x16, 0x04, 0xEB, 0x40, 0x00, 0x0A, 0x22, 0x00, 0xF0, 0x4E, 0xFE, + 0x80, 0x46, 0xDA, 0xF8, + 0x00, 0x00, 0x90, 0xF8, 0xE6, 0x10, 0x49, 0x06, 0x51, 0xD5, 0x90, 0xF8, + 0x62, 0x18, 0x90, 0xF8, + 0x63, 0x08, 0x46, 0xE0, 0x92, 0xF8, 0xFC, 0x00, 0x92, 0xF8, 0xF8, 0x60, + 0x00, 0xF0, 0x1F, 0x05, + 0x92, 0xF8, 0xF9, 0x00, 0x8A, 0x46, 0x00, 0xF0, 0x0F, 0x07, 0x92, 0xF8, + 0xE6, 0x00, 0x80, 0x06, + 0x09, 0xD5, 0x92, 0xF8, 0x55, 0x06, 0x92, 0xF8, 0x54, 0x16, 0x04, 0xEB, + 0x40, 0x00, 0x0A, 0x22, + 0x00, 0xF0, 0x2A, 0xFE, 0x80, 0x46, 0xDA, 0xF8, 0x00, 0x00, 0x90, 0xF8, + 0xE6, 0x10, 0x49, 0x06, + 0x2D, 0xD5, 0x90, 0xF8, 0x64, 0x18, 0x90, 0xF8, 0x65, 0x08, 0x22, 0xE0, + 0x92, 0xF8, 0xF5, 0x00, + 0x92, 0xF8, 0xF1, 0x60, 0x00, 0xF0, 0x1F, 0x05, 0x92, 0xF8, 0xF2, 0x00, + 0x8A, 0x46, 0x00, 0xF0, + 0x0F, 0x07, 0x92, 0xF8, 0xE6, 0x00, 0x80, 0x06, 0x09, 0xD5, 0x92, 0xF8, + 0x51, 0x06, 0x92, 0xF8, + 0x50, 0x16, 0x04, 0xEB, 0x40, 0x00, 0x0A, 0x22, 0x00, 0xF0, 0x06, 0xFE, + 0x80, 0x46, 0xDA, 0xF8, + 0x00, 0x00, 0x90, 0xF8, 0xE6, 0x10, 0x49, 0x06, 0x09, 0xD5, 0x90, 0xF8, + 0x60, 0x18, 0x90, 0xF8, + 0x61, 0x08, 0x0A, 0x22, 0x04, 0xEB, 0x40, 0x00, 0x00, 0xF0, 0xF6, 0xFD, + 0x81, 0x46, 0x4F, 0xF4, + 0x80, 0x70, 0x86, 0x42, 0x00, 0xD9, 0x06, 0x46, 0x1F, 0x2D, 0x00, 0xD9, + 0x1F, 0x25, 0x6D, 0x1C, + 0x6E, 0x43, 0x70, 0x00, 0x7F, 0x1C, 0x78, 0x43, 0xC0, 0xEB, 0x40, 0x20, + 0x00, 0xFB, 0x08, 0xF0, + 0x00, 0xFB, 0x09, 0xF0, 0xC0, 0x13, 0xB0, 0xFA, 0x80, 0xF0, 0xC0, 0xF1, + 0x20, 0x00, 0xC0, 0xB2, + 0xBD, 0xE8, 0xF0, 0x87, 0x70, 0xB5, 0x06, 0x00, 0x03, 0xD0, 0xBD, 0xE8, + 0x70, 0x40, 0x00, 0xF0, + 0x64, 0xBD, 0x00, 0xF0, 0x18, 0xF8, 0x01, 0x28, 0x14, 0xD1, 0xA9, 0x4D, + 0xA9, 0x4C, 0x28, 0x78, + 0x01, 0x28, 0x09, 0xD0, 0xA8, 0x48, 0xA0, 0x60, 0x68, 0x78, 0xE0, 0x73, + 0xE8, 0x78, 0xA0, 0x73, + 0xE8, 0x88, 0xA0, 0x81, 0x30, 0x46, 0xE8, 0xE7, 0x6A, 0x79, 0x29, 0x79, + 0xA2, 0x48, 0x00, 0xF0, + 0xB6, 0xFE, 0xF0, 0xE7, 0x70, 0xBD, 0x9E, 0x4B, 0x10, 0xB5, 0xD9, 0x78, + 0x9F, 0x4C, 0x49, 0x1C, + 0xCA, 0xB2, 0xDA, 0x70, 0x99, 0x78, 0x01, 0x20, 0x8A, 0x42, 0x1B, 0xD8, + 0x19, 0x79, 0x58, 0x79, + 0x41, 0x43, 0x99, 0x48, 0xFD, 0xF7, 0xA9, 0xF9, 0xEA, 0xF7, 0x33, 0xFC, + 0xA4, 0xF8, 0x76, 0x00, + 0x92, 0x48, 0x00, 0x68, 0x90, 0xF8, 0xB0, 0x00, 0xC0, 0x07, 0x06, 0xD0, + 0x94, 0xF8, 0x4D, 0x00, + 0x20, 0xF0, 0x03, 0x00, 0x80, 0x1C, 0x84, 0xF8, 0x4D, 0x00, 0x91, 0x49, + 0x20, 0x46, 0xEA, 0xF7, + 0x15, 0xFE, 0x00, 0x20, 0x10, 0xBD, 0x70, 0xB5, 0x04, 0x46, 0xEA, 0xF7, + 0x15, 0xFC, 0x8A, 0x48, + 0xEA, 0xF7, 0x17, 0xFC, 0xA4, 0xF8, 0x76, 0x00, 0x00, 0xF0, 0x66, 0xFD, + 0x83, 0x4D, 0x01, 0x46, + 0x28, 0x68, 0x90, 0xF8, 0xE7, 0x00, 0xC0, 0xF3, 0x41, 0x12, 0x20, 0x46, + 0xEA, 0xF7, 0xAC, 0xFD, + 0x00, 0xF0, 0x5E, 0xFD, 0x02, 0x46, 0x0B, 0x46, 0x20, 0x46, 0xEA, 0xF7, + 0xD1, 0xFD, 0xA0, 0x79, + 0x20, 0xF0, 0x02, 0x00, 0xA0, 0x71, 0x94, 0xF8, 0x6F, 0x00, 0x20, 0xF0, + 0x07, 0x00, 0x84, 0xF8, + 0x6F, 0x00, 0x94, 0xF8, 0x6E, 0x00, 0x20, 0xF0, 0x0F, 0x00, 0x84, 0xF8, + 0x6E, 0x00, 0x28, 0x68, + 0x90, 0xF8, 0xE7, 0x00, 0xC0, 0xF3, 0x41, 0x11, 0x20, 0x46, 0xEA, 0xF7, + 0xD3, 0xFD, 0x28, 0x68, + 0x90, 0xF8, 0xE7, 0x00, 0x10, 0xF0, 0x60, 0x0F, 0x94, 0xF8, 0x5E, 0x00, + 0x7E, 0xD0, 0x40, 0xF0, + 0x01, 0x00, 0x00, 0xBF, 0x84, 0xF8, 0x5E, 0x00, 0x20, 0x46, 0x00, 0xF0, + 0x6B, 0xFE, 0x04, 0x21, + 0x20, 0x46, 0xEA, 0xF7, 0x04, 0xFC, 0x28, 0x68, 0x90, 0xF8, 0xF1, 0x00, + 0xA0, 0x70, 0x28, 0x68, + 0x21, 0x8E, 0xB0, 0xF8, 0xF6, 0x20, 0x62, 0xF3, 0x08, 0x01, 0x21, 0x86, + 0x90, 0xF8, 0xF2, 0x00, + 0x00, 0xF0, 0x0F, 0x01, 0x20, 0x46, 0xEA, 0xF7, 0x52, 0xFC, 0x28, 0x68, + 0x62, 0x7D, 0x90, 0xF8, + 0xEA, 0x10, 0x61, 0xF3, 0x03, 0x02, 0x62, 0x75, 0xB0, 0xF8, 0xED, 0x20, + 0x21, 0x8A, 0x62, 0xF3, + 0x0B, 0x01, 0x21, 0x82, 0x90, 0xF8, 0xF3, 0x10, 0xA2, 0x7D, 0x61, 0xF3, + 0x03, 0x02, 0xA2, 0x75, + 0x10, 0xF8, 0xF2, 0x1F, 0x0A, 0x09, 0xA1, 0x7C, 0x62, 0xF3, 0x01, 0x01, + 0xA1, 0x74, 0x10, 0xF8, + 0x0C, 0x29, 0x92, 0x09, 0x62, 0xF3, 0x87, 0x11, 0xA1, 0x74, 0x01, 0x78, + 0xE2, 0x79, 0x61, 0xF3, + 0x00, 0x02, 0xE2, 0x71, 0xC1, 0x78, 0x61, 0x72, 0x80, 0x79, 0x01, 0x09, + 0xE0, 0x7A, 0x61, 0xF3, + 0x03, 0x00, 0xE0, 0x72, 0x4B, 0x48, 0xEA, 0xF7, 0x94, 0xFB, 0xA4, 0xF8, + 0x7C, 0x00, 0x29, 0x68, + 0x11, 0xF8, 0xE8, 0x0F, 0xC2, 0x08, 0x94, 0xF8, 0x27, 0x00, 0x62, 0xF3, + 0x83, 0x00, 0x84, 0xF8, + 0x27, 0x00, 0x11, 0xF8, 0x01, 0x29, 0x52, 0x08, 0x62, 0xF3, 0x00, 0x00, + 0x84, 0xF8, 0x27, 0x00, + 0x4A, 0x78, 0x92, 0x08, 0x62, 0xF3, 0x41, 0x00, 0x84, 0xF8, 0x27, 0x00, + 0x08, 0x78, 0xC0, 0xF3, + 0x81, 0x01, 0x20, 0x46, 0xEA, 0xF7, 0xF0, 0xFB, 0x28, 0x68, 0x90, 0xF8, + 0xE7, 0x00, 0x01, 0x09, + 0x14, 0xF8, 0x28, 0x0F, 0x61, 0xF3, 0x04, 0x10, 0x20, 0x70, 0x37, 0x48, + 0xEA, 0xF7, 0x69, 0xFB, + 0xA4, 0xF8, 0x5E, 0x00, 0x28, 0x68, 0x10, 0xF8, 0xE6, 0x1F, 0x00, 0xE0, + 0xA6, 0xE0, 0xCA, 0x08, + 0xE1, 0x7A, 0x62, 0xF3, 0x45, 0x11, 0xE1, 0x72, 0x40, 0x78, 0x41, 0x08, + 0x20, 0x78, 0x61, 0xF3, + 0x45, 0x10, 0x04, 0xF8, 0x28, 0x09, 0x20, 0x46, 0x00, 0xF0, 0x2A, 0xFD, + 0x28, 0x68, 0x90, 0xF8, + 0xEC, 0x00, 0xC0, 0xF3, 0x81, 0x01, 0x20, 0x46, 0xEA, 0xF7, 0xB3, 0xFB, + 0x14, 0xF8, 0x32, 0x0F, + 0x20, 0xF0, 0x0F, 0x00, 0x0A, 0x30, 0x20, 0xF0, 0xF0, 0x00, 0xA0, 0x30, + 0x04, 0xF8, 0x0A, 0x09, + 0x29, 0x68, 0x11, 0xF8, 0xE6, 0x0F, 0x82, 0x09, 0xE0, 0x7A, 0x62, 0xF3, + 0x00, 0x00, 0xE0, 0x72, + 0x0A, 0x78, 0x52, 0x09, 0x62, 0xF3, 0x41, 0x00, 0xE0, 0x72, 0x4A, 0x79, + 0xD2, 0x09, 0x62, 0xF3, + 0x82, 0x00, 0xE0, 0x72, 0x88, 0x7B, 0x62, 0x79, 0x60, 0xF3, 0x04, 0x02, + 0x62, 0x71, 0xC9, 0x7B, + 0x20, 0x79, 0x61, 0xF3, 0x04, 0x00, 0x20, 0x71, 0x14, 0x48, 0x94, 0xF8, + 0x37, 0x10, 0x40, 0x78, + 0x60, 0xF3, 0x04, 0x01, 0x84, 0xF8, 0x37, 0x10, 0x11, 0x48, 0xEA, 0xF7, + 0x1A, 0xFB, 0xA4, 0xF8, + 0x52, 0x00, 0x28, 0x68, 0xE1, 0x88, 0xB0, 0xF8, 0xEF, 0x20, 0x62, 0xF3, + 0x0B, 0x01, 0xE1, 0x80, + 0x90, 0xF8, 0xEB, 0x10, 0xCA, 0x08, 0x21, 0x7C, 0x14, 0xE0, 0x00, 0x00, + 0x4C, 0x07, 0x10, 0x00, + 0xE6, 0x07, 0x10, 0x00, 0xB4, 0x84, 0x10, 0x00, 0x14, 0xA6, 0x01, 0x20, + 0x00, 0x84, 0x10, 0x00, + 0x95, 0x5F, 0x01, 0x00, 0x78, 0x8B, 0x01, 0x20, 0x20, 0xBF, 0x01, 0x20, + 0x16, 0xBF, 0x01, 0x20, + 0x00, 0x9B, 0x01, 0x20, 0x62, 0xF3, 0x02, 0x01, 0x21, 0x74, 0x10, 0xF8, + 0xDF, 0x1F, 0xA2, 0x78, + 0x61, 0xF3, 0x03, 0x02, 0xA2, 0x70, 0x01, 0x78, 0x0A, 0x09, 0xE1, 0x78, + 0x62, 0xF3, 0x03, 0x01, + 0xE1, 0x70, 0xC1, 0x7A, 0x0A, 0x09, 0x61, 0x78, 0x62, 0xF3, 0x02, 0x01, + 0x61, 0x70, 0x41, 0x7A, + 0x8A, 0x09, 0x21, 0x78, 0x62, 0xF3, 0x00, 0x01, 0x21, 0x70, 0xC1, 0x79, + 0xCA, 0x09, 0x21, 0x7B, + 0x62, 0xF3, 0x00, 0x01, 0x21, 0x73, 0x94, 0xF8, 0x37, 0x10, 0x41, 0xF0, + 0x20, 0x01, 0x84, 0xF8, + 0x37, 0x10, 0x21, 0x8F, 0x6F, 0xF3, 0x08, 0x01, 0x01, 0xF5, 0x80, 0x71, + 0x21, 0x87, 0x00, 0x21, + 0xC4, 0xF8, 0x3A, 0x10, 0x01, 0x7B, 0x8A, 0x09, 0xE1, 0x7A, 0x62, 0xF3, + 0xC3, 0x01, 0xE1, 0x72, + 0xC0, 0x79, 0x00, 0x09, 0x60, 0xF3, 0x04, 0x11, 0xE1, 0x72, 0x70, 0xBD, + 0x20, 0xF0, 0x01, 0x00, + 0xD8, 0xE6, 0x70, 0xB5, 0x04, 0x46, 0xEA, 0xF7, 0xAF, 0xFA, 0xFD, 0x48, + 0xEA, 0xF7, 0xB1, 0xFA, + 0xA4, 0xF8, 0x76, 0x00, 0x00, 0xF0, 0x00, 0xFC, 0xFA, 0x4D, 0x01, 0x46, + 0x28, 0x68, 0x90, 0xF8, + 0xC9, 0x00, 0xC0, 0xF3, 0x41, 0x12, 0x20, 0x46, 0xEA, 0xF7, 0x46, 0xFC, + 0x00, 0xF0, 0xF8, 0xFB, + 0x02, 0x46, 0x0B, 0x46, 0x20, 0x46, 0xEA, 0xF7, 0x6B, 0xFC, 0xA0, 0x79, + 0x20, 0xF0, 0x02, 0x00, + 0xA0, 0x71, 0x94, 0xF8, 0x6F, 0x00, 0x20, 0xF0, 0x07, 0x00, 0x84, 0xF8, + 0x6F, 0x00, 0x94, 0xF8, + 0x6E, 0x00, 0x20, 0xF0, 0x0F, 0x00, 0x84, 0xF8, 0x6E, 0x00, 0x28, 0x68, + 0x90, 0xF8, 0xC9, 0x00, + 0xC0, 0xF3, 0x41, 0x11, 0x20, 0x46, 0xEA, 0xF7, 0x6D, 0xFC, 0x28, 0x68, + 0x90, 0xF8, 0xC9, 0x00, + 0x10, 0xF0, 0x60, 0x0F, 0x94, 0xF8, 0x5E, 0x00, 0x7E, 0xD0, 0x40, 0xF0, + 0x01, 0x00, 0x00, 0xBF, + 0x84, 0xF8, 0x5E, 0x00, 0x20, 0x46, 0x00, 0xF0, 0x05, 0xFD, 0x28, 0x68, + 0x90, 0xF8, 0xD3, 0x00, + 0xA0, 0x70, 0x28, 0x68, 0x21, 0x8E, 0xB0, 0xF8, 0xD8, 0x20, 0x62, 0xF3, + 0x08, 0x01, 0x21, 0x86, + 0x90, 0xF8, 0xD4, 0x00, 0x00, 0xF0, 0x0F, 0x01, 0x20, 0x46, 0xEA, 0xF7, + 0xF0, 0xFA, 0x28, 0x68, + 0x62, 0x7D, 0x90, 0xF8, 0xCC, 0x10, 0x61, 0xF3, 0x03, 0x02, 0x62, 0x75, + 0xB0, 0xF8, 0xCF, 0x20, + 0x21, 0x8A, 0x62, 0xF3, 0x0B, 0x01, 0x21, 0x82, 0x90, 0xF8, 0xD5, 0x10, + 0xA2, 0x7D, 0x61, 0xF3, + 0x03, 0x02, 0xA2, 0x75, 0x90, 0xF8, 0xF2, 0x21, 0xA1, 0x89, 0x62, 0xF3, + 0x0B, 0x01, 0xA1, 0x81, + 0x10, 0xF8, 0xD4, 0x1F, 0x0A, 0x09, 0xA1, 0x7C, 0x62, 0xF3, 0x01, 0x01, + 0xA1, 0x74, 0x10, 0xF8, + 0x0C, 0x29, 0x92, 0x09, 0x62, 0xF3, 0x87, 0x11, 0xA1, 0x74, 0x01, 0x78, + 0xE2, 0x79, 0x61, 0xF3, + 0x00, 0x02, 0xE2, 0x71, 0xC1, 0x78, 0x61, 0x72, 0x80, 0x79, 0x01, 0x09, + 0xE0, 0x7A, 0x61, 0xF3, + 0x03, 0x00, 0xE0, 0x72, 0xBC, 0x48, 0xEA, 0xF7, 0x2C, 0xFA, 0xA4, 0xF8, + 0x7C, 0x00, 0x29, 0x68, + 0x11, 0xF8, 0xCA, 0x0F, 0xC2, 0x08, 0x94, 0xF8, 0x27, 0x00, 0x62, 0xF3, + 0x83, 0x00, 0x84, 0xF8, + 0x27, 0x00, 0x11, 0xF8, 0x01, 0x29, 0x52, 0x08, 0x62, 0xF3, 0x00, 0x00, + 0x84, 0xF8, 0x27, 0x00, + 0x4A, 0x78, 0x92, 0x08, 0x62, 0xF3, 0x41, 0x00, 0x84, 0xF8, 0x27, 0x00, + 0x08, 0x78, 0xC0, 0xF3, + 0x81, 0x01, 0x20, 0x46, 0xEA, 0xF7, 0x88, 0xFA, 0x28, 0x68, 0x90, 0xF8, + 0xC9, 0x00, 0x01, 0x09, + 0x14, 0xF8, 0x28, 0x0F, 0x61, 0xF3, 0x04, 0x10, 0x20, 0x70, 0xA8, 0x48, + 0xEA, 0xF7, 0x01, 0xFA, + 0xA4, 0xF8, 0x5E, 0x00, 0x28, 0x68, 0x00, 0xE0, 0xA1, 0xE0, 0x10, 0xF8, + 0xC8, 0x1F, 0xCA, 0x08, + 0xE1, 0x7A, 0x62, 0xF3, 0x45, 0x11, 0xE1, 0x72, 0x40, 0x78, 0x41, 0x08, + 0x20, 0x78, 0x61, 0xF3, + 0x45, 0x10, 0x04, 0xF8, 0x28, 0x09, 0x20, 0x46, 0x00, 0xF0, 0xC2, 0xFB, + 0x28, 0x68, 0x90, 0xF8, + 0xCE, 0x00, 0xC0, 0xF3, 0x81, 0x01, 0x20, 0x46, 0xEA, 0xF7, 0x4B, 0xFA, + 0x14, 0xF8, 0x32, 0x0F, + 0x20, 0xF0, 0x0F, 0x00, 0x0A, 0x30, 0x20, 0xF0, 0xF0, 0x00, 0xA0, 0x30, + 0x04, 0xF8, 0x0A, 0x09, + 0x29, 0x68, 0x11, 0xF8, 0xC8, 0x0F, 0x82, 0x09, 0xE0, 0x7A, 0x62, 0xF3, + 0x00, 0x00, 0xE0, 0x72, + 0x0A, 0x78, 0x52, 0x09, 0x62, 0xF3, 0x41, 0x00, 0xE0, 0x72, 0x4A, 0x79, + 0xD2, 0x09, 0x62, 0xF3, + 0x82, 0x00, 0xE0, 0x72, 0x88, 0x7B, 0x62, 0x79, 0x60, 0xF3, 0x04, 0x02, + 0x62, 0x71, 0xC8, 0x7B, + 0x21, 0x79, 0x60, 0xF3, 0x04, 0x01, 0x86, 0x48, 0x21, 0x71, 0x94, 0xF8, + 0x37, 0x10, 0x40, 0x78, + 0x60, 0xF3, 0x04, 0x01, 0x84, 0xF8, 0x37, 0x10, 0x82, 0x48, 0xEA, 0xF7, + 0xB2, 0xF9, 0xA4, 0xF8, + 0x52, 0x00, 0x28, 0x68, 0xE1, 0x88, 0xB0, 0xF8, 0xD1, 0x20, 0x62, 0xF3, + 0x0B, 0x01, 0xE1, 0x80, + 0x10, 0xF8, 0xCD, 0x1F, 0xCA, 0x08, 0x21, 0x7C, 0x62, 0xF3, 0x02, 0x01, + 0x21, 0x74, 0x10, 0xF8, + 0x12, 0x1F, 0xA2, 0x78, 0x61, 0xF3, 0x03, 0x02, 0xA2, 0x70, 0x10, 0xF8, + 0x17, 0x19, 0x0A, 0x09, + 0xE1, 0x78, 0x62, 0xF3, 0x03, 0x01, 0xE1, 0x70, 0x01, 0x79, 0x0A, 0x09, + 0x61, 0x78, 0x62, 0xF3, + 0x02, 0x01, 0x61, 0x70, 0x81, 0x78, 0x8A, 0x09, 0x21, 0x78, 0x62, 0xF3, + 0x00, 0x01, 0x21, 0x70, + 0x01, 0x78, 0xCA, 0x09, 0x21, 0x7B, 0x62, 0xF3, 0x00, 0x01, 0x21, 0x73, + 0x01, 0x79, 0xCA, 0x09, + 0x94, 0xF8, 0x37, 0x10, 0x62, 0xF3, 0x45, 0x11, 0x84, 0xF8, 0x37, 0x10, + 0x22, 0x8F, 0x01, 0xF0, + 0x1F, 0x01, 0x6F, 0xF3, 0x08, 0x02, 0x02, 0xF5, 0x80, 0x72, 0x22, 0x87, + 0x83, 0x7B, 0xC2, 0x7A, + 0x03, 0xF0, 0x1F, 0x03, 0x5B, 0x1C, 0xC3, 0xEB, 0x43, 0x23, 0x5B, 0x00, + 0x5A, 0x43, 0xCA, 0x40, + 0x51, 0x08, 0xC4, 0xF8, 0x3A, 0x10, 0x41, 0x79, 0x8A, 0x09, 0xE1, 0x7A, + 0x62, 0xF3, 0xC3, 0x01, + 0xE1, 0x72, 0x00, 0x78, 0x00, 0x09, 0x60, 0xF3, 0x04, 0x11, 0xE1, 0x72, + 0x70, 0xBD, 0x20, 0xF0, + 0x01, 0x00, 0xDD, 0xE6, 0x10, 0xB5, 0x04, 0x46, 0xA4, 0x22, 0x53, 0x49, + 0x00, 0xF0, 0xA5, 0xFD, + 0x52, 0x48, 0xC1, 0x78, 0xA0, 0x89, 0x61, 0xF3, 0x0B, 0x00, 0xA0, 0x81, + 0x10, 0xBD, 0x70, 0xB5, + 0x04, 0x46, 0xFF, 0xF7, 0xEF, 0xFF, 0x00, 0xF0, 0x97, 0xFA, 0x01, 0x46, + 0x00, 0x22, 0x20, 0x46, + 0xEA, 0xF7, 0xE2, 0xFA, 0x4A, 0x48, 0xEA, 0xF7, 0x3C, 0xF9, 0x42, 0x4D, + 0xA4, 0xF8, 0x7C, 0x00, + 0x28, 0x68, 0x90, 0xF8, 0xF8, 0x00, 0xA0, 0x70, 0x28, 0x68, 0x90, 0xF8, + 0xF9, 0x00, 0x00, 0xF0, + 0x0F, 0x01, 0x20, 0x46, 0xEA, 0xF7, 0xBB, 0xF9, 0x28, 0x68, 0x10, 0xF8, + 0xF9, 0x1F, 0x0A, 0x09, + 0xA1, 0x7C, 0x62, 0xF3, 0x01, 0x01, 0xA1, 0x74, 0x02, 0x78, 0x92, 0x09, + 0x62, 0xF3, 0x87, 0x11, + 0xA1, 0x74, 0x81, 0x78, 0x94, 0xF8, 0x2D, 0x20, 0x61, 0xF3, 0x04, 0x02, + 0x84, 0xF8, 0x2D, 0x20, + 0xC1, 0x78, 0x94, 0xF8, 0x2C, 0x20, 0x61, 0xF3, 0x04, 0x02, 0x84, 0xF8, + 0x2C, 0x20, 0x40, 0x78, + 0xA1, 0x7D, 0x60, 0xF3, 0x03, 0x01, 0xA1, 0x75, 0x00, 0x21, 0x20, 0x46, + 0xEA, 0xF7, 0xF2, 0xFA, + 0x14, 0xF8, 0x5E, 0x0F, 0x20, 0xF0, 0x01, 0x00, 0x04, 0xF8, 0x01, 0x0B, + 0x28, 0x48, 0x21, 0x78, + 0x00, 0x79, 0x60, 0xF3, 0x04, 0x01, 0x04, 0xF8, 0x2F, 0x19, 0x28, 0x68, + 0xB0, 0xF8, 0xFD, 0x10, + 0x20, 0x88, 0x61, 0xF3, 0x08, 0x00, 0x20, 0x80, 0x70, 0xBD, 0x70, 0xB5, + 0x1D, 0x4E, 0x03, 0x28, + 0x34, 0x68, 0x04, 0xF2, 0x5E, 0x62, 0x04, 0xF6, 0x6E, 0x05, 0x15, 0xD0, + 0x04, 0x28, 0x21, 0xD0, + 0x94, 0xF8, 0x51, 0x06, 0x94, 0xF8, 0x50, 0x16, 0x02, 0xEB, 0x40, 0x00, + 0x00, 0xF0, 0x77, 0xFA, + 0x30, 0x68, 0x90, 0xF8, 0x60, 0x18, 0x90, 0xF8, 0x61, 0x08, 0x00, 0xBF, + 0x05, 0xEB, 0x40, 0x00, + 0xBD, 0xE8, 0x70, 0x40, 0x00, 0xF0, 0x82, 0xBA, 0x94, 0xF8, 0x53, 0x06, + 0x94, 0xF8, 0x52, 0x16, + 0x02, 0xEB, 0x40, 0x00, 0x00, 0xF0, 0x63, 0xFA, 0x30, 0x68, 0x90, 0xF8, + 0x62, 0x18, 0x90, 0xF8, + 0x63, 0x08, 0xEB, 0xE7, 0x94, 0xF8, 0x55, 0x06, 0x94, 0xF8, 0x54, 0x16, + 0x02, 0xEB, 0x40, 0x00, + 0x00, 0xF0, 0x55, 0xFA, 0x30, 0x68, 0x90, 0xF8, 0x64, 0x18, 0x90, 0xF8, + 0x65, 0x08, 0xDD, 0xE7, + 0x14, 0xA6, 0x01, 0x20, 0x4C, 0x07, 0x10, 0x00, 0x78, 0x8B, 0x01, 0x20, + 0x20, 0xBF, 0x01, 0x20, + 0x16, 0xBF, 0x01, 0x20, 0x00, 0x9B, 0x01, 0x20, 0x5C, 0x83, 0x10, 0x00, + 0x74, 0x07, 0x10, 0x00, + 0x08, 0x92, 0x01, 0x20, 0x2D, 0xE9, 0xF0, 0x47, 0x85, 0x4D, 0x4F, 0xF0, + 0x01, 0x09, 0x00, 0x26, + 0x85, 0xF8, 0x00, 0x90, 0xAE, 0x70, 0x83, 0x4F, 0xDF, 0xF8, 0x0C, 0x82, + 0x04, 0x46, 0xEE, 0x70, + 0x09, 0x29, 0x66, 0xD2, 0xDF, 0xE8, 0x01, 0xF0, 0x65, 0x05, 0x09, 0x18, + 0x44, 0x2F, 0x48, 0x5E, + 0x62, 0x00, 0x20, 0x46, 0xFF, 0xF7, 0x36, 0xFF, 0x5B, 0xE0, 0x20, 0x46, + 0xFF, 0xF7, 0x32, 0xFF, + 0xB8, 0x78, 0x94, 0xF8, 0x5F, 0x10, 0x60, 0xF3, 0x04, 0x01, 0x84, 0xF8, + 0x5F, 0x10, 0xD8, 0xF8, + 0x00, 0x00, 0x90, 0xF8, 0xFF, 0x00, 0x12, 0xE0, 0x20, 0x46, 0xFF, 0xF7, + 0x23, 0xFF, 0xD8, 0xF8, + 0x00, 0x10, 0x91, 0xF8, 0x01, 0x11, 0xA1, 0x70, 0xF9, 0x78, 0x94, 0xF8, + 0x5F, 0x20, 0x61, 0xF3, + 0x04, 0x02, 0x84, 0xF8, 0x5F, 0x20, 0xD8, 0xF8, 0x00, 0x00, 0x90, 0xF8, + 0x00, 0x01, 0x00, 0xF0, + 0x03, 0x00, 0xA8, 0x70, 0x35, 0xE0, 0x20, 0x46, 0xFF, 0xF7, 0x0C, 0xFF, + 0x00, 0x21, 0x20, 0x46, + 0xEA, 0xF7, 0xED, 0xF8, 0x94, 0xF8, 0x5E, 0x00, 0x20, 0xF0, 0x01, 0x00, + 0x84, 0xF8, 0x5E, 0x00, + 0x78, 0x79, 0x94, 0xF8, 0x5F, 0x10, 0x60, 0xF3, 0x04, 0x01, 0x84, 0xF8, + 0x5F, 0x10, 0x17, 0xE0, + 0x20, 0x46, 0xFF, 0xF7, 0x04, 0xFF, 0x1C, 0xE0, 0x20, 0x46, 0xFF, 0xF7, + 0x00, 0xFF, 0x00, 0x21, + 0x20, 0x46, 0xEA, 0xF7, 0xD4, 0xF8, 0xB8, 0x79, 0x94, 0xF8, 0x5F, 0x10, + 0x60, 0xF3, 0x04, 0x01, + 0x84, 0xF8, 0x5F, 0x10, 0x94, 0xF8, 0x5E, 0x00, 0x20, 0xF0, 0x01, 0x00, + 0x84, 0xF8, 0x5E, 0x00, + 0x2E, 0x70, 0x06, 0xE0, 0x20, 0x46, 0xFF, 0xF7, 0x7C, 0xFD, 0xF9, 0xE7, + 0x20, 0x46, 0x00, 0xF0, + 0x59, 0xF8, 0x20, 0x46, 0x00, 0xF0, 0xAA, 0xFA, 0x94, 0xF8, 0x27, 0x00, + 0xC0, 0x07, 0x01, 0xD0, + 0x6E, 0x70, 0x01, 0xE0, 0x85, 0xF8, 0x01, 0x90, 0xA0, 0x89, 0xC0, 0xF3, + 0x0B, 0x00, 0xE8, 0x80, + 0xBD, 0xE8, 0xF0, 0x87, 0x7F, 0xB5, 0x00, 0xF0, 0x6F, 0xF9, 0x01, 0x90, + 0x00, 0xF0, 0x70, 0xF9, + 0xCD, 0xE9, 0x02, 0x01, 0x04, 0x21, 0x01, 0xA8, 0xEA, 0xF7, 0xBD, 0xFC, + 0x3C, 0x4C, 0x08, 0x21, + 0x20, 0x71, 0x02, 0xA8, 0xEA, 0xF7, 0xB7, 0xFC, 0x60, 0x71, 0x3A, 0x48, + 0x00, 0x21, 0x80, 0x1E, + 0x38, 0x4D, 0x40, 0xF8, 0x02, 0x1F, 0x01, 0x24, 0x41, 0x60, 0x01, 0x72, + 0xE0, 0xB2, 0xFF, 0xF7, + 0xF5, 0xFA, 0x28, 0x55, 0x64, 0x1C, 0x09, 0x2C, 0xF8, 0xDB, 0x34, 0x48, + 0xFF, 0xF7, 0xDB, 0xFB, + 0x7F, 0xBD, 0x70, 0xB5, 0x31, 0x4D, 0x04, 0x79, 0xA4, 0x35, 0x8C, 0xB1, + 0x21, 0x46, 0x28, 0x46, + 0xFF, 0xF7, 0x48, 0xFF, 0xA8, 0x7C, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xF0, + 0xAF, 0xF9, 0x20, 0x46, + 0xFF, 0xF7, 0xF3, 0xFE, 0x2A, 0x49, 0x28, 0x46, 0xEA, 0xF7, 0xD8, 0xF9, + 0x00, 0x20, 0x70, 0xBD, + 0x01, 0x20, 0x70, 0xBD, 0x40, 0xF2, 0xFF, 0x10, 0xC1, 0xEB, 0x41, 0x21, + 0x91, 0xFB, 0xF0, 0xF0, + 0x00, 0x20, 0x70, 0x47, 0x10, 0xB5, 0x04, 0x46, 0xFF, 0xF7, 0x7C, 0xFE, + 0x94, 0xF8, 0x37, 0x00, + 0x40, 0xF0, 0x10, 0x00, 0x84, 0xF8, 0x37, 0x00, 0xA1, 0x89, 0x94, 0xF8, + 0x33, 0x00, 0x6F, 0xF3, + 0x0B, 0x01, 0x10, 0x31, 0xA1, 0x81, 0x21, 0x8A, 0x20, 0xF0, 0x23, 0x00, + 0x6F, 0xF3, 0x0B, 0x01, + 0x40, 0x31, 0x21, 0x82, 0x08, 0x21, 0xA1, 0x70, 0x94, 0xF8, 0x5F, 0x10, + 0x40, 0xF0, 0x0C, 0x00, + 0x21, 0xF0, 0x1F, 0x01, 0x84, 0xF8, 0x5F, 0x10, 0x21, 0x8E, 0x6F, 0xF3, + 0x08, 0x01, 0x21, 0x86, + 0x84, 0xF8, 0x33, 0x00, 0x0C, 0x48, 0x01, 0x21, 0x00, 0x68, 0x90, 0xF8, + 0xE7, 0x00, 0xC0, 0xF3, + 0x41, 0x12, 0x20, 0x46, 0xEA, 0xF7, 0x48, 0xF9, 0x94, 0xF8, 0x28, 0x00, + 0x40, 0xF0, 0x20, 0x00, + 0x84, 0xF8, 0x28, 0x00, 0x20, 0x46, 0xBD, 0xE8, 0x10, 0x40, 0x00, 0xF0, + 0x71, 0xB9, 0x00, 0x00, + 0xE6, 0x07, 0x10, 0x00, 0x16, 0xBF, 0x01, 0x20, 0x4C, 0x07, 0x10, 0x00, + 0x5C, 0x83, 0x10, 0x00, + 0x95, 0x5F, 0x01, 0x00, 0x70, 0xB5, 0x00, 0xF0, 0x72, 0xFA, 0xFF, 0xF7, + 0x6B, 0xFF, 0xFF, 0xF7, + 0x17, 0xFA, 0xFF, 0xF7, 0xD5, 0xF8, 0xFE, 0xF7, 0xD9, 0xFE, 0xFE, 0xF7, + 0xF7, 0xF9, 0xFD, 0xF7, + 0x67, 0xFF, 0xFE, 0xF7, 0xA8, 0xFA, 0x50, 0x48, 0x00, 0x25, 0x70, 0x21, + 0x45, 0x60, 0x4F, 0x48, + 0x00, 0xF0, 0x6C, 0xFC, 0x4E, 0x48, 0x04, 0x68, 0x94, 0xF8, 0xE7, 0x00, + 0xC0, 0x06, 0x05, 0xD5, + 0x28, 0x22, 0x04, 0xF5, 0x0F, 0x61, 0x49, 0x48, 0x00, 0xF0, 0xBF, 0xFB, + 0x94, 0xF8, 0x33, 0x01, + 0xC0, 0x06, 0x0D, 0xD5, 0x45, 0x48, 0x20, 0x22, 0x04, 0xF6, 0x16, 0x11, + 0x28, 0x30, 0x00, 0xF0, + 0xB4, 0xFB, 0x42, 0x48, 0x28, 0x22, 0x04, 0xF6, 0x3F, 0x11, 0x48, 0x30, + 0x00, 0xF0, 0xAD, 0xFB, + 0x40, 0x48, 0x05, 0x70, 0x70, 0xBD, 0x38, 0xB1, 0x01, 0x28, 0x07, 0xD0, + 0x02, 0x28, 0x07, 0xD0, + 0x17, 0x20, 0x00, 0x21, 0xFB, 0xF7, 0x65, 0xBD, 0x14, 0x20, 0xFA, 0xE7, + 0x15, 0x20, 0xF8, 0xE7, + 0x16, 0x20, 0xF6, 0xE7, 0x00, 0x21, 0x01, 0x60, 0x41, 0x60, 0x81, 0x60, + 0xC1, 0x60, 0x70, 0x47, + 0x70, 0xB5, 0x31, 0x4C, 0x34, 0x4D, 0x20, 0x68, 0x09, 0xE0, 0x00, 0xBF, + 0x55, 0xF8, 0x20, 0x10, + 0x32, 0x48, 0x88, 0x47, 0x00, 0x28, 0x11, 0xD0, 0x20, 0x68, 0x40, 0x1C, + 0x20, 0x60, 0x09, 0x28, + 0xF4, 0xD3, 0x60, 0x68, 0x2D, 0x49, 0x40, 0x1C, 0x10, 0x31, 0x00, 0x22, + 0x60, 0x60, 0x0A, 0x70, + 0x48, 0x60, 0x2A, 0x48, 0x01, 0x68, 0x10, 0x30, 0x88, 0x47, 0x01, 0x20, + 0x70, 0xBD, 0x70, 0xB5, + 0x04, 0x46, 0x00, 0x79, 0x00, 0x25, 0x60, 0xB9, 0xE0, 0x79, 0x50, 0xB9, + 0xA0, 0x79, 0x40, 0xB9, + 0x20, 0x7A, 0x30, 0xB9, 0x60, 0x79, 0x20, 0xB9, 0x60, 0x7A, 0x10, 0xB9, + 0xA0, 0x7A, 0x00, 0x28, + 0x12, 0xD0, 0x1E, 0x48, 0x34, 0x21, 0x10, 0x30, 0x00, 0xF0, 0x00, 0xFC, + 0x94, 0xE8, 0x4E, 0x00, + 0x1A, 0x48, 0x80, 0xE8, 0x4E, 0x00, 0x14, 0x48, 0x16, 0x49, 0x05, 0x60, + 0x60, 0x7B, 0x08, 0x70, + 0x16, 0x48, 0xFF, 0xF7, 0xBD, 0xFF, 0x01, 0x20, 0x70, 0xBD, 0x0F, 0x49, + 0x50, 0xB1, 0x4A, 0x68, + 0x52, 0x1C, 0x4A, 0x60, 0x11, 0x49, 0x10, 0x31, 0x08, 0x70, 0x10, 0x48, + 0x4A, 0x60, 0x01, 0x68, + 0x10, 0x30, 0x08, 0x47, 0x08, 0x68, 0x40, 0x1C, 0x08, 0x60, 0x0C, 0x48, + 0xA8, 0xE7, 0x00, 0xF0, + 0x3B, 0xB8, 0x00, 0xF0, 0x3D, 0xB8, 0x00, 0xF0, 0x40, 0xB8, 0x00, 0xF0, + 0x43, 0xB8, 0x00, 0xF0, + 0x48, 0xB8, 0x00, 0xF0, 0x4B, 0xB8, 0x00, 0x00, 0xF0, 0x07, 0x10, 0x00, + 0x20, 0xBF, 0x01, 0x20, + 0x4C, 0x07, 0x10, 0x00, 0xF9, 0x07, 0x10, 0x00, 0x0C, 0x7D, 0x01, 0x00, + 0xA4, 0x84, 0x10, 0x00, + 0x70, 0xB5, 0x00, 0x23, 0x14, 0x46, 0x08, 0xE0, 0x31, 0xF8, 0x13, 0x50, + 0x31, 0xF8, 0x14, 0x60, + 0x35, 0x44, 0x20, 0xF8, 0x13, 0x50, 0x5B, 0x1C, 0x64, 0x1C, 0x93, 0x42, + 0xF4, 0xDB, 0x70, 0xBD, + 0x30, 0xB4, 0x00, 0x24, 0x43, 0xB9, 0x0B, 0xE0, 0x30, 0xF8, 0x14, 0x30, + 0x31, 0xF8, 0x14, 0x50, + 0x2B, 0x44, 0x20, 0xF8, 0x14, 0x30, 0x64, 0x1C, 0x94, 0x42, 0xF5, 0xDB, + 0x30, 0xBC, 0x70, 0x47, + 0x30, 0xBC, 0x52, 0x00, 0x00, 0xF0, 0x01, 0xBB, 0xBF, 0x48, 0x00, 0x68, + 0x80, 0x6A, 0x70, 0x47, + 0xBD, 0x48, 0x00, 0x68, 0x41, 0x6A, 0x00, 0x6A, 0x70, 0x47, 0xBB, 0x48, + 0x00, 0x68, 0xD0, 0xF8, + 0x6A, 0x04, 0x70, 0x47, 0xB8, 0x48, 0x00, 0x68, 0xD0, 0xF8, 0x66, 0x14, + 0xD0, 0xF8, 0x62, 0x04, + 0x70, 0x47, 0xB5, 0x48, 0x00, 0x68, 0xD0, 0xF8, 0x38, 0x0A, 0x70, 0x47, + 0xB2, 0x48, 0x00, 0x68, + 0x00, 0xF5, 0x23, 0x60, 0x03, 0xC8, 0x70, 0x47, 0xF0, 0xB5, 0x00, 0x24, + 0x25, 0x46, 0xAF, 0x4E, + 0x4F, 0xF4, 0x00, 0x67, 0x14, 0xE0, 0x00, 0xBF, 0x30, 0xF8, 0x15, 0x30, + 0xC3, 0xF3, 0x0B, 0x03, + 0x5F, 0xEA, 0xD3, 0x2C, 0x00, 0xD0, 0x33, 0x43, 0x00, 0x2B, 0x00, 0xDA, + 0x5B, 0x42, 0x9C, 0x42, + 0x00, 0xDA, 0x9C, 0xB2, 0xBC, 0x42, 0x02, 0xD3, 0x40, 0xF2, 0xFF, 0x74, + 0x02, 0xE0, 0x6D, 0x1C, + 0x8D, 0x42, 0xE9, 0xDB, 0xD4, 0x40, 0x01, 0x20, 0xA0, 0x40, 0x80, 0xB2, + 0xF0, 0xBD, 0x30, 0xB5, + 0xA0, 0x4B, 0x9F, 0x4C, 0x5A, 0x68, 0x82, 0x42, 0x02, 0xD1, 0xDA, 0x68, + 0x8A, 0x42, 0x0A, 0xD0, + 0x00, 0x22, 0x06, 0xE0, 0x30, 0xF8, 0x12, 0x50, 0xC5, 0xF3, 0x0B, 0x05, + 0x24, 0xF8, 0x12, 0x50, + 0x52, 0x1C, 0x8A, 0x42, 0xF6, 0xDB, 0xD9, 0x60, 0x58, 0x60, 0x30, 0xBD, + 0x30, 0xB5, 0x95, 0x4B, + 0x95, 0x4C, 0x9A, 0x68, 0x82, 0x42, 0x02, 0xD1, 0x1A, 0x69, 0x8A, 0x42, + 0x0D, 0xD0, 0x00, 0x22, + 0x07, 0xE0, 0x00, 0xBF, 0x30, 0xF8, 0x12, 0x50, 0xC5, 0xF3, 0x0B, 0x05, + 0x24, 0xF8, 0x12, 0x50, + 0x52, 0x1C, 0x8A, 0x42, 0xF6, 0xDB, 0x19, 0x61, 0x98, 0x60, 0x30, 0xBD, + 0x70, 0xB5, 0x04, 0x46, + 0x8A, 0x48, 0x01, 0x68, 0x02, 0x29, 0x0A, 0xD1, 0x86, 0x4D, 0x29, 0x78, + 0x8C, 0x42, 0x06, 0xD0, + 0x00, 0xEB, 0x84, 0x00, 0xD0, 0xF8, 0x9C, 0x00, 0xE9, 0xF7, 0x3D, 0xFE, + 0x2C, 0x70, 0x70, 0xBD, + 0x10, 0xB5, 0x04, 0x46, 0x90, 0xF8, 0x28, 0x00, 0x80, 0x06, 0x1B, 0xD5, + 0x7A, 0x48, 0x00, 0x68, + 0x90, 0xF8, 0x70, 0x01, 0x80, 0x07, 0x01, 0xD5, 0x7D, 0x48, 0x0F, 0xE0, + 0x7D, 0x49, 0x08, 0x68, + 0x00, 0x28, 0x0F, 0xD1, 0xA0, 0x7C, 0x94, 0xF8, 0x27, 0x20, 0x80, 0x09, + 0xC0, 0xEB, 0xC0, 0x00, + 0x12, 0xF0, 0x01, 0x0F, 0x01, 0xEB, 0x00, 0x10, 0x05, 0xD0, 0x00, 0x1D, + 0xE9, 0xF7, 0x09, 0xFE, + 0xA4, 0xF8, 0x8C, 0x00, 0x10, 0xBD, 0x00, 0xF5, 0xAA, 0x70, 0xF7, 0xE7, + 0x70, 0xB5, 0x04, 0x46, + 0x90, 0xF8, 0x28, 0x00, 0x80, 0x06, 0x28, 0xD5, 0x67, 0x48, 0x00, 0x68, + 0x90, 0xF8, 0x88, 0x01, + 0xC0, 0x06, 0x08, 0xD5, 0x6A, 0x48, 0x28, 0x30, 0xE9, 0xF7, 0xF3, 0xFD, + 0x24, 0xF8, 0x8E, 0x0F, + 0x67, 0x48, 0x48, 0x30, 0x16, 0xE0, 0x67, 0x48, 0x01, 0x68, 0x00, 0x29, + 0x15, 0xD1, 0xA1, 0x7C, + 0x94, 0xF8, 0x27, 0x20, 0x89, 0x09, 0xC1, 0xEB, 0xC1, 0x01, 0x12, 0xF0, + 0x01, 0x0F, 0x00, 0xEB, + 0x01, 0x15, 0x0B, 0xD0, 0x05, 0xF1, 0x2C, 0x00, 0xE9, 0xF7, 0xDB, 0xFD, + 0x24, 0xF8, 0x8E, 0x0F, + 0x05, 0xF1, 0x4C, 0x00, 0xE9, 0xF7, 0xD5, 0xFD, 0x60, 0x80, 0x70, 0xBD, + 0x05, 0xF5, 0xBE, 0x70, + 0xE9, 0xF7, 0xCF, 0xFD, 0x24, 0xF8, 0x8E, 0x0F, 0x05, 0xF5, 0xCE, 0x70, + 0xF2, 0xE7, 0x70, 0xB5, + 0x04, 0x46, 0x90, 0xF8, 0x28, 0x00, 0x80, 0x06, 0x0B, 0xD5, 0x4B, 0x48, + 0x00, 0x68, 0x90, 0xF8, + 0x88, 0x01, 0xC0, 0x06, 0x06, 0xD5, 0x4E, 0x48, 0x70, 0x30, 0xE9, 0xF7, + 0xBA, 0xFD, 0xA4, 0xF8, + 0x8E, 0x00, 0x70, 0xBD, 0x4B, 0x48, 0x01, 0x68, 0x00, 0x29, 0xFA, 0xD1, + 0xA1, 0x7C, 0x94, 0xF8, + 0x27, 0x20, 0x89, 0x09, 0xC1, 0xEB, 0xC1, 0x01, 0x12, 0xF0, 0x01, 0x0F, + 0x00, 0xEB, 0x01, 0x15, + 0x0C, 0xD0, 0x05, 0xF1, 0x2C, 0x00, 0xE9, 0xF7, 0xA4, 0xFD, 0x24, 0xF8, + 0x8E, 0x0F, 0x05, 0xF1, + 0x4C, 0x00, 0x00, 0xBF, 0xE9, 0xF7, 0x9D, 0xFD, 0x60, 0x80, 0x70, 0xBD, + 0x05, 0xF5, 0xBE, 0x70, + 0xE9, 0xF7, 0x97, 0xFD, 0x24, 0xF8, 0x8E, 0x0F, 0x05, 0xF5, 0xCE, 0x70, + 0xF2, 0xE7, 0x70, 0x47, + 0x70, 0x47, 0x70, 0x47, 0x02, 0x00, 0x4F, 0xF0, 0x00, 0x00, 0x00, 0xD1, + 0x08, 0x46, 0x70, 0x47, + 0x02, 0x46, 0x00, 0x20, 0x01, 0x2A, 0x00, 0xD1, 0x08, 0x46, 0x70, 0x47, + 0x2A, 0x49, 0x10, 0xB5, + 0x09, 0x68, 0x91, 0xF8, 0xB0, 0x10, 0xCA, 0x07, 0x08, 0xD0, 0x2A, 0x4C, + 0x62, 0x78, 0x01, 0x2A, + 0x05, 0xD0, 0x02, 0x21, 0xE9, 0xF7, 0xA3, 0xFD, 0x00, 0x20, 0x60, 0x70, + 0x10, 0xBD, 0xC1, 0xF3, + 0xC1, 0x01, 0xF7, 0xE7, 0x20, 0x49, 0x0A, 0x68, 0x92, 0xF8, 0xB0, 0x20, + 0x93, 0x09, 0x10, 0xF8, + 0x3E, 0x2B, 0x63, 0xF3, 0x82, 0x02, 0x00, 0xF8, 0x3E, 0x2C, 0x0B, 0x68, + 0x93, 0xF8, 0xB0, 0x30, + 0x5B, 0x09, 0x63, 0xF3, 0x45, 0x12, 0x00, 0xF8, 0x3E, 0x2C, 0x09, 0x68, + 0xB1, 0xF8, 0xB7, 0x20, + 0x02, 0x82, 0x91, 0xF8, 0xB6, 0x20, 0x83, 0x7F, 0x62, 0xF3, 0x07, 0x13, + 0x83, 0x77, 0x91, 0xF8, + 0xB3, 0x20, 0x13, 0x09, 0x02, 0x7D, 0x63, 0xF3, 0x06, 0x12, 0x02, 0x75, + 0x31, 0xF8, 0xB1, 0x3F, + 0x02, 0x8B, 0x63, 0xF3, 0x09, 0x02, 0x02, 0x83, 0x11, 0xF8, 0x01, 0x2C, + 0x93, 0x08, 0x82, 0x7D, + 0x63, 0xF3, 0x86, 0x12, 0x82, 0x75, 0x8A, 0x78, 0xC3, 0x7D, 0x62, 0xF3, + 0x03, 0x03, 0xC3, 0x75, + 0xB1, 0xF8, 0x03, 0x30, 0xB0, 0xF8, 0x1B, 0x20, 0x63, 0xF3, 0x08, 0x02, + 0xA0, 0xF8, 0x1B, 0x20, + 0x09, 0x89, 0x41, 0x82, 0x70, 0x47, 0x00, 0x00, 0x4C, 0x07, 0x10, 0x00, + 0x00, 0xF0, 0xFF, 0xFF, + 0x00, 0x9B, 0x01, 0x20, 0xF8, 0x07, 0x10, 0x00, 0x00, 0x9D, 0x01, 0x20, + 0xE8, 0x84, 0x10, 0x00, + 0xE4, 0x9F, 0x01, 0x20, 0x40, 0x9D, 0x01, 0x20, 0x00, 0x20, 0xFF, 0xF7, + 0x1E, 0xBE, 0x70, 0x47, + 0x10, 0xB5, 0xE9, 0xF7, 0x64, 0xFF, 0x08, 0xB1, 0x01, 0x20, 0x10, 0xBD, + 0x0B, 0x48, 0xE9, 0xF7, + 0x47, 0xFF, 0x00, 0x20, 0x10, 0xBD, 0x10, 0xB5, 0x04, 0x46, 0x09, 0x48, + 0x00, 0x68, 0x90, 0xF8, + 0x70, 0x00, 0x40, 0x07, 0x01, 0xD5, 0xE9, 0xF7, 0x47, 0xFF, 0x20, 0x7B, + 0x20, 0xB1, 0x05, 0x49, + 0xEA, 0xF7, 0x12, 0xFC, 0x00, 0x20, 0x10, 0xBD, 0x01, 0x20, 0x10, 0xBD, + 0x6B, 0x6A, 0x01, 0x00, + 0x4C, 0x07, 0x10, 0x00, 0x29, 0x6E, 0x01, 0x00, 0x43, 0x48, 0x01, 0x68, + 0x21, 0xF0, 0x04, 0x01, + 0x01, 0x60, 0x70, 0x47, 0x00, 0xB5, 0xFF, 0xF7, 0xF7, 0xFF, 0x00, 0xBD, + 0x00, 0xB5, 0xFF, 0xF7, + 0xF3, 0xFF, 0x00, 0xBD, 0x00, 0xB5, 0xFF, 0xF7, 0xEF, 0xFF, 0x00, 0xBD, + 0x10, 0xB5, 0xFF, 0xF7, + 0xEB, 0xFF, 0xBD, 0xE8, 0x10, 0x40, 0xEA, 0xF7, 0x32, 0xBB, 0x10, 0xB5, + 0xFF, 0xF7, 0xE4, 0xFF, + 0xBD, 0xE8, 0x10, 0x40, 0xE9, 0xF7, 0xF8, 0xBC, 0x10, 0xB5, 0xFF, 0xF7, + 0xDD, 0xFF, 0xBD, 0xE8, + 0x10, 0x40, 0xEA, 0xF7, 0x28, 0xBC, 0x10, 0xB5, 0xFF, 0xF7, 0xD6, 0xFF, + 0xBD, 0xE8, 0x10, 0x40, + 0xEA, 0xF7, 0x66, 0xBC, 0x10, 0xB5, 0xFF, 0xF7, 0xCF, 0xFF, 0xBD, 0xE8, + 0x10, 0x40, 0xEA, 0xF7, + 0x90, 0xBC, 0x10, 0xB5, 0xFF, 0xF7, 0xC8, 0xFF, 0xBD, 0xE8, 0x10, 0x40, + 0xEA, 0xF7, 0xBA, 0xBC, + 0x10, 0xB5, 0xFF, 0xF7, 0xC1, 0xFF, 0xBD, 0xE8, 0x10, 0x40, 0xEA, 0xF7, + 0xC8, 0xBC, 0x10, 0xB5, + 0xFF, 0xF7, 0xBA, 0xFF, 0xBD, 0xE8, 0x10, 0x40, 0xEA, 0xF7, 0xE6, 0xBC, + 0x10, 0xB5, 0xFF, 0xF7, + 0xB3, 0xFF, 0xBD, 0xE8, 0x10, 0x40, 0xEA, 0xF7, 0xEF, 0xBC, 0x10, 0xB5, + 0xFF, 0xF7, 0xAC, 0xFF, + 0xBD, 0xE8, 0x10, 0x40, 0x00, 0xF0, 0xF2, 0xB9, 0x10, 0xB5, 0xFF, 0xF7, + 0xA5, 0xFF, 0xBD, 0xE8, + 0x10, 0x40, 0x00, 0xF0, 0xC7, 0xB9, 0x00, 0xB5, 0xFF, 0xF7, 0x9E, 0xFF, + 0x00, 0xBD, 0x00, 0xB5, + 0xFF, 0xF7, 0x9A, 0xFF, 0x00, 0xBD, 0x00, 0xB5, 0xFF, 0xF7, 0x96, 0xFF, + 0x00, 0xBD, 0x10, 0xB5, + 0xFF, 0xF7, 0x92, 0xFF, 0xBD, 0xE8, 0x10, 0x40, 0xEA, 0xF7, 0xFE, 0xBC, + 0x00, 0xB5, 0xFF, 0xF7, + 0x8B, 0xFF, 0x00, 0xBD, 0x00, 0xB5, 0xFF, 0xF7, 0x87, 0xFF, 0x00, 0xBD, + 0x00, 0xB5, 0xFF, 0xF7, + 0x83, 0xFF, 0x00, 0xBD, 0x00, 0xB5, 0xFF, 0xF7, 0x7F, 0xFF, 0x00, 0xBD, + 0x00, 0xB5, 0xFF, 0xF7, + 0x7B, 0xFF, 0x00, 0xBD, 0x70, 0x47, 0x00, 0x00, 0x10, 0xED, 0x00, 0xE0, + 0x10, 0xB5, 0x4A, 0xF2, + 0x33, 0x20, 0xE9, 0xF7, 0xB4, 0xFF, 0xFC, 0xF7, 0x19, 0xF8, 0xFB, 0xF7, + 0x2D, 0xFE, 0xE9, 0xF7, + 0x33, 0xFA, 0x00, 0x21, 0x47, 0x20, 0xFB, 0xF7, 0x74, 0xFA, 0x01, 0x20, + 0x10, 0xBD, 0x53, 0xEA, + 0x02, 0x0C, 0x00, 0xF0, 0x69, 0x80, 0x2D, 0xE9, 0xF0, 0x4B, 0x4F, 0xF0, + 0x00, 0x06, 0x00, 0x2B, + 0x1F, 0xBF, 0xB3, 0xFA, 0x83, 0xF5, 0x03, 0xFA, 0x05, 0xF4, 0x24, 0xFA, + 0x05, 0xF6, 0x5E, 0x40, + 0x12, 0xBF, 0x16, 0x43, 0xB2, 0xFA, 0x82, 0xF5, 0x02, 0xFA, 0x05, 0xF4, + 0xC5, 0xF1, 0x20, 0x05, + 0x1E, 0xBF, 0x22, 0xFA, 0x05, 0xFC, 0x44, 0xEA, 0x0C, 0x04, 0x20, 0x35, + 0x56, 0xEA, 0x04, 0x4C, + 0x4F, 0xEA, 0x14, 0x44, 0x18, 0xBF, 0x64, 0x1C, 0x4F, 0xF0, 0x00, 0x08, + 0x4F, 0xF0, 0x00, 0x09, + 0x90, 0x42, 0x71, 0xEB, 0x03, 0x0C, 0x39, 0xD3, 0x00, 0x29, 0x19, 0xBF, + 0xB1, 0xFA, 0x81, 0xF7, + 0x01, 0xFA, 0x07, 0xF6, 0xB0, 0xFA, 0x80, 0xF7, 0x00, 0xFA, 0x07, 0xF6, + 0xC7, 0xF1, 0x20, 0x07, + 0x1E, 0xBF, 0x20, 0xFA, 0x07, 0xFC, 0x46, 0xEA, 0x0C, 0x06, 0x20, 0x37, + 0xB6, 0xFB, 0xF4, 0xFC, + 0xA7, 0xEB, 0x05, 0x07, 0x10, 0x3F, 0x07, 0xF0, 0x1F, 0x0B, 0xCB, 0xF1, + 0x20, 0x06, 0x0C, 0xFA, + 0x0B, 0xFB, 0x2C, 0xFA, 0x06, 0xF6, 0x44, 0xBF, 0xB3, 0x46, 0x00, 0x26, + 0x20, 0x2F, 0xA4, 0xBF, + 0x5E, 0x46, 0x4F, 0xF0, 0x00, 0x0B, 0x5B, 0xEA, 0x06, 0x0C, 0x08, 0xBF, + 0x4F, 0xF0, 0x01, 0x0B, + 0x19, 0xEB, 0x0B, 0x09, 0xAB, 0xFB, 0x02, 0x7C, 0x48, 0xEB, 0x06, 0x08, + 0xC0, 0x1B, 0x06, 0xFB, + 0x02, 0xCC, 0x0B, 0xFB, 0x03, 0xCC, 0x71, 0xEB, 0x0C, 0x01, 0xC1, 0xE7, + 0x0B, 0x46, 0x02, 0x46, + 0x41, 0x46, 0x48, 0x46, 0xBD, 0xE8, 0xF0, 0x8B, 0x13, 0xB5, 0x4F, 0xF0, + 0x00, 0x00, 0x4F, 0xF0, + 0x00, 0x01, 0xAF, 0xF3, 0x00, 0x80, 0xBD, 0xE8, 0x1C, 0x40, 0x70, 0x47, + 0xB2, 0xF1, 0x20, 0x03, + 0x0A, 0xD5, 0xC2, 0xF1, 0x20, 0x03, 0x01, 0xFA, 0x02, 0xF1, 0x20, 0xFA, + 0x03, 0xF3, 0x00, 0xFA, + 0x02, 0xF0, 0x41, 0xEA, 0x03, 0x01, 0x70, 0x47, 0x00, 0xFA, 0x03, 0xF1, + 0x4F, 0xF0, 0x00, 0x00, + 0x70, 0x47, 0x10, 0xB5, 0x4C, 0x10, 0x84, 0xEA, 0x53, 0x04, 0x04, 0xD5, + 0x40, 0x42, 0xC1, 0xF1, + 0x00, 0x01, 0x38, 0xBF, 0x49, 0x1E, 0x1B, 0x42, 0x04, 0xD5, 0x52, 0x42, + 0xC3, 0xF1, 0x00, 0x03, + 0x38, 0xBF, 0x5B, 0x1E, 0xFF, 0xF7, 0x63, 0xFF, 0x14, 0xF0, 0x80, 0x4F, + 0x04, 0xD0, 0x40, 0x42, + 0xC1, 0xF1, 0x00, 0x01, 0x38, 0xBF, 0x49, 0x1E, 0x14, 0xF0, 0x00, 0x4F, + 0x04, 0xD0, 0x52, 0x42, + 0xC3, 0xF1, 0x00, 0x03, 0x38, 0xBF, 0x5B, 0x1E, 0x10, 0xBD, 0x03, 0x2A, + 0x40, 0xF2, 0x30, 0x80, + 0x10, 0xF0, 0x03, 0x0C, 0x00, 0xF0, 0x15, 0x80, 0x11, 0xF8, 0x01, 0x3B, + 0xBC, 0xF1, 0x02, 0x0F, + 0x62, 0x44, 0x98, 0xBF, 0x11, 0xF8, 0x01, 0xCB, 0x00, 0xF8, 0x01, 0x3B, + 0x38, 0xBF, 0x11, 0xF8, + 0x01, 0x3B, 0xA2, 0xF1, 0x04, 0x02, 0x98, 0xBF, 0x00, 0xF8, 0x01, 0xCB, + 0x38, 0xBF, 0x00, 0xF8, + 0x01, 0x3B, 0x11, 0xF0, 0x03, 0x03, 0x00, 0xF0, 0x25, 0x80, 0x08, 0x3A, + 0xC0, 0xF0, 0x08, 0x80, + 0x51, 0xF8, 0x04, 0x3B, 0x08, 0x3A, 0x51, 0xF8, 0x04, 0xCB, 0xA0, 0xE8, + 0x08, 0x10, 0xF5, 0xE7, + 0x12, 0x1D, 0x5C, 0xBF, 0x51, 0xF8, 0x04, 0x3B, 0x40, 0xF8, 0x04, 0x3B, + 0xAF, 0xF3, 0x00, 0x80, + 0xD2, 0x07, 0x24, 0xBF, 0x11, 0xF8, 0x01, 0x3B, 0x11, 0xF8, 0x01, 0xCB, + 0x48, 0xBF, 0x11, 0xF8, + 0x01, 0x2B, 0x24, 0xBF, 0x00, 0xF8, 0x01, 0x3B, 0x00, 0xF8, 0x01, 0xCB, + 0x48, 0xBF, 0x00, 0xF8, + 0x01, 0x2B, 0x70, 0x47, 0x10, 0xB5, 0x20, 0x3A, 0xC0, 0xF0, 0x0B, 0x80, + 0xB1, 0xE8, 0x18, 0x50, + 0x20, 0x3A, 0xA0, 0xE8, 0x18, 0x50, 0xB1, 0xE8, 0x18, 0x50, 0xA0, 0xE8, + 0x18, 0x50, 0xBF, 0xF4, + 0xF5, 0xAF, 0x5F, 0xEA, 0x02, 0x7C, 0x24, 0xBF, 0xB1, 0xE8, 0x18, 0x50, + 0xA0, 0xE8, 0x18, 0x50, + 0x44, 0xBF, 0x18, 0xC9, 0x18, 0xC0, 0xBD, 0xE8, 0x10, 0x40, 0x5F, 0xEA, + 0x82, 0x7C, 0x24, 0xBF, + 0x51, 0xF8, 0x04, 0x3B, 0x40, 0xF8, 0x04, 0x3B, 0x08, 0xBF, 0x70, 0x47, + 0xD2, 0x07, 0x28, 0xBF, + 0x31, 0xF8, 0x02, 0x3B, 0x48, 0xBF, 0x11, 0xF8, 0x01, 0x2B, 0x28, 0xBF, + 0x20, 0xF8, 0x02, 0x3B, + 0x48, 0xBF, 0x00, 0xF8, 0x01, 0x2B, 0x70, 0x47, 0x02, 0xF0, 0xFF, 0x03, + 0x43, 0xEA, 0x03, 0x22, + 0x42, 0xEA, 0x02, 0x42, 0x00, 0xF0, 0x02, 0xB8, 0x4F, 0xF0, 0x00, 0x02, + 0x04, 0x29, 0xC0, 0xF0, + 0x12, 0x80, 0x10, 0xF0, 0x03, 0x0C, 0x00, 0xF0, 0x1B, 0x80, 0xCC, 0xF1, + 0x04, 0x0C, 0xBC, 0xF1, + 0x02, 0x0F, 0x18, 0xBF, 0x00, 0xF8, 0x01, 0x2B, 0xA8, 0xBF, 0x20, 0xF8, + 0x02, 0x2B, 0xA1, 0xEB, + 0x0C, 0x01, 0x00, 0xF0, 0x0D, 0xB8, 0x5F, 0xEA, 0xC1, 0x7C, 0x24, 0xBF, + 0x00, 0xF8, 0x01, 0x2B, + 0x00, 0xF8, 0x01, 0x2B, 0x48, 0xBF, 0x00, 0xF8, 0x01, 0x2B, 0x70, 0x47, + 0x4F, 0xF0, 0x00, 0x02, + 0x00, 0xB5, 0x13, 0x46, 0x94, 0x46, 0x96, 0x46, 0x20, 0x39, 0x22, 0xBF, + 0xA0, 0xE8, 0x0C, 0x50, + 0xA0, 0xE8, 0x0C, 0x50, 0xB1, 0xF1, 0x20, 0x01, 0xBF, 0xF4, 0xF7, 0xAF, + 0x09, 0x07, 0x28, 0xBF, + 0xA0, 0xE8, 0x0C, 0x50, 0x48, 0xBF, 0x0C, 0xC0, 0x5D, 0xF8, 0x04, 0xEB, + 0x89, 0x00, 0x28, 0xBF, + 0x40, 0xF8, 0x04, 0x2B, 0x08, 0xBF, 0x70, 0x47, 0x48, 0xBF, 0x20, 0xF8, + 0x02, 0x2B, 0x11, 0xF0, + 0x80, 0x4F, 0x18, 0xBF, 0x00, 0xF8, 0x01, 0x2B, 0x70, 0x47, 0x04, 0x46, + 0xAF, 0xF3, 0x00, 0x80, + 0x20, 0x46, 0xE8, 0xF7, 0x93, 0xFF, 0x00, 0x00, 0x01, 0x49, 0x18, 0x20, + 0xAB, 0xBE, 0xFE, 0xE7, + 0x26, 0x00, 0x02, 0x00, 0x70, 0x47, 0x00, 0x00, 0x01, 0x49, 0x00, 0x20, + 0x08, 0x60, 0x70, 0x47, + 0x3C, 0x05, 0x00, 0x22, 0x2D, 0xE9, 0xF0, 0x41, 0x00, 0x24, 0x01, 0x27, + 0xDF, 0xF8, 0x38, 0x80, + 0x5F, 0xEA, 0x47, 0x75, 0x07, 0xFA, 0x04, 0xF0, 0xC6, 0xB2, 0x95, 0xF8, + 0x2F, 0x00, 0x30, 0x42, + 0x0C, 0xD0, 0x95, 0xF8, 0x39, 0x00, 0x30, 0x42, 0x08, 0xD0, 0x58, 0xF8, + 0x24, 0x00, 0x00, 0xB1, + 0x80, 0x47, 0x95, 0xF8, 0x2F, 0x00, 0xB0, 0x43, 0x85, 0xF8, 0x2F, 0x00, + 0x64, 0x1C, 0x06, 0x2C, + 0xE8, 0xDB, 0xBD, 0xE8, 0xF0, 0x81, 0x00, 0x00, 0xD4, 0x0E, 0x10, 0x00, + 0x2D, 0xE9, 0xF0, 0x41, + 0x00, 0x24, 0x01, 0x27, 0xDF, 0xF8, 0x38, 0x80, 0x5F, 0xEA, 0x47, 0x75, + 0x07, 0xFA, 0x04, 0xF0, + 0xC6, 0xB2, 0x95, 0xF8, 0x2E, 0x00, 0x30, 0x42, 0x0C, 0xD0, 0x95, 0xF8, + 0x38, 0x00, 0x30, 0x42, + 0x08, 0xD0, 0x58, 0xF8, 0x24, 0x00, 0x00, 0xB1, 0x80, 0x47, 0x95, 0xF8, + 0x2E, 0x00, 0xB0, 0x43, + 0x85, 0xF8, 0x2E, 0x00, 0x64, 0x1C, 0x06, 0x2C, 0xE8, 0xDB, 0xBD, 0xE8, + 0xF0, 0x81, 0x00, 0x00, + 0xB4, 0x0E, 0x10, 0x00, 0x01, 0x21, 0x81, 0x40, 0xC8, 0xB2, 0x4F, 0xF0, + 0x00, 0x51, 0x91, 0xF8, + 0x33, 0x10, 0x01, 0x42, 0x01, 0xD0, 0x01, 0x20, 0x70, 0x47, 0x00, 0x20, + 0x70, 0x47, 0x00, 0x00, + 0x01, 0x49, 0x01, 0x20, 0x08, 0x60, 0x70, 0x47, 0x3C, 0x05, 0x00, 0x22, + 0x07, 0x48, 0x41, 0x78, + 0x49, 0xB1, 0x80, 0x78, 0x38, 0xB1, 0x06, 0x48, 0x00, 0x78, 0x01, 0x28, + 0x03, 0xD1, 0x05, 0x48, + 0x00, 0x78, 0x01, 0x28, 0x00, 0xD0, 0x00, 0x20, 0x70, 0x47, 0x00, 0x00, + 0x74, 0x07, 0x10, 0x00, + 0xFD, 0x06, 0x10, 0x00, 0x5C, 0x05, 0x10, 0x00, 0x03, 0x48, 0x41, 0x78, + 0x11, 0xB9, 0x80, 0x78, + 0x00, 0x28, 0x00, 0xD0, 0x01, 0x20, 0x70, 0x47, 0x74, 0x07, 0x10, 0x00, + 0x01, 0x48, 0x00, 0x78, + 0x70, 0x47, 0x00, 0x00, 0x74, 0x06, 0x10, 0x00, 0xC0, 0x6E, 0x70, 0x47, + 0x90, 0xF8, 0x73, 0x00, + 0x70, 0x47, 0x90, 0xF8, 0x72, 0x00, 0x70, 0x47, 0x04, 0x48, 0x41, 0x78, + 0x82, 0x78, 0x00, 0x78, + 0x11, 0x44, 0x08, 0x18, 0x00, 0xD0, 0x01, 0x20, 0x70, 0x47, 0x00, 0x00, + 0x2D, 0x06, 0x10, 0x00, + 0x70, 0xB5, 0xD0, 0xE9, 0x00, 0x21, 0x4C, 0x08, 0x4F, 0xEA, 0x32, 0x03, + 0x96, 0x18, 0x41, 0xEB, + 0x01, 0x05, 0x33, 0x43, 0x2C, 0x43, 0x13, 0x43, 0x0C, 0x43, 0xC0, 0xE9, + 0x00, 0x34, 0x70, 0xBD, + 0x01, 0xEB, 0x81, 0x01, 0x00, 0xEB, 0xC1, 0x00, 0x29, 0x30, 0x70, 0x47, + 0x02, 0x4A, 0x12, 0x78, + 0x51, 0x43, 0x00, 0xEB, 0x41, 0x00, 0x70, 0x47, 0xEE, 0x06, 0x10, 0x00, + 0x01, 0xEB, 0x81, 0x01, + 0x00, 0xEB, 0xC1, 0x00, 0x29, 0x30, 0x70, 0x47, 0x02, 0x4A, 0x12, 0x78, + 0x51, 0x43, 0x00, 0xEB, + 0x41, 0x00, 0x70, 0x47, 0xEE, 0x06, 0x10, 0x00, 0x00, 0x04, 0x40, 0x00, + 0x10, 0x21, 0x00, 0x00, + 0x00, 0x1F, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x30, 0x00, 0x01, 0x00, + 0x00, 0x04, 0x06, 0x00, + 0x08, 0x02, 0x50, 0x00, 0x60, 0x02, 0x0C, 0x0C, 0x00, 0x00, 0x60, 0x02, + 0x0C, 0x30, 0x0C, 0x1A, + 0x31, 0x10, 0x0F, 0x0F, 0x00, 0x00, 0x2F, 0x00, 0x01, 0x00, 0xAA, 0x2C, + 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x18, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x60, 0x02, 0x01, 0x80, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x0C, 0x00, + 0x0C, 0x00, 0x0C, 0x00, 0x0C, 0x00, 0x07, 0x01, 0x26, 0x26, 0x12, 0x12, + 0x11, 0x00, 0x00, 0x00, + 0x00, 0x38, 0x00, 0x3C, 0x20, 0x3D, 0xD0, 0x41, 0xEC, 0x41, 0x08, 0x42, + 0x1C, 0x42, 0x30, 0x42, + 0x44, 0x42, 0x60, 0x42, 0x74, 0x42, 0x88, 0x42, 0xA4, 0x42, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, + 0x00, 0x01, 0x01, 0x02, + 0x01, 0x02, 0x02, 0x03, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, + 0x00, 0x00, 0x00, 0x00, + 0x1B, 0x00, 0x00, 0x80, 0x2D, 0x00, 0x00, 0x80, 0x36, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x80, + 0x5A, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x00, 0x00, 0x77, 0x00, 0x00, 0x80, + 0x99, 0x00, 0x00, 0x80, + 0x82, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x00, 0x00, 0xAF, 0x00, 0x00, 0x80, + 0xD8, 0x00, 0x00, 0x00, + 0xC3, 0x00, 0x00, 0x80, 0xF5, 0x00, 0x00, 0x80, 0xEE, 0x00, 0x00, 0x00, + 0x29, 0x01, 0x00, 0x80, + 0x32, 0x01, 0x00, 0x00, 0x04, 0x01, 0x00, 0x00, 0x1F, 0x01, 0x00, 0x80, + 0x68, 0x01, 0x00, 0x00, + 0x73, 0x01, 0x00, 0x80, 0x45, 0x01, 0x00, 0x80, 0x5E, 0x01, 0x00, 0x00, + 0xB0, 0x01, 0x00, 0x00, + 0xAB, 0x01, 0x00, 0x80, 0x9D, 0x01, 0x00, 0x80, 0x86, 0x01, 0x00, 0x00, + 0xF1, 0x01, 0x00, 0x80, + 0xEA, 0x01, 0x00, 0x00, 0xDC, 0x01, 0x00, 0x00, 0xC7, 0x01, 0x00, 0x80, + 0x49, 0x02, 0x00, 0x80, + 0x52, 0x02, 0x00, 0x00, 0x64, 0x02, 0x00, 0x00, 0x7F, 0x02, 0x00, 0x80, + 0x08, 0x02, 0x00, 0x00, + 0x13, 0x02, 0x00, 0x80, 0x25, 0x02, 0x00, 0x80, 0x3E, 0x02, 0x00, 0x00, + 0xD0, 0x02, 0x00, 0x00, + 0xCB, 0x02, 0x00, 0x80, 0xFD, 0x02, 0x00, 0x80, 0xE6, 0x02, 0x00, 0x00, + 0x91, 0x02, 0x00, 0x80, + 0x8A, 0x02, 0x00, 0x00, 0xBC, 0x02, 0x00, 0x00, 0xA7, 0x02, 0x00, 0x80, + 0x60, 0x03, 0x00, 0x00, + 0x7B, 0x03, 0x00, 0x80, 0x4D, 0x03, 0x00, 0x80, 0x56, 0x03, 0x00, 0x00, + 0x21, 0x03, 0x00, 0x80, + 0x3A, 0x03, 0x00, 0x00, 0x0C, 0x03, 0x00, 0x00, 0x17, 0x03, 0x00, 0x80, + 0xF9, 0x03, 0x00, 0x80, + 0xE2, 0x03, 0x00, 0x00, 0xD4, 0x03, 0x00, 0x00, 0xCF, 0x03, 0x00, 0x80, + 0xB8, 0x03, 0x00, 0x00, + 0xA3, 0x03, 0x00, 0x80, 0x95, 0x03, 0x00, 0x80, 0x8E, 0x03, 0x00, 0x00, + 0x89, 0x04, 0x00, 0x80, + 0x92, 0x04, 0x00, 0x00, 0xA4, 0x04, 0x00, 0x00, 0xBF, 0x04, 0x00, 0x80, + 0xC8, 0x04, 0x00, 0x00, + 0xD3, 0x04, 0x00, 0x80, 0xE5, 0x04, 0x00, 0x80, 0xFE, 0x04, 0x00, 0x00, + 0x10, 0x04, 0x00, 0x00, + 0x0B, 0x04, 0x00, 0x80, 0x3D, 0x04, 0x00, 0x80, 0x26, 0x04, 0x00, 0x00, + 0x51, 0x04, 0x00, 0x80, + 0x4A, 0x04, 0x00, 0x00, 0x7C, 0x04, 0x00, 0x00, 0x67, 0x04, 0x00, 0x80, + 0xA0, 0x05, 0x00, 0x00, + 0xBB, 0x05, 0x00, 0x80, 0x8D, 0x05, 0x00, 0x80, 0x96, 0x05, 0x00, 0x00, + 0xE1, 0x05, 0x00, 0x80, + 0xFA, 0x05, 0x00, 0x00, 0xCC, 0x05, 0x00, 0x00, 0xD7, 0x05, 0x00, 0x80, + 0x39, 0x05, 0x00, 0x80, + 0x22, 0x05, 0x00, 0x00, 0x14, 0x05, 0x00, 0x00, 0x0F, 0x05, 0x00, 0x80, + 0x78, 0x05, 0x00, 0x00, + 0x63, 0x05, 0x00, 0x80, 0x55, 0x05, 0x00, 0x80, 0x4E, 0x05, 0x00, 0x00, + 0xC0, 0x06, 0x00, 0x00, + 0xDB, 0x06, 0x00, 0x80, 0xED, 0x06, 0x00, 0x80, 0xF6, 0x06, 0x00, 0x00, + 0x81, 0x06, 0x00, 0x80, + 0x9A, 0x06, 0x00, 0x00, 0xAC, 0x06, 0x00, 0x00, 0xB7, 0x06, 0x00, 0x80, + 0x59, 0x06, 0x00, 0x80, + 0x42, 0x06, 0x00, 0x00, 0x74, 0x06, 0x00, 0x00, 0x6F, 0x06, 0x00, 0x80, + 0x18, 0x06, 0x00, 0x00, + 0x03, 0x06, 0x00, 0x80, 0x35, 0x06, 0x00, 0x80, 0x2E, 0x06, 0x00, 0x00, + 0xE9, 0x07, 0x00, 0x80, + 0xF2, 0x07, 0x00, 0x00, 0xC4, 0x07, 0x00, 0x00, 0xDF, 0x07, 0x00, 0x80, + 0xA8, 0x07, 0x00, 0x00, + 0xB3, 0x07, 0x00, 0x80, 0x85, 0x07, 0x00, 0x80, 0x9E, 0x07, 0x00, 0x00, + 0x70, 0x07, 0x00, 0x00, + 0x6B, 0x07, 0x00, 0x80, 0x5D, 0x07, 0x00, 0x80, 0x46, 0x07, 0x00, 0x00, + 0x31, 0x07, 0x00, 0x80, + 0x2A, 0x07, 0x00, 0x00, 0x1C, 0x07, 0x00, 0x00, 0x07, 0x07, 0x00, 0x80, + 0x09, 0x09, 0x00, 0x80, + 0x12, 0x09, 0x00, 0x00, 0x24, 0x09, 0x00, 0x00, 0x3F, 0x09, 0x00, 0x80, + 0x48, 0x09, 0x00, 0x00, + 0x53, 0x09, 0x00, 0x80, 0x65, 0x09, 0x00, 0x80, 0x7E, 0x09, 0x00, 0x00, + 0x90, 0x09, 0x00, 0x00, + 0x8B, 0x09, 0x00, 0x80, 0xBD, 0x09, 0x00, 0x80, 0xA6, 0x09, 0x00, 0x00, + 0xD1, 0x09, 0x00, 0x80, + 0xCA, 0x09, 0x00, 0x00, 0xFC, 0x09, 0x00, 0x00, 0xE7, 0x09, 0x00, 0x80, + 0x20, 0x08, 0x00, 0x00, + 0x3B, 0x08, 0x00, 0x80, 0x0D, 0x08, 0x00, 0x80, 0x16, 0x08, 0x00, 0x00, + 0x61, 0x08, 0x00, 0x80, + 0x7A, 0x08, 0x00, 0x00, 0x4C, 0x08, 0x00, 0x00, 0x57, 0x08, 0x00, 0x80, + 0xB9, 0x08, 0x00, 0x80, + 0xA2, 0x08, 0x00, 0x00, 0x94, 0x08, 0x00, 0x00, 0x8F, 0x08, 0x00, 0x80, + 0xF8, 0x08, 0x00, 0x00, + 0xE3, 0x08, 0x00, 0x80, 0xD5, 0x08, 0x00, 0x80, 0xCE, 0x08, 0x00, 0x00, + 0x40, 0x0B, 0x00, 0x00, + 0x5B, 0x0B, 0x00, 0x80, 0x6D, 0x0B, 0x00, 0x80, 0x76, 0x0B, 0x00, 0x00, + 0x01, 0x0B, 0x00, 0x80, + 0x1A, 0x0B, 0x00, 0x00, 0x2C, 0x0B, 0x00, 0x00, 0x37, 0x0B, 0x00, 0x80, + 0xD9, 0x0B, 0x00, 0x80, + 0xC2, 0x0B, 0x00, 0x00, 0xF4, 0x0B, 0x00, 0x00, 0xEF, 0x0B, 0x00, 0x80, + 0x98, 0x0B, 0x00, 0x00, + 0x83, 0x0B, 0x00, 0x80, 0xB5, 0x0B, 0x00, 0x80, 0xAE, 0x0B, 0x00, 0x00, + 0x69, 0x0A, 0x00, 0x80, + 0x72, 0x0A, 0x00, 0x00, 0x44, 0x0A, 0x00, 0x00, 0x5F, 0x0A, 0x00, 0x80, + 0x28, 0x0A, 0x00, 0x00, + 0x33, 0x0A, 0x00, 0x80, 0x05, 0x0A, 0x00, 0x80, 0x1E, 0x0A, 0x00, 0x00, + 0xF0, 0x0A, 0x00, 0x00, + 0xEB, 0x0A, 0x00, 0x80, 0xDD, 0x0A, 0x00, 0x80, 0xC6, 0x0A, 0x00, 0x00, + 0xB1, 0x0A, 0x00, 0x80, + 0xAA, 0x0A, 0x00, 0x00, 0x9C, 0x0A, 0x00, 0x00, 0x87, 0x0A, 0x00, 0x80, + 0x80, 0x0D, 0x00, 0x00, + 0x9B, 0x0D, 0x00, 0x80, 0xAD, 0x0D, 0x00, 0x80, 0xB6, 0x0D, 0x00, 0x00, + 0xC1, 0x0D, 0x00, 0x80, + 0xDA, 0x0D, 0x00, 0x00, 0xEC, 0x0D, 0x00, 0x00, 0xF7, 0x0D, 0x00, 0x80, + 0x19, 0x0D, 0x00, 0x80, + 0x02, 0x0D, 0x00, 0x00, 0x34, 0x0D, 0x00, 0x00, 0x2F, 0x0D, 0x00, 0x80, + 0x58, 0x0D, 0x00, 0x00, + 0x43, 0x0D, 0x00, 0x80, 0x75, 0x0D, 0x00, 0x80, 0x6E, 0x0D, 0x00, 0x00, + 0xA9, 0x0C, 0x00, 0x80, + 0xB2, 0x0C, 0x00, 0x00, 0x84, 0x0C, 0x00, 0x00, 0x9F, 0x0C, 0x00, 0x80, + 0xE8, 0x0C, 0x00, 0x00, + 0xF3, 0x0C, 0x00, 0x80, 0xC5, 0x0C, 0x00, 0x80, 0xDE, 0x0C, 0x00, 0x00, + 0x30, 0x0C, 0x00, 0x00, + 0x2B, 0x0C, 0x00, 0x80, 0x1D, 0x0C, 0x00, 0x80, 0x06, 0x0C, 0x00, 0x00, + 0x71, 0x0C, 0x00, 0x80, + 0x6A, 0x0C, 0x00, 0x00, 0x5C, 0x0C, 0x00, 0x00, 0x47, 0x0C, 0x00, 0x80, + 0xC9, 0x0F, 0x00, 0x80, + 0xD2, 0x0F, 0x00, 0x00, 0xE4, 0x0F, 0x00, 0x00, 0xFF, 0x0F, 0x00, 0x80, + 0x88, 0x0F, 0x00, 0x00, + 0x93, 0x0F, 0x00, 0x80, 0xA5, 0x0F, 0x00, 0x80, 0xBE, 0x0F, 0x00, 0x00, + 0x50, 0x0F, 0x00, 0x00, + 0x4B, 0x0F, 0x00, 0x80, 0x7D, 0x0F, 0x00, 0x80, 0x66, 0x0F, 0x00, 0x00, + 0x11, 0x0F, 0x00, 0x80, + 0x0A, 0x0F, 0x00, 0x00, 0x3C, 0x0F, 0x00, 0x00, 0x27, 0x0F, 0x00, 0x80, + 0xE0, 0x0E, 0x00, 0x00, + 0xFB, 0x0E, 0x00, 0x80, 0xCD, 0x0E, 0x00, 0x80, 0xD6, 0x0E, 0x00, 0x00, + 0xA1, 0x0E, 0x00, 0x80, + 0xBA, 0x0E, 0x00, 0x00, 0x8C, 0x0E, 0x00, 0x00, 0x97, 0x0E, 0x00, 0x80, + 0x79, 0x0E, 0x00, 0x80, + 0x62, 0x0E, 0x00, 0x00, 0x54, 0x0E, 0x00, 0x00, 0x4F, 0x0E, 0x00, 0x80, + 0x38, 0x0E, 0x00, 0x00, + 0x23, 0x0E, 0x00, 0x80, 0x15, 0x0E, 0x00, 0x80, 0x0E, 0x0E, 0x00, 0x00, + 0xFF, 0x00, 0xBE, 0x94, + 0xFE, 0xFF, 0x27, 0xC4, 0xFE, 0xFF, 0x59, 0xF2, 0xFE, 0xFF, 0x6B, 0x1F, + 0xFF, 0xFF, 0x81, 0x4B, + 0xFF, 0xFF, 0xC5, 0x76, 0xFF, 0xFF, 0x67, 0xA1, 0xFF, 0xFF, 0x97, 0xCB, + 0xFF, 0xFF, 0x88, 0xF5, + 0xFF, 0xFF, 0x6C, 0x1F, 0x00, 0x00, 0x76, 0x49, 0x00, 0x00, 0xD9, 0x73, + 0x00, 0x00, 0xC6, 0x9E, + 0x00, 0x00, 0x6D, 0xCA, 0x00, 0x00, 0xFC, 0xF6, 0x00, 0x00, 0x9A, 0x24, + 0x01, 0x00, 0x66, 0x53, + 0x01, 0x00, 0xA3, 0x8D, 0x00, 0x00, 0x71, 0x74, 0x00, 0x00, 0x02, 0x60, + 0x00, 0x00, 0xA2, 0x4F, + 0x00, 0x00, 0xC8, 0x42, 0x00, 0x00, 0x0C, 0x39, 0x00, 0x00, 0x21, 0x32, + 0x00, 0x00, 0xD2, 0x2D, + 0x00, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x9B, 0x2C, 0x00, 0x00, 0xA9, 0x2F, + 0x00, 0x00, 0x40, 0x35, + 0x00, 0x00, 0x8B, 0x3D, 0x00, 0x00, 0xCB, 0x48, 0x00, 0x00, 0x59, 0x57, + 0x00, 0x00, 0xAD, 0x69, + 0x00, 0x00, 0x65, 0x80, 0x00, 0x00, 0x30, 0x11, 0x00, 0x00, 0x4C, 0x1C, + 0x00, 0x00, 0x2D, 0x29, + 0x00, 0x00, 0x86, 0x37, 0x00, 0x00, 0x24, 0x47, 0x00, 0x00, 0xE8, 0x57, + 0x00, 0x00, 0xC4, 0x69, + 0x00, 0x00, 0xB5, 0x7C, 0x00, 0x00, 0xC4, 0x90, 0x00, 0x00, 0x03, 0xA6, + 0x00, 0x00, 0x8F, 0xBC, + 0x00, 0x00, 0x8C, 0xD4, 0x00, 0x00, 0x29, 0xEE, 0x00, 0x00, 0x9C, 0x09, + 0x01, 0x00, 0x2B, 0x27, + 0x01, 0x00, 0x24, 0x47, 0x01, 0x00, 0xE6, 0x69, 0x01, 0x00, 0x98, 0x08, + 0x00, 0x00, 0x26, 0x0E, + 0x00, 0x00, 0x97, 0x14, 0x00, 0x00, 0xC3, 0x1B, 0x00, 0x00, 0x92, 0x23, + 0x00, 0x00, 0xF4, 0x2B, + 0x00, 0x00, 0xE2, 0x34, 0x00, 0x00, 0x5A, 0x3E, 0x00, 0x00, 0x62, 0x48, + 0x00, 0x00, 0x02, 0x53, + 0x00, 0x00, 0x48, 0x5E, 0x00, 0x00, 0x46, 0x6A, 0x00, 0x00, 0x14, 0x77, + 0x00, 0x00, 0xCE, 0x84, + 0x00, 0x00, 0x95, 0x93, 0x00, 0x00, 0x92, 0xA3, 0x00, 0x00, 0xF3, 0xB4, + 0x00, 0x00, 0x01, 0x00, + 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x04, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x02, 0x00, 0x01, 0x00, + 0x10, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x14, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x0C, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xF8, 0x47, 0x10, 0x00, 0x44, 0x4D, 0x10, 0x00, 0xC5, 0x18, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xC3, 0x17, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD1, 0x17, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xDF, 0x17, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE3, 0x17, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x09, 0x18, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x6B, 0x18, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x81, 0x18, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xB5, 0x18, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0xB7, 0x18, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xB9, 0x18, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0xBB, 0x18, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xBD, 0x18, 0x01, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0xBF, 0x18, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xC1, 0x18, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0xC3, 0x18, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0x18, 0x01, 0x00, + 0xFB, 0x18, 0x01, 0x00, + 0x65, 0x19, 0x01, 0x00, 0xFB, 0x18, 0x01, 0x00, 0x17, 0x1A, 0x01, 0x00, + 0xFB, 0x18, 0x01, 0x00, + 0xFB, 0x18, 0x01, 0x00, 0xFB, 0x18, 0x01, 0x00, 0xFB, 0x18, 0x01, 0x00, + 0xFB, 0x18, 0x01, 0x00, + 0x2F, 0x19, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFB, 0x18, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x61, 0x19, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x91, 0x19, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0xB5, 0x19, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xD9, 0x19, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x0F, 0x1A, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0x13, 0x1A, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x43, 0x1A, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x91, 0x1A, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0xA7, 0x1A, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0xFF, 0x1A, 0x01, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0xB5, 0x1B, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x33, 0x1C, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x73, 0x1C, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x65, 0x1B, 0x01, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0xFB, 0x18, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0x18, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x5D, 0x1A, 0x01, 0x00, 0x67, 0x1A, 0x01, 0x00, 0x71, 0x1A, 0x01, 0x00, + 0x81, 0x1A, 0x01, 0x00, + 0xF5, 0x1D, 0x01, 0x00, 0x17, 0x1E, 0x01, 0x00, 0x1F, 0x1E, 0x01, 0x00, + 0x3F, 0x1E, 0x01, 0x00, + 0xA1, 0x1E, 0x01, 0x00, 0xB7, 0x1E, 0x01, 0x00, 0xC7, 0x1E, 0x01, 0x00, + 0xD9, 0x1E, 0x01, 0x00, + 0xEB, 0x1E, 0x01, 0x00, 0xFD, 0x1E, 0x01, 0x00, 0x0F, 0x1F, 0x01, 0x00, + 0x21, 0x1F, 0x01, 0x00, + 0x33, 0x1F, 0x01, 0x00, 0x45, 0x1F, 0x01, 0x00, 0x57, 0x1F, 0x01, 0x00, + 0x69, 0x1F, 0x01, 0x00, + 0x85, 0x86, 0x60, 0x61, 0xD0, 0x02, 0x0F, 0x01, 0x00, 0xA6, 0x02, 0x0F, + 0x01, 0x00, 0xA8, 0x02, + 0x0B, 0x03, 0x00, 0x72, 0x00, 0x0B, 0x01, 0x00, 0x73, 0x00, 0x08, 0x01, + 0x00, 0x00, 0x00, 0x00, + 0x49, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF3, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0xE3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, + 0x04, 0x69, 0x10, 0x00, 0x00, 0x04, 0x00, 0x00, 0xB5, 0x4E, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x6D, 0x10, 0x00, + 0x00, 0x04, 0x00, 0x00, 0xDD, 0x5E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x71, 0x10, 0x00, + 0x00, 0x08, 0x00, 0x00, + 0x8F, 0x15, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x04, 0x79, 0x10, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x1D, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x04, 0x7B, 0x10, 0x00, 0x80, 0x00, 0x00, 0x00, 0xB9, 0x16, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0xB1, 0x56, 0x01, 0x00, 0x5B, 0x56, 0x01, 0x00, + 0x31, 0x6E, 0x01, 0x00, + 0x23, 0x5B, 0x01, 0x00, 0x65, 0x48, 0x01, 0x00, 0x9B, 0x5D, 0x01, 0x00, + 0x8F, 0x4D, 0x01, 0x00, + 0x73, 0x68, 0x01, 0x00, 0xF1, 0x4E, 0x01, 0x00, 0x4D, 0x57, 0x01, 0x00, + 0x47, 0x6E, 0x01, 0x00, + 0x60, 0x7D, 0x01, 0x00, 0x00, 0x04, 0x10, 0x00, 0x0C, 0x04, 0x00, 0x00, + 0x1C, 0x01, 0x00, 0x00, + 0x98, 0x7D, 0x01, 0x00, 0x00, 0xFC, 0x10, 0x00, 0x40, 0x00, 0x00, 0x00, + 0x78, 0x01, 0x00, 0x00, + 0x98, 0x7D, 0x01, 0x00, 0x0C, 0x08, 0x10, 0x00, 0x88, 0x7D, 0x00, 0x00, + 0x94, 0x01, 0x00, 0x00, + 0x01, 0x29, 0x04, 0xFF, 0x90, 0xD0, 0x03, 0x01, 0x71, 0x14, 0x90, 0x5F, + 0x01, 0x32, 0x01, 0x32, + 0x02, 0x29, 0x08, 0x41, 0xA9, 0x08, 0x09, 0x1A, 0x0C, 0xB2, 0x64, 0x29, + 0x0C, 0x69, 0x48, 0x29, + 0x04, 0x0A, 0x11, 0x04, 0x10, 0x01, 0xFF, 0x01, 0x46, 0x07, 0xA5, 0xF0, + 0x01, 0x20, 0x18, 0x50, + 0x10, 0xAA, 0xFF, 0x0C, 0x59, 0x01, 0x00, 0x00, 0x30, 0xB5, 0x72, 0xB6, + 0x01, 0x24, 0x0C, 0x4D, + 0x2C, 0x60, 0x0C, 0x4B, 0x00, 0x22, 0x89, 0xB1, 0xC3, 0xF8, 0x00, 0x40, + 0x4F, 0xF0, 0x00, 0x51, + 0x00, 0xF0, 0x1F, 0x00, 0x81, 0xF8, 0x6A, 0x00, 0x06, 0x48, 0x1C, 0x38, + 0x04, 0x60, 0x01, 0x68, + 0x00, 0x29, 0xFC, 0xD1, 0x1A, 0x60, 0x2A, 0x60, 0x62, 0xB6, 0x30, 0xBD, + 0x1A, 0x60, 0xED, 0xE7, + 0xC0, 0x1B, 0x00, 0x22, 0x78, 0x0D, 0x00, 0x22, 0xD5, 0xA1, 0xA1, 0x0A, + 0x93, 0x02, 0x00, 0x00, + 0x2A, 0x5E, 0x5E, 0xF5, 0x6C, 0xFD, 0xFF, 0xFF, 0xA1, 0x00, 0xA1, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x3F, + 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x0F, 0xFF, 0x09, + 0x9F, 0x05, 0x09, 0xFF, + 0x09, 0x9F, 0x05, 0x1D, 0x02, 0x00, 0x00, 0x00, 0x01, 0x20, 0x40, 0x50, + 0xAA, 0x99, 0xC8, 0x00, + 0xC8, 0x00, 0xC8, 0x00, 0xC8, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x01, 0x70, 0x00, + 0x70, 0x00, 0x08, 0xB0, 0x00, 0xB0, 0x00, 0x00, 0x07, 0xAA, 0x38, 0xC8, + 0x00, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x08, 0x09, 0x0A, + 0x14, 0x0A, 0x00, 0x02, + 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x13, 0xE8, 0x03, + 0x64, 0x00, 0x64, 0x00, + 0xD0, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x64, + 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x08, 0x00, 0x02, + 0x08, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x41, 0x01, 0x08, 0x00, 0x13, 0x0C, 0x41, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x81, 0x06, 0x52, 0x4F, 0x04, 0xD8, 0x10, 0x50, 0x00, 0x2F, 0x00, 0x08, + 0xB0, 0x06, 0x03, 0x03, + 0x00, 0x00, 0xBB, 0xBB, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xE9, 0xA6, + 0x5E, 0x1F, 0x04, 0xD8, 0x18, 0x40, 0x00, 0x08, 0x00, 0x20, 0x50, 0x06, + 0x07, 0x00, 0x00, 0x00, + 0x20, 0x50, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01, 0x20, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x20, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x16, + 0x50, 0x4F, 0x04, 0x18, + 0x00, 0x30, 0x00, 0x06, 0x00, 0x2F, 0x00, 0x40, 0x10, 0x06, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA9, 0x26, + 0x9A, 0x1F, 0x98, 0x18, + 0x01, 0x01, 0x30, 0x00, 0x60, 0x00, 0x60, 0x00, 0x2F, 0x00, 0x21, 0x01, + 0x00, 0x00, 0x1F, 0x21, + 0x01, 0x00, 0x00, 0x1F, 0x00, 0x01, 0x20, 0x00, 0x99, 0x24, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB8, 0x0B, + 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x1A, + 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x11, 0x1A, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x11, 0x1A, 0x00, 0x00, 0x02, 0x00, 0x01, 0x01, 0x64, 0x00, + 0x64, 0x00, 0xD0, 0x00, + 0x80, 0x0C, 0xF0, 0x0A, 0xD0, 0x00, 0x00, 0x15, 0x00, 0x12, 0x50, 0x01, + 0x50, 0x01, 0x00, 0x15, + 0x00, 0x12, 0x50, 0x01, 0x80, 0x0C, 0xF0, 0x0A, 0xD0, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x0F, 0x00, 0x06, + 0x0A, 0x0F, 0x02, 0x64, + 0x90, 0x01, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0x0F, 0xFF, 0x0F, + 0x14, 0x14, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x02, 0x64, 0x90, 0x01, 0x90, 0x01, 0x80, 0x00, 0x80, 0x00, 0xF4, 0x01, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x00, 0x64, 0x00, + 0x80, 0x01, 0x64, 0x00, + 0x80, 0x01, 0x96, 0x00, 0x64, 0x00, 0x20, 0x00, 0x02, 0x00, 0x96, 0x00, + 0x64, 0x00, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x05, 0x01, 0xF4, 0x01, + 0x2C, 0x01, 0x02, 0xFF, + 0x7F, 0xFF, 0x7F, 0x02, 0x32, 0x64, 0x00, 0x00, 0x02, 0x02, 0xA2, 0x30, + 0x29, 0x64, 0x00, 0x64, + 0x00, 0xC8, 0x00, 0x58, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x64, 0x00, + 0x32, 0x00, 0x01, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x83, 0x02, 0x10, 0x00, + 0x20, 0x00, 0x18, 0x00, + 0x00, 0x01, 0x62, 0x84, 0x40, 0x01, 0x20, 0x00, 0x10, 0x00, 0x64, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x20, 0x00, + 0x40, 0x2C, 0x7D, 0x00, + 0x2C, 0x01, 0x64, 0x00, 0x00, 0x0A, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x01, 0x64, 0x00, + 0x2C, 0x01, 0x00, 0x20, + 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x5A, 0x00, + 0x14, 0x00, 0x01, 0x03, + 0x01, 0x0A, 0x00, 0x10, 0x11, 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x50, 0x00, + 0x70, 0x00, 0x32, 0x00, + 0x40, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x03, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x02, + 0x00, 0x02, 0x64, 0x00, 0x0A, 0x64, 0x00, 0x64, 0x00, 0x0E, 0x00, 0x05, + 0x00, 0x10, 0xC2, 0x00, + 0x32, 0x00, 0x20, 0x31, 0x0F, 0x02, 0x20, 0x1F, 0x00, 0x0A, 0x00, 0x14, + 0x92, 0x11, 0x15, 0x00, + 0x13, 0x00, 0x02, 0x10, 0x0A, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, 0x10, + 0xE8, 0x10, 0x00, 0x40, + 0x00, 0x62, 0x94, 0x94, 0x28, 0xF4, 0x0A, 0x00, 0x18, 0x00, 0x10, 0x00, + 0x30, 0x00, 0x05, 0x14, + 0x05, 0x20, 0x03, 0x10, 0xD8, 0x05, 0x32, 0xF8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0xC8, 0x00, + 0xC8, 0x00, 0x0A, 0x0A, + 0x20, 0x00, 0x64, 0x00, 0x01, 0x03, 0x64, 0x00, 0x64, 0x00, 0x01, 0x03, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x32, 0x1A, 0xFA, 0x00, 0x0A, 0x05, 0x20, 0x0A, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0A, 0x00, 0x14, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x03, 0x50, 0x00, + 0x2C, 0x01, 0x32, 0xFF, + 0x7F, 0x0B, 0x01, 0x32, 0x00, 0x20, 0x03, 0x00, 0x0A, 0x20, 0x14, 0x05, + 0xF8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, + 0x03, 0x04, 0x00, 0x01, + 0x02, 0x03, 0x50, 0x01, 0x50, 0x01, 0x50, 0x01, 0x50, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x7D, 0x6E, 0x67, + 0x65, 0x64, 0x64, 0x64, + 0x64, 0x64, 0x64, 0x64, 0x64, 0x65, 0x67, 0x6E, 0x7D, 0x96, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x0F, 0x13, 0x22, 0x22, 0x22, 0x00, 0x00, 0x0F, 0x00, 0x28, 0x00, 0x3C, + 0x00, 0x50, 0x00, 0xA0, + 0x00, 0x40, 0x01, 0xE0, 0x01, 0xD0, 0x02, 0xBF, 0x03, 0x5F, 0x04, 0xFF, + 0x04, 0x4F, 0x05, 0x63, + 0x05, 0x77, 0x05, 0x90, 0x05, 0x0F, 0x00, 0x28, 0x00, 0x3C, 0x00, 0x50, + 0x00, 0xA0, 0x00, 0x40, + 0x01, 0xE0, 0x01, 0xD0, 0x02, 0xC0, 0x03, 0x00, 0x05, 0x3F, 0x06, 0x2F, + 0x07, 0x1F, 0x08, 0xBF, + 0x08, 0x5F, 0x09, 0xAF, 0x09, 0xC3, 0x09, 0xD7, 0x09, 0xF5, 0x09, 0x00, + 0x00, 0x0A, 0x00, 0x19, + 0x00, 0x28, 0x00, 0x6C, 0x00, 0x27, 0x01, 0xD1, 0x01, 0xD0, 0x02, 0xC6, + 0x03, 0x72, 0x04, 0x21, + 0x05, 0x69, 0x05, 0x86, 0x05, 0x95, 0x05, 0x9F, 0x05, 0x00, 0x00, 0x0F, + 0x00, 0x1C, 0x00, 0x28, + 0x00, 0x78, 0x00, 0x1C, 0x01, 0xC6, 0x01, 0xBE, 0x02, 0xAE, 0x03, 0x00, + 0x05, 0x4F, 0x06, 0x44, + 0x07, 0x39, 0x08, 0xDE, 0x08, 0x8A, 0x09, 0xD8, 0x09, 0xE3, 0x09, 0xF1, + 0x09, 0xFF, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x64, 0x00, + 0x00, 0x00, 0x64, 0x80, + 0x10, 0xE5, 0x00, 0x00, 0x64, 0x80, 0x40, 0x00, 0x80, 0x00, 0xC0, 0x00, + 0x00, 0x01, 0x40, 0x01, + 0x80, 0x01, 0xC0, 0x01, 0x00, 0x02, 0x40, 0x02, 0x80, 0x02, 0xC0, 0x02, + 0x00, 0x03, 0x40, 0x03, + 0x80, 0x03, 0xC0, 0x03, 0x00, 0x04, 0x3F, 0x04, 0x7F, 0x04, 0xBF, 0x04, + 0xFF, 0x04, 0x3F, 0x05, + 0x7F, 0x05, 0xBF, 0x05, 0xFF, 0x05, 0x3F, 0x06, 0x7F, 0x06, 0xBF, 0x06, + 0xFF, 0x06, 0x3F, 0x07, + 0x7F, 0x07, 0xBF, 0x07, 0xFF, 0x07, 0xFF, 0x07, 0xBF, 0x07, 0x7F, 0x07, + 0x3F, 0x07, 0xFF, 0x06, + 0xBF, 0x06, 0x7F, 0x06, 0x3F, 0x06, 0xFF, 0x05, 0xBF, 0x05, 0x7F, 0x05, + 0x3F, 0x05, 0xFF, 0x04, + 0xBF, 0x04, 0x7F, 0x04, 0x3F, 0x04, 0x00, 0x04, 0xC0, 0x03, 0x80, 0x03, + 0x40, 0x03, 0x00, 0x03, + 0xC0, 0x02, 0x80, 0x02, 0x40, 0x02, 0x00, 0x02, 0xC0, 0x01, 0x80, 0x01, + 0x40, 0x01, 0x00, 0x01, + 0xC0, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x40, 0x00, 0x60, 0x00, + 0x80, 0x00, 0xA0, 0x00, + 0xC0, 0x00, 0xE0, 0x00, 0x00, 0x01, 0x00, 0x01, 0xE0, 0x00, 0xC0, 0x00, + 0xA0, 0x00, 0x80, 0x00, + 0x60, 0x00, 0x40, 0x00, 0x20, 0x00, 0x14, 0x05, 0x14, 0x05, 0x14, 0x05, + 0x14, 0x05, 0x14, 0x05, + 0x14, 0x05, 0x14, 0x05, 0x14, 0x05, 0x14, 0x05, 0x14, 0x05, 0x14, 0x05, + 0x14, 0x05, 0x14, 0x05, + 0x14, 0x05, 0x14, 0x05, 0x14, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, + 0x00, 0x03, 0x00, 0x03, + 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, + 0x00, 0x03, 0x00, 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, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCD, 0x00, 0xCD, 0x00, 0xCD, 0x00, + 0xCD, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x05, 0x07, + 0x08, 0x0C, 0x0D, 0x0F, + 0x0F, 0x0F, 0x0E, 0x0E, 0x0E, 0x0D, 0x0D, 0x0D, 0x0A, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA9, 0x64, + 0x9A, 0x1F, 0x18, 0x18, + 0x01, 0x01, 0x30, 0x00, 0x60, 0x00, 0x60, 0x00, 0x2F, 0x00, 0x21, 0x01, + 0x00, 0x00, 0x1F, 0x20, + 0x01, 0x00, 0x00, 0x1F, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x98, 0x24, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x46, 0x50, 0x10, + 0x10, 0xB0, 0x20, 0xFF, + 0xD0, 0x00, 0x06, 0x14, 0x14, 0x2A, 0x3A, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x14, 0x6E, 0x6E, 0x47, 0x1A, 0x38, 0x00, 0x00, 0x00, 0x01, 0x0F, + 0x1E, 0x00, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA9, 0x24, + 0x94, 0x1F, 0x18, 0x18, + 0x01, 0x00, 0x25, 0x00, 0x25, 0x00, 0x50, 0x00, 0x24, 0x00, 0x70, 0x01, + 0x00, 0x00, 0x1F, 0x80, + 0x01, 0x00, 0x00, 0x1F, 0x00, 0x01, 0x10, 0x00, 0x55, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x75, 0x6A, 0xB0, +}; + +#endif diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/Makefile b/drivers/input/touchscreen/stmicro_mmi/fts_lib/Makefile new file mode 100644 index 000000000000..49ac2e18229d --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the FTS touchscreen driver. +# + + +obj-y += ftsCompensation.o ftsCore.o ftsError.o ftsFrame.o ftsIO.o ftsTest.o ftsTime.o ftsTool.o ftsFlash.o ftsGesture.o + diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsCompensation.c b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsCompensation.c new file mode 100644 index 000000000000..36a1defeb6c9 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsCompensation.c @@ -0,0 +1,836 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS functions for getting Initialization Data * + * * + ************************************************************************** + ************************************************************************** + * + */ +/*! + * \file ftsCompensation.c + * \brief Contains all the function to work with Initialization Data + */ + +#include "ftsCompensation.h" +#include "ftsCore.h" +#include "ftsError.h" +#include "ftsFrame.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTime.h" +#include "ftsTool.h" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * Request to the FW to load the specified Initialization Data + * @param type type of Initialization data to load @link load_opt Load Host + * Data Option @endlink + * @return OK if success or an error code which specify the type of error + */ +int requestCompensationData(u8 type) +{ + int ret = ERROR_OP_NOT_ALLOW; + int retry = 0; + + logError(0, "%s %s: Requesting compensation data... attemp = %d\n", tag, + __func__, retry + 1); + while (retry < RETRY_COMP_DATA_READ) { + ret = writeSysCmd(SYS_CMD_LOAD_DATA, &type, 1); + /* send request to load in memory the Compensation Data */ + if (ret < OK) { + logError(1, "%s %s: failed at %d attemp!\n", tag, + __func__, retry + 1); + retry += 1; + } else { + logError(0, + "%s %s: Request Compensation data FINISHED!\n", + tag, + __func__); + return OK; + } + } + + logError(1, "%s %s: Requesting compensation data... ERROR %08X\n", tag, + __func__, ret | ERROR_REQU_COMP_DATA); + return ret | ERROR_REQU_COMP_DATA; +} + + +/** + * Read Initialization Data Header and check that the type loaded match + * with the one previously requested + * @param type type of Initialization data requested @link load_opt Load Host + * Data Option @endlink + * @param header pointer to DataHeader variable which will contain the header + * @param address pointer to a variable which will contain the updated address + * to the next data + * @return OK if success or an error code which specify the type of error + */ +int readCompensationDataHeader(u8 type, DataHeader *header, u64 *address) +{ + u64 offset = ADDR_FRAMEBUFFER; + u8 data[COMP_DATA_HEADER]; + int ret; + + ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, offset, data, + COMP_DATA_HEADER, DUMMY_FRAMEBUFFER); + if (ret < OK) { /* i2c function have already a retry mechanism */ + logError(1, + "%s %s: error while reading data header ERROR %08X\n", + tag, + __func__, ret); + return ret; + } + + logError(0, "%s Read Data Header done!\n", tag); + + if (data[0] != HEADER_SIGNATURE) { + logError(1, + "%s %s: The Header Signature was wrong! %02X != %02X ERROR %08X\n", + tag, __func__, data[0], HEADER_SIGNATURE, + ERROR_WRONG_DATA_SIGN); + return ERROR_WRONG_DATA_SIGN; + } + + + if (data[1] != type) { + logError(1, "%s %s: Wrong type found! %02X!=%02X ERROR %08X\n", + tag, __func__, data[1], type, ERROR_DIFF_DATA_TYPE); + return ERROR_DIFF_DATA_TYPE; + } + + logError(0, "%s Type = %02X of Compensation data OK!\n", tag, type); + + header->type = type; + + *address = offset + COMP_DATA_HEADER; + + return OK; +} + + +/** + * Read MS Global Initialization data from the buffer such as Cx1 + * @param address pointer to a variable which contain the address from where + * to read the data and will contain the updated address to the next data + * @param global pointer to MutualSenseData variable which will contain the MS + * initialization data + * @return OK if success or an error code which specify the type of error + */ +int readMutualSenseGlobalData(u64 *address, MutualSenseData *global) +{ + u8 data[COMP_DATA_GLOBAL]; + int ret; + + logError(0, "%s Address for Global data= %08llX\n", tag, *address); + + ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, *address, data, + COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(1, "%s %s: error while reading info data ERROR %08X\n", + tag, __func__, ret); + return ret; + } + logError(0, "%s Global data Read !\n", tag); + + global->header.force_node = data[0]; + global->header.sense_node = data[1]; + global->cx1 = data[2]; + /* all other bytes are reserved atm */ + + logError(0, "%s force_len = %d sense_len = %d CX1 = %d\n", tag, + global->header.force_node, global->header.sense_node, + global->cx1); + + *address += COMP_DATA_GLOBAL; + return OK; +} + + +/** + * Read MS Initialization data for each node from the buffer + * @param address a variable which contain the address from where to read the + * data + * @param node pointer to MutualSenseData variable which will contain the MS + * initialization data + * @return OK if success or an error code which specify the type of error + */ +int readMutualSenseNodeData(u64 address, MutualSenseData *node) +{ + int ret; + int size = node->header.force_node * node->header.sense_node; + + logError(0, "%s Address for Node data = %08llX\n", tag, address); + + node->node_data = (i8 *)kmalloc(size * (sizeof(i8)), GFP_KERNEL); + + if (node->node_data == NULL) { + logError(1, "%s %s: can not allocate node_data... ERROR %08X", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + logError(0, "%s Node Data to read %d bytes\n", tag, size); + ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, address, + node->node_data, size, DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(1, + "%s %s: error while reading node data ERROR %08X\n", + tag, + __func__, ret); + kfree(node->node_data); + return ret; + } + node->node_data_size = size; + + logError(0, "%s Read node data OK!\n", tag); + + return size; +} + +/** + * Perform all the steps to read the necessary info for MS Initialization data + * from the buffer and store it in a MutualSenseData variable + * @param type type of MS Initialization data to read @link load_opt Load Host + * Data Option @endlink + * @param data pointer to MutualSenseData variable which will contain the MS + * initialization data + * @return OK if success or an error code which specify the type of error + */ +int readMutualSenseCompensationData(u8 type, MutualSenseData *data) +{ + int ret; + u64 address; + + data->node_data = NULL; + + if (!(type == LOAD_CX_MS_TOUCH || type == LOAD_CX_MS_LOW_POWER || + type == LOAD_CX_MS_KEY || type == LOAD_CX_MS_FORCE)) { + logError(1, + "%s %s: Choose a MS type of compensation data ERROR %08X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestCompensationData(type); + if (ret < 0) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, + ERROR_REQU_COMP_DATA); + return ret | ERROR_REQU_COMP_DATA; + } + + ret = readCompensationDataHeader(type, &(data->header), &address); + if (ret < 0) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, + ERROR_COMP_DATA_HEADER); + return ret | ERROR_COMP_DATA_HEADER; + } + + ret = readMutualSenseGlobalData(&address, data); + if (ret < 0) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, + ERROR_COMP_DATA_GLOBAL); + return ret | ERROR_COMP_DATA_GLOBAL; + } + + ret = readMutualSenseNodeData(address, data); + if (ret < 0) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, + ERROR_COMP_DATA_NODE); + return ret | ERROR_COMP_DATA_NODE; + } + + return OK; +} + +/** + * Read SS Global Initialization data from the buffer such as Ix1/Cx1 for force + * and sense + * @param address pointer to a variable which contain the address from where + * to read the data and will contain the updated address to the next data + * @param global pointer to MutualSenseData variable which will contain the SS + * initialization data + * @return OK if success or an error code which specify the type of error + */ +int readSelfSenseGlobalData(u64 *address, SelfSenseData *global) +{ + int ret; + u8 data[COMP_DATA_GLOBAL]; + + logError(0, "%s Address for Global data= %08llX\n", tag, *address); + ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, *address, data, + COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(1, + "%s %s: error while reading the data... ERROR %08X\n", + tag, + __func__, ret); + return ret; + } + + logError(0, "%s Global data Read !\n", tag); + + + global->header.force_node = data[0]; + global->header.sense_node = data[1]; + global->f_ix1 = data[2]; + global->s_ix1 = data[3]; + global->f_cx1 = (i8)data[4]; + global->s_cx1 = (i8)data[5]; + global->f_max_n = data[6]; + global->s_max_n = data[7]; + global->f_ix0 = data[8]; + global->s_ix0 = data[9]; + + logError(0, + "%s force_len = %d sense_len = %d f_ix1 = %d s_ix1 = %d f_cx1 = %d s_cx1 = %d\n", + tag, global->header.force_node, global->header.sense_node, + global->f_ix1, global->s_ix1, global->f_cx1, global->s_cx1); + logError(0, "%s max_n = %d s_max_n = %d f_ix0 = %d s_ix0 = %d\n", + tag, global->f_max_n, global->s_max_n, + global->f_ix0, global->s_ix0); + + *address += COMP_DATA_GLOBAL; + + return OK; +} + +/** + * Read SS Initialization data for each node of force and sense channels from + * the buffer + * @param address a variable which contain the address from where to read the + * data + * @param node pointer to SelfSenseData variable which will contain the SS + * initialization data + * @return OK if success or an error code which specify the type of error + */ +int readSelfSenseNodeData(u64 address, SelfSenseData *node) +{ + int size = node->header.force_node * 2 + node->header.sense_node * 2; + u8 *data = NULL; + int ret; + + data = (u8 *)kzalloc(size, GFP_KERNEL); + node->ix2_fm = (u8 *)kmalloc(node->header.force_node * (sizeof(u8)), + GFP_KERNEL); + if (node->ix2_fm == NULL || data == NULL) { + logError(1, + "%s %s: can not allocate memory for ix2_fm... ERROR %08X", + tag, + __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + node->cx2_fm = (i8 *)kmalloc(node->header.force_node * (sizeof(i8)), + GFP_KERNEL); + if (node->cx2_fm == NULL) { + logError(1, + "%s %s: can not allocate memory for cx2_fm ... ERROR %08X", + tag, + __func__, ERROR_ALLOC); + kfree(node->ix2_fm); + kfree(data); + return ERROR_ALLOC; + } + node->ix2_sn = (u8 *)kmalloc(node->header.sense_node * (sizeof(u8)), + GFP_KERNEL); + if (node->ix2_sn == NULL) { + logError(1, + "%s %s: can not allocate memory for ix2_sn ERROR %08X", + tag, + __func__, ERROR_ALLOC); + kfree(node->ix2_fm); + kfree(node->cx2_fm); + kfree(data); + return ERROR_ALLOC; + } + node->cx2_sn = (i8 *)kmalloc(node->header.sense_node * (sizeof(i8)), + GFP_KERNEL); + if (node->cx2_sn == NULL) { + logError(1, + "%s %s: can not allocate memory for cx2_sn ERROR %08X", + tag, + __func__, ERROR_ALLOC); + kfree(node->ix2_fm); + kfree(node->cx2_fm); + kfree(node->ix2_sn); + kfree(data); + return ERROR_ALLOC; + } + + + logError(0, "%s Address for Node data = %08llX\n", tag, address); + + logError(0, "%s Node Data to read %d bytes\n", tag, size); + + ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, address, data, + size, DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(1, "%s %s: error while reading data... ERROR %08X\n", + tag, __func__, ret); + kfree(node->ix2_fm); + kfree(node->cx2_fm); + kfree(node->ix2_sn); + kfree(node->cx2_sn); + kfree(data); + return ret; + } + + logError(0, "%s Read node data ok!\n", tag); + + memcpy(node->ix2_fm, data, node->header.force_node); + memcpy(node->ix2_sn, &data[node->header.force_node], + node->header.sense_node); + memcpy(node->cx2_fm, &data[node->header.force_node + + node->header.sense_node], + node->header.force_node); + memcpy(node->cx2_sn, &data[node->header.force_node * 2 + + node->header.sense_node], + node->header.sense_node); + kfree(data); + + return OK; +} + +/** + * Perform all the steps to read the necessary info for SS Initialization data + * from the buffer and store it in a SelfSenseData variable + * @param type type of SS Initialization data to read @link load_opt Load Host + * Data Option @endlink + * @param data pointer to SelfSenseData variable which will contain the SS + * initialization data + * @return OK if success or an error code which specify the type of error + */ +int readSelfSenseCompensationData(u8 type, SelfSenseData *data) +{ + int ret; + u64 address; + + data->ix2_fm = NULL; + data->cx2_fm = NULL; + data->ix2_sn = NULL; + data->cx2_sn = NULL; + + if (!(type == LOAD_CX_SS_TOUCH || type == LOAD_CX_SS_TOUCH_IDLE || + type == LOAD_CX_SS_KEY || type == LOAD_CX_SS_FORCE)) { + logError(1, + "%s %s: Choose a SS type of compensation data ERROR %08X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestCompensationData(type); + if (ret < 0) { + logError(1, + "%s %s: error while requesting data... ERROR %08X\n", + tag, + __func__, ERROR_REQU_COMP_DATA); + return ret | ERROR_REQU_COMP_DATA; + } + + ret = readCompensationDataHeader(type, &(data->header), &address); + if (ret < 0) { + logError(1, + "%s %s: error while reading data header... ERROR %08X\n", + tag, + __func__, ERROR_COMP_DATA_HEADER); + return ret | ERROR_COMP_DATA_HEADER; + } + + ret = readSelfSenseGlobalData(&address, data); + if (ret < 0) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, + ERROR_COMP_DATA_GLOBAL); + return ret | ERROR_COMP_DATA_GLOBAL; + } + + ret = readSelfSenseNodeData(address, data); + if (ret < 0) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, + ERROR_COMP_DATA_NODE); + return ret | ERROR_COMP_DATA_NODE; + } + + return OK; +} + +/** + * Read TOT MS Global Initialization data from the buffer such as number of + * force and sense channels + * @param address pointer to a variable which contain the address from where + * to read the data and will contain the updated address to the next data + * @param global pointer to a variable which will contain the TOT MS + * initialization data + * @return OK if success or an error code which specify the type of error + */ +int readTotMutualSenseGlobalData(u64 *address, TotMutualSenseData *global) +{ + int ret; + u8 data[COMP_DATA_GLOBAL]; + + logError(0, "%s Address for Global data= %04llX\n", tag, *address); + + ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, *address, data, + COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(1, "%s %s: error while reading info data ERROR %08X\n", + tag, __func__, ret); + return ret; + } + logError(0, "%s Global data Read !\n", tag); + + global->header.force_node = data[0]; + global->header.sense_node = data[1]; + /* all other bytes are reserved atm */ + + logError(0, "%s force_len = %d sense_len = %d\n", tag, + global->header.force_node, global->header.sense_node); + + *address += COMP_DATA_GLOBAL; + return OK; +} + + +/** + * Read TOT MS Initialization data for each node from the buffer + * @param address a variable which contain the address from where to read the + * data + * @param node pointer to MutualSenseData variable which will contain the TOT + * MS initialization data + * @return OK if success or an error code which specify the type of error + */ +int readTotMutualSenseNodeData(u64 address, TotMutualSenseData *node) +{ + int ret, i; + int size = node->header.force_node * node->header.sense_node; + int toRead = size * sizeof(u16); + u8 *data; + + logError(0, "%s Address for Node data = %04llX\n", tag, address); + + data = (u8 *)kzalloc(toRead, GFP_KERNEL); + node->node_data = (short *)kmalloc(size * (sizeof(short)), GFP_KERNEL); + if (node->node_data == NULL || data == NULL) { + logError(1, "%s %s: can not allocate node_data... ERROR %08X", + tag, __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + logError(0, "%s Node Data to read %d bytes\n", tag, size); + + ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, address, data, + toRead, DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(1, + "%s %s: error while reading node data ERROR %08X\n", + tag, + __func__, ret); + kfree(node->node_data); + kfree(data); + return ret; + } + node->node_data_size = size; + + for (i = 0; i < size; i++) + node->node_data[i] = ((short)data[i * 2 + 1]) << 8 | data[i * + 2]; + + logError(0, "%s Read node data OK!\n", tag); + kfree(data); + + return size; +} + +/** + * Perform all the steps to read the necessary info for TOT MS Initialization + * data from the buffer and store it in a TotMutualSenseData variable + * @param type type of TOT MS Initialization data to read @link load_opt Load + * Host Data Option @endlink + * @param data pointer to a variable which will contain the TOT MS + * initialization data + * @return OK if success or an error code which specify the type of error + */ +int readTotMutualSenseCompensationData(u8 type, TotMutualSenseData *data) +{ + int ret; + u64 address; + + data->node_data = NULL; + + if (!(type == LOAD_PANEL_CX_TOT_MS_TOUCH || type == + LOAD_PANEL_CX_TOT_MS_LOW_POWER || type == + LOAD_PANEL_CX_TOT_MS_KEY || + type == LOAD_PANEL_CX_TOT_MS_FORCE)) { + logError(1, + "%s %s: Choose a TOT MS type of compensation data ERROR %08X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestCompensationData(type); + if (ret < 0) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, + ERROR_REQU_COMP_DATA); + return ret | ERROR_REQU_COMP_DATA; + } + + ret = readCompensationDataHeader(type, &(data->header), &address); + if (ret < 0) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, + ERROR_COMP_DATA_HEADER); + return ret | ERROR_COMP_DATA_HEADER; + } + + ret = readTotMutualSenseGlobalData(&address, data); + if (ret < 0) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, + ERROR_COMP_DATA_GLOBAL); + return ret | ERROR_COMP_DATA_GLOBAL; + } + + ret = readTotMutualSenseNodeData(address, data); + if (ret < 0) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, + ERROR_COMP_DATA_NODE); + return ret | ERROR_COMP_DATA_NODE; + } + + return OK; +} + +/** + * Read TOT SS Global Initialization data from the buffer such as number of + * force and sense channels + * @param address pointer to a variable which contain the address from where + * to read the data and will contain the updated address to the next data + * @param global pointer to a variable which will contain the TOT SS + * initialization data + * @return OK if success or an error code which specify the type of error + */ +int readTotSelfSenseGlobalData(u64 *address, TotSelfSenseData *global) +{ + int ret; + u8 data[COMP_DATA_GLOBAL]; + + logError(0, "%s Address for Global data= %04llX\n", tag, *address); + ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, *address, data, + COMP_DATA_GLOBAL, DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(1, + "%s %s: error while reading the data... ERROR %08X\n", + tag, + __func__, ret); + return ret; + } + + logError(0, "%s Global data Read !\n", tag); + + + global->header.force_node = data[0]; + global->header.sense_node = data[1]; + + + logError(0, "%s force_len = %d sense_len = %d\n", tag, + global->header.force_node, global->header.sense_node); + + + *address += COMP_DATA_GLOBAL; + + return OK; +} + +/** + * Read TOT SS Global Initialization data from the buffer such as number of + * force and sense channels + * @param address pointer to a variable which contain the address from where + * to read the data and will contain the updated address to the next data + * @param node pointer to a variable which will contain the TOT SS + * initialization data + * @return OK if success or an error code which specify the type of error + */ +int readTotSelfSenseNodeData(u64 address, TotSelfSenseData *node) +{ + int size = node->header.force_node * 2 + node->header.sense_node * 2; + int toRead = size * 2; /* *2 2 bytes each node */ + u8 *data; + int ret, i, j = 0; + + data = (u8 *)kzalloc(toRead, GFP_KERNEL); + node->ix_fm = (u16 *)kmalloc(node->header.force_node * (sizeof(u16)), + GFP_KERNEL); + if (node->ix_fm == NULL || data == NULL) { + logError(1, + "%s %s: can not allocate memory for ix2_fm... ERROR %08X", + tag, + __func__, ERROR_ALLOC); + return ERROR_ALLOC; + } + + node->cx_fm = (short *)kmalloc(node->header.force_node * + (sizeof(short)), GFP_KERNEL); + if (node->cx_fm == NULL) { + logError(1, + "%s %s: can not allocate memory for cx2_fm ... ERROR %08X", + tag, + __func__, ERROR_ALLOC); + kfree(node->ix_fm); + kfree(data); + return ERROR_ALLOC; + } + node->ix_sn = (u16 *)kmalloc(node->header.sense_node * (sizeof(u16)), + GFP_KERNEL); + if (node->ix_sn == NULL) { + logError(1, + "%s %s: can not allocate memory for ix2_sn ERROR %08X", + tag, + __func__, ERROR_ALLOC); + kfree(node->ix_fm); + kfree(node->cx_fm); + kfree(data); + return ERROR_ALLOC; + } + node->cx_sn = (short *)kmalloc(node->header.sense_node * + (sizeof(short)), GFP_KERNEL); + if (node->cx_sn == NULL) { + logError(1, + "%s %s: can not allocate memory for cx2_sn ERROR %08X", + tag, + __func__, ERROR_ALLOC); + kfree(node->ix_fm); + kfree(node->cx_fm); + kfree(node->ix_sn); + kfree(data); + return ERROR_ALLOC; + } + + + logError(0, "%s Address for Node data = %04llX\n", tag, address); + + logError(0, "%s Node Data to read %d bytes\n", tag, size); + + ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, address, data, + toRead, DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(1, "%s %s: error while reading data... ERROR %08X\n", + tag, __func__, ret); + kfree(node->ix_fm); + kfree(node->cx_fm); + kfree(node->ix_sn); + kfree(node->cx_sn); + kfree(data); + return ret; + } + + logError(0, "%s Read node data ok!\n", tag); + + j = 0; + for (i = 0; i < node->header.force_node; i++) { + node->ix_fm[i] = ((u16)data[j + 1]) << 8 | data[j]; + j += 2; + } + + for (i = 0; i < node->header.sense_node; i++) { + node->ix_sn[i] = ((u16)data[j + 1]) << 8 | data[j]; + j += 2; + } + + for (i = 0; i < node->header.force_node; i++) { + node->cx_fm[i] = ((short)data[j + 1]) << 8 | data[j]; + j += 2; + } + + for (i = 0; i < node->header.sense_node; i++) { + node->cx_sn[i] = ((short)data[j + 1]) << 8 | data[j]; + j += 2; + } + + if (j != toRead) + logError(1, "%s %s: parsed a wrong number of bytes %d!=%d\n", + tag, __func__, j, toRead); + kfree(data); + + return OK; +} + +/** + * Perform all the steps to read the necessary info for TOT SS Initialization + * data from the buffer and store it in a TotSelfSenseData variable + * @param type type of TOT MS Initialization data to read @link load_opt Load + * Host Data Option @endlink + * @param data pointer to a variable which will contain the TOT MS + * initialization data + * @return OK if success or an error code which specify the type of error + */ +int readTotSelfSenseCompensationData(u8 type, TotSelfSenseData *data) +{ + int ret; + u64 address; + + data->ix_fm = NULL; + data->cx_fm = NULL; + data->ix_sn = NULL; + data->cx_sn = NULL; + + if (!(type == LOAD_PANEL_CX_TOT_SS_TOUCH || type == + LOAD_PANEL_CX_TOT_SS_TOUCH_IDLE || type == + LOAD_PANEL_CX_TOT_SS_KEY || + type == LOAD_PANEL_CX_TOT_SS_FORCE)) { + logError(1, + "%s %s: Choose a TOT SS type of compensation data ERROR %08X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + ret = requestCompensationData(type); + if (ret < 0) { + logError(1, + "%s %s: error while requesting data... ERROR %08X\n", + tag, + __func__, ERROR_REQU_COMP_DATA); + return ret | ERROR_REQU_COMP_DATA; + } + + ret = readCompensationDataHeader(type, &(data->header), &address); + if (ret < 0) { + logError(1, + "%s %s: error while reading data header... ERROR %08X\n", + tag, + __func__, ERROR_COMP_DATA_HEADER); + return ret | ERROR_COMP_DATA_HEADER; + } + + ret = readTotSelfSenseGlobalData(&address, data); + if (ret < 0) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, + ERROR_COMP_DATA_GLOBAL); + return ret | ERROR_COMP_DATA_GLOBAL; + } + + ret = readTotSelfSenseNodeData(address, data); + if (ret < 0) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, + ERROR_COMP_DATA_NODE); + return ret | ERROR_COMP_DATA_NODE; + } + + return OK; +} diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsCompensation.h b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsCompensation.h new file mode 100644 index 000000000000..04079e34c581 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsCompensation.h @@ -0,0 +1,136 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS functions for getting Initialization Data ** + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsCompensation.h + * \brief Contains all the definitions and structs to work with Initialization + * Data + */ + +#ifndef FTS_COMPENSATION_H +#define FTS_COMPENSATION_H + +#include "ftsCore.h" +#include "ftsSoftware.h" + + + +#define RETRY_COMP_DATA_READ 2 /* /< max number of attempts to read + * initialization data */ + + +/* Bytes dimension of Compensation Data Format */ + +#define COMP_DATA_HEADER DATA_HEADER /* /< size in bytes of + * initialization data header */ +#define COMP_DATA_GLOBAL (16 - COMP_DATA_HEADER) /* /< size in bytes + * of initialization + *data general info */ + + +#define HEADER_SIGNATURE 0xA5 /* /< signature used as starting byte of + * data loaded in memory */ + + + +/** + * Struct which contains the general info about Frames and Initialization Data + */ +typedef struct { + int force_node; /* /< Number of Force Channels in the + * frame/Initialization data */ + int sense_node; /* /< Number of Sense Channels in the + * frame/Initialization data */ + int type; /* /< Type of frame/Initialization data */ +} DataHeader; + +/** + * Struct which contains the MS Initialization data + */ +typedef struct { + DataHeader header; /* /< Header */ + i8 cx1; /* /< Cx1 value (can be negative)) */ + i8 *node_data; /* /< Pointer to an array of bytes which contains the + * CX2 data (can be negative) */ + int node_data_size; /* /< size of the data */ +} MutualSenseData; + + +/** + * Struct which contains the SS Initialization data + */ +typedef struct { + DataHeader header; /* /< Header */ + u8 f_ix1; /* /< IX1 Force */ + u8 s_ix1; /* /< IX1 Sense */ + i8 f_cx1; /* /< CX1 Force (can be negative) */ + i8 s_cx1; /* /< CX1 Sense (can be negative) */ + u8 f_max_n; /* /< Force MaxN */ + u8 s_max_n; /* /< Sense MaxN */ + u8 f_ix0; /* /< IX0 Force */ + u8 s_ix0; /* /< IX0 Sense */ + u8 *ix2_fm; /* /< pointer to an array of bytes which contains Force + * Ix2 data node */ + u8 *ix2_sn; /* /< pointer to an array of bytes which contains Sense + * Ix2 data node */ + i8 *cx2_fm; /* /< pointer to an array of bytes which contains Force + * Cx2 data node (can be negative) */ + i8 *cx2_sn; /* /< pointer to an array of bytes which contains Sense + * Cx2 data node (can be negative)) */ +} SelfSenseData; + +/** + * Struct which contains the TOT MS Initialization data + */ +typedef struct { + DataHeader header; /* /< Header */ + short *node_data; /* /< pointer to an array of ushort which + * contains TOT MS Initialization data */ + int node_data_size; /* /< size of data */ +} TotMutualSenseData; + +/** + * Struct which contains the TOT SS Initialization data + */ +typedef struct { + DataHeader header; /* /< Header */ + + u16 *ix_fm; /* /< pointer to an array of ushort which contains TOT + * SS IX Force data */ + u16 *ix_sn; /* /< pointer to an array of ushort which contains TOT + * SS IX Sense data */ + short *cx_fm; /* /< pointer to an array of ushort which contains TOT + * SS CX Force data (can be negative) */ + short *cx_sn; /* /< pointer to an array of ushort which contains TOT + * SS CX Sense data (can be negative) */ +} TotSelfSenseData; + + + +int requestCompensationData(u8 type); +int readCompensationDataHeader(u8 type, DataHeader *header, u64 *address); +int readMutualSenseGlobalData(u64 *address, MutualSenseData *global); +int readMutualSenseNodeData(u64 address, MutualSenseData *node); +int readMutualSenseCompensationData(u8 type, MutualSenseData *data); +int readSelfSenseGlobalData(u64 *address, SelfSenseData *global); +int readSelfSenseNodeData(u64 address, SelfSenseData *node); +int readSelfSenseCompensationData(u8 type, SelfSenseData *data); +int readToTMutualSenseGlobalData(u64 *address, TotMutualSenseData *global); +int readToTMutualSenseNodeData(u64 address, TotMutualSenseData *node); +int readTotMutualSenseCompensationData(u8 type, TotMutualSenseData *data); +int readTotSelfSenseGlobalData(u64 *address, TotSelfSenseData *global); +int readTotSelfSenseNodeData(u64 address, TotSelfSenseData *node); +int readTotSelfSenseCompensationData(u8 type, TotSelfSenseData *data); +#endif diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsCore.c b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsCore.c new file mode 100644 index 000000000000..fd64be681e0b --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsCore.c @@ -0,0 +1,1169 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Core functions * + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsCore.c + * \brief Contains the implementation of the Core functions + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ftsCompensation.h" +#include "ftsCore.h" +#include "ftsError.h" +#include "ftsIO.h" +#include "ftsTest.h" +#include "ftsTime.h" +#include "ftsTool.h" + +/** @addtogroup system_info + * @{ + */ +SysInfo systemInfo; /* /< Global System Info variable, accessible in all the + * driver */ +/** @}*/ + +static int reset_gpio = GPIO_NOT_DEFINED; /* /< gpio number of the rest + * pin, the value is + * GPIO_NOT_DEFINED if the + * reset pin is not connected */ +static int system_reseted_up; /* /< flag checked during resume to + * understand if there was a system + * reset and restore the proper state */ +static int system_reseted_down; /* /< flag checked during suspend to + * understand if there was a system + * reset and restore the proper state */ +static int disable_irq_count = 1; /* /< count the number of call to + * disable_irq, start with 1 because at + * the boot IRQ are already disabled */ +DEFINE_MUTEX(fts_int_mlock); /* /< mutex lock to controll the access to the + * disable_irq_counter */ + + +/** + * Initialize core variables of the library. + * Must be called during the probe before any other lib function + * @param info pointer to fts_ts_info which contains info about the device and + *its hw setup + * @return OK if success or an error code which specify the type of error + */ +int initCore(struct fts_ts_info *info) +{ + int ret = OK; + + info->sysinfo = &systemInfo; + + logError(0, "%s %s: Initialization of the Core...\n", tag, __func__); + ret |= openChannel(info->client); + ret |= resetErrorList(); + ret |= initTestToDo(); + setResetGpio(info->board->reset_gpio); + if (ret < OK) + logError(0, "%s %s: Initialization Core ERROR %08X!\n", tag, + __func__, ret); + else + logError(0, "%s %s: Initialization Finished!\n", tag, __func__); + return ret; +} + +/** + * Set the reset_gpio variable with the actual gpio number of the board link to + *the reset pin + * @param gpio gpio number link to the reset pin of the IC + */ +void setResetGpio(int gpio) +{ + reset_gpio = gpio; + logError(1, "%s setResetGpio: reset_gpio = %d\n", tag, reset_gpio); +} + +int fts_wait_for_ready(void) +{ + u8 readData[FIFO_EVENT_SIZE]; + int event_to_search; + + event_to_search = (int)EVT_ID_CONTROLLER_READY; + + return pollForEvent(&event_to_search, 1, readData, GENERAL_TIMEOUT); +} + +/** + * Perform a system reset of the IC. + * If the reset pin is associated to a gpio, the function execute an hw reset + * (toggling of reset pin) otherwise send an hw command to the IC + * @return OK if success or an error code which specify the type of error + */ +int fts_system_reset(void) +{ + int res = -1; + int i; + u8 data[1] = { SYSTEM_RESET_VALUE }; + + logError(0, "%s System resetting...\n", tag); + for (i = 0; i < RETRY_SYSTEM_RESET && res < 0; i++) { + resetErrorList(); + fts_disableInterrupt(); /* disable interrupt before resetting to + * be able to get boot events */ + + if (reset_gpio == GPIO_NOT_DEFINED) + res = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG, + ADDR_SYSTEM_RESET, data, ARRAY_SIZE( + data)); + else { + gpio_set_value(reset_gpio, 0); + msleep(10); + gpio_set_value(reset_gpio, 1); + res = OK; + } + if (res < OK) + logError(1, "%s fts_system_reset: ERROR %08X\n", tag, + ERROR_BUS_W); + else { + res = fts_wait_for_ready(); + if (res < OK) + logError(1, "%s fts_system_reset: ERROR %08X\n", + tag, res); + } + } + if (res < OK) { + logError(1, + "%s fts_system_reset...failed after 3 attempts: ERROR %08X\n", + tag, (res | ERROR_SYSTEM_RESET_FAIL)); + return res | ERROR_SYSTEM_RESET_FAIL; + } else { + logError(0, "%s System reset DONE!\n", tag); + system_reseted_down = 1; + system_reseted_up = 1; + return OK; + } +} + +/** + * Return the value of system_resetted_down. + * @return the flag value: 0 if not set, 1 if set + */ +int isSystemResettedDown(void) +{ + return system_reseted_down; +} + +/** + * Return the value of system_resetted_up. + * @return the flag value: 0 if not set, 1 if set + */ +int isSystemResettedUp(void) +{ + return system_reseted_up; +} + + +/** + * Set the value of system_reseted_down flag + * @param val value to write in the flag + */ +void setSystemResetedDown(int val) +{ + system_reseted_down = val; +} + +/** + * Set the value of system_reseted_up flag + * @param val value to write in the flag + */ +void setSystemResetedUp(int val) +{ + system_reseted_up = val; +} + + +/** @addtogroup events_group + * @{ + */ + +/** + * Poll the FIFO looking for a specified event within a timeout. Support a + *retry mechanism. + * @param event_to_search pointer to an array of int where each element + * correspond to a byte of the event to find. + * If the element of the array has value -1, the byte of the event, + * in the same position of the element is ignored. + * @param event_bytes size of event_to_search + * @param readData pointer to an array of byte which will contain the event + *found + * @param time_to_wait time to wait before going in timeout + * @return OK if success or an error code which specify the type of error + */ +int pollForEvent(int *event_to_search, int event_bytes, u8 *readData, int + time_to_wait) +{ + int i, find, retry, count_err; + int time_to_count; + int err_handling = OK; + StopWatch clock; + + u8 cmd[1] = { FIFO_CMD_READONE }; + char temp[128] = { 0 }; + + find = 0; + retry = 0; + count_err = 0; + time_to_count = time_to_wait / TIMEOUT_RESOLUTION; + + startStopWatch(&clock); + while (find != 1 && retry < time_to_count && + fts_writeReadU8UX(cmd[0], + 0, 0, + readData, + FIFO_EVENT_SIZE, + DUMMY_FIFO) >= OK) { + /* Log of errors */ + if (readData[0] == EVT_ID_ERROR) { + logError(1, "%s %s\n", tag, printHex("ERROR EVENT = ", + readData, + FIFO_EVENT_SIZE, + temp)); + memset(temp, 0, 128); + count_err++; + err_handling = errorHandler(readData, FIFO_EVENT_SIZE); + if ((err_handling & 0xF0FF0000) == + ERROR_HANDLER_STOP_PROC) { + logError(1, + "%s pollForEvent: forced to be stopped! ERROR %08X\n", + tag, err_handling); + return err_handling; + } + } else { + if (readData[0] != EVT_ID_NOEVENT) { + logError(1, "%s %s\n", tag, printHex( + "READ EVENT = ", readData, + FIFO_EVENT_SIZE, + temp)); + memset(temp, 0, 128); + } + if (readData[0] == EVT_ID_CONTROLLER_READY && + event_to_search[0] != EVT_ID_CONTROLLER_READY) { + logError(1, + "%s pollForEvent: Unmanned Controller Ready Event! Setting reset flags...\n", + tag); + setSystemResetedUp(1); + setSystemResetedDown(1); + } + } + + find = 1; + + for (i = 0; i < event_bytes; i++) { + if (event_to_search[i] != -1 && (int)readData[i] != + event_to_search[i]) { + find = 0; + break; + } + } + + retry++; + msleep(TIMEOUT_RESOLUTION); + } + stopStopWatch(&clock); + if ((retry >= time_to_count) && find != 1) { + logError(1, "%s pollForEvent: ERROR %08X\n", tag, + ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } else if (find == 1) { + /* temp = printHex("FOUND EVENT = ", readData, FIFO_EVENT_SIZE); + * */ + /* if (temp != NULL) */ + logError(0, "%s %s\n", tag, printHex("FOUND EVENT = ", readData, + FIFO_EVENT_SIZE, temp)); + memset(temp, 0, 128); + /* kfree(temp); */ + logError(0, + "%s Event found in %d ms (%d iterations)! Number of errors found = %d\n", + tag, elapsedMillisecond(&clock), retry, count_err); + return count_err; + } else { + logError(1, "%s pollForEvent: ERROR %08X\n", tag, ERROR_BUS_R); + return ERROR_BUS_R; + } +} + +/** @}*/ + +/** + * Check that the FW sent the echo even after a command was sent + * @param cmd pointer to an array of byte which contain the command previously + *sent + * @param size size of cmd + * @return OK if success or an error code which specify the type of error + */ +int checkEcho(u8 *cmd, int size) +{ + int ret, i; + int event_to_search[FIFO_EVENT_SIZE]; + u8 readData[FIFO_EVENT_SIZE]; + + + if (size < 1) { + logError(1, + "%s checkEcho: Invalid Size = %d not valid! ERROR %08X\n", + tag, + size, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } else { + if ((size + 4) > FIFO_EVENT_SIZE) + size = FIFO_EVENT_SIZE - 4; + /* Echo event 0x43 0x01 xx xx xx xx xx fifo_status + * therefore command with more than 4 bytes will be trunked */ + + event_to_search[0] = EVT_ID_STATUS_UPDATE; + event_to_search[1] = EVT_TYPE_STATUS_ECHO; + for (i = 2; i < size + 2; i++) + event_to_search[i] = cmd[i - 2]; + if ((cmd[0] == FTS_CMD_SYSTEM) && (cmd[1] == SYS_CMD_SPECIAL) && + ((cmd[2] == SPECIAL_FULL_PANEL_INIT) || + (cmd[2] == SPECIAL_PANEL_INIT))) + ret = pollForEvent(event_to_search, size + 2, readData, + TIMEOUT_ECHO_FPI); + else if ((cmd[0] == FTS_CMD_SYSTEM) && + (cmd[1] == SYS_CMD_CX_TUNING)) + ret = pollForEvent(event_to_search, size + 2, readData, + TIMEOUT_ECHO_SINGLE_ENDED_SPECIAL_AUTOTUNE); + else + ret = pollForEvent(event_to_search, size + 2, readData, + TIEMOUT_ECHO); + if (ret < OK) { + logError(1, + "%s checkEcho: Echo Event not found! ERROR %08X\n", + tag, + ret); + return ret | ERROR_CHECK_ECHO_FAIL; + } else if (ret > OK) { + logError(1, + "%s checkEcho: Echo Event found but with some error events before! num_error = %d\n", + tag, ret); + return ERROR_CHECK_ECHO_FAIL; + } + + logError(0, "%s ECHO OK!\n", tag); + return ret; + } +} + + +/** @addtogroup scan_mode + * @{ + */ +/** + * Set a scan mode in the IC + * @param mode scan mode to set; possible values @link scan_opt Scan Mode + * Option @endlink + * @param settings option for the selected scan mode + * (for example @link active_bitmask Active Mode Bitmask @endlink) + * @return OK if success or an error code which specify the type of error + */ +int setScanMode(u8 mode, u8 settings) +{ + u8 cmd[3] = { FTS_CMD_SCAN_MODE, mode, settings }; + int ret, size = 3; + + logError(0, "%s %s: Setting scan mode: mode = %02X settings = %02X !\n", + tag, __func__, mode, settings); + if (mode == SCAN_MODE_LOW_POWER) + size = 2; + ret = fts_write(cmd, size); /* use write instead of writeFw because + * can be called while the interrupt are + * enabled */ + if (ret < OK) { + logError(1, "%s %s: write failed...ERROR %08X !\n", tag, + __func__, ret); + return ret | ERROR_SET_SCAN_MODE_FAIL; + } + logError(0, "%s %s: Setting scan mode OK!\n", tag, __func__); + return OK; +} +/** @}*/ + + +/** @addtogroup feat_sel + * @{ + */ +/** + * Set a feature and its option in the IC + * @param feat feature to set; possible values @link feat_opt Feature Selection + *Option @endlink + * @param settings pointer to an array of byte which store the options for + * the selected feature (for example the gesture mask to activate + * @link gesture_opt Gesture IDs @endlink) + * @param size in bytes of settings + * @return OK if success or an error code which specify the type of error + */ +int setFeatures(u8 feat, u8 *settings, int size) +{ + u8 *cmd; + int i = 0; + int ret = OK; + + cmd = (u8 *)kzalloc(2 + size, GFP_KERNEL); + if (!cmd) + return ret | ERROR_SET_FEATURE_FAIL; + + logError(0, "%s %s: Setting feature: feat = %02X !\n", tag, __func__, + feat); + cmd[0] = FTS_CMD_FEATURE; + cmd[1] = feat; + logError(0, "%s %s: Settings = ", tag, __func__); + for (i = 0; i < size; i++) { + cmd[2 + i] = settings[i]; + logError(0, "%02X ", settings[i]); + } + logError(0, "\n"); + ret = fts_write(cmd, 2 + size); /* use write instead of writeFw because + * can be called while the interrupts + * are enabled */ + if (ret < OK) { + logError(1, "%s %s: write failed...ERROR %08X !\n", tag, + __func__, ret); + return ret | ERROR_SET_FEATURE_FAIL; + } + logError(0, "%s %s: Setting feature OK!\n", tag, __func__); + kfree(cmd); + return OK; +} +/** @}*/ + +/** @addtogroup sys_cmd + * @{ + */ +/** + * Write a system command to the IC + * @param sys_cmd System Command to execute; possible values + * @link sys_opt System Command Option @endlink + * @param sett settings option for the selected system command + * (@link sys_special_opt Special Command Option @endlink, @link ito_opt + * ITO Test Option @endlink, @link load_opt Load Host Data Option @endlink) + * @param size in bytes of settings + * @return OK if success or an error code which specify the type of error + */ +int writeSysCmd(u8 sys_cmd, u8 *sett, int size) +{ + u8 *cmd; + int ret = OK; + + cmd = (u8 *)kzalloc(2 + size, GFP_KERNEL); + if (!cmd) + return ret | ERROR_SET_FEATURE_FAIL; + + cmd[0] = FTS_CMD_SYSTEM; + cmd[1] = sys_cmd; + + logError(0, "%s %s: Command = %02X %02X ", tag, __func__, cmd[0], + cmd[1]); + for (ret = 0; ret < size; ret++) { + cmd[2 + ret] = sett[ret]; + logError(0, "%02X ", cmd[2 + ret]); + } + logError(0, "\n"); + logError(0, "%s %s: Writing Sys command...\n", tag, __func__); + if (sys_cmd != SYS_CMD_LOAD_DATA) + ret = fts_writeFwCmd(cmd, 2 + size); + else { + if (size >= 1) + ret = requestSyncFrame(sett[0]); + else { + logError(1, "%s %s: No setting argument! ERROR %08X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + kfree(cmd); + return ERROR_OP_NOT_ALLOW; + } + } + if (ret < OK) + logError(1, "%s %s: ERROR %08X\n", tag, __func__, ret); + + else + logError(0, "%s %s: FINISHED!\n", tag, __func__); + kfree(cmd); + + return ret; +} +/** @}*/ + +/** @addtogroup system_info + * @{ + */ +/** + * Initialize the System Info Struct with default values according to the error + * found during the reading + * @param i2cError 1 if there was an I2C error while reading the System Info + * data from memory, other value if another error occurred + * @return OK if success or an error code which specify the type of error + */ +int defaultSysInfo(int i2cError) +{ + int i; + + logError(0, "%s Setting default System Info...\n", tag); + + if (i2cError == 1) { + systemInfo.u16_fwVer = 0xFFFF; + systemInfo.u16_cfgProjectId = 0xFFFF; + for (i = 0; i < RELEASE_INFO_SIZE; i++) + systemInfo.u8_releaseInfo[i] = 0xFF; + systemInfo.u16_cxVer = 0xFFFF; + } else { + systemInfo.u16_fwVer = 0x0000; + systemInfo.u16_cfgProjectId = 0x0000; + for (i = 0; i < RELEASE_INFO_SIZE; i++) + systemInfo.u8_releaseInfo[i] = 0x00; + systemInfo.u16_cxVer = 0x0000; + } + + systemInfo.u8_scrRxLen = 0; + systemInfo.u8_scrTxLen = 0; + + logError(0, "%s default System Info DONE!\n", tag); + return OK; +} + +/** + * Read the System Info data from memory. System Info is loaded automatically + * after every system reset. + * @param request if 1, will be asked to the FW to reload the data, otherwise + * attempt to read it directly from memory + * @return OK if success or an error code which specify the type of error + */ +int readSysInfo(int request) +{ + int ret, i, index = 0; + u8 sett = LOAD_SYS_INFO; + u8 data[SYS_INFO_SIZE] = { 0 }; + char temp[256] = { 0 }; + + if (request == 1) { + logError(0, "%s %s: Requesting System Info...\n", tag, + __func__); + + ret = writeSysCmd(SYS_CMD_LOAD_DATA, &sett, 1); + if (ret < OK) { + logError(1, + "%s %s: error while writing the sys cmd ERROR %08X\n", + tag, __func__, ret); + goto FAIL; + } + } + + logError(0, "%s %s: Reading System Info...\n", tag, __func__); + ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, + ADDR_FRAMEBUFFER, data, SYS_INFO_SIZE, + DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(1, + "%s %s: error while reading the system data ERROR %08X\n", + tag, + __func__, ret); + goto FAIL; + } + + logError(0, "%s %s: Parsing System Info...\n", tag, __func__); + + if (data[0] != HEADER_SIGNATURE) { + logError(1, + "%s %s: The Header Signature is wrong! sign: %02X != %02X ERROR %08X\n", + tag, __func__, data[0], HEADER_SIGNATURE, + ERROR_WRONG_DATA_SIGN); + ret = ERROR_WRONG_DATA_SIGN; + goto FAIL; + } + + + if (data[1] != LOAD_SYS_INFO) { + logError(1, + "%s %s: The Data ID is wrong! ids: %02X != %02X ERROR %08X\n", + tag, __func__, data[3], LOAD_SYS_INFO, + ERROR_DIFF_DATA_TYPE); + ret = ERROR_DIFF_DATA_TYPE; + goto FAIL; + } + + index += 4; + u8ToU16(&data[index], &systemInfo.u16_apiVer_rev); + index += 2; + systemInfo.u8_apiVer_minor = data[index++]; + systemInfo.u8_apiVer_major = data[index++]; + u8ToU16(&data[index], &systemInfo.u16_chip0Ver); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_chip0Id); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_chip1Ver); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_chip1Id); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_fwVer); + index += 2; + logError(1, "%s FW VER = %04X\n", tag, systemInfo.u16_fwVer); + u8ToU16(&data[index], &systemInfo.u16_svnRev); + index += 2; + logError(1, "%s SVN REV = %04X\n", tag, systemInfo.u16_svnRev); + u8ToU16(&data[index], &systemInfo.u16_cfgVer); + index += 2; + logError(1, "%s CONFIG VER = %04X\n", tag, systemInfo.u16_cfgVer); + u8ToU16(&data[index], &systemInfo.u16_cfgProjectId); + index += 2; + logError(1, "%s CONFIG PROJECT ID = %04X\n", tag, + systemInfo.u16_cfgProjectId); + u8ToU16(&data[index], &systemInfo.u16_cxVer); + index += 2; + logError(1, "%s CX VER = %04X\n", tag, systemInfo.u16_cxVer); + u8ToU16(&data[index], &systemInfo.u16_cxProjectId); + index += 2; + logError(1, "%s CX PROJECT ID = %04X\n", tag, + systemInfo.u16_cxProjectId); + systemInfo.u8_cfgAfeVer = data[index++]; + systemInfo.u8_cxAfeVer = data[index++]; + systemInfo.u8_panelCfgAfeVer = data[index++]; + logError(1, "%s AFE VER: CFG = %02X - CX = %02X - PANEL = %02X\n", tag, + systemInfo.u8_cfgAfeVer, systemInfo.u8_cxAfeVer, + systemInfo.u8_panelCfgAfeVer); + systemInfo.u8_protocol = data[index++]; + logError(1, "%s Protocol = %02X\n", tag, systemInfo.u8_protocol); + /* index+= 1; + * reserved area */ + + /* logError(1, "%s Die Info = ", tag); */ + for (i = 0; i < DIE_INFO_SIZE; i++) + systemInfo.u8_dieInfo[i] = data[index++]; + /* logError(1, "%02X ", systemInfo.u8_dieInfo[i]); */ + /* logError(1, "\n"); */ + logError(1, "%s %s\n", tag, printHex("Die Info = ", + systemInfo.u8_dieInfo, + DIE_INFO_SIZE, temp)); + memset(temp, 0, 256); + + + /* logError(1, "%s Release Info = ", tag); */ + for (i = 0; i < RELEASE_INFO_SIZE; i++) + systemInfo.u8_releaseInfo[i] = data[index++]; + /* logError(1, "%02X ", systemInfo.u8_releaseInfo[i]); */ + /* logError(1, "\n"); */ + + logError(1, "%s %s\n", tag, printHex("Release Info = ", + systemInfo.u8_releaseInfo, + RELEASE_INFO_SIZE, temp)); + memset(temp, 0, 256); + + u8ToU32(&data[index], &systemInfo.u32_fwCrc); + index += 4; + u8ToU32(&data[index], &systemInfo.u32_cfgCrc); + index += 4; + + index += 4; /* skip reserved area */ + + systemInfo.u8_mpFlag = data[index++]; + logError(1, "%s MP FLAG = %02X\n", tag, + systemInfo.u8_mpFlag); + + index += 3 + 4; /* +3 remaining from mp flag address */ + + systemInfo.u8_ssDetScanSet = data[index]; + logError(1, "%s SS Detect Scan Select = %d \n", tag, + systemInfo.u8_ssDetScanSet); + index += 4; + + u8ToU16(&data[index], &systemInfo.u16_scrResX); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_scrResY); + index += 2; + logError(1, "%s Screen Resolution = %d x %d\n", tag, + systemInfo.u16_scrResX, systemInfo.u16_scrResY); + systemInfo.u8_scrTxLen = data[index++]; + logError(1, "%s TX Len = %d\n", tag, systemInfo.u8_scrTxLen); + systemInfo.u8_scrRxLen = data[index++]; + logError(1, "%s RX Len = %d\n", tag, systemInfo.u8_scrRxLen); + systemInfo.u8_keyLen = data[index++]; + logError(1, "%s Key Len = %d\n", tag, systemInfo.u8_keyLen); + systemInfo.u8_forceLen = data[index++]; + logError(1, "%s Force Len = %d\n", tag, systemInfo.u8_forceLen); + index += 8; + + u8ToU32(&data[index], &systemInfo.u32_productionTimestamp); + logError(1, "%s Production Timestamp = %08X\n", + tag, systemInfo.u32_productionTimestamp); + + index += 32; /* skip reserved area */ + + u8ToU16(&data[index], &systemInfo.u16_dbgInfoAddr); + index += 2; + + index += 6; /* skip reserved area */ + + u8ToU16(&data[index], &systemInfo.u16_msTchRawAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_msTchFilterAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_msTchStrenAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_msTchBaselineAddr); + index += 2; + + u8ToU16(&data[index], &systemInfo.u16_ssTchTxRawAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssTchTxFilterAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssTchTxStrenAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssTchTxBaselineAddr); + index += 2; + + u8ToU16(&data[index], &systemInfo.u16_ssTchRxRawAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssTchRxFilterAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssTchRxStrenAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssTchRxBaselineAddr); + index += 2; + + u8ToU16(&data[index], &systemInfo.u16_keyRawAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_keyFilterAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_keyStrenAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_keyBaselineAddr); + index += 2; + + u8ToU16(&data[index], &systemInfo.u16_frcRawAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_frcFilterAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_frcStrenAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_frcBaselineAddr); + index += 2; + + u8ToU16(&data[index], &systemInfo.u16_ssHvrTxRawAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssHvrTxFilterAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssHvrTxStrenAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssHvrTxBaselineAddr); + index += 2; + + u8ToU16(&data[index], &systemInfo.u16_ssHvrRxRawAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssHvrRxFilterAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssHvrRxStrenAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssHvrRxBaselineAddr); + index += 2; + + u8ToU16(&data[index], &systemInfo.u16_ssPrxTxRawAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssPrxTxFilterAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssPrxTxStrenAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssPrxTxBaselineAddr); + index += 2; + + u8ToU16(&data[index], &systemInfo.u16_ssPrxRxRawAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssPrxRxFilterAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssPrxRxStrenAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssPrxRxBaselineAddr); + index += 2; + + u8ToU16(&data[index], &systemInfo.u16_ssDetRawAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssDetFilterAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssDetStrenAddr); + index += 2; + u8ToU16(&data[index], &systemInfo.u16_ssDetBaselineAddr); + index += 2; + + logError(1, "%s Parsed %d bytes!\n", tag, index); + + + if (index != SYS_INFO_SIZE) { + logError(1, "%s %s: index = %d different from %d ERROR %08X\n", + tag, __func__, index, SYS_INFO_SIZE, + ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + logError(1, "%s System Info Read DONE!\n", tag); + return OK; + +FAIL: + defaultSysInfo(isI2cError(ret)); + return ret; +} +/** @}*/ + + +/** + * Read data from the Config Memory + * @param offset Starting address in the Config Memory of data to read + * @param outBuf pointer of a byte array which contain the bytes to read + * @param len number of bytes to read + * @return OK if success or an error code which specify the type of error + */ +int readConfig(u16 offset, u8 *outBuf, int len) +{ + int ret; + u64 final_address = offset + ADDR_CONFIG_OFFSET; + + logError(0, "%s %s: Starting to read config memory at %08llX ...\n", + tag, __func__, final_address); + ret = fts_writeReadU8UX(FTS_CMD_CONFIG_R, BITS_16, final_address, + outBuf, len, DUMMY_CONFIG); + if (ret < OK) { + logError(1, + "%s %s: Impossible to read Config Memory... ERROR %08X!\n", + tag, + __func__, ret); + return ret; + } + + logError(0, "%s %s: Read config memory FINISHED!\n", tag, __func__); + return OK; +} + +/** + * Write data into the Config Memory + * @param offset Starting address in the Config Memory where write the data + * @param data pointer of a byte array which contain the data to write + * @param len number of bytes to write + * @return OK if success or an error code which specify the type of error + */ +int writeConfig(u16 offset, u8 *data, int len) +{ + int ret; + u64 final_address = offset + ADDR_CONFIG_OFFSET; + + logError(0, "%s %s: Starting to write config memory at %08llX ...\n", + tag, __func__, final_address); + ret = fts_writeU8UX(FTS_CMD_CONFIG_W, BITS_16, final_address, data, + len); + if (ret < OK) { + logError(1, + "%s %s: Impossible to write Config Memory... ERROR %08X!\n", + tag, __func__, ret); + return ret; + } + + logError(0, "%s %s: Write config memory FINISHED!\n", tag, __func__); + return OK; +} + +/** + * Disable the interrupt so the ISR of the driver can not be called + * @return OK if success or an error code which specify the type of error + */ +int fts_disableInterrupt(void) +{ + if (getClient() != NULL) { + mutex_lock(&fts_int_mlock); + if (disable_irq_count == 0) { + logError(0, "%s Executing Disable...\n", tag); + + disable_irq(getClient()->irq); + + disable_irq_count++; + } + /* disable_irq is re-entrant so it is required to keep track + * of the number of calls of this when reenabling */ + mutex_unlock(&fts_int_mlock); + return OK; + } else { + logError(1, "%s %s: Impossible get client irq... ERROR %08X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } +} + + +/** + * Disable the interrupt async so the ISR of the driver can not be called + * @return OK if success or an error code which specify the type of error + */ +int fts_disableInterruptNoSync(void) +{ + if (getClient() != NULL) { + mutex_lock(&fts_int_mlock); + if (disable_irq_count == 0) { + logError(0, "%s Executing DisableNoSync...\n", tag); + disable_irq_nosync(getClient()->irq); + disable_irq_count++; + } + /* disable_irq is re-entrant so it is required to keep track + * of the number of calls of this when reenabling */ + mutex_unlock(&fts_int_mlock); + return OK; + } else { + logError(1, "%s %s: Impossible get client irq... ERROR %08X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } +} + + +/** + * Reset the disable_irq count + * @return OK + */ +int fts_resetDisableIrqCount(void) +{ + disable_irq_count = 0; + return OK; +} + +int fts_is_InterruptEnabled(void) +{ + return !disable_irq_count ? 1 : 0; +} + +/** + * Enable the interrupt so the ISR of the driver can be called + * @return OK if success or an error code which specify the type of error + */ +int fts_enableInterrupt(void) +{ + if (getClient() != NULL) { + mutex_lock(&fts_int_mlock); + while (disable_irq_count > 0) { + /* loop N times according on the pending number of + * disable_irq to truly re-enable the int */ + + logError(0, "%s Executing Enable...\n", tag); + enable_irq(getClient()->irq); + + disable_irq_count--; + } + mutex_unlock(&fts_int_mlock); + return OK; + } else { + logError(1, "%s %s: Impossible get client irq... ERROR %08X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } +} + +/** + * Check if there is a crc error in the IC which prevent the fw to run. + * @return OK if no CRC error, or a number >OK according the CRC error + * found + */ +int fts_crc_check(void) +{ + u8 val; + u8 crc_status; + int res; + u8 error_to_search[6] = { EVT_TYPE_ERROR_CRC_CFG_HEAD, + EVT_TYPE_ERROR_CRC_CFG, + EVT_TYPE_ERROR_CRC_CX, + EVT_TYPE_ERROR_CRC_CX_HEAD, + EVT_TYPE_ERROR_CRC_CX_SUB, + EVT_TYPE_ERROR_CRC_CX_SUB_HEAD }; + + + res = fts_writeReadU8UX(FTS_CMD_HW_REG_R, ADDR_SIZE_HW_REG, ADDR_CRC, + &val, 1, DUMMY_HW_REG); + /* read 2 bytes because the first one is a dummy byte! */ + if (res < OK) { + logError(1, "%s %s Cannot read crc status ERROR %08X\n", tag, + __func__, res); + return res; + } + + crc_status = val & CRC_MASK; + if (crc_status != OK) { /* CRC error if crc_status!=0 */ + logError(1, "%s %s CRC ERROR = %02X\n", tag, __func__, + crc_status); + return CRC_CODE; + } + + logError(1, "%s %s: Verifying if Config CRC Error...\n", tag, __func__); + res = fts_system_reset(); + if (res >= OK) { + res = pollForErrorType(error_to_search, 2); + if (res < OK) { + logError(1, "%s %s: No Config CRC Error Found!\n", tag, + __func__); + logError(1, "%s %s: Verifying if Cx CRC Error...\n", + tag, __func__); + res = pollForErrorType(&error_to_search[2], 4); + if (res < OK) { + logError(1, "%s %s: No Cx CRC Error Found!\n", + tag, __func__); + return OK; + } else { + logError(1, + "%s %s: Cx CRC Error found! CRC ERROR = %02X\n", + tag, __func__, res); + return CRC_CX; + } + } else { + logError(1, + "%s %s: Config CRC Error found! CRC ERROR = %02X\n", + tag, __func__, res); + return CRC_CONFIG; + } + } else { + logError(1, + "%s %s: Error while executing system reset! ERROR %08X\n", + tag, + __func__, res); + return res; + } + + return OK; +} + +/** + * Request a host data and use the sync method to understand when the FW load + * it + * @param type the type ID of host data to load (@link load_opt Load Host Data + * Option @endlink) + * @return OK if success or an error code which specify the type of error + */ +int requestSyncFrame(u8 type) +{ + u8 request[3] = { FTS_CMD_SYSTEM, SYS_CMD_LOAD_DATA, type }; + u8 readData[DATA_HEADER] = { 0 }; + int ret, retry = 0, retry2 = 0, time_to_count; + int count, new_count; + + logError(0, "%s %s: Starting to get a sync frame...\n", tag, __func__); + + while (retry2 < RETRY_MAX_REQU_DATA) { + logError(0, "%s %s: Reading count...\n", tag, __func__); + + ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, + ADDR_FRAMEBUFFER, readData, DATA_HEADER, + DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(0, + "%s %s: Error while reading count! ERROR %08X\n", + tag, + __func__, ret | ERROR_REQU_DATA); + ret |= ERROR_REQU_DATA; + retry2++; + continue; + } + + if (readData[0] != HEADER_SIGNATURE) + logError(1, + "%s %s: Invalid Signature while reading count! ERROR %08X\n", + tag, __func__, ret | ERROR_REQU_DATA); + /*ret|=ERROR_REQU_DATA; + * retry2++; + * continue;*/ + + count = (readData[3] << 8) | readData[2]; + new_count = count; + logError(0, "%s %s: Base count = %d\n", tag, __func__, count); + + logError(0, "%s %s: Requesting frame %02X attempt = %d\n", + tag, __func__, type, retry2 + 1); + ret = fts_write(request, ARRAY_SIZE(request)); + if (ret >= OK) { + logError(0, "%s %s: Polling for new count...\n", tag, + __func__); + time_to_count = TIMEOUT_REQU_DATA / TIMEOUT_RESOLUTION; + while (count == new_count && retry < time_to_count) { + ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, + BITS_16, + ADDR_FRAMEBUFFER, + readData, + DATA_HEADER, + DUMMY_FRAMEBUFFER); + if ((ret >= OK) && (readData[0] == + HEADER_SIGNATURE) && + (readData[1] == type)) + new_count = ((readData[3] << 8) | + readData[2]); + else + logError(0, + "%s %s: invalid Signature or can not read count... ERROR %08X\n", + tag, __func__, ret); + retry++; + msleep(TIMEOUT_RESOLUTION); + } + + if (count == new_count) { + logError(1, + "%s %s: New count not received! ERROR %08X\n", + tag, __func__, ERROR_TIMEOUT | + ERROR_REQU_DATA); + ret = ERROR_TIMEOUT | ERROR_REQU_DATA; + } else { + logError(0, + "%s %s: New count found! count = %d! Frame ready!\n", + tag, __func__, new_count); + return OK; + } + } + retry2++; + } + logError(1, "%s %s: Request Data failed! ERROR %08X\n", tag, __func__, + ret); + return ret; +} + + +/** + * Save MP flag value into the flash + * @param mpflag Value to write in the MP Flag field + * @return OK if success or an error code which specify the type of error + */ +int saveMpFlag(u8 mpflag) +{ + int ret; + + logError(1, "%s %s: Saving MP Flag = %02X\n", tag, __func__, mpflag); + ret = writeSysCmd(SYS_CMD_MP_FLAG, &mpflag, 1); + if (ret < OK) { + logError(1, "%s %s: Error while writing MP flag on ram... ERROR %08X\n", + tag, __func__, ret); + return ret; + } + + mpflag = SAVE_PANEL_CONF; + ret = writeSysCmd(SYS_CMD_SAVE_FLASH, &mpflag, 1); + if (ret < OK) { + logError(1, "%s %s: Error while saving MP flag on flash... ERROR %08X\n", + tag, __func__, ret); + return ret; + } + + ret = readSysInfo(1); + if (ret < OK) { + logError(1, "%s %s: Error while refreshing SysInfo... ERROR %08X\n", + tag, __func__, ret); + return ret; + } + + logError(1, "%s %s: Saving MP Flag OK!\n", tag, __func__); + return OK; +} diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsCore.h b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsCore.h new file mode 100644 index 000000000000..c489a7b0a8e9 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsCore.h @@ -0,0 +1,224 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Core definitions ** + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsCore.h + * \brief Contains all the definitions and structs of Core functionalities + */ + +#ifndef FTS_CORE_H +#define FTS_CORE_H + +#include "ftsHardware.h" +#include "ftsSoftware.h" +#include "../fts.h" + +/* HW DATA */ +#define GPIO_NOT_DEFINED -1 /* /< value assumed by reset_gpio when + * the reset pin of the IC is not + * connected */ + + +#define ADDR_SIZE_HW_REG BITS_32 /* /< value of AddrSize for Hw register + * in FTI @see AddrSize */ + + +#define DATA_HEADER 4 /* /< size in byte of the header loaded + * with the data in the frambuffer */ + +/** + * Type of CRC errors + */ +typedef enum { + CRC_CODE = 1, /* /< CRC in the code section */ + CRC_CONFIG = 2, /* /< CRC in the config section */ + CRC_CX = 3, /* /< CRC in the cx section */ + CRC_PANEL = 4, /* /< CRC in the panel section */ + CRC_FLASH = 5/* /< No CRC ERR and force flash fw */ +} CRC_Error; + +/* CHIP INFO */ +/** @defgroup system_info System Info + * System Info Data collect the most important informations about hw and fw + * @{ + */ +/* Size in bytes of System Info data */ +#define SYS_INFO_SIZE 216 /* Num bytes of die info */ +#define DIE_INFO_SIZE 16 /* Num bytes of external release + * in config */ +#define EXTERNAL_RELEASE_INFO_SIZE 8 /* Num bytes of release info in + * sys info + * (first bytes are external + *release) */ +#define RELEASE_INFO_SIZE (EXTERNAL_RELEASE_INFO_SIZE) +/** @}*/ + +/* RETRY MECHANISM */ +#define RETRY_MAX_REQU_DATA 2 /* /< Max number of attemps + * performed + * when requesting data */ +#define RETRY_SYSTEM_RESET 3 /* /< Max number of attemps + * performed + * to reset the IC */ + +/** @addtogroup system_info + * @{ + */ + +/** + * Struct which contains fundamental informations about the chip and its + *configuration + */ +struct fts_sys_info { + u16 u16_apiVer_rev; /* /< API revision version */ + u8 u8_apiVer_minor; /* /< API minor version */ + u8 u8_apiVer_major; /* /< API major version */ + u16 u16_chip0Ver; /* /< Dev0 version */ + u16 u16_chip0Id; /* /< Dev0 ID */ + u16 u16_chip1Ver; /* /< Dev1 version */ + u16 u16_chip1Id; /* /< Dev1 ID */ + u16 u16_fwVer; /* /< Fw version */ + u16 u16_svnRev; /* /< SVN Revision */ + u16 u16_cfgVer; /* /< Config Version */ + u16 u16_cfgProjectId; /* /< Config Project ID */ + u16 u16_cxVer; /* /< Cx Version */ + u16 u16_cxProjectId; /* /< Cx Project ID */ + u8 u8_cfgAfeVer; /* /< AFE version in Config */ + u8 u8_cxAfeVer; /* /< AFE version in CX */ + u8 u8_panelCfgAfeVer; /* /< AFE version in PanelMem */ + u8 u8_protocol; /* /< Touch Report Protocol */ + u8 u8_dieInfo[DIE_INFO_SIZE]; /* /< Die information */ + u8 u8_releaseInfo[RELEASE_INFO_SIZE]; /* /< Release information */ + u32 u32_fwCrc; /* /< Crc of FW */ + u32 u32_cfgCrc; /* /< Crc of config */ + u8 u8_mpFlag; /* /< MP Flag */ + u8 u8_ssDetScanSet; /* /< Type of Detect Scan Selected */ + + u16 u16_scrResX; /* /< X resolution on main screen */ + u16 u16_scrResY; /* /< Y resolution on main screen */ + u8 u8_scrTxLen; /* /< Tx length */ + u8 u8_scrRxLen; /* /< Rx length */ + u8 u8_keyLen; /* /< Key Len */ + u8 u8_forceLen; /* /< Force Len */ + u32 u32_productionTimestamp; /* /< Production Timestamp */ + + u16 u16_dbgInfoAddr; /* /< Offset of debug Info structure */ + + u16 u16_msTchRawAddr; /* /< Offset of MS touch raw frame */ + u16 u16_msTchFilterAddr; /* /< Offset of MS touch filter frame */ + u16 u16_msTchStrenAddr; /* /< Offset of MS touch strength frame */ + u16 u16_msTchBaselineAddr; /* /< Offset of MS touch baseline frame + * */ + + u16 u16_ssTchTxRawAddr; /* /< Offset of SS touch force raw frame */ + u16 u16_ssTchTxFilterAddr; /* /< Offset of SS touch force filter + * frame */ + u16 u16_ssTchTxStrenAddr; /* /< Offset of SS touch force strength + * frame */ + u16 u16_ssTchTxBaselineAddr; /* /< Offset of SS touch force baseline + * frame */ + + u16 u16_ssTchRxRawAddr; /* /< Offset of SS touch sense raw frame */ + u16 u16_ssTchRxFilterAddr; /* /< Offset of SS touch sense filter + * frame */ + u16 u16_ssTchRxStrenAddr; /* /< Offset of SS touch sense strength + * frame */ + u16 u16_ssTchRxBaselineAddr; /* /< Offset of SS touch sense baseline + * frame */ + + u16 u16_keyRawAddr; /* /< Offset of key raw frame */ + u16 u16_keyFilterAddr; /* /< Offset of key filter frame */ + u16 u16_keyStrenAddr; /* /< Offset of key strength frame */ + u16 u16_keyBaselineAddr; /* /< Offset of key baseline frame */ + + u16 u16_frcRawAddr; /* /< Offset of force touch raw frame */ + u16 u16_frcFilterAddr; /* /< Offset of force touch filter frame */ + u16 u16_frcStrenAddr; /* /< Offset of force touch strength frame */ + u16 u16_frcBaselineAddr; /* /< Offset of force touch baseline + * frame */ + + u16 u16_ssHvrTxRawAddr; /* /< Offset of SS hover Force raw frame */ + u16 u16_ssHvrTxFilterAddr; /* /< Offset of SS hover Force filter + * frame */ + u16 u16_ssHvrTxStrenAddr; /* /< Offset of SS hover Force strength + * frame */ + u16 u16_ssHvrTxBaselineAddr; /* /< Offset of SS hover Force baseline + * frame */ + + u16 u16_ssHvrRxRawAddr; /* /< Offset of SS hover Sense raw frame */ + u16 u16_ssHvrRxFilterAddr; /* /< Offset of SS hover Sense filter + * frame */ + u16 u16_ssHvrRxStrenAddr; /* /< Offset of SS hover Sense strength + * frame */ + u16 u16_ssHvrRxBaselineAddr; /* /< Offset of SS hover Sense baseline + * frame */ + + u16 u16_ssPrxTxRawAddr; /* /< Offset of SS proximity force raw frame */ + u16 u16_ssPrxTxFilterAddr; /* /< Offset of SS proximity force + * filter frame */ + u16 u16_ssPrxTxStrenAddr; /* /< Offset of SS proximity force + * strength frame */ + u16 u16_ssPrxTxBaselineAddr; /* /< Offset of SS proximity force + * baseline frame */ + + u16 u16_ssPrxRxRawAddr; /* /< Offset of SS proximity sense raw frame */ + u16 u16_ssPrxRxFilterAddr; /* /< Offset of SS proximity sense + * filter frame */ + u16 u16_ssPrxRxStrenAddr; /* /< Offset of SS proximity sense + * strength frame */ + u16 u16_ssPrxRxBaselineAddr; /* /< Offset of SS proximity sense + * baseline frame */ + + u16 u16_ssDetRawAddr; /* /< Offset of SS detect raw frame */ + u16 u16_ssDetFilterAddr; /* /< Offset of SS detect filter + * frame */ + u16 u16_ssDetStrenAddr; /* /< Offset of SS detect strength + * frame */ + u16 u16_ssDetBaselineAddr; /* /< Offset of SS detect baseline + * frame */ +}; + +typedef struct fts_sys_info SysInfo; + +/** @}*/ + +int initCore(struct fts_ts_info *info); +void setResetGpio(int gpio); +int fts_system_reset(void); +int isSystemResettedUp(void); +int isSystemResettedDown(void); +void setSystemResetedUp(int val); +void setSystemResetedDown(int val); +int pollForEvent(int *event_to_search, int event_bytes, u8 *readData, int + time_to_wait); +int checkEcho(u8 *cmd, int size); +int setScanMode(u8 mode, u8 settings); +int setFeatures(u8 feat, u8 *settings, int size); +int defaultSysInfo(int i2cError); +int writeSysCmd(u8 sys_cmd, u8 *sett, int size); +int readSysInfo(int request); +int readConfig(u16 offset, u8 *outBuf, int len); +int writeConfig(u16 offset, u8 *data, int len); +int fts_disableInterrupt(void); +int fts_disableInterruptNoSync(void); +int fts_resetDisableIrqCount(void); +int fts_enableInterrupt(void); +int fts_is_InterruptEnabled(void); +int fts_crc_check(void); +int fts_wait_for_ready(void); +int requestSyncFrame(u8 type); +int saveMpFlag(u8 mpflag); + +#endif /* FTS_CORE_H */ diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsError.c b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsError.c new file mode 100644 index 000000000000..4db6d8091333 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsError.c @@ -0,0 +1,375 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS error/info kernel log reporting * + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsError.c + * \brief Contains all the function which handle with Error conditions + */ + +#include +#include +#include +#include +#include + + +#include "../fts.h" +#include "ftsCore.h" +#include "ftsError.h" +#include "ftsIO.h" +#include "ftsTool.h" +#include "ftsCompensation.h" + + +static ErrorList errors; /* /< private variable which implement the Error + * List */ + +/** + * Print messages in the kernel log + * @param force if 1, the log is printed always otherwise only if DEBUG is + * defined, the log will be printed + * @param msg string containing the message to print + * @param ... additional parameters that are used in msg according the format + * of printf + */ +void logError(int force, const char *msg, ...) +{ + if (force == 1 +#ifdef DEBUG + || 1 +#endif + ) { + va_list args; + va_start(args, msg); + vprintk(msg, args); + va_end(args); + } +} + +/** + * Check if an error code is related to an I2C failure + * @param error error code to check + * @return 1 if the first level error code is I2C related otherwise 0 + */ +int isI2cError(int error) +{ + if (((error & 0x000000FF) >= (ERROR_BUS_R & 0x000000FF)) && + ((error & 0x000000FF) <= (ERROR_BUS_O & 0x000000FF))) + return 1; + else + return 0; +} + + +/** + * Dump in the kernel log some debug info in case of FW hang + * @param outBuf (optional)pointer to bytes array where to copy the debug info, + * if NULL the data will just printed on the kernel log + * @param size dimension in bytes of outBuf, + * if > ERROR_DUMP_ROW_SIZE*ERROR_DUMP_COL_SIZE, only the first + * ERROR_DUMP_ROW_SIZE*ERROR_DUMP_COL_SIZE bytes will be copied + * @return OK if success or an error code which specify the type of error + */ +int dumpErrorInfo(u8 *outBuf, int size) +{ + int ret, i; + u8 data[ERROR_DUMP_ROW_SIZE * ERROR_DUMP_COL_SIZE] = { 0 }; + u32 sign = 0; + + logError(0, "%s %s: Starting dump of error info...\n", tag, __func__); + + ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, ADDR_ERROR_DUMP, + data, ERROR_DUMP_ROW_SIZE * ERROR_DUMP_COL_SIZE, + DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(1, "%s %s: reading data ERROR %08X\n", tag, __func__, + ret); + return ret; + } else { + if (outBuf != NULL) { + sign = size > ERROR_DUMP_ROW_SIZE * + ERROR_DUMP_COL_SIZE ? ERROR_DUMP_ROW_SIZE * + ERROR_DUMP_COL_SIZE : size; + memcpy(outBuf, data, sign); + logError(0, "%s %s: error info copied in the buffer!\n", + tag, __func__); + } + logError(1, "%s %s: Error Info =\n", tag, __func__); + u8ToU32(data, &sign); + if (sign != ERROR_DUMP_SIGNATURE) + logError(1, + "%s %s: Wrong Error Signature! Data may be invalid!\n", + tag, __func__); + else + logError(1, + "%s %s: Error Signature OK! Data are valid!\n", + tag, + __func__); + + for (i = 0; i < ERROR_DUMP_ROW_SIZE * ERROR_DUMP_COL_SIZE; + i++) { + if (i % ERROR_DUMP_COL_SIZE == 0) + logError(1, KERN_ERR "\n%s %s: %d) ", tag, + __func__, i / ERROR_DUMP_COL_SIZE); + logError(1, "%02X ", data[i]); + } + logError(1, "\n"); + + logError(0, "%s %s: dump of error info FINISHED!\n", tag, + __func__); + return OK; + } +} + + +/** + * Implement recovery strategies to be used when an error event is found + * while polling the FIFO + * @param event error event found during the polling + * @param size size of event + * @return OK if the error event doesn't require any action or the recovery + * strategy doesn't have any impact in the possible procedure that trigger the + * error, otherwise return an error code which specify the kind of error + * encountered. If ERROR_HANDLER_STOP_PROC the calling function must stop! + */ +int errorHandler(u8 *event, int size) +{ + int res = OK; + struct fts_ts_info *info = NULL; + + if (getDev() != NULL) + info = dev_get_drvdata(getDev()); + + if (info != NULL && event != NULL && size > 1 && event[0] == + EVT_ID_ERROR) { + logError(1, "%s errorHandler: Starting handling...\n", tag); + addErrorIntoList(event, size); + switch (event[1]) { /* TODO: write an error log for + * undefined command subtype 0xBA */ + case EVT_TYPE_ERROR_ESD: /* esd */ + res = fts_chip_powercycle(info); + if (res < OK) + logError(1, + "%s errorHandler: Error performing powercycle ERROR %08X\n", + tag, res); + + res = fts_system_reset(); + if (res < OK) + logError(1, + "%s errorHandler: Cannot reset the device ERROR %08X\n", + tag, res); + res = (ERROR_HANDLER_STOP_PROC | res); + break; + + case EVT_TYPE_ERROR_WATCHDOG: /* watchdog */ + dumpErrorInfo(NULL, 0); + res = fts_system_reset(); + if (res < OK) + logError(1, + "%s errorHandler: Cannot reset the device ERROR %08X\n", + tag, res); + res = (ERROR_HANDLER_STOP_PROC | res); + break; + + case EVT_TYPE_ERROR_ITO_FORCETOGND: + logError(1, "%s errorHandler: Force Short to GND!\n", + tag); + break; + case EVT_TYPE_ERROR_ITO_SENSETOGND: + logError(1, "%s errorHandler: Sense short to GND!\n", + tag); + break; + case EVT_TYPE_ERROR_ITO_FORCETOVDD: + logError(1, "%s errorHandler: Force short to VDD!\n", + tag); + break; + case EVT_TYPE_ERROR_ITO_SENSETOVDD: + logError(1, "%s errorHandler: Sense short to VDD!\n", + tag); + break; + case EVT_TYPE_ERROR_ITO_FORCE_P2P: + logError(1, + "%s errorHandler: Force Pin to Pin Short!\n", + tag); + break; + case EVT_TYPE_ERROR_ITO_SENSE_P2P: + logError(1, + "%s errorHandler: Sense Pin to Pin Short!\n", + tag); + break; + case EVT_TYPE_ERROR_ITO_FORCEOPEN: + logError(1, "%s errorHandler: Force Open !\n", tag); + break; + case EVT_TYPE_ERROR_ITO_SENSEOPEN: + logError(1, "%s errorHandler: Sense Open !\n", tag); + break; + case EVT_TYPE_ERROR_ITO_KEYOPEN: + logError(1, "%s errorHandler: Key Open !\n", tag); + break; + case EVT_TYPE_ERROR_CAL_NOISE: + logError(1, "%s errorHandler: Calibration noise check error !\n", tag); + res = (ERROR_HANDLER_STOP_PROC | res); + break; + + default: + logError(1, "%s errorHandler: No Action taken!\n", tag); + break; + } + logError(1, "%s errorHandler: handling Finished! res = %08X\n", + tag, res); + return res; + } else { + logError(1, + "%s errorHandler: event Null or not correct size! ERROR %08X\n", + tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } +} + + +/** + * Add an error event into the Error List + * @param event error event to add + * @param size size of event + * @return OK + */ +int addErrorIntoList(u8 *event, int size) +{ + int i = 0; + + logError(0, "%s Adding error in to ErrorList...\n", tag); + + memcpy(&errors.list[errors.last_index * FIFO_EVENT_SIZE], event, size); + i = FIFO_EVENT_SIZE - size; + if (i > 0) { + logError(0, + "%s Filling last %d bytes of the event with zero...\n", + tag, i); + memset(&errors.list[errors.last_index * FIFO_EVENT_SIZE + size], + 0, i); + } + logError(0, "%s Adding error in to ErrorList... FINISHED!\n", tag); + + errors.count += 1; + if (errors.count > FIFO_DEPTH) + logError(1, + "%s ErrorList is going in overflow... the first %d event(s) were override!\n", + tag, errors.count - FIFO_DEPTH); + errors.last_index = (errors.last_index + 1) % FIFO_DEPTH; + + return OK; +} + +/** + * Reset the Error List setting the count and last_index to 0. + * @return OK + */ +int resetErrorList(void) +{ + errors.count = 0; + errors.last_index = 0; + memset(errors.list, 0, FIFO_DEPTH * FIFO_EVENT_SIZE); + /* if count is not considered is better reset also the list in order to + * avoid to read data previously copied into the list */ + return OK; +} + +/** + * Get the number of error events copied into the Error List + * @return the number of error events into the Error List + */ +int getErrorListCount(void) +{ + if (errors.count > FIFO_DEPTH) + return FIFO_DEPTH; + else + return errors.count; +} + +/* in case of success return the index of the event found */ +/** + * Scroll the Error List looking for the event specified + * @param event_to_search event_to_search pointer to an array of int where + * each element correspond to a byte of the event to find. If the element + * of the array has value -1, the byte of the event, in the same position + * of the element is ignored. + * @param event_bytes size of event_to_search + * @return a value >=0 if the event is found which represent the index of + * the Error List where the event is located otherwise an error code + */ +int pollErrorList(int *event_to_search, int event_bytes) +{ + int i = 0, j = 0, find = 0; + int count = getErrorListCount(); + + logError(1, "%s Starting to poll ErrorList...\n", tag); + while (find != 1 && i < count) { + find = 1; + for (j = 0; j < event_bytes; j++) { + if ((event_to_search[i] != -1) && + ((int)errors.list[i * FIFO_EVENT_SIZE + j] != + event_to_search[i])) { + find = 0; + break; + } + } + i++; + } + if (find == 1) { + logError(1, "%s Error Found into ErrorList!\n", tag); + return i - 1; /* there is i++ at the end of the while */ + } else { + logError(1, "%s Error Not Found into ErrorList! ERROR %08X\n", + tag, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } +} + + + +/** + * Poll the Error List looking for any error types passed in the arguments. + * Return at the first match! + * @param list pointer to a list of error types to look for + * @param size size of list + * @return error type found if success or ERROR_TIMEOUT + */ +int pollForErrorType(u8 *list, int size) +{ + int i = 0, j = 0, find = 0; + int count = getErrorListCount(); + + logError(1, "%s %s: Starting to poll ErrorList... count = %d\n", tag, + __func__, count); + while (find != 1 && i < count) { + for (j = 0; j < size; j++) { + if (list[j] == errors.list[i * FIFO_EVENT_SIZE + 1]) { + find = 1; + break; + } + } + i++; + } + if (find == 1) { + logError(1, "%s %s: Error Type %02X into ErrorList!\n", tag, + __func__, list[j]); + return list[j]; + } else { + logError(1, + "%s %s: Error Type Not Found into ErrorList! ERROR %08X\n", + tag, __func__, ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } +} diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsError.h b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsError.h new file mode 100644 index 000000000000..3d44ee12d812 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsError.h @@ -0,0 +1,236 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS error/info kernel log reporting * + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsError.h + * \brief Contains all the definitions and structs which refer to Error + * conditions + */ + +#ifndef FTS_ERROR_H +#define FTS_ERROR_H + +#include "ftsHardware.h" +#include "ftsSoftware.h" + + +/** @defgroup error_codes Error Codes + * Error codes that can be reported by the driver functions. + * An error code is made up by 4 bytes, each byte indicate a logic error + * level.\n + * From the LSB to the MSB, the logic level increase going from a low level + * error (I2C,TIMEOUT) to an high level error (flashing procedure fail, + * production test fail etc) + * @{ + */ + +/* FIRST LEVEL ERROR CODE */ +/** @defgroup first_level First Level Error Code + * @ingroup error_codes + * Errors related to low level operation which are not under control of driver, + * such as: communication protocol (I2C/SPI), timeout, file operations ... + * @{ + */ +#define OK ((int)0x00000000) /* /< No ERROR */ +#define ERROR_ALLOC ((int)0x80000001) /* /< allocation of + * memory failed */ +#define ERROR_BUS_R ((int)0x80000002) /* /< i2c/spi read + * failed */ +#define ERROR_BUS_W ((int)0x80000003) /* /< i2c/spi write + * failed */ +#define ERROR_BUS_WR ((int)0x80000004) /* /< i2c/spi write/read + * failed */ +#define ERROR_BUS_O ((int)0x80000005) /* /< error during + * opening an i2c device + */ +#define ERROR_OP_NOT_ALLOW ((int)0x80000006) /* /< operation not + * allowed */ +#define ERROR_TIMEOUT ((int)0x80000007) /* /< timeout expired! + * exceed the max number + * of retries or the max + * waiting time */ +#define ERROR_FILE_NOT_FOUND ((int)0x80000008) /* /< the file that i + * want to open is not + * found */ +#define ERROR_FILE_PARSE ((int)0x80000009) /* /< error during + * parsing the file */ +#define ERROR_FILE_READ ((int)0x8000000A) /* /< error during + * reading the file */ +#define ERROR_LABEL_NOT_FOUND ((int)0x8000000B) /* /< label not found */ +#define ERROR_FW_NO_UPDATE ((int)0x8000000C) /* /< fw in the chip + * newer than the one in + * the memmh */ +#define ERROR_FLASH_UNKNOWN ((int)0x8000000D) /* /< flash status busy + * or unknown */ +/** @}*/ + +/* SECOND LEVEL ERROR CODE */ +/** @defgroup second_level Second Level Error Code + * @ingroup error_codes + * Errors related to simple logic operations in the IC which require one + * command or which are part of a more complex procedure + * @{ + */ +#define ERROR_DISABLE_INTER ((int)0x80000200) /* /< unable to disable + * the interrupt */ +#define ERROR_ENABLE_INTER ((int)0x80000300) /* /< unable to activate + * the interrup */ +#define ERROR_READ_CONFIG ((int)0x80000400) /* /< failed to read + * config memory */ +#define ERROR_GET_OFFSET ((int)0x80000500) /* /< unable to read an + * offset from memory */ +#define ERROR_GET_FRAME_DATA ((int)0x80000600) /* /< unable to + * retrieve the data of + * a required frame */ +#define ERROR_DIFF_DATA_TYPE ((int)0x80000700) /* /< FW answers with + * an event that has a + * different address + * respect the request + * done */ +#define ERROR_WRONG_DATA_SIGN ((int)0x80000800) /* /< the signature of + * the host data is not + * HEADER_SIGNATURE */ +#define ERROR_SET_SCAN_MODE_FAIL ((int)0x80000900) /* /< setting the + * scanning mode failed + * (sense on/off etc...) + */ +#define ERROR_SET_FEATURE_FAIL ((int)0x80000A00) /* /< setting a + * specific feature + * failed */ +#define ERROR_SYSTEM_RESET_FAIL ((int)0x80000B00) /* /< the comand + * SYSTEM RESET + * failed */ +#define ERROR_FLASH_NOT_READY ((int)0x80000C00) /* /< flash + * status not + * ready within + * a timeout */ +#define ERROR_FW_VER_READ ((int)0x80000D00) /* /< unable to read + * fw_vers or the + * config_id */ +#define ERROR_GESTURE_ENABLE_FAIL ((int)0x80000E00) /* /< unable to + * enable/disable the + * gesture */ +#define ERROR_GESTURE_START_ADD ((int)0x80000F00) /* /< unable to start + * add custom gesture */ +#define ERROR_GESTURE_FINISH_ADD ((int)0x80001000) /* /< unable to finish + * to add custom gesture + */ +#define ERROR_GESTURE_DATA_ADD ((int)0x80001100) /* /< unable to add + * custom gesture data + * */ +#define ERROR_GESTURE_REMOVE ((int)0x80001200) /* /< unable to remove + * custom gesture data + */ +#define ERROR_FEATURE_ENABLE_DISABLE ((int)0x80001300) /* /< unable to + * enable/disable a + * feature mode in the + * IC */ +#define ERROR_NOISE_PARAMETERS ((int)0x80001400) /* /< unable to + * set/read noise + * parameter in + * the IC */ +#define ERROR_CH_LEN ((int)0x80001500) /* /< unable to read + * the force and/or + * sense length */ +/** @}*/ + +/* THIRD LEVEL ERROR CODE */ +/** @defgroup third_level Third Level Error Code + * @ingroup error_codes + * Errors related to logic operations in the IC which require more + *commands/steps or which are part of a more complex procedure + * @{ + */ +#define ERROR_REQU_COMP_DATA ((int)0x80010000) /* /< compensation data + * request failed */ +#define ERROR_REQU_DATA ((int)0x80020000) /* /< data request failed */ +#define ERROR_COMP_DATA_HEADER ((int)0x80030000) /* /< unable to retrieve + * compensation data header */ +#define ERROR_COMP_DATA_GLOBAL ((int)0x80040000) /* /< unable to retrieve the + * global compensation data */ +#define ERROR_COMP_DATA_NODE ((int)0x80050000) /* /< unable to retrieve + * the compensation data + * for each node */ +#define ERROR_TEST_CHECK_FAIL ((int)0x80060000) /* /< check of + * production limits or + * of fw answers failed */ +#define ERROR_MEMH_READ ((int)0x80070000) /* /< memh reading failed */ +#define ERROR_FLASH_BURN_FAILED ((int)0x80080000) /* /< flash burn failed */ +#define ERROR_MS_TUNING ((int)0x80090000) /* /< ms tuning failed */ +#define ERROR_SS_TUNING ((int)0x800A0000) /* /< ss tuning failed */ +#define ERROR_LP_TIMER_TUNING ((int)0x800B0000) /* /< lp timer calibration + * failed */ +#define ERROR_SAVE_CX_TUNING ((int)0x800C0000) /* /< save cx data to flash + * failed */ +#define ERROR_HANDLER_STOP_PROC ((int)0x800D0000) /* /< stop the poll of + * the FIFO if + * particular errors are + * found */ +#define ERROR_CHECK_ECHO_FAIL ((int)0x800E0000) /* /< unable to retrieve + * echo event */ +#define ERROR_GET_FRAME ((int)0x800F0000) /* /< unable to get frame */ +/** @}*/ + +/* FOURTH LEVEL ERROR CODE */ +/** @defgroup fourth_level Fourth Level Error Code + * @ingroup error_codes + * Errors related to the highest logic operations in the IC which have an + * important impact on the driver flow or which require several commands and + * steps to be executed + * @{ + */ +#define ERROR_PROD_TEST_DATA ((int)0x81000000) /* /< production + * data test failed */ +#define ERROR_FLASH_PROCEDURE ((int)0x82000000) /* /< fw update + * procedure failed */ +#define ERROR_PROD_TEST_ITO ((int)0x83000000) /* /< production + * ito test failed */ +#define ERROR_PROD_TEST_INITIALIZATION ((int)0x84000000) /* /< production + * initialization test + * failed */ +#define ERROR_GET_INIT_STATUS ((int)0x85000000) /* /< mismatch of MS + * or SS tuning_version + * */ +/** @}*/ + +/** @}*/ /* end of error_commands section */ + +/** + * Struct which store an ordered list of the errors events encountered during + *the polling of a FIFO. + * The max number of error events that can be stored is equal to FIFO_DEPTH + */ +typedef struct { + u8 list[FIFO_DEPTH * FIFO_EVENT_SIZE]; /* /< byte array which contains + * the series of error events + * encountered from the last + * reset of the list. */ + int count; /* /< number of error events stored in the list */ + int last_index; /* /< index of the list where will be stored the next + * error event. Subtract -1 to have the index of the + * last error event! */ +} ErrorList; + +/* #define logError(_l, _msg, ...) pr_err(_msg, ##__VA_ARGS__) */ +void logError(int force, const char *msg, ...); +int isI2cError(int error); +int dumpErrorInfo(u8 *outBuf, int size); +int errorHandler(u8 *event, int size); +int addErrorIntoList(u8 *event, int size); +int getErrorListCount(void); +int resetErrorList(void); +int pollErrorList(int *event_to_search, int event_bytes); +int pollForErrorType(u8 *list, int size); +#endif diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsFlash.c b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsFlash.c new file mode 100644 index 000000000000..9aefece1abac --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsFlash.c @@ -0,0 +1,1249 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for Flashing the IC * + * * + ************************************************************************** + ************************************************************************** + * + */ + + +/*! + * \file ftsFlash.c + * \brief Contains all the functions to handle the FW update process + */ + +#include "ftsCore.h" +#include "ftsCompensation.h" +#include "ftsError.h" +#include "ftsFlash.h" +#include "ftsFrame.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTest.h" +#include "ftsTime.h" +#include "ftsTool.h" +#include "../fts.h" /* needed for including the define FW_H_FILE */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fts_mmi.h" + + +#ifdef FW_H_FILE +#include "../fts_fw.h" +#endif + + +extern SysInfo systemInfo; /* /< forward declaration of the global variable + * of containing System Info Data */ +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW +extern int flash_mode; /* /< if flash_mode == 0, use the request_firmware method to upgrade. + * if flash_mode == 1, read the firmware data in the /data/vendor/param/touch/ + * to upgrade. */ +#endif + +/** + * Retrieve the actual FW data from the system (bin file or header file) + * @param pathToFile name of FW file to load or "NULL" if the FW data should be + *loaded by a .h file + * @param data pointer to the pointer which will contains the FW data + * @param size pointer to a variable which will contain the size of the loaded + *data + * @return OK if success or an error code which specify the type of error + */ +int getFWdata(const char *pathToFile, u8 **data, int *size) +{ + const struct firmware *fw = NULL; + struct device *dev = NULL; + int res, from = 0; + char path[MAX_LIMIT_FILE_NAME] = { 0 }; +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW + struct file *filp = NULL; + struct inode *inode; + loff_t pos = 0; + loff_t file_len = 0; +#endif + + logError(1, "%s getFWdata starting ...\n", tag); + if (strncmp(pathToFile, "NULL", 4) == 0) { + from = 1; + snprintf(path, MAX_LIMIT_FILE_NAME, "%s", PATH_FILE_FW); + } +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW + else if (flash_mode == FW_PARAM_MODE) { + from = 2; + snprintf(path, MAX_LIMIT_FILE_NAME, "%s%s", TS_MMI_FW_PARAM_PATH, pathToFile); + } +#endif + else + snprintf(path, MAX_LIMIT_FILE_NAME, "%s", pathToFile); + + /* keep the switch case because if the argument passed is null but + * the option from .h is not set we still try to load from bin */ + + switch (from) { + case 0: + logError(1, "%s Read FW from BIN file %s !\n", tag, path); + dev = getDev(); + + if (dev != NULL) { + res = request_firmware(&fw, path, dev); + if (res == 0) { + *size = fw->size; + *data = (u8 *)kmalloc((*size) * sizeof(u8), + GFP_KERNEL); + if (*data == NULL) { + logError(1, + "%s getFWdata: Impossible to allocate memory! ERROR %08X\n", + tag, ERROR_ALLOC); + release_firmware(fw); + return ERROR_ALLOC; + } + memcpy(*data, (u8 *)fw->data, (*size)); + release_firmware(fw); + } else { + logError(1, + "%s getFWdata: No File found! ERROR %08X\n", + tag, ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; + } + } else { + logError(1, + "%s getFWdata: No device found! ERROR %08X\n", + tag, + ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + break; +#ifdef FW_H_FILE + case 1: + logError(1, "%s Read FW from .h file!\n", tag); + *size = FW_SIZE_NAME; + *data = (u8 *)kmalloc((*size) * sizeof(u8), GFP_KERNEL); + if (*data == NULL) { + logError(1, + "%s getFWdata: Impossible to allocate memory! ERROR %08X\n", + tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + memcpy(*data, (u8 *)FW_ARRAY_NAME, (*size)); + + break; +#endif +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW + case 2: + logError(1, "%s Read FW from parm path %s!\n", tag, path); + filp = filp_open(path, O_RDONLY, 0); + if (IS_ERR(filp)) { + logError(1, "%s open %s file fail", tag, file_path); + return -ENOENT; + } + inode = filp->f_inode; + file_len = inode->i_size; + *data = (u8 *)vmalloc(file_len); + if (NULL == *data) { + logError(1, "%s Failed to malloc param firmware memory! ERROR %08X\n", + tag, ERROR_ALLOC); + filp_close(filp, NULL); + return -ENOMEM; + } + res = kernel_read(filp, *data, file_len, &pos); + if (res < 0) { + logError(1, "%s Failed to read param firmware file!\n", tag); + filp_close(filp, NULL); + return -EIO; + } + *size = res; + logError(1, "%s fw file len:%d pos:%d!\n", tag, (u32)file_len, (u32)pos); + filp_close(filp, NULL); + + break; +#endif + default: + logError(1, "%s Invalid firmware upgrade mode: %d\n", tag, from); + return -1; + } + + logError(1, "%s getFWdata Finished!\n", tag); + return OK; +} + + +/** + * Perform all the steps to read the FW that should be burnt in the IC from + * the system and parse it in order to fill a Firmware struct with the relevant + * info + * @param path name of FW file to load or "NULL" if the FW data should be + *loaded by a .h file + * @param fw pointer to a Firmware variable which will contains the FW data and + *info + * @param keep_cx if 1, the CX area will be loaded otherwise will be skipped + * @return OK if success or an error code which specify the type of error + */ +int readFwFile(const char *path, Firmware *fw, int keep_cx) +{ + int res; + int orig_size; + u8 *orig_data = NULL; + + + res = getFWdata(path, &orig_data, &orig_size); + if (res < OK) { + logError(1, + "%s readFwFile: impossible retrieve FW... ERROR %08X\n", + tag, + ERROR_MEMH_READ); + return res | ERROR_MEMH_READ; + } + res = parseBinFile(orig_data, orig_size, fw, keep_cx); + if (res < OK) { + logError(1, "%s readFwFile: impossible parse ERROR %08X\n", tag, + ERROR_MEMH_READ); + return res | ERROR_MEMH_READ; + } + + return OK; +} + +/** + * Perform all the steps necessary to burn the FW into the IC + * @param path name of FW file to load or "NULL" if the FW data should be + *loaded by a .h file + * @param force if 1, the flashing procedure will be forced and executed + *regardless the additional info, otherwise the FW in the file will be burnt + *only if it is newer than the one running in the IC + * @param keep_cx if 1, the CX area will be loaded and burnt otherwise will be + *skipped and the area will be untouched + * @return OK if success or an error code which specify the type of error + */ +int flashProcedure(const char *path, int force, int keep_cx) +{ + Firmware fw; + int res; + + fw.data = NULL; + logError(0, "%s Reading Fw file...\n", tag); + res = readFwFile(path, &fw, keep_cx); + if (res < OK) { + logError(1, "%s flashProcedure: ERROR %08X\n", tag, (res | + ERROR_FLASH_PROCEDURE)); + kfree(fw.data); + return res | ERROR_FLASH_PROCEDURE; + } + logError(0, "%s Fw file read COMPLETED!\n", tag); + + logError(0, "%s Starting flashing procedure...\n", tag); + res = flash_burn(fw, force, keep_cx); + if (res < OK && res != (ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED)) { + logError(1, "%s flashProcedure: ERROR %08X\n", tag, + ERROR_FLASH_PROCEDURE); + kfree(fw.data); + return res | ERROR_FLASH_PROCEDURE; + } + logError(0, "%s flashing procedure Finished!\n", tag); + + kfree(fw.data); + + return res; +} + +/** + * Poll the Flash Status Registers after the execution of a command to check + * if the Flash becomes ready within a timeout + * @param type register to check according to the previous command sent + * @return OK if success or an error code which specify the type of error + */ +int wait_for_flash_ready(u8 type) +{ + u8 cmd[5] = { FTS_CMD_HW_REG_R, 0x20, 0x00, 0x00, type }; + + u8 readData[2] = { 0 }; + int i, res = -1; + + logError(0, "%s Waiting for flash ready ...\n", tag); + for (i = 0; i < FLASH_RETRY_COUNT && res != 0; i++) { + res = fts_writeRead(cmd, ARRAY_SIZE(cmd), readData, 2); + if (res < OK) + logError(1, "%s wait_for_flash_ready: ERROR %08X\n", + tag, ERROR_BUS_W); + else { +#ifdef I2C_INTERFACE /* in case of spi there is a dummy byte */ + res = readData[0] & 0x80; +#else + res = readData[1] & 0x80; +#endif + + logError(0, "%s flash status = %d\n", tag, res); + } + msleep(FLASH_WAIT_BEFORE_RETRY); + } + + if (i == FLASH_RETRY_COUNT && res != 0) { + logError(1, "%s Wait for flash TIMEOUT! ERROR %08X\n", tag, + ERROR_TIMEOUT); + return ERROR_TIMEOUT; + } + + logError(0, "%s Flash READY!\n", tag); + return OK; +} + + +/** + * Put the M3 in hold + * @return OK if success or an error code which specify the type of error + */ +int hold_m3(void) +{ + int ret; + u8 cmd[1] = { 0x01 }; + + logError(0, "%s Command m3 hold...\n", tag); + ret = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG, + ADDR_SYSTEM_RESET, cmd, 1); + if (ret < OK) { + logError(1, "%s hold_m3: ERROR %08X\n", tag, ret); + return ret; + } + logError(0, "%s Hold M3 DONE!\n", tag); + +#if !defined(I2C_INTERFACE) && defined(SPI4_WIRE) + /* configure manually SPI4 because when no fw is running the chip use + * SPI3 by default */ + logError(0, "%s Setting SPI4 mode...\n", tag); + cmd[0] = 0x10; + ret = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG, + ADDR_GPIO_DIRECTION, cmd, 1); + if (ret < OK) { + logError(1, "%s hold_m3: can not set gpio dir ERROR %08X\n", + tag, ret); + return ret; + } + + cmd[0] = 0x02; + ret = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG, + ADDR_GPIO_PULLUP, cmd, 1); + if (ret < OK) { + logError(1, "%s hold_m3: can not set gpio pull-up ERROR %08X\n", + tag, ret); + return ret; + } + +#if defined(ALIX) || defined (SALIXP) +#if defined(ALIX) + cmd[0] = 0x70; +#else + cmd[0] = 0x07; +#endif + ret = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG, + ADDR_GPIO_CONFIG_REG3, cmd, 1); + if (ret < OK) { + logError(1, "%s hold_m3: can not set gpio config ERROR %08X\n", + tag, ret); + return ret; + } + +#else + cmd[0] = 0x07; + ret = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG, + ADDR_GPIO_CONFIG_REG2, cmd, 1); + if (ret < OK) { + logError(1, "%s hold_m3: can not set gpio config ERROR %08X\n", + tag, ret); + return ret; + } +#endif + + cmd[0] = 0x30; + ret = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG, + ADDR_GPIO_CONFIG_REG0, cmd, 1); + if (ret < OK) { + logError(1, "%s hold_m3: can not set gpio config ERROR %08X\n", + tag, ret); + return ret; + } + + cmd[0] = SPI4_MASK; + ret = fts_writeU8UX(FTS_CMD_HW_REG_W, ADDR_SIZE_HW_REG, ADDR_ICR, cmd, + 1); + if (ret < OK) { + logError(1, "%s hold_m3: can not set spi4 mode ERROR %08X\n", + tag, ret); + return ret; + } + msleep(1); /* wait for the GPIO to stabilize */ +#endif + + return OK; +} + + + +/** + * Parse the raw data read from a FW file in order to fill properly the fields + * of a Firmware variable + * @param fw_data raw FW data loaded from system + * @param fw_size size of fw_data + * @param fwData pointer to a Firmware variable which will contain the + *processed data + * @param keep_cx if 1, the CX area will be loaded and burnt otherwise will be + * skipped and the area will be untouched + * @return OK if success or an error code which specify the type of error + */ +int parseBinFile(u8 *fw_data, int fw_size, Firmware *fwData, int keep_cx) +{ + int dimension, index = 0; + u32 temp; + int res, i; + + /* the file should contain at least the header plus the content_crc */ + if (fw_size < FW_HEADER_SIZE + FW_BYTES_ALLIGN || fw_data == NULL) { + logError(1, + "%s parseBinFile: Read only %d instead of %d... ERROR %08X\n", + tag, fw_size, FW_HEADER_SIZE + FW_BYTES_ALLIGN, + ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } else { + /* start parsing of bytes */ + u8ToU32(&fw_data[index], &temp); + if (temp != FW_HEADER_SIGNATURE) { + logError(1, + "%s parseBinFile: Wrong Signature %08X ... ERROR %08X\n", + tag, temp, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + logError(0, "%s parseBinFile: Fw Signature OK!\n", tag); + index += FW_BYTES_ALLIGN; + u8ToU32(&fw_data[index], &temp); + if (temp != FW_FTB_VER) { + logError(1, + "%s parseBinFile: Wrong ftb_version %08X ... ERROR %08X\n", + tag, temp, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + logError(0, "%s parseBinFile: ftb_version OK!\n", tag); + index += FW_BYTES_ALLIGN; + if (fw_data[index] != DCHIP_ID_0 || fw_data[index + 1] != + DCHIP_ID_1) { + logError(1, + "%s parseBinFile: Wrong target %02X != %02X %02X != %02X ... ERROR %08X\n", + tag, fw_data[index], DCHIP_ID_0, + fw_data[index + 1], + DCHIP_ID_1, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + index += FW_BYTES_ALLIGN; + u8ToU32(&fw_data[index], &temp); + logError(1, "%s parseBinFile: FILE SVN REV = %08X\n", tag, + temp); + + index += FW_BYTES_ALLIGN; + u8ToU32(&fw_data[index], &temp); + fwData->fw_ver = temp; + logError(1, "%s parseBinFile: FILE Fw Version = %04X\n", tag, + fwData->fw_ver); + + index += FW_BYTES_ALLIGN; + u8ToU32(&fw_data[index], &temp); + fwData->config_id = temp; + logError(1, "%s parseBinFile: FILE Config Project ID = %08X\n", + tag, temp); + + index += FW_BYTES_ALLIGN; + u8ToU32(&fw_data[index], &temp); + logError(1, "%s parseBinFile: FILE Config Version = %08X\n", + tag, temp); + + index += FW_BYTES_ALLIGN * 2; /* skip reserved data */ + + index += FW_BYTES_ALLIGN; + logError(1, "%s parseBinFile: File External Release = ", tag); + for (i = 0; i < EXTERNAL_RELEASE_INFO_SIZE; i++) { + fwData->externalRelease[i] = fw_data[index++]; + logError(1, "%02X ", fwData->externalRelease[i]); + } + logError(1, "\n"); + + /* index+=FW_BYTES_ALLIGN; */ + u8ToU32(&fw_data[index], &temp); + fwData->sec0_size = temp; + logError(1, "%s parseBinFile: sec0_size = %08X (%d bytes)\n", + tag, fwData->sec0_size, fwData->sec0_size); + + index += FW_BYTES_ALLIGN; + u8ToU32(&fw_data[index], &temp); + fwData->sec1_size = temp; + logError(1, "%s parseBinFile: sec1_size = %08X (%d bytes)\n", + tag, fwData->sec1_size, fwData->sec1_size); + + index += FW_BYTES_ALLIGN; + u8ToU32(&fw_data[index], &temp); + fwData->sec2_size = temp; + logError(1, "%s parseBinFile: sec2_size = %08X (%d bytes)\n", + tag, fwData->sec2_size, fwData->sec2_size); + + index += FW_BYTES_ALLIGN; + u8ToU32(&fw_data[index], &temp); + fwData->sec3_size = temp; + logError(1, "%s parseBinFile: sec3_size = %08X (%d bytes)\n", + tag, fwData->sec3_size, fwData->sec3_size); + + index += FW_BYTES_ALLIGN;/* skip header crc */ + + /* if (!keep_cx) */ + /* { */ + dimension = fwData->sec0_size + fwData->sec1_size + + fwData->sec2_size + fwData->sec3_size; + temp = fw_size; + /*} else + * { + * dimension = fwData->sec0_size + fwData->sec1_size; + * temp = fw_size - fwData->sec2_size - fwData->sec3_size; + * fwData->sec2_size = 0; + * fwData->sec3_size = 0; + * }*/ + + if (dimension + FW_HEADER_SIZE + FW_BYTES_ALLIGN != temp) { + logError(1, + "%s parseBinFile: Read only %d instead of %d... ERROR %08X\n", + tag, fw_size, dimension + FW_HEADER_SIZE + + FW_BYTES_ALLIGN, ERROR_FILE_PARSE); + res = ERROR_FILE_PARSE; + goto END; + } + + fwData->data = (u8 *)kmalloc(dimension * sizeof(u8), + GFP_KERNEL); + if (fwData->data == NULL) { + logError(1, "%s parseBinFile: ERROR %08X\n", tag, + ERROR_ALLOC); + res = ERROR_ALLOC; + goto END; + } + + index += FW_BYTES_ALLIGN; + memcpy(fwData->data, &fw_data[index], dimension); + if (fwData->sec2_size != 0) + u8ToU16(&fwData->data[fwData->sec0_size + + fwData->sec1_size + + FW_CX_VERSION], &fwData->cx_ver); + + else { + logError(1, + "%s parseBinFile: Initialize cx_ver to default value!\n", + tag); + fwData->cx_ver = systemInfo.u16_cxVer; + } + + logError(1, "%s parseBinFile: CX Version = %04X\n", tag, + fwData->cx_ver); + + fwData->data_size = dimension; + + index = FLASH_ORG_INFO_INDEX; + fwData->fw_code_size = fw_data[index++]; + fwData->panel_config_size = fw_data[index++]; + fwData->cx_area_size = fw_data[index++]; + fwData->fw_config_size = fw_data[index]; + + logError(0, "%s Code Pages: %d panel area Pages: %d" + " cx area Pages: %d fw config Pages: %d !\n", tag, + fwData->fw_code_size, fwData->panel_config_size, + fwData->cx_area_size, fwData->fw_config_size); + + if((fwData->fw_code_size == 0) || + (fwData->panel_config_size == 0) || + (fwData->cx_area_size == 0) || + (fwData->fw_config_size == 0)) + { + logError(0, "%s Using default flash Address\n", tag); + fwData->code_start_addr = FLASH_ADDR_CODE; + fwData->cx_start_addr = FLASH_ADDR_CX; + fwData->config_start_addr = FLASH_ADDR_CONFIG; + } + + else + { + fwData->code_start_addr = FLASH_ADDR_CODE; + fwData->cx_start_addr = (FLASH_ADDR_CODE + + (((fwData->fw_code_size + + fwData->panel_config_size) * + FLASH_PAGE_SIZE) / 4)); + fwData->config_start_addr = (FLASH_ADDR_CODE + + (((fwData->fw_code_size + + fwData->panel_config_size + + fwData->cx_area_size) * + FLASH_PAGE_SIZE) / 4)); + } + + logError(0, "%s Code start addr: 0x%08X cx start addr: 0x%08X" + " fw start addr: 0x%08X !\n", tag, + fwData->code_start_addr, fwData->cx_start_addr, + fwData->config_start_addr); + + logError(0, "%s READ FW DONE %d bytes!\n", tag, + fwData->data_size); + res = OK; + goto END; + } + +END: +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW + if (flash_mode == FW_PARAM_MODE) + vfree(fw_data); + else + kfree(fw_data); +#else + kfree(fw_data); +#endif + return res; +} + + + +/** + * Enable UVLO and Auto Power Down Mode + * @return OK if success or an error code which specify the type of error + */ +int flash_enable_uvlo_autopowerdown(void) +{ + u8 cmd[6] = { FTS_CMD_HW_REG_W, 0x20, 0x00, 0x00, FLASH_UVLO_ENABLE_CODE0, + FLASH_UVLO_ENABLE_CODE1 }; + u8 cmd1[6] = { FTS_CMD_HW_REG_W, 0x20, 0x00, 0x00, FLASH_AUTOPOWERDOWN_ENABLE_CODE0, + FLASH_AUTOPOWERDOWN_ENABLE_CODE1 }; + + logError(0, "%s Command enable uvlo ...\n", tag); + if (fts_write(cmd, ARRAY_SIZE(cmd)) < OK) { + logError(1, "%s flash_enable_uvlo_autopowerdown: ERROR %08X\n", tag, ERROR_BUS_W); + return ERROR_BUS_W; + } + if (fts_write(cmd1, ARRAY_SIZE(cmd1)) < OK) { + logError(1, "%s flash_enable_uvlo_autopowerdown: ERROR %08X\n", tag, ERROR_BUS_W); + return ERROR_BUS_W; + } + + logError(0, "%s Enable uvlo and flash auto power down DONE!\n", tag); + + return OK; +} + + +/** + * Unlock the flash to be programmed + * @return OK if success or an error code which specify the type of error + */ +int flash_unlock(void) +{ + u8 cmd[6] = { FTS_CMD_HW_REG_W, 0x20, 0x00, 0x00, FLASH_UNLOCK_CODE0, + FLASH_UNLOCK_CODE1 }; + + u8 cmd1[6] = { FTS_CMD_HW_REG_W, 0x20, 0x00, 0x00, FLASH_UNLOCK_CODE2, + FLASH_UNLOCK_CODE3 }; + + logError(0, "%s Command unlock ...\n", tag); + if (fts_write(cmd, ARRAY_SIZE(cmd)) < OK) { + logError(1, "%s flash_unlock: ERROR %08X\n", tag, ERROR_BUS_W); + return ERROR_BUS_W; + } + + if (fts_write(cmd1, ARRAY_SIZE(cmd1)) < OK) { + logError(1, "%s Command unlock: ERROR %08X\n", tag, ERROR_BUS_W); + return ERROR_BUS_W; + } + + logError(0, "%s Unlock flash DONE!\n", tag); + + return OK; +} + +/** + * Unlock the flash to be erased + * @return OK if success or an error code which specify the type of error + */ +int flash_erase_unlock(void) +{ + u8 cmd[6] = { FTS_CMD_HW_REG_W, 0x20, 0x00, + 0x00, + FLASH_ERASE_UNLOCK_CODE0, FLASH_ERASE_UNLOCK_CODE1 }; + + logError(0, "%s Try to erase unlock flash...\n", tag); + + logError(0, "%s Command erase unlock ...\n", tag); + if (fts_write(cmd, ARRAY_SIZE(cmd)) < 0) { + logError(1, "%s flash_erase_unlock: ERROR %08X\n", tag, + ERROR_BUS_W); + return ERROR_BUS_W; + } + + logError(0, "%s Erase Unlock flash DONE!\n", tag); + + return OK; +} + +/** + * Erase the full flash + * @return OK if success or an error code which specify the type of error + */ +int flash_full_erase(void) +{ + int status; + + u8 cmd1[6] = { FTS_CMD_HW_REG_W, 0x20, 0x00, 0x00, + FLASH_ERASE_CODE0 + 1, 0x00 }; + u8 cmd[6] = { FTS_CMD_HW_REG_W, 0x20, 0x00, 0x00, + FLASH_ERASE_CODE0, FLASH_ERASE_CODE1 }; + + if (fts_write(cmd1, ARRAY_SIZE(cmd1)) < OK) { + logError(1, "%s flash_erase_page_by_page: ERROR %08X\n", tag, + ERROR_BUS_W); + return ERROR_BUS_W; + } + + + logError(0, "%s Command full erase sent ...\n", tag); + if (fts_write(cmd, ARRAY_SIZE(cmd)) < OK) { + logError(1, "%s flash_full_erase: ERROR %08X\n", tag, + ERROR_BUS_W); + return ERROR_BUS_W; + } + + status = wait_for_flash_ready(FLASH_ERASE_CODE0); + + if (status != OK) { + logError(1, "%s flash_full_erase: ERROR %08X\n", tag, + ERROR_FLASH_NOT_READY); + return status | ERROR_FLASH_NOT_READY; + /* Flash not ready within the chosen time, better exit! */ + } + + logError(0, "%s Full Erase flash DONE!\n", tag); + + return OK; +} + +/** + * Erase the flash page by page, giving the possibility to skip the CX area and + * maintain therefore its value + * @param keep_cx if SKIP_PANEL_INIT the Panel Init pages will be skipped, + * if > SKIP_PANEL_CX_INIT Cx and Panel Init pages otherwise all the pages will + * be deleted + * @param fw raw FW data loaded from system + * @return OK if success or an error code which specify the type of error + */ +int flash_erase_page_by_page(ErasePage keep_cx, Firmware *fw) +{ + u8 status, i = 0; + u8 flash_cx_start_page = FLASH_CX_PAGE_START; + u8 flash_cx_end_page = FLASH_CX_PAGE_END; + u8 flash_panel_start_page = FLASH_PANEL_PAGE_START; + u8 flash_panel_end_page = FLASH_PANEL_PAGE_END; + +#ifdef SALIXP + u8 cmd[10] = { FTS_CMD_HW_REG_W, 0x20, 0x00, 0x00, FLASH_ERASE_CODE0, + 0x80, 0x00, 0xFF, 0x1C, 0x90 }; + u8 cmd2[11] = { FTS_CMD_HW_REG_W, 0x20, 0x00, + 0x01, 0x28, + 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + u8 mask[6] = { 0 }; + u8 mask_cnt = 6; +#else + u8 cmd1[6] = { FTS_CMD_HW_REG_W, 0x20, 0x00, 0x00, + FLASH_ERASE_CODE0 + 1, 0x00 }; + u8 cmd[6] = { FTS_CMD_HW_REG_W, 0x20, 0x00, 0x00, FLASH_ERASE_CODE0, + 0xA0 }; + u8 cmd2[9] = { FTS_CMD_HW_REG_W, 0x20, 0x00, + 0x01, 0x28, + 0xFF, + 0xFF, 0xFF, 0xFF }; + u8 mask[4] = { 0 }; + u8 mask_cnt = 4; +#endif + + if ((fw->fw_code_size == 0) || (fw->panel_config_size == 0) || (fw->cx_area_size == 0) || (fw->fw_config_size == 0)) + { + logError(0, "%s using default page addresses!\n", tag); + } + + else + { + flash_panel_start_page = fw->fw_code_size; + if (fw->panel_config_size > 1) + flash_panel_end_page = flash_panel_start_page + (fw->panel_config_size - 1); + else + flash_panel_end_page = flash_panel_start_page; + + flash_cx_start_page = flash_panel_end_page + 1; + if (fw->cx_area_size > 1) + flash_cx_end_page = flash_cx_start_page + (fw->cx_area_size - 1); + else + flash_cx_end_page = flash_cx_start_page; + } + + logError(0, "%s CX Start page: %d CX end page: %d Panel Start Page: %d Panel End page: %d!\n", tag, flash_cx_start_page, flash_cx_end_page, + flash_panel_start_page, flash_panel_end_page); + + for (i = flash_cx_start_page; i <= flash_cx_end_page && keep_cx >= + SKIP_PANEL_CX_INIT; i++) { + logError(0, "%s Skipping erase CX page %d!\n", tag, i); + fromIDtoMask(i, mask, mask_cnt); + + } + + + for (i = flash_panel_start_page; i <= flash_panel_end_page && keep_cx >= + SKIP_PANEL_INIT; i++) { + logError(0, "%s Skipping erase Panel Init page %d!\n", tag, i); + fromIDtoMask(i, mask, mask_cnt); + + } + + + logError(0, "%s Setting the page mask = ", tag); + for (i = 0; i < mask_cnt; i++) { + cmd2[5 + i] = cmd2[5 + i] & (~mask[i]); + logError(0, "%02X ", cmd2[5 + i]); + } + + logError(0, "\n%s Writing page mask...\n", tag); + if (fts_write(cmd2, ARRAY_SIZE(cmd2)) < OK) { + logError(1, + "%s flash_erase_page_by_page: Page mask ERROR %08X\n", + tag, + ERROR_BUS_W); + return ERROR_BUS_W; + } + +#ifndef SALIXP + if (fts_write(cmd1, ARRAY_SIZE(cmd1)) < OK) { + logError(1, + "%s flash_erase_page_by_page: Disable info ERROR %08X\n", + tag, + ERROR_BUS_W); + return ERROR_BUS_W; + } +#endif + + logError(0, "%s Command erase pages sent ...\n", tag); + if (fts_write(cmd, ARRAY_SIZE(cmd)) < OK) { + logError(1, "%s flash_erase_page_by_page: Erase ERROR %08X\n", + tag, ERROR_BUS_W); + return ERROR_BUS_W; + } + + status = wait_for_flash_ready(FLASH_ERASE_CODE0); + + if (status != OK) { + logError(1, "%s flash_erase_page_by_page: ERROR %08X\n", tag, + ERROR_FLASH_NOT_READY); + return status | ERROR_FLASH_NOT_READY; + /* Flash not ready within the chosen time, better exit! */ + } + + logError(0, "%s Erase flash page by page DONE!\n", tag); + + return OK; +} + + +/** + * Start the DMA procedure which actually transfer and burn the data loaded + * from memory into the Flash + * @return OK if success or an error code which specify the type of error + */ +int start_flash_dma(void) +{ + int status; + +#ifdef SALIXP + u8 cmd[12] = { FLASH_CMD_WRITE_REGISTER, 0x20, 0x00, 0x00, + 0x6B, 0x00, 0xFF, 0x1C, 0x10, 0x00, 0x00, FLASH_DMA_CODE1 }; +#else + u8 cmd[12] = { FLASH_CMD_WRITE_REGISTER, 0x20, 0x00, 0x00, + 0x6B, 0x00, 0x40, 0x42, 0x0F, 0x00, 0x00, FLASH_DMA_CODE1 }; +#endif + + /* write the command to erase the flash */ + + logError(0, "%s Command flash DMA ...\n", tag); + if (fts_write(cmd, ARRAY_SIZE(cmd)) < OK) { + logError(1, "%s start_flash_dma: ERROR %08X\n", tag, + ERROR_BUS_W); + return ERROR_BUS_W; + } + + status = wait_for_flash_ready(FLASH_DMA_CODE0); + + if (status != OK) { + logError(1, "%s start_flash_dma: ERROR %08X\n", tag, + ERROR_FLASH_NOT_READY); + return status | ERROR_FLASH_NOT_READY; + /* Flash not ready within the chosen time, better exit! */ + } + + logError(0, "%s flash DMA DONE!\n", tag); + + return OK; +} + +/** + * Copy the FW data that should be burnt in the Flash into the memory and then + * the DMA will take care about burning it into the Flash + * @param address address in memory where to copy the data, possible values + * are FLASH_ADDR_CODE, FLASH_ADDR_CONFIG, FLASH_ADDR_CX + * @param data pointer to an array of byte which contain the data that should + * be copied into the memory + * @param size size of data + * @return OK if success or an error code which specify the type of error + */ +int fillFlash(u32 address, u8 *data, int size) +{ + int remaining = size, index = 0; + int toWrite = 0; + int byteBlock = 0; + int wheel = 0; + u32 addr = 0; + int res; + int delta; + u8 *buff = NULL; + u8 buff2[12] = { 0 }; + + + buff = (u8 *)kmalloc((DMA_CHUNK + 5) * sizeof(u8), GFP_KERNEL); + if (buff == NULL) { + logError(1, "%s fillFlash: ERROR %08X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + while (remaining > 0) { + byteBlock = 0; + + addr = 0x00100000; + + while (byteBlock < FLASH_CHUNK && remaining > 0) { + index = 0; + if (remaining >= DMA_CHUNK) { + if ((byteBlock + DMA_CHUNK) <= FLASH_CHUNK) { + /* logError(1, "%s fillFlash: 1\n", + * tag); */ + toWrite = DMA_CHUNK; + remaining -= DMA_CHUNK; + byteBlock += DMA_CHUNK; + } else { + /* logError(1, "%s fillFlash: 2\n", + * tag); */ + delta = FLASH_CHUNK - byteBlock; + toWrite = delta; + remaining -= delta; + byteBlock += delta; + } + } else { + if ((byteBlock + remaining) <= FLASH_CHUNK) { + /* logError(1, "%s fillFlash: 3\n", + * tag); */ + toWrite = remaining; + byteBlock += remaining; + remaining = 0; + } else { + /* logError(1, "%s fillFlash: 4\n", + * tag); */ + delta = FLASH_CHUNK - byteBlock; + toWrite = delta; + remaining -= delta; + byteBlock += delta; + } + } + + + buff[index++] = FTS_CMD_HW_REG_W; + buff[index++] = (u8)((addr & 0xFF000000) >> 24); + buff[index++] = (u8)((addr & 0x00FF0000) >> 16); + buff[index++] = (u8)((addr & 0x0000FF00) >> 8); + buff[index++] = (u8)(addr & 0x000000FF); + + memcpy(&buff[index], data, toWrite); + /* logError(0, "%s Command = %02X , address = %02X %02X + * , bytes = %d, data = %02X %02X, %02X %02X\n", + * tag, buff[0], buff[1], buff[2], toWrite, buff[3], + * buff[4], buff[3 + toWrite-2], + * buff[3 + toWrite-1]); */ + if (fts_write(buff, index + toWrite) < OK) { + logError(1, "%s fillFlash: ERROR %08X\n", tag, + ERROR_BUS_W); + kfree(buff); + return ERROR_BUS_W; + } + + /* msleep(10); */ + addr += toWrite; + data += toWrite; + } + + + /* configuring the DMA */ + byteBlock = byteBlock / 4 - 1; + index = 0; + + buff2[index++] = FLASH_CMD_WRITE_REGISTER; + buff2[index++] = 0x20; + buff2[index++] = 0x00; + buff2[index++] = 0x00; + buff2[index++] = FLASH_DMA_CONFIG; + buff2[index++] = 0x00; + buff2[index++] = 0x00; + + addr = address + ((wheel * FLASH_CHUNK) / 4); + buff2[index++] = (u8)((addr & 0x000000FF)); + buff2[index++] = (u8)((addr & 0x0000FF00) >> 8); + buff2[index++] = (u8)(byteBlock & 0x000000FF); + buff2[index++] = (u8)((byteBlock & 0x0000FF00) >> 8); + buff2[index++] = 0x00; + + logError(0, + "%s DMA Command = %02X , address = %02X %02X, words = %02X %02X\n", + tag, buff2[0], buff2[8], buff2[7], buff2[10], + buff2[9]); + + if (fts_write(buff2, index) < OK) { + logError(1, + "%s Error during filling Flash! ERROR %08X\n", + tag, + ERROR_BUS_W); + kfree(buff); + return ERROR_BUS_W; + } + + res = start_flash_dma(); + if (res < OK) { + logError(1, + "%s Error during flashing DMA! ERROR %08X\n", + tag, + res); + kfree(buff); + return res; + } + wheel++; + } + kfree(buff); + return OK; +} + + +/** + * Execute the procedure to burn a FW in FTM4/FTI IC + * @param fw structure which contain the FW to be burnt + * @param force_burn if >0, the flashing procedure will be forced and executed + * regardless the additional info, otherwise the FW in the file will be burnt + * only if it is newer than the one running in the IC + * @param keep_cx if 1, the function preserve the CX/Panel Init area otherwise + * will be cleared + * @return OK if success or an error code which specify the type of error + */ +int flash_burn(Firmware fw, int force_burn, int keep_cx) +{ + int res; + + + if (!force_burn) { + for (res = EXTERNAL_RELEASE_INFO_SIZE - 1; res >= 0; res--) + if (fw.externalRelease[res] > + systemInfo.u8_releaseInfo[res]) + goto start; + logError(1, + "%s flash_burn: Firmware in the chip matches(or later) the firmware to flash! NO UPDATE ERROR %08X\n", + tag, ERROR_FW_NO_UPDATE); + return ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED; + } else { + /* Force burn TP FW */ + if (force_burn == CRC_FLASH) + goto start; + /* burn procedure to update the CX memory, if not present just + * skip it if there isn't a new fw release. */ + if (force_burn == CRC_CX && fw.sec2_size == 0) { + for (res = EXTERNAL_RELEASE_INFO_SIZE - 1; res >= 0; + res--) { + if (fw.externalRelease[res] > + systemInfo.u8_releaseInfo[res]) { + force_burn = 0; + /* Avoid loading the CX because it is missing + * in the bin file, it just need to update + * to last fw+cfg because a new release */ + goto start; + } + } + logError(1, + "%s flash_burn: CRC in CX but fw does not contain CX data! NO UPDATE ERROR %08X\n", + tag, ERROR_FW_NO_UPDATE); + return ERROR_FW_NO_UPDATE | ERROR_FLASH_BURN_FAILED; + } + } + + /* programming procedure start */ +start: + logError(0, "%s Programming Procedure for flashing started:\n\n", tag); + + logError(0, "%s 1) SYSTEM RESET:\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s system reset FAILED!\n", tag); + /* If there is no firmware, there is no controller ready event + * and there will be a timeout, we can keep going. But if + * there is an I2C error, we must exit. + */ + if (res != (ERROR_SYSTEM_RESET_FAIL | ERROR_TIMEOUT)) + return res | ERROR_FLASH_BURN_FAILED; + } else + logError(0, "%s system reset COMPLETED!\n\n", tag); + + msleep(100); /* required by hw during flash procedure */ + + logError(0, "%s 2) HOLD M3 :\n", tag); + res = hold_m3(); + if (res < OK) { + logError(1, "%s hold_m3 FAILED!\n", tag); + return res | ERROR_FLASH_BURN_FAILED; + } else + logError(0, "%s hold_m3 COMPLETED!\n\n", tag); + + + logError(0, "%s 3) ENABLE UVLO AND AUTO POWER DOWN MODE :\n", tag); + res = flash_enable_uvlo_autopowerdown(); + if (res < OK) { + logError(1, "%s flash_enable_uvlo_autopowerdown FAILED!\n", tag); + return res | ERROR_FLASH_BURN_FAILED; + } else + logError(0, "%s flash_enable_uvlo_autopowerdown COMPLETED!\n\n", tag); + + + logError(0, "%s 4) FLASH UNLOCK:\n", tag); + res = flash_unlock(); + if (res < OK) { + logError(1, "%s flash unlock FAILED! ERROR %08X\n", tag, + ERROR_FLASH_BURN_FAILED); + return res | ERROR_FLASH_BURN_FAILED; + } else + logError(0, "%s flash unlock COMPLETED!\n\n", tag); + + + logError(0, "%s 5) FLASH ERASE UNLOCK:\n", tag); + res = flash_erase_unlock(); + if (res < 0) { + logError(1, "%s flash unlock FAILED! ERROR %08X\n", tag, + ERROR_FLASH_BURN_FAILED); + return res | ERROR_FLASH_BURN_FAILED; + } else + logError(0, "%s flash unlock COMPLETED!\n\n", tag); + + logError(0, "%s 6) FLASH ERASE:\n", tag); + if (keep_cx > 0) { + if (fw.sec2_size != 0 && force_burn == CRC_CX) + res = flash_erase_page_by_page(SKIP_PANEL_INIT, &fw); + else + res = flash_erase_page_by_page(SKIP_PANEL_CX_INIT, &fw); + } else { + res = flash_erase_page_by_page(SKIP_PANEL_INIT, &fw); + if (fw.sec2_size == 0) + logError(1, + "%s WARNING!!! Erasing CX memory but no CX in fw file! touch will not work right after fw update!\n", + tag); + } + + if (res < OK) { + logError(1, "%s flash erase FAILED! ERROR %08X\n", tag, + ERROR_FLASH_BURN_FAILED); + return res | ERROR_FLASH_BURN_FAILED; + } else + logError(0, "%s flash erase COMPLETED!\n\n", tag); + + logError(0, "%s 7) LOAD PROGRAM:\n", tag); + res = fillFlash(fw.code_start_addr, &fw.data[0], fw.sec0_size); + if (res < OK) { + logError(1, "%s load program ERROR %08X\n", tag, + ERROR_FLASH_BURN_FAILED); + return res | ERROR_FLASH_BURN_FAILED; + } + logError(1, "%s load program DONE!\n", tag); + + logError(0, "%s 8) LOAD CONFIG:\n", tag); + res = fillFlash(fw.config_start_addr, &(fw.data[fw.sec0_size]), + fw.sec1_size); + if (res < OK) { + logError(1, "%s load config ERROR %08X\n", tag, + ERROR_FLASH_BURN_FAILED); + return res | ERROR_FLASH_BURN_FAILED; + } + logError(1, "%s load config DONE!\n", tag); + + if (fw.sec2_size != 0 && (force_burn == CRC_CX || keep_cx <= 0)) { + logError(0, "%s 8.1) LOAD CX:\n", tag); + res = fillFlash(fw.cx_start_addr, + &(fw.data[fw.sec0_size + fw.sec1_size]), + fw.sec2_size); + if (res < OK) { + logError(1, "%s load cx ERROR %08X\n", tag, + ERROR_FLASH_BURN_FAILED); + return res | ERROR_FLASH_BURN_FAILED; + } + logError(1, "%s load cx DONE!\n", tag); + } + + logError(0, "%s Flash burn COMPLETED!\n\n", tag); + + logError(0, "%s 9) SYSTEM RESET:\n", tag); + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s system reset FAILED! ERROR %08X\n", tag, + ERROR_FLASH_BURN_FAILED); + return res | ERROR_FLASH_BURN_FAILED; + } + logError(0, "%s system reset COMPLETED!\n\n", tag); + + + logError(0, "%s 10) FINAL CHECK:\n", tag); + res = readSysInfo(0); + if (res < 0) { + logError(1, + "%s flash_burn: Unable to retrieve Chip INFO! ERROR %08X\n", + tag, ERROR_FLASH_BURN_FAILED); + return res | ERROR_FLASH_BURN_FAILED; + } + + + for (res = 0; res < EXTERNAL_RELEASE_INFO_SIZE; res++) { + if (fw.externalRelease[res] != systemInfo.u8_releaseInfo[res]) { + /* External release is printed during readSysInfo */ + logError(1, + "%s Firmware in the chip different from the one that was burn!\n", + tag); + return ERROR_FLASH_BURN_FAILED; + } + } + + + logError(0, "%s Final check OK!\n", tag); + + return OK; +} diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsFlash.h b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsFlash.h new file mode 100644 index 000000000000..e0efd84cbffe --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsFlash.h @@ -0,0 +1,142 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for Flashing the IC * + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsFlash.h + * \brief Contains all the definitions and structs to handle the FW update + *process + */ + +#ifndef FTS_FLASH_H +#define FTS_FLASH_H + +#include "ftsSoftware.h" + +/* Flash possible status */ +#define FLASH_READY 0 /* /< value to indicate that the flash + * is ready */ +#define FLASH_BUSY 1 /* /< value to indicate that the flash + * is busy */ +#define FLASH_UNKNOWN -1 /* /< value to indicate an unknown + * status of the flash */ + +#define FLASH_STATUS_BYTES 1 /* /< number of bytes to check for read + * the flash status */ + + + +/* Flash timing parameters */ +#define FLASH_RETRY_COUNT 200 /* /< number of attemps to read the + * flash status */ +#define FLASH_WAIT_BEFORE_RETRY 50 /* /< time to wait in ms between status + * readings */ + + +#ifdef FW_H_FILE +#define PATH_FILE_FW "NULL" +#else +#define PATH_FILE_FW "st_fts.ftb" /* /< new FW bin file name */ +#endif + + +#ifdef ALIX +#define FLASH_CHUNK (32 * 1024) /* /< Max number of bytes that + * the + * DMA can burn on the flash in + *one shot in FTI */ +#else +#define FLASH_CHUNK (64 * 1024) /* /< Max number of bytes that + * the + * DMA can burn on the flash in + *one shot in FTI */ +#endif + + +#define DMA_CHUNK 32 /* /< Max number of bytes that can be + * written in I2C to the DMA */ + +#define FLASH_ORG_INFO_INDEX 280 + +#define FLASH_PAGE_SIZE (4 * 1024) //page size of 4KB + +/** + * Define which kind of erase page by page should be performed + */ +typedef enum { + ERASE_ALL = 0, /* /< erase all the pages */ + SKIP_PANEL_INIT = 1, /* /< skip erase Panel Init Pages */ + SKIP_PANEL_CX_INIT = 2 /* /< skip erase Panel Init and CX Pages */ +} ErasePage; + +/** @addtogroup fw_file + * @{ + */ + +/** + * Struct which contains information and data of the FW that should be burnt + *into the IC + */ +typedef struct { + u8 *data; /* /< pointer to an array of bytes which represent the + * FW data */ + u16 fw_ver; /* /< FW version of the FW file */ + u16 config_id; /* /< Config ID of the FW file */ + u16 cx_ver; /* /< Cx version of the FW file */ + u8 externalRelease[EXTERNAL_RELEASE_INFO_SIZE]; /* /< External Release + * Info of the FW file + * */ + int data_size; /* /< dimension of data (the actual data to be burnt) */ + u32 sec0_size; /* /< dimension of section 0 (FW) in .ftb file */ + u32 sec1_size; /* /< dimension of section 1 (Config) in .ftb file */ + u32 sec2_size; /* /< dimension of section 2 (Cx) in .ftb file */ + u32 sec3_size; /* /< dimension of section 3 (TBD) in .ftb file */ + u8 fw_code_size; /* /< size of fw code in pages in + .ftb file */ + u8 panel_config_size;/* /< size of panel area in pages in + .ftb file */ + u8 cx_area_size;/* /< size of cx area in pages in + .ftb file */ + u8 fw_config_size;/* /< size of fw config in pages in + .ftb file */ + u32 code_start_addr; /* start addr for fw code */ + u32 cx_start_addr; /* start addr for cx area */ + u32 config_start_addr; /* start addr for config area */ +} Firmware; + +/** @}*/ + +/** @addtogroup flash_command + * @{ + */ + +int wait_for_flash_ready(u8 type); +int hold_m3(void); +int flash_erase_unlock(void); +int flash_full_erase(void); +int flash_erase_page_by_page(ErasePage keep_cx, Firmware *fw); +int start_flash_dma(void); +int fillFlash(u32 address, u8 *data, int size); + +int flash_unlock(void); +int getFWdata(const char *pathToFile, u8 **data, int *size); +int parseBinFile(u8 *fw_data, int fw_size, Firmware *fw, int keep_cx); +int readFwFile(const char *path, Firmware *fw, int keep_cx); +int flash_burn(Firmware fw, int force_burn, int keep_cx); +int flashProcedure(const char *path, int force, int keep_cx); +int flash_enable_uvlo_autopowerdown(void); + +#endif + +/** @}*/ diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsFrame.c b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsFrame.c new file mode 100644 index 000000000000..30cb00215e48 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsFrame.c @@ -0,0 +1,692 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS functions for getting frames * + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsFrame.c + * \brief Contains all the functions to work with frames + */ + +#include "ftsCompensation.h" +#include "ftsCore.h" +#include "ftsError.h" +#include "ftsFrame.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTool.h" +#include "ftsTime.h" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + + + +extern SysInfo systemInfo; /* /< forward declaration of the global variable + * of containing System Info Data */ + + +/** + * Read the channels lengths from the config memory + * @return OK if success or an error code which specify the type of error + */ +int getChannelsLength(void) +{ + int ret; + u8 data[2]; + + ret = readConfig(ADDR_CONFIG_SENSE_LEN, data, 2); + if (ret < OK) { + logError(1, "%s getChannelsLength: ERROR %08X\n", tag, ret); + + return ret; + } + + systemInfo.u8_scrRxLen = (int)data[0]; + systemInfo.u8_scrTxLen = (int)data[1]; + + logError(0, "%s Force_len = %d Sense_Len = %d\n", tag, + systemInfo.u8_scrTxLen, systemInfo.u8_scrRxLen); + + return OK; +} + + + +/** + * Read and pack the frame data related to the nodes + * @param address address in memory when the frame data node start + * @param size amount of data to read + * @param frame pointer to an array of bytes which will contain the frame node + *data + * @return OK if success or an error code which specify the type of error + */ +int getFrameData(u16 address, int size, short *frame) +{ + int i, j, ret; + u8 *data = (u8 *)kmalloc(size * sizeof(u8), GFP_KERNEL); + + if (data == NULL) { + logError(1, "%s getFrameData: ERROR %08X\n", tag, ERROR_ALLOC); + return ERROR_ALLOC; + } + + ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, address, data, + size, DUMMY_FRAMEBUFFER); + if (ret < OK) { + logError(1, "%s getFrameData: ERROR %08X\n", tag, ERROR_BUS_R); + kfree(data); + return ERROR_BUS_R; + } + j = 0; + for (i = 0; i < size; i += 2) { + frame[j] = (short)((data[i + 1] << 8) + data[i]); + j++; + } + kfree(data); + return OK; +} + + +/** + * Return the number of Sense Channels (Rx) + * @return number of Rx channels + */ +int getSenseLen(void) +{ + if (systemInfo.u8_scrRxLen == 0) + getChannelsLength(); + return systemInfo.u8_scrRxLen; +} + +/** + * Return the number of Force Channels (Tx) + * @return number of Tx channels + */ +int getForceLen(void) +{ + if (systemInfo.u8_scrTxLen == 0) + getChannelsLength(); + return systemInfo.u8_scrTxLen; +} + + +/******************** New API **************************/ + +/** + * Read a MS Frame from frame buffer memory + * @param type type of MS frame to read + * @param frame pointer to MutualSenseFrame variable which will contain the + * data + * @return > 0 if success specifying the number of node into the frame or + * an error code which specify the type of error + */ +int getMSFrame3(MSFrameType type, MutualSenseFrame *frame) +{ + u16 offset; + int ret, force_len, sense_len; + + force_len = getForceLen(); + sense_len = getSenseLen(); + + frame->node_data = NULL; + + logError(0, "%s %s: Starting to get frame %02X\n", tag, __func__, + type); + switch (type) { + case MS_RAW: + offset = systemInfo.u16_msTchRawAddr; + goto LOAD_NORM; + case MS_FILTER: + offset = systemInfo.u16_msTchFilterAddr; + + goto LOAD_NORM; + case MS_STRENGTH: + offset = systemInfo.u16_msTchStrenAddr; + goto LOAD_NORM; + case MS_BASELINE: + offset = systemInfo.u16_msTchBaselineAddr; +LOAD_NORM: + if (force_len == 0 || sense_len == 0) { + logError(1, + "%s %s: number of channels not initialized ERROR %08X\n", + tag, __func__, ERROR_CH_LEN); + return ERROR_CH_LEN | ERROR_GET_FRAME; + } + + break; + + case MS_KEY_RAW: + offset = systemInfo.u16_keyRawAddr; + goto LOAD_KEY; + case MS_KEY_FILTER: + offset = systemInfo.u16_keyFilterAddr; + goto LOAD_KEY; + case MS_KEY_STRENGTH: + offset = systemInfo.u16_keyStrenAddr; + goto LOAD_KEY; + case MS_KEY_BASELINE: + offset = systemInfo.u16_keyBaselineAddr; +LOAD_KEY: + if (systemInfo.u8_keyLen == 0) { + logError(1, + "%s %s: number of channels not initialized ERROR %08X\n", + tag, __func__, ERROR_CH_LEN); + return ERROR_CH_LEN | ERROR_GET_FRAME; + } + force_len = 1; + sense_len = systemInfo.u8_keyLen; + break; + + case FRC_RAW: + offset = systemInfo.u16_frcRawAddr; + goto LOAD_FRC; + case FRC_FILTER: + offset = systemInfo.u16_frcFilterAddr; + goto LOAD_FRC; + case FRC_STRENGTH: + offset = systemInfo.u16_frcStrenAddr; + goto LOAD_FRC; + case FRC_BASELINE: + offset = systemInfo.u16_frcBaselineAddr; +LOAD_FRC: + if (force_len == 0) { + logError(1, + "%s %s: number of channels not initialized ERROR %08X\n", + tag, __func__, ERROR_CH_LEN); + return ERROR_CH_LEN | ERROR_GET_FRAME; + } + sense_len = 1; + break; + default: + logError(1, "%s %s: Invalid type ERROR %08X\n", tag, __func__, + ERROR_OP_NOT_ALLOW | ERROR_GET_FRAME); + return ERROR_OP_NOT_ALLOW | ERROR_GET_FRAME; + } + + frame->node_data_size = ((force_len) * sense_len); + frame->header.force_node = force_len; + frame->header.sense_node = sense_len; + frame->header.type = type; + + logError(0, "%s %s: Force_len = %d Sense_len = %d Offset = %04X\n", + tag, __func__, force_len, sense_len, offset); + + frame->node_data = (short *)kmalloc(frame->node_data_size * + sizeof(short), GFP_KERNEL); + if (frame->node_data == NULL) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, ERROR_ALLOC | + ERROR_GET_FRAME); + return ERROR_ALLOC | ERROR_GET_FRAME; + } + + ret = getFrameData(offset, frame->node_data_size * BYTES_PER_NODE, + (frame->node_data)); + if (ret < OK) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, + ERROR_GET_FRAME_DATA); + kfree(frame->node_data); + frame->node_data = NULL; + return ret | ERROR_GET_FRAME_DATA | ERROR_GET_FRAME; + } + /* if you want to access one node i,j, + * compute the offset like: offset = i*columns + j = > frame[i, j] */ + + logError(0, "%s Frame acquired!\n", tag); + return frame->node_data_size; + /* return the number of data put inside frame */ +} + +/** + * Read a SS Frame from frame buffer + * @param type type of SS frame to read + * @param frame pointer to SelfSenseFrame variable which will contain the data + * @return > 0 if success specifying the number of node into frame or an + * error code which specify the type of error + */ +int getSSFrame3(SSFrameType type, SelfSenseFrame *frame) +{ + u16 offset_force, offset_sense; + int ret; + + frame->force_data = NULL; + frame->sense_data = NULL; + + frame->header.force_node = getForceLen(); /* use getForce/SenseLen + * because introduce a + * recover mechanism in + * case of len =0 */ + frame->header.sense_node = getSenseLen(); + + if (frame->header.force_node == 0 || frame->header.sense_node == 0) { + logError(1, + "%s %s: number of channels not initialized ERROR %08X\n", + tag, + __func__, ERROR_CH_LEN); + return ERROR_CH_LEN | ERROR_GET_FRAME; + } + + + logError(0, "%s %s: Starting to get frame %02X\n", tag, __func__, + type); + switch (type) { + case SS_RAW: + offset_force = systemInfo.u16_ssTchTxRawAddr; + offset_sense = systemInfo.u16_ssTchRxRawAddr; + break; + case SS_FILTER: + offset_force = systemInfo.u16_ssTchTxFilterAddr; + offset_sense = systemInfo.u16_ssTchRxFilterAddr; + break; + case SS_STRENGTH: + offset_force = systemInfo.u16_ssTchTxStrenAddr; + offset_sense = systemInfo.u16_ssTchRxStrenAddr; + break; + case SS_BASELINE: + offset_force = systemInfo.u16_ssTchTxBaselineAddr; + offset_sense = systemInfo.u16_ssTchRxBaselineAddr; + break; + + case SS_HVR_RAW: + offset_force = systemInfo.u16_ssHvrTxRawAddr; + offset_sense = systemInfo.u16_ssHvrRxRawAddr; + break; + case SS_HVR_FILTER: + offset_force = systemInfo.u16_ssHvrTxFilterAddr; + offset_sense = systemInfo.u16_ssHvrRxFilterAddr; + break; + case SS_HVR_STRENGTH: + offset_force = systemInfo.u16_ssHvrTxStrenAddr; + offset_sense = systemInfo.u16_ssHvrRxStrenAddr; + break; + case SS_HVR_BASELINE: + offset_force = systemInfo.u16_ssHvrTxBaselineAddr; + offset_sense = systemInfo.u16_ssHvrRxBaselineAddr; + break; + + case SS_PRX_RAW: + offset_force = systemInfo.u16_ssPrxTxRawAddr; + offset_sense = systemInfo.u16_ssPrxRxRawAddr; + break; + case SS_PRX_FILTER: + offset_force = systemInfo.u16_ssPrxTxFilterAddr; + offset_sense = systemInfo.u16_ssPrxRxFilterAddr; + break; + case SS_PRX_STRENGTH: + offset_force = systemInfo.u16_ssPrxTxStrenAddr; + offset_sense = systemInfo.u16_ssPrxRxStrenAddr; + break; + case SS_PRX_BASELINE: + offset_force = systemInfo.u16_ssPrxTxBaselineAddr; + offset_sense = systemInfo.u16_ssPrxRxBaselineAddr; + break; + case SS_DETECT_RAW: + if (systemInfo.u8_ssDetScanSet == 0) { + offset_force = systemInfo.u16_ssDetRawAddr; + offset_sense = 0; + frame->header.sense_node = 0; + } else { + offset_sense = systemInfo.u16_ssDetRawAddr; + offset_force = 0; + frame->header.force_node = 0; + } + break; + + case SS_DETECT_FILTER: + if (systemInfo.u8_ssDetScanSet == 0) { + offset_force = systemInfo.u16_ssDetFilterAddr; + offset_sense = 0; + frame->header.sense_node = 0; + } else { + offset_sense = systemInfo.u16_ssDetFilterAddr; + offset_force = 0; + frame->header.force_node = 0; + } + break; + + case SS_DETECT_BASELINE: + if (systemInfo.u8_ssDetScanSet == 0) { + offset_force = systemInfo.u16_ssDetBaselineAddr; + offset_sense = 0; + frame->header.sense_node = 0; + } else { + offset_sense = systemInfo.u16_ssDetBaselineAddr; + offset_force = 0; + frame->header.force_node = 0; + } + break; + + case SS_DETECT_STRENGTH: + if (systemInfo.u8_ssDetScanSet == 0) { + offset_force = systemInfo.u16_ssDetStrenAddr; + offset_sense = 0; + frame->header.sense_node = 0; + } else { + offset_sense = systemInfo.u16_ssDetStrenAddr; + offset_force = 0; + frame->header.force_node = 0; + } + break; + + default: + logError(1, "%s %s: Invalid type ERROR %08X\n", tag, __func__, + ERROR_OP_NOT_ALLOW | ERROR_GET_FRAME); + return ERROR_OP_NOT_ALLOW | ERROR_GET_FRAME; + } + + frame->header.type = type; + + logError(0, + "%s %s: Force_len = %d Sense_len = %d Offset_force = %04X Offset_sense = %04X\n", + tag, __func__, frame->header.force_node, + frame->header.sense_node, + offset_force, offset_sense); + + frame->force_data = (short *)kmalloc(frame->header.force_node * + sizeof(short), GFP_KERNEL); + if (frame->force_data == NULL) { + logError(1, "%s %s: can not allocate force_data ERROR %08X\n", + tag, __func__, ERROR_ALLOC | ERROR_GET_FRAME); + return ERROR_ALLOC | ERROR_GET_FRAME; + } + + frame->sense_data = (short *)kmalloc(frame->header.sense_node * + sizeof(short), GFP_KERNEL); + if (frame->sense_data == NULL) { + kfree(frame->force_data); + frame->force_data = NULL; + logError(1, "%s %s: can not allocate sense_data ERROR %08X\n", + tag, __func__, ERROR_ALLOC | ERROR_GET_FRAME); + return ERROR_ALLOC | ERROR_GET_FRAME; + } + + ret = getFrameData(offset_force, frame->header.force_node * + BYTES_PER_NODE, (frame->force_data)); + if (ret < OK) { + logError(1, + "%s %s: error while reading force data ERROR %08X\n", + tag, + __func__, ERROR_GET_FRAME_DATA); + kfree(frame->force_data); + frame->force_data = NULL; + kfree(frame->sense_data); + frame->sense_data = NULL; + return ret | ERROR_GET_FRAME_DATA | ERROR_GET_FRAME; + } + + ret = getFrameData(offset_sense, frame->header.sense_node * + BYTES_PER_NODE, (frame->sense_data)); + if (ret < OK) { + logError(1, + "%s %s: error while reading sense data ERROR %08X\n", + tag, + __func__, ERROR_GET_FRAME_DATA); + kfree(frame->force_data); + frame->force_data = NULL; + kfree(frame->sense_data); + frame->sense_data = NULL; + return ret | ERROR_GET_FRAME_DATA | ERROR_GET_FRAME; + } + /* if you want to access one node i,j, you should compute the offset + * like: offset = i*columns + j = > frame[i, j] */ + + logError(0, "%s Frame acquired!\n", tag); + return frame->header.force_node + frame->header.sense_node; + /* return the number of data put inside frame */ +} + + +/** + * Read Initialization Data Header and check that the type loaded match with + * the one previously requested + * @param type type of Initialization data requested @link load_opt Load Host + * Data Option @endlink + * @param msHeader pointer to DataHeader variable which will contain the header + * info for the MS frame + * @param ssHeader pointer to DataHeader variable which will contain the header + * info for the SS frame + * @param address pointer to a variable which will contain the updated address + * to the next data + * @return OK if success or an error code which specify the type of error + */ +int readSyncDataHeader(u8 type, DataHeader *msHeader, DataHeader *ssHeader, + u64 *address) +{ + u64 offset = ADDR_FRAMEBUFFER; + u8 data[SYNCFRAME_DATA_HEADER]; + int ret; + + ret = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, offset, data, + SYNCFRAME_DATA_HEADER, DUMMY_FRAMEBUFFER); + if (ret < OK) { /* i2c function have already a retry mechanism */ + logError(1, + "%s %s: error while reading data header ERROR %08X\n", + tag, + __func__, ret); + return ret; + } + + logError(0, "%s Read Data Header done!\n", tag); + + if (data[0] != HEADER_SIGNATURE) { + logError(1, + "%s %s: The Header Signature was wrong! %02X != %02X ERROR %08X\n", + tag, __func__, data[0], HEADER_SIGNATURE, + ERROR_WRONG_DATA_SIGN); + return ERROR_WRONG_DATA_SIGN; + } + + + if (data[1] != type) { + logError(1, "%s %s: Wrong type found! %02X!=%02X ERROR %08X\n", + tag, __func__, data[1], type, ERROR_DIFF_DATA_TYPE); + return ERROR_DIFF_DATA_TYPE; + } + + logError(0, "%s Type = %02X of SyncFrame data OK!\n", tag, type); + + msHeader->force_node = data[5]; + msHeader->sense_node = data[6]; + logError(0, "%s MS Frame force_node = %d, sense_node = %d\n", tag, + msHeader->force_node, msHeader->sense_node); + + ssHeader->force_node = data[7]; + ssHeader->sense_node = data[8]; + logError(0, "%s SS Frame force_node = %d, sense_node = %d\n", tag, + ssHeader->force_node, ssHeader->sense_node); + + *address = offset + SYNCFRAME_DATA_HEADER + data[4]; + + return OK; +} + +/** + * Read a Sync Frame from frame buffer which contain MS and SS data collected + *for the same scan + * @param type type of Sync frame to read, possible values: + * LOAD_SYNC_FRAME_RAW, LOAD_SYNC_FRAME_FILTER, LOAD_SYNC_FRAME_BASELINE, + * LOAD_SYNC_FRAME_STRENGTH + * @param msFrame pointer to MutualSenseFrame variable which will contain the + *MS data + * @param ssFrame pointer to SelfSenseFrame variable which will contain the SS + *data + * @return >0 if success specifying the total number of nodes copied into + * msFrame and ssFrame or an error code which specify the type of error + */ +int getSyncFrame(u8 type, MutualSenseFrame *msFrame, SelfSenseFrame *ssFrame) +{ + int res; + u64 address; + + msFrame->node_data = NULL; + ssFrame->force_data = NULL; + ssFrame->sense_data = NULL; + + logError(0, "%s %s: Starting to get Sync Frame %02X...\n", tag, + __func__, type); + switch (type) { + case LOAD_SYNC_FRAME_RAW: + msFrame->header.type = MS_RAW; + ssFrame->header.type = SS_RAW; + break; + + case LOAD_SYNC_FRAME_FILTER: + msFrame->header.type = MS_FILTER; + ssFrame->header.type = SS_FILTER; + break; + + case LOAD_SYNC_FRAME_BASELINE: + msFrame->header.type = MS_BASELINE; + ssFrame->header.type = SS_BASELINE; + break; + + case LOAD_SYNC_FRAME_STRENGTH: + msFrame->header.type = MS_STRENGTH; + ssFrame->header.type = SS_STRENGTH; + break; + + default: + return ERROR_OP_NOT_ALLOW | ERROR_GET_FRAME; + } + + logError(0, "%s %s: Requesting Sync Frame %02X...\n", tag, __func__, + type); + res = requestSyncFrame(type); + if (res < OK) { + logError(1, + "%s %s: error while requesting Sync Frame ERROR %08X\n", + tag, + __func__, res | ERROR_GET_FRAME_DATA); + return res | ERROR_GET_FRAME_DATA; + } + + res = readSyncDataHeader(type, &(msFrame->header), &(ssFrame->header), + &address); + if (res < OK) { + logError(1, + "%s %s: error while reading Sync Frame header... ERROR %08X\n", + tag, __func__, res | ERROR_GET_FRAME_DATA); + return res | ERROR_GET_FRAME_DATA; + } + + msFrame->node_data_size = msFrame->header.force_node * + msFrame->header.sense_node; + + msFrame->node_data = (short *)kmalloc(msFrame->node_data_size * + sizeof(short), GFP_KERNEL); + if (msFrame->node_data == NULL) { + logError(1, + "%s %s: impossible allocate memory for MS frame... ERROR %08X\n", + tag, __func__, ERROR_ALLOC | ERROR_GET_FRAME); + return ERROR_ALLOC | ERROR_GET_FRAME; + } + + logError(0, "%s %s: Getting MS frame at %04llX...\n", tag, __func__, + address); + res = getFrameData(address, (msFrame->node_data_size) * BYTES_PER_NODE, + (msFrame->node_data)); + if (res < OK) { + logError(1, "%s %s: error while getting MS data...ERROR %08X\n", + tag, __func__, res); + res |= ERROR_GET_FRAME_DATA | ERROR_GET_FRAME; + goto ERROR; + } + + /* move the offset */ + address += (msFrame->node_data_size) * BYTES_PER_NODE; + + ssFrame->force_data = (short *)kmalloc(ssFrame->header.force_node * + sizeof(short), GFP_KERNEL); + if (ssFrame->force_data == NULL) { + logError(1, + "%s %s: impossible allocate memory for SS force frame...ERROR %08X\n", + tag, __func__, ERROR_ALLOC | ERROR_GET_FRAME); + res = ERROR_ALLOC | ERROR_GET_FRAME; + goto ERROR; + } + + logError(0, "%s %s: Getting SS force frame at %04llX...\n", tag, __func__, + address); + res = getFrameData(address, (ssFrame->header.force_node) * + BYTES_PER_NODE, (ssFrame->force_data)); + if (res < OK) { + logError(1, + "%s %s: error while getting SS force data...ERROR %08X\n", + tag, + __func__, res); + res |= ERROR_GET_FRAME_DATA | ERROR_GET_FRAME; + goto ERROR; + } + + /* move the offset */ + address += (ssFrame->header.force_node) * BYTES_PER_NODE; + + ssFrame->sense_data = (short *)kmalloc(ssFrame->header.sense_node * + sizeof(short), GFP_KERNEL); + if (ssFrame->sense_data == NULL) { + logError(1, + "%s %s: impossible allocate memory for SS sense frame...ERROR %08X\n", + tag, __func__, ERROR_ALLOC | ERROR_GET_FRAME); + res = ERROR_ALLOC | ERROR_GET_FRAME; + goto ERROR; + } + + logError(0, "%s %s: Getting SS sense frame at %04llX...\n", tag, __func__, + address); + res = getFrameData(address, (ssFrame->header.sense_node) * + BYTES_PER_NODE, (ssFrame->sense_data)); + if (res < OK) { + logError(1, + "%s %s: error while getting SS sense data...ERROR %08X\n", + tag, + __func__, res); + res |= ERROR_GET_FRAME_DATA | ERROR_GET_FRAME; + goto ERROR; + } + +ERROR: + if (res < OK) { + if (msFrame->node_data != NULL) { + kfree(msFrame->node_data); + msFrame->node_data = NULL; + } + + if (ssFrame->force_data != NULL) { + kfree(ssFrame->force_data); + ssFrame->force_data = NULL; + } + + if (ssFrame->sense_data != NULL) { + kfree(ssFrame->sense_data); + ssFrame->sense_data = NULL; + } + logError(0, "Getting Sync Frame FAILED! ERROR %08X!\n", res); + } else { + logError(0, "Getting Sync Frame FINISHED!\n"); + res = msFrame->node_data_size + ssFrame->header.force_node + + ssFrame->header.sense_node; + } + return res; +} diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsFrame.h b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsFrame.h new file mode 100644 index 000000000000..446dd7527bfe --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsFrame.h @@ -0,0 +1,114 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS functions for getting frames * + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsFrame.h + * \brief Contains all the definitions and structs to work with frames + */ + + +#ifndef FTS_FRAME_H +#define FTS_FRAME_H + +#include "ftsSoftware.h" +#include "ftsCore.h" + + + +/* Number of data bytes for each node */ +#define BYTES_PER_NODE 2 /* /< number of data bytes for each node */ + + +#define RETRY_FRAME_DATA_READ 2 /* /< max number of attempts to read a + * frame */ +#define SYNCFRAME_DATA_HEADER (DATA_HEADER + 12) /* /< number of bytes of + * Sync Frame Header */ +/** + * Possible types of MS frames + */ +typedef enum { + MS_RAW = 0, /* /< Mutual Sense Raw Frame */ + MS_FILTER = 1, /* /< Mutual Sense Filtered Frame */ + MS_STRENGTH = 2, /* /< Mutual Sense Strength Frame (Baseline-Raw) + * */ + MS_BASELINE = 3, /* /< Mutual Sense Baseline Frame */ + MS_KEY_RAW = 4, /* /< Mutual Sense Key Raw Frame */ + MS_KEY_FILTER = 5, /* /< Mutual Sense Key Filter Frame */ + MS_KEY_STRENGTH = 6, /* /< Mutual Sense Key Strength Frame + * (Baseline-Raw) */ + MS_KEY_BASELINE = 7, /* /< Mutual Sense Key Baseline Frame */ + FRC_RAW = 8, /* /< Force Raw Frame */ + FRC_FILTER = 9, /* /< Force Filtered Frame */ + FRC_STRENGTH = 10, /* /< Force Strength Frame (Baseline-Raw) */ + FRC_BASELINE = 11 /* /< Force Baseline Frame */ +} MSFrameType; + + +/** + * Possible types of SS frames + */ +typedef enum { + SS_RAW = 0, /* /< Self Sense Raw Frame */ + SS_FILTER = 1, /* /< Self Sense Filtered Frame */ + SS_STRENGTH = 2, /* /< Self Sense Strength Frame (Baseline-Raw) + * */ + SS_BASELINE = 3, /* /< Self Sense Baseline Frame */ + SS_HVR_RAW = 4, /* /< Self Sense Hover Raw Frame */ + SS_HVR_FILTER = 5, /* /< Self Sense Hover Filter Frame */ + SS_HVR_STRENGTH = 6, /* /< Self Sense Hover Strength Frame + * (Baseline-Raw) */ + SS_HVR_BASELINE = 7, /* /< Self Sense Hover Baseline Frame */ + SS_PRX_RAW = 8, /* /< Self Sense Proximity Raw Frame */ + SS_PRX_FILTER = 9, /* /< Self Sense Proximity Filtered Frame */ + SS_PRX_STRENGTH = 10, /* /< Self Sense Proximity Strength Frame + * (Baseline-Raw) */ + SS_PRX_BASELINE = 11, /* /< Self Sense Proximity Baseline Frame */ + + SS_DETECT_RAW = 12, /* /< Self Sense Detect Raw Frame */ + SS_DETECT_FILTER = 13, /* /< Self Sense Detect Filter Frame */ + SS_DETECT_STRENGTH = 14, /* /< Self Sense Detect Strength Frame */ + SS_DETECT_BASELINE = 15 /* /< Self Sense Detect Baseline Frame */ +} SSFrameType; + + +/** + * Struct which contains the data of a MS Frame + */ +typedef struct { + DataHeader header; /* /< Header which contain basic info of the + * frame */ + short *node_data; /* /< Data of the frame */ + int node_data_size; /* /< Dimension of the data of the frame */ +} MutualSenseFrame; + +/** + * Struct which contains the data of a SS Frame + */ +typedef struct { + DataHeader header; /* /< Header which contain basic info of the + * frame */ + short *force_data; /* /< Force Channels Data */ + short *sense_data; /* /< Sense Channels Data */ +} SelfSenseFrame; + + +int getChannelsLength(void); +int getFrameData(u16 address, int size, short *frame); +int getSenseLen(void); +int getForceLen(void); +int getMSFrame3(MSFrameType type, MutualSenseFrame *frame); +int getSSFrame3(SSFrameType type, SelfSenseFrame *frame); +int getSyncFrame(u8 type, MutualSenseFrame *msFrame, SelfSenseFrame *ssFrame); +#endif diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsGesture.c b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsGesture.c new file mode 100644 index 000000000000..55ff6d38f9f8 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsGesture.c @@ -0,0 +1,405 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Gesture Utilities ** + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsGesture.c + * \brief Contains all the functions and variable to handle the Gesture + *Detection features + */ + +#include "ftsSoftware.h" +#include "ftsCore.h" +#include "ftsError.h" +#include "ftsGesture.h" +#include "ftsIO.h" +#include "ftsTime.h" +#include "ftsTool.h" + +/* /< store the gesture bitmask which the host want to enable. + * If bit set 1 the corresponding gesture will be detected in Gesture Mode */ +static u8 gesture_mask[GESTURE_MASK_SIZE] = { 0 }; +/* /< store the x coordinates of the points draw by the user + * when a gesture is detected */ +u16 gesture_coordinates_x[GESTURE_MAX_COORDS_PAIRS_REPORT] = { 0 }; +/* /< store the y coordinates of the points draw by the user + * when a gesture is detected */ +u16 gesture_coordinates_y[GESTURE_MAX_COORDS_PAIRS_REPORT] = { 0 }; +/* /< number of coordinates pairs (points) reported with the detected gesture */ +int gesture_coords_reported = ERROR_OP_NOT_ALLOW; +static u8 refreshGestureMask; /* /< flag which indicate if there is + * the need to set the gesture mask in the FW */ +struct mutex gestureMask_mutex; /* /< mutex used to control access on gesture + * shared variables */ + +/** + * Update the gesture mask stored in the driver and have to be used in gesture + * mode + * @param mask pointer to a byte array which store the gesture mask update + * that want to be performed. + * @param size dimension in byte of mask. This size can be <= + * GESTURE_MASK_SIZE. + * If size < GESTURE_MASK_SIZE the bytes of mask are considering continuos + * and starting from the less significant byte. + * @param en 0 = enable the gestures set in mask, 1 = disable the gestures set + *in mask + * @return OK if success or an error code which specify the type of error + */ +int updateGestureMask(u8 *mask, int size, int en) +{ + u8 temp; + int i; + + if (mask != NULL) { + if (size <= GESTURE_MASK_SIZE) { + if (en == FEAT_ENABLE) { + mutex_lock(&gestureMask_mutex); + logError(0, + "%s updateGestureMask: setting gesture mask to enable...\n", + tag); + if (mask != NULL) + for (i = 0; i < size; i++) + gesture_mask[i] = + gesture_mask[i] | + mask[i]; + /* back up of the gesture enabled */ + refreshGestureMask = 1; + logError(0, + "%s updateGestureMask: gesture mask to enable SET!\n", + tag); + mutex_unlock(&gestureMask_mutex); + return OK; + } else if (en == FEAT_DISABLE) { + mutex_lock(&gestureMask_mutex); + logError(0, + "%s updateGestureMask: setting gesture mask to disable...\n", + tag); + for (i = 0; i < size; i++) { + temp = gesture_mask[i] ^ mask[i]; + /* enabled XOR disabled */ + gesture_mask[i] = temp & + gesture_mask[i]; + /* temp AND enabled + * disable the gestures that were + * enabled */ + } + logError(0, + "%s updateGestureMask: gesture mask to disable SET!\n", + tag); + refreshGestureMask = 1; + mutex_unlock(&gestureMask_mutex); + return OK; + } else { + logError(1, + "%s updateGestureMask: Enable parameter Invalid! %d != %d or %d ERROR %08X\n", + tag, en, FEAT_DISABLE, FEAT_ENABLE, + ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + } else { + logError(1, + "%s updateGestureMask: Size not valid! %d > %d ERROR %08X\n", + tag, size, GESTURE_MASK_SIZE, + ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + } else { + logError(1, "%s updateGestureMask: Mask NULL! ERROR %08X\n", + tag, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } +} + +/** + * Enable in the FW the gesture mask to be used in gesture mode + * @param mask pointer to a byte array which store the gesture mask update + * that want to be sent to the FW, if NULL, will be used gesture_mask + * set previously without any changes. + * @param size dimension in byte of mask. This size can be <= + * GESTURE_MASK_SIZE. + * If size < GESTURE_MASK_SIZE the bytes of mask are considering continuos and + * starting from the less significant byte. + * @return OK if success or an error code which specify the type of error + */ +int enableGesture(u8 *mask, int size) +{ + int i, res; + + logError(0, "%s Trying to enable gesture...\n", tag); + + if (size <= GESTURE_MASK_SIZE) { + mutex_lock(&gestureMask_mutex); + if (mask != NULL) + for (i = 0; i < size; i++) + gesture_mask[i] = gesture_mask[i] | mask[i]; + /* back up of the gesture enabled */ + + res = setFeatures(FEAT_SEL_GESTURE, gesture_mask, + GESTURE_MASK_SIZE); + if (res < OK) { + logError(1, "%s enableGesture: ERROR %08X\n", tag, + res); + goto END; + } + + logError(0, "%s enableGesture DONE!\n", tag); + res = OK; + +END: + mutex_unlock(&gestureMask_mutex); + return res; + } else { + logError(1, + "%s enableGesture: Size not valid! %d > %d ERROR %08X\n", + tag, + size, GESTURE_MASK_SIZE, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } +} + +/** + * Disable in the FW the gesture mask to be used in gesture mode + * @param mask pointer to a byte array which store the gesture mask update that + * want to be sent to the FW, if NULL, all the gestures will be disabled. + * @param size dimension in byte of mask. This size can be <= + * GESTURE_MASK_SIZE. + * If size < GESTURE_MASK_SIZE the bytes of mask are considering continuos and + * starting from the less significant byte. + * @return OK if success or an error code which specify the type of error + */ +int disableGesture(u8 *mask, int size) +{ + u8 temp; + int i, res; + u8 *pointer; + + + logError(0, "%s Trying to disable gesture...\n", tag); + + + if (size <= GESTURE_MASK_SIZE) { + mutex_lock(&gestureMask_mutex); + if (mask != NULL) { + for (i = 0; i < size; i++) { + temp = gesture_mask[i] ^ mask[i]; + /* enabled mask XOR disabled mask */ + gesture_mask[i] = temp & gesture_mask[i]; + /* temp AND enabled + * disable the gestures that are specified and + * previously enabled */ + } + + pointer = gesture_mask; + } else { + i = 0; /* if NULL is passed disable all the possible + * gestures */ + pointer = (u8 *)&i; + } + + res = setFeatures(FEAT_SEL_GESTURE, pointer, GESTURE_MASK_SIZE); + if (res < OK) { + logError(1, "%s disableGesture: ERROR %08X\n", tag, + res); + goto END; + } + + logError(0, "%s disableGesture DONE!\n", tag); + + res = OK; + +END: + mutex_unlock(&gestureMask_mutex); + return res; + } else { + logError(1, + "%s disableGesture: Size not valid! %d > %d ERROR %08X\n", + tag, + size, GESTURE_MASK_SIZE, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } +} + +/** + * Perform all the steps required to put the chip in gesture mode + * @param reload if set to 1, before entering in gesture mode it will re-enable + * in the FW the last defined gesture mask + * @return OK if success or an error code which specify the type of error + */ +int enterGestureMode(int reload) +{ + int res, ret; + + res = fts_disableInterrupt(); + if (res < OK) { + logError(1, "%s enterGestureMode: ERROR %08X\n", tag, res | + ERROR_DISABLE_INTER); + return res | ERROR_DISABLE_INTER; + } + + if (reload == 1 || refreshGestureMask == 1) { + res = enableGesture(NULL, 0); + if (res < OK) { + logError(1, + "%s enterGestureMode: enableGesture ERROR %08X\n", + tag, + res); + goto END; + } + + refreshGestureMask = 0; + } + + res = setScanMode(SCAN_MODE_LOW_POWER, 0); + if (res < OK) { + logError(1, + "%s enterGestureMode: enter gesture mode ERROR %08X\n", + tag, + res); + goto END; + } + + res = OK; +END: + ret = fts_enableInterrupt(); + if (ret < OK) { + logError(1, + "%s enterGestureMode: fts_enableInterrupt ERROR %08X\n", + tag, + res | ERROR_ENABLE_INTER); + res |= ret | ERROR_ENABLE_INTER; + } + + + return res; +} + +/** + * Check if one or more Gesture IDs are currently enabled in gesture_mask + * @return FEAT_ENABLE if one or more gesture ids are enabled, FEAT_DISABLE if + * all the gesture ids are currently disabled + */ +int isAnyGestureActive(void) +{ + int res = 0; + + while (res < (GESTURE_MASK_SIZE - 1) && gesture_mask[res] == 0) + /* -1 because in any case the last gesture mask byte will + * be evaluated with the following if */ + res++; + + if (gesture_mask[res] != 0) { + logError(0, + "%s %s: Active Gestures Found! gesture_mask[%d] = %02X !\n", + tag, __func__, res, gesture_mask[res]); + return FEAT_ENABLE; + } else { + logError(0, "%s %s: All Gestures Disabled!\n", tag, __func__); + return FEAT_DISABLE; + } +} + + +/** + * Read from the frame buffer the gesture coordinates pairs of the points draw + * by an user when a gesture is detected + * @param event pointer to a byte array which contains the gesture event + * reported + * by the fw when a gesture is detected + * @return OK if success or an error code which specify the type of error + */ +int readGestureCoords(u8 *event) +{ + int i = 0; + u64 address = 0; + int res; + + u8 val[GESTURE_MAX_COORDS_PAIRS_REPORT * 4]; + + /* the max coordinates to read are GESTURE_COORDS_REPORT_MAX*4 + * (because each coordinate is a short(*2) and we have x and y) */ + + + if (event[0] == EVT_ID_USER_REPORT && event[1] == + EVT_TYPE_USER_GESTURE) { + address = (event[4] << 8) | event[3]; /* Offset in framebuff */ + gesture_coords_reported = event[5]; /* number of pairs + * coords reported */ + if (gesture_coords_reported > GESTURE_MAX_COORDS_PAIRS_REPORT) { + logError(1, + "%s %s: FW reported more than %d points for the gestures! Decreasing to %d\n", + tag, __func__, gesture_coords_reported, + GESTURE_MAX_COORDS_PAIRS_REPORT); + gesture_coords_reported = + GESTURE_MAX_COORDS_PAIRS_REPORT; + } + + logError(1, "%s %s: Offset: %08llX , coords pairs = %d\n", tag, + __func__, address, gesture_coords_reported); + + res = fts_writeReadU8UX(FTS_CMD_FRAMEBUFFER_R, BITS_16, address, + val, (gesture_coords_reported * 2 * 2), + DUMMY_FRAMEBUFFER); + /* *2 because each coord is made by 2 bytes, + * *2 because there are x and y */ + if (res < OK) { + logError(1, + "%s %s: Cannot read the coordinates! ERROR %08X\n", + tag, __func__, res); + gesture_coords_reported = ERROR_OP_NOT_ALLOW; + return res; + } + + /* all the points of the gesture are stored in val */ + for (i = 0; i < gesture_coords_reported; i++) { + gesture_coordinates_x[i] = (((u16)val[i * 2 + 1]) & + 0x0F) << 8 | + (((u16)val[i * 2]) & 0xFF); + gesture_coordinates_y[i] = + (((u16)val[gesture_coords_reported * 2 + i * 2 + 1]) & + 0x0F) << 8 | + (((u16)val[gesture_coords_reported * 2 + + i * 2]) & 0xFF); + } + + + logError(1, "%s %s: Reading Gesture Coordinates DONE!\n", tag, + __func__); + return OK; + } else { + logError(1, + "%s %s: The event passsed as argument is invalid! ERROR %08X\n", + tag, __func__, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } +} + +/** + * Return the coordinates of the points stored during the last detected gesture + * @param x output parameter which will store the address of the array + * containing the x coordinates + * @param y output parameter which will store the address of the array + * containing the y coordinates + * @return the number of points (x,y) stored and therefore the size of the x + * and y array returned. + */ +int getGestureCoords(u16 **x, u16 **y) +{ + *x = gesture_coordinates_x; + *y = gesture_coordinates_y; + logError(1, + "%s %s: Number of gesture coordinates pairs returned = %d\n", + tag, + __func__, gesture_coords_reported); + return gesture_coords_reported; +} diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsGesture.h b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsGesture.h new file mode 100644 index 000000000000..cb59737f76a9 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsGesture.h @@ -0,0 +1,47 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Gesture Utilities * + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsGesture.h + * \brief Contains all the macro and prototypes to handle the Gesture Detection + * features + */ + + +#ifndef FTS_GESTURE_H_ +#define FTS_GESTURE_H_ + + + + +#include "ftsHardware.h" + +#define GESTURE_MASK_SIZE 4 /* /< number of bytes of the + * gesture mask */ + +#define GESTURE_MAX_COORDS_PAIRS_REPORT 100 /* /< max number of gestures + * coordinates pairs reported */ + + + +int updateGestureMask(u8 *mask, int size, int en); +int disableGesture(u8 *mask, int size); +int enableGesture(u8 *mask, int size); +int enterGestureMode(int reload); +int isAnyGestureActive(void); +int readGestureCoords(u8 *event); +int getGestureCoords(u16 **x, u16 **y); + +#endif /* ! _GESTURE_H_ */ diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsHardware.h b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsHardware.h new file mode 100644 index 000000000000..c87c6f1b20a1 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsHardware.h @@ -0,0 +1,326 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * HW related data * + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsHardware.h + * \brief Contains all the definitions and information related to the IC + * from an hardware point of view + */ + +#ifndef FTS_HARDWARE_H +#define FTS_HARDWARE_H + + +/* DIGITAL CHIP INFO */ +//#define ALIX +//#define WALNUT +//#define SALIXP + +#ifdef ALIX +#define DCHIP_ID_0 0x36 /* /< LSB chip ID for FTM5 */ +#define DCHIP_ID_1 0x48 /* /< MSB chip ID for FTM5 */ +#elif defined(WALNUT) +#define DCHIP_ID_0 0x36 /* /< LSB chip ID for FTM5 */ +#define DCHIP_ID_1 0x50 /* /< MSB chip ID for FTM5 */ +#elif defined(SALIXP) +#define DCHIP_ID_0 0x36 /* /< LSB chip ID for FTM5 */ +#define DCHIP_ID_1 0x40 /* /< MSB chip ID for FTM5 */ +#else +#define DCHIP_ID_0 0x36 /* /< LSB chip ID for FTM5 */ +#define DCHIP_ID_1 0x39 /* /< MSB chip ID for FTM5 */ +#endif + + +#define DCHIP_FW_VER_BYTE 2 /* /< number of bytes of the fw + * versions */ + +/* CHUNKS */ +#define READ_CHUNK 1024 /* /< chunk dimension of + * a single i2c read, + * max allowed value is 2kB */ +#define WRITE_CHUNK 1024 /* /< chunk dimension of + * a single i2c write, + * max allowed value is 2kB */ +#define MEMORY_CHUNK 1024 /* /< chunk dimenasion of + * a single i2c write on mem, + * max allowed value is 2kB */ + +/* PROTOCOL INFO */ +#define I2C_INTERFACE /*/< comment if the chip use SPI bus */ +#ifdef I2C_INTERFACE +#define I2C_SAD 0x49 /* /< slave address of the IC */ +#else +#define SPI4_WIRE /* /< comment if the master is SPI3 wires + * (MOSI and MISO share same line) */ +#define SPI_DELAY_CS 10 /* /< time in usec to wait + * before rising the CS */ +#define SPI_CLOCK_FREQ 7000000 /* /< clock frequency in Hz of + * the SPI bus */ +#endif + + + +#define IER_ENABLE 0x41 /* /< value to write in IER_ADDR + * to enable the interrupts */ +#define IER_DISABLE 0x00 /* /< value to write in IER_ADDR + * to disable the interrupts */ + +/* FLASH COMMAND */ +/** @defgroup flash_command Flash Commands + * All the commands that works with the flash of the IC + * @{ + */ +#define FLASH_CMD_UNLOCK 0xF7 + +#define FLASH_CMD_READ_REGISTER 0xFA +#define FLASH_CMD_WRITE_REGISTER 0xFA + + +/* FLASH UNLOCK PARAMETER */ +#define FLASH_UNLOCK_CODE0 0x25 +#define FLASH_UNLOCK_CODE1 0x20 +#define FLASH_UNLOCK_CODE2 0x6B +#define FLASH_UNLOCK_CODE3 0x00 + + +/* FLASH UVLO ENABLE PARAMETER */ +#define FLASH_UVLO_ENABLE_CODE0 0x1B +#define FLASH_UVLO_ENABLE_CODE1 0x66 + +/* FLASH AUTOPOWERDOWN ENABLE PARAMETER */ +#define FLASH_AUTOPOWERDOWN_ENABLE_CODE0 0x68 +#define FLASH_AUTOPOWERDOWN_ENABLE_CODE1 0x13 + + + +/* FLASH ERASE and DMA PARAMETER */ +#define FLASH_ERASE_START 0x80 +#define FLASH_ERASE_CODE1 0xC0 +#define FLASH_DMA_CODE1 0xC0 +#define FLASH_ERASE_UNLOCK_CODE0 0xDE +#define FLASH_ERASE_UNLOCK_CODE1 0x03 +#define FLASH_ERASE_CODE0 0x6A +#define FLASH_DMA_CODE0 0x71 +#define FLASH_DMA_CONFIG 0x72 + +#ifdef SALIXP +#define FLASH_NUM_PAGE 48 /* /< number of pages in main + * flash */ +#else +#define FLASH_NUM_PAGE 32 /* /< number of pages in main + * flash */ +#endif + +#define FLASH_CX_PAGE_START 28 /* /< starting page which + * contain Cx data */ +#define FLASH_CX_PAGE_END 30 /* /< last page which contain Cx + * data */ +#define FLASH_PANEL_PAGE_START 26 /* /< starting page which + * contain Panel Init data */ +#define FLASH_PANEL_PAGE_END 27 /* /< last page which contain + * Panel Init data */ + +/** @} */ + +/* FLASH ADDRESS */ + +#define FLASH_ADDR_CODE 0x00000000 /* /< starting address (words) + * in the flash of the code in + * FTI */ +#define FLASH_ADDR_CONFIG 0x00007C00 /* /< starting address (words) + * in the flash of the config in + * FTI */ +#define FLASH_ADDR_CX 0x00007000 /* /< starting address (words) + * in the flash of the Init data + * in FTI */ + + + +/* SIZES FW, CODE, CONFIG, MEMH */ +/** @defgroup fw_file FW file info + * All the info related to the fw file + * @{ + */ + +#define FW_HEADER_SIZE 64 /* /< dimension of the header of the + * .fts file */ +#define FW_HEADER_SIGNATURE 0xAA55AA55 /* /< header signature */ +#define FW_FTB_VER 0x00000001 /* /< .ftb version */ +#define FW_BYTES_ALLIGN 4 /* /< allignment of the info */ +#define FW_BIN_VER_OFFSET 16 /* /< offset of the fw version in the + * .ftb file */ +#define FW_BIN_CONFIG_ID_OFFSET 20 /* /< offset of the config id in the + * .ftb file */ +#define FW_CX_VERSION (16 + 4) /* /< offset of CX version in + * the sec2 of FW file */ + +/** @} */ + + +/* FIFO */ +#define FIFO_EVENT_SIZE 8 /* /< number of bytes of one event */ +#define FIFO_DEPTH 32 /* /< max number of events that the FIFO can + * collect before going in overflow in FTM5 */ + + + +#ifdef I2C_INTERFACE +#define FIFO_CMD_READALL 0x86 /* /< command to read all the events in + * the FIFO */ +#else +#define FIFO_CMD_READALL 0x87 /* /< command to read all the events in + * the FIFO */ +#endif +#define FIFO_CMD_READONE FIFO_CMD_READALL /* /< commad to read one + * event from FIFO */ + + +/* OP CODES FOR MEMORY (based on protocol) */ +#ifdef I2C_INTERFACE +#define FTS_CMD_HW_REG_R 0xFA /* /< command to read an hw register if + * FTI */ +#define FTS_CMD_HW_REG_W 0xFA /* /< command to write an hw register if + * FTI */ +#define FTS_CMD_FRAMEBUFFER_W 0xA6 /* /< command to write the framebuffer if + * FTI */ +#define FTS_CMD_FRAMEBUFFER_R 0xA6 /* /< command to read the framebuffer if + * FTI */ +#define FTS_CMD_CONFIG_R 0xA8 /* /< command to read the config memory + * if FTI */ +#define FTS_CMD_CONFIG_W 0xA8 /* /< command to write the config memory + * if FTI */ +#else +#define FTS_CMD_HW_REG_R 0xFB /* /< command to read an hw register if + * FTI */ +#define FTS_CMD_HW_REG_W 0xFA /* /< command to write an hw register if + * FTI */ +#define FTS_CMD_FRAMEBUFFER_W 0xA6 /* /< command to write the framebuffer if + * FTI */ +#define FTS_CMD_FRAMEBUFFER_R 0xA7 /* /< command to read the framebuffer if + * FTI */ +#define FTS_CMD_CONFIG_R 0xA9 /* /< command to read the config memory + * if FTI */ +#define FTS_CMD_CONFIG_W 0xA8 /* /< command to write the config memory + * if FTI */ +#endif + + +/* DUMMY BYTES DATA */ + +#ifndef I2C_INTERFACE +#define DUMMY_HW_REG 1 /* /< 1 if the first byte read from HW + * register is dummy */ +#define DUMMY_FRAMEBUFFER 1 /* /< 1 if the first byte read from + * Frame buffer is dummy */ +#define DUMMY_CONFIG 1 /* /< 1 if the first byte read from + * Config Memory is dummy */ +#define DUMMY_FIFO 1 /* /< 1 if the first byte read from FIFO + * is dummy */ +#else +#define DUMMY_HW_REG 0 /* /< 1 if the first byte read from HW + * register is dummy */ +#define DUMMY_FRAMEBUFFER 0 /* /< 1 if the first byte read from + * Frame buffer is dummy */ +#define DUMMY_CONFIG 0 /* /< 1 if the first byte read from + * Config Memory is dummy */ +#define DUMMY_FIFO 0 /* /< 1 if the first byte read from FIFO + * is dummy */ +#endif + + +/** @defgroup hw_adr HW Address + * @ingroup address + * Important addresses of hardware registers (and sometimes their important + *values) + * @{ + */ + +/* IMPORTANT HW ADDRESSES (u64) */ +#define ADDR_FRAMEBUFFER ((u64)0x0000000000000000) /* /< frame buffer + * address in memory */ +#define ADDR_ERROR_DUMP ((u64)0x000000000000EF80) /* /< start address + * dump error log */ + +/* SYSTEM RESET INFO */ +#define ADDR_SYSTEM_RESET ((u64)0x0000000020000024) /* /< address of + * System control reg + * in FTI */ +#define SYSTEM_RESET_VALUE 0x81 /* /< value to write in + * SYSTEM_RESET_ADDRESS to perform a + * system reset in FTM5 */ + + +/* REMAP REGISTER */ +#define ADDR_BOOT_OPTION ((u64)0x0000000020000025) /* /< address of Boot + * option register */ + + +/* INTERRUPT INFO */ +#define ADDR_IER ((u64)0x0000000020000029) /* /< address of the + * Interrupt enable + * register in FTMI */ + + +/* Chip ID/Fw Version */ +#define ADDR_DCHIP_ID ((u64)0x0000000020000000) /* /< chip id address + * for FTI */ +#define ADDR_DCHIP_FW_VER ((u64)0x0000000020000004) /* /< fw version + * address for FTI */ + + +/* INTERFACE REGISTER */ +#define ADDR_ICR ((u64)0x000000002000002D) /* /< address of Device + * control register to + * set the comunication + * protocol (SPI/I2C) */ +#define SPI4_MASK 0x02 /* /< bit to set spi4 */ + + +/* CRC ADDR */ +#define ADDR_CRC ((u64)0x0000000020000078) /* /< address of CRC + * control register in + * FTI */ +#define CRC_MASK 0x03 /* /< bitmask which reveal if there is a + * CRC error in the flash */ + + + +#define ADDR_CONFIG_OFFSET ((u64)0x0000000000000000) /* /< config address + * in memory if FTI */ + + + +#define ADDR_GPIO_INPUT ((u64)0x0000000020000030) /* /< address of GPIO + * input register */ +#define ADDR_GPIO_DIRECTION ((u64)0x0000000020000032) /* /< address of GPIO + * direction register */ +#define ADDR_GPIO_PULLUP ((u64)0x0000000020000034) /* /< address of GPIO + * pullup register */ +#define ADDR_GPIO_CONFIG_REG0 ((u64)0x000000002000003D) /* /< address of + * GPIO config register + */ +#define ADDR_GPIO_CONFIG_REG2 ((u64)0x000000002000003F) /* /< address of + * GPIO config register + */ +#define ADDR_GPIO_CONFIG_REG3 ((u64)0x000000002000003E) /* /< address of + * GPIO config register + */ + + + +/**@}*/ + + +#endif diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsIO.c b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsIO.c new file mode 100644 index 000000000000..85bef81c86f8 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsIO.c @@ -0,0 +1,787 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * I2C/SPI Communication * + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsIO.c + * \brief Contains all the functions which handle with the I2C/SPI + *communication + */ + + +#include "ftsSoftware.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef I2C_INTERFACE +#include +#include +static u16 I2CSAD; /* /< slave address of the IC in the i2c bus */ +#else +#include +#endif + +static void *client; /* /< bus client retrived by the OS and + * used to execute the bus transfers */ + + + +#include "ftsCore.h" +#include "ftsError.h" +#include "ftsHardware.h" +#include "ftsIO.h" + + +/** + * Initialize the static client variable of the fts_lib library in order + * to allow any i2c/spi transaction in the driver (Must be called in the probe) + * @param clt pointer to i2c_client or spi_device struct which identify the bus + * slave device + * @return OK + */ +int openChannel(void *clt) +{ + client = clt; +#ifdef I2C_INTERFACE + I2CSAD = ((struct i2c_client *)clt)->addr; + logError(1, "%s openChannel: SAD: %02X\n", tag, I2CSAD); +#else + logError(1, "%s %s: spi_master: flags = %04X !\n", tag, __func__, + ((struct spi_device *)client)->master->flags); + logError(1, + "%s %s: spi_device: max_speed = %d chip select = %02X bits_per_words = %d mode = %04X !\n", + tag, __func__, ((struct spi_device *)client)->max_speed_hz, + ((struct spi_device *)client)->chip_select, + ((struct spi_device *)client)->bits_per_word, + ((struct spi_device *)client)->mode); + logError(1, "%s openChannel: completed!\n", tag); +#endif + return OK; +} + +#ifdef I2C_INTERFACE +/** + * Change the I2C slave address which will be used during the transaction + * (For Debug Only) + * @param sad new slave address id + * @return OK + */ +int changeSAD(u8 sad) +{ + I2CSAD = sad; + return OK; +} +#endif + + +/** + * Retrieve the pointer to the device struct of the IC + * @return a the device struct pointer if client was previously set + * or NULL in all the other cases + */ +struct device *getDev(void) +{ + if (client != NULL) + return &(getClient()->dev); + else + return NULL; +} + + +#ifdef I2C_INTERFACE +/** + * Retrieve the pointer of the i2c_client struct representing the IC as i2c + *slave + * @return client if it was previously set or NULL in all the other cases + */ +struct i2c_client *getClient() +{ + if (client != NULL) + return (struct i2c_client *)client; + else + return NULL; +} +#else +/** + * Retrieve the pointer of the spi_device struct representing the IC as spi + *slave + * @return client if it was previously set or NULL in all the other cases + */ +struct spi_device *getClient() +{ + if (client != NULL) + return (struct spi_device *)client; + else + return NULL; +} +#endif + + + +/****************** New I2C API *********************/ + +/** + * Perform a direct bus read + * @param outBuf pointer of a byte array which should contain the byte read + *from the IC + * @param byteToRead number of bytes to read + * @return OK if success or an error code which specify the type of error + */ +int fts_read(u8 *outBuf, int byteToRead) +{ + int ret = -1; + int retry = 0; + +#ifdef I2C_INTERFACE + struct i2c_msg I2CMsg[1]; + + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)I2C_M_RD; + I2CMsg[0].len = (__u16)byteToRead; + I2CMsg[0].buf = (__u8 *)outBuf; +#else + struct spi_message msg; + struct spi_transfer transfer[1] = { { 0 } }; + + spi_message_init(&msg); + + transfer[0].len = byteToRead; + transfer[0].delay_usecs = SPI_DELAY_CS; + transfer[0].tx_buf = NULL; + transfer[0].rx_buf = outBuf; + spi_message_add_tail(&transfer[0], &msg); +#endif + + + if (client == NULL) + return ERROR_BUS_O; + while (retry < I2C_RETRY && ret < OK) { +#ifdef I2C_INTERFACE + ret = i2c_transfer(getClient()->adapter, I2CMsg, 1); +#else + ret = spi_sync(getClient(), &msg); +#endif + + retry++; + if (ret < OK) + msleep(I2C_WAIT_BEFORE_RETRY); + /* logError(1,"%s fts_writeCmd: attempt %d\n", tag, retry); */ + } + if (ret < 0) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, ERROR_BUS_R); + return ERROR_BUS_R; + } + return OK; +} + + +/** + * Perform a bus write followed by a bus read without a stop condition + * @param cmd byte array containing the command to write + * @param cmdLength size of cmd + * @param outBuf pointer of a byte array which should contain the bytes read + *from the IC + * @param byteToRead number of bytes to read + * @return OK if success or an error code which specify the type of error + */ +int fts_writeRead(u8 *cmd, int cmdLength, u8 *outBuf, int byteToRead) +{ + int ret = -1; + int retry = 0; + +#ifdef I2C_INTERFACE + struct i2c_msg I2CMsg[2]; + + /* write msg */ + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)cmdLength; + I2CMsg[0].buf = (__u8 *)cmd; + + /* read msg */ + I2CMsg[1].addr = (__u16)I2CSAD; + I2CMsg[1].flags = I2C_M_RD; + I2CMsg[1].len = byteToRead; + I2CMsg[1].buf = (__u8 *)outBuf; + +#else + struct spi_message msg; + struct spi_transfer transfer[2] = { { 0 }, { 0 } }; + + spi_message_init(&msg); + + transfer[0].len = cmdLength; + transfer[0].tx_buf = cmd; + transfer[0].rx_buf = NULL; + spi_message_add_tail(&transfer[0], &msg); + + transfer[1].len = byteToRead; + transfer[1].delay_usecs = SPI_DELAY_CS; + transfer[1].tx_buf = NULL; + transfer[1].rx_buf = outBuf; + spi_message_add_tail(&transfer[1], &msg); + +#endif + + if (client == NULL) + return ERROR_BUS_O; + + while (retry < I2C_RETRY && ret < OK) { +#ifdef I2C_INTERFACE + ret = i2c_transfer(getClient()->adapter, I2CMsg, 2); +#else + ret = spi_sync(getClient(), &msg); +#endif + + retry++; + if (ret < OK) + msleep(I2C_WAIT_BEFORE_RETRY); + } + if (ret < 0) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, ERROR_BUS_WR); + return ERROR_BUS_WR; + } + return OK; +} + + +/** + * Perform a bus write + * @param cmd byte array containing the command to write + * @param cmdLength size of cmd + * @return OK if success or an error code which specify the type of error + */ +int fts_write(u8 *cmd, int cmdLength) +{ + int ret = -1; + int retry = 0; + +#ifdef I2C_INTERFACE + struct i2c_msg I2CMsg[1]; + + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)cmdLength; + I2CMsg[0].buf = (__u8 *)cmd; +#else + struct spi_message msg; + struct spi_transfer transfer[1] = { { 0 } }; + + spi_message_init(&msg); + + transfer[0].len = cmdLength; + transfer[0].delay_usecs = SPI_DELAY_CS; + transfer[0].tx_buf = cmd; + transfer[0].rx_buf = NULL; + spi_message_add_tail(&transfer[0], &msg); +#endif + + + if (client == NULL) + return ERROR_BUS_O; + while (retry < I2C_RETRY && ret < OK) { +#ifdef I2C_INTERFACE + ret = i2c_transfer(getClient()->adapter, I2CMsg, 1); +#else + ret = spi_sync(getClient(), &msg); +#endif + + retry++; + if (ret < OK) + msleep(I2C_WAIT_BEFORE_RETRY); + /* logError(1,"%s fts_writeCmd: attempt %d\n", tag, retry); */ + } + if (ret < 0) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, ERROR_BUS_W); + return ERROR_BUS_W; + } + return OK; +} + +/** + * Write a FW command to the IC and check automatically the echo event + * @param cmd byte array containing the command to send + * @param cmdLength size of cmd + * @return OK if success, or an error code which specify the type of error + */ +int fts_writeFwCmd(u8 *cmd, int cmdLength) +{ + int ret = -1; + int ret2 = -1; + int retry = 0; + +#ifdef I2C_INTERFACE + struct i2c_msg I2CMsg[1]; + + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)cmdLength; + I2CMsg[0].buf = (__u8 *)cmd; +#else + struct spi_message msg; + struct spi_transfer transfer[1] = { { 0 } }; + + spi_message_init(&msg); + + transfer[0].len = cmdLength; + transfer[0].delay_usecs = SPI_DELAY_CS; + transfer[0].tx_buf = cmd; + transfer[0].rx_buf = NULL; + spi_message_add_tail(&transfer[0], &msg); +#endif + + if (client == NULL) + return ERROR_BUS_O; + resetErrorList(); + while (retry < I2C_RETRY && (ret < OK || ret2 < OK)) { +#ifdef I2C_INTERFACE + ret = i2c_transfer(getClient()->adapter, I2CMsg, 1); +#else + ret = spi_sync(getClient(), &msg); +#endif + retry++; + if (ret >= 0) + ret2 = checkEcho(cmd, cmdLength); + if (ret < OK || ret2 < OK) + msleep(I2C_WAIT_BEFORE_RETRY); + logError(1,"%s fts_writeCmd: attempt %d\n", tag, retry); + } + if (ret < 0) { + logError(1, "%s fts_writeFwCmd: ERROR %08X\n", tag, + ERROR_BUS_W); + return ERROR_BUS_W; + } + if (ret2 < OK) { + logError(1, "%s fts_writeFwCmd: check echo ERROR %08X\n", tag, + ret2); + return ret2; + } + return OK; +} + + +/** + * Perform two bus write and one bus read without any stop condition + * In case of FTI this function is not supported and the same sequence + * can be achieved calling fts_write followed by an fts_writeRead. + * @param writeCmd1 byte array containing the first command to write + * @param writeCmdLength size of writeCmd1 + * @param readCmd1 byte array containing the second command to write + * @param readCmdLength size of readCmd1 + * @param outBuf pointer of a byte array which should contain the bytes read + * from the IC + * @param byteToRead number of bytes to read + * @return OK if success or an error code which specify the type of error + */ +int fts_writeThenWriteRead(u8 *writeCmd1, int writeCmdLength, u8 *readCmd1, int + readCmdLength, u8 *outBuf, int byteToRead) +{ + int ret = -1; + int retry = 0; + +#ifdef I2C_INTERFACE + struct i2c_msg I2CMsg[3]; + + /* write msg */ + I2CMsg[0].addr = (__u16)I2CSAD; + I2CMsg[0].flags = (__u16)0; + I2CMsg[0].len = (__u16)writeCmdLength; + I2CMsg[0].buf = (__u8 *)writeCmd1; + + /* write msg */ + I2CMsg[1].addr = (__u16)I2CSAD; + I2CMsg[1].flags = (__u16)0; + I2CMsg[1].len = (__u16)readCmdLength; + I2CMsg[1].buf = (__u8 *)readCmd1; + + /* read msg */ + I2CMsg[2].addr = (__u16)I2CSAD; + I2CMsg[2].flags = I2C_M_RD; + I2CMsg[2].len = byteToRead; + I2CMsg[2].buf = (__u8 *)outBuf; +#else + struct spi_message msg; + struct spi_transfer transfer[3] = { { 0 }, { 0 }, { 0 } }; + + spi_message_init(&msg); + + transfer[0].len = writeCmdLength; + transfer[0].tx_buf = writeCmd1; + transfer[0].rx_buf = NULL; + spi_message_add_tail(&transfer[0], &msg); + + transfer[1].len = readCmdLength; + transfer[1].tx_buf = readCmd1; + transfer[1].rx_buf = NULL; + spi_message_add_tail(&transfer[1], &msg); + + transfer[2].len = byteToRead; + transfer[2].delay_usecs = SPI_DELAY_CS; + transfer[2].tx_buf = NULL; + transfer[2].rx_buf = outBuf; + spi_message_add_tail(&transfer[2], &msg); +#endif + + if (client == NULL) + return ERROR_BUS_O; + while (retry < I2C_RETRY && ret < OK) { +#ifdef I2C_INTERFACE + ret = i2c_transfer(getClient()->adapter, I2CMsg, 3); +#else + ret = spi_sync(getClient(), &msg); +#endif + retry++; + if (ret < OK) + msleep(I2C_WAIT_BEFORE_RETRY); + } + + if (ret < 0) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, ERROR_BUS_WR); + return ERROR_BUS_WR; + } + return OK; +} + + + +/** + * Perform a chunked write with one byte op code and 1 to 8 bytes address + * @param cmd byte containing the op code to write + * @param addrSize address size in byte + * @param address the starting address + * @param data pointer of a byte array which contain the bytes to write + * @param dataSize size of data + * @return OK if success or an error code which specify the type of error + */ +/* this function works only if the address is max 8 bytes */ +int fts_writeU8UX(u8 cmd, AddrSize addrSize, u64 address, u8 *data, int + dataSize) +{ + u8 *finalCmd; + int remaining = dataSize; + int toWrite = 0, i = 0; + + finalCmd = (u8 *)kzalloc(1 + addrSize + WRITE_CHUNK, GFP_KERNEL); + if (!finalCmd) + return ERROR_ALLOC; + + if (addrSize <= sizeof(u64)) { + while (remaining > 0) { + if (remaining >= WRITE_CHUNK) { + toWrite = WRITE_CHUNK; + remaining -= WRITE_CHUNK; + } else { + toWrite = remaining; + remaining = 0; + } + + finalCmd[0] = cmd; + logError(0, "%s %s: addrSize = %d\n", tag, __func__, + addrSize); + for (i = 0; i < addrSize; i++) { + finalCmd[i + 1] = (u8)((address >> ((addrSize - + 1 - i) * + 8)) & 0xFF); + logError(1, "%s %s: cmd[%d] = %02X\n", tag, + __func__, i + 1, finalCmd[i + 1]); + } + + memcpy(&finalCmd[addrSize + 1], data, toWrite); + + if (fts_write(finalCmd, 1 + addrSize + toWrite) < OK) { + logError(1, "%s %s: ERROR %08X\n", tag, + __func__, ERROR_BUS_W); + kfree(finalCmd); + return ERROR_BUS_W; + } + + address += toWrite; + + data += toWrite; + } + } else + logError(1, + "%s %s: address size bigger than max allowed %ld... ERROR %08X\n", + tag, __func__, sizeof(u64), ERROR_OP_NOT_ALLOW); + kfree(finalCmd); + return OK; +} + +/** + * Perform a chunked write read with one byte op code and 1 to 8 bytes address + * and dummy byte support. + * @param cmd byte containing the op code to write + * @param addrSize address size in byte + * @param address the starting address + * @param outBuf pointer of a byte array which contain the bytes to read + * @param byteToRead number of bytes to read + * @param hasDummyByte if the first byte of each reading is dummy (must be + * skipped) + * set to 1, otherwise if it is valid set to 0 (or any other value) + * @return OK if success or an error code which specify the type of error + */ +int fts_writeReadU8UX(u8 cmd, AddrSize addrSize, u64 address, u8 *outBuf, int + byteToRead, int hasDummyByte) +{ + u8 *finalCmd; + u8 buff[READ_CHUNK + 1];/* worst case has dummy byte */ + int remaining = byteToRead; + int toRead = 0, i = 0; + + finalCmd = (u8 *)kzalloc(1 + addrSize, GFP_KERNEL); + if (!finalCmd) + return ERROR_ALLOC; + + while (remaining > 0) { + if (remaining >= READ_CHUNK) { + toRead = READ_CHUNK; + remaining -= READ_CHUNK; + } else { + toRead = remaining; + remaining = 0; + } + + finalCmd[0] = cmd; + for (i = 0; i < addrSize; i++) + finalCmd[i + 1] = (u8)((address >> ((addrSize - 1 - i) * + 8)) & 0xFF); + + if (hasDummyByte == 1) { + if (fts_writeRead(finalCmd, 1 + addrSize, buff, toRead + + 1) < OK) { + logError(1, + "%s %s: read error... ERROR %08X\n", + tag, + __func__, ERROR_BUS_WR); + kfree(finalCmd); + return ERROR_BUS_WR; + } + memcpy(outBuf, buff + 1, toRead); + } else { + if (fts_writeRead(finalCmd, 1 + addrSize, buff, + toRead) < OK) { + logError(1, + "%s %s: read error... ERROR %08X\n", + tag, + __func__, ERROR_BUS_WR); + kfree(finalCmd); + return ERROR_BUS_WR; + } + memcpy(outBuf, buff, toRead); + } + + address += toRead; + + outBuf += toRead; + } + kfree(finalCmd); + return OK; +} + +/** + * Perform a chunked write followed by a second write with one byte op code + * for each write and 1 to 8 bytes address (the sum of the 2 address size of + * the two writes can not exceed 8 bytes) + * @param cmd1 byte containing the op code of first write + * @param addrSize1 address size in byte of first write + * @param cmd2 byte containing the op code of second write + * @param addrSize2 address size in byte of second write + * @param address the starting address + * @param data pointer of a byte array which contain the bytes to write + * @param dataSize size of data + * @return OK if success or an error code which specify the type of error + */ +/* this function works only if the sum of two addresses in the two commands is + * max 8 bytes */ +int fts_writeU8UXthenWriteU8UX(u8 cmd1, AddrSize addrSize1, u8 cmd2, AddrSize + addrSize2, u64 address, u8 *data, int dataSize) +{ + u8 *finalCmd1; + u8 *finalCmd2; + int remaining = dataSize; + int toWrite = 0, i = 0; + + finalCmd1 = (u8 *)kzalloc(1 + addrSize1, GFP_KERNEL); + if (!finalCmd1) + return ERROR_ALLOC; + + finalCmd2 = (u8 *)kzalloc(1 + addrSize2 + WRITE_CHUNK, GFP_KERNEL); + if (!finalCmd2) { + kfree(finalCmd1); + return ERROR_ALLOC; + } + + while (remaining > 0) { + if (remaining >= WRITE_CHUNK) { + toWrite = WRITE_CHUNK; + remaining -= WRITE_CHUNK; + } else { + toWrite = remaining; + remaining = 0; + } + + finalCmd1[0] = cmd1; + for (i = 0; i < addrSize1; i++) + finalCmd1[i + 1] = (u8)((address >> ((addrSize1 + + addrSize2 - 1 - + i) * 8)) & 0xFF); + + finalCmd2[0] = cmd2; + for (i = addrSize1; i < addrSize1 + addrSize2; i++) + finalCmd2[i - addrSize1 + 1] = (u8)((address >> + ((addrSize1 + + addrSize2 - 1 - + i) * 8)) & 0xFF); + + memcpy(&finalCmd2[addrSize2 + 1], data, toWrite); + + if (fts_write(finalCmd1, 1 + addrSize1) < OK) { + logError(1, "%s %s: first write error... ERROR %08X\n", + tag, __func__, ERROR_BUS_W); + kfree(finalCmd1); + kfree(finalCmd2); + return ERROR_BUS_W; + } + + if (fts_write(finalCmd2, 1 + addrSize2 + toWrite) < OK) { + logError(1, + "%s %s: second write error... ERROR %08X\n", + tag, + __func__, ERROR_BUS_W); + kfree(finalCmd1); + kfree(finalCmd2); + return ERROR_BUS_W; + } + + address += toWrite; + + data += toWrite; + } + kfree(finalCmd1); + kfree(finalCmd2); + return OK; +} + +/** + * Perform a chunked write followed by a write read with one byte op code + * and 1 to 8 bytes address for each write and dummy byte support. + * @param cmd1 byte containing the op code of first write + * @param addrSize1 address size in byte of first write + * @param cmd2 byte containing the op code of second write read + * @param addrSize2 address size in byte of second write read + * @param address the starting address + * @param outBuf pointer of a byte array which contain the bytes to read + * @param byteToRead number of bytes to read + * @param hasDummyByte if the first byte of each reading is dummy (must be + * skipped) set to 1, + * otherwise if it is valid set to 0 (or any other value) + * @return OK if success or an error code which specify the type of error + */ +/* this function works only if the sum of two addresses in the two commands is + * max 8 bytes */ +int fts_writeU8UXthenWriteReadU8UX(u8 cmd1, AddrSize addrSize1, u8 cmd2, + AddrSize addrSize2, u64 address, u8 *outBuf, + int byteToRead, int hasDummyByte) +{ + u8 *finalCmd1; + u8 *finalCmd2; + u8 buff[READ_CHUNK + 1];/* worst case has dummy byte */ + int remaining = byteToRead; + int toRead = 0, i = 0; + + finalCmd1 = (u8 *)kzalloc(1 + addrSize1, GFP_KERNEL); + if (!finalCmd1) + return ERROR_ALLOC; + + finalCmd2 = (u8 *)kzalloc(1 + addrSize2, GFP_KERNEL); + if (!finalCmd2) { + kfree(finalCmd1); + return ERROR_ALLOC; + } + + while (remaining > 0) { + if (remaining >= READ_CHUNK) { + toRead = READ_CHUNK; + remaining -= READ_CHUNK; + } else { + toRead = remaining; + remaining = 0; + } + + + finalCmd1[0] = cmd1; + for (i = 0; i < addrSize1; i++) + finalCmd1[i + 1] = (u8)((address >> ((addrSize1 + + addrSize2 - 1 - + i) * 8)) & 0xFF); + /* logError(1, "%s %s: finalCmd1[%d] = %02X\n", + * tag, __func__, i+1, finalCmd1[i + 1]); */ + + finalCmd2[0] = cmd2; + for (i = addrSize1; i < addrSize1 + addrSize2; i++) + finalCmd2[i - addrSize1 + 1] = (u8)((address >> + ((addrSize1 + + addrSize2 - 1 - + i) * 8)) & 0xFF); + + if (fts_write(finalCmd1, 1 + addrSize1) < OK) { + logError(1, "%s %s: first write error... ERROR %08X\n", + tag, __func__, ERROR_BUS_W); + kfree(finalCmd1); + kfree(finalCmd2); + return ERROR_BUS_W; + } + + if (hasDummyByte == 1) { + if (fts_writeRead(finalCmd2, 1 + addrSize2, buff, + toRead + 1) < OK) { + logError(1, + "%s %s: read error... ERROR %08X\n", + tag, + __func__, ERROR_BUS_WR); + kfree(finalCmd1); + kfree(finalCmd2); + return ERROR_BUS_WR; + } + memcpy(outBuf, buff + 1, toRead); + } else { + if (fts_writeRead(finalCmd2, 1 + addrSize2, buff, + toRead) < OK) { + logError(1, + "%s %s: read error... ERROR %08X\n", + tag, + __func__, ERROR_BUS_WR); + kfree(finalCmd1); + kfree(finalCmd2); + return ERROR_BUS_WR; + } + memcpy(outBuf, buff, toRead); + } + + address += toRead; + + outBuf += toRead; + } + kfree(finalCmd1); + kfree(finalCmd2); + return OK; +} diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsIO.h b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsIO.h new file mode 100644 index 000000000000..4dd2b6c67649 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsIO.h @@ -0,0 +1,66 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * I2C/SPI Communication * + * * + ************************************************************************** + ************************************************************************** + * + */ +/*! + * \file ftsIO.h + * \brief Contains all the definitions and prototypes used and implemented in + * ftsIO.c + */ + +#ifndef FTS_IO_H +#define FTS_IO_H + +#include "ftsSoftware.h" + +#define I2C_RETRY 3 /* /< number of retry in case of i2c + * failure */ +#define I2C_WAIT_BEFORE_RETRY 2 /* /< wait in ms before retry an i2c + * transaction */ + +#ifdef I2C_INTERFACE +#include +#include +struct i2c_client *getClient(void); +#else +#include +struct spi_device *getClient(void); +#endif + + + +int openChannel(void *clt); +struct device *getDev(void); + + + +/*************** NEW I2C API ****************/ +#ifdef I2C_INTERFACE +int changeSAD(u8 sad); +#endif +int fts_read(u8 *outBuf, int byteToRead); +int fts_writeRead(u8 *cmd, int cmdLength, u8 *outBuf, int byteToRead); +int fts_write(u8 *cmd, int cmdLength); +int fts_writeFwCmd(u8 *cmd, int cmdLenght); +int fts_writeThenWriteRead(u8 *writeCmd1, int writeCmdLength, u8 *readCmd1, int + readCmdLength, u8 *outBuf, int byteToRead); +int fts_writeU8UX(u8 cmd, AddrSize addrSize, u64 address, u8 *data, int + dataSize); +int fts_writeReadU8UX(u8 cmd, AddrSize addrSize, u64 address, u8 *outBuf, int + byteToRead, int hasDummyByte); +int fts_writeU8UXthenWriteU8UX(u8 cmd1, AddrSize addrSize1, u8 cmd2, AddrSize + addrSize2, u64 address, u8 *data, int dataSize); +int fts_writeU8UXthenWriteReadU8UX(u8 cmd1, AddrSize addrSize1, u8 cmd2, + AddrSize addrSize2, u64 address, u8 *outBuf, + int count, int hasDummyByte); +#endif diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsSoftware.h b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsSoftware.h new file mode 100644 index 000000000000..dc0fa30b2443 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsSoftware.h @@ -0,0 +1,514 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FW related data * + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsSoftware.h + * \brief Contains all the definitions and information related to the IC from a + *fw/driver point of view + */ + + +#ifndef FTS_SOFTWARE_H +#define FTS_SOFTWARE_H +#include +#include "ftsHardware.h" + + + +/* unsigned types */ +#define u8 uint8_t /* /< basic type that represent one byte (or 8 bits) */ +#define u16 uint16_t /* /< basic type that represent 2 bytes (or 16 bits) */ +#define u32 uint32_t /* /< basic type that represent 4 bytes (or 32 bits) */ +#define u64 uint64_t /* /< basic type that represent 8 bytes (or 64 bits) */ + +/* signed type */ +typedef signed char i8; /* /< basic type that represent one signed byte (or 8 + * bits) */ + +/** + * Enumerator which contains all the possible address length expressed in + *bytes. + */ +typedef enum { + NO_ADDR = 0, + BITS_8 = 1, + BITS_16 = 2, + BITS_24 = 3, + BITS_32 = 4, + BITS_40 = 5, + BITS_48 = 6, + BITS_56 = 7, + BITS_64 = 8 +} AddrSize; + + +/******************** NEW API *********************/ + +/* HOST COMMAND */ +/** @defgroup host_command Fw Host op codes + * Valid op codes for fw commands + * @{ + */ + +/** @defgroup scan_mode Scan Mode + * @ingroup host_command + * Set the scanning mode required according to the parameters + * @{ + */ +#define FTS_CMD_SCAN_MODE 0xA0 /* /< OP Code to set scan mode */ +/** @} */ + +/** @defgroup feat_sel Feature Select + * @ingroup host_command + * Set the system defined features to enable/disable according the parameters + * @{ + */ +#define FTS_CMD_FEATURE 0xA2 /* /< OP code to set features */ +/** @} */ + +/** @defgroup sys_cmd System Command + * @ingroup host_command + * Execute a system command to perform core tasks + * @{ + */ +#define FTS_CMD_SYSTEM 0xA4 /* /< OP code to write s system command + * */ +/** @} */ + +/** @} */ /* end host_command group */ + +/* SCAN MODE OPTION (0xA0) */ +/* Scan mode selection */ +/** @defgroup scan_opt Scan Mode Option + * @ingroup scan_mode + * Valid scanning modes and their options + * @{ + */ +#define SCAN_MODE_ACTIVE 0x00 /* /< Select the Active scanning mode */ +#define SCAN_MODE_LOW_POWER 0x01 /* /< Select the low power scanning mode + * */ +#define SCAN_MODE_JIG_1 0x02 /* /< Select the Jig test 1 */ +#define SCAN_MODE_LOCKED 0x03 /* /< Select the Scan mode which will be + * locked */ +/** @}*/ + +/* Active mode option (bitmask) */ +/** @defgroup active_bitmask Active Mode Bitmask + * @ingroup scan_opt + * Bitmask to use to enables the specific scanning with the SCAN_MODE_ACTIVE + *option + * @{ + */ +#define ACTIVE_MULTI_TOUCH 0x01 /* /< Bit 0 MS/SS scan */ +#define ACTIVE_KEY 0x02 /* /< Bit 1 Key scan */ +#define ACTIVE_HOVER 0x04 /* /< Bit 2 Hover scan */ +#define ACTIVE_PROXIMITY 0x08 /* /< Bit 3 Proximity scan */ +#define ACTIVE_FORCE 0x10 /* /< Bit 4 Force scan */ +/** @}*/ + + +/* Locked mode option (locked mode) */ +/** @defgroup locked_opt Locked Mode Option + * @ingroup scan_opt + * Options to enable and lock specific scanning with the SCAN_MODE_LOCKED + *option + * @{ + */ +#define LOCKED_ACTIVE 0x00 /* /< Active Scan Mode */ +#define LOCKED_HOVER 0x01 /* /< Hover Scan Mode */ +#define LOCKED_IDLE 0x02 /* /< Idle Scan Mode */ +#define LOCKED_LP_DETECT 0x10 /* /< Low Power SS */ +#define LOCKED_LP_ACTIVE 0x11 /* /< Low Power MS */ +/** @}*/ + +/* FEATURE SELECT OPTION (0xA2) */ +/* Feature Selection */ +/** @defgroup feat_opt Feature Selection Option + * @ingroup feat_sel + * System defined features that can be enable/disable + * @{ + */ +#define FEAT_SEL_GLOVE 0x00 /* /< Glove Mode */ +#define FEAT_SEL_COVER 0x01 /* /< Cover Mode */ +#define FEAT_SEL_CHARGER 0x02 /* /< Charger Mode */ +#define FEAT_SEL_GESTURE 0x03 /* /< Gesture Mode */ +#define FEAT_SEL_GRIP 0x04 /* /< Grip Detection */ +#define FEAT_SEL_STYLUS 0x07 /* /< Stylus Mode + * (this is a driver define, not + * available in FW) */ +/** @}*/ + +/* Feature Settings */ +#define FEAT_ENABLE 1 /* /< General value to enable a feature + * */ +#define FEAT_DISABLE 0 /* /< General value to disable a feature + * */ + + +/* Charger */ +/** @defgroup charger_opt Charger Mode Option + * @ingroup feat_sel + * Option for Charger Mode, it is a bitmask where the each bit indicate a + *different kind of chager + * @{ + */ +#define CHARGER_CABLE 0x01 /* /< normal usb charger */ +#define CHARGER_WIRLESS 0x02 /* /< wireless charger */ +/** @}*/ + +/* Gestures */ +/** @defgroup gesture_opt Gesture Mode Option + * @ingroup feat_sel + * Gesture IDs of the predefined gesture recognized by the fw. + * The ID represent also the position of the corresponding bit in the gesture + *mask + * @{ + */ +#define GEST_ID_UP_1F 0x01 /* /< Bottom to Top line */ +#define GEST_ID_DOWN_1F 0x02 /* /< Top to bottom line */ +#define GEST_ID_LEFT_1F 0x03 /* /< Right to left line */ +#define GEST_ID_RIGHT_1F 0x04 /* /< Left to right line */ +#define GEST_ID_DBLTAP 0x05 /* /< Double Tap */ +#define GEST_ID_O 0x06 /* /< 'O' */ +#define GEST_ID_C 0x07 /* /< 'C' */ +#define GEST_ID_M 0x08 /* /< 'M' */ +#define GEST_ID_W 0x09 /* /< 'W' */ +#define GEST_ID_E 0x0A /* /< 'e' */ +#define GEST_ID_L 0x0B /* /< 'L' */ +#define GEST_ID_F 0x0C /* /< 'F' */ +#define GEST_ID_V 0x0D /* /< 'V' */ +#define GEST_ID_AT 0x0E /* /< '@' */ +#define GEST_ID_S 0x0F /* /< 'S' */ +#define GEST_ID_Z 0x10 /* /< 'Z' */ +#define GEST_ID_LEFTBRACE 0x11 /* /< '<' */ +#define GEST_ID_RIGHTBRACE 0x12 /* /< '>' */ +#define GEST_ID_CARET 0x13 /* /< '^' */ +#define GEST_ID_SIGTAP 0x19 /* /< Single Tap */ +/** @}*/ + +/* WRYTE SYSTEM COMMAND (0xA4) */ +/* System command */ +/** @defgroup sys_opt System Command Option + * @ingroup sys_cmd + * Valid System Command Parameters + * @{ + */ +#define SYS_CMD_SPECIAL 0x00 /* /< Special Commands */ +#define SYS_CMD_INT 0x01 /* /< FW Interrupt Control */ +#define SYS_CMD_FORCE_CAL 0x02 /* /< Force Calibration */ +#define SYS_CMD_CX_TUNING 0x03 /* /< CX initialization */ +#define SYS_CMD_ITO 0x04 /* /< ITO test */ +#define SYS_CMD_SAVE_FLASH 0x05 /* /< Saving to flash */ +#define SYS_CMD_LOAD_DATA 0x06 /* /< Load Host data memory */ +#define SYS_CMD_SPECIAL_TUNING 0x08 /* /< Perform some special tuning */ +#define SYS_CMD_MP_FLAG 0x0C /* /< Update value of MP flag in RAM */ +/** @} */ + +/* System command settings */ +/* Special commands */ +/** @defgroup sys_special_opt Special Command Option + * @ingroup sys_cmd + * Valid special command + * @{ + */ +#define SPECIAL_SYS_RESET 0x00 /* /< System Reset triggered by the FW */ +#define SPECIAL_FIFO_FLUSH 0x01 /* /< Flush of the FIFO */ +#define SPECIAL_PANEL_INIT 0x02 /* /< Panel Initialization */ +#define SPECIAL_FULL_PANEL_INIT 0x03 /* /< Full panel initialization */ +/** @} */ + + +/* Force Cal and Cx auto tuning */ +/** @defgroup forcecal_cx_opt Force Cal and Tuning Option + * @ingroup sys_cmd + * Valid bitmask for triggering forcecal or performing manual autotune + * @{ + */ +#define CAL_MS_TOUCH 0x01 /* /< Mutual Sense Touch */ +#define CAL_MS_LOW_POWER 0x02 /* /< Mutual Sense Touch in low power + * mode */ +#define CAL_SS_TOUCH 0x04 /* /< Self Sense Touch */ +#define CAL_SS_IDLE 0x08 /* /< Self Sense Touch in idle mode */ +#define CAL_MS_KEY 0x10 /* /< Mutual Sense Key */ +#define CAL_SS_KEY 0x20 /* /< Self Sense Key */ +#define CAL_MS_FORCE 0x40 /* /< Mutual Sense Force */ +#define CAL_SS_FORCE 0x80 /* /< Self Sense Force */ +/** @} */ + +/* ITO checks (position of the bit in the mask) */ +/** @defgroup ito_opt ITO Test Option + * @ingroup sys_cmd + * Valid option for the ITO test + * @{ + */ +#define ITO_FORCE_OPEN 0x00 /* /< Check if some force channels is + * open */ +#define ITO_SENSE_OPEN 0x01 /* /< Check if some sense channels is + * open */ +#define ITO_FORCE_GROUND 0x02 /* /< Check if some force channels is + * short to ground */ +#define ITO_SENSE_GROUND 0x03 /* /< Check if some sense channels is + * short to ground */ +#define ITO_FORCE_VDD 0x04 /* /< Check if some force channels is + * short to VDD */ +#define ITO_SENSE_VDD 0x05 /* /< Check if some sense channels is + * short to VDD */ +#define ITO_FORCE_FORCE 0x06 /* /< Check force to force channels */ +#define ITO_FORCE_SENSE 0x07 /* /< Check force to sense channels */ +#define ITO_SENSE_SENSE 0x08 /* /< Check sense to sense channels */ +#define ITO_KEY_FORCE_OPEN 0x09 /* /< Check if some force channels used + * for the key is open */ +#define ITO_KEY_SENSE_OPEN 0x0A /* /< Check if some sense channels used + * for the key is open */ +/** @}*/ + +/* Save flash */ +/** @defgroup save_opt Save to Flash Option + * @ingroup sys_cmd + * Valid option for saving data to the Flash + * @{ + */ +#define SAVE_FW_CONF 0x01 /* /< Save the confing to the flash */ +#define SAVE_CX 0x02 /* /< Save the CX to the flash */ +#define SAVE_PANEL_CONF 0x04 /* /< Save the Panel configuration to the flash + * */ +/** @}*/ + +/* Load Data */ +/** @defgroup load_opt Load Host Data Option + * @ingroup sys_cmd + * Valid option to ask to the FW to load host data into the memory + * @{ + */ +#define LOAD_SYS_INFO 0x01 /* /< Load System Info */ +#define LOAD_CX_MS_TOUCH 0x10 /* /< Load MS Init Data for + * Active Mode */ +#define LOAD_CX_MS_LOW_POWER 0x11 /* /< Load MS Init Data for Low + * Power Mode */ +#define LOAD_CX_SS_TOUCH 0x12 /* /< Load SS Init Data for + * Active Mode */ +#define LOAD_CX_SS_TOUCH_IDLE 0x13 /* /< Load SS Init Data for Low + * Power Mode */ +#define LOAD_CX_MS_KEY 0x14 /* /< Load MS Init Data for Key + * */ +#define LOAD_CX_SS_KEY 0x15 /* /< Load SS Init Data for Key + * */ +#define LOAD_CX_MS_FORCE 0x16 /* /< Load MS Init Data for + * Force */ +#define LOAD_CX_SS_FORCE 0x17 /* /< Load SS Init Data for + * Force */ +#define LOAD_SYNC_FRAME_RAW 0x30 /* /< Load a Synchronized Raw + * Frame */ +#define LOAD_SYNC_FRAME_FILTER 0x31 /* /< Load a Synchronized Filter + * Frame */ +#define LOAD_SYNC_FRAME_STRENGTH 0x33 /* /< Load a Synchronized + * Strength Frame */ +#define LOAD_SYNC_FRAME_BASELINE 0x32 /* /< Load a Synchronized + * Baseline Frame */ +#define LOAD_PANEL_CX_TOT_MS_TOUCH 0x50 /* /< Load TOT MS Init Data for + * Active Mode */ +#define LOAD_PANEL_CX_TOT_MS_LOW_POWER 0x51 /* /< Load TOT MS Init Data for + * Low Power Mode */ +#define LOAD_PANEL_CX_TOT_SS_TOUCH 0x52 /* /< Load TOT SS Init Data for + * Active Mode */ +#define LOAD_PANEL_CX_TOT_SS_TOUCH_IDLE 0x53 /* /< Load TOT SS Init Data for + * Low Power Mode */ +#define LOAD_PANEL_CX_TOT_MS_KEY 0x54 /* /< Load TOT MS Init Data for + * Key */ +#define LOAD_PANEL_CX_TOT_SS_KEY 0x55 /* /< Load TOT SS Init Data for + * Key */ +#define LOAD_PANEL_CX_TOT_MS_FORCE 0x56 /* /< Load TOT MS Init Data for + * Force */ +#define LOAD_PANEL_CX_TOT_SS_FORCE 0x57 /* /< Load TOT SS Init Data for + * Force */ +/** @}*/ + +/* Special Tuning */ +/** @defgroup spcl_tun_opt Special Tuning Option + * @ingroup sys_cmd + * Valid special tuning operations which the fw can perform (bitmask) + * @{ + */ +#define SPECIAL_TUNING_LP_TIMER 0x01 /* /< Perform LP Timer calibration */ +#define SPECIAL_TUNING_IOFF 0x02 /* /< Perform Ioff calibration */ + +/** @}*/ + +/* EVENT ID */ +/** @defgroup events_group FW Event IDs and Types + * Event IDs and Types pushed by the FW into the FIFO + * @{ + */ +#define EVT_ID_NOEVENT 0x00 /* /< No Events */ +#define EVT_ID_CONTROLLER_READY 0x03 /* /< Controller ready, issued after a + system reset. */ +#define EVT_ID_ENTER_POINT 0x13 /* /< Touch enter in the sensing area */ +#define EVT_ID_MOTION_POINT 0x23 /* /< Touch motion (a specific touch + * changed position) */ +#define EVT_ID_LEAVE_POINT 0x33 /* /< Touch leave the sensing area */ +#define EVT_ID_STATUS_UPDATE 0x43 /* /< FW report a system condition + * change */ +#define EVT_ID_USER_REPORT 0x53 /* /< User related events triggered + * (keys, gestures, proximity etc) */ +#define EVT_ID_DEBUG 0xE3 /* /< Debug Info */ +#define EVT_ID_ERROR 0xF3 /* /< Error Event */ + +/* /< Max number of unique event IDs supported */ +#define NUM_EVT_ID (((EVT_ID_ERROR & 0xF0) >> 4)+1) + +/** @}*/ + +/* STATUS TYPE */ +/** @defgroup status_type Status Event Types + * @ingroup events_group + * Types of EVT_ID_STATUS_UPDATE events + * @{ + */ +#define EVT_TYPE_STATUS_ECHO 0x01 /* /< Echo event, contain the + * first 5 bytes of the FW + * command sent */ +#define EVT_TYPE_STATUS_FRAME_DROP 0x03 /* /< Some frame was skipped + * during the elaboration */ +#define EVT_TYPE_STATUS_FORCE_CAL 0x05 /* /< Force Calibration has + * triggered */ +#define EVT_TYPE_STATUS_WATER 0x06 /* /< Water Mode */ +#define EVT_TYPE_STATUS_SS_RAW_SAT 0x07 /* /< Self Sense data saturated + * */ +/** @} */ + +/* USER TYPE */ +/** @defgroup user_type User Event Types + * @ingroup events_group + * Types of EVT_ID_USER_REPORT events generated by the FW + * @{ + */ +#define EVT_TYPE_USER_KEY 0x00 /* /< Keys pressed/relesed event report + * */ +#define EVT_TYPE_USER_PROXIMITY 0x01 /* /< Proximity detection event report + * */ +#define EVT_TYPE_USER_GESTURE 0x02 /* /< Gesture detection event report */ +/** @}*/ + +/* ERROR TYPE */ +/** @defgroup error_type Error Event Types + * @ingroup events_group + * Types of EVT_ID_ERROR events reported by the FW + * @{ + */ +#define EVT_TYPE_ERROR_HARD_FAULT 0x02 /* /< Hard Fault */ +#define EVT_TYPE_ERROR_WATCHDOG 0x06 /* /< Watchdog timer expired */ + +#define EVT_TYPE_ERROR_CRC_CFG_HEAD 0x20 /* /< CRC error in the Config + * Area Header */ +#define EVT_TYPE_ERROR_CRC_CFG 0x21 /* /< CRC error in the Config + * Area */ +#define EVT_TYPE_ERROR_CRC_PANEL_HEAD 0x22 /* /< CRC error in the Panel + * Area Header */ +#define EVT_TYPE_ERROR_CRC_PANEL 0x23 /* /< CRC error in the Panel + * Area */ + +#define EVT_TYPE_ERROR_ITO_FORCETOGND 0x60 /* /< Force channel/s short to + * ground */ +#define EVT_TYPE_ERROR_ITO_SENSETOGND 0x61 /* /< Sense channel/s short to + * ground */ +#define EVT_TYPE_ERROR_ITO_FORCETOVDD 0x62 /* /< Force channel/s short to + * VDD */ +#define EVT_TYPE_ERROR_ITO_SENSETOVDD 0x63 /* /< Sense channel/s short to + * VDD */ +#define EVT_TYPE_ERROR_ITO_FORCE_P2P 0x64 /* /< Pin to Pin short Force + * channel/s */ +#define EVT_TYPE_ERROR_ITO_SENSE_P2P 0x65 /* /< Pin to Pin short Sense + * channel/s */ +#define EVT_TYPE_ERROR_ITO_FORCEOPEN 0x66 /* /< Force Panel open */ +#define EVT_TYPE_ERROR_ITO_SENSEOPEN 0x67 /* /< Sense Panel open */ +#define EVT_TYPE_ERROR_ITO_KEYOPEN 0x68 /* /< Key open */ + +#define EVT_TYPE_ERROR_CRC_CX_HEAD 0xA0 /* /< CRC error in the CX Area + * Header */ +#define EVT_TYPE_ERROR_CRC_CX 0xA1 /* /< CRC error in the CX Area + * */ +#define EVT_TYPE_ERROR_CRC_CX_SUB_HEAD 0xA5 /* /< CRC error in the CX + * Subsection Area Header */ +#define EVT_TYPE_ERROR_CRC_CX_SUB 0xA6 /* /< CRC error in the CX + * Subsection Area */ +#define EVT_TYPE_ERROR_CAL_NOISE 0xE0 + +#define EVT_TYPE_ERROR_ESD 0xF0 /* /< ESD error */ +/** @}*/ + + +/** @defgroup address Chip Address + * Collection of HW and SW Addresses useful to collect different kind of data + * @{ + */ + +/** @defgroup config_adr SW Address + * @ingroup address + * Important addresses of data stored into Config memory (and sometimes their + *dimensions) + * @{ + */ +#define ADDR_CONFIG_ID 0x0010 /* /< Starting Address of the config ID + * */ +#define CONFIG_ID_BYTE 2 /* /< Number of bytes of config ID */ +#define ADDR_CONFIG_SENSE_LEN 0x0030 /* /< Address where is stored the number + * of sense channels */ +#define ADDR_CONFIG_FORCE_LEN 0x0031 /* /< Address where is stored the number + * of force channels */ +/** @}*/ + +/** @}*/ + +/** @defgroup mp_flags MP Flags value + * @ingroup mp_test + * Specify the MP flags value which are written into the flash after performing + * a full panel initialization which pass all the tests. + * @{ + */ + +#define MP_FLAG_FACTORY 0xA5 /* /< Full Panel Init done in factory */ +#define MP_FLAG_BOOT 0x5A /* /< Full Panel Init done at boot */ +#define MP_FLAG_OTHERS 0xFF /* /< Full Panel Init done somewhere + * else */ + +/** @}*/ + +/* ERROR INFO */ +#define ERROR_DUMP_ROW_SIZE 32 /* /< number of rows of the error memory + * */ +#define ERROR_DUMP_COL_SIZE 4 /* /< number of bytes for each row of + * the error memory */ +#define ERROR_DUMP_SIGNATURE 0xFA5005AF /* /< first row signature of a + * proper dump */ + +/* Touch Types */ +#define TOUCH_TYPE_INVALID 0x00 /* /< Invalid touch type */ +#define TOUCH_TYPE_FINGER 0x01 /* /< Finger touch */ +#define TOUCH_TYPE_GLOVE 0x02 /* /< Glove touch */ +#define TOUCH_TYPE_STYLUS 0x03 /* /< Stylus touch */ +#define TOUCH_TYPE_PALM 0x04 /* /< Palm touch */ +#define TOUCH_TYPE_HOVER 0x05 /* /< Hovering touch */ + +/* Keys code */ +#define FTS_KEY_0 0x01 /* /< Key 0 bit */ +#define FTS_KEY_1 0x02 /* /< Key 1 bit */ +#define FTS_KEY_2 0x04 /* /< Key 2 bit */ +#define FTS_KEY_3 0x08 /* /< Key 3 bit */ +#define FTS_KEY_4 0x10 /* /< Key 4 bit */ +#define FTS_KEY_5 0x20 /* /< Key 5 bit */ +#define FTS_KEY_6 0x40 /* /< Key 6 bit */ +#define FTS_KEY_7 0x80 /* /< Key 7 bit */ + +#endif diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsTest.c b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsTest.c new file mode 100644 index 000000000000..55d6593fbf68 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsTest.c @@ -0,0 +1,7730 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for MP test * + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsTest.c + * \brief Contains all the functions related to the Mass Production Test + */ + +#include "ftsCompensation.h" +#include "ftsCore.h" +#include "ftsError.h" +#include "ftsFrame.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTest.h" +#include "ftsTime.h" +#include "ftsTool.h" +#include "../fts.h" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifdef LIMITS_H_FILE +#include "../fts_limits.h" +#endif + + +TestToDo tests; /* /< global variable that specify the tests to perform during + * the Mass Production Test */ +static LimitFile limit_file; /* /< variable which contains the limit file + * during test */ + +/** + * Initialize the testToDo variable with the default tests to perform during + * the Mass Production Test + * @return OK + */ +int initTestToDo(void) +{ + /*** Initialize Limit File ***/ + limit_file.size = 0; + limit_file.data = NULL; + strlcpy(limit_file.name, " ", MAX_LIMIT_FILE_NAME); + tests.SelfHoverForceRaw = 0; /* /< SS Hover Force Raw min/Max test */ + tests.SelfHoverSenceRaw = 0; /* /< SS Hover Sence Raw min/Max test */ + tests.SelfHoverForceIxTotal = 0; /* /< SS Hover Total Force Ix min/Max (for each node)* test */ + tests.SelfHoverSenceIxTotal = 0; +#ifndef COMPUTE_INIT_METHOD + tests.MutualRawAdjITO = 1; + + tests.MutualRaw = 1; + tests.MutualRawMap = 1; + tests.MutualRawGap = 0; + tests.MutualRawAdj = 0; + tests.MutualRawAdjGap = 0; + tests.MutualRawAdjPeak = 0; + + tests.MutualRawLP = 0; + tests.MutualRawGapLP = 0; + tests.MutualRawMapLP = 0; + tests.MutualRawAdjLP = 0; + + tests.MutualCx1 = 0; + tests.MutualCx2 = 0; + tests.MutualCx2Adj = 0; + tests.MutualCxTotal = 0; + tests.MutualCxTotalAdj = 0; + + tests.MutualCx1LP = 0; + tests.MutualCx2LP = 1; + tests.MutualCx2TotalLP = 1; + tests.MutualCxTotalLP = 0; + tests.MutualCxTotalAdjLP = 0; + +#ifdef PHONE_KEY + tests.MutualKeyRaw = 1; +#else + tests.MutualKeyRaw = 0; +#endif + tests.MutualKeyCx1 = 0; + tests.MutualKeyCx2 = 0; +#ifdef PHONE_KEY + tests.MutualKeyCxTotal = 1; +#else + tests.MutualKeyCxTotal = 0; +#endif + + tests.SelfForceRaw = 1; + tests.SelfForceRawGap = 0; + tests.SelfForceRawMap = 1; + + tests.SelfForceRawLP = 0; + tests.SelfForceRawGapLP = 0; + tests.SelfForceRawMapLP = 0; + + tests.SelfForceIx1 = 0; + tests.SelfForceIx2 = 0; + tests.SelfForceIx2Adj = 0; + tests.SelfForceIxTotal = 1; + tests.SelfForceIxTotalAdj = 0; + tests.SelfForceCx1 = 0; + tests.SelfForceCx2 = 0; + tests.SelfForceCx2Adj = 0; + tests.SelfForceCxTotal = 0; + tests.SelfForceCxTotalAdj = 0; + tests.SelfForceIx1LP = 0; + tests.SelfForceIx2LP = 0; + tests.SelfForceIx2AdjLP = 0; + tests.SelfForceIxTotalLP = 0; + tests.SelfForceIxTotalAdjLP = 0; + tests.SelfForceCx1LP = 0; + tests.SelfForceCx2LP = 0; + tests.SelfForceCx2AdjLP = 0; + tests.SelfForceCxTotalLP = 0; + tests.SelfForceCxTotalAdjLP = 0; + + tests.SelfSenseRaw = 1; + tests.SelfSenseRawGap = 0; + + tests.SelfSenseRawMap = 1; + tests.SelfSenseRawLP = 0; + tests.SelfSenseRawGapLP = 0; + tests.SelfSenseRawMapLP = 0; + + tests.SelfSenseIx1 = 0; + tests.SelfSenseIx2 = 0; + tests.SelfSenseIx2Adj = 0; + tests.SelfSenseIxTotal = 1; + tests.SelfSenseIxTotalAdj = 0; + tests.SelfSenseCx1 = 0; + tests.SelfSenseCx2 = 0; + tests.SelfSenseCx2Adj = 0; + tests.SelfSenseCxTotal = 0; + tests.SelfSenseCxTotalAdj = 0; + tests.SelfSenseIx1LP = 0; + tests.SelfSenseIx2LP = 0; + tests.SelfSenseIx2AdjLP = 0; + tests.SelfSenseIxTotalLP = 0; + tests.SelfSenseIxTotalAdjLP = 0; + tests.SelfSenseCx1LP = 0; + tests.SelfSenseCx2LP = 0; + tests.SelfSenseCx2AdjLP = 0; + tests.SelfSenseCxTotalLP = 0; + tests.SelfSenseCxTotalAdjLP = 0; +#else + tests.MutualRawAdjITO = 1; + + tests.MutualRaw = 1; /* in case of YOCTA please use Map */ + tests.MutualRawMap = 0; + tests.MutualRawGap = 0; + tests.MutualRawAdj = 0; + tests.MutualRawAdjGap = 0; + tests.MutualRawAdjPeak = 0; + + tests.MutualRawLP = 0; /* in case of YOCTA please use Map */ + tests.MutualRawGapLP = 0; + tests.MutualRawMapLP = 0; + tests.MutualRawAdjLP = 0; + + tests.MutualCx1 = 0; + tests.MutualCx2 = 0; + tests.MutualCx2Adj = 0; + tests.MutualCxTotal = 0; + tests.MutualCxTotalAdj = 0; + + tests.MutualCx1LP = 0; + tests.MutualCx2LP = 1; + tests.MutualCx2AdjLP = 1; + tests.MutualCxTotalLP = 0; + tests.MutualCxTotalAdjLP = 0; + +#ifdef PHONE_KEY + tests.MutualKeyRaw = 0; +#else + tests.MutualKeyRaw = 0; +#endif + tests.MutualKeyCx1 = 0; + tests.MutualKeyCx2 = 0; +#ifdef PHONE_KEY + tests.MutualKeyCxTotal = 0; +#else + tests.MutualKeyCxTotal = 0; +#endif + + tests.SelfForceRaw = 1; /* in case of YOCTA please use Map */ + tests.SelfForceRawGap = 0; + tests.SelfForceRawMap = 0; + + tests.SelfForceRawLP = 0; /* in case of YOCTA please use Map */ + tests.SelfForceRawGapLP = 0; + tests.SelfForceRawMapLP = 0; + + tests.SelfForceIx1 = 0; + tests.SelfForceIx2 = 0; + tests.SelfForceIx2Adj = 0; + tests.SelfForceIxTotal = 1; + tests.SelfForceIxTotalAdj = 0; + tests.SelfForceCx1 = 0; + tests.SelfForceCx2 = 0; + tests.SelfForceCx2Adj = 0; + tests.SelfForceCxTotal = 0; + tests.SelfForceCxTotalAdj = 0; + tests.SelfForceIx1LP = 0; + tests.SelfForceIx2LP = 0; + tests.SelfForceIx2AdjLP = 0; + tests.SelfForceIxTotalLP = 0; + tests.SelfForceIxTotalAdjLP = 0; + tests.SelfForceCx1LP = 0; + tests.SelfForceCx2LP = 0; + tests.SelfForceCx2AdjLP = 0; + tests.SelfForceCxTotalLP = 0; + tests.SelfForceCxTotalAdjLP = 0; + + tests.SelfSenseRaw = 1; + tests.SelfSenseRawGap = 0; + tests.SelfSenseRawMap = 0; + + tests.SelfSenseRawLP = 1; + tests.SelfSenseRawGapLP = 0; + tests.SelfSenseRawMapLP = 0; + + tests.SelfSenseIx1 = 0; + tests.SelfSenseIx2 = 0; + tests.SelfSenseIx2Adj = 0; + tests.SelfSenseIxTotal = 1; + tests.SelfSenseIxTotalAdj = 0; + tests.SelfSenseCx1 = 0; + tests.SelfSenseCx2 = 0; + tests.SelfSenseCx2Adj = 0; + tests.SelfSenseCxTotal = 0; + tests.SelfSenseCxTotalAdj = 0; + tests.SelfSenseIx1LP = 0; + tests.SelfSenseIx2LP = 0; + tests.SelfSenseIx2AdjLP = 0; + tests.SelfSenseIxTotalLP = 1; + tests.SelfSenseIxTotalAdjLP = 0; + tests.SelfSenseCx1LP = 0; + tests.SelfSenseCx2LP = 0; + tests.SelfSenseCx2AdjLP = 0; + tests.SelfSenseCxTotalLP = 0; + tests.SelfSenseCxTotalAdjLP = 0; +#endif + + return OK; +} + +/** + * Compute the Horizontal adjacent matrix doing the abs of the difference + * between the column i with the i-1 one. \n + * Both the original data matrix and the adj matrix are disposed as 1 dimension + * array one row after the other \n + * The resulting matrix has one column less than the starting original one \n + * @param data pointer to the array of signed bytes containing the original + * data + * @param row number of rows of the original data + * @param column number of columns of the original data + * @param result pointer of a pointer to an array of unsigned bytes which will + * contain the adj matrix + * @return OK if success or an error code which specify the type of error + */ +int computeAdjHoriz(i8 *data, int row, int column, u8 **result) +{ + int i, j; + int size = row * (column - 1); + + if (column < 2) { + logError(1, "%s computeAdjHoriz: ERROR %08X\n", tag, + ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u8 *)kmalloc(size * sizeof(u8), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s computeAdjHoriz: ERROR %08X\n", tag, + ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 0; i < row; i++) + for (j = 1; j < column; j++) + *(*result + (i * (column - 1) + (j - 1))) = + abs(data[i * column + j] - data[i * column + + (j - 1)]); + + return OK; +} + +/** + * Compute the Horizontal adjacent matrix of short values doing the abs of + * the difference between the column i with the i-1 one. + * Both the original data matrix and the adj matrix are disposed as 1 dimension + * array one row after the other \n + * The resulting matrix has one column less than the starting original one \n + * @param data pointer to the array of signed bytes containing the original + * data + * @param row number of rows of the original data + * @param column number of columns of the original data + * @param result pointer of a pointer to an array of unsigned bytes which + * will contain the adj matrix + * @return OK if success or an error code which specify the type of error + */ +int computeAdjHorizTotal(short *data, int row, int column, u16 **result) +{ + int i, j; + int size = row * (column - 1); + + if (column < 2) { + logError(1, "%s computeAdjHorizTotal: ERROR %08X\n", tag, + ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u16 *)kmalloc(size * sizeof(u16), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s computeAdjHorizTotal: ERROR %08X\n", tag, + ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 0; i < row; i++) + for (j = 1; j < column; j++) + *(*result + (i * (column - 1) + (j - 1))) = + abs(data[i * column + j] - data[i * column + + (j - 1)]); + + return OK; +} + +/** + * Compute the Vertical adjacent matrix doing the abs of the difference between + * the row i with the i-1 one. + * Both the original data matrix and the adj matrix are disposed as 1 dimension + * array one row after the other. \n + * The resulting matrix has one column less than the starting original one \n + * @param data pointer to the array of signed bytes containing the original + * data + * @param row number of rows of the original data + * @param column number of columns of the original data + * @param result pointer of a pointer to an array of unsigned bytes which will + * contain the adj matrix + * @return OK if success or an error code which specify the type of error + */ +int computeAdjVert(i8 *data, int row, int column, u8 **result) +{ + int i, j; + int size = (row - 1) * (column); + + if (row < 2) { + logError(1, "%s computeAdjVert: ERROR %08X\n", tag, + ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u8 *)kmalloc(size * sizeof(u8), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s computeAdjVert: ERROR %08X\n", tag, + ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 1; i < row; i++) + for (j = 0; j < column; j++) + *(*result + ((i - 1) * column + j)) = + abs(data[i * column + j] - data[(i - 1) * + column + j]); + + return OK; +} + +/** + * Compute the Vertical adjacent matrix of short values doing the abs of + * the difference between the row i with the i-1 one. + * Both the original data matrix and the adj matrix are disposed as 1 dimension + * array one row after the other. \n + * The resulting matrix has one column less than the starting original one \n + * @param data pointer to the array of signed bytes containing the original + * data + * @param row number of rows of the original data + * @param column number of columns of the original data + * @param result pointer of a pointer to an array of unsigned bytes which will + * contain the adj matrix + * @return OK if success or an error code which specify the type of error + */ +int computeAdjVertTotal(short *data, int row, int column, u16 **result) +{ + int i, j; + int size = (row - 1) * (column); + + if (row < 2) { + logError(1, "%s computeAdjVertTotal: ERROR %08X\n", tag, + ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u16 *)kmalloc(size * sizeof(u16), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s computeAdjVertTotal: ERROR %08X\n", tag, + ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 1; i < row; i++) + for (j = 0; j < column; j++) + *(*result + ((i - 1) * column + j)) = + abs(data[i * column + j] - data[(i - 1) * + column + j]); + + return OK; +} + +/** + * Compute the Horizontal adjacent matrix doing the abs of the difference + * between the column i with the i-1 one. \n + * Both the original data matrix and the adj matrix are disposed as 1 dimension + * array one row after the other \n + * The resulting matrix has one column less than the starting original one \n + * @param data pointer to the array of unsigned bytes containing the original + * data + * @param row number of rows of the original data + * @param column number of columns of the original data + * @param result pointer of a pointer to an array of unsigned bytes which will + * contain the adj matrix + * @return OK if success or an error code which specify the type of error + */ +int computeAdjHorizFromU(u8 *data, int row, int column, u8 **result) +{ + int i, j; + int size = row * (column - 1); + + if (column < 2) { + logError(1, "%s computeAdjHoriz: ERROR %08X\n", tag, + ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u8 *)kmalloc(size * sizeof(u8), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s computeAdjHoriz: ERROR %08X\n", tag, + ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 0; i < row; i++) + for (j = 1; j < column; j++) + *(*result + (i * (column - 1) + (j - 1))) = + abs(data[i * column + j] - data[i * column + + (j - 1)]); + + return OK; +} + +/** + * Compute the Horizontal adjacent matrix of u16 values doing the abs of + * the difference between the column i with the i-1 one. + * Both the original data matrix and the adj matrix are disposed as 1 dimension + * array one row after the other \n + * The resulting matrix has one column less than the starting original one \n + * @param data pointer to the array of unsigned bytes containing the original + * data + * @param row number of rows of the original data + * @param column number of columns of the original data + * @param result pointer of a pointer to an array of unsigned bytes which will + * contain the adj matrix + * @return OK if success or an error code which specify the type of error + */ +int computeAdjHorizTotalFromU(u16 *data, int row, int column, u16 **result) +{ + int i, j; + int size = row * (column - 1); + + if (column < 2) { + logError(1, "%s computeAdjHorizTotal: ERROR %08X\n", tag, + ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u16 *)kmalloc(size * sizeof(u16), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s computeAdjHorizTotal: ERROR %08X\n", tag, + ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 0; i < row; i++) + for (j = 1; j < column; j++) + *(*result + (i * (column - 1) + (j - 1))) = + abs(data[i * column + j] - data[i * column + + (j - 1)]); + + return OK; +} + +/** + * Compute the Vertical adjacent matrix doing the abs of the difference between + * the row i with the i-1 one. + * Both the original data matrix and the adj matrix are disposed as 1 dimension + * array one row after the other. \n + * The resulting matrix has one column less than the starting original one \n + * @param data pointer to the array of unsigned bytes containing the original + * data + * @param row number of rows of the original data + * @param column number of columns of the original data + * @param result pointer of a pointer to an array of unsigned bytes which will + * contain the adj matrix + * @return OK if success or an error code which specify the type of error + */ +int computeAdjVertFromU(u8 *data, int row, int column, u8 **result) +{ + int i, j; + int size = (row - 1) * (column); + + if (row < 2) { + logError(1, "%s computeAdjVert: ERROR %08X\n", tag, + ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u8 *)kmalloc(size * sizeof(u8), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s computeAdjVert: ERROR %08X\n", tag, + ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 1; i < row; i++) + for (j = 0; j < column; j++) + *(*result + ((i - 1) * column + j)) = + abs(data[i * column + j] - data[(i - 1) * + column + j]); + + return OK; +} + +/** + * Compute the Vertical adjacent matrix of u16 values doing the abs of + * the difference between the row i with the i-1 one. + * Both the original data matrix and the adj matrix are disposed as 1 dimension + * array one row after the other. \n + * The resulting matrix has one column less than the starting original one \n + * @param data pointer to the array of unsigned bytes containing the original + * data + * @param row number of rows of the original data + * @param column number of columns of the original data + * @param result pointer of a pointer to an array of unsigned bytes which will + * contain the adj matrix + * @return OK if success or an error code which specify the type of error + */ +int computeAdjVertTotalFromU(u16 *data, int row, int column, u16 **result) +{ + int i, j; + int size = (row - 1) * (column); + + if (row < 2) { + logError(1, "%s computeAdjVertTotal: ERROR %08X\n", tag, + ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + *result = (u16 *)kmalloc(size * sizeof(u16), GFP_KERNEL); + if (*result == NULL) { + logError(1, "%s computeAdjVertTotal: ERROR %08X\n", tag, + ERROR_ALLOC); + return ERROR_ALLOC; + } + + for (i = 1; i < row; i++) + for (j = 0; j < column; j++) + *(*result + ((i - 1) * column + j)) = + abs(data[i * column + j] - data[(i - 1) * + column + j]); + + return OK; +} + + + +/** + * Check that each value of a matrix of short doesn't exceed a min and a Max + * value (these values are included in the interval). \n + * The matrix is stored as 1 dimension array one row after the other. \n + * @param data pointer to the array of short containing the data to check + * @param row number of rows of data + * @param column number of columns of data + * @param min minimum value allowed + * @param max Maximum value allowed + * @return the number of elements that overcome the specified interval (0 = OK) + */ +int checkLimitsMinMax(short *data, int row, int column, int min, int max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min || data[i * column + j] > + max) { + logError(1, + "%s checkLimitsMinMax: Node[%d,%d] = %d exceed limit [%d, %d]\n", + tag, i, j, data[i * column + j], min, + max); + count++; + } + } + } + + return count; /* if count is 0 = OK, test completed successfully */ +} + +/** + * Check that the difference between the max and min of a matrix of short is + * less or equal to a threshold.\n + * The matrix is stored as 1 dimension array one row after the other. + * @param data pointer to the array of short containing the data to check + * @param row number of rows of data + * @param column number of columns of data + * @param threshold threshold value allowed + * @return OK if the difference is <= to threshold otherwise + * ERROR_TEST_CHECK_FAIL + */ +int checkLimitsGap(short *data, int row, int column, int threshold) +{ + int i, j; + int min_node; + int max_node; + + if (row == 0 || column == 0) { + logError(1, + "%s checkLimitsGap: invalid number of rows = %d or columns = %d ERROR %08X\n", + tag, row, column, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + min_node = data[0]; + max_node = data[0]; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min_node) + min_node = data[i * column + j]; + else if (data[i * column + j] > max_node) + max_node = data[i * column + j]; + } + } + + if (max_node - min_node > threshold) { + logError(1, "%s checkLimitsGap: GAP = %d exceed limit %d\n", + tag, max_node - min_node, threshold); + return ERROR_TEST_CHECK_FAIL; + } else + return OK; +} + +/** + * Check that the difference between the max and min of a matrix of short is + * less or equal to a threshold.\n + * The matrix is stored as 1 dimension array one row after the other. + * @param data pointer to the array of short containing the data to check + * @param row number of rows of data + * @param column number of columns of data + * @param threshold threshold value allowed + * @param row_start index of the starting column which should be considered + * @param column_start index of the starting column which should be considered + * @param row_end number of index to subtract to row to identify last + * valid row to check + * @param column_end number of index to subtract to column to identify last + * valid column to check + * @return OK if the difference is <= to threshold otherwise + * ERROR_TEST_CHECK_FAIL + */ +int checkLimitsGapOffsets(short *data, int row, int column, int threshold, + int row_start, int column_start, int row_end, int column_end) +{ + int i, j; + int min_node; + int max_node; + + if (row == 0 || column == 0) { + logError(1, + "%s checkLimitsGap: invalid number of rows = %d or columns = %d ERROR %08X\n", + tag, row, column, ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } + + min_node = data[row_start*column+column_start]; + max_node = data[row_start*column+column_start]; + + for (i = row_start; i < row-row_end; i++) { + for (j = column_start; j < column-column_end; j++) { + if (data[i * column + j] < min_node) + min_node = data[i * column + j]; + else if (data[i * column + j] > max_node) + max_node = data[i * column + j]; + } + } + + if (max_node - min_node > threshold) { + logError(1, "%s checkLimitsGap: GAP = %d exceed limit %d\n", + tag, max_node - min_node, threshold); + return ERROR_TEST_CHECK_FAIL; + } else + return OK; +} + +/** + * Check that each value of a matrix of i8 doesn't exceed a specific min and + *Max value set for each node (these values are included in the interval). \n + * The matrixes of data, min and max values are stored as 1 dimension arrays + *one row after the other. + * @param data pointer to the array of short containing the data to check + * @param row number of rows of data + * @param column number of columns of data + * @param min pointer to a matrix which specify the minimum value allowed for + *each node + * @param max pointer to a matrix which specify the Maximum value allowed for + *each node + * @return the number of elements that overcome the specified interval (0 = OK) + */ +int checkLimitsMap(i8 *data, int row, int column, int *min, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min[i * column + j] || + data[i * column + j] > max[i * column + j]) { + logError(1, + "%s checkLimitsMap: Node[%d,%d] = %d exceed limit [%d, %d]\n", + tag, i, j, data[i * column + j], + min[i * + column + + j], max[i * column + j]); + count++; + } + } + } + + return count; /* if count is 0 = OK, test completed successfully */ +} + +/** + * Check that each value of a matrix of short doesn't exceed a specific min and + * Max value set for each node (these values are included in the interval). + * The matrixes of data, min and max values are stored as 1 dimension arrays + * one row after the other. + * @param data pointer to the array of short containing the data to check + * @param row number of rows of data + * @param column number of columns of data + * @param min pointer to a matrix which specify the minimum value allowed for + * each node + * @param max pointer to a matrix which specify the Maximum value allowed for + * each node + * @return the number of elements that overcome the specified interval (0 = OK) + */ +int checkLimitsMapTotal(short *data, int row, int column, int *min, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min[i * column + j] || + data[i * column + j] > max[i * column + j]) { + logError(1, + "%s checkLimitsMapTotal: Node[%d,%d] = %d exceed limit [%d, %d]\n", + tag, i, j, data[i * column + j], + min[i * + column + + j], max[i * column + j]); + count++; + } + } + } + + return count; /* if count is 0 = OK, test completed successfully */ +} + +/** + * Check that each value of a matrix of u8 doesn't exceed a specific min and + * Max value set for each node (these values are included in the interval). \n + * The matrixes of data, min and max values are stored as 1 dimension arrays + * one row after the other. + * @param data pointer to the array of short containing the data to check + * @param row number of rows of data + * @param column number of columns of data + * @param min pointer to a matrix which specify the minimum value allowed for + * each node + * @param max pointer to a matrix which specify the Maximum value allowed for + * each node + * @return the number of elements that overcome the specified interval (0 = OK) + */ +int checkLimitsMapFromU(u8 *data, int row, int column, int *min, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min[i * column + j] || + data[i * column + j] > max[i * column + j]) { + logError(1, + "%s checkLimitsMap: Node[%d,%d] = %d exceed limit [%d, %d]\n", + tag, i, j, data[i * column + j], + min[i * + column + + j], max[i * column + j]); + count++; + } + } + } + + return count; /* if count is 0 = OK, test completed successfully */ +} + +/** + * Check that each value of a matrix of u16 doesn't exceed a specific min and + * Max value set for each node (these values are included in the interval). + * The matrixes of data, min and max values are stored as 1 dimension arrays + * one row after the other. + * @param data pointer to the array of short containing the data to check + * @param row number of rows of data + * @param column number of columns of data + * @param min pointer to a matrix which specify the minimum value allowed for + * each node + * @param max pointer to a matrix which specify the Maximum value allowed for + * each node + * @return the number of elements that overcome the specified interval (0 = OK) + */ +int checkLimitsMapTotalFromU(u16 *data, int row, int column, int *min, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] < min[i * column + j] || + data[i * column + j] > max[i * column + j]) { + logError(1, + "%s checkLimitsMapTotal: Node[%d,%d] = %d exceed limit [%d, %d]\n", + tag, i, j, data[i * column + j], + min[i * + column + + j], max[i * column + j]); + count++; + } + } + } + + return count; /* if count is 0 = OK, test completed successfully */ +} + +/** + * Check that each value of a matrix of u8 doesn't exceed a specific Max value + * set for each node (max value is included in the interval). + * The matrixes of data and max values are stored as 1 dimension arrays one row + * after the other. + * @param data pointer to the array of short containing the data to check + * @param row number of rows of data + * @param column number of columns of data + * @param max pointer to a matrix which specify the Maximum value allowed for + *each node + * @return the number of elements that overcome the specified interval (0 = OK) + */ +int checkLimitsMapAdj(u8 *data, int row, int column, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] > max[i * column + j]) { + logError(1, + "%s checkLimitsMapAdj: Node[%d,%d] = %d exceed limit > %d\n", + tag, i, j, data[i * column + j], + max[i * + column + + j]); + count++; + } + } + } + + return count; /* if count is 0 = OK, test completed successfully */ +} + +/** + * Check that each value of a matrix of u16 doesn't exceed a specific Max value + * set for each node (max value is included in the interval). + * The matrixes of data and max values are stored as 1 dimension arrays one row + * after the other. + * @param data pointer to the array of short containing the data to check + * @param row number of rows of data + * @param column number of columns of data + * @param max pointer to a matrix which specify the Maximum value allowed for + * each node + * @return the number of elements that overcome the specified interval (0 = OK) + */ +int checkLimitsMapAdjTotal(u16 *data, int row, int column, int *max) +{ + int i, j; + int count = 0; + + for (i = 0; i < row; i++) { + for (j = 0; j < column; j++) { + if (data[i * column + j] > max[i * column + j]) { + logError(1, + "%s checkLimitsMapAdjTotal: Node[%d,%d] = %d exceed limit > %d\n", + tag, i, j, data[i * column + j], + max[i * + column + + j]); + count++; + } + } + } + + return count; /* if count is 0 = OK, test completed successfully */ +} + +/** + * Perform an ITO test setting all the possible options + * (see @link ito_opt ITO Options @endlink) and checking MS Raw ADJ if enabled + * @param path_limits name of Production Limit file to load or + * "NULL" if the limits data should be loaded by a .h file + * @param todo pointer to a TestToDo variable which select the test to do + * @return OK if success or an error code which specify the type of error + */ +int production_test_ito(char *path_limits, TestToDo *todo) +{ + int res = OK; + u8 sett[2] = { 0x00, 0x00 }; + MutualSenseFrame msRawFrame; + int *thresholds = NULL; + u16 *adj = NULL; + int trows, tcolumns; + + logError(0, "%s ITO Production test is starting...\n", tag); + + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s %s: ERROR %08X\n", tag, __func__, + ERROR_PROD_TEST_ITO); + return res | ERROR_PROD_TEST_ITO; + } + + sett[0] = SPECIAL_TUNING_IOFF; + logError(0, "%s Trimming Ioff...\n", tag); + res = writeSysCmd(SYS_CMD_SPECIAL_TUNING, sett, 2); + if (res < OK) { + logError(1, "%s production_test_ito: Trimm Ioff ERROR %08X\n", + tag, (res | ERROR_PROD_TEST_ITO)); + return res | ERROR_PROD_TEST_ITO; + } + + sett[0] = 0xFF; + sett[1] = 0x01; + logError(0, "%s ITO Check command sent...\n", tag); + res = writeSysCmd(SYS_CMD_ITO, sett, 2); + if (res < OK) { + logError(1, "%s production_test_ito: ERROR %08X\n", tag, + (res | ERROR_PROD_TEST_ITO)); + return res | ERROR_PROD_TEST_ITO; + } + + logError(0, "%s ITO Command = OK!\n", tag); + + logError(0, "%s MS RAW ITO ADJ TEST:\n", tag); + if (todo->MutualRawAdjITO == 1) { + logError(0, "%s Collecting MS Raw data...\n", tag); + res |= getMSFrame3(MS_RAW, &msRawFrame); + if (res < OK) { + logError(1, "%s %s: getMSFrame failed... ERROR %08X\n", + tag, __func__, ERROR_PROD_TEST_ITO); + goto ERROR; + } + logError(0, "%s MS RAW ITO ADJ HORIZONTAL TEST:\n", tag); + res = computeAdjHorizTotal(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + &adj); + if (res < OK) { + logError(1, + "%s %s: computeAdjHoriz failed... ERROR %08X\n", + tag, + __func__, ERROR_PROD_TEST_ITO); + goto ERROR; + } + + res = parseProductionTestLimits(path_limits, &limit_file, + MS_RAW_ITO_ADJH, &thresholds, + &trows, &tcolumns); + if (res < OK || (trows != msRawFrame.header.force_node || + tcolumns != msRawFrame.header.sense_node - + 1)) { + logError(1, + "%s %s: parseProductionTestLimits MS_RAW_ITO_ADJH failed... ERROR %08X\n", + tag, __func__, ERROR_PROD_TEST_DATA); + goto ERROR; + } + + + res = checkLimitsMapAdjTotal(adj, msRawFrame.header.force_node, + msRawFrame.header.sense_node - 1, + thresholds); + if (res != OK) { + logError(1, + "%s production_test_data: checkLimitsAdj MS RAW ITO ADJH failed... ERROR COUNT = %d\n", + tag, res); + logError(0, + "%s MS RAW ITO ADJ HORIZONTAL TEST:.................FAIL\n\n", + tag); + print_frame_short("MS Raw ITO frame =", + array1dTo2d_short( + msRawFrame.node_data, + msRawFrame. + node_data_size, + msRawFrame.header. + sense_node), + msRawFrame.header.force_node, + msRawFrame.header.sense_node); + res = ERROR_PROD_TEST_ITO; + goto ERROR; + } else + logError(0, + "%s MS RAW ITO ADJ HORIZONTAL TEST:.................OK\n", + tag); + + kfree(thresholds); + thresholds = NULL; + + kfree(adj); + adj = NULL; + + logError(0, "%s MS RAW ITO ADJ VERTICAL TEST:\n", tag); + res = computeAdjVertTotal(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + &adj); + if (res < OK) { + logError(1, + "%s %s: computeAdjVert failed... ERROR %08X\n", + tag, + __func__, ERROR_PROD_TEST_ITO); + goto ERROR; + } + + res = parseProductionTestLimits(path_limits, &limit_file, + MS_RAW_ITO_ADJV, &thresholds, + &trows, &tcolumns); + if (res < OK || (trows != msRawFrame.header.force_node - 1 || + tcolumns != msRawFrame.header.sense_node)) { + logError(1, + "%s %s: parseProductionTestLimits MS_RAW_ITO_ADJV failed... ERROR %08X\n", + tag, __func__, ERROR_PROD_TEST_ITO); + goto ERROR; + } + + + res = checkLimitsMapAdjTotal(adj, msRawFrame.header.force_node - + 1, msRawFrame.header.sense_node, + thresholds); + if (res != OK) { + logError(1, + "%s %s: checkLimitsAdj MS RAW ITO ADJV failed... ERROR COUNT = %d\n", + tag, __func__, res); + logError(0, + "%s MS RAW ITO ADJ VERTICAL TEST:.................FAIL\n\n", + tag); + print_frame_short("MS Raw ITO frame =", + array1dTo2d_short( + msRawFrame.node_data, + msRawFrame. + node_data_size, + msRawFrame.header. + sense_node), + msRawFrame.header.force_node, + msRawFrame.header.sense_node); + res = ERROR_PROD_TEST_ITO; + goto ERROR; + } else + logError(0, + "%s MS RAW ITO ADJ VERTICAL TEST:.................OK\n", + tag); + + kfree(thresholds); + thresholds = NULL; + + kfree(adj); + adj = NULL; + } else + logError(0, + "%s MS RAW ITO ADJ TEST:.................SKIPPED\n", + tag); + +ERROR: + if (thresholds != NULL) + kfree(thresholds); + if (adj != NULL) + kfree(adj); + if (msRawFrame.node_data != NULL) + kfree(msRawFrame.node_data); + freeLimitsFile(&limit_file); + res |= fts_system_reset(); + if (res < OK) { + logError(1, "%s production_test_ito: ERROR %08X\n", tag, + ERROR_PROD_TEST_ITO); + res = (res | ERROR_PROD_TEST_ITO); + } + return res; +} + +/** + * Perform the Initialization of the IC + * @param type type of initialization to do + * (see @link sys_special_opt Initialization Options (Full or Panel) @endlink) + * @return OK if success or an error code which specify the type of error + */ +int production_test_initialization(u8 type) +{ + int res; + + logError(0, "%s INITIALIZATION Production test is starting...\n", tag); + if (type != SPECIAL_PANEL_INIT && type != SPECIAL_FULL_PANEL_INIT) { + logError(1, + "%s production_test_initialization: Type incompatible! Type = %02X ERROR %08X\n", + tag, type, ERROR_OP_NOT_ALLOW | + ERROR_PROD_TEST_INITIALIZATION); + return ERROR_OP_NOT_ALLOW | ERROR_PROD_TEST_INITIALIZATION; + } + + res = fts_system_reset(); + if (res < 0) { + logError(1, "%s production_test_initialization: ERROR %08X\n", + tag, ERROR_PROD_TEST_INITIALIZATION); + return res | ERROR_PROD_TEST_INITIALIZATION; + } + + logError(0, "%s INITIALIZATION command sent... %02X\n", tag, type); + res = writeSysCmd(SYS_CMD_SPECIAL, &type, 1); + if (res < OK) { + logError(1, "%s production_test_initialization: ERROR %08X\n", + tag, (res | ERROR_PROD_TEST_INITIALIZATION)); + return res | ERROR_PROD_TEST_INITIALIZATION; + } + + + logError(0, "%s Refresh Sys Info...\n", tag); + res |= readSysInfo(1); /* need to update the chipInfo in order + * to refresh several versions */ + + if (res < 0) { + logError(1, + "%s production_test_initialization: read sys info ERROR %08X\n", + tag, ERROR_PROD_TEST_INITIALIZATION); + res = (res | ERROR_PROD_TEST_INITIALIZATION); + } + + return res; +} + + +/** + * Perform a FULL (ITO + INIT + DATA CHECK) Mass Production Test of the IC + * @param pathThresholds name of Production Limit file to load or + * "NULL" if the limits data should be loaded by a .h file + * @param stop_on_fail if 1, the test flow stops at the first data check + * failure + * otherwise it keeps going performing all the selected test + * @param saveInit if >0 (possible values: NO_INIT, SPECIAL_PANEL_INIT or + * SPECIAL_FULL_PANEL_INIT), + * the Initialization of the IC is executed otherwise it is skipped + * @param todo pointer to a TestToDo variable which select the test to do + * @param mpflag MP flag value to write in case of successful test + * @return OK if success or an error code which specify the type of error + */ +int production_test_main(char *pathThresholds, int stop_on_fail, int saveInit, + TestToDo *todo, u8 mpflag) +{ + int res, ret; + + logError(0, "%s MAIN Production test is starting...\n", tag); + + logError(0, "%s\n", tag); + + logError(0, "%s ITO TEST:\n", tag); + res = production_test_ito(pathThresholds, todo); + if (res < 0) { + logError(0, "%s Error during ITO TEST! ERROR %08X\n", tag, res); + goto END;/* in case of ITO TEST failure is no sense keep going + * */ + } else + logError(0, "%s ITO TEST OK!\n", tag); + + + logError(0, "%s\n", tag); + + logError(0, "%s INITIALIZATION TEST :\n", tag); + if (saveInit != NO_INIT) { + res = production_test_initialization((u8)saveInit); + if (res < 0) { + logError(0, + "%s Error during INITIALIZATION TEST! ERROR %08X\n", + tag, res); + if (stop_on_fail) + goto END; + } else + logError(0, "%s INITIALIZATION TEST OK!\n", tag); + } else + logError(0, + "%s INITIALIZATION TEST :................. SKIPPED\n", + tag); + + + logError(0, "%s\n", tag); + + if (saveInit != NO_INIT) { + logError(0, "%s Cleaning up...\n", tag); + ret = fts_system_reset(); + if (ret < 0) { + logError(1, + "%s production_test_main: system reset ERROR %08X\n", + tag, ret); + res |= ret; + if (stop_on_fail) + goto END; + } + logError(0, "%s\n", tag); + } + + logError(0, "%s PRODUCTION DATA TEST:\n", tag); + ret = production_test_data(pathThresholds, stop_on_fail, todo); + if (ret < OK) + logError(0, + "%s Error during PRODUCTION DATA TEST! ERROR %08X\n", + tag, ret); + else { + logError(0, "%s PRODUCTION DATA TEST OK!\n", tag); + +#ifdef COMPUTE_INIT_METHOD + if (saveInit != NO_INIT) { + /* save the mp flag to desired value + * because data check OK */ + ret = saveMpFlag(mpflag); + if (ret < OK) + logError(0, + "%s Error while saving MP FLAG! ERROR %08X\n", + tag, ret); + else + logError(0, "%s MP FLAG saving OK!\n", tag); + } +#endif + + } + + res |= ret; + /* the OR is important because if the data test is OK but + * the init test fail, the main production test result should = FAIL */ + + +END: + if (res < OK) { + logError(0, + "%s MAIN Production test finished.................FAILED\n", + tag); + return res; + } else { + logError(0, + "%s MAIN Production test finished.................OK\n", + tag); + return OK; + } +} + +/** + * Perform all the test selected in a TestTodo variable related to MS raw data + * (touch, keys etc..) + * @param path_limits name of Production Limit file to load or + * "NULL" if the limits data should be loaded by a .h file + * @param stop_on_fail if 1, the test flow stops at the first data check + * failure + * otherwise it keeps going performing all the selected test + * @param todo pointer to a TestToDo variable which select the test to do + * @return OK if success or an error code which specify the type of error + */ +int production_test_ms_raw(char *path_limits, int stop_on_fail, TestToDo *todo) +{ + int ret, count_fail = 0; + MutualSenseFrame msRawFrame; + + + int *thresholds = NULL; + int trows, tcolumns; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + u16 *adj = NULL; + + int maxAdjH, maxAdjV; + int i, z; + + msRawFrame.node_data = NULL; + + /************** Mutual Sense Test *************/ + logError(0, "%s\n", tag); + logError(0, "%s MS RAW DATA TEST is starting...\n", tag); + if (todo->MutualRaw == 1 || todo->MutualRawGap == 1 || + todo->MutualRawAdj == 1 || todo->MutualRawMap == 1 || + todo->MutualRawAdjGap == 1 || todo->MutualRawAdjPeak == 1) { + ret = setScanMode(SCAN_MODE_LOCKED, LOCKED_ACTIVE); + msleep(WAIT_FOR_FRESH_FRAMES); + ret |= setScanMode(SCAN_MODE_ACTIVE, 0x00); + msleep(WAIT_AFTER_SENSEOFF); + ret |= getMSFrame3(MS_RAW, &msRawFrame); + if (ret < OK) { + logError(1, + "%s production_test_data: getMSFrame failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + return ret | ERROR_PROD_TEST_DATA; + } + + logError(0, "%s MS RAW MIN MAX TEST:\n", tag); + if (todo->MutualRaw == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + MS_RAW_MIN_MAX, + &thresholds, &trows, + &tcolumns); + if (ret < OK || (trows != 1 || tcolumns != 2)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_RAW_MIN_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + + ret = checkLimitsMinMax(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax MS RAW failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS RAW MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } else + logError(0, + "%s MS RAW MIN MAX TEST:.................OK\n", + tag); + kfree(thresholds); + thresholds = NULL; + } else + logError(0, + "%s MS RAW MIN MAX TEST:.................SKIPPED\n", + tag); + logError(0, "%s MS RAW MAP MIN MAX TEST:\n", tag); + if (todo->MutualRawMap == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, MS_RAW_EACH_NODE_MIN, + &thresholds_min, &trows, &tcolumns); + if (ret < OK || (trows != + msRawFrame.header.force_node || + tcolumns != msRawFrame.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_RAW_EACH_NODE_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + ret = parseProductionTestLimits(path_limits, + &limit_file, MS_RAW_EACH_NODE_MAX, + &thresholds_max, &trows, &tcolumns); + if (ret < OK || (trows != + msRawFrame.header.force_node || + tcolumns != msRawFrame.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_RAW_EACH_NODE_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + ret = checkLimitsMapTotal(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMaxEachNodeData failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS RAW MAP MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } else{ + logError(0, "%s MS RAW MAP MIN MAX TEST:.................OK\n", + tag); + } + if (thresholds_min != NULL) { + kfree(thresholds_min); + thresholds_min = NULL; + } + if (thresholds_max != NULL) { + kfree(thresholds_max); + thresholds_max = NULL; + } + } else { + logError(0, "%s MS RAW MAP MIN MAX TEST:.................SKIPPED\n", tag); + } + + logError(0, "%s\n", tag); + logError(0, "%s MS RAW GAP TEST:\n", tag); + if (todo->MutualRawGap == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, MS_RAW_GAP, + &thresholds, &trows, + &tcolumns); + if (ret < OK || (trows != 1 || tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_RAW_GAP failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsGap(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + thresholds[0]); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsGap MS RAW failed... ERROR = %08X\n", + tag, ret); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } else + logError(0, + "%s MS RAW GAP TEST:.................OK\n\n", + tag); + kfree(thresholds); + thresholds = NULL; + } else + logError(0, + "%s MS RAW GAP TEST:.................SKIPPED\n", + tag); + + logError(0, "%s\n", tag); + logError(0, "%s MS RAW ADJ TEST:\n", tag); + if ((todo->MutualRawAdj == 1) || (todo->MutualRawAdjGap == 1) || + (todo->MutualRawAdjPeak == 1)) { + logError(0, "%s MS RAW ADJ HORIZONTAL TESTs:\n", tag); + ret = computeAdjHorizTotal(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + &adj); + if (ret < OK) { + logError(1, + "%s production_test_data: computeAdjHoriz failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + if (todo->MutualRawAdj) { + logError(0, "%s MS RAW ADJ HORIZONTAL min/Max:\n", + tag); + + ret = parseProductionTestLimits(path_limits, + &limit_file, + MS_RAW_ADJH, + &thresholds, &trows, + &tcolumns); + if (ret < OK || (trows != + msRawFrame.header.force_node || + tcolumns != + msRawFrame.header.sense_node - 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_RAW_ADJH failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + + ret = checkLimitsMapAdjTotal(adj, + msRawFrame.header. + force_node, + msRawFrame.header. + sense_node - 1, + thresholds); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsAdj MS RAW ADJH failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS RAW ADJ HORIZONTAL min/Max:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } else + logError(0, + "%s MS RAW ADJ HORIZONTAL min/Max:.................OK\n", + tag); + + if (thresholds != NULL) { + kfree(thresholds); + thresholds = NULL; + } + } + + + if (todo->MutualRawAdjGap) { + logError(0, "%s MS RAW ADJ HORIZONTAL GAP:\n", + tag); + + ret = parseProductionTestLimits(path_limits, + &limit_file, + MS_RAW_ADJH_GAP, + &thresholds, &trows, + &tcolumns); + if (ret < OK || (trows != 1 || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_RAW_ADJH failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + + ret = checkLimitsGapOffsets(adj, + msRawFrame.header. + force_node, + msRawFrame.header. + sense_node - 1, + thresholds[0], + 1,1,1,1); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsAdj MS RAW ADJH GAP failed...\n", + tag); + logError(0, + "%s MS RAW ADJ HORIZONTAL GAP:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } else + logError(0, + "%s MS RAW ADJ HORIZONTAL GAP:.................OK\n", + tag); + + if (thresholds != NULL) { + kfree(thresholds); + thresholds = NULL; + } + } + + + if (todo->MutualRawAdjPeak) { + logError(0, "%s MS RAW ADJ Peak: Getting max ADJH\n", + tag); + maxAdjH = abs(adj[msRawFrame.header.force_node + + 1]); + /* skip nodes on the edges */ + for (i = 1; i < msRawFrame.header + .force_node - 1; i++) { + for (z = 1; z < + (msRawFrame.header + .sense_node - 2); z++){ + if (maxAdjH < abs(adj[(i * + msRawFrame.header + .force_node) + z])) + maxAdjH = abs(adj[(i * + msRawFrame.header + .force_node) + z]); + } + + } + } + + kfree(adj); + adj = NULL; + + logError(0, "%s MS RAW ADJ VERTICAL TESTs:\n", tag); + ret = computeAdjVertTotal(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + &adj); + if (ret < OK) { + logError(1, + "%s production_test_data: computeAdjVert failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + if (todo->MutualRawAdj) { + logError(0, "%s MS RAW ADJ VERTICAL min/Max:\n", + tag); + ret = parseProductionTestLimits(path_limits, + &limit_file, + MS_RAW_ADJV, + &thresholds, &trows, + &tcolumns); + if (ret < OK || (trows != + msRawFrame.header.force_node - 1 || + tcolumns != msRawFrame.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_RAW_ADJV failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + + ret = checkLimitsMapAdjTotal(adj, + msRawFrame.header. + force_node - 1, + msRawFrame.header. + sense_node, + thresholds); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsAdj MS RAW ADJV failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS RAW ADJ VERTICAL min/Max:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } else + logError(0, + "%s MS RAW ADJ VERTICAL min/Max:.................OK\n", + tag); + + if (thresholds != NULL) { + kfree(thresholds); + thresholds = NULL; + } + } + + if (todo->MutualRawAdjGap) { + logError(0, "%s MS RAW ADJ VERTICAL GAP:\n", + tag); + ret = parseProductionTestLimits(path_limits, + &limit_file, + MS_RAW_ADJV_GAP, + &thresholds, &trows, + &tcolumns); + if (ret < OK || (trows != 1 || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_RAW_ADJV_GAP failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + + ret = checkLimitsGapOffsets(adj, + msRawFrame.header. + force_node - 1, + msRawFrame.header. + sense_node, + thresholds[0], + 1,1,1,1); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsAdj MS RAW ADJV GAP failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS RAW ADJ VERTICAL GAP:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } else + logError(0, + "%s MS RAW ADJ VERTICAL GAP:.................OK\n", + tag); + + if (thresholds != NULL) { + kfree(thresholds); + thresholds = NULL; + } + } + + + if (todo->MutualRawAdjPeak) { + logError(0, "%s MS RAW ADJ Peak: Getting max ADJV\n", + tag); + maxAdjV = abs(adj[(msRawFrame.header.force_node) + + 1]); + + /* skip nodes on the edges */ + for (i = 1; i < (msRawFrame.header + .force_node - 2); i++) { + for (z = 1; z < msRawFrame.header + .sense_node - 1; z++) { + if (maxAdjV < abs(adj[(i * + msRawFrame.header + .force_node) + z])) + maxAdjV = abs(adj[(i * + msRawFrame.header + .force_node) + z]); + + } + } + + + + ret = parseProductionTestLimits(path_limits, + &limit_file, + MS_RAW_ADJ_PEAK, + &thresholds, &trows, + &tcolumns); + if (ret < OK || (trows != 1 || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_RAW_ADJV_PEAK failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + logError(1, "%s maxAdjH = %d maxAdjV = %d threshold = %d\n", + tag, maxAdjH, maxAdjV, thresholds[0]); + + ret = OK; + if (maxAdjH > maxAdjV) { + if (maxAdjH > thresholds[0]) + ret = ERROR_PROD_TEST_DATA; + } else { + if (maxAdjV > thresholds[0]) + ret = ERROR_PROD_TEST_DATA; + } + + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsAdj MS RAW ADJV GAP failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS RAW ADJ PEAK:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } else + logError(0, + "%s MS RAW ADJ PEAK:.................OK\n", + tag); + + if (thresholds != NULL) { + kfree(thresholds); + thresholds = NULL; + } + + } + + + kfree(adj); + adj = NULL; + } else + logError(0, + "%s MS RAW ADJ TEST:.................SKIPPED\n", + tag); + } else + logError(0, "%s MS RAW DATA TEST:.................SKIPPED\n", + tag); + + logError(0, "%s\n", tag); + logError(0, "%s MS KEY RAW TEST:\n", tag); + if (todo->MutualKeyRaw == 1) { + ret = production_test_ms_key_raw(path_limits); + if (ret < 0) { + logError(1, + "%s production_test_data: production_test_ms_key_raw failed... ERROR = %08X\n", + tag, ret); + count_fail += 1; + if (count_fail == 1) { + logError(0, + "%s MS RAW DATA TEST:.................FAIL fails_count = %d\n\n", + tag, count_fail); + goto ERROR_LIMITS; + } + } + } else + logError(0, "%s MS KEY RAW TEST:.................SKIPPED\n", + tag); + + ret = production_test_ms_raw_lp(path_limits, stop_on_fail, todo); + if (ret < 0) { + logError(1, + "%s production_test_data: production_test_ms_raw_lp failed... ERROR = %08X\n", + tag, ret); + count_fail += 1; + if (count_fail == 1) { + logError(0, + "%s MS RAW DATA TEST:.................FAIL fails_count = %d\n\n", + tag, count_fail); + goto ERROR_LIMITS; + } + } + +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + if (msRawFrame.node_data != NULL) { + kfree(msRawFrame.node_data); + msRawFrame.node_data = NULL; + } + logError(0, + "%s MS RAW DATA TEST finished!.................OK\n", + tag); + return OK; + } else { + print_frame_short("MS Raw frame =", array1dTo2d_short( + msRawFrame.node_data, + msRawFrame.node_data_size, + msRawFrame.header.sense_node), + msRawFrame.header.force_node, + msRawFrame.header.sense_node); + + if (msRawFrame.node_data != NULL) + kfree(msRawFrame.node_data); + if (thresholds != NULL) + kfree(thresholds); + if (adj != NULL) + kfree(adj); + if (thresholds_min != NULL) { + kfree(thresholds_min); + thresholds_min = NULL; + } + if (thresholds_max != NULL) { + kfree(thresholds_max); + thresholds_max = NULL; + } + logError(0, + "%s MS RAW DATA TEST:.................FAIL fails_count = %d\n\n", + tag, count_fail); + return ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL; + } + + +ERROR_LIMITS: + if (msRawFrame.node_data != NULL) + kfree(msRawFrame.node_data); + if (thresholds != NULL) + kfree(thresholds); + return ret; +} + + +/** + * Perform all the test selected in a TestTodo variable related to MS low power + * raw data + * @param path_limits name of Production Limit file to load or + * "NULL" if the limits data should be loaded by a .h file + * @param stop_on_fail if 1, the test flow stops at the first data check + * failure + * otherwise it keeps going performing all the selected test + * @param todo pointer to a TestToDo variable which select the test to do + * @return OK if success or an error code which specify the type of error + */ +int production_test_ms_raw_lp(char *path_limits, int stop_on_fail, + TestToDo *todo) +{ + int ret, count_fail = 0; + MutualSenseFrame msRawFrame; + + + int *thresholds = NULL; + int trows, tcolumns; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + + u16 *adj = NULL; + msRawFrame.node_data = NULL; + + /************** Mutual Sense Test **************/ + logError(0, "%s\n", tag); + logError(0, "%s MS RAW LP DATA TEST:\n", tag); + if (todo->MutualRawLP == 1 || todo->MutualRawGapLP == 1 || + todo->MutualRawAdjLP == 1) { + ret = setScanMode(SCAN_MODE_LOCKED, LOCKED_LP_ACTIVE); + msleep(WAIT_FOR_FRESH_FRAMES); + ret |= setScanMode(SCAN_MODE_ACTIVE, 0x00); + msleep(WAIT_AFTER_SENSEOFF); + ret |= getMSFrame3(MS_RAW, &msRawFrame); + if (ret < 0) { + logError(1, + "%s production_test_data: getMSFrame failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + return ret | ERROR_PROD_TEST_DATA; + } + + logError(0, "%s MS RAW LP MIN MAX TEST:\n", tag); + if (todo->MutualRawLP == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + MS_RAW_LP_MIN_MAX, + &thresholds, &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_RAW_LP_MIN_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + + ret = checkLimitsMinMax(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax MS RAW LP failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS RAW LP MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } else + logError(0, + "%s MS RAW LP MIN MAX TEST:.................OK\n", + tag); + kfree(thresholds); + thresholds = NULL; + } else + logError(0, + "%s MS RAW LP MIN MAX TEST:.................SKIPPED\n", + tag); + + logError(0, "%s MS RAW LP MAP MIN MAX TEST:\n", tag); + if (todo->MutualRawMapLP == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, MS_RAW_LP_EACH_NODE_MIN, + &thresholds_min, &trows, &tcolumns); + if (ret < OK || (trows != + msRawFrame.header.force_node || + tcolumns != msRawFrame.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_RAW_LP_EACH_NODE_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + ret = parseProductionTestLimits(path_limits, + &limit_file, MS_RAW_LP_EACH_NODE_MAX, + &thresholds_max, &trows, &tcolumns); + if (ret < OK || (trows != + msRawFrame.header.force_node || + tcolumns != msRawFrame.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_RAW_LP_EACH_NODE_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + ret = checkLimitsMapTotal(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMaxEachNodeData failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS RAW LP MAP MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } else{ + logError(0, "%s MS RAW LP MAP MIN MAX TEST:.................OK\n", + tag); + } + if (thresholds_min != NULL) { + kfree(thresholds_min); + thresholds_min = NULL; + } + if (thresholds_max != NULL) { + kfree(thresholds_max); + thresholds_max = NULL; + } + } else { + logError(0, "%s MS RAW LP MAP MIN MAX TEST:.................SKIPPED\n", tag); + } + + logError(0, "%s\n", tag); + logError(0, "%s MS RAW LP GAP TEST:\n", tag); + if (todo->MutualRawGapLP == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + MS_RAW_LP_GAP, + &thresholds, &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_RAW_LP_GAP failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsGap(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + thresholds[0]); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsGap MS RAW LP failed... ERROR = %08X\n", + tag, ret); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } else + logError(0, + "%s MS RAW LP GAP TEST:.................OK\n\n", + tag); + kfree(thresholds); + thresholds = NULL; + } else + logError(0, + "%s MS RAW LP GAP TEST:.................SKIPPED\n", + tag); + + logError(0, "%s\n", tag); + logError(0, "%s MS RAW LP ADJ TEST:\n", tag); + if (todo->MutualRawAdjLP == 1) { + logError(0, "%s MS RAW LP ADJ HORIZONTAL TEST:\n", tag); + ret = computeAdjHorizTotal(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + &adj); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjHoriz failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, + &limit_file, + MS_RAW_LP_ADJH, + &thresholds, &trows, + &tcolumns); + if (ret < 0 || (trows != msRawFrame.header.force_node || + tcolumns != + msRawFrame.header.sense_node - 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_RAW_LP_ADJH failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + + ret = checkLimitsMapAdjTotal(adj, + msRawFrame.header. + force_node, + msRawFrame.header. + sense_node - 1, + thresholds); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsAdj MS RAW LP ADJH failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS RAW LP ADJ HORIZONTAL TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } else + logError(0, + "%s MS RAW LP ADJ HORIZONTAL TEST:.................OK\n", + tag); + + kfree(thresholds); + thresholds = NULL; + + kfree(adj); + adj = NULL; + + logError(0, "%s MS RAW LP ADJ VERTICAL TEST:\n", tag); + ret = computeAdjVertTotal(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + &adj); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjVert failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, + &limit_file, + MS_RAW_LP_ADJV, + &thresholds, &trows, + &tcolumns); + if (ret < 0 || (trows != msRawFrame.header.force_node - + 1 || tcolumns != + msRawFrame.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_RAW_ADJV failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + + ret = checkLimitsMapAdjTotal(adj, + msRawFrame.header. + force_node - 1, + msRawFrame.header. + sense_node, thresholds); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsAdj MS RAW ADJV failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS RAW LP ADJ VERTICAL TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail == 1) + goto ERROR; + } else + logError(0, + "%s MS RAW LP ADJ VERTICAL TEST:.................OK\n", + tag); + kfree(thresholds); + thresholds = NULL; + + kfree(adj); + adj = NULL; + } else + logError(0, + "%s MS RAW LP ADJ TEST:.................SKIPPED\n", + tag); + } else + logError(0, + "%s MS RAW LP DATA TEST:.................SKIPPED\n", + tag); + +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + if (msRawFrame.node_data != NULL) { + kfree(msRawFrame.node_data); + msRawFrame.node_data = NULL; + } + logError(0, + "%s MS RAW DATA TEST finished!.................OK\n", + tag); + return OK; + } else { + if (msRawFrame.node_data != NULL) { + print_frame_short("MS Raw LP frame =", + array1dTo2d_short( + msRawFrame.node_data, + msRawFrame. + node_data_size, + msRawFrame.header. + sense_node), + msRawFrame.header.force_node, + msRawFrame.header.sense_node); + kfree(msRawFrame.node_data); + } + if (thresholds != NULL) + kfree(thresholds); + if (adj != NULL) + kfree(adj); + if (thresholds_min != NULL) { + kfree(thresholds_min); + thresholds_min = NULL; + } + if (thresholds_max != NULL) { + kfree(thresholds_max); + thresholds_max = NULL; + } + logError(0, + "%s MS RAW LP DATA TEST:.................FAIL fails_count = %d\n\n", + tag, count_fail); + return ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL; + } + + +ERROR_LIMITS: + if (msRawFrame.node_data != NULL) + kfree(msRawFrame.node_data); + if (thresholds != NULL) + kfree(thresholds); + return ret; +} + +/** + * Perform MS raw test for keys data + * @param path_limits name of Production Limit file to load or + * "NULL" if the limits data should be loaded by a .h file + * @return OK if success or an error code which specify the type of error + */ +int production_test_ms_key_raw(char *path_limits) +{ + int ret; + MutualSenseFrame msRawFrame; + + int *thresholds = NULL; + int trows, tcolumns; + + /************** Mutual Sense Test **************/ + logError(0, "%s MS KEY RAW DATA TEST is starting...\n", tag); + ret = setScanMode(SCAN_MODE_ACTIVE, 0xFF); + msleep(WAIT_FOR_FRESH_FRAMES); + ret |= setScanMode(SCAN_MODE_ACTIVE, 0x00); + msleep(WAIT_AFTER_SENSEOFF); + ret |= getMSFrame3(MS_KEY_RAW, &msRawFrame); + if (ret < 0) { + logError(1, + "%s production_test_data: getMSKeyFrame failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + return ret | ERROR_PROD_TEST_DATA; + } + + ret = parseProductionTestLimits(path_limits, &limit_file, + MS_KEY_RAW_MIN_MAX, &thresholds, &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_KEY_RAW_MIN_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMinMax(msRawFrame.node_data, + msRawFrame.header.force_node, + msRawFrame.header.sense_node, + thresholds[0], thresholds[1]); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax MS KEY RAW failed... ERROR COUNT = %d\n", + tag, ret); + goto ERROR; + } else + logError(0, "%s MS KEY RAW TEST:.................OK\n\n", tag); + + kfree(thresholds); + thresholds = NULL; + + kfree(msRawFrame.node_data); + msRawFrame.node_data = NULL; + return OK; + +ERROR: + print_frame_short("MS Key Raw frame =", array1dTo2d_short( + msRawFrame.node_data, + msRawFrame.node_data_size, + msRawFrame.header.sense_node), + msRawFrame.header.force_node, + msRawFrame.header.sense_node); + if (msRawFrame.node_data != NULL) + kfree(msRawFrame.node_data); + if (thresholds != NULL) + kfree(thresholds); + logError(0, "%s MS KEY RAW TEST:.................FAIL\n\n", tag); + return ERROR_PROD_TEST_DATA | ERROR_TEST_CHECK_FAIL; + +ERROR_LIMITS: + if (msRawFrame.node_data != NULL) + kfree(msRawFrame.node_data); + if (thresholds != NULL) + kfree(thresholds); + return ret; +} + +/** + * Perform all the tests selected in a TestTodo variable related to MS Init + * data (touch, keys etc..) + * @param path_limits name of Production Limit file to load or + * "NULL" if the limits data should be loaded by a .h file + * @param stop_on_fail if 1, the test flow stops at the first data check + * failure otherwise it keeps going performing all the selected test + * @param todo pointer to a TestToDo variable which select the test to do + * @return OK if success or an error code which specify the type of error + */ +int production_test_ms_cx(char *path_limits, int stop_on_fail, TestToDo *todo) +{ + int ret; + int count_fail = 0; + + int *thresholds = NULL; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + int trows, tcolumns; + + MutualSenseData msCompData; + TotMutualSenseData totCompData; + + u8 *adjhor = NULL; + + u8 *adjvert = NULL; + + u16 container; + /* u16 *total_cx = NULL; */ + u16 *total_adjhor = NULL; + u16 *total_adjvert = NULL; + + + /* MS CX TEST */ + logError(0, "%s\n", tag); + logError(0, "%s MS CX Testes are starting...\n", tag); + + ret = readMutualSenseCompensationData(LOAD_CX_MS_TOUCH, &msCompData); + /* read MS compensation data */ + if (ret < 0) { + logError(1, + "%s production_test_data: readMutualSenseCompensationData failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + return ret | ERROR_PROD_TEST_DATA; + } + + ret = readTotMutualSenseCompensationData(LOAD_PANEL_CX_TOT_MS_TOUCH, + &totCompData); + /* read TOT MS compensation data */ + if (ret < 0) { + logError(1, + "%s production_test_data: readTotMutualSenseCompensationData failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + kfree(msCompData.node_data); + msCompData.node_data = NULL; + return ret | ERROR_PROD_TEST_DATA; + } + + logError(0, "%s MS CX1 TEST:\n", tag); + if (todo->MutualCx1 == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + MS_CX1_MIN_MAX, &thresholds, + &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_CX1_MIN_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + container = (u16)msCompData.cx1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], + thresholds[1]); + /* check the limits */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax MS CX1 failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, "%s MS CX1 TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS CX1 TEST:.................OK\n\n", + tag); + } else + logError(0, "%s MS CX1 TEST:.................SKIPPED\n\n", + tag); + + kfree(thresholds); + thresholds = NULL; + + logError(0, "%s MS CX2 MIN MAX TEST:\n", tag); + if (todo->MutualCx2 == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + MS_CX2_MAP_MIN, &thresholds_min, + &trows, &tcolumns); + /* load min thresholds */ + if (ret < 0 || (trows != msCompData.header.force_node || + tcolumns != msCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_CX2_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, &limit_file, + MS_CX2_MAP_MAX, &thresholds_max, + &trows, &tcolumns); + /* load max thresholds */ + if (ret < 0 || (trows != msCompData.header.force_node || + tcolumns != msCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_CX2_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMap(msCompData.node_data, + msCompData.header.force_node, + msCompData.header.sense_node, + thresholds_min, thresholds_max); + /* check the limits */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap MS CX2 MIN MAX failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS CX2 MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s MS CX2 MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s MS CX2 MIN MAX TEST:.................SKIPPED\n\n", + tag); + + logError(0, "%s MS CX2 ADJ TEST:\n", tag); + if (todo->MutualCx2Adj == 1) { + /* MS CX2 ADJ HORIZ */ + logError(0, "%s MS CX2 ADJ HORIZ TEST:\n", tag); + + ret = computeAdjHoriz(msCompData.node_data, + msCompData.header.force_node, + msCompData.header.sense_node, + &adjhor); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjHoriz failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s MS CX2 ADJ HORIZ computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, &limit_file, + MS_CX2_ADJH_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node || + tcolumns != msCompData.header.sense_node - 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_CX2_ADJH_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdj(adjhor, msCompData.header.force_node, + msCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMapAdj CX2 ADJH failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS CX2 ADJ HORIZ TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s MS CX2 ADJ HORIZ TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjhor); + adjhor = NULL; + + /* MS CX2 ADJ VERT */ + logError(0, "%s MS CX2 ADJ VERT TEST:\n", tag); + + ret = computeAdjVert(msCompData.node_data, + msCompData.header.force_node, + msCompData.header.sense_node, + &adjvert); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjVert failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s MS CX2 ADJ VERT computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, &limit_file, + MS_CX2_ADJV_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node - 1 || + tcolumns != msCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_CX2_ADJV_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdj(adjvert, msCompData.header.force_node - + 1, msCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMapAdj CX2 ADJV failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS CX2 ADJ HORIZ TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s MS CX2 ADJ VERT TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjvert); + adjvert = NULL; + } else + logError(0, "%s MS CX2 ADJ TEST:.................SKIPPED\n\n", + tag); + + /* START OF TOTAL CHECK */ + logError(0, "%s MS TOTAL CX TEST:\n", tag); + + if (todo->MutualCxTotal == 1 || todo->MutualCxTotalAdj == 1) { + logError(0, "%s MS TOTAL CX MIN MAX TEST:\n", tag); + if (todo->MutualCxTotal == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + MS_TOTAL_CX_MAP_MIN, + &thresholds_min, + &trows, &tcolumns); + /* load min thresholds */ + if (ret < 0 || (trows != + totCompData.header.force_node || + tcolumns != + totCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, + &limit_file, + MS_TOTAL_CX_MAP_MAX, + &thresholds_max, + &trows, &tcolumns); + /* load max thresholds */ + if (ret < 0 || (trows != + totCompData.header.force_node || + tcolumns != + totCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapTotal(totCompData.node_data, + totCompData.header.force_node, + totCompData.header.sense_node, + thresholds_min, + thresholds_max); + /* check the limits */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap MS TOTAL CX TEST failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS TOTAL CX MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s MS TOTAL CX MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s MS TOTAL CX MIN MAX TEST:.................SKIPPED\n\n", + tag); + + + logError(0, "%s MS TOTAL CX ADJ TEST:\n", tag); + if (todo->MutualCxTotalAdj == 1) { + /* MS TOTAL CX ADJ HORIZ */ + logError(0, "%s MS TOTAL CX ADJ HORIZ TEST:\n", tag); + + ret = computeAdjHorizTotal(totCompData.node_data, + totCompData.header.force_node, + totCompData.header.sense_node, + &total_adjhor); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjHoriz failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s MS TOTAL CX ADJ HORIZ computed!\n", + tag); + + ret = parseProductionTestLimits(path_limits, + &limit_file, + MS_TOTAL_CX_ADJH_MAP_MAX, + &thresholds_max, + &trows, &tcolumns); + if (ret < 0 || (trows != + totCompData.header.force_node || + tcolumns != + totCompData.header.sense_node - 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_ADJH_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdjTotal(total_adjhor, + totCompData.header. + force_node, + totCompData.header. + sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMapAdj MS TOTAL CX ADJH failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS TOTAL CX ADJ HORIZ TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s MS TOTAL CX ADJ HORIZ TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjhor); + total_adjhor = NULL; + + /* MS TOTAL CX ADJ VERT */ + logError(0, "%s MS TOTAL CX ADJ VERT TEST:\n", tag); + + ret = computeAdjVertTotal(totCompData.node_data, + totCompData.header.force_node, + totCompData.header.sense_node, + &total_adjvert); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjVert failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s MS TOTAL CX ADJ VERT computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, + &limit_file, + MS_TOTAL_CX_ADJV_MAP_MAX, + &thresholds_max, + &trows, &tcolumns); + if (ret < 0 || (trows != totCompData.header.force_node - + 1 || tcolumns != + totCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_ADJV_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdjTotal(total_adjvert, + totCompData.header. + force_node - 1, + totCompData.header. + sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMapAdj MS TOTAL CX ADJV failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS TOTAL CX ADJ HORIZ TEST:.................FAIL\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s MS TOTAL CX ADJ VERT TEST:.................OK\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjvert); + total_adjvert = NULL; + } else + logError(0, + "%s MS TOTAL CX ADJ TEST:.................SKIPPED\n", + tag); + + kfree(totCompData.node_data); + totCompData.node_data = NULL; + } else + logError(0, "%s MS TOTAL CX TEST:.................SKIPPED\n", + tag); + + + + if ((todo->MutualCx1LP | todo->MutualCx2LP | todo->MutualCx2TotalLP | + todo->MutualCxTotalLP | todo->MutualCxTotalAdjLP) == 1) { + ret = production_test_ms_cx_lp(path_limits, stop_on_fail, todo); + if (ret < OK) { + count_fail += 1; + logError(1, + "%s production_test_data: production_test_cx_lp failed... ERROR = %08X\n", + tag, ret); + logError(0, + "%s MS CX testes finished!.................FAILED fails_count = %d\n\n", + tag, count_fail); + return ret; + } + } else + logError(0, "%s MS CX LP TEST:.................SKIPPED\n", + tag); + + + if ((todo->MutualKeyCx1 | todo->MutualKeyCx2 | + todo->MutualKeyCxTotal) == 1) { + ret = production_test_ms_key_cx(path_limits, stop_on_fail, + todo); + if (ret < 0) { + count_fail += 1; + logError(1, + "%s production_test_data: production_test_ms_key_cx failed... ERROR = %08X\n", + tag, ret); + logError(0, + "%s MS CX testes finished!.................FAILED fails_count = %d\n\n", + tag, count_fail); + return ret; + } + } else + logError(0, "%s MS KEY CX TEST:.................SKIPPED\n", + tag); + + + +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, "%s MS CX testes finished!.................OK\n", + tag); + kfree(msCompData.node_data); + msCompData.node_data = NULL; + return OK; + } else { + print_frame_i8("MS Init Data (Cx2) =", array1dTo2d_i8( + msCompData.node_data, + msCompData.node_data_size, + msCompData.header.sense_node), + msCompData.header.force_node, + msCompData.header.sense_node); + print_frame_short(" TOT MS Init Data (Cx) =", array1dTo2d_short( + totCompData.node_data, + totCompData.node_data_size, + totCompData.header.sense_node), + totCompData.header.force_node, + totCompData.header.sense_node); + logError(0, + "%s MS CX testes finished!.................FAILED fails_count = %d\n\n", + tag, count_fail); + if (thresholds != NULL) + kfree(thresholds); + if (thresholds_min != NULL) + kfree(thresholds_min); + if (thresholds_max != NULL) + kfree(thresholds_max); + if (adjhor != NULL) + kfree(adjhor); + if (adjvert != NULL) + kfree(adjvert); + if (totCompData.node_data != NULL) + kfree(totCompData.node_data); + if (total_adjhor != NULL) + kfree(total_adjhor); + if (total_adjvert != NULL) + kfree(total_adjvert); + if (msCompData.node_data != NULL) + kfree(msCompData.node_data); + return ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA; + } + +ERROR_LIMITS: + if (thresholds != NULL) + kfree(thresholds); + if (thresholds_min != NULL) + kfree(thresholds_min); + if (thresholds_max != NULL) + kfree(thresholds_max); + if (adjhor != NULL) + kfree(adjhor); + if (adjvert != NULL) + kfree(adjvert); + if (totCompData.node_data != NULL) + kfree(totCompData.node_data); + if (total_adjhor != NULL) + kfree(total_adjhor); + if (total_adjvert != NULL) + kfree(total_adjvert); + if (msCompData.node_data != NULL) + kfree(msCompData.node_data); + return ret; +} + +/** + * Perform all the tests selected in a TestTodo variable related to MS Init + * data of the keys + * @param path_limits name of Production Limit file to load or + * "NULL" if the limits data should be loaded by a .h file + * @param stop_on_fail if 1, the test flow stops at the first data check + * failure + * otherwise it keeps going performing all the selected test + * @param todo pointer to a TestToDo variable which select the test to do + * @return OK if success or an error code which specify the type of error + */ +int production_test_ms_key_cx(char *path_limits, int stop_on_fail, + TestToDo *todo) +{ + int ret; + int count_fail = 0; + int num_keys = 0; + + int *thresholds = NULL; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + int trows, tcolumns; + + MutualSenseData msCompData; + TotMutualSenseData totCompData; + + + short container; + + + /* MS CX TEST */ + logError(0, "%s MS KEY CX Testes are starting...\n", tag); + + ret = readMutualSenseCompensationData(LOAD_CX_MS_KEY, &msCompData); + /* read MS compensation data */ + if (ret < 0) { + logError(1, + "%s production_test_data: readMutualSenseCompensationData failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + return ret | ERROR_PROD_TEST_DATA; + } + + if (msCompData.header.force_node > msCompData.header.sense_node) + /* the meaningful data are only in the first row, + * the other rows are only a copy of the first one */ + num_keys = msCompData.header.force_node; + else + num_keys = msCompData.header.sense_node; + + logError(0, "%s MS KEY CX1 TEST:\n", tag); + if (todo->MutualKeyCx1 == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + MS_KEY_CX1_MIN_MAX, &thresholds, + &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_KEY_CX1_MIN_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + container = (short)msCompData.cx1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], + thresholds[1]); /* check the limits */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax MS CX1 failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS KEY CX1 TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s MS KEY CX1 TEST:.................OK\n\n", + tag); + } else + logError(0, "%s MS KEY CX1 TEST:.................SKIPPED\n\n", + tag); + + kfree(thresholds); + thresholds = NULL; + + logError(0, "%s MS KEY CX2 TEST:\n", tag); + if (todo->MutualKeyCx2 == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + MS_KEY_CX2_MAP_MIN, + &thresholds_min, &trows, + &tcolumns); + /* load min thresholds */ + if (ret < 0 || (trows != msCompData.header.force_node || + tcolumns != msCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_KEY_CX2_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, &limit_file, + MS_KEY_CX2_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load max thresholds */ + if (ret < 0 || (trows != msCompData.header.force_node || + tcolumns != msCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_KEY_CX2_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMap(msCompData.node_data, + msCompData.header.force_node, + msCompData.header.sense_node, + thresholds_min, thresholds_max); + /* check the limits */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap MS KEY CX2 failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS KEY CX2 TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s MS KEY CX2 TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, "%s MS CX2 TEST:.................SKIPPED\n\n", + tag); + + /* START OF TOTAL CHECK */ + logError(0, "%s MS KEY TOTAL CX TEST:\n", tag); + + if (todo->MutualKeyCxTotal == 1) { + ret = readTotMutualSenseCompensationData( + LOAD_PANEL_CX_TOT_MS_KEY, &totCompData); + if (ret < 0) { + logError(1, + "%s production_test_data: computeTotalCx failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, &limit_file, + MS_KEY_TOTAL_CX_MAP_MIN, + &thresholds_min, &trows, + &tcolumns); + /* load min thresholds */ + if (ret < 0 || (trows != totCompData.header.force_node || + tcolumns != totCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_KEY_TOTAL_CX_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, &limit_file, + MS_KEY_TOTAL_CX_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load max thresholds */ + if (ret < 0 || (trows != totCompData.header.force_node || + tcolumns != totCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_KEY_TOTAL_CX_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapTotal(totCompData.node_data, + totCompData.header.force_node, + totCompData.header.sense_node, + thresholds_min, thresholds_max); + /* check the limits */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap MS TOTAL KEY CX TEST failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS KEY TOTAL CX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s MS KEY TOTAL CX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + + kfree(totCompData.node_data); + totCompData.node_data = NULL; + } else + logError(0, + "%s MS KEY TOTAL CX TEST:.................SKIPPED\n", + tag); + + +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, + "%s MS KEY CX testes finished!.................OK\n", + tag); + kfree(msCompData.node_data); + msCompData.node_data = NULL; + return OK; + } else { + print_frame_i8("MS Key Init Data (Cx2) =", array1dTo2d_i8( + msCompData.node_data, + msCompData.node_data_size, + msCompData.header.sense_node), + msCompData.header.force_node, + msCompData.header.sense_node); + logError(0, + "%s MS Key CX testes finished!.................FAILED fails_count = %d\n\n", + tag, count_fail); + if (thresholds != NULL) + kfree(thresholds); + if (thresholds_min != NULL) + kfree(thresholds_min); + if (thresholds_max != NULL) + kfree(thresholds_max); + if (msCompData.node_data != NULL) + kfree(msCompData.node_data); + if (totCompData.node_data != NULL) + kfree(totCompData.node_data); + return ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA; + } + +ERROR_LIMITS: + if (thresholds != NULL) + kfree(thresholds); + if (thresholds_min != NULL) + kfree(thresholds_min); + if (thresholds_max != NULL) + kfree(thresholds_max); + if (msCompData.node_data != NULL) + kfree(msCompData.node_data); + if (totCompData.node_data != NULL) + kfree(totCompData.node_data); + return ret; +} + +/** + * Perform all the tests selected in a TestTodo variable related to MS LowPower + * Init data (touch, keys etc..) + * @param path_limits name of Production Limit file to load or + * "NULL" if the limits data should be loaded by a .h file + * @param stop_on_fail if 1, the test flow stops at the first data check + * failure otherwise it keeps going performing all the selected test + * @param todo pointer to a TestToDo variable which select the test to do + * @return OK if success or an error code which specify the type of error + */ +int production_test_ms_cx_lp(char *path_limits, int stop_on_fail, TestToDo *todo) +{ + int ret; + int count_fail = 0; + + int *thresholds = NULL; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + int trows, tcolumns; + + MutualSenseData msCompData; + TotMutualSenseData totCompData; + + u8 *adjhor = NULL; + + u8 *adjvert = NULL; + + u16 container; + /* u16 *total_cx = NULL; */ + u16 *total_adjhor = NULL; + u16 *total_adjvert = NULL; + + + /* MS CX TEST */ + logError(0, "%s\n", tag); + logError(0, "%s MS LP CX Testes are starting...\n", tag); + + ret = readMutualSenseCompensationData(LOAD_CX_MS_LOW_POWER, &msCompData); + /* read MS compensation data */ + if (ret < 0) { + logError(1, + "%s production_test_data: readMutualSenseCompensationData failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + return ret | ERROR_PROD_TEST_DATA; + } + + ret = readTotMutualSenseCompensationData(LOAD_PANEL_CX_TOT_MS_LOW_POWER, + &totCompData); + /* read TOT MS compensation data */ + if (ret < 0) { + logError(1, + "%s production_test_data: readTotMutualSenseCompensationData failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + kfree(msCompData.node_data); + msCompData.node_data = NULL; + return ret | ERROR_PROD_TEST_DATA; + } + + logError(0, "%s MS LP CX1 TEST:\n", tag); + if (todo->MutualCx1LP == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + MS_CX1_LP_MIN_MAX, &thresholds, + &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_CX1_LP_MIN_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + container = (u16)msCompData.cx1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], + thresholds[1]); + /* check the limits */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax MS LP CX1 failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, "%s MS LP CX1 TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, "%s MS LP CX1 TEST:.................OK\n\n", + tag); + } else + logError(0, "%s MS LP CX1 TEST:.................SKIPPED\n\n", + tag); + + kfree(thresholds); + thresholds = NULL; + + logError(0, "%s MS LP CX2 MIN MAX TEST:\n", tag); + if (todo->MutualCx2LP == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + MS_CX2_LP_MAP_MIN, &thresholds_min, + &trows, &tcolumns); + /* load min thresholds */ + if (ret < 0 || (trows != msCompData.header.force_node || + tcolumns != msCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_CX2_LP_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, &limit_file, + MS_CX2_LP_MAP_MAX, &thresholds_max, + &trows, &tcolumns); + /* load max thresholds */ + if (ret < 0 || (trows != msCompData.header.force_node || + tcolumns != msCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_CX2_LP_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMap(msCompData.node_data, + msCompData.header.force_node, + msCompData.header.sense_node, + thresholds_min, thresholds_max); + /* check the limits */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap MS LP CX2 MIN MAX failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS LP CX2 MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s MS LP CX2 MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s MS LP CX2 MIN MAX TEST:.................SKIPPED\n\n", + tag); + + logError(0, "%s MS LP CX2 ADJ TEST:\n", tag); + if (todo->MutualCx2TotalLP == 1) { + /* MS CX2 ADJ HORIZ */ + logError(0, "%s MS LP CX2 ADJ HORIZ TEST:\n", tag); + + ret = computeAdjHoriz(msCompData.node_data, + msCompData.header.force_node, + msCompData.header.sense_node, + &adjhor); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjHoriz failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s MS LP CX2 ADJ HORIZ computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, &limit_file, + MS_CX2_ADJH_LP_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node || + tcolumns != msCompData.header.sense_node - 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_CX2_ADJH_LP_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdj(adjhor, msCompData.header.force_node, + msCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMapAdj CX2 ADJH LP failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS LP CX2 ADJ HORIZ TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s MS LP CX2 ADJ HORIZ TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjhor); + adjhor = NULL; + + /* MS CX2 ADJ VERT */ + logError(0, "%s MS LP CX2 ADJ VERT TEST:\n", tag); + + ret = computeAdjVert(msCompData.node_data, + msCompData.header.force_node, + msCompData.header.sense_node, + &adjvert); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjVert failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s MS LP CX2 ADJ VERT computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, &limit_file, + MS_CX2_ADJV_LP_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + if (ret < 0 || (trows != msCompData.header.force_node - 1 || + tcolumns != msCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_CX2_ADJV_LP_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdj(adjvert, msCompData.header.force_node - + 1, msCompData.header.sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMapAdj CX2 ADJV LP failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS LP CX2 ADJ HORIZ TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s MS LP CX2 ADJ VERT TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjvert); + adjvert = NULL; + } else + logError(0, "%s MS LP CX2 ADJ TEST:.................SKIPPED\n\n", + tag); + + /* START OF TOTAL CHECK */ + logError(0, "%s MS TOTAL LP CX TEST:\n", tag); + + if (todo->MutualCxTotalLP == 1 || todo->MutualCxTotalAdjLP == 1) { + logError(0, "%s MS TOTAL LP CX MIN MAX TEST:\n", tag); + if (todo->MutualCxTotalLP == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + MS_TOTAL_CX_LP_MAP_MIN, + &thresholds_min, + &trows, &tcolumns); + /* load min thresholds */ + if (ret < 0 || (trows != + totCompData.header.force_node || + tcolumns != + totCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_LP_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, + &limit_file, + MS_TOTAL_CX_LP_MAP_MAX, + &thresholds_max, + &trows, &tcolumns); + /* load max thresholds */ + if (ret < 0 || (trows != + totCompData.header.force_node || + tcolumns != + totCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_LP_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapTotal(totCompData.node_data, + totCompData.header.force_node, + totCompData.header.sense_node, + thresholds_min, + thresholds_max); + /* check the limits */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap MS TOTAL CX LP TEST failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS TOTAL CX LP MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s MS TOTAL CX LP MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s MS TOTAL CX LP MIN MAX TEST:.................SKIPPED\n\n", + tag); + + + logError(0, "%s MS TOTAL CX ADJ LP TEST:\n", tag); + if (todo->MutualCxTotalAdjLP == 1) { + /* MS TOTAL CX ADJ HORIZ */ + logError(0, "%s MS TOTAL CX ADJ HORIZ LP TEST:\n", tag); + + ret = computeAdjHorizTotal(totCompData.node_data, + totCompData.header.force_node, + totCompData.header.sense_node, + &total_adjhor); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjHoriz failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s MS TOTAL CX ADJ HORIZ LP computed!\n", + tag); + + ret = parseProductionTestLimits(path_limits, + &limit_file, + MS_TOTAL_CX_ADJH_LP_MAP_MAX, + &thresholds_max, + &trows, &tcolumns); + if (ret < 0 || (trows != + totCompData.header.force_node || + tcolumns != + totCompData.header.sense_node - 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_ADJH_LP_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdjTotal(total_adjhor, + totCompData.header. + force_node, + totCompData.header. + sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMapAdj MS TOTAL CX ADJH LP failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS TOTAL CX ADJ HORIZ LP TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s MS TOTAL CX ADJ HORIZ LP TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjhor); + total_adjhor = NULL; + + /* MS TOTAL CX ADJ VERT */ + logError(0, "%s MS TOTAL CX ADJ VERT LP TEST:\n", tag); + + ret = computeAdjVertTotal(totCompData.node_data, + totCompData.header.force_node, + totCompData.header.sense_node, + &total_adjvert); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjVert failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s MS TOTAL CX ADJ VERT LP computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, + &limit_file, + MS_TOTAL_CX_ADJV_LP_MAP_MAX, + &thresholds_max, + &trows, &tcolumns); + if (ret < 0 || (trows != totCompData.header.force_node - + 1 || tcolumns != + totCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits MS_TOTAL_CX_ADJV_LP_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdjTotal(total_adjvert, + totCompData.header. + force_node - 1, + totCompData.header. + sense_node - 1, + thresholds_max); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMapAdj MS TOTAL CX ADJV failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s MS TOTAL CX ADJ HORIZ LP TEST:.................FAIL\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s MS TOTAL CX ADJ VERT LP TEST:.................OK\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjvert); + total_adjvert = NULL; + } else + logError(0, + "%s MS TOTAL CX ADJ LP TEST:.................SKIPPED\n", + tag); + + kfree(totCompData.node_data); + totCompData.node_data = NULL; + } else + logError(0, "%s MS TOTAL CX LP TEST:.................SKIPPED\n", + tag); + + + +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, "%s MS LP CX testes finished!.................OK\n", + tag); + kfree(msCompData.node_data); + msCompData.node_data = NULL; + return OK; + } else { + print_frame_i8("MS LP Init Data (Cx2) =", array1dTo2d_i8( + msCompData.node_data, + msCompData.node_data_size, + msCompData.header.sense_node), + msCompData.header.force_node, + msCompData.header.sense_node); + print_frame_short(" TOT MS LP Init Data (Cx) =", array1dTo2d_short( + totCompData.node_data, + totCompData.node_data_size, + totCompData.header.sense_node), + totCompData.header.force_node, + totCompData.header.sense_node); + logError(0, + "%s MS LP CX testes finished!.................FAILED fails_count = %d\n\n", + tag, count_fail); + if (thresholds != NULL) + kfree(thresholds); + if (thresholds_min != NULL) + kfree(thresholds_min); + if (thresholds_max != NULL) + kfree(thresholds_max); + if (adjhor != NULL) + kfree(adjhor); + if (adjvert != NULL) + kfree(adjvert); + if (totCompData.node_data != NULL) + kfree(totCompData.node_data); + if (total_adjhor != NULL) + kfree(total_adjhor); + if (total_adjvert != NULL) + kfree(total_adjvert); + if (msCompData.node_data != NULL) + kfree(msCompData.node_data); + return ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA; + } + +ERROR_LIMITS: + if (thresholds != NULL) + kfree(thresholds); + if (thresholds_min != NULL) + kfree(thresholds_min); + if (thresholds_max != NULL) + kfree(thresholds_max); + if (adjhor != NULL) + kfree(adjhor); + if (adjvert != NULL) + kfree(adjvert); + if (totCompData.node_data != NULL) + kfree(totCompData.node_data); + if (total_adjhor != NULL) + kfree(total_adjhor); + if (total_adjvert != NULL) + kfree(total_adjvert); + if (msCompData.node_data != NULL) + kfree(msCompData.node_data); + return ret; +} + +/** + * Perform all the test selected in a TestTodo variable related to SS raw data + * (touch, keys etc..) + * @param path_limits name of Production Limit file to load or + * "NULL" if the limits data should be loaded by a .h file + * @param stop_on_fail if 1, the test flow stops at the first data check + * failure + * otherwise it keeps going performing all the selected test + * @param todo pointer to a TestToDo variable which select the test to do + * @return OK if success or an error code which specify the type of error + */ +int production_test_ss_raw(char *path_limits, int stop_on_fail, TestToDo *todo) +{ + int ret; + int count_fail = 0; + int rows, columns; + + SelfSenseFrame ssRawFrame; + + int *thresholds = NULL; + int trows, tcolumns; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + + /* SS TEST */ + logError(0, "%s\n", tag); + logError(0, "%s SS RAW Testes are starting...\n", tag); + + /************** Self Sense Test **************/ + + logError(0, "%s Getting SS Frame...\n", tag); + ret = setScanMode(SCAN_MODE_LOCKED, LOCKED_ACTIVE); + msleep(WAIT_FOR_FRESH_FRAMES); + ret |= setScanMode(SCAN_MODE_ACTIVE, 0x00); + msleep(WAIT_AFTER_SENSEOFF); + ret |= getSSFrame3(SS_RAW, &ssRawFrame); + if (ret < 0) { + logError(1, + "%s production_test_data: getSSFrame failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + return ret | ERROR_PROD_TEST_DATA; + } + + /* SS RAW (PROXIMITY) FORCE TEST */ + logError(0, "%s SS RAW FORCE TEST:\n", tag); + + + + if (todo->SelfForceRaw == 1 || todo->SelfForceRawGap == 1 + || todo->SelfForceRawMap == 1) { + columns = 1; /* there are no data for the sense channels + * because is a force frame */ + rows = ssRawFrame.header.force_node; + + logError(0, "%s SS RAW FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceRaw == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_RAW_FORCE_MIN_MAX, + &thresholds, &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_RAW_FORCE_MIN_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMinMax(ssRawFrame.force_data, rows, + columns, thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax SS RAW FORCE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS RAW (PROXIMITY) FORCE MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + print_frame_short("SS Raw force frame =", + array1dTo2d_short( + ssRawFrame.force_data, + rows * + columns, + columns), rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA | + ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else + logError(0, + "%s SS RAW FORCE MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds); + thresholds = NULL; + } else + logError(0, + "%s SS RAW FORCE MIN MAX TEST:.................SKIPPED\n\n", + tag); + + logError(0, "%s SS RAW FORCE MAP MIN MAX TEST:\n", tag); + if (todo->SelfForceRawMap == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, SS_RAW_FORCE_EACH_NODE_MIN, + &thresholds_min, &trows, &tcolumns); + if (ret < OK || (trows != rows || + tcolumns != columns)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_RAW_FORCE_EACH_NODE_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + ret = parseProductionTestLimits(path_limits, + &limit_file, SS_RAW_FORCE_EACH_NODE_MAX, + &thresholds_max, &trows, &tcolumns); + if (ret < OK || (trows != rows || + tcolumns != columns)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_RAW_FORCE_EACH_NODE_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapTotal(ssRawFrame.force_data, rows, + columns, thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax SS RAW FORCE MAP failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS RAW FORCE MAP MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + print_frame_short("SS Raw force frame =", + array1dTo2d_short( + ssRawFrame.force_data, + rows * + columns, + columns), rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA | + ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else + logError(0, + "%s SS RAW FORCE MAP MIN MAX TEST:.................OK\n\n", + tag); + + if (thresholds_min != NULL) { + kfree(thresholds_min); + thresholds_min = NULL; + } + if (thresholds_max != NULL) { + kfree(thresholds_max); + thresholds_max = NULL; + } + } else + logError(0, + "%s SS RAW FORCE MAP MIN MAX TEST:.................SKIPPED\n\n", + tag); + + logError(0, "%s\n", tag); + logError(0, "%s SS RAW FORCE GAP TEST:\n", tag); + if (todo->SelfForceRawGap == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_RAW_FORCE_GAP, + &thresholds, &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_RAW_FORCE_GAP failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsGap(ssRawFrame.force_data, rows, + columns, thresholds[0]); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsGap SS RAW FORCE GAP failed... ERROR = %08X\n", + tag, ret); + logError(0, + "%s SS RAW FORCE GAP TEST:.................FAIL\n\n", + tag); + count_fail += 1; + print_frame_short("SS Raw force frame =", + array1dTo2d_short( + ssRawFrame.force_data, + rows * + columns, + columns), rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA | + ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else + logError(0, + "%s SS RAW FORCE GAP TEST:.................OK\n\n", + tag); + + kfree(thresholds); + thresholds = NULL; + } else + logError(0, + "%s SS RAW FORCE GAP TEST:.................SKIPPED\n\n", + tag); + + kfree(ssRawFrame.force_data); + ssRawFrame.force_data = NULL; + } else + logError(0, + "%s SS RAW FORCE TEST:.................SKIPPED\n\n", + tag); + + logError(0, "%s\n", tag); + /* SS RAW (PROXIMITY) SENSE TEST */ + logError(0, "%s SS RAW SENSE TEST:\n", tag); + + if (todo->SelfSenseRaw == 1 || todo->SelfSenseRawGap == 1 || + todo->SelfSenseRawMap == 1) { + columns = ssRawFrame.header.sense_node; + rows = 1; /* there are no data for the force channels + * because is a sense frame */ + + logError(0, "%s SS RAW SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseRaw == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_RAW_SENSE_MIN_MAX, + &thresholds, &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_RAW_SENSE_MIN_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMinMax(ssRawFrame.sense_data, rows, + columns, thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax SS RAW SENSE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS RAW SENSE MIN MAX TEST:.................FAIL\n", + tag); + count_fail += 1; + print_frame_short("SS Raw sense frame =", + array1dTo2d_short( + ssRawFrame.sense_data, + rows * + columns, + columns), rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA | + ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else + logError(0, + "%s SS RAW SENSE MIN MAX TEST:.................OK\n", + tag); + + kfree(thresholds); + thresholds = NULL; + } else + logError(0, + "%s SS RAW SENSE MIN MAX TEST:.................SKIPPED\n", + tag); + + logError(0, "%s SS RAW SENSE MAP MIN MAX TEST:\n", tag); + if (todo->SelfSenseRawMap == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, SS_RAW_SENSE_EACH_NODE_MIN, + &thresholds_min, &trows, &tcolumns); + if (ret < OK || (trows != rows || + tcolumns != columns)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_RAW_SENSE_EACH_NODE_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + ret = parseProductionTestLimits(path_limits, + &limit_file, SS_RAW_SENSE_EACH_NODE_MAX, + &thresholds_max, &trows, &tcolumns); + if (ret < OK || (trows != rows || + tcolumns != columns)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_RAW_SENSE_EACH_NODE_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapTotal(ssRawFrame.sense_data, rows, + columns, thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax SS RAW SENSE MAP failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS RAW SENSE MAP MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + print_frame_short("SS Raw sense frame =", + array1dTo2d_short( + ssRawFrame.sense_data, + rows * + columns, + columns), rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA | + ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else + logError(0, + "%s SS RAW SENSE MAP MIN MAX TEST:.................OK\n\n", + tag); + + if (thresholds_min != NULL) { + kfree(thresholds_min); + thresholds_min = NULL; + } + if (thresholds_max != NULL) { + kfree(thresholds_max); + thresholds_max = NULL; + } + } else + logError(0, + "%s SS RAW SENSE MAP MIN MAX TEST:.................SKIPPED\n\n", + tag); + + logError(0, "%s\n", tag); + logError(0, "%s SS RAW SENSE GAP TEST:\n", tag); + if (todo->SelfSenseRawGap == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_RAW_SENSE_GAP, + &thresholds, &trows, + &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_RAW_SENSE_GAP failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsGap(ssRawFrame.sense_data, rows, + columns, thresholds[0]); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsGap SS RAW SENSE GAP failed... ERROR = %08X\n", + tag, ret); + logError(0, + "%s SS RAW SENSE GAP TEST:.................FAIL\n", + tag); + count_fail += 1; + print_frame_short("SS Raw sense frame =", + array1dTo2d_short( + ssRawFrame.sense_data, + rows * + columns, + columns), rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA | + ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else + logError(0, + "%s SS RAW SENSE GAP TEST:.................OK\n", + tag); + + kfree(thresholds); + thresholds = NULL; + } else + logError(0, + "%s SS RAW SENSE GAP TEST:.................SKIPPED\n", + tag); + + kfree(ssRawFrame.sense_data); + ssRawFrame.sense_data = NULL; + } else + logError(0, + "%s SS RAW SENSE TEST:.................SKIPPED\n\n", + tag); + + ret = production_test_ss_raw_lp(path_limits, stop_on_fail, todo); + if (ret < OK) { + logError(1, + "%s production_test_data: production_test_ss_raw_lp failed... ERROR = %08X\n", + tag, ret); + count_fail += 1; + } + + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, "%s SS RAW testes finished!.................OK\n\n", + tag); + return OK; + } else { + logError(0, + "%s SS RAW testes finished!.................FAILED fails_count = %d\n\n", + tag, count_fail); + return ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA; + } + +ERROR_LIMITS: + if (ssRawFrame.force_data != NULL) + kfree(ssRawFrame.force_data); + if (ssRawFrame.sense_data != NULL) + kfree(ssRawFrame.sense_data); + if (thresholds != NULL) + kfree(thresholds); + if (thresholds_min != NULL) + kfree(thresholds_min); + if (thresholds_max != NULL) + kfree(thresholds_max); + + return ret; +} + + +/** + * Perform all the test selected in a TestTodo variable related to SS raw data + * low power + * @param path_limits name of Production Limit file to load or + * "NULL" if the limits data should be loaded by a .h file + * @param stop_on_fail if 1, the test flow stops at the first data check + * failure + * otherwise it keeps going performing all the selected test + * @param todo pointer to a TestToDo variable which select the test to do + * @return OK if success or an error code which specify the type of error + */ +int production_test_ss_raw_lp(char *path_limits, int stop_on_fail, + TestToDo *todo) +{ + int ret; + int count_fail = 0; + int rows, columns; + + SelfSenseFrame ssRawFrame; + + int *thresholds = NULL; + int trows, tcolumns; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + + /* SS TEST */ + logError(0, "%s\n", tag); + logError(0, "%s SS RAW LP Testes are starting...\n", tag); + + /************** Self Sense Test **************/ + + logError(0, "%s Getting SS LP Frame...\n", tag); + ret = setScanMode(SCAN_MODE_LOCKED, LOCKED_LP_DETECT); + msleep(WAIT_FOR_FRESH_FRAMES); + ret |= setScanMode(SCAN_MODE_ACTIVE, 0x00); + msleep(WAIT_AFTER_SENSEOFF); + ret |= getSSFrame3(SS_DETECT_RAW, &ssRawFrame); + if (ret < 0) { + logError(1, + "%s production_test_data: getSSFrame failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + return ret | ERROR_PROD_TEST_DATA; + } + + /* SS RAW (PROXIMITY) FORCE TEST */ + logError(0, "%s SS RAW LP FORCE TEST:\n", tag); + + + + if ((todo->SelfForceRawLP == 1 || todo->SelfForceRawGapLP == 1 || + todo->SelfForceRawMapLP == 1) && + ssRawFrame.header.force_node != 0) { + columns = 1; /* there are no data for the sense channels + * because is a force frame */ + rows = ssRawFrame.header.force_node; + + logError(0, "%s SS RAW LP FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceRawLP == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_RAW_LP_FORCE_MIN_MAX, + &thresholds, + &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_RAW_FORCE_MIN_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMinMax(ssRawFrame.force_data, rows, + columns, thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax SS RAW FORCE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS RAW LP FORCE MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + print_frame_short("SS Raw LP force frame =", + array1dTo2d_short( + ssRawFrame.force_data, + rows * + columns, + columns), rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA | + ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else + logError(0, + "%s SS RAW LP FORCE MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds); + thresholds = NULL; + } else + logError(0, + "%s SS RAW LP FORCE MIN MAX TEST:.................SKIPPED\n\n", + tag); + + logError(0, "%s SS RAW LP FORCE MAP MIN MAX TEST:\n", tag); + if (todo->SelfForceRawMapLP == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, SS_RAW_LP_FORCE_EACH_NODE_MIN, + &thresholds_min, &trows, &tcolumns); + if (ret < OK || (trows != rows || + tcolumns != columns)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_RAW_LP_FORCE_EACH_NODE_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + ret = parseProductionTestLimits(path_limits, + &limit_file, SS_RAW_LP_FORCE_EACH_NODE_MAX, + &thresholds_max, &trows, &tcolumns); + if (ret < OK || (trows != rows || + tcolumns != columns)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_RAW_LP_FORCE_EACH_NODE_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapTotal(ssRawFrame.force_data, rows, + columns, thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax SS RAW LP FORCE MAP failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS RAW LP FORCE MAP MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + print_frame_short("SS Raw LP force frame =", + array1dTo2d_short( + ssRawFrame.force_data, + rows * + columns, + columns), rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA | + ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else + logError(0, + "%s SS RAW FORCE LP MAP MIN MAX TEST:.................OK\n\n", + tag); + + if (thresholds_min != NULL) { + kfree(thresholds_min); + thresholds_min = NULL; + } + if (thresholds_max != NULL) { + kfree(thresholds_max); + thresholds_max = NULL; + } + } else + logError(0, + "%s SS RAW FORCE LP MAP MIN MAX TEST:.................SKIPPED\n\n", + tag); + + logError(0, "%s\n", tag); + logError(0, "%s SS RAW LP FORCE GAP TEST:\n", tag); + if (todo->SelfForceRawGapLP == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_RAW_LP_FORCE_GAP, + &thresholds, &trows, + &tcolumns); + if (ret < OK || (trows != 1 || tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_RAW_FORCE_GAP failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsGap(ssRawFrame.force_data, rows, + columns, thresholds[0]); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsGap SS RAW FORCE GAP failed... ERROR = %08X\n", + tag, ret); + logError(0, + "%s SS RAW LP FORCE GAP TEST:.................FAIL\n\n", + tag); + count_fail += 1; + print_frame_short("SS Raw LP force frame =", + array1dTo2d_short( + ssRawFrame.force_data, + rows * + columns, + columns), rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA | + ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else + logError(0, + "%s SS RAW LP FORCE GAP TEST:.................OK\n\n", + tag); + + kfree(thresholds); + thresholds = NULL; + } else + logError(0, + "%s SS RAW LP FORCE GAP TEST:.................SKIPPED\n\n", + tag); + + kfree(ssRawFrame.force_data); + ssRawFrame.force_data = NULL; + } else + logError(0, + "%s SS RAW LP FORCE TEST:.................SKIPPED\n\n", + tag); + + logError(0, "%s\n", tag); + /* SS RAW (PROXIMITY) SENSE TEST */ + logError(0, "%s SS RAW LP SENSE TEST:\n", tag); + + if ((todo->SelfSenseRawLP == 1 || todo->SelfSenseRawGapLP == 1 || + todo->SelfSenseRawMapLP == 1) && + ssRawFrame.header.sense_node != 0) { + columns = ssRawFrame.header.sense_node; + rows = 1; /* there are no data for the force channels + * because is a sense frame */ + + logError(0, "%s SS RAW LP SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseRawLP == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_RAW_LP_SENSE_MIN_MAX, + &thresholds, + &trows, &tcolumns); + if (ret < OK || (trows != 1 || tcolumns != 2)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_RAW_LP_SENSE_MIN_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMinMax(ssRawFrame.sense_data, rows, + columns, thresholds[0], + thresholds[1]); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax SS RAW LP SENSE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS RAW LP SENSE MIN MAX TEST:.................FAIL\n", + tag); + count_fail += 1; + print_frame_short("SS Raw LP sense frame =", + array1dTo2d_short( + ssRawFrame.sense_data, + rows * + columns, + columns), rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA | + ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else + logError(0, + "%s SS RAW SENSE MIN MAX TEST:.................OK\n", + tag); + + kfree(thresholds); + thresholds = NULL; + } else + logError(0, + "%s SS RAW LP SENSE MIN MAX TEST:.................SKIPPED\n", + tag); + + logError(0, "%s SS RAW LP SENSE MAP MIN MAX TEST:\n", tag); + if (todo->SelfSenseRawMapLP == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, SS_RAW_LP_SENSE_EACH_NODE_MIN, + &thresholds_min, &trows, &tcolumns); + if (ret < OK || (trows != rows || + tcolumns != columns)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_RAW_LP_SENSE_EACH_NODE_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + ret = parseProductionTestLimits(path_limits, + &limit_file, SS_RAW_LP_SENSE_EACH_NODE_MAX, + &thresholds_max, &trows, &tcolumns); + if (ret < OK || (trows != rows || + tcolumns != columns)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_RAW_LP_SENSE_EACH_NODE_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapTotal(ssRawFrame.sense_data, rows, + columns, thresholds_min, + thresholds_max); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax SS RAW LP SENSE MAP failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS RAW LP SENSE MAP MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + print_frame_short("SS Raw LP sense frame =", + array1dTo2d_short( + ssRawFrame.sense_data, + rows * + columns, + columns), rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA | + ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else + logError(0, + "%s SS RAW LP SENSE MAP MIN MAX TEST:.................OK\n\n", + tag); + + if (thresholds_min != NULL) { + kfree(thresholds_min); + thresholds_min = NULL; + } + if (thresholds_max != NULL) { + kfree(thresholds_max); + thresholds_max = NULL; + } + } else + logError(0, + "%s SS RAW LP SENSE MAP MIN MAX TEST:.................SKIPPED\n\n", + tag); + + logError(0, "%s\n", tag); + logError(0, "%s SS RAW LP SENSE GAP TEST:\n", tag); + if (todo->SelfSenseRawGapLP == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_RAW_LP_SENSE_GAP, + &thresholds, &trows, + &tcolumns); + if (ret < OK || (trows != 1 || tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_RAW_LP_SENSE_GAP failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsGap(ssRawFrame.sense_data, rows, + columns, thresholds[0]); + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsGap SS RAW LP SENSE GAP failed... ERROR = %08X\n", + tag, ret); + logError(0, + "%s SS RAW LP SENSE GAP TEST:.................FAIL\n", + tag); + count_fail += 1; + print_frame_short("SS Raw LP sense frame =", + array1dTo2d_short( + ssRawFrame.sense_data, + rows * + columns, + columns), rows, + columns); + if (stop_on_fail) { + ret = ERROR_PROD_TEST_DATA | + ERROR_TEST_CHECK_FAIL; + goto ERROR_LIMITS; + } + } else + logError(0, + "%s SS RAW LP SENSE GAP TEST:.................OK\n", + tag); + + kfree(thresholds); + thresholds = NULL; + } else + logError(0, + "%s SS RAW LP SENSE GAP TEST:.................SKIPPED\n", + tag); + + kfree(ssRawFrame.sense_data); + ssRawFrame.sense_data = NULL; + } else + logError(0, + "%s SS RAW LP SENSE TEST:.................SKIPPED\n\n", + tag); + + logError(0, "%s\n", tag); + if (count_fail == 0) { + logError(0, + "%s SS RAW LP testes finished!.................OK\n\n", + tag); + return OK; + } else { + logError(0, + "%s SS RAW LP testes finished!.................FAILED fails_count = %d\n\n", + tag, count_fail); + return ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA; + } + +ERROR_LIMITS: + if (ssRawFrame.force_data != NULL) + kfree(ssRawFrame.force_data); + if (ssRawFrame.sense_data != NULL) + kfree(ssRawFrame.sense_data); + if (thresholds != NULL) + kfree(thresholds); + if (thresholds_min != NULL) + kfree(thresholds_min); + if (thresholds_max != NULL) + kfree(thresholds_max); + return ret; +} + +/** + * Perform all the tests selected in a TestTodo variable related to SS Init + * data (touch, keys etc..) + * @param path_limits name of Production Limit file to load or + * "NULL" if the limits data should be loaded by a .h file + * @param stop_on_fail if 1, the test flow stops at the first data check + * failure + * otherwise it keeps going performing all the selected test + * @param todo pointer to a TestToDo variable which select the test to do + * @return OK if success or an error code which specify the type of error + */ +int production_test_ss_ix_cx(char *path_limits, int stop_on_fail, + TestToDo *todo) +{ + int ret; + int count_fail = 0; + + int *thresholds = NULL; + int trows, tcolumns; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + + SelfSenseData ssCompData; + TotSelfSenseData totCompData; + + u8 *adjhor = NULL; + u8 *adjvert = NULL; + + short container; + + u16 *total_adjhor = NULL; + u16 *total_adjvert = NULL; + + logError(0, "%s\n", tag); + logError(0, "%s SS IX CX testes are starting...\n", tag); + ret = readSelfSenseCompensationData(LOAD_CX_SS_TOUCH, &ssCompData); + /* read the SS compensation data */ + if (ret < 0) { + logError(1, + "%s production_test_data: readSelfSenseCompensationData failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + return ret | ERROR_PROD_TEST_DATA; + } + + ret = readTotSelfSenseCompensationData(LOAD_PANEL_CX_TOT_SS_TOUCH, + &totCompData); + /* read the TOT SS compensation data */ + if (ret < 0) { + logError(1, + "%s production_test_data: readTotSelfSenseCompensationData failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + kfree(ssCompData.ix2_fm); + kfree(ssCompData.ix2_sn); + kfree(ssCompData.cx2_fm); + kfree(ssCompData.cx2_sn); + return ret | ERROR_PROD_TEST_DATA; + } + + /************* SS FORCE IX **************/ + /* SS IX1 FORCE TEST */ + logError(0, "%s SS IX1 FORCE TEST:\n", tag); + if (todo->SelfForceIx1 == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_IX1_FORCE_MIN_MAX, + &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_IX1_FORCE_MIN_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + container = (short)ssCompData.f_ix1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], + thresholds[1]); + /* check the limits */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax SS IX1 FORCE TEST failed... ERROR COUNT = %d\n", + tag, ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS IX1 FORCE TEST:.................OK\n\n", + tag); + } else + logError(0, + "%s SS IX1 FORCE TEST:.................SKIPPED\n\n", + tag); + + kfree(thresholds); + thresholds = NULL; + /* SS IX2 FORCE TEST */ + logError(0, "%s SS IX2 FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceIx2 == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_IX2_FORCE_MAP_MIN, + &thresholds_min, &trows, + &tcolumns); + /* load the min thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_IX2_FORCE_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_IX2_FORCE_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_IX2_FORCE_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapFromU(ssCompData.ix2_fm, + ssCompData.header.force_node, 1, + thresholds_min, + thresholds_max); /* check the + * values with + * thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS IX2 FORCE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS IX2 FORCE MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS IX2 FORCE MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s SS IX2 FORCE MIN MAX TEST:.................SKIPPED\n\n", + tag); + + logError(0, "%s SS IX2 FORCE ADJ TEST:\n", tag); + if (todo->SelfForceIx2Adj == 1) { + /* SS IX2 FORCE ADJV TEST */ + logError(0, "%s SS IX2 FORCE ADJVERT TEST:\n", tag); + ret = computeAdjVertFromU(ssCompData.ix2_fm, + ssCompData.header.force_node, 1, + &adjvert); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjVert SS IX2 FORCE ADJV failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS IX2 FORCE ADJV computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_IX2_FORCE_ADJV_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); /* load the max + * thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node - 1 || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_IX2_FORCE_ADJV_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdj(adjvert, ssCompData.header.force_node - + 1, 1, thresholds_max); /* check the + * values with + * thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS IX2 FORCE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS IX2 FORCE ADJV TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS IX2 FORCE ADJV TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjvert); + adjvert = NULL; + } else + logError(0, + "%s SS IX2 FORCE ADJ TEST:.................SKIPPED\n\n", + tag); + + /* SS TOTAL FORCE IX */ + logError(0, "%s SS TOTAL IX FORCE TEST:\n", tag); + if (todo->SelfForceIxTotal == 1 || todo->SelfForceIxTotalAdj == 1) { + logError(0, "%s SS TOTAL IX FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceIxTotal == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_IX_FORCE_MAP_MIN, + &thresholds_min, + &trows, &tcolumns); + /* load the min thresholds */ + if (ret < 0 || (trows != + totCompData.header.force_node || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_FORCE_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_IX_FORCE_MAP_MAX, + &thresholds_max, + &trows, &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != + totCompData.header.force_node || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_FORCE_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapTotalFromU(totCompData.ix_fm, + totCompData.header. + force_node, 1, + thresholds_min, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS TOTAL IX FORCE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS TOTAL IX FORCE MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS TOTAL IX FORCE MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s SS TOTAL IX FORCE MIN MAX TEST:.................SKIPPED\n", + tag); + + logError(0, "%s SS TOTAL IX FORCE ADJ TEST:\n", tag); + if (todo->SelfForceIxTotalAdj == 1) { + /* SS TOTAL IX FORCE ADJV TEST */ + logError(0, "%s SS TOTAL IX FORCE ADJVERT TEST:\n", + tag); + ret = computeAdjVertTotalFromU(totCompData.ix_fm, + totCompData.header. + force_node, 1, + &total_adjvert); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjVert SS TOTAL IX FORCE ADJV failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS TOTAL IX FORCE ADJV computed!\n", + tag); + + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_IX_FORCE_ADJV_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != totCompData.header.force_node - + 1 || tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_FORCE_ADJV_MAP_MAX... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdjTotal(total_adjvert, + totCompData.header. + force_node - 1, 1, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS TOTAL IX FORCE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS TOTAL IX FORCE ADJV TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS TOTAL IX FORCE ADJV TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjvert); + total_adjvert = NULL; + } else + logError(0, + "%s SS TOTAL IX FORCE ADJ TEST:.................SKIPPED\n", + tag); + } else + logError(0, + "%s SS TOTAL IX FORCE TEST:.................SKIPPED\n\n", + tag); + + + /************** SS SENSE IX **************/ + /* SS IX1 SENSE TEST */ + logError(0, "%s SS IX1 SENSE TEST:\n", tag); + if (todo->SelfSenseIx1 == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_IX1_SENSE_MIN_MAX, + &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_IX1_SENSE_MIN_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + container = (short)ssCompData.s_ix1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], + thresholds[1]); + /* check the limits */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax SS IX1 SENSE TEST failed... ERROR COUNT = %d\n", + tag, ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS IX1 SENSE TEST:.................OK\n\n", + tag); + } else + logError(0, + "%s SS IX1 SENSE TEST:.................SKIPPED\n\n", + tag); + + kfree(thresholds); + thresholds = NULL; + /* SS IX2 SENSE TEST */ + logError(0, "%s SS IX2 SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseIx2 == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_IX2_SENSE_MAP_MIN, + &thresholds_min, &trows, + &tcolumns); + /* load the min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + ssCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_IX2_SENSE_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_IX2_SENSE_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + ssCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_IX2_SENSE_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapFromU(ssCompData.ix2_sn, 1, + ssCompData.header.sense_node, + thresholds_min, thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS IX2 SENSE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS IX2 SENSE MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS IX2 SENSE MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s SS IX2 SENSE MIN MAX TEST:.................SKIPPED\n\n", + tag); + + logError(0, "%s SS IX2 SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseIx2Adj == 1) { + /* SS IX2 SENSE ADJH TEST */ + logError(0, "%s SS IX2 SENSE ADJHORIZ TEST:\n", tag); + ret = computeAdjHorizFromU(ssCompData.ix2_sn, 1, + ssCompData.header.sense_node, + &adjhor); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjHoriz SS IX2 SENSE ADJH failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS IX2 SENSE ADJ HORIZ computed!\n", tag); + + + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_IX2_SENSE_ADJH_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + ssCompData.header.sense_node - 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_IX2_SENSE_ADJH_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdj(adjhor, 1, + ssCompData.header.sense_node - 1, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMapAdj SS IX2 SENSE ADJH failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS IX2 SENSE ADJH TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS IX2 SENSE ADJH TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjhor); + adjhor = NULL; + } else + logError(0, + "%s SS IX2 SENSE ADJ TEST:.................SKIPPED\n", + tag); + + /* SS TOTAL IX SENSE */ + logError(0, "%s SS TOTAL IX SENSE TEST:\n", tag); + if (todo->SelfSenseIxTotal == 1 || todo->SelfSenseIxTotalAdj == 1) { + logError(0, "%s SS TOTAL IX SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseIxTotal == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_IX_SENSE_MAP_MIN, + &thresholds_min, + &trows, &tcolumns); + /* load the min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + totCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_SENSE_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_IX_SENSE_MAP_MAX, + &thresholds_max, + &trows, &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + totCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_SENSE_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapTotalFromU(totCompData.ix_sn, 1, + totCompData.header. + sense_node, + thresholds_min, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS TOTAL IX SENSE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS TOTAL IX SENSE MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS TOTAL IX SENSE MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s SS TOTAL IX SENSE MIN MAX TEST:.................SKIPPED\n", + tag); + + + logError(0, "%s SS TOTAL IX SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseIxTotalAdj == 1) { + /* SS TOTAL IX SENSE ADJH TEST */ + logError(0, "%s SS TOTAL IX SENSE ADJHORIZ TEST:\n", + tag); + ret = computeAdjHorizTotalFromU(totCompData.ix_sn, 1, + totCompData.header. + sense_node, + &total_adjhor); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjHoriz SS TOTAL IX SENSE ADJH failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, + "%s SS TOTAL IX SENSE ADJ HORIZ computed!\n", + tag); + + + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_IX_SENSE_ADJH_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + totCompData.header.sense_node - 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_SENSE_ADJH_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdjTotal(total_adjhor, 1, + totCompData.header. + sense_node - 1, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMapAdj SS TOTAL IX SENSE ADJH failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS TOTAL IX SENSE ADJH TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS TOTAL IX SENSE ADJH TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjhor); + total_adjhor = NULL; + } else + logError(0, + "%s SS TOTAL IX SENSE ADJ TEST:.................SKIPPED\n", + tag); + } else + logError(0, + "%s SS TOTAL IX SENSE TEST:.................SKIPPED\n", + tag); + + /************* SS SENSE CX **************/ + /* SS CX1 FORCE TEST */ + logError(0, "%s SS CX1 FORCE TEST:\n", tag); + if (todo->SelfForceCx1 == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_CX1_FORCE_MIN_MAX, + &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_CX1_FORCE_MIN_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + container = (short)ssCompData.f_cx1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], + thresholds[1]); + /* check the limits */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax SS CX1 FORCE TEST failed... ERROR COUNT = %d\n", + tag, ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS CX1 FORCE TEST:.................OK\n\n", + tag); + kfree(thresholds); + thresholds = NULL; + } else + logError(0, + "%s SS CX1 FORCE TEST:.................SKIPPED\n\n", + tag); + + + + /* SS CX2 FORCE TEST */ + logError(0, "%s SS CX2 FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceCx2 == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_CX2_FORCE_MAP_MIN, + &thresholds_min, &trows, + &tcolumns); + /* load the min thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_CX2_FORCE_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_CX2_FORCE_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_CX2_FORCE_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMap(ssCompData.cx2_fm, + ssCompData.header.force_node, 1, + thresholds_min, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS CX2 FORCE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS CX2 FORCE MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS CX2 FORCE MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s SS CX2 FORCE MIN MAX TEST:.................SKIPPED\n", + tag); + + logError(0, "%s SS CX2 FORCE ADJ TEST:\n", tag); + if (todo->SelfForceCx2Adj == 1) { + /* SS CX2 FORCE ADJV TEST */ + logError(0, "%s SS CX2 FORCE ADJVERT TEST:\n", tag); + ret = computeAdjVert(ssCompData.cx2_fm, + ssCompData.header.force_node, 1, &adjvert); + /* compute the ADJV for CX2 FORCE */ + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjVert SS CX2 FORCE ADJV failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS CX2 FORCE ADJV computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_CX2_FORCE_ADJV_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node - 1 || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_CX2_FORCE_ADJV_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdj(adjvert, ssCompData.header.force_node - + 1, 1, thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS IX2 FORCE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS CX2 FORCE ADJV TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS CX2 FORCE ADJV TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjvert); + adjvert = NULL; + } else + logError(0, + "%s SS CX2 FORCE ADJ TEST:.................SKIPPED\n\n", + tag); + + /* SS TOTAL CX FORCE */ + logError(0, "%s SS TOTAL CX FORCE TEST:\n", tag); + if (todo->SelfForceCxTotal == 1 || todo->SelfForceCxTotalAdj == 1) { + logError(0, "%s SS TOTAL CX FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceCxTotal == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_CX_FORCE_MAP_MIN, + &thresholds_min, + &trows, &tcolumns); + /* load the min thresholds */ + if (ret < 0 || (trows != + totCompData.header.force_node || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_FORCE_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_CX_FORCE_MAP_MAX, + &thresholds_max, + &trows, &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != + totCompData.header.force_node || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_FORCE_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapTotal(totCompData.cx_fm, + totCompData.header.force_node, + 1, thresholds_min, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS TOTAL FORCE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS TOTAL FORCE MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS TOTAL FORCE MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s SS TOTAL CX FORCE MIN MAX TEST:.................SKIPPED\n", + tag); + + /* SS TOTAL CX FORCE ADJV TEST */ + logError(0, "%s SS TOTAL CX FORCE ADJ TEST:\n", tag); + if (todo->SelfForceCxTotalAdj == 1) { + logError(0, "%s SS TOTAL CX FORCE ADJVERT TEST:\n", + tag); + ret = computeAdjVertTotal(totCompData.cx_fm, + totCompData.header.force_node, + 1, &total_adjvert); + /* compute the ADJV for CX2 FORCE */ + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjVert SS TOTAL CX FORCE ADJV failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS TOTAL CX FORCE ADJV computed!\n", + tag); + + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_CX_FORCE_ADJV_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != totCompData.header.force_node - + 1 || tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_FORCE_ADJV_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdjTotal(total_adjvert, + totCompData.header. + force_node - 1, 1, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS TOTAL CX FORCE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS TOTAL CX FORCE ADJV TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS TOTAL CX FORCE ADJV TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjvert); + total_adjvert = NULL; + } else + logError(0, + "%s SS TOTAL CX FORCE ADJ TEST:.................SKIPPED\n", + tag); + } else + logError(0, + "%s SS TOTAL CX FORCE TEST:.................SKIPPED\n\n", + tag); + + + + /************* SS SENSE CX *************/ + /* SS CX1 SENSE TEST */ + logError(0, "%s SS CX1 SENSE TEST:\n", tag); + if (todo->SelfSenseCx1 == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_CX1_SENSE_MIN_MAX, + &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_CX1_SENSE_MIN_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + container = (short)ssCompData.s_cx1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], + thresholds[1]); + /* check the limits */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax SS CX1 SENSE TEST failed... ERROR COUNT = %d\n", + tag, ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS CX1 SENSE TEST:.................OK\n\n", + tag); + kfree(thresholds); + thresholds = NULL; + } else + logError(0, + "%s SS CX1 SENSE TEST:.................SKIPPED\n\n", + tag); + + + /* SS CX2 SENSE TEST */ + logError(0, "%s SS CX2 SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseCx2 == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_CX2_SENSE_MAP_MIN, + &thresholds_min, &trows, + &tcolumns); + /* load the min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + ssCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_CX2_SENSE_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_CX2_SENSE_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + ssCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_CX2_SENSE_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMap(ssCompData.cx2_sn, 1, + ssCompData.header.sense_node, + thresholds_min, thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS CX2 SENSE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS CX2 SENSE MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS CX2 SENSE MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s SS CX2 SENSE MIN MAX TEST:.................SKIPPED\n", + tag); + + logError(0, "%s SS CX2 SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseCx2Adj == 1) { + /* SS CX2 SENSE ADJH TEST */ + logError(0, "%s SS CX2 SENSE ADJHORIZ TEST:\n", tag); + ret = computeAdjHoriz(ssCompData.cx2_sn, 1, + ssCompData.header.sense_node, &adjhor); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjHoriz SS CX2 SENSE ADJH failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS CX2 SENSE ADJH computed!\n", tag); + + + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_CX2_SENSE_ADJH_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + ssCompData.header.sense_node - 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_IX2_SENSE_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdj(adjhor, 1, + ssCompData.header.sense_node - 1, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMapAdj SS CX2 SENSE ADJH failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS CX2 SENSE ADJH TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS CX2 SENSE ADJH TEST:.................OK\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjhor); + adjhor = NULL; + } else + logError(0, + "%s SS CX2 SENSE ADJ TEST:.................SKIPPED\n\n", + tag); + + /* SS TOTAL CX SENSE */ + logError(0, "%s SS TOTAL CX SENSE TEST:\n", tag); + if (todo->SelfSenseCxTotal == 1 || todo->SelfSenseCxTotalAdj == 1) { + logError(0, "%s SS TOTAL CX SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseCxTotal == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_CX_SENSE_MAP_MIN, + &thresholds_min, + &trows, &tcolumns); + /* load the min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + totCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_SENSE_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_CX_SENSE_MAP_MAX, + &thresholds_max, + &trows, &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + totCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_SENSE_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapTotal(totCompData.cx_sn, 1, + totCompData.header.sense_node, + thresholds_min, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS TOTAL CX SENSE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS TOTAL CX SENSE MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS TOTAL CX SENSE MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s SS TOTAL CX SENSE MIN MAX TEST:.................SKIPPED\n", + tag); + + + /* SS TOTAL IX SENSE ADJH TEST */ + logError(0, "%s SS TOTAL CX SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseCxTotalAdj == 1) { + logError(0, "%s SS TOTAL CX SENSE ADJHORIZ TEST:\n", + tag); + ret = computeAdjHorizTotal(totCompData.cx_sn, 1, + totCompData.header.sense_node, + &total_adjhor); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjHoriz SS TOTAL CX SENSE ADJH failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, + "%s SS TOTAL CX SENSE ADJ HORIZ computed!\n", + tag); + + + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_CX_SENSE_ADJH_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + totCompData.header.sense_node - 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_SENSE_ADJH_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdjTotal(total_adjhor, 1, + totCompData.header. + sense_node - 1, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMapAdj SS TOTAL CX SENSE ADJH failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS TOTAL CX SENSE ADJH TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS TOTAL CX SENSE ADJH TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjhor); + total_adjhor = NULL; + } else + logError(0, + "%s SS TOTAL CX SENSE ADJ TEST:.................SKIPPED\n", + tag); + } else + logError(0, + "%s SS TOTAL CX SENSE TEST:.................SKIPPED\n", + tag); + + + + if ((todo->SelfSenseCx1LP | todo->SelfSenseCx2LP | + todo->SelfSenseCx2AdjLP | todo->SelfSenseCxTotalLP | + todo->SelfSenseCxTotalAdjLP | todo->SelfSenseIx1LP | + todo->SelfSenseIx2LP | todo->SelfSenseIx2AdjLP | + todo->SelfSenseIxTotalLP | todo->SelfSenseIxTotalAdjLP) == 1) { + ret = production_test_ss_ix_cx_lp(path_limits, stop_on_fail, + todo); + if (ret < OK) { + count_fail += 1; + logError(1, + "%s production_test_data: production_test_ss_ix_cx_lp failed... ERROR = %08X\n", + tag, ret); + goto ERROR; + } + } else + logError(0, "%s SS IX CX LP TEST:.................SKIPPED\n", + tag); + +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + kfree(ssCompData.ix2_fm); + ssCompData.ix2_fm = NULL; + kfree(ssCompData.ix2_sn); + ssCompData.ix2_sn = NULL; + kfree(ssCompData.cx2_fm); + ssCompData.cx2_fm = NULL; + kfree(ssCompData.cx2_sn); + ssCompData.cx2_sn = NULL; + kfree(totCompData.ix_fm); + totCompData.ix_fm = NULL; + kfree(totCompData.ix_sn); + totCompData.ix_sn = NULL; + kfree(totCompData.cx_fm); + totCompData.cx_fm = NULL; + kfree(totCompData.cx_sn); + totCompData.cx_sn = NULL; + logError(0, + "%s SS IX CX testes finished!.................OK\n\n", + tag); + return OK; + } else { + /* print all kind of data in just one row for readability reason */ + print_frame_u8("SS Init Data Ix2_fm = ", array1dTo2d_u8( + ssCompData.ix2_fm, + ssCompData.header.force_node, 1), + ssCompData.header.force_node, 1); + print_frame_i8("SS Init Data Cx2_fm = ", array1dTo2d_i8( + ssCompData.cx2_fm, + ssCompData.header.force_node, 1), + ssCompData.header.force_node, 1); + print_frame_u8("SS Init Data Ix2_sn = ", array1dTo2d_u8( + ssCompData.ix2_sn, + ssCompData.header.sense_node, + ssCompData.header.sense_node), 1, + ssCompData.header.sense_node); + print_frame_i8("SS Init Data Cx2_sn = ", array1dTo2d_i8( + ssCompData.cx2_sn, + ssCompData.header.sense_node, + ssCompData.header.sense_node), 1, + ssCompData.header.sense_node); + print_frame_u16("TOT SS Init Data Ix_fm = ", array1dTo2d_u16( + totCompData.ix_fm, + totCompData.header.force_node, 1), + totCompData.header.force_node, 1); + print_frame_short("TOT SS Init Data Cx_fm = ", + array1dTo2d_short(totCompData.cx_fm, + totCompData.header. + force_node, 1), + totCompData.header.force_node, 1); + print_frame_u16("TOT SS Init Data Ix_sn = ", array1dTo2d_u16( + totCompData.ix_sn, + totCompData.header.sense_node, + totCompData.header.sense_node), 1, + totCompData.header.sense_node); + print_frame_short("TOT SS Init Data Cx_sn = ", + array1dTo2d_short(totCompData.cx_sn, + totCompData.header. + sense_node, + totCompData.header. + sense_node), + 1, totCompData.header.sense_node); + logError(0, + "%s SS IX CX testes finished!.................FAILED fails_count = %d\n\n", + tag, count_fail); + if (thresholds != NULL) + kfree(thresholds); + if (thresholds_min != NULL) + kfree(thresholds_min); + if (thresholds_max != NULL) + kfree(thresholds_max); + if (adjhor != NULL) + kfree(adjhor); + if (adjvert != NULL) + kfree(adjvert); + if (total_adjhor != NULL) + kfree(total_adjhor); + if (total_adjvert != NULL) + kfree(total_adjvert); + if (ssCompData.ix2_fm != NULL) + kfree(ssCompData.ix2_fm); + if (ssCompData.ix2_sn != NULL) + kfree(ssCompData.ix2_sn); + if (ssCompData.cx2_fm != NULL) + kfree(ssCompData.cx2_fm); + if (ssCompData.cx2_sn != NULL) + kfree(ssCompData.cx2_sn); + if (totCompData.ix_fm != NULL) + kfree(totCompData.ix_fm); + if (totCompData.ix_sn != NULL) + kfree(totCompData.ix_sn); + if (totCompData.cx_fm != NULL) + kfree(totCompData.cx_fm); + if (totCompData.cx_sn != NULL) + kfree(totCompData.cx_sn); + return ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA; + } + +ERROR_LIMITS: + if (thresholds != NULL) + kfree(thresholds); + if (thresholds_min != NULL) + kfree(thresholds_min); + if (thresholds_max != NULL) + kfree(thresholds_max); + if (adjhor != NULL) + kfree(adjhor); + if (adjvert != NULL) + kfree(adjvert); + if (total_adjhor != NULL) + kfree(total_adjhor); + if (total_adjvert != NULL) + kfree(total_adjvert); + if (ssCompData.ix2_fm != NULL) + kfree(ssCompData.ix2_fm); + if (ssCompData.ix2_sn != NULL) + kfree(ssCompData.ix2_sn); + if (ssCompData.cx2_fm != NULL) + kfree(ssCompData.cx2_fm); + if (ssCompData.cx2_sn != NULL) + kfree(ssCompData.cx2_sn); + if (totCompData.ix_fm != NULL) + kfree(totCompData.ix_fm); + if (totCompData.ix_sn != NULL) + kfree(totCompData.ix_sn); + if (totCompData.cx_fm != NULL) + kfree(totCompData.cx_fm); + if (totCompData.cx_sn != NULL) + kfree(totCompData.cx_sn); + return ret; +} + +/** + * Perform all the tests selected in a TestTodo variable related to SS Init + * data for LP mode (touch, keys etc..) + * @param path_limits name of Production Limit file to load or + * "NULL" if the limits data should be loaded by a .h file + * @param stop_on_fail if 1, the test flow stops at the first data check + * failure + * otherwise it keeps going performing all the selected test + * @param todo pointer to a TestToDo variable which select the test to do + * @return OK if success or an error code which specify the type of error + */ +int production_test_ss_ix_cx_lp(char *path_limits, int stop_on_fail, + TestToDo *todo) +{ + int ret; + int count_fail = 0; + + int *thresholds = NULL; + int trows, tcolumns; + int *thresholds_min = NULL; + int *thresholds_max = NULL; + + SelfSenseData ssCompData; + TotSelfSenseData totCompData; + + u8 *adjhor = NULL; + u8 *adjvert = NULL; + + short container; + + u16 *total_adjhor = NULL; + u16 *total_adjvert = NULL; + + logError(0, "%s\n", tag); + logError(0, "%s SS LP IX CX testes are starting...\n", tag); + ret = readSelfSenseCompensationData(LOAD_CX_SS_TOUCH_IDLE, &ssCompData); + /* read the SS LP compensation data */ + if (ret < 0) { + logError(1, + "%s production_test_data: readSelfSenseCompensationData failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + return ret | ERROR_PROD_TEST_DATA; + } + + ret = readTotSelfSenseCompensationData(LOAD_PANEL_CX_TOT_SS_TOUCH_IDLE, + &totCompData); + /* read the TOT SS LP compensation data */ + if (ret < 0) { + logError(1, + "%s production_test_data: readTotSelfSenseCompensationData failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + kfree(ssCompData.ix2_fm); + kfree(ssCompData.ix2_sn); + kfree(ssCompData.cx2_fm); + kfree(ssCompData.cx2_sn); + return ret | ERROR_PROD_TEST_DATA; + } + + /************* SS FORCE IX LP **************/ + /* SS IX1 LP FORCE TEST */ + logError(0, "%s SS IX1 LP FORCE TEST:\n", tag); + if (todo->SelfForceIx1LP == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_IX1_LP_FORCE_MIN_MAX, + &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_IX1_LP_FORCE_MIN_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + container = (short)ssCompData.f_ix1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], + thresholds[1]); + /* check the limits */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax SS IX1 LP FORCE TEST failed... ERROR COUNT = %d\n", + tag, ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS IX1 LP FORCE TEST:.................OK\n\n", + tag); + } else + logError(0, + "%s SS IX1 LP FORCE TEST:.................SKIPPED\n\n", + tag); + + kfree(thresholds); + thresholds = NULL; + /* SS IX2 LP FORCE TEST */ + logError(0, "%s SS IX2 LP FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceIx2LP == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_IX2_LP_FORCE_MAP_MIN, + &thresholds_min, &trows, + &tcolumns); + /* load the min thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_IX2_LP_FORCE_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_IX2_LP_FORCE_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_IX2_LP_FORCE_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapFromU(ssCompData.ix2_fm, + ssCompData.header.force_node, 1, + thresholds_min, + thresholds_max); /* check the + * values with + * thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS IX2 LP FORCE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS IX2 LP FORCE MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS IX2 LP FORCE MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s SS IX2 LP FORCE MIN MAX TEST:.................SKIPPED\n\n", + tag); + + logError(0, "%s SS IX2 LP FORCE ADJ TEST:\n", tag); + if (todo->SelfForceIx2AdjLP == 1) { + /* SS IX2 FORCE ADJV TEST */ + logError(0, "%s SS IX2 LP FORCE ADJVERT TEST:\n", tag); + ret = computeAdjVertFromU(ssCompData.ix2_fm, + ssCompData.header.force_node, 1, + &adjvert); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjVert SS IX2 LP FORCE ADJV failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS IX2 LP FORCE ADJV computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_IX2_LP_FORCE_ADJV_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); /* load the max + * thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node - 1 || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_IX2_LP_FORCE_ADJV_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdj(adjvert, ssCompData.header.force_node - + 1, 1, thresholds_max); /* check the + * values with + * thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS IX2 LP FORCE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS IX2 LP FORCE ADJV TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS IX2 LP FORCE ADJV TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjvert); + adjvert = NULL; + } else + logError(0, + "%s SS IX2 LP FORCE ADJ TEST:.................SKIPPED\n\n", + tag); + + /* SS TOTAL FORCE IX */ + logError(0, "%s SS TOTAL IX LP FORCE TEST:\n", tag); + if (todo->SelfForceIxTotalLP == 1 || todo->SelfForceIxTotalAdjLP == 1) { + logError(0, "%s SS TOTAL IX LP FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceIxTotalLP == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_IX_LP_FORCE_MAP_MIN, + &thresholds_min, + &trows, &tcolumns); + /* load the min thresholds */ + if (ret < 0 || (trows != + totCompData.header.force_node || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_LP_FORCE_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_IX_LP_FORCE_MAP_MAX, + &thresholds_max, + &trows, &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != + totCompData.header.force_node || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_LP_FORCE_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapTotalFromU(totCompData.ix_fm, + totCompData.header. + force_node, 1, + thresholds_min, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS TOTAL IX LP FORCE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS TOTAL IX LP FORCE MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS TOTAL IX LP FORCE MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s SS TOTAL IX LP FORCE MIN MAX TEST:.................SKIPPED\n", + tag); + + logError(0, "%s SS TOTAL IX LP FORCE ADJ TEST:\n", tag); + if (todo->SelfForceIxTotalAdjLP == 1) { + /* SS TOTAL IX FORCE ADJV TEST */ + logError(0, "%s SS TOTAL IX LP FORCE ADJVERT TEST:\n", + tag); + ret = computeAdjVertTotalFromU(totCompData.ix_fm, + totCompData.header. + force_node, 1, + &total_adjvert); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjVert SS TOTAL IX LP FORCE ADJV failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS TOTAL IX LP FORCE ADJV computed!\n", + tag); + + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_IX_LP_FORCE_ADJV_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != totCompData.header.force_node - + 1 || tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_LP_FORCE_ADJV_MAP_MAX... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdjTotal(total_adjvert, + totCompData.header. + force_node - 1, 1, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS TOTAL IX LP FORCE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS TOTAL IX LP FORCE ADJV TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS TOTAL IX LP FORCE ADJV TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjvert); + total_adjvert = NULL; + } else + logError(0, + "%s SS TOTAL IX LP FORCE ADJ TEST:.................SKIPPED\n", + tag); + } else + logError(0, + "%s SS TOTAL IX LP FORCE TEST:.................SKIPPED\n\n", + tag); + + + /************** SS SENSE IX LP **************/ + /* SS IX1 LP SENSE TEST */ + logError(0, "%s SS IX1 LP SENSE TEST:\n", tag); + if (todo->SelfSenseIx1LP == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_IX1_LP_SENSE_MIN_MAX, + &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_IX1_LP_SENSE_MIN_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + container = (short)ssCompData.s_ix1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], + thresholds[1]); + /* check the limits */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax SS IX1 SENSE LP TEST failed... ERROR COUNT = %d\n", + tag, ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS IX1 LP SENSE TEST:.................OK\n\n", + tag); + } else + logError(0, + "%s SS IX1 LP SENSE TEST:.................SKIPPED\n\n", + tag); + + kfree(thresholds); + thresholds = NULL; + /* SS IX2 SENSE TEST */ + logError(0, "%s SS IX2 LP SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseIx2LP == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_IX2_LP_SENSE_MAP_MIN, + &thresholds_min, &trows, + &tcolumns); + /* load the min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + ssCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_IX2_LP_SENSE_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_IX2_LP_SENSE_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + ssCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_IX2_LP_SENSE_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapFromU(ssCompData.ix2_sn, 1, + ssCompData.header.sense_node, + thresholds_min, thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS IX2 LP SENSE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS IX2 LP SENSE MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS IX2 LP SENSE MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s SS IX2 LP SENSE MIN MAX TEST:.................SKIPPED\n\n", + tag); + + logError(0, "%s SS IX2 LP SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseIx2AdjLP == 1) { + /* SS IX2 SENSE ADJH TEST */ + logError(0, "%s SS IX2 SENSE ADJHORIZ TEST:\n", tag); + ret = computeAdjHorizFromU(ssCompData.ix2_sn, 1, + ssCompData.header.sense_node, + &adjhor); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjHoriz SS IX2 SENSE ADJH failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS IX2 SENSE ADJ HORIZ computed!\n", tag); + + + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_IX2_SENSE_ADJH_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + ssCompData.header.sense_node - 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_IX2_LP_SENSE_ADJH_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdj(adjhor, 1, + ssCompData.header.sense_node - 1, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMapAdj SS IX2 LP SENSE ADJH failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS IX2 LP SENSE ADJH TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS IX2 LP SENSE ADJH TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjhor); + adjhor = NULL; + } else + logError(0, + "%s SS IX2 LP SENSE ADJ TEST:.................SKIPPED\n", + tag); + + /* SS TOTAL IX SENSE */ + logError(0, "%s SS TOTAL IX LP SENSE TEST:\n", tag); + if (todo->SelfSenseIxTotalLP == 1 || todo->SelfSenseIxTotalAdjLP == 1) { + logError(0, "%s SS TOTAL IX LP SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseIxTotalLP == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_IX_LP_SENSE_MAP_MIN, + &thresholds_min, + &trows, &tcolumns); + /* load the min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + totCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_LP_SENSE_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_IX_LP_SENSE_MAP_MAX, + &thresholds_max, + &trows, &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + totCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_LP_SENSE_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapTotalFromU(totCompData.ix_sn, 1, + totCompData.header. + sense_node, + thresholds_min, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS TOTAL IX LP SENSE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS TOTAL IX LP SENSE MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS TOTAL IX LP SENSE MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s SS TOTAL IX LP SENSE MIN MAX TEST:.................SKIPPED\n", + tag); + + + logError(0, "%s SS TOTAL IX LP SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseIxTotalAdjLP == 1) { + /* SS TOTAL IX SENSE ADJH TEST */ + logError(0, "%s SS TOTAL IX LP SENSE ADJHORIZ TEST:\n", + tag); + ret = computeAdjHorizTotalFromU(totCompData.ix_sn, 1, + totCompData.header. + sense_node, + &total_adjhor); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjHoriz SS TOTAL IX LP SENSE ADJH failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, + "%s SS TOTAL IX LP SENSE ADJ HORIZ computed!\n", + tag); + + + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_IX_LP_SENSE_ADJH_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + totCompData.header.sense_node - 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_IX_LP_SENSE_ADJH_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdjTotal(total_adjhor, 1, + totCompData.header. + sense_node - 1, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMapAdj SS TOTAL IX LP SENSE ADJH failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS TOTAL IX LP SENSE ADJH TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS TOTAL IX LP SENSE ADJH TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjhor); + total_adjhor = NULL; + } else + logError(0, + "%s SS TOTAL IX LP SENSE ADJ TEST:.................SKIPPED\n", + tag); + } else + logError(0, + "%s SS TOTAL IX LP SENSE TEST:.................SKIPPED\n", + tag); + + /************* SS SENSE CX LP **************/ + /* SS CX1 LP FORCE TEST */ + logError(0, "%s SS CX1 LP FORCE TEST:\n", tag); + if (todo->SelfForceCx1LP == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_CX1_LP_FORCE_MIN_MAX, + &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_CX1_LP_FORCE_MIN_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + container = (short)ssCompData.f_cx1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], + thresholds[1]); + /* check the limits */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax SS CX1 LP FORCE TEST failed... ERROR COUNT = %d\n", + tag, ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS CX1 LP FORCE TEST:.................OK\n\n", + tag); + kfree(thresholds); + thresholds = NULL; + } else + logError(0, + "%s SS CX1 LP FORCE TEST:.................SKIPPED\n\n", + tag); + + + + /* SS CX2 LP FORCE TEST */ + logError(0, "%s SS CX2 LP FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceCx2LP == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_CX2_LP_FORCE_MAP_MIN, + &thresholds_min, &trows, + &tcolumns); + /* load the min thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_CX2_LP_FORCE_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_CX2_LP_FORCE_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_CX2_LP_FORCE_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMap(ssCompData.cx2_fm, + ssCompData.header.force_node, 1, + thresholds_min, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS CX2 LP FORCE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS CX2 LP FORCE MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS CX2 LP FORCE MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s SS CX2 LP FORCE MIN MAX TEST:.................SKIPPED\n", + tag); + + logError(0, "%s SS CX2 LP FORCE ADJ TEST:\n", tag); + if (todo->SelfForceCx2AdjLP == 1) { + /* SS CX2 FORCE ADJV TEST */ + logError(0, "%s SS CX2 LP FORCE ADJVERT TEST:\n", tag); + ret = computeAdjVert(ssCompData.cx2_fm, + ssCompData.header.force_node, 1, &adjvert); + /* compute the ADJV for CX2 FORCE */ + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjVert SS CX2 LP FORCE ADJV failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS CX2 LP FORCE ADJV computed!\n", tag); + + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_CX2_LP_FORCE_ADJV_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != ssCompData.header.force_node - 1 || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_CX2_LP_FORCE_ADJV_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdj(adjvert, ssCompData.header.force_node - + 1, 1, thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS IX2 LP FORCE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS CX2 LP FORCE ADJV TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS CX2 LP FORCE ADJV TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjvert); + adjvert = NULL; + } else + logError(0, + "%s SS CX2 LP FORCE ADJ TEST:.................SKIPPED\n\n", + tag); + + /* SS TOTAL CX LP FORCE */ + logError(0, "%s SS TOTAL CX LP FORCE TEST:\n", tag); + if (todo->SelfForceCxTotalLP == 1 || todo->SelfForceCxTotalAdjLP == 1) { + logError(0, "%s SS TOTAL CX LP FORCE MIN MAX TEST:\n", tag); + if (todo->SelfForceCxTotalLP == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_CX_LP_FORCE_MAP_MIN, + &thresholds_min, + &trows, &tcolumns); + /* load the min thresholds */ + if (ret < 0 || (trows != + totCompData.header.force_node || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_LP_FORCE_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_CX_LP_FORCE_MAP_MAX, + &thresholds_max, + &trows, &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != + totCompData.header.force_node || + tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_LP_FORCE_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapTotal(totCompData.cx_fm, + totCompData.header.force_node, + 1, thresholds_min, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS TOTAL LP FORCE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS TOTAL FORCE LP MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS TOTAL FORCE LP MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s SS TOTAL CX LP FORCE MIN MAX TEST:.................SKIPPED\n", + tag); + + /* SS TOTAL CX LP FORCE ADJV TEST */ + logError(0, "%s SS TOTAL CX LP FORCE ADJ TEST:\n", tag); + if (todo->SelfForceCxTotalAdjLP == 1) { + logError(0, "%s SS TOTAL CX LP FORCE ADJVERT TEST:\n", + tag); + ret = computeAdjVertTotal(totCompData.cx_fm, + totCompData.header.force_node, + 1, &total_adjvert); + /* compute the ADJV for CX2 FORCE */ + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjVert SS TOTAL CX LP FORCE ADJV failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS TOTAL CX LP FORCE ADJV computed!\n", + tag); + + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_CX_LP_FORCE_ADJV_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != totCompData.header.force_node - + 1 || tcolumns != 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_LP_FORCE_ADJV_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdjTotal(total_adjvert, + totCompData.header. + force_node - 1, 1, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS TOTAL CX LP FORCE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS TOTAL CX LP FORCE ADJV TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS TOTAL CX LP FORCE ADJV TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjvert); + total_adjvert = NULL; + } else + logError(0, + "%s SS TOTAL CX LP FORCE ADJ TEST:.................SKIPPED\n", + tag); + } else + logError(0, + "%s SS TOTAL CX LP FORCE TEST:.................SKIPPED\n\n", + tag); + + + + /************* SS SENSE CX *************/ + /* SS CX1 SENSE TEST */ + logError(0, "%s SS CX1 LP SENSE TEST:\n", tag); + if (todo->SelfSenseCx1LP == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_CX1_LP_SENSE_MIN_MAX, + &thresholds, &trows, &tcolumns); + if (ret < 0 || (trows != 1 || tcolumns != 2)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_CX1_LP_SENSE_MIN_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + container = (short)ssCompData.s_cx1; + ret = checkLimitsMinMax(&container, 1, 1, thresholds[0], + thresholds[1]); + /* check the limits */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMinMax SS CX1 LP SENSE TEST failed... ERROR COUNT = %d\n", + tag, ret); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS CX1 LP SENSE TEST:.................OK\n\n", + tag); + kfree(thresholds); + thresholds = NULL; + } else + logError(0, + "%s SS CX1 LP SENSE TEST:.................SKIPPED\n\n", + tag); + + + /* SS CX2 LP SENSE TEST */ + logError(0, "%s SS CX2 LP SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseCx2LP == 1) { + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_CX2_LP_SENSE_MAP_MIN, + &thresholds_min, &trows, + &tcolumns); + /* load the min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + ssCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_CX2_LP_SENSE_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_CX2_LP_SENSE_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + ssCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_CX2_LP_SENSE_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMap(ssCompData.cx2_sn, 1, + ssCompData.header.sense_node, + thresholds_min, thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS CX2 LP SENSE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS CX2 LP SENSE MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS CX2 LP SENSE MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s SS CX2 LP SENSE MIN MAX TEST:.................SKIPPED\n", + tag); + + logError(0, "%s SS CX2 LP SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseCx2AdjLP == 1) { + /* SS CX2 SENSE ADJH TEST */ + logError(0, "%s SS CX2 LP SENSE ADJHORIZ TEST:\n", tag); + ret = computeAdjHoriz(ssCompData.cx2_sn, 1, + ssCompData.header.sense_node, &adjhor); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjHoriz SS CX2 LP SENSE ADJH failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, "%s SS CX2 LP SENSE ADJH computed!\n", tag); + + + ret = parseProductionTestLimits(path_limits, &limit_file, + SS_CX2_LP_SENSE_ADJH_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + ssCompData.header.sense_node - 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_IX2_SENSE_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdj(adjhor, 1, + ssCompData.header.sense_node - 1, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMapAdj SS CX2 LP SENSE ADJH failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS CX2 LP SENSE ADJH TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS CX2 LP SENSE ADJH TEST:.................OK\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(adjhor); + adjhor = NULL; + } else + logError(0, + "%s SS CX2 LP SENSE ADJ TEST:.................SKIPPED\n\n", + tag); + + /* SS TOTAL CX SENSE */ + logError(0, "%s SS TOTAL CX LP SENSE TEST:\n", tag); + if (todo->SelfSenseCxTotalLP == 1 || todo->SelfSenseCxTotalAdjLP == 1) { + logError(0, "%s SS TOTAL CX LP SENSE MIN MAX TEST:\n", tag); + if (todo->SelfSenseCxTotalLP == 1) { + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_CX_LP_SENSE_MAP_MIN, + &thresholds_min, + &trows, &tcolumns); + /* load the min thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + totCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_LP_SENSE_MAP_MIN failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_CX_LP_SENSE_MAP_MAX, + &thresholds_max, + &trows, &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + totCompData.header.sense_node)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_LP_SENSE_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapTotal(totCompData.cx_sn, 1, + totCompData.header.sense_node, + thresholds_min, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMap SS TOTAL CX LP SENSE failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS TOTAL CX LP SENSE MIN MAX TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS TOTAL CX LP SENSE MIN MAX TEST:.................OK\n\n", + tag); + + kfree(thresholds_min); + thresholds_min = NULL; + kfree(thresholds_max); + thresholds_max = NULL; + } else + logError(0, + "%s SS TOTAL CX LP SENSE MIN MAX TEST:.................SKIPPED\n", + tag); + + + /* SS TOTAL IX SENSE ADJH TEST */ + logError(0, "%s SS TOTAL CX LP SENSE ADJ TEST:\n", tag); + if (todo->SelfSenseCxTotalAdjLP == 1) { + logError(0, "%s SS TOTAL CX LP SENSE ADJHORIZ TEST:\n", + tag); + ret = computeAdjHorizTotal(totCompData.cx_sn, 1, + totCompData.header.sense_node, + &total_adjhor); + if (ret < 0) { + logError(1, + "%s production_test_data: computeAdjHoriz SS TOTAL CX LP SENSE ADJH failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + logError(0, + "%s SS TOTAL CX LP SENSE ADJ HORIZ computed!\n", + tag); + + + ret = parseProductionTestLimits(path_limits, + &limit_file, + SS_TOTAL_CX_LP_SENSE_ADJH_MAP_MAX, + &thresholds_max, &trows, + &tcolumns); + /* load the max thresholds */ + if (ret < 0 || (trows != 1 || tcolumns != + totCompData.header.sense_node - 1)) { + logError(1, + "%s production_test_data: parseProductionTestLimits SS_TOTAL_CX_LP_SENSE_ADJH_MAP_MAX failed... ERROR %08X\n", + tag, ERROR_PROD_TEST_DATA); + ret |= ERROR_PROD_TEST_DATA; + goto ERROR_LIMITS; + } + + ret = checkLimitsMapAdjTotal(total_adjhor, 1, + totCompData.header. + sense_node - 1, + thresholds_max); + /* check the values with thresholds */ + if (ret != OK) { + logError(1, + "%s production_test_data: checkLimitsMapAdj SS TOTAL CX LP SENSE ADJH failed... ERROR COUNT = %d\n", + tag, ret); + logError(0, + "%s SS TOTAL CX LP SENSE ADJH TEST:.................FAIL\n\n", + tag); + count_fail += 1; + if (stop_on_fail) + goto ERROR; + } else + logError(0, + "%s SS TOTAL CX LP SENSE ADJH TEST:.................OK\n\n", + tag); + + kfree(thresholds_max); + thresholds_max = NULL; + kfree(total_adjhor); + total_adjhor = NULL; + } else + logError(0, + "%s SS TOTAL CX LP SENSE ADJ TEST:.................SKIPPED\n", + tag); + } else + logError(0, + "%s SS TOTAL CX LP SENSE TEST:.................SKIPPED\n", + tag); + + + +ERROR: + logError(0, "%s\n", tag); + if (count_fail == 0) { + kfree(ssCompData.ix2_fm); + ssCompData.ix2_fm = NULL; + kfree(ssCompData.ix2_sn); + ssCompData.ix2_sn = NULL; + kfree(ssCompData.cx2_fm); + ssCompData.cx2_fm = NULL; + kfree(ssCompData.cx2_sn); + ssCompData.cx2_sn = NULL; + kfree(totCompData.ix_fm); + totCompData.ix_fm = NULL; + kfree(totCompData.ix_sn); + totCompData.ix_sn = NULL; + kfree(totCompData.cx_fm); + totCompData.cx_fm = NULL; + kfree(totCompData.cx_sn); + totCompData.cx_sn = NULL; + logError(0, + "%s SS LP IX CX testes finished!.................OK\n\n", + tag); + return OK; + } else { + /* print all kind of data in just one row for readability reason */ + print_frame_u8("SS LP Init Data Ix2_fm = ", array1dTo2d_u8( + ssCompData.ix2_fm, + ssCompData.header.force_node, 1), + ssCompData.header.force_node, 1); + print_frame_i8("SS LP Init Data Cx2_fm = ", array1dTo2d_i8( + ssCompData.cx2_fm, + ssCompData.header.force_node, 1), + ssCompData.header.force_node, 1); + print_frame_u8("SS LP Init Data Ix2_sn = ", array1dTo2d_u8( + ssCompData.ix2_sn, + ssCompData.header.sense_node, + ssCompData.header.sense_node), 1, + ssCompData.header.sense_node); + print_frame_i8("SS LP Init Data Cx2_sn = ", array1dTo2d_i8( + ssCompData.cx2_sn, + ssCompData.header.sense_node, + ssCompData.header.sense_node), 1, + ssCompData.header.sense_node); + print_frame_u16("TOT SS LP Init Data Ix_fm = ", array1dTo2d_u16( + totCompData.ix_fm, + totCompData.header.force_node, 1), + totCompData.header.force_node, 1); + print_frame_short("TOT SS LP Init Data Cx_fm = ", + array1dTo2d_short(totCompData.cx_fm, + totCompData.header. + force_node, 1), + totCompData.header.force_node, 1); + print_frame_u16("TOT SS LP Init Data Ix_sn = ", array1dTo2d_u16( + totCompData.ix_sn, + totCompData.header.sense_node, + totCompData.header.sense_node), 1, + totCompData.header.sense_node); + print_frame_short("TOT SS LP Init Data Cx_sn = ", + array1dTo2d_short(totCompData.cx_sn, + totCompData.header. + sense_node, + totCompData.header. + sense_node), + 1, totCompData.header.sense_node); + logError(0, + "%s SS LP IX CX testes finished!.................FAILED fails_count = %d\n\n", + tag, count_fail); + if (thresholds != NULL) + kfree(thresholds); + if (thresholds_min != NULL) + kfree(thresholds_min); + if (thresholds_max != NULL) + kfree(thresholds_max); + if (adjhor != NULL) + kfree(adjhor); + if (adjvert != NULL) + kfree(adjvert); + if (total_adjhor != NULL) + kfree(total_adjhor); + if (total_adjvert != NULL) + kfree(total_adjvert); + if (ssCompData.ix2_fm != NULL) + kfree(ssCompData.ix2_fm); + if (ssCompData.ix2_sn != NULL) + kfree(ssCompData.ix2_sn); + if (ssCompData.cx2_fm != NULL) + kfree(ssCompData.cx2_fm); + if (ssCompData.cx2_sn != NULL) + kfree(ssCompData.cx2_sn); + if (totCompData.ix_fm != NULL) + kfree(totCompData.ix_fm); + if (totCompData.ix_sn != NULL) + kfree(totCompData.ix_sn); + if (totCompData.cx_fm != NULL) + kfree(totCompData.cx_fm); + if (totCompData.cx_sn != NULL) + kfree(totCompData.cx_sn); + return ERROR_TEST_CHECK_FAIL | ERROR_PROD_TEST_DATA; + } + +ERROR_LIMITS: + if (thresholds != NULL) + kfree(thresholds); + if (thresholds_min != NULL) + kfree(thresholds_min); + if (thresholds_max != NULL) + kfree(thresholds_max); + if (adjhor != NULL) + kfree(adjhor); + if (adjvert != NULL) + kfree(adjvert); + if (total_adjhor != NULL) + kfree(total_adjhor); + if (total_adjvert != NULL) + kfree(total_adjvert); + if (ssCompData.ix2_fm != NULL) + kfree(ssCompData.ix2_fm); + if (ssCompData.ix2_sn != NULL) + kfree(ssCompData.ix2_sn); + if (ssCompData.cx2_fm != NULL) + kfree(ssCompData.cx2_fm); + if (ssCompData.cx2_sn != NULL) + kfree(ssCompData.cx2_sn); + if (totCompData.ix_fm != NULL) + kfree(totCompData.ix_fm); + if (totCompData.ix_sn != NULL) + kfree(totCompData.ix_sn); + if (totCompData.cx_fm != NULL) + kfree(totCompData.cx_fm); + if (totCompData.cx_sn != NULL) + kfree(totCompData.cx_sn); + return ret; +} + +/** + * Perform a complete Data Test check of the IC + * @param path_limits name of Production Limit file to load or + * "NULL" if the limits data should be loaded by a .h file + * @param stop_on_fail if 1, the test flow stops at the first data check + * failure otherwise it keeps going performing all the selected test + * @param todo pointer to a TestToDo variable which select the test to do + * @return OK if success or an error code which specify the type of error + */ +int production_test_data(char *path_limits, int stop_on_fail, TestToDo *todo) +{ + int res = OK, ret; + + if (todo == NULL) { + logError(1, + "%s production_test_data: No TestToDo specified!! ERROR = %08X\n", + tag, (ERROR_OP_NOT_ALLOW | ERROR_PROD_TEST_DATA)); + return ERROR_OP_NOT_ALLOW | ERROR_PROD_TEST_DATA; + } + + + logError(0, "%s DATA Production test is starting...\n", tag); + + + ret = production_test_ms_raw(path_limits, stop_on_fail, todo); + res |= ret; + if (ret < 0) { + logError(1, + "%s production_test_data: production_test_ms_raw failed... ERROR = %08X\n", + tag, ret); + if (stop_on_fail == 1) + goto END; + } + + + + ret = production_test_ms_cx(path_limits, stop_on_fail, todo); + res |= ret; + if (ret < 0) { + logError(1, + "%s production_test_data: production_test_ms_cx failed... ERROR = %08X\n", + tag, ret); + if (stop_on_fail == 1) + goto END; + } + + + ret = production_test_ss_raw(path_limits, stop_on_fail, todo); + res |= ret; + if (ret < 0) { + logError(1, + "%s production_test_data: production_test_ss_raw failed... ERROR = %08X\n", + tag, ret); + if (stop_on_fail == 1) + goto END; + } + + ret = production_test_ss_ix_cx(path_limits, stop_on_fail, todo); + res |= ret; + if (ret < 0) { + logError(1, + "%s production_test_data: production_test_ss_ix_cx failed... ERROR = %08X\n", + tag, ret); + if (stop_on_fail == 1) + goto END; + } + +END: + freeLimitsFile(&limit_file); /* /< release the limit file loaded + * during the test */ + if (res < OK) + logError(0, "%s DATA Production test failed!\n", tag); + else + logError(0, "%s DATA Production test finished!\n", tag); + return res; +} + + +/** + * Retrieve the actual Test Limit data from the system (bin file or header + *file) + * @param path name of Production Test Limit file to load or "NULL" if the + *limits data should be loaded by a .h file + * @param file pointer to the LimitFile struct which will contains the limits + *data + * @return OK if success or an error code which specify the type of error + *encountered + */ +int getLimitsFile(char *path, LimitFile *file) +{ + const struct firmware *fw = NULL; + struct device *dev = NULL; + int fd = -1; + + logError(0, "%s Get Limits File starting... %s\n", tag, path); + + if (file->data != NULL) {/* to avoid memory leak on consecutive call of + * the function with the same pointer */ + logError(0, + "%s Pointer to Limits Data already contains something... freeing its content!\n", + tag); + kfree(file->data); + file->data = NULL; + file->size = 0; + } + + strlcpy(file->name, path, MAX_LIMIT_FILE_NAME); + if (strncmp(path, "NULL", 4) == 0) { +#ifdef LIMITS_H_FILE + logError(0, "%s Loading Limits File from .h!\n", tag); + file->size = LIMITS_SIZE_NAME; + file->data = (char *)kmalloc((file->size) * sizeof(char), + GFP_KERNEL); + if (file->data != NULL) { + memcpy(file->data, (char *)(LIMITS_ARRAY_NAME), + file->size); + return OK; + } else { + logError(1, + "%s Error while allocating data... ERROR %08X\n", + tag, + path, ERROR_ALLOC); + return ERROR_ALLOC; + } +#else + logError(1, "%s limit file path NULL... ERROR %08X\n", tag, + ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; +#endif + } else { + dev = getDev(); + if (dev != NULL) { + logError(0, "%s Loading Limits File from .csv!\n", tag); + fd = request_firmware(&fw, path, dev); + if (fd == 0) { + logError(0, "%s Start to copy %s...\n", tag, + path); + file->size = fw->size; + file->data = (char *)kmalloc((file->size) * + sizeof(char), + GFP_KERNEL); + if (file->data != NULL) { + memcpy(file->data, (char *)fw->data, + file->size); + logError(0, + "%s Limit file Size = %d\n", + tag, + file->size); + release_firmware(fw); + return OK; + } else { + logError(1, + "%s Error while allocating data... ERROR %08X\n", + tag, ERROR_ALLOC); + release_firmware(fw); + return ERROR_ALLOC; + } + } else { + logError(1, + "%s Request the file %s failed... ERROR %08X\n", + tag, path, ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; + } + } else { + logError(1, + "%s Error while getting the device ERROR %08X\n", + tag, + ERROR_FILE_READ); + return ERROR_FILE_READ; + } + } +} + +/** + * Reset and release the memory which store a Production Limit File previously + *loaded + * @param file pointer to the LimitFile struct to free + * @return OK if success or an error code which specify the type of error + *encountered + */ + +int freeLimitsFile(LimitFile *file) +{ + logError(0, "%s Freeing Limit File ...\n", tag); + if (file != NULL) { + if (file->data != NULL) { + kfree(file->data); + file->data = NULL; + } else + logError(0, "%s Limit File was already freed!\n", tag); + file->size = 0; + strlcpy(file->name, " ", MAX_LIMIT_FILE_NAME); + return OK; + } else { + logError(1, "%s Passed a NULL argument! ERROR %08X\n", tag, + ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } +} + +/** + * Reset and release the memory which store the current Limit File previously + *loaded + * @return OK if success or an error code which specify the type of error + *encountered + */ + +int freeCurrentLimitsFile(void) +{ + return freeLimitsFile(&limit_file); +} + + +/** + * Parse the raw data read from a Production test limit file in order to find + *the specified information + * If no limits file data are passed, the function loads and stores the limit + *file from the system + * @param path name of Production Test Limit file to load or "NULL" if the + *limits data should be loaded by a .h file + * @param file pointer to LimitFile struct that should be parsed or NULL if the + *limit file in the system should be loaded and then parsed + * @param label string which identify a particular set of data in the file that + *want to be loaded + * @param data pointer to the pointer which will contains the specified limits + *data as 1 dimension matrix with data arranged row after row + * @param row pointer to a int variable which will contain the number of row of + *data + * @param column pointer to a int variable which will contain the number of + *column of data + * @return OK if success or an error code which specify the type of error + */ +int parseProductionTestLimits(char *path, LimitFile *file, char *label, + int **data, int *row, int *column) +{ + int find = 0; + char *token = NULL; + int i = 0; + int j = 0; + int z = 0; + + + char *line2 = NULL; + char line[800]; + char *buf = NULL; + int n, size, pointer = 0, ret = OK; + char *data_file = NULL; + struct fts_ts_info *info = dev_get_drvdata(getDev()); + + if (file == NULL || strcmp(path, file->name) != 0 || file->size == 0) { + logError(0, + "%s No limit File data passed... try to get them from the system!\n", + tag); + ret = getLimitsFile(info->limit_path, &limit_file); + if (ret < OK) { + logError(1, + "%s parseProductionTestLimits: ERROR %08X\n", + tag, + ERROR_FILE_NOT_FOUND); + return ERROR_FILE_NOT_FOUND; + } + size = limit_file.size; + data_file = limit_file.data; + } else { + logError(0, "%s Limit File data passed as arguments!\n", tag); + size = file->size; + data_file = file->data; + } + + + + logError(0, "%s The size of the limits file is %d bytes...\n", tag, + size); + + + + while (find == 0) { + /* start to look for the wanted label */ + if (readLine(&data_file[pointer], line, size - pointer, &n) < + 0) { + find = -1; + break; + } + pointer += n; + if (line[0] == '*') { /* each header row start with * ex. + * *label,n_row,n_colum */ + line2 = kstrdup(line, GFP_KERNEL); + if (line2 == NULL) { + logError(1, + "%s parseProductionTestLimits: kstrdup ERROR %08X\n", + tag, ERROR_ALLOC); + ret = ERROR_ALLOC; + goto END; + } + buf = line2; + line2 += 1; + token = strsep(&line2, ","); + if (strcmp(token, label) == 0) {/* if the row is the + * wanted one i retrieve + * rows and columns info + * */ + find = 1; + token = strsep(&line2, ","); + if (token != NULL) { + if (sscanf(token, "%d", row) == 1) + logError(0, "%s Row = %d\n", + tag, *row); + else { + logError(0, "%s ERROR while reading the row value! ERROR %08X\n", + tag, ERROR_FILE_PARSE); + ret = ERROR_FILE_PARSE; + goto END; + } + + } else { + logError(1, + "%s parseProductionTestLimits 1: ERROR %08X\n", + tag, ERROR_FILE_PARSE); + ret = ERROR_FILE_PARSE; + goto END; + } + token = strsep(&line2, ","); + if (token != NULL) { + if (sscanf(token, "%d", column) == 1) + logError(0, "%s Column = %d\n", + tag, *column); + else { + logError(0, "%s ERROR while reading the column value! ERROR %08X\n", + tag, ERROR_FILE_PARSE); + ret = ERROR_FILE_PARSE; + goto END; + } + + } else { + logError(1, + "%s parseProductionTestLimits 2: ERROR %08X\n", + tag, ERROR_FILE_PARSE); + ret = ERROR_FILE_PARSE; + goto END; + } + + kfree(buf); + buf = NULL; + *data = (int *)kmalloc(((*row) * (*column)) * + sizeof(int), GFP_KERNEL); + /* allocate the memory for containing the data */ + j = 0; + if (*data == NULL) { + logError(1, + "%s parseProductionTestLimits: ERROR %08X\n", + tag, ERROR_ALLOC); + ret = ERROR_ALLOC; + goto END; + } + + + /* start to read the data */ + for (i = 0; i < *row; i++) { + if (readLine(&data_file[pointer], line, + size - pointer, &n) < 0) { + logError(1, + "%s parseProductionTestLimits : ERROR %08X\n", + tag, ERROR_FILE_READ); + ret = ERROR_FILE_READ; + goto END; + } + pointer += n; + line2 = kstrdup(line, GFP_KERNEL); + if (line2 == NULL) { + logError(1, + "%s parseProductionTestLimits: kstrdup ERROR %08X\n", + tag, ERROR_ALLOC); + ret = ERROR_ALLOC; + goto END; + } + buf = line2; + token = strsep(&line2, ","); + for (z = 0; (z < *column) && (token != + NULL); + z++) { + if (sscanf(token, "%d", + ((*data) + j)) == 1) { + j++; + token = + strsep(&line2, ","); + } + } + kfree(buf); + buf = NULL; + } + if (j == ((*row) * (*column))) {/* check that + * all the data + * are read */ + logError(0, "%s READ DONE!\n", tag); + ret = OK; + goto END; + } + logError(1, + "%s parseProductionTestLimits 3: ERROR %08X\n", + tag, ERROR_FILE_PARSE); + ret = ERROR_FILE_PARSE; + goto END; + } + kfree(buf); + buf = NULL; + } + } + logError(1, "%s parseProductionTestLimits: ERROR %08X\n", tag, + ERROR_LABEL_NOT_FOUND); + ret = ERROR_LABEL_NOT_FOUND; +END: + if (buf != NULL) + kfree(buf); + return ret; +} + + +/** + * Read one line of a text file passed as array of byte and terminate it with a + *termination character '\0' + * @param data text file as array of bytes + * @param line pointer to an array of char that will contain the line read + * @param size size of data + * @param n pointer to a int variable which will contain the number of + *characters of the line + * @return OK if success or an error code which specify the type of error + */ +int readLine(char *data, char *line, int size, int *n) +{ + int i = 0; + + if (size < 1) + return ERROR_OP_NOT_ALLOW; + + while (data[i] != '\n' && i < size) { + line[i] = data[i]; + i++; + } + *n = i + 1; + line[i] = '\0'; + + return OK; +} diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsTest.h b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsTest.h new file mode 100644 index 000000000000..6f39ee5c8dd9 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsTest.h @@ -0,0 +1,442 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS API for MP test ** + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsTest.h + * \brief Contains all the definitions and structs related to the Mass + *Production Test + */ + +#ifndef FTS_TEST_H +#define FTS_TEST_H + +#include "ftsSoftware.h" + +#ifndef LIMITS_H_FILE +/* /< Name of the Production Test Limit File */ +#define LIMITS_FILE "stm_fts_production_limits.csv" +#else +#define LIMITS_FILE "NULL" +#endif + +#define WAIT_FOR_FRESH_FRAMES 200 /* /< Time in ms to wait after start to + * sensing before reading a frame */ +#define WAIT_AFTER_SENSEOFF 50 /* /< Time in ms to wait after stop + * sensing and before reading a frame + * from memory */ + +#define NO_INIT 0 /* /< No Initialization required during + * the MP */ + +#define RETRY_INIT_BOOT 3 /* /< number of retry of the + * initialization process at boot */ + +/** @defgroup mp_test Mass Production Test + * Mass production test API. + * Mass Production Test (MP) should be executed at least one time in the life + * of every device \n + * It used to verify that tit is not present any hardware damage and + * initialize some value of the chip in order to guarantee the working + * performance \n + * The MP test is made up by 3 steps: + * - ITO test = production_test_ito() \n + * - Initialization = production_test_initialization() \n + * - Data Test = production_test_data(), + * it is possible to select which items test thanks to the TestToDo struct\n + * To execute the Data Test it is mandatory load some thresholds that + * are stored in the Limit File. + * @{ + */ + +/** @defgroup limit_file Limit File + * @ingroup mp_test + * Production Test Limit File is a csv which contains thresholds of the data to + * test. + * This file can be loaded from the file system or stored as a header file + * according to the LIMITS_H_FILE define \n + * For each selectable test item there can be one or more associated labels + * which store the corresponding thresholds \n + * @{ + */ +/* LABELS PRODUCTION TEST LIMITS FILE */ +/** @defgroup test_labels Test Items Labels + * @ingroup limit_file + * Labels present in the Limit File and associated to the test items of + *TestToDo + * @{ + */ +#define MS_RAW_MIN_MAX "MS_RAW_DATA_MIN_MAX" +#define MS_RAW_EACH_NODE_MIN "MS_RAW_DATA_EACH_MIN" +#define MS_RAW_EACH_NODE_MAX "MS_RAW_DATA_EACH_MAX" +#define MS_RAW_GAP "MS_RAW_DATA_GAP" +#define MS_RAW_ADJH "MS_RAW_DATA_ADJ_HORIZONTAL" +#define MS_RAW_ADJV "MS_RAW_DATA_ADJ_VERTICAL" +#define MS_RAW_ITO_ADJH "MS_RAW_ITO_DATA_ADJ_HORIZONTAL" +#define MS_RAW_ITO_ADJV "MS_RAW_ITO_DATA_ADJ_VERTICAL" +#define MS_RAW_LP_MIN_MAX "MS_RAW_LOWPOWER_DATA_MIN_MAX" +#define MS_RAW_LP_EACH_NODE_MIN "MS_RAW_LOWPOWER_DATA_EACH_MIN" +#define MS_RAW_LP_EACH_NODE_MAX "MS_RAW_LOWPOWER_DATA_EACH_MAX" +#define MS_RAW_LP_GAP "MS_RAW_LOWPOWER_DATA_GAP" +#define MS_RAW_LP_ADJH "MS_RAW_LOWPOWER_DATA_ADJ_HORIZONTAL" +#define MS_RAW_LP_ADJV "MS_RAW_LOWPOWER_DATA_ADJ_VERTICAL" +#define MS_RAW_ADJH_GAP "MS_RAW_DATA_ADJ_HORIZONTAL_P2P" +#define MS_RAW_ADJV_GAP "MS_RAW_DATA_ADJ_VERTICAL_P2P" +#define MS_RAW_ADJ_PEAK "MS_RAW_DATA_ADJ_PEAK" +#define MS_CX1_MIN_MAX "MS_TOUCH_ACTIVE_CX1_MIN_MAX" +#define MS_CX2_MAP_MIN "MS_TOUCH_ACTIVE_CX2_MIN" +#define MS_CX2_MAP_MAX "MS_TOUCH_ACTIVE_CX2_MAX" +#define MS_CX2_ADJH_MAP_MAX "MS_TOUCH_ACTIVE_CX2_ADJ_HORIZONTAL" +#define MS_CX2_ADJV_MAP_MAX "MS_TOUCH_ACTIVE_CX2_ADJ_VERTICAL" +#define MS_TOTAL_CX_MAP_MIN "MS_TOUCH_ACTIVE_TOTAL_CX_MIN" +#define MS_TOTAL_CX_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_MAX" +#define MS_TOTAL_CX_ADJH_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_ADJ_HORIZONTAL" +#define MS_TOTAL_CX_ADJV_MAP_MAX "MS_TOUCH_ACTIVE_TOTAL_CX_ADJ_VERTICAL" +#define MS_CX1_LP_MIN_MAX "MS_TOUCH_LOWPOWER_CX1_MIN_MAX" +#define MS_CX2_LP_MAP_MIN "MS_TOUCH_LOWPOWER_CX2_MIN" +#define MS_CX2_LP_MAP_MAX "MS_TOUCH_LOWPOWER_CX2_MAX" +#define MS_CX2_ADJH_LP_MAP_MAX "MS_TOUCH_LOWPOWER_CX2_ADJ_HORIZONTAL" +#define MS_CX2_ADJV_LP_MAP_MAX "MS_TOUCH_LOWPOWER_CX2_ADJ_VERTICAL" +#define MS_TOTAL_CX_LP_MAP_MIN "MS_TOUCH_LOWPOWER_TOTAL_CX_MIN" +#define MS_TOTAL_CX_LP_MAP_MAX "MS_TOUCH_LOWPOWER_TOTAL_CX_MAX" +#define MS_TOTAL_CX_ADJH_LP_MAP_MAX "MS_TOUCH_LOWPOWER_TOTAL_CX_ADJ_HORIZONTAL" +#define MS_TOTAL_CX_ADJV_LP_MAP_MAX "MS_TOUCH_LOWPOWER_TOTAL_CX_ADJ_VERTICAL" +#define SS_RAW_FORCE_MIN_MAX "SS_RAW_DATA_FORCE_MIN_MAX" +#define SS_RAW_FORCE_EACH_NODE_MIN "SS_RAW_DATA_FORCE_EACH_MIN" +#define SS_RAW_FORCE_EACH_NODE_MAX "SS_RAW_DATA_FORCE_EACH_MAX" +#define SS_RAW_SENSE_MIN_MAX "SS_RAW_DATA_SENSE_MIN_MAX" +#define SS_RAW_SENSE_EACH_NODE_MIN "SS_RAW_DATA_SENSE_EACH_MIN" +#define SS_RAW_SENSE_EACH_NODE_MAX "SS_RAW_DATA_SENSE_EACH_MAX" +#define SS_RAW_FORCE_GAP "SS_RAW_DATA_FORCE_GAP" +#define SS_RAW_SENSE_GAP "SS_RAW_DATA_SENSE_GAP" +#define SS_RAW_LP_FORCE_MIN_MAX "SS_RAW_LOWPOWER_DATA_FORCE_MIN_MAX" +#define SS_RAW_LP_SENSE_MIN_MAX "SS_RAW_LOWPOWER_DATA_SENSE_MIN_MAX" +#define SS_RAW_LP_FORCE_EACH_NODE_MIN "SS_RAW_LOWPOWER_DATA_FORCE_EACH_MIN" +#define SS_RAW_LP_FORCE_EACH_NODE_MAX "SS_RAW_LOWPOWER_DATA_FORCE_EACH_MAX" +#define SS_RAW_LP_SENSE_MIN_MAX "SS_RAW_LOWPOWER_DATA_SENSE_MIN_MAX" +#define SS_RAW_LP_SENSE_EACH_NODE_MIN "SS_RAW_LOWPOWER_DATA_SENSE_EACH_MIN" +#define SS_RAW_LP_SENSE_EACH_NODE_MAX "SS_RAW_LOWPOWER_DATA_SENSE_EACH_MAX" +#define SS_RAW_LP_FORCE_GAP "SS_RAW_LOWPOWER_DATA_FORCE_GAP" +#define SS_RAW_LP_SENSE_GAP "SS_RAW_LOWPOWER_DATA_SENSE_GAP" +#define SS_IX1_FORCE_MIN_MAX "SS_TOUCH_ACTIVE_IX1_FORCE_MIN_MAX" +#define SS_IX1_SENSE_MIN_MAX "SS_TOUCH_ACTIVE_IX1_SENSE_MIN_MAX" +#define SS_CX1_FORCE_MIN_MAX "SS_TOUCH_ACTIVE_CX1_FORCE_MIN_MAX" +#define SS_CX1_SENSE_MIN_MAX "SS_TOUCH_ACTIVE_CX1_SENSE_MIN_MAX" +#define SS_IX2_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_IX2_FORCE_MIN" +#define SS_IX2_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_IX2_FORCE_MAX" +#define SS_IX2_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_IX2_SENSE_MIN" +#define SS_IX2_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_IX2_SENSE_MAX" +#define SS_IX2_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_IX2_ADJ_VERTICAL" +#define SS_IX2_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_IX2_ADJ_HORIZONTAL" +#define SS_CX2_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_CX2_FORCE_MIN" +#define SS_CX2_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_CX2_FORCE_MAX" +#define SS_CX2_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_CX2_SENSE_MIN" +#define SS_CX2_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_CX2_SENSE_MAX" +#define SS_CX2_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_CX2_ADJ_VERTICAL" +#define SS_CX2_SENSE_ADJH_MAP_MAX "SS_TOUCH_ACTIVE_CX2_ADJ_HORIZONTAL" + + +/* TOTAL SS */ +#define SS_TOTAL_IX_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_IX_FORCE_MIN" +#define SS_TOTAL_IX_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_FORCE_MAX" +#define SS_TOTAL_IX_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_IX_SENSE_MIN" +#define SS_TOTAL_IX_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_SENSE_MAX" +#define SS_TOTAL_IX_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_IX_ADJ_VERTICAL" +#define SS_TOTAL_IX_SENSE_ADJH_MAP_MAX \ + "SS_TOUCH_ACTIVE_TOTAL_IX_ADJ_HORIZONTAL" +#define SS_TOTAL_CX_FORCE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_CX_FORCE_MIN" +#define SS_TOTAL_CX_FORCE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_FORCE_MAX" +#define SS_TOTAL_CX_SENSE_MAP_MIN "SS_TOUCH_ACTIVE_TOTAL_CX_SENSE_MIN" +#define SS_TOTAL_CX_SENSE_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_SENSE_MAX" +#define SS_TOTAL_CX_FORCE_ADJV_MAP_MAX "SS_TOUCH_ACTIVE_TOTAL_CX_ADJ_VERTICAL" +#define SS_TOTAL_CX_SENSE_ADJH_MAP_MAX \ + "SS_TOUCH_ACTIVE_TOTAL_CX_ADJ_HORIZONTAL" + +/* Idle (LP) version*/ +#define SS_IX1_LP_FORCE_MIN_MAX "SS_TOUCH_IDLE_IX1_FORCE_MIN_MAX" +#define SS_IX1_LP_SENSE_MIN_MAX "SS_TOUCH_IDLE_IX1_SENSE_MIN_MAX" +#define SS_CX1_LP_FORCE_MIN_MAX "SS_TOUCH_IDLE_CX1_FORCE_MIN_MAX" +#define SS_CX1_LP_SENSE_MIN_MAX "SS_TOUCH_IDLE_CX1_SENSE_MIN_MAX" +#define SS_IX2_LP_FORCE_MAP_MIN "SS_TOUCH_IDLE_IX2_FORCE_MIN" +#define SS_IX2_LP_FORCE_MAP_MAX "SS_TOUCH_IDLE_IX2_FORCE_MAX" +#define SS_IX2_LP_SENSE_MAP_MIN "SS_TOUCH_IDLE_IX2_SENSE_MIN" +#define SS_IX2_LP_SENSE_MAP_MAX "SS_TOUCH_IDLE_IX2_SENSE_MAX" +#define SS_IX2_LP_FORCE_ADJV_MAP_MAX "SS_TOUCH_IDLE_IX2_ADJ_VERTICAL" +#define SS_IX2_LP_SENSE_ADJH_MAP_MAX "SS_TOUCH_IDLE_IX2_ADJ_HORIZONTAL" +#define SS_CX2_LP_FORCE_MAP_MIN "SS_TOUCH_IDLE_CX2_FORCE_MIN" +#define SS_CX2_LP_FORCE_MAP_MAX "SS_TOUCH_IDLE_CX2_FORCE_MAX" +#define SS_CX2_LP_SENSE_MAP_MIN "SS_TOUCH_IDLE_CX2_SENSE_MIN" +#define SS_CX2_LP_SENSE_MAP_MAX "SS_TOUCH_IDLE_CX2_SENSE_MAX" +#define SS_CX2_LP_FORCE_ADJV_MAP_MAX "SS_TOUCH_IDLE_CX2_ADJ_VERTICAL" +#define SS_CX2_LP_SENSE_ADJH_MAP_MAX "SS_TOUCH_IDLE_CX2_ADJ_HORIZONTAL" + + +/* TOTAL SS */ +#define SS_TOTAL_IX_LP_FORCE_MAP_MIN "SS_TOUCH_IDLE_TOTAL_IX_FORCE_MIN" +#define SS_TOTAL_IX_LP_FORCE_MAP_MAX "SS_TOUCH_IDLE_TOTAL_IX_FORCE_MAX" +#define SS_TOTAL_IX_LP_SENSE_MAP_MIN "SS_TOUCH_IDLE_TOTAL_IX_SENSE_MIN" +#define SS_TOTAL_IX_LP_SENSE_MAP_MAX "SS_TOUCH_IDLE_TOTAL_IX_SENSE_MAX" +#define SS_TOTAL_IX_LP_FORCE_ADJV_MAP_MAX \ + "SS_TOUCH_IDLE_TOTAL_IX_ADJ_VERTICAL" +#define SS_TOTAL_IX_LP_SENSE_ADJH_MAP_MAX \ + "SS_TOUCH_IDLE_TOTAL_IX_ADJ_HORIZONTAL" +#define SS_TOTAL_CX_LP_FORCE_MAP_MIN "SS_TOUCH_IDLE_TOTAL_CX_FORCE_MIN" +#define SS_TOTAL_CX_LP_FORCE_MAP_MAX "SS_TOUCH_IDLE_TOTAL_CX_FORCE_MAX" +#define SS_TOTAL_CX_LP_SENSE_MAP_MIN "SS_TOUCH_IDLE_TOTAL_CX_SENSE_MIN" +#define SS_TOTAL_CX_LP_SENSE_MAP_MAX "SS_TOUCH_IDLE_TOTAL_CX_SENSE_MAX" +#define SS_TOTAL_CX_LP_FORCE_ADJV_MAP_MAX \ + "SS_TOUCH_IDLE_TOTAL_CX_ADJ_VERTICAL" +#define SS_TOTAL_CX_LP_SENSE_ADJH_MAP_MAX \ + "SS_TOUCH_IDLE_TOTAL_CX_ADJ_HORIZONTAL" + +/* KEYS */ +#define MS_KEY_RAW_MIN_MAX "MS_KEY_RAW_DATA_MIN_MAX" +#define MS_KEY_CX1_MIN_MAX "MS_KEY_CX1_MIN_MAX" +#define MS_KEY_CX2_MAP_MIN "MS_KEY_CX2_MIN" +#define MS_KEY_CX2_MAP_MAX "MS_KEY_CX2_MAX" +#define MS_KEY_TOTAL_CX_MAP_MIN "MS_KEY_TOTAL_CX_MIN" +#define MS_KEY_TOTAL_CX_MAP_MAX "MS_KEY_TOTAL_CX_MAX" + +/* CONSTANT TOTAL IX */ +#define SS_IX1_FORCE_W "IX1_FORCE_W" +#define SS_IX2_FORCE_W "IX2_FORCE_W" +#define SS_IX1_SENSE_W "IX1_SENSE_W" +#define SS_IX2_SENSE_W "IX2_SENSE_W" +/** @}*/ + + +/** + * Struct used to specify which test perform during the Mass Production Test. + * For each test item selected in this structure, there should be one or + * more labels associated in the Limit file from where load the thresholds + */ +typedef struct { + int MutualRaw; /* /< MS Raw min/Max test */ + int MutualRawMap; /* /< MS Raw min/Max test for each node */ + int MutualRawGap; /* /< MS Raw Gap(max-min) test */ + int MutualRawAdj; /* /< MS Raw Adjacent test */ + int MutualRawAdjGap; /* /< MS Raw Adjacent Gap (max-min) test */ + int MutualRawAdjPeak; /* /< MS Raw Adjacent Peak + * max(max(adjv),max(adjh)) test */ + int MutualRawLP; /* /< MS Low Power Raw min/Max test */ + int MutualRawMapLP; /* /< MS Low Power Raw min/Max test + * for each node */ + int MutualRawGapLP; /* /< MS Low Power Raw Gap(max-min) test */ + int MutualRawAdjLP; /* /< MS Low Power Raw Adjacent test */ + int MutualRawAdjITO; /* /< MS Raw Adjacent test during ITO test */ + + int MutualCx1; /* /< MS Cx1 min/Max test */ + int MutualCx2; /* /< MS Cx2 min/Max (for each node) test */ + int MutualCx2Adj; /* /< MS Vertical and Horizontal Adj Cx2 min/Max + * (for each node) test */ + int MutualCxTotal; /* /< MS Total Cx min/Max (for each node) test + * */ + int MutualCxTotalAdj; /* /< MS Total vertical and Horizontal Adj Cx2 + * min/Max (for each node) test */ + + int MutualCx1LP; /* /< MS LowPower Cx1 min/Max test */ + int MutualCx2LP; /* /< MS LowPower Cx2 min/Max (for each node) test */ + int MutualCx2TotalLP; /* /< MS LowPower Vertical and Horizontal Adj Cx2 min/Max + * (for each node) test */ + int MutualCxTotalLP; /* /< MS Total LowPower Cx min/Max (for each node) test + * */ + int MutualCxTotalAdjLP; /* /< MS Total LowPower vertical and Horizontal Adj Cx2 + * min/Max (for each node) test */ + + int MutualKeyRaw; /* /< MS Raw Key min/Max test */ + int MutualKeyCx1; /* /< MS Cx1 Key min/Max test */ + int MutualKeyCx2; /* /< MS Cx2 Key min/Max (for each node) test */ + int MutualKeyCxTotal; /* /< MS Total Cx Key min/Max (for each node) + * test */ + int SelfHoverForceRaw; /* SS Hover Force Raw min/Max test */ + int SelfHoverSenceRaw; /*SS Hover Sence Raw min/Max test */ + int SelfHoverForceIxTotal; /*SS Hover Total Force Ix min/Max (for each node)* test */ + int SelfHoverSenceIxTotal; /*SS Hover Total Sence Ix min/Max (for each node)* test */ + int SelfForceRaw; /* /< SS Force Raw min/Max test */ + int SelfForceRawGap; /* /< SS Force Raw Gap(max-min) test */ + int SelfForceRawMap; /* /< SS Force Raw min/Max Map test */ + int SelfForceRawLP; /* /< SS Low Power Force Raw min/Max test */ + int SelfForceRawGapLP; /* /< SS Low Power Force Raw Gap(max-min)test */ + int SelfForceRawMapLP; /* /< SS Low Power Force Raw min/Max Map test */ + + int SelfForceIx1; /* /< SS Force Ix1 min/Max test */ + int SelfForceIx2; /* /< SS Force Ix2 min/Max (for each node) test + * */ + int SelfForceIx2Adj; /* /< SS Vertical Adj Force Ix2 min/Max + * (for each node) test */ + int SelfForceIxTotal; /* /< SS Total Force Ix min/Max (for each node) + * test */ + int SelfForceIxTotalAdj; /* /< SS Total Vertical Adj Force Ix + * min/Max (for each node) test */ + int SelfForceCx1; /* /< SS Force Cx1 min/Max test */ + int SelfForceCx2; /* /< SS Force Cx2 min/Max (for each node) test */ + int SelfForceCx2Adj; /* /< SS Vertical Adj Force Cx2 min/Max (for + * each node) test */ + int SelfForceCxTotal; /* /< SS Total Force Cx min/Max (for each node) + * test */ + int SelfForceCxTotalAdj; /* /< SS Total Vertical Adj Force Cx + * min/Max (for each node) test */ + + int SelfForceIx1LP; /* /< SS LP Force Ix1 min/Max test */ + int SelfForceIx2LP; /* /< SS LP Force Ix2 min/Max (for each node) + * test */ + int SelfForceIx2AdjLP; /* /< SS LP Vertical Adj Force Ix2 min/Max + * (for each node) test */ + int SelfForceIxTotalLP; /* /< SS LP Total Force Ix min/Max + * (for each node) test */ + int SelfForceIxTotalAdjLP; /* /< SS LP Total Vertical Adj Force Ix + * min/Max (for each node) test */ + int SelfForceCx1LP; /* /< SS LP Force Cx1 min/Max test */ + int SelfForceCx2LP; /* /< SS LP Force Cx2 min/Max (for each node) test */ + int SelfForceCx2AdjLP; /* /< SS LP Vertical Adj Force Cx2 min/Max (for + * each node) test */ + int SelfForceCxTotalLP; /* /< SS LP Total Force Cx min/Max + * (for each node) test */ + int SelfForceCxTotalAdjLP; /* /< SS LP Total Vertical Adj Force Cx + * min/Max (for each node) test */ + + int SelfSenseRaw; /* /< SS Sense Raw min/Max test */ + int SelfSenseRawGap; /* /< SS Sense Raw Gap(max-min) test */ + int SelfSenseRawMap; /* /< SS Sense Raw min/Max test for each node */ + int SelfSenseRawLP; /* /< SS Low Power Sense Raw min/Max test */ + int SelfSenseRawGapLP; /* /< SS Low Power Sense Raw Gap(max-min) test */ + int SelfSenseRawMapLP; /* /< SS Low Power Sense Raw min/Max test for + * each node */ + + int SelfSenseIx1; /* /< SS Sense Ix1 min/Max test */ + int SelfSenseIx2; /* /< SS Sense Ix2 min/Max (for each node) test */ + int SelfSenseIx2Adj; /* /< SS Horizontal Adj Sense Ix2 min/Max + * (for each node) test */ + int SelfSenseIxTotal; /* /< SS Total Horizontal Sense Ix min/Max + * (for each node) test */ + int SelfSenseIxTotalAdj; /* /< SS Total Horizontal Adj Sense Ix + * min/Max (for each node) test */ + int SelfSenseCx1; /* /< SS Sense Cx1 min/Max test */ + int SelfSenseCx2; /* /< SS Sense Cx2 min/Max (for each node) test */ + int SelfSenseCx2Adj; /* /< SS Horizontal Adj Sense Cx2 min/Max + * (for each node) test */ + int SelfSenseCxTotal; /* /< SS Total Sense Cx min/Max (for each node) + * test */ + int SelfSenseCxTotalAdj; /* /< SS Total Horizontal Adj Sense Cx + * min/Max (for each node) test */ + + int SelfSenseIx1LP; /* /< SS LP Sense Ix1 min/Max test */ + int SelfSenseIx2LP; /* /< SS LP Sense Ix2 min/Max (for each node) + * test */ + int SelfSenseIx2AdjLP; /* /< SS LP Horizontal Adj Sense Ix2 min/Max + * (for each node) test */ + int SelfSenseIxTotalLP; /* /< SS LP Total Horizontal Sense Ix min/Max + * (for each node) test */ + int SelfSenseIxTotalAdjLP; /* /< SS LP Total Horizontal Adj Sense Ix + * min/Max (for each node) test */ + int SelfSenseCx1LP; /* /< SS LP Sense Cx1 min/Max test */ + int SelfSenseCx2LP; /* /< SS LP Sense Cx2 min/Max (for each node) + * test */ + int SelfSenseCx2AdjLP; /* /< SS LP Horizontal Adj Sense Cx2 min/Max + * (for each node) test */ + int SelfSenseCxTotalLP; /* /< SS LP Total Sense Cx min/Max + * (for each node) test */ + int SelfSenseCxTotalAdjLP; /* /< SS LP Total Horizontal Adj Sense Cx + * min/Max (for each node) test */ +} TestToDo; + + +#define MAX_LIMIT_FILE_NAME 128 /* /< max number of chars of the limit file name + * */ + +/** + * Struct which store the data coming from a Production Limit File + */ +typedef struct { + char *data; /* /< pointer to an array of char which contains + * the content of the Production Limit File */ + int size; /* /< size of data */ + char name[MAX_LIMIT_FILE_NAME]; /* /< identifier of the source from + * where the limits data were loaded (if + * loaded from a file it will be the + * file name, while if loaded from .h + * will be "NULL") */ +} LimitFile; + + + +int initTestToDo(void); +/**@}*/ + +/**@}*/ + + +int computeAdjHoriz(i8 *data, int row, int column, u8 **result); +int computeAdjHorizTotal(short *data, int row, int column, u16 **result); +int computeAdjVert(i8 *data, int row, int column, u8 **result); +int computeAdjVertTotal(short *data, int row, int column, u16 **result); +int computeAdjHorizFromU(u8 *data, int row, int column, u8 **result); +int computeAdjHorizTotalFromU(u16 *data, int row, int column, u16 **result); +int computeAdjVertFromU(u8 *data, int row, int column, u8 **result); +int computeAdjVertTotalFromU(u16 *data, int row, int column, u16 **result); +int checkLimitsMinMax(short *data, int row, int column, int min, int max); +int checkLimitsMap(i8 *data, int row, int column, int *min, int *max); +int checkLimitsMapTotal(short *data, int row, int column, int *min, int *max); +int checkLimitsMapFromU(u8 *data, int row, int column, int *min, int *max); +int checkLimitsMapTotalFromU(u16 *data, int row, int column, int *min, + int *max); +int checkLimitsMapAdj(u8 *data, int row, int column, int *max); +int checkLimitsMapAdjTotal(u16 *data, int row, int column, int *max); +int checkLimitsGap(short *data, int row, int column, int threshold); +int checkLimitsGapOffsets(short *data, int row, int column, int threshold, + int row_start, int column_start, int row_end, int column_end); + +/** @defgroup mp_api MP API + * @ingroup mp_test + * Functions to execute the MP test. + * The parameters of these functions allow to customize their behavior + * in order to satisfy different scenarios + * @{ + */ +int production_test_ito(char *path_limits, TestToDo *todo); +int production_test_initialization(u8 type); +int production_test_main(char *pathThresholds, int stop_on_fail, int saveInit, + TestToDo *todo, u8 mpflag); +int production_test_ms_raw(char *path_limits, int stop_on_fail, TestToDo *todo); +int production_test_ms_raw_lp(char *path_limits, int stop_on_fail, + TestToDo *todo); +int production_test_ms_cx(char *path_limits, int stop_on_fail, TestToDo *todo); +int production_test_ms_cx_lp(char *path_limits, int stop_on_fail, + TestToDo *todo); +int production_test_ss_raw(char *path_limits, int stop_on_fail, TestToDo *todo); +int production_test_ss_raw_lp(char *path_limits, int stop_on_fail, + TestToDo *todo); +int production_test_ss_ix_cx(char *path_limits, int stop_on_fail, + TestToDo *todo); +int production_test_ss_ix_cx_lp(char *path_limits, int stop_on_fail, + TestToDo *todo); +int production_test_data(char *path_limits, int stop_on_fail, TestToDo *todo); +int production_test_ms_key_cx(char *path_limits, int stop_on_fail, + TestToDo *todo); +int production_test_ms_key_raw(char *path_limits); +/** @}*/ + +/** + * @addtogroup limit_file + * @{ + */ +int parseProductionTestLimits(char *path, LimitFile *file, char *label, + int **data, int *row, int *column); +int readLine(char *data, char *line, int size, int *n); +int getLimitsFile(char *path, LimitFile *file); +int freeLimitsFile(LimitFile *file); +int freeCurrentLimitsFile(void); +/**@}*/ + +#endif diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsTime.c b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsTime.c new file mode 100644 index 000000000000..13637ac1e050 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsTime.c @@ -0,0 +1,85 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Utility for mesuring/handling the time * + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsTime.c + * \brief Contains all functions to handle and measure the time in the driver + */ + +#include "ftsTime.h" + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * Take the starting time and save it in a StopWatch variable + * @param w pointer of a StopWatch struct + */ +void startStopWatch(StopWatch *w) +{ + ktime_get_ts64(&w->start); +} + +/** + * Take the stop time and save it in a StopWatch variable + * @param w pointer of a StopWatch struct + */ +void stopStopWatch(StopWatch *w) +{ + ktime_get_ts64(&w->end); +} + +/** + * Compute the amount of time spent from when the startStopWatch and then + * the stopStopWatch were called on the StopWatch variable + * @param w pointer of a StopWatch struct + * @return amount of time in ms (the return value is meaningless + * if the startStopWatch and stopStopWatch were not called before) + */ +int elapsedMillisecond(StopWatch *w) +{ + int result; + + result = ((w->end.tv_sec - w->start.tv_sec) * 1000) + (w->end.tv_nsec - + w->start.tv_nsec) + / 1000000; + return result; +} + +/** + * Compute the amount of time spent from when the startStopWatch and + * then the stopStopWatch were called on the StopWatch variable + * @param w pointer of a StopWatch struct + * @return amount of time in ns (the return value is meaningless + * if the startStopWatch and stopStopWatch were not called before) + */ +int elapsedNanosecond(StopWatch *w) +{ + int result; + + result = ((w->end.tv_sec - w->start.tv_sec) * 1000000000) + + (w->end.tv_nsec - w->start.tv_nsec); + return result; +} diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsTime.h b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsTime.h new file mode 100644 index 000000000000..855aa6b114d4 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsTime.h @@ -0,0 +1,73 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Utility for measuring/handling the time * + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsTime.h + * \brief Contains all the definitions and structs to handle and measure the + * time in the driver + */ + +#ifndef FTS_TIME_H +#define FTS_TIME_H + + +#include + +/* TIMEOUT */ +/** @defgroup timeouts Timeouts + * Definitions of all the Timeout used in several operations + * @{ + */ +#define TIMEOUT_RESOLUTION 50 +/* /< timeout resolution in ms (all timeout should be multiples of this unit) */ +#define GENERAL_TIMEOUT (15 * TIMEOUT_RESOLUTION) +/* /< general timeout in ms */ +#define RELEASE_INFO_TIMEOUT (2 * TIMEOUT_RESOLUTION) +/* /< timeout to request release info in ms */ + + +#define TIMEOUT_REQU_COMP_DATA (4 * TIMEOUT_RESOLUTION) +/* /< timeout to request compensation data in ms */ +#define TIMEOUT_REQU_DATA (8 * TIMEOUT_RESOLUTION) +/* /< timeout to request data in ms */ +#define TIMEOUT_ITO_TEST_RESULT (4 * TIMEOUT_RESOLUTION) +/* /< timeout to perform ito test in ms */ +#define TIMEOUT_INITIALIZATION_TEST_RESULT (5000 * TIMEOUT_RESOLUTION) +/* /< timeout to perform initialization test in ms */ +#define TIEMOUT_ECHO (50 * TIMEOUT_RESOLUTION) +/* /< timeout of the echo command,*/ +#define TIMEOUT_ECHO_FPI (200 * TIMEOUT_RESOLUTION) +/* /< timeout of the Full panel Init echo command */ +#define TIMEOUT_ECHO_SINGLE_ENDED_SPECIAL_AUTOTUNE \ + (100 * TIMEOUT_RESOLUTION) +/* /< timeout of the Single ended special Autotune echo command */ +/** @}*/ + + +/** + * Struct used to measure the time elapsed between a starting and ending point. + */ +typedef struct { + struct timespec64 start; /* /< store the starting time */ + struct timespec64 end; /* /< store the finishing time */ +} StopWatch; + + +void startStopWatch(StopWatch *w); +void stopStopWatch(StopWatch *w); +int elapsedMillisecond(StopWatch *w); +int elapsedNanosecond(StopWatch *w); + +#endif diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsTool.c b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsTool.c new file mode 100644 index 000000000000..f062bcbae00c --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsTool.c @@ -0,0 +1,715 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Utility Functions * + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsTool.c + * \brief Contains all the functions to support common operation inside the + *driver + */ + +#include "ftsCompensation.h" +#include "ftsCore.h" +#include "ftsError.h" +#include "ftsHardware.h" +#include "ftsIO.h" +#include "ftsSoftware.h" +#include "ftsTime.h" +#include "ftsTool.h" +#include "../fts.h" /* needed for the tag define */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * Print an array of byte in a HEX string and attach at the beginning a label. + * The function allocate memory that should be free outside the function itself + * @param label string to attach at the beginning + * @param buff pointer to the byte array that should be printed as HEX string + * @param count size of buff + * @param result pointer to the array of characters that compose the HEX final + * string + * @return pointer to the array of characters that compose the HEX string, + * (same address of result) + * @warning result MUST be allocated outside the function and should be + * big enough to contain the data converted as HEX! + */ +char *printHex(char *label, u8 *buff, int count, u8 *result) +{ + int i, offset; + + offset = strlen(label); + strlcpy(result, label, offset+1); /* +1 for terminator char */ + + for (i = 0; i < count; i++) { + snprintf(&result[offset], 4, "%02X ", buff[i]); + /* this append automatically a null terminator char */ + offset += 3; + } + return result; +} + +/** + * Clear the FIFO from any event + * @return OK if success or an error code which specify the type of error + */ +int flushFIFO(void) +{ + int ret; + u8 sett = SPECIAL_FIFO_FLUSH; + + ret = writeSysCmd(SYS_CMD_SPECIAL, &sett, 1); /* flush the FIFO */ + if (ret < OK) { + logError(1, "%s flushFIFO: ERROR %08X\n", tag, ret); + return ret; + } + + logError(0, "%s FIFO flushed!\n", tag); + return OK; +} + + + +/** + * Convert an array of bytes to an array of u16 taking two bytes at time, + * src has LSB first. + * @param src pointer to the source byte array + * @param src_length size of src + * @param dst pointer to the destination array. + * @return the final size of dst (half of the source) or ERROR_OP_NOT_ALLOW + * if the size of src is not multiple of 2. + */ +int u8ToU16n(u8 *src, int src_length, u16 *dst) +{ + int i, j; + + if (src_length % 2 != 0) + return ERROR_OP_NOT_ALLOW; + else { + j = 0; + dst = (u16 *)kmalloc((src_length / 2) * sizeof(u16), + GFP_KERNEL); + for (i = 0; i < src_length; i += 2) { + dst[j] = ((src[i + 1] & 0x00FF) << 8) + (src[i] & + 0x00FF); + j++; + } + } + + return src_length / 2; +} + +/** + * Convert an array of 2 bytes to a u16, src has LSB first (little endian). + * @param src pointer to the source byte array + * @param dst pointer to the destination u16. + * @return OK + */ +int u8ToU16(u8 *src, u16 *dst) +{ + *dst = (u16)(((src[1] & 0x00FF) << 8) + (src[0] & 0x00FF)); + return OK; +} + +/** + * Convert an array of 2 bytes to a u16, src has MSB first (big endian). + * @param src pointer to the source byte array + * @param dst pointer to the destination u16. + * @return OK + */ +int u8ToU16_be(u8 *src, u16 *dst) +{ + *dst = (u16)(((src[0] & 0x00FF) << 8) + (src[1] & 0x00FF)); + return OK; +} + +/** + * Convert an array of u16 to an array of u8, dst has MSB first (big endian). + * @param src pointer to the source array of u16 + * @param src_length size of src + * @param dst pointer to the destination array of u8. This array should be free + * when no need anymore + * @return size of dst (src size multiply by 2) + */ +int u16ToU8n_be(u16 *src, int src_length, u8 *dst) +{ + int i, j; + + dst = (u8 *)kmalloc((2 * src_length) * sizeof(u8), GFP_KERNEL); + j = 0; + for (i = 0; i < src_length; i++) { + dst[j] = (u8)(src[i] & 0xFF00) >> 8; + dst[j + 1] = (u8)(src[i] & 0x00FF); + j += 2; + } + + return src_length * 2; +} + +/** + * Convert a u16 to an array of 2 u8, dst has MSB first (big endian). + * @param src u16 to convert + * @param dst pointer to the destination array of 2 u8. + * @return OK + */ +int u16ToU8_be(u16 src, u8 *dst) +{ + dst[0] = (u8)((src & 0xFF00) >> 8); + dst[1] = (u8)(src & 0x00FF); + return OK; +} + + +/** + * Convert a u16 to an array of 2 u8, dst has LSB first (little endian). + * @param src u16 to convert + * @param dst pointer to the destination array of 2 u8. + * @return OK + */ +int u16ToU8(u16 src, u8 *dst) +{ + dst[1] = (u8)((src & 0xFF00) >> 8); + dst[0] = (u8)(src & 0x00FF); + return OK; +} + +/** + * Convert an array of bytes to a u32, src has LSB first (little endian). + * @param src array of bytes to convert + * @param dst pointer to the destination u32 variable. + * @return OK + */ +int u8ToU32(u8 *src, u32 *dst) +{ + *dst = (u32)(((src[3] & 0xFF) << 24) + ((src[2] & 0xFF) << 16) + + ((src[1] & 0xFF) << 8) + (src[0] & 0xFF)); + return OK; +} + +/** + * Convert an array of bytes to a u32, src has MSB first (big endian). + * @param src array of bytes to convert + * @param dst pointer to the destination u32 variable. + * @return OK + */ +int u8ToU32_be(u8 *src, u32 *dst) +{ + *dst = (u32)(((src[0] & 0xFF) << 24) + ((src[1] & 0xFF) << 16) + + ((src[2] & 0xFF) << 8) + (src[3] & 0xFF)); + return OK; +} + + +/** + * Convert a u32 to an array of 4 bytes, dst has LSB first (little endian). + * @param src u32 value to convert + * @param dst pointer to the destination array of 4 bytes. + * @return OK + */ +int u32ToU8(u32 src, u8 *dst) +{ + dst[3] = (u8)((src & 0xFF000000) >> 24); + dst[2] = (u8)((src & 0x00FF0000) >> 16); + dst[1] = (u8)((src & 0x0000FF00) >> 8); + dst[0] = (u8)(src & 0x000000FF); + return OK; +} + +/** + * Convert a u32 to an array of 4 bytes, dst has MSB first (big endian). + * @param src u32 value to convert + * @param dst pointer to the destination array of 4 bytes. + * @return OK + */ +int u32ToU8_be(u32 src, u8 *dst) +{ + dst[0] = (u8)((src & 0xFF000000) >> 24); + dst[1] = (u8)((src & 0x00FF0000) >> 16); + dst[2] = (u8)((src & 0x0000FF00) >> 8); + dst[3] = (u8)(src & 0x000000FF); + return OK; +} + +/** + * Execute a function passed as argment and retry it defined number of times if + *not successfull + * @param code pointer to a function which return an int and doesn't have any + *parameters + * @param wait_before_retry interval of time in ms to wait between one trial + *and another one + * @param retry_count max number of retry to attemp + * @return last return value obtained from the last execution of the code + *function + */ +int attempt_function(int (*code)(void), unsigned long wait_before_retry, int + retry_count) +{ + int result; + int count = 0; + + do { + result = code(); + count++; + msleep(wait_before_retry); + } while (count < retry_count && result < 0); + + + if (count == retry_count) + return result | ERROR_TIMEOUT; + else + return result; +} + +/** + * Enable all the possible sensing mode supported by the FW + * @return OK if success or an error code which specify the type of error + */ +int senseOn(void) +{ + int ret; + + ret = setScanMode(SCAN_MODE_ACTIVE, 0xFF); /* enable all */ + if (ret < OK) { + logError(1, "%s senseOn: ERROR %08X\n", tag, ret); + return ret; + } + + logError(0, "%s senseOn: SENSE ON\n", tag); + return OK; +} + +/** + * Disable all the sensing mode + * @return OK if success or an error code which specify the type of error + */ +int senseOff(void) +{ + int ret; + + ret = setScanMode(SCAN_MODE_ACTIVE, 0x00); + if (ret < OK) { + logError(1, "%s senseOff: ERROR %08X\n", tag, ret); + return ret; + } + + logError(0, "%s senseOff: SENSE OFF\n", tag); + return OK; +} + + + +/** + * Clean up the IC status executing a system reset and giving + * the possibility to re-enabling the sensing + * @param enableTouch if 1, re-enable the sensing and the interrupt of the IC + * @return OK if success or an error code which specify the type of error + */ +int cleanUp(int enableTouch) +{ + int res; + + logError(0, "%s cleanUp: system reset...\n", tag); + res = fts_system_reset(); + if (res < OK) + return res; + if (enableTouch) { + logError(0, "%s cleanUp: enabling touches...\n", tag); + res = senseOn(); /* already enable everything */ + if (res < OK) + return res; + logError(0, "%s cleanUp: enabling interrupts...\n", tag); + res = fts_enableInterrupt(); + if (res < OK) + return res; + } + return OK; +} + +/** + * Transform an array of short in a matrix of short with a defined number of + * columns and the resulting number of rows + * @param data array of bytes to convert + * @param size size of data + * @param columns number of columns that the resulting matrix should have. + * @return a reference to a matrix of short where for each row there are + * columns elements + * @warning If size = 0 it will be allocated a matrix 1*1 wich still should + * be free + */ +short **array1dTo2d_short(short *data, int size, int columns) +{ + int i; + short **matrix = NULL; + if (size == 0) { + matrix = (short **)kmalloc(1 * + sizeof(short *), GFP_KERNEL); + matrix[0] = (short *)kmalloc(0 * + sizeof(short), GFP_KERNEL); + } else { + matrix = (short **)kmalloc(((int)(size / columns)) * + sizeof(short *), GFP_KERNEL); + + if (matrix != NULL) { + for (i = 0; i < (int)(size / columns); i++) + matrix[i] = (short *)kmalloc(columns * + sizeof(short), GFP_KERNEL); + + for (i = 0; i < size; i++) + matrix[i / columns][i % columns] = data[i]; + } + } + + return matrix; +} + +/** + * Transform an array of u16 in a matrix of u16 with a defined number of + * columns and the resulting number of rows + * @param data array of bytes to convert + * @param size size of data + * @param columns number of columns that the resulting matrix should have. + * @return a reference to a matrix of u16 where for each row there are columns + * elements + * @warning If size = 0 it will be allocated a matrix 1*1 wich still should + * be free + */ +u16 **array1dTo2d_u16(u16 *data, int size, int columns) +{ + int i; + u16 **matrix = NULL; + + + if (size == 0) { + matrix = (u16 **)kmalloc(1 * + sizeof(u16 *), GFP_KERNEL); + matrix[0] = (u16 *)kmalloc(0 * + sizeof(u16), GFP_KERNEL); + } else { + matrix = (u16 **)kmalloc(((int)(size / columns)) * + sizeof(u16 *), GFP_KERNEL); + + if (matrix != NULL) { + for (i = 0; i < (int)(size / columns); i++) + matrix[i] = (u16 *)kmalloc(columns * + sizeof(u16), GFP_KERNEL); + + for (i = 0; i < size; i++) + matrix[i / columns][i % columns] = data[i]; + } + } + + return matrix; +} + +/** + * Transform an array of u8 in a matrix of u8 with a defined number of + * columns and the resulting number of rows + * @param data array of bytes to convert + * @param size size of data + * @param columns number of columns that the resulting matrix should have. + * @return a reference to a matrix of short where for each row there are + * columns elements + * @warning If size = 0 it will be allocated a matrix 1*1 wich still should + * be free + */ +u8 **array1dTo2d_u8(u8 *data, int size, int columns) +{ + int i; + u8 **matrix = NULL; + + + if (size == 0) { + matrix = (u8 **)kmalloc(1 * + sizeof(u8 *), GFP_KERNEL); + + matrix[0] = (u8 *)kmalloc(0 * + sizeof(u8), GFP_KERNEL); + } else { + + matrix = (u8 **)kmalloc(((int)(size / columns)) * sizeof(u8 *), + GFP_KERNEL); + + if (matrix != NULL) { + for (i = 0; i < (int)(size / columns); i++) + matrix[i] = (u8 *)kmalloc(columns * sizeof(u8), + GFP_KERNEL); + + for (i = 0; i < size; i++) + matrix[i / columns][i % columns] = data[i]; + } + } + + return matrix; +} + +/** + * Transform an array of i8 in a matrix of i8 with a defined number of + * columns and the resulting number of rows + * @param data array of bytes to convert + * @param size size of data + * @param columns number of columns that the resulting matrix should have. + * @return a reference to a matrix of short where for each row there are + * columns elements + * @warning If size = 0 it will be allocated a matrix 1*1 wich still should + * be free + */ +i8 **array1dTo2d_i8(i8 *data, int size, int columns) +{ + int i; + i8 **matrix = NULL; + + if (size == 0) { + matrix = (i8 **)kmalloc(1 * + sizeof(i8 *), GFP_KERNEL); + matrix[0] = (i8 *)kmalloc(0 * + sizeof(i8), GFP_KERNEL); + } else { + matrix = (i8 **)kmalloc(((int)(size / columns)) * sizeof(i8 *), + GFP_KERNEL); + + if (matrix != NULL) { + for (i = 0; i < (int)(size / columns); i++) + matrix[i] = (i8 *)kmalloc(columns * sizeof(i8), + GFP_KERNEL); + + for (i = 0; i < size; i++) + matrix[i / columns][i % columns] = data[i]; + } + } + + return matrix; +} + +/** + * Print in the kernel log a label followed by a matrix of short row x columns + *and free its memory + * @param label pointer to the string to print before the actual matrix + * @param matrix reference to the matrix of short which contain the actual data + * @param row number of rows on which the matrix should be print + * @param column number of columns for each row + */ +void print_frame_short(char *label, short **matrix, int row, int column) +{ + int i, j; + + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) + printk("%d ", matrix[i][j]); + logError(0, "\n"); + kfree(matrix[i]); + } + kfree(matrix); +} + +/** + * Print in the kernel log a label followed by a matrix of u16 row x columns + *and free its memory + * @param label pointer to the string to print before the actual matrix + * @param matrix reference to the matrix of u16 which contain the actual data + * @param row number of rows on which the matrix should be print + * @param column number of columns for each row + */ +void print_frame_u16(char *label, u16 **matrix, int row, int column) +{ + int i, j; + + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) + printk("%d ", matrix[i][j]); + logError(0, "\n"); + kfree(matrix[i]); + } + kfree(matrix); +} + +/** + * Print in the kernel log a label followed by a matrix of u8 row x columns and + *free its memory + * @param label pointer to the string to print before the actual matrix + * @param matrix reference to the matrix of u8 which contain the actual data + * @param row number of rows on which the matrix should be print + * @param column number of columns for each row + */ +void print_frame_u8(char *label, u8 **matrix, int row, int column) +{ + int i, j; + + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) + printk("%d ", matrix[i][j]); + logError(0, "\n"); + kfree(matrix[i]); + } + kfree(matrix); +} + +/** + * Print in the kernel log a label followed by a matrix of i8 row x columns and + * free its memory + * @param label pointer to the string to print before the actual matrix + * @param matrix reference to the matrix of u8 which contain the actual data + * @param row number of rows on which the matrix should be print + * @param column number of columns for each row + */ +void print_frame_i8(char *label, i8 **matrix, int row, int column) +{ + int i, j; + + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) + printk("%d ", matrix[i][j]); + logError(0, "\n"); + kfree(matrix[i]); + } + kfree(matrix); +} + +/** + * Print in the kernel log a label followed by a matrix of u32 row x columns + *and free its memory + * @param label pointer to the string to print before the actual matrix + * @param matrix reference to the matrix of u32 which contain the actual data + * @param row number of rows on which the matrix should be print + * @param column number of columns for each row + */ +void print_frame_u32(char *label, u32 **matrix, int row, int column) +{ + int i, j; + + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) + printk("%d ", matrix[i][j]); + logError(0, "\n"); + kfree(matrix[i]); + } + kfree(matrix); +} + +/** + * Print in the kernel log a label followed by a matrix of int row x columns + * and free its memory + * @param label pointer to the string to print before the actual matrix + * @param matrix reference to the matrix of int which contain the actual data + * @param row number of rows on which the matrix should be print + * @param column number of columns for each row + */ +void print_frame_int(char *label, int **matrix, int row, int column) +{ + int i, j; + + logError(0, "%s %s\n", tag, label); + for (i = 0; i < row; i++) { + logError(0, "%s ", tag); + for (j = 0; j < column; j++) + printk("%d ", matrix[i][j]); + logError(0, "\n"); + kfree(matrix[i]); + } + kfree(matrix); +} + + +/** + * Convert an array of bytes to an u64, src has MSB first (big endian). + * @param src array of bytes + * @param dest pointer to the destination u64. + * @param size size of src (can be <= 8) + * @return OK if success or ERROR_OP_NOT_ALLOW if size exceed 8 + */ +int u8ToU64_be(u8 *src, u64 *dest, int size) +{ + int i = 0; + + /* u64 temp =0; */ + if (size > sizeof(u64)) + return ERROR_OP_NOT_ALLOW; + else { + *dest = 0; + + for (i = 0; i < size; i++) + *dest |= (u64)(src[i]) << ((size - 1 - i) * 8); + + + return OK; + } +} + +/** + * Convert an u64 to an array of bytes, dest has MSB first (big endian). + * @param src value of u64 + * @param dest pointer to the destination array of bytes. + * @param size size of src (can be <= 8) + * @return OK if success or ERROR_OP_NOT_ALLOW if size exceed 8 + */ +int u64ToU8_be(u64 src, u8 *dest, int size) +{ + int i = 0; + + if (size > sizeof(u64)) + return ERROR_OP_NOT_ALLOW; + else + for (i = 0; i < size; i++) + dest[i] = (u8)((src >> ((size - 1 - i) * 8)) & 0xFF); + + return OK; +} + + + +/*********** NEW API *************/ + +/** + * Convert a value of an id in a bitmask with a 1 in the position of the value + *of the id + * @param id Value of the ID to convert + * @param mask pointer to the bitmask that will be updated with the value of id + * @param size dimension in bytes of mask + * @return OK if success or ERROR_OP_NOT_ALLOW if size of mask is not enough to + *contain ID + */ +int fromIDtoMask(u8 id, u8 *mask, int size) +{ + if (((int)((id) / 8)) < size) { + logError(0, "%s %s: ID = %d Index = %d Position = %d !\n", tag, + __func__, id, ((int)((id) / 8)), (id % 8)); + mask[((int)((id) / 8))] |= 0x01 << (id % 8); + return OK; + } else { + logError(1, + "%s %s: Bitmask too small! Impossible contain ID = %d %d>=%d! ERROR %08X\n", + tag, __func__, id, ((int)((id) / 8)), size, + ERROR_OP_NOT_ALLOW); + return ERROR_OP_NOT_ALLOW; + } +} diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsTool.h b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsTool.h new file mode 100644 index 000000000000..8fbf13296577 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_lib/ftsTool.h @@ -0,0 +1,59 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com ** + ************************************************************************** + * * + * FTS Utility Functions * + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file ftsTool.h + * \brief Contains all the definitions to support common operations inside the + * driver + */ + +#ifndef FTS_TOOL_H +#define FTS_TOOL_H + + +char *printHex(char *label, u8 *buff, int count, u8 *result); +int u8ToU16(u8 *src, u16 *dst); +int u8ToU16_be(u8 *src, u16 *dst); +int u8ToU16n(u8 *src, int src_length, u16 *dst); +int u16ToU8(u16 src, u8 *dst); +int u16ToU8_be(u16 src, u8 *dst); +int u16ToU8n_be(u16 *src, int src_length, u8 *dst); +int u8ToU32(u8 *src, u32 *dst); +int u8ToU32_be(u8 *src, u32 *dst); +int u32ToU8(u32 src, u8 *dst); +int u32ToU8_be(u32 src, u8 *dst); +int u8ToU64_be(u8 *src, u64 *dest, int size); +int u64ToU8_be(u64 src, u8 *dest, int size); +int attempt_function(int (*code)(void), unsigned long wait_before_retry, int + retry_count); +int senseOn(void); +int senseOff(void); +void print_frame_short(char *label, short **matrix, int row, int column); +short **array1dTo2d_short(short *data, int size, int columns); +void print_frame_u16(char *label, u16 **matrix, int row, int column); +u16 **array1dTo2d_u16(u16 *data, int size, int columns); +u8 **array1dTo2d_u8(u8 *data, int size, int columns); +i8 **array1dTo2d_i8(i8 *data, int size, int columns); +void print_frame_u8(char *label, u8 **matrix, int row, int column); +void print_frame_i8(char *label, i8 **matrix, int row, int column); +void print_frame_u32(char *label, u32 **matrix, int row, int column); +void print_frame_int(char *label, int **matrix, int row, int column); +int cleanUp(int enableTouch); +int flushFIFO(void); + +/* New API */ +int fromIDtoMask(u8 id, u8 *mask, int size); + +#endif diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_limits.h b/drivers/input/touchscreen/stmicro_mmi/fts_limits.h new file mode 100644 index 000000000000..ce225fca854e --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_limits.h @@ -0,0 +1,2111 @@ +/*! + * \file fts_limits.h + * \brief Contains the size and the byte array of the production test limit + *file which contains thresholds used to test data + */ + +#ifndef FTS_LIMITS_H +#define FTS_LIMITS_H +/* This is an auto generated header file */ +/* --->Remember to change the name of the two variables!<--- */ +const uint32_t myArray2_size = 16725; /* /< size of the array containing the + * production limit file */ + +/** + * Array containing the production limit file + */ +const uint8_t myArray2[] = { + 0x2A, 0x53, 0x54, 0x4F, 0x50, 0x5F, 0x4F, 0x4E, 0x5F, 0x46, 0x41, 0x49, + 0x4C, 0x2C, 0x31, 0x2C, + 0x31, 0x0A, 0x31, 0x0A, 0x2A, 0x53, 0x54, 0x4F, 0x50, 0x5F, 0x4F, 0x4E, + 0x5F, 0x45, 0x52, 0x52, + 0x4F, 0x52, 0x2C, 0x31, 0x2C, 0x31, 0x0A, 0x31, 0x0A, 0x2A, 0x46, 0x4F, + 0x52, 0x43, 0x45, 0x5F, + 0x46, 0x57, 0x5F, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x2C, 0x31, 0x2C, + 0x31, 0x0A, 0x30, 0x0A, + 0x2A, 0x53, 0x53, 0x5F, 0x49, 0x58, 0x31, 0x5F, 0x46, 0x4F, 0x52, 0x43, + 0x45, 0x5F, 0x57, 0x2C, + 0x31, 0x2C, 0x31, 0x0A, 0x32, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x49, 0x58, + 0x32, 0x5F, 0x46, 0x4F, + 0x52, 0x43, 0x45, 0x5F, 0x57, 0x2C, 0x31, 0x2C, 0x31, 0x0A, 0x31, 0x0A, + 0x2A, 0x53, 0x53, 0x5F, + 0x49, 0x58, 0x31, 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x57, 0x2C, + 0x31, 0x2C, 0x31, 0x0A, + 0x32, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x49, 0x58, 0x32, 0x5F, 0x53, 0x45, + 0x4E, 0x53, 0x45, 0x5F, + 0x57, 0x2C, 0x31, 0x2C, 0x31, 0x0A, 0x31, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, + 0x52, 0x41, 0x57, 0x5F, + 0x44, 0x41, 0x54, 0x41, 0x5F, 0x4D, 0x49, 0x4E, 0x5F, 0x4D, 0x41, 0x58, + 0x2C, 0x31, 0x2C, 0x32, + 0x0A, 0x31, 0x30, 0x30, 0x30, 0x2C, 0x31, 0x35, 0x30, 0x30, 0x30, 0x0A, + 0x2A, 0x4D, 0x53, 0x5F, + 0x52, 0x41, 0x57, 0x5F, 0x44, 0x41, 0x54, 0x41, 0x5F, 0x47, 0x41, 0x50, + 0x2C, 0x31, 0x2C, 0x31, + 0x0A, 0x33, 0x30, 0x30, 0x30, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x54, 0x4F, + 0x55, 0x43, 0x48, 0x5F, + 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x43, 0x58, 0x31, 0x5F, 0x4D, + 0x49, 0x4E, 0x5F, 0x4D, + 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x0A, 0x30, 0x2C, 0x36, 0x33, 0x0A, + 0x2A, 0x4D, 0x53, 0x5F, + 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, + 0x5F, 0x43, 0x58, 0x32, + 0x5F, 0x4D, 0x49, 0x4E, 0x2C, 0x32, 0x32, 0x2C, 0x32, 0x37, 0x0A, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x2A, + 0x4D, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, + 0x49, 0x56, 0x45, 0x5F, + 0x43, 0x58, 0x32, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x32, 0x32, 0x2C, 0x32, + 0x37, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, + 0x48, 0x5F, 0x41, 0x43, + 0x54, 0x49, 0x56, 0x45, 0x5F, 0x43, 0x58, 0x32, 0x5F, 0x41, 0x44, 0x4A, + 0x5F, 0x48, 0x4F, 0x52, + 0x49, 0x5A, 0x4F, 0x4E, 0x54, 0x41, 0x4C, 0x2C, 0x32, 0x32, 0x2C, 0x32, + 0x36, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, + 0x41, 0x43, 0x54, 0x49, + 0x56, 0x45, 0x5F, 0x43, 0x58, 0x32, 0x5F, 0x41, 0x44, 0x4A, 0x5F, 0x56, + 0x45, 0x52, 0x54, 0x49, + 0x43, 0x41, 0x4C, 0x2C, 0x32, 0x31, 0x2C, 0x32, 0x37, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x2A, + 0x4D, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, + 0x49, 0x56, 0x45, 0x5F, + 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x43, 0x58, 0x5F, 0x4D, 0x49, 0x4E, + 0x2C, 0x32, 0x32, 0x2C, + 0x32, 0x37, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x0A, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x54, + 0x4F, 0x55, 0x43, 0x48, + 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x54, 0x4F, 0x54, 0x41, + 0x4C, 0x5F, 0x43, 0x58, + 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x32, 0x32, 0x2C, 0x32, 0x37, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, + 0x43, 0x54, 0x49, 0x56, + 0x45, 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x43, 0x58, 0x5F, 0x41, + 0x44, 0x4A, 0x5F, 0x48, + 0x4F, 0x52, 0x49, 0x5A, 0x4F, 0x4E, 0x54, 0x41, 0x4C, 0x2C, 0x32, 0x32, + 0x2C, 0x32, 0x36, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, + 0x48, 0x5F, 0x41, 0x43, + 0x54, 0x49, 0x56, 0x45, 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x43, + 0x58, 0x5F, 0x41, 0x44, + 0x4A, 0x5F, 0x56, 0x45, 0x52, 0x54, 0x49, 0x43, 0x41, 0x4C, 0x2C, 0x32, + 0x31, 0x2C, 0x32, 0x37, + 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x0A, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, 0x34, + 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x2C, 0x36, 0x34, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x4B, 0x45, + 0x59, 0x5F, 0x52, 0x41, + 0x57, 0x5F, 0x44, 0x41, 0x54, 0x41, 0x5F, 0x4D, 0x49, 0x4E, 0x5F, 0x4D, + 0x41, 0x58, 0x2C, 0x31, + 0x2C, 0x32, 0x0A, 0x30, 0x2C, 0x38, 0x30, 0x30, 0x30, 0x0A, 0x2A, 0x4D, + 0x53, 0x5F, 0x4B, 0x45, + 0x59, 0x5F, 0x43, 0x58, 0x31, 0x5F, 0x4D, 0x49, 0x4E, 0x5F, 0x4D, 0x41, + 0x58, 0x2C, 0x31, 0x2C, + 0x32, 0x0A, 0x30, 0x2C, 0x36, 0x34, 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x4B, + 0x45, 0x59, 0x5F, 0x43, + 0x58, 0x32, 0x5F, 0x4D, 0x49, 0x4E, 0x2C, 0x31, 0x2C, 0x33, 0x0A, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x2A, 0x4D, 0x53, 0x5F, 0x4B, 0x45, 0x59, 0x5F, 0x43, 0x58, 0x32, + 0x5F, 0x4D, 0x41, 0x58, + 0x2C, 0x31, 0x2C, 0x33, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, 0x36, + 0x34, 0x0A, 0x2A, 0x4D, + 0x53, 0x5F, 0x4B, 0x45, 0x59, 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, + 0x43, 0x58, 0x5F, 0x4D, + 0x49, 0x4E, 0x2C, 0x31, 0x2C, 0x33, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x0A, 0x2A, 0x4D, 0x53, + 0x5F, 0x4B, 0x45, 0x59, 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x43, + 0x58, 0x5F, 0x4D, 0x41, + 0x58, 0x2C, 0x31, 0x2C, 0x33, 0x0A, 0x36, 0x34, 0x2C, 0x36, 0x34, 0x2C, + 0x36, 0x34, 0x0A, 0x2A, + 0x53, 0x53, 0x5F, 0x52, 0x41, 0x57, 0x5F, 0x44, 0x41, 0x54, 0x41, 0x5F, + 0x46, 0x4F, 0x52, 0x43, + 0x45, 0x5F, 0x4D, 0x49, 0x4E, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, + 0x32, 0x0A, 0x31, 0x30, + 0x30, 0x30, 0x2C, 0x31, 0x35, 0x30, 0x30, 0x30, 0x0A, 0x2A, 0x53, 0x53, + 0x5F, 0x52, 0x41, 0x57, + 0x5F, 0x44, 0x41, 0x54, 0x41, 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, + 0x47, 0x41, 0x50, 0x2C, + 0x31, 0x2C, 0x31, 0x0A, 0x33, 0x30, 0x30, 0x0A, 0x2A, 0x53, 0x53, 0x5F, + 0x54, 0x4F, 0x55, 0x43, + 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x49, 0x58, 0x31, + 0x5F, 0x46, 0x4F, 0x52, + 0x43, 0x45, 0x5F, 0x4D, 0x49, 0x4E, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, + 0x2C, 0x32, 0x0A, 0x30, + 0x2C, 0x36, 0x33, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, + 0x48, 0x5F, 0x41, 0x43, + 0x54, 0x49, 0x56, 0x45, 0x5F, 0x49, 0x58, 0x32, 0x5F, 0x46, 0x4F, 0x52, + 0x43, 0x45, 0x5F, 0x4D, + 0x49, 0x4E, 0x2C, 0x32, 0x32, 0x2C, 0x31, 0x0A, 0x30, 0x0A, 0x30, 0x0A, + 0x30, 0x0A, 0x30, 0x0A, + 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, + 0x30, 0x0A, 0x30, 0x0A, + 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, + 0x30, 0x0A, 0x30, 0x0A, + 0x30, 0x0A, 0x30, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, + 0x48, 0x5F, 0x41, 0x43, + 0x54, 0x49, 0x56, 0x45, 0x5F, 0x49, 0x58, 0x32, 0x5F, 0x46, 0x4F, 0x52, + 0x43, 0x45, 0x5F, 0x4D, + 0x41, 0x58, 0x2C, 0x32, 0x32, 0x2C, 0x31, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, + 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, + 0x54, 0x49, 0x56, 0x45, + 0x5F, 0x49, 0x58, 0x32, 0x5F, 0x41, 0x44, 0x4A, 0x5F, 0x56, 0x45, 0x52, + 0x54, 0x49, 0x43, 0x41, + 0x4C, 0x2C, 0x32, 0x31, 0x2C, 0x31, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, + 0x35, 0x35, 0x0A, 0x32, + 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, + 0x35, 0x35, 0x0A, 0x32, + 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, + 0x35, 0x35, 0x0A, 0x32, + 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, + 0x35, 0x35, 0x0A, 0x32, + 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, + 0x35, 0x35, 0x0A, 0x32, + 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x2A, + 0x53, 0x53, 0x5F, 0x54, + 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, + 0x54, 0x4F, 0x54, 0x41, + 0x4C, 0x5F, 0x49, 0x58, 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, 0x4D, + 0x49, 0x4E, 0x2C, 0x32, + 0x32, 0x2C, 0x31, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, + 0x30, 0x0A, 0x30, 0x0A, + 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, + 0x30, 0x0A, 0x30, 0x0A, + 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, + 0x30, 0x0A, 0x30, 0x0A, + 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, + 0x54, 0x49, 0x56, 0x45, + 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x49, 0x58, 0x5F, 0x46, 0x4F, + 0x52, 0x43, 0x45, 0x5F, + 0x4D, 0x41, 0x58, 0x2C, 0x32, 0x32, 0x2C, 0x31, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, + 0x43, 0x54, 0x49, 0x56, + 0x45, 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x49, 0x58, 0x5F, 0x41, + 0x44, 0x4A, 0x5F, 0x56, + 0x45, 0x52, 0x54, 0x49, 0x43, 0x41, 0x4C, 0x2C, 0x32, 0x31, 0x2C, 0x31, + 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, + 0x43, 0x54, 0x49, 0x56, + 0x45, 0x5F, 0x43, 0x58, 0x31, 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, + 0x4D, 0x49, 0x4E, 0x5F, + 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x0A, 0x30, 0x2C, 0x36, 0x33, + 0x0A, 0x2A, 0x53, 0x53, + 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, + 0x45, 0x5F, 0x43, 0x58, + 0x32, 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, 0x4D, 0x49, 0x4E, 0x2C, + 0x32, 0x32, 0x2C, 0x31, + 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, + 0x0A, 0x30, 0x0A, 0x30, + 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, + 0x0A, 0x30, 0x0A, 0x30, + 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, + 0x0A, 0x2A, 0x53, 0x53, + 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, + 0x45, 0x5F, 0x43, 0x58, + 0x32, 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, 0x4D, 0x41, 0x58, 0x2C, + 0x32, 0x32, 0x2C, 0x31, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, + 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x2A, 0x53, 0x53, + 0x5F, 0x54, 0x4F, 0x55, + 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x43, 0x58, + 0x32, 0x5F, 0x41, 0x44, + 0x4A, 0x5F, 0x56, 0x45, 0x52, 0x54, 0x49, 0x43, 0x41, 0x4C, 0x2C, 0x32, + 0x31, 0x2C, 0x31, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, + 0x32, 0x35, 0x35, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, + 0x48, 0x5F, 0x41, 0x43, + 0x54, 0x49, 0x56, 0x45, 0x5F, 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x43, + 0x58, 0x5F, 0x46, 0x4F, + 0x52, 0x43, 0x45, 0x5F, 0x4D, 0x49, 0x4E, 0x2C, 0x32, 0x32, 0x2C, 0x31, + 0x0A, 0x30, 0x0A, 0x30, + 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, + 0x0A, 0x30, 0x0A, 0x30, + 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, + 0x0A, 0x30, 0x0A, 0x30, + 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x30, 0x0A, 0x2A, 0x53, 0x53, + 0x5F, 0x54, 0x4F, 0x55, + 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x54, 0x4F, + 0x54, 0x41, 0x4C, 0x5F, + 0x43, 0x58, 0x5F, 0x46, 0x4F, 0x52, 0x43, 0x45, 0x5F, 0x4D, 0x41, 0x58, + 0x2C, 0x32, 0x32, 0x2C, + 0x31, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x2A, 0x53, + 0x53, 0x5F, 0x54, 0x4F, + 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x54, + 0x4F, 0x54, 0x41, 0x4C, + 0x5F, 0x43, 0x58, 0x5F, 0x41, 0x44, 0x4A, 0x5F, 0x56, 0x45, 0x52, 0x54, + 0x49, 0x43, 0x41, 0x4C, + 0x2C, 0x32, 0x31, 0x2C, 0x31, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, + 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x32, 0x35, 0x35, 0x0A, 0x2A, 0x53, + 0x53, 0x5F, 0x52, 0x41, + 0x57, 0x5F, 0x44, 0x41, 0x54, 0x41, 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, + 0x5F, 0x4D, 0x49, 0x4E, + 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x0A, 0x31, 0x30, 0x30, + 0x30, 0x2C, 0x31, 0x35, + 0x30, 0x30, 0x30, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x52, 0x41, 0x57, 0x5F, + 0x44, 0x41, 0x54, 0x41, + 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x47, 0x41, 0x50, 0x2C, 0x31, + 0x2C, 0x31, 0x0A, 0x33, + 0x30, 0x30, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, + 0x5F, 0x41, 0x43, 0x54, + 0x49, 0x56, 0x45, 0x5F, 0x49, 0x58, 0x31, 0x5F, 0x53, 0x45, 0x4E, 0x53, + 0x45, 0x5F, 0x4D, 0x49, + 0x4E, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x0A, 0x30, 0x2C, + 0x36, 0x33, 0x0A, 0x2A, + 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, + 0x49, 0x56, 0x45, 0x5F, + 0x49, 0x58, 0x32, 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x4D, 0x49, + 0x4E, 0x2C, 0x31, 0x2C, + 0x32, 0x37, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x2A, 0x53, 0x53, + 0x5F, 0x54, 0x4F, 0x55, + 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x49, 0x58, + 0x32, 0x5F, 0x53, 0x45, + 0x4E, 0x53, 0x45, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x37, + 0x0A, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x0A, 0x2A, 0x53, 0x53, + 0x5F, 0x54, 0x4F, 0x55, + 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x49, 0x58, + 0x32, 0x5F, 0x41, 0x44, + 0x4A, 0x5F, 0x48, 0x4F, 0x52, 0x49, 0x5A, 0x4F, 0x4E, 0x54, 0x41, 0x4C, + 0x2C, 0x31, 0x2C, 0x32, + 0x36, 0x0A, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x0A, 0x2A, 0x53, + 0x53, 0x5F, 0x54, 0x4F, + 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x54, + 0x4F, 0x54, 0x41, 0x4C, + 0x5F, 0x49, 0x58, 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x4D, 0x49, + 0x4E, 0x2C, 0x31, 0x2C, + 0x32, 0x37, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, + 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x2A, 0x53, 0x53, + 0x5F, 0x54, 0x4F, 0x55, + 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x54, 0x4F, + 0x54, 0x41, 0x4C, 0x5F, + 0x49, 0x58, 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x4D, 0x41, 0x58, + 0x2C, 0x31, 0x2C, 0x32, + 0x37, 0x0A, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x0A, 0x2A, 0x53, + 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, + 0x56, 0x45, 0x5F, 0x54, + 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x49, 0x58, 0x5F, 0x41, 0x44, 0x4A, 0x5F, + 0x48, 0x4F, 0x52, 0x49, + 0x5A, 0x4F, 0x4E, 0x54, 0x41, 0x4C, 0x2C, 0x31, 0x2C, 0x32, 0x36, 0x0A, + 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x2C, + 0x32, 0x35, 0x35, 0x0A, 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, + 0x48, 0x5F, 0x41, 0x43, + 0x54, 0x49, 0x56, 0x45, 0x5F, 0x43, 0x58, 0x31, 0x5F, 0x53, 0x45, 0x4E, + 0x53, 0x45, 0x5F, 0x4D, + 0x49, 0x4E, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, 0x0A, 0x30, + 0x2C, 0x36, 0x33, 0x0A, + 0x2A, 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, + 0x54, 0x49, 0x56, 0x45, + 0x5F, 0x43, 0x58, 0x32, 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x4D, + 0x49, 0x4E, 0x2C, 0x31, + 0x2C, 0x32, 0x37, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x2A, 0x53, + 0x53, 0x5F, 0x54, 0x4F, + 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x43, + 0x58, 0x32, 0x5F, 0x53, + 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x4D, 0x41, 0x58, 0x2C, 0x31, 0x2C, 0x32, + 0x37, 0x0A, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, + 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x0A, 0x2A, 0x53, + 0x53, 0x5F, 0x54, 0x4F, + 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x43, + 0x58, 0x32, 0x5F, 0x41, + 0x44, 0x4A, 0x5F, 0x48, 0x4F, 0x52, 0x49, 0x5A, 0x4F, 0x4E, 0x54, 0x41, + 0x4C, 0x2C, 0x31, 0x2C, + 0x32, 0x36, 0x0A, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x0A, 0x2A, + 0x53, 0x53, 0x5F, 0x54, + 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, + 0x54, 0x4F, 0x54, 0x41, + 0x4C, 0x5F, 0x43, 0x58, 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x4D, + 0x49, 0x4E, 0x2C, 0x31, + 0x2C, 0x32, 0x37, 0x0A, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, + 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x2C, 0x30, 0x0A, 0x2A, 0x53, + 0x53, 0x5F, 0x54, 0x4F, + 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x5F, 0x54, + 0x4F, 0x54, 0x41, 0x4C, + 0x5F, 0x43, 0x58, 0x5F, 0x53, 0x45, 0x4E, 0x53, 0x45, 0x5F, 0x4D, 0x41, + 0x58, 0x2C, 0x31, 0x2C, + 0x32, 0x37, 0x0A, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, + 0x35, 0x35, 0x0A, 0x2A, + 0x53, 0x53, 0x5F, 0x54, 0x4F, 0x55, 0x43, 0x48, 0x5F, 0x41, 0x43, 0x54, + 0x49, 0x56, 0x45, 0x5F, + 0x54, 0x4F, 0x54, 0x41, 0x4C, 0x5F, 0x43, 0x58, 0x5F, 0x41, 0x44, 0x4A, + 0x5F, 0x48, 0x4F, 0x52, + 0x49, 0x5A, 0x4F, 0x4E, 0x54, 0x41, 0x4C, 0x2C, 0x31, 0x2C, 0x32, 0x36, + 0x0A, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, + 0x2C, 0x32, 0x35, 0x35, 0x0A, +}; + +#endif diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_mmi.h b/drivers/input/touchscreen/stmicro_mmi/fts_mmi.h new file mode 100644 index 000000000000..5b08345cc366 --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_mmi.h @@ -0,0 +1,18 @@ +#ifndef _FTS_MMI_H_ +#define _FTS_MMI_H_ + +#include +#include +#include + +struct fts_ts_info; + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +int fts_mmi_init(struct fts_ts_info *info, bool enable); +#else +static int inline fts_mmi_init(struct fts_ts_info *info, bool enable) +{ + return -ENOSYS; +} +#endif +#endif diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_mmi_class.c b/drivers/input/touchscreen/stmicro_mmi/fts_mmi_class.c new file mode 100644 index 000000000000..e3c59a59d7fb --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_mmi_class.c @@ -0,0 +1,933 @@ +/* +* Copyright (C) 2020 Motorola Mobility LLC + * + * 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. + */ +#define pr_fmt(fmt) "[ FTS-MMI ] %s: " fmt, __func__ + +#include "fts.h" +#include "fts_mmi.h" +#include "fts_lib/ftsCore.h" +#include "fts_lib/ftsIO.h" +#include "fts_lib/ftsTool.h" +#include "fts_lib/ftsError.h" +#include "fts_lib/ftsSoftware.h" +#include "fts_lib/ftsGesture.h" +#include +#include +#include + +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW +int flash_mode; /* global variable */ +#endif + +extern void fts_resume_func(struct fts_ts_info *info); +extern void fts_suspend_func(struct fts_ts_info *info); +extern int fts_fw_update(struct fts_ts_info *info); + +#define ASSERT_PTR(p) if (p == NULL) { \ + dev_err(dev, "Failed to get driver data"); \ + return -ENODEV; \ + } + +static ssize_t fts_mmi_calibrate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t fts_mmi_interpolation_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t fts_mmi_interpolation_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t fts_mmi_linearity_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t fts_mmi_linearity_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t fts_mmi_first_filter_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t fts_mmi_first_filter_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t fts_mmi_jitter_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t fts_mmi_jitter_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t fts_mmi_edge_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t fts_mmi_edge_show(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t fts_mmi_sensitivity_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size); +static ssize_t fts_mmi_sensitivity_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static DEVICE_ATTR(calibrate, (S_IWUSR | S_IWGRP), NULL, fts_mmi_calibrate_store); +static DEVICE_ATTR(interpolation, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_mmi_interpolation_show, fts_mmi_interpolation_store); +static DEVICE_ATTR(linearity, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_mmi_linearity_show, fts_mmi_linearity_store); +static DEVICE_ATTR(first_filter, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_mmi_first_filter_show, fts_mmi_first_filter_store); +static DEVICE_ATTR(jitter, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_mmi_jitter_show, fts_mmi_jitter_store); +static DEVICE_ATTR(edge, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_mmi_edge_show, fts_mmi_edge_store); +static DEVICE_ATTR(sensitivity, (S_IRUGO | S_IWUSR | S_IWGRP), + fts_mmi_sensitivity_show, fts_mmi_sensitivity_store); + +#define MAX_ATTRS_ENTRIES 10 +/* hal settings */ +#define ROTATE_0 0 +#define ROTATE_90 1 +#define ROTATE_180 2 +#define ROTATE_270 3 +#define BIG_MODE 1 +#define SMALL_MODE 2 +#define DEFAULT_MODE 0 + +/* firmware settings */ +#define ROTATE_DEFAULT_0 0 +#define ROTATE_RIGHT_90 1 +#define ROTATE_LEFT_90 2 +#define DEFAULT_EDGE 0 +#define SMALL_EDGE 1 +#define BIG_EDGE 2 + +#define ADD_ATTR(name) { \ + if (idx < MAX_ATTRS_ENTRIES) { \ + dev_info(dev, "%s: [%d] adding %p\n", __func__, idx, &dev_attr_##name.attr); \ + ext_attributes[idx] = &dev_attr_##name.attr; \ + idx++; \ + } else { \ + dev_err(dev, "%s: cannot add attribute '%s'\n", __func__, #name); \ + } \ +} + +static struct attribute *ext_attributes[MAX_ATTRS_ENTRIES]; +static struct attribute_group ext_attr_group = { + .attrs = ext_attributes, +}; + + +static int fts_mmi_extend_attribute_group(struct device *dev, struct attribute_group **group) +{ + struct fts_ts_info *ts = dev_get_drvdata(dev); + int idx = 0; + + ASSERT_PTR(ts); + if (ts->board->interpolation_ctrl) + ADD_ATTR(interpolation); + + if (ts->board->jitter_ctrl) + ADD_ATTR(jitter); + + if (ts->board->linearity_ctrl) + ADD_ATTR(linearity); + + if (ts->board->first_filter_ctrl) + ADD_ATTR(first_filter); + + if (ts->board->edge_ctrl) + ADD_ATTR(edge); + + if (ts->board->sensitivity_ctrl) + ADD_ATTR(sensitivity); + + if (strncmp(bi_bootmode(), "mot-factory", strlen("mot-factory")) == 0) { + ADD_ATTR(calibrate); + } + + if (idx) { + ext_attributes[idx] = NULL; + *group = &ext_attr_group; + } else + *group = NULL; + + return 0; +} +/* + * To enable/disable interpolation algorithm to modify report rate. + * interpolation_cmd[2] == 0x00, Disable interpolation algorithm + * interpolation_cmd[2] == 0x01, Enable interpolation algorithm + * report_rate_cmd[2] == 0x00, Set report rate 240Hz + * report_rate_cmd[2] == 0x01, Set report rate 288Hz + */ +#define DSI_REFRESH_RATE_144 144 +static void fts_mmi_set_report_rate(struct fts_ts_info *ts) +{ + struct fts_hw_platform_data *board; + int ret = 0; + + board = ts->board; + + if (board->interpolation_cmd[2] == 0x01 && ts->refresh_rate == DSI_REFRESH_RATE_144) + board->report_rate_cmd[2] = 0x01; + else + board->report_rate_cmd[2] = 0x00; + + if (ts->report_rate == board->report_rate_cmd[2]) { + pr_debug("%s: report rate value is same, so not write.\n", __func__); + } else { + ret = fts_write(board->report_rate_cmd, ARRAY_SIZE(board->report_rate_cmd)); + if (ret == OK) { + ts->report_rate = board->report_rate_cmd[2]; + pr_err("Successfully to write refresh rate %dHz!\n", ts->refresh_rate); + } else + pr_err("Failed to write refresh rate %dHz!\n", ts->refresh_rate); + } + + if (ts->interpolation_val == board->interpolation_cmd[2]) { + pr_debug("%s: interpolation value is same, so not write.\n", __func__); + } else { + ret = fts_write(board->interpolation_cmd, ARRAY_SIZE(board->interpolation_cmd)); + if (ret == OK) { + ts->interpolation_val = board->interpolation_cmd[2]; + pr_info("Successfully to set inpolation = %d!\n", ts->interpolation_val); + } else + pr_err("Failed to set inpolation = %d!\n", ts->interpolation_val); + } +} + +static ssize_t fts_mmi_interpolation_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct fts_ts_info *ts; + struct fts_hw_platform_data *board; + int ret = 0; + unsigned long mode = 0; + + dev = MMI_DEV_TO_TS_DEV(dev); + ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + board = ts->board; + + ret = kstrtoul(buf, 0, &mode); + if (ret < 0) { + pr_info("Failed to convert value.\n"); + return -EINVAL; + } + + board->interpolation_cmd[2] = mode; + fts_mmi_set_report_rate(ts); + + return size; +} + +static ssize_t fts_mmi_interpolation_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_info *ts; + + dev = MMI_DEV_TO_TS_DEV(dev); + ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + + pr_info("Interpolation mode = %d.\n", ts->interpolation_val); + return scnprintf(buf, PAGE_SIZE, "0x%02x", ts->interpolation_val); +} + +static ssize_t fts_mmi_jitter_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct fts_ts_info *ts; + struct fts_hw_platform_data *board; + int ret = 0; + unsigned int args[3] = { 0 }; + + dev = MMI_DEV_TO_TS_DEV(dev); + ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + board = ts->board; + + ret = sscanf(buf, "0x%x 0x%x 0x%x", &args[0], &args[1], &args[2]); + if (ret < 3) + return -EINVAL; + + /* tap x jitter */ + board->jitter_cmd[2] = (args[0] >> 8) & 0xff; + /* tap y jitter */ + board->jitter_cmd[3] = args[0] & 0xff; + /* slow slidding x jitter */ + board->jitter_cmd[4] = (args[1] >> 8) & 0xff; + /* slow slidding y jitter */ + board->jitter_cmd[5] = args[1] & 0xff; + /* fast slidding y jitter */ + board->jitter_cmd[6] = (args[2] >> 8) & 0xff; + /* fsst slidding y jitter */ + board->jitter_cmd[7] = args[2] & 0xff; + + if (!memcmp(board->jitter_cmd, ts->jitter_val, sizeof(ts->jitter_val))) { + pr_debug("value is same,so not write.\n"); + return size; + } + + memcpy(ts->jitter_val, board->jitter_cmd, sizeof(ts->jitter_val)); + ret = fts_write(board->jitter_cmd, ARRAY_SIZE(board->jitter_cmd)); + if (ret == OK) { + pr_info("Successfully to set jitter mode:\n"); + pr_info("tap: %02x %02x, slow: %02x %02x, fast: %02x %02x!\n", + ts->jitter_val[2], ts->jitter_val[3], ts->jitter_val[4], + ts->jitter_val[5], ts->jitter_val[6], ts->jitter_val[7]); + } else { + pr_info("Failed to set jitter mode:\n"); + pr_info("tap: %02x %02x, slow: %02x %02x, fast: %02x %02x!\n", + ts->jitter_val[2], ts->jitter_val[3], ts->jitter_val[4], + ts->jitter_val[5], ts->jitter_val[6], ts->jitter_val[7]); + } + + return size; +} + +static ssize_t fts_mmi_jitter_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_info *ts; + + dev = MMI_DEV_TO_TS_DEV(dev); + ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + + return scnprintf(buf, PAGE_SIZE, "0x%04x 0x%04x 0x%04x", + (ts->jitter_val[2] << 8 | ts->jitter_val[3]), + (ts->jitter_val[4] << 8 | ts->jitter_val[5]), + (ts->jitter_val[6] << 8 | ts->jitter_val[7])); +} + +static ssize_t fts_mmi_sensitivity_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct fts_ts_info *ts; + struct fts_hw_platform_data *board; + int ret = 0; + unsigned long mode = 0; + + dev = MMI_DEV_TO_TS_DEV(dev); + ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + board = ts->board; + + ret = kstrtoul(buf, 0, &mode); + if (ret < 0) { + pr_info("Failed to convert value.\n"); + return -EINVAL; + } + + if (mode != 0x00 && mode != 0x01) { + pr_info("The mode = %02x does not support\n", mode); + return size; + } + + if (ts->sensitivity_val == mode) { + pr_debug("value is same, so not write.\n"); + return size; + } + + board->sensitivity_cmd[2] = mode; + + ret = fts_write(board->sensitivity_cmd, ARRAY_SIZE(board->sensitivity_cmd)); + if (ret == OK) { + pr_info("Successfully to %s sensitivity mode!\n", (!!mode ? "Enable" : "Disable")); + ts->sensitivity_val = mode; + } else + pr_err("Failed to %s sensitivity mode!\n", (!!mode ? "Enable" : "Disable")); + + return size; +} + +static ssize_t fts_mmi_sensitivity_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_info *ts; + + dev = MMI_DEV_TO_TS_DEV(dev); + ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + + pr_info("Sensitivity mode is %s!\n", (!!ts->sensitivity_val ? "Enable" : "Disable")); + return scnprintf(buf, PAGE_SIZE, "0x%02x", ts->sensitivity_val); +}; + + +static ssize_t fts_mmi_linearity_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct fts_ts_info *ts; + struct fts_hw_platform_data *board; + int ret = 0; + unsigned long mode = 0; + + dev = MMI_DEV_TO_TS_DEV(dev); + ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + board = ts->board; + + ret = kstrtoul(buf, 16, &mode); + if (ret < 0) { + pr_info("Failed to convert value.\n"); + return -EINVAL; + } + + board->linearity_cmd[2] = mode; + + if (!memcmp(board->linearity_cmd, ts->linearity_val, sizeof(ts->linearity_val))) { + pr_debug("value is same, so not write.\n"); + return size; + } + + memcpy(ts->linearity_val, board->linearity_cmd, sizeof(ts->linearity_val)); + ret = fts_write(board->linearity_cmd, ARRAY_SIZE(board->linearity_cmd)); + if (ret == OK) + pr_info("Successfully to %s linearity mode!\n", (!!mode ? "Enable" : "Disable")); + else + pr_err("Failed to %s linearity mode!\n", (!!mode ? "Enable" : "Disable")); + + return size; +} + +static ssize_t fts_mmi_linearity_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_info *ts; + + dev = MMI_DEV_TO_TS_DEV(dev); + ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + + pr_info("Linearity mode is %s!\n", (!!ts->linearity_val[2] ? "Enable" : "Disable")); + return scnprintf(buf, PAGE_SIZE, "0x%02x", ts->linearity_val[2]); +}; + +static ssize_t fts_mmi_first_filter_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct fts_ts_info *ts; + struct fts_hw_platform_data *board; + int ret = 0; + unsigned long value = 0; + + dev = MMI_DEV_TO_TS_DEV(dev); + ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + board = ts->board; + + ret = kstrtoul(buf, 0, &value); + if (ret < 0) { + pr_info("Failed to convert value.\n"); + return -EINVAL; + } + + if (value == ts->first_filter_val) { + pr_debug("First_filter is already %s, so not write.\n", + value ? "Enabled" : "Disabled"); + return size; + } + + ts->first_filter_val = value ? 1 : 0; + + ret = fts_write(&board->first_filter_cmd[ts->first_filter_val*4], 4); + + if (ret == OK) + pr_info("Successfully to %s first_filter mode!\n", + value ? "Enabled" : "Disabled"); + else + pr_info("Failed to %s first_filter mode!\n", + value ? "Enabled" : "Disabled"); + + return size; +} + +static ssize_t fts_mmi_first_filter_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_info *ts; + + dev = MMI_DEV_TO_TS_DEV(dev); + ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + + pr_info("finger filter mode = %d.\n", ts->first_filter_val); + return scnprintf(buf, PAGE_SIZE, "0x%02x", ts->first_filter_val); +} + +static ssize_t fts_mmi_edge_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct fts_ts_info *ts; + struct fts_hw_platform_data *board; + int ret = 0; + unsigned int args[2] = { 0 }; + + dev = MMI_DEV_TO_TS_DEV(dev); + ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + board = ts->board; + + ret = sscanf(buf, "%d %d", &args[0], &args[1]); + if (ret < 2) + return -EINVAL; + + if (ROTATE_0 == args[1]) { + board->edge_cmd[2] = ROTATE_DEFAULT_0; + } else if (ROTATE_90 == args[1]) { + board->edge_cmd[2] = ROTATE_RIGHT_90; + } else if (ROTATE_270 == args[1]) { + board->edge_cmd[2] = ROTATE_LEFT_90; + } else { + pr_err("Invalid rotation mode: %d!\n", args[1]); + } + + if(BIG_MODE == args[0]) { + board->edge_cmd[3] = BIG_EDGE; + } else if(SMALL_MODE == args[0]) { + board->edge_cmd[3] = SMALL_EDGE; + } else if(DEFAULT_MODE == args[0]) { + board->edge_cmd[3] = DEFAULT_EDGE; + } else { + pr_err("Invalid edge mode: %d!\n", args[0]); + } + + if (!memcmp(board->edge_cmd, ts->edge_val, sizeof(ts->edge_val))) { + pr_debug("value is same,so not write.\n"); + return size; + } + + memcpy(ts->edge_val, board->edge_cmd, sizeof(ts->edge_val)); + ret = fts_write(board->edge_cmd, ARRAY_SIZE(board->edge_cmd)); + if (ret == OK) + pr_info("Successfully to set rotation mode: %02x, edge mode: %02x!\n", + ts->edge_val[2], ts->edge_val[3]); + else + pr_info("Successfully to set rotation mode: %02x, edge mode: %02x!\n", + ts->edge_val[2], ts->edge_val[3]); + + return size; +} + +static ssize_t fts_mmi_edge_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fts_ts_info *ts; + + dev = MMI_DEV_TO_TS_DEV(dev); + ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + + pr_info("rotation mode = %02x, edge mode = %02x\n", + ts->board->edge_cmd[2], ts->board->edge_cmd[3]); + return scnprintf(buf, PAGE_SIZE, "0x%02x 0x%02x", + ts->board->edge_cmd[2], ts->board->edge_cmd[3]); +} + +static ssize_t fts_mmi_calibrate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int ret; + unsigned long value = 0; + + ret = kstrtoul(buf, 0, &value); + if (ret < 0) { + pr_info("Failed to convert value.\n"); + return -EINVAL; + } + + /* disable irq to fully control IC*/ + fts_disableInterrupt(); + + if (value == 3) { + u8 cmd[3] = { FTS_CMD_SYSTEM, SYS_CMD_SPECIAL, SPECIAL_FULL_PANEL_INIT }; + pr_info("Sending Event [0x%02x 0x%02x 0x%02x]...\n", + cmd[0], cmd[1], cmd[2]); + + ret = fts_writeFwCmd(cmd, 3); + if (ret != OK) { + pr_info("TP Calibration Failed!\n"); + return -EINVAL; + } + pr_info("TP Calibration Success!\n"); + } else { + pr_info("The value is invalid\n"); + return -EINVAL; + } + + fts_enableInterrupt(); + return size; +} + +static int fts_mmi_get_class_entry_name(struct device *dev, void *cdata) { + struct fts_ts_info *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "primary"); +} + +static int fts_mmi_get_vendor(struct device *dev, void *cdata) { + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "stmicro"); +} + +static int fts_mmi_get_productinfo(struct device *dev, void *cdata) { + struct fts_ts_info *ts = dev_get_drvdata(dev); + u8 readData[3]; + char buffer[64]; + ssize_t blen = 0; + ASSERT_PTR(ts); + fts_writeReadU8UX(FTS_CMD_HW_REG_R, ADDR_SIZE_HW_REG, + ADDR_DCHIP_ID, readData, 2, DUMMY_FIFO); + blen += scnprintf(buffer+blen, sizeof(buffer)-blen, "fts%02x%02x", + readData[0], readData[1]); + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_INFO_LEN, "%s", buffer); +} + +static int fts_mmi_get_build_id(struct device *dev, void *cdata) { + struct fts_ts_info *ts = dev_get_drvdata(dev); + char buffer[64]; + ssize_t blen = 0; + ASSERT_PTR(ts); + blen += scnprintf(buffer+blen, sizeof(buffer)-blen, "%04x%04x", + ts->sysinfo->u16_fwVer, ts->sysinfo->u16_cfgVer); + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%s", buffer); +} + +static int fts_mmi_get_config_id(struct device *dev, void *cdata) { + struct fts_ts_info *ts = dev_get_drvdata(dev); + char buffer[64]; + ssize_t blen = 0; + ASSERT_PTR(ts); + blen += scnprintf(buffer+blen, sizeof(buffer)-blen, "%02x%02x%02x%02x", + ts->sysinfo->u8_releaseInfo[3], ts->sysinfo->u8_releaseInfo[2], + ts->sysinfo->u8_releaseInfo[1], ts->sysinfo->u8_releaseInfo[0]); + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%s", buffer); +} + +static int fts_mmi_get_bus_type(struct device *dev, void *idata) { + TO_INT(idata) = TOUCHSCREEN_MMI_BUS_TYPE_I2C; + return 0; +} + +static int fts_mmi_get_irq_status(struct device *dev, void *idata) { + struct fts_ts_info *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + TO_INT(idata) = gpio_get_value(ts->board->irq_gpio); + return 0; +} + +static int fts_mmi_get_drv_irq(struct device *dev, void *idata) { + struct fts_ts_info *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + TO_INT(idata) = fts_is_InterruptEnabled(); + return 0; +} + +static int fts_mmi_get_poweron(struct device *dev, void *idata) +{ + struct fts_ts_info *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + TO_INT(idata) = (regulator_is_enabled(ts->avdd_reg) && + regulator_is_enabled(ts->vdd_reg)) ? 1 : 0; + return 0; +} + +static int fts_mmi_get_flashprog(struct device *dev, void *idata) +{ + struct fts_ts_info *ts = dev_get_drvdata(dev); + + ASSERT_PTR(ts); + //TO_INT(idata) = ts->in_bootloader ? 1 : 0; + + return 0; +} + +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW +static int fts_mmi_get_flash_mode(struct device *dev, void *idata) +{ + struct fts_ts_info *ts = dev_get_drvdata(dev); + + ASSERT_PTR(ts); + TO_INT(idata) = flash_mode; + + return 0; +} + +static int fts_mmi_flash_mode(struct device *dev, int mode) +{ + struct fts_ts_info *ts = dev_get_drvdata(dev); + + ASSERT_PTR(ts); + + flash_mode = mode; + + return 0; +} +#endif + +static int fts_mmi_drv_irq(struct device *dev, int state) +{ + struct fts_ts_info *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + pr_info("enter %d\n", state); + dev_dbg(dev, "%s\n", __func__); + switch (state) { + case 0: /* Disable irq */ + fts_disableInterrupt(); + break; + case 1: /* Enable irq */ + fts_enableInterrupt(); + break; + default: + dev_err(dev, "%s: invalid value\n", __func__); + return -EINVAL; + } + pr_info("IRQ is %s\n", fts_is_InterruptEnabled() ? "EN" : "DIS"); + return 0; +} + +static int fts_mmi_reset(struct device *dev, int type) +{ + struct fts_ts_info *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + pr_info("enter\n"); + dev_dbg(dev, "%s\n", __func__); + return fts_system_reset(); +} + +static int fts_mmi_panel_state(struct device *dev, + enum ts_mmi_pm_mode from, enum ts_mmi_pm_mode to) +{ + u8 mask[4] = { 0 }; + struct fts_ts_info *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + dev_dbg(dev, "%s: panel state change: %d->%d\n", __func__, from, to); + pr_info("panel state change: %d->%d\n", from, to); + switch (to) { + case TS_MMI_PM_GESTURE: + /* support single tap gesture */ + fromIDtoMask(GEST_ID_SIGTAP, mask, GESTURE_MASK_SIZE); + updateGestureMask(mask, GESTURE_MASK_SIZE, 1); + ts->gesture_enabled = 1; + case TS_MMI_PM_DEEPSLEEP: + fts_suspend_func(ts); + break; + case TS_MMI_PM_ACTIVE: + ts->gesture_enabled = 0; + break; + default: + dev_warn(dev, "invalid panel state %d\n", to); + return -EINVAL; + } + pr_info("IRQ is %s\n", fts_is_InterruptEnabled() ? "EN" : "DIS"); + return 0; +} + +static int fts_mmi_power(struct device *dev, int on) +{ + struct fts_ts_info *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + pr_info("enter %d\n", on); + dev_dbg(dev, "%s\n", __func__); + fts_chip_power_switch(ts, on == 1); + pr_info("IRQ is %s\n", fts_is_InterruptEnabled() ? "EN" : "DIS"); + return 0; +} + +static int fts_mmi_pinctrl(struct device *dev, int on) +{ + struct fts_ts_info *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + pr_info("enter %d\n", on); + dev_dbg(dev, "%s\n", __func__); + fts_pinctrl_state(ts, on == 1); + pr_info("IRQ is %s\n", fts_is_InterruptEnabled() ? "EN" : "DIS"); + return 0; +} + +static int fts_mmi_post_resume(struct device *dev) +{ + struct fts_ts_info *ts = dev_get_drvdata(dev); + struct fts_hw_platform_data *board; + int ret = 0; + + ASSERT_PTR(ts); + board = ts->board; + pr_info("enter\n"); + dev_dbg(dev, "%s\n", __func__); + fts_resume_func(ts); + pr_info("IRQ is %s\n", fts_is_InterruptEnabled() ? "EN" : "DIS"); + + /* restore data */ + mdelay(200); + if (ts->board->jitter_ctrl) { + ret = fts_write(board->jitter_cmd, ARRAY_SIZE(board->jitter_cmd)); + if (ret == OK) + dev_dbg(dev, "%s: Successfully to restore jitter mode\n", __func__); + } + + if (ts->board->linearity_ctrl) { + ret = fts_write(board->linearity_cmd, ARRAY_SIZE(board->linearity_cmd)); + if (ret == OK) + dev_dbg(dev, "%s: Successfully to restore linearity mode!\n", __func__); + } + + if (ts->board->first_filter_ctrl) { + ret = fts_write(&board->first_filter_cmd[ts->first_filter_val*4], 4); + if (ret == OK) + dev_dbg(dev, "%s: Successfully to restore first_filter mode!\n", __func__); + } + + if (ts->board->report_rate_ctrl) { + ret = fts_write(board->report_rate_cmd, ARRAY_SIZE(board->report_rate_cmd)); + if (ret == OK) + dev_dbg(dev, "%s: Successfully to restore report rate mode!\n", __func__); + } + + if (ts->board->edge_ctrl) { + ret = fts_write(board->edge_cmd, ARRAY_SIZE(board->edge_cmd)); + if (ret == OK) + dev_dbg(dev, "%s: Successfully to restore edge mode!\n", __func__); + } + + if (ts->board->sensitivity_ctrl) { + ret = fts_write(board->sensitivity_cmd, ARRAY_SIZE(board->sensitivity_cmd)); + if (ret == OK) + dev_dbg(dev, "%s: Successfully to restore edge mode!\n", __func__); + } + + return 0; +} + +static int fts_mmi_fw_update(struct device *dev, char *fwname) +{ + struct fts_ts_info *ts = dev_get_drvdata(dev); + int ret; + ASSERT_PTR(ts); + dev_dbg(dev, "%s\n", __func__); + + fts_disableInterrupt(); + fts_interrupt_uninstall(ts); + + ts->fw_file = fwname; + ts->force_reflash = true; + ret = fts_fw_update(ts); + ts->fw_file = NULL; + return ret; +} + +static int fts_mmi_charger_mode(struct device *dev, int mode) +{ + struct fts_ts_info *ts = dev_get_drvdata(dev); + int ret = OK; + u8 settings[4] = { 0 }; + ASSERT_PTR(ts); + + if (mode == ts->charger_enabled) { + logError(1, "%s %s: Charger mode is already %s.\n", + tag, __func__, !!mode ? "Enable" : "Disable"); + return ret; + } + + ts->mode = MODE_NOTHING; /* initialize the mode to nothing in + * order to be updated depending on the + * features enabled */ + ts->charger_enabled = mode; + settings[0] = ts->charger_enabled; + ret = setFeatures(FEAT_SEL_CHARGER, settings, 1); + if (ret < OK) + logError(1, "%s %s: error during setting CHARGER_MODE! ERROR %08X\n", + tag, __func__, ret); + + if (ret >= OK && ts->charger_enabled == FEAT_ENABLE) { + fromIDtoMask(FEAT_SEL_CHARGER, (u8 *)&ts->mode, sizeof(ts->mode)); + logError(1, "%s %s: CHARGER_MODE Enabled!\n", + tag, __func__); + } else + logError(1, "%s %s: CHARGER_MODE Disabled!\n", tag, __func__); + + return ret; +} + +static int fts_mmi_refresh_rate(struct device *dev, int freq) +{ + struct fts_ts_info *ts; + + ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + + ts->refresh_rate = freq; + + if (ts->board->interpolation_ctrl) + fts_mmi_set_report_rate(ts); + + return 0; +} + +static int fts_mmi_wait4ready(struct device *dev) +{ + struct fts_ts_info *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + dev_dbg(dev, "%s\n", __func__); + fts_wait_for_ready(); + return 0; +} + +static struct ts_mmi_methods fts_mmi_methods = { + .get_vendor = fts_mmi_get_vendor, + .get_productinfo = fts_mmi_get_productinfo, + .get_build_id = fts_mmi_get_build_id, + .get_config_id = fts_mmi_get_config_id, + .get_bus_type = fts_mmi_get_bus_type, + .get_irq_status = fts_mmi_get_irq_status, + .get_drv_irq = fts_mmi_get_drv_irq, + .get_poweron = fts_mmi_get_poweron, + .get_flashprog = fts_mmi_get_flashprog, +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW + .get_flash_mode = fts_mmi_get_flash_mode, +#endif + .get_class_entry_name = fts_mmi_get_class_entry_name, + /* SET methods */ + .reset = fts_mmi_reset, + .drv_irq = fts_mmi_drv_irq, + .charger_mode = fts_mmi_charger_mode, + .refresh_rate = fts_mmi_refresh_rate, + .power = fts_mmi_power, + .pinctrl = fts_mmi_pinctrl, + .wait_for_ready = fts_mmi_wait4ready, + /* Firmware */ +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW + .flash_mode = fts_mmi_flash_mode, +#endif + .firmware_update = fts_mmi_fw_update, + /* vendor specific attribute group */ + .extend_attribute_group = fts_mmi_extend_attribute_group, + /* PM callback */ + .panel_state = fts_mmi_panel_state, + .post_resume = fts_mmi_post_resume, +}; + +int fts_mmi_init(struct fts_ts_info *ts, bool enable) +{ + int ret = 0; + + if (enable) { + ret = ts_mmi_dev_register(ts->dev, &fts_mmi_methods); + if (ret) + dev_err(ts->dev, "Failed to register ts mmi\n"); + /* initialize class imported methods */ + ts->imports = &fts_mmi_methods.exports; + + dev_info(ts->dev, "MMI start sensing\n"); + fts_init_sensing(ts); + fts_enableInterrupt(); + + /* Disable interpolation algorithm */ + if (ts->board->interpolation_ctrl) + ts->interpolation_val = 0x00; + /* Disable first filter function */ + if (ts->board->first_filter_ctrl) + ts->first_filter_val = 0x00; + if (ts->board->sensitivity_ctrl) + ts->sensitivity_val = 0x00; + + } else + ts_mmi_dev_unregister(ts->dev); + return ret; +} diff --git a/drivers/input/touchscreen/stmicro_mmi/fts_proc.c b/drivers/input/touchscreen/stmicro_mmi/fts_proc.c new file mode 100644 index 000000000000..ec60e7ebe1eb --- /dev/null +++ b/drivers/input/touchscreen/stmicro_mmi/fts_proc.c @@ -0,0 +1,3103 @@ +/* + * + ************************************************************************** + ** STMicroelectronics ** + ************************************************************************** + ** marco.cali@st.com * + ************************************************************************** + * * + * Utilities published in /proc/fts * + * * + ************************************************************************** + ************************************************************************** + * + */ + +/*! + * \file fts_proc.c + * \brief contains the function and variables needed to publish a file node in + * the file system which allow to communicate with the IC from userspace + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "fts.h" +#include "fts_lib/ftsCompensation.h" +#include "fts_lib/ftsCore.h" +#include "fts_lib/ftsIO.h" +#include "fts_lib/ftsError.h" +#include "fts_lib/ftsFrame.h" +#include "fts_lib/ftsFlash.h" +#include "fts_lib/ftsTest.h" +#include "fts_lib/ftsTime.h" +#include "fts_lib/ftsTool.h" + + + + +#define DRIVER_TEST_FILE_NODE "driver_test" /* /< name of file node + * published */ +#define CHUNK_PROC 1024 /* /< Max chunk of data printed on the + * sequential file in each iteration */ +#define DIAGNOSTIC_NUM_FRAME 10 /* /< number of frames reading + * iterations during the diagnostic test + */ + + + +/** @defgroup proc_file_code Proc File Node + * @ingroup file_nodes + * The /proc/fts/driver_test file node provide expose the most important API + * implemented into the driver to execute any possible operation into the IC \n + * Thanks to a series of Operation Codes, each of them, with a different set of + * parameter, it is possible to select a function to execute\n + * The result of the function is usually returned into the shell as an ASCII + * hex string where each byte is encoded in two chars.\n + * @{ + */ + +/* Bus operations */ +#define CMD_READ 0x00 /* /< I2C/SPI read: need + * to pass: byteToRead1 + * byteToRead0 + * (optional) dummyByte + * */ +#define CMD_WRITE 0x01 /* /< I2C/SPI write: + * need to pass: cmd[0] + * cmd[1] … + * cmd[cmdLength-1] */ +#define CMD_WRITEREAD 0x02 /* /< I2C/SPI writeRead: + * need to pass: cmd[0] + * cmd[1] … + * cmd[cmdLength-1] + * byteToRead1 + * byteToRead0 dummyByte + * */ +#define CMD_WRITETHENWRITEREAD 0x03 /* /< I2C/SPI write then + * writeRead: need to + * pass: cmdSize1 + * cmdSize2 cmd1[0] + * cmd1[1] … + * cmd1[cmdSize1-1] + * cmd2[0] cmd2[1] … + * cmd2[cmdSize2-1] + * byteToRead1 + * byteToRead0 */ +#define CMD_WRITEU8UX 0x04 /* /< I2C/SPI writeU8UX: + * need to pass: cmd + * addrSize addr[0] … + * addr[addrSize-1] + * data[0] data[1] … */ +#define CMD_WRITEREADU8UX 0x05 /* /< I2C/SPI + * writeReadU8UX: need + * to pass: cmd addrSize + * addr[0] … + * addr[addrSize-1] + * byteToRead1 + * byteToRead0 + * hasDummyByte */ +#define CMD_WRITEU8UXTHENWRITEU8UX 0x06 /* /< I2C/SPI writeU8UX + * then writeU8UX: need + * to pass: cmd1 + * addrSize1 cmd2 + * addrSize2 addr[0] … + * addr[addrSize1+addrSize2-1] + * data[0] data[1] … */ +#define CMD_WRITEU8UXTHENWRITEREADU8UX 0x07 /* /< I2C/SPI writeU8UX + * then writeReadU8UX: + * need to pass: cmd1 + * addrSize1 cmd2 + * addrSize2 addr[0] … + * addr[addrSize1+addrSize2-1] + * byteToRead1 + * byteToRead0 + * hasDummybyte */ +#define CMD_GETLIMITSFILE 0x08 /* /< Get the Production + * Limits File and print + * its content into the + * shell: need to pass: + * path(optional) + * otherwise select the + * approach chosen at + * compile time */ +#define CMD_GETFWFILE 0x09 /* /< Get the FW file + * and print its content + * into the shell: need + * to pass: path + * (optional) otherwise + * select the approach + * chosen at compile + * time */ +#define CMD_VERSION 0x0A /* /< Get the driver + * version and other + * driver setting info + * */ +#define CMD_READCONFIG 0x0B /* /< Read The config + * memory, need to pass: + * addr[0] addr[1] + * byteToRead0 + * byteToRead1 */ + + +/* GUI utils byte ver */ +#define CMD_READ_BYTE 0xF0 /* /< Byte output + * version of I2C/SPI + * read @see CMD_READ */ +#define CMD_WRITE_BYTE 0xF1 /* /< Byte output + * version of I2C/SPI + * write @see CMD_WRITE + * */ +#define CMD_WRITEREAD_BYTE 0xF2 /* /< Byte output + * version of I2C/SPI + * writeRead @see + * CMD_WRITEREAD */ +#define CMD_WRITETHENWRITEREAD_BYTE 0xF3 /* /< Byte output + * version of I2C/SPI + * write then writeRead + * @see + * CMD_WRITETHENWRITEREAD + * */ +#define CMD_WRITEU8UX_BYTE 0xF4 /* /< Byte output + * version of I2C/SPI + * writeU8UX @see + * CMD_WRITEU8UX */ +#define CMD_WRITEREADU8UX_BYTE 0xF5 /* /< Byte output + * version of I2C/SPI + * writeReadU8UX @see + * CMD_WRITEREADU8UX */ +#define CMD_WRITEU8UXTHENWRITEU8UX_BYTE 0xF6 /* /< Byte output + * version of I2C/SPI + * writeU8UX then + * writeU8UX @see + * CMD_WRITEU8UXTHENWRITEU8UX + * */ +#define CMD_WRITEU8UXTHENWRITEREADU8UX_BYTE 0xF7 /* /< Byte output + * version of I2C/SPI + * writeU8UX then + * writeReadU8UX @see + * CMD_WRITEU8UXTHENWRITEREADU8UX + * */ +#define CMD_GETLIMITSFILE_BYTE 0xF8 /* /< Byte output + * version of Production + * Limits File @see + * CMD_GETLIMITSFILE */ +#define CMD_GETFWFILE_BYTE 0xF9 /* /< Byte output + * version of FW file + * need to pass: @see + * CMD_GETFWFILE */ +#define CMD_VERSION_BYTE 0xFA /* /< Byte output + * version of driver + * version and setting + * @see CMD_VERSION */ +#define CMD_CHANGE_OUTPUT_MODE 0xFF /* /< Select the output + * mode of the + * scriptless protocol, + * need to pass: + * bin_output = 1 data + * returned as binary, + * bin_output =0 data + * returned as hex + * string */ + +/* Core/Tools */ +#define CMD_POLLFOREVENT 0x11 /* /< Poll the FIFO for + * an event: need to + * pass: eventLength + * event[0] event[1] … + * event[eventLength-1] + * timeToWait1 + * timeToWait0 */ +#define CMD_SYSTEMRESET 0x12 /* /< System Reset */ +#define CMD_CLEANUP 0x13 /* /< Perform a system + * reset and optionally + * re-enable the + * scanning, need to + * pass: enableTouch */ +#define CMD_POWERCYCLE 0x14 /* /< Execute a power + * cycle toggling the + * regulators */ +#define CMD_READSYSINFO 0x15 /* /< Read the System + * Info information from + * the framebuffer, need + * to pass: doRequest */ +#define CMD_FWWRITE 0x16 /* /< Write a FW + * command: need to + * pass: cmd[0] cmd[1] + * … cmd[cmdLength-1] */ +#define CMD_INTERRUPT 0x17 /* /< Allow to enable or + * disable the + * interrupts, need to + * pass: enable (if 1 + * will enable the + * interrupt) */ +#define CMD_SETSCANMODE 0x18 /* /< set Scan Mode + * need to pass: + * scanType option */ +#define CMD_SAVEMPFLAG 0x19 /* /< save manually a + * value in the MP flag + * need to pass: mpflag + */ + +/* Frame */ +#define CMD_GETFORCELEN 0x20 /* /< Get the number of + * Force channels */ +#define CMD_GETSENSELEN 0x21 /* /< Get the number of + * Sense channels */ +#define CMD_GETMSFRAME 0x23 /* /< Get a MS frame: + * need to pass: + * MSFrameType */ +#define CMD_GETSSFRAME 0x24 /* /< Get a SS frame: + * need to pass: + * SSFrameType */ +#define CMD_GETSYNCFRAME 0x25 /* /< Get a Sync Frame: + * need to pass: + * frameType */ + +/* Compensation */ +#define CMD_REQCOMPDATA 0x30 /* /< Request Init data: + * need to pass: type */ +#define CMD_READCOMPDATAHEAD 0x31 /* /< Read Init data + * header: need to pass: + * type */ +#define CMD_READMSCOMPDATA 0x32 /* /< Read MS Init data: + * need to pass: type */ +#define CMD_READSSCOMPDATA 0x33 /* /< Read SS Init data: + * need to pass: type */ +#define CMD_READTOTMSCOMPDATA 0x35 /* /< Read Tot MS Init + * data: need to pass: + * type */ +#define CMD_READTOTSSCOMPDATA 0x36 /* /< Read Tot SS Init + * data: need to pass: + * type */ + +/* FW Update */ +#define CMD_GETFWVER 0x40 /* /< Get the FW version + * of the IC */ +#define CMD_FLASHUNLOCK 0x42 /* /< Unlock the flash + * */ +#define CMD_READFWFILE 0x43 /* /< Try to read the FW + * file, need to pass: + * keep_cx */ +#define CMD_FLASHPROCEDURE 0x44 /* /< Perform a full + * flashing procedure: + * need to pass: force + * keep_cx */ +#define CMD_FLASHERASEUNLOCK 0x45 /* /< Unlock the erase + * of the flash */ +#define CMD_FLASHERASEPAGE 0x46 /* /< Erase page by page + * the flash, need to + * pass: keep_cx, if + * keep_cx>SKIP_PANEL_INIT + * Panel Init Page will + * be skipped, if + * >SKIP_PANEL_CX_INIT + * Cx and Panel Init + * Pages will be skipped + * otherwise if + * =ERASE_ALL all the + * pages will be deleted + * */ + + +/* MP test */ +#define CMD_ITOTEST 0x50 /* /< Perform an ITO + * test */ +#define CMD_INITTEST 0x51 /* /< Perform an + * Initialization test: + * need to pass: type */ +#define CMD_MSRAWTEST 0x52 /* /< Perform MS raw + * test: need to pass + * stop_on_fail */ +#define CMD_MSINITDATATEST 0x53 /* /< Perform MS Init + * data test: need to + * pass stop_on_fail */ +#define CMD_SSRAWTEST 0x54 /* /< Perform SS raw + * test: need to pass + * stop_on_fail */ +#define CMD_SSINITDATATEST 0x55 /* /< Perform SS Init + * data test: need to + * pass stop_on_fail */ +#define CMD_MAINTEST 0x56 /* /< Perform a full + * Mass production test: + * need to pass + * stop_on_fail saveInit + * mpflag + * */ +#define CMD_FREELIMIT 0x57 /* /< Free (if any) + * limit file which was + * loaded during any + * test procedure */ + +/* Diagnostic */ +#define CMD_DIAGNOSTIC 0x60 /* /< Perform a series + * of commands and + * collect severals data + * to detect any + * malfunction */ + + +#define CMD_CHANGE_SAD 0x70 /* /< Allow to change + * the SAD address (for + * debugging) */ + +static u8 bin_output; /* /< Select the output type of the scriptless + * protocol (binary = 1 or hex string = 0) */ +/** @}*/ + +/** @defgroup scriptless Scriptless Protocol + * @ingroup proc_file_code + * Scriptless Protocol allows ST Software (such as FingerTip Studio etc) to + * communicate with the IC from an user space. + * This mode gives access to common bus operations (write, read etc) and + * support additional functionalities. \n + * The protocol is based on exchange of binary messages included between a + * start and an end byte + * @{ + */ + +#define MESSAGE_START_BYTE 0x7B /* /< start byte of each message + * transferred in Scriptless Mode */ +#define MESSAGE_END_BYTE 0x7D /* /< end byte of each message + * transferred in Scriptless Mode */ +#define MESSAGE_MIN_HEADER_SIZE 8 /* /< minimun number of bytes of the + * structure of a messages exchanged + * with host (include start/end byte, + * counter, actions, msg_size) */ + +/** + * Possible actions that can be requested by an host + */ +typedef enum { + ACTION_WRITE = (u16) 0x0001, /* /< Bus Write + * */ + ACTION_READ = (u16) 0x0002, /* /< Bus Read + * */ + ACTION_WRITE_READ = (u16) 0x0003, /* /< Bus Write + * followed by a + * Read */ + ACTION_GET_VERSION = (u16) 0x0004, /* /< Get + * Version of + * the protocol + * (equal to the + * first 2 bye + * of driver + * version) */ + ACTION_WRITEU8UX = (u16) 0x0011, /* /< Bus Write + * with support + * to different + * address size + * */ + ACTION_WRITEREADU8UX = (u16) 0x0012, /* /< Bus + * writeRead + * with support + * to different + * address size + * */ + ACTION_WRITETHENWRITEREAD = (u16) 0x0013, /* /< Bus write + * followed by a + * writeRead */ + ACTION_WRITEU8XTHENWRITEREADU8UX = (u16) 0x0014, /* /< Bus write + * followed by a + * writeRead + * with support + * to different + * address size + * */ + ACTION_WRITEU8UXTHENWRITEU8UX = (u16) 0x0015, /* /< Bus write + * followed by a + * write with + * support to + * different + * address size + * */ + ACTION_GET_FW = (u16) 0x1000, /* /< Get Fw + * file content + * used by the + * driver */ + ACTION_GET_LIMIT = (u16) 0x1001 /* /< Get Limit + * File content + * used by the + * driver */ +} Actions; + +/** + * Struct used to contain info of the message received by the host in + * Scriptless mode + */ +typedef struct { + u16 msg_size; /* /< total size of the message in bytes */ + u16 counter; /* /< counter ID to identify a message */ + Actions action; /* /< type of operation requested by the host @see + * Actions */ + u8 dummy; /* /< (optional)in case of any kind of read operations, + * specify if the first byte is dummy */ +} Message; + +/** @}*/ + + + +extern TestToDo tests; +extern SysInfo systemInfo; + +static int limit; /* /< store the amount of data to print into the shell */ +static int chunk; /* /< store the chuk of data that should be printed in + * this iteration */ +static int printed; /* /< store the amount of data already printed in the + * shell */ +static struct proc_dir_entry *fts_dir; /* /< reference to the directory + * fts under /proc */ +static u8 *driver_test_buff; /* /< pointer to an array of bytes used + * to store the result of the function + * executed */ +char buf_chunk[CHUNK_PROC] = { 0 }; /* /< buffer used to store the message + * info received */ +static Message mess = { 0 }; /* /< store the information of the Scriptless + * message received */ + + +/************************ SEQUENTIAL FILE UTILITIES **************************/ +/** + * This function is called at the beginning of the stream to a sequential file + * or every time into the sequential were already written PAGE_SIZE bytes and + * the stream need to restart + * @param s pointer to the sequential file on which print the data + * @param pos pointer to the offset where write the data + * @return NULL if there is no data to print or the pointer to the beginning of + * the data that need to be printed + */ +static void *fts_seq_start(struct seq_file *s, loff_t *pos) +{ + logError(0, + "%s %s: Entering start(), pos = %ld limit = %d printed = %d\n", + tag, + __func__, *pos, limit, printed); + + if (driver_test_buff == NULL && *pos == 0) { + logError(1, "%s %s: No data to print!\n", tag, __func__); + driver_test_buff = (u8 *)kmalloc(13 * sizeof(u8), GFP_KERNEL); + + snprintf(driver_test_buff, 14, "{ %08X }\n", ERROR_OP_NOT_ALLOW); + + + limit = strlen(driver_test_buff); + /* logError(0, "%s %s: len = %d driver_test_buff = %s\n", tag, + * __func__, limit, driver_test_buff); */ + } else { + if (*pos != 0) + *pos += chunk - 1; + + if (*pos >= limit) + /* logError(0, "%s %s: Apparently, we're done.\n", tag, + * __func__); */ + return NULL; + } + + chunk = CHUNK_PROC; + if (limit - *pos < CHUNK_PROC) + chunk = limit - *pos; + /* logError(0, "%s %s: In start(), updated pos = %ld limit = %d printed = %d chunk = %d\n", + * tag, __func__, *pos, limit, printed, chunk); */ + memset(buf_chunk, 0, CHUNK_PROC); + memcpy(buf_chunk, &driver_test_buff[(int)*pos], chunk); + + return buf_chunk; +} + +/** + * This function actually print a chunk amount of data in the sequential file + * @param s pointer to the sequential file where to print the data + * @param v pointer to the data to print + * @return 0 + */ +static int fts_seq_show(struct seq_file *s, void *v) +{ + /* logError(0, "%s %s: In show()\n", tag, __func__); */ + seq_write(s, (u8 *)v, chunk); + printed += chunk; + return 0; +} + +/** + * This function update the pointer and the counters to the next data to be + * printed + * @param s pointer to the sequential file where to print the data + * @param v pointer to the data to print + * @param pos pointer to the offset where write the next data + * @return NULL if there is no data to print or the pointer to the beginning of + * the next data that need to be printed + */ +static void *fts_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + /* int* val_ptr; */ + /* logError(0, "%s %s: In next(), v = %X, pos = %ld.\n", tag, __func__, + * v, *pos); */ + (*pos) += chunk;/* increase my position counter */ + chunk = CHUNK_PROC; + + /* logError(0, "%s %s: In next(), updated pos = %ld limit = %d printed = %d\n", + * tag, __func__, *pos, limit,printed); */ + if (*pos >= limit) /* are we done? */ + return NULL; + else if (limit - *pos < CHUNK_PROC) + chunk = limit - *pos; + + + memset(buf_chunk, 0, CHUNK_PROC); + memcpy(buf_chunk, &driver_test_buff[(int)*pos], chunk); + return buf_chunk; +} + + +/** + * This function is called when there are no more data to print the stream + *need to be terminated or when PAGE_SIZE data were already written into the + *sequential file + * @param s pointer to the sequential file where to print the data + * @param v pointer returned by fts_seq_next + */ +static void fts_seq_stop(struct seq_file *s, void *v) +{ + /* logError(0, "%s %s: Entering stop().\n", tag, __func__); */ + + if (v) { + /* logError(0, "%s %s: v is %X.\n", tag, __func__, v); */ + } else { + /* logError(0, "%s %s: v is null.\n", tag, __func__); */ + limit = 0; + chunk = 0; + printed = 0; + if (driver_test_buff != NULL) { + /* logError(0, "%s %s: Freeing and clearing driver_test_buff.\n", + * tag, __func__); */ + kfree(driver_test_buff); + driver_test_buff = NULL; + } else { + /* logError(0, "%s %s: driver_test_buff is already null.\n", + * tag, __func__); */ + } + } +} + +/** + * Struct where define and specify the functions which implements the flow for + *writing on a sequential file + */ +static const struct seq_operations fts_seq_ops = { + .start = fts_seq_start, + .next = fts_seq_next, + .stop = fts_seq_stop, + .show = fts_seq_show +}; + +/** + * This function open a sequential file + * @param inode Inode in the file system that was called and triggered this + * function + * @param file file associated to the file node + * @return error code, 0 if success + */ +static int fts_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &fts_seq_ops); +}; + + +/*****************************************************************************/ + +/**************************** DRIVER TEST ************************************/ + +/** @addtogroup proc_file_code + * @{ + */ + +/** + * Receive the OP code and the inputs from shell when the file node is called, + * parse it and then execute the corresponding function + * echo cmd+parameters > /proc/fts/driver_test to execute the select command + * cat /proc/fts/driver_test to obtain the result into the + * shell \n + * the string returned in the shell is made up as follow: \n + * { = start byte \n + * the answer content and format strictly depend on the cmd executed. In + * general can be: an HEX string or a byte array (e.g in case of 0xF- commands) + * \n + * } = end byte \n + */ +static ssize_t fts_driver_test_write(struct file *file, const char __user *buf, + size_t count, loff_t *pos) +{ + int numberParam = 0; + struct fts_ts_info *info = dev_get_drvdata(getDev()); + char *p = NULL; + char *pbuf = NULL; + char path[100] = { 0 }; + int res = -1, j, index = 0; + int size = 6; + int temp, byte_call = 0; + u16 byteToRead = 0; + u32 fileSize = 0; + u8 *readData = NULL; + u8 *cmd = NULL; + u32 *funcToTest = NULL; + u64 addr = 0; + MutualSenseFrame frameMS; + SelfSenseFrame frameSS; + + DataHeader dataHead; + MutualSenseData compData; + SelfSenseData comData; + TotMutualSenseData totCompData; + TotSelfSenseData totComData; + + u64 address; + + Firmware fw; + LimitFile lim; + + mess.dummy = 0; + mess.action = 0; + mess.msg_size = 0; + + pbuf = (char *)kzalloc(count + 1, GFP_KERNEL); + cmd = (u8 *)kzalloc(count, GFP_KERNEL); + funcToTest = (u32 *)kzalloc((int)((count + 1)/3), GFP_KERNEL); + if (!pbuf || !cmd || !funcToTest) { + res = ERROR_ALLOC; + goto END; + } + + /*for(temp = 0; temp MESSAGE_MIN_HEADER_SIZE - 1 && p[0] == MESSAGE_START_BYTE) { + logError(0, "%s Enter in Byte Mode!\n", tag); + byte_call = 1; + mess.msg_size = (p[1] << 8) | p[2]; + mess.counter = (p[3] << 8) | p[4]; + mess.action = (p[5] << 8) | p[6]; + logError(0, + "%s Message received: size = %d, counter_id = %d, action = %04X\n", + tag, mess.msg_size, mess.counter, mess.action); + size = MESSAGE_MIN_HEADER_SIZE + 2; /* +2 error code */ + if (count < mess.msg_size || p[count - 2] != MESSAGE_END_BYTE) { + logError(1, + "%s number of byte received or end byte wrong! msg_size = %d != %d, last_byte = %02X != %02X ... ERROR %08X\n", + tag, mess.msg_size, (int)count, p[count - 1], + MESSAGE_END_BYTE, ERROR_OP_NOT_ALLOW); + res = ERROR_OP_NOT_ALLOW; + goto END; + } else { + numberParam = mess.msg_size - MESSAGE_MIN_HEADER_SIZE + + 1; /* +1 because put the internal + * op code */ + size = MESSAGE_MIN_HEADER_SIZE + 2; /* +2 send also + * the first 2 + * lsb of the + * error code */ + switch (mess.action) { + case ACTION_READ: + /* numberParam = + * mess.msg_size-MESSAGE_MIN_HEADER_SIZE+1; */ + cmd[0] = funcToTest[0] = CMD_READ_BYTE; + break; + + case ACTION_WRITE: + cmd[0] = funcToTest[0] = CMD_WRITE_BYTE; + break; + + case ACTION_WRITE_READ: + cmd[0] = funcToTest[0] = CMD_WRITEREAD_BYTE; + break; + + case ACTION_GET_VERSION: + cmd[0] = funcToTest[0] = CMD_VERSION_BYTE; + break; + + case ACTION_WRITETHENWRITEREAD: + cmd[0] = funcToTest[0] = + CMD_WRITETHENWRITEREAD_BYTE; + break; + + case ACTION_WRITEU8UX: + cmd[0] = funcToTest[0] = CMD_WRITEU8UX_BYTE; + break; + + case ACTION_WRITEREADU8UX: + cmd[0] = funcToTest[0] = CMD_WRITEREADU8UX_BYTE; + break; + + case ACTION_WRITEU8UXTHENWRITEU8UX: + cmd[0] = funcToTest[0] = + CMD_WRITEU8UXTHENWRITEU8UX_BYTE; + break; + + case ACTION_WRITEU8XTHENWRITEREADU8UX: + cmd[0] = funcToTest[0] = + CMD_WRITEU8UXTHENWRITEREADU8UX_BYTE; + break; + + case ACTION_GET_FW: + cmd[0] = funcToTest[0] = CMD_GETFWFILE_BYTE; + break; + + case ACTION_GET_LIMIT: + cmd[0] = funcToTest[0] = CMD_GETLIMITSFILE_BYTE; + break; + + default: + logError(1, + "%s Invalid Action = %d ... ERROR %08X\n", + tag, + mess.action, ERROR_OP_NOT_ALLOW); + res = ERROR_OP_NOT_ALLOW; + goto END; + } + + if (numberParam - 1 != 0) + memcpy(&cmd[1], &p[7], numberParam - 1); + /* -1 because i need to exclude the cmd[0] */ + } + } else { + if (((count + 1) / 3) >= 1) { + if (sscanf(p, "%02X ", &funcToTest[0]) == 1) { + p += 3; + cmd[0] = (u8)funcToTest[0]; + numberParam = 1; + } + } else { + res = ERROR_OP_NOT_ALLOW; + goto END; + } + + logError(1, "%s functionToTest[0] = %02X cmd[0]= %02X\n", tag, + funcToTest[0], cmd[0]); + switch (funcToTest[0]) { + case CMD_GETFWFILE: + case CMD_GETLIMITSFILE: + if (count - 2 - 1 > 1) { + if (sscanf(p, "%100s", path) == 1) + numberParam = 2; + /* the first byte is an hex + * string coded in three byte (2 + * chars for hex and the space) + * and -1 for the space at the + * end */ + } + break; + + default: + for (; numberParam < (count + 1) / 3; numberParam++) { + if (sscanf(p, "%02X ", + &funcToTest[numberParam]) == 1) { + p += 3; + cmd[numberParam] = + (u8)funcToTest[numberParam]; + logError(1, + "%s functionToTest[%d] = %02X cmd[%d]= %02X\n", + tag, numberParam, + funcToTest[numberParam], + numberParam, cmd[numberParam]); + } + + } + } + } + + + fw.data = NULL; + lim.data = NULL; + + logError(1, "%s Number of Parameters = %d\n", tag, numberParam); + + /* elaborate input */ + if (numberParam >= 1) { + /*res = fts_disableInterrupt(); + * if (res < 0) + * { + * logError(0, "%s %s: ERROR %08X\n", tag, __func__, + *res); + * res = (res | ERROR_DISABLE_INTER); + * goto END; + * }*/ + switch (funcToTest[0]) { + case CMD_VERSION_BYTE: + logError(0, "%s %s: Get Version Byte\n", tag, + __func__); + byteToRead = 2; + mess.dummy = 0; + readData = (u8 *)kmalloc(byteToRead * sizeof(u8), + GFP_KERNEL); + size += byteToRead; + if (readData != NULL) { + readData[0] = (u8)(FTS_TS_DRV_VER >> 24); + readData[1] = (u8)(FTS_TS_DRV_VER >> 16); + res = OK; + logError(0, "%s %s: Version = %02X%02X\n", tag, + __func__, readData[0], readData[1]); + } else { + res = ERROR_ALLOC; + logError(1, + "%s %s: Impossible allocate memory... ERROR %08X\n", + tag, __func__, res); + } + break; + + + case CMD_VERSION: + byteToRead = 2 * sizeof(u32); + mess.dummy = 0; + readData = (u8 *)kmalloc(byteToRead * sizeof(u8), + GFP_KERNEL); + u32ToU8_be(FTS_TS_DRV_VER, readData); + fileSize = 0; + /* first two bytes bitmask of features enabled in the + * IC, second two bytes bitmask of features enabled in + * the driver */ + +#ifdef FW_H_FILE + fileSize |= 0x00010000; +#endif + +#ifdef LIMITS_H_FILE + fileSize |= 0x00020000; +#endif + +#ifdef USE_ONE_FILE_NODE + fileSize |= 0x00040000; +#endif + +#ifdef FW_UPDATE_ON_PROBE + fileSize |= 0x00080000; +#endif + +#ifdef PRE_SAVED_METHOD + fileSize |= 0x00100000; +#endif + +#ifdef COMPUTE_INIT_METHOD + fileSize |= 0x00200000; +#endif + +#ifdef USE_GESTURE_MASK + fileSize |= 0x00100000; +#endif + +#ifdef I2C_INTERFACE + fileSize |= 0x00200000; +#endif + +#ifdef SPI4_WIRE + fileSize |= 0x00400000; +#endif + +#ifdef PHONE_KEY /* it is a feature enabled in the config of the chip */ + fileSize |= 0x00000100; +#endif + +#ifdef GESTURE_MODE + fromIDtoMask(FEAT_SEL_GESTURE, (u8 *)&fileSize, 4); +#endif + + +#ifdef GRIP_MODE + fromIDtoMask(FEAT_SEL_GRIP, (u8 *)&fileSize, 4); +#endif + +#ifdef CHARGER_MODE + fromIDtoMask(FEAT_SEL_CHARGER, (u8 *)&fileSize, 4); +#endif + +#ifdef GLOVE_MODE + fromIDtoMask(FEAT_SEL_GLOVE, (u8 *)&fileSize, 4); +#endif + + +#ifdef COVER_MODE + fromIDtoMask(FEAT_SEL_COVER, (u8 *)&fileSize, 4); +#endif + +#ifdef STYLUS_MODE + fromIDtoMask(FEAT_SEL_STYLUS, (u8 *)&fileSize, 4); +#endif + + u32ToU8_be(fileSize, &readData[4]); + res = OK; + size += (byteToRead * sizeof(u8)); + break; + + case CMD_WRITEREAD: + case CMD_WRITEREAD_BYTE: + if (numberParam >= 5) { /* need to pass: cmd[0] cmd[1] + * … cmd[cmdLength-1] + * byteToRead1 byteToRead0 + * dummyByte */ + temp = numberParam - 4; + if (cmd[numberParam - 1] == 0) + mess.dummy = 0; + else + mess.dummy = 1; + + u8ToU16_be(&cmd[numberParam - 3], &byteToRead); + logError(0, "%s bytesToRead = %d\n", tag, + byteToRead + mess.dummy); + + readData = (u8 *)kmalloc((byteToRead + + mess.dummy) * + sizeof(u8), + GFP_KERNEL); + res = fts_writeRead(&cmd[1], temp, readData, + byteToRead + mess.dummy); + size += (byteToRead * sizeof(u8)); + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_WRITE: + case CMD_WRITE_BYTE: + if (numberParam >= 2) { /* need to pass: cmd[0] cmd[1] + * … cmd[cmdLength-1] */ + temp = numberParam - 1; + + res = fts_write(&cmd[1], temp); + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READ: + case CMD_READ_BYTE: + if (numberParam >= 3) { /* need to pass: byteToRead1 + * byteToRead0 (optional) + * dummyByte */ + if (numberParam == 3 || (numberParam == 4 && + cmd[numberParam - 1] == + 0)) + mess.dummy = 0; + else + mess.dummy = 1; + u8ToU16_be(&cmd[1], &byteToRead); + readData = (u8 *)kmalloc((byteToRead + + mess.dummy) * + sizeof(u8), + GFP_KERNEL); + res = fts_read(readData, byteToRead + + mess.dummy); + size += (byteToRead * sizeof(u8)); + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_WRITETHENWRITEREAD: + case CMD_WRITETHENWRITEREAD_BYTE: + /* need to pass: cmdSize1 cmdSize2 cmd1[0] cmd1[1] … + * cmd1[cmdSize1-1] cmd2[0] cmd2[1] … cmd2[cmdSize2-1] + * byteToRead1 byteToRead0 */ + if (numberParam >= 6) { + u8ToU16_be(&cmd[numberParam - 2], &byteToRead); + readData = (u8 *)kmalloc(byteToRead * + sizeof(u8), + GFP_KERNEL); + res = fts_writeThenWriteRead(&cmd[3], cmd[1], + &cmd[3 + + (int)cmd[1]], + cmd[2], readData, + byteToRead); + size += (byteToRead * sizeof(u8)); + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_WRITEU8UX: + case CMD_WRITEU8UX_BYTE: + /* need to pass: cmd addrSize addr[0] … addr[addrSize-1] + * data[0] data[1] … */ + if (numberParam >= 4) { + if (cmd[2] <= sizeof(u64)) { + u8ToU64_be(&cmd[3], &addr, cmd[2]); + logError(0, "%s addr = %016llX %ld\n", + tag, addr, (long int)addr); + res = fts_writeU8UX(cmd[1], cmd[2], + addr, &cmd[3 + + cmd[2]], + (numberParam - + cmd + [ + 2] - 3)); + } else { + logError(1, "%s Wrong address size!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + + case CMD_WRITEREADU8UX: + case CMD_WRITEREADU8UX_BYTE: + /* need to pass: cmd addrSize addr[0] … addr[addrSize-1] + * byteToRead1 byteToRead0 hasDummyByte */ + if (numberParam >= 6) { + if (cmd[2] <= sizeof(u64)) { + u8ToU64_be(&cmd[3], &addr, cmd[2]); + u8ToU16_be(&cmd[numberParam - 3], + &byteToRead); + readData = (u8 *)kmalloc(byteToRead * + sizeof(u8), + GFP_KERNEL); + logError(0, + "%s addr = %016llX byteToRead = %d\n", + tag, addr, byteToRead); + res = fts_writeReadU8UX(cmd[1], cmd[2], + addr, readData, + byteToRead, + cmd[numberParam + - 1]); + size += (byteToRead * sizeof(u8)); + } else { + logError(1, "%s Wrong address size!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_WRITEU8UXTHENWRITEU8UX: + case CMD_WRITEU8UXTHENWRITEU8UX_BYTE: + /* need to pass: cmd1 addrSize1 cmd2 addrSize2 addr[0] … + * addr[addrSize1+addrSize2-1] data[0] data[1] … */ + if (numberParam >= 6) { + if ((cmd[2] + cmd[4]) <= sizeof(u64)) { + u8ToU64_be(&cmd[5], &addr, cmd[2] + + cmd[4]); + + logError(0, "%s addr = %016llX %lld\n", + tag, addr, addr); + res = fts_writeU8UXthenWriteU8UX(cmd[1], + cmd[2], + cmd[3], + cmd[4], + addr, + &cmd[5 + + cmd[2] + + cmd[4]], + (numberParam - + cmd[2] - + cmd[4] - + 5)); + } else { + logError(1, "%s Wrong address size!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_WRITEU8UXTHENWRITEREADU8UX: + case CMD_WRITEU8UXTHENWRITEREADU8UX_BYTE: + /* need to pass: cmd1 addrSize1 cmd2 addrSize2 addr[0] … + * addr[addrSize1+addrSize2-1] byteToRead1 byteToRead0 + * hasDummybyte */ + if (numberParam >= 8) { + if ((cmd[2] + cmd[4]) <= sizeof(u64)) { + u8ToU64_be(&cmd[5], &addr, cmd[2] + + cmd[4]); + logError(1, + "%s %s: cmd[5] = %02X, addr = %016llX\n", + tag, __func__, cmd[5], addr); + u8ToU16_be(&cmd[numberParam - 3], + &byteToRead); + readData = (u8 *)kmalloc(byteToRead * + sizeof(u8), + GFP_KERNEL); + res = fts_writeU8UXthenWriteReadU8UX( + cmd[1], cmd[2], cmd[3], cmd[4], + addr, + readData, byteToRead, + cmd[numberParam - 1]); + size += (byteToRead * sizeof(u8)); + } else { + logError(1, + "%s Wrong total address size!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + + break; + + case CMD_CHANGE_OUTPUT_MODE: + /* need to pass: bin_output */ + if (numberParam >= 2) { + bin_output = cmd[1]; + logError(0, + "%s Setting Scriptless output mode: %d\n", + tag, + bin_output); + res = OK; + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_FWWRITE: + if (numberParam >= 3) { /* need to pass: cmd[0] cmd[1] + * … cmd[cmdLength-1] */ + if (numberParam >= 2) { + temp = numberParam - 1; + res = fts_writeFwCmd(&cmd[1], temp); + } else { + logError(1, "%s Wrong parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_INTERRUPT: + /* need to pass: enable */ + if (numberParam >= 2) { + if (cmd[1] == 1) + res = fts_enableInterrupt(); + else + res = fts_disableInterrupt(); + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_SETSCANMODE: + /* need to pass: scanMode option */ + if (numberParam >= 3) + res = setScanMode(cmd[1], cmd[2]); + else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_SAVEMPFLAG: + /* need to pass: mpflag */ + if (numberParam == 2) + res = saveMpFlag(cmd[1]); + else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + + case CMD_READCONFIG: + if (numberParam == 5) { /* need to pass: addr[0] + * addr[1] byteToRead1 + * byteToRead0 */ + byteToRead = ((funcToTest[3] << 8) | + funcToTest[4]); + readData = (u8 *)kmalloc(byteToRead * + sizeof(u8), + GFP_KERNEL); + res = readConfig((u16)((((u8)funcToTest[1] & + 0x00FF) << 8) + + ((u8)funcToTest[2] & + 0x00FF)), + readData, byteToRead); + size += (byteToRead * sizeof(u8)); + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_POLLFOREVENT: + if (numberParam >= 5) { /* need to pass: eventLength + * event[0] event[1] … + * event[eventLength-1] + * timeTowait1 timeTowait0 */ + temp = (int)funcToTest[1]; + if (numberParam == 5 + (temp - 1) && temp != + 0) { + readData = (u8 *)kmalloc( + FIFO_EVENT_SIZE * sizeof(u8), + GFP_KERNEL); + res = pollForEvent( + (int *)&funcToTest[2], temp, + readData, + ((funcToTest[temp + 2] & + 0x00FF) << 8) + + (funcToTest[temp + 3] & + 0x00FF)); + if (res >= OK) + res = OK; /* pollForEvent + * return the + * number of + * error found + * */ + size += (FIFO_EVENT_SIZE * sizeof(u8)); + byteToRead = FIFO_EVENT_SIZE; + } else { + logError(1, "%s Wrong parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_SYSTEMRESET: + res = fts_system_reset(); + + break; + + case CMD_READSYSINFO: + if (numberParam == 2) /* need to pass: doRequest */ + res = readSysInfo(funcToTest[1]); + else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + + break; + + case CMD_CLEANUP:/* TOUCH ENABLE/DISABLE */ + if (numberParam == 2) /* need to pass: enableTouch */ + res = cleanUp(funcToTest[1]); + else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + + break; + + case CMD_GETFORCELEN: /* read number Tx channels */ + temp = getForceLen(); + if (temp < OK) + res = temp; + else { + size += (1 * sizeof(u8)); + res = OK; + } + break; + + case CMD_GETSENSELEN: /* read number Rx channels */ + temp = getSenseLen(); + if (temp < OK) + res = temp; + else { + size += (1 * sizeof(u8)); + res = OK; + } + break; + + + case CMD_GETMSFRAME: + if (numberParam == 2) { + /*logError(0, "%s Get 1 MS Frame\n", tag); + setScanMode(SCAN_MODE_ACTIVE, 0xFF); + msleep(WAIT_FOR_FRESH_FRAMES); + setScanMode(SCAN_MODE_ACTIVE, 0x00); + msleep(WAIT_AFTER_SENSEOFF); + flushFIFO(); */ /* delete the events related to + * some touch (allow to call this function while + * touching the screen without having a flooding + * of the FIFO) */ + res = getMSFrame3((MSFrameType)cmd[1], + &frameMS); + if (res < 0) + logError(0, + "%s Error while taking the MS frame... ERROR %08X\n", + tag, res); + + else { + logError(0, + "%s The frame size is %d words\n", + tag, + res); + size += (res * sizeof(short) + 2); + /* +2 to add force and sense channels */ + /* set res to OK because if getMSFrame + * is + * successful res = number of words + *read + */ + res = OK; + print_frame_short("MS frame =", + array1dTo2d_short( + frameMS. + node_data, + frameMS + . + node_data_size, + frameMS + .header. + sense_node), + frameMS.header. + force_node, + frameMS.header. + sense_node); + } + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + + /*read self raw*/ + case CMD_GETSSFRAME: + if (numberParam == 2) { + logError(0, "%s Get 1 SS Frame\n", tag); + /* flushFIFO(); //delete the events related to + * some touch (allow to call this function while + * touching the screen without having a flooding + * of the FIFO) */ + /* setScanMode(SCAN_MODE_ACTIVE, 0xFF); + msleep(WAIT_FOR_FRESH_FRAMES); + setScanMode(SCAN_MODE_ACTIVE, 0x00); + msleep(WAIT_AFTER_SENSEOFF); */ + res = getSSFrame3((SSFrameType)cmd[1], + &frameSS); + + if (res < OK) + logError(0, + "%s Error while taking the SS frame... ERROR %08X\n", + tag, res); + + else { + logError(0, + "%s The frame size is %d words\n", + tag, + res); + size += (res * sizeof(short) + 2); + /* +2 to add force and sense channels */ + /* set res to OK because if getMSFrame + * is + * successful res = number of words + *read + */ + res = OK; + print_frame_short("SS force frame =", + array1dTo2d_short( + frameSS. + force_data, + frameSS + .header. + force_node, + 1), + frameSS.header. + force_node, 1); + print_frame_short("SS sense frame =", + array1dTo2d_short( + frameSS. + sense_data, + frameSS + .header. + sense_node, + frameSS + .header. + sense_node), + 1, + frameSS.header. + sense_node); + } + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_GETSYNCFRAME: + /* need to pass: frameType (this parameter can be + * one of LOAD_SYNC_FRAME_X) */ + if (numberParam == 2) { + logError(1, "%s Reading Sync Frame...\n", tag); + res = getSyncFrame(cmd[1], &frameMS, &frameSS); + if (res < OK) + logError(0, + "%s Error while taking the Sync Frame frame... ERROR %08X\n", + tag, res); + + else { + logError(0, + "%s The total frames size is %d words\n", + tag, res); + size += (res * sizeof(short) + 4); + /* +4 to add force and sense channels + * for MS and SS */ + /* set res to OK because if getSyncFrame + * is + * successful res = number of words + *read + */ + res = OK; + + print_frame_short("MS frame =", + array1dTo2d_short( + frameMS. + node_data, + frameMS + . + node_data_size, + frameMS + .header. + sense_node), + frameMS.header. + force_node, + frameMS.header. + sense_node); + print_frame_short("SS force frame =", + array1dTo2d_short( + frameSS. + force_data, + frameSS + .header. + force_node, + 1), + frameSS.header. + force_node, 1); + print_frame_short("SS sense frame =", + array1dTo2d_short( + frameSS. + sense_data, + frameSS + .header. + sense_node, + frameSS + .header. + sense_node), + 1, + frameSS.header. + sense_node); + } + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_REQCOMPDATA: /* request comp data */ + if (numberParam == 2) { + logError(0, + "%s Requesting Compensation Data\n", + tag); + res = requestCompensationData(cmd[1]); + + if (res < OK) + logError(0, + "%s Error requesting compensation data ERROR %08X\n", + tag, res); + else + logError(0, + "%s Requesting Compensation Data Finished!\n", + tag); + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READCOMPDATAHEAD: /* read comp data header */ + if (numberParam == 2) { + logError(0, + "%s Requesting Compensation Data\n", + tag); + res = requestCompensationData(cmd[1]); + if (res < OK) + logError(0, + "%s Error requesting compensation data ERROR %08X\n", + tag, res); + else { + logError(0, + "%s Requesting Compensation Data Finished!\n", + tag); + res = readCompensationDataHeader( + (u8)funcToTest[1], &dataHead, + &address); + if (res < OK) + logError(0, + "%s Read Compensation Data Header ERROR %08X\n", + tag, res); + else { + logError(0, + "%s Read Compensation Data Header OK!\n", + tag); + size += (1 * sizeof(u8)); + } + } + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + + case CMD_READMSCOMPDATA:/* read mutual comp data */ + if (numberParam == 2) { + logError(0, "%s Get MS Compensation Data\n", + tag); + res = readMutualSenseCompensationData(cmd[1], + &compData); + + if (res < OK) + logError(0, + "%s Error reading MS compensation data ERROR %08X\n", + tag, res); + else { + logError(0, + "%s MS Compensation Data Reading Finished!\n", + tag); + size = ((compData.node_data_size + 10) * + sizeof(i8)); + print_frame_i8("MS Data (Cx2) =", + array1dTo2d_i8( + compData. + node_data, + compData. + node_data_size, + compData. + header.sense_node), + compData.header. + force_node, + compData.header. + sense_node); + } + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READSSCOMPDATA: + if (numberParam == 2) { /* read self comp data */ + logError(0, "%s Get SS Compensation Data...\n", + tag); + res = readSelfSenseCompensationData(cmd[1], + &comData); + if (res < OK) + logError(0, + "%s Error reading SS compensation data ERROR %08X\n", + tag, res); + else { + logError(0, + "%s SS Compensation Data Reading Finished!\n", + tag); + size = ((comData.header.force_node + + comData.header.sense_node) * + 2 + 15) * + sizeof(i8); + print_frame_i8("SS Data Ix2_fm = ", + array1dTo2d_i8( + comData.ix2_fm, + comData. + header.force_node, + comData. + header.force_node), + 1, + comData.header.force_node); + print_frame_i8("SS Data Cx2_fm = ", + array1dTo2d_i8( + comData.cx2_fm, + comData. + header.force_node, + comData. + header.force_node), + 1, + comData.header.force_node); + print_frame_i8("SS Data Ix2_sn = ", + array1dTo2d_i8( + comData.ix2_sn, + comData. + header.sense_node, + comData. + header.sense_node), + 1, + comData.header.sense_node); + print_frame_i8("SS Data Cx2_sn = ", + array1dTo2d_i8( + comData.cx2_sn, + comData. + header.sense_node, + comData. + header.sense_node), + 1, + comData.header.sense_node); + } + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READTOTMSCOMPDATA: /* read mutual comp data */ + if (numberParam == 2) { + logError(0, + "%s Get TOT MS Compensation Data\n", + tag); + res = readTotMutualSenseCompensationData(cmd[1], + &totCompData); + + if (res < OK) + logError(0, + "%s Error reading TOT MS compensation data ERROR %08X\n", + tag, res); + else { + logError(0, + "%s TOT MS Compensation Data Reading Finished!\n", + tag); + size = (totCompData.node_data_size * + sizeof(short) + 9); + print_frame_short("MS Data (TOT Cx) =", + array1dTo2d_short( + totCompData. + node_data, + totCompData. + node_data_size, + totCompData + .header. + sense_node), + totCompData.header. + force_node, + totCompData.header. + sense_node); + } + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_READTOTSSCOMPDATA: + if (numberParam == 2) { /* read self comp data */ + logError(0, + "%s Get TOT SS Compensation Data...\n", + tag); + res = readTotSelfSenseCompensationData(cmd[1], + &totComData); + if (res < OK) + logError(0, + "%s Error reading TOT SS compensation data ERROR %08X\n", + tag, res); + else { + logError(0, + "%s TOT SS Compensation Data Reading Finished!\n", + tag); + size = ((totComData.header.force_node + + totComData.header.sense_node) * + 2 * + sizeof(short) + 9); + print_frame_u16("SS Data TOT Ix_fm = ", + array1dTo2d_u16( + totComData.ix_fm, + totComData + .header. + force_node, + totComData + .header. + force_node), 1, + totComData.header. + force_node); + print_frame_short( + "SS Data TOT Cx_fm = ", + array1dTo2d_short( + totComData.cx_fm, + totComData. + header.force_node, + totComData. + header.force_node), 1, + totComData.header.force_node); + print_frame_u16("SS Data TOT Ix_sn = ", + array1dTo2d_u16( + totComData.ix_sn, + totComData + .header. + sense_node, + totComData + .header. + sense_node), 1, + totComData.header. + sense_node); + print_frame_short( + "SS Data TOT Cx_sn = ", + array1dTo2d_short( + totComData.cx_sn, + totComData. + header.sense_node, + totComData. + header.sense_node), 1, + totComData.header.sense_node); + } + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + + case CMD_GETFWVER: + size += (EXTERNAL_RELEASE_INFO_SIZE)*sizeof(u8); + break; + + case CMD_FLASHUNLOCK: + res = flash_unlock(); + if (res < OK) + logError(1, + "%s Impossible Unlock Flash ERROR %08X\n", + tag, + res); + else + logError(0, "%s Flash Unlock OK!\n", tag); + break; + + case CMD_READFWFILE: + if (numberParam == 2) { /* read fw file */ + logError(0, "%s Reading FW File...\n", tag); + res = readFwFile(PATH_FILE_FW, &fw, + funcToTest[1]); + if (res < OK) + logError(0, + "%s Error reading FW File ERROR %08X\n", + tag, res); + else + logError(0, + "%s Read FW File Finished!\n", + tag); + kfree(fw.data); + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_FLASHPROCEDURE: + if (numberParam == 3) { /* flashing procedure */ + logError(0, + "%s Starting Flashing Procedure...\n", + tag); + res = flashProcedure(PATH_FILE_FW, cmd[1], + cmd[2]); + if (res < OK) + logError(0, + "%s Error during flash procedure ERROR %08X\n", + tag, res); + else + logError(0, + "%s Flash Procedure Finished!\n", + tag); + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_FLASHERASEUNLOCK: + res = flash_erase_unlock(); + if (res < OK) + logError(0, + "%s Error during flash erase unlock... ERROR %08X\n", + tag, res); + else + logError(0, "%s Flash Erase Unlock Finished!\n", + tag); + break; + + case CMD_FLASHERASEPAGE: + if (numberParam == 2) { /* need to pass: keep_cx */ + logError(0, "%s Reading FW File...\n", tag); + res = readFwFile(PATH_FILE_FW, &fw, + funcToTest[1]); + if (res < OK) + logError(0, + "%s Error reading FW File ERROR %08X\n", + tag, res); + else + logError(0, + "%s Read FW File Finished!\n", + tag); + logError(0, + "%s Starting Flashing Page Erase...\n", + tag); + res = flash_erase_page_by_page(cmd[1], &fw); + if (res < OK) + logError(0, + "%s Error during flash page erase... ERROR %08X\n", + tag, res); + else + logError(0, + "%s Flash Page Erase Finished!\n", + tag); + kfree(fw.data); + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + /*ITO TEST*/ + case CMD_ITOTEST: + res = production_test_ito(info->limit_path, &tests); + break; + + /*Initialization*/ + case CMD_INITTEST: + if (numberParam == 2) + res = production_test_initialization(cmd[1]); + + else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + + case CMD_MSRAWTEST: /* MS Raw DATA TEST */ + if (numberParam == 2) /* need to specify if stopOnFail + * */ + res = production_test_ms_raw(info->limit_path, + cmd[1], &tests); + else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_MSINITDATATEST:/* MS CX DATA TEST */ + if (numberParam == 2) /* need stopOnFail */ + res = production_test_ms_cx(info->limit_path, cmd[1], + &tests); + else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_SSRAWTEST: /* SS RAW DATA TEST */ + if (numberParam == 2) /* need stopOnFail */ + res = production_test_ss_raw(info->limit_path, + cmd[1], &tests); + else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_SSINITDATATEST:/* SS IX CX DATA TEST */ + if (numberParam == 2) /* need stopOnFail */ + res = production_test_ss_ix_cx(info->limit_path, + cmd[1], &tests); + else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + /*PRODUCTION TEST*/ + case CMD_MAINTEST: + if (numberParam >= 3) { /* need to specify if stopOnFail + * saveInit and + * mpflag(optional)*/ + if (numberParam == 3) + res = production_test_main(info->limit_path, + cmd[1], + cmd[2], &tests, + MP_FLAG_OTHERS); + else + res = production_test_main(info->limit_path, + cmd[1], + cmd[2], &tests, + cmd[3]); + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_FREELIMIT: + res = freeCurrentLimitsFile(); + break; + + case CMD_POWERCYCLE: + res = fts_chip_powercycle(info); + break; + + case CMD_GETLIMITSFILE: + /* need to pass: path(optional) return error code + + * number of byte read otherwise GUI could not now how + * many byte read */ + if (numberParam >= 1) { + lim.data = NULL; + lim.size = 0; + if (numberParam == 1) + res = getLimitsFile(info->limit_path, &lim); + else + res = getLimitsFile(path, &lim); + readData = lim.data; + fileSize = lim.size; + size += (fileSize * sizeof(u8)); + if (byte_call == 1) + size += sizeof(u32); /* transmit as + * first 4 bytes + * the size */ + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_GETLIMITSFILE_BYTE: + /* need to pass: byteToRead1 byteToRead0 */ + if (numberParam >= 3) { + lim.data = NULL; + lim.size = 0; + + u8ToU16_be(&cmd[1], &byteToRead); + addr = ((u64)byteToRead) * 4; /* number of + * words */ + + res = getLimitsFile(info->limit_path, &lim); + + readData = lim.data; + fileSize = lim.size; + + if (fileSize > addr) { + logError(1, + "%s Limits dimension expected by Host is less than actual size: expected = %d, real = %d\n", + tag, byteToRead, fileSize); + res = ERROR_OP_NOT_ALLOW; + } + + size += (addr * sizeof(u8)); + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_GETFWFILE: + /* need to pass: from (optional) otherwise select the + * approach chosen at compile time */ + if (numberParam >= 1) { + if (numberParam == 1) + res = getFWdata(PATH_FILE_FW, &readData, + &fileSize); + else + res = getFWdata(path, &readData, + &fileSize); + + size += (fileSize * sizeof(u8)); + if (byte_call == 1) + size += sizeof(u32); /* transmit as + * first 4 bytes + * the size */ + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + case CMD_GETFWFILE_BYTE: + /* need to pass: byteToRead1 byteToRead0 */ + if (numberParam == 3) { + u8ToU16_be(&cmd[1], &byteToRead); + addr = ((u64)byteToRead) * 4; /* number of + * words */ + + + res = getFWdata(PATH_FILE_FW, &readData, + &fileSize); + if (fileSize > addr) { + logError(1, + "%s FW dimension expected by Host is less than actual size: expected = %d, real = %d\n", + tag, byteToRead, fileSize); + res = ERROR_OP_NOT_ALLOW; + } + + size += (addr * sizeof(u8)); /* return always + * the amount + * requested by + * host, if real + * size is + * smaller, the + * data are + * padded to + * zero */ + } else { + logError(1, "%s Wrong number of parameters!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + break; + + /* finish all the diagnostic command with a goto ERROR in order + * to skip the modification on driver_test_buff */ + /* remember to set properly the limit and printed variables in + * order to make the seq_file logic to work */ + case CMD_DIAGNOSTIC: + index = 0; + size = 0; + fileSize = 256 * 1024 * sizeof(char); + driver_test_buff = (u8 *)kzalloc(fileSize, GFP_KERNEL); + readData = (u8 *)kmalloc((ERROR_DUMP_ROW_SIZE * + ERROR_DUMP_COL_SIZE) * + sizeof(u8), GFP_KERNEL); + if (driver_test_buff == NULL || readData == NULL) { + res = ERROR_ALLOC; + logError(1, + "%s Impossible allocate memory for buffers! ERROR %08X!\n", + tag, res); + goto END; + } + j = snprintf(&driver_test_buff[index], fileSize - index, + "DIAGNOSTIC TEST:\n1) I2C Test: "); + index += j; + + res = fts_writeReadU8UX(FTS_CMD_HW_REG_R, + ADDR_SIZE_HW_REG, ADDR_DCHIP_ID, + (u8 *)&temp, 2, + DUMMY_HW_REG); + if (res < OK) { + logError(1, + "%s Error during I2C test: ERROR %08X!\n", + tag, + res); + j = snprintf(&driver_test_buff[index], + fileSize - index, "ERROR %08X\n", + res); + index += j; + res = ERROR_OP_NOT_ALLOW; + goto END_DIAGNOSTIC; + } + + temp &= 0xFFFF; + logError(1, "%s Chip ID = %04X!\n", tag, temp); + j = snprintf(&driver_test_buff[index], fileSize - index, + "DATA = %04X, expected = %02X%02X\n", + temp, DCHIP_ID_1, + DCHIP_ID_0); + index += j; + if (temp != ((DCHIP_ID_1 << 8) | DCHIP_ID_0)) { + logError(1, + "%s Wrong CHIP ID, Diagnostic failed!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + goto END_DIAGNOSTIC; + } + + j = snprintf(&driver_test_buff[index], fileSize - index, + "Present Driver Mode: %08X\n", + info->mode); + index += j; + + j = snprintf(&driver_test_buff[index], fileSize - index, + "2) FW running: Sensing On..."); + index += j; + logError(1, "%s Sensing On!\n", tag); + readData[0] = FTS_CMD_SCAN_MODE; + readData[1] = SCAN_MODE_ACTIVE; + readData[2] = 0x1; + fts_write(readData, 3); + res = checkEcho(readData, 3); + if (res < OK) { + logError(1, + "%s No Echo received.. ERROR %08X !\n", + tag, + res); + j = snprintf(&driver_test_buff[index], + fileSize - index, + "No echo found... ERROR %08X!\n", + res); + index += j; + goto END_DIAGNOSTIC; + } else { + logError(1, "%s Echo FOUND... OK!\n", tag); + j = snprintf(&driver_test_buff[index], + fileSize - index, + "Echo FOUND... OK!\n"); + index += j; + } + + logError(1, "%s Reading Frames...!\n", tag); + j = snprintf(&driver_test_buff[index], fileSize - index, + "3) Read Frames:\n"); + index += j; + for (temp = 0; temp < DIAGNOSTIC_NUM_FRAME; temp++) { + logError(1, "%s Iteration n. %d...\n", tag, + temp + 1); + j = snprintf(&driver_test_buff[index], + fileSize - index, + "Iteration n. %d...\n", temp + + 1); + index += j; + for (addr = 0; addr < 3; addr++) { + switch (addr) { + case 0: + j = snprintf( + &driver_test_buff[index], + fileSize - index, + "MS RAW FRAME ="); + index += j; + res |= getMSFrame3(MS_RAW, + &frameMS); + break; + case 2: + j = snprintf( + &driver_test_buff[index], + fileSize - index, + "MS STRENGTH FRAME ="); + index += j; + res |= getMSFrame3(MS_STRENGTH, + &frameMS); + break; + case 1: + j = snprintf( + &driver_test_buff[index], + fileSize - index, + "MS BASELINE FRAME ="); + index += j; + res |= getMSFrame3(MS_BASELINE, + &frameMS); + break; + } + if (res < OK) { + j = snprintf( + &driver_test_buff[index], + fileSize - index, + "No data! ERROR %08X\n", + res); + index += j; + } else { + for (address = 0; address < + frameMS.node_data_size; + address++) { + if (address % + frameMS.header. + sense_node == 0) { + j = snprintf( + & + driver_test_buff + [index], + fileSize + - + index, + "\n"); + index += j; + } + j = snprintf( + & + driver_test_buff + [index], + fileSize - + index, + "%5d, ", + frameMS. + node_data[ + address]); + index += j; + } + j = snprintf( + &driver_test_buff[index], + fileSize - index, "\n"); + index += j; + } + if (frameMS.node_data != NULL) + kfree(frameMS.node_data); + } + for (addr = 0; addr < 3; addr++) { + switch (addr) { + case 0: + j = snprintf( + &driver_test_buff[index], + fileSize - index, + "SS RAW FRAME =\n"); + index += j; + res |= getSSFrame3(SS_RAW, + &frameSS); + break; + case 2: + j = snprintf( + &driver_test_buff[index], + fileSize - index, + "SS STRENGTH FRAME =\n"); + index += j; + res |= getSSFrame3(SS_STRENGTH, + &frameSS); + break; + case 1: + j = snprintf( + &driver_test_buff[index], + fileSize - index, + "SS BASELINE FRAME =\n"); + index += j; + res |= getSSFrame3(SS_BASELINE, + &frameSS); + break; + } + if (res < OK) { + j = snprintf( + &driver_test_buff[index], + fileSize - index, + "No data! ERROR %08X\n", + res); + index += j; + } else { + for (address = 0; address < + frameSS.header.force_node; + address++) { + j = snprintf( + & + driver_test_buff + [index], + fileSize - + index, + "%d\n", + frameSS. + force_data[ + address]); + + index += j; + } + for (address = 0; address < + frameSS.header.sense_node; + address++) { + j = snprintf( + & + driver_test_buff + [index], + fileSize - + index, + "%d, ", + frameSS. + sense_data[ + address]); + + index += j; + } + j = snprintf( + &driver_test_buff[index], + fileSize - index, "\n"); + index += j; + } + if (frameSS.force_data != NULL) + kfree(frameSS.force_data); + if (frameSS.sense_data != NULL) + kfree(frameSS.sense_data); + } + } + + + logError(1, "%s Reading error info...\n", tag); + j = snprintf(&driver_test_buff[index], fileSize - index, + "4) FW INFO DUMP: "); + index += j; + temp = dumpErrorInfo(readData, ERROR_DUMP_ROW_SIZE * + ERROR_DUMP_COL_SIZE); + /* OR to detect if there are failures also in the + * previous reading of frames and write the correct + * result */ + if (temp < OK) { + logError(1, + "%s Error during dump: ERROR %08X!\n", + tag, + res); + j = snprintf(&driver_test_buff[index], + fileSize - index, "ERROR %08X\n", + temp); + index += j; + } else { + logError(1, "%s DUMP OK!\n", tag); + for (temp = 0; temp < ERROR_DUMP_ROW_SIZE * + ERROR_DUMP_COL_SIZE; temp++) { + if (temp % ERROR_DUMP_COL_SIZE == 0) { + j = snprintf( + &driver_test_buff[index], + fileSize - index, + "\n%2d - ", + temp / + ERROR_DUMP_COL_SIZE); + index += j; + } + j = snprintf(&driver_test_buff[index], + fileSize - index, "%02X ", + readData[temp]); + index += j; + } + } + res |= temp; + +END_DIAGNOSTIC: + if (res < OK) { + j = snprintf(&driver_test_buff[index], + fileSize - index, + "\nRESULT = FAIL\n"); + index += j; + } else { + j = snprintf(&driver_test_buff[index], + fileSize - index, + "\nRESULT = FINISHED\n"); + index += j; + } + /* the sting is already terminated with the null char by + * snprintf */ + limit = index; + printed = 0; + goto ERROR; + break; +#ifdef I2C_INTERFACE + case CMD_CHANGE_SAD: + res = changeSAD(cmd[1]); + break; +#endif + + default: + logError(1, "%s COMMAND ID NOT VALID!!!\n", tag); + res = ERROR_OP_NOT_ALLOW; + break; + } + + /*res2 = fts_enableInterrupt(); + * the interrupt was disabled on purpose in this node because it + * can be used for testing procedure and between one step and + * another the interrupt wan to be kept disabled + * if (res2 < 0) { + * logError(0, "%s stm_driver_test_show: ERROR %08X\n", + *tag, (res2 | ERROR_ENABLE_INTER)); + * }*/ + } else { + logError(1, + "%s NO COMMAND SPECIFIED!!! do: 'echo [cmd_code] [args] > stm_fts_cmd' before looking for result!\n", + tag); + res = ERROR_OP_NOT_ALLOW; + } + +END: /* here start the reporting phase, assembling the data to send in the + * file node */ + if (driver_test_buff != NULL) { + logError(1, + "%s Consecutive echo on the file node, free the buffer with the previous result\n", + tag); + kfree(driver_test_buff); + } + + if (byte_call == 0) { + size *= 2; + size += 2; /* add \n and \0 (terminator char) */ + } else { + if (bin_output != 1) { + size *= 2; /* need to code each byte as HEX string */ + size -= 1; /* start byte is just one, the extra + * byte of end byte taken by \n */ + } else + size += 1; /* add \n */ + } + + logError(0, "%s Size = %d\n", tag, size); + driver_test_buff = (u8 *)kzalloc(size, GFP_KERNEL); + logError(0, "%s Finish to allocate memory!\n", tag); + if (driver_test_buff == NULL) { + logError(0, + "%s Unable to allocate driver_test_buff! ERROR %08X\n", + tag, + ERROR_ALLOC); + goto ERROR; + } + + if (byte_call == 0) { + index = 0; + snprintf(&driver_test_buff[index], 3, "{ "); + index += 2; + snprintf(&driver_test_buff[index], 9, "%08X", res); + + index += 8; + if (res >= OK) { + /*all the other cases are already fine printing only the + * res.*/ + switch (funcToTest[0]) { + case CMD_VERSION: + case CMD_READ: + case CMD_WRITEREAD: + case CMD_WRITETHENWRITEREAD: + case CMD_WRITEREADU8UX: + case CMD_WRITEU8UXTHENWRITEREADU8UX: + case CMD_READCONFIG: + case CMD_POLLFOREVENT: + /* logError(0, "%s Data = ", tag); */ + if (mess.dummy == 1) + j = 1; + else + j = 0; + for (; j < byteToRead + mess.dummy; j++) { + /* logError(0, "%02X ", readData[j]); */ + snprintf(&driver_test_buff[index], 3, + "%02X", readData[j]); + /* this approach is much more faster */ + index += 2; + } + /* logError(0, "\n"); */ + break; + case CMD_GETFWFILE: + case CMD_GETLIMITSFILE: + logError(0, "%s Start To parse!\n", tag); + for (j = 0; j < fileSize; j++) { + /* logError(0, "%02X ", readData[j]); */ + snprintf(&driver_test_buff[index], 3, + "%02X", readData[j]); + index += 2; + } + logError(0, "%s Finish to parse!\n", tag); + break; + + case CMD_GETFORCELEN: + case CMD_GETSENSELEN: + snprintf(&driver_test_buff[index], 3, "%02X", + (u8)temp); + index += 2; + + break; + + + case CMD_GETMSFRAME: + snprintf(&driver_test_buff[index], 3, "%02X", + (u8)frameMS.header.force_node); + index += 2; + + snprintf(&driver_test_buff[index], 3, "%02X", + (u8)frameMS.header.sense_node); + index += 2; + + for (j = 0; j < frameMS.node_data_size; j++) { + snprintf(&driver_test_buff[index], 5, + "%02X%02X", + (frameMS.node_data[j] & + 0xFF00) >> 8, + frameMS.node_data[j] & + 0xFF); + index += 4; + } + + kfree(frameMS.node_data); + break; + + case CMD_GETSSFRAME: + snprintf(&driver_test_buff[index], 3, "%02X", + (u8)frameSS.header.force_node); + index += 2; + + snprintf(&driver_test_buff[index], 3, "%02X", + (u8)frameSS.header.sense_node); + index += 2; + /* Copying self raw data Force */ + for (j = 0; j < frameSS.header.force_node; + j++) { + snprintf(&driver_test_buff[index], 5, + "%02X%02X", + (frameSS.force_data[j] & + 0xFF00) >> 8, + frameSS.force_data[j] & + 0xFF); + index += 4; + } + + + /* Copying self raw data Sense */ + for (j = 0; j < frameSS.header.sense_node; + j++) { + snprintf(&driver_test_buff[index], 5, + "%02X%02X", + (frameSS.sense_data[j] & + 0xFF00) >> 8, + frameSS.sense_data[j] & + 0xFF); + index += 4; + } + + kfree(frameSS.force_data); + kfree(frameSS.sense_data); + break; + + case CMD_GETSYNCFRAME: + snprintf(&driver_test_buff[index], 3, "%02X", + (u8)frameMS.header.force_node); + index += 2; + + snprintf(&driver_test_buff[index], 3, "%02X", + (u8)frameMS.header.sense_node); + index += 2; + + snprintf(&driver_test_buff[index], 3, "%02X", + (u8)frameSS.header.force_node); + index += 2; + + snprintf(&driver_test_buff[index], 3, "%02X", + (u8)frameSS.header.sense_node); + index += 2; + + /* Copying mutual data */ + for (j = 0; j < frameMS.node_data_size; j++) { + snprintf(&driver_test_buff[index], 5, + "%02X%02X", + (frameMS.node_data[j] & + 0xFF00) >> 8, + frameMS.node_data[j] & + 0xFF); + index += 4; + } + + /* Copying self data Force */ + for (j = 0; j < frameSS.header.force_node; + j++) { + snprintf(&driver_test_buff[index], 5, + "%02X%02X", + (frameSS.force_data[j] & + 0xFF00) >> 8, + frameSS.force_data[j] & + 0xFF); + index += 4; + } + + + /* Copying self data Sense */ + for (j = 0; j < frameSS.header.sense_node; + j++) { + snprintf(&driver_test_buff[index], 5, + "%02X%02X", + (frameSS.sense_data[j] & + 0xFF00) >> 8, + frameSS.sense_data[j] & + 0xFF); + index += 4; + } + + kfree(frameMS.node_data); + kfree(frameSS.force_data); + kfree(frameSS.sense_data); + break; + + case CMD_READMSCOMPDATA: + snprintf(&driver_test_buff[index], 3, "%02X", + (u8)compData.header.type); + index += 2; + + snprintf(&driver_test_buff[index], 3, "%02X", + (u8)compData.header.force_node); + index += 2; + + snprintf(&driver_test_buff[index], 3, "%02X", + (u8)compData.header.sense_node); + index += 2; + + /* Cpying CX1 value */ + snprintf(&driver_test_buff[index], 3, "%02X", + compData.cx1 & 0xFF); + index += 2; + + /* Copying CX2 values */ + for (j = 0; j < compData.node_data_size; j++) { + snprintf(&driver_test_buff[index], 3, + "%02X", compData.node_data[j] & + 0xFF); + index += 2; + } + + kfree(compData.node_data); + break; + + case CMD_READSSCOMPDATA: + snprintf(&driver_test_buff[index], 3, "%02X", + (u8)comData.header.type); + index += 2; + + snprintf(&driver_test_buff[index], 3, "%02X", + comData.header.force_node); + index += 2; + + + snprintf(&driver_test_buff[index], 3, "%02X", + comData.header.sense_node); + index += 2; + + + snprintf(&driver_test_buff[index], 3, "%02X", + comData.f_ix1 & 0xFF); + index += 2; + + + snprintf(&driver_test_buff[index], 3, "%02X", + comData.s_ix1 & 0xFF); + index += 2; + + + snprintf(&driver_test_buff[index], 3, "%02X", + comData.f_cx1 & 0xFF); + index += 2; + + + snprintf(&driver_test_buff[index], 3, "%02X", + comData.s_cx1 & 0xFF); + index += 2; + + snprintf(&driver_test_buff[index], 3, "%02X", + comData.f_ix0 & 0xFF); + index += 2; + + + snprintf(&driver_test_buff[index], 3, "%02X", + comData.s_ix0 & 0xFF); + index += 2; + + + /* Copying IX2 Force */ + for (j = 0; j < comData.header.force_node; + j++) { + snprintf(&driver_test_buff[index], 3, + "%02X", comData.ix2_fm[j] & + 0xFF); + index += 2; + } + + /* Copying IX2 Sense */ + for (j = 0; j < comData.header.sense_node; + j++) { + snprintf(&driver_test_buff[index], 3, + "%02X", comData.ix2_sn[j] & + 0xFF); + index += 2; + } + + /* Copying CX2 Force */ + for (j = 0; j < comData.header.force_node; + j++) { + snprintf(&driver_test_buff[index], 3, + "%02X", comData.cx2_fm[j] & + 0xFF); + + index += 2; + } + + /* Copying CX2 Sense */ + for (j = 0; j < comData.header.sense_node; + j++) { + snprintf(&driver_test_buff[index], 3, + "%02X", comData.cx2_sn[j] & + 0xFF); + index += 2; + } + + kfree(comData.ix2_fm); + kfree(comData.ix2_sn); + kfree(comData.cx2_fm); + kfree(comData.cx2_sn); + break; + + + + case CMD_READTOTMSCOMPDATA: + snprintf(&driver_test_buff[index], 3, "%02X", + (u8)totCompData.header.type); + index += 2; + + snprintf(&driver_test_buff[index], 3, "%02X", + (u8)totCompData.header.force_node); + index += 2; + + + snprintf(&driver_test_buff[index], 3, "%02X", + (u8)totCompData.header.sense_node); + + index += 2; + + /* Copying TOT CX values */ + for (j = 0; j < totCompData.node_data_size; + j++) { + snprintf(&driver_test_buff[index], 5, + "%02X%02X", + (totCompData.node_data[j] & + 0xFF00) >> 8, + totCompData.node_data[j] & + 0xFF); + index += 4; + } + + kfree(totCompData.node_data); + break; + + case CMD_READTOTSSCOMPDATA: + snprintf(&driver_test_buff[index], 3, "%02X", + (u8)totComData.header.type); + index += 2; + + snprintf(&driver_test_buff[index], 3, "%02X", + totComData.header.force_node); + index += 2; + + snprintf(&driver_test_buff[index], 3, "%02X", + totComData.header.sense_node); + index += 2; + + /* Copying TOT IX Force */ + for (j = 0; j < totComData.header.force_node; + j++) { + snprintf(&driver_test_buff[index], 5, + "%02X%02X", + (totComData.ix_fm[j] & + 0xFF00) >> 8, + totComData.ix_fm[j] & + 0xFF); + index += 4; + } + + /* Copying TOT IX Sense */ + for (j = 0; j < totComData.header.sense_node; + j++) { + snprintf(&driver_test_buff[index], 5, + "%02X%02X", + (totComData.ix_sn[j] & + 0xFF00) >> 8, + totComData.ix_sn[j] & + 0xFF); + index += 4; + } + + /* Copying TOT CX Force */ + for (j = 0; j < totComData.header.force_node; + j++) { + snprintf(&driver_test_buff[index], 5, + "%02X%02X", + (totComData.cx_fm[j] & + 0xFF00) >> 8, + totComData.cx_fm[j] & + 0xFF); + + + index += 4; + } + + /* Copying CX2 Sense */ + for (j = 0; j < totComData.header.sense_node; + j++) { + snprintf(&driver_test_buff[index], 5, + "%02X%02X", + (totComData.cx_sn[j] & + 0xFF00) >> 8, + totComData.cx_sn[j] & + 0xFF); + index += 4; + } + + kfree(totComData.ix_fm); + kfree(totComData.ix_sn); + kfree(totComData.cx_fm); + kfree(totComData.cx_sn); + break; + + case CMD_GETFWVER: + for (j = 0; j < EXTERNAL_RELEASE_INFO_SIZE; + j++) { + snprintf(&driver_test_buff[index], 3, + "%02X", + systemInfo.u8_releaseInfo[j]); + index += 2; + } + break; + + case CMD_READCOMPDATAHEAD: + snprintf(&driver_test_buff[index], 3, "%02X", + dataHead.type); + index += 2; + break; + + + default: + break; + } + } + + snprintf(&driver_test_buff[index], 4, " }\n"); + limit = size - 1;/* avoid to print \0 in the shell */ + printed = 0; + } else { + /* start byte */ + driver_test_buff[index++] = MESSAGE_START_BYTE; + if (bin_output == 1) { + /* msg_size */ + driver_test_buff[index++] = (size & 0xFF00) >> 8; + driver_test_buff[index++] = (size & 0x00FF); + /* counter id */ + driver_test_buff[index++] = (mess.counter & 0xFF00) >> + 8; + driver_test_buff[index++] = (mess.counter & 0x00FF); + /* action */ + driver_test_buff[index++] = (mess.action & 0xFF00) >> 8; + driver_test_buff[index++] = (mess.action & 0x00FF); + /* error */ + driver_test_buff[index++] = (res & 0xFF00) >> 8; + driver_test_buff[index++] = (res & 0x00FF); + } else { + if (funcToTest[0] == CMD_GETLIMITSFILE_BYTE || + funcToTest[0] == CMD_GETFWFILE_BYTE) + snprintf(&driver_test_buff[index], 5, + "%02X%02X", (((fileSize + 3) / 4) & + 0xFF00) >> + 8, ((fileSize + 3) / 4) & 0x00FF); + else + snprintf(&driver_test_buff[index], 5, + "%02X%02X", (size & 0xFF00) >> 8, + size & 0xFF); + index += 4; + index += snprintf(&driver_test_buff[index], 5, "%04X", + (u16)mess.counter); + index += snprintf(&driver_test_buff[index], 5, "%04X", + (u16)mess.action); + index += snprintf(&driver_test_buff[index], 5, + "%02X%02X", (res & 0xFF00) >> 8, res & + 0xFF); + } + + switch (funcToTest[0]) { + case CMD_VERSION_BYTE: + case CMD_READ_BYTE: + case CMD_WRITEREAD_BYTE: + case CMD_WRITETHENWRITEREAD_BYTE: + case CMD_WRITEREADU8UX_BYTE: + case CMD_WRITEU8UXTHENWRITEREADU8UX_BYTE: + if (bin_output == 1) { + if (mess.dummy == 1) + memcpy(&driver_test_buff[index], + &readData[1], byteToRead); + else + memcpy(&driver_test_buff[index], + readData, byteToRead); + index += byteToRead; + } else { + j = mess.dummy; + for (; j < byteToRead + mess.dummy; j++) + index += snprintf( + &driver_test_buff[index], 3, + "%02X", + (u8)readData[j]); + } + break; + + case CMD_GETLIMITSFILE_BYTE: + case CMD_GETFWFILE_BYTE: + if (bin_output == 1) { + /* override the msg_size with dimension in words + * */ + driver_test_buff[1] = (((fileSize + 3) / 4) & + 0xFF00) >> 8; + driver_test_buff[2] = (((fileSize + 3) / 4) & + 0x00FF); + + if (readData != NULL) + memcpy(&driver_test_buff[index], + readData, fileSize); + else + logError(0, + "%s readData = NULL... returning junk data!", + tag); + index += addr; /* in this case the byte to read + * are stored in addr because it + * is a u64 end byte need to be + * inserted at the end of the + * padded memory */ + } else { + /* snprintf(&driver_test_buff[1], 3, "%02X", + * (((fileSize + 3) / 4)&0xFF00) >> 8); */ + /* snprintf(&driver_test_buff[3], 3, "%02X", + * ((fileSize + 3) / 4)&0x00FF); */ + for (j = 0; j < fileSize; j++) + index += snprintf( + &driver_test_buff[index], 3, + "%02X", + (u8)readData[j]); + for (; j < addr; j++) + index += snprintf( + &driver_test_buff[index], 3, + "%02X", 0); /* pad memory + * with 0x00 */ + } + break; + default: + break; + } + + driver_test_buff[index++] = MESSAGE_END_BYTE; + driver_test_buff[index] = '\n'; + /*for(j=0; j + * + * 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 pr_fmt(fmt) "synaptics-CAB: %s: " fmt, __func__ + +#include +#include +#include "synaptics_dsx_i2c.h" +#include "synaptics_dsx_control_access_block.h" + +#define cab_debug pr_debug + +static struct touch_control_access_block *control_access_block; + +struct touch_control_access_block *control_access_block_get(void) { + return control_access_block; +} +EXPORT_SYMBOL_GPL(control_access_block_get); + +#define GROUP_NAME(grp) (grp == SYN_DSX_STATUS ? "STATUS" : \ + (grp == SYN_DSX_DATA ? "DATA" : \ + (grp == SYN_DSX_MODE ? "MODE" : \ + (grp == SYN_DSX_WAKE ? "WAKE" : \ + (grp == SYN_DSX_SLEEP ? "SLEEP" : \ + (grp == SYN_DSX_RESET ? "RESET" : "CONFIG")))))) + +#define DIR_NAME(grp) (grp & MODE_WO ? "WO" : "RO") + +#define SPRINTF_PAGE(fmt, args...) {\ + blen += scnprintf(buf + blen, PAGE_SIZE - blen, fmt, ##args);\ + } + +static ssize_t control_access_block_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i, j, k, m; + ssize_t blen = 0; + struct touch_control_access_block *cab = control_access_block_get(); + + if (cab->do_sync) + SPRINTF_PAGE("has been changed, "); + SPRINTF_PAGE("has %d groups\n", cab->grp_num); + + for (i = 0; i < cab->grp_num; i++) { + struct grp_info *ginfo = cab->grp_ptr[i]; + /* skip empty slots */ + if (!ginfo) + continue; + SPRINTF_PAGE("group [%s] access %s has %d data blocks\n", + GROUP_NAME(ginfo->group), + DIR_NAME(ginfo->group), + ginfo->data_blk_cnt); + for (m = 0; m < ginfo->data_blk_cnt; m++) { + SPRINTF_PAGE("\tdata block %d:\n", m); + if (ginfo->group & MODE_WO) { + struct op_write *wo = ginfo->wo_data[m]; + SPRINTF_PAGE("\t\tpage:%d, addr:0x%X, blk_sz:%d, blk_cnt:%d, mask:0x%x\n", + wo->paddr >> 8, wo->paddr & 0xff, + wo->blk_sz, wo->blk_cnt, wo->mask); + SPRINTF_PAGE("\t\tdata: "); + for (j = 0; j < wo->blk_cnt; j++) { + unsigned char *data = &wo->data[j]; + SPRINTF_PAGE("[%d] ", j); + for (k = 0; k < wo->blk_sz; k++) + SPRINTF_PAGE("0x%X ", *(data + k)); + SPRINTF_PAGE("\n"); + } + } else { + struct op_read *ro = ginfo->ro_data[m]; + SPRINTF_PAGE("\t\tpage:%d, addr:0x%X, blk_sz:%d, blk_cnt:%d\n", + ro->paddr >> 8, ro->paddr & 0xff, + ro->blk_sz, ro->blk_cnt); + } + } + } + + cab->do_sync = false; + cab_debug("clear do_sync flag\n"); + + return blen; +} + +static struct op_write *alloc_op_write(unsigned short paddr, + unsigned char blk_sz, unsigned char blk_cnt, + unsigned char mask, unsigned char *data) +{ + int i; + struct op_write *wo; + wo = kzalloc(sizeof(struct op_write) + blk_sz, GFP_KERNEL); + if (!wo) + return NULL; + wo->paddr = paddr; + wo->blk_sz = blk_sz; + wo->blk_cnt = blk_cnt; + wo->mask = mask; + for (i = 0; i < blk_sz; i++) + wo->data[i] = *data++; + cab_debug("added WO block: addr={%X}, sz=%u, cnt=%u\n", paddr, blk_sz, blk_cnt); + return wo; +} + +static struct op_read *alloc_op_read(unsigned short paddr, + unsigned char blk_sz, unsigned char blk_cnt) +{ + struct op_read *ro; + ro = kzalloc(sizeof(struct op_read), GFP_KERNEL); + if (!ro) + return NULL; + ro->paddr = paddr; + ro->blk_sz = blk_sz; + ro->blk_cnt = blk_cnt; + cab_debug("added RO block: addr={%X}, sz=%u, cnt=%u\n", paddr, blk_sz, blk_cnt); + return ro; +} + +/* create a new group and add one or more entities */ +static struct grp_info *alloc_grp_info(unsigned char group, + unsigned char data_blk_cnt, ...) +{ + int i; + va_list ap; + struct grp_info *gi; + gi = kzalloc(sizeof(struct grp_info), GFP_KERNEL); + if (!gi) + return NULL; + gi->group = group; + gi->data_blk_cnt = data_blk_cnt; + va_start(ap, data_blk_cnt); + if (group & MODE_WO) { + struct op_write *data; + gi->wo_data = (struct op_write **)kzalloc( + sizeof(struct op_write *)*data_blk_cnt, + GFP_KERNEL); + if (!gi->wo_data) + goto alloc_failure; + for (i = 0; i < data_blk_cnt; i++) { + data = va_arg(ap, struct op_write *); + gi->wo_data[i] = data; + } + } else { + struct op_read *data; + gi->ro_data = (struct op_read **)kzalloc( + sizeof(struct op_read *)*data_blk_cnt, + GFP_KERNEL); + if (!gi->ro_data) + goto alloc_failure; + for (i = 0; i < data_blk_cnt; i++) { + data = va_arg(ap, struct op_read *); + gi->ro_data[i] = data; + } + } + goto success; +alloc_failure: + gi->data_blk_cnt = 0; +success: + cab_debug("created group [%s] access %s has %d data block(s)\n", + GROUP_NAME(gi->group), DIR_NAME(gi->group), gi->data_blk_cnt); + va_end(ap); + return gi; +} + +/* add one or more entities to existing group */ +static int modify_grp_info(struct grp_info *gi, + unsigned char data_blk_cnt, ...) +{ + int i, retval = 0; + int new_max = gi->data_blk_cnt + data_blk_cnt; + va_list ap; + + va_start(ap, data_blk_cnt); + if (gi->group & MODE_WO) { + struct op_write **realloc, *append; + realloc = (struct op_write **)kzalloc(sizeof(struct op_write *)* + (gi->data_blk_cnt + data_blk_cnt), + GFP_KERNEL); + if (!realloc) { + retval = -ENOMEM; + goto alloc_failure; + } + for (i = 0; i < new_max; i++) { + if (i < gi->data_blk_cnt) { + realloc[i] = gi->wo_data[i]; + }else { + append = va_arg(ap, struct op_write *); + realloc[i] = append; + } + } + kfree(gi->wo_data); + gi->wo_data = realloc; + } else { + struct op_read **realloc, *append; + realloc = (struct op_read **)kzalloc(sizeof(struct op_read *)* + (gi->data_blk_cnt + data_blk_cnt), + GFP_KERNEL); + if (!realloc) { + retval = -ENOMEM; + goto alloc_failure; + } + for (i = 0; i < new_max; i++) { + if (i < gi->data_blk_cnt) { + realloc[i] = gi->ro_data[i]; + }else { + append = va_arg(ap, struct op_read *); + realloc[i] = append; + } + } + kfree(gi->ro_data); + gi->ro_data = realloc; + } + gi->data_blk_cnt += data_blk_cnt; + cab_debug("modified group:[%s] access:%s has %d data blocks\n", + GROUP_NAME(gi->group), DIR_NAME(gi->group), gi->data_blk_cnt); +alloc_failure: + va_end(ap); + return retval; +} + +void control_access_block_zap(unsigned char gid) +{ + struct touch_control_access_block *cab = control_access_block_get(); + struct grp_info **grp_ptr = NULL; + int i, idx = 0; + + for (i = 0; i < cab->grp_num; i++) { + if (cab->grp_ptr[i] && cab->grp_ptr[i]->group == gid) { + idx = i; + grp_ptr = &cab->grp_ptr[idx]; + break; + } + } + + if (!grp_ptr) + return; + + /* clean up data and make slot available */ + if ((*grp_ptr)->group & MODE_WO) { + struct op_write *data; + for (i = 0; i < (*grp_ptr)->data_blk_cnt; i++) { + data = (*grp_ptr)->wo_data[i]; + kfree(data); + } + kfree((*grp_ptr)->wo_data); + } else { + struct op_read *data; + for (i = 0; i < (*grp_ptr)->data_blk_cnt; i++) { + data = (*grp_ptr)->ro_data[i]; + kfree(data); + } + kfree((*grp_ptr)->ro_data); + } + + kfree(*grp_ptr); + *grp_ptr = NULL; + + cab_debug("group [%s] idx %d removed %d record(s)\n", GROUP_NAME(gid), idx, i); +} + +static struct grp_info **find_group(unsigned char gid) +{ + struct touch_control_access_block *cab = control_access_block_get(); + struct grp_info **grp_ptr = NULL; + int i; + + for (i = 0; i < cab->grp_num; i++) { + if (cab->grp_ptr[i] && cab->grp_ptr[i]->group && + cab->grp_ptr[i]->group != gid) + continue; + /* match or empty slot */ + grp_ptr = &cab->grp_ptr[i]; + break; + } + cab_debug("idx %d %s\n", i, *grp_ptr ? "existing" : "new"); + return grp_ptr; +} + +static int add_tail(struct grp_info **grp, unsigned char gid, void *io_data) +{ + int retval = 0; + + if (!*grp) { + struct grp_info *new; + + new = alloc_grp_info(gid, 1, (void *)io_data); + if (!new) { + retval = -ENOMEM; + goto alloc_failure; + } + *grp = new; + cab_debug("created new %s group\n", DIR_NAME((*grp)->group)); + } else { + modify_grp_info(*grp, 1, (void *)io_data); + cab_debug("added %s block\n", DIR_NAME((*grp)->group)); + } +alloc_failure: + return retval; +} + +int control_access_block_update_ro( + unsigned char gid, + unsigned short paddr, + unsigned char sz, + unsigned char cnt) +{ + struct op_read *ro = alloc_op_read(paddr, sz, cnt); + struct grp_info **grp = find_group(gid); + return !ro ? -1 : add_tail(grp, gid, (void *)ro); +} + +int control_access_block_update_wo( + unsigned char gid, + unsigned short paddr, + unsigned char sz, + unsigned char mask, + unsigned char *data) +{ + struct op_write *wo = alloc_op_write(paddr, sz, 1, mask, data); + struct grp_info **grp = find_group(gid); + return !wo ? -1 : add_tail(grp, gid, (void *)wo); +} + +static struct device_attribute attrs[] = { + __ATTR(control_access_block, S_IRUSR | S_IRGRP, + control_access_block_show, NULL), +}; + +static int synaptics_control_access_block_init( + struct synaptics_rmi4_data *rmi4_data) +{ + int retval, attr_count; + + control_access_block = kzalloc(sizeof(*control_access_block), GFP_KERNEL); + if (!control_access_block) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc controll access block\n", + __func__); + goto exit; + } + control_access_block->grp_num = GROUP_MAX; + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&rmi4_data->i2c_client->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto exit_remove_attrs; + } + } + + return 0; + +exit_remove_attrs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&rmi4_data->i2c_client->dev.kobj, + &attrs[attr_count].attr); + } +exit: + return 0; +} + +static struct completion remove_complete; + +static void synaptics_control_access_block_remove( + struct synaptics_rmi4_data *rmi4_data) +{ + int i, m, attr_count; + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&rmi4_data->i2c_client->dev.kobj, + &attrs[attr_count].attr); + } + + for (i = 0; i < control_access_block->grp_num; i++) { + struct grp_info *ginfo = control_access_block->grp_ptr[i]; + void **ptr_to_free; + + if (ginfo->group & MODE_WO) + ptr_to_free = (void **)ginfo->wo_data; + else + ptr_to_free = (void **)ginfo->ro_data; + + for (m = 0; m < ginfo->data_blk_cnt; m++) { + if (ginfo->group & MODE_WO) + kfree(ginfo->wo_data[m]); + else + kfree(ginfo->ro_data[m]); + } + kfree(ptr_to_free); + kfree(ginfo); + } + + kfree(control_access_block->grp_ptr); + kfree(control_access_block); + control_access_block = NULL; + + complete(&remove_complete); + + return; +} + +static int __init synaptics_control_access_block_module_init(void) +{ + synaptics_rmi4_new_function(RMI_CTRL_ACCESS_BLK, true, + synaptics_control_access_block_init, + synaptics_control_access_block_remove, + NULL, NULL, IC_MODE_UI); + return 0; +} + +static void __exit synaptics_control_access_block_module_exit(void) +{ + init_completion(&remove_complete); + synaptics_rmi4_new_function(RMI_CTRL_ACCESS_BLK, false, + synaptics_control_access_block_init, + synaptics_control_access_block_remove, + NULL, NULL, IC_MODE_UI); + wait_for_completion(&remove_complete); + return; +} + +module_init(synaptics_control_access_block_module_init); +module_exit(synaptics_control_access_block_module_exit); + +MODULE_AUTHOR("Motorola Mobility, Inc."); +MODULE_DESCRIPTION("Synaptics DSX Touch Access Block Module"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(SYNAPTICS_DSX_DRIVER_VERSION); diff --git a/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_control_access_block.h b/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_control_access_block.h new file mode 100644 index 000000000000..a5cc8cafeeb9 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_control_access_block.h @@ -0,0 +1,111 @@ +/* + * Synaptics DSX touchscreen driver extension + * + * Copyright (C) 2015 Motorola Mobility, Inc. + * + * Copyright (C) 2015 Konstantin Makariev + * + * 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 _SYNAPTICS_DSX_CONTROL_ACCESS_BLOCK_H_ +#define _SYNAPTICS_DSX_CONTROL_ACCESS_BLOCK_H_ + +#define MODE_WO (1 << 7) +#define GROUP_MAX 7 + +enum access_groups { + SYN_DSX_STATUS = 1, /* RO:mandatory, read status to clear IRQ */ + SYN_DSX_MODE, /* RO:mandatory, read touch IC mode */ + SYN_DSX_DATA, /* RO:mandatory, read touch data */ + SYN_DSX_WAKE = SYN_DSX_DATA + 1 + MODE_WO, + /* WO:mandatory, command(s) to wake up touch IC */ + SYN_DSX_SLEEP, /* WO:mandatory, command(s) to put touch IC to sleep */ + SYN_DSX_RESET, /* WO:optional, command to reset touch IC */ + SYN_DSX_CONFIG, /* WO:optional, command(s) to apply custom config */ + SYN_DSX_VAL_MAX = 0xff +}; + +struct op_read { + struct { + unsigned short paddr; /* (page << 8) + address */ + unsigned char blk_sz; /* read block size */ + unsigned char blk_cnt; /* read block count */ + }; +}; + +struct op_write { + struct { + unsigned short paddr; /* (page << 8) + address */ + unsigned char blk_sz; /* write block size */ + unsigned char blk_cnt; /* write block count */ + }; + unsigned char mask; /* mask to clear bit(s) before apply data */ + /* only effective when blk_cnt and blk_sz */ + /* both are 1s, otherwise mask is ignored */ + unsigned char data[0]; /* blk_cnt of data chunks of blk_sz each */ +}; + +struct grp_info { + unsigned char group; /* access group; defines type of data */ + unsigned char data_blk_cnt; /* number of data blocks in the group */ + union { + struct op_read **ro_data; + struct op_write **wo_data; + }; +}; + +struct touch_control_access_block { + unsigned char do_sync; /* flag to indicate data block was updated */ + unsigned char grp_num; /* number of groups in data block */ + struct grp_info *grp_ptr[GROUP_MAX]; +}; + +#if defined(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CTRL_ACCESS_BLK) +struct touch_control_access_block *control_access_block_get(void); +int control_access_block_update_ro(unsigned char gid, unsigned short paddr, + unsigned char sz, unsigned char cnt); +int control_access_block_update_wo(unsigned char gid, unsigned short paddr, + unsigned char sz, unsigned char mask, unsigned char *data); +void control_access_block_zap(unsigned char gid); +#else +static inline struct touch_control_access_block *control_access_block_get(void) { + return NULL; +} +static int inline control_access_block_update_ro( + unsigned char gid, unsigned short paddr, + unsigned char sz, unsigned char cnt) { + return -1; +} +static int inline control_access_block_update_wo( + unsigned char gid, unsigned short paddr, + unsigned char sz, unsigned char mask, unsigned char *data) { + return -1; +} +static void inline control_access_block_zap(unsigned char gid) { +} +#endif + +#define control_access_block_update_status(paddr, size) \ + control_access_block_update_ro(SYN_DSX_STATUS, paddr, size, 1) +#define control_access_block_update_mode(paddr, size) \ + control_access_block_update_ro(SYN_DSX_MODE, paddr, size, 1) +#define control_access_block_update_data(paddr, size) \ + control_access_block_update_ro(SYN_DSX_DATA, paddr, size, 1) +#define control_access_block_update_data_mt(paddr, size, mt) \ + control_access_block_update_ro(SYN_DSX_DATA, paddr, size, mt) +#define control_access_block_update_reset(paddr, size, data) \ + control_access_block_update_wo(SYN_DSX_RESET, paddr, size, 0, data) +#define control_access_block_update_wake(paddr, size, data) \ + control_access_block_update_wo(SYN_DSX_WAKE, paddr, size, 0x7, data) +#define control_access_block_update_sleep(paddr, size, data) \ + control_access_block_update_wo(SYN_DSX_SLEEP, paddr, size, 0x7, data) + +#endif diff --git a/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_dropbox.c b/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_dropbox.c new file mode 100644 index 000000000000..cb2e2f9070ad --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_dropbox.c @@ -0,0 +1,40 @@ +/* Copyright (c) 2014 - 2015, Motorola Mobility LLC. 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 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. +*/ +#include +#include + +#include "synaptics_dsx_dropbox.h" + +#define DROPBOX_TOUCH_ISSUE "touch_issue" + +static DEFINE_RATELIMIT_STATE(synaptics_dropbox_global_rl, 3600 * HZ, 1); + +void synaptics_dropbox_report_event(char *msg, int count) +{ + char dropbox_entry[256]; + + snprintf(dropbox_entry, sizeof(dropbox_entry), "%s\ncount: %d", + msg, count); + pr_err("%s: touch issue detected[count: %d][%s], reporting dropbox\n", + __func__, count, msg); + dropbox_queue_event_text(DROPBOX_TOUCH_ISSUE, + dropbox_entry, strlen(dropbox_entry)); +} + +void synaptics_dropbox_report_event_ratelimit(char *msg, int count) +{ + struct ratelimit_state *rs = &synaptics_dropbox_global_rl; + + if (!__ratelimit(rs)) + return; + synaptics_dropbox_report_event(msg, count); +} diff --git a/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_dropbox.h b/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_dropbox.h new file mode 100644 index 000000000000..f1514f3ebe30 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_dropbox.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2014 - 2015, Motorola Mobility LLC. 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 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. +*/ +#ifndef SYNAPTICS_DROPBOX_H +#define SYNAPTICS_DROPBOX_H + +#include + +#define SYNAPTICS_DROPBOX_MSG_I2C "Touch I2C communication error detected" +#define SYNAPTICS_DROPBOX_MSG_RESET_TIMEOUT "Touch reset time out detected" + +#if defined(CONFIG_DROPBOX) +void synaptics_dropbox_report_event(char *msg, int count); +void synaptics_dropbox_report_event_ratelimit(char *msg, int count); +#else +static inline void synaptics_dropbox_report_event(char *msg, int count) {} +static inline void synaptics_dropbox_report_event_ratelimit(char *msg, int count) {} +#endif +#endif /* SYNAPTICS_DROPBOX_H */ diff --git a/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_fw_update.c b/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_fw_update.c new file mode 100644 index 000000000000..372aa9ed9397 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_fw_update.c @@ -0,0 +1,4390 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_i2c.h" + +#if 0 +#ifdef pr_debug +#undef pr_debug +#define pr_debug pr_err +#endif +#ifdef dev_dbg +#undef dev_dbg +#define dev_dbg dev_err +#endif +#endif + +#define FORCE_UPDATE false +#define DO_LOCKDOWN false + +#define MAX_IMAGE_NAME_LEN 256 +#define MAX_FIRMWARE_ID_LEN 10 + +#define IMAGE_HEADER_VERSION_05 0x05 +#define IMAGE_HEADER_VERSION_06 0x06 +#define IMAGE_HEADER_VERSION_10 0x10 + +#define IMAGE_AREA_OFFSET 0x100 +#define LOCKDOWN_SIZE 0x50 + +#define V5V6_BOOTLOADER_ID_OFFSET 0 +#define V5V6_CONFIG_ID_SIZE 4 + +#define V5_PROPERTIES_OFFSET 2 +#define V5_BLOCK_SIZE_OFFSET 3 +#define V5_BLOCK_COUNT_OFFSET 5 +#define V5_BLOCK_NUMBER_OFFSET 0 +#define V5_BLOCK_DATA_OFFSET 2 + +#define V6_PROPERTIES_OFFSET 1 +#define V6_BLOCK_SIZE_OFFSET 2 +#define V6_BLOCK_COUNT_OFFSET 3 +#define V6_PROPERTIES_2_OFFSET 4 +#define V6_GUEST_CODE_BLOCK_COUNT_OFFSET 5 +#define V6_BLOCK_NUMBER_OFFSET 0 +#define V6_BLOCK_DATA_OFFSET 1 +#define V6_FLASH_COMMAND_OFFSET 2 +#define V6_FLASH_STATUS_OFFSET 3 + +#define V7_CONFIG_ID_SIZE 32 + +#define V7_FLASH_STATUS_OFFSET 0 +#define V7_PARTITION_ID_OFFSET 1 +#define V7_BLOCK_NUMBER_OFFSET 2 +#define V7_TRANSFER_LENGTH_OFFSET 3 +#define V7_COMMAND_OFFSET 4 +#define V7_PAYLOAD_OFFSET 5 + +#define V7_PARTITION_SUPPORT_BYTES 4 + +#define F35_ERROR_CODE_OFFSET 0 +#define F35_CHUNK_NUM_LSB_OFFSET 0 +#define F35_CHUNK_NUM_MSB_OFFSET 1 +#define F35_CHUNK_DATA_OFFSET 2 +#define F35_CHUNK_COMMAND_OFFSET 18 + +#define F35_CHUNK_SIZE 16 +#define F35_ERASE_ALL_WAIT_MS 3000 +#define F35_RESET_WAIT_MS 250 + +#define SLEEP_MODE_NORMAL (0x00) +#define SLEEP_MODE_SENSOR_SLEEP (0x01) +#define SLEEP_MODE_RESERVED0 (0x02) +#define SLEEP_MODE_RESERVED1 (0x03) + +#define ENABLE_WAIT_MS (1 * 1000) +#define WRITE_WAIT_MS (3 * 1000) +#define ERASE_WAIT_MS (5 * 1000) + +#define MIN_SLEEP_TIME_US 50 +#define MAX_SLEEP_TIME_US 100 + +#define INT_DISABLE_WAIT_MS 20 +#define ENTER_FLASH_PROG_WAIT_MS 20 + +#define LOGDEV (fwu->dev) +#define SYSFS_KOBJ (&rmi4_data->i2c_client->dev.kobj) + +static int fwu_do_reflash(struct synaptics_rmi4_fwu_handle *fwu); + +static int fwu_recovery_check_status(struct synaptics_rmi4_fwu_handle *fwu); + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static ssize_t fwu_sysfs_show_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t fwu_sysfs_store_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t fwu_sysfs_do_recovery_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_write_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_read_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_config_area_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_image_name_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_image_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_block_size_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_guest_code_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_write_guest_code_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +#endif + +static ssize_t fwu_sysfs_erase_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_force_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +enum f34_version { + F34_V0 = 0, + F34_V1, + F34_V2, +}; + +enum flash_area { + NONE = 0, + UI_FIRMWARE, + UI_CONFIG, +}; + +enum update_mode { + NORMAL = 1, + FORCE = 2, + LOCKDOWN = 8, +}; + +enum config_area { + UI_CONFIG_AREA = 0, + PM_CONFIG_AREA, + BL_CONFIG_AREA, + DP_CONFIG_AREA, + FLASH_CONFIG_AREA, +}; + +enum v7_status { + SUCCESS = 0x00, + DEVICE_NOT_IN_BOOTLOADER_MODE, + INVALID_PARTITION, + INVALID_COMMAND, + INVALID_BLOCK_OFFSET, + INVALID_TRANSFER, + NOT_ERASED, + FLASH_PROGRAMMING_KEY_INCORRECT, + BAD_PARTITION_TABLE, + CHECKSUM_FAILED, + FLASH_HARDWARE_FAILURE = 0x1f, +}; + +enum v7_partition_id { + BOOTLOADER_PARTITION = 0x01, + DEVICE_CONFIG_PARTITION, + FLASH_CONFIG_PARTITION, + MANUFACTURING_BLOCK_PARTITION, + GUEST_SERIALIZATION_PARTITION, + GLOBAL_PARAMETERS_PARTITION, + CORE_CODE_PARTITION, + CORE_CONFIG_PARTITION, + GUEST_CODE_PARTITION, + DISPLAY_CONFIG_PARTITION, +}; + +enum v7_flash_command { + CMD_V7_IDLE = 0x00, + CMD_V7_ENTER_BL, + CMD_V7_READ, + CMD_V7_WRITE, + CMD_V7_ERASE, + CMD_V7_ERASE_AP, + CMD_V7_SENSOR_ID, +}; + +enum v5v6_flash_command { + CMD_V5V6_IDLE = 0x0, + CMD_V5V6_WRITE_FW = 0x2, + CMD_V5V6_ERASE_ALL = 0x3, + CMD_V5V6_WRITE_LOCKDOWN = 0x4, + CMD_V5V6_READ_CONFIG = 0x5, + CMD_V5V6_WRITE_CONFIG = 0x6, + CMD_V5V6_ERASE_UI_CONFIG = 0x7, + CMD_V5V6_ERASE_BL_CONFIG = 0x9, + CMD_V5V6_ERASE_DISP_CONFIG = 0xa, + CMD_V5V6_ERASE_GUEST_CODE = 0xb, + CMD_V5V6_WRITE_GUEST_CODE = 0xc, + CMD_V5V6_ENABLE_FLASH_PROG = 0xf, +}; + +enum flash_command { + CMD_IDLE = 0, + CMD_WRITE_FW, + CMD_WRITE_CONFIG, + CMD_WRITE_LOCKDOWN, + CMD_WRITE_GUEST_CODE, + CMD_READ_CONFIG, + CMD_ERASE_ALL, + CMD_ERASE_UI_FIRMWARE, + CMD_ERASE_UI_CONFIG, + CMD_ERASE_BL_CONFIG, + CMD_ERASE_DISP_CONFIG, + CMD_ERASE_FLASH_CONFIG, + CMD_ERASE_GUEST_CODE, + CMD_ENABLE_FLASH_PROG, +}; + +enum f35_flash_command { + CMD_F35_IDLE = 0x0, + CMD_F35_RESERVED = 0x1, + CMD_F35_WRITE_CHUNK = 0x2, + CMD_F35_ERASE_ALL = 0x3, + CMD_F35_RESET = 0x10, +}; + +enum container_id { + TOP_LEVEL_CONTAINER = 0, + UI_CONTAINER, + UI_CONFIG_CONTAINER, + BL_CONTAINER, + BL_IMAGE_CONTAINER, + BL_CONFIG_CONTAINER, + BL_LOCKDOWN_INFO_CONTAINER, + PERMANENT_CONFIG_CONTAINER, + GUEST_CODE_CONTAINER, + BL_PROTOCOL_DESCRIPTOR_CONTAINER, + UI_PROTOCOL_DESCRIPTOR_CONTAINER, + RMI_SELF_DISCOVERY_CONTAINER, + RMI_PAGE_CONTENT_CONTAINER, + GENERAL_INFORMATION_CONTAINER, + DEVICE_CONFIG_CONTAINER, + FLASH_CONFIG_CONTAINER, + GUEST_SERIALIZATION_CONTAINER, + GLOBAL_PARAMETERS_CONTAINER, + CORE_CODE_CONTAINER, + CORE_CONFIG_CONTAINER, + DISPLAY_CONFIG_CONTAINER, +}; + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static struct bin_attribute dev_attr_data = { + .attr = { + .name = "data", + .mode = (S_IRUGO | S_IWUSR | S_IWGRP), + }, + .size = 0, + .read = fwu_sysfs_show_image, + .write = fwu_sysfs_store_image, +}; +#endif + +static struct device_attribute attrs[] = { +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI + __ATTR(dorecovery, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_do_recovery_store), + __ATTR(writeconfig, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_write_config_store), + __ATTR(readconfig, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_read_config_store), + __ATTR(configarea, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_config_area_store), + __ATTR(imagename, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_image_name_store), + __ATTR(imagesize, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_image_size_store), + __ATTR(blocksize, S_IRUGO, + fwu_sysfs_block_size_show, + synaptics_rmi4_store_error), + __ATTR(fwblockcount, S_IRUGO, + fwu_sysfs_firmware_block_count_show, + synaptics_rmi4_store_error), + __ATTR(configblockcount, S_IRUGO, + fwu_sysfs_configuration_block_count_show, + synaptics_rmi4_store_error), + __ATTR(dispconfigblockcount, S_IRUGO, + fwu_sysfs_disp_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(permconfigblockcount, S_IRUGO, + fwu_sysfs_perm_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(blconfigblockcount, S_IRUGO, + fwu_sysfs_bl_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(guestcodeblockcount, S_IRUGO, + fwu_sysfs_guest_code_block_count_show, + synaptics_rmi4_store_error), + __ATTR(writeguestcode, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_write_guest_code_store), +#endif + __ATTR(doreflash, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_do_reflash_store), + __ATTR(forcereflash, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_force_reflash_store), +}; + +#include + +static struct device_attribute erase_attr[] = { + __ATTR(erase_all, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_erase_store), +}; + +static inline void sema_clear(struct semaphore *sem) +{ + do { + } while (!down_trylock(sem)); +} + +static unsigned int le_to_uint(const unsigned char *ptr) +{ + return (unsigned int)ptr[0] + + (unsigned int)ptr[1] * 0x100 + + (unsigned int)ptr[2] * 0x10000 + + (unsigned int)ptr[3] * 0x1000000; +} + +static int fwu_allocate_read_config_buf( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned int count) +{ + if (count > fwu->read_config_buf_size) { + kfree(fwu->read_config_buf); + fwu->read_config_buf = kzalloc(count, GFP_KERNEL); + if (!fwu->read_config_buf) { + dev_err(LOGDEV, + "%s: Failed to alloc mem for fwu->read_config_buf\n", + __func__); + fwu->read_config_buf_size = 0; + return -ENOMEM; + } + fwu->read_config_buf_size = count; + } + return 0; +} + +static void fwu_compare_partition_tables(struct synaptics_rmi4_fwu_handle *fwu) +{ + if (fwu->phyaddr.ui_firmware != fwu->img.phyaddr.ui_firmware) { + fwu->new_partition_table = true; + return; + } + + if (fwu->phyaddr.ui_config != fwu->img.phyaddr.ui_config) { + fwu->new_partition_table = true; + return; + } + + if (fwu->flash_properties.has_disp_config) { + if (fwu->phyaddr.dp_config != fwu->img.phyaddr.dp_config) { + fwu->new_partition_table = true; + return; + } + } + + if (fwu->flash_properties.has_disp_config) { + if (fwu->phyaddr.dp_config != fwu->img.phyaddr.dp_config) { + fwu->new_partition_table = true; + return; + } + } + + if (fwu->has_guest_code) { + if (fwu->phyaddr.guest_code != fwu->img.phyaddr.guest_code) { + fwu->new_partition_table = true; + return; + } + } + + dev_dbg(LOGDEV, "%s: same partition table\n", __func__); + fwu->new_partition_table = false; + return; +} + +static void fwu_parse_partition_table( + struct synaptics_rmi4_fwu_handle *fwu, + const unsigned char *partition_table, + struct block_count *blkcount, + struct physical_address *phyaddr) +{ + unsigned char ii; + unsigned char index; + unsigned char offset; + unsigned short partition_length; + unsigned short physical_address; + struct partition_table *ptable; + + for (ii = 0; ii < fwu->partitions; ii++) { + index = ii * 8 + 2; + ptable = (struct partition_table *)&partition_table[index]; + partition_length = ptable->partition_length_15_8 << 8 | + ptable->partition_length_7_0; + physical_address = ptable->start_physical_address_15_8 << 8 | + ptable->start_physical_address_7_0; + dev_dbg(LOGDEV, + "%s: Partition entry %d:\n", + __func__, ii); + for (offset = 0; offset < 8; offset++) { + dev_dbg(LOGDEV, + "%s: 0x%02x\n", + __func__, + partition_table[index + offset]); + } + switch (ptable->partition_id) { + case CORE_CODE_PARTITION: + blkcount->ui_firmware = partition_length; + phyaddr->ui_firmware = physical_address; + dev_dbg(LOGDEV, + "%s: Core code block count: %d\n", + __func__, blkcount->ui_firmware); + break; + case CORE_CONFIG_PARTITION: + blkcount->ui_config = partition_length; + phyaddr->ui_config = physical_address; + dev_dbg(LOGDEV, + "%s: Core config block count: %d\n", + __func__, blkcount->ui_config); + break; + case DISPLAY_CONFIG_PARTITION: + blkcount->dp_config = partition_length; + phyaddr->dp_config = physical_address; + dev_dbg(LOGDEV, + "%s: Display config block count: %d\n", + __func__, blkcount->dp_config); + break; + case FLASH_CONFIG_PARTITION: + blkcount->fl_config = partition_length; + dev_dbg(LOGDEV, + "%s: Flash config block count: %d\n", + __func__, blkcount->fl_config); + break; + case GUEST_CODE_PARTITION: + blkcount->guest_code = partition_length; + phyaddr->guest_code = physical_address; + dev_dbg(LOGDEV, + "%s: Guest code block count: %d\n", + __func__, blkcount->guest_code); + break; + case GUEST_SERIALIZATION_PARTITION: + blkcount->pm_config = partition_length; + dev_dbg(LOGDEV, + "%s: Guest serialization block count: %d\n", + __func__, blkcount->pm_config); + break; + case GLOBAL_PARAMETERS_PARTITION: + blkcount->bl_config = partition_length; + dev_dbg(LOGDEV, + "%s: Global parameters block count: %d\n", + __func__, blkcount->bl_config); + break; + case DEVICE_CONFIG_PARTITION: + blkcount->lockdown = partition_length; + dev_dbg(LOGDEV, + "%s: Device config block count: %d\n", + __func__, blkcount->lockdown); + break; + }; + } + + return; +} + +static void fwu_parse_image_header_10_bl_container( + struct synaptics_rmi4_fwu_handle *fwu, + const unsigned char *image) +{ + unsigned char ii; + unsigned char num_of_containers; + unsigned int addr; + unsigned int container_id; + unsigned int length; + const unsigned char *content; + struct container_descriptor *descriptor; + + num_of_containers = (fwu->img.bootloader.size - 4) / 4; + + for (ii = 1; ii <= num_of_containers; ii++) { + addr = le_to_uint(fwu->img.bootloader.data + (ii * 4)); + descriptor = (struct container_descriptor *)(image + addr); + container_id = descriptor->container_id[0] | + descriptor->container_id[1] << 8; + content = image + le_to_uint(descriptor->content_address); + length = le_to_uint(descriptor->content_length); + switch (container_id) { + case BL_CONFIG_CONTAINER: + case GLOBAL_PARAMETERS_CONTAINER: + fwu->img.bl_config.data = content; + fwu->img.bl_config.size = length; + break; + case BL_LOCKDOWN_INFO_CONTAINER: + case DEVICE_CONFIG_CONTAINER: + fwu->img.lockdown.data = content; + fwu->img.lockdown.size = length; + break; + default: + break; + }; + } + + return; +} + +static void fwu_parse_image_header_10(struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned char ii; + unsigned char num_of_containers; + unsigned int addr; + unsigned int offset; + unsigned int container_id; + unsigned int length; + const unsigned char *image; + const unsigned char *content; + struct container_descriptor *descriptor; + struct image_header_10 *header; + + image = fwu->image; + header = (struct image_header_10 *)image; + + fwu->img.checksum = le_to_uint(header->checksum); + + /* address of top level container */ + offset = le_to_uint(header->top_level_container_start_addr); + descriptor = (struct container_descriptor *)(image + offset); + + /* address of top level container content */ + offset = le_to_uint(descriptor->content_address); + num_of_containers = le_to_uint(descriptor->content_length) / 4; + + for (ii = 0; ii < num_of_containers; ii++) { + addr = le_to_uint(image + offset); + offset += 4; + descriptor = (struct container_descriptor *)(image + addr); + container_id = descriptor->container_id[0] | + descriptor->container_id[1] << 8; + content = image + le_to_uint(descriptor->content_address); + length = le_to_uint(descriptor->content_length); + switch (container_id) { + case UI_CONTAINER: + case CORE_CODE_CONTAINER: + fwu->img.ui_firmware.data = content; + fwu->img.ui_firmware.size = length; + break; + case UI_CONFIG_CONTAINER: + case CORE_CONFIG_CONTAINER: + fwu->img.ui_config.data = content; + fwu->img.ui_config.size = length; + break; + case BL_CONTAINER: + fwu->img.bl_version = *content; + fwu->img.bootloader.data = content; + fwu->img.bootloader.size = length; + fwu_parse_image_header_10_bl_container(fwu, image); + break; + case GUEST_CODE_CONTAINER: + fwu->img.contains_guest_code = true; + fwu->img.guest_code.data = content; + fwu->img.guest_code.size = length; + break; + case DISPLAY_CONFIG_CONTAINER: + fwu->img.contains_disp_config = true; + fwu->img.dp_config.data = content; + fwu->img.dp_config.size = length; + break; + case FLASH_CONFIG_CONTAINER: + fwu->img.contains_flash_config = true; + fwu->img.fl_config.data = content; + fwu->img.fl_config.size = length; + break; + case GENERAL_INFORMATION_CONTAINER: + fwu->img.contains_firmware_id = true; + fwu->img.firmware_id = le_to_uint(content + 4); + break; + default: + break; + } + } + + return; +} + +static void fwu_parse_image_header_05_06(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + const unsigned char *image; + struct image_header_05_06 *header; + + image = fwu->image; + header = (struct image_header_05_06 *)image; + + fwu->img.checksum = le_to_uint(header->checksum); + + fwu->img.bl_version = header->header_version; + + fwu->img.contains_bootloader = header->options_bootloader; + if (fwu->img.contains_bootloader) + fwu->img.bootloader_size = le_to_uint(header->bootloader_size); + + fwu->img.ui_firmware.size = le_to_uint(header->firmware_size); + if (fwu->img.ui_firmware.size) { + fwu->img.ui_firmware.data = image + IMAGE_AREA_OFFSET; + if (fwu->img.contains_bootloader) + fwu->img.ui_firmware.data += fwu->img.bootloader_size; + } + + if ((fwu->img.bl_version == BL_V6) && header->options_tddi) + fwu->img.ui_firmware.data = image + IMAGE_AREA_OFFSET; + + fwu->img.ui_config.size = le_to_uint(header->config_size); + if (fwu->img.ui_config.size) { + fwu->img.ui_config.data = fwu->img.ui_firmware.data + + fwu->img.ui_firmware.size; + } + + if (fwu->img.contains_bootloader || header->options_tddi) + fwu->img.contains_disp_config = true; + else + fwu->img.contains_disp_config = false; + + if (fwu->img.contains_disp_config) { + fwu->img.disp_config_offset = le_to_uint(header->dsp_cfg_addr); + fwu->img.dp_config.size = le_to_uint(header->dsp_cfg_size); + fwu->img.dp_config.data = image + fwu->img.disp_config_offset; + } else { + retval = secure_memcpy(fwu->img.cstmr_product_id, + sizeof(fwu->img.cstmr_product_id), + header->cstmr_product_id, + sizeof(header->cstmr_product_id), + PRODUCT_ID_SIZE); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to copy custom product ID string\n", + __func__); + } + fwu->img.cstmr_product_id[PRODUCT_ID_SIZE] = 0; + } + + fwu->img.contains_firmware_id = header->options_firmware_id; + if (fwu->img.contains_firmware_id) + fwu->img.firmware_id = le_to_uint(header->firmware_id); + + retval = secure_memcpy(fwu->img.product_id, + sizeof(fwu->img.product_id), + header->product_id, + sizeof(header->product_id), + PRODUCT_ID_SIZE); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to copy product ID string\n", + __func__); + } + fwu->img.product_id[PRODUCT_ID_SIZE] = 0; + + fwu->img.lockdown.size = LOCKDOWN_SIZE; + fwu->img.lockdown.data = image + IMAGE_AREA_OFFSET - LOCKDOWN_SIZE; + + return; +} + +static int fwu_parse_image_info(struct synaptics_rmi4_fwu_handle *fwu) +{ + struct image_header_10 *header; + + header = (struct image_header_10 *)fwu->image; + + memset(&fwu->img, 0x00, sizeof(fwu->img)); + + switch (header->major_header_version) { + case IMAGE_HEADER_VERSION_10: + fwu_parse_image_header_10(fwu); + break; + case IMAGE_HEADER_VERSION_05: + case IMAGE_HEADER_VERSION_06: + fwu_parse_image_header_05_06(fwu); + break; + default: + dev_err(LOGDEV, + "%s: Unsupported image file format (0x%02x)\n", + __func__, header->major_header_version); + return -EINVAL; + } + + if (fwu->bl_version >= BL_V7) { + if (!fwu->img.contains_flash_config) { + dev_err(LOGDEV, + "%s: No flash config found in firmware image\n", + __func__); + return -EINVAL; + } + + fwu_parse_partition_table(fwu, fwu->img.fl_config.data, + &fwu->img.blkcount, &fwu->img.phyaddr); + + fwu_compare_partition_tables(fwu); + } else { + fwu->new_partition_table = false; + } + + return 0; +} + +static int fwu_read_flash_status(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval, partition = -1; + unsigned char status; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->off.flash_status, + &status, + sizeof(status)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read flash status\n", + __func__); + return retval; + } + + dev_dbg(LOGDEV, "%s: Flash status = 0x%02x\n", + __func__, (unsigned int)status); + fwu->in_bl_mode = status >> 7; + + if (fwu->bl_version == BL_V5) + fwu->flash_status = (status >> 4) & MASK_3BIT; + else if (fwu->bl_version == BL_V6) + fwu->flash_status = status & MASK_3BIT; + else + fwu->flash_status = status & MASK_5BIT; + + if (fwu->bl_version <= BL_V6) { + unsigned char command; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->off.flash_cmd, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read flash command\n", + __func__); + return retval; + } + + if (fwu->bl_version == BL_V5) + fwu->command = command & MASK_4BIT; + else if (fwu->bl_version == BL_V6) + fwu->command = command & MASK_6BIT; + else + fwu->command = command; + + } else { + struct f34_v7_data_1_5 data15; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->off.partition_id, + (unsigned char *)&data15, + sizeof(data15)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read data15\n", + __func__); + return retval; + } + + if (fwu->flash_status == BAD_PARTITION_TABLE) + fwu->flash_status = 0x00; + + partition = data15.partition_id; + fwu->command = data15.command; + } + + if (fwu->flash_status != 0x00) { + dev_err(LOGDEV, + "%s: Flash status = %d, part_id = %d, command = 0x%02x\n", + __func__, fwu->flash_status, partition, fwu->command); + } + + return 0; +} + +static int fwu_read_interrupt_status(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char interrupt_status; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + &interrupt_status, + sizeof(interrupt_status)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read intr status\n", + __func__); + return retval; + } + dev_dbg(LOGDEV, + "%s: F01 interrupt status = 0x%02x\n", + __func__, interrupt_status); + return interrupt_status; +} + +static irqreturn_t fwu_irq(int irq, void *data) +{ + struct synaptics_rmi4_fwu_handle *fwu_ptr = data; + up(&fwu_ptr->irq_sema); + return IRQ_HANDLED; +} + +static void fwu_irq_enable( + struct synaptics_rmi4_fwu_handle *fwu, bool enable) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + int retval; + + if (enable) { + if (fwu->irq_enabled) { + dev_warn(LOGDEV, + "%s: irq already enabled\n", __func__); + return; + } + + fwu_read_interrupt_status(fwu); + retval = request_irq(rmi4_data->irq, fwu_irq, + IRQF_TRIGGER_FALLING, "fwu", fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to request irq: %d\n", + __func__, retval); + } + + dev_dbg(LOGDEV, + "enabling F34 IRQ handler\n"); + fwu->irq_enabled = true; + } else { + if (!fwu->irq_enabled) { + dev_warn(LOGDEV, + "%s: irq already disabled\n", __func__); + return; + } + + dev_dbg(LOGDEV, + "disabling F34 IRQ handler\n"); + disable_irq(rmi4_data->irq); + free_irq(rmi4_data->irq, fwu); + fwu->irq_enabled = false; + } + sema_clear(&fwu->irq_sema); +} + +static int fwu_wait_for_idle( + struct synaptics_rmi4_fwu_handle *fwu, int timeout_ms) +{ + int retval; + + retval = down_timeout(&fwu->irq_sema, msecs_to_jiffies(timeout_ms)); + if (retval) { + retval = -ETIMEDOUT; + dev_err(LOGDEV, + "%s: timed out waiting for cmd to complete\n", + __func__); + } + + retval = fwu_read_interrupt_status(fwu); + fwu_read_flash_status(fwu); + if ((fwu->command == CMD_IDLE) && (fwu->flash_status == 0x00)) + return 0; + + return retval; +} + +static void fwu_reset_device(struct synaptics_rmi4_fwu_handle *fwu) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + rmi4_data->reset_device(rmi4_data); + if (fwu->irq_enabled) { + /* F34 irq handler will override default reset handler */ + /* and since ISR is triggered on falling edge, need to */ + /* wait for idle twice */ + fwu_wait_for_idle(fwu, ENABLE_WAIT_MS); + fwu_wait_for_idle(fwu, ENABLE_WAIT_MS); + } +} + +static int fwu_write_f34_v7_command_single_transaction( + struct synaptics_rmi4_fwu_handle *fwu, unsigned char cmd) +{ + int retval; + unsigned char base; + struct f34_v7_data_1_5 data_1_5; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + memset(data_1_5.data, 0x00, sizeof(data_1_5.data)); + + switch (cmd) { + case CMD_ERASE_ALL: + data_1_5.partition_id = CORE_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE_AP; + break; + case CMD_ERASE_UI_FIRMWARE: + data_1_5.partition_id = CORE_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_BL_CONFIG: + data_1_5.partition_id = GLOBAL_PARAMETERS_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_UI_CONFIG: + data_1_5.partition_id = CORE_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_DISP_CONFIG: + data_1_5.partition_id = DISPLAY_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_FLASH_CONFIG: + data_1_5.partition_id = FLASH_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_GUEST_CODE: + data_1_5.partition_id = GUEST_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ENABLE_FLASH_PROG: + data_1_5.partition_id = BOOTLOADER_PARTITION; + data_1_5.command = CMD_V7_ENTER_BL; + break; + }; + + data_1_5.payload_0 = fwu->bootloader_id[0]; + data_1_5.payload_1 = fwu->bootloader_id[1]; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.partition_id, + data_1_5.data, + sizeof(data_1_5.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write single transaction command\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_v7_command( + struct synaptics_rmi4_fwu_handle *fwu, unsigned char cmd) +{ + int retval; + unsigned char base; + unsigned char command; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_WRITE_FW: + case CMD_WRITE_CONFIG: + case CMD_WRITE_GUEST_CODE: + command = CMD_V7_WRITE; + break; + case CMD_READ_CONFIG: + command = CMD_V7_READ; + break; + case CMD_ERASE_ALL: + command = CMD_V7_ERASE_AP; + break; + case CMD_ERASE_UI_FIRMWARE: + case CMD_ERASE_BL_CONFIG: + case CMD_ERASE_UI_CONFIG: + case CMD_ERASE_DISP_CONFIG: + case CMD_ERASE_FLASH_CONFIG: + case CMD_ERASE_GUEST_CODE: + command = CMD_V7_ERASE; + break; + case CMD_ENABLE_FLASH_PROG: + command = CMD_V7_ENTER_BL; + break; + default: + dev_err(LOGDEV, + "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + }; + + fwu->command = command; + + switch (cmd) { + case CMD_ERASE_ALL: + case CMD_ERASE_UI_FIRMWARE: + case CMD_ERASE_BL_CONFIG: + case CMD_ERASE_UI_CONFIG: + case CMD_ERASE_DISP_CONFIG: + case CMD_ERASE_FLASH_CONFIG: + case CMD_ERASE_GUEST_CODE: + case CMD_ENABLE_FLASH_PROG: + retval = fwu_write_f34_v7_command_single_transaction(fwu, cmd); + if (retval < 0) + return retval; + else + return 0; + default: + break; + }; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.flash_cmd, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write flash command\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_v5v6_command( + struct synaptics_rmi4_fwu_handle *fwu, unsigned char cmd) +{ + int retval; + unsigned char base; + unsigned char command; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_IDLE: + command = CMD_V5V6_IDLE; + break; + case CMD_WRITE_FW: + command = CMD_V5V6_WRITE_FW; + break; + case CMD_WRITE_CONFIG: + command = CMD_V5V6_WRITE_CONFIG; + break; + case CMD_WRITE_LOCKDOWN: + command = CMD_V5V6_WRITE_LOCKDOWN; + break; + case CMD_WRITE_GUEST_CODE: + command = CMD_V5V6_WRITE_GUEST_CODE; + break; + case CMD_READ_CONFIG: + command = CMD_V5V6_READ_CONFIG; + break; + case CMD_ERASE_ALL: + command = CMD_V5V6_ERASE_ALL; + break; + case CMD_ERASE_UI_CONFIG: + command = CMD_V5V6_ERASE_UI_CONFIG; + break; + case CMD_ERASE_DISP_CONFIG: + command = CMD_V5V6_ERASE_DISP_CONFIG; + break; + case CMD_ERASE_GUEST_CODE: + command = CMD_V5V6_ERASE_GUEST_CODE; + break; + case CMD_ENABLE_FLASH_PROG: + command = CMD_V5V6_ENABLE_FLASH_PROG; + break; + default: + dev_err(LOGDEV, + "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + } + + switch (cmd) { + case CMD_ERASE_ALL: + case CMD_ERASE_UI_CONFIG: + case CMD_ERASE_DISP_CONFIG: + case CMD_ERASE_GUEST_CODE: + case CMD_ENABLE_FLASH_PROG: + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.payload, + fwu->bootloader_id, + sizeof(fwu->bootloader_id)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write bootloader ID\n", + __func__); + return retval; + } + break; + default: + break; + }; + + fwu->command = command; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.flash_cmd, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write command 0x%02x\n", + __func__, command); + return retval; + } + + return 0; +} + +static int fwu_write_f34_command( + struct synaptics_rmi4_fwu_handle *fwu, unsigned char cmd) +{ + int retval; + + if (fwu->bl_version >= BL_V7) + retval = fwu_write_f34_v7_command(fwu, cmd); + else + retval = fwu_write_f34_v5v6_command(fwu, cmd); + + return retval; +} + +static int fwu_write_f34_v7_partition_id( + struct synaptics_rmi4_fwu_handle *fwu, unsigned char cmd) +{ + int retval; + unsigned char base; + unsigned char partition; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_WRITE_FW: + partition = CORE_CODE_PARTITION; + break; + case CMD_WRITE_CONFIG: + case CMD_READ_CONFIG: + if (fwu->config_area == UI_CONFIG_AREA) + partition = CORE_CONFIG_PARTITION; + else if (fwu->config_area == DP_CONFIG_AREA) + partition = DISPLAY_CONFIG_PARTITION; + else if (fwu->config_area == PM_CONFIG_AREA) + partition = GUEST_SERIALIZATION_PARTITION; + else if (fwu->config_area == BL_CONFIG_AREA) + partition = GLOBAL_PARAMETERS_PARTITION; + else if (fwu->config_area == FLASH_CONFIG_AREA) + partition = FLASH_CONFIG_PARTITION; + break; + case CMD_WRITE_GUEST_CODE: + partition = GUEST_CODE_PARTITION; + break; + case CMD_ERASE_ALL: + partition = CORE_CODE_PARTITION; + break; + case CMD_ERASE_BL_CONFIG: + partition = GLOBAL_PARAMETERS_PARTITION; + break; + case CMD_ERASE_UI_CONFIG: + partition = CORE_CONFIG_PARTITION; + break; + case CMD_ERASE_DISP_CONFIG: + partition = DISPLAY_CONFIG_PARTITION; + break; + case CMD_ERASE_FLASH_CONFIG: + partition = FLASH_CONFIG_PARTITION; + break; + case CMD_ERASE_GUEST_CODE: + partition = GUEST_CODE_PARTITION; + break; + case CMD_ENABLE_FLASH_PROG: + partition = BOOTLOADER_PARTITION; + break; + default: + dev_err(LOGDEV, + "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + }; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.partition_id, + &partition, + sizeof(partition)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write partition ID\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_partition_id( + struct synaptics_rmi4_fwu_handle *fwu, unsigned char cmd) +{ + int retval; + + if (fwu->bl_version >= BL_V7) + retval = fwu_write_f34_v7_partition_id(fwu, cmd); + else + retval = 0; + + return retval; +} + +static int fwu_read_f34_v7_partition_table( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned char *partition_table) +{ + int retval; + unsigned char base; + unsigned char length[2]; + unsigned short block_number = 0; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + fwu->config_area = FLASH_CONFIG_AREA; + + retval = fwu_write_f34_partition_id(fwu, CMD_READ_CONFIG); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.block_number, + (unsigned char *)&block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + length[0] = (unsigned char)(fwu->flash_config_length & MASK_8BIT); + length[1] = (unsigned char)(fwu->flash_config_length >> 8); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.transfer_length, + length, + sizeof(length)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write transfer length\n", + __func__); + return retval; + } + + retval = fwu_write_f34_command(fwu, CMD_READ_CONFIG); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write command\n", + __func__); + return retval; + } + + retval = fwu_wait_for_idle(fwu, WRITE_WAIT_MS); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to wait for idle status\n", + __func__); + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.payload, + partition_table, + fwu->partition_table_bytes); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read block data\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_read_f34_v7_queries( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char ii; + unsigned char base; + unsigned char index; + unsigned char offset; + unsigned char *ptable; + struct f34_v7_query_0 query_0; + struct f34_v7_query_1_7 query_1_7; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.query_base_addr; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base, + query_0.data, + sizeof(query_0.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read query 0\n", + __func__); + return retval; + } + + offset = query_0.subpacket_1_size + 1; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + offset, + query_1_7.data, + sizeof(query_1_7.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read queries 1 to 7\n", + __func__); + return retval; + } + + fwu->bootloader_id[0] = query_1_7.bl_minor_revision; + fwu->bootloader_id[1] = query_1_7.bl_major_revision; + dev_dbg(LOGDEV, "%s: BL ver %d.%d\n", __func__, + fwu->bootloader_id[1], fwu->bootloader_id[0]); + + if (fwu->bootloader_id[1] == BL_V8) + fwu->bl_version = BL_V8; + else { + int i; + unsigned char *s = NULL; + struct synaptics_rmi4_device_info *rmi = &(rmi4_data->rmi4_mod_info); + + dev_info(LOGDEV, + "%s: BL ver.7 detected; need to update product ID\n", __func__); + /* need to add 2 extra characters, thus make sure they fit */ + for (i = 0; i < SYNAPTICS_RMI4_PRODUCT_ID_SIZE-2; i++) + if (rmi->product_id_string[i] == 0) { + if (!(rmi->product_id_string[i-1] == '7' && + rmi->product_id_string[i-2] == 'v')) + s = &rmi->product_id_string[i]; + break; + } + + if (s) { + /* adding suffix 'v7' to the original product id string */ + *s++ = 'v'; *s++ = '7'; *s = 0; + dev_info(LOGDEV, "%s: NOTE new product ID: '%s;\n", + __func__, rmi->product_id_string); + } + } + + fwu->block_size = query_1_7.block_size_15_8 << 8 | + query_1_7.block_size_7_0; + + fwu->flash_config_length = query_1_7.flash_config_length_15_8 << 8 | + query_1_7.flash_config_length_7_0; + + fwu->payload_length = query_1_7.payload_length_15_8 << 8 | + query_1_7.payload_length_7_0; + + fwu->off.flash_status = V7_FLASH_STATUS_OFFSET; + fwu->off.partition_id = V7_PARTITION_ID_OFFSET; + fwu->off.block_number = V7_BLOCK_NUMBER_OFFSET; + fwu->off.transfer_length = V7_TRANSFER_LENGTH_OFFSET; + fwu->off.flash_cmd = V7_COMMAND_OFFSET; + fwu->off.payload = V7_PAYLOAD_OFFSET; + + fwu->flash_properties.has_disp_config = query_1_7.has_display_config; + fwu->flash_properties.has_pm_config = query_1_7.has_guest_serialization; + fwu->flash_properties.has_bl_config = query_1_7.has_global_parameters; + + fwu->has_guest_code = query_1_7.has_guest_code; + + index = sizeof(query_1_7.data) - V7_PARTITION_SUPPORT_BYTES; + + fwu->partitions = 0; + for (offset = 0; offset < V7_PARTITION_SUPPORT_BYTES; offset++) { + for (ii = 0; ii < 8; ii++) { + if (query_1_7.data[index + offset] & (1 << ii)) + fwu->partitions++; + } + + dev_dbg(LOGDEV, + "%s: Supported partitions: 0x%02x\n", + __func__, query_1_7.data[index + offset]); + } + + fwu->partition_table_bytes = fwu->partitions * 8 + 2; + + ptable = kzalloc(fwu->partition_table_bytes, GFP_KERNEL); + if (!ptable) { + dev_err(LOGDEV, + "%s: Failed to alloc mem for partition table\n", + __func__); + return -ENOMEM; + } + + retval = fwu_read_f34_v7_partition_table(fwu, ptable); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read partition table\n", + __func__); + kfree(ptable); + return retval; + } + + fwu_parse_partition_table(fwu, ptable, &fwu->blkcount, &fwu->phyaddr); + + kfree(ptable); + + return 0; +} + +static int fwu_read_f34_v5v6_queries( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char count; + unsigned char base; + unsigned char buf[10]; + struct f34_v5v6_flash_properties_2 properties_2; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.query_base_addr; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + V5V6_BOOTLOADER_ID_OFFSET, + fwu->bootloader_id, + sizeof(fwu->bootloader_id)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read bootloader ID\n", + __func__); + return retval; + } + + if (fwu->bl_version == BL_V5) { + fwu->off.properties = V5_PROPERTIES_OFFSET; + fwu->off.block_size = V5_BLOCK_SIZE_OFFSET; + fwu->off.block_count = V5_BLOCK_COUNT_OFFSET; + fwu->off.block_number = V5_BLOCK_NUMBER_OFFSET; + fwu->off.payload = V5_BLOCK_DATA_OFFSET; + } else if (fwu->bl_version == BL_V6) { + fwu->off.properties = V6_PROPERTIES_OFFSET; + fwu->off.properties_2 = V6_PROPERTIES_2_OFFSET; + fwu->off.block_size = V6_BLOCK_SIZE_OFFSET; + fwu->off.block_count = V6_BLOCK_COUNT_OFFSET; + fwu->off.gc_block_count = V6_GUEST_CODE_BLOCK_COUNT_OFFSET; + fwu->off.block_number = V6_BLOCK_NUMBER_OFFSET; + fwu->off.payload = V6_BLOCK_DATA_OFFSET; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.block_size, + buf, + 2); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read block size info\n", + __func__); + return retval; + } + + batohs(&fwu->block_size, &(buf[0])); + + if (fwu->bl_version == BL_V5) { + fwu->off.flash_cmd = fwu->off.payload + fwu->block_size; + fwu->off.flash_status = fwu->off.flash_cmd; + } else if (fwu->bl_version == BL_V6) { + fwu->off.flash_cmd = V6_FLASH_COMMAND_OFFSET; + fwu->off.flash_status = V6_FLASH_STATUS_OFFSET; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.properties, + fwu->flash_properties.data, + sizeof(fwu->flash_properties.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read flash properties\n", + __func__); + return retval; + } + + count = 4; + + if (fwu->flash_properties.has_pm_config) + count += 2; + + if (fwu->flash_properties.has_bl_config) + count += 2; + + if (fwu->flash_properties.has_disp_config) + count += 2; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.block_count, + buf, + count); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read block count info\n", + __func__); + return retval; + } + + batohs(&fwu->blkcount.ui_firmware, &(buf[0])); + batohs(&fwu->blkcount.ui_config, &(buf[2])); + + count = 4; + + if (fwu->flash_properties.has_pm_config) { + batohs(&fwu->blkcount.pm_config, &(buf[count])); + count += 2; + } + + if (fwu->flash_properties.has_bl_config) { + batohs(&fwu->blkcount.bl_config, &(buf[count])); + count += 2; + } + + if (fwu->flash_properties.has_disp_config) + batohs(&fwu->blkcount.dp_config, &(buf[count])); + + fwu->has_guest_code = false; + + if (fwu->flash_properties.has_query4) { + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.properties_2, + properties_2.data, + sizeof(properties_2.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read flash properties 2\n", + __func__); + return retval; + } + + if (properties_2.has_guest_code) { + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.gc_block_count, + buf, + 2); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read guest code block count\n", + __func__); + return retval; + } + + batohs(&fwu->blkcount.guest_code, &(buf[0])); + fwu->has_guest_code = true; + } + } + + return 0; +} + +static int fwu_read_f34_queries( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + memset(&fwu->blkcount, 0x00, sizeof(fwu->blkcount)); + memset(&fwu->phyaddr, 0x00, sizeof(fwu->phyaddr)); + + /* Strict BL version comparison is correct in this case, */ + /* since PDT scan assigns bl_version to BL_V7 for F34 v.2*/ + if (fwu->bl_version == BL_V7) + retval = fwu_read_f34_v7_queries(fwu); + else + retval = fwu_read_f34_v5v6_queries(fwu); + + dev_info(LOGDEV, + "%s: BL version = %d\n", + __func__, fwu->bl_version); + + return retval; +} + +static int fwu_write_f34_v7_blocks( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned char *block_ptr, + unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char base; + unsigned char length[2]; + unsigned short transfer; + unsigned short max_transfer; + unsigned short remaining = block_cnt; + unsigned short block_number = 0; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + retval = fwu_write_f34_partition_id(fwu, command); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.block_number, + (unsigned char *)&block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + if (fwu->payload_length > (PAGE_SIZE / fwu->block_size)) + max_transfer = PAGE_SIZE / fwu->block_size; + else + max_transfer = fwu->payload_length; + + dev_dbg(LOGDEV, "%s: update %s (%3d / %3d)\n", + __func__, + command == CMD_WRITE_CONFIG ? + "config" : "firmware", + block_cnt - remaining, block_cnt); + + do { + if (remaining / max_transfer) + transfer = max_transfer; + else + transfer = remaining; + + length[0] = (unsigned char)(transfer & MASK_8BIT); + length[1] = (unsigned char)(transfer >> 8); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.transfer_length, + length, + sizeof(length)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write transfer length (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = fwu_write_f34_command(fwu, command); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write command (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.payload, + block_ptr, + transfer * fwu->block_size); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block data (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = fwu_wait_for_idle(fwu, WRITE_WAIT_MS); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to wait for idle status (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + block_ptr += (transfer * fwu->block_size); + remaining -= transfer; + dev_dbg(LOGDEV, "%s: update %s (%3d / %3d)\n", + __func__, + command == CMD_WRITE_CONFIG ? + "config" : "firmware", + block_cnt - remaining, block_cnt); + } while (remaining); + + return 0; +} + +static int fwu_write_f34_v5v6_blocks( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned char *block_ptr, + unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char base; + unsigned char block_number[] = {0, 0}; + unsigned short blk; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + unsigned int progress = (command == CMD_WRITE_CONFIG) ? + 10 : 100; + + base = fwu->f34_fd.data_base_addr; + + block_number[1] |= (fwu->config_area << 5); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.block_number, + block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + for (blk = 0; blk < block_cnt; blk++) { + if (blk % progress == 0) + dev_dbg(LOGDEV, + "%s: update %s %3d / %3d\n", + __func__, + command == CMD_WRITE_CONFIG ? + "config" : "firmware", + blk, + block_cnt); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.payload, + block_ptr, + fwu->block_size); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block data (block %d)\n", + __func__, blk); + return retval; + } + + retval = fwu_write_f34_command(fwu, command); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write command for block %d\n", + __func__, blk); + return retval; + } + + retval = fwu_wait_for_idle(fwu, WRITE_WAIT_MS); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to wait for idle status (block %d)\n", + __func__, blk); + return retval; + } + + block_ptr += fwu->block_size; + } + + return 0; +} + +static int fwu_write_f34_blocks( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned char *block_ptr, + unsigned short block_cnt, + unsigned char cmd) +{ + int retval; + + if (fwu->bl_version >= BL_V7) + retval = fwu_write_f34_v7_blocks(fwu, block_ptr, block_cnt, cmd); + else + retval = fwu_write_f34_v5v6_blocks(fwu, block_ptr, block_cnt, cmd); + + return retval; +} + +static int fwu_read_f34_v7_blocks( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char base; + unsigned char length[2]; + unsigned short transfer; + unsigned short max_transfer; + unsigned short remaining = block_cnt; + unsigned short block_number = 0; + unsigned short index = 0; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + retval = fwu_write_f34_partition_id(fwu, command); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.block_number, + (unsigned char *)&block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + if (fwu->payload_length > (PAGE_SIZE / fwu->block_size)) + max_transfer = PAGE_SIZE / fwu->block_size; + else + max_transfer = fwu->payload_length; + + do { + if (remaining / max_transfer) + transfer = max_transfer; + else + transfer = remaining; + + length[0] = (unsigned char)(transfer & MASK_8BIT); + length[1] = (unsigned char)(transfer >> 8); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.transfer_length, + length, + sizeof(length)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write transfer length (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = fwu_write_f34_command(fwu, command); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write command (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = fwu_wait_for_idle(fwu, WRITE_WAIT_MS); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to wait for idle status (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.payload, + &fwu->read_config_buf[index], + transfer * fwu->block_size); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read block data (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + index += (transfer * fwu->block_size); + remaining -= transfer; + } while (remaining); + + return 0; +} + +static int fwu_read_f34_v5v6_blocks( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char base; + unsigned char block_number[] = {0, 0}; + unsigned short blk; + unsigned short index = 0; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + block_number[1] |= (fwu->config_area << 5); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.block_number, + block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + for (blk = 0; blk < block_cnt; blk++) { + retval = fwu_write_f34_command(fwu, command); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write read config command\n", + __func__); + return retval; + } + + retval = fwu_wait_for_idle(fwu, WRITE_WAIT_MS); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to wait for idle status\n", + __func__); + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.payload, + &fwu->read_config_buf[index], + fwu->block_size); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read block data (block %d)\n", + __func__, blk); + return retval; + } + + index += fwu->block_size; + } + + return 0; +} + +static int fwu_read_f34_blocks( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned short block_cnt, + unsigned char cmd) +{ + int retval; + + if (fwu->bl_version >= BL_V7) + retval = fwu_read_f34_v7_blocks(fwu, block_cnt, cmd); + else + retval = fwu_read_f34_v5v6_blocks(fwu, block_cnt, cmd); + + return retval; +} + +static void fwu_get_device_firmware_id( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned int *dev_id) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + struct synaptics_rmi4_device_info *rmi = + &rmi4_data->rmi4_mod_info; + unsigned char *buf = (unsigned char *)dev_id; + buf[0] = 0; + buf[1] = rmi->build_id[0]; + buf[2] = rmi->build_id[1]; + buf[3] = rmi->build_id[2]; +} + +static int fwu_get_image_firmware_id( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned int *fw_id) +{ + if (fwu->img.contains_firmware_id) { + *fw_id = fwu->img.firmware_id; + return 0; + } + + dev_err(LOGDEV, "%s: no build ID in image\n", __func__); + return -EINVAL; +} + +static int fwu_get_device_config_id(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char config_id_size; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + if (fwu->bl_version >= BL_V7) + config_id_size = V7_CONFIG_ID_SIZE; + else + config_id_size = V5V6_CONFIG_ID_SIZE; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.ctrl_base_addr, + fwu->config_id, + config_id_size); + if (retval < 0) + return retval; + + return 0; +} + +static enum flash_area fwu_go_nogo(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + enum flash_area flash_area = NONE; + unsigned char ii; + unsigned char config_id_size; + unsigned int device_fw_id; + unsigned int image_fw_id; + + if (fwu->force_update) { + flash_area = UI_FIRMWARE; + goto exit; + } + + /* Update both UI and config if device is in bootloader mode */ + if (fwu->in_bl_mode) { + flash_area = UI_FIRMWARE; + dev_dbg(LOGDEV, "%s: BL mode shortcut\n", __func__); + goto exit; + } + + /* Get device firmware ID */ + fwu_get_device_firmware_id(fwu, &device_fw_id); + dev_info(LOGDEV, + "%s: Device firmware ID = %d\n", + __func__, device_fw_id); + + /* Get image firmware ID */ + retval = fwu_get_image_firmware_id(fwu, &image_fw_id); + if (retval < 0) { + flash_area = NONE; + goto exit; + } + dev_info(LOGDEV, + "%s: Image firmware ID = %d\n", + __func__, image_fw_id); + + if (image_fw_id != device_fw_id) { + dev_info(LOGDEV, + "%s: Image firmware ID differs from device firmware ID 0x%x 0x%x\n", + __func__, image_fw_id, device_fw_id); + flash_area = UI_FIRMWARE; + goto exit; + } + + /* Get device config ID */ + retval = fwu_get_device_config_id(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read device config ID\n", + __func__); + flash_area = NONE; + goto exit; + } + + if (fwu->bl_version >= BL_V7) + config_id_size = V7_CONFIG_ID_SIZE; + else + config_id_size = V5V6_CONFIG_ID_SIZE; + + for (ii = 0; ii < config_id_size; ii++) { + if (fwu->img.ui_config.data[ii] > fwu->config_id[ii]) { + flash_area = UI_CONFIG; + goto exit; + } else if (fwu->img.ui_config.data[ii] < fwu->config_id[ii]) { + flash_area = NONE; + goto exit; + } + } + + flash_area = NONE; + +exit: + if (flash_area == NONE) { + dev_info(LOGDEV, + "%s: No need to do reflash\n", + __func__); + } else { + dev_info(LOGDEV, + "%s: Updating %s\n", + __func__, + flash_area == UI_FIRMWARE ? + "UI firmware and config" : + "UI config only"); + } + + return flash_area; +} + +static int fwu_scan_pdt(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char ii; + unsigned char intr_count = 0; + unsigned char intr_off; + unsigned char intr_src; + unsigned short addr; + bool f01found = false; + bool f34found = false; + bool f35found = false; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + fwu->in_ub_mode = false; + + for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + if (rmi_fd.fn_number) { + dev_dbg(LOGDEV, + "%s: Found F%02x\n", + __func__, rmi_fd.fn_number); + switch (rmi_fd.fn_number) { + case SYNAPTICS_RMI4_F01: + f01found = true; + + rmi4_data->f01_query_base_addr = + rmi_fd.query_base_addr; + rmi4_data->f01_ctrl_base_addr = + rmi_fd.ctrl_base_addr; + rmi4_data->f01_data_base_addr = + rmi_fd.data_base_addr; + rmi4_data->f01_cmd_base_addr = + rmi_fd.cmd_base_addr; + break; + case SYNAPTICS_RMI4_F34: + f34found = true; + fwu->f34_fd.query_base_addr = + rmi_fd.query_base_addr; + fwu->f34_fd.ctrl_base_addr = + rmi_fd.ctrl_base_addr; + fwu->f34_fd.data_base_addr = + rmi_fd.data_base_addr; + + switch (rmi_fd.fn_version) { + case F34_V0: + fwu->bl_version = BL_V5; + break; + case F34_V1: + fwu->bl_version = BL_V6; + break; + case F34_V2: + fwu->bl_version = BL_V7; + break; + default: + dev_err(LOGDEV, + "%s: Unrecognized F34 version\n", + __func__); + return -EINVAL; + } + + dev_dbg(LOGDEV, + "%s: F34 version %d\n", + __func__, rmi_fd.fn_version); + + fwu->intr_mask = 0; + intr_src = rmi_fd.intr_src_count; + intr_off = intr_count % 8; + for (ii = intr_off; + ii < (intr_src + intr_off); + ii++) { + fwu->intr_mask |= 1 << ii; + } + break; + case SYNAPTICS_RMI4_F35: + f35found = true; + fwu->f35_fd.query_base_addr = + rmi_fd.query_base_addr; + fwu->f35_fd.ctrl_base_addr = + rmi_fd.ctrl_base_addr; + fwu->f35_fd.data_base_addr = + rmi_fd.data_base_addr; + break; + } + } else { + break; + } + + intr_count += rmi_fd.intr_src_count; + } + + if (!f01found || !f34found) { + dev_err(LOGDEV, + "%s: Failed to find both F01 and F34\n", + __func__); + if (!f35found) { + dev_err(LOGDEV, + "%s: Failed to find F35\n", + __func__); + return -EINVAL; + } else { + fwu->in_ub_mode = true; + dev_dbg(LOGDEV, + "%s: In microbootloader mode\n", + __func__); + fwu_recovery_check_status(fwu); + return 0; + } + } + + return 0; +} + +int fwu_check_intr_en(struct synaptics_rmi4_data *rmi4_data, + unsigned char *drv_intr_en, size_t size) +{ + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int i; + unsigned char intr_en[MAX_INTR_REGISTERS]; + int retval = 0; + unsigned char addr = rmi4_data->f01_ctrl_base_addr + 1; + + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + intr_en, + size); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read interrupt enable register\n", + __func__); + return retval; + } + /* ensure interrupt enable mask expected by driver is subset of */ + /* touch IC interrupt enable register */ + for (i = 0; i < size; ++i) + if (drv_intr_en[i] != (drv_intr_en[i] & intr_en[i])) { + retval = 1; + dev_err(LOGDEV, + "%s: Interrupt enable mismatch:" + " drv[%d] = 0x%02x, fw[%d] = 0x%02x\n", + __func__, i, drv_intr_en[i], i, intr_en[i]); + } + + return retval; +} + +static int fwu_enter_flash_prog(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + struct f01_device_control f01_device_control; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + retval = fwu_read_flash_status(fwu); + if (retval < 0) + return retval; + + if (fwu->in_bl_mode) { + dev_dbg(LOGDEV, "%s: BL mode shortcut\n", __func__); + return 0; + } + + retval = fwu_write_f34_command(fwu, CMD_ENABLE_FLASH_PROG); + if (retval < 0) + goto out; + + retval = fwu_wait_for_idle(fwu, ENABLE_WAIT_MS); + if (retval < 0) + goto out; + + if (!fwu->in_bl_mode) { + dev_err(LOGDEV, + "%s: BL mode not entered\n", + __func__); + retval = -EINVAL; + goto out; + } + + retval = fwu_scan_pdt(fwu); + if (retval < 0) + goto out; + + fwu_check_intr_en(rmi4_data, &fwu->intr_mask, sizeof(fwu->intr_mask)); + + retval = fwu_read_f34_queries(fwu); + if (retval < 0) + goto out; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + f01_device_control.data, + sizeof(f01_device_control.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read F01 device control\n", + __func__); + goto out; + } + + f01_device_control.nosleep = true; + f01_device_control.sleep_mode = SLEEP_MODE_NORMAL; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + f01_device_control.data, + sizeof(f01_device_control.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write F01 device control\n", + __func__); + goto out; + } + + msleep(ENTER_FLASH_PROG_WAIT_MS); +out: + return retval; +} + +static int fwu_check_ui_firmware_size( + struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short block_count; + + block_count = fwu->img.ui_firmware.size / fwu->block_size; + + if (block_count != fwu->blkcount.ui_firmware) { + dev_err(LOGDEV, + "%s: UI firmware size mismatch: %d (expected %d)\n", + __func__, block_count, fwu->blkcount.ui_firmware); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_ui_configuration_size( + struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short block_count; + + block_count = fwu->img.ui_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.ui_config) { + dev_err(LOGDEV, + "%s: UI config size mismatch: %d (expected %d)\n", + __func__, block_count, fwu->blkcount.ui_config); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_dp_configuration_size( + struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short block_count; + + block_count = fwu->img.dp_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.dp_config) { + dev_err(LOGDEV, + "%s: Display configuration size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_bl_configuration_size( + struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short block_count; + + block_count = fwu->img.bl_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.bl_config) { + dev_err(LOGDEV, + "%s: Bootloader configuration size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_guest_code_size( + struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short block_count; + + block_count = fwu->img.guest_code.size / fwu->block_size; + if (block_count != fwu->blkcount.guest_code) { + dev_err(LOGDEV, + "%s: Guest code size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_write_firmware( + struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short firmware_block_count; + + firmware_block_count = fwu->img.ui_firmware.size / fwu->block_size; + + return fwu_write_f34_blocks(fwu, (unsigned char *)fwu->img.ui_firmware.data, + firmware_block_count, CMD_WRITE_FW); +} + +static int fwu_erase_configuration( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + retval = fwu_write_f34_command(fwu, CMD_ERASE_UI_CONFIG); + if (retval < 0) + return retval; + break; + case DP_CONFIG_AREA: + retval = fwu_write_f34_command(fwu, CMD_ERASE_DISP_CONFIG); + if (retval < 0) + return retval; + break; + case BL_CONFIG_AREA: + retval = fwu_write_f34_command(fwu, CMD_ERASE_BL_CONFIG); + if (retval < 0) + return retval; + break; + } + + dev_dbg(LOGDEV, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(fwu, ERASE_WAIT_MS); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Idle status detected\n", + __func__); + + return retval; +} + +static int fwu_erase_guest_code( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + retval = fwu_write_f34_command(fwu, CMD_ERASE_GUEST_CODE); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(fwu, ERASE_WAIT_MS); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Idle status detected\n", + __func__); + + return 0; +} + +static int fwu_erase_all(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + if (fwu->bl_version == BL_V7) { + retval = fwu_write_f34_command(fwu, CMD_ERASE_UI_FIRMWARE); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(fwu, ERASE_WAIT_MS); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Idle status detected\n", + __func__); + + fwu->config_area = UI_CONFIG_AREA; + retval = fwu_erase_configuration(fwu); + if (retval < 0) + return retval; + } else { + retval = fwu_write_f34_command(fwu, CMD_ERASE_ALL); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Erase all command written\n", + __func__); + + retval = fwu_wait_for_idle(fwu, ERASE_WAIT_MS); + if (!(fwu->bl_version == BL_V8 && + fwu->flash_status == BAD_PARTITION_TABLE)) { + if (retval < 0) + return retval; + } + + dev_dbg(LOGDEV, + "%s: Idle status detected\n", + __func__); + + if (fwu->bl_version == BL_V8) + return 0; + } + + if (fwu->flash_properties.has_disp_config) { + fwu->config_area = DP_CONFIG_AREA; + retval = fwu_erase_configuration(fwu); + if (retval < 0) + return retval; + } + + if (fwu->new_partition_table && fwu->has_guest_code) { + retval = fwu_erase_guest_code(fwu); + if (retval < 0) + return retval; + } + + return 0; +} + +static int fwu_write_configuration(struct synaptics_rmi4_fwu_handle *fwu) +{ + return fwu_write_f34_blocks(fwu, (unsigned char *)fwu->config_data, + fwu->config_block_count, CMD_WRITE_CONFIG); +} + +static int fwu_write_ui_configuration(struct synaptics_rmi4_fwu_handle *fwu) +{ + fwu->config_area = UI_CONFIG_AREA; + fwu->config_data = fwu->img.ui_config.data; + fwu->config_size = fwu->img.ui_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + return fwu_write_configuration(fwu); +} + +static int fwu_write_dp_configuration(struct synaptics_rmi4_fwu_handle *fwu) +{ + fwu->config_area = DP_CONFIG_AREA; + fwu->config_data = fwu->img.dp_config.data; + fwu->config_size = fwu->img.dp_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + return fwu_write_configuration(fwu); +} + +static int fwu_write_flash_configuration(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + fwu->config_area = FLASH_CONFIG_AREA; + fwu->config_data = fwu->img.fl_config.data; + fwu->config_size = fwu->img.fl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + if (fwu->config_block_count != fwu->blkcount.fl_config) { + dev_err(LOGDEV, + "%s: Flash configuration size mismatch\n", + __func__); + return -EINVAL; + } + + retval = fwu_write_f34_command(fwu, CMD_ERASE_FLASH_CONFIG); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Erase flash configuration command written\n", + __func__); + + retval = fwu_wait_for_idle(fwu, ERASE_WAIT_MS); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Idle status detected\n", + __func__); + + retval = fwu_write_configuration(fwu); + if (retval < 0) + return retval; + + fwu_reset_device(fwu); + + return 0; +} + +static int fwu_write_guest_code(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned short guest_code_block_count; + + guest_code_block_count = fwu->img.guest_code.size / fwu->block_size; + + retval = fwu_write_f34_blocks(fwu, (unsigned char *)fwu->img.guest_code.data, + guest_code_block_count, CMD_WRITE_GUEST_CODE); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_write_lockdown(struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short lockdown_block_count; + + lockdown_block_count = fwu->img.lockdown.size / fwu->block_size; + + return fwu_write_f34_blocks(fwu, (unsigned char *)fwu->img.lockdown.data, + lockdown_block_count, CMD_WRITE_LOCKDOWN); +} + +static int fwu_write_partition_table_v8(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + fwu->config_area = FLASH_CONFIG_AREA; + fwu->config_data = fwu->img.fl_config.data; + fwu->config_size = fwu->img.fl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + if (fwu->config_block_count != fwu->blkcount.fl_config) { + dev_err(LOGDEV, + "%s: Flash configuration size mismatch\n", + __func__); + return -EINVAL; + } + + retval = fwu_write_configuration(fwu); + if (retval < 0) + return retval; + + fwu_reset_device(fwu); + + return 0; +} + +static int fwu_write_partition_table_v7(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned short block_count; + + block_count = fwu->blkcount.bl_config; + fwu->config_area = BL_CONFIG_AREA; + fwu->config_size = fwu->block_size * block_count; + + retval = fwu_allocate_read_config_buf(fwu, fwu->config_size); + if (retval < 0) + return retval; + + retval = fwu_read_f34_blocks(fwu, block_count, CMD_READ_CONFIG); + if (retval < 0) + return retval; + + retval = fwu_erase_configuration(fwu); + if (retval < 0) + return retval; + + retval = fwu_write_flash_configuration(fwu); + if (retval < 0) + return retval; + + fwu->config_area = BL_CONFIG_AREA; + fwu->config_data = fwu->read_config_buf; + fwu->config_size = fwu->img.bl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + retval = fwu_write_configuration(fwu); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_do_reflash(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + if (!fwu->new_partition_table) { + retval = fwu_check_ui_firmware_size(fwu); + if (retval < 0) + return retval; + + retval = fwu_check_ui_configuration_size(fwu); + if (retval < 0) + return retval; + + if (fwu->flash_properties.has_disp_config && + fwu->img.contains_disp_config) { + retval = fwu_check_dp_configuration_size(fwu); + if (retval < 0) + return retval; + } + + if (fwu->has_guest_code && fwu->img.contains_guest_code) { + retval = fwu_check_guest_code_size(fwu); + if (retval < 0) + return retval; + } + } else if (fwu->bl_version == BL_V7) { + retval = fwu_check_bl_configuration_size(fwu); + if (retval < 0) + return retval; + } + + retval = fwu_erase_all(fwu); + if (retval < 0) + return retval; + + if (fwu->bl_version == BL_V7 && fwu->new_partition_table) { + retval = fwu_write_partition_table_v7(fwu); + if (retval < 0) + return retval; + pr_notice("%s: Partition table programmed\n", __func__); + } else if (fwu->bl_version == BL_V8) { + retval = fwu_write_partition_table_v8(fwu); + if (retval < 0) + return retval; + pr_notice("%s: Partition table programmed\n", __func__); + } + + retval = fwu_write_firmware(fwu); + if (retval < 0) + return retval; + pr_notice("%s: Firmware programmed\n", __func__); + + fwu->config_area = UI_CONFIG_AREA; + retval = fwu_write_ui_configuration(fwu); + if (retval < 0) + return retval; + pr_notice("%s: Configuration programmed\n", __func__); + + if (fwu->flash_properties.has_disp_config && + fwu->img.contains_disp_config) { + retval = fwu_write_dp_configuration(fwu); + if (retval < 0) + return retval; + pr_notice("%s: Display configuration programmed\n", __func__); + } + + if (fwu->new_partition_table) { + if (fwu->has_guest_code && fwu->img.contains_guest_code) { + retval = fwu_write_guest_code(fwu); + if (retval < 0) + return retval; + pr_notice("%s: Guest code programmed\n", __func__); + } + } + + return retval; +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static int fwu_do_read_config( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned short block_count; + unsigned short config_area; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + block_count = fwu->blkcount.ui_config; + break; + case DP_CONFIG_AREA: + if (!fwu->flash_properties.has_disp_config) { + dev_err(LOGDEV, + "%s: Display configuration not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.dp_config; + break; + case PM_CONFIG_AREA: + if (!fwu->flash_properties.has_pm_config) { + dev_err(LOGDEV, + "%s: Permanent configuration not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.pm_config; + break; + case BL_CONFIG_AREA: + if (!fwu->flash_properties.has_bl_config) { + dev_err(LOGDEV, + "%s: Bootloader configuration not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.bl_config; + break; + default: + dev_err(LOGDEV, + "%s: Invalid config area\n", + __func__); + return -EINVAL; + } + + if (block_count == 0) { + dev_err(LOGDEV, + "%s: Invalid block count\n", + __func__); + return -EINVAL; + } + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + config_area = fwu->config_area; + + retval = fwu_enter_flash_prog(fwu); + if (retval < 0) + goto exit; + + fwu->config_area = config_area; + + fwu->config_size = fwu->block_size * block_count; + + retval = fwu_allocate_read_config_buf(fwu->config_size); + if (retval < 0) + goto exit; + + retval = fwu_read_f34_blocks(fwu, block_count, CMD_READ_CONFIG); + +exit: + fwu_reset_device(fwu); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + return retval; +} +#endif + +static int fwu_do_lockdown(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + retval = fwu_enter_flash_prog(fwu); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.query_base_addr + fwu->off.properties, + fwu->flash_properties.data, + sizeof(fwu->flash_properties.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read flash properties\n", + __func__); + return retval; + } + + if (fwu->flash_properties.unlocked == 0) { + dev_info(LOGDEV, + "%s: Device already locked down\n", + __func__); + return 0; + } + + retval = fwu_write_lockdown(fwu); + if (retval < 0) + return retval; + + pr_notice("%s: Lockdown programmed\n", __func__); + + return retval; +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static int fwu_start_write_guest_code(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + retval = fwu_parse_image_info(fwu); + if (retval < 0) + return -EINVAL; + + if (!fwu->has_guest_code) { + dev_err(LOGDEV, + "%s: Guest code not supported\n", + __func__); + return -EINVAL; + } + + if (!fwu->img.contains_guest_code) { + dev_err(LOGDEV, + "%s: No guest code in firmware image\n", + __func__); + return -EINVAL; + } + + rmi4_data->set_state(rmi4_data, STATE_INIT); + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + pr_notice("%s: Start of write guest code process\n", __func__); + + retval = fwu_enter_flash_prog(fwu); + if (retval < 0) + goto exit; + + retval = fwu_check_guest_code_size(fwu); + if (retval < 0) + goto exit; + + retval = fwu_erase_guest_code(fwu + if (retval < 0) + goto exit; + + retval = fwu_write_guest_code(fwu); + if (retval < 0) + goto exit; + + pr_notice("%s: Guest code programmed\n", __func__); + +exit: + fwu_reset_device(fwu); + + pr_notice("%s: End of write guest code process\n", __func__); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + return retval; +} + +static int fwu_start_write_config(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned short config_area; + unsigned int device_fw_id; + unsigned int image_fw_id; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + retval = fwu_parse_image_info(fwu); + if (retval < 0) + return -EINVAL; + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + fwu_get_device_firmware_id(fwu, &device_fw_id); + retval = fwu_get_image_firmware_id(fwu, &image_fw_id); + if (retval < 0) + return retval; + if (device_fw_id != image_fw_id) { + dev_err(LOGDEV, + "%s: Device and image firmware IDs don't match\n", + __func__); + return -EINVAL; + } + retval = fwu_check_ui_configuration_size(fwu); + if (retval < 0) + return retval; + break; + case DP_CONFIG_AREA: + if (!fwu->flash_properties.has_disp_config) { + dev_err(LOGDEV, + "%s: Display configuration not supported\n", + __func__); + return -EINVAL; + } + if (!fwu->img.contains_disp_config) { + dev_err(LOGDEV, + "%s: No display configuration in firmware image\n", + __func__); + return -EINVAL; + } + retval = fwu_check_dp_configuration_size(fwu); + if (retval < 0) + return retval; + break; + default: + dev_err(LOGDEV, + "%s: Configuration not supported\n", + __func__); + return -EINVAL; + } + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + pr_notice("%s: Start of write config process\n", __func__); + + config_area = fwu->config_area; + + retval = fwu_enter_flash_prog(fwu); + if (retval < 0) + goto exit; + + fwu->config_area = config_area; + + retval = fwu_erase_configuration(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to erase config\n", + __func__); + goto exit; + } + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + retval = fwu_write_ui_configuration(fwu); + if (retval < 0) + goto exit; + break; + case DP_CONFIG_AREA: + retval = fwu_write_dp_configuration(fwu); + if (retval < 0) + goto exit; + break; + } + + pr_notice("%s: Config written\n", __func__); + +exit: + switch (fwu->config_area) { + case UI_CONFIG_AREA: + fwu_reset_device(fwu); + break; + case DP_CONFIG_AREA: + fwu_reset_device(fwu); + break; + } + + pr_notice("%s: End of write config process\n", __func__); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + return retval; +} +#endif + +static bool fwu_tdat_image_format(const unsigned char *fw_image) +{ + return fw_image[0] == 0x31; +} + +static void fwu_tdat_config_set(const unsigned char *data, unsigned int size, + const unsigned char **image, unsigned int *image_size) +{ + unsigned short id; + unsigned int length, offset; + + for (offset = 0; offset < size; offset += length+5) { + id = (data[offset+1] << 8) | data[offset]; + length = (data[offset+4] << 8) | data[offset+3]; + if (id == 0x0001) { + *image = &data[offset+5]; + *image_size = length; + } + } +} + +static void fwu_tdat_section_offset( + const unsigned char **image, unsigned int *image_size) +{ + unsigned int offset; + offset = (*image)[0] + 1; + *image_size -= offset; + *image = &(*image)[offset]; +} + +static int fwu_parse_tdat_image(struct synaptics_rmi4_fwu_handle *fwu) +{ + int ii; + unsigned int id; + unsigned int length, offset; + struct image_metadata *img = &fwu->img; + const unsigned char *section, *data = fwu->image; + unsigned int fw_size = fwu->image_size; + img->contains_firmware_id = false; + img->contains_bootloader = false; + img->contains_disp_config = false; + img->contains_guest_code = false; + img->contains_flash_config = false; + img->lockdown.data = NULL; + + + pr_notice("%s: Start TDAT image processing\n", __func__); + + for (ii = 0, offset = 1; offset < fw_size; offset += length+4) { + length = (data[offset+3] << 16) | + (data[offset+2] << 8) | data[offset+1]; + + dev_dbg(LOGDEV, + "Record[%d]: length %u, offset %u\n", + ii++, length, offset); + + if ((offset+length+4) > fw_size) { + dev_err(LOGDEV, + "Data overflow at offset %u (%u)\n", + offset, data[offset]); + return -EINVAL; + } + } + + if (offset != fw_size) { + dev_err(LOGDEV, + "Data is misaligned\n"); + return -EINVAL; + } + + for (offset = 1; offset < fw_size; offset += length+4) { + id = data[offset]; + length = (data[offset+3] << 16) | + (data[offset+2] << 8) | data[offset+1]; + section = &data[offset+4]; + + switch (id) { + case 1: /* config */ + dev_dbg(LOGDEV, + "%s: Config record %d, size %u\n", + __func__, id, length); + + fwu_tdat_config_set(section, length, + &img->ui_config.data, + &img->ui_config.size); + fwu_tdat_section_offset(&img->ui_config.data, + &img->ui_config.size); + break; + + case 2: /* firmware */ + dev_dbg(LOGDEV, + "%s: Firmware record %d, size %u\n", + __func__, + id, + length); + + img->contains_firmware_id = true; + batohui(&fwu->img.firmware_id, + (unsigned char *)§ion[1], + SYNAPTICS_RMI4_BUILD_ID_SIZE); + + dev_dbg(LOGDEV, + "%s: Firmware build ID %x\n", + __func__, + fwu->img.firmware_id); + + img->ui_firmware.data = section; + img->ui_firmware.size = length; + fwu_tdat_section_offset(&img->ui_firmware.data, + &img->ui_firmware.size); + break; + default: + dev_dbg(LOGDEV, + "%s: Don't care section id %d\n", + __func__, + id); + break; + } + } + + dev_dbg(LOGDEV, + "%s: Firwmare size %u, config size %u\n", + __func__, + img->ui_firmware.size, + img->ui_config.size); + return 0; +} + +static int fwu_start_reflash(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval = 0; + enum flash_area flash_area; + const struct firmware *fw_entry = NULL; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + __pm_stay_awake(&fwu->flash_wake_src); + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + pr_notice("%s: Start of reflash process\n", __func__); + + rmi4_data->set_state(rmi4_data, STATE_INIT); + fwu_irq_enable(fwu, true); + + if (fwu->image == NULL) { + dev_dbg(LOGDEV, + "%s: Requesting firmware image %s\n", + __func__, fwu->image_name); + + retval = request_firmware(&fw_entry, fwu->image_name, + LOGDEV); + if (retval != 0) { + dev_err(LOGDEV, + "%s: Firmware image %s not available\n", + __func__, fwu->image_name); + retval = -EINVAL; + goto exit; + } + + dev_dbg(LOGDEV, + "%s: Firmware image size = %zd\n", + __func__, fw_entry->size); + + fwu->image = fw_entry->data; + fwu->image_size = fw_entry->size; + } + + if (fwu_tdat_image_format(fwu->image)) + fwu_parse_tdat_image(fwu); + else { + retval = fwu_parse_image_info(fwu); + if (retval < 0) + goto exit; + + if (fwu->bl_version != fwu->img.bl_version) { + dev_err(LOGDEV, + "%s: BL versions mismatch: ic=%d, image=%d\n", + __func__, fwu->bl_version, fwu->img.bl_version); + retval = -EINVAL; + goto exit; + } + + if (!fwu->force_update && fwu->new_partition_table) { + dev_err(LOGDEV, + "%s: Partition table mismatch\n", + __func__); + retval = -EINVAL; + goto exit; + } + } + + retval = fwu_read_flash_status(fwu); + if (retval < 0) + goto exit; + + if (fwu->in_bl_mode) { + sema_clear(&fwu->irq_sema); + dev_info(LOGDEV, + "%s: Device in bootloader mode\n", + __func__); + } + + if (fwu->do_lockdown && (fwu->img.lockdown.data != NULL)) { + switch (fwu->bl_version) { + case BL_V5: + case BL_V6: + retval = fwu_do_lockdown(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to do lockdown\n", + __func__); + } + fwu_reset_device(fwu); + break; + default: + break; + } + } + + flash_area = fwu_go_nogo(fwu); + dev_dbg(LOGDEV, "%s: flash_area = %d\n", __func__, flash_area); + + if (flash_area != NONE) { + retval = fwu_enter_flash_prog(fwu); + if (retval < 0) { + fwu_reset_device(fwu); + goto exit; + } + + } + + rmi4_data->set_state(rmi4_data, STATE_FLASH); + + switch (flash_area) { + case UI_FIRMWARE: + retval = fwu_do_reflash(fwu); + break; + case UI_CONFIG: + retval = fwu_check_ui_configuration_size(fwu); + if (retval < 0) + break; + fwu->config_area = UI_CONFIG_AREA; + retval = fwu_erase_configuration(fwu); + if (retval < 0) + break; + retval = fwu_write_ui_configuration(fwu); + break; + case NONE: + default: + break; + } + + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to do reflash\n", + __func__); + } + +exit: + if (fw_entry) + release_firmware(fw_entry); + + fwu_irq_enable(fwu, false); + rmi4_data->set_state(rmi4_data, STATE_UNKNOWN); + fwu_reset_device(fwu); + pr_notice("%s: End of reflash process\n", __func__); + + /* Rescan PDT after flashing and before register access */ + retval = fwu_scan_pdt(fwu); + if (retval < 0) + dev_err(LOGDEV, "%s: Failed to scan PDT\n", __func__); + + if (!fwu->in_ub_mode) { + retval = fwu_read_f34_queries(fwu); + if (retval < 0) + dev_err(LOGDEV, "%s: Failed to query F34\n", __func__); + } + + fwu_check_intr_en(rmi4_data, &rmi4_data->intr_mask[0], + rmi4_data->num_of_intr_regs); + + retval = fwu_get_device_config_id(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read device config ID\n", + __func__); + } + + memcpy(rmi4_data->rmi4_mod_info.config_id, fwu->config_id, + V5V6_CONFIG_ID_SIZE); + + rmi4_data->ready_state(rmi4_data, false); + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + __pm_relax(&fwu->flash_wake_src); + + return retval; +} + +static int fwu_recovery_check_status(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char base; + unsigned char status; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f35_fd.data_base_addr; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + F35_ERROR_CODE_OFFSET, + &status, + 1); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read status\n", + __func__); + return retval; + } + + status = status & MASK_7BIT; + + if (status != 0x00) { + dev_err(LOGDEV, + "%s: Recovery mode status = %d\n", + __func__, status); + return -EINVAL; + } + + return 0; +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static int fwu_recovery_erase_all( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char base; + unsigned char command = CMD_F35_ERASE_ALL; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f35_fd.ctrl_base_addr; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + F35_CHUNK_COMMAND_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to issue erase all command\n", + __func__); + return retval; + } + + msleep(F35_ERASE_ALL_WAIT_MS); + + retval = fwu_recovery_check_status(fwu); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_recovery_write_chunk( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char base; + unsigned char chunk_number[] = {0, 0}; + unsigned char chunk_spare; + unsigned char chunk_size; + unsigned char buf[F35_CHUNK_SIZE + 1]; + unsigned short chunk; + unsigned short chunk_total; + unsigned short bytes_written = 0; + unsigned char *chunk_ptr = (unsigned char *)fwu->image; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f35_fd.ctrl_base_addr; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + F35_CHUNK_NUM_LSB_OFFSET, + chunk_number, + sizeof(chunk_number)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write chunk number\n", + __func__); + return retval; + } + + buf[sizeof(buf) - 1] = CMD_F35_WRITE_CHUNK; + + chunk_total = fwu->image_size / F35_CHUNK_SIZE; + chunk_spare = fwu->image_size % F35_CHUNK_SIZE; + if (chunk_spare) + chunk_total++; + + for (chunk = 0; chunk < chunk_total; chunk++) { + if (chunk_spare && chunk == chunk_total - 1) + chunk_size = chunk_spare; + else + chunk_size = F35_CHUNK_SIZE; + + memset(buf, 0x00, F35_CHUNK_SIZE); + secure_memcpy(buf, sizeof(buf), chunk_ptr, + fwu->image_size - bytes_written, + chunk_size); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + F35_CHUNK_DATA_OFFSET, + buf, + sizeof(buf)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write chunk data (chunk %d)\n", + __func__, chunk); + return retval; + } + chunk_ptr += chunk_size; + bytes_written += chunk_size; + } + + retval = fwu_recovery_check_status(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write chunk data\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_recovery_reset( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char base; + unsigned char command = CMD_F35_RESET; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f35_fd.ctrl_base_addr; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + F35_CHUNK_COMMAND_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to issue reset command\n", + __func__); + return retval; + } + + msleep(F35_RESET_WAIT_MS); + + return 0; +} + +static int fwu_start_recovery( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + pr_notice("%s: Start of recovery process\n", __func__); + + __pm_stay_awake(&fwu->flash_wake_src); + + retval = rmi4_data->irq_enable(rmi4_data, false); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to disable interrupt\n", + __func__); + goto exit; + } + + msleep(INT_DISABLE_WAIT_MS); + + fwu_irq_enable(fwu, true); + + retval = fwu_recovery_erase_all(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to do erase all in recovery mode\n", + __func__); + goto exit; + } + + pr_notice("%s: External flash erased\n", __func__); + + retval = fwu_recovery_write_chunk(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write chunk data in recovery mode\n", + __func__); + goto exit; + } + + pr_notice("%s: Chunk data programmed\n", __func__); + + retval = fwu_recovery_reset(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to reset device in recovery mode\n", + __func__); + goto exit; + } + + pr_notice("%s: Recovery mode reset issued\n", __func__); + + + retval = 0; + +exit: + rmi4_data->set_state(rmi4_data, STATE_UNKNOWN); + fwu_reset_device(fwu); + fwu_irq_enable(fwu, false); + __pm_relax(&fwu->flash_wake_src); + + pr_notice("%s: End of recovery process\n", __func__); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + rmi4_data->ready_state(rmi4_data, false); + + return retval; +} +#endif + +int synaptics_fw_updater( + struct synaptics_rmi4_fwu_handle *fwu, + const unsigned char *fw_data) +{ + int retval; + + if (!fwu) + return -ENODEV; + + if (!fwu->initialized) + return -ENODEV; + + if (fwu->in_ub_mode) + return -ENODEV; + + fwu->image = fw_data; + + retval = fwu_start_reflash(fwu); + + fwu->image = NULL; + + return retval; +} +EXPORT_SYMBOL(synaptics_fw_updater); + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static ssize_t fwu_sysfs_show_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + + if (count < fwu->config_size) { + dev_err(LOGDEV, + "%s: Not enough space (%zd bytes) in buffer\n", + __func__, count); + return -EINVAL; + } + + retval = secure_memcpy(buf, count, fwu->read_config_buf, + fwu->read_config_buf_size, fwu->config_size); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to copy config data\n", + __func__); + return retval; + } + + return fwu->config_size; +} + +static ssize_t fwu_sysfs_store_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + + retval = secure_memcpy(&fwu->ext_data_source[fwu->data_pos], + fwu->image_size - fwu->data_pos, buf, count, count); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to copy image data\n", + __func__); + return retval; + } + + fwu->data_pos += count; + + return count; +} + +static ssize_t fwu_sysfs_do_recovery_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (!fwu->in_ub_mode) { + dev_err(LOGDEV, + "%s: Not in microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->ext_data_source) + return -EINVAL; + else + fwu->image = fwu->ext_data_source; + + retval = fwu_start_recovery(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to do recovery\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + return retval; +} +#endif + +static ssize_t fwu_sysfs_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + char prefix[SYNAPTICS_RMI4_FILENAME_SIZE] = "synaptics"; + char template[SYNAPTICS_RMI4_FILENAME_SIZE]; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + if (count > MAX_IMAGE_NAME_LEN) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: FW filename is too long\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->force_update) { + if (strncmp(buf, prefix, + strnlen(prefix, sizeof(prefix)))) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: FW does not belong to Synaptics\n", + __func__); + retval = -EINVAL; + goto exit; + } + + snprintf(template, sizeof(template), "-%s-", + rmi->product_id_string); + if (!strnstr(buf + strnlen(prefix, sizeof(prefix)), template, + count)) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: FW does not belong to %s\n", + __func__, + rmi->product_id_string); + retval = -EINVAL; + goto exit; + } + } + + strlcpy(fwu->image_name, buf, count); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: FW filename: %s\n", + __func__, + fwu->image_name); + + retval = synaptics_fw_updater(fwu, fwu->ext_data_source); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to do reflash\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + fwu->image_name[0] = 0; + fwu->force_update = FORCE_UPDATE; + fwu->do_lockdown = DO_LOCKDOWN; + return retval; +} + +static ssize_t fwu_sysfs_force_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + fwu->force_update = true; + + return count; +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static ssize_t fwu_sysfs_write_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (input != 1) { + retval = -EINVAL; + goto exit; + } + + if (fwu->in_ub_mode) { + dev_err(LOGDEV, + "%s: In microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->ext_data_source) + return -EINVAL; + else + fwu->image = fwu->ext_data_source; + + retval = fwu_start_write_config(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write config\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + return retval; +} + +static ssize_t fwu_sysfs_read_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + if (fwu->in_ub_mode) { + dev_err(LOGDEV, + "%s: In microbootloader mode\n", + __func__); + return -EINVAL; + } + + retval = fwu_do_read_config(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read config\n", + __func__); + return retval; + } + + return count; +} + +static ssize_t fwu_sysfs_config_area_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + unsigned long config_area; + + retval = sstrtoul(buf, 10, &config_area); + if (retval) + return retval; + + fwu->config_area = config_area; + + return count; +} + +static ssize_t fwu_sysfs_image_name_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + + retval = secure_memcpy(fwu->image_name, MAX_IMAGE_NAME_LEN, + buf, count, count); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to copy image file name\n", + __func__); + return retval; + } + + return count; +} + +static ssize_t fwu_sysfs_image_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + unsigned long size; + + retval = sstrtoul(buf, 10, &size); + if (retval) + return retval; + + fwu->image_size = size; + fwu->data_pos = 0; + + kfree(fwu->ext_data_source); + fwu->ext_data_source = kzalloc(fwu->image_size, GFP_KERNEL); + if (!fwu->ext_data_source) { + dev_err(LOGDEV, + "%s: Failed to alloc mem for image data\n", + __func__); + return -ENOMEM; + } + + return count; +} + +static ssize_t fwu_sysfs_block_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->block_size); +} + +static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.ui_firmware); +} + +static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.ui_config); +} + +static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.dp_config); +} + +static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.pm_config); +} + +static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.bl_config); +} + +static ssize_t fwu_sysfs_guest_code_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.guest_code); +} + +static ssize_t fwu_sysfs_write_guest_code_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (input != 1) { + retval = -EINVAL; + goto exit; + } + + if (fwu->in_ub_mode) { + dev_err(LOGDEV, + "%s: In microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->ext_data_source) + return -EINVAL; + else + fwu->image = fwu->ext_data_source; + + retval = fwu_start_write_guest_code(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write guest code\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + return retval; +} +#endif + +static ssize_t fwu_sysfs_erase_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + unsigned int input; + int retval; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + __pm_stay_awake(&fwu->flash_wake_src); + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + if (!fwu->in_bl_mode) { + fwu_irq_enable(fwu, true); + + retval = fwu_write_f34_command(fwu, CMD_ENABLE_FLASH_PROG); + if (retval < 0) + goto reset_and_exit; + + retval = fwu_wait_for_idle(fwu, ENABLE_WAIT_MS); + fwu_irq_enable(fwu, false); + if (retval < 0 || !fwu->in_bl_mode) + goto reset_and_exit; + } + + rmi4_data->set_state(rmi4_data, STATE_INIT); + fwu_irq_enable(fwu, true); + + retval = fwu_erase_all(fwu); + if (retval < 0) + pr_err("%s: ERASE_ALL failed\n", __func__); + + fwu_irq_enable(fwu, false); + rmi4_data->set_state(rmi4_data, STATE_UNKNOWN); + +reset_and_exit: + fwu_reset_device(fwu); + pr_notice("%s: End of reflash process\n", __func__); + + /* Rescan PDT after flashing and before register access */ + retval = fwu_scan_pdt(fwu); + if (retval < 0) + dev_err(LOGDEV, "%s: Failed to scan PDT\n", __func__); + + if (!fwu->in_ub_mode) { + retval = fwu_read_f34_queries(fwu); + if (retval < 0) + dev_err(LOGDEV, "%s: Failed to query F34\n", __func__); + } + + rmi4_data->ready_state(rmi4_data, false); + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + __pm_relax(&fwu->flash_wake_src); + + return count; +} + +static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + + if (!fwu) + return; + + if (fwu->intr_mask & intr_mask) + fwu_read_flash_status(fwu); + + return; +} + +static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval, attr_count; + struct pdt_properties pdt_props; + + fwu->image_name = kzalloc(MAX_IMAGE_NAME_LEN, GFP_KERNEL); + if (!fwu->image_name) { + dev_err(LOGDEV, + "%s: Failed to alloc mem for image name\n", + __func__); + retval = -ENOMEM; + goto exit_free_fwu; + } + + sema_init(&fwu->irq_sema, 0); + mutex_init(&rmi4_data->rmi4_exp_init_mutex); + + retval = synaptics_rmi4_reg_read(rmi4_data, + PDT_PROPS, + pdt_props.data, + sizeof(pdt_props.data)); + if (retval < 0) { + dev_dbg(LOGDEV, + "%s: Failed to read PDT properties, assuming 0x00\n", + __func__); + } else if (pdt_props.has_bsr) { + dev_err(LOGDEV, + "%s: Reflash for LTS not currently supported\n", + __func__); + retval = -ENODEV; + goto exit_free_mem; + } + + retval = fwu_scan_pdt(fwu); + if (retval < 0) + goto exit_free_mem; + + if (!fwu->in_ub_mode) { + fwu_irq_enable(fwu, true); + retval = fwu_read_f34_queries(fwu); + fwu_irq_enable(fwu, false); + if (retval < 0) + goto exit_free_mem; + + retval = fwu_get_device_config_id(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read device config ID\n", + __func__); + goto exit_free_mem; + } + } + + fwu->force_update = FORCE_UPDATE; + fwu->do_lockdown = DO_LOCKDOWN; + + wakeup_source_init(&fwu->flash_wake_src, "synaptics_fw_flash"); + + fwu->initialized = true; + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI + retval = sysfs_create_bin_file(SYSFS_KOBJ, &dev_attr_data); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to create sysfs bin file\n", + __func__); + goto exit_free_mem; + } +#endif + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(SYSFS_KOBJ, &attrs[attr_count].attr); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto exit_remove_attrs; + } + } + + if (strncmp(bi_bootmode(), "mot-factory", strlen("mot-factory")) == 0) { + retval = sysfs_create_file(SYSFS_KOBJ, &erase_attr[0].attr); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to create erase sysfs attributes\n", + __func__); + } else + fwu->has_erase_all = true; + } + + rmi4_data->fwu_data = (void *)fwu; + + return 0; + +exit_remove_attrs: + for (attr_count--; attr_count >= 0; attr_count--) + sysfs_remove_file(SYSFS_KOBJ, &attrs[attr_count].attr); +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI + sysfs_remove_bin_file(SYSFS_KOBJ, &dev_attr_data); +#endif +exit_free_mem: + kfree(fwu->image_name); + +exit_free_fwu: + kfree(fwu); + rmi4_data->fwu_data = NULL; + + return retval; +} + +static void synaptics_rmi4_fwu_remove(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int attr_count; + + if (!fwu) + goto exit; + + wakeup_source_trash(&fwu->flash_wake_src); + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(SYSFS_KOBJ, &attrs[attr_count].attr); + } + + if (fwu->has_erase_all) + sysfs_remove_file(SYSFS_KOBJ, &erase_attr[0].attr); + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI + sysfs_remove_bin_file(SYSFS_KOBJ, &dev_attr_data); +#endif + +exit: + complete(&fwu->remove_complete); + + return; +} + +static int synaptics_rmi4_fwu_flash_status( + struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int istatus = 0, retval; + unsigned char status; + + if (fwu == NULL) + return -EINVAL; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->off.flash_status, + &status, + sizeof(status)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read flash status\n", __func__); + return retval; + } + istatus = status >> 7; + + return istatus; +} + +static int __init rmi4_fw_update_module_init(void) +{ + struct synaptics_rmi4_fwu_handle *fwu; + struct synaptics_rmi4_data *next = NULL; + + while ((next = synaptics_driver_getdata(next)) != NULL) { + fwu = kzalloc(sizeof(*fwu), GFP_KERNEL); + fwu->dev = &next->i2c_client->dev; + /* rmi4_data pointer set as drvdata to i2c device */ + next->fwu_data = (void *)fwu; + synaptics_rmi4_new_function(next, RMI_FW_UPDATER, true, + synaptics_rmi4_fwu_init, + synaptics_rmi4_fwu_remove, + synaptics_rmi4_fwu_attn, + synaptics_rmi4_fwu_flash_status, + IC_MODE_ANY); + } + return 0; +} + +static void __exit rmi4_fw_update_module_exit(void) +{ + struct synaptics_rmi4_fwu_handle *fwu; + struct synaptics_rmi4_data *next = NULL; + + while ((next = synaptics_driver_getdata(next)) != NULL) { + fwu = (struct synaptics_rmi4_fwu_handle *)next->fwu_data; + init_completion(&fwu->remove_complete); + synaptics_rmi4_new_function(next, RMI_FW_UPDATER, false, + synaptics_rmi4_fwu_init, + synaptics_rmi4_fwu_remove, + synaptics_rmi4_fwu_attn, + synaptics_rmi4_fwu_flash_status, + IC_MODE_ANY); + wait_for_completion(&fwu->remove_complete); + if (fwu->read_config_buf_size) + kfree(fwu->read_config_buf); + kfree(fwu->image_name); + kfree(fwu); + next->fwu_data = NULL; + } + return; +} + +module_init(rmi4_fw_update_module_init); +module_exit(rmi4_fw_update_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX FW Update Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_i2c.c b/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_i2c.c new file mode 100644 index 000000000000..30d49b4d2780 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_i2c.c @@ -0,0 +1,8227 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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 pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#ifdef pr_debug +#undef pr_debug +#define pr_debug pr_err +#endif +#ifdef dev_dbg +#undef dev_dbg +#define dev_dbg dev_err +#endif +#endif + +#include "synaptics_dsx_i2c.h" +#include "synaptics_dsx_dropbox.h" +#include "synaptics_dsx_control_access_block.h" + +static int drv_instance_counter; +static DEFINE_MUTEX(instances_mutex); +static LIST_HEAD(drv_instances_list); +static struct workqueue_struct *det_workqueue; + +struct synaptics_rmi4_data *synaptics_driver_getdata( + struct synaptics_rmi4_data *prev) +{ + struct synaptics_rmi4_data *next = NULL; + mutex_lock(&instances_mutex); + if (!prev) { + if (!list_empty(&drv_instances_list)) + next = list_first_entry(&drv_instances_list, + struct synaptics_rmi4_data, node); + } else { + if (!list_is_last(&prev->node, &drv_instances_list)) + next = list_next_entry(prev, node); + } + mutex_unlock(&instances_mutex); + pr_debug("instance ptr %p\n", next); + return next; +} +EXPORT_SYMBOL(synaptics_driver_getdata); + +static char *INSTANCE_RMI(char *string, + struct synaptics_rmi4_data *rmi4_data) +{ + char instance[64]; + snprintf(instance, sizeof(instance), "%s.%d", + string, rmi4_data->instance); + return kstrdup(instance, GFP_KERNEL); +} + +static char *INSTANCE_DBG(char *string, + struct synaptics_rmi4_data *rmi4_data) +{ + static char name_with_instance[64]; + snprintf(name_with_instance, sizeof(name_with_instance), "%s.%d", + string, rmi4_data->instance); + return name_with_instance; +} + +#define DRIVER_NAME "synaptics_mmi" +#define CLASS_PRIMARY_FNAME "primary" +#define INPUT_PHYS_NAME "synaptics_dsx_i2c/input" +#define TYPE_B_PROTOCOL + +#define RMI4_WAIT_READY 0 +#define RMI4_HW_RESET 1 +#define RMI4_SW_RESET 2 + +#define NO_0D_WHILE_2D +/* +#define REPORT_2D_Z +*/ +#define REPORT_2D_W + +#define RPT_TYPE (1 << 0) +#define RPT_X_LSB (1 << 1) +#define RPT_X_MSB (1 << 2) +#define RPT_Y_LSB (1 << 3) +#define RPT_Y_MSB (1 << 4) +#define RPT_Z (1 << 5) +#define RPT_WX (1 << 6) +#define RPT_WY (1 << 7) +#define RPT_DEFAULT (RPT_TYPE | RPT_X_LSB | RPT_X_MSB | RPT_Y_LSB | RPT_Y_MSB) + +#define EXP_FN_DET_INTERVAL 1000 /* ms */ +#define POLLING_PERIOD 1 /* ms */ +#define SYN_I2C_RETRY_TIMES 10 +#define MAX_ABS_MT_TOUCH_MAJOR 15 +#define SYN_MAX_BUTTONS 4 + +#define F01_STD_QUERY_LEN 21 +#define F01_BUID_ID_OFFSET 18 +#define F01_QUERY1_OFFSET 1 +#define F01_QUERY21_OFFSET 21 +#define F11_STD_QUERY_LEN 9 +#define F11_STD_CTRL_LEN 10 +#define F11_STD_DATA_LEN 12 +#define F12_STD_QUERY_LEN 10 +#define F12_STD_CTRL_LEN 4 +#define F12_STD_DATA_LEN 80 + +#define RESET_GPIO_NAME "touch_reset" +#define IRQ_GPIO_NAME "touch_irq" + +#define tk_debug(fmt, args...) + +#ifdef CONFIG_MMI_HALL_NOTIFICATIONS +#include + +static int folio_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); +#endif + +__attribute__((weak)) +int FPS_register_notifier(struct notifier_block *nb, unsigned long stype, bool report) +{ + return -ENODEV; +} + +__attribute__((weak)) +int FPS_unregister_notifier(struct notifier_block *nb, unsigned long stype) +{ + return -ENODEV; +} + +__attribute__((weak)) +int irq_can_set_affinity(unsigned int irq) +{ + return 0; +} + +static int fps_notifier_callback(struct notifier_block *self, + unsigned long event, void *data); + +static int synaptics_rmi4_hw_init(struct synaptics_rmi4_data *rmi4_data); + +static void synaptics_dsx_free_patch( + struct synaptics_dsx_patch *patch); +static struct synaptics_dsx_patch * + synaptics_dsx_init_patch(const char *name); +static int synaptics_rmi4_set_page( + struct synaptics_rmi4_data *rmi4_data, + unsigned int address); +static int control_access_block_update_static( + struct synaptics_rmi4_data *rmi4_data); +static int control_access_block_update_dynamic( + struct synaptics_rmi4_data *rmi4_data); +static int synaptics_rmi4_query_device( + struct synaptics_rmi4_data *rmi4_data); +static int synaptics_rmi4_remove(struct i2c_client *client); + +/* F12 packet register description */ +struct f12_c8_s0_type { + unsigned char max_x_lsb; + unsigned char max_x_msb; + unsigned char max_y_lsb; + unsigned char max_y_msb; +}; + +struct f12_c10_s0_type { + unsigned char noise_floor; + unsigned char min_peak_amplitude; + unsigned char peak_merge_threshold_lsb; + unsigned char peak_merge_threshold_msb; +}; + +struct f12_c15_s0_type { + unsigned char finger_threshold; + unsigned char small_finger_threshold; + unsigned char small_finger_border; + unsigned char negative_finger_threshold; +}; + +struct f12_c18_s0_type { + unsigned char zone0_x_lsb; + unsigned char zone0_x_msb; + unsigned char zone0_y_lsb; + unsigned char zone0_y_msb; + unsigned char zone1_x_lsb; + unsigned char zone1_x_msb; + unsigned char zone1_y_lsb; + unsigned char zone1_y_msb; + unsigned char max_timer; + unsigned char max_distance; +}; + +struct f12_c20_s0_type { + unsigned char x_suppression; + unsigned char y_suppression; +}; + +struct f12_c20_s1_type { + union { + struct { + unsigned char flags:1; + unsigned char wakeup_gesture_only:1; + unsigned char dribble:1; + unsigned char proximity_only:1; + unsigned char hover_swipe:1; + unsigned char proximity:1; + unsigned char hover_pinch:1; + unsigned char lp_proximity:1; + } __packed; + unsigned char report; + }; +}; + +struct f12_c23_s0_type { + union { + struct { + unsigned char finger:1; + unsigned char stylus:1; + unsigned char palm:1; + unsigned char unclassified:1; + unsigned char reserved:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct f12_c23_s1_type { + unsigned char max_num_reported_objects; +}; + +struct f12_c27_s0_type { + union { + struct { + unsigned char double_tap:1; + unsigned char swipe:1; + unsigned char tap_n_hold:1; + unsigned char circle:1; + unsigned char triangle:1; + unsigned char vee:1; + unsigned char unicode:1; + unsigned char reserved:1; + } __packed; + unsigned char wakeup_gesture; + }; +}; + +struct f12_c28_s0_type { + unsigned char reported_bytes_per_object; +}; + +struct f12_d1_s0_type { + unsigned char type_and_stylus; + unsigned char x_lsb; + unsigned char x_msb; + unsigned char y_lsb; + unsigned char y_msb; + unsigned char z; + unsigned char wx; + unsigned char wy; +}; + +/* RMI4 has the following wakeup gesture defined: + • 0x00 = No gesture + • 0x01 = One-Finger single tap + • 0x02 = One-Finger tap-and-hold + • 0x03 = One-Finger double tap + • 0x04 = One-Finger early tap + • 0x05 = One-Finger flick + • 0x06 = One-Finger press + • 0x07 = One-Finger swipe + • 0x08 = One-Finger Circle + • 0x09 = One-Finger Triangle + • 0x0A = One-Finger Vee + • 0x0B = Reserved + • 0x0C = Triple Tap + • 0x0D = Click + • 0x0E = Reserved + • 0x30 = Edge gesture + • 0x7F = Reserved + • 0x80 = Pinch + • 0x81 = Rotate + • 0x82 through 0xFF = Reserved +*/ + +#define DOUBLE_TAP_GESTURE 0x03 + +struct f12_d4_s0_type { + unsigned char gesture; +}; + +struct f12_d15_s0_type { + unsigned char attn[2]; +}; + +/* F01 packet register description */ +struct f01_c0_s0_type { + union { + struct { + unsigned char sleep_mode:2; + unsigned char no_sleep:1; + unsigned char reserved:2; + unsigned char charger_connected:1; + unsigned char report_rate:1; + unsigned char configured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f01_c2_s0_type { + unsigned char doze_interval; +}; + +struct f01_c3_s0_type { + unsigned char wakeup_threshold; +}; + +struct f01_c5_s0_type { + unsigned char doze_holdoff; +}; + +struct f01_c9_s0_type { + unsigned char recalibration_interval; +}; + +/* F51 packet register description */ +struct f51_d0_s0_type { + union { + struct { + unsigned char noise_state:1; + unsigned char gear_change:1; + unsigned char guard_state:1; + unsigned char unused:5; + unsigned char md_present:1; + unsigned char reserved:7; + } __packed; + unsigned char data[2]; + }; +}; + +struct f51_c0_s0_type { + union { + struct { + unsigned char cid_check_enable:1; + unsigned char fsim_clear_enable:1; + unsigned char nsm_intr_enable:1; + unsigned char unused:5; + } __packed; + unsigned char data[1]; + }; +}; + +struct f51_c1_s0_type { + struct { + unsigned char dyn_jitter_strength; + unsigned char dyn_jitter_gain; + unsigned char dyn_jitter_step_size; + } __packed; +}; + +struct f51_c4_s0_type { + union { + struct { + unsigned char md_feature_enable:1; + unsigned char md_no_relax:1; + unsigned char md_uncond_shutoff:1; + unsigned char unused:5; + } __packed; + unsigned char data[1]; + }; + unsigned char md_min_threshold; + unsigned char md_max_threshold; + unsigned char md_2d_area; + unsigned char md_frame_count_in; + unsigned char md_frame_count_out; +}; + +/* F54 packet register description */ +struct f54_q12_s0_type { + union { + struct { + unsigned char num_of_scan_freq:4; + unsigned char reserved:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_c0_s0_type { + union { + struct { + unsigned char no_relax:1; + unsigned char no_scan:1; + unsigned char force_fast_relax:1; + unsigned char startup_fast_relax:1; + unsigned char gest_cancels_fast_relax:1; + unsigned char energy_ratio_relax:1; + unsigned char exces_noise_reporting:1; + unsigned char enable_cap_correction:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_c2_s0_type { + struct { + unsigned short saturation_cap; + } __packed; +}; + +struct f54_d6_s0_type { + union { + struct { + unsigned char interference_metric_lsb; + unsigned char interference_metric_msb; + } __packed; + unsigned char data[2]; + }; +}; + +struct f54_d10_s0_type { + unsigned char noise_state; +}; + +struct f54_d14_s0_type { + union { + struct { + unsigned char cid_im_lsb; + unsigned char cid_im_msb; + } __packed; + unsigned char data[2]; + }; +}; + +struct f54_d16_s0_type { + union { + struct { + unsigned char freq_scan_im_lsb; + unsigned char freq_scan_im_msb; + } __packed; + unsigned char data[2]; + }; +}; + +struct f54_d17_s0_type { + union { + struct { + unsigned char freq:7; + unsigned char inhibit_freq_shift:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_m0_s0_type { + unsigned char command; +}; + +#define CTRL_TYPE (0 << 8) +#define DATA_TYPE (1 << 8) +#define QUERY_TYPE (2 << 8) +#define COMMAND_TYPE (4 << 8) + +/*subpkt data type*/ +#define DYNA_RMI4_SUBPKT(a)\ +do {\ + s->present = 0;\ + s->expected = 1;\ + s->size = sizeof(struct a);\ + s->data = kzalloc(sizeof(struct a), GFP_KERNEL);\ + if (!s->data)\ + goto alloc_error_subpkt;\ + dev_dbg(&rmi4_data->i2c_client->dev, "allocated %lu bytes of subptk data\n", sizeof(struct a));\ +} while(0) + +/*register number, number of subpkts, register size*/ +#define DYNA_RMI4_REG(n, s, rsz)\ +do {\ + r->r_number = n;\ + r->offset = -1;\ + r->updated = 0;\ + r->modified = 0;\ + r->expected = 1;\ + r->size = rsz;\ + r->nr_subpkts = s;\ + r->subpkt = kzalloc((s)*sizeof(struct synaptics_rmi4_subpkt), GFP_KERNEL);\ + if (!r->subpkt)\ + goto alloc_error_reg;\ + dev_dbg(&rmi4_data->i2c_client->dev, "allocated %d subptks for reg %d\n", s, n);\ +} while(0) + +/*func number, base address, offset, number of registers*/ +#define DYNA_RMI4_FUNC(n, a, o, r)\ +do {\ + dev_dbg(&rmi4_data->i2c_client->dev, "config_regs for func[%02x:%d] = %p\n", (n & 0xff), (n >> 8), f);\ + f->f_number = n;\ + f->base_addr = a;\ + f->query_offset = o;\ + f->nr_regs = r;\ + f->regs = kzalloc((r)*sizeof(struct synaptics_rmi4_packet_reg), GFP_KERNEL);\ + if (!f->regs)\ + goto alloc_error_func;\ + dev_dbg(&rmi4_data->i2c_client->dev, "allocated %d regs for func[%02x:%d]\n", r, (n & 0xff), (n >> 8));\ +} while(0) + +#define RMI4_GET_FUNC(n) (&rmi4_data->config_regs[n]) +#define RMI4_GET_PKT_REG(n) (&f->regs[n]) +#define RMI4_GET_SUBPKT(n) (&r->subpkt[n]) + +#define RMI4_DECLARE_FUNC(nf, addr, off, nr) \ +do {\ + f = RMI4_GET_FUNC(fnum++); snum = 0; rnum = 0;\ + DYNA_RMI4_FUNC(nf, addr, off, nr);\ +} while(0) + +#define RMI4_DECLARE_REG_NO_ALLOC(rn, ns) \ +do {\ + r = RMI4_GET_PKT_REG(rnum++); snum = 0;\ + DYNA_RMI4_REG(rn, ns, 0);\ +} while(0) + +#define RMI4_DECLARE_REG_ONE_SUBPKT(rn, prefix) \ +do {\ + r = RMI4_GET_PKT_REG(rnum++); snum = 0;\ + DYNA_RMI4_REG(rn, 1, sizeof(struct prefix##_s0_type));\ + s = RMI4_GET_SUBPKT(0);\ + DYNA_RMI4_SUBPKT(prefix##_s0_type);\ +} while(0) + +/*subpkt data type*/ +#define RMI4_DECLARE_SUBPKT(subpkt_data_type) \ +do {\ + s = RMI4_GET_SUBPKT(snum++);\ + DYNA_RMI4_SUBPKT(subpkt_data_type);\ +} while(0) + +#define NO_MATTER 0 +#define SLOT_IN_USE (rmi4_data->config_regs[i].f_number) + +static int synaptics_dsx_config_regs_alloc(struct synaptics_rmi4_data *rmi4_data) +{ + int i, snum, rnum, fnum = 0; + struct synaptics_rmi4_func_packet_regs *f; + struct synaptics_rmi4_packet_reg *r; + struct synaptics_rmi4_subpkt *s; + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F01 | CTRL_TYPE), 0, NO_MATTER, 5); + RMI4_DECLARE_REG_ONE_SUBPKT(0, f01_c0); + RMI4_DECLARE_REG_ONE_SUBPKT(2, f01_c2); + RMI4_DECLARE_REG_ONE_SUBPKT(3, f01_c3); + RMI4_DECLARE_REG_ONE_SUBPKT(5, f01_c5); + RMI4_DECLARE_REG_ONE_SUBPKT(9, f01_c9); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F12 | CTRL_TYPE), 0, 4, 8); + RMI4_DECLARE_REG_ONE_SUBPKT( 8, f12_c8); + RMI4_DECLARE_REG_ONE_SUBPKT(10, f12_c10); + RMI4_DECLARE_REG_ONE_SUBPKT(15, f12_c15); + RMI4_DECLARE_REG_ONE_SUBPKT(18, f12_c18); + RMI4_DECLARE_REG_NO_ALLOC(20, 2); + RMI4_DECLARE_SUBPKT(f12_c20_s0_type); + RMI4_DECLARE_SUBPKT(f12_c20_s1_type); + RMI4_DECLARE_REG_NO_ALLOC(23, 2); + RMI4_DECLARE_SUBPKT(f12_c23_s0_type); + RMI4_DECLARE_SUBPKT(f12_c23_s1_type); + RMI4_DECLARE_REG_ONE_SUBPKT(27, f12_c27); + RMI4_DECLARE_REG_ONE_SUBPKT(28, f12_c28); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F12 | DATA_TYPE), 0, 7, 3); + RMI4_DECLARE_REG_NO_ALLOC(1, 10); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_REG_ONE_SUBPKT( 4, f12_d4); + RMI4_DECLARE_REG_ONE_SUBPKT(15, f12_d15); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F54 | DATA_TYPE), 0, NO_MATTER, 5); + RMI4_DECLARE_REG_ONE_SUBPKT( 6, f54_d6); + RMI4_DECLARE_REG_ONE_SUBPKT(10, f54_d10); + RMI4_DECLARE_REG_ONE_SUBPKT(14, f54_d14); + RMI4_DECLARE_REG_ONE_SUBPKT(16, f54_d16); + RMI4_DECLARE_REG_ONE_SUBPKT(17, f54_d17); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F54 | CTRL_TYPE), 0, NO_MATTER, 3); + RMI4_DECLARE_REG_ONE_SUBPKT(0, f54_c0); + RMI4_DECLARE_REG_ONE_SUBPKT(2, f54_c2); + RMI4_DECLARE_REG_NO_ALLOC(95, 8); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F54 | COMMAND_TYPE), 0, NO_MATTER, 1); + RMI4_DECLARE_REG_ONE_SUBPKT(0, f54_m0); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F54 | QUERY_TYPE), 0, NO_MATTER, 1); + RMI4_DECLARE_REG_ONE_SUBPKT(12, f54_q12); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F51 | CTRL_TYPE), 0, NO_MATTER, 3); + RMI4_DECLARE_REG_ONE_SUBPKT(0, f51_c0); + RMI4_DECLARE_REG_ONE_SUBPKT(1, f51_c1); + RMI4_DECLARE_REG_ONE_SUBPKT(4, f51_c4); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F51 | DATA_TYPE), 0, NO_MATTER, 1); + RMI4_DECLARE_REG_ONE_SUBPKT(0, f51_d0); + + return 0; + +alloc_error_subpkt: +alloc_error_reg: +alloc_error_func: + + for (i = 0; i < MAX_CONFIG_REGS && SLOT_IN_USE; i++) { + int j, k; + + f = &rmi4_data->config_regs[i]; + if (!f->nr_regs) + continue; + for (j = 0; j < f->nr_regs; j++) { + if (!f->regs[j].r_number) + break; + r = &f->regs[j]; + for (k = 0 ; k < r->nr_subpkts; k++) { + if (!r->subpkt[k].size) + break; + kfree(r->subpkt[k].data); + } + kfree(r->subpkt); + kfree(r->data); + } + kfree(f->regs); + memset(f, 0, sizeof(*f)); + } + + return -ENOMEM; +} + +static unsigned char power_sleep_value = 1; +static struct synaptics_dsx_func_patch power_sleep_func_patch = { + .func = SYNAPTICS_RMI4_F01, + .regstr = 0, + .subpkt = 0, + .size = 1, + .bitmask = 7, + .data = &power_sleep_value, +}; + +static unsigned char force_update_value = 4; +static struct synaptics_dsx_func_patch force_update_func_patch = { + .func = SYNAPTICS_RMI4_F54 | COMMAND_TYPE, + .regstr = 0, + .subpkt = 0, + .size = 1, + .bitmask = 0, + .data = &force_update_value, +}; + +static struct synaptics_dsx_patch *force_update_patch; +static struct synaptics_dsx_patch *power_sleep_patch; + +static inline int register_ascii_to_type(unsigned char *symbol) +{ + int reg_type = CTRL_TYPE; + switch (*symbol) { + case 'Q': + reg_type = QUERY_TYPE; + break; + case 'D': + reg_type = DATA_TYPE; + break; + case 'M': + reg_type = COMMAND_TYPE; + break; + case 'C': + break; + default: return reg_type; + } + *symbol = '\0'; + return reg_type; +} + +static inline unsigned char register_type_to_ascii(int type) +{ + unsigned char ascii; + switch (type) { + case QUERY_TYPE: + ascii = 'Q'; + break; + case DATA_TYPE: + ascii = 'D'; + break; + case COMMAND_TYPE: + ascii = 'M'; + break; + case CTRL_TYPE: + default: + ascii = 'C'; + } + return ascii; +} + +#define F12_D1_IDX 0 +#define F12_D4_IDX 1 +#define F12_D15_IDX 2 + +static char *synaptics_dsx_find_patch(char *head, char *delimiters, char **next) +{ + char *patch = head; + for (; patch; patch = *next) { + *next = strpbrk(patch, delimiters); + if (*next) + *(*next)++ = '\0'; + patch = skip_spaces(patch); + if (!patch || !*patch || *patch == '#') + continue; + else + break; + } + return patch; +} + +static int parse_patch_data(char *value_p, u8 data[], long *bitmask_v) +{ + int i, error, num_of_bytes = 1; + long value_v; + char *bitmask_p; + + bitmask_p = strpbrk(value_p, "&"); + if (bitmask_p) { + *bitmask_p++ = '\0'; + error = kstrtol(bitmask_p, 16, bitmask_v); + if (error) + pr_err("bitmask conv error\n"); + } + + /* detect multiple bytes data */ + if ((strlen(value_p)%2) == 0) + num_of_bytes = strlen(value_p)/2; + + for (i = 0; i < num_of_bytes; i++) { + char hex_val[3] = {0}; + hex_val[0] = *value_p; + hex_val[1] = *(value_p + 1); + error = kstrtol(hex_val, 16, &value_v); + if (error) + pr_err("value conv error\n"); + data[i] = (u8)value_v; + value_p += 2; + } + + return num_of_bytes; +} + +static int synaptics_dsx_parse_patch(int func, char *query, + struct synaptics_dsx_patch *patch_ptr, bool expect_data) +{ + int i, error, rt_mod, function, num_of_bytes; + u8 data[64]; + char *next, *subpkt_p, *value_p, *pair = query; + long regstr_v, bitmask_v, subpkt_v; + struct synaptics_dsx_func_patch *patch; + + rt_mod = func & 0xf00; + function = func & 0xff; + for (i = 0; pair; pair = next, i++) { + num_of_bytes = 0; + bitmask_v = subpkt_v = 0L; + + pair = synaptics_dsx_find_patch(pair, ",", &next); + + value_p = strpbrk(pair, "="); + if (!value_p) { + pr_err("F%x[%d]: invalid syntax '%s'\n", + function, i, pair); + continue; + } + + /* make sure string is null terminated */ + *value_p = '\0'; + + subpkt_p = strpbrk(pair, ":"); + if (subpkt_p) { + *subpkt_p++ = '\0'; + error = kstrtol(subpkt_p, 10, &subpkt_v); + if (error) { + pr_err("F%x[%d]: subpacket conv error\n", + function, i); + continue; + } + } + + error = kstrtol(pair, 10, ®str_v); + if (error) { + pr_err("F%x[%d]: register conv error\n", + function, i); + continue; + } + + if (expect_data) + num_of_bytes = parse_patch_data( + ++value_p, data, &bitmask_v); + /* primitive data validation */ + if (function <= 0 || function > 255 || + regstr_v < 0 || regstr_v > 255) { + pr_err("F%x[%d]: invalid values\n", function, i); + continue; + } + + patch = kzalloc(sizeof(*patch), GFP_KERNEL); + if (!patch) { + pr_err("failed to alloc mem\n"); + return -ENOMEM; + } + + if (expect_data) { + patch->data = kzalloc(num_of_bytes, GFP_KERNEL); + if (!patch->data) { + pr_err("failed to alloc mem\n"); + return -ENOMEM; + } + memcpy(patch->data, data, num_of_bytes); + } + + patch->func = (u16)func; + patch->regstr = (u8)regstr_v; + patch->subpkt = (u8)subpkt_v; + patch->size = (u8)num_of_bytes; + patch->bitmask = (u8)bitmask_v; + + pr_debug("F%x[%d], register %d, subpkt %d, size %d, mask %x\n", + function, i, patch->regstr, patch->subpkt, + patch->size, patch->bitmask); + patch_ptr->cfg_num++; + list_add_tail(&patch->link, &patch_ptr->cfg_head); + } + + return 0; +} + +/* + * Special settings passed to the driver via device tree + * Example: "F12@11:2=1fa0;F1@1=1;" + * Where: + * F12 - decimal object number + * @ - delimits function number and following patch sets + * 11:2 - decimal register and subpacket numbers (0 offset) + * 26=a - register number and hex value to assign + * 2=1fa0 - 2 bytes of data to write to the register + */ +static void synaptics_dsx_parse_string(struct synaptics_rmi4_data *data, + const char *patch_ptr, + struct synaptics_dsx_patch *patch, + bool expect_data) +{ + struct device *dev = &data->i2c_client->dev; + long number_v; + char *patch_string; + char *config_p, *next, *patch_set; + int i, error, rt_mod = 0; + + patch_string = kstrdup(patch_ptr, GFP_KERNEL); + for (i = 0, patch_set = patch_string; patch_set; patch_set = next) { + patch_set = synaptics_dsx_find_patch(patch_set, ";\n", &next); + if (!patch_set) + break; + + dev_dbg(dev, "patch set %d: \"%s\"\n", i, patch_set); + config_p = strpbrk(patch_set, "@"); + + /* consider force update and power seetings here */ + if (!config_p) { + switch (*patch_set) { + case 'S': + patch->flags |= FLAG_POWER_SLEEP; + break; + case 'U': + patch->flags |= FLAG_FORCE_UPDATE; + break; + case 'W': + patch->flags |= FLAG_WAKEABLE; + break; + } + continue; + } + + if (*patch_set != 'F' && *patch_set != 'f') { + dev_err(dev, "invalid syntax '%s'\n", patch_set); + continue; + } + + rt_mod = register_ascii_to_type(config_p-1); + dev_dbg(dev, "register type modifier %d\n", rt_mod); + + /* strip non digits */ + *config_p++ = '\0'; + + error = kstrtol(++patch_set, 16, &number_v); + if (error) { + dev_err(dev, "kstrtol error %d\n", error); + continue; + } + + error = synaptics_dsx_parse_patch((int)number_v + rt_mod, + config_p, patch, expect_data); + if (error < 0) { + dev_err(dev, "invalid patch; parse error %d\n", error); + continue; + } + + i++; + } + kfree(patch_string); + if (patch->cfg_num) + dev_info(dev, "processed %d patch sets for %d functions\n", + patch->cfg_num, i); + else + dev_info(dev, "no valid patch sets found\n"); +} + +#define LAST_SUBPACKET_ROW_IND_MASK 0x80 +#define NR_SUBPKT_PRESENCE_BITS 7 + +static struct synaptics_rmi4_func_packet_regs *find_function( + struct synaptics_rmi4_data *rmi4_data, int f_number) +{ + struct synaptics_rmi4_func_packet_regs *regs = NULL; + int i; + for (i = 0; i < MAX_CONFIG_REGS && SLOT_IN_USE; i++) { + if (rmi4_data->config_regs[i].f_number == f_number) { + regs = &rmi4_data->config_regs[i]; + break; + } + } + return regs; +} + +static struct synaptics_rmi4_packet_reg *find_packet_reg( + struct synaptics_rmi4_func_packet_regs *regs, int number) +{ + struct synaptics_rmi4_packet_reg *reg = NULL; + int r; + + for (r = 0; r < regs->nr_regs; r++) + if (number == regs->regs[r].r_number) { + reg = ®s->regs[r]; + break; + } + return reg; +} + +static struct synaptics_rmi4_subpkt *find_subpkt( + struct synaptics_rmi4_func_packet_regs *regs, int nr, int ns, + struct synaptics_rmi4_packet_reg **reg, void **data) +{ + struct synaptics_rmi4_packet_reg *lreg = find_packet_reg(regs, nr); + struct synaptics_rmi4_subpkt *subpkt = NULL; + + if (reg) + *reg = lreg; + + if (lreg && ns < lreg->nr_subpkts) { + subpkt = &lreg->subpkt[ns]; + if (subpkt->present && data) + *data = lreg->subpkt[ns].data; + } + return subpkt; +} + +int synaptics_rmi4_scan_f12_reg_info( + struct synaptics_rmi4_data *rmi4_data, + unsigned short query_addr, + unsigned short regs_base_addr, + struct synaptics_rmi4_func_packet_regs *regs) +{ + unsigned char sz, mask; + int ii, jj, r, s, retval, max_reg_number; + unsigned short r_offset; + unsigned short addr = query_addr; + unsigned char data[255]; + bool r_exist[128] = {false}; + + for (r = 0; r < regs->nr_regs; ++r) { + regs->regs[r].offset = -1; + regs->regs[r].size = 0; + /* reg data can be re-allocated */ + kfree(regs->regs[r].data); + for (s = 0; s < regs->regs[r].nr_subpkts; ++s) { + regs->regs[r].subpkt[s].present = 0; + if (regs->regs[r].subpkt[s].data && + regs->regs[r].subpkt[s].size) + /* subpkt data cannot be re-allocated */ + memset(regs->regs[r].subpkt[s].data, 0, + regs->regs[r].subpkt[s].size); + } + } + + regs->base_addr = regs_base_addr; + retval = rmi4_data->i2c_read(rmi4_data, addr, &sz, 1); + dev_dbg(&rmi4_data->i2c_client->dev, + "size of reg presence = %d, addr 0x%x\n", sz, addr); + if (retval < 0) + return retval; + if (!sz) + return -EIO; + /* Scan register presence */ + retval = rmi4_data->i2c_read(rmi4_data, ++addr, data, sz); + if (retval < 0) + return retval; + if (!data[0]) { + dev_err(&rmi4_data->i2c_client->dev, + "packet register size greater 255 bytes not supported\n"); + return -ENOSYS; + } + ii = 1; + for (r = 0, r_offset = 0; ii < sz; ++ii) { + dev_dbg(&rmi4_data->i2c_client->dev, + "reg presence [%d] = 0x%02x\n", ii, data[ii]); + for (jj = 0, mask = 1; jj < 8; ++jj, ++r, mask <<= 1) { + struct synaptics_rmi4_packet_reg *reg = + find_packet_reg(regs, r); + bool present = (data[ii] & mask) != 0; + bool allocated = reg ? true : false; + if (present != allocated) + dev_dbg(&rmi4_data->i2c_client->dev, + " reg: r%d is%s present, but was%s expected\n", r, + present ? "" : " NOT", + allocated ? "" : " NOT"); + if (!present) { + r_exist[r] = false; + continue; + } + r_exist[r] = true; + if (allocated) + reg->offset = r_offset; + dev_dbg(&rmi4_data->i2c_client->dev, + " r%d offset = %d\n", r, r_offset); + r_offset++; + } + } + + max_reg_number = r; + dev_dbg(&rmi4_data->i2c_client->dev, + "max register number = %d\n", max_reg_number); + + /* Scan register size and subpacket presence*/ + sz = data[0]; + dev_dbg(&rmi4_data->i2c_client->dev, + "subpacket presence sz = %d, addr 0x%x\n", sz, addr+1); + retval = rmi4_data->i2c_read(rmi4_data, ++addr, data, sz); + if (retval < 0) + return retval; + for (r = 0, ii = 0; r < max_reg_number && ii < sz; ++r) { + unsigned int reg_size, expected_reg_size; + struct synaptics_rmi4_packet_reg *reg = + find_packet_reg(regs, r); + if (!r_exist[r]) { + dev_dbg(&rmi4_data->i2c_client->dev, + "r%d does not exist; go to the next...\n", r); + continue; + } + reg_size = data[ii++]; + if (!reg_size) { + dev_err(&rmi4_data->i2c_client->dev, + "packet register size greater 255 bytes not supported\n"); + return -ENOSYS; + } + if (reg && reg->offset != -1) { + reg->data = kzalloc(reg_size, GFP_KERNEL); + if (reg->data) { + reg->size = reg_size; + dev_dbg(&rmi4_data->i2c_client->dev, + "r%d allocated %d bytes buffer @%p\n", + reg->r_number, reg->size, reg->data); + } else + dev_err(&rmi4_data->i2c_client->dev, + "cannot alloc register data buffer\n"); + } + dev_dbg(&rmi4_data->i2c_client->dev, "r%d sz = %d\n", r, reg_size); + expected_reg_size = 0; + for (s = 0; ii < sz;) { + dev_dbg(&rmi4_data->i2c_client->dev, + " subpkt presence [%d] = 0x%02x\n", + ii, data[ii]); + for (jj = 0, mask = 1; jj < NR_SUBPKT_PRESENCE_BITS; + ++jj, ++s, mask <<= 1) { + struct synaptics_rmi4_subpkt *subpkt = NULL; + bool present = (data[ii] & mask) != 0; + bool expected = false; + if (reg && s < reg->nr_subpkts) { + subpkt = reg->subpkt + s; + expected = subpkt->expected; + } + if (!present || !expected) { + if (present != expected) + dev_dbg(&rmi4_data->i2c_client->dev, + " subpacket:" + " r%d s%d is%s present," + " but was%s expected\n", + r, s, + present ? "" : " NOT", + expected ? "" : " NOT"); + continue; + } + dev_dbg(&rmi4_data->i2c_client->dev, + " r%d.s%d is present\n", r, s); + if (subpkt) { + subpkt->present = true; + expected_reg_size += subpkt->size; + } + } + if ((data[ii++] & LAST_SUBPACKET_ROW_IND_MASK) == 0) { + dev_dbg(&rmi4_data->i2c_client->dev, "no more subpackets\n"); + break; + } + } + if (reg && reg->expected && reg->size != expected_reg_size) { + dev_dbg(&rmi4_data->i2c_client->dev, + " r%d size error: expected %d actual is %d\n", + r, expected_reg_size, reg->size); + } + } + return 0; +} + +int synaptics_rmi4_read_packet_reg( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *regs, int r_number) +{ + int s, retval, offset; + struct synaptics_rmi4_packet_reg *reg = find_packet_reg(regs, r_number); + + if (!reg || !reg->size) { + dev_err(&rmi4_data->i2c_client->dev, + "register %d not found or zero size\n", r_number); + return -EINVAL; + } + + if (reg->offset == -1) { + dev_err(&rmi4_data->i2c_client->dev, + "touch register error: can't read r%d - not present\n", + r_number); + return -ENOENT; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "F%02x%c reading r%d{%X}, size %d to @%p\n", + regs->f_number & 0xff, + register_type_to_ascii(regs->f_number & 0xf00), + reg->r_number, regs->base_addr + reg->offset, + reg->size, reg->data); + + retval = rmi4_data->i2c_read(rmi4_data, + regs->base_addr + reg->offset, + reg->data, reg->size); + if (retval < 0) + return retval; + + for (s = 0, offset = 0; s < reg->nr_subpkts; ++s) { + struct synaptics_rmi4_subpkt *subpkt = reg->subpkt + s; + if (!subpkt->present) + continue; + + if ((reg->size - offset) < subpkt->size) { + dev_err(&rmi4_data->i2c_client->dev, + "subpkt size error: expected %d bytes," + " only %d present\n", subpkt->size, + (reg->size - offset)); + break; + } + + memcpy(subpkt->data, reg->data+offset, subpkt->size); + offset += subpkt->size; +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + { + int kk; + dev_dbg(&rmi4_data->i2c_client->dev, + "read r%d.s%d =\n", reg->r_number, s); + for (kk = 0; kk < subpkt->size; ++kk) + pr_debug("%02x\n", + ((unsigned char *)subpkt->data)[kk]); + } +#endif + } + return retval; +} + +int synaptics_rmi4_read_packet_regs( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *regs) +{ + int r; + int retval = 0; + + for (r = 0; r < regs->nr_regs; ++r) { + dev_dbg(&rmi4_data->i2c_client->dev, + "about to read F%02x%c register %d [%d/%d]\n", + regs->f_number & 0xff, + register_type_to_ascii(regs->f_number & 0xf00), + regs->regs[r].r_number, + regs->regs[r].expected, + regs->regs[r].offset >= 0); + if (regs->regs[r].expected && regs->regs[r].offset >= 0) { + retval = synaptics_rmi4_read_packet_reg( + rmi4_data, regs, regs->regs[r].r_number); + if (retval < 0) + break; + } + } + return retval; +} + +static int synaptics_rmi4_write_packet_reg( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *regs, int r_number) +{ + struct synaptics_rmi4_packet_reg *reg = find_packet_reg(regs, r_number); + int sz, ii, offset, retval; + + if (!reg || !reg->size) { + dev_err(&rmi4_data->i2c_client->dev, + "r%d does not exist or zero size\n", r_number); + return -EINVAL; + } + if (reg->offset == -1) { + dev_err(&rmi4_data->i2c_client->dev, + "touch register error: writing to absent register r%d\n", + r_number); + return -ENOENT; + } + + for (ii = 0, sz = 0, offset = 0; ii < reg->nr_subpkts; ++ii) { + struct synaptics_rmi4_subpkt *subpkt = reg->subpkt + ii; + if (!subpkt->present) + continue; + + if (subpkt->data && subpkt->size && + (offset + subpkt->size) <= reg->size) { + if ((reg->size - offset) >= subpkt->size) { + memcpy(reg->data + sz, subpkt->data, + subpkt->size); + sz += subpkt->size; + } else { + dev_err(&rmi4_data->i2c_client->dev, + "expected %d bytes, only %d present\n", + offset + subpkt->size, reg->size); + break; + } + } else { + retval = -EINVAL; + dev_err(&rmi4_data->i2c_client->dev, + "cannot update subpacket %d: "\ + "sz=%d, offset=%d, data=%p, reg_sz=%d\n", + ii, subpkt->size, offset, + subpkt->data, reg->size); + goto out; + } + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "writing r%d{%X}, size %d to @%p\n", reg->r_number, + regs->base_addr + reg->offset, reg->size, reg->data); + + retval = rmi4_data->i2c_write(rmi4_data, + regs->base_addr + reg->offset, + reg->data, sz); +out: + return retval; +} + +#ifdef CONFIG_OF +static int synaptics_dsx_gpio_config( + struct synaptics_dsx_platform_data *pdata, bool enable) +{ + int retval = 0; + struct synaptics_rmi4_data *data = + container_of(pdata, struct synaptics_rmi4_data, board); + + if (enable) { + if (!gpio_is_valid(pdata->irq_gpio)) { + pr_err("invalid %s\n", INSTANCE_DBG(IRQ_GPIO_NAME, data)); + retval = -EINVAL; + } + retval = gpio_request(pdata->irq_gpio, + INSTANCE_RMI(IRQ_GPIO_NAME, data)); + if (retval) { + pr_err("unable to request %s [%d]: rc=%d\n", + INSTANCE_DBG(IRQ_GPIO_NAME, data), pdata->irq_gpio, retval); + goto err_gpio; + } + retval = gpio_direction_input(pdata->irq_gpio); + if (retval) { + pr_err("unable to set %s [%d] dir: rc=%d\n", + INSTANCE_DBG(IRQ_GPIO_NAME, data), pdata->irq_gpio, retval); + goto err_gpio; + } + + if (!gpio_is_valid(pdata->reset_gpio)) + return retval; + + retval = gpio_request(pdata->reset_gpio, + INSTANCE_RMI(RESET_GPIO_NAME, data)); + if (retval) { + pr_err("unable to request %s [%d]: rc=%d\n", + INSTANCE_DBG(RESET_GPIO_NAME, data), pdata->reset_gpio, retval); + goto err_gpio; + } + retval = gpio_direction_output(pdata->reset_gpio, 1); + if (retval) { + pr_err("unable to set %s [%d] dir: rc=%d\n", + INSTANCE_DBG(RESET_GPIO_NAME, data), pdata->reset_gpio, retval); + goto err_gpio; + } + } else { + gpio_free(pdata->irq_gpio); + if (!gpio_is_valid(pdata->reset_gpio)) + return retval; + gpio_free(pdata->reset_gpio); + } + +err_gpio: + return retval; +} + +static void synaptics_dsx_dt_parse_modifier(struct synaptics_rmi4_data *data, + struct device_node *parent, struct config_modifier *config, + const char *modifier_name, bool active) +{ + struct device *dev = &data->i2c_client->dev; + const char *patch_string; + char node_name[64]; + struct synaptics_clip_area clipa; + struct device_node *np_config; + int err; + + scnprintf(node_name, 63, "%s-%s", modifier_name, + active ? "active" : "suspended"); + np_config = of_find_node_by_name(parent, node_name); + if (!np_config) { + dev_dbg(dev, "%s: node does not exist\n", node_name); + return; + } + + err = of_property_read_string(np_config, "patch-data", + (const char **)&patch_string); + if (!err) { + struct synaptics_dsx_patch *p_data = + synaptics_dsx_init_patch(modifier_name); + + if (!p_data) { + dev_err(dev, "%s: alloc error\n", node_name); + goto clip_area; + } + synaptics_dsx_parse_string(data, patch_string, p_data, true); + if (active) + config->active = p_data; + else + config->suspended = p_data; + } + +clip_area: + + err = of_property_read_u32_array(np_config, "touch-clip-area", + (unsigned int *)&clipa, sizeof(clipa)/sizeof(unsigned int)); + if (!err) { + config->clipa = kzalloc(sizeof(clipa), GFP_KERNEL); + if (!config->clipa) { + dev_err(dev, "clip area allocation failure\n"); + return; + } + memcpy(config->clipa, &clipa, sizeof(clipa)); + pr_notice("using touch clip area in %s\n", node_name); + } + + of_node_put(np_config); +} + +/* ASCII names order MUST match enum */ +static const char const *ascii_names[] = { "aod", "stats", "folio", + "charger", "wakeup", "fps", "query", "runtime", "na" +}; + +static int modifier_name2id(const char *name) +{ + int i, len2cmp, chosen = -1; + + for (i = 0; i < SYNA_MOD_MAX; i++) { + len2cmp = min_t(int, strlen(name), strlen(ascii_names[i])); + if (!strncmp(name, ascii_names[i], len2cmp)) { + chosen = i; + break; + } + } + return chosen; +} + +static inline const char *modifier_id2name(int id) +{ + return (id >= 0 && id < SYNA_MOD_MAX) ? + ascii_names[id] : ascii_names[SYNA_MOD_MAX]; +} + +static struct config_modifier *modifier_by_id( + struct synaptics_rmi4_data *data, int id) +{ + struct config_modifier *cm, *found = NULL; + + down(&data->modifiers.list_sema); + list_for_each_entry(cm, &data->modifiers.mod_head, link) { + pr_debug("walk-thru: ptr=%p modifier[%s] id=%d\n", + cm, cm->name, cm->id); + if (cm->id == id) { + found = cm; + break; + } + } + up(&data->modifiers.list_sema); + pr_debug("returning modifier id=%d[%d]\n", found ? found->id : -1, id); + return found; +} + +static int synaptics_dsx_dt_parse_modifiers(struct synaptics_rmi4_data *data) +{ + struct device *dev = &data->i2c_client->dev; + struct device_node *np = dev->of_node; + struct device_node *np_mod; + int i, num_names, ret = 0; + char node_name[64]; + static const char **modifiers_names; + + sema_init(&data->modifiers.list_sema, 1); + INIT_LIST_HEAD(&data->modifiers.mod_head); + data->modifiers.mods_num = 0; + + num_names = of_property_count_strings(np, "config_modifier-names"); + if (num_names < 0) { + dev_err(dev, "Cannot parse config_modifier-names: %d\n", + num_names); + return -ENODEV; + } + + modifiers_names = devm_kzalloc(dev, + sizeof(*modifiers_names) * num_names, GFP_KERNEL); + if (!modifiers_names) + return -ENOMEM; + + for (i = 0; i < num_names; i++) { + ret = of_property_read_string_index(np, "config_modifier-names", + i, &modifiers_names[i]); + if (ret < 0) { + dev_err(dev, "Cannot parse modifier-names: %d\n", ret); + return ret; + } + } + + data->modifiers.mods_num = num_names; + + for (i = 0; i < num_names; i++) { + int id; + struct config_modifier *cm, *config; + + scnprintf(node_name, 63, "config_modifier-%s", + modifiers_names[i]); + np_mod = of_find_node_by_name(np, node_name); + if (!np_mod) { + dev_warn(dev, "cannot find modifier node %s\n", + node_name); + continue; + } + + /* check for duplicate nodes in devtree */ + id = modifier_name2id(modifiers_names[i]); + pr_err("processing modifier %s[%d]\n", node_name, id); + list_for_each_entry(cm, &data->modifiers.mod_head, link) { + if (cm->id == id) { + dev_err(dev, "duplicate modifier node %s\n", + node_name); + return -EFAULT; + } + } + /* allocate modifier's structure */ + config = kzalloc(sizeof(*config), GFP_KERNEL); + if (!config) + return -ENOMEM; + + list_add_tail(&config->link, &data->modifiers.mod_head); + config->name = modifiers_names[i]; + config->id = id; + + if (of_property_read_bool(np_mod, "enable-notification")) { + switch (id) { + case SYNA_MOD_FOLIO: + pr_notice("using folio detection\n"); + data->folio_detection_enabled = true; + break; + case SYNA_MOD_CHARGER: + pr_notice("using charger detection\n"); + data->charger_detection_enabled = true; + break; + case SYNA_MOD_FPS: + pr_notice("using fingerprint sensor detection\n"); + data->fps_detection_enabled = true; + break; + case SYNA_MOD_WAKEUP: + pr_notice("using wakeup detection\n"); + data->wakeup_detection_enabled = true; + break; + case SYNA_MOD_AOD: + case SYNA_MOD_STATS: + break; + + } + } else { + config->effective = true; + dev_dbg(dev, "modifier %s enabled unconditionally\n", + node_name); + } + + dev_dbg(dev, "processing modifier %s[%d]\n", + node_name, config->id); + + synaptics_dsx_dt_parse_modifier(data, np_mod, config, + modifiers_names[i], true); + synaptics_dsx_dt_parse_modifier(data, np_mod, config, + modifiers_names[i], false); + of_node_put(np_mod); + } + + return 0; +} + +static int synaptics_dsx_of_init(struct i2c_client *client, + struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned int key_codes[SYN_MAX_BUTTONS]; + struct synaptics_dsx_platform_data *pdata = &rmi4_data->board; + struct device_node *np = client->dev.of_node; + struct synaptics_dsx_cap_button_map *button_map = NULL; + + retval = synaptics_dsx_dt_parse_modifiers(rmi4_data); + rmi4_data->patching_enabled = !retval; + + if (!force_update_patch && (force_update_patch = kzalloc( + sizeof(struct synaptics_dsx_patch), GFP_KERNEL))) { + force_update_patch->name = "force-update"; + force_update_patch->cfg_num = 1; + sema_init(&force_update_patch->list_sema, 1); + INIT_LIST_HEAD(&force_update_patch->cfg_head); + list_add(&force_update_func_patch.link, + &force_update_patch->cfg_head); + } + + if (!power_sleep_patch && (power_sleep_patch = kzalloc( + sizeof(struct synaptics_dsx_patch), GFP_KERNEL))) { + power_sleep_patch->name = "power-sleep"; + power_sleep_patch->cfg_num = 1; + sema_init(&power_sleep_patch->list_sema, 1); + INIT_LIST_HEAD(&power_sleep_patch->cfg_head); + list_add(&power_sleep_func_patch.link, + &power_sleep_patch->cfg_head); + } + + retval = of_get_gpio(np, 0); + if (retval < 0) { + dev_err(&client->dev, "get irq pin failure\n"); + return -EINVAL; + } + pdata->irq_gpio = retval; + + pdata->reset_gpio = ARCH_NR_GPIOS; + if (of_gpio_count(np) > 1) { + retval = of_get_gpio(np, 1); + if (retval < 0) { + dev_err(&client->dev, "get reset pin failure\n"); + return -EINVAL; + } + pdata->reset_gpio = retval; + } else + dev_info(&client->dev, "only found 1 gpio, assume no rst pin\n"); + + memset(key_codes, 0, sizeof(key_codes)); + retval = of_property_read_u32_array(np, "synaptics,key-buttons", + key_codes, SYN_MAX_BUTTONS); + if (!retval) { + int ii; + unsigned char *button_codes; + + button_map = kzalloc(sizeof(*button_map), GFP_KERNEL); + if (IS_ERR_OR_NULL(button_map)) { + dev_err(&client->dev, "button allocation failure\n"); + return -EINVAL; + } + + for (ii = 0; ii < SYN_MAX_BUTTONS; ii++) + if (key_codes[ii]) + button_map->nbuttons++; + + button_codes = kzalloc(button_map->nbuttons, GFP_KERNEL); + if (IS_ERR_OR_NULL(button_codes)) { + dev_err(&client->dev, "button allocation failure\n"); + kfree(button_map); + return -EINVAL; + } + + for (ii = 0; ii < button_map->nbuttons; ii++) + *(button_codes + ii) = (unsigned char)key_codes[ii]; + + button_map->map = button_codes; + } + + pdata->irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT; + pdata->cap_button_map = button_map; + + if (of_property_read_bool(np, "synaptics,gpio-config")) { + pr_notice("using gpio config\n"); + pdata->gpio_config = synaptics_dsx_gpio_config; + } + + if (of_property_read_bool(np, "synaptics,x-flip")) { + pr_notice("using flipped X axis\n"); + pdata->x_flip = true; + } + + if (of_property_read_bool(np, "synaptics,y-flip")) { + pr_notice("using flipped Y axis\n"); + pdata->y_flip = true; + } + + if (of_property_read_bool(np, "synaptics,purge-enabled")) { + pr_notice("using purge\n"); + rmi4_data->purge_enabled = true; + } + + if (of_property_read_bool(np, "synaptics,splash-screen-mode")) { + pr_notice("splash screen mode\n"); + rmi4_data->splash_screen_mode = true; + } + + retval = of_property_read_u32(np, "synaptics,pm-qos-latency", + &rmi4_data->pm_qos_latency); + if (!retval) + pr_notice("pm qos latency is %d\n", + rmi4_data->pm_qos_latency); + + retval = of_property_read_u32(np, "synaptics,control-dsi", + &rmi4_data->ctrl_dsi); + if (!retval) + pr_notice("control DSI %d\n", rmi4_data->ctrl_dsi); + + retval = of_property_read_string(np, "synaptics,bound-display", + &rmi4_data->bound_display); + if (!retval) + pr_notice("bound to display '%s'\n", rmi4_data->bound_display); + + retval = of_property_read_string(np, "synaptics,class-entry-name", + &rmi4_data->class_entry_name); + if (!retval) + pr_notice("class entry name '%s'\n", rmi4_data->class_entry_name); + + return 0; +} +#else +static inline int synaptics_dsx_of_init(struct i2c_client *client) +{ + return -EINVAL; +} +#endif + +static void synaptics_dsx_validate_product_string(unsigned char *id) +{ + unsigned char *s, *wc; + bool do_once = false, do_all = false; + + for (s = wc = id; *s; s++) { + if (*s == '-') { + do_all = true; + continue; + } + if (!isdigit(*s) && isupper(*s)) { + *s = tolower(*s); + do_once = true; + } + if (do_all || do_once) { + *wc = *s; + do_once = false; + } + wc++; + } + *wc = 0; +} + +#define NANO_SEC 1000000000 +#define SEC_TO_MSEC 1000 +#define NANO_TO_MSEC 1000000 + +#define CIRCULAR_BUFFER 1 +#define ONE_BASED_INDEX 2 + +static struct synaptics_dsx_stats gStat; + +static inline unsigned long long timediff_ms( + struct timespec start, struct timespec end) +{ + struct timespec temp; + + if ((end.tv_nsec - start.tv_nsec) < 0) { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = NANO_SEC + end.tv_nsec - start.tv_nsec; + } else { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return (temp.tv_sec * SEC_TO_MSEC) + (temp.tv_nsec / NANO_TO_MSEC); +} + +static inline void timekeeping_reset(struct statistics *sp) +{ + int ii; + + sp->active = -1; + sp->clk_run = false; + for (ii = 0; ii < sp->max; ii++) { + sp->keeper[ii].id = -1; + sp->keeper[ii].duration = (unsigned long long)0; + } +} + +static void statistics_reset(void) +{ + gStat.uptime_dur = (unsigned long long)0; + gStat.uptime_run = false; + timekeeping_reset(gStat.dur); + timekeeping_reset(gStat.hop); + timekeeping_reset(gStat.nms); +} + +static struct statistics *timekeeping_alloc(int max, + unsigned char abbr, unsigned int flags) +{ + ssize_t data_size = sizeof(struct time_keeping) * max; + struct statistics *stats; + + tk_debug("allocating [%zu/%zu] for %c\n", + sizeof(struct statistics), data_size, abbr); + stats = kzalloc(sizeof(struct statistics) + data_size, GFP_KERNEL); + if (!stats) + return NULL; + stats->active = -1; + stats->max = max; + stats->flags = flags; + stats->abbr = abbr; + timekeeping_reset(stats); + return stats; +} + +static int statistics_alloc(int max_gear, int max_hop, int max_nms, int max_mpd) +{ + gStat.dur = timekeeping_alloc(max_gear, 'G', 0); + if (!gStat.dur) + return -ENOMEM; + gStat.hop = timekeeping_alloc(max_hop, 'H', CIRCULAR_BUFFER); + if (!gStat.hop) { + kfree(gStat.hop); + return -ENOMEM; + } + gStat.nms = timekeeping_alloc(max_nms, 'N', ONE_BASED_INDEX); + if (!gStat.nms) { + kfree(gStat.dur); + kfree(gStat.hop); + return -ENOMEM; + } + gStat.mpd = timekeeping_alloc(max_mpd, 'M', 0); + if (!gStat.nms) { + kfree(gStat.dur); + kfree(gStat.hop); + kfree(gStat.nms); + return -ENOMEM; + } + return 0; +} + +static void statistics_log_time_end(struct statistics *stats, ktime_t end) +{ + struct time_keeping *keeper = NULL; + unsigned long long duration; + int c_idx; + + if (stats->active == -1) + return; + + keeper = &stats->keeper[stats->active]; + c_idx = keeper->id; + duration = timediff_ms(ktime_to_timespec(stats->start), + ktime_to_timespec(end)); + keeper->duration += duration; + tk_debug("%c[%d].%d logged +%llu/%llu\n", stats->abbr, stats->active, + c_idx, duration, keeper->duration); + stats->clk_run = false; +} + +static void statistics_log_time(struct statistics *stats, int idx, ktime_t log) +{ + struct time_keeping *keeper = NULL; + int c_idx = stats->active; + + /* c_idx stays -1 or gets set to id of the current cell */ + if (stats->active != -1) { + keeper = &stats->keeper[stats->active]; + c_idx = keeper->id; + } + + /* new idx and timer was running */ + if (keeper && c_idx != idx && stats->clk_run) { + unsigned long long duration; + + duration = timediff_ms(ktime_to_timespec(stats->start), + ktime_to_timespec(log)); + keeper->duration += duration; + /* clock is ticking already, just update time */ + stats->start = log; + tk_debug("%c[%d].%d logged +%llu/%llu\n", stats->abbr, + stats->active, c_idx, duration, keeper->duration); + } + + /* move to the next cell if idx is changing */ + if (c_idx != idx) { + /* shift index with overflow control */ + if (stats->flags & CIRCULAR_BUFFER) { + if (++stats->active == stats->max) + stats->active = 0; + } else if (stats->flags & ONE_BASED_INDEX) { + stats->active = idx - 1; + } else + stats->active = idx; + + /* store index in the cell */ + stats->keeper[stats->active].id = idx; + tk_debug("%c new %sindex %d\n", stats->abbr, + stats->flags & CIRCULAR_BUFFER ? "RR_" : + stats->flags & ONE_BASED_INDEX ? "1B_" : "", + stats->active); + } + + if (!stats->clk_run) { + stats->start = log; + stats->clk_run = true; + } +} + +static int statistics_start_timekeeping( + struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_subpkt *subpkt; + struct f54_d10_s0_type *f54_d10_0; + struct f54_d17_s0_type *f54_d17_0; + struct f51_d0_s0_type *f51_d0_0; + ktime_t log; + int ii, error; + + if (gStat.uptime_run) { + pr_debug("timekeeping already running\n"); + return 1; + } + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F51 | DATA_TYPE); + if (!regs) { + pr_err("F51 data: not found\n"); + return -ENOENT; + } + + error = synaptics_rmi4_read_packet_regs(rmi4_data, regs); + if (error < 0) + return -EIO; + + subpkt = find_subpkt(regs, 0, 0, NULL, (void **)&f51_d0_0); + if (!subpkt || !subpkt->present) { + pr_err("F51 data: subpkt not found\n"); + return -ENOENT; + } + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F54 | DATA_TYPE); + if (!regs) { + pr_err("F54 data not found\n"); + return -ENOENT; + } + + error = synaptics_rmi4_read_packet_regs(rmi4_data, regs); + if (error < 0) + return -EIO; + + subpkt = find_subpkt(regs, 10, 0, NULL, (void **)&f54_d10_0); + if (!subpkt || !subpkt->present) { + pr_err("F54 data: subpkt not found\n"); + return -ENOENT; + } + + subpkt = find_subpkt(regs, 17, 0, NULL, (void **)&f54_d17_0); + if (!subpkt || !subpkt->present) { + pr_err("F54 data: subpkt not found\n"); + return -ENOENT; + } + + for (ii = 0; ii < 10; ii++) { + error = synaptics_rmi4_read_packet_regs(rmi4_data, regs); + if (error < 0) + return -EIO; + + /* FIXME: it takes several attempts to read "real" value */ + /* noise state cannot be 0, thus use it as exit condition */ + if (!(int)f54_d10_0->noise_state) + msleep(50); + else + break; + } + + log = ktime_get(); + gStat.uptime = log; + gStat.uptime_run = true; + statistics_log_time(gStat.dur, (int)f54_d17_0->freq, log); + statistics_log_time(gStat.hop, (int)f54_d17_0->freq, log); + statistics_log_time(gStat.nms, (int)f54_d10_0->noise_state, log); + statistics_log_time(gStat.mpd, (int)f51_d0_0->md_present, log); + tk_debug("uptime notch; G%d:N%d:M%d\n", (int)f54_d17_0->freq, + (int)f54_d10_0->noise_state, (int)f51_d0_0->md_present); + return 0; +} + +static int statistics_init(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_subpkt *subpkt; + struct f54_q12_s0_type *f54_q12_0; + int error; + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F54 | QUERY_TYPE); + if (!regs) + return -ENOENT; + + error = synaptics_rmi4_read_packet_regs(rmi4_data, regs); + if (error < 0) + return -EIO; + + subpkt = find_subpkt(regs, 12, 0, NULL, (void **)&f54_q12_0); + if (!subpkt || !subpkt->present) + return -ENOENT; + + /* allocate frequencies by the number of gears in gear table */ + error = statistics_alloc( + (int)f54_q12_0->num_of_scan_freq & 0xf, + 100, /* 100 last gear hops */ + 2, /* 2 NMS modes */ + 2); /* 2 guard presence modes */ + if (error < 0) + return -ENOMEM; + + error = statistics_start_timekeeping(rmi4_data); + if (error < 0) + pr_err("statistics init failed\n"); + else { + pr_notice("touch statistics enabled\n"); + gStat.enabled = true; + } + + return error; +} + +static inline void statistics_stop_timekeeping(void) +{ + /* avoid being executed twice on firmware reflash */ + if (gStat.uptime_run) { + ktime_t end = ktime_get(); + gStat.uptime_dur += timediff_ms( + ktime_to_timespec(gStat.uptime), + ktime_to_timespec(end)); + gStat.uptime_run = false; + statistics_log_time_end(gStat.dur, end); + statistics_log_time_end(gStat.hop, end); + statistics_log_time_end(gStat.nms, end); + statistics_log_time_end(gStat.mpd, end); + tk_debug("uptime %llu\n", gStat.uptime_dur); + } else + pr_debug("timekeeping already stopped\n"); + +} + +static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, + unsigned short length); + +static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, + unsigned short length); + +static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data); + +static int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data, + bool enable); + +static void synaptics_dsx_sensor_state(struct synaptics_rmi4_data *rmi4_data, + int state); + +static void synaptics_dsx_release_all(struct synaptics_rmi4_data *rmi4_data); + +#if (defined(CONFIG_FB) || defined(CONFIG_DRM)) && !defined(CONFIG_MMI_PANEL_NOTIFICATIONS) +static int synaptics_dsx_panel_cb(struct notifier_block *nb, + unsigned long event, void *data); +#endif +#if defined(CONFIG_DRM) +extern bool dsi_display_is_panel_enable(int id, int *probe_status, char **pname); +#endif + +static int synaptics_rmi4_suspend(struct device *dev); + +static int synaptics_rmi4_resume(struct device *dev); + +static ssize_t synaptics_rmi4_ud_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_drv_irq_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_drv_irq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_pm_qos_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_pm_qos_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) +static ssize_t synaptics_rmi4_test_irq_delay_ms_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_test_irq_delay_ms_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_test_irq_data_contig_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_test_irq_data_contig_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +#endif + +static ssize_t synaptics_rmi4_hw_irqstat_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_mod_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_mod_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_mod_sw_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_mod_sw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_query_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_query_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_reporting_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_reporting_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_reporting_ntouch_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_reporting_ntouch_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_stats_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_stats_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +struct synaptics_rmi4_f01_device_status { + union { + struct { + unsigned char status_code:4; + unsigned char reserved:2; + unsigned char flash_prog:1; + unsigned char unconfigured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f01_query1 { + union { + struct { + unsigned char custom_map:1; + unsigned char non_compliant:1; + unsigned char reserved:1; + unsigned char has_sensor_id:1; + unsigned char has_charger_input:1; + unsigned char has_adjustable_doze:1; + unsigned char has_doze_holdoff:1; + unsigned char has_query42:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f01_query42 { + union { + struct { + unsigned char has_ds4_queries:1; + unsigned char has_multi_physical:1; + unsigned char has_guest:1; + unsigned char has_swr:1; + unsigned char has_report_rate:1; + unsigned char has_recalibration:1; + unsigned char reserved:2; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f1a_query { + union { + struct { + unsigned char max_button_count:3; + unsigned char reserved:5; + unsigned char has_general_control:1; + unsigned char has_interrupt_enable:1; + unsigned char has_multibutton_select:1; + unsigned char has_tx_rx_map:1; + unsigned char has_perbutton_threshold:1; + unsigned char has_release_threshold:1; + unsigned char has_strongestbtn_hysteresis:1; + unsigned char has_filter_strength:1; + } __packed; + unsigned char data[2]; + }; +}; + +struct synaptics_rmi4_f1a_control_0 { + union { + struct { + unsigned char multibutton_report:2; + unsigned char filter_mode:2; + unsigned char reserved:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f1a_control_3_4 { + unsigned char transmitterbutton; + unsigned char receiverbutton; +}; + +struct synaptics_rmi4_f1a_control { + struct synaptics_rmi4_f1a_control_0 general_control; + unsigned char *button_int_enable; + unsigned char *multi_button; + struct synaptics_rmi4_f1a_control_3_4 *electrode_map; + unsigned char *button_threshold; + unsigned char button_release_threshold; + unsigned char strongest_button_hysteresis; + unsigned char filter_strength; +}; + +struct synaptics_rmi4_f1a_handle { + int button_bitmask_size; + unsigned char button_count; + unsigned char valid_button_count; + unsigned char *button_data_buffer; + unsigned char *button_map; + struct synaptics_rmi4_f1a_query button_query; + struct synaptics_rmi4_f1a_control button_control; +}; + +struct synaptics_rmi4_exp_fn { + enum exp_fn fn_type; + enum ic_modes mode; + bool inserted; + int (*func_init)(struct synaptics_rmi4_data *rmi4_data); + void (*func_remove)(struct synaptics_rmi4_data *rmi4_data); + void (*func_attn)(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask); + int (*func_status)(struct synaptics_rmi4_data *rmi4_data); + struct list_head link; +}; + +static struct device_attribute attrs[] = { + __ATTR(reset, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + synaptics_rmi4_f01_reset_store), + __ATTR(productinfo, S_IRUGO, + synaptics_rmi4_f01_productinfo_show, + synaptics_rmi4_store_error), + __ATTR(buildid, S_IRUGO, + synaptics_rmi4_f01_buildid_show, + synaptics_rmi4_store_error), + __ATTR(flashprog, S_IRUSR | S_IRGRP, + synaptics_rmi4_f01_flashprog_show, + synaptics_rmi4_store_error), + __ATTR(0dbutton, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_0dbutton_show, + synaptics_rmi4_0dbutton_store), + __ATTR(drv_irq, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_drv_irq_show, + synaptics_rmi4_drv_irq_store), + __ATTR(reporting, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_reporting_show, + synaptics_rmi4_reporting_store), + __ATTR(reporting_ntouch, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_reporting_ntouch_show, + synaptics_rmi4_reporting_ntouch_store), + __ATTR(stats, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_stats_show, + synaptics_rmi4_stats_store), + __ATTR(hw_irqstat, S_IRUSR | S_IRGRP, + synaptics_rmi4_hw_irqstat_show, + synaptics_rmi4_store_error), + __ATTR(ic_ver, S_IRUGO, + synaptics_rmi4_ic_ver_show, + synaptics_rmi4_store_error), + __ATTR(mod, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_mod_show, + synaptics_rmi4_mod_store), + __ATTR(mod_sw, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_mod_sw_show, + synaptics_rmi4_mod_sw_store), + __ATTR(query, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_query_show, + synaptics_rmi4_query_store), + __ATTR(tsi, S_IRUSR | S_IRGRP, + synaptics_rmi4_ud_show, + synaptics_rmi4_store_error), + __ATTR(pm_qos, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_pm_qos_show, + synaptics_rmi4_pm_qos_store), +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + __ATTR(test_irq_delay_ms, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_test_irq_delay_ms_show, + synaptics_rmi4_test_irq_delay_ms_store), + __ATTR(test_irq_data_contig, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_test_irq_data_contig_show, + synaptics_rmi4_test_irq_data_contig_store), +#endif +}; + +static DEVICE_ATTR(poweron, S_IRUSR | S_IRGRP, + synaptics_rmi4_poweron_show, synaptics_rmi4_store_error); + +static irqreturn_t synaptics_dsx_reset_irq(int irq, void *data) +{ + struct synaptics_rmi4_data *rmi4_data = data; + up(&rmi4_data->reset_semaphore); + return IRQ_HANDLED; +} + +int synaptics_rmi4_sw_reset(struct synaptics_rmi4_data *rmi4_data, + bool requery) { + int retval; + unsigned char page_number; + unsigned short pdt_entry_addr; + struct synaptics_rmi4_fn_desc rmi_fd; + unsigned short cmd_reg_addr; + bool cmd_reg_found = false; + + if (!requery) { + cmd_reg_addr = rmi4_data->f01_cmd_base_addr; + cmd_reg_found = true; + goto sw_reset; + } + + for (page_number = 0; !cmd_reg_found && page_number < PAGES_TO_SERVICE; + page_number++) { + for (pdt_entry_addr = PDT_START; pdt_entry_addr > PDT_END; + pdt_entry_addr -= PDT_ENTRY_SIZE) { + pdt_entry_addr |= (page_number << 8); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + pdt_entry_addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + if (rmi_fd.fn_number == 0) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Reached end of PDT\n", + __func__); + break; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: F%02x found (page %d)\n", + __func__, rmi_fd.fn_number, + page_number); + + if (rmi_fd.fn_number == SYNAPTICS_RMI4_F01) { + cmd_reg_addr = rmi_fd.cmd_base_addr; + cmd_reg_found = true; + break; + } + } + } + +sw_reset: + if (cmd_reg_found) { + uint8_t command = 1; + retval = synaptics_rmi4_i2c_write(rmi4_data, + cmd_reg_addr, + &command, sizeof(command)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to issue SW reset command, error = %d\n", + __func__, retval); + return retval; + } + return 0; + } else + return -ENOENT; + +} + + +static int synaptics_dsx_ic_reset( + struct synaptics_rmi4_data *rmi4_data, int reset) +{ + int retval; + unsigned long start = jiffies; + struct synaptics_dsx_platform_data + *platform_data = &rmi4_data->board; + bool has_rst_pin = gpio_is_valid(platform_data->reset_gpio); + bool need_to_free_irq = true; + + sema_init(&rmi4_data->reset_semaphore, 0); + + if (has_rst_pin && (reset == RMI4_HW_RESET)) { + gpio_set_value(platform_data->reset_gpio, 0); + udelay(1500); + } else if (!has_rst_pin || (reset == RMI4_SW_RESET)) { + retval = synaptics_rmi4_sw_reset(rmi4_data, true); + if (retval < 0) + return retval; + } + + retval = request_irq(rmi4_data->irq, synaptics_dsx_reset_irq, + IRQF_TRIGGER_RISING, rmi4_data->reset_name, + rmi4_data); + if (retval < 0) { + need_to_free_irq = false; + dev_warn(&rmi4_data->i2c_client->dev, + "%s: Failed to request irq: %d\n", + __func__, retval); + } + + if (has_rst_pin && (reset == RMI4_HW_RESET)) + gpio_set_value(platform_data->reset_gpio, 1); + + retval = down_timeout(&rmi4_data->reset_semaphore, msecs_to_jiffies(100)); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: timed out waiting for reset to complete\n", __func__); + retval = -ETIMEDOUT; + } else { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: IRQ GPIO=%d after HW reset is %d\n", + __func__, platform_data->irq_gpio, + gpio_get_value(platform_data->irq_gpio)); + + retval = (int)jiffies_to_msecs(jiffies-start); + /* insert delay to ensure 1st i2c bus access succeeds */ + udelay(1000); + } + + if (need_to_free_irq) + free_irq(rmi4_data->irq, rmi4_data); + + /* perform SW reset if HW reset failed */ + if (need_to_free_irq && reset != RMI4_SW_RESET && retval == -ETIMEDOUT) + retval = synaptics_dsx_ic_reset(rmi4_data, RMI4_SW_RESET); + + return retval; +} + +static int synaptics_dsx_alloc_input(struct synaptics_rmi4_data *rmi4_data) +{ + char *suffix, input_name[64]; + + rmi4_data->input_dev = input_allocate_device(); + if (IS_ERR_OR_NULL(rmi4_data->input_dev)) + return PTR_ERR(rmi4_data->input_dev); + + if (rmi4_data->class_entry_name) + suffix = (char *)rmi4_data->class_entry_name; + else + suffix = CLASS_PRIMARY_FNAME; + + snprintf(input_name, sizeof(input_name), DRIVER_NAME ".%s", suffix); + rmi4_data->input_dev->name = kstrdup(input_name, GFP_KERNEL); + rmi4_data->input_dev->phys = INSTANCE_RMI(INPUT_PHYS_NAME, rmi4_data); + rmi4_data->input_dev->id.bustype = BUS_I2C; + rmi4_data->input_dev->dev.parent = &rmi4_data->i2c_client->dev; + + set_bit(EV_SYN, rmi4_data->input_dev->evbit); + /* enable power key injection */ + set_bit(EV_KEY, rmi4_data->input_dev->evbit); + input_set_capability(rmi4_data->input_dev, EV_KEY, KEY_POWER); + + pr_debug("allocated input device '%s'\n", rmi4_data->input_dev->name); + + return 0; +} + +static void synaptics_copy_multiple_subpkts( + struct synaptics_rmi4_packet_reg *reg, + struct synaptics_dsx_func_patch *fp) +{ + int i, b2c, leftover = fp->size; + unsigned char *data_ptr = fp->data; + struct synaptics_rmi4_subpkt *subpkt; + + for (i = fp->subpkt; i < reg->nr_subpkts; i++) { + subpkt = reg->subpkt + i; + if (!subpkt->present || !subpkt->data) + continue; + b2c = min(leftover, (int)subpkt->size); + memcpy(subpkt->data, data_ptr, b2c); + pr_debug("copied %d bytes to subpkt %d\n", b2c, i); + data_ptr += subpkt->size; + leftover -= subpkt->size; + } + pr_debug("%s misalignement detected\n", !leftover ? "no" : ""); +} + +static void synaptics_dsx_patch_function( + struct synaptics_rmi4_data *rmi4_data, int f_number, + struct synaptics_dsx_patch *patch) +{ + int r, error, function; + unsigned char *destination; + unsigned char *value, rt_mod; + struct device *dev = &rmi4_data->i2c_client->dev; + struct synaptics_rmi4_subpkt *subpkt; + struct synaptics_dsx_func_patch *fp; + struct synaptics_rmi4_packet_reg *reg; + struct synaptics_rmi4_func_packet_regs *regs = + find_function(rmi4_data, f_number); + + function = regs->f_number & 0xff; + rt_mod = register_type_to_ascii(regs->f_number & 0xf00); + pr_debug("patching F%x%c\n", function, rt_mod); + down(&patch->list_sema); + list_for_each_entry(fp, &patch->cfg_head, link) { + if (fp->func != f_number) + continue; + reg = find_packet_reg(regs, fp->regstr); + if (!reg || reg->offset < 0) { + pr_err("F%x%c@%d not present\n", + function, rt_mod, fp->regstr); + continue; + } + if (fp->subpkt >= reg->nr_subpkts) { + pr_err("F%x%c@%d:%d not present\n", + function, rt_mod, fp->regstr, fp->subpkt); + continue; + } + + /* read register only once */ + if (!reg->updated) { + error = synaptics_rmi4_read_packet_reg( + rmi4_data, regs, reg->r_number); + if (error < 0) + goto unlock_and_leave; +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + { + int ss, kk; + pr_debug("F%x%c@%d before patching:\n", + function, rt_mod, reg->r_number); + for (ss = 0; ss < reg->nr_subpkts; ss++) { + subpkt = reg->subpkt + ss; + for (kk = 0; kk < subpkt->size; ++kk) + pr_debug("%02x\n", + ((unsigned char *)subpkt->data)[kk]); + } + } +#endif + /* value has been updated */ + reg->updated = true; + } + + subpkt = reg->subpkt + fp->subpkt; + destination = (unsigned char *)subpkt->data; + if (!subpkt->present || !subpkt->data || + subpkt->size < fp->size) { + pr_debug("F%x%c@%d:%d subpkt: size=%d, offset=%d; reg-size=%d; fp-size=%d\n", + function, rt_mod, + fp->regstr, fp->subpkt, + subpkt->size, subpkt->offset, + reg->size, fp->size); + /* If more than one consequitive subpacket within */ + /* the same packet register needs to be updated, */ + /* then patch might include data for all subpackets */ + /* combined into a single array. Then its size will */ + /* be more than individual subpacket size. It should */ + /* not be a problem as packet register is updated */ + /* all in once. And as long as size of data array */ + /* fits the register, update should be allowed */ + if (reg->size <= (subpkt->offset + fp->size)) { + pr_debug("F%x%c@%d:%d improperly allocated\n", + function, rt_mod, + fp->regstr, fp->subpkt); + continue; + } else { + synaptics_copy_multiple_subpkts(reg, fp); + /* whole patch has been applied already */ + destination = NULL; + } + } + if (fp->bitmask && fp->size == 1) { + value = (unsigned char *)subpkt->data; + pr_debug("patching by mask: source 0x%x\n", *value); + *value &= ~(fp->bitmask); + pr_debug("patching by mask: clear-ed 0x%x\n", *value); + *value |= *fp->data; + pr_debug("patching by mask: result 0x%x\n", *value); + /* value has been modified already */ + destination = NULL; + } + + if (destination) + memcpy(destination, fp->data, fp->size); + + /* value has been changed */ + reg->modified = true; + + dev_dbg(dev, "patched F%x%c@%d:%d, sz=%d\n", + function, rt_mod, fp->regstr, fp->subpkt, fp->size); + } + + /* update loop */ + for (r = 0; r < regs->nr_regs; r++) { + reg = ®s->regs[r]; + if (reg->updated && reg->modified) { +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + { + int ss, kk; + pr_debug("F%x%c@%d after patching:\n", + function, rt_mod, reg->r_number); + for (ss = 0; ss < reg->nr_subpkts; ss++) { + subpkt = reg->subpkt + ss; + for (kk = 0; kk < subpkt->size; ++kk) + pr_debug("%02x\n", + ((unsigned char *)subpkt->data)[kk]); + } + } +#endif + error = synaptics_rmi4_write_packet_reg(rmi4_data, + regs, reg->r_number); + if (error < 0) + dev_warn(dev, "F%x%c@%d patch write failed\n", + function, rt_mod, reg->r_number); + reg->modified = false; + } + reg->updated = false; + } + +unlock_and_leave: + up(&patch->list_sema); +} + +static void synaptics_dsx_enable_wakeup_source( + struct synaptics_rmi4_data *rmi4_data, bool enable) +{ + int error; + error = irq_set_irq_wake(rmi4_data->irq, (int)enable); + pr_debug("%s wakeup; rc=%d\n", enable ? "enabled" : "disabled", error); +} + +static const char * const synaptics_state_names[] = {"UNKNOWN", + "ACTIVE", "SUSPEND", "UNUSED", "STANDBY", "BL", "INIT", + "FLASH", "QUERY", "LOADING", "INVALID" }; + +static const char *synaptics_dsx_state_name(int state) +{ + int index = state < 0 || state > STATE_INVALID ? STATE_INVALID : state; + return synaptics_state_names[index]; +} + +static void synaptics_dsx_apply_modifiers( + struct synaptics_rmi4_data *rmi4_data, int state) +{ + bool wakeup = false, update = false, sleep = false; + int i; + + if (!rmi4_data->modifiers.mods_num) { + pr_debug("patchset is empty!\n"); + goto no_modifiers; + } + + down(&rmi4_data->modifiers.list_sema); + for (i = 0; i < MAX_CONFIG_REGS && SLOT_IN_USE; i++) { + struct config_modifier *cm; + + /* skip query registers */ + if (rmi4_data->config_regs[i].f_number & QUERY_TYPE) + continue; + + list_for_each_entry(cm, &rmi4_data->modifiers.mod_head, link) { + struct synaptics_dsx_patch *patch = NULL; + + /* skip disabled modifiers */ + if (!cm->effective) + continue; + + if (state == STATE_ACTIVE) + patch = cm->active; + else if (state == STATE_SUSPEND) + patch = cm->suspended; + + /* skip effective modifier without patch */ + if (!patch) + continue; + + /* keep track of flags among effective modifiers */ + if (!update && (patch->flags & FLAG_FORCE_UPDATE)) + update = true; + if (!wakeup && (patch->flags & FLAG_WAKEABLE)) + wakeup = true; + if (!sleep && (patch->flags & FLAG_POWER_SLEEP)) + sleep = true; + /* finally apply patch */ + synaptics_dsx_patch_function(rmi4_data, + rmi4_data->config_regs[i].f_number, patch); + } + } + up(&rmi4_data->modifiers.list_sema); + + /* force update regardless the current state */ + if (update && force_update_patch) { + pr_debug("applying forced update patch!\n"); + synaptics_dsx_patch_function(rmi4_data, + SYNAPTICS_RMI4_F54 | COMMAND_TYPE, force_update_patch); + } + + /* power mode and wakeability only on entering suspend */ + if (state == STATE_SUSPEND) { + if (wakeup) { + rmi4_data->suspend_is_wakeable = true; + synaptics_dsx_enable_wakeup_source(rmi4_data, true); + } else if (sleep && power_sleep_patch) { + pr_debug("applying sleep patch!\n"); + synaptics_dsx_patch_function(rmi4_data, + SYNAPTICS_RMI4_F01, power_sleep_patch); + } + } + + if (!wakeup && rmi4_data->suspend_is_wakeable) { + rmi4_data->suspend_is_wakeable = false; + synaptics_dsx_enable_wakeup_source(rmi4_data, false); + } + +no_modifiers: + /* keep page 0 active */ + synaptics_rmi4_set_page(rmi4_data, 0); +} + +static int synaptics_dsx_get_state_safe(struct synaptics_rmi4_data *rmi4_data) +{ + int state; + mutex_lock(&(rmi4_data->state_mutex)); + state = rmi4_data->state; + mutex_unlock(&(rmi4_data->state_mutex)); + return state; +} + +static void synaptics_dsx_set_state_safe(struct synaptics_rmi4_data *rmi4_data, + int state) +{ + mutex_lock(&(rmi4_data->state_mutex)); + rmi4_data->state = state; + mutex_unlock(&(rmi4_data->state_mutex)); +} + +static int synaptics_dsx_wait_for_idle(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned long start_wait_jiffies = jiffies; + + do { + int current_state; + + current_state = synaptics_dsx_get_state_safe(rmi4_data); + if (!(current_state == STATE_INIT || + current_state == STATE_FLASH || + current_state == STATE_LOADING || + current_state == STATE_UNKNOWN)) + break; + + usleep_range(1000, 1000); + + } while (1); + + if ((jiffies - start_wait_jiffies)) + pr_info("entering suspend delayed for %ums\n", + jiffies_to_msecs(jiffies - start_wait_jiffies)); + return 0; +} + +static int synaptics_rmi4_f01_flashprog_status( + struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + struct synaptics_rmi4_f01_device_status status; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr, + status.data, + sizeof(status.data)); + return retval < 0 ? retval : status.flash_prog; +} + +static int synaptics_dsx_sensor_ready_state( + struct synaptics_rmi4_data *rmi4_data, bool standby) +{ + bool ui_mode; + int retval, state, i; + + for (i = 0; i < 10; ++i) { + retval = synaptics_rmi4_f01_flashprog_status(rmi4_data); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: (%d): flashprog bit=%d\n", + __func__, i, retval); + /* if simple flashprog bit check reports BL mode */ + /* then do not bother checking F34 flash status */ + if (!retval && + rmi4_data->get_status != synaptics_rmi4_f01_flashprog_status) { + retval = rmi4_data->get_status(rmi4_data); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: (%d): f34 flash status %d\n", + __func__, i, retval); + } + + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: (%d) Failed to query touch ic status\n", + __func__, i); + return retval; + } + + ui_mode = retval == 0; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: (%d) UI mode: %s\n", + __func__, i, ui_mode ? "true" : "false"); + if (ui_mode) + break; + + msleep(20); + } + + state = synaptics_dsx_get_state_safe(rmi4_data); + if (!ui_mode && state == STATE_SUSPEND && rmi4_data->input_registered) { + /* expecting touch IC to enter UI mode based on */ + /* its previous known good state */ + dev_err(&rmi4_data->i2c_client->dev, + "%s: Timed out waiting for UI mode - UI mode forced\n", + __func__); + ui_mode = 1; + } + + if (ui_mode) { + state = standby ? STATE_STANDBY : STATE_ACTIVE; + } else + if (!(state == STATE_INIT || state == STATE_FLASH)) + state = STATE_BL; + + synaptics_dsx_sensor_state(rmi4_data, state); + + return 0; +} + +static void synaptics_dsx_enforce_modifiers( + struct synaptics_rmi4_data *rmi4_data, + struct config_modifier *modifier) +{ + int error, state = synaptics_dsx_get_state_safe(rmi4_data); + + if (state == STATE_ACTIVE) { + if (!modifier->active) + return; + /* set unknown state to ensure IRQ gets */ + /* enabled on state transition to active */ + synaptics_dsx_sensor_state(rmi4_data, STATE_UNKNOWN); + /* disable IRQ to handle reset */ + synaptics_rmi4_irq_enable(rmi4_data, false); + /* perform SW reset to restore defaults */ + error = synaptics_dsx_ic_reset(rmi4_data, RMI4_SW_RESET); + if (error < 0) + dev_err(&rmi4_data->i2c_client->dev, + "folio: sw reset failed %d\n", error); + } + synaptics_dsx_sensor_ready_state(rmi4_data, false); +} + +static void synaptics_dsx_sensor_state(struct synaptics_rmi4_data *rmi4_data, + int state) +{ + if (synaptics_dsx_get_state_safe(rmi4_data) == state) + return; + + switch (state) { + case STATE_UNKNOWN: + rmi4_data->in_bootloader = false; + case STATE_FLASH: + case STATE_LOADING: + /* no special handling for these states */ + break; + + case STATE_SUSPEND: + synaptics_dsx_wait_for_idle(rmi4_data); + + if (!rmi4_data->in_bootloader) { + synaptics_dsx_apply_modifiers(rmi4_data, STATE_SUSPEND); + } + + if (!rmi4_data->suspend_is_wakeable) { + synaptics_rmi4_irq_enable(rmi4_data, false); + } + break; + + case STATE_ACTIVE: + if (!rmi4_data->in_bootloader) + synaptics_dsx_apply_modifiers(rmi4_data, STATE_ACTIVE); + + if (rmi4_data->input_registered) + synaptics_rmi4_irq_enable(rmi4_data, true); + else { + synaptics_rmi4_irq_enable(rmi4_data, false); + pr_err("Active state without input device\n"); + } + + if (gStat.enabled) { + statistics_start_timekeeping(rmi4_data); + } + break; + + case STATE_STANDBY: + synaptics_rmi4_irq_enable(rmi4_data, false); + break; + + case STATE_BL: + if (!rmi4_data->in_bootloader) + rmi4_data->in_bootloader = true; + case STATE_INIT: + synaptics_rmi4_irq_enable(rmi4_data, false); + + if (!rmi4_data->in_bootloader) { + int retval; + unsigned char mode = 1; + unsigned char intr_status[MAX_INTR_REGISTERS]; + + /* deactivate touch by putting touch IC into */ + /* sleep mode to prevent interference of touch */ + /* processing with flashing */ + pr_debug("enter sleep mode\n"); + retval = synaptics_rmi4_i2c_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &mode, + sizeof(mode)); + + if (retval < 0) + pr_notice("failed to enter sleep mode\n"); + + /* clear unhandled interrupt if any */ + pr_debug("clear interrupt\n"); + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + &intr_status[0], + rmi4_data->num_of_intr_regs); + + if (retval < 0) + pr_notice("failed to clear interrupt\n"); + } + + if (gStat.enabled) { + statistics_stop_timekeeping(); + } + break; + } + + dev_info(&rmi4_data->i2c_client->dev, "state change %s -> %s\n", + synaptics_dsx_state_name(rmi4_data->state), + synaptics_dsx_state_name(state)); + + synaptics_dsx_set_state_safe(rmi4_data, state); +} + +#define PM_ADD 0 +#define PM_UPDATE 1 +#define PM_REMOVE 2 + +static void synaptics_dsx_pm_qos(struct synaptics_rmi4_data *rmi4_data, + int update, bool use_default) +{ + if (irq_can_set_affinity(rmi4_data->irq)) + rmi4_data->pm_qos_irq.type = PM_QOS_REQ_AFFINE_IRQ; + else + rmi4_data->pm_qos_irq.type = PM_QOS_REQ_ALL_CORES; + + switch (update) { + case PM_REMOVE: + pm_qos_remove_request(&rmi4_data->pm_qos_irq); + break; + case PM_ADD: + pm_qos_add_request(&rmi4_data->pm_qos_irq, + PM_QOS_CPU_DMA_LATENCY, + rmi4_data->pm_qos_latency); + break; + case PM_UPDATE: + pm_qos_update_request(&rmi4_data->pm_qos_irq, + use_default ? PM_QOS_DEFAULT_VALUE : + rmi4_data->pm_qos_latency); + break; + } +} + +static void ud_set_id(struct touch_area_stats *tas, int id) +{ + tas->ud_id = id; +} + +static void ud_log_status(struct touch_area_stats *tas, bool down) +{ + struct touch_up_down *ud = tas->ud; + ssize_t id = tas->ud_id; + + if (id >= tas->ud_len) + tas->unknown_counter++; + + if (!down) { /* up */ + if (ud[id].up_down == 0x10) { + pr_debug("%s UP[%zu]\n", tas->name, id); + ud[id].up_down |= 1; + ud[id].mismatch--; + } + } else if (down) { /* down */ + if (ud[id].up_down == 0) { + ud[id].up_down |= (1 << 4); + pr_debug("%s DOWN[%zu]\n", tas->name, id); + ud[id].mismatch++; + } else if (ud[id].up_down == 0x10) + return; + } + + if (ud[id].up_down == 0x11) { + pr_debug("%s CLEAR[%zu]\n", tas->name, id); + ud[id].up_down = 0; + ud[id].counter++; + } +} + +static void TSI_state(struct synaptics_rmi4_data *rmi4_data, + unsigned int tool, bool status) +{ + ud_log_status(&rmi4_data->touch_ud_stats, status); + input_mt_report_slot_state(rmi4_data->input_dev, tool, status); +} + +static void TSI_id(struct synaptics_rmi4_data *rmi4_data, int id) +{ + ud_set_id(&rmi4_data->touch_ud_stats, id); + input_mt_slot(rmi4_data->input_dev, id); +} + +#define INPUT_REPORT_MT_SLOT_STATE TSI_state +#define INPUT_REPORT_MT_SLOT TSI_id + + +static ssize_t synaptics_rmi4_ud_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + ssize_t total = 0; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + total += scnprintf(buf + total, PAGE_SIZE - total, "display:\n"); + for (i = 0; i < rmi4_data->touch_ud_stats.ud_len; i++) + total += scnprintf(buf + total, PAGE_SIZE - total, + "[%d]: full cycles-%u, mismatch-%d\n", i, + rmi4_data->touch_ud_stats.ud[i].counter, + rmi4_data->touch_ud_stats.ud[i].mismatch); + + if (rmi4_data->button_0d_enabled) { + total += scnprintf(buf + total, PAGE_SIZE - total, + "buttons:\n"); + for (i = 0; i < rmi4_data->button_ud_stats.ud_len; i++) + total += scnprintf(buf + total, PAGE_SIZE - total, + "[%d]: full cycles-%u, mismatch-%d\n", i, + rmi4_data->button_ud_stats.ud[i].counter, + rmi4_data->button_ud_stats.ud[i].mismatch); + } + return total; +} + +static ssize_t synaptics_dsx_ud_stat(struct synaptics_rmi4_data *rmi4_data, + char *buf, ssize_t size) +{ + int i; + ssize_t total = 0; + + total += scnprintf(buf + total, size - total, "screen: "); + for (i = 0; i < rmi4_data->touch_ud_stats.ud_len; i++) + if (rmi4_data->touch_ud_stats.ud[i].mismatch) + total += scnprintf(buf + total, size - total, + "%d)%u,%d ", i, + rmi4_data->touch_ud_stats.ud[i].counter, + rmi4_data->touch_ud_stats.ud[i].mismatch); + else if (rmi4_data->touch_ud_stats.ud[i].counter) + total += scnprintf(buf + total, size - total, + "%d)%u ", i, + rmi4_data->touch_ud_stats.ud[i].counter); + + if (rmi4_data->button_0d_enabled) { + total += scnprintf(buf + total, size - total, "buttons: "); + for (i = 0; i < rmi4_data->button_ud_stats.ud_len; i++) + if (rmi4_data->button_ud_stats.ud[i].mismatch) + total += scnprintf(buf + total, size - total, + "%d)%u,%d ", i, + rmi4_data->button_ud_stats.ud[i].counter, + rmi4_data->button_ud_stats.ud[i].mismatch); + else if (rmi4_data->button_ud_stats.ud[i].counter) + total += scnprintf(buf + total, size - total, + "%d)%u ", i, + rmi4_data->button_ud_stats.ud[i].counter); + } + return total; +} + +static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int reset; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + + if (sscanf(buf, "%u", &reset) != 1) + return -EINVAL; + + if (reset != 1) + return -EINVAL; + + retval = synaptics_dsx_ic_reset(rmi4_data, RMI4_HW_RESET); + if (retval > 0) + pr_debug("successful reset took %dms\n", retval); + else + dev_warn(&rmi4_data->i2c_client->dev, "%s: timed out waiting for idle\n", + __func__); + + return count; +} + +static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%s\n", + rmi4_data->rmi4_mod_info.product_id_string); +} + +static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int firmware_id; + unsigned int config_id; + struct synaptics_rmi4_device_info *rmi; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + rmi = &(rmi4_data->rmi4_mod_info); + + batohui(&firmware_id, rmi->build_id, sizeof(rmi->build_id)); + batohui(&config_id, rmi->config_id, sizeof(rmi->config_id)); + + return scnprintf(buf, PAGE_SIZE, "%x-%08x\n", firmware_id, config_id); +} + +static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%d\n", rmi4_data->in_bootloader); +} + +static ssize_t synaptics_rmi4_hw_irqstat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + switch (gpio_get_value(rmi4_data->board.irq_gpio)) { + case 0: + return scnprintf(buf, PAGE_SIZE, "Low\n"); + case 1: + return scnprintf(buf, PAGE_SIZE, "High\n"); + default: + pr_err("Failed to get GPIO for irq %d\n", rmi4_data->irq); + return scnprintf(buf, PAGE_SIZE, "Unknown\n"); + } +} + +#define SPRINTF_PAGE(fmt, args...) {\ + blen += scnprintf(buf + blen, PAGE_SIZE - blen, fmt, ##args);\ + } + +#include + +static ssize_t synaptics_rmi4_stats_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ii, gear, state, percentage; + bool count_time_since; + ssize_t blen = 0; + unsigned long long gear_time, check_point, perc_u64; + unsigned long long total = gStat.uptime_dur; + unsigned long long duration = (unsigned long long)0; + struct statistics *stats; + struct timespec end = ktime_to_timespec(ktime_get()); + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + if (gStat.enabled == false) { + pr_warn("statistics is not enabled\n"); + return blen; + } + + /* if statistics fetched while driver in suspend, time */ + /* since phone has entered suspend should not be added */ + count_time_since = atomic_read(&rmi4_data->touch_stopped) == 0; + if (count_time_since) { + duration = timediff_ms(ktime_to_timespec(gStat.uptime), end); + tk_debug("uptime since last resume %llu\n", duration); + total += duration; + } + tk_debug("total sweep time %llums\n", total); + + /* currently active gear */ + gear = gStat.dur->active; + stats = gStat.dur; + check_point = (unsigned long long)0; + if (count_time_since) { + duration = timediff_ms(ktime_to_timespec(stats->start), end); + pr_debug("%c time since last resume %llu\n", + stats->abbr, duration); + } + for (ii = 0; ii < stats->max; ii++) { + gear_time = stats->keeper[ii].duration; + if (ii == gear) + gear_time += duration; + perc_u64 = 100000*gear_time; + do_div(perc_u64, total); + percentage = (int)perc_u64; + SPRINTF_PAGE("%c%d: %d%%\n", stats->abbr, ii, + percentage/1000); + check_point += gear_time; + tk_debug("%c%d: %d%% %llu/%llu\n", stats->abbr, + (stats->flags & ONE_BASED_INDEX) ? ii+1 : ii, + percentage/1000, gear_time, total); + } + tk_debug("gear time check point: %llu <-> %llu\n", check_point, total); + + /* currently active NSM state */ + state = gStat.nms->active; + stats = gStat.nms; + check_point = (unsigned long long)0; + if (count_time_since) { + duration = timediff_ms(ktime_to_timespec(stats->start), end); + pr_debug("%c time since last resume %llu\n", + stats->abbr, duration); + } + for (ii = 0; ii < stats->max; ii++) { + gear_time = stats->keeper[ii].duration; + if (ii == state) + gear_time += duration; + perc_u64 = 100000*gear_time; + do_div(perc_u64, total); + percentage = (int)perc_u64; + SPRINTF_PAGE("%c%d: %d%%\n", stats->abbr, + (stats->flags & ONE_BASED_INDEX) ? ii+1 : ii, + percentage/1000); + check_point += gear_time; + tk_debug("%c%d: %d%% %llu/%llu\n", stats->abbr, ii, + percentage/1000, gear_time, total); + } + tk_debug("NMS time check point: %llu <-> %llu\n", check_point, total); + + /* current guard state */ + state = gStat.mpd->active; + stats = gStat.mpd; + check_point = (unsigned long long)0; + if (count_time_since) { + duration = timediff_ms(ktime_to_timespec(stats->start), end); + pr_debug("%c time since last resume %llu\n", + stats->abbr, duration); + } + for (ii = 0; ii < stats->max; ii++) { + gear_time = stats->keeper[ii].duration; + if (ii == state) + gear_time += duration; + perc_u64 = 100000*gear_time; + do_div(perc_u64, total); + percentage = (int)perc_u64; + SPRINTF_PAGE("%c%d: %d%%\n", stats->abbr, + (stats->flags & ONE_BASED_INDEX) ? ii+1 : ii, + percentage/1000); + check_point += gear_time; + tk_debug("%c%d: %d%% %llu/%llu\n", stats->abbr, ii, + percentage/1000, gear_time, total); + } + tk_debug("MPG time check point: %llu <-> %llu\n", check_point, total); + + stats = gStat.hop; + check_point = (unsigned long long)0; + if (count_time_since) { + duration = timediff_ms(ktime_to_timespec(stats->start), end); + pr_debug("%c time since last resume %llu\n", + stats->abbr, duration); + } + /* show starting from the oldest hops */ + for (ii = 1; ii <= stats->max; ii++) { + int idx = stats->active + ii; + + if (idx >= stats->max) + idx -= stats->max; + if (idx < 0 || idx > stats->max) { + pr_warn("idx outside the range\n"); + break; + } + pr_debug("idx[%d], id[%d]\n", idx, stats->keeper[idx].id); + if (stats->keeper[idx].id != -1) { + gear_time = stats->keeper[idx].duration; + /* add extra time to the last hop record only */ + if (ii == stats->max && stats->keeper[idx].id == gear) + gear_time += duration; + SPRINTF_PAGE("%c%d: %llums\n", stats->abbr, + stats->keeper[idx].id, gear_time); + check_point += gear_time; + } + } + tk_debug("uptime check point: %llu <-> %llu\n", check_point, total); + return blen; +} + +static ssize_t synaptics_rmi4_stats_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + if (gStat.enabled) { + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + statistics_reset(); + pr_info("statistics reset\n"); + statistics_start_timekeeping(rmi4_data); + } + return count; +} + +static ssize_t synaptics_rmi4_reporting_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + if (rmi4_data->rctrl.reporting_stopped) + length = scnprintf(buf, PAGE_SIZE, "STOPPED(%u)\n", + rmi4_data->rctrl.events_cnt); + else + length = scnprintf(buf, PAGE_SIZE, "RUNNING\n"); + return length; +} + +static ssize_t synaptics_rmi4_reporting_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value = 0; + int err = 0; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + if (*buf == 's' || *buf == 'S') { + rmi4_data->rctrl.reporting_stopped = true; + } else if (*buf == 'r' || *buf == 'R') { + rmi4_data->rctrl.reporting_stopped = false; + } else { + err = kstrtoul(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + rmi4_data->rctrl.reporting_stopped = value == 0; + } + if (rmi4_data->rctrl.reporting_stopped) + rmi4_data->rctrl.events_cnt = 0; + return count; +} + +static ssize_t synaptics_rmi4_reporting_ntouch_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + down(&rmi4_data->rctrl.ctrl_sema); + length = scnprintf(buf, PAGE_SIZE, "%d\n", rmi4_data->rctrl.max_seen); + rmi4_data->rctrl.max_seen = 0; + up(&rmi4_data->rctrl.ctrl_sema); + + return length; +} + +static ssize_t synaptics_rmi4_reporting_ntouch_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value; + int err; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + err = kstrtoul(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + down(&rmi4_data->rctrl.ctrl_sema); + rmi4_data->rctrl.expected = (int)value; + rmi4_data->rctrl.max_seen = 0; + up(&rmi4_data->rctrl.ctrl_sema); + + return count; +} + +static ssize_t synaptics_rmi4_drv_irq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%s\n", + rmi4_data->irq_enabled ? "ENABLED" : "DISABLED"); +} + +static ssize_t synaptics_rmi4_drv_irq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value = 0; + int err = 0; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + err = kstrtoul(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + switch (value) { + case 0: + /* Disable irq */ + synaptics_rmi4_irq_enable(rmi4_data, false); + break; + case 1: + /* Enable irq */ + synaptics_rmi4_irq_enable(rmi4_data, true); + break; + default: + pr_err("Invalid value\n"); + return -EINVAL; + } + return count; +} + +static ssize_t synaptics_rmi4_pm_qos_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%d\n", rmi4_data->pm_qos_latency); +} + +static ssize_t synaptics_rmi4_pm_qos_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value = 0; + uint32_t old; + int err = 0; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + err = kstrtoul(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + old = rmi4_data->pm_qos_latency; + + switch (value) { + case 0: + rmi4_data->pm_qos_latency = PM_QOS_DEFAULT_VALUE; + break; + default: + rmi4_data->pm_qos_latency = value; + break; + } + + if (old != rmi4_data->pm_qos_latency && + atomic_read(&rmi4_data->touch_stopped) == 0) { + synaptics_dsx_pm_qos(rmi4_data, PM_UPDATE, false); + pr_debug("set pm qos latency to %d\n", + rmi4_data->pm_qos_latency); + } + + return count; +} + +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) +static ssize_t synaptics_rmi4_test_irq_delay_ms_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%d\n", rmi4_data->test_irq_delay_ms); +} + +static ssize_t synaptics_rmi4_test_irq_delay_ms_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + int err; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + + err = kstrtouint(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + rmi4_data->test_irq_delay_ms = value; + return count; +} + +static ssize_t synaptics_rmi4_test_irq_data_contig_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%d\n", rmi4_data->test_irq_data_contig); +} + +static ssize_t synaptics_rmi4_test_irq_data_contig_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + int err; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + + err = kstrtouint(buf, 10, &value); + if (err < 0 || !(value == 0 || value == 1)) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + if (rmi4_data->test_irq_data_contig != value) { + rmi4_data->test_irq_data_contig = value; + synaptics_rmi4_irq_enable(rmi4_data, false); + /* requery for change to take effect */ + if (synaptics_rmi4_query_device(rmi4_data) < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to query device\n", + __func__); + } + synaptics_rmi4_irq_enable(rmi4_data, true); + } + return count; +} +#endif + +static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%u\n", + rmi4_data->button_0d_enabled); +} + +static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + unsigned char ii; + unsigned char intr_enable; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + rmi = &(rmi4_data->rmi4_mod_info); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + input = input > 0 ? 1 : 0; + + if (rmi4_data->button_0d_enabled == input) + return count; + + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) { + ii = fhandler->intr_reg_num; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr + 1 + ii, + &intr_enable, + sizeof(intr_enable)); + if (retval < 0) + return retval; + + if (input == 1) + intr_enable |= fhandler->intr_mask; + else + intr_enable &= ~fhandler->intr_mask; + + retval = synaptics_rmi4_i2c_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr + 1 + ii, + &intr_enable, + sizeof(intr_enable)); + if (retval < 0) + return retval; + } + } + + rmi4_data->button_0d_enabled = input; + + return count; +} + +static ssize_t synaptics_rmi4_ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int build_id, config_id; + struct synaptics_rmi4_device_info *rmi; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + rmi = &(rmi4_data->rmi4_mod_info); + batohui(&build_id, rmi->build_id, sizeof(rmi->build_id)); + batohui(&config_id, rmi->config_id, sizeof(rmi->config_id)); + return scnprintf(buf, PAGE_SIZE, + "%s%s\n%s%x\n%s%x\n", + "Product ID: ", rmi->product_id_string, + "Build ID: ", build_id, + "Config ID: ", config_id); +} + +static ssize_t synaptics_rmi4_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%d\n", + atomic_read(&rmi4_data->touch_stopped) == 0 && + rmi4_data->flash_enabled); +} + +static ssize_t synaptics_dsx_patch_dump( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_dsx_patch *patch, + char *pout, ssize_t msize) +{ + struct synaptics_dsx_func_patch *fp; + ssize_t blen = 0; + + down(&patch->list_sema); + list_for_each_entry(fp, &patch->cfg_head, link) { + int i; + unsigned int data_size = fp->size; + unsigned char *value = fp->data; + + blen += scnprintf(pout + blen, msize - blen, + "F%02x%c@%d:%d=", fp->func & 0xff, + register_type_to_ascii(fp->func & 0xf00), + fp->regstr, fp->subpkt); + + for (i = 0; i < data_size; i++) + blen += scnprintf(pout + blen, msize - blen, + "%02x", *value++); + if (fp->bitmask) + blen += scnprintf(pout + blen, msize - blen, + "&%02x", fp->bitmask); + blen += scnprintf(pout + blen, msize - blen, "; "); + } + up(&patch->list_sema); + + if (patch->flags & FLAG_FORCE_UPDATE) + blen += scnprintf(pout + blen, msize - blen, "U; "); + if (patch->flags & FLAG_WAKEABLE) + blen += scnprintf(pout + blen, msize - blen, "W; "); + if (patch->flags & FLAG_POWER_SLEEP) + blen += scnprintf(pout + blen, msize - blen, "S; "); + blen += scnprintf(pout + blen, msize - blen, "\n"); + + return blen; +} + +static ssize_t synaptics_dsx_patch_query( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_dsx_patch *patch, + char *pout, ssize_t msize) +{ + struct synaptics_dsx_func_patch *fp; + ssize_t blen = 0; + + down(&patch->list_sema); + list_for_each_entry(fp, &patch->cfg_head, link) { + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_packet_reg *reg; + int i, error, function; + unsigned short data_addr; + unsigned int data_size; + unsigned char *value, rt_mod; + + regs = find_function(rmi4_data, fp->func); + if (!regs) + continue; + reg = find_packet_reg(regs, fp->regstr); + if (!reg || reg->offset < 0) { + pr_err("F%x@%d not present\n", fp->func, fp->regstr); + continue; + } + + rt_mod = register_type_to_ascii(fp->func & 0xf00); + function = fp->func & 0xff; + + error = synaptics_rmi4_read_packet_reg(rmi4_data, + regs, reg->r_number); + if (error < 0) { + pr_err("F%x@%d register read failed\n", + function, fp->regstr); + continue; + } + /* calculate register address */ + data_addr = regs->base_addr + reg->offset; + /* dump whole register if subpacket is 255 */ + if (fp->subpkt == 0xff) { + value = reg->data; + data_size = reg->size; + blen += scnprintf(pout + blen, msize - blen, + "F%x%c@%d{%X}=", + function, rt_mod, fp->regstr, data_addr); + } else { + struct synaptics_rmi4_subpkt *subpkt; + + subpkt = reg->subpkt + fp->subpkt; + value = (unsigned char *)subpkt->data; + data_size = subpkt->size; + data_addr += subpkt->offset; + blen += scnprintf(pout + blen, msize - blen, + "F%x%c@%d:%d{%X}=", + function, rt_mod, fp->regstr, + fp->subpkt, data_addr); + } + + for (i = 0; i < data_size; i++) + blen += scnprintf(pout + blen, msize - blen, + "%02x", *value++); + blen += scnprintf(pout + blen, msize - blen, "; "); + } + up(&patch->list_sema); + blen += scnprintf(pout + blen, msize - blen, "\n"); + return blen; +} + +static ssize_t modifiers_show(struct synaptics_rmi4_data *rmi4_data, + char *buf, size_t count, bool show_data, bool show_query) +{ + struct config_modifier *cm; + ssize_t (*handler)(struct synaptics_rmi4_data *, + struct synaptics_dsx_patch *, + char *, ssize_t); + ssize_t added, blen = 0; + + if (!rmi4_data->modifiers.mods_num) { + pr_debug("patchset is empty!\n"); + return 0; + } + + down(&rmi4_data->modifiers.list_sema); + list_for_each_entry(cm, &rmi4_data->modifiers.mod_head, link) { + const char *name = cm->name ? cm->name : "na"; + bool is_query = cm->id == SYNA_MOD_QUERY; + + /* skip printing modifiers, but query */ + if (!is_query && show_query) + continue; + blen += scnprintf(buf + blen, count - blen, "%c[%s]\n", + cm->effective ? '+' : '-', name); + if (!show_data) + continue; + + if (is_query && show_query) + handler = &synaptics_dsx_patch_query; + else + handler = &synaptics_dsx_patch_dump; + + if (cm->active) { + blen += scnprintf(buf + blen, + count - blen, " [active]"); + added = (*handler)(rmi4_data, cm->active, + buf + blen, count - blen); + blen += added; + count -= added; + } + + if (cm->clipa) { + blen += scnprintf(buf + blen, count - blen, + " [clip]<%u,%u,%u,%u>%s\n", + cm->clipa->xul_clip, cm->clipa->yul_clip, + cm->clipa->xbr_clip, cm->clipa->ybr_clip, + cm->clipa->inversion ? "in" : "out"); + } + + if (cm->suspended) { + blen += scnprintf(buf + blen, + count - blen, " [suspended]"); + added = (*handler)(rmi4_data, cm->suspended, + buf + blen, count - blen); + blen += added; + count -= added; + } + } + up(&rmi4_data->modifiers.list_sema); + + return blen; +} + +#define NAME_ID 0 +#define ACTION_ID 1 + +static ssize_t synaptics_rmi4_mod_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_dsx_patch **patch_ptr, *patch; + struct config_modifier *cm; + bool clear_only = false; + int id, bytes_left, ntokens = 0; + char *internal, *ptr, *token[2] = {0}; + + internal = kstrdup(buf, GFP_KERNEL); + for (ptr = internal; ntokens < 2; ntokens++) { + bytes_left = count - (ptr - internal); + ptr = strnchr(ptr, bytes_left, '['); + if (!ptr) + break; + token[ntokens] = ++ptr; /* advance to the name */ + bytes_left = count - (ptr - internal); + ptr = strnchr(ptr, bytes_left, ']'); + if (ptr) + *ptr++ = 0; + dev_dbg(dev, "token[%d]='%s'\n", ntokens, token[ntokens]); + } + + if (*buf != '[' && ntokens != 2) { + dev_err(dev, "invalid modifier syntax\n"); + count = -EINVAL; + goto leave_now; + } + + id = modifier_name2id(token[NAME_ID]); + switch (id) { + case SYNA_MOD_QUERY: + dev_err(dev, "use query attribute instead\n"); + count = -EINVAL; + break; + case -1: + dev_err(dev, "[%s] is invalid modifier\n", token[NAME_ID]); + count = -EINVAL; + break; + } + + if (count == -EINVAL) + goto leave_now; + + /* pointer has been advanced in the loop above to point to patch */ + if (*ptr == '\n') + clear_only = true; + + cm = modifier_by_id(rmi4_data, id); + if (!cm) { + if (clear_only) + goto leave_now; + + dev_info(dev, "allocating modifier [%s]\n", token[NAME_ID]); + cm = kzalloc(sizeof(*cm), GFP_KERNEL); + if (!cm) { + count = -ENOMEM; + goto leave_now; + } + + down(&rmi4_data->modifiers.list_sema); + list_add_tail(&cm->link, &rmi4_data->modifiers.mod_head); + up(&rmi4_data->modifiers.list_sema); + rmi4_data->modifiers.mods_num++; + cm->name = ascii_names[id]; + cm->id = id; + } + + if (!strncmp(token[ACTION_ID], "clip", 4)) { + bool clipping_is_active = false; + struct synaptics_clip_area clipa; + + if (cm->clipa) { + if (rmi4_data->clipping_on && + rmi4_data->clipa == cm->clipa) { + /* if currently effective clipping */ + /* is the one being edited */ + rmi4_data->clipa = NULL; + clipping_is_active = true; + } + kfree(cm->clipa); + cm->clipa = NULL; + } + + if (clear_only) { + if (clipping_is_active) { + rmi4_data->clipping_on = false; + pr_debug("removed [clip] from modifier [%s]\n", + token[NAME_ID]); + } + goto leave_now; + } + + ntokens = sscanf(ptr, "<%u %u %u %u %u>", &clipa.xul_clip, + &clipa.yul_clip, &clipa.xbr_clip, + &clipa.ybr_clip, &clipa.inversion); + if (ntokens != 5) { + dev_err(dev, "Invalid clip area modifier\n"); + goto leave_now; + } + + if (!cm->clipa) { + cm->clipa = kzalloc(sizeof(clipa), GFP_KERNEL); + if (!cm->clipa) { + count = -ENOMEM; + goto leave_now; + } + } + + memcpy(cm->clipa, &clipa, sizeof(clipa)); + + if (clipping_is_active) + rmi4_data->clipa = cm->clipa; + + pr_debug("added [clip] area to modifier [%s]\n", + token[NAME_ID]); + goto leave_now; + } + + if (!strncmp(token[ACTION_ID], "active", 6)) + patch_ptr = &cm->active; + else + patch_ptr = &cm->suspended; + + if (*patch_ptr) { + synaptics_dsx_free_patch(*patch_ptr); + *patch_ptr = NULL; + } + + if (clear_only) { + pr_debug("removed [%s] from modifier [%s]\n", + token[ACTION_ID], token[NAME_ID]); + goto apply_and_leave; + } + + patch = synaptics_dsx_init_patch(token[ACTION_ID]); + if (!patch) { + count = -ENOMEM; + goto leave_now; + } + + synaptics_dsx_parse_string(rmi4_data, ptr, patch, true); + *patch_ptr = patch; + pr_debug("added [%s] patch to modifier [%s]\n", + token[ACTION_ID], token[NAME_ID]); +apply_and_leave: + if (!rmi4_data->in_bootloader) + synaptics_dsx_enforce_modifiers(rmi4_data, cm); +leave_now: + kfree(internal); + return count; +} + +static ssize_t synaptics_rmi4_mod_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return modifiers_show(rmi4_data, buf, PAGE_SIZE, true, false); +} + +static ssize_t synaptics_rmi4_mod_sw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int id; + bool set_op; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct config_modifier *cm; + char mod_name[32]; + + if (*buf != '+' && *buf != '-') { + dev_err(dev, "invalid modifier syntax\n"); + return -EINVAL; + } + + set_op = *buf == '-' ? false : true; + if (sscanf(buf + 1, "[%s]", mod_name) != 1) { + dev_err(dev, "unable to read modifier name\n"); + return -EINVAL; + } + + id = modifier_name2id(mod_name); + if (id == -1) { + dev_err(dev, "invalid modifier name '%s'\n", mod_name); + return -EINVAL; + } + + cm = modifier_by_id(rmi4_data, id); + if (!cm) { + dev_err(dev, "modifier with id=%d does not exist\n", id); + return -EINVAL; + } + + if (cm->effective != set_op) { + cm->effective = set_op; + pr_debug("set [%s] %s\n", cm->name, + cm->effective ? "on" : "off"); + + if (cm->effective) { + if (cm->clipa) { + rmi4_data->clipping_on = true; + rmi4_data->clipa = cm->clipa; + pr_debug("enable clipping in [%s]\n", cm->name); + } + } else if (cm->clipa && rmi4_data->clipa == cm->clipa) { + rmi4_data->clipping_on = false; + rmi4_data->clipa = NULL; + pr_debug("disable clipping in [%s]\n", cm->name); + } + + if (!rmi4_data->in_bootloader) + synaptics_dsx_enforce_modifiers(rmi4_data, cm); + } + return count; +} + +static ssize_t synaptics_rmi4_mod_sw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return modifiers_show(rmi4_data, buf, PAGE_SIZE, false, false); +} + +/* NOTE: query is stored in active */ +static ssize_t synaptics_rmi4_query_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_dsx_patch *patch; + struct config_modifier *cm = modifier_by_id(rmi4_data, SYNA_MOD_QUERY); + + if (!cm) { + dev_info(dev, "allocating modifier [%s]\n", + modifier_id2name(SYNA_MOD_QUERY)); + cm = kzalloc(sizeof(*cm), GFP_KERNEL); + if (!cm) { + dev_err(dev, "Cannot allocate query modifier\n"); + return -ENOMEM; + } + + down(&rmi4_data->modifiers.list_sema); + list_add_tail(&cm->link, &rmi4_data->modifiers.mod_head); + up(&rmi4_data->modifiers.list_sema); + rmi4_data->modifiers.mods_num++; + cm->name = ascii_names[SYNA_MOD_QUERY]; + cm->id = SYNA_MOD_QUERY; + } + + if (*buf == '\n') { + if (cm->active) { + synaptics_dsx_free_patch(cm->active); + cm->active = NULL; + pr_debug("discarded current query\n"); + } + goto leave_now; + } + + if (*buf != 'F') { + dev_err(dev, "invalid query syntax\n"); + return -EINVAL; + } + + patch = synaptics_dsx_init_patch(ascii_names[SYNA_MOD_QUERY]); + if (!patch) + return -ENOMEM; + + synaptics_dsx_parse_string(rmi4_data, buf, patch, false); + cm->active = patch; + pr_debug("new query added\n"); + +leave_now: + return count; +} + +static ssize_t synaptics_rmi4_query_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct config_modifier *cm = modifier_by_id(rmi4_data, SYNA_MOD_QUERY); + + return !cm ? 0 : modifiers_show(rmi4_data, buf, PAGE_SIZE, true, true); +} + + /** + * synaptics_rmi4_set_page() + * + * Called by synaptics_rmi4_i2c_read() and synaptics_rmi4_i2c_write(). + * + * This function writes to the page select register to switch to the + * assigned page. + */ +static int synaptics_rmi4_set_page(struct synaptics_rmi4_data *rmi4_data, + unsigned int address) +{ + int retval = 0; + bool enforce_page_select = false; + unsigned char retry; + unsigned char buf[PAGE_SELECT_LEN]; + unsigned char page; + struct i2c_client *i2c = rmi4_data->i2c_client; + + if (address & PAGE_SELECT_ENFORCE) { + enforce_page_select = true; + dev_dbg(&i2c->dev, "%s: Enforced page select\n", __func__); + address &= ~PAGE_SELECT_ENFORCE; + } + + page = ((address >> 8) & MASK_8BIT); + if (page != rmi4_data->current_page || enforce_page_select) { + buf[0] = MASK_8BIT; + buf[1] = page; + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + retval = i2c_master_send(i2c, buf, PAGE_SELECT_LEN); + if (retval != PAGE_SELECT_LEN) { + dev_err(&i2c->dev, + "%s: I2C retry %d\n", + __func__, retry + 1); + usleep_range(5000, 5000); + } else { + rmi4_data->current_page = page; + break; + } + } + } else + return PAGE_SELECT_LEN; + return (retval == PAGE_SELECT_LEN) ? retval : -EIO; +} + +int alloc_buffer(struct temp_buffer *tb, size_t count) +{ + if (tb->buf_size != 0) { + kfree(tb->buf); + tb->buf = NULL; + tb->buf_size = 0; + } + + if (count > MAX_READ_WRITE_SIZE) { + pr_err("%s: Max buffer size exceeded\n", __func__); + return 1; + } + + if (count < MIN_READ_WRITE_BUF_SIZE) + count = MIN_READ_WRITE_BUF_SIZE; + + tb->buf = kzalloc(count, GFP_KERNEL); + if (!tb->buf) + return 1; + + tb->buf_size = count; + return 0; +} +EXPORT_SYMBOL(alloc_buffer); + + /** + * synaptics_rmi4_i2c_read() + * + * Called by various functions in this driver, and also exported to + * other expansion Function modules such as rmi_dev. + * + * This function reads data of an arbitrary length from the sensor, + * starting from an assigned register address of the sensor, via I2C + * with a retry mechanism. + */ +static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned short length) +{ + int retval; + unsigned char retry = SYN_I2C_RETRY_TIMES; + unsigned char buf; + struct i2c_msg msg[] = { + { + .addr = rmi4_data->i2c_client->addr, + .flags = 0, + .len = 1, + .buf = &buf, + }, + { + .addr = rmi4_data->i2c_client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + }, + }; + + buf = addr & MASK_8BIT; + + mutex_lock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + retval = synaptics_rmi4_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) + goto exit; + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(rmi4_data->i2c_client->adapter, msg, 2) == 2) { + retval = length; + break; + } + dev_err(&rmi4_data->i2c_client->dev, + "%s: I2C retry %d\n", + __func__, retry + 1); + synaptics_dropbox_report_event_ratelimit( + SYNAPTICS_DROPBOX_MSG_I2C, 1); + usleep_range(5000, 5000); + } +exit: + mutex_unlock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + if (retry == SYN_I2C_RETRY_TIMES) { + synaptics_dropbox_report_event(SYNAPTICS_DROPBOX_MSG_I2C, 1); + retval = -EIO; + } + + return retval; +} + + /** + * synaptics_rmi4_i2c_write() + * + * Called by various functions in this driver, and also exported to + * other expansion Function modules such as rmi_dev. + * + * This function writes data of an arbitrary length to the sensor, + * starting from an assigned register address of the sensor, via I2C with + * a retry mechanism. + */ +static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned short length) +{ + int retval; + unsigned char retry = SYN_I2C_RETRY_TIMES; + struct temp_buffer *tb = &rmi4_data->write_buf; + struct i2c_msg msg[1]; + + mutex_lock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + retval = synaptics_rmi4_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) + goto exit; + + if (tb->buf_size < (length+1) && alloc_buffer(tb, length+1) != 0) { + retval = -ENOMEM; + goto exit; + } + + msg[0].addr = rmi4_data->i2c_client->addr; + msg[0].flags = 0; + msg[0].len = length + 1; + msg[0].buf = tb->buf; + + *(tb->buf) = addr & MASK_8BIT; + memcpy(tb->buf+1, &data[0], length); + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(rmi4_data->i2c_client->adapter, msg, 1) == 1) { + retval = length; + break; + } + dev_err(&rmi4_data->i2c_client->dev, + "%s: I2C retry %d\n", + __func__, retry + 1); + synaptics_dropbox_report_event_ratelimit( + SYNAPTICS_DROPBOX_MSG_I2C, 1); + msleep(20); + } +exit: + mutex_unlock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + if (retry == SYN_I2C_RETRY_TIMES) { + synaptics_dropbox_report_event(SYNAPTICS_DROPBOX_MSG_I2C, 1); + retval = -EIO; + } + + return retval; +} + +static int synaptics_rmi4_f12_wakeup_gesture( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + struct synaptics_rmi4_func_packet_regs *regs = + find_function(rmi4_data, SYNAPTICS_RMI4_F12 | DATA_TYPE); + struct f12_d4_s0_type *f12_d4_0; + struct synaptics_rmi4_packet_reg *reg_data_4; + struct synaptics_rmi4_subpkt *subpkt = + find_subpkt(regs, 4, 0, ®_data_4, (void **)&f12_d4_0); + + if (subpkt && subpkt->offset == -1) { + dev_err(&rmi4_data->i2c_client->dev, + "unable to clear wakeup gesture IRQ\n"); + return -EINVAL; + } + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.data_base + reg_data_4->offset, + &f12_d4_0->gesture, sizeof(*f12_d4_0)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "failure clearing wakeup gesture IRQ rc=%d\n", retval); + return retval; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "wakeup gesture status=0x%02x\n", f12_d4_0->gesture); + + if (f12_d4_0->gesture & DOUBLE_TAP_GESTURE) { + /* emulate power key press */ + input_report_key(rmi4_data->input_dev, KEY_POWER, 1); + input_report_key(rmi4_data->input_dev, KEY_POWER, 0); + input_sync(rmi4_data->input_dev); + + dev_dbg(&rmi4_data->i2c_client->dev, + "DBL_TAP wakeup gesture detected\n"); + } + + return 0; +} + +static void analyze_finger_data(struct synaptics_rmi4_data *rmi4_data, + struct f12_d1_s0_type *data) +{ + int id, finger_cnt = 0; + + id = finger_cnt; + for (id = 0; id < rmi4_data->num_of_fingers; id++, data++) + if (data->type_and_stylus) + finger_cnt++; + + down(&rmi4_data->rctrl.ctrl_sema); + if (finger_cnt >= rmi4_data->rctrl.expected && + finger_cnt > rmi4_data->rctrl.max_seen) + rmi4_data->rctrl.max_seen = finger_cnt; + up(&rmi4_data->rctrl.ctrl_sema); +} + + /** + * synaptics_rmi4_f12_abs_report() + * + * Called by synaptics_rmi4_report_touch() when valid Function $12 + * finger data has been detected. + * + * This function reads the Function $12 data registers, determines the + * status of each finger supported by the Function, processes any + * necessary coordinate manipulation, reports the finger data to + * the input subsystem, and returns the number of fingers detected. + */ +static int synaptics_rmi4_f12_abs_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char touch_count = 0; /* number of touch points */ + unsigned char finger; + unsigned char fingers_to_process; + unsigned char finger_status; + unsigned short data_addr; + unsigned short data_size; + int x; + int y; + int p; + int w; + int id; +#ifdef USE_TIME_SYNC_EVENTS + struct timespec hw_time = ktime_to_timespec(ktime_get()); +#endif + struct f12_d1_s0_type *finger_data; + struct f12_d1_s0_type *finger_data_buf; + struct synaptics_rmi4_packet_reg *reg_data_1 = + &rmi4_data->f12_data_registers_ptr->regs[F12_D1_IDX]; + static int print_xy = -1; + + if (unlikely(print_xy == -1)) { + print_xy = 0; + if (strncmp(bi_bootmode(), "mot-factory", strlen("mot-factory")) == 0) + print_xy = 1; + } + + if (atomic_read(&rmi4_data->panel_off_flag)) + return 0; + + fingers_to_process = rmi4_data->num_of_fingers; + + if (rmi4_data->touch_data_contiguous) + finger_data_buf = (struct f12_d1_s0_type *)(rmi4_data->touch_data + + rmi4_data->num_of_intr_regs); + else { + data_addr = fhandler->full_addr.data_base; + data_size = fingers_to_process * + fhandler->size_of_data_register_block; + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr + reg_data_1->offset, + reg_data_1->data, + data_size); + if (retval < 0) + return 0; + + finger_data_buf = (struct f12_d1_s0_type *)reg_data_1->data; + } + + /* count valid event */ + rmi4_data->rctrl.events_cnt++; + if (rmi4_data->rctrl.reporting_stopped) { + analyze_finger_data(rmi4_data, finger_data_buf); + return 0; + } +#ifdef USE_TIME_SYNC_EVENTS + input_event(rmi4_data->input_dev, EV_SYN, + SYN_TIME_SEC, hw_time.tv_sec); + input_event(rmi4_data->input_dev, EV_SYN, + SYN_TIME_NSEC, hw_time.tv_nsec); +#endif + finger_data = finger_data_buf; + for (finger = 0; finger < fingers_to_process; finger++, finger_data++) { + finger_status = finger_data->type_and_stylus; + if (finger_status) { + x = finger_data->x_lsb | (finger_data->x_msb << 8); + y = finger_data->y_lsb | (finger_data->y_msb << 8); + p = w = finger_data->z; + id = finger; + + if (rmi4_data->board.x_flip) + x = rmi4_data->sensor_max_x - x; + if (rmi4_data->board.y_flip) + y = rmi4_data->sensor_max_y - y; + + if (rmi4_data->clipping_on && rmi4_data->clipa) { + bool inside; + + inside = (x >= rmi4_data->clipa->xul_clip) && + (x <= rmi4_data->clipa->xbr_clip) && + (y >= rmi4_data->clipa->yul_clip) && + (y <= rmi4_data->clipa->ybr_clip); + + if (inside == rmi4_data->clipa->inversion) { + /* Touch might still be active, but we're */ + /* sending release anyway to avoid touch */ + /* stuck at the last reported position. */ + /* Driver will suppress reporting to UI */ + /* touch events from within clipping area */ + INPUT_REPORT_MT_SLOT(rmi4_data, finger); + INPUT_REPORT_MT_SLOT_STATE(rmi4_data, + MT_TOOL_FINGER, 0); + + dev_dbg(&rmi4_data->i2c_client->dev, + "finger id-%d (%d,%d) within clipping area\n", + finger, x, y); + + continue; + } + } +#ifdef TYPE_B_PROTOCOL + INPUT_REPORT_MT_SLOT(rmi4_data, finger); + INPUT_REPORT_MT_SLOT_STATE(rmi4_data, + MT_TOOL_FINGER, finger_status); +#endif + if (print_xy) { + dev_info(&rmi4_data->i2c_client->dev, + "%s: Finger %d:\n" + "x = %d\n" + "y = %d\n" + "p = %d\n" + "w = %d\n", + __func__, finger, + x, y, p, w); + } else { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Finger %d:\n" + "x = %d\n" + "y = %d\n" + "p = %d\n" + "w = %d\n", + __func__, finger, + x, y, p, w); + } + + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_Y, y); + input_report_abs(rmi4_data->input_dev, + ABS_MT_PRESSURE, p); + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, w); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + touch_count++; +#ifdef TYPE_B_PROTOCOL + } else { + INPUT_REPORT_MT_SLOT(rmi4_data, finger); + /* Touch no longer active, close out slot */ + INPUT_REPORT_MT_SLOT_STATE(rmi4_data, + MT_TOOL_FINGER, 0); +#endif + } + } + + if (!touch_count) { +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + } + + input_mt_report_pointer_emulation(rmi4_data->input_dev, false); + input_sync(rmi4_data->input_dev); + + return touch_count; +} + + /** + * synaptics_rmi4_f11_abs_report() + * + * Called by synaptics_rmi4_report_touch() when valid Function $11 + * finger data has been detected. + * + * This function reads the Function $11 data registers, determines the + * status of each finger supported by the Function, processes any + * necessary coordinate manipulation, reports the finger data to + * the input subsystem, and returns the number of fingers detected. + */ +static int synaptics_rmi4_f11_abs_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char touch_count = 0; /* number of touch points */ + unsigned char reg_index; + unsigned char finger; + unsigned char fingers_supported; + unsigned char num_of_finger_status_regs; + unsigned char finger_shift; + unsigned char finger_status; + unsigned char data_reg_blk_size; + unsigned char finger_status_reg[3]; + unsigned char data[F11_STD_DATA_LEN]; + unsigned short data_addr; + unsigned short data_offset; + int x; + int y; + int wx; + int wy; + int z; +#ifdef USE_TIME_SYNC_EVENTS + struct timespec hw_time = ktime_to_timespec(ktime_get()); +#endif + /* + * The number of finger status registers is determined by the + * maximum number of fingers supported - 2 bits per finger. So + * the number of finger status registers to read is: + * register_count = ceil(max_num_of_fingers / 4) + */ + fingers_supported = fhandler->num_of_data_points; + num_of_finger_status_regs = (fingers_supported + 3) / 4; + data_addr = fhandler->full_addr.data_base; + data_reg_blk_size = fhandler->size_of_data_register_block; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr, + finger_status_reg, + num_of_finger_status_regs); + if (retval < 0) + return 0; + /* count valid event */ + rmi4_data->rctrl.events_cnt++; + if (rmi4_data->rctrl.reporting_stopped) + return 0; + + if (atomic_read(&rmi4_data->panel_off_flag)) + return 0; + +#ifdef USE_TIME_SYNC_EVENTS + input_event(rmi4_data->input_dev, EV_SYN, + SYN_TIME_SEC, hw_time.tv_sec); + input_event(rmi4_data->input_dev, EV_SYN, + SYN_TIME_NSEC, hw_time.tv_nsec); +#endif + for (finger = 0; finger < fingers_supported; finger++) { + reg_index = finger / 4; + finger_shift = (finger % 4) * 2; + finger_status = (finger_status_reg[reg_index] >> finger_shift) + & MASK_2BIT; + /* + * Each 2-bit finger status field represents the following: + * 00 = finger not present + * 01 = finger present and data accurate + * 10 = finger present but data may be inaccurate + * 11 = reserved + */ + if (finger_status) { + data_offset = data_addr + + num_of_finger_status_regs + + (finger * data_reg_blk_size); + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_offset, + data, + data_reg_blk_size); + if (retval < 0) + return 0; + + x = (data[0] << 4) | (data[2] & MASK_4BIT); + y = (data[1] << 4) | ((data[2] >> 4) & MASK_4BIT); + wx = (data[3] & MASK_4BIT); + wy = (data[3] >> 4) & MASK_4BIT; + z = data[4]; + + if (rmi4_data->board.x_flip) + x = rmi4_data->sensor_max_x - x; + if (rmi4_data->board.y_flip) + y = rmi4_data->sensor_max_y - y; + + if (rmi4_data->clipping_on && rmi4_data->clipa) { + bool inside; + + inside = (x >= rmi4_data->clipa->xul_clip) && + (x <= rmi4_data->clipa->xbr_clip) && + (y >= rmi4_data->clipa->yul_clip) && + (y <= rmi4_data->clipa->ybr_clip); + + if (!inside) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%d,%d ouside clipping area\n", + x, y); + continue; + } + } +#ifdef TYPE_B_PROTOCOL + INPUT_REPORT_MT_SLOT(rmi4_data, finger); + INPUT_REPORT_MT_SLOT_STATE(rmi4_data, + MT_TOOL_FINGER, finger_status); +#endif + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Finger %d:\n" + "x = %d\n" + "y = %d\n" + "wx = %d\n" + "wy = %d\n" + "z = %d\n", + __func__, finger, + x, y, wx, wy, z); + + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_Y, y); + input_report_abs(rmi4_data->input_dev, + ABS_MT_PRESSURE, z); + +#ifdef REPORT_2D_W + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, max(wx, wy)); + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, min(wx, wy)); +#endif +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + touch_count++; +#ifdef TYPE_B_PROTOCOL + } else { + INPUT_REPORT_MT_SLOT(rmi4_data, finger); + /* Touch no longer active, close out slot */ + INPUT_REPORT_MT_SLOT_STATE(rmi4_data, + MT_TOOL_FINGER, 0); +#endif + } + } + +#ifndef TYPE_B_PROTOCOL + if (!touch_count) + input_mt_sync(rmi4_data->input_dev); +#endif + + input_mt_report_pointer_emulation(rmi4_data->input_dev, false); + input_sync(rmi4_data->input_dev); + + return touch_count; +} + +static void synaptics_rmi4_f1a_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char button; + unsigned char index; + unsigned char shift; + unsigned char status; + unsigned char *data; + unsigned short data_addr = fhandler->full_addr.data_base; + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + static bool current_status[MAX_NUMBER_OF_BUTTONS]; +#ifdef NO_0D_WHILE_2D + static bool before_2d_status[MAX_NUMBER_OF_BUTTONS]; + static bool while_2d_status[MAX_NUMBER_OF_BUTTONS]; +#endif + + if (rmi4_data->tsb_buff_clean_flag) { + memset(current_status, 0, sizeof(current_status)); +#ifdef NO_0D_WHILE_2D + memset(before_2d_status, 0, sizeof(before_2d_status)); + memset(while_2d_status, 0, sizeof(while_2d_status)); +#endif + rmi4_data->tsb_buff_clean_flag = 0; + } + + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr, + f1a->button_data_buffer, + f1a->button_bitmask_size); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read button data registers\n", + __func__); + return; + } + + if (atomic_read(&rmi4_data->panel_off_flag)) + return; + + data = f1a->button_data_buffer; + + for (button = 0; button < f1a->valid_button_count; button++) { + index = button / 8; + shift = button % 8; + status = ((data[index] >> shift) & MASK_1BIT); + + if (current_status[button] == status) + continue; + else + current_status[button] = status; + + ud_set_id(&rmi4_data->button_ud_stats, button); + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Button %d (code %d) ->%d\n", + __func__, button, + f1a->button_map[button], + status); +#ifdef NO_0D_WHILE_2D + if (rmi4_data->fingers_on_2d == false) { + if (status == 1) { + before_2d_status[button] = 1; + } else { + if (while_2d_status[button] == 1) { + while_2d_status[button] = 0; + continue; + } else { + before_2d_status[button] = 0; + } + } + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + ud_log_status(&rmi4_data->button_ud_stats, status == 1); + } else { + if (before_2d_status[button] == 1) { + before_2d_status[button] = 0; + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + ud_log_status(&rmi4_data->button_ud_stats, status == 1); + } else { + if (status == 1) + while_2d_status[button] = 1; + else + while_2d_status[button] = 0; + } + } +#else + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + ud_log_status(&rmi4_data->button_ud_stats, status == 1); +#endif + } + + input_sync(rmi4_data->input_dev); + + return; +} + +static void synaptics_rmi4_f01_handler(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned short data_addr = fhandler->full_addr.data_base; + struct synaptics_rmi4_f01_device_status status; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr, + status.data, + sizeof(status.data)); + if (retval < 0) + return; + + switch (status.status_code) { + case 0x00: + dev_info(&rmi4_data->i2c_client->dev, + "%s: No error.\n", __func__); + break; + + case 0x01: + dev_info(&rmi4_data->i2c_client->dev, + "%s: Touch IC reset complete.\n", __func__); + break; + + case 0x02: + dev_info(&rmi4_data->i2c_client->dev, + "%s: Touch IC configuration error--%s.\n", + __func__, "check platform settings"); + break; + + case 0x03: + dev_info(&rmi4_data->i2c_client->dev, + "%s: Touch IC device failure.\n", __func__); + break; + + case 0x04: + dev_info(&rmi4_data->i2c_client->dev, + "%s: Configuration CRC failure.\n", __func__); + break; + + case 0x05: + dev_info(&rmi4_data->i2c_client->dev, + "%s: Firmware CRC failure.\n", __func__); + break; + + case 0x06: + dev_info(&rmi4_data->i2c_client->dev, + "%s: CRC in progress.\n", __func__); + break; + + default: + dev_info(&rmi4_data->i2c_client->dev, + "%s: Unknown error 0x%02X received.\n", + __func__, status.status_code); + break; + } +} + +#define IM_BIT 0 +#define NMS_BIT 1 +#define CID_IM_BIT 2 +#define FS_IM_BIT 3 +#define FREQ_BIT 4 +#define GUARD_BIT 5 + +static void synaptics_rmi4_f51_handler(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_packet_reg *reg; + struct synaptics_rmi4_subpkt *subpkt; + struct f54_d6_s0_type *f54_d6_0; + struct f54_d10_s0_type *f54_d10_0; + struct f54_d14_s0_type *f54_d14_0; + struct f54_d16_s0_type *f54_d16_0; + struct f54_d17_s0_type *f54_d17_0; + struct f51_d0_s0_type *f51_d0_0; + unsigned char presence_mask = 0; + ktime_t log = ktime_get(); + int ii, error; + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F51 | DATA_TYPE); + if (!regs) + return; + + reg = find_packet_reg(regs, 0); + if (!reg) + return; + + error = synaptics_rmi4_read_packet_reg(rmi4_data, regs, reg->r_number); + if (error < 0) { + pr_err("F51 read failed\n"); + return; + } + + subpkt = find_subpkt(regs, 0, 0, ®, (void **)&f51_d0_0); + if (!subpkt || !subpkt->present) + return; + + presence_mask |= (1 << GUARD_BIT); + pr_debug("F%x@D%d: int status [0]=0x%x, [1]=0x%x\n", + regs->f_number & 0xff, reg->r_number, + f51_d0_0->data[0], f51_d0_0->data[1]); + tk_debug("F%x@D%d: int status [%s][%s][%s] md = %d\n", + regs->f_number & 0xff, reg->r_number, + f51_d0_0->noise_state ? "N" : "-", + f51_d0_0->gear_change ? "H" : "-", + f51_d0_0->guard_state ? "M" : "-", + f51_d0_0->md_present); + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F54 | DATA_TYPE); + if (!regs) + return; + + /* not all features might be enabled, thus determine presence */ + for (ii = 0; ii < regs->nr_regs; ii++) + presence_mask |= (1 << ii); + + error = synaptics_rmi4_read_packet_regs(rmi4_data, regs); + if (error < 0) + return; + + find_subpkt(regs, 6, 0, NULL, (void **)&f54_d6_0); + find_subpkt(regs, 10, 0, NULL, (void **)&f54_d10_0); + find_subpkt(regs, 14, 0, NULL, (void **)&f54_d14_0); + find_subpkt(regs, 16, 0, NULL, (void **)&f54_d16_0); + find_subpkt(regs, 17, 0, NULL, (void **)&f54_d17_0); + + if ((presence_mask & (1 << FREQ_BIT)) && f54_d17_0) { + statistics_log_time(gStat.dur, (int)f54_d17_0->freq, log); + statistics_log_time(gStat.hop, (int)f54_d17_0->freq, log); + } + + if ((presence_mask & (1 << NMS_BIT)) && f54_d10_0) + statistics_log_time(gStat.nms, (int)f54_d10_0->noise_state, log); + + if ((presence_mask & (1 << GUARD_BIT)) && f51_d0_0) + statistics_log_time(gStat.mpd, (int)f51_d0_0->md_present, log); + + tk_debug("F%x: %d:%d %5d:%5d:%5d\n", + regs->f_number & 0xff, + (presence_mask & (1 << FREQ_BIT)) && f54_d17_0 ? + (int)f54_d17_0->freq : -1, + (presence_mask & (1 << NMS_BIT)) && f54_d10_0 ? + (int)f54_d10_0->noise_state : -1, + (presence_mask & (1 << CID_IM_BIT)) && f54_d14_0 ? + (int)f54_d14_0->cid_im_lsb | + (f54_d14_0->cid_im_msb << 4) : -1, + (presence_mask & (1 << IM_BIT)) && f54_d6_0 ? + (int)f54_d6_0->interference_metric_lsb | + (f54_d6_0->interference_metric_msb << 4) : -1, + (presence_mask & (1 << FS_IM_BIT)) && f54_d16_0 ? + (int)f54_d16_0->freq_scan_im_lsb | + (f54_d16_0->freq_scan_im_msb << 4) : -1); +} + + /** + * synaptics_rmi4_report_touch() + * + * Called by synaptics_rmi4_sensor_report(). + * + * This function calls the appropriate finger data reporting function + * based on the function handler it receives and returns the number of + * fingers detected. + */ +static void synaptics_rmi4_report_touch(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + unsigned char *touch_count) +{ + unsigned char touch_count_2d; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Function %02x reporting\n", + __func__, fhandler->fn_number); + + switch (fhandler->fn_number) { + case SYNAPTICS_RMI4_F01: + synaptics_rmi4_f01_handler(rmi4_data, fhandler); + break; + + case SYNAPTICS_RMI4_F51: + synaptics_rmi4_f51_handler(rmi4_data, fhandler); + break; + + case SYNAPTICS_RMI4_F11: + touch_count_2d = synaptics_rmi4_f11_abs_report(rmi4_data, + fhandler); + + *touch_count += touch_count_2d; + + if (touch_count_2d) + rmi4_data->fingers_on_2d = true; + else + rmi4_data->fingers_on_2d = false; + break; + + case SYNAPTICS_RMI4_F12: + if (rmi4_data->suspend_is_wakeable) { + synaptics_rmi4_f12_wakeup_gesture(rmi4_data, fhandler); + break; + } + + touch_count_2d = synaptics_rmi4_f12_abs_report(rmi4_data, + fhandler); + + + *touch_count += touch_count_2d; + + if (touch_count_2d) + rmi4_data->fingers_on_2d = true; + else + rmi4_data->fingers_on_2d = false; + break; + + case SYNAPTICS_RMI4_F1A: + synaptics_rmi4_f1a_report(rmi4_data, fhandler); + break; + + default: + break; + } + + return; +} + + /** + * synaptics_rmi4_sensor_report() + * + * Called by synaptics_rmi4_irq(). + * + * This function determines the interrupt source(s) from the sensor + * and calls synaptics_rmi4_report_touch() with the appropriate + * function handler for each function with valid data inputs. + */ +static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char touch_count = 0; + unsigned int intr_status_mask; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_exp_fn *exp_fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + /* + * Get interrupt status information from F01 Data1 register to + * determine the source(s) that are flagging the interrupt. + */ + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + rmi4_data->touch_data, + rmi4_data->touch_data_size); + if (retval < 0) + return retval; + + /* + * Traverse the function handler list and service the source(s) + * of the interrupt accordingly. + */ + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + if (fhandler->intr_mask & + rmi4_data->touch_data[fhandler->intr_reg_num]) { + synaptics_rmi4_report_touch(rmi4_data, + fhandler, &touch_count); + } + } + } + + batohui(&intr_status_mask, rmi4_data->touch_data, + rmi4_data->num_of_intr_regs); + /* + * Go through external handlers list only when interrupt + * is not handled by currently active internal functions. + */ + if (intr_status_mask & (~rmi4_data->active_fn_intr_mask)) { + mutex_lock(&rmi4_data->exp_fn_ctrl.list_mutex); + if (!list_empty(&rmi4_data->exp_fn_ctrl.fn_list)) { + list_for_each_entry(exp_fhandler, + &rmi4_data->exp_fn_ctrl.fn_list, link) { + if (exp_fhandler->inserted && + (exp_fhandler->func_attn != NULL)) + exp_fhandler->func_attn( + rmi4_data, + rmi4_data->touch_data[0]); + } + } + mutex_unlock(&rmi4_data->exp_fn_ctrl.list_mutex); + } + + return touch_count; +} + + /** + * synaptics_rmi4_irq() + * + * Called by the kernel when an interrupt occurs (when the sensor + * asserts the attention irq). + * + * This function is the ISR thread and handles the acquisition + * and the reporting of finger data when the presence of fingers + * is detected. + */ +static irqreturn_t synaptics_rmi4_irq(int irq, void *data) +{ + struct synaptics_rmi4_data *rmi4_data = data; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: IRQ=%d; rmi4_data ptr = %p\n", __func__, irq, rmi4_data); + +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + if (rmi4_data->test_irq_delay_ms) { + int i; + for (i = 0; i < rmi4_data->test_irq_delay_ms; ++i) + udelay(1000); + } +#endif + synaptics_rmi4_sensor_report(rmi4_data); + + return IRQ_HANDLED; +} + + /** + * synaptics_rmi4_irq_enable() + * + * Called by synaptics_rmi4_probe() and the power management functions + * in this driver and also exported to other expansion Function modules + * such as rmi_dev. + * + * This function handles the enabling and disabling of the attention + * irq including the setting up of the ISR thread. + */ +static int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval = 0; + unsigned char intr_status; + struct synaptics_dsx_platform_data + *platform_data = &rmi4_data->board; + + if (enable) { + if (rmi4_data->irq_enabled) + return retval; + + /* Clear interrupts first */ + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + &intr_status, + rmi4_data->num_of_intr_regs); + if (retval < 0) + return retval; + + retval = request_threaded_irq(rmi4_data->irq, NULL, + synaptics_rmi4_irq, platform_data->irq_flags, + rmi4_data->irq_name, rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create irq thread\n", + __func__); + return retval; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: IRQ=%d: Started irq thread\n", __func__, rmi4_data->irq); + + rmi4_data->irq_enabled = true; + } else { + if (rmi4_data->irq_enabled) { + disable_irq(rmi4_data->irq); + free_irq(rmi4_data->irq, rmi4_data); + rmi4_data->irq_enabled = false; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Stopped irq thread\n", __func__); + } + } + + return retval; +} + + /** + * synaptics_rmi4_f11_init() + * + * Called by synaptics_rmi4_query_device(). + * + * This funtion parses information from the Function 11 registers + * and determines the number of fingers supported, x and y data ranges, + * offset to the associated interrupt status register, interrupt bit + * mask, and gathers finger data acquisition capabilities from the query + * registers. + */ +static int synaptics_rmi4_f11_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char ii; + unsigned char intr_offset; + unsigned char abs_data_size; + unsigned char abs_data_blk_size; + unsigned char query[F11_STD_QUERY_LEN]; + unsigned char control[F11_STD_CTRL_LEN]; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = (fd->intr_src_count & MASK_3BIT); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.query_base, + query, + sizeof(query)); + if (retval < 0) + return retval; + + /* Maximum number of fingers supported */ + if ((query[1] & MASK_3BIT) <= 4) + fhandler->num_of_data_points = (query[1] & MASK_3BIT) + 1; + else if ((query[1] & MASK_3BIT) == 5) + fhandler->num_of_data_points = 10; + + rmi4_data->num_of_fingers = fhandler->num_of_data_points; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.ctrl_base, + control, + sizeof(control)); + if (retval < 0) + return retval; + + /* Maximum x and y */ + rmi4_data->sensor_max_x = ((control[6] & MASK_8BIT) << 0) | + ((control[7] & MASK_4BIT) << 8); + rmi4_data->sensor_max_y = ((control[8] & MASK_8BIT) << 0) | + ((control[9] & MASK_4BIT) << 8); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Function %02x max x = %d max y = %d\n", + __func__, fhandler->fn_number, + rmi4_data->sensor_max_x, + rmi4_data->sensor_max_y); + + /* Reporting mode */ + if (!(control[0] & MASK_3BIT)) { + pr_warn("Reporting mode: continuous\n"); + } else { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: thresholds: x=0x%x, y=0x%x\n", + __func__, control[2], control[3]); + } + + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num >= MAX_INTR_REGISTERS) { + fhandler->intr_reg_num = 0; + fhandler->num_of_data_sources = 0; + fhandler->intr_mask = 0; + return -EINVAL; + } + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + + abs_data_size = query[5] & MASK_2BIT; + abs_data_blk_size = 3 + (2 * (abs_data_size == 0 ? 1 : 0)); + fhandler->size_of_data_register_block = abs_data_blk_size; + +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit); +#endif + set_bit(EV_ABS, rmi4_data->input_dev->evbit); + + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_X, 0, + rmi4_data->sensor_max_x, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_Y, 0, + rmi4_data->sensor_max_y, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_PRESSURE, 0, + 255, 0, 0); +#ifdef REPORT_2D_W + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, 0, + 15, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, 0, + 15, 0, 0); +#endif +#ifdef TYPE_B_PROTOCOL + input_mt_init_slots(rmi4_data->input_dev, + rmi4_data->num_of_fingers, 0); +#else + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TRACKING_ID, 0, + rmi4_data->num_of_fingers - 1, 0, 0); + input_set_events_per_packet(rmi4_data->input_dev, 64); +#endif + return retval; +} + +static int synaptics_rmi4_f1a_alloc_mem(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + struct synaptics_rmi4_f1a_handle *f1a; + + f1a = kzalloc(sizeof(*f1a), GFP_KERNEL); + if (!f1a) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for function handle\n", + __func__); + return -ENOMEM; + } + + fhandler->data = (void *)f1a; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.query_base, + f1a->button_query.data, + sizeof(f1a->button_query.data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read query registers\n", + __func__); + return retval; + } + + f1a->button_count = f1a->button_query.max_button_count + 1; + f1a->button_bitmask_size = (f1a->button_count + 7) / 8; + + f1a->button_data_buffer = kcalloc(f1a->button_bitmask_size, + sizeof(*(f1a->button_data_buffer)), GFP_KERNEL); + if (!f1a->button_data_buffer) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for data buffer\n", + __func__); + return -ENOMEM; + } + + f1a->button_map = kcalloc(f1a->button_count, + sizeof(*(f1a->button_map)), GFP_KERNEL); + if (!f1a->button_map) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for button map\n", + __func__); + return -ENOMEM; + } + + return 0; +} + +static int synaptics_rmi4_f51_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char ii; + unsigned char intr_offset; + struct synaptics_rmi4_func_packet_regs *regs; + unsigned char *data; + struct synaptics_rmi4_packet_reg *reg; + struct synaptics_rmi4_subpkt *subpkt; + struct { + unsigned char num_of_query_regs; + unsigned char num_of_data_regs; + unsigned char num_of_ctrl_regs; + } f51_query = {0}; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.query_base, + (unsigned char *)&f51_query, + sizeof(f51_query)); + pr_debug("F%x: # of query=%d, data=%d, ctrl=%d\n", + fd->fn_number, + f51_query.num_of_query_regs, + f51_query.num_of_data_regs, + f51_query.num_of_ctrl_regs); + /* check for error and mandatory registers presense */ + if (retval < 0 || f51_query.num_of_query_regs < 3) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Error querying F51\n", + __func__); + return -EIO; + } + /* check for mandatory registers presense */ + if (f51_query.num_of_data_regs < 2 || + f51_query.num_of_ctrl_regs < 10) { + return -ENODEV; + } + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = (fd->intr_src_count & MASK_3BIT); + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num >= MAX_INTR_REGISTERS) { + fhandler->intr_reg_num = 0; + fhandler->num_of_data_sources = 0; + fhandler->intr_mask = 0; + return -EINVAL; + } + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + /* FIXME: interrupt bit 5 (belongs F54) is used for some reason */ + fhandler->intr_mask = 0x40; + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F51 | DATA_TYPE); + if (!regs) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: F%x data not found\n", + __func__, fd->fn_number); + return 0; + } + + regs->base_addr = fhandler->full_addr.data_base; + reg = ®s->regs[0]; + subpkt = ®->subpkt[0]; + pr_debug("F%x@D%d size %u\n", + fd->fn_number, reg->r_number, subpkt->size); + data = kzalloc(subpkt->size, GFP_KERNEL); + if (data) { + reg->offset = reg->r_number; + reg->size = subpkt->size; + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + } + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F51); + if (!regs) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: F%x ctrl not found\n", + __func__, fd->fn_number); + return 0; + } + + regs->base_addr = fhandler->full_addr.ctrl_base; + for (ii = 0; ii < regs->nr_regs; ii++) { + reg = ®s->regs[ii]; + subpkt = ®->subpkt[0]; + pr_debug("F%x@C%d size %u\n", + fd->fn_number, reg->r_number, subpkt->size); + data = kzalloc(subpkt->size, GFP_KERNEL); + if (data) { + reg->offset = reg->r_number; + reg->size = subpkt->size; + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + } + } + + retval = synaptics_rmi4_read_packet_regs(rmi4_data, regs); + if (retval < 0) + dev_err(&rmi4_data->i2c_client->dev, "Error reading F51\n"); +#if 0 + else { + dev_info(&rmi4_data->i2c_client->dev, + "F%x ctrl[0]=%02x\n", fd->fn_number, f51_c0_0.data[0]); + dev_info(&rmi4_data->i2c_client->dev, + "F%x ctrl[4]=%02x\n", fd->fn_number, f51_c4_0.data[0]); + } +#endif + return 0; +} + + /** + * synaptics_rmi4_f12_init() + * + * Called by synaptics_rmi4_query_device(). + * + * This funtion parses information from the Function 12 registers + * and determines the number of fingers supported, x and y data ranges, + * offset to the associated interrupt status register, interrupt bit + * mask, and gathers finger data acquisition capabilities from the query + * registers. + */ +static int synaptics_rmi4_f12_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char ii; + unsigned char intr_offset; + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_subpkt *subpkt; + struct f12_d15_s0_type *f12_d15_0; + struct f12_c8_s0_type *f12_c8_0; + struct f12_c23_s1_type *f12_c23_1; + struct f12_c28_s0_type *f12_c28_0; + + regs = rmi4_data->f12_data_registers_ptr; + retval = synaptics_rmi4_scan_f12_reg_info(rmi4_data, + fhandler->full_addr.query_base + regs->query_offset, + fhandler->full_addr.data_base, regs); + if (retval < 0) + return retval; + + subpkt = find_subpkt(regs, 15, 0, NULL, (void **)&f12_d15_0); + if (subpkt && subpkt->present) + pr_debug("F12 has data register 15\n"); + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F12); + retval = synaptics_rmi4_scan_f12_reg_info(rmi4_data, + fhandler->full_addr.query_base + regs->query_offset, + fhandler->full_addr.ctrl_base, regs); + if (retval < 0) + return retval; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = (fd->intr_src_count & MASK_3BIT); + + retval = synaptics_rmi4_read_packet_regs(rmi4_data, regs); + if (retval < 0) + return retval; + + /* Maximum number of fingers supported */ + subpkt = find_subpkt(regs, 23, 1, NULL, (void**)&f12_c23_1); + if (subpkt && subpkt->present) { + fhandler->num_of_data_points = + f12_c23_1->max_num_reported_objects; + rmi4_data->num_of_fingers = f12_c23_1->max_num_reported_objects; + } else + return -ENOENT; + + subpkt = find_subpkt(regs, 8, 0, NULL, (void **)&f12_c8_0); + if (subpkt && subpkt->present) { + rmi4_data->sensor_max_x = + (f12_c8_0->max_x_msb << 8) | f12_c8_0->max_x_lsb; + rmi4_data->sensor_max_y = + (f12_c8_0->max_y_msb << 8) | f12_c8_0->max_y_lsb; + } else + return -ENOENT; + + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num >= MAX_INTR_REGISTERS) { + fhandler->intr_reg_num = 0; + fhandler->num_of_data_sources = 0; + fhandler->intr_mask = 0; + return -EINVAL; + } + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + + /* Data size per touch */ + subpkt = find_subpkt(regs, 28, 0, NULL, (void **)&f12_c28_0); + if (subpkt && subpkt->present) { + for (ii = 0; ii < 8; ii++) + if (f12_c28_0->reported_bytes_per_object & (1 << ii)) + fhandler->size_of_data_register_block++; + } else + return -ENOENT; + +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit); +#endif + set_bit(EV_ABS, rmi4_data->input_dev->evbit); + + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_X, 0, + rmi4_data->sensor_max_x, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_Y, 0, + rmi4_data->sensor_max_y, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_PRESSURE, 0, + 255, 0, 0); +#ifdef REPORT_2D_W + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, 0, + 255, 0, 0); +#endif +#ifdef TYPE_B_PROTOCOL + input_mt_init_slots(rmi4_data->input_dev, + rmi4_data->num_of_fingers, 0); +#else + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TRACKING_ID, 0, + rmi4_data->num_of_fingers - 1, 0, 0); + input_set_events_per_packet(rmi4_data->input_dev, 64); +#endif + return retval; +} + +static int synaptics_rmi4_cap_button_map( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + unsigned char ii; + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + struct synaptics_dsx_platform_data *pdata = &rmi4_data->board; + + if (!pdata->cap_button_map) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: cap_button_map is \ + NULL in board file\n", + __func__); + return -ENODEV; + } else if (!pdata->cap_button_map->map) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Button map is missing in board file\n", + __func__); + return -ENODEV; + } else { + if (pdata->cap_button_map->nbuttons != + f1a->button_count) { + f1a->valid_button_count = min(f1a->button_count, + pdata->cap_button_map->nbuttons); + } else { + f1a->valid_button_count = f1a->button_count; + } + + set_bit(EV_KEY, rmi4_data->input_dev->evbit); + + for (ii = 0; ii < f1a->valid_button_count; ii++) { + f1a->button_map[ii] = + pdata->cap_button_map->map[ii]; + set_bit(f1a->button_map[ii], + rmi4_data->input_dev->keybit); + input_set_capability(rmi4_data->input_dev, + EV_KEY, f1a->button_map[ii]); + } + } + + return 0; +} + +static void synaptics_rmi4_f1a_kfree(struct synaptics_rmi4_fn *fhandler) +{ + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + + if (f1a) { + kfree(f1a->button_data_buffer); + kfree(f1a->button_map); + kfree(f1a); + fhandler->data = NULL; + } + + return; +} + +static int synaptics_rmi4_f1a_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char ii; + unsigned short intr_offset; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = (fd->intr_src_count & MASK_3BIT); + + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num >= MAX_INTR_REGISTERS) { + fhandler->intr_reg_num = 0; + fhandler->num_of_data_sources = 0; + fhandler->intr_mask = 0; + retval = -EINVAL; + goto error_exit; + } + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + + retval = synaptics_rmi4_f1a_alloc_mem(rmi4_data, fhandler); + if (retval < 0) + goto error_exit; + + retval = synaptics_rmi4_cap_button_map(rmi4_data, fhandler); + if (retval < 0) + goto error_exit; + + rmi4_data->button_0d_enabled = 1; + + return 0; + +error_exit: + synaptics_rmi4_f1a_kfree(fhandler); + + return retval; +} + +static int synaptics_rmi4_alloc_fh(struct synaptics_rmi4_fn **fhandler, + struct synaptics_rmi4_fn_desc *rmi_fd, int page_number) +{ + *fhandler = kzalloc(sizeof(**fhandler), GFP_KERNEL); + if (!(*fhandler)) + return -ENOMEM; + + (*fhandler)->full_addr.data_base = + (rmi_fd->data_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.ctrl_base = + (rmi_fd->ctrl_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.cmd_base = + (rmi_fd->cmd_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.query_base = + (rmi_fd->query_base_addr | + (page_number << 8)); + + return 0; +} + +static unsigned char *simple_init_packet_reg( + struct synaptics_rmi4_packet_reg *reg, short r_offset) +{ + unsigned char *data_ptr = NULL; + if (reg->subpkt[0].data && reg->subpkt[0].size) { + reg->subpkt[0].present = true; + reg->subpkt[0].offset = 0; + memset(reg->subpkt[0].data, 0, reg->subpkt[0].size); + + reg->offset = r_offset; + kfree(reg->data); + data_ptr = kzalloc(reg->size, GFP_KERNEL); + } + return data_ptr; +} + +static void simple_deinit_packet_reg(struct synaptics_rmi4_packet_reg *reg) +{ + reg->expected = false; + reg->offset = -1; + reg->size = 0; + reg->subpkt[0].expected = false; + reg->subpkt[0].offset = -1; +} + +#define F01_RMI_Ctrl0 0 +#define F01_RMI_Ctrl2 2 +#define F01_RMI_Ctrl3 3 +#define F01_RMI_Ctrl5 5 +#define F01_RMI_Ctrl9 9 + +static void synaptics_rmi4_scan_f01_reg_info( + struct synaptics_rmi4_data *rmi4_data) +{ + int r, s, retval; + unsigned short query42_offset = F01_QUERY21_OFFSET; + unsigned short query43_size = 0; + unsigned short current_ctrl_offset = rmi4_data->num_of_intr_regs; + bool has_recalibration = false; + bool can_access_ctrl9 = false; + u8 query43[4]; + struct synaptics_rmi4_f01_query42 query42; + struct synaptics_rmi4_f01_query1 query1; + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_packet_reg *reg; + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F01); + if (unlikely(regs == NULL)) { + dev_info(&rmi4_data->i2c_client->dev, + "%s: F01 registers not defined\n", __func__); + return; + } + /* invalidate offsets only and keep statically defined register size */ + for (r = 0; r < regs->nr_regs; ++r) { + regs->regs[r].offset = -1; + kfree(regs->regs[r].data); + regs->regs[r].data = NULL; + for (s = 0; s < regs->regs[r].nr_subpkts; ++s) { + regs->regs[r].subpkt[s].present = 0; + if (regs->regs[r].subpkt[s].data && + regs->regs[r].subpkt[s].size) + memset(regs->regs[r].subpkt[s].data, 0, + regs->regs[r].subpkt[s].size); + } + } + + regs->base_addr = rmi4_data->f01_ctrl_base_addr; + pr_debug("F01_RMI_Ctrl base addr 0x%x\n", regs->base_addr); + + reg = find_packet_reg(regs, F01_RMI_Ctrl0); + if (unlikely(reg == NULL)) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: F01_RMI_Ctrl0 not defined\n", + __func__); + return; + } + + if (regs && reg && reg->nr_subpkts) { + reg->data = simple_init_packet_reg(reg, 0); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr + F01_QUERY1_OFFSET, + query1.data, + sizeof(query1.data)); + if (retval < 0) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: error reading F01_RMI_Query1\n", __func__); + return; + } + + if (query1.has_sensor_id) { + query42_offset++; /* has F01_RMI_Query22 */ + pr_debug("has F01_RMI_Query22\n"); + } + + if (query1.has_adjustable_doze) { + pr_debug("has F01_RMI_Ctrl2 and F01_RMI_Ctrl3\n"); + current_ctrl_offset++; + reg = find_packet_reg(regs, F01_RMI_Ctrl2); + if (reg) { + reg->data = simple_init_packet_reg(reg, + current_ctrl_offset); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } + current_ctrl_offset++; + reg = find_packet_reg(regs, F01_RMI_Ctrl3); + if (reg) { + reg->data = simple_init_packet_reg(reg, + current_ctrl_offset); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } + } + + if (query1.reserved) { + current_ctrl_offset++; /* has F01_RMI_Ctrl4 */ + query42_offset++; /* has F01_RMI_Query21 */ + query42_offset += 18; /* has F01_RMI_Query23-41 */ + pr_debug("has F01_RMI_Query21,23-41 and F01_RMI_Ctrl4\n"); + } + + if (query1.has_doze_holdoff) { + pr_debug("has F01_RMI_Ctrl5\n"); + current_ctrl_offset++; + reg = find_packet_reg(regs, F01_RMI_Ctrl5); + if (reg) { + reg->data = simple_init_packet_reg(reg, + current_ctrl_offset); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } + } + + query42.data[0] = 0; + if (query1.has_query42) { + pr_debug("F01_RMI_Query42 offset %d\n", query42_offset); + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr + + query42_offset, + query42.data, + sizeof(query42.data)); + if (retval < 0) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: error reading F01_RMI_Query42\n", + __func__); + } else + has_recalibration = query42.has_recalibration; + } + + if (query42.has_ds4_queries) { + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr + + query42_offset + 1, + query43, + sizeof(query43)); + if (retval < 0) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: error reading F01_RMI_Query43\n", + __func__); + } else { + can_access_ctrl9 = true; + query43_size = query43[0] & 0xf; + pr_debug("F01_RMI_Query43 size: %d\n", query43_size); + } + } + + if (query43_size) { + if (query43[2] & 0x1) { + current_ctrl_offset++; /* has F01_RMI_Ctrl6 */ + pr_debug("has F01_RMI_Ctrl6\n"); + } + if (query43[2] & 0x2) { + current_ctrl_offset++; /* has F01_RMI_Ctrl7 */ + pr_debug("has F01_RMI_Ctrl7\n"); + } + if (query43[2] & 0x4) { + current_ctrl_offset++; /* has F01_RMI_Ctrl8 */ + pr_debug("has F01_RMI_Ctrl8\n"); + } + } + + reg = find_packet_reg(regs, F01_RMI_Ctrl9); + if (unlikely(reg == NULL)) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: F01_RMI_Ctrl9 not present\n", __func__); + return; + } + + if (regs && reg) { + if (has_recalibration == false || can_access_ctrl9 == false) { + simple_deinit_packet_reg(reg); + dev_warn(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + return; + } + + current_ctrl_offset++; + pr_debug("F01_RMI_Ctrl9 offset %d\n", current_ctrl_offset); + + reg->data = simple_init_packet_reg(reg, current_ctrl_offset); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } +} + +struct synaptics_rmi4_fn *get_fhandler(struct synaptics_rmi4_data *rmi4_data, + unsigned char fn_number) +{ + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi = + &(rmi4_data->rmi4_mod_info); + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == fn_number) + return fhandler; + } + return NULL; +} + /** + * synaptics_rmi4_query_device() + * + * Called by synaptics_rmi4_probe(). + * + * This funtion scans the page description table, records the offsets + * to the register types of Function $01, sets up the function handlers + * for Function $11 and Function $12, determines the number of interrupt + * sources from the sensor, adds valid Functions with data inputs to the + * Function linked list, parses information from the query registers of + * Function $01, and enables the interrupt sources from the valid Functions + * with data inputs. + */ +static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char page_number; + unsigned char intr_count = 0; + unsigned char f01_query[F01_STD_QUERY_LEN] = {0}; + unsigned short pdt_entry_addr; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + struct f34_properties f34_query; + struct f34_properties_v2 f34_query_v2; + struct synaptics_rmi4_f01_device_status status; + int finger_data_addr; + int intr_status_addr; + int touch_data_size; + struct synaptics_rmi4_fn *f12_handler; + + rmi = &(rmi4_data->rmi4_mod_info); + + INIT_LIST_HEAD(&rmi->support_fn_list); + + /* Scan the page description tables of the pages to service */ + for (page_number = 0; page_number < PAGES_TO_SERVICE; page_number++) { + for (pdt_entry_addr = PDT_START; pdt_entry_addr > PDT_END; + pdt_entry_addr -= PDT_ENTRY_SIZE) { + pdt_entry_addr |= (page_number << 8); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + pdt_entry_addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + fhandler = NULL; + + if (rmi_fd.fn_number == 0) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Reached end of PDT\n", + __func__); + break; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: F%02x v%d found (page %d)\n", + __func__, rmi_fd.fn_number, rmi_fd.fn_version, + page_number); + + switch (rmi_fd.fn_number) { + case SYNAPTICS_RMI4_F51: + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc for F%x\n", + __func__, rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f51_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) { + /* not critical, just continue */ + kfree(fhandler); + fhandler = NULL; + dev_err(&rmi4_data->i2c_client->dev, + "%s: No handler for F%x\n", + __func__, rmi_fd.fn_number); + } + break; + + case SYNAPTICS_RMI4_F34: + if (rmi_fd.fn_version < 2) { + retval = synaptics_rmi4_i2c_read( + rmi4_data, + rmi_fd.query_base_addr + + F34_PROPERTIES_OFFSET, + &f34_query.data[0], + sizeof(f34_query)); + if (retval < 0) + return retval; + + if (f34_query.has_config_id) { + retval = synaptics_rmi4_i2c_read( + rmi4_data, + rmi_fd.ctrl_base_addr, + rmi->config_id, + sizeof(rmi->config_id)); + if (retval < 0) + return retval; + } + } else if (rmi_fd.fn_version == 2) { + retval = synaptics_rmi4_i2c_read( + rmi4_data, + rmi_fd.query_base_addr + + F34_PROPERTIES_OFFSET_V2, + &f34_query_v2.data[0], + sizeof(f34_query_v2)); + if (retval < 0) + return retval; + + if (f34_query_v2.has_config_id) { + retval = synaptics_rmi4_i2c_read( + rmi4_data, + rmi_fd.ctrl_base_addr, + rmi->config_id, + sizeof(rmi->config_id)); + if (retval < 0) + return retval; + } + } + break; + + case SYNAPTICS_RMI4_F01: + rmi4_data->f01_query_base_addr = + rmi_fd.query_base_addr; + rmi4_data->f01_ctrl_base_addr = + rmi_fd.ctrl_base_addr; + rmi4_data->f01_data_base_addr = + rmi_fd.data_base_addr; + rmi4_data->f01_cmd_base_addr = + rmi_fd.cmd_base_addr; + pr_debug("query_base_addr 0x%04x\n", + rmi4_data->f01_query_base_addr); + pr_debug("ctrl_base_addr 0x%04x\n", + rmi4_data->f01_ctrl_base_addr); + pr_debug("data_base_addr 0x%04x\n", + rmi4_data->f01_data_base_addr); + pr_debug("cmd_base_addr 0x%04x\n", + rmi4_data->f01_cmd_base_addr); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr, + status.data, + sizeof(status.data)); + if (retval < 0) + return retval; + + /* FlashProg bit might be inaccurate, so */ + /* do not change the flag if it is set */ + if (!rmi4_data->in_bootloader) + rmi4_data->in_bootloader = + status.flash_prog == 1; + break; + + case SYNAPTICS_RMI4_F11: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc for F%x\n", + __func__, rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f11_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to init F11\n", + __func__); + return retval; + } + break; + + case SYNAPTICS_RMI4_F12: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc for F%x\n", + __func__, rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f12_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to init F12\n", + __func__); + return retval; + } + break; + + case SYNAPTICS_RMI4_F1A: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc for F%x\n", + __func__, rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f1a_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + switch (retval) { + case -ENOMEM: + return retval; + case -ENODEV: + /* removing buttons handler if there */ + /* were no key codes in platform data */ + kfree(fhandler); + fhandler = NULL; + rmi_fd.intr_src_count = 0; + break; + } + break; + } + + /* Accumulate the interrupt count */ + intr_count += (rmi_fd.intr_src_count & MASK_3BIT); + + if (fhandler && rmi_fd.intr_src_count) { + pr_debug("adding handler for F%x\n", + fhandler->fn_number); + list_add_tail(&fhandler->link, + &rmi->support_fn_list); + } + } + } + + rmi4_data->num_of_intr_regs = (intr_count + 7) / 8; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Number of interrupt registers = %d\n", + __func__, rmi4_data->num_of_intr_regs); + if (rmi4_data->num_of_intr_regs >= MAX_INTR_REGISTERS) + return -EINVAL; + + f12_handler = get_fhandler(rmi4_data, SYNAPTICS_RMI4_F12); + if (!f12_handler) { + rmi4_data->touch_data_contiguous = 0; + goto skip_f12_single_i2c_setup; + } + + finger_data_addr = f12_handler->full_addr.data_base + + rmi4_data->f12_data_registers_ptr->regs[F12_D1_IDX].offset; + intr_status_addr = rmi4_data->f01_data_base_addr + 1; + + /* is touch data contiguous? */ + if (finger_data_addr != + intr_status_addr + rmi4_data->num_of_intr_regs) { + rmi4_data->touch_data_contiguous = 0; + goto skip_f12_single_i2c_setup; + } + + touch_data_size = rmi4_data->num_of_intr_regs + + rmi4_data->num_of_fingers * f12_handler->size_of_data_register_block; + + /* size of contiguous block changed or not allocated? */ + if (touch_data_size != rmi4_data->touch_data_size || + !rmi4_data->touch_data) { + if (rmi4_data->touch_data) + kfree(rmi4_data->touch_data); + + rmi4_data->touch_data_size = touch_data_size; + rmi4_data->touch_data = kzalloc(rmi4_data->touch_data_size, + GFP_KERNEL); + if (rmi4_data->touch_data) + rmi4_data->touch_data_contiguous = 1; + else { + /* fall back to non-contigous handling */ + rmi4_data->touch_data_contiguous = 0; + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for touch_data\n", + __func__); + } + } + +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + /* Test mode can only be enforced in userdebug build */ + /* When test mode enforced, clear contiguous flag to */ + /* do two I2C transactions to retrieve touch data */ + if (!rmi4_data->test_irq_data_contig) { + rmi4_data->touch_data_contiguous = 0; + pr_notice("Forcing non-contiguous data flag\n"); + } +#endif + +skip_f12_single_i2c_setup: + if (!rmi4_data->touch_data_contiguous) { + rmi4_data->touch_data_size = rmi4_data->num_of_intr_regs; + pr_notice("touch interrupt status and finger data not contiguous\n"); + } + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr, + f01_query, + sizeof(f01_query)); + if (retval < 0) + return retval; + + /* RMI Version 4.0 currently supported */ + rmi->version_major = 4; + rmi->version_minor = 0; + + rmi->manufacturer_id = f01_query[0]; + rmi->product_props = f01_query[1]; + memcpy(&rmi->product_info[0], &f01_query[2], + SYNAPTICS_RMI4_PRODUCT_INFO_SIZE); + memcpy(rmi->serial, &f01_query[4], SYNAPTICS_RMI4_SERIAL_SIZE); + memcpy(rmi->product_id_string, &f01_query[11], + SYNAPTICS_RMI4_PRODUCT_ID_SIZE); + rmi->product_id_string[SYNAPTICS_RMI4_PRODUCT_ID_SIZE] = 0; + + synaptics_dsx_validate_product_string(rmi->product_id_string); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr+PACKAGE_ID_OFFSET, + rmi->package_id, + sizeof(rmi->package_id)); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr+FW_VERSION_OFFSET, + rmi->build_id, + sizeof(rmi->build_id)); + if (retval < 0) + return retval; + + if (rmi->manufacturer_id != 1) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Non-Synaptics device found," + "manufacturer ID = %d\n", + __func__, rmi->manufacturer_id); + } + + if (!rmi4_data->in_bootloader && !rmi4_data->input_registered) { + retval = input_register_device(rmi4_data->input_dev); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to register input device\n", + __func__); + return retval; + } + rmi4_data->input_registered = true; + } + + memset(rmi4_data->intr_mask, 0x00, sizeof(rmi4_data->intr_mask)); + + /* + * Map out the interrupt bit masks for the interrupt sources + * from the registered function handlers. + */ + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + rmi4_data->intr_mask[fhandler->intr_reg_num] |= + fhandler->intr_mask; + } + } + + /* interrupt mask for currently active functions */ + batohui(&rmi4_data->active_fn_intr_mask, + rmi4_data->intr_mask, rmi4_data->num_of_intr_regs); + pr_debug("active func intr_mask 0x%x\n", + rmi4_data->active_fn_intr_mask); + + if (rmi4_data->in_bootloader) + pr_info("Product: %s is in bootloader mode\n", + rmi->product_id_string); + else { + unsigned int config_id, firmware_id; + + batohui(&firmware_id, rmi->build_id, sizeof(rmi->build_id)); + batohui(&config_id, rmi->config_id, sizeof(config_id)); + pr_info("Product: %s, firmware id: %x, config id: %08x\n", + rmi->product_id_string, firmware_id, config_id); + /* has to be called after determining */ + /* the number of interrupt registers */ + synaptics_rmi4_scan_f01_reg_info(rmi4_data); + } + + return 0; +} + +static void synaptics_rmi4_cleanup(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_fn *fhandler, *next_list_entry; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + list_for_each_entry_safe(fhandler, next_list_entry, + &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) + synaptics_rmi4_f1a_kfree(fhandler); + else + kfree(fhandler->data); + list_del(&fhandler->link); + kfree(fhandler); + } +} + +static void synaptics_dsx_release_all(struct synaptics_rmi4_data *rmi4_data) +{ + /* reset some TSB global vars like fingers_on_2d after resume + * of reset touch IC + */ + if (rmi4_data->button_0d_enabled) { + rmi4_data->tsb_buff_clean_flag = 1; + rmi4_data->fingers_on_2d = false; + } + + if (rmi4_data->input_dev) { + /* + * Enforce touch release event report to work-around + * such event missing while touch IC is off. + */ +#ifdef TYPE_B_PROTOCOL + int i; + for (i = 0; i < rmi4_data->num_of_fingers; i++) { + INPUT_REPORT_MT_SLOT(rmi4_data, i); + INPUT_REPORT_MT_SLOT_STATE(rmi4_data, + MT_TOOL_FINGER, false); + } +#else + input_mt_sync(rmi4_data->input_dev); +#endif + input_sync(rmi4_data->input_dev); + } +} + +static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data) +{ + int current_state, retval; + bool need_to_query = false; + + current_state = synaptics_dsx_get_state_safe(rmi4_data); + if (current_state == STATE_UNKNOWN) { + synaptics_rmi4_cleanup(rmi4_data); + need_to_query = true; + } + + retval = synaptics_dsx_ic_reset(rmi4_data, RMI4_HW_RESET); + if (retval > 0) + pr_debug("successful reset took %dms\n", retval); + else + dev_warn(&rmi4_data->i2c_client->dev, "%s: timed out waiting for idle\n", + __func__); + + if (need_to_query) { + if (!rmi4_data->input_dev) { + retval = synaptics_dsx_alloc_input(rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to allocate input device\n", + __func__); + return retval; + } + } + + retval = synaptics_rmi4_query_device(rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to query device\n", + __func__); + return retval; + } + + if (control_access_block_get()) { + control_access_block_update_static(rmi4_data); + control_access_block_update_dynamic(rmi4_data); + } + + /* kick off detection work after touch ic changes its mode */ + if (det_workqueue) + queue_delayed_work(det_workqueue, + &rmi4_data->exp_fn_ctrl.det_work, 0); + } + + return 0; +} + +static int control_access_block_update_static( + struct synaptics_rmi4_data *rmi4_data) +{ + unsigned char one_data = 1; + unsigned char zero_data = 0; + unsigned char touch_data_size = 0; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + struct touch_control_access_block *cab = control_access_block_get(); + + if (!cab) + return -ENODEV; + + rmi = &(rmi4_data->rmi4_mod_info); + + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F11) { + touch_data_size = fhandler->size_of_data_register_block; + } else if (fhandler->fn_number == SYNAPTICS_RMI4_F12) { + touch_data_size = fhandler->size_of_data_register_block; + } + } + + control_access_block_zap(SYN_DSX_WAKE); + control_access_block_update_wake(rmi4_data->f01_ctrl_base_addr, + 1, &zero_data); + + control_access_block_zap(SYN_DSX_SLEEP); + control_access_block_update_sleep(rmi4_data->f01_ctrl_base_addr, + 1, &one_data); + + control_access_block_zap(SYN_DSX_RESET); + control_access_block_update_reset(rmi4_data->f01_cmd_base_addr, + 1, &one_data); + + control_access_block_zap(SYN_DSX_STATUS); + control_access_block_update_status(rmi4_data->f01_data_base_addr + 1, + rmi4_data->num_of_intr_regs); + + control_access_block_zap(SYN_DSX_MODE); + control_access_block_update_mode(rmi4_data->f01_data_base_addr, 1); + + if (touch_data_size) { + control_access_block_zap(SYN_DSX_DATA); + control_access_block_update_data_mt( + rmi4_data->f01_data_base_addr + + rmi4_data->num_of_intr_regs + 1, + touch_data_size, + rmi4_data->aod_mt); + } + + cab->do_sync = true; + pr_debug("updated control access block\n"); + return 0; +} + +static int control_access_block_update_dynamic( + struct synaptics_rmi4_data *rmi4_data) +{ + int i; + struct touch_control_access_block *cab = control_access_block_get(); + struct synaptics_dsx_patch *patch = NULL; + + if (!rmi4_data->patching_enabled || !cab || !patch || !patch->cfg_num) { + pr_debug("nothing to add to control access block\n"); + return 0; + } + + control_access_block_zap(SYN_DSX_CONFIG); + + for (i = 0; i < MAX_CONFIG_REGS && SLOT_IN_USE; i++) { + int f_number; + struct synaptics_dsx_func_patch *fp; + struct synaptics_rmi4_func_packet_regs *regs; + + /* skip query registers */ + if (rmi4_data->config_regs[i].f_number & QUERY_TYPE) + continue; + f_number = rmi4_data->config_regs[i].f_number; + regs = find_function(rmi4_data, f_number); + + list_for_each_entry(fp, &patch->cfg_head, link) { + struct synaptics_rmi4_subpkt *subpkt; + struct synaptics_rmi4_packet_reg *reg; + + if (fp->func != f_number) + continue; + reg = find_packet_reg(regs, fp->regstr); + if (!reg || reg->offset < 0) + continue; + if (fp->subpkt >= reg->nr_subpkts) + continue; + subpkt = reg->subpkt + fp->subpkt; + + /* no need to check for misalignment here, since */ + /* synaptics_copy_multiple_subpkts() takes care of it */ + if (!subpkt->present || !subpkt->data) + continue; + + /* exclude power control from patch set */ + /* TODO: with modifiers introduced, this */ + /* check might not be necessary anymore */ + if (fp->func == SYNAPTICS_RMI4_F01 && + fp->regstr == 0 && fp->subpkt == 0) + continue; + + control_access_block_update_wo(SYN_DSX_CONFIG, + rmi4_data->config_regs[i].base_addr + reg->offset + + subpkt->offset, + fp->size, fp->bitmask, fp->data); + } + } + + cab->do_sync = true; + pr_debug("filled in control access block\n"); + return 0; +} + +/** +* synaptics_rmi4_detection_work() +* +* Called by the kernel at the scheduled time. +* +* This function is armed by synaptics_new_function call. It checks for +* the insertion and removal of other expansion Function modules such as +* rmi_dev and calls their initialization and removal callback functions +* accordingly. +*/ +static void synaptics_rmi4_detection_work(struct work_struct *work) +{ + struct delayed_work *dw = + container_of(work, struct delayed_work, work); + struct synaptics_exp_fn_ctrl *exp_fn_ctrl = + container_of(dw, struct synaptics_exp_fn_ctrl, det_work); + struct synaptics_rmi4_data *rmi4_data = + container_of(exp_fn_ctrl, struct synaptics_rmi4_data, exp_fn_ctrl); + struct synaptics_rmi4_exp_fn *exp_fhandler, *next_list_entry; + struct device *dev = &rmi4_data->i2c_client->dev; + int state, error, probe_status; + unsigned int scheduled_delay = 0; + bool f34_inserted = false; + bool terminate = false; + bool panel_ready = true; + char *pname = NULL; + +#if defined(CONFIG_DRM) + if (rmi4_data->splash_screen_mode) { + /* DRM panel status */ + panel_ready = dsi_display_is_panel_enable(rmi4_data->ctrl_dsi, + &probe_status, &pname); + dev_dbg(dev, "%s: drm: probe=%d, enable=%d, panel'%s'\n", + __func__, probe_status, panel_ready, pname); + /* check if primary panel is not a dummy one */ + if (pname && strstr(pname, "dummy")) { + dev_info(dev, "%s: dummy panel detected\n", __func__); + rmi4_data->drm_state = DRM_ST_TERM; + } else if (panel_ready) { + if (rmi4_data->drm_state != DRM_ST_TERM) + rmi4_data->drm_state = DRM_ST_READY; + dev_dbg(dev, "%s: panel ready; drm_st=%d\n", __func__, rmi4_data->drm_state); + } + } +#endif + /* pname only gets initialized by DRM */ + /* it implies RMI_DRM_FRAMEWORK handler running */ + if (pname) { + if (ASSERT(DRM_ST_UNDEF)) + goto exit_reschedule; + + if (ASSERT(DRM_ST_TERM)) { + /* hw init failed on previous step, thus + * continue shutting things down */ + terminate = true; + dev_dbg(dev, "%s: set terminate flag\n", __func__); + } + } else { + if (rmi4_data->fps_detection_enabled && + !rmi4_data->is_fps_registered) { + error = FPS_register_notifier( + &rmi4_data->fps_notif, 0xBEEF, false); + if (error) { + queue_delayed_work(det_workqueue, + &exp_fn_ctrl->det_work, + msecs_to_jiffies(EXP_FN_DET_INTERVAL)); + dev_err(dev, "%s: Failed to register fps_notifier\n", __func__); + } else { + rmi4_data->is_fps_registered = true; + dev_dbg(dev, "%s: registered FPS notifier\n", __func__); + } + } + } + + mutex_lock(&exp_fn_ctrl->list_mutex); + if (list_empty(&exp_fn_ctrl->fn_list)) + goto release_mutex; + + /* F34 resets touch IC and might interfere with */ + /* reporting touch events. Need to ensure it was */ + /* successfully inserted before enabling interrupts */ + list_for_each_entry(exp_fhandler, + &exp_fn_ctrl->fn_list, + link) { + if (exp_fhandler->fn_type == RMI_FW_UPDATER && + exp_fhandler->inserted) { + f34_inserted = true; + dev_dbg(dev, "%s: F34 handler inserted\n", __func__); + break; + } + } + + list_for_each_entry_safe(exp_fhandler, + next_list_entry, + &exp_fn_ctrl->fn_list, + link) { + /* terminate all handlers */ + if (terminate) { + dev_dbg(dev, "%s: terminating the rest of handler: %d\n", + __func__, exp_fhandler->fn_type); + /* DRM handler still needs to run for proper cleanup */ + if (exp_fhandler->fn_type != RMI_DRM_FRAMEWORK) + exp_fhandler->func_init = NULL; + } + + if (exp_fhandler->inserted == true) { + if (exp_fhandler->func_init) { + dev_dbg(dev, "%s: handler %d already inserted\n", + __func__, exp_fhandler->fn_type); + continue; + } + + dev_dbg(dev, "%s: calling remove func for handler: %d\n", + __func__, exp_fhandler->fn_type); + exp_fhandler->func_remove(rmi4_data); + + /* need to restore status function on F34 removal */ + if (exp_fhandler->fn_type == RMI_FW_UPDATER && + exp_fhandler->func_status == rmi4_data->get_status) { + rmi4_data->get_status = synaptics_rmi4_f01_flashprog_status; + dev_info(dev, + "%s: using default status retrieval function\n", + __func__); + } + + if (exp_fhandler->fn_type == RMI_DRM_FRAMEWORK) { + if (terminate) { + cancel_delayed_work_sync(&rmi4_data->exp_fn_ctrl.det_work); + flush_workqueue(det_workqueue); + + i2c_set_clientdata(rmi4_data->i2c_client, NULL); + kfree(rmi4_data); + + dev_info(dev, "%s: driver removed\n", __func__); + } else if (f34_inserted) { + synaptics_rmi4_resume(&(rmi4_data->i2c_client->dev)); + dev_info(dev, "%s: drm panel ready\n", __func__); + dev_err(dev, "%s: touch_stopped=%d, flash_enabled=%d\n", + __func__, + atomic_read(&rmi4_data->touch_stopped), + rmi4_data->flash_enabled); + } else { + queue_delayed_work(det_workqueue, + &exp_fn_ctrl->det_work, + msecs_to_jiffies(EXP_FN_DET_INTERVAL)); + dev_dbg(dev, "%s: keep drm fhandler\n", __func__); + continue; + } + } + + list_del(&exp_fhandler->link); + kfree(exp_fhandler); + continue; + } + + /* postpone all handlers, except for RMI_DRM_FRAMEWORK */ + /* until RMI_FW_UPDATER processed */ + if (!f34_inserted && + !(exp_fhandler->fn_type == RMI_FW_UPDATER || + exp_fhandler->fn_type == RMI_DRM_FRAMEWORK)) { + dev_dbg(dev, "%s: hold handler %d until f34 inserted\n", + __func__, exp_fhandler->fn_type); + continue; + } + + state = synaptics_dsx_get_state_safe(rmi4_data); + dev_dbg(dev, "%s: current state: %s, in_bootloader: %s\n", + __func__, synaptics_dsx_state_name(state), + rmi4_data->in_bootloader ? "true" : "false"); + + if (exp_fhandler->mode == IC_MODE_UI) { + if (rmi4_data->in_bootloader) { + dev_dbg(dev, "%s: handler %d requires UI mode\n", + __func__, exp_fhandler->fn_type); + continue; + } + /* UI mode requires IC powered on */ + if (state == STATE_SUSPEND) { + dev_dbg(dev, "%s: handler %d cannot operate while suspended\n", + __func__, exp_fhandler->fn_type); + scheduled_delay = 500; + continue; + } + /* postpone suspend for loading RMI_F54 and */ + if (exp_fhandler->fn_type == RMI_F54) + synaptics_dsx_sensor_state(rmi4_data, STATE_LOADING); + } + + exp_fhandler->func_init(rmi4_data); + exp_fhandler->inserted = true; + dev_dbg(dev, "%s: handler %d inserted\n", + __func__, exp_fhandler->fn_type); + + if (exp_fhandler->fn_type == RMI_F54 && rmi4_data->f54_data) { + int scan_failures = 0; + struct synaptics_rmi4_func_packet_regs *regs; + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F54); + if (!regs) + goto restore_state; + error = rmi4_data->scan_f54_ctrl_regs(rmi4_data, regs); + if (error) { + regs->nr_regs = 0; + dev_err(dev, "%s: F54_Ctrl scan failed\n", __func__); + } + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F54 | COMMAND_TYPE); + if (!regs) + goto restore_state; + error = rmi4_data->scan_f54_cmd_regs(rmi4_data, regs); + if (error) { + regs->nr_regs = 0; + dev_err(dev, "%s: F54_Cmd scan failed\n", __func__); + } + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F54 | DATA_TYPE); + if (!regs) + goto restore_state; + error = rmi4_data->scan_f54_data_regs(rmi4_data, regs); + if (error) { + regs->nr_regs = 0; + dev_err(dev, "%s: F54_Data scan failed\n", __func__); + scan_failures++; + } + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F54 | QUERY_TYPE); + if (!regs) + goto restore_state; + error = rmi4_data->scan_f54_query_regs(rmi4_data, regs); + if (error) { + regs->nr_regs = 0; + dev_err(dev, "%s: F54_Query scan failed\n", __func__); + scan_failures++; + } + + if (!scan_failures) + statistics_init(rmi4_data); + } + + if (exp_fhandler->fn_type == RMI_CTRL_ACCESS_BLK) { + error = control_access_block_update_static(rmi4_data); + /* FIXME: what if feature is disabled? */ + /* invalidate insertion */ + if (error) { + exp_fhandler->inserted = false; + dev_err(dev, "%s: postpone CAB init\n", __func__); + } else + control_access_block_update_dynamic(rmi4_data); + } + + if (exp_fhandler->fn_type == RMI_FW_UPDATER && exp_fhandler->func_status) { + int status; + + status = exp_fhandler->func_status(rmi4_data); + /* consider changing to */ + /* if (status == 1) */ + /* if Synaptics confirm only 1 is valid */ + if (status) { + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + rmi4_data->in_bootloader = true; + synaptics_dsx_sensor_state(rmi4_data, STATE_BL); + + state = synaptics_dsx_get_state_safe(rmi4_data); + dev_dbg(dev, "%s: current state %s\n", + __func__, synaptics_dsx_state_name(state)); + dev_info(dev, "%s: Product: %s is in bootloader mode\n", + __func__, rmi->product_id_string); + } + /* replace default status retrieval function */ + rmi4_data->get_status = exp_fhandler->func_status; + dev_info(dev, "%s: using F34 status retrieval function\n", __func__); + /* allowing sufficient time for possible upgrade to start */ + scheduled_delay = 3000; + } + + if (exp_fhandler->fn_type == RMI_DRM_FRAMEWORK) { + /* allowing sufficient time for F34 to complete */ + scheduled_delay = 3000; + error = synaptics_rmi4_hw_init(rmi4_data); + exp_fhandler->func_init = NULL; + dev_dbg(dev, "%s: removing DRM\n", __func__); + if (error) { + dev_err(dev, "%s: hw init failed!!!\n", __func__); + /* not dummy panel found, but touch hw init failed, */ + /* thus we need to terminate further processing */ + rmi4_data->drm_state = DRM_ST_TERM; + scheduled_delay = 20; /* no reason to wait anymore */ + break; + } + } + +restore_state: + if (STATE_LOADING == synaptics_dsx_get_state_safe(rmi4_data)) + synaptics_dsx_sensor_state(rmi4_data, state); + } + + if (scheduled_delay) { + queue_delayed_work(det_workqueue, &exp_fn_ctrl->det_work, + msecs_to_jiffies(scheduled_delay)); + dev_dbg(dev, "%s: re-scheduled %d\n", __func__, scheduled_delay); + } + +release_mutex: + mutex_unlock(&exp_fn_ctrl->list_mutex); + + return; + +exit_reschedule: + dev_dbg(dev, "%s: re-scheduling...\n", __func__); + queue_delayed_work(det_workqueue, + &exp_fn_ctrl->det_work, + msecs_to_jiffies(EXP_FN_DET_INTERVAL)); +} + +static int synaptics_exp_fn_ctrl_init( + struct synaptics_rmi4_data *rmi4_data) +{ + struct device *dev = &rmi4_data->i2c_client->dev; + + mutex_init(&rmi4_data->exp_fn_ctrl.ctrl_mutex); + mutex_init(&rmi4_data->exp_fn_ctrl.list_mutex); + INIT_LIST_HEAD(&rmi4_data->exp_fn_ctrl.fn_list); + if (!det_workqueue) { + det_workqueue = create_singlethread_workqueue("rmi_det_workqueue"); + if (!det_workqueue) { + dev_err(dev, "%s: unable to create a workqueue\n", __func__); + return -ENODEV; + } + } + + INIT_DELAYED_WORK(&rmi4_data->exp_fn_ctrl.det_work, synaptics_rmi4_detection_work); + rmi4_data->exp_fn_ctrl.inited = true; + + return 0; +} + +/** +* synaptics_rmi4_new_function() +* +* Called by other expansion Function modules in their module init and +* module exit functions. +* +* This function is used by other expansion Function modules such as +* rmi_dev to register themselves with the driver by providing their +* initialization and removal callback function pointers so that they +* can be inserted or removed dynamically at module init and exit times, +* respectively. +*/ +void synaptics_rmi4_new_function( + struct synaptics_rmi4_data *rmi4_data, + enum exp_fn fn_type, bool insert, + int (*func_init)(struct synaptics_rmi4_data *rmi4_data), + void (*func_remove)(struct synaptics_rmi4_data *rmi4_data), + void (*func_attn)(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask), + int (*func_status)(struct synaptics_rmi4_data *rmi4_data), + enum ic_modes mode) +{ + struct synaptics_rmi4_exp_fn *exp_fhandler; + + mutex_lock(&rmi4_data->exp_fn_ctrl.list_mutex); + if (insert) { + exp_fhandler = kzalloc(sizeof(*exp_fhandler), GFP_KERNEL); + if (!exp_fhandler) { + pr_err("failed to alloc mem for expansion function\n"); + goto exit; + } + exp_fhandler->fn_type = fn_type; + exp_fhandler->func_init = func_init; + exp_fhandler->func_attn = func_attn; + exp_fhandler->func_status = func_status; + exp_fhandler->func_remove = func_remove; + exp_fhandler->inserted = false; + exp_fhandler->mode = mode; + list_add_tail(&exp_fhandler->link, &rmi4_data->exp_fn_ctrl.fn_list); + } else { + list_for_each_entry(exp_fhandler, &rmi4_data->exp_fn_ctrl.fn_list, link) { + if (exp_fhandler->func_init != func_init) + continue; + /* insert flag needed to run remove function in detection */ + /* work even though function was never properly inserted */ + if (!exp_fhandler->inserted) { + exp_fhandler->inserted = true; + dev_info(&rmi4_data->i2c_client->dev, + "removing not inserted handler %d\n", + exp_fhandler->fn_type); + } + exp_fhandler->func_init = NULL; + goto exit; + } + } + +exit: + mutex_unlock(&rmi4_data->exp_fn_ctrl.list_mutex); + if (det_workqueue) + queue_delayed_work(det_workqueue, + &rmi4_data->exp_fn_ctrl.det_work, 0); + return; +} +EXPORT_SYMBOL(synaptics_rmi4_new_function); + +static void synaptics_dsx_free_patch(struct synaptics_dsx_patch *patch_set) +{ + struct synaptics_dsx_func_patch *fp, *next_list_entry; + down(&patch_set->list_sema); + list_for_each_entry_safe(fp, next_list_entry, + &patch_set->cfg_head, link) { + kfree(fp->data); + list_del(&fp->link); + } + up(&patch_set->list_sema); +} + +static struct synaptics_dsx_patch *synaptics_dsx_init_patch(const char *name) +{ + struct synaptics_dsx_patch *patch_set; + patch_set = kzalloc(sizeof(struct synaptics_dsx_patch), GFP_KERNEL); + if (patch_set) { + patch_set->name = name; + sema_init(&patch_set->list_sema, 1); + INIT_LIST_HEAD(&patch_set->cfg_head); + } + return patch_set; +} + +static void synaptics_dsx_queued_resume(struct work_struct *w) +{ + struct synaptics_rmi4_data *rmi4_data = + container_of(w, struct synaptics_rmi4_data, resume_work); + synaptics_rmi4_resume(&(rmi4_data->i2c_client->dev)); +} + +static inline int synaptics_dsx_display_off(struct device *dev) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + cancel_work_sync(&rmi4_data->resume_work); + return synaptics_rmi4_suspend(dev); +} + +static inline int synaptics_dsx_display_on(struct device *dev) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + pr_debug("queue resume\n"); + queue_work(system_wq, &rmi4_data->resume_work); + return 0; +} + +static int rmi_reboot(struct notifier_block *nb, + unsigned long event, + void *unused) +{ + struct synaptics_rmi4_data *rmi4_data = + container_of(nb, struct synaptics_rmi4_data, rmi_reboot); + struct synaptics_dsx_platform_data + *platform_data = &rmi4_data->board; + struct regulator *reg_ptr; + int state = STATE_INVALID; + + if (likely(rmi4_data)) + state = synaptics_dsx_get_state_safe(rmi4_data); + + if (STATE_INVALID == state) + goto out; + + /* At this point, we're all good with clean-up works */ + synaptics_dsx_set_state_safe(rmi4_data, STATE_INVALID); + +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) + mmi_panel_unregister_notifier(&rmi4_data->panel_nb); +#elif defined(CONFIG_DRM) + msm_drm_unregister_client(&rmi4_data->panel_nb); +#elif defined(CONFIG_FB) + fb_unregister_client(&rmi4_data->panel_nb); +#endif + if (rmi4_data->irq_enabled) { + rmi4_data->irq_enabled = false; + disable_irq(rmi4_data->irq); + free_irq(rmi4_data->irq, rmi4_data); + } + dev_info(&rmi4_data->i2c_client->dev, "touch shutdown\n"); + + if (!IS_ERR(rmi4_data->vdd_quirk)) { + reg_ptr = rmi4_data->vdd_quirk; + rmi4_data->vdd_quirk = ERR_PTR(-ENODEV); + regulator_force_disable(reg_ptr); + regulator_put(reg_ptr); + } + + if (platform_data->regulator_en && !IS_ERR(rmi4_data->regulator)) { + reg_ptr = rmi4_data->regulator; + rmi4_data->regulator = ERR_PTR(-ENODEV); + pr_debug("touch reboot - disable regulators\n"); + regulator_force_disable(reg_ptr); + regulator_put(reg_ptr); + msleep(1000); + } +out: + return NOTIFY_DONE; +} + +#if defined(USB_CHARGER_DETECTION) +/***************************************************************/ +/* USB charging source info from power_supply driver directly */ +/***************************************************************/ +static enum power_supply_property ps_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, +}; + +static const char * const ps_usb_supply[] = { "usb", }; +static bool ps_usb_present; +static int ps_get_property(struct power_supply *psy, + enum power_supply_property psp, union power_supply_propval *val) +{ + val->intval = 0; + return 0; +} + +static void ps_external_power_changed(struct power_supply *psy) +{ + struct power_supply *usb_psy = power_supply_get_by_name("usb"); + union power_supply_propval pval = {0}; + struct synaptics_rmi4_data *rmi4_data = container_of(psy, + struct synaptics_rmi4_data, psy); + struct device *dev = &rmi4_data->i2c_client->dev; + struct config_modifier *cm; + int is_plugged, state; + + if (!usb_psy || !usb_psy->get_property) + return; + + usb_psy->get_property(usb_psy, POWER_SUPPLY_PROP_PRESENT, &pval); + dev_dbg(dev, "external_power_changed: %d\n", pval.intval); + + if (ps_usb_present == (pval.intval == 1)) { + ps_usb_present = pval.intval == 1; + return; + } + + is_plugged = !!pval.intval; + down(&rmi4_data->modifiers.list_sema); + list_for_each_entry(cm, &rmi4_data->modifiers.mod_head, link) { + if (cm->id == SYNA_MOD_CHARGER) { + cm->effective = is_plugged; + break; + } + } + up(&rmi4_data->modifiers.list_sema); + /* in case there is no patch required */ + if (!cm) + return; + state = synaptics_dsx_get_state_safe(rmi4_data); + dev_info(dev, "power supply presence %d in state %d\n", + is_plugged, state); + /* reset touch ic on power supply presence change */ + if (state == STATE_ACTIVE) { + /* set unknown state to ensure IRQ gets */ + /* enabled on state transition to active */ + synaptics_dsx_sensor_state(rmi4_data, STATE_UNKNOWN); + /* disable IRQ to handle reset */ + synaptics_rmi4_irq_enable(rmi4_data, false); + if (!is_plugged) { + int retval; + /* perform SW reset to restore defaults */ + retval = synaptics_dsx_ic_reset( + rmi4_data, RMI4_SW_RESET); + if (retval < 0) + dev_err(dev, "power supply: sw reset"\ + " failed %d\n", retval); + } + synaptics_dsx_sensor_ready_state(rmi4_data, false); + /* TODO: just apply a single patch on insertion??? */ + } + ps_usb_present = pval.intval == 1; +} + +#define ps_notifier_unregister(r) power_supply_unregister(&r->psy) + +static int ps_notifier_register(struct synaptics_rmi4_data *rmi4_data) +{ + int error; + struct device *dev = &rmi4_data->i2c_client->dev; + + rmi4_data->psy.num_supplies = 1; + rmi4_data->psy.supplied_from = ((char **) ps_usb_supply); + rmi4_data->psy.name = "synaptics-psy"; + rmi4_data->psy.type = POWER_SUPPLY_TYPE_UNKNOWN; + rmi4_data->psy.properties = ps_props; + rmi4_data->psy.num_properties = ARRAY_SIZE(ps_props); + rmi4_data->psy.get_property = ps_get_property; + rmi4_data->psy.external_power_changed = ps_external_power_changed; + + error = power_supply_register(dev, &rmi4_data->psy); + if (error < 0) { + dev_err(dev, "power_supply_register failed rc=%d\n", error); + return error; + } + return 0; +} +#else +#define ps_notifier_register(r) +#define ps_notifier_unregister(r) +#endif + +#include +#include + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!rmi4_data) { + pr_err("cannot get rmi4_data pointer\n"); + return (ssize_t)0; + } + path = kobject_get_path(&rmi4_data->i2c_client->dev.kobj, GFP_KERNEL); + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "synaptics"); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_NULL +}; + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +static struct class *touchscreen_class; +struct class *get_touchscreen_class_ptr(void) +{ + return touchscreen_class; +} +EXPORT_SYMBOL(get_touchscreen_class_ptr); + +void set_touchscreen_class_ptr(struct class *ptr) +{ + touchscreen_class = ptr; +} +EXPORT_SYMBOL(set_touchscreen_class_ptr); + +static int synaptics_dsx_sysfs_touchscreen( + struct synaptics_rmi4_data *rmi4_data, bool create) +{ + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + char *class_fname = CLASS_PRIMARY_FNAME; + static int minor; + + if (create) { + minor = input_get_new_minor(rmi4_data->i2c_client->addr, + 1, false); + if (minor < 0) + minor = input_get_new_minor(TSDEV_MINOR_BASE, + TSDEV_MINOR_MAX, true); + pr_info("assigned minor %d\n", minor); + + if (!touchscreen_class) { + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + } + + if (rmi4_data->class_entry_name) + class_fname = (char *)rmi4_data->class_entry_name; + + rmi4_data->ts_class_dev = device_create(touchscreen_class, + NULL, MKDEV(INPUT_MAJOR, minor), + rmi4_data, !rmi4_data->class_entry_name ? CLASS_PRIMARY_FNAME : "secondary"); + if (IS_ERR(rmi4_data->ts_class_dev)) { + error = PTR_ERR(rmi4_data->ts_class_dev); + rmi4_data->ts_class_dev = NULL; + return error; + } + + dev_set_drvdata(rmi4_data->ts_class_dev, rmi4_data); + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + error = device_create_file(rmi4_data->ts_class_dev, &attrs[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + } else { + if (!touchscreen_class || !rmi4_data->ts_class_dev) + return -ENODEV; + + dev_set_drvdata(rmi4_data->ts_class_dev, NULL); + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(rmi4_data->ts_class_dev, &attrs[i]); + + device_unregister(rmi4_data->ts_class_dev); + rmi4_data->ts_class_dev = NULL; + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(rmi4_data->ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor)); + rmi4_data->ts_class_dev = NULL; + class_unregister(touchscreen_class); + pr_err("error creating touchscreen class\n"); + + return -ENODEV; +} + +static int synaptics_rmi4_hw_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval = 0, attr_count; + struct pinctrl *pinctrl; + struct synaptics_dsx_platform_data + *platform_data = &rmi4_data->board; + + pinctrl = devm_pinctrl_get_select(&rmi4_data->i2c_client->dev, + "active"); + if (IS_ERR(pinctrl)) { + long int error = PTR_ERR(pinctrl); + dev_err(&rmi4_data->i2c_client->dev, + "%s: pinctrl failed err %ld\n", __func__, error); + return (int)error; + } + + if (platform_data->gpio_config) + retval = platform_data->gpio_config(platform_data, true); + else if (gpio_is_valid(platform_data->reset_gpio)) { + retval = gpio_request(platform_data->reset_gpio, + RESET_GPIO_NAME); + if (!retval) + retval = gpio_direction_output( + platform_data->reset_gpio, 1); + } + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to configure GPIO\n", + __func__); + return retval; + } + + retval = synaptics_dsx_alloc_input(rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to allocate input device\n", + __func__); + goto err_gpio_free; + } + + rmi4_data->regulator = devm_regulator_get( + &rmi4_data->i2c_client->dev, "touch_vdd"); + if (IS_ERR(rmi4_data->regulator)) { + retval = PTR_ERR(rmi4_data->regulator); + if (PTR_ERR(rmi4_data->regulator) == -EPROBE_DEFER) + goto err_gpio_free; + + dev_warn(&rmi4_data->i2c_client->dev, + "%s: Failed to get regulator\n", + __func__); + goto err_gpio_free; + } else { + retval = regulator_enable(rmi4_data->regulator); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Error %d enabling touch-vdd regulator\n", + __func__, retval); + goto err_touch_vdd; + } + platform_data->regulator_en = true; + } + + rmi4_data->vdd_quirk = devm_regulator_get( + &rmi4_data->i2c_client->dev, "vdd_quirk"); + if (!IS_ERR(rmi4_data->vdd_quirk)) { + retval = regulator_enable(rmi4_data->vdd_quirk); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, "Failed to enable vdd-quirk\n"); + goto err_vdd_quirk; + } + } else { + retval = PTR_ERR(rmi4_data->regulator); + if (retval == -EPROBE_DEFER) + goto err_vdd_quirk; + } + + retval = synaptics_dsx_ic_reset(rmi4_data, RMI4_HW_RESET); + if (retval > 0) + dev_dbg(&rmi4_data->i2c_client->dev, "%s: successful reset took %dms\n", + __func__, retval); + else + dev_warn(&rmi4_data->i2c_client->dev, "%s: timed out waiting for idle\n", + __func__); + + retval = synaptics_rmi4_query_device(rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to query device\n", + __func__); + goto err_query_device; + } + + INIT_WORK(&rmi4_data->resume_work, synaptics_dsx_queued_resume); + +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) + rmi4_data->panel_nb.pre_display_off = synaptics_rmi4_suspend; + rmi4_data->panel_nb.display_on = synaptics_rmi4_resume; + rmi4_data->panel_nb.dev = &client->dev; + if (!mmi_panel_register_notifier(&rmi4_data->panel_nb)) + pr_info("registered MMI panel notifier\n"); + else + dev_err(&rmi4_data->i2c_client->dev, + "%s: Unable to register MMI notifier\n", + __func__); +#elif defined(CONFIG_DRM) + rmi4_data->panel_nb.notifier_call = synaptics_dsx_panel_cb; + if (!msm_drm_register_client(&rmi4_data->panel_nb)) + pr_debug("registered DRM notifier\n"); + else + dev_err(&rmi4_data->i2c_client->dev, + "%s: Unable to register DRM notifier\n", + __func__); +#elif defined(CONFIG_FB) + rmi4_data->panel_nb.notifier_call = synaptics_dsx_panel_cb; + if (!fb_register_client(&rmi4_data->panel_nb)) + pr_debug("registered FB notifier\n"); + else + dev_err(&rmi4_data->i2c_client->dev, + "%s: Unable to register FB notifier\n", + __func__); +#endif + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&rmi4_data->i2c_client->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + goto err_sysfs; + } + } + + rmi4_data->pm_qos_irq.irq = rmi4_data->irq; + synaptics_dsx_sensor_ready_state(rmi4_data, true); + + rmi4_data->rmi_reboot.notifier_call = rmi_reboot; + rmi4_data->rmi_reboot.next = NULL; + rmi4_data->rmi_reboot.priority = 1; + retval = register_reboot_notifier(&rmi4_data->rmi_reboot); + if (retval) + dev_err(&rmi4_data->i2c_client->dev, "register for reboot failed\n"); + +#ifdef CONFIG_MMI_HALL_NOTIFICATIONS + if (rmi4_data->folio_detection_enabled) { + /* register notifier at the end of probe to */ + /* avoid unnecessary reset in STANDBY state */ + rmi4_data->folio_notif.notifier_call = folio_notifier_callback; + dev_dbg(&rmi4_data->i2c_client->dev, "registering folio notifier\n"); + retval = mmi_hall_register_notifier(&rmi4_data->folio_notif, + MMI_HALL_FOLIO, true); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, + "Error registering folio_notifier: %d\n", + retval); + /* inability to register folio notifications handler */ + /* is not fatal, thus reset return value to success */ + retval = 0; + } + } +#endif + synaptics_dsx_sysfs_touchscreen(rmi4_data, true); + + if (rmi4_data->charger_detection_enabled) + ps_notifier_register(rmi4_data); + + if (rmi4_data->fps_detection_enabled) { + rmi4_data->fps_notif.notifier_call = fps_notifier_callback; + dev_dbg(&rmi4_data->i2c_client->dev, "registering FPS notifier\n"); + retval = FPS_register_notifier( + &rmi4_data->fps_notif, 0xBEEF, false); + if (retval) { + if (det_workqueue) + queue_delayed_work(det_workqueue, + &rmi4_data->exp_fn_ctrl.det_work, + msecs_to_jiffies(EXP_FN_DET_INTERVAL)); + dev_err(&rmi4_data->i2c_client->dev, + "Failed to register fps_notifier: %d\n", + retval); + retval = 0; + } else + rmi4_data->is_fps_registered = true; + } + + synaptics_dsx_pm_qos(rmi4_data, PM_ADD, false); + + /* init reporting semaphore and set expected # of fingers to 1 */ + sema_init(&rmi4_data->rctrl.ctrl_sema, 1); + rmi4_data->rctrl.expected = 1; + + return retval; + +err_sysfs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&rmi4_data->i2c_client->dev.kobj, + &attrs[attr_count].attr); + } + + device_remove_file(&rmi4_data->i2c_client->dev, &dev_attr_poweron); + +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) + mmi_panel_unregister_notifier(&rmi4_data->panel_nb); +#elif defined(CONFIG_DRM) + msm_drm_unregister_client(&rmi4_data->panel_nb); +#elif defined(CONFIG_FB) + fb_unregister_client(&rmi4_data->panel_nb); +#endif + +err_query_device: + if (!IS_ERR(rmi4_data->vdd_quirk)) + regulator_disable(rmi4_data->vdd_quirk); + +err_vdd_quirk: + if (platform_data->regulator_en) + regulator_disable(rmi4_data->regulator); + +err_touch_vdd: + synaptics_rmi4_cleanup(rmi4_data); + if (rmi4_data->input_registered) + input_unregister_device(rmi4_data->input_dev); + else + input_free_device(rmi4_data->input_dev); + +err_gpio_free: + if (platform_data->gpio_config) + gpio_free(platform_data->irq_gpio); + if (gpio_is_valid(platform_data->reset_gpio)) { + gpio_set_value(platform_data->reset_gpio, 0); + gpio_free(platform_data->reset_gpio); + } + + return retval; +} + +static int dummy_init(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_data *ptr; + ptr = rmi4_data; + return 0; +} + +static void dummy_remove(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_data *ptr = rmi4_data; + ptr = rmi4_data; +} + + /** + * synaptics_rmi4_probe() + * + * Called by the kernel when an association with an I2C device of the + * same name is made (after doing i2c_add_driver). + * + * This funtion allocates and initializes the resources for the driver + * as an input driver, turns on the power to the sensor, queries the + * sensor for its supported Functions and characteristics, registers + * the driver to the input subsystem, sets up the interrupt, and creates + * a work queue for detection of other expansion Function modules. + */ +static int synaptics_rmi4_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int rc = 0; + struct synaptics_rmi4_data *rmi4_data; + struct synaptics_dsx_platform_data *platform_data; +#if defined(CONFIG_DRM) + int panel_ready, probe_status; + char *display_name = NULL; +#endif + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, + "%s: SMBus byte data not supported\n", + __func__); + return -EIO; + } + + rmi4_data = kzalloc(sizeof(*rmi4_data), GFP_KERNEL); + if (!rmi4_data) { + dev_err(&client->dev, + "%s: Failed to alloc mem for rmi4_data\n", + __func__); + return -ENOMEM; + } + dev_dbg(&client->dev, "%s: rmi4_data ptr = %p\n", __func__, rmi4_data); + + /* assign pointer to client structure right away for further use */ + rmi4_data->i2c_client = client; + + rmi4_data->pm_qos_latency = PM_QOS_DEFAULT_VALUE; + +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + /* TEST OPTIONS */ + rmi4_data->test_irq_delay_ms = 0; + rmi4_data->test_irq_data_contig = 1; /* enforce test mode */ +#endif + if (client->dev.of_node) { + rc = synaptics_dsx_of_init(client, rmi4_data); + if (rc) { + dev_err(&client->dev, + "%s: No platform data found\n", + __func__); + kfree(rmi4_data); + return -EINVAL; + } + } else + rmi4_data->board = *(struct synaptics_dsx_platform_data *) + client->dev.platform_data; + platform_data = &rmi4_data->board; + +#if defined(CONFIG_DRM) + /* DRM status of panel associated with control DSI */ + panel_ready = dsi_display_is_panel_enable(rmi4_data->ctrl_dsi, + &probe_status, &display_name); + dev_dbg(&client->dev, + "%s: drm: probe=%d, enable=%d, panel'%s'\n", + __func__, probe_status, panel_ready, display_name); + + if (rmi4_data->bound_display) { + if (probe_status == -ENODEV || + (display_name && strncmp(display_name, rmi4_data->bound_display, + strlen(rmi4_data->bound_display)))) { + dev_err(&client->dev, + "%s: display binding failed\n", __func__); + kfree(rmi4_data); + return -ENODEV; + } + } else { + /* check for dummy panel */ + if (display_name && strstr(display_name, "dummy")) { + dev_info(&client->dev, + "%s: dummy panel detected; exiting...\n", __func__); + kfree(rmi4_data); + return -ENODEV; + } + } +#endif + + rc = synaptics_dsx_config_regs_alloc(rmi4_data); + if (rc) { + dev_err(&client->dev, + "%s: cannot allocate resources\n", __func__); + kfree(rmi4_data); + return -ENOMEM; + } + + rmi4_data->touch_ud_stats.ud = devm_kzalloc(&client->dev, + TOUCH_UD_BUFF*sizeof(struct touch_up_down), GFP_KERNEL); + rmi4_data->touch_ud_stats.ud_len = TOUCH_UD_BUFF; + rmi4_data->touch_ud_stats.name = "ts"; + + if (rmi4_data->button_0d_enabled) { + rmi4_data->button_ud_stats.ud = devm_kzalloc(&client->dev, + BUTTON_UD_BUFF*sizeof(struct touch_up_down), GFP_KERNEL); + rmi4_data->button_ud_stats.ud_len = BUTTON_UD_BUFF; + rmi4_data->button_ud_stats.name = "btn"; + } + + rmi4_data->state = STATE_INVALID; + rmi4_data->current_page = MASK_8BIT; + rmi4_data->irq_enabled = false; + atomic_set(&rmi4_data->touch_stopped, 1); + rmi4_data->ic_on = true; + rmi4_data->flash_enabled = false; + rmi4_data->tsb_buff_clean_flag = 1; + rmi4_data->f12_data_registers_ptr = + find_function(rmi4_data, SYNAPTICS_RMI4_F12 | DATA_TYPE); + rmi4_data->i2c_read = synaptics_rmi4_i2c_read; + rmi4_data->i2c_write = synaptics_rmi4_i2c_write; + rmi4_data->set_state = synaptics_dsx_sensor_state; + rmi4_data->ready_state = synaptics_dsx_sensor_ready_state; + rmi4_data->irq_enable = synaptics_rmi4_irq_enable; + rmi4_data->reset_device = synaptics_rmi4_reset_device; + rmi4_data->get_status = synaptics_rmi4_f01_flashprog_status; + + /* get irq number initialized before calling reset */ + rmi4_data->irq = gpio_to_irq(platform_data->irq_gpio); + + i2c_set_clientdata(client, rmi4_data); + + mutex_init(&(rmi4_data->rmi4_io_ctrl_mutex)); + mutex_init(&(rmi4_data->state_mutex)); + + rc = synaptics_exp_fn_ctrl_init(rmi4_data); + if (rc) { + dev_err(&client->dev, + "%s: Cannot alloc exp_fn_ctrl\n", + __func__); + goto free_and_exit; + } + + rc = device_create_file(&client->dev, &dev_attr_poweron); + if (rc < 0) { + dev_err(&client->dev, + "%s: Cannot create poweron\n", + __func__); + goto free_and_exit; + } + + /* add driver's instance to the list */ + rmi4_data->instance = ++drv_instance_counter; + mutex_lock(&instances_mutex); + list_add_tail(&rmi4_data->node, &drv_instances_list); + mutex_unlock(&instances_mutex); + rmi4_data->irq_name = INSTANCE_RMI(DRIVER_NAME, rmi4_data); + rmi4_data->reset_name = INSTANCE_RMI("synaptics_reset", rmi4_data); + + if (!rmi4_data->splash_screen_mode) { + rc = synaptics_rmi4_hw_init(rmi4_data); + if (rc < 0) { + i2c_set_clientdata(rmi4_data->i2c_client, NULL); + goto free_and_exit; + } + return 0; + } + + dev_dbg(&client->dev, + "%s: delay hw init until DRM reports panel is ready\n", + __func__); + synaptics_dsx_sensor_state(rmi4_data, STATE_UNKNOWN); + synaptics_rmi4_new_function(rmi4_data, RMI_DRM_FRAMEWORK, true, + dummy_init, dummy_remove, NULL, NULL, IC_MODE_ANY); + return 0; + +free_and_exit: + kfree(rmi4_data); + return rc; +} + + /** + * synaptics_rmi4_remove() + * + * Called by the kernel when the association with an I2C device of the + * same name is broken (when the driver is unloaded). + * + * This funtion terminates the work queue, stops sensor data acquisition, + * frees the interrupt, unregisters the driver from the input subsystem, + * turns off the power to the sensor, and frees other allocated resources. + */ +static int synaptics_rmi4_remove(struct i2c_client *client) +{ + struct pinctrl *pinctrl; + int attr_count; + struct synaptics_dsx_platform_data *platform_data; + struct synaptics_rmi4_data *rmi4_data = i2c_get_clientdata(client); + + if (rmi4_data == NULL) + return 0; + + /* At this point, we're all good with clean-up works */ + synaptics_dsx_set_state_safe(rmi4_data, STATE_STANDBY); + atomic_set(&rmi4_data->touch_stopped, 1); + +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) + mmi_panel_unregister_notifier(&rmi4_data->panel_nb); +#elif defined(CONFIG_DRM) + msm_drm_unregister_client(&rmi4_data->panel_nb); +#elif defined(CONFIG_FB) + fb_unregister_client(&rmi4_data->panel_nb); +#endif + +#ifdef CONFIG_MMI_HALL_NOTIFICATIONS + if (rmi4_data->folio_detection_enabled) + mmi_hall_unregister_notifier(rmi4_data); +#endif + if (rmi4_data->charger_detection_enabled) + ps_notifier_unregister(rmi4_data); + if (rmi4_data->is_fps_registered) + FPS_unregister_notifier(&rmi4_data->fps_notif, 0xBEEF); + + unregister_reboot_notifier(&rmi4_data->rmi_reboot); + + /* power down touch ic */ + platform_data = &rmi4_data->board; + if (platform_data->gpio_config) + gpio_free(platform_data->irq_gpio); + if (gpio_is_valid(platform_data->reset_gpio)) + gpio_free(platform_data->reset_gpio); + + pinctrl = devm_pinctrl_get_select_default(&rmi4_data->i2c_client->dev); + if (IS_ERR(pinctrl)) + dev_err(&rmi4_data->i2c_client->dev, + "%s: pinctrl default failed\n", __func__); + + if (rmi4_data->ic_on) { + if (!IS_ERR(rmi4_data->vdd_quirk)) + regulator_disable(rmi4_data->vdd_quirk); + + if (rmi4_data->board.regulator_en) + regulator_disable(rmi4_data->regulator); + /* regulators are bound to device, thus no need to put here !!! */ + } + + if (rmi4_data->exp_fn_ctrl.inited) { + cancel_delayed_work_sync(&rmi4_data->exp_fn_ctrl.det_work); + flush_workqueue(det_workqueue); + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&rmi4_data->i2c_client->dev.kobj, + &attrs[attr_count].attr); + } + + device_remove_file(&rmi4_data->i2c_client->dev, &dev_attr_poweron); + + synaptics_dsx_sysfs_touchscreen(rmi4_data, false); + synaptics_rmi4_cleanup(rmi4_data); + + if (rmi4_data->input_registered) + input_unregister_device(rmi4_data->input_dev); + else + input_free_device(rmi4_data->input_dev); + + list_del(&rmi4_data->node); + if (list_empty(&drv_instances_list)) { + destroy_workqueue(det_workqueue); + } + + synaptics_dsx_pm_qos(rmi4_data, PM_REMOVE, false); + + i2c_set_clientdata(client, NULL); + + kfree(rmi4_data); + + return 0; +} + +static int fps_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + int state, fps_state = *(int *)data; + struct synaptics_rmi4_data *rmi4_data = + container_of(self, struct synaptics_rmi4_data, fps_notif); + + if (rmi4_data && event == 0xBEEF && + rmi4_data && rmi4_data->i2c_client) { + struct config_modifier *cm = + modifier_by_id(rmi4_data, SYNA_MOD_FPS); + if (!cm) { + dev_err(&rmi4_data->i2c_client->dev, + "No FPS modifier found\n"); + goto done; + } + + state = synaptics_dsx_get_state_safe(rmi4_data); + dev_dbg(&rmi4_data->i2c_client->dev, + "FPS: %s(%d), suspend flag: %d, BL flag: %d\n", + synaptics_dsx_state_name(state), state, + atomic_read(&rmi4_data->touch_stopped), + rmi4_data->in_bootloader); + if (fps_state) {/* on */ + rmi4_data->clipping_on = true; + rmi4_data->clipa = cm->clipa; + cm->effective = true; + } else {/* off */ + rmi4_data->clipping_on = false; + rmi4_data->clipa = NULL; + cm->effective = false; + } + pr_info("FPS: clipping is %s\n", + rmi4_data->clipping_on ? "ON" : "OFF"); + } +done: + return 0; +} + +#if defined(CONFIG_DRM) && !defined(CONFIG_MMI_PANEL_NOTIFICATIONS) +static int synaptics_dsx_panel_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + struct synaptics_rmi4_data *rmi4_data = + container_of(nb, struct synaptics_rmi4_data, panel_nb); + + if (!evdata || (evdata->id != rmi4_data->ctrl_dsi)) { + pr_debug("drm notification: id(%d) != ctrl_dsi(%d)\n", + evdata->id, rmi4_data->ctrl_dsi); + return 0; + } + + if ((event == MSM_DRM_EARLY_EVENT_BLANK || event == MSM_DRM_EVENT_BLANK) && + evdata && evdata->data && rmi4_data) { + int *blank = evdata->data; + pr_debug("drm notification: event = %lu blank = %d\n", event, *blank); + /* entering suspend upon early blank event */ + /* to ensure shared power supply is still on */ + /* for in-cell design touch solutions */ + if (event == MSM_DRM_EARLY_EVENT_BLANK) { + if (*blank != MSM_DRM_BLANK_POWERDOWN) + return 0; + synaptics_dsx_display_off(&rmi4_data->i2c_client->dev); + } else if (*blank == MSM_DRM_BLANK_UNBLANK) { + synaptics_dsx_display_on(&rmi4_data->i2c_client->dev); + } + } + + return 0; +} +#elif defined(CONFIG_FB) && !defined(CONFIG_MMI_PANEL_NOTIFICATIONS) +static int synaptics_dsx_panel_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + struct synaptics_rmi4_data *rmi4_data = + container_of(nb, struct synaptics_rmi4_data, panel_nb); + + if (!evdata || !evdata->info || + (evdata->info->node != rmi4_data->ctrl_dsi)) + return 0; + + if ((event == FB_EARLY_EVENT_BLANK || event == FB_EVENT_BLANK) && + evdata && evdata->data && rmi4_data) { + int *blank = evdata->data; + pr_debug("fb notification: event = %lu blank = %d\n", event, *blank); + /* entering suspend upon early blank event */ + /* to ensure shared power supply is still on */ + /* for in-cell design touch solutions */ + if (event == FB_EARLY_EVENT_BLANK) { + if (*blank != FB_BLANK_POWERDOWN) + return 0; + synaptics_dsx_display_off(&rmi4_data->i2c_client->dev); + } else if (*blank == FB_BLANK_UNBLANK || + (*blank == FB_BLANK_NORMAL && + atomic_read(&rmi4_data->touch_stopped))) { + synaptics_dsx_display_on(&rmi4_data->i2c_client->dev); + } + } + + return 0; +} +#endif + +#ifdef CONFIG_MMI_HALL_NOTIFICATIONS +static int folio_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + int state, folio_state = *(int *)data; + struct synaptics_rmi4_data *rmi4_data = + container_of(self, struct synaptics_rmi4_data, folio_notif); + + if (rmi4_data && event == MMI_HALL_FOLIO && + rmi4_data && rmi4_data->i2c_client) { + struct config_modifier *cm = + modifier_by_id(rmi4_data, SYNA_MOD_FOLIO); + if (!cm) { + dev_err(&rmi4_data->i2c_client->dev, + "No FOLIO modifier found\n"); + goto done; + } + + state = synaptics_dsx_get_state_safe(rmi4_data); + "state: %s(%d), suspend flag: %d, BL flag: %d\n", + synaptics_dsx_state_name(state), state, + atomic_read(&rmi4_data->touch_stopped), + rmi4_data->in_bootloader); + if (folio_state) {/* close */ + rmi4_data->clipa = cm->clipa; + rmi4_data->clipping_on = true; + cm->effective = true; + } else {/* open */ + rmi4_data->clipa = NULL; + rmi4_data->clipping_on = false; + cm->effective = false; + } + + if (control_access_block_get()) + control_access_block_update_dynamic(rmi4_data); + + dev_info(&rmi4_data->i2c_client->dev, "folio: %s\n", + folio_state ? "CLOSED" : "OPENED"); + + if (!(state & STATE_UI)) { + dev_dbg(&rmi4_data->i2c_client->dev, "Not in UI\n"); + goto done; + } + + if (!rmi4_data->in_bootloader) + synaptics_dsx_enforce_modifiers(rmi4_data, cm); + } +done: + return 0; +} +#endif + + /** + * synaptics_rmi4_suspend() + * + * Called by the kernel during the suspend phase when the system + * enters suspend. + * + * This function stops finger data acquisition and puts the sensor to + * sleep, disables the interrupt, and turns off the power to the sensor. + */ +static int synaptics_rmi4_suspend(struct device *dev) +{ + struct pinctrl *pinctrl; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + struct synaptics_dsx_platform_data + *platform_data = &rmi4_data->board; + static char ud_stats[PAGE_SIZE]; + + if (atomic_cmpxchg(&rmi4_data->touch_stopped, 0, 1) == 1) + return 0; + + rmi4_data->flash_enabled = false; + synaptics_dsx_sensor_state(rmi4_data, STATE_SUSPEND); + + /* print UD statistics and send release events thereafter */ + synaptics_dsx_ud_stat(rmi4_data, ud_stats, sizeof(ud_stats)); + dev_info(dev, "%s\n", ud_stats); + synaptics_dsx_release_all(rmi4_data); + + if (gStat.enabled) + statistics_stop_timekeeping(); + + if (rmi4_data->purge_enabled) { + int value = 1; /* set flag */ + atomic_set(&rmi4_data->panel_off_flag, value); + pr_debug("touches purge is %s\n", value ? "ON" : "OFF"); + } + + if (rmi4_data->ic_on) { + /* use pinctrl to put touch RESET GPIO into SUSPEND state */ + if (gpio_is_valid(platform_data->reset_gpio)) + gpio_free(platform_data->reset_gpio); + pinctrl = devm_pinctrl_get_select_default( + &rmi4_data->i2c_client->dev); + if (IS_ERR(pinctrl)) + dev_err(&rmi4_data->i2c_client->dev, + "pinctrl failed err %ld\n", PTR_ERR(pinctrl)); + + if (!rmi4_data->splash_screen_mode) { + if (!IS_ERR(rmi4_data->vdd_quirk)) + regulator_disable(rmi4_data->vdd_quirk); + + /* if touch REGULATOR is available - turn it OFF */ + if (platform_data->regulator_en) + regulator_disable(rmi4_data->regulator); + } + + rmi4_data->ic_on = false; + } + + synaptics_dsx_pm_qos(rmi4_data, PM_UPDATE, true); + + return 0; +} + + /** + * synaptics_rmi4_resume() + * + * Called by the kernel during the resume phase when the system + * wakes up from suspend. + * + * This function turns on the power to the sensor, wakes the sensor + * from sleep, enables the interrupt, and starts finger data + * acquisition. + */ +static int synaptics_rmi4_resume(struct device *dev) +{ + int retval; + int reset = RMI4_HW_RESET; + struct pinctrl *pinctrl; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + struct synaptics_dsx_platform_data + *platform_data = &rmi4_data->board; + + if (atomic_cmpxchg(&rmi4_data->touch_stopped, 1, 0) == 0) + return 0; + + synaptics_dsx_pm_qos(rmi4_data, PM_UPDATE, false); + pr_debug("set pm_qos latency %d\n", rmi4_data->pm_qos_latency); + + if (!rmi4_data->ic_on) { + if (!rmi4_data->splash_screen_mode) { + /* if touch REGULATOR is avaialble - turn it ON */ + if (platform_data->regulator_en) { + retval = regulator_enable(rmi4_data->regulator); + if (retval) + pr_err("failed enabling touch-vdd: %d\n", retval); + } + + if (!IS_ERR(rmi4_data->vdd_quirk)) { + retval = regulator_enable(rmi4_data->vdd_quirk); + if (retval) + pr_err("failed enabling vdd-quirk: %d\n", retval); + } + } + + /* if RESET GPIO is in SUSPEND state - no HW reset */ + if (gpio_is_valid(platform_data->reset_gpio)) { + retval = gpio_get_value(platform_data->reset_gpio); + pr_debug("reset gpio state: %d\n", retval); + if (retval == 0) + reset = RMI4_WAIT_READY; + else /* if not in reset, IRQ might be enabled */ + synaptics_rmi4_irq_enable(rmi4_data, false); + } else + synaptics_rmi4_irq_enable(rmi4_data, false); + + pinctrl = devm_pinctrl_get_select(&rmi4_data->i2c_client->dev, + "active"); + if (IS_ERR(pinctrl)) { + long int error = PTR_ERR(pinctrl); + dev_err(&rmi4_data->i2c_client->dev, + "pinctrl failed err %ld\n", error); + } + + if (gpio_is_valid(platform_data->reset_gpio)) { + if (gpio_request(platform_data->reset_gpio, + RESET_GPIO_NAME) < 0) + pr_err("failed to request reset gpio\n"); + + gpio_direction_output(platform_data->reset_gpio, 1); + } + rmi4_data->ic_on = true; + } + + /* perform HW reset if needed and wait for touch IC boot completion */ + retval = synaptics_dsx_ic_reset(rmi4_data, reset); + if (retval > 0) + pr_debug("waited for idle %dms\n", retval); + else + dev_warn(&rmi4_data->i2c_client->dev, "%s: timed out waiting for idle\n", + __func__); + + /* read touch IC state */ + synaptics_dsx_sensor_ready_state(rmi4_data, false); + /* transition to active state is completed - allow flashing */ + rmi4_data->flash_enabled = true; + + if (rmi4_data->purge_enabled) { + int value = 0; /* clear flag */ + atomic_set(&rmi4_data->panel_off_flag, value); + pr_debug("touches purge is %s\n", value ? "ON" : "OFF"); + } + + if (gStat.enabled) + statistics_start_timekeeping(rmi4_data); + + return 0; +} + +#if !defined(CONFIG_FB) && defined(CONFIG_PM) +static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = { + .suspend = synaptics_rmi4_suspend, + .resume = synaptics_rmi4_resume, +}; +#endif + +static const struct i2c_device_id synaptics_rmi4_id_table[] = { + {DRIVER_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table); + +static struct i2c_driver synaptics_rmi4_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, +#if !defined(CONFIG_FB) && defined(CONFIG_PM) + .pm = &synaptics_rmi4_dev_pm_ops, +#endif + }, + .probe = synaptics_rmi4_probe, + .remove = synaptics_rmi4_remove, + .id_table = synaptics_rmi4_id_table, +}; + + /** + * synaptics_rmi4_init() + * + * Called by the kernel during do_initcalls (if built-in) + * or when the driver is loaded (if a module). + * + * This function registers the driver to the I2C subsystem. + * + */ +static int __init synaptics_rmi4_init(void) +{ + return i2c_add_driver(&synaptics_rmi4_driver); +} + + /** + * synaptics_rmi4_exit() + * + * Called by the kernel when the driver is unloaded. + * + * This funtion unregisters the driver from the I2C subsystem. + * + */ +static void __exit synaptics_rmi4_exit(void) +{ + i2c_del_driver(&synaptics_rmi4_driver); +} + +module_init(synaptics_rmi4_init); +module_exit(synaptics_rmi4_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX I2C Touch Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(SYNAPTICS_DSX_DRIVER_VERSION); diff --git a/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_i2c.h b/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_i2c.h new file mode 100644 index 000000000000..af11b0835388 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_i2c.h @@ -0,0 +1,1075 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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 _SYNAPTICS_DSX_RMI4_H_ +#define _SYNAPTICS_DSX_RMI4_H_ + +#define SYNAPTICS_DSX_DRIVER_VERSION "DSX 1.1" + +/* define to enable USB charger detection */ +#undef USB_CHARGER_DETECTION + +#include +#include +#include +#include +#include +#include +#include +#if defined(USB_CHARGER_DETECTION) +#include +#include +#endif +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) +#include +#elif defined(CONFIG_DRM) +#include +#endif + +#include + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)) +#define KERNEL_ABOVE_2_6_38 +#endif + +#ifdef KERNEL_ABOVE_2_6_38 +#define sstrtoul(...) kstrtoul(__VA_ARGS__) +#else +#define sstrtoul(...) strict_strtoul(__VA_ARGS__) +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0)) +#define KERNEL_ABOVE_3_7 +#endif + +#define PDT_PROPS (0x00EF) +#define PDT_START (0x00E9) +#define PDT_END (0x000A) +#define PDT_ENTRY_SIZE (0x0006) +#define PAGES_TO_SERVICE (10) +#define PAGE_SELECT_LEN (2) +#define PAGE_SELECT_ENFORCE (0x8000) + +#define SYNAPTICS_RMI4_F01 (0x01) +#define SYNAPTICS_RMI4_F11 (0x11) +#define SYNAPTICS_RMI4_F12 (0x12) +#define SYNAPTICS_RMI4_F1A (0x1a) +#define SYNAPTICS_RMI4_F34 (0x34) +#define SYNAPTICS_RMI4_F35 (0x35) +#define SYNAPTICS_RMI4_F38 (0x38) +#define SYNAPTICS_RMI4_F51 (0x51) +#define SYNAPTICS_RMI4_F54 (0x54) +#define SYNAPTICS_RMI4_F55 (0x55) +#define SYNAPTICS_RMI4_FDB (0xdb) + +#define SYNAPTICS_RMI4_PRODUCT_INFO_SIZE 2 +#define SYNAPTICS_RMI4_PRODUCT_ID_SIZE 10 +#define SYNAPTICS_RMI4_BUILD_ID_SIZE 3 +#define SYNAPTICS_RMI4_SERIAL_SIZE 7 +#define SYNAPTICS_RMI4_CONFIG_ID_SIZE 4 +#define SYNAPTICS_RMI4_PACKAGE_ID_SIZE 4 +#define SYNAPTICS_RMI4_FILENAME_SIZE 80 + +#define PACKAGE_ID_OFFSET 17 +#define FW_VERSION_OFFSET 18 +#define F34_PROPERTIES_OFFSET 1 +#define F34_PROPERTIES_OFFSET_V2 0 + +#define MAX_NUMBER_OF_FINGERS 10 +#define MAX_NUMBER_OF_BUTTONS 4 +#define MAX_INTR_REGISTERS 4 + +#define MASK_16BIT 0xFFFF +#define MASK_8BIT 0xFF +#define MASK_7BIT 0x7F +#define MASK_6BIT 0x3F +#define MASK_5BIT 0x1F +#define MASK_4BIT 0x0F +#define MASK_3BIT 0x07 +#define MASK_2BIT 0x03 +#define MASK_1BIT 0x01 + +#define PRODUCT_INFO_SIZE 2 +#define PRODUCT_ID_SIZE 10 + +/* + * struct synaptics_rmi4_fn_desc - function descriptor fields in PDT + * @query_base_addr: base address for query registers + * @cmd_base_addr: base address for command registers + * @ctrl_base_addr: base address for control registers + * @data_base_addr: base address for data registers + * @intr_src_count: number of interrupt sources + * @fn_number: function number + */ +struct synaptics_rmi4_fn_desc { + unsigned char query_base_addr; + unsigned char cmd_base_addr; + unsigned char ctrl_base_addr; + unsigned char data_base_addr; + unsigned char intr_src_count:3; + unsigned char reserved_1:2; + unsigned char fn_version:2; + unsigned char reserved_2:1; + unsigned char fn_number; +}; + +/* + * synaptics_rmi4_fn_full_addr - full 16-bit base addresses + * @query_base: 16-bit base address for query registers + * @cmd_base: 16-bit base address for data registers + * @ctrl_base: 16-bit base address for command registers + * @data_base: 16-bit base address for control registers + */ +struct synaptics_rmi4_fn_full_addr { + unsigned short query_base; + unsigned short cmd_base; + unsigned short ctrl_base; + unsigned short data_base; +}; + +/* + * struct synaptics_rmi4_fn - function handler data structure + * @fn_number: function number + * @num_of_data_sources: number of data sources + * @num_of_data_points: maximum number of fingers supported + * @size_of_data_register_block: data register block size + * @data1_offset: offset to data1 register from data base address + * @intr_reg_num: index to associated interrupt register + * @intr_mask: interrupt mask + * @full_addr: full 16-bit base addresses of function registers + * @link: linked list for function handlers + * @data_size: size of private data + * @data: pointer to private data + */ +struct synaptics_rmi4_fn { + unsigned char fn_number; + unsigned char num_of_data_sources; + unsigned char num_of_data_points; + unsigned char size_of_data_register_block; + unsigned char data1_offset; + unsigned char intr_reg_num; + unsigned char intr_mask; + struct synaptics_rmi4_fn_full_addr full_addr; + struct list_head link; + int data_size; + void *data; +}; + +/* + * struct synaptics_rmi4_device_info - device information + * @version_major: rmi protocol major version number + * @version_minor: rmi protocol minor version number + * @manufacturer_id: manufacturer id + * @product_props: product properties information + * @product_info: product info array + * @date_code: device manufacture date + * @tester_id: tester id array + * @serial_number: device serial number + * @product_id_string: device product id + * @support_fn_list: linked list for function handlers + */ +struct synaptics_rmi4_device_info { + unsigned int version_major; + unsigned int version_minor; + unsigned char manufacturer_id; + unsigned char product_props; + unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE]; + unsigned char serial[SYNAPTICS_RMI4_SERIAL_SIZE]; + unsigned char package_id[SYNAPTICS_RMI4_PACKAGE_ID_SIZE]; + unsigned char product_id_string[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1]; + unsigned char build_id[SYNAPTICS_RMI4_BUILD_ID_SIZE]; + unsigned char config_id[SYNAPTICS_RMI4_CONFIG_ID_SIZE]; + struct list_head support_fn_list; +}; + +#define TOUCH_UD_BUFF 20 +#define BUTTON_UD_BUFF 10 + +struct touch_up_down { + int mismatch; + unsigned char up_down; + unsigned int counter; +}; + +struct touch_area_stats { + struct touch_up_down *ud; + ssize_t ud_len; + ssize_t ud_id; + ssize_t unknown_counter; + const char *name; +}; + +struct synaptics_dsx_func_patch { + unsigned short func; + unsigned char regstr; + unsigned char subpkt; + unsigned char size; + unsigned char bitmask; + unsigned char *data; + struct list_head link; +}; + +struct synaptics_clip_area { + unsigned xul_clip, yul_clip, xbr_clip, ybr_clip; + unsigned inversion; /* clip inside (when 1) or ouside otherwise */ +}; + +enum { + SYNA_MOD_AOD, + SYNA_MOD_STATS, + SYNA_MOD_FOLIO, + SYNA_MOD_CHARGER, + SYNA_MOD_WAKEUP, + SYNA_MOD_FPS, + SYNA_MOD_QUERY, /* run time query; active only */ + SYNA_MOD_RT, /* run time patch; active only; always effective */ + SYNA_MOD_MAX +}; + +#define FLAG_FORCE_UPDATE 1 +#define FLAG_WAKEABLE 2 +#define FLAG_POWER_SLEEP 4 + +struct synaptics_dsx_patch { + const char *name; + int cfg_num; + unsigned int flags; + struct semaphore list_sema; + struct list_head cfg_head; +}; + +struct config_modifier { + const char *name; + int id; + bool effective; + struct synaptics_dsx_patch *active; + struct synaptics_dsx_patch *suspended; + struct synaptics_clip_area *clipa; + struct list_head link; +}; + +struct synaptics_dsx_modifiers { + int mods_num; + struct semaphore list_sema; + struct list_head mod_head; + +}; + +struct f34_properties { + union { + struct { + unsigned char regmap:1; + unsigned char unlocked:1; + unsigned char has_config_id:1; + unsigned char has_perm_config:1; + unsigned char has_bl_config:1; + unsigned char has_display_config:1; + unsigned char has_blob_config:1; + unsigned char reserved:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_properties_v2 { + union { + struct { + unsigned char query0_subpkt1_size:3; + unsigned char has_config_id:1; + unsigned char reserved:1; + unsigned char has_thqa:1; + unsigned char reserved2:2; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_control_95n { + union { + struct { + /* byte 0 - flags*/ + unsigned char c95_filter_bw:3; + unsigned char c95_byte0_b3_b6:4; + unsigned char c95_disable:1; + + /* bytes 1 - 10 */ + unsigned char c95_first_burst_length_lsb; + unsigned char c95_first_burst_length_msb; + unsigned char c95_addl_burst_length_lsb; + unsigned char c95_addl_burst_length_msb; + unsigned char c95_i_stretch; + unsigned char c95_r_stretch; + unsigned char c95_noise_control1; + unsigned char c95_noise_control2; + unsigned char c95_noise_control3; + unsigned char c95_noise_control4; + } __packed; + struct { + unsigned char data[11]; + } __packed; + }; +}; + +enum { + STATE_UNKNOWN, + STATE_ACTIVE, + STATE_SUSPEND, + STATE_STANDBY = 4, + STATE_BL, + STATE_INIT, + STATE_FLASH, + STATE_QUERY, + STATE_LOADING, + STATE_INVALID +}; + +#define STATE_UI (STATE_ACTIVE | STATE_SUSPEND) + +enum ic_modes { + IC_MODE_ANY = 0, + IC_MODE_BL, + IC_MODE_UI +}; + +enum exp_fn { + RMI_DEV = 0, + RMI_F34, + RMI_F54, + RMI_FW_UPDATER, + RMI_CTRL_ACCESS_BLK, + RMI_DRM_FRAMEWORK, + RMI_LAST, +}; + +static inline void batohs(unsigned short *dest, unsigned char *src) +{ + *dest = src[1] * 0x100 + src[0]; +} + +static inline void hstoba(unsigned char *dest, unsigned short src) +{ + dest[0] = src % 0x100; + dest[1] = src / 0x100; +} + +static inline void batohui(unsigned int *dest, unsigned char *src, size_t size) +{ + int si = size; + *dest = 0; + for (; --si >= 0;) + *dest += src[si] << (si << 3); +} + +struct synaptics_rmi4_subpkt { + bool present; + bool expected; + short offset; + unsigned int size; + void *data; +}; + +struct synaptics_rmi4_packet_reg { + unsigned short r_number; + bool updated; /* indicate that value in *data has been updated */ + bool modified; /* indicate that value in *data has been modified */ + bool expected; + short offset; + unsigned int size; + unsigned char *data; + unsigned char nr_subpkts; + struct synaptics_rmi4_subpkt *subpkt; +}; + +#define MAX_CONFIG_REGS 10 + +struct synaptics_rmi4_func_packet_regs { + unsigned short f_number; + unsigned short base_addr; + unsigned short query_offset; + int nr_regs; + struct synaptics_rmi4_packet_reg *regs; +}; + +struct reporting_ctrl { + bool reporting_stopped; + unsigned int events_cnt; + int expected, max_seen; + struct semaphore ctrl_sema; +}; + +#define MAX_READ_WRITE_SIZE 8096 +#define MIN_READ_WRITE_BUF_SIZE 256 + +struct temp_buffer { + unsigned char *buf; + unsigned short buf_size; +}; + +struct synaptics_exp_fn_ctrl { + bool inited; + struct mutex ctrl_mutex; + struct mutex list_mutex; + struct list_head fn_list; + struct delayed_work det_work; +}; + +/* + * struct synaptics_rmi4_data - rmi4 device instance data + * @i2c_client: pointer to associated i2c client + * @input_dev: pointer to associated input device + * @board: constant pointer to platform data + * @rmi4_mod_info: device information + * @regulator: pointer to associated regulator + * @vdd_quirk: pointer to associated regulator for 'quirk' config + * @rmi4_io_ctrl_mutex: mutex for i2c i/o control + * @det_work: work thread instance for expansion function detection + * @det_workqueue: pointer to work queue for work thread instance + * @early_suspend: instance to support early suspend power management + * @current_page: current page in sensor to acess + * @button_0d_enabled: flag for 0d button support + * @full_pm_cycle: flag for full power management cycle in early suspend stage + * @num_of_intr_regs: number of interrupt registers + * @f01_query_base_addr: query base address for f01 + * @f01_cmd_base_addr: command base address for f01 + * @f01_ctrl_base_addr: control base address for f01 + * @f01_data_base_addr: data base address for f01 + * @irq: attention interrupt + * @sensor_max_x: sensor maximum x value + * @sensor_max_y: sensor maximum y value + * @irq_enabled: flag for indicating interrupt enable status + * @touch_stopped: touch is in suspend state + * @flash_enabled: allow flashing once transition to active state is complete + * @ic_on: touch ic power state + * @fingers_on_2d: flag to indicate presence of fingers in 2d area + * @number_irq: total number of remembered interrupt times + * @last_irq: last interrup time's number (index of the location of interrupt) + * @irq_info: information about last few interrupt times + * @i2c_read: pointer to i2c read function + * @i2c_write: pointer to i2c write function + * @irq_enable: pointer to irq enable function + */ +struct synaptics_rmi4_data { + struct i2c_client *i2c_client; + struct device *ts_class_dev; + struct input_dev *input_dev; + struct synaptics_dsx_platform_data board; + struct synaptics_rmi4_device_info rmi4_mod_info; + struct regulator *regulator; + struct regulator *vdd_quirk; + struct mutex rmi4_io_ctrl_mutex; + struct mutex state_mutex; +#if defined(CONFIG_MMI_PANEL_NOTIFICATIONS) + struct mmi_notifier panel_nb; +#elif defined(CONFIG_FB) + struct notifier_block panel_nb; +#endif + atomic_t panel_off_flag; + unsigned char current_page; + unsigned char button_0d_enabled; + unsigned char num_of_rx; + unsigned char num_of_tx; + unsigned char num_of_fingers; + unsigned char aod_mt; + unsigned char f01_ctrl_register_0; + unsigned char intr_mask[MAX_INTR_REGISTERS]; + unsigned short num_of_intr_regs; + unsigned short f01_query_base_addr; + unsigned short f01_cmd_base_addr; + unsigned short f01_ctrl_base_addr; + unsigned short f01_data_base_addr; + unsigned int active_fn_intr_mask; + int state; + int irq; + int ctrl_dsi; + const char *class_entry_name; + const char *bound_display; + int sensor_max_x; + int sensor_max_y; + bool irq_enabled; + atomic_t touch_stopped; + bool splash_screen_mode; + bool flash_enabled; + bool ic_on; + bool fingers_on_2d; + bool input_registered; + bool in_bootloader; + int (*i2c_read)(struct synaptics_rmi4_data *pdata, unsigned short addr, + unsigned char *data, unsigned short length); + int (*i2c_write)(struct synaptics_rmi4_data *pdata, unsigned short addr, + unsigned char *data, unsigned short length); + void (*set_state)(struct synaptics_rmi4_data *rmi4_data, int state); + int (*ready_state)(struct synaptics_rmi4_data *rmi4_data, bool standby); + int (*irq_enable)(struct synaptics_rmi4_data *rmi4_data, bool enable); + int (*reset_device)(struct synaptics_rmi4_data *rmi4_data); + int (*get_status)(struct synaptics_rmi4_data *rmi4_data); + int number_irq; + int last_irq; + struct synaptics_rmi4_irq_info *irq_info; + + /* features enablers */ + bool charger_detection_enabled; + bool folio_detection_enabled; + bool fps_detection_enabled; + bool wakeup_detection_enabled; + bool patching_enabled; + bool purge_enabled; + + bool suspend_is_wakeable; + bool clipping_on; + struct synaptics_clip_area *clipa; + struct synaptics_dsx_modifiers modifiers; + + struct work_struct resume_work; + struct synaptics_rmi4_func_packet_regs *f12_data_registers_ptr; + struct notifier_block rmi_reboot; +#if defined(USB_CHARGER_DETECTION) + struct power_supply psy; +#endif +#ifdef CONFIG_MMI_HALL_NOTIFICATIONS + struct notifier_block folio_notif; +#endif + bool is_fps_registered; /* FPS notif registration might be delayed */ + struct notifier_block fps_notif; + + struct mutex rmi4_exp_init_mutex; + uint32_t pm_qos_latency; + struct pm_qos_request pm_qos_irq; + + struct reporting_ctrl rctrl; + unsigned char tsb_buff_clean_flag; + + bool touch_data_contiguous; + uint8_t *touch_data; + uint16_t touch_data_size; + + struct temp_buffer write_buf; + +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + /* TEST OPTIONS */ + int test_irq_delay_ms; + int test_irq_data_contig; +#endif + + struct synaptics_exp_fn_ctrl exp_fn_ctrl; + struct list_head node; + struct semaphore reset_semaphore; + int drm_state; + int instance; + char *irq_name; + char *reset_name; + + void *fwu_data; + void *rmidev_data; + void *f54_data; + + int (*scan_f54_ctrl_regs)(struct synaptics_rmi4_data *data, + struct synaptics_rmi4_func_packet_regs *regs); + int (*scan_f54_cmd_regs)(struct synaptics_rmi4_data *data, + struct synaptics_rmi4_func_packet_regs *regs); + int (*scan_f54_query_regs)(struct synaptics_rmi4_data *data, + struct synaptics_rmi4_func_packet_regs *regs); + int (*scan_f54_data_regs)(struct synaptics_rmi4_data *data, + struct synaptics_rmi4_func_packet_regs *regs); + + struct touch_area_stats touch_ud_stats; + struct touch_area_stats button_ud_stats; + + struct synaptics_rmi4_func_packet_regs config_regs[MAX_CONFIG_REGS]; +}; + +enum { + DRM_ST_UNDEF = 0, + DRM_ST_READY, + DRM_ST_TERM, + DRM_ST_MAX +}; + +#define ASSERT(s) (rmi4_data->drm_state == s) + +struct pdt_properties { + union { + struct { + unsigned char reserved_1:6; + unsigned char has_bsr:1; + unsigned char reserved_2:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct partition_table { + unsigned char partition_id:5; + unsigned char byte_0_reserved:3; + unsigned char byte_1_reserved; + unsigned char partition_length_7_0; + unsigned char partition_length_15_8; + unsigned char start_physical_address_7_0; + unsigned char start_physical_address_15_8; + unsigned char partition_properties_7_0; + unsigned char partition_properties_15_8; +} __packed; + +struct f01_device_control { + union { + struct { + unsigned char sleep_mode:2; + unsigned char nosleep:1; + unsigned char reserved:2; + unsigned char charger_connected:1; + unsigned char report_rate:1; + unsigned char configured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v7_query_0 { + union { + struct { + unsigned char subpacket_1_size:3; + unsigned char has_config_id:1; + unsigned char f34_query0_b4:1; + unsigned char has_thqa:1; + unsigned char f34_query0_b6__7:2; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v7_query_1_7 { + union { + struct { + /* query 1 */ + unsigned char bl_minor_revision; + unsigned char bl_major_revision; + + /* query 2 */ + unsigned char bl_fw_id_7_0; + unsigned char bl_fw_id_15_8; + unsigned char bl_fw_id_23_16; + unsigned char bl_fw_id_31_24; + + /* query 3 */ + unsigned char minimum_write_size; + unsigned char block_size_7_0; + unsigned char block_size_15_8; + unsigned char flash_page_size_7_0; + unsigned char flash_page_size_15_8; + + /* query 4 */ + unsigned char adjustable_partition_area_size_7_0; + unsigned char adjustable_partition_area_size_15_8; + + /* query 5 */ + unsigned char flash_config_length_7_0; + unsigned char flash_config_length_15_8; + + /* query 6 */ + unsigned char payload_length_7_0; + unsigned char payload_length_15_8; + + /* query 7 */ + unsigned char f34_query7_b0:1; + unsigned char has_bootloader:1; + unsigned char has_device_config:1; + unsigned char has_flash_config:1; + unsigned char has_manufacturing_block:1; + unsigned char has_guest_serialization:1; + unsigned char has_global_parameters:1; + unsigned char has_core_code:1; + unsigned char has_core_config:1; + unsigned char has_guest_code:1; + unsigned char has_display_config:1; + unsigned char f34_query7_b11__15:5; + unsigned char f34_query7_b16__23; + unsigned char f34_query7_b24__31; + } __packed; + unsigned char data[21]; + }; +}; + +struct f34_v7_data_1_5 { + union { + struct { + unsigned char partition_id:5; + unsigned char f34_data1_b5__7:3; + unsigned char block_offset_7_0; + unsigned char block_offset_15_8; + unsigned char transfer_length_7_0; + unsigned char transfer_length_15_8; + unsigned char command; + unsigned char payload_0; + unsigned char payload_1; + } __packed; + unsigned char data[8]; + }; +}; + +struct f34_v5v6_flash_properties { + union { + struct { + unsigned char reg_map:1; + unsigned char unlocked:1; + unsigned char has_config_id:1; + unsigned char has_pm_config:1; + unsigned char has_bl_config:1; + unsigned char has_disp_config:1; + unsigned char has_ctrl1:1; + unsigned char has_query4:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v5v6_flash_properties_2 { + union { + struct { + unsigned char has_guest_code:1; + unsigned char reserved:7; + } __packed; + unsigned char data[1]; + }; +}; + +struct register_offset { + unsigned char properties; + unsigned char properties_2; + unsigned char block_size; + unsigned char block_count; + unsigned char gc_block_count; + unsigned char flash_status; + unsigned char partition_id; + unsigned char block_number; + unsigned char transfer_length; + unsigned char flash_cmd; + unsigned char payload; +}; + +struct block_count { + unsigned short ui_firmware; + unsigned short ui_config; + unsigned short dp_config; + unsigned short fl_config; + unsigned short pm_config; + unsigned short bl_config; + unsigned short lockdown; + unsigned short guest_code; +}; + +struct physical_address { + unsigned short ui_firmware; + unsigned short ui_config; + unsigned short dp_config; + unsigned short guest_code; +}; + +struct container_descriptor { + unsigned char content_checksum[4]; + unsigned char container_id[2]; + unsigned char minor_version; + unsigned char major_version; + unsigned char reserved_08; + unsigned char reserved_09; + unsigned char reserved_0a; + unsigned char reserved_0b; + unsigned char container_option_flags[4]; + unsigned char content_options_length[4]; + unsigned char content_options_address[4]; + unsigned char content_length[4]; + unsigned char content_address[4]; +}; + +struct image_header_10 { + unsigned char checksum[4]; + unsigned char reserved_04; + unsigned char reserved_05; + unsigned char minor_header_version; + unsigned char major_header_version; + unsigned char reserved_08; + unsigned char reserved_09; + unsigned char reserved_0a; + unsigned char reserved_0b; + unsigned char top_level_container_start_addr[4]; +}; + +struct image_header_05_06 { + /* 0x00 - 0x0f */ + unsigned char checksum[4]; + unsigned char reserved_04; + unsigned char reserved_05; + unsigned char options_firmware_id:1; + unsigned char options_bootloader:1; + unsigned char options_guest_code:1; + unsigned char options_tddi:1; + unsigned char options_reserved:4; + unsigned char header_version; + unsigned char firmware_size[4]; + unsigned char config_size[4]; + /* 0x10 - 0x1f */ + unsigned char product_id[PRODUCT_ID_SIZE]; + unsigned char package_id[2]; + unsigned char package_id_revision[2]; + unsigned char product_info[PRODUCT_INFO_SIZE]; + /* 0x20 - 0x2f */ + unsigned char bootloader_addr[4]; + unsigned char bootloader_size[4]; + unsigned char ui_addr[4]; + unsigned char ui_size[4]; + /* 0x30 - 0x3f */ + unsigned char ds_id[16]; + /* 0x40 - 0x4f */ + union { + struct { + unsigned char cstmr_product_id[PRODUCT_ID_SIZE]; + unsigned char reserved_4a_4f[6]; + }; + struct { + unsigned char dsp_cfg_addr[4]; + unsigned char dsp_cfg_size[4]; + unsigned char reserved_48_4f[8]; + }; + }; + /* 0x50 - 0x53 */ + unsigned char firmware_id[4]; +}; + +struct block_data { + unsigned int size; + const unsigned char *data; +}; + +struct image_metadata { + bool contains_firmware_id; + bool contains_bootloader; + bool contains_disp_config; + bool contains_guest_code; + bool contains_flash_config; + unsigned int firmware_id; + unsigned int checksum; + unsigned int bootloader_size; + unsigned int disp_config_offset; + unsigned char bl_version; + unsigned char product_id[PRODUCT_ID_SIZE + 1]; + unsigned char cstmr_product_id[PRODUCT_ID_SIZE + 1]; + struct block_data bootloader; + struct block_data ui_firmware; + struct block_data ui_config; + struct block_data dp_config; + struct block_data fl_config; + struct block_data bl_config; + struct block_data guest_code; + struct block_data lockdown; + struct block_count blkcount; + struct physical_address phyaddr; +}; + +enum bl_version { + BL_V5 = 5, + BL_V6 = 6, + BL_V7 = 7, + BL_V8 = 8, +}; + +struct synaptics_rmi4_fwu_handle { + enum bl_version bl_version; + bool initialized; + bool in_bl_mode; + bool in_ub_mode; + bool force_update; + bool do_lockdown; + bool has_guest_code; + bool new_partition_table; + bool has_erase_all; + unsigned int data_pos; + unsigned char *ext_data_source; + unsigned char *read_config_buf; + unsigned char intr_mask; + unsigned char command; + unsigned char bootloader_id[2]; + unsigned char config_id[32]; + unsigned char flash_status; + unsigned char partitions; + unsigned short block_size; + unsigned short config_size; + unsigned short config_area; + unsigned short config_block_count; + unsigned short flash_config_length; + unsigned short payload_length; + unsigned short partition_table_bytes; + unsigned short read_config_buf_size; + const unsigned char *config_data; + const unsigned char *image; + unsigned char *image_name; + unsigned int image_size; + struct image_metadata img; + struct register_offset off; + struct block_count blkcount; + struct physical_address phyaddr; + struct f34_v5v6_flash_properties flash_properties; + struct synaptics_rmi4_fn_desc f34_fd; + struct synaptics_rmi4_fn_desc f35_fd; + struct device *dev; + struct work_struct fwu_work; + bool irq_enabled; + struct semaphore irq_sema; + struct wakeup_source flash_wake_src; + struct completion remove_complete; +}; + +struct image_header { + unsigned int checksum; + unsigned int image_size; + unsigned int config_size; + unsigned char options; + unsigned char bootloader_version; + unsigned char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1]; + unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE]; +}; + +static inline ssize_t synaptics_rmi4_show_error(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + dev_warn(&rmi4_data->i2c_client->dev, + "%s Attempted to read from write-only attribute %s\n", + __func__, attr->attr.name); + return -EPERM; +} + +static inline ssize_t synaptics_rmi4_store_error(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + dev_warn(&rmi4_data->i2c_client->dev, + "%s Attempted to write to read-only attribute %s\n", + __func__, attr->attr.name); + return -EPERM; +} + +struct time_keeping { + int id; + unsigned long long duration; +} __packed; + +struct statistics { + int active; /* current index within keeper array */ + int max; /* keeper array dimension */ + bool clk_run;/* flag to indicate whether clock is ticking */ + unsigned int flags; /* time keeping flags */ + unsigned char abbr; /* abbreviation letter */ + ktime_t start; /* time notch */ + struct time_keeping keeper[0]; +} __packed; + +enum { + FREQ_DURATION, /* scanning frequencies duration */ + FREQ_HOPPING, /* scanning frequencies hopping */ + MODE_NMS, /* noise mitigation mode */ + MODE_METAL_PLATE, /* metal plate detection */ + STATISTICS_MAX +}; + +struct synaptics_dsx_stats { + bool enabled; + ktime_t uptime; + bool uptime_run; + unsigned long long uptime_dur; + struct statistics *stats[STATISTICS_MAX]; +#define dur stats[FREQ_DURATION] +#define hop stats[FREQ_HOPPING] +#define nms stats[MODE_NMS] +#define mpd stats[MODE_METAL_PLATE] +} __packed; + +struct synaptics_rmi4_exp_fn_ptr { + int (*read)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr, + unsigned char *data, unsigned short length); + int (*write)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr, + unsigned char *data, unsigned short length); + int (*enable)(struct synaptics_rmi4_data *rmi4_data, bool enable); +}; + +void synaptics_rmi4_new_function(struct synaptics_rmi4_data *rmi4_data, + enum exp_fn fn_type, bool insert, + int (*func_init)(struct synaptics_rmi4_data *rmi4_data), + void (*func_remove)(struct synaptics_rmi4_data *rmi4_data), + void (*func_attn)(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask), + int (*func_status)(struct synaptics_rmi4_data *rmi4_data), + enum ic_modes mode); + +int synaptics_rmi4_scan_packet_reg_info( + struct synaptics_rmi4_data *rmi4_data, + unsigned short query_addr, + unsigned short regs_base_addr, + struct synaptics_rmi4_func_packet_regs *regs); + +int synaptics_rmi4_read_packet_reg( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *regs, int idx); + +int synaptics_rmi4_read_packet_regs( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *regs); + +int alloc_buffer(struct temp_buffer *tb, size_t count); + +static inline int secure_memcpy(unsigned char *dest, unsigned int dest_size, + const unsigned char *src, unsigned int src_size, + unsigned int count) +{ + if (dest == NULL || src == NULL) + return -EINVAL; + + if (count > dest_size || count > src_size) + return -EINVAL; + + memcpy((void *)dest, (const void *)src, count); + + return 0; +} + +static inline int synaptics_rmi4_reg_read( + struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, + unsigned char *data, + unsigned short len) +{ + return rmi4_data->i2c_read(rmi4_data, addr, data, len); +} + +static inline int synaptics_rmi4_reg_write( + struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, + unsigned char *data, + unsigned short len) +{ + return rmi4_data->i2c_write(rmi4_data, addr, data, len); +} + +extern int FPS_register_notifier(struct notifier_block *nb, + unsigned long stype, bool report); +extern int FPS_unregister_notifier(struct notifier_block *nb, + unsigned long stype); + +struct synaptics_rmi4_data *synaptics_driver_getdata( + struct synaptics_rmi4_data *prev); +#endif diff --git a/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_rmi_dev.c b/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_rmi_dev.c new file mode 100644 index 000000000000..13b4a07c12f9 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_rmi_dev.c @@ -0,0 +1,890 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "synaptics_dsx_i2c.h" + +#define CHAR_DEVICE_NAME "rmi" +#define DEVICE_CLASS_NAME "rmidev" +#define DEV_NUMBER 1 +#define MAX_INSTANCE 5 +#define REG_ADDR_LIMIT 0xFFFF + +static char *rmi_cdev_node(struct device *dev, umode_t *mode) +{ + if (mode) + *mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev)); +} + +static struct class rmi_device_class = { + .owner = THIS_MODULE, + .name = DEVICE_CLASS_NAME, + .devnode = rmi_cdev_node, +}; + +static int rmidev_major_num; + +static ssize_t rmidev_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t rmidev_sysfs_data_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t rmidev_sysfs_open_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_release_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_address_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_length_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_attn_state_show(struct device *dev, + struct device_attribute *attr, char *buf); + +struct rmidev_handle { + dev_t dev_no; + unsigned short address; + unsigned int length; + struct device dev; + struct synaptics_rmi4_data *rmi4_data; + struct synaptics_rmi4_exp_fn_ptr *fn_ptr; + struct completion remove_complete; + void *data; +}; + +struct rmidev_data { + int ref_count; + struct cdev main_dev; + struct class *device_class; + struct mutex file_mutex; + struct rmidev_handle *rmi_dev; + struct temp_buffer data_buf; +}; + +static const struct bin_attribute attr_data = { + .attr = { + .name = "data", + .mode = (S_IRUGO | S_IWUSR | S_IWGRP), + }, + .size = 0, + .read = rmidev_sysfs_data_show, + .write = rmidev_sysfs_data_store, +}; + +static const struct device_attribute attrs[] = { + __ATTR(open, S_IWUSR | S_IWGRP, + NULL, + rmidev_sysfs_open_store), + __ATTR(release, S_IWUSR | S_IWGRP, + NULL, + rmidev_sysfs_release_store), + __ATTR(address, S_IWUSR | S_IWGRP, + NULL, + rmidev_sysfs_address_store), + __ATTR(length, S_IWUSR | S_IWGRP, + NULL, + rmidev_sysfs_length_store), + __ATTR(attn_state, S_IRUSR | S_IRGRP, + rmidev_sysfs_attn_state_show, + NULL), +}; + +static ssize_t rmidev_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + struct rmidev_data *dev_data = data_file->private_data; + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + int retval; + unsigned int data_length = rmidev->length; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + if (data_length > (REG_ADDR_LIMIT - rmidev->address)) + data_length = REG_ADDR_LIMIT - rmidev->address; + + if (count < data_length) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Not enough space (%zu bytes) in buffer\n", + __func__, count); + return -EINVAL; + } + + if (data_length) { + retval = rmidev->fn_ptr->read(rmi4_data, + rmidev->address, + (unsigned char *)buf, + data_length); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read data\n", + __func__); + return retval; + } + } else { + return -EINVAL; + } + + return data_length; +} + +static ssize_t rmidev_sysfs_data_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + struct rmidev_data *dev_data = data_file->private_data; + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + int retval; + unsigned int data_length = rmidev->length; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + if (data_length > (REG_ADDR_LIMIT - rmidev->address)) + data_length = REG_ADDR_LIMIT - rmidev->address; + + if (data_length) { + retval = rmidev->fn_ptr->write(rmidev->rmi4_data, + rmidev->address, + (unsigned char *)buf, + data_length); + if (retval < 0) { + dev_err(&rmidev->rmi4_data->i2c_client->dev, + "%s: Failed to write data\n", + __func__); + return retval; + } + } else { + return -EINVAL; + } + + return count; +} + +static ssize_t rmidev_sysfs_open_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct rmidev_data *dev_data = + dev_get_drvdata(dev); + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + unsigned int input; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + rmidev->fn_ptr->enable(rmi4_data, false); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Attention interrupt disabled\n", + __func__); + + return count; +} + +static ssize_t rmidev_sysfs_release_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct rmidev_data *dev_data = + dev_get_drvdata(dev); + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + unsigned int input; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + rmidev->fn_ptr->enable(rmi4_data, true); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Attention interrupt enabled\n", + __func__); + + return count; +} + +static ssize_t rmidev_sysfs_address_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct rmidev_data *dev_data = + dev_get_drvdata(dev); + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + unsigned int input; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input > REG_ADDR_LIMIT) + return -EINVAL; + + rmidev->address = (unsigned short)input; + + return count; +} + +static ssize_t rmidev_sysfs_length_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct rmidev_data *dev_data = + dev_get_drvdata(dev); + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + unsigned int input; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input > REG_ADDR_LIMIT) + return -EINVAL; + + rmidev->length = input; + + return count; +} + +static ssize_t rmidev_sysfs_attn_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rmidev_data *dev_data = + dev_get_drvdata(dev); + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + int attn_state; + const struct synaptics_dsx_platform_data *platform_data = + &rmi4_data->board; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + attn_state = gpio_get_value(platform_data->irq_gpio); + + return snprintf(buf, PAGE_SIZE, "%u\n", attn_state); +} + +/* + * rmidev_llseek - used to set up register address + * + * @filp: file structure for seek + * @off: offset + * if whence == SEEK_SET, + * high 16 bits: page address + * low 16 bits: register address + * if whence == SEEK_CUR, + * offset from current position + * if whence == SEEK_END, + * offset from end position (0xFFFF) + * @whence: SEEK_SET, SEEK_CUR, or SEEK_END + */ +static loff_t rmidev_llseek(struct file *filp, loff_t off, int whence) +{ + loff_t newpos; + struct rmidev_data *dev_data = filp->private_data; + struct rmidev_handle *rmidev; + struct synaptics_rmi4_data *rmi4_data; + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + rmidev = dev_data->rmi_dev; + rmi4_data = rmidev->rmi4_data; + mutex_lock(&(dev_data->file_mutex)); + + switch (whence) { + case SEEK_SET: + newpos = off; + break; + case SEEK_CUR: + newpos = filp->f_pos + off; + break; + case SEEK_END: + newpos = REG_ADDR_LIMIT + off; + break; + default: + newpos = -EINVAL; + goto clean_up; + } + + if (newpos < 0 || newpos > REG_ADDR_LIMIT) { + dev_err(&rmidev->rmi4_data->i2c_client->dev, + "%s: New position 0x%04x is invalid\n", + __func__, (unsigned int)newpos); + newpos = -EINVAL; + goto clean_up; + } + + filp->f_pos = newpos; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: current position set %lld\n", + __func__, newpos); +clean_up: + mutex_unlock(&(dev_data->file_mutex)); + + return newpos; +} + +/* + * rmidev_read: - use to read data from rmi device + * + * @filp: file structure for read + * @buf: user space buffer pointer + * @count: number of bytes to read + * @f_pos: offset (starting register address) + */ +static ssize_t rmidev_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + ssize_t retval; + struct rmidev_data *dev_data = filp->private_data; + struct temp_buffer *tb; + struct rmidev_handle *rmidev; + struct synaptics_rmi4_data *rmi4_data; + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + rmidev = dev_data->rmi_dev; + rmi4_data = rmidev->rmi4_data; + mutex_lock(&(dev_data->file_mutex)); + + if (count > (REG_ADDR_LIMIT - *f_pos)) + count = REG_ADDR_LIMIT - *f_pos; + + if (count == 0) { + retval = 0; + goto clean_up; + } + + if (*f_pos > REG_ADDR_LIMIT) { + retval = -EFAULT; + goto clean_up; + } + + tb = &dev_data->data_buf; + + if (tb->buf_size < count && alloc_buffer(tb, count) != 0) { + retval = -ENOMEM; + goto clean_up; + } + + retval = rmidev->fn_ptr->read(rmidev->rmi4_data, + *f_pos, + tb->buf, + count); + if (retval < 0) + goto clean_up; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: read %zu bytes at offset %lld\n", + __func__, count, *f_pos); + + if (copy_to_user(buf, tb->buf, count)) + retval = -EFAULT; + else + *f_pos += retval; + +clean_up: + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +/* + * rmidev_write: - used to write data to rmi device + * + * @filep: file structure for write + * @buf: user space buffer pointer + * @count: number of bytes to write + * @f_pos: offset (starting register address) + */ +static ssize_t rmidev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + ssize_t retval; + struct rmidev_data *dev_data = filp->private_data; + struct temp_buffer *tb; + struct rmidev_handle *rmidev; + struct synaptics_rmi4_data *rmi4_data; + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + rmidev = dev_data->rmi_dev; + rmi4_data = rmidev->rmi4_data; + mutex_lock(&(dev_data->file_mutex)); + + if (*f_pos > REG_ADDR_LIMIT) { + retval = -EFAULT; + goto clean_up; + } + + if (count > (REG_ADDR_LIMIT - *f_pos)) + count = REG_ADDR_LIMIT - *f_pos; + + if (count == 0) { + retval = 0; + goto clean_up; + } + + tb = &dev_data->data_buf; + + if (tb->buf_size < count && alloc_buffer(tb, count) != 0) { + retval = -ENOMEM; + goto clean_up; + } + + if (copy_from_user(tb->buf, buf, count)) { + retval = -EFAULT; + goto clean_up; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: write %zu bytes at offset %lld\n", + __func__, count, *f_pos); + + retval = rmidev->fn_ptr->write(rmidev->rmi4_data, + *f_pos, + tb->buf, + count); + if (retval >= 0) + *f_pos += retval; + +clean_up: + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +/* + * rmidev_open: enable access to rmi device + * @inp: inode struture + * @filp: file structure + */ +static int rmidev_open(struct inode *inp, struct file *filp) +{ + int retval = 0; + struct rmidev_data *dev_data = + container_of(inp->i_cdev, struct rmidev_data, main_dev); + struct rmidev_handle *rmidev; + + if (!dev_data) + return -EACCES; + + rmidev = dev_data->rmi_dev; + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: device data %p\n", + __func__, dev_data); + + filp->private_data = dev_data; + mutex_lock(&(dev_data->file_mutex)); + + rmidev->fn_ptr->enable(rmidev->rmi4_data, false); + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: Attention interrupt disabled\n", + __func__); + + if (dev_data->ref_count < 1) + dev_data->ref_count++; + else + retval = -EACCES; + + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +/* + * rmidev_release: - release access to rmi device + * @inp: inode structure + * @filp: file structure + */ +static int rmidev_release(struct inode *inp, struct file *filp) +{ + struct rmidev_data *dev_data = + container_of(inp->i_cdev, struct rmidev_data, main_dev); + struct rmidev_handle *rmidev; + + if (!dev_data) + return -EACCES; + + rmidev = dev_data->rmi_dev; + + mutex_lock(&(dev_data->file_mutex)); + + dev_data->ref_count--; + if (dev_data->ref_count < 0) + dev_data->ref_count = 0; + + rmidev->fn_ptr->enable(rmidev->rmi4_data, true); + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: Attention interrupt enabled\n", + __func__); + + mutex_unlock(&(dev_data->file_mutex)); + + return 0; +} + +static const struct file_operations rmidev_fops = { + .owner = THIS_MODULE, + .llseek = rmidev_llseek, + .read = rmidev_read, + .write = rmidev_write, + .open = rmidev_open, + .release = rmidev_release, +}; + +static void rmidev_device_cleanup(struct rmidev_data *dev_data) +{ + if (dev_data) { + dev_t devno; + struct rmidev_handle *rmidev = dev_data->rmi_dev; + + devno = dev_data->main_dev.dev; + + if (dev_data->device_class) + device_destroy(dev_data->device_class, devno); + + cdev_del(&dev_data->main_dev); + + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: rmidev device removed\n", + __func__); + } + + return; +} + +static void rmidev_registered_release(struct device *dev) +{ + struct rmidev_handle *rmidev = + container_of(dev, struct rmidev_handle, dev); + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: rmidev release function called\n", + __func__); +} + +static int rmidev_init_device(struct synaptics_rmi4_data *rmi4_data) +{ + struct rmidev_handle *rmidev = + (struct rmidev_handle *)rmi4_data->rmidev_data; + int retval; + dev_t dev_no; + unsigned char attr_count; + struct rmidev_data *dev_data; + struct device *device_ptr; + + pr_debug("%s: enter rmi4_data %p\n", __func__, rmi4_data); + + rmidev->fn_ptr = kzalloc(sizeof(*(rmidev->fn_ptr)), GFP_KERNEL); + if (!rmidev) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for fn_ptr\n", + __func__); + retval = -ENOMEM; + goto err_fn_ptr; + } + + rmidev->fn_ptr->read = rmi4_data->i2c_read; + rmidev->fn_ptr->write = rmi4_data->i2c_write; + rmidev->fn_ptr->enable = rmi4_data->irq_enable; + rmidev->rmi4_data = rmi4_data; + + if (!rmidev_major_num) { + retval = alloc_chrdev_region(&dev_no, rmi4_data->instance, + MAX_INSTANCE, CHAR_DEVICE_NAME); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to allocate char device region %d\n", + __func__, retval); + goto err_device_region; + } + + rmidev_major_num = MAJOR(dev_no); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Major number of rmidev = %d\n", + __func__, rmidev_major_num); + } else + dev_no = MKDEV(rmidev_major_num, rmi4_data->instance); + + dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL); + if (!dev_data) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for dev_data\n", + __func__); + retval = -ENOMEM; + goto err_dev_data; + } + + mutex_init(&dev_data->file_mutex); + dev_data->rmi_dev = rmidev; + dev_data->device_class = &rmi_device_class; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Device data %p\n", __func__, dev_data); + rmidev->data = dev_data; + + cdev_init(&dev_data->main_dev, &rmidev_fops); + + retval = cdev_add(&dev_data->main_dev, dev_no, 1); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to add rmi char device\n", + __func__); + goto err_char_device; + } + + device_ptr = device_create(dev_data->device_class, NULL, dev_no, + NULL, CHAR_DEVICE_NAME"%d", MINOR(dev_no)); + if (IS_ERR(device_ptr)) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create rmi char device\n", + __func__); + retval = -ENODEV; + goto err_char_device; + } + + retval = gpio_export(rmi4_data->board.irq_gpio, false); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to export attention gpio\n", + __func__); + } else { + retval = gpio_export_link(&(rmi4_data->i2c_client->dev), + "attn", rmi4_data->board.irq_gpio); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s Failed to create gpio symlink\n", + __func__); + } else { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Exported attention gpio %d\n", + __func__, rmi4_data->board.irq_gpio); + } + } + + rmidev->dev.class = &rmi_device_class; + rmidev->dev.parent = &rmi4_data->i2c_client->dev; + rmidev->dev.release = rmidev_registered_release; + + dev_set_name(&rmidev->dev, DEVICE_CLASS_NAME "%d", rmi4_data->instance); + dev_set_drvdata(&rmidev->dev, dev_data); + + retval = device_register(&rmidev->dev); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to register device\n", + __func__); + goto err_sysfs_dir; + } + + retval = device_create_bin_file(&rmidev->dev, &attr_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs bin file\n", + __func__); + goto err_sysfs_bin; + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = device_create_file(&rmidev->dev, &attrs[attr_count]); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto err_sysfs_attrs; + } + } + + return 0; + +err_sysfs_attrs: + for (attr_count--; attr_count >= 0; attr_count--) + device_remove_file(&rmidev->dev, &attrs[attr_count]); + + device_remove_bin_file(&rmidev->dev, &attr_data); + +err_sysfs_bin: + device_unregister(&rmidev->dev); + +err_sysfs_dir: +err_char_device: + rmidev_device_cleanup(dev_data); + kfree(dev_data); + +err_dev_data: + unregister_chrdev_region(dev_no, MAX_INSTANCE); + +err_device_region: + kfree(rmidev->fn_ptr); + +err_fn_ptr: + return retval; +} + +static void rmidev_remove_device(struct synaptics_rmi4_data *rmi4_data) +{ + struct rmidev_handle *rmidev = + (struct rmidev_handle *)rmi4_data->rmidev_data; + unsigned char attr_count; + struct rmidev_data *dev_data; + + pr_debug("%s: rmi4_data %p\n", __func__, rmi4_data); + + if (!rmidev) + return; + + gpio_unexport(rmi4_data->board.irq_gpio); + + sysfs_remove_link(&rmi4_data->i2c_client->dev.kobj, "attn"); + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) + device_remove_file(&rmidev->dev, &attrs[attr_count]); + + device_remove_bin_file(&rmidev->dev, &attr_data); + + device_unregister(&rmidev->dev); + + dev_data = rmidev->data; + if (dev_data) { + rmidev_device_cleanup(dev_data); + kfree(dev_data); + } + + unregister_chrdev_region(rmidev->dev_no, 1); + + kfree(rmidev->fn_ptr); + + complete(&rmidev->remove_complete); + + return; +} + +static int __init rmidev_module_init(void) +{ + int err; + struct rmidev_handle *rmidev; + struct synaptics_rmi4_data *next = NULL; + + err = class_register(&rmi_device_class); + if (err) { + pr_err("%s: couldn't register rmi device class\n", __func__); + return err; + } + + while ((next = synaptics_driver_getdata(next)) != NULL) { + rmidev = kzalloc(sizeof(*rmidev), GFP_KERNEL); + if (!rmidev) { + dev_err(&next->i2c_client->dev, + "%s: cannot allocate rmidev for rmi4_data %p\n", + __func__, next); + continue; + } + dev_dbg(&next->i2c_client->dev, + "%s: new rmidev %p\n", __func__, rmidev); + next->rmidev_data = (void *)rmidev; + synaptics_rmi4_new_function(next, RMI_DEV, true, + rmidev_init_device, + rmidev_remove_device, + NULL, NULL, IC_MODE_UI); + } + + return 0; +} + +static void __exit rmidev_module_exit(void) +{ + struct rmidev_handle *rmidev; + struct synaptics_rmi4_data *next = NULL; + + while ((next = synaptics_driver_getdata(next)) != NULL) { + rmidev = (struct rmidev_handle *)next->rmidev_data; + dev_dbg(&next->i2c_client->dev, + "%s: removing rmidev %p\n", __func__, rmidev); + init_completion(&rmidev->remove_complete); + synaptics_rmi4_new_function(next, RMI_DEV, false, + rmidev_init_device, + rmidev_remove_device, + NULL, NULL, IC_MODE_UI); + wait_for_completion(&rmidev->remove_complete); + kfree(rmidev); + next->rmidev_data = NULL; + } + + unregister_chrdev_region(MKDEV(rmidev_major_num, 1), MAX_INSTANCE); + class_unregister(&rmi_device_class); + + return; +} + +module_init(rmidev_module_init); +module_exit(rmidev_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX RMI Dev Module"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(SYNAPTICS_DSX_DRIVER_VERSION); diff --git a/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_test_reporting.c b/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_test_reporting.c new file mode 100644 index 000000000000..0527a84e6ff1 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi/synaptics_dsx_test_reporting.c @@ -0,0 +1,5979 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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 pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_i2c.h" + +#if 0 +#ifdef pr_debug +#undef pr_debug +#define pr_debug pr_err +#endif +#ifdef dev_dbg +#undef dev_dbg +#define dev_dbg dev_err +#endif +#endif + +#undef WATCHDOG_HRTIMER +#define WATCHDOG_TIMEOUT_S 1 +#define STATUS_WORK_INTERVAL 20 /* ms */ + +/* enable for report status polling */ +#define REPORT_STATUS_POLLING + +/* +#define RAW_HEX +#define HUMAN_READABLE +*/ + +#define STATUS_IDLE 0 +#define STATUS_BUSY 1 +#define STATUS_ERROR 2 + +#define DATA_REPORT_INDEX_OFFSET 1 +#define DATA_REPORT_DATA_OFFSET 3 + +#define COMMAND_GET_REPORT 1 +#define COMMAND_FORCE_CAL 2 +#define COMMAND_FORCE_UPDATE 4 +#define COMMAND_ENTER_IN_CELL_TESTMODE 16 + +#define HIGH_RESISTANCE_DATA_SIZE 6 +#define FULL_RAW_CAP_MIN_MAX_DATA_SIZE 4 +#define TREX_DATA_SIZE 7 + +#define NO_AUTO_CAL_MASK 0x01 + +struct synaptics_rmi4_f54_handle; + +static inline ssize_t synaptics_dsx_test_reporting_show_error( + struct synaptics_rmi4_f54_handle *f54, char *buf); +static inline ssize_t synaptics_dsx_test_reporting_store_error( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count); + +struct f54_attribute { + struct attribute attr; + ssize_t (*show)(struct synaptics_rmi4_f54_handle *f54, char *buf); + ssize_t (*store)(struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count); +}; + +#define concat(a, b) a##b + +#define GROUP(_attrs) {\ + .attrs = _attrs,\ +} + +#define attrify(propname) (&f54_attr_##propname.attr) + +#define show_prototype_ext(propname, perm)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ + struct synaptics_rmi4_f54_handle *f54, char *buf);\ +\ +static struct f54_attribute f54_attr_##propname =\ + __ATTR(propname, (perm),\ + concat(synaptics_rmi4_f54, _##propname##_show),\ + synaptics_dsx_test_reporting_store_error); + +#define show_prototype(propname)\ + show_prototype_ext(propname, S_IRUSR | S_IRGRP | S_IROTH) + +#define store_prototype(propname)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_store)(\ + struct synaptics_rmi4_f54_handle *f54,\ + const char *buf, size_t count);\ +\ +static struct f54_attribute f54_attr_##propname =\ + __ATTR(propname, S_IWUSR | S_IWGRP,\ + synaptics_dsx_test_reporting_show_error,\ + concat(synaptics_rmi4_f54, _##propname##_store)); + +#define show_store_prototype_ext(propname, perm)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ + struct synaptics_rmi4_f54_handle *f54, char *buf);\ +\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_store)(\ + struct synaptics_rmi4_f54_handle *f54,\ + const char *buf, size_t count);\ +\ +static struct f54_attribute f54_attr_##propname =\ + __ATTR(propname, (perm),\ + concat(synaptics_rmi4_f54, _##propname##_show),\ + concat(synaptics_rmi4_f54, _##propname##_store)); + +#define show_store_prototype(propname)\ + show_store_prototype_ext(propname, \ + S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP) + +#define simple_show_func(rtype, propname, fmt)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ + struct synaptics_rmi4_f54_handle *f54, char *buf)\ +{\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: stored value " #rtype "." #propname "\n", __func__);\ + return snprintf(buf, PAGE_SIZE, fmt, f54->rtype.propname);\ +} \ + +#define simple_show_func_unsigned(rtype, propname)\ +simple_show_func(rtype, propname, "%u\n") + +#define show_func(rtype, rgrp, propname, fmt)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ + struct synaptics_rmi4_f54_handle *f54, char *buf)\ +{\ + int retval;\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: read value " #rtype "." #rgrp "." #propname " at addr=0x%x\n",\ + __func__, f54->rtype.rgrp->address);\ +\ + mutex_lock(&f54->rtype##_mutex);\ +\ + retval = f54->fn_ptr->read(rmi4_data,\ + f54->rtype.rgrp->address,\ + f54->rtype.rgrp->data,\ + sizeof(f54->rtype.rgrp->data));\ + mutex_unlock(&f54->rtype##_mutex);\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read " #rtype\ + " " #rgrp "\n",\ + __func__);\ + return retval;\ + } \ +\ + return snprintf(buf, PAGE_SIZE, fmt,\ + f54->rtype.rgrp->propname);\ +} \ + +#define show_func_unsigned(rtype, rgrp, propname)\ +show_func(rtype, rgrp, propname, "%u\n") + +#define show_store_func(rtype, rgrp, propname, fmt)\ +show_func(rtype, rgrp, propname, fmt)\ +\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_store)(\ + struct synaptics_rmi4_f54_handle *f54,\ + const char *buf, size_t count)\ +{\ + int retval;\ + unsigned long setting;\ + unsigned long o_setting;\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ +\ + retval = sstrtoul(buf, 10, &setting);\ + if (retval)\ + return retval;\ +\ + mutex_lock(&f54->rtype##_mutex);\ + retval = f54->fn_ptr->read(rmi4_data,\ + f54->rtype.rgrp->address,\ + f54->rtype.rgrp->data,\ + sizeof(f54->rtype.rgrp->data));\ + if (retval < 0) {\ + mutex_unlock(&f54->rtype##_mutex);\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read " #rtype\ + " " #rgrp "\n",\ + __func__);\ + return retval;\ + } \ +\ + if (f54->rtype.rgrp->propname == setting) {\ + mutex_unlock(&f54->rtype##_mutex);\ + return count;\ + } \ +\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: write value %ld " #rtype "." #rgrp "." #propname " at addr=0x%x\n",\ + __func__, setting, f54->rtype.rgrp->address);\ +\ + o_setting = f54->rtype.rgrp->propname;\ + f54->rtype.rgrp->propname = setting;\ +\ + retval = f54->fn_ptr->write(rmi4_data,\ + f54->rtype.rgrp->address,\ + f54->rtype.rgrp->data,\ + sizeof(f54->rtype.rgrp->data));\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to write " #rtype\ + " " #rgrp "\n",\ + __func__);\ + f54->rtype.rgrp->propname = o_setting;\ + mutex_unlock(&f54->rtype##_mutex);\ + return retval;\ + } \ +\ + mutex_unlock(&f54->rtype##_mutex);\ + return count;\ +} \ + +#define show_store_func_unsigned(rtype, rgrp, propname)\ +show_store_func(rtype, rgrp, propname, "%u\n") + +#define show_subpkt_func(rtype, rgrp, subpkt, propname, fmt)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ + struct synaptics_rmi4_f54_handle *f54, char *buf)\ +{\ + int retval;\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: read value " #rtype "." #rgrp "." #propname "." #subpkt " at addr=0x%x\n",\ + __func__, f54->rtype.rgrp->address);\ +\ + mutex_lock(&f54->rtype##_mutex);\ +\ + retval = f54->fn_ptr->read(rmi4_data,\ + f54->rtype.rgrp->address,\ + f54->rtype.rgrp->data,\ + f54->rtype.rgrp->size);\ + mutex_unlock(&f54->rtype##_mutex);\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read " #rtype\ + " " #rgrp #subpkt"\n",\ + __func__);\ + return retval;\ + } \ +\ + return snprintf(buf, PAGE_SIZE, fmt,\ + f54->rtype.rgrp->sp##subpkt->propname);\ +} \ + +#define show_subpkt_func_unsigned(rtype, rgrp, subpkt, propname)\ +show_subpkt_func(rtype, rgrp, subpkt, propname, "%u\n") + +#define show_store_subpkt_func(rtype, rgrp, subpkt, propname, fmt)\ +show_subpkt_func(rtype, rgrp, subpkt, propname, fmt)\ +\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_store)(\ + struct synaptics_rmi4_f54_handle *f54,\ + const char *buf, size_t count)\ +{\ + int retval;\ + unsigned long setting;\ + unsigned long o_setting;\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: read then write value " #rtype "." #rgrp "." #propname "." #subpkt " at addr=0x%x\n",\ + __func__, f54->rtype.rgrp->address);\ +\ + retval = sstrtoul(buf, 10, &setting);\ + if (retval)\ + return retval;\ +\ + mutex_lock(&f54->rtype##_mutex);\ + retval = f54->fn_ptr->read(rmi4_data,\ + f54->rtype.rgrp->address,\ + f54->rtype.rgrp->data,\ + f54->rtype.rgrp->size);\ + if (retval < 0) {\ + mutex_unlock(&f54->rtype##_mutex);\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read " #rtype\ + " " #rgrp #subpkt"\n",\ + __func__);\ + return retval;\ + } \ +\ + if (f54->rtype.rgrp->sp##subpkt->propname == setting) {\ + mutex_unlock(&f54->rtype##_mutex);\ + return count;\ + } \ +\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: write value %ld " #rtype "." #rgrp "." #propname " at addr=0x%x\n",\ + __func__, setting, f54->rtype.rgrp->address);\ +\ + o_setting = f54->rtype.rgrp->sp##subpkt->propname;\ + f54->rtype.rgrp->sp##subpkt->propname = setting;\ +\ + retval = f54->fn_ptr->write(rmi4_data,\ + f54->rtype.rgrp->address,\ + f54->rtype.rgrp->data,\ + f54->rtype.rgrp->size);\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to write " #rtype\ + " " #rgrp #subpkt"\n",\ + __func__);\ + f54->rtype.rgrp->sp##subpkt->propname = o_setting;\ + mutex_unlock(&f54->rtype##_mutex);\ + return retval;\ + } \ +\ + mutex_unlock(&f54->rtype##_mutex);\ + return count;\ +} \ + +#define show_store_subpkt_func_unsigned(rtype, rgrp, subpkt, propname)\ +show_store_subpkt_func(rtype, rgrp, subpkt, propname, "%u\n") + +#define show_replicated_func(rtype, rgrp, propname, fmt)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ + struct synaptics_rmi4_f54_handle *f54, char *buf)\ +{\ + int retval;\ + int size = 0;\ + unsigned char ii;\ + unsigned char length;\ + unsigned char element_count;\ + unsigned char *temp;\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: read value " #rtype "." #rgrp "." #propname " at addr=0x%x\n",\ + __func__, f54->rtype.rgrp->address);\ +\ + mutex_lock(&f54->rtype##_mutex);\ +\ + length = f54->rtype.rgrp->length;\ + element_count = length / sizeof(*f54->rtype.rgrp->data);\ +\ + retval = f54->fn_ptr->read(rmi4_data,\ + f54->rtype.rgrp->address,\ + (unsigned char *)f54->rtype.rgrp->data,\ + length);\ + mutex_unlock(&f54->rtype##_mutex);\ + if (retval < 0) {\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read " #rtype\ + " " #rgrp "\n",\ + __func__);\ + } \ +\ + temp = buf;\ +\ + for (ii = 0; ii < element_count; ii++) {\ + retval = snprintf(temp, PAGE_SIZE - size, fmt " ",\ + f54->rtype.rgrp->data[ii].propname);\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to write output\n",\ + __func__);\ + return retval;\ + } \ + size += retval;\ + temp += retval;\ + } \ +\ + retval = snprintf(temp, PAGE_SIZE - size, "\n");\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Faild to write null terminator\n",\ + __func__);\ + return retval;\ + } \ +\ + return size + retval;\ +} \ + +#define show_replicated_func_unsigned(rtype, rgrp, propname)\ +show_replicated_func(rtype, rgrp, propname, "%u") + +#define show_store_replicated_func(rtype, rgrp, propname, fmt)\ +show_replicated_func(rtype, rgrp, propname, fmt)\ +\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_store)(\ + struct synaptics_rmi4_f54_handle *f54,\ + const char *buf, size_t count)\ +{\ + int retval;\ + unsigned int setting;\ + unsigned char ii;\ + unsigned char length;\ + unsigned char element_count;\ + const unsigned char *temp;\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: read then write value " #rtype "." #rgrp "." #propname " at addr=0x%x\n",\ + __func__, f54->rtype.rgrp->address);\ +\ + mutex_lock(&f54->rtype##_mutex);\ +\ + length = f54->rtype.rgrp->length;\ + element_count = length / sizeof(*f54->rtype.rgrp->data);\ +\ + retval = f54->fn_ptr->read(rmi4_data,\ + f54->rtype.rgrp->address,\ + (unsigned char *)f54->rtype.rgrp->data,\ + length);\ + if (retval < 0) {\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read " #rtype\ + " " #rgrp "\n",\ + __func__);\ + } \ +\ + temp = buf;\ +\ + for (ii = 0; ii < element_count; ii++) {\ + if (sscanf(temp, fmt, &setting) == 1) {\ + f54->rtype.rgrp->data[ii].propname = setting;\ + } else {\ + mutex_unlock(&f54->rtype##_mutex);\ + return -EINVAL;\ + } \ +\ + while (*temp != 0) {\ + temp++;\ + if (isspace(*(temp - 1)) && !isspace(*temp))\ + break;\ + } \ + } \ +\ + retval = f54->fn_ptr->write(rmi4_data,\ + f54->rtype.rgrp->address,\ + (unsigned char *)f54->rtype.rgrp->data,\ + length);\ + mutex_unlock(&f54->rtype##_mutex);\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to write " #rtype\ + " " #rgrp "\n",\ + __func__);\ + return retval;\ + } \ +\ + return count;\ +} \ + +#define show_store_replicated_func_unsigned(rtype, rgrp, propname)\ +show_store_replicated_func(rtype, rgrp, propname, "%u") + +#define DATA_REG_ADD(reg, skip, cond) \ + do { if (cond) { \ + attrs_data_regs_exist[reg_num] = true;\ + data->reg_##reg = kzalloc(sizeof(*data->reg_##reg),\ + GFP_KERNEL);\ + if (!data->reg_##reg)\ + goto exit_no_mem;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: d%s addr = 0x%02x added (%d)\n",\ + __func__, #reg, reg_addr, reg_num);\ + data->reg_##reg->address = reg_addr;\ + reg_addr += skip;\ + } \ + reg_num++;\ + } while (0) + +#define DATA_REG_PRESENCE(reg, skip, cond) \ + do { if (cond) { \ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: d%s addr = 0x%02x\n",\ + __func__, #reg, reg_addr);\ + reg_addr += skip;\ + } \ + } while (0) + +#define CTRL_REG_ADD(reg, skip, cond) \ + do { if (cond) { \ + attrs_ctrl_regs_exist[reg_num] = true;\ + control->reg_##reg = kzalloc(sizeof(*control->reg_##reg),\ + GFP_KERNEL);\ + if (!control->reg_##reg)\ + goto exit_no_mem;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: c%s addr = 0x%02x added (%d)\n",\ + __func__, #reg, reg_addr, reg_num);\ + control->reg_##reg->address = reg_addr | PAGE_SELECT_ENFORCE;\ + reg_addr += skip;\ + } \ + reg_num++;\ + } while (0) + +#define CTRL_REG_ADD_EXT(reg, skip, cond, size) \ + do { if (cond) { \ + attrs_ctrl_regs_exist[reg_num] = true;\ + control->reg_##reg = kzalloc(sizeof(*control->reg_##reg),\ + GFP_KERNEL);\ + if (!control->reg_##reg)\ + goto exit_no_mem;\ + control->reg_##reg->data = kzalloc(size, GFP_KERNEL);\ + if (!control->reg_##reg->data)\ + goto exit_no_mem;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: c%s addr = 0x%02x size = %zu added (%d)\n",\ + __func__, #reg, reg_addr, size, reg_num);\ + control->reg_##reg->length = size;\ + control->reg_##reg->address = reg_addr | PAGE_SELECT_ENFORCE;\ + reg_addr += skip;\ + } \ + reg_num++;\ + } while (0) + +#define CTRL_SUBPKT_SZ(reg, subpkt) \ + sizeof(*((struct f54_control_##reg *)0)->sp##subpkt) + +#define CTRL_PKT_REG_START(reg, max_size) \ + do {\ + control->reg_##reg = kzalloc(sizeof(*control->reg_##reg),\ + GFP_KERNEL);\ + if (!control->reg_##reg)\ + goto exit_no_mem;\ + control->reg_##reg->data = kzalloc(max_size,\ + GFP_KERNEL);\ + if (!control->reg_##reg->data)\ + goto exit_no_mem;\ + } while (0) + +#define CTRL_SUBPKT_ADD(reg, subpkt, cond) \ + do { if (cond) { \ + attrs_ctrl_regs_exist[reg_num] = true; \ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: subpkt c%s_%d added (%d)\n", \ + __func__, #reg, subpkt, reg_num); \ + control->reg_##reg->sp##subpkt = \ + (void *)control->reg_##reg->data + \ + control->reg_##reg->size; \ + control->reg_##reg->size += \ + sizeof(*control->reg_##reg->sp##subpkt); \ + } \ + reg_num++;\ + } while (0) + +#define CTRL_PKT_REG_END(reg, skip) \ + do { if (control->reg_##reg->size) { \ + control->reg_##reg->address = reg_addr; \ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: c%s addr = 0x%02x\n", \ + __func__, #reg, reg_addr); \ + reg_addr += skip; \ + } \ + else { \ + kfree(control->reg_##reg->data); \ + kfree(control->reg_##reg); \ + } } while (0) + +#define CTRL_REG_PRESENCE(reg, skip, cond) \ + do { if ((cond)) {\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: c%s addr = 0x%02x\n",\ + __func__, #reg, reg_addr);\ + reg_addr += (skip);\ + } } while (0) + +#define CTRL_REG_RESERVED_PRESENCE(reg, skip, cond) \ + do { if ((cond)) { \ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: c%s addr = 0x%02x (reserved)\n",\ + __func__, #reg, reg_addr);\ + reg_addr += skip;\ + } } while (0) + +#define QUERY_REG_READ(reg, cond)\ + do { if (cond) { \ + retval = f54->fn_ptr->read(rmi4_data,\ + reg_addr,\ + f54->query##reg.data,\ + sizeof(f54->query##reg.data));\ + if (retval < 0) { \ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read query %s register\n",\ + #reg, __func__);\ + goto err;\ + } \ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: q%s addr = 0x%02x, val = 0x%02x\n",\ + __func__, #reg, reg_addr, f54->query##reg.data[0]);\ + reg_addr += 1;\ + } \ + else { \ + memset(&f54->query##reg.data, 0, sizeof(f54->query##reg.data));\ + } } while (0) + +enum f54_report_types { + F54_8BIT_IMAGE = 1, + F54_16BIT_IMAGE = 2, + F54_RAW_16BIT_IMAGE = 3, + F54_HIGH_RESISTANCE = 4, + F54_TX_TO_TX_SHORT = 5, + F54_RX_TO_RX1 = 7, + F54_TRUE_BASELINE = 9, + F54_FULL_RAW_CAP_MIN_MAX = 13, + F54_RX_OPENS1 = 14, + F54_TX_OPEN = 15, + F54_TX_TO_GROUND = 16, + F54_RX_TO_RX2 = 17, + F54_RX_OPENS2 = 18, + F54_FULL_RAW_CAP = 19, + F54_FULL_RAW_CAP_RX_COUPLING_COMP = 20, + F54_SENSOR_SPEED = 22, + F54_ADC_RANGE = 23, + F54_TREX_OPENS = 24, + F54_TREX_TO_GND = 25, + F54_TREX_SHORTS = 26, + F54_HYB_ABS_RX_TX_DELTA = 59, + F54_HYB_ABS_RX_TX_RAW = 63, + INVALID_REPORT_TYPE = -1, +}; + +struct f54_query { + union { + struct { + /* query 0 */ + unsigned char num_of_rx_electrodes; + + /* query 1 */ + unsigned char num_of_tx_electrodes; + + /* query 2 */ + unsigned char f54_query2_b0__1:2; + unsigned char has_baseline:1; + unsigned char has_image8:1; + unsigned char f54_query2_b4__5:2; + unsigned char has_image16:1; + unsigned char f54_query2_b7:1; + + /* queries 3.0 and 3.1 */ + unsigned short clock_rate; + + /* query 4 */ + unsigned char touch_controller_family; + + /* query 5 */ + unsigned char has_pixel_touch_threshold_adjustment:1; + unsigned char f54_query5_b1__7:7; + + /* query 6 */ + unsigned char has_sensor_assignment:1; + unsigned char has_interference_metric:1; + unsigned char has_sense_frequency_control:1; + unsigned char has_firmware_noise_mitigation:1; + unsigned char has_ctrl11:1; + unsigned char has_two_byte_report_rate:1; + unsigned char has_one_byte_report_rate:1; + unsigned char has_relaxation_control:1; + + /* query 7 */ + unsigned char curve_compensation_mode:2; + unsigned char f54_query7_b2__7:6; + + /* query 8 */ + unsigned char f54_query8_b0:1; + unsigned char has_iir_filter:1; + unsigned char has_cmn_removal:1; + unsigned char has_cmn_maximum:1; + unsigned char has_touch_hysteresis:1; + unsigned char has_edge_compensation:1; + unsigned char has_per_frequency_noise_control:1; + unsigned char has_enhanced_stretch:1; + + /* query 9 */ + unsigned char has_force_fast_relaxation:1; + unsigned char has_multi_metric_state_machine:1; + unsigned char has_signal_clarity:1; + unsigned char has_variance_metric:1; + unsigned char has_0d_relaxation_control:1; + unsigned char has_0d_acquisition_control:1; + unsigned char has_status:1; + unsigned char has_slew_metric:1; + + /* query 10 */ + unsigned char has_h_blank:1; + unsigned char has_v_blank:1; + unsigned char has_long_h_blank:1; + unsigned char has_startup_fast_relaxation:1; + unsigned char has_esd_control:1; + unsigned char has_noise_mitigation2:1; + unsigned char has_noise_state:1; + unsigned char has_energy_ratio_relaxation:1; + + /* query 11 */ + unsigned char has_excessive_noise_reporting:1; + unsigned char has_slew_option:1; + unsigned char has_two_overhead_bursts:1; + unsigned char has_query13:1; + unsigned char has_one_overhead_burst:1; + unsigned char f54_query11_b5:1; + unsigned char has_ctrl88:1; + unsigned char has_query15:1; + } __packed; + unsigned char data[13]; + }; +}; + +struct f54_query12 { + union { + struct { + unsigned char number_of_sensing_frequencies:4; + unsigned char f54_query12_b4__7:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query13 { + union { + struct { + unsigned char has_ctrl86:1; + unsigned char has_ctrl87:1; + unsigned char has_ctrl87_sub0:1; + unsigned char has_ctrl87_sub1:1; + unsigned char has_ctrl87_sub2:1; + unsigned char has_cid_im:1; + unsigned char has_noise_mitigation_enh:1; + unsigned char has_rail_im:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query14 { + union { + struct { + unsigned char size_of_ctr87; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query15 { + union { + struct { + unsigned char has_ctrl90:1; + unsigned char has_traismit_strength:1; + unsigned char has_ctrl87_sub3:1; + unsigned char has_query16:1; + unsigned char has_query20:1; + unsigned char has_query21:1; + unsigned char has_query22:1; + unsigned char has_query25:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query16 { + union { + struct { + unsigned char has_query17:1; + unsigned char has_data17:1; + unsigned char has_ctrl92:1; + unsigned char has_ctrl93:1; + unsigned char has_ctrl94_query18:1; + unsigned char has_ctrl95_query19:1; + unsigned char has_ctrl99:1; + unsigned char has_ctrl100:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query17 { + union { + struct { + unsigned char q17_num_of_sense_freqs; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query18 { + union { + struct { + unsigned char has_ctrl94_sub0:1; + unsigned char has_ctrl94_sub1:1; + unsigned char has_ctrl94_sub2:1; + unsigned char has_ctrl94_sub3:1; + unsigned char has_ctrl94_sub4:1; + unsigned char has_ctrl94_sub5:1; + unsigned char has_ctrl94_sub6:1; + unsigned char has_ctrl94_sub7:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query19 { + union { + struct { + unsigned char size_of_ctrl95; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query20 { + union { + struct { + unsigned char adc_clock_divisor; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query21 { + union { + struct { + unsigned char has_abs_rx:1; + unsigned char has_abs_tx:1; + unsigned char has_ctrl91:1; + unsigned char has_ctrl96:1; + unsigned char has_ctrl97:1; + unsigned char has_ctrl98:1; + unsigned char has_data19:1; + unsigned char has_query24_data18:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query22 { + union { + struct { + unsigned char has_packed_image:1; + unsigned char has_ctrl101:1; + unsigned char has_dynamic_sense_display_ratio:1; + unsigned char has_query23:1; + unsigned char has_ctrl103_query26:1; + unsigned char has_ctrl104:1; + unsigned char has_ctrl105:1; + unsigned char has_query28:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query23 { + union { + struct { + unsigned char has_ctrl102:1; + unsigned char has_ctrl102_sub1:1; + unsigned char has_ctrl102_sub2:1; + unsigned char has_ctrl102_sub4:1; + unsigned char has_ctrl102_sub5:1; + unsigned char has_ctrl102_sub9:1; + unsigned char has_ctrl102_sub10:1; + unsigned char has_ctrl102_sub11:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query24 { + union { + struct { + unsigned char size_of_data18; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query25 { + union { + struct { + unsigned char has_ctrl106:1; + unsigned char has_ctrl102_sub12:1; + unsigned char has_ctrl107:1; + unsigned char has_ctrl108:1; + unsigned char has_ctrl109:1; + unsigned char has_data20:1; + unsigned char has_tags_for_moisture:1; + unsigned char has_query27:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query26 { + union { + struct { + unsigned char has_ctrl103_sub0:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query27 { + union { + struct { + unsigned char has_ctrl110:1; + unsigned char has_data21:1; + unsigned char has_ctrl111:1; + unsigned char has_ctrl112:1; + unsigned char has_ctrl113:1; + unsigned char has_data22:1; + unsigned char has_ctrl114:1; + unsigned char has_query29:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query28 { + union { + struct { + unsigned char has_capacitance_correction; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query29 { + union { + struct { + unsigned char has_ctrl115:1; + unsigned char has_ground_ring_options:1; + unsigned char has_lost_burst_tuning:1; + unsigned char has_aux_exvcom2_select:1; + unsigned char has_ctrl116:1; + unsigned char has_data23:1; + unsigned char has_ctrl117:1; + unsigned char has_query30:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query30 { + union { + struct { + unsigned char has_ctrl118:1; + unsigned char has_ctrl119:1; + unsigned char has_ctrl120:1; + unsigned char has_ctrl121:1; + unsigned char has_ctrl122_query31:1; + unsigned char has_ctrl123:1; + unsigned char has_ctrl124:1; + unsigned char has_query32:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query31 { + union { + struct { + unsigned char num_of_active_stylus_sensing_freqs; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query32 { + union { + struct { + unsigned char has_ctrl125:1; + unsigned char has_ctrl126:1; + unsigned char has_ctrl127:1; + unsigned char has_abs_charge_pump_disable:1; + unsigned char has_query33:1; + unsigned char has_data24:1; + unsigned char has_query34:1; + unsigned char has_query35:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query33 { + union { + struct { + unsigned char has_ctrl128:1; + unsigned char has_ctrl129:1; + unsigned char has_ctrl130:1; + unsigned char has_ctrl131:1; + unsigned char has_ctrl132:1; + unsigned char has_ctrl133:1; + unsigned char has_ctrl134:1; + unsigned char has_query36:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query34 { + union { + struct { + unsigned char max_fnm_sliding_window_width; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query35 { + union { + struct { + unsigned char has_data25:1; + unsigned char has_ctrl135:1; + unsigned char has_ctrl136:1; + unsigned char has_ctrl137:1; + unsigned char has_ctrl138:1; + unsigned char has_ctrl139:1; + unsigned char has_data26:1; + unsigned char has_ctrl140:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query36 { + union { + struct { + unsigned char has_ctrl141:1; + unsigned char has_ctrl142:1; + unsigned char has_query37:1; + unsigned char has_ctrl143:1; + unsigned char has_ctrl144:1; + unsigned char has_ctrl145:1; + unsigned char has_ctrl146:1; + unsigned char has_query38:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query38 { + union { + struct { + unsigned char has_ctrl147:1; + unsigned char has_ctrl148:1; + unsigned char has_ctrl149:1; + unsigned char has_ctrl150:1; + unsigned char has_ctrl151:1; + unsigned char has_ctrl152:1; + unsigned char has_ctrl153:1; + unsigned char has_query39:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query39 { + union { + struct { + unsigned char has_ctrl154:1; + unsigned char has_ctrl155:1; + unsigned char has_ctrl156:1; + unsigned char has_ctrl160:1; + unsigned char has_ctrl157_158:1; + unsigned char f54_q39_b5b6:2; + unsigned char has_query40:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query40 { + union { + struct { + unsigned char has_ctrl169:1; + unsigned char has_ctrl163_query41:1; + unsigned char f54_q40_b2:1; + unsigned char has_ctrl165_query42:1; + unsigned char has_ctrl166:1; + unsigned char has_ctrl167:1; + unsigned char has_ctrl168:1; + unsigned char has_query43:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query41 { + union { + struct { + unsigned char size_of_ctrl163; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query42 { + union { + struct { + unsigned char size_of_ctrl165; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query43 { + union { + struct { + unsigned char f54_q43_b0b1:2; + unsigned char has_ctrl171:1; + unsigned char has_ctrl172_query44_query45:1; + unsigned char has_ctrl173:1; + unsigned char has_ctrl174:1; + unsigned char has_ctrl175:1; + unsigned char has_query46:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query46 { + union { + struct { + unsigned char has_ctrl176:1; + unsigned char has_ctrl177_178:1; + unsigned char has_ctrl179:1; + unsigned char f54_q46_b3:1; + unsigned char has_data27:1; + unsigned char has_data28:1; + unsigned char f54_q46_b6:1; + unsigned char has_query47:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query47 { + union { + struct { + unsigned char f54_q47_b0:1; + unsigned char has_ctrl182:1; + unsigned char has_ctrl183:1; + unsigned char f54_q47_b3:1; + unsigned char has_ctrl185:1; + unsigned char has_ctrl186:1; + unsigned char has_ctrl187:1; + unsigned char has_query49:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query49 { + union { + struct { + unsigned char f54_q49_b0:1; + unsigned char has_data30:1; + unsigned char has_ctrl188:1; + unsigned char has_data31:1; + unsigned char f54_q49_b4b5:2; + unsigned char has_ctrl190:1; + unsigned char has_query50:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query50 { + union { + struct { + unsigned char f54_q50_b0b1:2; + unsigned char has_ctrl193:1; + unsigned char has_query52:1; + unsigned char f54_q50_b4b6:3; + unsigned char has_query51:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query51 { + union { + struct { + unsigned char has_signal_clarity:1; + unsigned char f54_q51_b1:1; + unsigned char has_ctrl197:1; + unsigned char has_negative_cp:1; + unsigned char f54_q51_b4:1; + unsigned char has_query53_query54_ctrl198:1; + unsigned char has_ctrl199:1; + unsigned char has_query55:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query55 { + union { + struct { + unsigned char has_query56:1; + unsigned char has_data33_data34:1; + unsigned char has_alt_report_rate:1; + unsigned char has_ctrl200:1; + unsigned char has_ctrl201_ctrl202:1; + unsigned char has_ctrl203:1; + unsigned char has_ctrl204:1; + unsigned char has_query57:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query57 { + union { + struct { + unsigned char has_ctrl205:1; + unsigned char has_ctrl206:1; + unsigned char has_usb_bulk_read:1; + unsigned char has_ctrl207:1; + unsigned char has_ctrl208:1; + unsigned char has_ctrl209:1; + unsigned char has_ctrl210:1; + unsigned char has_query58:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query58 { + union { + struct { + unsigned char has_query59:1; + unsigned char has_query60:1; + unsigned char has_ctrl211:1; + unsigned char has_ctrl212:1; + unsigned char has_hybrid_abs_tx_axis_filtering:1; + unsigned char has_hybrid_abs_tx_interpolation:1; + unsigned char has_ctrl213:1; + unsigned char has_query61:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query61 { + union { + struct { + unsigned char has_ctrl214:1; + unsigned char has_ctrl215_query62_query63:1; + unsigned char f54_query_61_b2:1; + unsigned char has_ctrl216:1; + unsigned char has_ctrl217:1; + unsigned char has_misc_host_ctrl:1; + unsigned char has_ctrl219:1; + unsigned char has_query64:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query64 { + union { + struct { + unsigned char has_ctrl101_sub1:1; + unsigned char has_ctrl220:1; + unsigned char has_ctrl221:1; + unsigned char has_ctrl222:1; + unsigned char has_ctrl219_sub1:1; + unsigned char has_ctrl103_sub3:1; + unsigned char has_ctrl224_ctrl226_ctrl227:1; + unsigned char has_query65:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query65 { + union { + struct { + unsigned char f54_q65_b0b1:2; + unsigned char has_ctrl101_sub2:1; + unsigned char f54_65_b3:1; + unsigned char has_ctrl230:1; + unsigned char has_query66_ctrl231:1; + unsigned char has_ctrl232:1; + unsigned char has_query67:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query67 { + union { + struct { + unsigned char has_abs_doze_spatial_filter_en:1; + unsigned char has_abs_doze_avg_filter_enhancement_en:1; + unsigned char has_single_display_pulse:1; + unsigned char f54_q67_b3b4:2; + unsigned char has_ctrl235_ctrl236:1; + unsigned char has_ctrl109_sub1_sub2:1; + unsigned char has_query68:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query68 { + union { + struct { + unsigned char has_ctrl237:1; + unsigned char has_ctrl238:1; + unsigned char has_ctrl238_sub1:1; + unsigned char has_ctrl238_sub2:1; + unsigned char has_ctrl239:1; + unsigned char has_freq_filter_ext:1; + unsigned char is_tddi_hic:1; + unsigned char has_query69:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query69 { + union { + struct { + unsigned char has_ctrl240sub0:1; + unsigned char has_ctrl240sub1sub2:1; + unsigned char has_ctrl240sub3:1; + unsigned char has_ctrl240sub4:1; + unsigned char f54_q69_b4b6:3; + unsigned char has_query70:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query70 { + union { + struct { + unsigned char f54_q70_b0b1:2; + unsigned char has_ctrl242:1; + unsigned char f54_q70_b3b6:4; + unsigned char has_query71:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query71 { + union { + struct { + unsigned char f54_q71_b0b6:7; + unsigned char has_query72:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query72 { + union { + struct { + unsigned char f54_q72_b0b6:7; + unsigned char has_query73:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query73 { + union { + struct { + unsigned char f54_q73_b0b6:7; + unsigned char has_query74:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query74 { + union { + struct { + unsigned char f54_q74_b0b6:7; + unsigned char has_query75:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query75 { + union { + struct { + unsigned char f54_q75_b0b1:2; + unsigned char has_ctrl269:1; + unsigned char f54_q75_b3b7:5; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_control_0 { + union { + struct { + unsigned char no_relax:1; + unsigned char no_scan:1; + unsigned char force_fast_relaxation:1; + unsigned char startup_fast_relaxation:1; + unsigned char gesture_cancels_sfr:1; + unsigned char enable_energy_ratio_relaxation:1; + unsigned char excessive_noise_attn_enable:1; + unsigned char f54_control0_b7:1; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_1 { + union { + struct { + unsigned char bursts_per_cluster:4; + unsigned char f54_ctrl1_b4__7:4; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_2 { + union { + struct { + unsigned short saturation_cap; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_3 { + union { + struct { + unsigned char pixel_touch_threshold; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_4__6 { + union { + struct { + /* control 4 */ + unsigned char rx_feedback_cap:2; + unsigned char bias_current:2; + unsigned char f54_ctrl4_b4__7:4; + + /* control 5 */ + unsigned char low_ref_cap:2; + unsigned char low_ref_feedback_cap:2; + unsigned char low_ref_polarity:1; + unsigned char f54_ctrl5_b5__7:3; + + /* control 6 */ + unsigned char high_ref_cap:2; + unsigned char high_ref_feedback_cap:2; + unsigned char high_ref_polarity:1; + unsigned char f54_ctrl6_b5__7:3; + } __packed; + struct { + unsigned char data[3]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_7 { + union { + struct { + unsigned char cbc_cap:2; + unsigned char cbc_polarity:2; + unsigned char cbc_tx_carrier_selection:1; + unsigned char f54_ctrl7_b5__7:3; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_8__9 { + union { + struct { + /* control 8 */ + unsigned short integration_duration:10; + unsigned short f54_ctrl8_b10__15:6; + + /* control 9 */ + unsigned char reset_duration; + } __packed; + struct { + unsigned char data[3]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_10 { + union { + struct { + unsigned char noise_sensing_bursts_per_image:4; + unsigned char f54_ctrl10_b4__7:4; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_11 { + union { + struct { + unsigned short f54_ctrl11; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_12__13 { + union { + struct { + /* control 12 */ + unsigned char slow_relaxation_rate; + + /* control 13 */ + unsigned char fast_relaxation_rate; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_14 { + union { + struct { + unsigned char rxs_on_xaxis:1; + unsigned char curve_comp_on_txs:1; + unsigned char f54_ctrl14_b2__7:6; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_15n { + unsigned char sensor_rx_assignment; +}; + +struct f54_control_15 { + struct f54_control_15n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_16n { + unsigned char sensor_tx_assignment; +}; + +struct f54_control_16 { + struct f54_control_16n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_17n { + unsigned char burst_count_b8__10:3; + unsigned char disable:1; + unsigned char f54_ctrl17_b4:1; + unsigned char filter_bandwidth:3; +}; + +struct f54_control_17 { + struct f54_control_17n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_18n { + unsigned char burst_count_b0__7; +}; + +struct f54_control_18 { + struct f54_control_18n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_19n { + unsigned char stretch_duration; +}; + +struct f54_control_19 { + struct f54_control_19n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_20 { + union { + struct { + unsigned char disable_noise_mitigation:1; + unsigned char f54_ctrl20_b2__7:7; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_21 { + union { + struct { + unsigned short freq_shift_noise_threshold; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_22__26 { + union { + struct { + /* control 22 */ + unsigned char f54_ctrl22; + + /* control 23 */ + unsigned short medium_noise_threshold; + + /* control 24 */ + unsigned short high_noise_threshold; + + /* control 25 */ + unsigned char noise_density; + + /* control 26 */ + unsigned char frame_count; + } __packed; + struct { + unsigned char data[7]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_27 { + union { + struct { + unsigned char iir_filter_coef; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_28 { + union { + struct { + unsigned short quiet_threshold; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_29 { + union { + struct { + /* control 29 */ + unsigned char f54_ctrl29_b0__6:7; + unsigned char cmn_filter_disable:1; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_30 { + union { + struct { + unsigned char cmn_filter_max; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_31 { + union { + struct { + unsigned char touch_hysteresis; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_32__35 { + union { + struct { + /* control 32 */ + unsigned short rx_low_edge_comp; + + /* control 33 */ + unsigned short rx_high_edge_comp; + + /* control 34 */ + unsigned short tx_low_edge_comp; + + /* control 35 */ + unsigned short tx_high_edge_comp; + } __packed; + struct { + unsigned char data[8]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_36n { + unsigned char axis1_comp; +}; + +struct f54_control_36 { + struct f54_control_36n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_37n { + unsigned char axis2_comp; +}; + +struct f54_control_37 { + struct f54_control_37n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_38n { + unsigned char noise_control_1; +}; + +struct f54_control_38 { + struct f54_control_38n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_39n { + unsigned char noise_control_2; +}; + +struct f54_control_39 { + struct f54_control_39n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_40n { + unsigned char noise_control_3; +}; + +struct f54_control_40 { + struct f54_control_40n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_89_subpkt0 { + unsigned char c89_cid_sel_opt:2; + unsigned char c89_cid_voltage_sel:3; + unsigned char c89_byte0_b5_b7:3; + unsigned char c89_cid_im_noise_threshold_lsb; + unsigned char c89_cid_im_noise_threshold_msb; +} __packed; + +struct f54_control_89_subpkt1 { + unsigned char c89_fnm_pixel_touch_mult; + unsigned char c89_freq_scan_threshold_lsb; + unsigned char c89_freq_scan_threshold_msb; + unsigned char c89_quiet_im_threshold_lsb; + unsigned char c89_quiet_im_threshold_msb; +} __packed; + +struct f54_control_89_subpkt2 { + unsigned char c89_rail_sel_opt:2; + unsigned char c89_byte0_b3_b7:6; + unsigned char c89_rail_im_noise_threshold_lsb; + unsigned char c89_rail_im_noise_threshold_msb; + unsigned char c89_rail_im_quiet_threshold_lsb; + unsigned char c89_rail_im_quiet_threshold_msb; +} __packed; + +struct f54_control_89 { + unsigned char *data; + int size; + unsigned short address; + struct f54_control_89_subpkt0 *sp0; + struct f54_control_89_subpkt1 *sp1; + struct f54_control_89_subpkt2 *sp2; +}; + +struct f54_control_93 { + union { + struct { + unsigned char c93_freq_shift_noise_threshold_lsb; + unsigned char c93_freq_shift_noise_threshold_msb; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_95 { + struct f54_control_95n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_99 { + union { + struct { + unsigned char c99_int_dur_lsb; + unsigned char c99_int_dur_msb; + unsigned char c99_reset_dur; + } __packed; + struct { + unsigned char data[3]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_107 { + union { + struct { + unsigned char c107_abs_int_dur; + unsigned char c107_abs_reset_dur; + unsigned char c107_abs_filter_bw; + unsigned char c107_abs_rstretch; + unsigned char c107_abs_burst_count_1; + unsigned char c107_abs_burst_count_2; + unsigned char c107_abs_stretch_dur; + unsigned char c107_abs_adc_clock_div; + unsigned char c107_abs_sub_burtst_size; + unsigned char c107_abs_trigger_delay; + } __packed; + struct { + unsigned char data[10]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_113 { + union { + struct { + unsigned char c113_en_hybrid_on_tx:1; + unsigned char c113_en_hybrid_on_rx:1; + unsigned char c113_tx_consistency_chk:1; + unsigned char c113_rx_consistency_chk:1; + unsigned char c113_en_gradient_baseline:1; + unsigned char c113_byte0_b5b7:3; + unsigned char c113_consistency_thresh; + unsigned char c113_tx_obj_thresh; + unsigned char c113_rx_obj_thresh; + unsigned char c113_tx_thermal_update_int_lsb; + unsigned char c113_tx_thermal_update_int_msb; + unsigned char c113_rx_thermal_update_int_lsb; + unsigned char c113_rx_thermal_update_int_msb; + unsigned char c113_tx_negative_thresh; + unsigned char c113_rx_negative_thresh; + } __packed; + struct { + unsigned char data[10]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_116 { + union { + struct { + unsigned char c116_im_thresh; + unsigned char c116_debounce; + unsigned char c116_quiet_im; + unsigned char c116_filter; + } __packed; + struct { + unsigned char data[4]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_137 { + union { + struct { + unsigned char c137_cmnr_adjust; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_145 { + union { + struct { + unsigned char c145_bursts_per_cluster_rx; + unsigned char c145_bursts_per_cluster_tx; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_146 { + union { + struct { + unsigned char c146_int_dur_lsb; + unsigned char c146_int_dur_msb; + unsigned char c146_reset_dur; + unsigned char c146_filter_bandwidth; + unsigned char c146_stretch_dur; + unsigned char c146_burst_count1; + unsigned char c146_burst_count2; + unsigned char c146_stretch_dur2; + unsigned char c146_adc_clock_div; + } __packed; + struct { + unsigned char data[9]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_242 { + union { + struct { + unsigned char c242_cbc_servo_disable:1; + unsigned char c242_b1b7:7; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_269 { + union { + struct { + unsigned char c269_bsc_calibration_en:1; + unsigned char c269_b1b7:7; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control { + struct f54_control_0 *reg_0; + struct f54_control_1 *reg_1; + struct f54_control_2 *reg_2; + struct f54_control_3 *reg_3; + struct f54_control_4__6 *reg_4__6; + struct f54_control_7 *reg_7; + struct f54_control_8__9 *reg_8__9; + struct f54_control_10 *reg_10; + struct f54_control_11 *reg_11; + struct f54_control_12__13 *reg_12__13; + struct f54_control_14 *reg_14; + struct f54_control_15 *reg_15; + struct f54_control_16 *reg_16; + struct f54_control_17 *reg_17; + struct f54_control_18 *reg_18; + struct f54_control_19 *reg_19; + struct f54_control_20 *reg_20; + struct f54_control_21 *reg_21; + struct f54_control_22__26 *reg_22__26; + struct f54_control_27 *reg_27; + struct f54_control_28 *reg_28; + struct f54_control_29 *reg_29; + struct f54_control_30 *reg_30; + struct f54_control_31 *reg_31; + struct f54_control_32__35 *reg_32__35; + struct f54_control_36 *reg_36; + struct f54_control_37 *reg_37; + struct f54_control_38 *reg_38; + struct f54_control_39 *reg_39; + struct f54_control_40 *reg_40; + struct f54_control_89 *reg_89; + struct f54_control_93 *reg_93; + struct f54_control_95 *reg_95; + struct f54_control_99 *reg_99; + struct f54_control_107 *reg_107; + struct f54_control_113 *reg_113; + struct f54_control_116 *reg_116; + struct f54_control_137 *reg_137; + struct f54_control_145 *reg_145; + struct f54_control_146 *reg_146; + struct f54_control_242 *reg_242; + struct f54_control_269 *reg_269; + struct f55_control_0 *reg_0_f55; + struct f55_control_8 *reg_8_f55; +}; + +struct f54_data_4 { + union { + struct { + unsigned char d4_sense_freq_sel:4; + unsigned char d4_baseline_sel:2; + unsigned char d4_b6:1; + unsigned char d4_inhibit_freq_shift:1; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; +struct f54_data_6 { + union { + struct { + unsigned char d6_interference_metric_lsb; + unsigned char d6_interference_metric_msb; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + + }; +}; + +struct f54_data_7_0 { + union { + struct { + unsigned char d7_current_report_rate_lsb; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_7_1 { + union { + struct { + unsigned char d7_current_report_rate_msb; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_8 { + union { + struct { + unsigned char d8_variance_metric_lsb; + unsigned char d8_variance_metric_msb; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_9 { + union { + struct { + unsigned char d9_averaged_im_lsb; + unsigned char d9_averaged_im_msb; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_10 { + union { + struct { + unsigned char d10_noise_state; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_14 { + union { + struct { + unsigned char d14_cid_im_lsb; + unsigned char d14_cid_im_msb; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_16 { + union { + struct { + unsigned char d16_freq_scan_im_lsb; + unsigned char d16_freq_scan_im_msb; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_17 { + union { + struct { + unsigned char d17_freq:7; + unsigned char d17_inhibit_freq_shift:1; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data { + struct f54_data_4 *reg_4; + struct f54_data_6 *reg_6; + struct f54_data_7_0 *reg_7_0; + struct f54_data_7_1 *reg_7_1; + struct f54_data_8 *reg_8; + struct f54_data_9 *reg_9; + struct f54_data_10 *reg_10; + struct f54_data_14 *reg_14; + struct f54_data_16 *reg_16; + struct f54_data_17 *reg_17; +}; + +struct f55_query_0_2 { + union { + struct { + /* query 0 */ + unsigned char num_of_rx_electrodes; + + /* query 1 */ + unsigned char num_of_tx_electrodes; + + /* query 2 */ + unsigned char has_sensor_assignment:1; + unsigned char has_edge_compensation:1; + unsigned char curve_compensation_mode:2; + unsigned char has_ctrl6:1; + unsigned char has_alternate_tx_assignment:1; + unsigned char f55_q2_has_single_layer_multitouch:1; + unsigned char has_query5:1; + } __packed; + unsigned char data[3]; + }; +}; + +struct f55_query_3 { + union { + struct { + unsigned char f55_q3_has_ctrl8:1; + unsigned char has_ctrl9:1; + unsigned char has_on_cell_pattern:1; + unsigned char has_data0:1; + unsigned char has_single_wide_pattern:1; + unsigned char has_mirrored_tx_pattern:1; + unsigned char has_discrete_pattern:1; + unsigned char has_query9:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_control_0 { + union { + struct { + unsigned char f55_c0_receivers_on_x:1; + unsigned char f55_c0_curve_compensation_on_tx:1; + unsigned char f55_c0_trx_sense:1; + unsigned char f55_c0_trx_config:1; + unsigned char f55_c0_guard_disable:1; + unsigned char f55_c0_b5_b7:3; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f55_control_8 { + union { + struct { + unsigned char f55_c8_pattern_type; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct synaptics_rmi4_fn55_desc { + unsigned short query_base_addr; + unsigned short control_base_addr; +}; + +struct f54_kobj_data { + struct kobject kobj; + struct completion kobj_unregistered; + struct bin_attribute *dev_report_data; + struct synaptics_rmi4_f54_handle *f54; +}; + +#define to_f54_kobj_data(x) container_of(x, struct f54_kobj_data, kobj) +#define to_f54_attr(x) container_of(x, struct f54_attribute, attr) + +struct synaptics_rmi4_f54_handle { + bool init_done; + bool no_auto_cal; + int status; + unsigned char intr_mask; + unsigned char intr_reg_num; + unsigned char *report_data; + unsigned short query_base_addr; + unsigned short control_base_addr; + unsigned short data_base_addr; + unsigned short command_base_addr; + unsigned short fifoindex; + unsigned int report_size; + unsigned int data_buffer_size; + enum f54_report_types report_type; + enum f54_report_types user_report_type1; + enum f54_report_types user_report_type2; + struct mutex status_mutex; + struct mutex data_mutex; + struct mutex control_mutex; + struct f54_query query; + struct f54_query12 query12; + struct f54_query13 query13; + struct f54_query14 query14; + struct f54_query15 query15; + struct f54_query16 query16; + struct f54_query17 query17; + struct f54_query18 query18; + struct f54_query19 query19; + struct f54_query20 query20; + struct f54_query21 query21; + struct f54_query22 query22; + struct f54_query23 query23; + struct f54_query24 query24; + struct f54_query25 query25; + struct f54_query26 query26; + struct f54_query27 query27; + struct f54_query28 query28; + struct f54_query29 query29; + struct f54_query30 query30; + struct f54_query31 query31; + struct f54_query32 query32; + struct f54_query33 query33; + struct f54_query34 query34; + struct f54_query35 query35; + struct f54_query36 query36; + struct f54_query38 query38; + struct f54_query39 query39; + struct f54_query40 query40; + struct f54_query41 query41; + struct f54_query42 query42; + struct f54_query43 query43; + struct f54_query46 query46; + struct f54_query47 query47; + struct f54_query49 query49; + struct f54_query50 query50; + struct f54_query51 query51; + struct f54_query55 query55; + struct f54_query57 query57; + struct f54_query58 query58; + struct f54_query61 query61; + struct f54_query64 query64; + struct f54_query65 query65; + struct f54_query67 query67; + struct f54_query68 query68; + struct f54_query69 query69; + struct f54_query70 query70; + struct f54_query71 query71; + struct f54_query72 query72; + struct f54_query73 query73; + struct f54_query74 query74; + struct f54_query75 query75; + struct f54_control control; + struct f54_data data; + struct hrtimer watchdog; + struct work_struct timeout_work; + struct delayed_work status_work; + struct workqueue_struct *status_workqueue; + struct synaptics_rmi4_exp_fn_ptr *fn_ptr; + struct synaptics_rmi4_data *rmi4_data; + struct synaptics_rmi4_fn55_desc *fn55; + struct f55_query_0_2 query_f55_0_2; + struct f55_query_3 query_f55_3; + struct wakeup_source test_wake_lock; + struct completion remove_complete; + struct f54_kobj_data *f54_kdata; +}; + +static ssize_t f54_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct f54_attribute *attribute = to_f54_attr(attr); + struct f54_kobj_data *f54_kobj_data = to_f54_kobj_data(kobj); + + if (!attribute->show) + return -EIO; + return attribute->show(f54_kobj_data->f54, buf); +} + +static ssize_t f54_attr_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t len) +{ + struct f54_attribute *attribute = to_f54_attr(attr); + struct f54_kobj_data *f54_kobj_data = to_f54_kobj_data(kobj); + + if (!attribute->store) + return -EIO; + return attribute->store(f54_kobj_data->f54, buf, len); +} + +static const struct sysfs_ops f54_sysfs_ops = { + .show = f54_attr_show, + .store = f54_attr_store, +}; + +void f54_kobj_data_release(struct kobject *kobj) +{ + struct f54_kobj_data *f54_kobj_data; + + f54_kobj_data = to_f54_kobj_data(kobj); + dev_dbg(&f54_kobj_data->f54->rmi4_data->i2c_client->dev, + "%s: f54_kobj_data released\n", __func__); + complete(&f54_kobj_data->kobj_unregistered); +} + +static struct kobj_type f54_ktype = { + .sysfs_ops = &f54_sysfs_ops, + .release = f54_kobj_data_release, +}; + +store_prototype(reset) +show_prototype_ext(status, S_IRUGO) +show_prototype_ext(report_size, S_IRUGO) +show_prototype_ext(num_of_mapped_rx, S_IRUGO) +show_prototype_ext(num_of_mapped_tx, S_IRUGO) +show_store_prototype(no_auto_cal) +show_store_prototype_ext(report_type, S_IRUGO | S_IWUSR | S_IWGRP) +show_store_prototype_ext(user_report_type1, S_IRUGO | S_IWUSR | S_IWGRP) +show_store_prototype_ext(user_report_type2, S_IRUGO | S_IWUSR | S_IWGRP) +show_prototype_ext(user_get_report1, S_IRUGO) +show_prototype_ext(user_get_report2, S_IRUGO) +show_store_prototype(fifoindex) +store_prototype(get_report) +show_store_prototype(force_update) +store_prototype(force_cal) +show_store_prototype(enter_in_cell_test_mode) +show_prototype_ext(num_of_rx_electrodes, S_IRUGO) +show_prototype_ext(num_of_tx_electrodes, S_IRUGO) +show_prototype(has_image16) +show_prototype(has_image8) +show_prototype(has_baseline) +show_prototype(clock_rate) +show_prototype(touch_controller_family) +show_prototype(has_pixel_touch_threshold_adjustment) +show_prototype(has_sensor_assignment) +show_prototype(has_interference_metric) +show_prototype(has_sense_frequency_control) +show_prototype(has_firmware_noise_mitigation) +show_prototype(has_two_byte_report_rate) +show_prototype(has_one_byte_report_rate) +show_prototype(has_relaxation_control) +show_prototype(curve_compensation_mode) +show_prototype(has_iir_filter) +show_prototype(has_cmn_removal) +show_prototype(has_cmn_maximum) +show_prototype(has_touch_hysteresis) +show_prototype(has_edge_compensation) +show_prototype(has_per_frequency_noise_control) +show_prototype(has_enhanced_stretch) +show_prototype(has_force_fast_relaxation) +show_prototype(has_multi_metric_state_machine) +show_prototype(has_signal_clarity) +show_prototype(has_variance_metric) +show_prototype(has_0d_relaxation_control) +show_prototype(has_0d_acquisition_control) +show_prototype(has_status) +show_prototype(has_slew_metric) +show_prototype(has_h_blank) +show_prototype(has_v_blank) +show_prototype(has_long_h_blank) +show_prototype(has_startup_fast_relaxation) +show_prototype(has_esd_control) +show_prototype(has_noise_mitigation2) +show_prototype(has_noise_state) +show_prototype(has_energy_ratio_relaxation) +show_prototype(number_of_sensing_frequencies) +show_prototype(has_data17) +show_prototype(has_ctrl113) +show_prototype(has_ctrl145) +show_prototype(has_ctrl146) +show_prototype(q17_num_of_sense_freqs) +show_store_prototype(no_relax) +show_store_prototype(no_scan) +show_store_prototype(bursts_per_cluster) +show_store_prototype(saturation_cap) +show_store_prototype(pixel_touch_threshold) +show_store_prototype(rx_feedback_cap) +show_store_prototype(low_ref_cap) +show_store_prototype(low_ref_feedback_cap) +show_store_prototype(low_ref_polarity) +show_store_prototype(high_ref_cap) +show_store_prototype(high_ref_feedback_cap) +show_store_prototype(high_ref_polarity) +show_store_prototype(cbc_cap) +show_store_prototype(cbc_polarity) +show_store_prototype(cbc_tx_carrier_selection) +show_store_prototype(integration_duration) +show_store_prototype(reset_duration) +show_store_prototype(noise_sensing_bursts_per_image) +show_store_prototype(slow_relaxation_rate) +show_store_prototype(fast_relaxation_rate) +show_store_prototype(rxs_on_xaxis) +show_store_prototype(curve_comp_on_txs) +show_prototype(sensor_rx_assignment) +show_prototype(sensor_tx_assignment) +show_prototype(burst_count) +show_prototype(disable) +show_prototype(filter_bandwidth) +show_prototype(stretch_duration) +show_store_prototype(disable_noise_mitigation) +show_store_prototype(freq_shift_noise_threshold) +show_store_prototype(medium_noise_threshold) +show_store_prototype(high_noise_threshold) +show_store_prototype(noise_density) +show_store_prototype(frame_count) +show_store_prototype(iir_filter_coef) +show_store_prototype(quiet_threshold) +show_store_prototype(cmn_filter_disable) +show_store_prototype(cmn_filter_max) +show_store_prototype(touch_hysteresis) +show_store_prototype(rx_low_edge_comp) +show_store_prototype(rx_high_edge_comp) +show_store_prototype(tx_low_edge_comp) +show_store_prototype(tx_high_edge_comp) +show_store_prototype(axis1_comp) +show_store_prototype(axis2_comp) +show_prototype(noise_control_1) +show_prototype(noise_control_2) +show_prototype(noise_control_3) +show_store_prototype(d4_sense_freq_sel) +show_store_prototype(d4_baseline_sel) +show_store_prototype(d4_inhibit_freq_shift) +show_prototype(d6_interference_metric_lsb) +show_prototype(d6_interference_metric_msb) +show_prototype(d7_current_report_rate_lsb) +show_prototype(d7_current_report_rate_msb) +show_prototype(d8_variance_metric_lsb) +show_prototype(d8_variance_metric_msb) +show_prototype(d9_averaged_im_lsb) +show_prototype(d9_averaged_im_msb) +show_prototype(d10_noise_state) +show_store_prototype(d16_freq_scan_im_lsb) +show_store_prototype(d16_freq_scan_im_msb) +show_prototype(d14_cid_im_lsb) +show_prototype(d14_cid_im_msb) +show_store_prototype(d17_inhibit_freq_shift) +show_store_prototype(d17_freq) +show_store_prototype(c89_cid_sel_opt) +show_store_prototype(c89_cid_voltage_sel) +show_store_prototype(c89_cid_im_noise_threshold_lsb) +show_store_prototype(c89_cid_im_noise_threshold_msb) +show_store_prototype(c89_fnm_pixel_touch_mult) +show_store_prototype(c89_freq_scan_threshold_lsb) +show_store_prototype(c89_freq_scan_threshold_msb) +show_store_prototype(c89_quiet_im_threshold_lsb) +show_store_prototype(c89_quiet_im_threshold_msb) +show_store_prototype(c89_rail_sel_opt) +show_store_prototype(c89_rail_im_noise_threshold_lsb) +show_store_prototype(c89_rail_im_noise_threshold_msb) +show_store_prototype(c89_rail_im_quiet_threshold_lsb) +show_store_prototype(c89_rail_im_quiet_threshold_msb) +show_store_prototype(c93_freq_shift_noise_threshold_lsb) +show_store_prototype(c93_freq_shift_noise_threshold_msb) +show_store_prototype(c95_disable) +show_store_prototype(c95_filter_bw) +show_store_prototype(c95_first_burst_length_lsb) +show_store_prototype(c95_first_burst_length_msb) +show_store_prototype(c95_addl_burst_length_lsb) +show_store_prototype(c95_addl_burst_length_msb) +show_store_prototype(c95_i_stretch) +show_store_prototype(c95_r_stretch) +show_store_prototype(c95_noise_control1) +show_store_prototype(c95_noise_control2) +show_store_prototype(c95_noise_control3) +show_store_prototype(c95_noise_control4) +show_store_prototype(c99_int_dur_lsb) +show_store_prototype(c99_int_dur_msb) +show_store_prototype(c99_reset_dur) +show_store_prototype(c107_abs_int_dur) +show_store_prototype(c107_abs_reset_dur) +show_store_prototype(c107_abs_filter_bw) +show_store_prototype(c107_abs_rstretch) +show_store_prototype(c107_abs_burst_count_1) +show_store_prototype(c107_abs_burst_count_2) +show_store_prototype(c107_abs_stretch_dur) +show_store_prototype(c107_abs_adc_clock_div) +show_store_prototype(c107_abs_sub_burtst_size) +show_store_prototype(c107_abs_trigger_delay) +show_store_prototype(c113_en_hybrid_on_tx) +show_store_prototype(c113_en_hybrid_on_rx) +show_store_prototype(c113_tx_consistency_chk) +show_store_prototype(c113_rx_consistency_chk) +show_store_prototype(c113_en_gradient_baseline) +show_store_prototype(c113_consistency_thresh) +show_store_prototype(c113_tx_obj_thresh) +show_store_prototype(c113_rx_obj_thresh) +show_store_prototype(c113_tx_thermal_update_int_lsb) +show_store_prototype(c113_tx_thermal_update_int_msb) +show_store_prototype(c113_rx_thermal_update_int_lsb) +show_store_prototype(c113_rx_thermal_update_int_msb) +show_store_prototype(c113_tx_negative_thresh) +show_store_prototype(c113_rx_negative_thresh) +show_store_prototype(c116_im_thresh) +show_store_prototype(c116_debounce) +show_store_prototype(c116_quiet_im) +show_store_prototype(c116_filter) +show_store_prototype(c137_cmnr_adjust) +show_store_prototype(c145_bursts_per_cluster_rx) +show_store_prototype(c145_bursts_per_cluster_tx) +show_store_prototype(c146_int_dur_lsb) +show_store_prototype(c146_int_dur_msb) +show_store_prototype(c146_reset_dur) +show_store_prototype(c146_filter_bandwidth) +show_store_prototype(c146_stretch_dur) +show_store_prototype(c146_burst_count1) +show_store_prototype(c146_burst_count2) +show_store_prototype(c146_stretch_dur2) +show_store_prototype(c146_adc_clock_div) +show_store_prototype(c242_cbc_servo_disable) +show_store_prototype(c269_bsc_calibration_en) +show_prototype(f55_q2_has_single_layer_multitouch) +show_prototype(f55_c0_receivers_on_x) +show_prototype(f55_c8_pattern_type) + +static ssize_t synaptics_rmi4_f54_data_read(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static struct attribute *attrs[] = { + attrify(reset), + attrify(status), + attrify(report_size), + attrify(num_of_mapped_rx), + attrify(num_of_mapped_tx), + attrify(no_auto_cal), + attrify(report_type), + attrify(user_report_type1), + attrify(user_report_type2), + attrify(user_get_report1), + attrify(user_get_report2), + attrify(fifoindex), + attrify(get_report), + attrify(force_update), + attrify(force_cal), + attrify(enter_in_cell_test_mode), + attrify(num_of_rx_electrodes), + attrify(num_of_tx_electrodes), + attrify(has_image16), + attrify(has_image8), + attrify(has_baseline), + attrify(clock_rate), + attrify(touch_controller_family), + attrify(has_pixel_touch_threshold_adjustment), + attrify(has_sensor_assignment), + attrify(has_interference_metric), + attrify(has_sense_frequency_control), + attrify(has_firmware_noise_mitigation), + attrify(has_two_byte_report_rate), + attrify(has_one_byte_report_rate), + attrify(has_relaxation_control), + attrify(curve_compensation_mode), + attrify(has_iir_filter), + attrify(has_cmn_removal), + attrify(has_cmn_maximum), + attrify(has_touch_hysteresis), + attrify(has_edge_compensation), + attrify(has_per_frequency_noise_control), + attrify(has_enhanced_stretch), + attrify(has_force_fast_relaxation), + attrify(has_multi_metric_state_machine), + attrify(has_signal_clarity), + attrify(has_variance_metric), + attrify(has_0d_relaxation_control), + attrify(has_0d_acquisition_control), + attrify(has_status), + attrify(has_slew_metric), + attrify(has_h_blank), + attrify(has_v_blank), + attrify(has_long_h_blank), + attrify(has_startup_fast_relaxation), + attrify(has_esd_control), + attrify(has_noise_mitigation2), + attrify(has_noise_state), + attrify(has_energy_ratio_relaxation), + attrify(number_of_sensing_frequencies), + attrify(has_data17), + attrify(has_ctrl113), + attrify(has_ctrl145), + attrify(has_ctrl146), + attrify(q17_num_of_sense_freqs), + attrify(f55_q2_has_single_layer_multitouch), + NULL, +}; + +static struct attribute_group attr_group = GROUP(attrs); + +static struct attribute *attrs_reg_0[] = { + attrify(no_relax), + attrify(no_scan), + NULL, +}; + +static struct attribute *attrs_reg_1[] = { + attrify(bursts_per_cluster), + NULL, +}; + +static struct attribute *attrs_reg_2[] = { + attrify(saturation_cap), + NULL, +}; + +static struct attribute *attrs_reg_3[] = { + attrify(pixel_touch_threshold), + NULL, +}; + +static struct attribute *attrs_reg_4__6[] = { + attrify(rx_feedback_cap), + attrify(low_ref_cap), + attrify(low_ref_feedback_cap), + attrify(low_ref_polarity), + attrify(high_ref_cap), + attrify(high_ref_feedback_cap), + attrify(high_ref_polarity), + NULL, +}; + +static struct attribute *attrs_reg_7[] = { + attrify(cbc_cap), + attrify(cbc_polarity), + attrify(cbc_tx_carrier_selection), + NULL, +}; + +static struct attribute *attrs_reg_8__9[] = { + attrify(integration_duration), + attrify(reset_duration), + NULL, +}; + +static struct attribute *attrs_reg_10[] = { + attrify(noise_sensing_bursts_per_image), + NULL, +}; + +static struct attribute *attrs_reg_11[] = { + NULL, +}; + +static struct attribute *attrs_reg_12__13[] = { + attrify(slow_relaxation_rate), + attrify(fast_relaxation_rate), + NULL, +}; + +static struct attribute *attrs_reg_14__16[] = { + attrify(rxs_on_xaxis), + attrify(curve_comp_on_txs), + attrify(sensor_rx_assignment), + attrify(sensor_tx_assignment), + NULL, +}; + +static struct attribute *attrs_reg_17__19[] = { + attrify(burst_count), + attrify(disable), + attrify(filter_bandwidth), + attrify(stretch_duration), + NULL, +}; + +static struct attribute *attrs_reg_20[] = { + attrify(disable_noise_mitigation), + NULL, +}; + +static struct attribute *attrs_reg_21[] = { + attrify(freq_shift_noise_threshold), + NULL, +}; + +static struct attribute *attrs_reg_22__26[] = { + attrify(medium_noise_threshold), + attrify(high_noise_threshold), + attrify(noise_density), + attrify(frame_count), + NULL, +}; + +static struct attribute *attrs_reg_27[] = { + attrify(iir_filter_coef), + NULL, +}; + +static struct attribute *attrs_reg_28[] = { + attrify(quiet_threshold), + NULL, +}; + +static struct attribute *attrs_reg_29[] = { + attrify(cmn_filter_disable), + NULL, +}; + +static struct attribute *attrs_reg_30[] = { + attrify(cmn_filter_max), + NULL, +}; + +static struct attribute *attrs_reg_31[] = { + attrify(touch_hysteresis), + NULL, +}; + +static struct attribute *attrs_reg_32__35[] = { + attrify(rx_low_edge_comp), + attrify(rx_high_edge_comp), + attrify(tx_low_edge_comp), + attrify(tx_high_edge_comp), + NULL, +}; + +static struct attribute *attrs_reg_36[] = { + attrify(axis1_comp), + NULL, +}; + +static struct attribute *attrs_reg_37[] = { + attrify(axis2_comp), + NULL, +}; + +static struct attribute *attrs_reg_38__40[] = { + attrify(noise_control_1), + attrify(noise_control_2), + attrify(noise_control_3), + NULL, +}; + +static struct attribute *attrs_reg_89_subpkt0[] = { + attrify(c89_cid_sel_opt), + attrify(c89_cid_voltage_sel), + attrify(c89_cid_im_noise_threshold_lsb), + attrify(c89_cid_im_noise_threshold_msb), + NULL, +}; + +static struct attribute *attrs_reg_89_subpkt1[] = { + attrify(c89_fnm_pixel_touch_mult), + attrify(c89_freq_scan_threshold_lsb), + attrify(c89_freq_scan_threshold_msb), + attrify(c89_quiet_im_threshold_lsb), + attrify(c89_quiet_im_threshold_msb), + NULL, +}; + +static struct attribute *attrs_reg_89_subpkt2[] = { + attrify(c89_rail_sel_opt), + attrify(c89_rail_im_noise_threshold_lsb), + attrify(c89_rail_im_noise_threshold_msb), + attrify(c89_rail_im_quiet_threshold_lsb), + attrify(c89_rail_im_quiet_threshold_msb), + NULL, +}; + +static struct attribute *attrs_reg_93[] = { + attrify(c93_freq_shift_noise_threshold_lsb), + attrify(c93_freq_shift_noise_threshold_msb), + NULL, +}; + +static struct attribute *attrs_reg_95[] = { + attrify(c95_disable), + attrify(c95_filter_bw), + attrify(c95_first_burst_length_lsb), + attrify(c95_first_burst_length_msb), + attrify(c95_addl_burst_length_lsb), + attrify(c95_addl_burst_length_msb), + attrify(c95_i_stretch), + attrify(c95_r_stretch), + attrify(c95_noise_control1), + attrify(c95_noise_control2), + attrify(c95_noise_control3), + attrify(c95_noise_control4), + NULL, +}; + +static struct attribute *attrs_reg_99[] = { + attrify(c99_int_dur_lsb), + attrify(c99_int_dur_msb), + attrify(c99_reset_dur), + NULL, +}; + +static struct attribute *attrs_reg_107[] = { + attrify(c107_abs_int_dur), + attrify(c107_abs_reset_dur), + attrify(c107_abs_filter_bw), + attrify(c107_abs_rstretch), + attrify(c107_abs_burst_count_1), + attrify(c107_abs_burst_count_2), + attrify(c107_abs_stretch_dur), + attrify(c107_abs_adc_clock_div), + attrify(c107_abs_sub_burtst_size), + attrify(c107_abs_trigger_delay), + NULL, +}; + +static struct attribute *attrs_reg_113[] = { + attrify(c113_en_hybrid_on_tx), + attrify(c113_en_hybrid_on_rx), + attrify(c113_tx_consistency_chk), + attrify(c113_rx_consistency_chk), + attrify(c113_en_gradient_baseline), + attrify(c113_consistency_thresh), + attrify(c113_tx_obj_thresh), + attrify(c113_rx_obj_thresh), + attrify(c113_tx_thermal_update_int_lsb), + attrify(c113_tx_thermal_update_int_msb), + attrify(c113_rx_thermal_update_int_lsb), + attrify(c113_rx_thermal_update_int_msb), + attrify(c113_tx_negative_thresh), + attrify(c113_rx_negative_thresh), + NULL, +}; + +static struct attribute *attrs_reg_116[] = { + attrify(c116_im_thresh), + attrify(c116_debounce), + attrify(c116_quiet_im), + attrify(c116_filter), + NULL, +}; + +static struct attribute *attrs_reg_137[] = { + attrify(c137_cmnr_adjust), + NULL, +}; + +static struct attribute *attrs_reg_145[] = { + attrify(c145_bursts_per_cluster_rx), + attrify(c145_bursts_per_cluster_tx), + NULL, +}; + +static struct attribute *attrs_reg_146[] = { + attrify(c146_int_dur_lsb), + attrify(c146_int_dur_msb), + attrify(c146_reset_dur), + attrify(c146_filter_bandwidth), + attrify(c146_stretch_dur), + attrify(c146_burst_count1), + attrify(c146_burst_count2), + attrify(c146_stretch_dur2), + attrify(c146_adc_clock_div), + NULL, +}; + +static struct attribute *attrs_reg_242[] = { + attrify(c242_cbc_servo_disable), + NULL, +}; + +static struct attribute *attrs_reg_269[] = { + attrify(c269_bsc_calibration_en), + NULL, +}; + +static struct attribute *attrs_f55_c0[] = { + attrify(f55_c0_receivers_on_x), + NULL, +}; + +static struct attribute *attrs_f55_c8[] = { + attrify(f55_c8_pattern_type), + NULL, +}; + +static struct attribute_group attrs_ctrl_regs[] = { + GROUP(attrs_reg_0), + GROUP(attrs_reg_1), + GROUP(attrs_reg_2), + GROUP(attrs_reg_3), + GROUP(attrs_reg_4__6), + GROUP(attrs_reg_7), + GROUP(attrs_reg_8__9), + GROUP(attrs_reg_10), + GROUP(attrs_reg_11), + GROUP(attrs_reg_12__13), + GROUP(attrs_reg_14__16), + GROUP(attrs_reg_17__19), + GROUP(attrs_reg_20), + GROUP(attrs_reg_21), + GROUP(attrs_reg_22__26), + GROUP(attrs_reg_27), + GROUP(attrs_reg_28), + GROUP(attrs_reg_29), + GROUP(attrs_reg_30), + GROUP(attrs_reg_31), + GROUP(attrs_reg_32__35), + GROUP(attrs_reg_36), + GROUP(attrs_reg_37), + GROUP(attrs_reg_38__40), + GROUP(attrs_reg_89_subpkt0), + GROUP(attrs_reg_89_subpkt1), + GROUP(attrs_reg_89_subpkt2), + GROUP(attrs_reg_93), + GROUP(attrs_reg_95), + GROUP(attrs_reg_99), + GROUP(attrs_reg_107), + GROUP(attrs_reg_113), + GROUP(attrs_reg_116), + GROUP(attrs_reg_137), + GROUP(attrs_reg_145), + GROUP(attrs_reg_146), + GROUP(attrs_reg_242), + GROUP(attrs_reg_269), + GROUP(attrs_f55_c0), + GROUP(attrs_f55_c8), +}; + +static bool attrs_ctrl_regs_exist[ARRAY_SIZE(attrs_ctrl_regs)]; + +static struct attribute *data_attrs_reg_4[] = { + attrify(d4_sense_freq_sel), + attrify(d4_baseline_sel), + attrify(d4_inhibit_freq_shift), + NULL, +}; + +static struct attribute *data_attrs_reg_6[] = { + attrify(d6_interference_metric_lsb), + attrify(d6_interference_metric_msb), + NULL, +}; + +static struct attribute *data_attrs_reg_7_0[] = { + attrify(d7_current_report_rate_lsb), + NULL, +}; + +static struct attribute *data_attrs_reg_7_1[] = { + attrify(d7_current_report_rate_msb), + NULL, +}; + +static struct attribute *data_attrs_reg_8[] = { + attrify(d8_variance_metric_lsb), + attrify(d8_variance_metric_msb), + NULL, +}; + +static struct attribute *data_attrs_reg_9[] = { + attrify(d9_averaged_im_lsb), + attrify(d9_averaged_im_msb), + NULL, +}; + +static struct attribute *data_attrs_reg_10[] = { + attrify(d10_noise_state), + NULL, +}; + +static struct attribute *data_attrs_reg_14[] = { + attrify(d14_cid_im_lsb), + attrify(d14_cid_im_msb), + NULL, +}; + +static struct attribute *data_attrs_reg_16[] = { + attrify(d16_freq_scan_im_lsb), + attrify(d16_freq_scan_im_msb), + NULL, +}; + +static struct attribute *data_attrs_reg_17[] = { + attrify(d17_freq), + attrify(d17_inhibit_freq_shift), + NULL, +}; + +static struct attribute_group attrs_data_regs[] = { + GROUP(data_attrs_reg_4), + GROUP(data_attrs_reg_6), + GROUP(data_attrs_reg_7_0), + GROUP(data_attrs_reg_7_1), + GROUP(data_attrs_reg_8), + GROUP(data_attrs_reg_9), + GROUP(data_attrs_reg_10), + GROUP(data_attrs_reg_14), + GROUP(data_attrs_reg_16), + GROUP(data_attrs_reg_17), +}; + +static bool attrs_data_regs_exist[ARRAY_SIZE(attrs_data_regs)]; + +static inline ssize_t synaptics_dsx_test_reporting_show_error( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + dev_warn(&rmi4_data->i2c_client->dev, + "%s: Attempted to read from write-only attribute\n", __func__); + return -EPERM; +} + +static inline ssize_t synaptics_dsx_test_reporting_store_error( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + dev_warn(&rmi4_data->i2c_client->dev, + "%s: Attempted to write to read-only attribute\n", __func__); + return -EPERM; +} + +static bool is_report_type_valid(enum f54_report_types report_type) +{ + switch (report_type) { + case F54_8BIT_IMAGE: + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_HIGH_RESISTANCE: + case F54_TX_TO_TX_SHORT: + case F54_RX_TO_RX1: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP_MIN_MAX: + case F54_RX_OPENS1: + case F54_TX_OPEN: + case F54_TX_TO_GROUND: + case F54_RX_TO_RX2: + case F54_RX_OPENS2: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_COUPLING_COMP: + case F54_SENSOR_SPEED: + case F54_ADC_RANGE: + case F54_TREX_OPENS: + case F54_TREX_TO_GND: + case F54_TREX_SHORTS: + case F54_HYB_ABS_RX_TX_DELTA: + case F54_HYB_ABS_RX_TX_RAW: + return true; + break; + default: + return false; + } +} + +static int set_user_report_type(struct synaptics_rmi4_f54_handle *f54, + enum f54_report_types *user_report_type, + const char *buf, size_t count) +{ + unsigned int report_type; + int retval = 0; + retval = kstrtouint(buf, 10, &report_type); + if (retval) + goto out; + + if (!is_report_type_valid((enum f54_report_types)report_type)) { + dev_err(&f54->rmi4_data->i2c_client->dev, + "%s: Report type not supported by driver\n", + __func__); + retval = -EINVAL; + } + +out: + if (retval == 0) { + *user_report_type = report_type; + retval = count; + } else + *user_report_type = INVALID_REPORT_TYPE; + + return retval; +} + +static ssize_t synaptics_rmi4_f54_user_report_type1_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + return set_user_report_type(f54, &f54->user_report_type1, buf, count); +} + +static ssize_t synaptics_rmi4_f54_user_report_type2_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + return set_user_report_type(f54, &f54->user_report_type2, buf, count); +} + +static void set_report_size(struct synaptics_rmi4_f54_handle *f54) +{ + unsigned char rx = f54->rmi4_data->num_of_rx; + unsigned char tx = f54->rmi4_data->num_of_tx; + + switch (f54->report_type) { + case F54_8BIT_IMAGE: + f54->report_size = rx * tx; + break; + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_COUPLING_COMP: + case F54_SENSOR_SPEED: + case F54_ADC_RANGE: + f54->report_size = 2 * rx * tx; + break; + case F54_HIGH_RESISTANCE: + f54->report_size = HIGH_RESISTANCE_DATA_SIZE; + break; + case F54_TX_TO_TX_SHORT: + case F54_TX_OPEN: + case F54_TX_TO_GROUND: + f54->report_size = (tx + 7) / 8; + break; + case F54_RX_TO_RX1: + case F54_RX_OPENS1: + if (rx < tx) + f54->report_size = 2 * rx * rx; + else + f54->report_size = 2 * rx * tx; + break; + case F54_FULL_RAW_CAP_MIN_MAX: + f54->report_size = FULL_RAW_CAP_MIN_MAX_DATA_SIZE; + break; + case F54_RX_TO_RX2: + case F54_RX_OPENS2: + if (rx <= tx) + f54->report_size = 0; + else + f54->report_size = 2 * rx * (rx - tx); + break; + case F54_TREX_OPENS: + case F54_TREX_TO_GND: + case F54_TREX_SHORTS: + f54->report_size = TREX_DATA_SIZE; + break; + case F54_HYB_ABS_RX_TX_DELTA: + case F54_HYB_ABS_RX_TX_RAW: + f54->report_size = 4 * (rx + tx); + break; + default: + f54->report_size = 0; + } + pr_debug("report size %d\n", f54->report_size); + return; +} + +int set_interrupt(struct synaptics_rmi4_f54_handle *f54, bool set) +{ + int retval; + unsigned char ii; + unsigned char zero = 0x00; + unsigned char *intr_mask; + unsigned short f01_ctrl_reg; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + intr_mask = rmi4_data->intr_mask; + f01_ctrl_reg = rmi4_data->f01_ctrl_base_addr + 1 + f54->intr_reg_num; + + if (!set) { + retval = f54->fn_ptr->write(rmi4_data, + f01_ctrl_reg, + &zero, + sizeof(zero)); + if (retval < 0) + return retval; + pr_debug("cleared intr_mask\n"); + } + + for (ii = 0; ii < rmi4_data->num_of_intr_regs; ii++) { + if (intr_mask[ii] != 0x00) { + f01_ctrl_reg = rmi4_data->f01_ctrl_base_addr + 1 + ii; + if (set) { + retval = f54->fn_ptr->write(rmi4_data, + f01_ctrl_reg, + &zero, + sizeof(zero)); + if (retval < 0) + return retval; + pr_debug("cleared intr_mask\n"); + } else { + retval = f54->fn_ptr->write(rmi4_data, + f01_ctrl_reg, + &(intr_mask[ii]), + sizeof(intr_mask[ii])); + if (retval < 0) + return retval; + } + } + } + + f01_ctrl_reg = rmi4_data->f01_ctrl_base_addr + 1 + f54->intr_reg_num; + + if (set) { + retval = f54->fn_ptr->write(rmi4_data, + f01_ctrl_reg, + &f54->intr_mask, + 1); + if (retval < 0) + return retval; + pr_debug("set intr_mask = %d\n", f54->intr_mask); + } + + return 0; +} + +#ifdef WATCHDOG_HRTIMER +static void timeout_set_status(struct work_struct *work) +{ + int retval; + unsigned char command; + struct synaptics_rmi4_f54_handle *f54 = container_of(work, + struct synaptics_rmi4_f54_handle, timeout_work); + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + mutex_lock(&f54->status_mutex); + if (f54->status == STATUS_BUSY) { + retval = f54->fn_ptr->read(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: read F54_Cmd0=%d, rc = %d\n", + __func__, command, retval); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read command register\n", + __func__); + } else if (command & COMMAND_GET_REPORT) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Report type not supported by FW\n", + __func__); + } else { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Get report not detected\n", + __func__); + } + f54->status = -ETIMEDOUT; + f54->report_type = INVALID_REPORT_TYPE; + f54->report_size = 0; + set_interrupt(f54, false); + __pm_relax(&f54->test_wake_lock); + } + mutex_unlock(&f54->status_mutex); + + return; +} + +static enum hrtimer_restart get_report_timeout(struct hrtimer *timer) +{ + struct synaptics_rmi4_f54_handle *f54 = container_of(timer, + struct synaptics_rmi4_f54_handle, watchdog); + + schedule_work(&(f54->timeout_work)); + + return HRTIMER_NORESTART; +} +#endif + +#ifdef RAW_HEX +static void print_raw_hex_report(struct synaptics_rmi4_f54_handle *f54) +{ + unsigned int ii; + + pr_info("%s: Report data (raw hex)\n", __func__); + + switch (f54->report_type) { + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_HIGH_RESISTANCE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP_MIN_MAX: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_COUPLING_COMP: + case F54_SENSOR_SPEED: + case F54_ADC_RANGE: + for (ii = 0; ii < f54->report_size; ii += 2) { + pr_info("%03d: 0x%02x%02x\n", + ii / 2, + f54->report_data[ii + 1], + f54->report_data[ii]); + } + break; + default: + for (ii = 0; ii < f54->report_size; ii++) + pr_info("%03d: 0x%02x\n", ii, f54->report_data[ii]); + break; + } + + return; +} +#endif + +#ifdef HUMAN_READABLE +static void print_image_report(struct synaptics_rmi4_f54_handle *f54) +{ + unsigned int ii; + unsigned int jj; + short *report_data; + + switch (f54->report_type) { + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_COUPLING_COMP: + pr_info("%s: Report data (image)\n", __func__); + + report_data = (short *)f54->report_data; + + for (ii = 0; ii < f54->rmi4_data->num_of_tx; ii++) { + for (jj = 0; jj < f54->rmi4_data->num_of_rx; jj++) { + if (*report_data < -64) + pr_cont("."); + else if (*report_data < 0) + pr_cont("-"); + else if (*report_data > 64) + pr_cont("*"); + else if (*report_data > 0) + pr_cont("+"); + else + pr_cont("0"); + + report_data++; + } + pr_info(""); + } + pr_info("%s: End of report\n", __func__); + break; + default: + pr_info("%s: Image not supported for report type %d\n", + __func__, f54->report_type); + } + + return; +} +#endif + +static void free_control_mem(struct synaptics_rmi4_f54_handle *f54) +{ + struct f54_control *control = &f54->control; + + kfree(control->reg_0); + kfree(control->reg_1); + kfree(control->reg_2); + kfree(control->reg_3); + kfree(control->reg_4__6); + kfree(control->reg_7); + kfree(control->reg_8__9); + kfree(control->reg_10); + kfree(control->reg_11); + kfree(control->reg_12__13); + kfree(control->reg_14); + if (control->reg_15) + kfree(control->reg_15->data); + kfree(control->reg_15); + if (control->reg_16) + kfree(control->reg_16->data); + kfree(control->reg_16); + if (control->reg_17) + kfree(control->reg_17->data); + kfree(control->reg_17); + if (control->reg_18) + kfree(control->reg_18->data); + kfree(control->reg_18); + if (control->reg_19) + kfree(control->reg_19->data); + kfree(control->reg_19); + kfree(control->reg_20); + kfree(control->reg_21); + kfree(control->reg_22__26); + kfree(control->reg_27); + kfree(control->reg_28); + kfree(control->reg_29); + kfree(control->reg_30); + kfree(control->reg_31); + kfree(control->reg_32__35); + if (control->reg_36) + kfree(control->reg_36->data); + kfree(control->reg_36); + if (control->reg_37) + kfree(control->reg_37->data); + kfree(control->reg_37); + if (control->reg_38) + kfree(control->reg_38->data); + kfree(control->reg_38); + if (control->reg_39) + kfree(control->reg_39->data); + kfree(control->reg_39); + if (control->reg_40) + kfree(control->reg_40->data); + kfree(control->reg_40); + if (control->reg_89) + kfree(control->reg_89->data); + kfree(control->reg_89); + kfree(control->reg_93); + if (control->reg_95) + kfree(control->reg_95->data); + kfree(control->reg_95); + kfree(control->reg_99); + kfree(control->reg_107); + kfree(control->reg_113); + kfree(control->reg_116); + kfree(control->reg_137); + kfree(control->reg_145); + kfree(control->reg_146); + kfree(control->reg_242); + kfree(control->reg_269); + + return; +} + +static void free_data_mem(struct synaptics_rmi4_f54_handle *f54) +{ + struct f54_data *data = &f54->data; + + kfree(data->reg_4); + kfree(data->reg_6); + kfree(data->reg_7_0); + kfree(data->reg_7_1); + kfree(data->reg_8); + kfree(data->reg_9); + kfree(data->reg_10); + kfree(data->reg_14); + kfree(data->reg_16); + kfree(data->reg_17); +} + +static void remove_sysfs(struct synaptics_rmi4_f54_handle *f54) +{ + int reg_num; + + sysfs_remove_bin_file(&f54->f54_kdata->kobj, f54->f54_kdata->dev_report_data); + + sysfs_remove_group(&f54->f54_kdata->kobj, &attr_group); + + for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs); reg_num++) + sysfs_remove_group(&f54->f54_kdata->kobj, &attrs_ctrl_regs[reg_num]); + + for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_data_regs); reg_num++) + sysfs_remove_group(&f54->f54_kdata->kobj, &attrs_data_regs[reg_num]); + + kobject_put(&f54->f54_kdata->kobj); + wait_for_completion(&f54->f54_kdata->kobj_unregistered); + kfree(f54->f54_kdata->dev_report_data); + kfree(f54->f54_kdata); + + return; +} + +static int synaptics_rmi4_f54_reset(struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + mutex_lock(&f54->status_mutex); + + rmi4_data->irq_enable(rmi4_data, false); + + retval = rmi4_data->reset_device(rmi4_data); + + rmi4_data->irq_enable(rmi4_data, true); + + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to issue reset command, error = %d\n", + __func__, retval); + return retval; + } + + f54->status = STATUS_IDLE; + + mutex_unlock(&f54->status_mutex); + + return 0; +} + +static ssize_t synaptics_rmi4_f54_reset_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned int reset; + + if (sscanf(buf, "%u", &reset) != 1) + return -EINVAL; + + if (reset != 1) + return -EINVAL; + + retval = synaptics_rmi4_f54_reset(f54); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t synaptics_rmi4_f54_status_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", f54->status); +} + +static ssize_t synaptics_rmi4_f54_report_size_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->report_size); +} + +static ssize_t synaptics_rmi4_f54_num_of_mapped_rx_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->rmi4_data->num_of_rx); +} + +static ssize_t synaptics_rmi4_f54_num_of_mapped_tx_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->rmi4_data->num_of_tx); +} + +static ssize_t synaptics_rmi4_f54_no_auto_cal_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->no_auto_cal); +} + +static ssize_t synaptics_rmi4_f54_no_auto_cal_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned char data; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting > 1) + return -EINVAL; + + retval = f54->fn_ptr->read(rmi4_data, + f54->control_base_addr, + &data, + sizeof(data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read control register\n", + __func__); + return retval; + } + + if ((data & NO_AUTO_CAL_MASK) == setting) + return count; + + data = (data & ~NO_AUTO_CAL_MASK) | (data & NO_AUTO_CAL_MASK); + + retval = f54->fn_ptr->write(rmi4_data, + f54->control_base_addr, + &data, + sizeof(data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write control register\n", + __func__); + return retval; + } + + f54->no_auto_cal = (setting == 1); + + return count; +} + +static ssize_t synaptics_rmi4_f54_report_type_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", f54->report_type); +} + +static int synaptics_rmi4_f54_report_type_set( + struct synaptics_rmi4_f54_handle *f54, + enum f54_report_types report_type) +{ + unsigned char data; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + int retval, report_type_valid = is_report_type_valid(report_type); + + if (!report_type_valid) { + f54->report_type = INVALID_REPORT_TYPE; + dev_err(&rmi4_data->i2c_client->dev, + "%s: Requested report type unsupported\n", __func__); + return -EINVAL; + } + + f54->report_type = report_type; + pr_debug("requested report type = %d\n", report_type); + + mutex_lock(&f54->status_mutex); + f54->report_type = report_type; + data = (unsigned char)report_type; + retval = f54->fn_ptr->write(rmi4_data, + f54->data_base_addr, + &data, + sizeof(data)); + mutex_unlock(&f54->status_mutex); + + if (retval < 0) + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write report type\n", __func__); + return 0; +} + +static ssize_t synaptics_rmi4_f54_report_type_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (f54->status != STATUS_BUSY) { + retval = synaptics_rmi4_f54_report_type_set(f54, + (enum f54_report_types)setting); + + if (retval < 0) + return retval; + + return count; + } else { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Previous get report still ongoing\n", + __func__); + return -EINVAL; + } +} + +static ssize_t synaptics_rmi4_f54_user_report_type1_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->user_report_type1); +} + +static ssize_t synaptics_rmi4_f54_user_report_type2_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->user_report_type2); +} + +static ssize_t synaptics_rmi4_f54_fifoindex_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + int retval; + unsigned char data[2]; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = f54->fn_ptr->read(rmi4_data, + f54->data_base_addr + DATA_REPORT_INDEX_OFFSET, + data, + sizeof(data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read data registers\n", + __func__); + return retval; + } + + batohs(&f54->fifoindex, data); + + return snprintf(buf, PAGE_SIZE, "%u\n", f54->fifoindex); +} +static ssize_t synaptics_rmi4_f54_fifoindex_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned char data[2]; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + f54->fifoindex = setting; + + hstoba(data, (unsigned short)setting); + + retval = f54->fn_ptr->write(rmi4_data, + f54->data_base_addr + DATA_REPORT_INDEX_OFFSET, + data, + sizeof(data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write data registers\n", + __func__); + return retval; + } + + return count; +} + +#if defined(REPORT_STATUS_POLLING) +static int test_wait_for_command_completion( + struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + unsigned char value; + unsigned char timeout_count; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + timeout_count = 0; + do { + retval = f54->fn_ptr->read(rmi4_data, + f54->command_base_addr, + &value, + sizeof(value)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read command register\n", + __func__); + return retval; + } + + if (value == 0x00) + break; + + msleep(100); + timeout_count++; + } while (timeout_count < STATUS_WORK_INTERVAL); + + if (timeout_count == STATUS_WORK_INTERVAL) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Timed out waiting for command completion\n", + __func__); + return -ETIMEDOUT; + } + + return 0; +} +#endif + +ssize_t send_get_report_command(struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + unsigned char command = (unsigned char)COMMAND_GET_REPORT; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + mutex_lock(&f54->status_mutex); + + if (f54->status != STATUS_IDLE) { + if (f54->status != STATUS_BUSY) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Invalid status (%d)\n", + __func__, f54->status); + } else { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Previous get report still ongoing\n", + __func__); + } + mutex_unlock(&f54->status_mutex); + return -EBUSY; + } + + __pm_stay_awake(&f54->test_wake_lock); +#if !defined(REPORT_STATUS_POLLING) + set_interrupt(f54, true); +#endif + f54->status = STATUS_BUSY; + + retval = f54->fn_ptr->write(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + + /* unlock status mutex to allow jumping to error_exit label */ + mutex_unlock(&f54->status_mutex); + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: sent get report command\n", __func__); + + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write get report command\n", + __func__); + goto error_exit; + } + +#if defined(REPORT_STATUS_POLLING) + /* if report status control method is polling */ + pr_debug("will be polling test report status\n"); + retval = test_wait_for_command_completion(f54); + if (retval < 0) + goto error_exit; + + /* invoke status work immediately */ + schedule_delayed_work(&f54->status_work, 0); +#else +#ifdef WATCHDOG_HRTIMER + hrtimer_start(&f54->watchdog, + ktime_set(WATCHDOG_TIMEOUT_S, 0), + HRTIMER_MODE_REL); +#endif +#endif +out: + return retval; + +error_exit: + mutex_lock(&f54->status_mutex); +#if !defined(REPORT_STATUS_POLLING) + set_interrupt(f54, false); +#endif + __pm_relax(&f54->test_wake_lock); + f54->status = retval; + mutex_unlock(&f54->status_mutex); + + goto out; +} + +static ssize_t synaptics_rmi4_f54_get_report_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return -EINVAL; + + if (!is_report_type_valid(f54->report_type)) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Invalid report type\n", + __func__); + return -EINVAL; + } + + retval = send_get_report_command(f54); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t synaptics_rmi4_f54_force_update_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + int retval; + unsigned char data[1]; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = f54->fn_ptr->read(rmi4_data, + f54->command_base_addr, + data, + sizeof(data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read command 0 register\n", + __func__); + return retval; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", + (data[0] & COMMAND_FORCE_UPDATE) == COMMAND_FORCE_UPDATE); +} + +static ssize_t synaptics_rmi4_f54_force_update_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned char command; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return count; + + command = (unsigned char)COMMAND_FORCE_UPDATE; + + if (f54->status == STATUS_BUSY) + return -EBUSY; + + retval = f54->fn_ptr->write(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write force update command\n", + __func__); + return retval; + } + + return count; +} + +static ssize_t synaptics_rmi4_f54_enter_in_cell_test_mode_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + int retval; + unsigned char data[1]; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = f54->fn_ptr->read(rmi4_data, + f54->command_base_addr, + data, + sizeof(data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read command 0 register\n", + __func__); + return retval; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", + (data[0] & COMMAND_ENTER_IN_CELL_TESTMODE) == + COMMAND_ENTER_IN_CELL_TESTMODE); +} + + +static ssize_t synaptics_rmi4_f54_enter_in_cell_test_mode_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned char command; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return count; + + command = (unsigned char)COMMAND_ENTER_IN_CELL_TESTMODE; + + if (f54->status == STATUS_BUSY) + return -EBUSY; + + retval = f54->fn_ptr->write(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write Enter In-Cell Test Mode command\n", + __func__); + return retval; + } + + return count; +} + + +static ssize_t synaptics_rmi4_f54_force_cal_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned char command; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return count; + + command = (unsigned char)COMMAND_FORCE_CAL; + + if (f54->status == STATUS_BUSY) + return -EBUSY; + + retval = f54->fn_ptr->write(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write force cal command\n", + __func__); + return retval; + } + + return count; +} + +simple_show_func_unsigned(query, num_of_rx_electrodes) +simple_show_func_unsigned(query, num_of_tx_electrodes) +simple_show_func_unsigned(query, has_image16) +simple_show_func_unsigned(query, has_image8) +simple_show_func_unsigned(query, has_baseline) +simple_show_func_unsigned(query, clock_rate) +simple_show_func_unsigned(query, touch_controller_family) +simple_show_func_unsigned(query, has_pixel_touch_threshold_adjustment) +simple_show_func_unsigned(query, has_sensor_assignment) +simple_show_func_unsigned(query, has_interference_metric) +simple_show_func_unsigned(query, has_sense_frequency_control) +simple_show_func_unsigned(query, has_firmware_noise_mitigation) +simple_show_func_unsigned(query, has_two_byte_report_rate) +simple_show_func_unsigned(query, has_one_byte_report_rate) +simple_show_func_unsigned(query, has_relaxation_control) +simple_show_func_unsigned(query, curve_compensation_mode) +simple_show_func_unsigned(query, has_iir_filter) +simple_show_func_unsigned(query, has_cmn_removal) +simple_show_func_unsigned(query, has_cmn_maximum) +simple_show_func_unsigned(query, has_touch_hysteresis) +simple_show_func_unsigned(query, has_edge_compensation) +simple_show_func_unsigned(query, has_per_frequency_noise_control) +simple_show_func_unsigned(query, has_enhanced_stretch) +simple_show_func_unsigned(query, has_force_fast_relaxation) +simple_show_func_unsigned(query, has_multi_metric_state_machine) +simple_show_func_unsigned(query, has_signal_clarity) +simple_show_func_unsigned(query, has_variance_metric) +simple_show_func_unsigned(query, has_0d_relaxation_control) +simple_show_func_unsigned(query, has_0d_acquisition_control) +simple_show_func_unsigned(query, has_status) +simple_show_func_unsigned(query, has_slew_metric) +simple_show_func_unsigned(query, has_h_blank) +simple_show_func_unsigned(query, has_v_blank) +simple_show_func_unsigned(query, has_long_h_blank) +simple_show_func_unsigned(query, has_startup_fast_relaxation) +simple_show_func_unsigned(query, has_esd_control) +simple_show_func_unsigned(query, has_noise_mitigation2) +simple_show_func_unsigned(query, has_noise_state) +simple_show_func_unsigned(query, has_energy_ratio_relaxation) +simple_show_func_unsigned(query12, number_of_sensing_frequencies) +simple_show_func_unsigned(query16, has_data17) +simple_show_func_unsigned(query27, has_ctrl113) +simple_show_func_unsigned(query36, has_ctrl145) +simple_show_func_unsigned(query36, has_ctrl146) +simple_show_func_unsigned(query17, q17_num_of_sense_freqs) +show_store_func_unsigned(data, reg_4, d4_inhibit_freq_shift) +show_store_func_unsigned(data, reg_4, d4_baseline_sel) +show_store_func_unsigned(data, reg_4, d4_sense_freq_sel) +show_store_func_unsigned(data, reg_16, d16_freq_scan_im_lsb) +show_store_func_unsigned(data, reg_16, d16_freq_scan_im_msb) +show_store_func_unsigned(data, reg_17, d17_inhibit_freq_shift) +show_store_func_unsigned(data, reg_17, d17_freq) +show_func_unsigned(data, reg_6, d6_interference_metric_lsb) +show_func_unsigned(data, reg_6, d6_interference_metric_msb) +show_func_unsigned(data, reg_7_0, d7_current_report_rate_lsb) +show_func_unsigned(data, reg_7_1, d7_current_report_rate_msb) +show_func_unsigned(data, reg_8, d8_variance_metric_lsb) +show_func_unsigned(data, reg_8, d8_variance_metric_msb) +show_func_unsigned(data, reg_9, d9_averaged_im_lsb) +show_func_unsigned(data, reg_9, d9_averaged_im_msb) +show_func_unsigned(data, reg_10, d10_noise_state) +show_func_unsigned(data, reg_14, d14_cid_im_lsb) +show_func_unsigned(data, reg_14, d14_cid_im_msb) + +show_store_func_unsigned(control, reg_0, no_relax) +show_store_func_unsigned(control, reg_0, no_scan) +show_store_func_unsigned(control, reg_1, bursts_per_cluster) +show_store_func_unsigned(control, reg_2, saturation_cap) +show_store_func_unsigned(control, reg_3, pixel_touch_threshold) +show_store_func_unsigned(control, reg_4__6, rx_feedback_cap) +show_store_func_unsigned(control, reg_4__6, low_ref_cap) +show_store_func_unsigned(control, reg_4__6, low_ref_feedback_cap) +show_store_func_unsigned(control, reg_4__6, low_ref_polarity) +show_store_func_unsigned(control, reg_4__6, high_ref_cap) +show_store_func_unsigned(control, reg_4__6, high_ref_feedback_cap) +show_store_func_unsigned(control, reg_4__6, high_ref_polarity) +show_store_func_unsigned(control, reg_7, cbc_cap) +show_store_func_unsigned(control, reg_7, cbc_polarity) +show_store_func_unsigned(control, reg_7, cbc_tx_carrier_selection) +show_store_func_unsigned(control, reg_8__9, integration_duration) +show_store_func_unsigned(control, reg_8__9, reset_duration) +show_store_func_unsigned(control, reg_10, noise_sensing_bursts_per_image) +show_store_func_unsigned(control, reg_12__13, slow_relaxation_rate) +show_store_func_unsigned(control, reg_12__13, fast_relaxation_rate) +show_store_func_unsigned(control, reg_14, rxs_on_xaxis) +show_store_func_unsigned(control, reg_14, curve_comp_on_txs) +show_store_func_unsigned(control, reg_20, disable_noise_mitigation) +show_store_func_unsigned(control, reg_21, freq_shift_noise_threshold) +show_store_func_unsigned(control, reg_22__26, medium_noise_threshold) +show_store_func_unsigned(control, reg_22__26, high_noise_threshold) +show_store_func_unsigned(control, reg_22__26, noise_density) +show_store_func_unsigned(control, reg_22__26, frame_count) +show_store_func_unsigned(control, reg_27, iir_filter_coef) +show_store_func_unsigned(control, reg_28, quiet_threshold) +show_store_func_unsigned(control, reg_29, cmn_filter_disable) +show_store_func_unsigned(control, reg_30, cmn_filter_max) +show_store_func_unsigned(control, reg_31, touch_hysteresis) +show_store_func_unsigned(control, reg_32__35, rx_low_edge_comp) +show_store_func_unsigned(control, reg_32__35, rx_high_edge_comp) +show_store_func_unsigned(control, reg_32__35, tx_low_edge_comp) +show_store_func_unsigned(control, reg_32__35, tx_high_edge_comp) + +show_replicated_func_unsigned(control, reg_15, sensor_rx_assignment) +show_replicated_func_unsigned(control, reg_16, sensor_tx_assignment) +show_replicated_func_unsigned(control, reg_17, disable) +show_replicated_func_unsigned(control, reg_17, filter_bandwidth) +show_replicated_func_unsigned(control, reg_19, stretch_duration) +show_replicated_func_unsigned(control, reg_38, noise_control_1) +show_replicated_func_unsigned(control, reg_39, noise_control_2) +show_replicated_func_unsigned(control, reg_40, noise_control_3) + +show_store_replicated_func_unsigned(control, reg_36, axis1_comp) +show_store_replicated_func_unsigned(control, reg_37, axis2_comp) + +show_store_subpkt_func_unsigned(control, reg_89, 0, c89_cid_sel_opt) +show_store_subpkt_func_unsigned(control, reg_89, 0, c89_cid_voltage_sel) +show_store_subpkt_func_unsigned(control, reg_89, 0, + c89_cid_im_noise_threshold_lsb) +show_store_subpkt_func_unsigned(control, reg_89, 0, + c89_cid_im_noise_threshold_msb) + +show_store_subpkt_func_unsigned(control, reg_89, 1, c89_fnm_pixel_touch_mult) +show_store_subpkt_func_unsigned(control, reg_89, 1, c89_freq_scan_threshold_lsb) +show_store_subpkt_func_unsigned(control, reg_89, 1, c89_freq_scan_threshold_msb) +show_store_subpkt_func_unsigned(control, reg_89, 1, c89_quiet_im_threshold_lsb) +show_store_subpkt_func_unsigned(control, reg_89, 1, c89_quiet_im_threshold_msb) + +show_store_subpkt_func_unsigned(control, reg_89, 2, c89_rail_sel_opt); +show_store_subpkt_func_unsigned(control, reg_89, 2, + c89_rail_im_noise_threshold_lsb); +show_store_subpkt_func_unsigned(control, reg_89, 2, + c89_rail_im_noise_threshold_msb); +show_store_subpkt_func_unsigned(control, reg_89, 2, + c89_rail_im_quiet_threshold_lsb); +show_store_subpkt_func_unsigned(control, reg_89, 2, + c89_rail_im_quiet_threshold_msb); + +show_store_func_unsigned(control, reg_93, c93_freq_shift_noise_threshold_lsb) +show_store_func_unsigned(control, reg_93, c93_freq_shift_noise_threshold_msb) + +show_store_replicated_func_unsigned(control, reg_95, c95_disable) +show_store_replicated_func_unsigned(control, reg_95, c95_filter_bw) +show_store_replicated_func_unsigned(control, reg_95, c95_first_burst_length_lsb) +show_store_replicated_func_unsigned(control, reg_95, c95_first_burst_length_msb) +show_store_replicated_func_unsigned(control, reg_95, c95_addl_burst_length_lsb) +show_store_replicated_func_unsigned(control, reg_95, c95_addl_burst_length_msb) +show_store_replicated_func_unsigned(control, reg_95, c95_i_stretch) +show_store_replicated_func_unsigned(control, reg_95, c95_r_stretch) +show_store_replicated_func_unsigned(control, reg_95, c95_noise_control1) +show_store_replicated_func_unsigned(control, reg_95, c95_noise_control2) +show_store_replicated_func_unsigned(control, reg_95, c95_noise_control3) +show_store_replicated_func_unsigned(control, reg_95, c95_noise_control4) + +show_store_func_unsigned(control, reg_99, c99_int_dur_lsb) +show_store_func_unsigned(control, reg_99, c99_int_dur_msb) +show_store_func_unsigned(control, reg_99, c99_reset_dur) + +show_store_func_unsigned(control, reg_107, c107_abs_int_dur) +show_store_func_unsigned(control, reg_107, c107_abs_reset_dur) +show_store_func_unsigned(control, reg_107, c107_abs_filter_bw) +show_store_func_unsigned(control, reg_107, c107_abs_rstretch) +show_store_func_unsigned(control, reg_107, c107_abs_burst_count_1) +show_store_func_unsigned(control, reg_107, c107_abs_burst_count_2) +show_store_func_unsigned(control, reg_107, c107_abs_stretch_dur) +show_store_func_unsigned(control, reg_107, c107_abs_adc_clock_div) +show_store_func_unsigned(control, reg_107, c107_abs_sub_burtst_size) +show_store_func_unsigned(control, reg_107, c107_abs_trigger_delay) + +show_store_func_unsigned(control, reg_113, c113_en_hybrid_on_tx) +show_store_func_unsigned(control, reg_113, c113_en_hybrid_on_rx) +show_store_func_unsigned(control, reg_113, c113_tx_consistency_chk) +show_store_func_unsigned(control, reg_113, c113_rx_consistency_chk) +show_store_func_unsigned(control, reg_113, c113_en_gradient_baseline) +show_store_func_unsigned(control, reg_113, c113_consistency_thresh) +show_store_func_unsigned(control, reg_113, c113_tx_obj_thresh) +show_store_func_unsigned(control, reg_113, c113_rx_obj_thresh) +show_store_func_unsigned(control, reg_113, c113_tx_thermal_update_int_lsb) +show_store_func_unsigned(control, reg_113, c113_tx_thermal_update_int_msb) +show_store_func_unsigned(control, reg_113, c113_rx_thermal_update_int_lsb) +show_store_func_unsigned(control, reg_113, c113_rx_thermal_update_int_msb) +show_store_func_unsigned(control, reg_113, c113_tx_negative_thresh) +show_store_func_unsigned(control, reg_113, c113_rx_negative_thresh) + +show_store_func_unsigned(control, reg_116, c116_im_thresh) +show_store_func_unsigned(control, reg_116, c116_debounce) +show_store_func_unsigned(control, reg_116, c116_quiet_im) +show_store_func_unsigned(control, reg_116, c116_filter) + +show_store_func_unsigned(control, reg_137, c137_cmnr_adjust) + +show_store_func_unsigned(control, reg_145, c145_bursts_per_cluster_rx) +show_store_func_unsigned(control, reg_145, c145_bursts_per_cluster_tx) + +show_store_func_unsigned(control, reg_146, c146_int_dur_lsb) +show_store_func_unsigned(control, reg_146, c146_int_dur_msb) +show_store_func_unsigned(control, reg_146, c146_reset_dur) +show_store_func_unsigned(control, reg_146, c146_filter_bandwidth) +show_store_func_unsigned(control, reg_146, c146_stretch_dur) +show_store_func_unsigned(control, reg_146, c146_burst_count1) +show_store_func_unsigned(control, reg_146, c146_burst_count2) +show_store_func_unsigned(control, reg_146, c146_stretch_dur2) +show_store_func_unsigned(control, reg_146, c146_adc_clock_div) + +show_store_func_unsigned(control, reg_242, c242_cbc_servo_disable) +show_store_func_unsigned(control, reg_269, c269_bsc_calibration_en) + +simple_show_func_unsigned(query_f55_0_2, f55_q2_has_single_layer_multitouch) +show_func_unsigned(control, reg_0_f55, f55_c0_receivers_on_x) +show_func_unsigned(control, reg_8_f55, f55_c8_pattern_type) + +static ssize_t synaptics_rmi4_f54_burst_count_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + int retval; + int size = 0; + unsigned char ii; + unsigned char *temp; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + mutex_lock(&f54->control_mutex); + + retval = f54->fn_ptr->read(rmi4_data, + f54->control.reg_17->address, + (unsigned char *)f54->control.reg_17->data, + f54->control.reg_17->length); + if (retval < 0) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Failed to read control reg_17\n", + __func__); + } + + retval = f54->fn_ptr->read(rmi4_data, + f54->control.reg_18->address, + (unsigned char *)f54->control.reg_18->data, + f54->control.reg_18->length); + if (retval < 0) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Failed to read control reg_18\n", + __func__); + } + + mutex_unlock(&f54->control_mutex); + + temp = buf; + + for (ii = 0; ii < f54->control.reg_17->length; ii++) { + retval = snprintf(temp, PAGE_SIZE - size, "%u ", (1 << 8) * + f54->control.reg_17->data[ii].burst_count_b8__10 + + f54->control.reg_18->data[ii].burst_count_b0__7); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Faild to write output\n", + __func__); + return retval; + } + size += retval; + temp += retval; + } + + retval = snprintf(temp, PAGE_SIZE - size, "\n"); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Faild to write null terminator\n", + __func__); + return retval; + } + + return size + retval; +} + +static ssize_t synaptics_rmi4_f54_user_get_report_show( + struct synaptics_rmi4_f54_handle *f54, + char *buf, enum f54_report_types report_type) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + if (f54->status != STATUS_IDLE) { + if (f54->status == STATUS_BUSY) + dev_err(&rmi4_data->i2c_client->dev, + "%s: WARNING: Resetting in busy state\n", + __func__); + + retval = synaptics_rmi4_f54_reset(f54); + if (retval < 0) + goto out; + } + + retval = synaptics_rmi4_f54_report_type_set(f54, report_type); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to set report type\n", __func__); + goto out; + } + + retval = send_get_report_command(f54); + +out: + retval = scnprintf(buf, PAGE_SIZE, "%d\n", retval); + return retval; +} + +static ssize_t synaptics_rmi4_f54_user_get_report1_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return synaptics_rmi4_f54_user_get_report_show(f54, + buf, f54->user_report_type1); +} + +static ssize_t synaptics_rmi4_f54_user_get_report2_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return synaptics_rmi4_f54_user_get_report_show(f54, + buf, f54->user_report_type2); +} + +static ssize_t synaptics_rmi4_f54_data_read(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + struct synaptics_rmi4_f54_handle *f54 = attributes->private; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + mutex_lock(&f54->data_mutex); + + if (!f54->report_data || !f54->report_size) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Report type %d data not available\n", + __func__, f54->report_type); + mutex_unlock(&f54->data_mutex); + return -EINVAL; + } + if (pos > f54->report_size || pos < 0) { + mutex_unlock(&f54->data_mutex); + return 0; + } + + count = min_t(loff_t, count, f54->report_size - pos); + memcpy(buf, f54->report_data + pos, count); + mutex_unlock(&f54->data_mutex); + pr_debug("copied %zu bytes to the caller's buffer\n", count); + return count; +} + +static int synaptics_rmi4_f54_set_sysfs(struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + int reg_num; + int dreg_num; + struct bin_attribute *dev_report_data; + struct f54_kobj_data *f54_kobj_data; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + f54_kobj_data = kzalloc(sizeof(*f54_kobj_data), GFP_KERNEL); + if (!f54_kobj_data) + goto exit; + + dev_report_data = kzalloc(sizeof(struct bin_attribute), GFP_KERNEL); + if (!dev_report_data) + goto exit_1; + + dev_report_data->attr.name = "report_data"; + dev_report_data->attr.mode = S_IRUGO; + dev_report_data->read = synaptics_rmi4_f54_data_read; + dev_report_data->private = (void *)f54; + + f54_kobj_data->f54 = f54; + f54_kobj_data->dev_report_data = dev_report_data; + + init_completion(&f54_kobj_data->kobj_unregistered); + f54->f54_kdata = f54_kobj_data; + + retval = kobject_init_and_add(&f54_kobj_data->kobj, &f54_ktype, + &rmi4_data->i2c_client->dev.kobj, "f54"); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs directory\n", + __func__); + goto exit_1; + } + + retval = sysfs_create_bin_file(&f54_kobj_data->kobj, dev_report_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs bin file\n", + __func__); + goto exit_2; + } + + retval = sysfs_create_group(&f54_kobj_data->kobj, &attr_group); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + goto exit_3; + } + + for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs); reg_num++) { + if (attrs_ctrl_regs_exist[reg_num]) { + retval = sysfs_create_group(&f54_kobj_data->kobj, + &attrs_ctrl_regs[reg_num]); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + goto exit_4; + } + } + } + + for (dreg_num = 0; dreg_num < ARRAY_SIZE(attrs_data_regs); dreg_num++) { + if (attrs_data_regs_exist[dreg_num]) { + retval = sysfs_create_group(&f54_kobj_data->kobj, + &attrs_data_regs[dreg_num]); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs attributes for data register %d\n", + __func__, dreg_num); + goto exit_5; + } + } + } + + return 0; + +exit_5: + for (dreg_num--; dreg_num >= 0; dreg_num--) + sysfs_remove_group(&f54_kobj_data->kobj, &attrs_data_regs[dreg_num]); + +exit_4: + for (reg_num--; reg_num >= 0; reg_num--) + sysfs_remove_group(&f54_kobj_data->kobj, &attrs_ctrl_regs[reg_num]); + + sysfs_remove_group(&f54_kobj_data->kobj, &attr_group); + +exit_3: + sysfs_remove_bin_file(&f54_kobj_data->kobj, dev_report_data); + +exit_2: + kobject_put(&f54_kobj_data->kobj); + +exit_1: + kfree(dev_report_data); + kfree(f54->f54_kdata); +exit: + return -ENODEV; +} + +/* + * Fill in base register address and offset of F54 + * control registers to allow run time patching + */ +static int synaptics_rmi4_scan_f54_ctrl_reg_info( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *f54_ctrl_regs) +{ + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + int ii, error = f54_ctrl_regs->nr_regs; + unsigned char *data; + struct synaptics_rmi4_packet_reg *reg; + struct synaptics_rmi4_subpkt *subpkt; + + f54_ctrl_regs->base_addr = f54->control_base_addr; + for (ii = 0; ii < f54_ctrl_regs->nr_regs; ii++) { + reg = &f54_ctrl_regs->regs[ii]; + subpkt = ®->subpkt[0]; + if (reg->r_number == 0 && f54->control.reg_0) { + data = kzalloc(sizeof(f54->control.reg_0->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->control.reg_0->address - + f54->control_base_addr; + reg->size = sizeof(f54->control.reg_0->data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + + if (reg->r_number == 2 && f54->control.reg_2) { + data = kzalloc(sizeof(f54->control.reg_2->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->control.reg_2->address - + f54->control_base_addr; + reg->size = 2; + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + + if (reg->r_number == 95 && f54->control.reg_95) { + int jj, num_of_subpkts; + data = kzalloc(f54->control.reg_95->length, GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->control.reg_95->address - + f54->control_base_addr; + reg->size = f54->control.reg_95->length; + reg->data = data; + /* not going over the number of predefined subpackets */ + num_of_subpkts = min((int)reg->nr_subpkts, + (int)(f54->control.reg_95->length / + subpkt->size)); + for (jj = 0; jj < num_of_subpkts; jj++) { + subpkt = ®->subpkt[jj]; + subpkt->present = true; + subpkt->offset = jj * subpkt->size; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: subpkt[%d], off = %d, sz = %d\n", __func__, + jj, subpkt->offset, subpkt->size); + } + error--; + } + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: reg[%d], addr = 0x%x, off = %d, sz = %d\n", __func__, + reg->r_number, f54->control_base_addr+reg->offset, + reg->offset, reg->size); + } + dev_dbg(&rmi4_data->i2c_client->dev, "%s: rc = %d\n", __func__, error); + return error; +} + +/* + * Fill in base register address and offset of F54 + * command register to allow run time patching + */ +static int synaptics_rmi4_scan_f54_cmd_reg_info( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *f54_cmd_regs) +{ + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + unsigned char *data; + struct synaptics_rmi4_packet_reg *reg = f54_cmd_regs->regs; + struct synaptics_rmi4_subpkt *subpkt = ®->subpkt[0]; + + f54_cmd_regs->base_addr = f54->command_base_addr; + data = kzalloc(1, GFP_KERNEL); + if (!data) + return -ENOMEM; + reg->offset = 0; + reg->size = 1; + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: reg[%d], addr = 0x%x, off = %d, sz = %d\n", __func__, + reg->r_number, f54->command_base_addr+reg->offset, + reg->offset, reg->size); + return 0; +} + +/* + * Fill in base register address and offset of F54 query + * register 12 to allow run time access + */ +static int synaptics_rmi4_scan_f54_query_reg_info( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *f54_query_regs) +{ + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + struct synaptics_rmi4_packet_reg *reg = f54_query_regs->regs; + struct synaptics_rmi4_subpkt *subpkt = ®->subpkt[0]; + unsigned char *data = kzalloc(sizeof(f54->query12.data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + f54_query_regs->base_addr = f54->query_base_addr; + /* need an offset off of base address here */ + reg->offset = 12; + reg->size = sizeof(f54->query12.data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: reg[%d], addr = 0x%x, off = %d, sz = %d\n", __func__, + reg->r_number, f54->query_base_addr+reg->offset, + reg->offset, reg->size); + return 0; +} + +/* + * Fill in base register address and offset of F54 data + * registers 10 & 17 to allow run time access + */ +static int synaptics_rmi4_scan_f54_data_reg_info( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *f54_data_regs) +{ + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + int ii, error = f54_data_regs->nr_regs; + unsigned char *data; + struct synaptics_rmi4_subpkt *subpkt; + struct synaptics_rmi4_packet_reg *reg; + + f54_data_regs->base_addr = f54->data_base_addr; + for (ii = 0; ii < f54_data_regs->nr_regs; ii++) { + reg = &f54_data_regs->regs[ii]; + if (reg->r_number == 6 && f54->data.reg_6) { + subpkt = ®->subpkt[0]; + data = kzalloc(sizeof(f54->data.reg_6->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->data.reg_6->address - + f54->data_base_addr; + reg->size = sizeof(f54->data.reg_6->data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + + if (reg->r_number == 10 && f54->data.reg_10) { + subpkt = ®->subpkt[0]; + data = kzalloc(sizeof(f54->data.reg_10->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->data.reg_10->address - + f54->data_base_addr; + reg->size = sizeof(f54->data.reg_10->data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + + if (reg->r_number == 14 && f54->data.reg_14) { + subpkt = ®->subpkt[0]; + data = kzalloc(sizeof(f54->data.reg_14->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->data.reg_14->address - + f54->data_base_addr; + reg->size = sizeof(f54->data.reg_14->data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + + if (reg->r_number == 16 && f54->data.reg_16) { + subpkt = ®->subpkt[0]; + data = kzalloc(sizeof(f54->data.reg_16->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->data.reg_16->address - + f54->data_base_addr; + reg->size = sizeof(f54->data.reg_16->data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + + if (reg->r_number == 17 && f54->data.reg_17) { + subpkt = ®->subpkt[0]; + data = kzalloc(sizeof(f54->data.reg_17->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->data.reg_17->address - + f54->data_base_addr; + reg->size = sizeof(f54->data.reg_17->data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: reg[%d], addr = 0x%x, off = %d, sz = %d\n", __func__, + reg->r_number, f54->data_base_addr+reg->offset, + reg->offset, reg->size); + } + dev_dbg(&rmi4_data->i2c_client->dev, "%s: rc = %d\n", __func__, error); + return error ? -ENOSYS : error; +} + +static int synaptics_rmi4_f54_set_ctrl( + struct synaptics_rmi4_f54_handle *f54) +{ + unsigned char length; + unsigned char reg_num = 0; + unsigned short reg_addr = f54->control_base_addr; + struct f54_control *control = &f54->control; + struct f54_query *query = &f54->query; + struct f54_query12 *query12 = &f54->query12; + struct f54_query13 *query13 = &f54->query13; + struct f54_query15 *query15 = &f54->query15; + struct f54_query16 *query16 = &f54->query16; + struct f54_query17 *query17 = &f54->query17; + struct f54_query21 *query21 = &f54->query21; + struct f54_query22 *query22 = &f54->query22; + struct f54_query23 *query23 = &f54->query23; + struct f54_query25 *query25 = &f54->query25; + struct f54_query27 *query27 = &f54->query27; + struct f54_query29 *query29 = &f54->query29; + struct f54_query30 *query30 = &f54->query30; + struct f54_query32 *query32 = &f54->query32; + struct f54_query33 *query33 = &f54->query33; + struct f54_query35 *query35 = &f54->query35; + struct f54_query36 *query36 = &f54->query36; + struct f54_query38 *query38 = &f54->query38; + struct f54_query39 *query39 = &f54->query39; + struct f54_query40 *query40 = &f54->query40; +// struct f54_query41 *query41 = &f54->query41; +// struct f54_query42 *query42 = &f54->query42; + struct f54_query43 *query43 = &f54->query43; + struct f54_query46 *query46 = &f54->query46; + /* BSC control access */ + struct f54_query47 *query47 = &f54->query47; + struct f54_query49 *query49 = &f54->query49; + struct f54_query50 *query50 = &f54->query50; + struct f54_query51 *query51 = &f54->query51; + struct f54_query55 *query55 = &f54->query55; + struct f54_query57 *query57 = &f54->query57; + struct f54_query58 *query58 = &f54->query58; + struct f54_query61 *query61 = &f54->query61; + struct f54_query64 *query64 = &f54->query64; + struct f54_query65 *query65 = &f54->query65; + struct f54_query67 *query67 = &f54->query67; + struct f54_query68 *query68 = &f54->query68; + struct f54_query69 *query69 = &f54->query69; + struct f54_query70 *query70 = &f54->query70; +// struct f54_query71 *query71 = &f54->query71; +// struct f54_query72 *query72 = &f54->query72; +// struct f54_query73 *query73 = &f54->query73; +// struct f54_query74 *query74 = &f54->query74; + struct f54_query75 *query75 = &f54->query75; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + unsigned char num_of_sensing_freqs; + + num_of_sensing_freqs = f54->query12.number_of_sensing_frequencies; + + CTRL_REG_ADD(0, 1, true); + CTRL_REG_ADD(1, 1, ((f54->query.touch_controller_family == 0) || + (f54->query.touch_controller_family == 1))); + CTRL_REG_ADD(2, 2, true); + CTRL_REG_ADD(3, 1, (f54->query.has_pixel_touch_threshold_adjustment == 1)); + CTRL_REG_ADD(4__6, 3, ((f54->query.touch_controller_family == 0) || + (f54->query.touch_controller_family == 1))); + CTRL_REG_ADD(7, 1, (f54->query.touch_controller_family == 1)); + CTRL_REG_ADD(8__9, 3, ((f54->query.touch_controller_family == 0) || + (f54->query.touch_controller_family == 1))); + CTRL_REG_ADD(10, 1, f54->query.has_interference_metric); + CTRL_REG_ADD(11, 2, f54->query.has_ctrl11); + CTRL_REG_ADD(12__13, 2, f54->query.has_relaxation_control); + + /* controls 14 15 16 */ + if (f54->query.has_sensor_assignment == 1) { + attrs_ctrl_regs_exist[reg_num] = true; + control->reg_14 = kzalloc(sizeof(*(control->reg_14)), + GFP_KERNEL); + if (!control->reg_14) + goto exit_no_mem; + control->reg_14->address = reg_addr; + reg_addr += sizeof(control->reg_14->data); + + control->reg_15 = kzalloc(sizeof(*(control->reg_15)), + GFP_KERNEL); + if (!control->reg_15) + goto exit_no_mem; + control->reg_15->length = f54->query.num_of_rx_electrodes; + control->reg_15->data = kzalloc(control->reg_15->length * + sizeof(*(control->reg_15->data)), GFP_KERNEL); + if (!control->reg_15->data) + goto exit_no_mem; + control->reg_15->address = reg_addr; + reg_addr += control->reg_15->length; + + control->reg_16 = kzalloc(sizeof(*(control->reg_16)), + GFP_KERNEL); + if (!control->reg_16) + goto exit_no_mem; + control->reg_16->length = f54->query.num_of_tx_electrodes; + control->reg_16->data = kzalloc(control->reg_16->length * + sizeof(*(control->reg_16->data)), GFP_KERNEL); + if (!control->reg_16->data) + goto exit_no_mem; + control->reg_16->address = reg_addr; + reg_addr += control->reg_16->length; + } + reg_num++; + + /* controls 17 18 19 */ + if (f54->query.has_sense_frequency_control == 1) { + attrs_ctrl_regs_exist[reg_num] = true; + control->reg_17 = kzalloc(sizeof(*(control->reg_17)), + GFP_KERNEL); + if (!control->reg_17) + goto exit_no_mem; + control->reg_17->length = num_of_sensing_freqs; + control->reg_17->data = kzalloc(num_of_sensing_freqs * + sizeof(*(control->reg_17->data)), GFP_KERNEL); + if (!control->reg_17->data) + goto exit_no_mem; + control->reg_17->address = reg_addr; + reg_addr += num_of_sensing_freqs; + + control->reg_18 = kzalloc(sizeof(*(control->reg_18)), + GFP_KERNEL); + if (!control->reg_18) + goto exit_no_mem; + control->reg_18->length = num_of_sensing_freqs; + control->reg_18->data = kzalloc(num_of_sensing_freqs * + sizeof(*(control->reg_18->data)), GFP_KERNEL); + if (!control->reg_18->data) + goto exit_no_mem; + control->reg_18->address = reg_addr; + reg_addr += num_of_sensing_freqs; + + control->reg_19 = kzalloc(sizeof(*(control->reg_19)), + GFP_KERNEL); + if (!control->reg_19) + goto exit_no_mem; + control->reg_19->length = num_of_sensing_freqs; + control->reg_19->data = kzalloc(num_of_sensing_freqs * + sizeof(*(control->reg_19->data)), GFP_KERNEL); + if (!control->reg_19->data) + goto exit_no_mem; + control->reg_19->address = reg_addr; + reg_addr += num_of_sensing_freqs; + } + reg_num++; + + CTRL_REG_ADD(20, 1, true); + CTRL_REG_ADD(21, 2, f54->query.has_sense_frequency_control); + CTRL_REG_ADD(22__26, 7, f54->query.has_firmware_noise_mitigation); + CTRL_REG_ADD(27, 1, f54->query.has_iir_filter); + CTRL_REG_ADD(28, 2, f54->query.has_firmware_noise_mitigation); + CTRL_REG_ADD(29, 1, f54->query.has_cmn_removal); + CTRL_REG_ADD(30, 1, f54->query.has_cmn_maximum); + CTRL_REG_ADD(31, 1, f54->query.has_touch_hysteresis); + CTRL_REG_ADD(32__35, 8, f54->query.has_edge_compensation); + + /* control 36 */ + if ((f54->query.curve_compensation_mode == 1) || + (f54->query.curve_compensation_mode == 2)) { + attrs_ctrl_regs_exist[reg_num] = true; + + if (f54->query.curve_compensation_mode == 1) { + length = max(f54->query.num_of_rx_electrodes, + f54->query.num_of_tx_electrodes); + } else if (f54->query.curve_compensation_mode == 2) { + length = f54->query.num_of_rx_electrodes; + } + + control->reg_36 = kzalloc(sizeof(*(control->reg_36)), + GFP_KERNEL); + if (!control->reg_36) + goto exit_no_mem; + control->reg_36->length = length; + control->reg_36->data = kzalloc(length * + sizeof(*(control->reg_36->data)), GFP_KERNEL); + if (!control->reg_36->data) + goto exit_no_mem; + control->reg_36->address = reg_addr; + reg_addr += length; + } + reg_num++; + + /* control 37 */ + if (f54->query.curve_compensation_mode == 2) { + attrs_ctrl_regs_exist[reg_num] = true; + + control->reg_37 = kzalloc(sizeof(*(control->reg_37)), + GFP_KERNEL); + if (!control->reg_37) + goto exit_no_mem; + control->reg_37->length = f54->query.num_of_tx_electrodes; + control->reg_37->data = kzalloc(control->reg_37->length * + sizeof(*(control->reg_37->data)), GFP_KERNEL); + if (!control->reg_37->data) + goto exit_no_mem; + + control->reg_37->address = reg_addr; + reg_addr += control->reg_37->length; + } + reg_num++; + + /* controls 38 39 40 */ + if (f54->query.has_per_frequency_noise_control == 1) { + attrs_ctrl_regs_exist[reg_num] = true; + + control->reg_38 = kzalloc(sizeof(*(control->reg_38)), + GFP_KERNEL); + if (!control->reg_38) + goto exit_no_mem; + control->reg_38->length = num_of_sensing_freqs; + + control->reg_38->data = kzalloc(num_of_sensing_freqs * + sizeof(*(control->reg_38->data)), GFP_KERNEL); + if (!control->reg_38->data) + goto exit_no_mem; + control->reg_38->address = reg_addr; + reg_addr += num_of_sensing_freqs; + + control->reg_39 = kzalloc(sizeof(*(control->reg_39)), + GFP_KERNEL); + if (!control->reg_39) + goto exit_no_mem; + control->reg_39->length = num_of_sensing_freqs; + + control->reg_39->data = kzalloc(num_of_sensing_freqs * + sizeof(*(control->reg_39->data)), GFP_KERNEL); + if (!control->reg_39->data) + goto exit_no_mem; + control->reg_39->address = reg_addr; + reg_addr += num_of_sensing_freqs; + + control->reg_40 = kzalloc(sizeof(*(control->reg_40)), + GFP_KERNEL); + if (!control->reg_40) + goto exit_no_mem; + control->reg_40->length = num_of_sensing_freqs; + + control->reg_40->data = kzalloc(num_of_sensing_freqs * + sizeof(*(control->reg_40->data)), GFP_KERNEL); + if (!control->reg_40->data) + goto exit_no_mem; + control->reg_40->address = reg_addr; + reg_addr += num_of_sensing_freqs; + } + reg_num++; + + CTRL_REG_PRESENCE(41, 1, query->has_signal_clarity); + CTRL_REG_PRESENCE(42, 2, query->has_variance_metric); + CTRL_REG_PRESENCE(43, 2, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(44, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(45, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(46, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(47, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(48, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(49, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(50, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(51, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(52, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(53, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(53, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(54, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(55, 1, query->has_0d_relaxation_control); + CTRL_REG_PRESENCE(56, 1, query->has_0d_relaxation_control); + CTRL_REG_PRESENCE(57, 1, query->has_0d_acquisition_control); + CTRL_REG_PRESENCE(58, 1, query->has_0d_acquisition_control); + CTRL_REG_PRESENCE(59, 2, query->has_h_blank); + CTRL_REG_PRESENCE(60, 1, query->has_h_blank || query->has_v_blank || + query->has_long_h_blank); + CTRL_REG_PRESENCE(61, 1, query->has_h_blank || query->has_v_blank || + query->has_long_h_blank); + CTRL_REG_PRESENCE(62, 1, query->has_h_blank || query->has_v_blank || + query->has_long_h_blank); + CTRL_REG_PRESENCE(63, 1, query->has_h_blank || query->has_v_blank || + query->has_long_h_blank || + query->has_slew_metric || + query->has_slew_option || + query->has_noise_mitigation2); + + if (query->has_h_blank) { + CTRL_REG_PRESENCE(64, 7, 1); + CTRL_REG_PRESENCE(65, 7, 1); + } else if (query->has_v_blank || query->has_long_h_blank) { + CTRL_REG_PRESENCE(64, 1, 1); + CTRL_REG_PRESENCE(65, 1, 1); + } + + if (query->has_h_blank || query->has_v_blank || + query->has_long_h_blank) { + CTRL_REG_PRESENCE(66, 1, 1); + CTRL_REG_PRESENCE(67, 1, 1); + CTRL_REG_PRESENCE(68, 1, 1); + CTRL_REG_PRESENCE(69, 1, 1); + CTRL_REG_PRESENCE(70, 1, 1); + CTRL_REG_PRESENCE(71, 1, 1); + CTRL_REG_PRESENCE(72, 2, 1); + CTRL_REG_PRESENCE(73, 2, 1); + } + CTRL_REG_PRESENCE(74, 2, query->has_slew_metric); + CTRL_REG_PRESENCE(75, query12->number_of_sensing_frequencies, + query->has_enhanced_stretch && + query->has_sense_frequency_control); + CTRL_REG_PRESENCE(76, 1, query->has_startup_fast_relaxation); + CTRL_REG_PRESENCE(77, 1, query->has_esd_control); + CTRL_REG_PRESENCE(78, 1, query->has_esd_control); + CTRL_REG_PRESENCE(79, 1, query->has_noise_mitigation2); + CTRL_REG_PRESENCE(80, 1, query->has_noise_mitigation2); + CTRL_REG_PRESENCE(81, 1, query->has_noise_mitigation2); + CTRL_REG_PRESENCE(82, 1, query->has_noise_mitigation2); + CTRL_REG_PRESENCE(83, 1, query->has_noise_mitigation2); + CTRL_REG_PRESENCE(84, 1, query->has_energy_ratio_relaxation); + CTRL_REG_PRESENCE(85, 1, query->has_energy_ratio_relaxation); + CTRL_REG_PRESENCE(86, 1, query13->has_ctrl86); + CTRL_REG_PRESENCE(87, 1, query13->has_ctrl87); + CTRL_REG_PRESENCE(88, 1, query->has_ctrl88); + +/* + CTRL_REG_ADD(89, 1, query13->has_cid_im || + query13->has_noise_mitigation_enh || + query13->has_rail_im); +*/ + CTRL_PKT_REG_START(89, + CTRL_SUBPKT_SZ(89, 0) + + CTRL_SUBPKT_SZ(89, 1) + + CTRL_SUBPKT_SZ(89, 2)); + + CTRL_SUBPKT_ADD(89, 0, query13->has_cid_im); + CTRL_SUBPKT_ADD(89, 1, query13->has_noise_mitigation_enh); + CTRL_SUBPKT_ADD(89, 2, query13->has_rail_im); + CTRL_PKT_REG_END(89, 1); + + CTRL_REG_PRESENCE(90, 1, query15->has_ctrl90); + CTRL_REG_PRESENCE(91, 1, query21->has_ctrl91); + CTRL_REG_PRESENCE(92, 1, query16->has_ctrl92); + + CTRL_REG_ADD(93, 1, query16->has_ctrl93); + + CTRL_REG_PRESENCE(94, 1, query16->has_ctrl94_query18); + + CTRL_REG_ADD_EXT(95, 1, query16->has_ctrl95_query19, + sizeof(struct f54_control_95n) * + query17->q17_num_of_sense_freqs); + + CTRL_REG_PRESENCE(96, 1, query21->has_ctrl96); + CTRL_REG_PRESENCE(97, 1, query21->has_ctrl97); + CTRL_REG_PRESENCE(98, 1, query21->has_ctrl98); + + CTRL_REG_ADD(99, 1, (query16->has_ctrl99 || + ((f54->query.touch_controller_family == 2) || + (f54->query.touch_controller_family == 4)))); + + CTRL_REG_PRESENCE(100, 1, query16->has_ctrl100); + CTRL_REG_PRESENCE(101, 1, query22->has_ctrl101); + CTRL_REG_PRESENCE(102, 1, query23->has_ctrl102); + CTRL_REG_PRESENCE(103, 1, query22->has_ctrl103_query26); + CTRL_REG_PRESENCE(104, 1, query22->has_ctrl104); + CTRL_REG_PRESENCE(105, 1, query22->has_ctrl105); + CTRL_REG_PRESENCE(106, 1, query25->has_ctrl106); + + CTRL_REG_ADD(107, 1, query25->has_ctrl107); + + CTRL_REG_PRESENCE(108, 1, query25->has_ctrl108); + CTRL_REG_PRESENCE(109, 1, query25->has_ctrl109); + CTRL_REG_PRESENCE(110, 1, query27->has_ctrl110); + CTRL_REG_PRESENCE(111, 1, query27->has_ctrl111); + CTRL_REG_PRESENCE(112, 1, query27->has_ctrl112); + + CTRL_REG_ADD(113, 1, query27->has_ctrl113); + + CTRL_REG_PRESENCE(114, 1, query27->has_ctrl114); + CTRL_REG_PRESENCE(115, 1, query29->has_ctrl115); + + CTRL_REG_ADD(116, 1, query29->has_ctrl116); + + CTRL_REG_PRESENCE(117, 1, query29->has_ctrl117); + CTRL_REG_PRESENCE(118, 1, query30->has_ctrl118); + CTRL_REG_PRESENCE(119, 1, query30->has_ctrl119); + CTRL_REG_PRESENCE(120, 1, query30->has_ctrl120); + CTRL_REG_PRESENCE(121, 1, query30->has_ctrl121); + CTRL_REG_PRESENCE(122, 1, query30->has_ctrl122_query31); + CTRL_REG_PRESENCE(123, 1, query30->has_ctrl123); + CTRL_REG_PRESENCE(124, 1, query30->has_ctrl124); + CTRL_REG_PRESENCE(125, 1, query32->has_ctrl125); + CTRL_REG_PRESENCE(126, 1, query32->has_ctrl126); + CTRL_REG_PRESENCE(127, 1, query32->has_ctrl127); + CTRL_REG_PRESENCE(128, 1, query33->has_ctrl128); + CTRL_REG_PRESENCE(129, 1, query33->has_ctrl129); + CTRL_REG_PRESENCE(130, 1, query33->has_ctrl130); + CTRL_REG_PRESENCE(131, 1, query33->has_ctrl131); + CTRL_REG_PRESENCE(132, 1, query33->has_ctrl132); + CTRL_REG_PRESENCE(133, 1, query33->has_ctrl133); + CTRL_REG_PRESENCE(134, 1, query33->has_ctrl134); + CTRL_REG_PRESENCE(135, 1, query35->has_ctrl135); + CTRL_REG_PRESENCE(136, 1, query35->has_ctrl136); + + CTRL_REG_ADD(137, 1, query35->has_ctrl137); + + CTRL_REG_PRESENCE(138, 1, query35->has_ctrl138); + CTRL_REG_PRESENCE(139, 1, query35->has_ctrl139); + CTRL_REG_PRESENCE(140, 1, query35->has_ctrl140); + CTRL_REG_PRESENCE(141, 1, query36->has_ctrl141); + CTRL_REG_PRESENCE(142, 1, query36->has_ctrl142); + CTRL_REG_PRESENCE(143, 1, query36->has_ctrl143); + CTRL_REG_PRESENCE(144, 1, query36->has_ctrl144); + + CTRL_REG_ADD(145, 1, query36->has_ctrl145); + CTRL_REG_ADD(146, 1, query36->has_ctrl146); + + CTRL_REG_PRESENCE(147, 1, query38->has_ctrl147); + CTRL_REG_PRESENCE(148, 1, query38->has_ctrl148); + CTRL_REG_PRESENCE(149, 1, query38->has_ctrl149); + CTRL_REG_PRESENCE(150, 1, query38->has_ctrl150); + CTRL_REG_PRESENCE(151, 1, query38->has_ctrl151); + CTRL_REG_PRESENCE(152, 1, query38->has_ctrl152); + CTRL_REG_PRESENCE(153, 1, query38->has_ctrl153); + CTRL_REG_PRESENCE(154, 1, query39->has_ctrl154); + CTRL_REG_PRESENCE(155, 1, query39->has_ctrl155); + CTRL_REG_PRESENCE(156, 1, query39->has_ctrl156); + CTRL_REG_PRESENCE(157, 1, query39->has_ctrl157_158); + CTRL_REG_PRESENCE(158, 1, query39->has_ctrl157_158); +// CTRL_REG_RESERVED_PRESENCE(159, 1, 0); + CTRL_REG_PRESENCE(160, 1, query39->has_ctrl160); +// CTRL_REG_RESERVED_PRESENCE(161, 1, 0); +// CTRL_REG_RESERVED_PRESENCE(162, 1, 0); + CTRL_REG_PRESENCE(163, 1, query40->has_ctrl163_query41); +// CTRL_REG_RESERVED_PRESENCE(164, 1, 0); + CTRL_REG_PRESENCE(165, 1, query40->has_ctrl165_query42); + CTRL_REG_PRESENCE(166, 1, query40->has_ctrl166); + CTRL_REG_PRESENCE(167, 1, query40->has_ctrl167); + CTRL_REG_PRESENCE(168, 1, query40->has_ctrl168); + CTRL_REG_PRESENCE(169, 1, query40->has_ctrl169); +// CTRL_REG_RESERVED_PRESENCE(170, 1, 0); + CTRL_REG_PRESENCE(171, 1, query43->has_ctrl171); + CTRL_REG_PRESENCE(172, 1, query43->has_ctrl172_query44_query45); + CTRL_REG_PRESENCE(173, 1, query43->has_ctrl173); + CTRL_REG_PRESENCE(174, 1, query43->has_ctrl174); + CTRL_REG_PRESENCE(175, 1, query43->has_ctrl175); + CTRL_REG_PRESENCE(176, 1, query46->has_ctrl176); + CTRL_REG_PRESENCE(177, 1, query46->has_ctrl177_178); + CTRL_REG_PRESENCE(178, 1, query46->has_ctrl177_178); + CTRL_REG_PRESENCE(179, 1, query46->has_ctrl179); + /* BSC control access */ +// CTRL_REG_RESERVED_PRESENCE(180, 1, 0); +// CTRL_REG_RESERVED_PRESENCE(181, 1, 0); + CTRL_REG_PRESENCE(182, 1, query47->has_ctrl182); + CTRL_REG_PRESENCE(183, 1, query47->has_ctrl183); +// CTRL_REG_RESERVED_PRESENCE(184, 1, 0); + CTRL_REG_PRESENCE(185, 1, query47->has_ctrl185); + CTRL_REG_PRESENCE(186, 1, query47->has_ctrl186); + CTRL_REG_PRESENCE(187, 1, query47->has_ctrl187); + CTRL_REG_PRESENCE(188, 1, query49->has_ctrl188); +// CTRL_REG_RESERVED_PRESENCE(189, 1, 0); + CTRL_REG_PRESENCE(190, 1, query49->has_ctrl190); +// CTRL_REG_RESERVED_PRESENCE(191, 1, 0); +// CTRL_REG_RESERVED_PRESENCE(192, 1, 0); + + CTRL_REG_PRESENCE(193, 1, query50->has_ctrl193); +// CTRL_REG_RESERVED_PRESENCE(194, 1, 0); +// CTRL_REG_RESERVED_PRESENCE(195, 1, 0); +// CTRL_REG_RESERVED_PRESENCE(196, 1, 0); + CTRL_REG_PRESENCE(197, 1, query51->has_ctrl197); + CTRL_REG_PRESENCE(198, 1, query51->has_query53_query54_ctrl198); + CTRL_REG_PRESENCE(199, 1, query51->has_ctrl199); + CTRL_REG_PRESENCE(200, 1, query55->has_ctrl200); + CTRL_REG_PRESENCE(201, 1, query55->has_ctrl201_ctrl202); + CTRL_REG_PRESENCE(202, 1, query55->has_ctrl201_ctrl202); + CTRL_REG_PRESENCE(203, 1, query55->has_ctrl203); + CTRL_REG_PRESENCE(204, 1, query55->has_ctrl204); + CTRL_REG_PRESENCE(205, 1, query57->has_ctrl205); + CTRL_REG_PRESENCE(206, 1, query57->has_ctrl206); + CTRL_REG_PRESENCE(207, 1, query57->has_ctrl207); + CTRL_REG_PRESENCE(208, 1, query57->has_ctrl208); + CTRL_REG_PRESENCE(209, 1, query57->has_ctrl209); + CTRL_REG_PRESENCE(210, 1, query57->has_ctrl210); + CTRL_REG_PRESENCE(211, 1, query58->has_ctrl211); + CTRL_REG_PRESENCE(212, 1, query58->has_ctrl212); + CTRL_REG_PRESENCE(213, 1, query58->has_ctrl213); + CTRL_REG_PRESENCE(214, 1, query61->has_ctrl214); + CTRL_REG_PRESENCE(215, 1, query61->has_ctrl215_query62_query63); + CTRL_REG_PRESENCE(216, 1, query61->has_ctrl216); + CTRL_REG_PRESENCE(217, 1, query61->has_ctrl217); + CTRL_REG_PRESENCE(218, 1, query61->has_misc_host_ctrl); + /* might have subpacket */ + CTRL_REG_PRESENCE(219, 1, query61->has_ctrl219); + CTRL_REG_PRESENCE(220, 1, query64->has_ctrl220); + CTRL_REG_PRESENCE(221, 1, query64->has_ctrl221); + CTRL_REG_PRESENCE(222, 1, query64->has_ctrl222); + CTRL_REG_PRESENCE(223, 1, query68->is_tddi_hic); + CTRL_REG_PRESENCE(224, 1, query64->has_ctrl224_ctrl226_ctrl227); + CTRL_REG_PRESENCE(225, 1, query67->has_abs_doze_spatial_filter_en); + CTRL_REG_PRESENCE(226, 1, query64->has_ctrl224_ctrl226_ctrl227); + CTRL_REG_PRESENCE(227, 1, query64->has_ctrl224_ctrl226_ctrl227); + CTRL_REG_PRESENCE(228, 1, (f54->query.touch_controller_family == 4)); +// CTRL_REG_RESERVED_PRESENCE(229, 1, 0); + CTRL_REG_PRESENCE(230, 1, query65->has_ctrl230); + CTRL_REG_PRESENCE(231, 1, query65->has_query66_ctrl231); + CTRL_REG_PRESENCE(232, 1, query65->has_ctrl232); +// CTRL_REG_RESERVED_PRESENCE(233, 1, 0); +// CTRL_REG_RESERVED_PRESENCE(234, 1, 0); + CTRL_REG_PRESENCE(235, 1, query67->has_ctrl235_ctrl236); + CTRL_REG_PRESENCE(236, 1, query67->has_ctrl235_ctrl236); + CTRL_REG_PRESENCE(237, 1, query68->has_ctrl237); + CTRL_REG_PRESENCE(238, 1, query68->has_ctrl238); + CTRL_REG_PRESENCE(239, 1, query68->has_ctrl239); + /* might have up to 4 subpackets */ + CTRL_REG_PRESENCE(240, 1, query69->data[0] & 0x0f); + CTRL_REG_ADD(242, 1, query70->has_ctrl242); + CTRL_REG_ADD(269, 1, query75->has_ctrl269); + + /* add F55 control registers */ + reg_addr = f54->fn55->control_base_addr; + + CTRL_REG_ADD(0_f55, 1, f54->query_f55_0_2.has_sensor_assignment); + + CTRL_REG_PRESENCE(1_f55, 1, f54->query_f55_0_2.has_sensor_assignment); + CTRL_REG_PRESENCE(2_f55, 1, f54->query_f55_0_2.has_sensor_assignment); + CTRL_REG_PRESENCE(3_f55, 1, f54->query_f55_0_2.has_edge_compensation); + CTRL_REG_PRESENCE(4_f55, 1, f54->query_f55_0_2.curve_compensation_mode); + CTRL_REG_PRESENCE(5_f55, 1, f54->query_f55_0_2.curve_compensation_mode); + CTRL_REG_PRESENCE(6_f55, 1, f54->query_f55_0_2.has_ctrl6); + CTRL_REG_PRESENCE(7_f55, 1, + f54->query_f55_0_2.has_alternate_tx_assignment); + + CTRL_REG_ADD(8_f55, 1, + (f54->query_f55_0_2.f55_q2_has_single_layer_multitouch && + f54->query_f55_3.f55_q3_has_ctrl8)); + return 0; + +exit_no_mem: + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for control registers\n", + __func__); + return -ENOMEM; +} + +static int synaptics_rmi4_f54_set_data( + struct synaptics_rmi4_f54_handle *f54) +{ + unsigned char reg_num = 0; + unsigned short reg_addr = f54->data_base_addr; + struct f54_data *data = &f54->data; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + /* data 0 - data 3: skip mandatory registers */ + reg_addr += 4; + + /* data 4 */ + DATA_REG_ADD(4, 1, f54->query.has_sense_frequency_control); + + /* F54_ANALOG_Data5 (reserved) is not present */ + /* - do not increment */ + + /* data 6 */ + DATA_REG_ADD(6, 2, f54->query.has_interference_metric); + /* data 7.0 */ + DATA_REG_ADD(7_0, 1, (f54->query.has_one_byte_report_rate || + f54->query.has_two_byte_report_rate)); + /* data 7.1 */ + DATA_REG_ADD(7_1, 1, f54->query.has_two_byte_report_rate); + /* data 8 */ + DATA_REG_ADD(8, 2, f54->query.has_variance_metric); + /* data 9 */ + DATA_REG_ADD(9, 2, f54->query.has_multi_metric_state_machine); + /* data 10 */ + DATA_REG_ADD(10, 1, (f54->query.has_multi_metric_state_machine || + f54->query.has_noise_state)); + + /* data 11 */ + DATA_REG_PRESENCE(11, 1, f54->query.has_status); + /* data 12 */ + DATA_REG_PRESENCE(12, 2, f54->query.has_slew_metric); + /* data 13 */ + DATA_REG_PRESENCE(13, 2, f54->query.has_multi_metric_state_machine); + + /* data 14 */ + DATA_REG_ADD(14, 1, f54->query13.has_cid_im); + + /* data 15 */ + DATA_REG_PRESENCE(15, 1, f54->query13.has_rail_im); + + /* data 16 */ + DATA_REG_ADD(16, 1, f54->query13.has_noise_mitigation_enh); + /* data 17 */ + DATA_REG_ADD(17, 1, f54->query16.has_data17); + + return 0; + +exit_no_mem: + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for data registers\n", + __func__); + return -ENOMEM; +} + +static int synaptics_rmi4_f55_read_query( + struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + uint16_t reg_addr; + + retval = f54->fn_ptr->read(rmi4_data, + f54->fn55->query_base_addr, + f54->query_f55_0_2.data, + sizeof(f54->query_f55_0_2.data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read F55 query registers 0-2\n", + __func__); + goto err; + } + + reg_addr = f54->fn55->query_base_addr; + + QUERY_REG_READ(_f55_0_2, 1); + QUERY_REG_READ(_f55_3, + f54->query_f55_0_2.f55_q2_has_single_layer_multitouch); +return 0; + +err: + return retval; +} + +static void synaptics_rmi4_f54_sensor_mapping( + struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + struct f54_control *control = &f54->control; + unsigned int rx_len; + unsigned int tx_len; + unsigned char *buffer; + unsigned char *rx_buffer; + unsigned char *tx_buffer; + unsigned short offset; + int i; + + rmi4_data->num_of_rx = 0; + rmi4_data->num_of_tx = 0; + + if (f54->query.has_sensor_assignment == 1) { + rx_len = control->reg_15->length; + tx_len = control->reg_16->length; + offset = control->reg_15->address; + + } else if (f54->fn55) { + if (f54->query_f55_0_2.has_sensor_assignment) { + rx_len = f54->query_f55_0_2.num_of_rx_electrodes; + tx_len = f54->query_f55_0_2.num_of_tx_electrodes; + + offset = f54->fn55->control_base_addr + 1; + } else { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Has sensor assignment is not set in F55\n", + __func__); + return; + } + } else { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Cannot find sensor mapping\n", + __func__); + return; + } + + buffer = kzalloc((rx_len + tx_len) * sizeof(unsigned char), + GFP_KERNEL); + + retval = f54->fn_ptr->read(rmi4_data, offset, buffer, rx_len + tx_len); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read control registers\n", + __func__); + goto exit; + } + rx_buffer = buffer; + tx_buffer = buffer + rx_len; + + for (i = 0; i < rx_len; i++) { + if (rx_buffer[i] != 0xFF) + rmi4_data->num_of_rx++; + } + for (i = 0; i < tx_len; i++) { + if (tx_buffer[i] != 0xFF) + rmi4_data->num_of_tx++; + } + + dev_info(&rmi4_data->i2c_client->dev, + "RxTx mapped from F$%s Rx mapped count %d(%d); Tx mapped count %d(%d)\n", + f54->fn55 ? "55" : "54", rmi4_data->num_of_rx, + rx_len, rmi4_data->num_of_tx, tx_len); +exit: + kfree(buffer); +} + +static void synaptics_rmi4_f54_status_work(struct work_struct *work) +{ + int retval; + unsigned char report_index[2]; + struct delayed_work *dw = + container_of(work, struct delayed_work, work); + struct synaptics_rmi4_f54_handle *f54 = container_of(dw, + struct synaptics_rmi4_f54_handle, status_work); + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + pr_debug("enter; status = %d\n", f54->status); + if (f54->status != STATUS_BUSY) { + retval = STATUS_ERROR; + goto error_exit; + } + + set_report_size(f54); + pr_debug("report_size = %d\n", f54->report_size); + if (f54->report_size == 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Report data size = 0\n", + __func__); + retval = -EINVAL; + goto error_exit; + } + + if (f54->data_buffer_size < f54->report_size) { + mutex_lock(&f54->data_mutex); + if (f54->data_buffer_size) + kfree(f54->report_data); + f54->report_data = kzalloc(f54->report_size, GFP_KERNEL); + if (!f54->report_data) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for data buffer\n", + __func__); + f54->data_buffer_size = 0; + mutex_unlock(&f54->data_mutex); + retval = -ENOMEM; + goto error_exit; + } + f54->data_buffer_size = f54->report_size; + mutex_unlock(&f54->data_mutex); + } + + report_index[0] = 0; + report_index[1] = 0; + + retval = f54->fn_ptr->write(rmi4_data, + f54->data_base_addr + DATA_REPORT_INDEX_OFFSET, + report_index, + sizeof(report_index)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write report data index\n", + __func__); + retval = -EINVAL; + goto error_exit; + } + + retval = f54->fn_ptr->read(rmi4_data, + f54->data_base_addr + DATA_REPORT_DATA_OFFSET, + f54->report_data, + f54->report_size); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read report data\n", + __func__); + retval = -EINVAL; + goto error_exit; + } + + pr_debug("retrieved %d bytes of test report data\n", retval); + retval = STATUS_IDLE; + +#ifdef RAW_HEX + print_raw_hex_report(f54); +#endif + +#ifdef HUMAN_READABLE + print_image_report(f54); +#endif + +error_exit: + mutex_lock(&f54->status_mutex); +#if !defined(REPORT_STATUS_POLLING) + set_interrupt(f54, false); +#endif + __pm_relax(&f54->test_wake_lock); + f54->status = retval; + mutex_unlock(&f54->status_mutex); + pr_debug("status set to %d\n", f54->status); + + return; +} + +static void synaptics_rmi4_f54_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + + pr_debug("called f54_int_mask %x, int_mask %x\n", f54->intr_mask, intr_mask); + if (f54->intr_mask & intr_mask) { + schedule_delayed_work(&f54->status_work, + msecs_to_jiffies(STATUS_WORK_INTERVAL)); + } + + return; +} + +int synaptics_rmi4_f54_read_query( + struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + uint16_t reg_addr; + + retval = f54->fn_ptr->read(rmi4_data, + f54->query_base_addr, + f54->query.data, + sizeof(f54->query.data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read query registers\n", + __func__); + goto err; + } + + reg_addr = f54->query_base_addr + sizeof(f54->query.data); + + QUERY_REG_READ(12, f54->query.has_sense_frequency_control); + QUERY_REG_READ(13, f54->query.has_query13); + QUERY_REG_READ(14, f54->query13.has_ctrl87); + QUERY_REG_READ(15, f54->query.has_query15); + QUERY_REG_READ(16, f54->query15.has_query16); + QUERY_REG_READ(17, f54->query16.has_query17); + QUERY_REG_READ(18, f54->query16.has_ctrl94_query18); + QUERY_REG_READ(19, f54->query16.has_ctrl95_query19); + QUERY_REG_READ(20, f54->query15.has_query20); + QUERY_REG_READ(21, f54->query15.has_query21); + QUERY_REG_READ(22, f54->query15.has_query22); + QUERY_REG_READ(23, f54->query22.has_query23); + QUERY_REG_READ(24, f54->query21.has_query24_data18); + QUERY_REG_READ(25, f54->query15.has_query25); + QUERY_REG_READ(26, f54->query22.has_ctrl103_query26); + QUERY_REG_READ(27, f54->query25.has_query27); + QUERY_REG_READ(28, f54->query22.has_query28); + QUERY_REG_READ(29, f54->query27.has_query29); + QUERY_REG_READ(30, f54->query29.has_query30); + QUERY_REG_READ(31, f54->query30.has_ctrl122_query31); + QUERY_REG_READ(32, f54->query30.has_query32); + QUERY_REG_READ(33, f54->query32.has_query33); + QUERY_REG_READ(34, f54->query32.has_query34); + QUERY_REG_READ(35, f54->query32.has_query35); + QUERY_REG_READ(36, f54->query33.has_query36); + if (f54->query36.has_query37) + reg_addr += 1; + QUERY_REG_READ(38, f54->query36.has_query38); + QUERY_REG_READ(39, f54->query38.has_query39); + QUERY_REG_READ(40, f54->query39.has_query40); + QUERY_REG_READ(41, f54->query40.has_ctrl163_query41); + QUERY_REG_READ(42, f54->query40.has_ctrl165_query42); + QUERY_REG_READ(43, f54->query40.has_query43); +#if 0 + reg_addr += 1; /* query44 is reserved - always present */ + reg_addr += 1; /* query45 is reserved - always present */ +#endif + QUERY_REG_READ(46, f54->query43.has_query46); + QUERY_REG_READ(47, f54->query46.has_query47); + QUERY_REG_READ(49, f54->query47.has_query49); + QUERY_REG_READ(50, f54->query49.has_query50); + QUERY_REG_READ(51, f54->query50.has_query51); + if (f54->query50.has_query52) + reg_addr += 1; + QUERY_REG_READ(55, f54->query51.has_query55); + QUERY_REG_READ(57, f54->query55.has_query57); + QUERY_REG_READ(58, f54->query57.has_query58); + QUERY_REG_READ(61, f54->query58.has_query61); + QUERY_REG_READ(64, f54->query61.has_query64); + QUERY_REG_READ(65, f54->query64.has_query65); + QUERY_REG_READ(67, f54->query65.has_query67); + QUERY_REG_READ(68, f54->query67.has_query68); + QUERY_REG_READ(69, f54->query68.has_query69); + QUERY_REG_READ(70, f54->query69.has_query70); + QUERY_REG_READ(71, f54->query70.has_query71); + QUERY_REG_READ(72, f54->query71.has_query72); + QUERY_REG_READ(73, f54->query72.has_query73); + QUERY_REG_READ(74, f54->query73.has_query74); + QUERY_REG_READ(75, f54->query74.has_query75); + + return 0; + +err: + return retval; +} + +static int synaptics_rmi4_f54_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + bool hasF54 = false; + bool hasF55 = false; + unsigned short ii; + unsigned char page; + unsigned char intr_count = 0; + unsigned char intr_offset; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + + f54->fn_ptr = kzalloc(sizeof(*(f54->fn_ptr)), GFP_KERNEL); + if (!f54->fn_ptr) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for fn_ptr\n", + __func__); + retval = -ENOMEM; + goto exit_free_f54; + } + + f54->rmi4_data = rmi4_data; + f54->fn_ptr->read = rmi4_data->i2c_read; + f54->fn_ptr->write = rmi4_data->i2c_write; + f54->fn_ptr->enable = rmi4_data->irq_enable; + + for (page = 0; page < PAGES_TO_SERVICE; page++) { + for (ii = PDT_START; ii > PDT_END; ii -= PDT_ENTRY_SIZE) { + ii |= (page << 8); + + retval = f54->fn_ptr->read(rmi4_data, + ii, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + goto exit_free_mem; + + if (!rmi_fd.fn_number) + break; + + if (rmi_fd.fn_number == SYNAPTICS_RMI4_F54) { + hasF54 = true; + f54->query_base_addr = + rmi_fd.query_base_addr | (page << 8); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: query_base_addr 0x%04x\n", + __func__, f54->query_base_addr); + f54->control_base_addr = + rmi_fd.ctrl_base_addr | (page << 8); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: ctrl_base_addr 0x%04x\n", + __func__, f54->control_base_addr); + f54->data_base_addr = + rmi_fd.data_base_addr | (page << 8); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: data_base_addr 0x%04x\n", + __func__, f54->data_base_addr); + f54->command_base_addr = + rmi_fd.cmd_base_addr | (page << 8); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: command_base_addr 0x%04x\n", + __func__, f54->command_base_addr); + } else if (rmi_fd.fn_number == SYNAPTICS_RMI4_F55) { + hasF55 = true; + f54->fn55 = kmalloc(sizeof(*f54->fn55), + GFP_KERNEL); + f54->fn55->query_base_addr = + rmi_fd.query_base_addr | (page << 8); + f54->fn55->control_base_addr = + rmi_fd.ctrl_base_addr | (page << 8); + } + + if (hasF54 && hasF55) + goto found; + + if (!hasF54) + intr_count += + (rmi_fd.intr_src_count & MASK_3BIT); + } + } + + if (!hasF54) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: F$54 is not available\n", + __func__); + goto exit; + } +found: + f54->intr_reg_num = (intr_count + 7) / 8; + if (f54->intr_reg_num != 0) + f54->intr_reg_num -= 1; + + f54->intr_mask = 0; + intr_offset = intr_count % 8; + for (ii = intr_offset; + ii < ((rmi_fd.intr_src_count & MASK_3BIT) + + intr_offset); + ii++) { + f54->intr_mask |= 1 << ii; + } + + retval = synaptics_rmi4_f54_read_query(f54); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read query registers\n", + __func__); + goto exit_free_mem; + } + + retval = synaptics_rmi4_f55_read_query(f54); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to set up F55 query registers\n", + __func__); + goto exit_free_control; + } + + retval = synaptics_rmi4_f54_set_ctrl(f54); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to set up control registers\n", + __func__); + goto exit_free_control; + } + + synaptics_rmi4_f54_sensor_mapping(f54); + + retval = synaptics_rmi4_f54_set_data(f54); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to set up data registers\n", + __func__); + goto exit_free_data; + } + + mutex_init(&f54->status_mutex); + mutex_init(&f54->data_mutex); + mutex_init(&f54->control_mutex); + + retval = synaptics_rmi4_f54_set_sysfs(f54); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs entries\n", + __func__); + goto exit_sysfs; + } + + INIT_DELAYED_WORK(&f54->status_work, + synaptics_rmi4_f54_status_work); + + f54->user_report_type1 = F54_16BIT_IMAGE; + f54->user_report_type2 = F54_RAW_16BIT_IMAGE; + + wakeup_source_init(&f54->test_wake_lock, "synaptics_test_report"); + +#ifdef WATCHDOG_HRTIMER + /* Watchdog timer to catch unanswered get report commands */ + hrtimer_init(&f54->watchdog, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + f54->watchdog.function = get_report_timeout; + + /* Work function to do actual cleaning up */ + INIT_WORK(&f54->timeout_work, timeout_set_status); +#endif + + f54->init_done = true; + + return 0; + +exit_sysfs: +exit_free_data: + free_data_mem(f54); + +exit_free_control: + free_control_mem(f54); + +exit_free_mem: + kfree(f54->fn_ptr); + +exit_free_f54: + kfree(f54); + rmi4_data->f54_data = NULL; +exit: + return retval; +} + +static void synaptics_rmi4_f54_remove(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + + if (!f54->init_done) { + dev_dbg(&rmi4_data->i2c_client->dev, "%s: no cleanup necessary!!!\n", __func__); + goto exit_and_complete; + } + +#ifdef WATCHDOG_HRTIMER + hrtimer_cancel(&f54->watchdog); +#endif + + remove_sysfs(f54); + + free_data_mem(f54); + free_control_mem(f54); + + kfree(f54->report_data); + kfree(f54->fn55); + /* cancel delayed work first then free fn_ptr!!! */ + cancel_delayed_work_sync(&f54->status_work); + kfree(f54->fn_ptr); + + wakeup_source_trash(&f54->test_wake_lock); +exit_and_complete: + complete(&f54->remove_complete); + + return; +} + +static int __init rmi4_f54_module_init(void) +{ + struct synaptics_rmi4_f54_handle *f54; + struct synaptics_rmi4_data *next = NULL; + + while ((next = synaptics_driver_getdata(next)) != NULL) { + f54 = kzalloc(sizeof(*f54), GFP_KERNEL); + next->f54_data = (void *)f54; + next->scan_f54_ctrl_regs = synaptics_rmi4_scan_f54_ctrl_reg_info; + next->scan_f54_cmd_regs = synaptics_rmi4_scan_f54_cmd_reg_info; + next->scan_f54_query_regs = synaptics_rmi4_scan_f54_query_reg_info; + next->scan_f54_data_regs = synaptics_rmi4_scan_f54_data_reg_info; + dev_dbg(&next->i2c_client->dev, + "%s: f54 ptr = %p, rmi4_data_ptr %p\n", + __func__, f54, dev_get_drvdata(&next->i2c_client->dev)); + synaptics_rmi4_new_function(next, RMI_F54, true, + synaptics_rmi4_f54_init, + synaptics_rmi4_f54_remove, + synaptics_rmi4_f54_attn, + NULL, IC_MODE_UI); + } + return 0; +} + +static void __exit rmi4_f54_module_exit(void) +{ + struct synaptics_rmi4_f54_handle *f54; + struct synaptics_rmi4_data *next = NULL; + + while ((next = synaptics_driver_getdata(next)) != NULL) { + f54 = (struct synaptics_rmi4_f54_handle *)next->f54_data; + init_completion(&f54->remove_complete); + synaptics_rmi4_new_function(next, RMI_F54, false, + synaptics_rmi4_f54_init, + synaptics_rmi4_f54_remove, + synaptics_rmi4_f54_attn, + NULL, IC_MODE_UI); + wait_for_completion(&f54->remove_complete); + kfree(f54); + next->scan_f54_ctrl_regs = NULL; + next->scan_f54_cmd_regs = NULL; + next->scan_f54_query_regs = NULL; + next->scan_f54_data_regs = NULL; + next->f54_data = NULL; + } + return; +} + +module_init(rmi4_f54_module_init); +module_exit(rmi4_f54_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX Test Reporting Module"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(SYNAPTICS_DSX_DRIVER_VERSION); diff --git a/drivers/input/touchscreen/synaptics_mmi_class/Android.mk b/drivers/input/touchscreen/synaptics_mmi_class/Android.mk new file mode 100644 index 000000000000..a54741d32672 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi_class/Android.mk @@ -0,0 +1,38 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) +KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_MMI=y + +ifeq ($(DRM_PANEL_NOTIFICATIONS),true) + KERNEL_CFLAGS += CONFIG_DRM_PANEL_NOTIFICATIONS=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_mmi_class.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/mmi_info.ko +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_mmi_class_fw_update.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_mmi_class_rmi_dev.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_mmi_class_test_reporting.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +endif diff --git a/drivers/input/touchscreen/synaptics_mmi_class/Kbuild b/drivers/input/touchscreen/synaptics_mmi_class/Kbuild new file mode 100644 index 000000000000..9a84af8d38da --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi_class/Kbuild @@ -0,0 +1,28 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/synaptics_mmi_class + +ifneq ($(filter banks%, $(TARGET_PRODUCT)),) + EXTRA_CFLAGS += -DSOFTDEP_GPIO_PCAL6408 +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) +EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI + +ifneq ($(filter m y, $(CONFIG_DRM_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_NOTIFICATIONS +endif + +KBUILD_EXTRA_SYMBOLS += \ + $(CURDIR)/$(KBUILD_EXTMOD)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers \ + $(CURDIR)/$(KBUILD_EXTMOD)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers + +synaptics_mmi_class-objs = synaptics_dsx_i2c.o synaptics_dsx_mmi.o synaptics_dsx_instance.o + +obj-m += synaptics_mmi_class.o +obj-m += synaptics_mmi_class_fw_update.o +obj-m += synaptics_mmi_class_rmi_dev.o +obj-m += synaptics_mmi_class_test_reporting.o + +endif diff --git a/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_i2c.c b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_i2c.c new file mode 100644 index 000000000000..a3e8000352d9 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_i2c.c @@ -0,0 +1,7148 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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 pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#ifdef pr_debug +#undef pr_debug +#define pr_debug pr_err +#endif +#ifdef dev_dbg +#undef dev_dbg +#define dev_dbg dev_err +#endif +#endif + +#include +#include "synaptics_dsx_i2c.h" + +#define PINCTRL_STATE_ACTIVE "active_state" +#define PINCTRL_STATE_SUSPEND "suspend_state" + +struct workqueue_struct *det_workqueue; + +static char *INSTANCE_RMI(char *string, + struct synaptics_rmi4_data *rmi4_data) +{ + char instance[64]; + snprintf(instance, sizeof(instance), "%s.%d", + string, rmi4_data->instance); + return kstrdup(instance, GFP_KERNEL); +} + +static char *INSTANCE_DBG(char *string, + struct synaptics_rmi4_data *rmi4_data) +{ + static char name_with_instance[64]; + snprintf(name_with_instance, sizeof(name_with_instance), "%s.%d", + string, rmi4_data->instance); + return name_with_instance; +} + +static int synaptics_rmi4_hw_init(struct synaptics_rmi4_data *rmi4_data); + +static void synaptics_dsx_free_patch( + struct synaptics_dsx_patch *patch); +static struct synaptics_dsx_patch * + synaptics_dsx_init_patch(const char *name); +static int synaptics_rmi4_set_page( + struct synaptics_rmi4_data *rmi4_data, + unsigned int address); +static int synaptics_rmi4_query_device( + struct synaptics_rmi4_data *rmi4_data); +static int synaptics_rmi4_remove(struct i2c_client *client); + +/* F12 packet register description */ +struct f12_c8_s0_type { + unsigned char max_x_lsb; + unsigned char max_x_msb; + unsigned char max_y_lsb; + unsigned char max_y_msb; +}; + +struct f12_c10_s0_type { + unsigned char noise_floor; + unsigned char min_peak_amplitude; + unsigned char peak_merge_threshold_lsb; + unsigned char peak_merge_threshold_msb; +}; + +struct f12_c15_s0_type { + unsigned char finger_threshold; + unsigned char small_finger_threshold; + unsigned char small_finger_border; + unsigned char negative_finger_threshold; +}; + +struct f12_c18_s0_type { + unsigned char zone0_x_lsb; + unsigned char zone0_x_msb; + unsigned char zone0_y_lsb; + unsigned char zone0_y_msb; + unsigned char zone1_x_lsb; + unsigned char zone1_x_msb; + unsigned char zone1_y_lsb; + unsigned char zone1_y_msb; + unsigned char max_timer; + unsigned char max_distance; +}; + +struct f12_c20_s0_type { + unsigned char x_suppression; + unsigned char y_suppression; +}; + +struct f12_c20_s1_type { + union { + struct { + unsigned char flags:1; + unsigned char wakeup_gesture_only:1; + unsigned char dribble:1; + unsigned char proximity_only:1; + unsigned char hover_swipe:1; + unsigned char proximity:1; + unsigned char hover_pinch:1; + unsigned char lp_proximity:1; + } __packed; + unsigned char report; + }; +}; + +struct f12_c23_s0_type { + union { + struct { + unsigned char finger:1; + unsigned char stylus:1; + unsigned char palm:1; + unsigned char unclassified:1; + unsigned char reserved:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct f12_c23_s1_type { + unsigned char max_num_reported_objects; +}; + +struct f12_c27_s0_type { + union { + struct { + unsigned char double_tap:1; + unsigned char swipe:1; + unsigned char tap_n_hold:1; + unsigned char circle:1; + unsigned char triangle:1; + unsigned char vee:1; + unsigned char unicode:1; + unsigned char reserved:1; + } __packed; + unsigned char wakeup_gesture; + }; +}; + +struct f12_c28_s0_type { + unsigned char reported_bytes_per_object; +}; + +struct f12_d1_s0_type { + unsigned char type_and_stylus; + unsigned char x_lsb; + unsigned char x_msb; + unsigned char y_lsb; + unsigned char y_msb; + unsigned char z; + unsigned char wx; + unsigned char wy; +}; + +/* RMI4 has the following wakeup gesture defined: + • 0x00 = No gesture + • 0x01 = One-Finger single tap + • 0x02 = One-Finger tap-and-hold + • 0x03 = One-Finger double tap + • 0x04 = One-Finger early tap + • 0x05 = One-Finger flick + • 0x06 = One-Finger press + • 0x07 = One-Finger swipe + • 0x08 = One-Finger Circle + • 0x09 = One-Finger Triangle + • 0x0A = One-Finger Vee + • 0x0B = Reserved + • 0x0C = Triple Tap + • 0x0D = Click + • 0x0E = Reserved + • 0x30 = Edge gesture + • 0x7F = Reserved + • 0x80 = Pinch + • 0x81 = Rotate + • 0x82 through 0xFF = Reserved +*/ + +#define DOUBLE_TAP_GESTURE 0x03 + +struct f12_d4_s0_type { + unsigned char gesture; +}; + +struct f12_d15_s0_type { + unsigned char attn[2]; +}; + +/* F01 packet register description */ +struct f01_c0_s0_type { + union { + struct { + unsigned char sleep_mode:2; + unsigned char no_sleep:1; + unsigned char reserved:2; + unsigned char charger_connected:1; + unsigned char report_rate:1; + unsigned char configured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f01_c2_s0_type { + unsigned char doze_interval; +}; + +struct f01_c3_s0_type { + unsigned char wakeup_threshold; +}; + +struct f01_c5_s0_type { + unsigned char doze_holdoff; +}; + +struct f01_c9_s0_type { + unsigned char recalibration_interval; +}; + +/* F51 packet register description */ +struct f51_d0_s0_type { + union { + struct { + unsigned char noise_state:1; + unsigned char gear_change:1; + unsigned char guard_state:1; + unsigned char unused:5; + unsigned char md_present:1; + unsigned char reserved:7; + } __packed; + unsigned char data[2]; + }; +}; + +struct f51_c0_s0_type { + union { + struct { + unsigned char cid_check_enable:1; + unsigned char fsim_clear_enable:1; + unsigned char nsm_intr_enable:1; + unsigned char unused:5; + } __packed; + unsigned char data[1]; + }; +}; + +struct f51_c1_s0_type { + struct { + unsigned char dyn_jitter_strength; + unsigned char dyn_jitter_gain; + unsigned char dyn_jitter_step_size; + } __packed; +}; + +struct f51_c4_s0_type { + union { + struct { + unsigned char md_feature_enable:1; + unsigned char md_no_relax:1; + unsigned char md_uncond_shutoff:1; + unsigned char unused:5; + } __packed; + unsigned char data[1]; + }; + unsigned char md_min_threshold; + unsigned char md_max_threshold; + unsigned char md_2d_area; + unsigned char md_frame_count_in; + unsigned char md_frame_count_out; +}; + +/* F54 packet register description */ +struct f54_q12_s0_type { + union { + struct { + unsigned char num_of_scan_freq:4; + unsigned char reserved:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_c0_s0_type { + union { + struct { + unsigned char no_relax:1; + unsigned char no_scan:1; + unsigned char force_fast_relax:1; + unsigned char startup_fast_relax:1; + unsigned char gest_cancels_fast_relax:1; + unsigned char energy_ratio_relax:1; + unsigned char exces_noise_reporting:1; + unsigned char enable_cap_correction:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_c2_s0_type { + struct { + unsigned short saturation_cap; + } __packed; +}; + +struct f54_d6_s0_type { + union { + struct { + unsigned char interference_metric_lsb; + unsigned char interference_metric_msb; + } __packed; + unsigned char data[2]; + }; +}; + +struct f54_d10_s0_type { + unsigned char noise_state; +}; + +struct f54_d14_s0_type { + union { + struct { + unsigned char cid_im_lsb; + unsigned char cid_im_msb; + } __packed; + unsigned char data[2]; + }; +}; + +struct f54_d16_s0_type { + union { + struct { + unsigned char freq_scan_im_lsb; + unsigned char freq_scan_im_msb; + } __packed; + unsigned char data[2]; + }; +}; + +struct f54_d17_s0_type { + union { + struct { + unsigned char freq:7; + unsigned char inhibit_freq_shift:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_m0_s0_type { + unsigned char command; +}; + +#define CTRL_TYPE (0 << 8) +#define DATA_TYPE (1 << 8) +#define QUERY_TYPE (2 << 8) +#define COMMAND_TYPE (4 << 8) + +/*subpkt data type*/ +#define DYNA_RMI4_SUBPKT(a)\ +do {\ + s->present = 0;\ + s->expected = 1;\ + s->size = sizeof(struct a);\ + s->data = kzalloc(sizeof(struct a), GFP_KERNEL);\ + if (!s->data)\ + goto alloc_error_subpkt;\ + dev_dbg(&rmi4_data->i2c_client->dev, "allocated %lu bytes of subptk data\n", sizeof(struct a));\ +} while(0) + +/*register number, number of subpkts, register size*/ +#define DYNA_RMI4_REG(n, s, rsz)\ +do {\ + r->r_number = n;\ + r->offset = -1;\ + r->updated = 0;\ + r->modified = 0;\ + r->expected = 1;\ + r->size = rsz;\ + r->nr_subpkts = s;\ + r->subpkt = kzalloc((s)*sizeof(struct synaptics_rmi4_subpkt), GFP_KERNEL);\ + if (!r->subpkt)\ + goto alloc_error_reg;\ + dev_dbg(&rmi4_data->i2c_client->dev, "allocated %d subptks for reg %d\n", s, n);\ +} while(0) + +/*func number, base address, offset, number of registers*/ +#define DYNA_RMI4_FUNC(n, a, o, r)\ +do {\ + dev_dbg(&rmi4_data->i2c_client->dev, "config_regs for func[%02x:%d] = %p\n", (n & 0xff), (n >> 8), f);\ + f->f_number = n;\ + f->base_addr = a;\ + f->query_offset = o;\ + f->nr_regs = r;\ + f->regs = kzalloc((r)*sizeof(struct synaptics_rmi4_packet_reg), GFP_KERNEL);\ + if (!f->regs)\ + goto alloc_error_func;\ + dev_dbg(&rmi4_data->i2c_client->dev, "allocated %d regs for func[%02x:%d]\n", r, (n & 0xff), (n >> 8));\ +} while(0) + +#define RMI4_GET_FUNC(n) (&rmi4_data->config_regs[n]) +#define RMI4_GET_PKT_REG(n) (&f->regs[n]) +#define RMI4_GET_SUBPKT(n) (&r->subpkt[n]) + +#define RMI4_DECLARE_FUNC(nf, addr, off, nr) \ +do {\ + f = RMI4_GET_FUNC(fnum++); snum = 0; rnum = 0;\ + DYNA_RMI4_FUNC(nf, addr, off, nr);\ +} while(0) + +#define RMI4_DECLARE_REG_NO_ALLOC(rn, ns) \ +do {\ + r = RMI4_GET_PKT_REG(rnum++); snum = 0;\ + DYNA_RMI4_REG(rn, ns, 0);\ +} while(0) + +#define RMI4_DECLARE_REG_ONE_SUBPKT(rn, prefix) \ +do {\ + r = RMI4_GET_PKT_REG(rnum++); snum = 0;\ + DYNA_RMI4_REG(rn, 1, sizeof(struct prefix##_s0_type));\ + s = RMI4_GET_SUBPKT(0);\ + DYNA_RMI4_SUBPKT(prefix##_s0_type);\ +} while(0) + +/*subpkt data type*/ +#define RMI4_DECLARE_SUBPKT(subpkt_data_type) \ +do {\ + s = RMI4_GET_SUBPKT(snum++);\ + DYNA_RMI4_SUBPKT(subpkt_data_type);\ +} while(0) + +#define NO_MATTER 0 +#define SLOT_IN_USE (rmi4_data->config_regs[i].f_number) + +static int synaptics_dsx_config_regs_alloc(struct synaptics_rmi4_data *rmi4_data) +{ + int i, snum, rnum, fnum = 0; + struct synaptics_rmi4_func_packet_regs *f; + struct synaptics_rmi4_packet_reg *r; + struct synaptics_rmi4_subpkt *s; + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F01 | CTRL_TYPE), 0, NO_MATTER, 5); + RMI4_DECLARE_REG_ONE_SUBPKT(0, f01_c0); + RMI4_DECLARE_REG_ONE_SUBPKT(2, f01_c2); + RMI4_DECLARE_REG_ONE_SUBPKT(3, f01_c3); + RMI4_DECLARE_REG_ONE_SUBPKT(5, f01_c5); + RMI4_DECLARE_REG_ONE_SUBPKT(9, f01_c9); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F12 | CTRL_TYPE), 0, 4, 8); + RMI4_DECLARE_REG_ONE_SUBPKT( 8, f12_c8); + RMI4_DECLARE_REG_ONE_SUBPKT(10, f12_c10); + RMI4_DECLARE_REG_ONE_SUBPKT(15, f12_c15); + RMI4_DECLARE_REG_ONE_SUBPKT(18, f12_c18); + RMI4_DECLARE_REG_NO_ALLOC(20, 2); + RMI4_DECLARE_SUBPKT(f12_c20_s0_type); + RMI4_DECLARE_SUBPKT(f12_c20_s1_type); + RMI4_DECLARE_REG_NO_ALLOC(23, 2); + RMI4_DECLARE_SUBPKT(f12_c23_s0_type); + RMI4_DECLARE_SUBPKT(f12_c23_s1_type); + RMI4_DECLARE_REG_ONE_SUBPKT(27, f12_c27); + RMI4_DECLARE_REG_ONE_SUBPKT(28, f12_c28); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F12 | DATA_TYPE), 0, 7, 3); + RMI4_DECLARE_REG_NO_ALLOC(1, 10); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_SUBPKT(f12_d1_s0_type); + RMI4_DECLARE_REG_ONE_SUBPKT( 4, f12_d4); + RMI4_DECLARE_REG_ONE_SUBPKT(15, f12_d15); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F54 | DATA_TYPE), 0, NO_MATTER, 5); + RMI4_DECLARE_REG_ONE_SUBPKT( 6, f54_d6); + RMI4_DECLARE_REG_ONE_SUBPKT(10, f54_d10); + RMI4_DECLARE_REG_ONE_SUBPKT(14, f54_d14); + RMI4_DECLARE_REG_ONE_SUBPKT(16, f54_d16); + RMI4_DECLARE_REG_ONE_SUBPKT(17, f54_d17); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F54 | CTRL_TYPE), 0, NO_MATTER, 3); + RMI4_DECLARE_REG_ONE_SUBPKT(0, f54_c0); + RMI4_DECLARE_REG_ONE_SUBPKT(2, f54_c2); + RMI4_DECLARE_REG_NO_ALLOC(95, 8); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + RMI4_DECLARE_SUBPKT(f54_control_95n); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F54 | COMMAND_TYPE), 0, NO_MATTER, 1); + RMI4_DECLARE_REG_ONE_SUBPKT(0, f54_m0); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F54 | QUERY_TYPE), 0, NO_MATTER, 1); + RMI4_DECLARE_REG_ONE_SUBPKT(12, f54_q12); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F51 | CTRL_TYPE), 0, NO_MATTER, 3); + RMI4_DECLARE_REG_ONE_SUBPKT(0, f51_c0); + RMI4_DECLARE_REG_ONE_SUBPKT(1, f51_c1); + RMI4_DECLARE_REG_ONE_SUBPKT(4, f51_c4); + + RMI4_DECLARE_FUNC((SYNAPTICS_RMI4_F51 | DATA_TYPE), 0, NO_MATTER, 1); + RMI4_DECLARE_REG_ONE_SUBPKT(0, f51_d0); + + return 0; + +alloc_error_subpkt: +alloc_error_reg: +alloc_error_func: + + for (i = 0; i < MAX_CONFIG_REGS && SLOT_IN_USE; i++) { + int j, k; + + f = &rmi4_data->config_regs[i]; + if (!f->nr_regs) + continue; + for (j = 0; j < f->nr_regs; j++) { + if (!f->regs[j].r_number) + break; + r = &f->regs[j]; + for (k = 0 ; k < r->nr_subpkts; k++) { + if (!r->subpkt[k].size) + break; + kfree(r->subpkt[k].data); + } + kfree(r->subpkt); + kfree(r->data); + } + kfree(f->regs); + memset(f, 0, sizeof(*f)); + } + + return -ENOMEM; +} + +static unsigned char power_sleep_value = 1; +static struct synaptics_dsx_func_patch power_sleep_func_patch = { + .func = SYNAPTICS_RMI4_F01, + .regstr = 0, + .subpkt = 0, + .size = 1, + .bitmask = 7, + .data = &power_sleep_value, +}; + +static unsigned char force_update_value = 4; +static struct synaptics_dsx_func_patch force_update_func_patch = { + .func = SYNAPTICS_RMI4_F54 | COMMAND_TYPE, + .regstr = 0, + .subpkt = 0, + .size = 1, + .bitmask = 0, + .data = &force_update_value, +}; + +static struct synaptics_dsx_patch *force_update_patch; +static struct synaptics_dsx_patch *power_sleep_patch; + +static inline int register_ascii_to_type(unsigned char *symbol) +{ + int reg_type = CTRL_TYPE; + switch (*symbol) { + case 'Q': + reg_type = QUERY_TYPE; + break; + case 'D': + reg_type = DATA_TYPE; + break; + case 'M': + reg_type = COMMAND_TYPE; + break; + case 'C': + break; + default: return reg_type; + } + *symbol = '\0'; + return reg_type; +} + +static inline unsigned char register_type_to_ascii(int type) +{ + unsigned char ascii; + switch (type) { + case QUERY_TYPE: + ascii = 'Q'; + break; + case DATA_TYPE: + ascii = 'D'; + break; + case COMMAND_TYPE: + ascii = 'M'; + break; + case CTRL_TYPE: + default: + ascii = 'C'; + } + return ascii; +} + +#define F12_D1_IDX 0 +#define F12_D4_IDX 1 +#define F12_D15_IDX 2 + +static char *synaptics_dsx_find_patch(char *head, char *delimiters, char **next) +{ + char *patch = head; + for (; patch; patch = *next) { + *next = strpbrk(patch, delimiters); + if (*next) + *(*next)++ = '\0'; + patch = skip_spaces(patch); + if (!patch || !*patch || *patch == '#') + continue; + else + break; + } + return patch; +} + +static int parse_patch_data(char *value_p, u8 data[], long *bitmask_v) +{ + int i, error, num_of_bytes = 1; + long value_v; + char *bitmask_p; + + bitmask_p = strpbrk(value_p, "&"); + if (bitmask_p) { + *bitmask_p++ = '\0'; + error = kstrtol(bitmask_p, 16, bitmask_v); + if (error) + pr_err("bitmask conv error\n"); + } + + /* detect multiple bytes data */ + if ((strlen(value_p)%2) == 0) + num_of_bytes = strlen(value_p)/2; + + for (i = 0; i < num_of_bytes; i++) { + char hex_val[3] = {0}; + hex_val[0] = *value_p; + hex_val[1] = *(value_p + 1); + error = kstrtol(hex_val, 16, &value_v); + if (error) + pr_err("value conv error\n"); + data[i] = (u8)value_v; + value_p += 2; + } + + return num_of_bytes; +} + +static int synaptics_dsx_parse_patch(int func, char *query, + struct synaptics_dsx_patch *patch_ptr, bool expect_data) +{ + int i, error, rt_mod, function, num_of_bytes; + u8 data[64]; + char *next, *subpkt_p, *value_p, *pair = query; + long regstr_v, bitmask_v, subpkt_v; + struct synaptics_dsx_func_patch *patch; + + rt_mod = func & 0xf00; + function = func & 0xff; + for (i = 0; pair; pair = next, i++) { + num_of_bytes = 0; + bitmask_v = subpkt_v = 0L; + + pair = synaptics_dsx_find_patch(pair, ",", &next); + + value_p = strpbrk(pair, "="); + if (!value_p) { + pr_err("F%x[%d]: invalid syntax '%s'\n", + function, i, pair); + continue; + } + + /* make sure string is null terminated */ + *value_p = '\0'; + + subpkt_p = strpbrk(pair, ":"); + if (subpkt_p) { + *subpkt_p++ = '\0'; + error = kstrtol(subpkt_p, 10, &subpkt_v); + if (error) { + pr_err("F%x[%d]: subpacket conv error\n", + function, i); + continue; + } + } + + error = kstrtol(pair, 10, ®str_v); + if (error) { + pr_err("F%x[%d]: register conv error\n", + function, i); + continue; + } + + if (expect_data) + num_of_bytes = parse_patch_data( + ++value_p, data, &bitmask_v); + /* primitive data validation */ + if (function <= 0 || function > 255 || + regstr_v < 0 || regstr_v > 255) { + pr_err("F%x[%d]: invalid values\n", function, i); + continue; + } + + patch = kzalloc(sizeof(*patch), GFP_KERNEL); + if (!patch) { + pr_err("failed to alloc mem\n"); + return -ENOMEM; + } + + if (expect_data) { + patch->data = kzalloc(num_of_bytes, GFP_KERNEL); + if (!patch->data) { + pr_err("failed to alloc mem\n"); + return -ENOMEM; + } + memcpy(patch->data, data, num_of_bytes); + } + + patch->func = (u16)func; + patch->regstr = (u8)regstr_v; + patch->subpkt = (u8)subpkt_v; + patch->size = (u8)num_of_bytes; + patch->bitmask = (u8)bitmask_v; + + pr_debug("F%x[%d], register %d, subpkt %d, size %d, mask %x\n", + function, i, patch->regstr, patch->subpkt, + patch->size, patch->bitmask); + patch_ptr->cfg_num++; + list_add_tail(&patch->link, &patch_ptr->cfg_head); + } + + return 0; +} + +/* + * Special settings passed to the driver via device tree + * Example: "F12@11:2=1fa0;F1@1=1;" + * Where: + * F12 - decimal object number + * @ - delimits function number and following patch sets + * 11:2 - decimal register and subpacket numbers (0 offset) + * 26=a - register number and hex value to assign + * 2=1fa0 - 2 bytes of data to write to the register + */ +static void synaptics_dsx_parse_string(struct synaptics_rmi4_data *data, + const char *patch_ptr, + struct synaptics_dsx_patch *patch, + bool expect_data) +{ + struct device *dev = &data->i2c_client->dev; + long number_v; + char *patch_string; + char *config_p, *next, *patch_set; + int i, error, rt_mod = 0; + + patch_string = kstrdup(patch_ptr, GFP_KERNEL); + for (i = 0, patch_set = patch_string; patch_set; patch_set = next) { + patch_set = synaptics_dsx_find_patch(patch_set, ";\n", &next); + if (!patch_set) + break; + + dev_dbg(dev, "patch set %d: \"%s\"\n", i, patch_set); + config_p = strpbrk(patch_set, "@"); + + /* consider force update and power seetings here */ + if (!config_p) { + switch (*patch_set) { + case 'S': + patch->flags |= FLAG_POWER_SLEEP; + break; + case 'U': + patch->flags |= FLAG_FORCE_UPDATE; + break; + case 'W': + patch->flags |= FLAG_WAKEABLE; + break; + } + continue; + } + + if (*patch_set != 'F' && *patch_set != 'f') { + dev_err(dev, "invalid syntax '%s'\n", patch_set); + continue; + } + + rt_mod = register_ascii_to_type(config_p-1); + dev_dbg(dev, "register type modifier %d\n", rt_mod); + + /* strip non digits */ + *config_p++ = '\0'; + + error = kstrtol(++patch_set, 16, &number_v); + if (error) { + dev_err(dev, "kstrtol error %d\n", error); + continue; + } + + error = synaptics_dsx_parse_patch((int)number_v + rt_mod, + config_p, patch, expect_data); + if (error < 0) { + dev_err(dev, "invalid patch; parse error %d\n", error); + continue; + } + + i++; + } + kfree(patch_string); + if (patch->cfg_num) + dev_info(dev, "processed %d patch sets for %d functions\n", + patch->cfg_num, i); + else + dev_info(dev, "no valid patch sets found\n"); +} + +#define LAST_SUBPACKET_ROW_IND_MASK 0x80 +#define NR_SUBPKT_PRESENCE_BITS 7 + +static struct synaptics_rmi4_func_packet_regs *find_function( + struct synaptics_rmi4_data *rmi4_data, int f_number) +{ + struct synaptics_rmi4_func_packet_regs *regs = NULL; + int i; + for (i = 0; i < MAX_CONFIG_REGS && SLOT_IN_USE; i++) { + if (rmi4_data->config_regs[i].f_number == f_number) { + regs = &rmi4_data->config_regs[i]; + break; + } + } + return regs; +} + +static struct synaptics_rmi4_packet_reg *find_packet_reg( + struct synaptics_rmi4_func_packet_regs *regs, int number) +{ + struct synaptics_rmi4_packet_reg *reg = NULL; + int r; + + for (r = 0; r < regs->nr_regs; r++) + if (number == regs->regs[r].r_number) { + reg = ®s->regs[r]; + break; + } + return reg; +} + +static struct synaptics_rmi4_subpkt *find_subpkt( + struct synaptics_rmi4_func_packet_regs *regs, int nr, int ns, + struct synaptics_rmi4_packet_reg **reg, void **data) +{ + struct synaptics_rmi4_packet_reg *lreg = find_packet_reg(regs, nr); + struct synaptics_rmi4_subpkt *subpkt = NULL; + + if (reg) + *reg = lreg; + + if (lreg && ns < lreg->nr_subpkts) { + subpkt = &lreg->subpkt[ns]; + if (subpkt->present && data) + *data = lreg->subpkt[ns].data; + } + return subpkt; +} + +int synaptics_rmi4_scan_f12_reg_info( + struct synaptics_rmi4_data *rmi4_data, + unsigned short query_addr, + unsigned short regs_base_addr, + struct synaptics_rmi4_func_packet_regs *regs) +{ + unsigned char sz, mask; + int ii, jj, r, s, retval, max_reg_number; + unsigned short r_offset; + unsigned short addr = query_addr; + unsigned char data[255]; + bool r_exist[128] = {false}; + + for (r = 0; r < regs->nr_regs; ++r) { + regs->regs[r].offset = -1; + regs->regs[r].size = 0; + /* reg data can be re-allocated */ + kfree(regs->regs[r].data); + for (s = 0; s < regs->regs[r].nr_subpkts; ++s) { + regs->regs[r].subpkt[s].present = 0; + if (regs->regs[r].subpkt[s].data && + regs->regs[r].subpkt[s].size) + /* subpkt data cannot be re-allocated */ + memset(regs->regs[r].subpkt[s].data, 0, + regs->regs[r].subpkt[s].size); + } + } + + regs->base_addr = regs_base_addr; + retval = rmi4_data->i2c_read(rmi4_data, addr, &sz, 1); + dev_dbg(&rmi4_data->i2c_client->dev, + "size of reg presence = %d, addr 0x%x\n", sz, addr); + if (retval < 0) + return retval; + if (!sz) + return -EIO; + /* Scan register presence */ + retval = rmi4_data->i2c_read(rmi4_data, ++addr, data, sz); + if (retval < 0) + return retval; + if (!data[0]) { + dev_err(&rmi4_data->i2c_client->dev, + "packet register size greater 255 bytes not supported\n"); + return -ENOSYS; + } + ii = 1; + for (r = 0, r_offset = 0; ii < sz; ++ii) { + dev_dbg(&rmi4_data->i2c_client->dev, + "reg presence [%d] = 0x%02x\n", ii, data[ii]); + for (jj = 0, mask = 1; jj < 8; ++jj, ++r, mask <<= 1) { + struct synaptics_rmi4_packet_reg *reg = + find_packet_reg(regs, r); + bool present = (data[ii] & mask) != 0; + bool allocated = reg ? true : false; + if (present != allocated) + dev_dbg(&rmi4_data->i2c_client->dev, + " reg: r%d is%s present, but was%s expected\n", r, + present ? "" : " NOT", + allocated ? "" : " NOT"); + if (!present) { + r_exist[r] = false; + continue; + } + r_exist[r] = true; + if (allocated) + reg->offset = r_offset; + dev_dbg(&rmi4_data->i2c_client->dev, + " r%d offset = %d\n", r, r_offset); + r_offset++; + } + } + + max_reg_number = r; + dev_dbg(&rmi4_data->i2c_client->dev, + "max register number = %d\n", max_reg_number); + + /* Scan register size and subpacket presence*/ + sz = data[0]; + dev_dbg(&rmi4_data->i2c_client->dev, + "subpacket presence sz = %d, addr 0x%x\n", sz, addr+1); + retval = rmi4_data->i2c_read(rmi4_data, ++addr, data, sz); + if (retval < 0) + return retval; + for (r = 0, ii = 0; r < max_reg_number && ii < sz; ++r) { + unsigned int reg_size, expected_reg_size; + struct synaptics_rmi4_packet_reg *reg = + find_packet_reg(regs, r); + if (!r_exist[r]) { + dev_dbg(&rmi4_data->i2c_client->dev, + "r%d does not exist; go to the next...\n", r); + continue; + } + reg_size = data[ii++]; + if (!reg_size) { + dev_err(&rmi4_data->i2c_client->dev, + "packet register size greater 255 bytes not supported\n"); + return -ENOSYS; + } + if (reg && reg->offset != -1) { + reg->data = kzalloc(reg_size, GFP_KERNEL); + if (reg->data) { + reg->size = reg_size; + dev_dbg(&rmi4_data->i2c_client->dev, + "r%d allocated %d bytes buffer @%p\n", + reg->r_number, reg->size, reg->data); + } else + dev_err(&rmi4_data->i2c_client->dev, + "cannot alloc register data buffer\n"); + } + dev_dbg(&rmi4_data->i2c_client->dev, "r%d sz = %d\n", r, reg_size); + expected_reg_size = 0; + for (s = 0; ii < sz;) { + dev_dbg(&rmi4_data->i2c_client->dev, + " subpkt presence [%d] = 0x%02x\n", + ii, data[ii]); + for (jj = 0, mask = 1; jj < NR_SUBPKT_PRESENCE_BITS; + ++jj, ++s, mask <<= 1) { + struct synaptics_rmi4_subpkt *subpkt = NULL; + bool present = (data[ii] & mask) != 0; + bool expected = false; + if (reg && s < reg->nr_subpkts) { + subpkt = reg->subpkt + s; + expected = subpkt->expected; + } + if (!present || !expected) { + if (present != expected) + dev_dbg(&rmi4_data->i2c_client->dev, + " subpacket:" + " r%d s%d is%s present," + " but was%s expected\n", + r, s, + present ? "" : " NOT", + expected ? "" : " NOT"); + continue; + } + dev_dbg(&rmi4_data->i2c_client->dev, + " r%d.s%d is present\n", r, s); + if (subpkt) { + subpkt->present = true; + expected_reg_size += subpkt->size; + } + } + if ((data[ii++] & LAST_SUBPACKET_ROW_IND_MASK) == 0) { + dev_dbg(&rmi4_data->i2c_client->dev, "no more subpackets\n"); + break; + } + } + if (reg && reg->expected && reg->size != expected_reg_size) { + dev_dbg(&rmi4_data->i2c_client->dev, + " r%d size error: expected %d actual is %d\n", + r, expected_reg_size, reg->size); + } + } + return 0; +} + +int synaptics_rmi4_read_packet_reg( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *regs, int r_number) +{ + int s, retval, offset; + struct synaptics_rmi4_packet_reg *reg = find_packet_reg(regs, r_number); + + if (!reg || !reg->size) { + dev_err(&rmi4_data->i2c_client->dev, + "register %d not found or zero size\n", r_number); + return -EINVAL; + } + + if (reg->offset == -1) { + dev_err(&rmi4_data->i2c_client->dev, + "touch register error: can't read r%d - not present\n", + r_number); + return -ENOENT; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "F%02x%c reading r%d{%X}, size %d to @%p\n", + regs->f_number & 0xff, + register_type_to_ascii(regs->f_number & 0xf00), + reg->r_number, regs->base_addr + reg->offset, + reg->size, reg->data); + + retval = rmi4_data->i2c_read(rmi4_data, + regs->base_addr + reg->offset, + reg->data, reg->size); + if (retval < 0) + return retval; + + for (s = 0, offset = 0; s < reg->nr_subpkts; ++s) { + struct synaptics_rmi4_subpkt *subpkt = reg->subpkt + s; + if (!subpkt->present) + continue; + + if ((reg->size - offset) < subpkt->size) { + dev_err(&rmi4_data->i2c_client->dev, + "subpkt size error: expected %d bytes," + " only %d present\n", subpkt->size, + (reg->size - offset)); + break; + } + + memcpy(subpkt->data, reg->data+offset, subpkt->size); + offset += subpkt->size; +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + { + int kk; + dev_dbg(&rmi4_data->i2c_client->dev, + "read r%d.s%d =\n", reg->r_number, s); + for (kk = 0; kk < subpkt->size; ++kk) + pr_debug("%02x\n", + ((unsigned char *)subpkt->data)[kk]); + } +#endif + } + return retval; +} + +int synaptics_rmi4_read_packet_regs( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *regs) +{ + int r; + int retval = 0; + + for (r = 0; r < regs->nr_regs; ++r) { + dev_dbg(&rmi4_data->i2c_client->dev, + "about to read F%02x%c register %d [%d/%d]\n", + regs->f_number & 0xff, + register_type_to_ascii(regs->f_number & 0xf00), + regs->regs[r].r_number, + regs->regs[r].expected, + regs->regs[r].offset >= 0); + if (regs->regs[r].expected && regs->regs[r].offset >= 0) { + retval = synaptics_rmi4_read_packet_reg( + rmi4_data, regs, regs->regs[r].r_number); + if (retval < 0) + break; + } + } + return retval; +} + +static int synaptics_rmi4_write_packet_reg( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *regs, int r_number) +{ + struct synaptics_rmi4_packet_reg *reg = find_packet_reg(regs, r_number); + int sz, ii, offset, retval; + + if (!reg || !reg->size) { + dev_err(&rmi4_data->i2c_client->dev, + "r%d does not exist or zero size\n", r_number); + return -EINVAL; + } + if (reg->offset == -1) { + dev_err(&rmi4_data->i2c_client->dev, + "touch register error: writing to absent register r%d\n", + r_number); + return -ENOENT; + } + + for (ii = 0, sz = 0, offset = 0; ii < reg->nr_subpkts; ++ii) { + struct synaptics_rmi4_subpkt *subpkt = reg->subpkt + ii; + if (!subpkt->present) + continue; + + if (subpkt->data && subpkt->size && + (offset + subpkt->size) <= reg->size) { + if ((reg->size - offset) >= subpkt->size) { + memcpy(reg->data + sz, subpkt->data, + subpkt->size); + sz += subpkt->size; + } else { + dev_err(&rmi4_data->i2c_client->dev, + "expected %d bytes, only %d present\n", + offset + subpkt->size, reg->size); + break; + } + } else { + retval = -EINVAL; + dev_err(&rmi4_data->i2c_client->dev, + "cannot update subpacket %d: "\ + "sz=%d, offset=%d, data=%p, reg_sz=%d\n", + ii, subpkt->size, offset, + subpkt->data, reg->size); + goto out; + } + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "writing r%d{%X}, size %d to @%p\n", reg->r_number, + regs->base_addr + reg->offset, reg->size, reg->data); + + retval = rmi4_data->i2c_write(rmi4_data, + regs->base_addr + reg->offset, + reg->data, sz); +out: + return retval; +} + +#ifdef CONFIG_OF +static int synaptics_dsx_gpio_config( + struct synaptics_dsx_platform_data *pdata, bool enable) +{ + int retval = 0; + struct synaptics_rmi4_data *data = + container_of(pdata, struct synaptics_rmi4_data, board); + + if (enable) { + if (!gpio_is_valid(pdata->irq_gpio)) { + pr_err("invalid %s\n", INSTANCE_DBG(IRQ_GPIO_NAME, data)); + retval = -EINVAL; + } + retval = gpio_request(pdata->irq_gpio, + INSTANCE_RMI(IRQ_GPIO_NAME, data)); + if (retval) { + pr_err("unable to request %s [%d]: rc=%d\n", + INSTANCE_DBG(IRQ_GPIO_NAME, data), pdata->irq_gpio, retval); + goto err_gpio; + } + retval = gpio_direction_input(pdata->irq_gpio); + if (retval) { + pr_err("unable to set %s [%d] dir: rc=%d\n", + INSTANCE_DBG(IRQ_GPIO_NAME, data), pdata->irq_gpio, retval); + goto err_gpio; + } + + if (!gpio_is_valid(pdata->reset_gpio)) + return retval; + + retval = gpio_request(pdata->reset_gpio, + INSTANCE_RMI(RESET_GPIO_NAME, data)); + if (retval) { + pr_err("unable to request %s [%d]: rc=%d\n", + INSTANCE_DBG(RESET_GPIO_NAME, data), pdata->reset_gpio, retval); + goto err_gpio; + } + retval = gpio_direction_output(pdata->reset_gpio, 1); + if (retval) { + pr_err("unable to set %s [%d] dir: rc=%d\n", + INSTANCE_DBG(RESET_GPIO_NAME, data), pdata->reset_gpio, retval); + goto err_gpio; + } + } else { + gpio_free(pdata->irq_gpio); + if (!gpio_is_valid(pdata->reset_gpio)) + return retval; + gpio_free(pdata->reset_gpio); + } + +err_gpio: + return retval; +} + +static void synaptics_dsx_dt_parse_modifier(struct synaptics_rmi4_data *data, + struct device_node *parent, struct config_modifier *config, + const char *modifier_name, bool active) +{ + struct device *dev = &data->i2c_client->dev; + const char *patch_string; + char node_name[64]; + struct synaptics_clip_area clipa; + struct device_node *np_config; + int err; + + scnprintf(node_name, 63, "%s-%s", modifier_name, + active ? "active" : "suspended"); + np_config = of_find_node_by_name(parent, node_name); + if (!np_config) { + dev_dbg(dev, "%s: node does not exist\n", node_name); + return; + } + + err = of_property_read_string(np_config, "patch-data", + (const char **)&patch_string); + if (!err) { + struct synaptics_dsx_patch *p_data = + synaptics_dsx_init_patch(modifier_name); + + if (!p_data) { + dev_err(dev, "%s: alloc error\n", node_name); + goto clip_area; + } + synaptics_dsx_parse_string(data, patch_string, p_data, true); + if (active) + config->active = p_data; + else + config->suspended = p_data; + } + +clip_area: + + err = of_property_read_u32_array(np_config, "touch-clip-area", + (unsigned int *)&clipa, sizeof(clipa)/sizeof(unsigned int)); + if (!err) { + config->clipa = kzalloc(sizeof(clipa), GFP_KERNEL); + if (!config->clipa) { + dev_err(dev, "clip area allocation failure\n"); + return; + } + memcpy(config->clipa, &clipa, sizeof(clipa)); + pr_notice("using touch clip area in %s\n", node_name); + } + + of_node_put(np_config); +} + +/* ASCII names order MUST match enum */ +static const char *ascii_names[] = { "aod", "stats", "folio", + "charger", "wakeup", "fps", "query", "runtime", "na" +}; + +static int modifier_name2id(const char *name) +{ + int i, len2cmp, chosen = -1; + + for (i = 0; i < SYNA_MOD_MAX; i++) { + len2cmp = min_t(int, strlen(name), strlen(ascii_names[i])); + if (!strncmp(name, ascii_names[i], len2cmp)) { + chosen = i; + break; + } + } + return chosen; +} + +static inline const char *modifier_id2name(int id) +{ + return (id >= 0 && id < SYNA_MOD_MAX) ? + ascii_names[id] : ascii_names[SYNA_MOD_MAX]; +} + +static struct config_modifier *modifier_by_id( + struct synaptics_rmi4_data *data, int id) +{ + struct config_modifier *cm, *found = NULL; + + down(&data->modifiers.list_sema); + list_for_each_entry(cm, &data->modifiers.mod_head, link) { + pr_debug("walk-thru: ptr=%p modifier[%s] id=%d\n", + cm, cm->name, cm->id); + if (cm->id == id) { + found = cm; + break; + } + } + up(&data->modifiers.list_sema); + pr_debug("returning modifier id=%d[%d]\n", found ? found->id : -1, id); + return found; +} + +static int synaptics_dsx_dt_parse_modifiers(struct synaptics_rmi4_data *data) +{ + struct device *dev = &data->i2c_client->dev; + struct device_node *np = dev->of_node; + struct device_node *np_mod; + int i, num_names, ret = 0; + char node_name[64]; + static const char **modifiers_names; + + sema_init(&data->modifiers.list_sema, 1); + INIT_LIST_HEAD(&data->modifiers.mod_head); + data->modifiers.mods_num = 0; + + num_names = of_property_count_strings(np, "config_modifier-names"); + if (num_names < 0) { + dev_err(dev, "Cannot parse config_modifier-names: %d\n", + num_names); + return -ENODEV; + } + + modifiers_names = devm_kzalloc(dev, + sizeof(*modifiers_names) * num_names, GFP_KERNEL); + if (!modifiers_names) + return -ENOMEM; + + for (i = 0; i < num_names; i++) { + ret = of_property_read_string_index(np, "config_modifier-names", + i, &modifiers_names[i]); + if (ret < 0) { + dev_err(dev, "Cannot parse modifier-names: %d\n", ret); + return ret; + } + } + + data->modifiers.mods_num = num_names; + + for (i = 0; i < num_names; i++) { + int id; + struct config_modifier *cm, *config; + + scnprintf(node_name, 63, "config_modifier-%s", + modifiers_names[i]); + np_mod = of_find_node_by_name(np, node_name); + if (!np_mod) { + dev_warn(dev, "cannot find modifier node %s\n", + node_name); + continue; + } + + /* check for duplicate nodes in devtree */ + id = modifier_name2id(modifiers_names[i]); + pr_err("processing modifier %s[%d]\n", node_name, id); + list_for_each_entry(cm, &data->modifiers.mod_head, link) { + if (cm->id == id) { + dev_err(dev, "duplicate modifier node %s\n", + node_name); + return -EFAULT; + } + } + /* allocate modifier's structure */ + config = kzalloc(sizeof(*config), GFP_KERNEL); + if (!config) + return -ENOMEM; + + list_add_tail(&config->link, &data->modifiers.mod_head); + config->name = modifiers_names[i]; + config->id = id; + + if (of_property_read_bool(np_mod, "enable-notification")) { + switch (id) { + case SYNA_MOD_FOLIO: + pr_notice("using folio detection\n"); + data->folio_detection_enabled = true; + break; + case SYNA_MOD_CHARGER: + pr_notice("using charger detection\n"); + data->charger_detection_enabled = true; + break; + case SYNA_MOD_FPS: + pr_notice("using fingerprint sensor detection\n"); + data->fps_detection_enabled = true; + break; + case SYNA_MOD_WAKEUP: + pr_notice("using wakeup detection\n"); + data->wakeup_detection_enabled = true; + break; + case SYNA_MOD_AOD: + case SYNA_MOD_STATS: + break; + + } + } else { + config->effective = true; + dev_dbg(dev, "modifier %s enabled unconditionally\n", + node_name); + } + + dev_dbg(dev, "processing modifier %s[%d]\n", + node_name, config->id); + + synaptics_dsx_dt_parse_modifier(data, np_mod, config, + modifiers_names[i], true); + synaptics_dsx_dt_parse_modifier(data, np_mod, config, + modifiers_names[i], false); + of_node_put(np_mod); + } + + return 0; +} + +static int synaptics_dsx_of_init(struct i2c_client *client, + struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned int key_codes[SYN_MAX_BUTTONS]; + struct synaptics_dsx_platform_data *pdata = &rmi4_data->board; + struct device_node *np = client->dev.of_node; + struct synaptics_dsx_cap_button_map *button_map = NULL; + + retval = synaptics_dsx_dt_parse_modifiers(rmi4_data); + rmi4_data->patching_enabled = !retval; + + if (!force_update_patch && (force_update_patch = kzalloc( + sizeof(struct synaptics_dsx_patch), GFP_KERNEL))) { + force_update_patch->name = "force-update"; + force_update_patch->cfg_num = 1; + sema_init(&force_update_patch->list_sema, 1); + INIT_LIST_HEAD(&force_update_patch->cfg_head); + list_add(&force_update_func_patch.link, + &force_update_patch->cfg_head); + } + + if (!power_sleep_patch && (power_sleep_patch = kzalloc( + sizeof(struct synaptics_dsx_patch), GFP_KERNEL))) { + power_sleep_patch->name = "power-sleep"; + power_sleep_patch->cfg_num = 1; + sema_init(&power_sleep_patch->list_sema, 1); + INIT_LIST_HEAD(&power_sleep_patch->cfg_head); + list_add(&power_sleep_func_patch.link, + &power_sleep_patch->cfg_head); + } + + retval = of_get_gpio(np, 0); + if (retval < 0) { + dev_err(&client->dev, "get irq pin failure\n"); + return -EINVAL; + } + pdata->irq_gpio = retval; + + pdata->reset_gpio = ARCH_NR_GPIOS; + if (of_gpio_count(np) > 1) { + retval = of_get_gpio(np, 1); + if (retval < 0) { + dev_err(&client->dev, "get reset pin failure\n"); + return -EINVAL; + } + pdata->reset_gpio = retval; + } else + dev_info(&client->dev, "only found 1 gpio, assume no rst pin\n"); + + memset(key_codes, 0, sizeof(key_codes)); + retval = of_property_read_u32_array(np, "synaptics,key-buttons", + key_codes, SYN_MAX_BUTTONS); + if (!retval) { + int ii; + unsigned char *button_codes; + + button_map = kzalloc(sizeof(*button_map), GFP_KERNEL); + if (IS_ERR_OR_NULL(button_map)) { + dev_err(&client->dev, "button allocation failure\n"); + return -EINVAL; + } + + for (ii = 0; ii < SYN_MAX_BUTTONS; ii++) + if (key_codes[ii]) + button_map->nbuttons++; + + button_codes = kzalloc(button_map->nbuttons, GFP_KERNEL); + if (IS_ERR_OR_NULL(button_codes)) { + dev_err(&client->dev, "button allocation failure\n"); + kfree(button_map); + return -EINVAL; + } + + for (ii = 0; ii < button_map->nbuttons; ii++) + *(button_codes + ii) = (unsigned char)key_codes[ii]; + + button_map->map = button_codes; + } + + pdata->irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT; + pdata->cap_button_map = button_map; + + if (of_property_read_bool(np, "synaptics,gpio-config")) { + pr_notice("using gpio config\n"); + pdata->gpio_config = synaptics_dsx_gpio_config; + } + + if (of_property_read_bool(np, "synaptics,x-flip")) { + pr_notice("using flipped X axis\n"); + pdata->x_flip = true; + } + + if (of_property_read_bool(np, "synaptics,y-flip")) { + pr_notice("using flipped Y axis\n"); + pdata->y_flip = true; + } + + if (of_property_read_bool(np, "synaptics,purge-enabled")) { + pr_notice("using purge\n"); + rmi4_data->purge_enabled = true; + } + + if (of_property_read_bool(np, "synaptics,splash-screen-mode")) { + pr_notice("splash screen mode\n"); + rmi4_data->splash_screen_mode = true; + } + + retval = of_property_read_u32(np, "synaptics,pm-qos-latency", + &rmi4_data->pm_qos_latency); + if (!retval) + pr_notice("pm qos latency is %d\n", + rmi4_data->pm_qos_latency); + + retval = of_property_read_u32(np, "synaptics,control-dsi", + &rmi4_data->ctrl_dsi); + if (!retval) + pr_notice("control DSI %d\n", rmi4_data->ctrl_dsi); + + retval = of_property_read_string(np, "synaptics,bound-display", + &rmi4_data->bound_display); + if (!retval) + pr_notice("bound to display '%s'\n", rmi4_data->bound_display); + + retval = of_property_read_string(np, "synaptics,class-entry-name", + &rmi4_data->class_entry_name); + if (!retval) + pr_notice("class entry name '%s'\n", rmi4_data->class_entry_name); + + return 0; +} +#else +static inline int synaptics_dsx_of_init(struct i2c_client *client) +{ + return -EINVAL; +} +#endif + +static void synaptics_dsx_validate_product_string(unsigned char *id) +{ + unsigned char *s, *wc; + bool do_once = false, do_all = false; + + for (s = wc = id; *s; s++) { + if (*s == '-') { + do_all = true; + continue; + } + if (!isdigit(*s) && isupper(*s)) { + *s = tolower(*s); + do_once = true; + } + if (do_all || do_once) { + *wc = *s; + do_once = false; + } + wc++; + } + *wc = 0; +} + +#define NANO_SEC 1000000000 +#define SEC_TO_MSEC 1000 +#define NANO_TO_MSEC 1000000 + +static inline unsigned long long timediff_ms( + struct timespec start, struct timespec end) +{ + struct timespec temp; + + if ((end.tv_nsec - start.tv_nsec) < 0) { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = NANO_SEC + end.tv_nsec - start.tv_nsec; + } else { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return (temp.tv_sec * SEC_TO_MSEC) + (temp.tv_nsec / NANO_TO_MSEC); +} + +static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, + unsigned short length); + +static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, + unsigned short length); + +static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data); + +static void synaptics_dsx_release_all(struct synaptics_rmi4_data *rmi4_data); + +#if defined(CONFIG_DRM) +extern bool dsi_display_is_panel_enable(int id, int *probe_status, char **pname); +#endif + +static ssize_t synaptics_rmi4_ud_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_drv_irq_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_drv_irq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_pm_qos_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_pm_qos_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) +static ssize_t synaptics_rmi4_test_irq_delay_ms_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_test_irq_delay_ms_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_test_irq_data_contig_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_test_irq_data_contig_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +#endif + +static ssize_t synaptics_rmi4_hw_irqstat_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_mod_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_mod_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_mod_sw_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_mod_sw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_query_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_query_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_reporting_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_reporting_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t synaptics_rmi4_reporting_ntouch_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t synaptics_rmi4_reporting_ntouch_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +struct synaptics_rmi4_f01_device_status { + union { + struct { + unsigned char status_code:4; + unsigned char reserved:2; + unsigned char flash_prog:1; + unsigned char unconfigured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f01_query1 { + union { + struct { + unsigned char custom_map:1; + unsigned char non_compliant:1; + unsigned char reserved:1; + unsigned char has_sensor_id:1; + unsigned char has_charger_input:1; + unsigned char has_adjustable_doze:1; + unsigned char has_doze_holdoff:1; + unsigned char has_query42:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f01_query42 { + union { + struct { + unsigned char has_ds4_queries:1; + unsigned char has_multi_physical:1; + unsigned char has_guest:1; + unsigned char has_swr:1; + unsigned char has_report_rate:1; + unsigned char has_recalibration:1; + unsigned char reserved:2; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f1a_query { + union { + struct { + unsigned char max_button_count:3; + unsigned char reserved:5; + unsigned char has_general_control:1; + unsigned char has_interrupt_enable:1; + unsigned char has_multibutton_select:1; + unsigned char has_tx_rx_map:1; + unsigned char has_perbutton_threshold:1; + unsigned char has_release_threshold:1; + unsigned char has_strongestbtn_hysteresis:1; + unsigned char has_filter_strength:1; + } __packed; + unsigned char data[2]; + }; +}; + +struct synaptics_rmi4_f1a_control_0 { + union { + struct { + unsigned char multibutton_report:2; + unsigned char filter_mode:2; + unsigned char reserved:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct synaptics_rmi4_f1a_control_3_4 { + unsigned char transmitterbutton; + unsigned char receiverbutton; +}; + +struct synaptics_rmi4_f1a_control { + struct synaptics_rmi4_f1a_control_0 general_control; + unsigned char *button_int_enable; + unsigned char *multi_button; + struct synaptics_rmi4_f1a_control_3_4 *electrode_map; + unsigned char *button_threshold; + unsigned char button_release_threshold; + unsigned char strongest_button_hysteresis; + unsigned char filter_strength; +}; + +struct synaptics_rmi4_f1a_handle { + int button_bitmask_size; + unsigned char button_count; + unsigned char valid_button_count; + unsigned char *button_data_buffer; + unsigned char *button_map; + struct synaptics_rmi4_f1a_query button_query; + struct synaptics_rmi4_f1a_control button_control; +}; + +struct synaptics_rmi4_exp_fn { + enum exp_fn fn_type; + enum ic_modes mode; + bool inserted; + int (*func_init)(struct synaptics_rmi4_data *rmi4_data); + void (*func_remove)(struct synaptics_rmi4_data *rmi4_data); + void (*func_attn)(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask); + int (*func_status)(struct synaptics_rmi4_data *rmi4_data); + struct list_head link; +}; + +static struct device_attribute attrs[] = { + __ATTR(reset, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + synaptics_rmi4_f01_reset_store), + __ATTR(productinfo, S_IRUGO, + synaptics_rmi4_f01_productinfo_show, + synaptics_rmi4_store_error), + __ATTR(buildid, S_IRUGO, + synaptics_rmi4_f01_buildid_show, + synaptics_rmi4_store_error), + __ATTR(flashprog, S_IRUSR | S_IRGRP, + synaptics_rmi4_f01_flashprog_show, + synaptics_rmi4_store_error), + __ATTR(0dbutton, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_0dbutton_show, + synaptics_rmi4_0dbutton_store), + __ATTR(drv_irq, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_drv_irq_show, + synaptics_rmi4_drv_irq_store), + __ATTR(reporting, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_reporting_show, + synaptics_rmi4_reporting_store), + __ATTR(reporting_ntouch, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_reporting_ntouch_show, + synaptics_rmi4_reporting_ntouch_store), + __ATTR(hw_irqstat, S_IRUSR | S_IRGRP, + synaptics_rmi4_hw_irqstat_show, + synaptics_rmi4_store_error), + __ATTR(ic_ver, S_IRUGO, + synaptics_rmi4_ic_ver_show, + synaptics_rmi4_store_error), + __ATTR(mod, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_mod_show, + synaptics_rmi4_mod_store), + __ATTR(mod_sw, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_mod_sw_show, + synaptics_rmi4_mod_sw_store), + __ATTR(query, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_query_show, + synaptics_rmi4_query_store), + __ATTR(tsi, S_IRUSR | S_IRGRP, + synaptics_rmi4_ud_show, + synaptics_rmi4_store_error), + __ATTR(pm_qos, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_pm_qos_show, + synaptics_rmi4_pm_qos_store), +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + __ATTR(test_irq_delay_ms, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_test_irq_delay_ms_show, + synaptics_rmi4_test_irq_delay_ms_store), + __ATTR(test_irq_data_contig, (S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP), + synaptics_rmi4_test_irq_data_contig_show, + synaptics_rmi4_test_irq_data_contig_store), +#endif +}; + +static DEVICE_ATTR(poweron, S_IRUSR | S_IRGRP, + synaptics_rmi4_poweron_show, synaptics_rmi4_store_error); + +static irqreturn_t synaptics_dsx_reset_irq(int irq, void *data) +{ + struct synaptics_rmi4_data *rmi4_data = data; + up(&rmi4_data->reset_semaphore); + return IRQ_HANDLED; +} + +int synaptics_rmi4_sw_reset(struct synaptics_rmi4_data *rmi4_data, + bool requery) { + int retval; + unsigned char page_number; + unsigned short pdt_entry_addr; + struct synaptics_rmi4_fn_desc rmi_fd; + unsigned short cmd_reg_addr; + bool cmd_reg_found = false; + + if (!requery) { + cmd_reg_addr = rmi4_data->f01_cmd_base_addr; + cmd_reg_found = true; + goto sw_reset; + } + + for (page_number = 0; !cmd_reg_found && page_number < PAGES_TO_SERVICE; + page_number++) { + for (pdt_entry_addr = PDT_START; pdt_entry_addr > PDT_END; + pdt_entry_addr -= PDT_ENTRY_SIZE) { + pdt_entry_addr |= (page_number << 8); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + pdt_entry_addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + if (rmi_fd.fn_number == 0) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Reached end of PDT\n", + __func__); + break; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: F%02x found (page %d)\n", + __func__, rmi_fd.fn_number, + page_number); + + if (rmi_fd.fn_number == SYNAPTICS_RMI4_F01) { + cmd_reg_addr = rmi_fd.cmd_base_addr; + cmd_reg_found = true; + break; + } + } + } + +sw_reset: + if (cmd_reg_found) { + uint8_t command = 1; + retval = synaptics_rmi4_i2c_write(rmi4_data, + cmd_reg_addr, + &command, sizeof(command)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to issue SW reset command, error = %d\n", + __func__, retval); + return retval; + } + return 0; + } else + return -ENOENT; + +} + + +int synaptics_dsx_ic_reset(struct synaptics_rmi4_data *rmi4_data, int reset) +{ + int retval; + unsigned long start = jiffies; + struct synaptics_dsx_platform_data + *platform_data = &rmi4_data->board; + bool has_rst_pin = gpio_is_valid(platform_data->reset_gpio); + bool need_to_free_irq = true; + + sema_init(&rmi4_data->reset_semaphore, 0); + + if (has_rst_pin && (reset == RMI4_HW_RESET)) { + gpio_set_value_cansleep(platform_data->reset_gpio, 0); + udelay(1500); + } else if (!has_rst_pin || (reset == RMI4_SW_RESET)) { + retval = synaptics_rmi4_sw_reset(rmi4_data, true); + if (retval < 0) + return retval; + } + + retval = request_irq(rmi4_data->irq, synaptics_dsx_reset_irq, + IRQF_TRIGGER_RISING, rmi4_data->reset_name, + rmi4_data); + if (retval < 0) { + need_to_free_irq = false; + dev_warn(&rmi4_data->i2c_client->dev, + "%s: Failed to request irq: %d\n", + __func__, retval); + } + + if (has_rst_pin && (reset == RMI4_HW_RESET)) + gpio_set_value_cansleep(platform_data->reset_gpio, 1); + + retval = down_timeout(&rmi4_data->reset_semaphore, msecs_to_jiffies(100)); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: timed out waiting for reset to complete\n", __func__); + retval = -ETIMEDOUT; + } else { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: IRQ GPIO=%d after HW reset is %d\n", + __func__, platform_data->irq_gpio, + gpio_get_value_cansleep(platform_data->irq_gpio)); + + retval = (int)jiffies_to_msecs(jiffies-start); + /* insert delay to ensure 1st i2c bus access succeeds */ + udelay(1000); + } + + if (need_to_free_irq) + free_irq(rmi4_data->irq, rmi4_data); + + /* perform SW reset if HW reset failed */ + if (need_to_free_irq && reset != RMI4_SW_RESET && retval == -ETIMEDOUT) + retval = synaptics_dsx_ic_reset(rmi4_data, RMI4_SW_RESET); + + return retval; +} + +static int synaptics_dsx_alloc_input(struct synaptics_rmi4_data *rmi4_data) +{ + char *suffix, input_name[64]; + + rmi4_data->input_dev = input_allocate_device(); + if (IS_ERR_OR_NULL(rmi4_data->input_dev)) + return PTR_ERR(rmi4_data->input_dev); + + if (rmi4_data->class_entry_name) + suffix = (char *)rmi4_data->class_entry_name; + else + suffix = CLASS_PRIMARY_FNAME; + + snprintf(input_name, sizeof(input_name), DRIVER_NAME ".%s", suffix); + rmi4_data->input_dev->name = kstrdup(input_name, GFP_KERNEL); + rmi4_data->input_dev->phys = INSTANCE_RMI(INPUT_PHYS_NAME, rmi4_data); + rmi4_data->input_dev->id.bustype = BUS_I2C; + rmi4_data->input_dev->dev.parent = &rmi4_data->i2c_client->dev; + + set_bit(EV_SYN, rmi4_data->input_dev->evbit); + /* enable power key injection */ + set_bit(EV_KEY, rmi4_data->input_dev->evbit); + input_set_capability(rmi4_data->input_dev, EV_KEY, KEY_POWER); + + pr_debug("allocated input device '%s'\n", rmi4_data->input_dev->name); + + return 0; +} + +static void synaptics_copy_multiple_subpkts( + struct synaptics_rmi4_packet_reg *reg, + struct synaptics_dsx_func_patch *fp) +{ + int i, b2c, leftover = fp->size; + unsigned char *data_ptr = fp->data; + struct synaptics_rmi4_subpkt *subpkt; + + for (i = fp->subpkt; i < reg->nr_subpkts; i++) { + subpkt = reg->subpkt + i; + if (!subpkt->present || !subpkt->data) + continue; + b2c = min(leftover, (int)subpkt->size); + memcpy(subpkt->data, data_ptr, b2c); + pr_debug("copied %d bytes to subpkt %d\n", b2c, i); + data_ptr += subpkt->size; + leftover -= subpkt->size; + } + pr_debug("%s misalignement detected\n", !leftover ? "no" : ""); +} + +static void synaptics_dsx_patch_function( + struct synaptics_rmi4_data *rmi4_data, int f_number, + struct synaptics_dsx_patch *patch) +{ + int r, error, function; + unsigned char *destination; + unsigned char *value, rt_mod; + struct device *dev = &rmi4_data->i2c_client->dev; + struct synaptics_rmi4_subpkt *subpkt; + struct synaptics_dsx_func_patch *fp; + struct synaptics_rmi4_packet_reg *reg; + struct synaptics_rmi4_func_packet_regs *regs = + find_function(rmi4_data, f_number); + + function = regs->f_number & 0xff; + rt_mod = register_type_to_ascii(regs->f_number & 0xf00); + pr_debug("patching F%x%c\n", function, rt_mod); + down(&patch->list_sema); + list_for_each_entry(fp, &patch->cfg_head, link) { + if (fp->func != f_number) + continue; + reg = find_packet_reg(regs, fp->regstr); + if (!reg || reg->offset < 0) { + pr_err("F%x%c@%d not present\n", + function, rt_mod, fp->regstr); + continue; + } + if (fp->subpkt >= reg->nr_subpkts) { + pr_err("F%x%c@%d:%d not present\n", + function, rt_mod, fp->regstr, fp->subpkt); + continue; + } + + /* read register only once */ + if (!reg->updated) { + error = synaptics_rmi4_read_packet_reg( + rmi4_data, regs, reg->r_number); + if (error < 0) + goto unlock_and_leave; +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + { + int ss, kk; + pr_debug("F%x%c@%d before patching:\n", + function, rt_mod, reg->r_number); + for (ss = 0; ss < reg->nr_subpkts; ss++) { + subpkt = reg->subpkt + ss; + for (kk = 0; kk < subpkt->size; ++kk) + pr_debug("%02x\n", + ((unsigned char *)subpkt->data)[kk]); + } + } +#endif + /* value has been updated */ + reg->updated = true; + } + + subpkt = reg->subpkt + fp->subpkt; + destination = (unsigned char *)subpkt->data; + if (!subpkt->present || !subpkt->data || + subpkt->size < fp->size) { + pr_debug("F%x%c@%d:%d subpkt: size=%d, offset=%d; reg-size=%d; fp-size=%d\n", + function, rt_mod, + fp->regstr, fp->subpkt, + subpkt->size, subpkt->offset, + reg->size, fp->size); + /* If more than one consequitive subpacket within */ + /* the same packet register needs to be updated, */ + /* then patch might include data for all subpackets */ + /* combined into a single array. Then its size will */ + /* be more than individual subpacket size. It should */ + /* not be a problem as packet register is updated */ + /* all in once. And as long as size of data array */ + /* fits the register, update should be allowed */ + if (reg->size <= (subpkt->offset + fp->size)) { + pr_debug("F%x%c@%d:%d improperly allocated\n", + function, rt_mod, + fp->regstr, fp->subpkt); + continue; + } else { + synaptics_copy_multiple_subpkts(reg, fp); + /* whole patch has been applied already */ + destination = NULL; + } + } + if (fp->bitmask && fp->size == 1) { + value = (unsigned char *)subpkt->data; + pr_debug("patching by mask: source 0x%x\n", *value); + *value &= ~(fp->bitmask); + pr_debug("patching by mask: clear-ed 0x%x\n", *value); + *value |= *fp->data; + pr_debug("patching by mask: result 0x%x\n", *value); + /* value has been modified already */ + destination = NULL; + } + + if (destination) + memcpy(destination, fp->data, fp->size); + + /* value has been changed */ + reg->modified = true; + + dev_dbg(dev, "patched F%x%c@%d:%d, sz=%d\n", + function, rt_mod, fp->regstr, fp->subpkt, fp->size); + } + + /* update loop */ + for (r = 0; r < regs->nr_regs; r++) { + reg = ®s->regs[r]; + if (reg->updated && reg->modified) { +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + { + int ss, kk; + pr_debug("F%x%c@%d after patching:\n", + function, rt_mod, reg->r_number); + for (ss = 0; ss < reg->nr_subpkts; ss++) { + subpkt = reg->subpkt + ss; + for (kk = 0; kk < subpkt->size; ++kk) + pr_debug("%02x\n", + ((unsigned char *)subpkt->data)[kk]); + } + } +#endif + error = synaptics_rmi4_write_packet_reg(rmi4_data, + regs, reg->r_number); + if (error < 0) + dev_warn(dev, "F%x%c@%d patch write failed\n", + function, rt_mod, reg->r_number); + reg->modified = false; + } + reg->updated = false; + } + +unlock_and_leave: + up(&patch->list_sema); +} + +static void synaptics_dsx_enable_wakeup_source( + struct synaptics_rmi4_data *rmi4_data, bool enable) +{ + int error; + error = irq_set_irq_wake(rmi4_data->irq, (int)enable); + pr_debug("%s wakeup; rc=%d\n", enable ? "enabled" : "disabled", error); +} + +static const char * const synaptics_state_names[] = {"UNKNOWN", + "ACTIVE", "SUSPEND", "UNUSED", "STANDBY", "BL", "INIT", + "FLASH", "QUERY", "LOADING", "INVALID" }; + +static const char *synaptics_dsx_state_name(int state) +{ + int index = state < 0 || state > STATE_INVALID ? STATE_INVALID : state; + return synaptics_state_names[index]; +} + +static void synaptics_dsx_apply_modifiers( + struct synaptics_rmi4_data *rmi4_data, int state) +{ + bool wakeup = false, update = false, sleep = false; + int i; + + if (!rmi4_data->modifiers.mods_num) { + pr_debug("patchset is empty!\n"); + goto no_modifiers; + } + + down(&rmi4_data->modifiers.list_sema); + for (i = 0; i < MAX_CONFIG_REGS && SLOT_IN_USE; i++) { + struct config_modifier *cm; + + /* skip query registers */ + if (rmi4_data->config_regs[i].f_number & QUERY_TYPE) + continue; + + list_for_each_entry(cm, &rmi4_data->modifiers.mod_head, link) { + struct synaptics_dsx_patch *patch = NULL; + + /* skip disabled modifiers */ + if (!cm->effective) + continue; + + if (state == STATE_ACTIVE) + patch = cm->active; + else if (state == STATE_SUSPEND) + patch = cm->suspended; + + /* skip effective modifier without patch */ + if (!patch) + continue; + + /* keep track of flags among effective modifiers */ + if (!update && (patch->flags & FLAG_FORCE_UPDATE)) + update = true; + if (!wakeup && (patch->flags & FLAG_WAKEABLE)) + wakeup = true; + if (!sleep && (patch->flags & FLAG_POWER_SLEEP)) + sleep = true; + /* finally apply patch */ + synaptics_dsx_patch_function(rmi4_data, + rmi4_data->config_regs[i].f_number, patch); + } + } + up(&rmi4_data->modifiers.list_sema); + + /* force update regardless the current state */ + if (update && force_update_patch) { + pr_debug("applying forced update patch!\n"); + synaptics_dsx_patch_function(rmi4_data, + SYNAPTICS_RMI4_F54 | COMMAND_TYPE, force_update_patch); + } + + /* power mode and wakeability only on entering suspend */ + if (state == STATE_SUSPEND) { + if (wakeup) { + rmi4_data->suspend_is_wakeable = true; + synaptics_dsx_enable_wakeup_source(rmi4_data, true); + } else if (sleep && power_sleep_patch) { + pr_debug("applying sleep patch!\n"); + synaptics_dsx_patch_function(rmi4_data, + SYNAPTICS_RMI4_F01, power_sleep_patch); + } + } + + if (!wakeup && rmi4_data->suspend_is_wakeable) { + rmi4_data->suspend_is_wakeable = false; + synaptics_dsx_enable_wakeup_source(rmi4_data, false); + } + +no_modifiers: + /* keep page 0 active */ + synaptics_rmi4_set_page(rmi4_data, 0); +} + +static int synaptics_dsx_get_state_safe(struct synaptics_rmi4_data *rmi4_data) +{ + int state; + mutex_lock(&(rmi4_data->state_mutex)); + state = rmi4_data->state; + mutex_unlock(&(rmi4_data->state_mutex)); + return state; +} + +static void synaptics_dsx_set_state_safe(struct synaptics_rmi4_data *rmi4_data, + int state) +{ + mutex_lock(&(rmi4_data->state_mutex)); + rmi4_data->state = state; + mutex_unlock(&(rmi4_data->state_mutex)); +} + +int synaptics_dsx_wait_for_idle(struct synaptics_rmi4_data *rmi4_data) +{ + unsigned long start_wait_jiffies = jiffies; + + do { + int current_state; + + current_state = synaptics_dsx_get_state_safe(rmi4_data); + if (!(current_state == STATE_INIT || + current_state == STATE_FLASH || + current_state == STATE_LOADING || + current_state == STATE_UNKNOWN)) + break; + + usleep_range(1000, 1000); + + } while (1); + + if ((jiffies - start_wait_jiffies)) + pr_info("entering suspend delayed for %ums\n", + jiffies_to_msecs(jiffies - start_wait_jiffies)); + return 0; +} + +static int synaptics_rmi4_f01_flashprog_status( + struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + struct synaptics_rmi4_f01_device_status status; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr, + status.data, + sizeof(status.data)); + return retval < 0 ? retval : status.flash_prog; +} + +int synaptics_dsx_sensor_ready_state( + struct synaptics_rmi4_data *rmi4_data, bool standby) +{ + bool ui_mode; + int retval, state, i; + + for (i = 0; i < 10; ++i) { + retval = synaptics_rmi4_f01_flashprog_status(rmi4_data); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: (%d): flashprog bit=%d\n", + __func__, i, retval); + /* if simple flashprog bit check reports BL mode */ + /* then do not bother checking F34 flash status */ + if (!retval && + rmi4_data->get_status != synaptics_rmi4_f01_flashprog_status) { + retval = rmi4_data->get_status(rmi4_data); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: (%d): f34 flash status %d\n", + __func__, i, retval); + } + + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: (%d) Failed to query touch ic status\n", + __func__, i); + return retval; + } + + ui_mode = retval == 0; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: (%d) UI mode: %s\n", + __func__, i, ui_mode ? "true" : "false"); + if (ui_mode) + break; + + msleep(20); + } + + state = synaptics_dsx_get_state_safe(rmi4_data); + if (!ui_mode && state == STATE_SUSPEND && rmi4_data->input_registered) { + /* expecting touch IC to enter UI mode based on */ + /* its previous known good state */ + dev_err(&rmi4_data->i2c_client->dev, + "%s: Timed out waiting for UI mode - UI mode forced\n", + __func__); + ui_mode = 1; + } + + if (ui_mode) { + state = standby ? STATE_STANDBY : STATE_ACTIVE; + } else + if (!(state == STATE_INIT || state == STATE_FLASH)) + state = STATE_BL; + + synaptics_dsx_sensor_state(rmi4_data, state); + + return 0; +} + +static void synaptics_dsx_enforce_modifiers( + struct synaptics_rmi4_data *rmi4_data, + struct config_modifier *modifier) +{ + int error, state = synaptics_dsx_get_state_safe(rmi4_data); + + if (state == STATE_ACTIVE) { + if (!modifier->active) + return; + /* set unknown state to ensure IRQ gets */ + /* enabled on state transition to active */ + synaptics_dsx_sensor_state(rmi4_data, STATE_UNKNOWN); + /* disable IRQ to handle reset */ + synaptics_rmi4_irq_enable(rmi4_data, false); + /* perform SW reset to restore defaults */ + error = synaptics_dsx_ic_reset(rmi4_data, RMI4_SW_RESET); + if (error < 0) + dev_err(&rmi4_data->i2c_client->dev, + "folio: sw reset failed %d\n", error); + } + synaptics_dsx_sensor_ready_state(rmi4_data, false); +} + +void synaptics_dsx_sensor_state(struct synaptics_rmi4_data *rmi4_data, + int state) +{ + if (synaptics_dsx_get_state_safe(rmi4_data) == state) + return; + + switch (state) { + case STATE_UNKNOWN: + rmi4_data->in_bootloader = false; + case STATE_FLASH: + case STATE_LOADING: + /* no special handling for these states */ + break; + + case STATE_SUSPEND: + synaptics_dsx_wait_for_idle(rmi4_data); + + if (!rmi4_data->in_bootloader) + synaptics_dsx_apply_modifiers(rmi4_data, STATE_SUSPEND); + + if (!rmi4_data->suspend_is_wakeable) { + synaptics_rmi4_irq_enable(rmi4_data, false); + } + break; + + case STATE_ACTIVE: + if (!rmi4_data->in_bootloader) + synaptics_dsx_apply_modifiers(rmi4_data, STATE_ACTIVE); + + if (rmi4_data->input_registered) + synaptics_rmi4_irq_enable(rmi4_data, true); + else { + synaptics_rmi4_irq_enable(rmi4_data, false); + pr_err("Active state without input device\n"); + } + break; + + case STATE_STANDBY: + synaptics_rmi4_irq_enable(rmi4_data, false); + break; + + case STATE_BL: + if (!rmi4_data->in_bootloader) + rmi4_data->in_bootloader = true; + case STATE_INIT: + synaptics_rmi4_irq_enable(rmi4_data, false); + + if (!rmi4_data->in_bootloader) { + int retval; + unsigned char mode = 1; + unsigned char intr_status[MAX_INTR_REGISTERS]; + + /* deactivate touch by putting touch IC into */ + /* sleep mode to prevent interference of touch */ + /* processing with flashing */ + pr_debug("enter sleep mode\n"); + retval = synaptics_rmi4_i2c_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + &mode, + sizeof(mode)); + + if (retval < 0) + pr_notice("failed to enter sleep mode\n"); + + /* clear unhandled interrupt if any */ + pr_debug("clear interrupt\n"); + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + &intr_status[0], + rmi4_data->num_of_intr_regs); + + if (retval < 0) + pr_notice("failed to clear interrupt\n"); + } + break; + } + + dev_info(&rmi4_data->i2c_client->dev, "state change %s -> %s\n", + synaptics_dsx_state_name(rmi4_data->state), + synaptics_dsx_state_name(state)); + + synaptics_dsx_set_state_safe(rmi4_data, state); +} + +#define PM_ADD 0 +#define PM_UPDATE 1 +#define PM_REMOVE 2 + +static void synaptics_dsx_pm_qos(struct synaptics_rmi4_data *rmi4_data, + int update, bool use_default) +{ +#if 0 + if (irq_can_set_affinity(rmi4_data->irq)) + rmi4_data->pm_qos_irq.type = PM_QOS_REQ_AFFINE_IRQ; + else + rmi4_data->pm_qos_irq.type = PM_QOS_REQ_ALL_CORES; + + switch (update) { + case PM_REMOVE: + pm_qos_remove_request(&rmi4_data->pm_qos_irq); + break; + case PM_ADD: + pm_qos_add_request(&rmi4_data->pm_qos_irq, + PM_QOS_CPU_DMA_LATENCY, + rmi4_data->pm_qos_latency); + break; + case PM_UPDATE: + pm_qos_update_request(&rmi4_data->pm_qos_irq, + use_default ? PM_QOS_DEFAULT_VALUE : + rmi4_data->pm_qos_latency); + break; + } +#endif +} + +static void ud_set_id(struct touch_area_stats *tas, int id) +{ + tas->ud_id = id; +} + +static void ud_log_status(struct touch_area_stats *tas, bool down) +{ + struct touch_up_down *ud = tas->ud; + ssize_t id = tas->ud_id; + + if (id >= tas->ud_len) + tas->unknown_counter++; + + if (!down) { /* up */ + if (ud[id].up_down == 0x10) { + pr_debug("%s UP[%zu]\n", tas->name, id); + ud[id].up_down |= 1; + ud[id].mismatch--; + } + } else if (down) { /* down */ + if (ud[id].up_down == 0) { + ud[id].up_down |= (1 << 4); + pr_debug("%s DOWN[%zu]\n", tas->name, id); + ud[id].mismatch++; + } else if (ud[id].up_down == 0x10) + return; + } + + if (ud[id].up_down == 0x11) { + pr_debug("%s CLEAR[%zu]\n", tas->name, id); + ud[id].up_down = 0; + ud[id].counter++; + } +} + +static void TSI_state(struct synaptics_rmi4_data *rmi4_data, + unsigned int tool, bool status) +{ + ud_log_status(&rmi4_data->touch_ud_stats, status); + input_mt_report_slot_state(rmi4_data->input_dev, tool, status); +} + +static void TSI_id(struct synaptics_rmi4_data *rmi4_data, int id) +{ + ud_set_id(&rmi4_data->touch_ud_stats, id); + input_mt_slot(rmi4_data->input_dev, id); +} + +#define INPUT_REPORT_MT_SLOT_STATE TSI_state +#define INPUT_REPORT_MT_SLOT TSI_id + +static ssize_t synaptics_rmi4_ud_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i; + ssize_t total = 0; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + total += scnprintf(buf + total, PAGE_SIZE - total, "display:\n"); + for (i = 0; i < rmi4_data->touch_ud_stats.ud_len; i++) + total += scnprintf(buf + total, PAGE_SIZE - total, + "[%d]: full cycles-%u, mismatch-%d\n", i, + rmi4_data->touch_ud_stats.ud[i].counter, + rmi4_data->touch_ud_stats.ud[i].mismatch); + + if (rmi4_data->button_0d_enabled) { + total += scnprintf(buf + total, PAGE_SIZE - total, + "buttons:\n"); + for (i = 0; i < rmi4_data->button_ud_stats.ud_len; i++) + total += scnprintf(buf + total, PAGE_SIZE - total, + "[%d]: full cycles-%u, mismatch-%d\n", i, + rmi4_data->button_ud_stats.ud[i].counter, + rmi4_data->button_ud_stats.ud[i].mismatch); + } + return total; +} + +static ssize_t synaptics_dsx_ud_stat(struct synaptics_rmi4_data *rmi4_data, + char *buf, ssize_t size) +{ + int i; + ssize_t total = 0; + + total += scnprintf(buf + total, size - total, "screen: "); + for (i = 0; i < rmi4_data->touch_ud_stats.ud_len; i++) + if (rmi4_data->touch_ud_stats.ud[i].mismatch) + total += scnprintf(buf + total, size - total, + "%d)%u,%d ", i, + rmi4_data->touch_ud_stats.ud[i].counter, + rmi4_data->touch_ud_stats.ud[i].mismatch); + else if (rmi4_data->touch_ud_stats.ud[i].counter) + total += scnprintf(buf + total, size - total, + "%d)%u ", i, + rmi4_data->touch_ud_stats.ud[i].counter); + + if (rmi4_data->button_0d_enabled) { + total += scnprintf(buf + total, size - total, "buttons: "); + for (i = 0; i < rmi4_data->button_ud_stats.ud_len; i++) + if (rmi4_data->button_ud_stats.ud[i].mismatch) + total += scnprintf(buf + total, size - total, + "%d)%u,%d ", i, + rmi4_data->button_ud_stats.ud[i].counter, + rmi4_data->button_ud_stats.ud[i].mismatch); + else if (rmi4_data->button_ud_stats.ud[i].counter) + total += scnprintf(buf + total, size - total, + "%d)%u ", i, + rmi4_data->button_ud_stats.ud[i].counter); + } + return total; +} + +static ssize_t synaptics_rmi4_f01_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int reset; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + + if (sscanf(buf, "%u", &reset) != 1) + return -EINVAL; + + if (reset != 1) + return -EINVAL; + + retval = synaptics_dsx_ic_reset(rmi4_data, RMI4_HW_RESET); + if (retval > 0) + pr_debug("successful reset took %dms\n", retval); + else + dev_warn(&rmi4_data->i2c_client->dev, "%s: timed out waiting for idle\n", + __func__); + + return count; +} + +static ssize_t synaptics_rmi4_f01_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%s\n", + rmi4_data->rmi4_mod_info.product_id_string); +} + +static ssize_t synaptics_rmi4_f01_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int firmware_id; + unsigned int config_id; + struct synaptics_rmi4_device_info *rmi; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + rmi = &(rmi4_data->rmi4_mod_info); + + batohui(&firmware_id, rmi->build_id, sizeof(rmi->build_id)); + batohui(&config_id, rmi->config_id, sizeof(rmi->config_id)); + + return scnprintf(buf, PAGE_SIZE, "%x-%08x\n", firmware_id, config_id); +} + +static ssize_t synaptics_rmi4_f01_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%d\n", rmi4_data->in_bootloader); +} + +static ssize_t synaptics_rmi4_hw_irqstat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + switch (gpio_get_value_cansleep(rmi4_data->board.irq_gpio)) { + case 0: + return scnprintf(buf, PAGE_SIZE, "Low\n"); + case 1: + return scnprintf(buf, PAGE_SIZE, "High\n"); + default: + pr_err("Failed to get GPIO for irq %d\n", rmi4_data->irq); + return scnprintf(buf, PAGE_SIZE, "Unknown\n"); + } +} + +#define SPRINTF_PAGE(fmt, args...) {\ + blen += scnprintf(buf + blen, PAGE_SIZE - blen, fmt, ##args);\ + } + +static ssize_t synaptics_rmi4_reporting_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + if (rmi4_data->rctrl.reporting_stopped) + length = scnprintf(buf, PAGE_SIZE, "STOPPED(%u)\n", + rmi4_data->rctrl.events_cnt); + else + length = scnprintf(buf, PAGE_SIZE, "RUNNING\n"); + return length; +} + +static ssize_t synaptics_rmi4_reporting_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value = 0; + int err = 0; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + if (*buf == 's' || *buf == 'S') { + rmi4_data->rctrl.reporting_stopped = true; + } else if (*buf == 'r' || *buf == 'R') { + rmi4_data->rctrl.reporting_stopped = false; + } else { + err = kstrtoul(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + rmi4_data->rctrl.reporting_stopped = value == 0; + } + if (rmi4_data->rctrl.reporting_stopped) + rmi4_data->rctrl.events_cnt = 0; + return count; +} + +static ssize_t synaptics_rmi4_reporting_ntouch_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t length; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + down(&rmi4_data->rctrl.ctrl_sema); + length = scnprintf(buf, PAGE_SIZE, "%d\n", rmi4_data->rctrl.max_seen); + rmi4_data->rctrl.max_seen = 0; + up(&rmi4_data->rctrl.ctrl_sema); + + return length; +} + +static ssize_t synaptics_rmi4_reporting_ntouch_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value; + int err; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + err = kstrtoul(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + down(&rmi4_data->rctrl.ctrl_sema); + rmi4_data->rctrl.expected = (int)value; + rmi4_data->rctrl.max_seen = 0; + up(&rmi4_data->rctrl.ctrl_sema); + + return count; +} + +static ssize_t synaptics_rmi4_drv_irq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%s\n", + rmi4_data->irq_enabled ? "ENABLED" : "DISABLED"); +} + +static ssize_t synaptics_rmi4_drv_irq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value = 0; + int err = 0; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + err = kstrtoul(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + switch (value) { + case 0: + /* Disable irq */ + synaptics_rmi4_irq_enable(rmi4_data, false); + break; + case 1: + /* Enable irq */ + synaptics_rmi4_irq_enable(rmi4_data, true); + break; + default: + pr_err("Invalid value\n"); + return -EINVAL; + } + return count; +} + +static ssize_t synaptics_rmi4_pm_qos_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%d\n", rmi4_data->pm_qos_latency); +} + +static ssize_t synaptics_rmi4_pm_qos_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned long value = 0; + uint32_t old; + int err = 0; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + err = kstrtoul(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + old = rmi4_data->pm_qos_latency; + + switch (value) { + case 0: + rmi4_data->pm_qos_latency = PM_QOS_DEFAULT_VALUE; + break; + default: + rmi4_data->pm_qos_latency = value; + break; + } + + if (old != rmi4_data->pm_qos_latency && + atomic_read(&rmi4_data->touch_stopped) == 0) { + synaptics_dsx_pm_qos(rmi4_data, PM_UPDATE, false); + pr_debug("set pm qos latency to %d\n", + rmi4_data->pm_qos_latency); + } + + return count; +} + +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) +static ssize_t synaptics_rmi4_test_irq_delay_ms_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%d\n", rmi4_data->test_irq_delay_ms); +} + +static ssize_t synaptics_rmi4_test_irq_delay_ms_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + int err; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + + err = kstrtouint(buf, 10, &value); + if (err < 0) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + rmi4_data->test_irq_delay_ms = value; + return count; +} + +static ssize_t synaptics_rmi4_test_irq_data_contig_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%d\n", rmi4_data->test_irq_data_contig); +} + +static ssize_t synaptics_rmi4_test_irq_data_contig_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value = 0; + int err; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + + err = kstrtouint(buf, 10, &value); + if (err < 0 || !(value == 0 || value == 1)) { + pr_err("Failed to convert value\n"); + return -EINVAL; + } + + if (rmi4_data->test_irq_data_contig != value) { + rmi4_data->test_irq_data_contig = value; + synaptics_rmi4_irq_enable(rmi4_data, false); + /* requery for change to take effect */ + if (synaptics_rmi4_query_device(rmi4_data) < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to query device\n", + __func__); + } + synaptics_rmi4_irq_enable(rmi4_data, true); + } + return count; +} +#endif + +static ssize_t synaptics_rmi4_0dbutton_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%u\n", + rmi4_data->button_0d_enabled); +} + +static ssize_t synaptics_rmi4_0dbutton_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + unsigned char ii; + unsigned char intr_enable; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + rmi = &(rmi4_data->rmi4_mod_info); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + input = input > 0 ? 1 : 0; + + if (rmi4_data->button_0d_enabled == input) + return count; + + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) { + ii = fhandler->intr_reg_num; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr + 1 + ii, + &intr_enable, + sizeof(intr_enable)); + if (retval < 0) + return retval; + + if (input == 1) + intr_enable |= fhandler->intr_mask; + else + intr_enable &= ~fhandler->intr_mask; + + retval = synaptics_rmi4_i2c_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr + 1 + ii, + &intr_enable, + sizeof(intr_enable)); + if (retval < 0) + return retval; + } + } + + rmi4_data->button_0d_enabled = input; + + return count; +} + +static ssize_t synaptics_rmi4_ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned int build_id, config_id; + struct synaptics_rmi4_device_info *rmi; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + rmi = &(rmi4_data->rmi4_mod_info); + batohui(&build_id, rmi->build_id, sizeof(rmi->build_id)); + batohui(&config_id, rmi->config_id, sizeof(rmi->config_id)); + return scnprintf(buf, PAGE_SIZE, + "%s%s\n%s%x\n%s%x\n", + "Product ID: ", rmi->product_id_string, + "Build ID: ", build_id, + "Config ID: ", config_id); +} + +static ssize_t synaptics_rmi4_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + return scnprintf(buf, PAGE_SIZE, "%d\n", + atomic_read(&rmi4_data->touch_stopped) == 0 && + rmi4_data->flash_enabled); +} + +static ssize_t synaptics_dsx_patch_dump( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_dsx_patch *patch, + char *pout, ssize_t msize) +{ + struct synaptics_dsx_func_patch *fp; + ssize_t blen = 0; + + down(&patch->list_sema); + list_for_each_entry(fp, &patch->cfg_head, link) { + int i; + unsigned int data_size = fp->size; + unsigned char *value = fp->data; + + blen += scnprintf(pout + blen, msize - blen, + "F%02x%c@%d:%d=", fp->func & 0xff, + register_type_to_ascii(fp->func & 0xf00), + fp->regstr, fp->subpkt); + + for (i = 0; i < data_size; i++) + blen += scnprintf(pout + blen, msize - blen, + "%02x", *value++); + if (fp->bitmask) + blen += scnprintf(pout + blen, msize - blen, + "&%02x", fp->bitmask); + blen += scnprintf(pout + blen, msize - blen, "; "); + } + up(&patch->list_sema); + + if (patch->flags & FLAG_FORCE_UPDATE) + blen += scnprintf(pout + blen, msize - blen, "U; "); + if (patch->flags & FLAG_WAKEABLE) + blen += scnprintf(pout + blen, msize - blen, "W; "); + if (patch->flags & FLAG_POWER_SLEEP) + blen += scnprintf(pout + blen, msize - blen, "S; "); + blen += scnprintf(pout + blen, msize - blen, "\n"); + + return blen; +} + +static ssize_t synaptics_dsx_patch_query( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_dsx_patch *patch, + char *pout, ssize_t msize) +{ + struct synaptics_dsx_func_patch *fp; + ssize_t blen = 0; + + down(&patch->list_sema); + list_for_each_entry(fp, &patch->cfg_head, link) { + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_packet_reg *reg; + int i, error, function; + unsigned short data_addr; + unsigned int data_size; + unsigned char *value, rt_mod; + + regs = find_function(rmi4_data, fp->func); + if (!regs) + continue; + reg = find_packet_reg(regs, fp->regstr); + if (!reg || reg->offset < 0) { + pr_err("F%x@%d not present\n", fp->func, fp->regstr); + continue; + } + + rt_mod = register_type_to_ascii(fp->func & 0xf00); + function = fp->func & 0xff; + + error = synaptics_rmi4_read_packet_reg(rmi4_data, + regs, reg->r_number); + if (error < 0) { + pr_err("F%x@%d register read failed\n", + function, fp->regstr); + continue; + } + /* calculate register address */ + data_addr = regs->base_addr + reg->offset; + /* dump whole register if subpacket is 255 */ + if (fp->subpkt == 0xff) { + value = reg->data; + data_size = reg->size; + blen += scnprintf(pout + blen, msize - blen, + "F%x%c@%d{%X}=", + function, rt_mod, fp->regstr, data_addr); + } else { + struct synaptics_rmi4_subpkt *subpkt; + + subpkt = reg->subpkt + fp->subpkt; + value = (unsigned char *)subpkt->data; + data_size = subpkt->size; + data_addr += subpkt->offset; + blen += scnprintf(pout + blen, msize - blen, + "F%x%c@%d:%d{%X}=", + function, rt_mod, fp->regstr, + fp->subpkt, data_addr); + } + + for (i = 0; i < data_size; i++) + blen += scnprintf(pout + blen, msize - blen, + "%02x", *value++); + blen += scnprintf(pout + blen, msize - blen, "; "); + } + up(&patch->list_sema); + blen += scnprintf(pout + blen, msize - blen, "\n"); + return blen; +} + +static ssize_t modifiers_show(struct synaptics_rmi4_data *rmi4_data, + char *buf, size_t count, bool show_data, bool show_query) +{ + struct config_modifier *cm; + ssize_t (*handler)(struct synaptics_rmi4_data *, + struct synaptics_dsx_patch *, + char *, ssize_t); + ssize_t added, blen = 0; + + if (!rmi4_data->modifiers.mods_num) { + pr_debug("patchset is empty!\n"); + return 0; + } + + down(&rmi4_data->modifiers.list_sema); + list_for_each_entry(cm, &rmi4_data->modifiers.mod_head, link) { + const char *name = cm->name ? cm->name : "na"; + bool is_query = cm->id == SYNA_MOD_QUERY; + + /* skip printing modifiers, but query */ + if (!is_query && show_query) + continue; + blen += scnprintf(buf + blen, count - blen, "%c[%s]\n", + cm->effective ? '+' : '-', name); + if (!show_data) + continue; + + if (is_query && show_query) + handler = &synaptics_dsx_patch_query; + else + handler = &synaptics_dsx_patch_dump; + + if (cm->active) { + blen += scnprintf(buf + blen, + count - blen, " [active]"); + added = (*handler)(rmi4_data, cm->active, + buf + blen, count - blen); + blen += added; + count -= added; + } + + if (cm->clipa) { + blen += scnprintf(buf + blen, count - blen, + " [clip]<%u,%u,%u,%u>%s\n", + cm->clipa->xul_clip, cm->clipa->yul_clip, + cm->clipa->xbr_clip, cm->clipa->ybr_clip, + cm->clipa->inversion ? "in" : "out"); + } + + if (cm->suspended) { + blen += scnprintf(buf + blen, + count - blen, " [suspended]"); + added = (*handler)(rmi4_data, cm->suspended, + buf + blen, count - blen); + blen += added; + count -= added; + } + } + up(&rmi4_data->modifiers.list_sema); + + return blen; +} + +#define NAME_ID 0 +#define ACTION_ID 1 + +static ssize_t synaptics_rmi4_mod_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_dsx_patch **patch_ptr, *patch; + struct config_modifier *cm; + bool clear_only = false; + int id, bytes_left, ntokens = 0; + char *internal, *ptr, *token[2] = {0}; + + internal = kstrdup(buf, GFP_KERNEL); + for (ptr = internal; ntokens < 2; ntokens++) { + bytes_left = count - (ptr - internal); + ptr = strnchr(ptr, bytes_left, '['); + if (!ptr) + break; + token[ntokens] = ++ptr; /* advance to the name */ + bytes_left = count - (ptr - internal); + ptr = strnchr(ptr, bytes_left, ']'); + if (ptr) + *ptr++ = 0; + dev_dbg(dev, "token[%d]='%s'\n", ntokens, token[ntokens]); + } + + if (*buf != '[' && ntokens != 2) { + dev_err(dev, "invalid modifier syntax\n"); + count = -EINVAL; + goto leave_now; + } + + id = modifier_name2id(token[NAME_ID]); + switch (id) { + case SYNA_MOD_QUERY: + dev_err(dev, "use query attribute instead\n"); + count = -EINVAL; + break; + case -1: + dev_err(dev, "[%s] is invalid modifier\n", token[NAME_ID]); + count = -EINVAL; + break; + } + + if (count == -EINVAL) + goto leave_now; + + /* pointer has been advanced in the loop above to point to patch */ + if (*ptr == '\n') + clear_only = true; + + cm = modifier_by_id(rmi4_data, id); + if (!cm) { + if (clear_only) + goto leave_now; + + dev_info(dev, "allocating modifier [%s]\n", token[NAME_ID]); + cm = kzalloc(sizeof(*cm), GFP_KERNEL); + if (!cm) { + count = -ENOMEM; + goto leave_now; + } + + down(&rmi4_data->modifiers.list_sema); + list_add_tail(&cm->link, &rmi4_data->modifiers.mod_head); + up(&rmi4_data->modifiers.list_sema); + rmi4_data->modifiers.mods_num++; + cm->name = ascii_names[id]; + cm->id = id; + } + + if (!strncmp(token[ACTION_ID], "clip", 4)) { + bool clipping_is_active = false; + struct synaptics_clip_area clipa; + + if (cm->clipa) { + if (rmi4_data->clipping_on && + rmi4_data->clipa == cm->clipa) { + /* if currently effective clipping */ + /* is the one being edited */ + rmi4_data->clipa = NULL; + clipping_is_active = true; + } + kfree(cm->clipa); + cm->clipa = NULL; + } + + if (clear_only) { + if (clipping_is_active) { + rmi4_data->clipping_on = false; + pr_debug("removed [clip] from modifier [%s]\n", + token[NAME_ID]); + } + goto leave_now; + } + + ntokens = sscanf(ptr, "<%u %u %u %u %u>", &clipa.xul_clip, + &clipa.yul_clip, &clipa.xbr_clip, + &clipa.ybr_clip, &clipa.inversion); + if (ntokens != 5) { + dev_err(dev, "Invalid clip area modifier\n"); + goto leave_now; + } + + if (!cm->clipa) { + cm->clipa = kzalloc(sizeof(clipa), GFP_KERNEL); + if (!cm->clipa) { + count = -ENOMEM; + goto leave_now; + } + } + + memcpy(cm->clipa, &clipa, sizeof(clipa)); + + if (clipping_is_active) + rmi4_data->clipa = cm->clipa; + + pr_debug("added [clip] area to modifier [%s]\n", + token[NAME_ID]); + goto leave_now; + } + + if (!strncmp(token[ACTION_ID], "active", 6)) + patch_ptr = &cm->active; + else + patch_ptr = &cm->suspended; + + if (*patch_ptr) { + synaptics_dsx_free_patch(*patch_ptr); + *patch_ptr = NULL; + } + + if (clear_only) { + pr_debug("removed [%s] from modifier [%s]\n", + token[ACTION_ID], token[NAME_ID]); + goto apply_and_leave; + } + + patch = synaptics_dsx_init_patch(token[ACTION_ID]); + if (!patch) { + count = -ENOMEM; + goto leave_now; + } + + synaptics_dsx_parse_string(rmi4_data, ptr, patch, true); + *patch_ptr = patch; + pr_debug("added [%s] patch to modifier [%s]\n", + token[ACTION_ID], token[NAME_ID]); +apply_and_leave: + if (!rmi4_data->in_bootloader) + synaptics_dsx_enforce_modifiers(rmi4_data, cm); +leave_now: + kfree(internal); + return count; +} + +static ssize_t synaptics_rmi4_mod_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return modifiers_show(rmi4_data, buf, PAGE_SIZE, true, false); +} + +static ssize_t synaptics_rmi4_mod_sw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int id; + bool set_op; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct config_modifier *cm; + char mod_name[32]; + + if (*buf != '+' && *buf != '-') { + dev_err(dev, "invalid modifier syntax\n"); + return -EINVAL; + } + + set_op = *buf == '-' ? false : true; + if (sscanf(buf + 1, "[%s]", mod_name) != 1) { + dev_err(dev, "unable to read modifier name\n"); + return -EINVAL; + } + + id = modifier_name2id(mod_name); + if (id == -1) { + dev_err(dev, "invalid modifier name '%s'\n", mod_name); + return -EINVAL; + } + + cm = modifier_by_id(rmi4_data, id); + if (!cm) { + dev_err(dev, "modifier with id=%d does not exist\n", id); + return -EINVAL; + } + + if (cm->effective != set_op) { + cm->effective = set_op; + pr_debug("set [%s] %s\n", cm->name, + cm->effective ? "on" : "off"); + + if (cm->effective) { + if (cm->clipa) { + rmi4_data->clipping_on = true; + rmi4_data->clipa = cm->clipa; + pr_debug("enable clipping in [%s]\n", cm->name); + } + } else if (cm->clipa && rmi4_data->clipa == cm->clipa) { + rmi4_data->clipping_on = false; + rmi4_data->clipa = NULL; + pr_debug("disable clipping in [%s]\n", cm->name); + } + + if (!rmi4_data->in_bootloader) + synaptics_dsx_enforce_modifiers(rmi4_data, cm); + } + return count; +} + +static ssize_t synaptics_rmi4_mod_sw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + return modifiers_show(rmi4_data, buf, PAGE_SIZE, false, false); +} + +/* NOTE: query is stored in active */ +static ssize_t synaptics_rmi4_query_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_dsx_patch *patch; + struct config_modifier *cm = modifier_by_id(rmi4_data, SYNA_MOD_QUERY); + + if (!cm) { + dev_info(dev, "allocating modifier [%s]\n", + modifier_id2name(SYNA_MOD_QUERY)); + cm = kzalloc(sizeof(*cm), GFP_KERNEL); + if (!cm) { + dev_err(dev, "Cannot allocate query modifier\n"); + return -ENOMEM; + } + + down(&rmi4_data->modifiers.list_sema); + list_add_tail(&cm->link, &rmi4_data->modifiers.mod_head); + up(&rmi4_data->modifiers.list_sema); + rmi4_data->modifiers.mods_num++; + cm->name = ascii_names[SYNA_MOD_QUERY]; + cm->id = SYNA_MOD_QUERY; + } + + if (*buf == '\n') { + if (cm->active) { + synaptics_dsx_free_patch(cm->active); + cm->active = NULL; + pr_debug("discarded current query\n"); + } + goto leave_now; + } + + if (*buf != 'F') { + dev_err(dev, "invalid query syntax\n"); + return -EINVAL; + } + + patch = synaptics_dsx_init_patch(ascii_names[SYNA_MOD_QUERY]); + if (!patch) + return -ENOMEM; + + synaptics_dsx_parse_string(rmi4_data, buf, patch, false); + cm->active = patch; + pr_debug("new query added\n"); + +leave_now: + return count; +} + +static ssize_t synaptics_rmi4_query_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct config_modifier *cm = modifier_by_id(rmi4_data, SYNA_MOD_QUERY); + + return !cm ? 0 : modifiers_show(rmi4_data, buf, PAGE_SIZE, true, true); +} + + /** + * synaptics_rmi4_set_page() + * + * Called by synaptics_rmi4_i2c_read() and synaptics_rmi4_i2c_write(). + * + * This function writes to the page select register to switch to the + * assigned page. + */ +static int synaptics_rmi4_set_page(struct synaptics_rmi4_data *rmi4_data, + unsigned int address) +{ + int retval = 0; + bool enforce_page_select = false; + unsigned char retry; + unsigned char buf[PAGE_SELECT_LEN]; + unsigned char page; + struct i2c_client *i2c = rmi4_data->i2c_client; + + if (address & PAGE_SELECT_ENFORCE) { + enforce_page_select = true; + dev_dbg(&i2c->dev, "%s: Enforced page select\n", __func__); + address &= ~PAGE_SELECT_ENFORCE; + } + + page = ((address >> 8) & MASK_8BIT); + if (page != rmi4_data->current_page || enforce_page_select) { + buf[0] = MASK_8BIT; + buf[1] = page; + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + retval = i2c_master_send(i2c, buf, PAGE_SELECT_LEN); + if (retval != PAGE_SELECT_LEN) { + dev_err(&i2c->dev, + "%s: I2C retry %d\n", + __func__, retry + 1); + usleep_range(5000, 5000); + } else { + rmi4_data->current_page = page; + break; + } + } + } else + return PAGE_SELECT_LEN; + return (retval == PAGE_SELECT_LEN) ? retval : -EIO; +} + +int alloc_buffer(struct temp_buffer *tb, size_t count) +{ + if (tb->buf_size != 0) { + kfree(tb->buf); + tb->buf = NULL; + tb->buf_size = 0; + } + + if (count > MAX_READ_WRITE_SIZE) { + pr_err("%s: Max buffer size exceeded\n", __func__); + return 1; + } + + if (count < MIN_READ_WRITE_BUF_SIZE) + count = MIN_READ_WRITE_BUF_SIZE; + + tb->buf = kzalloc(count, GFP_KERNEL); + if (!tb->buf) + return 1; + + tb->buf_size = count; + return 0; +} +EXPORT_SYMBOL(alloc_buffer); + + /** + * synaptics_rmi4_i2c_read() + * + * Called by various functions in this driver, and also exported to + * other expansion Function modules such as rmi_dev. + * + * This function reads data of an arbitrary length from the sensor, + * starting from an assigned register address of the sensor, via I2C + * with a retry mechanism. + */ +static int synaptics_rmi4_i2c_read(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned short length) +{ + int retval; + unsigned char retry = SYN_I2C_RETRY_TIMES; + unsigned char buf; + struct i2c_msg msg[] = { + { + .addr = rmi4_data->i2c_client->addr, + .flags = 0, + .len = 1, + .buf = &buf, + }, + { + .addr = rmi4_data->i2c_client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + }, + }; + + buf = addr & MASK_8BIT; + + mutex_lock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + retval = synaptics_rmi4_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) + goto exit; + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(rmi4_data->i2c_client->adapter, msg, 2) == 2) { + retval = length; + break; + } + dev_err(&rmi4_data->i2c_client->dev, + "%s: I2C retry %d\n", + __func__, retry + 1); + usleep_range(5000, 5000); + } +exit: + mutex_unlock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + return retval; +} + + /** + * synaptics_rmi4_i2c_write() + * + * Called by various functions in this driver, and also exported to + * other expansion Function modules such as rmi_dev. + * + * This function writes data of an arbitrary length to the sensor, + * starting from an assigned register address of the sensor, via I2C with + * a retry mechanism. + */ +static int synaptics_rmi4_i2c_write(struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, unsigned char *data, unsigned short length) +{ + int retval; + unsigned char retry = SYN_I2C_RETRY_TIMES; + struct temp_buffer *tb = &rmi4_data->write_buf; + struct i2c_msg msg[1]; + + mutex_lock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + retval = synaptics_rmi4_set_page(rmi4_data, addr); + if (retval != PAGE_SELECT_LEN) + goto exit; + + if (tb->buf_size < (length+1) && alloc_buffer(tb, length+1) != 0) { + retval = -ENOMEM; + goto exit; + } + + msg[0].addr = rmi4_data->i2c_client->addr; + msg[0].flags = 0; + msg[0].len = length + 1; + msg[0].buf = tb->buf; + + *(tb->buf) = addr & MASK_8BIT; + memcpy(tb->buf+1, &data[0], length); + + for (retry = 0; retry < SYN_I2C_RETRY_TIMES; retry++) { + if (i2c_transfer(rmi4_data->i2c_client->adapter, msg, 1) == 1) { + retval = length; + break; + } + dev_err(&rmi4_data->i2c_client->dev, + "%s: I2C retry %d\n", + __func__, retry + 1); + msleep(20); + } +exit: + mutex_unlock(&(rmi4_data->rmi4_io_ctrl_mutex)); + + return retval; +} + +static int synaptics_rmi4_f12_wakeup_gesture( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + struct synaptics_rmi4_func_packet_regs *regs = + find_function(rmi4_data, SYNAPTICS_RMI4_F12 | DATA_TYPE); + struct f12_d4_s0_type *f12_d4_0; + struct synaptics_rmi4_packet_reg *reg_data_4; + struct synaptics_rmi4_subpkt *subpkt = + find_subpkt(regs, 4, 0, ®_data_4, (void **)&f12_d4_0); + + if (subpkt && subpkt->offset == -1) { + dev_err(&rmi4_data->i2c_client->dev, + "unable to clear wakeup gesture IRQ\n"); + return -EINVAL; + } + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.data_base + reg_data_4->offset, + &f12_d4_0->gesture, sizeof(*f12_d4_0)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "failure clearing wakeup gesture IRQ rc=%d\n", retval); + return retval; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "wakeup gesture status=0x%02x\n", f12_d4_0->gesture); + + if (f12_d4_0->gesture & DOUBLE_TAP_GESTURE) { + /* emulate power key press */ + input_report_key(rmi4_data->input_dev, KEY_POWER, 1); + input_report_key(rmi4_data->input_dev, KEY_POWER, 0); + input_sync(rmi4_data->input_dev); + + dev_dbg(&rmi4_data->i2c_client->dev, + "DBL_TAP wakeup gesture detected\n"); + } + + return 0; +} + +static void analyze_finger_data(struct synaptics_rmi4_data *rmi4_data, + struct f12_d1_s0_type *data) +{ + int id, finger_cnt = 0; + + id = finger_cnt; + for (id = 0; id < rmi4_data->num_of_fingers; id++, data++) + if (data->type_and_stylus) + finger_cnt++; + + down(&rmi4_data->rctrl.ctrl_sema); + if (finger_cnt >= rmi4_data->rctrl.expected && + finger_cnt > rmi4_data->rctrl.max_seen) + rmi4_data->rctrl.max_seen = finger_cnt; + up(&rmi4_data->rctrl.ctrl_sema); +} + + /** + * synaptics_rmi4_f12_abs_report() + * + * Called by synaptics_rmi4_report_touch() when valid Function $12 + * finger data has been detected. + * + * This function reads the Function $12 data registers, determines the + * status of each finger supported by the Function, processes any + * necessary coordinate manipulation, reports the finger data to + * the input subsystem, and returns the number of fingers detected. + */ +static int synaptics_rmi4_f12_abs_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char touch_count = 0; /* number of touch points */ + unsigned char finger; + unsigned char fingers_to_process; + unsigned char finger_status; + unsigned short data_addr; + unsigned short data_size; + int x; + int y; + int p; + int w; + int id; +#ifdef USE_TIME_SYNC_EVENTS + struct timespec hw_time = ktime_to_timespec(ktime_get()); +#endif + struct f12_d1_s0_type *finger_data; + struct f12_d1_s0_type *finger_data_buf; + struct synaptics_rmi4_packet_reg *reg_data_1 = + &rmi4_data->f12_data_registers_ptr->regs[F12_D1_IDX]; + static int print_xy = -1; + + if (unlikely(print_xy == -1)) { + print_xy = 0; + if (strncmp(bi_bootmode(), "mot-factory", strlen("mot-factory")) == 0) + print_xy = 1; + } + + if (atomic_read(&rmi4_data->panel_off_flag)) + return 0; + + fingers_to_process = rmi4_data->num_of_fingers; + + if (rmi4_data->touch_data_contiguous) + finger_data_buf = (struct f12_d1_s0_type *)(rmi4_data->touch_data + + rmi4_data->num_of_intr_regs); + else { + data_addr = fhandler->full_addr.data_base; + data_size = fingers_to_process * + fhandler->size_of_data_register_block; + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr + reg_data_1->offset, + reg_data_1->data, + data_size); + if (retval < 0) + return 0; + + finger_data_buf = (struct f12_d1_s0_type *)reg_data_1->data; + } + + /* count valid event */ + rmi4_data->rctrl.events_cnt++; + if (rmi4_data->rctrl.reporting_stopped) { + analyze_finger_data(rmi4_data, finger_data_buf); + return 0; + } +#ifdef USE_TIME_SYNC_EVENTS + input_event(rmi4_data->input_dev, EV_SYN, + SYN_TIME_SEC, hw_time.tv_sec); + input_event(rmi4_data->input_dev, EV_SYN, + SYN_TIME_NSEC, hw_time.tv_nsec); +#endif + finger_data = finger_data_buf; + for (finger = 0; finger < fingers_to_process; finger++, finger_data++) { + finger_status = finger_data->type_and_stylus; + if (finger_status) { + x = finger_data->x_lsb | (finger_data->x_msb << 8); + y = finger_data->y_lsb | (finger_data->y_msb << 8); + p = w = finger_data->z; + id = finger; + + if (rmi4_data->board.x_flip) + x = rmi4_data->sensor_max_x - x; + if (rmi4_data->board.y_flip) + y = rmi4_data->sensor_max_y - y; + + if (rmi4_data->clipping_on && rmi4_data->clipa) { + bool inside; + + inside = (x >= rmi4_data->clipa->xul_clip) && + (x <= rmi4_data->clipa->xbr_clip) && + (y >= rmi4_data->clipa->yul_clip) && + (y <= rmi4_data->clipa->ybr_clip); + + if (inside == rmi4_data->clipa->inversion) { + /* Touch might still be active, but we're */ + /* sending release anyway to avoid touch */ + /* stuck at the last reported position. */ + /* Driver will suppress reporting to UI */ + /* touch events from within clipping area */ + INPUT_REPORT_MT_SLOT(rmi4_data, finger); + INPUT_REPORT_MT_SLOT_STATE(rmi4_data, + MT_TOOL_FINGER, 0); + + dev_dbg(&rmi4_data->i2c_client->dev, + "finger id-%d (%d,%d) within clipping area\n", + finger, x, y); + + continue; + } + } +#ifdef TYPE_B_PROTOCOL + INPUT_REPORT_MT_SLOT(rmi4_data, finger); + INPUT_REPORT_MT_SLOT_STATE(rmi4_data, + MT_TOOL_FINGER, finger_status); +#endif + if (print_xy) { + dev_info(&rmi4_data->i2c_client->dev, + "%s: Finger %d:\n" + "x = %d\n" + "y = %d\n" + "p = %d\n" + "w = %d\n", + __func__, finger, + x, y, p, w); + } else { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Finger %d:\n" + "x = %d\n" + "y = %d\n" + "p = %d\n" + "w = %d\n", + __func__, finger, + x, y, p, w); + } + + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_Y, y); + input_report_abs(rmi4_data->input_dev, + ABS_MT_PRESSURE, p); + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, w); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + touch_count++; +#ifdef TYPE_B_PROTOCOL + } else { + INPUT_REPORT_MT_SLOT(rmi4_data, finger); + /* Touch no longer active, close out slot */ + INPUT_REPORT_MT_SLOT_STATE(rmi4_data, + MT_TOOL_FINGER, 0); +#endif + } + } + + if (!touch_count) { +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + } + + input_mt_report_pointer_emulation(rmi4_data->input_dev, false); + input_sync(rmi4_data->input_dev); + + return touch_count; +} + + /** + * synaptics_rmi4_f11_abs_report() + * + * Called by synaptics_rmi4_report_touch() when valid Function $11 + * finger data has been detected. + * + * This function reads the Function $11 data registers, determines the + * status of each finger supported by the Function, processes any + * necessary coordinate manipulation, reports the finger data to + * the input subsystem, and returns the number of fingers detected. + */ +static int synaptics_rmi4_f11_abs_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char touch_count = 0; /* number of touch points */ + unsigned char reg_index; + unsigned char finger; + unsigned char fingers_supported; + unsigned char num_of_finger_status_regs; + unsigned char finger_shift; + unsigned char finger_status; + unsigned char data_reg_blk_size; + unsigned char finger_status_reg[3]; + unsigned char data[F11_STD_DATA_LEN]; + unsigned short data_addr; + unsigned short data_offset; + int x; + int y; + int wx; + int wy; + int z; +#ifdef USE_TIME_SYNC_EVENTS + struct timespec hw_time = ktime_to_timespec(ktime_get()); +#endif + /* + * The number of finger status registers is determined by the + * maximum number of fingers supported - 2 bits per finger. So + * the number of finger status registers to read is: + * register_count = ceil(max_num_of_fingers / 4) + */ + fingers_supported = fhandler->num_of_data_points; + num_of_finger_status_regs = (fingers_supported + 3) / 4; + data_addr = fhandler->full_addr.data_base; + data_reg_blk_size = fhandler->size_of_data_register_block; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr, + finger_status_reg, + num_of_finger_status_regs); + if (retval < 0) + return 0; + /* count valid event */ + rmi4_data->rctrl.events_cnt++; + if (rmi4_data->rctrl.reporting_stopped) + return 0; + + if (atomic_read(&rmi4_data->panel_off_flag)) + return 0; + +#ifdef USE_TIME_SYNC_EVENTS + input_event(rmi4_data->input_dev, EV_SYN, + SYN_TIME_SEC, hw_time.tv_sec); + input_event(rmi4_data->input_dev, EV_SYN, + SYN_TIME_NSEC, hw_time.tv_nsec); +#endif + for (finger = 0; finger < fingers_supported; finger++) { + reg_index = finger / 4; + finger_shift = (finger % 4) * 2; + finger_status = (finger_status_reg[reg_index] >> finger_shift) + & MASK_2BIT; + /* + * Each 2-bit finger status field represents the following: + * 00 = finger not present + * 01 = finger present and data accurate + * 10 = finger present but data may be inaccurate + * 11 = reserved + */ + if (finger_status) { + data_offset = data_addr + + num_of_finger_status_regs + + (finger * data_reg_blk_size); + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_offset, + data, + data_reg_blk_size); + if (retval < 0) + return 0; + + x = (data[0] << 4) | (data[2] & MASK_4BIT); + y = (data[1] << 4) | ((data[2] >> 4) & MASK_4BIT); + wx = (data[3] & MASK_4BIT); + wy = (data[3] >> 4) & MASK_4BIT; + z = data[4]; + + if (rmi4_data->board.x_flip) + x = rmi4_data->sensor_max_x - x; + if (rmi4_data->board.y_flip) + y = rmi4_data->sensor_max_y - y; + + if (rmi4_data->clipping_on && rmi4_data->clipa) { + bool inside; + + inside = (x >= rmi4_data->clipa->xul_clip) && + (x <= rmi4_data->clipa->xbr_clip) && + (y >= rmi4_data->clipa->yul_clip) && + (y <= rmi4_data->clipa->ybr_clip); + + if (!inside) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%d,%d ouside clipping area\n", + x, y); + continue; + } + } +#ifdef TYPE_B_PROTOCOL + INPUT_REPORT_MT_SLOT(rmi4_data, finger); + INPUT_REPORT_MT_SLOT_STATE(rmi4_data, + MT_TOOL_FINGER, finger_status); +#endif + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Finger %d:\n" + "x = %d\n" + "y = %d\n" + "wx = %d\n" + "wy = %d\n" + "z = %d\n", + __func__, finger, + x, y, wx, wy, z); + + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(rmi4_data->input_dev, + ABS_MT_POSITION_Y, y); + input_report_abs(rmi4_data->input_dev, + ABS_MT_PRESSURE, z); + +#ifdef REPORT_2D_W + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, max(wx, wy)); + input_report_abs(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, min(wx, wy)); +#endif +#ifndef TYPE_B_PROTOCOL + input_mt_sync(rmi4_data->input_dev); +#endif + touch_count++; +#ifdef TYPE_B_PROTOCOL + } else { + INPUT_REPORT_MT_SLOT(rmi4_data, finger); + /* Touch no longer active, close out slot */ + INPUT_REPORT_MT_SLOT_STATE(rmi4_data, + MT_TOOL_FINGER, 0); +#endif + } + } + +#ifndef TYPE_B_PROTOCOL + if (!touch_count) + input_mt_sync(rmi4_data->input_dev); +#endif + + input_mt_report_pointer_emulation(rmi4_data->input_dev, false); + input_sync(rmi4_data->input_dev); + + return touch_count; +} + +static void synaptics_rmi4_f1a_report(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned char button; + unsigned char index; + unsigned char shift; + unsigned char status; + unsigned char *data; + unsigned short data_addr = fhandler->full_addr.data_base; + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + static bool current_status[MAX_NUMBER_OF_BUTTONS]; +#ifdef NO_0D_WHILE_2D + static bool before_2d_status[MAX_NUMBER_OF_BUTTONS]; + static bool while_2d_status[MAX_NUMBER_OF_BUTTONS]; +#endif + + if (rmi4_data->tsb_buff_clean_flag) { + memset(current_status, 0, sizeof(current_status)); +#ifdef NO_0D_WHILE_2D + memset(before_2d_status, 0, sizeof(before_2d_status)); + memset(while_2d_status, 0, sizeof(while_2d_status)); +#endif + rmi4_data->tsb_buff_clean_flag = 0; + } + + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr, + f1a->button_data_buffer, + f1a->button_bitmask_size); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read button data registers\n", + __func__); + return; + } + + if (atomic_read(&rmi4_data->panel_off_flag)) + return; + + data = f1a->button_data_buffer; + + for (button = 0; button < f1a->valid_button_count; button++) { + index = button / 8; + shift = button % 8; + status = ((data[index] >> shift) & MASK_1BIT); + + if (current_status[button] == status) + continue; + else + current_status[button] = status; + + ud_set_id(&rmi4_data->button_ud_stats, button); + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Button %d (code %d) ->%d\n", + __func__, button, + f1a->button_map[button], + status); +#ifdef NO_0D_WHILE_2D + if (rmi4_data->fingers_on_2d == false) { + if (status == 1) { + before_2d_status[button] = 1; + } else { + if (while_2d_status[button] == 1) { + while_2d_status[button] = 0; + continue; + } else { + before_2d_status[button] = 0; + } + } + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + ud_log_status(&rmi4_data->button_ud_stats, status == 1); + } else { + if (before_2d_status[button] == 1) { + before_2d_status[button] = 0; + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + ud_log_status(&rmi4_data->button_ud_stats, status == 1); + } else { + if (status == 1) + while_2d_status[button] = 1; + else + while_2d_status[button] = 0; + } + } +#else + input_report_key(rmi4_data->input_dev, + f1a->button_map[button], + status); + ud_log_status(&rmi4_data->button_ud_stats, status == 1); +#endif + } + + input_sync(rmi4_data->input_dev); + + return; +} + +static void synaptics_rmi4_f01_handler(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + unsigned short data_addr = fhandler->full_addr.data_base; + struct synaptics_rmi4_f01_device_status status; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + data_addr, + status.data, + sizeof(status.data)); + if (retval < 0) + return; + + switch (status.status_code) { + case 0x00: + dev_info(&rmi4_data->i2c_client->dev, + "%s: No error.\n", __func__); + break; + + case 0x01: + dev_info(&rmi4_data->i2c_client->dev, + "%s: Touch IC reset complete.\n", __func__); + break; + + case 0x02: + dev_info(&rmi4_data->i2c_client->dev, + "%s: Touch IC configuration error--%s.\n", + __func__, "check platform settings"); + break; + + case 0x03: + dev_info(&rmi4_data->i2c_client->dev, + "%s: Touch IC device failure.\n", __func__); + break; + + case 0x04: + dev_info(&rmi4_data->i2c_client->dev, + "%s: Configuration CRC failure.\n", __func__); + break; + + case 0x05: + dev_info(&rmi4_data->i2c_client->dev, + "%s: Firmware CRC failure.\n", __func__); + break; + + case 0x06: + dev_info(&rmi4_data->i2c_client->dev, + "%s: CRC in progress.\n", __func__); + break; + + default: + dev_info(&rmi4_data->i2c_client->dev, + "%s: Unknown error 0x%02X received.\n", + __func__, status.status_code); + break; + } +} + +#define IM_BIT 0 +#define NMS_BIT 1 +#define CID_IM_BIT 2 +#define FS_IM_BIT 3 +#define FREQ_BIT 4 +#define GUARD_BIT 5 + +static void synaptics_rmi4_f51_handler(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_packet_reg *reg; + struct synaptics_rmi4_subpkt *subpkt; + struct f54_d6_s0_type *f54_d6_0; + struct f54_d10_s0_type *f54_d10_0; + struct f54_d14_s0_type *f54_d14_0; + struct f54_d16_s0_type *f54_d16_0; + struct f54_d17_s0_type *f54_d17_0; + struct f51_d0_s0_type *f51_d0_0; + unsigned char presence_mask = 0; + //ktime_t log = ktime_get(); + int ii, error; + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F51 | DATA_TYPE); + if (!regs) + return; + + reg = find_packet_reg(regs, 0); + if (!reg) + return; + + error = synaptics_rmi4_read_packet_reg(rmi4_data, regs, reg->r_number); + if (error < 0) { + pr_err("F51 read failed\n"); + return; + } + + subpkt = find_subpkt(regs, 0, 0, ®, (void **)&f51_d0_0); + if (!subpkt || !subpkt->present) + return; + + presence_mask |= (1 << GUARD_BIT); + pr_debug("F%x@D%d: int status [0]=0x%x, [1]=0x%x\n", + regs->f_number & 0xff, reg->r_number, + f51_d0_0->data[0], f51_d0_0->data[1]); +#if 0 + tk_debug("F%x@D%d: int status [%s][%s][%s] md = %d\n", + regs->f_number & 0xff, reg->r_number, + f51_d0_0->noise_state ? "N" : "-", + f51_d0_0->gear_change ? "H" : "-", + f51_d0_0->guard_state ? "M" : "-", + f51_d0_0->md_present); +#endif + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F54 | DATA_TYPE); + if (!regs) + return; + + /* not all features might be enabled, thus determine presence */ + for (ii = 0; ii < regs->nr_regs; ii++) + presence_mask |= (1 << ii); + + error = synaptics_rmi4_read_packet_regs(rmi4_data, regs); + if (error < 0) + return; + + find_subpkt(regs, 6, 0, NULL, (void **)&f54_d6_0); + find_subpkt(regs, 10, 0, NULL, (void **)&f54_d10_0); + find_subpkt(regs, 14, 0, NULL, (void **)&f54_d14_0); + find_subpkt(regs, 16, 0, NULL, (void **)&f54_d16_0); + find_subpkt(regs, 17, 0, NULL, (void **)&f54_d17_0); +#if 0 + if ((presence_mask & (1 << FREQ_BIT)) && f54_d17_0) { + statistics_log_time(gStat.dur, (int)f54_d17_0->freq, log); + statistics_log_time(gStat.hop, (int)f54_d17_0->freq, log); + } + + if ((presence_mask & (1 << NMS_BIT)) && f54_d10_0) + statistics_log_time(gStat.nms, (int)f54_d10_0->noise_state, log); + + if ((presence_mask & (1 << GUARD_BIT)) && f51_d0_0) + statistics_log_time(gStat.mpd, (int)f51_d0_0->md_present, log); + + tk_debug("F%x: %d:%d %5d:%5d:%5d\n", + regs->f_number & 0xff, + (presence_mask & (1 << FREQ_BIT)) && f54_d17_0 ? + (int)f54_d17_0->freq : -1, + (presence_mask & (1 << NMS_BIT)) && f54_d10_0 ? + (int)f54_d10_0->noise_state : -1, + (presence_mask & (1 << CID_IM_BIT)) && f54_d14_0 ? + (int)f54_d14_0->cid_im_lsb | + (f54_d14_0->cid_im_msb << 4) : -1, + (presence_mask & (1 << IM_BIT)) && f54_d6_0 ? + (int)f54_d6_0->interference_metric_lsb | + (f54_d6_0->interference_metric_msb << 4) : -1, + (presence_mask & (1 << FS_IM_BIT)) && f54_d16_0 ? + (int)f54_d16_0->freq_scan_im_lsb | + (f54_d16_0->freq_scan_im_msb << 4) : -1); +#endif +} + + /** + * synaptics_rmi4_report_touch() + * + * Called by synaptics_rmi4_sensor_report(). + * + * This function calls the appropriate finger data reporting function + * based on the function handler it receives and returns the number of + * fingers detected. + */ +static void synaptics_rmi4_report_touch(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + unsigned char *touch_count) +{ + unsigned char touch_count_2d; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Function %02x reporting\n", + __func__, fhandler->fn_number); + + switch (fhandler->fn_number) { + case SYNAPTICS_RMI4_F01: + synaptics_rmi4_f01_handler(rmi4_data, fhandler); + break; + + case SYNAPTICS_RMI4_F51: + synaptics_rmi4_f51_handler(rmi4_data, fhandler); + break; + + case SYNAPTICS_RMI4_F11: + touch_count_2d = synaptics_rmi4_f11_abs_report(rmi4_data, + fhandler); + + *touch_count += touch_count_2d; + + if (touch_count_2d) + rmi4_data->fingers_on_2d = true; + else + rmi4_data->fingers_on_2d = false; + break; + + case SYNAPTICS_RMI4_F12: + if (rmi4_data->suspend_is_wakeable) { + synaptics_rmi4_f12_wakeup_gesture(rmi4_data, fhandler); + break; + } + + touch_count_2d = synaptics_rmi4_f12_abs_report(rmi4_data, + fhandler); + + + *touch_count += touch_count_2d; + + if (touch_count_2d) + rmi4_data->fingers_on_2d = true; + else + rmi4_data->fingers_on_2d = false; + break; + + case SYNAPTICS_RMI4_F1A: + synaptics_rmi4_f1a_report(rmi4_data, fhandler); + break; + + default: + break; + } + + return; +} + + /** + * synaptics_rmi4_sensor_report() + * + * Called by synaptics_rmi4_irq(). + * + * This function determines the interrupt source(s) from the sensor + * and calls synaptics_rmi4_report_touch() with the appropriate + * function handler for each function with valid data inputs. + */ +static int synaptics_rmi4_sensor_report(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char touch_count = 0; + unsigned int intr_status_mask; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_exp_fn *exp_fhandler; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + /* + * Get interrupt status information from F01 Data1 register to + * determine the source(s) that are flagging the interrupt. + */ + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + rmi4_data->touch_data, + rmi4_data->touch_data_size); + if (retval < 0) + return retval; + + /* + * Traverse the function handler list and service the source(s) + * of the interrupt accordingly. + */ + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + if (fhandler->intr_mask & + rmi4_data->touch_data[fhandler->intr_reg_num]) { + synaptics_rmi4_report_touch(rmi4_data, + fhandler, &touch_count); + } + } + } + + batohui(&intr_status_mask, rmi4_data->touch_data, + rmi4_data->num_of_intr_regs); + /* + * Go through external handlers list only when interrupt + * is not handled by currently active internal functions. + */ + if (intr_status_mask & (~rmi4_data->active_fn_intr_mask)) { + mutex_lock(&rmi4_data->exp_fn_ctrl.list_mutex); + if (!list_empty(&rmi4_data->exp_fn_ctrl.fn_list)) { + list_for_each_entry(exp_fhandler, + &rmi4_data->exp_fn_ctrl.fn_list, link) { + if (exp_fhandler->inserted && + (exp_fhandler->func_attn != NULL)) + exp_fhandler->func_attn( + rmi4_data, + rmi4_data->touch_data[0]); + } + } + mutex_unlock(&rmi4_data->exp_fn_ctrl.list_mutex); + } + + return touch_count; +} + + /** + * synaptics_rmi4_irq() + * + * Called by the kernel when an interrupt occurs (when the sensor + * asserts the attention irq). + * + * This function is the ISR thread and handles the acquisition + * and the reporting of finger data when the presence of fingers + * is detected. + */ +static irqreturn_t synaptics_rmi4_irq(int irq, void *data) +{ + struct synaptics_rmi4_data *rmi4_data = data; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: IRQ=%d; rmi4_data ptr = %p\n", __func__, irq, rmi4_data); + +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + if (rmi4_data->test_irq_delay_ms) { + int i; + for (i = 0; i < rmi4_data->test_irq_delay_ms; ++i) + udelay(1000); + } +#endif + synaptics_rmi4_sensor_report(rmi4_data); + + return IRQ_HANDLED; +} + + /** + * synaptics_rmi4_irq_enable() + * + * Called by synaptics_rmi4_probe() and the power management functions + * in this driver and also exported to other expansion Function modules + * such as rmi_dev. + * + * This function handles the enabling and disabling of the attention + * irq including the setting up of the ISR thread. + */ +int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data, + bool enable) +{ + int retval = 0; + unsigned char intr_status; + struct synaptics_dsx_platform_data + *platform_data = &rmi4_data->board; + + if (enable) { + if (rmi4_data->irq_enabled) + return retval; + + /* Clear interrupts first */ + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + &intr_status, + rmi4_data->num_of_intr_regs); + if (retval < 0) + return retval; + + retval = request_threaded_irq(rmi4_data->irq, NULL, + synaptics_rmi4_irq, platform_data->irq_flags, + rmi4_data->irq_name, rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create irq thread\n", + __func__); + return retval; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: IRQ=%d: Started irq thread\n", __func__, rmi4_data->irq); + + rmi4_data->irq_enabled = true; + } else { + if (rmi4_data->irq_enabled) { + disable_irq(rmi4_data->irq); + free_irq(rmi4_data->irq, rmi4_data); + rmi4_data->irq_enabled = false; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Stopped irq thread\n", __func__); + } + } + + return retval; +} + + /** + * synaptics_rmi4_f11_init() + * + * Called by synaptics_rmi4_query_device(). + * + * This funtion parses information from the Function 11 registers + * and determines the number of fingers supported, x and y data ranges, + * offset to the associated interrupt status register, interrupt bit + * mask, and gathers finger data acquisition capabilities from the query + * registers. + */ +static int synaptics_rmi4_f11_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char ii; + unsigned char intr_offset; + unsigned char abs_data_size; + unsigned char abs_data_blk_size; + unsigned char query[F11_STD_QUERY_LEN]; + unsigned char control[F11_STD_CTRL_LEN]; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = (fd->intr_src_count & MASK_3BIT); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.query_base, + query, + sizeof(query)); + if (retval < 0) + return retval; + + /* Maximum number of fingers supported */ + if ((query[1] & MASK_3BIT) <= 4) + fhandler->num_of_data_points = (query[1] & MASK_3BIT) + 1; + else if ((query[1] & MASK_3BIT) == 5) + fhandler->num_of_data_points = 10; + + rmi4_data->num_of_fingers = fhandler->num_of_data_points; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.ctrl_base, + control, + sizeof(control)); + if (retval < 0) + return retval; + + /* Maximum x and y */ + rmi4_data->sensor_max_x = ((control[6] & MASK_8BIT) << 0) | + ((control[7] & MASK_4BIT) << 8); + rmi4_data->sensor_max_y = ((control[8] & MASK_8BIT) << 0) | + ((control[9] & MASK_4BIT) << 8); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Function %02x max x = %d max y = %d\n", + __func__, fhandler->fn_number, + rmi4_data->sensor_max_x, + rmi4_data->sensor_max_y); + + /* Reporting mode */ + if (!(control[0] & MASK_3BIT)) { + pr_warn("Reporting mode: continuous\n"); + } else { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: thresholds: x=0x%x, y=0x%x\n", + __func__, control[2], control[3]); + } + + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num >= MAX_INTR_REGISTERS) { + fhandler->intr_reg_num = 0; + fhandler->num_of_data_sources = 0; + fhandler->intr_mask = 0; + return -EINVAL; + } + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + + abs_data_size = query[5] & MASK_2BIT; + abs_data_blk_size = 3 + (2 * (abs_data_size == 0 ? 1 : 0)); + fhandler->size_of_data_register_block = abs_data_blk_size; + +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit); +#endif + set_bit(EV_ABS, rmi4_data->input_dev->evbit); + + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_X, 0, + rmi4_data->sensor_max_x, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_Y, 0, + rmi4_data->sensor_max_y, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_PRESSURE, 0, + 255, 0, 0); +#ifdef REPORT_2D_W + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, 0, + 15, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MINOR, 0, + 15, 0, 0); +#endif +#ifdef TYPE_B_PROTOCOL + input_mt_init_slots(rmi4_data->input_dev, + rmi4_data->num_of_fingers, 0); +#else + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TRACKING_ID, 0, + rmi4_data->num_of_fingers - 1, 0, 0); + input_set_events_per_packet(rmi4_data->input_dev, 64); +#endif + return retval; +} + +static int synaptics_rmi4_f1a_alloc_mem(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + int retval; + struct synaptics_rmi4_f1a_handle *f1a; + + f1a = kzalloc(sizeof(*f1a), GFP_KERNEL); + if (!f1a) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for function handle\n", + __func__); + return -ENOMEM; + } + + fhandler->data = (void *)f1a; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.query_base, + f1a->button_query.data, + sizeof(f1a->button_query.data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read query registers\n", + __func__); + return retval; + } + + f1a->button_count = f1a->button_query.max_button_count + 1; + f1a->button_bitmask_size = (f1a->button_count + 7) / 8; + + f1a->button_data_buffer = kcalloc(f1a->button_bitmask_size, + sizeof(*(f1a->button_data_buffer)), GFP_KERNEL); + if (!f1a->button_data_buffer) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for data buffer\n", + __func__); + return -ENOMEM; + } + + f1a->button_map = kcalloc(f1a->button_count, + sizeof(*(f1a->button_map)), GFP_KERNEL); + if (!f1a->button_map) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for button map\n", + __func__); + return -ENOMEM; + } + + return 0; +} + +static int synaptics_rmi4_f51_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char ii; + unsigned char intr_offset; + struct synaptics_rmi4_func_packet_regs *regs; + unsigned char *data; + struct synaptics_rmi4_packet_reg *reg; + struct synaptics_rmi4_subpkt *subpkt; + struct { + unsigned char num_of_query_regs; + unsigned char num_of_data_regs; + unsigned char num_of_ctrl_regs; + } f51_query = {0}; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + fhandler->full_addr.query_base, + (unsigned char *)&f51_query, + sizeof(f51_query)); + pr_debug("F%x: # of query=%d, data=%d, ctrl=%d\n", + fd->fn_number, + f51_query.num_of_query_regs, + f51_query.num_of_data_regs, + f51_query.num_of_ctrl_regs); + /* check for error and mandatory registers presense */ + if (retval < 0 || f51_query.num_of_query_regs < 3) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Error querying F51\n", + __func__); + return -EIO; + } + /* check for mandatory registers presense */ + if (f51_query.num_of_data_regs < 2 || + f51_query.num_of_ctrl_regs < 10) { + return -ENODEV; + } + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = (fd->intr_src_count & MASK_3BIT); + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num >= MAX_INTR_REGISTERS) { + fhandler->intr_reg_num = 0; + fhandler->num_of_data_sources = 0; + fhandler->intr_mask = 0; + return -EINVAL; + } + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + /* FIXME: interrupt bit 5 (belongs F54) is used for some reason */ + fhandler->intr_mask = 0x40; + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F51 | DATA_TYPE); + if (!regs) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: F%x data not found\n", + __func__, fd->fn_number); + return 0; + } + + regs->base_addr = fhandler->full_addr.data_base; + reg = ®s->regs[0]; + subpkt = ®->subpkt[0]; + pr_debug("F%x@D%d size %u\n", + fd->fn_number, reg->r_number, subpkt->size); + data = kzalloc(subpkt->size, GFP_KERNEL); + if (data) { + reg->offset = reg->r_number; + reg->size = subpkt->size; + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + } + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F51); + if (!regs) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: F%x ctrl not found\n", + __func__, fd->fn_number); + return 0; + } + + regs->base_addr = fhandler->full_addr.ctrl_base; + for (ii = 0; ii < regs->nr_regs; ii++) { + reg = ®s->regs[ii]; + subpkt = ®->subpkt[0]; + pr_debug("F%x@C%d size %u\n", + fd->fn_number, reg->r_number, subpkt->size); + data = kzalloc(subpkt->size, GFP_KERNEL); + if (data) { + reg->offset = reg->r_number; + reg->size = subpkt->size; + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + } + } + + retval = synaptics_rmi4_read_packet_regs(rmi4_data, regs); + if (retval < 0) + dev_err(&rmi4_data->i2c_client->dev, "Error reading F51\n"); + + return 0; +} + + /** + * synaptics_rmi4_f12_init() + * + * Called by synaptics_rmi4_query_device(). + * + * This funtion parses information from the Function 12 registers + * and determines the number of fingers supported, x and y data ranges, + * offset to the associated interrupt status register, interrupt bit + * mask, and gathers finger data acquisition capabilities from the query + * registers. + */ +static int synaptics_rmi4_f12_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char ii; + unsigned char intr_offset; + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_subpkt *subpkt; + struct f12_d15_s0_type *f12_d15_0; + struct f12_c8_s0_type *f12_c8_0; + struct f12_c23_s1_type *f12_c23_1; + struct f12_c28_s0_type *f12_c28_0; + + regs = rmi4_data->f12_data_registers_ptr; + retval = synaptics_rmi4_scan_f12_reg_info(rmi4_data, + fhandler->full_addr.query_base + regs->query_offset, + fhandler->full_addr.data_base, regs); + if (retval < 0) + return retval; + + subpkt = find_subpkt(regs, 15, 0, NULL, (void **)&f12_d15_0); + if (subpkt && subpkt->present) + pr_debug("F12 has data register 15\n"); + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F12); + retval = synaptics_rmi4_scan_f12_reg_info(rmi4_data, + fhandler->full_addr.query_base + regs->query_offset, + fhandler->full_addr.ctrl_base, regs); + if (retval < 0) + return retval; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = (fd->intr_src_count & MASK_3BIT); + + retval = synaptics_rmi4_read_packet_regs(rmi4_data, regs); + if (retval < 0) + return retval; + + /* Maximum number of fingers supported */ + subpkt = find_subpkt(regs, 23, 1, NULL, (void**)&f12_c23_1); + if (subpkt && subpkt->present) { + fhandler->num_of_data_points = + f12_c23_1->max_num_reported_objects; + rmi4_data->num_of_fingers = f12_c23_1->max_num_reported_objects; + } else + return -ENOENT; + + subpkt = find_subpkt(regs, 8, 0, NULL, (void **)&f12_c8_0); + if (subpkt && subpkt->present) { + rmi4_data->sensor_max_x = + (f12_c8_0->max_x_msb << 8) | f12_c8_0->max_x_lsb; + rmi4_data->sensor_max_y = + (f12_c8_0->max_y_msb << 8) | f12_c8_0->max_y_lsb; + } else + return -ENOENT; + + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num >= MAX_INTR_REGISTERS) { + fhandler->intr_reg_num = 0; + fhandler->num_of_data_sources = 0; + fhandler->intr_mask = 0; + return -EINVAL; + } + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + + /* Data size per touch */ + subpkt = find_subpkt(regs, 28, 0, NULL, (void **)&f12_c28_0); + if (subpkt && subpkt->present) { + for (ii = 0; ii < 8; ii++) + if (f12_c28_0->reported_bytes_per_object & (1 << ii)) + fhandler->size_of_data_register_block++; + } else + return -ENOENT; + +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, rmi4_data->input_dev->propbit); +#endif + set_bit(EV_ABS, rmi4_data->input_dev->evbit); + + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_X, 0, + rmi4_data->sensor_max_x, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_POSITION_Y, 0, + rmi4_data->sensor_max_y, 0, 0); + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_PRESSURE, 0, + 255, 0, 0); +#ifdef REPORT_2D_W + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TOUCH_MAJOR, 0, + 255, 0, 0); +#endif +#ifdef TYPE_B_PROTOCOL + input_mt_init_slots(rmi4_data->input_dev, + rmi4_data->num_of_fingers, 0); +#else + input_set_abs_params(rmi4_data->input_dev, + ABS_MT_TRACKING_ID, 0, + rmi4_data->num_of_fingers - 1, 0, 0); + input_set_events_per_packet(rmi4_data->input_dev, 64); +#endif + return retval; +} + +static int synaptics_rmi4_cap_button_map( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler) +{ + unsigned char ii; + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + struct synaptics_dsx_platform_data *pdata = &rmi4_data->board; + + if (!pdata->cap_button_map) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: cap_button_map is \ + NULL in board file\n", + __func__); + return -ENODEV; + } else if (!pdata->cap_button_map->map) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Button map is missing in board file\n", + __func__); + return -ENODEV; + } else { + if (pdata->cap_button_map->nbuttons != + f1a->button_count) { + f1a->valid_button_count = min(f1a->button_count, + pdata->cap_button_map->nbuttons); + } else { + f1a->valid_button_count = f1a->button_count; + } + + set_bit(EV_KEY, rmi4_data->input_dev->evbit); + + for (ii = 0; ii < f1a->valid_button_count; ii++) { + f1a->button_map[ii] = + pdata->cap_button_map->map[ii]; + set_bit(f1a->button_map[ii], + rmi4_data->input_dev->keybit); + input_set_capability(rmi4_data->input_dev, + EV_KEY, f1a->button_map[ii]); + } + } + + return 0; +} + +static void synaptics_rmi4_f1a_kfree(struct synaptics_rmi4_fn *fhandler) +{ + struct synaptics_rmi4_f1a_handle *f1a = fhandler->data; + + if (f1a) { + kfree(f1a->button_data_buffer); + kfree(f1a->button_map); + kfree(f1a); + fhandler->data = NULL; + } + + return; +} + +static int synaptics_rmi4_f1a_init(struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_fn *fhandler, + struct synaptics_rmi4_fn_desc *fd, + unsigned int intr_count) +{ + int retval; + unsigned char ii; + unsigned short intr_offset; + + fhandler->fn_number = fd->fn_number; + fhandler->num_of_data_sources = (fd->intr_src_count & MASK_3BIT); + + fhandler->intr_reg_num = (intr_count + 7) / 8; + if (fhandler->intr_reg_num >= MAX_INTR_REGISTERS) { + fhandler->intr_reg_num = 0; + fhandler->num_of_data_sources = 0; + fhandler->intr_mask = 0; + retval = -EINVAL; + goto error_exit; + } + if (fhandler->intr_reg_num != 0) + fhandler->intr_reg_num -= 1; + + /* Set an enable bit for each data source */ + intr_offset = intr_count % 8; + fhandler->intr_mask = 0; + for (ii = intr_offset; + ii < ((fd->intr_src_count & MASK_3BIT) + + intr_offset); + ii++) + fhandler->intr_mask |= 1 << ii; + + retval = synaptics_rmi4_f1a_alloc_mem(rmi4_data, fhandler); + if (retval < 0) + goto error_exit; + + retval = synaptics_rmi4_cap_button_map(rmi4_data, fhandler); + if (retval < 0) + goto error_exit; + + rmi4_data->button_0d_enabled = 1; + + return 0; + +error_exit: + synaptics_rmi4_f1a_kfree(fhandler); + + return retval; +} + +static int synaptics_rmi4_alloc_fh(struct synaptics_rmi4_fn **fhandler, + struct synaptics_rmi4_fn_desc *rmi_fd, int page_number) +{ + *fhandler = kzalloc(sizeof(**fhandler), GFP_KERNEL); + if (!(*fhandler)) + return -ENOMEM; + + (*fhandler)->full_addr.data_base = + (rmi_fd->data_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.ctrl_base = + (rmi_fd->ctrl_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.cmd_base = + (rmi_fd->cmd_base_addr | + (page_number << 8)); + (*fhandler)->full_addr.query_base = + (rmi_fd->query_base_addr | + (page_number << 8)); + + return 0; +} + +static unsigned char *simple_init_packet_reg( + struct synaptics_rmi4_packet_reg *reg, short r_offset) +{ + unsigned char *data_ptr = NULL; + if (reg->subpkt[0].data && reg->subpkt[0].size) { + reg->subpkt[0].present = true; + reg->subpkt[0].offset = 0; + memset(reg->subpkt[0].data, 0, reg->subpkt[0].size); + + reg->offset = r_offset; + kfree(reg->data); + data_ptr = kzalloc(reg->size, GFP_KERNEL); + } + return data_ptr; +} + +static void simple_deinit_packet_reg(struct synaptics_rmi4_packet_reg *reg) +{ + reg->expected = false; + reg->offset = -1; + reg->size = 0; + reg->subpkt[0].expected = false; + reg->subpkt[0].offset = -1; +} + +#define F01_RMI_Ctrl0 0 +#define F01_RMI_Ctrl2 2 +#define F01_RMI_Ctrl3 3 +#define F01_RMI_Ctrl5 5 +#define F01_RMI_Ctrl9 9 + +static void synaptics_rmi4_scan_f01_reg_info( + struct synaptics_rmi4_data *rmi4_data) +{ + int r, s, retval; + unsigned short query42_offset = F01_QUERY21_OFFSET; + unsigned short query43_size = 0; + unsigned short current_ctrl_offset = rmi4_data->num_of_intr_regs; + bool has_recalibration = false; + bool can_access_ctrl9 = false; + u8 query43[4]; + struct synaptics_rmi4_f01_query42 query42; + struct synaptics_rmi4_f01_query1 query1; + struct synaptics_rmi4_func_packet_regs *regs; + struct synaptics_rmi4_packet_reg *reg; + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F01); + if (unlikely(regs == NULL)) { + dev_info(&rmi4_data->i2c_client->dev, + "%s: F01 registers not defined\n", __func__); + return; + } + /* invalidate offsets only and keep statically defined register size */ + for (r = 0; r < regs->nr_regs; ++r) { + regs->regs[r].offset = -1; + kfree(regs->regs[r].data); + regs->regs[r].data = NULL; + for (s = 0; s < regs->regs[r].nr_subpkts; ++s) { + regs->regs[r].subpkt[s].present = 0; + if (regs->regs[r].subpkt[s].data && + regs->regs[r].subpkt[s].size) + memset(regs->regs[r].subpkt[s].data, 0, + regs->regs[r].subpkt[s].size); + } + } + + regs->base_addr = rmi4_data->f01_ctrl_base_addr; + pr_debug("F01_RMI_Ctrl base addr 0x%x\n", regs->base_addr); + + reg = find_packet_reg(regs, F01_RMI_Ctrl0); + if (unlikely(reg == NULL)) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: F01_RMI_Ctrl0 not defined\n", + __func__); + return; + } + + if (regs && reg && reg->nr_subpkts) { + reg->data = simple_init_packet_reg(reg, 0); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr + F01_QUERY1_OFFSET, + query1.data, + sizeof(query1.data)); + if (retval < 0) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: error reading F01_RMI_Query1\n", __func__); + return; + } + + if (query1.has_sensor_id) { + query42_offset++; /* has F01_RMI_Query22 */ + pr_debug("has F01_RMI_Query22\n"); + } + + if (query1.has_adjustable_doze) { + pr_debug("has F01_RMI_Ctrl2 and F01_RMI_Ctrl3\n"); + current_ctrl_offset++; + reg = find_packet_reg(regs, F01_RMI_Ctrl2); + if (reg) { + reg->data = simple_init_packet_reg(reg, + current_ctrl_offset); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } + current_ctrl_offset++; + reg = find_packet_reg(regs, F01_RMI_Ctrl3); + if (reg) { + reg->data = simple_init_packet_reg(reg, + current_ctrl_offset); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } + } + + if (query1.reserved) { + current_ctrl_offset++; /* has F01_RMI_Ctrl4 */ + query42_offset++; /* has F01_RMI_Query21 */ + query42_offset += 18; /* has F01_RMI_Query23-41 */ + pr_debug("has F01_RMI_Query21,23-41 and F01_RMI_Ctrl4\n"); + } + + if (query1.has_doze_holdoff) { + pr_debug("has F01_RMI_Ctrl5\n"); + current_ctrl_offset++; + reg = find_packet_reg(regs, F01_RMI_Ctrl5); + if (reg) { + reg->data = simple_init_packet_reg(reg, + current_ctrl_offset); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } + } + + query42.data[0] = 0; + if (query1.has_query42) { + pr_debug("F01_RMI_Query42 offset %d\n", query42_offset); + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr + + query42_offset, + query42.data, + sizeof(query42.data)); + if (retval < 0) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: error reading F01_RMI_Query42\n", + __func__); + } else + has_recalibration = query42.has_recalibration; + } + + if (query42.has_ds4_queries) { + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr + + query42_offset + 1, + query43, + sizeof(query43)); + if (retval < 0) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: error reading F01_RMI_Query43\n", + __func__); + } else { + can_access_ctrl9 = true; + query43_size = query43[0] & 0xf; + pr_debug("F01_RMI_Query43 size: %d\n", query43_size); + } + } + + if (query43_size) { + if (query43[2] & 0x1) { + current_ctrl_offset++; /* has F01_RMI_Ctrl6 */ + pr_debug("has F01_RMI_Ctrl6\n"); + } + if (query43[2] & 0x2) { + current_ctrl_offset++; /* has F01_RMI_Ctrl7 */ + pr_debug("has F01_RMI_Ctrl7\n"); + } + if (query43[2] & 0x4) { + current_ctrl_offset++; /* has F01_RMI_Ctrl8 */ + pr_debug("has F01_RMI_Ctrl8\n"); + } + } + + reg = find_packet_reg(regs, F01_RMI_Ctrl9); + if (unlikely(reg == NULL)) { + dev_warn(&rmi4_data->i2c_client->dev, + "%s: F01_RMI_Ctrl9 not present\n", __func__); + return; + } + + if (regs && reg) { + if (has_recalibration == false || can_access_ctrl9 == false) { + simple_deinit_packet_reg(reg); + dev_warn(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + return; + } + + current_ctrl_offset++; + pr_debug("F01_RMI_Ctrl9 offset %d\n", current_ctrl_offset); + + reg->data = simple_init_packet_reg(reg, current_ctrl_offset); + if (!reg->data) { + simple_deinit_packet_reg(reg); + dev_err(&rmi4_data->i2c_client->dev, + "%s: remove r%d.s0 from the list\n", + __func__, reg->r_number); + } + } +} + +struct synaptics_rmi4_fn *get_fhandler(struct synaptics_rmi4_data *rmi4_data, + unsigned char fn_number) +{ + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi = + &(rmi4_data->rmi4_mod_info); + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->fn_number == fn_number) + return fhandler; + } + return NULL; +} + /** + * synaptics_rmi4_query_device() + * + * Called by synaptics_rmi4_probe(). + * + * This funtion scans the page description table, records the offsets + * to the register types of Function $01, sets up the function handlers + * for Function $11 and Function $12, determines the number of interrupt + * sources from the sensor, adds valid Functions with data inputs to the + * Function linked list, parses information from the query registers of + * Function $01, and enables the interrupt sources from the valid Functions + * with data inputs. + */ +static int synaptics_rmi4_query_device(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + unsigned char page_number; + unsigned char intr_count = 0; + unsigned char f01_query[F01_STD_QUERY_LEN] = {0}; + unsigned short pdt_entry_addr; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_fn *fhandler; + struct synaptics_rmi4_device_info *rmi; + struct f34_properties f34_query; + struct f34_properties_v2 f34_query_v2; + struct synaptics_rmi4_f01_device_status status; + int finger_data_addr; + int intr_status_addr; + int touch_data_size; + struct synaptics_rmi4_fn *f12_handler; + + rmi = &(rmi4_data->rmi4_mod_info); + + INIT_LIST_HEAD(&rmi->support_fn_list); + + /* Scan the page description tables of the pages to service */ + for (page_number = 0; page_number < PAGES_TO_SERVICE; page_number++) { + for (pdt_entry_addr = PDT_START; pdt_entry_addr > PDT_END; + pdt_entry_addr -= PDT_ENTRY_SIZE) { + pdt_entry_addr |= (page_number << 8); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + pdt_entry_addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + fhandler = NULL; + + if (rmi_fd.fn_number == 0) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Reached end of PDT\n", + __func__); + break; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: F%02x v%d found (page %d)\n", + __func__, rmi_fd.fn_number, rmi_fd.fn_version, + page_number); + + switch (rmi_fd.fn_number) { + case SYNAPTICS_RMI4_F51: + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc for F%x\n", + __func__, rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f51_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) { + /* not critical, just continue */ + kfree(fhandler); + fhandler = NULL; + dev_err(&rmi4_data->i2c_client->dev, + "%s: No handler for F%x\n", + __func__, rmi_fd.fn_number); + } + break; + + case SYNAPTICS_RMI4_F34: + if (rmi_fd.fn_version < 2) { + retval = synaptics_rmi4_i2c_read( + rmi4_data, + rmi_fd.query_base_addr + + F34_PROPERTIES_OFFSET, + &f34_query.data[0], + sizeof(f34_query)); + if (retval < 0) + return retval; + + if (f34_query.has_config_id) { + retval = synaptics_rmi4_i2c_read( + rmi4_data, + rmi_fd.ctrl_base_addr, + rmi->config_id, + sizeof(rmi->config_id)); + if (retval < 0) + return retval; + } + } else if (rmi_fd.fn_version == 2) { + retval = synaptics_rmi4_i2c_read( + rmi4_data, + rmi_fd.query_base_addr + + F34_PROPERTIES_OFFSET_V2, + &f34_query_v2.data[0], + sizeof(f34_query_v2)); + if (retval < 0) + return retval; + + if (f34_query_v2.has_config_id) { + retval = synaptics_rmi4_i2c_read( + rmi4_data, + rmi_fd.ctrl_base_addr, + rmi->config_id, + sizeof(rmi->config_id)); + if (retval < 0) + return retval; + } + } + break; + + case SYNAPTICS_RMI4_F01: + rmi4_data->f01_query_base_addr = + rmi_fd.query_base_addr; + rmi4_data->f01_ctrl_base_addr = + rmi_fd.ctrl_base_addr; + rmi4_data->f01_data_base_addr = + rmi_fd.data_base_addr; + rmi4_data->f01_cmd_base_addr = + rmi_fd.cmd_base_addr; + pr_debug("query_base_addr 0x%04x\n", + rmi4_data->f01_query_base_addr); + pr_debug("ctrl_base_addr 0x%04x\n", + rmi4_data->f01_ctrl_base_addr); + pr_debug("data_base_addr 0x%04x\n", + rmi4_data->f01_data_base_addr); + pr_debug("cmd_base_addr 0x%04x\n", + rmi4_data->f01_cmd_base_addr); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_data_base_addr, + status.data, + sizeof(status.data)); + if (retval < 0) + return retval; + + /* FlashProg bit might be inaccurate, so */ + /* do not change the flag if it is set */ + if (!rmi4_data->in_bootloader) + rmi4_data->in_bootloader = + status.flash_prog == 1; + break; + + case SYNAPTICS_RMI4_F11: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc for F%x\n", + __func__, rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f11_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to init F11\n", + __func__); + return retval; + } + break; + + case SYNAPTICS_RMI4_F12: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc for F%x\n", + __func__, rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f12_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to init F12\n", + __func__); + return retval; + } + break; + + case SYNAPTICS_RMI4_F1A: + if (rmi_fd.intr_src_count == 0) + break; + + retval = synaptics_rmi4_alloc_fh(&fhandler, + &rmi_fd, page_number); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc for F%x\n", + __func__, rmi_fd.fn_number); + return retval; + } + + retval = synaptics_rmi4_f1a_init(rmi4_data, + fhandler, &rmi_fd, intr_count); + switch (retval) { + case -ENOMEM: + return retval; + case -ENODEV: + /* removing buttons handler if there */ + /* were no key codes in platform data */ + kfree(fhandler); + fhandler = NULL; + rmi_fd.intr_src_count = 0; + break; + } + break; + } + + /* Accumulate the interrupt count */ + intr_count += (rmi_fd.intr_src_count & MASK_3BIT); + + if (fhandler && rmi_fd.intr_src_count) { + pr_debug("adding handler for F%x\n", + fhandler->fn_number); + list_add_tail(&fhandler->link, + &rmi->support_fn_list); + } + } + } + + rmi4_data->num_of_intr_regs = (intr_count + 7) / 8; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Number of interrupt registers = %d\n", + __func__, rmi4_data->num_of_intr_regs); + if (rmi4_data->num_of_intr_regs >= MAX_INTR_REGISTERS) + return -EINVAL; + + f12_handler = get_fhandler(rmi4_data, SYNAPTICS_RMI4_F12); + if (!f12_handler) { + rmi4_data->touch_data_contiguous = 0; + goto skip_f12_single_i2c_setup; + } + + finger_data_addr = f12_handler->full_addr.data_base + + rmi4_data->f12_data_registers_ptr->regs[F12_D1_IDX].offset; + intr_status_addr = rmi4_data->f01_data_base_addr + 1; + + /* is touch data contiguous? */ + if (finger_data_addr != + intr_status_addr + rmi4_data->num_of_intr_regs) { + rmi4_data->touch_data_contiguous = 0; + goto skip_f12_single_i2c_setup; + } + + touch_data_size = rmi4_data->num_of_intr_regs + + rmi4_data->num_of_fingers * f12_handler->size_of_data_register_block; + + /* size of contiguous block changed or not allocated? */ + if (touch_data_size != rmi4_data->touch_data_size || + !rmi4_data->touch_data) { + if (rmi4_data->touch_data) + kfree(rmi4_data->touch_data); + + rmi4_data->touch_data_size = touch_data_size; + rmi4_data->touch_data = kzalloc(rmi4_data->touch_data_size, + GFP_KERNEL); + if (rmi4_data->touch_data) + rmi4_data->touch_data_contiguous = 1; + else { + /* fall back to non-contigous handling */ + rmi4_data->touch_data_contiguous = 0; + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for touch_data\n", + __func__); + } + } + +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + /* Test mode can only be enforced in userdebug build */ + /* When test mode enforced, clear contiguous flag to */ + /* do two I2C transactions to retrieve touch data */ + if (!rmi4_data->test_irq_data_contig) { + rmi4_data->touch_data_contiguous = 0; + pr_notice("Forcing non-contiguous data flag\n"); + } +#endif + +skip_f12_single_i2c_setup: + if (!rmi4_data->touch_data_contiguous) { + rmi4_data->touch_data_size = rmi4_data->num_of_intr_regs; + pr_notice("touch interrupt status and finger data not contiguous\n"); + } + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr, + f01_query, + sizeof(f01_query)); + if (retval < 0) + return retval; + + /* RMI Version 4.0 currently supported */ + rmi->version_major = 4; + rmi->version_minor = 0; + + rmi->manufacturer_id = f01_query[0]; + rmi->product_props = f01_query[1]; + memcpy(&rmi->product_info[0], &f01_query[2], + SYNAPTICS_RMI4_PRODUCT_INFO_SIZE); + memcpy(rmi->serial, &f01_query[4], SYNAPTICS_RMI4_SERIAL_SIZE); + memcpy(rmi->product_id_string, &f01_query[11], + SYNAPTICS_RMI4_PRODUCT_ID_SIZE); + rmi->product_id_string[SYNAPTICS_RMI4_PRODUCT_ID_SIZE] = 0; + + synaptics_dsx_validate_product_string(rmi->product_id_string); + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr+PACKAGE_ID_OFFSET, + rmi->package_id, + sizeof(rmi->package_id)); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_i2c_read(rmi4_data, + rmi4_data->f01_query_base_addr+FW_VERSION_OFFSET, + rmi->build_id, + sizeof(rmi->build_id)); + if (retval < 0) + return retval; + + if (rmi->manufacturer_id != 1) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Non-Synaptics device found," + "manufacturer ID = %d\n", + __func__, rmi->manufacturer_id); + } + + if (!rmi4_data->in_bootloader && !rmi4_data->input_registered) { + retval = input_register_device(rmi4_data->input_dev); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to register input device\n", + __func__); + return retval; + } + rmi4_data->input_registered = true; + } + + memset(rmi4_data->intr_mask, 0x00, sizeof(rmi4_data->intr_mask)); + + /* + * Map out the interrupt bit masks for the interrupt sources + * from the registered function handlers. + */ + list_for_each_entry(fhandler, &rmi->support_fn_list, link) { + if (fhandler->num_of_data_sources) { + rmi4_data->intr_mask[fhandler->intr_reg_num] |= + fhandler->intr_mask; + } + } + + /* interrupt mask for currently active functions */ + batohui(&rmi4_data->active_fn_intr_mask, + rmi4_data->intr_mask, rmi4_data->num_of_intr_regs); + pr_debug("active func intr_mask 0x%x\n", + rmi4_data->active_fn_intr_mask); + + if (rmi4_data->in_bootloader) + pr_info("Product: %s is in bootloader mode\n", + rmi->product_id_string); + else { + unsigned int config_id, firmware_id; + + batohui(&firmware_id, rmi->build_id, sizeof(rmi->build_id)); + batohui(&config_id, rmi->config_id, sizeof(config_id)); + pr_info("Product: %s, firmware id: %x, config id: %08x\n", + rmi->product_id_string, firmware_id, config_id); + /* has to be called after determining */ + /* the number of interrupt registers */ + synaptics_rmi4_scan_f01_reg_info(rmi4_data); + } + + atomic_set(&rmi4_data->query_done, 1); + + return 0; +} + +static void synaptics_rmi4_cleanup(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_fn *fhandler, *next_list_entry; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + list_for_each_entry_safe(fhandler, next_list_entry, + &rmi->support_fn_list, link) { + if (fhandler->fn_number == SYNAPTICS_RMI4_F1A) + synaptics_rmi4_f1a_kfree(fhandler); + else + kfree(fhandler->data); + list_del(&fhandler->link); + kfree(fhandler); + } +} + +static void synaptics_dsx_release_all(struct synaptics_rmi4_data *rmi4_data) +{ + /* reset some TSB global vars like fingers_on_2d after resume + * of reset touch IC + */ + if (rmi4_data->button_0d_enabled) { + rmi4_data->tsb_buff_clean_flag = 1; + rmi4_data->fingers_on_2d = false; + } + + if (rmi4_data->input_dev) { + /* + * Enforce touch release event report to work-around + * such event missing while touch IC is off. + */ +#ifdef TYPE_B_PROTOCOL + int i; + for (i = 0; i < rmi4_data->num_of_fingers; i++) { + INPUT_REPORT_MT_SLOT(rmi4_data, i); + INPUT_REPORT_MT_SLOT_STATE(rmi4_data, + MT_TOOL_FINGER, false); + } +#else + input_mt_sync(rmi4_data->input_dev); +#endif + input_sync(rmi4_data->input_dev); + } +} + +static int synaptics_rmi4_reset_device(struct synaptics_rmi4_data *rmi4_data) +{ + int current_state, retval; + bool need_to_query = false; + + current_state = synaptics_dsx_get_state_safe(rmi4_data); + if (current_state == STATE_UNKNOWN) { + synaptics_rmi4_cleanup(rmi4_data); + need_to_query = true; + } + + retval = synaptics_dsx_ic_reset(rmi4_data, RMI4_HW_RESET); + if (retval > 0) + pr_debug("successful reset took %dms\n", retval); + else + dev_warn(&rmi4_data->i2c_client->dev, "%s: timed out waiting for idle\n", + __func__); + + if (need_to_query) { + if (!rmi4_data->input_dev) { + retval = synaptics_dsx_alloc_input(rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to allocate input device\n", + __func__); + return retval; + } + } + + retval = synaptics_rmi4_query_device(rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to query device\n", + __func__); + return retval; + } + + /* kick off detection work after touch ic changes its mode */ + if (det_workqueue) + queue_delayed_work(det_workqueue, + &rmi4_data->exp_fn_ctrl.det_work, 0); + } + + return 0; +} + +/** +* synaptics_rmi4_detection_work() +* +* Called by the kernel at the scheduled time. +* +* This function is armed by synaptics_new_function call. It checks for +* the insertion and removal of other expansion Function modules such as +* rmi_dev and calls their initialization and removal callback functions +* accordingly. +*/ +static void synaptics_rmi4_detection_work(struct work_struct *work) +{ + struct delayed_work *dw = + container_of(work, struct delayed_work, work); + struct synaptics_exp_fn_ctrl *exp_fn_ctrl = + container_of(dw, struct synaptics_exp_fn_ctrl, det_work); + struct synaptics_rmi4_data *rmi4_data = + container_of(exp_fn_ctrl, struct synaptics_rmi4_data, exp_fn_ctrl); + struct synaptics_rmi4_exp_fn *exp_fhandler, *next_list_entry; + struct device *dev = &rmi4_data->i2c_client->dev; + int state, error, probe_status; + unsigned int scheduled_delay = 0; + bool f34_inserted = false; + bool terminate = false; + bool panel_ready = true; + char *pname = NULL; + +#if defined(CONFIG_DRM) + if (rmi4_data->splash_screen_mode) { + /* DRM panel status */ + panel_ready = dsi_display_is_panel_enable(rmi4_data->ctrl_dsi, + &probe_status, &pname); + dev_dbg(dev, "%s: drm: probe=%d, enable=%d, panel'%s'\n", + __func__, probe_status, panel_ready, pname); + /* check if primary panel is not a dummy one */ + if (pname && strstr(pname, "dummy")) { + dev_info(dev, "%s: dummy panel detected\n", __func__); + rmi4_data->drm_state = DRM_ST_TERM; + } else if (panel_ready) { + if (rmi4_data->drm_state != DRM_ST_TERM) + rmi4_data->drm_state = DRM_ST_READY; + dev_dbg(dev, "%s: panel ready; drm_st=%d\n", __func__, rmi4_data->drm_state); + } + } +#endif + /* pname only gets initialized by DRM */ + /* it implies RMI_DRM_FRAMEWORK handler running */ + if (pname) { + if (ASSERT(DRM_ST_UNDEF)) + goto exit_reschedule; + + if (ASSERT(DRM_ST_TERM)) { + /* hw init failed on previous step, thus + * continue shutting things down */ + terminate = true; + dev_dbg(dev, "%s: set terminate flag\n", __func__); + } + } + + mutex_lock(&exp_fn_ctrl->list_mutex); + if (list_empty(&exp_fn_ctrl->fn_list)) + goto release_mutex; + + /* F34 resets touch IC and might interfere with */ + /* reporting touch events. Need to ensure it was */ + /* successfully inserted before enabling interrupts */ + list_for_each_entry(exp_fhandler, + &exp_fn_ctrl->fn_list, + link) { + if (exp_fhandler->fn_type == RMI_FW_UPDATER && + exp_fhandler->inserted) { + f34_inserted = true; + dev_dbg(dev, "%s: F34 handler inserted\n", __func__); + break; + } + } + + list_for_each_entry_safe(exp_fhandler, + next_list_entry, + &exp_fn_ctrl->fn_list, + link) { + /* terminate all handlers */ + if (terminate) { + dev_dbg(dev, "%s: terminating the rest of handler: %d\n", + __func__, exp_fhandler->fn_type); + /* DRM handler still needs to run for proper cleanup */ + if (exp_fhandler->fn_type != RMI_DRM_FRAMEWORK) + exp_fhandler->func_init = NULL; + } + + if (exp_fhandler->inserted == true) { + if (exp_fhandler->func_init) { + dev_dbg(dev, "%s: handler %d already inserted\n", + __func__, exp_fhandler->fn_type); + continue; + } + + dev_dbg(dev, "%s: calling remove func for handler: %d\n", + __func__, exp_fhandler->fn_type); + exp_fhandler->func_remove(rmi4_data); + + /* need to restore status function on F34 removal */ + if (exp_fhandler->fn_type == RMI_FW_UPDATER && + exp_fhandler->func_status == rmi4_data->get_status) { + rmi4_data->get_status = synaptics_rmi4_f01_flashprog_status; + dev_info(dev, + "%s: using default status retrieval function\n", + __func__); + } + + if (exp_fhandler->fn_type == RMI_DRM_FRAMEWORK) { + if (terminate) { + cancel_delayed_work_sync(&rmi4_data->exp_fn_ctrl.det_work); + flush_workqueue(det_workqueue); + + i2c_set_clientdata(rmi4_data->i2c_client, NULL); + kfree(rmi4_data); + + dev_info(dev, "%s: driver removed\n", __func__); + } else if (f34_inserted) { + state = synaptics_dsx_get_state_safe(rmi4_data); + if (state != STATE_SUSPEND) + synaptics_rmi4_resume(&(rmi4_data->i2c_client->dev)); + dev_info(dev, "%s: panel ready; touch_stopped=%d, flash_enabled=%d\n", + __func__, + atomic_read(&rmi4_data->touch_stopped), + rmi4_data->flash_enabled); + } else { + queue_delayed_work(det_workqueue, + &exp_fn_ctrl->det_work, + msecs_to_jiffies(EXP_FN_DET_INTERVAL)); + dev_dbg(dev, "%s: keep drm fhandler\n", __func__); + continue; + } + } + + list_del(&exp_fhandler->link); + kfree(exp_fhandler); + continue; + } + + /* postpone all handlers, except for RMI_DRM_FRAMEWORK */ + /* until RMI_FW_UPDATER processed */ + if (!f34_inserted && + !(exp_fhandler->fn_type == RMI_FW_UPDATER || + exp_fhandler->fn_type == RMI_DRM_FRAMEWORK)) { + dev_dbg(dev, "%s: hold handler %d until f34 inserted\n", + __func__, exp_fhandler->fn_type); + continue; + } + + state = synaptics_dsx_get_state_safe(rmi4_data); + dev_dbg(dev, "%s: current state: %s, in_bootloader: %s\n", + __func__, synaptics_dsx_state_name(state), + rmi4_data->in_bootloader ? "true" : "false"); + + /* postpone loading test reporting while firmware */ + /* update still in progress */ + if (f34_inserted && (state == STATE_INIT || + state == STATE_FLASH || state == STATE_UNKNOWN)) { + dev_dbg(dev, "%s: firmware update in progress\n", + __func__); + scheduled_delay = 1000; + continue; + } + + if (exp_fhandler->mode == IC_MODE_UI) { + if (rmi4_data->in_bootloader) { + dev_dbg(dev, "%s: handler %d requires UI mode\n", + __func__, exp_fhandler->fn_type); + continue; + } + /* UI mode requires IC powered on */ + if (state == STATE_SUSPEND) { + dev_dbg(dev, "%s: handler %d cannot operate while suspended\n", + __func__, exp_fhandler->fn_type); + scheduled_delay = 500; + continue; + } + /* postpone suspend for loading RMI_F54 and */ + if (exp_fhandler->fn_type == RMI_F54) + synaptics_dsx_sensor_state(rmi4_data, STATE_LOADING); + } + + exp_fhandler->func_init(rmi4_data); + exp_fhandler->inserted = true; + dev_dbg(dev, "%s: handler %d inserted\n", + __func__, exp_fhandler->fn_type); + + if (exp_fhandler->fn_type == RMI_F54 && rmi4_data->f54_data) { + int scan_failures = 0; + struct synaptics_rmi4_func_packet_regs *regs; + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F54); + if (!regs) + goto restore_state; + error = rmi4_data->scan_f54_ctrl_regs(rmi4_data, regs); + if (error) { + regs->nr_regs = 0; + dev_err(dev, "%s: F54_Ctrl scan failed\n", __func__); + } + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F54 | COMMAND_TYPE); + if (!regs) + goto restore_state; + error = rmi4_data->scan_f54_cmd_regs(rmi4_data, regs); + if (error) { + regs->nr_regs = 0; + dev_err(dev, "%s: F54_Cmd scan failed\n", __func__); + } + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F54 | DATA_TYPE); + if (!regs) + goto restore_state; + error = rmi4_data->scan_f54_data_regs(rmi4_data, regs); + if (error) { + regs->nr_regs = 0; + dev_err(dev, "%s: F54_Data scan failed\n", __func__); + scan_failures++; + } + + regs = find_function(rmi4_data, SYNAPTICS_RMI4_F54 | QUERY_TYPE); + if (!regs) + goto restore_state; + error = rmi4_data->scan_f54_query_regs(rmi4_data, regs); + if (error) { + regs->nr_regs = 0; + dev_err(dev, "%s: F54_Query scan failed\n", __func__); + scan_failures++; + } + } + + if (exp_fhandler->fn_type == RMI_FW_UPDATER && exp_fhandler->func_status) { + int status; + + status = exp_fhandler->func_status(rmi4_data); + /* consider changing to */ + /* if (status == 1) */ + /* if Synaptics confirm only 1 is valid */ + if (status) { + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + rmi4_data->in_bootloader = true; + synaptics_dsx_sensor_state(rmi4_data, STATE_BL); + + state = synaptics_dsx_get_state_safe(rmi4_data); + dev_dbg(dev, "%s: current state %s\n", + __func__, synaptics_dsx_state_name(state)); + dev_info(dev, "%s: Product: %s is in bootloader mode\n", + __func__, rmi->product_id_string); + } + /* replace default status retrieval function */ + rmi4_data->get_status = exp_fhandler->func_status; + dev_info(dev, "%s: using F34 status retrieval function\n", __func__); + /* allowing sufficient time for possible upgrade to start */ + scheduled_delay = 3000; + } + + if (exp_fhandler->fn_type == RMI_DRM_FRAMEWORK) { + /* allowing sufficient time for F34 to complete */ + scheduled_delay = 3000; + error = synaptics_rmi4_hw_init(rmi4_data); + exp_fhandler->func_init = NULL; + dev_dbg(dev, "%s: removing DRM\n", __func__); + if (error) { + dev_err(dev, "%s: hw init failed!!!\n", __func__); + /* not dummy panel found, but touch hw init failed, */ + /* thus we need to terminate further processing */ + rmi4_data->drm_state = DRM_ST_TERM; + scheduled_delay = 20; /* no reason to wait anymore */ + break; + } + } + +restore_state: + if (STATE_LOADING == synaptics_dsx_get_state_safe(rmi4_data)) + synaptics_dsx_sensor_state(rmi4_data, state); + } + + if (scheduled_delay) { + queue_delayed_work(det_workqueue, &exp_fn_ctrl->det_work, + msecs_to_jiffies(scheduled_delay)); + dev_dbg(dev, "%s: re-scheduled %d\n", __func__, scheduled_delay); + } + +release_mutex: + mutex_unlock(&exp_fn_ctrl->list_mutex); + + return; + +exit_reschedule: + dev_dbg(dev, "%s: re-scheduling...\n", __func__); + queue_delayed_work(det_workqueue, + &exp_fn_ctrl->det_work, + msecs_to_jiffies(EXP_FN_DET_INTERVAL)); +} + +static int synaptics_exp_fn_ctrl_init( + struct synaptics_rmi4_data *rmi4_data) +{ + struct device *dev = &rmi4_data->i2c_client->dev; + + mutex_init(&rmi4_data->exp_fn_ctrl.ctrl_mutex); + mutex_init(&rmi4_data->exp_fn_ctrl.list_mutex); + INIT_LIST_HEAD(&rmi4_data->exp_fn_ctrl.fn_list); + if (!det_workqueue) { + det_workqueue = create_singlethread_workqueue("rmi_det_workqueue"); + if (!det_workqueue) { + dev_err(dev, "%s: unable to create a workqueue\n", __func__); + return -ENODEV; + } + } + + INIT_DELAYED_WORK(&rmi4_data->exp_fn_ctrl.det_work, synaptics_rmi4_detection_work); + rmi4_data->exp_fn_ctrl.inited = true; + + return 0; +} + +/** +* synaptics_rmi4_new_function() +* +* Called by other expansion Function modules in their module init and +* module exit functions. +* +* This function is used by other expansion Function modules such as +* rmi_dev to register themselves with the driver by providing their +* initialization and removal callback function pointers so that they +* can be inserted or removed dynamically at module init and exit times, +* respectively. +*/ +void synaptics_rmi4_new_function( + struct synaptics_rmi4_data *rmi4_data, + enum exp_fn fn_type, bool insert, + int (*func_init)(struct synaptics_rmi4_data *rmi4_data), + void (*func_remove)(struct synaptics_rmi4_data *rmi4_data), + void (*func_attn)(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask), + int (*func_status)(struct synaptics_rmi4_data *rmi4_data), + enum ic_modes mode) +{ + struct synaptics_rmi4_exp_fn *exp_fhandler; + + mutex_lock(&rmi4_data->exp_fn_ctrl.list_mutex); + if (insert) { + exp_fhandler = kzalloc(sizeof(*exp_fhandler), GFP_KERNEL); + if (!exp_fhandler) { + pr_err("failed to alloc mem for expansion function\n"); + goto exit; + } + exp_fhandler->fn_type = fn_type; + exp_fhandler->func_init = func_init; + exp_fhandler->func_attn = func_attn; + exp_fhandler->func_status = func_status; + exp_fhandler->func_remove = func_remove; + exp_fhandler->inserted = false; + exp_fhandler->mode = mode; + list_add_tail(&exp_fhandler->link, &rmi4_data->exp_fn_ctrl.fn_list); + } else { + list_for_each_entry(exp_fhandler, &rmi4_data->exp_fn_ctrl.fn_list, link) { + if (exp_fhandler->func_init != func_init) + continue; + /* insert flag needed to run remove function in detection */ + /* work even though function was never properly inserted */ + if (!exp_fhandler->inserted) { + exp_fhandler->inserted = true; + dev_info(&rmi4_data->i2c_client->dev, + "removing not inserted handler %d\n", + exp_fhandler->fn_type); + } + exp_fhandler->func_init = NULL; + goto exit; + } + } + +exit: + mutex_unlock(&rmi4_data->exp_fn_ctrl.list_mutex); + if (det_workqueue) + queue_delayed_work(det_workqueue, + &rmi4_data->exp_fn_ctrl.det_work, 0); + return; +} +EXPORT_SYMBOL(synaptics_rmi4_new_function); + +static void synaptics_dsx_free_patch(struct synaptics_dsx_patch *patch_set) +{ + struct synaptics_dsx_func_patch *fp, *next_list_entry; + down(&patch_set->list_sema); + list_for_each_entry_safe(fp, next_list_entry, + &patch_set->cfg_head, link) { + kfree(fp->data); + list_del(&fp->link); + } + up(&patch_set->list_sema); +} + +static struct synaptics_dsx_patch *synaptics_dsx_init_patch(const char *name) +{ + struct synaptics_dsx_patch *patch_set; + patch_set = kzalloc(sizeof(struct synaptics_dsx_patch), GFP_KERNEL); + if (patch_set) { + patch_set->name = name; + sema_init(&patch_set->list_sema, 1); + INIT_LIST_HEAD(&patch_set->cfg_head); + } + return patch_set; +} + +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +static void synaptics_dsx_queued_resume(struct work_struct *w) +{ + struct synaptics_rmi4_data *rmi4_data = + container_of(w, struct synaptics_rmi4_data, resume_work); + synaptics_rmi4_resume(&(rmi4_data->i2c_client->dev)); +} +#endif + +static inline int synaptics_dsx_display_off(struct device *dev) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + cancel_work_sync(&rmi4_data->resume_work); + return synaptics_rmi4_suspend(dev); +} + +static inline int synaptics_dsx_display_on(struct device *dev) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + pr_debug("queue resume\n"); + queue_work(system_wq, &rmi4_data->resume_work); + return 0; +} + +static int rmi_reboot(struct notifier_block *nb, + unsigned long event, + void *unused) +{ + struct synaptics_rmi4_data *rmi4_data = + container_of(nb, struct synaptics_rmi4_data, rmi_reboot); + struct synaptics_dsx_platform_data + *platform_data = &rmi4_data->board; + struct regulator *reg_ptr; + int state = STATE_INVALID; + + if (likely(rmi4_data)) + state = synaptics_dsx_get_state_safe(rmi4_data); + + if (STATE_INVALID == state) + goto out; + + /* At this point, we're all good with clean-up works */ + synaptics_dsx_set_state_safe(rmi4_data, STATE_INVALID); + + if (rmi4_data->irq_enabled) { + rmi4_data->irq_enabled = false; + disable_irq(rmi4_data->irq); + free_irq(rmi4_data->irq, rmi4_data); + } + dev_info(&rmi4_data->i2c_client->dev, "touch shutdown\n"); + + if (!IS_ERR(rmi4_data->vdd_quirk)) { + reg_ptr = rmi4_data->vdd_quirk; + rmi4_data->vdd_quirk = ERR_PTR(-ENODEV); + regulator_force_disable(reg_ptr); + regulator_put(reg_ptr); + } + + if (platform_data->regulator_en && !IS_ERR(rmi4_data->regulator)) { + reg_ptr = rmi4_data->regulator; + rmi4_data->regulator = ERR_PTR(-ENODEV); + pr_debug("touch reboot - disable regulators\n"); + regulator_force_disable(reg_ptr); + regulator_put(reg_ptr); + msleep(1000); + } +out: + return NOTIFY_DONE; +} + +int synaptics_dsx_charger_mode(struct synaptics_rmi4_data *rmi4_data , int is_plugged) +{ + struct config_modifier *cm; + int state; + + down(&rmi4_data->modifiers.list_sema); + list_for_each_entry(cm, &rmi4_data->modifiers.mod_head, link) { + if (cm->id == SYNA_MOD_CHARGER) { + cm->effective = is_plugged; + break; + } + } + up(&rmi4_data->modifiers.list_sema); + /* in case there is no patch required */ + if (!cm) + return -ENODEV; + state = synaptics_dsx_get_state_safe(rmi4_data); + dev_info(&rmi4_data->i2c_client->dev, + "power supply presence %d in state %d\n", is_plugged, state); + /* reset touch ic on power supply presence change */ + if (state == STATE_ACTIVE) { + /* set unknown state to ensure IRQ gets */ + /* enabled on state transition to active */ + synaptics_dsx_sensor_state(rmi4_data, STATE_UNKNOWN); + /* disable IRQ to handle reset */ + synaptics_rmi4_irq_enable(rmi4_data, false); + if (!is_plugged) { + int retval; + /* perform SW reset to restore defaults */ + retval = synaptics_dsx_ic_reset( + rmi4_data, RMI4_SW_RESET); + if (retval < 0) + dev_err(&rmi4_data->i2c_client->dev, + "power supply: sw reset failed %d\n", retval); + } + synaptics_dsx_sensor_ready_state(rmi4_data, false); + /* TODO: just apply a single patch on insertion??? */ + } + + return 0; +} + +static int synaptics_rmi4_hw_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval = 0, attr_count; + struct synaptics_dsx_platform_data + *platform_data = &rmi4_data->board; + + rmi4_data->regulator = devm_regulator_get( + &rmi4_data->i2c_client->dev, "touch_vdd"); + if (IS_ERR(rmi4_data->regulator)) { + retval = PTR_ERR(rmi4_data->regulator); + if (PTR_ERR(rmi4_data->regulator) == -EPROBE_DEFER) + goto err_gpio_free; + + dev_warn(&rmi4_data->i2c_client->dev, + "Failed to get touch_vdd\n"); + goto err_out; + } else { + retval = regulator_enable(rmi4_data->regulator); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, + "Failed to enable touch_vdd\n"); + goto err_out; + } + platform_data->regulator_en = true; + } + + rmi4_data->vdd_quirk = devm_regulator_get( + &rmi4_data->i2c_client->dev, "vdd_quirk"); + if (!IS_ERR(rmi4_data->vdd_quirk)) { + retval = regulator_enable(rmi4_data->vdd_quirk); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, + "Failed to enable vdd-quirk\n"); + goto err_vdd_quirk; + } + } else { + retval = PTR_ERR(rmi4_data->regulator); + if (retval == -EPROBE_DEFER) + goto err_vdd_quirk; + } + + if (platform_data->gpio_config) + retval = platform_data->gpio_config(platform_data, true); + else if (gpio_is_valid(platform_data->reset_gpio)) { + retval = gpio_request(platform_data->reset_gpio, + RESET_GPIO_NAME); + if (!retval) + retval = gpio_direction_output( + platform_data->reset_gpio, 1); + } + + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to configure GPIO\n", + __func__); + goto err_gpio_free; + } + + if (rmi4_data->ts_pinctrl && rmi4_data->pinctrl_state_active) + pinctrl_select_state(rmi4_data->ts_pinctrl, + rmi4_data->pinctrl_state_active); + + retval = synaptics_dsx_alloc_input(rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to allocate input device\n", + __func__); + goto err_gpio_free; + } + + retval = synaptics_dsx_ic_reset(rmi4_data, RMI4_HW_RESET); + if (retval > 0) + dev_dbg(&rmi4_data->i2c_client->dev, "%s: successful reset took %dms\n", + __func__, retval); + else + dev_warn(&rmi4_data->i2c_client->dev, "%s: timed out waiting for idle\n", + __func__); + + retval = synaptics_rmi4_query_device(rmi4_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to query device\n", + __func__); + goto err_query_device; + } + +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + INIT_WORK(&rmi4_data->resume_work, synaptics_dsx_queued_resume); +#endif + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(&rmi4_data->i2c_client->dev.kobj, + &attrs[attr_count].attr); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + goto err_sysfs; + } + } + + synaptics_dsx_sensor_ready_state(rmi4_data, true); + + rmi4_data->rmi_reboot.notifier_call = rmi_reboot; + rmi4_data->rmi_reboot.next = NULL; + rmi4_data->rmi_reboot.priority = 1; + retval = register_reboot_notifier(&rmi4_data->rmi_reboot); + if (retval) + dev_err(&rmi4_data->i2c_client->dev, "register for reboot failed\n"); + +#ifdef CONFIG_MMI_HALL_NOTIFICATIONS + if (rmi4_data->folio_detection_enabled) { + /* register notifier at the end of probe to */ + /* avoid unnecessary reset in STANDBY state */ + rmi4_data->folio_notif.notifier_call = folio_notifier_callback; + dev_dbg(&rmi4_data->i2c_client->dev, "registering folio notifier\n"); + retval = mmi_hall_register_notifier(&rmi4_data->folio_notif, + MMI_HALL_FOLIO, true); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, + "Error registering folio_notifier: %d\n", + retval); + /* inability to register folio notifications handler */ + /* is not fatal, thus reset return value to success */ + retval = 0; + } + } +#endif +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + synaptics_dsx_sysfs_touchscreen(rmi4_data, true); +#endif + synaptics_dsx_pm_qos(rmi4_data, PM_ADD, false); + + /* init reporting semaphore and set expected # of fingers to 1 */ + sema_init(&rmi4_data->rctrl.ctrl_sema, 1); + rmi4_data->rctrl.expected = 1; + + return retval; + +err_sysfs: + for (attr_count--; attr_count >= 0; attr_count--) { + sysfs_remove_file(&rmi4_data->i2c_client->dev.kobj, + &attrs[attr_count].attr); + } + + device_remove_file(&rmi4_data->i2c_client->dev, &dev_attr_poweron); + +err_query_device: + synaptics_rmi4_cleanup(rmi4_data); + if (rmi4_data->input_registered) + input_unregister_device(rmi4_data->input_dev); + else + input_free_device(rmi4_data->input_dev); + +err_gpio_free: + if (platform_data->gpio_config) + gpio_free(platform_data->irq_gpio); + if (gpio_is_valid(platform_data->reset_gpio)) { + gpio_set_value_cansleep(platform_data->reset_gpio, 0); + gpio_free(platform_data->reset_gpio); + } + + if (!IS_ERR(rmi4_data->vdd_quirk)) + regulator_disable(rmi4_data->vdd_quirk); + +err_vdd_quirk: + if (platform_data->regulator_en) + regulator_disable(rmi4_data->regulator); + +err_out: + return retval; +} + +static int dummy_init(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_data *ptr; + ptr = rmi4_data; + return 0; +} + +static void dummy_remove(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_data *ptr = rmi4_data; + ptr = rmi4_data; +} + +static int dsx_pinctrl_init(struct synaptics_rmi4_data *info) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + info->ts_pinctrl = devm_pinctrl_get(&info->i2c_client->dev); + if (IS_ERR_OR_NULL(info->ts_pinctrl)) { + retval = PTR_ERR(info->ts_pinctrl); + dev_err(&info->i2c_client->dev, + "%s cannot find pinctrl %d\n", __func__, retval); + goto err_pinctrl_get; + } + + info->pinctrl_state_active = pinctrl_lookup_state( + info->ts_pinctrl, PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(info->pinctrl_state_active)) { + retval = PTR_ERR(info->pinctrl_state_active); + dev_err(&info->i2c_client->dev, + "%s cannot lookup %s pinstate %d\n", + __func__, PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + info->pinctrl_state_suspend = pinctrl_lookup_state( + info->ts_pinctrl, PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(info->pinctrl_state_suspend)) { + retval = PTR_ERR(info->pinctrl_state_suspend); + dev_err(&info->i2c_client->dev, + "%s cannot lookup %s pinstate %d\n", + __func__, PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(info->ts_pinctrl); +err_pinctrl_get: + info->ts_pinctrl = NULL; + + return retval; +} + + /** + * synaptics_rmi4_probe() + * + * Called by the kernel when an association with an I2C device of the + * same name is made (after doing i2c_add_driver). + * + * This funtion allocates and initializes the resources for the driver + * as an input driver, turns on the power to the sensor, queries the + * sensor for its supported Functions and characteristics, registers + * the driver to the input subsystem, sets up the interrupt, and creates + * a work queue for detection of other expansion Function modules. + */ +static int synaptics_rmi4_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int rc = 0; + struct synaptics_rmi4_data *rmi4_data; + struct synaptics_dsx_platform_data *platform_data; +#if defined(CONFIG_DRM) + int panel_ready, probe_status; + char *display_name = NULL; +#endif + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, + "%s: SMBus byte data not supported\n", + __func__); + return -EIO; + } + + if (client->dev.of_node && !mmi_device_is_available(client->dev.of_node)) { + dev_err(&client->dev, "%s: mmi: device not supported\n", __func__); + return -ENODEV; + } + + rmi4_data = kzalloc(sizeof(*rmi4_data), GFP_KERNEL); + if (!rmi4_data) { + dev_err(&client->dev, + "%s: Failed to alloc mem for rmi4_data\n", + __func__); + return -ENOMEM; + } + dev_dbg(&client->dev, "%s: rmi4_data ptr = %p\n", __func__, rmi4_data); + + /* assign pointer to client structure right away for further use */ + rmi4_data->i2c_client = client; + + rmi4_data->pm_qos_latency = PM_QOS_DEFAULT_VALUE; + +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + /* TEST OPTIONS */ + rmi4_data->test_irq_delay_ms = 0; + rmi4_data->test_irq_data_contig = 1; /* enforce test mode */ +#endif + if (client->dev.of_node) { + rc = synaptics_dsx_of_init(client, rmi4_data); + rc = rc && dsx_pinctrl_init(rmi4_data); + if (rc) { + dev_err(&client->dev, + "%s: No platform data found\n", + __func__); + kfree(rmi4_data); + return -EINVAL; + } + } else + rmi4_data->board = *(struct synaptics_dsx_platform_data *) + client->dev.platform_data; + platform_data = &rmi4_data->board; + +#if defined(CONFIG_DRM) + /* DRM status of panel associated with control DSI */ + panel_ready = dsi_display_is_panel_enable(rmi4_data->ctrl_dsi, + &probe_status, &display_name); + dev_dbg(&client->dev, + "%s: drm: probe=%d, enable=%d, panel'%s'\n", + __func__, probe_status, panel_ready, display_name); + + if (rmi4_data->bound_display) { + if (probe_status == -ENODEV || + (display_name && strncmp(display_name, rmi4_data->bound_display, + strlen(rmi4_data->bound_display)))) { + dev_err(&client->dev, + "%s: display binding failed\n", __func__); + kfree(rmi4_data); + return -ENODEV; + } + } else { + /* check for dummy panel */ + if (display_name && strstr(display_name, "dummy")) { + dev_info(&client->dev, + "%s: dummy panel detected; exiting...\n", __func__); + kfree(rmi4_data); + return -ENODEV; + } + } +#endif + + rc = synaptics_dsx_config_regs_alloc(rmi4_data); + if (rc) { + dev_err(&client->dev, + "%s: cannot allocate resources\n", __func__); + kfree(rmi4_data); + return -ENOMEM; + } + + rmi4_data->touch_ud_stats.ud = devm_kzalloc(&client->dev, + TOUCH_UD_BUFF*sizeof(struct touch_up_down), GFP_KERNEL); + rmi4_data->touch_ud_stats.ud_len = TOUCH_UD_BUFF; + rmi4_data->touch_ud_stats.name = "ts"; + + if (rmi4_data->button_0d_enabled) { + rmi4_data->button_ud_stats.ud = devm_kzalloc(&client->dev, + BUTTON_UD_BUFF*sizeof(struct touch_up_down), GFP_KERNEL); + rmi4_data->button_ud_stats.ud_len = BUTTON_UD_BUFF; + rmi4_data->button_ud_stats.name = "btn"; + } + + rmi4_data->state = STATE_INVALID; + rmi4_data->current_page = MASK_8BIT; + rmi4_data->irq_enabled = false; + atomic_set(&rmi4_data->touch_stopped, 1); + rmi4_data->ic_on = true; + rmi4_data->flash_enabled = false; + rmi4_data->tsb_buff_clean_flag = 1; + rmi4_data->f12_data_registers_ptr = + find_function(rmi4_data, SYNAPTICS_RMI4_F12 | DATA_TYPE); + rmi4_data->i2c_read = synaptics_rmi4_i2c_read; + rmi4_data->i2c_write = synaptics_rmi4_i2c_write; + rmi4_data->set_state = synaptics_dsx_sensor_state; + rmi4_data->ready_state = synaptics_dsx_sensor_ready_state; + rmi4_data->irq_enable = synaptics_rmi4_irq_enable; + rmi4_data->reset_device = synaptics_rmi4_reset_device; + rmi4_data->get_status = synaptics_rmi4_f01_flashprog_status; + + /* get irq number initialized before calling reset */ + rmi4_data->irq = gpio_to_irq(platform_data->irq_gpio); + + i2c_set_clientdata(client, rmi4_data); + + mutex_init(&(rmi4_data->rmi4_io_ctrl_mutex)); + mutex_init(&(rmi4_data->state_mutex)); + + rc = synaptics_exp_fn_ctrl_init(rmi4_data); + if (rc) { + dev_err(&client->dev, + "%s: Cannot alloc exp_fn_ctrl\n", + __func__); + goto free_and_exit; + } + + rc = device_create_file(&client->dev, &dev_attr_poweron); + if (rc < 0) { + dev_err(&client->dev, + "%s: Cannot create poweron\n", + __func__); + goto free_and_exit; + } + + /* add driver's instance to the list */ + rmi4_data->instance = ++drv_instance_counter; + mutex_lock(&instances_mutex); + list_add_tail(&rmi4_data->node, &drv_instances_list); + mutex_unlock(&instances_mutex); + rmi4_data->irq_name = INSTANCE_RMI(DRIVER_NAME, rmi4_data); + rmi4_data->reset_name = INSTANCE_RMI("synaptics_reset", rmi4_data); + + rc = synaptics_mmi_data_init(rmi4_data, true); + if (rc < 0) { + dev_err(&client->dev, "%s: Class init failure\n", __func__); + } + + if (!rmi4_data->splash_screen_mode) { + rc = synaptics_rmi4_hw_init(rmi4_data); + if (rc < 0) { + i2c_set_clientdata(rmi4_data->i2c_client, NULL); + goto free_and_exit; + } + + synaptics_rmi4_resume(&client->dev); + dev_info(&client->dev, "%s: drm panel ready\n", __func__); + dev_err(&client->dev, "%s: touch_stopped=%d, flash_enabled=%d\n", __func__, + atomic_read(&rmi4_data->touch_stopped), + rmi4_data->flash_enabled); + return 0; + } + + dev_dbg(&client->dev, + "%s: delay hw init until DRM reports panel is ready\n", + __func__); + synaptics_dsx_sensor_state(rmi4_data, STATE_UNKNOWN); + synaptics_rmi4_new_function(rmi4_data, RMI_DRM_FRAMEWORK, true, + dummy_init, dummy_remove, NULL, NULL, IC_MODE_ANY); + return 0; + +free_and_exit: + kfree(rmi4_data); + return rc; +} + + /** + * synaptics_rmi4_remove() + * + * Called by the kernel when the association with an I2C device of the + * same name is broken (when the driver is unloaded). + * + * This funtion terminates the work queue, stops sensor data acquisition, + * frees the interrupt, unregisters the driver from the input subsystem, + * turns off the power to the sensor, and frees other allocated resources. + */ +static int synaptics_rmi4_remove(struct i2c_client *client) +{ + int attr_count; + struct synaptics_dsx_platform_data *platform_data; + struct synaptics_rmi4_data *rmi4_data = i2c_get_clientdata(client); + + if (rmi4_data == NULL) + return 0; + + /* At this point, we're all good with clean-up works */ + synaptics_dsx_set_state_safe(rmi4_data, STATE_STANDBY); + atomic_set(&rmi4_data->touch_stopped, 1); + +#ifdef CONFIG_MMI_HALL_NOTIFICATIONS + if (rmi4_data->folio_detection_enabled) + mmi_hall_unregister_notifier(rmi4_data); +#endif + unregister_reboot_notifier(&rmi4_data->rmi_reboot); + + /* power down touch ic */ + platform_data = &rmi4_data->board; + if (platform_data->gpio_config) + gpio_free(platform_data->irq_gpio); + if (gpio_is_valid(platform_data->reset_gpio)) + gpio_free(platform_data->reset_gpio); + + if (rmi4_data->ts_pinctrl && rmi4_data->pinctrl_state_suspend) + pinctrl_select_state(rmi4_data->ts_pinctrl, + rmi4_data->pinctrl_state_suspend); + + if (rmi4_data->ic_on) { + if (!IS_ERR(rmi4_data->vdd_quirk)) + regulator_disable(rmi4_data->vdd_quirk); + + if (rmi4_data->board.regulator_en) + regulator_disable(rmi4_data->regulator); + /* regulators are bound to device, thus no need to put here !!! */ + } + + if (rmi4_data->exp_fn_ctrl.inited) { + cancel_delayed_work_sync(&rmi4_data->exp_fn_ctrl.det_work); + flush_workqueue(det_workqueue); + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(&rmi4_data->i2c_client->dev.kobj, + &attrs[attr_count].attr); + } + + device_remove_file(&rmi4_data->i2c_client->dev, &dev_attr_poweron); +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + synaptics_dsx_sysfs_touchscreen(rmi4_data, false); +#endif + synaptics_rmi4_cleanup(rmi4_data); + + if (rmi4_data->input_registered) + input_unregister_device(rmi4_data->input_dev); + else + input_free_device(rmi4_data->input_dev); + + list_del(&rmi4_data->node); + if (list_empty(&drv_instances_list)) { + destroy_workqueue(det_workqueue); + } + + synaptics_dsx_pm_qos(rmi4_data, PM_REMOVE, false); + + i2c_set_clientdata(client, NULL); + + kfree(rmi4_data); + + return 0; +} + + /** + * synaptics_rmi4_suspend() + * + * Called by the kernel during the suspend phase when the system + * enters suspend. + * + * This function stops finger data acquisition and puts the sensor to + * sleep, disables the interrupt, and turns off the power to the sensor. + */ +int synaptics_rmi4_suspend(struct device *dev) +{ + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + struct synaptics_dsx_platform_data + *platform_data = &rmi4_data->board; + static char ud_stats[PAGE_SIZE]; + + if (atomic_read(&rmi4_data->query_done) == 0) { + pr_warn("called before ic queried\n"); + return 0; + } + + if (atomic_cmpxchg(&rmi4_data->touch_stopped, 0, 1) == 1) + return 0; + + rmi4_data->flash_enabled = false; + synaptics_dsx_sensor_state(rmi4_data, STATE_SUSPEND); + + /* print UD statistics and send release events thereafter */ + synaptics_dsx_ud_stat(rmi4_data, ud_stats, sizeof(ud_stats)); + dev_info(dev, "%s\n", ud_stats); + synaptics_dsx_release_all(rmi4_data); + + if (rmi4_data->purge_enabled) { + int value = 1; /* set flag */ + atomic_set(&rmi4_data->panel_off_flag, value); + pr_debug("touches purge is %s\n", value ? "ON" : "OFF"); + } + + if (rmi4_data->ic_on) { + /* use pinctrl to put touch RESET GPIO into SUSPEND state */ + if (gpio_is_valid(platform_data->reset_gpio)) + gpio_free(platform_data->reset_gpio); + + if (rmi4_data->ts_pinctrl && rmi4_data->pinctrl_state_suspend) + pinctrl_select_state(rmi4_data->ts_pinctrl, + rmi4_data->pinctrl_state_suspend); + + if (!rmi4_data->splash_screen_mode) { + if (!IS_ERR(rmi4_data->vdd_quirk)) + regulator_disable(rmi4_data->vdd_quirk); + + /* if touch REGULATOR is available - turn it OFF */ + if (platform_data->regulator_en) + regulator_disable(rmi4_data->regulator); + } + + rmi4_data->ic_on = false; + } + + synaptics_dsx_pm_qos(rmi4_data, PM_UPDATE, true); + + return 0; +} + + /** + * synaptics_rmi4_resume() + * + * Called by the kernel during the resume phase when the system + * wakes up from suspend. + * + * This function turns on the power to the sensor, wakes the sensor + * from sleep, enables the interrupt, and starts finger data + * acquisition. + */ +int synaptics_rmi4_resume(struct device *dev) +{ + int retval; + int reset = RMI4_HW_RESET; + struct synaptics_rmi4_data *rmi4_data = + i2c_get_clientdata(to_i2c_client(dev)); + struct synaptics_dsx_platform_data + *platform_data = &rmi4_data->board; + + if (atomic_read(&rmi4_data->query_done) == 0) { + pr_warn("called before ic queried\n"); + return 0; + } + + if (atomic_cmpxchg(&rmi4_data->touch_stopped, 1, 0) == 0) + return 0; + + synaptics_dsx_pm_qos(rmi4_data, PM_UPDATE, false); + pr_debug("set pm_qos latency %d\n", rmi4_data->pm_qos_latency); + + if (!rmi4_data->ic_on) { + if (!rmi4_data->splash_screen_mode) { + /* if touch REGULATOR is avaialble - turn it ON */ + if (platform_data->regulator_en) { + retval = regulator_enable(rmi4_data->regulator); + if (retval) + pr_err("failed enabling touch-vdd: %d\n", retval); + } + + if (!IS_ERR(rmi4_data->vdd_quirk)) { + retval = regulator_enable(rmi4_data->vdd_quirk); + if (retval) + pr_err("failed enabling vdd-quirk: %d\n", retval); + } + } + + /* if RESET GPIO is in SUSPEND state - no HW reset */ + if (gpio_is_valid(platform_data->reset_gpio)) { + retval = gpio_get_value_cansleep(platform_data->reset_gpio); + pr_debug("reset gpio state: %d\n", retval); + if (retval == 0) + reset = RMI4_WAIT_READY; + else /* if not in reset, IRQ might be enabled */ + synaptics_rmi4_irq_enable(rmi4_data, false); + } else + synaptics_rmi4_irq_enable(rmi4_data, false); + + if (rmi4_data->ts_pinctrl && rmi4_data->pinctrl_state_active ) + pinctrl_select_state(rmi4_data->ts_pinctrl, + rmi4_data->pinctrl_state_active); + + if (gpio_is_valid(platform_data->reset_gpio)) { + if (gpio_request(platform_data->reset_gpio, + RESET_GPIO_NAME) < 0) + pr_err("failed to request reset gpio\n"); + + gpio_direction_output(platform_data->reset_gpio, 1); + } + rmi4_data->ic_on = true; + } + + /* perform HW reset if needed and wait for touch IC boot completion */ + retval = synaptics_dsx_ic_reset(rmi4_data, reset); + if (retval > 0) + pr_debug("waited for idle %dms\n", retval); + else + dev_warn(&rmi4_data->i2c_client->dev, "%s: timed out waiting for idle\n", + __func__); + + /* read touch IC state */ + synaptics_dsx_sensor_ready_state(rmi4_data, false); + /* transition to active state is completed - allow flashing */ + rmi4_data->flash_enabled = true; + + if (rmi4_data->purge_enabled) { + int value = 0; /* clear flag */ + atomic_set(&rmi4_data->panel_off_flag, value); + pr_debug("touches purge is %s\n", value ? "ON" : "OFF"); + } + + return 0; +} + +#if !defined(CONFIG_PANEL_NOTIFICATIONS) +static const struct dev_pm_ops synaptics_rmi4_dev_pm_ops = { + .suspend = synaptics_rmi4_suspend, + .resume = synaptics_rmi4_resume, +}; +#endif + +static const struct i2c_device_id synaptics_rmi4_id_table[] = { + {DRIVER_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, synaptics_rmi4_id_table); + +static struct i2c_driver synaptics_rmi4_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, +#if !defined(CONFIG_PANEL_NOTIFICATIONS) + .pm = &synaptics_rmi4_dev_pm_ops, +#endif + }, + .probe = synaptics_rmi4_probe, + .remove = synaptics_rmi4_remove, + .id_table = synaptics_rmi4_id_table, +}; + + /** + * synaptics_rmi4_init() + * + * Called by the kernel during do_initcalls (if built-in) + * or when the driver is loaded (if a module). + * + * This function registers the driver to the I2C subsystem. + * + */ +static int __init synaptics_rmi4_init(void) +{ + return i2c_add_driver(&synaptics_rmi4_driver); +} + + /** + * synaptics_rmi4_exit() + * + * Called by the kernel when the driver is unloaded. + * + * This funtion unregisters the driver from the I2C subsystem. + * + */ +static void __exit synaptics_rmi4_exit(void) +{ + i2c_del_driver(&synaptics_rmi4_driver); +} + +module_init(synaptics_rmi4_init); +module_exit(synaptics_rmi4_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX I2C Touch Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(SYNAPTICS_DSX_DRIVER_VERSION); + +#ifdef SOFTDEP_GPIO_PCAL6408 +MODULE_SOFTDEP("pre: gpio-pcal6408"); +#endif diff --git a/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_i2c.h b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_i2c.h new file mode 100644 index 000000000000..49025d60a34a --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_i2c.h @@ -0,0 +1,1109 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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 _SYNAPTICS_DSX_RMI4_H_ +#define _SYNAPTICS_DSX_RMI4_H_ + +#define SYNAPTICS_DSX_DRIVER_VERSION "DSX 1.1" + +/* define to enable USB charger detection */ +#undef USB_CHARGER_DETECTION + +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(USB_CHARGER_DETECTION) +#include +#include +#endif +#if defined(CONFIG_PANEL_NOTIFICATIONS) +#include +#elif defined(CONFIG_DRM) +#include +#endif + +#include +#include + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 38)) +#define KERNEL_ABOVE_2_6_38 +#endif + +#ifdef KERNEL_ABOVE_2_6_38 +#define sstrtoul(...) kstrtoul(__VA_ARGS__) +#else +#define sstrtoul(...) strict_strtoul(__VA_ARGS__) +#endif + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 7, 0)) +#define KERNEL_ABOVE_3_7 +#endif + +#define CLASS_PRIMARY_FNAME "primary" +#define DRIVER_NAME "synaptics_mmi" +#define INPUT_PHYS_NAME "synaptics_dsx_i2c/input" +#define TYPE_B_PROTOCOL + +#define RMI4_WAIT_READY 0 +#define RMI4_HW_RESET 1 +#define RMI4_SW_RESET 2 + +#define NO_0D_WHILE_2D +/* #define REPORT_2D_Z */ +#define REPORT_2D_W + +#define RPT_TYPE (1 << 0) +#define RPT_X_LSB (1 << 1) +#define RPT_X_MSB (1 << 2) +#define RPT_Y_LSB (1 << 3) +#define RPT_Y_MSB (1 << 4) +#define RPT_Z (1 << 5) +#define RPT_WX (1 << 6) +#define RPT_WY (1 << 7) +#define RPT_DEFAULT (RPT_TYPE | RPT_X_LSB | RPT_X_MSB | RPT_Y_LSB | RPT_Y_MSB) + +#define EXP_FN_DET_INTERVAL 1000 /* ms */ +#define POLLING_PERIOD 1 /* ms */ +#define SYN_I2C_RETRY_TIMES 10 +#define MAX_ABS_MT_TOUCH_MAJOR 15 +#define SYN_MAX_BUTTONS 4 + +#define F01_STD_QUERY_LEN 21 +#define F01_BUID_ID_OFFSET 18 +#define F01_QUERY1_OFFSET 1 +#define F01_QUERY21_OFFSET 21 +#define F11_STD_QUERY_LEN 9 +#define F11_STD_CTRL_LEN 10 +#define F11_STD_DATA_LEN 12 +#define F12_STD_QUERY_LEN 10 +#define F12_STD_CTRL_LEN 4 +#define F12_STD_DATA_LEN 80 + +#define RESET_GPIO_NAME "touch_reset" +#define IRQ_GPIO_NAME "touch_irq" + +#define PDT_PROPS (0x00EF) +#define PDT_START (0x00E9) +#define PDT_END (0x000A) +#define PDT_ENTRY_SIZE (0x0006) +#define PAGES_TO_SERVICE (10) +#define PAGE_SELECT_LEN (2) +#define PAGE_SELECT_ENFORCE (0x8000) + +#define SYNAPTICS_RMI4_F01 (0x01) +#define SYNAPTICS_RMI4_F11 (0x11) +#define SYNAPTICS_RMI4_F12 (0x12) +#define SYNAPTICS_RMI4_F1A (0x1a) +#define SYNAPTICS_RMI4_F34 (0x34) +#define SYNAPTICS_RMI4_F35 (0x35) +#define SYNAPTICS_RMI4_F38 (0x38) +#define SYNAPTICS_RMI4_F51 (0x51) +#define SYNAPTICS_RMI4_F54 (0x54) +#define SYNAPTICS_RMI4_F55 (0x55) +#define SYNAPTICS_RMI4_FDB (0xdb) + +#define SYNAPTICS_RMI4_PRODUCT_INFO_SIZE 2 +#define SYNAPTICS_RMI4_PRODUCT_ID_SIZE 10 +#define SYNAPTICS_RMI4_BUILD_ID_SIZE 3 +#define SYNAPTICS_RMI4_SERIAL_SIZE 7 +#define SYNAPTICS_RMI4_CONFIG_ID_SIZE 4 +#define SYNAPTICS_RMI4_PACKAGE_ID_SIZE 4 +#define SYNAPTICS_RMI4_FILENAME_SIZE 80 + +#define PACKAGE_ID_OFFSET 17 +#define FW_VERSION_OFFSET 18 +#define F34_PROPERTIES_OFFSET 1 +#define F34_PROPERTIES_OFFSET_V2 0 + +#define MAX_NUMBER_OF_FINGERS 10 +#define MAX_NUMBER_OF_BUTTONS 4 +#define MAX_INTR_REGISTERS 4 + +#define MASK_16BIT 0xFFFF +#define MASK_8BIT 0xFF +#define MASK_7BIT 0x7F +#define MASK_6BIT 0x3F +#define MASK_5BIT 0x1F +#define MASK_4BIT 0x0F +#define MASK_3BIT 0x07 +#define MASK_2BIT 0x03 +#define MASK_1BIT 0x01 + +#define PRODUCT_INFO_SIZE 2 +#define PRODUCT_ID_SIZE 10 + +/* + * struct synaptics_rmi4_fn_desc - function descriptor fields in PDT + * @query_base_addr: base address for query registers + * @cmd_base_addr: base address for command registers + * @ctrl_base_addr: base address for control registers + * @data_base_addr: base address for data registers + * @intr_src_count: number of interrupt sources + * @fn_number: function number + */ +struct synaptics_rmi4_fn_desc { + unsigned char query_base_addr; + unsigned char cmd_base_addr; + unsigned char ctrl_base_addr; + unsigned char data_base_addr; + unsigned char intr_src_count:3; + unsigned char reserved_1:2; + unsigned char fn_version:2; + unsigned char reserved_2:1; + unsigned char fn_number; +}; + +/* + * synaptics_rmi4_fn_full_addr - full 16-bit base addresses + * @query_base: 16-bit base address for query registers + * @cmd_base: 16-bit base address for data registers + * @ctrl_base: 16-bit base address for command registers + * @data_base: 16-bit base address for control registers + */ +struct synaptics_rmi4_fn_full_addr { + unsigned short query_base; + unsigned short cmd_base; + unsigned short ctrl_base; + unsigned short data_base; +}; + +/* + * struct synaptics_rmi4_fn - function handler data structure + * @fn_number: function number + * @num_of_data_sources: number of data sources + * @num_of_data_points: maximum number of fingers supported + * @size_of_data_register_block: data register block size + * @data1_offset: offset to data1 register from data base address + * @intr_reg_num: index to associated interrupt register + * @intr_mask: interrupt mask + * @full_addr: full 16-bit base addresses of function registers + * @link: linked list for function handlers + * @data_size: size of private data + * @data: pointer to private data + */ +struct synaptics_rmi4_fn { + unsigned char fn_number; + unsigned char num_of_data_sources; + unsigned char num_of_data_points; + unsigned char size_of_data_register_block; + unsigned char data1_offset; + unsigned char intr_reg_num; + unsigned char intr_mask; + struct synaptics_rmi4_fn_full_addr full_addr; + struct list_head link; + int data_size; + void *data; +}; + +/* + * struct synaptics_rmi4_device_info - device information + * @version_major: rmi protocol major version number + * @version_minor: rmi protocol minor version number + * @manufacturer_id: manufacturer id + * @product_props: product properties information + * @product_info: product info array + * @date_code: device manufacture date + * @tester_id: tester id array + * @serial_number: device serial number + * @product_id_string: device product id + * @support_fn_list: linked list for function handlers + */ +struct synaptics_rmi4_device_info { + unsigned int version_major; + unsigned int version_minor; + unsigned char manufacturer_id; + unsigned char product_props; + unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE]; + unsigned char serial[SYNAPTICS_RMI4_SERIAL_SIZE]; + unsigned char package_id[SYNAPTICS_RMI4_PACKAGE_ID_SIZE]; + unsigned char product_id_string[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1]; + unsigned char build_id[SYNAPTICS_RMI4_BUILD_ID_SIZE]; + unsigned char config_id[SYNAPTICS_RMI4_CONFIG_ID_SIZE]; + struct list_head support_fn_list; +}; + +#define TOUCH_UD_BUFF 20 +#define BUTTON_UD_BUFF 10 + +struct touch_up_down { + int mismatch; + unsigned char up_down; + unsigned int counter; +}; + +struct touch_area_stats { + struct touch_up_down *ud; + ssize_t ud_len; + ssize_t ud_id; + ssize_t unknown_counter; + const char *name; +}; + +struct synaptics_dsx_func_patch { + unsigned short func; + unsigned char regstr; + unsigned char subpkt; + unsigned char size; + unsigned char bitmask; + unsigned char *data; + struct list_head link; +}; + +struct synaptics_clip_area { + unsigned xul_clip, yul_clip, xbr_clip, ybr_clip; + unsigned inversion; /* clip inside (when 1) or ouside otherwise */ +}; + +enum { + SYNA_MOD_AOD, + SYNA_MOD_STATS, + SYNA_MOD_FOLIO, + SYNA_MOD_CHARGER, + SYNA_MOD_WAKEUP, + SYNA_MOD_FPS, + SYNA_MOD_QUERY, /* run time query; active only */ + SYNA_MOD_RT, /* run time patch; active only; always effective */ + SYNA_MOD_MAX +}; + +#define FLAG_FORCE_UPDATE 1 +#define FLAG_WAKEABLE 2 +#define FLAG_POWER_SLEEP 4 + +struct synaptics_dsx_patch { + const char *name; + int cfg_num; + unsigned int flags; + struct semaphore list_sema; + struct list_head cfg_head; +}; + +struct config_modifier { + const char *name; + int id; + bool effective; + struct synaptics_dsx_patch *active; + struct synaptics_dsx_patch *suspended; + struct synaptics_clip_area *clipa; + struct list_head link; +}; + +struct synaptics_dsx_modifiers { + int mods_num; + struct semaphore list_sema; + struct list_head mod_head; + +}; + +struct f34_properties { + union { + struct { + unsigned char regmap:1; + unsigned char unlocked:1; + unsigned char has_config_id:1; + unsigned char has_perm_config:1; + unsigned char has_bl_config:1; + unsigned char has_display_config:1; + unsigned char has_blob_config:1; + unsigned char reserved:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_properties_v2 { + union { + struct { + unsigned char query0_subpkt1_size:3; + unsigned char has_config_id:1; + unsigned char reserved:1; + unsigned char has_thqa:1; + unsigned char reserved2:2; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_control_95n { + union { + struct { + /* byte 0 - flags*/ + unsigned char c95_filter_bw:3; + unsigned char c95_byte0_b3_b6:4; + unsigned char c95_disable:1; + + /* bytes 1 - 10 */ + unsigned char c95_first_burst_length_lsb; + unsigned char c95_first_burst_length_msb; + unsigned char c95_addl_burst_length_lsb; + unsigned char c95_addl_burst_length_msb; + unsigned char c95_i_stretch; + unsigned char c95_r_stretch; + unsigned char c95_noise_control1; + unsigned char c95_noise_control2; + unsigned char c95_noise_control3; + unsigned char c95_noise_control4; + } __packed; + struct { + unsigned char data[11]; + } __packed; + }; +}; + +enum { + STATE_UNKNOWN, + STATE_ACTIVE, + STATE_SUSPEND, + STATE_STANDBY = 4, + STATE_BL, + STATE_INIT, + STATE_FLASH, + STATE_QUERY, + STATE_LOADING, + STATE_INVALID +}; + +#define STATE_UI (STATE_ACTIVE | STATE_SUSPEND) + +enum ic_modes { + IC_MODE_ANY = 0, + IC_MODE_BL, + IC_MODE_UI +}; + +enum exp_fn { + RMI_DEV = 0, + RMI_F34, + RMI_F54, + RMI_FW_UPDATER, + RMI_CTRL_ACCESS_BLK, + RMI_DRM_FRAMEWORK, + RMI_LAST, +}; + +static inline void batohs(unsigned short *dest, unsigned char *src) +{ + *dest = src[1] * 0x100 + src[0]; +} + +static inline void hstoba(unsigned char *dest, unsigned short src) +{ + dest[0] = src % 0x100; + dest[1] = src / 0x100; +} + +static inline void batohui(unsigned int *dest, unsigned char *src, size_t size) +{ + int si = size; + *dest = 0; + for (; --si >= 0;) + *dest += src[si] << (si << 3); +} + +struct synaptics_rmi4_subpkt { + bool present; + bool expected; + short offset; + unsigned int size; + void *data; +}; + +struct synaptics_rmi4_packet_reg { + unsigned short r_number; + bool updated; /* indicate that value in *data has been updated */ + bool modified; /* indicate that value in *data has been modified */ + bool expected; + short offset; + unsigned int size; + unsigned char *data; + unsigned char nr_subpkts; + struct synaptics_rmi4_subpkt *subpkt; +}; + +#define MAX_CONFIG_REGS 10 + +struct synaptics_rmi4_func_packet_regs { + unsigned short f_number; + unsigned short base_addr; + unsigned short query_offset; + int nr_regs; + struct synaptics_rmi4_packet_reg *regs; +}; + +struct reporting_ctrl { + bool reporting_stopped; + unsigned int events_cnt; + int expected, max_seen; + struct semaphore ctrl_sema; +}; + +#define MAX_READ_WRITE_SIZE 8096 +#define MIN_READ_WRITE_BUF_SIZE 256 + +struct temp_buffer { + unsigned char *buf; + unsigned short buf_size; +}; + +struct synaptics_exp_fn_ctrl { + bool inited; + struct mutex ctrl_mutex; + struct mutex list_mutex; + struct list_head fn_list; + struct delayed_work det_work; +}; + +/* + * struct synaptics_rmi4_data - rmi4 device instance data + * @i2c_client: pointer to associated i2c client + * @input_dev: pointer to associated input device + * @board: constant pointer to platform data + * @rmi4_mod_info: device information + * @regulator: pointer to associated regulator + * @vdd_quirk: pointer to associated regulator for 'quirk' config + * @rmi4_io_ctrl_mutex: mutex for i2c i/o control + * @det_work: work thread instance for expansion function detection + * @det_workqueue: pointer to work queue for work thread instance + * @early_suspend: instance to support early suspend power management + * @current_page: current page in sensor to acess + * @button_0d_enabled: flag for 0d button support + * @full_pm_cycle: flag for full power management cycle in early suspend stage + * @num_of_intr_regs: number of interrupt registers + * @f01_query_base_addr: query base address for f01 + * @f01_cmd_base_addr: command base address for f01 + * @f01_ctrl_base_addr: control base address for f01 + * @f01_data_base_addr: data base address for f01 + * @irq: attention interrupt + * @sensor_max_x: sensor maximum x value + * @sensor_max_y: sensor maximum y value + * @irq_enabled: flag for indicating interrupt enable status + * @touch_stopped: touch is in suspend state + * @flash_enabled: allow flashing once transition to active state is complete + * @ic_on: touch ic power state + * @fingers_on_2d: flag to indicate presence of fingers in 2d area + * @number_irq: total number of remembered interrupt times + * @last_irq: last interrup time's number (index of the location of interrupt) + * @irq_info: information about last few interrupt times + * @i2c_read: pointer to i2c read function + * @i2c_write: pointer to i2c write function + * @irq_enable: pointer to irq enable function + */ +struct synaptics_rmi4_data { + struct i2c_client *i2c_client; + struct device *ts_class_dev; + struct input_dev *input_dev; + struct synaptics_dsx_platform_data board; + struct synaptics_rmi4_device_info rmi4_mod_info; + struct regulator *regulator; + struct regulator *vdd_quirk; + struct mutex rmi4_io_ctrl_mutex; + struct mutex state_mutex; + atomic_t panel_off_flag; + unsigned char current_page; + unsigned char button_0d_enabled; + unsigned char num_of_rx; + unsigned char num_of_tx; + unsigned char num_of_fingers; + unsigned char aod_mt; + unsigned char f01_ctrl_register_0; + unsigned char intr_mask[MAX_INTR_REGISTERS]; + unsigned short num_of_intr_regs; + unsigned short f01_query_base_addr; + unsigned short f01_cmd_base_addr; + unsigned short f01_ctrl_base_addr; + unsigned short f01_data_base_addr; + unsigned int active_fn_intr_mask; + int state; + int irq; + int ctrl_dsi; + const char *class_entry_name; + const char *bound_display; + int sensor_max_x; + int sensor_max_y; + bool irq_enabled; + atomic_t touch_stopped; + atomic_t query_done; + bool splash_screen_mode; + bool flash_enabled; + bool ic_on; + bool fingers_on_2d; + bool input_registered; + bool in_bootloader; + int (*i2c_read)(struct synaptics_rmi4_data *pdata, unsigned short addr, + unsigned char *data, unsigned short length); + int (*i2c_write)(struct synaptics_rmi4_data *pdata, unsigned short addr, + unsigned char *data, unsigned short length); + void (*set_state)(struct synaptics_rmi4_data *rmi4_data, int state); + int (*ready_state)(struct synaptics_rmi4_data *rmi4_data, bool standby); + int (*irq_enable)(struct synaptics_rmi4_data *rmi4_data, bool enable); + int (*reset_device)(struct synaptics_rmi4_data *rmi4_data); + int (*get_status)(struct synaptics_rmi4_data *rmi4_data); + int number_irq; + int last_irq; + struct synaptics_rmi4_irq_info *irq_info; + + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + + /* features enablers */ + bool charger_detection_enabled; + bool folio_detection_enabled; + bool fps_detection_enabled; + bool wakeup_detection_enabled; + bool patching_enabled; + bool purge_enabled; + + bool suspend_is_wakeable; + bool clipping_on; + struct synaptics_clip_area *clipa; + struct synaptics_dsx_modifiers modifiers; + + struct work_struct resume_work; + struct synaptics_rmi4_func_packet_regs *f12_data_registers_ptr; + struct notifier_block rmi_reboot; +#if defined(USB_CHARGER_DETECTION) + struct power_supply psy; +#endif +#ifdef CONFIG_MMI_HALL_NOTIFICATIONS + struct notifier_block folio_notif; +#endif + bool is_fps_registered; /* FPS notif registration might be delayed */ + struct notifier_block fps_notif; + + struct mutex rmi4_exp_init_mutex; + uint32_t pm_qos_latency; + + struct reporting_ctrl rctrl; + unsigned char tsb_buff_clean_flag; + + bool touch_data_contiguous; + uint8_t *touch_data; + uint16_t touch_data_size; + + struct temp_buffer write_buf; + +#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG) + /* TEST OPTIONS */ + int test_irq_delay_ms; + int test_irq_data_contig; +#endif + + struct synaptics_exp_fn_ctrl exp_fn_ctrl; + struct list_head node; + struct semaphore reset_semaphore; + int drm_state; + int instance; + char *irq_name; + char *reset_name; + + void *fwu_data; + void *rmidev_data; + void *f54_data; + + int (*scan_f54_ctrl_regs)(struct synaptics_rmi4_data *data, + struct synaptics_rmi4_func_packet_regs *regs); + int (*scan_f54_cmd_regs)(struct synaptics_rmi4_data *data, + struct synaptics_rmi4_func_packet_regs *regs); + int (*scan_f54_query_regs)(struct synaptics_rmi4_data *data, + struct synaptics_rmi4_func_packet_regs *regs); + int (*scan_f54_data_regs)(struct synaptics_rmi4_data *data, + struct synaptics_rmi4_func_packet_regs *regs); + + struct touch_area_stats touch_ud_stats; + struct touch_area_stats button_ud_stats; + + struct synaptics_rmi4_func_packet_regs config_regs[MAX_CONFIG_REGS]; +}; + +enum { + DRM_ST_UNDEF = 0, + DRM_ST_READY, + DRM_ST_TERM, + DRM_ST_MAX +}; + +#define ASSERT(s) (rmi4_data->drm_state == s) + +struct pdt_properties { + union { + struct { + unsigned char reserved_1:6; + unsigned char has_bsr:1; + unsigned char reserved_2:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct partition_table { + unsigned char partition_id:5; + unsigned char byte_0_reserved:3; + unsigned char byte_1_reserved; + unsigned char partition_length_7_0; + unsigned char partition_length_15_8; + unsigned char start_physical_address_7_0; + unsigned char start_physical_address_15_8; + unsigned char partition_properties_7_0; + unsigned char partition_properties_15_8; +} __packed; + +struct f01_device_control { + union { + struct { + unsigned char sleep_mode:2; + unsigned char nosleep:1; + unsigned char reserved:2; + unsigned char charger_connected:1; + unsigned char report_rate:1; + unsigned char configured:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v7_query_0 { + union { + struct { + unsigned char subpacket_1_size:3; + unsigned char has_config_id:1; + unsigned char f34_query0_b4:1; + unsigned char has_thqa:1; + unsigned char f34_query0_b6__7:2; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v7_query_1_7 { + union { + struct { + /* query 1 */ + unsigned char bl_minor_revision; + unsigned char bl_major_revision; + + /* query 2 */ + unsigned char bl_fw_id_7_0; + unsigned char bl_fw_id_15_8; + unsigned char bl_fw_id_23_16; + unsigned char bl_fw_id_31_24; + + /* query 3 */ + unsigned char minimum_write_size; + unsigned char block_size_7_0; + unsigned char block_size_15_8; + unsigned char flash_page_size_7_0; + unsigned char flash_page_size_15_8; + + /* query 4 */ + unsigned char adjustable_partition_area_size_7_0; + unsigned char adjustable_partition_area_size_15_8; + + /* query 5 */ + unsigned char flash_config_length_7_0; + unsigned char flash_config_length_15_8; + + /* query 6 */ + unsigned char payload_length_7_0; + unsigned char payload_length_15_8; + + /* query 7 */ + unsigned char f34_query7_b0:1; + unsigned char has_bootloader:1; + unsigned char has_device_config:1; + unsigned char has_flash_config:1; + unsigned char has_manufacturing_block:1; + unsigned char has_guest_serialization:1; + unsigned char has_global_parameters:1; + unsigned char has_core_code:1; + unsigned char has_core_config:1; + unsigned char has_guest_code:1; + unsigned char has_display_config:1; + unsigned char f34_query7_b11__15:5; + unsigned char f34_query7_b16__23; + unsigned char f34_query7_b24__31; + } __packed; + unsigned char data[21]; + }; +}; + +struct f34_v7_data_1_5 { + union { + struct { + unsigned char partition_id:5; + unsigned char f34_data1_b5__7:3; + unsigned char block_offset_7_0; + unsigned char block_offset_15_8; + unsigned char transfer_length_7_0; + unsigned char transfer_length_15_8; + unsigned char command; + unsigned char payload_0; + unsigned char payload_1; + } __packed; + unsigned char data[8]; + }; +}; + +struct f34_v5v6_flash_properties { + union { + struct { + unsigned char reg_map:1; + unsigned char unlocked:1; + unsigned char has_config_id:1; + unsigned char has_pm_config:1; + unsigned char has_bl_config:1; + unsigned char has_disp_config:1; + unsigned char has_ctrl1:1; + unsigned char has_query4:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f34_v5v6_flash_properties_2 { + union { + struct { + unsigned char has_guest_code:1; + unsigned char reserved:7; + } __packed; + unsigned char data[1]; + }; +}; + +struct register_offset { + unsigned char properties; + unsigned char properties_2; + unsigned char block_size; + unsigned char block_count; + unsigned char gc_block_count; + unsigned char flash_status; + unsigned char partition_id; + unsigned char block_number; + unsigned char transfer_length; + unsigned char flash_cmd; + unsigned char payload; +}; + +struct block_count { + unsigned short ui_firmware; + unsigned short ui_config; + unsigned short dp_config; + unsigned short fl_config; + unsigned short pm_config; + unsigned short bl_config; + unsigned short lockdown; + unsigned short guest_code; +}; + +struct physical_address { + unsigned short ui_firmware; + unsigned short ui_config; + unsigned short dp_config; + unsigned short guest_code; +}; + +struct container_descriptor { + unsigned char content_checksum[4]; + unsigned char container_id[2]; + unsigned char minor_version; + unsigned char major_version; + unsigned char reserved_08; + unsigned char reserved_09; + unsigned char reserved_0a; + unsigned char reserved_0b; + unsigned char container_option_flags[4]; + unsigned char content_options_length[4]; + unsigned char content_options_address[4]; + unsigned char content_length[4]; + unsigned char content_address[4]; +}; + +struct image_header_10 { + unsigned char checksum[4]; + unsigned char reserved_04; + unsigned char reserved_05; + unsigned char minor_header_version; + unsigned char major_header_version; + unsigned char reserved_08; + unsigned char reserved_09; + unsigned char reserved_0a; + unsigned char reserved_0b; + unsigned char top_level_container_start_addr[4]; +}; + +struct image_header_05_06 { + /* 0x00 - 0x0f */ + unsigned char checksum[4]; + unsigned char reserved_04; + unsigned char reserved_05; + unsigned char options_firmware_id:1; + unsigned char options_bootloader:1; + unsigned char options_guest_code:1; + unsigned char options_tddi:1; + unsigned char options_reserved:4; + unsigned char header_version; + unsigned char firmware_size[4]; + unsigned char config_size[4]; + /* 0x10 - 0x1f */ + unsigned char product_id[PRODUCT_ID_SIZE]; + unsigned char package_id[2]; + unsigned char package_id_revision[2]; + unsigned char product_info[PRODUCT_INFO_SIZE]; + /* 0x20 - 0x2f */ + unsigned char bootloader_addr[4]; + unsigned char bootloader_size[4]; + unsigned char ui_addr[4]; + unsigned char ui_size[4]; + /* 0x30 - 0x3f */ + unsigned char ds_id[16]; + /* 0x40 - 0x4f */ + union { + struct { + unsigned char cstmr_product_id[PRODUCT_ID_SIZE]; + unsigned char reserved_4a_4f[6]; + }; + struct { + unsigned char dsp_cfg_addr[4]; + unsigned char dsp_cfg_size[4]; + unsigned char reserved_48_4f[8]; + }; + }; + /* 0x50 - 0x53 */ + unsigned char firmware_id[4]; +}; + +struct block_data { + unsigned int size; + const unsigned char *data; +}; + +struct image_metadata { + bool contains_firmware_id; + bool contains_bootloader; + bool contains_disp_config; + bool contains_guest_code; + bool contains_flash_config; + unsigned int firmware_id; + unsigned int checksum; + unsigned int bootloader_size; + unsigned int disp_config_offset; + unsigned char bl_version; + unsigned char product_id[PRODUCT_ID_SIZE + 1]; + unsigned char cstmr_product_id[PRODUCT_ID_SIZE + 1]; + struct block_data bootloader; + struct block_data ui_firmware; + struct block_data ui_config; + struct block_data dp_config; + struct block_data fl_config; + struct block_data bl_config; + struct block_data guest_code; + struct block_data lockdown; + struct block_count blkcount; + struct physical_address phyaddr; +}; + +enum bl_version { + BL_V5 = 5, + BL_V6 = 6, + BL_V7 = 7, + BL_V8 = 8, +}; + +struct synaptics_rmi4_fwu_handle { + enum bl_version bl_version; + bool initialized; + bool in_bl_mode; + bool in_ub_mode; + bool force_update; + bool do_lockdown; + bool has_guest_code; + bool new_partition_table; + bool has_erase_all; + unsigned int data_pos; + unsigned char *ext_data_source; + unsigned char *read_config_buf; + unsigned char intr_mask; + unsigned char command; + unsigned char bootloader_id[2]; + unsigned char config_id[32]; + unsigned char flash_status; + unsigned char partitions; + unsigned short block_size; + unsigned short config_size; + unsigned short config_area; + unsigned short config_block_count; + unsigned short flash_config_length; + unsigned short payload_length; + unsigned short partition_table_bytes; + unsigned short read_config_buf_size; + const unsigned char *config_data; + const unsigned char *image; + unsigned char *image_name; + unsigned int image_size; + struct image_metadata img; + struct register_offset off; + struct block_count blkcount; + struct physical_address phyaddr; + struct f34_v5v6_flash_properties flash_properties; + struct synaptics_rmi4_fn_desc f34_fd; + struct synaptics_rmi4_fn_desc f35_fd; + struct device *dev; + struct work_struct fwu_work; + bool irq_enabled; + struct semaphore irq_sema; + struct wakeup_source *flash_wake_src; + struct completion remove_complete; + + int (*firmware_update)(struct device *dev, const char *fwname); + int (*firmware_erase)(struct device *dev); +}; + +struct image_header { + unsigned int checksum; + unsigned int image_size; + unsigned int config_size; + unsigned char options; + unsigned char bootloader_version; + unsigned char product_id[SYNAPTICS_RMI4_PRODUCT_ID_SIZE + 1]; + unsigned char product_info[SYNAPTICS_RMI4_PRODUCT_INFO_SIZE]; +}; + +static inline ssize_t synaptics_rmi4_show_error(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + dev_warn(&rmi4_data->i2c_client->dev, + "%s Attempted to read from write-only attribute %s\n", + __func__, attr->attr.name); + return -EPERM; +} + +static inline ssize_t synaptics_rmi4_store_error(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + + dev_warn(&rmi4_data->i2c_client->dev, + "%s Attempted to write to read-only attribute %s\n", + __func__, attr->attr.name); + return -EPERM; +} + +/* Touchscreen MMI class related section */ +extern int drv_instance_counter; +extern struct list_head drv_instances_list; +extern struct mutex instances_mutex; + +int synaptics_mmi_data_init(struct synaptics_rmi4_data *ts, bool enable); +int synaptics_dsx_sensor_ready_state( + struct synaptics_rmi4_data *rmi4_data, bool standby); +int synaptics_dsx_wait_for_idle(struct synaptics_rmi4_data *rmi4_data); +int synaptics_rmi4_irq_enable(struct synaptics_rmi4_data *rmi4_data, bool on); +void synaptics_dsx_sensor_state(struct synaptics_rmi4_data *rmi4_data, + int state); +int synaptics_dsx_ic_reset(struct synaptics_rmi4_data *rmi4_data, int reset); +int synaptics_dsx_charger_mode(struct synaptics_rmi4_data *rmi4_data, int state); + +/* suspend resume */ +int synaptics_rmi4_suspend(struct device *dev); +int synaptics_rmi4_resume(struct device *dev); + +/* end of class */ + +struct synaptics_rmi4_exp_fn_ptr { + int (*read)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr, + unsigned char *data, unsigned short length); + int (*write)(struct synaptics_rmi4_data *rmi4_data, unsigned short addr, + unsigned char *data, unsigned short length); + int (*enable)(struct synaptics_rmi4_data *rmi4_data, bool enable); +}; + +void synaptics_rmi4_new_function(struct synaptics_rmi4_data *rmi4_data, + enum exp_fn fn_type, bool insert, + int (*func_init)(struct synaptics_rmi4_data *rmi4_data), + void (*func_remove)(struct synaptics_rmi4_data *rmi4_data), + void (*func_attn)(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask), + int (*func_status)(struct synaptics_rmi4_data *rmi4_data), + enum ic_modes mode); + +int synaptics_rmi4_scan_packet_reg_info( + struct synaptics_rmi4_data *rmi4_data, + unsigned short query_addr, + unsigned short regs_base_addr, + struct synaptics_rmi4_func_packet_regs *regs); + +int synaptics_rmi4_read_packet_reg( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *regs, int idx); + +int synaptics_rmi4_read_packet_regs( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *regs); + +int alloc_buffer(struct temp_buffer *tb, size_t count); + +static inline int secure_memcpy(unsigned char *dest, unsigned int dest_size, + const unsigned char *src, unsigned int src_size, + unsigned int count) +{ + if (dest == NULL || src == NULL) + return -EINVAL; + + if (count > dest_size || count > src_size) + return -EINVAL; + + memcpy((void *)dest, (const void *)src, count); + + return 0; +} + +static inline int synaptics_rmi4_reg_read( + struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, + unsigned char *data, + unsigned short len) +{ + return rmi4_data->i2c_read(rmi4_data, addr, data, len); +} + +static inline int synaptics_rmi4_reg_write( + struct synaptics_rmi4_data *rmi4_data, + unsigned short addr, + unsigned char *data, + unsigned short len) +{ + return rmi4_data->i2c_write(rmi4_data, addr, data, len); +} + +extern int FPS_register_notifier(struct notifier_block *nb, + unsigned long stype, bool report); +extern int FPS_unregister_notifier(struct notifier_block *nb, + unsigned long stype); + +extern struct synaptics_rmi4_data *synaptics_driver_getdata( + struct synaptics_rmi4_data *prev); + +#endif diff --git a/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_instance.c b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_instance.c new file mode 100644 index 000000000000..f268c75b4696 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_instance.c @@ -0,0 +1,45 @@ +/* + * Synaptics DSX touchscreen driver MMI class extention + * + * Copyright (C) 2019 Motorola Mobility + * + * 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 pr_fmt(fmt) "%s: " fmt, __func__ + +#include + +#include "synaptics_dsx_i2c.h" + +int drv_instance_counter; +DEFINE_MUTEX(instances_mutex); +LIST_HEAD(drv_instances_list); + +struct synaptics_rmi4_data *synaptics_driver_getdata( + struct synaptics_rmi4_data *prev) +{ + struct synaptics_rmi4_data *next = NULL; + mutex_lock(&instances_mutex); + if (!prev) { + if (!list_empty(&drv_instances_list)) + next = list_first_entry(&drv_instances_list, + struct synaptics_rmi4_data, node); + } else { + if (!list_is_last(&prev->node, &drv_instances_list)) + next = list_next_entry(prev, node); + } + mutex_unlock(&instances_mutex); + pr_debug("instance ptr %p\n", next); + return next; +} +EXPORT_SYMBOL(synaptics_driver_getdata); + + diff --git a/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_mmi.c b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_mmi.c new file mode 100644 index 000000000000..611098ea0928 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_dsx_mmi.c @@ -0,0 +1,263 @@ +/* + * Synaptics DSX touchscreen driver MMI class extention + * + * Copyright (C) 2019 Motorola Mobility + * + * 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 pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include + +#include "synaptics_dsx_i2c.h" + +#define ASSERT_PTR(p) if (p == NULL) { \ + dev_err(dev, "Failed to get driver data"); \ + return -ENODEV; \ + } + +static int synaptics_mmi_get_class_entry_name(struct device *dev, void *cdata) { + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", ts->class_entry_name); +} + +static int synaptics_mmi_get_vendor(struct device *dev, void *cdata) { + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", "synaptics"); +} + +static int synaptics_mmi_get_productinfo(struct device *dev, void *cdata) { + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_INFO_LEN, "%s", + ts->rmi4_mod_info.product_id_string); +} + +static int synaptics_mmi_get_build_id(struct device *dev, void *cdata) { + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + unsigned int firmware_id; + struct synaptics_rmi4_device_info *rmi; + + ASSERT_PTR(ts); + rmi = &(ts->rmi4_mod_info); + batohui(&firmware_id, rmi->build_id, sizeof(rmi->build_id)); + + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%x", firmware_id); +} + +static int synaptics_mmi_get_config_id(struct device *dev, void *cdata) { + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + unsigned int config_id; + struct synaptics_rmi4_device_info *rmi; + + ASSERT_PTR(ts); + rmi = &(ts->rmi4_mod_info); + batohui(&config_id, rmi->config_id, sizeof(rmi->config_id)); + + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%08x", config_id); +} + +static int synaptics_mmi_get_bus_type(struct device *dev, void *idata) { + TO_INT(idata) = TOUCHSCREEN_MMI_BUS_TYPE_I2C; + return 0; +} + +static int synaptics_mmi_get_irq_status(struct device *dev, void *idata) { + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + + ASSERT_PTR(ts); + TO_INT(idata) = gpio_get_value(ts->board.irq_gpio); + + return 0; +} + +static int synaptics_mmi_get_drv_irq(struct device *dev, void *idata) { + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + + ASSERT_PTR(ts); + TO_INT(idata) = ts->irq_enabled ? 1 : 0; + + return 0; +} + +static int synaptics_mmi_get_poweron(struct device *dev, void *idata) +{ + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + + ASSERT_PTR(ts); + TO_INT(idata) = (atomic_read(&ts->touch_stopped) == 0 && ts->flash_enabled) ? 1 : 0; + + return 0; +} + +static int synaptics_mmi_get_flashprog(struct device *dev, void *idata) +{ + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + + ASSERT_PTR(ts); + TO_INT(idata) = ts->in_bootloader ? 1 : 0; + + return 0; +} + +static int synaptics_mmi_drv_irq(struct device *dev, int state) +{ + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + + ASSERT_PTR(ts); + dev_dbg(dev, "%s\n", __func__); + if (atomic_read(&ts->query_done) != 1) + return -EBUSY; + switch (state) { + case 0: /* Disable irq */ + synaptics_rmi4_irq_enable(ts, false); + break; + case 1: /* Enable irq */ + synaptics_rmi4_irq_enable(ts, true); + break; + default: + dev_err(dev, "%s: invalid value\n", __func__); + return -EINVAL; + } + + return 0; +} + +static int synaptics_mmi_reset(struct device *dev, int type) +{ + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + int ret; + + ASSERT_PTR(ts); + dev_dbg(dev, "%s\n", __func__); + if (atomic_read(&ts->query_done) != 1) + return -EBUSY; + ret = synaptics_dsx_ic_reset(ts, type); + if (ret > 0) + dev_dbg(dev,"%s: successful reset took %dms\n", __func__, ret); + else + dev_warn(dev, "%s: timed out waiting for idle\n", __func__); + + return 0; +} + +static int synaptics_mmi_panel_state(struct device *dev, + enum ts_mmi_pm_mode from, enum ts_mmi_pm_mode to) +{ + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + dev_dbg(dev, "%s: panel state change: %d->%d\n", __func__, from, to); + if (atomic_read(&ts->query_done) != 1) + return -EBUSY; + switch (to) { + case TS_MMI_PM_GESTURE: + case TS_MMI_PM_DEEPSLEEP: + synaptics_rmi4_suspend(dev); + break; + case TS_MMI_PM_ACTIVE: + break; + default: + dev_warn(dev, "invalid panel state %d\n", to); + return -EINVAL; + } + return 0; +} + +static int synaptics_mmi_post_resume(struct device *dev) +{ + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + dev_dbg(dev, "%s\n", __func__); + if (atomic_read(&ts->query_done) != 1) + return -EBUSY; + synaptics_rmi4_resume(dev); + return 0; +} + +static int synaptics_mmi_fw_update(struct device *dev, char *fwname) +{ + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu; + const char *name = fwname; + ASSERT_PTR(ts); + dev_dbg(dev, "%s\n", __func__); + fwu = ts->fwu_data; + if (!fwu || !fwu->firmware_update) + return -ENOSYS; + if (atomic_read(&ts->query_done) != 1) + return -EBUSY; + atomic_set(&ts->query_done, 0); + return fwu->firmware_update(dev, name); +} + +static int synaptics_mmi_fw_erase(struct device *dev) +{ + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu; + ASSERT_PTR(ts); + dev_dbg(dev, "%s\n", __func__); + fwu = ts->fwu_data; + if (!fwu || !fwu->firmware_erase) + return -ENOSYS; + if (atomic_read(&ts->query_done) != 1) + return -EBUSY; + return fwu->firmware_erase(dev); +} + +static int synaptics_mmi_charger_mode(struct device *dev, int mode) +{ + struct synaptics_rmi4_data *ts = dev_get_drvdata(dev); + ASSERT_PTR(ts); + dev_dbg(dev, "%s\n", __func__); + if (atomic_read(&ts->query_done) != 1) + return -EBUSY; + synaptics_dsx_charger_mode(ts, mode); + return 0; +} + +static struct ts_mmi_methods synaptics_mmi_methods = { + .get_vendor = synaptics_mmi_get_vendor, + .get_productinfo = synaptics_mmi_get_productinfo, + .get_build_id = synaptics_mmi_get_build_id, + .get_config_id = synaptics_mmi_get_config_id, + .get_bus_type = synaptics_mmi_get_bus_type, + .get_irq_status = synaptics_mmi_get_irq_status, + .get_drv_irq = synaptics_mmi_get_drv_irq, + .get_poweron = synaptics_mmi_get_poweron, + .get_flashprog = synaptics_mmi_get_flashprog, + .get_class_entry_name = synaptics_mmi_get_class_entry_name, + /* SET methods */ + .reset = synaptics_mmi_reset, + .drv_irq = synaptics_mmi_drv_irq, + .charger_mode = synaptics_mmi_charger_mode, + /* Firmware */ + .firmware_update = synaptics_mmi_fw_update, + .firmware_erase = synaptics_mmi_fw_erase, + /* vendor specific attribute group */ + /* PM callback */ + .panel_state = synaptics_mmi_panel_state, + .post_resume = synaptics_mmi_post_resume, +}; + +int synaptics_mmi_data_init(struct synaptics_rmi4_data *ts, bool enable) +{ + int ret = 0; + if (enable) { + ret = ts_mmi_dev_register(&ts->i2c_client->dev, &synaptics_mmi_methods); + if (ret) + dev_err(&ts->i2c_client->dev, "Failed to register ts mmi\n"); + + } else + ts_mmi_dev_unregister(&ts->i2c_client->dev); + + return ret; +} diff --git a/drivers/input/touchscreen/synaptics_mmi_class/synaptics_mmi_class_fw_update.c b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_mmi_class_fw_update.c new file mode 100644 index 000000000000..d12335bcd63c --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_mmi_class_fw_update.c @@ -0,0 +1,4403 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_i2c.h" + +#if 0 +#ifdef pr_debug +#undef pr_debug +#define pr_debug pr_err +#endif +#ifdef dev_dbg +#undef dev_dbg +#define dev_dbg dev_err +#endif +#endif + +#define FORCE_UPDATE false +#define DO_LOCKDOWN false + +#define MAX_IMAGE_NAME_LEN 256 +#define MAX_FIRMWARE_ID_LEN 10 + +#define IMAGE_HEADER_VERSION_05 0x05 +#define IMAGE_HEADER_VERSION_06 0x06 +#define IMAGE_HEADER_VERSION_10 0x10 + +#define IMAGE_AREA_OFFSET 0x100 +#define LOCKDOWN_SIZE 0x50 + +#define V5V6_BOOTLOADER_ID_OFFSET 0 +#define V5V6_CONFIG_ID_SIZE 4 + +#define V5_PROPERTIES_OFFSET 2 +#define V5_BLOCK_SIZE_OFFSET 3 +#define V5_BLOCK_COUNT_OFFSET 5 +#define V5_BLOCK_NUMBER_OFFSET 0 +#define V5_BLOCK_DATA_OFFSET 2 + +#define V6_PROPERTIES_OFFSET 1 +#define V6_BLOCK_SIZE_OFFSET 2 +#define V6_BLOCK_COUNT_OFFSET 3 +#define V6_PROPERTIES_2_OFFSET 4 +#define V6_GUEST_CODE_BLOCK_COUNT_OFFSET 5 +#define V6_BLOCK_NUMBER_OFFSET 0 +#define V6_BLOCK_DATA_OFFSET 1 +#define V6_FLASH_COMMAND_OFFSET 2 +#define V6_FLASH_STATUS_OFFSET 3 + +#define V7_CONFIG_ID_SIZE 32 + +#define V7_FLASH_STATUS_OFFSET 0 +#define V7_PARTITION_ID_OFFSET 1 +#define V7_BLOCK_NUMBER_OFFSET 2 +#define V7_TRANSFER_LENGTH_OFFSET 3 +#define V7_COMMAND_OFFSET 4 +#define V7_PAYLOAD_OFFSET 5 + +#define V7_PARTITION_SUPPORT_BYTES 4 + +#define F35_ERROR_CODE_OFFSET 0 +#define F35_CHUNK_NUM_LSB_OFFSET 0 +#define F35_CHUNK_NUM_MSB_OFFSET 1 +#define F35_CHUNK_DATA_OFFSET 2 +#define F35_CHUNK_COMMAND_OFFSET 18 + +#define F35_CHUNK_SIZE 16 +#define F35_ERASE_ALL_WAIT_MS 3000 +#define F35_RESET_WAIT_MS 250 + +#define SLEEP_MODE_NORMAL (0x00) +#define SLEEP_MODE_SENSOR_SLEEP (0x01) +#define SLEEP_MODE_RESERVED0 (0x02) +#define SLEEP_MODE_RESERVED1 (0x03) + +#define ENABLE_WAIT_MS (1 * 1000) +#define WRITE_WAIT_MS (3 * 1000) +#define ERASE_WAIT_MS (5 * 1000) + +#define MIN_SLEEP_TIME_US 50 +#define MAX_SLEEP_TIME_US 100 + +#define INT_DISABLE_WAIT_MS 20 +#define ENTER_FLASH_PROG_WAIT_MS 20 + +#define LOGDEV (fwu->dev) +#define SYSFS_KOBJ (&rmi4_data->i2c_client->dev.kobj) + +static int fwu_do_reflash(struct synaptics_rmi4_fwu_handle *fwu); + +static int fwu_recovery_check_status(struct synaptics_rmi4_fwu_handle *fwu); + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static ssize_t fwu_sysfs_show_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t fwu_sysfs_store_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t fwu_sysfs_do_recovery_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_write_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_read_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_config_area_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_image_name_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_image_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_block_size_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_guest_code_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t fwu_sysfs_write_guest_code_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); +#endif + +static ssize_t fwu_sysfs_erase_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t fwu_sysfs_force_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +enum f34_version { + F34_V0 = 0, + F34_V1, + F34_V2, +}; + +enum flash_area { + NONE = 0, + UI_FIRMWARE, + UI_CONFIG, +}; + +enum update_mode { + NORMAL = 1, + FORCE = 2, + LOCKDOWN = 8, +}; + +enum config_area { + UI_CONFIG_AREA = 0, + PM_CONFIG_AREA, + BL_CONFIG_AREA, + DP_CONFIG_AREA, + FLASH_CONFIG_AREA, +}; + +enum v7_status { + SUCCESS = 0x00, + DEVICE_NOT_IN_BOOTLOADER_MODE, + INVALID_PARTITION, + INVALID_COMMAND, + INVALID_BLOCK_OFFSET, + INVALID_TRANSFER, + NOT_ERASED, + FLASH_PROGRAMMING_KEY_INCORRECT, + BAD_PARTITION_TABLE, + CHECKSUM_FAILED, + FLASH_HARDWARE_FAILURE = 0x1f, +}; + +enum v7_partition_id { + BOOTLOADER_PARTITION = 0x01, + DEVICE_CONFIG_PARTITION, + FLASH_CONFIG_PARTITION, + MANUFACTURING_BLOCK_PARTITION, + GUEST_SERIALIZATION_PARTITION, + GLOBAL_PARAMETERS_PARTITION, + CORE_CODE_PARTITION, + CORE_CONFIG_PARTITION, + GUEST_CODE_PARTITION, + DISPLAY_CONFIG_PARTITION, +}; + +enum v7_flash_command { + CMD_V7_IDLE = 0x00, + CMD_V7_ENTER_BL, + CMD_V7_READ, + CMD_V7_WRITE, + CMD_V7_ERASE, + CMD_V7_ERASE_AP, + CMD_V7_SENSOR_ID, +}; + +enum v5v6_flash_command { + CMD_V5V6_IDLE = 0x0, + CMD_V5V6_WRITE_FW = 0x2, + CMD_V5V6_ERASE_ALL = 0x3, + CMD_V5V6_WRITE_LOCKDOWN = 0x4, + CMD_V5V6_READ_CONFIG = 0x5, + CMD_V5V6_WRITE_CONFIG = 0x6, + CMD_V5V6_ERASE_UI_CONFIG = 0x7, + CMD_V5V6_ERASE_BL_CONFIG = 0x9, + CMD_V5V6_ERASE_DISP_CONFIG = 0xa, + CMD_V5V6_ERASE_GUEST_CODE = 0xb, + CMD_V5V6_WRITE_GUEST_CODE = 0xc, + CMD_V5V6_ENABLE_FLASH_PROG = 0xf, +}; + +enum flash_command { + CMD_IDLE = 0, + CMD_WRITE_FW, + CMD_WRITE_CONFIG, + CMD_WRITE_LOCKDOWN, + CMD_WRITE_GUEST_CODE, + CMD_READ_CONFIG, + CMD_ERASE_ALL, + CMD_ERASE_UI_FIRMWARE, + CMD_ERASE_UI_CONFIG, + CMD_ERASE_BL_CONFIG, + CMD_ERASE_DISP_CONFIG, + CMD_ERASE_FLASH_CONFIG, + CMD_ERASE_GUEST_CODE, + CMD_ENABLE_FLASH_PROG, +}; + +enum f35_flash_command { + CMD_F35_IDLE = 0x0, + CMD_F35_RESERVED = 0x1, + CMD_F35_WRITE_CHUNK = 0x2, + CMD_F35_ERASE_ALL = 0x3, + CMD_F35_RESET = 0x10, +}; + +enum container_id { + TOP_LEVEL_CONTAINER = 0, + UI_CONTAINER, + UI_CONFIG_CONTAINER, + BL_CONTAINER, + BL_IMAGE_CONTAINER, + BL_CONFIG_CONTAINER, + BL_LOCKDOWN_INFO_CONTAINER, + PERMANENT_CONFIG_CONTAINER, + GUEST_CODE_CONTAINER, + BL_PROTOCOL_DESCRIPTOR_CONTAINER, + UI_PROTOCOL_DESCRIPTOR_CONTAINER, + RMI_SELF_DISCOVERY_CONTAINER, + RMI_PAGE_CONTENT_CONTAINER, + GENERAL_INFORMATION_CONTAINER, + DEVICE_CONFIG_CONTAINER, + FLASH_CONFIG_CONTAINER, + GUEST_SERIALIZATION_CONTAINER, + GLOBAL_PARAMETERS_CONTAINER, + CORE_CODE_CONTAINER, + CORE_CONFIG_CONTAINER, + DISPLAY_CONFIG_CONTAINER, +}; + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static struct bin_attribute dev_attr_data = { + .attr = { + .name = "data", + .mode = (S_IRUGO | S_IWUSR | S_IWGRP), + }, + .size = 0, + .read = fwu_sysfs_show_image, + .write = fwu_sysfs_store_image, +}; +#endif + +static struct device_attribute attrs[] = { +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI + __ATTR(dorecovery, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_do_recovery_store), + __ATTR(writeconfig, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_write_config_store), + __ATTR(readconfig, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_read_config_store), + __ATTR(configarea, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_config_area_store), + __ATTR(imagename, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_image_name_store), + __ATTR(imagesize, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_image_size_store), + __ATTR(blocksize, S_IRUGO, + fwu_sysfs_block_size_show, + synaptics_rmi4_store_error), + __ATTR(fwblockcount, S_IRUGO, + fwu_sysfs_firmware_block_count_show, + synaptics_rmi4_store_error), + __ATTR(configblockcount, S_IRUGO, + fwu_sysfs_configuration_block_count_show, + synaptics_rmi4_store_error), + __ATTR(dispconfigblockcount, S_IRUGO, + fwu_sysfs_disp_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(permconfigblockcount, S_IRUGO, + fwu_sysfs_perm_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(blconfigblockcount, S_IRUGO, + fwu_sysfs_bl_config_block_count_show, + synaptics_rmi4_store_error), + __ATTR(guestcodeblockcount, S_IRUGO, + fwu_sysfs_guest_code_block_count_show, + synaptics_rmi4_store_error), + __ATTR(writeguestcode, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_write_guest_code_store), +#endif + __ATTR(doreflash, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_do_reflash_store), + __ATTR(forcereflash, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_force_reflash_store), +}; + +#include + +static struct device_attribute erase_attr[] = { + __ATTR(erase_all, S_IWUSR | S_IWGRP, + synaptics_rmi4_show_error, + fwu_sysfs_erase_store), +}; + +static inline void sema_clear(struct semaphore *sem) +{ + do { + } while (!down_trylock(sem)); +} + +static unsigned int le_to_uint(const unsigned char *ptr) +{ + return (unsigned int)ptr[0] + + (unsigned int)ptr[1] * 0x100 + + (unsigned int)ptr[2] * 0x10000 + + (unsigned int)ptr[3] * 0x1000000; +} + +static int fwu_allocate_read_config_buf( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned int count) +{ + if (count > fwu->read_config_buf_size) { + kfree(fwu->read_config_buf); + fwu->read_config_buf = kzalloc(count, GFP_KERNEL); + if (!fwu->read_config_buf) { + dev_err(LOGDEV, + "%s: Failed to alloc mem for fwu->read_config_buf\n", + __func__); + fwu->read_config_buf_size = 0; + return -ENOMEM; + } + fwu->read_config_buf_size = count; + } + return 0; +} + +static void fwu_compare_partition_tables(struct synaptics_rmi4_fwu_handle *fwu) +{ + if (fwu->phyaddr.ui_firmware != fwu->img.phyaddr.ui_firmware) { + fwu->new_partition_table = true; + return; + } + + if (fwu->phyaddr.ui_config != fwu->img.phyaddr.ui_config) { + fwu->new_partition_table = true; + return; + } + + if (fwu->flash_properties.has_disp_config) { + if (fwu->phyaddr.dp_config != fwu->img.phyaddr.dp_config) { + fwu->new_partition_table = true; + return; + } + } + + if (fwu->flash_properties.has_disp_config) { + if (fwu->phyaddr.dp_config != fwu->img.phyaddr.dp_config) { + fwu->new_partition_table = true; + return; + } + } + + if (fwu->has_guest_code) { + if (fwu->phyaddr.guest_code != fwu->img.phyaddr.guest_code) { + fwu->new_partition_table = true; + return; + } + } + + dev_dbg(LOGDEV, "%s: same partition table\n", __func__); + fwu->new_partition_table = false; + return; +} + +static void fwu_parse_partition_table( + struct synaptics_rmi4_fwu_handle *fwu, + const unsigned char *partition_table, + struct block_count *blkcount, + struct physical_address *phyaddr) +{ + unsigned char ii; + unsigned char index; + unsigned char offset; + unsigned short partition_length; + unsigned short physical_address; + struct partition_table *ptable; + + for (ii = 0; ii < fwu->partitions; ii++) { + index = ii * 8 + 2; + ptable = (struct partition_table *)&partition_table[index]; + partition_length = ptable->partition_length_15_8 << 8 | + ptable->partition_length_7_0; + physical_address = ptable->start_physical_address_15_8 << 8 | + ptable->start_physical_address_7_0; + dev_dbg(LOGDEV, + "%s: Partition entry %d:\n", + __func__, ii); + for (offset = 0; offset < 8; offset++) { + dev_dbg(LOGDEV, + "%s: 0x%02x\n", + __func__, + partition_table[index + offset]); + } + switch (ptable->partition_id) { + case CORE_CODE_PARTITION: + blkcount->ui_firmware = partition_length; + phyaddr->ui_firmware = physical_address; + dev_dbg(LOGDEV, + "%s: Core code block count: %d\n", + __func__, blkcount->ui_firmware); + break; + case CORE_CONFIG_PARTITION: + blkcount->ui_config = partition_length; + phyaddr->ui_config = physical_address; + dev_dbg(LOGDEV, + "%s: Core config block count: %d\n", + __func__, blkcount->ui_config); + break; + case DISPLAY_CONFIG_PARTITION: + blkcount->dp_config = partition_length; + phyaddr->dp_config = physical_address; + dev_dbg(LOGDEV, + "%s: Display config block count: %d\n", + __func__, blkcount->dp_config); + break; + case FLASH_CONFIG_PARTITION: + blkcount->fl_config = partition_length; + dev_dbg(LOGDEV, + "%s: Flash config block count: %d\n", + __func__, blkcount->fl_config); + break; + case GUEST_CODE_PARTITION: + blkcount->guest_code = partition_length; + phyaddr->guest_code = physical_address; + dev_dbg(LOGDEV, + "%s: Guest code block count: %d\n", + __func__, blkcount->guest_code); + break; + case GUEST_SERIALIZATION_PARTITION: + blkcount->pm_config = partition_length; + dev_dbg(LOGDEV, + "%s: Guest serialization block count: %d\n", + __func__, blkcount->pm_config); + break; + case GLOBAL_PARAMETERS_PARTITION: + blkcount->bl_config = partition_length; + dev_dbg(LOGDEV, + "%s: Global parameters block count: %d\n", + __func__, blkcount->bl_config); + break; + case DEVICE_CONFIG_PARTITION: + blkcount->lockdown = partition_length; + dev_dbg(LOGDEV, + "%s: Device config block count: %d\n", + __func__, blkcount->lockdown); + break; + }; + } + + return; +} + +static void fwu_parse_image_header_10_bl_container( + struct synaptics_rmi4_fwu_handle *fwu, + const unsigned char *image) +{ + unsigned char ii; + unsigned char num_of_containers; + unsigned int addr; + unsigned int container_id; + unsigned int length; + const unsigned char *content; + struct container_descriptor *descriptor; + + num_of_containers = (fwu->img.bootloader.size - 4) / 4; + + for (ii = 1; ii <= num_of_containers; ii++) { + addr = le_to_uint(fwu->img.bootloader.data + (ii * 4)); + descriptor = (struct container_descriptor *)(image + addr); + container_id = descriptor->container_id[0] | + descriptor->container_id[1] << 8; + content = image + le_to_uint(descriptor->content_address); + length = le_to_uint(descriptor->content_length); + switch (container_id) { + case BL_CONFIG_CONTAINER: + case GLOBAL_PARAMETERS_CONTAINER: + fwu->img.bl_config.data = content; + fwu->img.bl_config.size = length; + break; + case BL_LOCKDOWN_INFO_CONTAINER: + case DEVICE_CONFIG_CONTAINER: + fwu->img.lockdown.data = content; + fwu->img.lockdown.size = length; + break; + default: + break; + }; + } + + return; +} + +static void fwu_parse_image_header_10(struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned char ii; + unsigned char num_of_containers; + unsigned int addr; + unsigned int offset; + unsigned int container_id; + unsigned int length; + const unsigned char *image; + const unsigned char *content; + struct container_descriptor *descriptor; + struct image_header_10 *header; + + image = fwu->image; + header = (struct image_header_10 *)image; + + fwu->img.checksum = le_to_uint(header->checksum); + + /* address of top level container */ + offset = le_to_uint(header->top_level_container_start_addr); + descriptor = (struct container_descriptor *)(image + offset); + + /* address of top level container content */ + offset = le_to_uint(descriptor->content_address); + num_of_containers = le_to_uint(descriptor->content_length) / 4; + + for (ii = 0; ii < num_of_containers; ii++) { + addr = le_to_uint(image + offset); + offset += 4; + descriptor = (struct container_descriptor *)(image + addr); + container_id = descriptor->container_id[0] | + descriptor->container_id[1] << 8; + content = image + le_to_uint(descriptor->content_address); + length = le_to_uint(descriptor->content_length); + switch (container_id) { + case UI_CONTAINER: + case CORE_CODE_CONTAINER: + fwu->img.ui_firmware.data = content; + fwu->img.ui_firmware.size = length; + break; + case UI_CONFIG_CONTAINER: + case CORE_CONFIG_CONTAINER: + fwu->img.ui_config.data = content; + fwu->img.ui_config.size = length; + break; + case BL_CONTAINER: + fwu->img.bl_version = *content; + fwu->img.bootloader.data = content; + fwu->img.bootloader.size = length; + fwu_parse_image_header_10_bl_container(fwu, image); + break; + case GUEST_CODE_CONTAINER: + fwu->img.contains_guest_code = true; + fwu->img.guest_code.data = content; + fwu->img.guest_code.size = length; + break; + case DISPLAY_CONFIG_CONTAINER: + fwu->img.contains_disp_config = true; + fwu->img.dp_config.data = content; + fwu->img.dp_config.size = length; + break; + case FLASH_CONFIG_CONTAINER: + fwu->img.contains_flash_config = true; + fwu->img.fl_config.data = content; + fwu->img.fl_config.size = length; + break; + case GENERAL_INFORMATION_CONTAINER: + fwu->img.contains_firmware_id = true; + fwu->img.firmware_id = le_to_uint(content + 4); + break; + default: + break; + } + } + + return; +} + +static void fwu_parse_image_header_05_06(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + const unsigned char *image; + struct image_header_05_06 *header; + + image = fwu->image; + header = (struct image_header_05_06 *)image; + + fwu->img.checksum = le_to_uint(header->checksum); + + fwu->img.bl_version = header->header_version; + + fwu->img.contains_bootloader = header->options_bootloader; + if (fwu->img.contains_bootloader) + fwu->img.bootloader_size = le_to_uint(header->bootloader_size); + + fwu->img.ui_firmware.size = le_to_uint(header->firmware_size); + if (fwu->img.ui_firmware.size) { + fwu->img.ui_firmware.data = image + IMAGE_AREA_OFFSET; + if (fwu->img.contains_bootloader) + fwu->img.ui_firmware.data += fwu->img.bootloader_size; + } + + if ((fwu->img.bl_version == BL_V6) && header->options_tddi) + fwu->img.ui_firmware.data = image + IMAGE_AREA_OFFSET; + + fwu->img.ui_config.size = le_to_uint(header->config_size); + if (fwu->img.ui_config.size) { + fwu->img.ui_config.data = fwu->img.ui_firmware.data + + fwu->img.ui_firmware.size; + } + + if (fwu->img.contains_bootloader || header->options_tddi) + fwu->img.contains_disp_config = true; + else + fwu->img.contains_disp_config = false; + + if (fwu->img.contains_disp_config) { + fwu->img.disp_config_offset = le_to_uint(header->dsp_cfg_addr); + fwu->img.dp_config.size = le_to_uint(header->dsp_cfg_size); + fwu->img.dp_config.data = image + fwu->img.disp_config_offset; + } else { + retval = secure_memcpy(fwu->img.cstmr_product_id, + sizeof(fwu->img.cstmr_product_id), + header->cstmr_product_id, + sizeof(header->cstmr_product_id), + PRODUCT_ID_SIZE); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to copy custom product ID string\n", + __func__); + } + fwu->img.cstmr_product_id[PRODUCT_ID_SIZE] = 0; + } + + fwu->img.contains_firmware_id = header->options_firmware_id; + if (fwu->img.contains_firmware_id) + fwu->img.firmware_id = le_to_uint(header->firmware_id); + + retval = secure_memcpy(fwu->img.product_id, + sizeof(fwu->img.product_id), + header->product_id, + sizeof(header->product_id), + PRODUCT_ID_SIZE); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to copy product ID string\n", + __func__); + } + fwu->img.product_id[PRODUCT_ID_SIZE] = 0; + + fwu->img.lockdown.size = LOCKDOWN_SIZE; + fwu->img.lockdown.data = image + IMAGE_AREA_OFFSET - LOCKDOWN_SIZE; + + return; +} + +static int fwu_parse_image_info(struct synaptics_rmi4_fwu_handle *fwu) +{ + struct image_header_10 *header; + + header = (struct image_header_10 *)fwu->image; + + memset(&fwu->img, 0x00, sizeof(fwu->img)); + + switch (header->major_header_version) { + case IMAGE_HEADER_VERSION_10: + fwu_parse_image_header_10(fwu); + break; + case IMAGE_HEADER_VERSION_05: + case IMAGE_HEADER_VERSION_06: + fwu_parse_image_header_05_06(fwu); + break; + default: + dev_err(LOGDEV, + "%s: Unsupported image file format (0x%02x)\n", + __func__, header->major_header_version); + return -EINVAL; + } + + if (fwu->bl_version >= BL_V7) { + if (!fwu->img.contains_flash_config) { + dev_err(LOGDEV, + "%s: No flash config found in firmware image\n", + __func__); + return -EINVAL; + } + + fwu_parse_partition_table(fwu, fwu->img.fl_config.data, + &fwu->img.blkcount, &fwu->img.phyaddr); + + fwu_compare_partition_tables(fwu); + } else { + fwu->new_partition_table = false; + } + + return 0; +} + +static int fwu_read_flash_status(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval, partition = -1; + unsigned char status; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->off.flash_status, + &status, + sizeof(status)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read flash status\n", + __func__); + return retval; + } + + dev_dbg(LOGDEV, "%s: Flash status = 0x%02x\n", + __func__, (unsigned int)status); + fwu->in_bl_mode = status >> 7; + + if (fwu->bl_version == BL_V5) + fwu->flash_status = (status >> 4) & MASK_3BIT; + else if (fwu->bl_version == BL_V6) + fwu->flash_status = status & MASK_3BIT; + else + fwu->flash_status = status & MASK_5BIT; + + if (fwu->bl_version <= BL_V6) { + unsigned char command; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->off.flash_cmd, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read flash command\n", + __func__); + return retval; + } + + if (fwu->bl_version == BL_V5) + fwu->command = command & MASK_4BIT; + else if (fwu->bl_version == BL_V6) + fwu->command = command & MASK_6BIT; + else + fwu->command = command; + + } else { + struct f34_v7_data_1_5 data15; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->off.partition_id, + (unsigned char *)&data15, + sizeof(data15)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read data15\n", + __func__); + return retval; + } + + if (fwu->flash_status == BAD_PARTITION_TABLE) + fwu->flash_status = 0x00; + + partition = data15.partition_id; + fwu->command = data15.command; + } + + if (fwu->flash_status != 0x00) { + dev_err(LOGDEV, + "%s: Flash status = %d, part_id = %d, command = 0x%02x\n", + __func__, fwu->flash_status, partition, fwu->command); + } + + return 0; +} + +static int fwu_read_interrupt_status(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char interrupt_status; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_data_base_addr + 1, + &interrupt_status, + sizeof(interrupt_status)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read intr status\n", + __func__); + return retval; + } + dev_dbg(LOGDEV, + "%s: F01 interrupt status = 0x%02x\n", + __func__, interrupt_status); + return interrupt_status; +} + +static irqreturn_t fwu_irq(int irq, void *data) +{ + struct synaptics_rmi4_fwu_handle *fwu_ptr = data; + up(&fwu_ptr->irq_sema); + return IRQ_HANDLED; +} + +static void fwu_irq_enable( + struct synaptics_rmi4_fwu_handle *fwu, bool enable) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + int retval; + + if (enable) { + if (fwu->irq_enabled) { + dev_warn(LOGDEV, + "%s: irq already enabled\n", __func__); + return; + } + + fwu_read_interrupt_status(fwu); + retval = request_irq(rmi4_data->irq, fwu_irq, + IRQF_TRIGGER_FALLING, "fwu", fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to request irq: %d\n", + __func__, retval); + } + + dev_dbg(LOGDEV, + "enabling F34 IRQ handler\n"); + fwu->irq_enabled = true; + } else { + if (!fwu->irq_enabled) { + dev_warn(LOGDEV, + "%s: irq already disabled\n", __func__); + return; + } + + dev_dbg(LOGDEV, + "disabling F34 IRQ handler\n"); + disable_irq(rmi4_data->irq); + free_irq(rmi4_data->irq, fwu); + fwu->irq_enabled = false; + } + sema_clear(&fwu->irq_sema); +} + +static int fwu_wait_for_idle( + struct synaptics_rmi4_fwu_handle *fwu, int timeout_ms) +{ + int retval; + + retval = down_timeout(&fwu->irq_sema, msecs_to_jiffies(timeout_ms)); + if (retval) { + retval = -ETIMEDOUT; + dev_err(LOGDEV, + "%s: timed out waiting for cmd to complete\n", + __func__); + } + + retval = fwu_read_interrupt_status(fwu); + fwu_read_flash_status(fwu); + if ((fwu->command == CMD_IDLE) && (fwu->flash_status == 0x00)) + return 0; + + return retval; +} + +static void fwu_reset_device(struct synaptics_rmi4_fwu_handle *fwu) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + rmi4_data->reset_device(rmi4_data); + if (fwu->irq_enabled) { + /* F34 irq handler will override default reset handler */ + /* and since ISR is triggered on falling edge, need to */ + /* wait for idle twice */ + fwu_wait_for_idle(fwu, ENABLE_WAIT_MS); + fwu_wait_for_idle(fwu, ENABLE_WAIT_MS); + } +} + +static int fwu_write_f34_v7_command_single_transaction( + struct synaptics_rmi4_fwu_handle *fwu, unsigned char cmd) +{ + int retval; + unsigned char base; + struct f34_v7_data_1_5 data_1_5; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + memset(data_1_5.data, 0x00, sizeof(data_1_5.data)); + + switch (cmd) { + case CMD_ERASE_ALL: + data_1_5.partition_id = CORE_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE_AP; + break; + case CMD_ERASE_UI_FIRMWARE: + data_1_5.partition_id = CORE_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_BL_CONFIG: + data_1_5.partition_id = GLOBAL_PARAMETERS_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_UI_CONFIG: + data_1_5.partition_id = CORE_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_DISP_CONFIG: + data_1_5.partition_id = DISPLAY_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_FLASH_CONFIG: + data_1_5.partition_id = FLASH_CONFIG_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ERASE_GUEST_CODE: + data_1_5.partition_id = GUEST_CODE_PARTITION; + data_1_5.command = CMD_V7_ERASE; + break; + case CMD_ENABLE_FLASH_PROG: + data_1_5.partition_id = BOOTLOADER_PARTITION; + data_1_5.command = CMD_V7_ENTER_BL; + break; + }; + + data_1_5.payload_0 = fwu->bootloader_id[0]; + data_1_5.payload_1 = fwu->bootloader_id[1]; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.partition_id, + data_1_5.data, + sizeof(data_1_5.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write single transaction command\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_v7_command( + struct synaptics_rmi4_fwu_handle *fwu, unsigned char cmd) +{ + int retval; + unsigned char base; + unsigned char command; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_WRITE_FW: + case CMD_WRITE_CONFIG: + case CMD_WRITE_GUEST_CODE: + command = CMD_V7_WRITE; + break; + case CMD_READ_CONFIG: + command = CMD_V7_READ; + break; + case CMD_ERASE_ALL: + command = CMD_V7_ERASE_AP; + break; + case CMD_ERASE_UI_FIRMWARE: + case CMD_ERASE_BL_CONFIG: + case CMD_ERASE_UI_CONFIG: + case CMD_ERASE_DISP_CONFIG: + case CMD_ERASE_FLASH_CONFIG: + case CMD_ERASE_GUEST_CODE: + command = CMD_V7_ERASE; + break; + case CMD_ENABLE_FLASH_PROG: + command = CMD_V7_ENTER_BL; + break; + default: + dev_err(LOGDEV, + "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + }; + + fwu->command = command; + + switch (cmd) { + case CMD_ERASE_ALL: + case CMD_ERASE_UI_FIRMWARE: + case CMD_ERASE_BL_CONFIG: + case CMD_ERASE_UI_CONFIG: + case CMD_ERASE_DISP_CONFIG: + case CMD_ERASE_FLASH_CONFIG: + case CMD_ERASE_GUEST_CODE: + case CMD_ENABLE_FLASH_PROG: + retval = fwu_write_f34_v7_command_single_transaction(fwu, cmd); + if (retval < 0) + return retval; + else + return 0; + default: + break; + }; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.flash_cmd, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write flash command\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_v5v6_command( + struct synaptics_rmi4_fwu_handle *fwu, unsigned char cmd) +{ + int retval; + unsigned char base; + unsigned char command; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_IDLE: + command = CMD_V5V6_IDLE; + break; + case CMD_WRITE_FW: + command = CMD_V5V6_WRITE_FW; + break; + case CMD_WRITE_CONFIG: + command = CMD_V5V6_WRITE_CONFIG; + break; + case CMD_WRITE_LOCKDOWN: + command = CMD_V5V6_WRITE_LOCKDOWN; + break; + case CMD_WRITE_GUEST_CODE: + command = CMD_V5V6_WRITE_GUEST_CODE; + break; + case CMD_READ_CONFIG: + command = CMD_V5V6_READ_CONFIG; + break; + case CMD_ERASE_ALL: + command = CMD_V5V6_ERASE_ALL; + break; + case CMD_ERASE_UI_CONFIG: + command = CMD_V5V6_ERASE_UI_CONFIG; + break; + case CMD_ERASE_DISP_CONFIG: + command = CMD_V5V6_ERASE_DISP_CONFIG; + break; + case CMD_ERASE_GUEST_CODE: + command = CMD_V5V6_ERASE_GUEST_CODE; + break; + case CMD_ENABLE_FLASH_PROG: + command = CMD_V5V6_ENABLE_FLASH_PROG; + break; + default: + dev_err(LOGDEV, + "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + } + + switch (cmd) { + case CMD_ERASE_ALL: + case CMD_ERASE_UI_CONFIG: + case CMD_ERASE_DISP_CONFIG: + case CMD_ERASE_GUEST_CODE: + case CMD_ENABLE_FLASH_PROG: + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.payload, + fwu->bootloader_id, + sizeof(fwu->bootloader_id)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write bootloader ID\n", + __func__); + return retval; + } + break; + default: + break; + }; + + fwu->command = command; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.flash_cmd, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write command 0x%02x\n", + __func__, command); + return retval; + } + + return 0; +} + +static int fwu_write_f34_command( + struct synaptics_rmi4_fwu_handle *fwu, unsigned char cmd) +{ + int retval; + + if (fwu->bl_version >= BL_V7) + retval = fwu_write_f34_v7_command(fwu, cmd); + else + retval = fwu_write_f34_v5v6_command(fwu, cmd); + + return retval; +} + +static int fwu_write_f34_v7_partition_id( + struct synaptics_rmi4_fwu_handle *fwu, unsigned char cmd) +{ + int retval; + unsigned char base; + unsigned char partition; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + switch (cmd) { + case CMD_WRITE_FW: + partition = CORE_CODE_PARTITION; + break; + case CMD_WRITE_CONFIG: + case CMD_READ_CONFIG: + if (fwu->config_area == UI_CONFIG_AREA) + partition = CORE_CONFIG_PARTITION; + else if (fwu->config_area == DP_CONFIG_AREA) + partition = DISPLAY_CONFIG_PARTITION; + else if (fwu->config_area == PM_CONFIG_AREA) + partition = GUEST_SERIALIZATION_PARTITION; + else if (fwu->config_area == BL_CONFIG_AREA) + partition = GLOBAL_PARAMETERS_PARTITION; + else if (fwu->config_area == FLASH_CONFIG_AREA) + partition = FLASH_CONFIG_PARTITION; + break; + case CMD_WRITE_GUEST_CODE: + partition = GUEST_CODE_PARTITION; + break; + case CMD_ERASE_ALL: + partition = CORE_CODE_PARTITION; + break; + case CMD_ERASE_BL_CONFIG: + partition = GLOBAL_PARAMETERS_PARTITION; + break; + case CMD_ERASE_UI_CONFIG: + partition = CORE_CONFIG_PARTITION; + break; + case CMD_ERASE_DISP_CONFIG: + partition = DISPLAY_CONFIG_PARTITION; + break; + case CMD_ERASE_FLASH_CONFIG: + partition = FLASH_CONFIG_PARTITION; + break; + case CMD_ERASE_GUEST_CODE: + partition = GUEST_CODE_PARTITION; + break; + case CMD_ENABLE_FLASH_PROG: + partition = BOOTLOADER_PARTITION; + break; + default: + dev_err(LOGDEV, + "%s: Invalid command 0x%02x\n", + __func__, cmd); + return -EINVAL; + }; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.partition_id, + &partition, + sizeof(partition)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write partition ID\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_write_f34_partition_id( + struct synaptics_rmi4_fwu_handle *fwu, unsigned char cmd) +{ + int retval; + + if (fwu->bl_version >= BL_V7) + retval = fwu_write_f34_v7_partition_id(fwu, cmd); + else + retval = 0; + + return retval; +} + +static int fwu_read_f34_v7_partition_table( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned char *partition_table) +{ + int retval; + unsigned char base; + unsigned char length[2]; + unsigned short block_number = 0; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + fwu->config_area = FLASH_CONFIG_AREA; + + retval = fwu_write_f34_partition_id(fwu, CMD_READ_CONFIG); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.block_number, + (unsigned char *)&block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + length[0] = (unsigned char)(fwu->flash_config_length & MASK_8BIT); + length[1] = (unsigned char)(fwu->flash_config_length >> 8); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.transfer_length, + length, + sizeof(length)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write transfer length\n", + __func__); + return retval; + } + + retval = fwu_write_f34_command(fwu, CMD_READ_CONFIG); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write command\n", + __func__); + return retval; + } + + retval = fwu_wait_for_idle(fwu, WRITE_WAIT_MS); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to wait for idle status\n", + __func__); + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.payload, + partition_table, + fwu->partition_table_bytes); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read block data\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_read_f34_v7_queries( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char ii; + unsigned char base; + unsigned char index; + unsigned char offset; + unsigned char *ptable; + struct f34_v7_query_0 query_0; + struct f34_v7_query_1_7 query_1_7; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.query_base_addr; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base, + query_0.data, + sizeof(query_0.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read query 0\n", + __func__); + return retval; + } + + offset = query_0.subpacket_1_size + 1; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + offset, + query_1_7.data, + sizeof(query_1_7.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read queries 1 to 7\n", + __func__); + return retval; + } + + fwu->bootloader_id[0] = query_1_7.bl_minor_revision; + fwu->bootloader_id[1] = query_1_7.bl_major_revision; + dev_dbg(LOGDEV, "%s: BL ver %d.%d\n", __func__, + fwu->bootloader_id[1], fwu->bootloader_id[0]); + + if (fwu->bootloader_id[1] == BL_V8) + fwu->bl_version = BL_V8; + else { + int i; + unsigned char *s = NULL; + struct synaptics_rmi4_device_info *rmi = &(rmi4_data->rmi4_mod_info); + + dev_info(LOGDEV, + "%s: BL ver.7 detected; need to update product ID\n", __func__); + /* need to add 2 extra characters, thus make sure they fit */ + for (i = 0; i < SYNAPTICS_RMI4_PRODUCT_ID_SIZE-2; i++) + if (rmi->product_id_string[i] == 0) { + if (!(rmi->product_id_string[i-1] == '7' && + rmi->product_id_string[i-2] == 'v')) + s = &rmi->product_id_string[i]; + break; + } + + if (s) { + /* adding suffix 'v7' to the original product id string */ + *s++ = 'v'; *s++ = '7'; *s = 0; + dev_info(LOGDEV, "%s: NOTE new product ID: '%s;\n", + __func__, rmi->product_id_string); + } + } + + fwu->block_size = query_1_7.block_size_15_8 << 8 | + query_1_7.block_size_7_0; + + fwu->flash_config_length = query_1_7.flash_config_length_15_8 << 8 | + query_1_7.flash_config_length_7_0; + + fwu->payload_length = query_1_7.payload_length_15_8 << 8 | + query_1_7.payload_length_7_0; + + fwu->off.flash_status = V7_FLASH_STATUS_OFFSET; + fwu->off.partition_id = V7_PARTITION_ID_OFFSET; + fwu->off.block_number = V7_BLOCK_NUMBER_OFFSET; + fwu->off.transfer_length = V7_TRANSFER_LENGTH_OFFSET; + fwu->off.flash_cmd = V7_COMMAND_OFFSET; + fwu->off.payload = V7_PAYLOAD_OFFSET; + + fwu->flash_properties.has_disp_config = query_1_7.has_display_config; + fwu->flash_properties.has_pm_config = query_1_7.has_guest_serialization; + fwu->flash_properties.has_bl_config = query_1_7.has_global_parameters; + + fwu->has_guest_code = query_1_7.has_guest_code; + + index = sizeof(query_1_7.data) - V7_PARTITION_SUPPORT_BYTES; + + fwu->partitions = 0; + for (offset = 0; offset < V7_PARTITION_SUPPORT_BYTES; offset++) { + for (ii = 0; ii < 8; ii++) { + if (query_1_7.data[index + offset] & (1 << ii)) + fwu->partitions++; + } + + dev_dbg(LOGDEV, + "%s: Supported partitions: 0x%02x\n", + __func__, query_1_7.data[index + offset]); + } + + fwu->partition_table_bytes = fwu->partitions * 8 + 2; + + ptable = kzalloc(fwu->partition_table_bytes, GFP_KERNEL); + if (!ptable) { + dev_err(LOGDEV, + "%s: Failed to alloc mem for partition table\n", + __func__); + return -ENOMEM; + } + + retval = fwu_read_f34_v7_partition_table(fwu, ptable); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read partition table\n", + __func__); + kfree(ptable); + return retval; + } + + fwu_parse_partition_table(fwu, ptable, &fwu->blkcount, &fwu->phyaddr); + + kfree(ptable); + + return 0; +} + +static int fwu_read_f34_v5v6_queries( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char count; + unsigned char base; + unsigned char buf[10]; + struct f34_v5v6_flash_properties_2 properties_2; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.query_base_addr; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + V5V6_BOOTLOADER_ID_OFFSET, + fwu->bootloader_id, + sizeof(fwu->bootloader_id)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read bootloader ID\n", + __func__); + return retval; + } + + if (fwu->bl_version == BL_V5) { + fwu->off.properties = V5_PROPERTIES_OFFSET; + fwu->off.block_size = V5_BLOCK_SIZE_OFFSET; + fwu->off.block_count = V5_BLOCK_COUNT_OFFSET; + fwu->off.block_number = V5_BLOCK_NUMBER_OFFSET; + fwu->off.payload = V5_BLOCK_DATA_OFFSET; + } else if (fwu->bl_version == BL_V6) { + fwu->off.properties = V6_PROPERTIES_OFFSET; + fwu->off.properties_2 = V6_PROPERTIES_2_OFFSET; + fwu->off.block_size = V6_BLOCK_SIZE_OFFSET; + fwu->off.block_count = V6_BLOCK_COUNT_OFFSET; + fwu->off.gc_block_count = V6_GUEST_CODE_BLOCK_COUNT_OFFSET; + fwu->off.block_number = V6_BLOCK_NUMBER_OFFSET; + fwu->off.payload = V6_BLOCK_DATA_OFFSET; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.block_size, + buf, + 2); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read block size info\n", + __func__); + return retval; + } + + batohs(&fwu->block_size, &(buf[0])); + + if (fwu->bl_version == BL_V5) { + fwu->off.flash_cmd = fwu->off.payload + fwu->block_size; + fwu->off.flash_status = fwu->off.flash_cmd; + } else if (fwu->bl_version == BL_V6) { + fwu->off.flash_cmd = V6_FLASH_COMMAND_OFFSET; + fwu->off.flash_status = V6_FLASH_STATUS_OFFSET; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.properties, + fwu->flash_properties.data, + sizeof(fwu->flash_properties.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read flash properties\n", + __func__); + return retval; + } + + count = 4; + + if (fwu->flash_properties.has_pm_config) + count += 2; + + if (fwu->flash_properties.has_bl_config) + count += 2; + + if (fwu->flash_properties.has_disp_config) + count += 2; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.block_count, + buf, + count); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read block count info\n", + __func__); + return retval; + } + + batohs(&fwu->blkcount.ui_firmware, &(buf[0])); + batohs(&fwu->blkcount.ui_config, &(buf[2])); + + count = 4; + + if (fwu->flash_properties.has_pm_config) { + batohs(&fwu->blkcount.pm_config, &(buf[count])); + count += 2; + } + + if (fwu->flash_properties.has_bl_config) { + batohs(&fwu->blkcount.bl_config, &(buf[count])); + count += 2; + } + + if (fwu->flash_properties.has_disp_config) + batohs(&fwu->blkcount.dp_config, &(buf[count])); + + fwu->has_guest_code = false; + + if (fwu->flash_properties.has_query4) { + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.properties_2, + properties_2.data, + sizeof(properties_2.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read flash properties 2\n", + __func__); + return retval; + } + + if (properties_2.has_guest_code) { + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.gc_block_count, + buf, + 2); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read guest code block count\n", + __func__); + return retval; + } + + batohs(&fwu->blkcount.guest_code, &(buf[0])); + fwu->has_guest_code = true; + } + } + + return 0; +} + +static int fwu_read_f34_queries( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + memset(&fwu->blkcount, 0x00, sizeof(fwu->blkcount)); + memset(&fwu->phyaddr, 0x00, sizeof(fwu->phyaddr)); + + /* Strict BL version comparison is correct in this case, */ + /* since PDT scan assigns bl_version to BL_V7 for F34 v.2*/ + if (fwu->bl_version == BL_V7) + retval = fwu_read_f34_v7_queries(fwu); + else + retval = fwu_read_f34_v5v6_queries(fwu); + + dev_info(LOGDEV, + "%s: BL version = %d\n", + __func__, fwu->bl_version); + + return retval; +} + +static int fwu_write_f34_v7_blocks( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned char *block_ptr, + unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char base; + unsigned char length[2]; + unsigned short transfer; + unsigned short max_transfer; + unsigned short remaining = block_cnt; + unsigned short block_number = 0; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + retval = fwu_write_f34_partition_id(fwu, command); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.block_number, + (unsigned char *)&block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + if (fwu->payload_length > (PAGE_SIZE / fwu->block_size)) + max_transfer = PAGE_SIZE / fwu->block_size; + else + max_transfer = fwu->payload_length; + + dev_dbg(LOGDEV, "%s: update %s (%3d / %3d)\n", + __func__, + command == CMD_WRITE_CONFIG ? + "config" : "firmware", + block_cnt - remaining, block_cnt); + + do { + if (remaining / max_transfer) + transfer = max_transfer; + else + transfer = remaining; + + length[0] = (unsigned char)(transfer & MASK_8BIT); + length[1] = (unsigned char)(transfer >> 8); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.transfer_length, + length, + sizeof(length)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write transfer length (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = fwu_write_f34_command(fwu, command); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write command (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.payload, + block_ptr, + transfer * fwu->block_size); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block data (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = fwu_wait_for_idle(fwu, WRITE_WAIT_MS); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to wait for idle status (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + block_ptr += (transfer * fwu->block_size); + remaining -= transfer; + dev_dbg(LOGDEV, "%s: update %s (%3d / %3d)\n", + __func__, + command == CMD_WRITE_CONFIG ? + "config" : "firmware", + block_cnt - remaining, block_cnt); + } while (remaining); + + return 0; +} + +static int fwu_write_f34_v5v6_blocks( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned char *block_ptr, + unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char base; + unsigned char block_number[] = {0, 0}; + unsigned short blk; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + unsigned int progress = (command == CMD_WRITE_CONFIG) ? + 10 : 100; + + base = fwu->f34_fd.data_base_addr; + + block_number[1] |= (fwu->config_area << 5); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.block_number, + block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + for (blk = 0; blk < block_cnt; blk++) { + if (blk % progress == 0) + dev_dbg(LOGDEV, + "%s: update %s %3d / %3d\n", + __func__, + command == CMD_WRITE_CONFIG ? + "config" : "firmware", + blk, + block_cnt); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.payload, + block_ptr, + fwu->block_size); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block data (block %d)\n", + __func__, blk); + return retval; + } + + retval = fwu_write_f34_command(fwu, command); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write command for block %d\n", + __func__, blk); + return retval; + } + + retval = fwu_wait_for_idle(fwu, WRITE_WAIT_MS); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to wait for idle status (block %d)\n", + __func__, blk); + return retval; + } + + block_ptr += fwu->block_size; + } + + return 0; +} + +static int fwu_write_f34_blocks( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned char *block_ptr, + unsigned short block_cnt, + unsigned char cmd) +{ + int retval; + + if (fwu->bl_version >= BL_V7) + retval = fwu_write_f34_v7_blocks(fwu, block_ptr, block_cnt, cmd); + else + retval = fwu_write_f34_v5v6_blocks(fwu, block_ptr, block_cnt, cmd); + + return retval; +} + +static int fwu_read_f34_v7_blocks( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char base; + unsigned char length[2]; + unsigned short transfer; + unsigned short max_transfer; + unsigned short remaining = block_cnt; + unsigned short block_number = 0; + unsigned short index = 0; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + retval = fwu_write_f34_partition_id(fwu, command); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.block_number, + (unsigned char *)&block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + if (fwu->payload_length > (PAGE_SIZE / fwu->block_size)) + max_transfer = PAGE_SIZE / fwu->block_size; + else + max_transfer = fwu->payload_length; + + do { + if (remaining / max_transfer) + transfer = max_transfer; + else + transfer = remaining; + + length[0] = (unsigned char)(transfer & MASK_8BIT); + length[1] = (unsigned char)(transfer >> 8); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.transfer_length, + length, + sizeof(length)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write transfer length (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = fwu_write_f34_command(fwu, command); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write command (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = fwu_wait_for_idle(fwu, WRITE_WAIT_MS); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to wait for idle status (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.payload, + &fwu->read_config_buf[index], + transfer * fwu->block_size); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read block data (%d blocks remaining)\n", + __func__, remaining); + return retval; + } + + index += (transfer * fwu->block_size); + remaining -= transfer; + } while (remaining); + + return 0; +} + +static int fwu_read_f34_v5v6_blocks( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned short block_cnt, + unsigned char command) +{ + int retval; + unsigned char base; + unsigned char block_number[] = {0, 0}; + unsigned short blk; + unsigned short index = 0; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f34_fd.data_base_addr; + + block_number[1] |= (fwu->config_area << 5); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + fwu->off.block_number, + block_number, + sizeof(block_number)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write block number\n", + __func__); + return retval; + } + + for (blk = 0; blk < block_cnt; blk++) { + retval = fwu_write_f34_command(fwu, command); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write read config command\n", + __func__); + return retval; + } + + retval = fwu_wait_for_idle(fwu, WRITE_WAIT_MS); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to wait for idle status\n", + __func__); + return retval; + } + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + fwu->off.payload, + &fwu->read_config_buf[index], + fwu->block_size); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read block data (block %d)\n", + __func__, blk); + return retval; + } + + index += fwu->block_size; + } + + return 0; +} + +static int fwu_read_f34_blocks( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned short block_cnt, + unsigned char cmd) +{ + int retval; + + if (fwu->bl_version >= BL_V7) + retval = fwu_read_f34_v7_blocks(fwu, block_cnt, cmd); + else + retval = fwu_read_f34_v5v6_blocks(fwu, block_cnt, cmd); + + return retval; +} + +static void fwu_get_device_firmware_id( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned int *dev_id) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + struct synaptics_rmi4_device_info *rmi = + &rmi4_data->rmi4_mod_info; + unsigned char *buf = (unsigned char *)dev_id; + buf[0] = 0; + buf[1] = rmi->build_id[0]; + buf[2] = rmi->build_id[1]; + buf[3] = rmi->build_id[2]; +} + +static int fwu_get_image_firmware_id( + struct synaptics_rmi4_fwu_handle *fwu, + unsigned int *fw_id) +{ + if (fwu->img.contains_firmware_id) { + *fw_id = fwu->img.firmware_id; + return 0; + } + + dev_err(LOGDEV, "%s: no build ID in image\n", __func__); + return -EINVAL; +} + +static int fwu_get_device_config_id(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char config_id_size; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + if (fwu->bl_version >= BL_V7) + config_id_size = V7_CONFIG_ID_SIZE; + else + config_id_size = V5V6_CONFIG_ID_SIZE; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.ctrl_base_addr, + fwu->config_id, + config_id_size); + if (retval < 0) + return retval; + + return 0; +} + +static enum flash_area fwu_go_nogo(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + enum flash_area flash_area = NONE; + unsigned char ii; + unsigned char config_id_size; + unsigned int device_fw_id; + unsigned int image_fw_id; + + if (fwu->force_update) { + flash_area = UI_FIRMWARE; + goto exit; + } + + /* Update both UI and config if device is in bootloader mode */ + if (fwu->in_bl_mode) { + flash_area = UI_FIRMWARE; + dev_dbg(LOGDEV, "%s: BL mode shortcut\n", __func__); + goto exit; + } + + /* Get device firmware ID */ + fwu_get_device_firmware_id(fwu, &device_fw_id); + dev_info(LOGDEV, + "%s: Device firmware ID = %d\n", + __func__, device_fw_id); + + /* Get image firmware ID */ + retval = fwu_get_image_firmware_id(fwu, &image_fw_id); + if (retval < 0) { + flash_area = NONE; + goto exit; + } + dev_info(LOGDEV, + "%s: Image firmware ID = %d\n", + __func__, image_fw_id); + + if (image_fw_id != device_fw_id) { + dev_info(LOGDEV, + "%s: Image firmware ID differs from device firmware ID 0x%x 0x%x\n", + __func__, image_fw_id, device_fw_id); + flash_area = UI_FIRMWARE; + goto exit; + } + + /* Get device config ID */ + retval = fwu_get_device_config_id(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read device config ID\n", + __func__); + flash_area = NONE; + goto exit; + } + + if (fwu->bl_version >= BL_V7) + config_id_size = V7_CONFIG_ID_SIZE; + else + config_id_size = V5V6_CONFIG_ID_SIZE; + + for (ii = 0; ii < config_id_size; ii++) { + if (fwu->img.ui_config.data[ii] > fwu->config_id[ii]) { + flash_area = UI_CONFIG; + goto exit; + } else if (fwu->img.ui_config.data[ii] < fwu->config_id[ii]) { + flash_area = NONE; + goto exit; + } + } + + flash_area = NONE; + +exit: + if (flash_area == NONE) { + dev_info(LOGDEV, + "%s: No need to do reflash\n", + __func__); + } else { + dev_info(LOGDEV, + "%s: Updating %s\n", + __func__, + flash_area == UI_FIRMWARE ? + "UI firmware and config" : + "UI config only"); + } + + return flash_area; +} + +static int fwu_scan_pdt(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char ii; + unsigned char intr_count = 0; + unsigned char intr_off; + unsigned char intr_src; + unsigned short addr; + bool f01found = false; + bool f34found = false; + bool f35found = false; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + fwu->in_ub_mode = false; + + for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) { + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + return retval; + + if (rmi_fd.fn_number) { + dev_dbg(LOGDEV, + "%s: Found F%02x\n", + __func__, rmi_fd.fn_number); + switch (rmi_fd.fn_number) { + case SYNAPTICS_RMI4_F01: + f01found = true; + + rmi4_data->f01_query_base_addr = + rmi_fd.query_base_addr; + rmi4_data->f01_ctrl_base_addr = + rmi_fd.ctrl_base_addr; + rmi4_data->f01_data_base_addr = + rmi_fd.data_base_addr; + rmi4_data->f01_cmd_base_addr = + rmi_fd.cmd_base_addr; + break; + case SYNAPTICS_RMI4_F34: + f34found = true; + fwu->f34_fd.query_base_addr = + rmi_fd.query_base_addr; + fwu->f34_fd.ctrl_base_addr = + rmi_fd.ctrl_base_addr; + fwu->f34_fd.data_base_addr = + rmi_fd.data_base_addr; + + switch (rmi_fd.fn_version) { + case F34_V0: + fwu->bl_version = BL_V5; + break; + case F34_V1: + fwu->bl_version = BL_V6; + break; + case F34_V2: + fwu->bl_version = BL_V7; + break; + default: + dev_err(LOGDEV, + "%s: Unrecognized F34 version\n", + __func__); + return -EINVAL; + } + + dev_dbg(LOGDEV, + "%s: F34 version %d\n", + __func__, rmi_fd.fn_version); + + fwu->intr_mask = 0; + intr_src = rmi_fd.intr_src_count; + intr_off = intr_count % 8; + for (ii = intr_off; + ii < (intr_src + intr_off); + ii++) { + fwu->intr_mask |= 1 << ii; + } + break; + case SYNAPTICS_RMI4_F35: + f35found = true; + fwu->f35_fd.query_base_addr = + rmi_fd.query_base_addr; + fwu->f35_fd.ctrl_base_addr = + rmi_fd.ctrl_base_addr; + fwu->f35_fd.data_base_addr = + rmi_fd.data_base_addr; + break; + } + } else { + break; + } + + intr_count += rmi_fd.intr_src_count; + } + + if (!f01found || !f34found) { + dev_err(LOGDEV, + "%s: Failed to find both F01 and F34\n", + __func__); + if (!f35found) { + dev_err(LOGDEV, + "%s: Failed to find F35\n", + __func__); + return -EINVAL; + } else { + fwu->in_ub_mode = true; + dev_dbg(LOGDEV, + "%s: In microbootloader mode\n", + __func__); + fwu_recovery_check_status(fwu); + return 0; + } + } + + return 0; +} + +int fwu_check_intr_en(struct synaptics_rmi4_data *rmi4_data, + unsigned char *drv_intr_en, size_t size) +{ + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int i; + unsigned char intr_en[MAX_INTR_REGISTERS]; + int retval = 0; + unsigned char addr = rmi4_data->f01_ctrl_base_addr + 1; + + retval = synaptics_rmi4_reg_read(rmi4_data, + addr, + intr_en, + size); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read interrupt enable register\n", + __func__); + return retval; + } + /* ensure interrupt enable mask expected by driver is subset of */ + /* touch IC interrupt enable register */ + for (i = 0; i < size; ++i) + if (drv_intr_en[i] != (drv_intr_en[i] & intr_en[i])) { + retval = 1; + dev_err(LOGDEV, + "%s: Interrupt enable mismatch:" + " drv[%d] = 0x%02x, fw[%d] = 0x%02x\n", + __func__, i, drv_intr_en[i], i, intr_en[i]); + } + + return retval; +} + +static int fwu_enter_flash_prog(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + struct f01_device_control f01_device_control; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + retval = fwu_read_flash_status(fwu); + if (retval < 0) + return retval; + + if (fwu->in_bl_mode) { + dev_dbg(LOGDEV, "%s: BL mode shortcut\n", __func__); + return 0; + } + + retval = fwu_write_f34_command(fwu, CMD_ENABLE_FLASH_PROG); + if (retval < 0) + goto out; + + retval = fwu_wait_for_idle(fwu, ENABLE_WAIT_MS); + if (retval < 0) + goto out; + + if (!fwu->in_bl_mode) { + dev_err(LOGDEV, + "%s: BL mode not entered\n", + __func__); + retval = -EINVAL; + goto out; + } + + retval = fwu_scan_pdt(fwu); + if (retval < 0) + goto out; + + fwu_check_intr_en(rmi4_data, &fwu->intr_mask, sizeof(fwu->intr_mask)); + + retval = fwu_read_f34_queries(fwu); + if (retval < 0) + goto out; + + retval = synaptics_rmi4_reg_read(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + f01_device_control.data, + sizeof(f01_device_control.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read F01 device control\n", + __func__); + goto out; + } + + f01_device_control.nosleep = true; + f01_device_control.sleep_mode = SLEEP_MODE_NORMAL; + + retval = synaptics_rmi4_reg_write(rmi4_data, + rmi4_data->f01_ctrl_base_addr, + f01_device_control.data, + sizeof(f01_device_control.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write F01 device control\n", + __func__); + goto out; + } + + msleep(ENTER_FLASH_PROG_WAIT_MS); +out: + return retval; +} + +static int fwu_check_ui_firmware_size( + struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short block_count; + + block_count = fwu->img.ui_firmware.size / fwu->block_size; + + if (block_count != fwu->blkcount.ui_firmware) { + dev_err(LOGDEV, + "%s: UI firmware size mismatch: %d (expected %d)\n", + __func__, block_count, fwu->blkcount.ui_firmware); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_ui_configuration_size( + struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short block_count; + + block_count = fwu->img.ui_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.ui_config) { + dev_err(LOGDEV, + "%s: UI config size mismatch: %d (expected %d)\n", + __func__, block_count, fwu->blkcount.ui_config); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_dp_configuration_size( + struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short block_count; + + block_count = fwu->img.dp_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.dp_config) { + dev_err(LOGDEV, + "%s: Display configuration size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_bl_configuration_size( + struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short block_count; + + block_count = fwu->img.bl_config.size / fwu->block_size; + + if (block_count != fwu->blkcount.bl_config) { + dev_err(LOGDEV, + "%s: Bootloader configuration size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_check_guest_code_size( + struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short block_count; + + block_count = fwu->img.guest_code.size / fwu->block_size; + if (block_count != fwu->blkcount.guest_code) { + dev_err(LOGDEV, + "%s: Guest code size mismatch\n", + __func__); + return -EINVAL; + } + + return 0; +} + +static int fwu_write_firmware( + struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short firmware_block_count; + + firmware_block_count = fwu->img.ui_firmware.size / fwu->block_size; + + return fwu_write_f34_blocks(fwu, (unsigned char *)fwu->img.ui_firmware.data, + firmware_block_count, CMD_WRITE_FW); +} + +static int fwu_erase_configuration( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + retval = fwu_write_f34_command(fwu, CMD_ERASE_UI_CONFIG); + if (retval < 0) + return retval; + break; + case DP_CONFIG_AREA: + retval = fwu_write_f34_command(fwu, CMD_ERASE_DISP_CONFIG); + if (retval < 0) + return retval; + break; + case BL_CONFIG_AREA: + retval = fwu_write_f34_command(fwu, CMD_ERASE_BL_CONFIG); + if (retval < 0) + return retval; + break; + } + + dev_dbg(LOGDEV, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(fwu, ERASE_WAIT_MS); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Idle status detected\n", + __func__); + + return retval; +} + +static int fwu_erase_guest_code( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + retval = fwu_write_f34_command(fwu, CMD_ERASE_GUEST_CODE); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(fwu, ERASE_WAIT_MS); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Idle status detected\n", + __func__); + + return 0; +} + +static int fwu_erase_all(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + if (fwu->bl_version == BL_V7) { + retval = fwu_write_f34_command(fwu, CMD_ERASE_UI_FIRMWARE); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Erase command written\n", + __func__); + + retval = fwu_wait_for_idle(fwu, ERASE_WAIT_MS); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Idle status detected\n", + __func__); + + fwu->config_area = UI_CONFIG_AREA; + retval = fwu_erase_configuration(fwu); + if (retval < 0) + return retval; + } else { + retval = fwu_write_f34_command(fwu, CMD_ERASE_ALL); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Erase all command written\n", + __func__); + + retval = fwu_wait_for_idle(fwu, ERASE_WAIT_MS); + if (!(fwu->bl_version == BL_V8 && + fwu->flash_status == BAD_PARTITION_TABLE)) { + if (retval < 0) + return retval; + } + + dev_dbg(LOGDEV, + "%s: Idle status detected\n", + __func__); + + if (fwu->bl_version == BL_V8) + return 0; + } + + if (fwu->flash_properties.has_disp_config) { + fwu->config_area = DP_CONFIG_AREA; + retval = fwu_erase_configuration(fwu); + if (retval < 0) + return retval; + } + + if (fwu->new_partition_table && fwu->has_guest_code) { + retval = fwu_erase_guest_code(fwu); + if (retval < 0) + return retval; + } + + return 0; +} + +static int fwu_write_configuration(struct synaptics_rmi4_fwu_handle *fwu) +{ + return fwu_write_f34_blocks(fwu, (unsigned char *)fwu->config_data, + fwu->config_block_count, CMD_WRITE_CONFIG); +} + +static int fwu_write_ui_configuration(struct synaptics_rmi4_fwu_handle *fwu) +{ + fwu->config_area = UI_CONFIG_AREA; + fwu->config_data = fwu->img.ui_config.data; + fwu->config_size = fwu->img.ui_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + return fwu_write_configuration(fwu); +} + +static int fwu_write_dp_configuration(struct synaptics_rmi4_fwu_handle *fwu) +{ + fwu->config_area = DP_CONFIG_AREA; + fwu->config_data = fwu->img.dp_config.data; + fwu->config_size = fwu->img.dp_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + return fwu_write_configuration(fwu); +} + +static int fwu_write_flash_configuration(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + fwu->config_area = FLASH_CONFIG_AREA; + fwu->config_data = fwu->img.fl_config.data; + fwu->config_size = fwu->img.fl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + if (fwu->config_block_count != fwu->blkcount.fl_config) { + dev_err(LOGDEV, + "%s: Flash configuration size mismatch\n", + __func__); + return -EINVAL; + } + + retval = fwu_write_f34_command(fwu, CMD_ERASE_FLASH_CONFIG); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Erase flash configuration command written\n", + __func__); + + retval = fwu_wait_for_idle(fwu, ERASE_WAIT_MS); + if (retval < 0) + return retval; + + dev_dbg(LOGDEV, + "%s: Idle status detected\n", + __func__); + + retval = fwu_write_configuration(fwu); + if (retval < 0) + return retval; + + fwu_reset_device(fwu); + + return 0; +} + +static int fwu_write_guest_code(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned short guest_code_block_count; + + guest_code_block_count = fwu->img.guest_code.size / fwu->block_size; + + retval = fwu_write_f34_blocks(fwu, (unsigned char *)fwu->img.guest_code.data, + guest_code_block_count, CMD_WRITE_GUEST_CODE); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_write_lockdown(struct synaptics_rmi4_fwu_handle *fwu) +{ + unsigned short lockdown_block_count; + + lockdown_block_count = fwu->img.lockdown.size / fwu->block_size; + + return fwu_write_f34_blocks(fwu, (unsigned char *)fwu->img.lockdown.data, + lockdown_block_count, CMD_WRITE_LOCKDOWN); +} + +static int fwu_write_partition_table_v8(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + fwu->config_area = FLASH_CONFIG_AREA; + fwu->config_data = fwu->img.fl_config.data; + fwu->config_size = fwu->img.fl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + if (fwu->config_block_count != fwu->blkcount.fl_config) { + dev_err(LOGDEV, + "%s: Flash configuration size mismatch\n", + __func__); + return -EINVAL; + } + + retval = fwu_write_configuration(fwu); + if (retval < 0) + return retval; + + fwu_reset_device(fwu); + + return 0; +} + +static int fwu_write_partition_table_v7(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned short block_count; + + block_count = fwu->blkcount.bl_config; + fwu->config_area = BL_CONFIG_AREA; + fwu->config_size = fwu->block_size * block_count; + + retval = fwu_allocate_read_config_buf(fwu, fwu->config_size); + if (retval < 0) + return retval; + + retval = fwu_read_f34_blocks(fwu, block_count, CMD_READ_CONFIG); + if (retval < 0) + return retval; + + retval = fwu_erase_configuration(fwu); + if (retval < 0) + return retval; + + retval = fwu_write_flash_configuration(fwu); + if (retval < 0) + return retval; + + fwu->config_area = BL_CONFIG_AREA; + fwu->config_data = fwu->read_config_buf; + fwu->config_size = fwu->img.bl_config.size; + fwu->config_block_count = fwu->config_size / fwu->block_size; + + retval = fwu_write_configuration(fwu); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_do_reflash(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + + if (!fwu->new_partition_table) { + retval = fwu_check_ui_firmware_size(fwu); + if (retval < 0) + return retval; + + retval = fwu_check_ui_configuration_size(fwu); + if (retval < 0) + return retval; + + if (fwu->flash_properties.has_disp_config && + fwu->img.contains_disp_config) { + retval = fwu_check_dp_configuration_size(fwu); + if (retval < 0) + return retval; + } + + if (fwu->has_guest_code && fwu->img.contains_guest_code) { + retval = fwu_check_guest_code_size(fwu); + if (retval < 0) + return retval; + } + } else if (fwu->bl_version == BL_V7) { + retval = fwu_check_bl_configuration_size(fwu); + if (retval < 0) + return retval; + } + + retval = fwu_erase_all(fwu); + if (retval < 0) + return retval; + + if (fwu->bl_version == BL_V7 && fwu->new_partition_table) { + retval = fwu_write_partition_table_v7(fwu); + if (retval < 0) + return retval; + pr_notice("%s: Partition table programmed\n", __func__); + } else if (fwu->bl_version == BL_V8) { + retval = fwu_write_partition_table_v8(fwu); + if (retval < 0) + return retval; + pr_notice("%s: Partition table programmed\n", __func__); + } + + retval = fwu_write_firmware(fwu); + if (retval < 0) + return retval; + pr_notice("%s: Firmware programmed\n", __func__); + + fwu->config_area = UI_CONFIG_AREA; + retval = fwu_write_ui_configuration(fwu); + if (retval < 0) + return retval; + pr_notice("%s: Configuration programmed\n", __func__); + + if (fwu->flash_properties.has_disp_config && + fwu->img.contains_disp_config) { + retval = fwu_write_dp_configuration(fwu); + if (retval < 0) + return retval; + pr_notice("%s: Display configuration programmed\n", __func__); + } + + if (fwu->new_partition_table) { + if (fwu->has_guest_code && fwu->img.contains_guest_code) { + retval = fwu_write_guest_code(fwu); + if (retval < 0) + return retval; + pr_notice("%s: Guest code programmed\n", __func__); + } + } + + return retval; +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static int fwu_do_read_config( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned short block_count; + unsigned short config_area; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + block_count = fwu->blkcount.ui_config; + break; + case DP_CONFIG_AREA: + if (!fwu->flash_properties.has_disp_config) { + dev_err(LOGDEV, + "%s: Display configuration not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.dp_config; + break; + case PM_CONFIG_AREA: + if (!fwu->flash_properties.has_pm_config) { + dev_err(LOGDEV, + "%s: Permanent configuration not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.pm_config; + break; + case BL_CONFIG_AREA: + if (!fwu->flash_properties.has_bl_config) { + dev_err(LOGDEV, + "%s: Bootloader configuration not supported\n", + __func__); + return -EINVAL; + } + block_count = fwu->blkcount.bl_config; + break; + default: + dev_err(LOGDEV, + "%s: Invalid config area\n", + __func__); + return -EINVAL; + } + + if (block_count == 0) { + dev_err(LOGDEV, + "%s: Invalid block count\n", + __func__); + return -EINVAL; + } + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + config_area = fwu->config_area; + + retval = fwu_enter_flash_prog(fwu); + if (retval < 0) + goto exit; + + fwu->config_area = config_area; + + fwu->config_size = fwu->block_size * block_count; + + retval = fwu_allocate_read_config_buf(fwu->config_size); + if (retval < 0) + goto exit; + + retval = fwu_read_f34_blocks(fwu, block_count, CMD_READ_CONFIG); + +exit: + fwu_reset_device(fwu); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + return retval; +} +#endif + +static int fwu_do_lockdown(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + retval = fwu_enter_flash_prog(fwu); + if (retval < 0) + return retval; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.query_base_addr + fwu->off.properties, + fwu->flash_properties.data, + sizeof(fwu->flash_properties.data)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read flash properties\n", + __func__); + return retval; + } + + if (fwu->flash_properties.unlocked == 0) { + dev_info(LOGDEV, + "%s: Device already locked down\n", + __func__); + return 0; + } + + retval = fwu_write_lockdown(fwu); + if (retval < 0) + return retval; + + pr_notice("%s: Lockdown programmed\n", __func__); + + return retval; +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static int fwu_start_write_guest_code(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + retval = fwu_parse_image_info(fwu); + if (retval < 0) + return -EINVAL; + + if (!fwu->has_guest_code) { + dev_err(LOGDEV, + "%s: Guest code not supported\n", + __func__); + return -EINVAL; + } + + if (!fwu->img.contains_guest_code) { + dev_err(LOGDEV, + "%s: No guest code in firmware image\n", + __func__); + return -EINVAL; + } + + rmi4_data->set_state(rmi4_data, STATE_INIT); + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + pr_notice("%s: Start of write guest code process\n", __func__); + + retval = fwu_enter_flash_prog(fwu); + if (retval < 0) + goto exit; + + retval = fwu_check_guest_code_size(fwu); + if (retval < 0) + goto exit; + + retval = fwu_erase_guest_code(fwu + if (retval < 0) + goto exit; + + retval = fwu_write_guest_code(fwu); + if (retval < 0) + goto exit; + + pr_notice("%s: Guest code programmed\n", __func__); + +exit: + fwu_reset_device(fwu); + + pr_notice("%s: End of write guest code process\n", __func__); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + return retval; +} + +static int fwu_start_write_config(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned short config_area; + unsigned int device_fw_id; + unsigned int image_fw_id; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + retval = fwu_parse_image_info(fwu); + if (retval < 0) + return -EINVAL; + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + fwu_get_device_firmware_id(fwu, &device_fw_id); + retval = fwu_get_image_firmware_id(fwu, &image_fw_id); + if (retval < 0) + return retval; + if (device_fw_id != image_fw_id) { + dev_err(LOGDEV, + "%s: Device and image firmware IDs don't match\n", + __func__); + return -EINVAL; + } + retval = fwu_check_ui_configuration_size(fwu); + if (retval < 0) + return retval; + break; + case DP_CONFIG_AREA: + if (!fwu->flash_properties.has_disp_config) { + dev_err(LOGDEV, + "%s: Display configuration not supported\n", + __func__); + return -EINVAL; + } + if (!fwu->img.contains_disp_config) { + dev_err(LOGDEV, + "%s: No display configuration in firmware image\n", + __func__); + return -EINVAL; + } + retval = fwu_check_dp_configuration_size(fwu); + if (retval < 0) + return retval; + break; + default: + dev_err(LOGDEV, + "%s: Configuration not supported\n", + __func__); + return -EINVAL; + } + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + pr_notice("%s: Start of write config process\n", __func__); + + config_area = fwu->config_area; + + retval = fwu_enter_flash_prog(fwu); + if (retval < 0) + goto exit; + + fwu->config_area = config_area; + + retval = fwu_erase_configuration(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to erase config\n", + __func__); + goto exit; + } + + switch (fwu->config_area) { + case UI_CONFIG_AREA: + retval = fwu_write_ui_configuration(fwu); + if (retval < 0) + goto exit; + break; + case DP_CONFIG_AREA: + retval = fwu_write_dp_configuration(fwu); + if (retval < 0) + goto exit; + break; + } + + pr_notice("%s: Config written\n", __func__); + +exit: + switch (fwu->config_area) { + case UI_CONFIG_AREA: + fwu_reset_device(fwu); + break; + case DP_CONFIG_AREA: + fwu_reset_device(fwu); + break; + } + + pr_notice("%s: End of write config process\n", __func__); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + return retval; +} +#endif + +static bool fwu_tdat_image_format(const unsigned char *fw_image) +{ + return fw_image[0] == 0x31; +} + +static void fwu_tdat_config_set(const unsigned char *data, unsigned int size, + const unsigned char **image, unsigned int *image_size) +{ + unsigned short id; + unsigned int length, offset; + + for (offset = 0; offset < size; offset += length+5) { + id = (data[offset+1] << 8) | data[offset]; + length = (data[offset+4] << 8) | data[offset+3]; + if (id == 0x0001) { + *image = &data[offset+5]; + *image_size = length; + } + } +} + +static void fwu_tdat_section_offset( + const unsigned char **image, unsigned int *image_size) +{ + unsigned int offset; + offset = (*image)[0] + 1; + *image_size -= offset; + *image = &(*image)[offset]; +} + +static int fwu_parse_tdat_image(struct synaptics_rmi4_fwu_handle *fwu) +{ + int ii; + unsigned int id; + unsigned int length, offset; + struct image_metadata *img = &fwu->img; + const unsigned char *section, *data = fwu->image; + unsigned int fw_size = fwu->image_size; + img->contains_firmware_id = false; + img->contains_bootloader = false; + img->contains_disp_config = false; + img->contains_guest_code = false; + img->contains_flash_config = false; + img->lockdown.data = NULL; + + + pr_notice("%s: Start TDAT image processing\n", __func__); + + for (ii = 0, offset = 1; offset < fw_size; offset += length+4) { + length = (data[offset+3] << 16) | + (data[offset+2] << 8) | data[offset+1]; + + dev_dbg(LOGDEV, + "Record[%d]: length %u, offset %u\n", + ii++, length, offset); + + if ((offset+length+4) > fw_size) { + dev_err(LOGDEV, + "Data overflow at offset %u (%u)\n", + offset, data[offset]); + return -EINVAL; + } + } + + if (offset != fw_size) { + dev_err(LOGDEV, + "Data is misaligned\n"); + return -EINVAL; + } + + for (offset = 1; offset < fw_size; offset += length+4) { + id = data[offset]; + length = (data[offset+3] << 16) | + (data[offset+2] << 8) | data[offset+1]; + section = &data[offset+4]; + + switch (id) { + case 1: /* config */ + dev_dbg(LOGDEV, + "%s: Config record %d, size %u\n", + __func__, id, length); + + fwu_tdat_config_set(section, length, + &img->ui_config.data, + &img->ui_config.size); + fwu_tdat_section_offset(&img->ui_config.data, + &img->ui_config.size); + break; + + case 2: /* firmware */ + dev_dbg(LOGDEV, + "%s: Firmware record %d, size %u\n", + __func__, + id, + length); + + img->contains_firmware_id = true; + batohui(&fwu->img.firmware_id, + (unsigned char *)§ion[1], + SYNAPTICS_RMI4_BUILD_ID_SIZE); + + dev_dbg(LOGDEV, + "%s: Firmware build ID %x\n", + __func__, + fwu->img.firmware_id); + + img->ui_firmware.data = section; + img->ui_firmware.size = length; + fwu_tdat_section_offset(&img->ui_firmware.data, + &img->ui_firmware.size); + break; + default: + dev_dbg(LOGDEV, + "%s: Don't care section id %d\n", + __func__, + id); + break; + } + } + + dev_dbg(LOGDEV, + "%s: Firwmare size %u, config size %u\n", + __func__, + img->ui_firmware.size, + img->ui_config.size); + return 0; +} + +static int fwu_start_reflash(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval = 0; + enum flash_area flash_area; + const struct firmware *fw_entry = NULL; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + PM_STAY_AWAKE(fwu->flash_wake_src); + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + pr_notice("%s: Start of reflash process\n", __func__); + + rmi4_data->set_state(rmi4_data, STATE_INIT); + fwu_irq_enable(fwu, true); + + if (fwu->image == NULL) { + dev_dbg(LOGDEV, + "%s: Requesting firmware image %s\n", + __func__, fwu->image_name); + + retval = request_firmware(&fw_entry, fwu->image_name, + LOGDEV); + if (retval != 0) { + dev_err(LOGDEV, + "%s: Firmware image %s not available\n", + __func__, fwu->image_name); + retval = -EINVAL; + goto exit; + } + + dev_dbg(LOGDEV, + "%s: Firmware image size = %zd\n", + __func__, fw_entry->size); + + fwu->image = fw_entry->data; + fwu->image_size = fw_entry->size; + } + + if (fwu_tdat_image_format(fwu->image)) + fwu_parse_tdat_image(fwu); + else { + retval = fwu_parse_image_info(fwu); + if (retval < 0) + goto exit; + + if (fwu->bl_version != fwu->img.bl_version) { + dev_err(LOGDEV, + "%s: BL versions mismatch: ic=%d, image=%d\n", + __func__, fwu->bl_version, fwu->img.bl_version); + retval = -EINVAL; + goto exit; + } + + if (!fwu->force_update && fwu->new_partition_table) { + dev_err(LOGDEV, + "%s: Partition table mismatch\n", + __func__); + retval = -EINVAL; + goto exit; + } + } + + retval = fwu_read_flash_status(fwu); + if (retval < 0) + goto exit; + + if (fwu->in_bl_mode) { + sema_clear(&fwu->irq_sema); + dev_info(LOGDEV, + "%s: Device in bootloader mode\n", + __func__); + } + + if (fwu->do_lockdown && (fwu->img.lockdown.data != NULL)) { + switch (fwu->bl_version) { + case BL_V5: + case BL_V6: + retval = fwu_do_lockdown(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to do lockdown\n", + __func__); + } + fwu_reset_device(fwu); + break; + default: + break; + } + } + + flash_area = fwu_go_nogo(fwu); + dev_dbg(LOGDEV, "%s: flash_area = %d\n", __func__, flash_area); + + if (flash_area != NONE) { + retval = fwu_enter_flash_prog(fwu); + if (retval < 0) { + fwu_reset_device(fwu); + goto exit; + } + + } + + rmi4_data->set_state(rmi4_data, STATE_FLASH); + + switch (flash_area) { + case UI_FIRMWARE: + retval = fwu_do_reflash(fwu); + break; + case UI_CONFIG: + retval = fwu_check_ui_configuration_size(fwu); + if (retval < 0) + break; + fwu->config_area = UI_CONFIG_AREA; + retval = fwu_erase_configuration(fwu); + if (retval < 0) + break; + retval = fwu_write_ui_configuration(fwu); + break; + case NONE: + default: + break; + } + + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to do reflash\n", + __func__); + } + +exit: + if (fw_entry) + release_firmware(fw_entry); + + fwu_irq_enable(fwu, false); + rmi4_data->set_state(rmi4_data, STATE_UNKNOWN); + fwu_reset_device(fwu); + pr_notice("%s: End of reflash process\n", __func__); + + /* Rescan PDT after flashing and before register access */ + retval = fwu_scan_pdt(fwu); + if (retval < 0) + dev_err(LOGDEV, "%s: Failed to scan PDT\n", __func__); + + if (!fwu->in_ub_mode) { + retval = fwu_read_f34_queries(fwu); + if (retval < 0) + dev_err(LOGDEV, "%s: Failed to query F34\n", __func__); + } + + fwu_check_intr_en(rmi4_data, &rmi4_data->intr_mask[0], + rmi4_data->num_of_intr_regs); + + retval = fwu_get_device_config_id(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read device config ID\n", + __func__); + } + + memcpy(rmi4_data->rmi4_mod_info.config_id, fwu->config_id, + V5V6_CONFIG_ID_SIZE); + + rmi4_data->ready_state(rmi4_data, false); + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + PM_RELAX(fwu->flash_wake_src); + + return retval; +} + +static int fwu_recovery_check_status(struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char base; + unsigned char status; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f35_fd.data_base_addr; + + retval = synaptics_rmi4_reg_read(rmi4_data, + base + F35_ERROR_CODE_OFFSET, + &status, + 1); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read status\n", + __func__); + return retval; + } + + status = status & MASK_7BIT; + + if (status != 0x00) { + dev_err(LOGDEV, + "%s: Recovery mode status = %d\n", + __func__, status); + return -EINVAL; + } + + return 0; +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static int fwu_recovery_erase_all( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char base; + unsigned char command = CMD_F35_ERASE_ALL; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f35_fd.ctrl_base_addr; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + F35_CHUNK_COMMAND_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to issue erase all command\n", + __func__); + return retval; + } + + msleep(F35_ERASE_ALL_WAIT_MS); + + retval = fwu_recovery_check_status(fwu); + if (retval < 0) + return retval; + + return 0; +} + +static int fwu_recovery_write_chunk( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char base; + unsigned char chunk_number[] = {0, 0}; + unsigned char chunk_spare; + unsigned char chunk_size; + unsigned char buf[F35_CHUNK_SIZE + 1]; + unsigned short chunk; + unsigned short chunk_total; + unsigned short bytes_written = 0; + unsigned char *chunk_ptr = (unsigned char *)fwu->image; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f35_fd.ctrl_base_addr; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + F35_CHUNK_NUM_LSB_OFFSET, + chunk_number, + sizeof(chunk_number)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write chunk number\n", + __func__); + return retval; + } + + buf[sizeof(buf) - 1] = CMD_F35_WRITE_CHUNK; + + chunk_total = fwu->image_size / F35_CHUNK_SIZE; + chunk_spare = fwu->image_size % F35_CHUNK_SIZE; + if (chunk_spare) + chunk_total++; + + for (chunk = 0; chunk < chunk_total; chunk++) { + if (chunk_spare && chunk == chunk_total - 1) + chunk_size = chunk_spare; + else + chunk_size = F35_CHUNK_SIZE; + + memset(buf, 0x00, F35_CHUNK_SIZE); + secure_memcpy(buf, sizeof(buf), chunk_ptr, + fwu->image_size - bytes_written, + chunk_size); + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + F35_CHUNK_DATA_OFFSET, + buf, + sizeof(buf)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write chunk data (chunk %d)\n", + __func__, chunk); + return retval; + } + chunk_ptr += chunk_size; + bytes_written += chunk_size; + } + + retval = fwu_recovery_check_status(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write chunk data\n", + __func__); + return retval; + } + + return 0; +} + +static int fwu_recovery_reset( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + unsigned char base; + unsigned char command = CMD_F35_RESET; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + base = fwu->f35_fd.ctrl_base_addr; + + retval = synaptics_rmi4_reg_write(rmi4_data, + base + F35_CHUNK_COMMAND_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to issue reset command\n", + __func__); + return retval; + } + + msleep(F35_RESET_WAIT_MS); + + return 0; +} + +static int fwu_start_recovery( + struct synaptics_rmi4_fwu_handle *fwu) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(fwu->dev); + + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + pr_notice("%s: Start of recovery process\n", __func__); + + PM_STAY_AWAKE(fwu->flash_wake_src); + + retval = rmi4_data->irq_enable(rmi4_data, false); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to disable interrupt\n", + __func__); + goto exit; + } + + msleep(INT_DISABLE_WAIT_MS); + + fwu_irq_enable(fwu, true); + + retval = fwu_recovery_erase_all(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to do erase all in recovery mode\n", + __func__); + goto exit; + } + + pr_notice("%s: External flash erased\n", __func__); + + retval = fwu_recovery_write_chunk(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write chunk data in recovery mode\n", + __func__); + goto exit; + } + + pr_notice("%s: Chunk data programmed\n", __func__); + + retval = fwu_recovery_reset(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to reset device in recovery mode\n", + __func__); + goto exit; + } + + pr_notice("%s: Recovery mode reset issued\n", __func__); + + + retval = 0; + +exit: + rmi4_data->set_state(rmi4_data, STATE_UNKNOWN); + fwu_reset_device(fwu); + fwu_irq_enable(fwu, false); + PM_RELAX(fwu->flash_wake_src); + + pr_notice("%s: End of recovery process\n", __func__); + + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + + rmi4_data->ready_state(rmi4_data, false); + + return retval; +} +#endif + +static int synaptics_fw_updater_local( + struct synaptics_rmi4_fwu_handle *fwu, + const unsigned char *fw_data) +{ + int retval; + + if (!fwu) + return -ENODEV; + + if (!fwu->initialized) + return -ENODEV; + + if (fwu->in_ub_mode) + return -ENODEV; + + fwu->image = fw_data; + + retval = fwu_start_reflash(fwu); + + fwu->image = NULL; + + return retval; +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static ssize_t fwu_sysfs_show_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + + if (count < fwu->config_size) { + dev_err(LOGDEV, + "%s: Not enough space (%zd bytes) in buffer\n", + __func__, count); + return -EINVAL; + } + + retval = secure_memcpy(buf, count, fwu->read_config_buf, + fwu->read_config_buf_size, fwu->config_size); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to copy config data\n", + __func__); + return retval; + } + + return fwu->config_size; +} + +static ssize_t fwu_sysfs_store_image(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + + retval = secure_memcpy(&fwu->ext_data_source[fwu->data_pos], + fwu->image_size - fwu->data_pos, buf, count, count); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to copy image data\n", + __func__); + return retval; + } + + fwu->data_pos += count; + + return count; +} + +static ssize_t fwu_sysfs_do_recovery_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (!fwu->in_ub_mode) { + dev_err(LOGDEV, + "%s: Not in microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->ext_data_source) + return -EINVAL; + else + fwu->image = fwu->ext_data_source; + + retval = fwu_start_recovery(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to do recovery\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + return retval; +} +#endif + +static int synaptics_dsx_firmware_update(struct device *dev, + const char *fwname) +{ + int retval; + char prefix[SYNAPTICS_RMI4_FILENAME_SIZE] = "synaptics"; + char template[SYNAPTICS_RMI4_FILENAME_SIZE]; + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + struct synaptics_rmi4_device_info *rmi; + + rmi = &(rmi4_data->rmi4_mod_info); + + if (!fwu->force_update) { + if (strncmp(fwname, prefix, + strnlen(prefix, sizeof(prefix)))) { + dev_err(dev, "%s: FW does not belong to Synaptics\n", + __func__); + retval = -EINVAL; + goto exit; + } + + snprintf(template, sizeof(template), "-%s-", + rmi->product_id_string); + if (!strnstr(fwname + strnlen(prefix, sizeof(prefix)), + template, strlen(fwname))) { + dev_err(dev, "%s: FW does not belong to %s\n", + __func__, rmi->product_id_string); + retval = -EINVAL; + goto exit; + } + } + + strlcpy(fwu->image_name, fwname, MAX_IMAGE_NAME_LEN); + dev_dbg(dev, "%s: FW filename: %s\n", __func__, fwu->image_name); + + retval = synaptics_fw_updater_local(fwu, NULL); + if (retval < 0) { + dev_err(dev, "%s: Failed to do reflash\n", __func__); + goto exit; + } + + retval = 0; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + fwu->image_name[0] = 0; + fwu->force_update = FORCE_UPDATE; + fwu->do_lockdown = DO_LOCKDOWN; + return retval; +} + +static ssize_t fwu_sysfs_do_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + + if (count > MAX_IMAGE_NAME_LEN) + return -EINVAL; + + retval = synaptics_dsx_firmware_update(dev, buf); + + return count; +} + +static ssize_t fwu_sysfs_force_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + fwu->force_update = true; + + return count; +} + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI +static ssize_t fwu_sysfs_write_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (input != 1) { + retval = -EINVAL; + goto exit; + } + + if (fwu->in_ub_mode) { + dev_err(LOGDEV, + "%s: In microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->ext_data_source) + return -EINVAL; + else + fwu->image = fwu->ext_data_source; + + retval = fwu_start_write_config(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write config\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + return retval; +} + +static ssize_t fwu_sysfs_read_config_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + if (fwu->in_ub_mode) { + dev_err(LOGDEV, + "%s: In microbootloader mode\n", + __func__); + return -EINVAL; + } + + retval = fwu_do_read_config(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read config\n", + __func__); + return retval; + } + + return count; +} + +static ssize_t fwu_sysfs_config_area_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + unsigned long config_area; + + retval = sstrtoul(buf, 10, &config_area); + if (retval) + return retval; + + fwu->config_area = config_area; + + return count; +} + +static ssize_t fwu_sysfs_image_name_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + + retval = secure_memcpy(fwu->image_name, MAX_IMAGE_NAME_LEN, + buf, count, count); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to copy image file name\n", + __func__); + return retval; + } + + return count; +} + +static ssize_t fwu_sysfs_image_size_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + unsigned long size; + + retval = sstrtoul(buf, 10, &size); + if (retval) + return retval; + + fwu->image_size = size; + fwu->data_pos = 0; + + kfree(fwu->ext_data_source); + fwu->ext_data_source = kzalloc(fwu->image_size, GFP_KERNEL); + if (!fwu->ext_data_source) { + dev_err(LOGDEV, + "%s: Failed to alloc mem for image data\n", + __func__); + return -ENOMEM; + } + + return count; +} + +static ssize_t fwu_sysfs_block_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->block_size); +} + +static ssize_t fwu_sysfs_firmware_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.ui_firmware); +} + +static ssize_t fwu_sysfs_configuration_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.ui_config); +} + +static ssize_t fwu_sysfs_disp_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.dp_config); +} + +static ssize_t fwu_sysfs_perm_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.pm_config); +} + +static ssize_t fwu_sysfs_bl_config_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.bl_config); +} + +static ssize_t fwu_sysfs_guest_code_block_count_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + return snprintf(buf, PAGE_SIZE, "%u\n", fwu->blkcount.guest_code); +} + +static ssize_t fwu_sysfs_write_guest_code_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + unsigned int input; + + if (sscanf(buf, "%u", &input) != 1) { + retval = -EINVAL; + goto exit; + } + + if (input != 1) { + retval = -EINVAL; + goto exit; + } + + if (fwu->in_ub_mode) { + dev_err(LOGDEV, + "%s: In microbootloader mode\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!fwu->ext_data_source) + return -EINVAL; + else + fwu->image = fwu->ext_data_source; + + retval = fwu_start_write_guest_code(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to write guest code\n", + __func__); + goto exit; + } + + retval = count; + +exit: + kfree(fwu->ext_data_source); + fwu->ext_data_source = NULL; + fwu->image = NULL; + return retval; +} +#endif + +static int synaptics_dsx_firmware_erase(struct device *dev) +{ + struct synaptics_rmi4_data *rmi4_data = dev_get_drvdata(dev); + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval; + + PM_STAY_AWAKE(fwu->flash_wake_src); + mutex_lock(&rmi4_data->rmi4_exp_init_mutex); + + if (!fwu->in_bl_mode) { + fwu_irq_enable(fwu, true); + + retval = fwu_write_f34_command(fwu, CMD_ENABLE_FLASH_PROG); + if (retval < 0) + goto reset_and_exit; + + retval = fwu_wait_for_idle(fwu, ENABLE_WAIT_MS); + fwu_irq_enable(fwu, false); + if (retval < 0 || !fwu->in_bl_mode) + goto reset_and_exit; + } + + rmi4_data->set_state(rmi4_data, STATE_INIT); + fwu_irq_enable(fwu, true); + + retval = fwu_erase_all(fwu); + if (retval < 0) + pr_err("%s: ERASE_ALL failed\n", __func__); + + fwu_irq_enable(fwu, false); + rmi4_data->set_state(rmi4_data, STATE_UNKNOWN); + +reset_and_exit: + fwu_reset_device(fwu); + pr_notice("%s: End of reflash process\n", __func__); + + /* Rescan PDT after flashing and before register access */ + retval = fwu_scan_pdt(fwu); + if (retval < 0) + dev_err(LOGDEV, "%s: Failed to scan PDT\n", __func__); + + if (!fwu->in_ub_mode) { + retval = fwu_read_f34_queries(fwu); + if (retval < 0) + dev_err(LOGDEV, "%s: Failed to query F34\n", __func__); + } + + rmi4_data->ready_state(rmi4_data, false); + mutex_unlock(&rmi4_data->rmi4_exp_init_mutex); + PM_RELAX(fwu->flash_wake_src); + + return 0; +} + +static ssize_t fwu_sysfs_erase_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + int retval; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + retval = synaptics_dsx_firmware_erase(dev); + + return count; +} + +static void synaptics_rmi4_fwu_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + + if (!fwu) + return; + + if (fwu->intr_mask & intr_mask) + fwu_read_flash_status(fwu); + + return; +} + +static int synaptics_rmi4_fwu_init(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int retval, attr_count; + struct pdt_properties pdt_props; + + fwu->image_name = kzalloc(MAX_IMAGE_NAME_LEN, GFP_KERNEL); + if (!fwu->image_name) { + dev_err(LOGDEV, + "%s: Failed to alloc mem for image name\n", + __func__); + retval = -ENOMEM; + goto exit_free_fwu; + } + + sema_init(&fwu->irq_sema, 0); + mutex_init(&rmi4_data->rmi4_exp_init_mutex); + + retval = synaptics_rmi4_reg_read(rmi4_data, + PDT_PROPS, + pdt_props.data, + sizeof(pdt_props.data)); + if (retval < 0) { + dev_dbg(LOGDEV, + "%s: Failed to read PDT properties, assuming 0x00\n", + __func__); + } else if (pdt_props.has_bsr) { + dev_err(LOGDEV, + "%s: Reflash for LTS not currently supported\n", + __func__); + retval = -ENODEV; + goto exit_free_mem; + } + + retval = fwu_scan_pdt(fwu); + if (retval < 0) + goto exit_free_mem; + + if (!fwu->in_ub_mode) { + fwu_irq_enable(fwu, true); + retval = fwu_read_f34_queries(fwu); + fwu_irq_enable(fwu, false); + if (retval < 0) + goto exit_free_mem; + + retval = fwu_get_device_config_id(fwu); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read device config ID\n", + __func__); + goto exit_free_mem; + } + } + + fwu->force_update = FORCE_UPDATE; + fwu->do_lockdown = DO_LOCKDOWN; + + PM_WAKEUP_REGISTER(fwu->dev, fwu->flash_wake_src, "synaptics_fw_flash"); + if (!fwu->flash_wake_src) { + dev_err(LOGDEV, + "Failed to allocate wakeup source\n"); + retval = -ENOMEM; + goto exit_free_mem; + } + + fwu->initialized = true; + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI + retval = sysfs_create_bin_file(SYSFS_KOBJ, &dev_attr_data); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to create sysfs bin file\n", + __func__); + goto exit_free_mem; + } +#endif + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = sysfs_create_file(SYSFS_KOBJ, &attrs[attr_count].attr); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto exit_remove_attrs; + } + } +/* + if (strncmp(bi_bootmode(), "mot-factory", strlen("mot-factory")) == 0) { + retval = sysfs_create_file(SYSFS_KOBJ, &erase_attr[0].attr); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to create erase sysfs attributes\n", + __func__); + } else + fwu->has_erase_all = true; + } +*/ + rmi4_data->fwu_data = (void *)fwu; + + return 0; + +exit_remove_attrs: + for (attr_count--; attr_count >= 0; attr_count--) + sysfs_remove_file(SYSFS_KOBJ, &attrs[attr_count].attr); +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI + sysfs_remove_bin_file(SYSFS_KOBJ, &dev_attr_data); +#endif +exit_free_mem: + kfree(fwu->image_name); + +exit_free_fwu: + kfree(fwu); + rmi4_data->fwu_data = NULL; + + return retval; +} + +static void synaptics_rmi4_fwu_remove(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int attr_count; + + if (!fwu) + goto exit; + + PM_WAKEUP_UNREGISTER(fwu->flash_wake_src); + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + sysfs_remove_file(SYSFS_KOBJ, &attrs[attr_count].attr); + } + + if (fwu->has_erase_all) + sysfs_remove_file(SYSFS_KOBJ, &erase_attr[0].attr); + +#ifdef CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_FW_UPDATE_EXTRA_SYSFS_MMI + sysfs_remove_bin_file(SYSFS_KOBJ, &dev_attr_data); +#endif + +exit: + complete(&fwu->remove_complete); + + return; +} + +static int synaptics_rmi4_fwu_flash_status( + struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_fwu_handle *fwu = + (struct synaptics_rmi4_fwu_handle *)rmi4_data->fwu_data; + int istatus = 0, retval; + unsigned char status; + + if (fwu == NULL) + return -EINVAL; + + retval = synaptics_rmi4_reg_read(rmi4_data, + fwu->f34_fd.data_base_addr + fwu->off.flash_status, + &status, + sizeof(status)); + if (retval < 0) { + dev_err(LOGDEV, + "%s: Failed to read flash status\n", __func__); + return retval; + } + istatus = status >> 7; + + return istatus; +} + +static int __init rmi4_fw_update_module_init(void) +{ + struct synaptics_rmi4_fwu_handle *fwu; + struct synaptics_rmi4_data *next = NULL; + + while ((next = synaptics_driver_getdata(next)) != NULL) { + fwu = kzalloc(sizeof(*fwu), GFP_KERNEL); + fwu->firmware_update = synaptics_dsx_firmware_update; + fwu->firmware_erase = synaptics_dsx_firmware_erase; + fwu->dev = &next->i2c_client->dev; + /* rmi4_data pointer set as drvdata to i2c device */ + next->fwu_data = (void *)fwu; + synaptics_rmi4_new_function(next, RMI_FW_UPDATER, true, + synaptics_rmi4_fwu_init, + synaptics_rmi4_fwu_remove, + synaptics_rmi4_fwu_attn, + synaptics_rmi4_fwu_flash_status, + IC_MODE_ANY); + } + return 0; +} + +static void __exit rmi4_fw_update_module_exit(void) +{ + struct synaptics_rmi4_fwu_handle *fwu; + struct synaptics_rmi4_data *next = NULL; + + while ((next = synaptics_driver_getdata(next)) != NULL) { + fwu = (struct synaptics_rmi4_fwu_handle *)next->fwu_data; + init_completion(&fwu->remove_complete); + synaptics_rmi4_new_function(next, RMI_FW_UPDATER, false, + synaptics_rmi4_fwu_init, + synaptics_rmi4_fwu_remove, + synaptics_rmi4_fwu_attn, + synaptics_rmi4_fwu_flash_status, + IC_MODE_ANY); + wait_for_completion(&fwu->remove_complete); + if (fwu->read_config_buf_size) + kfree(fwu->read_config_buf); + kfree(fwu->image_name); + kfree(fwu); + next->fwu_data = NULL; + } + return; +} + +module_init(rmi4_fw_update_module_init); +module_exit(rmi4_fw_update_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX FW Update Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_mmi_class/synaptics_mmi_class_rmi_dev.c b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_mmi_class_rmi_dev.c new file mode 100644 index 000000000000..ecbf832d0ea8 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_mmi_class_rmi_dev.c @@ -0,0 +1,890 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "synaptics_dsx_i2c.h" + +#define CHAR_DEVICE_NAME "rmi" +#define DEVICE_CLASS_NAME "rmidev" +#define DEV_NUMBER 1 +#define MAX_INSTANCE 5 +#define REG_ADDR_LIMIT 0xFFFF + +static char *rmi_cdev_node(struct device *dev, umode_t *mode) +{ + if (mode) + *mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); + return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev)); +} + +static struct class rmi_device_class = { + .owner = THIS_MODULE, + .name = DEVICE_CLASS_NAME, + .devnode = rmi_cdev_node, +}; + +static int rmidev_major_num; + +static ssize_t rmidev_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t rmidev_sysfs_data_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t rmidev_sysfs_open_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_release_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_address_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_length_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count); + +static ssize_t rmidev_sysfs_attn_state_show(struct device *dev, + struct device_attribute *attr, char *buf); + +struct rmidev_handle { + dev_t dev_no; + unsigned short address; + unsigned int length; + struct device dev; + struct synaptics_rmi4_data *rmi4_data; + struct synaptics_rmi4_exp_fn_ptr *fn_ptr; + struct completion remove_complete; + void *data; +}; + +struct rmidev_data { + int ref_count; + struct cdev main_dev; + struct class *device_class; + struct mutex file_mutex; + struct rmidev_handle *rmi_dev; + struct temp_buffer data_buf; +}; + +static const struct bin_attribute attr_data = { + .attr = { + .name = "data", + .mode = (S_IRUGO | S_IWUSR | S_IWGRP), + }, + .size = 0, + .read = rmidev_sysfs_data_show, + .write = rmidev_sysfs_data_store, +}; + +static const struct device_attribute attrs[] = { + __ATTR(open, S_IWUSR | S_IWGRP, + NULL, + rmidev_sysfs_open_store), + __ATTR(release, S_IWUSR | S_IWGRP, + NULL, + rmidev_sysfs_release_store), + __ATTR(address, S_IWUSR | S_IWGRP, + NULL, + rmidev_sysfs_address_store), + __ATTR(length, S_IWUSR | S_IWGRP, + NULL, + rmidev_sysfs_length_store), + __ATTR(attn_state, S_IRUSR | S_IRGRP, + rmidev_sysfs_attn_state_show, + NULL), +}; + +static ssize_t rmidev_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + struct rmidev_data *dev_data = data_file->private_data; + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + int retval; + unsigned int data_length = rmidev->length; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + if (data_length > (REG_ADDR_LIMIT - rmidev->address)) + data_length = REG_ADDR_LIMIT - rmidev->address; + + if (count < data_length) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Not enough space (%zu bytes) in buffer\n", + __func__, count); + return -EINVAL; + } + + if (data_length) { + retval = rmidev->fn_ptr->read(rmi4_data, + rmidev->address, + (unsigned char *)buf, + data_length); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read data\n", + __func__); + return retval; + } + } else { + return -EINVAL; + } + + return data_length; +} + +static ssize_t rmidev_sysfs_data_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + struct rmidev_data *dev_data = data_file->private_data; + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + int retval; + unsigned int data_length = rmidev->length; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + if (data_length > (REG_ADDR_LIMIT - rmidev->address)) + data_length = REG_ADDR_LIMIT - rmidev->address; + + if (data_length) { + retval = rmidev->fn_ptr->write(rmidev->rmi4_data, + rmidev->address, + (unsigned char *)buf, + data_length); + if (retval < 0) { + dev_err(&rmidev->rmi4_data->i2c_client->dev, + "%s: Failed to write data\n", + __func__); + return retval; + } + } else { + return -EINVAL; + } + + return count; +} + +static ssize_t rmidev_sysfs_open_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct rmidev_data *dev_data = + dev_get_drvdata(dev); + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + unsigned int input; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + rmidev->fn_ptr->enable(rmi4_data, false); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Attention interrupt disabled\n", + __func__); + + return count; +} + +static ssize_t rmidev_sysfs_release_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct rmidev_data *dev_data = + dev_get_drvdata(dev); + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + unsigned int input; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 1) + return -EINVAL; + + rmidev->fn_ptr->enable(rmi4_data, true); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Attention interrupt enabled\n", + __func__); + + return count; +} + +static ssize_t rmidev_sysfs_address_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct rmidev_data *dev_data = + dev_get_drvdata(dev); + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + unsigned int input; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input > REG_ADDR_LIMIT) + return -EINVAL; + + rmidev->address = (unsigned short)input; + + return count; +} + +static ssize_t rmidev_sysfs_length_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct rmidev_data *dev_data = + dev_get_drvdata(dev); + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + unsigned int input; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input > REG_ADDR_LIMIT) + return -EINVAL; + + rmidev->length = input; + + return count; +} + +static ssize_t rmidev_sysfs_attn_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rmidev_data *dev_data = + dev_get_drvdata(dev); + struct rmidev_handle *rmidev = dev_data->rmi_dev; + struct synaptics_rmi4_data *rmi4_data = rmidev->rmi4_data; + int attn_state; + const struct synaptics_dsx_platform_data *platform_data = + &rmi4_data->board; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: dev_data %p, rmidev %p, rmi4_data %p\n", + __func__, dev_data, rmidev, rmi4_data); + + attn_state = gpio_get_value_cansleep(platform_data->irq_gpio); + + return snprintf(buf, PAGE_SIZE, "%u\n", attn_state); +} + +/* + * rmidev_llseek - used to set up register address + * + * @filp: file structure for seek + * @off: offset + * if whence == SEEK_SET, + * high 16 bits: page address + * low 16 bits: register address + * if whence == SEEK_CUR, + * offset from current position + * if whence == SEEK_END, + * offset from end position (0xFFFF) + * @whence: SEEK_SET, SEEK_CUR, or SEEK_END + */ +static loff_t rmidev_llseek(struct file *filp, loff_t off, int whence) +{ + loff_t newpos; + struct rmidev_data *dev_data = filp->private_data; + struct rmidev_handle *rmidev; + struct synaptics_rmi4_data *rmi4_data; + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + rmidev = dev_data->rmi_dev; + rmi4_data = rmidev->rmi4_data; + mutex_lock(&(dev_data->file_mutex)); + + switch (whence) { + case SEEK_SET: + newpos = off; + break; + case SEEK_CUR: + newpos = filp->f_pos + off; + break; + case SEEK_END: + newpos = REG_ADDR_LIMIT + off; + break; + default: + newpos = -EINVAL; + goto clean_up; + } + + if (newpos < 0 || newpos > REG_ADDR_LIMIT) { + dev_err(&rmidev->rmi4_data->i2c_client->dev, + "%s: New position 0x%04x is invalid\n", + __func__, (unsigned int)newpos); + newpos = -EINVAL; + goto clean_up; + } + + filp->f_pos = newpos; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: current position set %lld\n", + __func__, newpos); +clean_up: + mutex_unlock(&(dev_data->file_mutex)); + + return newpos; +} + +/* + * rmidev_read: - use to read data from rmi device + * + * @filp: file structure for read + * @buf: user space buffer pointer + * @count: number of bytes to read + * @f_pos: offset (starting register address) + */ +static ssize_t rmidev_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + ssize_t retval; + struct rmidev_data *dev_data = filp->private_data; + struct temp_buffer *tb; + struct rmidev_handle *rmidev; + struct synaptics_rmi4_data *rmi4_data; + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + rmidev = dev_data->rmi_dev; + rmi4_data = rmidev->rmi4_data; + mutex_lock(&(dev_data->file_mutex)); + + if (count > (REG_ADDR_LIMIT - *f_pos)) + count = REG_ADDR_LIMIT - *f_pos; + + if (count == 0) { + retval = 0; + goto clean_up; + } + + if (*f_pos > REG_ADDR_LIMIT) { + retval = -EFAULT; + goto clean_up; + } + + tb = &dev_data->data_buf; + + if (tb->buf_size < count && alloc_buffer(tb, count) != 0) { + retval = -ENOMEM; + goto clean_up; + } + + retval = rmidev->fn_ptr->read(rmidev->rmi4_data, + *f_pos, + tb->buf, + count); + if (retval < 0) + goto clean_up; + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: read %zu bytes at offset %lld\n", + __func__, count, *f_pos); + + if (copy_to_user(buf, tb->buf, count)) + retval = -EFAULT; + else + *f_pos += retval; + +clean_up: + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +/* + * rmidev_write: - used to write data to rmi device + * + * @filep: file structure for write + * @buf: user space buffer pointer + * @count: number of bytes to write + * @f_pos: offset (starting register address) + */ +static ssize_t rmidev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + ssize_t retval; + struct rmidev_data *dev_data = filp->private_data; + struct temp_buffer *tb; + struct rmidev_handle *rmidev; + struct synaptics_rmi4_data *rmi4_data; + + if (IS_ERR(dev_data)) { + pr_err("%s: Pointer of char device data is invalid", __func__); + return -EBADF; + } + + rmidev = dev_data->rmi_dev; + rmi4_data = rmidev->rmi4_data; + mutex_lock(&(dev_data->file_mutex)); + + if (*f_pos > REG_ADDR_LIMIT) { + retval = -EFAULT; + goto clean_up; + } + + if (count > (REG_ADDR_LIMIT - *f_pos)) + count = REG_ADDR_LIMIT - *f_pos; + + if (count == 0) { + retval = 0; + goto clean_up; + } + + tb = &dev_data->data_buf; + + if (tb->buf_size < count && alloc_buffer(tb, count) != 0) { + retval = -ENOMEM; + goto clean_up; + } + + if (copy_from_user(tb->buf, buf, count)) { + retval = -EFAULT; + goto clean_up; + } + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: write %zu bytes at offset %lld\n", + __func__, count, *f_pos); + + retval = rmidev->fn_ptr->write(rmidev->rmi4_data, + *f_pos, + tb->buf, + count); + if (retval >= 0) + *f_pos += retval; + +clean_up: + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +/* + * rmidev_open: enable access to rmi device + * @inp: inode struture + * @filp: file structure + */ +static int rmidev_open(struct inode *inp, struct file *filp) +{ + int retval = 0; + struct rmidev_data *dev_data = + container_of(inp->i_cdev, struct rmidev_data, main_dev); + struct rmidev_handle *rmidev; + + if (!dev_data) + return -EACCES; + + rmidev = dev_data->rmi_dev; + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: device data %p\n", + __func__, dev_data); + + filp->private_data = dev_data; + mutex_lock(&(dev_data->file_mutex)); + + rmidev->fn_ptr->enable(rmidev->rmi4_data, false); + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: Attention interrupt disabled\n", + __func__); + + if (dev_data->ref_count < 1) + dev_data->ref_count++; + else + retval = -EACCES; + + mutex_unlock(&(dev_data->file_mutex)); + + return retval; +} + +/* + * rmidev_release: - release access to rmi device + * @inp: inode structure + * @filp: file structure + */ +static int rmidev_release(struct inode *inp, struct file *filp) +{ + struct rmidev_data *dev_data = + container_of(inp->i_cdev, struct rmidev_data, main_dev); + struct rmidev_handle *rmidev; + + if (!dev_data) + return -EACCES; + + rmidev = dev_data->rmi_dev; + + mutex_lock(&(dev_data->file_mutex)); + + dev_data->ref_count--; + if (dev_data->ref_count < 0) + dev_data->ref_count = 0; + + rmidev->fn_ptr->enable(rmidev->rmi4_data, true); + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: Attention interrupt enabled\n", + __func__); + + mutex_unlock(&(dev_data->file_mutex)); + + return 0; +} + +static const struct file_operations rmidev_fops = { + .owner = THIS_MODULE, + .llseek = rmidev_llseek, + .read = rmidev_read, + .write = rmidev_write, + .open = rmidev_open, + .release = rmidev_release, +}; + +static void rmidev_device_cleanup(struct rmidev_data *dev_data) +{ + if (dev_data) { + dev_t devno; + struct rmidev_handle *rmidev = dev_data->rmi_dev; + + devno = dev_data->main_dev.dev; + + if (dev_data->device_class) + device_destroy(dev_data->device_class, devno); + + cdev_del(&dev_data->main_dev); + + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: rmidev device removed\n", + __func__); + } + + return; +} + +static void rmidev_registered_release(struct device *dev) +{ + struct rmidev_handle *rmidev = + container_of(dev, struct rmidev_handle, dev); + dev_dbg(&rmidev->rmi4_data->i2c_client->dev, + "%s: rmidev release function called\n", + __func__); +} + +static int rmidev_init_device(struct synaptics_rmi4_data *rmi4_data) +{ + struct rmidev_handle *rmidev = + (struct rmidev_handle *)rmi4_data->rmidev_data; + int retval; + dev_t dev_no; + unsigned char attr_count; + struct rmidev_data *dev_data; + struct device *device_ptr; + + pr_debug("%s: enter rmi4_data %p\n", __func__, rmi4_data); + + rmidev->fn_ptr = kzalloc(sizeof(*(rmidev->fn_ptr)), GFP_KERNEL); + if (!rmidev) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for fn_ptr\n", + __func__); + retval = -ENOMEM; + goto err_fn_ptr; + } + + rmidev->fn_ptr->read = rmi4_data->i2c_read; + rmidev->fn_ptr->write = rmi4_data->i2c_write; + rmidev->fn_ptr->enable = rmi4_data->irq_enable; + rmidev->rmi4_data = rmi4_data; + + if (!rmidev_major_num) { + retval = alloc_chrdev_region(&dev_no, rmi4_data->instance, + MAX_INSTANCE, CHAR_DEVICE_NAME); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to allocate char device region %d\n", + __func__, retval); + goto err_device_region; + } + + rmidev_major_num = MAJOR(dev_no); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Major number of rmidev = %d\n", + __func__, rmidev_major_num); + } else + dev_no = MKDEV(rmidev_major_num, rmi4_data->instance); + + dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL); + if (!dev_data) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for dev_data\n", + __func__); + retval = -ENOMEM; + goto err_dev_data; + } + + mutex_init(&dev_data->file_mutex); + dev_data->rmi_dev = rmidev; + dev_data->device_class = &rmi_device_class; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Device data %p\n", __func__, dev_data); + rmidev->data = dev_data; + + cdev_init(&dev_data->main_dev, &rmidev_fops); + + retval = cdev_add(&dev_data->main_dev, dev_no, 1); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to add rmi char device\n", + __func__); + goto err_char_device; + } + + device_ptr = device_create(dev_data->device_class, NULL, dev_no, + NULL, CHAR_DEVICE_NAME"%d", MINOR(dev_no)); + if (IS_ERR(device_ptr)) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create rmi char device\n", + __func__); + retval = -ENODEV; + goto err_char_device; + } + + retval = gpio_export(rmi4_data->board.irq_gpio, false); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to export attention gpio\n", + __func__); + } else { + retval = gpio_export_link(&(rmi4_data->i2c_client->dev), + "attn", rmi4_data->board.irq_gpio); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s Failed to create gpio symlink\n", + __func__); + } else { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Exported attention gpio %d\n", + __func__, rmi4_data->board.irq_gpio); + } + } + + rmidev->dev.class = &rmi_device_class; + rmidev->dev.parent = &rmi4_data->i2c_client->dev; + rmidev->dev.release = rmidev_registered_release; + + dev_set_name(&rmidev->dev, DEVICE_CLASS_NAME "%d", rmi4_data->instance); + dev_set_drvdata(&rmidev->dev, dev_data); + + retval = device_register(&rmidev->dev); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to register device\n", + __func__); + goto err_sysfs_dir; + } + + retval = device_create_bin_file(&rmidev->dev, &attr_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs bin file\n", + __func__); + goto err_sysfs_bin; + } + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) { + retval = device_create_file(&rmidev->dev, &attrs[attr_count]); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + retval = -ENODEV; + goto err_sysfs_attrs; + } + } + + return 0; + +err_sysfs_attrs: + for (attr_count--; attr_count >= 0; attr_count--) + device_remove_file(&rmidev->dev, &attrs[attr_count]); + + device_remove_bin_file(&rmidev->dev, &attr_data); + +err_sysfs_bin: + device_unregister(&rmidev->dev); + +err_sysfs_dir: +err_char_device: + rmidev_device_cleanup(dev_data); + kfree(dev_data); + +err_dev_data: + unregister_chrdev_region(dev_no, MAX_INSTANCE); + +err_device_region: + kfree(rmidev->fn_ptr); + +err_fn_ptr: + return retval; +} + +static void rmidev_remove_device(struct synaptics_rmi4_data *rmi4_data) +{ + struct rmidev_handle *rmidev = + (struct rmidev_handle *)rmi4_data->rmidev_data; + unsigned char attr_count; + struct rmidev_data *dev_data; + + pr_debug("%s: rmi4_data %p\n", __func__, rmi4_data); + + if (!rmidev) + return; + + gpio_unexport(rmi4_data->board.irq_gpio); + + sysfs_remove_link(&rmi4_data->i2c_client->dev.kobj, "attn"); + + for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) + device_remove_file(&rmidev->dev, &attrs[attr_count]); + + device_remove_bin_file(&rmidev->dev, &attr_data); + + device_unregister(&rmidev->dev); + + dev_data = rmidev->data; + if (dev_data) { + rmidev_device_cleanup(dev_data); + kfree(dev_data); + } + + unregister_chrdev_region(rmidev->dev_no, 1); + + kfree(rmidev->fn_ptr); + + complete(&rmidev->remove_complete); + + return; +} + +static int __init rmidev_module_init(void) +{ + int err; + struct rmidev_handle *rmidev; + struct synaptics_rmi4_data *next = NULL; + + err = class_register(&rmi_device_class); + if (err) { + pr_err("%s: couldn't register rmi device class\n", __func__); + return err; + } + + while ((next = synaptics_driver_getdata(next)) != NULL) { + rmidev = kzalloc(sizeof(*rmidev), GFP_KERNEL); + if (!rmidev) { + dev_err(&next->i2c_client->dev, + "%s: cannot allocate rmidev for rmi4_data %p\n", + __func__, next); + continue; + } + dev_dbg(&next->i2c_client->dev, + "%s: new rmidev %p\n", __func__, rmidev); + next->rmidev_data = (void *)rmidev; + synaptics_rmi4_new_function(next, RMI_DEV, true, + rmidev_init_device, + rmidev_remove_device, + NULL, NULL, IC_MODE_UI); + } + + return 0; +} + +static void __exit rmidev_module_exit(void) +{ + struct rmidev_handle *rmidev; + struct synaptics_rmi4_data *next = NULL; + + while ((next = synaptics_driver_getdata(next)) != NULL) { + rmidev = (struct rmidev_handle *)next->rmidev_data; + dev_dbg(&next->i2c_client->dev, + "%s: removing rmidev %p\n", __func__, rmidev); + init_completion(&rmidev->remove_complete); + synaptics_rmi4_new_function(next, RMI_DEV, false, + rmidev_init_device, + rmidev_remove_device, + NULL, NULL, IC_MODE_UI); + wait_for_completion(&rmidev->remove_complete); + kfree(rmidev); + next->rmidev_data = NULL; + } + + unregister_chrdev_region(MKDEV(rmidev_major_num, 1), MAX_INSTANCE); + class_unregister(&rmi_device_class); + + return; +} + +module_init(rmidev_module_init); +module_exit(rmidev_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX RMI Dev Module"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(SYNAPTICS_DSX_DRIVER_VERSION); diff --git a/drivers/input/touchscreen/synaptics_mmi_class/synaptics_mmi_class_test_reporting.c b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_mmi_class_test_reporting.c new file mode 100644 index 000000000000..ac703c11b643 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_mmi_class/synaptics_mmi_class_test_reporting.c @@ -0,0 +1,5991 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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 pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "synaptics_dsx_i2c.h" + +#if 0 +#ifdef pr_debug +#undef pr_debug +#define pr_debug pr_err +#endif +#ifdef dev_dbg +#undef dev_dbg +#define dev_dbg dev_err +#endif +#endif + +#undef WATCHDOG_HRTIMER +#define WATCHDOG_TIMEOUT_S 1 +#define STATUS_WORK_INTERVAL 20 /* ms */ + +/* enable for report status polling */ +#define REPORT_STATUS_POLLING + +/* +#define RAW_HEX +#define HUMAN_READABLE +*/ + +#define STATUS_IDLE 0 +#define STATUS_BUSY 1 +#define STATUS_ERROR 2 + +#define DATA_REPORT_INDEX_OFFSET 1 +#define DATA_REPORT_DATA_OFFSET 3 + +#define COMMAND_GET_REPORT 1 +#define COMMAND_FORCE_CAL 2 +#define COMMAND_FORCE_UPDATE 4 +#define COMMAND_ENTER_IN_CELL_TESTMODE 16 + +#define HIGH_RESISTANCE_DATA_SIZE 6 +#define FULL_RAW_CAP_MIN_MAX_DATA_SIZE 4 +#define TREX_DATA_SIZE 7 + +#define NO_AUTO_CAL_MASK 0x01 + +struct synaptics_rmi4_f54_handle; + +static inline ssize_t synaptics_dsx_test_reporting_show_error( + struct synaptics_rmi4_f54_handle *f54, char *buf); +static inline ssize_t synaptics_dsx_test_reporting_store_error( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count); + +struct f54_attribute { + struct attribute attr; + ssize_t (*show)(struct synaptics_rmi4_f54_handle *f54, char *buf); + ssize_t (*store)(struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count); +}; + +#define concat(a, b) a##b + +#define GROUP(_attrs) {\ + .attrs = _attrs,\ +} + +#define attrify(propname) (&f54_attr_##propname.attr) + +#define show_prototype_ext(propname, perm)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ + struct synaptics_rmi4_f54_handle *f54, char *buf);\ +\ +static struct f54_attribute f54_attr_##propname =\ + __ATTR(propname, (perm),\ + concat(synaptics_rmi4_f54, _##propname##_show),\ + synaptics_dsx_test_reporting_store_error); + +#define show_prototype(propname)\ + show_prototype_ext(propname, S_IRUSR | S_IRGRP | S_IROTH) + +#define store_prototype(propname)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_store)(\ + struct synaptics_rmi4_f54_handle *f54,\ + const char *buf, size_t count);\ +\ +static struct f54_attribute f54_attr_##propname =\ + __ATTR(propname, S_IWUSR | S_IWGRP,\ + synaptics_dsx_test_reporting_show_error,\ + concat(synaptics_rmi4_f54, _##propname##_store)); + +#define show_store_prototype_ext(propname, perm)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ + struct synaptics_rmi4_f54_handle *f54, char *buf);\ +\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_store)(\ + struct synaptics_rmi4_f54_handle *f54,\ + const char *buf, size_t count);\ +\ +static struct f54_attribute f54_attr_##propname =\ + __ATTR(propname, (perm),\ + concat(synaptics_rmi4_f54, _##propname##_show),\ + concat(synaptics_rmi4_f54, _##propname##_store)); + +#define show_store_prototype(propname)\ + show_store_prototype_ext(propname, \ + S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP) + +#define simple_show_func(rtype, propname, fmt)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ + struct synaptics_rmi4_f54_handle *f54, char *buf)\ +{\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: stored value " #rtype "." #propname "\n", __func__);\ + return snprintf(buf, PAGE_SIZE, fmt, f54->rtype.propname);\ +} \ + +#define simple_show_func_unsigned(rtype, propname)\ +simple_show_func(rtype, propname, "%u\n") + +#define show_func(rtype, rgrp, propname, fmt)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ + struct synaptics_rmi4_f54_handle *f54, char *buf)\ +{\ + int retval;\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: read value " #rtype "." #rgrp "." #propname " at addr=0x%x\n",\ + __func__, f54->rtype.rgrp->address);\ +\ + mutex_lock(&f54->rtype##_mutex);\ +\ + retval = f54->fn_ptr->read(rmi4_data,\ + f54->rtype.rgrp->address,\ + f54->rtype.rgrp->data,\ + sizeof(f54->rtype.rgrp->data));\ + mutex_unlock(&f54->rtype##_mutex);\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read " #rtype\ + " " #rgrp "\n",\ + __func__);\ + return retval;\ + } \ +\ + return snprintf(buf, PAGE_SIZE, fmt,\ + f54->rtype.rgrp->propname);\ +} \ + +#define show_func_unsigned(rtype, rgrp, propname)\ +show_func(rtype, rgrp, propname, "%u\n") + +#define show_store_func(rtype, rgrp, propname, fmt)\ +show_func(rtype, rgrp, propname, fmt)\ +\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_store)(\ + struct synaptics_rmi4_f54_handle *f54,\ + const char *buf, size_t count)\ +{\ + int retval;\ + unsigned long setting;\ + unsigned long o_setting;\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ +\ + retval = sstrtoul(buf, 10, &setting);\ + if (retval)\ + return retval;\ +\ + mutex_lock(&f54->rtype##_mutex);\ + retval = f54->fn_ptr->read(rmi4_data,\ + f54->rtype.rgrp->address,\ + f54->rtype.rgrp->data,\ + sizeof(f54->rtype.rgrp->data));\ + if (retval < 0) {\ + mutex_unlock(&f54->rtype##_mutex);\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read " #rtype\ + " " #rgrp "\n",\ + __func__);\ + return retval;\ + } \ +\ + if (f54->rtype.rgrp->propname == setting) {\ + mutex_unlock(&f54->rtype##_mutex);\ + return count;\ + } \ +\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: write value %ld " #rtype "." #rgrp "." #propname " at addr=0x%x\n",\ + __func__, setting, f54->rtype.rgrp->address);\ +\ + o_setting = f54->rtype.rgrp->propname;\ + f54->rtype.rgrp->propname = setting;\ +\ + retval = f54->fn_ptr->write(rmi4_data,\ + f54->rtype.rgrp->address,\ + f54->rtype.rgrp->data,\ + sizeof(f54->rtype.rgrp->data));\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to write " #rtype\ + " " #rgrp "\n",\ + __func__);\ + f54->rtype.rgrp->propname = o_setting;\ + mutex_unlock(&f54->rtype##_mutex);\ + return retval;\ + } \ +\ + mutex_unlock(&f54->rtype##_mutex);\ + return count;\ +} \ + +#define show_store_func_unsigned(rtype, rgrp, propname)\ +show_store_func(rtype, rgrp, propname, "%u\n") + +#define show_subpkt_func(rtype, rgrp, subpkt, propname, fmt)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ + struct synaptics_rmi4_f54_handle *f54, char *buf)\ +{\ + int retval;\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: read value " #rtype "." #rgrp "." #propname "." #subpkt " at addr=0x%x\n",\ + __func__, f54->rtype.rgrp->address);\ +\ + mutex_lock(&f54->rtype##_mutex);\ +\ + retval = f54->fn_ptr->read(rmi4_data,\ + f54->rtype.rgrp->address,\ + f54->rtype.rgrp->data,\ + f54->rtype.rgrp->size);\ + mutex_unlock(&f54->rtype##_mutex);\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read " #rtype\ + " " #rgrp #subpkt"\n",\ + __func__);\ + return retval;\ + } \ +\ + return snprintf(buf, PAGE_SIZE, fmt,\ + f54->rtype.rgrp->sp##subpkt->propname);\ +} \ + +#define show_subpkt_func_unsigned(rtype, rgrp, subpkt, propname)\ +show_subpkt_func(rtype, rgrp, subpkt, propname, "%u\n") + +#define show_store_subpkt_func(rtype, rgrp, subpkt, propname, fmt)\ +show_subpkt_func(rtype, rgrp, subpkt, propname, fmt)\ +\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_store)(\ + struct synaptics_rmi4_f54_handle *f54,\ + const char *buf, size_t count)\ +{\ + int retval;\ + unsigned long setting;\ + unsigned long o_setting;\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: read then write value " #rtype "." #rgrp "." #propname "." #subpkt " at addr=0x%x\n",\ + __func__, f54->rtype.rgrp->address);\ +\ + retval = sstrtoul(buf, 10, &setting);\ + if (retval)\ + return retval;\ +\ + mutex_lock(&f54->rtype##_mutex);\ + retval = f54->fn_ptr->read(rmi4_data,\ + f54->rtype.rgrp->address,\ + f54->rtype.rgrp->data,\ + f54->rtype.rgrp->size);\ + if (retval < 0) {\ + mutex_unlock(&f54->rtype##_mutex);\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read " #rtype\ + " " #rgrp #subpkt"\n",\ + __func__);\ + return retval;\ + } \ +\ + if (f54->rtype.rgrp->sp##subpkt->propname == setting) {\ + mutex_unlock(&f54->rtype##_mutex);\ + return count;\ + } \ +\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: write value %ld " #rtype "." #rgrp "." #propname " at addr=0x%x\n",\ + __func__, setting, f54->rtype.rgrp->address);\ +\ + o_setting = f54->rtype.rgrp->sp##subpkt->propname;\ + f54->rtype.rgrp->sp##subpkt->propname = setting;\ +\ + retval = f54->fn_ptr->write(rmi4_data,\ + f54->rtype.rgrp->address,\ + f54->rtype.rgrp->data,\ + f54->rtype.rgrp->size);\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to write " #rtype\ + " " #rgrp #subpkt"\n",\ + __func__);\ + f54->rtype.rgrp->sp##subpkt->propname = o_setting;\ + mutex_unlock(&f54->rtype##_mutex);\ + return retval;\ + } \ +\ + mutex_unlock(&f54->rtype##_mutex);\ + return count;\ +} \ + +#define show_store_subpkt_func_unsigned(rtype, rgrp, subpkt, propname)\ +show_store_subpkt_func(rtype, rgrp, subpkt, propname, "%u\n") + +#define show_replicated_func(rtype, rgrp, propname, fmt)\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_show)(\ + struct synaptics_rmi4_f54_handle *f54, char *buf)\ +{\ + int retval;\ + int size = 0;\ + unsigned char ii;\ + unsigned char length;\ + unsigned char element_count;\ + unsigned char *temp;\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: read value " #rtype "." #rgrp "." #propname " at addr=0x%x\n",\ + __func__, f54->rtype.rgrp->address);\ +\ + mutex_lock(&f54->rtype##_mutex);\ +\ + length = f54->rtype.rgrp->length;\ + element_count = length / sizeof(*f54->rtype.rgrp->data);\ +\ + retval = f54->fn_ptr->read(rmi4_data,\ + f54->rtype.rgrp->address,\ + (unsigned char *)f54->rtype.rgrp->data,\ + length);\ + mutex_unlock(&f54->rtype##_mutex);\ + if (retval < 0) {\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read " #rtype\ + " " #rgrp "\n",\ + __func__);\ + } \ +\ + temp = buf;\ +\ + for (ii = 0; ii < element_count; ii++) {\ + retval = snprintf(temp, PAGE_SIZE - size, fmt " ",\ + f54->rtype.rgrp->data[ii].propname);\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to write output\n",\ + __func__);\ + return retval;\ + } \ + size += retval;\ + temp += retval;\ + } \ +\ + retval = snprintf(temp, PAGE_SIZE - size, "\n");\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Faild to write null terminator\n",\ + __func__);\ + return retval;\ + } \ +\ + return size + retval;\ +} \ + +#define show_replicated_func_unsigned(rtype, rgrp, propname)\ +show_replicated_func(rtype, rgrp, propname, "%u") + +#define show_store_replicated_func(rtype, rgrp, propname, fmt)\ +show_replicated_func(rtype, rgrp, propname, fmt)\ +\ +static ssize_t concat(synaptics_rmi4_f54, _##propname##_store)(\ + struct synaptics_rmi4_f54_handle *f54,\ + const char *buf, size_t count)\ +{\ + int retval;\ + unsigned int setting;\ + unsigned char ii;\ + unsigned char length;\ + unsigned char element_count;\ + const unsigned char *temp;\ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: rmi4_data_ptr %p\n", __func__, rmi4_data);\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: read then write value " #rtype "." #rgrp "." #propname " at addr=0x%x\n",\ + __func__, f54->rtype.rgrp->address);\ +\ + mutex_lock(&f54->rtype##_mutex);\ +\ + length = f54->rtype.rgrp->length;\ + element_count = length / sizeof(*f54->rtype.rgrp->data);\ +\ + retval = f54->fn_ptr->read(rmi4_data,\ + f54->rtype.rgrp->address,\ + (unsigned char *)f54->rtype.rgrp->data,\ + length);\ + if (retval < 0) {\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read " #rtype\ + " " #rgrp "\n",\ + __func__);\ + } \ +\ + temp = buf;\ +\ + for (ii = 0; ii < element_count; ii++) {\ + if (sscanf(temp, fmt, &setting) == 1) {\ + f54->rtype.rgrp->data[ii].propname = setting;\ + } else {\ + mutex_unlock(&f54->rtype##_mutex);\ + return -EINVAL;\ + } \ +\ + while (*temp != 0) {\ + temp++;\ + if (isspace(*(temp - 1)) && !isspace(*temp))\ + break;\ + } \ + } \ +\ + retval = f54->fn_ptr->write(rmi4_data,\ + f54->rtype.rgrp->address,\ + (unsigned char *)f54->rtype.rgrp->data,\ + length);\ + mutex_unlock(&f54->rtype##_mutex);\ + if (retval < 0) {\ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to write " #rtype\ + " " #rgrp "\n",\ + __func__);\ + return retval;\ + } \ +\ + return count;\ +} \ + +#define show_store_replicated_func_unsigned(rtype, rgrp, propname)\ +show_store_replicated_func(rtype, rgrp, propname, "%u") + +#define DATA_REG_ADD(reg, skip, cond) \ + do { if (cond) { \ + attrs_data_regs_exist[reg_num] = true;\ + data->reg_##reg = kzalloc(sizeof(*data->reg_##reg),\ + GFP_KERNEL);\ + if (!data->reg_##reg)\ + goto exit_no_mem;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: d%s addr = 0x%02x added (%d)\n",\ + __func__, #reg, reg_addr, reg_num);\ + data->reg_##reg->address = reg_addr;\ + reg_addr += skip;\ + } \ + reg_num++;\ + } while (0) + +#define DATA_REG_PRESENCE(reg, skip, cond) \ + do { if (cond) { \ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: d%s addr = 0x%02x\n",\ + __func__, #reg, reg_addr);\ + reg_addr += skip;\ + } \ + } while (0) + +#define CTRL_REG_ADD(reg, skip, cond) \ + do { if (cond) { \ + attrs_ctrl_regs_exist[reg_num] = true;\ + control->reg_##reg = kzalloc(sizeof(*control->reg_##reg),\ + GFP_KERNEL);\ + if (!control->reg_##reg)\ + goto exit_no_mem;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: c%s addr = 0x%02x added (%d)\n",\ + __func__, #reg, reg_addr, reg_num);\ + control->reg_##reg->address = reg_addr | PAGE_SELECT_ENFORCE;\ + reg_addr += skip;\ + } \ + reg_num++;\ + } while (0) + +#define CTRL_REG_ADD_EXT(reg, skip, cond, size) \ + do { if (cond) { \ + attrs_ctrl_regs_exist[reg_num] = true;\ + control->reg_##reg = kzalloc(sizeof(*control->reg_##reg),\ + GFP_KERNEL);\ + if (!control->reg_##reg)\ + goto exit_no_mem;\ + control->reg_##reg->data = kzalloc(size, GFP_KERNEL);\ + if (!control->reg_##reg->data)\ + goto exit_no_mem;\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: c%s addr = 0x%02x size = %zu added (%d)\n",\ + __func__, #reg, reg_addr, size, reg_num);\ + control->reg_##reg->length = size;\ + control->reg_##reg->address = reg_addr | PAGE_SELECT_ENFORCE;\ + reg_addr += skip;\ + } \ + reg_num++;\ + } while (0) + +#define CTRL_SUBPKT_SZ(reg, subpkt) \ + sizeof(*((struct f54_control_##reg *)0)->sp##subpkt) + +#define CTRL_PKT_REG_START(reg, max_size) \ + do {\ + control->reg_##reg = kzalloc(sizeof(*control->reg_##reg),\ + GFP_KERNEL);\ + if (!control->reg_##reg)\ + goto exit_no_mem;\ + control->reg_##reg->data = kzalloc(max_size,\ + GFP_KERNEL);\ + if (!control->reg_##reg->data)\ + goto exit_no_mem;\ + } while (0) + +#define CTRL_SUBPKT_ADD(reg, subpkt, cond) \ + do { if (cond) { \ + attrs_ctrl_regs_exist[reg_num] = true; \ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: subpkt c%s_%d added (%d)\n", \ + __func__, #reg, subpkt, reg_num); \ + control->reg_##reg->sp##subpkt = \ + (void *)control->reg_##reg->data + \ + control->reg_##reg->size; \ + control->reg_##reg->size += \ + sizeof(*control->reg_##reg->sp##subpkt); \ + } \ + reg_num++;\ + } while (0) + +#define CTRL_PKT_REG_END(reg, skip) \ + do { if (control->reg_##reg->size) { \ + control->reg_##reg->address = reg_addr; \ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: c%s addr = 0x%02x\n", \ + __func__, #reg, reg_addr); \ + reg_addr += skip; \ + } \ + else { \ + kfree(control->reg_##reg->data); \ + kfree(control->reg_##reg); \ + } } while (0) + +#define CTRL_REG_PRESENCE(reg, skip, cond) \ + do { if ((cond)) {\ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: c%s addr = 0x%02x\n",\ + __func__, #reg, reg_addr);\ + reg_addr += (skip);\ + } } while (0) + +#define CTRL_REG_RESERVED_PRESENCE(reg, skip, cond) \ + do { if ((cond)) { \ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: c%s addr = 0x%02x (reserved)\n",\ + __func__, #reg, reg_addr);\ + reg_addr += skip;\ + } } while (0) + +#define QUERY_REG_READ(reg, cond)\ + do { if (cond) { \ + retval = f54->fn_ptr->read(rmi4_data,\ + reg_addr,\ + f54->query##reg.data,\ + sizeof(f54->query##reg.data));\ + if (retval < 0) { \ + dev_err(&rmi4_data->i2c_client->dev,\ + "%s: Failed to read query %s register\n",\ + #reg, __func__);\ + goto err;\ + } \ + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: q%s addr = 0x%02x, val = 0x%02x\n",\ + __func__, #reg, reg_addr, f54->query##reg.data[0]);\ + reg_addr += 1;\ + } \ + else { \ + memset(&f54->query##reg.data, 0, sizeof(f54->query##reg.data));\ + } } while (0) + +enum f54_report_types { + F54_8BIT_IMAGE = 1, + F54_16BIT_IMAGE = 2, + F54_RAW_16BIT_IMAGE = 3, + F54_HIGH_RESISTANCE = 4, + F54_TX_TO_TX_SHORT = 5, + F54_RX_TO_RX1 = 7, + F54_TRUE_BASELINE = 9, + F54_FULL_RAW_CAP_MIN_MAX = 13, + F54_RX_OPENS1 = 14, + F54_TX_OPEN = 15, + F54_TX_TO_GROUND = 16, + F54_RX_TO_RX2 = 17, + F54_RX_OPENS2 = 18, + F54_FULL_RAW_CAP = 19, + F54_FULL_RAW_CAP_RX_COUPLING_COMP = 20, + F54_SENSOR_SPEED = 22, + F54_ADC_RANGE = 23, + F54_TREX_OPENS = 24, + F54_TREX_TO_GND = 25, + F54_TREX_SHORTS = 26, + F54_HYB_ABS_RX_TX_DELTA = 59, + F54_HYB_ABS_RX_TX_RAW = 63, + INVALID_REPORT_TYPE = -1, +}; + +struct f54_query { + union { + struct { + /* query 0 */ + unsigned char num_of_rx_electrodes; + + /* query 1 */ + unsigned char num_of_tx_electrodes; + + /* query 2 */ + unsigned char f54_query2_b0__1:2; + unsigned char has_baseline:1; + unsigned char has_image8:1; + unsigned char f54_query2_b4__5:2; + unsigned char has_image16:1; + unsigned char f54_query2_b7:1; + + /* queries 3.0 and 3.1 */ + unsigned short clock_rate; + + /* query 4 */ + unsigned char touch_controller_family; + + /* query 5 */ + unsigned char has_pixel_touch_threshold_adjustment:1; + unsigned char f54_query5_b1__7:7; + + /* query 6 */ + unsigned char has_sensor_assignment:1; + unsigned char has_interference_metric:1; + unsigned char has_sense_frequency_control:1; + unsigned char has_firmware_noise_mitigation:1; + unsigned char has_ctrl11:1; + unsigned char has_two_byte_report_rate:1; + unsigned char has_one_byte_report_rate:1; + unsigned char has_relaxation_control:1; + + /* query 7 */ + unsigned char curve_compensation_mode:2; + unsigned char f54_query7_b2__7:6; + + /* query 8 */ + unsigned char f54_query8_b0:1; + unsigned char has_iir_filter:1; + unsigned char has_cmn_removal:1; + unsigned char has_cmn_maximum:1; + unsigned char has_touch_hysteresis:1; + unsigned char has_edge_compensation:1; + unsigned char has_per_frequency_noise_control:1; + unsigned char has_enhanced_stretch:1; + + /* query 9 */ + unsigned char has_force_fast_relaxation:1; + unsigned char has_multi_metric_state_machine:1; + unsigned char has_signal_clarity:1; + unsigned char has_variance_metric:1; + unsigned char has_0d_relaxation_control:1; + unsigned char has_0d_acquisition_control:1; + unsigned char has_status:1; + unsigned char has_slew_metric:1; + + /* query 10 */ + unsigned char has_h_blank:1; + unsigned char has_v_blank:1; + unsigned char has_long_h_blank:1; + unsigned char has_startup_fast_relaxation:1; + unsigned char has_esd_control:1; + unsigned char has_noise_mitigation2:1; + unsigned char has_noise_state:1; + unsigned char has_energy_ratio_relaxation:1; + + /* query 11 */ + unsigned char has_excessive_noise_reporting:1; + unsigned char has_slew_option:1; + unsigned char has_two_overhead_bursts:1; + unsigned char has_query13:1; + unsigned char has_one_overhead_burst:1; + unsigned char f54_query11_b5:1; + unsigned char has_ctrl88:1; + unsigned char has_query15:1; + } __packed; + unsigned char data[13]; + }; +}; + +struct f54_query12 { + union { + struct { + unsigned char number_of_sensing_frequencies:4; + unsigned char f54_query12_b4__7:4; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query13 { + union { + struct { + unsigned char has_ctrl86:1; + unsigned char has_ctrl87:1; + unsigned char has_ctrl87_sub0:1; + unsigned char has_ctrl87_sub1:1; + unsigned char has_ctrl87_sub2:1; + unsigned char has_cid_im:1; + unsigned char has_noise_mitigation_enh:1; + unsigned char has_rail_im:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query14 { + union { + struct { + unsigned char size_of_ctr87; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query15 { + union { + struct { + unsigned char has_ctrl90:1; + unsigned char has_traismit_strength:1; + unsigned char has_ctrl87_sub3:1; + unsigned char has_query16:1; + unsigned char has_query20:1; + unsigned char has_query21:1; + unsigned char has_query22:1; + unsigned char has_query25:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query16 { + union { + struct { + unsigned char has_query17:1; + unsigned char has_data17:1; + unsigned char has_ctrl92:1; + unsigned char has_ctrl93:1; + unsigned char has_ctrl94_query18:1; + unsigned char has_ctrl95_query19:1; + unsigned char has_ctrl99:1; + unsigned char has_ctrl100:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query17 { + union { + struct { + unsigned char q17_num_of_sense_freqs; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query18 { + union { + struct { + unsigned char has_ctrl94_sub0:1; + unsigned char has_ctrl94_sub1:1; + unsigned char has_ctrl94_sub2:1; + unsigned char has_ctrl94_sub3:1; + unsigned char has_ctrl94_sub4:1; + unsigned char has_ctrl94_sub5:1; + unsigned char has_ctrl94_sub6:1; + unsigned char has_ctrl94_sub7:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query19 { + union { + struct { + unsigned char size_of_ctrl95; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query20 { + union { + struct { + unsigned char adc_clock_divisor; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query21 { + union { + struct { + unsigned char has_abs_rx:1; + unsigned char has_abs_tx:1; + unsigned char has_ctrl91:1; + unsigned char has_ctrl96:1; + unsigned char has_ctrl97:1; + unsigned char has_ctrl98:1; + unsigned char has_data19:1; + unsigned char has_query24_data18:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query22 { + union { + struct { + unsigned char has_packed_image:1; + unsigned char has_ctrl101:1; + unsigned char has_dynamic_sense_display_ratio:1; + unsigned char has_query23:1; + unsigned char has_ctrl103_query26:1; + unsigned char has_ctrl104:1; + unsigned char has_ctrl105:1; + unsigned char has_query28:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query23 { + union { + struct { + unsigned char has_ctrl102:1; + unsigned char has_ctrl102_sub1:1; + unsigned char has_ctrl102_sub2:1; + unsigned char has_ctrl102_sub4:1; + unsigned char has_ctrl102_sub5:1; + unsigned char has_ctrl102_sub9:1; + unsigned char has_ctrl102_sub10:1; + unsigned char has_ctrl102_sub11:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query24 { + union { + struct { + unsigned char size_of_data18; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query25 { + union { + struct { + unsigned char has_ctrl106:1; + unsigned char has_ctrl102_sub12:1; + unsigned char has_ctrl107:1; + unsigned char has_ctrl108:1; + unsigned char has_ctrl109:1; + unsigned char has_data20:1; + unsigned char has_tags_for_moisture:1; + unsigned char has_query27:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query26 { + union { + struct { + unsigned char has_ctrl103_sub0:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query27 { + union { + struct { + unsigned char has_ctrl110:1; + unsigned char has_data21:1; + unsigned char has_ctrl111:1; + unsigned char has_ctrl112:1; + unsigned char has_ctrl113:1; + unsigned char has_data22:1; + unsigned char has_ctrl114:1; + unsigned char has_query29:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query28 { + union { + struct { + unsigned char has_capacitance_correction; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query29 { + union { + struct { + unsigned char has_ctrl115:1; + unsigned char has_ground_ring_options:1; + unsigned char has_lost_burst_tuning:1; + unsigned char has_aux_exvcom2_select:1; + unsigned char has_ctrl116:1; + unsigned char has_data23:1; + unsigned char has_ctrl117:1; + unsigned char has_query30:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query30 { + union { + struct { + unsigned char has_ctrl118:1; + unsigned char has_ctrl119:1; + unsigned char has_ctrl120:1; + unsigned char has_ctrl121:1; + unsigned char has_ctrl122_query31:1; + unsigned char has_ctrl123:1; + unsigned char has_ctrl124:1; + unsigned char has_query32:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query31 { + union { + struct { + unsigned char num_of_active_stylus_sensing_freqs; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query32 { + union { + struct { + unsigned char has_ctrl125:1; + unsigned char has_ctrl126:1; + unsigned char has_ctrl127:1; + unsigned char has_abs_charge_pump_disable:1; + unsigned char has_query33:1; + unsigned char has_data24:1; + unsigned char has_query34:1; + unsigned char has_query35:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query33 { + union { + struct { + unsigned char has_ctrl128:1; + unsigned char has_ctrl129:1; + unsigned char has_ctrl130:1; + unsigned char has_ctrl131:1; + unsigned char has_ctrl132:1; + unsigned char has_ctrl133:1; + unsigned char has_ctrl134:1; + unsigned char has_query36:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query34 { + union { + struct { + unsigned char max_fnm_sliding_window_width; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query35 { + union { + struct { + unsigned char has_data25:1; + unsigned char has_ctrl135:1; + unsigned char has_ctrl136:1; + unsigned char has_ctrl137:1; + unsigned char has_ctrl138:1; + unsigned char has_ctrl139:1; + unsigned char has_data26:1; + unsigned char has_ctrl140:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query36 { + union { + struct { + unsigned char has_ctrl141:1; + unsigned char has_ctrl142:1; + unsigned char has_query37:1; + unsigned char has_ctrl143:1; + unsigned char has_ctrl144:1; + unsigned char has_ctrl145:1; + unsigned char has_ctrl146:1; + unsigned char has_query38:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query38 { + union { + struct { + unsigned char has_ctrl147:1; + unsigned char has_ctrl148:1; + unsigned char has_ctrl149:1; + unsigned char has_ctrl150:1; + unsigned char has_ctrl151:1; + unsigned char has_ctrl152:1; + unsigned char has_ctrl153:1; + unsigned char has_query39:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query39 { + union { + struct { + unsigned char has_ctrl154:1; + unsigned char has_ctrl155:1; + unsigned char has_ctrl156:1; + unsigned char has_ctrl160:1; + unsigned char has_ctrl157_158:1; + unsigned char f54_q39_b5b6:2; + unsigned char has_query40:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query40 { + union { + struct { + unsigned char has_ctrl169:1; + unsigned char has_ctrl163_query41:1; + unsigned char f54_q40_b2:1; + unsigned char has_ctrl165_query42:1; + unsigned char has_ctrl166:1; + unsigned char has_ctrl167:1; + unsigned char has_ctrl168:1; + unsigned char has_query43:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query41 { + union { + struct { + unsigned char size_of_ctrl163; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query42 { + union { + struct { + unsigned char size_of_ctrl165; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query43 { + union { + struct { + unsigned char f54_q43_b0b1:2; + unsigned char has_ctrl171:1; + unsigned char has_ctrl172_query44_query45:1; + unsigned char has_ctrl173:1; + unsigned char has_ctrl174:1; + unsigned char has_ctrl175:1; + unsigned char has_query46:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query46 { + union { + struct { + unsigned char has_ctrl176:1; + unsigned char has_ctrl177_178:1; + unsigned char has_ctrl179:1; + unsigned char f54_q46_b3:1; + unsigned char has_data27:1; + unsigned char has_data28:1; + unsigned char f54_q46_b6:1; + unsigned char has_query47:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query47 { + union { + struct { + unsigned char f54_q47_b0:1; + unsigned char has_ctrl182:1; + unsigned char has_ctrl183:1; + unsigned char f54_q47_b3:1; + unsigned char has_ctrl185:1; + unsigned char has_ctrl186:1; + unsigned char has_ctrl187:1; + unsigned char has_query49:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query49 { + union { + struct { + unsigned char f54_q49_b0:1; + unsigned char has_data30:1; + unsigned char has_ctrl188:1; + unsigned char has_data31:1; + unsigned char f54_q49_b4b5:2; + unsigned char has_ctrl190:1; + unsigned char has_query50:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query50 { + union { + struct { + unsigned char f54_q50_b0b1:2; + unsigned char has_ctrl193:1; + unsigned char has_query52:1; + unsigned char f54_q50_b4b6:3; + unsigned char has_query51:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query51 { + union { + struct { + unsigned char has_signal_clarity:1; + unsigned char f54_q51_b1:1; + unsigned char has_ctrl197:1; + unsigned char has_negative_cp:1; + unsigned char f54_q51_b4:1; + unsigned char has_query53_query54_ctrl198:1; + unsigned char has_ctrl199:1; + unsigned char has_query55:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query55 { + union { + struct { + unsigned char has_query56:1; + unsigned char has_data33_data34:1; + unsigned char has_alt_report_rate:1; + unsigned char has_ctrl200:1; + unsigned char has_ctrl201_ctrl202:1; + unsigned char has_ctrl203:1; + unsigned char has_ctrl204:1; + unsigned char has_query57:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query57 { + union { + struct { + unsigned char has_ctrl205:1; + unsigned char has_ctrl206:1; + unsigned char has_usb_bulk_read:1; + unsigned char has_ctrl207:1; + unsigned char has_ctrl208:1; + unsigned char has_ctrl209:1; + unsigned char has_ctrl210:1; + unsigned char has_query58:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query58 { + union { + struct { + unsigned char has_query59:1; + unsigned char has_query60:1; + unsigned char has_ctrl211:1; + unsigned char has_ctrl212:1; + unsigned char has_hybrid_abs_tx_axis_filtering:1; + unsigned char has_hybrid_abs_tx_interpolation:1; + unsigned char has_ctrl213:1; + unsigned char has_query61:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query61 { + union { + struct { + unsigned char has_ctrl214:1; + unsigned char has_ctrl215_query62_query63:1; + unsigned char f54_query_61_b2:1; + unsigned char has_ctrl216:1; + unsigned char has_ctrl217:1; + unsigned char has_misc_host_ctrl:1; + unsigned char has_ctrl219:1; + unsigned char has_query64:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query64 { + union { + struct { + unsigned char has_ctrl101_sub1:1; + unsigned char has_ctrl220:1; + unsigned char has_ctrl221:1; + unsigned char has_ctrl222:1; + unsigned char has_ctrl219_sub1:1; + unsigned char has_ctrl103_sub3:1; + unsigned char has_ctrl224_ctrl226_ctrl227:1; + unsigned char has_query65:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query65 { + union { + struct { + unsigned char f54_q65_b0b1:2; + unsigned char has_ctrl101_sub2:1; + unsigned char f54_65_b3:1; + unsigned char has_ctrl230:1; + unsigned char has_query66_ctrl231:1; + unsigned char has_ctrl232:1; + unsigned char has_query67:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query67 { + union { + struct { + unsigned char has_abs_doze_spatial_filter_en:1; + unsigned char has_abs_doze_avg_filter_enhancement_en:1; + unsigned char has_single_display_pulse:1; + unsigned char f54_q67_b3b4:2; + unsigned char has_ctrl235_ctrl236:1; + unsigned char has_ctrl109_sub1_sub2:1; + unsigned char has_query68:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query68 { + union { + struct { + unsigned char has_ctrl237:1; + unsigned char has_ctrl238:1; + unsigned char has_ctrl238_sub1:1; + unsigned char has_ctrl238_sub2:1; + unsigned char has_ctrl239:1; + unsigned char has_freq_filter_ext:1; + unsigned char is_tddi_hic:1; + unsigned char has_query69:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query69 { + union { + struct { + unsigned char has_ctrl240sub0:1; + unsigned char has_ctrl240sub1sub2:1; + unsigned char has_ctrl240sub3:1; + unsigned char has_ctrl240sub4:1; + unsigned char f54_q69_b4b6:3; + unsigned char has_query70:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query70 { + union { + struct { + unsigned char f54_q70_b0b1:2; + unsigned char has_ctrl242:1; + unsigned char f54_q70_b3b6:4; + unsigned char has_query71:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query71 { + union { + struct { + unsigned char f54_q71_b0b6:7; + unsigned char has_query72:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query72 { + union { + struct { + unsigned char f54_q72_b0b6:7; + unsigned char has_query73:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query73 { + union { + struct { + unsigned char f54_q73_b0b6:7; + unsigned char has_query74:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query74 { + union { + struct { + unsigned char f54_q74_b0b6:7; + unsigned char has_query75:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_query75 { + union { + struct { + unsigned char f54_q75_b0b1:2; + unsigned char has_ctrl269:1; + unsigned char f54_q75_b3b7:5; + } __packed; + unsigned char data[1]; + }; +}; + +struct f54_control_0 { + union { + struct { + unsigned char no_relax:1; + unsigned char no_scan:1; + unsigned char force_fast_relaxation:1; + unsigned char startup_fast_relaxation:1; + unsigned char gesture_cancels_sfr:1; + unsigned char enable_energy_ratio_relaxation:1; + unsigned char excessive_noise_attn_enable:1; + unsigned char f54_control0_b7:1; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_1 { + union { + struct { + unsigned char bursts_per_cluster:4; + unsigned char f54_ctrl1_b4__7:4; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_2 { + union { + struct { + unsigned short saturation_cap; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_3 { + union { + struct { + unsigned char pixel_touch_threshold; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_4__6 { + union { + struct { + /* control 4 */ + unsigned char rx_feedback_cap:2; + unsigned char bias_current:2; + unsigned char f54_ctrl4_b4__7:4; + + /* control 5 */ + unsigned char low_ref_cap:2; + unsigned char low_ref_feedback_cap:2; + unsigned char low_ref_polarity:1; + unsigned char f54_ctrl5_b5__7:3; + + /* control 6 */ + unsigned char high_ref_cap:2; + unsigned char high_ref_feedback_cap:2; + unsigned char high_ref_polarity:1; + unsigned char f54_ctrl6_b5__7:3; + } __packed; + struct { + unsigned char data[3]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_7 { + union { + struct { + unsigned char cbc_cap:2; + unsigned char cbc_polarity:2; + unsigned char cbc_tx_carrier_selection:1; + unsigned char f54_ctrl7_b5__7:3; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_8__9 { + union { + struct { + /* control 8 */ + unsigned short integration_duration:10; + unsigned short f54_ctrl8_b10__15:6; + + /* control 9 */ + unsigned char reset_duration; + } __packed; + struct { + unsigned char data[3]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_10 { + union { + struct { + unsigned char noise_sensing_bursts_per_image:4; + unsigned char f54_ctrl10_b4__7:4; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_11 { + union { + struct { + unsigned short f54_ctrl11; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_12__13 { + union { + struct { + /* control 12 */ + unsigned char slow_relaxation_rate; + + /* control 13 */ + unsigned char fast_relaxation_rate; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_14 { + union { + struct { + unsigned char rxs_on_xaxis:1; + unsigned char curve_comp_on_txs:1; + unsigned char f54_ctrl14_b2__7:6; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_15n { + unsigned char sensor_rx_assignment; +}; + +struct f54_control_15 { + struct f54_control_15n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_16n { + unsigned char sensor_tx_assignment; +}; + +struct f54_control_16 { + struct f54_control_16n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_17n { + unsigned char burst_count_b8__10:3; + unsigned char disable:1; + unsigned char f54_ctrl17_b4:1; + unsigned char filter_bandwidth:3; +}; + +struct f54_control_17 { + struct f54_control_17n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_18n { + unsigned char burst_count_b0__7; +}; + +struct f54_control_18 { + struct f54_control_18n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_19n { + unsigned char stretch_duration; +}; + +struct f54_control_19 { + struct f54_control_19n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_20 { + union { + struct { + unsigned char disable_noise_mitigation:1; + unsigned char f54_ctrl20_b2__7:7; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_21 { + union { + struct { + unsigned short freq_shift_noise_threshold; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_22__26 { + union { + struct { + /* control 22 */ + unsigned char f54_ctrl22; + + /* control 23 */ + unsigned short medium_noise_threshold; + + /* control 24 */ + unsigned short high_noise_threshold; + + /* control 25 */ + unsigned char noise_density; + + /* control 26 */ + unsigned char frame_count; + } __packed; + struct { + unsigned char data[7]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_27 { + union { + struct { + unsigned char iir_filter_coef; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_28 { + union { + struct { + unsigned short quiet_threshold; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_29 { + union { + struct { + /* control 29 */ + unsigned char f54_ctrl29_b0__6:7; + unsigned char cmn_filter_disable:1; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_30 { + union { + struct { + unsigned char cmn_filter_max; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_31 { + union { + struct { + unsigned char touch_hysteresis; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_32__35 { + union { + struct { + /* control 32 */ + unsigned short rx_low_edge_comp; + + /* control 33 */ + unsigned short rx_high_edge_comp; + + /* control 34 */ + unsigned short tx_low_edge_comp; + + /* control 35 */ + unsigned short tx_high_edge_comp; + } __packed; + struct { + unsigned char data[8]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_36n { + unsigned char axis1_comp; +}; + +struct f54_control_36 { + struct f54_control_36n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_37n { + unsigned char axis2_comp; +}; + +struct f54_control_37 { + struct f54_control_37n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_38n { + unsigned char noise_control_1; +}; + +struct f54_control_38 { + struct f54_control_38n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_39n { + unsigned char noise_control_2; +}; + +struct f54_control_39 { + struct f54_control_39n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_40n { + unsigned char noise_control_3; +}; + +struct f54_control_40 { + struct f54_control_40n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_89_subpkt0 { + unsigned char c89_cid_sel_opt:2; + unsigned char c89_cid_voltage_sel:3; + unsigned char c89_byte0_b5_b7:3; + unsigned char c89_cid_im_noise_threshold_lsb; + unsigned char c89_cid_im_noise_threshold_msb; +} __packed; + +struct f54_control_89_subpkt1 { + unsigned char c89_fnm_pixel_touch_mult; + unsigned char c89_freq_scan_threshold_lsb; + unsigned char c89_freq_scan_threshold_msb; + unsigned char c89_quiet_im_threshold_lsb; + unsigned char c89_quiet_im_threshold_msb; +} __packed; + +struct f54_control_89_subpkt2 { + unsigned char c89_rail_sel_opt:2; + unsigned char c89_byte0_b3_b7:6; + unsigned char c89_rail_im_noise_threshold_lsb; + unsigned char c89_rail_im_noise_threshold_msb; + unsigned char c89_rail_im_quiet_threshold_lsb; + unsigned char c89_rail_im_quiet_threshold_msb; +} __packed; + +struct f54_control_89 { + unsigned char *data; + int size; + unsigned short address; + struct f54_control_89_subpkt0 *sp0; + struct f54_control_89_subpkt1 *sp1; + struct f54_control_89_subpkt2 *sp2; +}; + +struct f54_control_93 { + union { + struct { + unsigned char c93_freq_shift_noise_threshold_lsb; + unsigned char c93_freq_shift_noise_threshold_msb; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_95 { + struct f54_control_95n *data; + unsigned short address; + unsigned char length; +}; + +struct f54_control_99 { + union { + struct { + unsigned char c99_int_dur_lsb; + unsigned char c99_int_dur_msb; + unsigned char c99_reset_dur; + } __packed; + struct { + unsigned char data[3]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_107 { + union { + struct { + unsigned char c107_abs_int_dur; + unsigned char c107_abs_reset_dur; + unsigned char c107_abs_filter_bw; + unsigned char c107_abs_rstretch; + unsigned char c107_abs_burst_count_1; + unsigned char c107_abs_burst_count_2; + unsigned char c107_abs_stretch_dur; + unsigned char c107_abs_adc_clock_div; + unsigned char c107_abs_sub_burtst_size; + unsigned char c107_abs_trigger_delay; + } __packed; + struct { + unsigned char data[10]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_113 { + union { + struct { + unsigned char c113_en_hybrid_on_tx:1; + unsigned char c113_en_hybrid_on_rx:1; + unsigned char c113_tx_consistency_chk:1; + unsigned char c113_rx_consistency_chk:1; + unsigned char c113_en_gradient_baseline:1; + unsigned char c113_byte0_b5b7:3; + unsigned char c113_consistency_thresh; + unsigned char c113_tx_obj_thresh; + unsigned char c113_rx_obj_thresh; + unsigned char c113_tx_thermal_update_int_lsb; + unsigned char c113_tx_thermal_update_int_msb; + unsigned char c113_rx_thermal_update_int_lsb; + unsigned char c113_rx_thermal_update_int_msb; + unsigned char c113_tx_negative_thresh; + unsigned char c113_rx_negative_thresh; + } __packed; + struct { + unsigned char data[10]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_116 { + union { + struct { + unsigned char c116_im_thresh; + unsigned char c116_debounce; + unsigned char c116_quiet_im; + unsigned char c116_filter; + } __packed; + struct { + unsigned char data[4]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_137 { + union { + struct { + unsigned char c137_cmnr_adjust; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_145 { + union { + struct { + unsigned char c145_bursts_per_cluster_rx; + unsigned char c145_bursts_per_cluster_tx; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_146 { + union { + struct { + unsigned char c146_int_dur_lsb; + unsigned char c146_int_dur_msb; + unsigned char c146_reset_dur; + unsigned char c146_filter_bandwidth; + unsigned char c146_stretch_dur; + unsigned char c146_burst_count1; + unsigned char c146_burst_count2; + unsigned char c146_stretch_dur2; + unsigned char c146_adc_clock_div; + } __packed; + struct { + unsigned char data[9]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_242 { + union { + struct { + unsigned char c242_cbc_servo_disable:1; + unsigned char c242_b1b7:7; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control_269 { + union { + struct { + unsigned char c269_bsc_calibration_en:1; + unsigned char c269_b1b7:7; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_control { + struct f54_control_0 *reg_0; + struct f54_control_1 *reg_1; + struct f54_control_2 *reg_2; + struct f54_control_3 *reg_3; + struct f54_control_4__6 *reg_4__6; + struct f54_control_7 *reg_7; + struct f54_control_8__9 *reg_8__9; + struct f54_control_10 *reg_10; + struct f54_control_11 *reg_11; + struct f54_control_12__13 *reg_12__13; + struct f54_control_14 *reg_14; + struct f54_control_15 *reg_15; + struct f54_control_16 *reg_16; + struct f54_control_17 *reg_17; + struct f54_control_18 *reg_18; + struct f54_control_19 *reg_19; + struct f54_control_20 *reg_20; + struct f54_control_21 *reg_21; + struct f54_control_22__26 *reg_22__26; + struct f54_control_27 *reg_27; + struct f54_control_28 *reg_28; + struct f54_control_29 *reg_29; + struct f54_control_30 *reg_30; + struct f54_control_31 *reg_31; + struct f54_control_32__35 *reg_32__35; + struct f54_control_36 *reg_36; + struct f54_control_37 *reg_37; + struct f54_control_38 *reg_38; + struct f54_control_39 *reg_39; + struct f54_control_40 *reg_40; + struct f54_control_89 *reg_89; + struct f54_control_93 *reg_93; + struct f54_control_95 *reg_95; + struct f54_control_99 *reg_99; + struct f54_control_107 *reg_107; + struct f54_control_113 *reg_113; + struct f54_control_116 *reg_116; + struct f54_control_137 *reg_137; + struct f54_control_145 *reg_145; + struct f54_control_146 *reg_146; + struct f54_control_242 *reg_242; + struct f54_control_269 *reg_269; + struct f55_control_0 *reg_0_f55; + struct f55_control_8 *reg_8_f55; +}; + +struct f54_data_4 { + union { + struct { + unsigned char d4_sense_freq_sel:4; + unsigned char d4_baseline_sel:2; + unsigned char d4_b6:1; + unsigned char d4_inhibit_freq_shift:1; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; +struct f54_data_6 { + union { + struct { + unsigned char d6_interference_metric_lsb; + unsigned char d6_interference_metric_msb; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + + }; +}; + +struct f54_data_7_0 { + union { + struct { + unsigned char d7_current_report_rate_lsb; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_7_1 { + union { + struct { + unsigned char d7_current_report_rate_msb; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_8 { + union { + struct { + unsigned char d8_variance_metric_lsb; + unsigned char d8_variance_metric_msb; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_9 { + union { + struct { + unsigned char d9_averaged_im_lsb; + unsigned char d9_averaged_im_msb; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_10 { + union { + struct { + unsigned char d10_noise_state; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_14 { + union { + struct { + unsigned char d14_cid_im_lsb; + unsigned char d14_cid_im_msb; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_16 { + union { + struct { + unsigned char d16_freq_scan_im_lsb; + unsigned char d16_freq_scan_im_msb; + } __packed; + struct { + unsigned char data[2]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data_17 { + union { + struct { + unsigned char d17_freq:7; + unsigned char d17_inhibit_freq_shift:1; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f54_data { + struct f54_data_4 *reg_4; + struct f54_data_6 *reg_6; + struct f54_data_7_0 *reg_7_0; + struct f54_data_7_1 *reg_7_1; + struct f54_data_8 *reg_8; + struct f54_data_9 *reg_9; + struct f54_data_10 *reg_10; + struct f54_data_14 *reg_14; + struct f54_data_16 *reg_16; + struct f54_data_17 *reg_17; +}; + +struct f55_query_0_2 { + union { + struct { + /* query 0 */ + unsigned char num_of_rx_electrodes; + + /* query 1 */ + unsigned char num_of_tx_electrodes; + + /* query 2 */ + unsigned char has_sensor_assignment:1; + unsigned char has_edge_compensation:1; + unsigned char curve_compensation_mode:2; + unsigned char has_ctrl6:1; + unsigned char has_alternate_tx_assignment:1; + unsigned char f55_q2_has_single_layer_multitouch:1; + unsigned char has_query5:1; + } __packed; + unsigned char data[3]; + }; +}; + +struct f55_query_3 { + union { + struct { + unsigned char f55_q3_has_ctrl8:1; + unsigned char has_ctrl9:1; + unsigned char has_on_cell_pattern:1; + unsigned char has_data0:1; + unsigned char has_single_wide_pattern:1; + unsigned char has_mirrored_tx_pattern:1; + unsigned char has_discrete_pattern:1; + unsigned char has_query9:1; + } __packed; + unsigned char data[1]; + }; +}; + +struct f55_control_0 { + union { + struct { + unsigned char f55_c0_receivers_on_x:1; + unsigned char f55_c0_curve_compensation_on_tx:1; + unsigned char f55_c0_trx_sense:1; + unsigned char f55_c0_trx_config:1; + unsigned char f55_c0_guard_disable:1; + unsigned char f55_c0_b5_b7:3; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct f55_control_8 { + union { + struct { + unsigned char f55_c8_pattern_type; + } __packed; + struct { + unsigned char data[1]; + unsigned short address; + } __packed; + }; +}; + +struct synaptics_rmi4_fn55_desc { + unsigned short query_base_addr; + unsigned short control_base_addr; +}; + +struct f54_kobj_data { + struct kobject kobj; + struct completion kobj_unregistered; + struct bin_attribute *dev_report_data; + struct synaptics_rmi4_f54_handle *f54; +}; + +#define to_f54_kobj_data(x) container_of(x, struct f54_kobj_data, kobj) +#define to_f54_attr(x) container_of(x, struct f54_attribute, attr) + +struct synaptics_rmi4_f54_handle { + bool init_done; + bool no_auto_cal; + int status; + unsigned char intr_mask; + unsigned char intr_reg_num; + unsigned char *report_data; + unsigned short query_base_addr; + unsigned short control_base_addr; + unsigned short data_base_addr; + unsigned short command_base_addr; + unsigned short fifoindex; + unsigned int report_size; + unsigned int data_buffer_size; + enum f54_report_types report_type; + enum f54_report_types user_report_type1; + enum f54_report_types user_report_type2; + struct mutex status_mutex; + struct mutex data_mutex; + struct mutex control_mutex; + struct f54_query query; + struct f54_query12 query12; + struct f54_query13 query13; + struct f54_query14 query14; + struct f54_query15 query15; + struct f54_query16 query16; + struct f54_query17 query17; + struct f54_query18 query18; + struct f54_query19 query19; + struct f54_query20 query20; + struct f54_query21 query21; + struct f54_query22 query22; + struct f54_query23 query23; + struct f54_query24 query24; + struct f54_query25 query25; + struct f54_query26 query26; + struct f54_query27 query27; + struct f54_query28 query28; + struct f54_query29 query29; + struct f54_query30 query30; + struct f54_query31 query31; + struct f54_query32 query32; + struct f54_query33 query33; + struct f54_query34 query34; + struct f54_query35 query35; + struct f54_query36 query36; + struct f54_query38 query38; + struct f54_query39 query39; + struct f54_query40 query40; + struct f54_query41 query41; + struct f54_query42 query42; + struct f54_query43 query43; + struct f54_query46 query46; + struct f54_query47 query47; + struct f54_query49 query49; + struct f54_query50 query50; + struct f54_query51 query51; + struct f54_query55 query55; + struct f54_query57 query57; + struct f54_query58 query58; + struct f54_query61 query61; + struct f54_query64 query64; + struct f54_query65 query65; + struct f54_query67 query67; + struct f54_query68 query68; + struct f54_query69 query69; + struct f54_query70 query70; + struct f54_query71 query71; + struct f54_query72 query72; + struct f54_query73 query73; + struct f54_query74 query74; + struct f54_query75 query75; + struct f54_control control; + struct f54_data data; + struct hrtimer watchdog; + struct work_struct timeout_work; + struct delayed_work status_work; + struct workqueue_struct *status_workqueue; + struct synaptics_rmi4_exp_fn_ptr *fn_ptr; + struct synaptics_rmi4_data *rmi4_data; + struct synaptics_rmi4_fn55_desc *fn55; + struct f55_query_0_2 query_f55_0_2; + struct f55_query_3 query_f55_3; + struct wakeup_source *test_wake_lock; + struct completion remove_complete; + struct f54_kobj_data *f54_kdata; +}; + +static ssize_t f54_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct f54_attribute *attribute = to_f54_attr(attr); + struct f54_kobj_data *f54_kobj_data = to_f54_kobj_data(kobj); + + if (!attribute->show) + return -EIO; + return attribute->show(f54_kobj_data->f54, buf); +} + +static ssize_t f54_attr_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t len) +{ + struct f54_attribute *attribute = to_f54_attr(attr); + struct f54_kobj_data *f54_kobj_data = to_f54_kobj_data(kobj); + + if (!attribute->store) + return -EIO; + return attribute->store(f54_kobj_data->f54, buf, len); +} + +static const struct sysfs_ops f54_sysfs_ops = { + .show = f54_attr_show, + .store = f54_attr_store, +}; + +void f54_kobj_data_release(struct kobject *kobj) +{ + struct f54_kobj_data *f54_kobj_data; + + f54_kobj_data = to_f54_kobj_data(kobj); + dev_dbg(&f54_kobj_data->f54->rmi4_data->i2c_client->dev, + "%s: f54_kobj_data released\n", __func__); + complete(&f54_kobj_data->kobj_unregistered); +} + +static struct kobj_type f54_ktype = { + .sysfs_ops = &f54_sysfs_ops, + .release = f54_kobj_data_release, +}; + +store_prototype(reset) +show_prototype_ext(status, S_IRUGO) +show_prototype_ext(report_size, S_IRUGO) +show_prototype_ext(num_of_mapped_rx, S_IRUGO) +show_prototype_ext(num_of_mapped_tx, S_IRUGO) +show_store_prototype(no_auto_cal) +show_store_prototype_ext(report_type, S_IRUGO | S_IWUSR | S_IWGRP) +show_store_prototype_ext(user_report_type1, S_IRUGO | S_IWUSR | S_IWGRP) +show_store_prototype_ext(user_report_type2, S_IRUGO | S_IWUSR | S_IWGRP) +show_prototype_ext(user_get_report1, S_IRUGO) +show_prototype_ext(user_get_report2, S_IRUGO) +show_store_prototype(fifoindex) +store_prototype(get_report) +show_store_prototype(force_update) +store_prototype(force_cal) +show_store_prototype(enter_in_cell_test_mode) +show_prototype_ext(num_of_rx_electrodes, S_IRUGO) +show_prototype_ext(num_of_tx_electrodes, S_IRUGO) +show_prototype(has_image16) +show_prototype(has_image8) +show_prototype(has_baseline) +show_prototype(clock_rate) +show_prototype(touch_controller_family) +show_prototype(has_pixel_touch_threshold_adjustment) +show_prototype(has_sensor_assignment) +show_prototype(has_interference_metric) +show_prototype(has_sense_frequency_control) +show_prototype(has_firmware_noise_mitigation) +show_prototype(has_two_byte_report_rate) +show_prototype(has_one_byte_report_rate) +show_prototype(has_relaxation_control) +show_prototype(curve_compensation_mode) +show_prototype(has_iir_filter) +show_prototype(has_cmn_removal) +show_prototype(has_cmn_maximum) +show_prototype(has_touch_hysteresis) +show_prototype(has_edge_compensation) +show_prototype(has_per_frequency_noise_control) +show_prototype(has_enhanced_stretch) +show_prototype(has_force_fast_relaxation) +show_prototype(has_multi_metric_state_machine) +show_prototype(has_signal_clarity) +show_prototype(has_variance_metric) +show_prototype(has_0d_relaxation_control) +show_prototype(has_0d_acquisition_control) +show_prototype(has_status) +show_prototype(has_slew_metric) +show_prototype(has_h_blank) +show_prototype(has_v_blank) +show_prototype(has_long_h_blank) +show_prototype(has_startup_fast_relaxation) +show_prototype(has_esd_control) +show_prototype(has_noise_mitigation2) +show_prototype(has_noise_state) +show_prototype(has_energy_ratio_relaxation) +show_prototype(number_of_sensing_frequencies) +show_prototype(has_data17) +show_prototype(has_ctrl113) +show_prototype(has_ctrl145) +show_prototype(has_ctrl146) +show_prototype(q17_num_of_sense_freqs) +show_store_prototype(no_relax) +show_store_prototype(no_scan) +show_store_prototype(bursts_per_cluster) +show_store_prototype(saturation_cap) +show_store_prototype(pixel_touch_threshold) +show_store_prototype(rx_feedback_cap) +show_store_prototype(low_ref_cap) +show_store_prototype(low_ref_feedback_cap) +show_store_prototype(low_ref_polarity) +show_store_prototype(high_ref_cap) +show_store_prototype(high_ref_feedback_cap) +show_store_prototype(high_ref_polarity) +show_store_prototype(cbc_cap) +show_store_prototype(cbc_polarity) +show_store_prototype(cbc_tx_carrier_selection) +show_store_prototype(integration_duration) +show_store_prototype(reset_duration) +show_store_prototype(noise_sensing_bursts_per_image) +show_store_prototype(slow_relaxation_rate) +show_store_prototype(fast_relaxation_rate) +show_store_prototype(rxs_on_xaxis) +show_store_prototype(curve_comp_on_txs) +show_prototype(sensor_rx_assignment) +show_prototype(sensor_tx_assignment) +show_prototype(burst_count) +show_prototype(disable) +show_prototype(filter_bandwidth) +show_prototype(stretch_duration) +show_store_prototype(disable_noise_mitigation) +show_store_prototype(freq_shift_noise_threshold) +show_store_prototype(medium_noise_threshold) +show_store_prototype(high_noise_threshold) +show_store_prototype(noise_density) +show_store_prototype(frame_count) +show_store_prototype(iir_filter_coef) +show_store_prototype(quiet_threshold) +show_store_prototype(cmn_filter_disable) +show_store_prototype(cmn_filter_max) +show_store_prototype(touch_hysteresis) +show_store_prototype(rx_low_edge_comp) +show_store_prototype(rx_high_edge_comp) +show_store_prototype(tx_low_edge_comp) +show_store_prototype(tx_high_edge_comp) +show_store_prototype(axis1_comp) +show_store_prototype(axis2_comp) +show_prototype(noise_control_1) +show_prototype(noise_control_2) +show_prototype(noise_control_3) +show_store_prototype(d4_sense_freq_sel) +show_store_prototype(d4_baseline_sel) +show_store_prototype(d4_inhibit_freq_shift) +show_prototype(d6_interference_metric_lsb) +show_prototype(d6_interference_metric_msb) +show_prototype(d7_current_report_rate_lsb) +show_prototype(d7_current_report_rate_msb) +show_prototype(d8_variance_metric_lsb) +show_prototype(d8_variance_metric_msb) +show_prototype(d9_averaged_im_lsb) +show_prototype(d9_averaged_im_msb) +show_prototype(d10_noise_state) +show_store_prototype(d16_freq_scan_im_lsb) +show_store_prototype(d16_freq_scan_im_msb) +show_prototype(d14_cid_im_lsb) +show_prototype(d14_cid_im_msb) +show_store_prototype(d17_inhibit_freq_shift) +show_store_prototype(d17_freq) +show_store_prototype(c89_cid_sel_opt) +show_store_prototype(c89_cid_voltage_sel) +show_store_prototype(c89_cid_im_noise_threshold_lsb) +show_store_prototype(c89_cid_im_noise_threshold_msb) +show_store_prototype(c89_fnm_pixel_touch_mult) +show_store_prototype(c89_freq_scan_threshold_lsb) +show_store_prototype(c89_freq_scan_threshold_msb) +show_store_prototype(c89_quiet_im_threshold_lsb) +show_store_prototype(c89_quiet_im_threshold_msb) +show_store_prototype(c89_rail_sel_opt) +show_store_prototype(c89_rail_im_noise_threshold_lsb) +show_store_prototype(c89_rail_im_noise_threshold_msb) +show_store_prototype(c89_rail_im_quiet_threshold_lsb) +show_store_prototype(c89_rail_im_quiet_threshold_msb) +show_store_prototype(c93_freq_shift_noise_threshold_lsb) +show_store_prototype(c93_freq_shift_noise_threshold_msb) +show_store_prototype(c95_disable) +show_store_prototype(c95_filter_bw) +show_store_prototype(c95_first_burst_length_lsb) +show_store_prototype(c95_first_burst_length_msb) +show_store_prototype(c95_addl_burst_length_lsb) +show_store_prototype(c95_addl_burst_length_msb) +show_store_prototype(c95_i_stretch) +show_store_prototype(c95_r_stretch) +show_store_prototype(c95_noise_control1) +show_store_prototype(c95_noise_control2) +show_store_prototype(c95_noise_control3) +show_store_prototype(c95_noise_control4) +show_store_prototype(c99_int_dur_lsb) +show_store_prototype(c99_int_dur_msb) +show_store_prototype(c99_reset_dur) +show_store_prototype(c107_abs_int_dur) +show_store_prototype(c107_abs_reset_dur) +show_store_prototype(c107_abs_filter_bw) +show_store_prototype(c107_abs_rstretch) +show_store_prototype(c107_abs_burst_count_1) +show_store_prototype(c107_abs_burst_count_2) +show_store_prototype(c107_abs_stretch_dur) +show_store_prototype(c107_abs_adc_clock_div) +show_store_prototype(c107_abs_sub_burtst_size) +show_store_prototype(c107_abs_trigger_delay) +show_store_prototype(c113_en_hybrid_on_tx) +show_store_prototype(c113_en_hybrid_on_rx) +show_store_prototype(c113_tx_consistency_chk) +show_store_prototype(c113_rx_consistency_chk) +show_store_prototype(c113_en_gradient_baseline) +show_store_prototype(c113_consistency_thresh) +show_store_prototype(c113_tx_obj_thresh) +show_store_prototype(c113_rx_obj_thresh) +show_store_prototype(c113_tx_thermal_update_int_lsb) +show_store_prototype(c113_tx_thermal_update_int_msb) +show_store_prototype(c113_rx_thermal_update_int_lsb) +show_store_prototype(c113_rx_thermal_update_int_msb) +show_store_prototype(c113_tx_negative_thresh) +show_store_prototype(c113_rx_negative_thresh) +show_store_prototype(c116_im_thresh) +show_store_prototype(c116_debounce) +show_store_prototype(c116_quiet_im) +show_store_prototype(c116_filter) +show_store_prototype(c137_cmnr_adjust) +show_store_prototype(c145_bursts_per_cluster_rx) +show_store_prototype(c145_bursts_per_cluster_tx) +show_store_prototype(c146_int_dur_lsb) +show_store_prototype(c146_int_dur_msb) +show_store_prototype(c146_reset_dur) +show_store_prototype(c146_filter_bandwidth) +show_store_prototype(c146_stretch_dur) +show_store_prototype(c146_burst_count1) +show_store_prototype(c146_burst_count2) +show_store_prototype(c146_stretch_dur2) +show_store_prototype(c146_adc_clock_div) +show_store_prototype(c242_cbc_servo_disable) +show_store_prototype(c269_bsc_calibration_en) +show_prototype(f55_q2_has_single_layer_multitouch) +show_prototype(f55_c0_receivers_on_x) +show_prototype(f55_c8_pattern_type) + +static ssize_t synaptics_rmi4_f54_data_read(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static struct attribute *attrs[] = { + attrify(reset), + attrify(status), + attrify(report_size), + attrify(num_of_mapped_rx), + attrify(num_of_mapped_tx), + attrify(no_auto_cal), + attrify(report_type), + attrify(user_report_type1), + attrify(user_report_type2), + attrify(user_get_report1), + attrify(user_get_report2), + attrify(fifoindex), + attrify(get_report), + attrify(force_update), + attrify(force_cal), + attrify(enter_in_cell_test_mode), + attrify(num_of_rx_electrodes), + attrify(num_of_tx_electrodes), + attrify(has_image16), + attrify(has_image8), + attrify(has_baseline), + attrify(clock_rate), + attrify(touch_controller_family), + attrify(has_pixel_touch_threshold_adjustment), + attrify(has_sensor_assignment), + attrify(has_interference_metric), + attrify(has_sense_frequency_control), + attrify(has_firmware_noise_mitigation), + attrify(has_two_byte_report_rate), + attrify(has_one_byte_report_rate), + attrify(has_relaxation_control), + attrify(curve_compensation_mode), + attrify(has_iir_filter), + attrify(has_cmn_removal), + attrify(has_cmn_maximum), + attrify(has_touch_hysteresis), + attrify(has_edge_compensation), + attrify(has_per_frequency_noise_control), + attrify(has_enhanced_stretch), + attrify(has_force_fast_relaxation), + attrify(has_multi_metric_state_machine), + attrify(has_signal_clarity), + attrify(has_variance_metric), + attrify(has_0d_relaxation_control), + attrify(has_0d_acquisition_control), + attrify(has_status), + attrify(has_slew_metric), + attrify(has_h_blank), + attrify(has_v_blank), + attrify(has_long_h_blank), + attrify(has_startup_fast_relaxation), + attrify(has_esd_control), + attrify(has_noise_mitigation2), + attrify(has_noise_state), + attrify(has_energy_ratio_relaxation), + attrify(number_of_sensing_frequencies), + attrify(has_data17), + attrify(has_ctrl113), + attrify(has_ctrl145), + attrify(has_ctrl146), + attrify(q17_num_of_sense_freqs), + attrify(f55_q2_has_single_layer_multitouch), + NULL, +}; + +static struct attribute_group attr_group = GROUP(attrs); + +static struct attribute *attrs_reg_0[] = { + attrify(no_relax), + attrify(no_scan), + NULL, +}; + +static struct attribute *attrs_reg_1[] = { + attrify(bursts_per_cluster), + NULL, +}; + +static struct attribute *attrs_reg_2[] = { + attrify(saturation_cap), + NULL, +}; + +static struct attribute *attrs_reg_3[] = { + attrify(pixel_touch_threshold), + NULL, +}; + +static struct attribute *attrs_reg_4__6[] = { + attrify(rx_feedback_cap), + attrify(low_ref_cap), + attrify(low_ref_feedback_cap), + attrify(low_ref_polarity), + attrify(high_ref_cap), + attrify(high_ref_feedback_cap), + attrify(high_ref_polarity), + NULL, +}; + +static struct attribute *attrs_reg_7[] = { + attrify(cbc_cap), + attrify(cbc_polarity), + attrify(cbc_tx_carrier_selection), + NULL, +}; + +static struct attribute *attrs_reg_8__9[] = { + attrify(integration_duration), + attrify(reset_duration), + NULL, +}; + +static struct attribute *attrs_reg_10[] = { + attrify(noise_sensing_bursts_per_image), + NULL, +}; + +static struct attribute *attrs_reg_11[] = { + NULL, +}; + +static struct attribute *attrs_reg_12__13[] = { + attrify(slow_relaxation_rate), + attrify(fast_relaxation_rate), + NULL, +}; + +static struct attribute *attrs_reg_14__16[] = { + attrify(rxs_on_xaxis), + attrify(curve_comp_on_txs), + attrify(sensor_rx_assignment), + attrify(sensor_tx_assignment), + NULL, +}; + +static struct attribute *attrs_reg_17__19[] = { + attrify(burst_count), + attrify(disable), + attrify(filter_bandwidth), + attrify(stretch_duration), + NULL, +}; + +static struct attribute *attrs_reg_20[] = { + attrify(disable_noise_mitigation), + NULL, +}; + +static struct attribute *attrs_reg_21[] = { + attrify(freq_shift_noise_threshold), + NULL, +}; + +static struct attribute *attrs_reg_22__26[] = { + attrify(medium_noise_threshold), + attrify(high_noise_threshold), + attrify(noise_density), + attrify(frame_count), + NULL, +}; + +static struct attribute *attrs_reg_27[] = { + attrify(iir_filter_coef), + NULL, +}; + +static struct attribute *attrs_reg_28[] = { + attrify(quiet_threshold), + NULL, +}; + +static struct attribute *attrs_reg_29[] = { + attrify(cmn_filter_disable), + NULL, +}; + +static struct attribute *attrs_reg_30[] = { + attrify(cmn_filter_max), + NULL, +}; + +static struct attribute *attrs_reg_31[] = { + attrify(touch_hysteresis), + NULL, +}; + +static struct attribute *attrs_reg_32__35[] = { + attrify(rx_low_edge_comp), + attrify(rx_high_edge_comp), + attrify(tx_low_edge_comp), + attrify(tx_high_edge_comp), + NULL, +}; + +static struct attribute *attrs_reg_36[] = { + attrify(axis1_comp), + NULL, +}; + +static struct attribute *attrs_reg_37[] = { + attrify(axis2_comp), + NULL, +}; + +static struct attribute *attrs_reg_38__40[] = { + attrify(noise_control_1), + attrify(noise_control_2), + attrify(noise_control_3), + NULL, +}; + +static struct attribute *attrs_reg_89_subpkt0[] = { + attrify(c89_cid_sel_opt), + attrify(c89_cid_voltage_sel), + attrify(c89_cid_im_noise_threshold_lsb), + attrify(c89_cid_im_noise_threshold_msb), + NULL, +}; + +static struct attribute *attrs_reg_89_subpkt1[] = { + attrify(c89_fnm_pixel_touch_mult), + attrify(c89_freq_scan_threshold_lsb), + attrify(c89_freq_scan_threshold_msb), + attrify(c89_quiet_im_threshold_lsb), + attrify(c89_quiet_im_threshold_msb), + NULL, +}; + +static struct attribute *attrs_reg_89_subpkt2[] = { + attrify(c89_rail_sel_opt), + attrify(c89_rail_im_noise_threshold_lsb), + attrify(c89_rail_im_noise_threshold_msb), + attrify(c89_rail_im_quiet_threshold_lsb), + attrify(c89_rail_im_quiet_threshold_msb), + NULL, +}; + +static struct attribute *attrs_reg_93[] = { + attrify(c93_freq_shift_noise_threshold_lsb), + attrify(c93_freq_shift_noise_threshold_msb), + NULL, +}; + +static struct attribute *attrs_reg_95[] = { + attrify(c95_disable), + attrify(c95_filter_bw), + attrify(c95_first_burst_length_lsb), + attrify(c95_first_burst_length_msb), + attrify(c95_addl_burst_length_lsb), + attrify(c95_addl_burst_length_msb), + attrify(c95_i_stretch), + attrify(c95_r_stretch), + attrify(c95_noise_control1), + attrify(c95_noise_control2), + attrify(c95_noise_control3), + attrify(c95_noise_control4), + NULL, +}; + +static struct attribute *attrs_reg_99[] = { + attrify(c99_int_dur_lsb), + attrify(c99_int_dur_msb), + attrify(c99_reset_dur), + NULL, +}; + +static struct attribute *attrs_reg_107[] = { + attrify(c107_abs_int_dur), + attrify(c107_abs_reset_dur), + attrify(c107_abs_filter_bw), + attrify(c107_abs_rstretch), + attrify(c107_abs_burst_count_1), + attrify(c107_abs_burst_count_2), + attrify(c107_abs_stretch_dur), + attrify(c107_abs_adc_clock_div), + attrify(c107_abs_sub_burtst_size), + attrify(c107_abs_trigger_delay), + NULL, +}; + +static struct attribute *attrs_reg_113[] = { + attrify(c113_en_hybrid_on_tx), + attrify(c113_en_hybrid_on_rx), + attrify(c113_tx_consistency_chk), + attrify(c113_rx_consistency_chk), + attrify(c113_en_gradient_baseline), + attrify(c113_consistency_thresh), + attrify(c113_tx_obj_thresh), + attrify(c113_rx_obj_thresh), + attrify(c113_tx_thermal_update_int_lsb), + attrify(c113_tx_thermal_update_int_msb), + attrify(c113_rx_thermal_update_int_lsb), + attrify(c113_rx_thermal_update_int_msb), + attrify(c113_tx_negative_thresh), + attrify(c113_rx_negative_thresh), + NULL, +}; + +static struct attribute *attrs_reg_116[] = { + attrify(c116_im_thresh), + attrify(c116_debounce), + attrify(c116_quiet_im), + attrify(c116_filter), + NULL, +}; + +static struct attribute *attrs_reg_137[] = { + attrify(c137_cmnr_adjust), + NULL, +}; + +static struct attribute *attrs_reg_145[] = { + attrify(c145_bursts_per_cluster_rx), + attrify(c145_bursts_per_cluster_tx), + NULL, +}; + +static struct attribute *attrs_reg_146[] = { + attrify(c146_int_dur_lsb), + attrify(c146_int_dur_msb), + attrify(c146_reset_dur), + attrify(c146_filter_bandwidth), + attrify(c146_stretch_dur), + attrify(c146_burst_count1), + attrify(c146_burst_count2), + attrify(c146_stretch_dur2), + attrify(c146_adc_clock_div), + NULL, +}; + +static struct attribute *attrs_reg_242[] = { + attrify(c242_cbc_servo_disable), + NULL, +}; + +static struct attribute *attrs_reg_269[] = { + attrify(c269_bsc_calibration_en), + NULL, +}; + +static struct attribute *attrs_f55_c0[] = { + attrify(f55_c0_receivers_on_x), + NULL, +}; + +static struct attribute *attrs_f55_c8[] = { + attrify(f55_c8_pattern_type), + NULL, +}; + +static struct attribute_group attrs_ctrl_regs[] = { + GROUP(attrs_reg_0), + GROUP(attrs_reg_1), + GROUP(attrs_reg_2), + GROUP(attrs_reg_3), + GROUP(attrs_reg_4__6), + GROUP(attrs_reg_7), + GROUP(attrs_reg_8__9), + GROUP(attrs_reg_10), + GROUP(attrs_reg_11), + GROUP(attrs_reg_12__13), + GROUP(attrs_reg_14__16), + GROUP(attrs_reg_17__19), + GROUP(attrs_reg_20), + GROUP(attrs_reg_21), + GROUP(attrs_reg_22__26), + GROUP(attrs_reg_27), + GROUP(attrs_reg_28), + GROUP(attrs_reg_29), + GROUP(attrs_reg_30), + GROUP(attrs_reg_31), + GROUP(attrs_reg_32__35), + GROUP(attrs_reg_36), + GROUP(attrs_reg_37), + GROUP(attrs_reg_38__40), + GROUP(attrs_reg_89_subpkt0), + GROUP(attrs_reg_89_subpkt1), + GROUP(attrs_reg_89_subpkt2), + GROUP(attrs_reg_93), + GROUP(attrs_reg_95), + GROUP(attrs_reg_99), + GROUP(attrs_reg_107), + GROUP(attrs_reg_113), + GROUP(attrs_reg_116), + GROUP(attrs_reg_137), + GROUP(attrs_reg_145), + GROUP(attrs_reg_146), + GROUP(attrs_reg_242), + GROUP(attrs_reg_269), + GROUP(attrs_f55_c0), + GROUP(attrs_f55_c8), +}; + +static bool attrs_ctrl_regs_exist[ARRAY_SIZE(attrs_ctrl_regs)]; + +static struct attribute *data_attrs_reg_4[] = { + attrify(d4_sense_freq_sel), + attrify(d4_baseline_sel), + attrify(d4_inhibit_freq_shift), + NULL, +}; + +static struct attribute *data_attrs_reg_6[] = { + attrify(d6_interference_metric_lsb), + attrify(d6_interference_metric_msb), + NULL, +}; + +static struct attribute *data_attrs_reg_7_0[] = { + attrify(d7_current_report_rate_lsb), + NULL, +}; + +static struct attribute *data_attrs_reg_7_1[] = { + attrify(d7_current_report_rate_msb), + NULL, +}; + +static struct attribute *data_attrs_reg_8[] = { + attrify(d8_variance_metric_lsb), + attrify(d8_variance_metric_msb), + NULL, +}; + +static struct attribute *data_attrs_reg_9[] = { + attrify(d9_averaged_im_lsb), + attrify(d9_averaged_im_msb), + NULL, +}; + +static struct attribute *data_attrs_reg_10[] = { + attrify(d10_noise_state), + NULL, +}; + +static struct attribute *data_attrs_reg_14[] = { + attrify(d14_cid_im_lsb), + attrify(d14_cid_im_msb), + NULL, +}; + +static struct attribute *data_attrs_reg_16[] = { + attrify(d16_freq_scan_im_lsb), + attrify(d16_freq_scan_im_msb), + NULL, +}; + +static struct attribute *data_attrs_reg_17[] = { + attrify(d17_freq), + attrify(d17_inhibit_freq_shift), + NULL, +}; + +static struct attribute_group attrs_data_regs[] = { + GROUP(data_attrs_reg_4), + GROUP(data_attrs_reg_6), + GROUP(data_attrs_reg_7_0), + GROUP(data_attrs_reg_7_1), + GROUP(data_attrs_reg_8), + GROUP(data_attrs_reg_9), + GROUP(data_attrs_reg_10), + GROUP(data_attrs_reg_14), + GROUP(data_attrs_reg_16), + GROUP(data_attrs_reg_17), +}; + +static bool attrs_data_regs_exist[ARRAY_SIZE(attrs_data_regs)]; + +static inline ssize_t synaptics_dsx_test_reporting_show_error( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + dev_warn(&rmi4_data->i2c_client->dev, + "%s: Attempted to read from write-only attribute\n", __func__); + return -EPERM; +} + +static inline ssize_t synaptics_dsx_test_reporting_store_error( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + dev_warn(&rmi4_data->i2c_client->dev, + "%s: Attempted to write to read-only attribute\n", __func__); + return -EPERM; +} + +static bool is_report_type_valid(enum f54_report_types report_type) +{ + switch (report_type) { + case F54_8BIT_IMAGE: + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_HIGH_RESISTANCE: + case F54_TX_TO_TX_SHORT: + case F54_RX_TO_RX1: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP_MIN_MAX: + case F54_RX_OPENS1: + case F54_TX_OPEN: + case F54_TX_TO_GROUND: + case F54_RX_TO_RX2: + case F54_RX_OPENS2: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_COUPLING_COMP: + case F54_SENSOR_SPEED: + case F54_ADC_RANGE: + case F54_TREX_OPENS: + case F54_TREX_TO_GND: + case F54_TREX_SHORTS: + case F54_HYB_ABS_RX_TX_DELTA: + case F54_HYB_ABS_RX_TX_RAW: + return true; + break; + default: + return false; + } +} + +static int set_user_report_type(struct synaptics_rmi4_f54_handle *f54, + enum f54_report_types *user_report_type, + const char *buf, size_t count) +{ + unsigned int report_type; + int retval = 0; + retval = kstrtouint(buf, 10, &report_type); + if (retval) + goto out; + + if (!is_report_type_valid((enum f54_report_types)report_type)) { + dev_err(&f54->rmi4_data->i2c_client->dev, + "%s: Report type not supported by driver\n", + __func__); + retval = -EINVAL; + } + +out: + if (retval == 0) { + *user_report_type = report_type; + retval = count; + } else + *user_report_type = INVALID_REPORT_TYPE; + + return retval; +} + +static ssize_t synaptics_rmi4_f54_user_report_type1_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + return set_user_report_type(f54, &f54->user_report_type1, buf, count); +} + +static ssize_t synaptics_rmi4_f54_user_report_type2_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + return set_user_report_type(f54, &f54->user_report_type2, buf, count); +} + +static void set_report_size(struct synaptics_rmi4_f54_handle *f54) +{ + unsigned char rx = f54->rmi4_data->num_of_rx; + unsigned char tx = f54->rmi4_data->num_of_tx; + + switch (f54->report_type) { + case F54_8BIT_IMAGE: + f54->report_size = rx * tx; + break; + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_COUPLING_COMP: + case F54_SENSOR_SPEED: + case F54_ADC_RANGE: + f54->report_size = 2 * rx * tx; + break; + case F54_HIGH_RESISTANCE: + f54->report_size = HIGH_RESISTANCE_DATA_SIZE; + break; + case F54_TX_TO_TX_SHORT: + case F54_TX_OPEN: + case F54_TX_TO_GROUND: + f54->report_size = (tx + 7) / 8; + break; + case F54_RX_TO_RX1: + case F54_RX_OPENS1: + if (rx < tx) + f54->report_size = 2 * rx * rx; + else + f54->report_size = 2 * rx * tx; + break; + case F54_FULL_RAW_CAP_MIN_MAX: + f54->report_size = FULL_RAW_CAP_MIN_MAX_DATA_SIZE; + break; + case F54_RX_TO_RX2: + case F54_RX_OPENS2: + if (rx <= tx) + f54->report_size = 0; + else + f54->report_size = 2 * rx * (rx - tx); + break; + case F54_TREX_OPENS: + case F54_TREX_TO_GND: + case F54_TREX_SHORTS: + f54->report_size = TREX_DATA_SIZE; + break; + case F54_HYB_ABS_RX_TX_DELTA: + case F54_HYB_ABS_RX_TX_RAW: + f54->report_size = 4 * (rx + tx); + break; + default: + f54->report_size = 0; + } + pr_debug("report size %d\n", f54->report_size); + return; +} + +int set_interrupt(struct synaptics_rmi4_f54_handle *f54, bool set) +{ + int retval; + unsigned char ii; + unsigned char zero = 0x00; + unsigned char *intr_mask; + unsigned short f01_ctrl_reg; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + intr_mask = rmi4_data->intr_mask; + f01_ctrl_reg = rmi4_data->f01_ctrl_base_addr + 1 + f54->intr_reg_num; + + if (!set) { + retval = f54->fn_ptr->write(rmi4_data, + f01_ctrl_reg, + &zero, + sizeof(zero)); + if (retval < 0) + return retval; + pr_debug("cleared intr_mask\n"); + } + + for (ii = 0; ii < rmi4_data->num_of_intr_regs; ii++) { + if (intr_mask[ii] != 0x00) { + f01_ctrl_reg = rmi4_data->f01_ctrl_base_addr + 1 + ii; + if (set) { + retval = f54->fn_ptr->write(rmi4_data, + f01_ctrl_reg, + &zero, + sizeof(zero)); + if (retval < 0) + return retval; + pr_debug("cleared intr_mask\n"); + } else { + retval = f54->fn_ptr->write(rmi4_data, + f01_ctrl_reg, + &(intr_mask[ii]), + sizeof(intr_mask[ii])); + if (retval < 0) + return retval; + } + } + } + + f01_ctrl_reg = rmi4_data->f01_ctrl_base_addr + 1 + f54->intr_reg_num; + + if (set) { + retval = f54->fn_ptr->write(rmi4_data, + f01_ctrl_reg, + &f54->intr_mask, + 1); + if (retval < 0) + return retval; + pr_debug("set intr_mask = %d\n", f54->intr_mask); + } + + return 0; +} + +#ifdef WATCHDOG_HRTIMER +static void timeout_set_status(struct work_struct *work) +{ + int retval; + unsigned char command; + struct synaptics_rmi4_f54_handle *f54 = container_of(work, + struct synaptics_rmi4_f54_handle, timeout_work); + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + mutex_lock(&f54->status_mutex); + if (f54->status == STATUS_BUSY) { + retval = f54->fn_ptr->read(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + dev_dbg(&rmi4_data->i2c_client->dev,\ + "%s: read F54_Cmd0=%d, rc = %d\n", + __func__, command, retval); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read command register\n", + __func__); + } else if (command & COMMAND_GET_REPORT) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Report type not supported by FW\n", + __func__); + } else { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Get report not detected\n", + __func__); + } + f54->status = -ETIMEDOUT; + f54->report_type = INVALID_REPORT_TYPE; + f54->report_size = 0; + set_interrupt(f54, false); + PM_RELAX(f54->test_wake_lock); + } + mutex_unlock(&f54->status_mutex); + + return; +} + +static enum hrtimer_restart get_report_timeout(struct hrtimer *timer) +{ + struct synaptics_rmi4_f54_handle *f54 = container_of(timer, + struct synaptics_rmi4_f54_handle, watchdog); + + schedule_work(&(f54->timeout_work)); + + return HRTIMER_NORESTART; +} +#endif + +#ifdef RAW_HEX +static void print_raw_hex_report(struct synaptics_rmi4_f54_handle *f54) +{ + unsigned int ii; + + pr_info("%s: Report data (raw hex)\n", __func__); + + switch (f54->report_type) { + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_HIGH_RESISTANCE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP_MIN_MAX: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_COUPLING_COMP: + case F54_SENSOR_SPEED: + case F54_ADC_RANGE: + for (ii = 0; ii < f54->report_size; ii += 2) { + pr_info("%03d: 0x%02x%02x\n", + ii / 2, + f54->report_data[ii + 1], + f54->report_data[ii]); + } + break; + default: + for (ii = 0; ii < f54->report_size; ii++) + pr_info("%03d: 0x%02x\n", ii, f54->report_data[ii]); + break; + } + + return; +} +#endif + +#ifdef HUMAN_READABLE +static void print_image_report(struct synaptics_rmi4_f54_handle *f54) +{ + unsigned int ii; + unsigned int jj; + short *report_data; + + switch (f54->report_type) { + case F54_16BIT_IMAGE: + case F54_RAW_16BIT_IMAGE: + case F54_TRUE_BASELINE: + case F54_FULL_RAW_CAP: + case F54_FULL_RAW_CAP_RX_COUPLING_COMP: + pr_info("%s: Report data (image)\n", __func__); + + report_data = (short *)f54->report_data; + + for (ii = 0; ii < f54->rmi4_data->num_of_tx; ii++) { + for (jj = 0; jj < f54->rmi4_data->num_of_rx; jj++) { + if (*report_data < -64) + pr_cont("."); + else if (*report_data < 0) + pr_cont("-"); + else if (*report_data > 64) + pr_cont("*"); + else if (*report_data > 0) + pr_cont("+"); + else + pr_cont("0"); + + report_data++; + } + pr_info(""); + } + pr_info("%s: End of report\n", __func__); + break; + default: + pr_info("%s: Image not supported for report type %d\n", + __func__, f54->report_type); + } + + return; +} +#endif + +static void free_control_mem(struct synaptics_rmi4_f54_handle *f54) +{ + struct f54_control *control = &f54->control; + + kfree(control->reg_0); + kfree(control->reg_1); + kfree(control->reg_2); + kfree(control->reg_3); + kfree(control->reg_4__6); + kfree(control->reg_7); + kfree(control->reg_8__9); + kfree(control->reg_10); + kfree(control->reg_11); + kfree(control->reg_12__13); + kfree(control->reg_14); + if (control->reg_15) + kfree(control->reg_15->data); + kfree(control->reg_15); + if (control->reg_16) + kfree(control->reg_16->data); + kfree(control->reg_16); + if (control->reg_17) + kfree(control->reg_17->data); + kfree(control->reg_17); + if (control->reg_18) + kfree(control->reg_18->data); + kfree(control->reg_18); + if (control->reg_19) + kfree(control->reg_19->data); + kfree(control->reg_19); + kfree(control->reg_20); + kfree(control->reg_21); + kfree(control->reg_22__26); + kfree(control->reg_27); + kfree(control->reg_28); + kfree(control->reg_29); + kfree(control->reg_30); + kfree(control->reg_31); + kfree(control->reg_32__35); + if (control->reg_36) + kfree(control->reg_36->data); + kfree(control->reg_36); + if (control->reg_37) + kfree(control->reg_37->data); + kfree(control->reg_37); + if (control->reg_38) + kfree(control->reg_38->data); + kfree(control->reg_38); + if (control->reg_39) + kfree(control->reg_39->data); + kfree(control->reg_39); + if (control->reg_40) + kfree(control->reg_40->data); + kfree(control->reg_40); + if (control->reg_89) + kfree(control->reg_89->data); + kfree(control->reg_89); + kfree(control->reg_93); + if (control->reg_95) + kfree(control->reg_95->data); + kfree(control->reg_95); + kfree(control->reg_99); + kfree(control->reg_107); + kfree(control->reg_113); + kfree(control->reg_116); + kfree(control->reg_137); + kfree(control->reg_145); + kfree(control->reg_146); + kfree(control->reg_242); + kfree(control->reg_269); + + return; +} + +static void free_data_mem(struct synaptics_rmi4_f54_handle *f54) +{ + struct f54_data *data = &f54->data; + + kfree(data->reg_4); + kfree(data->reg_6); + kfree(data->reg_7_0); + kfree(data->reg_7_1); + kfree(data->reg_8); + kfree(data->reg_9); + kfree(data->reg_10); + kfree(data->reg_14); + kfree(data->reg_16); + kfree(data->reg_17); +} + +static void remove_sysfs(struct synaptics_rmi4_f54_handle *f54) +{ + int reg_num; + + sysfs_remove_bin_file(&f54->f54_kdata->kobj, f54->f54_kdata->dev_report_data); + + sysfs_remove_group(&f54->f54_kdata->kobj, &attr_group); + + for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs); reg_num++) + sysfs_remove_group(&f54->f54_kdata->kobj, &attrs_ctrl_regs[reg_num]); + + for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_data_regs); reg_num++) + sysfs_remove_group(&f54->f54_kdata->kobj, &attrs_data_regs[reg_num]); + + kobject_put(&f54->f54_kdata->kobj); + wait_for_completion(&f54->f54_kdata->kobj_unregistered); + kfree(f54->f54_kdata->dev_report_data); + kfree(f54->f54_kdata); + + return; +} + +static int synaptics_rmi4_f54_reset(struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + mutex_lock(&f54->status_mutex); + + rmi4_data->irq_enable(rmi4_data, false); + + retval = rmi4_data->reset_device(rmi4_data); + + rmi4_data->irq_enable(rmi4_data, true); + + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to issue reset command, error = %d\n", + __func__, retval); + return retval; + } + + f54->status = STATUS_IDLE; + + mutex_unlock(&f54->status_mutex); + + return 0; +} + +static ssize_t synaptics_rmi4_f54_reset_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned int reset; + + if (sscanf(buf, "%u", &reset) != 1) + return -EINVAL; + + if (reset != 1) + return -EINVAL; + + retval = synaptics_rmi4_f54_reset(f54); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t synaptics_rmi4_f54_status_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", f54->status); +} + +static ssize_t synaptics_rmi4_f54_report_size_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->report_size); +} + +static ssize_t synaptics_rmi4_f54_num_of_mapped_rx_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->rmi4_data->num_of_rx); +} + +static ssize_t synaptics_rmi4_f54_num_of_mapped_tx_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->rmi4_data->num_of_tx); +} + +static ssize_t synaptics_rmi4_f54_no_auto_cal_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->no_auto_cal); +} + +static ssize_t synaptics_rmi4_f54_no_auto_cal_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned char data; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting > 1) + return -EINVAL; + + retval = f54->fn_ptr->read(rmi4_data, + f54->control_base_addr, + &data, + sizeof(data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read control register\n", + __func__); + return retval; + } + + if ((data & NO_AUTO_CAL_MASK) == setting) + return count; + + data = (data & ~NO_AUTO_CAL_MASK) | (data & NO_AUTO_CAL_MASK); + + retval = f54->fn_ptr->write(rmi4_data, + f54->control_base_addr, + &data, + sizeof(data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write control register\n", + __func__); + return retval; + } + + f54->no_auto_cal = (setting == 1); + + return count; +} + +static ssize_t synaptics_rmi4_f54_report_type_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", f54->report_type); +} + +static int synaptics_rmi4_f54_report_type_set( + struct synaptics_rmi4_f54_handle *f54, + enum f54_report_types report_type) +{ + unsigned char data; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + int retval, report_type_valid = is_report_type_valid(report_type); + + if (!report_type_valid) { + f54->report_type = INVALID_REPORT_TYPE; + dev_err(&rmi4_data->i2c_client->dev, + "%s: Requested report type unsupported\n", __func__); + return -EINVAL; + } + + f54->report_type = report_type; + pr_debug("requested report type = %d\n", report_type); + + mutex_lock(&f54->status_mutex); + f54->report_type = report_type; + data = (unsigned char)report_type; + retval = f54->fn_ptr->write(rmi4_data, + f54->data_base_addr, + &data, + sizeof(data)); + mutex_unlock(&f54->status_mutex); + + if (retval < 0) + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write report type\n", __func__); + return 0; +} + +static ssize_t synaptics_rmi4_f54_report_type_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (f54->status != STATUS_BUSY) { + retval = synaptics_rmi4_f54_report_type_set(f54, + (enum f54_report_types)setting); + + if (retval < 0) + return retval; + + return count; + } else { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Previous get report still ongoing\n", + __func__); + return -EINVAL; + } +} + +static ssize_t synaptics_rmi4_f54_user_report_type1_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->user_report_type1); +} + +static ssize_t synaptics_rmi4_f54_user_report_type2_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", f54->user_report_type2); +} + +static ssize_t synaptics_rmi4_f54_fifoindex_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + int retval; + unsigned char data[2]; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = f54->fn_ptr->read(rmi4_data, + f54->data_base_addr + DATA_REPORT_INDEX_OFFSET, + data, + sizeof(data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read data registers\n", + __func__); + return retval; + } + + batohs(&f54->fifoindex, data); + + return snprintf(buf, PAGE_SIZE, "%u\n", f54->fifoindex); +} +static ssize_t synaptics_rmi4_f54_fifoindex_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned char data[2]; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + f54->fifoindex = setting; + + hstoba(data, (unsigned short)setting); + + retval = f54->fn_ptr->write(rmi4_data, + f54->data_base_addr + DATA_REPORT_INDEX_OFFSET, + data, + sizeof(data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write data registers\n", + __func__); + return retval; + } + + return count; +} + +#if defined(REPORT_STATUS_POLLING) +static int test_wait_for_command_completion( + struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + unsigned char value; + unsigned char timeout_count; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + timeout_count = 0; + do { + retval = f54->fn_ptr->read(rmi4_data, + f54->command_base_addr, + &value, + sizeof(value)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read command register\n", + __func__); + return retval; + } + + if (value == 0x00) + break; + + msleep(100); + timeout_count++; + } while (timeout_count < STATUS_WORK_INTERVAL); + + if (timeout_count == STATUS_WORK_INTERVAL) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Timed out waiting for command completion\n", + __func__); + return -ETIMEDOUT; + } + + return 0; +} +#endif + +ssize_t send_get_report_command(struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + unsigned char command = (unsigned char)COMMAND_GET_REPORT; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + mutex_lock(&f54->status_mutex); + + if (f54->status != STATUS_IDLE) { + if (f54->status != STATUS_BUSY) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Invalid status (%d)\n", + __func__, f54->status); + } else { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Previous get report still ongoing\n", + __func__); + } + mutex_unlock(&f54->status_mutex); + return -EBUSY; + } + + PM_STAY_AWAKE(f54->test_wake_lock); +#if !defined(REPORT_STATUS_POLLING) + set_interrupt(f54, true); +#endif + f54->status = STATUS_BUSY; + + retval = f54->fn_ptr->write(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + + /* unlock status mutex to allow jumping to error_exit label */ + mutex_unlock(&f54->status_mutex); + + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: sent get report command\n", __func__); + + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write get report command\n", + __func__); + goto error_exit; + } + +#if defined(REPORT_STATUS_POLLING) + /* if report status control method is polling */ + pr_debug("will be polling test report status\n"); + retval = test_wait_for_command_completion(f54); + if (retval < 0) + goto error_exit; + + /* invoke status work immediately */ + schedule_delayed_work(&f54->status_work, 0); +#else +#ifdef WATCHDOG_HRTIMER + hrtimer_start(&f54->watchdog, + ktime_set(WATCHDOG_TIMEOUT_S, 0), + HRTIMER_MODE_REL); +#endif +#endif +out: + return retval; + +error_exit: + mutex_lock(&f54->status_mutex); +#if !defined(REPORT_STATUS_POLLING) + set_interrupt(f54, false); +#endif + PM_RELAX(f54->test_wake_lock); + f54->status = retval; + mutex_unlock(&f54->status_mutex); + + goto out; +} + +static ssize_t synaptics_rmi4_f54_get_report_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return -EINVAL; + + if (!is_report_type_valid(f54->report_type)) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Invalid report type\n", + __func__); + return -EINVAL; + } + + retval = send_get_report_command(f54); + if (retval < 0) + return retval; + + return count; +} + +static ssize_t synaptics_rmi4_f54_force_update_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + int retval; + unsigned char data[1]; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = f54->fn_ptr->read(rmi4_data, + f54->command_base_addr, + data, + sizeof(data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read command 0 register\n", + __func__); + return retval; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", + (data[0] & COMMAND_FORCE_UPDATE) == COMMAND_FORCE_UPDATE); +} + +static ssize_t synaptics_rmi4_f54_force_update_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned char command; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return count; + + command = (unsigned char)COMMAND_FORCE_UPDATE; + + if (f54->status == STATUS_BUSY) + return -EBUSY; + + retval = f54->fn_ptr->write(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write force update command\n", + __func__); + return retval; + } + + return count; +} + +static ssize_t synaptics_rmi4_f54_enter_in_cell_test_mode_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + int retval; + unsigned char data[1]; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = f54->fn_ptr->read(rmi4_data, + f54->command_base_addr, + data, + sizeof(data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read command 0 register\n", + __func__); + return retval; + } + + return snprintf(buf, PAGE_SIZE, "%u\n", + (data[0] & COMMAND_ENTER_IN_CELL_TESTMODE) == + COMMAND_ENTER_IN_CELL_TESTMODE); +} + + +static ssize_t synaptics_rmi4_f54_enter_in_cell_test_mode_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned char command; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return count; + + command = (unsigned char)COMMAND_ENTER_IN_CELL_TESTMODE; + + if (f54->status == STATUS_BUSY) + return -EBUSY; + + retval = f54->fn_ptr->write(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write Enter In-Cell Test Mode command\n", + __func__); + return retval; + } + + return count; +} + + +static ssize_t synaptics_rmi4_f54_force_cal_store( + struct synaptics_rmi4_f54_handle *f54, + const char *buf, size_t count) +{ + int retval; + unsigned char command; + unsigned long setting; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + retval = sstrtoul(buf, 10, &setting); + if (retval) + return retval; + + if (setting != 1) + return count; + + command = (unsigned char)COMMAND_FORCE_CAL; + + if (f54->status == STATUS_BUSY) + return -EBUSY; + + retval = f54->fn_ptr->write(rmi4_data, + f54->command_base_addr, + &command, + sizeof(command)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write force cal command\n", + __func__); + return retval; + } + + return count; +} + +simple_show_func_unsigned(query, num_of_rx_electrodes) +simple_show_func_unsigned(query, num_of_tx_electrodes) +simple_show_func_unsigned(query, has_image16) +simple_show_func_unsigned(query, has_image8) +simple_show_func_unsigned(query, has_baseline) +simple_show_func_unsigned(query, clock_rate) +simple_show_func_unsigned(query, touch_controller_family) +simple_show_func_unsigned(query, has_pixel_touch_threshold_adjustment) +simple_show_func_unsigned(query, has_sensor_assignment) +simple_show_func_unsigned(query, has_interference_metric) +simple_show_func_unsigned(query, has_sense_frequency_control) +simple_show_func_unsigned(query, has_firmware_noise_mitigation) +simple_show_func_unsigned(query, has_two_byte_report_rate) +simple_show_func_unsigned(query, has_one_byte_report_rate) +simple_show_func_unsigned(query, has_relaxation_control) +simple_show_func_unsigned(query, curve_compensation_mode) +simple_show_func_unsigned(query, has_iir_filter) +simple_show_func_unsigned(query, has_cmn_removal) +simple_show_func_unsigned(query, has_cmn_maximum) +simple_show_func_unsigned(query, has_touch_hysteresis) +simple_show_func_unsigned(query, has_edge_compensation) +simple_show_func_unsigned(query, has_per_frequency_noise_control) +simple_show_func_unsigned(query, has_enhanced_stretch) +simple_show_func_unsigned(query, has_force_fast_relaxation) +simple_show_func_unsigned(query, has_multi_metric_state_machine) +simple_show_func_unsigned(query, has_signal_clarity) +simple_show_func_unsigned(query, has_variance_metric) +simple_show_func_unsigned(query, has_0d_relaxation_control) +simple_show_func_unsigned(query, has_0d_acquisition_control) +simple_show_func_unsigned(query, has_status) +simple_show_func_unsigned(query, has_slew_metric) +simple_show_func_unsigned(query, has_h_blank) +simple_show_func_unsigned(query, has_v_blank) +simple_show_func_unsigned(query, has_long_h_blank) +simple_show_func_unsigned(query, has_startup_fast_relaxation) +simple_show_func_unsigned(query, has_esd_control) +simple_show_func_unsigned(query, has_noise_mitigation2) +simple_show_func_unsigned(query, has_noise_state) +simple_show_func_unsigned(query, has_energy_ratio_relaxation) +simple_show_func_unsigned(query12, number_of_sensing_frequencies) +simple_show_func_unsigned(query16, has_data17) +simple_show_func_unsigned(query27, has_ctrl113) +simple_show_func_unsigned(query36, has_ctrl145) +simple_show_func_unsigned(query36, has_ctrl146) +simple_show_func_unsigned(query17, q17_num_of_sense_freqs) +show_store_func_unsigned(data, reg_4, d4_inhibit_freq_shift) +show_store_func_unsigned(data, reg_4, d4_baseline_sel) +show_store_func_unsigned(data, reg_4, d4_sense_freq_sel) +show_store_func_unsigned(data, reg_16, d16_freq_scan_im_lsb) +show_store_func_unsigned(data, reg_16, d16_freq_scan_im_msb) +show_store_func_unsigned(data, reg_17, d17_inhibit_freq_shift) +show_store_func_unsigned(data, reg_17, d17_freq) +show_func_unsigned(data, reg_6, d6_interference_metric_lsb) +show_func_unsigned(data, reg_6, d6_interference_metric_msb) +show_func_unsigned(data, reg_7_0, d7_current_report_rate_lsb) +show_func_unsigned(data, reg_7_1, d7_current_report_rate_msb) +show_func_unsigned(data, reg_8, d8_variance_metric_lsb) +show_func_unsigned(data, reg_8, d8_variance_metric_msb) +show_func_unsigned(data, reg_9, d9_averaged_im_lsb) +show_func_unsigned(data, reg_9, d9_averaged_im_msb) +show_func_unsigned(data, reg_10, d10_noise_state) +show_func_unsigned(data, reg_14, d14_cid_im_lsb) +show_func_unsigned(data, reg_14, d14_cid_im_msb) + +show_store_func_unsigned(control, reg_0, no_relax) +show_store_func_unsigned(control, reg_0, no_scan) +show_store_func_unsigned(control, reg_1, bursts_per_cluster) +show_store_func_unsigned(control, reg_2, saturation_cap) +show_store_func_unsigned(control, reg_3, pixel_touch_threshold) +show_store_func_unsigned(control, reg_4__6, rx_feedback_cap) +show_store_func_unsigned(control, reg_4__6, low_ref_cap) +show_store_func_unsigned(control, reg_4__6, low_ref_feedback_cap) +show_store_func_unsigned(control, reg_4__6, low_ref_polarity) +show_store_func_unsigned(control, reg_4__6, high_ref_cap) +show_store_func_unsigned(control, reg_4__6, high_ref_feedback_cap) +show_store_func_unsigned(control, reg_4__6, high_ref_polarity) +show_store_func_unsigned(control, reg_7, cbc_cap) +show_store_func_unsigned(control, reg_7, cbc_polarity) +show_store_func_unsigned(control, reg_7, cbc_tx_carrier_selection) +show_store_func_unsigned(control, reg_8__9, integration_duration) +show_store_func_unsigned(control, reg_8__9, reset_duration) +show_store_func_unsigned(control, reg_10, noise_sensing_bursts_per_image) +show_store_func_unsigned(control, reg_12__13, slow_relaxation_rate) +show_store_func_unsigned(control, reg_12__13, fast_relaxation_rate) +show_store_func_unsigned(control, reg_14, rxs_on_xaxis) +show_store_func_unsigned(control, reg_14, curve_comp_on_txs) +show_store_func_unsigned(control, reg_20, disable_noise_mitigation) +show_store_func_unsigned(control, reg_21, freq_shift_noise_threshold) +show_store_func_unsigned(control, reg_22__26, medium_noise_threshold) +show_store_func_unsigned(control, reg_22__26, high_noise_threshold) +show_store_func_unsigned(control, reg_22__26, noise_density) +show_store_func_unsigned(control, reg_22__26, frame_count) +show_store_func_unsigned(control, reg_27, iir_filter_coef) +show_store_func_unsigned(control, reg_28, quiet_threshold) +show_store_func_unsigned(control, reg_29, cmn_filter_disable) +show_store_func_unsigned(control, reg_30, cmn_filter_max) +show_store_func_unsigned(control, reg_31, touch_hysteresis) +show_store_func_unsigned(control, reg_32__35, rx_low_edge_comp) +show_store_func_unsigned(control, reg_32__35, rx_high_edge_comp) +show_store_func_unsigned(control, reg_32__35, tx_low_edge_comp) +show_store_func_unsigned(control, reg_32__35, tx_high_edge_comp) + +show_replicated_func_unsigned(control, reg_15, sensor_rx_assignment) +show_replicated_func_unsigned(control, reg_16, sensor_tx_assignment) +show_replicated_func_unsigned(control, reg_17, disable) +show_replicated_func_unsigned(control, reg_17, filter_bandwidth) +show_replicated_func_unsigned(control, reg_19, stretch_duration) +show_replicated_func_unsigned(control, reg_38, noise_control_1) +show_replicated_func_unsigned(control, reg_39, noise_control_2) +show_replicated_func_unsigned(control, reg_40, noise_control_3) + +show_store_replicated_func_unsigned(control, reg_36, axis1_comp) +show_store_replicated_func_unsigned(control, reg_37, axis2_comp) + +show_store_subpkt_func_unsigned(control, reg_89, 0, c89_cid_sel_opt) +show_store_subpkt_func_unsigned(control, reg_89, 0, c89_cid_voltage_sel) +show_store_subpkt_func_unsigned(control, reg_89, 0, + c89_cid_im_noise_threshold_lsb) +show_store_subpkt_func_unsigned(control, reg_89, 0, + c89_cid_im_noise_threshold_msb) + +show_store_subpkt_func_unsigned(control, reg_89, 1, c89_fnm_pixel_touch_mult) +show_store_subpkt_func_unsigned(control, reg_89, 1, c89_freq_scan_threshold_lsb) +show_store_subpkt_func_unsigned(control, reg_89, 1, c89_freq_scan_threshold_msb) +show_store_subpkt_func_unsigned(control, reg_89, 1, c89_quiet_im_threshold_lsb) +show_store_subpkt_func_unsigned(control, reg_89, 1, c89_quiet_im_threshold_msb) + +show_store_subpkt_func_unsigned(control, reg_89, 2, c89_rail_sel_opt); +show_store_subpkt_func_unsigned(control, reg_89, 2, + c89_rail_im_noise_threshold_lsb); +show_store_subpkt_func_unsigned(control, reg_89, 2, + c89_rail_im_noise_threshold_msb); +show_store_subpkt_func_unsigned(control, reg_89, 2, + c89_rail_im_quiet_threshold_lsb); +show_store_subpkt_func_unsigned(control, reg_89, 2, + c89_rail_im_quiet_threshold_msb); + +show_store_func_unsigned(control, reg_93, c93_freq_shift_noise_threshold_lsb) +show_store_func_unsigned(control, reg_93, c93_freq_shift_noise_threshold_msb) + +show_store_replicated_func_unsigned(control, reg_95, c95_disable) +show_store_replicated_func_unsigned(control, reg_95, c95_filter_bw) +show_store_replicated_func_unsigned(control, reg_95, c95_first_burst_length_lsb) +show_store_replicated_func_unsigned(control, reg_95, c95_first_burst_length_msb) +show_store_replicated_func_unsigned(control, reg_95, c95_addl_burst_length_lsb) +show_store_replicated_func_unsigned(control, reg_95, c95_addl_burst_length_msb) +show_store_replicated_func_unsigned(control, reg_95, c95_i_stretch) +show_store_replicated_func_unsigned(control, reg_95, c95_r_stretch) +show_store_replicated_func_unsigned(control, reg_95, c95_noise_control1) +show_store_replicated_func_unsigned(control, reg_95, c95_noise_control2) +show_store_replicated_func_unsigned(control, reg_95, c95_noise_control3) +show_store_replicated_func_unsigned(control, reg_95, c95_noise_control4) + +show_store_func_unsigned(control, reg_99, c99_int_dur_lsb) +show_store_func_unsigned(control, reg_99, c99_int_dur_msb) +show_store_func_unsigned(control, reg_99, c99_reset_dur) + +show_store_func_unsigned(control, reg_107, c107_abs_int_dur) +show_store_func_unsigned(control, reg_107, c107_abs_reset_dur) +show_store_func_unsigned(control, reg_107, c107_abs_filter_bw) +show_store_func_unsigned(control, reg_107, c107_abs_rstretch) +show_store_func_unsigned(control, reg_107, c107_abs_burst_count_1) +show_store_func_unsigned(control, reg_107, c107_abs_burst_count_2) +show_store_func_unsigned(control, reg_107, c107_abs_stretch_dur) +show_store_func_unsigned(control, reg_107, c107_abs_adc_clock_div) +show_store_func_unsigned(control, reg_107, c107_abs_sub_burtst_size) +show_store_func_unsigned(control, reg_107, c107_abs_trigger_delay) + +show_store_func_unsigned(control, reg_113, c113_en_hybrid_on_tx) +show_store_func_unsigned(control, reg_113, c113_en_hybrid_on_rx) +show_store_func_unsigned(control, reg_113, c113_tx_consistency_chk) +show_store_func_unsigned(control, reg_113, c113_rx_consistency_chk) +show_store_func_unsigned(control, reg_113, c113_en_gradient_baseline) +show_store_func_unsigned(control, reg_113, c113_consistency_thresh) +show_store_func_unsigned(control, reg_113, c113_tx_obj_thresh) +show_store_func_unsigned(control, reg_113, c113_rx_obj_thresh) +show_store_func_unsigned(control, reg_113, c113_tx_thermal_update_int_lsb) +show_store_func_unsigned(control, reg_113, c113_tx_thermal_update_int_msb) +show_store_func_unsigned(control, reg_113, c113_rx_thermal_update_int_lsb) +show_store_func_unsigned(control, reg_113, c113_rx_thermal_update_int_msb) +show_store_func_unsigned(control, reg_113, c113_tx_negative_thresh) +show_store_func_unsigned(control, reg_113, c113_rx_negative_thresh) + +show_store_func_unsigned(control, reg_116, c116_im_thresh) +show_store_func_unsigned(control, reg_116, c116_debounce) +show_store_func_unsigned(control, reg_116, c116_quiet_im) +show_store_func_unsigned(control, reg_116, c116_filter) + +show_store_func_unsigned(control, reg_137, c137_cmnr_adjust) + +show_store_func_unsigned(control, reg_145, c145_bursts_per_cluster_rx) +show_store_func_unsigned(control, reg_145, c145_bursts_per_cluster_tx) + +show_store_func_unsigned(control, reg_146, c146_int_dur_lsb) +show_store_func_unsigned(control, reg_146, c146_int_dur_msb) +show_store_func_unsigned(control, reg_146, c146_reset_dur) +show_store_func_unsigned(control, reg_146, c146_filter_bandwidth) +show_store_func_unsigned(control, reg_146, c146_stretch_dur) +show_store_func_unsigned(control, reg_146, c146_burst_count1) +show_store_func_unsigned(control, reg_146, c146_burst_count2) +show_store_func_unsigned(control, reg_146, c146_stretch_dur2) +show_store_func_unsigned(control, reg_146, c146_adc_clock_div) + +show_store_func_unsigned(control, reg_242, c242_cbc_servo_disable) +show_store_func_unsigned(control, reg_269, c269_bsc_calibration_en) + +simple_show_func_unsigned(query_f55_0_2, f55_q2_has_single_layer_multitouch) +show_func_unsigned(control, reg_0_f55, f55_c0_receivers_on_x) +show_func_unsigned(control, reg_8_f55, f55_c8_pattern_type) + +static ssize_t synaptics_rmi4_f54_burst_count_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + int retval; + int size = 0; + unsigned char ii; + unsigned char *temp; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + mutex_lock(&f54->control_mutex); + + retval = f54->fn_ptr->read(rmi4_data, + f54->control.reg_17->address, + (unsigned char *)f54->control.reg_17->data, + f54->control.reg_17->length); + if (retval < 0) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Failed to read control reg_17\n", + __func__); + } + + retval = f54->fn_ptr->read(rmi4_data, + f54->control.reg_18->address, + (unsigned char *)f54->control.reg_18->data, + f54->control.reg_18->length); + if (retval < 0) { + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: Failed to read control reg_18\n", + __func__); + } + + mutex_unlock(&f54->control_mutex); + + temp = buf; + + for (ii = 0; ii < f54->control.reg_17->length; ii++) { + retval = snprintf(temp, PAGE_SIZE - size, "%u ", (1 << 8) * + f54->control.reg_17->data[ii].burst_count_b8__10 + + f54->control.reg_18->data[ii].burst_count_b0__7); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Faild to write output\n", + __func__); + return retval; + } + size += retval; + temp += retval; + } + + retval = snprintf(temp, PAGE_SIZE - size, "\n"); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Faild to write null terminator\n", + __func__); + return retval; + } + + return size + retval; +} + +static ssize_t synaptics_rmi4_f54_user_get_report_show( + struct synaptics_rmi4_f54_handle *f54, + char *buf, enum f54_report_types report_type) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + if (f54->status != STATUS_IDLE) { + if (f54->status == STATUS_BUSY) + dev_err(&rmi4_data->i2c_client->dev, + "%s: WARNING: Resetting in busy state\n", + __func__); + + retval = synaptics_rmi4_f54_reset(f54); + if (retval < 0) + goto out; + } + + retval = synaptics_rmi4_f54_report_type_set(f54, report_type); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to set report type\n", __func__); + goto out; + } + + retval = send_get_report_command(f54); + +out: + retval = scnprintf(buf, PAGE_SIZE, "%d\n", retval); + return retval; +} + +static ssize_t synaptics_rmi4_f54_user_get_report1_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return synaptics_rmi4_f54_user_get_report_show(f54, + buf, f54->user_report_type1); +} + +static ssize_t synaptics_rmi4_f54_user_get_report2_show( + struct synaptics_rmi4_f54_handle *f54, char *buf) +{ + return synaptics_rmi4_f54_user_get_report_show(f54, + buf, f54->user_report_type2); +} + +static ssize_t synaptics_rmi4_f54_data_read(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + struct synaptics_rmi4_f54_handle *f54 = attributes->private; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + mutex_lock(&f54->data_mutex); + + if (!f54->report_data || !f54->report_size) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Report type %d data not available\n", + __func__, f54->report_type); + mutex_unlock(&f54->data_mutex); + return -EINVAL; + } + if (pos > f54->report_size || pos < 0) { + mutex_unlock(&f54->data_mutex); + return 0; + } + + count = min_t(loff_t, count, f54->report_size - pos); + memcpy(buf, f54->report_data + pos, count); + mutex_unlock(&f54->data_mutex); + pr_debug("copied %zu bytes to the caller's buffer\n", count); + return count; +} + +static int synaptics_rmi4_f54_set_sysfs(struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + int reg_num; + int dreg_num; + struct bin_attribute *dev_report_data; + struct f54_kobj_data *f54_kobj_data; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + f54_kobj_data = kzalloc(sizeof(*f54_kobj_data), GFP_KERNEL); + if (!f54_kobj_data) + goto exit; + + dev_report_data = kzalloc(sizeof(struct bin_attribute), GFP_KERNEL); + if (!dev_report_data) + goto exit_1; + + dev_report_data->attr.name = "report_data"; + dev_report_data->attr.mode = S_IRUGO; + dev_report_data->read = synaptics_rmi4_f54_data_read; + dev_report_data->private = (void *)f54; + + f54_kobj_data->f54 = f54; + f54_kobj_data->dev_report_data = dev_report_data; + + init_completion(&f54_kobj_data->kobj_unregistered); + f54->f54_kdata = f54_kobj_data; + + retval = kobject_init_and_add(&f54_kobj_data->kobj, &f54_ktype, + &rmi4_data->i2c_client->dev.kobj, "f54"); + if (retval) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs directory\n", + __func__); + goto exit_1; + } + + retval = sysfs_create_bin_file(&f54_kobj_data->kobj, dev_report_data); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs bin file\n", + __func__); + goto exit_2; + } + + retval = sysfs_create_group(&f54_kobj_data->kobj, &attr_group); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + goto exit_3; + } + + for (reg_num = 0; reg_num < ARRAY_SIZE(attrs_ctrl_regs); reg_num++) { + if (attrs_ctrl_regs_exist[reg_num]) { + retval = sysfs_create_group(&f54_kobj_data->kobj, + &attrs_ctrl_regs[reg_num]); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs attributes\n", + __func__); + goto exit_4; + } + } + } + + for (dreg_num = 0; dreg_num < ARRAY_SIZE(attrs_data_regs); dreg_num++) { + if (attrs_data_regs_exist[dreg_num]) { + retval = sysfs_create_group(&f54_kobj_data->kobj, + &attrs_data_regs[dreg_num]); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs attributes for data register %d\n", + __func__, dreg_num); + goto exit_5; + } + } + } + + return 0; + +exit_5: + for (dreg_num--; dreg_num >= 0; dreg_num--) + sysfs_remove_group(&f54_kobj_data->kobj, &attrs_data_regs[dreg_num]); + +exit_4: + for (reg_num--; reg_num >= 0; reg_num--) + sysfs_remove_group(&f54_kobj_data->kobj, &attrs_ctrl_regs[reg_num]); + + sysfs_remove_group(&f54_kobj_data->kobj, &attr_group); + +exit_3: + sysfs_remove_bin_file(&f54_kobj_data->kobj, dev_report_data); + +exit_2: + kobject_put(&f54_kobj_data->kobj); + +exit_1: + kfree(dev_report_data); + kfree(f54->f54_kdata); +exit: + return -ENODEV; +} + +/* + * Fill in base register address and offset of F54 + * control registers to allow run time patching + */ +static int synaptics_rmi4_scan_f54_ctrl_reg_info( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *f54_ctrl_regs) +{ + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + int ii, error = f54_ctrl_regs->nr_regs; + unsigned char *data; + struct synaptics_rmi4_packet_reg *reg; + struct synaptics_rmi4_subpkt *subpkt; + + f54_ctrl_regs->base_addr = f54->control_base_addr; + for (ii = 0; ii < f54_ctrl_regs->nr_regs; ii++) { + reg = &f54_ctrl_regs->regs[ii]; + subpkt = ®->subpkt[0]; + if (reg->r_number == 0 && f54->control.reg_0) { + data = kzalloc(sizeof(f54->control.reg_0->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->control.reg_0->address - + f54->control_base_addr; + reg->size = sizeof(f54->control.reg_0->data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + + if (reg->r_number == 2 && f54->control.reg_2) { + data = kzalloc(sizeof(f54->control.reg_2->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->control.reg_2->address - + f54->control_base_addr; + reg->size = 2; + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + + if (reg->r_number == 95 && f54->control.reg_95) { + int jj, num_of_subpkts; + data = kzalloc(f54->control.reg_95->length, GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->control.reg_95->address - + f54->control_base_addr; + reg->size = f54->control.reg_95->length; + reg->data = data; + /* not going over the number of predefined subpackets */ + num_of_subpkts = min((int)reg->nr_subpkts, + (int)(f54->control.reg_95->length / + subpkt->size)); + for (jj = 0; jj < num_of_subpkts; jj++) { + subpkt = ®->subpkt[jj]; + subpkt->present = true; + subpkt->offset = jj * subpkt->size; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: subpkt[%d], off = %d, sz = %d\n", __func__, + jj, subpkt->offset, subpkt->size); + } + error--; + } + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: reg[%d], addr = 0x%x, off = %d, sz = %d\n", __func__, + reg->r_number, f54->control_base_addr+reg->offset, + reg->offset, reg->size); + } + dev_dbg(&rmi4_data->i2c_client->dev, "%s: rc = %d\n", __func__, error); + return error; +} + +/* + * Fill in base register address and offset of F54 + * command register to allow run time patching + */ +static int synaptics_rmi4_scan_f54_cmd_reg_info( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *f54_cmd_regs) +{ + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + unsigned char *data; + struct synaptics_rmi4_packet_reg *reg = f54_cmd_regs->regs; + struct synaptics_rmi4_subpkt *subpkt = ®->subpkt[0]; + + f54_cmd_regs->base_addr = f54->command_base_addr; + data = kzalloc(1, GFP_KERNEL); + if (!data) + return -ENOMEM; + reg->offset = 0; + reg->size = 1; + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: reg[%d], addr = 0x%x, off = %d, sz = %d\n", __func__, + reg->r_number, f54->command_base_addr+reg->offset, + reg->offset, reg->size); + return 0; +} + +/* + * Fill in base register address and offset of F54 query + * register 12 to allow run time access + */ +static int synaptics_rmi4_scan_f54_query_reg_info( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *f54_query_regs) +{ + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + struct synaptics_rmi4_packet_reg *reg = f54_query_regs->regs; + struct synaptics_rmi4_subpkt *subpkt = ®->subpkt[0]; + unsigned char *data = kzalloc(sizeof(f54->query12.data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + f54_query_regs->base_addr = f54->query_base_addr; + /* need an offset off of base address here */ + reg->offset = 12; + reg->size = sizeof(f54->query12.data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: reg[%d], addr = 0x%x, off = %d, sz = %d\n", __func__, + reg->r_number, f54->query_base_addr+reg->offset, + reg->offset, reg->size); + return 0; +} + +/* + * Fill in base register address and offset of F54 data + * registers 10 & 17 to allow run time access + */ +static int synaptics_rmi4_scan_f54_data_reg_info( + struct synaptics_rmi4_data *rmi4_data, + struct synaptics_rmi4_func_packet_regs *f54_data_regs) +{ + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + int ii, error = f54_data_regs->nr_regs; + unsigned char *data; + struct synaptics_rmi4_subpkt *subpkt; + struct synaptics_rmi4_packet_reg *reg; + + f54_data_regs->base_addr = f54->data_base_addr; + for (ii = 0; ii < f54_data_regs->nr_regs; ii++) { + reg = &f54_data_regs->regs[ii]; + if (reg->r_number == 6 && f54->data.reg_6) { + subpkt = ®->subpkt[0]; + data = kzalloc(sizeof(f54->data.reg_6->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->data.reg_6->address - + f54->data_base_addr; + reg->size = sizeof(f54->data.reg_6->data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + + if (reg->r_number == 10 && f54->data.reg_10) { + subpkt = ®->subpkt[0]; + data = kzalloc(sizeof(f54->data.reg_10->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->data.reg_10->address - + f54->data_base_addr; + reg->size = sizeof(f54->data.reg_10->data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + + if (reg->r_number == 14 && f54->data.reg_14) { + subpkt = ®->subpkt[0]; + data = kzalloc(sizeof(f54->data.reg_14->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->data.reg_14->address - + f54->data_base_addr; + reg->size = sizeof(f54->data.reg_14->data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + + if (reg->r_number == 16 && f54->data.reg_16) { + subpkt = ®->subpkt[0]; + data = kzalloc(sizeof(f54->data.reg_16->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->data.reg_16->address - + f54->data_base_addr; + reg->size = sizeof(f54->data.reg_16->data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + + if (reg->r_number == 17 && f54->data.reg_17) { + subpkt = ®->subpkt[0]; + data = kzalloc(sizeof(f54->data.reg_17->data), + GFP_KERNEL); + if (!data) + return -ENOMEM; + /* need an offset off of base address here */ + reg->offset = f54->data.reg_17->address - + f54->data_base_addr; + reg->size = sizeof(f54->data.reg_17->data); + reg->data = data; + subpkt->present = true; + subpkt->offset = 0; + error--; + } + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: reg[%d], addr = 0x%x, off = %d, sz = %d\n", __func__, + reg->r_number, f54->data_base_addr+reg->offset, + reg->offset, reg->size); + } + dev_dbg(&rmi4_data->i2c_client->dev, "%s: rc = %d\n", __func__, error); + return error ? -ENOSYS : error; +} + +static int synaptics_rmi4_f54_set_ctrl( + struct synaptics_rmi4_f54_handle *f54) +{ + unsigned char length; + unsigned char reg_num = 0; + unsigned short reg_addr = f54->control_base_addr; + struct f54_control *control = &f54->control; + struct f54_query *query = &f54->query; + struct f54_query12 *query12 = &f54->query12; + struct f54_query13 *query13 = &f54->query13; + struct f54_query15 *query15 = &f54->query15; + struct f54_query16 *query16 = &f54->query16; + struct f54_query17 *query17 = &f54->query17; + struct f54_query21 *query21 = &f54->query21; + struct f54_query22 *query22 = &f54->query22; + struct f54_query23 *query23 = &f54->query23; + struct f54_query25 *query25 = &f54->query25; + struct f54_query27 *query27 = &f54->query27; + struct f54_query29 *query29 = &f54->query29; + struct f54_query30 *query30 = &f54->query30; + struct f54_query32 *query32 = &f54->query32; + struct f54_query33 *query33 = &f54->query33; + struct f54_query35 *query35 = &f54->query35; + struct f54_query36 *query36 = &f54->query36; + struct f54_query38 *query38 = &f54->query38; + struct f54_query39 *query39 = &f54->query39; + struct f54_query40 *query40 = &f54->query40; +// struct f54_query41 *query41 = &f54->query41; +// struct f54_query42 *query42 = &f54->query42; + struct f54_query43 *query43 = &f54->query43; + struct f54_query46 *query46 = &f54->query46; + /* BSC control access */ + struct f54_query47 *query47 = &f54->query47; + struct f54_query49 *query49 = &f54->query49; + struct f54_query50 *query50 = &f54->query50; + struct f54_query51 *query51 = &f54->query51; + struct f54_query55 *query55 = &f54->query55; + struct f54_query57 *query57 = &f54->query57; + struct f54_query58 *query58 = &f54->query58; + struct f54_query61 *query61 = &f54->query61; + struct f54_query64 *query64 = &f54->query64; + struct f54_query65 *query65 = &f54->query65; + struct f54_query67 *query67 = &f54->query67; + struct f54_query68 *query68 = &f54->query68; + struct f54_query69 *query69 = &f54->query69; + struct f54_query70 *query70 = &f54->query70; +// struct f54_query71 *query71 = &f54->query71; +// struct f54_query72 *query72 = &f54->query72; +// struct f54_query73 *query73 = &f54->query73; +// struct f54_query74 *query74 = &f54->query74; + struct f54_query75 *query75 = &f54->query75; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + unsigned char num_of_sensing_freqs; + + num_of_sensing_freqs = f54->query12.number_of_sensing_frequencies; + + CTRL_REG_ADD(0, 1, true); + CTRL_REG_ADD(1, 1, ((f54->query.touch_controller_family == 0) || + (f54->query.touch_controller_family == 1))); + CTRL_REG_ADD(2, 2, true); + CTRL_REG_ADD(3, 1, (f54->query.has_pixel_touch_threshold_adjustment == 1)); + CTRL_REG_ADD(4__6, 3, ((f54->query.touch_controller_family == 0) || + (f54->query.touch_controller_family == 1))); + CTRL_REG_ADD(7, 1, (f54->query.touch_controller_family == 1)); + CTRL_REG_ADD(8__9, 3, ((f54->query.touch_controller_family == 0) || + (f54->query.touch_controller_family == 1))); + CTRL_REG_ADD(10, 1, f54->query.has_interference_metric); + CTRL_REG_ADD(11, 2, f54->query.has_ctrl11); + CTRL_REG_ADD(12__13, 2, f54->query.has_relaxation_control); + + /* controls 14 15 16 */ + if (f54->query.has_sensor_assignment == 1) { + attrs_ctrl_regs_exist[reg_num] = true; + control->reg_14 = kzalloc(sizeof(*(control->reg_14)), + GFP_KERNEL); + if (!control->reg_14) + goto exit_no_mem; + control->reg_14->address = reg_addr; + reg_addr += sizeof(control->reg_14->data); + + control->reg_15 = kzalloc(sizeof(*(control->reg_15)), + GFP_KERNEL); + if (!control->reg_15) + goto exit_no_mem; + control->reg_15->length = f54->query.num_of_rx_electrodes; + control->reg_15->data = kzalloc(control->reg_15->length * + sizeof(*(control->reg_15->data)), GFP_KERNEL); + if (!control->reg_15->data) + goto exit_no_mem; + control->reg_15->address = reg_addr; + reg_addr += control->reg_15->length; + + control->reg_16 = kzalloc(sizeof(*(control->reg_16)), + GFP_KERNEL); + if (!control->reg_16) + goto exit_no_mem; + control->reg_16->length = f54->query.num_of_tx_electrodes; + control->reg_16->data = kzalloc(control->reg_16->length * + sizeof(*(control->reg_16->data)), GFP_KERNEL); + if (!control->reg_16->data) + goto exit_no_mem; + control->reg_16->address = reg_addr; + reg_addr += control->reg_16->length; + } + reg_num++; + + /* controls 17 18 19 */ + if (f54->query.has_sense_frequency_control == 1) { + attrs_ctrl_regs_exist[reg_num] = true; + control->reg_17 = kzalloc(sizeof(*(control->reg_17)), + GFP_KERNEL); + if (!control->reg_17) + goto exit_no_mem; + control->reg_17->length = num_of_sensing_freqs; + control->reg_17->data = kzalloc(num_of_sensing_freqs * + sizeof(*(control->reg_17->data)), GFP_KERNEL); + if (!control->reg_17->data) + goto exit_no_mem; + control->reg_17->address = reg_addr; + reg_addr += num_of_sensing_freqs; + + control->reg_18 = kzalloc(sizeof(*(control->reg_18)), + GFP_KERNEL); + if (!control->reg_18) + goto exit_no_mem; + control->reg_18->length = num_of_sensing_freqs; + control->reg_18->data = kzalloc(num_of_sensing_freqs * + sizeof(*(control->reg_18->data)), GFP_KERNEL); + if (!control->reg_18->data) + goto exit_no_mem; + control->reg_18->address = reg_addr; + reg_addr += num_of_sensing_freqs; + + control->reg_19 = kzalloc(sizeof(*(control->reg_19)), + GFP_KERNEL); + if (!control->reg_19) + goto exit_no_mem; + control->reg_19->length = num_of_sensing_freqs; + control->reg_19->data = kzalloc(num_of_sensing_freqs * + sizeof(*(control->reg_19->data)), GFP_KERNEL); + if (!control->reg_19->data) + goto exit_no_mem; + control->reg_19->address = reg_addr; + reg_addr += num_of_sensing_freqs; + } + reg_num++; + + CTRL_REG_ADD(20, 1, true); + CTRL_REG_ADD(21, 2, f54->query.has_sense_frequency_control); + CTRL_REG_ADD(22__26, 7, f54->query.has_firmware_noise_mitigation); + CTRL_REG_ADD(27, 1, f54->query.has_iir_filter); + CTRL_REG_ADD(28, 2, f54->query.has_firmware_noise_mitigation); + CTRL_REG_ADD(29, 1, f54->query.has_cmn_removal); + CTRL_REG_ADD(30, 1, f54->query.has_cmn_maximum); + CTRL_REG_ADD(31, 1, f54->query.has_touch_hysteresis); + CTRL_REG_ADD(32__35, 8, f54->query.has_edge_compensation); + + /* control 36 */ + if ((f54->query.curve_compensation_mode == 1) || + (f54->query.curve_compensation_mode == 2)) { + attrs_ctrl_regs_exist[reg_num] = true; + + if (f54->query.curve_compensation_mode == 1) { + length = max(f54->query.num_of_rx_electrodes, + f54->query.num_of_tx_electrodes); + } else if (f54->query.curve_compensation_mode == 2) { + length = f54->query.num_of_rx_electrodes; + } + + control->reg_36 = kzalloc(sizeof(*(control->reg_36)), + GFP_KERNEL); + if (!control->reg_36) + goto exit_no_mem; + control->reg_36->length = length; + control->reg_36->data = kzalloc(length * + sizeof(*(control->reg_36->data)), GFP_KERNEL); + if (!control->reg_36->data) + goto exit_no_mem; + control->reg_36->address = reg_addr; + reg_addr += length; + } + reg_num++; + + /* control 37 */ + if (f54->query.curve_compensation_mode == 2) { + attrs_ctrl_regs_exist[reg_num] = true; + + control->reg_37 = kzalloc(sizeof(*(control->reg_37)), + GFP_KERNEL); + if (!control->reg_37) + goto exit_no_mem; + control->reg_37->length = f54->query.num_of_tx_electrodes; + control->reg_37->data = kzalloc(control->reg_37->length * + sizeof(*(control->reg_37->data)), GFP_KERNEL); + if (!control->reg_37->data) + goto exit_no_mem; + + control->reg_37->address = reg_addr; + reg_addr += control->reg_37->length; + } + reg_num++; + + /* controls 38 39 40 */ + if (f54->query.has_per_frequency_noise_control == 1) { + attrs_ctrl_regs_exist[reg_num] = true; + + control->reg_38 = kzalloc(sizeof(*(control->reg_38)), + GFP_KERNEL); + if (!control->reg_38) + goto exit_no_mem; + control->reg_38->length = num_of_sensing_freqs; + + control->reg_38->data = kzalloc(num_of_sensing_freqs * + sizeof(*(control->reg_38->data)), GFP_KERNEL); + if (!control->reg_38->data) + goto exit_no_mem; + control->reg_38->address = reg_addr; + reg_addr += num_of_sensing_freqs; + + control->reg_39 = kzalloc(sizeof(*(control->reg_39)), + GFP_KERNEL); + if (!control->reg_39) + goto exit_no_mem; + control->reg_39->length = num_of_sensing_freqs; + + control->reg_39->data = kzalloc(num_of_sensing_freqs * + sizeof(*(control->reg_39->data)), GFP_KERNEL); + if (!control->reg_39->data) + goto exit_no_mem; + control->reg_39->address = reg_addr; + reg_addr += num_of_sensing_freqs; + + control->reg_40 = kzalloc(sizeof(*(control->reg_40)), + GFP_KERNEL); + if (!control->reg_40) + goto exit_no_mem; + control->reg_40->length = num_of_sensing_freqs; + + control->reg_40->data = kzalloc(num_of_sensing_freqs * + sizeof(*(control->reg_40->data)), GFP_KERNEL); + if (!control->reg_40->data) + goto exit_no_mem; + control->reg_40->address = reg_addr; + reg_addr += num_of_sensing_freqs; + } + reg_num++; + + CTRL_REG_PRESENCE(41, 1, query->has_signal_clarity); + CTRL_REG_PRESENCE(42, 2, query->has_variance_metric); + CTRL_REG_PRESENCE(43, 2, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(44, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(45, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(46, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(47, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(48, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(49, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(50, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(51, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(52, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(53, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(53, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(54, 1, query->has_multi_metric_state_machine); + CTRL_REG_PRESENCE(55, 1, query->has_0d_relaxation_control); + CTRL_REG_PRESENCE(56, 1, query->has_0d_relaxation_control); + CTRL_REG_PRESENCE(57, 1, query->has_0d_acquisition_control); + CTRL_REG_PRESENCE(58, 1, query->has_0d_acquisition_control); + CTRL_REG_PRESENCE(59, 2, query->has_h_blank); + CTRL_REG_PRESENCE(60, 1, query->has_h_blank || query->has_v_blank || + query->has_long_h_blank); + CTRL_REG_PRESENCE(61, 1, query->has_h_blank || query->has_v_blank || + query->has_long_h_blank); + CTRL_REG_PRESENCE(62, 1, query->has_h_blank || query->has_v_blank || + query->has_long_h_blank); + CTRL_REG_PRESENCE(63, 1, query->has_h_blank || query->has_v_blank || + query->has_long_h_blank || + query->has_slew_metric || + query->has_slew_option || + query->has_noise_mitigation2); + + if (query->has_h_blank) { + CTRL_REG_PRESENCE(64, 7, 1); + CTRL_REG_PRESENCE(65, 7, 1); + } else if (query->has_v_blank || query->has_long_h_blank) { + CTRL_REG_PRESENCE(64, 1, 1); + CTRL_REG_PRESENCE(65, 1, 1); + } + + if (query->has_h_blank || query->has_v_blank || + query->has_long_h_blank) { + CTRL_REG_PRESENCE(66, 1, 1); + CTRL_REG_PRESENCE(67, 1, 1); + CTRL_REG_PRESENCE(68, 1, 1); + CTRL_REG_PRESENCE(69, 1, 1); + CTRL_REG_PRESENCE(70, 1, 1); + CTRL_REG_PRESENCE(71, 1, 1); + CTRL_REG_PRESENCE(72, 2, 1); + CTRL_REG_PRESENCE(73, 2, 1); + } + CTRL_REG_PRESENCE(74, 2, query->has_slew_metric); + CTRL_REG_PRESENCE(75, query12->number_of_sensing_frequencies, + query->has_enhanced_stretch && + query->has_sense_frequency_control); + CTRL_REG_PRESENCE(76, 1, query->has_startup_fast_relaxation); + CTRL_REG_PRESENCE(77, 1, query->has_esd_control); + CTRL_REG_PRESENCE(78, 1, query->has_esd_control); + CTRL_REG_PRESENCE(79, 1, query->has_noise_mitigation2); + CTRL_REG_PRESENCE(80, 1, query->has_noise_mitigation2); + CTRL_REG_PRESENCE(81, 1, query->has_noise_mitigation2); + CTRL_REG_PRESENCE(82, 1, query->has_noise_mitigation2); + CTRL_REG_PRESENCE(83, 1, query->has_noise_mitigation2); + CTRL_REG_PRESENCE(84, 1, query->has_energy_ratio_relaxation); + CTRL_REG_PRESENCE(85, 1, query->has_energy_ratio_relaxation); + CTRL_REG_PRESENCE(86, 1, query13->has_ctrl86); + CTRL_REG_PRESENCE(87, 1, query13->has_ctrl87); + CTRL_REG_PRESENCE(88, 1, query->has_ctrl88); + +/* + CTRL_REG_ADD(89, 1, query13->has_cid_im || + query13->has_noise_mitigation_enh || + query13->has_rail_im); +*/ + CTRL_PKT_REG_START(89, + CTRL_SUBPKT_SZ(89, 0) + + CTRL_SUBPKT_SZ(89, 1) + + CTRL_SUBPKT_SZ(89, 2)); + + CTRL_SUBPKT_ADD(89, 0, query13->has_cid_im); + CTRL_SUBPKT_ADD(89, 1, query13->has_noise_mitigation_enh); + CTRL_SUBPKT_ADD(89, 2, query13->has_rail_im); + CTRL_PKT_REG_END(89, 1); + + CTRL_REG_PRESENCE(90, 1, query15->has_ctrl90); + CTRL_REG_PRESENCE(91, 1, query21->has_ctrl91); + CTRL_REG_PRESENCE(92, 1, query16->has_ctrl92); + + CTRL_REG_ADD(93, 1, query16->has_ctrl93); + + CTRL_REG_PRESENCE(94, 1, query16->has_ctrl94_query18); + + CTRL_REG_ADD_EXT(95, 1, query16->has_ctrl95_query19, + sizeof(struct f54_control_95n) * + query17->q17_num_of_sense_freqs); + + CTRL_REG_PRESENCE(96, 1, query21->has_ctrl96); + CTRL_REG_PRESENCE(97, 1, query21->has_ctrl97); + CTRL_REG_PRESENCE(98, 1, query21->has_ctrl98); + + CTRL_REG_ADD(99, 1, (query16->has_ctrl99 || + ((f54->query.touch_controller_family == 2) || + (f54->query.touch_controller_family == 4)))); + + CTRL_REG_PRESENCE(100, 1, query16->has_ctrl100); + CTRL_REG_PRESENCE(101, 1, query22->has_ctrl101); + CTRL_REG_PRESENCE(102, 1, query23->has_ctrl102); + CTRL_REG_PRESENCE(103, 1, query22->has_ctrl103_query26); + CTRL_REG_PRESENCE(104, 1, query22->has_ctrl104); + CTRL_REG_PRESENCE(105, 1, query22->has_ctrl105); + CTRL_REG_PRESENCE(106, 1, query25->has_ctrl106); + + CTRL_REG_ADD(107, 1, query25->has_ctrl107); + + CTRL_REG_PRESENCE(108, 1, query25->has_ctrl108); + CTRL_REG_PRESENCE(109, 1, query25->has_ctrl109); + CTRL_REG_PRESENCE(110, 1, query27->has_ctrl110); + CTRL_REG_PRESENCE(111, 1, query27->has_ctrl111); + CTRL_REG_PRESENCE(112, 1, query27->has_ctrl112); + + CTRL_REG_ADD(113, 1, query27->has_ctrl113); + + CTRL_REG_PRESENCE(114, 1, query27->has_ctrl114); + CTRL_REG_PRESENCE(115, 1, query29->has_ctrl115); + + CTRL_REG_ADD(116, 1, query29->has_ctrl116); + + CTRL_REG_PRESENCE(117, 1, query29->has_ctrl117); + CTRL_REG_PRESENCE(118, 1, query30->has_ctrl118); + CTRL_REG_PRESENCE(119, 1, query30->has_ctrl119); + CTRL_REG_PRESENCE(120, 1, query30->has_ctrl120); + CTRL_REG_PRESENCE(121, 1, query30->has_ctrl121); + CTRL_REG_PRESENCE(122, 1, query30->has_ctrl122_query31); + CTRL_REG_PRESENCE(123, 1, query30->has_ctrl123); + CTRL_REG_PRESENCE(124, 1, query30->has_ctrl124); + CTRL_REG_PRESENCE(125, 1, query32->has_ctrl125); + CTRL_REG_PRESENCE(126, 1, query32->has_ctrl126); + CTRL_REG_PRESENCE(127, 1, query32->has_ctrl127); + CTRL_REG_PRESENCE(128, 1, query33->has_ctrl128); + CTRL_REG_PRESENCE(129, 1, query33->has_ctrl129); + CTRL_REG_PRESENCE(130, 1, query33->has_ctrl130); + CTRL_REG_PRESENCE(131, 1, query33->has_ctrl131); + CTRL_REG_PRESENCE(132, 1, query33->has_ctrl132); + CTRL_REG_PRESENCE(133, 1, query33->has_ctrl133); + CTRL_REG_PRESENCE(134, 1, query33->has_ctrl134); + CTRL_REG_PRESENCE(135, 1, query35->has_ctrl135); + CTRL_REG_PRESENCE(136, 1, query35->has_ctrl136); + + CTRL_REG_ADD(137, 1, query35->has_ctrl137); + + CTRL_REG_PRESENCE(138, 1, query35->has_ctrl138); + CTRL_REG_PRESENCE(139, 1, query35->has_ctrl139); + CTRL_REG_PRESENCE(140, 1, query35->has_ctrl140); + CTRL_REG_PRESENCE(141, 1, query36->has_ctrl141); + CTRL_REG_PRESENCE(142, 1, query36->has_ctrl142); + CTRL_REG_PRESENCE(143, 1, query36->has_ctrl143); + CTRL_REG_PRESENCE(144, 1, query36->has_ctrl144); + + CTRL_REG_ADD(145, 1, query36->has_ctrl145); + CTRL_REG_ADD(146, 1, query36->has_ctrl146); + + CTRL_REG_PRESENCE(147, 1, query38->has_ctrl147); + CTRL_REG_PRESENCE(148, 1, query38->has_ctrl148); + CTRL_REG_PRESENCE(149, 1, query38->has_ctrl149); + CTRL_REG_PRESENCE(150, 1, query38->has_ctrl150); + CTRL_REG_PRESENCE(151, 1, query38->has_ctrl151); + CTRL_REG_PRESENCE(152, 1, query38->has_ctrl152); + CTRL_REG_PRESENCE(153, 1, query38->has_ctrl153); + CTRL_REG_PRESENCE(154, 1, query39->has_ctrl154); + CTRL_REG_PRESENCE(155, 1, query39->has_ctrl155); + CTRL_REG_PRESENCE(156, 1, query39->has_ctrl156); + CTRL_REG_PRESENCE(157, 1, query39->has_ctrl157_158); + CTRL_REG_PRESENCE(158, 1, query39->has_ctrl157_158); +// CTRL_REG_RESERVED_PRESENCE(159, 1, 0); + CTRL_REG_PRESENCE(160, 1, query39->has_ctrl160); +// CTRL_REG_RESERVED_PRESENCE(161, 1, 0); +// CTRL_REG_RESERVED_PRESENCE(162, 1, 0); + CTRL_REG_PRESENCE(163, 1, query40->has_ctrl163_query41); +// CTRL_REG_RESERVED_PRESENCE(164, 1, 0); + CTRL_REG_PRESENCE(165, 1, query40->has_ctrl165_query42); + CTRL_REG_PRESENCE(166, 1, query40->has_ctrl166); + CTRL_REG_PRESENCE(167, 1, query40->has_ctrl167); + CTRL_REG_PRESENCE(168, 1, query40->has_ctrl168); + CTRL_REG_PRESENCE(169, 1, query40->has_ctrl169); +// CTRL_REG_RESERVED_PRESENCE(170, 1, 0); + CTRL_REG_PRESENCE(171, 1, query43->has_ctrl171); + CTRL_REG_PRESENCE(172, 1, query43->has_ctrl172_query44_query45); + CTRL_REG_PRESENCE(173, 1, query43->has_ctrl173); + CTRL_REG_PRESENCE(174, 1, query43->has_ctrl174); + CTRL_REG_PRESENCE(175, 1, query43->has_ctrl175); + CTRL_REG_PRESENCE(176, 1, query46->has_ctrl176); + CTRL_REG_PRESENCE(177, 1, query46->has_ctrl177_178); + CTRL_REG_PRESENCE(178, 1, query46->has_ctrl177_178); + CTRL_REG_PRESENCE(179, 1, query46->has_ctrl179); + /* BSC control access */ +// CTRL_REG_RESERVED_PRESENCE(180, 1, 0); +// CTRL_REG_RESERVED_PRESENCE(181, 1, 0); + CTRL_REG_PRESENCE(182, 1, query47->has_ctrl182); + CTRL_REG_PRESENCE(183, 1, query47->has_ctrl183); +// CTRL_REG_RESERVED_PRESENCE(184, 1, 0); + CTRL_REG_PRESENCE(185, 1, query47->has_ctrl185); + CTRL_REG_PRESENCE(186, 1, query47->has_ctrl186); + CTRL_REG_PRESENCE(187, 1, query47->has_ctrl187); + CTRL_REG_PRESENCE(188, 1, query49->has_ctrl188); +// CTRL_REG_RESERVED_PRESENCE(189, 1, 0); + CTRL_REG_PRESENCE(190, 1, query49->has_ctrl190); +// CTRL_REG_RESERVED_PRESENCE(191, 1, 0); +// CTRL_REG_RESERVED_PRESENCE(192, 1, 0); + + CTRL_REG_PRESENCE(193, 1, query50->has_ctrl193); +// CTRL_REG_RESERVED_PRESENCE(194, 1, 0); +// CTRL_REG_RESERVED_PRESENCE(195, 1, 0); +// CTRL_REG_RESERVED_PRESENCE(196, 1, 0); + CTRL_REG_PRESENCE(197, 1, query51->has_ctrl197); + CTRL_REG_PRESENCE(198, 1, query51->has_query53_query54_ctrl198); + CTRL_REG_PRESENCE(199, 1, query51->has_ctrl199); + CTRL_REG_PRESENCE(200, 1, query55->has_ctrl200); + CTRL_REG_PRESENCE(201, 1, query55->has_ctrl201_ctrl202); + CTRL_REG_PRESENCE(202, 1, query55->has_ctrl201_ctrl202); + CTRL_REG_PRESENCE(203, 1, query55->has_ctrl203); + CTRL_REG_PRESENCE(204, 1, query55->has_ctrl204); + CTRL_REG_PRESENCE(205, 1, query57->has_ctrl205); + CTRL_REG_PRESENCE(206, 1, query57->has_ctrl206); + CTRL_REG_PRESENCE(207, 1, query57->has_ctrl207); + CTRL_REG_PRESENCE(208, 1, query57->has_ctrl208); + CTRL_REG_PRESENCE(209, 1, query57->has_ctrl209); + CTRL_REG_PRESENCE(210, 1, query57->has_ctrl210); + CTRL_REG_PRESENCE(211, 1, query58->has_ctrl211); + CTRL_REG_PRESENCE(212, 1, query58->has_ctrl212); + CTRL_REG_PRESENCE(213, 1, query58->has_ctrl213); + CTRL_REG_PRESENCE(214, 1, query61->has_ctrl214); + CTRL_REG_PRESENCE(215, 1, query61->has_ctrl215_query62_query63); + CTRL_REG_PRESENCE(216, 1, query61->has_ctrl216); + CTRL_REG_PRESENCE(217, 1, query61->has_ctrl217); + CTRL_REG_PRESENCE(218, 1, query61->has_misc_host_ctrl); + /* might have subpacket */ + CTRL_REG_PRESENCE(219, 1, query61->has_ctrl219); + CTRL_REG_PRESENCE(220, 1, query64->has_ctrl220); + CTRL_REG_PRESENCE(221, 1, query64->has_ctrl221); + CTRL_REG_PRESENCE(222, 1, query64->has_ctrl222); + CTRL_REG_PRESENCE(223, 1, query68->is_tddi_hic); + CTRL_REG_PRESENCE(224, 1, query64->has_ctrl224_ctrl226_ctrl227); + CTRL_REG_PRESENCE(225, 1, query67->has_abs_doze_spatial_filter_en); + CTRL_REG_PRESENCE(226, 1, query64->has_ctrl224_ctrl226_ctrl227); + CTRL_REG_PRESENCE(227, 1, query64->has_ctrl224_ctrl226_ctrl227); + CTRL_REG_PRESENCE(228, 1, (f54->query.touch_controller_family == 4)); +// CTRL_REG_RESERVED_PRESENCE(229, 1, 0); + CTRL_REG_PRESENCE(230, 1, query65->has_ctrl230); + CTRL_REG_PRESENCE(231, 1, query65->has_query66_ctrl231); + CTRL_REG_PRESENCE(232, 1, query65->has_ctrl232); +// CTRL_REG_RESERVED_PRESENCE(233, 1, 0); +// CTRL_REG_RESERVED_PRESENCE(234, 1, 0); + CTRL_REG_PRESENCE(235, 1, query67->has_ctrl235_ctrl236); + CTRL_REG_PRESENCE(236, 1, query67->has_ctrl235_ctrl236); + CTRL_REG_PRESENCE(237, 1, query68->has_ctrl237); + CTRL_REG_PRESENCE(238, 1, query68->has_ctrl238); + CTRL_REG_PRESENCE(239, 1, query68->has_ctrl239); + /* might have up to 4 subpackets */ + CTRL_REG_PRESENCE(240, 1, query69->data[0] & 0x0f); + CTRL_REG_ADD(242, 1, query70->has_ctrl242); + CTRL_REG_ADD(269, 1, query75->has_ctrl269); + + /* add F55 control registers */ + reg_addr = f54->fn55->control_base_addr; + + CTRL_REG_ADD(0_f55, 1, f54->query_f55_0_2.has_sensor_assignment); + + CTRL_REG_PRESENCE(1_f55, 1, f54->query_f55_0_2.has_sensor_assignment); + CTRL_REG_PRESENCE(2_f55, 1, f54->query_f55_0_2.has_sensor_assignment); + CTRL_REG_PRESENCE(3_f55, 1, f54->query_f55_0_2.has_edge_compensation); + CTRL_REG_PRESENCE(4_f55, 1, f54->query_f55_0_2.curve_compensation_mode); + CTRL_REG_PRESENCE(5_f55, 1, f54->query_f55_0_2.curve_compensation_mode); + CTRL_REG_PRESENCE(6_f55, 1, f54->query_f55_0_2.has_ctrl6); + CTRL_REG_PRESENCE(7_f55, 1, + f54->query_f55_0_2.has_alternate_tx_assignment); + + CTRL_REG_ADD(8_f55, 1, + (f54->query_f55_0_2.f55_q2_has_single_layer_multitouch && + f54->query_f55_3.f55_q3_has_ctrl8)); + return 0; + +exit_no_mem: + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for control registers\n", + __func__); + return -ENOMEM; +} + +static int synaptics_rmi4_f54_set_data( + struct synaptics_rmi4_f54_handle *f54) +{ + unsigned char reg_num = 0; + unsigned short reg_addr = f54->data_base_addr; + struct f54_data *data = &f54->data; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + /* data 0 - data 3: skip mandatory registers */ + reg_addr += 4; + + /* data 4 */ + DATA_REG_ADD(4, 1, f54->query.has_sense_frequency_control); + + /* F54_ANALOG_Data5 (reserved) is not present */ + /* - do not increment */ + + /* data 6 */ + DATA_REG_ADD(6, 2, f54->query.has_interference_metric); + /* data 7.0 */ + DATA_REG_ADD(7_0, 1, (f54->query.has_one_byte_report_rate || + f54->query.has_two_byte_report_rate)); + /* data 7.1 */ + DATA_REG_ADD(7_1, 1, f54->query.has_two_byte_report_rate); + /* data 8 */ + DATA_REG_ADD(8, 2, f54->query.has_variance_metric); + /* data 9 */ + DATA_REG_ADD(9, 2, f54->query.has_multi_metric_state_machine); + /* data 10 */ + DATA_REG_ADD(10, 1, (f54->query.has_multi_metric_state_machine || + f54->query.has_noise_state)); + + /* data 11 */ + DATA_REG_PRESENCE(11, 1, f54->query.has_status); + /* data 12 */ + DATA_REG_PRESENCE(12, 2, f54->query.has_slew_metric); + /* data 13 */ + DATA_REG_PRESENCE(13, 2, f54->query.has_multi_metric_state_machine); + + /* data 14 */ + DATA_REG_ADD(14, 1, f54->query13.has_cid_im); + + /* data 15 */ + DATA_REG_PRESENCE(15, 1, f54->query13.has_rail_im); + + /* data 16 */ + DATA_REG_ADD(16, 1, f54->query13.has_noise_mitigation_enh); + /* data 17 */ + DATA_REG_ADD(17, 1, f54->query16.has_data17); + + return 0; + +exit_no_mem: + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for data registers\n", + __func__); + return -ENOMEM; +} + +static int synaptics_rmi4_f55_read_query( + struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + uint16_t reg_addr; + + retval = f54->fn_ptr->read(rmi4_data, + f54->fn55->query_base_addr, + f54->query_f55_0_2.data, + sizeof(f54->query_f55_0_2.data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read F55 query registers 0-2\n", + __func__); + goto err; + } + + reg_addr = f54->fn55->query_base_addr; + + QUERY_REG_READ(_f55_0_2, 1); + QUERY_REG_READ(_f55_3, + f54->query_f55_0_2.f55_q2_has_single_layer_multitouch); +return 0; + +err: + return retval; +} + +static void synaptics_rmi4_f54_sensor_mapping( + struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + struct f54_control *control = &f54->control; + unsigned int rx_len; + unsigned int tx_len; + unsigned char *buffer; + unsigned char *rx_buffer; + unsigned char *tx_buffer; + unsigned short offset; + int i; + + rmi4_data->num_of_rx = 0; + rmi4_data->num_of_tx = 0; + + if (f54->query.has_sensor_assignment == 1) { + rx_len = control->reg_15->length; + tx_len = control->reg_16->length; + offset = control->reg_15->address; + + } else if (f54->fn55) { + if (f54->query_f55_0_2.has_sensor_assignment) { + rx_len = f54->query_f55_0_2.num_of_rx_electrodes; + tx_len = f54->query_f55_0_2.num_of_tx_electrodes; + + offset = f54->fn55->control_base_addr + 1; + } else { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Has sensor assignment is not set in F55\n", + __func__); + return; + } + } else { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Cannot find sensor mapping\n", + __func__); + return; + } + + buffer = kzalloc((rx_len + tx_len) * sizeof(unsigned char), + GFP_KERNEL); + + retval = f54->fn_ptr->read(rmi4_data, offset, buffer, rx_len + tx_len); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read control registers\n", + __func__); + goto exit; + } + rx_buffer = buffer; + tx_buffer = buffer + rx_len; + + for (i = 0; i < rx_len; i++) { + if (rx_buffer[i] != 0xFF) + rmi4_data->num_of_rx++; + } + for (i = 0; i < tx_len; i++) { + if (tx_buffer[i] != 0xFF) + rmi4_data->num_of_tx++; + } + + dev_info(&rmi4_data->i2c_client->dev, + "RxTx mapped from F$%s Rx mapped count %d(%d); Tx mapped count %d(%d)\n", + f54->fn55 ? "55" : "54", rmi4_data->num_of_rx, + rx_len, rmi4_data->num_of_tx, tx_len); +exit: + kfree(buffer); +} + +static void synaptics_rmi4_f54_status_work(struct work_struct *work) +{ + int retval; + unsigned char report_index[2]; + struct delayed_work *dw = + container_of(work, struct delayed_work, work); + struct synaptics_rmi4_f54_handle *f54 = container_of(dw, + struct synaptics_rmi4_f54_handle, status_work); + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + + pr_debug("enter; status = %d\n", f54->status); + if (f54->status != STATUS_BUSY) { + retval = STATUS_ERROR; + goto error_exit; + } + + set_report_size(f54); + pr_debug("report_size = %d\n", f54->report_size); + if (f54->report_size == 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Report data size = 0\n", + __func__); + retval = -EINVAL; + goto error_exit; + } + + if (f54->data_buffer_size < f54->report_size) { + mutex_lock(&f54->data_mutex); + if (f54->data_buffer_size) + kfree(f54->report_data); + f54->report_data = kzalloc(f54->report_size, GFP_KERNEL); + if (!f54->report_data) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for data buffer\n", + __func__); + f54->data_buffer_size = 0; + mutex_unlock(&f54->data_mutex); + retval = -ENOMEM; + goto error_exit; + } + f54->data_buffer_size = f54->report_size; + mutex_unlock(&f54->data_mutex); + } + + report_index[0] = 0; + report_index[1] = 0; + + retval = f54->fn_ptr->write(rmi4_data, + f54->data_base_addr + DATA_REPORT_INDEX_OFFSET, + report_index, + sizeof(report_index)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to write report data index\n", + __func__); + retval = -EINVAL; + goto error_exit; + } + + retval = f54->fn_ptr->read(rmi4_data, + f54->data_base_addr + DATA_REPORT_DATA_OFFSET, + f54->report_data, + f54->report_size); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read report data\n", + __func__); + retval = -EINVAL; + goto error_exit; + } + + pr_debug("retrieved %d bytes of test report data\n", retval); + retval = STATUS_IDLE; + +#ifdef RAW_HEX + print_raw_hex_report(f54); +#endif + +#ifdef HUMAN_READABLE + print_image_report(f54); +#endif + +error_exit: + mutex_lock(&f54->status_mutex); +#if !defined(REPORT_STATUS_POLLING) + set_interrupt(f54, false); +#endif + PM_RELAX(f54->test_wake_lock); + f54->status = retval; + mutex_unlock(&f54->status_mutex); + pr_debug("status set to %d\n", f54->status); + + return; +} + +static void synaptics_rmi4_f54_attn(struct synaptics_rmi4_data *rmi4_data, + unsigned char intr_mask) +{ + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + + pr_debug("called f54_int_mask %x, int_mask %x\n", f54->intr_mask, intr_mask); + if (f54->intr_mask & intr_mask) { + schedule_delayed_work(&f54->status_work, + msecs_to_jiffies(STATUS_WORK_INTERVAL)); + } + + return; +} + +int synaptics_rmi4_f54_read_query( + struct synaptics_rmi4_f54_handle *f54) +{ + int retval; + struct synaptics_rmi4_data *rmi4_data = f54->rmi4_data; + uint16_t reg_addr; + + retval = f54->fn_ptr->read(rmi4_data, + f54->query_base_addr, + f54->query.data, + sizeof(f54->query.data)); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read query registers\n", + __func__); + goto err; + } + + reg_addr = f54->query_base_addr + sizeof(f54->query.data); + + QUERY_REG_READ(12, f54->query.has_sense_frequency_control); + QUERY_REG_READ(13, f54->query.has_query13); + QUERY_REG_READ(14, f54->query13.has_ctrl87); + QUERY_REG_READ(15, f54->query.has_query15); + QUERY_REG_READ(16, f54->query15.has_query16); + QUERY_REG_READ(17, f54->query16.has_query17); + QUERY_REG_READ(18, f54->query16.has_ctrl94_query18); + QUERY_REG_READ(19, f54->query16.has_ctrl95_query19); + QUERY_REG_READ(20, f54->query15.has_query20); + QUERY_REG_READ(21, f54->query15.has_query21); + QUERY_REG_READ(22, f54->query15.has_query22); + QUERY_REG_READ(23, f54->query22.has_query23); + QUERY_REG_READ(24, f54->query21.has_query24_data18); + QUERY_REG_READ(25, f54->query15.has_query25); + QUERY_REG_READ(26, f54->query22.has_ctrl103_query26); + QUERY_REG_READ(27, f54->query25.has_query27); + QUERY_REG_READ(28, f54->query22.has_query28); + QUERY_REG_READ(29, f54->query27.has_query29); + QUERY_REG_READ(30, f54->query29.has_query30); + QUERY_REG_READ(31, f54->query30.has_ctrl122_query31); + QUERY_REG_READ(32, f54->query30.has_query32); + QUERY_REG_READ(33, f54->query32.has_query33); + QUERY_REG_READ(34, f54->query32.has_query34); + QUERY_REG_READ(35, f54->query32.has_query35); + QUERY_REG_READ(36, f54->query33.has_query36); + if (f54->query36.has_query37) + reg_addr += 1; + QUERY_REG_READ(38, f54->query36.has_query38); + QUERY_REG_READ(39, f54->query38.has_query39); + QUERY_REG_READ(40, f54->query39.has_query40); + QUERY_REG_READ(41, f54->query40.has_ctrl163_query41); + QUERY_REG_READ(42, f54->query40.has_ctrl165_query42); + QUERY_REG_READ(43, f54->query40.has_query43); +#if 0 + reg_addr += 1; /* query44 is reserved - always present */ + reg_addr += 1; /* query45 is reserved - always present */ +#endif + QUERY_REG_READ(46, f54->query43.has_query46); + QUERY_REG_READ(47, f54->query46.has_query47); + QUERY_REG_READ(49, f54->query47.has_query49); + QUERY_REG_READ(50, f54->query49.has_query50); + QUERY_REG_READ(51, f54->query50.has_query51); + if (f54->query50.has_query52) + reg_addr += 1; + QUERY_REG_READ(55, f54->query51.has_query55); + QUERY_REG_READ(57, f54->query55.has_query57); + QUERY_REG_READ(58, f54->query57.has_query58); + QUERY_REG_READ(61, f54->query58.has_query61); + QUERY_REG_READ(64, f54->query61.has_query64); + QUERY_REG_READ(65, f54->query64.has_query65); + QUERY_REG_READ(67, f54->query65.has_query67); + QUERY_REG_READ(68, f54->query67.has_query68); + QUERY_REG_READ(69, f54->query68.has_query69); + QUERY_REG_READ(70, f54->query69.has_query70); + QUERY_REG_READ(71, f54->query70.has_query71); + QUERY_REG_READ(72, f54->query71.has_query72); + QUERY_REG_READ(73, f54->query72.has_query73); + QUERY_REG_READ(74, f54->query73.has_query74); + QUERY_REG_READ(75, f54->query74.has_query75); + + return 0; + +err: + return retval; +} + +static int synaptics_rmi4_f54_init(struct synaptics_rmi4_data *rmi4_data) +{ + int retval; + bool hasF54 = false; + bool hasF55 = false; + unsigned short ii; + unsigned char page; + unsigned char intr_count = 0; + unsigned char intr_offset; + struct synaptics_rmi4_fn_desc rmi_fd; + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + + f54->fn_ptr = kzalloc(sizeof(*(f54->fn_ptr)), GFP_KERNEL); + if (!f54->fn_ptr) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to alloc mem for fn_ptr\n", + __func__); + retval = -ENOMEM; + goto exit_free_f54; + } + + f54->rmi4_data = rmi4_data; + f54->fn_ptr->read = rmi4_data->i2c_read; + f54->fn_ptr->write = rmi4_data->i2c_write; + f54->fn_ptr->enable = rmi4_data->irq_enable; + + for (page = 0; page < PAGES_TO_SERVICE; page++) { + for (ii = PDT_START; ii > PDT_END; ii -= PDT_ENTRY_SIZE) { + ii |= (page << 8); + + retval = f54->fn_ptr->read(rmi4_data, + ii, + (unsigned char *)&rmi_fd, + sizeof(rmi_fd)); + if (retval < 0) + goto exit_free_mem; + + if (!rmi_fd.fn_number) + break; + + if (rmi_fd.fn_number == SYNAPTICS_RMI4_F54) { + hasF54 = true; + f54->query_base_addr = + rmi_fd.query_base_addr | (page << 8); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: query_base_addr 0x%04x\n", + __func__, f54->query_base_addr); + f54->control_base_addr = + rmi_fd.ctrl_base_addr | (page << 8); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: ctrl_base_addr 0x%04x\n", + __func__, f54->control_base_addr); + f54->data_base_addr = + rmi_fd.data_base_addr | (page << 8); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: data_base_addr 0x%04x\n", + __func__, f54->data_base_addr); + f54->command_base_addr = + rmi_fd.cmd_base_addr | (page << 8); + dev_dbg(&rmi4_data->i2c_client->dev, + "%s: command_base_addr 0x%04x\n", + __func__, f54->command_base_addr); + } else if (rmi_fd.fn_number == SYNAPTICS_RMI4_F55) { + hasF55 = true; + f54->fn55 = kmalloc(sizeof(*f54->fn55), + GFP_KERNEL); + f54->fn55->query_base_addr = + rmi_fd.query_base_addr | (page << 8); + f54->fn55->control_base_addr = + rmi_fd.ctrl_base_addr | (page << 8); + } + + if (hasF54 && hasF55) + goto found; + + if (!hasF54) + intr_count += + (rmi_fd.intr_src_count & MASK_3BIT); + } + } + + if (!hasF54) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: F$54 is not available\n", + __func__); + goto exit; + } +found: + f54->intr_reg_num = (intr_count + 7) / 8; + if (f54->intr_reg_num != 0) + f54->intr_reg_num -= 1; + + f54->intr_mask = 0; + intr_offset = intr_count % 8; + for (ii = intr_offset; + ii < ((rmi_fd.intr_src_count & MASK_3BIT) + + intr_offset); + ii++) { + f54->intr_mask |= 1 << ii; + } + + retval = synaptics_rmi4_f54_read_query(f54); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to read query registers\n", + __func__); + goto exit_free_mem; + } + + retval = synaptics_rmi4_f55_read_query(f54); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to set up F55 query registers\n", + __func__); + goto exit_free_control; + } + + retval = synaptics_rmi4_f54_set_ctrl(f54); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to set up control registers\n", + __func__); + goto exit_free_control; + } + + synaptics_rmi4_f54_sensor_mapping(f54); + + retval = synaptics_rmi4_f54_set_data(f54); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to set up data registers\n", + __func__); + goto exit_free_data; + } + + mutex_init(&f54->status_mutex); + mutex_init(&f54->data_mutex); + mutex_init(&f54->control_mutex); + + retval = synaptics_rmi4_f54_set_sysfs(f54); + if (retval < 0) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Failed to create sysfs entries\n", + __func__); + goto exit_sysfs; + } + + INIT_DELAYED_WORK(&f54->status_work, + synaptics_rmi4_f54_status_work); + + f54->user_report_type1 = F54_16BIT_IMAGE; + f54->user_report_type2 = F54_RAW_16BIT_IMAGE; + + PM_WAKEUP_REGISTER(&f54->rmi4_data->i2c_client->dev, + f54->test_wake_lock, "synaptics_test_report"); + if(!f54->test_wake_lock) { + dev_err(&rmi4_data->i2c_client->dev, + "%s: Allocating synaptics_test_report err\n", + __func__); + retval = -ENOMEM; + goto exit_sysfs; + } + +#ifdef WATCHDOG_HRTIMER + /* Watchdog timer to catch unanswered get report commands */ + hrtimer_init(&f54->watchdog, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + f54->watchdog.function = get_report_timeout; + + /* Work function to do actual cleaning up */ + INIT_WORK(&f54->timeout_work, timeout_set_status); +#endif + + f54->init_done = true; + + return 0; + +exit_sysfs: +exit_free_data: + free_data_mem(f54); + +exit_free_control: + free_control_mem(f54); + +exit_free_mem: + kfree(f54->fn_ptr); + +exit_free_f54: + kfree(f54); + rmi4_data->f54_data = NULL; +exit: + return retval; +} + +static void synaptics_rmi4_f54_remove(struct synaptics_rmi4_data *rmi4_data) +{ + struct synaptics_rmi4_f54_handle *f54 = + (struct synaptics_rmi4_f54_handle *)rmi4_data->f54_data; + + if (!f54->init_done) { + dev_dbg(&rmi4_data->i2c_client->dev, "%s: no cleanup necessary!!!\n", __func__); + goto exit_and_complete; + } + +#ifdef WATCHDOG_HRTIMER + hrtimer_cancel(&f54->watchdog); +#endif + + remove_sysfs(f54); + + free_data_mem(f54); + free_control_mem(f54); + + kfree(f54->report_data); + kfree(f54->fn55); + /* cancel delayed work first then free fn_ptr!!! */ + cancel_delayed_work_sync(&f54->status_work); + kfree(f54->fn_ptr); + + PM_WAKEUP_UNREGISTER(f54->test_wake_lock); +exit_and_complete: + complete(&f54->remove_complete); + + return; +} + +static int __init rmi4_f54_module_init(void) +{ + struct synaptics_rmi4_f54_handle *f54; + struct synaptics_rmi4_data *next = NULL; + + while ((next = synaptics_driver_getdata(next)) != NULL) { + f54 = kzalloc(sizeof(*f54), GFP_KERNEL); + next->f54_data = (void *)f54; + next->scan_f54_ctrl_regs = synaptics_rmi4_scan_f54_ctrl_reg_info; + next->scan_f54_cmd_regs = synaptics_rmi4_scan_f54_cmd_reg_info; + next->scan_f54_query_regs = synaptics_rmi4_scan_f54_query_reg_info; + next->scan_f54_data_regs = synaptics_rmi4_scan_f54_data_reg_info; + dev_dbg(&next->i2c_client->dev, + "%s: f54 ptr = %p, rmi4_data_ptr %p\n", + __func__, f54, dev_get_drvdata(&next->i2c_client->dev)); + synaptics_rmi4_new_function(next, RMI_F54, true, + synaptics_rmi4_f54_init, + synaptics_rmi4_f54_remove, + synaptics_rmi4_f54_attn, + NULL, IC_MODE_UI); + } + return 0; +} + +static void __exit rmi4_f54_module_exit(void) +{ + struct synaptics_rmi4_f54_handle *f54; + struct synaptics_rmi4_data *next = NULL; + + while ((next = synaptics_driver_getdata(next)) != NULL) { + f54 = (struct synaptics_rmi4_f54_handle *)next->f54_data; + /* f54 can be NULL when function */ + /* has not been inserted yet */ + if (f54 == NULL) + continue; + init_completion(&f54->remove_complete); + synaptics_rmi4_new_function(next, RMI_F54, false, + synaptics_rmi4_f54_init, + synaptics_rmi4_f54_remove, + synaptics_rmi4_f54_attn, + NULL, IC_MODE_UI); + wait_for_completion(&f54->remove_complete); + kfree(f54); + next->scan_f54_ctrl_regs = NULL; + next->scan_f54_cmd_regs = NULL; + next->scan_f54_query_regs = NULL; + next->scan_f54_data_regs = NULL; + next->f54_data = NULL; + } + return; +} + +module_init(rmi4_f54_module_init); +module_exit(rmi4_f54_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics DSX Test Reporting Module"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(SYNAPTICS_DSX_DRIVER_VERSION); diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/Android.mk b/drivers/input/touchscreen/synaptics_tcm2_mmi/Android.mk new file mode 100644 index 000000000000..931c188e0f7a --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/Android.mk @@ -0,0 +1,33 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + + +ifeq ($(DRM_PANEL_NOTIFICATIONS),true) + KBUILD_OPTIONS += CONFIG_DRM_PANEL_NOTIFICATIONS=y +endif + +ifeq ($(TOUCHSCREEN_SYNA_TCM2_SYSFS),true) + KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_SYNA_TCM2_SYSFS=y +endif + +ifeq ($(TOUCHSCREEN_SYNA_TCM2_TESTING),true) + KBUILD_OPTIONS += CONFIG_TOUCHSCREEN_SYNA_TCM2_TESTING=y +endif + +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KBUILD_OPTIONS += CONFIG_INPUT_TOUCHSCREEN_MMI=y +endif + + + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_v2_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/mmi_info.ko +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KBUILD_OPTIONS += CONFIG_INPUT_TOUCHSCREEN_MMI=y + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +endif +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/Kbuild b/drivers/input/touchscreen/synaptics_tcm2_mmi/Kbuild new file mode 100644 index 000000000000..95d38184dc28 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/Kbuild @@ -0,0 +1,43 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/synaptics_tcm2_mmi \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm + +obj-m := synaptics_v2_mmi.o +synaptics_v2_mmi-objs := \ + syna_tcm2.o \ + syna_tcm2_testing.o \ + syna_tcm2_sysfs.o \ + syna_tcm2_platform_spi.o \ + tcm/synaptics_touchcom_core_v1.o \ + tcm/synaptics_touchcom_core_v2.o \ + tcm/synaptics_touchcom_func_base.o \ + tcm/synaptics_touchcom_func_reflash.o \ + tcm/synaptics_touchcom_func_touch.o + + + + +ifneq ($(filter m y, $(CONFIG_DRM_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_NOTIFICATIONS +endif + +ifneq ($(filter m y,$(CONFIG_TOUCHSCREEN_SYNA_TCM2_SYSFS)),) + EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_SYNA_TCM2_SYSFS +endif + +ifneq ($(filter m y,$(CONFIG_TOUCHSCREEN_SYNA_TCM2_TESTING)),) + EXTRA_CFLAGS += -DCONFIG_TOUCHSCREEN_SYNA_TCM2_TESTING +endif + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers + EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI + synaptics_v2_mmi-objs += syna_ts_mmi.o +endif + + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/Kconfig b/drivers/input/touchscreen/synaptics_tcm2_mmi/Kconfig new file mode 100644 index 000000000000..9eb1bd134c95 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/Kconfig @@ -0,0 +1,60 @@ +# +# Synaptics TCM touchscreen driver configuration +# +menuconfig TOUCHSCREEN_SYNA_TCM2 + tristate "Synaptics TCM touchscreen" + default y + help + Say Y here if you have a Synaptics TCM touchscreen connected + to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here. + +if TOUCHSCREEN_SYNA_TCM2 + +choice + default TOUCHSCREEN_SYNA_TCM2_I2C + prompt "Synaptics TCM bus module" +config TOUCHSCREEN_SYNA_TCM2_I2C + bool "I2C" + depends on I2C +config TOUCHSCREEN_SYNA_TCM2_SPI + bool "SPI" + depends on SPI_MASTER +endchoice + +config TOUCHSCREEN_SYNA_TCM2_SYSFS + bool "Synaptics TCM device sysfs module" + depends on TOUCHSCREEN_SYNA_TCM2 + help + Say Y here to enable support for the sysfs interface. + + If unsure, say N. + +config TOUCHSCREEN_SYNA_TCM2_REFLASH + bool "Synaptics TCM reflash module" + depends on TOUCHSCREEN_SYNA_TCM2 + help + Say Y here to enable support for the reflashing. + + If unsure, say N. + +config TOUCHSCREEN_SYNA_TCM2_TESTING + bool "Synaptics TCM testing module" + depends on TOUCHSCREEN_SYNA_TCM2 + help + Say Y here to enable support for the production test. + + If unsure, say N. + +config TOUCHSCREEN_SYNA_TCM2_ROMBOOT + bool "Synaptics TCM rom-bootloader module" + depends on TOUCHSCREEN_SYNA_TCM2 + help + Say Y here to enable support for the rom-boot access. + + If unsure, say N. + +endif diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/Makefile b/drivers/input/touchscreen/synaptics_tcm2_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2.c b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2.c new file mode 100644 index 000000000000..a34765f12343 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2.c @@ -0,0 +1,1975 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file syna_tcm2.c + * + * This file implements the Synaptics device driver running under Linux kernel + * input device subsystem, and also communicate with Synaptics touch controller + * through TouchComm command-response protocol. + */ + +#include "syna_tcm2.h" +#include "syna_tcm2_platform.h" +#include "synaptics_touchcom_core_dev.h" +#include "synaptics_touchcom_func_base.h" +#include "synaptics_touchcom_func_touch.h" +#include "synaptics_touchcom_func_reflash.h" +#include + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +extern int syna_ts_mmi_dev_register(struct syna_tcm *tcm); +extern void syna_ts_mmi_dev_unregister(struct syna_tcm *tcm); +#endif + +#ifdef STARTUP_REFLASH +#include "synaptics_touchcom_func_reflash.h" +#endif +#ifdef MULTICHIP_DUT_REFLASH +#include "synaptics_touchcom_func_romboot.h" +#endif + +/** + * @section: USE_CUSTOM_TOUCH_REPORT_CONFIG + * Open if willing to set up the format of touch report. + * The custom_touch_format[] array can be used to describe the + * customized report format. + */ +#ifdef USE_CUSTOM_TOUCH_REPORT_CONFIG +static unsigned char custom_touch_format[] = { + /* entity code */ /* bits */ +#ifdef ENABLE_WAKEUP_GESTURE + TOUCH_REPORT_GESTURE_ID, 8, +#endif + TOUCH_REPORT_NUM_OF_ACTIVE_OBJECTS, 8, + TOUCH_REPORT_FOREACH_ACTIVE_OBJECT, + TOUCH_REPORT_OBJECT_N_INDEX, 8, + TOUCH_REPORT_OBJECT_N_CLASSIFICATION, 8, + TOUCH_REPORT_OBJECT_N_X_POSITION, 16, + TOUCH_REPORT_OBJECT_N_Y_POSITION, 16, + TOUCH_REPORT_FOREACH_END, + TOUCH_REPORT_END +}; +#endif + +/** + * @section: RESET_ON_RESUME_DELAY_MS + * The delayed time to issue a reset on resume state. + * This configuration depends on RESET_ON_RESUME. + */ +#ifdef RESET_ON_RESUME +#define RESET_ON_RESUME_DELAY_MS (100) +#endif + + +/** + * @section: POWER_ALIVE_AT_SUSPEND + * indicate that the power is still alive even at + * system suspend. + * otherwise, there is no power supplied when system + * is going to suspend stage. + */ +#define POWER_ALIVE_AT_SUSPEND + +/** + * @section: global variables for an active drm panel + * in order to register display notifier + */ +#ifdef USE_DRM_PANEL_NOTIFIER + struct drm_panel *active_panel; +#endif + +#ifdef ENABLE_WAKEUP_GESTURE +#ifdef CONFIG_HAS_WAKELOCK +static struct wake_lock gesture_wakelock; +#else +static struct wakeup_source *gesture_wakelock; +#endif +#endif + +#if defined(ENABLE_HELPER) +/** + * syna_dev_reset_detected_cb() + * + * Callback to assign a task to helper thread. + * + * Please be noted that this function will be invoked in ISR so don't + * issue another touchcomm command here. + * + * @param + * [ in] callback_data: pointer to caller data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static void syna_dev_reset_detected_cb(void *callback_data) +{ + struct syna_tcm *tcm = (struct syna_tcm *)callback_data; + + if (!tcm->helper.workqueue) { + LOGW("No helper thread created\n"); + return; + } + + if (ATOMIC_GET(tcm->helper.task) == HELP_NONE) { + ATOMIC_SET(tcm->helper.task, HELP_RESET_DETECTED); + + queue_work(tcm->helper.workqueue, &tcm->helper.work); + } +} +/** + * syna_dev_helper_work() + * + * According to the given task, perform the delayed work + * + * @param + * [ in] work: data for work used + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static void syna_dev_helper_work(struct work_struct *work) +{ + unsigned char task; + struct syna_tcm_helper *helper = + container_of(work, struct syna_tcm_helper, work); + struct syna_tcm *tcm = + container_of(helper, struct syna_tcm, helper); + + task = ATOMIC_GET(helper->task); + + switch (task) { + case HELP_RESET_DETECTED: + LOGI("Reset caught, device mode 0x%x\n", tcm->tcm_dev->dev_mode); + break; + default: + break; + } + + ATOMIC_SET(helper->task, HELP_NONE); +} +#endif +/** + * syna_dev_enable_lowpwr_gesture() + * + * Enable or disable the low power gesture mode. + * Furthermore, set up the wake-up irq. + * + * @param + * [ in] tcm: tcm driver handle + * [ in] en: '1' to enable low power gesture mode; '0' to disable + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_dev_enable_lowpwr_gesture(struct syna_tcm *tcm, bool en) +{ + int retval = 0; + struct syna_hw_attn_data *attn = &tcm->hw_if->bdata_attn; + + if (!tcm->lpwg_enabled) + return 0; + + if (attn->irq_id == 0) + return 0; + + if (en) { + if (!tcm->irq_wake) { + enable_irq_wake(attn->irq_id); + tcm->irq_wake = true; + } + + /* enable wakeup gesture mode + * + * the wakeup gesture control may result from by finger event; + * therefore, it's better to use ATTN driven mode here + */ + retval = syna_tcm_set_dynamic_config(tcm->tcm_dev, + DC_ENABLE_WAKEUP_GESTURE_MODE, + 1, + RESP_IN_ATTN); + if (retval < 0) { + LOGE("Fail to enable wakeup gesture via DC command\n"); + return retval; + } + } else { + if (tcm->irq_wake) { + disable_irq_wake(attn->irq_id); + tcm->irq_wake = false; + } + + /* disable wakeup gesture mode + * + * the wakeup gesture control may result from by finger event; + * therefore, it's better to use ATTN driven mode here + */ + retval = syna_tcm_set_dynamic_config(tcm->tcm_dev, + DC_ENABLE_WAKEUP_GESTURE_MODE, + 0, + RESP_IN_ATTN); + if (retval < 0) { + LOGE("Fail to disable wakeup gesture via DC command\n"); + return retval; + } + } + + return retval; +} + +#ifdef ENABLE_CUSTOM_TOUCH_ENTITY +/** + * syna_dev_parse_custom_touch_data_cb() + * + * Callback to parse the custom or non-standard touch entity from the + * touch report. + * + * Please be noted that this function will be invoked in ISR so don't + * issue another touchcomm command here. + * If really needed, please assign a task to helper thread. + * + * @param + * [ in] code: the code of current touch entity + * [ in] config: the report configuration stored + * [in/out] config_offset: offset of current position in report config, + * and then return the updated position. + * [ in] report: touch report given + * [in/out] report_offset: offset of current position in touch report, + * the updated position should be returned. + * [ in] report_size: size of given touch report + * [ in] callback_data: pointer to caller data passed to callback function + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_dev_parse_custom_touch_data_cb(const unsigned char code, + const unsigned char *config, unsigned int *config_offset, + const unsigned char *report, unsigned int *report_offset, + unsigned int report_size, void *callback_data) +{ + /** + * sample code to demonstrate how to parse the custom touch entity + * from the touch report, additional modifications will be needed. + * + * struct syna_tcm *tcm = (struct syna_tcm *)callback_data; + * unsigned int data; + * unsigned int bits; + * + * switch (code) { + * case CUSTOM_ENTITY_CODE: + * bits = config[(*config_offset)++]; + * syna_tcm_get_touch_data(report, report_size, + * *report_offset, bits, &data); + * + * *report_offset += bits; + * return bits; + * default: + * LOGW("Unknown touch config code (idx:%d 0x%02x)\n", + * *config_offset, code); + * return (-EINVAL); + * } + * + */ + + return (-EINVAL); +} +#endif + +/** + * syna_tcm_free_input_events() + * + * Clear all relevant touched events. + * + * @param + * [ in] tcm: the driver handle + * + * @return + * none. + */ +static void syna_dev_free_input_events(struct syna_tcm *tcm) +{ + struct input_dev *input_dev = tcm->input_dev; +#ifdef TYPE_B_PROTOCOL + unsigned int idx; +#endif + + if (input_dev == NULL) + return; + + syna_pal_mutex_lock(&tcm->tp_event_mutex); + +#ifdef TYPE_B_PROTOCOL + for (idx = 0; idx < MAX_NUM_OBJECTS; idx++) { + input_mt_slot(input_dev, idx); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); + } +#endif + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_key(input_dev, BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(input_dev); +#endif + input_sync(input_dev); + + syna_pal_mutex_unlock(&tcm->tp_event_mutex); + +} + +/** + * syna_dev_report_input_events() + * + * Report touched events to the input subsystem. + * + * After syna_tcm_get_event_data() function and the touched data is ready, + * this function can be called to report an input event. + * + * @param + * [ in] tcm: the driver handle + * + * @return + * none. + */ +static void syna_dev_report_input_events(struct syna_tcm *tcm) +{ + unsigned int idx; + unsigned int x; + unsigned int y; + int wx; + int wy; + unsigned int status; + unsigned int touch_count; + struct input_dev *input_dev = tcm->input_dev; + unsigned int max_objects = tcm->tcm_dev->max_objects; + struct tcm_touch_data_blob *touch_data; + struct tcm_objects_data_blob *object_data; + int retval = 0; + + if (input_dev == NULL) + return; + + syna_pal_mutex_lock(&tcm->tp_event_mutex); + + touch_data = &tcm->tp_data; + object_data = &tcm->tp_data.object_data[0]; + +#ifdef ENABLE_WAKEUP_GESTURE + if ((tcm->pwr_state == LOW_PWR) && tcm->irq_wake) { + if (touch_data->gesture_id) { + LOGI("Gesture detected, id:%d\n", + touch_data->gesture_id); + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + if (tcm->imports && tcm->imports->report_gesture) { + struct gesture_event_data event; + event.evcode = 1; + retval = tcm->imports->report_gesture(&event); + if (!retval) +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&gesture_wakelock, msecs_to_jiffies(5000)); +#else + PM_WAKEUP_EVENT(gesture_wakelock, 5000); +#endif + } +#else + input_report_key(input_dev, KEY_WAKEUP, 1); + input_sync(input_dev); + input_report_key(input_dev, KEY_WAKEUP, 0); + input_sync(input_dev); +#endif + } + } +#endif + + if (tcm->pwr_state == LOW_PWR) + goto exit; + + touch_count = 0; + + for (idx = 0; idx < max_objects; idx++) { + if (tcm->prev_obj_status[idx] == LIFT && + object_data[idx].status == LIFT) + status = NOP; + else + status = object_data[idx].status; + + switch (status) { + case LIFT: +#ifdef TYPE_B_PROTOCOL + input_mt_slot(input_dev, idx); + input_mt_report_slot_state(input_dev, + MT_TOOL_FINGER, 0); +#endif + break; + case FINGER: + case GLOVED_OBJECT: + x = object_data[idx].x_pos; + y = object_data[idx].y_pos; + wx = object_data[idx].x_width; + wy = object_data[idx].y_width; +#ifdef REPORT_SWAP_XY + x = x ^ y; + y = x ^ y; + x = x ^ y; +#endif +#ifdef REPORT_FLIP_X + x = tcm->input_dev_params.max_x - x; +#endif +#ifdef REPORT_FLIP_Y + y = tcm->input_dev_params.max_y - y; +#endif +#ifdef TYPE_B_PROTOCOL + input_mt_slot(input_dev, idx); + input_mt_report_slot_state(input_dev, + MT_TOOL_FINGER, 1); +#endif + input_report_key(input_dev, BTN_TOUCH, 1); + input_report_key(input_dev, BTN_TOOL_FINGER, 1); + input_report_abs(input_dev, ABS_MT_POSITION_X, x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, y); +#ifdef REPORT_TOUCH_WIDTH + input_report_abs(input_dev, + ABS_MT_TOUCH_MAJOR, MAX(wx, wy)); + input_report_abs(input_dev, + ABS_MT_TOUCH_MINOR, MIN(wx, wy)); +#endif +#ifndef TYPE_B_PROTOCOL + input_mt_sync(input_dev); +#endif + LOGD("Finger %d: x = %d, y = %d\n", idx, x, y); + touch_count++; + break; + default: + break; + } + + tcm->prev_obj_status[idx] = object_data[idx].status; + } + + if (touch_count == 0) { + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_key(input_dev, BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(input_dev); +#endif + } + + input_sync(input_dev); + +exit: + syna_pal_mutex_unlock(&tcm->tp_event_mutex); + +} + +/** + * syna_dev_create_input_device() + * + * Allocate an input device and set up relevant parameters to the + * input subsystem. + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_dev_create_input_device(struct syna_tcm *tcm) +{ + int retval = 0; + struct tcm_dev *tcm_dev = tcm->tcm_dev; + struct input_dev *input_dev = NULL; +#ifdef DEV_MANAGED_API + struct device *dev = syna_request_managed_device(); + + if (!dev) { + LOGE("Invalid managed device\n"); + return -EINVAL; + } + + input_dev = devm_input_allocate_device(dev); +#else /* Legacy API */ + input_dev = input_allocate_device(); +#endif + if (input_dev == NULL) { + LOGE("Fail to allocate input device\n"); + return -ENODEV; + } + + input_dev->name = TOUCH_INPUT_NAME; + input_dev->phys = TOUCH_INPUT_PHYS_PATH; + input_dev->id.product = SYNAPTICS_TCM_DRIVER_ID; + input_dev->id.version = SYNAPTICS_TCM_DRIVER_VERSION; + input_dev->dev.parent = tcm->pdev->dev.parent; + input_set_drvdata(input_dev, tcm); + + 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); +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, input_dev->propbit); +#endif + +#ifdef ENABLE_WAKEUP_GESTURE + set_bit(KEY_WAKEUP, input_dev->keybit); + input_set_capability(input_dev, EV_KEY, KEY_WAKEUP); +#endif + + input_set_abs_params(input_dev, + ABS_MT_POSITION_X, 0, tcm_dev->max_x, 0, 0); + input_set_abs_params(input_dev, + ABS_MT_POSITION_Y, 0, tcm_dev->max_y, 0, 0); + + input_mt_init_slots(input_dev, tcm_dev->max_objects, + INPUT_MT_DIRECT); + +#ifdef REPORT_TOUCH_WIDTH + input_set_abs_params(input_dev, + ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(input_dev, + ABS_MT_TOUCH_MINOR, 0, 255, 0, 0); +#endif + + tcm->input_dev_params.max_x = tcm_dev->max_x; + tcm->input_dev_params.max_y = tcm_dev->max_y; + tcm->input_dev_params.max_objects = tcm_dev->max_objects; + + retval = input_register_device(input_dev); + if (retval < 0) { + LOGE("Fail to register input device\n"); + input_free_device(input_dev); + input_dev = NULL; + return retval; + } + + tcm->input_dev = input_dev; + + return 0; +} + +/** + * syna_dev_release_input_device() + * + * Release an input device allocated previously. + * + * @param + * [ in] tcm: the driver handle + * + * @return + * none. + */ +static void syna_dev_release_input_device(struct syna_tcm *tcm) +{ + if (!tcm->input_dev) + return; + + input_unregister_device(tcm->input_dev); + input_free_device(tcm->input_dev); + + tcm->input_dev = NULL; +} + +/** + * syna_dev_check_input_params() + * + * Check if any of the input parameters registered to the input subsystem + * has changed. + * + * @param + * [ in] tcm: the driver handle + * + * @return + * positive value to indicate mismatching parameters; otherwise, return 0. + */ +static int syna_dev_check_input_params(struct syna_tcm *tcm) +{ + struct tcm_dev *tcm_dev = tcm->tcm_dev; + + if (tcm_dev->max_x == 0 && tcm_dev->max_y == 0) + return 0; + + if (tcm->input_dev_params.max_x != tcm_dev->max_x) + return 1; + + if (tcm->input_dev_params.max_y != tcm_dev->max_y) + return 1; + + if (tcm->input_dev_params.max_objects != tcm_dev->max_objects) + return 1; + + if (tcm_dev->max_objects > MAX_NUM_OBJECTS) { + LOGW("Out of max num objects defined, in app_info: %d\n", + tcm_dev->max_objects); + return 0; + } + + LOGN("Input parameters unchanged\n"); + + return 0; +} + +/** + * syna_dev_set_up_input_device() + * + * Set up input device to the input subsystem by confirming the supported + * parameters and creating the device. + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_dev_set_up_input_device(struct syna_tcm *tcm) +{ + int retval = 0; + + if (IS_NOT_APP_FW_MODE(tcm->tcm_dev->dev_mode)) { + LOGN("Application firmware not running, current mode: %02x\n", + tcm->tcm_dev->dev_mode); + return 0; + } + + syna_dev_free_input_events(tcm); + + syna_pal_mutex_lock(&tcm->tp_event_mutex); + + retval = syna_dev_check_input_params(tcm); + if (retval == 0) + goto exit; + + if (tcm->input_dev != NULL) + syna_dev_release_input_device(tcm); + + retval = syna_dev_create_input_device(tcm); + if (retval < 0) { + LOGE("Fail to create input device\n"); + goto exit; + } + +exit: + syna_pal_mutex_unlock(&tcm->tp_event_mutex); + + return retval; +} + +/** + * syna_dev_isr() + * + * This is the function to be called when the interrupt is asserted. + * The purposes of this handler is to read events generated by device and + * retrieve all enqueued messages until ATTN is no longer asserted. + * + * @param + * [ in] irq: interrupt line + * [ in] data: private data being passed to the handler function + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static irqreturn_t syna_dev_isr(int irq, void *data) +{ + int retval; + unsigned char code = 0; + struct syna_tcm *tcm = data; + struct syna_hw_attn_data *attn = &tcm->hw_if->bdata_attn; + + if (unlikely(gpio_get_value(attn->irq_gpio) != attn->irq_on_state)) + goto exit; + + tcm->isr_pid = current->pid; + +#ifdef HAS_SYSFS_INTERFACE + if (tcm->is_attn_redirecting) { + syna_cdev_redirect_attn(tcm); + goto exit; + } +#endif + /* retrieve the original report date generated by firmware */ + retval = syna_tcm_get_event_data(tcm->tcm_dev, + &code, + &tcm->event_data); + if (retval < 0) { + LOGE("Fail to get event data\n"); + goto exit; + } + +#ifdef ENABLE_EXTERNAL_FRAME_PROCESS + if (tcm->report_to_queue[code] == EFP_ENABLE) { + syna_tcm_buf_lock(&tcm->tcm_dev->external_buf); + syna_cdev_update_report_queue(tcm, code, + &tcm->tcm_dev->external_buf); + syna_tcm_buf_unlock(&tcm->tcm_dev->external_buf); +#ifndef REPORT_CONCURRENTLY + goto exit; +#endif + } +#endif + /* report input event only when receiving a touch report */ + if (code == REPORT_TOUCH) { + /* parse touch report once received */ + retval = syna_tcm_parse_touch_report(tcm->tcm_dev, + tcm->event_data.buf, + tcm->event_data.data_length, + &tcm->tp_data); + if (retval < 0) { + LOGE("Fail to parse touch report\n"); + goto exit; + } + /* forward the touch event to system */ + syna_dev_report_input_events(tcm); + } + +exit: + return IRQ_HANDLED; +} + + +/** + * syna_dev_request_irq() + * + * Allocate an interrupt line and register the ISR handler + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_dev_request_irq(struct syna_tcm *tcm) +{ + int retval; + struct syna_hw_attn_data *attn = &tcm->hw_if->bdata_attn; +#ifdef DEV_MANAGED_API + struct device *dev = syna_request_managed_device(); + + if (!dev) { + LOGE("Invalid managed device\n"); + retval = -EINVAL; + goto exit; + } +#endif + + if (attn->irq_gpio < 0) { + LOGE("Invalid IRQ GPIO\n"); + retval = -EINVAL; + goto exit; + } + + attn->irq_id = gpio_to_irq(attn->irq_gpio); + +#ifdef DEV_MANAGED_API + retval = devm_request_threaded_irq(dev, + attn->irq_id, + NULL, + syna_dev_isr, + attn->irq_flags, + PLATFORM_DRIVER_NAME, + tcm); +#else /* Legacy API */ + retval = request_threaded_irq(attn->irq_id, + NULL, + syna_dev_isr, + attn->irq_flags, + PLATFORM_DRIVER_NAME, + tcm); +#endif + if (retval < 0) { + LOGE("Fail to request threaded irq\n"); + goto exit; + } + + attn->irq_enabled = true; + + LOGI("Interrupt handler registered\n"); + +exit: + return retval; +} + +/** + * syna_dev_release_irq() + * + * Release an interrupt line allocated previously + * + * @param + * [ in] tcm: the driver handle + * + * @return + * none. + */ +static void syna_dev_release_irq(struct syna_tcm *tcm) +{ + struct syna_hw_attn_data *attn = &tcm->hw_if->bdata_attn; +#ifdef DEV_MANAGED_API + struct device *dev = syna_request_managed_device(); + + if (!dev) { + LOGE("Invalid managed device\n"); + return; + } +#endif + + if (attn->irq_id <= 0) + return; + + if (tcm->hw_if->ops_enable_irq) + tcm->hw_if->ops_enable_irq(tcm->hw_if, false); + +#ifdef DEV_MANAGED_API + devm_free_irq(dev, attn->irq_id, tcm); +#else + free_irq(attn->irq_id, tcm); +#endif + + attn->irq_id = 0; + attn->irq_enabled = false; + + LOGI("Interrupt handler released\n"); +} + +/** + * syna_dev_set_up_app_fw() + * + * Implement the essential steps for the initialization including the + * preparation of app info and the configuration of touch report. + * + * This function should be called whenever the device initially powers + * up, resets, or firmware update. + * + * @param + * [ in] tcm: tcm driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_dev_set_up_app_fw(struct syna_tcm *tcm) +{ + int retval = 0; + struct tcm_dev *tcm_dev = tcm->tcm_dev; + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGN("Application firmware not running, current mode: %02x\n", + tcm_dev->dev_mode); + return -EINVAL; + } + + /* collect app info containing most of sensor information */ + retval = syna_tcm_get_app_info(tcm_dev, &tcm_dev->app_info); + if (retval < 0) { + LOGE("Fail to get application info\n"); + return -EIO; + } + + /* set up the format of touch report */ +#ifdef USE_CUSTOM_TOUCH_REPORT_CONFIG + retval = syna_tcm_set_touch_report_config(tcm_dev, + custom_touch_format, + (unsigned int)sizeof(custom_touch_format)); + if (retval < 0) { + LOGE("Fail to setup the custom touch report format\n"); + return -EIO; + } +#endif + /* preserve the format of touch report */ + retval = syna_tcm_preserve_touch_report_config(tcm_dev); + if (retval < 0) { + LOGE("Fail to preserve touch report config\n"); + return -EIO; + } + +#ifdef ENABLE_CUSTOM_TOUCH_ENTITY + /* set up custom touch data parsing method */ + retval = syna_tcm_set_custom_touch_entity_callback(tcm_dev, + syna_dev_parse_custom_touch_data_cb, + (void *)tcm); + if (retval < 0) { + LOGE("Fail to set up custom touch data parsing method\n"); + return -EIO; + } +#endif + return retval; +} + +int syna_set_fw_name(struct syna_tcm *tcm, char *name) +{ + int len; + char* pos; + + len = strlen(name); + if (len > FILE_NAME_LENGTH) { + LOGD("vendor name is too long"); + return -EINVAL; + } + pos = strstr(name, ".img"); + if (pos == NULL) + snprintf(tcm->fw_name, FILE_NAME_LENGTH, "%s", name); + else { + len = len - strlen(".img"); + snprintf(tcm->fw_name, len + 1, "%s", name); + } + + return 0; +} + +int syna_reflash_do_reflash(struct syna_tcm *tcm, char *name) +{ + int retval; + struct tcm_dev *tcm_dev; + const struct firmware *fw_entry; + const unsigned char *fw_image = NULL; + unsigned int fw_image_size; + char fwname[FILE_NAME_LENGTH] = { 0 }; + + tcm_dev = tcm->tcm_dev; + + snprintf(fwname, FILE_NAME_LENGTH, "%s.img", tcm->fw_name); + LOGD("Firmware name is %s\n", fwname); + + /* get firmware image */ + retval = request_firmware(&fw_entry, + fwname, + tcm->pdev->dev.parent); + if (retval < 0) { + LOGE("Fail to request %s\n", fwname); + return retval; + } + + fw_image = fw_entry->data; + fw_image_size = fw_entry->size; + + LOGE("Firmware image size = %d\n", fw_image_size); + + pm_stay_awake(&tcm->pdev->dev); + + /* perform fw update */ +#ifdef MULTICHIP_DUT_REFLASH + /* do firmware update for the multichip-based device */ + retval = syna_tcm_romboot_do_multichip_reflash(tcm_dev, + fw_image, + fw_image_size, + RESP_IN_ATTN, + false); +#else + /* do firmware update for the common device */ + retval = syna_tcm_do_fw_update(tcm_dev, + fw_image, + fw_image_size, + RESP_IN_ATTN, + false); +#endif + if (retval < 0) { + LOGE("Fail to do reflash\n"); + goto exit; + } + + /* re-initialize the app fw */ + retval = syna_dev_set_up_app_fw(tcm); + if (retval < 0) { + LOGE("Fail to set up app fw after fw update\n"); + goto exit; + } + + /* ensure the settings of input device + * if needed, re-create a new input device + */ + retval = syna_dev_set_up_input_device(tcm); + if (retval < 0) { + LOGE("Fail to register input device\n"); + goto exit; + } + +exit: + pm_relax(&tcm->pdev->dev); + return retval; +} + +/** + * syna_dev_reflash_startup_work() + * + * Perform firmware update during system startup. + * Function is available when the 'STARTUP_REFLASH' configuration + * is enabled. + * + * @param + * [ in] work: handle of work structure + * + * @return + * none. + */ +#ifdef STARTUP_REFLASH +struct void syna_dev_reflash_startup_work(struct work_struct *work) +{ + int retval; + struct delayed_work *delayed_work; + struct syna_tcm *tcm; + struct tcm_dev *tcm_dev; + const struct firmware *fw_entry; + const unsigned char *fw_image = NULL; + unsigned int fw_image_size; + + tcm->update_flag = FIRMWARE_UPDATE_SUCCESS; + delayed_work = container_of(work, struct delayed_work, work); + tcm = container_of(delayed_work, struct syna_tcm, reflash_work); + + tcm_dev = tcm->tcm_dev; + + if (reflash_data->fw_name[0] != 0) { + LOGD("Request firmware %s\n", reflash_data->fw_name); + /* get firmware image */ + retval = request_firmware(&fw_entry, + reflash_data->fw_name, + tcm->pdev->dev.parent); + if (retval < 0) { + LOGE("Fail to request %s\n", reflash_data->fw_name); + tcm->update_flag = FIRMWARE_UPDATE_FAILED; + return; + } + } + + fw_image = fw_entry->data; + fw_image_size = fw_entry->size; + + LOGD("Firmware image size = %d\n", fw_image_size); + + pm_stay_awake(&tcm->pdev->dev); + + /* perform fw update */ +#ifdef MULTICHIP_DUT_REFLASH + /* do firmware update for the multichip-based device */ + retval = syna_tcm_romboot_do_multichip_reflash(tcm_dev, + fw_image, + fw_image_size, + RESP_IN_ATTN, + false); +#else + /* do firmware update for the common device */ + retval = syna_tcm_do_fw_update(tcm_dev, + fw_image, + fw_image_size, + RESP_IN_ATTN, + false); +#endif + if (retval < 0) { + LOGE("Fail to do reflash\n"); + tcm->update_flag = FIRMWARE_UPDATE_FAILED; + goto exit; + } + + /* re-initialize the app fw */ + retval = syna_dev_set_up_app_fw(tcm); + if (retval < 0) { + LOGE("Fail to set up app fw after fw update\n"); + tcm->update_flag = FIRMWARE_UPDATE_FAILED; + goto exit; + } + + /* ensure the settings of input device + * if needed, re-create a new input device + */ + retval = syna_dev_set_up_input_device(tcm); + if (retval < 0) { + LOGE("Fail to register input device\n"); + tcm->update_flag = FIRMWARE_UPDATE_FAILED; + goto exit; + } +exit: + pm_relax(&tcm->pdev->dev); +} +#endif +/** + * syna_dev_enter_normal_sensing() + * + * Helper to enter normal seneing mode + * + * @param + * [ in] tcm: tcm driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_dev_enter_normal_sensing(struct syna_tcm *tcm) +{ + int retval = 0; + + if (!tcm) + return -EINVAL; + + /* bring out of sleep mode. */ + retval = syna_tcm_sleep(tcm->tcm_dev, false); + if (retval < 0) { + LOGE("Fail to exit deep sleep\n"); + return retval; + } + + /* disable low power gesture mode, if needed */ + if (tcm->lpwg_enabled) { + retval = syna_dev_enable_lowpwr_gesture(tcm, false); + if (retval < 0) { + LOGE("Fail to disable low power gesture mode\n"); + return retval; + } + } + + return 0; +} +/** + * syna_dev_enter_lowpwr_sensing() + * + * Helper to enter power-saved sensing mode, that + * may be the lower power gesture mode or deep sleep mode. + * + * @param + * [ in] tcm: tcm driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_dev_enter_lowpwr_sensing(struct syna_tcm *tcm) +{ + int retval = 0; + + if (!tcm) + return -EINVAL; + + /* enable low power gesture mode, if needed */ + if (tcm->lpwg_enabled) { + retval = syna_dev_enable_lowpwr_gesture(tcm, true); + if (retval < 0) { + LOGE("Fail to disable low power gesture mode\n"); + return retval; + } + } else { + /* enter sleep mode for non-LPWG cases */ + if (!tcm->slept_in_early_suspend) { + retval = syna_tcm_sleep(tcm->tcm_dev, true); + if (retval < 0) { + LOGE("Fail to enter deep sleep\n"); + return retval; + } + } + } + + tcm->pwr_state = LOW_PWR; + + return 0; +} +/** + * syna_dev_resume() + * + * Resume from the suspend state. + * If RESET_ON_RESUME is defined, a reset is issued to the touch controller. + * Otherwise, the touch controller is brought out of sleep mode. + * + * @param + * [ in] dev: an instance of device + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_dev_resume(struct device *dev) +{ + int retval; + struct syna_tcm *tcm = dev_get_drvdata(dev); + struct syna_hw_interface *hw_if = tcm->hw_if; + bool irq_enabled = true; + + /* exit directly if device isn't in suspend state */ + if (tcm->pwr_state == PWR_ON) + return 0; + + LOGI("Prepare to resume device\n"); + +#ifdef RESET_ON_RESUME + syna_pal_sleep_ms(RESET_ON_RESUME_DELAY_MS); + + if (hw_if->ops_hw_reset) { + hw_if->ops_hw_reset(hw_if); + } else { + retval = syna_tcm_reset(tcm->tcm_dev); + if (retval < 0) { + LOGE("Fail to do reset\n"); + goto exit; + } + } +#else + /* enter normal power mode */ + retval = syna_dev_enter_normal_sensing(tcm); + if (retval < 0) { + LOGE("Fail to enter normal power mode\n"); + goto exit; + } + + retval = syna_tcm_rezero(tcm->tcm_dev); + if (retval < 0) { + LOGE("Fail to rezero\n"); + goto exit; + } +#endif /* end of RESET_ON_RESUME */ + + tcm->pwr_state = PWR_ON; + + LOGI("Prepare to set up application firmware\n"); + + /* set up app firmware */ + retval = syna_dev_set_up_app_fw(tcm); + if (retval < 0) { + LOGE("Fail to set up app firmware on resume\n"); + goto exit; + } + + retval = 0; + + LOGI("Device resumed (pwr_state:%d)\n", tcm->pwr_state); + +exit: + /* once lpwg is enabled, irq should be enabled already. + * otherwise, set irq back to active mode. + */ + irq_enabled = (!tcm->lpwg_enabled); + + /* enable irq */ + if (irq_enabled && (hw_if->ops_enable_irq)) + hw_if->ops_enable_irq(hw_if, true); + + tcm->slept_in_early_suspend = false; + + return retval; +} + +/** + * syna_dev_suspend() + * + * Put device into suspend state. + * Enter either the lower power gesture mode or sleep mode. + * + * @param + * [ in] dev: an instance of device + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_dev_suspend(struct device *dev) +{ +#ifdef POWER_ALIVE_AT_SUSPEND + int retval; +#endif + struct syna_tcm *tcm = dev_get_drvdata(dev); + struct syna_hw_interface *hw_if = tcm->hw_if; + bool irq_disabled = true; + + /* exit directly if device is already in suspend state */ + if (tcm->pwr_state != PWR_ON) + return 0; + + LOGI("Prepare to suspend device\n"); + + /* clear all input events */ + syna_dev_free_input_events(tcm); + +#ifdef POWER_ALIVE_AT_SUSPEND + /* enter power saved mode if power is not off */ + retval = syna_dev_enter_lowpwr_sensing(tcm); + if (retval < 0) { + LOGE("Fail to enter suspended power mode\n"); + return retval; + } +#else + tcm->pwr_state = PWR_OFF; +#endif + + /* once lpwg is enabled, irq should be alive. + * otherwise, disable irq in suspend. + */ + irq_disabled = (!tcm->lpwg_enabled); + + /* disable irq */ + if (irq_disabled && (hw_if->ops_enable_irq)) + hw_if->ops_enable_irq(hw_if, false); + + LOGI("Device suspended (pwr_state:%d)\n", tcm->pwr_state); + + return 0; +} + +//#if defined(ENABLE_DISP_NOTIFIER) +/** + * syna_dev_early_suspend() + * + * If having early suspend support, enter the sleep mode for + * non-lpwg cases. + * + * @param + * [ in] dev: an instance of device + * + * @return + * on success, 0; otherwise, negative value on error. + */ +int syna_dev_early_suspend(struct device *dev) +{ + int retval; + struct syna_tcm *tcm = dev_get_drvdata(dev); + + LOGI("early_suspend enter\n"); + + /* exit directly if device is already in suspend state */ + if (tcm->pwr_state != PWR_ON) + return 0; + + if (!tcm->lpwg_enabled) { + retval = syna_tcm_sleep(tcm->tcm_dev, true); + if (retval < 0) { + LOGE("Fail to enter deep sleep\n"); + return retval; + } + } + + tcm->slept_in_early_suspend = true; + LOGI("early_suspend exit\n"); + + return 0; +} + +#if defined(ENABLE_DISP_NOTIFIER) && !defined (CONFIG_INPUT_TOUCHSCREEN_MMI) +/** + * syna_dev_fb_notifier_cb() + * + * Listen the display screen on/off event and perform the corresponding + * actions. + * + * @param + * [ in] nb: instance of notifier_block + * [ in] action: fb action + * [ in] data: fb event data + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_dev_fb_notifier_cb(struct notifier_block *nb, + unsigned long action, void *data) +{ + int retval; + int transition; +#if defined(USE_DRM_PANEL_NOTIFIER) + struct drm_panel_notifier *evdata = data; +#else + struct fb_event *evdata = data; +#endif + struct syna_tcm *tcm = container_of(nb, struct syna_tcm, fb_notifier); + int time = 0; + int disp_blank_powerdown; + int disp_early_event_blank; + int disp_blank; + int disp_blank_unblank; + + if (!evdata || !evdata->data || !tcm) + return 0; + + retval = 0; + +#if defined(USE_DRM_PANEL_NOTIFIER) + disp_blank_powerdown = DRM_PANEL_BLANK_POWERDOWN; + disp_early_event_blank = DRM_PANEL_EARLY_EVENT_BLANK; + disp_blank = DRM_PANEL_EVENT_BLANK; + disp_blank_unblank = DRM_PANEL_BLANK_UNBLANK; +#else + disp_blank_powerdown = FB_BLANK_POWERDOWN; + disp_early_event_blank = FB_EARLY_EVENT_BLANK; + disp_blank = FB_EVENT_BLANK; + disp_blank_unblank = FB_BLANK_UNBLANK; +#endif + + transition = *(int *)evdata->data; + + /* confirm the firmware flashing is completed before screen off */ + if (transition == disp_blank_powerdown) { + while (ATOMIC_GET(tcm->tcm_dev->firmware_flashing)) { + syna_pal_sleep_ms(500); + + time += 500; + if (time >= 5000) { + LOGE("Timed out waiting for reflashing\n"); + ATOMIC_SET(tcm->tcm_dev->firmware_flashing, 0); + return -EIO; + } + } + } + + if (action == disp_early_event_blank && + transition == disp_blank_powerdown) { + retval = syna_dev_early_suspend(&tcm->pdev->dev); + } else if (action == disp_blank) { + if (transition == disp_blank_powerdown) { + retval = syna_dev_suspend(&tcm->pdev->dev); + tcm->fb_ready = 0; + } else if (transition == disp_blank_unblank) { +#ifndef RESUME_EARLY_UNBLANK + retval = syna_dev_resume(&tcm->pdev->dev); + tcm->fb_ready++; +#endif + } else if (action == disp_early_event_blank && + transition == disp_blank_unblank) { +#ifdef RESUME_EARLY_UNBLANK + retval = syna_dev_resume(&tcm->pdev->dev); + tcm->fb_ready++; +#endif + } + } + + return 0; +} +#endif + +/** + * syna_dev_disconnect() + * + * This function will power off the connected device. + * Then, all the allocated resource will be released. + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_dev_disconnect(struct syna_tcm *tcm) +{ + struct syna_hw_interface *hw_if = tcm->hw_if; + + if (tcm->is_connected == false) { + LOGI("%s already disconnected\n", PLATFORM_DRIVER_NAME); + return 0; + } + +#ifdef STARTUP_REFLASH + cancel_delayed_work_sync(&tcm->reflash_work); + flush_workqueue(tcm->reflash_workqueue); + destroy_workqueue(tcm->reflash_workqueue); +#endif + + /* free interrupt line */ + if (hw_if->bdata_attn.irq_id) + syna_dev_release_irq(tcm); + + /* unregister input device */ + syna_dev_release_input_device(tcm); + + tcm->input_dev_params.max_x = 0; + tcm->input_dev_params.max_y = 0; + tcm->input_dev_params.max_objects = 0; + + /* power off */ + if (hw_if->ops_power_on) + hw_if->ops_power_on(hw_if, false); + + tcm->pwr_state = PWR_OFF; + tcm->is_connected = false; + + LOGI("%s device disconnected\n", PLATFORM_DRIVER_NAME); + + return 0; +} + +/** + * syna_dev_connect() + * + * This function will power on and identify the connected device. + * At the end of function, the ISR will be registered as well. + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_dev_connect(struct syna_tcm *tcm) +{ + int retval; + struct syna_hw_interface *hw_if = tcm->hw_if; + struct tcm_dev *tcm_dev = tcm->tcm_dev; + + if (!tcm_dev) { + LOGE("Invalid tcm_dev\n"); + return -EINVAL; + } + + if (tcm->is_connected) { + LOGI("%s already connected\n", PLATFORM_DRIVER_NAME); + return 0; + } + + /* power on the connected device */ + if (hw_if->ops_power_on) { + retval = hw_if->ops_power_on(hw_if, true); + if (retval < 0) + return -ENODEV; + } + + /* perform a hardware reset */ + if (hw_if->ops_hw_reset) + hw_if->ops_hw_reset(hw_if); + + /* detect which modes of touch controller is running + * + * the signal of the Touch IC ready is called as "identify" + * report and generated by firmware + */ + retval = syna_tcm_detect_device(tcm->tcm_dev); + if (retval < 0) { + LOGE("Fail to detect the device\n"); + goto err_detect_dev; + } + + switch (retval) { + case MODE_APPLICATION_FIRMWARE: + retval = syna_dev_set_up_app_fw(tcm); + if (retval < 0) { +#ifdef FORCE_CONNECTION + LOGW("App firmware is not available yet\n"); + LOGW("However, connect and skip initialization\n"); +#else + LOGE("Fail to set up application firmware\n"); + /* switch to bootloader mode when failed */ + LOGI("Switch device to bootloader mode instead\n"); + syna_tcm_switch_fw_mode(tcm_dev, + MODE_BOOTLOADER, + FW_MODE_SWITCH_DELAY_MS); +#endif + } else { + /* allocate and register to input device subsystem */ + retval = syna_dev_set_up_input_device(tcm); + if (retval < 0) { + LOGE("Fail to set up input device\n"); + goto err_setup_input_dev; + } + } + + break; + default: + LOGN("Application firmware not running, current mode: %02x\n", + retval); + break; + } + + /* register the interrupt handler */ + retval = syna_dev_request_irq(tcm); + if (retval < 0) { + LOGE("Fail to request the interrupt line\n"); + goto err_request_irq; + } + + /* for the reference, + * create a delayed work to perform fw update during the startup time + */ +#ifdef STARTUP_REFLASH + tcm->reflash_workqueue = + create_singlethread_workqueue("syna_reflash"); + INIT_DELAYED_WORK(&tcm->reflash_work, syna_dev_reflash_startup_work); + queue_delayed_work(tcm->reflash_workqueue, &tcm->reflash_work, + msecs_to_jiffies(STARTUP_REFLASH_DELAY_TIME_MS)); +#endif + + tcm->pwr_state = PWR_ON; + tcm->is_connected = true; + + LOGI("TCM packrat: %d\n", tcm->tcm_dev->packrat_number); + LOGI("Configuration: name(%s), lpwg(%s), hw_reset(%s), irq_ctrl(%s)\n", + PLATFORM_DRIVER_NAME, + (tcm->lpwg_enabled) ? "yes" : "no", + (hw_if->ops_hw_reset) ? "yes" : "no", + (hw_if->ops_enable_irq) ? "yes" : "no"); + + return 0; + +err_request_irq: + /* unregister input device */ + syna_dev_release_input_device(tcm); + +err_setup_input_dev: +err_detect_dev: + if (hw_if->ops_power_on) + hw_if->ops_power_on(hw_if, false); + + return retval; +} + +#ifdef USE_DRM_PANEL_NOTIFIER +static struct drm_panel *syna_dev_get_panel(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 NULL; + + 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)) { + LOGI("Find available panel\n"); + return panel; + } + } + + return NULL; +} +#endif + +/** + * syna_dev_probe() + * + * Install the TouchComm device driver + * + * @param + * [ in] pdev: an instance of platform device + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_dev_probe(struct platform_device *pdev) +{ + int retval; + struct syna_tcm *tcm = NULL; + struct tcm_dev *tcm_dev = NULL; + struct syna_hw_interface *hw_if = NULL; +#if defined(USE_DRM_PANEL_NOTIFIER) + struct device *dev; +#endif + +#ifdef ENABLE_WAKEUP_GESTURE + static bool initialized_sensor; +#endif + + hw_if = pdev->dev.platform_data; + if (!hw_if) { + LOGE("Fail to find hardware configuration\n"); + return -EINVAL; + } + + tcm = syna_pal_mem_alloc(1, sizeof(struct syna_tcm)); + if (!tcm) { + LOGE("Fail to create the instance of syna_tcm\n"); + return -ENOMEM; + } + + /* allocate the TouchCom device handle + * recommend to set polling mode here because isr is not registered yet + */ + retval = syna_tcm_allocate_device(&tcm_dev, hw_if, RESP_IN_POLLING); + if ((retval < 0) || (!tcm_dev)) { + LOGE("Fail to allocate TouchCom device handle\n"); + goto err_allocate_cdev; + } + + tcm->tcm_dev = tcm_dev; + tcm->pdev = pdev; + tcm->hw_if = hw_if; + + syna_tcm_buf_init(&tcm->event_data); + + syna_pal_mutex_alloc(&tcm->tp_event_mutex); + +#ifdef ENABLE_WAKEUP_GESTURE + tcm->lpwg_enabled = true; +#else + tcm->lpwg_enabled = false; +#endif + tcm->irq_wake = false; + +#ifdef ENABLE_WAKEUP_GESTURE + if (!initialized_sensor) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&gesture_wakelock, WAKE_LOCK_SUSPEND, "syna_gesture_wakelock"); +#else + PM_WAKEUP_REGISTER(&pdev->dev, gesture_wakelock, "syna_gesture_wakelock"); + if (!gesture_wakelock) { + LOGE("failed to allocate wakeup source\n"); + } +#endif + initialized_sensor = true; + } +#endif + + tcm->is_connected = false; + tcm->pwr_state = PWR_OFF; + + tcm->dev_connect = syna_dev_connect; + tcm->dev_disconnect = syna_dev_disconnect; + tcm->dev_set_up_app_fw = syna_dev_set_up_app_fw; + tcm->dev_resume = syna_dev_resume; + tcm->dev_suspend = syna_dev_suspend; + + platform_set_drvdata(pdev, tcm); + + device_init_wakeup(&pdev->dev, 1); + +#if defined(TCM_CONNECT_IN_PROBE) + /* connect to target device */ + retval = tcm->dev_connect(tcm); + if (retval < 0) { + LOGE("Fail to connect to the device\n"); + syna_pal_mutex_free(&tcm->tp_event_mutex); + goto err_connect; + } +#endif + +#ifdef HAS_SYSFS_INTERFACE + /* create the device file and register to char device classes */ + retval = syna_cdev_create_sysfs(tcm, pdev); + if (retval < 0) { + LOGE("Fail to create the device sysfs\n"); + syna_pal_mutex_free(&tcm->tp_event_mutex); + goto err_create_cdev; + } +#endif + +#if defined(ENABLE_DISP_NOTIFIER) +#if defined(USE_DRM_PANEL_NOTIFIER) + dev = syna_request_managed_device(); + active_panel = syna_dev_get_panel(dev->of_node); + if (active_panel) { + tcm->fb_notifier.notifier_call = syna_dev_fb_notifier_cb; + retval = drm_panel_notifier_register(active_panel, + &tcm->fb_notifier); + if (retval < 0) { + LOGE("Fail to register FB notifier client\n"); + goto err_create_cdev; + } + } else { + LOGE("No available drm panel\n"); + } +#else + tcm->fb_notifier.notifier_call = syna_dev_fb_notifier_cb; + retval = fb_register_client(&tcm->fb_notifier); + if (retval < 0) { + LOGE("Fail to register FB notifier client\n"); + goto err_create_cdev; + } +#endif +#endif + +#if defined(ENABLE_HELPER) + ATOMIC_SET(tcm->helper.task, HELP_NONE); + tcm->helper.workqueue = + create_singlethread_workqueue("synaptics_tcm_helper"); + INIT_WORK(&tcm->helper.work, syna_dev_helper_work); + /* set up custom touch data parsing method */ + syna_tcm_set_reset_occurrence_callback(tcm_dev, + syna_dev_reset_detected_cb, + (void *)tcm); +#endif + + LOGI("%s TouchComm driver v%d.%s installed\n", + PLATFORM_DRIVER_NAME, + SYNAPTICS_TCM_DRIVER_VERSION, + SYNAPTICS_TCM_DRIVER_SUBVER); + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + syna_ts_mmi_dev_register(tcm); +#endif + + return 0; + +#ifdef HAS_SYSFS_INTERFACE +err_create_cdev: + syna_tcm_remove_device(tcm->tcm_dev); +#endif + +#if defined(TCM_CONNECT_IN_PROBE) + tcm->dev_disconnect(tcm); +err_connect: +#endif + syna_tcm_buf_release(&tcm->event_data); + syna_pal_mutex_free(&tcm->tp_event_mutex); +err_allocate_cdev: + syna_pal_mem_free((void *)tcm); + + return retval; +} + +/** + * syna_dev_remove() + * + * Release all allocated resources and remove the TouchCom device handle + * + * @param + * [ in] pdev: an instance of platform device + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_dev_remove(struct platform_device *pdev) +{ + struct syna_tcm *tcm = platform_get_drvdata(pdev); + + if (!tcm) { + LOGW("Invalid handle to remove\n"); + return 0; + } + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + syna_ts_mmi_dev_unregister(tcm); +#endif + + +#if defined(ENABLE_HELPER) + cancel_work_sync(&tcm->helper.work); + flush_workqueue(tcm->helper.workqueue); + destroy_workqueue(tcm->helper.workqueue); +#endif +#if defined(ENABLE_DISP_NOTIFIER) +#if defined(USE_DRM_PANEL_NOTIFIER) + if (active_panel) + drm_panel_notifier_unregister(active_panel, + &tcm->fb_notifier); +#else + fb_unregister_client(&tcm->fb_notifier); +#endif +#endif + +#ifdef HAS_SYSFS_INTERFACE + /* remove the cdev and sysfs nodes */ + syna_cdev_remove_sysfs(tcm); +#endif + + /* check the connection statusm, and do disconnection */ + if (tcm->dev_disconnect(tcm) < 0) + LOGE("Fail to do device disconnection\n"); + + syna_tcm_buf_release(&tcm->event_data); + + syna_pal_mutex_free(&tcm->tp_event_mutex); + + /* remove the allocated tcm device */ + syna_tcm_remove_device(tcm->tcm_dev); + + /* release the device context */ + syna_pal_mem_free((void *)tcm); + + return 0; +} + +/** + * syna_dev_shutdown() + * + * Call syna_dev_remove() to release all resources + * + * @param + * [in] pdev: an instance of platform device + * + * @return + * none. + */ +static void syna_dev_shutdown(struct platform_device *pdev) +{ + syna_dev_remove(pdev); +} + + +/** + * Declare a TouchComm platform device + */ +#ifdef CONFIG_PM +static const struct dev_pm_ops syna_dev_pm_ops = { +#if !defined(ENABLE_DISP_NOTIFIER) && !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + .suspend = syna_dev_suspend, + .resume = syna_dev_resume, +#endif +}; +#endif + +static struct platform_driver syna_dev_driver = { + .driver = { + .name = PLATFORM_DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &syna_dev_pm_ops, +#endif + }, + .probe = syna_dev_probe, + .remove = syna_dev_remove, + .shutdown = syna_dev_shutdown, +}; + + +/** + * syna_dev_module_init() + * + * The entry function of the reference driver, which initialize the + * lower-level bus and register a platform driver. + * + * @param + * void. + * + * @return + * 0 if the driver registered and bound to a device, + * else returns a negative error code and with the driver not registered. + */ +static int __init syna_dev_module_init(void) +{ + int retval; + + retval = syna_hw_interface_init(); + if (retval < 0) + return retval; + + return platform_driver_register(&syna_dev_driver); +} + +/** + * syna_dev_module_exit() + * + * Function is called when un-installing the driver. + * Remove the registered platform driver and the associated bus driver. + * + * @param + * void. + * + * @return + * none. + */ +static void __exit syna_dev_module_exit(void) +{ + platform_driver_unregister(&syna_dev_driver); + + syna_hw_interface_exit(); +} + +late_initcall(syna_dev_module_init); +module_exit(syna_dev_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Touch Driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2.h b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2.h new file mode 100644 index 000000000000..ed273e9f3864 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2.h @@ -0,0 +1,487 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file syna_tcm2.h + * + * The header file is used for the Synaptics TouchComm reference driver. + * Platform-specific functions and included headers are implemented in + * syna_touchcom_platform.h and syna_touchcom_runtime.h. + */ + +#ifndef _SYNAPTICS_TCM2_DRIVER_H_ +#define _SYNAPTICS_TCM2_DRIVER_H_ + +#include "syna_tcm2_platform.h" +#include "synaptics_touchcom_core_dev.h" +#include "synaptics_touchcom_func_touch.h" + +#define PLATFORM_DRIVER_NAME "synaptics_tcm" + +#define TOUCH_INPUT_NAME "synaptics_tcm_touch" +#define TOUCH_INPUT_PHYS_PATH "synaptics_tcm/touch_input" + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +#include +#endif + +#define CHAR_DEVICE_NAME "tcm" +#define CHAR_DEVICE_MODE (0x0600) + +#define SYNAPTICS_TCM_DRIVER_ID (1 << 0) +#define SYNAPTICS_TCM_DRIVER_VERSION 1 +#define SYNAPTICS_TCM_DRIVER_SUBVER "3.0" + +#define FILE_NAME_LENGTH 128 + +#define IS_NOT_FW_MODE(mode) \ + ((mode != MODE_APPLICATION_FIRMWARE) && (mode != MODE_HOSTDOWNLOAD_FIRMWARE)) +/* +#define IS_NOT_FW_MODE(mode) \ + ((mode == MODE_APPLICATION_FIRMWARE) || (mode == MODE_HOSTDOWNLOAD_FIRMWARE)) +*/ +/** + * @section: Driver Configurations + * + * The macros in the driver files below are used for doing compile time + * configuration of the driver. + */ + +/** + * @brief: HAS_SYSFS_INTERFACE + * Open to enable the sysfs interface + * + * @brief: HAS_REFLASH_FEATURE + * Open to enable firmware reflash features + * + * @brief: HAS_ROMBOOT_REFLASH_FEATURE + * Open to enable ROMBOOT reflash features + * + * @brief: HAS_TESTING_FEATURE + * Open to enable testing features + */ +#if defined(CONFIG_TOUCHSCREEN_SYNA_TCM2_SYSFS) +#define HAS_SYSFS_INTERFACE +#endif +#if defined(CONFIG_TOUCHSCREEN_SYNA_TCM2_REFLASH) +#define HAS_REFLASH_FEATURE +#endif +#if defined(CONFIG_TOUCHSCREEN_SYNA_TCM2_ROMBOOT) +#define HAS_ROMBOOT_REFLASH_FEATURE +#endif +#if defined(CONFIG_TOUCHSCREEN_SYNA_TCM2_TESTING) +#define HAS_TESTING_FEATURE +#endif + +/** + * @brief: TYPE_B_PROTOCOL + * Open to enable the multi-touch (MT) protocol + */ +#define TYPE_B_PROTOCOL + +/** + * @brief: RESET_ON_RESUME + * Open if willing to issue a reset to the touch controller + * from suspend. + * Set "disable" in default. + */ +/* #define RESET_ON_RESUME */ + +/** + * @brief ENABLE_WAKEUP_GESTURE + * Open if having wake-up gesture support. + */ +#define ENABLE_WAKEUP_GESTURE + +/** + * @brief REPORT_SWAP_XY + * Open if trying to swap x and y position coordinate reported. + * @brief REPORT_FLIP_X + * Open if trying to flip x position coordinate reported. + * @brief REPORT_FLIP_Y + * Open if trying to flip x position coordinate reported. + */ +/* #define REPORT_SWAP_XY */ +/* #define REPORT_FLIP_X */ +/* #define REPORT_FLIP_Y */ + +/** + * @brief REPORT_TOUCH_WIDTH + * Open if willing to add the width data to the input event. + */ +#define REPORT_TOUCH_WIDTH + +/** + * @brief USE_CUSTOM_TOUCH_REPORT_CONFIG + * Open if willing to set up the format of touch report. + * The custom_touch_format[] array in syna_tcm2.c can be used + * to describe the customized report format. + */ +/* #define USE_CUSTOM_TOUCH_REPORT_CONFIG */ + +/** + * @brief STARTUP_REFLASH + * Open if willing to do fw checking and update at startup. + * The firmware image will be obtained by request_firmware() API, + * so please ensure the image is built-in or included properly. + * + * This property is available only when SYNA_TCM2_REFLASH + * feature is enabled. + */ +#if defined(HAS_REFLASH_FEATURE) || defined(HAS_ROMBOOT_REFLASH_FEATURE) +#define STARTUP_REFLASH +#endif +/** + * @brief MULTICHIP_DUT_REFLASH + * Open if willing to do fw update and the DUT belongs to multi-chip + * product. This property dependent on STARTUP_REFLASH property. + * + * Set "disable" in default. + */ +#if defined(HAS_ROMBOOT_REFLASH_FEATURE) && defined(STARTUP_REFLASH) +/* #define MULTICHIP_DUT_REFLASH */ +#endif + +/** + * @brief ENABLE_DISP_NOTIFIER + * Open if having display notification event and willing to listen + * the event from display driver. + * + * Set "disable" in default due to no generic notifier for DRM + */ +#if defined(CONFIG_FB) || defined(CONFIG_DRM_PANEL) +/* #define ENABLE_DISP_NOTIFIER */ +#endif +/** + * @brief RESUME_EARLY_UNBLANK + * Open if willing to resume in early un-blanking state. + * + * This property is available only when ENABLE_DISP_NOTIFIER + * feature is enabled. + */ +#ifdef ENABLE_DISP_NOTIFIER +/* #define RESUME_EARLY_UNBLANK */ +#endif +/** + * @brief USE_DRM_PANEL_NOTIFIER + * Open if willing to listen the notification event from + * DRM_PANEL. Please be noted that 'struct drm_panel_notifier' + * must be implemented in the target BSP. + * + * This property is available only when ENABLE_DISP_NOTIFIER + * feature is enabled. + * + * Set "disable" in default due to no generic notifier for DRM + */ +#if defined(ENABLE_DISP_NOTIFIER) && defined(CONFIG_DRM_PANEL) +#define USE_DRM_PANEL_NOTIFIER +#endif + +/** + * @brief ENABLE_EXTERNAL_FRAME_PROCESS + * Open if having external frame process to the userspace application. + * + * Set "enable" in default + * + * @brief REPORT_TYPES + * Total types of report being used for external frame process. + * + * @brief EFP_ENABLE / EFP_DISABLE + * Specific value to label whether the report is required to be + * process or not. + * + * @brief REPORT_CONCURRENTLY + * Open if willing to concurrently handle reports for both kernel + * and userspace application. + * + * Set "disable" in default + */ +#define ENABLE_EXTERNAL_FRAME_PROCESS +#define REPORT_TYPES (256) +#define EFP_ENABLE (1) +#define EFP_DISABLE (0) +/* #define REPORT_CONCURRENTLY */ + +/** + * @brief TCM_CONNECT_IN_PROBE + * Open if willing to detect and connect to TouchComm device at + * probe function; otherwise, please invoke connect() manually. + * + * Set "enable" in default + */ +#define TCM_CONNECT_IN_PROBE + +/** + * @brief FORCE_CONNECTION + * Open if willing to connect to TouchComm device w/o error outs. + * + * Set "disable" in default + */ +/* #define FORCE_CONNECTION */ + +/** + * @brief ENABLE_CUSTOM_TOUCH_ENTITY + * Open if having custom requirements to parse the custom code + * entity in the touch report. + * + * Set "disable" in default + */ +/* #define ENABLE_CUSTOM_TOUCH_ENTITY */ + +/** + * @brief ENABLE_HELPER + * Open if willing to do additional handling upon helper wokqueue + * + * Set "disable" in default + */ +/* #define ENABLE_HELPER */ + +/** + * @brief: Power States + * + * Enumerate the power states of device + */ +enum power_state { + PWR_OFF = 0, + PWR_ON, + LOW_PWR, +}; + +#if defined(ENABLE_HELPER) +/** + * @brief: Tasks for helper + * + * Tasks being supported in the helper thread and the structure + */ +enum helper_task { + HELP_NONE = 0, + HELP_RESET_DETECTED, +}; + +struct syna_tcm_helper { + syna_pal_atomic_t task; + struct work_struct work; + struct workqueue_struct *workqueue; +}; +#endif + +/** + * @brief: context of the synaptics linux-based driver + * + * The structure defines the kernel specific data in linux-based driver + */ +struct syna_tcm { + + /* TouchComm device core context */ + struct tcm_dev *tcm_dev; + + /* PLatform device driver */ + struct platform_device *pdev; + + /* Generic touched data generated by tcm core lib */ + struct tcm_touch_data_blob tp_data; + + syna_pal_mutex_t tp_event_mutex; + + unsigned char prev_obj_status[MAX_NUM_OBJECTS]; + + /* Buffer stored the irq event data */ + struct tcm_buffer event_data; + + /* Hardware interface layer */ + struct syna_hw_interface *hw_if; + + /* ISR-related variables */ + pid_t isr_pid; + bool irq_wake; + + /* cdev and sysfs nodes creation */ + struct cdev char_dev; + dev_t char_dev_num; + int char_dev_ref_count; + + struct class *device_class; + struct device *device; + + struct kobject *sysfs_dir; + + /* Input device registration */ + struct input_dev *input_dev; + struct input_params { + unsigned int max_x; + unsigned int max_y; + unsigned int max_objects; + } input_dev_params; + + /* Workqueue used for fw update */ + struct delayed_work reflash_work; + struct workqueue_struct *reflash_workqueue; + + /* IOCTL-related variables */ + pid_t proc_pid; + struct task_struct *proc_task; + + /* flags */ + int pwr_state; + bool slept_in_early_suspend; + bool lpwg_enabled; + bool is_attn_redirecting; + unsigned char fb_ready; + bool is_connected; + char fw_name[FILE_NAME_LENGTH]; + + /* framebuffer callbacks notifier */ +#if defined(ENABLE_DISP_NOTIFIER) + struct notifier_block fb_notifier; +#endif + + /* fifo to pass the data to userspace */ + unsigned int fifo_remaining_frame; + struct list_head frame_fifo_queue; + wait_queue_head_t wait_frame; + unsigned char report_to_queue[REPORT_TYPES]; + +#if defined(ENABLE_HELPER) + /* helper workqueue */ + struct syna_tcm_helper helper; +#endif + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + struct ts_mmi_class_methods *imports; +#endif + + /* testing */ + char *testing_log_data; + unsigned int testing_log_size; + syna_pal_mutex_t testing_mutex; + + /* Specific function pointer to do device connection. + * + * This function will power on and identify the connected device. + * At the end of function, the ISR will be registered as well. + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ + int (*dev_connect)(struct syna_tcm *tcm); + + /* Specific function pointer to disconnect the device + * + * This function will power off the connected device. + * Then, all the allocated resource will be released. + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ + int (*dev_disconnect)(struct syna_tcm *tcm); + + /* Specific function pointer to set up app fw firmware + * + * This function should be called whenever the device initially + * powers up, resets, or firmware update. + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ + int (*dev_set_up_app_fw)(struct syna_tcm *tcm); + + /* Specific function pointer to resume the device from suspend state. + * + * @param + * [ in] dev: an instance of device + * + * @return + * on success, 0; otherwise, negative value on error. + */ + int (*dev_resume)(struct device *dev); + + /* Specific function pointer to put device into suspend state. + * + * @param + * [ in] dev: an instance of device + * + * @return + * on success, 0; otherwise, negative value on error. + */ + int (*dev_suspend)(struct device *dev); + +}; + +/** + * @brief: Helpers for cdevice nodes and sysfs nodes creation + * + * These functions are implemented in syna_touchcom_sysfs.c + * and available only whentesting_ HAS_SYSFS_INTERFACE is enabled. + */ +#ifdef HAS_SYSFS_INTERFACE + +int syna_cdev_create_sysfs(struct syna_tcm *ptcm, + struct platform_device *pdev); + +void syna_cdev_remove_sysfs(struct syna_tcm *ptcm); + +void syna_cdev_redirect_attn(struct syna_tcm *ptcm); + +#ifdef ENABLE_EXTERNAL_FRAME_PROCESS +void syna_cdev_update_report_queue(struct syna_tcm *tcm, + unsigned char code, struct tcm_buffer *pevent_data); +#endif + +#endif + +#endif /* end of _SYNAPTICS_TCM2_DRIVER_H_ */ + +/** +* @section: STARTUP_REFLASH_DELAY_TIME_MS +* The delayed time to start fw update during the startup time. +* This configuration depends on STARTUP_REFLASH. +*/ + +#ifdef STARTUP_REFLASH +#define STARTUP_REFLASH_DELAY_TIME_MS (200) +#define FW_IMAGE_NAME "synaptics/firmware.img" +void syna_dev_reflash_startup_work(struct work_struct *work); +#endif + +int syna_set_fw_name(struct syna_tcm *tcm, char *name); +int syna_reflash_do_reflash(struct syna_tcm *tcm, char *fwname); +int syna_dev_early_suspend(struct device *dev); +bool syna_check_panel(struct device_node *np); +extern bool limit_panel; \ No newline at end of file diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_platform.h b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_platform.h new file mode 100644 index 000000000000..beabcc53fd4b --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_platform.h @@ -0,0 +1,319 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file: syna_tcm2_platform.h + * + * This file declares the platform-specific or hardware relevant data. + * + * The main structure, syna_hw_interface, abstracts the bus transferred, + * ATTN signal, RST_N pin, and power control operations. + */ + +#ifndef _SYNAPTICS_TCM2_PLATFORM_H_ +#define _SYNAPTICS_TCM2_PLATFORM_H_ + +#include "syna_tcm2_runtime.h" + +/** + * @section: The capability of bus transferred + * + * Declare read/write capability in bytes (0 = unlimited) + */ +#define RD_CHUNK_SIZE (0) +#define WR_CHUNK_SIZE (512) + +/** + * @section: Type of power supply + * + * The below enumerates the type of power supply + */ +enum power_supply { + PSU_REGULATOR = 0, + PSU_GPIO, +}; + +/** + * @section: Defined Hardware-Specific Data + * + * @brief: syna_hw_bus_data + * Hardware Data for bus transferred + * + * @brief: syna_hw_attn_data + * Hardware Data for ATTN signal + * + * @brief: syna_hw_rst_data + * Hardware Data for RST_N pin + * + * @brief: syna_hw_pwr_data + * Hardware Data for power control + * + * @brief: syna_hw_interface + * Contain all above hardware data and abstract the + * hardware operations + */ + +/* The hardware data especially for bus transferred */ +struct syna_hw_bus_data { + unsigned char type; + /* capability of i/o chunk */ + unsigned int rd_chunk_size; + unsigned int wr_chunk_size; + /* clock frequency in hz */ + unsigned int frequency_hz; + /* parameters for i2c */ + unsigned int i2c_addr; + /* parameters for spi */ + unsigned int spi_mode; + unsigned int spi_byte_delay_us; + unsigned int spi_block_delay_us; + /* mutex to protect the i/o, if needed */ + syna_pal_mutex_t io_mutex; + /* parameters for io switch, if needed */ + int switch_gpio; + int switch_state; +}; + +/* The hardware data especially for ATTN signal */ +struct syna_hw_attn_data { + /* parameters */ + int irq_gpio; + int irq_on_state; + unsigned long irq_flags; + int irq_id; + bool irq_enabled; + /* mutex to protect the irq control, if needed */ + syna_pal_mutex_t irq_en_mutex; +}; + +/* The hardware data especially for RST_N pin */ +struct syna_hw_rst_data { + /* parameters */ + int reset_gpio; + int reset_on_state; + unsigned int reset_delay_ms; + unsigned int reset_active_ms; +}; + +/* The hardware data especially for power control */ +struct syna_hw_pwr_data { + int psu; + /* parameters */ + int vdd_gpio; + int avdd_gpio; + int power_on_state; + unsigned int power_on_delay_ms; + /* voltage */ + unsigned int vdd; + unsigned int vled; + unsigned int vio; + unsigned int vddtx; + /* regulators */ + const char *vdd_reg_name; + void *vdd_reg_dev; + const char *avdd_reg_name; + void *avdd_reg_dev; +}; + +/** + * @section: Hardware Interface Abstraction Layer + * + * The structure contains the hardware-specific implementations + * on the target platform. + */ +struct syna_hw_interface { + /* The handle of hardware device */ + void *pdev; + + /* Hardware specific data */ + struct syna_hw_bus_data bdata_io; + struct syna_hw_attn_data bdata_attn; + struct syna_hw_rst_data bdata_rst; + struct syna_hw_pwr_data bdata_pwr; + + /* Operation to do power on/off, if supported + * + * This is an optional operation. + * + * Implementation should request that the power device be + * enabled with the output at the proper voltage. + * + * Assign the pointer NULL if power supply module is not controllable. + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] en: '1' for powering on, and '0' for powering off + * + * @return + * 0 on success; otherwise, on error. + */ + int (*ops_power_on)(struct syna_hw_interface *hw_if, + bool en); + + /* Operation to perform the hardware reset, if supported + * + * This is an optional operation. + * + * The actual reset waveform should be reference to the device spec. + * + * Assign the pointer NULL if RST_N pin is not controllable. + * + * @param + * [ in] hw_if: the handle of hw interface + * + * @return + * 0 on success; otherwise, on error. + */ + void (*ops_hw_reset)(struct syna_hw_interface *hw_if); + + /* Operation to set up the bus connection + * + * This is an optional operation to add in the extra configuration + * before doing connection. + * + * Assign the pointer NULL if this operation is not required. + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] config: parameters to change + * + * @return + * 0 or positive value on success; otherwise, on error. + */ + int (*ops_bus_setup)(struct syna_hw_interface *hw_if, + struct syna_hw_bus_data *config); + + /* Operation to read the bare data from bus + * + * This is an essential operation; otherwise, the communication + * will not be created. + * + * @param + * [ in] hw_if: the handle of hw interface + * [out] rd_data: buffer for storing data retrieved + * [ in] rd_len: length of reading data in bytes + * + * @return + * 0 or positive value on success; otherwise, on error. + */ + int (*ops_read_data)(struct syna_hw_interface *hw_if, + unsigned char *rd_data, unsigned int rd_len); + + /* Operation to write the bare data to bus + * + * This is an essential operation; otherwise, the communication + * will not be created. + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] wr_data: written data + * [ in] wr_len: length of written data in bytes + * + * @return + * 0 or positive value on success; otherwise, on error. + */ + int (*ops_write_data)(struct syna_hw_interface *hw_if, + unsigned char *wr_data, unsigned int wr_len); + + /* Operation to enable/disable the irq, if supported + * + * This is an optional operation. Providing this operation could + * minimize the frequency of ISR being called. + * + * Once disabled, ISR will not be invoked even ATTN is asserted. + * + * Assign the pointer NULL if irq is not controllable. + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] en: '1' for enabling, and '0' for disabling + * + * @return + * 0 on success; otherwise, on error. + */ + int (*ops_enable_irq)(struct syna_hw_interface *hw_if, + bool en); + + /* Operation to wait for the signal of interrupt, if supported + * + * This is an optional operation to help for creating the + * custom interrupt handler. Besides, the recommendation is to + * implement in one-shot approach if possible. + * + * Implementation should return control if ATTN is asserted or + * specified timeout expire. If timeout is 0, should check the + * state of the ATTN signal and return control immediately. + * + * Assign the pointer NULL if customized ISR is not required. + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] timeout_ms: time frame in milliseconds + * + * @return + * 0 on success; otherwise, on error. + */ + int (*ops_wait_irq)(struct syna_hw_interface *hw_if, + unsigned int timeout_ms); + +}; +/* end of structure syna_hw_interface */ + + +/** + * syna_hw_interface_init() + * + * Initialize the lower-level hardware interface module. + * After returning, the handle of hw interface should be ready. + * + * @param + * void + * + * @return + * on success, 0; otherwise, negative value on error. + */ +int syna_hw_interface_init(void); + +/** + * syna_hw_interface_exit() + * + * Delete the lower-level hardware interface module. + * + * @param + * void + * + * @return + * none. + */ +void syna_hw_interface_exit(void); + + +#endif /* end of _SYNAPTICS_TCM2_PLATFORM_H_ */ diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_platform_i2c.c b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_platform_i2c.c new file mode 100644 index 000000000000..5eb4998c6064 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_platform_i2c.c @@ -0,0 +1,1095 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file syna_tcm2_platform_i2c.c + * + * This file is the reference code of I2C module used for communicating with + * Synaptics TouchCom device using I2C + */ + +#include + +#include "syna_tcm2.h" +#include "syna_tcm2_platform.h" + +#define I2C_MODULE_NAME "synaptics_tcm_i2c" + +#define XFER_ATTEMPTS 5 + +static struct platform_device *syna_i2c_device; + + +/** + * syna_request_managed_device() + * + * Request and return the device pointer for managed + * + * @param + * none. + * + * @return + * a device pointer allocated previously + */ +#if defined(DEV_MANAGED_API) || defined(USE_DRM_PANEL_NOTIFIER) +struct device *syna_request_managed_device(void) +{ + if (!syna_i2c_device) + return NULL; + + return syna_i2c_device->dev.parent; +} +#endif + +/** + * syna_i2c_hw_reset() + * + * Toggle the hardware gpio pin to perform the chip reset + * + * @param + * [ in] hw_if: the handle of hw interface + * + * @return + * none. + */ +static void syna_i2c_hw_reset(struct syna_hw_interface *hw_if) +{ + struct syna_hw_rst_data *rst = &hw_if->bdata_rst; + + if (rst->reset_gpio >= 0) { + gpio_set_value(rst->reset_gpio, rst->reset_on_state); + syna_pal_sleep_ms(rst->reset_active_ms); + gpio_set_value(rst->reset_gpio, !rst->reset_on_state); + syna_pal_sleep_ms(rst->reset_delay_ms); + } +} + +/** + * syna_i2c_request_gpio() + * + * Setup the given gpio + * + * @param + * [ in] gpio: the target gpio + * [ in] config: '1' for setting up, and '0' to release the gpio + * [ in] dir: default direction of gpio + * [ in] state: default state of gpio + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_i2c_request_gpio(int gpio, bool config, int dir, + int state, char *label) +{ + int retval; +#ifdef DEV_MANAGED_API + struct device *dev = syna_request_managed_device(); + + if (!dev) { + LOGE("Invalid managed device\n"); + return -ENODEV; + } +#endif + + if (gpio < 0) { + LOGE("Invalid gpio pin\n"); + return -EINVAL; + } + + if (config) { + retval = snprintf(label, 16, "tcm_gpio_%d\n", gpio); + if (retval < 0) { + LOGE("Fail to set GPIO label\n"); + return retval; + } +#ifdef DEV_MANAGED_API + retval = devm_gpio_request(dev, gpio, label); +#else /* Legacy API */ + retval = gpio_request(gpio, label); +#endif + if (retval < 0) { + LOGE("Fail to request GPIO %d\n", gpio); + return retval; + } + + if (dir == 0) + retval = gpio_direction_input(gpio); + else + retval = gpio_direction_output(gpio, state); + + if (retval < 0) { + LOGE("Fail to set GPIO %d direction\n", gpio); + return retval; + } + } else { +#ifdef DEV_MANAGED_API + devm_gpio_free(dev, gpio); +#else /* Legacy API */ + gpio_free(gpio); +#endif + } + + return 0; +} + +/** + * syna_i2c_release_gpio() + * + * Release the GPIOs requested previously + * + * @param + * [ in] hw_if: the handle of hw interface + * + * @return + * none + */ +static void syna_i2c_release_gpio(struct syna_hw_interface *hw_if) +{ + struct syna_hw_attn_data *attn = &hw_if->bdata_attn; + struct syna_hw_rst_data *rst = &hw_if->bdata_rst; + struct syna_hw_bus_data *bus = &hw_if->bdata_io; + + /* release gpios */ + if (rst->reset_gpio >= 0) + syna_i2c_request_gpio(rst->reset_gpio, false, 0, 0, NULL); + if (attn->irq_gpio >= 0) + syna_i2c_request_gpio(attn->irq_gpio, false, 0, 0, NULL); + if (bus->switch_gpio >= 0) + syna_i2c_request_gpio(bus->switch_gpio, false, 0, 0, NULL); +} + +/** + * syna_i2c_config_gpio() + * + * Initialize the GPIOs defined in device tree + * + * @param + * [ in] hw_if: the handle of hw interface + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_i2c_config_gpio(struct syna_hw_interface *hw_if) +{ + int retval; + static char str_irq_gpio[32] = {0}; + static char str_rst_gpio[32] = {0}; + static char str_io_switch_gpio[32] = {0}; + struct syna_hw_attn_data *attn = &hw_if->bdata_attn; + struct syna_hw_rst_data *rst = &hw_if->bdata_rst; + struct syna_hw_bus_data *bus = &hw_if->bdata_io; + + if (attn->irq_gpio >= 0) { + retval = syna_i2c_request_gpio(attn->irq_gpio, + true, 0, 0, str_irq_gpio); + if (retval < 0) { + LOGE("Fail to configure interrupt GPIO %d\n", + attn->irq_gpio); + goto err_set_gpio_irq; + } + } + + if (rst->reset_gpio >= 0) { + retval = syna_i2c_request_gpio(rst->reset_gpio, + true, 1, !rst->reset_on_state, + str_rst_gpio); + if (retval < 0) { + LOGE("Fail to configure reset GPIO %d\n", + rst->reset_gpio); + goto err_set_gpio_reset; + } + } + + if (bus->switch_gpio >= 0) { + retval = syna_i2c_request_gpio(bus->switch_gpio, + true, 1, bus->switch_state, + str_io_switch_gpio); + if (retval < 0) { + LOGE("Fail to configure switch GPIO %d\n", + bus->switch_gpio); + goto err_set_gpio_switch; + } + } + + return 0; + +err_set_gpio_switch: + if (rst->reset_gpio >= 0) + syna_i2c_request_gpio(rst->reset_gpio, false, 0, 0, NULL); +err_set_gpio_reset: + if (attn->irq_gpio >= 0) + syna_i2c_request_gpio(attn->irq_gpio, false, 0, 0, NULL); +err_set_gpio_irq: + return retval; +} +/** + * syna_i2c_enable_pwr_gpio() + * + * Helper to enable power supply through GPIO + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] en: '1' for enabling, and '0' for disabling + * + * @return + * none + */ +static int syna_i2c_enable_pwr_gpio(struct syna_hw_interface *hw_if, + bool en) +{ + struct syna_hw_pwr_data *pwr = &hw_if->bdata_pwr; + int state = (en) ? pwr->power_on_state : !pwr->power_on_state; + + if (pwr->avdd_gpio >= 0) + gpio_set_value(pwr->avdd_gpio, state); + + if (pwr->vdd_gpio >= 0) + gpio_set_value(pwr->vdd_gpio, state); + + return 0; +} + +/** + * syna_i2c_enable_regulator() + * + * Enable or disable the regulator + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] en: '1' for enabling, and '0' for disabling + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_i2c_enable_regulator(struct syna_hw_interface *hw_if, + bool en) +{ + int retval; + struct syna_hw_pwr_data *pwr = &hw_if->bdata_pwr; + struct regulator *vdd_reg = pwr->vdd_reg_dev; + struct regulator *avdd_reg = pwr->avdd_reg_dev; + + if (!en) { + retval = 0; + goto disable_pwr_reg; + } + + if (vdd_reg) { + retval = regulator_enable(vdd_reg); + if (retval < 0) { + LOGE("Fail to enable vdd regulator\n"); + goto exit; + } + } + + if (avdd_reg) { + retval = regulator_enable(avdd_reg); + if (retval < 0) { + LOGE("Fail to enable avdd regulator\n"); + goto disable_avdd_reg; + } + syna_pal_sleep_ms(pwr->power_on_delay_ms); + } + + return 0; + +disable_pwr_reg: + if (vdd_reg) + regulator_disable(vdd_reg); + +disable_avdd_reg: + if (avdd_reg) + regulator_disable(avdd_reg); + +exit: + return retval; +} + +/** + * syna_i2c_power_on() + * + * Power on touch controller through regulators or gpios for PWM + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] en: '1' for powering on, and '0' for powering off + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_i2c_power_on(struct syna_hw_interface *hw_if, + bool en) +{ + int retval; + struct syna_hw_pwr_data *pwr = &hw_if->bdata_pwr; + + LOGI("Prepare to power %s device through %s\n", + (en) ? "on" : "off", + (pwr->psu == PSU_GPIO) ? "gpio" : "regulator"); + + if (pwr->psu == PSU_GPIO) + retval = syna_i2c_enable_pwr_gpio(hw_if, en); + else + retval = syna_i2c_enable_regulator(hw_if, en); + + if (retval < 0) { + LOGE("Fail to power %s device\n", (en) ? "on" : "off"); + return retval; + } + + LOGI("Device power %s\n", (en) ? "on" : "off"); + + return 0; +} + +/** + * syna_i2c_get_regulator() + * + * Acquire or release the regulator + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] get: '1' for getting the regulator, and '0' for removing + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_i2c_get_regulator(struct syna_hw_interface *hw_if, + bool get) +{ + int retval; + struct device *dev = syna_i2c_device->dev.parent; + struct syna_hw_pwr_data *pwr = &hw_if->bdata_pwr; + + if (!get) { + retval = 0; + goto regulator_put; + } + + if (pwr->vdd_reg_name != NULL && *pwr->vdd_reg_name != 0) { +#ifdef DEV_MANAGED_API + pwr->vdd_reg_dev = devm_regulator_get(dev, pwr->vdd_reg_name); +#else /* Legacy API */ + pwr->vdd_reg_dev = regulator_get(dev, pwr->vdd_reg_name); +#endif + if (IS_ERR((struct regulator *)pwr->vdd_reg_dev)) { + LOGW("Vdd regulator is not ready\n"); + retval = PTR_ERR((struct regulator *)pwr->vdd_reg_dev); + goto exit; + } + } + + if (pwr->avdd_reg_name != NULL && *pwr->avdd_reg_name != 0) { +#ifdef DEV_MANAGED_API + pwr->avdd_reg_dev = devm_regulator_get(dev, pwr->avdd_reg_name); +#else /* Legacy API */ + pwr->avdd_reg_dev = regulator_get(dev, pwr->avdd_reg_name); +#endif + if (IS_ERR((struct regulator *)pwr->avdd_reg_dev)) { + LOGW("AVdd regulator is not ready\n"); + retval = PTR_ERR((struct regulator *)pwr->avdd_reg_dev); + goto regulator_vdd_put; + } + } + + return 0; + +regulator_put: + if (pwr->vdd_reg_dev) { +#ifdef DEV_MANAGED_API + devm_regulator_put(pwr->vdd_reg_dev); +#else /* Legacy API */ + regulator_put(pwr->vdd_reg_dev); +#endif + pwr->vdd_reg_dev = NULL; + } +regulator_vdd_put: + if (pwr->avdd_reg_dev) { +#ifdef DEV_MANAGED_API + devm_regulator_put(pwr->avdd_reg_dev); +#else /* Legacy API */ + regulator_put(pwr->avdd_reg_dev); +#endif + pwr->avdd_reg_dev = NULL; + } +exit: + return retval; +} + +/** + * syna_i2c_config_psu() + * + * Initialize the power supply unit + * + * @param + * [ in] hw_if: the handle of hw interface + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_i2c_config_psu(struct syna_hw_interface *hw_if) +{ + int retval; + static char str_vdd_gpio[32] = {0}; + static char str_avdd_gpio[32] = {0}; + struct syna_hw_pwr_data *pwr = &hw_if->bdata_pwr; + + if (pwr->psu == PSU_GPIO) { + /* set up power gpio */ + if (pwr->vdd_gpio >= 0) { + retval = syna_i2c_request_gpio(pwr->vdd_gpio, + true, 1, !pwr->power_on_state, + str_vdd_gpio); + if (retval < 0) { + LOGE("Fail to configure vdd GPIO %d\n", + pwr->vdd_gpio); + return retval; + } + } + + if (pwr->avdd_gpio >= 0) { + retval = syna_i2c_request_gpio(pwr->avdd_gpio, + true, 1, !pwr->power_on_state, + str_avdd_gpio); + if (retval < 0) { + LOGE("Fail to configure avdd GPIO %d\n", + pwr->avdd_gpio); + + syna_i2c_request_gpio(pwr->vdd_gpio, + false, 0, 0, NULL); + return retval; + } + } + } else { + /* set up regulator */ + retval = syna_i2c_get_regulator(hw_if, true); + if (retval < 0) { + LOGE("Fail to configure regulators\n"); + return retval; + } + } + + return 0; +} + +/** + * syna_i2c_release_psu() + * + * Release the power supply unit + * + * @param + * [ in] hw_if: the handle of hw interface + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_i2c_release_psu(struct syna_hw_interface *hw_if) +{ + struct syna_hw_pwr_data *pwr = &hw_if->bdata_pwr; + + if (pwr->psu == PSU_GPIO) { + syna_i2c_request_gpio(pwr->avdd_gpio, false, 0, 0, NULL); + syna_i2c_request_gpio(pwr->vdd_gpio, false, 0, 0, NULL); + } else { + syna_i2c_get_regulator(hw_if, false); + } + + return 0; +} + +/** + * syna_i2c_enable_irq() + * + * Enable or disable the handling of interrupt + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] en: '1' for enabling, and '0' for disabling + * + * @return + * 0 on success; otherwise, on error. + */ +static int syna_i2c_enable_irq(struct syna_hw_interface *hw_if, + bool en) +{ + int retval = 0; + struct syna_hw_attn_data *attn = &hw_if->bdata_attn; + + if (attn->irq_id == 0) + return 0; + + syna_pal_mutex_lock(&attn->irq_en_mutex); + + /* enable the handling of interrupt */ + if (en) { + if (attn->irq_enabled) { + LOGI("Interrupt already enabled\n"); + retval = 0; + goto exit; + } + + enable_irq(attn->irq_id); + attn->irq_enabled = true; + + LOGD("irq enabled\n"); + } + /* disable the handling of interrupt */ + else { + if (!attn->irq_enabled) { + LOGI("Interrupt already disabled\n"); + retval = 0; + goto exit; + } + + disable_irq_nosync(attn->irq_id); + attn->irq_enabled = false; + + LOGD("irq disabled\n"); + } + +exit: + syna_pal_mutex_unlock(&attn->irq_en_mutex); + + return retval; +} + + +/** + * syna_i2c_parse_dt() + * + * Parse and obtain board specific data from the device tree source file. + * Keep the data in structure syna_tcm_hw_data for later using. + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] dev: device model + * + * @return + * on success, 0; otherwise, negative value on error. + */ +#ifdef CONFIG_OF +static int syna_i2c_parse_dt(struct syna_hw_interface *hw_if, + struct device *dev) +{ + int retval; + u32 value; + struct property *prop; + struct device_node *np = dev->of_node; + const char *name; + struct syna_hw_attn_data *attn = &hw_if->bdata_attn; + struct syna_hw_pwr_data *pwr = &hw_if->bdata_pwr; + struct syna_hw_rst_data *rst = &hw_if->bdata_rst; + struct syna_hw_bus_data *bus = &hw_if->bdata_io; + + prop = of_find_property(np, "synaptics,irq-gpio", NULL); + if (prop && prop->length) { + attn->irq_gpio = of_get_named_gpio_flags(np, + "synaptics,irq-gpio", 0, + (enum of_gpio_flags *)&attn->irq_flags); + } else { + attn->irq_gpio = -1; + } + + retval = of_property_read_u32(np, "synaptics,irq-on-state", &value); + if (retval < 0) + attn->irq_on_state = 0; + else + attn->irq_on_state = value; + + prop = of_find_property(np, "synaptics,power-supply", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-supply", + &value); + if (retval < 0) { + LOGE("Fail to read power-supply property\n"); + return retval; + } + + pwr->psu = value; + + } else { + pwr->psu = (int)PSU_REGULATOR; + } + + retval = of_property_read_string(np, "synaptics,avdd-name", &name); + if (retval < 0) + pwr->avdd_reg_name = NULL; + else + pwr->avdd_reg_name = name; + + retval = of_property_read_string(np, "synaptics,vdd-name", &name); + if (retval < 0) + pwr->vdd_reg_name = NULL; + else + pwr->vdd_reg_name = name; + + prop = of_find_property(np, "synaptics,vdd-gpio", NULL); + if (prop && prop->length) { + pwr->vdd_gpio = of_get_named_gpio_flags(np, + "synaptics,vdd-gpio", 0, NULL); + } else { + pwr->vdd_gpio = -1; + } + + prop = of_find_property(np, "synaptics,avdd-gpio", NULL); + if (prop && prop->length) { + pwr->avdd_gpio = of_get_named_gpio_flags(np, + "synaptics,avdd-gpio", 0, NULL); + } else { + pwr->avdd_gpio = -1; + } + + prop = of_find_property(np, "synaptics,power-on-state", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-on-state", + &value); + if (retval < 0) { + LOGE("Fail to read power-on-state property\n"); + return retval; + } + + pwr->power_on_state = value; + + } else { + pwr->power_on_state = 0; + } + + prop = of_find_property(np, "synaptics,power-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-delay-ms", + &value); + if (retval < 0) { + LOGE("Fail to read power-delay-ms property\n"); + return retval; + } + + pwr->power_on_delay_ms = value; + + } else { + pwr->power_on_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-gpio", NULL); + if (prop && prop->length) { + rst->reset_gpio = of_get_named_gpio_flags(np, + "synaptics,reset-gpio", 0, NULL); + } else { + rst->reset_gpio = -1; + } + + prop = of_find_property(np, "synaptics,reset-on-state", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-on-state", + &value); + if (retval < 0) { + LOGE("Fail to read reset-on-state property\n"); + return retval; + } + + rst->reset_on_state = value; + + } else { + rst->reset_on_state = 0; + } + + prop = of_find_property(np, "synaptics,reset-active-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-active-ms", + &value); + if (retval < 0) { + LOGE("Fail to read reset-active-ms property\n"); + return retval; + } + + rst->reset_active_ms = value; + + } else { + rst->reset_active_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-delay-ms", + &value); + if (retval < 0) { + LOGE("Fail to read reset-delay-ms property\n"); + return retval; + } + + rst->reset_delay_ms = value; + + } else { + rst->reset_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,io-switch-gpio", NULL); + if (prop && prop->length) { + bus->switch_gpio = of_get_named_gpio_flags(np, + "synaptics,io-switch-gpio", 0, NULL); + } else { + bus->switch_gpio = -1; + } + + prop = of_find_property(np, "synaptics,io-switch", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,io-switch", + &value); + if (retval < 0) { + LOGE("Fail to read io-switch property\n"); + return retval; + } + + bus->switch_state = value; + + } else { + bus->switch_state = 1; + } + + return 0; +} +#endif + + +/** + * syna_i2c_read() + * + * TouchCom over I2C uses the normal I2C addressing and transaction direction + * mechanisms to select the device and retrieve the data. + * + * @param + * [ in] hw_if: the handle of hw interface + * [out] rd_data: buffer for storing data retrieved from device + * [ in] rd_len: number of bytes retrieved from device + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_i2c_read(struct syna_hw_interface *hw_if, + unsigned char *rd_data, unsigned int rd_len) +{ + int retval; + unsigned int attempt; + struct i2c_msg msg; + struct i2c_client *i2c = hw_if->pdev; + struct syna_hw_bus_data *bus = &hw_if->bdata_io; + + if (!i2c) { + LOGE("Invalid bus io device\n"); + return -EINVAL; + } + + syna_pal_mutex_lock(&bus->io_mutex); + + msg.addr = i2c->addr; + msg.flags = I2C_M_RD; + msg.len = rd_len; + msg.buf = rd_data; + + for (attempt = 0; attempt < XFER_ATTEMPTS; attempt++) { + if (i2c_transfer(i2c->adapter, &msg, 1) == 1) { + retval = rd_len; + goto exit; + } + LOGE("Transfer attempt %d failed\n", attempt + 1); + + if (attempt + 1 == XFER_ATTEMPTS) { + retval = -EIO; + goto exit; + } + + syna_pal_sleep_ms(20); + } + +exit: + syna_pal_mutex_unlock(&bus->io_mutex); + + return retval; +} + +/** + * syna_i2c_write() + * + * TouchCom over I2C uses the normal I2C addressing and transaction direction + * mechanisms to select the device and send the data to the device. + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] wr_data: written data + * [ in] wr_len: length of written data in bytes + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_i2c_write(struct syna_hw_interface *hw_if, + unsigned char *wr_data, unsigned int wr_len) +{ + int retval; + unsigned int attempt; + struct i2c_msg msg; + struct i2c_client *i2c = hw_if->pdev; + struct syna_hw_bus_data *bus = &hw_if->bdata_io; + + if (!i2c) { + LOGE("Invalid bus io device\n"); + return -EINVAL; + } + + syna_pal_mutex_lock(&bus->io_mutex); + + msg.addr = i2c->addr; + msg.flags = 0; + msg.len = wr_len; + msg.buf = wr_data; + + for (attempt = 0; attempt < XFER_ATTEMPTS; attempt++) { + if (i2c_transfer(i2c->adapter, &msg, 1) == 1) { + retval = wr_len; + goto exit; + } + LOGE("Transfer attempt %d failed\n", attempt + 1); + + if (attempt + 1 == XFER_ATTEMPTS) { + retval = -EIO; + goto exit; + } + + syna_pal_sleep_ms(20); + } + +exit: + syna_pal_mutex_unlock(&bus->io_mutex); + + return retval; +} + + +/** + * syna_hw_interface + * + * Provide the hardware specific settings in defaults. + * Be noted the followings could be changed after .dtsi is parsed + */ +static struct syna_hw_interface syna_i2c_hw_if = { + .bdata_io = { + .type = BUS_TYPE_I2C, + .rd_chunk_size = RD_CHUNK_SIZE, + .wr_chunk_size = WR_CHUNK_SIZE, + }, + .bdata_attn = { + .irq_enabled = false, + .irq_on_state = 0, + }, + .bdata_rst = { + .reset_on_state = 0, + .reset_delay_ms = 200, + .reset_active_ms = 20, + }, + .bdata_pwr = { + .power_on_state = 1, + .power_on_delay_ms = 200, + }, + .ops_power_on = syna_i2c_power_on, + .ops_hw_reset = syna_i2c_hw_reset, + .ops_read_data = syna_i2c_read, + .ops_write_data = syna_i2c_write, + .ops_enable_irq = syna_i2c_enable_irq, +}; + +/** + * syna_i2c_probe() + * + * Prepare the specific hardware interface and register the platform i2c device + * + * @param + * [ in] i2c: i2c client device + * [ in] dev_id: i2c device id + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *dev_id) +{ + int retval; + struct syna_hw_attn_data *attn = &syna_i2c_hw_if.bdata_attn; + struct syna_hw_bus_data *bus = &syna_i2c_hw_if.bdata_io; + + /* allocate an i2c platform device */ + syna_i2c_device = platform_device_alloc(PLATFORM_DRIVER_NAME, 0); + if (!syna_i2c_device) { + LOGE("Fail to allocate platform device\n"); + return _ENODEV; + } + +#ifdef CONFIG_OF + syna_i2c_parse_dt(&syna_i2c_hw_if, &i2c->dev); +#endif + + syna_pal_mutex_alloc(&attn->irq_en_mutex); + syna_pal_mutex_alloc(&bus->io_mutex); + + /* keep the i/o device */ + syna_i2c_hw_if.pdev = i2c; + + syna_i2c_device->dev.parent = &i2c->dev; + syna_i2c_device->dev.platform_data = &syna_i2c_hw_if; + + /* initialize power unit */ + retval = syna_i2c_config_psu(&syna_i2c_hw_if); + if (retval < 0) { + LOGE("Fail to config power unit\n"); + return retval; + } + + /* initialize the gpio pins */ + retval = syna_i2c_config_gpio(&syna_i2c_hw_if); + if (retval < 0) { + LOGE("Fail to config gpio\n"); + return retval; + } + + /* do i/o switch if defined */ + if (bus->switch_gpio >= 0) + gpio_set_value(bus->switch_gpio, bus->switch_state); + + /* register the i2c platform device */ + retval = platform_device_add(syna_i2c_device); + if (retval < 0) { + LOGE("Fail to add platform device\n"); + return retval; + } + + return 0; +} + +/** + * syna_i2c_remove() + * + * Unregister the platform i2c device + * + * @param + * [ in] i2c: i2c client device + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_i2c_remove(struct i2c_client *i2c) +{ + struct syna_hw_attn_data *attn = &syna_i2c_hw_if.bdata_attn; + struct syna_hw_bus_data *bus = &syna_i2c_hw_if.bdata_io; + + /* release gpios */ + syna_i2c_release_gpio(&syna_i2c_hw_if); + + /* release power unit */ + syna_i2c_release_psu(&syna_i2c_hw_if); + + /* release mutex */ + syna_pal_mutex_free(&attn->irq_en_mutex); + syna_pal_mutex_free(&bus->io_mutex); + + /* remove the platform device */ + syna_i2c_device->dev.platform_data = NULL; + platform_device_unregister(syna_i2c_device); + + return 0; +} + +/** + * Describe an i2c device driver and its related declarations + */ +static const struct i2c_device_id syna_i2c_id_table[] = { + {I2C_MODULE_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, syna_i2c_id_table); + +#ifdef CONFIG_OF +static const struct of_device_id syna_i2c_of_match_table[] = { + { + .compatible = "synaptics,tcm-i2c", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, syna_i2c_of_match_table); +#else +#define syna_i2c_of_match_table NULL +#endif + +static struct i2c_driver syna_i2c_driver = { + .driver = { + .name = I2C_MODULE_NAME, + .owner = THIS_MODULE, + .of_match_table = syna_i2c_of_match_table, + }, + .probe = syna_i2c_probe, + .remove = syna_i2c_remove, + .id_table = syna_i2c_id_table, +}; + + +/** + * syna_hw_interface_init() + * + * Initialize the lower-level hardware interface module. + * After returning, the handle of hw interface should be ready. + * + * @param + * void + * + * @return + * on success, 0; otherwise, negative value on error. + */ +int syna_hw_interface_init(void) +{ + return i2c_add_driver(&syna_i2c_driver); +} + +/** + * syna_hw_interface_exit() + * + * Delete the lower-level hardware interface module + * + * @param + * void + * + * @return + * none. + */ +void syna_hw_interface_exit(void) +{ + i2c_del_driver(&syna_i2c_driver); +} + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TouchCom I2C Bus Module"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_platform_spi.c b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_platform_spi.c new file mode 100644 index 000000000000..94e2972323b4 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_platform_spi.c @@ -0,0 +1,1346 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file syna_tcm2_platform_spi.c + * + * This file is the reference code of I2C module used for communicating with + * Synaptics TouchCom device using I2C + */ + +#include +#include + +#include "syna_tcm2.h" +#include "syna_tcm2_platform.h" + +#define SPI_MODULE_NAME "synaptics_tcm_spi" + +static unsigned char *rx_buf; +static unsigned char *tx_buf; + +static unsigned int buf_size; + +static struct spi_transfer *xfer; + +static struct platform_device *syna_spi_device; +bool limit_panel; + +/** + * syna_request_managed_device() + * + * Request and return the device pointer for managed + * + * @param + * none. + * + * @return + * a device pointer allocated previously + */ +#if defined(DEV_MANAGED_API) || defined(USE_DRM_PANEL_NOTIFIER) +struct device *syna_request_managed_device(void) +{ + if (!syna_spi_device) + return NULL; + + return syna_spi_device->dev.parent; +} +#endif + +/** + * syna_spi_hw_reset() + * + * Toggle the hardware gpio pin to perform the chip reset + * + * @param + * [ in] hw_if: the handle of hw interface + * + * @return + * none. + */ +static void syna_spi_hw_reset(struct syna_hw_interface *hw_if) +{ + struct syna_hw_rst_data *rst = &hw_if->bdata_rst; + + if (rst->reset_gpio >= 0) { + LOGI("hw reset start ...\n"); + gpio_set_value(rst->reset_gpio, rst->reset_on_state); + syna_pal_sleep_ms(rst->reset_active_ms); + gpio_set_value(rst->reset_gpio, !rst->reset_on_state); + syna_pal_sleep_ms(rst->reset_delay_ms); + LOGI("hw reset done\n"); + } +} + +/** + * syna_spi_request_gpio() + * + * Setup the given gpio + * + * @param + * [ in] gpio: the target gpio + * [ in] config: '1' for setting up, and '0' to release the gpio + * [ in] dir: default direction of gpio + * [ in] state: default state of gpio + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_spi_request_gpio(int gpio, bool config, int dir, + int state, char *label) +{ + int retval; +#ifdef DEV_MANAGED_API + struct device *dev = syna_request_managed_device(); + + if (!dev) { + LOGE("Invalid managed device\n"); + return -ENODEV; + } +#endif + + if (gpio < 0) { + LOGE("Invalid gpio pin\n"); + return -EINVAL; + } + + if (config) { + retval = snprintf(label, 16, "tcm_gpio_%d\n", gpio); + if (retval < 0) { + LOGE("Fail to set GPIO label\n"); + return retval; + } +#ifdef DEV_MANAGED_API + retval = devm_gpio_request(dev, gpio, label); +#else /* Legacy API */ + retval = gpio_request(gpio, label); +#endif + if (retval < 0) { + LOGE("Fail to request GPIO %d\n", gpio); + return retval; + } + + if (dir == 0) + retval = gpio_direction_input(gpio); + else + retval = gpio_direction_output(gpio, state); + + if (retval < 0) { + LOGE("Fail to set GPIO %d direction\n", gpio); + return retval; + } + } else { +#ifdef DEV_MANAGED_API + devm_gpio_free(dev, gpio); +#else /* Legacy API */ + gpio_free(gpio); +#endif + } + + return 0; +} + +/** + * syna_spi_release_gpio() + * + * Release the GPIOs requested previously + * + * @param + * [ in] hw_if: the handle of hw interface + * + * @return + * none + */ +static void syna_spi_release_gpio(struct syna_hw_interface *hw_if) +{ + struct syna_hw_attn_data *attn = &hw_if->bdata_attn; + struct syna_hw_rst_data *rst = &hw_if->bdata_rst; + struct syna_hw_bus_data *bus = &hw_if->bdata_io; + + /* release gpios */ + if (rst->reset_gpio >= 0) + syna_spi_request_gpio(rst->reset_gpio, false, 0, 0, NULL); + if (attn->irq_gpio >= 0) + syna_spi_request_gpio(attn->irq_gpio, false, 0, 0, NULL); + if (bus->switch_gpio >= 0) + syna_spi_request_gpio(bus->switch_gpio, false, 0, 0, NULL); +} + +/** + * syna_spi_config_gpio() + * + * Initialize the GPIOs defined in device tree + * + * @param + * [ in] hw_if: the handle of hw interface + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_spi_config_gpio(struct syna_hw_interface *hw_if) +{ + int retval; + static char str_irq_gpio[32] = {0}; + static char str_rst_gpio[32] = {0}; + static char str_io_switch_gpio[32] = {0}; + struct syna_hw_attn_data *attn = &hw_if->bdata_attn; + struct syna_hw_rst_data *rst = &hw_if->bdata_rst; + struct syna_hw_bus_data *bus = &hw_if->bdata_io; + + if (attn->irq_gpio >= 0) { + retval = syna_spi_request_gpio(attn->irq_gpio, + true, 0, 0, str_irq_gpio); + if (retval < 0) { + LOGE("Fail to configure interrupt GPIO %d\n", + attn->irq_gpio); + goto err_set_gpio_irq; + } + } + + if (rst->reset_gpio >= 0) { + retval = syna_spi_request_gpio(rst->reset_gpio, + true, 1, !rst->reset_on_state, + str_rst_gpio); + if (retval < 0) { + LOGE("Fail to configure reset GPIO %d\n", + rst->reset_gpio); + goto err_set_gpio_reset; + } + } + + if (bus->switch_gpio >= 0) { + retval = syna_spi_request_gpio(bus->switch_gpio, + true, 1, bus->switch_state, + str_io_switch_gpio); + if (retval < 0) { + LOGE("Fail to configure switch GPIO %d\n", + bus->switch_gpio); + goto err_set_gpio_switch; + } + } + + return 0; + +err_set_gpio_switch: + if (rst->reset_gpio >= 0) + syna_spi_request_gpio(rst->reset_gpio, false, 0, 0, NULL); +err_set_gpio_reset: + if (attn->irq_gpio >= 0) + syna_spi_request_gpio(attn->irq_gpio, false, 0, 0, NULL); +err_set_gpio_irq: + return retval; +} +/** + * syna_spi_enable_pwr_gpio() + * + * Helper to enable power supply through GPIO + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] en: '1' for enabling, and '0' for disabling + * + * @return + * none + */ +static int syna_spi_enable_pwr_gpio(struct syna_hw_interface *hw_if, + bool en) +{ + struct syna_hw_pwr_data *pwr = &hw_if->bdata_pwr; + int state = (en) ? pwr->power_on_state : !pwr->power_on_state; + + if (pwr->avdd_gpio >= 0) + gpio_set_value(pwr->avdd_gpio, state); + + if (pwr->vdd_gpio >= 0) + gpio_set_value(pwr->vdd_gpio, state); + + return 0; +} + +/** + * syna_spi_enable_regulator() + * + * Enable or disable the regulator + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] en: '1' for enabling, and '0' for disabling + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_spi_enable_regulator(struct syna_hw_interface *hw_if, + bool en) +{ + int retval; + struct syna_hw_pwr_data *pwr = &hw_if->bdata_pwr; + struct regulator *vdd_reg = pwr->vdd_reg_dev; + struct regulator *avdd_reg = pwr->avdd_reg_dev; + + if (!en) { + retval = 0; + goto disable_pwr_reg; + } + + if (vdd_reg) { + retval = regulator_enable(vdd_reg); + if (retval < 0) { + LOGE("Fail to enable vdd regulator\n"); + goto exit; + } + } + + if (avdd_reg) { + retval = regulator_enable(avdd_reg); + if (retval < 0) { + LOGE("Fail to enable avdd regulator\n"); + goto disable_avdd_reg; + } + syna_pal_sleep_ms(pwr->power_on_delay_ms); + } + + return 0; + +disable_pwr_reg: + if (vdd_reg) + regulator_disable(vdd_reg); + +disable_avdd_reg: + if (avdd_reg) + regulator_disable(avdd_reg); + +exit: + return retval; +} + +/** + * syna_spi_power_on() + * + * Power on touch controller through regulators or gpios for PWM + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] en: '1' for powering on, and '0' for powering off + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_spi_power_on(struct syna_hw_interface *hw_if, + bool en) +{ + int retval; + struct syna_hw_pwr_data *pwr = &hw_if->bdata_pwr; + + LOGI("Prepare to power %s device through %s\n", + (en) ? "on" : "off", + (pwr->psu == PSU_GPIO) ? "gpio" : "regulator"); + + if (pwr->psu == PSU_GPIO) + retval = syna_spi_enable_pwr_gpio(hw_if, en); + else + retval = syna_spi_enable_regulator(hw_if, en); + + if (retval < 0) { + LOGE("Fail to power %s device\n", (en) ? "on" : "off"); + return retval; + } + + LOGI("Device power %s\n", (en) ? "on" : "off"); + + return 0; +} + +/** + * syna_spi_get_regulator() + * + * Acquire or release the regulator + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] get: '1' for getting the regulator, and '0' for removing + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_spi_get_regulator(struct syna_hw_interface *hw_if, + bool get) +{ + int retval; + struct device *dev = syna_spi_device->dev.parent; + struct syna_hw_pwr_data *pwr = &hw_if->bdata_pwr; + + if (!get) { + retval = 0; + goto regulator_put; + } + + if (pwr->vdd_reg_name != NULL && *pwr->vdd_reg_name != 0) { +#ifdef DEV_MANAGED_API + pwr->vdd_reg_dev = devm_regulator_get(dev, pwr->vdd_reg_name); +#else /* Legacy API */ + pwr->vdd_reg_dev = regulator_get(dev, pwr->vdd_reg_name); +#endif + if (IS_ERR((struct regulator *)pwr->vdd_reg_dev)) { + LOGW("Vdd regulator is not ready\n"); + retval = PTR_ERR((struct regulator *)pwr->vdd_reg_dev); + goto exit; + } + } + + if (pwr->avdd_reg_name != NULL && *pwr->avdd_reg_name != 0) { +#ifdef DEV_MANAGED_API + pwr->avdd_reg_dev = devm_regulator_get(dev, pwr->avdd_reg_name); +#else /* Legacy API */ + pwr->avdd_reg_dev = regulator_get(dev, pwr->avdd_reg_name); +#endif + if (IS_ERR((struct regulator *)pwr->avdd_reg_dev)) { + LOGW("AVdd regulator is not ready\n"); + retval = PTR_ERR((struct regulator *)pwr->avdd_reg_dev); + goto regulator_vdd_put; + } + } + + return 0; + +regulator_put: + if (pwr->vdd_reg_dev) { +#ifdef DEV_MANAGED_API + devm_regulator_put(pwr->vdd_reg_dev); +#else /* Legacy API */ + regulator_put(pwr->vdd_reg_dev); +#endif + pwr->vdd_reg_dev = NULL; + } +regulator_vdd_put: + if (pwr->avdd_reg_dev) { +#ifdef DEV_MANAGED_API + devm_regulator_put(pwr->avdd_reg_dev); +#else /* Legacy API */ + regulator_put(pwr->avdd_reg_dev); +#endif + pwr->avdd_reg_dev = NULL; + } +exit: + return retval; +} + +/** + * syna_spi_config_psu() + * + * Initialize the power supply unit + * + * @param + * [ in] hw_if: the handle of hw interface + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_spi_config_psu(struct syna_hw_interface *hw_if) +{ + int retval; + static char str_vdd_gpio[32] = {0}; + static char str_avdd_gpio[32] = {0}; + struct syna_hw_pwr_data *pwr = &hw_if->bdata_pwr; + + if (pwr->psu == PSU_GPIO) { + /* set up power gpio */ + if (pwr->vdd_gpio >= 0) { + retval = syna_spi_request_gpio(pwr->vdd_gpio, + true, 1, !pwr->power_on_state, + str_vdd_gpio); + if (retval < 0) { + LOGE("Fail to configure vdd GPIO %d\n", + pwr->vdd_gpio); + return retval; + } + } + + if (pwr->avdd_gpio >= 0) { + retval = syna_spi_request_gpio(pwr->avdd_gpio, + true, 1, !pwr->power_on_state, + str_avdd_gpio); + if (retval < 0) { + LOGE("Fail to configure avdd GPIO %d\n", + pwr->avdd_gpio); + + syna_spi_request_gpio(pwr->vdd_gpio, + false, 0, 0, NULL); + return retval; + } + } + } else { + /* set up regulator */ + retval = syna_spi_get_regulator(hw_if, true); + if (retval < 0) { + LOGE("Fail to configure regulators\n"); + return retval; + } + } + + return 0; +} + +/** + * syna_spi_release_psu() + * + * Release the power supply unit + * + * @param + * [ in] hw_if: the handle of hw interface + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_spi_release_psu(struct syna_hw_interface *hw_if) +{ + struct syna_hw_pwr_data *pwr = &hw_if->bdata_pwr; + + if (pwr->psu == PSU_GPIO) { + syna_spi_request_gpio(pwr->avdd_gpio, false, 0, 0, NULL); + syna_spi_request_gpio(pwr->vdd_gpio, false, 0, 0, NULL); + } else { + syna_spi_get_regulator(hw_if, false); + } + + return 0; +} + +/** + * syna_spi_enable_irq() + * + * Enable or disable the handling of interrupt + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] en: '1' for enabling, and '0' for disabling + * + * @return + * 0 on success; otherwise, on error. + */ +static int syna_spi_enable_irq(struct syna_hw_interface *hw_if, + bool en) +{ + int retval = 0; + struct syna_hw_attn_data *attn = &hw_if->bdata_attn; + + if (attn->irq_id == 0) + return 0; + + syna_pal_mutex_lock(&attn->irq_en_mutex); + + /* enable the handling of interrupt */ + if (en) { + if (attn->irq_enabled) { + LOGI("Interrupt already enabled\n"); + retval = 0; + goto exit; + } + + enable_irq(attn->irq_id); + attn->irq_enabled = true; + + LOGD("irq enabled\n"); + } + /* disable the handling of interrupt */ + else { + if (!attn->irq_enabled) { + LOGI("Interrupt already disabled\n"); + retval = 0; + goto exit; + } + + disable_irq_nosync(attn->irq_id); + attn->irq_enabled = false; + + LOGD("irq disabled\n"); + } + +exit: + syna_pal_mutex_unlock(&attn->irq_en_mutex); + + return retval; +} + + +/** + * syna_spi_parse_dt() + * + * Parse and obtain board specific data from the device tree source file. + * Keep the data in structure syna_tcm_hw_data for later using. + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] dev: device model + * + * @return + * on success, 0; otherwise, negative value on error. + */ +#ifdef CONFIG_OF +static int syna_spi_parse_dt(struct syna_hw_interface *hw_if, + struct device *dev) +{ + int retval; + u32 value; + struct property *prop; + struct device_node *np = dev->of_node; + const char *name; + struct syna_hw_attn_data *attn = &hw_if->bdata_attn; + struct syna_hw_pwr_data *pwr = &hw_if->bdata_pwr; + struct syna_hw_rst_data *rst = &hw_if->bdata_rst; + struct syna_hw_bus_data *bus = &hw_if->bdata_io; + + prop = of_find_property(np, "synaptics,irq-gpio", NULL); + if (prop && prop->length) { + attn->irq_gpio = of_get_named_gpio_flags(np, + "synaptics,irq-gpio", 0, + (enum of_gpio_flags *)&attn->irq_flags); + } else { + attn->irq_gpio = -1; + } + + retval = of_property_read_u32(np, "synaptics,irq-on-state", &value); + if (retval < 0) + attn->irq_on_state = 0; + else + attn->irq_on_state = value; + + prop = of_find_property(np, "synaptics,power-supply", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-supply", + &value); + if (retval < 0) { + LOGE("Fail to read power-supply property\n"); + return retval; + } + + pwr->psu = value; + + } else { + pwr->psu = (int)PSU_REGULATOR; + } + + retval = of_property_read_string(np, "synaptics,avdd-name", &name); + if (retval < 0) + pwr->avdd_reg_name = NULL; + else + pwr->avdd_reg_name = name; + + retval = of_property_read_string(np, "synaptics,vdd-name", &name); + if (retval < 0) + pwr->vdd_reg_name = NULL; + else + pwr->vdd_reg_name = name; + + prop = of_find_property(np, "synaptics,vdd-gpio", NULL); + if (prop && prop->length) { + pwr->vdd_gpio = of_get_named_gpio_flags(np, + "synaptics,vdd-gpio", 0, NULL); + } else { + pwr->vdd_gpio = -1; + } + + prop = of_find_property(np, "synaptics,avdd-gpio", NULL); + if (prop && prop->length) { + pwr->avdd_gpio = of_get_named_gpio_flags(np, + "synaptics,avdd-gpio", 0, NULL); + } else { + pwr->avdd_gpio = -1; + } + + prop = of_find_property(np, "synaptics,power-on-state", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-on-state", + &value); + if (retval < 0) { + LOGE("Fail to read power-on-state property\n"); + return retval; + } + + pwr->power_on_state = value; + + } else { + pwr->power_on_state = 0; + } + + prop = of_find_property(np, "synaptics,power-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-delay-ms", + &value); + if (retval < 0) { + LOGE("Fail to read power-delay-ms property\n"); + return retval; + } + + pwr->power_on_delay_ms = value; + + } else { + pwr->power_on_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-gpio", NULL); + if (prop && prop->length) { + rst->reset_gpio = of_get_named_gpio_flags(np, + "synaptics,reset-gpio", 0, NULL); + } else { + rst->reset_gpio = -1; + } + + prop = of_find_property(np, "synaptics,reset-on-state", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-on-state", + &value); + if (retval < 0) { + LOGE("Fail to read reset-on-state property\n"); + return retval; + } + + rst->reset_on_state = value; + + } else { + rst->reset_on_state = 0; + } + + prop = of_find_property(np, "synaptics,reset-active-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-active-ms", + &value); + if (retval < 0) { + LOGE("Fail to read reset-active-ms property\n"); + return retval; + } + + rst->reset_active_ms = value; + + } else { + rst->reset_active_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-delay-ms", + &value); + if (retval < 0) { + LOGE("Fail to read reset-delay-ms property\n"); + return retval; + } + + rst->reset_delay_ms = value; + + } else { + rst->reset_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,spi-byte-delay-us", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, + "synaptics,spi-byte-delay-us", &value); + if (retval < 0) { + LOGE("Fail to read byte-delay-us property\n"); + return retval; + } + + bus->spi_byte_delay_us = value; + + } else { + bus->spi_byte_delay_us = 0; + } + + prop = of_find_property(np, "synaptics,spi-block-delay-us", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, + "synaptics,spi-block-delay-us", &value); + if (retval < 0) { + LOGE("Fail to read block-delay-us property\n"); + return retval; + } + bus->spi_block_delay_us = value; + + } else { + bus->spi_block_delay_us = 0; + } + + prop = of_find_property(np, "synaptics,spi-mode", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,spi-mode", + &value); + if (retval < 0) { + LOGE("Fail to read synaptics,spi-mode property\n"); + return retval; + } + + bus->spi_mode = value; + + } else { + bus->spi_mode = 0; + } + + prop = of_find_property(np, "synaptics,io-switch-gpio", NULL); + if (prop && prop->length) { + bus->switch_gpio = of_get_named_gpio_flags(np, + "synaptics,io-switch-gpio", 0, NULL); + } else { + bus->switch_gpio = -1; + } + + prop = of_find_property(np, "synaptics,io-switch", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,io-switch", + &value); + if (retval < 0) { + LOGE("Fail to read io-switch property\n"); + return retval; + } + + bus->switch_state = value; + + } else { + bus->switch_state = 1; + } + + return 0; +} +#endif + +/** + * syna_tcm_spi_alloc_mem() + * + * Manage and allocate the memory to buf being as a temporary buffer for IO + * + * @param + * [ in] count: number of spi_transfer structures to send + * [ in] size: size of temporary buffer + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_spi_alloc_mem(unsigned int count, unsigned int size) +{ + static unsigned int xfer_count; + + if (count > xfer_count) { + syna_pal_mem_free((void *)xfer); + xfer = syna_pal_mem_alloc(count, sizeof(*xfer)); + if (!xfer) { + LOGE("Fail to allocate memory for xfer\n"); + xfer_count = 0; + return -ENOMEM; + } + xfer_count = count; + } else { + syna_pal_mem_set(xfer, 0, count * sizeof(*xfer)); + } + + if (size > buf_size) { + if (rx_buf) { + syna_pal_mem_free((void *)rx_buf); + rx_buf = NULL; + } + if (tx_buf) { + syna_pal_mem_free((void *)tx_buf); + tx_buf = NULL; + } + + rx_buf = syna_pal_mem_alloc(size, sizeof(unsigned char)); + if (!rx_buf) { + LOGE("Fail to allocate memory for rx_buf\n"); + buf_size = 0; + return -ENOMEM; + } + tx_buf = syna_pal_mem_alloc(size, sizeof(unsigned char)); + if (!tx_buf) { + LOGE("Fail to allocate memory for tx_buf\n"); + buf_size = 0; + return -ENOMEM; + } + + buf_size = size; + } + + return 0; +} + + +/** + * syna_spi_read() + * + * TouchCom over SPI requires the host to assert the SSB signal to address + * the device and retrieve the data. + * + * @param + * [ in] hw_if: the handle of hw interface + * [out] rd_data: buffer for storing data retrieved from device + * [ in] rd_len: number of bytes retrieved from device + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_spi_read(struct syna_hw_interface *hw_if, + unsigned char *rd_data, unsigned int rd_len) +{ + int retval; + unsigned int idx; + struct spi_message msg; + struct spi_device *spi = hw_if->pdev; + struct syna_hw_bus_data *bus = &hw_if->bdata_io; + + if (!spi) { + LOGE("Invalid bus io device\n"); + return -EINVAL; + } + + syna_pal_mutex_lock(&bus->io_mutex); + + spi_message_init(&msg); + + if (bus->spi_byte_delay_us == 0) + retval = syna_spi_alloc_mem(1, rd_len); + else + retval = syna_spi_alloc_mem(rd_len, rd_len); + if (retval < 0) { + LOGE("Fail to allocate memory\n"); + goto exit; + } + + if (bus->spi_byte_delay_us == 0) { + syna_pal_mem_set(tx_buf, 0xff, rd_len); + xfer[0].len = rd_len; + xfer[0].tx_buf = tx_buf; + xfer[0].rx_buf = rx_buf; + if (bus->spi_block_delay_us) + xfer[0].delay_usecs = bus->spi_block_delay_us; + spi_message_add_tail(&xfer[0], &msg); + } else { + tx_buf[0] = 0xff; + for (idx = 0; idx < rd_len; idx++) { + xfer[idx].len = 1; + xfer[idx].tx_buf = tx_buf; + xfer[idx].rx_buf = &rx_buf[idx]; + xfer[idx].delay_usecs = bus->spi_byte_delay_us; + if (bus->spi_block_delay_us && (idx == rd_len - 1)) + xfer[idx].delay_usecs = bus->spi_block_delay_us; + spi_message_add_tail(&xfer[idx], &msg); + } + } + + retval = spi_sync(spi, &msg); + if (retval != 0) { + LOGE("Failed to complete SPI transfer, error = %d\n", retval); + goto exit; + } + retval = syna_pal_mem_cpy(rd_data, rd_len, rx_buf, rd_len, rd_len); + if (retval < 0) { + LOGE("Fail to copy rx_buf to rd_data\n"); + goto exit; + } + + retval = rd_len; + +exit: + syna_pal_mutex_unlock(&bus->io_mutex); + + return retval; +} + +/** + * syna_spi_write() + * + * TouchCom over SPI requires the host to assert the SSB signal to address + * the device and send the data to the device. + * + * @param + * [ in] hw_if: the handle of hw interface + * [ in] wr_data: written data + * [ in] wr_len: length of written data in bytes + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_spi_write(struct syna_hw_interface *hw_if, + unsigned char *wr_data, unsigned int wr_len) +{ + int retval; + unsigned int idx; + struct spi_message msg; + struct spi_device *spi = hw_if->pdev; + struct syna_hw_bus_data *bus = &hw_if->bdata_io; + + if (!spi) { + LOGE("Invalid bus io device\n"); + return -EINVAL; + } + + syna_pal_mutex_lock(&bus->io_mutex); + + spi_message_init(&msg); + + if (bus->spi_byte_delay_us == 0) + retval = syna_spi_alloc_mem(1, wr_len); + else + retval = syna_spi_alloc_mem(wr_len, wr_len); + if (retval < 0) { + LOGE("Failed to allocate memory\n"); + goto exit; + } + + retval = syna_pal_mem_cpy(tx_buf, wr_len, wr_data, wr_len, wr_len); + if (retval < 0) { + LOGE("Fail to copy wr_data to tx_buf\n"); + goto exit; + } + + if (bus->spi_byte_delay_us == 0) { + xfer[0].len = wr_len; + xfer[0].tx_buf = tx_buf; + if (bus->spi_block_delay_us) + xfer[0].delay_usecs = bus->spi_block_delay_us; + spi_message_add_tail(&xfer[0], &msg); + } else { + for (idx = 0; idx < wr_len; idx++) { + xfer[idx].len = 1; + xfer[idx].tx_buf = &tx_buf[idx]; + xfer[idx].delay_usecs = bus->spi_byte_delay_us; + if (bus->spi_block_delay_us && (idx == wr_len - 1)) + xfer[idx].delay_usecs = bus->spi_block_delay_us; + spi_message_add_tail(&xfer[idx], &msg); + } + } + + retval = spi_sync(spi, &msg); + if (retval != 0) { + LOGE("Fail to complete SPI transfer, error = %d\n", retval); + goto exit; + } + + retval = wr_len; + +exit: + syna_pal_mutex_unlock(&bus->io_mutex); + + return retval; +} + + +/** + * syna_hw_interface + * + * Provide the hardware specific settings in defaults. + * Be noted the followings could be changed after .dtsi is parsed + */ +static struct syna_hw_interface syna_spi_hw_if = { + .bdata_io = { + .type = BUS_TYPE_SPI, + .rd_chunk_size = RD_CHUNK_SIZE, + .wr_chunk_size = WR_CHUNK_SIZE, + }, + .bdata_attn = { + .irq_enabled = false, + .irq_on_state = 0, + }, + .bdata_rst = { + .reset_on_state = 0, + .reset_delay_ms = 200, + .reset_active_ms = 20, + }, + .bdata_pwr = { + .power_on_state = 1, + .power_on_delay_ms = 200, + }, + .ops_power_on = syna_spi_power_on, + .ops_hw_reset = syna_spi_hw_reset, + .ops_read_data = syna_spi_read, + .ops_write_data = syna_spi_write, + .ops_enable_irq = syna_spi_enable_irq, +}; + +/** + * syna_spi_probe() + * + * Prepare the specific hardware interface and register the platform spi device + * + * @param + * [ in] spi: spi device + * + * @return + * on success, 0; otherwise, negative value on error. + */ + + bool syna_check_panel(struct device_node *np) +{ + struct property *prop; + int len; + struct device_node *mmi_np; + const char *mmi_dts_val; + const char *path, *mmi_dts; + prop = of_find_property(np, "mmi,status", &len); + if (prop == NULL || len < 0) { + return true; + } + path = prop->value; + mmi_np = of_find_node_by_path(path); + if (mmi_np == NULL) + { + return false; + } + + mmi_dts = of_prop_next_string(prop, path); + if (mmi_dts == NULL) + { + return false; + } + + mmi_dts_val = of_get_property(mmi_np, mmi_dts, &len); + if (mmi_dts_val == NULL || len <= 0) + { + return false; + } + if (strstr(mmi_dts_val,"visionox")) + { + return true; + } + return false; +} + +static int syna_spi_probe(struct spi_device *spi) +{ + int retval; + struct syna_hw_attn_data *attn = &syna_spi_hw_if.bdata_attn; + struct syna_hw_bus_data *bus = &syna_spi_hw_if.bdata_io; + + LOGD("syna_spi_probe enter\n"); + if (spi->dev.of_node && !mmi_device_is_available(spi->dev.of_node)) { + LOGE("mmi: device not supported\n"); + return -ENODEV; + } + + limit_panel = syna_check_panel(spi->dev.of_node); + + if(limit_panel){ + LOGI("i am five"); + }else{ + LOGI("i am four"); + } + + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) { + LOGE("Full duplex not supported by host\n"); + return -EIO; + } + + /* allocate an spi platform device */ + syna_spi_device = platform_device_alloc(PLATFORM_DRIVER_NAME, 0); + if (!syna_spi_device) { + LOGE("Fail to allocate platform device\n"); + return _ENODEV; + } + +#ifdef CONFIG_OF + syna_spi_parse_dt(&syna_spi_hw_if, &spi->dev); +#endif + + syna_pal_mutex_alloc(&attn->irq_en_mutex); + syna_pal_mutex_alloc(&bus->io_mutex); + + switch (bus->spi_mode) { + case 0: + spi->mode = SPI_MODE_0; + break; + case 1: + spi->mode = SPI_MODE_1; + break; + case 2: + spi->mode = SPI_MODE_2; + break; + case 3: + spi->mode = SPI_MODE_3; + break; + } + + /* keep the i/o device */ + syna_spi_hw_if.pdev = spi; + + syna_spi_device->dev.parent = &spi->dev; + syna_spi_device->dev.platform_data = &syna_spi_hw_if; + + spi->bits_per_word = 8; + spi->chip_select = 0; + + /* set up spi driver */ + retval = spi_setup(spi); + if (retval < 0) { + LOGE("Fail to set up SPI protocol driver\n"); + return retval; + } + spi_set_drvdata(spi, syna_spi_device); + /* initialize power unit */ + retval = syna_spi_config_psu(&syna_spi_hw_if); + if (retval < 0) { + LOGE("Fail to config power unit\n"); + return retval; + } + + /* initialize the gpio pins */ + retval = syna_spi_config_gpio(&syna_spi_hw_if); + if (retval < 0) { + LOGE("Fail to config gpio\n"); + return retval; + } + + /* do i/o switch if defined */ + if (bus->switch_gpio >= 0) + gpio_set_value(bus->switch_gpio, bus->switch_state); + + /* register the spi platform device */ + retval = platform_device_add(syna_spi_device); + if (retval < 0) { + LOGE("Fail to add platform device\n"); + return retval; + } + + return 0; +} + +/** + * syna_spi_remove() + * + * Unregister the platform spi device + * + * @param + * [ in] spi: spi device + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_spi_remove(struct spi_device *spi) +{ + struct syna_hw_attn_data *attn = &syna_spi_hw_if.bdata_attn; + struct syna_hw_bus_data *bus = &syna_spi_hw_if.bdata_io; + + /* release gpios */ + syna_spi_release_gpio(&syna_spi_hw_if); + + /* disable the regulators */ + syna_spi_release_psu(&syna_spi_hw_if); + + /* release mutex */ + syna_pal_mutex_free(&attn->irq_en_mutex); + syna_pal_mutex_free(&bus->io_mutex); + + /* remove the platform device */ + syna_spi_device->dev.platform_data = NULL; + platform_device_unregister(syna_spi_device); + + return 0; +} + +/** + * Describe an spi device driver and its related declarations + */ +static const struct spi_device_id syna_spi_id_table[] = { + {SPI_MODULE_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(spi, syna_spi_id_table); + +#ifdef CONFIG_OF +static const struct of_device_id syna_spi_of_match_table[] = { + { + .compatible = "synaptics,tcm-spi", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, syna_spi_of_match_table); +#else +#define syna_spi_of_match_table NULL +#endif + +static struct spi_driver syna_spi_driver = { + .driver = { + .name = SPI_MODULE_NAME, + .owner = THIS_MODULE, + .of_match_table = syna_spi_of_match_table, + }, + .probe = syna_spi_probe, + .remove = syna_spi_remove, + .id_table = syna_spi_id_table, +}; + + +/** + * syna_hw_interface_init() + * + * Initialize the lower-level hardware interface module. + * After returning, the handle of hw interface should be ready. + * + * @param + * void + * + * @return + * on success, 0; otherwise, negative value on error. + */ +int syna_hw_interface_init(void) +{ + return spi_register_driver(&syna_spi_driver); +} + +/** + * syna_hw_interface_exit() + * + * Delete the lower-level hardware interface module + * + * @param + * void + * + * @return + * none. + */ +void syna_hw_interface_exit(void) +{ + if (rx_buf) { + syna_pal_mem_free((void *)rx_buf); + rx_buf = NULL; + } + + if (tx_buf) { + syna_pal_mem_free((void *)tx_buf); + tx_buf = NULL; + } + + if (xfer) { + syna_pal_mem_free((void *)xfer); + xfer = NULL; + } + + spi_unregister_driver(&syna_spi_driver); +} + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TouchCom SPI Bus Module"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_runtime.h b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_runtime.h new file mode 100644 index 000000000000..0a21d5eec436 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_runtime.h @@ -0,0 +1,753 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file syna_tcm2_runtime.h + * + * This file abstracts platform-specific headers and C runtime APIs being used + * on the target platform. + */ + +#ifndef _SYNAPTICS_TCM2_C_RUNTIME_H_ +#define _SYNAPTICS_TCM2_C_RUNTIME_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_DRM_PANEL +#include +#elif CONFIG_FB +#include +#include +#endif +#include +#include + +/** + * @brief: DEV_MANAGED_API + * + * For linux kernel, managed interface was created for resources commonly + * used by device drivers using devres. + * + * Open if willing to use managed-APIs rather than legacy APIs. + */ +#define DEV_MANAGED_API + +#if defined(DEV_MANAGED_API) || defined(USE_DRM_PANEL_NOTIFIER) +extern struct device *syna_request_managed_device(void); +#endif + +/** + * @section: Log helpers + * + * @brief: LOGD + * Output the debug message + * + * @brief: LOGI + * Output the info message + * + * @brief: LOGN + * Output the notice message + * + * @brief: LOGW + * Output the warning message + * + * @brief: LOGE + * Output the error message + */ +#define LOGD(log, ...) \ + pr_debug("[SYNA-debug] %s: " log, __func__, ##__VA_ARGS__) +#define LOGI(log, ...) \ + pr_info("[SYNA-info] %s: " log, __func__, ##__VA_ARGS__) +#define LOGN(log, ...) \ + pr_notice("[SYNA-info] %s: " log, __func__, ##__VA_ARGS__) +#define LOGW(log, ...) \ + pr_warn("[SYNA-warning] %s: " log, __func__, ##__VA_ARGS__) +#define LOGE(log, ...) \ + pr_err("[SYNA-error] %s: " log, __func__, ##__VA_ARGS__) + + +/** + * @section: Error Codes returned + * Functions usually return 0 or positive value on success. + * Thus, please defines negative value here. + */ +#define _EIO (-EIO) /* I/O errors */ +#define _ENOMEM (-ENOMEM) /* Out of memory */ +#define _EINVAL (-EINVAL) /* Invalid parameters */ +#define _ENODEV (-ENODEV) /* No such device */ +#define _ETIMEDOUT (-ETIMEDOUT) /* execution timeout */ + + +/** + * @section: Data Comparison helpers + * + * @brief: MAX + * Find the maximum value between + * + * @brief: MIN: + * Find the minimum value between + * + * @brief: GET_BIT + * Return the value of target bit + */ +#define MAX(a, b) \ + ({__typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a > _b ? _a : _b; }) + +#define MIN(a, b) \ + ({__typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a < _b ? _a : _b; }) + +#define GET_BIT(var, pos) \ + (((var) & (1 << (pos))) >> (pos)) + + +/** + * @section: C Atomic operations + * + * @brief: ATOMIC_SET + * Set an atomic data + * + * @brief: ATOMIC_GET: + * Get an atomic data + */ +typedef atomic_t syna_pal_atomic_t; + +#define ATOMIC_SET(atomic, value) \ + atomic_set(&atomic, value) + +#define ATOMIC_GET(atomic) \ + atomic_read(&atomic) + + +/** + * @section: C Integer Calculation helpers + * + * @brief: syna_pal_le2_to_uint + * Convert 2-byte data to an unsigned integer + * + * @brief: syna_pal_le4_to_uint + * Convert 4-byte data to an unsigned integer + * + * @brief: syna_pal_ceil_div + * Calculate the ceiling of the integer division + */ + +/** + * syna_pal_le2_to_uint() + * + * Convert 2-byte data in little-endianness to an unsigned integer + * + * @param + * [ in] src: 2-byte data in little-endianness + * + * @return + * an unsigned integer converted + */ +static inline unsigned int syna_pal_le2_to_uint(const unsigned char *src) +{ + return (unsigned int)src[0] + + (unsigned int)src[1] * 0x100; +} +/** + * syna_pal_le4_to_uint() + * + * Convert 4-byte data in little-endianness to an unsigned integer + * + * @param + * [ in] src: 4-byte data in little-endianness + * + * @return + * an unsigned integer converted + */ +static inline unsigned int syna_pal_le4_to_uint(const unsigned char *src) +{ + return (unsigned int)src[0] + + (unsigned int)src[1] * 0x100 + + (unsigned int)src[2] * 0x10000 + + (unsigned int)src[3] * 0x1000000; +} +/** + * syna_pal_ceil_div() + * + * Calculate the ceiling of the integer division + * + * @param + * [ in] dividend: the dividend value + * [ in] divisor: the divisor value + * + * @return + * the ceiling of the integer division + */ +static inline unsigned int syna_pal_ceil_div(unsigned int dividend, + unsigned int divisor) +{ + return (dividend + divisor - 1) / divisor; +} + + +/** + * @section: C Runtime for Memory Management helpers + * + * @brief: syna_pal_mem_calloc + * Allocate a block of memory space + * + * @brief: syna_pal_mem_free + * Deallocate a block of memory previously allocated + * + * @brief: syna_pal_mem_set + * Fill memory with a constant byte + * + * @brief: syna_pal_mem_cpy + * Ensure the safe size before doing memory copy + */ + +/** + * syna_pal_mem_calloc() + * + * Allocates a block of memory for an array of 'num' elements, + * each of them has 'size' bytes long, and initializes all its bits to zero. + * + * @param + * [ in] num: number of elements for an array + * [ in] size: number of bytes for each elements + * + * @return + * On success, a pointer to the memory block allocated by the function. + */ +static inline void *syna_pal_mem_alloc(unsigned int num, unsigned int size) +{ +#ifdef DEV_MANAGED_API + struct device *dev = syna_request_managed_device(); + + if (!dev) { + LOGE("Invalid managed device\n"); + return NULL; + } +#endif + + if ((int)(num * size) <= 0) { + LOGE("Invalid parameter\n"); + return NULL; + } + +#ifdef DEV_MANAGED_API + return devm_kcalloc(dev, num, size, GFP_KERNEL); +#else /* Legacy API */ + return kcalloc(num, size, GFP_KERNEL); +#endif +} +/** + * syna_pal_mem_free() + * + * Deallocate a block of memory previously allocated. + * + * @param + * [ in] ptr: a memory block previously allocated + * + * @return + * none. + */ +static inline void syna_pal_mem_free(void *ptr) +{ +#ifdef DEV_MANAGED_API + struct device *dev = syna_request_managed_device(); + + if (!dev) { + LOGE("Invalid managed device\n"); + return; + } + + if (ptr) + devm_kfree(dev, ptr); +#else /* Legacy API */ + kfree(ptr); +#endif +} +/** + * syna_pal_mem_set() + * + * Fill memory with a constant byte + * + * @param + * [ in] ptr: pointer to a memory block + * [ in] c: the constant value + * [ in] n: number of byte being set + * + * @return + * none. + */ +static inline void syna_pal_mem_set(void *ptr, int c, unsigned int n) +{ + memset(ptr, c, n); +} +/** + * syna_pal_mem_cpy() + * + * Ensure the safe size before copying the values of num bytes from the + * location to the memory block pointed to by destination. + * + * @param + * [out] dest: pointer to the destination space + * [ in] dest_size: size of destination array + * [ in] src: pointer to the source of data to be copied + * [ in] src_size: size of source array + * [ in] num: number of bytes to copy + * + * @return + * 0 on success; otherwise, on error. + */ +static inline int syna_pal_mem_cpy(void *dest, unsigned int dest_size, + const void *src, unsigned int src_size, unsigned int num) +{ + if (dest == NULL || src == NULL) + return -1; + + if (num > dest_size || num > src_size) { + LOGE("Invalid size. src:%d, dest:%d, num:%d\n", + src_size, dest_size, num); + return -1; + } + + memcpy((void *)dest, (const void *)src, num); + + return 0; +} + + +/** + * @section: C Runtime for Muxtex Control helpers + * + * @brief: syna_pal_mutex_alloc + * Create a mutex object + * + * @brief: syna_pal_mutex_free + * Release the mutex object previously allocated + * + * @brief: syna_pal_mutex_lock + * Lock the mutex + * + * @brief: syna_pal_mutex_unlock + * Unlock the mutex previously locked + */ + +typedef struct mutex syna_pal_mutex_t; + +/** + * syna_pal_mutex_alloc() + * + * Create a mutex object. + * + * @param + * [out] ptr: pointer to the mutex handle being allocated + * + * @return + * 0 on success; otherwise, on error. + */ +static inline int syna_pal_mutex_alloc(syna_pal_mutex_t *ptr) +{ + mutex_init((struct mutex *)ptr); + return 0; +} +/** + * syna_pal_mutex_free() + * + * Release the mutex object previously allocated. + * + * @param + * [ in] ptr: mutex handle previously allocated + * + * @return + * none. + */ +static inline void syna_pal_mutex_free(syna_pal_mutex_t *ptr) +{ + /* do nothing */ +} +/** + * syna_pal_mutex_lock() + * + * Acquire/lock the mutex. + * + * @param + * [ in] ptr: a mutex handle + * + * @return + * none. + */ +static inline void syna_pal_mutex_lock(syna_pal_mutex_t *ptr) +{ + mutex_lock((struct mutex *)ptr); +} +/** + * syna_pal_mutex_unlock() + * + * Unlock the locked mutex. + * + * @param + * [ in] ptr: a mutex handle + * + * @return + * none. + */ +static inline void syna_pal_mutex_unlock(syna_pal_mutex_t *ptr) +{ + mutex_unlock((struct mutex *)ptr); +} + + +/** + * @section: C Runtime for Completion Event + * + * @brief: syna_pal_completion_alloc + * Allocate a completion event + * + * @brief: syna_pal_completion_free + * Release the completion event previously allocated + * + * @brief: syna_pal_completion_complete + * Complete the completion event being waiting for + * + * @brief: syna_pal_completion_reset + * Reset or reinitialize the completion event + * + * @brief: syna_pal_completion_wait_for + * Wait for the completion event + */ + +typedef struct completion syna_pal_completion_t; + +/** + * syna_pal_completion_alloc() + * + * Allocate a completion event, and the default state is not set. + * Caller must reset the event before each use. + * + * @param + * [out] ptr: pointer to the completion handle being allocated + * + * @return + * 0 on success; otherwise, on error. + */ +static inline int syna_pal_completion_alloc(syna_pal_completion_t *ptr) +{ + init_completion((struct completion *)ptr); + return 0; +} +/** + * syna_pal_completion_free() + * + * Release the completion event previously allocated + * + * @param + * [ in] ptr: the completion event previously allocated + event + * @return + * none. + */ +static inline void syna_pal_completion_free(syna_pal_completion_t *ptr) +{ + /* do nothing */ +} +/** + * syna_pal_completion_complete() + * + * Complete the completion event being waiting for + * + * @param + * [ in] ptr: the completion event + * + * @return + * none. + */ +static inline void syna_pal_completion_complete(syna_pal_completion_t *ptr) +{ + complete((struct completion *)ptr); +} +/** + * syna_pal_completion_reset() + * + * Reset or reinitialize the completion event + * + * @param + * [ in] ptr: the completion event + * + * @return + * none. + */ +static inline void syna_pal_completion_reset(syna_pal_completion_t *ptr) +{ +#if (KERNEL_VERSION(3, 13, 0) > LINUX_VERSION_CODE) + init_completion((struct completion *)ptr); +#else + reinit_completion((struct completion *)ptr); +#endif +} +/** + * syna_pal_completion_wait_for() + * + * Wait for the completion event during the given time slot + * + * @param + * [ in] ptr: the completion event + * [ in] timeout_ms: time frame in milliseconds + * + * @return + * 0 if a signal is received; otherwise, on timeout or error occurs. + */ +static inline int syna_pal_completion_wait_for(syna_pal_completion_t *ptr, + unsigned int timeout_ms) +{ + int retval; + + retval = wait_for_completion_timeout((struct completion *)ptr, + msecs_to_jiffies(timeout_ms)); + if (retval == 0) /* timeout occurs */ + return -1; + + return 0; +} + + +/** + * @section: C Runtime to Pause the Execution + * + * @brief: syna_pal_sleep_ms + * Sleep for a fixed amount of time in milliseconds + * + * @brief: syna_pal_sleep_us + * Sleep for a range of time in microseconds + * + * @brief: syna_pal_busy_delay_ms + * Busy wait for a fixed amount of time in milliseconds + */ + +/** + * syna_pal_sleep_ms() + * + * Sleep for a fixed amount of time in milliseconds + * + * @param + * [ in] time_ms: time frame in milliseconds + * + * @return + * none. + */ +static inline void syna_pal_sleep_ms(int time_ms) +{ + msleep(time_ms); +} +/** + * syna_pal_sleep_us() + * + * Sleep for a range of time in microseconds + * + * @param + * [ in] time_us_min: the min. time frame in microseconds + * [ in] time_us_max: the max. time frame in microseconds + * + * @return + * none. + */ +static inline void syna_pal_sleep_us(int time_us_min, int time_us_max) +{ + usleep_range(time_us_min, time_us_max); +} +/** + * syna_pal_busy_delay_ms() + * + * Busy wait for a fixed amount of time in milliseconds + * + * @param + * [ in] time_ms: time frame in milliseconds + * + * @return + * none. + */ +static inline void syna_pal_busy_delay_ms(int time_ms) +{ + mdelay(time_ms); +} + + +/** + * @section: C Runtime for String operations + * + * @brief: syna_pal_str_len + * Return the length of C string + * + * @brief: syna_pal_str_cpy: + * Ensure the safe size before doing C string copy + * + * @brief: syna_pal_str_cmp: + * Compare whether the given C strings are equal or not + */ + +/** + * syna_pal_str_len() + * + * Return the length of C string + * + * @param + * [ in] str: an array of characters + * + * @return + * the length of given string + */ +static inline unsigned int syna_pal_str_len(const char *str) +{ + return (unsigned int)strlen(str); +} +/** + * syna_pal_str_cpy() + * + * Copy the C string pointed by source into the array pointed by destination. + * + * @param + * [ in] dest: pointer to the destination C string + * [ in] dest_size: size of destination C string + * [out] src: pointer to the source of C string to be copied + * [ in] src_size: size of source C string + * [ in] num: number of bytes to copy + * + * @return + * 0 on success; otherwise, on error. + */ +static inline int syna_pal_str_cpy(char *dest, unsigned int dest_size, + const char *src, unsigned int src_size, unsigned int num) +{ + if (dest == NULL || src == NULL) + return -1; + + if (num > dest_size || num > src_size) { + LOGE("Invalid size. src:%d, dest:%d, num:%d\n", + src_size, dest_size, num); + return -1; + } + + strncpy(dest, src, num); + + return 0; +} +/** + * syna_pal_str_cmp() + * + * Compares up to num characters of the C string str1 to those of the + * C string str2. + * + * @param + * [ in] str1: C string to be compared + * [ in] str2: C string to be compared + * [ in] num: number of characters to compare + * + * @return + * 0 if both strings are equal; otherwise, not equal. + */ +static inline int syna_pal_str_cmp(const char *str1, const char *str2, + unsigned int num) +{ + return strncmp(str1, str2, num); +} +/** + * syna_pal_hex_to_uint() + * + * Convert the given string in hex to an integer returned + * + * @param + * [ in] str: C string to be converted + * [ in] length: target length + * + * @return + * An integer converted + */ +static inline unsigned int syna_pal_hex_to_uint(char *str, int length) +{ + unsigned int result = 0; + char *ptr = NULL; + + for (ptr = str; ptr != str + length; ++ptr) { + result <<= 4; + if (*ptr >= 'A') + result += *ptr - 'A' + 10; + else + result += *ptr - '0'; + } + + return result; +} + +/** + * @section: C Runtime for Checksum Calculation + * + * @brief: syna_pal_crc32 + * Calculates the CRC32 value + */ + +/** + * syna_pal_crc32() + * + * Calculates the CRC32 value of the data + * + * @param + * [ in] seed: the previous crc32 value + * [ in] data: byte data for the calculation + * [ in] len: the byte length of the data. + * + * @return + * 0 if both strings are equal; otherwise, not equal. + */ +static inline unsigned int syna_pal_crc32(unsigned int seed, + const char *data, unsigned int len) +{ + return crc32(seed, data, len); +} + +#endif /* end of _SYNAPTICS_TCM2_C_RUNTIME_H_ */ diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_sysfs.c b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_sysfs.c new file mode 100644 index 000000000000..7ac7977e5089 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_sysfs.c @@ -0,0 +1,2290 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file syna_tcm2_sysfs.c + * + * This file implements cdev and ioctl interface in the reference driver. + */ + +#include + +#include "syna_tcm2.h" +#include "synaptics_touchcom_core_dev.h" +#include "synaptics_touchcom_func_base.h" +#include "synaptics_touchcom_func_touch.h" +#ifdef HAS_TESTING_FEATURE +#include "syna_tcm2_testing.h" +#endif + +#if (KERNEL_VERSION(5, 9, 0) <= LINUX_VERSION_CODE) || \ + defined(HAVE_UNLOCKED_IOCTL) +#define USE_UNLOCKED_IOCTL +#endif + +#if defined(CONFIG_COMPAT) && defined(HAVE_COMPAT_IOCTL) +#define USE_COMPAT_IOCTL +#endif + +#if (KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE) +#define REPLACE_KTIME +#endif + + +/* #define ENABLE_PID_TASK */ + +#define SIG_ATTN (46) + +/* structure for IOCTLs + */ +struct syna_ioctl_data { + unsigned int data_length; + unsigned int buf_size; + unsigned char __user *buf; +}; + +#ifdef USE_COMPAT_IOCTL +struct syna_tcm_ioctl_data_compat { + unsigned int data_length; + unsigned int buf_size; + compat_uptr_t __user *buf; +}; +#endif + +/* defines the IOCTLs supported + */ +#define IOCTL_MAGIC 's' + +/* Previous IOCTLs in early driver */ +#define OLD_RESET_ID (0x00) +#define OLD_SET_IRQ_MODE_ID (0x01) +#define OLD_SET_RAW_MODE_ID (0x02) +#define OLD_CONCURRENT_ID (0x03) + +#define IOCTL_OLD_RESET \ + _IO(IOCTL_MAGIC, OLD_RESET_ID) +#define IOCTL_OLD_SET_IRQ_MODE \ + _IOW(IOCTL_MAGIC, OLD_SET_IRQ_MODE_ID, int) +#define IOCTL_OLD_SET_RAW_MODE \ + _IOW(IOCTL_MAGIC, OLD_SET_RAW_MODE_ID, int) +#define IOCTL_OLD_CONCURRENT \ + _IOW(IOCTL_MAGIC, OLD_CONCURRENT_ID, int) + +/* Standard IOCTLs in TCM2 driver */ +#define STD_IOCTL_BEGIN (0x10) +#define STD_SET_PID_ID (0x11) +#define STD_ENABLE_IRQ_ID (0x12) +#define STD_RAW_READ_ID (0x13) +#define STD_RAW_WRITE_ID (0x14) +#define STD_GET_FRAME_ID (0x15) +#define STD_SEND_MESSAGE_ID (0x16) +#define STD_SET_REPORTS_ID (0x17) +#define STD_CHECK_FRAMES_ID (0x18) +#define STD_CLEAN_OUT_FRAMES_ID (0x19) + +#define IOCTL_STD_IOCTL_BEGIN \ + _IOR(IOCTL_MAGIC, STD_IOCTL_BEGIN) +#define IOCTL_STD_SET_PID \ + _IOW(IOCTL_MAGIC, STD_SET_PID_ID, struct syna_ioctl_data *) +#define IOCTL_STD_ENABLE_IRQ \ + _IOW(IOCTL_MAGIC, STD_ENABLE_IRQ_ID, struct syna_ioctl_data *) +#define IOCTL_STD_RAW_READ \ + _IOR(IOCTL_MAGIC, STD_RAW_READ_ID, struct syna_ioctl_data *) +#define IOCTL_STD_RAW_WRITE \ + _IOW(IOCTL_MAGIC, STD_RAW_WRITE_ID, struct syna_ioctl_data *) +#define IOCTL_STD_GET_FRAME \ + _IOWR(IOCTL_MAGIC, STD_GET_FRAME_ID, struct syna_ioctl_data *) +#define IOCTL_STD_SEND_MESSAGE \ + _IOWR(IOCTL_MAGIC, STD_SEND_MESSAGE_ID, struct syna_ioctl_data *) +#define IOCTL_STD_SET_REPORT_TYPES \ + _IOW(IOCTL_MAGIC, STD_SET_REPORTS_ID, struct syna_ioctl_data *) +#define IOCTL_STD_CHECK_FRAMES \ + _IOWR(IOCTL_MAGIC, STD_CHECK_FRAMES_ID, struct syna_ioctl_data *) +#define IOCTL_STD_CLEAN_OUT_FRAMES \ + _IOWR(IOCTL_MAGIC, STD_CLEAN_OUT_FRAMES_ID, struct syna_ioctl_data *) + +/* g_sysfs_dir represents the root directory of sysfs nodes being created + */ +static struct kobject *g_sysfs_dir; + +/* g_extif_mutex is used to protect the access from the userspace application + */ +static syna_pal_mutex_t g_extif_mutex; + +/* g_cdev_buf is a temporary buffer storing the data from userspace + */ +static struct tcm_buffer g_cdev_cbuf; + +/* g_fifo_queue_mutex is used to protect the access from + * the userspace application + */ +static syna_pal_mutex_t g_fifo_queue_mutex; + +/* The g_sysfs_io_polling_interval is used to set the polling interval + * for syna_tcm_send_command from syna_cdev_ioctl_send_message. + * It will set to the mode SYSFS_FULL_INTERRUPT for using the full + * interrupt mode. The way to update this variable is through the + * syna_cdev_ioctl_enable_irq. + */ +unsigned int g_sysfs_io_polling_interval; + +/* a buffer to record the streaming report + * considering touch report and another reports may be co-enabled + * at the same time, give a little buffer here (3 sec x 300 fps) + */ +#define FIFO_QUEUE_MAX_FRAMES (1200) +#define SEND_MESSAGE_HEADER_LENGTH (3) + +/* Indicate the interrupt status especially for sysfs using */ +#define SYSFS_DISABLED_INTERRUPT (0) +#define SYSFS_ENABLED_INTERRUPT (1) + +/*Define a data structure that contains a list_head*/ +struct fifo_queue { + struct list_head next; + unsigned char *fifo_data; + unsigned int data_length; +#ifdef REPLACE_KTIME + struct timespec64 timestamp; +#else + struct timeval timestamp; +#endif +}; + + +/** + * syna_sysfs_info_show() + * + * Attribute to show the device and driver information to the console. + * + * @param + * [ in] kobj: an instance of kobj + * [ in] attr: an instance of kobj attribute structure + * [out] buf: string buffer shown on console + * + * @return + * on success, number of characters being output; + * otherwise, negative value on error. + */ +static ssize_t syna_sysfs_info_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int retval; + unsigned int count; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm *tcm; + struct tcm_dev *tcm_dev; + int i; + + p_kobj = g_sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm = dev_get_drvdata(p_dev); + tcm_dev = tcm->tcm_dev; + + syna_pal_mutex_lock(&g_extif_mutex); + + count = 0; + + retval = snprintf(buf, PAGE_SIZE - count, + "Driver version: %d.%s\n", + SYNAPTICS_TCM_DRIVER_VERSION, + SYNAPTICS_TCM_DRIVER_SUBVER); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "Core lib version: %d.%02d\n\n", + (unsigned char)(SYNA_TCM_CORE_LIB_VERSION >> 8), + (unsigned char)SYNA_TCM_CORE_LIB_VERSION); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + if (!tcm->is_connected) { + retval = snprintf(buf, PAGE_SIZE - count, + "Device is NOT connected\n"); + goto exit; + } + + + retval = snprintf(buf, PAGE_SIZE - count, + "TouchComm version: %d\n", tcm_dev->id_info.version); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + switch (tcm_dev->id_info.mode) { + case MODE_APPLICATION_FIRMWARE: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: Application Firmware, 0x%02x\n", + tcm_dev->id_info.mode); + if (retval < 0) + goto exit; + break; + case MODE_BOOTLOADER: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: Bootloader, 0x%02x\n", + tcm_dev->id_info.mode); + if (retval < 0) + goto exit; + break; + case MODE_ROMBOOTLOADER: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: Rom Bootloader, 0x%02x\n", + tcm_dev->id_info.mode); + if (retval < 0) + goto exit; + break; + default: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: Mode 0x%02x\n", + tcm_dev->id_info.mode); + if (retval < 0) + goto exit; + break; + } + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "Part number: "); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = syna_pal_mem_cpy(buf, + PAGE_SIZE - count, + tcm_dev->id_info.part_number, + sizeof(tcm_dev->id_info.part_number), + sizeof(tcm_dev->id_info.part_number)); + if (retval < 0) { + LOGE("Fail to copy part number string\n"); + goto exit; + } + buf += sizeof(tcm_dev->id_info.part_number); + count += sizeof(tcm_dev->id_info.part_number); + + retval = snprintf(buf, PAGE_SIZE - count, "\n"); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "Packrat number: %d\n\n", tcm_dev->packrat_number); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + if (tcm_dev->id_info.mode != MODE_APPLICATION_FIRMWARE) { + retval = count; + goto exit; + } + + retval = snprintf(buf, PAGE_SIZE - count, "Config ID: "); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + for (i = 0; i < MAX_SIZE_CONFIG_ID; i++) { + retval = snprintf(buf, PAGE_SIZE - count, + "0x%2x ", tcm_dev->config_id[i]); + if (retval < 0) + goto exit; + buf += retval; + count += retval; + } + + retval = snprintf(buf, PAGE_SIZE - count, "\n"); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "Max X & Y: %d, %d\n", tcm_dev->max_x, tcm_dev->max_y); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "Num of objects: %d\n", tcm_dev->max_objects); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "Num of cols & rows: %d, %d\n", tcm_dev->cols, tcm_dev->rows); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = count; + +exit: + syna_pal_mutex_unlock(&g_extif_mutex); + return retval; +} + +static struct kobj_attribute kobj_attr_info = + __ATTR(info, 0444, syna_sysfs_info_show, NULL); + +/** + * syna_sysfs_irq_en_store() + * + * Attribute to disable/enable the irq + * + * @param + * [ in] kobj: an instance of kobj + * [ in] attr: an instance of kobj attribute structure + * [ in] buf: string buffer input + * [ in] count: size of buffer input + * + * @return + * on success, return count; otherwise, return error code + */ +static ssize_t syna_sysfs_irq_en_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int retval = 0; + unsigned int input; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm *tcm; + + p_kobj = g_sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm = dev_get_drvdata(p_dev); + + if (kstrtouint(buf, 10, &input)) + return -EINVAL; + + if (!tcm->hw_if->ops_enable_irq) + return 0; + + if (!tcm->is_connected) { + LOGW("Device is NOT connected\n"); + return count; + } + + syna_pal_mutex_lock(&g_extif_mutex); + + /* disable the interrupt line */ + if (input == 0) { + retval = tcm->hw_if->ops_enable_irq(tcm->hw_if, false); + if (retval < 0) { + LOGE("Fail to disable interrupt\n"); + goto exit; + } + } else if (input == 1) { + /* enable the interrupt line */ + retval = tcm->hw_if->ops_enable_irq(tcm->hw_if, true); + if (retval < 0) { + LOGE("Fail to enable interrupt\n"); + goto exit; + } + } else { + LOGW("Unknown option %d (0:disable / 1:enable)\n", input); + retval = -EINVAL; + goto exit; + } + + retval = count; + +exit: + syna_pal_mutex_unlock(&g_extif_mutex); + return retval; +} + +static struct kobj_attribute kobj_attr_irq_en = + __ATTR(irq_en, 0220, NULL, syna_sysfs_irq_en_store); + +/** + * syna_sysfs_reset_store() + * + * Attribute to issue a reset. + * "1" for a sw reset; "2" for a hardware reset + * + * @param + * [ in] kobj: an instance of kobj + * [ in] attr: an instance of kobj attribute structure + * [ in] buf: string buffer input + * [ in] count: size of buffer input + * + * @return + * on success, return count; otherwise, return error code + */ +static ssize_t syna_sysfs_reset_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int retval = 0; + unsigned int input; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm *tcm; + + p_kobj = g_sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm = dev_get_drvdata(p_dev); + + if (kstrtouint(buf, 10, &input)) + return -EINVAL; + + if (!tcm->is_connected) { + LOGW("Device is NOT connected\n"); + return count; + } + + syna_pal_mutex_lock(&g_extif_mutex); + + if (input == 1) { + retval = syna_tcm_reset(tcm->tcm_dev); + if (retval < 0) { + LOGE("Fail to do reset\n"); + goto exit; + } + } else if (input == 2) { + if (!tcm->hw_if->ops_hw_reset) { + LOGE("No hardware reset support\n"); + goto exit; + } + + tcm->hw_if->ops_hw_reset(tcm->hw_if); + } else { + LOGW("Unknown option %d (1:sw / 2:hw)\n", input); + retval = -EINVAL; + goto exit; + } + + /* check the fw setup in case the settings is changed */ + if (IS_APP_FW_MODE(tcm->tcm_dev->dev_mode)) { + retval = tcm->dev_set_up_app_fw(tcm); + if (retval < 0) { + LOGE("Fail to set up app fw\n"); + goto exit; + } + } + + retval = count; + +exit: + syna_pal_mutex_unlock(&g_extif_mutex); + return retval; +} + +static struct kobj_attribute kobj_attr_reset = + __ATTR(reset, 0220, NULL, syna_sysfs_reset_store); + + +/** + * syna_sysfs_pwr_store() + * + * Attribute to change the power state. + * + * @param + * [ in] kobj: an instance of kobj + * [ in] attr: an instance of kobj attribute structure + * [ in] buf: string buffer input + * [ in] count: size of buffer input + * + * @return + * on success, return count; otherwise, return error code + */ +static ssize_t syna_sysfs_pwr_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int retval = 0; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm *tcm; + + p_kobj = g_sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm = dev_get_drvdata(p_dev); + + if (!tcm->is_connected) { + LOGW("Device is NOT connected\n"); + return count; + } + + syna_pal_mutex_lock(&g_extif_mutex); + + if (strncmp(buf, "resume", 6) == 0) { + if (tcm->dev_resume) + tcm->dev_resume(p_dev); + } else if (strncmp(buf, "suspend", 7) == 0) { + if (tcm->dev_suspend) + tcm->dev_suspend(p_dev); + } else { + LOGW("Unknown option %s\n", buf); + retval = -EINVAL; + goto exit; + } + + retval = count; + +exit: + syna_pal_mutex_unlock(&g_extif_mutex); + return retval; +} + +static struct kobj_attribute kobj_attr_pwr = + __ATTR(power_state, 0220, NULL, syna_sysfs_pwr_store); + +/** + * declaration of sysfs attributes + */ +static struct attribute *attrs[] = { + &kobj_attr_info.attr, + &kobj_attr_irq_en.attr, + &kobj_attr_reset.attr, + &kobj_attr_pwr.attr, + NULL, +}; + +static struct attribute_group attr_group = { + .attrs = attrs, +}; + +/** + * syna_sysfs_create_dir() + * + * Create a directory and register it with sysfs. + * Then, create all defined sysfs files. + * + * @param + * [ in] tcm: the driver handle + * [ in] pdev: an instance of platform device + * + * @return + * on success, 0; otherwise, negative value on error. + */ +int syna_sysfs_create_dir(struct syna_tcm *tcm, + struct platform_device *pdev) +{ + int retval = 0; + + g_sysfs_dir = kobject_create_and_add("sysfs", + &pdev->dev.kobj); + if (!g_sysfs_dir) { + LOGE("Fail to create sysfs directory\n"); + return -ENOTDIR; + } + + tcm->sysfs_dir = g_sysfs_dir; + + retval = sysfs_create_group(g_sysfs_dir, &attr_group); + if (retval < 0) { + LOGE("Fail to create sysfs group\n"); + + kobject_put(tcm->sysfs_dir); + return retval; + } + +#ifdef HAS_TESTING_FEATURE + retval = syna_testing_create_dir(tcm, g_sysfs_dir); + if (retval < 0) { + LOGE("Fail to create testing sysfs\n"); + + sysfs_remove_group(tcm->sysfs_dir, &attr_group); + kobject_put(tcm->sysfs_dir); + return retval; + } +#endif + + return 0; +} +/** + * syna_sysfs_remove_dir() + * + * Remove the allocate sysfs directory + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +void syna_sysfs_remove_dir(struct syna_tcm *tcm) +{ + if (!tcm) { + LOGE("Invalid tcm device handle\n"); + return; + } + + if (tcm->sysfs_dir) { +#ifdef HAS_TESTING_FEATURE + syna_testing_remove_dir(); +#endif + + sysfs_remove_group(tcm->sysfs_dir, &attr_group); + + kobject_put(tcm->sysfs_dir); + } + +} +#ifdef ENABLE_EXTERNAL_FRAME_PROCESS +/** + * syna_cdev_insert_fifo() + * + * Insert/Push the data to the queue. + * + * This function is called by syna_cdev_update_report_queue(), + * where the event data will be placed as the below format in byte + * and use this function to store the data in queue. + * [0 ] : status / report code + * [1 : 2 ] : length of data frame + * [3 : N + 3] : N bytes of data payload + * + * @param + * [ in] tcm: the driver handle + * [ in] buf_ptr: points to a data going to push + * [ in] length: data length + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_cdev_insert_fifo(struct syna_tcm *tcm, + unsigned char *buf_ptr, unsigned int length) +{ + int retval = 0; + struct fifo_queue *pfifo_data; + struct fifo_queue *pfifo_data_temp; + static int pre_remaining_frames = -1; + + syna_pal_mutex_lock(&g_fifo_queue_mutex); + + /* check queue buffer limit */ + if (tcm->fifo_remaining_frame >= FIFO_QUEUE_MAX_FRAMES) { + if (tcm->fifo_remaining_frame != pre_remaining_frames) + LOGI("Reached %d and drop FIFO first frame\n", + tcm->fifo_remaining_frame); + + pfifo_data_temp = list_first_entry(&tcm->frame_fifo_queue, + struct fifo_queue, next); + + list_del(&pfifo_data_temp->next); + kfree(pfifo_data_temp->fifo_data); + kfree(pfifo_data_temp); + pre_remaining_frames = tcm->fifo_remaining_frame; + tcm->fifo_remaining_frame--; + } else if (pre_remaining_frames >= FIFO_QUEUE_MAX_FRAMES) { + LOGI("Reached limit, dropped oldest frame, remaining:%d\n", + tcm->fifo_remaining_frame); + pre_remaining_frames = tcm->fifo_remaining_frame; + } + + pfifo_data = kmalloc(sizeof(*pfifo_data), GFP_KERNEL); + if (!(pfifo_data)) { + LOGE("Failed to allocate memory\n"); + LOGE("Allocation size = %zu\n", (sizeof(*pfifo_data))); + retval = -ENOMEM; + goto exit; + } + + pfifo_data->fifo_data = kmalloc(length, GFP_KERNEL); + if (!(pfifo_data->fifo_data)) { + LOGE("Failed to allocate memory, size = %d\n", length); + retval = -ENOMEM; + goto exit; + } + + pfifo_data->data_length = length; + + memcpy((void *)pfifo_data->fifo_data, (void *)buf_ptr, length); +#ifdef REPLACE_KTIME + ktime_get_real_ts64(&(pfifo_data->timestamp)); +#else + do_gettimeofday(&(pfifo_data->timestamp)); +#endif + /* append the data to the tail for FIFO queueing */ + list_add_tail(&pfifo_data->next, &tcm->frame_fifo_queue); + tcm->fifo_remaining_frame++; + retval = 0; + +exit: + syna_pal_mutex_unlock(&g_fifo_queue_mutex); + return retval; +} +#endif +/** + * syna_cdev_ioctl_check_frame() + * + * Check the queuing status and wait for the data if it's empty. + * + * @param + * [ in] tcm: the driver handle + * [ in/out] ubuf_ptr: points to a memory space from userspace + * [ in] buf_size: size of given space + * [ in] data_size: timeout value for queue waiting + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_cdev_ioctl_check_frame(struct syna_tcm *tcm, + const unsigned char *ubuf_ptr, unsigned int buf_size, + unsigned int data_size) +{ + int retval = 0; + int result = 0; + unsigned int timeout = 0; + unsigned int frames = 0; + unsigned char data[4] = {0}; + + if (!tcm->is_connected) { + LOGE("Not connected\n"); + return _EINVAL; + } + + if (buf_size < sizeof(data) || data_size < sizeof(data)) { + LOGE("Invalid sync data size, buf_size: %u\n", buf_size); + retval = -EINVAL; + goto exit; + } + + result = copy_from_user(data, ubuf_ptr, + sizeof(data)); + if (result) { + LOGE("Fail to copy data from user space\n"); + retval = -EBADE; + goto exit; + } + + /* Parese the waiting duration length */ + timeout = syna_pal_le4_to_uint(&data[0]); + LOGD("Time out: %d\n", timeout); + + if (list_empty(&tcm->frame_fifo_queue)) { + LOGD("The queue is empty, wait for the frames\n"); + result = wait_event_interruptible_timeout(tcm->wait_frame, + (tcm->fifo_remaining_frame > 0), + msecs_to_jiffies(timeout)); + if (result == 0) { + LOGD("Queue waiting timed out after %dms\n", timeout); + retval = -ETIMEDOUT; + goto exit; + } + LOGD("Data queued\n"); + retval = data_size; + } else { + LOGD("Queue is not empty\n"); + retval = data_size; + } + +exit: + if (retval > 0) { + frames = tcm->fifo_remaining_frame; + data[0] = (unsigned char)(frames & 0xff); + data[1] = (unsigned char)((frames >> 8) & 0xff); + data[2] = (unsigned char)((frames >> 16) & 0xff); + data[3] = (unsigned char)((frames >> 24) & 0xff); + result = copy_to_user((void *)ubuf_ptr, + data, sizeof(data)); + if (result) { + LOGE("Fail to copy data to user space\n"); + retval = -EBADE; + } + } + + return retval; +} + +/** + * syna_cdev_clean_queue() + * + * Clean the data queue. + * All data in the queue will be cleaned up in every time of device + * open and close. + * + * @param + * [ in] tcm: the driver handle + * + * @return + * void. + */ +static void syna_cdev_clean_queue(struct syna_tcm *tcm) +{ + struct fifo_queue *pfifo_data; + + syna_pal_mutex_lock(&g_fifo_queue_mutex); + + while (!list_empty(&tcm->frame_fifo_queue)) { + pfifo_data = list_first_entry(&tcm->frame_fifo_queue, + struct fifo_queue, next); + list_del(&pfifo_data->next); + kfree(pfifo_data->fifo_data); + kfree(pfifo_data); + if (tcm->fifo_remaining_frame != 0) + tcm->fifo_remaining_frame--; + } + + LOGD("Queue cleaned, frame: %d\n", tcm->fifo_remaining_frame); + + syna_pal_mutex_unlock(&g_fifo_queue_mutex); +} +/** + * syna_cdev_ioctl_get_frame() + * + * Read the data from the queue and return to userspace if data is + * copied or the specified timeout is expired. + * + * Please be noted that the retried data is formatted as follows. + * [0 ] : status / report code + * [1 : 2 ] : length of data frame + * [3 : N + 3] : N bytes of data payload + * + * @param + * [ in] tcm: the driver handle + * [in/out] ubuf_ptr: points to a memory space from userspace + * [ in] buf_size: size of given space + * [out] frame_size: frame size returned + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_cdev_ioctl_get_frame(struct syna_tcm *tcm, + const unsigned char *ubuf_ptr, unsigned int buf_size, + unsigned int *frame_size) +{ + int retval = 0; + int timeout = 0; + unsigned char timeout_data[4] = {0}; + struct fifo_queue *pfifo_data; + + if (!tcm->is_connected) { + LOGE("Not connected\n"); + return _EINVAL; + } + + if (buf_size < sizeof(timeout_data)) { + LOGE("Invalid sync data size, buf_size:%d\n", buf_size); + retval = -EINVAL; + goto exit; + } + +#if !defined(ENABLE_EXTERNAL_FRAME_PROCESS) + LOGE("ENABLE_EXTERNAL_FRAME_PROCESS is not enabled\n"); + return -EFAULT; +#endif + + retval = copy_from_user(timeout_data, ubuf_ptr, sizeof(timeout_data)); + if (retval) { + LOGE("Fail to copy data from user space, size:%d\n", retval); + retval = -EBADE; + goto exit; + } + + /* get the waiting duration */ + timeout = syna_pal_le4_to_uint(&timeout_data[0]); + LOGD("Wait time: %dms\n", timeout); + + if (list_empty(&tcm->frame_fifo_queue)) { + LOGD("The queue is empty, wait for the frame\n"); + retval = wait_event_interruptible_timeout(tcm->wait_frame, + (tcm->fifo_remaining_frame > 0), + msecs_to_jiffies(timeout)); + if (retval == 0) { + LOGD("Queue waiting timed out after %dms\n", timeout); + retval = -ETIMEDOUT; + *frame_size = 0; + goto exit; + } + LOGD("Data queued\n"); + } + + /* confirm the queue status */ + if (list_empty(&tcm->frame_fifo_queue)) { + LOGD("Is queue empty? The remaining frame = %d\n", + tcm->fifo_remaining_frame); + retval = -ENODATA; + goto exit; + } + + syna_pal_mutex_lock(&g_fifo_queue_mutex); + + pfifo_data = list_first_entry(&tcm->frame_fifo_queue, + struct fifo_queue, next); + + LOGD("Pop data from the queue, data length = %d\n", + pfifo_data->data_length); + + if (buf_size >= pfifo_data->data_length) { + retval = copy_to_user((void *)ubuf_ptr, + pfifo_data->fifo_data, + pfifo_data->data_length); + if (retval) { + LOGE("Fail to copy data to user space, size:%d\n", + retval); + retval = -EBADE; + } + + *frame_size = pfifo_data->data_length; + + } else { + LOGE("No enough space for data copy, buf_size:%d data:%d\n", + buf_size, pfifo_data->data_length); + + retval = -EOVERFLOW; + goto exit; + } + + LOGD("From FIFO: (0x%02x, 0x%02x, 0x%02x, 0x%02x)\n", + pfifo_data->fifo_data[0], pfifo_data->fifo_data[1], + pfifo_data->fifo_data[2], pfifo_data->fifo_data[3]); + + list_del(&pfifo_data->next); + + if (retval >= 0) + retval = pfifo_data->data_length; + + kfree(pfifo_data->fifo_data); + kfree(pfifo_data); + if (tcm->fifo_remaining_frame != 0) + tcm->fifo_remaining_frame--; + + syna_pal_mutex_unlock(&g_fifo_queue_mutex); + +exit: + return retval; +} + +/** + * syna_cdev_ioctl_set_reports() + * + * Assign the report types for queuing. The enabled reports will be queued + * into the FIFO queue. + * + * @param + * [ in] tcm: the driver handle + * [ in] ubuf_ptr: points to a memory space from userspace + * [ in] buf_size: size of given space + * [ in] report_size: report types data size + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_cdev_ioctl_set_reports(struct syna_tcm *tcm, + const unsigned char *ubuf_ptr, unsigned int buf_size, + unsigned int report_size) +{ + int retval = 0; + unsigned char data[REPORT_TYPES] = {0}; + unsigned int reports = 0; + unsigned int report_set = 0; + + if (buf_size < sizeof(data)) { + LOGE("Invalid sync data size, buf_size:%d, expected:%d\n", + buf_size, (unsigned int)sizeof(data)); + return -EINVAL; + } + +#if !defined(ENABLE_EXTERNAL_FRAME_PROCESS) + LOGE("ENABLE_EXTERNAL_FRAME_PROCESS is not enabled\n"); + return -EINVAL; +#endif + + if (report_size == 0) { + LOGE("Invalid written size\n"); + return -EINVAL; + } + + retval = copy_from_user(data, ubuf_ptr, report_size); + if (retval) { + LOGE("Fail to copy data from user space, size:%d\n", retval); + retval = -EBADE; + goto exit; + } + + retval = syna_pal_mem_cpy(tcm->report_to_queue, REPORT_TYPES, + data, sizeof(data), REPORT_TYPES); + for (reports = 0 ; reports < REPORT_TYPES ; reports++) { + if (tcm->report_to_queue[reports] == EFP_ENABLE) { + report_set++; + LOGD("Set report 0x%02x for queue\n", reports); + } + } + + LOGI("Forward %d types of reports to the Queue.\n", report_set); + + retval = report_set; + +exit: + return retval; +} +/** + * syna_cdev_ioctl_send_message() + * + * Send the command/message from userspace. + * + * For updating the g_sysfs_io_polling_interval, it need to be configured + * by syna_cdev_ioctl_enable_irq from userspace. + * + * @param + * [ in] tcm: the driver handle + * [ in/out] ubuf_ptr: points to a memory space from userspace + * [ in] buf_size: size of given space + * [ in/out] msg_size: size of message + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_cdev_ioctl_send_message(struct syna_tcm *tcm, + const unsigned char *ubuf_ptr, unsigned int buf_size, + unsigned int *msg_size) +{ + int retval = 0; + unsigned char *data = NULL; + unsigned char resp_code = 0; + unsigned int payload_length = 0; + unsigned int delay_ms_resp = RESP_IN_POLLING; + struct tcm_buffer resp_data_buf; + + if (!tcm->is_connected) { + LOGE("Not connected\n"); + return -ENXIO; + } + + if (buf_size < SEND_MESSAGE_HEADER_LENGTH) { + LOGE("Invalid sync data size, buf_size:%d\n", buf_size); + return -EINVAL; + } + + if (*msg_size == 0) { + LOGE("Invalid message length, msg size: 0\n"); + return -EINVAL; + } + + syna_tcm_buf_lock(&g_cdev_cbuf); + + retval = syna_tcm_buf_alloc(&g_cdev_cbuf, buf_size); + if (retval < 0) { + LOGE("Fail to allocate memory for g_cdev_cbuf, size: %d\n", + buf_size); + goto exit; + } + + data = g_cdev_cbuf.buf; + + retval = copy_from_user(data, ubuf_ptr, *msg_size); + if (retval) { + LOGE("Fail to copy data from user space, size:%d\n", *msg_size); + retval = -EBADE; + goto exit; + } + + payload_length = syna_pal_le2_to_uint(&data[1]); + LOGD("Command = 0x%02x, payload length = %d\n", + data[0], payload_length); + + /* init a buffer for the response data */ + syna_tcm_buf_init(&resp_data_buf); + + if (g_sysfs_io_polling_interval == RESP_IN_ATTN) + delay_ms_resp = RESP_IN_ATTN; + else + delay_ms_resp = g_sysfs_io_polling_interval; + + retval = syna_tcm_send_command(tcm->tcm_dev, + data[0], + &data[3], + payload_length, + &resp_code, + &resp_data_buf, + delay_ms_resp); + if (retval < 0) { + LOGE("Fail to send command:%d\n", retval); + goto exit; + } + + syna_pal_mem_set(data, 0, buf_size); + /* status code */ + data[0] = resp_code; + /* the length for response data */ + data[1] = (unsigned char)(resp_data_buf.data_length & 0xff); + data[2] = (unsigned char)((resp_data_buf.data_length >> 8) & 0xff); + /* response data */ + if (resp_data_buf.data_length > 0) { + retval = syna_pal_mem_cpy(&g_cdev_cbuf.buf[3], + (g_cdev_cbuf.buf_size - SEND_MESSAGE_HEADER_LENGTH), + resp_data_buf.buf, + resp_data_buf.buf_size, + resp_data_buf.data_length); + if (retval < 0) { + LOGE("Fail to copy resp data\n"); + goto exit; + } + } + +#ifdef ENABLE_EXTERNAL_FRAME_PROCESS + /* It's for queuing the data when user is polling the command + * response for the selected responses. The response will not be + * queued if the user doesn't set the report/response types through + * syna_cdev_ioctl_set_reports. + */ + if (delay_ms_resp != RESP_IN_ATTN) { + if (tcm->report_to_queue[resp_code] == EFP_ENABLE) { + syna_cdev_update_report_queue(tcm, resp_code, + &resp_data_buf); + } + } +#endif + + if (buf_size < resp_data_buf.data_length) { + LOGE("No enough space for data copy, buf_size:%d data:%d\n", + buf_size, resp_data_buf.data_length); + retval = -EOVERFLOW; + goto exit; + } + + retval = copy_to_user((void *)ubuf_ptr, + data, resp_data_buf.data_length); + if (retval) { + LOGE("Fail to copy data to user space\n"); + retval = -EBADE; + goto exit; + } + + *msg_size = resp_data_buf.data_length + 3; + retval = *msg_size; + +exit: + syna_tcm_buf_unlock(&g_cdev_cbuf); + + syna_tcm_buf_release(&resp_data_buf); + + return retval; +} + +/** + * syna_cdev_ioctl_enable_irq() + * + * Enable or disable the irq via IOCTL. + * + * Expect to get 4 bytes unsigned int parameter from userspace: + * 0: disable the irq. + * 1: enable the irq and set g_sysfs_io_polling_interval + * to RESP_IN_ATTN + * otherwise: enable the irq and also assign the polling interval + * to a specific time, which will be used when calling + * syna_cdev_ioctl_send_message. + * the min. polling time is RESP_IN_POLLING + * + * @param + * [ in] tcm: the driver handle + * [ in] ubuf_ptr: points to a memory space from userspace + * [ in] buf_size: size of given space + * [ in] data_size: size of actual data + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_cdev_ioctl_enable_irq(struct syna_tcm *tcm, + const unsigned char *ubuf_ptr, unsigned int buf_size, + unsigned int data_size) +{ + int retval = 0; + unsigned int data; + + if (!tcm->is_connected) { + LOGE("Not connected\n"); + return -ENXIO; + } + + if ((buf_size < sizeof(data)) || (data_size < sizeof(data))) { + LOGE("Invalid sync data size, buf_size:%d, data_size:%d\n", + buf_size, data_size); + return -EINVAL; + } + + if (!tcm->hw_if->ops_enable_irq) { + LOGW("Not support irq control\n"); + return -EFAULT; + } + + retval = copy_from_user(&data, ubuf_ptr, buf_size); + if (retval) { + LOGE("Fail to copy data from user space, size:%d\n", retval); + return -EBADE; + } + + switch (data) { + case SYSFS_DISABLED_INTERRUPT: + retval = tcm->hw_if->ops_enable_irq(tcm->hw_if, false); + if (retval < 0) { + LOGE("Fail to disable interrupt\n"); + return retval; + } + + g_sysfs_io_polling_interval = + tcm->tcm_dev->msg_data.default_resp_reading; + + LOGI("IRQ is disabled by userspace application\n"); + + break; + case SYSFS_ENABLED_INTERRUPT: + retval = tcm->hw_if->ops_enable_irq(tcm->hw_if, true); + if (retval < 0) { + LOGE("Fail to enable interrupt\n"); + return retval; + } + + g_sysfs_io_polling_interval = RESP_IN_ATTN; + + LOGI("IRQ is enabled by userspace application\n"); + + break; + default: + /* recover the interrupt and also assing the polling interval */ + retval = tcm->hw_if->ops_enable_irq(tcm->hw_if, true); + if (retval < 0) { + LOGE("Fail to enable interrupt\n"); + return retval; + } + + g_sysfs_io_polling_interval = data; + if (g_sysfs_io_polling_interval < RESP_IN_POLLING) + g_sysfs_io_polling_interval = RESP_IN_POLLING; + + LOGI("IRQ is enabled by userspace application\n"); + LOGI("Polling interval is set to %d ms\n", + g_sysfs_io_polling_interval); + + break; + } + + return 0; +} +/** + * syna_cdev_ioctl_store_pid() + * + * Save PID through IOCTL interface + * + * @param + * [ in] tcm: the driver handle + * [ in] ubuf_ptr: points to a memory space from userspace + * [ in] buf_size: size of given space + * [ in] data_size: size of actual data + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_cdev_ioctl_store_pid(struct syna_tcm *tcm, + const unsigned char *ubuf_ptr, unsigned int buf_size, + unsigned int data_size) +{ + int retval = 0; + unsigned char *data = NULL; + + if (!tcm->is_connected) { + LOGE("Not connected\n"); + return -ENXIO; + } + + if (buf_size < 4) { + LOGE("Invalid sync data size, buf_size:%d\n", buf_size); + return -EINVAL; + } + + if (data_size < 4) { + LOGE("Invalid data_size\n"); + return -EINVAL; + } + + syna_tcm_buf_lock(&g_cdev_cbuf); + + retval = syna_tcm_buf_alloc(&g_cdev_cbuf, buf_size); + if (retval < 0) { + LOGE("Fail to allocate memory for g_cdev_buf, size: %d\n", + buf_size); + goto exit; + } + + data = g_cdev_cbuf.buf; + + retval = copy_from_user(data, ubuf_ptr, data_size); + if (retval) { + LOGE("Fail to copy data from user space, size:%d\n", retval); + retval = -EBADE; + goto exit; + } + + tcm->proc_pid = syna_pal_le4_to_uint(&data[0]); + + LOGD("PID: %d\n", (unsigned int)tcm->proc_pid); +#ifdef ENABLE_PID_TASK + if (tcm->proc_pid) { + tcm->proc_task = pid_task( + find_vpid(tcm->proc_pid), + PIDTYPE_PID); + if (!tcm->proc_task) { + LOGE("Fail to locate task, pid: %d\n", + (unsigned int)tcm->proc_pid); + retval = -ESRCH; + goto exit; + } + } +#endif +exit: + syna_tcm_buf_unlock(&g_cdev_cbuf); + + return retval; +} +/** + * syna_cdev_ioctl_raw_read() + * + * Read the data from device directly without routing to command wrapper + * interface. + * + * @param + * [ in] tcm: the driver handle + * [in/out] ubuf_ptr: ubuf_ptr: points to a memory space from userspace + * [ in] buf_size: size of given space + * [ in] rd_size: reading size + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_cdev_ioctl_raw_read(struct syna_tcm *tcm, + const unsigned char *ubuf_ptr, unsigned int buf_size, + unsigned int rd_size) +{ + int retval = 0; + unsigned char *data = NULL; + + if (!tcm->is_connected) { + LOGE("Not connected\n"); + return -ENXIO; + } + + if ((buf_size < 0) || (rd_size > buf_size)) { + LOGE("Invalid sync data size, buf_size:%d, rd_size:%d\n", + buf_size, rd_size); + return -EINVAL; + } + + if (rd_size == 0) { + LOGE("The read length is 0\n"); + return 0; + } + + syna_tcm_buf_lock(&g_cdev_cbuf); + + retval = syna_tcm_buf_alloc(&g_cdev_cbuf, rd_size); + if (retval < 0) { + LOGE("Fail to allocate memory for g_cdev_cbuf, size: %d\n", + rd_size); + goto exit; + } + + data = g_cdev_cbuf.buf; + + retval = syna_tcm_read(tcm->tcm_dev, + data, + rd_size); + if (retval < 0) { + LOGE("Fail to read raw data, size: %d\n", rd_size); + goto exit; + } + + if (copy_to_user((void *)ubuf_ptr, data, rd_size)) { + LOGE("Fail to copy data to user space\n"); + retval = -EBADE; + goto exit; + } + + retval = rd_size; + +exit: + syna_tcm_buf_unlock(&g_cdev_cbuf); + + return retval; +} +/** + * syna_cdev_ioctl_raw_write() + * + * Write the given data to device directly without routing to command wrapper + * interface. + * + * @param + * [ in] tcm: the driver handle + * [ in] ubuf_ptr: points to a memory space from userspace + * [ in] buf_size: size of given space + * [ in] wr_size: size to write + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_cdev_ioctl_raw_write(struct syna_tcm *tcm, + const unsigned char *ubuf_ptr, unsigned int buf_size, + unsigned int wr_size) +{ + int retval = 0; + unsigned char *data = NULL; + + if (!tcm->is_connected) { + LOGE("Not connected\n"); + return -ENXIO; + } + + if ((buf_size < 0) || (wr_size > buf_size)) { + LOGE("Invalid sync data size, buf_size:%d, wr_size:%d\n", + buf_size, wr_size); + return -EINVAL; + } + + if (wr_size == 0) { + LOGE("Invalid written size\n"); + return -EINVAL; + } + + syna_tcm_buf_lock(&g_cdev_cbuf); + + retval = syna_tcm_buf_alloc(&g_cdev_cbuf, wr_size); + if (retval < 0) { + LOGE("Fail to allocate memory for g_cdev_cbuf, size: %d\n", + wr_size); + goto exit; + } + + data = g_cdev_cbuf.buf; + + retval = copy_from_user(data, ubuf_ptr, wr_size); + if (retval) { + LOGE("Fail to copy data from user space, size:%d\n", retval); + retval = -EBADE; + goto exit; + } + + LOGD("Write command: 0x%02x, legnth: 0x%02x, 0x%02x (size:%d)\n", + data[0], data[1], data[2], wr_size); + + retval = syna_tcm_write(tcm->tcm_dev, + data, + wr_size); + if (retval < 0) { + LOGE("Fail to write raw data, size: %d\n", wr_size); + goto exit; + } + + retval = wr_size; + +exit: + syna_tcm_buf_unlock(&g_cdev_cbuf); + + return retval; +} + +/** + * syna_cdev_ioctl_dispatch() + * + * Dispatch the IOCTLs operation based on the given code + * + * @param + * [ in] tcm: the driver handle + * [ in] code: code for the target operation + * [ in] ubuf_ptr: points to a memory space from userspace + * [ in] ubuf_size: size of given space + * [ in] wr_size: written data size + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_cdev_ioctl_dispatch(struct syna_tcm *tcm, + unsigned int code, const unsigned char *ubuf_ptr, + unsigned int ubuf_size, unsigned int *data_size) +{ + int retval = 0; + + switch (code) { + case STD_SET_PID_ID: + retval = syna_cdev_ioctl_store_pid(tcm, + ubuf_ptr, ubuf_size, *data_size); + break; + case STD_ENABLE_IRQ_ID: + retval = syna_cdev_ioctl_enable_irq(tcm, + ubuf_ptr, ubuf_size, *data_size); + break; + case STD_RAW_WRITE_ID: + retval = syna_cdev_ioctl_raw_write(tcm, + ubuf_ptr, ubuf_size, *data_size); + break; + case STD_RAW_READ_ID: + retval = syna_cdev_ioctl_raw_read(tcm, + ubuf_ptr, ubuf_size, *data_size); + break; + case STD_GET_FRAME_ID: + retval = syna_cdev_ioctl_get_frame(tcm, + ubuf_ptr, ubuf_size, data_size); + break; + case STD_SEND_MESSAGE_ID: + retval = syna_cdev_ioctl_send_message(tcm, + ubuf_ptr, ubuf_size, data_size); + break; + case STD_SET_REPORTS_ID: + retval = syna_cdev_ioctl_set_reports(tcm, + ubuf_ptr, ubuf_size, *data_size); + break; + case STD_CHECK_FRAMES_ID: + retval = syna_cdev_ioctl_check_frame(tcm, + ubuf_ptr, ubuf_size, *data_size); + break; + case STD_CLEAN_OUT_FRAMES_ID: + LOGD("STD_CLEAN_OUT_FRAMES_ID called\n"); + syna_cdev_clean_queue(tcm); + retval = 0; + break; + default: + LOGE("Unknown ioctl code: 0x%x\n", code); + return -EINVAL; + } + + return retval; +} +/** + * syna_cdev_ioctl_old_dispatch() + * + * Dispatch the old IOCTLs operation based on the given code + * + * @param + * [ in] tcm: the driver handle + * [ in] code: code for the target operation + * [ in] arg: argument passed from user-space + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_cdev_ioctl_old_dispatch(struct syna_tcm *tcm, + unsigned int code, unsigned long arg) +{ + int retval = 0; + + switch (code) { + case OLD_RESET_ID: + retval = syna_tcm_reset(tcm->tcm_dev); + if (retval < 0) { + LOGE("Fail to do reset\n"); + break; + } + + retval = tcm->dev_set_up_app_fw(tcm); + if (retval < 0) { + LOGE("Fail to set up app fw\n"); + break; + } + + break; + case OLD_SET_IRQ_MODE_ID: + if (!tcm->hw_if->ops_enable_irq) { + retval = -EFAULT; + break; + } + + if (arg == 0) + retval = tcm->hw_if->ops_enable_irq(tcm->hw_if, + false); + else if (arg == 1) + retval = tcm->hw_if->ops_enable_irq(tcm->hw_if, + true); + break; + case OLD_SET_RAW_MODE_ID: + if (arg == 0) + tcm->is_attn_redirecting = false; + else if (arg == 1) + tcm->is_attn_redirecting = true; + + break; + case OLD_CONCURRENT_ID: + retval = 0; + break; + + default: + LOGE("Unknown ioctl code: 0x%x\n", code); + retval = -EINVAL; + break; + } + + return retval; +} + +/** + * syna_cdev_ioctls() + * + * Used to implements the IOCTL operations + * + * @param + * [ in] filp: represents the file descriptor + * [ in] cmd: command code sent from userspace + * [ in] arg: arguments sent from userspace + * + * @return + * on success, 0; otherwise, negative value on error. + */ +#ifdef USE_UNLOCKED_IOCTL +static long syna_cdev_ioctls(struct file *filp, unsigned int cmd, + unsigned long arg) +#else +static int syna_cdev_ioctls(struct inode *inp, struct file *filp, + unsigned int cmd, unsigned long arg) +#endif +{ + int retval = 0; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm *tcm; + struct syna_ioctl_data ioc_data; + unsigned char *ptr = NULL; + + p_kobj = g_sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm = dev_get_drvdata(p_dev); + + syna_pal_mutex_lock(&g_extif_mutex); + + retval = 0; + + /* handle the old IOCTLs */ + if ((_IOC_NR(cmd)) < STD_IOCTL_BEGIN) { + retval = syna_cdev_ioctl_old_dispatch(tcm, + (unsigned int)_IOC_NR(cmd), arg); + + goto exit; + } else if ((_IOC_NR(cmd)) == STD_IOCTL_BEGIN) { + retval = 1; + goto exit; + } + + retval = copy_from_user(&ioc_data, + (void __user *) arg, + sizeof(struct syna_ioctl_data)); + if (retval) { + LOGE("Fail to copy ioctl_data from user space, size:%d\n", + retval); + retval = -EBADE; + goto exit; + } + + ptr = ioc_data.buf; + + retval = syna_cdev_ioctl_dispatch(tcm, + (unsigned int)_IOC_NR(cmd), + (const unsigned char *)ptr, + ioc_data.buf_size, + &ioc_data.data_length); + if (retval < 0) + goto exit; + + retval = copy_to_user((void __user *) arg, + &ioc_data, + sizeof(struct syna_ioctl_data)); + if (retval) { + LOGE("Fail to update ioctl_data to user space, size:%d\n", + retval); + retval = -EBADE; + goto exit; + } + +exit: + syna_pal_mutex_unlock(&g_extif_mutex); + + return retval; +} + +#ifdef USE_COMPAT_IOCTL +/** + * syna_cdev_compat_ioctls() + * + * Used to implements the IOCTL compatible operations + * + * @param + * [ in] filp: represents the file descriptor + * [ in] cmd: command code sent from userspace + * [ in] arg: arguments sent from userspace + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static long syna_cdev_compat_ioctls(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int retval = 0; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm *tcm; + struct syna_tcm_ioctl_data_compat ioc_data; + unsigned char *ptr = NULL; + + p_kobj = g_sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm = dev_get_drvdata(p_dev); + + syna_pal_mutex_lock(&g_extif_mutex); + + retval = 0; + + /* handle the old IOCTLs */ + if ((_IOC_NR(cmd)) < STD_IOCTL_BEGIN) { + retval = syna_cdev_ioctl_old_dispatch(tcm, + (unsigned int)_IOC_NR(cmd), arg); + + goto exit; + } else if ((_IOC_NR(cmd)) == STD_IOCTL_BEGIN) { + retval = 1; + goto exit; + } + + retval = copy_from_user(&ioc_data, + (struct syna_tcm_ioctl_data_compat __user *) compat_ptr(arg), + sizeof(struct syna_tcm_ioctl_data_compat)); + if (retval) { + LOGE("Fail to copy ioctl_data from user space, size:%d\n", + retval); + retval = -EBADE; + goto exit; + } + + ptr = compat_ptr((unsigned long)ioc_data.buf); + + retval = syna_cdev_ioctl_dispatch(tcm, + (unsigned int)_IOC_NR(cmd), + (const unsigned char *)ptr, + ioc_data.buf_size, + &ioc_data.data_length); + if (retval < 0) + goto exit; + + retval = copy_to_user(compat_ptr(arg), + &ioc_data, + sizeof(struct syna_tcm_ioctl_data_compat)); + if (retval) { + LOGE("Fail to update ioctl_data to user space, size:%d\n", + retval); + retval = -EBADE; + goto exit; + } + +exit: + syna_pal_mutex_unlock(&g_extif_mutex); + + return retval; +} +#endif + +/** + * syna_cdev_llseek() + * + * Used to change the current position in a file. + * + * @param + * [ in] filp: represents the file descriptor + * [ in] off: the file position + * [ in] whence: flag for seeking + * + * @return + * not support + */ +static loff_t syna_cdev_llseek(struct file *filp, + loff_t off, int whence) +{ + return -EFAULT; +} +/** + * syna_cdev_read() + * + * Used to read data through the device file. + * Function will use raw write approach. + * + * @param + * [ in] filp: represents the file descriptor + * [out] buf: given buffer from userspace + * [ in] count: size of buffer + * [ in] f_pos: the file position + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static ssize_t syna_cdev_read(struct file *filp, + char __user *buf, size_t count, loff_t *f_pos) +{ + int retval = 0; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm *tcm; + + p_kobj = g_sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm = dev_get_drvdata(p_dev); + + if (count == 0) + return 0; + + syna_pal_mutex_lock(&g_extif_mutex); + + retval = syna_cdev_ioctl_raw_read(tcm, + (const unsigned char *)buf, count, count); + if (retval != count) { + LOGE("Invalid read operation, request:%d, return:%d\n", + (unsigned int)count, retval); + } + + syna_pal_mutex_unlock(&g_extif_mutex); + + return retval; +} +/** + * syna_cdev_write() + * + * Used to send data to device through the device file. + * Function will use raw write approach. + * + * @param + * [ in] filp: represents the file descriptor + * [ in] buf: given buffer from userspace + * [ in] count: size of buffer + * [ in] f_pos: the file position + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static ssize_t syna_cdev_write(struct file *filp, + const char __user *buf, size_t count, loff_t *f_pos) +{ + int retval = 0; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm *tcm; + + p_kobj = g_sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm = dev_get_drvdata(p_dev); + + if (count == 0) + return 0; + + syna_pal_mutex_lock(&g_extif_mutex); + + retval = syna_cdev_ioctl_raw_write(tcm, + (const unsigned char *)buf, count, count); + if (retval != count) { + LOGE("Invalid write operation, request:%d, return:%d\n", + (unsigned int)count, retval); + } + + syna_pal_mutex_unlock(&g_extif_mutex); + + return retval; +} +/** + * syna_cdev_open() + * + * Invoked when the device file is being open, which should be + * always the first operation performed on the device file + * + * @param + * [ in] inp: represents a file in rootfs + * [ in] filp: represents the file descriptor + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_cdev_open(struct inode *inp, struct file *filp) +{ + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm *tcm; + + p_kobj = g_sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm = dev_get_drvdata(p_dev); + + syna_pal_mutex_lock(&g_extif_mutex); + + if (tcm->char_dev_ref_count != 0) { + LOGN("cdev already open, %d\n", + tcm->char_dev_ref_count); + return -EBUSY; + } + + tcm->char_dev_ref_count++; + + g_sysfs_io_polling_interval = 0; + +#ifdef ENABLE_EXTERNAL_FRAME_PROCESS + syna_cdev_clean_queue(tcm); +#endif + syna_pal_mutex_unlock(&g_extif_mutex); + + LOGI("cdev open\n"); + + return 0; +} +/** + * syna_cdev_release() + * + * Invoked when the device file is being released + * + * @param + * [ in] inp: represents a file in rootfs + * [ in] filp: represents the file descriptor + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_cdev_release(struct inode *inp, struct file *filp) +{ + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm *tcm; + + p_kobj = g_sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm = dev_get_drvdata(p_dev); + + syna_pal_mutex_lock(&g_extif_mutex); + + if (tcm->char_dev_ref_count <= 0) { + LOGN("cdev already closed, %d\n", + tcm->char_dev_ref_count); + return 0; + } + + tcm->char_dev_ref_count--; + + tcm->is_attn_redirecting = false; + syna_pal_mem_set(tcm->report_to_queue, 0, REPORT_TYPES); +#ifdef ENABLE_EXTERNAL_FRAME_PROCESS + syna_cdev_clean_queue(tcm); +#endif + syna_pal_mutex_unlock(&g_extif_mutex); + + g_sysfs_io_polling_interval = 0; + + LOGI("cdev close\n"); + + return 0; +} + +/** + * Declare the operations of TouchCom device file + */ +static const struct file_operations device_fops = { + .owner = THIS_MODULE, +#ifdef USE_UNLOCKED_IOCTL + .unlocked_ioctl = syna_cdev_ioctls, +#ifdef USE_COMPAT_IOCTL + .compat_ioctl = syna_cdev_compat_ioctls, +#endif +#else + .ioctl = syna_cdev_ioctls, +#endif + .llseek = syna_cdev_llseek, + .read = syna_cdev_read, + .write = syna_cdev_write, + .open = syna_cdev_open, + .release = syna_cdev_release, +}; +/** + * syna_cdev_redirect_attn() + * + * Expose the status of ATTN signal to userspace + * + * @param + * [ in] tcm: the driver handle + * + * @return + * none. + */ +void syna_cdev_redirect_attn(struct syna_tcm *tcm) +{ + if (tcm->proc_pid) + return; +} +#ifdef ENABLE_EXTERNAL_FRAME_PROCESS +/** + * syna_cdev_update_report_queue() + * + * Push the selected data to the queue. + * + * @param + * [ in] tcm: the driver handle + * [ in] code: report type + * [ in] pevent_data: report payload + * + * @return + * none. + */ +void syna_cdev_update_report_queue(struct syna_tcm *tcm, + unsigned char code, struct tcm_buffer *pevent_data) +{ + int retval; + unsigned char *frame_buffer = NULL; + unsigned int frame_length = 0; + + if (pevent_data == NULL) { + LOGE("Returned, invalid event data pointer\n"); + return; + } + frame_length = pevent_data->data_length + 3; + LOGD("The overall queuing data length = %d\n", frame_length); + frame_buffer = (unsigned char *)syna_pal_mem_alloc(frame_length, + sizeof(unsigned char)); + if (!frame_buffer) { + LOGE("Fail to allocate buffer, size: %d, data_length: %d\n", + pevent_data->data_length + 3, pevent_data->data_length); + return; + } + + frame_buffer[0] = code; + frame_buffer[1] = (unsigned char)pevent_data->data_length; + frame_buffer[2] = (unsigned char)(pevent_data->data_length >> 8); + + if (pevent_data->data_length > 0) { + retval = syna_pal_mem_cpy(&frame_buffer[3], + (frame_length - 3), + pevent_data->buf, + pevent_data->data_length, + pevent_data->data_length); + if (retval < 0) { + LOGE("Fail to copy data to buffer, size: %d\n", + pevent_data->data_length); + goto exit; + } + } + retval = syna_cdev_insert_fifo(tcm, frame_buffer, frame_length); + if (retval < 0) { + LOGE("Fail to insert data to fifo\n"); + goto exit; + } + + wake_up_interruptible(&(tcm->wait_frame)); + +exit: + syna_pal_mem_free((void *)frame_buffer); +} +#endif +/** + * syna_cdev_devnode() + * + * Provide the declaration of devtmpfs + * + * @param + * [ in] dev: an instance of device + * [ in] mode: mode of created node + * + * @return + * the string of devtmpfs + */ +static char *syna_cdev_devnode(struct device *dev, umode_t *mode) +{ + if (!mode) + return NULL; + + /* S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH */ + *mode = CHAR_DEVICE_MODE; + + return kasprintf(GFP_KERNEL, "%s", dev_name(dev)); +} +/** + * syna_cdev_create_sysfs() + * + * Create a device node and register it with sysfs. + * + * @param + * [ in] tcm: the driver handle + * [ in] pdev: an instance of platform device + * + * @return + * on success, 0; otherwise, negative value on error. + */ +int syna_cdev_create_sysfs(struct syna_tcm *tcm, + struct platform_device *pdev) +{ + int retval = 0; + struct class *device_class = NULL; + struct device *device = NULL; + static int cdev_major_num; + + tcm->device_class = NULL; + tcm->device = NULL; + + tcm->is_attn_redirecting = false; + + syna_pal_mutex_alloc(&g_extif_mutex); +#ifdef ENABLE_EXTERNAL_FRAME_PROCESS + syna_pal_mutex_alloc(&g_fifo_queue_mutex); +#endif + syna_tcm_buf_init(&g_cdev_cbuf); + + if (cdev_major_num) { + tcm->char_dev_num = MKDEV(cdev_major_num, 0); + retval = register_chrdev_region(tcm->char_dev_num, 1, + PLATFORM_DRIVER_NAME); + if (retval < 0) { + LOGE("Fail to register char device\n"); + goto err_register_chrdev_region; + } + } else { + retval = alloc_chrdev_region(&tcm->char_dev_num, 0, 1, + PLATFORM_DRIVER_NAME); + if (retval < 0) { + LOGE("Fail to allocate char device\n"); + goto err_alloc_chrdev_region; + } + + cdev_major_num = MAJOR(tcm->char_dev_num); + } + + cdev_init(&tcm->char_dev, &device_fops); + tcm->char_dev.owner = THIS_MODULE; + + retval = cdev_add(&tcm->char_dev, tcm->char_dev_num, 1); + if (retval < 0) { + LOGE("Fail to add cdev_add\n"); + goto err_add_chardev; + } + + device_class = class_create(THIS_MODULE, PLATFORM_DRIVER_NAME); + if (IS_ERR(device_class)) { + LOGE("Fail to create device class\n"); + retval = PTR_ERR(device_class); + goto err_create_class; + } + + device_class->devnode = syna_cdev_devnode; + + device = device_create(device_class, NULL, + tcm->char_dev_num, NULL, + CHAR_DEVICE_NAME"%d", MINOR(tcm->char_dev_num)); + if (IS_ERR(tcm->device)) { + LOGE("Fail to create character device\n"); + retval = -ENOENT; + goto err_create_device; + } + + tcm->device_class = device_class; + + tcm->device = device; + + tcm->char_dev_ref_count = 0; + tcm->proc_pid = 0; + +#ifdef ENABLE_EXTERNAL_FRAME_PROCESS + INIT_LIST_HEAD(&tcm->frame_fifo_queue); + init_waitqueue_head(&tcm->wait_frame); +#endif + syna_pal_mem_set(tcm->report_to_queue, 0, REPORT_TYPES); + + retval = syna_sysfs_create_dir(tcm, pdev); + if (retval < 0) { + LOGE("Fail to create sysfs dir\n"); + retval = -ENOTDIR; + goto err_create_dir; + } + + return 0; + +err_create_dir: + device_destroy(device_class, tcm->char_dev_num); +err_create_device: + class_destroy(device_class); +err_create_class: + cdev_del(&tcm->char_dev); +err_add_chardev: + unregister_chrdev_region(tcm->char_dev_num, 1); +err_alloc_chrdev_region: +err_register_chrdev_region: + return retval; +} +/** + * syna_cdev_remove_sysfs() + * + * Remove the allocate cdev device node and release the resource + * + * @param + * [ in] tcm: the driver handle + * + * @return + * none. + */ +void syna_cdev_remove_sysfs(struct syna_tcm *tcm) +{ + if (!tcm) { + LOGE("Invalid tcm driver handle\n"); + return; + } + + syna_sysfs_remove_dir(tcm); + + syna_pal_mem_set(tcm->report_to_queue, 0, REPORT_TYPES); + syna_cdev_clean_queue(tcm); + syna_pal_mutex_free(&g_fifo_queue_mutex); + + tcm->char_dev_ref_count = 0; + tcm->proc_pid = 0; + + if (tcm->device) { + device_destroy(tcm->device_class, tcm->char_dev_num); + class_destroy(tcm->device_class); + cdev_del(&tcm->char_dev); + unregister_chrdev_region(tcm->char_dev_num, 1); + } + + syna_tcm_buf_release(&g_cdev_cbuf); + + syna_pal_mutex_free(&g_extif_mutex); + + tcm->device_class = NULL; + + tcm->device = NULL; +} + + diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_testing.c b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_testing.c new file mode 100644 index 000000000000..1f64a8be8bfb --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_testing.c @@ -0,0 +1,1653 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file syna_tcm2_testing.c + * + * This file implements the sample code to perform chip testing. + */ +#include "syna_tcm2_testing.h" +#include "syna_tcm2_testing_limits_csot.h" +#include "syna_tcm2_testing_limits_visionox.h" +#include "synaptics_touchcom_core_dev.h" +#include "synaptics_touchcom_func_base.h" +#include "syna_tcm2.h" + + +#define SYNA_LOG_SIZE_MAX (PAGE_SIZE * 6) +#define SYNA_TESTING_RESULT_IN_CSV (1) + +/* g_testing_dir represents the root folder of testing sysfs + */ +static struct kobject *g_testing_dir; +static struct syna_tcm *g_tcm_ptr; +static unsigned char testing_config_id_data[MAX_SIZE_CONFIG_ID]; + +static void syna_save2file(char *buf, int size) +{ +#if 1 // output to dmesg, only for debug + int retval = 0; + char *str = NULL; + char *src = NULL; + char c = '\n'; + int str_size = 0; + int offset = 0; + #define STR_MAX_SIZE 512 + char str_buf[STR_MAX_SIZE] = {0}; + + if (!buf || (size == 0)) { + LOGE("Invalid test result data or size\n"); + goto exit; + } + + LOGI("size = %d\n", size); + + offset = 0; + while (offset < size) { + syna_pal_mem_set(str_buf, 0, sizeof(str_buf)); + + // fine next '\n' + src = buf + offset; + str = strchr(src, c); + if (str == NULL) { + // print the last + LOGI("%s", src); + break; + } + + str_size = &str[0] - &src[0] + 1; + retval = syna_pal_mem_cpy(str_buf, STR_MAX_SIZE, &buf[offset], str_size, str_size); + if (retval < 0) { + LOGE("Fail to copy part number string\n"); + goto exit; + } + LOGW("[%3d %4d]%s", str_size, offset, str_buf); + offset += str_size; + } +#endif + +#if SYNA_TESTING_RESULT_IN_CSV + if (!buf || (size == 0)) { + LOGE("Invalid test result data or size\n"); + goto exit; + } + + // save to file + // open file + // write to file + // close file +#endif + +exit: + return; +} + +/** + * syna_testing_log_test_data() + * + * log the test data + * + * @param + * [ in] data: target test data + * [ in] data_size: size of test data + * [ in] test_item: test item + * [ in] rows: the number of rows + * [ in] cols: the number of column + * + * @return + * no return + */ +static void syna_testing_log_test_data(unsigned char *data, + unsigned int data_size, unsigned char test_item, + int rows, int cols) +{ + int i, j, cnt, offset, log_size; + int *abs_raw_ptr = NULL; + short *data_ptr = NULL; + unsigned char *open_short_ptr = NULL; + char *log_ptr = NULL; + struct syna_tcm *tcm = g_tcm_ptr; + + if (!data || (data_size == 0)) { + LOGE("Invalid test data or size\n"); + goto exit; + } + + if (tcm->testing_log_data == NULL) { + LOGE("Do not support log data\n"); + goto exit; + } + + log_ptr = tcm->testing_log_data + tcm->testing_log_size; + log_size = SYNA_LOG_SIZE_MAX - tcm->testing_log_size; + + if ((test_item >= TEST_PID01_TRX_TRX_SHORTS) && (test_item <= TEST_PID03_TRX_GROUND_SHORTS)) { + goto open_short; + } + else if (test_item == TEST_PID18_HYBRID_ABS_RAW) { + goto abs_raw; + } + +//trans_data: + if (data_size != (2 * rows * cols)) { + LOGE("Size mismatched, data:%d (exppected:%d)\n", + data_size, (2 * rows * cols)); + goto exit; + } + data_ptr = (short *)&data[0]; + offset = 0; + cnt = snprintf(log_ptr + offset, log_size - offset, "TEST PT$%02d:\n", test_item); + offset += cnt; + for (i = 0; i < rows; i++) { + for (j = 0; j < cols; j++) { + cnt = snprintf(log_ptr + offset, log_size - offset, "%4d ", *data_ptr); + offset += cnt; + data_ptr++; + } + cnt = snprintf(log_ptr + offset, log_size - offset, "\n"); + offset += cnt; + } + tcm->testing_log_size += offset; + goto exit; + +abs_raw: + if (data_size != (4 * (rows + cols))) { + LOGE("Size mismatched, data:%d (exppected:%d)\n", + data_size, (4 * (rows + cols))); + goto exit; + } + abs_raw_ptr = (int *)&data[0]; + + offset = 0; + cnt = snprintf(log_ptr + offset, log_size - offset, "TEST PT$%02d:\n", test_item); + offset += cnt; + cnt = snprintf(log_ptr + offset, log_size - offset, "abs raw-cols:"); + offset += cnt; + for (i = 0; i < cols; i++) { + cnt = snprintf(log_ptr + offset, log_size - offset, "%5d ", *abs_raw_ptr); + offset += cnt; + abs_raw_ptr++; + } + cnt = snprintf(log_ptr + offset, log_size - offset, "\n"); + offset += cnt; + + cnt = snprintf(log_ptr + offset, log_size - offset, "abs raw-rows:"); + offset += cnt; + for (i = 0; i < rows; i++) { + cnt = snprintf(log_ptr + offset, log_size - offset, "%5d ", *abs_raw_ptr); + offset += cnt; + abs_raw_ptr++; + } + cnt = snprintf(log_ptr + offset, log_size - offset, "\n"); + offset += cnt; + tcm->testing_log_size += offset; + goto exit; + +open_short: + open_short_ptr = (unsigned char *)&data[0]; + offset = 0; + cnt = snprintf(log_ptr + offset, log_size - offset, "TEST PT$%02d:", test_item); + offset += cnt; + for (i = 0; i < data_size; i++) { + cnt = snprintf(log_ptr + offset, log_size - offset, "0x%02x ", *open_short_ptr); + offset += cnt; + open_short_ptr++; + } + cnt = snprintf(log_ptr + offset, log_size - offset, "\n"); + offset += cnt; + tcm->testing_log_size += offset; + goto exit; + +exit: + if (tcm->testing_log_size >= SYNA_LOG_SIZE_MAX) { + LOGE("There is no enough log buf (%d, %ld), please extend the buf size\n", + tcm->testing_log_size, SYNA_LOG_SIZE_MAX); + } + + return; +} + + +/** + * syna_testing_compare_byte_vector() + * + * Sample code to compare the test result with limits + * by byte vector + * + * @param + * [ in] data: target test data + * [ in] data_size: size of test data + * [ in] limit: test limit value to be compared with + * [ in] limit_size: size of test limit + * + * @return + * on success, true; otherwise, return false + */ +static bool syna_testing_compare_byte_vector(unsigned char *data, + unsigned int data_size, const unsigned char *limit, + unsigned int limit_size) +{ + bool result = false; + unsigned char tmp; + unsigned char p, l; + int i, j; + + if (!data || (data_size == 0)) { + LOGE("Invalid test data\n"); + return false; + } + if (!limit || (limit_size == 0)) { + LOGE("Invalid limits\n"); + return false; + } + + if (limit_size < data_size) { + LOGE("Limit size mismatched, data size: %d, limits: %d\n", + data_size, limit_size); + return false; + } + + result = true; + for (i = 0; i < data_size; i++) { + tmp = data[i]; + + for (j = 0; j < 8; j++) { + p = GET_BIT(tmp, j); + l = GET_BIT(limit[i], j); + if (p != l) { + LOGE("Fail on TRX-%03d (data:%X, limit:%X)\n", + (i*8 + j), p, l); + result = false; + } + } + } + + return result; +} + +/** + * syna_testing_compare_frame() + * + * Sample code to compare the test result with limits + * by a lower-bound frame + * + * @param + * [ in] data: target test data + * [ in] data_size: size of test data + * [ in] rows: the number of rows + * [ in] cols: the number of column + * [ in] limits_hi: upper-bound test limit + * [ in] limits_lo: lower-bound test limit + * + * @return + * on success, true; otherwise, return false + */ +static bool syna_testing_compare_frame(unsigned char *data, + unsigned int data_size, int rows, int cols, + const short *limits_hi, const short *limits_lo) +{ + bool result = false; + short *data_ptr = NULL; + short limit; + int i, j; + + if (!data || (data_size == 0)) { + LOGE("Invalid test data\n"); + return false; + } + + if (data_size < (2 * rows * cols)) { + LOGE("Size mismatched, data:%d (exppected:%d)\n", + data_size, (2 * rows * cols)); + result = false; + return false; + } + + if(limit_panel) { + if (rows > LIMIT_BOUNDARY_V) { + LOGE("Rows mismatched, rows:%d (exppected:%d)\n", + rows, LIMIT_BOUNDARY_V); + result = false; + return false; + } + if (cols > LIMIT_BOUNDARY_V) { + LOGE("Columns mismatched, cols: %d (exppected:%d)\n", + cols, LIMIT_BOUNDARY_V); + result = false; + return false; + } + + } else { + if (rows > LIMIT_BOUNDARY_C) { + LOGE("Rows mismatched, rows:%d (exppected:%d)\n", + rows, LIMIT_BOUNDARY_C); + result = false; + return false; + } + + if (cols > LIMIT_BOUNDARY_C) { + LOGE("Columns mismatched, cols: %d (exppected:%d)\n", + cols, LIMIT_BOUNDARY_C); + result = false; + return false; + } + } + result = true; + + if (!limits_hi) + goto end_of_upper_bound_limit; + + data_ptr = (short *)&data[0]; + + if(limit_panel) { + for (i = 0; i < rows; i++) { + for (j = 0; j < cols; j++) { + limit = limits_hi[i * LIMIT_BOUNDARY_V + j]; + if (*data_ptr > limit) { + LOGE("Fail on (%2d,%2d)=%5d, limits_hi:%4d\n", + i, j, *data_ptr, limit); + result = false; + } + data_ptr++; + } + } + } else { + for (i = 0; i < rows; i++) { + for (j = 0; j < cols; j++) { + limit = limits_hi[i * LIMIT_BOUNDARY_C + j]; + if (*data_ptr > limit) { + LOGE("Fail on (%2d,%2d)=%5d, limits_hi:%4d\n", + i, j, *data_ptr, limit); + result = false; + } + data_ptr++; + } + } + } + +end_of_upper_bound_limit: + + if (!limits_lo) + goto end_of_lower_bound_limit; + + data_ptr = (short *)&data[0]; + + if(limit_panel){ + for (i = 0; i < rows; i++) { + for (j = 0; j < cols; j++) { + limit = limits_lo[i * LIMIT_BOUNDARY_V + j]; + if (*data_ptr < limit) { + LOGE("Fail on (%2d,%2d)=%5d, limits_lo:%4d\n", + i, j, *data_ptr, limit); + result = false; + } + data_ptr++; + } + } + }else { + for (i = 0; i < rows; i++) { + for (j = 0; j < cols; j++) { + limit = limits_lo[i * LIMIT_BOUNDARY_C + j]; + if (*data_ptr < limit) { + LOGE("Fail on (%2d,%2d)=%5d, limits_lo:%4d\n", + i, j, *data_ptr, limit); + result = false; + } + data_ptr++; + } + } + } + +end_of_lower_bound_limit: + return result; +} + +/** + * syna_testing_device_id() + * + * Sample code to ensure the device id is expected + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_testing_device_id(struct syna_tcm *tcm) +{ + int retval; + bool result; + struct tcm_identification_info info; + char *strptr = NULL; + + LOGI("Start testing\n"); + + retval = syna_tcm_identify(tcm->tcm_dev, &info); + if (retval < 0) { + LOGE("Fail to get identification\n"); + result = false; + goto exit; + } + + if(limit_panel){ + strptr = strnstr(info.part_number, + device_id_limit_v, + strlen(info.part_number)); + if (strptr != NULL) + result = true; + else { + LOGE("Device ID mismatched, FW: %s (limit: %s)\n", + info.part_number, device_id_limit_v); + result = false; + } + }else{ + strptr = strnstr(info.part_number, + device_id_limit_c, + strlen(info.part_number)); + if (strptr != NULL) + result = true; + else { + LOGE("Device ID mismatched, FW: %s (limit: %s)\n", + info.part_number, device_id_limit_c); + result = false; + } + } + + +exit: + LOGI("Result = %s\n", (result)?"pass":"fail"); + + return ((result) ? 0 : -1); +} + +/** + * syna_testing_config_id() + * + * Sample code to ensure the config id is expected + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_testing_config_id(struct syna_tcm *tcm) +{ + int retval; + bool result; + struct tcm_application_info info; + int idx; + + LOGI("Start testing\n"); + + syna_pal_mem_set(testing_config_id_data, 0, sizeof(testing_config_id_data)); + + retval = syna_tcm_get_app_info(tcm->tcm_dev, &info); + if (retval < 0) { + LOGE("Fail to get app info\n"); + result = false; + goto exit; + } + + result = true; + + if(limit_panel){ + for (idx = 0; idx < sizeof(config_id_limit_v); idx++) { + if (config_id_limit_v[idx] != info.customer_config_id[idx]) { + LOGE("Fail on byte.%d (data: %02X, limit: %02X)\n", + idx, info.customer_config_id[idx], + config_id_limit_v[idx]); + result = false; + } + } + }else{ + for (idx = 0; idx < sizeof(config_id_limit_c); idx++) { + if (config_id_limit_c[idx] != info.customer_config_id[idx]) { + LOGE("Fail on byte.%d (data: %02X, limit: %02X)\n", + idx, info.customer_config_id[idx], + config_id_limit_c[idx]); + result = false; + } + } + } + + + /* backup config id */ + for (idx = 0; idx < MAX_SIZE_CONFIG_ID; idx++) { + testing_config_id_data[idx] = info.customer_config_id[idx]; + } + +exit: + LOGI("Result = %s\n", (result)?"pass":"fail"); + + return ((result) ? 0 : -1); +} + +/** + * syna_testing_check_id_show() + * + * Attribute to show the result of ID comparsion to the console. + * + * @param + * [ in] kobj: an instance of kobj + * [ in] attr: an instance of kobj attribute structure + * [out] buf: string buffer shown on console + * + * @return + * on success, number of characters being output; + * otherwise, negative value on error. + */ +static ssize_t syna_testing_check_id_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int retval; + unsigned int count = 0; + struct syna_tcm *tcm = g_tcm_ptr; + + if (!tcm->is_connected) { + retval = snprintf(buf, PAGE_SIZE, + "Device is NOT connected\n"); + goto exit; + } + + count = 0; + + retval = syna_testing_device_id(tcm); + + retval = snprintf(buf, PAGE_SIZE - count, + "Device ID check: %s\n", + (retval < 0) ? "fail" : "pass"); + + buf += retval; + count += retval; + + retval = syna_testing_config_id(tcm); + + retval = snprintf(buf, PAGE_SIZE - count, + "Config ID check: %s\n", + (retval < 0) ? "fail" : "pass"); + + buf += retval; + count += retval; + + retval = count; +exit: + return retval; +} + +static struct kobj_attribute kobj_attr_check_id = + __ATTR(check_id, 0444, syna_testing_check_id_show, NULL); + +/** + * syna_testing_pt01() + * + * Sample code to perform PT01 testing + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_testing_pt01(struct syna_tcm *tcm) +{ + int retval; + bool result = false; + struct tcm_buffer test_data; + + syna_tcm_buf_init(&test_data); + + LOGI("Start testing\n"); + + retval = syna_tcm_run_production_test(tcm->tcm_dev, + TEST_PID01_TRX_TRX_SHORTS, + &test_data); + if (retval < 0) { + LOGE("Fail to run test %d\n", TEST_PID01_TRX_TRX_SHORTS); + result = false; + goto exit; + } + + if(limit_panel) { + result = syna_testing_compare_byte_vector(test_data.buf, + test_data.data_length, + pt01_limits_v, + ARRAY_SIZE(pt01_limits_v)); + }else { + result = syna_testing_compare_byte_vector(test_data.buf, + test_data.data_length, + pt01_limits_c, + ARRAY_SIZE(pt01_limits_c)); + } + + syna_testing_log_test_data(test_data.buf, + test_data.data_length, + TEST_PID01_TRX_TRX_SHORTS, + tcm->tcm_dev->rows, + tcm->tcm_dev->cols); + +exit: + LOGI("Result = %s\n", (result)?"pass":"fail"); + + syna_tcm_buf_release(&test_data); + + return ((result) ? 0 : -1); +} + +/** + * syna_testing_pt01_show() + * + * Attribute to show the result of PT01 test to the console. + * + * @param + * [ in] kobj: an instance of kobj + * [ in] attr: an instance of kobj attribute structure + * [out] buf: string buffer shown on console + * + * @return + * on success, number of characters being output; + * otherwise, negative value on error. + */ +static ssize_t syna_testing_pt01_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int retval; + unsigned int count = 0; + struct syna_tcm *tcm = g_tcm_ptr; + + if (!tcm->is_connected) { + count = snprintf(buf, PAGE_SIZE, + "Device is NOT connected\n"); + goto exit; + } + + retval = syna_testing_pt01(tcm); + + count = snprintf(buf, PAGE_SIZE, + "TEST PT$01: %s\n", + (retval < 0) ? "fail" : "pass"); + +exit: + return count; +} + +static struct kobj_attribute kobj_attr_pt01 = + __ATTR(pt01, 0444, syna_testing_pt01_show, NULL); + +/** + * syna_testing_pt02() + * + * Sample code to perform PT02 testing + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_testing_pt02(struct syna_tcm *tcm) +{ + int retval; + bool result = false; + struct tcm_buffer test_data; + + syna_tcm_buf_init(&test_data); + + LOGI("Start testing\n"); + + retval = syna_tcm_run_production_test(tcm->tcm_dev, + TEST_PID02_TRX_SENSOR_OPENS, + &test_data); + if (retval < 0) { + LOGE("Fail to run test %d\n", TEST_PID02_TRX_SENSOR_OPENS); + result = false; + goto exit; + } + + if(limit_panel){ + result = syna_testing_compare_byte_vector(test_data.buf, + test_data.data_length, + pt02_limits_v, + ARRAY_SIZE(pt02_limits_v)); + + }else{ + result = syna_testing_compare_byte_vector(test_data.buf, + test_data.data_length, + pt02_limits_c, + ARRAY_SIZE(pt02_limits_c)); + } + + syna_testing_log_test_data(test_data.buf, + test_data.data_length, + TEST_PID02_TRX_SENSOR_OPENS, + tcm->tcm_dev->rows, + tcm->tcm_dev->cols); + +exit: + LOGI("Result = %s\n", (result)?"pass":"fail"); + + syna_tcm_buf_release(&test_data); + + return ((result) ? 0 : -1); +} + +/** + * syna_testing_pt02_show() + * + * Attribute to show the result of PT02 test to the console. + * + * @param + * [ in] kobj: an instance of kobj + * [ in] attr: an instance of kobj attribute structure + * [out] buf: string buffer shown on console + * + * @return + * on success, number of characters being output; + * otherwise, negative value on error. + */ +static ssize_t syna_testing_pt02_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int retval; + unsigned int count = 0; + struct syna_tcm *tcm = g_tcm_ptr; + + if (!tcm->is_connected) { + count = snprintf(buf, PAGE_SIZE, + "Device is NOT connected\n"); + goto exit; + } + + retval = syna_testing_pt02(tcm); + + count = snprintf(buf, PAGE_SIZE, + "TEST PT$02: %s\n", + (retval < 0) ? "fail" : "pass"); + +exit: + return count; +} + +static struct kobj_attribute kobj_attr_pt02 = + __ATTR(pt02, 0444, syna_testing_pt02_show, NULL); + +/** + * syna_testing_pt03() + * + * Sample code to perform PT03 testing + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_testing_pt03(struct syna_tcm *tcm) +{ + int retval; + bool result = false; + struct tcm_buffer test_data; + + syna_tcm_buf_init(&test_data); + + LOGI("Start testing\n"); + + retval = syna_tcm_run_production_test(tcm->tcm_dev, + TEST_PID03_TRX_GROUND_SHORTS, + &test_data); + if (retval < 0) { + LOGE("Fail to run test %d\n", TEST_PID03_TRX_GROUND_SHORTS); + result = false; + goto exit; + } + + if(limit_panel) { + result = syna_testing_compare_byte_vector(test_data.buf, + test_data.data_length, + pt03_limits_v, + ARRAY_SIZE(pt03_limits_v)); + } else { + result = syna_testing_compare_byte_vector(test_data.buf, + test_data.data_length, + pt03_limits_c, + ARRAY_SIZE(pt03_limits_c)); + } + + syna_testing_log_test_data(test_data.buf, + test_data.data_length, + TEST_PID03_TRX_GROUND_SHORTS, + tcm->tcm_dev->rows, + tcm->tcm_dev->cols); + +exit: + LOGI("Result = %s\n", (result)?"pass":"fail"); + + syna_tcm_buf_release(&test_data); + + return ((result) ? 0 : -1); +} + +/** + * syna_testing_pt03_show() + * + * Attribute to show the result of PT03 test to the console. + * + * @param + * [ in] kobj: an instance of kobj + * [ in] attr: an instance of kobj attribute structure + * [out] buf: string buffer shown on console + * + * @return + * on success, number of characters being output; + * otherwise, negative value on error. + */ +static ssize_t syna_testing_pt03_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int retval; + unsigned int count = 0; + struct syna_tcm *tcm = g_tcm_ptr; + + if (!tcm->is_connected) { + count = snprintf(buf, PAGE_SIZE, + "Device is NOT connected\n"); + goto exit; + } + + retval = syna_testing_pt03(tcm); + + count = snprintf(buf, PAGE_SIZE, + "TEST PT$03: %s\n", + (retval < 0) ? "fail" : "pass"); + +exit: + return count; +} + +static struct kobj_attribute kobj_attr_pt03 = + __ATTR(pt03, 0444, syna_testing_pt03_show, NULL); + +/** + * syna_testing_pt05() + * + * Sample code to perform PT05 testing + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_testing_pt05(struct syna_tcm *tcm) +{ + int retval; + bool result = false; + struct tcm_buffer test_data; + + syna_tcm_buf_init(&test_data); + + LOGI("Start testing\n"); + + retval = syna_tcm_run_production_test(tcm->tcm_dev, + TEST_PID05_FULL_RAW_CAP, + &test_data); + if (retval < 0) { + LOGE("Fail to run test %d\n", TEST_PID05_FULL_RAW_CAP); + result = false; + goto exit; + } + + if (limit_panel) { + result = syna_testing_compare_frame(test_data.buf, + test_data.data_length, + tcm->tcm_dev->rows, + tcm->tcm_dev->cols, + (const short *)&pt05_hi_limits_v[0], + (const short *)&pt05_lo_limits_v[0]); + } else { + result = syna_testing_compare_frame(test_data.buf, + test_data.data_length, + tcm->tcm_dev->rows, + tcm->tcm_dev->cols, + (const short *)&pt05_hi_limits_c[0], + (const short *)&pt05_lo_limits_c[0]); + } + + syna_testing_log_test_data(test_data.buf, + test_data.data_length, + TEST_PID05_FULL_RAW_CAP, + tcm->tcm_dev->rows, + tcm->tcm_dev->cols); + +exit: + LOGI("Result = %s\n", (result)?"pass":"fail"); + + syna_tcm_buf_release(&test_data); + + return ((result) ? 0 : -1); +} + +/** + * syna_testing_pt05_show() + * + * Attribute to show the result of PT05 test to the console. + * + * @param + * [ in] kobj: an instance of kobj + * [ in] attr: an instance of kobj attribute structure + * [out] buf: string buffer shown on console + * + * @return + * on success, number of characters being output; + * otherwise, negative value on error. + */ +static ssize_t syna_testing_pt05_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int retval; + unsigned int count = 0; + struct syna_tcm *tcm = g_tcm_ptr; + + if (!tcm->is_connected) { + count = snprintf(buf, PAGE_SIZE, + "Device is NOT connected\n"); + goto exit; + } + + retval = syna_testing_pt05(tcm); + + count = snprintf(buf, PAGE_SIZE, + "TEST PT$05: %s\n", (retval < 0) ? "fail" : "pass"); + +exit: + return count; +} + +static struct kobj_attribute kobj_attr_pt05 = + __ATTR(pt05, 0444, syna_testing_pt05_show, NULL); + +/** + * syna_testing_pt0a() + * + * Sample code to perform PT0A testing + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_testing_pt0a(struct syna_tcm *tcm) +{ + int retval; + bool result = false; + struct tcm_buffer test_data; + + syna_tcm_buf_init(&test_data); + + LOGI("Start testing\n"); + + retval = syna_tcm_run_production_test(tcm->tcm_dev, + TEST_PID10_DELTA_NOISE, + &test_data); + if (retval < 0) { + LOGE("Fail to run test %d\n", TEST_PID10_DELTA_NOISE); + result = false; + goto exit; + } + + if (limit_panel) { + result = syna_testing_compare_frame(test_data.buf, + test_data.data_length, + tcm->tcm_dev->rows, + tcm->tcm_dev->cols, + (const short *)&pt0a_hi_limits_v[0], + (const short *)&pt0a_lo_limits_v[0]); + + } else { + result = syna_testing_compare_frame(test_data.buf, + test_data.data_length, + tcm->tcm_dev->rows, + tcm->tcm_dev->cols, + (const short *)&pt0a_hi_limits_c[0], + (const short *)&pt0a_lo_limits_c[0]); + } + +syna_testing_log_test_data(test_data.buf, + test_data.data_length, + TEST_PID10_DELTA_NOISE, + tcm->tcm_dev->rows, + tcm->tcm_dev->cols); + +exit: + LOGI("Result = %s\n", (result)?"pass":"fail"); + + syna_tcm_buf_release(&test_data); + + return ((result) ? 0 : -1); +} + +/** + * syna_testing_pt0a_show() + * + * Attribute to show the result of PT0A test to the console. + * + * @param + * [ in] kobj: an instance of kobj + * [ in] attr: an instance of kobj attribute structure + * [out] buf: string buffer shown on console + * + * @return + * on success, number of characters being output; + * otherwise, negative value on error. + */ +static ssize_t syna_testing_pt0a_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int retval; + unsigned int count = 0; + struct syna_tcm *tcm = g_tcm_ptr; + + if (!tcm->is_connected) { + count = snprintf(buf, PAGE_SIZE, + "Device is NOT connected\n"); + goto exit; + } + + retval = syna_testing_pt0a(tcm); + + count = snprintf(buf, PAGE_SIZE, + "TEST PT$0A: %s\n", (retval < 0) ? "fail" : "pass"); + +exit: + return count; +} + +static struct kobj_attribute kobj_attr_pt0a = + __ATTR(pt0a, 0444, syna_testing_pt0a_show, NULL); + +/** + * syna_testing_pt10() + * + * Sample code to perform PT10 testing + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_testing_pt10(struct syna_tcm *tcm) +{ + int retval; + bool result = false; + struct tcm_buffer test_data; + + syna_tcm_buf_init(&test_data); + + LOGI("Start testing\n"); + + retval = syna_tcm_run_production_test(tcm->tcm_dev, + TEST_PID16_SENSOR_SPEED, + &test_data); + if (retval < 0) { + LOGE("Fail to run test %d\n", TEST_PID16_SENSOR_SPEED); + result = false; + goto exit; + } + + if(limit_panel){ + result = syna_testing_compare_frame(test_data.buf, + test_data.data_length, + tcm->tcm_dev->rows, + tcm->tcm_dev->cols, + (const short *)&pt10_hi_limits_v[0], + (const short *)&pt10_lo_limits_v[0]); + }else { + result = syna_testing_compare_frame(test_data.buf, + test_data.data_length, + tcm->tcm_dev->rows, + tcm->tcm_dev->cols, + (const short *)&pt10_hi_limits_c[0], + (const short *)&pt10_lo_limits_c[0]); + } + +exit: + LOGI("Result = %s\n", (result)?"pass":"fail"); + + syna_tcm_buf_release(&test_data); + + return ((result) ? 0 : -1); +} + +/** + * syna_testing_pt10_show() + * + * Attribute to show the result of PT10 test to the console. + * + * @param + * [ in] kobj: an instance of kobj + * [ in] attr: an instance of kobj attribute structure + * [out] buf: string buffer shown on console + * + * @return + * on success, number of characters being output; + * otherwise, negative value on error. + */ +static ssize_t syna_testing_pt10_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int retval; + unsigned int count = 0; + struct syna_tcm *tcm = g_tcm_ptr; + + if (!tcm->is_connected) { + count = snprintf(buf, PAGE_SIZE, + "Device is NOT connected\n"); + goto exit; + } + + retval = syna_testing_pt10(tcm); + + count = snprintf(buf, PAGE_SIZE, + "TEST PT$10: %s\n", (retval < 0) ? "fail" : "pass"); + +exit: + return count; +} + +static struct kobj_attribute kobj_attr_pt10 = + __ATTR(pt10, 0444, syna_testing_pt10_show, NULL); + +/** + * syna_testing_pt11() + * + * Sample code to perform PT11 testing + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_testing_pt11(struct syna_tcm *tcm) +{ + int retval; + bool result = false; + struct tcm_buffer test_data; + + syna_tcm_buf_init(&test_data); + + LOGI("Start testing\n"); + + retval = syna_tcm_run_production_test(tcm->tcm_dev, + TEST_PID17_ADC_RANGE, + &test_data); + if (retval < 0) { + LOGE("Fail to run test %d\n", TEST_PID17_ADC_RANGE); + result = false; + goto exit; + } + + if(limit_panel){ + result = syna_testing_compare_frame(test_data.buf, + test_data.data_length, + tcm->tcm_dev->rows, + tcm->tcm_dev->cols, + (const short *)&pt11_hi_limits_v[0], + (const short *)&pt11_lo_limits_v[0]); + }else{ + result = syna_testing_compare_frame(test_data.buf, + test_data.data_length, + tcm->tcm_dev->rows, + tcm->tcm_dev->cols, + (const short *)&pt11_hi_limits_c[0], + (const short *)&pt11_lo_limits_c[0]); + } + +exit: + LOGI("Result = %s\n", (result)?"pass":"fail"); + + syna_tcm_buf_release(&test_data); + + return ((result) ? 0 : -1); +} + +/** + * syna_testing_pt11_show() + * + * Attribute to show the result of PT11 test to the console. + * + * @param + * [ in] kobj: an instance of kobj + * [ in] attr: an instance of kobj attribute structure + * [out] buf: string buffer shown on console + * + * @return + * on success, number of characters being output; + * otherwise, negative value on error. + */ +static ssize_t syna_testing_pt11_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int retval; + unsigned int count = 0; + struct syna_tcm *tcm = g_tcm_ptr; + + if (!tcm->is_connected) { + count = snprintf(buf, PAGE_SIZE, + "Device is NOT connected\n"); + goto exit; + } + + retval = syna_testing_pt11(tcm); + + count = snprintf(buf, PAGE_SIZE, + "TEST PT$11: %s\n", (retval < 0) ? "fail" : "pass"); + +exit: + return count; +} + +static struct kobj_attribute kobj_attr_pt11 = + __ATTR(pt11, 0444, syna_testing_pt11_show, NULL); + +/** + * syna_testing_pt12() + * + * Sample code to perform PT12 testing + * + * @param + * [ in] tcm: the driver handle + * + * @return + * on success, 0; otherwise, negative value on error. + */ +static int syna_testing_pt12(struct syna_tcm *tcm) +{ + int retval; + bool result = false; + struct tcm_buffer test_data; + int *data_ptr = NULL; + int rows = tcm->tcm_dev->rows; + int cols = tcm->tcm_dev->cols; + int i; + + syna_tcm_buf_init(&test_data); + + LOGI("Start testing\n"); + + retval = syna_tcm_run_production_test(tcm->tcm_dev, + TEST_PID18_HYBRID_ABS_RAW, + &test_data); + if (retval < 0) { + LOGE("Fail to run test %d\n", TEST_PID18_HYBRID_ABS_RAW); + result = false; + goto exit; + } + + if(limit_panel){ + if ((LIMIT_BOUNDARY_V < rows) || (LIMIT_BOUNDARY_V < cols)) { + LOGE("Size mismatched, rows:%d cols:%d\n", + rows, cols); + result = false; + goto exit; + } + }else{ + if ((LIMIT_BOUNDARY_C < rows) || (LIMIT_BOUNDARY_C < cols)) { + LOGE("Size mismatched, rows:%d cols:%d\n", + rows, cols); + result = false; + goto exit; + } + } + + if (test_data.data_length < 4 * (rows + cols)) { + LOGE("Data mismatched, size:%d (expected:%d)\n", + test_data.data_length, 4 * (rows + cols)); + result = false; + goto exit; + } + + result = true; + data_ptr = (int *)&test_data.buf[0]; + + if(limit_panel){ + for (i = 0; i < cols; i++) { + if (*data_ptr > pt12_limits_v[i]) { + LOGE("Fail on Col-%2d=%5d, limits:%4d\n", + i, *data_ptr, pt12_limits_v[i]); + + result = false; + } + data_ptr++; + } + }else { + for (i = 0; i < cols; i++) { + if (*data_ptr > pt12_limits_c[i]) { + LOGE("Fail on Col-%2d=%5d, limits:%4d\n", + i, *data_ptr, pt12_limits_c[i]); + + result = false; + } + data_ptr++; + } + } + + if(limit_panel){ + for (i = 0; i < rows; i++) { + if (*data_ptr > pt12_limits_v[LIMIT_BOUNDARY_V + i]) { + LOGE("Fail on Row-%2d=%5d, limits:%4d\n", + i, *data_ptr, + pt12_limits_v[LIMIT_BOUNDARY_V + i]); + + result = false; + } + data_ptr++; + } + }else{ + for (i = 0; i < rows; i++) { + if (*data_ptr > pt12_limits_c[LIMIT_BOUNDARY_C + i]) { + LOGE("Fail on Row-%2d=%5d, limits:%4d\n", + i, *data_ptr, + pt12_limits_c[LIMIT_BOUNDARY_C + i]); + + result = false; + } + data_ptr++; + } + } + + syna_testing_log_test_data(test_data.buf, + test_data.data_length, + TEST_PID18_HYBRID_ABS_RAW, + tcm->tcm_dev->rows, + tcm->tcm_dev->cols); + +exit: + LOGI("Result = %s\n", (result)?"pass":"fail"); + + syna_tcm_buf_release(&test_data); + + return ((result) ? 0 : -1); +} + +/** + * syna_testing_pt12_show() + * + * Attribute to show the result of PT12 test to the console. + * + * @param + * [ in] kobj: an instance of kobj + * [ in] attr: an instance of kobj attribute structure + * [out] buf: string buffer shown on console + * + * @return + * on success, number of characters being output; + * otherwise, negative value on error. + */ +static ssize_t syna_testing_pt12_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int retval; + unsigned int count = 0; + struct syna_tcm *tcm = g_tcm_ptr; + + if (!tcm->is_connected) { + count = snprintf(buf, PAGE_SIZE, + "Device is NOT connected\n"); + goto exit; + } + + retval = syna_testing_pt12(tcm); + + count = snprintf(buf, PAGE_SIZE, + "TEST PT$12: %s\n", (retval < 0) ? "fail" : "pass"); + +exit: + return count; +} + +static struct kobj_attribute kobj_attr_pt12 = + __ATTR(pt12, 0444, syna_testing_pt12_show, NULL); + +/** + * syna_testing_get_rawdata_show() + * + * Attribute to show the result of get_rawdata test to the console. + * + * @param + * [ in] kobj: an instance of kobj + * [ in] attr: an instance of kobj attribute structure + * [out] buf: string buffer shown on console + * + * @return + * on success, number of characters being output; + * otherwise, negative value on error. + */ +static ssize_t syna_testing_get_rawdata_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int retval, offset, idx; + unsigned int count = 0; + unsigned int tmp = 0; + struct syna_tcm *tcm = g_tcm_ptr; + + mutex_lock(&tcm->testing_mutex); + + if (!tcm->is_connected) { + count = snprintf(buf, PAGE_SIZE, + "Device is NOT connected\n"); + goto exit; + } + +#if SYNA_TESTING_RESULT_IN_CSV + tcm->testing_log_data = vmalloc(SYNA_LOG_SIZE_MAX);//kzalloc(SAVE_BUF_SIZE, GFP_KERNEL); + if (!tcm->testing_log_data) { + LOGE("Failed to allocate memory for testing_log_data\n"); + retval = -ENOMEM; + goto exit; + } + tcm->testing_log_size = 0; +#endif + + // config ID test + retval = syna_testing_config_id(tcm); + if (tcm->testing_log_data) { + /* save config id to log */ + offset = 0; + tmp = snprintf(tcm->testing_log_data + offset, SYNA_LOG_SIZE_MAX - offset, + "TEST Config ID:\n"); + offset += tmp; + for (idx = 0; idx < MAX_SIZE_CONFIG_ID; idx++) { + tmp = snprintf(tcm->testing_log_data + offset, SYNA_LOG_SIZE_MAX - offset, + "%02x ", testing_config_id_data[idx]); + offset += tmp; + } + tmp = snprintf(tcm->testing_log_data + offset, SYNA_LOG_SIZE_MAX - offset, "\n"); + offset += tmp; + + tcm->testing_log_size = offset; + } + + // output result to buf + offset = 0; + tmp = snprintf(buf + offset, PAGE_SIZE - offset, + "Config ID Test: %s\n", (retval < 0) ? "fail" : "pass"); + offset += tmp; + + // TRX short Test (TEST_PID01_TRX_TRX_SHORTS) + retval = syna_testing_pt01(tcm); + tmp = snprintf(buf + offset, PAGE_SIZE - offset, + "TEST PT$01: %s\n", (retval < 0) ? "fail" : "pass"); + offset += tmp; + + // Trx gnd test TEST_PID03_TRX_GROUND_SHORTS + retval = syna_testing_pt03(tcm); + tmp = snprintf(buf + offset, PAGE_SIZE - offset, + "TEST PT$03: %s\n", (retval < 0) ? "fail" : "pass"); + offset += tmp; + + // Full raw cap TEST_PID05_FULL_RAW_CAP + retval = syna_testing_pt05(tcm); + tmp = snprintf(buf + offset, PAGE_SIZE - offset, + "TEST PT$05: %s\n", (retval < 0) ? "fail" : "pass"); + offset += tmp; + + // noise test TEST_PID10_DELTA_NOISE + retval = syna_testing_pt0a(tcm); + tmp = snprintf(buf + offset, PAGE_SIZE - offset, + "TEST PT$0a: %s\n", (retval < 0) ? "fail" : "pass"); + offset += tmp; + +#if 0 + // abs raw TEST_PID18_HYBRID_ABS_RAW + retval = syna_testing_pt12(tcm); + tmp = snprintf(buf + offset, PAGE_SIZE - offset, + "TEST PT$12: %s\n", (retval < 0) ? "fail" : "pass"); + offset += tmp; +#endif + count = offset; + +#if SYNA_TESTING_RESULT_IN_CSV + tmp = snprintf(tcm->testing_log_data + tcm->testing_log_size, + SYNA_LOG_SIZE_MAX - tcm->testing_log_size, "%s", buf); + tcm->testing_log_size += tmp; + + if (tcm->testing_log_size >= SYNA_LOG_SIZE_MAX) { + LOGE("There is no enough log buf (%u, %lu), please extend the buf size\n", + tcm->testing_log_size, SYNA_LOG_SIZE_MAX); + } + syna_save2file(tcm->testing_log_data, tcm->testing_log_size); +#endif + +exit: +#if SYNA_TESTING_RESULT_IN_CSV + if(tcm->testing_log_data) { + vfree(tcm->testing_log_data); + tcm->testing_log_data = NULL; + } +#endif + + mutex_unlock(&tcm->testing_mutex); + + return count; +} + +static struct kobj_attribute kobj_attr_get_rawdata = + __ATTR(get_rawdata, 0444, syna_testing_get_rawdata_show, NULL); + +/* + * declaration of sysfs attributes + */ +static struct attribute *attrs[] = { + &kobj_attr_check_id.attr, + &kobj_attr_pt01.attr, + &kobj_attr_pt02.attr, + &kobj_attr_pt03.attr, + &kobj_attr_pt05.attr, + &kobj_attr_pt0a.attr, + &kobj_attr_pt10.attr, + &kobj_attr_pt11.attr, + &kobj_attr_pt12.attr, + &kobj_attr_get_rawdata.attr, + NULL, +}; + +static struct attribute_group attr_testing_group = { + .attrs = attrs, +}; + +/** + * syna_testing_create_dir() + * + * Create a directory and register it with sysfs. + * Then, create all defined sysfs files. + * + * @param + * [ in] tcm: the driver handle + * [ in] sysfs_dir: root directory of sysfs nodes + * + * @return + * on success, 0; otherwise, negative value on error. + */ +int syna_testing_create_dir(struct syna_tcm *tcm, + struct kobject *sysfs_dir) +{ + int retval = 0; + + g_testing_dir = kobject_create_and_add("testing", + sysfs_dir); + if (!g_testing_dir) { + LOGE("Fail to create testing directory\n"); + return -EINVAL; + } + + retval = sysfs_create_group(g_testing_dir, &attr_testing_group); + if (retval < 0) { + LOGE("Fail to create sysfs group\n"); + + kobject_put(g_testing_dir); + return retval; + } + +syna_pal_mutex_alloc(&tcm->testing_mutex); + + g_tcm_ptr = tcm; + + return 0; +} +/** + *syna_testing_remove_dir() + * + * Remove the allocate sysfs directory + * + * @param + * none + * + * @return + * on success, 0; otherwise, negative value on error. + */ +void syna_testing_remove_dir(void) +{ + struct syna_tcm *tcm = g_tcm_ptr; + if (g_testing_dir) { + syna_pal_mutex_free(&tcm->testing_mutex); + if(tcm->testing_log_data) { + vfree(tcm->testing_log_data); + tcm->testing_log_data = NULL; + } + + sysfs_remove_group(g_testing_dir, &attr_testing_group); + + kobject_put(g_testing_dir); + } +} diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_testing.h b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_testing.h new file mode 100644 index 000000000000..bbc7254a24ab --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_testing.h @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. 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 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. + */ + +#ifndef _SYNAPTICS_TCM2_TESTING_H_ +#define _SYNAPTICS_TCM2_TESTING_H_ + +#include "syna_tcm2.h" + +/** + * syna_testing_create_dir() + * + * Create a directory and register it with sysfs. + * Then, create all defined sysfs files. + * + * @param + * [ in] tcm: the driver handle + * [ in] sysfs_dir: root directory of sysfs nodes + * + * @return + * on success, 0; otherwise, negative value on error. + */ +int syna_testing_create_dir(struct syna_tcm *tcm, + struct kobject *sysfs_dir); +/** + *syna_testing_remove_dir() + * + * Remove the allocate sysfs directory + * + * @param + * none + * + * @return + * on success, 0; otherwise, negative value on error. + */ +void syna_testing_remove_dir(void); + + +#endif /* end of _SYNAPTICS_TCM2_TESTING_H_ */ diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_testing_limits_csot.h b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_testing_limits_csot.h new file mode 100644 index 000000000000..76dff42344d9 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_testing_limits_csot.h @@ -0,0 +1,439 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. 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 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. + */ + + +#ifndef _SYNAPTICS_TCM2_TESTING_LIMITS_H_C_ +#define _SYNAPTICS_TCM2_TESTING_LIMITS_H_C_ + +#define LIMIT_BOUNDARY_C (40) + +/* test limit for the device id checking */ +static const char *device_id_limit_c = "3908"; + +/* test limit for the config id checking */ +static const unsigned char config_id_limit_c[16] = { + 0x30, 0x30, 0x30, 0x33, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 +}; + +/** + * @section test limit for PT01 testing + */ +static const unsigned char pt01_limits_c[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/** + * @section test limit for PT02 testing + */ +static const unsigned char pt02_limits_c[16] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/** + * @section test limit for PT03 testing + */ +static const unsigned char pt03_limits_c[16] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f +}; + +/** + * @section test limit for the PT05 testing + */ +static const short pt05_hi_limits_c[LIMIT_BOUNDARY_C * LIMIT_BOUNDARY_C] = { +/* 00 01 02 03 04 05 06 07 08 09 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 */ +/* 00 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 01 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 02 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 03 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 04 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 05 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 06 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 07 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 08 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 09 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 10 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 11 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 12 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 13 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 14 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 15 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 16 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 17 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 18 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 19 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 20 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 21 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 22 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 23 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 24 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 25 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 26 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 27 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 28 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 29 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 30 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 31 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 32 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 33 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 34 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 35 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 36 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 37 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 38 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 39 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +}; + +static const short pt05_lo_limits_c[LIMIT_BOUNDARY_C * LIMIT_BOUNDARY_C] = { +/* 00 01 02 03 04 05 06 07 08 09 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 */ +/* 00 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 01 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 02 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 03 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 04 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 05 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 06 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 07 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 08 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 09 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 10 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 11 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 12 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 13 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 14 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 15 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 16 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 17 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 18 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 19 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 20 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 21 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 22 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 23 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 24 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 25 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 26 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 27 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 28 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 29 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 30 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 31 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 32 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 33 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 34 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 35 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 36 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 37 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 38 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 39 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +}; + +/** + * @section test limit for the PT0A testing + */ +static const short pt0a_hi_limits_c[LIMIT_BOUNDARY_C * LIMIT_BOUNDARY_C] = { +/* 00 01 02 03 04 05 06 07 08 09 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 */ +/* 00 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 01 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 02 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 03 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 04 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 05 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 06 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 07 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 08 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 09 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 10 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 11 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 12 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 13 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 14 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 15 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 16 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 17 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 18 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 19 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 20 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 21 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 22 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 23 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 24 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 25 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 26 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 27 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 28 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 29 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 30 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 31 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 32 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 33 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 34 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 35 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 36 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 37 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 38 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 39 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +}; + +static const short pt0a_lo_limits_c[LIMIT_BOUNDARY_C * LIMIT_BOUNDARY_C] = { +/* 00 01 02 03 04 05 06 07 08 09 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 */ +/* 00 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 01 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 02 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 03 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 04 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 05 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 06 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 07 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 08 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 09 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 10 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 11 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 12 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 13 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 14 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 15 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 16 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 17 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 18 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 19 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 20 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 21 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 22 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 23 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 24 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 25 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 26 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 27 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 28 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 29 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 30 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 31 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 32 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 33 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 34 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 35 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 36 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 37 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 38 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 39 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +}; + +/** + * @section test limit for the PT10 testing + */ +static const short pt10_hi_limits_c[LIMIT_BOUNDARY_C * LIMIT_BOUNDARY_C] = { +/* 00 01 02 03 04 05 06 07 08 09 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 */ +/* 00 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 01 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 02 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 03 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 04 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 05 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 06 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 07 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 08 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 09 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 10 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 11 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 12 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 13 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 14 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 15 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 16 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 17 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 18 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 19 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 20 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 21 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 22 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 23 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 24 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 25 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 26 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 27 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 28 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 29 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 30 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 31 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 32 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 33 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 34 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 35 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 36 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 37 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 38 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 39 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +}; + +static const short pt10_lo_limits_c[LIMIT_BOUNDARY_C * LIMIT_BOUNDARY_C] = { +/* 00 01 02 03 04 05 06 07 08 09 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 */ +/* 00 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 01 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 02 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 03 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 04 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 05 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 06 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 07 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 08 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 09 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 10 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 11 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 12 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 13 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 14 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 15 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 16 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 17 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 18 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 19 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 20 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 21 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 22 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 23 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 24 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 25 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 26 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 27 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 28 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 29 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 30 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 31 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 32 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 33 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 34 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 35 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 36 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 37 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 38 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 39 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +}; + +/** + * @section test limit for the PT11 testing + */ +static const short pt11_hi_limits_c[LIMIT_BOUNDARY_C * LIMIT_BOUNDARY_C] = { +/* 00 01 02 03 04 05 06 07 08 09 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 */ +/* 00 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 01 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 02 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 03 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 04 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 05 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 06 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 07 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 08 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 09 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 10 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 11 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 12 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 13 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 14 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 15 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 16 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 17 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 18 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 19 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 20 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 21 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 22 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 23 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 24 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 25 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 26 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 27 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 28 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 29 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 30 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 31 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 32 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 33 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 34 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 35 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 36 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 37 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 38 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 39 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +}; + +static const short pt11_lo_limits_c[LIMIT_BOUNDARY_C * LIMIT_BOUNDARY_C] = { +/* 00 01 02 03 04 05 06 07 08 09 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 */ +/* 00 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 01 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 02 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 03 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 04 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 05 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 06 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 07 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 08 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 09 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 10 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 11 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 12 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 13 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 14 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 15 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 16 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 17 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 18 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 19 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 20 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 21 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 22 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 23 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 24 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 25 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 26 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 27 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 28 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 29 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 30 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 31 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 32 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 33 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 34 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 35 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 36 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 37 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 38 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 39 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +}; + +/** + * @section test limit for the PT12 + */ +static const int pt12_limits_c[LIMIT_BOUNDARY_C * 2] = { +/* 00 01 02 03 04 05 06 07 08 09 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 */ +/* Cols (RX) */ 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, +/* Rows (TX) */ 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, +}; +#endif /* end of _SYNAPTICS_TCM2_TESTING_LIMITS_H_ */ diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_testing_limits_visionox.h b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_testing_limits_visionox.h new file mode 100644 index 000000000000..60bb11476be1 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_tcm2_testing_limits_visionox.h @@ -0,0 +1,438 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. 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 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. + */ + +#ifndef _SYNAPTICS_TCM2_TESTING_LIMITS_H_V_ +#define _SYNAPTICS_TCM2_TESTING_LIMITS_H_V_ + +#define LIMIT_BOUNDARY_V (40) + +/* test limit for the device id checking */ +static const char *device_id_limit_v= "3908"; + +/* test limit for the config id checking */ +static const unsigned char config_id_limit_v[16] = { + 0x31, 0x30, 0x30, 0x44, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 +}; + +/** + * @section test limit for PT01 testing + */ +static const unsigned char pt01_limits_v[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/** + * @section test limit for PT02 testing + */ +static const unsigned char pt02_limits_v[16] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/** + * @section test limit for PT03 testing + */ +static const unsigned char pt03_limits_v[16] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f +}; + +/** + * @section test limit for the PT05 testing + */ +static const short pt05_hi_limits_v[LIMIT_BOUNDARY_V * LIMIT_BOUNDARY_V] = { +/* 00 01 02 03 04 05 06 07 08 09 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 */ +/* 00 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 01 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 02 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 03 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 04 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 05 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 06 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 07 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 08 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 09 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 10 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 11 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 12 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 13 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 14 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 15 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 16 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 17 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 18 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 19 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 20 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 21 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 22 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 23 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 24 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 25 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 26 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 27 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 28 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 29 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 30 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 31 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 32 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 33 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 34 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 35 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 36 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 37 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 38 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +/* 39 */ 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, +}; + +static const short pt05_lo_limits_v[LIMIT_BOUNDARY_V * LIMIT_BOUNDARY_V] = { +/* 00 01 02 03 04 05 06 07 08 09 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 */ +/* 00 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 01 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 02 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 03 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 04 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 05 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 06 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 07 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 08 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 09 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 10 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 11 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 12 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 13 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 14 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 15 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 16 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 17 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 18 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 19 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 20 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 21 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 22 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 23 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 24 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 25 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 26 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 27 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 28 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 29 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 30 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 31 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 32 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 33 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 34 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 35 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 36 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 37 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 38 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +/* 39 */ 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, +}; + +/** + * @section test limit for the PT0A testing + */ +static const short pt0a_hi_limits_v[LIMIT_BOUNDARY_V * LIMIT_BOUNDARY_V] = { +/* 00 01 02 03 04 05 06 07 08 09 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 */ +/* 00 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 01 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 02 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 03 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 04 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 05 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 06 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 07 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 08 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 09 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 10 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 11 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 12 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 13 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 14 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 15 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 16 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 17 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 18 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 19 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 20 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 21 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 22 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 23 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 24 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 25 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 26 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 27 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 28 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 29 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 30 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 31 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 32 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 33 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 34 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 35 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 36 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 37 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 38 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +/* 39 */ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, +}; + +static const short pt0a_lo_limits_v[LIMIT_BOUNDARY_V * LIMIT_BOUNDARY_V] = { +/* 00 01 02 03 04 05 06 07 08 09 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 */ +/* 00 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 01 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 02 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 03 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 04 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 05 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 06 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 07 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 08 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 09 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 10 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 11 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 12 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 13 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 14 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 15 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 16 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 17 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 18 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 19 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 20 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 21 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 22 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 23 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 24 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 25 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 26 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 27 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 28 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 29 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 30 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 31 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 32 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 33 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 34 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 35 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 36 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 37 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 38 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 39 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +}; + +/** + * @section test limit for the PT10 testing + */ +static const short pt10_hi_limits_v[LIMIT_BOUNDARY_V * LIMIT_BOUNDARY_V] = { +/* 00 01 02 03 04 05 06 07 08 09 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 */ +/* 00 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 01 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 02 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 03 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 04 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 05 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 06 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 07 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 08 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 09 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 10 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 11 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 12 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 13 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 14 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 15 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 16 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 17 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 18 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 19 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 20 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 21 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 22 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 23 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 24 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 25 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 26 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 27 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 28 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 29 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 30 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 31 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 32 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 33 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 34 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 35 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 36 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 37 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 38 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +/* 39 */ 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, +}; + +static const short pt10_lo_limits_v[LIMIT_BOUNDARY_V * LIMIT_BOUNDARY_V] = { +/* 00 01 02 03 04 05 06 07 08 09 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 */ +/* 00 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 01 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 02 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 03 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 04 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 05 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 06 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 07 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 08 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 09 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 10 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 11 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 12 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 13 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 14 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 15 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 16 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 17 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 18 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 19 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 20 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 21 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 22 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 23 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 24 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 25 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 26 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 27 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 28 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 29 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 30 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 31 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 32 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 33 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 34 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 35 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 36 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 37 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 38 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +/* 39 */ -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, -20, +}; + +/** + * @section test limit for the PT11 testing + */ +static const short pt11_hi_limits_v[LIMIT_BOUNDARY_V * LIMIT_BOUNDARY_V] = { +/* 00 01 02 03 04 05 06 07 08 09 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 */ +/* 00 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 01 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 02 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 03 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 04 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 05 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 06 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 07 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 08 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 09 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 10 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 11 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 12 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 13 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 14 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 15 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 16 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 17 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 18 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 19 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 20 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 21 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 22 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 23 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 24 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 25 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 26 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 27 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 28 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 29 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 30 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 31 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 32 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 33 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 34 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 35 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 36 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 37 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 38 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +/* 39 */ 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, 230, +}; + +static const short pt11_lo_limits_v[LIMIT_BOUNDARY_V * LIMIT_BOUNDARY_V] = { +/* 00 01 02 03 04 05 06 07 08 09 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 */ +/* 00 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 01 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 02 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 03 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 04 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 05 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 06 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 07 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 08 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 09 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 10 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 11 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 12 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 13 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 14 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 15 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 16 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 17 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 18 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 19 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 20 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 21 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 22 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 23 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 24 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 25 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 26 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 27 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 28 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 29 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 30 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 31 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 32 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 33 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 34 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 35 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 36 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 37 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 38 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +/* 39 */ 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, +}; + +/** + * @section test limit for the PT12 + */ +static const int pt12_limits_v[LIMIT_BOUNDARY_V * 2] = { +/* 00 01 02 03 04 05 06 07 08 09 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 */ +/* Cols (RX) */ 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, +/* Rows (TX) */ 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, 500000, +}; +#endif /* end of _SYNAPTICS_TCM2_TESTING_LIMITS_H_ */ diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_ts_mmi.c b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_ts_mmi.c new file mode 100644 index 000000000000..8aae78bf9ade --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/syna_ts_mmi.c @@ -0,0 +1,542 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * 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. + */ +#include +#include +#include +#include "syna_tcm2_runtime.h" +#include "syna_tcm2_platform.h" +#include "syna_tcm2.h" +#include "synaptics_touchcom_func_base.h" +#include "synaptics_touchcom_func_reflash.h" + +static int syna_ts_mmi_methods_get_vendor(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", "synaptics"); +} + +static int syna_ts_mmi_methods_get_productinfo(struct device *dev, void *cdata) +{ + struct syna_tcm *tcm; + struct platform_device *pdev = dev_get_drvdata(dev); + + if(!dev){ + LOGE("dev is null"); + } + if(!dev->p){ + LOGE("dev->p is null"); + } + if (!pdev) { + LOGE("Failed to get platform device"); + return -ENODEV; + } + tcm = platform_get_drvdata(pdev); + if (!tcm) { + LOGE("Failed to get driver data"); + return -ENODEV; + } + return scnprintf(TO_CHARP(cdata), 6, "%s", tcm->tcm_dev->id_info.part_number); +} + +static int syna_ts_mmi_methods_get_config_id(struct device *dev, void *cdata) +{ + struct syna_tcm *tcm; + struct platform_device *pdev = dev_get_drvdata(dev); + unsigned int buildid; + if (!pdev) { + LOGE("Failed to get platform device"); + return -ENODEV; + } + tcm = platform_get_drvdata(pdev); + if (!tcm) { + LOGE("Failed to get driver data"); + return -ENODEV; + } + buildid = tcm->tcm_dev->packrat_number; + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%d", buildid); +} + +/*return firmware version*/ +static int syna_ts_mmi_methods_get_build_id(struct device *dev, void *cdata) +{ + struct syna_tcm *tcm; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + LOGE("Failed to get platform device"); + return -ENODEV; + } + tcm = platform_get_drvdata(pdev); + if (!tcm) { + LOGE("Failed to get driver data"); + return -ENODEV; + } + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%s", tcm->tcm_dev->app_info.customer_config_id); +} + +static int syna_ts_mmi_methods_get_bus_type(struct device *dev, void *idata) +{ + const struct syna_hw_interface *hw_if; + struct syna_tcm *tcm; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + LOGE("Failed to get platform device"); + return -ENODEV; + } + tcm = platform_get_drvdata(pdev); + if (!tcm) { + LOGE("Failed to get driver data"); + return -ENODEV; + } + hw_if = tcm->hw_if; + if (!hw_if) { + LOGE("Hardware interface not found\n"); + return -ENODEV; + } + TO_INT(idata) = hw_if->bdata_io.type; + return 0; +} + +static int syna_ts_mmi_methods_get_irq_status(struct device *dev, void *idata) +{ + struct syna_hw_interface *hw_if; + struct syna_hw_attn_data *attn; + struct syna_tcm *tcm; + struct platform_device *pdev = dev_get_drvdata(dev); + if (!pdev) { + LOGE("Failed to get platform device"); + } + tcm = platform_get_drvdata(pdev); + if (!tcm) { + LOGE("Failed to get driver data"); + return -ENODEV; + } + hw_if = tcm->hw_if; + if (!hw_if) { + LOGE("Hardware interface not found\n"); + return -ENODEV; + } + attn = &hw_if->bdata_attn; + if (!attn) { + LOGE("Failed to get ts attn"); + return -ENODEV; + } + TO_INT(idata) = gpio_get_value(attn->irq_gpio); + return 0; +} + +static int syna_ts_mmi_methods_get_drv_irq(struct device *dev, void *idata) +{ + struct syna_hw_attn_data *attn; + struct syna_tcm *tcm; + struct platform_device *pdev = dev_get_drvdata(dev); + if (!pdev) { + LOGE("Failed to get platform device"); + return -ENODEV; + } + tcm = platform_get_drvdata(pdev); + if (!tcm) { + LOGE("Failed to get driver data"); + return -ENODEV; + } + attn = &tcm->hw_if->bdata_attn; + if (!attn) { + LOGE("Failed to get ts attn"); + return -ENODEV; + } + TO_INT(idata) = attn->irq_enabled ? 1 : 0; + return 0; +} + +static int syna_ts_mmi_methods_get_poweron(struct device *dev, void *idata) +{ + struct syna_tcm *tcm; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + LOGE("Failed to get platform device"); + return -ENODEV; + } + tcm = platform_get_drvdata(pdev); + if (!tcm) { + LOGE("Failed to get driver data"); + return -ENODEV; + } + TO_INT(idata) = tcm->pwr_state ? 1 : 0; + return 0; +} + +static int syna_ts_mmi_methods_get_flashprog(struct device *dev, void *idata) +{ + struct syna_tcm *tcm; + struct platform_device *pdev = dev_get_drvdata(dev); + int app_status; + + if (!pdev) { + LOGE("Failed to get platform device"); + return -ENODEV; + } + tcm = platform_get_drvdata(pdev); + if (!tcm) { + LOGE("Failed to get driver data"); + return -ENODEV; + } + app_status = syna_pal_le2_to_uint(tcm->tcm_dev->app_info.status); + if (IS_NOT_FW_MODE(tcm->tcm_dev->id_info.mode) || + app_status != APP_STATUS_OK) + TO_INT(idata) = 1; + else + TO_INT(idata) = 0; + + return 0; +} + +static int syna_ts_mmi_methods_drv_irq(struct device *dev, int state) +{ + int retval; + struct syna_tcm *tcm; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + LOGE("Failed to get platform device"); + return -ENODEV; + } + tcm = platform_get_drvdata(pdev); + if (!tcm) { + LOGE("Failed to get driver data"); + return -ENODEV; + } + mutex_lock(&tcm->tp_event_mutex); + if (state == 0) { + retval = tcm->hw_if->ops_enable_irq(tcm->hw_if, false); + goto exit; + } else if (state == 1) { + retval = tcm->hw_if->ops_enable_irq(tcm->hw_if, true); + if (retval < 0) { + LOGE("Failed to enable interrupt\n"); + goto exit; + } + } else { + retval = -EINVAL; + goto exit; + } + +exit: + mutex_unlock(&tcm->tp_event_mutex); + return retval; +} + +static int syna_ts_mmi_methods_reset(struct device *dev, int type) +{ + struct syna_tcm *tcm; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + LOGE("Failed to get platform device"); + return -ENODEV; + } + tcm = platform_get_drvdata(pdev); + if (!tcm) { + LOGE("Failed to get driver data"); + return -ENODEV; + } + if (tcm->hw_if->ops_hw_reset) + { + tcm->hw_if->ops_hw_reset(tcm->hw_if); + return 0; + } + return -EFAULT; +} + +static int syna_ts_firmware_update(struct device *dev, char *fwname) { + struct syna_tcm *tcm; + struct platform_device *pdev = dev_get_drvdata(dev); + int retval; + + if (!pdev) { + LOGE("Failed to get platform device"); + return -ENODEV; + } + tcm = platform_get_drvdata(pdev); + if (!tcm) { + LOGE("Failed to get driver data"); + return -ENODEV; + } + LOGI("HW request update fw"); + + retval = syna_set_fw_name(tcm, fwname); + if (retval < 0) + { + LOGE("Failed to set fw_name"); + return -EFAULT; + } + retval = syna_reflash_do_reflash(tcm, fwname); + if (retval < 0) + { + LOGE("Failed to do reflash"); + return -EFAULT; + } + return 0; +} + +static int syna_ts_mmi_methods_power(struct device *dev, int on) +{ + int retval; + struct syna_tcm *tcm; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + LOGE("Failed to get platform device"); + return -ENODEV; + } + tcm = platform_get_drvdata(pdev); + if (!tcm) { + LOGE("Failed to get driver data"); + return -ENODEV; + } + if (!on) { + retval = 0; + goto disable_pwr_reg; + } + + if (tcm->hw_if->bdata_pwr.vdd_reg_dev) { + retval = regulator_enable(tcm->hw_if->bdata_pwr.vdd_reg_dev); + if (retval < 0) { + LOGE("Fail to enable vdd regulator\n"); + goto exit; + } + } + + if (tcm->hw_if->bdata_pwr.avdd_reg_dev) { + retval = regulator_enable(tcm->hw_if->bdata_pwr.avdd_reg_dev); + if (retval < 0) { + LOGE("Fail to enable avdd regulator\n"); + goto disable_bus_reg; + } + syna_pal_sleep_ms(tcm->hw_if->bdata_pwr.power_on_delay_ms); + } + return 0; + +disable_pwr_reg: + if (tcm->hw_if->bdata_pwr.avdd_reg_dev) + retval = regulator_enable(tcm->hw_if->bdata_pwr.avdd_reg_dev); +disable_bus_reg: + if (tcm->hw_if->bdata_pwr.vdd_reg_dev) + retval = regulator_enable(tcm->hw_if->bdata_pwr.vdd_reg_dev); +exit: + return retval; +} + +static int syna_ts_mmi_pre_resume(struct device *dev) +{ + int retval; + struct syna_tcm *tcm; + struct platform_device *pdev = dev_get_drvdata(dev); + + if (!pdev) { + LOGE("Failed to get platform device"); + return -ENODEV; + } + tcm = platform_get_drvdata(pdev); + if (!tcm) { + return -ENODEV; + } + if (!tcm->pwr_state) + { + return 0; + } + retval = syna_tcm_sleep(tcm->tcm_dev, false); + if (retval < 0) { + LOGE("Failed to exit deep sleep\n"); + goto exit; + } + + tcm->dev_resume(&tcm->pdev->dev); + tcm->pwr_state = true; + return 0; + +exit: + tcm->pwr_state = false; + return retval; +} + +static int syna_ts_mmi_post_suspend(struct device *dev) +{ + struct syna_tcm *tcm; + struct platform_device *pdev = dev_get_drvdata(dev); + + LOGI("post_suspend enter\n"); + + if (!pdev) { + LOGE("Failed to get platform device"); + return -ENODEV; + } + tcm = platform_get_drvdata(pdev); + if (!tcm) { + LOGE("Failed to get driver data"); + return -ENODEV; + } + if (!tcm->pwr_state) + { + return 0; + } + + LOGI("post_suspend exit\n"); + + return 0; +} + +static int syna_ts_mmi_pre_suspend(struct device *dev) +{ + struct syna_tcm *tcm; + struct platform_device *pdev = dev_get_drvdata(dev); + + LOGI("pre_suspend enter\n"); + + if (!pdev) { + LOGE("Failed to get platform device"); + return -ENODEV; + } + tcm = platform_get_drvdata(pdev); + if (!tcm) { + LOGE("Failed to get driver data"); + return -ENODEV; + } + if (!tcm->pwr_state) + return 0; + + LOGI("pre_suspend exit\n"); + + return 0; +} + +static int syna_ts_mmi_panel_state(struct device *dev, + enum ts_mmi_pm_mode from, enum ts_mmi_pm_mode to) +{ + struct syna_tcm *tcm; + struct platform_device *pdev = dev_get_drvdata(dev); + int retval = 0; + if (!pdev) { + LOGE("Failed to get platform device"); + return -ENODEV; + } + tcm = platform_get_drvdata(pdev); + if (!tcm) { + LOGE("Failed to get driver data"); + return -ENODEV; + } + switch (to) { + case TS_MMI_PM_DEEPSLEEP: + tcm->lpwg_enabled = false; + retval = syna_dev_early_suspend(&tcm->pdev->dev); + if (retval < 0) { + LOGE("early suspend fail"); + } + tcm->dev_suspend(&tcm->pdev->dev); + break; + case TS_MMI_PM_GESTURE: + tcm->lpwg_enabled = true; + tcm->dev_suspend(&tcm->pdev->dev); + break; + case TS_MMI_PM_ACTIVE: + break; + default: + LOGI("panel mode %d is invalid.\n",to); + return -EINVAL; + } + return 0; +}; +static int syna_ts_mmi_charger_mode(struct device *dev, int mode) +{ + struct syna_tcm *tcm; + struct platform_device *pdev = dev_get_drvdata(dev); + unsigned short cval = 0; + int retval = 0; + struct syna_hw_attn_data *attn; + + if (!pdev) { + LOGE("Failed to get platform device"); + return -ENODEV; + } + + tcm = platform_get_drvdata(pdev); + if (!tcm) { + LOGE("Failed to get driver data"); + return -ENODEV; + } + attn = &tcm->hw_if->bdata_attn; + if(attn->irq_enabled == false) { + LOGI("Interrupt is closed, so cannot access CHARGER_CONNECTED\n"); + return -EINVAL; + } + + retval = syna_tcm_get_dynamic_config(tcm->tcm_dev,DC_ENABLE_CHARGER_CONNECTED,&cval,RESP_IN_ATTN); + if(retval < 0) { + LOGE("Failed to get charger_connected mode\n"); + goto exit; + } + if(cval != mode){ + retval = syna_tcm_set_dynamic_config(tcm->tcm_dev,DC_ENABLE_CHARGER_CONNECTED,mode,RESP_IN_ATTN); + if (retval < 0) { + LOGE("Failed to set charger_connected mode\n"); + goto exit; + } + LOGI("%s: charger mode success %d\n",__func__,cval); + } else { + LOGI("%s: charger mode already %d\n",__func__,cval); + } +exit: + + return 0; +}; + + +static struct ts_mmi_methods syna_ts_mmi_methods = { + .get_vendor = syna_ts_mmi_methods_get_vendor, + .get_productinfo = syna_ts_mmi_methods_get_productinfo, + .get_build_id = syna_ts_mmi_methods_get_build_id, + .get_config_id = syna_ts_mmi_methods_get_config_id, + .get_bus_type = syna_ts_mmi_methods_get_bus_type, + .get_irq_status = syna_ts_mmi_methods_get_irq_status, + .get_drv_irq = syna_ts_mmi_methods_get_drv_irq, + .get_flashprog = syna_ts_mmi_methods_get_flashprog, + .get_poweron = syna_ts_mmi_methods_get_poweron, + /* SET methods */ + .reset = syna_ts_mmi_methods_reset, + .drv_irq = syna_ts_mmi_methods_drv_irq, + .power = syna_ts_mmi_methods_power, + .charger_mode = syna_ts_mmi_charger_mode, + /* Firmware */ + .firmware_update = syna_ts_firmware_update, + /* PM callback */ + .panel_state = syna_ts_mmi_panel_state, + .pre_resume = syna_ts_mmi_pre_resume, + .pre_suspend = syna_ts_mmi_pre_suspend, + .post_suspend = syna_ts_mmi_post_suspend, +}; + +int syna_ts_mmi_dev_register(struct syna_tcm *tcm) { + int ret; + ret = ts_mmi_dev_register(tcm->pdev->dev.parent, &syna_ts_mmi_methods); + if (ret) { + LOGE("Failed to register ts mmi ret = %d\n", ret); + return ret; + } + /* initialize class imported methods */ + tcm->imports = &syna_ts_mmi_methods.exports; + return 0; +} + +void syna_ts_mmi_dev_unregister(struct syna_tcm *tcm) { + ts_mmi_dev_unregister(tcm->pdev->dev.parent); +} diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_core_dev.h b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_core_dev.h new file mode 100644 index 000000000000..d6bd810dbb19 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_core_dev.h @@ -0,0 +1,1240 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file: synaptics_touchcom_core_dev.h + * + * This file is the topmost header file for Synaptics TouchComm device, also + * defines the TouchComm device context structure which will be passed to + * all other functions that expect a device handle. + */ + +#ifndef _SYNAPTICS_TOUCHCOM_CORE_DEV_H_ +#define _SYNAPTICS_TOUCHCOM_CORE_DEV_H_ + + +#include "syna_tcm2_platform.h" + + +#define SYNA_TCM_CORE_LIB_VERSION 0x0116 + + +/** + * @section: Parameters pre-defined + * + * @brief: MAX_NUM_OBJECTS + * Maximum number of objects being detected + * + * @brief: MAX_SIZE_GESTURE_DATA + * Maximum size of gesture data + * + * @brief: MAX_SIZE_CONFIG_ID + * Maximum size of customer configuration ID + */ +#define MAX_NUM_OBJECTS (10) + +#define MAX_SIZE_GESTURE_DATA (8) + +#define MAX_SIZE_CONFIG_ID (16) + +/** + * @section: Command-handling relevant definitions + * + * @brief: MESSAGE_HEADER_SIZE + * The size of message header + * + * @brief: CMD_RESPONSE_TIMEOUT_MS + * Time frame for a command execution + * + * @brief: CMD_RESPONSE_POLLING_DELAY_MS + * Generic time frame to check the response in polling + * + * @brief: RD_RETRY_US + * For retry reading, delay time range in microsecond + * + * @brief: WR_DELAY_US + * For continued writes, delay time range in microsecond + * + * @brief: TAT_DELAY_US + * For bus turn-around, delay time range in microsecond + * + * @brief: FW_MODE_SWITCH_DELAY_MS + * The default time for fw mode switching + * + * @brief: RESET_DELAY_MS + * The default time after reset in case it's not set properly + * + * @brief: FORCE_ATTN_DRIVEN + * Special flag to read in resp packet in ISR function + */ +#define MESSAGE_HEADER_SIZE (4) + +#define CMD_RESPONSE_TIMEOUT_MS (3000) + +#define CMD_RESPONSE_POLLING_DELAY_MS (10) + +#define RD_RETRY_US_MIN (5000) +#define RD_RETRY_US_MAX (10000) + +#define WR_DELAY_US_MIN (500) +#define WR_DELAY_US_MAX (1000) + +#define TAT_DELAY_US_MIN (100) +#define TAT_DELAY_US_MAX (200) + +#define FW_MODE_SWITCH_DELAY_MS (200) + +#define RESET_DELAY_MS (200) + +#define DEFAULT_FLASH_ERASE_DELAY (~0) +#define DEFAULT_FLASH_WRITE_DELAY (~0) +#define DEFAULT_FLASH_READ_DELAY (~0) + +#define RESP_IN_ATTN (0) +#define RESP_IN_POLLING (CMD_RESPONSE_POLLING_DELAY_MS) + +/** + * @section: Macro to show string in log + */ +#define STR(x) #x + +/** + * @section: Helpers to check the device mode + */ +#define IS_APP_FW_MODE(mode) \ + (mode == MODE_APPLICATION_FIRMWARE) + +#define IS_NOT_APP_FW_MODE(mode) \ + (!IS_APP_FW_MODE(mode)) + +#define IS_BOOTLOADER_MODE(mode) \ + ((mode == MODE_BOOTLOADER) || \ + (mode == MODE_TDDI_BOOTLOADER) || \ + (mode == MODE_TDDI_HDL_BOOTLOADER) || \ + (mode == MODE_MULTICHIP_TDDI_BOOTLOADER)) + +#define IS_ROM_BOOTLOADER_MODE(mode) \ + (mode == MODE_ROMBOOTLOADER) + + +/** + * @section: Types for lower-level bus being used + */ +enum bus_connection { + BUS_TYPE_NONE, + BUS_TYPE_I2C, + BUS_TYPE_SPI, + BUS_TYPE_I3C, +}; + +/** + * @section: TouchComm Firmware Modes + * + * The current mode running is defined in Identify Info Packet. + */ +enum tcm_firmware_mode { + MODE_UNKNOWN = 0x00, + MODE_APPLICATION_FIRMWARE = 0x01, + MODE_HOSTDOWNLOAD_FIRMWARE = 0x02, + MODE_ROMBOOTLOADER = 0x04, + MODE_BOOTLOADER = 0x0b, + MODE_TDDI_BOOTLOADER = 0x0c, + MODE_TDDI_HDL_BOOTLOADER = 0x0d, + MODE_PRODUCTIONTEST_FIRMWARE = 0x0e, + MODE_MULTICHIP_TDDI_BOOTLOADER = 0xab, +}; + +/** + * @section: Status of Application Firmware + * + * The current status is defined in Application Info Packet. + */ +enum tcm_app_status { + APP_STATUS_OK = 0x00, + APP_STATUS_BOOTING = 0x01, + APP_STATUS_UPDATING = 0x02, + APP_STATUS_BAD_APP_CONFIG = 0xff, +}; + +/** + * @section: Field IDs in Dynamic Configuration + * + * The codes specify the generic dynamic configuration options. + */ +enum dynamic_tcm_config_id { + DC_UNKNOWN = 0x00, + DC_DISABLE_DOZE = 0x01, + DC_DISABLE_NOISE_MITIGATION = 0x02, + DC_DISABLE_FREQUENCY_SHIFT = 0x03, + DC_REQUEST_FREQUENCY_INDEX = 0x04, + DC_DISABLE_HSYNC = 0x05, + DC_REZERO_ON_EXIT_DEEP_SLEEP = 0x06, + DC_ENABLE_CHARGER_CONNECTED = 0x07, + DC_DISABLE_BASELINE_RELAXATION = 0x08, + DC_ENABLE_WAKEUP_GESTURE_MODE = 0x09, + DC_REQUEST_TESTING_FINGERS = 0x0a, + DC_ENABLE_GRIP_SUPPRESSION = 0x0b, + DC_ENABLE_THICK_GLOVE = 0x0c, + DC_ENABLE_GLOVE = 0x0d, + DC_ENABLE_FACE_DETECTION = 0x0e, + DC_INHIBIT_ACTIVE_GESTURE = 0x0f, + DC_DISABLE_PROXIMITY = 0x10, +}; + +/** + * @section: TouchComm Commands + * + * List the generic commands supported in TouchComm command-response protocol. + */ +enum tcm_command { + CMD_NONE = 0x00, + CMD_CONTINUE_WRITE = 0x01, + CMD_IDENTIFY = 0x02, + CMD_RESET = 0x04, + CMD_ENABLE_REPORT = 0x05, + CMD_DISABLE_REPORT = 0x06, + CMD_TCM2_ACK = 0x07, + CMD_TCM2_RETRY = 0x08, + CMD_TCM2_SET_MAX_READ_LENGTH = 0x09, + CMD_TCM2_GET_REPORT = 0x0a, + CMD_GET_BOOT_INFO = 0x10, + CMD_ERASE_FLASH = 0x11, + CMD_WRITE_FLASH = 0x12, + CMD_READ_FLASH = 0x13, + CMD_RUN_APPLICATION_FIRMWARE = 0x14, + CMD_SPI_MASTER_WRITE_THEN_READ = 0x15, + CMD_REBOOT_TO_ROM_BOOTLOADER = 0x16, + CMD_RUN_BOOTLOADER_FIRMWARE = 0x1f, + CMD_GET_APPLICATION_INFO = 0x20, + CMD_GET_STATIC_CONFIG = 0x21, + CMD_SET_STATIC_CONFIG = 0x22, + CMD_GET_DYNAMIC_CONFIG = 0x23, + CMD_SET_DYNAMIC_CONFIG = 0x24, + CMD_GET_TOUCH_REPORT_CONFIG = 0x25, + CMD_SET_TOUCH_REPORT_CONFIG = 0x26, + CMD_REZERO = 0x27, + CMD_COMMIT_CONFIG = 0x28, + CMD_DESCRIBE_DYNAMIC_CONFIG = 0x29, + CMD_PRODUCTION_TEST = 0x2a, + CMD_SET_CONFIG_ID = 0x2b, + CMD_ENTER_DEEP_SLEEP = 0x2c, + CMD_EXIT_DEEP_SLEEP = 0x2d, + CMD_GET_TOUCH_INFO = 0x2e, + CMD_GET_DATA_LOCATION = 0x2f, + CMD_DOWNLOAD_CONFIG = 0x30, + CMD_ENTER_PRODUCTION_TEST_MODE = 0x31, + CMD_GET_FEATURES = 0x32, + CMD_GET_ROMBOOT_INFO = 0x40, + CMD_WRITE_PROGRAM_RAM = 0x41, + CMD_ROMBOOT_RUN_BOOTLOADER_FIRMWARE = 0x42, + CMD_SPI_MASTER_WRITE_THEN_READ_EXTENDED = 0x43, + CMD_ENTER_IO_BRIDGE_MODE = 0x44, + CMD_ROMBOOT_DOWNLOAD = 0x45, +}; + +/** + * @section: TouchComm Status Codes + * + * Define the following status codes for all command responses. + * 0x00: (v1) no commands are pending and no reports are available. + * 0x01: (v1 & v2) the previous command succeeded. + * 0x03: (v1 & v2) the payload continues a previous response. + * 0x04: (v2) command was written, but no reports were available. + * 0x07: (v2) the previous write was successfully received. + * 0x08: (v2) the previous write was corrupt. The host should resend. + * 0x09: (v2) the previous command failed. + * 0x0c: (v1 & v2) write was larger than the device's receive buffer. + * 0x0d: (v1 & v2) a command was sent before the previous command completed. + * 0x0e: (v1 & v2) the requested command is not implemented. + * 0x0f: (v1 & v2) generic communication error, probably incorrect payload. + * + * 0xfe: self-defined status for a corrupted packet. + * 0xff: self-defined status for an invalid data. + */ +enum tcm_status_code { + STATUS_IDLE = 0x00, + STATUS_OK = 0x01, + STATUS_CONTINUED_READ = 0x03, + STATUS_NO_REPORT_AVAILABLE = 0x04, + STATUS_ACK = 0x07, + STATUS_RETRY_REQUESTED = 0x08, + STATUS_CMD_FAILED = 0x09, + STATUS_RECEIVE_BUFFER_OVERFLOW = 0x0c, + STATUS_PREVIOUS_COMMAND_PENDING = 0x0d, + STATUS_NOT_IMPLEMENTED = 0x0e, + STATUS_ERROR = 0x0f, + STATUS_PACKET_CORRUPTED = 0xfe, + STATUS_INVALID = 0xff, +}; + +/** + * @section: TouchComm Report Codes + * + * Define the following report codes generated by TouchComm firmware. + * 0x10: Identify Info Packet + * 0x11: Touch Report + * 0x12: Delta Cap. Image + * 0x13: Raw Cap. Image + */ +enum tcm_report_type { + REPORT_IDENTIFY = 0x10, + REPORT_TOUCH = 0x11, + REPORT_DELTA = 0x12, + REPORT_RAW = 0x13, +}; + +/** + * @section: States in Command Processing + * + * List the states in command processing. + */ +enum tcm_command_status { + CMD_STATE_IDLE = 0, + CMD_STATE_BUSY = 1, + CMD_STATE_ERROR = -1, +}; + +/** + * @section: Production Test Items + * + * List the generic production test items + */ +enum tcm_test_code { + TEST_NOT_IMPLEMENTED = 0x00, + + TEST_PID01_TRX_TRX_SHORTS = 0x01, + TEST_PID02_TRX_SENSOR_OPENS = 0x02, + TEST_PID03_TRX_GROUND_SHORTS = 0x03, + TEST_PID05_FULL_RAW_CAP = 0x05, + TEST_PID06_EE_SHORT = 0x06, + TEST_PID07_DYNAMIC_RANGE = 0x07, + TEST_PID08_HIGH_RESISTANCE = 0x08, + TEST_PID10_DELTA_NOISE = 0x0a, + TEST_PID11_OPEN_DETECTION = 0x0b, + TEST_PID12 = 0x0c, + TEST_PID13 = 0x0d, + TEST_PID14_DOZE_DYNAMIC_RANGE = 0x0e, + TEST_PID15_DOZE_NOISE = 0x0f, + TEST_PID16_SENSOR_SPEED = 0x10, + TEST_PID17_ADC_RANGE = 0x11, + TEST_PID18_HYBRID_ABS_RAW = 0x12, + TEST_PID29_HYBRID_ABS_NOISE = 0x1D, + + TEST_PID_MAX, +}; + + +/** + * @section: Internal Buffer Structure + * + * This structure is taken as the internal common buffer. + */ +struct tcm_buffer { + unsigned char *buf; + unsigned int buf_size; + unsigned int data_length; + syna_pal_mutex_t buf_mutex; + unsigned char ref_cnt; +}; + +/** + * @section: TouchComm Identify Info Packet + * Ver.1: size is 24 (0x18) bytes + * Ver.2: size is extended to 32 (0x20) bytes + * + * The identify packet provides the basic TouchComm information and indicate + * that the device is ready to receive commands. + * + * The report is received whenever the device initially powers up, resets, + * or switches fw between bootloader and application modes. + */ +struct tcm_identification_info { + unsigned char version; + unsigned char mode; + unsigned char part_number[16]; + unsigned char build_id[4]; + unsigned char max_write_size[2]; + /* extension in ver.2 */ + unsigned char max_read_size[2]; + unsigned char reserved[6]; +}; + + +/** + * @section: TouchComm Application Information Packet + * + * The application info packet provides the information about the application + * firmware as well as the touch controller. + */ +struct tcm_application_info { + unsigned char version[2]; + unsigned char status[2]; + unsigned char static_config_size[2]; + unsigned char dynamic_config_size[2]; + unsigned char app_config_start_write_block[2]; + unsigned char app_config_size[2]; + unsigned char max_touch_report_config_size[2]; + unsigned char max_touch_report_payload_size[2]; + unsigned char customer_config_id[MAX_SIZE_CONFIG_ID]; + unsigned char max_x[2]; + unsigned char max_y[2]; + unsigned char max_objects[2]; + unsigned char num_of_buttons[2]; + unsigned char num_of_image_rows[2]; + unsigned char num_of_image_cols[2]; + unsigned char has_hybrid_data[2]; + unsigned char num_of_force_elecs[2]; +}; + +/** + * @section: TouchComm boot information packet + * + * The boot info packet provides the information of TouchBoot. + */ +struct tcm_boot_info { + unsigned char version; + unsigned char status; + unsigned char asic_id[2]; + unsigned char write_block_size_words; + unsigned char erase_page_size_words[2]; + unsigned char max_write_payload_size[2]; + unsigned char last_reset_reason; + unsigned char pc_at_time_of_last_reset[2]; + unsigned char boot_config_start_block[2]; + unsigned char boot_config_size_blocks[2]; + /* extension in ver.2 */ + unsigned char display_config_start_block[4]; + unsigned char display_config_length_blocks[2]; + unsigned char backup_display_config_start_block[4]; + unsigned char backup_display_config_length_blocks[2]; + unsigned char custom_otp_start_block[2]; + unsigned char custom_otp_length_blocks[2]; +}; + +/** + * @section: TouchComm ROMboot information packet + * + * The ROMboot info packet provides the information of ROM bootloader. + */ +struct tcm_romboot_info { + unsigned char version; + unsigned char status; + unsigned char asic_id[2]; + unsigned char write_block_size_words; + unsigned char max_write_payload_size[2]; + unsigned char last_reset_reason; + unsigned char pc_at_time_of_last_reset[2]; +}; + +/** + * @section: TouchComm features description packet + * + * The features description packet tells which features are supported. + */ +struct tcm_features_info { + unsigned char byte[16]; +}; + +/** + * @section: Data blob for touch data reported + * + * Once receiving a touch report generated by firmware, the touched data + * will be parsed and converted to touch_data_blob structure as a data blob. + * + * @subsection: tcm_touch_data_blob + * The touch_data_blob contains all sorts of touched data entities. + * + * @subsection tcm_objects_data_blob + * The objects_data_blob includes the data for each active objects. + * + * @subsection tcm_gesture_data_blob + * The gesture_data_blob contains the gesture data if detected. + */ +struct tcm_objects_data_blob { + unsigned char status; + unsigned int x_pos; + unsigned int y_pos; + unsigned int x_width; + unsigned int y_width; + unsigned int z; + unsigned int tx_pos; + unsigned int rx_pos; +}; +struct tcm_gesture_data_blob { + union { + struct { + unsigned char tap_x[2]; + unsigned char tap_y[2]; + }; + struct { + unsigned char swipe_x[2]; + unsigned char swipe_y[2]; + unsigned char swipe_direction[2]; + }; + unsigned char data[MAX_SIZE_GESTURE_DATA]; + }; +}; +struct tcm_touch_data_blob { + + /* for each active objects */ + unsigned int num_of_active_objects; + struct tcm_objects_data_blob object_data[MAX_NUM_OBJECTS]; + + /* for gesture */ + unsigned int gesture_id; + struct tcm_gesture_data_blob gesture_data; + + /* various data */ + unsigned int timestamp; + unsigned int buttons_state; + unsigned int frame_rate; + unsigned int power_im; + unsigned int cid_im; + unsigned int rail_im; + unsigned int cid_variance_im; + unsigned int nsm_frequency; + unsigned int nsm_state; + unsigned int num_of_cpu_cycles; + unsigned int fd_data; + unsigned int force_data; + unsigned int fingerprint_area_meet; + unsigned int sensing_mode; +}; + +/** + * @section: Callback function used to parse custom touch entity + * + * Allow to invoke the customized implementation to parse touch data inside + * touch report. + * + * @param + * [ in] code: the code of current touch entity + * [ in] config: the report configuration stored + * [in/out] config_offset: offset of current position in report config, + * the updated position should be returned. + * [ in] report: touch report given + * [in/out] report_offset: offset of current position in touch report, + * the updated position should be returned. + * [ in] report_size: size of given touch report + * [ in] callback_data: pointer to caller data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +typedef int (*tcm_custom_touch_entity_callback_t) (const unsigned char code, + const unsigned char *config, unsigned int *config_offset, + const unsigned char *report, unsigned int *report_offset, + unsigned int report_size, void *callback_data); + +/** + * @section: Callback function used to parse custom gesture data + * + * Allow to invoke the customized implementation to get gesture data inside + * touch report. + * + * @param + * [ in] code: the code of current touch entity + * [ in] config: the report configuration stored + * [in/out] config_offset: offset of current position in report config, + * the updated position should be returned. + * [ in] report: touch report given + * [in/out] report_offset: offset of current position in touch report, + * the updated position should be returned. + * [ in] report_size: size of given touch report + * [ in] callback_data: pointer to caller data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +typedef int (*tcm_custom_gesture_callback_t) (const unsigned char code, + const unsigned char *config, unsigned int *config_offset, + const unsigned char *report, unsigned int *report_offset, + unsigned int report_size, void *callback_data); + +/** + * @section: Callback function being invoked once getting a reset + * + * Allow to invoke the customized implementation to handle the unexpected reset + * including self-reset, external reset, and so on. + * + * @param + * [ in] callback_data: pointer to caller data + * + * @return + * none. + */ +typedef void (*tcm_reset_occurrence_callback_t) (void *callback_data); + +/** + * @section: TouchComm Message Handling Wrapper + * + * The structure contains the essential buffers and parameters to implement + * the command-response protocol for both TouchCom ver.1 and TouchCom ver.2. + */ +struct tcm_message_data_blob { + + /* parameters for command processing */ + syna_pal_atomic_t command_status; + unsigned char command; + unsigned char status_report_code; + unsigned int payload_length; + unsigned char response_code; + unsigned char report_code; + unsigned char seq_toggle; + unsigned int default_resp_reading; + + /* completion event for command processing */ + syna_pal_completion_t cmd_completion; + + /* internal buffers + * in : buffer storing the data being read 'in' + * out : buffer storing the data being sent 'out' + * temp: 'temp' buffer used for continued read operation + */ + struct tcm_buffer in; + struct tcm_buffer out; + struct tcm_buffer temp; + + /* mutex for the protection of command processing */ + syna_pal_mutex_t cmd_mutex; + + /* mutex for the read/write protection */ + syna_pal_mutex_t rw_mutex; + + /* flag for the enabling of predict reading + * predict reading aims to retrieve all data in one transfer; + * otherwise, standard reading reads 4-byte header and payload + * data separately + */ + bool predict_reads; + unsigned int predict_length; + + /* variables for the crc appended + */ + bool has_crc; + unsigned short crc_bytes; +}; + +/** + * @section: TouchComm core device context structure + * + * The device context contains parameters and internal buffers, that will + * be passed to all other functions that expect a device handle. + * + * Calling syna_tcm_allocate_device() can allocate this structure, and + * syna_tcm_remove_device() releases the structure if no longer needed. + */ +struct tcm_dev { + + /* basic device information */ + unsigned char dev_mode; + unsigned int packrat_number; + unsigned int max_x; + unsigned int max_y; + unsigned int max_objects; + unsigned int rows; + unsigned int cols; + unsigned char config_id[MAX_SIZE_CONFIG_ID]; + + /* capability of read/write transferred + * being assigned through syna_hw_interface + */ + unsigned int max_wr_size; + unsigned int max_rd_size; + + /* hardware-specific data structure + * defined in syna_touchcom_platform.h + */ + struct syna_hw_interface *hw_if; + + /* TouchComm defined structures */ + struct tcm_identification_info id_info; + struct tcm_application_info app_info; + struct tcm_boot_info boot_info; + + /* internal buffers + * report: record the TouchComm report to caller + * resp : record the command response to caller + */ + struct tcm_buffer report_buf; + struct tcm_buffer resp_buf; + struct tcm_buffer external_buf; + + /* touch report configuration */ + struct tcm_buffer touch_config; + + /* TouchComm message handling wrapper */ + struct tcm_message_data_blob msg_data; + + /* indicate that fw update is on-going */ + syna_pal_atomic_t firmware_flashing; + + /* abstraction to read a TouchComm message from device. + * Function will be assigned by syna_tcm_detect_device(). + * + * After read_message() returned, the retrieved data will be available + * and stored either in buffer.report or buffer.resp based on the + * code returned. + * + * @param + * [ in] tcm_dev: the device handle + * [out] status_report_code: status code or report code received + * + * @return + * 0 or positive value on success; otherwise, on error. + */ + int (*read_message)(struct tcm_dev *tcm_dev, + unsigned char *status_report_code); + + /* abstraction to write a TouchComm message to device and retrieve the + * response to command. + * Function will be assigned by syna_tcm_detect_device(). + * + * After calling write_message(), the response code is returned + * and the response data is stored in buffer.resp. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: TouchComm command to write + * [ in] payload: data payload, if any + * [ in] payload_length: length of data payload, if any + * [out] resp_code: response code returned + * [ in] delay_ms_resp: delay time for response reading. + * a positive value presents the polling time; + * or, set '0' (RESP_IN_ATTN) for ATTN driven + * @return + * 0 or positive value on success; otherwise, on error. + */ + int (*write_message)(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *payload, + unsigned int payload_length, unsigned char *resp_code, + unsigned int delay_ms_resp); + + + /* callbacks + * cb_custom_touch_entity: callback to parse custom touch entity + * cb_custom_gesture : callback to parse custom gesture + * cb_reset_occurrence : callback once reset occurrence + */ + tcm_custom_touch_entity_callback_t cb_custom_touch_entity; + void *cbdata_touch_entity; + tcm_custom_gesture_callback_t cb_custom_gesture; + void *cbdata_gesture; + tcm_reset_occurrence_callback_t cb_reset_occurrence; + void *cbdata_reset; +}; +/* end of structure syna_tcm_dev */ + + +/** + * @section: Protocol detection + * + * @brief: syna_tcm_v1_detect + * Check whether TouchComm ver.1 firmware is running + * + * @brief: syna_tcm_v2_detect + * Check whether TouchComm ver.2 firmware is running + */ + +/* syna_tcm_v1_detect() + * + * Check whether TouchComm ver.1 firmware is running. + * Function is implemented in synaptics_tcm2_core_v1.c. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] data: raw 4-byte data + * [ in] size: length of input data in bytes + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_v1_detect(struct tcm_dev *tcm_dev, unsigned char *data, + unsigned int data_len); + +/* syna_tcm_v2_detect() + * + * Check whether TouchComm ver.2 firmware is running. + * Function is implemented in synaptics_tcm2_core_v2.c. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] data: raw 4-byte data + * [ in] size: length of input data in bytes + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_v2_detect(struct tcm_dev *tcm_dev, unsigned char *data, + unsigned int data_len); + +/** + * @section: Buffers Management helpers + * + * @brief: syna_tcm_buf_alloc + * Allocate the requested memory space for the buffer structure + * + * @brief: syna_tcm_buf_realloc + * Extend the requested memory space for the buffer structure + * + * @brief: syna_tcm_buf_init + * Initialize the buffer structure + * + * @brief: syna_tcm_buf_lock + * Protect the access of current buffer structure + * + * @brief: syna_tcm_buf_unlock + * Open the access of current buffer structure + * + * @brief: syna_tcm_buf_release + * Release the buffer structure + */ + +/** + * syna_tcm_buf_alloc() + * + * Allocate the requested memory space for the given buffer only if + * the existed buffer is not enough for the requirement. + * + * @param + * [ in] pbuf: pointer to an internal buffer + * [ in] size: required size to be allocated + * + * @return + * 0 or positive value on success; otherwise, on error. + */ +static inline int syna_tcm_buf_alloc(struct tcm_buffer *pbuf, + unsigned int size) +{ + if (!pbuf) { + LOGE("Invalid buffer structure\n"); + return -1; + } + + if (size > pbuf->buf_size) { + if (pbuf->buf) + syna_pal_mem_free((void *)pbuf->buf); + + pbuf->buf = syna_pal_mem_alloc(size, sizeof(unsigned char)); + if (!(pbuf->buf)) { + LOGE("Fail to allocate memory (size = %d)\n", + (int)(size*sizeof(unsigned char))); + pbuf->buf_size = 0; + pbuf->data_length = 0; + return -1; + } + pbuf->buf_size = size; + } + + syna_pal_mem_set(pbuf->buf, 0x00, pbuf->buf_size); + pbuf->data_length = 0; + + return 0; +} + +/** + * syna_tcm_buf_realloc() + * + * Extend the requested memory space for the given buffer only if + * the existed buffer is not enough for the requirement. + * Then, move the content to the new memory space. + * + * @param + * [ in] pbuf: pointer to an internal buffer + * [ in] size: required size to be extended + * + * @return + * 0 or positive value on success; otherwise, on error. + */ +static inline int syna_tcm_buf_realloc(struct tcm_buffer *pbuf, + unsigned int size) +{ + int retval; + unsigned char *temp_src; + unsigned int temp_size = 0; + + if (!pbuf) { + LOGE("Invalid buffer structure\n"); + return -1; + } + + if (size > pbuf->buf_size) { + temp_src = pbuf->buf; + temp_size = pbuf->buf_size; + + pbuf->buf = syna_pal_mem_alloc(size, sizeof(unsigned char)); + if (!(pbuf->buf)) { + LOGE("Fail to allocate memory (size = %d)\n", + (int)(size * sizeof(unsigned char))); + syna_pal_mem_free((void *)temp_src); + pbuf->buf_size = 0; + return -1; + } + + retval = syna_pal_mem_cpy(pbuf->buf, + size, + temp_src, + temp_size, + temp_size); + if (retval < 0) { + LOGE("Fail to copy data\n"); + syna_pal_mem_free((void *)temp_src); + syna_pal_mem_free((void *)pbuf->buf); + pbuf->buf_size = 0; + return retval; + } + + syna_pal_mem_free((void *)temp_src); + pbuf->buf_size = size; + } + + return 0; +} +/** + * syna_tcm_buf_init() + * + * Initialize the buffer structure. + * + * @param + * [ in] pbuf: pointer to an internal buffer + * + * @return + * none + */ +static inline void syna_tcm_buf_init(struct tcm_buffer *pbuf) +{ + pbuf->buf_size = 0; + pbuf->data_length = 0; + pbuf->ref_cnt = 0; + pbuf->buf = NULL; + syna_pal_mutex_alloc(&pbuf->buf_mutex); +} +/** + * syna_tcm_buf_lock() + * + * Protect the access of current buffer structure. + * + * @param + * [ in] pbuf: pointer to an internal buffer + * + * @return + * none + */ +static inline void syna_tcm_buf_lock(struct tcm_buffer *pbuf) +{ + if (pbuf->ref_cnt != 0) { + LOGE("Buffer access out-of balance, %d\n", pbuf->ref_cnt); + return; + } + + syna_pal_mutex_lock(&pbuf->buf_mutex); + pbuf->ref_cnt++; +} +/** + * syna_tcm_buf_unlock() + * + * Open the access of current buffer structure. + * + * @param + * [ in] pbuf: pointer to an internal buffer + * + * @return + * none + */ +static inline void syna_tcm_buf_unlock(struct tcm_buffer *pbuf) +{ + if (pbuf->ref_cnt != 1) { + LOGE("Buffer access out-of balance, %d\n", pbuf->ref_cnt); + return; + } + + pbuf->ref_cnt--; + syna_pal_mutex_unlock(&pbuf->buf_mutex); +} +/** + * syna_tcm_buf_release() + * + * Release the buffer structure. + * + * @param + * [ in] pbuf: pointer to an internal buffer + * + * @return + * none + */ +static inline void syna_tcm_buf_release(struct tcm_buffer *pbuf) +{ + if (pbuf->ref_cnt != 0) + LOGE("Buffer access hold, %d\n", pbuf->ref_cnt); + + syna_pal_mutex_free(&pbuf->buf_mutex); + syna_pal_mem_free((void *)pbuf->buf); + pbuf->buf_size = 0; + pbuf->data_length = 0; + pbuf->ref_cnt = 0; +} +/** + * syna_tcm_buf_copy() + * + * Helper to copy data from the source buffer to the destination buffer. + * The size of destination buffer may be reallocated, if the size is + * smaller than the actual data size to be copied. + * + * @param + * [out] dest: pointer to an internal buffer + * [ in] src: required size to be extended + * + * @return + * 0 or positive value on success; otherwise, on error. + */ +static inline int syna_tcm_buf_copy(struct tcm_buffer *dest, + struct tcm_buffer *src) +{ + int retval = 0; + + if (dest->buf_size < src->data_length) { + retval = syna_tcm_buf_alloc(dest, src->data_length + 1); + if (retval < 0) { + LOGE("Fail to reallocate the given buffer, size: %d\n", + src->data_length + 1); + return retval; + } + } + + /* copy data content to the destination */ + retval = syna_pal_mem_cpy(dest->buf, + dest->buf_size, + src->buf, + src->buf_size, + src->data_length); + if (retval < 0) { + LOGE("Fail to copy data to caller, size: %d\n", + src->data_length); + return retval; + } + + dest->data_length = src->data_length; + + return retval; +} +/** + * @section: Reads / Writes Abstraction Function + * + * @brief: syna_tcm_read + * Read the data from bus directly + * + * @brief: syna_tcm_write + * Write the data to bus directly + */ + +/** + * syna_tcm_read() + * + * The bare read function, reading in the requested data bytes + * from bus directly. + * + * @param + * [ in] tcm_dev: the device handle + * [out] rd_data: buffer for storing data retrieved from device + * [ in] rd_len: length of reading data in bytes + * + * @return + * the number of data bytes retrieved; + * otherwise, negative value, on error. + */ +static inline int syna_tcm_read(struct tcm_dev *tcm_dev, + unsigned char *rd_data, unsigned int rd_len) +{ + struct syna_hw_interface *hw_if; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + hw_if = tcm_dev->hw_if; + if (!hw_if->ops_read_data) { + LOGE("Invalid hw ops_read function\n"); + return _ENODEV; + } + + return hw_if->ops_read_data(hw_if, rd_data, rd_len); +} + +/** + * syna_tcm_write() + * + * The bare write function, writing the given data bytes to bus directly. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] wr_data: written data + * [ in] wr_len: length of written data in bytes + * + * @return + * the number of data bytes retrieved; + * otherwise, negative value, on error. + */ +static inline int syna_tcm_write(struct tcm_dev *tcm_dev, + unsigned char *wr_data, unsigned int wr_len) +{ + struct syna_hw_interface *hw_if; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + hw_if = tcm_dev->hw_if; + if (!hw_if->ops_write_data) { + LOGE("Invalid hw ops_write function\n"); + return _ENODEV; + } + + return hw_if->ops_write_data(hw_if, wr_data, wr_len); +} + +/** + * @section: CRC Calculation Function + * + * @brief: syna_tcm_crc6 + * Return the crc-6 calculated + * + * @brief: syna_tcm_crc16 + * Return the crc-16 calculated + */ + +/** + * syna_tcm_crc6() + * + * Calculate the crc-6 with polynomial and return + * + * @param + * [ in] p: byte array for the calculation + * [ in] bits: number of bits + * + * @return + * the crc-6 value + */ +static inline unsigned char syna_tcm_crc6(unsigned char *p, + unsigned int bits) +{ + unsigned short r = 0x003F << 2; + unsigned short x; + static unsigned short crc6_table[16] = { + 0, 268, 536, 788, 1072, 1340, 1576, 1828, + 2144, 2412, 2680, 2932, 3152, 3420, 3656, 3908 + }; + + for (; bits > 8; bits -= 8) { + r ^= *p++; + r = (r << 4) ^ crc6_table[r >> 4]; + r = (r << 4) ^ crc6_table[r >> 4]; + } + + if (bits > 0) { + x = *p; + while (bits--) { + if (x & 0x80) + r ^= 0x80; + + x <<= 1; + r <<= 1; + if (r & 0x100) + r ^= (0x03 << 2); + } + } + + return (unsigned char)((r >> 2) & 0x3F); +} +/** + * syna_tcm_crc16() + * + * Calculate the crc-16 for TouchCom packet. + * + * @param + * [ in] p: byte array for the calculation + * [ in] len: length in bytes + * [ in] val: the initial value given + * + * @return + * the crc-16 value + */ +static inline unsigned short syna_tcm_crc16(unsigned char *p, + unsigned int len, unsigned short val) +{ + unsigned short r = val; + static unsigned short crc16_table[256] = { + 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 + }; + + if (len == 0) + return r; + + while (len--) + r = (r << 8) ^ crc16_table[(r >> 8) ^ *p++]; + + return r; +} + + +#endif /* end of _SYNAPTICS_TOUCHCOM_CORE_DEV_H_ */ diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_core_v1.c b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_core_v1.c new file mode 100644 index 000000000000..75d4c4acd768 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_core_v1.c @@ -0,0 +1,1212 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file synaptics_touchcom_core_v1.c + * + * This file implements the TouchComm version 1 command-response protocol. + */ + +#include "synaptics_touchcom_core_dev.h" + +#define TCM_V1_MESSAGE_MARKER 0xa5 +#define TCM_V1_MESSAGE_PADDING 0x5a + +/** + * @section: Header of TouchComm v1 Message Packet + * + * The 4-byte header in the TouchComm v1 packet + */ +struct tcm_v1_message_header { + union { + struct { + unsigned char marker; + unsigned char code; + unsigned char length[2]; + }; + unsigned char data[MESSAGE_HEADER_SIZE]; + }; +}; + +/** + * syna_tcm_v1_parse_idinfo() + * + * Copy the given data to the identification info structure + * and parse the basic information, e.g. fw build id. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] data: data buffer + * [ in] size: size of given data buffer + * [ in] data_len: length of actual data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v1_parse_idinfo(struct tcm_dev *tcm_dev, + unsigned char *data, unsigned int size, unsigned int data_len) +{ + int retval; + unsigned int wr_size = 0; + unsigned int build_id = 0; + struct tcm_identification_info *id_info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if ((!data) || (data_len == 0)) { + LOGE("Invalid given data buffer\n"); + return _EINVAL; + } + + id_info = &tcm_dev->id_info; + + retval = syna_pal_mem_cpy((unsigned char *)id_info, + sizeof(struct tcm_identification_info), + data, + size, + MIN(sizeof(*id_info), data_len)); + if (retval < 0) { + LOGE("Fail to copy identification info\n"); + return retval; + } + + build_id = syna_pal_le4_to_uint(id_info->build_id); + + wr_size = syna_pal_le2_to_uint(id_info->max_write_size); + tcm_dev->max_wr_size = MIN(wr_size, WR_CHUNK_SIZE); + if (tcm_dev->max_wr_size == 0) { + tcm_dev->max_wr_size = wr_size; + LOGD("max_wr_size = %d\n", tcm_dev->max_wr_size); + } + + LOGI("TCM Fw mode: 0x%02x\n", id_info->mode); + + if (tcm_dev->packrat_number != build_id) + tcm_dev->packrat_number = build_id; + + tcm_dev->dev_mode = id_info->mode; + + return 0; +} + +/** + * syna_tcm_v1_dispatch_report() + * + * Handle the TouchCom report packet being received. + * + * If it's an identify report, parse the identification packet and signal + * the command completion just in case. + * Otherwise, copy the data from internal buffer.in to internal buffer.report + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * none. + */ +static void syna_tcm_v1_dispatch_report(struct tcm_dev *tcm_dev) +{ + int retval; + struct tcm_message_data_blob *tcm_msg = NULL; + syna_pal_completion_t *cmd_completion = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return; + } + + tcm_msg = &tcm_dev->msg_data; + cmd_completion = &tcm_msg->cmd_completion; + + tcm_msg->report_code = tcm_msg->status_report_code; + + if (tcm_msg->payload_length == 0) { + tcm_dev->report_buf.data_length = 0; + goto exit; + } + + /* The identify report may be resulted from reset or fw mode switching + */ + if (tcm_msg->report_code == REPORT_IDENTIFY) { + + syna_tcm_buf_lock(&tcm_msg->in); + + retval = syna_tcm_v1_parse_idinfo(tcm_dev, + &tcm_msg->in.buf[MESSAGE_HEADER_SIZE], + tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to identify device\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + return; + } + + syna_tcm_buf_unlock(&tcm_msg->in); + + /* in case, the identify info packet is caused by the command */ + if (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_BUSY) { + switch (tcm_msg->command) { + case CMD_RESET: + LOGD("Reset by CMD_RESET\n"); + case CMD_REBOOT_TO_ROM_BOOTLOADER: + case CMD_RUN_BOOTLOADER_FIRMWARE: + case CMD_RUN_APPLICATION_FIRMWARE: + case CMD_ENTER_PRODUCTION_TEST_MODE: + case CMD_ROMBOOT_RUN_BOOTLOADER_FIRMWARE: + tcm_msg->response_code = STATUS_OK; + ATOMIC_SET(tcm_msg->command_status, + CMD_STATE_IDLE); + syna_pal_completion_complete(cmd_completion); + goto exit; + default: + LOGN("Device has been reset\n"); + ATOMIC_SET(tcm_msg->command_status, + CMD_STATE_ERROR); + syna_pal_completion_complete(cmd_completion); + goto exit; + } + } else { + /* invoke callback to handle unexpected reset if doesn't + * result from command + */ + if (tcm_dev->cb_reset_occurrence) + tcm_dev->cb_reset_occurrence(tcm_dev->cbdata_reset); + } + } + + /* store the received report into the internal buffer.report */ + syna_tcm_buf_lock(&tcm_dev->report_buf); + + retval = syna_tcm_buf_alloc(&tcm_dev->report_buf, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf.report\n"); + syna_tcm_buf_unlock(&tcm_dev->report_buf); + goto exit; + } + + syna_tcm_buf_lock(&tcm_msg->in); + + retval = syna_pal_mem_cpy(tcm_dev->report_buf.buf, + tcm_dev->report_buf.buf_size, + &tcm_msg->in.buf[MESSAGE_HEADER_SIZE], + tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to copy payload to buf_report\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + syna_tcm_buf_unlock(&tcm_dev->report_buf); + goto exit; + } + + tcm_dev->report_buf.data_length = tcm_msg->payload_length; + + syna_tcm_buf_unlock(&tcm_msg->in); + syna_tcm_buf_unlock(&tcm_dev->report_buf); + +exit: + return; +} + +/** + * syna_tcm_v1_dispatch_response() + * + * Handle the response packet. + * + * Copy the data from internal buffer.in to internal buffer.resp, + * and then signal the command completion. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * none. + */ +static void syna_tcm_v1_dispatch_response(struct tcm_dev *tcm_dev) +{ + int retval; + struct tcm_message_data_blob *tcm_msg = NULL; + syna_pal_completion_t *cmd_completion = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return; + } + + tcm_msg = &tcm_dev->msg_data; + cmd_completion = &tcm_msg->cmd_completion; + + tcm_msg->response_code = tcm_msg->status_report_code; + + if (ATOMIC_GET(tcm_msg->command_status) != CMD_STATE_BUSY) + return; + + if (tcm_msg->payload_length == 0) { + tcm_dev->resp_buf.data_length = tcm_msg->payload_length; + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE); + goto exit; + } + + /* copy the received resp data into the internal buffer.resp */ + syna_tcm_buf_lock(&tcm_dev->resp_buf); + + retval = syna_tcm_buf_alloc(&tcm_dev->resp_buf, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf.resp\n"); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + goto exit; + } + + syna_tcm_buf_lock(&tcm_msg->in); + + retval = syna_pal_mem_cpy(tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + &tcm_msg->in.buf[MESSAGE_HEADER_SIZE], + tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to copy payload to internal resp_buf\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + goto exit; + } + + tcm_dev->resp_buf.data_length = tcm_msg->payload_length; + + syna_tcm_buf_unlock(&tcm_msg->in); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE); + +exit: + syna_pal_completion_complete(cmd_completion); +} + + +/** + * syna_tcm_v1_read() + * + * Read in a TouchCom packet from device. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] rd_length: number of reading bytes; + * '0' means to read the message header only + * [in/out] buf: pointer to a buffer which is stored the retrieved data + * [out] buf_size: size of the buffer pointed + * [ in] extra_crc: flag to read in extra crc bytes + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v1_read(struct tcm_dev *tcm_dev, unsigned int rd_length, + unsigned char *buf, unsigned int buf_size, bool extra_crc) +{ + int retval; + unsigned int max_rd_size; + int retry; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (rd_length == 0) + return 0; + + if (rd_length > buf_size) { + LOGE("Invalid read length, len: %d, buf_size: %d\n", + rd_length, buf_size); + return _EINVAL; + } + + max_rd_size = tcm_dev->max_rd_size; + + if ((max_rd_size != 0) && (rd_length > max_rd_size)) { + LOGE("Invalid read length, len: %d, max_rd_size: %d\n", + rd_length, max_rd_size); + return _EINVAL; + } + + /* read in the message header from device + * will do retry if the packet is not expected + */ + for (retry = 0; retry < 10; retry++) { + retval = syna_tcm_read(tcm_dev, + buf, + rd_length + ); + if (retval < 0) { + LOGE("Fail to read %d bytes to device\n", rd_length); + goto exit; + } + + /* check the message header */ + if (buf[0] == TCM_V1_MESSAGE_MARKER) + break; + + LOGE("Incorrect header marker, 0x%02x (retry:%d)\n", + buf[0], retry); + + retval = _EIO; + syna_pal_sleep_us(RD_RETRY_US_MIN, RD_RETRY_US_MAX); + } + +exit: + return retval; +} + +/** + * syna_tcm_v1_write() + * + * Construct the TouchCom v1 packet and send it to device. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: command code + * [ in] payload: data payload if any + * [ in] payload_len: length of data payload if any + * [ in] crc_append: flag to send extra crc bytes + * [ in] extra_crc: two bytes crc value + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v1_write(struct tcm_dev *tcm_dev, unsigned char command, + unsigned char *payload, unsigned int payload_len, + bool extra_crc, unsigned short crc) +{ + int retval = 0; + struct tcm_message_data_blob *tcm_msg = NULL; + int size, buf_size; + unsigned char crc16[2] = { 0 }; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + + syna_tcm_buf_lock(&tcm_msg->out); + + /* allocate the space storing the written data */ + buf_size = payload_len + 3; + if (extra_crc) { + crc16[0] = (unsigned char) crc & 0xff; + crc16[1] = (unsigned char) (crc >> 8); + + buf_size += 2; + } + + retval = syna_tcm_buf_alloc(&tcm_msg->out, buf_size); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf.out\n"); + goto exit; + } + + if (command != CMD_CONTINUE_WRITE) { + /* construct the command packet + * size = 1-byte command + 2-byte length + payload + */ + size = payload_len + 3; + + tcm_msg->out.buf[0] = command; + tcm_msg->out.buf[1] = (unsigned char)payload_len; + tcm_msg->out.buf[2] = (unsigned char)(payload_len >> 8); + + if (payload_len > 0) { + retval = syna_pal_mem_cpy(&tcm_msg->out.buf[3], + tcm_msg->out.buf_size - 3, + payload, + payload_len, + payload_len + ); + if (retval < 0) { + LOGE("Fail to copy payload\n"); + goto exit; + } + } + } else { + /* construct the continued writes packet + * size = 1-byte continued write + payload + */ + size = payload_len + 1; + + tcm_msg->out.buf[0] = CMD_CONTINUE_WRITE; + + retval = syna_pal_mem_cpy(&tcm_msg->out.buf[1], + tcm_msg->out.buf_size - 1, + payload, + payload_len, + payload_len + ); + if (retval < 0) { + LOGE("Fail to copy continued write\n"); + goto exit; + } + } + + /* append the crc16 value at the end */ + if (extra_crc) { + retval = syna_pal_mem_cpy(&tcm_msg->out.buf[size], + tcm_msg->out.buf_size - size, + crc16, + sizeof(crc16), + sizeof(crc16) + ); + if (retval < 0) { + LOGE("Fail to append crc16\n"); + goto exit; + } + + size += 2; + } + + /* write command packet to the device */ + retval = syna_tcm_write(tcm_dev, + tcm_msg->out.buf, + size + ); + if (retval < 0) { + LOGE("Fail to write %d bytes to device\n", size); + goto exit; + } + +exit: + syna_tcm_buf_unlock(&tcm_msg->out); + + return retval; +} + +/** + * syna_tcm_v1_continued_read() + * + * The remaining data payload is read in continuously until the end of data. + * All the retrieved data is appended to the internal buffer.in. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] length: remaining length of payload data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v1_continued_read(struct tcm_dev *tcm_dev, + unsigned int length) +{ + int retval = 0; + unsigned char code; + unsigned int idx; + unsigned int offset; + unsigned int chunks; + unsigned int chunk_space; + unsigned int xfer_length; + unsigned int total_length; + unsigned int remaining_length; + struct tcm_message_data_blob *tcm_msg = NULL; + bool last = false; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + + if ((length == 0) || (tcm_msg->payload_length == 0)) + return 0; + + if ((length & 0xffff) == 0xffff) { + LOGE("Invalid length to read\n"); + return _EINVAL; + } + + /* continued read packet contains the header, payload, and a padding */ + total_length = MESSAGE_HEADER_SIZE + tcm_msg->payload_length + 1; + /* length to read, remember a padding at the end */ + remaining_length = length + 1; + + /* read extra crc if supported */ + if (tcm_msg->has_crc) { + total_length += 2; + remaining_length += 2; + } + + syna_tcm_buf_lock(&tcm_msg->in); + + /* in case the current buf.in is smaller than requested size */ + retval = syna_tcm_buf_realloc(&tcm_msg->in, + total_length + 1); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf_in\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + return retval; + } + + /* available chunk space for payload = + * total chunk size - (marker + status code) + */ + if (tcm_dev->max_rd_size == 0) + chunk_space = remaining_length; + else + chunk_space = tcm_dev->max_rd_size - 2; + + chunks = syna_pal_ceil_div(remaining_length, chunk_space); + chunks = chunks == 0 ? 1 : chunks; + + offset = MESSAGE_HEADER_SIZE + (tcm_msg->payload_length - length); + + syna_tcm_buf_lock(&tcm_msg->temp); + + for (idx = 0; idx < chunks; idx++) { + if (remaining_length > chunk_space) + xfer_length = chunk_space; + else + xfer_length = remaining_length; + + last = ((idx + 1) == chunks); + + if (xfer_length == 1) { + tcm_msg->in.buf[offset] = TCM_V1_MESSAGE_PADDING; + offset += xfer_length; + remaining_length -= xfer_length; + continue; + } + + /* allocate the internal temp buffer */ + retval = syna_tcm_buf_alloc(&tcm_msg->temp, + xfer_length + 2); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf.temp\n"); + goto exit; + } + /* retrieve data from the bus + * data should include header marker and status code + */ + retval = syna_tcm_v1_read(tcm_dev, + xfer_length + 2, + tcm_msg->temp.buf, + tcm_msg->temp.buf_size, + (tcm_msg->has_crc) && last); + if (retval < 0) { + LOGE("Fail to read %d bytes from device\n", + xfer_length + 2); + goto exit; + } + + tcm_msg->temp.data_length = xfer_length + 2; + + /* check the data content */ + code = tcm_msg->temp.buf[1]; + + if (code != STATUS_CONTINUED_READ) { + LOGE("Incorrect status code 0x%02x at %d out of %d\n", + code, idx, chunks); + retval = _EIO; + goto exit; + } + + /* copy data from internal buffer.temp to buffer.in */ + retval = syna_pal_mem_cpy(&tcm_msg->in.buf[offset], + tcm_msg->in.buf_size - offset, + &tcm_msg->temp.buf[2], + tcm_msg->temp.buf_size - 2, + xfer_length); + if (retval < 0) { + LOGE("Fail to copy payload\n"); + goto exit; + } + + offset += xfer_length; + remaining_length -= xfer_length; + } + /* copy crc bytes which are followed by EOM (0x5a) */ + offset = MESSAGE_HEADER_SIZE + tcm_msg->payload_length; + if (tcm_msg->has_crc) { + tcm_msg->crc_bytes = (unsigned short)syna_pal_le2_to_uint( + &tcm_msg->in.buf[offset + 1]); /* skip EOM */ + + LOGD("CRC to read: 0x%04X\n", tcm_msg->crc_bytes); + } +exit: + syna_tcm_buf_unlock(&tcm_msg->temp); + syna_tcm_buf_unlock(&tcm_msg->in); + + return retval; +} + +/** + * syna_tcm_v1_read_message() + * + * Read in a TouchCom packet from device. + * The packet including its payload is read in from device and stored in + * the internal buffer.resp or buffer.report based on the code received. + * + * @param + * [ in] tcm_dev: the device handle + * [out] status_report_code: status code or report code received + * + * @return + * 0 or positive value on success; otherwise, on error. + */ +static int syna_tcm_v1_read_message(struct tcm_dev *tcm_dev, + unsigned char *status_report_code) +{ + int retval = 0; + struct tcm_v1_message_header *header; + struct tcm_message_data_blob *tcm_msg = NULL; + syna_pal_mutex_t *rw_mutex = NULL; + syna_pal_completion_t *cmd_completion = NULL; + unsigned int len = 0; + bool do_predict = false; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + rw_mutex = &tcm_msg->rw_mutex; + cmd_completion = &tcm_msg->cmd_completion; + + /* predict reading is applied when report streaming only */ + do_predict = (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_IDLE); + + if (status_report_code) + *status_report_code = STATUS_INVALID; + + tcm_msg->crc_bytes = 0; + + syna_pal_mutex_lock(rw_mutex); + + syna_tcm_buf_lock(&tcm_msg->in); + + /* determine the length to read */ + len = MESSAGE_HEADER_SIZE; + if (tcm_msg->predict_reads && do_predict) + len += tcm_msg->predict_length; + + /* ensure the size of in.buf */ + if (len > tcm_msg->in.buf_size) { + retval = syna_tcm_buf_alloc(&tcm_msg->in, len); + if (retval < 0) { + LOGE("Fail to allocate memory for buf_in\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + + tcm_msg->status_report_code = STATUS_INVALID; + tcm_msg->payload_length = 0; + goto exit; + } + } + + /* read in the message header from device */ + retval = syna_tcm_v1_read(tcm_dev, + len, + tcm_msg->in.buf, + tcm_msg->in.buf_size, + false); + if (retval < 0) { + LOGE("Fail to read message header from device\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + + tcm_msg->status_report_code = STATUS_INVALID; + tcm_msg->payload_length = 0; + goto exit; + } + + /* check the message header */ + header = (struct tcm_v1_message_header *)tcm_msg->in.buf; + + tcm_msg->status_report_code = header->code; + + tcm_msg->payload_length = syna_pal_le2_to_uint(header->length); + + if (tcm_msg->status_report_code != STATUS_IDLE) + LOGD("Status code: 0x%02x, length: %d (%02x %02x %02x %02x)\n", + tcm_msg->status_report_code, tcm_msg->payload_length, + header->data[0], header->data[1], header->data[2], + header->data[3]); + + syna_tcm_buf_unlock(&tcm_msg->in); + + if (tcm_msg->payload_length == 0) + goto do_dispatch; + + if (tcm_msg->payload_length > (len - MESSAGE_HEADER_SIZE)) + len = tcm_msg->payload_length - (len - MESSAGE_HEADER_SIZE); + else + len = 0; + + /* retrieve the remaining data, if any */ + retval = syna_tcm_v1_continued_read(tcm_dev, len); + if (retval < 0) { + LOGE("Fail to do continued read\n"); + goto exit; + } + + /* refill the header for dispatching */ + syna_tcm_buf_lock(&tcm_msg->in); + + tcm_msg->in.buf[0] = TCM_V1_MESSAGE_MARKER; + tcm_msg->in.buf[1] = tcm_msg->status_report_code; + tcm_msg->in.buf[2] = (unsigned char)tcm_msg->payload_length; + tcm_msg->in.buf[3] = (unsigned char)(tcm_msg->payload_length >> 8); + + syna_tcm_buf_unlock(&tcm_msg->in); + +do_dispatch: + /* duplicate the data to external buffer */ + syna_tcm_buf_lock(&tcm_dev->external_buf); + if (tcm_msg->payload_length > 0) { + retval = syna_tcm_buf_alloc(&tcm_dev->external_buf, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to allocate memory, external_buf invalid\n"); + } else { + retval = syna_pal_mem_cpy(&tcm_dev->external_buf.buf[0], + tcm_msg->payload_length, + &tcm_msg->in.buf[MESSAGE_HEADER_SIZE], + tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE, + tcm_msg->payload_length); + if (retval < 0) + LOGE("Fail to copy data to external buffer\n"); + } + } + tcm_dev->external_buf.data_length = tcm_msg->payload_length; + syna_tcm_buf_unlock(&tcm_dev->external_buf); + + if ((tcm_msg->status_report_code <= STATUS_ERROR) || + (tcm_msg->status_report_code == STATUS_INVALID)) { + switch (tcm_msg->status_report_code) { + case STATUS_OK: + break; + case STATUS_CONTINUED_READ: + LOGE("Out-of-sync continued read\n"); + retval = _EIO; + goto exit; + case STATUS_IDLE: + retval = 0; + goto exit; + default: + LOGE("Incorrect Status code, 0x%02x\n", + tcm_msg->status_report_code); + break; + } + } + + /* process the retrieved packet */ + if (tcm_msg->status_report_code >= REPORT_IDENTIFY) + syna_tcm_v1_dispatch_report(tcm_dev); + else + syna_tcm_v1_dispatch_response(tcm_dev); + + /* copy the status report code to caller */ + if (status_report_code) + *status_report_code = tcm_msg->status_report_code; + + /* update the length for predict reading */ + if (tcm_msg->predict_reads && do_predict) { + if (tcm_dev->max_rd_size < MESSAGE_HEADER_SIZE) + tcm_msg->predict_length = tcm_msg->payload_length; + else + tcm_msg->predict_length = MIN(tcm_msg->payload_length, + tcm_dev->max_rd_size - MESSAGE_HEADER_SIZE - 1); + + tcm_msg->predict_length += 1; /* padding byte */ + } + + retval = 0; + +exit: + /* raise the completion event when errors out */ + if (retval < 0) { + if (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_BUSY) { + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_ERROR); + syna_pal_completion_complete(cmd_completion); + } + } + + syna_pal_mutex_unlock(rw_mutex); + + return retval; +} + +/** + * syna_tcm_v1_write_message() + * + * Write message including command and its payload to TouchCom device. + * Then, the response of the command generated by the device will be + * read in and stored in internal buffer.resp. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: TouchComm command + * [ in] payload: data payload, if any + * [ in] payload_len: length of data payload, if any + * [out] resp_code: response code returned + * [ in] delay_ms_resp: delay time for response reading. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * + * @return + * 0 or positive value on success; otherwise, on error. + */ +static int syna_tcm_v1_write_message(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *payload, + unsigned int payload_len, unsigned char *resp_code, + unsigned int delay_ms_resp) +{ + int retval = 0; + unsigned int idx; + unsigned int chunks; + unsigned int chunk_space; + unsigned int xfer_length; + unsigned int remaining_length; + int timeout = 0; + int polling_ms = 0; + struct tcm_message_data_blob *tcm_msg = NULL; + syna_pal_mutex_t *cmd_mutex = NULL; + syna_pal_mutex_t *rw_mutex = NULL; + syna_pal_completion_t *cmd_completion = NULL; + bool has_irq_ctrl = false; + bool in_polling = false; + bool last = false; + unsigned char tmp; + unsigned short crc16 = 0xFFFF; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + cmd_mutex = &tcm_msg->cmd_mutex; + rw_mutex = &tcm_msg->rw_mutex; + cmd_completion = &tcm_msg->cmd_completion; + + if (resp_code) + *resp_code = STATUS_INVALID; + + /* indicate which mode is used */ + in_polling = (delay_ms_resp != RESP_IN_ATTN); + + /* irq control is enabled only when the operations is implemented + * and the current status of irq is enabled. + * do not enable irq if it is disabled by someone. + */ + has_irq_ctrl = (bool)(tcm_dev->hw_if->ops_enable_irq != NULL); + has_irq_ctrl &= tcm_dev->hw_if->bdata_attn.irq_enabled; + + /* disable irq when using polling mode */ + if (has_irq_ctrl && in_polling && tcm_dev->hw_if->ops_enable_irq) + tcm_dev->hw_if->ops_enable_irq(tcm_dev->hw_if, false); + + syna_pal_mutex_lock(cmd_mutex); + + syna_pal_mutex_lock(rw_mutex); + + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_BUSY); + + /* reset the command completion */ + syna_pal_completion_reset(cmd_completion); + + tcm_msg->command = command; + + remaining_length = payload_len; + + LOGD("Command: 0x%02x, payload len: %d\n", command, payload_len); + + /* calculate the crc if supported */ + if (tcm_msg->has_crc) { + crc16 = syna_tcm_crc16(&command, 1, crc16); + tmp = (unsigned char)payload_len & 0xff; + crc16 = syna_tcm_crc16(&tmp, 1, crc16); + tmp = (unsigned char)(payload_len >> 8) & 0xff; + crc16 = syna_tcm_crc16(&tmp, 1, crc16); + if (payload_len > 0) + crc16 = syna_tcm_crc16(payload, payload_len, crc16); + + LOGD("CRC to write: 0x%04X\n", crc16); + } + + /* available space for payload = total size - command byte */ + if (tcm_dev->max_wr_size == 0) + chunk_space = remaining_length; + else + chunk_space = tcm_dev->max_wr_size - 1; + + chunks = syna_pal_ceil_div(remaining_length, chunk_space); + chunks = chunks == 0 ? 1 : chunks; + + /* send out command packets + * + * separate into several sub-packets if the overall size is over + * than the maximum write size. + */ + for (idx = 0; idx < chunks; idx++) { + if (remaining_length > chunk_space) + xfer_length = chunk_space; + else + xfer_length = remaining_length; + + last = ((idx + 1) == chunks); + + if (idx == 0) { + retval = syna_tcm_v1_write(tcm_dev, + tcm_msg->command, + &payload[0], + xfer_length, + (tcm_msg->has_crc) && last, + crc16); + } else { + retval = syna_tcm_v1_write(tcm_dev, + CMD_CONTINUE_WRITE, + &payload[idx * chunk_space], + xfer_length, + (tcm_msg->has_crc) && last, + crc16); + } + + if (retval < 0) { + LOGE("Fail to write %d bytes to device\n", + xfer_length); + syna_pal_mutex_unlock(rw_mutex); + goto exit; + } + + remaining_length -= xfer_length; + + if (chunks > 1) + syna_pal_sleep_us(WR_DELAY_US_MIN, WR_DELAY_US_MAX); + } + + syna_pal_mutex_unlock(rw_mutex); + + /* handle the command response + * + * assuming to select the polling mode, the while-loop below will + * repeatedly read in the respose data based on the given polling + * time; otherwise, wait until receiving a completion event from + * interupt thread. + */ + timeout = 0; + if (!in_polling) + polling_ms = CMD_RESPONSE_TIMEOUT_MS; + else + polling_ms = delay_ms_resp; + + do { + /* wait for the completion event triggered by read_message */ + retval = syna_pal_completion_wait_for(cmd_completion, + polling_ms); + /* reset the status when times out and keep in polling */ + if (retval < 0) + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_BUSY); + + /* break when geting a valid resp; otherwise, keep in polling */ + if (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_IDLE) + goto check_response; + + if (in_polling) { + /* retrieve the message packet back */ + retval = syna_tcm_v1_read_message(tcm_dev, NULL); + /* keep in polling if still not having a valid resp */ + if (retval < 0) + syna_pal_completion_reset(cmd_completion); + } + + timeout += polling_ms + 10; + + } while (timeout < CMD_RESPONSE_TIMEOUT_MS); + + /* check the status of response data + * according to the touchcomm spec, each command message + * should have an associated reponse message. + */ +check_response: + if (ATOMIC_GET(tcm_msg->command_status) != CMD_STATE_IDLE) { + if (timeout >= CMD_RESPONSE_TIMEOUT_MS) { + LOGE("Timed out wait for response of command 0x%02x\n", + command); + retval = _ETIMEDOUT; + goto exit; + } else { + LOGE("Fail to get valid response of command 0x%02x\n", + command); + retval = _EIO; + goto exit; + } + } + + /* copy response code to the caller */ + LOGD("Received code 0x%02x\n", + tcm_msg->status_report_code); + if (resp_code) + *resp_code = tcm_msg->status_report_code; + + if (tcm_msg->response_code != STATUS_OK) + retval = _EIO; + else + retval = 0; + +exit: + tcm_msg->command = CMD_NONE; + + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE); + + syna_pal_mutex_unlock(cmd_mutex); + + /* recovery the irq if using polling mode */ + if (has_irq_ctrl && in_polling && tcm_dev->hw_if->ops_enable_irq) + tcm_dev->hw_if->ops_enable_irq(tcm_dev->hw_if, true); + + return retval; +} + +/** + * syna_tcm_v1_detect() + * + * For TouchCom v1 protocol, the given raw data must start with a specific + * maker code. If so, read the remaining packet from TouchCom device. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] data: raw 4-byte data + * [ in] size: length of input data in bytes + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_v1_detect(struct tcm_dev *tcm_dev, unsigned char *data, + unsigned int size) +{ + int retval; + struct tcm_v1_message_header *header; + struct tcm_message_data_blob *tcm_msg = NULL; + unsigned int payload_length = 0; + unsigned char resp_code = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if ((!data) || (size != MESSAGE_HEADER_SIZE)) { + LOGE("Invalid parameters\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + + header = (struct tcm_v1_message_header *)data; + + if (header->marker != TCM_V1_MESSAGE_MARKER) + return _ENODEV; + + /* assume having crc appended, will determine whether feature + * is enabled or not + */ + tcm_msg->has_crc = true; + tcm_dev->msg_data.crc_bytes = 0x5a5a; + + /* after initially powering on, the identify report should be the + * first packet + */ + if (header->code == REPORT_IDENTIFY) { + payload_length = syna_pal_le2_to_uint(header->length); + tcm_msg->payload_length = payload_length; + + /* retrieve the identify info packet */ + retval = syna_tcm_v1_continued_read(tcm_dev, + payload_length); + if (retval < 0) { + LOGE("Fail to read in identify info packet\n"); + return retval; + } + } else { + /* if not, send an identify command instead */ + retval = syna_tcm_v1_write_message(tcm_dev, + CMD_IDENTIFY, + NULL, + 0, + &resp_code, + RESP_IN_POLLING); + if (retval < 0) { + /* in case the identify command is not working, + * send a rest command as the workaround + */ + retval = syna_tcm_v1_write_message(tcm_dev, + CMD_RESET, + NULL, + 0, + &resp_code, + RESET_DELAY_MS); + if (retval < 0) { + LOGE("Fail to identify the device\n"); + return _ENODEV; + } + } + + payload_length = tcm_msg->payload_length; + } + + /* parse the identify info packet */ + syna_tcm_buf_lock(&tcm_msg->in); + + retval = syna_tcm_v1_parse_idinfo(tcm_dev, + &tcm_msg->in.buf[MESSAGE_HEADER_SIZE], + tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE, + payload_length); + if (retval < 0) { + LOGE("Fail to identify device\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + return retval; + } + + syna_tcm_buf_unlock(&tcm_msg->in); + + /* expose the read / write operations */ + tcm_dev->read_message = syna_tcm_v1_read_message; + tcm_dev->write_message = syna_tcm_v1_write_message; + /* if all crc bytes belong to EOM, crc feature is yet enabled */ + if (tcm_dev->msg_data.crc_bytes == 0x5a5a) + tcm_msg->has_crc = false; + + LOGI("Message CRC appending is %s\n", + (tcm_msg->has_crc) ? "enabled" : "disabled"); + + return retval; +} diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_core_v2.c b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_core_v2.c new file mode 100644 index 000000000000..edfeecbb64c6 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_core_v2.c @@ -0,0 +1,1352 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file synaptics_touchcom_core_v2.c + * + * This file implements the TouchComm version 2 command-response protocol + */ + +#include "synaptics_touchcom_core_dev.h" + +#define BITS_IN_MESSAGE_HEADER (MESSAGE_HEADER_SIZE * 8) + +#define HOST_PRIMARY (0) + +#define COMMAND_RETRY_TIMES (5) + +#define CHECK_PACKET_CRC + +/** + * @section: Header of TouchComm v2 Message Packet + * + * The 4-byte header in the TouchComm v2 packet + */ +struct tcm_v2_message_header { + union { + struct { + unsigned char code; + unsigned char length[2]; + unsigned char byte3; + }; + unsigned char data[MESSAGE_HEADER_SIZE]; + }; +}; + +/* helper to execute a tcm v2 command + */ +static int syna_tcm_v2_execute_cmd_request(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *payload, + unsigned int payload_length); + +/** + * syna_tcm_v2_set_max_read_size() + * + * Configure the max length for message reading. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * none. + */ +static int syna_tcm_v2_set_max_read_size(struct tcm_dev *tcm_dev) +{ + int retval; + unsigned int rd_size; + struct tcm_identification_info *id_info; + unsigned char data[2] = { 0 }; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + id_info = &tcm_dev->id_info; + + rd_size = syna_pal_le2_to_uint(id_info->max_read_size); + + if (rd_size == 0) { + LOGE("Invalid max_read_length: %d\n", rd_size); + return 0; + } + + if (rd_size == tcm_dev->max_rd_size) + return 0; + + tcm_dev->max_rd_size = MIN(rd_size, tcm_dev->max_rd_size); + + LOGD("max_rd_size = %d\n", tcm_dev->max_rd_size); + + data[0] = (unsigned char)tcm_dev->max_rd_size; + data[1] = (unsigned char)(tcm_dev->max_rd_size >> 8); + + retval = syna_tcm_v2_execute_cmd_request(tcm_dev, + CMD_TCM2_SET_MAX_READ_LENGTH, + data, + sizeof(data)); + if (retval < 0) { + LOGE("Fail to set max_read_length\n"); + return retval; + } + + return 0; +} + +/** + * syna_tcm_v2_parse_idinfo() + * + * Copy the given data to the identification info structure + * and parse the basic information, e.g. fw build id. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] data: data buffer + * [ in] size: size of given data buffer + * [ in] data_len: length of actual data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v2_parse_idinfo(struct tcm_dev *tcm_dev, + unsigned char *data, unsigned int size, unsigned int data_len) +{ + int retval; + unsigned int wr_size = 0; + unsigned int build_id = 0; + struct tcm_identification_info *id_info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if ((!data) || (data_len == 0)) { + LOGE("Invalid given data buffer\n"); + return _EINVAL; + } + + id_info = &tcm_dev->id_info; + + retval = syna_pal_mem_cpy((unsigned char *)id_info, + sizeof(struct tcm_identification_info), + data, + size, + MIN(sizeof(*id_info), data_len)); + if (retval < 0) { + LOGE("Fail to copy identification info\n"); + return retval; + } + + build_id = syna_pal_le4_to_uint(id_info->build_id); + + wr_size = syna_pal_le2_to_uint(id_info->max_write_size); + tcm_dev->max_wr_size = MIN(wr_size, WR_CHUNK_SIZE); + if (tcm_dev->max_wr_size == 0) { + tcm_dev->max_wr_size = wr_size; + LOGD("max_wr_size = %d\n", tcm_dev->max_wr_size); + } + + LOGI("TCM Fw mode: 0x%02x\n", id_info->mode); + + if (tcm_dev->packrat_number != build_id) + tcm_dev->packrat_number = build_id; + + /* set up the max. reading length */ + retval = syna_tcm_v2_set_max_read_size(tcm_dev); + if (retval < 0) { + LOGE("Fail to setup the max reading length\n"); + return retval; + } + + tcm_dev->dev_mode = id_info->mode; + + return 0; +} + +/** + * syna_tcm_v2_dispatch_report() + * + * Handle the TouchCom report packet being received. + * + * If it's an identify report, parse the identification packet and signal + * the command completion just in case. + * Otherwise, copy the data from internal buffer.in to internal buffer.report + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * none. + */ +static void syna_tcm_v2_dispatch_report(struct tcm_dev *tcm_dev) +{ + int retval; + struct tcm_message_data_blob *tcm_msg = NULL; + syna_pal_completion_t *cmd_completion = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return; + } + + tcm_msg = &tcm_dev->msg_data; + cmd_completion = &tcm_msg->cmd_completion; + + tcm_msg->report_code = tcm_msg->status_report_code; + + if (tcm_msg->payload_length == 0) { + tcm_dev->resp_buf.data_length = tcm_msg->payload_length; + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE); + goto exit; + } + + /* The identify report may be resulted from reset or fw mode switching + */ + if (tcm_msg->report_code == REPORT_IDENTIFY) { + + syna_tcm_buf_lock(&tcm_msg->in); + + retval = syna_tcm_v2_parse_idinfo(tcm_dev, + &tcm_msg->in.buf[MESSAGE_HEADER_SIZE], + tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to identify device\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + return; + } + + syna_tcm_buf_unlock(&tcm_msg->in); + + /* in case, the identify info packet is caused by the command */ + if (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_BUSY) { + switch (tcm_msg->command) { + case CMD_RESET: + LOGD("Reset by CMD_RESET\n"); + case CMD_REBOOT_TO_ROM_BOOTLOADER: + case CMD_RUN_BOOTLOADER_FIRMWARE: + case CMD_RUN_APPLICATION_FIRMWARE: + case CMD_ENTER_PRODUCTION_TEST_MODE: + case CMD_ROMBOOT_RUN_BOOTLOADER_FIRMWARE: + tcm_msg->status_report_code = STATUS_OK; + tcm_msg->response_code = STATUS_OK; + ATOMIC_SET(tcm_msg->command_status, + CMD_STATE_IDLE); + syna_pal_completion_complete(cmd_completion); + goto exit; + default: + LOGN("Device has been reset\n"); + ATOMIC_SET(tcm_msg->command_status, + CMD_STATE_ERROR); + syna_pal_completion_complete(cmd_completion); + goto exit; + } + } else { + /* invoke callback to handle unexpected reset if doesn't + * result from command + */ + if (tcm_dev->cb_reset_occurrence) + tcm_dev->cb_reset_occurrence(tcm_dev->cbdata_reset); + } + } + + /* store the received report into the internal buffer.report */ + syna_tcm_buf_lock(&tcm_dev->report_buf); + + retval = syna_tcm_buf_alloc(&tcm_dev->report_buf, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf.report\n"); + syna_tcm_buf_unlock(&tcm_dev->report_buf); + goto exit; + } + + syna_tcm_buf_lock(&tcm_msg->in); + + retval = syna_pal_mem_cpy(tcm_dev->report_buf.buf, + tcm_dev->report_buf.buf_size, + &tcm_msg->in.buf[MESSAGE_HEADER_SIZE], + tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to copy payload to buf_report\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + syna_tcm_buf_unlock(&tcm_dev->report_buf); + goto exit; + } + + tcm_dev->report_buf.data_length = tcm_msg->payload_length; + + syna_tcm_buf_unlock(&tcm_msg->in); + syna_tcm_buf_unlock(&tcm_dev->report_buf); + +exit: + return; +} + +/** + * syna_tcm_v2_dispatch_response() + * + * Handle the response packet. + * + * Copy the data from internal buffer.in to internal buffer.resp, + * and then signal the command completion. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * none. + */ +static void syna_tcm_v2_dispatch_response(struct tcm_dev *tcm_dev) +{ + int retval; + unsigned int resp_data_length; + struct tcm_message_data_blob *tcm_msg = NULL; + syna_pal_completion_t *cmd_completion = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return; + } + + tcm_msg = &tcm_dev->msg_data; + cmd_completion = &tcm_msg->cmd_completion; + + if (ATOMIC_GET(tcm_msg->command_status) != CMD_STATE_BUSY) + return; + + resp_data_length = tcm_msg->payload_length; + + if (resp_data_length == 0) { + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE); + goto exit; + } + + /* store the received report into the temporary buffer */ + syna_tcm_buf_lock(&tcm_dev->resp_buf); + + retval = syna_tcm_buf_alloc(&tcm_dev->resp_buf, + resp_data_length + 1); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf.resp\n"); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_ERROR); + goto exit; + } + + syna_tcm_buf_lock(&tcm_msg->in); + + retval = syna_pal_mem_cpy(tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + &tcm_msg->in.buf[MESSAGE_HEADER_SIZE], + tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE, + resp_data_length); + if (retval < 0) { + LOGE("Fail to copy payload to internal resp_buf\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_ERROR); + goto exit; + } + + tcm_dev->resp_buf.data_length = resp_data_length; + + syna_tcm_buf_unlock(&tcm_msg->in); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE); + +exit: + syna_pal_completion_complete(cmd_completion); +} + +/** + * syna_tcm_v2_read() + * + * Read in a TouchCom packet from device. + * Checking the CRC is necessary to ensure a valid message received. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] rd_length: number of reading bytes; + * '0' means to read the message header only + * [out] buf: pointer to a buffer which is stored the retrieved data + * [out] buf_size: size of the buffer pointed + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v2_read(struct tcm_dev *tcm_dev, unsigned int rd_length, + unsigned char **buf, unsigned int *buf_size) +{ + int retval; + struct tcm_v2_message_header *header; + int max_rd_size; + int xfer_len; + unsigned char crc6 = 0; + unsigned short crc16 = 0xFFFF; + struct tcm_message_data_blob *tcm_msg = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + max_rd_size = tcm_dev->max_rd_size; + + /* continued packet crc if containing payload data */ + xfer_len = (rd_length > 0) ? (rd_length + 2) : rd_length; + xfer_len += sizeof(struct tcm_v2_message_header); + + if ((max_rd_size != 0) && (xfer_len > max_rd_size)) { + LOGE("Invalid xfer length, len: %d, max_rd_size: %d\n", + xfer_len, max_rd_size); + tcm_msg->status_report_code = STATUS_INVALID; + return _EINVAL; + } + + syna_tcm_buf_lock(&tcm_msg->temp); + + /* allocate the internal temp buffer */ + retval = syna_tcm_buf_alloc(&tcm_msg->temp, xfer_len); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf.temp\n"); + goto exit; + } + /* read data from the bus */ + retval = syna_tcm_read(tcm_dev, + tcm_msg->temp.buf, + xfer_len); + if (retval < 0) { + LOGE("Fail to read from device\n"); + goto exit; + } + + header = (struct tcm_v2_message_header *)tcm_msg->temp.buf; + + /* check header crc always */ + crc6 = syna_tcm_crc6(header->data, BITS_IN_MESSAGE_HEADER); + if (crc6 != 0) { + LOGE("Invalid header crc: 0x%02x\n", (header->byte3 & 0x3f)); + + tcm_msg->status_report_code = STATUS_PACKET_CORRUPTED; + goto exit; + } + +#ifdef CHECK_PACKET_CRC + /* check packet crc */ + if (rd_length > 0) { + crc16 = syna_tcm_crc16(&tcm_msg->temp.buf[0], xfer_len, 0xFFFF); + if (crc16 != 0) { + LOGE("Invalid packet crc: %02x %02x\n", + tcm_msg->temp.buf[xfer_len - 2], + tcm_msg->temp.buf[xfer_len - 1]); + + tcm_msg->status_report_code = STATUS_PACKET_CORRUPTED; + goto exit; + } + + tcm_msg->crc_bytes = tcm_msg->temp.buf[xfer_len - 2] + + (tcm_msg->temp.buf[xfer_len - 1] << 8); + } +#endif + + tcm_msg->status_report_code = header->code; + + tcm_msg->payload_length = syna_pal_le2_to_uint(header->length); + + if (tcm_msg->status_report_code != STATUS_IDLE) + LOGD("Status code: 0x%02x, length: %d (%02x %02x %02x %02x)\n", + tcm_msg->status_report_code, tcm_msg->payload_length, + header->data[0], header->data[1], header->data[2], + header->data[3]); + + *buf = tcm_msg->temp.buf; + *buf_size = tcm_msg->temp.buf_size; + +exit: + syna_tcm_buf_unlock(&tcm_msg->temp); + + return retval; +} + +/** + * syna_tcm_v2_write() + * + * Construct the TouchCom v2 packet and send it to device. + * Add 4-byte header at the beginning of a message and appended crc if needed. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: command code + * [ in] payload: data payload if any + * [ in] payload_len: length of data payload if have any + * [ in] resend: flag for re-sending the packet + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v2_write(struct tcm_dev *tcm_dev, unsigned char command, + unsigned char *payload, unsigned int payload_len, bool resend) +{ + int retval; + struct tcm_v2_message_header *header; + unsigned char bits = BITS_IN_MESSAGE_HEADER - 6; + int xfer_len; + int size = MESSAGE_HEADER_SIZE + payload_len; + unsigned short crc16; + int max_wr_size; + struct tcm_message_data_blob *tcm_msg = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + max_wr_size = tcm_dev->max_wr_size; + + /* continued packet crc if containing payload data */ + xfer_len = (payload_len > 0) ? (payload_len + 2) : payload_len; + xfer_len += sizeof(struct tcm_v2_message_header); + + if ((max_wr_size != 0) && (xfer_len > max_wr_size)) { + LOGE("Invalid xfer length, len: %d, max_wr_size: %d\n", + xfer_len, max_wr_size); + tcm_msg->status_report_code = STATUS_INVALID; + return _EINVAL; + } + + syna_tcm_buf_lock(&tcm_msg->out); + + /* allocate the internal out buffer */ + retval = syna_tcm_buf_alloc(&tcm_msg->out, xfer_len); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf.out\n"); + goto exit; + } + + /* construct packet header */ + header = (struct tcm_v2_message_header *)tcm_msg->out.buf; + + if (resend) + tcm_msg->seq_toggle -= 1; + + header->code = command; + header->length[0] = (unsigned char)payload_len; + header->length[1] = (unsigned char)(payload_len >> 8); + header->byte3 = ((HOST_PRIMARY & 0x01) << 7); + header->byte3 |= ((tcm_msg->seq_toggle++ & 0x01) << 6); + header->byte3 |= syna_tcm_crc6(header->data, bits); + + /* copy payload, if any */ + if (payload_len) { + retval = syna_pal_mem_cpy( + &tcm_msg->out.buf[MESSAGE_HEADER_SIZE], + tcm_msg->out.buf_size - MESSAGE_HEADER_SIZE, + payload, + payload_len, + payload_len); + if (retval < 0) { + LOGE("Fail to copy payload data\n"); + goto exit; + } + + /* append packet crc */ + crc16 = syna_tcm_crc16(&tcm_msg->out.buf[0], size, 0xFFFF); + tcm_msg->out.buf[size] = (unsigned char)((crc16 >> 8) & 0xFF); + tcm_msg->out.buf[size + 1] = (unsigned char)(crc16 & 0xFF); + } + + /* write command packet to the bus */ + retval = syna_tcm_write(tcm_dev, + tcm_msg->out.buf, + xfer_len); + if (retval < 0) { + LOGE("Fail to write to device\n"); + goto exit; + } + +exit: + syna_tcm_buf_unlock(&tcm_msg->out); + + return retval; +} + +/** + * syna_tcm_v2_continued_read() + * + * Write a CMD_ACK to read in the remaining data payload continuously + * until the end of data. All the retrieved data is appended to the + * internal buffer.in. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] length: remaining data length in bytes + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v2_continued_read(struct tcm_dev *tcm_dev, + unsigned int length) +{ + int retval; + unsigned char *tmp_buf; + unsigned int tmp_buf_size; + int retry_cnt = 0; + unsigned int idx; + unsigned int offset; + unsigned int chunks; + unsigned int chunk_space; + unsigned int xfer_length; + unsigned int total_length; + unsigned int remaining_length; + unsigned char command; + struct tcm_message_data_blob *tcm_msg = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + + /* continued read packet contains the header and its payload */ + total_length = MESSAGE_HEADER_SIZE + tcm_msg->payload_length; + + remaining_length = length; + + offset = tcm_msg->payload_length - length; + + syna_tcm_buf_lock(&tcm_msg->in); + + /* extend the internal buf_in if needed */ + retval = syna_tcm_buf_realloc(&tcm_msg->in, + total_length + 1); + if (retval < 0) { + LOGE("Fail to allocate memory for internal buf_in\n"); + goto exit; + } + + /* available space for payload = total chunk size - header - crc */ + chunk_space = tcm_dev->max_rd_size; + if (chunk_space == 0) + chunk_space = remaining_length; + else + chunk_space = chunk_space - (MESSAGE_HEADER_SIZE + 2); + + chunks = syna_pal_ceil_div(remaining_length, chunk_space); + chunks = chunks == 0 ? 1 : chunks; + + offset += MESSAGE_HEADER_SIZE; + + /* send CMD_ACK for a continued read */ + command = CMD_TCM2_ACK; + + for (idx = 0; idx < chunks; idx++) { +retry: + LOGD("Command: 0x%02x\n", command); + + /* construct the command packet */ + retval = syna_tcm_v2_write(tcm_dev, + command, + NULL, + 0, + false); + if (retval < 0) { + LOGE("Fail to send CMD_TCM2_ACK in continued read\n"); + goto exit; + } + + if (remaining_length > chunk_space) + xfer_length = chunk_space; + else + xfer_length = remaining_length; + + /* read in the requested size of data */ + retval = syna_tcm_v2_read(tcm_dev, + xfer_length, + &tmp_buf, + &tmp_buf_size); + if (retval < 0) { + LOGE("Fail to read %d bytes from device\n", + xfer_length); + goto exit; + } + + /* If see an error, retry the previous read transaction + * Send RETRY instead of CMD_ACK + */ + if (tcm_msg->status_report_code == STATUS_PACKET_CORRUPTED) { + if (retry_cnt > COMMAND_RETRY_TIMES) { + LOGE("Continued read packet corrupted\n"); + goto exit; + } + + retry_cnt += 1; + command = CMD_TCM2_RETRY; + + LOGW("Read corrupted, retry %d\n", retry_cnt); + goto retry; + } + + retry_cnt = 0; + command = CMD_TCM2_ACK; + + /* append data from temporary buffer to in_buf */ + syna_tcm_buf_lock(&tcm_msg->temp); + + /* copy data from internal buffer.temp to buffer.in */ + retval = syna_pal_mem_cpy(&tcm_msg->in.buf[offset], + tcm_msg->in.buf_size - offset, + &tmp_buf[MESSAGE_HEADER_SIZE], + tmp_buf_size - MESSAGE_HEADER_SIZE, + xfer_length); + if (retval < 0) { + LOGE("Fail to copy payload to internal buf_in\n"); + syna_tcm_buf_unlock(&tcm_msg->temp); + goto exit; + } + + syna_tcm_buf_unlock(&tcm_msg->temp); + + remaining_length -= xfer_length; + + offset += xfer_length; + } + + retval = 0; + +exit: + syna_tcm_buf_unlock(&tcm_msg->in); + + return retval; +} + +/** + * syna_tcm_v2_get_response() + * + * Read in the response packet from device. + * If containing payload data, use continued_read() function and read the + * remaining payload data. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v2_get_response(struct tcm_dev *tcm_dev) +{ + int retval; + struct tcm_v2_message_header *header; + unsigned char *tmp_buf; + unsigned int tmp_buf_size; + struct tcm_message_data_blob *tcm_msg = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + + /* read in the message header at first */ + retval = syna_tcm_v2_read(tcm_dev, + 0, + &tmp_buf, + &tmp_buf_size); + if (retval < 0) { + LOGE("Fail to read message header from device\n"); + return retval; + } + + /* error out once the response packet is corrupted */ + if (tcm_msg->status_report_code == STATUS_PACKET_CORRUPTED) + return 0; + + + /* allocate the required space = header + payload */ + syna_tcm_buf_lock(&tcm_msg->in); + + retval = syna_tcm_buf_alloc(&tcm_msg->in, + MESSAGE_HEADER_SIZE + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to reallocate memory for internal buf.in\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + return retval; + } + + retval = syna_pal_mem_cpy(tcm_msg->in.buf, + tcm_msg->in.buf_size, + tmp_buf, + tmp_buf_size, + MESSAGE_HEADER_SIZE); + if (retval < 0) { + LOGE("Fail to copy data to internal buf_in\n"); + syna_tcm_buf_unlock(&tcm_msg->in); + return retval; + } + + syna_tcm_buf_unlock(&tcm_msg->in); + + /* read in payload, if any */ + if (tcm_msg->payload_length > 0) { + + retval = syna_tcm_v2_continued_read(tcm_dev, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to read in payload data, size: %d)\n", + tcm_msg->payload_length); + return retval; + } + } + + syna_tcm_buf_lock(&tcm_msg->in); + + header = (struct tcm_v2_message_header *)tcm_msg->in.buf; + + tcm_msg->payload_length = syna_pal_le2_to_uint(header->length); + tcm_msg->status_report_code = header->code; + + syna_tcm_buf_unlock(&tcm_msg->in); + + return retval; +} + +/** + * syna_tcm_v2_send_cmd() + * + * Forward the given command and payload to syna_tcm_v2_write(). + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: command code + * [ in] payload: data payload if any + * [ in] payload_len: length of data payload if have any + * [ in] resend: flag for re-sending the packet + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static inline int syna_tcm_v2_send_cmd(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *payload, + unsigned int length, bool resend) +{ + int retval; + + retval = syna_tcm_v2_write(tcm_dev, + command, + payload, + length, + resend); + if (retval < 0) + LOGE("Fail to write Command 0x%02x to device\n", command); + + return retval; +} + +/** + * syna_tcm_v2_execute_cmd_request() + * + * Process the command message. + * The helper is responsible for sending the given command and its payload, + * to device. Once the total size of message is over the wr_chunk, divide + * into continued writes + * + * In addition, the response to the command generated by the device will be + * read in immediately. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: command code + * [ in] payload: data payload if any + * [ in] payload_length: length of payload in bytes + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_v2_execute_cmd_request(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *payload, + unsigned int payload_length) +{ + int retval; + unsigned int idx; + unsigned int offset; + unsigned int chunks; + unsigned int xfer_length; + unsigned int remaining_length; + int retry_cnt = 0; + unsigned int chunk_space; + struct tcm_message_data_blob *tcm_msg = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + chunk_space = tcm_dev->max_wr_size; + + remaining_length = payload_length; + + /* available space for payload = total size - header - crc */ + if (chunk_space == 0) + chunk_space = remaining_length; + else + chunk_space = chunk_space - (MESSAGE_HEADER_SIZE + 2); + + chunks = syna_pal_ceil_div(remaining_length, chunk_space); + + chunks = chunks == 0 ? 1 : chunks; + + offset = 0; + + /* process the command message and handle the response + * to the command + */ + for (idx = 0; idx < chunks; idx++) { + if (remaining_length > chunk_space) + xfer_length = chunk_space; + else + xfer_length = remaining_length; + +retry: + /* send command to device */ + command = (idx == 0) ? command : CMD_CONTINUE_WRITE; + + LOGD("Command: 0x%02x\n", command); + + retval = syna_tcm_v2_send_cmd(tcm_dev, + command, + &payload[offset], + xfer_length, + (retry_cnt > 0)); + if (retval < 0) + goto exit; + + /* bus turnaround delay */ + syna_pal_sleep_us(TAT_DELAY_US_MIN, TAT_DELAY_US_MAX); + + /* get the response to the command immediately */ + retval = syna_tcm_v2_get_response(tcm_dev); + if (retval < 0) { + LOGE("Fail to get the response to command 0x%02x\n", + command); + goto exit; + } + + /* check the response code */ + tcm_msg->response_code = tcm_msg->status_report_code; + + LOGD("Response code: 0x%x\n", tcm_msg->response_code); + + if (tcm_msg->status_report_code >= REPORT_IDENTIFY) + goto next; + + switch (tcm_msg->status_report_code) { + case STATUS_NO_REPORT_AVAILABLE: + case STATUS_OK: + case STATUS_ACK: + retry_cnt = 0; + break; + case STATUS_PACKET_CORRUPTED: + case STATUS_RETRY_REQUESTED: + retry_cnt += 1; + break; + default: + LOGE("Incorrect status code 0x%02x of command 0x%02x\n", + tcm_msg->status_report_code, command); + goto exit; + } + + if (retry_cnt > 0) { + if (command == CMD_RESET) { + LOGE("Command CMD_RESET corrupted, exit\n"); + /* assume ACK and wait for interrupt assertion + * once the response of reset is corrupted + */ + tcm_msg->response_code = STATUS_ACK; + goto exit; + } else if (retry_cnt > COMMAND_RETRY_TIMES) { + LOGE("Command 0x%02x corrupted\n", command); + goto exit; + } + + LOGN("Command 0x%02x, retry %d\n", command, retry_cnt); + syna_pal_sleep_us(WR_DELAY_US_MIN, WR_DELAY_US_MAX); + + goto retry; + } +next: + offset += xfer_length; + + remaining_length -= xfer_length; + + if (chunks > 1) + syna_pal_sleep_us(WR_DELAY_US_MIN, WR_DELAY_US_MAX); + } + +exit: + return retval; +} + +/** + * syna_tcm_v2_read_message() + * + * Send a CMD_GET_REPORT to acquire a TouchCom v2 report packet from device. + * Meanwhile, the retrieved data will be stored in the internal buffer.resp + * or buffer.report. + * + * @param + * [ in] tcm_dev: the device handle + * [out] status_report_code: status code or report code received + * + * @return + * 0 or positive value on success; otherwise, on error. + */ +static int syna_tcm_v2_read_message(struct tcm_dev *tcm_dev, + unsigned char *status_report_code) +{ + int retval; + struct tcm_message_data_blob *tcm_msg = NULL; + syna_pal_mutex_t *rw_mutex = NULL; + syna_pal_completion_t *cmd_completion = NULL; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + rw_mutex = &tcm_msg->rw_mutex; + cmd_completion = &tcm_msg->cmd_completion; + + if (status_report_code) + *status_report_code = STATUS_INVALID; + + tcm_msg->crc_bytes = 0; + + syna_pal_mutex_lock(rw_mutex); + + /* request a command */ + retval = syna_tcm_v2_execute_cmd_request(tcm_dev, + CMD_TCM2_GET_REPORT, + NULL, + 0); + if (retval < 0) { + LOGE("Fail to send command CMD_TCM2_GET_REPORT\n"); + + if (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_BUSY) { + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_ERROR); + syna_pal_completion_complete(cmd_completion); + } + goto exit; + } + + /* duplicate the data to external buffer */ + syna_tcm_buf_lock(&tcm_dev->external_buf); + if (tcm_msg->payload_length > 0) { + retval = syna_tcm_buf_alloc(&tcm_dev->external_buf, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to allocate memory, external_buf invalid\n"); + syna_tcm_buf_unlock(&tcm_dev->external_buf); + goto exit; + } else { + retval = syna_pal_mem_cpy(&tcm_dev->external_buf.buf[0], + tcm_msg->payload_length, + &tcm_msg->in.buf[MESSAGE_HEADER_SIZE], + tcm_msg->in.buf_size - MESSAGE_HEADER_SIZE, + tcm_msg->payload_length); + if (retval < 0) { + LOGE("Fail to copy data to external buffer\n"); + syna_tcm_buf_unlock(&tcm_dev->external_buf); + goto exit; + } + } + } + tcm_dev->external_buf.data_length = tcm_msg->payload_length; + syna_tcm_buf_unlock(&tcm_dev->external_buf); + + if (tcm_msg->response_code == STATUS_NO_REPORT_AVAILABLE) + goto exit; + + /* process the retrieved packet */ + if (tcm_msg->status_report_code >= REPORT_IDENTIFY) + syna_tcm_v2_dispatch_report(tcm_dev); + else + syna_tcm_v2_dispatch_response(tcm_dev); + + /* copy the status report code to caller */ + if (status_report_code) + *status_report_code = tcm_msg->status_report_code; + +exit: + syna_pal_mutex_unlock(rw_mutex); + + return retval; +} + +/** + * syna_tcm_v2_write_message() + * + * Write message including command and its payload to TouchCom device. + * Then, the response of the command generated by the device will be + * read in and stored in internal buffer.resp. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: TouchComm command + * [ in] payload: data payload, if any + * [ in] payload_length: length of data payload, if any + * [out] resp_code: response code returned + * [ in] delay_ms_resp: delay time for response reading. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * + * @return + * 0 or positive value on success; otherwise, on error. + */ +static int syna_tcm_v2_write_message(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *payload, + unsigned int payload_length, unsigned char *resp_code, + unsigned int delay_ms_resp) +{ + int retval; + int timeout = 0; + int polling_ms = 0; + struct tcm_message_data_blob *tcm_msg = NULL; + syna_pal_mutex_t *cmd_mutex = NULL; + syna_pal_mutex_t *rw_mutex = NULL; + syna_pal_completion_t *cmd_completion = NULL; + bool has_irq_ctrl = false; + bool in_polling = false; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_msg = &tcm_dev->msg_data; + cmd_mutex = &tcm_msg->cmd_mutex; + rw_mutex = &tcm_msg->rw_mutex; + cmd_completion = &tcm_msg->cmd_completion; + + if (resp_code) + *resp_code = STATUS_INVALID; + + /* indicate which mode is used */ + in_polling = (delay_ms_resp != RESP_IN_ATTN); + + /* irq control is enabled only when the operations is implemented + * and the current status of irq is enabled. + * do not enable irq if it is disabled by someone. + */ + has_irq_ctrl = (bool)(tcm_dev->hw_if->ops_enable_irq != NULL); + has_irq_ctrl &= tcm_dev->hw_if->bdata_attn.irq_enabled; + + /* disable irq when using polling mode */ + if (has_irq_ctrl && in_polling && tcm_dev->hw_if->ops_enable_irq) + tcm_dev->hw_if->ops_enable_irq(tcm_dev->hw_if, false); + + LOGD("write command: 0x%02x, payload size: %d\n", + command, payload_length); + + syna_pal_mutex_lock(cmd_mutex); + + syna_pal_mutex_lock(rw_mutex); + + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_BUSY); + + /* reset the command completion */ + syna_pal_completion_reset(cmd_completion); + + tcm_msg->command = command; + + /* request a command execution */ + retval = syna_tcm_v2_execute_cmd_request(tcm_dev, + command, + payload, + payload_length); + if (retval < 0) { + LOGE("Fail to send command 0x%02x to device\n", command); + goto exit; + } + + syna_pal_mutex_unlock(rw_mutex); + + /* waiting for the resp data only at STATUS_ACK */ + if (tcm_msg->response_code != STATUS_ACK) { + syna_tcm_v2_dispatch_response(tcm_dev); + + goto check_response; + } + + /* handle the report generated by the command + * + * assuming to select the polling mode, the while-loop below will + * repeatedly read in the respose data based on the given polling + * time; otherwise, wait until receiving a completion event from + * interupt thread. + */ + timeout = 0; + if (!in_polling) + polling_ms = CMD_RESPONSE_TIMEOUT_MS; + else + polling_ms = delay_ms_resp; + + do { + /* wait for the completion event triggered by read_message */ + retval = syna_pal_completion_wait_for(cmd_completion, + polling_ms); + /* reset the status when times out and keep in polling */ + if (retval < 0) + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_BUSY); + + /* break when geting a valid resp; otherwise, keep in polling */ + if (ATOMIC_GET(tcm_msg->command_status) == CMD_STATE_IDLE) + goto check_response; + + if (in_polling) { + /* retrieve the message packet back */ + retval = syna_tcm_v2_read_message(tcm_dev, NULL); + /* keep in polling if still not having a valid resp */ + if (retval < 0) + syna_pal_completion_reset(cmd_completion); + } + + timeout += polling_ms + 10; + + } while (timeout < CMD_RESPONSE_TIMEOUT_MS); + + /* check the status of response data + * according to the touchcomm spec, each command message + * should have an associated reponse message. + */ +check_response: + if (ATOMIC_GET(tcm_msg->command_status) != CMD_STATE_IDLE) { + if (timeout >= CMD_RESPONSE_TIMEOUT_MS) { + LOGE("Timed out wait for response of command 0x%02x\n", + command); + retval = _ETIMEDOUT; + goto exit; + } else { + LOGE("Fail to get valid response of command 0x%02x\n", + command); + retval = _EIO; + goto exit; + } + } + + /* copy response code to the caller */ + if (resp_code) + *resp_code = tcm_msg->response_code; + + if (tcm_msg->response_code != STATUS_OK) { + LOGE("Error code 0x%02x of command 0x%02x\n", + tcm_msg->response_code, tcm_msg->command); + retval = _EIO; + } else { + retval = 0; + } + +exit: + tcm_msg->command = CMD_NONE; + + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE); + + syna_pal_mutex_unlock(cmd_mutex); + + /* recovery the irq if using polling mode */ + if (has_irq_ctrl && in_polling && tcm_dev->hw_if->ops_enable_irq) + tcm_dev->hw_if->ops_enable_irq(tcm_dev->hw_if, true); + + return retval; +} + + /** + * syna_tcm_v2_detect() + * + * For TouchCom v2 protocol, the given data must have a valid crc-6 at the end. + * If so, send an identify command to identify the device. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] data: raw 4-byte data + * [ in] size: length of input data in bytes + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_v2_detect(struct tcm_dev *tcm_dev, unsigned char *data, + unsigned int size) +{ + int retval; + unsigned char resp_code = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if ((!data) || (size < MESSAGE_HEADER_SIZE)) { + LOGE("Invalid parameters\n"); + return _EINVAL; + } + + if (syna_tcm_crc6(data, BITS_IN_MESSAGE_HEADER) != 0) + return _ENODEV; + + /* send an identify command to identify the device */ + retval = syna_tcm_v2_write_message(tcm_dev, + CMD_IDENTIFY, + NULL, + 0, + &resp_code, + RESP_IN_POLLING); + if (retval < 0) { + LOGE("Fail to get identification info from device\n"); + return retval; + } + + /* expose the read / write operations */ + tcm_dev->read_message = syna_tcm_v2_read_message; + tcm_dev->write_message = syna_tcm_v2_write_message; + + tcm_dev->msg_data.has_crc = true; + tcm_dev->msg_data.crc_bytes = 0xffff; + + return retval; +} diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_base.c b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_base.c new file mode 100644 index 000000000000..df9afb1a4ced --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_base.c @@ -0,0 +1,1785 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file synaptics_touchcom_func_base.c + * + * This file implements generic and foundational functions supported in + * Synaptics TouchComm communication protocol. + * + * The declarations are in synaptics_touchcom_func_base.h. + */ + +#include "synaptics_touchcom_func_base.h" +#include "synaptics_touchcom_func_touch.h" + + +/** + * syna_tcm_change_resp_read() + * + * Helper to change the default resp reading method, which was previously set + * when calling syna_tcm_allocate_device + * + * @param + * [in] tcm_dev: the device handle + * [in] request: resp reading method to change + * set '0' or 'RESP_IN_ATTN' for ATTN-driven; otherwise, + * assign a positive value standing for the polling time + * @return + * none. + */ +void syna_tcm_change_resp_read(struct tcm_dev *tcm_dev, unsigned int request) +{ + if (request == RESP_IN_ATTN) { + tcm_dev->msg_data.default_resp_reading = RESP_IN_ATTN; + + LOGI("Change default resp reading method by attn\n"); + } else { + if (request < RESP_IN_POLLING) + request = RESP_IN_POLLING; + + tcm_dev->msg_data.default_resp_reading = request; + + LOGI("Change default resp reading method by polling (%dms)\n", + tcm_dev->msg_data.default_resp_reading); + } +} + +/** + * syna_tcm_init_message_wrap() + * + * Initialize internal buffers and related structures for command processing. + * The function must be called to prepare all essential structures for + * command wrapper. + * + * @param + * [in] tcm_msg: message wrapper structure + * [in] resp_reading: default method to retrieve the resp data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_init_message_wrap(struct tcm_message_data_blob *tcm_msg, + unsigned int resp_reading) +{ + /* initialize internal buffers */ + syna_tcm_buf_init(&tcm_msg->in); + syna_tcm_buf_init(&tcm_msg->out); + syna_tcm_buf_init(&tcm_msg->temp); + + /* allocate the completion event for command processing */ + if (syna_pal_completion_alloc(&tcm_msg->cmd_completion) < 0) { + LOGE("Fail to allocate cmd completion event\n"); + return _EINVAL; + } + + /* allocate the cmd_mutex for command protection */ + if (syna_pal_mutex_alloc(&tcm_msg->cmd_mutex) < 0) { + LOGE("Fail to allocate cmd_mutex\n"); + return _EINVAL; + } + + /* allocate the rw_mutex for rw protection */ + if (syna_pal_mutex_alloc(&tcm_msg->rw_mutex) < 0) { + LOGE("Fail to allocate rw_mutex\n"); + return _EINVAL; + } + + /* set default state of command_status */ + ATOMIC_SET(tcm_msg->command_status, CMD_STATE_IDLE); + + /* allocate the internal buffer.in at first */ + syna_tcm_buf_lock(&tcm_msg->in); + + if (syna_tcm_buf_alloc(&tcm_msg->in, MESSAGE_HEADER_SIZE) < 0) { + LOGE("Fail to allocate memory for buf.in (size = %d)\n", + MESSAGE_HEADER_SIZE); + tcm_msg->in.buf_size = 0; + tcm_msg->in.data_length = 0; + syna_tcm_buf_unlock(&tcm_msg->in); + return _EINVAL; + } + tcm_msg->in.buf_size = MESSAGE_HEADER_SIZE; + + syna_tcm_buf_unlock(&tcm_msg->in); + + tcm_msg->default_resp_reading = resp_reading; + + LOGI("Resp reading method (default): %s\n", + (resp_reading == RESP_IN_ATTN) ? "attn" : "polling"); + + return 0; +} + +/** + * syna_tcm_del_message_wrap() + * + * Remove message wrapper interface and internal buffers. + * Call the function once the message wrapper is no longer needed. + * + * @param + * [in] tcm_msg: message wrapper structure + * + * @return + * none. + */ +static void syna_tcm_del_message_wrap(struct tcm_message_data_blob *tcm_msg) +{ + /* release the mutex */ + syna_pal_mutex_free(&tcm_msg->rw_mutex); + syna_pal_mutex_free(&tcm_msg->cmd_mutex); + + /* release the completion event */ + syna_pal_completion_free(&tcm_msg->cmd_completion); + + /* release internal buffers */ + syna_tcm_buf_release(&tcm_msg->temp); + syna_tcm_buf_release(&tcm_msg->out); + syna_tcm_buf_release(&tcm_msg->in); +} + +/** + * syna_tcm_allocate_device() + * + * Create the TouchCom core device handle. + * This function must be called in order to allocate the main device handle, + * structure syna_tcm_dev, which will be passed to all other operations and + * functions within the entire source code. + * + * Meanwhile, caller has to prepare specific syna_tcm_hw_interface structure, + * so that all the implemented functions can access hardware components + * through syna_tcm_hw_interface. + * + * @param + * [out] ptcm_dev_ptr: a pointer to the device handle returned + * [ in] hw_if: hardware-specific data on target platform + * [ in] resp_reading: default resp reading method + * set 'RESP_IN_ATTN' to apply ATTN-driven method; + * set 'RESP_IN_POLLING' to read in resp by polling + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_allocate_device(struct tcm_dev **ptcm_dev_ptr, + struct syna_hw_interface *hw_if, unsigned int resp_reading) +{ + int retval = 0; + struct tcm_dev *tcm_dev = NULL; + + if (!hw_if) { + LOGE("Invalid parameter of hw_if\n"); + return _EINVAL; + } + + if ((!hw_if->ops_read_data) || (!hw_if->ops_write_data)) { + LOGE("Invalid hw read write operation\n"); + return _EINVAL; + } + + *ptcm_dev_ptr = NULL; + + /* allocate the core device handle */ + tcm_dev = (struct tcm_dev *)syna_pal_mem_alloc( + 1, + sizeof(struct tcm_dev)); + if (!tcm_dev) { + LOGE("Fail to create tcm device handle\n"); + return _ENOMEM; + } + + /* link to the given hardware data */ + tcm_dev->hw_if = hw_if; + + tcm_dev->max_rd_size = hw_if->bdata_io.rd_chunk_size; + tcm_dev->max_wr_size = hw_if->bdata_io.wr_chunk_size; + + tcm_dev->write_message = NULL; + tcm_dev->read_message = NULL; + + /* allocate internal buffers */ + syna_tcm_buf_init(&tcm_dev->report_buf); + syna_tcm_buf_init(&tcm_dev->resp_buf); + syna_tcm_buf_init(&tcm_dev->external_buf); + syna_tcm_buf_init(&tcm_dev->touch_config); + + /* initialize the command wrapper interface */ + retval = syna_tcm_init_message_wrap(&tcm_dev->msg_data, + resp_reading); + if (retval < 0) { + LOGE("Fail to initialize command interface\n"); + goto err_init_message_wrap; + } + + /* return the created device handle */ + *ptcm_dev_ptr = tcm_dev; + + LOGI("TouchComm core module created, ver.: %d.%02d\n", + (unsigned char)(SYNA_TCM_CORE_LIB_VERSION >> 8), + (unsigned char)SYNA_TCM_CORE_LIB_VERSION & 0xff); + + LOGI("Capability: wr_chunk(%d), rd_chunk(%d), irq_control(%s)\n", + tcm_dev->max_wr_size, tcm_dev->max_rd_size, + (hw_if->ops_enable_irq) ? "yes" : "no"); + + return 0; + +err_init_message_wrap: + syna_tcm_buf_release(&tcm_dev->touch_config); + syna_tcm_buf_release(&tcm_dev->external_buf); + syna_tcm_buf_release(&tcm_dev->report_buf); + syna_tcm_buf_release(&tcm_dev->resp_buf); + + tcm_dev->hw_if = NULL; + + syna_pal_mem_free((void *)tcm_dev); + + return retval; +} + +/** + * syna_tcm_remove_device() + * + * Remove the TouchCom core device handler. + * This function must be invoked when the device is no longer needed. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * none. + */ +void syna_tcm_remove_device(struct tcm_dev *tcm_dev) +{ + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return; + } + + /* release the command interface */ + syna_tcm_del_message_wrap(&tcm_dev->msg_data); + + /* release buffers */ + syna_tcm_buf_release(&tcm_dev->touch_config); + syna_tcm_buf_release(&tcm_dev->external_buf); + syna_tcm_buf_release(&tcm_dev->report_buf); + syna_tcm_buf_release(&tcm_dev->resp_buf); + + tcm_dev->hw_if = NULL; + + /* release the device handle */ + syna_pal_mem_free((void *)tcm_dev); + + LOGI("tcm device handle removed\n"); +} + +/** + * syna_tcm_detect_protocol() + * + * Helper to distinguish which TouchCom firmware is running. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] data: raw data from device + * [ in] data_len: length of input data in bytes + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_detect_protocol(struct tcm_dev *tcm_dev, + unsigned char *data, unsigned int data_len) +{ + int retval; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + retval = syna_tcm_v2_detect(tcm_dev, data, data_len); + if (retval < 0) + retval = syna_tcm_v1_detect(tcm_dev, data, data_len); + + return retval; +} + +/** + * syna_tcm_detect_device() + * + * Determine the type of device being connected, and distinguish which + * version of TouchCom firmware running on the device. + * This function must be called before using this TouchComm core library. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * on success, the current mode running on the device is returned; + * otherwise, negative value on error. + */ +int syna_tcm_detect_device(struct tcm_dev *tcm_dev) +{ + int retval = 0; + unsigned char data[4] = { 0 }; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_dev->dev_mode = MODE_UNKNOWN; + + /* get the bare data from the bus directly */ + data[0] = 0x07; + retval = syna_tcm_write(tcm_dev, &data[0], 1); + if (retval < 0) { + LOGE("Fail to write magic to bus\n"); + return _EIO; + } + + retval = syna_tcm_read(tcm_dev, + data, (unsigned int)sizeof(data)); + if (retval < 0) { + LOGE("Fail to retrieve 4-byte data from bus\n"); + return _EIO; + } + + LOGD("bare data: %02x %02x %02x %02x\n", + data[0], data[1], data[2], data[3]); + + /* distinguish which tcm version running on the device */ + retval = syna_tcm_detect_protocol(tcm_dev, + data, (unsigned int)sizeof(data)); + if (retval < 0) { + LOGE("Fail to detect TouchCom device, %02x %02x %02x %02x\n", + data[0], data[1], data[2], data[3]); + return retval; + } + + if ((!tcm_dev->write_message) || (!tcm_dev->read_message)) { + LOGE("Invalid TouchCom rw operations\n"); + return _ENODEV; + } + + /* check the running mode */ + switch (tcm_dev->dev_mode) { + case MODE_APPLICATION_FIRMWARE: + LOGI("Device in Application FW, build id: %d, %s\n", + tcm_dev->packrat_number, + tcm_dev->id_info.part_number); + break; + case MODE_BOOTLOADER: + case MODE_TDDI_BOOTLOADER: + LOGI("Device in Bootloader\n"); + break; + case MODE_ROMBOOTLOADER: + LOGI("Device in ROMBoot uBL\n"); + break; + case MODE_MULTICHIP_TDDI_BOOTLOADER: + LOGI("Device in multi-chip TDDI Bootloader\n"); + break; + default: + LOGW("Found TouchCom device, but unsupported mode: 0x%02x\n", + tcm_dev->dev_mode); + break; + } + + retval = tcm_dev->dev_mode; + return retval; +} + +/** + * syna_tcm_get_event_data() + * + * Helper to read TouchComm messages when ATTN signal is asserted. + * After returning, the ATTN signal should be no longer asserted. + * + * The 'code' returned will guide the caller on the next action. + * For example, do touch reporting once returned code is equal to REPORT_TOUCH. + * + * @param + * [ in] tcm_dev: the device handle + * [out] code: received report code + * [out] data: a user buffer for data returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_event_data(struct tcm_dev *tcm_dev, + unsigned char *code, struct tcm_buffer *data) +{ + int retval = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!code) { + LOGE("Invalid parameter\n"); + return _EINVAL; + } + + /* retrieve the event data */ + retval = tcm_dev->read_message(tcm_dev, + code); + if (retval < 0) { + LOGE("Fail to read messages\n"); + return retval; + } + + /* exit if no buffer provided */ + if (!data) + goto exit; + + /* if gathering a report, copy to the user buffer */ + if ((*code >= REPORT_IDENTIFY) && (*code != STATUS_INVALID)) { + if (tcm_dev->report_buf.data_length == 0) + goto exit; + + syna_tcm_buf_lock(&tcm_dev->report_buf); + + retval = syna_tcm_buf_copy(data, &tcm_dev->report_buf); + if (retval < 0) { + LOGE("Fail to copy data, report type: %x\n", *code); + syna_tcm_buf_unlock(&tcm_dev->report_buf); + goto exit; + } + + syna_tcm_buf_unlock(&tcm_dev->report_buf); + } + + /* if gathering a response, copy to the user buffer */ + if ((*code > STATUS_IDLE) && (*code <= STATUS_ERROR)) { + if (tcm_dev->resp_buf.data_length == 0) + goto exit; + + syna_tcm_buf_lock(&tcm_dev->resp_buf); + + retval = syna_tcm_buf_copy(data, &tcm_dev->resp_buf); + if (retval < 0) { + LOGE("Fail to copy data, status code: %x\n", *code); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + goto exit; + } + + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + } + +exit: + return retval; +} + +/** + * syna_tcm_identify() + * + * Implement the standard command code, which is used to request + * an IDENTIFY report packet. + * + * @param + * [ in] tcm_dev: the device handle + * [out] id_info: the identification info packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_identify(struct tcm_dev *tcm_dev, + struct tcm_identification_info *id_info) +{ + int retval = 0; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_IDENTIFY, + NULL, + 0, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", CMD_IDENTIFY); + goto exit; + } + + tcm_dev->dev_mode = tcm_dev->id_info.mode; + + if (id_info == NULL) + goto show_info; + + /* copy identify info to caller */ + retval = syna_pal_mem_cpy((unsigned char *)id_info, + sizeof(struct tcm_identification_info), + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + MIN(sizeof(*id_info), tcm_dev->resp_buf.data_length)); + if (retval < 0) { + LOGE("Fail to copy identify info to caller\n"); + goto exit; + } + +show_info: + LOGI("TCM Fw mode: 0x%02x, TCM ver.: %d\n", + tcm_dev->id_info.mode, tcm_dev->id_info.version); + +exit: + return retval; +} + +/** + * syna_tcm_reset() + * + * Implement the standard command code, which is used to perform a sw reset + * immediately. After a successful reset, an IDENTIFY report to indicate that + * device is ready. + * + * Caller shall be aware that the firmware will be reloaded after reset. + * Therefore, if expecting that a different firmware version is loaded, please + * do app firmware setup after reset. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_reset(struct tcm_dev *tcm_dev) +{ + int retval = 0; + unsigned char resp_code; + unsigned int board_setting; + unsigned int resp_handling; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + resp_handling = tcm_dev->msg_data.default_resp_reading; + + /* gather the board settings of reset delay time */ + board_setting = tcm_dev->hw_if->bdata_rst.reset_delay_ms; + if (board_setting == 0) + board_setting = RESET_DELAY_MS; + + /* select the proper period to handle the resp of reset */ + if (resp_handling != RESP_IN_ATTN) { + if (board_setting > resp_handling) { + resp_handling = board_setting; + LOGI("Use board settings %dms to poll resp of reset\n", + resp_handling); + } + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_RESET, + NULL, + 0, + &resp_code, + resp_handling); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", CMD_RESET); + goto exit; + } + + /* current device mode is expected to be updated + * because identification report will be received after reset + */ + tcm_dev->dev_mode = tcm_dev->id_info.mode; + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGI("Device mode 0x%02X running after reset\n", + tcm_dev->dev_mode); + } + + retval = 0; +exit: + return retval; +} + +/** + * syna_tcm_enable_report() + * + * Implement the application fw command code to enable or disable a report. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] report_code: the requested report code being generated + * [ in] en: '1' for enabling; '0' for disabling + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_enable_report(struct tcm_dev *tcm_dev, + unsigned char report_code, bool en) +{ + int retval = 0; + unsigned char resp_code; + unsigned char command; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + command = (en) ? CMD_ENABLE_REPORT : CMD_DISABLE_REPORT; + + retval = tcm_dev->write_message(tcm_dev, + command, + &report_code, + 1, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x to %s 0x%02x report\n", + command, (en)?"enable":"disable", report_code); + goto exit; + } + + if (resp_code != STATUS_OK) { + LOGE("Fail to %s 0x%02x report, resp_code:%x\n", + (en) ? "enable" : "disable", report_code, resp_code); + } else { + LOGD("Report 0x%x %s\n", report_code, + (en) ? "enabled" : "disabled"); + } + +exit: + return retval; +} + +/** + * syna_tcm_run_rom_bootloader_fw() + * + * Requests that the rombootloader firmware be run. + * Once the rombootloader firmware has finished starting, an IDENTIFY report + * to indicate that it is in the new mode. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] fw_switch_delay: delay time for fw mode switching. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_run_rom_bootloader_fw(struct tcm_dev *tcm_dev, + unsigned int fw_switch_delay) +{ + int retval = 0; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_REBOOT_TO_ROM_BOOTLOADER, + NULL, + 0, + &resp_code, + fw_switch_delay); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_REBOOT_TO_ROM_BOOTLOADER); + goto exit; + } + + if (!IS_ROM_BOOTLOADER_MODE(tcm_dev->dev_mode)) { + LOGE("Fail to enter rom bootloader, mode: %x\n", + tcm_dev->dev_mode); + retval = _ENODEV; + goto exit; + } + + LOGI("ROM Bootloader (mode 0x%x) activated\n", + tcm_dev->dev_mode); + + retval = 0; + +exit: + return retval; +} + +/** + * syna_tcm_run_bootloader_fw() + * + * Requests that the bootloader firmware be run. + * Once the bootloader firmware has finished starting, an IDENTIFY report + * to indicate that it is in the new mode. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] fw_switch_delay: delay time for fw mode switching. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_run_bootloader_fw(struct tcm_dev *tcm_dev, + unsigned int fw_switch_delay) +{ + int retval = 0; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_RUN_BOOTLOADER_FIRMWARE, + NULL, + 0, + &resp_code, + fw_switch_delay); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_RUN_BOOTLOADER_FIRMWARE); + goto exit; + } + + if (!IS_BOOTLOADER_MODE(tcm_dev->dev_mode)) { + LOGE("Fail to enter bootloader, mode: %x\n", + tcm_dev->dev_mode); + retval = _ENODEV; + goto exit; + } + + LOGI("Bootloader Firmware (mode 0x%x) activated\n", + tcm_dev->dev_mode); + + retval = 0; + +exit: + return retval; +} + +/** + * syna_tcm_run_application_fw() + * + * Requests that the application firmware be run. + * Once the application firmware has finished starting, an IDENTIFY report + * to indicate that it is in the new mode. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] fw_switch_delay: delay time for fw mode switching. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_run_application_fw(struct tcm_dev *tcm_dev, + unsigned int fw_switch_delay) +{ + int retval = 0; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_RUN_APPLICATION_FIRMWARE, + NULL, + 0, + &resp_code, + fw_switch_delay); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_RUN_APPLICATION_FIRMWARE); + goto exit; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGW("Fail to enter application fw, mode: %x\n", + tcm_dev->dev_mode); + retval = _ENODEV; + goto exit; + } + + LOGI("Application Firmware (mode 0x%x) activated\n", + tcm_dev->dev_mode); + + retval = 0; + +exit: + return retval; +} + +/** + * syna_tcm_switch_fw_mode() + * + * Implement the command code to switch the firmware mode. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] mode: target firmware mode + * [ in] fw_switch_delay: delay time for fw mode switching. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_switch_fw_mode(struct tcm_dev *tcm_dev, + unsigned char mode, unsigned int fw_switch_delay) +{ + int retval = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + switch (mode) { + case MODE_APPLICATION_FIRMWARE: + retval = syna_tcm_run_application_fw(tcm_dev, + fw_switch_delay); + if (retval < 0) { + LOGE("Fail to switch to application mode\n"); + goto exit; + } + break; + case MODE_BOOTLOADER: + case MODE_TDDI_BOOTLOADER: + case MODE_TDDI_HDL_BOOTLOADER: + case MODE_MULTICHIP_TDDI_BOOTLOADER: + retval = syna_tcm_run_bootloader_fw(tcm_dev, + fw_switch_delay); + if (retval < 0) { + LOGE("Fail to switch to bootloader mode\n"); + goto exit; + } + break; + case MODE_ROMBOOTLOADER: + retval = syna_tcm_run_rom_bootloader_fw(tcm_dev, + fw_switch_delay); + if (retval < 0) { + LOGE("Fail to switch to rom bootloader mode\n"); + goto exit; + } + break; + default: + LOGE("Invalid firmware mode requested\n"); + retval = _EINVAL; + goto exit; + } + + retval = 0; + +exit: + return retval; +} + +/** + * syna_tcm_get_boot_info() + * + * Implement the bootloader command code, which is used to request a + * boot information packet. + * + * @param + * [ in] tcm_dev: the device handle + * [out] boot_info: the boot info packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_boot_info(struct tcm_dev *tcm_dev, + struct tcm_boot_info *boot_info) +{ + int retval = 0; + unsigned char resp_code; + unsigned int resp_data_len = 0; + unsigned int copy_size; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_GET_BOOT_INFO, + NULL, + 0, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_GET_BOOT_INFO); + goto exit; + } + + resp_data_len = tcm_dev->resp_buf.data_length; + copy_size = MIN(sizeof(struct tcm_boot_info), resp_data_len); + + /* save the boot_info */ + retval = syna_pal_mem_cpy((unsigned char *)&tcm_dev->boot_info, + sizeof(struct tcm_boot_info), + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + copy_size); + if (retval < 0) { + LOGE("Fail to copy boot info\n"); + goto exit; + } + + if (boot_info == NULL) + goto exit; + + /* copy boot_info to caller */ + retval = syna_pal_mem_cpy((unsigned char *)boot_info, + sizeof(struct tcm_boot_info), + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + copy_size); + if (retval < 0) { + LOGE("Fail to copy boot info to caller\n"); + goto exit; + } + +exit: + return retval; +} + +/** + * syna_tcm_get_app_info() + * + * Implement the application fw command code to request an application + * info packet from device. + * + * @param + * [ in] tcm_dev: the device handle + * [out] app_info: the application info packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_app_info(struct tcm_dev *tcm_dev, + struct tcm_application_info *app_info) +{ + int retval = 0; + unsigned char resp_code; + unsigned int app_status; + unsigned int resp_data_len = 0; + unsigned int copy_size; + struct tcm_application_info *info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_GET_APPLICATION_INFO, + NULL, + 0, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_GET_APPLICATION_INFO); + goto exit; + } + + resp_data_len = tcm_dev->resp_buf.data_length; + copy_size = MIN(sizeof(tcm_dev->app_info), resp_data_len); + + info = &tcm_dev->app_info; + + /* save the app_info */ + retval = syna_pal_mem_cpy((unsigned char *)info, + sizeof(struct tcm_application_info), + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + copy_size); + if (retval < 0) { + LOGE("Fail to copy application info\n"); + goto exit; + } + + if (app_info == NULL) + goto show_info; + + /* copy app_info to caller */ + retval = syna_pal_mem_cpy((unsigned char *)app_info, + sizeof(struct tcm_application_info), + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + copy_size); + if (retval < 0) { + LOGE("Fail to copy application info to caller\n"); + goto exit; + } + +show_info: + app_status = syna_pal_le2_to_uint(tcm_dev->app_info.status); + + if (app_status == APP_STATUS_BAD_APP_CONFIG) { + LOGE("Bad application firmware, status: 0x%x\n", app_status); + retval = _ENODEV; + goto exit; + } else if (app_status != APP_STATUS_OK) { + LOGE("Incorrect application status, 0x%x\n", app_status); + retval = _ENODEV; + goto exit; + } + + tcm_dev->max_objects = syna_pal_le2_to_uint(info->max_objects); + tcm_dev->max_x = syna_pal_le2_to_uint(info->max_x); + tcm_dev->max_y = syna_pal_le2_to_uint(info->max_y); + + tcm_dev->cols = syna_pal_le2_to_uint(info->num_of_image_cols); + tcm_dev->rows = syna_pal_le2_to_uint(info->num_of_image_rows); + syna_pal_mem_cpy((unsigned char *)tcm_dev->config_id, + MAX_SIZE_CONFIG_ID, + info->customer_config_id, + MAX_SIZE_CONFIG_ID, + MAX_SIZE_CONFIG_ID); + + LOGD("App info version: %d, status: %d\n", + syna_pal_le2_to_uint(info->version), app_status); + LOGD("App info: max_objs: %d, max_x:%d, max_y: %d, img: %dx%d\n", + tcm_dev->max_objects, tcm_dev->max_x, tcm_dev->max_y, + tcm_dev->rows, tcm_dev->cols); + +exit: + return retval; +} + +/** + * syna_tcm_get_static_config() + * + * Implement the application fw command code to retrieve the contents of + * the static configuration. + * + * The size of static configuration is available in app info packet. + * + * @param + * [ in] tcm_dev: the device handle + * [out] buf: buffer stored the static configuration + * [ in] buf_size: the size of given buffer + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_static_config(struct tcm_dev *tcm_dev, + unsigned char *buf, unsigned int buf_size) +{ + int retval = 0; + unsigned char resp_code; + unsigned int size; + struct tcm_application_info *app_info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + app_info = &tcm_dev->app_info; + + size = syna_pal_le2_to_uint(app_info->static_config_size); + + if (size > buf_size) { + LOGE("Invalid buffer input, given size: %d (actual: %d)\n", + buf_size, size); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_GET_STATIC_CONFIG, + NULL, + 0, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_GET_STATIC_CONFIG); + goto exit; + } + + if (buf == NULL) + goto exit; + + /* copy app_info to caller */ + retval = syna_pal_mem_cpy((unsigned char *)buf, + buf_size, + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + tcm_dev->resp_buf.data_length); + if (retval < 0) { + LOGE("Fail to copy static config data to caller\n"); + goto exit; + } + +exit: + return retval; +} + +/** + * syna_tcm_set_static_config() + * + * Implement the application fw command code to set the contents of + * the static configuration. When the write is completed, the device will + * restart touch sensing with the new settings. + * + * The size of static configuration is available in app info packet. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] config_data: the data of static configuration + * [ in] config_data_size: the size of given data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_static_config(struct tcm_dev *tcm_dev, + unsigned char *config_data, unsigned int config_data_size) +{ + int retval = 0; + unsigned char resp_code; + unsigned int size; + struct tcm_application_info *app_info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + app_info = &tcm_dev->app_info; + + size = syna_pal_le2_to_uint(app_info->static_config_size); + + if (size != config_data_size) { + LOGE("Invalid static config size, given: %d (actual: %d)\n", + config_data_size, size); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_SET_STATIC_CONFIG, + config_data, + config_data_size, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_SET_STATIC_CONFIG); + goto exit; + } + + retval = 0; +exit: + return retval; +} + +/** + * syna_tcm_get_dynamic_config() + * + * Implement the application fw command code to get the value from the a single + * field of the dynamic configuration. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] id: target field id + * [out] value: the value returned + * [ in] delay_ms_resp: delay time for response reading. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_dynamic_config(struct tcm_dev *tcm_dev, + unsigned char id, unsigned short *value, + unsigned int delay_ms_resp) +{ + int retval = 0; + unsigned char out; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + out = (unsigned char)id; + + retval = tcm_dev->write_message(tcm_dev, + CMD_GET_DYNAMIC_CONFIG, + &out, + sizeof(out), + &resp_code, + delay_ms_resp); + if (retval < 0) { + LOGE("Fail to send command 0x%02x to get dynamic field 0x%x\n", + CMD_GET_DYNAMIC_CONFIG, (unsigned char)id); + goto exit; + } + + /* return dynamic config data */ + if (tcm_dev->resp_buf.data_length < 2) { + LOGE("Invalid resp data size, %d\n", + tcm_dev->resp_buf.data_length); + goto exit; + } + + *value = (unsigned short)syna_pal_le2_to_uint(tcm_dev->resp_buf.buf); + + LOGD("Get %d from dynamic field 0x%x\n", *value, id); + + retval = 0; + +exit: + return retval; +} + +/** + * syna_tcm_set_dynamic_config() + * + * Implement the application fw command code to set the specified value to + * the selected field of the dynamic configuration. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] id: target field id + * [ in] value: the value to the selected field + * [ in] delay_ms_resp: delay time for response reading. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_dynamic_config(struct tcm_dev *tcm_dev, + unsigned char id, unsigned short value, + unsigned int delay_ms_resp) +{ + int retval = 0; + unsigned char out[3]; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + LOGD("Set %d to dynamic field 0x%x\n", value, id); + + out[0] = (unsigned char)id; + out[1] = (unsigned char)value; + out[2] = (unsigned char)(value >> 8); + + retval = tcm_dev->write_message(tcm_dev, + CMD_SET_DYNAMIC_CONFIG, + out, + sizeof(out), + &resp_code, + delay_ms_resp); + if (retval < 0) { + LOGE("Fail to send command 0x%02x to set %d to field 0x%x\n", + CMD_SET_DYNAMIC_CONFIG, value, (unsigned char)id); + goto exit; + } + + retval = 0; + +exit: + return retval; +} + +/** + * syna_tcm_rezero() + * + * Implement the application fw command code to force the device to rezero its + * baseline estimate. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_rezero(struct tcm_dev *tcm_dev) +{ + int retval = 0; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_REZERO, + NULL, + 0, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_REZERO); + goto exit; + } + + retval = 0; +exit: + return retval; +} + +/** + * syna_tcm_set_config_id() + * + * Implement the application fw command code to set the 16-byte config id, + * which can be read in the app info packet. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] config_id: config id to be set + * [ in] size: size of input data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_config_id(struct tcm_dev *tcm_dev, + unsigned char *config_id, unsigned int size) +{ + int retval = 0; + unsigned char resp_code; + unsigned int config_id_len = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + config_id_len = sizeof(tcm_dev->app_info.customer_config_id); + + if (size != config_id_len) { + LOGE("Invalid config id input, given size: %d (%d)\n", + size, config_id_len); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_SET_CONFIG_ID, + config_id, + size, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_SET_CONFIG_ID); + goto exit; + } + + retval = 0; +exit: + return retval; +} + +/** + * syna_tcm_sleep() + * + * Implement the application fw command code to put the device into low power + * deep sleep mode or set to normal active mode. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] en: '1' to low power deep sleep mode; '0' to active mode + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_sleep(struct tcm_dev *tcm_dev, bool en) +{ + int retval = 0; + unsigned char resp_code; + unsigned char command; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + command = (en) ? CMD_ENTER_DEEP_SLEEP : CMD_EXIT_DEEP_SLEEP; + + retval = tcm_dev->write_message(tcm_dev, + command, + NULL, + 0, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%x\n", command); + goto exit; + } + + retval = 0; +exit: + return retval; +} + +/** + * syna_tcm_get_features() + * + * Implement the application fw command code to query the supported features. + * + * @param + * [ in] tcm_dev: the device handle + * [out] info: the features description packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_features(struct tcm_dev *tcm_dev, + struct tcm_features_info *info) +{ + int retval = 0; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_GET_FEATURES, + NULL, + 0, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_GET_FEATURES); + goto exit; + } + + if (info == NULL) + goto exit; + + /* copy features_info to caller */ + retval = syna_pal_mem_cpy((unsigned char *)info, + sizeof(struct tcm_features_info), + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + MIN(sizeof(*info), tcm_dev->resp_buf.data_length)); + if (retval < 0) { + LOGE("Fail to copy features_info to caller\n"); + goto exit; + } + +exit: + return retval; +} + +/** + * syna_tcm_run_production_test() + * + * Implement the appplication fw command code to request the device to run + * the production test. + * + * Production tests are listed at enum test_code (PID$). + * + * @param + * [ in] tcm_dev: the device handle + * [ in] test_item: the requested testing item + * [out] tdata: testing data returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_run_production_test(struct tcm_dev *tcm_dev, + unsigned char test_item, struct tcm_buffer *tdata) +{ + int retval = 0; + unsigned char resp_code; + unsigned char test_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in application fw mode, mode: %x\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + test_code = (unsigned char)test_item; + + retval = tcm_dev->write_message(tcm_dev, + CMD_PRODUCTION_TEST, + &test_code, + 1, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_PRODUCTION_TEST); + goto exit; + } + + if (tdata == NULL) + goto exit; + + /* copy testing data to caller */ + retval = syna_tcm_buf_copy(tdata, &tcm_dev->resp_buf); + if (retval < 0) { + LOGE("Fail to copy testing data\n"); + goto exit; + } +exit: + return retval; +} +/** + * syna_tcm_send_command() + * + * Helper to forward the custom commnd to the device + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: TouchComm command + * [ in] payload: data payload, if any + * [ in] payload_length: length of data payload, if any + * [out] resp_code: response code returned + * [out] resp: buffer to store the response data + * [ in] delay_ms_resp: delay time for response reading. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_send_command(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *payload, + unsigned int payload_length, unsigned char *code, + struct tcm_buffer *resp, unsigned int delay_ms_resp) +{ + int retval = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!code) { + LOGE("Invalid parameter\n"); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + command, + payload, + payload_length, + code, + delay_ms_resp); + + LOGD("Status code returned: 0x%02x\n", *code); + + /* exit if no buffer provided */ + if (!resp) + goto exit; + + /* if gathering a report, copy to the user buffer */ + if ((*code >= REPORT_IDENTIFY) && (*code != STATUS_INVALID)) { + if (tcm_dev->report_buf.data_length == 0) + goto exit; + + syna_tcm_buf_lock(&tcm_dev->report_buf); + + if (syna_tcm_buf_copy(resp, &tcm_dev->report_buf) < 0) { + LOGE("Fail to copy data, report type: %x\n", + *code); + syna_tcm_buf_unlock(&tcm_dev->report_buf); + retval = _ENOMEM; + goto exit; + } + + syna_tcm_buf_unlock(&tcm_dev->report_buf); + } + + /* if gathering a response, copy to the user buffer */ + if ((*code > STATUS_IDLE) && (*code <= STATUS_ERROR)) { + if (tcm_dev->resp_buf.data_length == 0) + goto exit; + + syna_tcm_buf_lock(&tcm_dev->resp_buf); + + if (syna_tcm_buf_copy(resp, &tcm_dev->resp_buf) < 0) { + LOGE("Fail to copy resp data, status code: %x\n", + *code); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + retval = _ENOMEM; + goto exit; + } + + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + } + +exit: + return retval; +} + +/** + * syna_tcm_enable_predict_reading() + * + * predict reading aims to retrieve all data in one transfer; + * while, standard reads will read 4-byte header and payload data separately + * + * @param + * [ in] tcm_dev: the device handle + * [ in] en: '1' to low power deep sleep mode; '0' to active mode + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_enable_predict_reading(struct tcm_dev *tcm_dev, bool en) +{ + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_dev->msg_data.predict_reads = en; + tcm_dev->msg_data.predict_length = 0; + + LOGI("Predicted reading is %s\n", + (en) ? "enabled":"disabled"); + + return 0; +} + +/** + * syna_tcm_get_message_crc() + * + * this function is used to return the crc of message retrieved previously + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * 2 bytes crc value + */ +unsigned short syna_tcm_get_message_crc(struct tcm_dev *tcm_dev) +{ + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + return tcm_dev->msg_data.crc_bytes; +} + +/** + * syna_tcm_set_reset_occurrence_callback() + * + * Set up callback function once an unexpected reset received + * + * @param + * [ in] tcm_dev: the device handle + * [ in] p_cb: the pointer of callback function + * [ in] p_cbdata: pointer to caller data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_reset_occurrence_callback(struct tcm_dev *tcm_dev, + tcm_reset_occurrence_callback_t p_cb, void *p_cbdata) +{ + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_dev->cb_reset_occurrence = p_cb; + tcm_dev->cbdata_reset = p_cbdata; + + LOGI("enabled\n"); + + return 0; +} + diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_base.h b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_base.h new file mode 100644 index 000000000000..bdf3e189d6a7 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_base.h @@ -0,0 +1,460 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file synaptics_touchcom_func_base.h + * + * This file declares generic and foundational APIs being used to communicate + * with Synaptics touch controller through TouchComm communication protocol. + */ + +#ifndef _SYNAPTICS_TOUCHCOM_BASE_FUNCS_H_ +#define _SYNAPTICS_TOUCHCOM_BASE_FUNCS_H_ + +#include "synaptics_touchcom_core_dev.h" + +/** + * syna_tcm_allocate_device() + * + * Create the TouchCom core device handle. + * This function must be called in order to allocate the main device handle, + * structure syna_tcm_dev, which will be passed to all other operations and + * functions within the entire source code. + * + * Meanwhile, caller has to prepare specific syna_tcm_hw_interface structure, + * so that all the implemented functions can access hardware components + * through syna_tcm_hw_interface. + * + * @param + * [out] ptcm_dev_ptr: a pointer to the device handle returned + * [ in] hw_if: hardware-specific data on target platform + * [ in] resp_reading: default resp reading method + * set 'RESP_IN_ATTN' to apply ATTN-driven method; + * set 'RESP_IN_POLLING' to read in resp by polling + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_allocate_device(struct tcm_dev **ptcm_dev_ptr, + struct syna_hw_interface *hw_if, unsigned int resp_reading); + +/** + * syna_tcm_remove_device() + * + * Remove the TouchCom core device handle. + * This function must be invoked when the device is no longer needed. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * none. + */ +void syna_tcm_remove_device(struct tcm_dev *tcm_dev); + +/** + * syna_tcm_detect_device() + * + * Determine the type of device being connected and distinguish which + * version of TouchCom firmware running on the device. + * This function should be called before using this TouchComm core library. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * on success, the current mode running on the device is returned; + * otherwise, negative value on error. + */ +int syna_tcm_detect_device(struct tcm_dev *tcm_dev); + +/** + * syna_tcm_get_event_data() + * + * Helper to read TouchComm messages when ATTN signal is asserted. + * After returning, the ATTN signal should be no longer asserted. + * + * The 'code' returned will guide the caller on the next action. + * For example, do touch reporting once returned code is equal to REPORT_TOUCH. + * + * @param + * [ in] tcm_dev: the device handle + * [out] code: received report code + * [out] report: report data returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_event_data(struct tcm_dev *tcm_dev, + unsigned char *code, + struct tcm_buffer *report); + +/** + * syna_tcm_change_resp_read() + * + * Helper to change the default resp reading method, which was previously set + * when calling syna_tcm_allocate_device + * + * @param + * [in] tcm_dev: the device handle + * [in] request: resp reading method to change + * set '0' or 'RESP_IN_ATTN' for ATTN-driven; otherwise, + * assign a positive value standing for the polling time + * @return + * none. + */ +void syna_tcm_change_resp_read(struct tcm_dev *tcm_dev, unsigned int request); + +/** + * syna_tcm_identify() + * + * Implement the standard command code, which is used to request + * an IDENTIFY report packet + * + * @param + * [ in] tcm_dev: the device handle + * [out] id_info: the identification info packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_identify(struct tcm_dev *tcm_dev, + struct tcm_identification_info *id_info); + +/** + * syna_tcm_reset() + * + * Implement the standard command code, which is used to perform a sw reset + * immediately. After a successful reset, an IDENTIFY report to indicate that + * device is ready. + * + * Caller shall be aware that the firmware will be reloaded after reset. + * Therefore, if expecting that a different firmware version is loaded, please + * do app firmware setup after reset. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_reset(struct tcm_dev *tcm_dev); + +/** + * syna_tcm_enable_report() + * + * Implement the application fw command code to enable or disable a report. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] report_code: the requested report code being generated + * [ in] en: '1' for enabling; '0' for disabling + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_enable_report(struct tcm_dev *tcm_dev, + unsigned char report_code, bool en); + +/** + * syna_tcm_switch_fw_mode() + * + * Implement the command code to switch the firmware mode. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] mode: target firmware mode + * [ in] fw_switch_delay: delay time for fw mode switching. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_switch_fw_mode(struct tcm_dev *tcm_dev, + unsigned char mode, unsigned int fw_switch_delay); + +/** + * syna_tcm_get_boot_info() + * + * Implement the bootloader command code, which is used to request a + * boot information packet. + * + * @param + * [ in] tcm_dev: the device handle + * [out] boot_info: the boot info packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_boot_info(struct tcm_dev *tcm_dev, + struct tcm_boot_info *boot_info); + +/** + * syna_tcm_get_app_info() + * + * Implement the application fw command code to request an application + * info packet from device + * + * @param + * [ in] tcm_dev: the device handle + * [out] app_info: the application info packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_app_info(struct tcm_dev *tcm_dev, + struct tcm_application_info *app_info); + +/** + * syna_tcm_get_static_config() + * + * Implement the application fw command code to retrieve the contents of + * the static configuration. + * + * The size of static configuration is available in app info packet. + * + * @param + * [ in] tcm_dev: the device handle + * [out] buf: buffer stored the static configuration + * [ in] buf_size: the size of given buffer + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_static_config(struct tcm_dev *tcm_dev, + unsigned char *buf, unsigned int buf_size); + +/** + * syna_tcm_set_static_config() + * + * Implement the application fw command code to set the contents of + * the static configuration. When the write is completed, the device will + * restart touch sensing with the new settings + * + * The size of static configuration is available in app info packet. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] config_data: the data of static configuration + * [ in] config_data_size: the size of given data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_static_config(struct tcm_dev *tcm_dev, + unsigned char *config_data, unsigned int config_data_size); + +/** + * syna_tcm_get_dynamic_config() + * + * Implement the application fw command code to get the value from the a single + * field of the dynamic configuration + * + * @param + * [ in] tcm_dev: the device handle + * [ in] id: target field id + * [out] value: the value returned + * [ in] delay_ms_resp: delay time for response reading. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_dynamic_config(struct tcm_dev *tcm_dev, + unsigned char id, unsigned short *value, + unsigned int delay_ms_resp); + +/** + * syna_tcm_set_dynamic_config() + * + * Implement the application fw command code to set the specified value to + * the selected field of the dynamic configuration + * + * @param + * [ in] tcm_dev: the device handle + * [ in] id: target field id + * [ in] value: the value to the selected field + * [ in] delay_ms_resp: delay time for response reading. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_dynamic_config(struct tcm_dev *tcm_dev, + unsigned char id, unsigned short value, + unsigned int delay_ms_resp); + +/** + * syna_tcm_rezero() + * + * Implement the application fw command code to force the device to rezero its + * baseline estimate. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_rezero(struct tcm_dev *tcm_dev); + +/** + * syna_tcm_set_config_id() + * + * Implement the application fw command code to set the 16-byte config id, + * which can be read in the app info packet. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] config_id: config id to be set + * [ in] size: size of input data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_config_id(struct tcm_dev *tcm_dev, + unsigned char *config_id, unsigned int size); + +/** + * syna_tcm_sleep() + * + * Implement the application fw command code to put the device into low power + * deep sleep mode or set to normal active mode + * + * @param + * [ in] tcm_dev: the device handle + * [ in] en: '1' to low power deep sleep mode; '0' to active mode + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_sleep(struct tcm_dev *tcm_dev, bool en); + +/** + * syna_tcm_get_features() + * + * Implement the application fw command code to query the supported features. + * + * @param + * [ in] tcm_dev: the device handle + * [out] info: the features description packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_features(struct tcm_dev *tcm_dev, + struct tcm_features_info *info); + +/** + * syna_tcm_run_production_test() + * + * Implement the appplication fw command code to request the device to run + * the production test. + * + * Production tests are listed at enum test_code (PID$). + * + * @param + * [ in] tcm_dev: the device handle + * [ in] test_item: the requested testing item + * [out] tdata: testing data returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_run_production_test(struct tcm_dev *tcm_dev, + unsigned char test_item, struct tcm_buffer *tdata); + +/** + * syna_tcm_send_command() + * + * Helper to forward the custom commnd to the device + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: TouchComm command + * [ in] payload: data payload, if any + * [ in] payload_length: length of data payload, if any + * [out] resp_code: response code returned + * [out] resp: buffer to store the response data + * [ in] delay_ms_resp: delay time for response reading. + * a positive value presents the time for polling; + * or, set '0' or 'RESP_IN_ATTN' for ATTN driven + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_send_command(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *payload, + unsigned int payload_length, unsigned char *resp_code, + struct tcm_buffer *resp, unsigned int delay_ms_resp); + +/** + * syna_tcm_enable_predict_reading() + * + * predict reading aims to retrieve all data in one transfer; + * while, standard reads will read 4-byte header and payload data separately + * + * @param + * [ in] tcm_dev: the device handle + * [ in] en: '1' to low power deep sleep mode; '0' to active mode + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_enable_predict_reading(struct tcm_dev *tcm_dev, bool en); +/** + * syna_tcm_get_message_crc() + * + * this function is used to return the crc of message retrieved previously + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * 2 bytes crc value + */ +unsigned short syna_tcm_get_message_crc(struct tcm_dev *tcm_dev); +/** + * syna_tcm_set_reset_occurrence_callback() + * + * Set up callback function once an unexpected reset received + * + * @param + * [ in] tcm_dev: the device handle + * [ in] p_cb: the pointer of callback function + * [ in] p_cbdata: pointer to caller data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_reset_occurrence_callback(struct tcm_dev *tcm_dev, + tcm_reset_occurrence_callback_t p_cb, void *p_cbdata); + + +#endif /* end of _SYNAPTICS_TOUCHCOM_BASE_FUNCS_H_ */ diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_base_flash.h b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_base_flash.h new file mode 100644 index 000000000000..87d0a4eeb220 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_base_flash.h @@ -0,0 +1,569 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file synaptics_touchcom_func_base_flash.h + * + * This file declares the common functions and structures being used in relevant + * functions of fw update. + */ + +#ifndef _SYNAPTICS_TOUCHCOM_PARSE_FW_FILES_H_ +#define _SYNAPTICS_TOUCHCOM_PARSE_FW_FILES_H_ + +#include "synaptics_touchcom_core_dev.h" + +/** + * @section: Some specific definition in the firmware file + */ +#define ID_STRING_SIZE (32) + +#define SIZE_WORDS (8) + +#define IHEX_RECORD_SIZE (14) + +#define IHEX_MAX_BLOCKS (64) + +#define IMAGE_FILE_MAGIC_VALUE (0x4818472b) + +#define FLASH_AREA_MAGIC_VALUE (0x7c05e516) + + +/** + * @section: Helper macros for firmware file parsing + */ +#define CRC32(data, length) \ + (syna_pal_crc32(~0, data, length) ^ ~0) + +#define VALUE(value) \ + (syna_pal_le2_to_uint(value)) + +#define AREA_ID_STR(area) \ + (syna_tcm_get_flash_area_string(area)) + + +/** + * @section: Area Partitions in firmware + */ +enum flash_area { + AREA_NONE = 0, + /* please add the declarations below */ + + AREA_BOOT_CODE, + AREA_BOOT_CONFIG, + AREA_APP_CODE, + AREA_APP_CODE_COPRO, + AREA_APP_CONFIG, + AREA_PROD_TEST, + AREA_DISP_CONFIG, + AREA_F35_APP_CODE, + AREA_FORCE_TUNING, + AREA_GAMMA_TUNING, + AREA_TEMPERATURE_GAMM_TUNING, + AREA_CUSTOM_LCM, + AREA_LOOKUP, + AREA_CUSTOM_OEM, + AREA_OPEN_SHORT_TUNING, + AREA_CUSTOM_OTP, + AREA_PPDT, + AREA_ROMBOOT_APP_CODE, + AREA_TOOL_BOOT_CONFIG, + + /* please add the declarations above */ + AREA_MAX, +}; +/** + * @section: String of Area Partitions in firmware + */ +static char *flash_area_str[] = { + NULL, + /* please add the declarations below */ + + "BOOT_CODE", /* AREA_BOOT_CODE */ + "BOOT_CONFIG", /* AREA_BOOT_CONFIG */ + "APP_CODE", /* AREA_APP_CODE */ + "APP_CODE_COPRO", /* AREA_APP_CODE_COPRO */ + "APP_CONFIG", /* AREA_APP_CONFIG */ + "APP_PROD_TEST", /* AREA_PROD_TEST */ + "DISPLAY", /* AREA_DISP_CONFIG */ + "F35_APP_CODE", /* AREA_F35_APP_CODE */ + "FORCE", /* AREA_FORCE_TUNING */ + "GAMMA", /* AREA_GAMMA_TUNING */ + "TEMPERATURE_GAMM",/* AREA_TEMPERATURE_GAMM_TUNING */ + "LCM", /* AREA_CUSTOM_LCM */ + "LOOKUP", /* AREA_LOOKUP */ + "OEM", /* AREA_CUSTOM_OEM */ + "OPEN_SHORT", /* AREA_OPEN_SHORT_TUNING */ + "OTP", /* AREA_CUSTOM_OTP */ + "PPDT", /* AREA_PPDT */ + "ROMBOOT_APP_CODE",/* AREA_ROMBOOT_APP_CODE */ + "TOOL_BOOT_CONFIG",/* AREA_TOOL_BOOT_CONFIG */ + + /* please add the declarations above */ + NULL +}; +/** + * @section: Header Content of app config defined + * in firmware file + */ +struct app_config_header { + unsigned short magic_value[4]; + unsigned char checksum[4]; + unsigned char length[2]; + unsigned char build_id[4]; + unsigned char customer_config_id[16]; +}; +/** + * @section: The Partition Descriptor defined + * in firmware file + */ +struct area_descriptor { + unsigned char magic_value[4]; + unsigned char id_string[16]; + unsigned char flags[4]; + unsigned char flash_addr_words[4]; + unsigned char length[4]; + unsigned char checksum[4]; +}; +/** + * @section: Structure for the Data Block defined + * in firmware file + */ +struct block_data { + bool available; + const unsigned char *data; + unsigned int size; + unsigned int flash_addr; + unsigned char id; +}; +/** + * @section: Structure for the Parsed Image File + */ +struct image_info { + struct block_data data[AREA_MAX]; +}; +/** + * @section: Header of Image File + * + * Define the header of firmware image file + */ +struct image_header { + unsigned char magic_value[4]; + unsigned char num_of_areas[4]; +}; +/** + * @section: Structure for the Parsed iHex File + */ +struct ihex_info { + unsigned int records; + unsigned char *bin; + unsigned int bin_size; + struct block_data block[IHEX_MAX_BLOCKS]; +}; + +/** + * syna_tcm_get_flash_area_string() + * + * Return the string ID of target area in the flash memory + * + * @param + * [ in] area: target flash area + * + * @return + * the string ID + */ +static inline char *syna_tcm_get_flash_area_string(enum flash_area area) +{ + if (area < AREA_MAX) + return (char *)flash_area_str[area]; + else + return ""; +} + +/** + * syna_tcm_save_flash_block_data() + * + * Save the block data of flash memory to the corresponding structure. + * + * @param + * [out] image_info: image info used for storing the block data + * [ in] area: target area + * [ in] content: content of data + * [ in] flash_addr: offset of block data + * [ in] size: size of block data + * [ in] checksum: checksum of block data + * + * @return + * on success, return 0; otherwise, negative value on error. + */ +static int syna_tcm_save_flash_block_data(struct image_info *image_info, + enum flash_area area, const unsigned char *content, + unsigned int offset, unsigned int size, unsigned int checksum) +{ + if (!image_info) { + LOGE("Invalid image_info\n"); + return _EINVAL; + } + + if (area >= AREA_MAX) { + LOGE("Invalid flash area\n"); + return _EINVAL; + } + + if (checksum != CRC32((const char *)content, size)) { + LOGE("%s checksum error, in image: 0x%x (0x%x)\n", + AREA_ID_STR(area), checksum, + CRC32((const char *)content, size)); + return _EINVAL; + } + image_info->data[area].size = size; + image_info->data[area].data = content; + image_info->data[area].flash_addr = offset; + image_info->data[area].id = (unsigned char)area; + image_info->data[area].available = true; + + LOGI("%s area - address:0x%08x (%d), size:%d\n", + AREA_ID_STR(area), offset, offset, size); + + return 0; +} + +/** + * syna_tcm_get_flash_area_id() + * + * Return the corresponding ID of flash area based on the given string + * + * @param + * [ in] str: string to look for + * + * + * @return + * if matching, return the corresponding ID; otherwise, return AREA_MAX. + */ +static enum flash_area syna_tcm_get_flash_area_id(char *str) +{ + int area; + char *target; + unsigned int len; + + for (area = AREA_MAX - 1; area >= 0; area--) { + target = AREA_ID_STR(area); + len = syna_pal_str_len(target); + + if (syna_pal_str_cmp(str, target, len) == 0) + return area; + } + + LOGW("Un-defined area string, %s\n", str); + return AREA_MAX; +} + +/** + * syna_tcm_parse_fw_image() + * + * Parse and analyze the information of each areas from the given + * firmware image. + * + * @param + * [ in] image: image file given + * [ in] image_info: data blob stored the parsed data from an image file + * + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static inline int syna_tcm_parse_fw_image(const unsigned char *image, + struct image_info *image_info) +{ + int retval = 0; + unsigned int idx; + unsigned int addr; + unsigned int offset; + unsigned int length; + unsigned int checksum; + unsigned int flash_addr; + unsigned int magic_value; + unsigned int num_of_areas; + struct image_header *header; + struct area_descriptor *descriptor; + const unsigned char *content; + enum flash_area target_area; + + if (!image) { + LOGE("No image data\n"); + return _EINVAL; + } + + if (!image_info) { + LOGE("Invalid image_info blob\n"); + return _EINVAL; + } + + syna_pal_mem_set(image_info, 0x00, sizeof(struct image_info)); + + header = (struct image_header *)image; + + magic_value = syna_pal_le4_to_uint(header->magic_value); + if (magic_value != IMAGE_FILE_MAGIC_VALUE) { + LOGE("Invalid image file magic value\n"); + return _EINVAL; + } + + offset = sizeof(struct image_header); + num_of_areas = syna_pal_le4_to_uint(header->num_of_areas); + + for (idx = 0; idx < num_of_areas; idx++) { + addr = syna_pal_le4_to_uint(image + offset); + descriptor = (struct area_descriptor *)(image + addr); + offset += 4; + + magic_value = syna_pal_le4_to_uint(descriptor->magic_value); + if (magic_value != FLASH_AREA_MAGIC_VALUE) + continue; + + length = syna_pal_le4_to_uint(descriptor->length); + content = (unsigned char *)descriptor + sizeof(*descriptor); + flash_addr = syna_pal_le4_to_uint(descriptor->flash_addr_words); + flash_addr = flash_addr * 2; + checksum = syna_pal_le4_to_uint(descriptor->checksum); + + target_area = syna_tcm_get_flash_area_id( + (char *)descriptor->id_string); + + retval = syna_tcm_save_flash_block_data(image_info, + target_area, + content, + flash_addr, + length, + checksum); + if (retval < 0) + return _EINVAL; + } + + return 0; +} + + +/** + * syna_tcm_parse_ihex_line() + * + * Parse a line in the ihex file and convert into an actual data + * + * @param + * [ in] line: a line of string stored in the ihex file + * [out] count: size of actual data + * [out] addr: address of data located + * [out] type: the type of data belonging + * [out] buf: a buffer to store the converted data + * [int] buf_size: size of buffer + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static inline int syna_tcm_parse_ihex_line(char *line, unsigned int *count, + unsigned int *addr, unsigned int *type, unsigned char *buf, + unsigned int buf_size) +{ + const int OFFSET_COUNT = 1; + const int SIZE_COUNT = 2; + const int OFFSET_ADDR = OFFSET_COUNT + SIZE_COUNT; + const int SIZE_ADDR = 4; + const int OFFSET_TYPE = OFFSET_ADDR + SIZE_ADDR; + const int SIZE_TYPE = 2; + const int OFFSET_DATA = OFFSET_TYPE + SIZE_TYPE; + const int SIZE_DATA = 2; + unsigned int pos; + + if (!line) { + LOGE("No string line\n"); + return _EINVAL; + } + + if ((!buf) || (buf_size == 0)) { + LOGE("Invalid temporary data buffer\n"); + return _EINVAL; + } + + *count = syna_pal_hex_to_uint( + line + OFFSET_COUNT, 2); + *addr = syna_pal_hex_to_uint( + line + OFFSET_ADDR, SIZE_ADDR); + *type = syna_pal_hex_to_uint( + line + OFFSET_TYPE, SIZE_TYPE); + + if (*count > buf_size) { + LOGE("Data size mismatched, required:%d, given:%d\n", + *count, buf_size); + return _EINVAL; + } + + for (pos = 0; pos < *count; pos++) + buf[pos] = (unsigned char)syna_pal_hex_to_uint( + line + (((int)(pos << 1)) + OFFSET_DATA), + SIZE_DATA); + + return 0; +} + +/** + * syna_tcm_parse_fw_ihex() + * + * Based on the firmware ihex file given , parse and convert into a binary + * firmware data to update. + * + * @param + * [ in] ihex: original ihex file + * [ in] ihex_size: size of given file + * [ in] ihex_info: data blob stored the parsed data from an ihex file. + * assume the data buffer inside was allocated + * [ in] len_per_line: length for a useful data in a line + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static inline int syna_tcm_parse_fw_ihex(const char *ihex, int ihex_size, + struct ihex_info *ihex_info, const unsigned int len_per_line) +{ + int retval; + unsigned int pos; + unsigned int record; + char *tmp = NULL; + unsigned int count; + unsigned int type; + unsigned char data[32] = { 0 }; + unsigned int addr; + unsigned int offset; + unsigned int prev_addr; + unsigned int block_idx = 0; + + if (!ihex) { + LOGE("No ihex data\n"); + return _EINVAL; + } + + if (!ihex_info) { + LOGE("Invalid ihex_info blob\n"); + return _EINVAL; + } + + if ((!ihex_info->bin) || (ihex_info->bin_size == 0)) { + LOGE("Invalid ihex_info->data\n"); + return _EINVAL; + } + + tmp = syna_pal_mem_alloc(len_per_line + 1, sizeof(char)); + if (!tmp) { + LOGE("Fail to allocate temporary buffer\n"); + return _ENOMEM; + } + + offset = 0; + addr = 0; + pos = 0; + prev_addr = 0; + + ihex_info->records = ihex_size / len_per_line; + LOGD("records = %d\n", ihex_info->records); + + for (record = 0; record < ihex_info->records; record++) { + pos = record * len_per_line; + if ((char)ihex[pos] != ':') { + LOGE("Invalid string maker at pos %d, marker:%c\n", + pos, (char)ihex[pos]); + goto exit; + } + + retval = syna_pal_mem_cpy(tmp, len_per_line, + &ihex[pos], ihex_size - pos, len_per_line); + if (retval < 0) { + LOGE("Fail to copy a line at pos %d\n", pos); + goto exit; + } + + retval = syna_tcm_parse_ihex_line(tmp, &count, &addr, &type, + data, sizeof(data)); + if (retval < 0) { + LOGE("Fail to parse line at pos %d\n", pos); + goto exit; + } + + if ((((prev_addr + 2) & 0xFFFF) != addr) && (type == 0x00)) { + block_idx = (record == 0) ? 0 : block_idx + 1; + if (block_idx >= IHEX_MAX_BLOCKS) { + LOGE("Invalid block index\n"); + goto exit; + } + + ihex_info->block[block_idx].flash_addr = + addr + offset; + ihex_info->block[block_idx].data = + &ihex_info->bin[addr + offset]; + ihex_info->block[block_idx].available = true; + } + + if (type == 0x00) { + + prev_addr = addr; + addr += offset; + + if (addr >= ihex_info->bin_size) { + LOGE("No enough size for data0 addr:0x%x(%d)\n", + addr, addr); + goto exit; + } + ihex_info->bin[addr++] = data[0]; + + if (addr >= ihex_info->bin_size) { + LOGE("No enough size for data1 addr:0x%x(%d)\n", + addr, addr); + goto exit; + } + ihex_info->bin[addr++] = data[1]; + + ihex_info->block[block_idx].size += 2; + + } else if (type == 0x02) { + offset = (data[0] << 8) + data[1]; + offset <<= 4; + } + } + + ihex_info->bin_size = addr; /* the actual size after data reordering */ + LOGN("Size of firmware binary data = %d\n", ihex_info->bin_size); + +exit: + syna_pal_mem_free((void *)tmp); + + return 0; +} + + +#endif /* end of _SYNAPTICS_TOUCHCOM_PARSE_FW_FILES_H_ */ diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_reflash.c b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_reflash.c new file mode 100644 index 000000000000..4bdbef41151c --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_reflash.c @@ -0,0 +1,2079 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file synaptics_touchcom_func_reflash.c + * + * This file implements the fw reflash related functions of TouchBoot. + * The declarations are available in synaptics_touchcom_func_reflash.h. + */ + +#include "synaptics_touchcom_func_base.h" +#include "synaptics_touchcom_func_reflash.h" + +/** + * @section: Reflash relevant definitions + * + */ +#define FLASH_READ_DELAY_MS (10) +#define FLASH_WRITE_DELAY_MS (20) +#define FLASH_ERASE_DELAY_MS (500) + +#define BOOT_CONFIG_SIZE 8 +#define BOOT_CONFIG_SLOTS 16 + +#define DO_NONE (0) +#define DO_UPDATE (1) + +/** + * syna_tcm_set_up_flash_access() + * + * Enter the bootloader fw if not in the mode. + * Besides, get the necessary parameters in boot info. + * + * @param + * [ in] tcm_dev: the device handle + * [out] reflash_data: data blob for reflash + * + * @return + * Result of image file comparison + */ +static int syna_tcm_set_up_flash_access(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data) +{ + int retval; + unsigned int temp; + struct tcm_identification_info id_info; + struct tcm_boot_info *boot_info; + unsigned int wr_chunk; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return _EINVAL; + } + + LOGI("Set up flash access\n"); + + retval = syna_tcm_identify(tcm_dev, &id_info); + if (retval < 0) { + LOGE("Fail to do identification\n"); + return retval; + } + + /* switch to bootloader mode */ + if (IS_APP_FW_MODE(id_info.mode)) { + LOGI("Prepare to enter bootloader mode\n"); + + retval = syna_tcm_switch_fw_mode(tcm_dev, + MODE_BOOTLOADER, + FW_MODE_SWITCH_DELAY_MS); + if (retval < 0) { + LOGE("Fail to enter bootloader mode\n"); + return retval; + } + } + + if (!IS_BOOTLOADER_MODE(tcm_dev->dev_mode)) { + LOGE("Fail to enter bootloader mode (current: 0x%x)\n", + tcm_dev->dev_mode); + return retval; + } + + boot_info = &tcm_dev->boot_info; + + /* get boot info to set up the flash access */ + retval = syna_tcm_get_boot_info(tcm_dev, boot_info); + if (retval < 0) { + LOGE("Fail to get boot info at mode 0x%x\n", + id_info.mode); + return retval; + } + + wr_chunk = tcm_dev->max_wr_size; + + temp = boot_info->write_block_size_words; + reflash_data->write_block_size = temp * 2; + + LOGI("Write block size: %d (words size: %d)\n", + reflash_data->write_block_size, temp); + + temp = syna_pal_le2_to_uint(boot_info->erase_page_size_words); + reflash_data->page_size = temp * 2; + + LOGI("Erase page size: %d (words size: %d)\n", + reflash_data->page_size, temp); + + temp = syna_pal_le2_to_uint(boot_info->max_write_payload_size); + reflash_data->max_write_payload_size = temp; + + LOGI("Max write flash data size: %d\n", + reflash_data->max_write_payload_size); + + if (reflash_data->write_block_size > (wr_chunk - 9)) { + LOGE("Write block size, %d, greater than chunk space, %d\n", + reflash_data->write_block_size, (wr_chunk - 9)); + return _EINVAL; + } + + if (reflash_data->write_block_size == 0) { + LOGE("Invalid write block size %d\n", + reflash_data->write_block_size); + return _EINVAL; + } + + if (reflash_data->page_size == 0) { + LOGE("Invalid erase page size %d\n", + reflash_data->page_size); + return _EINVAL; + } + + return 0; +} + +/** + * syna_tcm_compare_image_id_info() + * + * Compare the ID information between device and the image file, + * and then determine the area to be updated. + * The function should be called after parsing the image file. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * + * @return + * Blocks to be updated + */ +int syna_tcm_compare_image_id_info(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data) +{ + enum update_area result; + unsigned int idx; + unsigned int image_fw_id; + unsigned int device_fw_id; + unsigned char *image_config_id; + unsigned char *device_config_id; + struct app_config_header *header; + const unsigned char *app_config_data; + struct block_data *app_config; + + result = UPDATE_NONE; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash_data\n"); + return _EINVAL; + } + + app_config = &reflash_data->image_info.data[AREA_APP_CONFIG]; + + if (app_config->size < sizeof(struct app_config_header)) { + LOGE("Invalid application config in image file\n"); + return _EINVAL; + } + + app_config_data = app_config->data; + header = (struct app_config_header *)app_config_data; + + image_fw_id = syna_pal_le4_to_uint(header->build_id); + device_fw_id = tcm_dev->packrat_number; + + LOGN("Device firmware ID: %d, image build id: %d\n", + device_fw_id, image_fw_id); + + if (image_fw_id != device_fw_id) { + LOGN("Image build ID and device fw ID mismatched\n"); + result = UPDATE_FIRMWARE_CONFIG; + goto exit; + } + + image_config_id = header->customer_config_id; + device_config_id = tcm_dev->app_info.customer_config_id; + + for (idx = 0; idx < MAX_SIZE_CONFIG_ID; idx++) { + if (image_config_id[idx] != device_config_id[idx]) { + LOGN("Different Config ID\n"); + result = UPDATE_CONFIG_ONLY; + goto exit; + } + } + + result = UPDATE_NONE; + +exit: + switch (result) { + case UPDATE_FIRMWARE_CONFIG: + LOGN("Update firmware and config\n"); + break; + case UPDATE_CONFIG_ONLY: + LOGN("Update config only\n"); + break; + case UPDATE_NONE: + default: + LOGN("No need to do reflash\n"); + break; + } + + return (int)result; +} + +/** + * syna_tcm_check_flash_boot_config() + * + * Check whether the same flash address of boot config in between the device + * and the image file. + * + * @param + * [ in] boot_config: block data of boot_config from image file + * [ in] boot_info: data of boot info + * [ in] block_size: max size of write block + * + * @return + * '0' represent no need to do reflash; + * '1' represent a positive result of checking; + * otherwise, negative value on error. + */ +static int syna_tcm_check_flash_boot_config(struct block_data *boot_config, + struct tcm_boot_info *boot_info, unsigned int block_size) +{ + unsigned int start_block; + unsigned int image_addr; + unsigned int device_addr; + + if (!boot_config) { + LOGE("Invalid boot_config block data\n"); + return _EINVAL; + } + + if (!boot_info) { + LOGE("Invalid boot_info\n"); + return _EINVAL; + } + + if (boot_config->size < BOOT_CONFIG_SIZE) { + LOGE("No valid BOOT_CONFIG size, %d, in image file\n", + boot_config->size); + return _EINVAL; + } + + image_addr = boot_config->flash_addr; + + LOGD("Boot Config address in image file: 0x%x\n", image_addr); + + start_block = VALUE(boot_info->boot_config_start_block); + device_addr = start_block * block_size; + + LOGD("Boot Config address in device: 0x%x\n", device_addr); + + return DO_NONE; +} + +/** + * syna_tcm_check_flash_app_config() + * + * Check whether the same flash address of app config in between the + * device and the image file. + * + * @param + * [ in] app_config: block data of app_config from image file + * [ in] app_info: data of application info + * [ in] block_size: max size of write block + * + * @return + * '0' represent no need to do reflash; + * '1' represent a positive result of checking; + * otherwise, negative value on error. + */ +static int syna_tcm_check_flash_app_config(struct block_data *app_config, + struct tcm_application_info *app_info, unsigned int block_size) +{ + unsigned int temp; + unsigned int image_addr; + unsigned int image_size; + unsigned int device_addr; + unsigned int device_size; + + if (!app_config) { + LOGE("Invalid app_config block data\n"); + return _EINVAL; + } + + if (!app_info) { + LOGE("Invalid app_info\n"); + return _EINVAL; + } + + if (app_config->size == 0) { + LOGD("No APP_CONFIG in image file\n"); + return DO_NONE; + } + + image_addr = app_config->flash_addr; + image_size = app_config->size; + + LOGD("App Config address in image file: 0x%x, size: %d\n", + image_addr, image_size); + + temp = VALUE(app_info->app_config_start_write_block); + device_addr = temp * block_size; + device_size = VALUE(app_info->app_config_size); + + LOGD("App Config address in device: 0x%x, size: %d\n", + device_addr, device_size); + + if (device_addr == 0 && device_size == 0) + return DO_UPDATE; + + if (image_addr != device_addr) + LOGW("App Config address mismatch, image:0x%x, dev:0x%x\n", + image_addr, device_addr); + + if (image_size != device_size) + LOGW("App Config address size mismatch, image:%d, dev:%d\n", + image_size, device_size); + + return DO_UPDATE; +} + +/** + * syna_tcm_check_flash_disp_config() + * + * Check whether the same flash address of display config in between the + * device and the image file. + * + * @param + * [ in] disp_config: block data of disp_config from image file + * [ in] boot_info: data of boot info + * [ in] block_size: max size of write block + * + * @return + * '0' represent no need to do reflash; + * '1' represent a positive result of checking; + * otherwise, negative value on error. + */ +static int syna_tcm_check_flash_disp_config(struct block_data *disp_config, + struct tcm_boot_info *boot_info, unsigned int block_size) +{ + unsigned int temp; + unsigned int image_addr; + unsigned int image_size; + unsigned int device_addr; + unsigned int device_size; + + if (!disp_config) { + LOGE("Invalid disp_config block data\n"); + return _EINVAL; + } + + if (!boot_info) { + LOGE("Invalid boot_info\n"); + return _EINVAL; + } + + /* disp_config area may not be included in all product */ + if (disp_config->size == 0) { + LOGD("No DISP_CONFIG in image file\n"); + return DO_NONE; + } + + image_addr = disp_config->flash_addr; + image_size = disp_config->size; + + LOGD("Disp Config address in image file: 0x%x, size: %d\n", + image_addr, image_size); + + temp = VALUE(boot_info->display_config_start_block); + device_addr = temp * block_size; + + temp = VALUE(boot_info->display_config_length_blocks); + device_size = temp * block_size; + + LOGD("Disp Config address in device: 0x%x, size: %d\n", + device_addr, device_size); + + if (image_addr != device_addr) + LOGW("Disp Config address mismatch, image:0x%x, dev:0x%x\n", + image_addr, device_addr); + + if (image_size != device_size) + LOGW("Disp Config address size mismatch, image:%d, dev:%d\n", + image_size, device_size); + + return DO_UPDATE; +} + +/** + * syna_tcm_check_flash_app_code() + * + * Check whether the valid size of app firmware in the image file + * + * @param + * [ in] app_code: block data of app_code from image file + * + * @return + * '0' represent no need to do reflash; + * '1' represent a positive result of checking; + * otherwise, negative value on error. + */ +static int syna_tcm_check_flash_app_code(struct block_data *app_code) +{ + if (!app_code) { + LOGE("Invalid app_code block data\n"); + return _EINVAL; + } + + if (app_code->size == 0) { + LOGD("No %s in image file\n", AREA_ID_STR(app_code->id)); + return _EINVAL; + } + + return DO_UPDATE; +} + +/** + * syna_tcm_check_flash_openshort() + * + * Check whether the valid size of openshort area in the image file + * + * @param + * [ in] open_short: block data of open_short from image file + * + * @return + * '0' represent no need to do reflash; + * '1' represent a positive result of checking; + * otherwise, negative value on error. + */ +static int syna_tcm_check_flash_openshort(struct block_data *open_short) +{ + if (!open_short) { + LOGE("Invalid open_short block data\n"); + return _EINVAL; + } + + /* open_short area may not be included in all product */ + if (open_short->size == 0) { + LOGD("No %s in image file\n", AREA_ID_STR(open_short->id)); + return DO_NONE; + } + + return DO_UPDATE; +} + +/** + * syna_tcm_check_flash_app_prod_test() + * + * Check whether the valid size of app prod_test area in the image file + * + * @param + * [ in] prod_test: block data of app_prod_test from image file + * + * @return + * '0' represent no need to do reflash; + * '1' represent a positive result of checking; + * otherwise, negative value on error. + */ +static int syna_tcm_check_flash_app_prod_test(struct block_data *prod_test) +{ + if (!prod_test) { + LOGE("Invalid app_prod_test block data\n"); + return _EINVAL; + } + + /* app_prod_test area may not be included in all product */ + if (prod_test->size == 0) { + LOGD("No %s in image file\n", AREA_ID_STR(prod_test->id)); + return DO_NONE; + } + + return DO_UPDATE; +} + +/** + * syna_tcm_check_flash_ppdt() + * + * Check whether the valid size of ppdt area in the image file + * + * @param + * [ in] ppdt: block data of PPDT from image file + * + * @return + * '0' represent no need to do reflash; + * '1' represent a positive result of checking; + * otherwise, negative value on error. + */ +static int syna_tcm_check_flash_ppdt(struct block_data *ppdt) +{ + if (!ppdt) { + LOGE("Invalid ppdt block data\n"); + return _EINVAL; + } + + /* open_short area may not be included in all product */ + if (ppdt->size == 0) { + LOGD("No %s in image file\n", AREA_ID_STR(ppdt->id)); + return DO_NONE; + } + + return DO_UPDATE; +} + +/** + * syna_tcm_check_flash_block() + * + * Dispatch to the proper helper to ensure the data of associated block area + * is correct in between the device and the image file. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [ in] block: target block area to check + * + * @return + * '0' represent no need to do reflash; + * '1' represent a positive result of checking; + * otherwise, negative value on error. + */ +static int syna_tcm_check_flash_block(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + struct block_data *block) +{ + int retval = 0; + struct tcm_application_info *app_info; + struct tcm_boot_info *boot_info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return DO_NONE; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return DO_NONE; + } + + if (!block) { + LOGE("Invalid block data\n"); + return DO_NONE; + } + + app_info = &tcm_dev->app_info; + boot_info = &tcm_dev->boot_info; + + switch (block->id) { + case AREA_APP_CODE: + retval = syna_tcm_check_flash_app_code(block); + break; + case AREA_APP_CONFIG: + retval = syna_tcm_check_flash_app_config(block, app_info, + reflash_data->write_block_size); + break; + case AREA_BOOT_CONFIG: + retval = syna_tcm_check_flash_boot_config(block, boot_info, + reflash_data->write_block_size); + break; + case AREA_DISP_CONFIG: + retval = syna_tcm_check_flash_disp_config(block, boot_info, + reflash_data->write_block_size); + break; + case AREA_OPEN_SHORT_TUNING: + retval = syna_tcm_check_flash_openshort(block); + break; + case AREA_PROD_TEST: + retval = syna_tcm_check_flash_app_prod_test(block); + break; + case AREA_PPDT: + retval = syna_tcm_check_flash_ppdt(block); + break; + default: + retval = DO_NONE; + break; + } + + return retval; +} + +/** + * syna_tcm_get_flash_data_location() + * + * Return the address and length of the specified data area + * in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] area: specified area in flash memory + * [out] addr: the flash address of the specified area returned + * [out] len: the size of the specified area returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_get_flash_data_location(struct tcm_dev *tcm_dev, + enum flash_area area, unsigned int *addr, unsigned int *len) +{ + int retval = 0; + unsigned char resp_code; + unsigned char payload; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + switch (area) { + case AREA_CUSTOM_LCM: + payload = FLASH_LCM_DATA; + break; + case AREA_CUSTOM_OEM: + payload = FLASH_OEM_DATA; + break; + case AREA_PPDT: + payload = FLASH_PPDT_DATA; + break; + case AREA_FORCE_TUNING: + payload = FLASH_FORCE_CALIB_DATA; + break; + case AREA_OPEN_SHORT_TUNING: + payload = FLASH_OPEN_SHORT_TUNING_DATA; + break; + default: + LOGE("Invalid flash area %d\n", area); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_GET_DATA_LOCATION, + &payload, + sizeof(payload), + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_GET_DATA_LOCATION); + goto exit; + } + + if (tcm_dev->resp_buf.data_length != 4) { + LOGE("Invalid data length %d\n", + tcm_dev->resp_buf.data_length); + retval = _EINVAL; + goto exit; + } + + *addr = syna_pal_le2_to_uint(&tcm_dev->resp_buf.buf[0]); + *len = syna_pal_le2_to_uint(&tcm_dev->resp_buf.buf[2]); + + retval = 0; + +exit: + return retval; +} + +/** + * syna_tcm_reflash_send_command() + * + * Helper to wrap up the write_message() function. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: given command code + * [ in] payload: payload data, if any + * [ in] payload_len: length of payload data + * [ in] delay_ms_resp: delay time to get the response of command + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_reflash_send_command(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *payload, + unsigned int payload_len, unsigned int delay_ms_resp) +{ + int retval = 0; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!IS_BOOTLOADER_MODE(tcm_dev->dev_mode)) { + LOGE("Device is not in BL mode, 0x%x\n", tcm_dev->dev_mode); + retval = _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + command, + payload, + payload_len, + &resp_code, + delay_ms_resp); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", command); + goto exit; + } + +exit: + return retval; +} + +/** + * syna_tcm_read_flash() + * + * Implement the bootloader command to read specified data from flash memory. + * + * Reads to the protected bootloader code or application code areas will read + * as 0. If the number of words requested is too large, it may be truncated to + * an defined maximum read size. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] address: the address in flash memory to read + * [out] rd_data: data retrieved + * [ in] rd_len: length of data to be read + * [ in] rd_delay_ms: a short delay after the command executed + * set 'DEFAULT_FLASH_READ_DELAY' to use default, + * which is 10 us; + * set '0' or 'RESP_IN_ATTN' to select ATTN-driven. + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_read_flash(struct tcm_dev *tcm_dev, + unsigned int address, unsigned char *rd_data, + unsigned int rd_len, unsigned int rd_delay_ms) +{ + int retval = 0; + unsigned int length_words; + unsigned int flash_addr_words; + unsigned char out[6] = { 0 }; + unsigned int delay_ms = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!rd_data) { + LOGE("Invalid rd_data buffer\n"); + return _EINVAL; + } + + if (address == 0 || rd_len == 0) { + LOGE("Invalid flash address and length\n"); + retval = _EINVAL; + goto exit; + } + + length_words = rd_len / 2; + flash_addr_words = address / 2; + + LOGD("Flash address: 0x%x (words: 0x%x), size: %d (words: %d)\n", + address, flash_addr_words, rd_len, length_words); + + out[0] = (unsigned char)flash_addr_words; + out[1] = (unsigned char)(flash_addr_words >> 8); + out[2] = (unsigned char)(flash_addr_words >> 16); + out[3] = (unsigned char)(flash_addr_words >> 24); + out[4] = (unsigned char)length_words; + out[5] = (unsigned char)(length_words >> 8); + + if (rd_delay_ms == DEFAULT_FLASH_READ_DELAY) + delay_ms = FLASH_READ_DELAY_MS; + else + delay_ms = rd_delay_ms; + + if (delay_ms == RESP_IN_ATTN) { + LOGD("xfer: %d, delay: ATTN-driven\n", length_words); + } else { + delay_ms = (delay_ms * length_words) / 1000; + LOGD("xfer: %d, delay: %d\n", length_words, delay_ms); + } + + retval = syna_tcm_reflash_send_command(tcm_dev, + CMD_READ_FLASH, + out, + sizeof(out), + delay_ms); + if (retval < 0) { + LOGE("Fail to read flash data from addr 0x%x, size %d\n", + address, rd_len); + goto exit; + } + + if (tcm_dev->resp_buf.data_length != rd_len) { + LOGE("Fail to read requested length %d, rd_len %d\n", + tcm_dev->resp_buf.data_length, rd_len); + retval = _EIO; + goto exit; + } + + retval = syna_pal_mem_cpy(rd_data, + rd_len, + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + rd_len); + if (retval < 0) { + LOGE("Fail to copy read data, size %d\n", rd_len); + goto exit; + } + +exit: + return retval; +} + +/** + * syna_tcm_read_flash_boot_config() + * + * Read the data of boot config area in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [out] rd_data: buffer used for storing the retrieved data + * [ in] rd_delay_us: delay time to access data in flash memory + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_read_flash_boot_config(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + struct tcm_buffer *rd_data, unsigned int rd_delay_us) +{ + int retval; + unsigned int temp; + unsigned int addr = 0; + unsigned int length = 0; + struct tcm_boot_info *boot_info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return _EINVAL; + } + + if (!rd_data) { + LOGE("Invalid read data buffer\n"); + return _EINVAL; + } + + boot_info = &tcm_dev->boot_info; + + if (IS_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("BOOT_CONFIG not available in app fw mode %d\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + temp = VALUE(boot_info->boot_config_start_block); + addr = temp * reflash_data->write_block_size; + length = BOOT_CONFIG_SIZE * BOOT_CONFIG_SLOTS; + + if (addr == 0 || length == 0) { + LOGE("BOOT_CONFIG data area unavailable\n"); + retval = _EINVAL; + goto exit; + } + + LOGD("BOOT_CONFIG address: 0x%x, length: %d\n", addr, length); + + retval = syna_tcm_buf_alloc(rd_data, length); + if (retval < 0) { + LOGE("Fail to allocate memory for rd_data buffer\n"); + goto exit; + } + + retval = syna_tcm_read_flash(tcm_dev, addr, rd_data->buf, + length, rd_delay_us); + if (retval < 0) { + LOGE("Fail to read BOOT_CONFIG area (addr: 0x%x, length: %d)\n", + addr, length); + goto exit; + } + + rd_data->data_length = length; + +exit: + return retval; +} + +/** + * syna_tcm_read_flash_app_config() + * + * Read the data of app config area in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [out] rd_data: buffer used for storing the retrieved data + * [ in] rd_delay_us: delay time to access data in flash memory + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_read_flash_app_config(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + struct tcm_buffer *rd_data, unsigned int rd_delay_us) +{ + int retval; + unsigned int temp; + unsigned int addr = 0; + unsigned int length = 0; + struct tcm_application_info *app_info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return _EINVAL; + } + + if (!rd_data) { + LOGE("Invalid read data buffer\n"); + return _EINVAL; + } + + app_info = &tcm_dev->app_info; + + if (IS_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("APP_CONFIG not available in app fw mode %d\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + temp = VALUE(app_info->app_config_start_write_block); + addr = temp * reflash_data->write_block_size; + length = VALUE(app_info->app_config_size); + + if (addr == 0 || length == 0) { + LOGE("APP_CONFIG data area unavailable\n"); + retval = _EINVAL; + goto exit; + } + + LOGD("APP_CONFIG address: 0x%x, length: %d\n", addr, length); + + retval = syna_tcm_buf_alloc(rd_data, length); + if (retval < 0) { + LOGE("Fail to allocate memory for rd_data buffer\n"); + goto exit; + } + + retval = syna_tcm_read_flash(tcm_dev, addr, rd_data->buf, + length, rd_delay_us); + if (retval < 0) { + LOGE("Fail to read APP_CONFIG area (addr: 0x%x, length: %d)\n", + addr, length); + goto exit; + } + + rd_data->data_length = length; + +exit: + return retval; +} + +/** + * syna_tcm_read_flash_disp_config() + * + * Read the data of display config area in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [out] rd_data: buffer used for storing the retrieved data + * [ in] rd_delay_us: delay time to access data in flash memory + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_read_flash_disp_config(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + struct tcm_buffer *rd_data, unsigned int rd_delay_us) +{ + int retval; + unsigned int temp; + unsigned int addr = 0; + unsigned int length = 0; + struct tcm_boot_info *boot_info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return _EINVAL; + } + + if (!rd_data) { + LOGE("Invalid read data buffer\n"); + return _EINVAL; + } + + boot_info = &tcm_dev->boot_info; + + if (IS_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("DISP_CONFIG not available in app fw mode %d\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + temp = VALUE(boot_info->display_config_start_block); + addr = temp * reflash_data->write_block_size; + temp = VALUE(boot_info->display_config_length_blocks); + length = temp * reflash_data->write_block_size; + + if (addr == 0 || length == 0) { + LOGE("DISP_CONFIG data area unavailable\n"); + retval = _EINVAL; + goto exit; + } + + LOGD("DISP_CONFIG address: 0x%x, length: %d\n", addr, length); + + retval = syna_tcm_buf_alloc(rd_data, length); + if (retval < 0) { + LOGE("Fail to allocate memory for rd_data buffer\n"); + goto exit; + } + + retval = syna_tcm_read_flash(tcm_dev, addr, rd_data->buf, + length, rd_delay_us); + if (retval < 0) { + LOGE("Fail to read DISP_CONFIG area (addr: 0x%x, length: %d)\n", + addr, length); + goto exit; + } + + rd_data->data_length = length; + +exit: + return retval; +} + +/** + * syna_tcm_read_flash_custom_otp() + * + * Read the data of custom OTP area in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [out] rd_data: buffer used for storing the retrieved data + * [ in] rd_delay_us: delay time to access data in flash memory + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_read_flash_custom_otp(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + struct tcm_buffer *rd_data, unsigned int rd_delay_us) +{ + int retval; + unsigned int temp; + unsigned int addr = 0; + unsigned int length = 0; + struct tcm_boot_info *boot_info; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return _EINVAL; + } + + if (!rd_data) { + LOGE("Invalid read data buffer\n"); + return _EINVAL; + } + + boot_info = &tcm_dev->boot_info; + + if (IS_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("CUSTOM_OTP not available in app fw mode %d\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + temp = VALUE(boot_info->custom_otp_start_block); + addr = temp * reflash_data->write_block_size; + temp = VALUE(boot_info->custom_otp_length_blocks); + length = temp * reflash_data->write_block_size; + + if (addr == 0 || length == 0) { + LOGE("CUSTOM_OTP data area unavailable\n"); + retval = _EINVAL; + goto exit; + } + + LOGD("CUSTOM_OTP address: 0x%x, length: %d\n", addr, length); + + retval = syna_tcm_buf_alloc(rd_data, length); + if (retval < 0) { + LOGE("Fail to allocate memory for rd_data buffer\n"); + goto exit; + } + + retval = syna_tcm_read_flash(tcm_dev, addr, rd_data->buf, + length, rd_delay_us); + if (retval < 0) { + LOGE("Fail to read CUSTOM_OTP area (addr: 0x%x, length: %d)\n", + addr, length); + goto exit; + } + + rd_data->data_length = length; + +exit: + return retval; +} + +/** + * syna_tcm_read_flash_custom_data() + * + * Read the data of custom data in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [ in] address: address generated by get_flash_data_location() + * [ in] size: size generated by get_flash_data_location() + * [out] rd_data: buffer used for storing the retrieved data + * [ in] rd_delay_us: delay time to access data in flash memory + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_read_flash_custom_data(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + unsigned int address, unsigned int size, + struct tcm_buffer *rd_data, unsigned int rd_delay_us) +{ + int retval; + unsigned int addr = 0; + unsigned int length = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return _EINVAL; + } + + if (!rd_data) { + LOGE("Invalid read data buffer\n"); + return _EINVAL; + } + + if (IS_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Custom data not available in app fw mode %d\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + addr = address * reflash_data->write_block_size; + length = size * reflash_data->write_block_size; + + if (addr == 0 || length == 0) { + LOGE("Custom data area unavailable\n"); + retval = _EINVAL; + goto exit; + } + + LOGD("Custom data address: 0x%x, length: %d\n", addr, length); + + retval = syna_tcm_buf_alloc(rd_data, length); + if (retval < 0) { + LOGE("Fail to allocate memory for rd_data buffer\n"); + goto exit; + } + + retval = syna_tcm_read_flash(tcm_dev, addr, rd_data->buf, + length, rd_delay_us); + if (retval < 0) { + LOGE("Fail to read custom data (addr: 0x%x, length: %d)\n", + addr, length); + goto exit; + } + + rd_data->data_length = length; + +exit: + return retval; +} +/** + * syna_tcm_read_flash_area() + * + * Entry function to read in the data of specific area in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] area: flash area to read + * [out] data: buffer storing the retrieved data + * [ in] rd_delay_us: delay time in micro-sec to read words data from flash. + * set 'DEFAULT_FLASH_READ_DELAY' to use default, + * which is 10 us; + * set '0' or 'RESP_IN_ATTN' to select ATTN-driven. + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_read_flash_area(struct tcm_dev *tcm_dev, + enum flash_area area, struct tcm_buffer *rd_data, + unsigned int rd_delay_us) +{ + int retval; + unsigned int addr = 0; + unsigned int length = 0; + struct tcm_reflash_data_blob reflash_data; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!rd_data) { + LOGE("Invalid data buffer\n"); + return _EINVAL; + } + + switch (area) { + case AREA_CUSTOM_LCM: + case AREA_CUSTOM_OEM: + case AREA_PPDT: + case AREA_FORCE_TUNING: + case AREA_OPEN_SHORT_TUNING: + retval = syna_tcm_get_flash_data_location(tcm_dev, + area, &addr, &length); + if (retval < 0) { + LOGE("Fail to get data location of 0x%x\n", area); + return retval; + } + break; + default: + break; + } + + retval = syna_tcm_set_up_flash_access(tcm_dev, + &reflash_data); + if (retval < 0) { + LOGE("Fail to set up flash access\n"); + return retval; + } + + syna_tcm_buf_init(&reflash_data.out); + + switch (area) { + case AREA_BOOT_CONFIG: + retval = syna_tcm_read_flash_boot_config(tcm_dev, + &reflash_data, rd_data, rd_delay_us); + if (retval < 0) { + LOGE("Fail to get boot config data\n"); + goto exit; + } + break; + case AREA_APP_CONFIG: + retval = syna_tcm_read_flash_app_config(tcm_dev, + &reflash_data, rd_data, rd_delay_us); + if (retval < 0) { + LOGE("Fail to get app config data\n"); + goto exit; + } + break; + case AREA_DISP_CONFIG: + retval = syna_tcm_read_flash_disp_config(tcm_dev, + &reflash_data, rd_data, rd_delay_us); + if (retval < 0) { + LOGE("Fail to get disp config data\n"); + goto exit; + } + break; + case AREA_CUSTOM_OTP: + retval = syna_tcm_read_flash_custom_otp(tcm_dev, + &reflash_data, rd_data, rd_delay_us); + if (retval < 0) { + LOGE("Fail to get custom otp data\n"); + goto exit; + } + break; + case AREA_CUSTOM_LCM: + case AREA_CUSTOM_OEM: + case AREA_PPDT: + case AREA_FORCE_TUNING: + case AREA_OPEN_SHORT_TUNING: + retval = syna_tcm_read_flash_custom_data(tcm_dev, + &reflash_data, addr, length, rd_data, + rd_delay_us); + break; + default: + LOGE("Invalid data area\n"); + retval = _EINVAL; + goto exit; + } + + LOGI("%s read\n", AREA_ID_STR(area)); + + retval = 0; + +exit: + retval = syna_tcm_switch_fw_mode(tcm_dev, + MODE_APPLICATION_FIRMWARE, + FW_MODE_SWITCH_DELAY_MS); + if (retval < 0) + LOGE("Fail to go back to application firmware\n"); + + syna_tcm_buf_release(&reflash_data.out); + + return retval; +} + +/** + * syna_tcm_write_flash() + * + * Implement the bootloader command to write specified data to flash memory. + * + * If the length of the data to write is not an integer multiple of words, + * the trailing byte will be discarded. If the length of the data to write + * is not an integer number of write blocks, it will be zero-padded to the + * next write block. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [ in] address: the address in flash memory to write + * [ in] wr_data: data to write + * [ in] wr_len: length of data to write + * [ in] wr_delay_ms: a short delay after the command executed + * set 'DEFAULT_FLASH_WRITE_DELAY' to use default, + * which is 20 us; + * set '0' or 'RESP_IN_ATTN' to select ATTN-driven. + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_write_flash(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + unsigned int address, const unsigned char *wr_data, + unsigned int wr_len, unsigned int wr_delay_ms) +{ + int retval; + unsigned int offset; + unsigned int w_length; + unsigned int xfer_length; + unsigned int remaining_length; + unsigned int flash_address; + unsigned int block_address; + unsigned int delay_ms; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + w_length = tcm_dev->max_wr_size - 8; + + w_length = w_length - (w_length % reflash_data->write_block_size); + + w_length = MIN(w_length, reflash_data->max_write_payload_size); + + offset = 0; + + remaining_length = wr_len; + + syna_tcm_buf_lock(&reflash_data->out); + + while (remaining_length) { + if (remaining_length > w_length) + xfer_length = w_length; + else + xfer_length = remaining_length; + + retval = syna_tcm_buf_alloc(&reflash_data->out, + xfer_length + 2); + if (retval < 0) { + LOGE("Fail to allocate memory for buf.out\n"); + syna_tcm_buf_unlock(&reflash_data->out); + return retval; + } + + flash_address = address + offset; + block_address = flash_address / reflash_data->write_block_size; + reflash_data->out.buf[0] = (unsigned char)block_address; + reflash_data->out.buf[1] = (unsigned char)(block_address >> 8); + + retval = syna_pal_mem_cpy(&reflash_data->out.buf[2], + reflash_data->out.buf_size - 2, + &wr_data[offset], + wr_len - offset, + xfer_length); + if (retval < 0) { + LOGE("Fail to copy write data ,size: %d\n", + xfer_length); + syna_tcm_buf_unlock(&reflash_data->out); + return retval; + } + + if (wr_delay_ms == DEFAULT_FLASH_WRITE_DELAY) + delay_ms = FLASH_WRITE_DELAY_MS; + else + delay_ms = wr_delay_ms; + + if (delay_ms == RESP_IN_ATTN) { + LOGD("xfer: %d, delay: ATTN-driven\n", xfer_length); + } else { + delay_ms = (delay_ms * xfer_length) / 1000; + LOGD("xfer: %d, delay: %d\n", xfer_length, delay_ms); + } + + retval = syna_tcm_reflash_send_command(tcm_dev, + CMD_WRITE_FLASH, + reflash_data->out.buf, + xfer_length + 2, + delay_ms); + if (retval < 0) { + LOGE("Fail to write data to flash addr 0x%x, size %d\n", + flash_address, xfer_length + 2); + syna_tcm_buf_unlock(&reflash_data->out); + return retval; + } + + offset += xfer_length; + remaining_length -= xfer_length; + } + + syna_tcm_buf_unlock(&reflash_data->out); + + return 0; +} + +/** + * syna_tcm_write_flash_block() + * + * Write data to the target block data area in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [ in] area: target block area to write + * [ in] wr_delay_us: delay time in micro-sec to write block data to flash. + * set 'DEFAULT_FLASH_WRITE_DELAY' to use default, + * which is 20 ms; + * set '0' or 'RESP_IN_ATTN' to select ATTN-driven. + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_write_flash_block(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + struct block_data *block, unsigned int wr_delay_us) +{ + int retval; + unsigned int size; + unsigned int flash_addr; + const unsigned char *data; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return _EINVAL; + } + + if (!block) { + LOGE("Invalid block data\n"); + return _EINVAL; + } + + data = block->data; + size = block->size; + flash_addr = block->flash_addr; + + LOGD("Write data to %s - address: 0x%x, size: %d\n", + AREA_ID_STR(block->id), flash_addr, size); + + if (size == 0) { + LOGI("No need to update, size = %d\n", size); + goto exit; + } + + retval = syna_tcm_write_flash(tcm_dev, reflash_data, + flash_addr, data, size, wr_delay_us); + if (retval < 0) { + LOGE("Fail to write %s to flash (addr: 0x%x, size: %d)\n", + AREA_ID_STR(block->id), flash_addr, size); + return retval; + } + +exit: + LOGN("%s area written\n", AREA_ID_STR(block->id)); + + return 0; +} + +/** + * syna_tcm_erase_flash() + * + * Implement the bootloader command, which is used to erase the specified + * blocks of flash memory. + * + * Until this command completes, the device may be unresponsive. + * Therefore, this helper is implemented as a blocked function, and the delay + * time is set to 200 ms in default. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [ in] address: the address in flash memory to read + * [ in] size: size of data to write + * [ in] erase_delay_ms: the delay time to get the resp from mass erase + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_erase_flash(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + unsigned int address, unsigned int size, + unsigned int erase_delay_ms) +{ + int retval; + unsigned int page_start = 0; + unsigned int page_count = 0; + unsigned char out_buf[4] = {0}; + int size_erase_cmd; + + page_start = address / reflash_data->page_size; + + page_count = syna_pal_ceil_div(size, reflash_data->page_size); + + LOGD("Page start = %d (0x%04x), Page count = %d (0x%04x)\n", + page_start, page_start, page_count, page_count); + + if ((page_start > 0xff) || (page_count > 0xff)) { + size_erase_cmd = 4; + + out_buf[0] = (unsigned char)(page_start & 0xff); + out_buf[1] = (unsigned char)((page_start >> 8) & 0xff); + out_buf[2] = (unsigned char)(page_count & 0xff); + out_buf[3] = (unsigned char)((page_count >> 8) & 0xff); + } else { + size_erase_cmd = 2; + + out_buf[0] = (unsigned char)(page_start & 0xff); + out_buf[1] = (unsigned char)(page_count & 0xff); + } + + retval = syna_tcm_reflash_send_command(tcm_dev, + CMD_ERASE_FLASH, + out_buf, + size_erase_cmd, + erase_delay_ms); + if (retval < 0) { + LOGE("Fail to erase data at flash page 0x%x, count %d\n", + page_start, page_count); + return retval; + } + + return 0; +} + +/** + * syna_tcm_erase_flash_block() + * + * Mass erase the target block data area in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [ in] block: target block area to erase + * [ in] delay_ms: a short delay after the erase command executed + * set 'DEFAULT_FLASH_ERASE_DELAY' to use default, + * which is 500 ms; + * set '0' or 'RESP_IN_ATTN' to select ATTN-driven. + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_erase_flash_block(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + struct block_data *block, unsigned int delay_ms) +{ + int retval; + unsigned int size; + unsigned int flash_addr; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return _EINVAL; + } + + if (!block) { + LOGE("Invalid block data\n"); + return _EINVAL; + } + + flash_addr = block->flash_addr; + + size = block->size; + + if (delay_ms == DEFAULT_FLASH_ERASE_DELAY) + delay_ms = FLASH_ERASE_DELAY_MS; + + LOGD("Erase %s block - address: 0x%x, size: %d\n", + AREA_ID_STR(block->id), flash_addr, size); + + if (size == 0) { + LOGI("No need to erase, size = %d\n", size); + goto exit; + } + + retval = syna_tcm_erase_flash(tcm_dev, reflash_data, + flash_addr, size, delay_ms); + if (retval < 0) { + LOGE("Fail to erase %s data (addr: 0x%x, size: %d)\n", + AREA_ID_STR(block->id), flash_addr, size); + return retval; + } + +exit: + LOGN("%s area erased\n", AREA_ID_STR(block->id)); + + return 0; +} + +/** + * syna_tcm_update_flash_block() + * + * Perform the reflash sequence to the target area + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * [ in] block: target block area to update + * [ in] delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * for polling, set a value formatted with + * [erase | write]; + * for ATTN-driven, set a '0' or 'RESP_IN_ATTN' + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_update_flash_block(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + struct block_data *block, unsigned int delay_ms) +{ + int retval; + unsigned int erase_delay_ms = (delay_ms >> 16) & 0xFFFF; + unsigned int wr_blk_delay_ms = delay_ms & 0xFFFF; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash data blob\n"); + return _EINVAL; + } + + if (!block) { + LOGE("Invalid block data\n"); + return _EINVAL; + } + + /* reflash is not needed for the partition */ + retval = syna_tcm_check_flash_block(tcm_dev, + reflash_data, + block); + if (retval < 0) { + LOGE("Invalid %s area\n", AREA_ID_STR(block->id)); + return retval; + } + + if (retval == DO_NONE) + return 0; + + LOGN("Prepare to erase %s area\n", AREA_ID_STR(block->id)); + + retval = syna_tcm_erase_flash_block(tcm_dev, + reflash_data, + block, + erase_delay_ms); + if (retval < 0) { + LOGE("Fail to erase %s area\n", AREA_ID_STR(block->id)); + return retval; + } + + LOGN("Prepare to update %s area\n", AREA_ID_STR(block->id)); + + retval = syna_tcm_write_flash_block(tcm_dev, + reflash_data, + block, + wr_blk_delay_ms); + if (retval < 0) { + LOGE("Fail to write %s area\n", AREA_ID_STR(block->id)); + return retval; + } + + return 0; +} + +/** + * syna_tcm_do_reflash_tddi() + * + * Implement the sequence specific for MODE_TDDI_BOOTLOADER. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: misc. data used for fw update + * [ in] type: the area to update + * [ in] delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * for polling, set a value formatted with + * [erase | write]; + * for ATTN-driven, set a '0' or 'RESP_IN_ATTN' + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_do_reflash_tddi(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + enum update_area type, unsigned int delay_ms) +{ + int retval = 0; + int idx; + unsigned int erase_delay_ms = (delay_ms >> 16) & 0xFFFF; + unsigned int wr_blk_delay_ms = delay_ms & 0xFFFF; + struct image_info *image_info; + struct block_data *block; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash_data blob\n"); + return _EINVAL; + } + + image_info = &reflash_data->image_info; + + if (tcm_dev->dev_mode != MODE_TDDI_BOOTLOADER) { + LOGE("Incorrect bootloader mode, 0x%02x, expected: 0x%02x\n", + tcm_dev->dev_mode, MODE_TDDI_BOOTLOADER); + return _EINVAL; + } + + if (type == UPDATE_NONE) + goto exit; + + /* Always mass erase all blocks, before writing the data */ + for (idx = 0; idx < AREA_MAX; idx++) { + + block = &image_info->data[idx]; + + if (!block->available) + continue; + + retval = syna_tcm_check_flash_block(tcm_dev, + reflash_data, + block); + if (retval == DO_NONE) + continue; + + LOGN("Prepare to erase %s area\n", AREA_ID_STR(block->id)); + + retval = syna_tcm_erase_flash_block(tcm_dev, + reflash_data, + block, + erase_delay_ms); + if (retval < 0) { + LOGE("Fail to erase %s area\n", AREA_ID_STR(block->id)); + goto exit; + } + } + + /* Write all the data to flash */ + for (idx = 0; idx < AREA_MAX; idx++) { + + block = &image_info->data[idx]; + + if (!block->available) + continue; + + retval = syna_tcm_check_flash_block(tcm_dev, + reflash_data, + block); + if (retval == DO_NONE) + continue; + + LOGN("Prepare to update %s area\n", AREA_ID_STR(block->id)); + + retval = syna_tcm_write_flash_block(tcm_dev, + reflash_data, + block, + wr_blk_delay_ms); + if (retval < 0) { + LOGE("Fail to update %s area\n", + AREA_ID_STR(block->id)); + goto exit; + } + } + +exit: + return retval; +} + +/** + * syna_tcm_do_reflash_generic() + * + * Implement the generic sequence of fw update in MODE_BOOTLOADER. + * + * Typically, it is applied on most of discrete touch controllers + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: misc. data used for fw update + * [ in] type: the area to update + * [ in] wait_delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * for polling, set a value formatted with + * [erase | write]; + * for ATTN-driven, set a '0' or 'RESP_IN_ATTN' + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_do_reflash_generic(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data, + enum update_area type, unsigned int wait_delay_ms) +{ + int retval = 0; + struct block_data *block; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!reflash_data) { + LOGE("Invalid reflash_data blob\n"); + return _EINVAL; + } + + if (tcm_dev->dev_mode != MODE_BOOTLOADER) { + LOGE("Incorrect bootloader mode, 0x%02x, expected: 0x%02x\n", + tcm_dev->dev_mode, MODE_BOOTLOADER); + return _EINVAL; + } + + switch (type) { + case UPDATE_FIRMWARE_CONFIG: + block = &reflash_data->image_info.data[AREA_APP_CODE]; + + retval = syna_tcm_update_flash_block(tcm_dev, + reflash_data, + block, + wait_delay_ms); + if (retval < 0) { + LOGE("Fail to update application firmware\n"); + goto exit; + } + case UPDATE_CONFIG_ONLY: + block = &reflash_data->image_info.data[AREA_APP_CONFIG]; + + retval = syna_tcm_update_flash_block(tcm_dev, + reflash_data, + block, + wait_delay_ms); + if (retval < 0) { + LOGE("Fail to update application config\n"); + goto exit; + } + break; + case UPDATE_NONE: + default: + break; + } +exit: + return retval; +} + +/** + * syna_tcm_do_fw_update() + * + * The entry function to perform fw update upon TouchBoot. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] image: binary data to write + * [ in] image_size: size of data array + * [ in] wait_delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * for polling, set a value formatted with + * [erase | write]; + * for ATTN-driven, set a '0' or 'RESP_IN_ATTN' + * [ in] force_reflash: '1' to do reflash anyway + * '0' to compare ID info before doing reflash. + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_do_fw_update(struct tcm_dev *tcm_dev, + const unsigned char *image, unsigned int image_size, + unsigned int wait_delay_ms, bool force_reflash) +{ + int retval; + enum update_area type = UPDATE_NONE; + struct tcm_reflash_data_blob reflash_data; + int app_status; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if ((!image) || (image_size == 0)) { + LOGE("Invalid image data\n"); + return _EINVAL; + } + + LOGN("Prepare to do reflash\n"); + + syna_tcm_buf_init(&reflash_data.out); + + reflash_data.image = image; + reflash_data.image_size = image_size; + syna_pal_mem_set(&reflash_data.image_info, 0x00, + sizeof(struct image_info)); + + retval = syna_tcm_parse_fw_image(image, &reflash_data.image_info); + if (retval < 0) { + LOGE("Fail to parse firmware image\n"); + return retval; + } + + LOGN("Start of reflash\n"); + + ATOMIC_SET(tcm_dev->firmware_flashing, 1); + + if (force_reflash) { + type = UPDATE_FIRMWARE_CONFIG; + goto reflash; + } + + app_status = syna_pal_le2_to_uint(tcm_dev->app_info.status); + if ((IS_APP_FW_MODE(tcm_dev->dev_mode)) && (app_status != APP_STATUS_OK)) { + LOGN("Bad application firmware, need to update firmware and config\n"); + type = UPDATE_FIRMWARE_CONFIG; + goto reflash; + } + + type = (enum update_area)syna_tcm_compare_image_id_info(tcm_dev, + &reflash_data); + + if (type == UPDATE_NONE) + goto exit; + +reflash: + syna_tcm_buf_init(&reflash_data.out); + + /* set up flash access, and enter the bootloader mode */ + retval = syna_tcm_set_up_flash_access(tcm_dev, &reflash_data); + if (retval < 0) { + LOGE("Fail to set up flash access\n"); + goto exit; + } + + /* perform the fw update */ + if (tcm_dev->dev_mode == MODE_BOOTLOADER) { + retval = syna_tcm_do_reflash_generic(tcm_dev, + &reflash_data, + type, + wait_delay_ms); + } else if (tcm_dev->dev_mode == MODE_TDDI_BOOTLOADER) { + retval = syna_tcm_do_reflash_tddi(tcm_dev, + &reflash_data, + type, + wait_delay_ms); + } else { + LOGE("Incorrect bootloader mode, 0x%02x\n", + tcm_dev->dev_mode); + goto reset; + } + + if (retval < 0) { + LOGE("Fail to do firmware update\n"); + goto reset; + } + + LOGN("End of reflash\n"); + + retval = 0; +reset: + retval = syna_tcm_reset(tcm_dev); + if (retval < 0) { + LOGE("Fail to do reset\n"); + goto exit; + } + +exit: + ATOMIC_SET(tcm_dev->firmware_flashing, 0); + + syna_tcm_buf_release(&reflash_data.out); + + return retval; +} + diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_reflash.h b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_reflash.h new file mode 100644 index 000000000000..02d43ca72716 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_reflash.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file synaptics_touchcom_func_reflash.h + * + * This file declares relevant functions and structures for TouchBoot. + */ + +#ifndef _SYNAPTICS_TOUCHCOM_REFLASH_FUNCS_H_ +#define _SYNAPTICS_TOUCHCOM_REFLASH_FUNCS_H_ + +#include "synaptics_touchcom_core_dev.h" +#include "synaptics_touchcom_func_base_flash.h" + +/** + * @section: Blocks to be updated + */ +enum update_area { + UPDATE_NONE = 0, + UPDATE_FIRMWARE_CONFIG, + UPDATE_CONFIG_ONLY, + UPDATE_ALL_BLOCKS, +}; + +/** + * @section: Data Type in flash memory + */ +enum flash_data { + FLASH_LCM_DATA = 1, + FLASH_OEM_DATA, + FLASH_PPDT_DATA, + FLASH_FORCE_CALIB_DATA, + FLASH_OPEN_SHORT_TUNING_DATA, +}; + +/** + * @section: Specific data blob for reflash + * + * The structure contains various parameters being used in reflash + */ +struct tcm_reflash_data_blob { + /* binary data of an image file */ + const unsigned char *image; + unsigned int image_size; + /* parsed data based on given image file */ + struct image_info image_info; + /* standard information for flash access */ + unsigned int page_size; + unsigned int write_block_size; + unsigned int max_write_payload_size; + /* temporary buffer during the reflash */ + struct tcm_buffer out; + struct syna_tcm *tcm; +}; + +/** + * syna_tcm_compare_image_id_info() + * + * Compare the ID information between device and the image file, + * and determine the area to be updated. + * The function should be called after parsing the image file. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] reflash_data: data blob for reflash + * + * @return + * Comparison result + */ +int syna_tcm_compare_image_id_info(struct tcm_dev *tcm_dev, + struct tcm_reflash_data_blob *reflash_data); + +/** + * syna_tcm_read_flash_area() + * + * Entry function to read in the data of specific area in the flash memory. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] area: flash area to read + * [out] data: buffer storing the retrieved data + * [ in] rd_delay_us: delay time in micro-sec to read words data from flash. + * set '0' to use default time, which is 10 us; + * set 'FORCE_ATTN_DRIVEN' to adopt ATTN-driven. + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_read_flash_area(struct tcm_dev *tcm_dev, + enum flash_area area, struct tcm_buffer *rd_data, + unsigned int rd_delay_us); + +/** + * syna_tcm_do_fw_update() + * + * The entry function to perform fw update upon TouchBoot. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] image: image file given + * [ in] image_size: size of data array + * [ in] wait_delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * for polling, set a value formatted with + * [erase | write]; + * for ATTN-driven, set a '0' or 'RESP_IN_ATTN' + * [ in] force_reflash: '1' to do reflash anyway + * '0' to compare ID info before doing reflash. + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_do_fw_update(struct tcm_dev *tcm_dev, + const unsigned char *image, unsigned int image_size, + unsigned int wait_delay_ms, bool force_reflash); + +#endif /* end of _SYNAPTICS_TOUCHCOM_REFLASH_FUNCS_H_ */ diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_romboot.c b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_romboot.c new file mode 100644 index 000000000000..e144fa305d91 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_romboot.c @@ -0,0 +1,1586 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file synaptics_touchcom_func_romboot.c + * + * This file implements the ROM boot-loader related functions. + * The declarations are available in synaptics_touchcom_func_romboot.h. + */ + +#include "synaptics_touchcom_func_base.h" +#include "synaptics_touchcom_func_romboot.h" + +#define JEDEC_STATUS_CHECK_US_MIN 5000 +#define JEDEC_STATUS_CHECK_US_MAX 10000 + +#define BINARY_FILE_MAGIC_VALUE 0xaa55 + +#define ROMBOOT_FLASH_PAGE_SIZE 256 + +/** + * @section: JEDEC flash command set + */ +enum flash_command { + JEDEC_PAGE_PROGRAM = 0x02, + JEDEC_READ_STATUS = 0x05, + JEDEC_WRITE_ENABLE = 0x06, + JEDEC_CHIP_ERASE = 0xc7, +}; + + +/** + * syna_tcm_romboot_send_command() + * + * Helper to send a packet to ROM bootloader. + * + * Please be noted that the given packet must be formatted into the + * specific structure in order to communicate with flash. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] out: data sent out, if any + * [ in] out_size: size of data sent out + * [ in] in: buffer to store the data read in + * [ in] in_size: size of data read in + * [ in] delay_ms_resp: delay time to get the response + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_send_command(struct tcm_dev *tcm_dev, + unsigned char *out, unsigned int out_size, unsigned char *in, + unsigned int in_size, unsigned int delay_ms_resp) +{ + int retval; + unsigned char resp_code; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (out_size < sizeof(struct flash_param)) { + LOGE("Invalid size of out data, %d, min. size:%d\n", + out_size, (int)sizeof(struct flash_param)); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_SPI_MASTER_WRITE_THEN_READ_EXTENDED, + out, + out_size, + &resp_code, + delay_ms_resp); + if (retval < 0) { + LOGE("Fail to send romboot flash command 0x%02x\n", + CMD_SPI_MASTER_WRITE_THEN_READ_EXTENDED); + goto exit; + } + + LOGD("resp_code: 0x%x, resp length: %d\n", + resp_code, tcm_dev->resp_buf.data_length); + + if ((in == NULL) || (in_size < tcm_dev->resp_buf.data_length)) + goto exit; + + /* copy resp data to caller */ + retval = syna_pal_mem_cpy((unsigned char *)in, + in_size, + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + tcm_dev->resp_buf.data_length); + if (retval < 0) { + LOGE("Fail to copy resp data to caller\n"); + goto exit; + } + +exit: + return retval; +} + + +/** + * syna_tcm_romboot_multichip_send_command() + * + * Send a command code to the ROM bootloader inside the multi-chip device + * + * @param + * [ in] tcm_dev: the device handle + * [ in] command: command to send + * [ in] out: additional data sent out, if any + * [ in] out_size: size of data sent out + * [ in] in: buffer to store the data read in + * [ in] in_size: size of data read in + * [ in] delay_ms: delay time to get the response + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_multichip_send_command(struct tcm_dev *tcm_dev, + unsigned char command, unsigned char *out, + unsigned int out_size, unsigned char *in, + unsigned int in_size, unsigned int delay_ms) +{ + int retval; + unsigned char *payld_buf = NULL; + unsigned int payld_size; + struct flash_param flash_param; + unsigned int offset = (int)sizeof(struct flash_param); + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + syna_pal_mem_set((void *)&flash_param, 0x00, sizeof(flash_param)); + + flash_param.read_size[0] = (unsigned char)(in_size & 0xff); + flash_param.read_size[1] = (unsigned char)(in_size >> 8) & 0xff; + + flash_param.command = command; + + payld_size = offset + out_size; + if (flash_param.command != 0x00) + payld_size += 2; + + LOGD("Command: 0x%02x, packet size: %d, wr:%d, rd:%d\n", + command, payld_size, out_size, in_size); + + payld_buf = syna_pal_mem_alloc(payld_size, sizeof(unsigned char)); + if (!payld_buf) { + LOGE("Fail to allocate buffer to store flash command\n"); + return _ENOMEM; + } + + if (flash_param.command != 0x00) { + payld_buf[offset] = (unsigned char)out_size; + payld_buf[offset + 1] = (unsigned char)(out_size >> 8); + + if (out_size > 0) { + retval = syna_pal_mem_cpy(&payld_buf[offset + 2], + payld_size - offset - 2, + out, + out_size, + out_size); + if (retval < 0) { + LOGE("Fail to copy payload to payld_buf\n"); + goto exit; + } + } + + LOGD("Packet: %02x %02x %02x %02x %02x %02x %02x %02x\n", + flash_param.byte0, flash_param.byte1, flash_param.byte2, + flash_param.read_size[0], flash_param.read_size[1], + flash_param.command, payld_buf[offset], + payld_buf[offset + 1]); + } + + retval = syna_pal_mem_cpy(payld_buf, payld_size, + &flash_param, sizeof(flash_param), sizeof(flash_param)); + if (retval < 0) { + LOGE("Fail to copy flash_param header to payld_buf\n"); + goto exit; + } + + retval = syna_tcm_romboot_send_command(tcm_dev, + payld_buf, + payld_size, + in, + in_size, + delay_ms); + if (retval < 0) { + LOGE("Fail to write command 0x%x\n", flash_param.command); + goto exit; + } + +exit: + syna_pal_mem_free((void *)payld_buf); + + return retval; +} +/** + * syna_tcm_romboot_multichip_get_resp() + * + * To get the response data from ROM bootloader inside the multi-chip device + * + * @param + * [ in] tcm_dev: the device handle + * [ in] length: size to read + * [ in] resp: buffer to store the resp data + * [ in] resp_size: size of resp data + * [ in] delay_ms: delay time to get the response + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_multichip_get_resp(struct tcm_dev *tcm_dev, + unsigned int length, unsigned char *resp, + unsigned int resp_size, unsigned int delay_ms) +{ + int retval; + unsigned char *tmp_buf = NULL; + unsigned int xfer_len; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (resp && (resp_size < length)) { + LOGE("Invalid buffer size, len:%d, size:%d\n", + length, resp_size); + return _EINVAL; + } + + xfer_len = length + 2; + + tmp_buf = syna_pal_mem_alloc(xfer_len, sizeof(unsigned char)); + if (!tmp_buf) { + LOGE("Fail to allocate tmp_buf\n"); + return _ENOMEM; + } + + retval = syna_tcm_romboot_multichip_send_command(tcm_dev, + CMD_NONE, NULL, 0, + tmp_buf, xfer_len, delay_ms); + if (retval < 0) { + LOGE("Fail to get resp, size: %d\n", xfer_len); + goto exit; + } + + if (resp) { + retval = syna_pal_mem_cpy(resp, resp_size, + &tmp_buf[1], xfer_len - 1, length); + if (retval < 0) { + LOGE("Fail to copy resp data\n"); + goto exit; + } + } + +exit: + syna_pal_mem_free((void *)tmp_buf); + + return retval; +} +/** + * syna_tcm_romboot_multichip_get_status() + * + * To poll the status until the completion + * + * @param + * [ in] tcm_dev: the device handle + * [ in] resp_status: response status returned + * [ in] resp_length: response length returned + * [ in] delay_ms: delay time to get the response + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_multichip_get_status(struct tcm_dev *tcm_dev, + unsigned char *resp_status, unsigned int *resp_length, + unsigned int delay_ms) +{ + int retval; + unsigned char resp[4] = { 0 }; + int timeout = 0; + int MAX_TIMEOUT = 1000; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + syna_pal_sleep_ms(delay_ms); + + do { + retval = syna_tcm_romboot_multichip_send_command(tcm_dev, + CMD_NONE, NULL, 0, + resp, 3, delay_ms); + if (retval < 0) { + LOGE("Fail to poll the resp\n"); + goto exit; + } + + LOGD("status: %02x %02x %02x\n", resp[0], resp[1], resp[2]); + + if (resp[0] == 0xff) { + syna_pal_sleep_ms(100); + timeout += 100; + continue; + } else if (resp[0] == 0x01) { + *resp_status = resp[0]; + *resp_length = syna_pal_le2_to_uint(&resp[1]); + goto exit; + } else { + LOGE("Invalid resp, %02x %02x %02x\n", + resp[0], resp[1], resp[2]); + retval = _EIO; + goto exit; + } + + } while (timeout < MAX_TIMEOUT); + + if (timeout >= 500) { + LOGE("Timeout to get the status\n"); + retval = _EIO; + } +exit: + return retval; +} +/** + * syna_tcm_romboot_multichip_write_flash() + * + * Write the given binary data to the flash through the ROM bootloader + * inside the multi-chip device + * + * @param + * [ in] tcm_dev: the device handle + * [ in] romboot_data: data blob for romboot + * [ in] address: the address in flash memory to write + * [ in] wr_data: binary data to write + * [ in] wr_len: length of data to write + * [ in] wr_delay_ms: a short delay after the command executed + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_multichip_write_flash(struct tcm_dev *tcm_dev, + struct tcm_romboot_data_blob *romboot_data, + unsigned int address, const unsigned char *wr_data, + unsigned int wr_len, unsigned int wr_delay_ms) +{ + int retval; + unsigned int offset; + unsigned int w_length; + unsigned int xfer_length; + unsigned int remaining_length; + unsigned int flash_address; + unsigned int block_address; + unsigned char resp_code = 0; + unsigned int resp_length = 0; + unsigned int delay_ms; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + w_length = tcm_dev->max_wr_size - 16; + + w_length = w_length - (w_length % romboot_data->write_block_size); + + w_length = MIN(w_length, romboot_data->max_write_payload_size); + + offset = 0; + + remaining_length = wr_len; + + syna_tcm_buf_lock(&romboot_data->out); + + while (remaining_length) { + if (remaining_length > w_length) + xfer_length = w_length; + else + xfer_length = remaining_length; + + retval = syna_tcm_buf_alloc(&romboot_data->out, + xfer_length + 2); + if (retval < 0) { + LOGE("Fail to allocate memory for buf.out\n"); + goto exit; + } + + flash_address = address + offset; + block_address = flash_address / romboot_data->write_block_size; + + romboot_data->out.buf[0] = (unsigned char)block_address & 0xff; + romboot_data->out.buf[1] = (unsigned char)(block_address >> 8); + + retval = syna_pal_mem_cpy(&romboot_data->out.buf[2], + romboot_data->out.buf_size - 2, + &wr_data[offset], + wr_len - offset, + xfer_length); + if (retval < 0) { + LOGE("Fail to copy write data ,size: %d\n", + xfer_length); + goto exit; + } + + if (wr_delay_ms == 0) + delay_ms = ROMBOOT_DELAY_MS; + else + delay_ms = wr_delay_ms; + + LOGD("write xfer: %d (remaining: %d)\n", + xfer_length, remaining_length); + + retval = syna_tcm_romboot_multichip_send_command(tcm_dev, + CMD_WRITE_FLASH, + romboot_data->out.buf, + xfer_length + 2, + NULL, + 0, + delay_ms); + if (retval < 0) { + LOGE("Fail to write data to flash addr 0x%x, size %d\n", + flash_address, xfer_length + 2); + goto exit; + } + + retval = syna_tcm_romboot_multichip_get_status(tcm_dev, + &resp_code, &resp_length, ROMBOOT_DELAY_MS); + if (retval < 0) { + LOGE("Fail to get the response of command 0x%x\n", + CMD_WRITE_FLASH); + goto exit; + } + + LOGD("status:%02x, data_length:%d\n", resp_code, resp_length); + + if (resp_code != STATUS_OK) { + LOGE("Invalid response of command %x\n", + CMD_WRITE_FLASH); + retval = _EIO; + goto exit; + } + + retval = syna_tcm_romboot_multichip_get_resp(tcm_dev, + resp_length, NULL, 0, ROMBOOT_DELAY_MS); + if (retval < 0) { + LOGE("Fail to get the boot info packet\n"); + goto exit; + } + + offset += xfer_length; + remaining_length -= xfer_length; + } + + retval = 0; + +exit: + syna_tcm_buf_unlock(&romboot_data->out); + + return retval; +} +/** + * syna_tcm_romboot_multichip_erase_flash() + * + * Ask the ROM bootloader to erase the flash inside the multi-chip device + * + * @param + * [ in] tcm_dev: the device handle + * [ in] romboot_data: data blob for remboot + * [ in] address: the address in flash memory to read + * [ in] size: size of data to write + * [ in] erase_delay_ms: the delay time to get the resp from mass erase + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_multichip_erase_flash(struct tcm_dev *tcm_dev, + struct tcm_romboot_data_blob *romboot_data, + unsigned int address, unsigned int size, + unsigned int erase_delay_ms) +{ + int retval; + unsigned int page_start = 0; + unsigned int page_count = 0; + unsigned char out_buf[4] = { 0 }; + unsigned char resp_code = 0; + unsigned int resp_length = 0; + int size_erase_cmd; + + page_start = address / romboot_data->page_size; + + page_count = syna_pal_ceil_div(size, romboot_data->page_size); + + LOGD("Page start = %d (0x%04x), Page count = %d (0x%04x)\n", + page_start, page_start, page_count, page_count); + + if ((page_start > 0xff) || (page_count > 0xff)) { + size_erase_cmd = 4; + + out_buf[0] = (unsigned char)(page_start & 0xff); + out_buf[1] = (unsigned char)((page_start >> 8) & 0xff); + out_buf[2] = (unsigned char)(page_count & 0xff); + out_buf[3] = (unsigned char)((page_count >> 8) & 0xff); + } else { + size_erase_cmd = 2; + + out_buf[0] = (unsigned char)(page_start & 0xff); + out_buf[1] = (unsigned char)(page_count & 0xff); + } + + retval = syna_tcm_romboot_multichip_send_command(tcm_dev, + CMD_ERASE_FLASH, + out_buf, + size_erase_cmd, + NULL, + 0, + erase_delay_ms); + if (retval < 0) { + LOGE("Fail to erase data at 0x%x (page:0x%x, count:%d)\n", + address, page_start, page_count); + return retval; + } + + retval = syna_tcm_romboot_multichip_get_status(tcm_dev, + &resp_code, &resp_length, ROMBOOT_DELAY_MS); + if (retval < 0) { + LOGE("Fail to get the response of command 0x%x\n", + CMD_ERASE_FLASH); + return retval; + } + + LOGD("status:%02x, data_length:%d\n", resp_code, resp_length); + + if (resp_code != STATUS_OK) { + LOGE("Invalid response of command %x\n", CMD_WRITE_FLASH); + retval = _EIO; + return retval; + } + + return retval; +} + +/** + * syna_tcm_romboot_multichip_get_boot_info() + * + * To request a boot information packet from ROM bootloader inside + * the multi-chip device + * + * @param + * [ in] tcm_dev: the device handle + * [out] boot_info: the boot info packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_multichip_get_boot_info(struct tcm_dev *tcm_dev, + struct tcm_boot_info *boot_info) +{ + int retval = 0; + unsigned char resp_code; + unsigned int resp_data_len = 0; + unsigned int copy_size; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + retval = syna_tcm_romboot_multichip_send_command(tcm_dev, + CMD_GET_BOOT_INFO, NULL, 0, NULL, 0, ROMBOOT_DELAY_MS); + if (retval < 0) { + LOGE("Fail to run command 0x%x\n", CMD_GET_BOOT_INFO); + goto exit; + } + + retval = syna_tcm_romboot_multichip_get_status(tcm_dev, + &resp_code, &resp_data_len, ROMBOOT_DELAY_MS); + if (retval < 0) { + LOGE("Fail to get the response of command 0x%x\n", + CMD_GET_BOOT_INFO); + return retval; + } + + LOGD("status:%02x, data_length:%d\n", resp_code, resp_data_len); + + if (resp_code != STATUS_OK) { + LOGE("Invalid response of command %x\n", CMD_GET_BOOT_INFO); + retval = _EIO; + return retval; + } + + if (boot_info == NULL) + goto exit; + + copy_size = MIN(sizeof(struct tcm_boot_info), resp_data_len); + + retval = syna_tcm_romboot_multichip_get_resp(tcm_dev, + copy_size, (unsigned char *)boot_info, + sizeof(struct tcm_boot_info), ROMBOOT_DELAY_MS); + if (retval < 0) { + LOGE("Fail to get the boot info packet\n"); + return retval; + } + +exit: + return retval; +} +/** + * syna_tcm_romboot_preparation() + * + * Perform the preparation before doing firmware update of multi-chip device + * + * @param + * [ in] tcm_dev: the device handle + * [out] romboot_data: data blob for romboot access + * [ in] is_multichip: flag to indicate a multichip DUT + * + * @return + * Result of image file comparison + */ +static int syna_tcm_romboot_preparation(struct tcm_dev *tcm_dev, + struct tcm_romboot_data_blob *romboot_data, bool is_multichip) +{ + int retval; + unsigned int temp; + struct tcm_boot_info *boot_info; + unsigned int wr_chunk; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!romboot_data) { + LOGE("Invalid romboot data blob\n"); + return _EINVAL; + } + + LOGI("Set up preparation, multi-chip: %s\n", + (is_multichip)?"yes":"no"); + + retval = syna_tcm_identify(tcm_dev, NULL); + if (retval < 0) { + LOGE("Fail to do identification\n"); + return retval; + } + + /* switch to bootloader mode */ + if (IS_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGI("Prepare to enter bootloader mode\n"); + if (is_multichip) + retval = syna_tcm_switch_fw_mode(tcm_dev, + MODE_MULTICHIP_TDDI_BOOTLOADER, + FW_MODE_SWITCH_DELAY_MS); + else + retval = syna_tcm_switch_fw_mode(tcm_dev, + MODE_TDDI_BOOTLOADER, + FW_MODE_SWITCH_DELAY_MS); + + if (retval < 0) { + LOGE("Fail to enter bootloader mode\n"); + return retval; + } + } + /* switch to rom boot mode */ + if (!IS_ROM_BOOTLOADER_MODE(tcm_dev->dev_mode)) { + LOGI("Prepare to enter rom boot mode\n"); + + retval = syna_tcm_switch_fw_mode(tcm_dev, + MODE_ROMBOOTLOADER, + FW_MODE_SWITCH_DELAY_MS); + if (retval < 0) { + LOGE("Fail to enter rom boot mode\n"); + return retval; + } + } + + if (!IS_ROM_BOOTLOADER_MODE(tcm_dev->dev_mode)) { + LOGE("Device not in romboot mode\n"); + return _EINVAL; + } + + if (!is_multichip) + return 0; + + boot_info = &tcm_dev->boot_info; + + retval = syna_tcm_romboot_multichip_get_boot_info(tcm_dev, + boot_info); + if (retval < 0) { + LOGE("Fail to get boot info\n"); + return retval; + } + + wr_chunk = tcm_dev->max_wr_size; + + temp = boot_info->write_block_size_words; + romboot_data->write_block_size = temp * 2; + + LOGI("Write block size: %d (words size: %d)\n", + romboot_data->write_block_size, temp); + + temp = syna_pal_le2_to_uint(boot_info->erase_page_size_words); + romboot_data->page_size = temp * 2; + + LOGI("Erase page size: %d (words size: %d)\n", + romboot_data->page_size, temp); + + temp = syna_pal_le2_to_uint(boot_info->max_write_payload_size); + romboot_data->max_write_payload_size = temp; + + LOGI("Max write flash data size: %d\n", + romboot_data->max_write_payload_size); + + if (romboot_data->write_block_size > (wr_chunk - 9)) { + LOGE("Write block size, %d, greater than chunk space, %d\n", + romboot_data->write_block_size, (wr_chunk - 9)); + return _EINVAL; + } + + if (romboot_data->write_block_size == 0) { + LOGE("Invalid write block size %d\n", + romboot_data->write_block_size); + return _EINVAL; + } + + if (romboot_data->page_size == 0) { + LOGE("Invalid erase page size %d\n", + romboot_data->page_size); + return _EINVAL; + } + + return 0; +} + +/** + * syna_tcm_romboot_jedec_send_command() + * + * Send a jedec flash commend to the ROM bootloader + * + * @param + * [ in] tcm_dev: the device handle + * [ in] flash_command: flash command to send + * [ in] out: additional data sent out, if any + * [ in] out_size: size of data sent out + * [ in] in: buffer to store the data read in + * [ in] in_size: size of data read in + * [ in] delay_ms: delay time to get the response + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_jedec_send_command(struct tcm_dev *tcm_dev, + unsigned char flash_command, unsigned char *out, + unsigned int out_size, unsigned char *in, unsigned int in_size, + unsigned int delay_ms) +{ + int retval; + unsigned char *payld_buf = NULL; + unsigned int payld_size; + struct flash_param flash_param; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + syna_pal_mem_set((void *)&flash_param, 0x00, sizeof(flash_param)); + + flash_param.spi_param = 1; + flash_param.clk_div = 0x19; + + flash_param.read_size[0] = (unsigned char)(in_size & 0xff); + flash_param.read_size[1] = (unsigned char)(in_size >> 8) & 0xff; + + flash_param.command = flash_command; + + payld_size = sizeof(struct flash_param) + out_size; + + LOGD("Flash command: 0x%02x, total size: %d, wr: %d, rd: %d\n", + flash_command, payld_size, out_size, in_size); + LOGD("Packet: %02x %02x %02x %02x %02x %02x\n", + flash_param.byte0, flash_param.byte1, flash_param.byte2, + flash_param.read_size[0], flash_param.read_size[1], + flash_param.command); + + payld_buf = syna_pal_mem_alloc(payld_size, sizeof(unsigned char)); + if (!payld_buf) { + LOGE("Fail to allocate buffer to store flash command\n"); + return _ENOMEM; + } + + retval = syna_pal_mem_cpy(payld_buf, payld_size, + &flash_param, sizeof(flash_param), sizeof(flash_param)); + if (retval < 0) { + LOGE("Fail to copy flash_param header to payld_buf\n"); + goto exit; + } + + if (out && (out_size > 0)) { + retval = syna_pal_mem_cpy(payld_buf + sizeof(flash_param), + payld_size - sizeof(flash_param), + out, out_size, out_size); + if (retval < 0) { + LOGE("Fail to copy data to payld_buf\n"); + goto exit; + } + } + + retval = syna_tcm_romboot_send_command(tcm_dev, + payld_buf, + payld_size, + in, + in_size, + delay_ms); + if (retval < 0) { + LOGE("Fail to write flash command 0x%x\n", flash_command); + goto exit; + } + +exit: + syna_pal_mem_free((void *)payld_buf); + + return retval; +} + +/** + * syna_tcm_romboot_jedec_get_status() + * + * Use jedec command to poll the flash status until the completion + * + * @param + * [ in] tcm_dev: the device handle + * [ in] delay_ms: delay time to get the response + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_jedec_get_status(struct tcm_dev *tcm_dev, + unsigned int delay_ms) +{ + int retval; + int idx; + unsigned char status; + int STATUS_CHECK_RETRY = 50; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + for (idx = 0; idx < STATUS_CHECK_RETRY; idx++) { + retval = syna_tcm_romboot_jedec_send_command(tcm_dev, + JEDEC_READ_STATUS, + NULL, + 0, + &status, + sizeof(status), + delay_ms); + if (retval < 0) { + LOGE("Failed to write JEDEC_READ_STATUS\n"); + return retval; + } + + syna_pal_sleep_us(JEDEC_STATUS_CHECK_US_MIN, + JEDEC_STATUS_CHECK_US_MAX); + /* once completed, status = 0 */ + if (!status) + break; + } + + if (status) + retval = _EIO; + else + retval = status; + + return retval; +} + +/** + * syna_tcm_romboot_jedec_erase_flash() + * + * Ask the ROM bootloader to erase the flash by using the jedec command + * + * @param + * [ in] tcm_dev: the device handle + * [ in] delay_ms: delay time to get the response + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_jedec_erase_flash(struct tcm_dev *tcm_dev, + unsigned int delay_ms) +{ + int retval; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + retval = syna_tcm_romboot_jedec_send_command(tcm_dev, + JEDEC_WRITE_ENABLE, + NULL, + 0, + NULL, + 0, + delay_ms); + if (retval < 0) { + LOGE("Failed to write JEDEC_WRITE_ENABLE\n"); + return retval; + } + + retval = syna_tcm_romboot_jedec_send_command(tcm_dev, + JEDEC_CHIP_ERASE, + NULL, + 0, + NULL, + 0, + delay_ms); + if (retval < 0) { + LOGE("Failed to write JEDEC_WRITE_ENABLE\n"); + return retval; + } + + retval = syna_tcm_romboot_jedec_get_status(tcm_dev, delay_ms); + if (retval < 0) + LOGE("Fail to get correct status, retval = %d\n", retval); + + return retval; +} + + /** + * syna_tcm_romboot_jedec_write_flash() + * + * Write the given binary data to the flash through the ROM bootloader + * by using the jedec command + * + * @param + * [ in] tcm_dev: the device handle + * [ in] address: the address in flash memory to write + * [ in] data: binary data to write + * [ in] data_size: size of binary data + * [ in] delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_jedec_write_flash(struct tcm_dev *tcm_dev, + unsigned int address, const unsigned char *data, + unsigned int data_size, unsigned int delay_ms) +{ + int retval = 0; + unsigned int offset; + unsigned char buf[ROMBOOT_FLASH_PAGE_SIZE + 3]; + unsigned int remaining_length; + unsigned int xfer_length; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if ((!data) || (data_size == 0)) { + LOGE("Invalid image data, no data available\n"); + return _EINVAL; + } + + remaining_length = data_size; + + offset = 0; + + while (remaining_length) { + if (remaining_length > ROMBOOT_FLASH_PAGE_SIZE) + xfer_length = ROMBOOT_FLASH_PAGE_SIZE; + else + xfer_length = remaining_length; + + syna_pal_mem_set(buf, 0x00, sizeof(buf)); + + retval = syna_tcm_romboot_jedec_send_command(tcm_dev, + JEDEC_WRITE_ENABLE, + NULL, + 0, + NULL, + 0, + delay_ms); + if (retval < 0) { + LOGE("Failed to write JEDEC_WRITE_ENABLE\n"); + goto exit; + } + + buf[0] = (unsigned char)((address + offset) >> 16); + buf[1] = (unsigned char)((address + offset) >> 8); + buf[2] = (unsigned char)(address + offset); + + retval = syna_pal_mem_cpy(&buf[3], + sizeof(buf) - 3, + &data[offset], + data_size - offset, + xfer_length); + if (retval < 0) { + LOGE("Fail to copy data to write, size: %d\n", + xfer_length); + goto exit; + } + + retval = syna_tcm_romboot_jedec_send_command(tcm_dev, + JEDEC_PAGE_PROGRAM, + buf, + sizeof(buf), + NULL, + 0, + delay_ms); + if (retval < 0) { + LOGE("Failed to write data to addr 0x%x (offset: %x)\n", + address + offset, offset); + LOGE("Remaining data %d\n", + remaining_length); + goto exit; + } + + retval = syna_tcm_romboot_jedec_get_status(tcm_dev, delay_ms); + if (retval < 0) { + LOGE("Fail to get correct status, retval = %d\n", + retval); + goto exit; + } + offset += xfer_length; + remaining_length -= xfer_length; + } + +exit: + return retval; +} + + +/** + * syna_tcm_romboot_erase_flash() + * + * The entry function to perform mass erase + * + * @param + * @param + * [ in] tcm_dev: the device handle + * [ in] romboot_data: data blob for remboot + * [ in] blk: the block in flash memory to erase + * [ in] delay_ms: delay time to get the response + * [ in] is_multichip: use multi-chip command packet instead + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_erase_flash(struct tcm_dev *tcm_dev, + struct tcm_romboot_data_blob *romboot_data, + struct block_data *blk, unsigned int delay_ms, + bool is_multichip) +{ + int retval; + + if (!tcm_dev || !romboot_data || !blk) + return _EINVAL; + + if (is_multichip) + retval = syna_tcm_romboot_multichip_erase_flash(tcm_dev, + romboot_data, blk->flash_addr, blk->size, + delay_ms); + else + retval = syna_tcm_romboot_jedec_erase_flash(tcm_dev, + delay_ms); + + return retval; +} + +/** + * syna_tcm_romboot_write_flash() + * + * The entry function to write hex data to flash + * + * @param + * [ in] tcm_dev: the device handle + * [ in] romboot_data: data blob for remboot + * [ in] blk: the block in flash memory to update + * [ in] delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * [ in] is_multichip: use multi-chip command packet instead + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_romboot_write_flash(struct tcm_dev *tcm_dev, + struct tcm_romboot_data_blob *romboot_data, + struct block_data *blk, unsigned int delay_ms, + bool is_multichip) +{ + int retval; + + if (!tcm_dev || !romboot_data || !blk) + return _EINVAL; + + if (is_multichip) + retval = syna_tcm_romboot_multichip_write_flash(tcm_dev, + romboot_data, blk->flash_addr, blk->data, + blk->size, delay_ms); + else + retval = syna_tcm_romboot_jedec_write_flash(tcm_dev, + blk->flash_addr, blk->data, blk->size, + delay_ms); + + return retval; +} + + +/** + * syna_tcm_romboot_do_ihex_update() + * + * The entry function to perform ihex update upon ROM Boot. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] ihex: ihex data to write + * [ in] ihex_size: size of ihex data + * [ in] flash_size: size for temporary buffer allocation, which used + * to re-order the flash data. + * in general, (ihex_size + 4K) is preferred size + * [ in] len_per_line: length per line in the ihex file + * [ in] delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * [ in] is_multichip: flag to indicate a multi-chip product used + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_romboot_do_ihex_update(struct tcm_dev *tcm_dev, + const unsigned char *ihex, unsigned int ihex_size, + unsigned int flash_size, unsigned int len_per_line, + unsigned int delay_ms, bool is_multichip) +{ + int retval; + struct tcm_romboot_data_blob romboot_data; + struct ihex_info *ihex_info = NULL; + struct block_data *block; + unsigned int erase_delay_ms = (delay_ms >> 16) & 0xFFFF; + unsigned int wr_delay_ms = delay_ms & 0xFFFF; + unsigned short *header; + int idx; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if ((!ihex) || (ihex_size == 0)) { + LOGE("Invalid ihex data\n"); + return _EINVAL; + } + + if (flash_size == 0) + flash_size = ihex_size + 4096; + + romboot_data.bdata = ihex; + romboot_data.bdata_size = ihex_size; + syna_pal_mem_set(&romboot_data.ihex_info, 0x00, + sizeof(struct ihex_info)); + + ihex_info = &romboot_data.ihex_info; + + ihex_info->bin = syna_pal_mem_alloc(flash_size, + sizeof(unsigned char)); + if (!ihex_info->bin) { + LOGE("Fail to allocate buffer for ihex data\n"); + + return _ENOMEM; + } + + ihex_info->bin_size = flash_size; + + syna_tcm_buf_init(&romboot_data.out); + + LOGN("Parse ihex file\n"); + + /* parse ihex file */ + retval = syna_tcm_parse_fw_ihex((const char *)ihex, + ihex_size, ihex_info, len_per_line); + if (retval < 0) { + LOGE("Fail to parse firmware ihex file\n"); + goto exit; + } + + if (!is_multichip) { + header = (unsigned short *)ihex_info->block[0].data; + if (*header != BINARY_FILE_MAGIC_VALUE) { + LOGE("Incorrect image header 0x%04X\n", *header); + goto exit; + } + } + + /* set up flash access, and enter the bootloader mode */ + retval = syna_tcm_romboot_preparation(tcm_dev, + &romboot_data, + is_multichip); + if (retval < 0) { + LOGE("Fail to do preparation\n"); + goto reset; + } + + LOGN("Start of ihex update\n"); + + ATOMIC_SET(tcm_dev->firmware_flashing, 1); + + for (idx = 0; idx < IHEX_MAX_BLOCKS; idx++) { + + block = &ihex_info->block[idx]; + + if (!block->available) + continue; + + if (block->size == 0) + continue; + + /* for single-chip, mass erase will affect the + * entire flash memory, so it just takes once + */ + if ((idx != 0) && (!is_multichip)) + break; + + retval = syna_tcm_romboot_erase_flash(tcm_dev, + &romboot_data, + block, + erase_delay_ms, + is_multichip); + if (retval < 0) { + LOGE("Fail to erase flash\n"); + goto reset; + } + } + + LOGN("Flash erased\n"); + LOGN("Start to write all data to flash\n"); + + for (idx = 0; idx < IHEX_MAX_BLOCKS; idx++) { + + block = &ihex_info->block[idx]; + + if (!block->available) + continue; + + LOGD("block:%d, addr:0x%x, size:%d\n", + idx, block->flash_addr, block->size); + + if (block->size == 0) + continue; + + retval = syna_tcm_romboot_write_flash(tcm_dev, + &romboot_data, + block, + wr_delay_ms, + is_multichip); + if (retval < 0) { + LOGE("Fail to write data to addr 0x%x, size:%d\n", + block->flash_addr, block->size); + goto reset; + } + + LOGI("Data written, size:%d\n", block->size); + } + + LOGN("End of ihex update\n"); + + retval = 0; + +reset: + retval = syna_tcm_reset(tcm_dev); + if (retval < 0) { + LOGE("Fail to do reset\n"); + goto exit; + } + +exit: + syna_pal_mem_free((void *)ihex_info->bin); + + ATOMIC_SET(tcm_dev->firmware_flashing, 0); + + syna_tcm_buf_release(&romboot_data.out); + + return retval; +} + +/** + * syna_tcm_romboot_do_multichip_reflash() + * + * The entry function to perform fw update in multi-chip product. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] image: image file given + * [ in] image_size: size of data array + * [ in] wait_delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * for polling, set a value formatted with + * [erase | write]; + * for ATTN-driven, set a '0' or 'RESP_IN_ATTN' + * [ in] force_reflash: '1' to do reflash anyway + * '0' to compare ID info before doing reflash. + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_romboot_do_multichip_reflash(struct tcm_dev *tcm_dev, + const unsigned char *image, unsigned int image_size, + unsigned int wait_delay_ms, bool force_reflash) +{ + int retval; + int idx; + struct tcm_romboot_data_blob romboot_data; + struct block_data *block; + struct app_config_header *header; + unsigned int image_fw_id; + unsigned int erase_delay_ms = (wait_delay_ms >> 16) & 0xFFFF; + unsigned int wr_delay_ms = wait_delay_ms & 0xFFFF; + bool has_tool_boot_cfg = false; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if ((!image) || (image_size == 0)) { + LOGE("Invalid image data\n"); + return _EINVAL; + } + + LOGN("Prepare to do reflash\n"); + + syna_pal_mem_set(&romboot_data, 0x00, + sizeof(struct tcm_romboot_data_blob)); + + syna_tcm_buf_init(&romboot_data.out); + + romboot_data.bdata = image; + romboot_data.bdata_size = image_size; + + syna_tcm_buf_init(&romboot_data.out); + + retval = syna_tcm_parse_fw_image(image, &romboot_data.image_info); + if (retval < 0) { + LOGE("Fail to parse firmware image\n"); + retval = _EINVAL; + goto exit; + } + + block = &romboot_data.image_info.data[AREA_APP_CONFIG]; + if (block->size < sizeof(struct app_config_header)) { + LOGE("Invalid application config in image file\n"); + retval = _EINVAL; + goto exit; + } + header = (struct app_config_header *)block->data; + + image_fw_id = syna_pal_le4_to_uint(header->build_id); + + LOGN("Device firmware ID: %d, image build id: %d\n", + tcm_dev->packrat_number, image_fw_id); + + if ((image_fw_id <= tcm_dev->packrat_number) && !force_reflash) { + LOGN("No need to do reflash\n"); + retval = 0; + goto exit; + } + + block = &romboot_data.image_info.data[AREA_TOOL_BOOT_CONFIG]; + has_tool_boot_cfg = block->available; + + /* set up flash access, and enter the bootloader mode */ + retval = syna_tcm_romboot_preparation(tcm_dev, &romboot_data, true); + if (retval < 0) { + LOGE("Fail to do preparation\n"); + goto reset; + } + + if (!IS_ROM_BOOTLOADER_MODE(tcm_dev->dev_mode)) { + LOGE("Incorrect device mode 0x%02x, expected:0x%02x\n", + tcm_dev->dev_mode, MODE_ROMBOOTLOADER); + retval = _EINVAL; + goto reset; + } + + LOGN("Start of reflash\n"); + + ATOMIC_SET(tcm_dev->firmware_flashing, 1); + + /* Traverse through all blocks in the image file, + * then erase the corresponding block area + */ + for (idx = 0; idx < AREA_MAX; idx++) { + + block = &romboot_data.image_info.data[idx]; + + if (!block->available) + continue; + + if (idx == AREA_ROMBOOT_APP_CODE) + continue; + + if ((idx == AREA_BOOT_CONFIG) && has_tool_boot_cfg) + continue; + + LOGD("Erase %s block - address: 0x%x (%d), size: %d\n", + AREA_ID_STR(block->id), block->flash_addr, + block->flash_addr, block->size); + + if (block->size == 0) + continue; + + if (erase_delay_ms == DEFAULT_FLASH_ERASE_DELAY) + erase_delay_ms = ROMBOOT_DELAY_MS; + + retval = syna_tcm_romboot_erase_flash(tcm_dev, + &romboot_data, + block, + erase_delay_ms, + true); + if (retval < 0) { + LOGE("Fail to erase %s area\n", AREA_ID_STR(block->id)); + goto reset; + } + + LOGN("%s partition erased\n", AREA_ID_STR(block->id)); + } + + /* Traverse through all blocks in the image file, + * and then update the corresponding block area + */ + for (idx = 0; idx < AREA_MAX; idx++) { + + LOGD("Prepare to update %s partition\n", AREA_ID_STR(idx)); + + block = &romboot_data.image_info.data[idx]; + + if (!block->available) + continue; + + if (idx == AREA_ROMBOOT_APP_CODE) + continue; + + if ((idx == AREA_BOOT_CONFIG) && has_tool_boot_cfg) + continue; + + LOGD("Write data to %s - address: 0x%x (%d), size: %d\n", + AREA_ID_STR(block->id), block->flash_addr, + block->flash_addr, block->size); + + if (block->size == 0) + continue; + + if (wr_delay_ms == DEFAULT_FLASH_WRITE_DELAY) + wr_delay_ms = ROMBOOT_DELAY_MS; + + retval = syna_tcm_romboot_write_flash(tcm_dev, + &romboot_data, + block, + wr_delay_ms, + true); + + if (retval < 0) { + LOGE("Fail to update %s partition, size: %d\n", + AREA_ID_STR(block->id), block->size); + goto reset; + } + + LOGN("%s written\n", AREA_ID_STR(block->id)); + } + + LOGN("End of reflash\n"); + + retval = 0; +reset: + retval = syna_tcm_reset(tcm_dev); + if (retval < 0) { + LOGE("Fail to do reset\n"); + goto exit; + } + +exit: + ATOMIC_SET(tcm_dev->firmware_flashing, 0); + + syna_tcm_buf_release(&romboot_data.out); + + return retval; +} + + +/** + * syna_tcm_get_romboot_info() + * + * Implement the bootloader command code, which is used to request a + * RomBoot information packet. + * + * @param + * [ in] tcm_dev: the device handle + * [out] rom_boot_info: the romboot info packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_romboot_info(struct tcm_dev *tcm_dev, + struct tcm_romboot_info *rom_boot_info) +{ + int retval = 0; + unsigned char resp_code; + unsigned int copy_size = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_GET_ROMBOOT_INFO, + NULL, + 0, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to send command 0x%02x\n", + CMD_GET_ROMBOOT_INFO); + goto exit; + } + + if (rom_boot_info == NULL) + goto exit; + + copy_size = MIN(sizeof(struct tcm_romboot_info), + tcm_dev->resp_buf.data_length); + + /* copy romboot_info to caller */ + retval = syna_pal_mem_cpy((unsigned char *)rom_boot_info, + sizeof(struct tcm_romboot_info), + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + copy_size); + if (retval < 0) { + LOGE("Fail to copy romboot info to caller\n"); + goto exit; + } + +exit: + return retval; +} + + + diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_romboot.h b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_romboot.h new file mode 100644 index 000000000000..812caf9d441f --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_romboot.h @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file synaptics_touchcom_func_romboot.h + * + * This file declares relevant functions and structures for ROM boot-loader. + */ + +#ifndef _SYNAPTICS_TOUCHCOM_ROMBOOT_FUNCS_H_ +#define _SYNAPTICS_TOUCHCOM_ROMBOOT_FUNCS_H_ + +#include "synaptics_touchcom_core_dev.h" +#include "synaptics_touchcom_func_base_flash.h" + + +#define ROMBOOT_DELAY_MS (20) + +/** + * @section: Structure to assemble flash command + */ +struct flash_param { + union { + struct { + unsigned char byte0; + unsigned char byte1; + unsigned char byte2; + }; + struct { + unsigned char spi_param; + unsigned char clk_div; + unsigned char mode; + }; + }; + unsigned char read_size[2]; + unsigned char command; +}; + +/** + * @section: Specific data blob for romboot + * + * The structure contains various parameters being used in + * ROM boot control + */ +struct tcm_romboot_data_blob { + /* binary data to write */ + const unsigned char *bdata; + unsigned int bdata_size; + /* parsed data based on given binary data */ + struct ihex_info ihex_info; + struct image_info image_info; + /* standard information for flash access */ + unsigned int page_size; + unsigned int write_block_size; + unsigned int max_write_payload_size; + /* temporary buffer during the reflash */ + struct tcm_buffer out; +}; + + +/** + * syna_tcm_romboot_do_ihex_update() + * + * The entry function to perform ihex update upon ROM Boot. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] ihex: ihex data to write + * [ in] ihex_size: size of ihex data + * [ in] flash_size: size for temporary buffer allocation, which used + * to re-order the flash data. + * in general, (ihex_size + 4K) is preferred size + * [ in] len_per_line: length per line in the ihex file + * [ in] delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * [ in] is_multichip: flag to indicate a multi-chip product used + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_romboot_do_ihex_update(struct tcm_dev *tcm_dev, + const unsigned char *ihex, unsigned int ihex_size, + unsigned int flash_size, unsigned int len_per_line, + unsigned int delay_ms, bool is_multichip); + +/** + * syna_tcm_romboot_do_multichip_reflash() + * + * The entry function to perform fw update with multi-chip product. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] image: image file given + * [ in] image_size: size of data array + * [ in] wait_delay_ms: a short delay time in millisecond to wait for + * the completion of flash access + * set [erase_delay_ms | write_delay_ms] for setup; + * set '0' to use default time; + * set 'FORCE_ATTN_DRIVEN' to adopt ATTN-driven. + * [ in] force_reflash: '1' to do reflash anyway + * '0' to compare ID info before doing reflash. + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_romboot_do_multichip_reflash(struct tcm_dev *tcm_dev, + const unsigned char *image, unsigned int image_size, + unsigned int wait_delay_ms, bool force_reflash); + +/** + * syna_tcm_get_romboot_info() + * + * Implement the bootloader command code, which is used to request a + * RomBoot information packet. + * + * @param + * [ in] tcm_dev: the device handle + * [out] rom_boot_info: the romboot info packet returned + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_romboot_info(struct tcm_dev *tcm_dev, + struct tcm_romboot_info *rom_boot_info); + + +#endif /* end of _SYNAPTICS_TOUCHCOM_ROMBOOT_FUNCS_H_ */ diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_touch.c b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_touch.c new file mode 100644 index 000000000000..061d5fd3ed45 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_touch.c @@ -0,0 +1,877 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file synaptics_tcm2_func_touch.c + * + * This file implements the touch report handling functions. + * The declarations are available in synaptics_touchcom_func_touch.h. + */ + +#include "synaptics_touchcom_func_touch.h" + +/** + * syna_tcm_get_touch_data() + * + * Get data entity from the received report according to bit offset and bit + * length defined in the touch report configuration. + * + * @param + * [ in] report: touch report generated by TouchComm device + * [ in] report_size: size of given report + * [ in] offset: bit offset in the report + * [ in] bits: number of bits representing the data + * [out] data: data parsed + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_touch_data(const unsigned char *report, + unsigned int report_size, unsigned int offset, + unsigned int bits, unsigned int *data) +{ + unsigned char mask; + unsigned char byte_data; + unsigned int output_data; + unsigned int bit_offset; + unsigned int byte_offset; + unsigned int data_bits; + unsigned int available_bits; + unsigned int remaining_bits; + + if (bits == 0 || bits > 32) { + LOGE("Invalid number of bits %d\n", bits); + return _EINVAL; + } + + if (!report) { + LOGE("Invalid report data\n"); + return _EINVAL; + } + + if (offset + bits > report_size * 8) { + *data = 0; + return 0; + } + + output_data = 0; + remaining_bits = bits; + + bit_offset = offset % 8; + byte_offset = offset / 8; + + while (remaining_bits) { + byte_data = report[byte_offset]; + byte_data >>= bit_offset; + + available_bits = 8 - bit_offset; + data_bits = MIN(available_bits, remaining_bits); + mask = 0xff >> (8 - data_bits); + + byte_data &= mask; + + output_data |= byte_data << (bits - remaining_bits); + + bit_offset = 0; + byte_offset += 1; + remaining_bits -= data_bits; + } + + *data = output_data; + + return 0; +} + +/** + * syna_tcm_get_gesture_data() + * + * The contents of the gesture data entity depend on which gesture + * is detected. The default size of data is defined in 16-64 bits natively. + * + * @param + * [ in] report: touch report generated by TouchComm device + * [ in] report_size: size of given report + * [ in] offset: bit offset in the report + * [ in] bits: total bits representing the gesture data + * [out] gesture_data: gesture data parsed + * [ in] gesture_id: gesture id retrieved + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +static int syna_tcm_get_gesture_data(const unsigned char *report, + unsigned int report_size, unsigned int offset, + unsigned int bits, struct tcm_gesture_data_blob *gesture_data, + unsigned int gesture_id) +{ + int retval; + unsigned int idx; + unsigned int data; + unsigned int size; + unsigned int data_end; + + if (!report) { + LOGE("Invalid report data\n"); + return _EINVAL; + } + + if (offset + bits > report_size * 8) + return 0; + + data_end = offset + bits; + + size = (sizeof(gesture_data->data) / sizeof(unsigned char)); + + idx = 0; + while ((offset < data_end) && (idx < size)) { + retval = syna_tcm_get_touch_data(report, report_size, + offset, 16, &data); + if (retval < 0) { + LOGE("Fail to get object index\n"); + return retval; + } + gesture_data->data[idx++] = (unsigned char)(data & 0xff); + gesture_data->data[idx++] = (unsigned char)((data >> 8) & 0xff); + offset += 16; + } + + switch (gesture_id) { + case GESTURE_ID_DOUBLE_TAP: + case GESTURE_ID_ACTIVE_TAP_AND_HOLD: + case GESTURE_ID_ACTIVE_SINGLE_TAP: + LOGD("Tap info: (%d, %d)\n", + syna_pal_le2_to_uint(gesture_data->tap_x), + syna_pal_le2_to_uint(gesture_data->tap_y)); + break; + case GESTURE_ID_SWIPE: + LOGD("Swipe info: direction:%x (%d, %d)\n", + syna_pal_le2_to_uint(gesture_data->swipe_direction), + syna_pal_le2_to_uint(gesture_data->tap_x), + syna_pal_le2_to_uint(gesture_data->tap_y)); + break; + default: + LOGW("Unknown gesture_id:%d\n", gesture_id); + break; + } + + return 0; +} + +/** + * syna_tcm_parse_touch_report() + * + * Traverse through touch report configuration and parse the contents of + * report packet to get the exactly touched data entity from touch reports. + * + * At the end of function, the touched data will be parsed and stored at the + * associated position in structure touch_data_blob. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] report: touch report generated by TouchComm device + * [ in] report_size: size of given report + * [out] touch_data: touch data generated + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_parse_touch_report(struct tcm_dev *tcm_dev, + unsigned char *report, unsigned int report_size, + struct tcm_touch_data_blob *touch_data) +{ + int retval; + bool active_only; + bool num_of_active_objects; + unsigned char code; + unsigned int size; + unsigned int idx; + unsigned int obj; + unsigned int next; + unsigned int data; + unsigned int bits; + unsigned int offset; + unsigned int objects; + unsigned int active_objects; + unsigned int config_size; + unsigned char *config_data; + struct tcm_objects_data_blob *object_data; + static unsigned int end_of_foreach; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (!report) { + LOGE("Invalid report data\n"); + return _EINVAL; + } + + if (report_size <= 0) { + LOGE("Invalid report data length\n"); + return _EINVAL; + } + + if (!touch_data) { + LOGE("Invalid touch data structure\n"); + return _EINVAL; + } + + if (tcm_dev->max_objects == 0) { + LOGE("Invalid max_objects supported\n"); + return _EINVAL; + } + + object_data = touch_data->object_data; + + if (!object_data) { + LOGE("Invalid object_data\n"); + return _EINVAL; + } + + config_data = tcm_dev->touch_config.buf; + config_size = tcm_dev->touch_config.data_length; + + if ((!config_data) || (config_size == 0)) { + LOGE("Invalid config_data\n"); + return _EINVAL; + } + + size = sizeof(touch_data->object_data); + syna_pal_mem_set(touch_data->object_data, 0x00, size); + + num_of_active_objects = false; + + idx = 0; + offset = 0; + objects = 0; + active_objects = 0; + active_only = false; + obj = 0; + next = 0; + + while (idx < config_size) { + code = config_data[idx++]; + switch (code) { + case TOUCH_REPORT_END: + goto exit; + case TOUCH_REPORT_FOREACH_ACTIVE_OBJECT: + obj = 0; + next = idx; + active_only = true; + break; + case TOUCH_REPORT_FOREACH_OBJECT: + obj = 0; + next = idx; + active_only = false; + break; + case TOUCH_REPORT_FOREACH_END: + end_of_foreach = idx; + if (active_only) { + if (num_of_active_objects) { + objects++; + obj++; + if (objects < active_objects) + idx = next; + } else if (offset < report_size * 8) { + obj++; + idx = next; + } + } else { + obj++; + if (obj < tcm_dev->max_objects) + idx = next; + } + break; + case TOUCH_REPORT_PAD_TO_NEXT_BYTE: + offset = syna_pal_ceil_div(offset, 8) * 8; + break; + case TOUCH_REPORT_TIMESTAMP: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get time-stamp\n"); + return retval; + } + touch_data->timestamp = data; + offset += bits; + break; + case TOUCH_REPORT_OBJECT_N_INDEX: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get object index\n"); + return retval; + } + obj = data; + offset += bits; + break; + case TOUCH_REPORT_OBJECT_N_CLASSIFICATION: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get object classification\n"); + return retval; + } + object_data[obj].status = (unsigned char)data; + offset += bits; + break; + case TOUCH_REPORT_OBJECT_N_X_POSITION: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get object x position\n"); + return retval; + } + object_data[obj].x_pos = data; + offset += bits; + break; + case TOUCH_REPORT_OBJECT_N_Y_POSITION: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get object y position\n"); + return retval; + } + object_data[obj].y_pos = data; + offset += bits; + break; + case TOUCH_REPORT_OBJECT_N_Z: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get object z\n"); + return retval; + } + object_data[obj].z = data; + offset += bits; + break; + case TOUCH_REPORT_OBJECT_N_X_WIDTH: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get object x width\n"); + return retval; + } + object_data[obj].x_width = data; + offset += bits; + break; + case TOUCH_REPORT_OBJECT_N_Y_WIDTH: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get object y width\n"); + return retval; + } + object_data[obj].y_width = data; + offset += bits; + break; + case TOUCH_REPORT_OBJECT_N_TX_POSITION_TIXELS: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get object tx position\n"); + return retval; + } + object_data[obj].tx_pos = data; + offset += bits; + break; + case TOUCH_REPORT_OBJECT_N_RX_POSITION_TIXELS: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get object rx position\n"); + return retval; + } + object_data[obj].rx_pos = data; + offset += bits; + break; + case TOUCH_REPORT_NUM_OF_ACTIVE_OBJECTS: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get number of active objects\n"); + return retval; + } + active_objects = data; + num_of_active_objects = true; + touch_data->num_of_active_objects = data; + offset += bits; + if (touch_data->num_of_active_objects == 0) { + if (end_of_foreach == 0) + return 0; + + idx = end_of_foreach; + } + break; + case TOUCH_REPORT_0D_BUTTONS_STATE: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get 0D buttons state\n"); + return retval; + } + touch_data->buttons_state = data; + offset += bits; + break; + case TOUCH_REPORT_GESTURE_ID: + if (tcm_dev->cb_custom_gesture) { + retval = tcm_dev->cb_custom_gesture( + TOUCH_REPORT_GESTURE_ID, config_data, + &idx, report, &offset, report_size, + tcm_dev->cbdata_gesture); + } else { + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, + report_size, offset, bits, &data); + touch_data->gesture_id = data; + offset += bits; + } + if (retval < 0) { + LOGE("Fail to get gesture id\n"); + return retval; + } + break; + case TOUCH_REPORT_GESTURE_DATA: + if (tcm_dev->cb_custom_gesture) { + retval = tcm_dev->cb_custom_gesture( + TOUCH_REPORT_GESTURE_DATA, config_data, + &idx, report, &offset, report_size, + tcm_dev->cbdata_gesture); + } else { + bits = config_data[idx++]; + retval = syna_tcm_get_gesture_data(report, + report_size, + offset, bits, + &touch_data->gesture_data, + touch_data->gesture_id); + offset += bits; + } + if (retval < 0) { + LOGE("Fail to get gesture data\n"); + return retval; + } + break; + case TOUCH_REPORT_FRAME_RATE: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get frame rate\n"); + return retval; + } + touch_data->frame_rate = data; + offset += bits; + break; + case TOUCH_REPORT_FORCE_MEASUREMENT: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get force measurement data\n"); + return retval; + } + touch_data->force_data = data; + offset += bits; + break; + case TOUCH_REPORT_FINGERPRINT_AREA_MEET: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get data for fingerprint area\n"); + return retval; + } + touch_data->fingerprint_area_meet = data; + offset += bits; + break; + case TOUCH_REPORT_POWER_IM: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get power IM\n"); + return retval; + } + touch_data->power_im = data; + offset += bits; + break; + case TOUCH_REPORT_CID_IM: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get CID IM\n"); + return retval; + } + touch_data->cid_im = data; + offset += bits; + break; + case TOUCH_REPORT_RAIL_IM: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get rail IM\n"); + return retval; + } + touch_data->rail_im = data; + offset += bits; + break; + case TOUCH_REPORT_CID_VARIANCE_IM: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get CID variance IM\n"); + return retval; + } + touch_data->cid_variance_im = data; + offset += bits; + break; + case TOUCH_REPORT_NSM_FREQUENCY_INDEX: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get NSM frequency\n"); + return retval; + } + touch_data->nsm_frequency = data; + offset += bits; + break; + case TOUCH_REPORT_NSM_STATE: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get NSM state\n"); + return retval; + } + touch_data->nsm_state = data; + offset += bits; + break; + case TOUCH_REPORT_CPU_CYCLES_USED_SINCE_LAST_FRAME: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get cpu cycles info\n"); + return retval; + } + touch_data->num_of_cpu_cycles = data; + offset += bits; + break; + case TOUCH_REPORT_FACE_DETECT: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to detect face\n"); + return retval; + } + touch_data->fd_data = data; + offset += bits; + break; + case TOUCH_REPORT_SENSING_MODE: + bits = config_data[idx++]; + retval = syna_tcm_get_touch_data(report, report_size, + offset, bits, &data); + if (retval < 0) { + LOGE("Fail to get sensing mode\n"); + return retval; + } + touch_data->sensing_mode = data; + offset += bits; + break; + default: + /* use custom parsing method, if registered */ + if (tcm_dev->cb_custom_touch_entity) { + retval = tcm_dev->cb_custom_touch_entity( + code, config_data, &idx, report, + &offset, report_size, + tcm_dev->cbdata_touch_entity); + if (retval >= 0) + continue; + } + + LOGW("Unknown touch config code:0x%02x (length:%d)\n", + code, config_data[idx]); + bits = config_data[idx++]; + offset += bits; + break; + } + } + +exit: + return 0; +} + +/** + * syna_tcm_set_touch_report_config() + * + * Setup the format and content of touch report if needed. + * + * TouchComm allows to set how touch reports are formatted and what items get + * reported each time a touch report is generated. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] config: the customized report configuration + * [ in] config_size: size of given config + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_touch_report_config(struct tcm_dev *tcm_dev, + unsigned char *config, unsigned int config_size) +{ + int retval = 0; + unsigned char resp_code; + unsigned int size; + struct tcm_application_info *app_info; + unsigned char *data; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if ((!config) || (config_size == 0)) { + LOGE("Invalid given config data\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Not in application fw mode, mode: %d\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + app_info = &tcm_dev->app_info; + size = syna_pal_le2_to_uint(app_info->max_touch_report_config_size); + + if (config_size > size) { + LOGE("Invalid config size: %d (max: %d)\n", config_size, size); + return _EINVAL; + } + + data = syna_pal_mem_alloc(size, sizeof(unsigned char)); + if (!data) { + LOGE("Fail to allocate memory for touch config setting\n"); + return _ENOMEM; + } + + retval = syna_pal_mem_cpy(data, + size, + config, + config_size, + config_size); + if (retval < 0) { + LOGE("Fail to copy custom touch config\n"); + goto exit; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_SET_TOUCH_REPORT_CONFIG, + data, + size, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to write command CMD_SET_TOUCH_REPORT_CONFIG\n"); + goto exit; + } + + LOGI("Set touch config done\n"); + +exit: + if (data) + syna_pal_mem_free((void *)data); + + return retval; +} + +/** + * syna_tcm_preserve_touch_report_config() + * + * Retrieve and preserve the current touch report configuration. + * + * The retrieved configuration is stored in touch_config buffer defined + * in structure syna_tcm_dev for later using of touch position parsing. + * + * The touch_config buffer will be allocated internally and its size will + * be updated accordingly. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_preserve_touch_report_config(struct tcm_dev *tcm_dev) +{ + int retval = 0; + unsigned char resp_code; + unsigned int size = 0; + + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + if (IS_NOT_APP_FW_MODE(tcm_dev->dev_mode)) { + LOGE("Not in application fw mode, mode: %d\n", + tcm_dev->dev_mode); + return _EINVAL; + } + + retval = tcm_dev->write_message(tcm_dev, + CMD_GET_TOUCH_REPORT_CONFIG, + NULL, + 0, + &resp_code, + tcm_dev->msg_data.default_resp_reading); + if (retval < 0) { + LOGE("Fail to write command CMD_GET_TOUCH_REPORT_CONFIG\n"); + goto exit; + } + + syna_tcm_buf_lock(&tcm_dev->resp_buf); + + size = tcm_dev->resp_buf.data_length; + retval = syna_tcm_buf_alloc(&tcm_dev->touch_config, + size); + if (retval < 0) { + LOGE("Fail to allocate memory for internal touch_config\n"); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + goto exit; + } + + syna_tcm_buf_lock(&tcm_dev->touch_config); + + retval = syna_pal_mem_cpy(tcm_dev->touch_config.buf, + tcm_dev->touch_config.buf_size, + tcm_dev->resp_buf.buf, + tcm_dev->resp_buf.buf_size, + size); + if (retval < 0) { + LOGE("Fail to clone touch config\n"); + syna_tcm_buf_unlock(&tcm_dev->touch_config); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + goto exit; + } + + tcm_dev->touch_config.data_length = size; + + syna_tcm_buf_unlock(&tcm_dev->touch_config); + syna_tcm_buf_unlock(&tcm_dev->resp_buf); + +exit: + return retval; +} + +/** + * syna_tcm_set_custom_touch_entity_callback() + * + * Set up callback function to handle custom touch data. + * + * Once finding the "new" custom entity in touch report, the core library will + * invoke the custom parsing method to handle this "new" code entity. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] p_cb: the callback function pointer + * [ in] p_cbdata: pointer to caller data + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_custom_touch_entity_callback(struct tcm_dev *tcm_dev, + tcm_custom_touch_entity_callback_t p_cb, void *p_cbdata) +{ + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_dev->cb_custom_touch_entity = p_cb; + tcm_dev->cbdata_touch_entity = p_cbdata; + + LOGI("enabled\n"); + + return 0; +} + +/** + * syna_tcm_set_custom_gesture_callback() + * + * Set up callback function to handle the gesture data defined as the + * following code entities + * - TOUCH_REPORT_GESTURE_ID + * - TOUCH_REPORT_GESTURE_DATA + * + * @param + * [ in] tcm_dev: the device handle + * [ in] p_cb: the callback function pointer + * [ in] p_cbdata: pointer to caller data passed to callback function + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_custom_gesture_callback(struct tcm_dev *tcm_dev, + tcm_custom_gesture_callback_t p_cb, void *p_cbdata) +{ + if (!tcm_dev) { + LOGE("Invalid tcm device handle\n"); + return _EINVAL; + } + + tcm_dev->cb_custom_gesture = p_cb; + tcm_dev->cbdata_gesture = p_cbdata; + + LOGI("enabled\n"); + + return 0; +} + diff --git a/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_touch.h b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_touch.h new file mode 100644 index 000000000000..99133cc54998 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm2_mmi/tcm/synaptics_touchcom_func_touch.h @@ -0,0 +1,236 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * Synaptics TouchCom touchscreen driver + * + * Copyright (C) 2017-2020 Synaptics Incorporated. 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 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. + */ + +/** + * @file synaptics_tcm2_func_touch.h + * + * This file declares related APIs and definitions for touch report handling. + */ + +#ifndef _SYNAPTICS_TOUCHCOM_TOUCH_FUNCS_H_ +#define _SYNAPTICS_TOUCHCOM_TOUCH_FUNCS_H_ + +#include "synaptics_touchcom_core_dev.h" + +/** + * @section: Types of Object Reported + * + * List the object classifications + */ +enum object_classification { + LIFT = 0, + FINGER = 1, + GLOVED_OBJECT = 2, + STYLUS = 3, + ERASER = 4, + SMALL_OBJECT = 5, + PALM = 6, + EDGE_TOUCHED = 8, + HOVER_OBJECT = 9, + NOP = -1, +}; + +/** + * @section: Types of Gesture ID + * + * List the gesture ID assigned + */ +enum gesture_classification { + GESTURE_ID_NONE = 0, + GESTURE_ID_DOUBLE_TAP = 1, + GESTURE_ID_SWIPE = 2, + GESTURE_ID_ACTIVE_SINGLE_TAP = 0x10, + GESTURE_ID_ACTIVE_TAP_AND_HOLD = 7, +}; + +/** + * @section: Codes for Touch Report Configuration + * + * Define the 8-bit codes for the touch report configuration + */ +enum touch_report_code { + /* control flow codes */ + TOUCH_REPORT_END = 0x00, + TOUCH_REPORT_FOREACH_ACTIVE_OBJECT = 0x01, + TOUCH_REPORT_FOREACH_OBJECT = 0x02, + TOUCH_REPORT_FOREACH_END = 0x03, + TOUCH_REPORT_PAD_TO_NEXT_BYTE = 0x04, + /* entity codes */ + TOUCH_REPORT_TIMESTAMP = 0x05, + TOUCH_REPORT_OBJECT_N_INDEX = 0x06, + TOUCH_REPORT_OBJECT_N_CLASSIFICATION = 0x07, + TOUCH_REPORT_OBJECT_N_X_POSITION = 0x08, + TOUCH_REPORT_OBJECT_N_Y_POSITION = 0x09, + TOUCH_REPORT_OBJECT_N_Z = 0x0a, + TOUCH_REPORT_OBJECT_N_X_WIDTH = 0x0b, + TOUCH_REPORT_OBJECT_N_Y_WIDTH = 0x0c, + TOUCH_REPORT_OBJECT_N_TX_POSITION_TIXELS = 0x0d, + TOUCH_REPORT_OBJECT_N_RX_POSITION_TIXELS = 0x0e, + TOUCH_REPORT_0D_BUTTONS_STATE = 0x0f, + TOUCH_REPORT_GESTURE_ID = 0x10, + TOUCH_REPORT_FRAME_RATE = 0x11, + TOUCH_REPORT_POWER_IM = 0x12, + TOUCH_REPORT_CID_IM = 0x13, + TOUCH_REPORT_RAIL_IM = 0x14, + TOUCH_REPORT_CID_VARIANCE_IM = 0x15, + TOUCH_REPORT_NSM_FREQUENCY_INDEX = 0x16, + TOUCH_REPORT_NSM_STATE = 0x17, + TOUCH_REPORT_NUM_OF_ACTIVE_OBJECTS = 0x18, + TOUCH_REPORT_CPU_CYCLES_USED_SINCE_LAST_FRAME = 0x19, + TOUCH_REPORT_FACE_DETECT = 0x1a, + TOUCH_REPORT_GESTURE_DATA = 0x1b, + TOUCH_REPORT_FORCE_MEASUREMENT = 0x1c, + TOUCH_REPORT_FINGERPRINT_AREA_MEET = 0x1d, + TOUCH_REPORT_SENSING_MODE = 0x1e, + TOUCH_REPORT_KNOB_DATA = 0x24, +}; + +/** + * syna_tcm_parse_touch_report() + * + * Traverse through touch report configuration and parse the contents of + * report packet to get the exactly touched data entity from touch reports. + * + * At the end of function, the touched data will be parsed and stored at the + * associated position in struct touch_data_blob. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] report: touch report generated by TouchComm device + * [ in] report_size: size of given report + * [out] touch_data: touch data generated + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_parse_touch_report(struct tcm_dev *tcm_dev, + unsigned char *report, unsigned int report_size, + struct tcm_touch_data_blob *touch_data); + +/** + * syna_tcm_set_touch_report_config() + * + * Setup the format and content of touch report if needed + * + * TouchComm allows to set how touch reports are formatted and what items get + * reported each time a touch report is generated. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] config: the customized report configuration + * [ in] config_size: size of given config + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_touch_report_config(struct tcm_dev *tcm_dev, + unsigned char *config, unsigned int config_size); + +/** + * syna_tcm_preserve_touch_report_config() + * + * Retrieve and preserve the current touch report configuration. + * + * The retrieved configuration is stored in touch_config buffer defined + * in struct syna_tcm_dev for later using of touch position parsing. + * + * The touch_config buffer will be allocated internally and its size will + * be updated accordingly. + * + * @param + * [ in] tcm_dev: the device handle + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_preserve_touch_report_config(struct tcm_dev *tcm_dev); + + +/** + * syna_tcm_get_touch_data() + * + * Get data entity from the received report according to bit offset and bit + * length defined in the touch report configuration. + * + * @param + * [ in] report: touch report generated by TouchComm device + * [ in] report_size: size of given report + * [ in] offset: bit offset in the report + * [ in] bits: number of bits representing the data + * [out] data: data parsed + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_get_touch_data(const unsigned char *report, + unsigned int report_size, unsigned int offset, + unsigned int bits, unsigned int *data); + +/** + * syna_tcm_set_custom_touch_entity_callback() + * + * Set up callback function to handle custom touch data. + * + * Once finding the "new" custom entity in touch report, the core library will + * invoke the custom parsing method to handle this "new" code entity. + * + * @param + * [ in] tcm_dev: the device handle + * [ in] p_cb: the callback function pointer + * [ in] p_cbdata: pointer to caller data passed to callback function + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_custom_touch_entity_callback(struct tcm_dev *tcm_dev, + tcm_custom_touch_entity_callback_t p_cb, void *p_cbdata); + +/** + * syna_tcm_set_custom_gesture_callback() + * + * Set up callback function to handle the gesture data defined as the following + * code entities + * - TOUCH_REPORT_GESTURE_ID + * - TOUCH_REPORT_GESTURE_DATA + * + * @param + * [ in] tcm_dev: the device handle + * [ in] p_cb: the callback function pointer + * [ in] p_cbdata: pointer to caller data passed to callback function + * + * @return + * on success, 0 or positive value; otherwise, negative value on error. + */ +int syna_tcm_set_custom_gesture_callback(struct tcm_dev *tcm_dev, + tcm_custom_gesture_callback_t p_cb, void *p_cbdata); + + +#endif /* end of _SYNAPTICS_TOUCHCOM_TOUCH_FUNCS_H_ */ diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi/Android.mk b/drivers/input/touchscreen/synaptics_tcm_mmi/Android.mk new file mode 100644 index 000000000000..8e86a32eb6e5 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi/Android.mk @@ -0,0 +1,71 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +SYNA_TCM_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +SYNA_TCM_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_tcm_spi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SYNA_TCM_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_tcm_i2c.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SYNA_TCM_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_tcm_core.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SYNA_TCM_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_tcm_device.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SYNA_TCM_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_tcm_diagnostics.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SYNA_TCM_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_tcm_recovery.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SYNA_TCM_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_tcm_reflash.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SYNA_TCM_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_tcm_testing.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SYNA_TCM_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_tcm_touch.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SYNA_TCM_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_tcm_zeroflash.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SYNA_TCM_MODULE_PATH) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi/Kbuild b/drivers/input/touchscreen/synaptics_tcm_mmi/Kbuild new file mode 100644 index 000000000000..8be46f23597a --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi/Kbuild @@ -0,0 +1,15 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/input/touchscreen/synaptics_tcm_mmi + +obj-m += synaptics_tcm_spi.o +obj-m += synaptics_tcm_i2c.o +obj-m += synaptics_tcm_core.o +obj-m += synaptics_tcm_device.o +obj-m += synaptics_tcm_diagnostics.o +obj-m += synaptics_tcm_recovery.o +obj-m += synaptics_tcm_reflash.o +obj-m += synaptics_tcm_testing.o +obj-m += synaptics_tcm_touch.o +obj-m += synaptics_tcm_zeroflash.o diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi/Makefile b/drivers/input/touchscreen/synaptics_tcm_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_core.c b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_core.c new file mode 100644 index 000000000000..eb2c00d57a49 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_core.c @@ -0,0 +1,3732 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * + * 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. + */ + +#include +#include +#include +#include +#include "synaptics_tcm_core.h" + +//#define RESET_ON_RESUME + +#define RESET_ON_RESUME_DELAY_MS 20 + +#define RESET_IF_RESUME_FAILED + +#define PM_SKIP_SLEEP + +#define PREDICTIVE_READING + +#define MIN_READ_LENGTH 9 + +#define FORCE_RUN_APPLICATION_FIRMWARE + +#define NOTIFIER_PRIORITY 2 + +#define NOTIFIER_TIMEOUT_MS 500 + +#define RESPONSE_TIMEOUT_MS 3000 + +#define APP_STATUS_POLL_TIMEOUT_MS 1000 + +#define APP_STATUS_POLL_MS 100 + +#define ENABLE_IRQ_DELAY_MS 20 + +#define FALL_BACK_ON_POLLING + +#define POLLING_DELAY_MS 5 + +#define RUN_WATCHDOG true + +#define WATCHDOG_TRIGGER_COUNT 2 + +#define WATCHDOG_DELAY_MS 1000 + +#define MODE_SWITCH_DELAY_MS 100 + +#define READ_RETRY_US_MIN 5000 + +#define READ_RETRY_US_MAX 10000 + +#define WRITE_DELAY_US_MIN 500 + +#define WRITE_DELAY_US_MAX 1000 + +#define HOST_DOWNLOAD_WAIT_MS 100 + +#define HOST_DOWNLOAD_TIMEOUT_MS 1000 + +#define DYNAMIC_CONFIG_SYSFS_DIR_NAME "dynamic_config" + +#define dynamic_config_sysfs(c_name, id) \ +static ssize_t syna_tcm_sysfs_##c_name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + int retval; \ + unsigned short value; \ + struct device *p_dev; \ + struct kobject *p_kobj; \ + struct syna_tcm_hcd *tcm_hcd; \ +\ + p_kobj = sysfs_dir->parent; \ + p_dev = container_of(p_kobj, struct device, kobj); \ + tcm_hcd = dev_get_drvdata(p_dev); \ +\ + mutex_lock(&tcm_hcd->extif_mutex); \ +\ + retval = tcm_hcd->get_dynamic_config(tcm_hcd, id, &value); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to get dynamic config\n"); \ + goto exit; \ + } \ +\ + retval = snprintf(buf, PAGE_SIZE, "%u\n", value); \ +\ +exit: \ + mutex_unlock(&tcm_hcd->extif_mutex); \ +\ + return retval; \ +} \ +\ +static ssize_t syna_tcm_sysfs_##c_name##_store(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + int retval; \ + unsigned int input; \ + struct device *p_dev; \ + struct kobject *p_kobj; \ + struct syna_tcm_hcd *tcm_hcd; \ +\ + p_kobj = sysfs_dir->parent; \ + p_dev = container_of(p_kobj, struct device, kobj); \ + tcm_hcd = dev_get_drvdata(p_dev); \ +\ + if (sscanf(buf, "%u", &input) != 1) \ + return -EINVAL; \ +\ + mutex_lock(&tcm_hcd->extif_mutex); \ +\ + retval = tcm_hcd->set_dynamic_config(tcm_hcd, id, input); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to set dynamic config\n"); \ + goto exit; \ + } \ +\ + retval = count; \ +\ +exit: \ + mutex_unlock(&tcm_hcd->extif_mutex); \ +\ + return retval; \ +} + +static int syna_tcm_update_product_id(struct syna_tcm_hcd *tcm_hcd); + +DECLARE_COMPLETION(response_complete); + +static struct kobject *sysfs_dir; + +static struct syna_tcm_module_pool mod_pool; + +SHOW_PROTOTYPE(syna_tcm, info) +STORE_PROTOTYPE(syna_tcm, irq_en) +STORE_PROTOTYPE(syna_tcm, reset) +STORE_PROTOTYPE(syna_tcm, watchdog) +SHOW_STORE_PROTOTYPE(syna_tcm, no_doze) +SHOW_STORE_PROTOTYPE(syna_tcm, disable_noise_mitigation) +SHOW_STORE_PROTOTYPE(syna_tcm, inhibit_frequency_shift) +SHOW_STORE_PROTOTYPE(syna_tcm, requested_frequency) +SHOW_STORE_PROTOTYPE(syna_tcm, disable_hsync) +SHOW_STORE_PROTOTYPE(syna_tcm, rezero_on_exit_deep_sleep) +SHOW_STORE_PROTOTYPE(syna_tcm, charger_connected) +SHOW_STORE_PROTOTYPE(syna_tcm, no_baseline_relaxation) +SHOW_STORE_PROTOTYPE(syna_tcm, in_wakeup_gesture_mode) +SHOW_STORE_PROTOTYPE(syna_tcm, stimulus_fingers) +SHOW_STORE_PROTOTYPE(syna_tcm, grip_suppression_enabled) +SHOW_STORE_PROTOTYPE(syna_tcm, enable_thick_glove) +SHOW_STORE_PROTOTYPE(syna_tcm, enable_glove) +SHOW_PROTOTYPE(syna_tcm, poweron) +SHOW_PROTOTYPE(syna_tcm, flashprog) +SHOW_PROTOTYPE(syna_tcm, productinfo) + +static struct device_attribute *attrs[] = { + ATTRIFY(info), + ATTRIFY(irq_en), + ATTRIFY(reset), + ATTRIFY(watchdog), + ATTRIFY(poweron), + ATTRIFY(flashprog), + ATTRIFY(productinfo), +}; + +static struct device_attribute *dynamic_config_attrs[] = { + ATTRIFY(no_doze), + ATTRIFY(disable_noise_mitigation), + ATTRIFY(inhibit_frequency_shift), + ATTRIFY(requested_frequency), + ATTRIFY(disable_hsync), + ATTRIFY(rezero_on_exit_deep_sleep), + ATTRIFY(charger_connected), + ATTRIFY(no_baseline_relaxation), + ATTRIFY(in_wakeup_gesture_mode), + ATTRIFY(stimulus_fingers), + ATTRIFY(grip_suppression_enabled), + ATTRIFY(enable_thick_glove), + ATTRIFY(enable_glove), +}; + +static ssize_t syna_tcm_sysfs_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned int count; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm_hcd *tcm_hcd; + + p_kobj = sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm_hcd = dev_get_drvdata(p_dev); + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = tcm_hcd->identify(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + goto exit; + } + + count = 0; + + retval = snprintf(buf, PAGE_SIZE - count, + "TouchComm version: %d\n", + tcm_hcd->id_info.version); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + if (SYNAPTICS_TCM_ID_SUBVERSION == 0) { + retval = snprintf(buf, PAGE_SIZE - count, + "Driver version: %d.%d\n", + (unsigned char)(SYNAPTICS_TCM_ID_VERSION >> 8), + (unsigned char)SYNAPTICS_TCM_ID_VERSION); + } else { + retval = snprintf(buf, PAGE_SIZE - count, + "Driver version: %d.%d.%d\n", + (unsigned char)(SYNAPTICS_TCM_ID_VERSION >> 8), + (unsigned char)SYNAPTICS_TCM_ID_VERSION, + SYNAPTICS_TCM_ID_SUBVERSION); + } + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + switch (tcm_hcd->id_info.mode) { + case MODE_APPLICATION: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: Application\n"); + if (retval < 0) + goto exit; + break; + case MODE_HOST_DOWNLOAD: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: Host Download\n"); + if (retval < 0) + goto exit; + break; + case MODE_BOOTLOADER: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: Bootloader\n"); + if (retval < 0) + goto exit; + break; + case MODE_TDDI_BOOTLOADER: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: TDDI Bootloader\n"); + if (retval < 0) + goto exit; + break; + default: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: Unknown (%d)\n", + tcm_hcd->id_info.mode); + if (retval < 0) + goto exit; + break; + } + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "Part number: "); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = secure_memcpy(buf, + PAGE_SIZE - count, + tcm_hcd->id_info.part_number, + sizeof(tcm_hcd->id_info.part_number), + sizeof(tcm_hcd->id_info.part_number)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy part number string\n"); + goto exit; + } + buf += sizeof(tcm_hcd->id_info.part_number); + count += sizeof(tcm_hcd->id_info.part_number); + + retval = snprintf(buf, PAGE_SIZE - count, + "\n"); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "Packrat number: %d\n", + tcm_hcd->packrat_number); + if (retval < 0) + goto exit; + + count += retval; + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t syna_tcm_sysfs_irq_en_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm_hcd *tcm_hcd; + + p_kobj = sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm_hcd = dev_get_drvdata(p_dev); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (input == 0) { + retval = tcm_hcd->enable_irq(tcm_hcd, false, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to disable interrupt\n"); + goto exit; + } + } else if (input == 1) { + retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable interrupt\n"); + goto exit; + } + } else { + retval = -EINVAL; + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t syna_tcm_sysfs_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + bool hw_reset; + unsigned int input; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm_hcd *tcm_hcd; + + p_kobj = sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm_hcd = dev_get_drvdata(p_dev); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input == 1) + hw_reset = false; + else if (input == 2) + hw_reset = true; + else + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = tcm_hcd->reset(tcm_hcd, hw_reset, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t syna_tcm_sysfs_watchdog_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm_hcd *tcm_hcd; + + p_kobj = sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm_hcd = dev_get_drvdata(p_dev); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 0 && input != 1) + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + tcm_hcd->watchdog.run = input; + tcm_hcd->update_watchdog(tcm_hcd, input); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return count; +} + +static ssize_t syna_tcm_sysfs_poweron_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm_hcd *tcm_hcd; + + p_kobj = sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm_hcd = dev_get_drvdata(p_dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", + (tcm_hcd->in_suspend) ? 0 : 1); +} + +static ssize_t syna_tcm_sysfs_flashprog_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm_hcd *tcm_hcd; + int error; + + p_kobj = sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm_hcd = dev_get_drvdata(p_dev); + + mutex_lock(&tcm_hcd->extif_mutex); + error = tcm_hcd->identify(tcm_hcd, true); + mutex_unlock(&tcm_hcd->extif_mutex); + if (error < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + return (ssize_t)0; + } + + return scnprintf(buf, PAGE_SIZE, "%d\n", + ((tcm_hcd->id_info.mode != MODE_APPLICATION) && + (tcm_hcd->id_info.mode != MODE_PRODUCTION_TEST)) ? 1 : 0); +} + +static ssize_t syna_tcm_sysfs_productinfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm_hcd *tcm_hcd; + int error; + + p_kobj = sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm_hcd = dev_get_drvdata(p_dev); + + mutex_lock(&tcm_hcd->extif_mutex); + error = syna_tcm_update_product_id(tcm_hcd); + mutex_unlock(&tcm_hcd->extif_mutex); + if (error < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to update product id\n"); + return (ssize_t)0; + } + + return scnprintf(buf, PAGE_SIZE, "%s\n", tcm_hcd->product_id_string); +} + +dynamic_config_sysfs(no_doze, DC_NO_DOZE) + +dynamic_config_sysfs(disable_noise_mitigation, DC_DISABLE_NOISE_MITIGATION) + +dynamic_config_sysfs(inhibit_frequency_shift, DC_INHIBIT_FREQUENCY_SHIFT) + +dynamic_config_sysfs(requested_frequency, DC_REQUESTED_FREQUENCY) + +dynamic_config_sysfs(disable_hsync, DC_DISABLE_HSYNC) + +dynamic_config_sysfs(rezero_on_exit_deep_sleep, DC_REZERO_ON_EXIT_DEEP_SLEEP) + +dynamic_config_sysfs(charger_connected, DC_CHARGER_CONNECTED) + +dynamic_config_sysfs(no_baseline_relaxation, DC_NO_BASELINE_RELAXATION) + +dynamic_config_sysfs(in_wakeup_gesture_mode, DC_IN_WAKEUP_GESTURE_MODE) + +dynamic_config_sysfs(stimulus_fingers, DC_STIMULUS_FINGERS) + +dynamic_config_sysfs(grip_suppression_enabled, DC_GRIP_SUPPRESSION_ENABLED) + +dynamic_config_sysfs(enable_thick_glove, DC_ENABLE_THICK_GLOVE) + +dynamic_config_sysfs(enable_glove, DC_ENABLE_GLOVE) + +int syna_tcm_add_module(struct syna_tcm_module_cb *mod_cb, bool insert) +{ + struct syna_tcm_module_handler *mod_handler; + + if (!mod_pool.initialized) { + mutex_init(&mod_pool.mutex); + INIT_LIST_HEAD(&mod_pool.list); + mod_pool.initialized = true; + } + + mutex_lock(&mod_pool.mutex); + + if (insert) { + mod_handler = kzalloc(sizeof(*mod_handler), GFP_KERNEL); + if (!mod_handler) { + pr_err("%s: Failed to allocate memory for mod_handler\n", + __func__); + mutex_unlock(&mod_pool.mutex); + return -ENOMEM; + } + mod_handler->mod_cb = mod_cb; + mod_handler->insert = true; + mod_handler->detach = false; + list_add_tail(&mod_handler->link, &mod_pool.list); + } else if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (mod_handler->mod_cb->type == mod_cb->type) { + mod_handler->insert = false; + mod_handler->detach = true; + goto exit; + } + } + } + +exit: + mutex_unlock(&mod_pool.mutex); + + if (mod_pool.queue_work) + queue_work(mod_pool.workqueue, &mod_pool.work); + + return 0; +} +EXPORT_SYMBOL(syna_tcm_add_module); + +#include +#include + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + struct syna_tcm_hcd *tcm_hcd = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!tcm_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "cannot get tcm_hcd pointer\n"); + return (ssize_t)0; + } + path = kobject_get_path(tcm_hcd->sysfs_dir, GFP_KERNEL); + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "synaptics_tcm"); +} + +static struct device_attribute touchscreen_attributes[] = { + __ATTR_RO(path), + __ATTR_RO(vendor), + __ATTR_NULL +}; + +#define TSDEV_MINOR_BASE 128 +#define TSDEV_MINOR_MAX 32 + +static int syna_tcm_sysfs_touchscreen( + struct syna_tcm_hcd *tcm_hcd, bool create) +{ + struct device_attribute *attrs = touchscreen_attributes; + int i, error = 0; + static struct class *touchscreen_class; + static struct device *ts_class_dev; + static int minor; + + if (create) { + if (syna_tcm_update_product_id(tcm_hcd) < 0) + goto exit; + + minor = input_get_new_minor(TSDEV_MINOR_BASE, TSDEV_MINOR_MAX, + true); + LOGI(tcm_hcd->pdev->dev.parent, + "assigned minor %d\n", minor); + + touchscreen_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreen_class)) { + error = PTR_ERR(touchscreen_class); + touchscreen_class = NULL; + return error; + } + + ts_class_dev = device_create(touchscreen_class, NULL, + MKDEV(INPUT_MAJOR, minor), + tcm_hcd, tcm_hcd->product_id_string); + if (IS_ERR(ts_class_dev)) { + error = PTR_ERR(ts_class_dev); + ts_class_dev = NULL; + return error; + } + + for (i = 0; attrs[i].attr.name != NULL; ++i) { + error = device_create_file(ts_class_dev, &attrs[i]); + if (error) + break; + } + + if (error) + goto device_destroy; + } else { + if (!touchscreen_class || !ts_class_dev) + return -ENODEV; + + for (i = 0; attrs[i].attr.name != NULL; ++i) + device_remove_file(ts_class_dev, &attrs[i]); + + device_unregister(ts_class_dev); + class_unregister(touchscreen_class); + } + + return 0; + +device_destroy: + for (--i; i >= 0; --i) + device_remove_file(ts_class_dev, &attrs[i]); + device_destroy(touchscreen_class, MKDEV(INPUT_MAJOR, minor)); + ts_class_dev = NULL; + class_unregister(touchscreen_class); + LOGE(tcm_hcd->pdev->dev.parent, "error creating touchscreen class\n"); +exit: + return -ENODEV; +} + +static int syna_tcm_update_product_id(struct syna_tcm_hcd *tcm_hcd) +{ + int i, error = 0; + struct syna_tcm_identification *id_info = &tcm_hcd->id_info; + char *src = (char *)id_info->part_number; + char *dst = tcm_hcd->product_id_string; + + error = tcm_hcd->identify(tcm_hcd, true); + if (error < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification when update id\n"); + return -ENODEV; + } + for (i = 0; (*src != '-') && (i < sizeof(id_info->part_number)); i++) + *dst++ = *src++; + + LOGI(tcm_hcd->pdev->dev.parent, + "id string %s\n", tcm_hcd->product_id_string); + + return 0; +} + +static void syna_tcm_module_work(struct work_struct *work) +{ + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_module_handler *tmp_handler; + struct syna_tcm_hcd *tcm_hcd = mod_pool.tcm_hcd; + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry_safe(mod_handler, + tmp_handler, + &mod_pool.list, + link) { + if (mod_handler->insert) { + if (mod_handler->mod_cb->init) + mod_handler->mod_cb->init(tcm_hcd); + mod_handler->insert = false; + } + if (mod_handler->detach) { + if (mod_handler->mod_cb->remove) + mod_handler->mod_cb->remove(tcm_hcd); + list_del(&mod_handler->link); + kfree(mod_handler); + } + } + } + + mutex_unlock(&mod_pool.mutex); + + return; +} + +/** + * syna_tcm_report_notifier() - notify occurrence of report received from device + * + * @data: handle of core module + * + * The occurrence of the report generated by the device is forwarded to the + * asynchronous inbox of each registered application module. + */ +static int syna_tcm_report_notifier(void *data) +{ + struct sched_param param = { .sched_priority = NOTIFIER_PRIORITY }; + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_hcd *tcm_hcd = data; + + sched_setscheduler(current, SCHED_RR, ¶m); + + set_current_state(TASK_INTERRUPTIBLE); + + while (!kthread_should_stop()) { + schedule(); + + if (kthread_should_stop()) + break; + + set_current_state(TASK_RUNNING); + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->asyncbox)) + mod_handler->mod_cb->asyncbox(tcm_hcd); + } + } + + mutex_unlock(&mod_pool.mutex); + + set_current_state(TASK_INTERRUPTIBLE); + }; + + return 0; +} + +/** + * syna_tcm_dispatch_report() - dispatch report received from device + * + * @tcm_hcd: handle of core module + * + * The report generated by the device is forwarded to the synchronous inbox of + * each registered application module for further processing. In addition, the + * report notifier thread is woken up for asynchronous notification of the + * report occurrence. + */ +static void syna_tcm_dispatch_report(struct syna_tcm_hcd *tcm_hcd) +{ + struct syna_tcm_module_handler *mod_handler; + + LOCK_BUFFER(tcm_hcd->in); + LOCK_BUFFER(tcm_hcd->report.buffer); + + tcm_hcd->report.buffer.buf = &tcm_hcd->in.buf[MESSAGE_HEADER_SIZE]; + + tcm_hcd->report.buffer.buf_size = tcm_hcd->in.buf_size; + tcm_hcd->report.buffer.buf_size -= MESSAGE_HEADER_SIZE; + + tcm_hcd->report.buffer.data_length = tcm_hcd->payload_length; + + tcm_hcd->report.id = tcm_hcd->status_report_code; + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->syncbox)) + mod_handler->mod_cb->syncbox(tcm_hcd); + } + } + + + tcm_hcd->async_report_id = tcm_hcd->status_report_code; + + mutex_unlock(&mod_pool.mutex); + + UNLOCK_BUFFER(tcm_hcd->report.buffer); + UNLOCK_BUFFER(tcm_hcd->in); + + wake_up_process(tcm_hcd->notifier_thread); + + return; +} + +/** + * syna_tcm_dispatch_response() - dispatch response received from device + * + * @tcm_hcd: handle of core module + * + * The response to a command is forwarded to the sender of the command. + */ +static void syna_tcm_dispatch_response(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (atomic_read(&tcm_hcd->command_status) != CMD_BUSY) + return; + + tcm_hcd->response_code = tcm_hcd->status_report_code; + + if (tcm_hcd->payload_length == 0) { + atomic_set(&tcm_hcd->command_status, CMD_IDLE); + goto exit; + } + + LOCK_BUFFER(tcm_hcd->resp); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->resp, + tcm_hcd->payload_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for tcm_hcd->resp.buf\n"); + UNLOCK_BUFFER(tcm_hcd->resp); + atomic_set(&tcm_hcd->command_status, CMD_ERROR); + goto exit; + } + + LOCK_BUFFER(tcm_hcd->in); + + retval = secure_memcpy(tcm_hcd->resp.buf, + tcm_hcd->resp.buf_size, + &tcm_hcd->in.buf[MESSAGE_HEADER_SIZE], + tcm_hcd->in.buf_size - MESSAGE_HEADER_SIZE, + tcm_hcd->payload_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy payload\n"); + UNLOCK_BUFFER(tcm_hcd->in); + UNLOCK_BUFFER(tcm_hcd->resp); + atomic_set(&tcm_hcd->command_status, CMD_ERROR); + goto exit; + } + + tcm_hcd->resp.data_length = tcm_hcd->payload_length; + + UNLOCK_BUFFER(tcm_hcd->in); + UNLOCK_BUFFER(tcm_hcd->resp); + + atomic_set(&tcm_hcd->command_status, CMD_IDLE); + +exit: + complete(&response_complete); + + return; +} + +/** + * syna_tcm_dispatch_message() - dispatch message received from device + * + * @tcm_hcd: handle of core module + * + * The information received in the message read in from the device is dispatched + * to the appropriate destination based on whether the information represents a + * report or a response to a command. + */ +static void syna_tcm_dispatch_message(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *build_id; + unsigned int payload_length; + unsigned int max_write_size; + + if (tcm_hcd->status_report_code == REPORT_IDENTIFY) { + payload_length = tcm_hcd->payload_length; + + LOCK_BUFFER(tcm_hcd->in); + + retval = secure_memcpy((unsigned char *)&tcm_hcd->id_info, + sizeof(tcm_hcd->id_info), + &tcm_hcd->in.buf[MESSAGE_HEADER_SIZE], + tcm_hcd->in.buf_size - MESSAGE_HEADER_SIZE, + MIN(sizeof(tcm_hcd->id_info), payload_length)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy identification info\n"); + UNLOCK_BUFFER(tcm_hcd->in); + return; + } + + UNLOCK_BUFFER(tcm_hcd->in); + + build_id = tcm_hcd->id_info.build_id; + tcm_hcd->packrat_number = le4_to_uint(build_id); + + max_write_size = le2_to_uint(tcm_hcd->id_info.max_write_size); + tcm_hcd->wr_chunk_size = MIN(max_write_size, WR_CHUNK_SIZE); + if (tcm_hcd->wr_chunk_size == 0) + tcm_hcd->wr_chunk_size = max_write_size; + + LOGN(tcm_hcd->pdev->dev.parent, + "Received identify report (firmware mode = 0x%02x)\n", + tcm_hcd->id_info.mode); + + if (atomic_read(&tcm_hcd->command_status) == CMD_BUSY) { + switch (tcm_hcd->command) { + case CMD_RESET: + case CMD_RUN_BOOTLOADER_FIRMWARE: + case CMD_RUN_APPLICATION_FIRMWARE: + case CMD_ENTER_PRODUCTION_TEST_MODE: + tcm_hcd->response_code = STATUS_OK; + atomic_set(&tcm_hcd->command_status, CMD_IDLE); + complete(&response_complete); + break; + default: + LOGN(tcm_hcd->pdev->dev.parent, + "Device has been reset\n"); + atomic_set(&tcm_hcd->command_status, CMD_ERROR); + complete(&response_complete); + break; + } + } + + if (tcm_hcd->id_info.mode == MODE_HOST_DOWNLOAD) { + tcm_hcd->host_download_mode = true; + return; + } + +#ifdef FORCE_RUN_APPLICATION_FIRMWARE + if (tcm_hcd->id_info.mode != MODE_APPLICATION && + !mutex_is_locked(&tcm_hcd->reset_mutex)) { + if (atomic_read(&tcm_hcd->helper.task) == HELP_NONE) { + atomic_set(&tcm_hcd->helper.task, + HELP_RUN_APPLICATION_FIRMWARE); + queue_work(tcm_hcd->helper.workqueue, + &tcm_hcd->helper.work); + return; + } + } +#endif + } + + if (tcm_hcd->status_report_code >= REPORT_IDENTIFY) + syna_tcm_dispatch_report(tcm_hcd); + else + syna_tcm_dispatch_response(tcm_hcd); + + return; +} + +/** + * syna_tcm_continued_read() - retrieve entire payload from device + * + * @tcm_hcd: handle of core module + * + * Read transactions are carried out until the entire payload is retrieved from + * the device and stored in the handle of the core module. + */ +static int syna_tcm_continued_read(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char marker; + unsigned char code; + unsigned int idx; + unsigned int offset; + unsigned int chunks; + unsigned int chunk_space; + unsigned int xfer_length; + unsigned int total_length; + unsigned int remaining_length; + + total_length = MESSAGE_HEADER_SIZE + tcm_hcd->payload_length + 1; + + remaining_length = total_length - tcm_hcd->read_length; + + LOCK_BUFFER(tcm_hcd->in); + + retval = syna_tcm_realloc_mem(tcm_hcd, + &tcm_hcd->in, + total_length + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reallocate memory for tcm_hcd->in.buf\n"); + UNLOCK_BUFFER(tcm_hcd->in); + return retval; + } + + /* available chunk space for payload = total chunk size minus header + * marker byte and header code byte */ + if (tcm_hcd->rd_chunk_size == 0) + chunk_space = remaining_length; + else + chunk_space = tcm_hcd->rd_chunk_size - 2; + + chunks = ceil_div(remaining_length, chunk_space); + + chunks = chunks == 0 ? 1 : chunks; + + offset = tcm_hcd->read_length; + + LOCK_BUFFER(tcm_hcd->temp); + + for (idx = 0; idx < chunks; idx++) { + if (remaining_length > chunk_space) + xfer_length = chunk_space; + else + xfer_length = remaining_length; + + if (xfer_length == 1) { + tcm_hcd->in.buf[offset] = MESSAGE_PADDING; + offset += xfer_length; + remaining_length -= xfer_length; + continue; + } + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->temp, + xfer_length + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for tcm_hcd->temp.buf\n"); + UNLOCK_BUFFER(tcm_hcd->temp); + UNLOCK_BUFFER(tcm_hcd->in); + return retval; + } + + retval = syna_tcm_read(tcm_hcd, + tcm_hcd->temp.buf, + xfer_length + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read from device\n"); + UNLOCK_BUFFER(tcm_hcd->temp); + UNLOCK_BUFFER(tcm_hcd->in); + return retval; + } + + marker = tcm_hcd->temp.buf[0]; + code = tcm_hcd->temp.buf[1]; + + if (marker != MESSAGE_MARKER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect header marker (0x%02x)\n", + marker); + UNLOCK_BUFFER(tcm_hcd->temp); + UNLOCK_BUFFER(tcm_hcd->in); + return -EIO; + } + + if (code != STATUS_CONTINUED_READ) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect header code (0x%02x)\n", + code); + UNLOCK_BUFFER(tcm_hcd->temp); + UNLOCK_BUFFER(tcm_hcd->in); + return -EIO; + } + + retval = secure_memcpy(&tcm_hcd->in.buf[offset], + tcm_hcd->in.buf_size - offset, + &tcm_hcd->temp.buf[2], + tcm_hcd->temp.buf_size - 2, + xfer_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy payload\n"); + UNLOCK_BUFFER(tcm_hcd->temp); + UNLOCK_BUFFER(tcm_hcd->in); + return retval; + } + + offset += xfer_length; + + remaining_length -= xfer_length; + } + + UNLOCK_BUFFER(tcm_hcd->temp); + UNLOCK_BUFFER(tcm_hcd->in); + + return 0; +} + +/** + * syna_tcm_raw_read() - retrieve specific number of data bytes from device + * + * @tcm_hcd: handle of core module + * @in_buf: buffer for storing data retrieved from device + * @length: number of bytes to retrieve from device + * + * Read transactions are carried out until the specific number of data bytes are + * retrieved from the device and stored in in_buf. + */ +static int syna_tcm_raw_read(struct syna_tcm_hcd *tcm_hcd, + unsigned char *in_buf, unsigned int length) +{ + int retval; + unsigned char code; + unsigned int idx; + unsigned int offset; + unsigned int chunks; + unsigned int chunk_space; + unsigned int xfer_length; + unsigned int remaining_length; + + if (length < 2) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid length information\n"); + return -EINVAL; + } + + /* minus header marker byte and header code byte */ + remaining_length = length - 2; + + /* available chunk space for data = total chunk size minus header marker + * byte and header code byte */ + if (tcm_hcd->rd_chunk_size == 0) + chunk_space = remaining_length; + else + chunk_space = tcm_hcd->rd_chunk_size - 2; + + chunks = ceil_div(remaining_length, chunk_space); + + chunks = chunks == 0 ? 1 : chunks; + + offset = 0; + + LOCK_BUFFER(tcm_hcd->temp); + + for (idx = 0; idx < chunks; idx++) { + if (remaining_length > chunk_space) + xfer_length = chunk_space; + else + xfer_length = remaining_length; + + if (xfer_length == 1) { + in_buf[offset] = MESSAGE_PADDING; + offset += xfer_length; + remaining_length -= xfer_length; + continue; + } + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->temp, + xfer_length + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for tcm_hcd->temp.buf\n"); + UNLOCK_BUFFER(tcm_hcd->temp); + return retval; + } + + retval = syna_tcm_read(tcm_hcd, + tcm_hcd->temp.buf, + xfer_length + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read from device\n"); + UNLOCK_BUFFER(tcm_hcd->temp); + return retval; + } + + code = tcm_hcd->temp.buf[1]; + + if (idx == 0) { + retval = secure_memcpy(&in_buf[0], + length, + &tcm_hcd->temp.buf[0], + tcm_hcd->temp.buf_size, + xfer_length + 2); + } else { + if (code != STATUS_CONTINUED_READ) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect header code (0x%02x)\n", + code); + UNLOCK_BUFFER(tcm_hcd->temp); + return -EIO; + } + + retval = secure_memcpy(&in_buf[offset], + length - offset, + &tcm_hcd->temp.buf[2], + tcm_hcd->temp.buf_size - 2, + xfer_length); + } + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy data\n"); + UNLOCK_BUFFER(tcm_hcd->temp); + return retval; + } + + if (idx == 0) + offset += (xfer_length + 2); + else + offset += xfer_length; + + remaining_length -= xfer_length; + } + + UNLOCK_BUFFER(tcm_hcd->temp); + + return 0; +} + +/** + * syna_tcm_raw_write() - write command/data to device without receiving + * response + * + * @tcm_hcd: handle of core module + * @command: command to send to device + * @data: data to send to device + * @length: length of data in bytes + * + * A command and its data, if any, are sent to the device. + */ +static int syna_tcm_raw_write(struct syna_tcm_hcd *tcm_hcd, + unsigned char command, unsigned char *data, unsigned int length) +{ + int retval; + unsigned int idx; + unsigned int chunks; + unsigned int chunk_space; + unsigned int xfer_length; + unsigned int remaining_length; + + remaining_length = length; + + /* available chunk space for data = total chunk size minus command + * byte */ + if (tcm_hcd->wr_chunk_size == 0) + chunk_space = remaining_length; + else + chunk_space = tcm_hcd->wr_chunk_size - 1; + + chunks = ceil_div(remaining_length, chunk_space); + + chunks = chunks == 0 ? 1 : chunks; + + LOCK_BUFFER(tcm_hcd->out); + + for (idx = 0; idx < chunks; idx++) { + if (remaining_length > chunk_space) + xfer_length = chunk_space; + else + xfer_length = remaining_length; + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->out, + xfer_length + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for tcm_hcd->out.buf\n"); + UNLOCK_BUFFER(tcm_hcd->out); + return retval; + } + + if (idx == 0) + tcm_hcd->out.buf[0] = command; + else + tcm_hcd->out.buf[0] = CMD_CONTINUE_WRITE; + + if (xfer_length) { + retval = secure_memcpy(&tcm_hcd->out.buf[1], + tcm_hcd->out.buf_size - 1, + &data[idx * chunk_space], + remaining_length, + xfer_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy data\n"); + UNLOCK_BUFFER(tcm_hcd->out); + return retval; + } + } + + retval = syna_tcm_write(tcm_hcd, + tcm_hcd->out.buf, + xfer_length + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to device\n"); + UNLOCK_BUFFER(tcm_hcd->out); + return retval; + } + + remaining_length -= xfer_length; + } + + UNLOCK_BUFFER(tcm_hcd->out); + + return 0; +} + +/** + * syna_tcm_read_message() - read message from device + * + * @tcm_hcd: handle of core module + * @in_buf: buffer for storing data in raw read mode + * @length: length of data in bytes in raw read mode + * + * If in_buf is not NULL, raw read mode is used and syna_tcm_raw_read() is + * called. Otherwise, a message including its entire payload is retrieved from + * the device and dispatched to the appropriate destination. + */ +static int syna_tcm_read_message(struct syna_tcm_hcd *tcm_hcd, + unsigned char *in_buf, unsigned int length) +{ + int retval; + bool retry; + unsigned int total_length; + struct syna_tcm_message_header *header; + + mutex_lock(&tcm_hcd->rw_ctrl_mutex); + + if (in_buf != NULL) { + retval = syna_tcm_raw_read(tcm_hcd, in_buf, length); + goto exit; + } + + retry = true; + +retry: + LOCK_BUFFER(tcm_hcd->in); + + retval = syna_tcm_read(tcm_hcd, + tcm_hcd->in.buf, + tcm_hcd->read_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read from device\n"); + UNLOCK_BUFFER(tcm_hcd->in); + if (retry) { + usleep_range(READ_RETRY_US_MIN, READ_RETRY_US_MAX); + retry = false; + goto retry; + } + goto exit; + } + + header = (struct syna_tcm_message_header *)tcm_hcd->in.buf; + + if (header->marker != MESSAGE_MARKER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect header marker (0x%02x)\n", + header->marker); + UNLOCK_BUFFER(tcm_hcd->in); + retval = -ENXIO; + if (retry) { + usleep_range(READ_RETRY_US_MIN, READ_RETRY_US_MAX); + retry = false; + goto retry; + } + goto exit; + } + + tcm_hcd->status_report_code = header->code; + + tcm_hcd->payload_length = le2_to_uint(header->length); + + LOGD(tcm_hcd->pdev->dev.parent, + "Header code = 0x%02x\n", + tcm_hcd->status_report_code); + + LOGD(tcm_hcd->pdev->dev.parent, + "Payload length = %d\n", + tcm_hcd->payload_length); + + if (tcm_hcd->status_report_code <= STATUS_ERROR || + tcm_hcd->status_report_code == STATUS_INVALID) { + switch (tcm_hcd->status_report_code) { + case STATUS_OK: + break; + case STATUS_CONTINUED_READ: + LOGD(tcm_hcd->pdev->dev.parent, + "Out-of-sync continued read\n"); + case STATUS_IDLE: + case STATUS_BUSY: + tcm_hcd->payload_length = 0; + UNLOCK_BUFFER(tcm_hcd->in); + retval = 0; + goto exit; + default: + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect header code (0x%02x)\n", + tcm_hcd->status_report_code); + if (tcm_hcd->status_report_code == STATUS_INVALID) { + if (retry) { + usleep_range(READ_RETRY_US_MIN, + READ_RETRY_US_MAX); + retry = false; + goto retry; + } else { + tcm_hcd->payload_length = 0; + } + } + } + } + + total_length = MESSAGE_HEADER_SIZE + tcm_hcd->payload_length + 1; + +#ifdef PREDICTIVE_READING + if (total_length <= tcm_hcd->read_length) { + goto check_padding; + } else if (total_length - 1 == tcm_hcd->read_length) { + tcm_hcd->in.buf[total_length - 1] = MESSAGE_PADDING; + goto check_padding; + } +#else + if (tcm_hcd->payload_length == 0) { + tcm_hcd->in.buf[total_length - 1] = MESSAGE_PADDING; + goto check_padding; + } +#endif + + UNLOCK_BUFFER(tcm_hcd->in); + + retval = syna_tcm_continued_read(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do continued read\n"); + goto exit; + }; + + LOCK_BUFFER(tcm_hcd->in); + + tcm_hcd->in.buf[0] = MESSAGE_MARKER; + tcm_hcd->in.buf[1] = tcm_hcd->status_report_code; + tcm_hcd->in.buf[2] = (unsigned char)tcm_hcd->payload_length; + tcm_hcd->in.buf[3] = (unsigned char)(tcm_hcd->payload_length >> 8); + +check_padding: + if (tcm_hcd->in.buf[total_length - 1] != MESSAGE_PADDING) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect message padding byte (0x%02x)\n", + tcm_hcd->in.buf[total_length - 1]); + UNLOCK_BUFFER(tcm_hcd->in); + retval = -EIO; + goto exit; + } + + UNLOCK_BUFFER(tcm_hcd->in); + +#ifdef PREDICTIVE_READING + total_length = MAX(total_length, MIN_READ_LENGTH); + tcm_hcd->read_length = MIN(total_length, tcm_hcd->rd_chunk_size); + if (tcm_hcd->rd_chunk_size == 0) + tcm_hcd->read_length = total_length; +#endif + + syna_tcm_dispatch_message(tcm_hcd); + + retval = 0; + +exit: + if (retval < 0) { + if (atomic_read(&tcm_hcd->command_status) == CMD_BUSY) { + atomic_set(&tcm_hcd->command_status, CMD_ERROR); + complete(&response_complete); + } + } + + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + + return retval; +} + +/** + * syna_tcm_write_message() - write message to device and receive response + * + * @tcm_hcd: handle of core module + * @command: command to send to device + * @payload: payload of command + * @length: length of payload in bytes + * @resp_buf: buffer for storing command response + * @resp_buf_size: size of response buffer in bytes + * @resp_length: length of command response in bytes + * @response_code: status code returned in command response + * @polling_delay_ms: delay time after sending command before resuming polling + * + * If resp_buf is NULL, raw write mode is used and syna_tcm_raw_write() is + * called. Otherwise, a command and its payload, if any, are sent to the device + * and the response to the command generated by the device is read in. + */ +static int syna_tcm_write_message(struct syna_tcm_hcd *tcm_hcd, + unsigned char command, unsigned char *payload, + unsigned int length, unsigned char **resp_buf, + unsigned int *resp_buf_size, unsigned int *resp_length, + unsigned char *response_code, unsigned int polling_delay_ms) +{ + int retval; + unsigned int idx; + unsigned int chunks; + unsigned int chunk_space; + unsigned int xfer_length; + unsigned int remaining_length; + unsigned int command_status; + + if (response_code != NULL) + *response_code = STATUS_INVALID; + + if (!tcm_hcd->do_polling && current->pid == tcm_hcd->isr_pid) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid execution context\n"); + return -EINVAL; + } + + mutex_lock(&tcm_hcd->command_mutex); + + mutex_lock(&tcm_hcd->rw_ctrl_mutex); + + if (resp_buf == NULL) { + retval = syna_tcm_raw_write(tcm_hcd, command, payload, length); + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + goto exit; + } + + if (tcm_hcd->do_polling && polling_delay_ms) { + cancel_delayed_work_sync(&tcm_hcd->polling_work); + flush_workqueue(tcm_hcd->polling_workqueue); + } + + atomic_set(&tcm_hcd->command_status, CMD_BUSY); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)) + reinit_completion(&response_complete); +#else + INIT_COMPLETION(response_complete); +#endif + + tcm_hcd->command = command; + + LOCK_BUFFER(tcm_hcd->resp); + + tcm_hcd->resp.buf = *resp_buf; + tcm_hcd->resp.buf_size = *resp_buf_size; + tcm_hcd->resp.data_length = 0; + + UNLOCK_BUFFER(tcm_hcd->resp); + + /* adding two length bytes as part of payload */ + remaining_length = length + 2; + + /* available chunk space for payload = total chunk size minus command + * byte */ + if (tcm_hcd->wr_chunk_size == 0) + chunk_space = remaining_length; + else + chunk_space = tcm_hcd->wr_chunk_size - 1; + + chunks = ceil_div(remaining_length, chunk_space); + + chunks = chunks == 0 ? 1 : chunks; + + LOGD(tcm_hcd->pdev->dev.parent, + "Command = 0x%02x\n", + command); + + LOCK_BUFFER(tcm_hcd->out); + + for (idx = 0; idx < chunks; idx++) { + if (remaining_length > chunk_space) + xfer_length = chunk_space; + else + xfer_length = remaining_length; + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->out, + xfer_length + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for tcm_hcd->out.buf\n"); + UNLOCK_BUFFER(tcm_hcd->out); + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + goto exit; + } + + if (idx == 0) { + tcm_hcd->out.buf[0] = command; + tcm_hcd->out.buf[1] = (unsigned char)length; + tcm_hcd->out.buf[2] = (unsigned char)(length >> 8); + + if (xfer_length > 2) { + retval = secure_memcpy(&tcm_hcd->out.buf[3], + tcm_hcd->out.buf_size - 3, + payload, + remaining_length - 2, + xfer_length - 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy payload\n"); + UNLOCK_BUFFER(tcm_hcd->out); + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + goto exit; + } + } + } else { + tcm_hcd->out.buf[0] = CMD_CONTINUE_WRITE; + + retval = secure_memcpy(&tcm_hcd->out.buf[1], + tcm_hcd->out.buf_size - 1, + &payload[idx * chunk_space - 2], + remaining_length, + xfer_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy payload\n"); + UNLOCK_BUFFER(tcm_hcd->out); + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + goto exit; + } + } + + retval = syna_tcm_write(tcm_hcd, + tcm_hcd->out.buf, + xfer_length + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to device\n"); + UNLOCK_BUFFER(tcm_hcd->out); + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + goto exit; + } + + remaining_length -= xfer_length; + + if (chunks > 1) + usleep_range(WRITE_DELAY_US_MIN, WRITE_DELAY_US_MAX); + } + + UNLOCK_BUFFER(tcm_hcd->out); + + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + + if (tcm_hcd->do_polling && polling_delay_ms) { + queue_delayed_work(tcm_hcd->polling_workqueue, + &tcm_hcd->polling_work, + msecs_to_jiffies(polling_delay_ms)); + } + + retval = wait_for_completion_timeout(&response_complete, + msecs_to_jiffies(RESPONSE_TIMEOUT_MS)); + if (retval == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Timed out waiting for response (command 0x%02x)\n", + tcm_hcd->command); + retval = -EIO; + goto exit; + } + + command_status = atomic_read(&tcm_hcd->command_status); + if (command_status != CMD_IDLE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get valid response (command 0x%02x)\n", + tcm_hcd->command); + retval = -EIO; + goto exit; + } + + LOCK_BUFFER(tcm_hcd->resp); + + if (tcm_hcd->response_code != STATUS_OK) { + if (tcm_hcd->resp.data_length) { + LOGE(tcm_hcd->pdev->dev.parent, + "Error code = 0x%02x (command 0x%02x)\n", + tcm_hcd->resp.buf[0], tcm_hcd->command); + } + retval = -EIO; + } else { + retval = 0; + } + + *resp_buf = tcm_hcd->resp.buf; + *resp_buf_size = tcm_hcd->resp.buf_size; + *resp_length = tcm_hcd->resp.data_length; + + if (response_code != NULL) + *response_code = tcm_hcd->response_code; + + UNLOCK_BUFFER(tcm_hcd->resp); + +exit: + tcm_hcd->command = CMD_NONE; + + atomic_set(&tcm_hcd->command_status, CMD_IDLE); + + mutex_unlock(&tcm_hcd->command_mutex); + + return retval; +} + +static int syna_tcm_wait_hdl(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + msleep(HOST_DOWNLOAD_WAIT_MS); + + if (!atomic_read(&tcm_hcd->host_downloading)) + return 0; + + retval = wait_event_interruptible_timeout(tcm_hcd->hdl_wq, + !atomic_read(&tcm_hcd->host_downloading), + msecs_to_jiffies(HOST_DOWNLOAD_TIMEOUT_MS)); + if (retval == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Timed out waiting for completion of host download\n"); + atomic_set(&tcm_hcd->host_downloading, 0); + retval = -EIO; + } else { + retval = 0; + } + + return retval; +} + +static void syna_tcm_check_hdl(struct syna_tcm_hcd *tcm_hcd) +{ + struct syna_tcm_module_handler *mod_handler; + + LOCK_BUFFER(tcm_hcd->report.buffer); + + tcm_hcd->report.buffer.buf = NULL; + tcm_hcd->report.buffer.buf_size = 0; + tcm_hcd->report.buffer.data_length = 0; + tcm_hcd->report.id = REPORT_HDL; + + UNLOCK_BUFFER(tcm_hcd->report.buffer); + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->syncbox)) + mod_handler->mod_cb->syncbox(tcm_hcd); + } + } + + mutex_unlock(&mod_pool.mutex); + + return; +} + +static void syna_tcm_update_watchdog(struct syna_tcm_hcd *tcm_hcd, bool en) +{ + cancel_delayed_work_sync(&tcm_hcd->watchdog.work); + flush_workqueue(tcm_hcd->watchdog.workqueue); + + if (!tcm_hcd->watchdog.run) { + tcm_hcd->watchdog.count = 0; + return; + } + + if (en) { + queue_delayed_work(tcm_hcd->watchdog.workqueue, + &tcm_hcd->watchdog.work, + msecs_to_jiffies(WATCHDOG_DELAY_MS)); + } else { + tcm_hcd->watchdog.count = 0; + } + + return; +} + +static void syna_tcm_watchdog_work(struct work_struct *work) +{ + int retval; + unsigned char marker; + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct syna_tcm_watchdog *watchdog = + container_of(delayed_work, struct syna_tcm_watchdog, + work); + struct syna_tcm_hcd *tcm_hcd = + container_of(watchdog, struct syna_tcm_hcd, watchdog); + + if (mutex_is_locked(&tcm_hcd->rw_ctrl_mutex)) + goto exit; + + mutex_lock(&tcm_hcd->rw_ctrl_mutex); + + retval = syna_tcm_read(tcm_hcd, + &marker, + 1); + + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + + if (retval < 0 || marker != MESSAGE_MARKER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read from device\n"); + + tcm_hcd->watchdog.count++; + + if (tcm_hcd->watchdog.count >= WATCHDOG_TRIGGER_COUNT) { + retval = tcm_hcd->reset(tcm_hcd, true, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + tcm_hcd->watchdog.count = 0; + } + } + +exit: + queue_delayed_work(tcm_hcd->watchdog.workqueue, + &tcm_hcd->watchdog.work, + msecs_to_jiffies(WATCHDOG_DELAY_MS)); + + return; +} + +static void syna_tcm_polling_work(struct work_struct *work) +{ + int retval; + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct syna_tcm_hcd *tcm_hcd = + container_of(delayed_work, struct syna_tcm_hcd, + polling_work); + + if (!tcm_hcd->do_polling) + return; + + retval = tcm_hcd->read_message(tcm_hcd, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read message\n"); + if (retval == -ENXIO && tcm_hcd->hw_if->bus_io->type == BUS_SPI) + syna_tcm_check_hdl(tcm_hcd); + } + + if (!(tcm_hcd->in_suspend && retval < 0)) { + queue_delayed_work(tcm_hcd->polling_workqueue, + &tcm_hcd->polling_work, + msecs_to_jiffies(POLLING_DELAY_MS)); + } + + return; +} + +static irqreturn_t syna_tcm_isr(int irq, void *data) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = data; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (unlikely(gpio_get_value(bdata->irq_gpio) != bdata->irq_on_state)) + goto exit; + + tcm_hcd->isr_pid = current->pid; + + retval = tcm_hcd->read_message(tcm_hcd, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read message\n"); + if (retval == -ENXIO && tcm_hcd->hw_if->bus_io->type == BUS_SPI) + syna_tcm_check_hdl(tcm_hcd); + } + +exit: + return IRQ_HANDLED; +} + +static int syna_tcm_enable_irq(struct syna_tcm_hcd *tcm_hcd, bool en, bool ns) +{ + int retval; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + static bool irq_freed = true; + + mutex_lock(&tcm_hcd->irq_en_mutex); + + if (en) { + if (tcm_hcd->irq_enabled) { + LOGD(tcm_hcd->pdev->dev.parent, + "Interrupt already enabled\n"); + retval = 0; + goto exit; + } + + if (bdata->irq_gpio < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid IRQ GPIO\n"); + retval = -EINVAL; + goto queue_polling_work; + } + + if (irq_freed) { + retval = request_threaded_irq(tcm_hcd->irq, NULL, + syna_tcm_isr, bdata->irq_flags, + PLATFORM_DRIVER_NAME, tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create interrupt thread\n"); + } + } else { + enable_irq(tcm_hcd->irq); + retval = 0; + } + +queue_polling_work: + if (retval < 0) { +#ifdef FALL_BACK_ON_POLLING + queue_delayed_work(tcm_hcd->polling_workqueue, + &tcm_hcd->polling_work, + msecs_to_jiffies(POLLING_DELAY_MS)); + tcm_hcd->do_polling = true; + retval = 0; +#endif + } + + if (retval < 0) + goto exit; + else + msleep(ENABLE_IRQ_DELAY_MS); + } else { + if (!tcm_hcd->irq_enabled) { + LOGD(tcm_hcd->pdev->dev.parent, + "Interrupt already disabled\n"); + retval = 0; + goto exit; + } + + if (bdata->irq_gpio >= 0) { + if (ns) { + disable_irq_nosync(tcm_hcd->irq); + } else { + disable_irq(tcm_hcd->irq); + free_irq(tcm_hcd->irq, tcm_hcd); + } + irq_freed = !ns; + } + + if (ns) { + cancel_delayed_work(&tcm_hcd->polling_work); + } else { + cancel_delayed_work_sync(&tcm_hcd->polling_work); + flush_workqueue(tcm_hcd->polling_workqueue); + } + + tcm_hcd->do_polling = false; + } + + retval = 0; + +exit: + if (retval == 0) + tcm_hcd->irq_enabled = en; + + mutex_unlock(&tcm_hcd->irq_en_mutex); + + return retval; +} + +static int syna_tcm_set_gpio(struct syna_tcm_hcd *tcm_hcd, int gpio, + bool config, int dir, int state) +{ + int retval; + char label[16]; + + if (config) { + retval = snprintf(label, 16, "tcm_gpio_%d\n", gpio); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set GPIO label\n"); + return retval; + } + + retval = gpio_request(gpio, label); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to request GPIO %d\n", + gpio); + return retval; + } + + if (dir == 0) + retval = gpio_direction_input(gpio); + else + retval = gpio_direction_output(gpio, state); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set GPIO %d direction\n", + gpio); + return retval; + } + } else { + gpio_free(gpio); + } + + return 0; +} + +static int syna_tcm_config_gpio(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (bdata->irq_gpio >= 0) { + retval = syna_tcm_set_gpio(tcm_hcd, bdata->irq_gpio, + true, 0, 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to configure interrupt GPIO\n"); + goto err_set_gpio_irq; + } + } + + if (bdata->power_gpio >= 0) { + retval = syna_tcm_set_gpio(tcm_hcd, bdata->power_gpio, + true, 1, !bdata->power_on_state); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to configure power GPIO\n"); + goto err_set_gpio_power; + } + } + + if (bdata->reset_gpio >= 0) { + retval = syna_tcm_set_gpio(tcm_hcd, bdata->reset_gpio, + true, 1, !bdata->reset_on_state); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to configure reset GPIO\n"); + goto err_set_gpio_reset; + } + } + + if (bdata->power_gpio >= 0) { + gpio_set_value(bdata->power_gpio, bdata->power_on_state); + msleep(bdata->power_delay_ms); + } + + if (bdata->reset_gpio >= 0) { + gpio_set_value(bdata->reset_gpio, bdata->reset_on_state); + msleep(bdata->reset_active_ms); + gpio_set_value(bdata->reset_gpio, !bdata->reset_on_state); + msleep(bdata->reset_delay_ms); + } + + return 0; + +err_set_gpio_reset: + if (bdata->power_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->power_gpio, false, 0, 0); + +err_set_gpio_power: + if (bdata->irq_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->irq_gpio, false, 0, 0); + +err_set_gpio_irq: + return retval; +} + +static int syna_tcm_enable_regulator(struct syna_tcm_hcd *tcm_hcd, bool en) +{ + int retval; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (!en) { + retval = 0; + goto disable_pwr_reg; + } + + if (tcm_hcd->bus_reg) { + retval = regulator_enable(tcm_hcd->bus_reg); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable bus regulator\n"); + goto exit; + } + } + + if (tcm_hcd->pwr_reg) { + retval = regulator_enable(tcm_hcd->pwr_reg); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable power regulator\n"); + goto disable_bus_reg; + } + msleep(bdata->power_delay_ms); + } + + return 0; + +disable_pwr_reg: + if (tcm_hcd->pwr_reg) + regulator_disable(tcm_hcd->pwr_reg); + +disable_bus_reg: + if (tcm_hcd->bus_reg) + regulator_disable(tcm_hcd->bus_reg); + +exit: + return retval; +} + +static int syna_tcm_get_regulator(struct syna_tcm_hcd *tcm_hcd, bool get) +{ + int retval; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (!get) { + retval = 0; + goto regulator_put; + } + + if (bdata->bus_reg_name != NULL && *bdata->bus_reg_name != 0) { + tcm_hcd->bus_reg = regulator_get(tcm_hcd->pdev->dev.parent, + bdata->bus_reg_name); + if (IS_ERR(tcm_hcd->bus_reg)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get bus regulator\n"); + retval = PTR_ERR(tcm_hcd->bus_reg); + goto regulator_put; + } + } + + if (bdata->pwr_reg_name != NULL && *bdata->pwr_reg_name != 0) { + tcm_hcd->pwr_reg = regulator_get(tcm_hcd->pdev->dev.parent, + bdata->pwr_reg_name); + if (IS_ERR(tcm_hcd->pwr_reg)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get power regulator\n"); + retval = PTR_ERR(tcm_hcd->pwr_reg); + goto regulator_put; + } + } + + return 0; + +regulator_put: + if (tcm_hcd->bus_reg) { + regulator_put(tcm_hcd->bus_reg); + tcm_hcd->bus_reg = NULL; + } + + if (tcm_hcd->pwr_reg) { + regulator_put(tcm_hcd->pwr_reg); + tcm_hcd->pwr_reg = NULL; + } + + return retval; +} + +static int syna_tcm_get_app_info(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + unsigned int timeout; + + timeout = APP_STATUS_POLL_TIMEOUT_MS; + + resp_buf = NULL; + resp_buf_size = 0; + +get_app_info: + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_APPLICATION_INFO, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_APPLICATION_INFO)); + goto exit; + } + + retval = secure_memcpy((unsigned char *)&tcm_hcd->app_info, + sizeof(tcm_hcd->app_info), + resp_buf, + resp_buf_size, + MIN(sizeof(tcm_hcd->app_info), resp_length)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy application info\n"); + goto exit; + } + + tcm_hcd->app_status = le2_to_uint(tcm_hcd->app_info.status); + + if (tcm_hcd->app_status == APP_STATUS_BOOTING || + tcm_hcd->app_status == APP_STATUS_UPDATING) { + if (timeout > 0) { + msleep(APP_STATUS_POLL_MS); + timeout -= APP_STATUS_POLL_MS; + goto get_app_info; + } + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_get_boot_info(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + resp_buf = NULL; + resp_buf_size = 0; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_BOOT_INFO, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_BOOT_INFO)); + goto exit; + } + + retval = secure_memcpy((unsigned char *)&tcm_hcd->boot_info, + sizeof(tcm_hcd->boot_info), + resp_buf, + resp_buf_size, + MIN(sizeof(tcm_hcd->boot_info), resp_length)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy boot info\n"); + goto exit; + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_identify(struct syna_tcm_hcd *tcm_hcd, bool id) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + unsigned int max_write_size; + + resp_buf = NULL; + resp_buf_size = 0; + + mutex_lock(&tcm_hcd->identify_mutex); + + if (!id) + goto get_info; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_IDENTIFY, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_IDENTIFY)); + goto exit; + } + + retval = secure_memcpy((unsigned char *)&tcm_hcd->id_info, + sizeof(tcm_hcd->id_info), + resp_buf, + resp_buf_size, + MIN(sizeof(tcm_hcd->id_info), resp_length)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy identification info\n"); + goto exit; + } + + tcm_hcd->packrat_number = le4_to_uint(tcm_hcd->id_info.build_id); + + max_write_size = le2_to_uint(tcm_hcd->id_info.max_write_size); + tcm_hcd->wr_chunk_size = MIN(max_write_size, WR_CHUNK_SIZE); + if (tcm_hcd->wr_chunk_size == 0) + tcm_hcd->wr_chunk_size = max_write_size; + +get_info: + switch (tcm_hcd->id_info.mode) { + case MODE_APPLICATION: + retval = syna_tcm_get_app_info(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get application info\n"); + goto exit; + } + break; + case MODE_BOOTLOADER: + case MODE_TDDI_BOOTLOADER: + retval = syna_tcm_get_boot_info(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get boot info\n"); + goto exit; + } + break; + default: + break; + } + + retval = 0; + +exit: + mutex_unlock(&tcm_hcd->identify_mutex); + + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_run_production_test_firmware(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + bool retry; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + retry = true; + + resp_buf = NULL; + resp_buf_size = 0; + +retry: + retval = tcm_hcd->write_message(tcm_hcd, + CMD_ENTER_PRODUCTION_TEST_MODE, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + MODE_SWITCH_DELAY_MS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_ENTER_PRODUCTION_TEST_MODE)); + goto exit; + } + + if (tcm_hcd->id_info.mode != MODE_PRODUCTION_TEST) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run production test firmware\n"); + if (retry) { + retry = false; + goto retry; + } + retval = -EINVAL; + goto exit; + } else if (tcm_hcd->app_status != APP_STATUS_OK) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application status = 0x%02x\n", + tcm_hcd->app_status); + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_run_application_firmware(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + bool retry; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + retry = true; + + resp_buf = NULL; + resp_buf_size = 0; + +retry: + retval = tcm_hcd->write_message(tcm_hcd, + CMD_RUN_APPLICATION_FIRMWARE, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + MODE_SWITCH_DELAY_MS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_RUN_APPLICATION_FIRMWARE)); + goto exit; + } + + retval = tcm_hcd->identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + goto exit; + } + + if (tcm_hcd->id_info.mode != MODE_APPLICATION) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware (boot status = 0x%02x)\n", + tcm_hcd->boot_info.status); + if (retry) { + retry = false; + goto retry; + } + retval = -EINVAL; + goto exit; + } else if (tcm_hcd->app_status != APP_STATUS_OK) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application status = 0x%02x\n", + tcm_hcd->app_status); + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_run_bootloader_firmware(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + resp_buf = NULL; + resp_buf_size = 0; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_RUN_BOOTLOADER_FIRMWARE, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + MODE_SWITCH_DELAY_MS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_RUN_BOOTLOADER_FIRMWARE)); + goto exit; + } + + retval = tcm_hcd->identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + goto exit; + } + + if (tcm_hcd->id_info.mode == MODE_APPLICATION) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enter bootloader mode\n"); + retval = -EINVAL; + goto exit; + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_switch_mode(struct syna_tcm_hcd *tcm_hcd, + enum firmware_mode mode) +{ + int retval; + + mutex_lock(&tcm_hcd->reset_mutex); + + tcm_hcd->update_watchdog(tcm_hcd, false); + + switch (mode) { + case FW_MODE_BOOTLOADER: + retval = syna_tcm_run_bootloader_firmware(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to switch to bootloader mode\n"); + goto exit; + } + break; + case FW_MODE_APPLICATION: + retval = syna_tcm_run_application_firmware(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to switch to application mode\n"); + goto exit; + } + break; + case FW_MODE_PRODUCTION_TEST: + retval = syna_tcm_run_production_test_firmware(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to switch to production test mode\n"); + goto exit; + } + break; + default: + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid firmware mode\n"); + retval = -EINVAL; + goto exit; + } + + retval = 0; + +exit: + tcm_hcd->update_watchdog(tcm_hcd, true); + + mutex_unlock(&tcm_hcd->reset_mutex); + + return retval; +} + +static int syna_tcm_get_dynamic_config(struct syna_tcm_hcd *tcm_hcd, + enum dynamic_config_id id, unsigned short *value) +{ + int retval; + unsigned char out_buf; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + resp_buf = NULL; + resp_buf_size = 0; + + out_buf = (unsigned char)id; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_DYNAMIC_CONFIG, + &out_buf, + sizeof(out_buf), + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_DYNAMIC_CONFIG)); + goto exit; + } + + if (resp_length < 2) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid data length\n"); + retval = -EINVAL; + goto exit; + } + + *value = (unsigned short)le2_to_uint(resp_buf); + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_set_dynamic_config(struct syna_tcm_hcd *tcm_hcd, + enum dynamic_config_id id, unsigned short value) +{ + int retval; + unsigned char out_buf[3]; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + resp_buf = NULL; + resp_buf_size = 0; + + out_buf[0] = (unsigned char)id; + out_buf[1] = (unsigned char)value; + out_buf[2] = (unsigned char)(value >> 8); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_SET_DYNAMIC_CONFIG, + out_buf, + sizeof(out_buf), + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_SET_DYNAMIC_CONFIG)); + goto exit; + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_get_data_location(struct syna_tcm_hcd *tcm_hcd, + enum flash_area area, unsigned int *addr, unsigned int *length) +{ + int retval; + unsigned char out_buf; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + switch (area) { + case CUSTOM_LCM: + out_buf = LCM_DATA; + break; + case CUSTOM_OEM: + out_buf = OEM_DATA; + break; + case PPDT: + out_buf = PPDT_DATA; + break; + default: + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid flash area\n"); + return -EINVAL; + } + + resp_buf = NULL; + resp_buf_size = 0; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_DATA_LOCATION, + &out_buf, + sizeof(out_buf), + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_DATA_LOCATION)); + goto exit; + } + + if (resp_length != 4) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid data length\n"); + retval = -EINVAL; + goto exit; + } + + *addr = le2_to_uint(&resp_buf[0]); + *length = le2_to_uint(&resp_buf[2]); + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_sleep(struct syna_tcm_hcd *tcm_hcd, bool en) +{ + int retval; + unsigned char command; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + command = en ? CMD_ENTER_DEEP_SLEEP : CMD_EXIT_DEEP_SLEEP; + + resp_buf = NULL; + resp_buf_size = 0; + + retval = tcm_hcd->write_message(tcm_hcd, + command, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + en ? + STR(CMD_ENTER_DEEP_SLEEP) : + STR(CMD_EXIT_DEEP_SLEEP)); + goto exit; + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_reset(struct syna_tcm_hcd *tcm_hcd, bool hw, bool update_wd) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + struct syna_tcm_module_handler *mod_handler; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + resp_buf = NULL; + resp_buf_size = 0; + + mutex_lock(&tcm_hcd->reset_mutex); + + if (update_wd) + tcm_hcd->update_watchdog(tcm_hcd, false); + + if (hw) { + if (bdata->reset_gpio < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Hardware reset unavailable\n"); + retval = -EINVAL; + goto exit; + } + gpio_set_value(bdata->reset_gpio, bdata->reset_on_state); + msleep(bdata->reset_active_ms); + gpio_set_value(bdata->reset_gpio, !bdata->reset_on_state); + } else { + retval = tcm_hcd->write_message(tcm_hcd, + CMD_RESET, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + bdata->reset_delay_ms); + if (retval < 0 && !tcm_hcd->host_download_mode) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_RESET)); + goto exit; + } + } + + if (tcm_hcd->host_download_mode) { + mutex_unlock(&tcm_hcd->reset_mutex); + kfree(resp_buf); + retval = syna_tcm_wait_hdl(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to wait for completion of host download\n"); + return retval; + } + if (update_wd) + tcm_hcd->update_watchdog(tcm_hcd, true); + return 0; + } + + msleep(bdata->reset_delay_ms); + + retval = tcm_hcd->identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + goto exit; + } + + if (tcm_hcd->id_info.mode == MODE_APPLICATION) + goto get_features; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_RUN_APPLICATION_FIRMWARE, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + MODE_SWITCH_DELAY_MS); + if (retval < 0) { + LOGN(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_RUN_APPLICATION_FIRMWARE)); + } + + retval = tcm_hcd->identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + goto exit; + } + +get_features: + LOGN(tcm_hcd->pdev->dev.parent, + "Firmware mode = 0x%02x\n", + tcm_hcd->id_info.mode); + + if (tcm_hcd->id_info.mode != MODE_APPLICATION) { + LOGN(tcm_hcd->pdev->dev.parent, + "Boot status = 0x%02x\n", + tcm_hcd->boot_info.status); + } else if (tcm_hcd->app_status != APP_STATUS_OK) { + LOGN(tcm_hcd->pdev->dev.parent, + "Application status = 0x%02x\n", + tcm_hcd->app_status); + } + + if (tcm_hcd->id_info.mode != MODE_APPLICATION) + goto dispatch_reset; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_FEATURES, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGN(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_FEATURES)); + } else { + retval = secure_memcpy((unsigned char *)&tcm_hcd->features, + sizeof(tcm_hcd->features), + resp_buf, + resp_buf_size, + MIN(sizeof(tcm_hcd->features), resp_length)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy feature description\n"); + } + } + +dispatch_reset: + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->reset)) + mod_handler->mod_cb->reset(tcm_hcd); + } + } + + mutex_unlock(&mod_pool.mutex); + + retval = 0; + +exit: + if (update_wd) + tcm_hcd->update_watchdog(tcm_hcd, true); + + mutex_unlock(&tcm_hcd->reset_mutex); + + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_rezero(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + resp_buf = NULL; + resp_buf_size = 0; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_REZERO, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_REZERO)); + goto exit; + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static void syna_tcm_helper_work(struct work_struct *work) +{ + int retval; + unsigned char task; + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_helper *helper = + container_of(work, struct syna_tcm_helper, work); + struct syna_tcm_hcd *tcm_hcd = + container_of(helper, struct syna_tcm_hcd, helper); + + task = atomic_read(&helper->task); + + switch (task) { + case HELP_RUN_APPLICATION_FIRMWARE: + mutex_lock(&tcm_hcd->reset_mutex); + tcm_hcd->update_watchdog(tcm_hcd, false); + retval = syna_tcm_run_application_firmware(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to switch to application mode\n"); + } + tcm_hcd->update_watchdog(tcm_hcd, true); + mutex_unlock(&tcm_hcd->reset_mutex); + break; + case HELP_SEND_RESET_NOTIFICATION: + mutex_lock(&tcm_hcd->reset_mutex); + retval = tcm_hcd->identify(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + mutex_unlock(&tcm_hcd->reset_mutex); + break; + } + mutex_lock(&mod_pool.mutex); + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->reset)) + mod_handler->mod_cb->reset(tcm_hcd); + } + } + mutex_unlock(&mod_pool.mutex); + mutex_unlock(&tcm_hcd->reset_mutex); + break; + default: + break; + } + + atomic_set(&helper->task, HELP_NONE); + + return; +} + +#if defined(CONFIG_PM) || defined(CONFIG_FB) +static int syna_tcm_resume(struct device *dev) +{ + int retval; + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_hcd *tcm_hcd = dev_get_drvdata(dev); + + if (!tcm_hcd->in_suspend) + return 0; + + tcm_hcd->enable_irq(tcm_hcd, true, NULL); + +#ifdef PM_SKIP_SLEEP + LOGI(tcm_hcd->pdev->dev.parent, "skip sleep in resume\n"); + retval = 0; + goto exit; +#endif + + if (tcm_hcd->host_download_mode) { + retval = syna_tcm_wait_hdl(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to wait for completion of host download\n"); + goto exit; + } + } + + tcm_hcd->update_watchdog(tcm_hcd, true); + +#ifdef RESET_ON_RESUME + msleep(RESET_ON_RESUME_DELAY_MS); + + goto do_reset; +#endif + + if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + LOGN(tcm_hcd->pdev->dev.parent, + "Application firmware not running\n"); + goto do_reset; + } + + retval = tcm_hcd->sleep(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to exit deep sleep\n"); +#ifdef RESET_IF_RESUME_FAILED + goto try_resume_again; +#else + goto exit; +#endif + } + + retval = syna_tcm_rezero(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to rezero\n"); +#ifdef RESET_IF_RESUME_FAILED + goto try_resume_again; +#else + goto exit; +#endif + } + + goto mod_resume; + +do_reset: + retval = tcm_hcd->reset(tcm_hcd, false, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + goto exit; + } + + if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + LOGN(tcm_hcd->pdev->dev.parent, + "Application firmware not running\n"); + retval = 0; + goto exit; + } + +mod_resume: + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->resume)) + mod_handler->mod_cb->resume(tcm_hcd); + } + } + + mutex_unlock(&mod_pool.mutex); + + retval = 0; + +exit: + tcm_hcd->in_suspend = false; + + return retval; + +#ifdef RESET_IF_RESUME_FAILED +try_resume_again: + LOGN(tcm_hcd->pdev->dev.parent, + "Resume failed, reset IC and try again\n"); + tcm_hcd->reset(tcm_hcd, true, true); + queue_work(system_wq, &tcm_hcd->resume_work); + return 0; +#endif +} + +static int syna_tcm_suspend(struct device *dev) +{ + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_hcd *tcm_hcd = dev_get_drvdata(dev); + + if (tcm_hcd->in_suspend) + return 0; + +#ifdef PM_SKIP_SLEEP + LOGI(tcm_hcd->pdev->dev.parent, "skip sleep in suspend\n"); + goto suspend_disable_irq; +#endif + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->suspend)) + mod_handler->mod_cb->suspend(tcm_hcd); + } + } + + mutex_unlock(&mod_pool.mutex); + +#ifndef WAKEUP_GESTURE +suspend_disable_irq: + tcm_hcd->enable_irq(tcm_hcd, false, true); +#endif + + tcm_hcd->in_suspend = true; + + return 0; +} +#endif + +#ifdef CONFIG_FB +static int syna_tcm_early_suspend(struct device *dev) +{ +#ifndef WAKEUP_GESTURE + int retval; +#endif + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_hcd *tcm_hcd = dev_get_drvdata(dev); + + if (tcm_hcd->in_suspend) + return 0; + + tcm_hcd->update_watchdog(tcm_hcd, false); + + if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + LOGN(tcm_hcd->pdev->dev.parent, + "Application firmware not running\n"); + return 0; + } + +#ifdef PM_SKIP_SLEEP + LOGI(tcm_hcd->pdev->dev.parent, "skip sleep in early resume\n"); + goto early_suspend_disable_irq; +#endif + +#ifndef WAKEUP_GESTURE + retval = tcm_hcd->sleep(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enter deep sleep\n"); + return retval; + } +#endif + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->early_suspend)) + mod_handler->mod_cb->early_suspend(tcm_hcd); + } + } + + mutex_unlock(&mod_pool.mutex); + +#ifndef WAKEUP_GESTURE +early_suspend_disable_irq: + tcm_hcd->enable_irq(tcm_hcd, false, true); +#endif + + return 0; +} + +static void syna_tcm_queued_resume(struct work_struct *w) +{ + struct syna_tcm_hcd *tcm_hcd = + container_of(w, struct syna_tcm_hcd, resume_work); + syna_tcm_resume(&(tcm_hcd->pdev->dev)); +} + +static inline int syna_tcm_display_off(struct device *dev) +{ + struct syna_tcm_hcd *tcm_hcd = dev_get_drvdata(dev); + cancel_work_sync(&tcm_hcd->resume_work); + return syna_tcm_suspend(dev); +} + +static inline int syna_tcm_display_early_off(struct device *dev) +{ + struct syna_tcm_hcd *tcm_hcd = dev_get_drvdata(dev); + cancel_work_sync(&tcm_hcd->resume_work); + return syna_tcm_early_suspend(dev); +} + +static inline int syna_tcm_display_on(struct device *dev) +{ + struct syna_tcm_hcd *tcm_hcd = dev_get_drvdata(dev); + pr_debug("queue resume\n"); + queue_work(system_wq, &tcm_hcd->resume_work); + return 0; +} + +static int syna_tcm_fb_notifier_cb(struct notifier_block *nb, + unsigned long action, void *data) +{ + int retval; + int *transition; + struct fb_event *evdata = data; + struct syna_tcm_hcd *tcm_hcd = + container_of(nb, struct syna_tcm_hcd, fb_notifier); + + retval = 0; + + if (evdata && evdata->data && tcm_hcd) { + transition = evdata->data; + if (action == FB_EARLY_EVENT_BLANK && + *transition == FB_BLANK_POWERDOWN) + retval = syna_tcm_display_early_off(&tcm_hcd->pdev->dev); + else if (action == FB_EVENT_BLANK) { + if (*transition == FB_BLANK_POWERDOWN) { + retval = syna_tcm_display_off(&tcm_hcd->pdev->dev); + tcm_hcd->fb_ready = 0; + } else if (*transition == FB_BLANK_UNBLANK) { + retval = syna_tcm_display_on(&tcm_hcd->pdev->dev); + tcm_hcd->fb_ready++; + } + } + } + + return 0; +} +#endif + +static int syna_tcm_probe(struct platform_device *pdev) +{ + int retval; + int idx; + struct syna_tcm_hcd *tcm_hcd; + const struct syna_tcm_board_data *bdata; + const struct syna_tcm_hw_interface *hw_if; + + hw_if = pdev->dev.platform_data; + if (!hw_if) { + LOGE(&pdev->dev, + "Hardware interface not found\n"); + return -ENODEV; + } + + bdata = hw_if->bdata; + if (!bdata) { + LOGE(&pdev->dev, + "Board data not found\n"); + return -ENODEV; + } + + tcm_hcd = kzalloc(sizeof(*tcm_hcd), GFP_KERNEL); + if (!tcm_hcd) { + LOGE(&pdev->dev, + "Failed to allocate memory for tcm_hcd\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, tcm_hcd); + + tcm_hcd->pdev = pdev; + tcm_hcd->hw_if = hw_if; + tcm_hcd->reset = syna_tcm_reset; + tcm_hcd->sleep = syna_tcm_sleep; + tcm_hcd->identify = syna_tcm_identify; + tcm_hcd->enable_irq = syna_tcm_enable_irq; + tcm_hcd->switch_mode = syna_tcm_switch_mode; + tcm_hcd->read_message = syna_tcm_read_message; + tcm_hcd->write_message = syna_tcm_write_message; + tcm_hcd->get_dynamic_config = syna_tcm_get_dynamic_config; + tcm_hcd->set_dynamic_config = syna_tcm_set_dynamic_config; + tcm_hcd->get_data_location = syna_tcm_get_data_location; + + tcm_hcd->rd_chunk_size = RD_CHUNK_SIZE; + tcm_hcd->wr_chunk_size = WR_CHUNK_SIZE; + +#ifdef PREDICTIVE_READING + tcm_hcd->read_length = MIN_READ_LENGTH; +#else + tcm_hcd->read_length = MESSAGE_HEADER_SIZE; +#endif + + tcm_hcd->watchdog.run = RUN_WATCHDOG; + tcm_hcd->update_watchdog = syna_tcm_update_watchdog; + + if (bdata->irq_gpio >= 0) + tcm_hcd->irq = gpio_to_irq(bdata->irq_gpio); + else + tcm_hcd->irq = bdata->irq_gpio; + + mutex_init(&tcm_hcd->extif_mutex); + mutex_init(&tcm_hcd->reset_mutex); + mutex_init(&tcm_hcd->irq_en_mutex); + mutex_init(&tcm_hcd->io_ctrl_mutex); + mutex_init(&tcm_hcd->rw_ctrl_mutex); + mutex_init(&tcm_hcd->command_mutex); + mutex_init(&tcm_hcd->identify_mutex); + + INIT_BUFFER(tcm_hcd->in, false); + INIT_BUFFER(tcm_hcd->out, false); + INIT_BUFFER(tcm_hcd->resp, true); + INIT_BUFFER(tcm_hcd->temp, false); + INIT_BUFFER(tcm_hcd->config, false); + INIT_BUFFER(tcm_hcd->report.buffer, true); + + LOCK_BUFFER(tcm_hcd->in); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->in, + tcm_hcd->read_length + 1); + if (retval < 0) { + LOGE(&pdev->dev, + "Failed to allocate memory for tcm_hcd->in.buf\n"); + UNLOCK_BUFFER(tcm_hcd->in); + goto err_alloc_mem; + } + + UNLOCK_BUFFER(tcm_hcd->in); + + atomic_set(&tcm_hcd->command_status, CMD_IDLE); + + atomic_set(&tcm_hcd->helper.task, HELP_NONE); + + device_init_wakeup(&pdev->dev, 1); + + init_waitqueue_head(&tcm_hcd->hdl_wq); + + if (!mod_pool.initialized) { + mutex_init(&mod_pool.mutex); + INIT_LIST_HEAD(&mod_pool.list); + mod_pool.initialized = true; + } + + retval = syna_tcm_get_regulator(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get regulators\n"); + goto err_get_regulator; + } + + retval = syna_tcm_enable_regulator(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable regulators\n"); + goto err_enable_regulator; + } + + retval = syna_tcm_config_gpio(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to configure GPIO's\n"); + goto err_config_gpio; + } + + sysfs_dir = kobject_create_and_add(PLATFORM_DRIVER_NAME, + &pdev->dev.kobj); + if (!sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs directory\n"); + retval = -EINVAL; + goto err_sysfs_create_dir; + } + + tcm_hcd->sysfs_dir = sysfs_dir; + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + retval = sysfs_create_file(tcm_hcd->sysfs_dir, + &(*attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs file\n"); + goto err_sysfs_create_file; + } + } + + tcm_hcd->dynamnic_config_sysfs_dir = + kobject_create_and_add(DYNAMIC_CONFIG_SYSFS_DIR_NAME, + tcm_hcd->sysfs_dir); + if (!tcm_hcd->dynamnic_config_sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create dynamic config sysfs directory\n"); + retval = -EINVAL; + goto err_sysfs_create_dynamic_config_dir; + } + + for (idx = 0; idx < ARRAY_SIZE(dynamic_config_attrs); idx++) { + retval = sysfs_create_file(tcm_hcd->dynamnic_config_sysfs_dir, + &(*dynamic_config_attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create dynamic config sysfs file\n"); + goto err_sysfs_create_dynamic_config_file; + } + } + + INIT_WORK(&tcm_hcd->resume_work, syna_tcm_queued_resume); +#ifdef CONFIG_FB + tcm_hcd->fb_notifier.notifier_call = syna_tcm_fb_notifier_cb; + retval = fb_register_client(&tcm_hcd->fb_notifier); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to register FB notifier client\n"); + } +#endif + + tcm_hcd->notifier_thread = kthread_run(syna_tcm_report_notifier, + tcm_hcd, "syna_tcm_report_notifier"); + if (IS_ERR(tcm_hcd->notifier_thread)) { + retval = PTR_ERR(tcm_hcd->notifier_thread); + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create and run tcm_hcd->notifier_thread\n"); + goto err_create_run_kthread; + } + + tcm_hcd->helper.workqueue = + create_singlethread_workqueue("syna_tcm_helper"); + INIT_WORK(&tcm_hcd->helper.work, syna_tcm_helper_work); + + tcm_hcd->watchdog.workqueue = + create_singlethread_workqueue("syna_tcm_watchdog"); + INIT_DELAYED_WORK(&tcm_hcd->watchdog.work, syna_tcm_watchdog_work); + + tcm_hcd->polling_workqueue = + create_singlethread_workqueue("syna_tcm_polling"); + INIT_DELAYED_WORK(&tcm_hcd->polling_work, syna_tcm_polling_work); + + retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable interrupt\n"); + goto err_enable_irq; + } + + retval = tcm_hcd->reset(tcm_hcd, false, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + tcm_hcd->init_okay = false; + tcm_hcd->watchdog.run = false; + tcm_hcd->update_watchdog(tcm_hcd, false); + tcm_hcd->enable_irq(tcm_hcd, false, false); +#ifndef KEEP_DRIVER_ON_ERROR + goto err_reset; +#endif + } else { + tcm_hcd->init_okay = true; + tcm_hcd->update_watchdog(tcm_hcd, true); + } + + syna_tcm_sysfs_touchscreen(tcm_hcd, true); + mod_pool.workqueue = + create_singlethread_workqueue("syna_tcm_module"); + INIT_WORK(&mod_pool.work, syna_tcm_module_work); + mod_pool.tcm_hcd = tcm_hcd; + mod_pool.queue_work = true; + queue_work(mod_pool.workqueue, &mod_pool.work); + + return 0; + +#ifndef KEEP_DRIVER_ON_ERROR +err_reset: +#endif +err_enable_irq: + cancel_delayed_work_sync(&tcm_hcd->polling_work); + flush_workqueue(tcm_hcd->polling_workqueue); + destroy_workqueue(tcm_hcd->polling_workqueue); + + cancel_delayed_work_sync(&tcm_hcd->watchdog.work); + flush_workqueue(tcm_hcd->watchdog.workqueue); + destroy_workqueue(tcm_hcd->watchdog.workqueue); + + kthread_stop(tcm_hcd->notifier_thread); + +err_create_run_kthread: +#ifdef CONFIG_FB + fb_unregister_client(&tcm_hcd->fb_notifier); +#endif + +err_sysfs_create_dynamic_config_file: + for (idx--; idx >= 0; idx--) { + sysfs_remove_file(tcm_hcd->dynamnic_config_sysfs_dir, + &(*dynamic_config_attrs[idx]).attr); + } + + kobject_put(tcm_hcd->dynamnic_config_sysfs_dir); + + idx = ARRAY_SIZE(attrs); + +err_sysfs_create_dynamic_config_dir: +err_sysfs_create_file: + for (idx--; idx >= 0; idx--) + sysfs_remove_file(tcm_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(tcm_hcd->sysfs_dir); + +err_sysfs_create_dir: + if (bdata->irq_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->irq_gpio, false, 0, 0); + + if (bdata->power_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->power_gpio, false, 0, 0); + + if (bdata->reset_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->reset_gpio, false, 0, 0); + +err_config_gpio: + syna_tcm_enable_regulator(tcm_hcd, false); + +err_enable_regulator: + syna_tcm_get_regulator(tcm_hcd, false); + +err_get_regulator: + device_init_wakeup(&pdev->dev, 0); + +err_alloc_mem: + RELEASE_BUFFER(tcm_hcd->report.buffer); + RELEASE_BUFFER(tcm_hcd->config); + RELEASE_BUFFER(tcm_hcd->temp); + RELEASE_BUFFER(tcm_hcd->resp); + RELEASE_BUFFER(tcm_hcd->out); + RELEASE_BUFFER(tcm_hcd->in); + + kfree(tcm_hcd); + + return retval; +} + +static int syna_tcm_remove(struct platform_device *pdev) +{ + int idx; + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_hcd *tcm_hcd = platform_get_drvdata(pdev); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (mod_handler->mod_cb->remove) + mod_handler->mod_cb->remove(tcm_hcd); + list_del(&mod_handler->link); + kfree(mod_handler); + } + } + + mod_pool.queue_work = false; + cancel_work_sync(&mod_pool.work); + flush_workqueue(mod_pool.workqueue); + destroy_workqueue(mod_pool.workqueue); + + mutex_unlock(&mod_pool.mutex); + + if (tcm_hcd->irq_enabled && bdata->irq_gpio >= 0) { + disable_irq(tcm_hcd->irq); + free_irq(tcm_hcd->irq, tcm_hcd); + } + + cancel_delayed_work_sync(&tcm_hcd->polling_work); + flush_workqueue(tcm_hcd->polling_workqueue); + destroy_workqueue(tcm_hcd->polling_workqueue); + + cancel_delayed_work_sync(&tcm_hcd->watchdog.work); + flush_workqueue(tcm_hcd->watchdog.workqueue); + destroy_workqueue(tcm_hcd->watchdog.workqueue); + + kthread_stop(tcm_hcd->notifier_thread); + +#ifdef CONFIG_FB + fb_unregister_client(&tcm_hcd->fb_notifier); +#endif + syna_tcm_sysfs_touchscreen(tcm_hcd, false); + + for (idx = 0; idx < ARRAY_SIZE(dynamic_config_attrs); idx++) { + sysfs_remove_file(tcm_hcd->dynamnic_config_sysfs_dir, + &(*dynamic_config_attrs[idx]).attr); + } + + kobject_put(tcm_hcd->dynamnic_config_sysfs_dir); + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) + sysfs_remove_file(tcm_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(tcm_hcd->sysfs_dir); + + if (bdata->irq_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->irq_gpio, false, 0, 0); + + if (bdata->power_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->power_gpio, false, 0, 0); + + if (bdata->reset_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->reset_gpio, false, 0, 0); + + syna_tcm_enable_regulator(tcm_hcd, false); + + syna_tcm_get_regulator(tcm_hcd, false); + + device_init_wakeup(&pdev->dev, 0); + + RELEASE_BUFFER(tcm_hcd->report.buffer); + RELEASE_BUFFER(tcm_hcd->config); + RELEASE_BUFFER(tcm_hcd->temp); + RELEASE_BUFFER(tcm_hcd->resp); + RELEASE_BUFFER(tcm_hcd->out); + RELEASE_BUFFER(tcm_hcd->in); + + kfree(tcm_hcd); + + return 0; +} + +#ifdef CONFIG_PM +static const struct dev_pm_ops syna_tcm_dev_pm_ops = { +#ifndef CONFIG_FB + .suspend = syna_tcm_suspend, + .resume = syna_tcm_resume, +#endif +}; +#endif + +static struct platform_driver syna_tcm_driver = { + .driver = { + .name = PLATFORM_DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &syna_tcm_dev_pm_ops, +#endif + }, + .probe = syna_tcm_probe, + .remove = syna_tcm_remove, +}; + +static int __init syna_tcm_module_init(void) +{ + int retval; + + retval = syna_tcm_bus_init(); + if (retval < 0) + return retval; + + return platform_driver_register(&syna_tcm_driver); +} + +static void __exit syna_tcm_module_exit(void) +{ + platform_driver_unregister(&syna_tcm_driver); + + syna_tcm_bus_exit(); + + return; +} + +module_init(syna_tcm_module_init); +module_exit(syna_tcm_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Touch Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_core.h b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_core.h new file mode 100644 index 000000000000..fe8189c5b445 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_core.h @@ -0,0 +1,661 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * + * 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. + */ + +#ifndef _SYNAPTICS_TCM_CORE_H_ +#define _SYNAPTICS_TCM_CORE_H_ + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_FB +#include +#include +#endif + +#define SYNAPTICS_TCM_ID_PRODUCT (1 << 0) +#define SYNAPTICS_TCM_ID_VERSION 0x0100 +#define SYNAPTICS_TCM_ID_SUBVERSION 0 + +#define PLATFORM_DRIVER_NAME "synaptics_tcm" + +#define TOUCH_INPUT_NAME "synaptics_tcm_touch" +#define TOUCH_INPUT_PHYS_PATH "synaptics_tcm/touch_input" + +#define RD_CHUNK_SIZE 0 /* read length limit in bytes, 0 = unlimited */ +#define WR_CHUNK_SIZE 0 /* write length limit in bytes, 0 = unlimited */ + +#define MESSAGE_HEADER_SIZE 4 +#define MESSAGE_MARKER 0xa5 +#define MESSAGE_PADDING 0x5a + +#define LOGx(func, dev, log, ...) \ + func(dev, "%s: " log, __func__, ##__VA_ARGS__) + +#define LOGy(func, dev, log, ...) \ + func(dev, "%s (line %d): " log, __func__, __LINE__, ##__VA_ARGS__) + +#define LOGD(dev, log, ...) LOGx(dev_dbg, dev, log, ##__VA_ARGS__) +#define LOGI(dev, log, ...) LOGx(dev_info, dev, log, ##__VA_ARGS__) +#define LOGN(dev, log, ...) LOGx(dev_notice, dev, log, ##__VA_ARGS__) +#define LOGW(dev, log, ...) LOGy(dev_warn, dev, log, ##__VA_ARGS__) +#define LOGE(dev, log, ...) LOGy(dev_err, dev, log, ##__VA_ARGS__) + +#define INIT_BUFFER(buffer, is_clone) \ + mutex_init(&buffer.buf_mutex); \ + buffer.clone = is_clone + +#define LOCK_BUFFER(buffer) \ + mutex_lock(&buffer.buf_mutex) + +#define UNLOCK_BUFFER(buffer) \ + mutex_unlock(&buffer.buf_mutex) + +#define RELEASE_BUFFER(buffer) \ + do { \ + if (buffer.clone == false) { \ + kfree(buffer.buf); \ + buffer.buf_size = 0; \ + buffer.data_length = 0; \ + } \ + } while (0) + +#define MAX(a, b) \ + ({__typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a > _b ? _a : _b; }) + +#define MIN(a, b) \ + ({__typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a < _b ? _a : _b; }) + +#define STR(x) #x + +#define CONCAT(a, b) a##b + +#define SHOW_PROTOTYPE(m_name, a_name) \ +static ssize_t CONCAT(m_name##_sysfs, _##a_name##_show)(struct device *dev, \ + struct device_attribute *attr, char *buf); \ +\ +static struct device_attribute dev_attr_##a_name = \ + __ATTR(a_name, S_IRUGO, \ + CONCAT(m_name##_sysfs, _##a_name##_show), \ + syna_tcm_store_error); + +#define STORE_PROTOTYPE(m_name, a_name) \ +static ssize_t CONCAT(m_name##_sysfs, _##a_name##_store)(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count); \ +\ +static struct device_attribute dev_attr_##a_name = \ + __ATTR(a_name, (S_IWUSR | S_IWGRP), \ + syna_tcm_show_error, \ + CONCAT(m_name##_sysfs, _##a_name##_store)); + +#define SHOW_STORE_PROTOTYPE(m_name, a_name) \ +static ssize_t CONCAT(m_name##_sysfs, _##a_name##_show)(struct device *dev, \ + struct device_attribute *attr, char *buf); \ +\ +static ssize_t CONCAT(m_name##_sysfs, _##a_name##_store)(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count); \ +\ +static struct device_attribute dev_attr_##a_name = \ + __ATTR(a_name, (S_IRUGO | S_IWUSR | S_IWGRP), \ + CONCAT(m_name##_sysfs, _##a_name##_show), \ + CONCAT(m_name##_sysfs, _##a_name##_store)); + +#define ATTRIFY(a_name) (&dev_attr_##a_name) + +enum module_type { + TCM_TOUCH = 0, + TCM_DEVICE = 1, + TCM_TESTING = 2, + TCM_REFLASH = 3, + TCM_RECOVERY = 4, + TCM_ZEROFLASH = 5, + TCM_DIAGNOSTICS = 6, + TCM_LAST, +}; + +enum boot_mode { + MODE_APPLICATION = 0x01, + MODE_HOST_DOWNLOAD = 0x02, + MODE_BOOTLOADER = 0x0b, + MODE_TDDI_BOOTLOADER = 0x0c, + MODE_PRODUCTION_TEST = 0x0e, +}; + +enum boot_status { + BOOT_STATUS_OK = 0x00, + BOOT_STATUS_BOOTING = 0x01, + BOOT_STATUS_APP_BAD_DISPLAY_CRC = 0xfc, + BOOT_STATUS_BAD_DISPLAY_CONFIG = 0xfd, + BOOT_STATUS_BAD_APP_FIRMWARE = 0xfe, + BOOT_STATUS_WARM_BOOT = 0xff, +}; + +enum app_status { + APP_STATUS_OK = 0x00, + APP_STATUS_BOOTING = 0x01, + APP_STATUS_UPDATING = 0x02, + APP_STATUS_BAD_APP_CONFIG = 0xff, +}; + +enum firmware_mode { + FW_MODE_BOOTLOADER = 0, + FW_MODE_APPLICATION = 1, + FW_MODE_PRODUCTION_TEST = 2, +}; + +enum dynamic_config_id { + DC_UNKNOWN = 0x00, + DC_NO_DOZE, + DC_DISABLE_NOISE_MITIGATION, + DC_INHIBIT_FREQUENCY_SHIFT, + DC_REQUESTED_FREQUENCY, + DC_DISABLE_HSYNC, + DC_REZERO_ON_EXIT_DEEP_SLEEP, + DC_CHARGER_CONNECTED, + DC_NO_BASELINE_RELAXATION, + DC_IN_WAKEUP_GESTURE_MODE, + DC_STIMULUS_FINGERS, + DC_GRIP_SUPPRESSION_ENABLED, + DC_ENABLE_THICK_GLOVE, + DC_ENABLE_GLOVE, +}; + +enum command { + CMD_NONE = 0x00, + CMD_CONTINUE_WRITE = 0x01, + CMD_IDENTIFY = 0x02, + CMD_RESET = 0x04, + CMD_ENABLE_REPORT = 0x05, + CMD_DISABLE_REPORT = 0x06, + CMD_GET_BOOT_INFO = 0x10, + CMD_ERASE_FLASH = 0x11, + CMD_WRITE_FLASH = 0x12, + CMD_READ_FLASH = 0x13, + CMD_RUN_APPLICATION_FIRMWARE = 0x14, + CMD_SPI_MASTER_WRITE_THEN_READ = 0x15, + CMD_REBOOT_TO_ROM_BOOTLOADER = 0x16, + CMD_RUN_BOOTLOADER_FIRMWARE = 0x1f, + CMD_GET_APPLICATION_INFO = 0x20, + CMD_GET_STATIC_CONFIG = 0x21, + CMD_SET_STATIC_CONFIG = 0x22, + CMD_GET_DYNAMIC_CONFIG = 0x23, + CMD_SET_DYNAMIC_CONFIG = 0x24, + CMD_GET_TOUCH_REPORT_CONFIG = 0x25, + CMD_SET_TOUCH_REPORT_CONFIG = 0x26, + CMD_REZERO = 0x27, + CMD_COMMIT_CONFIG = 0x28, + CMD_DESCRIBE_DYNAMIC_CONFIG = 0x29, + CMD_PRODUCTION_TEST = 0x2a, + CMD_SET_CONFIG_ID = 0x2b, + CMD_ENTER_DEEP_SLEEP = 0x2c, + CMD_EXIT_DEEP_SLEEP = 0x2d, + CMD_GET_TOUCH_INFO = 0x2e, + CMD_GET_DATA_LOCATION = 0x2f, + CMD_DOWNLOAD_CONFIG = 0x30, + CMD_ENTER_PRODUCTION_TEST_MODE = 0x31, + CMD_GET_FEATURES = 0x32, +}; + +enum status_code { + STATUS_IDLE = 0x00, + STATUS_OK = 0x01, + STATUS_BUSY = 0x02, + STATUS_CONTINUED_READ = 0x03, + STATUS_NOT_EXECUTED_IN_DEEP_SLEEP = 0x0b, + STATUS_RECEIVE_BUFFER_OVERFLOW = 0x0c, + STATUS_PREVIOUS_COMMAND_PENDING = 0x0d, + STATUS_NOT_IMPLEMENTED = 0x0e, + STATUS_ERROR = 0x0f, + STATUS_INVALID = 0xff, +}; + +enum report_type { + REPORT_IDENTIFY = 0x10, + REPORT_TOUCH = 0x11, + REPORT_DELTA = 0x12, + REPORT_RAW = 0x13, + REPORT_STATUS = 0x1b, + REPORT_PRINTF = 0x82, + REPORT_HDL = 0xfe, +}; + +enum command_status { + CMD_IDLE = 0, + CMD_BUSY = 1, + CMD_ERROR = -1, +}; + +enum flash_area { + BOOTLOADER = 0, + BOOT_CONFIG, + APP_FIRMWARE, + APP_CONFIG, + DISP_CONFIG, + CUSTOM_OTP, + CUSTOM_LCM, + CUSTOM_OEM, + PPDT, +}; + +enum flash_data { + LCM_DATA = 1, + OEM_DATA, + PPDT_DATA, +}; + +enum helper_task { + HELP_NONE = 0, + HELP_RUN_APPLICATION_FIRMWARE, + HELP_SEND_RESET_NOTIFICATION, +}; + +struct syna_tcm_helper { + atomic_t task; + struct work_struct work; + struct workqueue_struct *workqueue; +}; + +struct syna_tcm_watchdog { + bool run; + unsigned char count; + struct delayed_work work; + struct workqueue_struct *workqueue; +}; + +struct syna_tcm_buffer { + bool clone; + unsigned char *buf; + unsigned int buf_size; + unsigned int data_length; + struct mutex buf_mutex; +}; + +struct syna_tcm_report { + unsigned char id; + struct syna_tcm_buffer buffer; +}; + +struct syna_tcm_identification { + unsigned char version; + unsigned char mode; + unsigned char part_number[16]; + unsigned char build_id[4]; + unsigned char max_write_size[2]; +}; + +struct syna_tcm_boot_info { + unsigned char version; + unsigned char status; + unsigned char asic_id[2]; + unsigned char write_block_size_words; + unsigned char erase_page_size_words[2]; + unsigned char max_write_payload_size[2]; + unsigned char last_reset_reason; + unsigned char pc_at_time_of_last_reset[2]; + unsigned char boot_config_start_block[2]; + unsigned char boot_config_size_blocks[2]; + unsigned char display_config_start_block[4]; + unsigned char display_config_length_blocks[2]; + unsigned char backup_display_config_start_block[4]; + unsigned char backup_display_config_length_blocks[2]; + unsigned char custom_otp_start_block[2]; + unsigned char custom_otp_length_blocks[2]; +}; + +struct syna_tcm_app_info { + unsigned char version[2]; + unsigned char status[2]; + unsigned char static_config_size[2]; + unsigned char dynamic_config_size[2]; + unsigned char app_config_start_write_block[2]; + unsigned char app_config_size[2]; + unsigned char max_touch_report_config_size[2]; + unsigned char max_touch_report_payload_size[2]; + unsigned char customer_config_id[16]; + unsigned char max_x[2]; + unsigned char max_y[2]; + unsigned char max_objects[2]; + unsigned char num_of_buttons[2]; + unsigned char num_of_image_rows[2]; + unsigned char num_of_image_cols[2]; + unsigned char has_hybrid_data[2]; +}; + +struct syna_tcm_touch_info { + unsigned char image_2d_scale_factor[4]; + unsigned char image_0d_scale_factor[4]; + unsigned char hybrid_x_scale_factor[4]; + unsigned char hybrid_y_scale_factor[4]; +}; + +struct syna_tcm_message_header { + unsigned char marker; + unsigned char code; + unsigned char length[2]; +}; + +struct syna_tcm_features { + unsigned char byte_0_reserved; + unsigned char byte_1_reserved; + unsigned char dual_firmware:1; + unsigned char byte_2_reserved:7; +} __packed; + +struct syna_tcm_hcd { + pid_t isr_pid; + atomic_t command_status; + atomic_t host_downloading; + wait_queue_head_t hdl_wq; + int irq; + bool init_okay; + bool do_polling; + bool in_suspend; + bool irq_enabled; + bool host_download_mode; + char product_id_string[16]; + unsigned char fb_ready; + unsigned char command; + unsigned char async_report_id; + unsigned char status_report_code; + unsigned char response_code; + unsigned int read_length; + unsigned int payload_length; + unsigned int packrat_number; + unsigned int rd_chunk_size; + unsigned int wr_chunk_size; + unsigned int app_status; + struct platform_device *pdev; + struct regulator *pwr_reg; + struct regulator *bus_reg; + struct kobject *sysfs_dir; + struct kobject *dynamnic_config_sysfs_dir; + struct mutex extif_mutex; + struct mutex reset_mutex; + struct mutex irq_en_mutex; + struct mutex io_ctrl_mutex; + struct mutex rw_ctrl_mutex; + struct mutex command_mutex; + struct mutex identify_mutex; + struct delayed_work polling_work; + struct workqueue_struct *polling_workqueue; + struct work_struct resume_work; + struct task_struct *notifier_thread; +#ifdef CONFIG_FB + struct notifier_block fb_notifier; +#endif + struct syna_tcm_buffer in; + struct syna_tcm_buffer out; + struct syna_tcm_buffer resp; + struct syna_tcm_buffer temp; + struct syna_tcm_buffer config; + struct syna_tcm_report report; + struct syna_tcm_app_info app_info; + struct syna_tcm_boot_info boot_info; + struct syna_tcm_touch_info touch_info; + struct syna_tcm_identification id_info; + struct syna_tcm_helper helper; + struct syna_tcm_watchdog watchdog; + struct syna_tcm_features features; + const struct syna_tcm_hw_interface *hw_if; + int (*reset)(struct syna_tcm_hcd *tcm_hcd, bool hw, bool update_wd); + int (*sleep)(struct syna_tcm_hcd *tcm_hcd, bool en); + int (*identify)(struct syna_tcm_hcd *tcm_hcd, bool id); + int (*enable_irq)(struct syna_tcm_hcd *tcm_hcd, bool en, bool ns); + int (*switch_mode)(struct syna_tcm_hcd *tcm_hcd, + enum firmware_mode mode); + int (*read_message)(struct syna_tcm_hcd *tcm_hcd, + unsigned char *in_buf, unsigned int length); + int (*write_message)(struct syna_tcm_hcd *tcm_hcd, + unsigned char command, unsigned char *payload, + unsigned int length, unsigned char **resp_buf, + unsigned int *resp_buf_size, unsigned int *resp_length, + unsigned char *response_code, + unsigned int polling_delay_ms); + int (*get_dynamic_config)(struct syna_tcm_hcd *tcm_hcd, + enum dynamic_config_id id, unsigned short *value); + int (*set_dynamic_config)(struct syna_tcm_hcd *tcm_hcd, + enum dynamic_config_id id, unsigned short value); + int (*get_data_location)(struct syna_tcm_hcd *tcm_hcd, + enum flash_area area, unsigned int *addr, + unsigned int *length); + int (*read_flash_data)(enum flash_area area, bool run_app_firmware, + struct syna_tcm_buffer *output); + void (*report_touch)(void); + void (*update_watchdog)(struct syna_tcm_hcd *tcm_hcd, bool en); +}; + +struct syna_tcm_module_cb { + enum module_type type; + int (*init)(struct syna_tcm_hcd *tcm_hcd); + int (*remove)(struct syna_tcm_hcd *tcm_hcd); + int (*syncbox)(struct syna_tcm_hcd *tcm_hcd); + int (*asyncbox)(struct syna_tcm_hcd *tcm_hcd); + int (*reset)(struct syna_tcm_hcd *tcm_hcd); + int (*suspend)(struct syna_tcm_hcd *tcm_hcd); + int (*resume)(struct syna_tcm_hcd *tcm_hcd); + int (*early_suspend)(struct syna_tcm_hcd *tcm_hcd); +}; + +struct syna_tcm_module_handler { + bool insert; + bool detach; + struct list_head link; + struct syna_tcm_module_cb *mod_cb; +}; + +struct syna_tcm_module_pool { + bool initialized; + bool queue_work; + struct mutex mutex; + struct list_head list; + struct work_struct work; + struct workqueue_struct *workqueue; + struct syna_tcm_hcd *tcm_hcd; +}; + +struct syna_tcm_bus_io { + unsigned char type; + int (*rmi_read)(struct syna_tcm_hcd *tcm_hcd, unsigned short addr, + unsigned char *data, unsigned int length); + int (*rmi_write)(struct syna_tcm_hcd *tcm_hcd, unsigned short addr, + unsigned char *data, unsigned int length); + int (*read)(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, + unsigned int length); + int (*write)(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, + unsigned int length); +}; + +struct syna_tcm_hw_interface { + struct syna_tcm_board_data *bdata; + const struct syna_tcm_bus_io *bus_io; +}; + +int syna_tcm_bus_init(void); + +void syna_tcm_bus_exit(void); + +int syna_tcm_add_module(struct syna_tcm_module_cb *mod_cb, bool insert); + +static inline int syna_tcm_rmi_read(struct syna_tcm_hcd *tcm_hcd, + unsigned short addr, unsigned char *data, unsigned int length) +{ + return tcm_hcd->hw_if->bus_io->rmi_read(tcm_hcd, addr, data, length); +} + +static inline int syna_tcm_rmi_write(struct syna_tcm_hcd *tcm_hcd, + unsigned short addr, unsigned char *data, unsigned int length) +{ + return tcm_hcd->hw_if->bus_io->rmi_write(tcm_hcd, addr, data, length); +} + +static inline int syna_tcm_read(struct syna_tcm_hcd *tcm_hcd, + unsigned char *data, unsigned int length) +{ + return tcm_hcd->hw_if->bus_io->read(tcm_hcd, data, length); +} + +static inline int syna_tcm_write(struct syna_tcm_hcd *tcm_hcd, + unsigned char *data, unsigned int length) +{ + return tcm_hcd->hw_if->bus_io->write(tcm_hcd, data, length); +} + +static inline ssize_t syna_tcm_show_error(struct device *dev, + struct device_attribute *attr, char *buf) +{ + pr_err("%s: Attribute not readable\n", + __func__); + + return -EPERM; +} + +static inline ssize_t syna_tcm_store_error(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + pr_err("%s: Attribute not writable\n", + __func__); + + return -EPERM; +} + +static inline int secure_memcpy(unsigned char *dest, unsigned int dest_size, + const unsigned char *src, unsigned int src_size, + unsigned int count) +{ + if (dest == NULL || src == NULL) + return -EINVAL; + + if (count > dest_size || count > src_size) { + pr_err("%s: src_size = %d, dest_size = %d, count = %d\n", + __func__, src_size, dest_size, count); + return -EINVAL; + } + + memcpy((void *)dest, (const void *)src, count); + + return 0; +} + +static inline int syna_tcm_realloc_mem(struct syna_tcm_hcd *tcm_hcd, + struct syna_tcm_buffer *buffer, unsigned int size) +{ + int retval; + unsigned char *temp; + + if (size > buffer->buf_size) { + temp = buffer->buf; + + buffer->buf = kmalloc(size, GFP_KERNEL); + if (!(buffer->buf)) { + dev_err(tcm_hcd->pdev->dev.parent, + "%s: Failed to allocate memory\n", + __func__); + kfree(temp); + buffer->buf_size = 0; + return -ENOMEM; + } + + retval = secure_memcpy(buffer->buf, + size, + temp, + buffer->buf_size, + buffer->buf_size); + if (retval < 0) { + dev_err(tcm_hcd->pdev->dev.parent, + "%s: Failed to copy data\n", + __func__); + kfree(temp); + kfree(buffer->buf); + buffer->buf_size = 0; + return retval; + } + + kfree(temp); + buffer->buf_size = size; + } + + return 0; +} + +static inline int syna_tcm_alloc_mem(struct syna_tcm_hcd *tcm_hcd, + struct syna_tcm_buffer *buffer, unsigned int size) +{ + if (size > buffer->buf_size) { + kfree(buffer->buf); + buffer->buf = kmalloc(size, GFP_KERNEL); + if (!(buffer->buf)) { + dev_err(tcm_hcd->pdev->dev.parent, + "%s: Failed to allocate memory\n", + __func__); + dev_err(tcm_hcd->pdev->dev.parent, + "%s: Allocation size = %d\n", + __func__, size); + buffer->buf_size = 0; + buffer->data_length = 0; + return -ENOMEM; + } + buffer->buf_size = size; + } + + memset(buffer->buf, 0x00, buffer->buf_size); + buffer->data_length = 0; + + return 0; +} + +static inline unsigned int le2_to_uint(const unsigned char *src) +{ + return (unsigned int)src[0] + + (unsigned int)src[1] * 0x100; +} + +static inline unsigned int le4_to_uint(const unsigned char *src) +{ + return (unsigned int)src[0] + + (unsigned int)src[1] * 0x100 + + (unsigned int)src[2] * 0x10000 + + (unsigned int)src[3] * 0x1000000; +} + +static inline unsigned int ceil_div(unsigned int dividend, unsigned divisor) +{ + return (dividend + divisor - 1) / divisor; +} + +#endif diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_device.c b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_device.c new file mode 100644 index 000000000000..6f0cecd1fe62 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_device.c @@ -0,0 +1,708 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * + * 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. + */ + +#include +#include +#include +#include "synaptics_tcm_core.h" + +#define CHAR_DEVICE_NAME "tcm" + +#define CONCURRENT true + +#define DEVICE_IOC_MAGIC 's' +#define DEVICE_IOC_RESET _IO(DEVICE_IOC_MAGIC, 0) /* 0x00007300 */ +#define DEVICE_IOC_IRQ _IOW(DEVICE_IOC_MAGIC, 1, int) /* 0x40047301 */ +#define DEVICE_IOC_RAW _IOW(DEVICE_IOC_MAGIC, 2, int) /* 0x40047302 */ +#define DEVICE_IOC_CONCURRENT _IOW(DEVICE_IOC_MAGIC, 3, int) /* 0x40047303 */ + +struct device_hcd { + dev_t dev_num; + bool raw_mode; + bool concurrent; + unsigned int ref_count; + struct cdev char_dev; + struct class *class; + struct device *device; + struct syna_tcm_buffer out; + struct syna_tcm_buffer resp; + struct syna_tcm_buffer report; + struct syna_tcm_hcd *tcm_hcd; +}; + +DECLARE_COMPLETION(device_remove_complete); + +static struct device_hcd *device_hcd; + +static int rmidev_major_num; + +static void device_capture_touch_report(unsigned int count) +{ + int retval; + unsigned char id; + unsigned int idx; + unsigned int size; + unsigned char *data; + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + static bool report; + static unsigned int offset; + static unsigned int remaining_size; + + if (count < 2) + return; + + data = &device_hcd->resp.buf[0]; + + if (data[0] != MESSAGE_MARKER) + return; + + id = data[1]; + + size = 0; + + LOCK_BUFFER(device_hcd->report); + + switch (id) { + case REPORT_TOUCH: + if (count >= 4) { + remaining_size = le2_to_uint(&data[2]); + } else { + report = false; + goto exit; + } + retval = syna_tcm_alloc_mem(tcm_hcd, + &device_hcd->report, + remaining_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for device_hcd->report.buf\n"); + report = false; + goto exit; + } + idx = 4; + size = count - idx; + offset = 0; + report = true; + break; + case STATUS_CONTINUED_READ: + if (report == false) + goto exit; + if (count >= 2) { + idx = 2; + size = count - idx; + } + break; + default: + goto exit; + } + + if (size) { + size = MIN(size, remaining_size); + retval = secure_memcpy(&device_hcd->report.buf[offset], + device_hcd->report.buf_size - offset, + &data[idx], + count - idx, + size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy touch report data\n"); + report = false; + goto exit; + } else { + offset += size; + remaining_size -= size; + device_hcd->report.data_length += size; + } + } + + if (remaining_size) + goto exit; + + LOCK_BUFFER(tcm_hcd->report.buffer); + + tcm_hcd->report.buffer.buf = device_hcd->report.buf; + tcm_hcd->report.buffer.buf_size = device_hcd->report.buf_size; + tcm_hcd->report.buffer.data_length = device_hcd->report.data_length; + + tcm_hcd->report_touch(); + + UNLOCK_BUFFER(tcm_hcd->report.buffer); + + report = false; + +exit: + UNLOCK_BUFFER(device_hcd->report); + + return; +} + +static int device_capture_touch_report_config(unsigned int count) +{ + int retval; + unsigned int size; + unsigned int buf_size; + unsigned char *data; + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + if (device_hcd->raw_mode) { + if (count < 3) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid write data\n"); + return -EINVAL; + } + + size = le2_to_uint(&device_hcd->out.buf[1]); + + if (count - 3 < size) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incomplete write data\n"); + return -EINVAL; + } + + if (!size) + return 0; + + data = &device_hcd->out.buf[3]; + buf_size = device_hcd->out.buf_size - 3; + } else { + size = count - 1; + + if (!size) + return 0; + + data = &device_hcd->out.buf[1]; + buf_size = device_hcd->out.buf_size - 1; + } + + LOCK_BUFFER(tcm_hcd->config); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->config, + size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for tcm_hcd->config.buf\n"); + UNLOCK_BUFFER(tcm_hcd->config); + return retval; + } + + retval = secure_memcpy(tcm_hcd->config.buf, + tcm_hcd->config.buf_size, + data, + buf_size, + size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy touch report config data\n"); + UNLOCK_BUFFER(tcm_hcd->config); + return retval; + } + + tcm_hcd->config.data_length = size; + + UNLOCK_BUFFER(tcm_hcd->config); + + return 0; +} + +#ifdef HAVE_UNLOCKED_IOCTL +static long device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#else +static int device_ioctl(struct inode *inp, struct file *filp, unsigned int cmd, + unsigned long arg) +#endif +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = 0; + + switch (cmd) { + case DEVICE_IOC_RESET: + retval = tcm_hcd->reset(tcm_hcd, false, true); + break; + case DEVICE_IOC_IRQ: + if (arg == 0) + retval = tcm_hcd->enable_irq(tcm_hcd, false, false); + else if (arg == 1) + retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL); + break; + case DEVICE_IOC_RAW: + if (arg == 0) + device_hcd->raw_mode = false; + else if (arg == 1) + device_hcd->raw_mode = true; + break; + case DEVICE_IOC_CONCURRENT: + if (arg == 0) + device_hcd->concurrent = false; + else if (arg == 1) + device_hcd->concurrent = true; + break; + default: + retval = -ENOTTY; + break; + } + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static loff_t device_llseek(struct file *filp, loff_t off, int whence) +{ + return -EINVAL; +} + +static ssize_t device_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + if (count == 0) + return 0; + + mutex_lock(&tcm_hcd->extif_mutex); + + LOCK_BUFFER(device_hcd->resp); + + if (device_hcd->raw_mode) { + retval = syna_tcm_alloc_mem(tcm_hcd, + &device_hcd->resp, + count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for device_hcd->resp.buf\n"); + UNLOCK_BUFFER(device_hcd->resp); + goto exit; + } + + retval = tcm_hcd->read_message(tcm_hcd, + device_hcd->resp.buf, + count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read message\n"); + UNLOCK_BUFFER(device_hcd->resp); + goto exit; + } + } else { + if (count != device_hcd->resp.data_length) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid length information\n"); + UNLOCK_BUFFER(device_hcd->resp); + retval = -EINVAL; + goto exit; + } + } + + if (copy_to_user(buf, device_hcd->resp.buf, count)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy data to user space\n"); + UNLOCK_BUFFER(device_hcd->resp); + retval = -EINVAL; + goto exit; + } + + if (!device_hcd->concurrent) + goto skip_concurrent; + + if (tcm_hcd->report_touch == NULL) { + LOGE(tcm_hcd->pdev->dev.parent, + "Unable to report touch\n"); + device_hcd->concurrent = false; + } + + if (device_hcd->raw_mode) + device_capture_touch_report(count); + +skip_concurrent: + UNLOCK_BUFFER(device_hcd->resp); + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t device_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + if (count == 0) + return 0; + + mutex_lock(&tcm_hcd->extif_mutex); + + LOCK_BUFFER(device_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &device_hcd->out, + count == 1 ? count + 1 : count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for device_hcd->out.buf\n"); + UNLOCK_BUFFER(device_hcd->out); + goto exit; + } + + if (copy_from_user(device_hcd->out.buf, buf, count)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy data from user space\n"); + UNLOCK_BUFFER(device_hcd->out); + retval = -EINVAL; + goto exit; + } + + LOCK_BUFFER(device_hcd->resp); + + if (device_hcd->raw_mode) { + retval = tcm_hcd->write_message(tcm_hcd, + device_hcd->out.buf[0], + &device_hcd->out.buf[1], + count - 1, + NULL, + NULL, + NULL, + NULL, + 0); + } else { + mutex_lock(&tcm_hcd->reset_mutex); + retval = tcm_hcd->write_message(tcm_hcd, + device_hcd->out.buf[0], + &device_hcd->out.buf[1], + count - 1, + &device_hcd->resp.buf, + &device_hcd->resp.buf_size, + &device_hcd->resp.data_length, + NULL, + 0); + mutex_unlock(&tcm_hcd->reset_mutex); + } + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command 0x%02x\n", + device_hcd->out.buf[0]); + UNLOCK_BUFFER(device_hcd->resp); + UNLOCK_BUFFER(device_hcd->out); + goto exit; + } + + if (count && device_hcd->out.buf[0] == CMD_SET_TOUCH_REPORT_CONFIG) { + retval = device_capture_touch_report_config(count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to capture touch report config\n"); + } + } + + UNLOCK_BUFFER(device_hcd->out); + + if (device_hcd->raw_mode) + retval = count; + else + retval = device_hcd->resp.data_length; + + UNLOCK_BUFFER(device_hcd->resp); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static int device_open(struct inode *inp, struct file *filp) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (device_hcd->ref_count < 1) { + device_hcd->ref_count++; + retval = 0; + } else { + retval = -EACCES; + } + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static int device_release(struct inode *inp, struct file *filp) +{ + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (device_hcd->ref_count) + device_hcd->ref_count--; + + mutex_unlock(&tcm_hcd->extif_mutex); + + return 0; +} + +static char *device_devnode(struct device *dev, umode_t *mode) +{ + if (!mode) + return NULL; + + *mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + + return kasprintf(GFP_KERNEL, "%s/%s", PLATFORM_DRIVER_NAME, + dev_name(dev)); +} + +static int device_create_class(void) +{ + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + if (device_hcd->class != NULL) + return 0; + + device_hcd->class = class_create(THIS_MODULE, PLATFORM_DRIVER_NAME); + + if (IS_ERR(device_hcd->class)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create class\n"); + return -ENODEV; + } + + device_hcd->class->devnode = device_devnode; + + return 0; +} + +static const struct file_operations device_fops = { + .owner = THIS_MODULE, +#ifdef HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = device_ioctl, +#ifdef HAVE_COMPAT_IOCTL + .compat_ioctl = device_ioctl, +#endif +#else + .ioctl = device_ioctl, +#endif + .llseek = device_llseek, + .read = device_read, + .write = device_write, + .open = device_open, + .release = device_release, +}; + +static int device_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + dev_t dev_num; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + device_hcd = kzalloc(sizeof(*device_hcd), GFP_KERNEL); + if (!device_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for device_hcd\n"); + return -ENOMEM; + } + + device_hcd->tcm_hcd = tcm_hcd; + + device_hcd->concurrent = CONCURRENT; + + INIT_BUFFER(device_hcd->out, false); + INIT_BUFFER(device_hcd->resp, false); + INIT_BUFFER(device_hcd->report, false); + + if (rmidev_major_num) { + dev_num = MKDEV(rmidev_major_num, 0); + retval = register_chrdev_region(dev_num, 1, + PLATFORM_DRIVER_NAME); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to register char device\n"); + goto err_register_chrdev_region; + } + } else { + retval = alloc_chrdev_region(&dev_num, 0, 1, + PLATFORM_DRIVER_NAME); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate char device\n"); + goto err_alloc_chrdev_region; + } + + rmidev_major_num = MAJOR(dev_num); + } + + device_hcd->dev_num = dev_num; + + cdev_init(&device_hcd->char_dev, &device_fops); + + retval = cdev_add(&device_hcd->char_dev, dev_num, 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add char device\n"); + goto err_add_chardev; + } + + retval = device_create_class(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create class\n"); + goto err_create_class; + } + + device_hcd->device = device_create(device_hcd->class, NULL, + device_hcd->dev_num, NULL, CHAR_DEVICE_NAME"%d", + MINOR(device_hcd->dev_num)); + if (IS_ERR(device_hcd->device)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create device\n"); + retval = -ENODEV; + goto err_create_device; + } + + if (bdata->irq_gpio >= 0) { + retval = gpio_export(bdata->irq_gpio, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to export GPIO\n"); + } else { + retval = gpio_export_link(&tcm_hcd->pdev->dev, + "attn", bdata->irq_gpio); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to export GPIO link\n"); + } + } + } + + return 0; + +err_create_device: + class_destroy(device_hcd->class); + +err_create_class: + cdev_del(&device_hcd->char_dev); + +err_add_chardev: + unregister_chrdev_region(dev_num, 1); + +err_alloc_chrdev_region: +err_register_chrdev_region: + RELEASE_BUFFER(device_hcd->report); + RELEASE_BUFFER(device_hcd->resp); + RELEASE_BUFFER(device_hcd->out); + + kfree(device_hcd); + device_hcd = NULL; + + return retval; +} + +static int device_remove(struct syna_tcm_hcd *tcm_hcd) +{ + if (!device_hcd) + goto exit; + + device_destroy(device_hcd->class, device_hcd->dev_num); + + class_destroy(device_hcd->class); + + cdev_del(&device_hcd->char_dev); + + unregister_chrdev_region(device_hcd->dev_num, 1); + + RELEASE_BUFFER(device_hcd->report); + RELEASE_BUFFER(device_hcd->resp); + RELEASE_BUFFER(device_hcd->out); + + kfree(device_hcd); + device_hcd = NULL; + +exit: + complete(&device_remove_complete); + + return 0; +} + +static int device_reset(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!device_hcd) { + retval = device_init(tcm_hcd); + return retval; + } + + return 0; +} + +static struct syna_tcm_module_cb device_module = { + .type = TCM_DEVICE, + .init = device_init, + .remove = device_remove, + .syncbox = NULL, + .asyncbox = NULL, + .reset = device_reset, + .suspend = NULL, + .resume = NULL, + .early_suspend = NULL, +}; + +static int __init device_module_init(void) +{ + return syna_tcm_add_module(&device_module, true); +} + +static void __exit device_module_exit(void) +{ + syna_tcm_add_module(&device_module, false); + + wait_for_completion(&device_remove_complete); + + return; +} + +module_init(device_module_init); +module_exit(device_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Device Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_diagnostics.c b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_diagnostics.c new file mode 100644 index 000000000000..822429431d07 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_diagnostics.c @@ -0,0 +1,567 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * + * 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. + */ + +#include "synaptics_tcm_core.h" + +#define SYSFS_DIR_NAME "diagnostics" + +enum pingpong_state { + PING = 0, + PONG = 1, +}; + +struct diag_hcd { + pid_t pid; + unsigned char report_type; + enum pingpong_state state; + struct kobject *sysfs_dir; + struct siginfo sigio; + struct task_struct *task; + struct syna_tcm_buffer ping; + struct syna_tcm_buffer pong; + struct syna_tcm_hcd *tcm_hcd; +}; + +DECLARE_COMPLETION(diag_remove_complete); + +static struct diag_hcd *diag_hcd; + +STORE_PROTOTYPE(diag, pid) +SHOW_PROTOTYPE(diag, size) +STORE_PROTOTYPE(diag, type) +SHOW_PROTOTYPE(diag, rows) +SHOW_PROTOTYPE(diag, cols) +SHOW_PROTOTYPE(diag, hybrid) +SHOW_PROTOTYPE(diag, buttons) + +static struct device_attribute *attrs[] = { + ATTRIFY(pid), + ATTRIFY(size), + ATTRIFY(type), + ATTRIFY(rows), + ATTRIFY(cols), + ATTRIFY(hybrid), + ATTRIFY(buttons), +}; + +static ssize_t diag_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static struct bin_attribute bin_attr = { + .attr = { + .name = "data", + .mode = S_IRUGO, + }, + .size = 0, + .read = diag_sysfs_data_show, +}; + +static ssize_t diag_sysfs_pid_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + diag_hcd->pid = input; + + if (diag_hcd->pid) { + diag_hcd->task = pid_task(find_vpid(diag_hcd->pid), + PIDTYPE_PID); + if (!diag_hcd->task) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to locate task\n"); + retval = -EINVAL; + goto exit; + } + } + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t diag_sysfs_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (diag_hcd->state == PING) { + LOCK_BUFFER(diag_hcd->ping); + + retval = snprintf(buf, PAGE_SIZE, + "%u\n", + diag_hcd->ping.data_length); + + UNLOCK_BUFFER(diag_hcd->ping); + } else { + LOCK_BUFFER(diag_hcd->pong); + + retval = snprintf(buf, PAGE_SIZE, + "%u\n", + diag_hcd->pong.data_length); + + UNLOCK_BUFFER(diag_hcd->pong); + } + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t diag_sysfs_type_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + diag_hcd->report_type = (unsigned char)input; + + mutex_unlock(&tcm_hcd->extif_mutex); + + return count; +} + +static ssize_t diag_sysfs_rows_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned int rows; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + retval = -ENODEV; + goto exit; + } + + app_info = &tcm_hcd->app_info; + rows = le2_to_uint(app_info->num_of_image_rows); + + retval = snprintf(buf, PAGE_SIZE, "%u\n", rows); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t diag_sysfs_cols_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned int cols; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + retval = -ENODEV; + goto exit; + } + + app_info = &tcm_hcd->app_info; + cols = le2_to_uint(app_info->num_of_image_cols); + + retval = snprintf(buf, PAGE_SIZE, "%u\n", cols); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t diag_sysfs_hybrid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned int hybrid; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + retval = -ENODEV; + goto exit; + } + + app_info = &tcm_hcd->app_info; + hybrid = le2_to_uint(app_info->has_hybrid_data); + + retval = snprintf(buf, PAGE_SIZE, "%u\n", hybrid); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t diag_sysfs_buttons_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned int buttons; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + retval = -ENODEV; + goto exit; + } + + app_info = &tcm_hcd->app_info; + buttons = le2_to_uint(app_info->num_of_buttons); + + retval = snprintf(buf, PAGE_SIZE, "%u\n", buttons); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t diag_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int readlen; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = 0; + + if (diag_hcd->state == PING) { + LOCK_BUFFER(diag_hcd->ping); + + if (diag_hcd->ping.data_length == 0) { + readlen = 0; + goto exit; + } + + readlen = MIN(count, diag_hcd->ping.data_length - pos); + + if (diag_hcd->ping.data_length) { + retval = secure_memcpy(buf, + count, + &diag_hcd->ping.buf[pos], + diag_hcd->ping.buf_size - pos, + readlen); + } + + UNLOCK_BUFFER(diag_hcd->ping); + } else { + LOCK_BUFFER(diag_hcd->pong); + + if (diag_hcd->pong.data_length == 0) { + readlen = 0; + goto exit; + } + + readlen = MIN(count, diag_hcd->pong.data_length - pos); + + if (diag_hcd->pong.data_length) { + retval = secure_memcpy(buf, + count, + &diag_hcd->pong.buf[pos], + diag_hcd->pong.buf_size - pos, + readlen); + } + + UNLOCK_BUFFER(diag_hcd->pong); + } + +exit: + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy report data\n"); + } else { + retval = readlen; + } + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static void diag_report(void) +{ + int retval; + static enum pingpong_state state = PING; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + if (state == PING) { + LOCK_BUFFER(diag_hcd->ping); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &diag_hcd->ping, + tcm_hcd->report.buffer.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for diag_hcd->ping.buf\n"); + UNLOCK_BUFFER(diag_hcd->ping); + return; + } + + retval = secure_memcpy(diag_hcd->ping.buf, + diag_hcd->ping.buf_size, + tcm_hcd->report.buffer.buf, + tcm_hcd->report.buffer.buf_size, + tcm_hcd->report.buffer.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy report data\n"); + UNLOCK_BUFFER(diag_hcd->ping); + return; + } + + diag_hcd->ping.data_length = tcm_hcd->report.buffer.data_length; + + UNLOCK_BUFFER(diag_hcd->ping); + + diag_hcd->state = state; + state = PONG; + } else { + LOCK_BUFFER(diag_hcd->pong); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &diag_hcd->pong, + tcm_hcd->report.buffer.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for diag_hcd->pong.buf\n"); + UNLOCK_BUFFER(diag_hcd->pong); + return; + } + + retval = secure_memcpy(diag_hcd->pong.buf, + diag_hcd->pong.buf_size, + tcm_hcd->report.buffer.buf, + tcm_hcd->report.buffer.buf_size, + tcm_hcd->report.buffer.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy report data\n"); + UNLOCK_BUFFER(diag_hcd->pong); + return; + } + + diag_hcd->pong.data_length = tcm_hcd->report.buffer.data_length; + + UNLOCK_BUFFER(diag_hcd->pong); + + diag_hcd->state = state; + state = PING; + } + + if (diag_hcd->pid) + send_sig_info(SIGIO, &diag_hcd->sigio, diag_hcd->task); + + return; +} + +static int diag_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + int idx; + + diag_hcd = kzalloc(sizeof(*diag_hcd), GFP_KERNEL); + if (!diag_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for diag_hcd\n"); + return -ENOMEM; + } + + diag_hcd->tcm_hcd = tcm_hcd; + diag_hcd->state = PING; + + INIT_BUFFER(diag_hcd->ping, false); + INIT_BUFFER(diag_hcd->pong, false); + + memset(&diag_hcd->sigio, 0x00, sizeof(diag_hcd->sigio)); + diag_hcd->sigio.si_signo = SIGIO; + diag_hcd->sigio.si_code = SI_USER; + + diag_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME, + tcm_hcd->sysfs_dir); + if (!diag_hcd->sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs directory\n"); + retval = -EINVAL; + goto err_sysfs_create_dir; + } + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + retval = sysfs_create_file(diag_hcd->sysfs_dir, + &(*attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs file\n"); + goto err_sysfs_create_file; + } + } + + retval = sysfs_create_bin_file(diag_hcd->sysfs_dir, &bin_attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs bin file\n"); + goto err_sysfs_create_bin_file; + } + + return 0; + +err_sysfs_create_bin_file: +err_sysfs_create_file: + for (idx--; idx >= 0; idx--) + sysfs_remove_file(diag_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(diag_hcd->sysfs_dir); + +err_sysfs_create_dir: + RELEASE_BUFFER(diag_hcd->pong); + RELEASE_BUFFER(diag_hcd->ping); + + kfree(diag_hcd); + diag_hcd = NULL; + + return retval; +} + +static int diag_remove(struct syna_tcm_hcd *tcm_hcd) +{ + int idx; + + if (!diag_hcd) + goto exit; + + sysfs_remove_bin_file(diag_hcd->sysfs_dir, &bin_attr); + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) + sysfs_remove_file(diag_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(diag_hcd->sysfs_dir); + + RELEASE_BUFFER(diag_hcd->pong); + RELEASE_BUFFER(diag_hcd->ping); + + kfree(diag_hcd); + diag_hcd = NULL; + +exit: + complete(&diag_remove_complete); + + return 0; +} + +static int diag_syncbox(struct syna_tcm_hcd *tcm_hcd) +{ + if (!diag_hcd) + return 0; + + if (tcm_hcd->report.id == diag_hcd->report_type) + diag_report(); + + return 0; +} + +static int diag_reset(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!diag_hcd) { + retval = diag_init(tcm_hcd); + return retval; + } + + return 0; +} + +static struct syna_tcm_module_cb diag_module = { + .type = TCM_DIAGNOSTICS, + .init = diag_init, + .remove = diag_remove, + .syncbox = diag_syncbox, + .asyncbox = NULL, + .reset = diag_reset, + .suspend = NULL, + .resume = NULL, + .early_suspend = NULL, +}; + +static int __init diag_module_init(void) +{ + return syna_tcm_add_module(&diag_module, true); +} + +static void __exit diag_module_exit(void) +{ + syna_tcm_add_module(&diag_module, false); + + wait_for_completion(&diag_remove_complete); + + return; +} + +module_init(diag_module_init); +module_exit(diag_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Diagnostics Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_i2c.c b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_i2c.c new file mode 100644 index 000000000000..45f50d444ed1 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_i2c.c @@ -0,0 +1,510 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * + * 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. + */ + +#include +#include +#include "synaptics_tcm_core.h" + +#define XFER_ATTEMPTS 10 + +static unsigned char *buf; + +static unsigned int buf_size; + +static struct syna_tcm_bus_io bus_io; + +static struct syna_tcm_hw_interface hw_if; + +static struct platform_device *syna_tcm_i2c_device; + +#ifdef CONFIG_OF +static int parse_dt(struct device *dev, struct syna_tcm_board_data *bdata) +{ + int retval; + u32 value; + struct property *prop; + struct device_node *np = dev->of_node; + const char *name; + + prop = of_find_property(np, "synaptics,irq-gpio", NULL); + if (prop && prop->length) { + bdata->irq_gpio = of_get_named_gpio_flags(np, + "synaptics,irq-gpio", 0, + (enum of_gpio_flags *)&bdata->irq_flags); + } else { + bdata->irq_gpio = -1; + } + + retval = of_property_read_u32(np, "synaptics,irq-on-state", &value); + if (retval < 0) + bdata->irq_on_state = 0; + else + bdata->irq_on_state = value; + + retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name); + if (retval < 0) + bdata->pwr_reg_name = NULL; + else + bdata->pwr_reg_name = name; + + retval = of_property_read_string(np, "synaptics,bus-reg-name", &name); + if (retval < 0) + bdata->bus_reg_name = NULL; + else + bdata->bus_reg_name = name; + + prop = of_find_property(np, "synaptics,power-gpio", NULL); + if (prop && prop->length) { + bdata->power_gpio = of_get_named_gpio_flags(np, + "synaptics,power-gpio", 0, NULL); + } else { + bdata->power_gpio = -1; + } + + prop = of_find_property(np, "synaptics,power-on-state", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-on-state", + &value); + if (retval < 0) { + LOGE(dev, + "Failed to read synaptics,power-on-state property\n"); + return retval; + } else { + bdata->power_on_state = value; + } + } else { + bdata->power_on_state = 0; + } + + prop = of_find_property(np, "synaptics,power-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-delay-ms", + &value); + if (retval < 0) { + LOGE(dev, + "Failed to read synaptics,power-delay-ms property\n"); + return retval; + } else { + bdata->power_delay_ms = value; + } + } else { + bdata->power_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-gpio", NULL); + if (prop && prop->length) { + bdata->reset_gpio = of_get_named_gpio_flags(np, + "synaptics,reset-gpio", 0, NULL); + } else { + bdata->reset_gpio = -1; + } + + prop = of_find_property(np, "synaptics,reset-on-state", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-on-state", + &value); + if (retval < 0) { + LOGE(dev, + "Failed to read synaptics,reset-on-state property\n"); + return retval; + } else { + bdata->reset_on_state = value; + } + } else { + bdata->reset_on_state = 0; + } + + prop = of_find_property(np, "synaptics,reset-active-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-active-ms", + &value); + if (retval < 0) { + LOGE(dev, + "Failed to read synaptics,reset-active-ms property\n"); + return retval; + } else { + bdata->reset_active_ms = value; + } + } else { + bdata->reset_active_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-delay-ms", + &value); + if (retval < 0) { + LOGE(dev, + "Unable to read synaptics,reset-delay-ms property\n"); + return retval; + } else { + bdata->reset_delay_ms = value; + } + } else { + bdata->reset_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,x-flip", NULL); + bdata->x_flip = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,y-flip", NULL); + bdata->y_flip = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,swap-axes", NULL); + bdata->swap_axes = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,ubl-i2c-addr", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,ubl-i2c-addr", + &value); + if (retval < 0) { + LOGE(dev, + "Unable to read synaptics,ubl-i2c-addr property\n"); + return retval; + } else { + bdata->ubl_i2c_addr = value; + } + } else { + bdata->ubl_i2c_addr = 0; + } + + return 0; +} +#endif + +static int syna_tcm_i2c_alloc_mem(struct syna_tcm_hcd *tcm_hcd, + unsigned int size) +{ + struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent); + + if (size > buf_size) { + if (buf_size) + kfree(buf); + buf = kmalloc(size, GFP_KERNEL); + if (!buf) { + LOGE(&i2c->dev, + "Failed to allocate memory for buf\n"); + buf_size = 0; + return -ENOMEM; + } + buf_size = size; + } + + return 0; +} + +static int syna_tcm_i2c_rmi_read(struct syna_tcm_hcd *tcm_hcd, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned char address; + unsigned int attempt; + struct i2c_msg msg[2]; + struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + address = (unsigned char)addr; + + msg[0].addr = bdata->ubl_i2c_addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &address; + + msg[1].addr = bdata->ubl_i2c_addr; + msg[1].flags = I2C_M_RD; + msg[1].len = length; + msg[1].buf = data; + + for (attempt = 0; attempt < XFER_ATTEMPTS; attempt++) { + if (i2c_transfer(i2c->adapter, msg, 2) == 2) { + retval = length; + goto exit; + } + LOGE(&i2c->dev, + "Transfer attempt %d failed\n", + attempt + 1); + + if (attempt + 1 == XFER_ATTEMPTS) { + retval = -EIO; + goto exit; + } + + msleep(20); + } + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_i2c_rmi_write(struct syna_tcm_hcd *tcm_hcd, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned int attempt; + unsigned int byte_count; + struct i2c_msg msg; + struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + byte_count = length + 1; + + retval = syna_tcm_i2c_alloc_mem(tcm_hcd, byte_count); + if (retval < 0) { + LOGE(&i2c->dev, + "Failed to allocate memory\n"); + goto exit; + } + + buf[0] = (unsigned char)addr; + retval = secure_memcpy(&buf[1], + buf_size - 1, + data, + length, + length); + if (retval < 0) { + LOGE(&i2c->dev, + "Failed to copy write data\n"); + goto exit; + } + + msg.addr = bdata->ubl_i2c_addr; + msg.flags = 0; + msg.len = byte_count; + msg.buf = buf; + + for (attempt = 0; attempt < XFER_ATTEMPTS; attempt++) { + if (i2c_transfer(i2c->adapter, &msg, 1) == 1) { + retval = length; + goto exit; + } + LOGE(&i2c->dev, + "Transfer attempt %d failed\n", + attempt + 1); + + if (attempt + 1 == XFER_ATTEMPTS) { + retval = -EIO; + goto exit; + } + + msleep(20); + } + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_i2c_read(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, + unsigned int length) +{ + int retval; + unsigned int attempt; + struct i2c_msg msg; + struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent); + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + msg.addr = i2c->addr; + msg.flags = I2C_M_RD; + msg.len = length; + msg.buf = data; + + for (attempt = 0; attempt < XFER_ATTEMPTS; attempt++) { + if (i2c_transfer(i2c->adapter, &msg, 1) == 1) { + retval = length; + goto exit; + } + LOGE(&i2c->dev, + "Transfer attempt %d failed\n", + attempt + 1); + + if (attempt + 1 == XFER_ATTEMPTS) { + retval = -EIO; + goto exit; + } + + msleep(20); + } + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_i2c_write(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, + unsigned int length) +{ + int retval; + unsigned int attempt; + struct i2c_msg msg; + struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent); + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + msg.addr = i2c->addr; + msg.flags = 0; + msg.len = length; + msg.buf = data; + + for (attempt = 0; attempt < XFER_ATTEMPTS; attempt++) { + if (i2c_transfer(i2c->adapter, &msg, 1) == 1) { + retval = length; + goto exit; + } + LOGE(&i2c->dev, + "Transfer attempt %d failed\n", + attempt + 1); + + if (attempt + 1 == XFER_ATTEMPTS) { + retval = -EIO; + goto exit; + } + + msleep(20); + } + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *dev_id) +{ + int retval; + + syna_tcm_i2c_device = platform_device_alloc(PLATFORM_DRIVER_NAME, 0); + if (!syna_tcm_i2c_device) { + LOGE(&i2c->dev, + "Failed to allocate platform device\n"); + return -ENOMEM; + } + +#ifdef CONFIG_OF + hw_if.bdata = devm_kzalloc(&i2c->dev, sizeof(*hw_if.bdata), GFP_KERNEL); + if (!hw_if.bdata) { + LOGE(&i2c->dev, + "Failed to allocate memory for board data\n"); + return -ENOMEM; + } + parse_dt(&i2c->dev, hw_if.bdata); +#else + hw_if.bdata = i2c->dev.platform_data; +#endif + + bus_io.type = BUS_I2C; + bus_io.read = syna_tcm_i2c_read; + bus_io.write = syna_tcm_i2c_write; + bus_io.rmi_read = syna_tcm_i2c_rmi_read; + bus_io.rmi_write = syna_tcm_i2c_rmi_write; + + hw_if.bus_io = &bus_io; + + syna_tcm_i2c_device->dev.parent = &i2c->dev; + syna_tcm_i2c_device->dev.platform_data = &hw_if; + + retval = platform_device_add(syna_tcm_i2c_device); + if (retval < 0) { + LOGE(&i2c->dev, + "Failed to add platform device\n"); + return retval; + } + + return 0; +} + +static int syna_tcm_i2c_remove(struct i2c_client *i2c) +{ + syna_tcm_i2c_device->dev.platform_data = NULL; + + platform_device_unregister(syna_tcm_i2c_device); + + return 0; +} + +static const struct i2c_device_id syna_tcm_id_table[] = { + {I2C_MODULE_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, syna_tcm_id_table); + +#ifdef CONFIG_OF +static struct of_device_id syna_tcm_of_match_table[] = { + { + .compatible = "synaptics,tcm-i2c", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, syna_tcm_of_match_table); +#else +#define syna_tcm_of_match_table NULL +#endif + +static struct i2c_driver syna_tcm_i2c_driver = { + .driver = { + .name = I2C_MODULE_NAME, + .owner = THIS_MODULE, + .of_match_table = syna_tcm_of_match_table, + }, + .probe = syna_tcm_i2c_probe, + .remove = syna_tcm_i2c_remove, + .id_table = syna_tcm_id_table, +}; + +int syna_tcm_bus_init(void) +{ + return i2c_add_driver(&syna_tcm_i2c_driver); +} +EXPORT_SYMBOL(syna_tcm_bus_init); + +void syna_tcm_bus_exit(void) +{ + kfree(buf); + + i2c_del_driver(&syna_tcm_i2c_driver); + + return; +} +EXPORT_SYMBOL(syna_tcm_bus_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM I2C Bus Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_recovery.c b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_recovery.c new file mode 100644 index 000000000000..d9c29fb27c4a --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_recovery.c @@ -0,0 +1,902 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * + * 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. + */ + +#include "synaptics_tcm_core.h" + +#define SET_UP_RECOVERY_MODE true + +#define ENABLE_SYSFS_INTERFACE true + +#define SYSFS_DIR_NAME "recovery" + +#define IHEX_BUF_SIZE (2048 * 1024) + +#define DATA_BUF_SIZE (512 * 1024) + +#define IHEX_RECORD_SIZE 14 + +#define PDT_START_ADDR 0x00e9 + +#define UBL_FN_NUMBER 0x35 + +#define F35_CHUNK_SIZE 16 + +#define F35_CHUNK_SIZE_WORDS 8 + +#define F35_ERASE_ALL_WAIT_MS 5000 + +#define F35_ERASE_ALL_POLL_MS 100 + +#define F35_DATA5_OFFSET 5 + +#define F35_CTRL3_OFFSET 18 + +#define F35_RESET_COMMAND 16 + +#define F35_ERASE_ALL_COMMAND 3 + +#define F35_WRITE_CHUNK_COMMAND 2 + +#define F35_READ_FLASH_STATUS_COMMAND 1 + +struct rmi_pdt_entry { + unsigned char query_base_addr; + unsigned char command_base_addr; + unsigned char control_base_addr; + unsigned char data_base_addr; + unsigned char intr_src_count:3; + unsigned char reserved_1:2; + unsigned char fn_version:2; + unsigned char reserved_2:1; + unsigned char fn_number; +} __packed; + +struct rmi_addr { + unsigned short query_base; + unsigned short command_base; + unsigned short control_base; + unsigned short data_base; +}; + +struct recovery_hcd { + bool set_up_recovery_mode; + unsigned char chunk_buf[F35_CHUNK_SIZE + 3]; + unsigned char out_buf[3]; + unsigned char *ihex_buf; + unsigned char *data_buf; + unsigned int ihex_size; + unsigned int ihex_records; + unsigned int data_entries; + struct kobject *sysfs_dir; + struct rmi_addr f35_addr; + struct syna_tcm_hcd *tcm_hcd; +}; + +DECLARE_COMPLETION(recovery_remove_complete); + +static struct recovery_hcd *recovery_hcd; + +static int recovery_do_recovery(void); + +STORE_PROTOTYPE(recovery, recovery) + +static struct device_attribute *attrs[] = { + ATTRIFY(recovery), +}; + +static ssize_t recovery_sysfs_ihex_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static struct bin_attribute bin_attr = { + .attr = { + .name = "ihex", + .mode = (S_IWUSR | S_IWGRP), + }, + .size = 0, + .write = recovery_sysfs_ihex_store, +}; + +static ssize_t recovery_sysfs_recovery_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input == 1) + recovery_hcd->set_up_recovery_mode = true; + else if (input == 2) + recovery_hcd->set_up_recovery_mode = false; + else + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (recovery_hcd->ihex_size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get ihex data\n"); + retval = -EINVAL; + goto exit; + } + + if (recovery_hcd->ihex_size % IHEX_RECORD_SIZE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid ihex data\n"); + retval = -EINVAL; + goto exit; + } + + recovery_hcd->ihex_records = recovery_hcd->ihex_size / IHEX_RECORD_SIZE; + + retval = recovery_do_recovery(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do recovery\n"); + goto exit; + } + + retval = count; + +exit: + recovery_hcd->set_up_recovery_mode = SET_UP_RECOVERY_MODE; + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t recovery_sysfs_ihex_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = secure_memcpy(&recovery_hcd->ihex_buf[pos], + IHEX_BUF_SIZE - pos, + buf, + count, + count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy ihex data\n"); + recovery_hcd->ihex_size = 0; + goto exit; + } + + recovery_hcd->ihex_size = pos + count; + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static int recovery_device_reset(void) +{ + int retval; + unsigned char command; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + command = F35_RESET_COMMAND; + + retval = syna_tcm_rmi_write(tcm_hcd, + recovery_hcd->f35_addr.control_base + F35_CTRL3_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write F$35 command\n"); + return retval; + } + + msleep(bdata->reset_delay_ms); + + return 0; +} + +static int recovery_add_data_entry(unsigned char data) +{ + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + if (recovery_hcd->data_entries >= DATA_BUF_SIZE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Reached data buffer size limit\n"); + return -EINVAL; + } + + recovery_hcd->data_buf[recovery_hcd->data_entries++] = data; + + return 0; +} + +static int recovery_add_padding(unsigned int *words) +{ + int retval; + unsigned int padding; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + padding = (F35_CHUNK_SIZE_WORDS - *words % F35_CHUNK_SIZE_WORDS); + padding %= F35_CHUNK_SIZE_WORDS; + + while (padding) { + retval = recovery_add_data_entry(0xff); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry\n"); + return retval; + } + + retval = recovery_add_data_entry(0xff); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry\n"); + return retval; + } + + (*words)++; + padding--; + } + + return 0; +} + +static int recovery_parse_ihex(void) +{ + int retval; + unsigned char colon; + unsigned char *buf; + unsigned int addr; + unsigned int type; + unsigned int addrl; + unsigned int addrh; + unsigned int data0; + unsigned int data1; + unsigned int count; + unsigned int words; + unsigned int offset; + unsigned int record; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + words = 0; + + offset = 0; + + buf = recovery_hcd->ihex_buf; + + recovery_hcd->data_entries = 0; + + for (record = 0; record < recovery_hcd->ihex_records; record++) { + buf[(record + 1) * IHEX_RECORD_SIZE - 1] = 0x00; + retval = sscanf(&buf[record * IHEX_RECORD_SIZE], + "%c%02x%02x%02x%02x%02x%02x", + &colon, + &count, + &addrh, + &addrl, + &type, + &data0, + &data1); + if (retval != 7) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read ihex record\n"); + return -EINVAL; + } + + if (type == 0x00) { + if ((words % F35_CHUNK_SIZE_WORDS) == 0) { + addr = (addrh << 8) + addrl; + addr += offset; + addr >>= 4; + + retval = recovery_add_data_entry(addr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry\n"); + return retval; + } + + retval = recovery_add_data_entry(addr >> 8); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry\n"); + return retval; + } + } + + retval = recovery_add_data_entry(data0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry\n"); + return retval; + } + + retval = recovery_add_data_entry(data1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry\n"); + return retval; + } + + words++; + } else if (type == 0x02) { + retval = recovery_add_padding(&words); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add padding\n"); + return retval; + } + + offset = (data0 << 8) + data1; + offset <<= 4; + } + } + + retval = recovery_add_padding(&words); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add padding\n"); + return retval; + } + + return 0; +} + +static int recovery_check_status(void) +{ + int retval; + unsigned char status; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + retval = syna_tcm_rmi_read(tcm_hcd, + recovery_hcd->f35_addr.data_base, + &status, + sizeof(status)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read status\n"); + return retval; + } + + status = status & 0x1f; + + if (status != 0x00) { + LOGE(tcm_hcd->pdev->dev.parent, + "Recovery mode status = 0x%02x\n", + status); + return -EINVAL; + } + + return 0; +} + +static int recovery_write_flash(void) +{ + int retval; + unsigned char *data_ptr; + unsigned int chunk_buf_size; + unsigned int chunk_data_size; + unsigned int entries_written; + unsigned int entries_to_write; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + entries_written = 0; + + data_ptr = recovery_hcd->data_buf; + + chunk_buf_size = sizeof(recovery_hcd->chunk_buf); + + chunk_data_size = chunk_buf_size - 1; + + recovery_hcd->chunk_buf[chunk_buf_size - 1] = F35_WRITE_CHUNK_COMMAND; + + while (entries_written < recovery_hcd->data_entries) { + entries_to_write = F35_CHUNK_SIZE + 2; + + retval = secure_memcpy(recovery_hcd->chunk_buf, + chunk_buf_size - 1, + data_ptr, + recovery_hcd->data_entries - entries_written, + entries_to_write); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy chunk data\n"); + return retval; + } + + retval = syna_tcm_rmi_write(tcm_hcd, + recovery_hcd->f35_addr.control_base, + recovery_hcd->chunk_buf, + chunk_buf_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write chunk data\n"); + return retval; + } + + data_ptr += entries_to_write; + entries_written += entries_to_write; + } + + retval = recovery_check_status(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get no error recovery mode status\n"); + return retval; + } + + return 0; +} + +static int recovery_poll_erase_completion(void) +{ + int retval; + unsigned char status; + unsigned char command; + unsigned char data_base; + unsigned int timeout; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + timeout = F35_ERASE_ALL_WAIT_MS; + + data_base = recovery_hcd->f35_addr.data_base; + + do { + command = F35_READ_FLASH_STATUS_COMMAND; + + retval = syna_tcm_rmi_write(tcm_hcd, + recovery_hcd->f35_addr.command_base, + &command, + sizeof(command)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write F$35 command\n"); + return retval; + } + + do { + retval = syna_tcm_rmi_read(tcm_hcd, + recovery_hcd->f35_addr.command_base, + &command, + sizeof(command)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read command status\n"); + return retval; + } + + if (command == 0x00) + break; + + if (timeout == 0) + break; + + msleep(F35_ERASE_ALL_POLL_MS); + timeout -= F35_ERASE_ALL_POLL_MS; + } while (true); + + if (command != 0 && timeout == 0) { + retval = -EINVAL; + goto exit; + } + + retval = syna_tcm_rmi_read(tcm_hcd, + data_base + F35_DATA5_OFFSET, + &status, + sizeof(status)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read flash status\n"); + return retval; + } + + if ((status & 0x01) == 0x00) + break; + + if (timeout == 0) { + retval = -EINVAL; + goto exit; + } + + msleep(F35_ERASE_ALL_POLL_MS); + timeout -= F35_ERASE_ALL_POLL_MS; + } while (true); + + retval = 0; + +exit: + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get erase completion\n"); + } + + return retval; +} + +static int recovery_erase_flash(void) +{ + int retval; + unsigned char command; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + command = F35_ERASE_ALL_COMMAND; + + retval = syna_tcm_rmi_write(tcm_hcd, + recovery_hcd->f35_addr.control_base + F35_CTRL3_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write F$35 command\n"); + return retval; + } + + if (recovery_hcd->f35_addr.command_base) { + retval = recovery_poll_erase_completion(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to wait for erase completion\n"); + return retval; + } + } else { + msleep(F35_ERASE_ALL_WAIT_MS); + } + + retval = recovery_check_status(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get no error recovery mode status\n"); + return retval; + } + + return 0; +} + +static int recovery_set_up_recovery_mode(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + retval = tcm_hcd->identify(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + return retval; + } + + if (tcm_hcd->id_info.mode == MODE_APPLICATION) { + retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_BOOTLOADER); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enter bootloader mode\n"); + return retval; + } + } + + retval = tcm_hcd->write_message(tcm_hcd, + recovery_hcd->out_buf[0], + &recovery_hcd->out_buf[1], + 2, + NULL, + NULL, + NULL, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_REBOOT_TO_ROM_BOOTLOADER)); + return retval; + } + + msleep(bdata->reset_delay_ms); + + return 0; +} + +static int recovery_do_recovery(void) +{ + int retval; + struct rmi_pdt_entry p_entry; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + retval = recovery_parse_ihex(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to parse ihex data\n"); + return retval; + } + + if (recovery_hcd->set_up_recovery_mode) { + retval = recovery_set_up_recovery_mode(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up recovery mode\n"); + return retval; + } + } + + tcm_hcd->update_watchdog(tcm_hcd, false); + + retval = syna_tcm_rmi_read(tcm_hcd, + PDT_START_ADDR, + (unsigned char *)&p_entry, + sizeof(p_entry)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read PDT entry\n"); + return retval; + } + + if (p_entry.fn_number != UBL_FN_NUMBER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to find F$35\n"); + return -ENODEV; + } + + recovery_hcd->f35_addr.query_base = p_entry.query_base_addr; + recovery_hcd->f35_addr.command_base = p_entry.command_base_addr; + recovery_hcd->f35_addr.control_base = p_entry.control_base_addr; + recovery_hcd->f35_addr.data_base = p_entry.data_base_addr; + + LOGN(tcm_hcd->pdev->dev.parent, + "Start of recovery\n"); + + retval = recovery_erase_flash(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase flash\n"); + return retval; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Flash erased\n"); + + retval = recovery_write_flash(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to flash\n"); + return retval; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Flash written\n"); + + retval = recovery_device_reset(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + return retval; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "End of recovery\n"); + + if (recovery_hcd->set_up_recovery_mode) + return 0; + + tcm_hcd->update_watchdog(tcm_hcd, true); + + retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable interrupt\n"); + return retval; + } + + retval = tcm_hcd->identify(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + return retval; + } + + if (tcm_hcd->id_info.mode != MODE_APPLICATION) { + retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware\n"); + return retval; + } + } + + return 0; +} + +static int recovery_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + int idx; + + recovery_hcd = kzalloc(sizeof(*recovery_hcd), GFP_KERNEL); + if (!recovery_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for recovery_hcd\n"); + return -ENOMEM; + } + + recovery_hcd->ihex_buf = kzalloc(IHEX_BUF_SIZE, GFP_KERNEL); + if (!recovery_hcd->ihex_buf) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for recovery_hcd->ihex_buf\n"); + retval = -ENOMEM; + goto err_allocate_ihex_buf; + } + + recovery_hcd->data_buf = kzalloc(DATA_BUF_SIZE, GFP_KERNEL); + if (!recovery_hcd->data_buf) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for recovery_hcd->data_buf\n"); + retval = -ENOMEM; + goto err_allocate_data_buf; + } + + recovery_hcd->tcm_hcd = tcm_hcd; + + recovery_hcd->set_up_recovery_mode = SET_UP_RECOVERY_MODE; + + recovery_hcd->out_buf[0] = CMD_REBOOT_TO_ROM_BOOTLOADER; + recovery_hcd->out_buf[1] = 0; + recovery_hcd->out_buf[2] = 0; + + if (ENABLE_SYSFS_INTERFACE == false) + return 0; + + recovery_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME, + tcm_hcd->sysfs_dir); + if (!recovery_hcd->sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs directory\n"); + retval = -EINVAL; + goto err_sysfs_create_dir; + } + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + retval = sysfs_create_file(recovery_hcd->sysfs_dir, + &(*attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs file\n"); + goto err_sysfs_create_file; + } + } + + retval = sysfs_create_bin_file(recovery_hcd->sysfs_dir, &bin_attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs bin file\n"); + goto err_sysfs_create_bin_file; + } + + return 0; + +err_sysfs_create_bin_file: +err_sysfs_create_file: + for (idx--; idx >= 0; idx--) + sysfs_remove_file(recovery_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(recovery_hcd->sysfs_dir); + +err_sysfs_create_dir: + kfree(recovery_hcd->data_buf); +err_allocate_data_buf: + kfree(recovery_hcd->ihex_buf); +err_allocate_ihex_buf: + kfree(recovery_hcd); + recovery_hcd = NULL; + + return retval; +} + +static int recovery_remove(struct syna_tcm_hcd *tcm_hcd) +{ + int idx; + + if (!recovery_hcd) + goto exit; + + if (ENABLE_SYSFS_INTERFACE == true) { + sysfs_remove_bin_file(recovery_hcd->sysfs_dir, &bin_attr); + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + sysfs_remove_file(recovery_hcd->sysfs_dir, + &(*attrs[idx]).attr); + } + + kobject_put(recovery_hcd->sysfs_dir); + } + + kfree(recovery_hcd->data_buf); + kfree(recovery_hcd->ihex_buf); + kfree(recovery_hcd); + recovery_hcd = NULL; + +exit: + complete(&recovery_remove_complete); + + return 0; +} + +static int recovery_reset(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!recovery_hcd) { + retval = recovery_init(tcm_hcd); + return retval; + } + + return 0; +} + +static struct syna_tcm_module_cb recovery_module = { + .type = TCM_RECOVERY, + .init = recovery_init, + .remove = recovery_remove, + .syncbox = NULL, + .asyncbox = NULL, + .reset = recovery_reset, + .suspend = NULL, + .resume = NULL, + .early_suspend = NULL, +}; + +static int __init recovery_module_init(void) +{ + return syna_tcm_add_module(&recovery_module, true); +} + +static void __exit recovery_module_exit(void) +{ + syna_tcm_add_module(&recovery_module, false); + + wait_for_completion(&recovery_remove_complete); + + return; +} + +module_init(recovery_module_init); +module_exit(recovery_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Recovery Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_reflash.c b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_reflash.c new file mode 100644 index 000000000000..b49cbaa54f39 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_reflash.c @@ -0,0 +1,2378 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * + * 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. + */ + +#include +#include +#include "synaptics_tcm_core.h" + +#define FORCE_REFLASH false + +#define DISPLAY_REFLASH true + +#define ENABLE_SYSFS_INTERFACE true + +#define SYSFS_DIR_NAME "reflash" + +#define CUSTOM_DIR_NAME "custom" + +#define FW_IMAGE_NAME "synaptics/firmware.img" + +#define BOOT_CONFIG_ID "BOOT_CONFIG" + +#define APP_CODE_ID "APP_CODE" + +#define PROD_TEST_ID "APP_PROD_TEST" + +#define APP_CONFIG_ID "APP_CONFIG" + +#define DISP_CONFIG_ID "DISPLAY" + +#define FB_READY_COUNT 1 + +#define FB_READY_WAIT_MS 100 + +#define FB_READY_TIMEOUT_S 30 + +#define IMAGE_FILE_MAGIC_VALUE 0x4818472b + +#define FLASH_AREA_MAGIC_VALUE 0x7c05e516 + +#define BOOT_CONFIG_SIZE 8 + +#define BOOT_CONFIG_SLOTS 16 + +#define IMAGE_BUF_SIZE (512 * 1024) + +#define IMAGE_FILENAME_SIZE 80 + +#define ERASE_FLASH_DELAY_MS 500 + +#define WRITE_FLASH_DELAY_MS 20 + +#define REFLASH (1 << 0) + +#define FORCE_UPDATE (1 << 1) + +#define APP_CFG_UPDATE (1 << 2) + +#define DISP_CFG_UPDATE (1 << 3) + +#define BOOT_CFG_UPDATE (1 << 4) + +#define BOOT_CFG_LOCKDOWN (1 << 5) + +#define reflash_write(p_name) \ +static int reflash_write_##p_name(void) \ +{ \ + int retval; \ + unsigned int size; \ + unsigned int flash_addr; \ + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; \ + const unsigned char *data; \ +\ + data = reflash_hcd->image_info.p_name.data; \ + size = reflash_hcd->image_info.p_name.size; \ + flash_addr = reflash_hcd->image_info.p_name.flash_addr; \ +\ + retval = reflash_write_flash(flash_addr, data, size); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to write to flash\n"); \ + return retval; \ + } \ +\ + return 0; \ +} + +#define reflash_erase(p_name) \ +static int reflash_erase_##p_name(void) \ +{ \ + int retval; \ + unsigned int size; \ + unsigned int flash_addr; \ + unsigned int page_start; \ + unsigned int page_count; \ + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; \ +\ + flash_addr = reflash_hcd->image_info.p_name.flash_addr; \ +\ + page_start = flash_addr / reflash_hcd->page_size; \ +\ + size = reflash_hcd->image_info.p_name.size; \ + page_count = ceil_div(size, reflash_hcd->page_size); \ +\ + LOGD(tcm_hcd->pdev->dev.parent, \ + "Page start = %d\n", \ + page_start); \ +\ + LOGD(tcm_hcd->pdev->dev.parent, \ + "Page count = %d\n", \ + page_count); \ +\ + retval = reflash_erase_flash(page_start, page_count); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to erase flash pages\n"); \ + return retval; \ + } \ +\ + return 0; \ +} + +#define reflash_update(p_name) \ +static int reflash_update_##p_name(bool reset) \ +{ \ + int retval; \ + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; \ +\ + retval = reflash_set_up_flash_access(); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to set up flash access\n"); \ + return retval; \ + } \ +\ + tcm_hcd->update_watchdog(tcm_hcd, false); \ +\ + retval = reflash_check_##p_name(); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed "#p_name" partition check\n"); \ + reset = true; \ + goto reset; \ + } \ +\ + retval = reflash_erase_##p_name(); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to erase "#p_name" partition\n"); \ + reset = true; \ + goto reset; \ + } \ +\ + LOGN(tcm_hcd->pdev->dev.parent, \ + "Partition erased ("#p_name")\n"); \ +\ + retval = reflash_write_##p_name(); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to write "#p_name" partition\n"); \ + reset = true; \ + goto reset; \ + } \ +\ + LOGN(tcm_hcd->pdev->dev.parent, \ + "Partition written ("#p_name")\n"); \ +\ + retval = 0; \ +\ +reset: \ + if (!reset) \ + goto exit; \ +\ + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to do reset\n"); \ + } \ +\ +exit: \ + tcm_hcd->update_watchdog(tcm_hcd, true); \ +\ + return retval; \ +} + +#define reflash_show_data() \ +{ \ + LOCK_BUFFER(reflash_hcd->read); \ +\ + readlen = MIN(count, reflash_hcd->read.data_length - pos); \ +\ + retval = secure_memcpy(buf, \ + count, \ + &reflash_hcd->read.buf[pos], \ + reflash_hcd->read.buf_size - pos, \ + readlen); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to copy read data\n"); \ + } else { \ + retval = readlen; \ + } \ +\ + UNLOCK_BUFFER(reflash_hcd->read); \ +} + +enum update_area { + NONE = 0, + FIRMWARE_CONFIG, + CONFIG_ONLY, +}; + +struct app_config_header { + unsigned short magic_value[4]; + unsigned char checksum[4]; + unsigned char length[2]; + unsigned char build_id[4]; + unsigned char customer_config_id[16]; +}; + +struct area_descriptor { + unsigned char magic_value[4]; + unsigned char id_string[16]; + unsigned char flags[4]; + unsigned char flash_addr_words[4]; + unsigned char length[4]; + unsigned char checksum[4]; +}; + +struct block_data { + const unsigned char *data; + unsigned int size; + unsigned int flash_addr; +}; + +struct image_info { + struct block_data boot_config; + struct block_data app_firmware; + struct block_data prod_test_firmware; + struct block_data app_config; + struct block_data disp_config; +}; + +struct image_header { + unsigned char magic_value[4]; + unsigned char num_of_areas[4]; +}; + +struct boot_config { + union { + unsigned char i2c_address; + struct { + unsigned char cpha:1; + unsigned char cpol:1; + unsigned char word0_b2__7:6; + } __packed; + }; + unsigned char attn_polarity:1; + unsigned char attn_drive:2; + unsigned char attn_pullup:1; + unsigned char word0_b12__14:3; + unsigned char used:1; + unsigned short customer_part_id; + unsigned short boot_timeout; + unsigned short continue_on_reset:1; + unsigned short word3_b1__15:15; +} __packed; + +struct reflash_hcd { + bool force_update; + bool disp_cfg_update; + char image_name[IMAGE_FILENAME_SIZE]; + const unsigned char *image; + unsigned char *image_buf; + unsigned int image_size; + unsigned int page_size; + unsigned int write_block_size; + unsigned int max_write_payload_size; + const struct firmware *fw_entry; + struct mutex reflash_mutex; + struct kobject *sysfs_dir; + struct kobject *custom_dir; + struct work_struct work; + struct workqueue_struct *workqueue; + struct image_info image_info; + struct syna_tcm_buffer out; + struct syna_tcm_buffer resp; + struct syna_tcm_buffer read; + struct syna_tcm_hcd *tcm_hcd; +}; + +DECLARE_COMPLETION(reflash_remove_complete); + +static struct reflash_hcd *reflash_hcd; + +static int reflash_get_fw_image(void); + +static int reflash_read_data(enum flash_area area, bool run_app_firmware, + struct syna_tcm_buffer *output); + +static int reflash_update_custom_otp(const unsigned char *data, + unsigned int offset, unsigned int datalen); + +static int reflash_update_custom_lcm(const unsigned char *data, + unsigned int offset, unsigned int datalen); + +static int reflash_update_custom_oem(const unsigned char *data, + unsigned int offset, unsigned int datalen); + +static int reflash_update_boot_config(bool lock); + +static int reflash_update_app_config(bool reset); + +static int reflash_update_disp_config(bool reset); + +static int reflash_do_reflash(void); + +STORE_PROTOTYPE(reflash, reflash) + +static struct device_attribute *attrs[] = { + ATTRIFY(reflash), +}; + +static ssize_t reflash_sysfs_image_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_lockdown_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_lockdown_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_lcm_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_lcm_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_oem_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_oem_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_buildid_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_forcereflash_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_doreflash_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + + +static struct bin_attribute bin_attrs[] = { + { + .attr = { + .name = "image", + .mode = (S_IWUSR | S_IWGRP), + }, + .size = 0, + .write = reflash_sysfs_image_store, + }, + { + .attr = { + .name = "lockdown", + .mode = (S_IRUGO | S_IWUSR | S_IWGRP), + }, + .size = 0, + .read = reflash_sysfs_lockdown_show, + .write = reflash_sysfs_lockdown_store, + }, + { + .attr = { + .name = "lcm", + .mode = (S_IRUGO | S_IWUSR | S_IWGRP), + }, + .size = 0, + .read = reflash_sysfs_lcm_show, + .write = reflash_sysfs_lcm_store, + }, + { + .attr = { + .name = "oem", + .mode = (S_IRUGO | S_IWUSR | S_IWGRP), + }, + .size = 0, + .read = reflash_sysfs_oem_show, + .write = reflash_sysfs_oem_store, + }, +}; + +static struct bin_attribute moto_attrs[] = { + { + .attr = { + .name = "buildid", + .mode = (S_IRUGO), + }, + .size = 0, + .read = reflash_sysfs_buildid_show, + }, + { + .attr = { + .name = "forcereflash", + .mode = (S_IWUSR | S_IWGRP), + }, + .size = 0, + .write = reflash_sysfs_forcereflash_store, + }, + { + .attr = { + .name = "doreflash", + .mode = (S_IWUSR | S_IWGRP), + }, + .size = 0, + .write = reflash_sysfs_doreflash_store, + }, +}; + +static ssize_t reflash_sysfs_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + pm_stay_awake(&tcm_hcd->pdev->dev); + + mutex_lock(&reflash_hcd->reflash_mutex); + + if (reflash_hcd->image_size != 0) + reflash_hcd->image = reflash_hcd->image_buf; + + reflash_hcd->force_update = input & FORCE_UPDATE ? true : false; + + if (input & REFLASH || input & FORCE_UPDATE) { + retval = reflash_do_reflash(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reflash\n"); + goto exit; + } + } + + if ((input & ~(REFLASH | FORCE_UPDATE)) == 0) { + retval = count; + goto exit; + } + + retval = reflash_get_fw_image(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get firmware image\n"); + goto exit; + } + + if (input & BOOT_CFG_LOCKDOWN) { + retval = reflash_update_boot_config(true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to lockdown boot config\n"); + goto exit; + } + } else if (input & BOOT_CFG_UPDATE) { + retval = reflash_update_boot_config(false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to update boot config\n"); + goto exit; + } + } + + if (input & REFLASH || input & FORCE_UPDATE) { + retval = count; + goto exit; + } + + if (input & DISP_CFG_UPDATE) { + if (input & APP_CFG_UPDATE) + retval = reflash_update_disp_config(false); + else + retval = reflash_update_disp_config(true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reflash display config\n"); + goto exit; + } + } + + if (input & APP_CFG_UPDATE) { + retval = reflash_update_app_config(true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reflash application config\n"); + goto exit; + } + } + + retval = count; + +exit: + if (reflash_hcd->fw_entry) { + release_firmware(reflash_hcd->fw_entry); + reflash_hcd->fw_entry = NULL; + } + + reflash_hcd->image = NULL; + reflash_hcd->image_size = 0; + reflash_hcd->force_update = FORCE_REFLASH; + + mutex_unlock(&reflash_hcd->reflash_mutex); + + pm_relax(&tcm_hcd->pdev->dev); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_image_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = secure_memcpy(&reflash_hcd->image_buf[pos], + IMAGE_BUF_SIZE - pos, + buf, + count, + count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy firmware image data\n"); + reflash_hcd->image_size = 0; + goto exit; + } + + reflash_hcd->image_size = pos + count; + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_lockdown_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int readlen; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_read_data(CUSTOM_OTP, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read lockdown data\n"); + goto exit; + } + + reflash_show_data(); + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_lockdown_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + pm_stay_awake(&tcm_hcd->pdev->dev); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_update_custom_otp(buf, pos, count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to update custom OTP data\n"); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + pm_relax(&tcm_hcd->pdev->dev); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_lcm_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int readlen; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_read_data(CUSTOM_LCM, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read LCM data\n"); + goto exit; + } + + reflash_show_data(); + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_lcm_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + pm_stay_awake(&tcm_hcd->pdev->dev); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_update_custom_lcm(buf, pos, count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to update custom LCM data\n"); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + pm_relax(&tcm_hcd->pdev->dev); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_oem_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int readlen; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_read_data(CUSTOM_OEM, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read OEM data\n"); + goto exit; + } + + reflash_show_data(); + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_oem_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + pm_stay_awake(&tcm_hcd->pdev->dev); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_update_custom_oem(buf, pos, count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to update custom OEM data\n"); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + pm_relax(&tcm_hcd->pdev->dev); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_buildid_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + int readlen; + unsigned int config_id; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + config_id = le4_to_uint(&tcm_hcd->app_info.customer_config_id[12]); + + retval = scnprintf(buf, PAGE_SIZE, "%x-%08x\n", + tcm_hcd->packrat_number, config_id); + + readlen = MIN(count, retval - pos); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return readlen; +} + +static ssize_t reflash_sysfs_forcereflash_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + unsigned long value = 0; + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = kstrtoul(buf, 10, &value); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, "Failed to convert value\n"); + goto exit; + } + reflash_hcd->force_update = (value != 0); + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_doreflash_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + char prefix[IMAGE_FILENAME_SIZE] = "synaptics_tcm"; + char template[IMAGE_FILENAME_SIZE]; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (count > IMAGE_FILENAME_SIZE) { + LOGE(tcm_hcd->pdev->dev.parent, + "%s: FW filename is too long\n", + __func__); + retval = -EINVAL; + goto exit; + } + + if (!reflash_hcd->force_update) { + if (strncmp(buf, prefix, + strnlen(prefix, sizeof(prefix)))) { + LOGE(tcm_hcd->pdev->dev.parent, + "%s: FW does not belong to Synaptics TCM\n", + __func__); + retval = -EINVAL; + goto exit; + } + + snprintf(template, sizeof(template), "-%s-", + tcm_hcd->product_id_string); + if (!strnstr(buf + strnlen(prefix, sizeof(prefix)), template, + count)) { + LOGE(tcm_hcd->pdev->dev.parent, + "%s: FW does not belong to %s\n", + __func__, + tcm_hcd->product_id_string); + retval = -EINVAL; + goto exit; + } + } + + strlcpy(reflash_hcd->image_name, buf, count); + LOGD(tcm_hcd->pdev->dev.parent, + "%s: FW filename: %s\n", + __func__, + reflash_hcd->image_name); + + mutex_lock(&tcm_hcd->extif_mutex); + pm_stay_awake(&tcm_hcd->pdev->dev); + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_do_reflash(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reflash\n"); + goto reflash_failed; + } + retval = count; + +reflash_failed: + if (reflash_hcd->fw_entry) { + release_firmware(reflash_hcd->fw_entry); + reflash_hcd->fw_entry = NULL; + } + + reflash_hcd->image = NULL; + reflash_hcd->image_size = 0; + reflash_hcd->force_update = FORCE_REFLASH; + + mutex_unlock(&reflash_hcd->reflash_mutex); + pm_relax(&tcm_hcd->pdev->dev); + mutex_unlock(&tcm_hcd->extif_mutex); +exit: + return retval; +} + +static int reflash_set_up_flash_access(void) +{ + int retval; + unsigned int temp; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = tcm_hcd->identify(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + return retval; + } + + if (tcm_hcd->id_info.mode == MODE_APPLICATION) { + retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_BOOTLOADER); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enter bootloader mode\n"); + return retval; + } + } + + temp = tcm_hcd->boot_info.write_block_size_words; + reflash_hcd->write_block_size = temp * 2; + + temp = le2_to_uint(tcm_hcd->boot_info.erase_page_size_words); + reflash_hcd->page_size = temp * 2; + + temp = le2_to_uint(tcm_hcd->boot_info.max_write_payload_size); + reflash_hcd->max_write_payload_size = temp; + + LOGD(tcm_hcd->pdev->dev.parent, + "Write block size = %d\n", + reflash_hcd->write_block_size); + + LOGD(tcm_hcd->pdev->dev.parent, + "Page size = %d\n", + reflash_hcd->page_size); + + LOGD(tcm_hcd->pdev->dev.parent, + "Max write payload size = %d\n", + reflash_hcd->max_write_payload_size); + + if (reflash_hcd->write_block_size > (tcm_hcd->wr_chunk_size - 5)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Write block size greater than available chunk space\n"); + return -EINVAL; + } + + return 0; +} + +static int reflash_parse_fw_image(void) +{ + unsigned int idx; + unsigned int addr; + unsigned int offset; + unsigned int length; + unsigned int checksum; + unsigned int flash_addr; + unsigned int magic_value; + unsigned int num_of_areas; + struct image_header *header; + struct image_info *image_info; + struct area_descriptor *descriptor; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + const unsigned char *image; + const unsigned char *content; + + image = reflash_hcd->image; + image_info = &reflash_hcd->image_info; + header = (struct image_header *)image; + + magic_value = le4_to_uint(header->magic_value); + if (magic_value != IMAGE_FILE_MAGIC_VALUE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid image file magic value\n"); + return -EINVAL; + } + + memset(image_info, 0x00, sizeof(*image_info)); + + offset = sizeof(*header); + num_of_areas = le4_to_uint(header->num_of_areas); + + for (idx = 0; idx < num_of_areas; idx++) { + addr = le4_to_uint(image + offset); + descriptor = (struct area_descriptor *)(image + addr); + offset += 4; + + magic_value = le4_to_uint(descriptor->magic_value); + if (magic_value != FLASH_AREA_MAGIC_VALUE) + continue; + + length = le4_to_uint(descriptor->length); + content = (unsigned char *)descriptor + sizeof(*descriptor); + flash_addr = le4_to_uint(descriptor->flash_addr_words) * 2; + checksum = le4_to_uint(descriptor->checksum); + + if (0 == strncmp((char *)descriptor->id_string, + BOOT_CONFIG_ID, + strlen(BOOT_CONFIG_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Boot config checksum error\n"); + return -EINVAL; + } + image_info->boot_config.size = length; + image_info->boot_config.data = content; + image_info->boot_config.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Boot config size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Boot config flash address = 0x%08x\n", + flash_addr); + } else if (0 == strncmp((char *)descriptor->id_string, + APP_CODE_ID, + strlen(APP_CODE_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application firmware checksum error\n"); + return -EINVAL; + } + image_info->app_firmware.size = length; + image_info->app_firmware.data = content; + image_info->app_firmware.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Application firmware size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Application firmware flash address = 0x%08x\n", + flash_addr); + } else if (0 == strncmp((char *)descriptor->id_string, + PROD_TEST_ID, + strlen(PROD_TEST_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Production test firmware checksum error\n"); + return -EINVAL; + } + image_info->prod_test_firmware.size = length; + image_info->prod_test_firmware.data = content; + image_info->prod_test_firmware.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Production test firmware size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Production test firmware flash address = 0x%08x\n", + flash_addr); + } else if (0 == strncmp((char *)descriptor->id_string, + APP_CONFIG_ID, + strlen(APP_CONFIG_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application config checksum error\n"); + return -EINVAL; + } + image_info->app_config.size = length; + image_info->app_config.data = content; + image_info->app_config.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Application config size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Application config flash address = 0x%08x\n", + flash_addr); + } else if (0 == strncmp((char *)descriptor->id_string, + DISP_CONFIG_ID, + strlen(DISP_CONFIG_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Display config checksum error\n"); + return -EINVAL; + } + image_info->disp_config.size = length; + image_info->disp_config.data = content; + image_info->disp_config.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Display config size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Display config flash address = 0x%08x\n", + flash_addr); + } + } + + return 0; +} + +static int reflash_get_fw_image(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (reflash_hcd->image == NULL) { + if (reflash_hcd->image_name[0] == 0) + retval = request_firmware(&reflash_hcd->fw_entry, + FW_IMAGE_NAME, + tcm_hcd->pdev->dev.parent); + else + retval = request_firmware(&reflash_hcd->fw_entry, + reflash_hcd->image_name, + tcm_hcd->pdev->dev.parent); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to request %s\n", + FW_IMAGE_NAME); + return retval; + } + + LOGD(tcm_hcd->pdev->dev.parent, + "Firmware image size = %d\n", + (unsigned int)reflash_hcd->fw_entry->size); + + reflash_hcd->image = reflash_hcd->fw_entry->data; + reflash_hcd->image_size = reflash_hcd->fw_entry->size; + } + + retval = reflash_parse_fw_image(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to parse firmware image\n"); + return retval; + } + + return 0; +} + +static enum update_area reflash_compare_id_info(void) +{ + enum update_area update_area; + unsigned int idx; + unsigned int image_fw_id; + unsigned int device_fw_id; + unsigned char *image_config_id; + unsigned char *device_config_id; + struct app_config_header *header; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + const unsigned char *app_config_data; + + update_area = NONE; + + if (reflash_hcd->image_info.app_config.size < sizeof(*header)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid application config in image file\n"); + goto exit; + } + + app_config_data = reflash_hcd->image_info.app_config.data; + header = (struct app_config_header *)app_config_data; + + if (reflash_hcd->force_update) { + update_area = FIRMWARE_CONFIG; + goto exit; + } + + if (tcm_hcd->id_info.mode != MODE_APPLICATION) { + update_area = FIRMWARE_CONFIG; + goto exit; + } + + image_fw_id = le4_to_uint(header->build_id); + device_fw_id = tcm_hcd->packrat_number; + + if (image_fw_id > device_fw_id) { + LOGN(tcm_hcd->pdev->dev.parent, + "Image firmware ID newer than device firmware ID\n"); + update_area = FIRMWARE_CONFIG; + goto exit; + } else if (image_fw_id < device_fw_id) { + LOGN(tcm_hcd->pdev->dev.parent, + "Image firmware ID older than device firmware ID\n"); + update_area = NONE; + goto exit; + } + + image_config_id = header->customer_config_id; + device_config_id = tcm_hcd->app_info.customer_config_id; + + for (idx = 0; idx < 16; idx++) { + if (image_config_id[idx] > device_config_id[idx]) { + LOGN(tcm_hcd->pdev->dev.parent, + "Image config ID newer than device config ID\n"); + update_area = CONFIG_ONLY; + goto exit; + } else if (image_config_id[idx] < device_config_id[idx]) { + LOGN(tcm_hcd->pdev->dev.parent, + "Image config ID older than device config ID\n"); + update_area = NONE; + goto exit; + } + } + + update_area = NONE; + +exit: + if (update_area == NONE) { + LOGN(tcm_hcd->pdev->dev.parent, + "No need to do reflash\n"); + } else { + LOGN(tcm_hcd->pdev->dev.parent, + "Updating %s\n", + update_area == FIRMWARE_CONFIG ? + "firmware and config" : + "config only"); + } + + return update_area; +} + +static int reflash_read_flash(unsigned int address, unsigned char *data, + unsigned int datalen) +{ + int retval; + unsigned int length_words; + unsigned int flash_addr_words; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + LOCK_BUFFER(reflash_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &reflash_hcd->out, + 6); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for reflash_hcd->out.buf\n"); + UNLOCK_BUFFER(reflash_hcd->out); + return retval; + } + + length_words = datalen / 2; + flash_addr_words = address / 2; + + reflash_hcd->out.buf[0] = (unsigned char)flash_addr_words; + reflash_hcd->out.buf[1] = (unsigned char)(flash_addr_words >> 8); + reflash_hcd->out.buf[2] = (unsigned char)(flash_addr_words >> 16); + reflash_hcd->out.buf[3] = (unsigned char)(flash_addr_words >> 24); + reflash_hcd->out.buf[4] = (unsigned char)length_words; + reflash_hcd->out.buf[5] = (unsigned char)(length_words >> 8); + + LOCK_BUFFER(reflash_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_READ_FLASH, + reflash_hcd->out.buf, + 6, + &reflash_hcd->resp.buf, + &reflash_hcd->resp.buf_size, + &reflash_hcd->resp.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_READ_FLASH)); + UNLOCK_BUFFER(reflash_hcd->resp); + UNLOCK_BUFFER(reflash_hcd->out); + return retval; + } + + UNLOCK_BUFFER(reflash_hcd->out); + + if (reflash_hcd->resp.data_length != datalen) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read requested length\n"); + UNLOCK_BUFFER(reflash_hcd->resp); + return -EIO; + } + + retval = secure_memcpy(data, + datalen, + reflash_hcd->resp.buf, + reflash_hcd->resp.buf_size, + datalen); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy read data\n"); + UNLOCK_BUFFER(reflash_hcd->resp); + return retval; + } + + UNLOCK_BUFFER(reflash_hcd->resp); + + return 0; +} + +static int reflash_read_data(enum flash_area area, bool run_app_firmware, + struct syna_tcm_buffer *output) +{ + int retval; + unsigned int temp; + unsigned int addr; + unsigned int length; + struct syna_tcm_app_info *app_info; + struct syna_tcm_boot_info *boot_info; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + switch (area) { + case CUSTOM_LCM: + case CUSTOM_OEM: + case PPDT: + retval = tcm_hcd->get_data_location(tcm_hcd, + area, + &addr, + &length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get data location\n"); + return retval; + } + break; + default: + break; + } + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + + app_info = &tcm_hcd->app_info; + boot_info = &tcm_hcd->boot_info; + + switch (area) { + case BOOT_CONFIG: + temp = le2_to_uint(boot_info->boot_config_start_block); + addr = temp * reflash_hcd->write_block_size; + length = BOOT_CONFIG_SIZE * BOOT_CONFIG_SLOTS; + break; + case APP_CONFIG: + temp = le2_to_uint(app_info->app_config_start_write_block); + addr = temp * reflash_hcd->write_block_size; + length = le2_to_uint(app_info->app_config_size); + break; + case DISP_CONFIG: + temp = le4_to_uint(boot_info->display_config_start_block); + addr = temp * reflash_hcd->write_block_size; + temp = le2_to_uint(boot_info->display_config_length_blocks); + length = temp * reflash_hcd->write_block_size; + break; + case CUSTOM_OTP: + temp = le2_to_uint(boot_info->custom_otp_start_block); + addr = temp * reflash_hcd->write_block_size; + temp = le2_to_uint(boot_info->custom_otp_length_blocks); + length = temp * reflash_hcd->write_block_size; + break; + case CUSTOM_LCM: + case CUSTOM_OEM: + case PPDT: + addr *= reflash_hcd->write_block_size; + length *= reflash_hcd->write_block_size; + break; + default: + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid data area\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + if (addr == 0 || length == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Data area unavailable\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + LOCK_BUFFER(reflash_hcd->read); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &reflash_hcd->read, + length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for reflash_hcd->read.buf\n"); + UNLOCK_BUFFER(reflash_hcd->read); + goto run_app_firmware; + } + + retval = reflash_read_flash(addr, reflash_hcd->read.buf, length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read from flash\n"); + UNLOCK_BUFFER(reflash_hcd->read); + goto run_app_firmware; + } + + reflash_hcd->read.data_length = length; + + if (output != NULL) { + retval = syna_tcm_alloc_mem(tcm_hcd, + output, + length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for output->buf\n"); + UNLOCK_BUFFER(reflash_hcd->read); + goto run_app_firmware; + } + + retval = secure_memcpy(output->buf, + output->buf_size, + reflash_hcd->read.buf, + reflash_hcd->read.buf_size, + length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy read data\n"); + UNLOCK_BUFFER(reflash_hcd->read); + goto run_app_firmware; + } + + output->data_length = length; + } + + UNLOCK_BUFFER(reflash_hcd->read); + + retval = 0; + +run_app_firmware: + if (!run_app_firmware) + goto exit; + + if (tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware\n"); + } + +exit: + return retval; +} + +static int reflash_check_boot_config(void) +{ + unsigned int temp; + unsigned int image_addr; + unsigned int device_addr; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (reflash_hcd->image_info.boot_config.size < BOOT_CONFIG_SIZE) { + LOGE(tcm_hcd->pdev->dev.parent, + "No valid boot config in image file\n"); + return -EINVAL; + } + + image_addr = reflash_hcd->image_info.boot_config.flash_addr; + + temp = le2_to_uint(tcm_hcd->boot_info.boot_config_start_block); + device_addr = temp * reflash_hcd->write_block_size; + + if (image_addr != device_addr) { + LOGE(tcm_hcd->pdev->dev.parent, + "Flash address mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int reflash_check_app_config(void) +{ + unsigned int temp; + unsigned int image_addr; + unsigned int image_size; + unsigned int device_addr; + unsigned int device_size; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (reflash_hcd->image_info.app_config.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No application config in image file\n"); + return -EINVAL; + } + + image_addr = reflash_hcd->image_info.app_config.flash_addr; + image_size = reflash_hcd->image_info.app_config.size; + + temp = le2_to_uint(tcm_hcd->app_info.app_config_start_write_block); + device_addr = temp * reflash_hcd->write_block_size; + device_size = le2_to_uint(tcm_hcd->app_info.app_config_size); + + if (device_addr == 0 && device_size == 0) + return 0; + + if (image_addr != device_addr) { + LOGE(tcm_hcd->pdev->dev.parent, + "Flash address mismatch\n"); + return -EINVAL; + } + + if (image_size != device_size) { + LOGE(tcm_hcd->pdev->dev.parent, + "Config size mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int reflash_check_disp_config(void) +{ + unsigned int temp; + unsigned int image_addr; + unsigned int image_size; + unsigned int device_addr; + unsigned int device_size; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (reflash_hcd->image_info.disp_config.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No display config in image file\n"); + return -EINVAL; + } + + image_addr = reflash_hcd->image_info.disp_config.flash_addr; + image_size = reflash_hcd->image_info.disp_config.size; + + temp = le4_to_uint(tcm_hcd->boot_info.display_config_start_block); + device_addr = temp * reflash_hcd->write_block_size; + + temp = le2_to_uint(tcm_hcd->boot_info.display_config_length_blocks); + device_size = temp * reflash_hcd->write_block_size; + + if (image_addr != device_addr) { + LOGE(tcm_hcd->pdev->dev.parent, + "Flash address mismatch\n"); + return -EINVAL; + } + + if (image_size != device_size) { + LOGE(tcm_hcd->pdev->dev.parent, + "Config size mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int reflash_check_prod_test_firmware(void) +{ + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (reflash_hcd->image_info.prod_test_firmware.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No production test firmware in image file\n"); + return -EINVAL; + } + + return 0; +} + +static int reflash_check_app_firmware(void) +{ + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (reflash_hcd->image_info.app_firmware.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No application firmware in image file\n"); + return -EINVAL; + } + + return 0; +} + +static int reflash_write_flash(unsigned int address, const unsigned char *data, + unsigned int datalen) +{ + int retval; + unsigned int offset; + unsigned int w_length; + unsigned int xfer_length; + unsigned int remaining_length; + unsigned int flash_address; + unsigned int block_address; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + w_length = tcm_hcd->wr_chunk_size - 5; + + w_length = w_length - (w_length % reflash_hcd->write_block_size); + + w_length = MIN(w_length, reflash_hcd->max_write_payload_size); + + offset = 0; + + remaining_length = datalen; + + LOCK_BUFFER(reflash_hcd->out); + LOCK_BUFFER(reflash_hcd->resp); + + while (remaining_length) { + if (remaining_length > w_length) + xfer_length = w_length; + else + xfer_length = remaining_length; + + retval = syna_tcm_alloc_mem(tcm_hcd, + &reflash_hcd->out, + xfer_length + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for reflash_hcd->out.buf\n"); + UNLOCK_BUFFER(reflash_hcd->resp); + UNLOCK_BUFFER(reflash_hcd->out); + return retval; + } + + flash_address = address + offset; + block_address = flash_address / reflash_hcd->write_block_size; + reflash_hcd->out.buf[0] = (unsigned char)block_address; + reflash_hcd->out.buf[1] = (unsigned char)(block_address >> 8); + + retval = secure_memcpy(&reflash_hcd->out.buf[2], + reflash_hcd->out.buf_size - 2, + &data[offset], + datalen - offset, + xfer_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy write data\n"); + UNLOCK_BUFFER(reflash_hcd->resp); + UNLOCK_BUFFER(reflash_hcd->out); + return retval; + } + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_WRITE_FLASH, + reflash_hcd->out.buf, + xfer_length + 2, + &reflash_hcd->resp.buf, + &reflash_hcd->resp.buf_size, + &reflash_hcd->resp.data_length, + NULL, + WRITE_FLASH_DELAY_MS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_WRITE_FLASH)); + LOGE(tcm_hcd->pdev->dev.parent, + "Flash address = 0x%08x\n", + flash_address); + LOGE(tcm_hcd->pdev->dev.parent, + "Data length = %d\n", + xfer_length); + UNLOCK_BUFFER(reflash_hcd->resp); + UNLOCK_BUFFER(reflash_hcd->out); + return retval; + } + + offset += xfer_length; + remaining_length -= xfer_length; + } + + UNLOCK_BUFFER(reflash_hcd->resp); + UNLOCK_BUFFER(reflash_hcd->out); + + return 0; +} + +reflash_write(app_config) + +reflash_write(disp_config) + +reflash_write(prod_test_firmware) + +reflash_write(app_firmware) + +static int reflash_erase_flash(unsigned int page_start, unsigned int page_count) +{ + int retval; + unsigned char out_buf[2]; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + out_buf[0] = (unsigned char)page_start; + out_buf[1] = (unsigned char)page_count; + + LOCK_BUFFER(reflash_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_ERASE_FLASH, + out_buf, + sizeof(out_buf), + &reflash_hcd->resp.buf, + &reflash_hcd->resp.buf_size, + &reflash_hcd->resp.data_length, + NULL, + ERASE_FLASH_DELAY_MS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_ERASE_FLASH)); + UNLOCK_BUFFER(reflash_hcd->resp); + return retval; + } + + UNLOCK_BUFFER(reflash_hcd->resp); + + return 0; +} + +reflash_erase(app_config) + +reflash_erase(disp_config) + +reflash_erase(prod_test_firmware) + +reflash_erase(app_firmware) + +static int reflash_update_custom_otp(const unsigned char *data, + unsigned int offset, unsigned int datalen) +{ + int retval; + unsigned int temp; + unsigned int addr; + unsigned int length; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + + tcm_hcd->update_watchdog(tcm_hcd, false); + + temp = le2_to_uint(tcm_hcd->boot_info.custom_otp_start_block); + addr = temp * reflash_hcd->write_block_size; + + temp = le2_to_uint(tcm_hcd->boot_info.custom_otp_length_blocks); + length = temp * reflash_hcd->write_block_size; + + if (addr == 0 || length == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Data area unavailable\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + if (datalen + offset > length) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid data length\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + retval = reflash_write_flash(addr + offset, + data, + datalen); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to flash\n"); + goto run_app_firmware; + } + + retval = 0; + +run_app_firmware: + if (tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware\n"); + } + + tcm_hcd->update_watchdog(tcm_hcd, true); + + return retval; +} + +static int reflash_update_custom_lcm(const unsigned char *data, + unsigned int offset, unsigned int datalen) +{ + int retval; + unsigned int addr; + unsigned int length; + unsigned int page_start; + unsigned int page_count; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = tcm_hcd->get_data_location(tcm_hcd, + CUSTOM_LCM, + &addr, + &length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get data location\n"); + return retval; + } + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + + tcm_hcd->update_watchdog(tcm_hcd, false); + + addr *= reflash_hcd->write_block_size; + length *= reflash_hcd->write_block_size; + + if (addr == 0 || length == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Data area unavailable\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + if (datalen + offset > length) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid data length\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + if (offset == 0) { + page_start = addr / reflash_hcd->page_size; + + page_count = ceil_div(length, reflash_hcd->page_size); + + retval = reflash_erase_flash(page_start, page_count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase flash pages\n"); + goto run_app_firmware; + } + } + + retval = reflash_write_flash(addr + offset, + data, + datalen); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to flash\n"); + goto run_app_firmware; + } + + retval = 0; + +run_app_firmware: + if (tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware\n"); + } + + tcm_hcd->update_watchdog(tcm_hcd, true); + + return retval; +} + +static int reflash_update_custom_oem(const unsigned char *data, + unsigned int offset, unsigned int datalen) +{ + int retval; + unsigned int addr; + unsigned int length; + unsigned int page_start; + unsigned int page_count; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = tcm_hcd->get_data_location(tcm_hcd, + CUSTOM_OEM, + &addr, + &length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get data location\n"); + return retval; + } + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + + tcm_hcd->update_watchdog(tcm_hcd, false); + + addr *= reflash_hcd->write_block_size; + length *= reflash_hcd->write_block_size; + + if (addr == 0 || length == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Data area unavailable\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + if (datalen + offset > length) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid data length\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + if (offset == 0) { + page_start = addr / reflash_hcd->page_size; + + page_count = ceil_div(length, reflash_hcd->page_size); + + retval = reflash_erase_flash(page_start, page_count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase flash pages\n"); + goto run_app_firmware; + } + } + + retval = reflash_write_flash(addr + offset, + data, + datalen); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to flash\n"); + goto run_app_firmware; + } + + retval = 0; + +run_app_firmware: + if (tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware\n"); + } + + tcm_hcd->update_watchdog(tcm_hcd, true); + + return retval; +} + +static int reflash_update_boot_config(bool lock) +{ + int retval; + unsigned char slot_used; + unsigned int idx; + unsigned int addr = 0; + struct boot_config *data; + struct boot_config *last_slot; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + + tcm_hcd->update_watchdog(tcm_hcd, false); + + retval = reflash_check_boot_config(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed boot_config partition check\n"); + goto reset; + } + + retval = reflash_read_data(BOOT_CONFIG, false, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read boot config\n"); + goto reset; + } + + LOCK_BUFFER(reflash_hcd->read); + + data = (struct boot_config *)reflash_hcd->read.buf; + last_slot = data + (BOOT_CONFIG_SLOTS - 1); + slot_used = tcm_hcd->id_info.mode == MODE_TDDI_BOOTLOADER ? 0 : 1; + + if (last_slot->used == slot_used) { + LOGE(tcm_hcd->pdev->dev.parent, + "Boot config already locked down\n"); + UNLOCK_BUFFER(reflash_hcd->read); + goto reset; + } + + if (lock) { + idx = BOOT_CONFIG_SLOTS - 1; + } else { + for (idx = 0; idx < BOOT_CONFIG_SLOTS; idx++) { + if (data->used == slot_used) { + data++; + continue; + } else { + break; + } + } + } + + UNLOCK_BUFFER(reflash_hcd->read); + + if (idx == BOOT_CONFIG_SLOTS) { + LOGE(tcm_hcd->pdev->dev.parent, + "No free boot config slot available\n"); + goto reset; + } + + addr += idx * BOOT_CONFIG_SIZE; + + retval = reflash_write_flash(addr, + reflash_hcd->image_info.boot_config.data, + BOOT_CONFIG_SIZE); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to flash\n"); + goto reset; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Slot %d updated with new boot config\n", + idx); + + retval = 0; + +reset: + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + + tcm_hcd->update_watchdog(tcm_hcd, true); + + return retval; +} + +reflash_update(app_config) + +reflash_update(disp_config) + +reflash_update(prod_test_firmware) + +reflash_update(app_firmware) + +static int reflash_do_reflash(void) +{ + int retval; + enum update_area update_area; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = reflash_get_fw_image(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get firmware image\n"); + goto exit; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Start of reflash\n"); + + update_area = reflash_compare_id_info(); + + switch (update_area) { + case FIRMWARE_CONFIG: + retval = reflash_update_app_firmware(false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reflash application firmware\n"); + goto exit; + } + memset(&tcm_hcd->app_info, 0x00, sizeof(tcm_hcd->app_info)); + if (tcm_hcd->features.dual_firmware) { + retval = reflash_update_prod_test_firmware(false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reflash production test firmware\n"); + goto exit; + } + } + case CONFIG_ONLY: + if (reflash_hcd->disp_cfg_update) { + retval = reflash_update_disp_config(false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reflash display config\n"); + goto exit; + } + } + retval = reflash_update_app_config(true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reflash application config\n"); + goto exit; + } + break; + case NONE: + default: + break; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "End of reflash\n"); + + retval = 0; + +exit: + if (reflash_hcd->fw_entry) { + release_firmware(reflash_hcd->fw_entry); + reflash_hcd->fw_entry = NULL; + reflash_hcd->image = NULL; + reflash_hcd->image_size = 0; + } + + return retval; +} + +#ifdef STARTUP_REFLASH +static void reflash_startup_work(struct work_struct *work) +{ + int retval; +#ifdef CONFIG_FB + unsigned int timeout; +#endif + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + +#ifdef CONFIG_FB + timeout = FB_READY_TIMEOUT_S * 1000 / FB_READY_WAIT_MS; + + while (tcm_hcd->fb_ready != FB_READY_COUNT) { + if (timeout == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Timed out waiting for FB ready\n"); + return; + } + msleep(FB_READY_WAIT_MS); + timeout--; + } +#endif + + pm_stay_awake(&tcm_hcd->pdev->dev); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_do_reflash(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reflash\n"); + } + + mutex_unlock(&reflash_hcd->reflash_mutex); + + pm_relax(&tcm_hcd->pdev->dev); + + return; +} +#endif + +static int reflash_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + int idx; + + reflash_hcd = kzalloc(sizeof(*reflash_hcd), GFP_KERNEL); + if (!reflash_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for reflash_hcd\n"); + return -ENOMEM; + } + + reflash_hcd->image_buf = kzalloc(IMAGE_BUF_SIZE, GFP_KERNEL); + if (!reflash_hcd->image_buf) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for reflash_hcd->image_buf\n"); + retval = -ENOMEM; + goto err_allocate_memory; + } + + reflash_hcd->tcm_hcd = tcm_hcd; + + reflash_hcd->force_update = FORCE_REFLASH; + + reflash_hcd->disp_cfg_update = DISPLAY_REFLASH; + + mutex_init(&reflash_hcd->reflash_mutex); + + INIT_BUFFER(reflash_hcd->out, false); + INIT_BUFFER(reflash_hcd->resp, false); + INIT_BUFFER(reflash_hcd->read, false); + +#ifdef STARTUP_REFLASH + reflash_hcd->workqueue = + create_singlethread_workqueue("syna_tcm_reflash"); + INIT_WORK(&reflash_hcd->work, reflash_startup_work); + queue_work(reflash_hcd->workqueue, &reflash_hcd->work); +#endif + + if (ENABLE_SYSFS_INTERFACE == false) + return 0; + + reflash_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME, + tcm_hcd->sysfs_dir); + if (!reflash_hcd->sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs directory\n"); + retval = -EINVAL; + goto err_sysfs_create_dir; + } + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + retval = sysfs_create_file(reflash_hcd->sysfs_dir, + &(*attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs file\n"); + goto err_sysfs_create_file; + } + } + + retval = sysfs_create_bin_file(reflash_hcd->sysfs_dir, &bin_attrs[0]); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs bin file\n"); + goto err_sysfs_create_bin_file; + } + + reflash_hcd->custom_dir = kobject_create_and_add(CUSTOM_DIR_NAME, + reflash_hcd->sysfs_dir); + if (!reflash_hcd->custom_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create custom sysfs directory\n"); + retval = -EINVAL; + goto err_custom_sysfs_create_dir; + } + + for (idx = 1; idx < ARRAY_SIZE(bin_attrs); idx++) { + retval = sysfs_create_bin_file(reflash_hcd->custom_dir, + &bin_attrs[idx]); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs bin file\n"); + goto err_custom_sysfs_create_bin_file; + } + } + + for (idx = 0; idx < ARRAY_SIZE(moto_attrs); idx++) { + retval = sysfs_create_bin_file(tcm_hcd->sysfs_dir, + &moto_attrs[idx]); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs bin file\n"); + goto err_moto_sysfs_create_bin_file; + } + } + + tcm_hcd->read_flash_data = reflash_read_data; + + return 0; + +err_moto_sysfs_create_bin_file: + for (idx = 0; idx < ARRAY_SIZE(moto_attrs); idx++) + sysfs_remove_bin_file(tcm_hcd->sysfs_dir, &moto_attrs[idx]); + +err_custom_sysfs_create_bin_file: + for (idx--; idx > 0; idx--) + sysfs_remove_bin_file(reflash_hcd->custom_dir, &bin_attrs[idx]); + + kobject_put(reflash_hcd->custom_dir); + + idx = ARRAY_SIZE(attrs); + +err_custom_sysfs_create_dir: + sysfs_remove_bin_file(reflash_hcd->sysfs_dir, &bin_attrs[0]); + +err_sysfs_create_bin_file: +err_sysfs_create_file: + for (idx--; idx >= 0; idx--) + sysfs_remove_file(reflash_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(reflash_hcd->sysfs_dir); + +err_sysfs_create_dir: +err_allocate_memory: + kfree(reflash_hcd->image_buf); + + RELEASE_BUFFER(reflash_hcd->read); + RELEASE_BUFFER(reflash_hcd->resp); + RELEASE_BUFFER(reflash_hcd->out); + + kfree(reflash_hcd); + reflash_hcd = NULL; + + return retval; +} + +static int reflash_remove(struct syna_tcm_hcd *tcm_hcd) +{ + int idx; + + if (!reflash_hcd) + goto exit; + + tcm_hcd->read_flash_data = NULL; + + if (ENABLE_SYSFS_INTERFACE == true) { + for (idx = 0; idx < ARRAY_SIZE(moto_attrs); idx++) { + sysfs_remove_bin_file(tcm_hcd->sysfs_dir, + &moto_attrs[idx]); + } + + for (idx = 1; idx < ARRAY_SIZE(bin_attrs); idx++) { + sysfs_remove_bin_file(reflash_hcd->custom_dir, + &bin_attrs[idx]); + } + + kobject_put(reflash_hcd->custom_dir); + + sysfs_remove_bin_file(reflash_hcd->sysfs_dir, &bin_attrs[0]); + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + sysfs_remove_file(reflash_hcd->sysfs_dir, + &(*attrs[idx]).attr); + } + + kobject_put(reflash_hcd->sysfs_dir); + } + +#ifdef STARTUP_REFLASH + cancel_work_sync(&reflash_hcd->work); + flush_workqueue(reflash_hcd->workqueue); + destroy_workqueue(reflash_hcd->workqueue); +#endif + + kfree(reflash_hcd->image_buf); + + RELEASE_BUFFER(reflash_hcd->read); + RELEASE_BUFFER(reflash_hcd->resp); + RELEASE_BUFFER(reflash_hcd->out); + + kfree(reflash_hcd); + reflash_hcd = NULL; + +exit: + complete(&reflash_remove_complete); + + return 0; +} + +static int reflash_reset(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!reflash_hcd) { + retval = reflash_init(tcm_hcd); + return retval; + } + + return 0; +} + +static struct syna_tcm_module_cb reflash_module = { + .type = TCM_REFLASH, + .init = reflash_init, + .remove = reflash_remove, + .syncbox = NULL, + .asyncbox = NULL, + .reset = reflash_reset, + .suspend = NULL, + .resume = NULL, + .early_suspend = NULL, +}; + +static int __init reflash_module_init(void) +{ + return syna_tcm_add_module(&reflash_module, true); +} + +static void __exit reflash_module_exit(void) +{ + syna_tcm_add_module(&reflash_module, false); + + wait_for_completion(&reflash_remove_complete); + + return; +} + +module_init(reflash_module_init); +module_exit(reflash_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Reflash Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_spi.c b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_spi.c new file mode 100644 index 000000000000..0cddf272bc17 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_spi.c @@ -0,0 +1,684 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * + * 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. + */ + +#include +#include +#include "synaptics_tcm_core.h" + +static unsigned char *buf; + +static unsigned int buf_size; + +static struct spi_transfer *xfer; + +static struct syna_tcm_bus_io bus_io; + +static struct syna_tcm_hw_interface hw_if; + +static struct platform_device *syna_tcm_spi_device; + +#ifdef CONFIG_OF +static int parse_dt(struct device *dev, struct syna_tcm_board_data *bdata) +{ + int retval; + u32 value; + struct property *prop; + struct device_node *np = dev->of_node; + const char *name; + + prop = of_find_property(np, "synaptics,irq-gpio", NULL); + if (prop && prop->length) { + bdata->irq_gpio = of_get_named_gpio_flags(np, + "synaptics,irq-gpio", 0, + (enum of_gpio_flags *)&bdata->irq_flags); + } else { + bdata->irq_gpio = -1; + } + + retval = of_property_read_u32(np, "synaptics,irq-on-state", &value); + if (retval < 0) + bdata->irq_on_state = 0; + else + bdata->irq_on_state = value; + + retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name); + if (retval < 0) + bdata->pwr_reg_name = NULL; + else + bdata->pwr_reg_name = name; + + retval = of_property_read_string(np, "synaptics,bus-reg-name", &name); + if (retval < 0) + bdata->bus_reg_name = NULL; + else + bdata->bus_reg_name = name; + + prop = of_find_property(np, "synaptics,power-gpio", NULL); + if (prop && prop->length) { + bdata->power_gpio = of_get_named_gpio_flags(np, + "synaptics,power-gpio", 0, NULL); + } else { + bdata->power_gpio = -1; + } + + prop = of_find_property(np, "synaptics,power-on-state", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-on-state", + &value); + if (retval < 0) { + LOGE(dev, + "Failed to read synaptics,power-on-state property\n"); + return retval; + } else { + bdata->power_on_state = value; + } + } else { + bdata->power_on_state = 0; + } + + prop = of_find_property(np, "synaptics,power-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-delay-ms", + &value); + if (retval < 0) { + LOGE(dev, + "Failed to read synaptics,power-delay-ms property\n"); + return retval; + } else { + bdata->power_delay_ms = value; + } + } else { + bdata->power_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-gpio", NULL); + if (prop && prop->length) { + bdata->reset_gpio = of_get_named_gpio_flags(np, + "synaptics,reset-gpio", 0, NULL); + } else { + bdata->reset_gpio = -1; + } + + prop = of_find_property(np, "synaptics,reset-on-state", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-on-state", + &value); + if (retval < 0) { + LOGE(dev, + "Failed to read synaptics,reset-on-state property\n"); + return retval; + } else { + bdata->reset_on_state = value; + } + } else { + bdata->reset_on_state = 0; + } + + prop = of_find_property(np, "synaptics,reset-active-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-active-ms", + &value); + if (retval < 0) { + LOGE(dev, + "Failed to read synaptics,reset-active-ms property\n"); + return retval; + } else { + bdata->reset_active_ms = value; + } + } else { + bdata->reset_active_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-delay-ms", + &value); + if (retval < 0) { + LOGE(dev, + "Unable to read synaptics,reset-delay-ms property\n"); + return retval; + } else { + bdata->reset_delay_ms = value; + } + } else { + bdata->reset_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,x-flip", NULL); + bdata->x_flip = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,y-flip", NULL); + bdata->y_flip = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,swap-axes", NULL); + bdata->swap_axes = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,byte-delay-us", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,byte-delay-us", + &value); + if (retval < 0) { + LOGE(dev, + "Unable to read synaptics,byte-delay-us property\n"); + return retval; + } else { + bdata->byte_delay_us = value; + } + } else { + bdata->byte_delay_us = 0; + } + + prop = of_find_property(np, "synaptics,block-delay-us", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,block-delay-us", + &value); + if (retval < 0) { + LOGE(dev, + "Unable to read synaptics,block-delay-us property\n"); + return retval; + } else { + bdata->block_delay_us = value; + } + } else { + bdata->block_delay_us = 0; + } + + prop = of_find_property(np, "synaptics,spi-mode", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,spi-mode", + &value); + if (retval < 0) { + LOGE(dev, + "Unable to read synaptics,spi-mode property\n"); + return retval; + } else { + bdata->spi_mode = value; + } + } else { + bdata->spi_mode = 0; + } + + prop = of_find_property(np, "synaptics,ubl-max-freq", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,ubl-max-freq", + &value); + if (retval < 0) { + LOGE(dev, + "Unable to read synaptics,ubl-max-freq property\n"); + return retval; + } else { + bdata->ubl_max_freq = value; + } + } else { + bdata->ubl_max_freq = 0; + } + + prop = of_find_property(np, "synaptics,ubl-byte-delay-us", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,ubl-byte-delay-us", + &value); + if (retval < 0) { + LOGE(dev, + "Unable to read synaptics,ubl-byte-delay-us property\n"); + return retval; + } else { + bdata->ubl_byte_delay_us = value; + } + } else { + bdata->ubl_byte_delay_us = 0; + } + + return 0; +} +#endif + +static int syna_tcm_spi_alloc_mem(struct syna_tcm_hcd *tcm_hcd, + unsigned int count, unsigned int size) +{ + static unsigned int xfer_count; + struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent); + + if (count > xfer_count) { + kfree(xfer); + xfer = kcalloc(count, sizeof(*xfer), GFP_KERNEL); + if (!xfer) { + LOGE(&spi->dev, + "Failed to allocate memory for xfer\n"); + xfer_count = 0; + return -ENOMEM; + } + xfer_count = count; + } else { + memset(xfer, 0, count * sizeof(*xfer)); + } + + if (size > buf_size) { + if (buf_size) + kfree(buf); + buf = kmalloc(size, GFP_KERNEL); + if (!buf) { + LOGE(&spi->dev, + "Failed to allocate memory for buf\n"); + buf_size = 0; + return -ENOMEM; + } + buf_size = size; + } + + return 0; +} + +static int syna_tcm_spi_rmi_read(struct syna_tcm_hcd *tcm_hcd, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned int idx; + unsigned int mode; + unsigned int byte_count; + struct spi_message msg; + struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + spi_message_init(&msg); + + byte_count = length + 2; + + if (bdata->ubl_byte_delay_us == 0) + retval = syna_tcm_spi_alloc_mem(tcm_hcd, 2, 2); + else + retval = syna_tcm_spi_alloc_mem(tcm_hcd, byte_count, 2); + if (retval < 0) { + LOGE(&spi->dev, + "Failed to allocate memory\n"); + goto exit; + } + + buf[0] = (unsigned char)(addr >> 8) | 0x80; + buf[1] = (unsigned char)addr; + + if (bdata->ubl_byte_delay_us == 0) { + xfer[0].len = 2; + xfer[0].tx_buf = buf; + xfer[0].speed_hz = bdata->ubl_max_freq; + spi_message_add_tail(&xfer[0], &msg); + xfer[1].len = length; + xfer[1].rx_buf = data; + if (bdata->block_delay_us) + xfer[1].delay_usecs = bdata->block_delay_us; + xfer[1].speed_hz = bdata->ubl_max_freq; + spi_message_add_tail(&xfer[1], &msg); + } else { + for (idx = 0; idx < byte_count; idx++) { + xfer[idx].len = 1; + if (idx < 2) + xfer[idx].tx_buf = &buf[idx]; + else + xfer[idx].rx_buf = &data[idx - 2]; + xfer[idx].delay_usecs = bdata->ubl_byte_delay_us; + if (bdata->block_delay_us && (idx == byte_count - 1)) + xfer[idx].delay_usecs = bdata->block_delay_us; + xfer[idx].speed_hz = bdata->ubl_max_freq; + spi_message_add_tail(&xfer[idx], &msg); + } + } + + mode = spi->mode; + spi->mode = SPI_MODE_3; + + retval = spi_sync(spi, &msg); + if (retval == 0) { + retval = length; + } else { + LOGE(&spi->dev, + "Failed to complete SPI transfer, error = %d\n", + retval); + } + + spi->mode = mode; + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_spi_rmi_write(struct syna_tcm_hcd *tcm_hcd, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned int mode; + unsigned int byte_count; + struct spi_message msg; + struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + spi_message_init(&msg); + + byte_count = length + 2; + + retval = syna_tcm_spi_alloc_mem(tcm_hcd, 1, byte_count); + if (retval < 0) { + LOGE(&spi->dev, + "Failed to allocate memory\n"); + goto exit; + } + + buf[0] = (unsigned char)(addr >> 8) & ~0x80; + buf[1] = (unsigned char)addr; + retval = secure_memcpy(&buf[2], + buf_size - 2, + data, + length, + length); + if (retval < 0) { + LOGE(&spi->dev, + "Failed to copy write data\n"); + goto exit; + } + + xfer[0].len = byte_count; + xfer[0].tx_buf = buf; + if (bdata->block_delay_us) + xfer[0].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[0], &msg); + + mode = spi->mode; + spi->mode = SPI_MODE_3; + + retval = spi_sync(spi, &msg); + if (retval == 0) { + retval = length; + } else { + LOGE(&spi->dev, + "Failed to complete SPI transfer, error = %d\n", + retval); + } + + spi->mode = mode; + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_spi_read(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, + unsigned int length) +{ + int retval; + unsigned int idx; + struct spi_message msg; + struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + spi_message_init(&msg); + + if (bdata->byte_delay_us == 0) + retval = syna_tcm_spi_alloc_mem(tcm_hcd, 1, length); + else + retval = syna_tcm_spi_alloc_mem(tcm_hcd, length, 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory\n"); + goto exit; + } + + if (bdata->byte_delay_us == 0) { + memset(buf, 0xff, length); + xfer[0].len = length; + xfer[0].tx_buf = buf; + xfer[0].rx_buf = data; + if (bdata->block_delay_us) + xfer[0].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[0], &msg); + } else { + buf[0] = 0xff; + for (idx = 0; idx < length; idx++) { + xfer[idx].len = 1; + xfer[idx].tx_buf = buf; + xfer[idx].rx_buf = &data[idx]; + xfer[idx].delay_usecs = bdata->byte_delay_us; + if (bdata->block_delay_us && (idx == length - 1)) + xfer[idx].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[idx], &msg); + } + } + + retval = spi_sync(spi, &msg); + if (retval == 0) { + retval = length; + } else { + LOGE(&spi->dev, + "Failed to complete SPI transfer, error = %d\n", + retval); + } + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_spi_write(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, + unsigned int length) +{ + int retval; + unsigned int idx; + struct spi_message msg; + struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + spi_message_init(&msg); + + if (bdata->byte_delay_us == 0) + retval = syna_tcm_spi_alloc_mem(tcm_hcd, 1, 0); + else + retval = syna_tcm_spi_alloc_mem(tcm_hcd, length, 0); + if (retval < 0) { + LOGE(&spi->dev, + "Failed to allocate memory\n"); + goto exit; + } + + if (bdata->byte_delay_us == 0) { + xfer[0].len = length; + xfer[0].tx_buf = data; + if (bdata->block_delay_us) + xfer[0].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[0], &msg); + } else { + for (idx = 0; idx < length; idx++) { + xfer[idx].len = 1; + xfer[idx].tx_buf = &data[idx]; + xfer[idx].delay_usecs = bdata->byte_delay_us; + if (bdata->block_delay_us && (idx == length - 1)) + xfer[idx].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[idx], &msg); + } + } + + retval = spi_sync(spi, &msg); + if (retval == 0) { + retval = length; + } else { + LOGE(&spi->dev, + "Failed to complete SPI transfer, error = %d\n", + retval); + } + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_spi_probe(struct spi_device *spi) +{ + int retval; + + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) { + LOGE(&spi->dev, + "Full duplex not supported by host\n"); + return -EIO; + } + + syna_tcm_spi_device = platform_device_alloc(PLATFORM_DRIVER_NAME, 0); + if (!syna_tcm_spi_device) { + LOGE(&spi->dev, + "Failed to allocate platform device\n"); + return -ENOMEM; + } + +#ifdef CONFIG_OF + hw_if.bdata = devm_kzalloc(&spi->dev, sizeof(*hw_if.bdata), GFP_KERNEL); + if (!hw_if.bdata) { + LOGE(&spi->dev, + "Failed to allocate memory for board data\n"); + return -ENOMEM; + } + parse_dt(&spi->dev, hw_if.bdata); +#else + hw_if.bdata = spi->dev.platform_data; +#endif + + switch (hw_if.bdata->spi_mode) { + case 0: + spi->mode = SPI_MODE_0; + break; + case 1: + spi->mode = SPI_MODE_1; + break; + case 2: + spi->mode = SPI_MODE_2; + break; + case 3: + spi->mode = SPI_MODE_3; + break; + } + + bus_io.type = BUS_SPI; + bus_io.read = syna_tcm_spi_read; + bus_io.write = syna_tcm_spi_write; + bus_io.rmi_read = syna_tcm_spi_rmi_read; + bus_io.rmi_write = syna_tcm_spi_rmi_write; + + hw_if.bus_io = &bus_io; + + spi->bits_per_word = 8; + + retval = spi_setup(spi); + if (retval < 0) { + LOGE(&spi->dev, + "Failed to set up SPI protocol driver\n"); + return retval; + } + + syna_tcm_spi_device->dev.parent = &spi->dev; + syna_tcm_spi_device->dev.platform_data = &hw_if; + + retval = platform_device_add(syna_tcm_spi_device); + if (retval < 0) { + LOGE(&spi->dev, + "Failed to add platform device\n"); + return retval; + } + + return 0; +} + +static int syna_tcm_spi_remove(struct spi_device *spi) +{ + syna_tcm_spi_device->dev.platform_data = NULL; + + platform_device_unregister(syna_tcm_spi_device); + + return 0; +} + +static const struct spi_device_id syna_tcm_id_table[] = { + {SPI_MODULE_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(spi, syna_tcm_id_table); + +#ifdef CONFIG_OF +static struct of_device_id syna_tcm_of_match_table[] = { + { + .compatible = "synaptics,tcm-spi", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, syna_tcm_of_match_table); +#else +#define syna_tcm_of_match_table NULL +#endif + +static struct spi_driver syna_tcm_spi_driver = { + .driver = { + .name = SPI_MODULE_NAME, + .owner = THIS_MODULE, + .of_match_table = syna_tcm_of_match_table, + }, + .probe = syna_tcm_spi_probe, + .remove = syna_tcm_spi_remove, + .id_table = syna_tcm_id_table, +}; + +int syna_tcm_bus_init(void) +{ + return spi_register_driver(&syna_tcm_spi_driver); +} +EXPORT_SYMBOL(syna_tcm_bus_init); + +void syna_tcm_bus_exit(void) +{ + kfree(buf); + + kfree(xfer); + + spi_unregister_driver(&syna_tcm_spi_driver); + + return; +} +EXPORT_SYMBOL(syna_tcm_bus_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM SPI Bus Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_testing.c b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_testing.c new file mode 100644 index 000000000000..ebd5d232d823 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_testing.c @@ -0,0 +1,1958 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * + * 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. + */ + +#include +#include "synaptics_tcm_core.h" +#include "synaptics_tcm_testing.h" + +#define SYSFS_DIR_NAME "testing" + +#define REPORT_TIMEOUT_MS 500 + +#define testing_sysfs_show(t_name) \ +static ssize_t testing_sysfs_##t_name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + int retval; \ + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; \ +\ + mutex_lock(&tcm_hcd->extif_mutex); \ +\ + retval = testing_##t_name(); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to do "#t_name" test\n"); \ + goto exit; \ + } \ +\ + retval = snprintf(buf, PAGE_SIZE, \ + "%s\n", \ + testing_hcd->result ? "Passed" : "Failed"); \ +\ +exit: \ + mutex_unlock(&tcm_hcd->extif_mutex); \ +\ + return retval; \ +} + +enum test_code { + TEST_TRX_TRX_SHORTS = 0, + TEST_TRX_SENSOR_OPENS = 1, + TEST_TRX_GROUND_SHORTS = 2, + TEST_DYNAMIC_RANGE = 7, + TEST_OPEN_SHORT_DETECTOR = 8, + TEST_NOISE = 10, + TEST_PT11 = 11, + TEST_PT12 = 12, + TEST_PT13 = 13, + TEST_DYNAMIC_RANGE_DOZE = 14, + TEST_NOISE_DOZE = 15, +}; + +struct testing_hcd { + bool result; + unsigned char report_type; + unsigned int report_index; + unsigned int num_of_reports; + struct kobject *sysfs_dir; + struct syna_tcm_buffer out; + struct syna_tcm_buffer resp; + struct syna_tcm_buffer report; + struct syna_tcm_buffer process; + struct syna_tcm_buffer output; + struct syna_tcm_hcd *tcm_hcd; + int (*collect_reports)(enum report_type report_type, + unsigned int num_of_reports); +}; + +DECLARE_COMPLETION(report_complete); + +DECLARE_COMPLETION(testing_remove_complete); + +static struct testing_hcd *testing_hcd; + +static int testing_dynamic_range(void); + +static int testing_dynamic_range_lpwg(void); + +static int testing_dynamic_range_doze(void); + +static int testing_noise(void); + +static int testing_noise_lpwg(void); + +static int testing_noise_doze(void); + +static int testing_open_short_detector(void); + +static int testing_pt11(void); + +static int testing_pt12(void); + +static int testing_pt13(void); + +static int testing_reset_open(void); + +static int testing_lockdown(void); + +static int testing_trx(enum test_code test_code); + +SHOW_PROTOTYPE(testing, dynamic_range) +SHOW_PROTOTYPE(testing, dynamic_range_lpwg) +SHOW_PROTOTYPE(testing, dynamic_range_doze) +SHOW_PROTOTYPE(testing, noise) +SHOW_PROTOTYPE(testing, noise_lpwg) +SHOW_PROTOTYPE(testing, noise_doze) +SHOW_PROTOTYPE(testing, open_short_detector) +SHOW_PROTOTYPE(testing, pt11) +SHOW_PROTOTYPE(testing, pt12) +SHOW_PROTOTYPE(testing, pt13) +SHOW_PROTOTYPE(testing, reset_open) +SHOW_PROTOTYPE(testing, lockdown) +SHOW_PROTOTYPE(testing, trx_trx_shorts) +SHOW_PROTOTYPE(testing, trx_sensor_opens) +SHOW_PROTOTYPE(testing, trx_ground_shorts) +SHOW_PROTOTYPE(testing, size) + +static struct device_attribute *attrs[] = { + ATTRIFY(dynamic_range), + ATTRIFY(dynamic_range_lpwg), + ATTRIFY(dynamic_range_doze), + ATTRIFY(noise), + ATTRIFY(noise_lpwg), + ATTRIFY(noise_doze), + ATTRIFY(open_short_detector), + ATTRIFY(pt11), + ATTRIFY(pt12), + ATTRIFY(pt13), + ATTRIFY(reset_open), + ATTRIFY(lockdown), + ATTRIFY(trx_trx_shorts), + ATTRIFY(trx_sensor_opens), + ATTRIFY(trx_ground_shorts), + ATTRIFY(size), +}; + +static ssize_t testing_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static struct bin_attribute bin_attr = { + .attr = { + .name = "data", + .mode = S_IRUGO, + }, + .size = 0, + .read = testing_sysfs_data_show, +}; + +testing_sysfs_show(dynamic_range) + +testing_sysfs_show(dynamic_range_lpwg) + +testing_sysfs_show(dynamic_range_doze) + +testing_sysfs_show(noise) + +testing_sysfs_show(noise_lpwg) + +testing_sysfs_show(noise_doze) + +testing_sysfs_show(open_short_detector) + +testing_sysfs_show(pt11) + +testing_sysfs_show(pt12) + +testing_sysfs_show(pt13) + +testing_sysfs_show(reset_open) + +testing_sysfs_show(lockdown) + +static ssize_t testing_sysfs_trx_trx_shorts_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = testing_trx(TEST_TRX_TRX_SHORTS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do TRX-TRX shorts test\n"); + goto exit; + } + + retval = snprintf(buf, PAGE_SIZE, + "%s\n", + testing_hcd->result ? "Passed" : "Failed"); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t testing_sysfs_trx_sensor_opens_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = testing_trx(TEST_TRX_SENSOR_OPENS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do TRX-sensor opens test\n"); + goto exit; + } + + retval = snprintf(buf, PAGE_SIZE, + "%s\n", + testing_hcd->result ? "Passed" : "Failed"); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t testing_sysfs_trx_ground_shorts_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = testing_trx(TEST_TRX_GROUND_SHORTS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do TRX-ground shorts test\n"); + goto exit; + } + + retval = snprintf(buf, PAGE_SIZE, + "%s\n", + testing_hcd->result ? "Passed" : "Failed"); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t testing_sysfs_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + LOCK_BUFFER(testing_hcd->output); + + retval = snprintf(buf, PAGE_SIZE, + "%u\n", + testing_hcd->output.data_length); + + UNLOCK_BUFFER(testing_hcd->output); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t testing_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int readlen; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + LOCK_BUFFER(testing_hcd->output); + + readlen = MIN(count, testing_hcd->output.data_length - pos); + + retval = secure_memcpy(buf, + count, + &testing_hcd->output.buf[pos], + testing_hcd->output.buf_size - pos, + readlen); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy report data\n"); + } else { + retval = readlen; + } + + UNLOCK_BUFFER(testing_hcd->output); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static int testing_run_prod_test_item(enum test_code test_code) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + if (tcm_hcd->features.dual_firmware && + tcm_hcd->id_info.mode != MODE_PRODUCTION_TEST) { + retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_PRODUCTION_TEST); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run production test firmware\n"); + return retval; + } + } else if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application firmware not running\n"); + return -ENODEV; + } + + LOCK_BUFFER(testing_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->out, + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd->out.buf\n"); + UNLOCK_BUFFER(testing_hcd->out); + return retval; + } + + testing_hcd->out.buf[0] = test_code; + + LOCK_BUFFER(testing_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_PRODUCTION_TEST, + testing_hcd->out.buf, + 1, + &testing_hcd->resp.buf, + &testing_hcd->resp.buf_size, + &testing_hcd->resp.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_PRODUCTION_TEST)); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + return retval; + } + + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + + return 0; +} + +static int testing_collect_reports(enum report_type report_type, + unsigned int num_of_reports) +{ + int retval; + bool completed; + unsigned int timeout; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + testing_hcd->report_index = 0; + testing_hcd->report_type = report_type; + testing_hcd->num_of_reports = num_of_reports; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)) + reinit_completion(&report_complete); +#else + INIT_COMPLETION(report_complete); +#endif + + LOCK_BUFFER(testing_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->out, + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd->out.buf\n"); + UNLOCK_BUFFER(testing_hcd->out); + goto exit; + } + + testing_hcd->out.buf[0] = testing_hcd->report_type; + + LOCK_BUFFER(testing_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_ENABLE_REPORT, + testing_hcd->out.buf, + 1, + &testing_hcd->resp.buf, + &testing_hcd->resp.buf_size, + &testing_hcd->resp.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_ENABLE_REPORT)); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + goto exit; + } + + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + + completed = false; + timeout = REPORT_TIMEOUT_MS * num_of_reports; + + retval = wait_for_completion_timeout(&report_complete, + msecs_to_jiffies(timeout)); + if (retval == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Timed out waiting for report collection\n"); + } else { + completed = true; + } + + LOCK_BUFFER(testing_hcd->out); + + testing_hcd->out.buf[0] = testing_hcd->report_type; + + LOCK_BUFFER(testing_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_DISABLE_REPORT, + testing_hcd->out.buf, + 1, + &testing_hcd->resp.buf, + &testing_hcd->resp.buf_size, + &testing_hcd->resp.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_DISABLE_REPORT)); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + goto exit; + } + + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + + if (completed) + retval = 0; + else + retval = -EIO; + +exit: + testing_hcd->report_type = 0; + + return retval; +} + +static void testing_get_frame_size_words(unsigned int *size, bool image_only) +{ + unsigned int rows; + unsigned int cols; + unsigned int hybrid; + unsigned int buttons; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + hybrid = le2_to_uint(app_info->has_hybrid_data); + buttons = le2_to_uint(app_info->num_of_buttons); + + *size = rows * cols; + + if (!image_only) { + if (hybrid) + *size += rows + cols; + *size += buttons; + } + + return; +} + +static void testing_doze_frame_output(unsigned int rows, unsigned int cols) +{ + int retval; + unsigned int data_size; + unsigned int header_size; + unsigned int output_size; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + header_size = 2; + + data_size = rows * cols; + + if (le2_to_uint(app_info->num_of_buttons)) + data_size++; + + output_size = header_size + data_size * 2; + + LOCK_BUFFER(testing_hcd->output); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->output, + output_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd->output.buf\n"); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + testing_hcd->output.buf[0] = rows; + testing_hcd->output.buf[1] = cols; + + output_size = header_size; + + LOCK_BUFFER(testing_hcd->resp); + + retval = secure_memcpy(testing_hcd->output.buf + header_size, + testing_hcd->output.buf_size - header_size, + testing_hcd->resp.buf, + testing_hcd->resp.buf_size, + testing_hcd->resp.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy test data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + output_size += testing_hcd->resp.data_length; + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_hcd->output.data_length = output_size; + + UNLOCK_BUFFER(testing_hcd->output); + + return; +} + +static void testing_standard_frame_output(bool image_only) +{ + int retval; + unsigned int data_size; + unsigned int header_size; + unsigned int output_size; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + testing_get_frame_size_words(&data_size, image_only); + + header_size = sizeof(app_info->num_of_buttons) + + sizeof(app_info->num_of_image_rows) + + sizeof(app_info->num_of_image_cols) + + sizeof(app_info->has_hybrid_data); + + output_size = header_size + data_size * 2; + + LOCK_BUFFER(testing_hcd->output); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->output, + output_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd->output.buf\n"); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + retval = secure_memcpy(testing_hcd->output.buf, + testing_hcd->output.buf_size, + &app_info->num_of_buttons[0], + header_size, + header_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy header data\n"); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + output_size = header_size; + + LOCK_BUFFER(testing_hcd->resp); + + retval = secure_memcpy(testing_hcd->output.buf + header_size, + testing_hcd->output.buf_size - header_size, + testing_hcd->resp.buf, + testing_hcd->resp.buf_size, + testing_hcd->resp.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy test data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + output_size += testing_hcd->resp.data_length; + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_hcd->output.data_length = output_size; + + UNLOCK_BUFFER(testing_hcd->output); + + return; +} + +static int testing_dynamic_range_doze(void) +{ + int retval; + unsigned char *buf; + unsigned int idx; + unsigned int row; + unsigned int col; + unsigned int data; + unsigned int rows; + unsigned int cols; + unsigned int data_size; + unsigned int limits_rows; + unsigned int limits_cols; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + cols = le2_to_uint(app_info->num_of_image_cols); + + retval = testing_run_prod_test_item(TEST_DYNAMIC_RANGE_DOZE); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + data_size = testing_hcd->resp.data_length / 2; + + if (le2_to_uint(app_info->num_of_buttons)) + data_size--; + + if (data_size % cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid max number of rows per burst\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + rows = data_size / cols; + + limits_rows = sizeof(drt_hi_limits) / sizeof(drt_hi_limits[0]); + limits_cols = sizeof(drt_hi_limits[0]) / sizeof(drt_hi_limits[0][0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = sizeof(drt_lo_limits) / sizeof(drt_lo_limits[0]); + limits_cols = sizeof(drt_lo_limits[0]) / sizeof(drt_lo_limits[0][0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + idx = 0; + buf = testing_hcd->resp.buf; + testing_hcd->result = true; + + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + data = le2_to_uint(&buf[idx * 2]); + if (data > drt_hi_limits[row][col] || + data < drt_lo_limits[row][col]) { + testing_hcd->result = false; + break; + } + idx++; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_doze_frame_output(rows, cols); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + +static int testing_dynamic_range_lpwg(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_IN_WAKEUP_GESTURE_MODE, + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable wakeup gesture mode\n"); + return retval; + } + + retval = testing_dynamic_range(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do dynamic range test\n"); + return retval; + } + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_IN_WAKEUP_GESTURE_MODE, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to disable wakeup gesture mode\n"); + return retval; + } + + return 0; +} + +static int testing_dynamic_range(void) +{ + int retval; + unsigned char *buf; + unsigned int idx; + unsigned int row; + unsigned int col; + unsigned int data; + unsigned int rows; + unsigned int cols; + unsigned int limits_rows; + unsigned int limits_cols; + unsigned int frame_size_words; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + + testing_get_frame_size_words(&frame_size_words, false); + + retval = testing_run_prod_test_item(TEST_DYNAMIC_RANGE); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + if (frame_size_words != testing_hcd->resp.data_length / 2) { + LOGE(tcm_hcd->pdev->dev.parent, + "Frame size mismatch\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = sizeof(drt_hi_limits) / sizeof(drt_hi_limits[0]); + limits_cols = sizeof(drt_hi_limits[0]) / sizeof(drt_hi_limits[0][0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = sizeof(drt_lo_limits) / sizeof(drt_lo_limits[0]); + limits_cols = sizeof(drt_lo_limits[0]) / sizeof(drt_lo_limits[0][0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + idx = 0; + buf = testing_hcd->resp.buf; + testing_hcd->result = true; + + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + data = le2_to_uint(&buf[idx * 2]); + if (data > drt_hi_limits[row][col] || + data < drt_lo_limits[row][col]) { + testing_hcd->result = false; + break; + } + idx++; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_standard_frame_output(false); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + +static int testing_noise_doze(void) +{ + int retval; + short data; + unsigned char *buf; + unsigned int idx; + unsigned int row; + unsigned int col; + unsigned int rows; + unsigned int cols; + unsigned int data_size; + unsigned int limits_rows; + unsigned int limits_cols; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + cols = le2_to_uint(app_info->num_of_image_cols); + + retval = testing_run_prod_test_item(TEST_NOISE_DOZE); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + data_size = testing_hcd->resp.data_length / 2; + + if (le2_to_uint(app_info->num_of_buttons)) + data_size--; + + if (data_size % cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid max number of rows per burst\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + rows = data_size / cols; + + limits_rows = sizeof(noise_limits) / sizeof(noise_limits[0]); + limits_cols = sizeof(noise_limits[0]) / sizeof(noise_limits[0][0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + idx = 0; + buf = testing_hcd->resp.buf; + testing_hcd->result = true; + + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + data = (short)le2_to_uint(&buf[idx * 2]); + if (data > noise_limits[row][col]) { + testing_hcd->result = false; + break; + } + idx++; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_doze_frame_output(rows, cols); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + +static int testing_noise_lpwg(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_IN_WAKEUP_GESTURE_MODE, + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable wakeup gesture mode\n"); + return retval; + } + + retval = testing_noise(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do noise test\n"); + return retval; + } + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_IN_WAKEUP_GESTURE_MODE, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to disable wakeup gesture mode\n"); + return retval; + } + + return 0; +} + +static int testing_noise(void) +{ + int retval; + short data; + unsigned char *buf; + unsigned int idx; + unsigned int row; + unsigned int col; + unsigned int rows; + unsigned int cols; + unsigned int limits_rows; + unsigned int limits_cols; + unsigned int frame_size_words; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + + testing_get_frame_size_words(&frame_size_words, false); + + retval = testing_run_prod_test_item(TEST_NOISE); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + if (frame_size_words != testing_hcd->resp.data_length / 2) { + LOGE(tcm_hcd->pdev->dev.parent, + "Frame size mismatch\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = sizeof(noise_limits) / sizeof(noise_limits[0]); + limits_cols = sizeof(noise_limits[0]) / sizeof(noise_limits[0][0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + idx = 0; + buf = testing_hcd->resp.buf; + testing_hcd->result = true; + + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + data = (short)le2_to_uint(&buf[idx * 2]); + if (data > noise_limits[row][col]) { + testing_hcd->result = false; + break; + } + idx++; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_standard_frame_output(false); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + +static void testing_open_short_detector_output(void) +{ + int retval; + unsigned int rows; + unsigned int cols; + unsigned int data_size; + unsigned int header_size; + unsigned int output_size; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + data_size = (rows * cols + 7) / 8; + + header_size = sizeof(app_info->num_of_buttons) + + sizeof(app_info->num_of_image_rows) + + sizeof(app_info->num_of_image_cols) + + sizeof(app_info->has_hybrid_data); + + output_size = header_size + data_size * 2; + + LOCK_BUFFER(testing_hcd->output); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->output, + output_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd->output.buf\n"); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + retval = secure_memcpy(testing_hcd->output.buf, + testing_hcd->output.buf_size, + &app_info->num_of_buttons[0], + header_size, + header_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy header data\n"); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + output_size = header_size; + + LOCK_BUFFER(testing_hcd->resp); + + retval = secure_memcpy(testing_hcd->output.buf + header_size, + testing_hcd->output.buf_size - header_size, + testing_hcd->resp.buf, + testing_hcd->resp.buf_size, + testing_hcd->resp.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy test data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + output_size += testing_hcd->resp.data_length; + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_hcd->output.data_length = output_size; + + UNLOCK_BUFFER(testing_hcd->output); + + return; +} + +static int testing_open_short_detector(void) +{ + int retval; + unsigned int bit; + unsigned int byte; + unsigned int row; + unsigned int col; + unsigned int rows; + unsigned int cols; + unsigned int data_size; + unsigned char *data; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + data_size = (rows * cols + 7) / 8; + + retval = testing_run_prod_test_item(TEST_OPEN_SHORT_DETECTOR); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + if (data_size * 2 != testing_hcd->resp.data_length) { + LOGE(tcm_hcd->pdev->dev.parent, + "Data size mismatch\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + testing_hcd->result = true; + + bit = 0; + byte = 0; + data = &testing_hcd->resp.buf[0]; + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + if (data[byte] & (1 << bit)) { + testing_hcd->result = false; + break; + } + if (bit++ > 7) { + bit = 0; + byte++; + } + } + } + + if (testing_hcd->result == true) { + bit = 0; + byte = 0; + data = &testing_hcd->resp.buf[data_size]; + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + if (data[byte] & (1 << bit)) { + testing_hcd->result = false; + break; + } + if (bit++ > 7) { + bit = 0; + byte++; + } + } + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_open_short_detector_output(); + + retval = 0; + +exit: + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + + return retval; +} + +static int testing_pt11(void) +{ + int retval; + short data; + unsigned char *buf; + unsigned int idx; + unsigned int row; + unsigned int col; + unsigned int rows; + unsigned int cols; + unsigned int limits_rows; + unsigned int limits_cols; + unsigned int image_size_words; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + + testing_get_frame_size_words(&image_size_words, true); + + retval = testing_run_prod_test_item(TEST_PT11); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + if (image_size_words != testing_hcd->resp.data_length / 2) { + LOGE(tcm_hcd->pdev->dev.parent, + "Image size mismatch\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = sizeof(pt11_hi_limits) / sizeof(pt11_hi_limits[0]); + limits_cols = sizeof(pt11_hi_limits[0]) / sizeof(pt11_hi_limits[0][0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = sizeof(pt11_lo_limits) / sizeof(pt11_lo_limits[0]); + limits_cols = sizeof(pt11_lo_limits[0]) / sizeof(pt11_lo_limits[0][0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + idx = 0; + buf = testing_hcd->resp.buf; + testing_hcd->result = true; + + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + data = (short)le2_to_uint(&buf[idx * 2]); + if (data > pt11_hi_limits[row][col] || + data < pt11_lo_limits[row][col]) { + testing_hcd->result = false; + break; + } + idx++; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_standard_frame_output(true); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + +static int testing_pt12(void) +{ + int retval; + short data; + unsigned char *buf; + unsigned int idx; + unsigned int row; + unsigned int col; + unsigned int rows; + unsigned int cols; + unsigned int limits_rows; + unsigned int limits_cols; + unsigned int image_size_words; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + + testing_get_frame_size_words(&image_size_words, true); + + retval = testing_run_prod_test_item(TEST_PT12); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + if (image_size_words != testing_hcd->resp.data_length / 2) { + LOGE(tcm_hcd->pdev->dev.parent, + "Image size mismatch\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = sizeof(pt12_limits) / sizeof(pt12_limits[0]); + limits_cols = sizeof(pt12_limits[0]) / sizeof(pt12_limits[0][0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + idx = 0; + buf = testing_hcd->resp.buf; + testing_hcd->result = true; + + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + data = (short)le2_to_uint(&buf[idx * 2]); + if (data < pt12_limits[row][col]) { + testing_hcd->result = false; + break; + } + idx++; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_standard_frame_output(true); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + +static int testing_pt13(void) +{ + int retval; + short data; + unsigned char *buf; + unsigned int idx; + unsigned int row; + unsigned int col; + unsigned int rows; + unsigned int cols; + unsigned int limits_rows; + unsigned int limits_cols; + unsigned int image_size_words; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + + testing_get_frame_size_words(&image_size_words, true); + + retval = testing_run_prod_test_item(TEST_PT13); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + if (image_size_words != testing_hcd->resp.data_length / 2) { + LOGE(tcm_hcd->pdev->dev.parent, + "Image size mismatch\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = sizeof(pt13_limits) / sizeof(pt13_limits[0]); + limits_cols = sizeof(pt13_limits[0]) / sizeof(pt13_limits[0][0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + idx = 0; + buf = testing_hcd->resp.buf; + testing_hcd->result = true; + + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + data = (short)le2_to_uint(&buf[idx * 2]); + if (data < pt13_limits[row][col]) { + testing_hcd->result = false; + break; + } + idx++; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_standard_frame_output(true); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + +static int testing_reset_open(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (bdata->reset_gpio < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Hardware reset unavailable\n"); + return -EINVAL; + } + + mutex_lock(&tcm_hcd->reset_mutex); + + tcm_hcd->update_watchdog(tcm_hcd, false); + + gpio_set_value(bdata->reset_gpio, bdata->reset_on_state); + msleep(bdata->reset_active_ms); + gpio_set_value(bdata->reset_gpio, !bdata->reset_on_state); + msleep(bdata->reset_delay_ms); + + tcm_hcd->update_watchdog(tcm_hcd, true); + + mutex_unlock(&tcm_hcd->reset_mutex); + + if (tcm_hcd->id_info.mode == MODE_APPLICATION) { + retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_BOOTLOADER); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enter bootloader mode\n"); + return retval; + } + } else { + retval = tcm_hcd->identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + goto run_app_firmware; + } + } + + if (tcm_hcd->boot_info.last_reset_reason == reset_open_limit) + testing_hcd->result = true; + else + testing_hcd->result = false; + + retval = 0; + +run_app_firmware: + if (tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware\n"); + } + + return retval; +} + +static void testing_lockdown_output(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + LOCK_BUFFER(testing_hcd->output); + LOCK_BUFFER(testing_hcd->resp); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->output, + testing_hcd->resp.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd->output.buf\n"); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + retval = secure_memcpy(testing_hcd->output.buf, + testing_hcd->output.buf_size, + testing_hcd->resp.buf, + testing_hcd->resp.buf_size, + testing_hcd->resp.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy test data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + testing_hcd->output.data_length = testing_hcd->resp.data_length; + + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); + + return; +} + +static int testing_lockdown(void) +{ + int retval; + unsigned int idx; + unsigned int lockdown_size; + unsigned int limits_size; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + if (tcm_hcd->read_flash_data == NULL) { + LOGE(tcm_hcd->pdev->dev.parent, + "Unable to read from flash\n"); + return -EINVAL; + } + + LOCK_BUFFER(testing_hcd->resp); + + retval = tcm_hcd->read_flash_data(CUSTOM_OTP, true, &testing_hcd->resp); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read lockdown data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + return retval; + } + + lockdown_size = testing_hcd->resp.data_length; + + limits_size = sizeof(lockdown_limits) / sizeof(*lockdown_limits); + + if (lockdown_size != limits_size) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + return -EINVAL; + } + + testing_hcd->result = true; + + for (idx = 0; idx < lockdown_size; idx++) { + if (testing_hcd->resp.buf[idx] != lockdown_limits[idx]) { + testing_hcd->result = false; + break; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_lockdown_output(); + + return 0; +} + +static void testing_trx_output(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + LOCK_BUFFER(testing_hcd->output); + LOCK_BUFFER(testing_hcd->resp); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->output, + testing_hcd->resp.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd->output.buf\n"); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + retval = secure_memcpy(testing_hcd->output.buf, + testing_hcd->output.buf_size, + testing_hcd->resp.buf, + testing_hcd->resp.buf_size, + testing_hcd->resp.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy test data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + testing_hcd->output.data_length = testing_hcd->resp.data_length; + + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); + + return; +} + +static int testing_trx(enum test_code test_code) +{ + int retval; + unsigned char pass_vector; + unsigned int idx; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + switch (test_code) { + case TEST_TRX_TRX_SHORTS: + case TEST_TRX_GROUND_SHORTS: + pass_vector = 0xff; + break; + case TEST_TRX_SENSOR_OPENS: + pass_vector = 0x00; + break; + default: + return -EINVAL; + } + + retval = testing_run_prod_test_item(test_code); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + testing_hcd->result = true; + + for (idx = 0; idx < testing_hcd->resp.data_length; idx++) { + if (testing_hcd->resp.buf[idx] != pass_vector) { + testing_hcd->result = false; + break; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_trx_output(); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + +static void testing_report(void) +{ + int retval; + unsigned int offset; + unsigned int report_size; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + report_size = tcm_hcd->report.buffer.data_length; + + LOCK_BUFFER(testing_hcd->report); + + if (testing_hcd->report_index == 0) { + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->report, + report_size * testing_hcd->num_of_reports); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd->report.buf\n"); + UNLOCK_BUFFER(testing_hcd->report); + return; + } + } + + if (testing_hcd->report_index < testing_hcd->num_of_reports) { + offset = report_size * testing_hcd->report_index; + + retval = secure_memcpy(testing_hcd->report.buf + offset, + testing_hcd->report.buf_size - offset, + tcm_hcd->report.buffer.buf, + tcm_hcd->report.buffer.buf_size, + tcm_hcd->report.buffer.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy report data\n"); + UNLOCK_BUFFER(testing_hcd->report); + return; + } + + testing_hcd->report_index++; + testing_hcd->report.data_length += report_size; + } + + UNLOCK_BUFFER(testing_hcd->report); + + if (testing_hcd->report_index == testing_hcd->num_of_reports) + complete(&report_complete); + + return; +} + +static int testing_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + int idx; + + testing_hcd = kzalloc(sizeof(*testing_hcd), GFP_KERNEL); + if (!testing_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd\n"); + return -ENOMEM; + } + + testing_hcd->tcm_hcd = tcm_hcd; + + testing_hcd->collect_reports = testing_collect_reports; + + INIT_BUFFER(testing_hcd->out, false); + INIT_BUFFER(testing_hcd->resp, false); + INIT_BUFFER(testing_hcd->report, false); + INIT_BUFFER(testing_hcd->process, false); + INIT_BUFFER(testing_hcd->output, false); + + testing_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME, + tcm_hcd->sysfs_dir); + if (!testing_hcd->sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs directory\n"); + retval = -EINVAL; + goto err_sysfs_create_dir; + } + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + retval = sysfs_create_file(testing_hcd->sysfs_dir, + &(*attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs file\n"); + goto err_sysfs_create_file; + } + } + + retval = sysfs_create_bin_file(testing_hcd->sysfs_dir, &bin_attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs bin file\n"); + goto err_sysfs_create_bin_file; + } + + return 0; + +err_sysfs_create_bin_file: +err_sysfs_create_file: + for (idx--; idx >= 0; idx--) + sysfs_remove_file(testing_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(testing_hcd->sysfs_dir); + +err_sysfs_create_dir: + RELEASE_BUFFER(testing_hcd->output); + RELEASE_BUFFER(testing_hcd->process); + RELEASE_BUFFER(testing_hcd->report); + RELEASE_BUFFER(testing_hcd->resp); + RELEASE_BUFFER(testing_hcd->out); + + kfree(testing_hcd); + testing_hcd = NULL; + + return retval; +} + +static int testing_remove(struct syna_tcm_hcd *tcm_hcd) +{ + int idx; + + if (!testing_hcd) + goto exit; + + sysfs_remove_bin_file(testing_hcd->sysfs_dir, &bin_attr); + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) + sysfs_remove_file(testing_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(testing_hcd->sysfs_dir); + + RELEASE_BUFFER(testing_hcd->output); + RELEASE_BUFFER(testing_hcd->process); + RELEASE_BUFFER(testing_hcd->report); + RELEASE_BUFFER(testing_hcd->resp); + RELEASE_BUFFER(testing_hcd->out); + + kfree(testing_hcd); + testing_hcd = NULL; + +exit: + complete(&testing_remove_complete); + + return 0; +} + +static int testing_reset(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!testing_hcd) { + retval = testing_init(tcm_hcd); + return retval; + } + + return 0; +} + +static int testing_syncbox(struct syna_tcm_hcd *tcm_hcd) +{ + if (!testing_hcd) + return 0; + + if (tcm_hcd->report.id == testing_hcd->report_type) + testing_report(); + + return 0; +} + +static struct syna_tcm_module_cb testing_module = { + .type = TCM_TESTING, + .init = testing_init, + .remove = testing_remove, + .syncbox = testing_syncbox, + .asyncbox = NULL, + .reset = testing_reset, + .suspend = NULL, + .resume = NULL, + .early_suspend = NULL, +}; + +static int __init testing_module_init(void) +{ + return syna_tcm_add_module(&testing_module, true); +} + +static void __exit testing_module_exit(void) +{ + syna_tcm_add_module(&testing_module, false); + + wait_for_completion(&testing_remove_complete); + + return; +} + +module_init(testing_module_init); +module_exit(testing_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Testing Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_testing.h b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_testing.h new file mode 100644 index 000000000000..9734955aae9d --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_testing.h @@ -0,0 +1,302 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * + * 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. + */ + +#ifndef _SYNAPTICS_TCM_TESTING_H_ +#define _SYNAPTICS_TCM_TESTING_H_ + +static const unsigned short drt_hi_limits[32][32] = { + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, +}; + +static const unsigned short drt_lo_limits[32][32] = { + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, +}; + +static const unsigned short noise_limits[32][32] = { + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, +}; + +static const short pt11_hi_limits[32][32] = { + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +}; + +static const short pt11_lo_limits[32][32] = { + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, +}; + +static const short pt12_limits[32][32] = { + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, + {-10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10, -10}, +}; + +static const short pt13_limits[32][32] = { + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, + {-100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100, -100}, +}; + +static const unsigned char lockdown_limits[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, +}; + +static const unsigned char reset_open_limit = 0x13; + +#endif diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_touch.c b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_touch.c new file mode 100644 index 000000000000..5ca1a0f20e1d --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_touch.c @@ -0,0 +1,1271 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * + * 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. + */ + +#include +#include +#include "synaptics_tcm_core.h" + +#define TYPE_B_PROTOCOL + +#define USE_DEFAULT_TOUCH_REPORT_CONFIG + +#define TOUCH_REPORT_CONFIG_SIZE 128 + +enum touch_status { + LIFT = 0, + FINGER = 1, + GLOVED_FINGER = 2, + NOP = -1, +}; + +enum touch_report_code { + TOUCH_END = 0, + TOUCH_FOREACH_ACTIVE_OBJECT, + TOUCH_FOREACH_OBJECT, + TOUCH_FOREACH_END, + TOUCH_PAD_TO_NEXT_BYTE, + TOUCH_TIMESTAMP, + TOUCH_OBJECT_N_INDEX, + TOUCH_OBJECT_N_CLASSIFICATION, + TOUCH_OBJECT_N_X_POSITION, + TOUCH_OBJECT_N_Y_POSITION, + TOUCH_OBJECT_N_Z, + TOUCH_OBJECT_N_X_WIDTH, + TOUCH_OBJECT_N_Y_WIDTH, + TOUCH_OBJECT_N_TX_POSITION_TIXELS, + TOUCH_OBJECT_N_RX_POSITION_TIXELS, + TOUCH_0D_BUTTONS_STATE, + TOUCH_GESTURE_DOUBLE_TAP, + TOUCH_FRAME_RATE, + TOUCH_POWER_IM, + TOUCH_CID_IM, + TOUCH_RAIL_IM, + TOUCH_CID_VARIANCE_IM, + TOUCH_NSM_FREQUENCY, + TOUCH_NSM_STATE, + TOUCH_NUM_OF_ACTIVE_OBJECTS, + TOUCH_NUM_OF_CPU_CYCLES_USED_SINCE_LAST_FRAME, + TOUCH_TUNING_GAUSSIAN_WIDTHS = 0x80, + TOUCH_TUNING_SMALL_OBJECT_PARAMS, + TOUCH_TUNING_0D_BUTTONS_VARIANCE, +}; + +struct object_data { + unsigned char status; + unsigned int x_pos; + unsigned int y_pos; + unsigned int x_width; + unsigned int y_width; + unsigned int z; + unsigned int tx_pos; + unsigned int rx_pos; +}; + +struct input_params { + unsigned int max_x; + unsigned int max_y; + unsigned int max_objects; +}; + +struct touch_data { + struct object_data *object_data; + unsigned int timestamp; + unsigned int buttons_state; + unsigned int gesture_double_tap; + unsigned int frame_rate; + unsigned int power_im; + unsigned int cid_im; + unsigned int rail_im; + unsigned int cid_variance_im; + unsigned int nsm_frequency; + unsigned int nsm_state; + unsigned int num_of_active_objects; + unsigned int num_of_cpu_cycles; +}; + +struct touch_hcd { + bool irq_wake; + bool report_touch; + bool suspend_touch; + unsigned char *prev_status; + unsigned int max_x; + unsigned int max_y; + unsigned int max_objects; + struct mutex report_mutex; + struct input_dev *input_dev; + struct touch_data touch_data; + struct input_params input_params; + struct syna_tcm_buffer out; + struct syna_tcm_buffer resp; + struct syna_tcm_hcd *tcm_hcd; +}; + +DECLARE_COMPLETION(touch_remove_complete); + +static struct touch_hcd *touch_hcd; + +/** + * touch_free_objects() - Free all touch objects + * + * Report finger lift events to the input subsystem for all touch objects. + */ +static void touch_free_objects(void) +{ +#ifdef TYPE_B_PROTOCOL + unsigned int idx; +#endif + + if (touch_hcd->input_dev == NULL) + return; + + mutex_lock(&touch_hcd->report_mutex); + +#ifdef TYPE_B_PROTOCOL + for (idx = 0; idx < touch_hcd->max_objects; idx++) { + input_mt_slot(touch_hcd->input_dev, idx); + input_mt_report_slot_state(touch_hcd->input_dev, + MT_TOOL_FINGER, 0); + } +#endif + input_report_key(touch_hcd->input_dev, + BTN_TOUCH, 0); + input_report_key(touch_hcd->input_dev, + BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(touch_hcd->input_dev); +#endif + input_sync(touch_hcd->input_dev); + + mutex_unlock(&touch_hcd->report_mutex); + + return; +} + +/** + * touch_get_report_data() - Retrieve data from touch report + * + * Retrieve data from the touch report based on the bit offset and bit length + * information from the touch report configuration. + */ +static int touch_get_report_data(unsigned int offset, + unsigned int bits, unsigned int *data) +{ + unsigned char mask; + unsigned char byte_data; + unsigned int output_data; + unsigned int bit_offset; + unsigned int byte_offset; + unsigned int data_bits; + unsigned int available_bits; + unsigned int remaining_bits; + unsigned char *touch_report; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + + if (bits == 0 || bits > 32) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid number of bits\n"); + return -EINVAL; + } + + if (offset + bits > tcm_hcd->report.buffer.data_length * 8) { + *data = 0; + return 0; + } + + touch_report = tcm_hcd->report.buffer.buf; + + output_data = 0; + remaining_bits = bits; + + bit_offset = offset % 8; + byte_offset = offset / 8; + + while (remaining_bits) { + byte_data = touch_report[byte_offset]; + byte_data >>= bit_offset; + + available_bits = 8 - bit_offset; + data_bits = MIN(available_bits, remaining_bits); + mask = 0xff >> (8 - data_bits); + + byte_data &= mask; + + output_data |= byte_data << (bits - remaining_bits); + + bit_offset = 0; + byte_offset += 1; + remaining_bits -= data_bits; + } + + *data = output_data; + + return 0; +} + +/** + * touch_parse_report() - Parse touch report + * + * Traverse through the touch report configuration and parse the touch report + * generated by the device accordingly to retrieve the touch data. + */ +static int touch_parse_report(void) +{ + int retval; + bool active_only = false; + bool num_of_active_objects; + unsigned char code; + unsigned int size; + unsigned int idx; + unsigned int obj; + unsigned int next; + unsigned int data; + unsigned int bits; + unsigned int offset; + unsigned int objects; + unsigned int active_objects = 0; + unsigned int report_size; + unsigned int config_size; + unsigned char *config_data; + struct touch_data *touch_data; + struct object_data *object_data; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + static unsigned int end_of_foreach; + + touch_data = &touch_hcd->touch_data; + object_data = touch_hcd->touch_data.object_data; + + config_data = tcm_hcd->config.buf; + config_size = tcm_hcd->config.data_length; + + report_size = tcm_hcd->report.buffer.data_length; + + size = sizeof(*object_data) * touch_hcd->max_objects; + memset(touch_hcd->touch_data.object_data, 0x00, size); + + num_of_active_objects = false; + + idx = 0; + offset = 0; + objects = 0; + while (idx < config_size) { + code = config_data[idx++]; + switch (code) { + case TOUCH_END: + goto exit; + case TOUCH_FOREACH_ACTIVE_OBJECT: + obj = 0; + next = idx; + active_only = true; + break; + case TOUCH_FOREACH_OBJECT: + obj = 0; + next = idx; + active_only = false; + break; + case TOUCH_FOREACH_END: + end_of_foreach = idx; + if (active_only) { + if (num_of_active_objects) { + objects++; + if (objects < active_objects) + idx = next; + } else if (offset < report_size * 8) { + idx = next; + } + } else { + obj++; + if (obj < touch_hcd->max_objects) + idx = next; + } + break; + case TOUCH_PAD_TO_NEXT_BYTE: + offset = ceil_div(offset, 8) * 8; + break; + case TOUCH_TIMESTAMP: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get timestamp\n"); + return retval; + } + touch_data->timestamp = data; + offset += bits; + break; + case TOUCH_OBJECT_N_INDEX: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &obj); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object index\n"); + return retval; + } + offset += bits; + break; + case TOUCH_OBJECT_N_CLASSIFICATION: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object classification\n"); + return retval; + } + object_data[obj].status = data; + offset += bits; + break; + case TOUCH_OBJECT_N_X_POSITION: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object x position\n"); + return retval; + } + object_data[obj].x_pos = data; + offset += bits; + break; + case TOUCH_OBJECT_N_Y_POSITION: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object y position\n"); + return retval; + } + object_data[obj].y_pos = data; + offset += bits; + break; + case TOUCH_OBJECT_N_Z: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object z\n"); + return retval; + } + object_data[obj].z = data; + offset += bits; + break; + case TOUCH_OBJECT_N_X_WIDTH: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object x width\n"); + return retval; + } + object_data[obj].x_width = data; + offset += bits; + break; + case TOUCH_OBJECT_N_Y_WIDTH: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object y width\n"); + return retval; + } + object_data[obj].y_width = data; + offset += bits; + break; + case TOUCH_OBJECT_N_TX_POSITION_TIXELS: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object tx position\n"); + return retval; + } + object_data[obj].tx_pos = data; + offset += bits; + break; + case TOUCH_OBJECT_N_RX_POSITION_TIXELS: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object rx position\n"); + return retval; + } + object_data[obj].rx_pos = data; + offset += bits; + break; + case TOUCH_0D_BUTTONS_STATE: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get 0D buttons state\n"); + return retval; + } + touch_data->buttons_state = data; + offset += bits; + break; + case TOUCH_GESTURE_DOUBLE_TAP: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get gesture double tap\n"); + return retval; + } + touch_data->gesture_double_tap = data; + offset += bits; + break; + case TOUCH_FRAME_RATE: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get frame rate\n"); + return retval; + } + touch_data->frame_rate = data; + offset += bits; + break; + case TOUCH_POWER_IM: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get power IM\n"); + return retval; + } + touch_data->power_im = data; + offset += bits; + break; + case TOUCH_CID_IM: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get CID IM\n"); + return retval; + } + touch_data->cid_im = data; + offset += bits; + break; + case TOUCH_RAIL_IM: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get rail IM\n"); + return retval; + } + touch_data->rail_im = data; + offset += bits; + break; + case TOUCH_CID_VARIANCE_IM: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get CID variance IM\n"); + return retval; + } + touch_data->cid_variance_im = data; + offset += bits; + break; + case TOUCH_NSM_FREQUENCY: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get NSM frequency\n"); + return retval; + } + touch_data->nsm_frequency = data; + offset += bits; + break; + case TOUCH_NSM_STATE: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get NSM state\n"); + return retval; + } + touch_data->nsm_state = data; + offset += bits; + break; + case TOUCH_NUM_OF_ACTIVE_OBJECTS: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get number of active objects\n"); + return retval; + } + active_objects = data; + num_of_active_objects = true; + touch_data->num_of_active_objects = data; + offset += bits; + if (touch_data->num_of_active_objects == 0) + idx = end_of_foreach; + break; + case TOUCH_NUM_OF_CPU_CYCLES_USED_SINCE_LAST_FRAME: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get number of CPU cycles used since last frame\n"); + return retval; + } + touch_data->num_of_cpu_cycles = data; + offset += bits; + break; + case TOUCH_TUNING_GAUSSIAN_WIDTHS: + bits = config_data[idx++]; + offset += bits; + break; + case TOUCH_TUNING_SMALL_OBJECT_PARAMS: + bits = config_data[idx++]; + offset += bits; + break; + case TOUCH_TUNING_0D_BUTTONS_VARIANCE: + bits = config_data[idx++]; + offset += bits; + break; + } + } + +exit: + return 0; +} + +/** + * touch_report() - Report touch events + * + * Retrieve data from the touch report generated by the device and report touch + * events to the input subsystem. + */ +static void touch_report(void) +{ + int retval; + unsigned int idx; + unsigned int x; + unsigned int y; + unsigned int temp; + unsigned int status; + unsigned int touch_count; + struct touch_data *touch_data; + struct object_data *object_data; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (!touch_hcd->report_touch) + return; + + if (touch_hcd->input_dev == NULL) + return; + + mutex_lock(&touch_hcd->report_mutex); + + retval = touch_parse_report(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to parse touch report\n"); + goto exit; + } + + touch_data = &touch_hcd->touch_data; + object_data = touch_hcd->touch_data.object_data; + +#ifdef WAKEUP_GESTURE + if (touch_data->gesture_double_tap && tcm_hcd->in_suspend) { + input_report_key(touch_hcd->input_dev, KEY_WAKEUP, 1); + input_sync(touch_hcd->input_dev); + input_report_key(touch_hcd->input_dev, KEY_WAKEUP, 0); + input_sync(touch_hcd->input_dev); + } +#endif + + if (tcm_hcd->in_suspend) + goto exit; + + touch_count = 0; + + for (idx = 0; idx < touch_hcd->max_objects; idx++) { + if (touch_hcd->prev_status[idx] == LIFT && + object_data[idx].status == LIFT) + status = NOP; + else + status = object_data[idx].status; + + switch (status) { + case LIFT: +#ifdef TYPE_B_PROTOCOL + input_mt_slot(touch_hcd->input_dev, idx); + input_mt_report_slot_state(touch_hcd->input_dev, + MT_TOOL_FINGER, 0); +#endif + break; + case FINGER: + case GLOVED_FINGER: + x = object_data[idx].x_pos; + y = object_data[idx].y_pos; + if (bdata->swap_axes) { + temp = x; + x = y; + y = temp; + } + if (bdata->x_flip) + x = touch_hcd->input_params.max_x - x; + if (bdata->y_flip) + y = touch_hcd->input_params.max_y - y; +#ifdef TYPE_B_PROTOCOL + input_mt_slot(touch_hcd->input_dev, idx); + input_mt_report_slot_state(touch_hcd->input_dev, + MT_TOOL_FINGER, 1); +#endif + input_report_key(touch_hcd->input_dev, + BTN_TOUCH, 1); + input_report_key(touch_hcd->input_dev, + BTN_TOOL_FINGER, 1); + input_report_abs(touch_hcd->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(touch_hcd->input_dev, + ABS_MT_POSITION_Y, y); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(touch_hcd->input_dev); +#endif + LOGD(tcm_hcd->pdev->dev.parent, + "Finger %d: x = %d, y = %d\n", + idx, x, y); + touch_count++; + break; + default: + break; + } + + touch_hcd->prev_status[idx] = object_data[idx].status; + } + + if (touch_count == 0) { + input_report_key(touch_hcd->input_dev, + BTN_TOUCH, 0); + input_report_key(touch_hcd->input_dev, + BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(touch_hcd->input_dev); +#endif + } + + input_sync(touch_hcd->input_dev); + +exit: + mutex_unlock(&touch_hcd->report_mutex); + + return; +} + +/** + * touch_set_input_params() - Set input parameters + * + * Set the input parameters of the input device based on the information + * retrieved from the application information packet. In addition, set up an + * array for tracking the status of touch objects. + */ +static int touch_set_input_params(void) +{ + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + + input_set_abs_params(touch_hcd->input_dev, + ABS_MT_POSITION_X, 0, touch_hcd->max_x, 0, 0); + input_set_abs_params(touch_hcd->input_dev, + ABS_MT_POSITION_Y, 0, touch_hcd->max_y, 0, 0); + + input_mt_init_slots(touch_hcd->input_dev, touch_hcd->max_objects, + INPUT_MT_DIRECT); + + touch_hcd->input_params.max_x = touch_hcd->max_x; + touch_hcd->input_params.max_y = touch_hcd->max_y; + touch_hcd->input_params.max_objects = touch_hcd->max_objects; + + if (touch_hcd->max_objects == 0) + return 0; + + kfree(touch_hcd->prev_status); + touch_hcd->prev_status = kzalloc(touch_hcd->max_objects, GFP_KERNEL); + if (!touch_hcd->prev_status) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for touch_hcd->prev_status\n"); + return -ENOMEM; + } + + return 0; +} + +/** + * touch_get_input_params() - Get input parameters + * + * Retrieve the input parameters to register with the input subsystem for + * the input device from the application information packet. In addition, + * the touch report configuration is retrieved and stored. + */ +static int touch_get_input_params(void) +{ + int retval; + unsigned int temp; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + app_info = &tcm_hcd->app_info; + touch_hcd->max_x = le2_to_uint(app_info->max_x); + touch_hcd->max_y = le2_to_uint(app_info->max_y); + touch_hcd->max_objects = le2_to_uint(app_info->max_objects); + + if (bdata->swap_axes) { + temp = touch_hcd->max_x; + touch_hcd->max_x = touch_hcd->max_y; + touch_hcd->max_y = temp; + } + + LOCK_BUFFER(tcm_hcd->config); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_TOUCH_REPORT_CONFIG, + NULL, + 0, + &tcm_hcd->config.buf, + &tcm_hcd->config.buf_size, + &tcm_hcd->config.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_TOUCH_REPORT_CONFIG)); + UNLOCK_BUFFER(tcm_hcd->config); + return retval; + } + + UNLOCK_BUFFER(tcm_hcd->config); + + return 0; +} + +/** + * touch_set_input_dev() - Set up input device + * + * Allocate an input device, configure the input device based on the particular + * input events to be reported, and register the input device with the input + * subsystem. + */ +static int touch_set_input_dev(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + + touch_hcd->input_dev = input_allocate_device(); + if (touch_hcd->input_dev == NULL) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate input device\n"); + return -ENODEV; + } + + touch_hcd->input_dev->name = TOUCH_INPUT_NAME; + touch_hcd->input_dev->phys = TOUCH_INPUT_PHYS_PATH; + touch_hcd->input_dev->id.product = SYNAPTICS_TCM_ID_PRODUCT; + touch_hcd->input_dev->id.version = SYNAPTICS_TCM_ID_VERSION; + touch_hcd->input_dev->dev.parent = tcm_hcd->pdev->dev.parent; + input_set_drvdata(touch_hcd->input_dev, tcm_hcd); + + set_bit(EV_SYN, touch_hcd->input_dev->evbit); + set_bit(EV_KEY, touch_hcd->input_dev->evbit); + set_bit(EV_ABS, touch_hcd->input_dev->evbit); + set_bit(BTN_TOUCH, touch_hcd->input_dev->keybit); + set_bit(BTN_TOOL_FINGER, touch_hcd->input_dev->keybit); +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, touch_hcd->input_dev->propbit); +#endif + +#ifdef WAKEUP_GESTURE + set_bit(KEY_WAKEUP, touch_hcd->input_dev->keybit); + input_set_capability(touch_hcd->input_dev, EV_KEY, KEY_WAKEUP); +#endif + + retval = touch_set_input_params(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set input parameters\n"); + input_free_device(touch_hcd->input_dev); + touch_hcd->input_dev = NULL; + return retval; + } + + retval = input_register_device(touch_hcd->input_dev); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to register input device\n"); + input_free_device(touch_hcd->input_dev); + touch_hcd->input_dev = NULL; + return retval; + } + + return 0; +} + +/** + * touch_set_report_config() - Set touch report configuration + * + * Send the SET_TOUCH_REPORT_CONFIG command to configure the format and content + * of the touch report. + */ +static int touch_set_report_config(void) +{ + int retval; + unsigned int idx; + unsigned int length; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + +#ifdef USE_DEFAULT_TOUCH_REPORT_CONFIG + return 0; +#endif + + app_info = &tcm_hcd->app_info; + length = le2_to_uint(app_info->max_touch_report_config_size); + + if (length < TOUCH_REPORT_CONFIG_SIZE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid maximum touch report config size\n"); + return -EINVAL; + } + + LOCK_BUFFER(touch_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &touch_hcd->out, + length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for touch_hcd->out.buf\n"); + UNLOCK_BUFFER(touch_hcd->out); + return retval; + } + + idx = 0; +#ifdef WAKEUP_GESTURE + touch_hcd->out.buf[idx++] = TOUCH_GESTURE_DOUBLE_TAP; + touch_hcd->out.buf[idx++] = 8; +#endif + touch_hcd->out.buf[idx++] = TOUCH_FOREACH_ACTIVE_OBJECT; + touch_hcd->out.buf[idx++] = TOUCH_OBJECT_N_INDEX; + touch_hcd->out.buf[idx++] = 4; + touch_hcd->out.buf[idx++] = TOUCH_OBJECT_N_CLASSIFICATION; + touch_hcd->out.buf[idx++] = 4; + touch_hcd->out.buf[idx++] = TOUCH_OBJECT_N_X_POSITION; + touch_hcd->out.buf[idx++] = 12; + touch_hcd->out.buf[idx++] = TOUCH_OBJECT_N_Y_POSITION; + touch_hcd->out.buf[idx++] = 12; + touch_hcd->out.buf[idx++] = TOUCH_FOREACH_END; + touch_hcd->out.buf[idx++] = TOUCH_END; + + LOCK_BUFFER(touch_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_SET_TOUCH_REPORT_CONFIG, + touch_hcd->out.buf, + length, + &touch_hcd->resp.buf, + &touch_hcd->resp.buf_size, + &touch_hcd->resp.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_SET_TOUCH_REPORT_CONFIG)); + UNLOCK_BUFFER(touch_hcd->resp); + UNLOCK_BUFFER(touch_hcd->out); + return retval; + } + + UNLOCK_BUFFER(touch_hcd->resp); + UNLOCK_BUFFER(touch_hcd->out); + + return 0; +} + +/** + * touch_check_input_params() - Check input parameters + * + * Check if any of the input parameters registered with the input subsystem for + * the input device has changed. + */ +static int touch_check_input_params(void) +{ + unsigned int size; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + + if (touch_hcd->max_x == 0 && touch_hcd->max_y == 0) + return 0; + + if (touch_hcd->input_params.max_objects != touch_hcd->max_objects) { + kfree(touch_hcd->touch_data.object_data); + size = sizeof(*touch_hcd->touch_data.object_data); + size *= touch_hcd->max_objects; + touch_hcd->touch_data.object_data = kzalloc(size, GFP_KERNEL); + if (!touch_hcd->touch_data.object_data) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for touch_hcd->touch_data.object_data\n"); + return -ENOMEM; + } + return 1; + } + + if (touch_hcd->input_params.max_x != touch_hcd->max_x) + return 1; + + if (touch_hcd->input_params.max_y != touch_hcd->max_y) + return 1; + + return 0; +} + +/** + * touch_set_input_reporting() - Configure touch report and set up new input + * device if necessary + * + * After a device reset event, configure the touch report and set up a new input + * device if any of the input parameters has changed after the device reset. + */ +static int touch_set_input_reporting(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + + if (tcm_hcd->id_info.mode != MODE_APPLICATION || + tcm_hcd->app_status != APP_STATUS_OK) { + LOGN(tcm_hcd->pdev->dev.parent, + "Application firmware not running\n"); + return 0; + } + + touch_hcd->report_touch = false; + + touch_free_objects(); + + mutex_lock(&touch_hcd->report_mutex); + + retval = touch_set_report_config(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set report config\n"); + goto exit; + } + + retval = touch_get_input_params(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get input parameters\n"); + goto exit; + } + + retval = touch_check_input_params(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to check input parameters\n"); + goto exit; + } else if (retval == 0) { + LOGN(tcm_hcd->pdev->dev.parent, + "Input parameters unchanged\n"); + goto exit; + } + + if (touch_hcd->input_dev != NULL) { + input_unregister_device(touch_hcd->input_dev); + touch_hcd->input_dev = NULL; + } + + retval = touch_set_input_dev(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up input device\n"); + goto exit; + } + +exit: + mutex_unlock(&touch_hcd->report_mutex); + + touch_hcd->report_touch = retval < 0 ? false : true; + + return retval; +} + +static int touch_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + touch_hcd = kzalloc(sizeof(*touch_hcd), GFP_KERNEL); + if (!touch_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for touch_hcd\n"); + return -ENOMEM; + } + + touch_hcd->tcm_hcd = tcm_hcd; + + mutex_init(&touch_hcd->report_mutex); + + INIT_BUFFER(touch_hcd->out, false); + INIT_BUFFER(touch_hcd->resp, false); + + retval = touch_set_input_reporting(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up input reporting\n"); + goto err_set_input_reporting; + } + + tcm_hcd->report_touch = touch_report; + + return 0; + +err_set_input_reporting: + kfree(touch_hcd->touch_data.object_data); + kfree(touch_hcd->prev_status); + + RELEASE_BUFFER(touch_hcd->resp); + RELEASE_BUFFER(touch_hcd->out); + + kfree(touch_hcd); + touch_hcd = NULL; + + return retval; +} + +static int touch_remove(struct syna_tcm_hcd *tcm_hcd) +{ + if (!touch_hcd) + goto exit; + + tcm_hcd->report_touch = NULL; + + input_unregister_device(touch_hcd->input_dev); + + kfree(touch_hcd->touch_data.object_data); + kfree(touch_hcd->prev_status); + + RELEASE_BUFFER(touch_hcd->resp); + RELEASE_BUFFER(touch_hcd->out); + + kfree(touch_hcd); + touch_hcd = NULL; + +exit: + complete(&touch_remove_complete); + + return 0; +} + +static int touch_syncbox(struct syna_tcm_hcd *tcm_hcd) +{ + if (!touch_hcd) + return 0; + + switch (tcm_hcd->report.id) { + case REPORT_IDENTIFY: + touch_free_objects(); + break; + case REPORT_TOUCH: + if (!touch_hcd->suspend_touch) + touch_report(); + break; + default: + break; + } + + return 0; +} + +static int touch_asyncbox(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!touch_hcd) + return 0; + + switch (tcm_hcd->async_report_id) { + case REPORT_IDENTIFY: + if (tcm_hcd->id_info.mode != MODE_APPLICATION) + break; + retval = tcm_hcd->identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + return retval; + } + retval = touch_set_input_reporting(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up input reporting\n"); + return retval; + } + break; + default: + break; + } + + return 0; +} + +static int touch_reset(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!touch_hcd) { + retval = touch_init(tcm_hcd); + return retval; + } + + if (tcm_hcd->id_info.mode == MODE_APPLICATION) { + retval = touch_set_input_reporting(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up input reporting\n"); + return retval; + } + } + + return 0; +} + +static int touch_early_suspend(struct syna_tcm_hcd *tcm_hcd) +{ + if (!touch_hcd) + return 0; + + touch_hcd->suspend_touch = true; + + touch_free_objects(); + + return 0; +} + +static int touch_suspend(struct syna_tcm_hcd *tcm_hcd) +{ +#ifdef WAKEUP_GESTURE + int retval; +#endif + + if (!touch_hcd) + return 0; + + touch_hcd->suspend_touch = true; + + touch_free_objects(); + +#ifdef WAKEUP_GESTURE + if (!touch_hcd->irq_wake) { + enable_irq_wake(tcm_hcd->irq); + touch_hcd->irq_wake = true; + } + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_IN_WAKEUP_GESTURE_MODE, + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable wakeup gesture mode\n"); + return retval; + } +#endif + + return 0; +} + +static int touch_resume(struct syna_tcm_hcd *tcm_hcd) +{ +#ifdef WAKEUP_GESTURE + int retval; +#endif + + if (!touch_hcd) + return 0; + + touch_hcd->suspend_touch = false; + +#ifdef WAKEUP_GESTURE + if (touch_hcd->irq_wake) { + disable_irq_wake(tcm_hcd->irq); + touch_hcd->irq_wake = false; + } + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_IN_WAKEUP_GESTURE_MODE, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to disable wakeup gesture mode\n"); + return retval; + } +#endif + + return 0; +} + +static struct syna_tcm_module_cb touch_module = { + .type = TCM_TOUCH, + .init = touch_init, + .remove = touch_remove, + .syncbox = touch_syncbox, + .asyncbox = touch_asyncbox, + .reset = touch_reset, + .suspend = touch_suspend, + .resume = touch_resume, + .early_suspend = touch_early_suspend, +}; + +static int __init touch_module_init(void) +{ + return syna_tcm_add_module(&touch_module, true); +} + +static void __exit touch_module_exit(void) +{ + syna_tcm_add_module(&touch_module, false); + + wait_for_completion(&touch_remove_complete); + + return; +} + +module_init(touch_module_init); +module_exit(touch_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Touch Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_zeroflash.c b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_zeroflash.c new file mode 100644 index 000000000000..549844ac940f --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi/synaptics_tcm_zeroflash.c @@ -0,0 +1,975 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * + * 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. + */ + +#include +#include +#include "synaptics_tcm_core.h" + +#define FW_IMAGE_NAME "synaptics/hdl_firmware.img" + +#define BOOT_CONFIG_ID "BOOT_CONFIG" + +#define F35_APP_CODE_ID "F35_APP_CODE" + +#define APP_CONFIG_ID "APP_CONFIG" + +#define DISP_CONFIG_ID "DISPLAY" + +#define IMAGE_FILE_MAGIC_VALUE 0x4818472b + +#define FLASH_AREA_MAGIC_VALUE 0x7c05e516 + +#define PDT_START_ADDR 0x00e9 + +#define PDT_END_ADDR 0x00ee + +#define UBL_FN_NUMBER 0x35 + +#define F35_CTRL3_OFFSET 18 + +#define F35_CTRL7_OFFSET 22 + +#define F35_WRITE_FW_TO_PMEM_COMMAND 4 + +#define DOWNLOAD_RETRY_COUNT 10 + +enum f35_error_code { + SUCCESS = 0, + UNKNOWN_FLASH_PRESENT, + MAGIC_NUMBER_NOT_PRESENT, + INVALID_BLOCK_NUMBER, + BLOCK_NOT_ERASED, + NO_FLASH_PRESENT, + CHECKSUM_FAILURE, + WRITE_FAILURE, + INVALID_COMMAND, + IN_DEBUG_MODE, + INVALID_HEADER, + REQUESTING_FIRMWARE, + INVALID_CONFIGURATION, + DISABLE_BLOCK_PROTECT_FAILURE, +}; + +enum config_download { + HDL_INVALID = 0, + HDL_TOUCH_CONFIG_TO_PMEM, + HDL_DISPLAY_CONFIG_TO_PMEM, + HDL_DISPLAY_CONFIG_TO_RAM, +}; + +struct area_descriptor { + unsigned char magic_value[4]; + unsigned char id_string[16]; + unsigned char flags[4]; + unsigned char flash_addr_words[4]; + unsigned char length[4]; + unsigned char checksum[4]; +}; + +struct block_data { + const unsigned char *data; + unsigned int size; + unsigned int flash_addr; +}; + +struct image_info { + unsigned int packrat_number; + struct block_data boot_config; + struct block_data app_firmware; + struct block_data app_config; + struct block_data disp_config; +}; + +struct image_header { + unsigned char magic_value[4]; + unsigned char num_of_areas[4]; +}; + +struct rmi_f35_query { + unsigned char version:4; + unsigned char has_debug_mode:1; + unsigned char has_data5:1; + unsigned char has_query1:1; + unsigned char has_query2:1; + unsigned char chunk_size; + unsigned char has_ctrl7:1; + unsigned char has_host_download:1; + unsigned char has_spi_master:1; + unsigned char advanced_recovery_mode:1; + unsigned char reserved:4; +} __packed; + +struct rmi_f35_data { + unsigned char error_code:5; + unsigned char recovery_mode_forced:1; + unsigned char nvm_programmed:1; + unsigned char in_recovery:1; +} __packed; + +struct rmi_pdt_entry { + unsigned char query_base_addr; + unsigned char command_base_addr; + unsigned char control_base_addr; + unsigned char data_base_addr; + unsigned char intr_src_count:3; + unsigned char reserved_1:2; + unsigned char fn_version:2; + unsigned char reserved_2:1; + unsigned char fn_number; +} __packed; + +struct rmi_addr { + unsigned short query_base; + unsigned short command_base; + unsigned short control_base; + unsigned short data_base; +}; + +struct firmware_status { + unsigned short invalid_static_config:1; + unsigned short need_disp_config:1; + unsigned short need_app_config:1; + unsigned short reserved:13; +} __packed; + +struct zeroflash_hcd { + bool has_hdl; + bool f35_ready; + const unsigned char *image; + unsigned char *buf; + const struct firmware *fw_entry; + struct work_struct config_work; + struct work_struct firmware_work; + struct workqueue_struct *workqueue; + struct rmi_addr f35_addr; + struct image_info image_info; + struct firmware_status fw_status; + struct syna_tcm_buffer out; + struct syna_tcm_buffer resp; + struct syna_tcm_hcd *tcm_hcd; +}; + +DECLARE_COMPLETION(zeroflash_remove_complete); + +static struct zeroflash_hcd *zeroflash_hcd; + +static int zeroflash_check_uboot(void) +{ + int retval; + unsigned char fn_number; + struct rmi_f35_query query; + struct rmi_pdt_entry p_entry; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + + retval = syna_tcm_rmi_read(tcm_hcd, + PDT_END_ADDR, + &fn_number, + sizeof(fn_number)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read RMI function number\n"); + return retval; + } + + LOGD(tcm_hcd->pdev->dev.parent, + "Found F$%02x\n", + fn_number); + + if (fn_number != UBL_FN_NUMBER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to find F$35\n"); + return -ENODEV; + } + + if (zeroflash_hcd->f35_ready) + return 0; + + retval = syna_tcm_rmi_read(tcm_hcd, + PDT_START_ADDR, + (unsigned char *)&p_entry, + sizeof(p_entry)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read PDT entry\n"); + return retval; + } + + zeroflash_hcd->f35_addr.query_base = p_entry.query_base_addr; + zeroflash_hcd->f35_addr.command_base = p_entry.command_base_addr; + zeroflash_hcd->f35_addr.control_base = p_entry.control_base_addr; + zeroflash_hcd->f35_addr.data_base = p_entry.data_base_addr; + + retval = syna_tcm_rmi_read(tcm_hcd, + zeroflash_hcd->f35_addr.query_base, + (unsigned char *)&query, + sizeof(query)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read F$35 query\n"); + return retval; + } + + zeroflash_hcd->f35_ready = true; + + if (query.has_query2 && query.has_ctrl7 && query.has_host_download) { + zeroflash_hcd->has_hdl = true; + } else { + LOGE(tcm_hcd->pdev->dev.parent, + "Host download not supported\n"); + zeroflash_hcd->has_hdl = false; + return -ENODEV; + } + + return 0; +} + +static int zeroflash_parse_fw_image(void) +{ + unsigned int idx; + unsigned int addr; + unsigned int offset; + unsigned int length; + unsigned int checksum; + unsigned int flash_addr; + unsigned int magic_value; + unsigned int num_of_areas; + struct image_header *header; + struct image_info *image_info; + struct area_descriptor *descriptor; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + const unsigned char *image; + const unsigned char *content; + + image = zeroflash_hcd->image; + image_info = &zeroflash_hcd->image_info; + header = (struct image_header *)image; + + magic_value = le4_to_uint(header->magic_value); + if (magic_value != IMAGE_FILE_MAGIC_VALUE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid image file magic value\n"); + return -EINVAL; + } + + memset(image_info, 0x00, sizeof(*image_info)); + + offset = sizeof(*header); + num_of_areas = le4_to_uint(header->num_of_areas); + + for (idx = 0; idx < num_of_areas; idx++) { + addr = le4_to_uint(image + offset); + descriptor = (struct area_descriptor *)(image + addr); + offset += 4; + + magic_value = le4_to_uint(descriptor->magic_value); + if (magic_value != FLASH_AREA_MAGIC_VALUE) + continue; + + length = le4_to_uint(descriptor->length); + content = (unsigned char *)descriptor + sizeof(*descriptor); + flash_addr = le4_to_uint(descriptor->flash_addr_words) * 2; + checksum = le4_to_uint(descriptor->checksum); + + if (0 == strncmp((char *)descriptor->id_string, + BOOT_CONFIG_ID, + strlen(BOOT_CONFIG_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Boot config checksum error\n"); + return -EINVAL; + } + image_info->boot_config.size = length; + image_info->boot_config.data = content; + image_info->boot_config.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Boot config size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Boot config flash address = 0x%08x\n", + flash_addr); + } else if (0 == strncmp((char *)descriptor->id_string, + F35_APP_CODE_ID, + strlen(F35_APP_CODE_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application firmware checksum error\n"); + return -EINVAL; + } + image_info->app_firmware.size = length; + image_info->app_firmware.data = content; + image_info->app_firmware.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Application firmware size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Application firmware flash address = 0x%08x\n", + flash_addr); + } else if (0 == strncmp((char *)descriptor->id_string, + APP_CONFIG_ID, + strlen(APP_CONFIG_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application config checksum error\n"); + return -EINVAL; + } + image_info->app_config.size = length; + image_info->app_config.data = content; + image_info->app_config.flash_addr = flash_addr; + image_info->packrat_number = le4_to_uint(&content[14]); + LOGD(tcm_hcd->pdev->dev.parent, + "Application config size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Application config flash address = 0x%08x\n", + flash_addr); + } else if (0 == strncmp((char *)descriptor->id_string, + DISP_CONFIG_ID, + strlen(DISP_CONFIG_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Display config checksum error\n"); + return -EINVAL; + } + image_info->disp_config.size = length; + image_info->disp_config.data = content; + image_info->disp_config.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Display config size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Display config flash address = 0x%08x\n", + flash_addr); + } + } + + return 0; +} + +static int zeroflash_get_fw_image(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + + if (zeroflash_hcd->fw_entry != NULL) + return 0; + + do { + retval = request_firmware(&zeroflash_hcd->fw_entry, + FW_IMAGE_NAME, + tcm_hcd->pdev->dev.parent); + if (retval < 0) { + LOGD(tcm_hcd->pdev->dev.parent, + "Failed to request %s\n", + FW_IMAGE_NAME); + msleep(100); + } else { + break; + } + } while (1); + + LOGD(tcm_hcd->pdev->dev.parent, + "Firmware image size = %d\n", + (unsigned int)zeroflash_hcd->fw_entry->size); + + zeroflash_hcd->image = zeroflash_hcd->fw_entry->data; + + retval = zeroflash_parse_fw_image(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to parse firmware image\n"); + release_firmware(zeroflash_hcd->fw_entry); + zeroflash_hcd->fw_entry = NULL; + zeroflash_hcd->image = NULL; + return retval; + } + + return 0; +} + +static void zeroflash_download_config(void) +{ + struct firmware_status *fw_status; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + + fw_status = &zeroflash_hcd->fw_status; + + if (!fw_status->need_app_config && !fw_status->need_disp_config) { + if (atomic_read(&tcm_hcd->helper.task) == HELP_NONE) { + atomic_set(&tcm_hcd->helper.task, + HELP_SEND_RESET_NOTIFICATION); + queue_work(tcm_hcd->helper.workqueue, + &tcm_hcd->helper.work); + } + atomic_set(&tcm_hcd->host_downloading, 0); + wake_up_interruptible(&tcm_hcd->hdl_wq); + return; + } + + queue_work(zeroflash_hcd->workqueue, &zeroflash_hcd->config_work); + + return; +} + +static void zeroflash_download_firmware(void) +{ + queue_work(zeroflash_hcd->workqueue, &zeroflash_hcd->firmware_work); + + return; +} + +static int zeroflash_download_disp_config(void) +{ + int retval; + unsigned char response_code; + struct image_info *image_info; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + static unsigned int retry_count; + + LOGN(tcm_hcd->pdev->dev.parent, + "Downloading display config\n"); + + image_info = &zeroflash_hcd->image_info; + + if (image_info->disp_config.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No display config in image file\n"); + return -EINVAL; + } + + LOCK_BUFFER(zeroflash_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &zeroflash_hcd->out, + image_info->disp_config.size + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for zeroflash_hcd->out.buf\n"); + goto unlock_out; + } + + zeroflash_hcd->out.buf[0] = 1; + zeroflash_hcd->out.buf[1] = HDL_DISPLAY_CONFIG_TO_RAM; + + retval = secure_memcpy(&zeroflash_hcd->out.buf[2], + zeroflash_hcd->out.buf_size - 2, + image_info->disp_config.data, + image_info->disp_config.size, + image_info->disp_config.size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy display config data\n"); + goto unlock_out; + } + + zeroflash_hcd->out.data_length = image_info->disp_config.size + 2; + + LOCK_BUFFER(zeroflash_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_DOWNLOAD_CONFIG, + zeroflash_hcd->out.buf, + zeroflash_hcd->out.data_length, + &zeroflash_hcd->resp.buf, + &zeroflash_hcd->resp.buf_size, + &zeroflash_hcd->resp.data_length, + &response_code, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_DOWNLOAD_CONFIG)); + if (response_code != STATUS_ERROR) + goto unlock_resp; + retry_count++; + if (DOWNLOAD_RETRY_COUNT && retry_count > DOWNLOAD_RETRY_COUNT) + goto unlock_resp; + } else { + retry_count = 0; + } + + retval = secure_memcpy((unsigned char *)&zeroflash_hcd->fw_status, + sizeof(zeroflash_hcd->fw_status), + zeroflash_hcd->resp.buf, + zeroflash_hcd->resp.buf_size, + sizeof(zeroflash_hcd->fw_status)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy firmware status\n"); + goto unlock_resp; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Display config downloaded\n"); + + retval = 0; + +unlock_resp: + UNLOCK_BUFFER(zeroflash_hcd->resp); + +unlock_out: + UNLOCK_BUFFER(zeroflash_hcd->out); + + return retval; +} + +static int zeroflash_download_app_config(void) +{ + int retval; + unsigned char response_code; + struct image_info *image_info; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + static unsigned int retry_count; + + LOGN(tcm_hcd->pdev->dev.parent, + "Downloading application config\n"); + + image_info = &zeroflash_hcd->image_info; + + if (image_info->app_config.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No application config in image file\n"); + return -EINVAL; + } + + LOCK_BUFFER(zeroflash_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &zeroflash_hcd->out, + image_info->app_config.size + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for zeroflash_hcd->out.buf\n"); + goto unlock_out; + } + + zeroflash_hcd->out.buf[0] = 1; + zeroflash_hcd->out.buf[1] = HDL_TOUCH_CONFIG_TO_PMEM; + + retval = secure_memcpy(&zeroflash_hcd->out.buf[2], + zeroflash_hcd->out.buf_size - 2, + image_info->app_config.data, + image_info->app_config.size, + image_info->app_config.size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy application config data\n"); + goto unlock_out; + } + + zeroflash_hcd->out.data_length = image_info->app_config.size + 2; + + LOCK_BUFFER(zeroflash_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_DOWNLOAD_CONFIG, + zeroflash_hcd->out.buf, + zeroflash_hcd->out.data_length, + &zeroflash_hcd->resp.buf, + &zeroflash_hcd->resp.buf_size, + &zeroflash_hcd->resp.data_length, + &response_code, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_DOWNLOAD_CONFIG)); + if (response_code != STATUS_ERROR) + goto unlock_resp; + retry_count++; + if (DOWNLOAD_RETRY_COUNT && retry_count > DOWNLOAD_RETRY_COUNT) + goto unlock_resp; + } else { + retry_count = 0; + } + + retval = secure_memcpy((unsigned char *)&zeroflash_hcd->fw_status, + sizeof(zeroflash_hcd->fw_status), + zeroflash_hcd->resp.buf, + zeroflash_hcd->resp.buf_size, + sizeof(zeroflash_hcd->fw_status)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy firmware status\n"); + goto unlock_resp; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Application config downloaded\n"); + + retval = 0; + +unlock_resp: + UNLOCK_BUFFER(zeroflash_hcd->resp); + +unlock_out: + UNLOCK_BUFFER(zeroflash_hcd->out); + + return retval; +} + +static void zeroflash_download_config_work(struct work_struct *work) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + + retval = zeroflash_get_fw_image(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get firmware image\n"); + return; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Start of config download\n"); + + if (zeroflash_hcd->fw_status.need_app_config) { + retval = zeroflash_download_app_config(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to download application config\n"); + return; + } + goto exit; + } + + if (zeroflash_hcd->fw_status.need_disp_config) { + retval = zeroflash_download_disp_config(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to download display config\n"); + return; + } + goto exit; + } + +exit: + LOGN(tcm_hcd->pdev->dev.parent, + "End of config download\n"); + + zeroflash_download_config(); + + return; +} + +static int zeroflash_download_app_fw(void) +{ + int retval; + unsigned char command; + struct image_info *image_info; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + + LOGN(tcm_hcd->pdev->dev.parent, + "Downloading application firmware\n"); + + image_info = &zeroflash_hcd->image_info; + + if (image_info->app_firmware.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No application firmware in image file\n"); + return -EINVAL; + } + + LOCK_BUFFER(zeroflash_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &zeroflash_hcd->out, + image_info->app_firmware.size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for zeroflash_hcd->out.buf\n"); + UNLOCK_BUFFER(zeroflash_hcd->out); + return retval; + } + + retval = secure_memcpy(zeroflash_hcd->out.buf, + zeroflash_hcd->out.buf_size, + image_info->app_firmware.data, + image_info->app_firmware.size, + image_info->app_firmware.size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy application firmware data\n"); + UNLOCK_BUFFER(zeroflash_hcd->out); + return retval; + } + + zeroflash_hcd->out.data_length = image_info->app_firmware.size; + + command = F35_WRITE_FW_TO_PMEM_COMMAND; + + retval = syna_tcm_rmi_write(tcm_hcd, + zeroflash_hcd->f35_addr.control_base + F35_CTRL3_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write F$35 command\n"); + UNLOCK_BUFFER(zeroflash_hcd->out); + return retval; + } + + retval = syna_tcm_rmi_write(tcm_hcd, + zeroflash_hcd->f35_addr.control_base + F35_CTRL7_OFFSET, + zeroflash_hcd->out.buf, + zeroflash_hcd->out.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write application firmware data\n"); + UNLOCK_BUFFER(zeroflash_hcd->out); + return retval; + } + + UNLOCK_BUFFER(zeroflash_hcd->out); + + LOGN(tcm_hcd->pdev->dev.parent, + "Application firmware downloaded\n"); + + return 0; +} + +static void zeroflash_download_firmware_work(struct work_struct *work) +{ + int retval; + struct rmi_f35_data data; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + static unsigned int retry_count; + + retval = zeroflash_check_uboot(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Microbootloader support unavailable\n"); + goto exit; + } + + atomic_set(&tcm_hcd->host_downloading, 1); + + retval = syna_tcm_rmi_read(tcm_hcd, + zeroflash_hcd->f35_addr.data_base, + (unsigned char *)&data, + sizeof(data)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read F$35 data\n"); + goto exit; + } + + if (data.error_code != REQUESTING_FIRMWARE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Microbootloader error code = 0x%02x\n", + data.error_code); + if (data.error_code != CHECKSUM_FAILURE) { + retval = -EIO; + goto exit; + } else { + retry_count++; + } + } else { + retry_count = 0; + } + + retval = zeroflash_get_fw_image(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get firmware image\n"); + goto exit; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Start of firmware download\n"); + + retval = zeroflash_download_app_fw(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to download application firmware\n"); + goto exit; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "End of firmware download\n"); + +exit: + if (retval < 0) + retry_count++; + + if (DOWNLOAD_RETRY_COUNT && retry_count > DOWNLOAD_RETRY_COUNT) { + retval = tcm_hcd->enable_irq(tcm_hcd, false, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to disable interrupt\n"); + } + } else { + retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable interrupt\n"); + } + } + + return; +} + +static int zeroflash_init(struct syna_tcm_hcd *tcm_hcd) +{ + zeroflash_hcd = kzalloc(sizeof(*zeroflash_hcd), GFP_KERNEL); + if (!zeroflash_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for zeroflash_hcd\n"); + return -ENOMEM; + } + + zeroflash_hcd->tcm_hcd = tcm_hcd; + + INIT_BUFFER(zeroflash_hcd->out, false); + INIT_BUFFER(zeroflash_hcd->resp, false); + + zeroflash_hcd->workqueue = + create_singlethread_workqueue("syna_tcm_zeroflash"); + INIT_WORK(&zeroflash_hcd->config_work, + zeroflash_download_config_work); + INIT_WORK(&zeroflash_hcd->firmware_work, + zeroflash_download_firmware_work); + + if (tcm_hcd->init_okay == false && + tcm_hcd->hw_if->bus_io->type == BUS_SPI) + zeroflash_download_firmware(); + + return 0; +} + +static int zeroflash_remove(struct syna_tcm_hcd *tcm_hcd) +{ + if (!zeroflash_hcd) + goto exit; + + if (zeroflash_hcd->fw_entry) + release_firmware(zeroflash_hcd->fw_entry); + + cancel_work_sync(&zeroflash_hcd->config_work); + cancel_work_sync(&zeroflash_hcd->firmware_work); + flush_workqueue(zeroflash_hcd->workqueue); + destroy_workqueue(zeroflash_hcd->workqueue); + + RELEASE_BUFFER(zeroflash_hcd->resp); + RELEASE_BUFFER(zeroflash_hcd->out); + + kfree(zeroflash_hcd); + zeroflash_hcd = NULL; + +exit: + complete(&zeroflash_remove_complete); + + return 0; +} + +static int zeroflash_syncbox(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *fw_status; + + if (!zeroflash_hcd) + return 0; + + switch (tcm_hcd->report.id) { + case REPORT_STATUS: + fw_status = (unsigned char *)&zeroflash_hcd->fw_status; + retval = secure_memcpy(fw_status, + sizeof(zeroflash_hcd->fw_status), + tcm_hcd->report.buffer.buf, + tcm_hcd->report.buffer.buf_size, + sizeof(zeroflash_hcd->fw_status)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy firmware status\n"); + return retval; + } + zeroflash_download_config(); + break; + case REPORT_HDL: + retval = tcm_hcd->enable_irq(tcm_hcd, false, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to disable interrupt\n"); + return retval; + } + zeroflash_download_firmware(); + break; + default: + break; + } + + return 0; +} + +static int zeroflash_reset(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!zeroflash_hcd) { + retval = zeroflash_init(tcm_hcd); + return retval; + } + + return 0; +} + +static struct syna_tcm_module_cb zeroflash_module = { + .type = TCM_ZEROFLASH, + .init = zeroflash_init, + .remove = zeroflash_remove, + .syncbox = zeroflash_syncbox, + .asyncbox = NULL, + .reset = zeroflash_reset, + .suspend = NULL, + .resume = NULL, + .early_suspend = NULL, +}; + +static int __init zeroflash_module_init(void) +{ + return syna_tcm_add_module(&zeroflash_module, true); +} + +static void __exit zeroflash_module_exit(void) +{ + syna_tcm_add_module(&zeroflash_module, false); + + wait_for_completion(&zeroflash_remove_complete); + + return; +} + +module_init(zeroflash_module_init); +module_exit(zeroflash_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Zeroflash Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_bus/Android.mk b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_bus/Android.mk new file mode 100644 index 000000000000..05f303839955 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_bus/Android.mk @@ -0,0 +1,22 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +SYNA_TCM_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +SYNA_TCM_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(CLEAR_VARS) +ifneq ($(findstring synaptics_i2c.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KERNEL_CFLAGS += USE_TOUCHSCREEN_MMI_I2C=y + LOCAL_MODULE := synaptics_i2c.ko +else + LOCAL_MODULE := synaptics_spi.ko +endif +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SYNA_TCM_MODULE_PATH) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/mmi_info.ko + +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_bus/Kbuild b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_bus/Kbuild new file mode 100644 index 000000000000..e7f7c8edf017 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_bus/Kbuild @@ -0,0 +1,11 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/synaptics_tcm_mmi_v2 + +ifneq ($(filter m y,$(USE_TOUCHSCREEN_MMI_I2C)),) + obj-m += synaptics_i2c.o + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers +else + obj-m += synaptics_spi.o +endif diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_bus/synaptics_i2c.c b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_bus/synaptics_i2c.c new file mode 100644 index 000000000000..b594dea71305 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_bus/synaptics_i2c.c @@ -0,0 +1,560 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * Copyright (C) 2018-2019 Ian Su + * Copyright (C) 2018-2019 Joey Zhou + * Copyright (C) 2018-2019 Yuehao Qiu + * Copyright (C) 2018-2019 Aaron Chen + * + * 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. + */ + +#include +#include +#include +#include "../synaptics_core/synaptics_core.h" + +#define XFER_ATTEMPTS 10 + +static unsigned char *buf; + +static unsigned int buf_size; + +static struct syna_tcm_bus_io bus_io; + +static struct syna_tcm_hw_interface hw_if; + +static struct platform_device *syna_tcm_i2c_device; + +#ifdef CONFIG_OF +static int parse_dt(struct device *dev, struct syna_tcm_board_data *bdata) +{ + int retval; + u32 value; + struct property *prop; + struct device_node *np = dev->of_node; + const char *name; + + prop = of_find_property(np, "synaptics,irq-gpio", NULL); + if (prop && prop->length) { + bdata->irq_gpio = of_get_named_gpio_flags(np, + "synaptics,irq-gpio", 0, + (enum of_gpio_flags *)&bdata->irq_flags); + } else { + bdata->irq_gpio = -1; + } + + retval = of_property_read_u32(np, "synaptics,irq-on-state", &value); + if (retval < 0) + bdata->irq_on_state = 0; + else + bdata->irq_on_state = value; + + retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name); + if (retval < 0) + bdata->pwr_reg_name = NULL; + else + bdata->pwr_reg_name = name; + + retval = of_property_read_string(np, "synaptics,bus-reg-name", &name); + if (retval < 0) + bdata->bus_reg_name = NULL; + else + bdata->bus_reg_name = name; + + prop = of_find_property(np, "synaptics,power-gpio", NULL); + if (prop && prop->length) { + bdata->power_gpio = of_get_named_gpio_flags(np, + "synaptics,power-gpio", 0, NULL); + } else { + bdata->power_gpio = -1; + } + + prop = of_find_property(np, "synaptics,power-on-state", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-on-state", + &value); + if (retval < 0) { + LOGE(dev, + "Failed to read synaptics,power-on-state property\n"); + return retval; + } else { + bdata->power_on_state = value; + } + } else { + bdata->power_on_state = 0; + } + + prop = of_find_property(np, "synaptics,power-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-delay-ms", + &value); + if (retval < 0) { + LOGE(dev, + "Failed to read synaptics,power-delay-ms property\n"); + return retval; + } else { + bdata->power_delay_ms = value; + } + } else { + bdata->power_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-gpio", NULL); + if (prop && prop->length) { + bdata->reset_gpio = of_get_named_gpio_flags(np, + "synaptics,reset-gpio", 0, NULL); + } else { + bdata->reset_gpio = -1; + } + + prop = of_find_property(np, "synaptics,reset-on-state", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-on-state", + &value); + if (retval < 0) { + LOGE(dev, + "Failed to read synaptics,reset-on-state property\n"); + return retval; + } else { + bdata->reset_on_state = value; + } + } else { + bdata->reset_on_state = 0; + } + + prop = of_find_property(np, "synaptics,reset-active-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-active-ms", + &value); + if (retval < 0) { + LOGE(dev, + "Failed to read synaptics,reset-active-ms property\n"); + return retval; + } else { + bdata->reset_active_ms = value; + } + } else { + bdata->reset_active_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-delay-ms", + &value); + if (retval < 0) { + LOGE(dev, + "Unable to read synaptics,reset-delay-ms property\n"); + return retval; + } else { + bdata->reset_delay_ms = value; + } + } else { + bdata->reset_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,tpio-reset-gpio", NULL); + if (prop && prop->length) { + bdata->tpio_reset_gpio = of_get_named_gpio_flags(np, + "synaptics,tpio-reset-gpio", 0, NULL); + } else { + bdata->tpio_reset_gpio = -1; + } + + /* If multiple_factor not set in device_tree. Set as default value. + * report major and minor value = raw value * size_to_pixel_multiple_factor / 128 + */ + prop = of_find_property(np, "synaptics,size_to_pixel_multiple_factor", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,size_to_pixel_multiple_factor", + &value); + if (retval < 0) { + LOGE(dev, + "Unable to read synaptics,size_to_pixel_multiple_factor property\n"); + return retval; + } else { + bdata->size_to_pixel_multiple_factor = value; + } + } else { + bdata->size_to_pixel_multiple_factor = 128; + } + + prop = of_find_property(np, "synaptics,x-flip", NULL); + bdata->x_flip = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,y-flip", NULL); + bdata->y_flip = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,swap-axes", NULL); + bdata->swap_axes = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,ubl-i2c-addr", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,ubl-i2c-addr", + &value); + if (retval < 0) { + LOGE(dev, + "Unable to read synaptics,ubl-i2c-addr property\n"); + return retval; + } else { + bdata->ubl_i2c_addr = value; + } + } else { + bdata->ubl_i2c_addr = 0; + } + + return 0; +} +#endif + +static int syna_tcm_i2c_alloc_mem(struct syna_tcm_hcd *tcm_hcd, + unsigned int size) +{ + struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent); + + if (size > buf_size) { + if (buf_size) + kfree(buf); + buf = kmalloc(size, GFP_KERNEL); + if (!buf) { + LOGE(&i2c->dev, + "Failed to allocate memory for buf\n"); + buf_size = 0; + return -ENOMEM; + } + buf_size = size; + } + + return 0; +} + +static int syna_tcm_i2c_rmi_read(struct syna_tcm_hcd *tcm_hcd, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval = -1; + unsigned char address; + unsigned int attempt; + struct i2c_msg msg[2]; + struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + address = (unsigned char)addr; + + msg[0].addr = bdata->ubl_i2c_addr; + msg[0].flags = 0; + msg[0].len = 1; + msg[0].buf = &address; + + msg[1].addr = bdata->ubl_i2c_addr; + msg[1].flags = I2C_M_RD; + msg[1].len = length; + msg[1].buf = data; + + for (attempt = 0; attempt < XFER_ATTEMPTS; attempt++) { + if (i2c_transfer(i2c->adapter, msg, 2) == 2) { + retval = length; + goto exit; + } + LOGD(&i2c->dev, + "Transfer attempt %d failed\n", + attempt + 1); + + if (attempt + 1 == XFER_ATTEMPTS) { + LOGE(&i2c->dev, + "Transfer attempt %d failed, exceeding the maximum number of attempts\n", + attempt + 1); + retval = -EIO; + goto exit; + } + + msleep(20); + } + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_i2c_rmi_write(struct syna_tcm_hcd *tcm_hcd, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned int attempt; + unsigned int byte_count; + struct i2c_msg msg; + struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + byte_count = length + 1; + + retval = syna_tcm_i2c_alloc_mem(tcm_hcd, byte_count); + if (retval < 0) { + LOGE(&i2c->dev, + "Failed to allocate memory\n"); + goto exit; + } + + buf[0] = (unsigned char)addr; + retval = secure_memcpy(&buf[1], + buf_size - 1, + data, + length, + length); + if (retval < 0) { + LOGE(&i2c->dev, + "Failed to copy write data\n"); + goto exit; + } + + msg.addr = bdata->ubl_i2c_addr; + msg.flags = 0; + msg.len = byte_count; + msg.buf = buf; + + for (attempt = 0; attempt < XFER_ATTEMPTS; attempt++) { + if (i2c_transfer(i2c->adapter, &msg, 1) == 1) { + retval = length; + goto exit; + } + LOGD(&i2c->dev, + "Transfer attempt %d failed\n", + attempt + 1); + + if (attempt + 1 == XFER_ATTEMPTS) { + LOGE(&i2c->dev, + "Transfer attempt %d failed, exceeding the maximum number of attempts\n", + attempt + 1); + retval = -EIO; + goto exit; + } + + msleep(20); + } + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_i2c_read(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, + unsigned int length) +{ + int retval = -1; + unsigned int attempt; + struct i2c_msg msg; + struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent); + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + msg.addr = i2c->addr; + msg.flags = I2C_M_RD; + msg.len = length; + msg.buf = data; + + for (attempt = 0; attempt < XFER_ATTEMPTS; attempt++) { + if (i2c_transfer(i2c->adapter, &msg, 1) == 1) { + retval = length; + goto exit; + } + LOGD(&i2c->dev, + "Transfer attempt %d failed\n", + attempt + 1); + + if (attempt + 1 == XFER_ATTEMPTS) { + LOGE(&i2c->dev, + "Transfer attempt %d failed, exceeding the maximum number of attempts\n", + attempt + 1); + retval = -EIO; + goto exit; + } + + msleep(20); + } + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_i2c_write(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, + unsigned int length) +{ + int retval = -1; + unsigned int attempt; + struct i2c_msg msg; + struct i2c_client *i2c = to_i2c_client(tcm_hcd->pdev->dev.parent); + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + msg.addr = i2c->addr; + msg.flags = 0; + msg.len = length; + msg.buf = data; + + for (attempt = 0; attempt < XFER_ATTEMPTS; attempt++) { + if (i2c_transfer(i2c->adapter, &msg, 1) == 1) { + retval = length; + goto exit; + } + LOGD(&i2c->dev, + "Transfer attempt %d failed\n", + attempt + 1); + + if (attempt + 1 == XFER_ATTEMPTS) { + LOGE(&i2c->dev, + "Transfer attempt %d failed, exceeding the maximum number of attempts\n", + attempt + 1); + retval = -EIO; + goto exit; + } + + msleep(20); + } + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *dev_id) +{ + int retval; + + syna_tcm_i2c_device = platform_device_alloc(PLATFORM_DRIVER_NAME, 0); + if (!syna_tcm_i2c_device) { + LOGE(&i2c->dev, + "Failed to allocate platform device\n"); + return -ENOMEM; + } + + i2c_set_clientdata(i2c, syna_tcm_i2c_device); + + if (i2c->dev.of_node && !mmi_device_is_available(i2c->dev.of_node)) { + LOGE(&i2c->dev, "%s: mmi: device not supported\n", __func__); + return -ENODEV; + } + +#ifdef CONFIG_OF + hw_if.bdata = devm_kzalloc(&i2c->dev, sizeof(*hw_if.bdata), GFP_KERNEL); + if (!hw_if.bdata) { + LOGE(&i2c->dev, + "Failed to allocate memory for board data\n"); + return -ENOMEM; + } + parse_dt(&i2c->dev, hw_if.bdata); +#else + hw_if.bdata = i2c->dev.platform_data; +#endif + + bus_io.type = BUS_I2C; + bus_io.read = syna_tcm_i2c_read; + bus_io.write = syna_tcm_i2c_write; + bus_io.rmi_read = syna_tcm_i2c_rmi_read; + bus_io.rmi_write = syna_tcm_i2c_rmi_write; + + hw_if.bus_io = &bus_io; + + syna_tcm_i2c_device->dev.parent = &i2c->dev; + syna_tcm_i2c_device->dev.platform_data = &hw_if; + + retval = platform_device_add(syna_tcm_i2c_device); + if (retval < 0) { + LOGE(&i2c->dev, + "Failed to add platform device\n"); + return retval; + } + + return 0; +} + +static int syna_tcm_i2c_remove(struct i2c_client *i2c) +{ + syna_tcm_i2c_device->dev.platform_data = NULL; + + platform_device_unregister(syna_tcm_i2c_device); + + return 0; +} + +static const struct i2c_device_id syna_tcm_id_table[] = { + {I2C_MODULE_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, syna_tcm_id_table); + +#ifdef CONFIG_OF +static struct of_device_id syna_tcm_of_match_table[] = { + { + .compatible = "synaptics,tcm-i2c", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, syna_tcm_of_match_table); +#else +#define syna_tcm_of_match_table NULL +#endif + +static struct i2c_driver syna_tcm_i2c_driver = { + .driver = { + .name = I2C_MODULE_NAME, + .owner = THIS_MODULE, + .of_match_table = syna_tcm_of_match_table, + }, + .probe = syna_tcm_i2c_probe, + .remove = syna_tcm_i2c_remove, + .id_table = syna_tcm_id_table, +}; + +int syna_tcm_bus_init(void) +{ + return i2c_add_driver(&syna_tcm_i2c_driver); +} +EXPORT_SYMBOL(syna_tcm_bus_init); + +void syna_tcm_bus_exit(void) +{ + kfree(buf); + + i2c_del_driver(&syna_tcm_i2c_driver); + + return; +} +EXPORT_SYMBOL(syna_tcm_bus_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM I2C Bus Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_bus/synaptics_spi.c b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_bus/synaptics_spi.c new file mode 100644 index 000000000000..17ba6dd094d8 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_bus/synaptics_spi.c @@ -0,0 +1,701 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * Copyright (C) 2018-2019 Ian Su + * Copyright (C) 2018-2019 Joey Zhou + * Copyright (C) 2018-2019 Yuehao Qiu + * Copyright (C) 2018-2019 Aaron Chen + * + * 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. + */ + +#include +#include +#include "../synaptics_core/synaptics_core.h" + +static unsigned char *buf; + +static unsigned int buf_size; + +static struct spi_transfer *xfer; + +static struct syna_tcm_bus_io bus_io; + +static struct syna_tcm_hw_interface hw_if; + +static struct platform_device *syna_tcm_spi_device; + +#ifdef CONFIG_OF +static int parse_dt(struct device *dev, struct syna_tcm_board_data *bdata) +{ + int retval; + u32 value; + struct property *prop; + struct device_node *np = dev->of_node; + const char *name; + + prop = of_find_property(np, "synaptics,irq-gpio", NULL); + if (prop && prop->length) { + bdata->irq_gpio = of_get_named_gpio_flags(np, + "synaptics,irq-gpio", 0, + (enum of_gpio_flags *)&bdata->irq_flags); + } else { + bdata->irq_gpio = -1; + } + + retval = of_property_read_u32(np, "synaptics,irq-on-state", &value); + if (retval < 0) + bdata->irq_on_state = 0; + else + bdata->irq_on_state = value; + + retval = of_property_read_string(np, "synaptics,pwr-reg-name", &name); + if (retval < 0) + bdata->pwr_reg_name = NULL; + else + bdata->pwr_reg_name = name; + + retval = of_property_read_string(np, "synaptics,bus-reg-name", &name); + if (retval < 0) + bdata->bus_reg_name = NULL; + else + bdata->bus_reg_name = name; + + prop = of_find_property(np, "synaptics,power-gpio", NULL); + if (prop && prop->length) { + bdata->power_gpio = of_get_named_gpio_flags(np, + "synaptics,power-gpio", 0, NULL); + } else { + bdata->power_gpio = -1; + } + + prop = of_find_property(np, "synaptics,power-on-state", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-on-state", + &value); + if (retval < 0) { + LOGE(dev, + "Failed to read synaptics,power-on-state property\n"); + return retval; + } else { + bdata->power_on_state = value; + } + } else { + bdata->power_on_state = 0; + } + + prop = of_find_property(np, "synaptics,power-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,power-delay-ms", + &value); + if (retval < 0) { + LOGE(dev, + "Failed to read synaptics,power-delay-ms property\n"); + return retval; + } else { + bdata->power_delay_ms = value; + } + } else { + bdata->power_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-gpio", NULL); + if (prop && prop->length) { + bdata->reset_gpio = of_get_named_gpio_flags(np, + "synaptics,reset-gpio", 0, NULL); + } else { + bdata->reset_gpio = -1; + } + + prop = of_find_property(np, "synaptics,reset-on-state", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-on-state", + &value); + if (retval < 0) { + LOGE(dev, + "Failed to read synaptics,reset-on-state property\n"); + return retval; + } else { + bdata->reset_on_state = value; + } + } else { + bdata->reset_on_state = 0; + } + + prop = of_find_property(np, "synaptics,reset-active-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-active-ms", + &value); + if (retval < 0) { + LOGE(dev, + "Failed to read synaptics,reset-active-ms property\n"); + return retval; + } else { + bdata->reset_active_ms = value; + } + } else { + bdata->reset_active_ms = 0; + } + + prop = of_find_property(np, "synaptics,reset-delay-ms", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,reset-delay-ms", + &value); + if (retval < 0) { + LOGE(dev, + "Unable to read synaptics,reset-delay-ms property\n"); + return retval; + } else { + bdata->reset_delay_ms = value; + } + } else { + bdata->reset_delay_ms = 0; + } + + prop = of_find_property(np, "synaptics,tpio-reset-gpio", NULL); + if (prop && prop->length) { + bdata->tpio_reset_gpio = of_get_named_gpio_flags(np, + "synaptics,tpio-reset-gpio", 0, NULL); + } else { + bdata->tpio_reset_gpio = -1; + } + + prop = of_find_property(np, "synaptics,x-flip", NULL); + bdata->x_flip = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,y-flip", NULL); + bdata->y_flip = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,swap-axes", NULL); + bdata->swap_axes = prop > 0 ? true : false; + + prop = of_find_property(np, "synaptics,byte-delay-us", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,byte-delay-us", + &value); + if (retval < 0) { + LOGE(dev, + "Unable to read synaptics,byte-delay-us property\n"); + return retval; + } else { + bdata->byte_delay_us = value; + } + } else { + bdata->byte_delay_us = 0; + } + + prop = of_find_property(np, "synaptics,block-delay-us", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,block-delay-us", + &value); + if (retval < 0) { + LOGE(dev, + "Unable to read synaptics,block-delay-us property\n"); + return retval; + } else { + bdata->block_delay_us = value; + } + } else { + bdata->block_delay_us = 0; + } + + prop = of_find_property(np, "synaptics,spi-mode", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,spi-mode", + &value); + if (retval < 0) { + LOGE(dev, + "Unable to read synaptics,spi-mode property\n"); + return retval; + } else { + bdata->spi_mode = value; + } + } else { + bdata->spi_mode = 0; + } + + prop = of_find_property(np, "synaptics,ubl-max-freq", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,ubl-max-freq", + &value); + if (retval < 0) { + LOGE(dev, + "Unable to read synaptics,ubl-max-freq property\n"); + return retval; + } else { + bdata->ubl_max_freq = value; + } + } else { + bdata->ubl_max_freq = 0; + } + + prop = of_find_property(np, "synaptics,ubl-byte-delay-us", NULL); + if (prop && prop->length) { + retval = of_property_read_u32(np, "synaptics,ubl-byte-delay-us", + &value); + if (retval < 0) { + LOGE(dev, + "Unable to read synaptics,ubl-byte-delay-us property\n"); + return retval; + } else { + bdata->ubl_byte_delay_us = value; + } + } else { + bdata->ubl_byte_delay_us = 0; + } + + return 0; +} +#endif + +static int syna_tcm_spi_alloc_mem(struct syna_tcm_hcd *tcm_hcd, + unsigned int count, unsigned int size) +{ + static unsigned int xfer_count; + struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent); + + if (count > xfer_count) { + kfree(xfer); + xfer = kcalloc(count, sizeof(*xfer), GFP_KERNEL); + if (!xfer) { + LOGE(&spi->dev, + "Failed to allocate memory for xfer\n"); + xfer_count = 0; + return -ENOMEM; + } + xfer_count = count; + } else { + memset(xfer, 0, count * sizeof(*xfer)); + } + + if (size > buf_size) { + if (buf_size) + kfree(buf); + buf = kmalloc(size, GFP_KERNEL); + if (!buf) { + LOGE(&spi->dev, + "Failed to allocate memory for buf\n"); + buf_size = 0; + return -ENOMEM; + } + buf_size = size; + } + + return 0; +} + +static int syna_tcm_spi_rmi_read(struct syna_tcm_hcd *tcm_hcd, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned int idx; + unsigned int mode; + unsigned int byte_count; + struct spi_message msg; + struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + spi_message_init(&msg); + + byte_count = length + 2; + + if (bdata->ubl_byte_delay_us == 0) + retval = syna_tcm_spi_alloc_mem(tcm_hcd, 2, byte_count); + else + retval = syna_tcm_spi_alloc_mem(tcm_hcd, byte_count, 3); + if (retval < 0) { + LOGE(&spi->dev, + "Failed to allocate memory\n"); + goto exit; + } + + buf[0] = (unsigned char)(addr >> 8) | 0x80; + buf[1] = (unsigned char)addr; + + if (bdata->ubl_byte_delay_us == 0) { + xfer[0].len = 2; + xfer[0].tx_buf = buf; + xfer[0].speed_hz = bdata->ubl_max_freq; + spi_message_add_tail(&xfer[0], &msg); + memset(&buf[2], 0xff, length); + xfer[1].len = length; + xfer[1].tx_buf = &buf[2]; + xfer[1].rx_buf = data; + if (bdata->block_delay_us) + xfer[1].delay_usecs = bdata->block_delay_us; + xfer[1].speed_hz = bdata->ubl_max_freq; + spi_message_add_tail(&xfer[1], &msg); + } else { + buf[2] = 0xff; + for (idx = 0; idx < byte_count; idx++) { + xfer[idx].len = 1; + if (idx < 2) { + xfer[idx].tx_buf = &buf[idx]; + } else { + xfer[idx].tx_buf = &buf[2]; + xfer[idx].rx_buf = &data[idx - 2]; + } + xfer[idx].delay_usecs = bdata->ubl_byte_delay_us; + if (bdata->block_delay_us && (idx == byte_count - 1)) + xfer[idx].delay_usecs = bdata->block_delay_us; + xfer[idx].speed_hz = bdata->ubl_max_freq; + spi_message_add_tail(&xfer[idx], &msg); + } + } + + mode = spi->mode; + spi->mode = SPI_MODE_3; + + retval = spi_sync(spi, &msg); + if (retval == 0) { + retval = length; + } else { + LOGE(&spi->dev, + "Failed to complete SPI transfer, error = %d\n", + retval); + } + + spi->mode = mode; + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_spi_rmi_write(struct syna_tcm_hcd *tcm_hcd, + unsigned short addr, unsigned char *data, unsigned int length) +{ + int retval; + unsigned int mode; + unsigned int byte_count; + struct spi_message msg; + struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + spi_message_init(&msg); + + byte_count = length + 2; + + retval = syna_tcm_spi_alloc_mem(tcm_hcd, 1, byte_count); + if (retval < 0) { + LOGE(&spi->dev, + "Failed to allocate memory\n"); + goto exit; + } + + buf[0] = (unsigned char)(addr >> 8) & ~0x80; + buf[1] = (unsigned char)addr; + retval = secure_memcpy(&buf[2], + buf_size - 2, + data, + length, + length); + if (retval < 0) { + LOGE(&spi->dev, + "Failed to copy write data\n"); + goto exit; + } + + xfer[0].len = byte_count; + xfer[0].tx_buf = buf; + if (bdata->block_delay_us) + xfer[0].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[0], &msg); + + mode = spi->mode; + spi->mode = SPI_MODE_3; + + retval = spi_sync(spi, &msg); + if (retval == 0) { + retval = length; + } else { + LOGE(&spi->dev, + "Failed to complete SPI transfer, error = %d\n", + retval); + } + + spi->mode = mode; + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_spi_read(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, + unsigned int length) +{ + int retval; + unsigned int idx; + struct spi_message msg; + struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + spi_message_init(&msg); + + if (bdata->byte_delay_us == 0) + retval = syna_tcm_spi_alloc_mem(tcm_hcd, 1, length); + else + retval = syna_tcm_spi_alloc_mem(tcm_hcd, length, 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory\n"); + goto exit; + } + + if (bdata->byte_delay_us == 0) { + memset(buf, 0xff, length); + xfer[0].len = length; + xfer[0].tx_buf = buf; + xfer[0].rx_buf = data; + if (bdata->block_delay_us) + xfer[0].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[0], &msg); + } else { + buf[0] = 0xff; + for (idx = 0; idx < length; idx++) { + xfer[idx].len = 1; + xfer[idx].tx_buf = buf; + xfer[idx].rx_buf = &data[idx]; + xfer[idx].delay_usecs = bdata->byte_delay_us; + if (bdata->block_delay_us && (idx == length - 1)) + xfer[idx].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[idx], &msg); + } + } + + retval = spi_sync(spi, &msg); + if (retval == 0) { + retval = length; + } else { + LOGE(&spi->dev, + "Failed to complete SPI transfer, error = %d\n", + retval); + } + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_spi_write(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, + unsigned int length) +{ + int retval; + unsigned int idx; + struct spi_message msg; + struct spi_device *spi = to_spi_device(tcm_hcd->pdev->dev.parent); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + mutex_lock(&tcm_hcd->io_ctrl_mutex); + + spi_message_init(&msg); + + if (bdata->byte_delay_us == 0) + retval = syna_tcm_spi_alloc_mem(tcm_hcd, 1, 0); + else + retval = syna_tcm_spi_alloc_mem(tcm_hcd, length, 0); + if (retval < 0) { + LOGE(&spi->dev, + "Failed to allocate memory\n"); + goto exit; + } + + if (bdata->byte_delay_us == 0) { + xfer[0].len = length; + xfer[0].tx_buf = data; + if (bdata->block_delay_us) + xfer[0].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[0], &msg); + } else { + for (idx = 0; idx < length; idx++) { + xfer[idx].len = 1; + xfer[idx].tx_buf = &data[idx]; + xfer[idx].delay_usecs = bdata->byte_delay_us; + if (bdata->block_delay_us && (idx == length - 1)) + xfer[idx].delay_usecs = bdata->block_delay_us; + spi_message_add_tail(&xfer[idx], &msg); + } + } + + retval = spi_sync(spi, &msg); + if (retval == 0) { + retval = length; + } else { + LOGE(&spi->dev, + "Failed to complete SPI transfer, error = %d\n", + retval); + } + +exit: + mutex_unlock(&tcm_hcd->io_ctrl_mutex); + + return retval; +} + +static int syna_tcm_spi_probe(struct spi_device *spi) +{ + int retval; + + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) { + LOGE(&spi->dev, + "Full duplex not supported by host\n"); + return -EIO; + } + + syna_tcm_spi_device = platform_device_alloc(PLATFORM_DRIVER_NAME, 0); + if (!syna_tcm_spi_device) { + LOGE(&spi->dev, + "Failed to allocate platform device\n"); + return -ENOMEM; + } + +#ifdef CONFIG_OF + hw_if.bdata = devm_kzalloc(&spi->dev, sizeof(*hw_if.bdata), GFP_KERNEL); + if (!hw_if.bdata) { + LOGE(&spi->dev, + "Failed to allocate memory for board data\n"); + return -ENOMEM; + } + parse_dt(&spi->dev, hw_if.bdata); +#else + hw_if.bdata = spi->dev.platform_data; +#endif + + switch (hw_if.bdata->spi_mode) { + case 0: + spi->mode = SPI_MODE_0; + break; + case 1: + spi->mode = SPI_MODE_1; + break; + case 2: + spi->mode = SPI_MODE_2; + break; + case 3: + spi->mode = SPI_MODE_3; + break; + } + + bus_io.type = BUS_SPI; + bus_io.read = syna_tcm_spi_read; + bus_io.write = syna_tcm_spi_write; + bus_io.rmi_read = syna_tcm_spi_rmi_read; + bus_io.rmi_write = syna_tcm_spi_rmi_write; + + hw_if.bus_io = &bus_io; + + spi->bits_per_word = 8; + + retval = spi_setup(spi); + if (retval < 0) { + LOGE(&spi->dev, + "Failed to set up SPI protocol driver\n"); + return retval; + } + + syna_tcm_spi_device->dev.parent = &spi->dev; + syna_tcm_spi_device->dev.platform_data = &hw_if; + + retval = platform_device_add(syna_tcm_spi_device); + if (retval < 0) { + LOGE(&spi->dev, + "Failed to add platform device\n"); + return retval; + } + + return 0; +} + +static int syna_tcm_spi_remove(struct spi_device *spi) +{ + syna_tcm_spi_device->dev.platform_data = NULL; + + platform_device_unregister(syna_tcm_spi_device); + + return 0; +} + +static const struct spi_device_id syna_tcm_id_table[] = { + {SPI_MODULE_NAME, 0}, + {}, +}; +MODULE_DEVICE_TABLE(spi, syna_tcm_id_table); + +#ifdef CONFIG_OF +static struct of_device_id syna_tcm_of_match_table[] = { + { + .compatible = "synaptics,tcm-spi", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, syna_tcm_of_match_table); +#else +#define syna_tcm_of_match_table NULL +#endif + +static struct spi_driver syna_tcm_spi_driver = { + .driver = { + .name = SPI_MODULE_NAME, + .owner = THIS_MODULE, + .of_match_table = syna_tcm_of_match_table, + }, + .probe = syna_tcm_spi_probe, + .remove = syna_tcm_spi_remove, + .id_table = syna_tcm_id_table, +}; + +int syna_tcm_bus_init(void) +{ + return spi_register_driver(&syna_tcm_spi_driver); +} +EXPORT_SYMBOL(syna_tcm_bus_init); + +void syna_tcm_bus_exit(void) +{ + kfree(buf); + + kfree(xfer); + + spi_unregister_driver(&syna_tcm_spi_driver); + + return; +} +EXPORT_SYMBOL(syna_tcm_bus_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM SPI Bus Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/Android.mk b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/Android.mk new file mode 100644 index 000000000000..7333abeb6c1c --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/Android.mk @@ -0,0 +1,29 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DRM_PANEL_NOTIFICATIONS),true) + KERNEL_CFLAGS += CONFIG_DRM_PANEL_NOTIFICATIONS=y +endif + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +SYNA_TCM_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +SYNA_TCM_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_core_module.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SYNA_TCM_MODULE_PATH) +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_MMI=y + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +endif +ifneq ($(findstring synaptics_i2c.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/synaptics_i2c.ko +else + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/synaptics_spi.ko +endif + +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/Kbuild b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/Kbuild new file mode 100644 index 000000000000..ee1274c3aaf2 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/Kbuild @@ -0,0 +1,18 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/synaptics_tcm_mmi_v2 + +obj-m += synaptics_core_module.o + +synaptics_core_module-objs := synaptics_core.o synaptics_touch.o +ifneq ($(filter m y, $(CONFIG_DRM_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_NOTIFICATIONS +endif +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI + synaptics_core_module-objs += synaptics_ts_mmi.o + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers +endif + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../synaptics_bus/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/Makefile b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/synaptics_core.c b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/synaptics_core.c new file mode 100644 index 000000000000..b95c18c8fcb5 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/synaptics_core.c @@ -0,0 +1,4187 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * Copyright (C) 2018-2019 Ian Su + * Copyright (C) 2018-2019 Joey Zhou + * Copyright (C) 2018-2019 Yuehao Qiu + * Copyright (C) 2018-2019 Aaron Chen + * + * 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. + */ + +#include +#include +#include +#include +#include "synaptics_core.h" + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +extern int syna_ts_mmi_dev_register(struct syna_tcm_hcd *tcm_hcd); +extern void syna_ts_mmi_dev_unregister(struct syna_tcm_hcd *tcm_hcd); +#endif + +/* #define RESET_ON_RESUME */ + +/* #define RESUME_EARLY_UNBLANK */ + +#define PREDICTIVE_READING + +#define MIN_READ_LENGTH 9 + +/* #define FORCE_RUN_APPLICATION_FIRMWARE */ + +#define NOTIFIER_PRIORITY 2 + +#define RESPONSE_TIMEOUT_MS 3000 + +#define APP_STATUS_POLL_TIMEOUT_MS 1000 + +#define APP_STATUS_POLL_MS 100 + +#define ENABLE_IRQ_DELAY_MS 20 + +#define FALL_BACK_ON_POLLING + +#define POLLING_DELAY_MS 5 + +#define MODE_SWITCH_DELAY_MS 100 + +#define READ_RETRY_US_MIN 5000 + +#define READ_RETRY_US_MAX 10000 + +#define WRITE_DELAY_US_MIN 500 + +#define WRITE_DELAY_US_MAX 1000 + +#define HOST_DOWNLOAD_WAIT_MS 100 + +#define HOST_DOWNLOAD_TIMEOUT_MS 1000 + +#define DYNAMIC_CONFIG_SYSFS_DIR_NAME "dynamic_config" + +#define ROMBOOT_DOWNLOAD_UNIT 16 + +#define PDT_END_ADDR 0x00ee + +#define RMI_UBL_FN_NUMBER 0x35 + +#define dynamic_config_sysfs(c_name, id) \ +static ssize_t syna_tcm_sysfs_##c_name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + int retval; \ + unsigned short value; \ + struct device *p_dev; \ + struct kobject *p_kobj; \ + struct syna_tcm_hcd *tcm_hcd; \ +\ + p_kobj = sysfs_dir->parent; \ + p_dev = container_of(p_kobj, struct device, kobj); \ + tcm_hcd = dev_get_drvdata(p_dev); \ +\ + mutex_lock(&tcm_hcd->extif_mutex); \ +\ + retval = tcm_hcd->get_dynamic_config(tcm_hcd, id, &value); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to get dynamic config\n"); \ + goto exit; \ + } \ +\ + retval = snprintf(buf, PAGE_SIZE, "%u\n", value); \ +\ +exit: \ + mutex_unlock(&tcm_hcd->extif_mutex); \ +\ + return retval; \ +} \ +\ +static ssize_t syna_tcm_sysfs_##c_name##_store(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + int retval; \ + unsigned int input; \ + struct device *p_dev; \ + struct kobject *p_kobj; \ + struct syna_tcm_hcd *tcm_hcd; \ +\ + p_kobj = sysfs_dir->parent; \ + p_dev = container_of(p_kobj, struct device, kobj); \ + tcm_hcd = dev_get_drvdata(p_dev); \ +\ + if (sscanf(buf, "%u", &input) != 1) \ + return -EINVAL; \ +\ + mutex_lock(&tcm_hcd->extif_mutex); \ +\ + retval = tcm_hcd->set_dynamic_config(tcm_hcd, id, input); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to set dynamic config\n"); \ + goto exit; \ + } \ +\ + retval = count; \ +\ +exit: \ + mutex_unlock(&tcm_hcd->extif_mutex); \ +\ + return retval; \ +} + +DECLARE_COMPLETION(response_complete); + +static struct kobject *sysfs_dir; + +static struct syna_tcm_module_pool mod_pool; + +SHOW_PROTOTYPE(syna_tcm, info) +SHOW_PROTOTYPE(syna_tcm, info_appfw) +STORE_PROTOTYPE(syna_tcm, irq_en) +STORE_PROTOTYPE(syna_tcm, reset) +#ifdef WATCHDOG_SW +STORE_PROTOTYPE(syna_tcm, watchdog) +#endif +SHOW_STORE_PROTOTYPE(syna_tcm, no_doze) +SHOW_STORE_PROTOTYPE(syna_tcm, disable_noise_mitigation) +SHOW_STORE_PROTOTYPE(syna_tcm, inhibit_frequency_shift) +SHOW_STORE_PROTOTYPE(syna_tcm, requested_frequency) +SHOW_STORE_PROTOTYPE(syna_tcm, disable_hsync) +SHOW_STORE_PROTOTYPE(syna_tcm, rezero_on_exit_deep_sleep) +SHOW_STORE_PROTOTYPE(syna_tcm, charger_connected) +SHOW_STORE_PROTOTYPE(syna_tcm, no_baseline_relaxation) +SHOW_STORE_PROTOTYPE(syna_tcm, in_wakeup_gesture_mode) +SHOW_STORE_PROTOTYPE(syna_tcm, stimulus_fingers) +SHOW_STORE_PROTOTYPE(syna_tcm, grip_suppression_enabled) +SHOW_STORE_PROTOTYPE(syna_tcm, enable_thick_glove) +SHOW_STORE_PROTOTYPE(syna_tcm, enable_glove) + +static struct device_attribute *attrs[] = { + ATTRIFY(info), + ATTRIFY(info_appfw), + ATTRIFY(irq_en), + ATTRIFY(reset), +#ifdef WATCHDOG_SW + ATTRIFY(watchdog), +#endif +}; + +static struct device_attribute *dynamic_config_attrs[] = { + ATTRIFY(no_doze), + ATTRIFY(disable_noise_mitigation), + ATTRIFY(inhibit_frequency_shift), + ATTRIFY(requested_frequency), + ATTRIFY(disable_hsync), + ATTRIFY(rezero_on_exit_deep_sleep), + ATTRIFY(charger_connected), + ATTRIFY(no_baseline_relaxation), + ATTRIFY(in_wakeup_gesture_mode), + ATTRIFY(stimulus_fingers), + ATTRIFY(grip_suppression_enabled), + ATTRIFY(enable_thick_glove), + ATTRIFY(enable_glove), +}; + +static int syna_tcm_get_app_info(struct syna_tcm_hcd *tcm_hcd); +static int syna_tcm_sensor_detection(struct syna_tcm_hcd *tcm_hcd); +static void syna_tcm_check_hdl(struct syna_tcm_hcd *tcm_hcd, + unsigned char id); + +static ssize_t syna_tcm_sysfs_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned int count; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm_hcd *tcm_hcd; + + p_kobj = sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm_hcd = dev_get_drvdata(p_dev); + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = tcm_hcd->identify(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + goto exit; + } + + count = 0; + + retval = snprintf(buf, PAGE_SIZE - count, + "TouchComm version: %d\n", + tcm_hcd->id_info.version); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + if (SYNAPTICS_TCM_ID_SUBVERSION == 0) { + retval = snprintf(buf, PAGE_SIZE - count, + "Driver version: %d.%d\n", + (unsigned char)(SYNAPTICS_TCM_ID_VERSION >> 8), + (unsigned char)SYNAPTICS_TCM_ID_VERSION); + } else { + retval = snprintf(buf, PAGE_SIZE - count, + "Driver version: %d.%d.%d\n", + (unsigned char)(SYNAPTICS_TCM_ID_VERSION >> 8), + (unsigned char)SYNAPTICS_TCM_ID_VERSION, + SYNAPTICS_TCM_ID_SUBVERSION); + } + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + switch (tcm_hcd->id_info.mode) { + case MODE_APPLICATION_FIRMWARE: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: Application Firmware\n"); + if (retval < 0) + goto exit; + break; + case MODE_HOSTDOWNLOAD_FIRMWARE: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: Host Download Firmware\n"); + if (retval < 0) + goto exit; + break; + case MODE_BOOTLOADER: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: Bootloader\n"); + if (retval < 0) + goto exit; + break; + case MODE_TDDI_BOOTLOADER: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: TDDI Bootloader\n"); + if (retval < 0) + goto exit; + break; + case MODE_TDDI_HOSTDOWNLOAD_BOOTLOADER: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: TDDI Host Download Bootloader\n"); + if (retval < 0) + goto exit; + break; + case MODE_ROMBOOTLOADER: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: Rom Bootloader\n"); + if (retval < 0) + goto exit; + break; + default: + retval = snprintf(buf, PAGE_SIZE - count, + "Firmware mode: Unknown (%d)\n", + tcm_hcd->id_info.mode); + if (retval < 0) + goto exit; + break; + } + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "Part number: "); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = secure_memcpy(buf, + PAGE_SIZE - count, + tcm_hcd->id_info.part_number, + sizeof(tcm_hcd->id_info.part_number), + sizeof(tcm_hcd->id_info.part_number)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy part number string\n"); + goto exit; + } + buf += sizeof(tcm_hcd->id_info.part_number); + count += sizeof(tcm_hcd->id_info.part_number); + + retval = snprintf(buf, PAGE_SIZE - count, + "\n"); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "Packrat number: %d\n", + tcm_hcd->packrat_number); + if (retval < 0) + goto exit; + + count += retval; + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t syna_tcm_sysfs_info_appfw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned int count; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm_hcd *tcm_hcd; + int i; + + p_kobj = sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm_hcd = dev_get_drvdata(p_dev); + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = syna_tcm_get_app_info(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get app info\n"); + goto exit; + } + + count = 0; + + retval = snprintf(buf, PAGE_SIZE - count, + "app info version: %d\n", + le2_to_uint(tcm_hcd->app_info.version)); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "app info status: %d\n", + le2_to_uint(tcm_hcd->app_info.status)); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "static config size: %d\n", + le2_to_uint(tcm_hcd->app_info.static_config_size)); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "dynamic config size: %d\n", + le2_to_uint(tcm_hcd->app_info.dynamic_config_size)); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "app config block: %d\n", + le2_to_uint(tcm_hcd->app_info.app_config_start_write_block)); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "app config size: %d\n", + le2_to_uint(tcm_hcd->app_info.app_config_size)); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "touch report config max size: %d\n", + le2_to_uint(tcm_hcd->app_info.max_touch_report_config_size)); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "touch report payload max size: %d\n", + le2_to_uint(tcm_hcd->app_info.max_touch_report_payload_size)); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, "config id: "); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + for (i = 0; i < sizeof(tcm_hcd->app_info.customer_config_id); i++) { + retval = snprintf(buf, PAGE_SIZE - count, + "0x%2x ", tcm_hcd->app_info.customer_config_id[i]); + buf += retval; + count += retval; + } + + retval = snprintf(buf, PAGE_SIZE - count, "\n"); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "max x: %d\n", + le2_to_uint(tcm_hcd->app_info.max_x)); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "max y: %d\n", + le2_to_uint(tcm_hcd->app_info.max_y)); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "max objects: %d\n", + le2_to_uint(tcm_hcd->app_info.max_objects)); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "num cols: %d\n", + le2_to_uint(tcm_hcd->app_info.num_of_image_cols)); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "num rows: %d\n", + le2_to_uint(tcm_hcd->app_info.num_of_image_rows)); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "num buttons: %d\n", + le2_to_uint(tcm_hcd->app_info.num_of_buttons)); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "has profile: %d\n", + le2_to_uint(tcm_hcd->app_info.has_hybrid_data)); + if (retval < 0) + goto exit; + + buf += retval; + count += retval; + + retval = snprintf(buf, PAGE_SIZE - count, + "num force electrodes: %d\n", + le2_to_uint(tcm_hcd->app_info.num_of_force_elecs)); + if (retval < 0) + goto exit; + + count += retval; + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t syna_tcm_sysfs_irq_en_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm_hcd *tcm_hcd; + + p_kobj = sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm_hcd = dev_get_drvdata(p_dev); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (input == 0) { + retval = tcm_hcd->enable_irq(tcm_hcd, false, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to disable interrupt\n"); + goto exit; + } + } else if (input == 1) { + retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable interrupt\n"); + goto exit; + } + } else { + retval = -EINVAL; + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t syna_tcm_sysfs_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + bool hw_reset; + unsigned int input; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm_hcd *tcm_hcd; + + p_kobj = sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm_hcd = dev_get_drvdata(p_dev); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input == 1) + hw_reset = false; + else if (input == 2) + hw_reset = true; + else + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = tcm_hcd->reset_n_reinit(tcm_hcd, hw_reset, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset and reinit\n"); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +#ifdef WATCHDOG_SW +static ssize_t syna_tcm_sysfs_watchdog_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct device *p_dev; + struct kobject *p_kobj; + struct syna_tcm_hcd *tcm_hcd; + + p_kobj = sysfs_dir->parent; + p_dev = container_of(p_kobj, struct device, kobj); + tcm_hcd = dev_get_drvdata(p_dev); + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input != 0 && input != 1) + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + tcm_hcd->watchdog.run = input; + tcm_hcd->update_watchdog(tcm_hcd, input); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return count; +} +#endif + +dynamic_config_sysfs(no_doze, DC_NO_DOZE) + +dynamic_config_sysfs(disable_noise_mitigation, DC_DISABLE_NOISE_MITIGATION) + +dynamic_config_sysfs(inhibit_frequency_shift, DC_INHIBIT_FREQUENCY_SHIFT) + +dynamic_config_sysfs(requested_frequency, DC_REQUESTED_FREQUENCY) + +dynamic_config_sysfs(disable_hsync, DC_DISABLE_HSYNC) + +dynamic_config_sysfs(rezero_on_exit_deep_sleep, DC_REZERO_ON_EXIT_DEEP_SLEEP) + +dynamic_config_sysfs(charger_connected, DC_CHARGER_CONNECTED) + +dynamic_config_sysfs(no_baseline_relaxation, DC_NO_BASELINE_RELAXATION) + +dynamic_config_sysfs(in_wakeup_gesture_mode, DC_IN_WAKEUP_GESTURE_MODE) + +dynamic_config_sysfs(stimulus_fingers, DC_STIMULUS_FINGERS) + +dynamic_config_sysfs(grip_suppression_enabled, DC_GRIP_SUPPRESSION_ENABLED) + +dynamic_config_sysfs(enable_thick_glove, DC_ENABLE_THICK_GLOVE) + +dynamic_config_sysfs(enable_glove, DC_ENABLE_GLOVE) + + +int syna_tcm_add_module(struct syna_tcm_module_cb *mod_cb, bool insert) +{ + struct syna_tcm_module_handler *mod_handler; + + if (!mod_pool.initialized) { + mutex_init(&mod_pool.mutex); + INIT_LIST_HEAD(&mod_pool.list); + mod_pool.initialized = true; + } + + mutex_lock(&mod_pool.mutex); + + if (insert) { + mod_handler = kzalloc(sizeof(*mod_handler), GFP_KERNEL); + if (!mod_handler) { + pr_err("%s: Failed to allocate memory for mod_handler\n", + __func__); + mutex_unlock(&mod_pool.mutex); + return -ENOMEM; + } + mod_handler->mod_cb = mod_cb; + mod_handler->insert = true; + mod_handler->detach = false; + list_add_tail(&mod_handler->link, &mod_pool.list); + } else if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (mod_handler->mod_cb->type == mod_cb->type) { + mod_handler->insert = false; + mod_handler->detach = true; + goto exit; + } + } + } + +exit: + mutex_unlock(&mod_pool.mutex); + + if (mod_pool.queue_work) + queue_work(mod_pool.workqueue, &mod_pool.work); + + return 0; +} +EXPORT_SYMBOL(syna_tcm_add_module); + +static void syna_tcm_module_work(struct work_struct *work) +{ + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_module_handler *tmp_handler; + struct syna_tcm_hcd *tcm_hcd = mod_pool.tcm_hcd; + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry_safe(mod_handler, + tmp_handler, + &mod_pool.list, + link) { + if (mod_handler->insert) { + if (mod_handler->mod_cb->init) + mod_handler->mod_cb->init(tcm_hcd); + mod_handler->insert = false; + } + if (mod_handler->detach) { + if (mod_handler->mod_cb->remove) + mod_handler->mod_cb->remove(tcm_hcd); + list_del(&mod_handler->link); + kfree(mod_handler); + } + } + } + + mutex_unlock(&mod_pool.mutex); + + return; +} + + +#ifdef REPORT_NOTIFIER +/** + * syna_tcm_report_notifier() - notify occurrence of report received from device + * + * @data: handle of core module + * + * The occurrence of the report generated by the device is forwarded to the + * asynchronous inbox of each registered application module. + */ +static int syna_tcm_report_notifier(void *data) +{ + struct sched_param param = { .sched_priority = NOTIFIER_PRIORITY }; + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_hcd *tcm_hcd = data; + + sched_setscheduler(current, SCHED_RR, ¶m); + + set_current_state(TASK_INTERRUPTIBLE); + + while (!kthread_should_stop()) { + schedule(); + + if (kthread_should_stop()) + break; + + set_current_state(TASK_RUNNING); + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->asyncbox)) + mod_handler->mod_cb->asyncbox(tcm_hcd); + } + } + + mutex_unlock(&mod_pool.mutex); + + set_current_state(TASK_INTERRUPTIBLE); + }; + + return 0; +} +#endif + +/** + * syna_tcm_dispatch_report() - dispatch report received from device + * + * @tcm_hcd: handle of core module + * + * The report generated by the device is forwarded to the synchronous inbox of + * each registered application module for further processing. In addition, the + * report notifier thread is woken up for asynchronous notification of the + * report occurrence. + */ +static void syna_tcm_dispatch_report(struct syna_tcm_hcd *tcm_hcd) +{ + struct syna_tcm_module_handler *mod_handler; + + LOCK_BUFFER(tcm_hcd->in); + LOCK_BUFFER(tcm_hcd->report.buffer); + + tcm_hcd->report.buffer.buf = &tcm_hcd->in.buf[MESSAGE_HEADER_SIZE]; + + tcm_hcd->report.buffer.buf_size = tcm_hcd->in.buf_size; + tcm_hcd->report.buffer.buf_size -= MESSAGE_HEADER_SIZE; + + tcm_hcd->report.buffer.data_length = tcm_hcd->payload_length; + + tcm_hcd->report.id = tcm_hcd->status_report_code; + + /* report directly if touch report is received */ + if (tcm_hcd->report.id == REPORT_TOUCH) { + if (tcm_hcd->report_touch) + tcm_hcd->report_touch(); + + } else { + + /* once an identify report is received, */ + /* reinitialize touch in case any changes */ + if ((tcm_hcd->report.id == REPORT_IDENTIFY) && + IS_FW_MODE(tcm_hcd->id_info.mode)) { + + if (atomic_read(&tcm_hcd->helper.task) == HELP_NONE) { + atomic_set(&tcm_hcd->helper.task, + HELP_TOUCH_REINIT); + queue_work(tcm_hcd->helper.workqueue, + &tcm_hcd->helper.work); + } + } + + /* dispatch received report to the other modules */ + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->syncbox)) + mod_handler->mod_cb->syncbox(tcm_hcd); + } + } + + tcm_hcd->async_report_id = tcm_hcd->status_report_code; + + mutex_unlock(&mod_pool.mutex); + } + + UNLOCK_BUFFER(tcm_hcd->report.buffer); + UNLOCK_BUFFER(tcm_hcd->in); + +#ifdef REPORT_NOTIFIER + wake_up_process(tcm_hcd->notifier_thread); +#endif + + return; +} + +/** + * syna_tcm_dispatch_response() - dispatch response received from device + * + * @tcm_hcd: handle of core module + * + * The response to a command is forwarded to the sender of the command. + */ +static void syna_tcm_dispatch_response(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (atomic_read(&tcm_hcd->command_status) != CMD_BUSY) + return; + + tcm_hcd->response_code = tcm_hcd->status_report_code; + + if (tcm_hcd->payload_length == 0) { + atomic_set(&tcm_hcd->command_status, CMD_IDLE); + goto exit; + } + + LOCK_BUFFER(tcm_hcd->resp); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->resp, + tcm_hcd->payload_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for tcm_hcd->resp.buf\n"); + UNLOCK_BUFFER(tcm_hcd->resp); + atomic_set(&tcm_hcd->command_status, CMD_ERROR); + goto exit; + } + + LOCK_BUFFER(tcm_hcd->in); + + retval = secure_memcpy(tcm_hcd->resp.buf, + tcm_hcd->resp.buf_size, + &tcm_hcd->in.buf[MESSAGE_HEADER_SIZE], + tcm_hcd->in.buf_size - MESSAGE_HEADER_SIZE, + tcm_hcd->payload_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy payload\n"); + UNLOCK_BUFFER(tcm_hcd->in); + UNLOCK_BUFFER(tcm_hcd->resp); + atomic_set(&tcm_hcd->command_status, CMD_ERROR); + goto exit; + } + + tcm_hcd->resp.data_length = tcm_hcd->payload_length; + + UNLOCK_BUFFER(tcm_hcd->in); + UNLOCK_BUFFER(tcm_hcd->resp); + + atomic_set(&tcm_hcd->command_status, CMD_IDLE); + +exit: + complete(&response_complete); + + return; +} + +/** + * syna_tcm_dispatch_message() - dispatch message received from device + * + * @tcm_hcd: handle of core module + * + * The information received in the message read in from the device is dispatched + * to the appropriate destination based on whether the information represents a + * report or a response to a command. + */ +static void syna_tcm_dispatch_message(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *build_id; + unsigned int payload_length; + unsigned int max_write_size; + + if (tcm_hcd->status_report_code == REPORT_IDENTIFY) { + payload_length = tcm_hcd->payload_length; + + LOCK_BUFFER(tcm_hcd->in); + + retval = secure_memcpy((unsigned char *)&tcm_hcd->id_info, + sizeof(tcm_hcd->id_info), + &tcm_hcd->in.buf[MESSAGE_HEADER_SIZE], + tcm_hcd->in.buf_size - MESSAGE_HEADER_SIZE, + MIN(sizeof(tcm_hcd->id_info), payload_length)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy identification info\n"); + UNLOCK_BUFFER(tcm_hcd->in); + return; + } + + UNLOCK_BUFFER(tcm_hcd->in); + + build_id = tcm_hcd->id_info.build_id; + tcm_hcd->packrat_number = le4_to_uint(build_id); + + max_write_size = le2_to_uint(tcm_hcd->id_info.max_write_size); + tcm_hcd->wr_chunk_size = MIN(max_write_size, WR_CHUNK_SIZE); + if (tcm_hcd->wr_chunk_size == 0) + tcm_hcd->wr_chunk_size = max_write_size; + + LOGN(tcm_hcd->pdev->dev.parent, + "Received identify report (firmware mode = 0x%02x)\n", + tcm_hcd->id_info.mode); + + if (atomic_read(&tcm_hcd->command_status) == CMD_BUSY) { + switch (tcm_hcd->command) { + case CMD_RESET: + case CMD_RUN_BOOTLOADER_FIRMWARE: + case CMD_RUN_APPLICATION_FIRMWARE: + case CMD_ENTER_PRODUCTION_TEST_MODE: + case CMD_ROMBOOT_RUN_BOOTLOADER_FIRMWARE: + tcm_hcd->response_code = STATUS_OK; + atomic_set(&tcm_hcd->command_status, CMD_IDLE); + complete(&response_complete); + break; + default: + LOGN(tcm_hcd->pdev->dev.parent, + "Device has been reset\n"); + atomic_set(&tcm_hcd->command_status, CMD_ERROR); + complete(&response_complete); + break; + } + } else { + + if ((tcm_hcd->id_info.mode == MODE_ROMBOOTLOADER) && + tcm_hcd->in_hdl_mode) { + + if (atomic_read(&tcm_hcd->helper.task) == + HELP_NONE) { + atomic_set(&tcm_hcd->helper.task, + HELP_SEND_ROMBOOT_HDL); + queue_work(tcm_hcd->helper.workqueue, + &tcm_hcd->helper.work); + } else { + LOGN(tcm_hcd->pdev->dev.parent, + "Helper thread is busy\n"); + } + return; + } + } + +#ifdef FORCE_RUN_APPLICATION_FIRMWARE + if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode) && + !mutex_is_locked(&tcm_hcd->reset_mutex)) { + + if (atomic_read(&tcm_hcd->helper.task) == HELP_NONE) { + atomic_set(&tcm_hcd->helper.task, + HELP_RUN_APPLICATION_FIRMWARE); + queue_work(tcm_hcd->helper.workqueue, + &tcm_hcd->helper.work); + return; + } + } +#endif + } + + + if (tcm_hcd->status_report_code >= REPORT_IDENTIFY) + syna_tcm_dispatch_report(tcm_hcd); + else + syna_tcm_dispatch_response(tcm_hcd); + + return; +} + +/** + * syna_tcm_continued_read() - retrieve entire payload from device + * + * @tcm_hcd: handle of core module + * + * Read transactions are carried out until the entire payload is retrieved from + * the device and stored in the handle of the core module. + */ +static int syna_tcm_continued_read(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char marker; + unsigned char code; + unsigned int idx; + unsigned int offset; + unsigned int chunks; + unsigned int chunk_space; + unsigned int xfer_length; + unsigned int total_length; + unsigned int remaining_length; + + total_length = MESSAGE_HEADER_SIZE + tcm_hcd->payload_length + 1; + + remaining_length = total_length - tcm_hcd->read_length; + + LOCK_BUFFER(tcm_hcd->in); + + retval = syna_tcm_realloc_mem(tcm_hcd, + &tcm_hcd->in, + total_length + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reallocate memory for tcm_hcd->in.buf\n"); + UNLOCK_BUFFER(tcm_hcd->in); + return retval; + } + + /* available chunk space for payload = total chunk size minus header + * marker byte and header code byte */ + if (tcm_hcd->rd_chunk_size == 0) + chunk_space = remaining_length; + else + chunk_space = tcm_hcd->rd_chunk_size - 2; + + chunks = ceil_div(remaining_length, chunk_space); + + chunks = chunks == 0 ? 1 : chunks; + + offset = tcm_hcd->read_length; + + LOCK_BUFFER(tcm_hcd->temp); + + for (idx = 0; idx < chunks; idx++) { + if (remaining_length > chunk_space) + xfer_length = chunk_space; + else + xfer_length = remaining_length; + + if (xfer_length == 1) { + tcm_hcd->in.buf[offset] = MESSAGE_PADDING; + offset += xfer_length; + remaining_length -= xfer_length; + continue; + } + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->temp, + xfer_length + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for tcm_hcd->temp.buf\n"); + UNLOCK_BUFFER(tcm_hcd->temp); + UNLOCK_BUFFER(tcm_hcd->in); + return retval; + } + + retval = syna_tcm_read(tcm_hcd, + tcm_hcd->temp.buf, + xfer_length + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read from device\n"); + UNLOCK_BUFFER(tcm_hcd->temp); + UNLOCK_BUFFER(tcm_hcd->in); + return retval; + } + + marker = tcm_hcd->temp.buf[0]; + code = tcm_hcd->temp.buf[1]; + + if (marker != MESSAGE_MARKER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect header marker (0x%02x)\n", + marker); + UNLOCK_BUFFER(tcm_hcd->temp); + UNLOCK_BUFFER(tcm_hcd->in); + return -EIO; + } + + if (code != STATUS_CONTINUED_READ) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect header code (0x%02x)\n", + code); + UNLOCK_BUFFER(tcm_hcd->temp); + UNLOCK_BUFFER(tcm_hcd->in); + return -EIO; + } + + retval = secure_memcpy(&tcm_hcd->in.buf[offset], + tcm_hcd->in.buf_size - offset, + &tcm_hcd->temp.buf[2], + tcm_hcd->temp.buf_size - 2, + xfer_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy payload\n"); + UNLOCK_BUFFER(tcm_hcd->temp); + UNLOCK_BUFFER(tcm_hcd->in); + return retval; + } + + offset += xfer_length; + + remaining_length -= xfer_length; + } + + UNLOCK_BUFFER(tcm_hcd->temp); + UNLOCK_BUFFER(tcm_hcd->in); + + return 0; +} + +/** + * syna_tcm_raw_read() - retrieve specific number of data bytes from device + * + * @tcm_hcd: handle of core module + * @in_buf: buffer for storing data retrieved from device + * @length: number of bytes to retrieve from device + * + * Read transactions are carried out until the specific number of data bytes are + * retrieved from the device and stored in in_buf. + */ +static int syna_tcm_raw_read(struct syna_tcm_hcd *tcm_hcd, + unsigned char *in_buf, unsigned int length) +{ + int retval; + unsigned char code; + unsigned int idx; + unsigned int offset; + unsigned int chunks; + unsigned int chunk_space; + unsigned int xfer_length; + unsigned int remaining_length; + + if (length < 2) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid length information\n"); + return -EINVAL; + } + + /* minus header marker byte and header code byte */ + remaining_length = length - 2; + + /* available chunk space for data = total chunk size minus header marker + * byte and header code byte */ + if (tcm_hcd->rd_chunk_size == 0) + chunk_space = remaining_length; + else + chunk_space = tcm_hcd->rd_chunk_size - 2; + + chunks = ceil_div(remaining_length, chunk_space); + + chunks = chunks == 0 ? 1 : chunks; + + offset = 0; + + LOCK_BUFFER(tcm_hcd->temp); + + for (idx = 0; idx < chunks; idx++) { + if (remaining_length > chunk_space) + xfer_length = chunk_space; + else + xfer_length = remaining_length; + + if (xfer_length == 1) { + in_buf[offset] = MESSAGE_PADDING; + offset += xfer_length; + remaining_length -= xfer_length; + continue; + } + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->temp, + xfer_length + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for tcm_hcd->temp.buf\n"); + UNLOCK_BUFFER(tcm_hcd->temp); + return retval; + } + + retval = syna_tcm_read(tcm_hcd, + tcm_hcd->temp.buf, + xfer_length + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read from device\n"); + UNLOCK_BUFFER(tcm_hcd->temp); + return retval; + } + + code = tcm_hcd->temp.buf[1]; + + if (idx == 0) { + retval = secure_memcpy(&in_buf[0], + length, + &tcm_hcd->temp.buf[0], + tcm_hcd->temp.buf_size, + xfer_length + 2); + } else { + if (code != STATUS_CONTINUED_READ) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect header code (0x%02x)\n", + code); + UNLOCK_BUFFER(tcm_hcd->temp); + return -EIO; + } + + retval = secure_memcpy(&in_buf[offset], + length - offset, + &tcm_hcd->temp.buf[2], + tcm_hcd->temp.buf_size - 2, + xfer_length); + } + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy data\n"); + UNLOCK_BUFFER(tcm_hcd->temp); + return retval; + } + + if (idx == 0) + offset += (xfer_length + 2); + else + offset += xfer_length; + + remaining_length -= xfer_length; + } + + UNLOCK_BUFFER(tcm_hcd->temp); + + return 0; +} + +/** + * syna_tcm_raw_write() - write command/data to device without receiving + * response + * + * @tcm_hcd: handle of core module + * @command: command to send to device + * @data: data to send to device + * @length: length of data in bytes + * + * A command and its data, if any, are sent to the device. + */ +static int syna_tcm_raw_write(struct syna_tcm_hcd *tcm_hcd, + unsigned char command, unsigned char *data, unsigned int length) +{ + int retval; + unsigned int idx; + unsigned int chunks; + unsigned int chunk_space; + unsigned int xfer_length; + unsigned int remaining_length; + + remaining_length = length; + + /* available chunk space for data = total chunk size minus command + * byte */ + if (tcm_hcd->wr_chunk_size == 0) + chunk_space = remaining_length; + else + chunk_space = tcm_hcd->wr_chunk_size - 1; + + chunks = ceil_div(remaining_length, chunk_space); + + chunks = chunks == 0 ? 1 : chunks; + + LOCK_BUFFER(tcm_hcd->out); + + for (idx = 0; idx < chunks; idx++) { + if (remaining_length > chunk_space) + xfer_length = chunk_space; + else + xfer_length = remaining_length; + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->out, + xfer_length + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for tcm_hcd->out.buf\n"); + UNLOCK_BUFFER(tcm_hcd->out); + return retval; + } + + if (idx == 0) + tcm_hcd->out.buf[0] = command; + else + tcm_hcd->out.buf[0] = CMD_CONTINUE_WRITE; + + if (xfer_length) { + retval = secure_memcpy(&tcm_hcd->out.buf[1], + tcm_hcd->out.buf_size - 1, + &data[idx * chunk_space], + remaining_length, + xfer_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy data\n"); + UNLOCK_BUFFER(tcm_hcd->out); + return retval; + } + } + + retval = syna_tcm_write(tcm_hcd, + tcm_hcd->out.buf, + xfer_length + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to device\n"); + UNLOCK_BUFFER(tcm_hcd->out); + return retval; + } + + remaining_length -= xfer_length; + } + + UNLOCK_BUFFER(tcm_hcd->out); + + return 0; +} + +/** + * syna_tcm_read_message() - read message from device + * + * @tcm_hcd: handle of core module + * @in_buf: buffer for storing data in raw read mode + * @length: length of data in bytes in raw read mode + * + * If in_buf is not NULL, raw read mode is used and syna_tcm_raw_read() is + * called. Otherwise, a message including its entire payload is retrieved from + * the device and dispatched to the appropriate destination. + */ +static int syna_tcm_read_message(struct syna_tcm_hcd *tcm_hcd, + unsigned char *in_buf, unsigned int length) +{ + int retval; + bool retry; + unsigned int total_length; + struct syna_tcm_message_header *header; + + mutex_lock(&tcm_hcd->rw_ctrl_mutex); + + if (in_buf != NULL) { + retval = syna_tcm_raw_read(tcm_hcd, in_buf, length); + goto exit; + } + + retry = true; + +retry: + LOCK_BUFFER(tcm_hcd->in); + + retval = syna_tcm_read(tcm_hcd, + tcm_hcd->in.buf, + tcm_hcd->read_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read from device\n"); + UNLOCK_BUFFER(tcm_hcd->in); + if (retry) { + usleep_range(READ_RETRY_US_MIN, READ_RETRY_US_MAX); + retry = false; + goto retry; + } + goto exit; + } + + header = (struct syna_tcm_message_header *)tcm_hcd->in.buf; + + + if (header->marker != MESSAGE_MARKER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect header marker (0x%02x)\n", + header->marker); + UNLOCK_BUFFER(tcm_hcd->in); + retval = -ENXIO; + if (retry) { + usleep_range(READ_RETRY_US_MIN, READ_RETRY_US_MAX); + retry = false; + goto retry; + } + goto exit; + } + + tcm_hcd->status_report_code = header->code; + + tcm_hcd->payload_length = le2_to_uint(header->length); + + LOGD(tcm_hcd->pdev->dev.parent, + "Status report code = 0x%02x\n", + tcm_hcd->status_report_code); + + LOGD(tcm_hcd->pdev->dev.parent, + "Payload length = %d\n", + tcm_hcd->payload_length); + + if (tcm_hcd->status_report_code <= STATUS_ERROR || + tcm_hcd->status_report_code == STATUS_INVALID) { + switch (tcm_hcd->status_report_code) { + case STATUS_OK: + break; + case STATUS_CONTINUED_READ: + LOGD(tcm_hcd->pdev->dev.parent, + "Out-of-sync continued read\n"); + case STATUS_IDLE: + case STATUS_BUSY: + tcm_hcd->payload_length = 0; + UNLOCK_BUFFER(tcm_hcd->in); + retval = 0; + goto exit; + default: + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect Status code (0x%02x)\n", + tcm_hcd->status_report_code); + if (tcm_hcd->status_report_code == STATUS_INVALID) { + if (retry) { + usleep_range(READ_RETRY_US_MIN, + READ_RETRY_US_MAX); + retry = false; + goto retry; + } else { + tcm_hcd->payload_length = 0; + } + } + } + } + + total_length = MESSAGE_HEADER_SIZE + tcm_hcd->payload_length + 1; + +#ifdef PREDICTIVE_READING + if (total_length <= tcm_hcd->read_length) { + goto check_padding; + } else if (total_length - 1 == tcm_hcd->read_length) { + tcm_hcd->in.buf[total_length - 1] = MESSAGE_PADDING; + goto check_padding; + } +#else + if (tcm_hcd->payload_length == 0) { + tcm_hcd->in.buf[total_length - 1] = MESSAGE_PADDING; + goto check_padding; + } +#endif + + UNLOCK_BUFFER(tcm_hcd->in); + + retval = syna_tcm_continued_read(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do continued read\n"); + goto exit; + }; + + LOCK_BUFFER(tcm_hcd->in); + + tcm_hcd->in.buf[0] = MESSAGE_MARKER; + tcm_hcd->in.buf[1] = tcm_hcd->status_report_code; + tcm_hcd->in.buf[2] = (unsigned char)tcm_hcd->payload_length; + tcm_hcd->in.buf[3] = (unsigned char)(tcm_hcd->payload_length >> 8); + +check_padding: + if (tcm_hcd->in.buf[total_length - 1] != MESSAGE_PADDING) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect message padding byte (0x%02x)\n", + tcm_hcd->in.buf[total_length - 1]); + UNLOCK_BUFFER(tcm_hcd->in); + retval = -EIO; + goto exit; + } + + UNLOCK_BUFFER(tcm_hcd->in); + +#ifdef PREDICTIVE_READING + total_length = MAX(total_length, MIN_READ_LENGTH); + tcm_hcd->read_length = MIN(total_length, tcm_hcd->rd_chunk_size); + if (tcm_hcd->rd_chunk_size == 0) + tcm_hcd->read_length = total_length; +#endif + if (tcm_hcd->is_detected) + syna_tcm_dispatch_message(tcm_hcd); + + retval = 0; + +exit: + if (retval < 0) { + if (atomic_read(&tcm_hcd->command_status) == CMD_BUSY) { + atomic_set(&tcm_hcd->command_status, CMD_ERROR); + complete(&response_complete); + } + } + + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + + return retval; +} + +/** + * syna_tcm_write_message() - write message to device and receive response + * + * @tcm_hcd: handle of core module + * @command: command to send to device + * @payload: payload of command + * @length: length of payload in bytes + * @resp_buf: buffer for storing command response + * @resp_buf_size: size of response buffer in bytes + * @resp_length: length of command response in bytes + * @response_code: status code returned in command response + * @polling_delay_ms: delay time after sending command before resuming polling + * + * If resp_buf is NULL, raw write mode is used and syna_tcm_raw_write() is + * called. Otherwise, a command and its payload, if any, are sent to the device + * and the response to the command generated by the device is read in. + */ +static int syna_tcm_write_message(struct syna_tcm_hcd *tcm_hcd, + unsigned char command, unsigned char *payload, + unsigned int length, unsigned char **resp_buf, + unsigned int *resp_buf_size, unsigned int *resp_length, + unsigned char *response_code, unsigned int polling_delay_ms) +{ + int retval; + unsigned int idx; + unsigned int chunks; + unsigned int chunk_space; + unsigned int xfer_length; + unsigned int remaining_length; + unsigned int command_status; + bool is_romboot_hdl = (command == CMD_ROMBOOT_DOWNLOAD) ? true : false; + bool is_hdl_reset = (command == CMD_RESET) && (tcm_hcd->in_hdl_mode); + + if (response_code != NULL) + *response_code = STATUS_INVALID; + + if (!tcm_hcd->do_polling && current->pid == tcm_hcd->isr_pid) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid execution context\n"); + return -EINVAL; + } + + mutex_lock(&tcm_hcd->command_mutex); + + mutex_lock(&tcm_hcd->rw_ctrl_mutex); + + if (resp_buf == NULL) { + retval = syna_tcm_raw_write(tcm_hcd, command, payload, length); + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + goto exit; + } + + if (tcm_hcd->do_polling && polling_delay_ms) { + cancel_delayed_work_sync(&tcm_hcd->polling_work); + flush_workqueue(tcm_hcd->polling_workqueue); + } + + atomic_set(&tcm_hcd->command_status, CMD_BUSY); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)) + reinit_completion(&response_complete); +#else + INIT_COMPLETION(response_complete); +#endif + + tcm_hcd->command = command; + + LOCK_BUFFER(tcm_hcd->resp); + + tcm_hcd->resp.buf = *resp_buf; + tcm_hcd->resp.buf_size = *resp_buf_size; + tcm_hcd->resp.data_length = 0; + + UNLOCK_BUFFER(tcm_hcd->resp); + + /* adding two length bytes as part of payload */ + remaining_length = length + 2; + + /* available chunk space for payload = total chunk size minus command + * byte */ + if (tcm_hcd->wr_chunk_size == 0) + chunk_space = remaining_length; + else + chunk_space = tcm_hcd->wr_chunk_size - 1; + + if (is_romboot_hdl) { + if (WR_CHUNK_SIZE) { + chunk_space = WR_CHUNK_SIZE - 1; + chunk_space = chunk_space - + (chunk_space % ROMBOOT_DOWNLOAD_UNIT); + } else { + chunk_space = remaining_length; + } + } + + chunks = ceil_div(remaining_length, chunk_space); + + chunks = chunks == 0 ? 1 : chunks; + + LOGD(tcm_hcd->pdev->dev.parent, + "Command = 0x%02x\n", + command); + + LOCK_BUFFER(tcm_hcd->out); + + for (idx = 0; idx < chunks; idx++) { + if (remaining_length > chunk_space) + xfer_length = chunk_space; + else + xfer_length = remaining_length; + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->out, + xfer_length + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for tcm_hcd->out.buf\n"); + UNLOCK_BUFFER(tcm_hcd->out); + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + goto exit; + } + + if (idx == 0) { + tcm_hcd->out.buf[0] = command; + tcm_hcd->out.buf[1] = (unsigned char)length; + tcm_hcd->out.buf[2] = (unsigned char)(length >> 8); + + if (xfer_length > 2) { + retval = secure_memcpy(&tcm_hcd->out.buf[3], + tcm_hcd->out.buf_size - 3, + payload, + remaining_length - 2, + xfer_length - 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy payload\n"); + UNLOCK_BUFFER(tcm_hcd->out); + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + goto exit; + } + } + } else { + tcm_hcd->out.buf[0] = CMD_CONTINUE_WRITE; + + retval = secure_memcpy(&tcm_hcd->out.buf[1], + tcm_hcd->out.buf_size - 1, + &payload[idx * chunk_space - 2], + remaining_length, + xfer_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy payload\n"); + UNLOCK_BUFFER(tcm_hcd->out); + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + goto exit; + } + } + + retval = syna_tcm_write(tcm_hcd, + tcm_hcd->out.buf, + xfer_length + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to device\n"); + UNLOCK_BUFFER(tcm_hcd->out); + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + goto exit; + } + + remaining_length -= xfer_length; + + if (chunks > 1) + usleep_range(WRITE_DELAY_US_MIN, WRITE_DELAY_US_MAX); + } + + UNLOCK_BUFFER(tcm_hcd->out); + + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + + if (is_hdl_reset) + goto exit; + + if (tcm_hcd->do_polling && polling_delay_ms) { + queue_delayed_work(tcm_hcd->polling_workqueue, + &tcm_hcd->polling_work, + msecs_to_jiffies(polling_delay_ms)); + } + + retval = wait_for_completion_timeout(&response_complete, + msecs_to_jiffies(RESPONSE_TIMEOUT_MS)); + if (retval == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Timed out waiting for response (command 0x%02x)\n", + tcm_hcd->command); + retval = -ETIME; + goto exit; + } + + command_status = atomic_read(&tcm_hcd->command_status); + if (command_status != CMD_IDLE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get valid response (command 0x%02x)\n", + tcm_hcd->command); + retval = -EIO; + goto exit; + } + + LOCK_BUFFER(tcm_hcd->resp); + + if (tcm_hcd->response_code != STATUS_OK) { + if (tcm_hcd->resp.data_length) { + LOGE(tcm_hcd->pdev->dev.parent, + "Error code = 0x%02x (command 0x%02x)\n", + tcm_hcd->resp.buf[0], tcm_hcd->command); + } + retval = -EIO; + } else { + retval = 0; + } + + *resp_buf = tcm_hcd->resp.buf; + *resp_buf_size = tcm_hcd->resp.buf_size; + *resp_length = tcm_hcd->resp.data_length; + + if (response_code != NULL) + *response_code = tcm_hcd->response_code; + + UNLOCK_BUFFER(tcm_hcd->resp); + +exit: + tcm_hcd->command = CMD_NONE; + + atomic_set(&tcm_hcd->command_status, CMD_IDLE); + + mutex_unlock(&tcm_hcd->command_mutex); + + return retval; +} + +static int syna_tcm_wait_hdl(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + msleep(HOST_DOWNLOAD_WAIT_MS); + + if (!atomic_read(&tcm_hcd->host_downloading)) + return 0; + + retval = wait_event_interruptible_timeout(tcm_hcd->hdl_wq, + !atomic_read(&tcm_hcd->host_downloading), + msecs_to_jiffies(HOST_DOWNLOAD_TIMEOUT_MS)); + if (retval == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Timed out waiting for completion of host download\n"); + atomic_set(&tcm_hcd->host_downloading, 0); + retval = -EIO; + } else { + retval = 0; + } + + return retval; +} + +static void syna_tcm_check_hdl(struct syna_tcm_hcd *tcm_hcd, unsigned char id) +{ + struct syna_tcm_module_handler *mod_handler; + + LOCK_BUFFER(tcm_hcd->report.buffer); + + tcm_hcd->report.buffer.buf = NULL; + tcm_hcd->report.buffer.buf_size = 0; + tcm_hcd->report.buffer.data_length = 0; + tcm_hcd->report.id = id; + + UNLOCK_BUFFER(tcm_hcd->report.buffer); + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->syncbox)) + mod_handler->mod_cb->syncbox(tcm_hcd); + } + } + + mutex_unlock(&mod_pool.mutex); + + return; +} + +#ifdef WATCHDOG_SW +static void syna_tcm_update_watchdog(struct syna_tcm_hcd *tcm_hcd, bool en) +{ + cancel_delayed_work_sync(&tcm_hcd->watchdog.work); + flush_workqueue(tcm_hcd->watchdog.workqueue); + + if (!tcm_hcd->watchdog.run) { + tcm_hcd->watchdog.count = 0; + return; + } + + if (en) { + queue_delayed_work(tcm_hcd->watchdog.workqueue, + &tcm_hcd->watchdog.work, + msecs_to_jiffies(WATCHDOG_DELAY_MS)); + } else { + tcm_hcd->watchdog.count = 0; + } + + return; +} + +static void syna_tcm_watchdog_work(struct work_struct *work) +{ + int retval; + unsigned char marker; + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct syna_tcm_watchdog *watchdog = + container_of(delayed_work, struct syna_tcm_watchdog, + work); + struct syna_tcm_hcd *tcm_hcd = + container_of(watchdog, struct syna_tcm_hcd, watchdog); + + if (mutex_is_locked(&tcm_hcd->rw_ctrl_mutex)) + goto exit; + + mutex_lock(&tcm_hcd->rw_ctrl_mutex); + + retval = syna_tcm_read(tcm_hcd, + &marker, + 1); + + mutex_unlock(&tcm_hcd->rw_ctrl_mutex); + + if (retval < 0 || marker != MESSAGE_MARKER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read from device\n"); + + tcm_hcd->watchdog.count++; + + if (tcm_hcd->watchdog.count >= WATCHDOG_TRIGGER_COUNT) { + retval = tcm_hcd->reset_n_reinit(tcm_hcd, true, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset and reinit\n"); + } + tcm_hcd->watchdog.count = 0; + } + } + +exit: + queue_delayed_work(tcm_hcd->watchdog.workqueue, + &tcm_hcd->watchdog.work, + msecs_to_jiffies(WATCHDOG_DELAY_MS)); + + return; +} +#endif + +static void syna_tcm_polling_work(struct work_struct *work) +{ + int retval; + struct delayed_work *delayed_work = + container_of(work, struct delayed_work, work); + struct syna_tcm_hcd *tcm_hcd = + container_of(delayed_work, struct syna_tcm_hcd, + polling_work); + + if (!tcm_hcd->do_polling) + return; + + retval = tcm_hcd->read_message(tcm_hcd, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read message\n"); + if (retval == -ENXIO && tcm_hcd->hw_if->bus_io->type == BUS_SPI) + syna_tcm_check_hdl(tcm_hcd, REPORT_HDL_F35); + } + + if (!(tcm_hcd->in_suspend && retval < 0)) { + queue_delayed_work(tcm_hcd->polling_workqueue, + &tcm_hcd->polling_work, + msecs_to_jiffies(POLLING_DELAY_MS)); + } + + return; +} + +static irqreturn_t syna_tcm_isr(int irq, void *data) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = data; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (unlikely(gpio_get_value(bdata->irq_gpio) != bdata->irq_on_state)) + goto exit; + + tcm_hcd->isr_pid = current->pid; + + retval = tcm_hcd->read_message(tcm_hcd, + NULL, + 0); + if (retval < 0) { + if (tcm_hcd->sensor_type == TYPE_F35) + syna_tcm_check_hdl(tcm_hcd, REPORT_HDL_F35); + else + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read message\n"); + } + +exit: + return IRQ_HANDLED; +} + +static int syna_tcm_enable_irq(struct syna_tcm_hcd *tcm_hcd, bool en, bool ns) +{ + int retval; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + static bool irq_freed = true; + + mutex_lock(&tcm_hcd->irq_en_mutex); + + if (en) { + if (tcm_hcd->irq_enabled) { + LOGD(tcm_hcd->pdev->dev.parent, + "Interrupt already enabled\n"); + retval = 0; + goto exit; + } + + if (bdata->irq_gpio < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid IRQ GPIO\n"); + retval = -EINVAL; + goto queue_polling_work; + } + + if (irq_freed) { + retval = request_threaded_irq(tcm_hcd->irq, NULL, + syna_tcm_isr, bdata->irq_flags, + PLATFORM_DRIVER_NAME, tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create interrupt thread\n"); + } + } else { + enable_irq(tcm_hcd->irq); + retval = 0; + } + +queue_polling_work: + if (retval < 0) { +#ifdef FALL_BACK_ON_POLLING + queue_delayed_work(tcm_hcd->polling_workqueue, + &tcm_hcd->polling_work, + msecs_to_jiffies(POLLING_DELAY_MS)); + tcm_hcd->do_polling = true; + retval = 0; +#endif + } + + if (retval < 0) + goto exit; + else + msleep(ENABLE_IRQ_DELAY_MS); + } else { + if (!tcm_hcd->irq_enabled) { + LOGD(tcm_hcd->pdev->dev.parent, + "Interrupt already disabled\n"); + retval = 0; + goto exit; + } + + if (bdata->irq_gpio >= 0) { + if (ns) { + disable_irq_nosync(tcm_hcd->irq); + } else { + disable_irq(tcm_hcd->irq); + free_irq(tcm_hcd->irq, tcm_hcd); + } + irq_freed = !ns; + } + + if (ns) { + cancel_delayed_work(&tcm_hcd->polling_work); + } else { + cancel_delayed_work_sync(&tcm_hcd->polling_work); + flush_workqueue(tcm_hcd->polling_workqueue); + } + + tcm_hcd->do_polling = false; + } + + retval = 0; + +exit: + if (retval == 0) + tcm_hcd->irq_enabled = en; + + mutex_unlock(&tcm_hcd->irq_en_mutex); + + return retval; +} + +static int syna_tcm_set_gpio(struct syna_tcm_hcd *tcm_hcd, int gpio, + bool config, int dir, int state) +{ + int retval; + char label[16]; + + if (config) { + retval = snprintf(label, 16, "tcm_gpio_%d\n", gpio); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set GPIO label\n"); + return retval; + } + + retval = gpio_request(gpio, label); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to request GPIO %d\n", + gpio); + return retval; + } + + if (dir == 0) + retval = gpio_direction_input(gpio); + else + retval = gpio_direction_output(gpio, state); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set GPIO %d direction\n", + gpio); + return retval; + } + } else { + gpio_free(gpio); + } + + return 0; +} + +static int syna_tcm_config_gpio(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (bdata->irq_gpio >= 0) { + retval = syna_tcm_set_gpio(tcm_hcd, bdata->irq_gpio, + true, 0, 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to configure interrupt GPIO\n"); + goto err_set_gpio_irq; + } + } + + if (bdata->power_gpio >= 0) { + retval = syna_tcm_set_gpio(tcm_hcd, bdata->power_gpio, + true, 1, !bdata->power_on_state); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to configure power GPIO\n"); + goto err_set_gpio_power; + } + } + + if (bdata->reset_gpio >= 0) { + retval = syna_tcm_set_gpio(tcm_hcd, bdata->reset_gpio, + true, 1, !bdata->reset_on_state); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to configure reset GPIO\n"); + goto err_set_gpio_reset; + } + } + + if (bdata->power_gpio >= 0) { + gpio_set_value(bdata->power_gpio, bdata->power_on_state); + msleep(bdata->power_delay_ms); + } + + if (bdata->reset_gpio >= 0) { + gpio_set_value(bdata->reset_gpio, bdata->reset_on_state); + msleep(bdata->reset_active_ms); + gpio_set_value(bdata->reset_gpio, !bdata->reset_on_state); + msleep(bdata->reset_delay_ms); + } + + return 0; + +err_set_gpio_reset: + if (bdata->power_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->power_gpio, false, 0, 0); + +err_set_gpio_power: + if (bdata->irq_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->irq_gpio, false, 0, 0); + +err_set_gpio_irq: + return retval; +} + +static int syna_tcm_enable_regulator(struct syna_tcm_hcd *tcm_hcd, bool en) +{ + int retval; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (!en) { + retval = 0; + goto disable_pwr_reg; + } + + if (tcm_hcd->bus_reg) { + retval = regulator_enable(tcm_hcd->bus_reg); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable bus regulator\n"); + goto exit; + } + } + + if (tcm_hcd->pwr_reg) { + retval = regulator_enable(tcm_hcd->pwr_reg); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable power regulator\n"); + goto disable_bus_reg; + } + msleep(bdata->power_delay_ms); + } + + return 0; + +disable_pwr_reg: + if (tcm_hcd->pwr_reg) + regulator_disable(tcm_hcd->pwr_reg); + +disable_bus_reg: + if (tcm_hcd->bus_reg) + regulator_disable(tcm_hcd->bus_reg); + +exit: + return retval; +} + +static int syna_tcm_get_regulator(struct syna_tcm_hcd *tcm_hcd, bool get) +{ + int retval; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (!get) { + retval = 0; + goto regulator_put; + } + + if (bdata->bus_reg_name != NULL && *bdata->bus_reg_name != 0) { + tcm_hcd->bus_reg = regulator_get(tcm_hcd->pdev->dev.parent, + bdata->bus_reg_name); + if (IS_ERR(tcm_hcd->bus_reg)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get bus regulator\n"); + retval = PTR_ERR(tcm_hcd->bus_reg); + goto regulator_put; + } + } + + if (bdata->pwr_reg_name != NULL && *bdata->pwr_reg_name != 0) { + tcm_hcd->pwr_reg = regulator_get(tcm_hcd->pdev->dev.parent, + bdata->pwr_reg_name); + if (IS_ERR(tcm_hcd->pwr_reg)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get power regulator\n"); + retval = PTR_ERR(tcm_hcd->pwr_reg); + goto regulator_put; + } + } + + return 0; + +regulator_put: + if (tcm_hcd->bus_reg) { + regulator_put(tcm_hcd->bus_reg); + tcm_hcd->bus_reg = NULL; + } + + if (tcm_hcd->pwr_reg) { + regulator_put(tcm_hcd->pwr_reg); + tcm_hcd->pwr_reg = NULL; + } + + return retval; +} + +static int syna_tcm_get_app_info(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + unsigned int timeout; + + timeout = APP_STATUS_POLL_TIMEOUT_MS; + + resp_buf = NULL; + resp_buf_size = 0; + +get_app_info: + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_APPLICATION_INFO, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_APPLICATION_INFO)); + goto exit; + } + + retval = secure_memcpy((unsigned char *)&tcm_hcd->app_info, + sizeof(tcm_hcd->app_info), + resp_buf, + resp_buf_size, + MIN(sizeof(tcm_hcd->app_info), resp_length)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy application info\n"); + goto exit; + } + + tcm_hcd->app_status = le2_to_uint(tcm_hcd->app_info.status); + + if (tcm_hcd->app_status == APP_STATUS_BOOTING || + tcm_hcd->app_status == APP_STATUS_UPDATING) { + if (timeout > 0) { + msleep(APP_STATUS_POLL_MS); + timeout -= APP_STATUS_POLL_MS; + goto get_app_info; + } + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_get_boot_info(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + resp_buf = NULL; + resp_buf_size = 0; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_BOOT_INFO, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_BOOT_INFO)); + goto exit; + } + + retval = secure_memcpy((unsigned char *)&tcm_hcd->boot_info, + sizeof(tcm_hcd->boot_info), + resp_buf, + resp_buf_size, + MIN(sizeof(tcm_hcd->boot_info), resp_length)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy boot info\n"); + goto exit; + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_get_romboot_info(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + resp_buf = NULL; + resp_buf_size = 0; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_ROMBOOT_INFO, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_ROMBOOT_INFO)); + goto exit; + } + + retval = secure_memcpy((unsigned char *)&tcm_hcd->romboot_info, + sizeof(tcm_hcd->romboot_info), + resp_buf, + resp_buf_size, + MIN(sizeof(tcm_hcd->romboot_info), resp_length)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy boot info\n"); + goto exit; + } + + LOGD(tcm_hcd->pdev->dev.parent, + "version = %d\n", tcm_hcd->romboot_info.version); + + LOGD(tcm_hcd->pdev->dev.parent, + "status = 0x%02x\n", tcm_hcd->romboot_info.status); + + LOGD(tcm_hcd->pdev->dev.parent, + "version = 0x%02x 0x%02x\n", + tcm_hcd->romboot_info.asic_id[0], + tcm_hcd->romboot_info.asic_id[1]); + + LOGD(tcm_hcd->pdev->dev.parent, + "write_block_size_words = %d\n", + tcm_hcd->romboot_info.write_block_size_words); + + LOGD(tcm_hcd->pdev->dev.parent, + "max_write_payload_size = %d\n", + tcm_hcd->romboot_info.max_write_payload_size[0] | + tcm_hcd->romboot_info.max_write_payload_size[1] << 8); + + LOGD(tcm_hcd->pdev->dev.parent, + "last_reset_reason = 0x%02x\n", + tcm_hcd->romboot_info.last_reset_reason); + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_identify(struct syna_tcm_hcd *tcm_hcd, bool id) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + unsigned int max_write_size; + + resp_buf = NULL; + resp_buf_size = 0; + + mutex_lock(&tcm_hcd->identify_mutex); + + if (!id) + goto get_info; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_IDENTIFY, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_IDENTIFY)); + goto exit; + } + + retval = secure_memcpy((unsigned char *)&tcm_hcd->id_info, + sizeof(tcm_hcd->id_info), + resp_buf, + resp_buf_size, + MIN(sizeof(tcm_hcd->id_info), resp_length)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy identification info\n"); + goto exit; + } + + tcm_hcd->packrat_number = le4_to_uint(tcm_hcd->id_info.build_id); + + max_write_size = le2_to_uint(tcm_hcd->id_info.max_write_size); + tcm_hcd->wr_chunk_size = MIN(max_write_size, WR_CHUNK_SIZE); + if (tcm_hcd->wr_chunk_size == 0) + tcm_hcd->wr_chunk_size = max_write_size; + + LOGN(tcm_hcd->pdev->dev.parent, + "Firmware build id = %d\n", tcm_hcd->packrat_number); + +get_info: + switch (tcm_hcd->id_info.mode) { + case MODE_APPLICATION_FIRMWARE: + case MODE_HOSTDOWNLOAD_FIRMWARE: + + retval = syna_tcm_get_app_info(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get application info\n"); + goto exit; + } + break; + case MODE_BOOTLOADER: + case MODE_TDDI_BOOTLOADER: + + LOGD(tcm_hcd->pdev->dev.parent, + "In bootloader mode\n"); + + retval = syna_tcm_get_boot_info(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get boot info\n"); + goto exit; + } + break; + case MODE_ROMBOOTLOADER: + + LOGD(tcm_hcd->pdev->dev.parent, + "In rombootloader mode\n"); + + retval = syna_tcm_get_romboot_info(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get application info\n"); + goto exit; + } + break; + default: + break; + } + + retval = 0; + +exit: + mutex_unlock(&tcm_hcd->identify_mutex); + + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_run_production_test_firmware(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + bool retry; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + retry = true; + + resp_buf = NULL; + resp_buf_size = 0; + +retry: + retval = tcm_hcd->write_message(tcm_hcd, + CMD_ENTER_PRODUCTION_TEST_MODE, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + MODE_SWITCH_DELAY_MS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_ENTER_PRODUCTION_TEST_MODE)); + goto exit; + } + + if (tcm_hcd->id_info.mode != MODE_PRODUCTIONTEST_FIRMWARE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run production test firmware\n"); + if (retry) { + retry = false; + goto retry; + } + retval = -EINVAL; + goto exit; + } else if (tcm_hcd->app_status != APP_STATUS_OK) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application status = 0x%02x\n", + tcm_hcd->app_status); + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_run_application_firmware(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + bool retry; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + retry = true; + + resp_buf = NULL; + resp_buf_size = 0; + +retry: + retval = tcm_hcd->write_message(tcm_hcd, + CMD_RUN_APPLICATION_FIRMWARE, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + MODE_SWITCH_DELAY_MS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_RUN_APPLICATION_FIRMWARE)); + goto exit; + } + + retval = tcm_hcd->identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + goto exit; + } + + if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware (boot status = 0x%02x)\n", + tcm_hcd->boot_info.status); + if (retry) { + retry = false; + goto retry; + } + retval = -EINVAL; + goto exit; + } else if (tcm_hcd->app_status != APP_STATUS_OK) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application status = 0x%02x\n", + tcm_hcd->app_status); + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_run_bootloader_firmware(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + unsigned char command; + + resp_buf = NULL; + resp_buf_size = 0; + command = (tcm_hcd->id_info.mode == MODE_ROMBOOTLOADER) ? + CMD_ROMBOOT_RUN_BOOTLOADER_FIRMWARE : + CMD_RUN_BOOTLOADER_FIRMWARE; + + retval = tcm_hcd->write_message(tcm_hcd, + command, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + MODE_SWITCH_DELAY_MS); + if (retval < 0) { + if (tcm_hcd->id_info.mode == MODE_ROMBOOTLOADER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_ROMBOOT_RUN_BOOTLOADER_FIRMWARE)); + } else { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_RUN_BOOTLOADER_FIRMWARE)); + } + goto exit; + } + + if (command != CMD_ROMBOOT_RUN_BOOTLOADER_FIRMWARE) { + retval = tcm_hcd->identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + goto exit; + } + + if (IS_FW_MODE(tcm_hcd->id_info.mode)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enter bootloader mode\n"); + retval = -EINVAL; + goto exit; + } + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_switch_mode(struct syna_tcm_hcd *tcm_hcd, + enum firmware_mode mode) +{ + int retval; + + mutex_lock(&tcm_hcd->reset_mutex); + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, false); +#endif + + switch (mode) { + case FW_MODE_BOOTLOADER: + retval = syna_tcm_run_bootloader_firmware(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to switch to bootloader mode\n"); + goto exit; + } + break; + case FW_MODE_APPLICATION: + retval = syna_tcm_run_application_firmware(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to switch to application mode\n"); + goto exit; + } + break; + case FW_MODE_PRODUCTION_TEST: + retval = syna_tcm_run_production_test_firmware(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to switch to production test mode\n"); + goto exit; + } + break; + default: + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid firmware mode\n"); + retval = -EINVAL; + goto exit; + } + + retval = 0; + +exit: +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, true); +#endif + + mutex_unlock(&tcm_hcd->reset_mutex); + + return retval; +} + +static int syna_tcm_get_dynamic_config(struct syna_tcm_hcd *tcm_hcd, + enum dynamic_config_id id, unsigned short *value) +{ + int retval; + unsigned char out_buf; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + resp_buf = NULL; + resp_buf_size = 0; + + out_buf = (unsigned char)id; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_DYNAMIC_CONFIG, + &out_buf, + sizeof(out_buf), + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_DYNAMIC_CONFIG)); + goto exit; + } + + if (resp_length < 2) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid data length\n"); + retval = -EINVAL; + goto exit; + } + + *value = (unsigned short)le2_to_uint(resp_buf); + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_set_dynamic_config(struct syna_tcm_hcd *tcm_hcd, + enum dynamic_config_id id, unsigned short value) +{ + int retval; + unsigned char out_buf[3]; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + resp_buf = NULL; + resp_buf_size = 0; + + out_buf[0] = (unsigned char)id; + out_buf[1] = (unsigned char)value; + out_buf[2] = (unsigned char)(value >> 8); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_SET_DYNAMIC_CONFIG, + out_buf, + sizeof(out_buf), + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_SET_DYNAMIC_CONFIG)); + goto exit; + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_get_data_location(struct syna_tcm_hcd *tcm_hcd, + enum flash_area area, unsigned int *addr, unsigned int *length) +{ + int retval; + unsigned char out_buf; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + switch (area) { + case CUSTOM_LCM: + out_buf = LCM_DATA; + break; + case CUSTOM_OEM: + out_buf = OEM_DATA; + break; + case PPDT: + out_buf = PPDT_DATA; + break; + default: + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid flash area\n"); + return -EINVAL; + } + + resp_buf = NULL; + resp_buf_size = 0; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_DATA_LOCATION, + &out_buf, + sizeof(out_buf), + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_DATA_LOCATION)); + goto exit; + } + + if (resp_length != 4) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid data length\n"); + retval = -EINVAL; + goto exit; + } + + *addr = le2_to_uint(&resp_buf[0]); + *length = le2_to_uint(&resp_buf[2]); + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_sleep(struct syna_tcm_hcd *tcm_hcd, bool en) +{ + int retval; + unsigned char command; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + command = en ? CMD_ENTER_DEEP_SLEEP : CMD_EXIT_DEEP_SLEEP; + + resp_buf = NULL; + resp_buf_size = 0; + + retval = tcm_hcd->write_message(tcm_hcd, + command, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + en ? + STR(CMD_ENTER_DEEP_SLEEP) : + STR(CMD_EXIT_DEEP_SLEEP)); + goto exit; + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_reset(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_RESET, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + bdata->reset_delay_ms); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_RESET)); + } + + return retval; +} + +static int syna_tcm_reset_and_reinit(struct syna_tcm_hcd *tcm_hcd, + bool hw, bool update_wd) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + struct syna_tcm_module_handler *mod_handler; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + resp_buf = NULL; + resp_buf_size = 0; + + mutex_lock(&tcm_hcd->reset_mutex); + +#ifdef WATCHDOG_SW + if (update_wd) + tcm_hcd->update_watchdog(tcm_hcd, false); +#endif + + if (hw) { + if (bdata->reset_gpio < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Hardware reset unavailable\n"); + retval = -EINVAL; + goto exit; + } + gpio_set_value(bdata->reset_gpio, bdata->reset_on_state); + msleep(bdata->reset_active_ms); + gpio_set_value(bdata->reset_gpio, !bdata->reset_on_state); + } else { + retval = syna_tcm_reset(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + goto exit; + } + } + + /* for hdl, the remaining re-init process will be done */ + /* in the helper thread, so wait for the completion here */ + if (tcm_hcd->in_hdl_mode) { + mutex_unlock(&tcm_hcd->reset_mutex); + kfree(resp_buf); + + retval = syna_tcm_wait_hdl(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to wait for completion of host download\n"); + return retval; + } + +#ifdef WATCHDOG_SW + if (update_wd) + tcm_hcd->update_watchdog(tcm_hcd, true); +#endif + return 0; + } + + msleep(bdata->reset_delay_ms); + + retval = tcm_hcd->identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + goto exit; + } + + if (IS_FW_MODE(tcm_hcd->id_info.mode)) + goto get_features; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_RUN_APPLICATION_FIRMWARE, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + MODE_SWITCH_DELAY_MS); + if (retval < 0) { + LOGN(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_RUN_APPLICATION_FIRMWARE)); + } + + retval = tcm_hcd->identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + goto exit; + } + +get_features: + LOGN(tcm_hcd->pdev->dev.parent, + "Firmware mode = 0x%02x\n", + tcm_hcd->id_info.mode); + + if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode)) { + LOGN(tcm_hcd->pdev->dev.parent, + "Boot status = 0x%02x\n", + tcm_hcd->boot_info.status); + } else if (tcm_hcd->app_status != APP_STATUS_OK) { + LOGN(tcm_hcd->pdev->dev.parent, + "Application status = 0x%02x\n", + tcm_hcd->app_status); + } + + if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode)) + goto dispatch_reinit; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_FEATURES, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGN(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_FEATURES)); + } else { + retval = secure_memcpy((unsigned char *)&tcm_hcd->features, + sizeof(tcm_hcd->features), + resp_buf, + resp_buf_size, + MIN(sizeof(tcm_hcd->features), resp_length)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy feature description\n"); + } + } + +dispatch_reinit: + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->reinit)) + mod_handler->mod_cb->reinit(tcm_hcd); + } + } + + mutex_unlock(&mod_pool.mutex); + + retval = 0; + +exit: +#ifdef WATCHDOG_SW + if (update_wd) + tcm_hcd->update_watchdog(tcm_hcd, true); +#endif + + mutex_unlock(&tcm_hcd->reset_mutex); + + kfree(resp_buf); + + return retval; +} + +static int syna_tcm_rezero(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *resp_buf; + unsigned int resp_buf_size; + unsigned int resp_length; + + resp_buf = NULL; + resp_buf_size = 0; + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_REZERO, + NULL, + 0, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_REZERO)); + goto exit; + } + + retval = 0; + +exit: + kfree(resp_buf); + + return retval; +} + +static void syna_tcm_helper_work(struct work_struct *work) +{ + int retval; + unsigned char task; + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_helper *helper = + container_of(work, struct syna_tcm_helper, work); + struct syna_tcm_hcd *tcm_hcd = + container_of(helper, struct syna_tcm_hcd, helper); + + task = atomic_read(&helper->task); + + switch (task) { + + /* this helper can help to run the application firmware */ + case HELP_RUN_APPLICATION_FIRMWARE: + mutex_lock(&tcm_hcd->reset_mutex); + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, false); +#endif + retval = syna_tcm_run_application_firmware(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to switch to application mode\n"); + } +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, true); +#endif + mutex_unlock(&tcm_hcd->reset_mutex); + break; + + /* the reinit helper is used to notify all installed modules to */ + /* do the re-initialization process, since the HDL is completed */ + case HELP_SEND_REINIT_NOTIFICATION: + mutex_lock(&tcm_hcd->reset_mutex); + + /* do identify to ensure application firmware is running */ + retval = tcm_hcd->identify(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application firmware is not running\n"); + mutex_unlock(&tcm_hcd->reset_mutex); + break; + } + + /* init the touch reporting here */ + /* since the HDL is completed */ + retval = touch_reinit(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to initialze touch reporting\n"); + break; + } + + mutex_lock(&mod_pool.mutex); + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->reinit)) + mod_handler->mod_cb->reinit(tcm_hcd); + } + } + mutex_unlock(&mod_pool.mutex); + mutex_unlock(&tcm_hcd->reset_mutex); + wake_up_interruptible(&tcm_hcd->hdl_wq); + break; + + /* this helper is used to reinit the touch reporting */ + case HELP_TOUCH_REINIT: + retval = touch_reinit(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to re-initialze touch reporting\n"); + } + break; + + /* this helper is used to trigger a romboot hdl */ + case HELP_SEND_ROMBOOT_HDL: + syna_tcm_check_hdl(tcm_hcd, REPORT_HDL_ROMBOOT); + break; + + case HELP_SEND_REZERO_COMMAND: + retval = syna_tcm_rezero(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to re-zero for baseline update\n"); + } + break; + + default: + break; + } + + atomic_set(&helper->task, HELP_NONE); + + return; +} + +#if defined(CONFIG_PM) || defined(CONFIG_FB) +int syna_tcm_resume(struct device *dev) +{ + int retval; + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_hcd *tcm_hcd = dev_get_drvdata(dev); + + if (!tcm_hcd->in_suspend) + return 0; + + + if (tcm_hcd->in_hdl_mode) { + if (!tcm_hcd->wakeup_gesture_enabled) { + tcm_hcd->enable_irq(tcm_hcd, true, NULL); + retval = syna_tcm_wait_hdl(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to wait for completion of host download\n"); + goto exit; + } + } + } else { + if (!tcm_hcd->wakeup_gesture_enabled) + tcm_hcd->enable_irq(tcm_hcd, true, NULL); + +#ifdef RESET_ON_RESUME + msleep(RESET_ON_RESUME_DELAY_MS); + goto do_reset; +#endif + } + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, true); +#endif + + if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode) || + tcm_hcd->app_status != APP_STATUS_OK) { + LOGN(tcm_hcd->pdev->dev.parent, + "Identifying mode = 0x%02x\n", + tcm_hcd->id_info.mode); + goto do_reset; + } + + retval = tcm_hcd->sleep(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to exit deep sleep\n"); + goto exit; + } + + /* When delay_baseline_update is true, the baseline is not updated, + * so syna_tcm_rezero function is not run. + */ + if (!tcm_hcd->delay_baseline_update) { + LOGD(tcm_hcd->pdev->dev.parent, + "Enter into rezero function to update baseline \n"); + retval = syna_tcm_rezero(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to rezero\n"); + goto exit; + } + } + + goto mod_resume; + +do_reset: + retval = tcm_hcd->reset_n_reinit(tcm_hcd, false, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset and reinit\n"); + goto exit; + } + + if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode) || + tcm_hcd->app_status != APP_STATUS_OK) { + LOGN(tcm_hcd->pdev->dev.parent, + "Identifying mode = 0x%02x\n", + tcm_hcd->id_info.mode); + retval = 0; + goto exit; + } + +mod_resume: + touch_resume(tcm_hcd); + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->resume)) + mod_handler->mod_cb->resume(tcm_hcd); + } + } + + mutex_unlock(&mod_pool.mutex); + + retval = 0; + +exit: + tcm_hcd->in_suspend = false; + + return retval; +} + +int syna_tcm_suspend(struct device *dev) +{ + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_hcd *tcm_hcd = dev_get_drvdata(dev); + + if (tcm_hcd->in_suspend) + return 0; + + + touch_suspend(tcm_hcd); + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->suspend)) + mod_handler->mod_cb->suspend(tcm_hcd); + } + } + + mutex_unlock(&mod_pool.mutex); + + if (!tcm_hcd->wakeup_gesture_enabled) + tcm_hcd->enable_irq(tcm_hcd, false, true); + + tcm_hcd->in_suspend = true; + touch_free_objects(); + + return 0; +} +#endif + +#if defined (CONFIG_FB) || defined (CONFIG_INPUT_TOUCHSCREEN_MMI) +int syna_tcm_early_suspend(struct device *dev) +{ + int retval; + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_hcd *tcm_hcd = dev_get_drvdata(dev); + + if (tcm_hcd->in_suspend) + return 0; + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, false); +#endif + + if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode) || + tcm_hcd->app_status != APP_STATUS_OK) { + LOGN(tcm_hcd->pdev->dev.parent, + "Identifying mode = 0x%02x\n", + tcm_hcd->id_info.mode); + return 0; + } + + if (!tcm_hcd->wakeup_gesture_enabled) { + retval = tcm_hcd->sleep(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enter deep sleep\n"); + return retval; + } + } + + touch_early_suspend(tcm_hcd); + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry(mod_handler, &mod_pool.list, link) { + if (!mod_handler->insert && + !mod_handler->detach && + (mod_handler->mod_cb->early_suspend)) + mod_handler->mod_cb->early_suspend(tcm_hcd); + } + } + + mutex_unlock(&mod_pool.mutex); + + return 0; +} +#endif + +#if defined (CONFIG_FB) || !defined (CONFIG_INPUT_TOUCHSCREEN_MMI) +static int syna_tcm_fb_notifier_cb(struct notifier_block *nb, + unsigned long action, void *data) +{ + int retval; + int *transition; + struct fb_event *evdata = data; + struct syna_tcm_hcd *tcm_hcd = + container_of(nb, struct syna_tcm_hcd, fb_notifier); + + retval = 0; + + if (evdata && evdata->data && tcm_hcd) { + transition = evdata->data; + + if (atomic_read(&tcm_hcd->firmware_flashing) && + *transition == FB_BLANK_POWERDOWN) { + + retval = wait_event_interruptible_timeout(tcm_hcd->reflash_wq, + !atomic_read(&tcm_hcd->firmware_flashing), + msecs_to_jiffies(RESPONSE_TIMEOUT_MS)); + if (retval == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Timed out waiting for completion of flashing firmware\n"); + atomic_set(&tcm_hcd->firmware_flashing, 0); + return -EIO; + } else { + retval = 0; + } + } + + if (action == FB_EARLY_EVENT_BLANK && + *transition == FB_BLANK_POWERDOWN) + retval = syna_tcm_early_suspend(&tcm_hcd->pdev->dev); + else if (action == FB_EVENT_BLANK) { + if (*transition == FB_BLANK_POWERDOWN) { + retval = syna_tcm_suspend(&tcm_hcd->pdev->dev); + tcm_hcd->fb_ready = 0; + } else if (*transition == FB_BLANK_UNBLANK) { +#ifndef RESUME_EARLY_UNBLANK + retval = syna_tcm_resume(&tcm_hcd->pdev->dev); + tcm_hcd->fb_ready++; +#endif + } + } else if (action == FB_EARLY_EVENT_BLANK && + *transition == FB_BLANK_UNBLANK) { +#ifdef RESUME_EARLY_UNBLANK + retval = syna_tcm_resume(&tcm_hcd->pdev->dev); + tcm_hcd->fb_ready++; +#endif + } + } + + return 0; +} +#endif + +static int syna_tcm_check_f35(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char fn_number; + int retry = 0; + const int retry_max = 10; + +f35_boot_recheck: + retval = syna_tcm_rmi_read(tcm_hcd, + PDT_END_ADDR, + &fn_number, + sizeof(fn_number)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read F35 function number\n"); + tcm_hcd->is_detected = false; + return -ENODEV; + } + + LOGD(tcm_hcd->pdev->dev.parent, + "Found F$%02x\n", + fn_number); + + if (fn_number != RMI_UBL_FN_NUMBER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to find F$35\n"); + if (retry < retry_max) { + msleep(100); + retry++; + goto f35_boot_recheck; + } + tcm_hcd->is_detected = false; + return -ENODEV; + } + return 0; +} + +static int syna_tcm_sensor_detection(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *build_id; + unsigned int payload_length; + unsigned int max_write_size; + + tcm_hcd->in_hdl_mode = false; + tcm_hcd->sensor_type = TYPE_UNKNOWN; + + /* read sensor info for identification */ + retval = tcm_hcd->read_message(tcm_hcd, + NULL, + 0); + + /* once the tcm communication interface is not ready, */ + /* check whether the device is in F35 mode */ + if (retval < 0) { + if (retval == -ENXIO && + tcm_hcd->hw_if->bus_io->type == BUS_SPI) { + + retval = syna_tcm_check_f35(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read TCM message\n"); + return retval; + } + tcm_hcd->in_hdl_mode = true; + tcm_hcd->sensor_type = TYPE_F35; + tcm_hcd->is_detected = true; + LOGN(tcm_hcd->pdev->dev.parent, + "F35 mode\n"); + + return retval; + } else { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read TCM message\n"); + + return retval; + } + } + + /* expect to get an identify report after powering on */ + + if (tcm_hcd->status_report_code != REPORT_IDENTIFY) { + LOGE(tcm_hcd->pdev->dev.parent, + "Unexpected report code (0x%02x)\n", + tcm_hcd->status_report_code); + + return -ENODEV; + } + + tcm_hcd->is_detected = true; + payload_length = tcm_hcd->payload_length; + + LOCK_BUFFER(tcm_hcd->in); + + retval = secure_memcpy((unsigned char *)&tcm_hcd->id_info, + sizeof(tcm_hcd->id_info), + &tcm_hcd->in.buf[MESSAGE_HEADER_SIZE], + tcm_hcd->in.buf_size - MESSAGE_HEADER_SIZE, + MIN(sizeof(tcm_hcd->id_info), payload_length)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy identification info\n"); + UNLOCK_BUFFER(tcm_hcd->in); + return retval; + } + + UNLOCK_BUFFER(tcm_hcd->in); + + build_id = tcm_hcd->id_info.build_id; + tcm_hcd->packrat_number = le4_to_uint(build_id); + + max_write_size = le2_to_uint(tcm_hcd->id_info.max_write_size); + tcm_hcd->wr_chunk_size = MIN(max_write_size, WR_CHUNK_SIZE); + if (tcm_hcd->wr_chunk_size == 0) + tcm_hcd->wr_chunk_size = max_write_size; + + if (tcm_hcd->id_info.mode == MODE_ROMBOOTLOADER) { + tcm_hcd->in_hdl_mode = true; + tcm_hcd->sensor_type = TYPE_ROMBOOT; + LOGN(tcm_hcd->pdev->dev.parent, + "RomBoot mode\n"); + } else if (tcm_hcd->id_info.mode == MODE_APPLICATION_FIRMWARE) { + tcm_hcd->sensor_type = TYPE_FLASH; + LOGN(tcm_hcd->pdev->dev.parent, + "Application mode (build id = %d)\n", + tcm_hcd->packrat_number); + } else { + LOGW(tcm_hcd->pdev->dev.parent, + "TCM is detected, but mode is 0x%02x\n", + tcm_hcd->id_info.mode); + } + + return 0; +} + +static int syna_tcm_probe(struct platform_device *pdev) +{ + int retval; + int idx; + struct syna_tcm_hcd *tcm_hcd; + const struct syna_tcm_board_data *bdata; + const struct syna_tcm_hw_interface *hw_if; + + hw_if = pdev->dev.platform_data; + if (!hw_if) { + LOGE(&pdev->dev, + "Hardware interface not found\n"); + return -ENODEV; + } + + bdata = hw_if->bdata; + if (!bdata) { + LOGE(&pdev->dev, + "Board data not found\n"); + return -ENODEV; + } + + tcm_hcd = kzalloc(sizeof(*tcm_hcd), GFP_KERNEL); + if (!tcm_hcd) { + LOGE(&pdev->dev, + "Failed to allocate memory for tcm_hcd\n"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, tcm_hcd); + + tcm_hcd->pdev = pdev; + tcm_hcd->hw_if = hw_if; + tcm_hcd->reset = syna_tcm_reset; + tcm_hcd->reset_n_reinit = syna_tcm_reset_and_reinit; + tcm_hcd->sleep = syna_tcm_sleep; + tcm_hcd->identify = syna_tcm_identify; + tcm_hcd->enable_irq = syna_tcm_enable_irq; + tcm_hcd->switch_mode = syna_tcm_switch_mode; + tcm_hcd->read_message = syna_tcm_read_message; + tcm_hcd->write_message = syna_tcm_write_message; + tcm_hcd->get_dynamic_config = syna_tcm_get_dynamic_config; + tcm_hcd->set_dynamic_config = syna_tcm_set_dynamic_config; + tcm_hcd->get_data_location = syna_tcm_get_data_location; + + tcm_hcd->rd_chunk_size = RD_CHUNK_SIZE; + tcm_hcd->wr_chunk_size = WR_CHUNK_SIZE; + tcm_hcd->is_detected = false; + tcm_hcd->wakeup_gesture_enabled = WAKEUP_GESTURE; + +#ifdef PREDICTIVE_READING + tcm_hcd->read_length = MIN_READ_LENGTH; +#else + tcm_hcd->read_length = MESSAGE_HEADER_SIZE; +#endif + +#ifdef WATCHDOG_SW + tcm_hcd->watchdog.run = RUN_WATCHDOG; + tcm_hcd->update_watchdog = syna_tcm_update_watchdog; +#endif + + if (bdata->irq_gpio >= 0) + tcm_hcd->irq = gpio_to_irq(bdata->irq_gpio); + else + tcm_hcd->irq = bdata->irq_gpio; + + mutex_init(&tcm_hcd->extif_mutex); + mutex_init(&tcm_hcd->reset_mutex); + mutex_init(&tcm_hcd->irq_en_mutex); + mutex_init(&tcm_hcd->io_ctrl_mutex); + mutex_init(&tcm_hcd->rw_ctrl_mutex); + mutex_init(&tcm_hcd->command_mutex); + mutex_init(&tcm_hcd->identify_mutex); + + INIT_BUFFER(tcm_hcd->in, false); + INIT_BUFFER(tcm_hcd->out, false); + INIT_BUFFER(tcm_hcd->resp, true); + INIT_BUFFER(tcm_hcd->temp, false); + INIT_BUFFER(tcm_hcd->config, false); + INIT_BUFFER(tcm_hcd->report.buffer, true); + + LOCK_BUFFER(tcm_hcd->in); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->in, + tcm_hcd->read_length + 1); + if (retval < 0) { + LOGE(&pdev->dev, + "Failed to allocate memory for tcm_hcd->in.buf\n"); + UNLOCK_BUFFER(tcm_hcd->in); + goto err_alloc_mem; + } + + UNLOCK_BUFFER(tcm_hcd->in); + + atomic_set(&tcm_hcd->command_status, CMD_IDLE); + + atomic_set(&tcm_hcd->helper.task, HELP_NONE); + + device_init_wakeup(&pdev->dev, 1); + + init_waitqueue_head(&tcm_hcd->hdl_wq); + + init_waitqueue_head(&tcm_hcd->reflash_wq); + atomic_set(&tcm_hcd->firmware_flashing, 0); + + if (!mod_pool.initialized) { + mutex_init(&mod_pool.mutex); + INIT_LIST_HEAD(&mod_pool.list); + mod_pool.initialized = true; + } + + retval = syna_tcm_get_regulator(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get regulators\n"); + goto err_get_regulator; + } + + retval = syna_tcm_enable_regulator(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable regulators\n"); + goto err_enable_regulator; + } + + retval = syna_tcm_config_gpio(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to configure GPIO's\n"); + goto err_config_gpio; + } + + /* detect the type of touch controller */ + retval = syna_tcm_sensor_detection(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to detect the sensor\n"); + goto err_sysfs_create_dir; + } + + sysfs_dir = kobject_create_and_add(PLATFORM_DRIVER_NAME, + &pdev->dev.kobj); + if (!sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs directory\n"); + retval = -EINVAL; + goto err_sysfs_create_dir; + } + + tcm_hcd->sysfs_dir = sysfs_dir; + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + retval = sysfs_create_file(tcm_hcd->sysfs_dir, + &(*attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs file\n"); + goto err_sysfs_create_file; + } + } + + tcm_hcd->dynamnic_config_sysfs_dir = + kobject_create_and_add(DYNAMIC_CONFIG_SYSFS_DIR_NAME, + tcm_hcd->sysfs_dir); + if (!tcm_hcd->dynamnic_config_sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create dynamic config sysfs directory\n"); + retval = -EINVAL; + goto err_sysfs_create_dynamic_config_dir; + } + + for (idx = 0; idx < ARRAY_SIZE(dynamic_config_attrs); idx++) { + retval = sysfs_create_file(tcm_hcd->dynamnic_config_sysfs_dir, + &(*dynamic_config_attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create dynamic config sysfs file\n"); + goto err_sysfs_create_dynamic_config_file; + } + } + +#ifdef CONFIG_FB + tcm_hcd->fb_notifier.notifier_call = syna_tcm_fb_notifier_cb; + retval = fb_register_client(&tcm_hcd->fb_notifier); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to register FB notifier client\n"); + } +#endif + + +#ifdef REPORT_NOTIFIER + tcm_hcd->notifier_thread = kthread_run(syna_tcm_report_notifier, + tcm_hcd, "syna_tcm_report_notifier"); + if (IS_ERR(tcm_hcd->notifier_thread)) { + retval = PTR_ERR(tcm_hcd->notifier_thread); + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create and run tcm_hcd->notifier_thread\n"); + goto err_create_run_kthread; + } +#endif + + tcm_hcd->helper.workqueue = + create_singlethread_workqueue("syna_tcm_helper"); + INIT_WORK(&tcm_hcd->helper.work, syna_tcm_helper_work); + +#ifdef WATCHDOG_SW + tcm_hcd->watchdog.workqueue = + create_singlethread_workqueue("syna_tcm_watchdog"); + INIT_DELAYED_WORK(&tcm_hcd->watchdog.work, syna_tcm_watchdog_work); +#endif + + tcm_hcd->polling_workqueue = + create_singlethread_workqueue("syna_tcm_polling"); + INIT_DELAYED_WORK(&tcm_hcd->polling_work, syna_tcm_polling_work); + + + /* skip the following initialization */ + /* since the fw is not ready for hdl devices */ + if (tcm_hcd->in_hdl_mode) + goto prepare_modules; + + + /* register and enable the interrupt in probe */ + /* if this is not the hdl device */ + retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable interrupt\n"); + goto err_enable_irq; + } + LOGD(tcm_hcd->pdev->dev.parent, + "Interrupt is registered\n"); + + /* ensure the app firmware is running */ + retval = syna_tcm_identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application firmware is not running\n"); + goto err_enable_irq; + } + /* initialize the touch reporting */ + retval = touch_init(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to initialze touch reporting\n"); + goto err_enable_irq; + } + +prepare_modules: + /* prepare to add other modules */ + mod_pool.workqueue = + create_singlethread_workqueue("syna_tcm_module"); + INIT_WORK(&mod_pool.work, syna_tcm_module_work); + mod_pool.tcm_hcd = tcm_hcd; + mod_pool.queue_work = true; + queue_work(mod_pool.workqueue, &mod_pool.work); + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + syna_ts_mmi_dev_register(tcm_hcd); +#endif + + return 0; + +err_enable_irq: + cancel_delayed_work_sync(&tcm_hcd->polling_work); + flush_workqueue(tcm_hcd->polling_workqueue); + destroy_workqueue(tcm_hcd->polling_workqueue); + +#ifdef WATCHDOG_SW + cancel_delayed_work_sync(&tcm_hcd->watchdog.work); + flush_workqueue(tcm_hcd->watchdog.workqueue); + destroy_workqueue(tcm_hcd->watchdog.workqueue); +#endif + + cancel_work_sync(&tcm_hcd->helper.work); + flush_workqueue(tcm_hcd->helper.workqueue); + destroy_workqueue(tcm_hcd->helper.workqueue); + +#ifdef REPORT_NOTIFIER + kthread_stop(tcm_hcd->notifier_thread); + +err_create_run_kthread: +#endif +#ifdef CONFIG_FB + fb_unregister_client(&tcm_hcd->fb_notifier); +#endif + +err_sysfs_create_dynamic_config_file: + for (idx--; idx >= 0; idx--) { + sysfs_remove_file(tcm_hcd->dynamnic_config_sysfs_dir, + &(*dynamic_config_attrs[idx]).attr); + } + + kobject_put(tcm_hcd->dynamnic_config_sysfs_dir); + + idx = ARRAY_SIZE(attrs); + +err_sysfs_create_dynamic_config_dir: +err_sysfs_create_file: + for (idx--; idx >= 0; idx--) + sysfs_remove_file(tcm_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(tcm_hcd->sysfs_dir); + +err_sysfs_create_dir: + if (bdata->irq_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->irq_gpio, false, 0, 0); + + if (bdata->power_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->power_gpio, false, 0, 0); + + if (bdata->reset_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->reset_gpio, false, 0, 0); + +err_config_gpio: + syna_tcm_enable_regulator(tcm_hcd, false); + +err_enable_regulator: + syna_tcm_get_regulator(tcm_hcd, false); + +err_get_regulator: + device_init_wakeup(&pdev->dev, 0); + +err_alloc_mem: + RELEASE_BUFFER(tcm_hcd->report.buffer); + RELEASE_BUFFER(tcm_hcd->config); + RELEASE_BUFFER(tcm_hcd->temp); + RELEASE_BUFFER(tcm_hcd->resp); + RELEASE_BUFFER(tcm_hcd->out); + RELEASE_BUFFER(tcm_hcd->in); + + kfree(tcm_hcd); + + return retval; +} + +static int syna_tcm_remove(struct platform_device *pdev) +{ + int idx; + struct syna_tcm_module_handler *mod_handler; + struct syna_tcm_module_handler *tmp_handler; + struct syna_tcm_hcd *tcm_hcd = platform_get_drvdata(pdev); + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + syna_ts_mmi_dev_unregister(tcm_hcd); +#endif + touch_remove(tcm_hcd); + + mutex_lock(&mod_pool.mutex); + + if (!list_empty(&mod_pool.list)) { + list_for_each_entry_safe(mod_handler, + tmp_handler, + &mod_pool.list, + link) { + if (mod_handler->mod_cb->remove) + mod_handler->mod_cb->remove(tcm_hcd); + list_del(&mod_handler->link); + kfree(mod_handler); + } + } + + mod_pool.queue_work = false; + cancel_work_sync(&mod_pool.work); + flush_workqueue(mod_pool.workqueue); + destroy_workqueue(mod_pool.workqueue); + + mutex_unlock(&mod_pool.mutex); + + if (tcm_hcd->irq_enabled && bdata->irq_gpio >= 0) { + disable_irq(tcm_hcd->irq); + free_irq(tcm_hcd->irq, tcm_hcd); + } + + cancel_delayed_work_sync(&tcm_hcd->polling_work); + flush_workqueue(tcm_hcd->polling_workqueue); + destroy_workqueue(tcm_hcd->polling_workqueue); + +#ifdef WATCHDOG_SW + cancel_delayed_work_sync(&tcm_hcd->watchdog.work); + flush_workqueue(tcm_hcd->watchdog.workqueue); + destroy_workqueue(tcm_hcd->watchdog.workqueue); +#endif + + cancel_work_sync(&tcm_hcd->helper.work); + flush_workqueue(tcm_hcd->helper.workqueue); + destroy_workqueue(tcm_hcd->helper.workqueue); + +#ifdef REPORT_NOTIFIER + kthread_stop(tcm_hcd->notifier_thread); +#endif + +#ifdef CONFIG_FB + fb_unregister_client(&tcm_hcd->fb_notifier); +#endif + + for (idx = 0; idx < ARRAY_SIZE(dynamic_config_attrs); idx++) { + sysfs_remove_file(tcm_hcd->dynamnic_config_sysfs_dir, + &(*dynamic_config_attrs[idx]).attr); + } + + kobject_put(tcm_hcd->dynamnic_config_sysfs_dir); + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) + sysfs_remove_file(tcm_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(tcm_hcd->sysfs_dir); + + if (bdata->irq_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->irq_gpio, false, 0, 0); + + if (bdata->power_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->power_gpio, false, 0, 0); + + if (bdata->reset_gpio >= 0) + syna_tcm_set_gpio(tcm_hcd, bdata->reset_gpio, false, 0, 0); + + syna_tcm_enable_regulator(tcm_hcd, false); + + syna_tcm_get_regulator(tcm_hcd, false); + + device_init_wakeup(&pdev->dev, 0); + + RELEASE_BUFFER(tcm_hcd->report.buffer); + RELEASE_BUFFER(tcm_hcd->config); + RELEASE_BUFFER(tcm_hcd->temp); + RELEASE_BUFFER(tcm_hcd->resp); + RELEASE_BUFFER(tcm_hcd->out); + RELEASE_BUFFER(tcm_hcd->in); + + kfree(tcm_hcd); + + return 0; +} + +static void syna_tcm_shutdown(struct platform_device *pdev) +{ + int retval; + + retval = syna_tcm_remove(pdev); +} + +#ifdef CONFIG_PM +static const struct dev_pm_ops syna_tcm_dev_pm_ops = { +#ifndef CONFIG_FB + .suspend = syna_tcm_suspend, + .resume = syna_tcm_resume, +#endif +}; +#endif + +static struct platform_driver syna_tcm_driver = { + .driver = { + .name = PLATFORM_DRIVER_NAME, + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &syna_tcm_dev_pm_ops, +#endif + }, + .probe = syna_tcm_probe, + .remove = syna_tcm_remove, + .shutdown = syna_tcm_shutdown, +}; + +static int __init syna_tcm_module_init(void) +{ + int retval; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + if (!ts_mmi_is_panel_match("mmi,panel_name", "syna")) { + pr_info("%s failed, the panel is not match the touch driver", __func__); + return false; + } +#endif + + retval = syna_tcm_bus_init(); + if (retval < 0) + return retval; + + return platform_driver_register(&syna_tcm_driver); +} + +static void __exit syna_tcm_module_exit(void) +{ + platform_driver_unregister(&syna_tcm_driver); + + syna_tcm_bus_exit(); + + return; +} + +module_init(syna_tcm_module_init); +module_exit(syna_tcm_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Touch Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/synaptics_core.h b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/synaptics_core.h new file mode 100644 index 000000000000..6a0bec360ace --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/synaptics_core.h @@ -0,0 +1,759 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * Copyright (C) 2018-2019 Ian Su + * Copyright (C) 2018-2019 Joey Zhou + * Copyright (C) 2018-2019 Yuehao Qiu + * Copyright (C) 2018-2019 Aaron Chen + * + * 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. + */ + +#ifndef _SYNAPTICS_TCM_CORE_H_ +#define _SYNAPTICS_TCM_CORE_H_ + +#include +#include +#include +#include +#include +#include +#include "synaptics_tcm.h" +#include +#include +#ifdef CONFIG_FB +#include +#include +#endif + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +#include +#endif + +#define SYNAPTICS_TCM_ID_PRODUCT (1 << 0) +#define SYNAPTICS_TCM_ID_VERSION 0x0200 +#define SYNAPTICS_TCM_ID_SUBVERSION 0 + +#define PLATFORM_DRIVER_NAME "synaptics_tcm" + +#define TOUCH_INPUT_NAME "synaptics_tcm_touch" +#define TOUCH_INPUT_PHYS_PATH "synaptics_tcm/touch_input" + +#define WAKEUP_GESTURE (1) + +#define RD_CHUNK_SIZE 256 /* read length limit in bytes, 0 = unlimited */ +#define WR_CHUNK_SIZE 256 /* write length limit in bytes, 0 = unlimited */ + +#define MESSAGE_HEADER_SIZE 4 +#define MESSAGE_MARKER 0xa5 +#define MESSAGE_PADDING 0x5a + +#define RESET_ON_RESUME_DELAY_MS 50 + +/* +#define REPORT_NOTIFIER +*/ + +/* +#define WATCHDOG_SW +*/ +#ifdef WATCHDOG_SW +#define RUN_WATCHDOG false +#define WATCHDOG_TRIGGER_COUNT 2 +#define WATCHDOG_DELAY_MS 1000 +#endif + +#define LOGx(func, dev, log, ...) \ + func(dev, "%s info: " log, __func__, ##__VA_ARGS__) + +#define LOGy(func, dev, log, ...) \ + func(dev, "%s error: (line %d) " log, __func__, __LINE__, ##__VA_ARGS__) + +#define LOGD(dev, log, ...) LOGx(dev_dbg, dev, log, ##__VA_ARGS__) +#define LOGI(dev, log, ...) LOGx(dev_info, dev, log, ##__VA_ARGS__) +#define LOGN(dev, log, ...) LOGx(dev_notice, dev, log, ##__VA_ARGS__) +#define LOGW(dev, log, ...) LOGy(dev_warn, dev, log, ##__VA_ARGS__) +#define LOGE(dev, log, ...) LOGy(dev_err, dev, log, ##__VA_ARGS__) + +#define INIT_BUFFER(buffer, is_clone) \ + mutex_init(&buffer.buf_mutex); \ + buffer.clone = is_clone; \ + buffer.buf_size = 0 + +#define LOCK_BUFFER(buffer) \ + mutex_lock(&buffer.buf_mutex) + +#define UNLOCK_BUFFER(buffer) \ + mutex_unlock(&buffer.buf_mutex) + +#define RELEASE_BUFFER(buffer) \ + do { \ + if (buffer.clone == false && (buffer.buf_size != 0)) { \ + kfree(buffer.buf); \ + buffer.buf_size = 0; \ + buffer.data_length = 0; \ + } \ + } while (0) + +#define MAX(a, b) \ + ({__typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a > _b ? _a : _b; }) + +#define MIN(a, b) \ + ({__typeof__(a) _a = (a); \ + __typeof__(b) _b = (b); \ + _a < _b ? _a : _b; }) + +#define STR(x) #x + +#define CONCAT(a, b) a##b + +#define IS_NOT_FW_MODE(mode) \ + ((mode != MODE_APPLICATION_FIRMWARE) && (mode != MODE_HOSTDOWNLOAD_FIRMWARE)) + +#define IS_FW_MODE(mode) \ + ((mode == MODE_APPLICATION_FIRMWARE) || (mode == MODE_HOSTDOWNLOAD_FIRMWARE)) + +#define SHOW_PROTOTYPE(m_name, a_name) \ +static ssize_t CONCAT(m_name##_sysfs, _##a_name##_show)(struct device *dev, \ + struct device_attribute *attr, char *buf); \ +\ +static struct device_attribute dev_attr_##a_name = \ + __ATTR(a_name, S_IRUGO, \ + CONCAT(m_name##_sysfs, _##a_name##_show), \ + syna_tcm_store_error); + +#define STORE_PROTOTYPE(m_name, a_name) \ +static ssize_t CONCAT(m_name##_sysfs, _##a_name##_store)(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count); \ +\ +static struct device_attribute dev_attr_##a_name = \ + __ATTR(a_name, (S_IWUSR | S_IWGRP), \ + syna_tcm_show_error, \ + CONCAT(m_name##_sysfs, _##a_name##_store)); + +#define SHOW_STORE_PROTOTYPE(m_name, a_name) \ +static ssize_t CONCAT(m_name##_sysfs, _##a_name##_show)(struct device *dev, \ + struct device_attribute *attr, char *buf); \ +\ +static ssize_t CONCAT(m_name##_sysfs, _##a_name##_store)(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t count); \ +\ +static struct device_attribute dev_attr_##a_name = \ + __ATTR(a_name, (S_IRUGO | S_IWUSR | S_IWGRP), \ + CONCAT(m_name##_sysfs, _##a_name##_show), \ + CONCAT(m_name##_sysfs, _##a_name##_store)); + +#define ATTRIFY(a_name) (&dev_attr_##a_name) + +enum module_type { + TCM_ZEROFLASH = 0, + TCM_REFLASH = 1, + TCM_DEVICE = 2, + TCM_TESTING = 3, + TCM_RECOVERY = 4, + TCM_DIAGNOSTICS = 5, + TCM_LAST, +}; + +enum boot_mode { + MODE_APPLICATION_FIRMWARE = 0x01, + MODE_HOSTDOWNLOAD_FIRMWARE = 0x02, + MODE_ROMBOOTLOADER = 0x04, + MODE_BOOTLOADER = 0x0b, + MODE_TDDI_BOOTLOADER = 0x0c, + MODE_TDDI_HOSTDOWNLOAD_BOOTLOADER = 0x0d, + MODE_PRODUCTIONTEST_FIRMWARE = 0x0e, +}; + +enum sensor_types { + TYPE_UNKNOWN = 0, + TYPE_FLASH = 1, + TYPE_F35 = 2, + TYPE_ROMBOOT = 3, +}; + +enum boot_status { + BOOT_STATUS_OK = 0x00, + BOOT_STATUS_BOOTING = 0x01, + BOOT_STATUS_APP_BAD_DISPLAY_CRC = 0xfc, + BOOT_STATUS_BAD_DISPLAY_CONFIG = 0xfd, + BOOT_STATUS_BAD_APP_FIRMWARE = 0xfe, + BOOT_STATUS_WARM_BOOT = 0xff, +}; + +enum app_status { + APP_STATUS_OK = 0x00, + APP_STATUS_BOOTING = 0x01, + APP_STATUS_UPDATING = 0x02, + APP_STATUS_BAD_APP_CONFIG = 0xff, +}; + +enum firmware_mode { + FW_MODE_BOOTLOADER = 0, + FW_MODE_APPLICATION = 1, + FW_MODE_PRODUCTION_TEST = 2, +}; + +enum dynamic_config_id { + DC_UNKNOWN = 0x00, + DC_NO_DOZE, + DC_DISABLE_NOISE_MITIGATION, + DC_INHIBIT_FREQUENCY_SHIFT, + DC_REQUESTED_FREQUENCY, + DC_DISABLE_HSYNC, + DC_REZERO_ON_EXIT_DEEP_SLEEP, + DC_CHARGER_CONNECTED, + DC_NO_BASELINE_RELAXATION, + DC_IN_WAKEUP_GESTURE_MODE, + DC_STIMULUS_FINGERS, + DC_GRIP_SUPPRESSION_ENABLED, + DC_ENABLE_THICK_GLOVE, + DC_ENABLE_GLOVE, + //vendor config id + DC_SUPP_X_WIDTH = 0xd6, + DC_SUPP_Y_START_L = 0xd7, + DC_SUPP_Y_END_L = 0xd8, + DC_SUPP_Y_START_R = 0xd9, + DC_SUPP_Y_END_R = 0xda, + DC_GRIP_SUPPRESSION_INFO = 0xdb, + DC_HOLD_DISTANCE = 0xdc, +}; + +enum command { + CMD_NONE = 0x00, + CMD_CONTINUE_WRITE = 0x01, + CMD_IDENTIFY = 0x02, + CMD_RESET = 0x04, + CMD_ENABLE_REPORT = 0x05, + CMD_DISABLE_REPORT = 0x06, + CMD_GET_BOOT_INFO = 0x10, + CMD_ERASE_FLASH = 0x11, + CMD_WRITE_FLASH = 0x12, + CMD_READ_FLASH = 0x13, + CMD_RUN_APPLICATION_FIRMWARE = 0x14, + CMD_SPI_MASTER_WRITE_THEN_READ = 0x15, + CMD_REBOOT_TO_ROM_BOOTLOADER = 0x16, + CMD_RUN_BOOTLOADER_FIRMWARE = 0x1f, + CMD_GET_APPLICATION_INFO = 0x20, + CMD_GET_STATIC_CONFIG = 0x21, + CMD_SET_STATIC_CONFIG = 0x22, + CMD_GET_DYNAMIC_CONFIG = 0x23, + CMD_SET_DYNAMIC_CONFIG = 0x24, + CMD_GET_TOUCH_REPORT_CONFIG = 0x25, + CMD_SET_TOUCH_REPORT_CONFIG = 0x26, + CMD_REZERO = 0x27, + CMD_COMMIT_CONFIG = 0x28, + CMD_DESCRIBE_DYNAMIC_CONFIG = 0x29, + CMD_PRODUCTION_TEST = 0x2a, + CMD_SET_CONFIG_ID = 0x2b, + CMD_ENTER_DEEP_SLEEP = 0x2c, + CMD_EXIT_DEEP_SLEEP = 0x2d, + CMD_GET_TOUCH_INFO = 0x2e, + CMD_GET_DATA_LOCATION = 0x2f, + CMD_DOWNLOAD_CONFIG = 0x30, + CMD_ENTER_PRODUCTION_TEST_MODE = 0x31, + CMD_GET_FEATURES = 0x32, + CMD_GET_ROMBOOT_INFO = 0x40, + CMD_WRITE_PROGRAM_RAM = 0x41, + CMD_ROMBOOT_RUN_BOOTLOADER_FIRMWARE = 0x42, + CMD_SPI_MASTER_WRITE_THEN_READ_EXTENDED = 0x43, + CMD_ENTER_IO_BRIDGE_MODE = 0x44, + CMD_ROMBOOT_DOWNLOAD = 0x45, +}; + +enum status_code { + STATUS_IDLE = 0x00, + STATUS_OK = 0x01, + STATUS_BUSY = 0x02, + STATUS_CONTINUED_READ = 0x03, + STATUS_NOT_EXECUTED_IN_DEEP_SLEEP = 0x0b, + STATUS_RECEIVE_BUFFER_OVERFLOW = 0x0c, + STATUS_PREVIOUS_COMMAND_PENDING = 0x0d, + STATUS_NOT_IMPLEMENTED = 0x0e, + STATUS_ERROR = 0x0f, + STATUS_INVALID = 0xff, +}; + +enum report_type { + REPORT_IDENTIFY = 0x10, + REPORT_TOUCH = 0x11, + REPORT_DELTA = 0x12, + REPORT_RAW = 0x13, + REPORT_STATUS = 0x1b, + REPORT_PRINTF = 0x82, + REPORT_HDL_ROMBOOT = 0xfd, + REPORT_HDL_F35 = 0xfe, +}; + +enum command_status { + CMD_IDLE = 0, + CMD_BUSY = 1, + CMD_ERROR = -1, +}; + +enum flash_area { + BOOTLOADER = 0, + BOOT_CONFIG, + APP_FIRMWARE, + APP_CONFIG, + DISP_CONFIG, + CUSTOM_OTP, + CUSTOM_LCM, + CUSTOM_OEM, + PPDT, +}; + +enum flash_data { + LCM_DATA = 1, + OEM_DATA, + PPDT_DATA, +}; + +enum helper_task { + HELP_NONE = 0, + HELP_RUN_APPLICATION_FIRMWARE, + HELP_SEND_REINIT_NOTIFICATION, + HELP_TOUCH_REINIT, + HELP_SEND_ROMBOOT_HDL, + HELP_SEND_REZERO_COMMAND, +}; + +struct syna_tcm_helper { + atomic_t task; + struct work_struct work; + struct workqueue_struct *workqueue; +}; + +struct syna_tcm_watchdog { + bool run; + unsigned char count; + struct delayed_work work; + struct workqueue_struct *workqueue; +}; + +struct syna_tcm_buffer { + bool clone; + unsigned char *buf; + unsigned int buf_size; + unsigned int data_length; + struct mutex buf_mutex; +}; + +struct syna_tcm_report { + unsigned char id; + struct syna_tcm_buffer buffer; +}; + +struct syna_tcm_identification { + unsigned char version; + unsigned char mode; + unsigned char part_number[16]; + unsigned char build_id[4]; + unsigned char max_write_size[2]; +}; + +struct syna_tcm_boot_info { + unsigned char version; + unsigned char status; + unsigned char asic_id[2]; + unsigned char write_block_size_words; + unsigned char erase_page_size_words[2]; + unsigned char max_write_payload_size[2]; + unsigned char last_reset_reason; + unsigned char pc_at_time_of_last_reset[2]; + unsigned char boot_config_start_block[2]; + unsigned char boot_config_size_blocks[2]; + unsigned char display_config_start_block[4]; + unsigned char display_config_length_blocks[2]; + unsigned char backup_display_config_start_block[4]; + unsigned char backup_display_config_length_blocks[2]; + unsigned char custom_otp_start_block[2]; + unsigned char custom_otp_length_blocks[2]; +}; + +struct syna_tcm_app_info { + unsigned char version[2]; + unsigned char status[2]; + unsigned char static_config_size[2]; + unsigned char dynamic_config_size[2]; + unsigned char app_config_start_write_block[2]; + unsigned char app_config_size[2]; + unsigned char max_touch_report_config_size[2]; + unsigned char max_touch_report_payload_size[2]; + unsigned char customer_config_id[16]; + unsigned char max_x[2]; + unsigned char max_y[2]; + unsigned char max_objects[2]; + unsigned char num_of_buttons[2]; + unsigned char num_of_image_rows[2]; + unsigned char num_of_image_cols[2]; + unsigned char has_hybrid_data[2]; + unsigned char num_of_force_elecs[2]; +}; + +struct syna_tcm_romboot_info { + unsigned char version; + unsigned char status; + unsigned char asic_id[2]; + unsigned char write_block_size_words; + unsigned char max_write_payload_size[2]; + unsigned char last_reset_reason; + unsigned char pc_at_time_of_last_reset[2]; +}; + +struct syna_tcm_touch_info { + unsigned char image_2d_scale_factor[4]; + unsigned char image_0d_scale_factor[4]; + unsigned char hybrid_x_scale_factor[4]; + unsigned char hybrid_y_scale_factor[4]; +}; + +struct syna_tcm_message_header { + unsigned char marker; + unsigned char code; + unsigned char length[2]; +}; + +struct syna_tcm_features { + unsigned char byte_0_reserved; + unsigned char byte_1_reserved; + unsigned char dual_firmware:1; + unsigned char byte_2_reserved:7; +} __packed; + +struct syna_tcm_hcd { + pid_t isr_pid; + atomic_t command_status; + atomic_t host_downloading; + atomic_t firmware_flashing; + wait_queue_head_t hdl_wq; + wait_queue_head_t reflash_wq; + int irq; + bool power_status; + bool do_polling; + bool in_suspend; + bool irq_enabled; + bool in_hdl_mode; + bool is_detected; + bool wakeup_gesture_enabled; + bool delay_baseline_update; + unsigned char sensor_type; + unsigned char fb_ready; + unsigned char command; + unsigned char async_report_id; + unsigned char status_report_code; + unsigned char response_code; + unsigned int read_length; + unsigned int payload_length; + unsigned int packrat_number; + unsigned int rd_chunk_size; + unsigned int wr_chunk_size; + unsigned int app_status; + + struct platform_device *pdev; + struct regulator *pwr_reg; + struct regulator *bus_reg; + struct kobject *sysfs_dir; + struct kobject *dynamnic_config_sysfs_dir; + struct mutex extif_mutex; + struct mutex reset_mutex; + struct mutex irq_en_mutex; + struct mutex io_ctrl_mutex; + struct mutex rw_ctrl_mutex; + struct mutex command_mutex; + struct mutex identify_mutex; + struct delayed_work polling_work; + struct workqueue_struct *polling_workqueue; + struct task_struct *notifier_thread; +#ifdef CONFIG_FB + struct notifier_block fb_notifier; +#endif + struct syna_tcm_buffer in; + struct syna_tcm_buffer out; + struct syna_tcm_buffer resp; + struct syna_tcm_buffer temp; + struct syna_tcm_buffer config; + struct syna_tcm_report report; + struct syna_tcm_app_info app_info; + struct syna_tcm_boot_info boot_info; + struct syna_tcm_romboot_info romboot_info; + struct syna_tcm_touch_info touch_info; + struct syna_tcm_identification id_info; + struct syna_tcm_helper helper; + struct syna_tcm_watchdog watchdog; + struct syna_tcm_features features; + const struct syna_tcm_hw_interface *hw_if; + int (*reset)(struct syna_tcm_hcd *tcm_hcd); + int (*reset_n_reinit)(struct syna_tcm_hcd *tcm_hcd, bool hw, bool update_wd); + int (*sleep)(struct syna_tcm_hcd *tcm_hcd, bool en); + int (*identify)(struct syna_tcm_hcd *tcm_hcd, bool id); + int (*enable_irq)(struct syna_tcm_hcd *tcm_hcd, bool en, bool ns); + int (*switch_mode)(struct syna_tcm_hcd *tcm_hcd, + enum firmware_mode mode); + int (*read_message)(struct syna_tcm_hcd *tcm_hcd, + unsigned char *in_buf, unsigned int length); + int (*write_message)(struct syna_tcm_hcd *tcm_hcd, + unsigned char command, unsigned char *payload, + unsigned int length, unsigned char **resp_buf, + unsigned int *resp_buf_size, unsigned int *resp_length, + unsigned char *response_code, + unsigned int polling_delay_ms); + int (*get_dynamic_config)(struct syna_tcm_hcd *tcm_hcd, + enum dynamic_config_id id, unsigned short *value); + int (*set_dynamic_config)(struct syna_tcm_hcd *tcm_hcd, + enum dynamic_config_id id, unsigned short value); + int (*get_data_location)(struct syna_tcm_hcd *tcm_hcd, + enum flash_area area, unsigned int *addr, + unsigned int *length); + int (*read_flash_data)(enum flash_area area, bool run_app_firmware, + struct syna_tcm_buffer *output); + void (*report_touch)(void); + void (*update_watchdog)(struct syna_tcm_hcd *tcm_hcd, bool en); + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + int (*do_fw_update)(void); + int (*set_fw_name)(const char* fw_name); + struct ts_mmi_class_methods *imports; +#endif +}; + +struct syna_tcm_module_cb { + enum module_type type; + int (*init)(struct syna_tcm_hcd *tcm_hcd); + int (*remove)(struct syna_tcm_hcd *tcm_hcd); + int (*syncbox)(struct syna_tcm_hcd *tcm_hcd); +#ifdef REPORT_NOTIFIER + int (*asyncbox)(struct syna_tcm_hcd *tcm_hcd); +#endif + int (*reinit)(struct syna_tcm_hcd *tcm_hcd); + int (*suspend)(struct syna_tcm_hcd *tcm_hcd); + int (*resume)(struct syna_tcm_hcd *tcm_hcd); + int (*early_suspend)(struct syna_tcm_hcd *tcm_hcd); +}; + +struct syna_tcm_module_handler { + bool insert; + bool detach; + struct list_head link; + struct syna_tcm_module_cb *mod_cb; +}; + +struct syna_tcm_module_pool { + bool initialized; + bool queue_work; + struct mutex mutex; + struct list_head list; + struct work_struct work; + struct workqueue_struct *workqueue; + struct syna_tcm_hcd *tcm_hcd; +}; + +struct syna_tcm_bus_io { + unsigned char type; + int (*rmi_read)(struct syna_tcm_hcd *tcm_hcd, unsigned short addr, + unsigned char *data, unsigned int length); + int (*rmi_write)(struct syna_tcm_hcd *tcm_hcd, unsigned short addr, + unsigned char *data, unsigned int length); + int (*read)(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, + unsigned int length); + int (*write)(struct syna_tcm_hcd *tcm_hcd, unsigned char *data, + unsigned int length); +}; + +struct syna_tcm_hw_interface { + struct syna_tcm_board_data *bdata; + const struct syna_tcm_bus_io *bus_io; +}; + +int syna_tcm_bus_init(void); + +void syna_tcm_bus_exit(void); + +int syna_tcm_add_module(struct syna_tcm_module_cb *mod_cb, bool insert); + +int touch_init(struct syna_tcm_hcd *tcm_hcd); +int touch_remove(struct syna_tcm_hcd *tcm_hcd); +int touch_reinit(struct syna_tcm_hcd *tcm_hcd); +int touch_early_suspend(struct syna_tcm_hcd *tcm_hcd); +int touch_suspend(struct syna_tcm_hcd *tcm_hcd); +int touch_resume(struct syna_tcm_hcd *tcm_hcd); +void touch_free_objects(void); + +int syna_tcm_resume(struct device *dev); +int syna_tcm_suspend(struct device *dev); +int syna_tcm_early_suspend(struct device *dev); + +static inline int syna_tcm_rmi_read(struct syna_tcm_hcd *tcm_hcd, + unsigned short addr, unsigned char *data, unsigned int length) +{ + return tcm_hcd->hw_if->bus_io->rmi_read(tcm_hcd, addr, data, length); +} + +static inline int syna_tcm_rmi_write(struct syna_tcm_hcd *tcm_hcd, + unsigned short addr, unsigned char *data, unsigned int length) +{ + return tcm_hcd->hw_if->bus_io->rmi_write(tcm_hcd, addr, data, length); +} + +static inline int syna_tcm_read(struct syna_tcm_hcd *tcm_hcd, + unsigned char *data, unsigned int length) +{ + return tcm_hcd->hw_if->bus_io->read(tcm_hcd, data, length); +} + +static inline int syna_tcm_write(struct syna_tcm_hcd *tcm_hcd, + unsigned char *data, unsigned int length) +{ + return tcm_hcd->hw_if->bus_io->write(tcm_hcd, data, length); +} + +static inline ssize_t syna_tcm_show_error(struct device *dev, + struct device_attribute *attr, char *buf) +{ + pr_err("%s: Attribute not readable\n", + __func__); + + return -EPERM; +} + +static inline ssize_t syna_tcm_store_error(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + pr_err("%s: Attribute not writable\n", + __func__); + + return -EPERM; +} + +static inline int secure_memcpy(unsigned char *dest, unsigned int dest_size, + const unsigned char *src, unsigned int src_size, + unsigned int count) +{ + if (dest == NULL || src == NULL) + return -EINVAL; + + if (count > dest_size || count > src_size) { + pr_err("%s: src_size = %d, dest_size = %d, count = %d\n", + __func__, src_size, dest_size, count); + return -EINVAL; + } + + memcpy((void *)dest, (const void *)src, count); + + return 0; +} + +static inline int syna_tcm_realloc_mem(struct syna_tcm_hcd *tcm_hcd, + struct syna_tcm_buffer *buffer, unsigned int size) +{ + int retval; + unsigned char *temp; + + if (size > buffer->buf_size) { + temp = buffer->buf; + + buffer->buf = kmalloc(size, GFP_KERNEL); + if (!(buffer->buf)) { + dev_err(tcm_hcd->pdev->dev.parent, + "%s: Failed to allocate memory\n", + __func__); + kfree(temp); + buffer->buf_size = 0; + return -ENOMEM; + } + + retval = secure_memcpy(buffer->buf, + size, + temp, + buffer->buf_size, + buffer->buf_size); + if (retval < 0) { + dev_err(tcm_hcd->pdev->dev.parent, + "%s: Failed to copy data\n", + __func__); + kfree(temp); + kfree(buffer->buf); + buffer->buf_size = 0; + return retval; + } + + kfree(temp); + buffer->buf_size = size; + } + + return 0; +} + +static inline int syna_tcm_alloc_mem(struct syna_tcm_hcd *tcm_hcd, + struct syna_tcm_buffer *buffer, unsigned int size) +{ + if (size > buffer->buf_size) { + kfree(buffer->buf); + buffer->buf = kmalloc(size, GFP_KERNEL); + if (!(buffer->buf)) { + dev_err(tcm_hcd->pdev->dev.parent, + "%s: Failed to allocate memory\n", + __func__); + dev_err(tcm_hcd->pdev->dev.parent, + "%s: Allocation size = %d\n", + __func__, size); + buffer->buf_size = 0; + buffer->data_length = 0; + return -ENOMEM; + } + buffer->buf_size = size; + } + + memset(buffer->buf, 0x00, buffer->buf_size); + buffer->data_length = 0; + + return 0; +} + +static inline unsigned int le2_to_uint(const unsigned char *src) +{ + return (unsigned int)src[0] + + (unsigned int)src[1] * 0x100; +} + +static inline unsigned int le4_to_uint(const unsigned char *src) +{ + return (unsigned int)src[0] + + (unsigned int)src[1] * 0x100 + + (unsigned int)src[2] * 0x10000 + + (unsigned int)src[3] * 0x1000000; +} + +static inline unsigned int ceil_div(unsigned int dividend, unsigned divisor) +{ + return (dividend + divisor - 1) / divisor; +} + +#endif diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/synaptics_tcm.h b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/synaptics_tcm.h new file mode 100644 index 000000000000..2274fbd601eb --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/synaptics_tcm.h @@ -0,0 +1,65 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * + * 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. + */ + +#ifndef _SYNAPTICS_TCM_H_ +#define _SYNAPTICS_TCM_H_ + +#define I2C_MODULE_NAME "synaptics_tcm_i2c" +#define SPI_MODULE_NAME "synaptics_tcm_spi" + +struct syna_tcm_board_data { + bool x_flip; + bool y_flip; + bool swap_axes; + int size_to_pixel_multiple_factor; + int irq_gpio; + int irq_on_state; + int power_gpio; + int power_on_state; + int reset_gpio; + int reset_on_state; + int tpio_reset_gpio; + unsigned int spi_mode; + unsigned int power_delay_ms; + unsigned int reset_delay_ms; + unsigned int reset_active_ms; + unsigned int byte_delay_us; + unsigned int block_delay_us; + unsigned int ubl_i2c_addr; + unsigned int ubl_max_freq; + unsigned int ubl_byte_delay_us; + unsigned long irq_flags; + const char *pwr_reg_name; + const char *bus_reg_name; +}; + +#endif diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/synaptics_touch.c b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/synaptics_touch.c new file mode 100644 index 000000000000..f1dc9be25f4b --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/synaptics_touch.c @@ -0,0 +1,1411 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * Copyright (C) 2018-2019 Ian Su + * Copyright (C) 2018-2019 Joey Zhou + * Copyright (C) 2018-2019 Yuehao Qiu + * Copyright (C) 2018-2019 Aaron Chen + * + * 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. + */ + +#include +#include +#include +#include "synaptics_core.h" + +#define TYPE_B_PROTOCOL + +#define USE_DEFAULT_TOUCH_REPORT_CONFIG + +#define TOUCH_REPORT_CONFIG_SIZE 128 + +enum touch_status { + LIFT = 0, + FINGER = 1, + GLOVED_FINGER = 2, + NOP = -1, +}; + +enum gesture_id { + NO_GESTURE_DETECTED = 0, + GESTURE_DOUBLE_TAP = 0X01, + GESTURE_SINGLE_TAP = 0x10, + GESTURE_ZERO_TAP = 0x11, +}; + +enum touch_report_code { + TOUCH_END = 0, + TOUCH_FOREACH_ACTIVE_OBJECT, + TOUCH_FOREACH_OBJECT, + TOUCH_FOREACH_END, + TOUCH_PAD_TO_NEXT_BYTE, + TOUCH_TIMESTAMP, + TOUCH_OBJECT_N_INDEX, + TOUCH_OBJECT_N_CLASSIFICATION, + TOUCH_OBJECT_N_X_POSITION, + TOUCH_OBJECT_N_Y_POSITION, + TOUCH_OBJECT_N_Z, + TOUCH_OBJECT_N_X_WIDTH, + TOUCH_OBJECT_N_Y_WIDTH, + TOUCH_OBJECT_N_TX_POSITION_TIXELS, + TOUCH_OBJECT_N_RX_POSITION_TIXELS, + TOUCH_0D_BUTTONS_STATE, + TOUCH_GESTURE_ID, + TOUCH_FRAME_RATE, + TOUCH_POWER_IM, + TOUCH_CID_IM, + TOUCH_RAIL_IM, + TOUCH_CID_VARIANCE_IM, + TOUCH_NSM_FREQUENCY, + TOUCH_NSM_STATE, + TOUCH_NUM_OF_ACTIVE_OBJECTS, + TOUCH_NUM_OF_CPU_CYCLES_USED_SINCE_LAST_FRAME, + TOUCH_FACE_DETECT, + TOUCH_GESTURE_DATA, + TOUCH_OBJECT_N_FORCE, + TOUCH_FINGERPRINT_AREA_MEET, + TOUCH_TUNING_GAUSSIAN_WIDTHS = 0x80, + TOUCH_TUNING_SMALL_OBJECT_PARAMS, + TOUCH_TUNING_0D_BUTTONS_VARIANCE, +}; + +struct object_data { + unsigned char status; + unsigned int x_pos; + unsigned int y_pos; + unsigned int x_width; + unsigned int y_width; + unsigned int z; + unsigned int tx_pos; + unsigned int rx_pos; +}; + +struct input_params { + unsigned int max_x; + unsigned int max_y; + unsigned int max_objects; +}; + +struct gesture_data { + unsigned int x_pos; + unsigned int y_pos; + unsigned int z_pos; +}; + +struct touch_data { + struct object_data *object_data; + unsigned int timestamp; + unsigned int buttons_state; + unsigned int gesture_id; + unsigned int frame_rate; + unsigned int power_im; + unsigned int cid_im; + unsigned int rail_im; + unsigned int cid_variance_im; + unsigned int nsm_frequency; + unsigned int nsm_state; + unsigned int num_of_active_objects; + unsigned int num_of_cpu_cycles; + unsigned int fd_data; + unsigned int force_data; + unsigned int fingerprint_area_meet; + struct gesture_data gesture_data; +}; + +struct touch_hcd { + bool irq_wake; + bool init_touch_ok; + bool suspend_touch; + unsigned char *prev_status; + unsigned int max_x; + unsigned int max_y; + unsigned int max_objects; + struct mutex report_mutex; + struct input_dev *input_dev; + struct touch_data touch_data; + struct input_params input_params; + struct syna_tcm_buffer out; + struct syna_tcm_buffer resp; + struct syna_tcm_hcd *tcm_hcd; + struct wakeup_source *gesture_wakelock; +}; + +static struct touch_hcd *touch_hcd; + +/** + * touch_free_objects() - Free all touch objects + * + * Report finger lift events to the input subsystem for all touch objects. + */ +void touch_free_objects(void) +{ +#ifdef TYPE_B_PROTOCOL + unsigned int idx; +#endif + + if (touch_hcd->input_dev == NULL) + return; + + mutex_lock(&touch_hcd->report_mutex); + +#ifdef TYPE_B_PROTOCOL + for (idx = 0; idx < touch_hcd->max_objects; idx++) { + input_mt_slot(touch_hcd->input_dev, idx); + input_mt_report_slot_state(touch_hcd->input_dev, + MT_TOOL_FINGER, 0); + touch_hcd->prev_status[idx] = LIFT; + } +#endif + input_report_key(touch_hcd->input_dev, + BTN_TOUCH, 0); + input_report_key(touch_hcd->input_dev, + BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(touch_hcd->input_dev); +#endif + input_sync(touch_hcd->input_dev); + + mutex_unlock(&touch_hcd->report_mutex); + + return; +} + +/** + * touch_get_report_data() - Retrieve data from touch report + * + * Retrieve data from the touch report based on the bit offset and bit length + * information from the touch report configuration. + */ +static int touch_get_report_data(unsigned int offset, + unsigned int bits, unsigned int *data) +{ + unsigned char mask; + unsigned char byte_data; + unsigned int output_data; + unsigned int bit_offset; + unsigned int byte_offset; + unsigned int data_bits; + unsigned int available_bits; + unsigned int remaining_bits; + unsigned char *touch_report; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + + if (bits == 0 || bits > 32) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid number of bits\n"); + return -EINVAL; + } + + if (offset + bits > tcm_hcd->report.buffer.data_length * 8) { + *data = 0; + return 0; + } + + touch_report = tcm_hcd->report.buffer.buf; + + output_data = 0; + remaining_bits = bits; + + bit_offset = offset % 8; + byte_offset = offset / 8; + + while (remaining_bits) { + byte_data = touch_report[byte_offset]; + byte_data >>= bit_offset; + + available_bits = 8 - bit_offset; + data_bits = MIN(available_bits, remaining_bits); + mask = 0xff >> (8 - data_bits); + + byte_data &= mask; + + output_data |= byte_data << (bits - remaining_bits); + + bit_offset = 0; + byte_offset += 1; + remaining_bits -= data_bits; + } + + *data = output_data; + + return 0; +} + +/** + * touch_parse_report() - Parse touch report + * + * Traverse through the touch report configuration and parse the touch report + * generated by the device accordingly to retrieve the touch data. + */ +static int touch_parse_report(void) +{ + int retval; + bool active_only; + bool num_of_active_objects; + unsigned char code; + unsigned int size; + unsigned int idx; + unsigned int obj; + unsigned int next; + unsigned int data; + unsigned int bits; + unsigned int offset; + unsigned int objects; + unsigned int active_objects; + unsigned int report_size; + unsigned int config_size; + unsigned char *config_data; + struct touch_data *touch_data; + struct object_data *object_data; + struct gesture_data *gesture_data; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + static unsigned int end_of_foreach; + + touch_data = &touch_hcd->touch_data; + object_data = touch_hcd->touch_data.object_data; + gesture_data = &touch_hcd->touch_data.gesture_data; + + config_data = tcm_hcd->config.buf; + config_size = tcm_hcd->config.data_length; + + report_size = tcm_hcd->report.buffer.data_length; + + size = sizeof(*object_data) * touch_hcd->max_objects; + memset(touch_hcd->touch_data.object_data, 0x00, size); + + num_of_active_objects = false; + + idx = 0; + offset = 0; + objects = 0; + while (idx < config_size) { + code = config_data[idx++]; + switch (code) { + case TOUCH_END: + goto exit; + case TOUCH_FOREACH_ACTIVE_OBJECT: + obj = 0; + next = idx; + active_only = true; + break; + case TOUCH_FOREACH_OBJECT: + obj = 0; + next = idx; + active_only = false; + break; + case TOUCH_FOREACH_END: + end_of_foreach = idx; + if (active_only) { + if (num_of_active_objects) { + objects++; + if (objects < active_objects) + idx = next; + } else if (offset < report_size * 8) { + idx = next; + } + } else { + obj++; + if (obj < touch_hcd->max_objects) + idx = next; + } + break; + case TOUCH_PAD_TO_NEXT_BYTE: + offset = ceil_div(offset, 8) * 8; + break; + case TOUCH_TIMESTAMP: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get timestamp\n"); + return retval; + } + touch_data->timestamp = data; + offset += bits; + break; + case TOUCH_OBJECT_N_INDEX: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &obj); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object index\n"); + return retval; + } + offset += bits; + break; + case TOUCH_OBJECT_N_CLASSIFICATION: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object classification\n"); + return retval; + } + object_data[obj].status = data; + offset += bits; + break; + case TOUCH_OBJECT_N_X_POSITION: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object x position\n"); + return retval; + } + object_data[obj].x_pos = data; + offset += bits; + break; + case TOUCH_OBJECT_N_Y_POSITION: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object y position\n"); + return retval; + } + object_data[obj].y_pos = data; + offset += bits; + break; + case TOUCH_OBJECT_N_Z: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object z\n"); + return retval; + } + object_data[obj].z = data; + offset += bits; + break; + case TOUCH_OBJECT_N_X_WIDTH: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object x width\n"); + return retval; + } + object_data[obj].x_width = data; + offset += bits; + break; + case TOUCH_OBJECT_N_Y_WIDTH: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object y width\n"); + return retval; + } + object_data[obj].y_width = data; + offset += bits; + break; + case TOUCH_OBJECT_N_TX_POSITION_TIXELS: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object tx position\n"); + return retval; + } + object_data[obj].tx_pos = data; + offset += bits; + break; + case TOUCH_OBJECT_N_RX_POSITION_TIXELS: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object rx position\n"); + return retval; + } + object_data[obj].rx_pos = data; + offset += bits; + break; + case TOUCH_OBJECT_N_FORCE: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object force\n"); + return retval; + } + touch_data->force_data = data; + offset += bits; + break; + case TOUCH_FINGERPRINT_AREA_MEET: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get object force\n"); + return retval; + } + touch_data->fingerprint_area_meet = data; + LOGN(tcm_hcd->pdev->dev.parent, + "fingerprint_area_meet = %x\n", touch_data->fingerprint_area_meet); + offset += bits; + break; + case TOUCH_0D_BUTTONS_STATE: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get 0D buttons state\n"); + return retval; + } + touch_data->buttons_state = data; + offset += bits; + break; + case TOUCH_GESTURE_ID: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get gesture double tap\n"); + return retval; + } + touch_data->gesture_id = data; + offset += bits; + break; + case TOUCH_FRAME_RATE: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get frame rate\n"); + return retval; + } + touch_data->frame_rate = data; + offset += bits; + break; + case TOUCH_POWER_IM: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get power IM\n"); + return retval; + } + touch_data->power_im = data; + offset += bits; + break; + case TOUCH_CID_IM: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get CID IM\n"); + return retval; + } + touch_data->cid_im = data; + offset += bits; + break; + case TOUCH_RAIL_IM: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get rail IM\n"); + return retval; + } + touch_data->rail_im = data; + offset += bits; + break; + case TOUCH_CID_VARIANCE_IM: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get CID variance IM\n"); + return retval; + } + touch_data->cid_variance_im = data; + offset += bits; + break; + case TOUCH_NSM_FREQUENCY: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get NSM frequency\n"); + return retval; + } + touch_data->nsm_frequency = data; + offset += bits; + break; + case TOUCH_NSM_STATE: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get NSM state\n"); + return retval; + } + touch_data->nsm_state = data; + offset += bits; + break; + case TOUCH_GESTURE_DATA: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits/3, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get number of active objects\n"); + return retval; + } + gesture_data->x_pos = data; + offset += (bits/3); + + retval = touch_get_report_data(offset, bits/3, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get number of active objects\n"); + return retval; + } + gesture_data->y_pos = data; + offset += (bits/3); + + retval = touch_get_report_data(offset, bits/3, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get number of active objects\n"); + return retval; + } + gesture_data->z_pos = data; + offset += (bits/3); + break; + case TOUCH_NUM_OF_ACTIVE_OBJECTS: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get number of active objects\n"); + return retval; + } + active_objects = data; + num_of_active_objects = true; + touch_data->num_of_active_objects = data; + offset += bits; + if (touch_data->num_of_active_objects == 0) + idx = end_of_foreach; + break; + case TOUCH_NUM_OF_CPU_CYCLES_USED_SINCE_LAST_FRAME: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get number of CPU cycles used since last frame\n"); + return retval; + } + touch_data->num_of_cpu_cycles = data; + offset += bits; + break; + case TOUCH_FACE_DETECT: + bits = config_data[idx++]; + retval = touch_get_report_data(offset, bits, &data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to detect face\n"); + return retval; + } + touch_data->fd_data = data; + offset += bits; + break; + case TOUCH_TUNING_GAUSSIAN_WIDTHS: + bits = config_data[idx++]; + offset += bits; + break; + case TOUCH_TUNING_SMALL_OBJECT_PARAMS: + bits = config_data[idx++]; + offset += bits; + break; + case TOUCH_TUNING_0D_BUTTONS_VARIANCE: + bits = config_data[idx++]; + offset += bits; + break; + default: + bits = config_data[idx++]; + offset += bits; + break; + } + } + +exit: + return 0; +} + +/** + * touch_report() - Report touch events + * + * Retrieve data from the touch report generated by the device and report touch + * events to the input subsystem. + */ +static void touch_report(void) +{ + int retval; + unsigned int idx; + unsigned int x; + unsigned int y; + unsigned int major; + unsigned int minor; + unsigned int temp; + unsigned int status; + unsigned int touch_count; + struct touch_data *touch_data; + struct object_data *object_data; + struct gesture_data *gesture_data; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + if (!touch_hcd->init_touch_ok) + return; + + if (touch_hcd->input_dev == NULL) + return; + + if (touch_hcd->suspend_touch) + return; + + mutex_lock(&touch_hcd->report_mutex); + + retval = touch_parse_report(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to parse touch report\n"); + goto exit; + } + + touch_data = &touch_hcd->touch_data; + object_data = touch_hcd->touch_data.object_data; + gesture_data = &touch_hcd->touch_data.gesture_data; + +#if WAKEUP_GESTURE +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + if (touch_data->gesture_id == GESTURE_SINGLE_TAP && + tcm_hcd->in_suspend && + tcm_hcd->wakeup_gesture_enabled) { + if (tcm_hcd->imports && tcm_hcd->imports->report_gesture) { + struct gesture_event_data event; + event.evcode = 1; + event.evdata.x = gesture_data->x_pos; + event.evdata.y = gesture_data->y_pos; + retval = tcm_hcd->imports->report_gesture(&event); + if (!retval) + PM_WAKEUP_EVENT(touch_hcd->gesture_wakelock, 3000); + } + } + + if (touch_data->gesture_id == GESTURE_ZERO_TAP && + tcm_hcd->in_suspend && + tcm_hcd->wakeup_gesture_enabled) { + if (tcm_hcd->imports && tcm_hcd->imports->report_gesture) { + struct gesture_event_data event; + event.evcode = 2; + event.evdata.x = gesture_data->x_pos; + event.evdata.y = gesture_data->y_pos; + retval = tcm_hcd->imports->report_gesture(&event); + if (!retval) + PM_WAKEUP_EVENT(touch_hcd->gesture_wakelock, 3000); + } + } +#endif + + if (touch_data->gesture_id == GESTURE_DOUBLE_TAP && + tcm_hcd->in_suspend && + tcm_hcd->wakeup_gesture_enabled) { + + input_report_key(touch_hcd->input_dev, KEY_WAKEUP, 1); + input_sync(touch_hcd->input_dev); + input_report_key(touch_hcd->input_dev, KEY_WAKEUP, 0); + input_sync(touch_hcd->input_dev); + } +#endif + + if (tcm_hcd->in_suspend) + goto exit; + + touch_count = 0; + + for (idx = 0; idx < touch_hcd->max_objects; idx++) { + if (touch_hcd->prev_status[idx] == LIFT && + object_data[idx].status == LIFT) + status = NOP; + else + status = object_data[idx].status; + + switch (status) { + case LIFT: +#ifdef TYPE_B_PROTOCOL +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + if (tcm_hcd->imports && tcm_hcd->imports->report_touch_event) { + struct touch_event_data touch_event; + memset(&touch_event, 0, sizeof(touch_event)); + touch_event.type = TS_COORDINATE_ACTION_RELEASE; + touch_event.id = idx; + tcm_hcd->imports->report_touch_event(&touch_event, touch_hcd->input_dev); + if (touch_event.skip_report) { + /* input event is reported by touchscreen class. + * vendor touch event may need update based on touch_event + */ + break; + } + } +#endif + input_mt_slot(touch_hcd->input_dev, idx); + input_mt_report_slot_state(touch_hcd->input_dev, + MT_TOOL_FINGER, 0); +#endif + break; + case FINGER: + case GLOVED_FINGER: + x = object_data[idx].x_pos; + y = object_data[idx].y_pos; + if (object_data[idx].x_width > object_data[idx].y_width) { + minor = (object_data[idx].y_width * bdata->size_to_pixel_multiple_factor) >> 7; + major = (object_data[idx].x_width * bdata->size_to_pixel_multiple_factor) >> 7; + } else { + minor = (object_data[idx].x_width * bdata->size_to_pixel_multiple_factor) >> 7; + major = (object_data[idx].y_width * bdata->size_to_pixel_multiple_factor) >> 7; + } + if (bdata->swap_axes) { + temp = x; + x = y; + y = temp; + } + if (bdata->x_flip) + x = touch_hcd->input_params.max_x - x; + if (bdata->y_flip) + y = touch_hcd->input_params.max_y - y; + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + if (tcm_hcd->imports && tcm_hcd->imports->report_touch_event) { + struct touch_event_data touch_event; + memset(&touch_event, 0, sizeof(touch_event)); + touch_event.type = (object_data[idx].status != touch_hcd->prev_status[idx])? + TS_COORDINATE_ACTION_PRESS : TS_COORDINATE_ACTION_MOVE; + touch_event.id = idx; + touch_event.x = x; + touch_event.y = y; + touch_event.major = major; + touch_event.minor = minor; + tcm_hcd->imports->report_touch_event(&touch_event, touch_hcd->input_dev); + if (touch_event.skip_report) { + /* input event is reported by touchscreen class. + * vendor touch event may need update based on touch_event + */ + if ((touch_event.type == TS_COORDINATE_ACTION_PRESS) || + (touch_event.type == TS_COORDINATE_ACTION_MOVE)) + touch_count++; + break; + } + } +#endif + +#ifdef TYPE_B_PROTOCOL + input_mt_slot(touch_hcd->input_dev, idx); + input_mt_report_slot_state(touch_hcd->input_dev, + MT_TOOL_FINGER, 1); +#endif + input_report_key(touch_hcd->input_dev, + BTN_TOUCH, 1); + input_report_key(touch_hcd->input_dev, + BTN_TOOL_FINGER, 1); + input_report_abs(touch_hcd->input_dev, + ABS_MT_POSITION_X, x); + input_report_abs(touch_hcd->input_dev, + ABS_MT_POSITION_Y, y); + input_report_abs(touch_hcd->input_dev, + ABS_MT_TOUCH_MAJOR, major); + input_report_abs(touch_hcd->input_dev, + ABS_MT_TOUCH_MINOR, minor); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(touch_hcd->input_dev); +#endif + LOGD(tcm_hcd->pdev->dev.parent, + "Finger %d: x = %d, y = %d, x_width = %d, y_width = %d\n", + idx, x, y, object_data[idx].x_width, object_data[idx].y_width); + touch_count++; + break; + default: + break; + } + + touch_hcd->prev_status[idx] = object_data[idx].status; + } + + if (touch_count == 0) { + input_report_key(touch_hcd->input_dev, + BTN_TOUCH, 0); + input_report_key(touch_hcd->input_dev, + BTN_TOOL_FINGER, 0); +#ifndef TYPE_B_PROTOCOL + input_mt_sync(touch_hcd->input_dev); +#endif + } + + input_sync(touch_hcd->input_dev); + +exit: + mutex_unlock(&touch_hcd->report_mutex); + + return; +} + +/** + * touch_set_input_params() - Set input parameters + * + * Set the input parameters of the input device based on the information + * retrieved from the application information packet. In addition, set up an + * array for tracking the status of touch objects. + */ +static int touch_set_input_params(void) +{ + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + + input_set_abs_params(touch_hcd->input_dev, + ABS_MT_POSITION_X, 0, touch_hcd->max_x, 0, 0); + input_set_abs_params(touch_hcd->input_dev, + ABS_MT_POSITION_Y, 0, touch_hcd->max_y, 0, 0); + input_set_abs_params(touch_hcd->input_dev, + ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(touch_hcd->input_dev, + ABS_MT_TOUCH_MINOR, 0, 255, 0, 0); + + input_mt_init_slots(touch_hcd->input_dev, touch_hcd->max_objects, + INPUT_MT_DIRECT); + + touch_hcd->input_params.max_x = touch_hcd->max_x; + touch_hcd->input_params.max_y = touch_hcd->max_y; + touch_hcd->input_params.max_objects = touch_hcd->max_objects; + + if (touch_hcd->max_objects == 0) + return 0; + + kfree(touch_hcd->prev_status); + touch_hcd->prev_status = kzalloc(touch_hcd->max_objects, GFP_KERNEL); + if (!touch_hcd->prev_status) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for touch_hcd->prev_status\n"); + return -ENOMEM; + } + + return 0; +} + +/** + * touch_get_input_params() - Get input parameters + * + * Retrieve the input parameters to register with the input subsystem for + * the input device from the application information packet. In addition, + * the touch report configuration is retrieved and stored. + */ +static int touch_get_input_params(void) +{ + int retval; + unsigned int temp; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + app_info = &tcm_hcd->app_info; + touch_hcd->max_x = le2_to_uint(app_info->max_x); + touch_hcd->max_y = le2_to_uint(app_info->max_y); + touch_hcd->max_objects = le2_to_uint(app_info->max_objects); + + if (bdata->swap_axes) { + temp = touch_hcd->max_x; + touch_hcd->max_x = touch_hcd->max_y; + touch_hcd->max_y = temp; + } + + LOCK_BUFFER(tcm_hcd->config); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_TOUCH_REPORT_CONFIG, + NULL, + 0, + &tcm_hcd->config.buf, + &tcm_hcd->config.buf_size, + &tcm_hcd->config.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_TOUCH_REPORT_CONFIG)); + UNLOCK_BUFFER(tcm_hcd->config); + return retval; + } + + UNLOCK_BUFFER(tcm_hcd->config); + + return 0; +} + +/** + * touch_set_input_dev() - Set up input device + * + * Allocate an input device, configure the input device based on the particular + * input events to be reported, and register the input device with the input + * subsystem. + */ +static int touch_set_input_dev(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + + touch_hcd->input_dev = input_allocate_device(); + if (touch_hcd->input_dev == NULL) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate input device\n"); + return -ENODEV; + } + + touch_hcd->input_dev->name = TOUCH_INPUT_NAME; + touch_hcd->input_dev->phys = TOUCH_INPUT_PHYS_PATH; + touch_hcd->input_dev->id.product = SYNAPTICS_TCM_ID_PRODUCT; + touch_hcd->input_dev->id.version = SYNAPTICS_TCM_ID_VERSION; + touch_hcd->input_dev->dev.parent = tcm_hcd->pdev->dev.parent; + input_set_drvdata(touch_hcd->input_dev, tcm_hcd); + + set_bit(EV_SYN, touch_hcd->input_dev->evbit); + set_bit(EV_KEY, touch_hcd->input_dev->evbit); + set_bit(EV_ABS, touch_hcd->input_dev->evbit); + set_bit(BTN_TOUCH, touch_hcd->input_dev->keybit); + set_bit(BTN_TOOL_FINGER, touch_hcd->input_dev->keybit); +#ifdef INPUT_PROP_DIRECT + set_bit(INPUT_PROP_DIRECT, touch_hcd->input_dev->propbit); +#endif + +#if WAKEUP_GESTURE + set_bit(KEY_WAKEUP, touch_hcd->input_dev->keybit); + input_set_capability(touch_hcd->input_dev, EV_KEY, KEY_WAKEUP); +#endif + + retval = touch_set_input_params(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set input parameters\n"); + input_free_device(touch_hcd->input_dev); + touch_hcd->input_dev = NULL; + return retval; + } + + retval = input_register_device(touch_hcd->input_dev); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to register input device\n"); + input_free_device(touch_hcd->input_dev); + touch_hcd->input_dev = NULL; + return retval; + } + + return 0; +} + +/** + * touch_set_report_config() - Set touch report configuration + * + * Send the SET_TOUCH_REPORT_CONFIG command to configure the format and content + * of the touch report. + */ +static int touch_set_report_config(void) +{ + int retval; + unsigned int idx; + unsigned int length; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + +#ifdef USE_DEFAULT_TOUCH_REPORT_CONFIG + return 0; +#endif + + app_info = &tcm_hcd->app_info; + length = le2_to_uint(app_info->max_touch_report_config_size); + + if (length < TOUCH_REPORT_CONFIG_SIZE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid maximum touch report config size\n"); + return -EINVAL; + } + + LOCK_BUFFER(touch_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &touch_hcd->out, + length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for touch_hcd->out.buf\n"); + UNLOCK_BUFFER(touch_hcd->out); + return retval; + } + + idx = 0; +#if WAKEUP_GESTURE + touch_hcd->out.buf[idx++] = TOUCH_GESTURE_ID; + touch_hcd->out.buf[idx++] = 8; + touch_hcd->out.buf[idx++] = TOUCH_GESTURE_DATA; + touch_hcd->out.buf[idx++] = 48; +#endif + touch_hcd->out.buf[idx++] = TOUCH_FOREACH_ACTIVE_OBJECT; + touch_hcd->out.buf[idx++] = TOUCH_OBJECT_N_INDEX; + touch_hcd->out.buf[idx++] = 4; + touch_hcd->out.buf[idx++] = TOUCH_OBJECT_N_CLASSIFICATION; + touch_hcd->out.buf[idx++] = 4; + touch_hcd->out.buf[idx++] = TOUCH_OBJECT_N_X_POSITION; + touch_hcd->out.buf[idx++] = 12; + touch_hcd->out.buf[idx++] = TOUCH_OBJECT_N_Y_POSITION; + touch_hcd->out.buf[idx++] = 12; + touch_hcd->out.buf[idx++] = TOUCH_FOREACH_END; + touch_hcd->out.buf[idx++] = TOUCH_END; + + LOCK_BUFFER(touch_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_SET_TOUCH_REPORT_CONFIG, + touch_hcd->out.buf, + length, + &touch_hcd->resp.buf, + &touch_hcd->resp.buf_size, + &touch_hcd->resp.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_SET_TOUCH_REPORT_CONFIG)); + UNLOCK_BUFFER(touch_hcd->resp); + UNLOCK_BUFFER(touch_hcd->out); + return retval; + } + + UNLOCK_BUFFER(touch_hcd->resp); + UNLOCK_BUFFER(touch_hcd->out); + + LOGN(tcm_hcd->pdev->dev.parent, + "Set touch config done\n"); + + return 0; +} + +/** + * touch_check_input_params() - Check input parameters + * + * Check if any of the input parameters registered with the input subsystem for + * the input device has changed. + */ +static int touch_check_input_params(void) +{ + unsigned int size; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + + if (touch_hcd->max_x == 0 && touch_hcd->max_y == 0) + return 0; + + if (touch_hcd->input_params.max_objects != touch_hcd->max_objects) { + kfree(touch_hcd->touch_data.object_data); + size = sizeof(*touch_hcd->touch_data.object_data); + size *= touch_hcd->max_objects; + touch_hcd->touch_data.object_data = kzalloc(size, GFP_KERNEL); + if (!touch_hcd->touch_data.object_data) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for touch_hcd->touch_data.object_data\n"); + return -ENOMEM; + } + return 1; + } + + if (touch_hcd->input_params.max_x != touch_hcd->max_x) + return 1; + + if (touch_hcd->input_params.max_y != touch_hcd->max_y) + return 1; + + return 0; +} + +/** + * touch_set_input_reporting() - Configure touch report and set up new input + * device if necessary + * + * After a device reset event, configure the touch report and set up a new input + * device if any of the input parameters has changed after the device reset. + */ +static int touch_set_input_reporting(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = touch_hcd->tcm_hcd; + + if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode) || + tcm_hcd->app_status != APP_STATUS_OK) { + LOGN(tcm_hcd->pdev->dev.parent, + "Identifying mode = 0x%02x\n", + tcm_hcd->id_info.mode); + + return 0; + } + + touch_hcd->init_touch_ok = false; + + touch_free_objects(); + + mutex_lock(&touch_hcd->report_mutex); + + retval = touch_set_report_config(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set report config\n"); + goto exit; + } + + retval = touch_get_input_params(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get input parameters\n"); + goto exit; + } + + retval = touch_check_input_params(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to check input parameters\n"); + goto exit; + } else if (retval == 0) { + LOGN(tcm_hcd->pdev->dev.parent, + "Input parameters unchanged\n"); + goto exit; + } + + if (touch_hcd->input_dev != NULL) { + input_unregister_device(touch_hcd->input_dev); + touch_hcd->input_dev = NULL; + } + + retval = touch_set_input_dev(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up input device\n"); + goto exit; + } + +exit: + mutex_unlock(&touch_hcd->report_mutex); + + touch_hcd->init_touch_ok = retval < 0 ? false : true; + + return retval; +} + + +int touch_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + touch_hcd = kzalloc(sizeof(*touch_hcd), GFP_KERNEL); + if (!touch_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for touch_hcd\n"); + return -ENOMEM; + } + + touch_hcd->tcm_hcd = tcm_hcd; + + PM_WAKEUP_REGISTER(&tcm_hcd->pdev->dev, touch_hcd->gesture_wakelock, + "syna_gesture_wakelock"); + if (!touch_hcd->gesture_wakelock) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate wakeup source\n"); + kfree(touch_hcd); + touch_hcd = NULL; + return -ENOMEM; + } + + mutex_init(&touch_hcd->report_mutex); + + INIT_BUFFER(touch_hcd->out, false); + INIT_BUFFER(touch_hcd->resp, false); + + retval = touch_set_input_reporting(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up input reporting\n"); + goto err_set_input_reporting; + } + + tcm_hcd->report_touch = touch_report; + + return 0; + +err_set_input_reporting: + kfree(touch_hcd->touch_data.object_data); + kfree(touch_hcd->prev_status); + + RELEASE_BUFFER(touch_hcd->resp); + RELEASE_BUFFER(touch_hcd->out); + + PM_WAKEUP_UNREGISTER(touch_hcd->gesture_wakelock); + kfree(touch_hcd); + touch_hcd = NULL; + + return retval; +} + +int touch_remove(struct syna_tcm_hcd *tcm_hcd) +{ + if (!touch_hcd) + goto exit; + + tcm_hcd->report_touch = NULL; + + if (touch_hcd->input_dev) + input_unregister_device(touch_hcd->input_dev); + + kfree(touch_hcd->touch_data.object_data); + kfree(touch_hcd->prev_status); + + RELEASE_BUFFER(touch_hcd->resp); + RELEASE_BUFFER(touch_hcd->out); + + PM_WAKEUP_UNREGISTER(touch_hcd->gesture_wakelock); + kfree(touch_hcd); + touch_hcd = NULL; + +exit: + + return 0; +} + +int touch_reinit(struct syna_tcm_hcd *tcm_hcd) +{ + int retval = 0; + + if (!touch_hcd) { + retval = touch_init(tcm_hcd); + return retval; + } + + touch_free_objects(); + + if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application mode is not running (firmware mode = %d)\n", + tcm_hcd->id_info.mode); + return 0; + } + + retval = tcm_hcd->identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + return retval; + } + + retval = touch_set_input_reporting(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up input reporting\n"); + } + + return retval; +} + +int touch_early_suspend(struct syna_tcm_hcd *tcm_hcd) +{ + if (!touch_hcd) + return 0; + + if (tcm_hcd->wakeup_gesture_enabled) + touch_hcd->suspend_touch = false; + else + touch_hcd->suspend_touch = true; + + return 0; +} + +int touch_suspend(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!touch_hcd) + return 0; + + touch_hcd->suspend_touch = true; + + if (tcm_hcd->wakeup_gesture_enabled) { + if (!touch_hcd->irq_wake) { + enable_irq_wake(tcm_hcd->irq); + touch_hcd->irq_wake = true; + } + + touch_hcd->suspend_touch = false; + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_IN_WAKEUP_GESTURE_MODE, + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable wakeup gesture mode\n"); + return retval; + } + } + + return 0; +} + +int touch_resume(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!touch_hcd) + return 0; + + touch_hcd->suspend_touch = false; + + if (tcm_hcd->wakeup_gesture_enabled) { + if (touch_hcd->irq_wake) { + disable_irq_wake(tcm_hcd->irq); + touch_hcd->irq_wake = false; + } + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_IN_WAKEUP_GESTURE_MODE, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to disable wakeup gesture mode\n"); + return retval; + } + } + + return 0; +} + + + + diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/synaptics_ts_mmi.c b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/synaptics_ts_mmi.c new file mode 100644 index 000000000000..da061c53e6cd --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_core/synaptics_ts_mmi.c @@ -0,0 +1,767 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include "synaptics_core.h" + +#define GET_SYNA_DATA(dev) { \ + pdev = dev_get_drvdata(dev); \ + if (!pdev) { \ + LOGE(dev, "Failed to get platform device");\ + return -ENODEV; \ + } \ + tcm_hcd = platform_get_drvdata(pdev); \ + if (!tcm_hcd) { \ + LOGE(dev, "Failed to get driver data"); \ + return -ENODEV; \ + } \ +} + +struct pill_region_data { + unsigned short y_start_l; + unsigned short y_end_l; + unsigned short y_start_r; + unsigned short y_end_r; +}; + +static int syna_ts_mmi_methods_hold_distance(struct device *dev, int value) +{ + int retval; + unsigned short buffer; + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + + mutex_lock(&tcm_hcd->extif_mutex); + buffer = (unsigned short)value; + dev_dbg(dev, "%s: program value 0x%02x\n", __func__, (unsigned int)value); + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_HOLD_DISTANCE, + buffer); + + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write hold distance (%d)\n", retval); + goto exit; + } + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + return retval; +} + +static int syna_ts_mmi_methods_get_hold_distance(struct device *dev, void *idata) +{ + int retval; + unsigned short value; + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + + mutex_lock(&tcm_hcd->extif_mutex); + retval = tcm_hcd->get_dynamic_config(tcm_hcd, DC_HOLD_DISTANCE, &value); + if (retval < 0) + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read hold distance info (%d)\n", retval); + else + TO_INT(idata) = value; + + dev_dbg(dev, "%s: program value 0x%02x\n", __func__, (unsigned int)value); + mutex_unlock(&tcm_hcd->extif_mutex); + return retval; +} + +static int syna_ts_mmi_methods_suppression(struct device *dev, int value) +{ + int retval; + unsigned short buffer; + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + + mutex_lock(&tcm_hcd->extif_mutex); + buffer = (unsigned short)value; + dev_dbg(dev, "%s: program value 0x%02x\n", __func__, (unsigned int)value); + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_GRIP_SUPPRESSION_INFO, + buffer); + + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write suppression info (%d)\n", retval); + goto exit; + } + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + return retval; +} + +static int syna_ts_mmi_methods_get_suppression(struct device *dev, void *idata) +{ + int retval; + unsigned short value; + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + + mutex_lock(&tcm_hcd->extif_mutex); + retval = tcm_hcd->get_dynamic_config(tcm_hcd, DC_GRIP_SUPPRESSION_INFO, &value); + if (retval < 0) + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read suppression info (%d)\n", retval); + else + TO_INT(idata) = value; + + dev_dbg(dev, "%s: program value 0x%02x\n", __func__, (unsigned int)value); + mutex_unlock(&tcm_hcd->extif_mutex); + return retval; +} + +static int syna_ts_mmi_methods_gs_distance(struct device *dev, int value) +{ + int retval; + unsigned short buffer; + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + + mutex_lock(&tcm_hcd->extif_mutex); + buffer = (unsigned short)value; + dev_dbg(dev, "%s: program value 0x%02x\n", __func__, (unsigned int)value); + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_SUPP_X_WIDTH, + buffer); + + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable wakeup gesture mode\n"); + goto exit; + } + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + return retval; +} + +static int syna_ts_mmi_methods_get_gs_distance(struct device *dev, void *idata) +{ + int retval; + unsigned short value; + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + + mutex_lock(&tcm_hcd->extif_mutex); + retval = tcm_hcd->get_dynamic_config(tcm_hcd, DC_SUPP_X_WIDTH, &value); + if (retval < 0) + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read gs_distance (%d)\n", retval); + else + TO_INT(idata) = value; + + dev_dbg(dev, "%s: program value 0x%02x\n", __func__, (unsigned int)value); + mutex_unlock(&tcm_hcd->extif_mutex); + return retval; +} + +static int syna_ts_get_pill_region(struct syna_tcm_hcd *tcm_hcd, struct pill_region_data *region_data) +{ + int retval; + + retval = tcm_hcd->get_dynamic_config(tcm_hcd, + DC_SUPP_Y_START_L, ®ion_data->y_start_l); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read LEFT_Y_START (%d)\n", retval); + return retval; + } + + retval = tcm_hcd->get_dynamic_config(tcm_hcd, + DC_SUPP_Y_END_L, ®ion_data->y_end_l); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read LEFT_Y_END (%d)\n", retval); + return retval; + } + + retval = tcm_hcd->get_dynamic_config(tcm_hcd, + DC_SUPP_Y_START_R, ®ion_data->y_start_r); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read RIGHT_Y_START (%d)\n", retval); + return retval; + } + + retval = tcm_hcd->get_dynamic_config(tcm_hcd, + DC_SUPP_Y_END_R, ®ion_data->y_end_r); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read RIGHT_Y_END (%d)\n", retval); + return retval; + } + + return retval; +} + +static int syna_ts_set_pill_region(struct syna_tcm_hcd *tcm_hcd, struct pill_region_data region_data) +{ + int retval; + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_SUPP_Y_START_L, region_data.y_start_l); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set LEFT_Y_START of pill_region\n"); + return retval; + } + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_SUPP_Y_END_L, region_data.y_end_l); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set LEFT_Y_END of pill_region\n"); + return retval; + } + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_SUPP_Y_START_R, region_data.y_start_r); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set RIGHT_Y_START of pill_region\n"); + return retval; + } + + retval = tcm_hcd->set_dynamic_config(tcm_hcd, + DC_SUPP_Y_END_R, region_data.y_end_r); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set RIGHT_Y_END of pill_region\n"); + return retval; + } + + return retval; +} + +static int syna_ts_mmi_methods_pill_region(struct device *dev, int *value) +{ + int retval; + struct pill_region_data region_data; + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + + dev_dbg(dev, "%s: program pill region:0x%02x 0x%04x 0x%04x\n", + __func__, value[0], value[1], value[2]); + + switch (value[0]) { + case 0: + region_data.y_start_l = 0x00; + region_data.y_end_l = 0x00; + region_data.y_start_r = 0x00; + region_data.y_end_r = 0x00; + break; + + case 1: + region_data.y_start_l = (unsigned short)value[1]; + region_data.y_end_l = (unsigned short)value[2]; + region_data.y_start_r = 0x00; + region_data.y_end_r = 0x00; + break; + + case 2: + region_data.y_start_l = 0x00; + region_data.y_end_l = 0x00; + region_data.y_start_r = (unsigned short)value[1]; + region_data.y_end_r = (unsigned short)value[2]; + break; + + default: + LOGE(dev, "The commond is not support!\n"); + return -EINVAL; + } + mutex_lock(&tcm_hcd->extif_mutex); + + retval = syna_ts_set_pill_region(tcm_hcd, region_data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set pill_region none\n"); + goto exit; + } + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + return retval; +} + +static int syna_ts_mmi_methods_get_pill_region(struct device *dev, void *idata) +{ + int retval; + struct pill_region_data region_data; + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = syna_ts_get_pill_region(tcm_hcd, ®ion_data); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get pill_region value (%d)\n", retval); + goto exit; + } + + if (region_data.y_start_l == 0 && region_data.y_end_l == 0) { + if (region_data.y_start_r == 0 && region_data.y_end_r == 0) + ((int *)idata)[0] = 0x00; + else + ((int *)idata)[0] = 0x02; + + ((int *)idata)[1] = region_data.y_start_r; + ((int *)idata)[2] = region_data.y_end_r; + + } else { + ((int *)idata)[0] = 0x01; + ((int *)idata)[1] = region_data.y_start_l; + ((int *)idata)[2] = region_data.y_end_l; + } + dev_dbg(dev, "%s: program pill region show %02x, %x, %x",__func__,((int *)idata)[0], ((int *)idata)[1], ((int *)idata)[2]); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + return retval; +} + +static int syna_ts_mmi_methods_get_vendor(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", "synaptics"); +} + +static int syna_ts_mmi_methods_get_productinfo(struct device *dev, void *cdata) +{ + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + return scnprintf(TO_CHARP(cdata), 6, "%s", tcm_hcd->id_info.part_number); +} + +static int syna_ts_mmi_methods_get_build_id(struct device *dev, void *cdata) +{ + unsigned int buildid; + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + buildid = tcm_hcd->packrat_number; + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%06x", buildid); +} + +/*return firmware version*/ +static int syna_ts_mmi_methods_get_config_id(struct device *dev, void *cdata) +{ + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%s", tcm_hcd->app_info.customer_config_id); +} + +static int syna_ts_mmi_methods_get_bus_type(struct device *dev, void *idata) +{ + const struct syna_tcm_hw_interface *hw_if; + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + hw_if = tcm_hcd->hw_if; + if (!hw_if) { + LOGE(dev, "Hardware interface not found\n"); + return -ENODEV; + } + TO_INT(idata) = hw_if->bus_io->type; + return 0; +} + +static int syna_ts_mmi_methods_get_irq_status(struct device *dev, void *idata) +{ + const struct syna_tcm_hw_interface *hw_if; + struct syna_tcm_board_data *bdata; + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + hw_if = tcm_hcd->hw_if; + if (!hw_if) { + LOGE(dev, "Hardware interface not found\n"); + return -ENODEV; + } + bdata = hw_if->bdata; + if (!bdata) { + LOGE(dev, "Failed to get ts board data"); + return -ENODEV; + } + TO_INT(idata) = gpio_get_value(bdata->irq_gpio); + return 0; +} + +static int syna_ts_mmi_methods_get_drv_irq(struct device *dev, void *idata) +{ + struct syna_tcm_board_data *bdata; + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + bdata = tcm_hcd->hw_if->bdata; + if (!bdata) { + LOGE(dev, "Failed to get ts board data"); + return -ENODEV; + } + TO_INT(idata) = tcm_hcd->irq_enabled ? 1 : 0; + return 0; +} + +static int syna_ts_mmi_methods_get_poweron(struct device *dev, void *idata) +{ + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + TO_INT(idata) = tcm_hcd->power_status == 0 ? 1 : 0; + return 0; +} + +static int syna_ts_mmi_methods_get_flashprog(struct device *dev, void *idata) +{ + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode) || + tcm_hcd->app_status != APP_STATUS_OK) + TO_INT(idata) = 1; + else + TO_INT(idata) = 0; + + return 0; +} + +static int syna_ts_mmi_methods_drv_irq(struct device *dev, int state) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + mutex_lock(&tcm_hcd->extif_mutex); + if (state == 0) { + retval = tcm_hcd->enable_irq(tcm_hcd, false, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to disable interrupt\n"); + goto exit; + } + } else if (state == 1) { + retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable interrupt\n"); + goto exit; + } + } else { + retval = -EINVAL; + goto exit; + } + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + return retval; +} + +static int syna_ts_mmi_methods_reset(struct device *dev, int type) +{ + bool hw_reset = !!type; + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + if (tcm_hcd->reset) + return tcm_hcd->reset_n_reinit(tcm_hcd, hw_reset, true); + return -EFAULT; +} + +static int syna_ts_firmware_update(struct device *dev, char *fwname) { + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + LOGI(dev, "HW request update fw"); + /* set firmware image name */ + if (tcm_hcd->set_fw_name) + tcm_hcd->set_fw_name(fwname); + else { + LOGE(dev, "fw update handler not ready, can't set fw_name"); + return -EFAULT; + } + if (tcm_hcd->do_fw_update) { + tcm_hcd->do_fw_update(); + } else { + LOGE(dev, "fw update handler not ready."); + return -EFAULT; + } + return 0; +} + +static int syna_ts_mmi_methods_power(struct device *dev, int on) +{ + int retval; + const struct syna_tcm_board_data *bdata; + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + bdata = tcm_hcd->hw_if->bdata; + if (!bdata) { + LOGE(dev, "Failed to get ts board data"); + return -ENODEV; + } + if (!on) { + retval = 0; + goto disable_pwr_reg; + } + + if (tcm_hcd->bus_reg) { + retval = regulator_enable(tcm_hcd->bus_reg); + if (retval < 0) { + LOGE(dev, "Failed to enable bus regulator\n"); + goto exit; + } + } + + if (tcm_hcd->pwr_reg) { + retval = regulator_enable(tcm_hcd->pwr_reg); + if (retval < 0) { + LOGE(dev, "Failed to enable power regulator\n"); + goto disable_bus_reg; + } + msleep(bdata->power_delay_ms); + } + tcm_hcd->power_status = true; + return 0; + +disable_pwr_reg: + if (tcm_hcd->pwr_reg) + regulator_disable(tcm_hcd->pwr_reg); +disable_bus_reg: + if (tcm_hcd->bus_reg) + regulator_disable(tcm_hcd->bus_reg); +exit: + tcm_hcd->power_status = false; + return retval; +} + +static int syna_ts_mmi_wait_for_ready(struct device *dev) +{ + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + + msleep(RESET_ON_RESUME_DELAY_MS); + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, true); +#endif + + return 0; +} + +static int syna_ts_mmi_post_resume(struct device *dev) +{ + struct platform_device *pdev; + pdev = dev_get_drvdata(dev); + + if (!pdev) { + LOGE(dev, "Failed to get platform device"); + return -ENODEV; + } + + return syna_tcm_resume(&pdev->dev); +} + +static int syna_ts_mmi_panel_state(struct device *dev, + enum ts_mmi_pm_mode from, enum ts_mmi_pm_mode to) +{ + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + + switch (to) { + case TS_MMI_PM_DEEPSLEEP: + tcm_hcd->wakeup_gesture_enabled = false; + syna_tcm_early_suspend(&pdev->dev); + syna_tcm_suspend(&pdev->dev); + break; + + case TS_MMI_PM_GESTURE: + tcm_hcd->wakeup_gesture_enabled = true; + syna_tcm_early_suspend(&pdev->dev); + syna_tcm_suspend(&pdev->dev); + break; + + case TS_MMI_PM_ACTIVE: + break; + + default: + dev_warn(dev, "panel mode %d is invalid.\n", to); + return -EINVAL; + } + return 0; +} + +static int syna_ts_mmi_charger_mode(struct device *dev, int mode) +{ + int retval; + unsigned short cval = 0; + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + + GET_SYNA_DATA(dev); + + if (tcm_hcd->irq_enabled == false) { + dev_info(dev, "%s, Interrupt is closed, so cannot access CHARGER_CONNECTED\n", + __func__); + return -EINVAL; + } + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = tcm_hcd->get_dynamic_config(tcm_hcd, DC_CHARGER_CONNECTED, &cval); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get charger_connected mode\n"); + goto exit; + } + + dev_dbg(dev, "%s: Set charger mode:%d, charger current mode:%d\n", + __func__, mode, cval); + + if (cval != mode) { + retval = tcm_hcd->set_dynamic_config(tcm_hcd, DC_CHARGER_CONNECTED, mode); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set charger_connected mode\n"); + goto exit; + } + } else + dev_info(dev, "%s: charger mode already %d\n", __func__, cval); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + return 0; +} + +static int syna_ts_mmi_update_baseline(struct device *dev, int mode) +{ + struct syna_tcm_hcd *tcm_hcd; + struct platform_device *pdev; + int retval; + + GET_SYNA_DATA(dev); + + mutex_lock(&tcm_hcd->extif_mutex); + + if (mode && tcm_hcd->delay_baseline_update) { + if (atomic_read(&tcm_hcd->helper.task) == HELP_NONE) { + dev_info(dev, "%s: Start to update baseline\n", __func__); + atomic_set(&tcm_hcd->helper.task, + HELP_SEND_REZERO_COMMAND); + queue_work(tcm_hcd->helper.workqueue, + &tcm_hcd->helper.work); + } else { + retval = -EINVAL; + goto exit; + } + } + retval = 0; + tcm_hcd->delay_baseline_update = !mode; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + return retval; +} + +static struct ts_mmi_methods syna_ts_mmi_methods = { + .get_vendor = syna_ts_mmi_methods_get_vendor, + .get_productinfo = syna_ts_mmi_methods_get_productinfo, + .get_build_id = syna_ts_mmi_methods_get_build_id, + .get_config_id = syna_ts_mmi_methods_get_config_id, + .get_bus_type = syna_ts_mmi_methods_get_bus_type, + .get_irq_status = syna_ts_mmi_methods_get_irq_status, + .get_drv_irq = syna_ts_mmi_methods_get_drv_irq, + .get_flashprog = syna_ts_mmi_methods_get_flashprog, + .get_poweron = syna_ts_mmi_methods_get_poweron, + .get_suppression = syna_ts_mmi_methods_get_suppression, + .get_gs_distance = syna_ts_mmi_methods_get_gs_distance, + .get_pill_region = syna_ts_mmi_methods_get_pill_region, + .get_hold_distance = syna_ts_mmi_methods_get_hold_distance, + /* SET methods */ + .reset = syna_ts_mmi_methods_reset, + .drv_irq = syna_ts_mmi_methods_drv_irq, + .power = syna_ts_mmi_methods_power, + .charger_mode = syna_ts_mmi_charger_mode, + .suppression = syna_ts_mmi_methods_suppression, + .gs_distance = syna_ts_mmi_methods_gs_distance, + .pill_region = syna_ts_mmi_methods_pill_region, + .update_baseline = syna_ts_mmi_update_baseline, + .hold_distance = syna_ts_mmi_methods_hold_distance, + /* Firmware */ + .firmware_update = syna_ts_firmware_update, + /* PM callback */ + .wait_for_ready = syna_ts_mmi_wait_for_ready, + .panel_state = syna_ts_mmi_panel_state, + .post_resume = syna_ts_mmi_post_resume, +}; + +int syna_ts_mmi_dev_register(struct syna_tcm_hcd *tcm_hcd) { + int ret; + + ret = ts_mmi_dev_register(tcm_hcd->pdev->dev.parent, &syna_ts_mmi_methods); + if (ret) { + dev_err(tcm_hcd->pdev->dev.parent, "Failed to register ts mmi\n"); + return ret; + } + + /* initialize class imported methods */ + tcm_hcd->imports = &syna_ts_mmi_methods.exports; + + return 0; +} + +void syna_ts_mmi_dev_unregister(struct syna_tcm_hcd *tcm_hcd) { + ts_mmi_dev_unregister(tcm_hcd->pdev->dev.parent); +} diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/Android.mk b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/Android.mk new file mode 100644 index 000000000000..efb851d1b811 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/Android.mk @@ -0,0 +1,64 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KERNEL_CFLAGS += CONFIG_INPUT_TOUCHSCREEN_MMI=y +endif +ifeq ($(DRM_PANEL_NOTIFICATIONS),true) + KERNEL_CFLAGS += CONFIG_DRM_PANEL_NOTIFICATIONS=y +endif + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +SYNA_TCM_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +SYNA_TCM_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_device.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SYNA_TCM_MODULE_PATH) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/synaptics_core_module.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_diagnostics.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SYNA_TCM_MODULE_PATH) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/synaptics_core_module.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_recovery.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SYNA_TCM_MODULE_PATH) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/synaptics_core_module.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_reflash.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SYNA_TCM_MODULE_PATH) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/synaptics_core_module.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_testing.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SYNA_TCM_MODULE_PATH) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/synaptics_core_module.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := synaptics_zeroflash.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(SYNA_TCM_MODULE_PATH) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/synaptics_core_module.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/Kbuild b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/Kbuild new file mode 100644 index 000000000000..a061f18d34a0 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/Kbuild @@ -0,0 +1,19 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/input/touchscreen/synaptics_tcm_mmi_v2 + +obj-m += synaptics_device.o +obj-m += synaptics_diagnostics.o +obj-m += synaptics_recovery.o +obj-m += synaptics_reflash.o +obj-m += synaptics_testing.o +obj-m += synaptics_zeroflash.o + +ifneq ($(filter m y, $(CONFIG_DRM_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_NOTIFICATIONS +endif +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI +endif +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../synaptics_core/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_device.c b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_device.c new file mode 100644 index 000000000000..e28b381ab95b --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_device.c @@ -0,0 +1,721 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * Copyright (C) 2018-2019 Ian Su + * Copyright (C) 2018-2019 Joey Zhou + * Copyright (C) 2018-2019 Yuehao Qiu + * Copyright (C) 2018-2019 Aaron Chen + * + * 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. + */ + +#include +#include +#include +#include "../synaptics_core/synaptics_core.h" + +#define CHAR_DEVICE_NAME "tcm" + +#define CONCURRENT true + +#define DEVICE_IOC_MAGIC 's' +#define DEVICE_IOC_RESET _IO(DEVICE_IOC_MAGIC, 0) /* 0x00007300 */ +#define DEVICE_IOC_IRQ _IOW(DEVICE_IOC_MAGIC, 1, int) /* 0x40047301 */ +#define DEVICE_IOC_RAW _IOW(DEVICE_IOC_MAGIC, 2, int) /* 0x40047302 */ +#define DEVICE_IOC_CONCURRENT _IOW(DEVICE_IOC_MAGIC, 3, int) /* 0x40047303 */ + +struct device_hcd { + dev_t dev_num; + bool raw_mode; + bool concurrent; + unsigned int ref_count; + struct cdev char_dev; + struct class *class; + struct device *device; + struct syna_tcm_buffer out; + struct syna_tcm_buffer resp; + struct syna_tcm_buffer report; + struct syna_tcm_hcd *tcm_hcd; +}; + +DECLARE_COMPLETION(device_remove_complete); + +static struct device_hcd *device_hcd; + +static int rmidev_major_num; + +static void device_capture_touch_report(unsigned int count) +{ + int retval; + unsigned char id; + unsigned int idx; + unsigned int size; + unsigned char *data; + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + static bool report; + static unsigned int offset; + static unsigned int remaining_size; + + if (count < 2) + return; + + data = &device_hcd->resp.buf[0]; + + if (data[0] != MESSAGE_MARKER) + return; + + id = data[1]; + + size = 0; + + LOCK_BUFFER(device_hcd->report); + + switch (id) { + case REPORT_TOUCH: + if (count >= 4) { + remaining_size = le2_to_uint(&data[2]); + } else { + report = false; + goto exit; + } + retval = syna_tcm_alloc_mem(tcm_hcd, + &device_hcd->report, + remaining_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for device_hcd->report.buf\n"); + report = false; + goto exit; + } + idx = 4; + size = count - idx; + offset = 0; + report = true; + break; + case STATUS_CONTINUED_READ: + if (report == false) + goto exit; + if (count >= 2) { + idx = 2; + size = count - idx; + } + break; + default: + goto exit; + } + + if (size) { + size = MIN(size, remaining_size); + retval = secure_memcpy(&device_hcd->report.buf[offset], + device_hcd->report.buf_size - offset, + &data[idx], + count - idx, + size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy touch report data\n"); + report = false; + goto exit; + } else { + offset += size; + remaining_size -= size; + device_hcd->report.data_length += size; + } + } + + if (remaining_size) + goto exit; + + LOCK_BUFFER(tcm_hcd->report.buffer); + + tcm_hcd->report.buffer.buf = device_hcd->report.buf; + tcm_hcd->report.buffer.buf_size = device_hcd->report.buf_size; + tcm_hcd->report.buffer.data_length = device_hcd->report.data_length; + + tcm_hcd->report_touch(); + + UNLOCK_BUFFER(tcm_hcd->report.buffer); + + report = false; + +exit: + UNLOCK_BUFFER(device_hcd->report); + + return; +} + +static int device_capture_touch_report_config(unsigned int count) +{ + int retval; + unsigned int size; + unsigned int buf_size; + unsigned char *data; + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + if (device_hcd->raw_mode) { + if (count < 3) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid write data\n"); + return -EINVAL; + } + + size = le2_to_uint(&device_hcd->out.buf[1]); + + if (count - 3 < size) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incomplete write data\n"); + return -EINVAL; + } + + if (!size) + return 0; + + data = &device_hcd->out.buf[3]; + buf_size = device_hcd->out.buf_size - 3; + } else { + size = count - 1; + + if (!size) + return 0; + + data = &device_hcd->out.buf[1]; + buf_size = device_hcd->out.buf_size - 1; + } + + LOCK_BUFFER(tcm_hcd->config); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &tcm_hcd->config, + size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for tcm_hcd->config.buf\n"); + UNLOCK_BUFFER(tcm_hcd->config); + return retval; + } + + retval = secure_memcpy(tcm_hcd->config.buf, + tcm_hcd->config.buf_size, + data, + buf_size, + size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy touch report config data\n"); + UNLOCK_BUFFER(tcm_hcd->config); + return retval; + } + + tcm_hcd->config.data_length = size; + + UNLOCK_BUFFER(tcm_hcd->config); + + return 0; +} + +#ifdef HAVE_UNLOCKED_IOCTL +static long device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +#else +static int device_ioctl(struct inode *inp, struct file *filp, unsigned int cmd, + unsigned long arg) +#endif +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = 0; + + switch (cmd) { + case DEVICE_IOC_RESET: + retval = tcm_hcd->reset_n_reinit(tcm_hcd, false, true); + break; + case DEVICE_IOC_IRQ: + if (arg == 0) + retval = tcm_hcd->enable_irq(tcm_hcd, false, false); + else if (arg == 1) + retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL); + break; + case DEVICE_IOC_RAW: + if (arg == 0) { + device_hcd->raw_mode = false; +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, true); +#endif + } else if (arg == 1) { + device_hcd->raw_mode = true; +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, false); +#endif + } + break; + case DEVICE_IOC_CONCURRENT: + if (arg == 0) + device_hcd->concurrent = false; + else if (arg == 1) + device_hcd->concurrent = true; + break; + default: + retval = -ENOTTY; + break; + } + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static loff_t device_llseek(struct file *filp, loff_t off, int whence) +{ + return -EINVAL; +} + +static ssize_t device_read(struct file *filp, char __user *buf, + size_t count, loff_t *f_pos) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + if (count == 0) + return 0; + + mutex_lock(&tcm_hcd->extif_mutex); + + LOCK_BUFFER(device_hcd->resp); + + if (device_hcd->raw_mode) { + retval = syna_tcm_alloc_mem(tcm_hcd, + &device_hcd->resp, + count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for device_hcd->resp.buf\n"); + UNLOCK_BUFFER(device_hcd->resp); + goto exit; + } + + retval = tcm_hcd->read_message(tcm_hcd, + device_hcd->resp.buf, + count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read message\n"); + UNLOCK_BUFFER(device_hcd->resp); + goto exit; + } + } else { + if (count != device_hcd->resp.data_length) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid length information\n"); + UNLOCK_BUFFER(device_hcd->resp); + retval = -EINVAL; + goto exit; + } + } + + if (copy_to_user(buf, device_hcd->resp.buf, count)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy data to user space\n"); + UNLOCK_BUFFER(device_hcd->resp); + retval = -EINVAL; + goto exit; + } + + if (!device_hcd->concurrent) + goto skip_concurrent; + + if (tcm_hcd->report_touch == NULL) { + LOGE(tcm_hcd->pdev->dev.parent, + "Unable to report touch\n"); + device_hcd->concurrent = false; + } + + if (device_hcd->raw_mode) + device_capture_touch_report(count); + +skip_concurrent: + UNLOCK_BUFFER(device_hcd->resp); + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t device_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + if (count == 0) + return 0; + + mutex_lock(&tcm_hcd->extif_mutex); + + LOCK_BUFFER(device_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &device_hcd->out, + count == 1 ? count + 1 : count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for device_hcd->out.buf\n"); + UNLOCK_BUFFER(device_hcd->out); + goto exit; + } + + if (copy_from_user(device_hcd->out.buf, buf, count)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy data from user space\n"); + UNLOCK_BUFFER(device_hcd->out); + retval = -EINVAL; + goto exit; + } + + LOCK_BUFFER(device_hcd->resp); + + if (device_hcd->raw_mode) { + retval = tcm_hcd->write_message(tcm_hcd, + device_hcd->out.buf[0], + &device_hcd->out.buf[1], + count - 1, + NULL, + NULL, + NULL, + NULL, + 0); + } else { + mutex_lock(&tcm_hcd->reset_mutex); + retval = tcm_hcd->write_message(tcm_hcd, + device_hcd->out.buf[0], + &device_hcd->out.buf[1], + count - 1, + &device_hcd->resp.buf, + &device_hcd->resp.buf_size, + &device_hcd->resp.data_length, + NULL, + 0); + mutex_unlock(&tcm_hcd->reset_mutex); + } + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command 0x%02x\n", + device_hcd->out.buf[0]); + UNLOCK_BUFFER(device_hcd->resp); + UNLOCK_BUFFER(device_hcd->out); + goto exit; + } + + if (count && device_hcd->out.buf[0] == CMD_SET_TOUCH_REPORT_CONFIG) { + retval = device_capture_touch_report_config(count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to capture touch report config\n"); + } + } + + UNLOCK_BUFFER(device_hcd->out); + + if (device_hcd->raw_mode) + retval = count; + else + retval = device_hcd->resp.data_length; + + UNLOCK_BUFFER(device_hcd->resp); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static int device_open(struct inode *inp, struct file *filp) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (device_hcd->ref_count < 1) { + device_hcd->ref_count++; + retval = 0; + } else { + retval = -EACCES; + } + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static int device_release(struct inode *inp, struct file *filp) +{ + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (device_hcd->ref_count) + device_hcd->ref_count--; + + mutex_unlock(&tcm_hcd->extif_mutex); + + return 0; +} + +static char *device_devnode(struct device *dev, umode_t *mode) +{ + if (!mode) + return NULL; + + *mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + + return kasprintf(GFP_KERNEL, "%s/%s", PLATFORM_DRIVER_NAME, + dev_name(dev)); +} + +static int device_create_class(void) +{ + struct syna_tcm_hcd *tcm_hcd = device_hcd->tcm_hcd; + + if (device_hcd->class != NULL) + return 0; + + device_hcd->class = class_create(THIS_MODULE, PLATFORM_DRIVER_NAME); + + if (IS_ERR(device_hcd->class)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create class\n"); + return -ENODEV; + } + + device_hcd->class->devnode = device_devnode; + + return 0; +} + +static const struct file_operations device_fops = { + .owner = THIS_MODULE, +#ifdef HAVE_UNLOCKED_IOCTL + .unlocked_ioctl = device_ioctl, +#ifdef HAVE_COMPAT_IOCTL + .compat_ioctl = device_ioctl, +#endif +#else + .ioctl = device_ioctl, +#endif + .llseek = device_llseek, + .read = device_read, + .write = device_write, + .open = device_open, + .release = device_release, +}; + +static int device_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + dev_t dev_num; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + device_hcd = kzalloc(sizeof(*device_hcd), GFP_KERNEL); + if (!device_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for device_hcd\n"); + return -ENOMEM; + } + + device_hcd->tcm_hcd = tcm_hcd; + + device_hcd->concurrent = CONCURRENT; + + INIT_BUFFER(device_hcd->out, false); + INIT_BUFFER(device_hcd->resp, false); + INIT_BUFFER(device_hcd->report, false); + + if (rmidev_major_num) { + dev_num = MKDEV(rmidev_major_num, 0); + retval = register_chrdev_region(dev_num, 1, + PLATFORM_DRIVER_NAME); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to register char device\n"); + goto err_register_chrdev_region; + } + } else { + retval = alloc_chrdev_region(&dev_num, 0, 1, + PLATFORM_DRIVER_NAME); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate char device\n"); + goto err_alloc_chrdev_region; + } + + rmidev_major_num = MAJOR(dev_num); + } + + device_hcd->dev_num = dev_num; + + cdev_init(&device_hcd->char_dev, &device_fops); + + retval = cdev_add(&device_hcd->char_dev, dev_num, 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add char device\n"); + goto err_add_chardev; + } + + retval = device_create_class(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create class\n"); + goto err_create_class; + } + + device_hcd->device = device_create(device_hcd->class, NULL, + device_hcd->dev_num, NULL, CHAR_DEVICE_NAME"%d", + MINOR(device_hcd->dev_num)); + if (IS_ERR(device_hcd->device)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create device\n"); + retval = -ENODEV; + goto err_create_device; + } + + if (bdata->irq_gpio >= 0) { + retval = gpio_export(bdata->irq_gpio, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to export GPIO\n"); + } else { + retval = gpio_export_link(&tcm_hcd->pdev->dev, + "attn", bdata->irq_gpio); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to export GPIO link\n"); + } + } + } + + return 0; + +err_create_device: + class_destroy(device_hcd->class); + +err_create_class: + cdev_del(&device_hcd->char_dev); + +err_add_chardev: + unregister_chrdev_region(dev_num, 1); + +err_alloc_chrdev_region: +err_register_chrdev_region: + RELEASE_BUFFER(device_hcd->report); + RELEASE_BUFFER(device_hcd->resp); + RELEASE_BUFFER(device_hcd->out); + + kfree(device_hcd); + device_hcd = NULL; + + return retval; +} + +static int device_remove(struct syna_tcm_hcd *tcm_hcd) +{ + if (!device_hcd) + goto exit; + + device_destroy(device_hcd->class, device_hcd->dev_num); + + class_destroy(device_hcd->class); + + cdev_del(&device_hcd->char_dev); + + unregister_chrdev_region(device_hcd->dev_num, 1); + + RELEASE_BUFFER(device_hcd->report); + RELEASE_BUFFER(device_hcd->resp); + RELEASE_BUFFER(device_hcd->out); + + kfree(device_hcd); + device_hcd = NULL; + +exit: + complete(&device_remove_complete); + + return 0; +} + +static int device_reinit(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!device_hcd) { + retval = device_init(tcm_hcd); + return retval; + } + + return 0; +} + +static struct syna_tcm_module_cb device_module = { + .type = TCM_DEVICE, + .init = device_init, + .remove = device_remove, + .syncbox = NULL, +#ifdef REPORT_NOTIFIER + .asyncbox = NULL, +#endif + .reinit = device_reinit, + .suspend = NULL, + .resume = NULL, + .early_suspend = NULL, +}; + +static int __init device_module_init(void) +{ + return syna_tcm_add_module(&device_module, true); +} + +static void __exit device_module_exit(void) +{ + syna_tcm_add_module(&device_module, false); + + wait_for_completion(&device_remove_complete); + + return; +} + +module_init(device_module_init); +module_exit(device_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Device Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_diagnostics.c b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_diagnostics.c new file mode 100644 index 000000000000..160935e13d91 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_diagnostics.c @@ -0,0 +1,575 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * Copyright (C) 2018-2019 Ian Su + * Copyright (C) 2018-2019 Joey Zhou + * Copyright (C) 2018-2019 Yuehao Qiu + * Copyright (C) 2018-2019 Aaron Chen + * + * 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. + */ + +#include "../synaptics_core/synaptics_core.h" +#include +#include + +#define SYSFS_DIR_NAME "diagnostics" + +enum pingpong_state { + PING = 0, + PONG = 1, +}; + +struct diag_hcd { + pid_t pid; + unsigned char report_type; + enum pingpong_state state; + struct kobject *sysfs_dir; + struct SIGINFO sigio; + struct task_struct *task; + struct syna_tcm_buffer ping; + struct syna_tcm_buffer pong; + struct syna_tcm_hcd *tcm_hcd; +}; + +DECLARE_COMPLETION(diag_remove_complete); + +static struct diag_hcd *diag_hcd; + +STORE_PROTOTYPE(diag, pid) +SHOW_PROTOTYPE(diag, size) +STORE_PROTOTYPE(diag, type) +SHOW_PROTOTYPE(diag, rows) +SHOW_PROTOTYPE(diag, cols) +SHOW_PROTOTYPE(diag, hybrid) +SHOW_PROTOTYPE(diag, buttons) + +static struct device_attribute *attrs[] = { + ATTRIFY(pid), + ATTRIFY(size), + ATTRIFY(type), + ATTRIFY(rows), + ATTRIFY(cols), + ATTRIFY(hybrid), + ATTRIFY(buttons), +}; + +static ssize_t diag_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static struct bin_attribute bin_attr = { + .attr = { + .name = "data", + .mode = S_IRUGO, + }, + .size = 0, + .read = diag_sysfs_data_show, +}; + +static ssize_t diag_sysfs_pid_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + diag_hcd->pid = input; + + if (diag_hcd->pid) { + diag_hcd->task = get_pid_task(find_vpid(diag_hcd->pid), + PIDTYPE_PID); + if (!diag_hcd->task) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to locate task\n"); + retval = -EINVAL; + goto exit; + } + } + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t diag_sysfs_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (diag_hcd->state == PING) { + LOCK_BUFFER(diag_hcd->ping); + + retval = snprintf(buf, PAGE_SIZE, + "%u\n", + diag_hcd->ping.data_length); + + UNLOCK_BUFFER(diag_hcd->ping); + } else { + LOCK_BUFFER(diag_hcd->pong); + + retval = snprintf(buf, PAGE_SIZE, + "%u\n", + diag_hcd->pong.data_length); + + UNLOCK_BUFFER(diag_hcd->pong); + } + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t diag_sysfs_type_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int input; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + diag_hcd->report_type = (unsigned char)input; + + mutex_unlock(&tcm_hcd->extif_mutex); + + return count; +} + +static ssize_t diag_sysfs_rows_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned int rows; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode) || + tcm_hcd->app_status != APP_STATUS_OK) { + retval = -ENODEV; + goto exit; + } + + app_info = &tcm_hcd->app_info; + rows = le2_to_uint(app_info->num_of_image_rows); + + retval = snprintf(buf, PAGE_SIZE, "%u\n", rows); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t diag_sysfs_cols_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned int cols; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode) || + tcm_hcd->app_status != APP_STATUS_OK) { + retval = -ENODEV; + goto exit; + } + + app_info = &tcm_hcd->app_info; + cols = le2_to_uint(app_info->num_of_image_cols); + + retval = snprintf(buf, PAGE_SIZE, "%u\n", cols); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t diag_sysfs_hybrid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned int hybrid; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode) || + tcm_hcd->app_status != APP_STATUS_OK) { + retval = -ENODEV; + goto exit; + } + + app_info = &tcm_hcd->app_info; + hybrid = le2_to_uint(app_info->has_hybrid_data); + + retval = snprintf(buf, PAGE_SIZE, "%u\n", hybrid); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t diag_sysfs_buttons_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + unsigned int buttons; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode) || + tcm_hcd->app_status != APP_STATUS_OK) { + retval = -ENODEV; + goto exit; + } + + app_info = &tcm_hcd->app_info; + buttons = le2_to_uint(app_info->num_of_buttons); + + retval = snprintf(buf, PAGE_SIZE, "%u\n", buttons); + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t diag_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int readlen; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = 0; + + if (diag_hcd->state == PING) { + LOCK_BUFFER(diag_hcd->ping); + + if (diag_hcd->ping.data_length == 0) { + readlen = 0; + goto exit; + } + + readlen = MIN(count, diag_hcd->ping.data_length - pos); + + if (diag_hcd->ping.data_length) { + retval = secure_memcpy(buf, + count, + &diag_hcd->ping.buf[pos], + diag_hcd->ping.buf_size - pos, + readlen); + } + + UNLOCK_BUFFER(diag_hcd->ping); + } else { + LOCK_BUFFER(diag_hcd->pong); + + if (diag_hcd->pong.data_length == 0) { + readlen = 0; + goto exit; + } + + readlen = MIN(count, diag_hcd->pong.data_length - pos); + + if (diag_hcd->pong.data_length) { + retval = secure_memcpy(buf, + count, + &diag_hcd->pong.buf[pos], + diag_hcd->pong.buf_size - pos, + readlen); + } + + UNLOCK_BUFFER(diag_hcd->pong); + } + +exit: + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy report data\n"); + } else { + retval = readlen; + } + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static void diag_report(void) +{ + int retval; + static enum pingpong_state state = PING; + struct syna_tcm_hcd *tcm_hcd = diag_hcd->tcm_hcd; + + if (state == PING) { + LOCK_BUFFER(diag_hcd->ping); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &diag_hcd->ping, + tcm_hcd->report.buffer.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for diag_hcd->ping.buf\n"); + UNLOCK_BUFFER(diag_hcd->ping); + return; + } + + retval = secure_memcpy(diag_hcd->ping.buf, + diag_hcd->ping.buf_size, + tcm_hcd->report.buffer.buf, + tcm_hcd->report.buffer.buf_size, + tcm_hcd->report.buffer.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy report data\n"); + UNLOCK_BUFFER(diag_hcd->ping); + return; + } + + diag_hcd->ping.data_length = tcm_hcd->report.buffer.data_length; + + UNLOCK_BUFFER(diag_hcd->ping); + + diag_hcd->state = state; + state = PONG; + } else { + LOCK_BUFFER(diag_hcd->pong); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &diag_hcd->pong, + tcm_hcd->report.buffer.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for diag_hcd->pong.buf\n"); + UNLOCK_BUFFER(diag_hcd->pong); + return; + } + + retval = secure_memcpy(diag_hcd->pong.buf, + diag_hcd->pong.buf_size, + tcm_hcd->report.buffer.buf, + tcm_hcd->report.buffer.buf_size, + tcm_hcd->report.buffer.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy report data\n"); + UNLOCK_BUFFER(diag_hcd->pong); + return; + } + + diag_hcd->pong.data_length = tcm_hcd->report.buffer.data_length; + + UNLOCK_BUFFER(diag_hcd->pong); + + diag_hcd->state = state; + state = PING; + } + + if (diag_hcd->pid) + send_sig_info(SIGIO, &diag_hcd->sigio, diag_hcd->task); + + return; +} + +static int diag_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + int idx; + + diag_hcd = kzalloc(sizeof(*diag_hcd), GFP_KERNEL); + if (!diag_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for diag_hcd\n"); + return -ENOMEM; + } + + diag_hcd->tcm_hcd = tcm_hcd; + diag_hcd->state = PING; + + INIT_BUFFER(diag_hcd->ping, false); + INIT_BUFFER(diag_hcd->pong, false); + + memset(&diag_hcd->sigio, 0x00, sizeof(diag_hcd->sigio)); + diag_hcd->sigio.si_signo = SIGIO; + diag_hcd->sigio.si_code = SI_USER; + + diag_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME, + tcm_hcd->sysfs_dir); + if (!diag_hcd->sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs directory\n"); + retval = -EINVAL; + goto err_sysfs_create_dir; + } + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + retval = sysfs_create_file(diag_hcd->sysfs_dir, + &(*attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs file\n"); + goto err_sysfs_create_file; + } + } + + retval = sysfs_create_bin_file(diag_hcd->sysfs_dir, &bin_attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs bin file\n"); + goto err_sysfs_create_bin_file; + } + + return 0; + +err_sysfs_create_bin_file: +err_sysfs_create_file: + for (idx--; idx >= 0; idx--) + sysfs_remove_file(diag_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(diag_hcd->sysfs_dir); + +err_sysfs_create_dir: + RELEASE_BUFFER(diag_hcd->pong); + RELEASE_BUFFER(diag_hcd->ping); + + kfree(diag_hcd); + diag_hcd = NULL; + + return retval; +} + +static int diag_remove(struct syna_tcm_hcd *tcm_hcd) +{ + int idx; + + if (!diag_hcd) + goto exit; + + sysfs_remove_bin_file(diag_hcd->sysfs_dir, &bin_attr); + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) + sysfs_remove_file(diag_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(diag_hcd->sysfs_dir); + + RELEASE_BUFFER(diag_hcd->pong); + RELEASE_BUFFER(diag_hcd->ping); + + kfree(diag_hcd); + diag_hcd = NULL; + +exit: + complete(&diag_remove_complete); + + return 0; +} + +static int diag_syncbox(struct syna_tcm_hcd *tcm_hcd) +{ + if (!diag_hcd) + return 0; + + if (tcm_hcd->report.id == diag_hcd->report_type) + diag_report(); + + return 0; +} + +static int diag_reinit(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!diag_hcd) { + retval = diag_init(tcm_hcd); + return retval; + } + + return 0; +} + +static struct syna_tcm_module_cb diag_module = { + .type = TCM_DIAGNOSTICS, + .init = diag_init, + .remove = diag_remove, + .syncbox = diag_syncbox, +#ifdef REPORT_NOTIFIER + .asyncbox = NULL, +#endif + .reinit = diag_reinit, + .suspend = NULL, + .resume = NULL, + .early_suspend = NULL, +}; + +static int __init diag_module_init(void) +{ + return syna_tcm_add_module(&diag_module, true); +} + +static void __exit diag_module_exit(void) +{ + syna_tcm_add_module(&diag_module, false); + + wait_for_completion(&diag_remove_complete); + + return; +} + +module_init(diag_module_init); +module_exit(diag_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Diagnostics Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_recovery.c b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_recovery.c new file mode 100644 index 000000000000..3cad33b37958 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_recovery.c @@ -0,0 +1,1326 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * Copyright (C) 2018-2019 Ian Su + * Copyright (C) 2018-2019 Joey Zhou + * Copyright (C) 2018-2019 Yuehao Qiu + * Copyright (C) 2018-2019 Aaron Chen + * + * 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. + */ + +#include "../synaptics_core/synaptics_core.h" + +#define SET_UP_RECOVERY_MODE true + +#define ENABLE_SYSFS_RECOVERY true + +#define SYSFS_DIR_NAME "recovery" + +#define IHEX_BUF_SIZE (2048 * 1024) + +#define DATA_BUF_SIZE (512 * 1024) + +#define IHEX_RECORD_SIZE 14 + +#define PDT_START_ADDR 0x00e9 + +#define UBL_FN_NUMBER 0x35 + +#define F35_CHUNK_SIZE 16 + +#define F35_CHUNK_SIZE_WORDS 8 + +#define F35_ERASE_ALL_WAIT_MS 5000 + +#define F35_ERASE_ALL_POLL_MS 100 + +#define F35_DATA5_OFFSET 5 + +#define F35_CTRL3_OFFSET 18 + +#define F35_RESET_COMMAND 16 + +#define F35_ERASE_ALL_COMMAND 3 + +#define F35_WRITE_CHUNK_COMMAND 2 + +#define F35_READ_FLASH_STATUS_COMMAND 1 + +#define BINARY_FILE_MAGIC_VALUE 0xaa55 + +#define FLASH_PAGE_SIZE 256 + +#define STATUS_CHECK_US_MIN 5000 + +#define STATUS_CHECK_US_MAX 10000 + +#define STATUS_CHECK_RETRY 50 + +#define DATA_ROMBOOT_BUF_SIZE (512 * 256) + +struct block_data { + const unsigned char *data; + unsigned int size; + unsigned int flash_addr; +}; + +struct image_info { + unsigned int packrat_number; + struct block_data boot_config; + struct block_data app_firmware; + struct block_data app_config; + struct block_data disp_config; +}; + +struct rmi_pdt_entry { + unsigned char query_base_addr; + unsigned char command_base_addr; + unsigned char control_base_addr; + unsigned char data_base_addr; + unsigned char intr_src_count:3; + unsigned char reserved_1:2; + unsigned char fn_version:2; + unsigned char reserved_2:1; + unsigned char fn_number; +} __packed; + +struct rmi_addr { + unsigned short query_base; + unsigned short command_base; + unsigned short control_base; + unsigned short data_base; +}; + +struct recovery_hcd { + bool set_up_recovery_mode; + unsigned char chunk_buf[F35_CHUNK_SIZE + 3]; + unsigned char out_buf[3]; + unsigned char *ihex_buf; + unsigned char *data_buf; + unsigned int ihex_size; + unsigned int ihex_records; + unsigned int data_entries; + struct image_info image_info; + struct kobject *sysfs_dir; + struct rmi_addr f35_addr; + struct syna_tcm_hcd *tcm_hcd; +}; + +enum flash_command { + JEDEC_PAGE_PROGRAM = 0x02, + JEDEC_READ_STATUS = 0x05, + JEDEC_WRITE_ENABLE = 0x06, + JEDEC_CHIP_ERASE = 0xc7, +}; + +struct flash_param { + unsigned char spi_param; + unsigned char clk_div; + unsigned char mode; + unsigned short read_size; + unsigned char jedec_cmd; +} __packed; + +DECLARE_COMPLETION(recovery_remove_complete); + +static struct recovery_hcd *recovery_hcd; + +static void recovery_do_romboot_recovery(struct syna_tcm_hcd *tcm_hcd); + +static int recovery_do_f35_recovery(void); + +static int recovery_add_romboot_data_entry(unsigned char data); + +STORE_PROTOTYPE(recovery, f35_recovery) +STORE_PROTOTYPE(recovery, romboot_recovery) + + +static struct device_attribute *attrs[] = { + ATTRIFY(f35_recovery), + ATTRIFY(romboot_recovery), +}; + +static ssize_t recovery_sysfs_ihex_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static struct bin_attribute bin_attr = { + .attr = { + .name = "ihex", + .mode = (S_IWUSR | S_IWGRP), + }, + .size = 0, + .write = recovery_sysfs_ihex_store, +}; + +static ssize_t recovery_sysfs_romboot_recovery_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval = 0; + unsigned int input; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input) + recovery_do_romboot_recovery(tcm_hcd); + + return retval; +} + +static int recovery_parse_romboot_ihex(void) +{ + int retval; + unsigned char colon; + unsigned char *buf; + unsigned int addr; + unsigned int type; + unsigned int addrl; + unsigned int addrh; + unsigned int data0; + unsigned int data1; + unsigned int count; + unsigned int words; + unsigned int offset; + unsigned int record; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + struct image_info *image_info = &recovery_hcd->image_info; + + if (!(recovery_hcd->ihex_buf)) { + LOGE(tcm_hcd->pdev->dev.parent, + "No ihex data\n"); + return -ENOBUFS; + } + + words = 0; + offset = 0; + buf = recovery_hcd->ihex_buf; + recovery_hcd->data_entries = 0; + recovery_hcd->ihex_records = recovery_hcd->ihex_size / IHEX_RECORD_SIZE; + memset(recovery_hcd->data_buf, 0xff, DATA_ROMBOOT_BUF_SIZE); + + for (record = 0; record < recovery_hcd->ihex_records; record++) { + buf[(record + 1) * IHEX_RECORD_SIZE - 1] = 0x00; + retval = sscanf(&buf[record * IHEX_RECORD_SIZE], + "%c%02x%02x%02x%02x%02x%02x", + &colon, + &count, + &addrh, + &addrl, + &type, + &data0, + &data1); + if (retval != 7) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read ihex record\n"); + return -EINVAL; + } + + if (type == 0x00) { + addr = (addrh << 8) + addrl; + addr += offset; + + recovery_hcd->data_entries = addr; + + retval = recovery_add_romboot_data_entry(data0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry for data0\n"); + return retval; + } + + retval = recovery_add_romboot_data_entry(data1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry for data1\n"); + return retval; + } + + words++; + } else if (type == 0x02) { + offset = (data0 << 8) + data1; + offset <<= 4; + + recovery_hcd->data_entries = offset; + } + } + + image_info->app_firmware.size = DATA_ROMBOOT_BUF_SIZE; + image_info->app_firmware.data = recovery_hcd->data_buf; + + return 0; +} + +static int recovery_flash_romboot_command(struct syna_tcm_hcd *tcm_hcd, + unsigned char flash_command, unsigned char *out, + unsigned int out_size, unsigned char *in, + unsigned int in_size) +{ + int retval; + unsigned char *payld_buf = NULL; + unsigned char *resp_buf = NULL; + unsigned int resp_buf_size; + unsigned int resp_length; + struct flash_param flash_param = {1, 0x19, 0, 0, 0}; + + flash_param.read_size = in_size; + + flash_param.jedec_cmd = flash_command; + + resp_buf = NULL; + resp_buf_size = 0; + + payld_buf = kzalloc(sizeof(flash_param) + out_size, + GFP_KERNEL); + + memcpy(payld_buf, &flash_param, sizeof(flash_param)); + + memcpy(payld_buf + sizeof(flash_param), out, out_size); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_SPI_MASTER_WRITE_THEN_READ_EXTENDED, + payld_buf, + sizeof(flash_param) + out_size, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 20); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command CMD_SPI_MASTER_WRITE_THEN_READ_EXTENDED"); + goto exit; + } + + if (in_size && (in_size <= resp_length)) + memcpy(in, resp_buf, in_size); + +exit: + kfree(payld_buf); + kfree(resp_buf); + return retval; +} + +static int recovery_flash_romboot_status(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + int idx; + unsigned char status; + + for (idx = 0; idx < STATUS_CHECK_RETRY; idx++) { + retval = recovery_flash_romboot_command(tcm_hcd, + JEDEC_READ_STATUS, NULL, 0, + &status, sizeof(status)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write JEDEC_READ_STATUS"); + return retval; + } + + usleep_range(STATUS_CHECK_US_MIN, STATUS_CHECK_US_MAX); + + if (!status) + break; + } + + if (status) + retval = -EIO; + else + retval = status; + + return retval; +} + +static int recovery_flash_romboot_erase(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + LOGN(tcm_hcd->pdev->dev.parent, + "%s", __func__); + + retval = recovery_flash_romboot_command(tcm_hcd, JEDEC_WRITE_ENABLE, + NULL, 0, NULL, 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write JEDEC_WRITE_ENABLE"); + return retval; + } + + retval = recovery_flash_romboot_command(tcm_hcd, JEDEC_CHIP_ERASE, + NULL, 0, NULL, 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write JEDEC_CHIP_ERASE"); + return retval; + } + + retval = recovery_flash_romboot_status(tcm_hcd); + if (retval < 0) + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get correct status: %d", retval); + + return retval; +} + +static int recovery_flash_romboot_reprogram(struct syna_tcm_hcd *tcm_hcd, + unsigned char *image_buf, unsigned int image_size) +{ + int retval; + int idx; + unsigned short img_header; + unsigned int pages; + unsigned char buf[FLASH_PAGE_SIZE + 3]; + + img_header = image_buf[0] | image_buf[1] << 8; + if ((image_size % FLASH_PAGE_SIZE) || + (img_header != BINARY_FILE_MAGIC_VALUE)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Wrong image file"); + LOGE(tcm_hcd->pdev->dev.parent, + "image_size = %d, img_header = 0x%04x", + image_size, img_header); + return -EINVAL; + } + + pages = image_size / FLASH_PAGE_SIZE; + + LOGE(tcm_hcd->pdev->dev.parent, + "image_size = %d, img_header = 0x%04x, pages = %d", + image_size, img_header, pages); + + for (idx = 0; idx < pages; idx++) { + retval = recovery_flash_romboot_command(tcm_hcd, + JEDEC_WRITE_ENABLE, NULL, 0, NULL, 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write JEDEC_WRITE_ENABLE"); + return retval; + } + + buf[0] = FLASH_PAGE_SIZE * idx >> 16; + buf[1] = FLASH_PAGE_SIZE * idx >> 8; + buf[2] = FLASH_PAGE_SIZE * idx; + + memcpy(buf + 3, image_buf + FLASH_PAGE_SIZE * idx, + FLASH_PAGE_SIZE); + + retval = recovery_flash_romboot_command(tcm_hcd, + JEDEC_PAGE_PROGRAM, buf, sizeof(buf), NULL, 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write JEDEC_READ_STATUS"); + return retval; + } + + retval = recovery_flash_romboot_status(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get correct status: %d", + retval); + return retval; + } + + } + + return retval; +} + +static void recovery_do_romboot_recovery(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned int image_size; + unsigned char *out_buf = NULL; + + LOGN(tcm_hcd->pdev->dev.parent, + "%s\n", __func__); + + mutex_lock(&tcm_hcd->extif_mutex); + + pm_stay_awake(&tcm_hcd->pdev->dev); + + if (tcm_hcd->id_info.mode != MODE_ROMBOOTLOADER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Not in romboot\n"); + goto do_program_exit; + } + + retval = recovery_parse_romboot_ihex(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to parse ihex data\n"); + goto do_program_exit; + } + + image_size = (unsigned int)recovery_hcd->image_info.app_firmware.size; + out_buf = kzalloc(image_size, GFP_KERNEL); + + retval = secure_memcpy(out_buf, + recovery_hcd->image_info.app_firmware.size, + recovery_hcd->image_info.app_firmware.data, + recovery_hcd->image_info.app_firmware.size, + recovery_hcd->image_info.app_firmware.size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy payload\n"); + goto do_program_exit; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "image_size = %d\n", + image_size); + + retval = recovery_flash_romboot_erase(tcm_hcd); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase chip"); + goto do_program_exit; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Do recovery_flash_romboot_reprogram"); + retval = recovery_flash_romboot_reprogram(tcm_hcd, out_buf, image_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to program chip"); + goto do_program_exit; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Do reset and re-init"); + retval = tcm_hcd->reset_n_reinit(tcm_hcd, true, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reset"); + } + +do_program_exit: + + pm_relax(&tcm_hcd->pdev->dev); + + mutex_unlock(&tcm_hcd->extif_mutex); + + kfree(out_buf); + + return; +} + +static ssize_t recovery_sysfs_f35_recovery_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input == 1) + recovery_hcd->set_up_recovery_mode = true; + else if (input == 2) + recovery_hcd->set_up_recovery_mode = false; + else + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + if (recovery_hcd->ihex_size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get ihex data\n"); + retval = -EINVAL; + goto exit; + } + + if (recovery_hcd->ihex_size % IHEX_RECORD_SIZE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid ihex data\n"); + retval = -EINVAL; + goto exit; + } + + recovery_hcd->ihex_records = recovery_hcd->ihex_size / IHEX_RECORD_SIZE; + + retval = recovery_do_f35_recovery(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do recovery\n"); + goto exit; + } + + retval = count; + +exit: + recovery_hcd->set_up_recovery_mode = SET_UP_RECOVERY_MODE; + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t recovery_sysfs_ihex_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = secure_memcpy(&recovery_hcd->ihex_buf[pos], + IHEX_BUF_SIZE - pos, + buf, + count, + count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy ihex data\n"); + recovery_hcd->ihex_size = 0; + goto exit; + } + + recovery_hcd->ihex_size = pos + count; + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static int recovery_device_reset(void) +{ + int retval; + unsigned char command; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + command = F35_RESET_COMMAND; + + retval = syna_tcm_rmi_write(tcm_hcd, + recovery_hcd->f35_addr.control_base + F35_CTRL3_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write F$35 command\n"); + return retval; + } + + msleep(bdata->reset_delay_ms); + + return 0; +} + +static int recovery_add_data_entry(unsigned char data) +{ + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + if (recovery_hcd->data_entries >= DATA_BUF_SIZE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Reached data buffer size limit\n"); + return -EINVAL; + } + + recovery_hcd->data_buf[recovery_hcd->data_entries++] = data; + + return 0; +} + +static int recovery_add_romboot_data_entry(unsigned char data) +{ + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + if (recovery_hcd->data_entries >= DATA_ROMBOOT_BUF_SIZE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Reached data buffer size limit\n"); + return -EINVAL; + } + + recovery_hcd->data_buf[recovery_hcd->data_entries++] = data; + + return 0; +} + +static int recovery_add_padding(unsigned int *words) +{ + int retval; + unsigned int padding; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + padding = (F35_CHUNK_SIZE_WORDS - *words % F35_CHUNK_SIZE_WORDS); + padding %= F35_CHUNK_SIZE_WORDS; + + while (padding) { + retval = recovery_add_data_entry(0xff); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry\n"); + return retval; + } + + retval = recovery_add_data_entry(0xff); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry\n"); + return retval; + } + + (*words)++; + padding--; + } + + return 0; +} + +static int recovery_parse_f35_ihex(void) +{ + int retval; + unsigned char colon; + unsigned char *buf; + unsigned int addr; + unsigned int type; + unsigned int addrl; + unsigned int addrh; + unsigned int data0; + unsigned int data1; + unsigned int count; + unsigned int words; + unsigned int offset; + unsigned int record; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + words = 0; + + offset = 0; + + buf = recovery_hcd->ihex_buf; + + recovery_hcd->data_entries = 0; + + for (record = 0; record < recovery_hcd->ihex_records; record++) { + buf[(record + 1) * IHEX_RECORD_SIZE - 1] = 0x00; + retval = sscanf(&buf[record * IHEX_RECORD_SIZE], + "%c%02x%02x%02x%02x%02x%02x", + &colon, + &count, + &addrh, + &addrl, + &type, + &data0, + &data1); + if (retval != 7) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read ihex record\n"); + return -EINVAL; + } + + if (type == 0x00) { + if ((words % F35_CHUNK_SIZE_WORDS) == 0) { + addr = (addrh << 8) + addrl; + addr += offset; + addr >>= 4; + + retval = recovery_add_data_entry(addr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry\n"); + return retval; + } + + retval = recovery_add_data_entry(addr >> 8); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry\n"); + return retval; + } + } + + retval = recovery_add_data_entry(data0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry\n"); + return retval; + } + + retval = recovery_add_data_entry(data1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add data entry\n"); + return retval; + } + + words++; + } else if (type == 0x02) { + retval = recovery_add_padding(&words); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add padding\n"); + return retval; + } + + offset = (data0 << 8) + data1; + offset <<= 4; + } + } + + retval = recovery_add_padding(&words); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to add padding\n"); + return retval; + } + + return 0; +} + +static int recovery_check_status(void) +{ + int retval; + unsigned char status; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + retval = syna_tcm_rmi_read(tcm_hcd, + recovery_hcd->f35_addr.data_base, + &status, + sizeof(status)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read status\n"); + return retval; + } + + status = status & 0x1f; + + if (status != 0x00) { + LOGE(tcm_hcd->pdev->dev.parent, + "Recovery mode status = 0x%02x\n", + status); + return -EINVAL; + } + + return 0; +} + +static int recovery_write_flash(void) +{ + int retval; + unsigned char *data_ptr; + unsigned int chunk_buf_size; + unsigned int chunk_data_size; + unsigned int entries_written; + unsigned int entries_to_write; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + entries_written = 0; + + data_ptr = recovery_hcd->data_buf; + + chunk_buf_size = sizeof(recovery_hcd->chunk_buf); + + chunk_data_size = chunk_buf_size - 1; + + recovery_hcd->chunk_buf[chunk_buf_size - 1] = F35_WRITE_CHUNK_COMMAND; + + while (entries_written < recovery_hcd->data_entries) { + entries_to_write = F35_CHUNK_SIZE + 2; + + retval = secure_memcpy(recovery_hcd->chunk_buf, + chunk_buf_size - 1, + data_ptr, + recovery_hcd->data_entries - entries_written, + entries_to_write); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy chunk data\n"); + return retval; + } + + retval = syna_tcm_rmi_write(tcm_hcd, + recovery_hcd->f35_addr.control_base, + recovery_hcd->chunk_buf, + chunk_buf_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write chunk data\n"); + return retval; + } + + data_ptr += entries_to_write; + entries_written += entries_to_write; + } + + retval = recovery_check_status(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get no error recovery mode status\n"); + return retval; + } + + return 0; +} + +static int recovery_poll_erase_completion(void) +{ + int retval; + unsigned char status; + unsigned char command; + unsigned char data_base; + unsigned int timeout; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + timeout = F35_ERASE_ALL_WAIT_MS; + + data_base = recovery_hcd->f35_addr.data_base; + + do { + command = F35_READ_FLASH_STATUS_COMMAND; + + retval = syna_tcm_rmi_write(tcm_hcd, + recovery_hcd->f35_addr.command_base, + &command, + sizeof(command)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write F$35 command\n"); + return retval; + } + + do { + retval = syna_tcm_rmi_read(tcm_hcd, + recovery_hcd->f35_addr.command_base, + &command, + sizeof(command)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read command status\n"); + return retval; + } + + if (command == 0x00) + break; + + if (timeout == 0) + break; + + msleep(F35_ERASE_ALL_POLL_MS); + timeout -= F35_ERASE_ALL_POLL_MS; + } while (true); + + if (command != 0 && timeout == 0) { + retval = -EINVAL; + goto exit; + } + + retval = syna_tcm_rmi_read(tcm_hcd, + data_base + F35_DATA5_OFFSET, + &status, + sizeof(status)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read flash status\n"); + return retval; + } + + if ((status & 0x01) == 0x00) + break; + + if (timeout == 0) { + retval = -EINVAL; + goto exit; + } + + msleep(F35_ERASE_ALL_POLL_MS); + timeout -= F35_ERASE_ALL_POLL_MS; + } while (true); + + retval = 0; + +exit: + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get erase completion\n"); + } + + return retval; +} + +static int recovery_erase_flash(void) +{ + int retval; + unsigned char command; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + command = F35_ERASE_ALL_COMMAND; + + retval = syna_tcm_rmi_write(tcm_hcd, + recovery_hcd->f35_addr.control_base + F35_CTRL3_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write F$35 command\n"); + return retval; + } + + if (recovery_hcd->f35_addr.command_base) { + retval = recovery_poll_erase_completion(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to wait for erase completion\n"); + return retval; + } + } else { + msleep(F35_ERASE_ALL_WAIT_MS); + } + + retval = recovery_check_status(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get no error recovery mode status\n"); + return retval; + } + + return 0; +} + +static int recovery_set_up_recovery_mode(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + retval = tcm_hcd->identify(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + return retval; + } + + if (tcm_hcd->id_info.mode == MODE_APPLICATION_FIRMWARE) { + retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_BOOTLOADER); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enter bootloader mode\n"); + return retval; + } + } + + retval = tcm_hcd->write_message(tcm_hcd, + recovery_hcd->out_buf[0], + &recovery_hcd->out_buf[1], + 2, + NULL, + NULL, + NULL, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_REBOOT_TO_ROM_BOOTLOADER)); + return retval; + } + + msleep(bdata->reset_delay_ms); + + return 0; +} + +static int recovery_do_f35_recovery(void) +{ + int retval; + struct rmi_pdt_entry p_entry; + struct syna_tcm_hcd *tcm_hcd = recovery_hcd->tcm_hcd; + + retval = recovery_parse_f35_ihex(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to parse ihex data\n"); + return retval; + } + + if (recovery_hcd->set_up_recovery_mode) { + retval = recovery_set_up_recovery_mode(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up recovery mode\n"); + return retval; + } + } + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, false); +#endif + + retval = syna_tcm_rmi_read(tcm_hcd, + PDT_START_ADDR, + (unsigned char *)&p_entry, + sizeof(p_entry)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read PDT entry\n"); + return retval; + } + + if (p_entry.fn_number != UBL_FN_NUMBER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to find F$35\n"); + return -ENODEV; + } + + recovery_hcd->f35_addr.query_base = p_entry.query_base_addr; + recovery_hcd->f35_addr.command_base = p_entry.command_base_addr; + recovery_hcd->f35_addr.control_base = p_entry.control_base_addr; + recovery_hcd->f35_addr.data_base = p_entry.data_base_addr; + + LOGN(tcm_hcd->pdev->dev.parent, + "Start of recovery\n"); + + retval = recovery_erase_flash(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase flash\n"); + return retval; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Flash erased\n"); + + retval = recovery_write_flash(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to flash\n"); + return retval; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Flash written\n"); + + retval = recovery_device_reset(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + return retval; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "End of recovery\n"); + + if (recovery_hcd->set_up_recovery_mode) + return 0; + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, true); +#endif + + retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable interrupt\n"); + return retval; + } + + retval = tcm_hcd->identify(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + return retval; + } + + if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode)) { + retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware\n"); + return retval; + } + } + + return 0; +} + +static int recovery_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval = -1; + int idx; + + recovery_hcd = kzalloc(sizeof(*recovery_hcd), GFP_KERNEL); + if (!recovery_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for recovery_hcd\n"); + return -ENOMEM; + } + + recovery_hcd->ihex_buf = vzalloc(IHEX_BUF_SIZE); + if (!recovery_hcd->ihex_buf) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for recovery_hcd->ihex_buf\n"); + goto err_allocate_ihex_buf; + } + + recovery_hcd->data_buf = kzalloc(DATA_BUF_SIZE, GFP_KERNEL); + if (!recovery_hcd->data_buf) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for recovery_hcd->data_buf\n"); + goto err_allocate_data_buf; + } + + recovery_hcd->tcm_hcd = tcm_hcd; + + recovery_hcd->set_up_recovery_mode = SET_UP_RECOVERY_MODE; + + recovery_hcd->out_buf[0] = CMD_REBOOT_TO_ROM_BOOTLOADER; + recovery_hcd->out_buf[1] = 0; + recovery_hcd->out_buf[2] = 0; + + if (ENABLE_SYSFS_RECOVERY == false) + goto init_finished; + + recovery_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME, + tcm_hcd->sysfs_dir); + if (!recovery_hcd->sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs directory\n"); + retval = -EINVAL; + goto err_sysfs_create_dir; + } + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + retval = sysfs_create_file(recovery_hcd->sysfs_dir, + &(*attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs file\n"); + goto err_sysfs_create_file; + } + } + + retval = sysfs_create_bin_file(recovery_hcd->sysfs_dir, &bin_attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs bin file\n"); + goto err_sysfs_create_bin_file; + } + +init_finished: + return 0; + +err_sysfs_create_bin_file: +err_sysfs_create_file: + for (idx--; idx >= 0; idx--) + sysfs_remove_file(recovery_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(recovery_hcd->sysfs_dir); + +err_sysfs_create_dir: + kfree(recovery_hcd->data_buf); +err_allocate_data_buf: + vfree(recovery_hcd->ihex_buf); +err_allocate_ihex_buf: + kfree(recovery_hcd); + recovery_hcd = NULL; + + return retval; +} + +static int recovery_remove(struct syna_tcm_hcd *tcm_hcd) +{ + int idx; + + if (!recovery_hcd) + goto exit; + + if (ENABLE_SYSFS_RECOVERY == true) { + sysfs_remove_bin_file(recovery_hcd->sysfs_dir, &bin_attr); + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + sysfs_remove_file(recovery_hcd->sysfs_dir, + &(*attrs[idx]).attr); + } + + kobject_put(recovery_hcd->sysfs_dir); + } + kfree(recovery_hcd->data_buf); + vfree(recovery_hcd->ihex_buf); + kfree(recovery_hcd); + recovery_hcd = NULL; + +exit: + complete(&recovery_remove_complete); + + return 0; +} + +static int recovery_reinit(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!recovery_hcd) { + retval = recovery_init(tcm_hcd); + return retval; + } + + return 0; +} + +static struct syna_tcm_module_cb recovery_module = { + .type = TCM_RECOVERY, + .init = recovery_init, + .remove = recovery_remove, + .syncbox = NULL, +#ifdef REPORT_NOTIFIER + .asyncbox = NULL, +#endif + .reinit = recovery_reinit, + .suspend = NULL, + .resume = NULL, + .early_suspend = NULL, +}; + +static int __init recovery_module_init(void) +{ + return syna_tcm_add_module(&recovery_module, true); +} + +static void __exit recovery_module_exit(void) +{ + syna_tcm_add_module(&recovery_module, false); + + wait_for_completion(&recovery_remove_complete); + + return; +} + +module_init(recovery_module_init); +module_exit(recovery_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Recovery Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_reflash.c b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_reflash.c new file mode 100644 index 000000000000..78ecb8b1a460 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_reflash.c @@ -0,0 +1,2642 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * Copyright (C) 2018-2019 Ian Su + * Copyright (C) 2018-2019 Joey Zhou + * Copyright (C) 2018-2019 Yuehao Qiu + * Copyright (C) 2018-2019 Aaron Chen + * + * 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. + */ + +#include +#include +#include "../synaptics_core/synaptics_core.h" +/* +#define STARTUP_REFLASH +*/ +#define FORCE_REFLASH false + +#define ENABLE_SYS_REFLASH true + +#define SYSFS_DIR_NAME "reflash" + +#define CUSTOM_DIR_NAME "custom" + +#define FW_IMAGE_NAME "synaptics/reflash_firmware.img" + +#define FW_IMAGE_NAME_MANUAL "synaptics/reflash_firmware_manual.img" + +#define BOOT_CONFIG_ID "BOOT_CONFIG" + +#define APP_CODE_ID "APP_CODE" + +#define PROD_TEST_ID "APP_PROD_TEST" + +#define APP_CONFIG_ID "APP_CONFIG" + +#define DISP_CONFIG_ID "DISPLAY" + +#define FB_READY_COUNT 2 + +#define FB_READY_WAIT_MS 100 + +#define FB_READY_TIMEOUT_S 30 + +#define IMAGE_FILE_MAGIC_VALUE 0x4818472b + +#define FLASH_AREA_MAGIC_VALUE 0x7c05e516 + +#define BOOT_CONFIG_SIZE 8 + +#define BOOT_CONFIG_SLOTS 16 + +#define IMAGE_BUF_SIZE (512 * 1024) + +#define ERASE_FLASH_DELAY_MS 500 + +#define WRITE_FLASH_DELAY_MS 20 + +#define REFLASH (1 << 0) + +#define FORCE_UPDATE (1 << 1) + +#define APP_CFG_UPDATE (1 << 2) + +#define DISP_CFG_UPDATE (1 << 3) + +#define BOOT_CFG_UPDATE (1 << 4) + +#define BOOT_CFG_LOCKDOWN (1 << 5) + +#define reflash_show_data() \ +{ \ + LOCK_BUFFER(reflash_hcd->read); \ +\ + readlen = MIN(count, reflash_hcd->read.data_length - pos); \ +\ + retval = secure_memcpy(buf, \ + count, \ + &reflash_hcd->read.buf[pos], \ + reflash_hcd->read.buf_size - pos, \ + readlen); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to copy read data\n"); \ + } else { \ + retval = readlen; \ + } \ +\ + UNLOCK_BUFFER(reflash_hcd->read); \ +} + +enum update_area { + NONE = 0, + FIRMWARE_CONFIG, + CONFIG_ONLY, +}; + +struct app_config_header { + unsigned short magic_value[4]; + unsigned char checksum[4]; + unsigned char length[2]; + unsigned char build_id[4]; + unsigned char customer_config_id[16]; +}; + +struct area_descriptor { + unsigned char magic_value[4]; + unsigned char id_string[16]; + unsigned char flags[4]; + unsigned char flash_addr_words[4]; + unsigned char length[4]; + unsigned char checksum[4]; +}; + +struct block_data { + const unsigned char *data; + unsigned int size; + unsigned int flash_addr; +}; + +struct image_info { + struct block_data boot_config; + struct block_data app_firmware; + struct block_data prod_test_firmware; + struct block_data app_config; + struct block_data disp_config; +}; + +struct image_header { + unsigned char magic_value[4]; + unsigned char num_of_areas[4]; +}; + +struct boot_config { + union { + unsigned char i2c_address; + struct { + unsigned char cpha:1; + unsigned char cpol:1; + unsigned char word0_b2__7:6; + } __packed; + }; + unsigned char attn_polarity:1; + unsigned char attn_drive:2; + unsigned char attn_pullup:1; + unsigned char word0_b12__14:3; + unsigned char used:1; + unsigned short customer_part_id; + unsigned short boot_timeout; + unsigned short continue_on_reset:1; + unsigned short word3_b1__15:15; +} __packed; + +struct reflash_hcd { + bool force_update; + bool disp_cfg_update; + bool reflash_by_manual; + const unsigned char *image; + unsigned char *image_buf; + unsigned int image_size; + unsigned int page_size; + unsigned int write_block_size; + unsigned int max_write_payload_size; + const struct firmware *fw_entry; + struct mutex reflash_mutex; + struct kobject *sysfs_dir; + struct kobject *custom_dir; + struct work_struct work; + struct workqueue_struct *workqueue; + struct image_info image_info; + struct syna_tcm_buffer out; + struct syna_tcm_buffer resp; + struct syna_tcm_buffer read; + struct syna_tcm_hcd *tcm_hcd; + char fw_name[64]; +}; + +DECLARE_COMPLETION(reflash_remove_complete); + +static struct reflash_hcd *reflash_hcd; + +static int reflash_get_fw_image(void); + +static int reflash_read_data(enum flash_area area, bool run_app_firmware, + struct syna_tcm_buffer *output); + +static int reflash_update_custom_otp(const unsigned char *data, + unsigned int offset, unsigned int datalen); + +static int reflash_update_custom_lcm(const unsigned char *data, + unsigned int offset, unsigned int datalen); + +static int reflash_update_custom_oem(const unsigned char *data, + unsigned int offset, unsigned int datalen); + +static int reflash_update_boot_config(bool lock); + +static int reflash_update_app_config(bool reset); + +static int reflash_update_disp_config(bool reset); + +static int reflash_do_reflash(void); + +STORE_PROTOTYPE(reflash, reflash) + +static struct device_attribute *attrs[] = { + ATTRIFY(reflash), +}; + +static ssize_t reflash_sysfs_image_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_lockdown_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_lockdown_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_lcm_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_lcm_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_oem_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_oem_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static ssize_t reflash_sysfs_cs_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static struct bin_attribute bin_attrs[] = { + { + .attr = { + .name = "image", + .mode = (S_IWUSR | S_IWGRP), + }, + .size = 0, + .write = reflash_sysfs_image_store, + }, + { + .attr = { + .name = "lockdown", + .mode = (S_IRUGO | S_IWUSR | S_IWGRP), + }, + .size = 0, + .read = reflash_sysfs_lockdown_show, + .write = reflash_sysfs_lockdown_store, + }, + { + .attr = { + .name = "lcm", + .mode = (S_IRUGO | S_IWUSR | S_IWGRP), + }, + .size = 0, + .read = reflash_sysfs_lcm_show, + .write = reflash_sysfs_lcm_store, + }, + { + .attr = { + .name = "oem", + .mode = (S_IRUGO | S_IWUSR | S_IWGRP), + }, + .size = 0, + .read = reflash_sysfs_oem_show, + .write = reflash_sysfs_oem_store, + }, + { + .attr = { + .name = "customer_serialization", + .mode = (S_IRUGO | S_IRUSR | S_IRGRP), + }, + .size = 0, + .read = reflash_sysfs_cs_show, + }, +}; + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +static int syna_ts_set_fwname(const char* fw_name) { + if (fw_name != NULL) { + strlcpy(reflash_hcd->fw_name, fw_name, + sizeof(reflash_hcd->fw_name)); + return 0; + } + LOGI(reflash_hcd->tcm_hcd->pdev->dev.parent, + "set fw name = %s", reflash_hcd->fw_name); + return -EINVAL; +} + +static int syna_ts_do_reflash(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&reflash_hcd->reflash_mutex); + reflash_hcd->force_update = true; + retval = reflash_do_reflash(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, "Failed to do reflash\n"); + } + reflash_hcd->force_update = FORCE_REFLASH; + mutex_unlock(&reflash_hcd->reflash_mutex); + + return retval; +} +#endif + +static ssize_t reflash_sysfs_reflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval; + unsigned int input; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + mutex_lock(&tcm_hcd->extif_mutex); + + pm_stay_awake(&tcm_hcd->pdev->dev); + + mutex_lock(&reflash_hcd->reflash_mutex); + + if (reflash_hcd->image_size != 0) + reflash_hcd->image = reflash_hcd->image_buf; + + reflash_hcd->force_update = input & FORCE_UPDATE ? true : false; + + reflash_hcd->reflash_by_manual = true; + + if (input & REFLASH || input & FORCE_UPDATE) { + retval = reflash_do_reflash(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reflash\n"); + goto exit; + } + } + + if ((input & ~(REFLASH | FORCE_UPDATE)) == 0) { + retval = count; + goto exit; + } + + retval = reflash_get_fw_image(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get firmware image\n"); + goto exit; + } + + if (input & BOOT_CFG_LOCKDOWN) { + retval = reflash_update_boot_config(true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to lockdown boot config\n"); + goto exit; + } + } else if (input & BOOT_CFG_UPDATE) { + retval = reflash_update_boot_config(false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to update boot config\n"); + goto exit; + } + } + + if (input & REFLASH || input & FORCE_UPDATE) { + retval = count; + goto exit; + } + + if (input & DISP_CFG_UPDATE) { + if (input & APP_CFG_UPDATE) + retval = reflash_update_disp_config(false); + else + retval = reflash_update_disp_config(true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reflash display config\n"); + goto exit; + } + } + + if (input & APP_CFG_UPDATE) { + retval = reflash_update_app_config(true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reflash application config\n"); + goto exit; + } + } + + retval = count; + +exit: + if (reflash_hcd->fw_entry) { + release_firmware(reflash_hcd->fw_entry); + reflash_hcd->fw_entry = NULL; + } + + reflash_hcd->reflash_by_manual = false; + reflash_hcd->image = NULL; + reflash_hcd->image_size = 0; + reflash_hcd->force_update = FORCE_REFLASH; + + mutex_unlock(&reflash_hcd->reflash_mutex); + + pm_relax(&tcm_hcd->pdev->dev); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_image_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + retval = secure_memcpy(&reflash_hcd->image_buf[pos], + IMAGE_BUF_SIZE - pos, + buf, + count, + count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy firmware image data\n"); + reflash_hcd->image_size = 0; + goto exit; + } + + reflash_hcd->image_size = pos + count; + + retval = count; + +exit: + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_lockdown_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int readlen; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_read_data(CUSTOM_OTP, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read lockdown data\n"); + goto exit; + } + + reflash_show_data(); + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_lockdown_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + pm_stay_awake(&tcm_hcd->pdev->dev); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_update_custom_otp(buf, pos, count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to update custom OTP data\n"); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + pm_relax(&tcm_hcd->pdev->dev); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_lcm_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int readlen; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_read_data(CUSTOM_LCM, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read LCM data\n"); + goto exit; + } + + reflash_show_data(); + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_lcm_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + pm_stay_awake(&tcm_hcd->pdev->dev); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_update_custom_lcm(buf, pos, count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to update custom LCM data\n"); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + pm_relax(&tcm_hcd->pdev->dev); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_oem_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int readlen; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_read_data(CUSTOM_OEM, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read OEM data\n"); + goto exit; + } + + reflash_show_data(); + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_oem_store(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + pm_stay_awake(&tcm_hcd->pdev->dev); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_update_custom_oem(buf, pos, count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to update custom OEM data\n"); + goto exit; + } + + retval = count; + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + pm_relax(&tcm_hcd->pdev->dev); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t reflash_sysfs_cs_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int readlen; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_read_data(BOOT_CONFIG, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read OEM data\n"); + goto exit; + } + + reflash_show_data(); + +exit: + mutex_unlock(&reflash_hcd->reflash_mutex); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static int reflash_set_up_flash_access(void) +{ + int retval; + unsigned int temp; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = tcm_hcd->identify(tcm_hcd, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + return retval; + } + + if (tcm_hcd->id_info.mode == MODE_APPLICATION_FIRMWARE) { + retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_BOOTLOADER); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enter bootloader mode\n"); + return retval; + } + } + + temp = tcm_hcd->boot_info.write_block_size_words; + reflash_hcd->write_block_size = temp * 2; + + temp = le2_to_uint(tcm_hcd->boot_info.erase_page_size_words); + reflash_hcd->page_size = temp * 2; + + temp = le2_to_uint(tcm_hcd->boot_info.max_write_payload_size); + reflash_hcd->max_write_payload_size = temp; + + LOGD(tcm_hcd->pdev->dev.parent, + "Write block size = %d\n", + reflash_hcd->write_block_size); + + LOGD(tcm_hcd->pdev->dev.parent, + "Page size = %d\n", + reflash_hcd->page_size); + + LOGD(tcm_hcd->pdev->dev.parent, + "Max write payload size = %d\n", + reflash_hcd->max_write_payload_size); + + if (reflash_hcd->write_block_size > (tcm_hcd->wr_chunk_size - 5)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Write block size greater than available chunk space\n"); + return -EINVAL; + } + + return 0; +} + +static int reflash_parse_fw_image(void) +{ + unsigned int idx; + unsigned int addr; + unsigned int offset; + unsigned int length; + unsigned int checksum; + unsigned int flash_addr; + unsigned int magic_value; + unsigned int num_of_areas; + struct image_header *header; + struct image_info *image_info; + struct area_descriptor *descriptor; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + const unsigned char *image; + const unsigned char *content; + + image = reflash_hcd->image; + image_info = &reflash_hcd->image_info; + header = (struct image_header *)image; + + reflash_hcd->disp_cfg_update = false; + + magic_value = le4_to_uint(header->magic_value); + if (magic_value != IMAGE_FILE_MAGIC_VALUE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid image file magic value\n"); + return -EINVAL; + } + + memset(image_info, 0x00, sizeof(*image_info)); + + offset = sizeof(*header); + num_of_areas = le4_to_uint(header->num_of_areas); + + for (idx = 0; idx < num_of_areas; idx++) { + addr = le4_to_uint(image + offset); + descriptor = (struct area_descriptor *)(image + addr); + offset += 4; + + magic_value = le4_to_uint(descriptor->magic_value); + if (magic_value != FLASH_AREA_MAGIC_VALUE) + continue; + + length = le4_to_uint(descriptor->length); + content = (unsigned char *)descriptor + sizeof(*descriptor); + flash_addr = le4_to_uint(descriptor->flash_addr_words) * 2; + checksum = le4_to_uint(descriptor->checksum); + + if (0 == strncmp((char *)descriptor->id_string, + BOOT_CONFIG_ID, + strlen(BOOT_CONFIG_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Boot config checksum error\n"); + return -EINVAL; + } + image_info->boot_config.size = length; + image_info->boot_config.data = content; + image_info->boot_config.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Boot config size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Boot config flash address = 0x%08x\n", + flash_addr); + } else if (0 == strncmp((char *)descriptor->id_string, + APP_CODE_ID, + strlen(APP_CODE_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application firmware checksum error\n"); + return -EINVAL; + } + image_info->app_firmware.size = length; + image_info->app_firmware.data = content; + image_info->app_firmware.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Application firmware size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Application firmware flash address = 0x%08x\n", + flash_addr); + } else if (0 == strncmp((char *)descriptor->id_string, + PROD_TEST_ID, + strlen(PROD_TEST_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Production test firmware checksum error\n"); + return -EINVAL; + } + image_info->prod_test_firmware.size = length; + image_info->prod_test_firmware.data = content; + image_info->prod_test_firmware.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Production test firmware size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Production test firmware flash address = 0x%08x\n", + flash_addr); + } else if (0 == strncmp((char *)descriptor->id_string, + APP_CONFIG_ID, + strlen(APP_CONFIG_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application config checksum error\n"); + return -EINVAL; + } + image_info->app_config.size = length; + image_info->app_config.data = content; + image_info->app_config.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Application config size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Application config flash address = 0x%08x\n", + flash_addr); + } else if (0 == strncmp((char *)descriptor->id_string, + DISP_CONFIG_ID, + strlen(DISP_CONFIG_ID))) { + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Display config checksum error\n"); + return -EINVAL; + } + reflash_hcd->disp_cfg_update = true; + image_info->disp_config.size = length; + image_info->disp_config.data = content; + image_info->disp_config.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Display config size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Display config flash address = 0x%08x\n", + flash_addr); + } + } + + return 0; +} + +static int reflash_get_fw_image(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (reflash_hcd->image == NULL) { + if (reflash_hcd->fw_name[0] != 0) { + LOGN(tcm_hcd->pdev->dev.parent, "Request firmware %s\n", reflash_hcd->fw_name); + retval = request_firmware(&reflash_hcd->fw_entry, + reflash_hcd->fw_name, tcm_hcd->pdev->dev.parent); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, "Failed to request %s\n", reflash_hcd->fw_name); + return retval; + } + + } else if (reflash_hcd->reflash_by_manual == false) { + retval = request_firmware(&reflash_hcd->fw_entry, + FW_IMAGE_NAME, tcm_hcd->pdev->dev.parent); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to request %s\n", + FW_IMAGE_NAME); + return retval; + } + + } else { + retval = request_firmware(&reflash_hcd->fw_entry, + FW_IMAGE_NAME_MANUAL, + tcm_hcd->pdev->dev.parent); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to request %s\n", + FW_IMAGE_NAME_MANUAL); + return retval; + } + } + + reflash_hcd->image = reflash_hcd->fw_entry->data; + reflash_hcd->image_size = reflash_hcd->fw_entry->size; + + LOGD(tcm_hcd->pdev->dev.parent, + "Firmware image size = %d\n", + reflash_hcd->image_size); + } + + retval = reflash_parse_fw_image(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to parse firmware image\n"); + return retval; + } + + return 0; +} + +static enum update_area reflash_compare_id_info(void) +{ + enum update_area update_area; + unsigned int idx; + unsigned int image_fw_id; + unsigned int device_fw_id; + unsigned char *image_config_id; + unsigned char *device_config_id; + struct app_config_header *header; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + const unsigned char *app_config_data; + + update_area = NONE; + + if (reflash_hcd->image_info.app_config.size < sizeof(*header)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid application config in image file\n"); + goto exit; + } + + app_config_data = reflash_hcd->image_info.app_config.data; + header = (struct app_config_header *)app_config_data; + + if (reflash_hcd->force_update) { + update_area = FIRMWARE_CONFIG; + goto exit; + } + + if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode)) { + update_area = FIRMWARE_CONFIG; + goto exit; + } + + image_fw_id = le4_to_uint(header->build_id); + device_fw_id = tcm_hcd->packrat_number; + + if (image_fw_id > device_fw_id) { + LOGN(tcm_hcd->pdev->dev.parent, + "Image firmware ID newer than device firmware ID\n"); + update_area = FIRMWARE_CONFIG; + goto exit; + } else if (image_fw_id < device_fw_id) { + LOGN(tcm_hcd->pdev->dev.parent, + "Image firmware ID older than device firmware ID\n"); + update_area = NONE; + goto exit; + } + + image_config_id = header->customer_config_id; + device_config_id = tcm_hcd->app_info.customer_config_id; + + for (idx = 0; idx < 16; idx++) { + if (image_config_id[idx] > device_config_id[idx]) { + LOGN(tcm_hcd->pdev->dev.parent, + "Image config ID newer than device config ID\n"); + update_area = CONFIG_ONLY; + goto exit; + } else if (image_config_id[idx] < device_config_id[idx]) { + LOGN(tcm_hcd->pdev->dev.parent, + "Image config ID older than device config ID\n"); + update_area = NONE; + goto exit; + } + } + + update_area = NONE; + +exit: + if (update_area == NONE) { + LOGN(tcm_hcd->pdev->dev.parent, + "No need to do reflash\n"); + } else { + LOGN(tcm_hcd->pdev->dev.parent, + "Updating %s\n", + update_area == FIRMWARE_CONFIG ? + "firmware and config" : + "config only"); + } + + return update_area; +} + +static int reflash_read_flash(unsigned int address, unsigned char *data, + unsigned int datalen) +{ + int retval; + unsigned int length_words; + unsigned int flash_addr_words; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + LOCK_BUFFER(reflash_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &reflash_hcd->out, + 6); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for reflash_hcd->out.buf\n"); + UNLOCK_BUFFER(reflash_hcd->out); + return retval; + } + + length_words = datalen / 2; + flash_addr_words = address / 2; + + reflash_hcd->out.buf[0] = (unsigned char)flash_addr_words; + reflash_hcd->out.buf[1] = (unsigned char)(flash_addr_words >> 8); + reflash_hcd->out.buf[2] = (unsigned char)(flash_addr_words >> 16); + reflash_hcd->out.buf[3] = (unsigned char)(flash_addr_words >> 24); + reflash_hcd->out.buf[4] = (unsigned char)length_words; + reflash_hcd->out.buf[5] = (unsigned char)(length_words >> 8); + + LOCK_BUFFER(reflash_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_READ_FLASH, + reflash_hcd->out.buf, + 6, + &reflash_hcd->resp.buf, + &reflash_hcd->resp.buf_size, + &reflash_hcd->resp.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_READ_FLASH)); + UNLOCK_BUFFER(reflash_hcd->resp); + UNLOCK_BUFFER(reflash_hcd->out); + return retval; + } + + UNLOCK_BUFFER(reflash_hcd->out); + + if (reflash_hcd->resp.data_length != datalen) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read requested length\n"); + UNLOCK_BUFFER(reflash_hcd->resp); + return -EIO; + } + + retval = secure_memcpy(data, + datalen, + reflash_hcd->resp.buf, + reflash_hcd->resp.buf_size, + datalen); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy read data\n"); + UNLOCK_BUFFER(reflash_hcd->resp); + return retval; + } + + UNLOCK_BUFFER(reflash_hcd->resp); + + return 0; +} + +static int reflash_read_data(enum flash_area area, bool run_app_firmware, + struct syna_tcm_buffer *output) +{ + int retval; + unsigned int temp; + unsigned int addr; + unsigned int length; + struct syna_tcm_app_info *app_info; + struct syna_tcm_boot_info *boot_info; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + switch (area) { + case CUSTOM_LCM: + case CUSTOM_OEM: + case PPDT: + retval = tcm_hcd->get_data_location(tcm_hcd, + area, + &addr, + &length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get data location\n"); + return retval; + } + break; + default: + break; + } + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + + app_info = &tcm_hcd->app_info; + boot_info = &tcm_hcd->boot_info; + + switch (area) { + case BOOT_CONFIG: + temp = le2_to_uint(boot_info->boot_config_start_block); + addr = temp * reflash_hcd->write_block_size; + length = BOOT_CONFIG_SIZE * BOOT_CONFIG_SLOTS; + break; + case APP_CONFIG: + temp = le2_to_uint(app_info->app_config_start_write_block); + addr = temp * reflash_hcd->write_block_size; + length = le2_to_uint(app_info->app_config_size); + break; + case DISP_CONFIG: + temp = le4_to_uint(boot_info->display_config_start_block); + addr = temp * reflash_hcd->write_block_size; + temp = le2_to_uint(boot_info->display_config_length_blocks); + length = temp * reflash_hcd->write_block_size; + break; + case CUSTOM_OTP: + temp = le2_to_uint(boot_info->custom_otp_start_block); + addr = temp * reflash_hcd->write_block_size; + temp = le2_to_uint(boot_info->custom_otp_length_blocks); + length = temp * reflash_hcd->write_block_size; + break; + case CUSTOM_LCM: + case CUSTOM_OEM: + case PPDT: + addr *= reflash_hcd->write_block_size; + length *= reflash_hcd->write_block_size; + break; + default: + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid data area\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + if (addr == 0 || length == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Data area unavailable\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + LOCK_BUFFER(reflash_hcd->read); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &reflash_hcd->read, + length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for reflash_hcd->read.buf\n"); + UNLOCK_BUFFER(reflash_hcd->read); + goto run_app_firmware; + } + + retval = reflash_read_flash(addr, reflash_hcd->read.buf, length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read from flash\n"); + UNLOCK_BUFFER(reflash_hcd->read); + goto run_app_firmware; + } + + reflash_hcd->read.data_length = length; + + if (output != NULL) { + retval = syna_tcm_alloc_mem(tcm_hcd, + output, + length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for output->buf\n"); + UNLOCK_BUFFER(reflash_hcd->read); + goto run_app_firmware; + } + + retval = secure_memcpy(output->buf, + output->buf_size, + reflash_hcd->read.buf, + reflash_hcd->read.buf_size, + length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy read data\n"); + UNLOCK_BUFFER(reflash_hcd->read); + goto run_app_firmware; + } + + output->data_length = length; + } + + UNLOCK_BUFFER(reflash_hcd->read); + + retval = 0; + +run_app_firmware: + if (!run_app_firmware) + goto exit; + + if (tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware\n"); + } + +exit: + return retval; +} + +static int reflash_check_boot_config(void) +{ + unsigned int temp; + unsigned int image_addr; + unsigned int device_addr; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (reflash_hcd->image_info.boot_config.size < BOOT_CONFIG_SIZE) { + LOGE(tcm_hcd->pdev->dev.parent, + "No valid boot config in image file\n"); + return -EINVAL; + } + + image_addr = reflash_hcd->image_info.boot_config.flash_addr; + + temp = le2_to_uint(tcm_hcd->boot_info.boot_config_start_block); + device_addr = temp * reflash_hcd->write_block_size; + + if (image_addr != device_addr) { + LOGE(tcm_hcd->pdev->dev.parent, + "Flash address mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int reflash_check_app_config(void) +{ + unsigned int temp; + unsigned int image_addr; + unsigned int image_size; + unsigned int device_addr; + unsigned int device_size; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (reflash_hcd->image_info.app_config.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No application config in image file\n"); + return -EINVAL; + } + + image_addr = reflash_hcd->image_info.app_config.flash_addr; + image_size = reflash_hcd->image_info.app_config.size; + + temp = le2_to_uint(tcm_hcd->app_info.app_config_start_write_block); + device_addr = temp * reflash_hcd->write_block_size; + device_size = le2_to_uint(tcm_hcd->app_info.app_config_size); + + if (device_addr == 0 && device_size == 0) + return 0; + + if (image_addr != device_addr) { + LOGE(tcm_hcd->pdev->dev.parent, + "Flash address mismatch\n"); + return -EINVAL; + } + + if (image_size != device_size) { + LOGE(tcm_hcd->pdev->dev.parent, + "Config size mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int reflash_check_disp_config(void) +{ + unsigned int temp; + unsigned int image_addr; + unsigned int image_size; + unsigned int device_addr; + unsigned int device_size; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (reflash_hcd->image_info.disp_config.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No display config in image file\n"); + return -EINVAL; + } + + image_addr = reflash_hcd->image_info.disp_config.flash_addr; + image_size = reflash_hcd->image_info.disp_config.size; + + temp = le4_to_uint(tcm_hcd->boot_info.display_config_start_block); + device_addr = temp * reflash_hcd->write_block_size; + + temp = le2_to_uint(tcm_hcd->boot_info.display_config_length_blocks); + device_size = temp * reflash_hcd->write_block_size; + + if (image_addr != device_addr) { + LOGE(tcm_hcd->pdev->dev.parent, + "Flash address mismatch\n"); + return -EINVAL; + } + + if (image_size != device_size) { + LOGE(tcm_hcd->pdev->dev.parent, + "Config size mismatch\n"); + return -EINVAL; + } + + return 0; +} + +static int reflash_check_prod_test_firmware(void) +{ + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (reflash_hcd->image_info.prod_test_firmware.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No production test firmware in image file\n"); + return -EINVAL; + } + + return 0; +} + +static int reflash_check_app_firmware(void) +{ + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if (reflash_hcd->image_info.app_firmware.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No application firmware in image file\n"); + return -EINVAL; + } + + return 0; +} + +static int reflash_write_flash(unsigned int address, const unsigned char *data, + unsigned int datalen) +{ + int retval; + unsigned int offset; + unsigned int w_length; + unsigned int xfer_length; + unsigned int remaining_length; + unsigned int flash_address; + unsigned int block_address; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + w_length = tcm_hcd->wr_chunk_size - 5; + + w_length = w_length - (w_length % reflash_hcd->write_block_size); + + w_length = MIN(w_length, reflash_hcd->max_write_payload_size); + + offset = 0; + + remaining_length = datalen; + + LOCK_BUFFER(reflash_hcd->out); + LOCK_BUFFER(reflash_hcd->resp); + + while (remaining_length) { + if (remaining_length > w_length) + xfer_length = w_length; + else + xfer_length = remaining_length; + + retval = syna_tcm_alloc_mem(tcm_hcd, + &reflash_hcd->out, + xfer_length + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for reflash_hcd->out.buf\n"); + UNLOCK_BUFFER(reflash_hcd->resp); + UNLOCK_BUFFER(reflash_hcd->out); + return retval; + } + + flash_address = address + offset; + block_address = flash_address / reflash_hcd->write_block_size; + reflash_hcd->out.buf[0] = (unsigned char)block_address; + reflash_hcd->out.buf[1] = (unsigned char)(block_address >> 8); + + retval = secure_memcpy(&reflash_hcd->out.buf[2], + reflash_hcd->out.buf_size - 2, + &data[offset], + datalen - offset, + xfer_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy write data\n"); + UNLOCK_BUFFER(reflash_hcd->resp); + UNLOCK_BUFFER(reflash_hcd->out); + return retval; + } + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_WRITE_FLASH, + reflash_hcd->out.buf, + xfer_length + 2, + &reflash_hcd->resp.buf, + &reflash_hcd->resp.buf_size, + &reflash_hcd->resp.data_length, + NULL, + WRITE_FLASH_DELAY_MS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_WRITE_FLASH)); + LOGE(tcm_hcd->pdev->dev.parent, + "Flash address = 0x%08x\n", + flash_address); + LOGE(tcm_hcd->pdev->dev.parent, + "Data length = %d\n", + xfer_length); + UNLOCK_BUFFER(reflash_hcd->resp); + UNLOCK_BUFFER(reflash_hcd->out); + return retval; + } + + offset += xfer_length; + remaining_length -= xfer_length; + } + + UNLOCK_BUFFER(reflash_hcd->resp); + UNLOCK_BUFFER(reflash_hcd->out); + + return 0; +} + +static int reflash_write_app_config(void) +{ + int retval; + unsigned int size; + unsigned int flash_addr; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + const unsigned char *data; + + data = reflash_hcd->image_info.app_config.data; + size = reflash_hcd->image_info.app_config.size; + flash_addr = reflash_hcd->image_info.app_config.flash_addr; + + retval = reflash_write_flash(flash_addr, data, size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write app_config to flash\n"); + return retval; + } + + return 0; +} + +static int reflash_write_disp_config(void) +{ + int retval; + unsigned int size; + unsigned int flash_addr; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + const unsigned char *data; + + data = reflash_hcd->image_info.disp_config.data; + size = reflash_hcd->image_info.disp_config.size; + flash_addr = reflash_hcd->image_info.disp_config.flash_addr; + + retval = reflash_write_flash(flash_addr, data, size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write disp_config to flash\n"); + return retval; + } + + return 0; +} + +static int reflash_write_prod_test_firmware(void) +{ + int retval; + unsigned int size; + unsigned int flash_addr; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + const unsigned char *data; + + data = reflash_hcd->image_info.prod_test_firmware.data; + size = reflash_hcd->image_info.prod_test_firmware.size; + flash_addr = reflash_hcd->image_info.prod_test_firmware.flash_addr; + + retval = reflash_write_flash(flash_addr, data, size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write prod_test_firmware to flash\n"); + return retval; + } + + return 0; +} + +static int reflash_write_app_firmware(void) +{ + int retval; + unsigned int size; + unsigned int flash_addr; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + const unsigned char *data; + + data = reflash_hcd->image_info.app_firmware.data; + size = reflash_hcd->image_info.app_firmware.size; + flash_addr = reflash_hcd->image_info.app_firmware.flash_addr; + + retval = reflash_write_flash(flash_addr, data, size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write app_firmware to flash\n"); + return retval; + } + + return 0; +} + + +static int reflash_erase_flash(unsigned int page_start, unsigned int page_count) +{ + int retval; + unsigned char out_buf[4] = {0}; + int size_erase_cmd; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + if ((page_start > 0xff) || (page_count > 0xff)) { + size_erase_cmd = 4; + + out_buf[0] = (unsigned char)(page_start & 0xff); + out_buf[1] = (unsigned char)((page_start >> 8) & 0xff); + out_buf[2] = (unsigned char)(page_count & 0xff); + out_buf[3] = (unsigned char)((page_count >> 8) & 0xff); + } else { + size_erase_cmd = 2; + + out_buf[0] = (unsigned char)(page_start & 0xff); + out_buf[1] = (unsigned char)(page_count & 0xff); + } + + LOCK_BUFFER(reflash_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_ERASE_FLASH, + out_buf, + size_erase_cmd, + &reflash_hcd->resp.buf, + &reflash_hcd->resp.buf_size, + &reflash_hcd->resp.data_length, + NULL, + ERASE_FLASH_DELAY_MS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_ERASE_FLASH)); + UNLOCK_BUFFER(reflash_hcd->resp); + return retval; + } + + UNLOCK_BUFFER(reflash_hcd->resp); + + return 0; +} + + +static int reflash_erase(unsigned int flash_addr, unsigned int size) +{ + int retval; + unsigned int page_start; + unsigned int page_count; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + page_start = flash_addr / reflash_hcd->page_size; + + page_count = ceil_div(size, reflash_hcd->page_size); + + LOGD(tcm_hcd->pdev->dev.parent, + "Page start = %d (0x%04x)\n", + page_start, page_start); + + LOGD(tcm_hcd->pdev->dev.parent, + "Page count = %d (0x%04x)\n", + page_count, page_count); + + retval = reflash_erase_flash(page_start, page_count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase pages, addr = 0x%04x, count = %d\n", + page_start, page_count); + return retval; + } + + return 0; +} + +static int reflash_erase_app_config(void) +{ + int retval; + unsigned int size; + unsigned int flash_addr; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + flash_addr = reflash_hcd->image_info.app_config.flash_addr; + + size = reflash_hcd->image_info.app_config.size; + + retval = reflash_erase(flash_addr, size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase app_config\n"); + return retval; + } + + return 0; +} + +static int reflash_erase_disp_config(void) +{ + int retval; + unsigned int size; + unsigned int flash_addr; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + flash_addr = reflash_hcd->image_info.disp_config.flash_addr; + + size = reflash_hcd->image_info.disp_config.size; + + retval = reflash_erase(flash_addr, size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase disp_config\n"); + return retval; + } + + return 0; +} + +static int reflash_erase_prod_test_firmware(void) +{ + int retval; + unsigned int size; + unsigned int flash_addr; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + flash_addr = reflash_hcd->image_info.prod_test_firmware.flash_addr; + + size = reflash_hcd->image_info.prod_test_firmware.size; + + retval = reflash_erase(flash_addr, size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase prod_test_firmware\n"); + return retval; + } + + return 0; +} + +static int reflash_erase_app_firmware(void) +{ + int retval; + unsigned int size; + unsigned int flash_addr; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + flash_addr = reflash_hcd->image_info.app_firmware.flash_addr; + + size = reflash_hcd->image_info.app_firmware.size; + + retval = reflash_erase(flash_addr, size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase app_firmware\n"); + return retval; + } + + return 0; +} + + +static int reflash_update_custom_otp(const unsigned char *data, + unsigned int offset, unsigned int datalen) +{ + int retval; + unsigned int temp; + unsigned int addr; + unsigned int length; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, false); +#endif + + temp = le2_to_uint(tcm_hcd->boot_info.custom_otp_start_block); + addr = temp * reflash_hcd->write_block_size; + + temp = le2_to_uint(tcm_hcd->boot_info.custom_otp_length_blocks); + length = temp * reflash_hcd->write_block_size; + + if (addr == 0 || length == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Data area unavailable\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + if (datalen + offset > length) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid data length\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + retval = reflash_write_flash(addr + offset, + data, + datalen); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to flash\n"); + goto run_app_firmware; + } + + retval = 0; + +run_app_firmware: + if (tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware\n"); + } + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, true); +#endif + + return retval; +} + +static int reflash_update_custom_lcm(const unsigned char *data, + unsigned int offset, unsigned int datalen) +{ + int retval; + unsigned int addr; + unsigned int length; + unsigned int page_start; + unsigned int page_count; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = tcm_hcd->get_data_location(tcm_hcd, + CUSTOM_LCM, + &addr, + &length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get data location\n"); + return retval; + } + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, false); +#endif + + addr *= reflash_hcd->write_block_size; + length *= reflash_hcd->write_block_size; + + if (addr == 0 || length == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Data area unavailable\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + if (datalen + offset > length) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid data length\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + if (offset == 0) { + page_start = addr / reflash_hcd->page_size; + + page_count = ceil_div(length, reflash_hcd->page_size); + + retval = reflash_erase_flash(page_start, page_count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase flash pages\n"); + goto run_app_firmware; + } + } + + retval = reflash_write_flash(addr + offset, + data, + datalen); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to flash\n"); + goto run_app_firmware; + } + + retval = 0; + +run_app_firmware: + if (tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware\n"); + } + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, true); +#endif + + return retval; +} + +static int reflash_update_custom_oem(const unsigned char *data, + unsigned int offset, unsigned int datalen) +{ + int retval; + unsigned int addr; + unsigned int length; + unsigned int page_start; + unsigned int page_count; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = tcm_hcd->get_data_location(tcm_hcd, + CUSTOM_OEM, + &addr, + &length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get data location\n"); + return retval; + } + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, false); +#endif + + addr *= reflash_hcd->write_block_size; + length *= reflash_hcd->write_block_size; + + if (addr == 0 || length == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Data area unavailable\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + if (datalen + offset > length) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid data length\n"); + retval = -EINVAL; + goto run_app_firmware; + } + + if (offset == 0) { + page_start = addr / reflash_hcd->page_size; + + page_count = ceil_div(length, reflash_hcd->page_size); + + retval = reflash_erase_flash(page_start, page_count); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase flash pages\n"); + goto run_app_firmware; + } + } + + retval = reflash_write_flash(addr + offset, + data, + datalen); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to flash\n"); + goto run_app_firmware; + } + + retval = 0; + +run_app_firmware: + if (tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware\n"); + } + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, true); +#endif + + return retval; +} + +static int reflash_update_boot_config(bool lock) +{ + int retval; + unsigned char slot_used; + unsigned int idx; + unsigned int addr = 0; + struct boot_config *data; + struct boot_config *last_slot; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, false); +#endif + + retval = reflash_check_boot_config(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed boot_config partition check\n"); + goto reset; + } + + retval = reflash_read_data(BOOT_CONFIG, false, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read boot config\n"); + goto reset; + } + + LOCK_BUFFER(reflash_hcd->read); + + data = (struct boot_config *)reflash_hcd->read.buf; + last_slot = data + (BOOT_CONFIG_SLOTS - 1); + slot_used = tcm_hcd->id_info.mode == MODE_TDDI_BOOTLOADER ? 0 : 1; + + if (last_slot->used == slot_used) { + LOGE(tcm_hcd->pdev->dev.parent, + "Boot config already locked down\n"); + UNLOCK_BUFFER(reflash_hcd->read); + goto reset; + } + + if (lock) { + idx = BOOT_CONFIG_SLOTS - 1; + } else { + for (idx = 0; idx < BOOT_CONFIG_SLOTS; idx++) { + if (data->used == slot_used) { + data++; + continue; + } else { + break; + } + } + } + + UNLOCK_BUFFER(reflash_hcd->read); + + if (idx == BOOT_CONFIG_SLOTS) { + LOGE(tcm_hcd->pdev->dev.parent, + "No free boot config slot available\n"); + goto reset; + } + + addr += idx * BOOT_CONFIG_SIZE; + + retval = reflash_write_flash(addr, + reflash_hcd->image_info.boot_config.data, + BOOT_CONFIG_SIZE); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write to flash\n"); + goto reset; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Slot %d updated with new boot config\n", + idx); + + retval = 0; + +reset: + if (tcm_hcd->reset(tcm_hcd) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, true); +#endif + + return retval; +} + +static int reflash_update_app_config(bool do_reset) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, false); +#endif + + retval = reflash_check_app_config(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to check app_config partition\n"); + do_reset = true; + goto reset; + } + + retval = reflash_erase_app_config(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase app_config partition\n"); + do_reset = true; + goto reset; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "app_config partition erased\n"); + + retval = reflash_write_app_config(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write app_config partition\n"); + do_reset = true; + goto reset; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "app_config partition written\n"); + + retval = 0; + +reset: + if (!do_reset) + goto exit; + + if (tcm_hcd->reset_n_reinit(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + +exit: +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, true); +#endif + + return retval; +} + +static int reflash_update_disp_config(bool do_reset) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, false); +#endif + + retval = reflash_check_disp_config(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to check disp_config partition\n"); + do_reset = true; + goto reset; + } + + retval = reflash_erase_disp_config(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase disp_config partition\n"); + do_reset = true; + goto reset; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "disp_config partition erased\n"); + + retval = reflash_write_disp_config(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write disp_config partition\n"); + do_reset = true; + goto reset; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "disp_config partition written\n"); + + retval = 0; + +reset: + if (!do_reset) + goto exit; + + if (tcm_hcd->reset_n_reinit(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + +exit: +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, true); +#endif + + return retval; +} + +static int reflash_update_prod_test_firmware(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, false); +#endif + + retval = reflash_check_prod_test_firmware(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to check prod_test_firmware partition\n"); + goto reset; + } + + retval = reflash_erase_prod_test_firmware(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase prod_test_firmware partition\n"); + goto reset; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "prod_test_firmware partition erased\n"); + + retval = reflash_write_prod_test_firmware(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write prod_test_firmware partition\n"); + goto reset; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "prod_test_firmware partition written\n"); + + retval = 0; + +reset: + if (tcm_hcd->reset_n_reinit(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, true); +#endif + + return retval; +} + +static int reflash_update_app_firmware(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = reflash_set_up_flash_access(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to set up flash access\n"); + return retval; + } + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, false); +#endif + + retval = reflash_check_app_firmware(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to check app_firmware partition\n"); + goto reset; + } + + retval = reflash_erase_app_firmware(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to erase app_firmware partition\n"); + goto reset; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "app_firmware partition erased\n"); + + retval = reflash_write_app_firmware(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write app_firmware partition\n"); + goto reset; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "app_firmware partition written\n"); + + retval = 0; + +reset: + if (tcm_hcd->reset_n_reinit(tcm_hcd, false, true) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, true); +#endif + + return retval; +} + + +static int reflash_do_reflash(void) +{ + int retval; + enum update_area update_area; + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + + retval = reflash_get_fw_image(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get firmware image\n"); + goto exit; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Start of reflash\n"); + + atomic_set(&tcm_hcd->firmware_flashing, 1); + + update_area = reflash_compare_id_info(); + + switch (update_area) { + case FIRMWARE_CONFIG: + retval = reflash_update_app_firmware(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reflash application firmware\n"); + goto exit; + } + memset(&tcm_hcd->app_info, 0x00, sizeof(tcm_hcd->app_info)); + if (tcm_hcd->features.dual_firmware) { + retval = reflash_update_prod_test_firmware(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reflash production test firmware\n"); + goto exit; + } + } + case CONFIG_ONLY: + if (reflash_hcd->disp_cfg_update) { + retval = reflash_update_disp_config(false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reflash display config\n"); + goto exit; + } + } + retval = reflash_update_app_config(true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to reflash application config\n"); + goto exit; + } + break; + case NONE: + default: + break; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "End of reflash\n"); + + retval = 0; + +exit: + if (reflash_hcd->fw_entry) { + release_firmware(reflash_hcd->fw_entry); + reflash_hcd->fw_entry = NULL; + reflash_hcd->image = NULL; + reflash_hcd->image_size = 0; + } + + atomic_set(&tcm_hcd->firmware_flashing, 0); + wake_up_interruptible(&tcm_hcd->reflash_wq); + return retval; +} + +#ifdef STARTUP_REFLASH +static void reflash_startup_work(struct work_struct *work) +{ + int retval; +#ifdef CONFIG_FB + unsigned int timeout; +#endif + struct syna_tcm_hcd *tcm_hcd = reflash_hcd->tcm_hcd; + +#ifdef CONFIG_FB + timeout = FB_READY_TIMEOUT_S * 1000 / FB_READY_WAIT_MS; + + while (tcm_hcd->fb_ready != FB_READY_COUNT) { + if (timeout == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Timed out waiting for FB ready\n"); + return; + } + msleep(FB_READY_WAIT_MS); + timeout--; + } +#endif + + pm_stay_awake(&tcm_hcd->pdev->dev); + + mutex_lock(&reflash_hcd->reflash_mutex); + + retval = reflash_do_reflash(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reflash\n"); + } + + mutex_unlock(&reflash_hcd->reflash_mutex); + + pm_relax(&tcm_hcd->pdev->dev); + + return; +} +#endif + +static int reflash_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval = -1; + int idx; + + reflash_hcd = NULL; + if (tcm_hcd->in_hdl_mode) + return 0; + + reflash_hcd = vzalloc(sizeof(*reflash_hcd)); + if (!reflash_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for reflash_hcd\n"); + return -ENOMEM; + } + + reflash_hcd->image_buf = kzalloc(IMAGE_BUF_SIZE, GFP_KERNEL); + if (!reflash_hcd->image_buf) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for reflash_hcd->image_buf\n"); + goto err_allocate_memory; + } + + reflash_hcd->tcm_hcd = tcm_hcd; + + reflash_hcd->force_update = FORCE_REFLASH; +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + tcm_hcd->set_fw_name = syna_ts_set_fwname; + tcm_hcd->do_fw_update = syna_ts_do_reflash; +#endif + + mutex_init(&reflash_hcd->reflash_mutex); + + INIT_BUFFER(reflash_hcd->out, false); + INIT_BUFFER(reflash_hcd->resp, false); + INIT_BUFFER(reflash_hcd->read, false); + +#ifdef STARTUP_REFLASH + reflash_hcd->workqueue = + create_singlethread_workqueue("syna_tcm_reflash"); + INIT_WORK(&reflash_hcd->work, reflash_startup_work); + queue_work(reflash_hcd->workqueue, &reflash_hcd->work); +#endif + + if (ENABLE_SYS_REFLASH == false) + goto init_finished; + + reflash_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME, + tcm_hcd->sysfs_dir); + if (!reflash_hcd->sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs directory\n"); + retval = -EINVAL; + goto err_sysfs_create_dir; + } + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + retval = sysfs_create_file(reflash_hcd->sysfs_dir, + &(*attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs file\n"); + goto err_sysfs_create_file; + } + } + + retval = sysfs_create_bin_file(reflash_hcd->sysfs_dir, &bin_attrs[0]); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs bin file\n"); + goto err_sysfs_create_bin_file; + } + + reflash_hcd->custom_dir = kobject_create_and_add(CUSTOM_DIR_NAME, + reflash_hcd->sysfs_dir); + if (!reflash_hcd->custom_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create custom sysfs directory\n"); + retval = -EINVAL; + goto err_custom_sysfs_create_dir; + } + + for (idx = 1; idx < ARRAY_SIZE(bin_attrs); idx++) { + retval = sysfs_create_bin_file(reflash_hcd->custom_dir, + &bin_attrs[idx]); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs bin file\n"); + goto err_custom_sysfs_create_bin_file; + } + } + +init_finished: + tcm_hcd->read_flash_data = reflash_read_data; + + return 0; + +err_custom_sysfs_create_bin_file: + for (idx--; idx > 0; idx--) + sysfs_remove_bin_file(reflash_hcd->custom_dir, &bin_attrs[idx]); + + kobject_put(reflash_hcd->custom_dir); + + idx = ARRAY_SIZE(attrs); + +err_custom_sysfs_create_dir: + sysfs_remove_bin_file(reflash_hcd->sysfs_dir, &bin_attrs[0]); + +err_sysfs_create_bin_file: +err_sysfs_create_file: + for (idx--; idx >= 0; idx--) + sysfs_remove_file(reflash_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(reflash_hcd->sysfs_dir); + +err_sysfs_create_dir: +err_allocate_memory: + kfree(reflash_hcd->image_buf); + + RELEASE_BUFFER(reflash_hcd->read); + RELEASE_BUFFER(reflash_hcd->resp); + RELEASE_BUFFER(reflash_hcd->out); + + vfree(reflash_hcd); + reflash_hcd = NULL; + + return retval; +} + +static int reflash_remove(struct syna_tcm_hcd *tcm_hcd) +{ + int idx; + + if (!reflash_hcd) + goto exit; + + tcm_hcd->read_flash_data = NULL; + + if (ENABLE_SYS_REFLASH == true) { + for (idx = 1; idx < ARRAY_SIZE(bin_attrs); idx++) { + sysfs_remove_bin_file(reflash_hcd->custom_dir, + &bin_attrs[idx]); + } + + kobject_put(reflash_hcd->custom_dir); + + sysfs_remove_bin_file(reflash_hcd->sysfs_dir, &bin_attrs[0]); + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + sysfs_remove_file(reflash_hcd->sysfs_dir, + &(*attrs[idx]).attr); + } + + kobject_put(reflash_hcd->sysfs_dir); + } + +#ifdef STARTUP_REFLASH + cancel_work_sync(&reflash_hcd->work); + flush_workqueue(reflash_hcd->workqueue); + destroy_workqueue(reflash_hcd->workqueue); +#endif + if (reflash_hcd->image_buf) + kfree(reflash_hcd->image_buf); + + RELEASE_BUFFER(reflash_hcd->read); + RELEASE_BUFFER(reflash_hcd->resp); + RELEASE_BUFFER(reflash_hcd->out); + vfree(reflash_hcd); + reflash_hcd = NULL; + +exit: + complete(&reflash_remove_complete); + + return 0; +} + +static int reflash_reinit(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!reflash_hcd && !(tcm_hcd->in_hdl_mode)) { + retval = reflash_init(tcm_hcd); + return retval; + } + + return 0; +} + +static struct syna_tcm_module_cb reflash_module = { + .type = TCM_REFLASH, + .init = reflash_init, + .remove = reflash_remove, + .syncbox = NULL, +#ifdef REPORT_NOTIFIER + .asyncbox = NULL, +#endif + .reinit = reflash_reinit, + .suspend = NULL, + .resume = NULL, + .early_suspend = NULL, +}; + +static int __init reflash_module_init(void) +{ + return syna_tcm_add_module(&reflash_module, true); +} + +static void __exit reflash_module_exit(void) +{ + syna_tcm_add_module(&reflash_module, false); + + wait_for_completion(&reflash_remove_complete); + + return; +} + +module_init(reflash_module_init); +module_exit(reflash_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Reflash Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_testing.c b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_testing.c new file mode 100644 index 000000000000..ab6653241555 --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_testing.c @@ -0,0 +1,1494 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * Copyright (C) 2018-2019 Ian Su + * Copyright (C) 2018-2019 Joey Zhou + * Copyright (C) 2018-2019 Yuehao Qiu + * Copyright (C) 2018-2019 Aaron Chen + * + * 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. + */ + +#include +#include "../synaptics_core/synaptics_core.h" +#include "synaptics_testing.h" + +#define SYSFS_DIR_NAME "testing" + +#define REPORT_TIMEOUT_MS 5000 + +#define testing_sysfs_show(t_name) \ +static ssize_t testing_sysfs_##t_name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + int retval; \ + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; \ +\ + mutex_lock(&tcm_hcd->extif_mutex); \ +\ + retval = testing_##t_name(); \ + if (retval < 0) { \ + LOGE(tcm_hcd->pdev->dev.parent, \ + "Failed to do "#t_name" test\n"); \ + goto exit; \ + } \ +\ + LOCK_BUFFER(testing_hcd->resp); \ + memcpy(buf, testing_hcd->resp.buf, testing_hcd->resp.data_length); \ + retval = testing_hcd->resp.data_length; \ + UNLOCK_BUFFER(testing_hcd->resp); \ +\ +exit: \ + mutex_unlock(&tcm_hcd->extif_mutex); \ +\ + return retval; \ +} + +#define CHECK_BIT(var, pos) ((var) & (1<<(pos))) + +/* Sync with the Comm2 Release 21 */ +/* Not all are implemented for every ASIC */ +enum test_code { + TEST_NOT_IMPLEMENTED = 0x00, + TEST_PT1_TRX_TRX_SHORTS = 0x01, + TEST_PT2_TRX_SENSOR_OPENS = 0x02, + TEST_PT3_TRX_GROUND_SHORTS = 0x03, + TEST_PT5_FULL_RAW_CAP = 0x05, + TEST_PT6_EE_SHORT = 0x06, + TEST_PT7_DYNAMIC_RANGE = 0x07, + TEST_PT8_HIGH_RESISTANCE = 0x08, + TEST_PT10_DELTA_NOISE = 0x0a, + TEST_PT11_OPEN_DETECTION = 0x0b, + TEST_PT12 = 0x0c, + TEST_PT13 = 0x0d, + TEST_PT14_DOZE_DYNAMIC_RANGE = 0x0e, + TEST_PT15_DOZE_NOISE = 0x0f, + TEST_PT16_SENSOR_SPEED = 0x10, + TEST_PT17_ADC_RANGE = 0x11, + TEST_PT18_HYBRID_ABS_RAW = 0x12, + TEST_PT29_HYBRID_ABS_NOISE = 0x1D, +}; + +struct testing_hcd { + bool result; + unsigned char report_type; + unsigned int report_index; + unsigned int num_of_reports; + struct kobject *sysfs_dir; + struct syna_tcm_buffer out; + struct syna_tcm_buffer resp; + struct syna_tcm_buffer report; + struct syna_tcm_buffer process; + struct syna_tcm_buffer output; + struct syna_tcm_hcd *tcm_hcd; + int (*collect_reports)(enum report_type report_type, + unsigned int num_of_reports); + +#ifdef PT1_GET_PIN_ASSIGNMENT +#define MAX_PINS (64) + unsigned char *satic_cfg_buf; + short tx_pins[MAX_PINS]; + short tx_assigned; + short rx_pins[MAX_PINS]; + short rx_assigned; + short guard_pins[MAX_PINS]; + short guard_assigned; +#endif + +}; + +DECLARE_COMPLETION(report_complete); + +DECLARE_COMPLETION(testing_remove_complete); + +static struct testing_hcd *testing_hcd; + + +/* testing implementation */ +static int testing_device_id(void); + +static int testing_config_id(void); + +static int testing_reset_open(void); + +static int testing_pt01_trx_trx_short(void); + +static int testing_pt05_full_raw(void); + +static int testing_pt07_dynamic_range(void); + +static int testing_pt10_noise(void); + +static int testing_pt11_open_detection(void); + +SHOW_PROTOTYPE(testing, size) + +/* nodes for testing */ +SHOW_PROTOTYPE(testing, device_id) +SHOW_PROTOTYPE(testing, config_id) +SHOW_PROTOTYPE(testing, reset_open) +SHOW_PROTOTYPE(testing, pt01_trx_trx_short) +SHOW_PROTOTYPE(testing, pt05_full_raw) +SHOW_PROTOTYPE(testing, pt07_dynamic_range) +SHOW_PROTOTYPE(testing, pt10_noise) +SHOW_PROTOTYPE(testing, pt11_open_detection) + + +static struct device_attribute *attrs[] = { + ATTRIFY(size), + ATTRIFY(device_id), + ATTRIFY(config_id), + ATTRIFY(pt01_trx_trx_short), + ATTRIFY(pt05_full_raw), + ATTRIFY(pt07_dynamic_range), + ATTRIFY(pt10_noise), + ATTRIFY(pt11_open_detection), + ATTRIFY(reset_open), +}; + +static ssize_t testing_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count); + +static struct bin_attribute bin_attr = { + .attr = { + .name = "data", + .mode = S_IRUGO, + }, + .size = 0, + .read = testing_sysfs_data_show, +}; + +testing_sysfs_show(device_id) + +testing_sysfs_show(config_id) + +testing_sysfs_show(pt01_trx_trx_short) + +testing_sysfs_show(pt05_full_raw) + +testing_sysfs_show(pt07_dynamic_range) + +testing_sysfs_show(pt10_noise) + +testing_sysfs_show(pt11_open_detection) + +testing_sysfs_show(reset_open) + + +static ssize_t testing_sysfs_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + LOCK_BUFFER(testing_hcd->output); + + retval = snprintf(buf, PAGE_SIZE, + "%u\n", + testing_hcd->output.data_length); + + UNLOCK_BUFFER(testing_hcd->output); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static ssize_t testing_sysfs_data_show(struct file *data_file, + struct kobject *kobj, struct bin_attribute *attributes, + char *buf, loff_t pos, size_t count) +{ + int retval; + unsigned int readlen; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + mutex_lock(&tcm_hcd->extif_mutex); + + LOCK_BUFFER(testing_hcd->output); + + readlen = MIN(count, testing_hcd->output.data_length - pos); + + retval = secure_memcpy(buf, + count, + &testing_hcd->output.buf[pos], + testing_hcd->output.buf_size - pos, + readlen); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy report data\n"); + } else { + retval = readlen; + } + + UNLOCK_BUFFER(testing_hcd->output); + + mutex_unlock(&tcm_hcd->extif_mutex); + + return retval; +} + +static int testing_run_prod_test_item(enum test_code test_code) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + if (tcm_hcd->features.dual_firmware && + tcm_hcd->id_info.mode != MODE_PRODUCTIONTEST_FIRMWARE) { + retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_PRODUCTION_TEST); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run production test firmware\n"); + return retval; + } + } else if (IS_NOT_FW_MODE(tcm_hcd->id_info.mode) || + tcm_hcd->app_status != APP_STATUS_OK) { + LOGE(tcm_hcd->pdev->dev.parent, + "Identifying mode = 0x%02x\n", + tcm_hcd->id_info.mode); + return -ENODEV; + } + + LOCK_BUFFER(testing_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->out, + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd->out.buf\n"); + UNLOCK_BUFFER(testing_hcd->out); + return retval; + } + + testing_hcd->out.buf[0] = test_code; + + LOCK_BUFFER(testing_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_PRODUCTION_TEST, + testing_hcd->out.buf, + 1, + &testing_hcd->resp.buf, + &testing_hcd->resp.buf_size, + &testing_hcd->resp.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_PRODUCTION_TEST)); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + return retval; + } + + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + + return 0; +} + +static int testing_collect_reports(enum report_type report_type, + unsigned int num_of_reports) +{ + int retval; + bool completed; + unsigned int timeout; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + testing_hcd->report_index = 0; + testing_hcd->report_type = report_type; + testing_hcd->num_of_reports = num_of_reports; + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 13, 0)) + reinit_completion(&report_complete); +#else + INIT_COMPLETION(report_complete); +#endif + + LOCK_BUFFER(testing_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->out, + 1); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd->out.buf\n"); + UNLOCK_BUFFER(testing_hcd->out); + goto exit; + } + + testing_hcd->out.buf[0] = testing_hcd->report_type; + + LOCK_BUFFER(testing_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_ENABLE_REPORT, + testing_hcd->out.buf, + 1, + &testing_hcd->resp.buf, + &testing_hcd->resp.buf_size, + &testing_hcd->resp.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_ENABLE_REPORT)); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + goto exit; + } + + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + + completed = false; + timeout = REPORT_TIMEOUT_MS * num_of_reports; + + retval = wait_for_completion_timeout(&report_complete, + msecs_to_jiffies(timeout)); + if (retval == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Timed out waiting for report collection\n"); + } else { + completed = true; + } + + LOCK_BUFFER(testing_hcd->out); + + testing_hcd->out.buf[0] = testing_hcd->report_type; + + LOCK_BUFFER(testing_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_DISABLE_REPORT, + testing_hcd->out.buf, + 1, + &testing_hcd->resp.buf, + &testing_hcd->resp.buf_size, + &testing_hcd->resp.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_DISABLE_REPORT)); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + goto exit; + } + + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + + if (completed) + retval = 0; + else + retval = -EIO; + +exit: + testing_hcd->report_type = 0; + + return retval; +} + +static void testing_get_frame_size_words(unsigned int *size, bool image_only) +{ + unsigned int rows; + unsigned int cols; + unsigned int hybrid; + unsigned int buttons; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + hybrid = le2_to_uint(app_info->has_hybrid_data); + buttons = le2_to_uint(app_info->num_of_buttons); + + *size = rows * cols; + + if (!image_only) { + if (hybrid) + *size += rows + cols; + *size += buttons; + } + + return; +} + +static void testing_standard_frame_output(bool image_only) +{ + int retval; + unsigned int data_size; + unsigned int header_size; + unsigned int output_size; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + app_info = &tcm_hcd->app_info; + + testing_get_frame_size_words(&data_size, image_only); + + header_size = sizeof(app_info->num_of_buttons) + + sizeof(app_info->num_of_image_rows) + + sizeof(app_info->num_of_image_cols) + + sizeof(app_info->has_hybrid_data); + + output_size = header_size + data_size * 2; + + LOCK_BUFFER(testing_hcd->output); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->output, + output_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd->output.buf\n"); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + retval = secure_memcpy(testing_hcd->output.buf, + testing_hcd->output.buf_size, + &app_info->num_of_buttons[0], + header_size, + header_size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy header data\n"); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + output_size = header_size; + + LOCK_BUFFER(testing_hcd->resp); + + retval = secure_memcpy(testing_hcd->output.buf + header_size, + testing_hcd->output.buf_size - header_size, + testing_hcd->resp.buf, + testing_hcd->resp.buf_size, + testing_hcd->resp.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy test data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->output); + return; + } + + output_size += testing_hcd->resp.data_length; + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_hcd->output.data_length = output_size; + + UNLOCK_BUFFER(testing_hcd->output); + + return; +} + +static int testing_device_id(void) +{ + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + struct syna_tcm_identification *id_info; + char *strptr = NULL; + + LOGN(tcm_hcd->pdev->dev.parent, + "Start testing\n"); + testing_hcd->result = false; + + id_info = &tcm_hcd->id_info; + + strptr = strnstr(id_info->part_number, + device_id_limit, + sizeof(id_info->part_number)); + if (strptr != NULL) + testing_hcd->result = true; + else + LOGE(tcm_hcd->pdev->dev.parent, + "Device ID is mismatching, FW: %s (%s)\n", + id_info->part_number, device_id_limit); + + LOGN(tcm_hcd->pdev->dev.parent, + "Result = %s\n", (testing_hcd->result)?"pass":"fail"); + return 0; +} + +static int testing_config_id(void) +{ + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + struct syna_tcm_app_info *app_info; + int i; + + LOGN(tcm_hcd->pdev->dev.parent, + "Start testing\n"); + testing_hcd->result = false; + + app_info = &tcm_hcd->app_info; + + testing_hcd->result = true; + for (i = 0; i < sizeof(config_id_limit); i++) { + if (config_id_limit[i] != + tcm_hcd->app_info.customer_config_id[i]) { + LOGE(tcm_hcd->pdev->dev.parent, + "Config ID is mismatching at byte %d\n", + i); + testing_hcd->result = false; + } + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Result = %s\n", (testing_hcd->result)?"pass":"fail"); + return 0; +} + +#ifdef PT1_GET_PIN_ASSIGNMENT + +static int testing_get_static_config(unsigned char *buf, unsigned int buf_len) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + if (!buf) { + LOGE(tcm_hcd->pdev->dev.parent, + "invalid parameter\n"); + return -EINVAL; + } + + LOCK_BUFFER(testing_hcd->out); + LOCK_BUFFER(testing_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_GET_STATIC_CONFIG, + NULL, + 0, + &testing_hcd->resp.buf, + &testing_hcd->resp.buf_size, + &testing_hcd->resp.data_length, + NULL, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_GET_STATIC_CONFIG)); + goto exit; + } + + if (testing_hcd->resp.data_length != buf_len) { + LOGE(tcm_hcd->pdev->dev.parent, + "Cfg size mismatch\n"); + retval = -EINVAL; + goto exit; + } + + retval = secure_memcpy(buf, + buf_len, + testing_hcd->resp.buf, + testing_hcd->resp.buf_size, + buf_len); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy cfg data\n"); + goto exit; + } + +exit: + UNLOCK_BUFFER(testing_hcd->resp); + UNLOCK_BUFFER(testing_hcd->out); + + return retval; +} + +static bool testing_is_pins_assigned(int pin) +{ + int i; + short *tx_pins = testing_hcd->tx_pins; + short tx_assigned = testing_hcd->tx_assigned; + short *rx_pins = testing_hcd->rx_pins; + short rx_assigned = testing_hcd->rx_assigned; + short *guard_pins = testing_hcd->guard_pins; + short guard_assigned = testing_hcd->guard_assigned; + + for (i = 0; i < tx_assigned; i++) { + if (pin == tx_pins[i]) + return true; + } + for (i = 0; i < rx_assigned; i++) { + if (pin == rx_pins[i]) + return true; + } + for (i = 0; i < guard_assigned; i++) { + if (pin == guard_pins[i]) + return true; + } + + return false; +} + +static int testing_get_pins_mapping(unsigned char *cfg_data, int length) +{ + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + int i, j = 0; + int idx; + + int offset_tx_pin = cfg_imagetxes.offset/8; + int length_tx_pin = cfg_imagetxes.length/8; + int offset_rx_pin = cfg_imagerxes.offset/8; + int length_rx_pin = cfg_imagerxes.length/8; + + int num_tx_guard = 0; + int offset_num_tx_guard = cfg_numtxguards.offset/8; + int length_num_tx_guard = cfg_numtxguards.length/8; + int offset_tx_guard = cfg_txguardpins.offset/8; + int length_tx_guard = cfg_txguardpins.length/8; + + int num_rx_guard = 0; + int offset_num_rx_guard = cfg_numrxguards.offset/8; + int length_num_rx_guard = cfg_numrxguards.length/8; + int offset_rx_guard = cfg_rxguardpins.offset/8; + int length_rx_guard = cfg_rxguardpins.length/8; + + if (!cfg_data) { + LOGE(tcm_hcd->pdev->dev.parent, + "invalid parameter\n"); + return -EINVAL; + } + + testing_hcd->tx_assigned = 0; + testing_hcd->rx_assigned = 0; + testing_hcd->guard_assigned = 0; + + /* get tx pins mapping */ + if (0 == (offset_tx_pin + length_tx_pin)) + goto get_rx_pins; + + if (length > offset_tx_pin + length_tx_pin) { + + testing_hcd->tx_assigned = (length_tx_pin/2); + + idx = 0; + for (i = 0; i < (length_tx_pin/2); i++) { + testing_hcd->tx_pins[i] = + (short)cfg_data[offset_tx_pin + idx] | + (short)(cfg_data[offset_tx_pin + idx + 1] << 8); + idx += 2; + + LOGD(tcm_hcd->pdev->dev.parent, + "tx[%d] = %2d\n", + i, testing_hcd->tx_pins[i]); + } + } + +get_rx_pins: + /* get rx pins mapping */ + if (0 == (offset_rx_pin + length_rx_pin)) + goto get_num_tx_guards; + + if (length > offset_rx_pin + length_rx_pin) { + + testing_hcd->rx_assigned = (length_rx_pin/2); + + idx = 0; + for (i = 0; i < (length_rx_pin/2); i++) { + testing_hcd->rx_pins[i] = + (short)cfg_data[offset_rx_pin + idx] | + (short)(cfg_data[offset_rx_pin + idx + 1] << 8); + idx += 2; + + LOGD(tcm_hcd->pdev->dev.parent, + "rx[%d] = %2d\n", + i, testing_hcd->rx_pins[i]); + } + } + +get_num_tx_guards: + /* get number of tx guards */ + if (0 == (offset_num_tx_guard + length_num_tx_guard)) + goto get_num_rx_guards; + + if (length > offset_num_tx_guard + length_num_tx_guard) { + + num_tx_guard = (short)cfg_data[offset_num_tx_guard] | + (short)(cfg_data[offset_num_tx_guard + 1] << 8); + + testing_hcd->guard_assigned += num_tx_guard; + } + +get_num_rx_guards: + /* get number of rx guards */ + if (0 == (offset_num_rx_guard + length_num_rx_guard)) + goto get_guards; + + if (length > offset_num_rx_guard + length_num_rx_guard) { + + num_rx_guard = (short)cfg_data[offset_num_rx_guard] | + (short)(cfg_data[offset_num_rx_guard + 1] << 8); + + testing_hcd->guard_assigned += num_rx_guard; + } + +get_guards: + if (testing_hcd->guard_assigned > 0) + LOGD(tcm_hcd->pdev->dev.parent, + "num of guards = %2d\n", + testing_hcd->guard_assigned); + + /* get tx guards mapping */ + if ((num_tx_guard > 0) && + (length > offset_tx_guard + length_tx_guard)) { + idx = 0; + for (i = 0; i < num_tx_guard; i++) { + testing_hcd->guard_pins[j] = + (short)cfg_data[offset_tx_guard + idx] | + (short)(cfg_data[offset_tx_guard + idx + 1] << 8); + + LOGD(tcm_hcd->pdev->dev.parent, + "guard_pins[%d] = %2d\n", + i, testing_hcd->guard_pins[j]); + idx += 2; + j += 1; + } + } + + /* get rx guards mapping */ + if ((num_rx_guard > 0) && + (length > offset_rx_guard + length_rx_guard)) { + idx = 0; + for (i = 0; i < num_rx_guard; i++) { + testing_hcd->guard_pins[j] = + (short)cfg_data[offset_rx_guard + idx] | + (short)(cfg_data[offset_rx_guard + idx + 1] << 8); + + LOGD(tcm_hcd->pdev->dev.parent, + "guard_pins[%d] = %2d\n", + i, testing_hcd->guard_pins[j]); + idx += 2; + j += 1; + } + } + + return 0; +} + +#endif /* end of PT1_GET_PIN_ASSIGNMENT */ + +static int testing_pt01_trx_trx_short(void) +{ + int retval; + int i, j; + int phy_pin; + bool do_pin_test = false; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + unsigned char limit; + unsigned char *buf; + unsigned char data; +#ifdef PT1_GET_PIN_ASSIGNMENT + unsigned char *satic_cfg_buf = NULL; + unsigned int satic_cfg_length; +#endif + + LOGN(tcm_hcd->pdev->dev.parent, + "Start testing\n"); + + app_info = &tcm_hcd->app_info; + +#ifdef PT1_GET_PIN_ASSIGNMENT + satic_cfg_length = le2_to_uint(app_info->static_config_size); + + if (!testing_hcd->satic_cfg_buf) { + satic_cfg_buf = kzalloc(satic_cfg_length, GFP_KERNEL); + if (!satic_cfg_buf) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for satic_cfg_buf\n"); + goto exit; + } + + retval = testing_get_static_config(satic_cfg_buf, + satic_cfg_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get static config\n"); + goto exit; + } + + testing_hcd->satic_cfg_buf = satic_cfg_buf; + } + + if ((testing_hcd->tx_assigned <= 0) || + (testing_hcd->rx_assigned <= 0) || + (testing_hcd->guard_assigned <= 0)) { + + if (!testing_hcd->satic_cfg_buf) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get proper satic_cfg\n"); + goto exit; + } + + retval = testing_get_pins_mapping(testing_hcd->satic_cfg_buf, + satic_cfg_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get pins mapping\n"); + goto exit; + } + } +#endif + + retval = testing_run_prod_test_item(TEST_PT1_TRX_TRX_SHORTS); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + return -EIO; + } + + LOCK_BUFFER(testing_hcd->resp); + + buf = testing_hcd->resp.buf; + + for (i = 0; i < testing_hcd->resp.data_length; i++) { + + data = buf[i]; + LOGD(tcm_hcd->pdev->dev.parent, + "[%d]: 0x%02x\n", i, data); + for (j = 0; j < 8; j++) { + + phy_pin = (i*8 + j); + +#ifdef PT1_GET_PIN_ASSIGNMENT + do_pin_test = testing_is_pins_assigned(phy_pin); +#else + do_pin_test = true; +#endif + limit = CHECK_BIT(pt1_limits[i], j); + if (do_pin_test) { + if (CHECK_BIT(data, j) != limit) { + LOGE(tcm_hcd->pdev->dev.parent, + "pin-%2d : fail\n", + phy_pin); + testing_hcd->result = false; + } else + LOGD(tcm_hcd->pdev->dev.parent, + "pin-%2d : pass\n", + phy_pin); + } + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + +#ifdef PT1_GET_PIN_ASSIGNMENT + kfree(satic_cfg_buf); +#endif + + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + +static int testing_pt05_full_raw(void) +{ + int retval; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + LOGN(tcm_hcd->pdev->dev.parent, + "Start testing\n"); + testing_hcd->result = false; + + app_info = &tcm_hcd->app_info; + + retval = testing_run_prod_test_item(TEST_PT5_FULL_RAW_CAP); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + + +static int testing_pt07_dynamic_range(void) +{ + int retval; + unsigned char *buf; + unsigned int idx; + unsigned int row; + unsigned int col; + unsigned int data; + unsigned int rows; + unsigned int cols; + unsigned int limits_rows; + unsigned int limits_cols; + unsigned int frame_size_words; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + LOGN(tcm_hcd->pdev->dev.parent, + "Start testing\n"); + testing_hcd->result = false; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + + testing_get_frame_size_words(&frame_size_words, false); + + retval = testing_run_prod_test_item(TEST_PT7_DYNAMIC_RANGE); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + if (frame_size_words != testing_hcd->resp.data_length / 2) { + LOGE(tcm_hcd->pdev->dev.parent, + "Frame size mismatch\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = + sizeof(pt7_hi_limits) / sizeof(pt7_hi_limits[0]); + limits_cols = + sizeof(pt7_hi_limits[0]) / sizeof(pt7_hi_limits[0][0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = + sizeof(pt7_lo_limits) / sizeof(pt7_lo_limits[0]); + limits_cols = + sizeof(pt7_lo_limits[0]) / sizeof(pt7_lo_limits[0][0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + idx = 0; + buf = testing_hcd->resp.buf; + testing_hcd->result = true; + + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + data = le2_to_uint(&buf[idx * 2]); + if (data > pt7_hi_limits[row][col] || + data < pt7_lo_limits[row][col]) { + + LOGE(tcm_hcd->pdev->dev.parent, + "fail at (%2d, %2d) data = %5d, limit = (%4d, %4d)\n", + row, col, data, pt7_lo_limits[row][col], + pt7_hi_limits[row][col]); + + testing_hcd->result = false; + } + idx++; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_standard_frame_output(false); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Result = %s\n", (testing_hcd->result)?"pass":"fail"); + return retval; +} + +static int testing_pt10_noise(void) +{ + int retval; + unsigned int frame_size_words; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + LOGN(tcm_hcd->pdev->dev.parent, + "Start testing\n"); + testing_hcd->result = false; + + app_info = &tcm_hcd->app_info; + + testing_get_frame_size_words(&frame_size_words, true); + + retval = testing_run_prod_test_item(TEST_PT10_DELTA_NOISE); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + testing_standard_frame_output(false); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + return retval; +} + + +static int testing_pt11_open_detection(void) +{ + int retval; + short data; + unsigned char *buf; + unsigned int idx; + unsigned int row; + unsigned int col; + unsigned int rows; + unsigned int cols; + unsigned int limits_rows; + unsigned int limits_cols; + unsigned int image_size_words; + struct syna_tcm_app_info *app_info; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + LOGN(tcm_hcd->pdev->dev.parent, + "Start testing\n"); + testing_hcd->result = false; + + app_info = &tcm_hcd->app_info; + + rows = le2_to_uint(app_info->num_of_image_rows); + cols = le2_to_uint(app_info->num_of_image_cols); + + testing_get_frame_size_words(&image_size_words, true); + + retval = testing_run_prod_test_item(TEST_PT11_OPEN_DETECTION); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run test\n"); + goto exit; + } + + LOCK_BUFFER(testing_hcd->resp); + + if (image_size_words != testing_hcd->resp.data_length / 2) { + LOGE(tcm_hcd->pdev->dev.parent, + "Image size mismatch\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = + sizeof(pt11_hi_limits) / sizeof(pt11_hi_limits[0]); + limits_cols = + sizeof(pt11_hi_limits[0]) / sizeof(pt11_hi_limits[0][0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + limits_rows = + sizeof(pt11_lo_limits) / sizeof(pt11_lo_limits[0]); + limits_cols = + sizeof(pt11_lo_limits[0]) / sizeof(pt11_lo_limits[0][0]); + + if (rows > limits_rows || cols > limits_cols) { + LOGE(tcm_hcd->pdev->dev.parent, + "Mismatching limits data\n"); + UNLOCK_BUFFER(testing_hcd->resp); + retval = -EINVAL; + goto exit; + } + + idx = 0; + buf = testing_hcd->resp.buf; + testing_hcd->result = true; + + for (row = 0; row < rows; row++) { + for (col = 0; col < cols; col++) { + data = (short)le2_to_uint(&buf[idx * 2]); + if (data > pt11_hi_limits[row][col] || + data < pt11_lo_limits[row][col]) { + + LOGE(tcm_hcd->pdev->dev.parent, + "fail at (%2d, %2d) data = %5d, limit = (%4d, %4d)\n", + row, col, data, + pt11_lo_limits[row][col], + pt11_hi_limits[row][col]); + + testing_hcd->result = false; + } + idx++; + } + } + + UNLOCK_BUFFER(testing_hcd->resp); + + testing_standard_frame_output(true); + + retval = 0; + +exit: + if (tcm_hcd->features.dual_firmware) { + if (tcm_hcd->reset(tcm_hcd) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do reset\n"); + } + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Result = %s\n", (testing_hcd->result)?"pass":"fail"); + return retval; +} + + +static int testing_reset_open(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; + + LOGN(tcm_hcd->pdev->dev.parent, + "Start testing\n"); + testing_hcd->result = false; + + if (bdata->reset_gpio < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Hardware reset unavailable\n"); + return -EINVAL; + } + + mutex_lock(&tcm_hcd->reset_mutex); + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, false); +#endif + + gpio_set_value(bdata->reset_gpio, bdata->reset_on_state); + msleep(bdata->reset_active_ms); + gpio_set_value(bdata->reset_gpio, !bdata->reset_on_state); + msleep(bdata->reset_delay_ms); + +#ifdef WATCHDOG_SW + tcm_hcd->update_watchdog(tcm_hcd, true); +#endif + + mutex_unlock(&tcm_hcd->reset_mutex); + + if (tcm_hcd->id_info.mode == MODE_APPLICATION_FIRMWARE) { + retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_BOOTLOADER); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enter bootloader mode\n"); + return retval; + } + } else { + retval = tcm_hcd->identify(tcm_hcd, false); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to do identification\n"); + goto run_app_firmware; + } + } + + if (tcm_hcd->boot_info.last_reset_reason == reset_open_limit) + testing_hcd->result = true; + else + testing_hcd->result = false; + + retval = 0; + +run_app_firmware: + if (tcm_hcd->switch_mode(tcm_hcd, FW_MODE_APPLICATION) < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to run application firmware\n"); + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Result = %s\n", (testing_hcd->result)?"pass":"fail"); + return retval; +} + + +static void testing_report(void) +{ + int retval; + unsigned int offset; + unsigned int report_size; + struct syna_tcm_hcd *tcm_hcd = testing_hcd->tcm_hcd; + + report_size = tcm_hcd->report.buffer.data_length; + + LOCK_BUFFER(testing_hcd->report); + + if (testing_hcd->report_index == 0) { + retval = syna_tcm_alloc_mem(tcm_hcd, + &testing_hcd->report, + report_size * testing_hcd->num_of_reports); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd->report.buf\n"); + UNLOCK_BUFFER(testing_hcd->report); + return; + } + } + + if (testing_hcd->report_index < testing_hcd->num_of_reports) { + offset = report_size * testing_hcd->report_index; + + retval = secure_memcpy(testing_hcd->report.buf + offset, + testing_hcd->report.buf_size - offset, + tcm_hcd->report.buffer.buf, + tcm_hcd->report.buffer.buf_size, + tcm_hcd->report.buffer.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy report data\n"); + UNLOCK_BUFFER(testing_hcd->report); + return; + } + + testing_hcd->report_index++; + testing_hcd->report.data_length += report_size; + } + + UNLOCK_BUFFER(testing_hcd->report); + + if (testing_hcd->report_index == testing_hcd->num_of_reports) + complete(&report_complete); + + return; +} + + +static int testing_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + int idx; + + testing_hcd = kzalloc(sizeof(*testing_hcd), GFP_KERNEL); + if (!testing_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for testing_hcd\n"); + return -ENOMEM; + } + + testing_hcd->tcm_hcd = tcm_hcd; + + testing_hcd->collect_reports = testing_collect_reports; + + INIT_BUFFER(testing_hcd->out, false); + INIT_BUFFER(testing_hcd->resp, false); + INIT_BUFFER(testing_hcd->report, false); + INIT_BUFFER(testing_hcd->process, false); + INIT_BUFFER(testing_hcd->output, false); + + testing_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME, + tcm_hcd->sysfs_dir); + if (!testing_hcd->sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs directory\n"); + retval = -EINVAL; + goto err_sysfs_create_dir; + } + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + retval = sysfs_create_file(testing_hcd->sysfs_dir, + &(*attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs file\n"); + goto err_sysfs_create_file; + } + } + + retval = sysfs_create_bin_file(testing_hcd->sysfs_dir, &bin_attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs bin file\n"); + goto err_sysfs_create_bin_file; + } + + return 0; + +err_sysfs_create_bin_file: +err_sysfs_create_file: + for (idx--; idx >= 0; idx--) + sysfs_remove_file(testing_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(testing_hcd->sysfs_dir); + +err_sysfs_create_dir: + RELEASE_BUFFER(testing_hcd->output); + RELEASE_BUFFER(testing_hcd->process); + RELEASE_BUFFER(testing_hcd->report); + RELEASE_BUFFER(testing_hcd->resp); + RELEASE_BUFFER(testing_hcd->out); + + kfree(testing_hcd); + testing_hcd = NULL; + + return retval; +} + +static int testing_remove(struct syna_tcm_hcd *tcm_hcd) +{ + int idx; + + if (!testing_hcd) + goto exit; + + sysfs_remove_bin_file(testing_hcd->sysfs_dir, &bin_attr); + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) + sysfs_remove_file(testing_hcd->sysfs_dir, &(*attrs[idx]).attr); + + kobject_put(testing_hcd->sysfs_dir); + + RELEASE_BUFFER(testing_hcd->output); + RELEASE_BUFFER(testing_hcd->process); + RELEASE_BUFFER(testing_hcd->report); + RELEASE_BUFFER(testing_hcd->resp); + RELEASE_BUFFER(testing_hcd->out); + + kfree(testing_hcd); + testing_hcd = NULL; + +exit: + complete(&testing_remove_complete); + + return 0; +} + +static int testing_reinit(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!testing_hcd) { + retval = testing_init(tcm_hcd); + return retval; + } + + return 0; +} + +static int testing_syncbox(struct syna_tcm_hcd *tcm_hcd) +{ + if (!testing_hcd) + return 0; + + if (tcm_hcd->report.id == testing_hcd->report_type) + testing_report(); + + return 0; +} + +static struct syna_tcm_module_cb testing_module = { + .type = TCM_TESTING, + .init = testing_init, + .remove = testing_remove, + .syncbox = testing_syncbox, +#ifdef REPORT_NOTIFIER + .asyncbox = NULL, +#endif + .reinit = testing_reinit, + .suspend = NULL, + .resume = NULL, + .early_suspend = NULL, +}; + +static int __init testing_module_init(void) +{ + return syna_tcm_add_module(&testing_module, true); +} + +static void __exit testing_module_exit(void) +{ + syna_tcm_add_module(&testing_module, false); + + wait_for_completion(&testing_remove_complete); + + return; +} + +module_init(testing_module_init); +module_exit(testing_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Testing Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_testing.h b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_testing.h new file mode 100644 index 000000000000..ff8219a581ec --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_testing.h @@ -0,0 +1,405 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * Copyright (C) 2018-2019 Ian Su + * Copyright (C) 2018-2019 Joey Zhou + * Copyright (C) 2018-2019 Yuehao Qiu + * Copyright (C) 2018-2019 Aaron Chen + * + * 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. + */ + +#ifndef _SYNAPTICS_TCM_TESTING_H_ +#define _SYNAPTICS_TCM_TESTING_H_ + +struct CFG_PARAMETER { + int offset; + int length; +}; + +/* test limit for the device id checking */ +static const char *device_id_limit = "3618"; + +/* test limit for the config id checking */ +static const char config_id_limit[16] = { + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x47 +}; + + +/* test limit for the pt1 testing */ +/* including the limits for rows and columns */ +static const unsigned char pt1_limits[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* +#define PT1_GET_PIN_ASSIGNMENT +*/ + +#ifdef PT1_GET_PIN_ASSIGNMENT +/* If PT1_GET_PIN_ASSIGNMENT is enabled, */ +/* please ensure that the following parameters */ +/* are the same with the fw's config file. */ +/* Otherwise, the result may be incorrect. */ +/* */ +/* Set the value to '0' if undefined. */ + + +/* value of 'offset' and 'length' in "imageTxes" */ +struct CFG_PARAMETER cfg_imagetxes = { + .offset = 2992, + .length = 240 +}; +/* value of 'offset' and 'length' in "imageRxes" */ +struct CFG_PARAMETER cfg_imagerxes = { + .offset = 2416, + .length = 576 +}; +/* value of 'offset' and 'length' in "numTxGuards" */ +struct CFG_PARAMETER cfg_numtxguards = { + .offset = 0, + .length = 0 +}; +/* value of 'offset' and 'length' in "txGuardPins" */ +struct CFG_PARAMETER cfg_txguardpins = { + .offset = 0, + .length = 0 +}; +/* value of 'offset' and 'length' in "numRxGuards" */ +struct CFG_PARAMETER cfg_numrxguards = { + .offset = 0, + .length = 0 +}; +/* value of 'offset' and 'length' in "rxGuardPins" */ +struct CFG_PARAMETER cfg_rxguardpins = { + .offset = 0, + .length = 0 +}; + +#endif /* end of #ifdef PT1_GET_PIN_ASSIGNMENT */ + + +/* test limit for the pt5 testing */ +/* including upper and lower bounds */ +static const unsigned short pt5_hi_limits[38][38] = { + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, + {4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000}, +}; + +static const unsigned short pt5_lo_limits[38][38] = { + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, +}; + +/* test limit for the pt7 testing */ +/* including upper and lower bounds */ +static const unsigned short pt7_hi_limits[38][38] = { + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, + {1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700, 1700}, +}; + +static const unsigned short pt7_lo_limits[38][38] = { + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, + {300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300, 300}, +}; + + +/* test limit for the pt10 testing */ +static const unsigned short pt10_limits[38][38] = { + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, + {28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28}, +}; + +/* test limit for the pt11 testing */ +/* including upper and lower bounds */ +static const short pt11_hi_limits[38][38] = { + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, + {8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}, +}; + +static const short pt11_lo_limits[38][38] = { + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, + {-8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8, -8}, +}; + +/* test limit for the hw reset pin testing */ +static const unsigned char reset_open_limit = 0x13; + +#endif /* end of _SYNAPTICS_TCM_TESTING_H_ */ diff --git a/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_zeroflash.c b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_zeroflash.c new file mode 100644 index 000000000000..32f0d08e7ccc --- /dev/null +++ b/drivers/input/touchscreen/synaptics_tcm_mmi_v2/synaptics_extmodules/synaptics_zeroflash.c @@ -0,0 +1,1315 @@ +/* + * Synaptics TCM touchscreen driver + * + * Copyright (C) 2017-2018 Synaptics Incorporated. All rights reserved. + * + * Copyright (C) 2017-2018 Scott Lin + * Copyright (C) 2018-2019 Ian Su + * Copyright (C) 2018-2019 Joey Zhou + * Copyright (C) 2018-2019 Yuehao Qiu + * Copyright (C) 2018-2019 Aaron Chen + * + * 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. + */ + +#include +#include +#include +#include "../synaptics_core/synaptics_core.h" + + +#define ENABLE_SYS_ZEROFLASH true + +#define FW_IMAGE_NAME "synaptics/hdl_firmware.img" + +#define BOOT_CONFIG_ID "BOOT_CONFIG" + +#define F35_APP_CODE_ID "F35_APP_CODE" + +#define ROMBOOT_APP_CODE_ID "ROMBOOT_APP_CODE" + +#define RESERVED_BYTES 14 + +#define APP_CONFIG_ID "APP_CONFIG" + +#define DISP_CONFIG_ID "DISPLAY" + +#define SYSFS_DIR_NAME "zeroflash" + +#define IMAGE_FILE_MAGIC_VALUE 0x4818472b + +#define FLASH_AREA_MAGIC_VALUE 0x7c05e516 + +#define PDT_START_ADDR 0x00e9 + +#define PDT_END_ADDR 0x00ee + +#define UBL_FN_NUMBER 0x35 + +#define F35_CTRL3_OFFSET 18 + +#define F35_CTRL7_OFFSET 22 + +#define F35_WRITE_FW_TO_PMEM_COMMAND 4 + +#define TP_RESET_TO_HDL_DELAY_MS 12 + +#define DOWNLOAD_RETRY_COUNT 10 + +#define ZEROFLASH_HOST_DOWNLOAD_WAIT_MS 300 + +#define ZEROFLASH_HOST_DOWNLOAD_TIMEOUT_MS 1000 + +enum f35_error_code { + SUCCESS = 0, + UNKNOWN_FLASH_PRESENT, + MAGIC_NUMBER_NOT_PRESENT, + INVALID_BLOCK_NUMBER, + BLOCK_NOT_ERASED, + NO_FLASH_PRESENT, + CHECKSUM_FAILURE, + WRITE_FAILURE, + INVALID_COMMAND, + IN_DEBUG_MODE, + INVALID_HEADER, + REQUESTING_FIRMWARE, + INVALID_CONFIGURATION, + DISABLE_BLOCK_PROTECT_FAILURE, +}; + +enum config_download { + HDL_INVALID = 0, + HDL_TOUCH_CONFIG, + HDL_DISPLAY_CONFIG, + HDL_DISPLAY_CONFIG_TO_RAM, +}; + +struct area_descriptor { + unsigned char magic_value[4]; + unsigned char id_string[16]; + unsigned char flags[4]; + unsigned char flash_addr_words[4]; + unsigned char length[4]; + unsigned char checksum[4]; +}; + +struct block_data { + const unsigned char *data; + unsigned int size; + unsigned int flash_addr; +}; + +struct image_info { + unsigned int packrat_number; + struct block_data boot_config; + struct block_data app_firmware; + struct block_data app_config; + struct block_data disp_config; +}; + +struct image_header { + unsigned char magic_value[4]; + unsigned char num_of_areas[4]; +}; + +struct rmi_f35_query { + unsigned char version:4; + unsigned char has_debug_mode:1; + unsigned char has_data5:1; + unsigned char has_query1:1; + unsigned char has_query2:1; + unsigned char chunk_size; + unsigned char has_ctrl7:1; + unsigned char has_host_download:1; + unsigned char has_spi_master:1; + unsigned char advanced_recovery_mode:1; + unsigned char reserved:4; +} __packed; + +struct rmi_f35_data { + unsigned char error_code:5; + unsigned char recovery_mode_forced:1; + unsigned char nvm_programmed:1; + unsigned char in_recovery:1; +} __packed; + +struct rmi_pdt_entry { + unsigned char query_base_addr; + unsigned char command_base_addr; + unsigned char control_base_addr; + unsigned char data_base_addr; + unsigned char intr_src_count:3; + unsigned char reserved_1:2; + unsigned char fn_version:2; + unsigned char reserved_2:1; + unsigned char fn_number; +} __packed; + +struct rmi_addr { + unsigned short query_base; + unsigned short command_base; + unsigned short control_base; + unsigned short data_base; +}; + +struct firmware_status { + unsigned short invalid_static_config:1; + unsigned short need_disp_config:1; + unsigned short need_app_config:1; + unsigned short hdl_version:4; + unsigned short reserved:9; +} __packed; + +struct zeroflash_hcd { + bool has_hdl; + bool f35_ready; + const unsigned char *image; + unsigned char *buf; + const struct firmware *fw_entry; + struct work_struct config_work; + struct workqueue_struct *workqueue; + struct kobject *sysfs_dir; + struct rmi_addr f35_addr; + struct image_info image_info; + struct firmware_status fw_status; + struct syna_tcm_buffer out; + struct syna_tcm_buffer resp; + struct syna_tcm_hcd *tcm_hcd; +}; + +static void zeroflash_do_romboot_firmware_download(void); + +DECLARE_COMPLETION(zeroflash_remove_complete); + +STORE_PROTOTYPE(zeroflash, hdl) + +static struct device_attribute *attrs[] = { + ATTRIFY(hdl), +}; + +static struct zeroflash_hcd *zeroflash_hcd; + +static int zeroflash_wait_hdl(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + msleep(ZEROFLASH_HOST_DOWNLOAD_WAIT_MS); + + if (!atomic_read(&tcm_hcd->host_downloading)) + return 0; + + retval = wait_event_interruptible_timeout(tcm_hcd->hdl_wq, + !atomic_read(&tcm_hcd->host_downloading), + msecs_to_jiffies(ZEROFLASH_HOST_DOWNLOAD_TIMEOUT_MS)); + if (retval == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Timed out waiting for completion of host download\n"); + atomic_set(&tcm_hcd->host_downloading, 0); + retval = -EIO; + } else { + retval = 0; + } + + return retval; +} + +static ssize_t zeroflash_sysfs_hdl_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int retval = 0; + unsigned int input; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + + if (sscanf(buf, "%u", &input) != 1) + return -EINVAL; + + if (input && (tcm_hcd->in_hdl_mode)) { + + retval = tcm_hcd->reset(tcm_hcd); + if (retval < 0) + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to trigger the host download by reset\n"); + + retval = zeroflash_wait_hdl(tcm_hcd); + if (retval < 0) + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to wait for completion of host download\n"); + + if (zeroflash_hcd->fw_entry) { + release_firmware(zeroflash_hcd->fw_entry); + zeroflash_hcd->fw_entry = NULL; + } + + zeroflash_hcd->image = NULL; + + } else { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid HDL devices\n"); + } + return count; +} + +static int zeroflash_check_uboot(void) +{ + int retval; + unsigned char fn_number; + struct rmi_f35_query query; + struct rmi_pdt_entry p_entry; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + + retval = syna_tcm_rmi_read(tcm_hcd, + PDT_END_ADDR, + &fn_number, + sizeof(fn_number)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read RMI function number\n"); + return retval; + } + + LOGD(tcm_hcd->pdev->dev.parent, + "Found F$%02x\n", + fn_number); + + if (fn_number != UBL_FN_NUMBER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to find F$35\n"); + return -ENODEV; + } + + if (zeroflash_hcd->f35_ready) + return 0; + + retval = syna_tcm_rmi_read(tcm_hcd, + PDT_START_ADDR, + (unsigned char *)&p_entry, + sizeof(p_entry)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read PDT entry\n"); + return retval; + } + + zeroflash_hcd->f35_addr.query_base = p_entry.query_base_addr; + zeroflash_hcd->f35_addr.command_base = p_entry.command_base_addr; + zeroflash_hcd->f35_addr.control_base = p_entry.control_base_addr; + zeroflash_hcd->f35_addr.data_base = p_entry.data_base_addr; + + retval = syna_tcm_rmi_read(tcm_hcd, + zeroflash_hcd->f35_addr.query_base, + (unsigned char *)&query, + sizeof(query)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read F$35 query\n"); + return retval; + } + + zeroflash_hcd->f35_ready = true; + + if (query.has_query2 && query.has_ctrl7 && query.has_host_download) { + zeroflash_hcd->has_hdl = true; + } else { + LOGE(tcm_hcd->pdev->dev.parent, + "Host download not supported\n"); + zeroflash_hcd->has_hdl = false; + return -ENODEV; + } + + return 0; +} + +static int zeroflash_parse_fw_image(void) +{ + unsigned int idx; + unsigned int addr; + unsigned int offset; + unsigned int length; + unsigned int checksum; + unsigned int flash_addr; + unsigned int magic_value; + unsigned int num_of_areas; + struct image_header *header; + struct image_info *image_info; + struct area_descriptor *descriptor; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + const unsigned char *image; + const unsigned char *content; + + image = zeroflash_hcd->image; + image_info = &zeroflash_hcd->image_info; + header = (struct image_header *)image; + + magic_value = le4_to_uint(header->magic_value); + if (magic_value != IMAGE_FILE_MAGIC_VALUE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid image file magic value\n"); + return -EINVAL; + } + + memset(image_info, 0x00, sizeof(*image_info)); + + offset = sizeof(*header); + num_of_areas = le4_to_uint(header->num_of_areas); + + for (idx = 0; idx < num_of_areas; idx++) { + addr = le4_to_uint(image + offset); + descriptor = (struct area_descriptor *)(image + addr); + offset += 4; + + magic_value = le4_to_uint(descriptor->magic_value); + if (magic_value != FLASH_AREA_MAGIC_VALUE) + continue; + + length = le4_to_uint(descriptor->length); + content = (unsigned char *)descriptor + sizeof(*descriptor); + flash_addr = le4_to_uint(descriptor->flash_addr_words) * 2; + checksum = le4_to_uint(descriptor->checksum); + + if (0 == strncmp((char *)descriptor->id_string, + BOOT_CONFIG_ID, strlen(BOOT_CONFIG_ID))) { + + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Boot config checksum error\n"); + return -EINVAL; + } + image_info->boot_config.size = length; + image_info->boot_config.data = content; + image_info->boot_config.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Boot config size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Boot config flash address = 0x%08x\n", + flash_addr); + } else if ((0 == strncmp((char *)descriptor->id_string, + F35_APP_CODE_ID, strlen(F35_APP_CODE_ID)))) { + + if (tcm_hcd->sensor_type != TYPE_F35) { + LOGE(tcm_hcd->pdev->dev.parent, + "Improper descriptor, F35_APP_CODE_ID\n"); + return -EINVAL; + } + + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "HDL_F35 firmware checksum error\n"); + return -EINVAL; + } + image_info->app_firmware.size = length; + image_info->app_firmware.data = content; + image_info->app_firmware.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "HDL_F35 firmware size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "HDL_F35 firmware flash address = 0x%08x\n", + flash_addr); + + } else if ((0 == strncmp((char *)descriptor->id_string, + ROMBOOT_APP_CODE_ID, + strlen(ROMBOOT_APP_CODE_ID)))) { + + if (tcm_hcd->sensor_type != TYPE_ROMBOOT) { + LOGE(tcm_hcd->pdev->dev.parent, + "Improper descriptor, ROMBOOT_APP_CODE_ID\n"); + return -EINVAL; + } + + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "HDL_ROMBoot firmware checksum error\n"); + return -EINVAL; + } + image_info->app_firmware.size = length; + image_info->app_firmware.data = content; + image_info->app_firmware.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "HDL_ROMBoot firmware size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "HDL_ROMBoot firmware flash address = 0x%08x\n", + flash_addr); + + } else if (0 == strncmp((char *)descriptor->id_string, + APP_CONFIG_ID, strlen(APP_CONFIG_ID))) { + + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Application config checksum error\n"); + return -EINVAL; + } + image_info->app_config.size = length; + image_info->app_config.data = content; + image_info->app_config.flash_addr = flash_addr; + image_info->packrat_number = le4_to_uint(&content[14]); + LOGD(tcm_hcd->pdev->dev.parent, + "Application config size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Application config flash address = 0x%08x\n", + flash_addr); + } else if (0 == strncmp((char *)descriptor->id_string, + DISP_CONFIG_ID, strlen(DISP_CONFIG_ID))) { + + if (checksum != (crc32(~0, content, length) ^ ~0)) { + LOGE(tcm_hcd->pdev->dev.parent, + "Display config checksum error\n"); + return -EINVAL; + } + image_info->disp_config.size = length; + image_info->disp_config.data = content; + image_info->disp_config.flash_addr = flash_addr; + LOGD(tcm_hcd->pdev->dev.parent, + "Display config size = %d\n", + length); + LOGD(tcm_hcd->pdev->dev.parent, + "Display config flash address = 0x%08x\n", + flash_addr); + } + } + + return 0; +} + +static int zeroflash_get_fw_image(void) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + + if (zeroflash_hcd->fw_entry != NULL) + return 0; + + if (zeroflash_hcd->image == NULL) { + retval = request_firmware(&zeroflash_hcd->fw_entry, + FW_IMAGE_NAME, + tcm_hcd->pdev->dev.parent); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to request %s\n", + FW_IMAGE_NAME); + return retval; + } + } + + LOGD(tcm_hcd->pdev->dev.parent, + "Firmware image size = %d\n", + (unsigned int)zeroflash_hcd->fw_entry->size); + + zeroflash_hcd->image = zeroflash_hcd->fw_entry->data; + + retval = zeroflash_parse_fw_image(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to parse firmware image\n"); + release_firmware(zeroflash_hcd->fw_entry); + zeroflash_hcd->fw_entry = NULL; + zeroflash_hcd->image = NULL; + return retval; + } + + return 0; +} + +static void zeroflash_download_config(void) +{ + struct firmware_status *fw_status; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + + fw_status = &zeroflash_hcd->fw_status; + + if (!fw_status->need_app_config && !fw_status->need_disp_config) { + if (atomic_read(&tcm_hcd->helper.task) == HELP_NONE) { + atomic_set(&tcm_hcd->helper.task, + HELP_SEND_REINIT_NOTIFICATION); + queue_work(tcm_hcd->helper.workqueue, + &tcm_hcd->helper.work); + } + atomic_set(&tcm_hcd->host_downloading, 0); + return; + } + + queue_work(zeroflash_hcd->workqueue, &zeroflash_hcd->config_work); + + return; +} + +static int zeroflash_download_disp_config(void) +{ + int retval; + unsigned char response_code; + struct image_info *image_info; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + static unsigned int retry_count; + + LOGN(tcm_hcd->pdev->dev.parent, + "Downloading display config\n"); + + image_info = &zeroflash_hcd->image_info; + + if (image_info->disp_config.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No display config in image file\n"); + return -EINVAL; + } + + LOCK_BUFFER(zeroflash_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &zeroflash_hcd->out, + image_info->disp_config.size + 2); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for display config\n"); + goto unlock_out; + } + + switch (zeroflash_hcd->fw_status.hdl_version) { + case 0: + case 1: + zeroflash_hcd->out.buf[0] = 1; + break; + case 2: + zeroflash_hcd->out.buf[0] = 2; + break; + default: + retval = -EINVAL; + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid HDL version (%d)\n", + zeroflash_hcd->fw_status.hdl_version); + goto unlock_out; + } + + zeroflash_hcd->out.buf[1] = HDL_DISPLAY_CONFIG; + + retval = secure_memcpy(&zeroflash_hcd->out.buf[2], + zeroflash_hcd->out.buf_size - 2, + image_info->disp_config.data, + image_info->disp_config.size, + image_info->disp_config.size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy display config data\n"); + goto unlock_out; + } + + zeroflash_hcd->out.data_length = image_info->disp_config.size + 2; + + LOCK_BUFFER(zeroflash_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_DOWNLOAD_CONFIG, + zeroflash_hcd->out.buf, + zeroflash_hcd->out.data_length, + &zeroflash_hcd->resp.buf, + &zeroflash_hcd->resp.buf_size, + &zeroflash_hcd->resp.data_length, + &response_code, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_DOWNLOAD_CONFIG)); + if (response_code != STATUS_ERROR) + goto unlock_resp; + retry_count++; + if (DOWNLOAD_RETRY_COUNT && retry_count > DOWNLOAD_RETRY_COUNT) + goto unlock_resp; + } else { + retry_count = 0; + } + + retval = secure_memcpy((unsigned char *)&zeroflash_hcd->fw_status, + sizeof(zeroflash_hcd->fw_status), + zeroflash_hcd->resp.buf, + zeroflash_hcd->resp.buf_size, + sizeof(zeroflash_hcd->fw_status)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy firmware status\n"); + goto unlock_resp; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Display config downloaded\n"); + + retval = 0; + +unlock_resp: + UNLOCK_BUFFER(zeroflash_hcd->resp); + +unlock_out: + UNLOCK_BUFFER(zeroflash_hcd->out); + + return retval; +} + +static int zeroflash_download_app_config(void) +{ + int retval; + unsigned char padding; + unsigned char response_code; + struct image_info *image_info; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + static unsigned int retry_count; + + LOGN(tcm_hcd->pdev->dev.parent, + "Downloading application config\n"); + + image_info = &zeroflash_hcd->image_info; + + if (image_info->app_config.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No application config in image file\n"); + return -EINVAL; + } + + padding = image_info->app_config.size % 8; + if (padding) + padding = 8 - padding; + + LOCK_BUFFER(zeroflash_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &zeroflash_hcd->out, + image_info->app_config.size + 2 + padding); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for application config\n"); + goto unlock_out; + } + + switch (zeroflash_hcd->fw_status.hdl_version) { + case 0: + case 1: + zeroflash_hcd->out.buf[0] = 1; + break; + case 2: + zeroflash_hcd->out.buf[0] = 2; + break; + default: + retval = -EINVAL; + LOGE(tcm_hcd->pdev->dev.parent, + "Invalid HDL version (%d)\n", + zeroflash_hcd->fw_status.hdl_version); + goto unlock_out; + } + + zeroflash_hcd->out.buf[1] = HDL_TOUCH_CONFIG; + + retval = secure_memcpy(&zeroflash_hcd->out.buf[2], + zeroflash_hcd->out.buf_size - 2, + image_info->app_config.data, + image_info->app_config.size, + image_info->app_config.size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy application config data\n"); + goto unlock_out; + } + + zeroflash_hcd->out.data_length = image_info->app_config.size + 2; + zeroflash_hcd->out.data_length += padding; + + LOCK_BUFFER(zeroflash_hcd->resp); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_DOWNLOAD_CONFIG, + zeroflash_hcd->out.buf, + zeroflash_hcd->out.data_length, + &zeroflash_hcd->resp.buf, + &zeroflash_hcd->resp.buf_size, + &zeroflash_hcd->resp.data_length, + &response_code, + 0); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command %s\n", + STR(CMD_DOWNLOAD_CONFIG)); + if (response_code != STATUS_ERROR) + goto unlock_resp; + retry_count++; + if (DOWNLOAD_RETRY_COUNT && retry_count > DOWNLOAD_RETRY_COUNT) + goto unlock_resp; + } else { + retry_count = 0; + } + + retval = secure_memcpy((unsigned char *)&zeroflash_hcd->fw_status, + sizeof(zeroflash_hcd->fw_status), + zeroflash_hcd->resp.buf, + zeroflash_hcd->resp.buf_size, + sizeof(zeroflash_hcd->fw_status)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy firmware status\n"); + goto unlock_resp; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Application config downloaded\n"); + + retval = 0; + +unlock_resp: + UNLOCK_BUFFER(zeroflash_hcd->resp); + +unlock_out: + UNLOCK_BUFFER(zeroflash_hcd->out); + + return retval; +} + +static void zeroflash_download_config_work(struct work_struct *work) +{ + int retval; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + + retval = zeroflash_get_fw_image(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get firmware image\n"); + return; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Start of config download\n"); + + if (zeroflash_hcd->fw_status.need_app_config) { + retval = zeroflash_download_app_config(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to download application config\n"); + return; + } + goto exit; + } + + if (zeroflash_hcd->fw_status.need_disp_config) { + retval = zeroflash_download_disp_config(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to download display config\n"); + return; + } + goto exit; + } + +exit: + LOGN(tcm_hcd->pdev->dev.parent, + "End of config download\n"); + + zeroflash_download_config(); + + return; +} + +static int zeroflash_download_app_fw(void) +{ + int retval; + unsigned char command; + struct image_info *image_info; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; +#if TP_RESET_TO_HDL_DELAY_MS + const struct syna_tcm_board_data *bdata = tcm_hcd->hw_if->bdata; +#endif + + LOGN(tcm_hcd->pdev->dev.parent, + "Downloading application firmware\n"); + + image_info = &zeroflash_hcd->image_info; + + if (image_info->app_firmware.size == 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "No application firmware in image file\n"); + return -EINVAL; + } + + LOCK_BUFFER(zeroflash_hcd->out); + + retval = syna_tcm_alloc_mem(tcm_hcd, + &zeroflash_hcd->out, + image_info->app_firmware.size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for application firmware\n"); + UNLOCK_BUFFER(zeroflash_hcd->out); + return retval; + } + + retval = secure_memcpy(zeroflash_hcd->out.buf, + zeroflash_hcd->out.buf_size, + image_info->app_firmware.data, + image_info->app_firmware.size, + image_info->app_firmware.size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy application firmware data\n"); + UNLOCK_BUFFER(zeroflash_hcd->out); + return retval; + } + + zeroflash_hcd->out.data_length = image_info->app_firmware.size; + + command = F35_WRITE_FW_TO_PMEM_COMMAND; + +#if TP_RESET_TO_HDL_DELAY_MS + if (bdata->tpio_reset_gpio >= 0) { + gpio_set_value(bdata->tpio_reset_gpio, bdata->reset_on_state); + msleep(bdata->reset_active_ms); + gpio_set_value(bdata->tpio_reset_gpio, !bdata->reset_on_state); + mdelay(TP_RESET_TO_HDL_DELAY_MS); + } +#endif + + retval = syna_tcm_rmi_write(tcm_hcd, + zeroflash_hcd->f35_addr.control_base + F35_CTRL3_OFFSET, + &command, + sizeof(command)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write F$35 command\n"); + UNLOCK_BUFFER(zeroflash_hcd->out); + return retval; + } + + retval = syna_tcm_rmi_write(tcm_hcd, + zeroflash_hcd->f35_addr.control_base + F35_CTRL7_OFFSET, + zeroflash_hcd->out.buf, + zeroflash_hcd->out.data_length); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write application firmware data\n"); + UNLOCK_BUFFER(zeroflash_hcd->out); + return retval; + } + + UNLOCK_BUFFER(zeroflash_hcd->out); + + LOGN(tcm_hcd->pdev->dev.parent, + "Application firmware downloaded\n"); + + return 0; +} + + +static void zeroflash_do_f35_firmware_download(void) +{ + int retval; + struct rmi_f35_data data; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + static unsigned int retry_count; + + if (tcm_hcd->irq_enabled) { + retval = tcm_hcd->enable_irq(tcm_hcd, false, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to disable interrupt\n"); + } + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Prepare F35 firmware download\n"); + + if (tcm_hcd->id_info.mode == MODE_ROMBOOTLOADER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Incorrect uboot type, exit\n"); + goto exit; + } + retval = zeroflash_check_uboot(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to find valid uboot\n"); + goto exit; + } + + atomic_set(&tcm_hcd->host_downloading, 1); + + retval = syna_tcm_rmi_read(tcm_hcd, + zeroflash_hcd->f35_addr.data_base, + (unsigned char *)&data, + sizeof(data)); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to read F$35 data\n"); + goto exit; + } + + if (data.error_code != REQUESTING_FIRMWARE) { + LOGE(tcm_hcd->pdev->dev.parent, + "Microbootloader error code = 0x%02x\n", + data.error_code); + if (data.error_code != CHECKSUM_FAILURE) { + retval = -EIO; + goto exit; + } else { + retry_count++; + } + } else { + retry_count = 0; + } + + retval = zeroflash_get_fw_image(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to get firmware image\n"); + goto exit; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "Start of firmware download\n"); + + /* perform firmware downloading */ + retval = zeroflash_download_app_fw(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to download application firmware\n"); + goto exit; + } + + LOGN(tcm_hcd->pdev->dev.parent, + "End of firmware download\n"); + +exit: + if (retval < 0) + retry_count++; + + if (DOWNLOAD_RETRY_COUNT && retry_count > DOWNLOAD_RETRY_COUNT) { + retval = tcm_hcd->enable_irq(tcm_hcd, false, true); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to disable interrupt\n"); + } + + LOGD(tcm_hcd->pdev->dev.parent, + "Interrupt is disabled\n"); + } else { + retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable interrupt\n"); + } + } + + return; +} + +static void zeroflash_do_romboot_firmware_download(void) +{ + int retval; + unsigned char *out_buf = NULL; + unsigned char *resp_buf = NULL; + unsigned int resp_buf_size; + unsigned int resp_length; + unsigned int data_size_blocks; + unsigned int image_size; + struct syna_tcm_hcd *tcm_hcd = zeroflash_hcd->tcm_hcd; + + LOGN(tcm_hcd->pdev->dev.parent, + "Prepare ROMBOOT firmware download\n"); + + atomic_set(&tcm_hcd->host_downloading, 1); + resp_buf = NULL; + resp_buf_size = 0; + + if (!tcm_hcd->irq_enabled) { + retval = tcm_hcd->enable_irq(tcm_hcd, true, NULL); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to enable interrupt\n"); + } + } + + pm_stay_awake(&tcm_hcd->pdev->dev); + + if (tcm_hcd->id_info.mode != MODE_ROMBOOTLOADER) { + LOGE(tcm_hcd->pdev->dev.parent, + "Not in romboot mode\n"); + atomic_set(&tcm_hcd->host_downloading, 0); + goto exit; + } + + retval = zeroflash_get_fw_image(); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to request romboot.img\n"); + goto exit; + } + + image_size = (unsigned int)zeroflash_hcd->image_info.app_firmware.size; + + LOGD(tcm_hcd->pdev->dev.parent, + "image_size = %d\n", + image_size); + + data_size_blocks = image_size / 16; + + out_buf = kzalloc(image_size + RESERVED_BYTES, + GFP_KERNEL); + + memset(out_buf, 0x00, RESERVED_BYTES); + + out_buf[0] = zeroflash_hcd->image_info.app_firmware.size >> 16; + + retval = secure_memcpy(&out_buf[RESERVED_BYTES], + zeroflash_hcd->image_info.app_firmware.size, + zeroflash_hcd->image_info.app_firmware.data, + zeroflash_hcd->image_info.app_firmware.size, + zeroflash_hcd->image_info.app_firmware.size); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy payload\n"); + goto exit; + } + + LOGD(tcm_hcd->pdev->dev.parent, + "data_size_blocks: %d\n", + data_size_blocks); + + retval = tcm_hcd->write_message(tcm_hcd, + CMD_ROMBOOT_DOWNLOAD, + out_buf, + image_size + RESERVED_BYTES, + &resp_buf, + &resp_buf_size, + &resp_length, + NULL, + 20); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to write command ROMBOOT DOWNLOAD"); + goto exit; + } + + retval = tcm_hcd->switch_mode(tcm_hcd, FW_MODE_BOOTLOADER); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to switch to bootloader"); + goto exit; + } + +exit: + + pm_relax(&tcm_hcd->pdev->dev); + + kfree(out_buf); + + kfree(resp_buf); + + return; +} + +static int zeroflash_init(struct syna_tcm_hcd *tcm_hcd) +{ + int retval = 0; + int idx; + + zeroflash_hcd = NULL; + if (!(tcm_hcd->in_hdl_mode)) + return 0; + + zeroflash_hcd = kzalloc(sizeof(*zeroflash_hcd), GFP_KERNEL); + if (!zeroflash_hcd) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to allocate memory for zeroflash_hcd\n"); + return -ENOMEM; + } + + zeroflash_hcd->tcm_hcd = tcm_hcd; + zeroflash_hcd->image = NULL; + + INIT_BUFFER(zeroflash_hcd->out, false); + INIT_BUFFER(zeroflash_hcd->resp, false); + + zeroflash_hcd->workqueue = + create_singlethread_workqueue("syna_tcm_zeroflash"); + INIT_WORK(&zeroflash_hcd->config_work, + zeroflash_download_config_work); + + if (ENABLE_SYS_ZEROFLASH == false) + goto init_finished; + + zeroflash_hcd->sysfs_dir = kobject_create_and_add(SYSFS_DIR_NAME, + tcm_hcd->sysfs_dir); + if (!zeroflash_hcd->sysfs_dir) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs directory\n"); + return -EINVAL; + } + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + retval = sysfs_create_file(zeroflash_hcd->sysfs_dir, + &(*attrs[idx]).attr); + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to create sysfs file\n"); + } + } + +init_finished: + /* prepare the firmware download process */ + if (tcm_hcd->in_hdl_mode) { + switch (tcm_hcd->sensor_type) { + case TYPE_F35: + zeroflash_do_f35_firmware_download(); + break; + case TYPE_ROMBOOT: + zeroflash_do_romboot_firmware_download(); + break; + default: + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to find valid HDL state (%d)\n", + tcm_hcd->sensor_type); + break; + + } + } + return retval; +} + +static int zeroflash_remove(struct syna_tcm_hcd *tcm_hcd) +{ + int idx; + + if (!zeroflash_hcd) + goto exit; + + if (zeroflash_hcd->fw_entry) + release_firmware(zeroflash_hcd->fw_entry); + + + if (ENABLE_SYS_ZEROFLASH == true) { + + for (idx = 0; idx < ARRAY_SIZE(attrs); idx++) { + sysfs_remove_file(zeroflash_hcd->sysfs_dir, + &(*attrs[idx]).attr); + } + + kobject_put(zeroflash_hcd->sysfs_dir); + } + + + cancel_work_sync(&zeroflash_hcd->config_work); + flush_workqueue(zeroflash_hcd->workqueue); + destroy_workqueue(zeroflash_hcd->workqueue); + + RELEASE_BUFFER(zeroflash_hcd->resp); + RELEASE_BUFFER(zeroflash_hcd->out); + + kfree(zeroflash_hcd); + zeroflash_hcd = NULL; + +exit: + complete(&zeroflash_remove_complete); + + return 0; +} + +static int zeroflash_syncbox(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + unsigned char *fw_status; + + if (!zeroflash_hcd) + return 0; + + switch (tcm_hcd->report.id) { + case REPORT_STATUS: + fw_status = (unsigned char *)&zeroflash_hcd->fw_status; + + retval = secure_memcpy(fw_status, + sizeof(zeroflash_hcd->fw_status), + tcm_hcd->report.buffer.buf, + tcm_hcd->report.buffer.buf_size, + sizeof(zeroflash_hcd->fw_status)); + + if (retval < 0) { + LOGE(tcm_hcd->pdev->dev.parent, + "Failed to copy firmware status\n"); + return retval; + } + zeroflash_download_config(); + break; + case REPORT_HDL_F35: + zeroflash_do_f35_firmware_download(); + break; + case REPORT_HDL_ROMBOOT: + zeroflash_do_romboot_firmware_download(); + break; + + default: + break; + } + + return 0; +} + +static int zeroflash_reinit(struct syna_tcm_hcd *tcm_hcd) +{ + int retval; + + if (!zeroflash_hcd && tcm_hcd->in_hdl_mode) { + retval = zeroflash_init(tcm_hcd); + return retval; + } + + return 0; +} + +static struct syna_tcm_module_cb zeroflash_module = { + .type = TCM_ZEROFLASH, + .init = zeroflash_init, + .remove = zeroflash_remove, + .syncbox = zeroflash_syncbox, +#ifdef REPORT_NOTIFIER + .asyncbox = NULL, +#endif + .reinit = zeroflash_reinit, + .suspend = NULL, + .resume = NULL, + .early_suspend = NULL, +}; + +static int __init zeroflash_module_init(void) +{ + return syna_tcm_add_module(&zeroflash_module, true); +} + +static void __exit zeroflash_module_exit(void) +{ + syna_tcm_add_module(&zeroflash_module, false); + + wait_for_completion(&zeroflash_remove_complete); + + return; +} + +module_init(zeroflash_module_init); +module_exit(zeroflash_module_exit); + +MODULE_AUTHOR("Synaptics, Inc."); +MODULE_DESCRIPTION("Synaptics TCM Zeroflash Module"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/touchscreen_mmi/Android.mk b/drivers/input/touchscreen/touchscreen_mmi/Android.mk new file mode 100644 index 000000000000..fe10a4d5ae97 --- /dev/null +++ b/drivers/input/touchscreen/touchscreen_mmi/Android.mk @@ -0,0 +1,53 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(DRM_DYNAMIC_REFRESH_RATE),true) + KBUILD_OPTIONS += CONFIG_DRM_DYNAMIC_REFRESH_RATE=y +endif + +ifeq ($(TOUCHCLASS_MMI_DEBUG_INFO),true) + KERNEL_CFLAGS += CONFIG_TOUCHCLASS_MMI_DEBUG_INFO=y +endif + +ifeq ($(TOUCHCLASS_MMI_GESTURE_POISON_EVENT),true) + KERNEL_CFLAGS += CONFIG_TOUCHCLASS_MMI_GESTURE_POISON_EVENT=y +endif + +ifeq ($(TOUCHCLASS_MMI_MULTIWAY_UPDATE_FW),true) + KBUILD_OPTIONS += CONFIG_TOUCHCLASS_MMI_MULTIWAY_UPDATE_FW=y +endif + +ifeq ($(DRM_PANEL_NOTIFICATIONS),true) + KBUILD_OPTIONS += CONFIG_DRM_PANEL_NOTIFICATIONS=y +endif + +ifeq ($(DRM_PANEL_EVENT_NOTIFICATIONS),true) + KBUILD_OPTIONS += CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS=y +endif + +ifeq ($(TOUCH_PANEL_NOTIFICATIONS),true) + KBUILD_OPTIONS += CONFIG_TOUCH_PANEL_NOTIFICATIONS=y +endif + +ifneq (,$(findstring gki, $(KERNEL_DEFCONFIG))) + KERNEL_CFLAGS += CONFIG_TS_KERNEL_USE_GKI=y +endif + +ifeq ($(BOARD_USES_DOUBLE_TAP_CTRL),true) + KBUILD_OPTIONS += CONFIG_BOARD_USES_DOUBLE_TAP_CTRL=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := touchscreen_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/sensors_class.ko +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/mmi_relay.ko + +ifeq ($(DRM_DYNAMIC_REFRESH_RATE),true) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/msm_drm.ko +endif + +KBUILD_OPTIONS_GKI += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/input/touchscreen/touchscreen_mmi/Kbuild b/drivers/input/touchscreen/touchscreen_mmi/Kbuild new file mode 100644 index 000000000000..f937be291cf6 --- /dev/null +++ b/drivers/input/touchscreen/touchscreen_mmi/Kbuild @@ -0,0 +1,41 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +ifneq ($(filter m y, $(CONFIG_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_PANEL_NOTIFICATIONS +endif +ifneq ($(filter m y, $(CONFIG_DRM_DYNAMIC_REFRESH_RATE)),) + EXTRA_CFLAGS += -DCONFIG_DRM_DYNAMIC_REFRESH_RATE +endif +ifneq ($(filter m y, $(CONFIG_TOUCHCLASS_MMI_DEBUG_INFO)),) + EXTRA_CFLAGS += -DCONFIG_TOUCHCLASS_MMI_DEBUG_INFO +endif +ifneq ($(filter m y, $(CONFIG_TOUCHCLASS_MMI_GESTURE_POISON_EVENT)),) + EXTRA_CFLAGS += -DTS_MMI_TOUCH_GESTURE_POISON_EVENT +endif +ifneq ($(filter m y, $(CONFIG_TOUCHCLASS_MMI_MULTIWAY_UPDATE_FW)),) + EXTRA_CFLAGS += -DTS_MMI_TOUCH_MULTIWAY_UPDATE_FW +endif +ifneq ($(filter m y, $(CONFIG_DRM_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_NOTIFICATIONS +endif +ifneq ($(filter m y, $(CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_EVENT_NOTIFICATIONS +endif +ifneq ($(filter m y, $(CONFIG_TOUCH_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_PANEL_NOTIFICATIONS +endif +ifneq ($(filter m y, $(CONFIG_TS_KERNEL_USE_GKI)),) + EXTRA_CFLAGS += -DCONFIG_TS_KERNEL_USE_GKI +endif +ifneq ($(filter m y, $(CONFIG_BOARD_USES_DOUBLE_TAP_CTRL)),) + EXTRA_CFLAGS += -DCONFIG_BOARD_USES_DOUBLE_TAP_CTRL +endif + +obj-m := touchscreen_mmi.o +touchscreen_mmi-objs := touchscreen_mmi_class.o touchscreen_mmi_panel.o touchscreen_mmi_notif.o touchscreen_mmi_gesture.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../mmi_relay/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/../../kernel/msm-$(MODULE_KERNEL_VERSION)/Module.symvers diff --git a/drivers/input/touchscreen/touchscreen_mmi/Makefile b/drivers/input/touchscreen/touchscreen_mmi/Makefile new file mode 100644 index 000000000000..881b2808debf --- /dev/null +++ b/drivers/input/touchscreen/touchscreen_mmi/Makefile @@ -0,0 +1,16 @@ +all: modules + +modules: + $(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) + +KBUILD_EXTRA_SYMBOLS += $(M)/../../../sensors/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(M)/../../../mmi_relay/Module.symvers +ifneq ($(filter m y, $(CONFIG_DRM_DYNAMIC_REFRESH_RATE)),) +KBUILD_EXTRA_SYMBOLS += $(OUT_DIR)/../../vendor/qcom/opensource/display-drivers/msm/Module.symvers +endif diff --git a/drivers/input/touchscreen/touchscreen_mmi/touchscreen_mmi_class.c b/drivers/input/touchscreen/touchscreen_mmi/touchscreen_mmi_class.c new file mode 100644 index 000000000000..603c8c51702a --- /dev/null +++ b/drivers/input/touchscreen/touchscreen_mmi/touchscreen_mmi_class.c @@ -0,0 +1,1115 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FMT_STRING "%s" +#define FMT_INTEGER "%d" +#define FMT_HEX_INTEGER "0x%02x" + +#define TOUCH_MMI_SHOW(name, chk_tp_status, fmt) \ +static ssize_t name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct ts_mmi_dev *touch_cdev = dev_get_drvdata(dev); \ + int ret = 0; \ + if (!touch_cdev) { \ + dev_err(dev, "get_%s: invalid pointer\n", #name); \ + return (ssize_t)0; \ + } \ + mutex_lock(&touch_cdev->extif_mutex); \ + if (!chk_tp_status || is_touch_active) { \ + TRY_TO_GET(name, &touch_cdev->name); \ + if (ret < 0) { \ + dev_err(dev, "get_%s: return error %d\n", #name, ret); \ + ret = 0; \ + goto TOUCH_MMI_SHOW_OUT; \ + } \ + } else \ + dev_dbg(dev, "get_%s: read from cache data.\n", #name); \ + ret = scnprintf(buf, PAGE_SIZE, fmt, touch_cdev->name); \ +TOUCH_MMI_SHOW_OUT: \ + mutex_unlock(&touch_cdev->extif_mutex); \ + return (ssize_t)ret; \ +} +#define TOUCH_MMI_STORE(name, chk_tp_status, fmt) \ +static ssize_t name##_store(struct device *dev, \ + struct device_attribute *attr, const char *buf, size_t size) \ +{ \ + struct ts_mmi_dev *touch_cdev = dev_get_drvdata(dev); \ + unsigned long value = 0; \ + int ret = 0; \ + ret = kstrtoul(buf, 0, &value); \ + if (ret < 0) { \ + dev_err(dev, "%s: Failed to convert value\n", #name); \ + return -EINVAL; \ + } \ + if (!touch_cdev) { \ + dev_err(dev, "%s: invalid pointer\n", #name); \ + return -EINVAL; \ + } \ + mutex_lock(&touch_cdev->extif_mutex); \ + mutex_lock(&touch_cdev->method_mutex); \ + touch_cdev->name = value; \ + if (!chk_tp_status || is_touch_active) { \ + _TRY_TO_CALL(name, touch_cdev->name); \ + if (ret < 0) { \ + dev_err(dev, "%s: return error %d\n", #name, ret); \ + goto TOUCH_MMI_STORE_OUT; \ + } \ + } else \ + dev_dbg(dev, "%s: write to cache data.\n", #name); \ +TOUCH_MMI_STORE_OUT: \ + mutex_unlock(&touch_cdev->method_mutex); \ + mutex_unlock(&touch_cdev->extif_mutex); \ + return size; \ +} \ + +#define TOUCH_MMI_GET_ATTR_RO(name, fmt) \ +TOUCH_MMI_SHOW(name, false, fmt) \ +static DEVICE_ATTR(name, S_IRUGO, name##_show, NULL) + +#define TOUCH_MMI_GET_ATTR_WO(name) \ +TOUCH_MMI_STORE(name, false, fmt) \ +static DEVICE_ATTR(name, (S_IWUSR | S_IWGRP), NULL, name##_store) + +#define TOUCH_MMI_GET_ATTR_RW(name, fmt) \ +TOUCH_MMI_SHOW(name, true, fmt) \ +TOUCH_MMI_STORE(name, true, fmt) \ +static DEVICE_ATTR(name, (S_IWUSR | S_IWGRP | S_IRUGO), name##_show, name##_store) + +static struct class *touchscreens_class; + +DECLARE_RWSEM(touchscreens_list_lock); +LIST_HEAD(touchscreens_list); + + +TOUCH_MMI_GET_ATTR_RO(vendor, FMT_STRING); +TOUCH_MMI_GET_ATTR_RO(productinfo, FMT_STRING); +TOUCH_MMI_GET_ATTR_RO(build_id, FMT_STRING); +TOUCH_MMI_GET_ATTR_RO(config_id, FMT_STRING); +TOUCH_MMI_GET_ATTR_RO(poweron, FMT_INTEGER); +TOUCH_MMI_GET_ATTR_RO(flashprog, FMT_INTEGER); +TOUCH_MMI_GET_ATTR_RO(irq_status, FMT_INTEGER); +TOUCH_MMI_GET_ATTR_RW(drv_irq, FMT_INTEGER); +TOUCH_MMI_GET_ATTR_RW(suppression, FMT_HEX_INTEGER); +TOUCH_MMI_GET_ATTR_RW(hold_grip, FMT_HEX_INTEGER); +TOUCH_MMI_GET_ATTR_RW(hold_distance, FMT_HEX_INTEGER); +TOUCH_MMI_GET_ATTR_RW(gs_distance, FMT_HEX_INTEGER); +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW +TOUCH_MMI_GET_ATTR_RW(flash_mode, FMT_INTEGER); +#endif +#ifdef TS_MMI_TOUCH_GESTURE_POISON_EVENT +TOUCH_MMI_GET_ATTR_RW(poison_timeout, FMT_HEX_INTEGER); +TOUCH_MMI_GET_ATTR_RW(poison_distance, FMT_HEX_INTEGER); +TOUCH_MMI_GET_ATTR_RW(poison_trigger_distance, FMT_HEX_INTEGER); +#endif +TOUCH_MMI_GET_ATTR_WO(reset); +TOUCH_MMI_GET_ATTR_WO(pinctrl); +TOUCH_MMI_GET_ATTR_WO(refresh_rate); +TOUCH_MMI_GET_ATTR_WO(charger_mode); +TOUCH_MMI_GET_ATTR_WO(update_baseline); + +static char *ts_mmi_kobject_get_path(struct kobject *kobj, gfp_t gfp_mask) +{ + char *path; + int len = 1; + struct kobject *parent = kobj; + + do { + if (parent->name == NULL) { + len = 0; + break; + } + len += strlen(parent->name) + 1; + parent = parent->parent; + } while (parent); + + if (len == 0) + return NULL; + + path = kzalloc(len, gfp_mask); + if (!path) + return NULL; + + --len; + for (parent = kobj; parent; parent = parent->parent) { + int cur = strlen(parent->name); + len -= cur; + memcpy(path + len, parent->name, cur); + *(path + --len) = '/'; + } + pr_debug("kobject: '%s' (%p): %s: path = '%s'\n", kobj->name, + kobj, __func__, path); + + return path; +} + +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ts_mmi_dev *touch_cdev = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!touch_cdev) { + dev_err(dev, "touchscreen device pointer is NULL\n"); + return (ssize_t)0; + } + + path = ts_mmi_kobject_get_path(&DEV_MMI->kobj, GFP_KERNEL); + + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} +static DEVICE_ATTR(path, S_IRUGO, path_show, NULL); + + +static ssize_t ts_mmi_buildid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ts_mmi_dev *touch_cdev = dev_get_drvdata(dev); + + build_id_show(dev, attr, buf); + config_id_show(dev, attr, buf); + return scnprintf(buf, PAGE_SIZE, "%s-%s\n", + touch_cdev->build_id, + touch_cdev->config_id); +} +static DEVICE_ATTR(buildid, S_IRUGO, ts_mmi_buildid_show, NULL); + +static ssize_t ts_mmi_ic_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ts_mmi_dev *touch_cdev = dev_get_drvdata(dev); + + + productinfo_show(dev, attr, buf); + build_id_show(dev, attr, buf); + config_id_show(dev, attr, buf); + return scnprintf(buf, PAGE_SIZE, "%s%s\n%s%s\n%s%s\n", + "Product ID: ", touch_cdev->productinfo, + "Build ID: ", touch_cdev->build_id, + "Config ID: ", touch_cdev->config_id); +} +static DEVICE_ATTR(ic_ver, S_IRUGO, ts_mmi_ic_ver_show, NULL); + +static ssize_t ts_mmi_hw_irqstat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ts_mmi_dev *touch_cdev = dev_get_drvdata(dev); + + irq_status_show(dev, attr, buf); + switch (touch_cdev->irq_status) { + case 0: + return scnprintf(buf, PAGE_SIZE, "Low\n"); + case 1: + return scnprintf(buf, PAGE_SIZE, "High\n"); + default: + dev_err(DEV_TS, "%s: Failed to get irq state\n", + __func__); + return scnprintf(buf, PAGE_SIZE, "Unknown\n"); + } +} +static DEVICE_ATTR(hw_irqstat, S_IRUGO, ts_mmi_hw_irqstat_show, NULL); + +static ssize_t ts_mmi_forcereflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct ts_mmi_dev *touch_cdev = dev_get_drvdata(dev); + unsigned long value = 0; + int err = 0; + err = kstrtoul(buf, 10, &value); + if (err < 0) { + dev_err(dev, "forcereflash: Failed to convert value\n"); + return -EINVAL; + } + touch_cdev->forcereflash = value; + return size; +} +static DEVICE_ATTR(forcereflash, (S_IWUSR | S_IWGRP), NULL, ts_mmi_forcereflash_store); + + +static ssize_t ts_mmi_doreflash_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct ts_mmi_dev *touch_cdev = dev_get_drvdata(dev); + char fw_path[TS_MMI_MAX_FW_PATH]; + char template[TS_MMI_MAX_FW_PATH]; + int ret = 0; + + if (size > TS_MMI_MAX_FW_PATH) { + dev_err(dev, "%s: FW filename is too long\n", __func__); + return -EINVAL; + } + + if (!touch_cdev->forcereflash) { + TRY_TO_GET(vendor, &touch_cdev->vendor); + if (strncmp(buf, touch_cdev->vendor, + strnlen(touch_cdev->vendor, TS_MMI_MAX_VENDOR_LEN))) { + dev_err(dev, + "%s: FW does not belong to %s\n", + __func__, touch_cdev->vendor); + return -EINVAL; + } + + TRY_TO_GET(productinfo, &touch_cdev->productinfo); + snprintf(template, sizeof(template), "-%s-", touch_cdev->productinfo); + if (!strnstr(buf + strnlen(touch_cdev->vendor, TS_MMI_MAX_VENDOR_LEN), + template, size)) { + dev_err(dev, "%s: FW does not belong to %s\n", + __func__, touch_cdev->productinfo); + return -EINVAL; + } + } + + strlcpy(fw_path, buf, size); + dev_dbg(dev, "%s: FW filename: %s\n", __func__, fw_path); + + TRY_TO_CALL(firmware_update, fw_path); + if (ret < 0) { + dev_err(dev, "%s: firmware_update failed %d.\n", __func__, ret); + return -EINVAL; + } + + dev_info(dev, "%s: update fw from %s, return %d\n", + __func__, fw_path, ret); + + return size; +} +static DEVICE_ATTR(doreflash, (S_IWUSR | S_IWGRP), NULL, ts_mmi_doreflash_store); + +static ssize_t pwr_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct ts_mmi_dev *touch_cdev = dev_get_drvdata(dev); + unsigned long value = 0; + int ret = 0; + ret = kstrtoul(buf, 10, &value); + if (ret < 0) { + dev_err(dev, "%s: Failed to convert value\n", __func__); + return -EINVAL; + } + if (!touch_cdev) { + dev_err(dev, "%s: invalid pointer\n", __func__); + return -EINVAL; + } + TRY_TO_CALL(power, value); + return size; +} +static DEVICE_ATTR(pwr, (S_IWUSR | S_IWGRP), NULL, pwr_store); + +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL +/* + * gesture value used to indicate which gesture mode type is enabled + */ +static ssize_t gesture_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ts_mmi_dev *touch_cdev = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%02x\n", touch_cdev->pdata.supported_gesture_type); +} +static ssize_t gesture_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct ts_mmi_dev *touch_cdev = dev_get_drvdata(dev); + unsigned int value = 0; + int err = 0; + + mutex_lock(&touch_cdev->extif_mutex); + err = sscanf(buf, "%d", &value); + if (err < 0) { + dev_err(dev, "forcereflash: Failed to convert value\n"); + mutex_unlock(&touch_cdev->extif_mutex); + return -EINVAL; + } + switch (value) { + case 0x10: + dev_info(dev, "%s: zero tap disable\n", __func__); + touch_cdev->gesture_mode_type &= 0xFE; + break; + case 0x11: + dev_info(dev, "%s: zero tap enable\n", __func__); + touch_cdev->gesture_mode_type |= 0x01; + break; + case 0x20: + dev_info(dev, "%s: single tap disable\n", __func__); + touch_cdev->gesture_mode_type &= 0xFD; + break; + case 0x21: + dev_info(dev, "%s: single tap enable\n", __func__); + touch_cdev->gesture_mode_type |= 0x02; + break; + case 0x30: + dev_info(dev, "%s: double tap disable\n", __func__); + touch_cdev->gesture_mode_type &= 0xFB; + break; + case 0x31: + dev_info(dev, "%s: double tap enable\n", __func__); + touch_cdev->gesture_mode_type |= 0x04; + break; + default: + dev_info(dev, "%s: unsupport gesture mode type\n", __func__); + ; + } + mutex_unlock(&touch_cdev->extif_mutex); + dev_info(dev, "%s: gesture_mode_type = 0x%02x \n", __func__, touch_cdev->gesture_mode_type); + + return size; +} +static DEVICE_ATTR(gesture, (S_IWUSR | S_IWGRP | S_IRUGO), gesture_show, gesture_store); +#endif + +/* + * Show liquid detection function status and detection result + */ +static ssize_t liquid_detection_ctl_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ts_mmi_dev *touch_cdev = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "function status: %02x, detection result: %02x\n", + touch_cdev->lpd_state, touch_cdev->liquid_status); +} + +/* + * Enable/disable liquid detection function for debug + */ +static ssize_t liquid_detection_ctl_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct ts_mmi_dev *touch_cdev = dev_get_drvdata(dev); + unsigned int value = 0; + int err = 0; + + err = sscanf(buf, "%d", &value); + if (err < 0) { + dev_err(dev, "liquid_detection_ctl: Failed to convert value\n"); + return -EINVAL; + } + + if (value == 2) { + dev_info(dev, "liquid_detection_ctl: Not support liquid detecion\n"); + touch_cdev->pdata.support_liquid_detection = 0; + return size; + } else if (value == 3) { + dev_info(dev, "liquid_detection_ctl: Support liquid detecion\n"); + touch_cdev->pdata.support_liquid_detection = 1; + return size; + } + + mutex_lock(&touch_cdev->extif_mutex); + if (value != touch_cdev->lpd_state) { + touch_cdev->lpd_state = !!value; + kfifo_put(&touch_cdev->cmd_pipe, TS_MMI_DO_LIQUID_DETECTION); + schedule_delayed_work(&touch_cdev->work, 0); + dev_info(DEV_MMI, "%s: LPD state is %d\n", __func__, touch_cdev->lpd_state); + } + mutex_unlock(&touch_cdev->extif_mutex); + + return size; +} +static DEVICE_ATTR(liquid_detection_ctl, (S_IWUSR | S_IWGRP | S_IRUGO), + liquid_detection_ctl_show, liquid_detection_ctl_store); + +static struct attribute *sysfs_class_attrs[] = { + &dev_attr_path.attr, + &dev_attr_vendor.attr, + &dev_attr_productinfo.attr, + &dev_attr_buildid.attr, + &dev_attr_build_id.attr, + &dev_attr_config_id.attr, + &dev_attr_poweron.attr, + &dev_attr_flashprog.attr, + &dev_attr_irq_status.attr, + &dev_attr_hw_irqstat.attr, + &dev_attr_ic_ver.attr, + &dev_attr_drv_irq.attr, + &dev_attr_reset.attr, + &dev_attr_forcereflash.attr, + &dev_attr_doreflash.attr, +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW + &dev_attr_flash_mode.attr, +#endif + &dev_attr_pwr.attr, + &dev_attr_pinctrl.attr, + &dev_attr_refresh_rate.attr, + &dev_attr_charger_mode.attr, + &dev_attr_update_baseline.attr, +#ifdef TS_MMI_TOUCH_GESTURE_POISON_EVENT + &dev_attr_poison_timeout.attr, + &dev_attr_poison_distance.attr, + &dev_attr_poison_trigger_distance.attr, +#endif +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + &dev_attr_gesture.attr, +#endif + &dev_attr_liquid_detection_ctl.attr, + NULL, +}; + +static const struct attribute_group sysfs_class_group = { + .attrs = sysfs_class_attrs, +}; + +/* + * pill_region input value is much different between others sys entry + * override default functions + */ +static ssize_t pill_region_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ts_mmi_dev *touch_cdev = dev_get_drvdata(dev); + int ret = 0; + + if (!touch_cdev) { + dev_err(dev, "get_pill_region: invalid pointer\n"); + return (ssize_t)0; + } + mutex_lock(&touch_cdev->extif_mutex); + if (is_touch_active) { + TRY_TO_GET(pill_region, &touch_cdev->pill_region); + if (ret < 0) { + dev_err(dev, "get_pill_region: return error %d\n", ret); + ret = 0; + goto PILL_REGION_SHOW_OUT; + } + } else + dev_dbg(dev, "get_pill_region: read from cache data.\n"); + + ret = scnprintf(buf, PAGE_SIZE, "0x%02x 0x%x 0x%x", + touch_cdev->pill_region[0], touch_cdev->pill_region[1], touch_cdev->pill_region[2]); +PILL_REGION_SHOW_OUT: + mutex_unlock(&touch_cdev->extif_mutex); + return (ssize_t)ret; + +} +static ssize_t pill_region_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct ts_mmi_dev *touch_cdev = dev_get_drvdata(dev); + unsigned int args[TS_MMI_PILL_REGION_REQ_ARGS_NUM] = {0}; + int ret = 0; + int i = TS_MMI_PILL_REGION_REQ_ARGS_NUM; + + ret = sscanf(buf, "0x%x 0x%x 0x%x", &args[0], &args[1], &args[2]); + if (ret < TS_MMI_PILL_REGION_REQ_ARGS_NUM) { + dev_err(dev, "pill_region: Failed to convert value\n"); + return -EINVAL; + } + + if (!touch_cdev) { + dev_err(dev, "pill_region: invalid pointer\n"); + return -EINVAL; + } + mutex_lock(&touch_cdev->extif_mutex); + mutex_lock(&touch_cdev->method_mutex); + while (i--) + touch_cdev->pill_region[i] = args[i]; + if (is_touch_active) + _TRY_TO_CALL(pill_region, touch_cdev->pill_region); + else + dev_dbg(dev, "pill_region: write to cache data.\n"); + mutex_unlock(&touch_cdev->method_mutex); + mutex_unlock(&touch_cdev->extif_mutex); + return size; +} +static DEVICE_ATTR(pill_region, (S_IWUSR | S_IWGRP | S_IRUGO), pill_region_show, pill_region_store); + +/* + * active_region input requires 4 parameters + */ +static ssize_t active_region_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ts_mmi_dev *touch_cdev = dev_get_drvdata(dev); + int ret = 0; + + if (!touch_cdev) { + dev_err(dev, "get_active_region: invalid pointer\n"); + return (ssize_t)0; + } + mutex_lock(&touch_cdev->extif_mutex); + if (is_touch_active) { + TRY_TO_GET(active_region, &touch_cdev->active_region); + if (ret < 0) { + dev_err(dev, "get_active_region: return error %d\n", ret); + ret = 0; + goto ACTIVE_REGION_SHOW_OUT; + } + } else + dev_dbg(dev, "get_active_region: read from cache data.\n"); + + ret = scnprintf(buf, PAGE_SIZE, "%u %u %u %u", + touch_cdev->active_region[0], touch_cdev->active_region[1], + touch_cdev->active_region[2], touch_cdev->active_region[3]); +ACTIVE_REGION_SHOW_OUT: + mutex_unlock(&touch_cdev->extif_mutex); + + return (ssize_t)ret; + +} +static ssize_t active_region_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct ts_mmi_dev *touch_cdev = dev_get_drvdata(dev); + unsigned int args[TS_MMI_ACTIVE_REGION_REQ_ARGS_NUM] = {0}; + int ret = 0; + int i = TS_MMI_ACTIVE_REGION_REQ_ARGS_NUM; + + ret = sscanf(buf, "%u %u %u %u", &args[0], &args[1], &args[2], &args[3]); + if (ret < TS_MMI_ACTIVE_REGION_REQ_ARGS_NUM) { + dev_err(dev, "active_region: Failed to convert value\n"); + return -EINVAL; + } + + if (!touch_cdev) { + dev_err(dev, "active_region: invalid pointer\n"); + return -EINVAL; + } + mutex_lock(&touch_cdev->extif_mutex); + mutex_lock(&touch_cdev->method_mutex); + while (i--) + touch_cdev->active_region[i] = args[i]; + if (is_touch_active) + _TRY_TO_CALL(active_region, touch_cdev->active_region); + else + dev_dbg(dev, "active_region: write to cache data.\n"); + mutex_unlock(&touch_cdev->method_mutex); + mutex_unlock(&touch_cdev->extif_mutex); + + return size; +} +static DEVICE_ATTR(active_region, (S_IWUSR | S_IWGRP | S_IRUGO), active_region_show, active_region_store); + +static int ts_mmi_sysfs_create_edge_entries(struct ts_mmi_dev *touch_cdev, bool create) { + int ret = 0; + + if (create) { + /*initialize value*/ + if (touch_cdev->pdata.active_region_ctrl) { + TRY_TO_GET(active_region, &touch_cdev->active_region); + if (ret < 0) + dev_err(DEV_TS, "%s: failed to read active_region info (%d)\n", + __func__, ret); + ret = sysfs_create_file(&DEV_MMI->kobj, &dev_attr_active_region.attr); + if (ret < 0) { + dev_err(DEV_TS, "%s: failed to create active_region entry (%d)\n", + __func__, ret); + goto CREATE_SUPPRESSION_FAILED; + } + } + if (touch_cdev->pdata.suppression_ctrl) { + TRY_TO_GET(suppression, &touch_cdev->suppression); + if (ret < 0) + dev_err(DEV_TS, "%s: failed to read suppression info (%d)\n", + __func__, ret); + ret = sysfs_create_file(&DEV_MMI->kobj, &dev_attr_suppression.attr); + if (ret < 0) { + dev_err(DEV_TS, "%s: failed to create suppression entry (%d)\n", + __func__, ret); + goto CREATE_SUPPRESSION_FAILED; + } + } + if (touch_cdev->pdata.pill_region_ctrl) { + TRY_TO_GET(pill_region, &touch_cdev->pill_region); + if (ret < 0) + dev_err(DEV_TS, "%s: failed to read pill_region info (%d)\n", + __func__, ret); + ret = sysfs_create_file(&DEV_MMI->kobj, &dev_attr_pill_region.attr); + if (ret < 0) { + dev_err(DEV_TS, "%s: failed to create pill_region entry (%d)\n", + __func__, ret); + goto CREATE_PILL_REGION_FAILED; + } + } + if (touch_cdev->pdata.hold_distance_ctrl) { + TRY_TO_GET(hold_distance, &touch_cdev->hold_distance); + if (ret < 0) + dev_err(DEV_TS, "%s: failed to read hold_distance info (%d)\n", + __func__, ret); + ret = sysfs_create_file(&DEV_MMI->kobj, &dev_attr_hold_distance.attr); + if (ret < 0) { + dev_err(DEV_TS, "%s: failed to create hold_distance entry (%d)\n", + __func__, ret); + goto CREATE_HOLD_DISTANCE_FAILED; + } + } + if (touch_cdev->pdata.gs_distance_ctrl) { + TRY_TO_GET(gs_distance, &touch_cdev->gs_distance); + if (ret < 0) { + dev_err(DEV_TS, "%s: failed to read gs_distance info (%d)\n", + __func__, ret); + /* Set default gs distance value */ + touch_cdev->gs_distance = 0x1E; + } + + ret = sysfs_create_file(&DEV_MMI->kobj, &dev_attr_gs_distance.attr); + if (ret < 0) { + dev_err(DEV_TS, "%s: failed to create gs_distance entry (%d)\n", + __func__, ret); + goto CREATE_GS_DISTANCE_FAILED; + } + } + if (touch_cdev->pdata.hold_grip_ctrl) { + TRY_TO_GET(hold_grip, &touch_cdev->hold_grip); + if (ret < 0) + dev_err(DEV_TS, "%s: failed to read hold_grip info (%d)\n", + __func__, ret); + ret = sysfs_create_file(&DEV_MMI->kobj, &dev_attr_hold_grip.attr); + if (ret < 0) { + dev_err(DEV_TS, "%s: failed to create hold_grip entry (%d)\n", + __func__, ret); + goto CREATE_HOLD_GRIP_FAILED; + } + } +#ifdef TS_MMI_TOUCH_GESTURE_POISON_EVENT + if (touch_cdev->pdata.poison_slot_ctrl) { + touch_cdev->poison_distance = TOUCHSCREEN_MMI_DEFAULT_POISON_DISTANCE; + touch_cdev->poison_trigger_distance = TOUCHSCREEN_MMI_DEFAULT_POISON_TRIGGER_DISTANCE; + touch_cdev->poison_timeout = TOUCHSCREEN_MMI_DEFAULT_POISON_TIMEOUT_MS; + } +#endif + return 0; + } + + if (touch_cdev->pdata.hold_grip_ctrl) + sysfs_remove_file(&DEV_MMI->kobj, &dev_attr_hold_grip.attr); +CREATE_HOLD_GRIP_FAILED: + if (touch_cdev->pdata.gs_distance_ctrl) + sysfs_remove_file(&DEV_MMI->kobj, &dev_attr_gs_distance.attr); +CREATE_GS_DISTANCE_FAILED: + if (touch_cdev->pdata.hold_distance_ctrl) + sysfs_remove_file(&DEV_MMI->kobj, &dev_attr_hold_distance.attr); +CREATE_HOLD_DISTANCE_FAILED: + if (touch_cdev->pdata.pill_region_ctrl) + sysfs_remove_file(&DEV_MMI->kobj, &dev_attr_pill_region.attr); +CREATE_PILL_REGION_FAILED: + if (touch_cdev->pdata.suppression_ctrl) + sysfs_remove_file(&DEV_MMI->kobj, &dev_attr_suppression.attr); +CREATE_SUPPRESSION_FAILED: + return ret; +} + +static int ts_mmi_get_vendor_info( + struct ts_mmi_dev *touch_cdev) { + int ret = 0; + + TRY_TO_GET(productinfo, &touch_cdev->productinfo); + TRY_TO_GET(vendor, &touch_cdev->vendor); + TRY_TO_GET(bus_type, &touch_cdev->bus_type); + + return 0; +} + +static struct ts_mmi_dev *ts_mmi_dev_to_cdev(struct device *parent) +{ + struct ts_mmi_dev *touch_cdev = NULL; + + down_write(&touchscreens_list_lock); + list_for_each_entry(touch_cdev, &touchscreens_list, node) { + if(DEV_TS == parent) + break; + } + up_write(&touchscreens_list_lock); + return touch_cdev; +} + +static int get_class_fname_handler(struct device *parent, const char **pfname) +{ + struct ts_mmi_dev *touch_cdev = ts_mmi_dev_to_cdev(parent); + if (!touch_cdev) + return -ENODEV; + *pfname = dev_name(touch_cdev->class_dev); + return 0; +} + +static int get_supplier_handler(struct device *parent, const char **psname) +{ + struct ts_mmi_dev *touch_cdev = ts_mmi_dev_to_cdev(parent); + if (!touch_cdev) + return -ENODEV; + *psname = touch_cdev->panel_supplier; + return 0; +} + +static int get_gesture_type_handler(struct device *parent, unsigned char *gesture_type) +{ + struct ts_mmi_dev *touch_cdev = ts_mmi_dev_to_cdev(parent); + if (!touch_cdev) + return -ENODEV; + *gesture_type = touch_cdev->gesture_mode_type; + return 0; +} + +static int report_liquid_detection_status_handler(struct device *parent, int status) +{ + struct ts_mmi_dev *touch_cdev = ts_mmi_dev_to_cdev(parent); + if (!touch_cdev) + return -ENODEV; + + dev_info(DEV_TS, "%s: notify liquid detection status: %d\n", __func__, status); + touch_cdev->liquid_status = status; + if (touch_cdev->is_lpd_registered) + relay_notifier_fire(BLOCKING, LPD, NOTIFY_EVENT_TUD_STATUS, + (void *)&status); + else + dev_err(DEV_TS, "%s: lpd_notifier does not register\n", __func__); + return 0; +} + +static int ts_mmi_default_pinctrl(struct device *parent, int on) +{ + struct ts_mmi_dev *touch_cdev = ts_mmi_dev_to_cdev(parent); + + if (!touch_cdev) + return -ENODEV; + + dev_info(DEV_TS, "%s: %s\n", __func__, on ? "ACTIVE" : "SUSPEND"); + + if (on) { + if (!IS_ERR_OR_NULL(touch_cdev->pinctrl_on_state)) + pinctrl_select_state(touch_cdev->pinctrl_node, + touch_cdev->pinctrl_on_state); + } else { + if (!IS_ERR_OR_NULL(touch_cdev->pinctrl_off_state)) + pinctrl_select_state(touch_cdev->pinctrl_node, + touch_cdev->pinctrl_off_state); + } + + return 0; +} + +static int ts_mmi_clip_event_handler(struct device *parent, + struct touch_event_data *tev, struct input_dev *input_dev) +{ + struct ts_mmi_dev *touch_cdev = ts_mmi_dev_to_cdev(parent); + bool inside; + + inside = (tev->x >= touch_cdev->clip.xUL) && (tev->x <= touch_cdev->clip.xBR) && + (tev->y >= touch_cdev->clip.yUL) && (tev->y <= touch_cdev->clip.yBR); + + if (inside == touch_cdev->clip.inversion) { + if (tev->type == TS_COORDINATE_ACTION_MOVE) { + /* cancel active touch when it falls off the clip area */ + input_mt_slot(input_dev, tev->id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + /* indicate cancelled touch to vendor driver */ + tev->type = TS_COORDINATE_ACTION_FORCE_LIFT; + } + tev->skip_report = true; + } + + return 0; +} + +/** + * ts_mmi_dev_register - register a new object of ts_mmi_dev class. + * @parent: The device to register. + * @touch_cdev: the ts_mmi_dev structure for this device. + */ +int ts_mmi_dev_register(struct device *parent, + struct ts_mmi_methods *mdata) +{ + struct ts_mmi_dev *touch_cdev; + int ret = 0; + const char *class_fname= "primary"; + + if (!touchscreens_class || !parent) + return -ENODEV; + + touch_cdev = devm_kzalloc(parent, sizeof(struct ts_mmi_dev), + GFP_KERNEL); + if (!touch_cdev) + return -ENOMEM; + + dev_info(parent, "%s: new device\n", __func__); + DEV_TS = parent; + touch_cdev->mdata = mdata; + mutex_init(&touch_cdev->extif_mutex); + mutex_init(&touch_cdev->method_mutex); + + ret = ts_mmi_parse_dt(touch_cdev, DEV_TS->of_node); + if (ret < 0) { + dev_err(DEV_TS, "%s: init panel failed. %d\n", __func__, ret); + goto PANEL_PARSE_DT_FAILED; + } + + touch_cdev->pinctrl_node = devm_pinctrl_get(DEV_TS); + if (IS_ERR_OR_NULL(touch_cdev->pinctrl_node)) { + dev_info(DEV_TS, "%s: No pinctrl defined\n", __func__); + } else if (!touch_cdev->mdata->pinctrl) { + touch_cdev->pinctrl_on_state = pinctrl_lookup_state(touch_cdev->pinctrl_node, "ts_mmi_on_state"); + touch_cdev->pinctrl_off_state = pinctrl_lookup_state(touch_cdev->pinctrl_node, "ts_mmi_off_state"); + if (!IS_ERR_OR_NULL(touch_cdev->pinctrl_on_state) || + !IS_ERR_OR_NULL(touch_cdev->pinctrl_off_state)) { + dev_info(DEV_TS, "%s: No pinctrl method add default\n", __func__); + touch_cdev->mdata->pinctrl = ts_mmi_default_pinctrl; + if (!IS_ERR_OR_NULL(touch_cdev->pinctrl_on_state)) + pinctrl_select_state(touch_cdev->pinctrl_node, + touch_cdev->pinctrl_on_state); + } + } + + ts_mmi_get_vendor_info(touch_cdev); + + ret = alloc_chrdev_region(&touch_cdev->class_dev_no, 0, 1, class_fname); + if (ret < 0) { + dev_info(DEV_TS, "%s: get device number failed. %d\n", + __func__, ret); + goto GET_NEW_MINOT_FAILED; + } + + if (touch_cdev->pdata.class_entry_name) + class_fname = touch_cdev->pdata.class_entry_name; + else if (touch_cdev->mdata->get_class_entry_name) { + touch_cdev->mdata->get_class_entry_name(DEV_TS, + &touch_cdev->class_entry_name); + class_fname = touch_cdev->class_entry_name; + } + dev_info(DEV_TS, "class entry name %s\n", class_fname); + + DEV_MMI = device_create(touchscreens_class, + parent, touch_cdev->class_dev_no, + touch_cdev, "%s", class_fname); + if (IS_ERR(DEV_MMI)) { + ret = PTR_ERR(DEV_MMI); + goto CLASS_DEVICE_CREATE_FAILED; + } + touch_cdev->mdata->exports.get_class_fname = get_class_fname_handler; + touch_cdev->mdata->exports.get_supplier = get_supplier_handler; + touch_cdev->mdata->exports.get_gesture_type = get_gesture_type_handler; + touch_cdev->mdata->exports.report_liquid_detection_status = report_liquid_detection_status_handler; + touch_cdev->mdata->exports.kobj_notify = &DEV_MMI->kobj; + + down_write(&touchscreens_list_lock); + list_add_tail(&touch_cdev->node, &touchscreens_list); + up_write(&touchscreens_list_lock); + + ret = sysfs_create_group(&DEV_MMI->kobj, &sysfs_class_group); + if (ret) + goto CLASS_DEVICE_ATTR_CREATE_FAILED; + + if (touch_cdev->mdata->extend_attribute_group) { + touch_cdev->mdata->extend_attribute_group(DEV_TS, + &touch_cdev->extern_group); + if (touch_cdev->extern_group) { + ret = sysfs_create_group(&DEV_MMI->kobj, + touch_cdev->extern_group); + if (ret) + goto CLASS_DEVICE_EXT_ATTR_CREATE_FAILED; + } + } + + ret = ts_mmi_sysfs_create_edge_entries(touch_cdev, true); + if (ret < 0) { + dev_err(DEV_TS, "%s: Create edge sys entries failed. %d\n", + __func__, ret); + goto CLASS_DEVICE_EDGE_ATTR_CREATE_FAILED; + } + + ret = ts_mmi_panel_register(touch_cdev); + if (ret < 0) { + dev_err(DEV_TS, "%s: Register panel failed. %d\n", + __func__, ret); + goto PANEL_INIT_FAILED; + } + + ret = ts_mmi_notifiers_register(touch_cdev); + if (ret < 0) { + dev_err(DEV_TS, "%s: Register notifiers failed. %d\n", + __func__, ret); + goto NOTIFIER_INIT_FAILED; + } + + if (touch_cdev->pdata.gestures_enabled) { + ret = ts_mmi_gesture_init(touch_cdev); + if (ret < 0) { + dev_err(DEV_TS, "%s: Register gesture failed. %d\n", + __func__, ret); + goto GESTURE_INIT_FAILED; + } + } + + if (touch_cdev->pdata.cli_gestures_enabled) { + ret = ts_mmi_cli_gesture_init(touch_cdev); + if (ret < 0) { + dev_err(DEV_TS, "%s: Register CLI gesture failed. %d\n", + __func__, ret); + goto GESTURE_INIT_FAILED; + } + } + + if (touch_cdev->pdata.palm_enabled) { + ret = ts_mmi_palm_init(touch_cdev); + if (ret < 0) { + dev_err(DEV_TS, "%s: Register plam failed. %d\n", + __func__, ret); + goto GESTURE_INIT_FAILED; + } + } + + if (touch_cdev->pdata.clip_area_ctrl) { + /* export report touch event function to vendor */ + touch_cdev->mdata->exports.clip_touch_event = ts_mmi_clip_event_handler; + } + + dev_info(DEV_TS, "Registered touchscreen device: %s.\n", class_fname); + + return 0; + +GESTURE_INIT_FAILED: + ts_mmi_notifiers_unregister(touch_cdev); +NOTIFIER_INIT_FAILED: + ts_mmi_panel_unregister(touch_cdev); +PANEL_INIT_FAILED: + ts_mmi_sysfs_create_edge_entries(touch_cdev, false); +CLASS_DEVICE_EDGE_ATTR_CREATE_FAILED: + if (touch_cdev->extern_group) + sysfs_remove_group(&DEV_MMI->kobj, touch_cdev->extern_group); +CLASS_DEVICE_EXT_ATTR_CREATE_FAILED: + sysfs_remove_group(&DEV_MMI->kobj, &sysfs_class_group); +CLASS_DEVICE_ATTR_CREATE_FAILED: + down_write(&touchscreens_list_lock); + list_del(&touch_cdev->node); + up_write(&touchscreens_list_lock); + device_unregister(DEV_MMI); +CLASS_DEVICE_CREATE_FAILED: + DEV_MMI = NULL; + unregister_chrdev_region(touch_cdev->class_dev_no, 1); +GET_NEW_MINOT_FAILED: + ts_mmi_panel_unregister(touch_cdev); +PANEL_PARSE_DT_FAILED: + devm_kfree(parent, touch_cdev); + return ret; +} +EXPORT_SYMBOL(ts_mmi_dev_register); + +/** + * ts_mmi_dev_unregister - unregister a object of touchscreens class. + * @touch_cdev: the touchscreen device to unregister + * Unregister a previously registered via ts_mmi_dev_register object. + */ +void ts_mmi_dev_unregister(struct device *parent) +{ + struct ts_mmi_dev *touch_cdev; + + if (!touchscreens_class) + return; + + down_write(&touchscreens_list_lock); + list_for_each_entry(touch_cdev, &touchscreens_list, node) { + if (DEV_TS == parent) { + list_del(&touch_cdev->node); + break; + } + } + up_write(&touchscreens_list_lock); + + if (DEV_TS != parent) { + dev_err(parent, "%s: device not registered before.\n", __func__); + return; + } + if (touch_cdev->pdata.gestures_enabled) + ts_mmi_gesture_remove(touch_cdev); + if (touch_cdev->pdata.cli_gestures_enabled) + ts_mmi_cli_gesture_remove(touch_cdev); + if (touch_cdev->pdata.palm_enabled) + ts_mmi_palm_remove(touch_cdev); + ts_mmi_notifiers_unregister(touch_cdev); + ts_mmi_panel_unregister(touch_cdev); + dev_info(DEV_TS, "%s: delete device\n", __func__); + + dev_set_drvdata(DEV_TS, NULL); + ts_mmi_sysfs_create_edge_entries(touch_cdev, false); + if (touch_cdev->extern_group) + sysfs_remove_group(&DEV_MMI->kobj, touch_cdev->extern_group); + sysfs_remove_group(&DEV_MMI->kobj, &sysfs_class_group); + device_unregister(DEV_MMI); + DEV_MMI = NULL; + unregister_chrdev_region(touch_cdev->class_dev_no, 1); + devm_kfree(parent, touch_cdev); +} +EXPORT_SYMBOL(ts_mmi_dev_unregister); + +bool ts_mmi_is_panel_match(const char *panel_node, char *touch_ic_name) +{ + struct device_node *of_chosen; + const char *panel_name; + + of_chosen = of_find_node_by_path("/chosen"); + if (of_chosen == NULL) + of_chosen = of_find_node_by_path("/chosen@0"); + + if (of_chosen == NULL) + return false; + + if (of_property_read_string(of_chosen, panel_node, &panel_name)) { + pr_err("%s: Failed to get %s", __func__, panel_node); + return false; + } + + if (strstr(panel_name, touch_ic_name)) + return true; + + return false; +} +EXPORT_SYMBOL(ts_mmi_is_panel_match); + +static int __init touchscreens_init(void) +{ + int error = 0; + pr_info("touchscreen_class: moto touchscreen class init!\n"); + if (touchscreens_class) { + pr_info("touchscreen_class: moto touchscreen already exist!\n"); + return 0; + } + touchscreens_class = class_create(THIS_MODULE, "touchscreen"); + if (IS_ERR(touchscreens_class)) { + error = PTR_ERR(touchscreens_class); + touchscreens_class = NULL; + pr_err("touchscreen_class: touchscreen class init fail!\n"); + return error; + } + pr_info("touchscreen_class: moto touchscreen class init success!\n"); + return 0; +} + +static void __exit touchscreens_exit(void) +{ + if (touchscreens_class) + class_destroy(touchscreens_class); +} + +subsys_initcall(touchscreens_init); +module_exit(touchscreens_exit); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/touchscreen_mmi/touchscreen_mmi_gesture.c b/drivers/input/touchscreen/touchscreen_mmi/touchscreen_mmi_gesture.c new file mode 100644 index 000000000000..1372b6b90511 --- /dev/null +++ b/drivers/input/touchscreen/touchscreen_mmi/touchscreen_mmi_gesture.c @@ -0,0 +1,771 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef TS_MMI_TOUCH_GESTURE_POISON_EVENT +/* Declare depended feature */ +#ifndef TS_MMI_TOUCH_GESTURE_LOG_EVENT +#define TS_MMI_TOUCH_GESTURE_LOG_EVENT +#endif + +#ifndef TS_MMI_TOUCH_GESTURE_REPORT_TOUCH_EVENT +#define TS_MMI_TOUCH_GESTURE_REPORT_TOUCH_EVENT +#endif + +#ifndef TS_MMI_TOUCH_GESTURE_SUPPRESSION +#define TS_MMI_TOUCH_GESTURE_SUPPRESSION +#endif + +#ifndef TS_MMI_TOUCH_GESTURE_GS_DISTANCE +#define TS_MMI_TOUCH_GESTURE_GS_DISTANCE +#endif +#endif /* TS_MMI_TOUCH_GESTURE_POISON_EVENT */ + +#ifdef TS_MMI_TOUCH_GESTURE_SUPPRESSION +/* Declare depended feature */ +#ifndef TS_MMI_TOUCH_GESTURE_GS_DISTANCE +#define TS_MMI_TOUCH_GESTURE_GS_DISTANCE +#endif +#endif /* TS_MMI_TOUCH_GESTURE_SUPPRESSION */ + + +struct ts_mmi_sensor_platform_data { + struct input_dev *input_sensor_dev; + struct sensors_classdev ps_cdev; + int sensor_opened; + char sensor_data; /* 0 near, 1 far */ + struct ts_mmi_dev *touch_cdev; +}; + +#ifdef TS_MMI_TOUCH_GESTURE_LOG_EVENT +struct touch_event_with_time_data { + struct touch_event_data event; + ktime_t time; +}; +#endif + +#ifdef TS_MMI_TOUCH_GESTURE_POISON_EVENT +struct touch_event_slot_poison_data { + struct touch_event_with_time_data center; + bool is_slot_poisoned; +}; +#endif + +struct ts_mmi_touch_events_data { + struct ts_mmi_dev *touch_cdev; +#ifdef TS_MMI_TOUCH_GESTURE_POISON_EVENT + /* Touch slot is poisoned, do not report this slot point until slot release or IC resumed. */ + struct touch_event_slot_poison_data poison_events[TS_MMI_MAX_POINT_NUM]; +#endif +}; + +static struct ts_mmi_sensor_platform_data *sensor_pdata; +static struct ts_mmi_sensor_platform_data *palm_sensor_pdata; +static struct ts_mmi_touch_events_data *events_data; +static struct ts_mmi_sensor_platform_data *cli_sensor_pdata; + +#ifdef TS_MMI_TOUCH_GESTURE_LOG_EVENT +static inline void ts_mmi_touch_log_event(struct touch_event_with_time_data *dst, const struct touch_event_data *src) +{ + memcpy(&dst->event, src, sizeof(struct touch_event_data)); + dst->time = ktime_get(); +} + +static inline void ts_mmi_touch_clear_event(struct touch_event_with_time_data *dst) +{ + memset(dst, 0, sizeof(struct touch_event_with_time_data)); +} +#endif /* TS_MMI_TOUCH_GESTURE_LOG_EVENT */ + +#ifdef TS_MMI_TOUCH_GESTURE_REPORT_TOUCH_EVENT +static inline void ts_mmi_touch_event_report_event_release(int index, struct input_dev *input_dev) +{ + input_mt_slot(input_dev, index); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); +} +#endif + +#ifdef TS_MMI_TOUCH_GESTURE_GS_DISTANCE +static inline bool ts_mmi_touch_can_handle_gs_distance(void) +{ + struct ts_mmi_dev *touch_cdev = events_data->touch_cdev; + + return (touch_cdev->pdata.gs_distance_ctrl); +} + +static inline int ts_mmi_touch_event_get_gs_distance(struct ts_mmi_dev *touch_cdev) +{ + return (ts_mmi_touch_can_handle_gs_distance()) ? touch_cdev->gs_distance : TOUCHSCREEN_MMI_DEFAULT_GS_DISTANCE; +} +#endif /* TS_MMI_TOUCH_GESTURE_GS_DISTANCE */ + +#ifdef TS_MMI_TOUCH_GESTURE_SUPPRESSION +static inline bool ts_mmi_touch_can_handle_suppression(void) +{ + struct ts_mmi_dev *touch_cdev = events_data->touch_cdev; + + return ((touch_cdev->pdata.suppression_ctrl) && + (touch_cdev->pdata.gs_distance_ctrl) && + (touch_cdev->pdata.max_x > 0) && + (touch_cdev->pdata.max_y > 0)); +} + +static inline bool ts_mmi_touch_event_is_in_suppression(struct touch_event_data *tev, struct ts_mmi_dev *touch_cdev) +{ + int gs_distance = ts_mmi_touch_event_get_gs_distance(touch_cdev); + + if (touch_cdev->suppression == TS_MMI_DISABLE_SUPPRESSION_ALL) + return false; + if (touch_cdev->suppression == TS_MMI_DISABLE_SUPPRESSION_LEFT) + return (tev->x > (touch_cdev->pdata.max_x - gs_distance)) ? true : false; + if (touch_cdev->suppression == TS_MMI_DISABLE_SUPPRESSION_RIGHT) + return (tev->x < gs_distance) ? true : false; + + return ((tev->x < gs_distance) || (tev->x > (touch_cdev->pdata.max_x - gs_distance))) ? true : false; +} +#endif /* TS_MMI_TOUCH_GESTURE_SUPPRESSION */ + +#ifdef TS_MMI_TOUCH_GESTURE_POISON_EVENT +static inline bool ts_mmi_touch_event_rescue_slot(int slot_id) +{ + if (events_data->poison_events[slot_id].is_slot_poisoned) { + ts_mmi_touch_clear_event(&events_data->poison_events[slot_id].center); + events_data->poison_events[slot_id].is_slot_poisoned = false; + return true; + } + return false; +} + +static inline bool ts_mmi_touch_can_handle_poison_slot(void) +{ + struct ts_mmi_dev *touch_cdev = events_data->touch_cdev; + + return ((touch_cdev->pdata.suppression_ctrl) && + (touch_cdev->pdata.gs_distance_ctrl) && + (touch_cdev->pdata.poison_slot_ctrl) && + (touch_cdev->pdata.max_x > 0) && + (touch_cdev->pdata.max_y > 0)); +} + +static inline bool ts_mmi_touch_event_is_in_poison_trigger(struct touch_event_data *tev, struct ts_mmi_dev *touch_cdev) +{ + int trigger_distance = touch_cdev->poison_trigger_distance; + + if (touch_cdev->suppression == TS_MMI_DISABLE_SUPPRESSION_ALL) + return false; + if (touch_cdev->suppression == TS_MMI_DISABLE_SUPPRESSION_LEFT) + return (tev->x > (touch_cdev->pdata.max_x - trigger_distance)) ? true : false; + if (touch_cdev->suppression == TS_MMI_DISABLE_SUPPRESSION_RIGHT) + return (tev->x < trigger_distance) ? true : false; + + return ((tev->x < trigger_distance) || (tev->x > (touch_cdev->pdata.max_x - trigger_distance))) ? true : false; +} + +static inline bool need_handle_poison_event(int id) +{ + return ((events_data->poison_events[id].center.event.type == TS_COORDINATE_ACTION_PRESS) || + (events_data->poison_events[id].center.event.type == TS_COORDINATE_ACTION_MOVE)); +} + +static inline bool is_far_from_poison_center(struct touch_event_data *tev) +{ + int delta_y = abs(tev->y - events_data->poison_events[tev->id].center.event.y); + return (delta_y > events_data->touch_cdev->poison_distance) ? true : false; +} + +static inline bool is_stack_at_poison_center(struct touch_event_data *tev) +{ + if (events_data->poison_events[tev->id].center.time) { + unsigned long long duration = timediff_ms( + ktime_to_timespec(events_data->poison_events[tev->id].center.time), + ktime_to_timespec(ktime_get())); + + return (duration > events_data->touch_cdev->poison_timeout) ? true : false; + } + + return false; +} + +static inline void update_poison_center(struct touch_event_data *tev) +{ + return ts_mmi_touch_log_event(&events_data->poison_events[tev->id].center, tev); +} +#endif /* TS_MMI_TOUCH_GESTURE_POISON_EVENT */ + +static int ts_mmi_gesture_handler(struct gesture_event_data *gev) +{ + int key_code; + bool need2report = true; + struct ts_mmi_dev *touch_cdev = sensor_pdata->touch_cdev; + + switch (gev->evcode) { + case 1: + key_code = BTN_TRIGGER_HAPPY3; + pr_info("%s: single tap\n", __func__); + break; + case 2: + key_code = BTN_TRIGGER_HAPPY4; + if(gev->evdata.x == 0) + gev->evdata.x = touch_cdev->pdata.fod_x ; + if(gev->evdata.y== 0) + gev->evdata.y = touch_cdev->pdata.fod_y; + input_report_abs(sensor_pdata->input_sensor_dev, ABS_X, gev->evdata.x); + input_report_abs(sensor_pdata->input_sensor_dev, ABS_Y, gev->evdata.y); + pr_info("%s: zero tap; x=%x, y=%x\n", __func__, gev->evdata.x, gev->evdata.y); + break; + case 3: + key_code = BTN_TRIGGER_HAPPY5; + pr_info("%s: zero tap up\n", __func__); + break; + case 4: + key_code = BTN_TRIGGER_HAPPY6; + pr_info("%s: double tap\n", __func__); + break; + default: + need2report = false; + pr_info("%s: unknown id=%x\n", __func__, gev->evcode); + } + + if (!need2report) + return 1; + + input_report_key(sensor_pdata->input_sensor_dev, key_code, 1); + input_sync(sensor_pdata->input_sensor_dev); + input_report_key(sensor_pdata->input_sensor_dev, key_code, 0); + input_sync(sensor_pdata->input_sensor_dev); + + return 0; +} + +static int ts_mmi_cli_gesture_handler(struct gesture_event_data *gev) +{ + int key_code; + bool need2report = true; + struct ts_mmi_dev *touch_cdev = cli_sensor_pdata->touch_cdev; + + switch (gev->evcode) { + case 1: + key_code = BTN_TRIGGER_HAPPY3; + pr_info("%s: single tap\n", __func__); + break; + case 2: + key_code = BTN_TRIGGER_HAPPY4; + if(gev->evdata.x == 0) + gev->evdata.x = touch_cdev->pdata.fod_x ; + if(gev->evdata.y== 0) + gev->evdata.y = touch_cdev->pdata.fod_y; + input_report_abs(cli_sensor_pdata->input_sensor_dev, ABS_X, gev->evdata.x); + input_report_abs(cli_sensor_pdata->input_sensor_dev, ABS_Y, gev->evdata.y); + pr_info("%s: zero tap; x=%x, y=%x\n", __func__, gev->evdata.x, gev->evdata.y); + break; + case 3: + key_code = BTN_TRIGGER_HAPPY5; + pr_info("%s: zero tap up\n", __func__); + break; + case 4: + key_code = BTN_TRIGGER_HAPPY6; + pr_info("%s: double tap\n", __func__); + break; + default: + need2report = false; + pr_info("%s: unknown id=%x\n", __func__, gev->evcode); + } + + if (!need2report) + return 1; + + input_report_key(cli_sensor_pdata->input_sensor_dev, key_code, 1); + input_sync(cli_sensor_pdata->input_sensor_dev); + input_report_key(cli_sensor_pdata->input_sensor_dev, key_code, 0); + input_sync(cli_sensor_pdata->input_sensor_dev); + + return 0; +} + +static int ts_mmi_palm_handler(bool value) +{ + if (!palm_sensor_pdata->input_sensor_dev) + return 0; + + if (value) { + input_report_abs(palm_sensor_pdata->input_sensor_dev, + ABS_DISTANCE, 1); + pr_info("%s: palm report 1\n", __func__); + } else { + input_report_abs(palm_sensor_pdata->input_sensor_dev, + ABS_DISTANCE, 0); + pr_info("%s: palm report 0\n", __func__); + } + input_sync(palm_sensor_pdata->input_sensor_dev); + return 0; +} + + +#ifdef TS_MMI_TOUCH_GESTURE_POISON_EVENT +static int ts_mmi_touch_event_poison_slot_handler(struct touch_event_data *tev, struct input_dev *input_dev) +{ + struct ts_mmi_dev *touch_cdev = events_data->touch_cdev; + + if (tev->type == TS_COORDINATE_ACTION_PRESS) { + if (ts_mmi_touch_event_is_in_poison_trigger(tev, touch_cdev)) { + update_poison_center(tev); + } + } else if (tev->type == TS_COORDINATE_ACTION_RELEASE) { + if (need_handle_poison_event(tev->id)) { + if (events_data->poison_events[tev->id].is_slot_poisoned) { + if (ts_mmi_touch_event_rescue_slot(tev->id)) + pr_info("%s: Poison slot(%d) rescued\n", __func__, tev->id); + tev->skip_report = true; + } + } + } else if (tev->type == TS_COORDINATE_ACTION_MOVE) { + if (need_handle_poison_event(tev->id)) { + if (events_data->poison_events[tev->id].is_slot_poisoned) { + if (ts_mmi_touch_event_is_in_poison_trigger(tev, touch_cdev)) { + dev_dbg(DEV_TS, "%s: slot(%d) is poisoned\n", __func__, tev->id); + tev->skip_report = true; + tev->type = TS_COORDINATE_ACTION_NONE; + } else { + events_data->poison_events[tev->id].is_slot_poisoned = false; + pr_info("%s: Poison slot(%d) temperary rescued because outof trigger area\n", __func__, tev->id); + } + } else if (is_far_from_poison_center(tev) && ts_mmi_touch_event_is_in_suppression(tev, touch_cdev)) { + update_poison_center(tev); + } else if (is_stack_at_poison_center(tev) && ts_mmi_touch_event_is_in_suppression(tev, touch_cdev)) { + dev_info(DEV_TS, "%s: slot(%d) is stacked near poison center. Mark this slot poisoned\n", __func__, tev->id); + ts_mmi_touch_event_report_event_release(tev->id, input_dev); + events_data->poison_events[tev->id].is_slot_poisoned = true; + tev->skip_report = true; + tev->type = TS_COORDINATE_ACTION_NONE; + } + } + } + return 0; +} +#endif /* TS_MMI_TOUCH_GESTURE_POISON_EVENT */ + +#ifdef TS_MMI_TOUCH_EDGE_GESTURE +static int ts_mmi_touch_event_edge_handler(struct touch_event_data *tev, struct input_dev *input_dev) +{ +#ifdef TS_MMI_TOUCH_GESTURE_POISON_EVENT + if (ts_mmi_touch_can_handle_poison_slot()) + ts_mmi_touch_event_poison_slot_handler(tev, input_dev); +#endif + + return 0; +} +#endif + +static int ts_mmi_touch_event_handler(struct touch_event_data *tev, struct input_dev *input_dev) +{ + struct ts_mmi_dev *touch_cdev = events_data->touch_cdev; + __maybe_unused int ret = 0; + +#ifdef TS_MMI_TOUCH_EDGE_GESTURE + ts_mmi_touch_event_edge_handler(tev, input_dev); +#endif + + switch (tev->type) { + case TS_COORDINATE_ACTION_PRESS: +#if defined(CONFIG_TOUCHCLASS_MMI_DEBUG_INFO) + pr_info("%s: [P]Finger %d: Down, x=%d, y=%d, major=%d, minor=%d", + __func__, tev->id, tev->x, tev->y, tev->major, tev->minor); +#endif + case TS_COORDINATE_ACTION_MOVE: + break; + + case TS_COORDINATE_ACTION_RELEASE: +#if defined(CONFIG_TOUCHCLASS_MMI_DEBUG_INFO) + pr_info("%s: [R]Finger %d: UP", __func__, tev->id); +#endif + + if (touch_cdev->pdata.fps_detection) { + if ((touch_cdev->delay_baseline_update) && + mutex_trylock(&touch_cdev->method_mutex)) { + _TRY_TO_CALL(update_baseline, TS_MMI_UPDATE_BASELINE_ON); + touch_cdev->delay_baseline_update = false; + mutex_unlock(&touch_cdev->method_mutex); + } + } + break; + case TS_COORDINATE_ACTION_NONE: + break; + default: + pr_info("%s: unsupport type=%d\n", __func__, tev->type); + + } + + return 0; +} + +bool ts_mmi_is_sensor_enable(void) +{ + if (sensor_pdata != NULL) { + return !!sensor_pdata->sensor_opened; + } + else + return false; +} + +static int ts_mmi_sensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ +#if !defined(CONFIG_BOARD_USES_DOUBLE_TAP_CTRL) + struct ts_mmi_sensor_platform_data *sensor_pdata = container_of( + sensors_cdev, struct ts_mmi_sensor_platform_data, ps_cdev); + struct ts_mmi_dev *touch_cdev = sensor_pdata->touch_cdev; + + sensor_pdata->sensor_opened = enable; + if (enable == 1) { + dev_info(DEV_TS, "%s: sensor ENABLE\n", __func__); + } else if (enable == 0) { + dev_info(DEV_TS, "%s: sensor DISABLE\n", __func__); + } else { + dev_err(DEV_TS, "%s: unknown enable symbol\n", __func__); + } +#endif + return 0; +} + +static int ts_mmi_palm_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + struct ts_mmi_sensor_platform_data *sensor_pdata = container_of( + sensors_cdev, struct ts_mmi_sensor_platform_data, ps_cdev); + struct ts_mmi_dev *touch_cdev = sensor_pdata->touch_cdev; + int ret = 0; + + mutex_lock(&touch_cdev->extif_mutex); + TRY_TO_CALL(palm_set_enable, enable); + if (enable == 1) { + touch_cdev->gesture_mode_type |= 0x08; + dev_info(DEV_TS, "%s: sensor ENABLE\n", __func__); + } else if (enable == 0) { + touch_cdev->gesture_mode_type &= 0xF7; + dev_info(DEV_TS, "%s: sensor DISABLE\n", __func__); + } else { + dev_err(DEV_TS, "%s: unknown enable symbol\n", __func__); + } + mutex_unlock(&touch_cdev->extif_mutex); + + return 0; +} + +static struct sensors_classdev __maybe_unused sensors_touch_cdev = { + .name = "dt-gesture", + .vendor = "Motorola", + .version = 1, + .type = SENSOR_TYPE_MOTO_DOUBLE_TAP, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + /* WAKE_UP & SPECIAL_REPORT */ + .flags = 1 | 6, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; + +static struct sensors_classdev __maybe_unused cli_sensors_touch_cdev = { + .name = "s-dt-gesture", + .vendor = "Motorola", + .version = 1, + .type = SENSOR_TYPE_MOTO_DOUBLE_TAP, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + /* WAKE_UP & SPECIAL_REPORT */ + .flags = 1 | 6, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; + +static struct sensors_classdev __maybe_unused palm_sensors_touch_cdev = { + .name = "palm-gesture", + .vendor = "Motorola", + .version = 1, + .type = SENSOR_TYPE_MOTO_TOUCH_PALM, + .max_range = "5.0", + .resolution = "5.0", + .sensor_power = "1", + .min_delay = 0, + .max_delay = 0, + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 200, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; + +#ifdef TS_MMI_TOUCH_EDGE_GESTURE +int ts_mmi_gesture_suspend(struct ts_mmi_dev *touch_cdev) +{ +#ifdef TS_MMI_TOUCH_GESTURE_POISON_EVENT + int i; + for (i = 0; i < TS_MMI_MAX_POINT_NUM; i++) + if (ts_mmi_touch_event_rescue_slot(i)) + pr_info("%s: Poison slot(%d) rescued\n", __func__, i); +#endif + return 0; +} +#endif + +int ts_mmi_gesture_init(struct ts_mmi_dev *touch_cdev) +{ + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + dev_err(DEV_TS, "%s: Failed to allocate device", __func__); + goto exit; + } + + sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct ts_mmi_sensor_platform_data), GFP_KERNEL); + if (!sensor_pdata) { + dev_err(DEV_TS, "%s: Failed to allocate memory", __func__); + goto free_sensor_input_dev; + } + events_data = devm_kzalloc(DEV_TS, + sizeof(struct ts_mmi_touch_events_data), GFP_KERNEL); + if (!events_data) { + dev_err(DEV_TS, "%s: Failed to allocate memory", __func__); + goto free_sensor_pdata; + } + events_data->touch_cdev = touch_cdev; + + __set_bit(EV_KEY, sensor_input_dev->evbit); + __set_bit(BTN_TRIGGER_HAPPY3, sensor_input_dev->keybit); + __set_bit(BTN_TRIGGER_HAPPY4, sensor_input_dev->keybit); + __set_bit(BTN_TRIGGER_HAPPY5, sensor_input_dev->keybit); + __set_bit(BTN_TRIGGER_HAPPY6, sensor_input_dev->keybit); + __set_bit(EV_ABS, sensor_input_dev->evbit); + __set_bit(EV_SYN, sensor_input_dev->evbit); + /* TODO: fill in real screen resolution */ + input_set_abs_params(sensor_input_dev, ABS_X, 0, 4096, 0, 0); + input_set_abs_params(sensor_input_dev, ABS_Y, 0, 4096, 0, 0); + + sensor_input_dev->name = "double-tap"; + sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + dev_err(DEV_TS, "%s: Unable to register device, err=%d", __func__, err); + goto free_touch_events_data; + } + + sensor_pdata->ps_cdev = sensors_touch_cdev; + sensor_pdata->ps_cdev.sensors_enable = ts_mmi_sensor_set_enable; + sensor_pdata->touch_cdev = touch_cdev; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + + /* export report gesture function to vendor */ + touch_cdev->mdata->exports.report_gesture = ts_mmi_gesture_handler; + /* export report touch event function to vendor */ + touch_cdev->mdata->exports.report_touch_event = ts_mmi_touch_event_handler; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(sensor_input_dev); + sensor_input_dev = NULL; +free_touch_events_data: + if (events_data) + devm_kfree(DEV_TS, events_data); +free_sensor_pdata: + if (sensor_input_dev && sensor_pdata) + devm_kfree(&sensor_input_dev->dev, sensor_pdata); +free_sensor_input_dev: + if (sensor_input_dev) + input_free_device(sensor_input_dev); +exit: + return 1; +} + +int ts_mmi_gesture_remove(struct ts_mmi_dev *touch_cdev) +{ + sensors_classdev_unregister(&sensor_pdata->ps_cdev); + input_unregister_device(sensor_pdata->input_sensor_dev); + devm_kfree(&sensor_pdata->input_sensor_dev->dev, sensor_pdata); + devm_kfree(DEV_TS, events_data); + sensor_pdata = NULL; + events_data = NULL; + + return 0; +} + +int ts_mmi_cli_gesture_init(struct ts_mmi_dev *touch_cdev) +{ + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + dev_err(DEV_TS, "%s: Failed to allocate input device", __func__); + goto exit; + } + + cli_sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct ts_mmi_sensor_platform_data), GFP_KERNEL); + if (!cli_sensor_pdata) { + dev_err(DEV_TS, "%s: Failed to allocate memory", __func__); + goto free_sensor_input_dev; + } + + __set_bit(EV_KEY, sensor_input_dev->evbit); + __set_bit(BTN_TRIGGER_HAPPY3, sensor_input_dev->keybit); + __set_bit(BTN_TRIGGER_HAPPY4, sensor_input_dev->keybit); + __set_bit(BTN_TRIGGER_HAPPY5, sensor_input_dev->keybit); + __set_bit(BTN_TRIGGER_HAPPY6, sensor_input_dev->keybit); + __set_bit(EV_ABS, sensor_input_dev->evbit); + __set_bit(EV_SYN, sensor_input_dev->evbit); + /* TODO: fill in real screen resolution */ + input_set_abs_params(sensor_input_dev, ABS_X, 0, 4096, 0, 0); + input_set_abs_params(sensor_input_dev, ABS_Y, 0, 4096, 0, 0); + + sensor_input_dev->name = "s-double-tap"; + cli_sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + dev_err(DEV_TS, "%s: Unable to register device, err=%d", __func__, err); + goto free_sensor_pdata; + } + + cli_sensor_pdata->ps_cdev = cli_sensors_touch_cdev; + cli_sensor_pdata->ps_cdev.sensors_enable = ts_mmi_sensor_set_enable; + cli_sensor_pdata->touch_cdev = touch_cdev; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &cli_sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + + /* export report gesture function to vendor */ + touch_cdev->mdata->exports.report_gesture = ts_mmi_cli_gesture_handler; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(sensor_input_dev); + sensor_input_dev = NULL; +free_sensor_pdata: + if (sensor_input_dev && cli_sensor_pdata) + devm_kfree(&sensor_input_dev->dev, cli_sensor_pdata); +free_sensor_input_dev: + if (sensor_input_dev) + input_free_device(sensor_input_dev); +exit: + return 1; +} + +int ts_mmi_cli_gesture_remove(struct ts_mmi_dev *touch_cdev) +{ + sensors_classdev_unregister(&cli_sensor_pdata->ps_cdev); + input_unregister_device(cli_sensor_pdata->input_sensor_dev); + devm_kfree(&cli_sensor_pdata->input_sensor_dev->dev, cli_sensor_pdata); + cli_sensor_pdata = NULL; + + return 0; +} + +int ts_mmi_palm_init(struct ts_mmi_dev *touch_cdev) +{ + struct input_dev *sensor_input_dev; + int err; + + sensor_input_dev = input_allocate_device(); + if (!sensor_input_dev) { + dev_err(DEV_TS, "%s: Failed to allocate device", __func__); + goto exit; + } + + palm_sensor_pdata = devm_kzalloc(&sensor_input_dev->dev, + sizeof(struct ts_mmi_sensor_platform_data), + GFP_KERNEL); + if (!palm_sensor_pdata) { + dev_err(DEV_TS, "%s: Failed to allocate memory", __func__); + goto free_sensor_pdata; + } + + __set_bit(EV_ABS, sensor_input_dev->evbit); + __set_bit(EV_SYN, sensor_input_dev->evbit); + input_set_abs_params(sensor_input_dev, ABS_DISTANCE, + 0, 5, 0, 0); + sensor_input_dev->name = "palm_detect"; + palm_sensor_pdata->input_sensor_dev = sensor_input_dev; + + err = input_register_device(sensor_input_dev); + if (err) { + dev_err(DEV_TS, "Unable to register device, err=%d", err); + goto free_sensor_input_dev; + } + + palm_sensor_pdata->ps_cdev = palm_sensors_touch_cdev; + palm_sensor_pdata->ps_cdev.sensors_enable = ts_mmi_palm_set_enable; + palm_sensor_pdata->touch_cdev = touch_cdev; + + err = sensors_classdev_register(&sensor_input_dev->dev, + &palm_sensor_pdata->ps_cdev); + if (err) + goto unregister_sensor_input_device; + + /* export report gesture function to vendor */ + touch_cdev->mdata->exports.report_palm = ts_mmi_palm_handler; + + return 0; + +unregister_sensor_input_device: + input_unregister_device(sensor_input_dev); +free_sensor_input_dev: + input_free_device(sensor_input_dev); +free_sensor_pdata: + devm_kfree(&sensor_input_dev->dev, palm_sensor_pdata); + palm_sensor_pdata = NULL; +exit: + return 1; +} + +int ts_mmi_palm_remove(struct ts_mmi_dev *touch_cdev) +{ + sensors_classdev_unregister(&palm_sensor_pdata->ps_cdev); + input_unregister_device(palm_sensor_pdata->input_sensor_dev); + devm_kfree(&palm_sensor_pdata->input_sensor_dev->dev, palm_sensor_pdata); + palm_sensor_pdata = NULL; + + return 0; +} diff --git a/drivers/input/touchscreen/touchscreen_mmi/touchscreen_mmi_notif.c b/drivers/input/touchscreen/touchscreen_mmi/touchscreen_mmi_notif.c new file mode 100644 index 000000000000..f6f6892ddc1d --- /dev/null +++ b/drivers/input/touchscreen/touchscreen_mmi/touchscreen_mmi_notif.c @@ -0,0 +1,664 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_DRM_DYNAMIC_REFRESH_RATE) +extern struct blocking_notifier_head dsi_freq_head; +#define register_dynamic_refresh_rate_notifier(_nb) \ + blocking_notifier_chain_register(&dsi_freq_head, _nb) +#define unregister_dynamic_refresh_rate_notifier(_nb) \ + blocking_notifier_chain_unregister(&dsi_freq_head, _nb) +#else +#define register_dynamic_refresh_rate_notifier(...) ret +#define unregister_dynamic_refresh_rate_notifier(...) +#endif + +#define IS_GESTURE_MODE (touch_cdev->pm_mode == TS_MMI_PM_GESTURE ? 1 : 0) +#define IS_DEEPSLEEP_MODE (touch_cdev->pm_mode == TS_MMI_PM_DEEPSLEEP ? 1 : 0) +#define IS_ACTIVE_MODE (touch_cdev->pm_mode == TS_MMI_PM_ACTIVE ? 1 : 0) +#define NEED_TO_SET_PINCTRL \ + (touch_cdev->pdata.power_off_suspend && \ + touch_cdev->mdata->pinctrl && \ + IS_DEEPSLEEP_MODE) +#define NEED_TO_SET_POWER \ + (touch_cdev->pdata.power_off_suspend && \ + touch_cdev->mdata->power && \ + IS_DEEPSLEEP_MODE) + +static int ts_mmi_panel_off(struct ts_mmi_dev *touch_cdev) { + int ret = 0; + + if (atomic_cmpxchg(&touch_cdev->touch_stopped, 0, 1) == 1) + return 0; + + atomic_set(&touch_cdev->resume_should_stop, 1); + + TRY_TO_CALL(pre_suspend); + if (touch_cdev->pdata.gestures_enabled || touch_cdev->pdata.cli_gestures_enabled || + touch_cdev->pdata.support_liquid_detection || touch_cdev->pdata.palm_enabled) { +#if defined(CONFIG_BOARD_USES_DOUBLE_TAP_CTRL) + if(touch_cdev->gesture_mode_type != 0 || touch_cdev->pdata.support_liquid_detection != 0) { + dev_info(DEV_MMI, "%s: try to enter Gesture mode\n", __func__); + TRY_TO_CALL(panel_state, touch_cdev->pm_mode, TS_MMI_PM_GESTURE); + touch_cdev->pm_mode = TS_MMI_PM_GESTURE; + } +#else + if(ts_mmi_is_sensor_enable()) { + dev_info(DEV_MMI, "%s: try to enter Gesture mode\n", __func__); + TRY_TO_CALL(panel_state, touch_cdev->pm_mode, TS_MMI_PM_GESTURE); + touch_cdev->pm_mode = TS_MMI_PM_GESTURE; + } +#endif + } + if (IS_ACTIVE_MODE) { + /* IC power is off. IRQ pin status is floated. So disable IRQ. */ + dev_info(DEV_MMI, "%s: try to enter Deepsleep mode\n", __func__); + TRY_TO_CALL(panel_state, touch_cdev->pm_mode, TS_MMI_PM_DEEPSLEEP); + TRY_TO_CALL(drv_irq, TS_MMI_IRQ_OFF); + touch_cdev->pm_mode = TS_MMI_PM_DEEPSLEEP; + } + +#ifdef TS_MMI_TOUCH_EDGE_GESTURE + if (touch_cdev->pdata.gestures_enabled) + ts_mmi_gesture_suspend(touch_cdev); +#endif + TRY_TO_CALL(post_suspend); + + dev_info(DEV_MMI, "%s: done\n", __func__); + + return 0; +} + +static int inline ts_mmi_panel_on(struct ts_mmi_dev *touch_cdev) { + atomic_set(&touch_cdev->resume_should_stop, 0); + kfifo_put(&touch_cdev->cmd_pipe, TS_MMI_DO_RESUME); + /* schedule_delayed_work returns true if work has been scheduled */ + /* and false otherwise, thus return 0 on success to comply POSIX */ + return schedule_delayed_work(&touch_cdev->work, 0) == false; +} + +static int ts_mmi_panel_event_handle(struct ts_mmi_dev *touch_cdev, enum ts_mmi_panel_event event) +{ + int ret = 0; + /* entering suspend upon early blank event */ + /* to ensure shared power supply is still on */ + /* for in-cell design touch solutions */ + switch (event) { + case TS_MMI_EVENT_PRE_DISPLAY_OFF: + cancel_delayed_work_sync(&touch_cdev->work); + ts_mmi_panel_off(touch_cdev); + if (NEED_TO_SET_PINCTRL) { + dev_dbg(DEV_MMI, "%s: touch pinctrl off\n", __func__); + TRY_TO_CALL(pinctrl, TS_MMI_PINCTL_OFF); + } + break; + + case TS_MMI_EVENT_DISPLAY_OFF: + if (NEED_TO_SET_POWER) { + /* then proceed with de-powering */ + TRY_TO_CALL(power, TS_MMI_POWER_OFF); + dev_dbg(DEV_MMI, "%s: touch powered off\n", __func__); + } + break; + + case TS_MMI_EVENT_PRE_DISPLAY_ON: + if (NEED_TO_SET_POWER) { + /* powering on early */ + TRY_TO_CALL(power, TS_MMI_POWER_ON); + dev_dbg(DEV_MMI, "%s: touch powered on\n", __func__); + } else if (touch_cdev->pdata.reset && + touch_cdev->mdata->reset) { + /* Power is not off in previous suspend. + * But need reset IC in resume. + */ + dev_dbg(DEV_MMI, "%s: resetting...\n", __func__); + TRY_TO_CALL(reset, TS_MMI_RESET_HARD); + } + break; + + case TS_MMI_EVENT_DISPLAY_ON: + /* out of reset to allow wait for boot complete */ + if (NEED_TO_SET_PINCTRL) { + TRY_TO_CALL(pinctrl, TS_MMI_PINCTL_ON); + dev_dbg(DEV_MMI, "%s: touch pinctrl_on\n", __func__); + } + ts_mmi_panel_on(touch_cdev); + break; + + default: + dev_dbg(DEV_TS, "%s: function not implemented\n", __func__); + break; + } + + return ret; +} + +#ifdef CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS +static void ts_mmi_panel_cb(enum panel_event_notifier_tag tag, + struct panel_event_notification *notification, void *client_data) +{ + int event = 0; + enum ts_mmi_panel_event panel_event; + struct ts_mmi_dev *touch_cdev = client_data; + struct panel_event_notification_data evdata; + + if (!touch_cdev || !notification) { + dev_err(DEV_MMI, "%s: invalid evdata\n", __func__); \ + return; + } + + event = notification->notif_type; + evdata = notification->notif_data; + + dev_dbg(DEV_MMI, "%s: %s Notify_type=%d, early=%d, ctrl_dsi=%d\n", __func__, + EVENT_PRE_DISPLAY_OFF ? "EVENT_PRE_DISPLAY_OFF" : + (EVENT_DISPLAY_OFF ? "EVENT_DISPLAY_OFF" : + (EVENT_PRE_DISPLAY_ON ? "EVENT_PRE_DISPLAY_ON" : + (EVENT_DISPLAY_ON ? "EVENT_DISPLAY_ON" : "Unknown"))), + event, (evdata.early_trigger ? 1 : 0), touch_cdev->pdata.ctrl_dsi); + + panel_event = EVENT_PRE_DISPLAY_OFF ? TS_MMI_EVENT_PRE_DISPLAY_OFF : + (EVENT_DISPLAY_OFF ? TS_MMI_EVENT_DISPLAY_OFF : + (EVENT_PRE_DISPLAY_ON ? TS_MMI_EVENT_PRE_DISPLAY_ON : + (EVENT_DISPLAY_ON ? TS_MMI_EVENT_DISPLAY_ON : TS_MMI_EVENT_UNKNOWN))); + + ts_mmi_panel_event_handle(touch_cdev, panel_event); + + return; +} +#else +static int ts_mmi_panel_cb(struct notifier_block *nb, + unsigned long event, void *evd) +{ + int idx = -1; + int ret = 0; + enum ts_mmi_panel_event panel_event; + struct ts_mmi_dev *touch_cdev = + container_of(nb, struct ts_mmi_dev, panel_nb); + + GET_CONTROL_DSI_INDEX; + + if (!touch_cdev) + return 0; + + dev_dbg(DEV_MMI,"%s: %s event(%lu), ctrl_dsi=%d, idx=%d\n", __func__, + EVENT_PRE_DISPLAY_OFF ? "EVENT_PRE_DISPLAY_OFF" : + (EVENT_DISPLAY_OFF ? "EVENT_DISPLAY_OFF" : + (EVENT_PRE_DISPLAY_ON ? "EVENT_PRE_DISPLAY_ON" : + (EVENT_DISPLAY_ON ? "EVENT_DISPLAY_ON" : "Unknown"))), + event, touch_cdev->pdata.ctrl_dsi, idx); + + if (touch_cdev->pdata.ctrl_dsi != idx) + return 0; + + panel_event = EVENT_PRE_DISPLAY_OFF ? TS_MMI_EVENT_PRE_DISPLAY_OFF : + (EVENT_DISPLAY_OFF ? TS_MMI_EVENT_DISPLAY_OFF : + (EVENT_PRE_DISPLAY_ON ? TS_MMI_EVENT_PRE_DISPLAY_ON : + (EVENT_DISPLAY_ON ? TS_MMI_EVENT_DISPLAY_ON : TS_MMI_EVENT_UNKNOWN))); + + ret = ts_mmi_panel_event_handle(touch_cdev, panel_event); + + return ret; +} +#endif + +static inline void ts_mmi_restore_settings(struct ts_mmi_dev *touch_cdev) +{ + int ret = 0; + + if (touch_cdev->pdata.usb_detection) + TRY_TO_CALL(charger_mode, (int)touch_cdev->ps_is_present); + if (touch_cdev->pdata.update_refresh_rate) + TRY_TO_CALL(refresh_rate, (int)touch_cdev->refresh_rate); + if (touch_cdev->pdata.suppression_ctrl) + TRY_TO_CALL(suppression, (int)touch_cdev->suppression); + if (touch_cdev->pdata.pill_region_ctrl) + TRY_TO_CALL(pill_region, (unsigned int *)touch_cdev->pill_region); + if (touch_cdev->pdata.hold_distance_ctrl) + TRY_TO_CALL(hold_distance, (int)touch_cdev->hold_distance); + if (touch_cdev->pdata.gs_distance_ctrl) + TRY_TO_CALL(gs_distance, (int)touch_cdev->gs_distance); + if (touch_cdev->pdata.active_region_ctrl) + TRY_TO_CALL(active_region, (unsigned int *)touch_cdev->active_region); + + dev_dbg(DEV_MMI, "%s: done\n", __func__); +} + +static void ts_mmi_queued_resume(struct ts_mmi_dev *touch_cdev) +{ + bool wait4_boot_complete = true; + int ret = 0; + + if (atomic_cmpxchg(&touch_cdev->touch_stopped, 1, 0) == 0) + return; + +#ifdef TS_MMI_TOUCH_MULTIWAY_UPDATE_FW + if (touch_cdev->flash_mode == FW_PARAM_MODE &&\ + touch_cdev->pdata.fw_load_resume) { + dev_info(DEV_MMI, "%s: send sysfs notify to touchUpg\n", __func__); + sysfs_notify(touch_cdev->mdata->exports.kobj_notify, NULL, "flash_mode"); + } +#endif + + TRY_TO_CALL(pre_resume); + + /* touch IC baseline update always done when IC resume. + * So touchscreen class need let vendor driver know baseline update work need to be done + * or not before vendor resume is called. + */ + if (touch_cdev->pdata.fps_detection) { + if (touch_cdev->fps_state) { + TRY_TO_CALL(update_baseline, TS_MMI_UPDATE_BASELINE_OFF); + touch_cdev->delay_baseline_update = true; + } + if (!touch_cdev->fps_state) { + TRY_TO_CALL(update_baseline, TS_MMI_UPDATE_BASELINE_ON); + touch_cdev->delay_baseline_update = false; + } + } + if (touch_cdev->pdata.fod_detection) { + TRY_TO_CALL(update_fod_mode, touch_cdev->fps_state); + } + if (NEED_TO_SET_POWER) { + /* power turn on in PANEL_EVENT_PRE_DISPLAY_ON. + * IC need some time to boot up. + * Check IC is ready or not. + */ + TRY_TO_CALL(wait_for_ready); + } else if (!touch_cdev->pdata.reset) { + /* IC power is not down in suspend. + * IC also do not need reset in resume. + * So IC RAM is not lost, just change IC working mode to normal mode. + */ + TRY_TO_CALL(panel_state, touch_cdev->pm_mode, TS_MMI_PM_ACTIVE); + wait4_boot_complete = false; + } + + if (wait4_boot_complete) { + /* IC is just power on or reseted. + * Need setup post resume work. + */ + if (touch_cdev->pdata.power_off_suspend) + TRY_TO_CALL(drv_irq, TS_MMI_IRQ_ON); + } + + if (IS_DEEPSLEEP_MODE) + TRY_TO_CALL(drv_irq, TS_MMI_IRQ_ON); + + TRY_TO_CALL(post_resume); + + /* Incase user space interface is R/W during restore cached value, + * hold extif mutex when restore those values. + */ + mutex_lock(&touch_cdev->extif_mutex); + ts_mmi_restore_settings(touch_cdev); + touch_cdev->pm_mode = TS_MMI_PM_ACTIVE; + mutex_unlock(&touch_cdev->extif_mutex); + dev_info(DEV_MMI, "%s: done\n", __func__); +} + +static void ts_mmi_worker_func(struct work_struct *w) +{ + struct delayed_work *dw = + container_of(w, struct delayed_work, work); + struct ts_mmi_dev *touch_cdev = + container_of(dw, struct ts_mmi_dev, work); + int ret, cmd = 0; +#if defined (CONFIG_DRM_PANEL_NOTIFICATIONS) || defined (CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS) + static DEFINE_RATELIMIT_STATE(register_panel, HZ, 1); +#endif + + while (kfifo_get(&touch_cdev->cmd_pipe, &cmd)) { + switch (cmd) { + case TS_MMI_DO_RESUME: + ret = atomic_read(&touch_cdev->resume_should_stop); + if (ret) { + dev_info(DEV_MMI, "%s: resume cancelled\n", __func__); + break; + } + ts_mmi_queued_resume(touch_cdev); + break; + + case TS_MMI_DO_PS: + TRY_TO_CALL(charger_mode, (int)touch_cdev->ps_is_present); + break; + + case TS_MMI_DO_REFRESH_RATE: + TRY_TO_CALL(refresh_rate, (int)touch_cdev->refresh_rate); + break; + case TS_MMI_DO_FPS: + if (touch_cdev->pdata.fps_detection) { + if (touch_cdev->fps_state) {/* on */ + TRY_TO_CALL(update_baseline, TS_MMI_UPDATE_BASELINE_OFF); + + touch_cdev->delay_baseline_update = true; + } else { /* off */ + if (touch_cdev->delay_baseline_update) { + TRY_TO_CALL(update_baseline, TS_MMI_UPDATE_BASELINE_ON); + touch_cdev->delay_baseline_update = false; + } + } + } + if (touch_cdev->pdata.fod_detection) { + TRY_TO_CALL(update_fod_mode, touch_cdev->fps_state); + } + break; + + case TS_MMI_TASK_INIT: +#if defined (CONFIG_DRM_PANEL_NOTIFICATIONS) || defined (CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS) + ret = ts_mmi_check_drm_panel(touch_cdev, DEV_TS->of_node); + if (ret < 0) { + /* 1 error message for every 1 sec */ + if (__ratelimit(®ister_panel)) + dev_err(DEV_TS, "%s: check drm panel failed. %d\n", __func__, ret); + touch_cdev->panel_status = -1; + } else + touch_cdev->panel_status = 0; +#endif + if (!touch_cdev->panel_status) { + REGISTER_PANEL_NOTIFIER; + dev_info(DEV_MMI, "%s: register panel notifier\n", __func__); + } + break; + + case TS_MMI_DO_LIQUID_DETECTION: + TRY_TO_CALL(update_liquid_detect_mode, touch_cdev->lpd_state); + break; + + default: + dev_dbg(DEV_MMI, "%s: unknown command %d\n", __func__, cmd); + } + } + + if (touch_cdev->panel_status == -1) { + kfifo_put(&touch_cdev->cmd_pipe, TS_MMI_TASK_INIT); + schedule_delayed_work(&touch_cdev->work, msecs_to_jiffies(100)); + } +} + +static int ts_mmi_refresh_rate_cb(struct notifier_block *nb, + unsigned long refresh_rate, void *ptr) +{ + struct ts_mmi_dev *touch_cdev = + container_of(nb, struct ts_mmi_dev, freq_nb); + bool do_calibration = false; + + if (!touch_cdev) + return 0; + + if (!touch_cdev->refresh_rate || + (touch_cdev->refresh_rate != (refresh_rate & 0xFF))) { + touch_cdev->refresh_rate = refresh_rate & 0xFF; + if (is_touch_active) + do_calibration = true; + } + + if (do_calibration) { + kfifo_put(&touch_cdev->cmd_pipe, TS_MMI_DO_REFRESH_RATE); + schedule_delayed_work(&touch_cdev->work, 0); + } + + return 0; +} + +static inline int ts_mmi_ps_get_state(struct power_supply *psy, bool *present) +{ + union power_supply_propval pval = {0}; + int ret; + + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &pval); + if (ret) { + ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, &pval); + if (ret) + return ret; + } + *present = !pval.intval ? false : true; + return 0; +} + +static int ts_mmi_charger_cb(struct notifier_block *self, + unsigned long event, void *ptr) +{ + struct ts_mmi_dev *touch_cdev = container_of( + self, struct ts_mmi_dev, ps_notif); + struct power_supply *psy = ptr; + int ret; + bool present; + + if (!((event == PSY_EVENT_PROP_CHANGED) && psy && + psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, "usb", sizeof("usb")))) + return 0; + + ret = ts_mmi_ps_get_state(psy, &present); + if (ret) { + dev_err(DEV_MMI, "%s: failed to get usb status: %d\n", + __func__, ret); + return ret; + } + + dev_dbg(DEV_MMI, "%s: event=%lu, usb status: cur=%d, prev=%d\n", + __func__, event, present, touch_cdev->ps_is_present); + + if (touch_cdev->ps_is_present != present) { + touch_cdev->ps_is_present = present; + if (is_touch_active) { + kfifo_put(&touch_cdev->cmd_pipe, TS_MMI_DO_PS); + schedule_delayed_work(&touch_cdev->work, 0); + } + } + + return 0; +} + +static int ts_mmi_fps_cb(struct notifier_block *self, + unsigned long event, void *p) +{ + int fps_state = *(int *)p; + struct ts_mmi_dev *touch_cdev = container_of( + self, struct ts_mmi_dev, fps_notif); + + if (touch_cdev && event == 0xBEEF && fps_state != touch_cdev->fps_state) { + touch_cdev->fps_state = fps_state; + dev_info(DEV_MMI, "FPS: state is %s\n", touch_cdev->fps_state ? "ON" : "OFF"); + if (is_touch_active) { + kfifo_put(&touch_cdev->cmd_pipe, TS_MMI_DO_FPS); + schedule_delayed_work(&touch_cdev->work, 0); + } + } + + return 0; +} + +static int ts_mmi_fps_notifier_register(struct ts_mmi_dev *touch_cdev, bool enable) { + int ret; + + if (enable) { + touch_cdev->fps_notif.notifier_call = ts_mmi_fps_cb; + /*register a blocking notification to receive FPS events*/ + ret = relay_register_action(BLOCKING, FPS, &touch_cdev->fps_notif); + if (ret < 0) { + dev_err(DEV_TS, + "Failed to register fps_notifier: %d\n", ret); + return ret; + } + touch_cdev->is_fps_registered = true; + dev_info(DEV_TS, "Register fps_notifier OK\n"); + } else if (touch_cdev->is_fps_registered){ + ret = relay_unregister_action(BLOCKING, FPS, &touch_cdev->fps_notif); + if (ret < 0) { + dev_err(DEV_TS, + "Failed to unregister fps_notifier: %d\n", ret); + } + touch_cdev->is_fps_registered = false; + dev_info(DEV_TS, "Unregister fps_notifier OK\n"); + } + return 0; +} + +static int ts_mmi_lpd_cb(struct notifier_block *self, + unsigned long event, void *p) +{ + int lpd_state = *(int *)p; + struct ts_mmi_dev *touch_cdev = container_of( + self, struct ts_mmi_dev, lpd_notif); + + if (touch_cdev && event == NOTIFY_EVENT_LPD_STATUS && + lpd_state != touch_cdev->lpd_state) { + touch_cdev->lpd_state = lpd_state; + kfifo_put(&touch_cdev->cmd_pipe, TS_MMI_DO_LIQUID_DETECTION); + schedule_delayed_work(&touch_cdev->work, 0); + dev_info(DEV_MMI, "LPD state is %d\n", touch_cdev->lpd_state); + } + + return 0; +} + +static int ts_mmi_lpd_notifier_register(struct ts_mmi_dev *touch_cdev, bool enable) { + int ret; + + if (enable) { + touch_cdev->lpd_notif.notifier_call = ts_mmi_lpd_cb; + /*register a blocking notification to receive LPD events*/ + ret = relay_register_action(BLOCKING, LPD, &touch_cdev->lpd_notif); + if (ret < 0) { + dev_err(DEV_TS, + "Failed to register lpd_notifier: %d\n", ret); + return ret; + } + touch_cdev->is_lpd_registered = true; + dev_info(DEV_TS, "Register lpd_notifier OK\n"); + } else if (touch_cdev->is_lpd_registered){ + ret = relay_unregister_action(BLOCKING, LPD, &touch_cdev->lpd_notif); + if (ret < 0) { + dev_err(DEV_TS, + "Failed to unregister lpd_notifier: %d\n", ret); + } + touch_cdev->is_lpd_registered = false; + dev_info(DEV_TS, "Unregister lpd_notifier OK\n"); + } + return 0; +} + +int ts_mmi_notifiers_register(struct ts_mmi_dev *touch_cdev) +{ + int ret = 0; + + if (!touch_cdev->class_dev) { + return -ENODEV; + } + + dev_info(DEV_TS, "%s: Start notifiers init.\n", __func__); + + INIT_DELAYED_WORK(&touch_cdev->work, ts_mmi_worker_func); + ret = kfifo_alloc(&touch_cdev->cmd_pipe, + sizeof(unsigned int)* 10, GFP_KERNEL); + if (ret) + goto FIFO_ALLOC_FAILED; + + if (touch_cdev->pdata.usb_detection) { + struct power_supply *psy = NULL; + bool present; + touch_cdev->ps_notif.notifier_call = ts_mmi_charger_cb; + ret = power_supply_reg_notifier(&touch_cdev->ps_notif); + if (ret) + goto PS_NOTIF_REGISTER_FAILED; + + psy = power_supply_get_by_name("usb"); + if (psy) { + ret = ts_mmi_ps_get_state(psy, &present); + if (!ret) { + touch_cdev->ps_is_present = present; + kfifo_put(&touch_cdev->cmd_pipe, TS_MMI_DO_PS); + schedule_delayed_work(&touch_cdev->work, 0); + } + power_supply_put(psy); + dev_info(DEV_MMI, "%s: USB initial status=%d\n", + __func__, touch_cdev->ps_is_present); + } + } + + /* + Because the touch pm_mode default is TS_MMI_PM_DEEPSLEEP, when the first suspend occurs, + the interrupt will not be turned off, which results in I2C error. + Need to set the initial pm_mode of the touch to PM_ACTIVE. + */ + touch_cdev->pm_mode = TS_MMI_PM_ACTIVE; + + kfifo_put(&touch_cdev->cmd_pipe, TS_MMI_TASK_INIT); + schedule_delayed_work(&touch_cdev->work, 0); + + if (touch_cdev->pdata.update_refresh_rate) { + touch_cdev->freq_nb.notifier_call = ts_mmi_refresh_rate_cb; + ret = register_dynamic_refresh_rate_notifier(&touch_cdev->freq_nb); + if (ret) + goto FREQ_NOTIF_REGISTER_FAILED; + } + + if (touch_cdev->pdata.fps_detection || touch_cdev->pdata.fod_detection) { + ret = ts_mmi_fps_notifier_register(touch_cdev, true); + if (ret < 0) + dev_err(DEV_TS, + "Failed to register fps_notifier: %d\n", ret); + } + + if (touch_cdev->pdata.support_liquid_detection) { + ret = ts_mmi_lpd_notifier_register(touch_cdev, true); + if (ret < 0) + dev_err(DEV_TS, + "Failed to register lpd_notifier: %d\n", ret); + } + + dev_info(DEV_TS, "%s: Notifiers init OK.\n", __func__); + return 0; + +FREQ_NOTIF_REGISTER_FAILED: + cancel_delayed_work(&touch_cdev->work); +PS_NOTIF_REGISTER_FAILED: + kfifo_free(&touch_cdev->cmd_pipe); +FIFO_ALLOC_FAILED: + return ret; +} + +void ts_mmi_notifiers_unregister(struct ts_mmi_dev *touch_cdev) +{ + if (touch_cdev->class_dev == NULL) { + dev_err(DEV_MMI, "%s:touch_cdev->class_dev == NULL", __func__); + return; + } + + if (touch_cdev->pdata.fps_detection || touch_cdev->pdata.fod_detection) + ts_mmi_fps_notifier_register(touch_cdev, false); + + if (touch_cdev->pdata.update_refresh_rate) + unregister_dynamic_refresh_rate_notifier(&touch_cdev->freq_nb); + + if (touch_cdev->pdata.usb_detection) + power_supply_unreg_notifier(&touch_cdev->ps_notif); + + if (!touch_cdev->panel_status) + UNREGISTER_PANEL_NOTIFIER; + + if (touch_cdev->pdata.support_liquid_detection) + ts_mmi_lpd_notifier_register(touch_cdev, false); + + cancel_delayed_work(&touch_cdev->work); + kfifo_free(&touch_cdev->cmd_pipe); + dev_info(DEV_MMI, "%s:notifiers_unregister finish", __func__); +} diff --git a/drivers/input/touchscreen/touchscreen_mmi/touchscreen_mmi_panel.c b/drivers/input/touchscreen/touchscreen_mmi/touchscreen_mmi_panel.c new file mode 100644 index 000000000000..1b037dd32a1a --- /dev/null +++ b/drivers/input/touchscreen/touchscreen_mmi/touchscreen_mmi_panel.c @@ -0,0 +1,400 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +static ssize_t ts_mmi_panel_supplier_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ts_mmi_dev *touch_cdev = dev_get_drvdata(dev); + return scnprintf(buf, PAGE_SIZE, "%s\n", touch_cdev->panel_supplier); +} +static DEVICE_ATTR(panel_supplier, S_IRUGO, ts_mmi_panel_supplier_show, NULL); + +static struct attribute *sysfs_panel_attrs[] = { + &dev_attr_panel_supplier.attr, + NULL, +}; + +static const struct attribute_group sysfs_panel_group = { + .attrs = sysfs_panel_attrs, +}; + +#define PRIM_PANEL_NAME "mmi,panel_name" +#define PRIM_PANEL_VER "mmi,panel_ver" +#define PRIM_PANEL_ID "mmi,panel_id" +#define SEC_PANEL_NAME PRIM_PANEL_NAME"_s" +#define SEC_PANEL_VER PRIM_PANEL_VER"_s" +#define SEC_PANEL_ID PRIM_PANEL_ID"_s" + +int ts_mmi_parse_dt(struct ts_mmi_dev *touch_cdev, + struct device_node *of_node) +{ + struct device_node *chosen; + struct ts_mmi_dev_pdata *ppdata = &touch_cdev->pdata; + u32 coords[4]; + + dev_info(DEV_TS, "%s: Start parse touchscreen mmi device tree.\n", __func__); + if (!of_property_read_string(of_node, "mmi,class-entry-name", &ppdata->class_entry_name)) + dev_info(DEV_TS, "%s: class-entry-name property %s\n", + __func__, ppdata->class_entry_name); + + if (!of_property_read_string(of_node, "mmi,bound-display", &ppdata->bound_display)) + dev_info(DEV_TS, "%s: bound-display property %s\n", + __func__, ppdata->bound_display); + + if (!of_property_read_u32(of_node, "mmi,control-dsi", &ppdata->ctrl_dsi)) + dev_info(DEV_TS, "%s: ctrl-dsi property %d\n", + __func__, ppdata->ctrl_dsi); + + if (of_property_read_bool(of_node, "mmi,usb-charger-detection")) { + dev_info(DEV_TS, "%s: using usb detection\n", __func__); + ppdata->usb_detection = true; + } + + if (!of_property_read_u32(of_node, "mmi,reset-on-resume", &ppdata->reset)) + dev_info(DEV_TS, "%s: using %u reset on resume\n", + __func__, ppdata->reset); + + if (of_property_read_bool(of_node, "mmi,fw-load-on-resume")) { + dev_info(DEV_TS, "%s: load firmware on resume\n", __func__); + ppdata->fw_load_resume = true; + } + + if (of_property_read_bool(of_node, "mmi,power-off-suspend")) { + dev_info(DEV_TS, "%s: using power off in suspend\n", __func__); + ppdata->power_off_suspend = true; + if (ppdata->reset) { + ppdata->reset = 0; + dev_info(DEV_TS, "%s: unset reset on resume!!!\n", __func__); + } + } + + if (of_property_read_bool(of_node, "mmi,enable-gestures")) { + dev_info(DEV_TS, "%s: using enable gestures\n", __func__); + ppdata->gestures_enabled = true; + } + + if (of_property_read_bool(of_node, "mmi,cli-enable-gestures")) { + dev_info(DEV_TS, "%s: using enable cli gestures\n", __func__); + ppdata->cli_gestures_enabled = true; + } + + if (of_property_read_bool(of_node, "mmi,enable-palm")) { + dev_info(DEV_TS, "%s: using enable palm\n", __func__); + ppdata->palm_enabled = true; + } + + if (of_property_read_bool(of_node, "mmi,refresh-rate-update")) { + dev_info(DEV_TS, "%s: using refresh rate update\n", __func__); + ppdata->update_refresh_rate = true; + } + + if (of_property_read_bool(of_node, "mmi,suppression-control")) { + dev_info(DEV_TS, "%s: using suppression\n", __func__); + ppdata->suppression_ctrl = true; + } + + if (of_property_read_bool(of_node, "mmi,pill-region-control")) { + dev_info(DEV_TS, "%s: using pill region\n", __func__); + ppdata->pill_region_ctrl = true; + } + + if (of_property_read_bool(of_node, "mmi,active-region-control")) { + dev_info(DEV_TS, "%s: using active region\n", __func__); + ppdata->active_region_ctrl = true; + } + + if (of_property_read_bool(of_node, "mmi,hold-distance-control")) { + dev_info(DEV_TS, "%s: using hold distance\n", __func__); + ppdata->hold_distance_ctrl = true; + } + + if (of_property_read_bool(of_node, "mmi,gs-distance-control")) { + dev_info(DEV_TS, "%s: using gs distance\n", __func__); + ppdata->gs_distance_ctrl = true; + } + + if (of_property_read_bool(of_node, "mmi,hold-grip-control")) { + dev_info(DEV_TS, "%s: using hold grip\n", __func__); + ppdata->hold_grip_ctrl = true; + } + + if (of_property_read_bool(of_node, "mmi,poison-slot-control")) { + dev_info(DEV_TS, "%s: using poison slot\n", __func__); + ppdata->poison_slot_ctrl = true; + } + + if (of_property_read_bool(of_node, "mmi,fps_detection")) { + dev_info(DEV_TS, "%s: using fps detection\n", __func__); + ppdata->fps_detection = true; + } + + if (!of_property_read_u32_array(of_node, "mmi,max_coords", coords, 2)) { + dev_info(DEV_TS, "%s: get max_coords property\n", __func__); + ppdata->max_x = coords[0] - 1; + ppdata->max_y = coords[1] - 1; + } + if (of_property_read_bool(of_node, "mmi,fod_detection")) { + dev_info(DEV_TS, "%s: using fod detection\n", __func__); + ppdata->fod_detection = true; + } + + if (!of_property_read_u32_array(of_node, "mmi,fod_coords", coords, 2)) { + ppdata->fod_x = coords[0]; + ppdata->fod_y = coords[1]; + dev_info(DEV_TS, "%s: get fod_coords property x:%d y:%d\n", __func__,ppdata->fod_x,ppdata->fod_y); + } + +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + if (!of_property_read_u32(of_node, "mmi,supported_gesture_type", &ppdata->supported_gesture_type)) + dev_info(DEV_TS, "%s: supported_gesture_type property %02x\n", + __func__, ppdata->supported_gesture_type); +#endif + + if (of_property_read_bool(of_node, "mmi,support-liquid-detection")) { + dev_info(DEV_TS, "%s: support liquid detection\n", __func__); + ppdata->support_liquid_detection = true; + } + + if (!of_property_read_u32_array(of_node, "mmi,clip-area", coords, 4)) { + ppdata->clip_area_ctrl = true; + touch_cdev->clip.xUL = coords[0]; + touch_cdev->clip.yUL = coords[1]; + touch_cdev->clip.xBR = coords[2]; + touch_cdev->clip.yBR = coords[3]; + if (of_property_read_bool(of_node, "mmi,clip-inversion")) + touch_cdev->clip.inversion = true; + dev_info(DEV_TS, "%s: clip %sarea: (%u,%u) (%u,%u)\n", __func__, + touch_cdev->clip.inversion ? "IN " : "OUT ", + touch_cdev->clip.xUL, touch_cdev->clip.yUL, + touch_cdev->clip.xBR, touch_cdev->clip.yBR); + } + + chosen = of_find_node_by_name(NULL, "chosen"); + if (chosen) { + struct device_node *child; + struct device_node *np; + const char *panel_name_prop; + const char *panel_ver_prop; + const char *panel_id_prop; + const char *supplier; + char *s, *d; + int rc; + u64 panel_ver, panel_id; + + if (!ppdata->ctrl_dsi) { + panel_name_prop = PRIM_PANEL_NAME; + panel_ver_prop = PRIM_PANEL_VER; + panel_id_prop = PRIM_PANEL_ID; + } else { + panel_name_prop = SEC_PANEL_NAME; + panel_ver_prop = SEC_PANEL_VER; + panel_id_prop = SEC_PANEL_ID; + } + + rc = of_property_read_string(chosen, panel_name_prop, + (const char **)&supplier); + if (rc) { + dev_err(DEV_TS, "%s: cannot read %s %d\n", + __func__, panel_name_prop, rc); + goto done; + } + dev_info(DEV_TS, "%s: %s %s\n", + __func__, panel_name_prop, supplier); + s = (char *)supplier; + /* skip dsi_ part */ + if (!strncmp(supplier, "dsi_", 4)) + s += 4; + d = touch_cdev->panel_supplier; + while (*s != '_') *d++ = *s++; + + rc = of_property_read_u64(chosen, panel_id_prop, &panel_id); + if (rc) { + dev_err(DEV_TS, "%s: cannot read %s %d\n", + __func__, panel_id_prop, rc); + goto done; + } + rc = of_property_read_u64(chosen, panel_ver_prop, &panel_ver); + if (rc) { + dev_err(DEV_TS, "%s: cannot read %s %d\n", + __func__, panel_ver_prop, rc); + goto done; + } + of_node_put(chosen); + dev_dbg(DEV_TS, "%s: [%s] id=%llx ver=%llx\n", + __func__, chosen->name, panel_id, panel_ver); + + np = of_find_node_by_name(of_node, "mmi,panel-mappings"); + if (!np) { + dev_info(DEV_TS, "%s: no panel mappings\n", __func__); + goto done; + } + + for_each_child_of_node(np, child) { + u64 id, ver; + + rc = of_property_read_u64(child, "panel-id", &id); + if (rc) { + dev_err(DEV_TS, "%s: cannot read panel-id %d\n", __func__, rc); + goto done; + } + rc = of_property_read_u64(child, "panel-ver", &ver); + if (rc) { + dev_err(DEV_TS, "%s: cannot read panel-ver %d\n", __func__, rc); + goto done; + } + + dev_dbg(DEV_TS, "%s: [%s] id=%llx ver=%llx\n", + __func__, child->name, id, ver); + of_node_put(child); + + if (id == panel_id && ver == panel_ver) { + strlcpy(touch_cdev->panel_supplier, child->name, + sizeof(touch_cdev->panel_supplier)); + break; + } + } + + of_node_put(np); + dev_info(DEV_TS, "%s: panel-supplier %s\n", + __func__, touch_cdev->panel_supplier); + } + +done: + return 0; +} + +#if defined(CONFIG_DRM_PANEL_NOTIFICATIONS) || defined (CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS) + +static int ts_mmi_check_dt(struct ts_mmi_dev *touch_cdev, struct device_node *np) +{ + + int i = 0; + int count = 0; + struct device_node *node = NULL; + struct drm_panel *panel = NULL; + + count = of_count_phandle_with_args(np, "panel", NULL); + if (count <= 0) { + dev_dbg(DEV_TS, "%s: find drm_panel count(%d) fail", __func__, count); + return -ENODEV; + } + + 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)) { + dev_info(DEV_TS, "%s: find drm_panel successfully", __func__); + touch_cdev->active_panel = panel; + return 0; + } + } + dev_dbg(DEV_TS, "%s: No find drm_panel", __func__); + return -ENODEV; +} + +static int ts_mmi_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) + return -ENODEV; + + active_tp = kcalloc(count, sizeof(char *), GFP_KERNEL); + if (!active_tp) { + pr_err("%s: alloc active_tp failed", __func__); + return -ENOMEM; + } + + ret = of_property_read_string_array(dt->parent, prop, + active_tp, count); + if (ret < 0) { + pr_err("%s: fail to read %s (%d)\n", __func__, 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) { + pr_err("%s: Not panel match this driver\n", __func__); + ret = -ENODEV; + goto out; + } + ret = 0; +out: + kfree(active_tp); + return ret; +} + +int ts_mmi_check_drm_panel(struct ts_mmi_dev* touch_cdev, struct device_node *of_node) +{ + int ret; + + ret = ts_mmi_check_dt(touch_cdev, of_node); + if (ret) { + dev_dbg(DEV_TS, "%s: parse drm-panel fail\n", __func__); + if (!ts_mmi_check_default_tp(of_node, "qcom,mmi-touch-active")) + ret = -EPROBE_DEFER; + else + ret = -ENODEV; + } + return ret; +} +#endif + +int ts_mmi_panel_register(struct ts_mmi_dev *touch_cdev) { + int ret = 0; + + if (!touch_cdev->class_dev) { + return -ENODEV; + } + + dev_info(DEV_TS, "%s: Start panel init.\n", __func__); + ret = sysfs_create_group(&touch_cdev->class_dev->kobj, &sysfs_panel_group); + if (ret) + goto PANEL_DEVICE_ATTR_CREATE_FAILED; + + dev_info(DEV_TS, "%s: Panel init OK.\n", __func__); + return 0; + +PANEL_DEVICE_ATTR_CREATE_FAILED: + return ret; +} + +void ts_mmi_panel_unregister(struct ts_mmi_dev *touch_cdev) { + if(touch_cdev->class_dev == NULL) { + dev_err(DEV_MMI, "%s:touch_cdev->class_dev == NULL", __func__); + } else { + sysfs_remove_group(&touch_cdev->class_dev->kobj, &sysfs_panel_group); + dev_info(DEV_MMI, "%s:panel_unregister finish", __func__); + } +} diff --git a/drivers/input/touchscreen/zinitix_ztw62x_mmi/Android.mk b/drivers/input/touchscreen/zinitix_ztw62x_mmi/Android.mk new file mode 100644 index 000000000000..2efecff07c08 --- /dev/null +++ b/drivers/input/touchscreen/zinitix_ztw62x_mmi/Android.mk @@ -0,0 +1,29 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := zinitix_ztw62x_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/mmi_info.ko + +ifeq ($(DRM_PANEL_EVENT_NOTIFICATIONS),true) + KBUILD_OPTIONS += CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS=y +endif + +ifeq ($(DRM_PANEL_NOTIFICATIONS),true) + KBUILD_OPTIONS += CONFIG_DRM_PANEL_NOTIFICATIONS=y +endif + +ifeq ($(GTP_LIMIT_USE_SUPPLIER),true) + KBUILD_OPTIONS += CONFIG_GTP_LIMIT_USE_SUPPLIER=y +endif + +ifneq ($(findstring touchscreen_mmi.ko,$(BOARD_VENDOR_KERNEL_MODULES)),) + KBUILD_OPTIONS += CONFIG_INPUT_TOUCHSCREEN_MMI=y + LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/touchscreen_mmi.ko +endif +include $(DLKM_DIR)/AndroidKernelModule.mk + + +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki diff --git a/drivers/input/touchscreen/zinitix_ztw62x_mmi/Kbuild b/drivers/input/touchscreen/zinitix_ztw62x_mmi/Kbuild new file mode 100644 index 000000000000..d1e7a7ec87ad --- /dev/null +++ b/drivers/input/touchscreen/zinitix_ztw62x_mmi/Kbuild @@ -0,0 +1,28 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m := zinitix_ztw62x_mmi.o + +zinitix_ztw62x_mmi-objs := zinitix.o + + +ifneq ($(filter m y,$(CONFIG_INPUT_TOUCHSCREEN_MMI)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_TOUCHSCREEN_MMI + zinitix_ztw62x_mmi-objs += zinitix_ts_mmi.o +endif + +ifneq ($(filter m y, $(CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_EVENT_NOTIFICATIONS +endif + +ifneq ($(filter m y,$(CONFIG_DRM_PANEL_NOTIFICATIONS)),) + EXTRA_CFLAGS += -DCONFIG_DRM_PANEL_NOTIFICATIONS +endif + +ifneq ($(filter m y,$(CONFIG_GTP_LIMIT_USE_SUPPLIER)),) + EXTRA_CFLAGS += -DCONFIG_GTP_LIMIT_USE_SUPPLIER +endif + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers \ No newline at end of file diff --git a/drivers/input/touchscreen/zinitix_ztw62x_mmi/Makefile b/drivers/input/touchscreen/zinitix_ztw62x_mmi/Makefile new file mode 100644 index 000000000000..f73c3318c1f7 --- /dev/null +++ b/drivers/input/touchscreen/zinitix_ztw62x_mmi/Makefile @@ -0,0 +1,17 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(M)/../touchscreen_mmi/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(M)/../../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers \ No newline at end of file diff --git a/drivers/input/touchscreen/zinitix_ztw62x_mmi/zinitix.c b/drivers/input/touchscreen/zinitix_ztw62x_mmi/zinitix.c new file mode 100644 index 000000000000..790b09811d91 --- /dev/null +++ b/drivers/input/touchscreen/zinitix_ztw62x_mmi/zinitix.c @@ -0,0 +1,6576 @@ +/* + * + * Zinitix bt541 touchscreen driver + * + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * + * 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. + * + */ + + + + +#include +#include +#include +#include +#if defined(CONFIG_PM_RUNTIME) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MACH_PXA_SAMSUNG +#include +#endif +#if defined(CONFIG_FB) +#include +#include +#elif defined(CONFIG_HAS_EARLYSUSPEND) +#include +#endif +#include + +#include "zinitix_touch.h" +#include "zinitix_ts_mmi.h" +#include "zinitix_touch_zxt_firmware.h" + +#if (TSP_TYPE_COUNT == 1) +u8 *m_pFirmware [TSP_TYPE_COUNT] = {(u8*)m_firmware_data,}; +#else +u8 *m_pFirmware [TSP_TYPE_COUNT] = {(u8*)m_firmware_data_01,(u8*)m_firmware_data_02,}; +#endif +u8 m_FirmwareIdx = 0; + +#define TSP_HW_ID_INDEX_0 1 +#define TSP_HW_ID_INDEX_1 2 + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +extern char *saved_command_line; +#endif + +int easy_wake = 1; +int big_object = 1; + +/* if you use isp mode, you must add i2c device : +name = "zinitix_isp" , addr 0x50*/ +/* resolution offset */ +#define ABS_PT_OFFSET (-1) + +/* PMIC Regulator based supply to TSP */ +#define TSP_REGULATOR_SUPPLY 1 +/* gpio controlled LDO based supply to TSP */ +#define TSP_LDO_SUPPLY 0 + +/* Key Enum */ +enum key_event { + ICON_BUTTON_UNCHANGE, + ICON_BUTTON_DOWN, + ICON_BUTTON_UP, +}; + +#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 PINCTRL_STATE_ACTIVE "cli_pmx_ts_active" +#define PINCTRL_STATE_SUSPEND "cli_pmx_ts_suspend" +#define PINCTRL_STATE_RELEASE "cli_pmx_ts_release" + + +/* preriod raw data interval */ +#define RAWDATA_DELAY_FOR_HOST 100 + +#define ZINITIX_MAX_PRBUF_SIZE PIPE_BUF +#define ZINITIX_THRESHOLD_FILE_NAME "zinitix_cmcp_thresholdfile.csv" +#define MAX_LINE_LEN (1024 * 3 * 7) +#define CSV_TP_RAW_DATA_MIN "raw_data_min" +#define CSV_TP_RAW_DATA_MAX "raw_data_max" +#define CSV_TP_SHORT_DATA_THRESHOLD "short_data_threshold" +#define CSV_BUFFER_LEN (1024*80*5) +#define CHECKSUM_VALUE 0xA5A5A5A5 + +struct raw_ioctl { + int sz; + u8 *buf; +}; + +struct reg_ioctl { + int addr; + int *val; +}; + +#define TOUCH_SEC_MODE 48 +#define TOUCH_REF_MODE 10 +#define TOUCH_NORMAL_MODE 5 +#define TOUCH_DELTA_MODE 3 +#define TOUCH_DND_MODE 6 +#define TOUCH_PDND_MODE 11 + +#define TOUCH_CHECK_SHORT_MODE 14 +#define CHECK_SHORT_DATA_NUM 24 + +/* Other Things */ +#define INIT_RETRY_CNT 3 +#define I2C_SUCCESS 0 +#define I2C_FAIL 1 + +/*------------Link modify IC flash size 20180523-------------------*/ + +/* chip code */ +#define ZTW523_CHIP_CODE 0xE628 +#define ZTW522_CHIP_CODE 0xE532 +#define ZT7548_CHIP_CODE 0xE548 +#define ZT7538_CHIP_CODE 0xE538 +#define ZT7554_CHIP_CODE 0xE700 +#define ZTW622_CHIP_CODE 0xE622 +#define ZTW623_CHIP_CODE 0xE623 + +/*------------Link modify IC flash size 20180523-------------------*/ + +/* Register Map*/ +#define BT541_SWRESET_CMD 0x0000 +#define BT541_WAKEUP_CMD 0x0001 + +#define BT541_IDLE_CMD 0x0004 +#define BT541_SLEEP_CMD 0x0005 + +#define BT541_CLEAR_INT_STATUS_CMD 0x0003 +#define BT541_CALIBRATE_CMD 0x0006 +#define BT541_SAVE_STATUS_CMD 0x0007 +#define BT541_SAVE_CALIBRATION_CMD 0x0008 +#define BT541_RECALL_FACTORY_CMD 0x000f + +#define BT541_THRESHOLD 0x0020 + +#define BT541_DEBUG_REG 0x0115 /* 0~7 */ + +#define BT541_TOUCH_MODE 0x0010 +#define BT541_CHIP_REVISION 0x0011 +#define BT541_FIRMWARE_VERSION 0x0034 + +#define ZINITIX_USB_DETECT 0x116 + +#define BT541_MINOR_FW_VERSION 0x0035 + +#define BT541_VENDOR_ID 0x001C +#define BT541_HW_ID 0x0014 + +#define BT541_DATA_VERSION_REG 0x0013 +#define BT541_FW_DAY_VERSION 0x0036 + +#define BT541_SUPPORTED_FINGER_NUM 0x0015 +#define BT541_EEPROM_INFO 0x0018 +#define BT541_INITIAL_TOUCH_MODE 0x0019 + +#define BT541_TOTAL_NUMBER_OF_X 0x0060 +#define BT541_TOTAL_NUMBER_OF_Y 0x0061 + +#define BT541_DELAY_RAW_FOR_HOST 0x007f + +#define BT541_BUTTON_SUPPORTED_NUM 0x00B0 +#define BT541_BUTTON_SENSITIVITY 0x00B2 +#define BT541_DUMMY_BUTTON_SENSITIVITY 0X00C8 + +#define BT541_X_RESOLUTION 0x00C0 +#define BT541_Y_RESOLUTION 0x00C1 + +#define BT541_POINT_STATUS_REG 0x0080 +#define BT541_ICON_STATUS_REG 0x00AA + +#define BT541_AFE_FREQUENCY 0x0100 +#define BT541_DND_N_COUNT 0x0122 +#define BT541_DND_U_COUNT 0x0135 + +#define BT541_RAWDATA_REG 0x0200 + +#define BT541_EEPROM_INFO_REG 0x0018 + +#define BT541_INT_ENABLE_FLAG 0x00f0 +#define BT541_PERIODICAL_INTERRUPT_INTERVAL 0x00f1 + +#define BT541_BTN_WIDTH 0x016d + +#define BT541_CHECKSUM_RESULT 0x012c + +#define BT541_INIT_FLASH 0x01d0 +#define BT541_WRITE_FLASH 0x01d1 +#define BT541_READ_FLASH 0x01d2 + +#define ZINITIX_INTERNAL_FLAG_02 0x011e +#define ZINITIX_INTERNAL_FLAG_03 0x011f + +#define ZINITIX_I2C_CHECKSUM_WCNT 0x016a +#define ZINITIX_I2C_CHECKSUM_RESULT 0x016c + +#define BT541_GESTURE_CONTROL_REG 0x011D +#define BT541_CLEAR_GESTURE_CMD 0x0088 + +/* Interrupt & status register flag bit +------------------------------------------------- +*/ +#define BIT_PT_CNT_CHANGE 0 +#define BIT_DOWN 1 +#define BIT_MOVE 2 +#define BIT_UP 3 +#define BIT_PALM 4 +#define BIT_PALM_REJECT 5 +#define RESERVED_0 6 +#define RESERVED_1 7 +#define BIT_WIDTH_CHANGE 8 +#define BIT_PT_NO_CHANGE 9 +#define BIT_REJECT 10 +#define BIT_PT_EXIST 11 +#define RESERVED_2 12 +#define BIT_MUST_ZERO 13 +#define BIT_DEBUG 14 +#define BIT_ICON_EVENT 15 + +/* button */ +#define BIT_O_ICON0_DOWN 0 +#define BIT_O_ICON1_DOWN 1 +#define BIT_O_ICON2_DOWN 2 +#define BIT_O_ICON3_DOWN 3 +#define BIT_O_ICON4_DOWN 4 +#define BIT_O_ICON5_DOWN 5 +#define BIT_O_ICON6_DOWN 6 +#define BIT_O_ICON7_DOWN 7 + +#define BIT_O_ICON0_UP 8 +#define BIT_O_ICON1_UP 9 +#define BIT_O_ICON2_UP 10 +#define BIT_O_ICON3_UP 11 +#define BIT_O_ICON4_UP 12 +#define BIT_O_ICON5_UP 13 +#define BIT_O_ICON6_UP 14 +#define BIT_O_ICON7_UP 15 + + +#define SUB_BIT_EXIST 0 +#define SUB_BIT_DOWN 1 +#define SUB_BIT_MOVE 2 +#define SUB_BIT_UP 3 +#define SUB_BIT_UPDATE 4 +#define SUB_BIT_WAIT 5 + +/* Mode status */ +#define TS_USB_DETECT_BIT 0 +#define TS_SVIEW_DETECT_BIT 1 +#define TS_SENSIVE_MODE_BIT 2 + +#define zinitix_bit_set(val, n) ((val) &= ~(1<<(n)), (val) |= (1<<(n))) +#define zinitix_bit_clr(val, n) ((val) &= ~(1<<(n))) +#define zinitix_bit_test(val, n) ((val) & (1<<(n))) +#define zinitix_swap_v(a, b, t) ((t) = (a), (a) = (b), (b) = (t)) +#define zinitix_swap_16(s) (((((s) & 0xff) << 8) | (((s) >> 8) & 0xff))) + + +extern int get_lcd_attached(void); +extern unsigned int system_rev; + +struct class *sec_class; +/* end header file */ + +#ifdef SEC_FACTORY_TEST +/* Touch Screen */ +#define TSP_CMD_STR_LEN 32 +#define TSP_CMD_RESULT_STR_LEN 512 +#define TSP_CMD_PARAM_NUM 8 +#define TSP_CMD_Y_NUM 18 +#define TSP_CMD_X_NUM 30 +#define TSP_CMD_NODE_NUM (TSP_CMD_Y_NUM * TSP_CMD_X_NUM) +#define tostring(x) #x + +struct tsp_factory_info { + struct list_head cmd_list_head; + char cmd[TSP_CMD_STR_LEN]; + char cmd_param[TSP_CMD_PARAM_NUM]; + char cmd_result[TSP_CMD_RESULT_STR_LEN]; + char cmd_buff[TSP_CMD_RESULT_STR_LEN]; + struct mutex cmd_lock; + bool cmd_is_running; + u8 cmd_state; +}; + +struct tsp_raw_data { + //Link modified here on 20190506 for circle design the corner minus value + s16 ref_data[TSP_CMD_NODE_NUM]; + s16 pref_data[TSP_CMD_NODE_NUM]; + //u16 ref_data[TSP_CMD_NODE_NUM]; + //u16 pref_data[TSP_CMD_NODE_NUM]; + /*s16 scantime_data[TSP_CMD_NODE_NUM]; */ + s16 delta_data[TSP_CMD_NODE_NUM]; +}; + +enum { + WAITING = 0, + RUNNING, + OK, + FAIL, + NOT_APPLICABLE, +}; + +struct tsp_cmd { + struct list_head list; + const char *cmd_name; + void (*cmd_func)(void *device_data); +}; +#ifdef CONFIG_HAS_EARLYSUSPEND +static void zinitix_early_suspend(struct early_suspend *h); +static void zinitix_late_resume(struct early_suspend *h); +#endif + +static void fw_update(void *device_data); +static void get_fw_ver_bin(void *device_data); +static void get_fw_ver_ic(void *device_data); +static void get_threshold(void *device_data); +static void module_off_master(void *device_data); +static void module_on_master(void *device_data); +static void module_off_slave(void *device_data); +static void module_on_slave(void *device_data); +static void get_chip_vendor(void *device_data); +static void get_chip_name(void *device_data); +static void get_x_num(void *device_data); +static void get_y_num(void *device_data); +static void not_support_cmd(void *device_data); + +/* Vendor dependant command */ +/* static void run_reference_read(void *device_data); */ +static void get_reference(void *device_data); +static void run_preference_read(void *device_data); +static void run_short_data_read(void *device_data); +static void get_preference(void *device_data); +static void run_delta_read(void *device_data); +static void get_delta(void *device_data); +static void get_module_vendor(void *device_data); +static void get_config_ver(void *device_data); +#ifdef GLOVE_MODE +static void glove_mode(void *device_data); +#endif +#define TSP_CMD(name, func) .cmd_name = name, .cmd_func = func + +static struct tsp_cmd tsp_cmds[] = { + {TSP_CMD("fw_update", fw_update),}, + {TSP_CMD("get_fw_ver_bin", get_fw_ver_bin),}, + {TSP_CMD("get_fw_ver_ic", get_fw_ver_ic),}, + {TSP_CMD("get_threshold", get_threshold),}, + {TSP_CMD("module_off_master", module_off_master),}, + {TSP_CMD("module_on_master", module_on_master),}, + {TSP_CMD("module_off_slave", module_off_slave),}, + {TSP_CMD("module_on_slave", module_on_slave),}, + {TSP_CMD("get_module_vendor", get_module_vendor),}, + {TSP_CMD("get_chip_vendor", get_chip_vendor),}, + {TSP_CMD("get_chip_name", get_chip_name),}, + {TSP_CMD("get_x_num", get_x_num),}, + {TSP_CMD("get_y_num", get_y_num),}, + {TSP_CMD("not_support_cmd", not_support_cmd),}, + + /* vendor dependant command */ + {TSP_CMD("run_reference_read", run_preference_read),}, + {TSP_CMD("run_short_data_read", run_short_data_read),}, + {TSP_CMD("get_reference", get_reference),}, + {TSP_CMD("run_dnd_read", run_preference_read),}, + {TSP_CMD("get_dnd", get_preference),}, + {TSP_CMD("run_delta_read", run_delta_read),}, + {TSP_CMD("get_delta", get_delta),}, + {TSP_CMD("get_config_ver", get_config_ver),}, +#ifdef GLOVE_MODE + {TSP_CMD("glove_mode", glove_mode),}, +#endif +}; + +#endif + +#define TSP_NORMAL_EVENT_MSG 1 +static int m_ts_debug_mode = ZINITIX_DEBUG; +#ifdef USE_TSP_TA_CALLBACKS +static bool ta_connected =0; +#endif +void (*tsp_charger_status_cb)(int); +static u16 m_optional_mode = 0; + + +#if ESD_TIMER_INTERVAL +static struct workqueue_struct *esd_tmr_workqueue; +#endif + +#define TOUCH_V_FLIP 0x01 +#define TOUCH_H_FLIP 0x02 +#define TOUCH_XY_SWAP 0x04 + +enum { + BUILT_IN = 0, + UMS, + REQ_FW, +}; + +struct bt541_ts_info *misc_touch_dev = NULL; + +/* Dummy touchkey code */ +#define KEY_DUMMY_HOME1 249 +#define KEY_DUMMY_HOME2 250 +#define KEY_DUMMY_MENU 251 +#define KEY_DUMMY_HOME 252 +#define KEY_DUMMY_BACK 253 + +/*<= you must set key button mapping*/ +#ifdef SUPPORTED_TOUCH_KEY +#ifdef NOT_SUPPORTED_TOUCH_DUMMY_KEY +u32 BUTTON_MAPPING_KEY[MAX_SUPPORTED_BUTTON_NUM] = { + KEY_MENU, KEY_HOME, KEY_BACK}; +#else +u32 BUTTON_MAPPING_KEY[MAX_SUPPORTED_BUTTON_NUM] = { + KEY_DUMMY_MENU, KEY_RECENT,// KEY_DUMMY_HOME1, + /*KEY_DUMMY_HOME2,*/ KEY_BACK, KEY_DUMMY_BACK}; +#endif +#endif + +#ifdef USE_TSP_TA_CALLBACKS +static void bt541_set_ta_status(struct bt541_ts_info *info, bool force); +#endif + +/* define i2c sub functions*/ +static inline s32 read_data(struct i2c_client *client, + u16 reg, u8 *values, u16 length) +{ + s32 ret; + int count = 0; +retry: + /* select register*/ + ret = i2c_master_send(client , (u8 *)® , 2); + if (ret < 0) { + mdelay(1); + + if (++count < 8) + goto retry; + + return ret; + } + /* for setup tx transaction. */ + udelay(DELAY_FOR_TRANSCATION); + ret = i2c_master_recv(client , values , length); + if (ret < 0) + return ret; + + udelay(DELAY_FOR_POST_TRANSCATION); + return length; +} + +static inline s32 write_data(struct i2c_client *client, + u16 reg, u8 *values, u16 length) +{ + s32 ret; + //int count = 0; + u8 pkt[66]; // addr: 2bytes, data: 64bytes /* max packet */ + pkt[0] = (reg) & 0xff; /* reg addr */ + pkt[1] = (reg >> 8)&0xff; + memcpy((u8 *)&pkt[2], values, length); + +//retry: + ret = i2c_master_send(client , pkt , length + 2); + if (ret < 0) { + // mdelay(1); + + // if (++count < 8) + // goto retry; + + return ret; + } + + udelay(DELAY_FOR_POST_TRANSCATION); + return length; +} + +static inline s32 write_reg(struct i2c_client *client, u16 reg, u16 value) +{ + if (write_data(client, reg, (u8 *)&value, 2) < 0) + return I2C_FAIL; + + return I2C_SUCCESS; +} + +static inline s32 write_cmd(struct i2c_client *client, u16 reg) +{ + s32 ret; + //int count = 0; + +//retry: + ret = i2c_master_send(client , (u8 *)® , 2); + if (ret < 0) { + // mdelay(1); + + // if (++count < 8) + // goto retry; + + return ret; + } + + udelay(DELAY_FOR_POST_TRANSCATION); + return I2C_SUCCESS; +} + +static inline s32 read_raw_data(struct i2c_client *client, + u16 reg, u8 *values, u16 length) +{ + s32 ret; + int count = 0; + +retry: + /* select register */ + ret = i2c_master_send(client , (u8 *)® , 2); + if (ret < 0) { + mdelay(1); + + if (++count < 8) + goto retry; + + return ret; + } + + /* for setup tx transaction. */ + udelay(200); + + ret = i2c_master_recv(client , values , length); + if (ret < 0) + return ret; + + udelay(DELAY_FOR_POST_TRANSCATION); + return length; +} + +static inline s32 read_firmware_data(struct i2c_client *client, + u16 addr, u8 *values, u16 length) +{ + s32 ret; + /* select register*/ + + ret = i2c_master_send(client , (u8 *)&addr , 2); + if (ret < 0) + return ret; + + /* for setup tx transaction. */ + mdelay(1); + + ret = i2c_master_recv(client , values , length); + if (ret < 0) + return ret; + + udelay(DELAY_FOR_POST_TRANSCATION); + return length; +} + +static bool bt541_power_sequence(struct bt541_ts_info *info); +//static bool zinitix_resume_sequence(struct bt541_ts_info *info); +static int zinitix_power_control(struct bt541_ts_info *data, int on); + + +static bool init_touch(struct bt541_ts_info *info); +#if ESD_TIMER_INTERVAL +static void esd_timer_start(u16 sec, struct bt541_ts_info *info); +static void esd_timer_stop(struct bt541_ts_info *info); +static void esd_timer_init(struct bt541_ts_info *info); +static void esd_timeout_handler(unsigned long data); +#endif + +static long ts_misc_fops_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg); +static int ts_misc_fops_open(struct inode *inode, struct file *filp); +static int ts_misc_fops_close(struct inode *inode, struct file *filp); + +static const struct file_operations ts_misc_fops = { + .owner = THIS_MODULE, + .open = ts_misc_fops_open, + .release = ts_misc_fops_close, + .unlocked_ioctl = ts_misc_fops_ioctl, +}; + +static struct miscdevice touch_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "zinitix_touch_misc", + .fops = &ts_misc_fops, +}; + +#define TOUCH_IOCTL_BASE 0xbc +#define TOUCH_IOCTL_GET_DEBUGMSG_STATE _IOW(TOUCH_IOCTL_BASE, 0, int) +#define TOUCH_IOCTL_SET_DEBUGMSG_STATE _IOW(TOUCH_IOCTL_BASE, 1, int) +#define TOUCH_IOCTL_GET_CHIP_REVISION _IOW(TOUCH_IOCTL_BASE, 2, int) +#define TOUCH_IOCTL_GET_FW_VERSION _IOW(TOUCH_IOCTL_BASE, 3, int) +#define TOUCH_IOCTL_GET_REG_DATA_VERSION _IOW(TOUCH_IOCTL_BASE, 4, int) +#define TOUCH_IOCTL_VARIFY_UPGRADE_SIZE _IOW(TOUCH_IOCTL_BASE, 5, int) +#define TOUCH_IOCTL_VARIFY_UPGRADE_DATA _IOW(TOUCH_IOCTL_BASE, 6, int) +#define TOUCH_IOCTL_START_UPGRADE _IOW(TOUCH_IOCTL_BASE, 7, int) +#define TOUCH_IOCTL_GET_X_NODE_NUM _IOW(TOUCH_IOCTL_BASE, 8, int) +#define TOUCH_IOCTL_GET_Y_NODE_NUM _IOW(TOUCH_IOCTL_BASE, 9, int) +#define TOUCH_IOCTL_GET_TOTAL_NODE_NUM _IOW(TOUCH_IOCTL_BASE, 10, int) +#define TOUCH_IOCTL_SET_RAW_DATA_MODE _IOW(TOUCH_IOCTL_BASE, 11, int) +#define TOUCH_IOCTL_GET_RAW_DATA _IOW(TOUCH_IOCTL_BASE, 12, int) +#define TOUCH_IOCTL_GET_X_RESOLUTION _IOW(TOUCH_IOCTL_BASE, 13, int) +#define TOUCH_IOCTL_GET_Y_RESOLUTION _IOW(TOUCH_IOCTL_BASE, 14, int) +#define TOUCH_IOCTL_HW_CALIBRAION _IOW(TOUCH_IOCTL_BASE, 15, int) +#define TOUCH_IOCTL_GET_REG _IOW(TOUCH_IOCTL_BASE, 16, int) +#define TOUCH_IOCTL_SET_REG _IOW(TOUCH_IOCTL_BASE, 17, int) +#define TOUCH_IOCTL_SEND_SAVE_STATUS _IOW(TOUCH_IOCTL_BASE, 18, int) +#define TOUCH_IOCTL_DONOT_TOUCH_EVENT _IOW(TOUCH_IOCTL_BASE, 19, int) + +struct bt541_ts_info *misc_info; +#ifdef CONFIG_OF +static int zinitix_init_gpio(struct bt541_ts_info* data,bool on ); +//static int resume_hw_reset( struct bt541_ts_info* data,bool on ); + +#endif + + +#define I2C_BUFFER_SIZE 64 + +#ifdef SEC_FACTORY_TEST +static bool get_raw_data(struct bt541_ts_info *info, u8 *buff, int skip_cnt) +{ + struct i2c_client *client = info->client; + struct bt541_ts_platform_data *pdata = info->pdata; + u32 total_node = info->cap_info.total_node_num; + int sz; + int i; + u32 temp_sz; + + if (info->irq_enabled == true) { + disable_irq(info->irq); + info->irq_enabled = false; + zinitix_printk("Disable irq.\n"); + } + + down(&info->work_lock); + if (info->work_state != NOTHING) { + dev_err(&client->dev, "%s: Other process occupied (%d)\n", + __func__, info->work_state); + enable_irq(info->irq); + info->irq_enabled = true; + up(&info->work_lock); + + return false; + } + + info->work_state = RAW_DATA; + + for(i = 0; i < skip_cnt; i++) { + while (gpio_get_value(pdata->gpio_int)) + mdelay(1); + + write_cmd(client, BT541_CLEAR_INT_STATUS_CMD); + mdelay(1); + } + + sz = total_node * 2; + + while (gpio_get_value(pdata->gpio_int)) + mdelay(1); + + zinitix_printk("sz = %d\n", sz); + for(i = 0; sz > 0; i++){ + temp_sz = I2C_BUFFER_SIZE; + + if(sz < I2C_BUFFER_SIZE) + temp_sz = sz; + + if (read_raw_data(client, BT541_RAWDATA_REG + i, (char *)(buff + (i*I2C_BUFFER_SIZE)), temp_sz) < 0) { + dev_err(&client->dev, "%s: Failed to read raw data\n", __func__); + info->work_state = NOTHING; + enable_irq(info->irq); + info->irq_enabled = true; + up(&info->work_lock); + + return false; + } + sz -= I2C_BUFFER_SIZE; + } + + write_cmd(client, BT541_CLEAR_INT_STATUS_CMD); + info->work_state = NOTHING; + up(&info->work_lock); + + return true; +} +#endif + + +static bool get_short_data(struct bt541_ts_info *info, u8 *buff, int skip_cnt) +{ + struct i2c_client *client = info->client; + struct bt541_ts_platform_data *pdata = info->pdata; + int i; + int sz; + + if (info->irq_enabled == true) { + disable_irq(info->irq); + info->irq_enabled = false; + zinitix_printk("Disable irq.\n"); + } + + down(&info->work_lock); + if (info->work_state != NOTHING) { + dev_err(&client->dev, "%s: Other process occupied (%d)\n", + __func__, info->work_state); + enable_irq(info->irq); + info->irq_enabled = true; + up(&info->work_lock); + + return false; + } + + info->work_state = RAW_DATA; + + for(i = 0; i < skip_cnt; i++) { + while (gpio_get_value(pdata->gpio_int)) + msleep(1); + + write_cmd(client, BT541_CLEAR_INT_STATUS_CMD); + msleep(1); + } + + while (gpio_get_value(pdata->gpio_int)) + msleep(1); + + sz = (info->cap_info.x_node_num + info->cap_info.y_node_num) * 2; + zinitix_printk("sz = %d, x_node_num = %d, y_node_num = %d\n", sz, + info->cap_info.x_node_num, info->cap_info.y_node_num); + + if (read_raw_data(client, BT541_RAWDATA_REG, (char *)(buff), sz) < 0) { + dev_err(&client->dev, "%s: Failed to read short data\n", __func__); + info->work_state = NOTHING; + enable_irq(info->irq); + info->irq_enabled = true; + up(&info->work_lock); + + return false; + } + + write_cmd(client, BT541_CLEAR_INT_STATUS_CMD); + info->work_state = NOTHING; + up(&info->work_lock); + + return true; +} + +static bool ts_get_raw_data(struct bt541_ts_info *info) +{ + struct i2c_client *client = info->client; + u32 total_node = info->cap_info.total_node_num; + int sz; + u16 temp_sz; + int i; + + if (down_trylock(&info->raw_data_lock)) { + dev_err(&client->dev, "%s: Failed to occupy work lock\n", __func__); + info->touch_info.status = 0; + + return true; + } + + sz = total_node * 2 + sizeof(struct point_info); + + for(i = 0; sz > 0; i++){ + temp_sz = I2C_BUFFER_SIZE; + + if(sz < I2C_BUFFER_SIZE) + temp_sz = sz; + + dev_info(&client->dev, "%s: sz = %d\n", __func__, sz); + if (read_raw_data(client, BT541_RAWDATA_REG + i, (char *)((u8*)(info->cur_data)+ (i*I2C_BUFFER_SIZE)), temp_sz) < 0) { + dev_err(&client->dev, "%s: Failed to read raw data\n", __func__); + up(&info->raw_data_lock); + + return false; + } + sz -= I2C_BUFFER_SIZE; + } + info->update = 1; + memcpy((u8 *)(&info->touch_info), + (u8 *)&info->cur_data[total_node], + sizeof(struct point_info)); + up(&info->raw_data_lock); + + return true; +} + +static bool ts_read_coord(struct bt541_ts_info *info) +{ + struct i2c_client *client = info->client; +#if (TOUCH_POINT_MODE == 1) + int i; +#endif + + zinitix_debug_msg("ts_read_coord+\r\n"); + + if (info->touch_mode != TOUCH_POINT_MODE) { + if (ts_get_raw_data(info) == false) + return false; + + dev_err(&client->dev, "status = 0x%04X\n", info->touch_info.status); + + goto out; + } + +#if (TOUCH_POINT_MODE == 1) + memset(&info->touch_info, + 0x0, sizeof(struct point_info)); + + if (read_data(info->client, BT541_POINT_STATUS_REG, + (u8 *)(&info->touch_info), 4) < 0) { + dev_err(&client->dev, "%s: Failed to read point info\n", __func__); + + return false; + } + + dev_dbg(&client->dev, "status reg = 0x%x , event_flag = 0x%04x\n", + info->touch_info.status, info->touch_info.event_flag); + + if (info->touch_info.event_flag == 0) + goto out; + + for (i = 0; i < info->cap_info.multi_fingers; i++) { + if (zinitix_bit_test(info->touch_info.event_flag, i)) { + udelay(20); + + if (read_data(info->client, BT541_POINT_STATUS_REG + 2 + ( i * 4), + (u8 *)(&info->touch_info.coord[i]), + sizeof(struct coord)) < 0) { + dev_err(&client->dev, "Failed to read point info\n"); + + return false; + } + } + } + +#else + if (read_data(info->client, BT541_POINT_STATUS_REG, + (u8 *)(&info->touch_info), sizeof(struct point_info)) < 0) { + dev_err(&client->dev, "Failed to read point info\n"); + + return false; + } +#endif + +out: + /* error */ + if (zinitix_bit_test(info->touch_info.status, BIT_MUST_ZERO) && + info->touch_info.status != 0xffff) { + dev_err(&client->dev, "Invalid must zero bit(%04x)\n", + info->touch_info.status); + /*write_cmd(info->client, BT541_CLEAR_INT_STATUS_CMD); + udelay(DELAY_FOR_SIGNAL_DELAY);*/ + return false; + } + + return true; +} + +#if ESD_TIMER_INTERVAL +static void esd_timeout_handler(unsigned long data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)data; + + info->p_esd_timeout_tmr = NULL; + queue_work(esd_tmr_workqueue, &info->tmr_work); +} + +static void esd_timer_start(u16 sec, struct bt541_ts_info *info) +{ + unsigned long flags; + spin_lock_irqsave(&info->lock, flags); + if (info->p_esd_timeout_tmr != NULL) +#ifdef CONFIG_SMP + del_singleshot_timer_sync(info->p_esd_timeout_tmr); +#else + del_timer(info->p_esd_timeout_tmr); +#endif + info->p_esd_timeout_tmr = NULL; + //init_timer(&(info->esd_timeout_tmr)); + //info->esd_timeout_tmr.data = (unsigned long)(info); + //info->esd_timeout_tmr.function = esd_timeout_handler; + info->esd_timeout_tmr.expires = jiffies + (HZ * sec); + info->p_esd_timeout_tmr = &info->esd_timeout_tmr; + add_timer(&info->esd_timeout_tmr); + spin_unlock_irqrestore(&info->lock, flags); +} + +static void esd_timer_stop(struct bt541_ts_info *info) +{ + unsigned long flags; + spin_lock_irqsave(&info->lock, flags); + if (info->p_esd_timeout_tmr) +#ifdef CONFIG_SMP + del_singleshot_timer_sync(info->p_esd_timeout_tmr); +#else + del_timer(info->p_esd_timeout_tmr); +#endif + + info->p_esd_timeout_tmr = NULL; + spin_unlock_irqrestore(&info->lock, flags); +} + +static void esd_timer_init(struct bt541_ts_info *info) +{ + unsigned long flags; + spin_lock_irqsave(&info->lock, flags); + //init_timer(&(info->esd_timeout_tmr)); + //info->esd_timeout_tmr.data = (unsigned long)(info); + //info->esd_timeout_tmr.function = esd_timeout_handler; + info->p_esd_timeout_tmr = NULL; + spin_unlock_irqrestore(&info->lock, flags); +} + +static void ts_tmr_work(struct work_struct *work) +{ + struct bt541_ts_info *info = + container_of(work, struct bt541_ts_info, tmr_work); + struct i2c_client *client = info->client; + +#if defined(TSP_VERBOSE_DEBUG) + dev_err(&client->dev, "tmr queue work ++\n"); +#endif + + if (down_trylock(&info->work_lock)) { + dev_err(&client->dev, "%s: Failed to occupy work lock\n", __func__); + esd_timer_start(CHECK_ESD_TIMER, info); + + return; + } + + if (info->work_state != NOTHING) { + dev_info(&client->dev, "%s: Other process occupied (%d)\n", + __func__, info->work_state); + up(&info->work_lock); + + return; + } + info->work_state = ESD_TIMER; + + disable_irq(info->irq); + info->irq_enabled = false; + //bt541_power_control(info, POWER_OFF); + bt541_power_control(info, POWER_ON_SEQUENCE); + + clear_report_data(info); + if (mini_init_touch(info) == false) + goto fail_time_out_init; + + info->work_state = NOTHING; + enable_irq(info->irq); + info->irq_enabled = true; + up(&info->work_lock); +#if defined(TSP_VERBOSE_DEBUG) + dev_info(&client->dev, "tmr queue work--\n"); +#endif + + return; +fail_time_out_init: + dev_err(&client->dev, "%s: Failed to restart\n", __func__); + esd_timer_start(CHECK_ESD_TIMER, info); + info->work_state = NOTHING; + enable_irq(info->irq); + info->irq_enabled = true; + up(&info->work_lock); + + return; +} +#endif + +static bool bt541_power_sequence(struct bt541_ts_info *info) +{ + struct i2c_client *client = info->client; + int retry = 0; + u16 chip_code; + + info->cap_info.ic_fw_size = 44*1024; + +retry_power_sequence: + if (write_reg(client, 0xc000, 0x0001) != I2C_SUCCESS) { + dev_err(&client->dev, "Failed to send power sequence(vendor cmd enable)\n"); + goto fail_power_sequence; + } + udelay(10); + + if (read_data(client, 0xcc00, (u8 *)&chip_code, 2) < 0) { + dev_err(&client->dev, "Failed to read chip code\n"); + goto fail_power_sequence; + } + + dev_info(&client->dev, "%s: chip code = 0x%x\n", __func__, chip_code); + udelay(10); + + //Link modify the flash size + if(chip_code == ZT7554_CHIP_CODE) + info->cap_info.ic_fw_size = 64*1024; + else if((chip_code == ZT7548_CHIP_CODE)||(chip_code == ZTW523_CHIP_CODE)||(chip_code == ZTW622_CHIP_CODE)||(chip_code == ZTW623_CHIP_CODE)) + info->cap_info.ic_fw_size = 48*1024; + else if((chip_code == ZT7538_CHIP_CODE)||(chip_code == ZTW522_CHIP_CODE)) + info->cap_info.ic_fw_size = 44*1024; + + + if (write_cmd(client, 0xc004) != I2C_SUCCESS) { + dev_err(&client->dev, "Failed to send power sequence(intn clear)\n"); + goto fail_power_sequence; + } + udelay(10); + + if (write_reg(client, 0xc002, 0x0001) != I2C_SUCCESS) { + dev_err(&client->dev, "Failed to send power sequence(nvm init)\n"); + goto fail_power_sequence; + } + mdelay(2); + + if (write_reg(client, 0xc001, 0x0001) != I2C_SUCCESS) { + dev_err(&client->dev, "Failed to send power sequence(program start)\n"); + goto fail_power_sequence; + } + mdelay(FIRMWARE_ON_DELAY); /* wait for checksum cal */ + + dev_info(&client->dev, "bt541_power_sequence: OK\n"); + + return true; + +fail_power_sequence: + if (retry++ < 3) { + mdelay(CHIP_ON_DELAY); + dev_info(&client->dev, "retry = %d\n", retry); + goto retry_power_sequence; + } + + dev_err(&client->dev, "Failed to send power sequence\n"); + return false; +} +/* +static bool zinitix_resume_sequence(struct bt541_ts_info *info) +{ + struct i2c_client *client = info->client; + int retry = 0; + + +retry_zinitix_resume_sequence: + if (write_reg(client, 0xc000, 0x0001) != I2C_SUCCESS) { + dev_err(&client->dev, "Failed to send power sequence(vendor cmd enable)\n"); + goto fail_zinitix_resume_sequence; + } + mdelay(1); + + if (write_reg(client, 0xc002, 0x0001) != I2C_SUCCESS) { + dev_err(&client->dev, "Failed to send power sequence(nvm init)\n"); + goto fail_zinitix_resume_sequence; + } + mdelay(1); + + if (write_reg(client, 0xc001, 0x0001) != I2C_SUCCESS) { + dev_err(&client->dev, "Failed to send power sequence(program start)\n"); + goto fail_zinitix_resume_sequence; + } + mdelay(1);*/ /* wait for checksum cal */ +/* + dev_info(&client->dev, "bt541_power_sequence: OK\n"); + + return true; + +fail_zinitix_resume_sequence: + if (retry++ < 3) { + mdelay(CHIP_ON_DELAY); + dev_info(&client->dev, "retry = %d\n", retry); + goto retry_zinitix_resume_sequence; + } + dev_err(&client->dev, "Failed to send power sequence\n"); + return false; +} +*/ +static int zinitix_power_control(struct bt541_ts_info *data, int on) +{ + int rc; + int avdd_gpio = data->pdata->avdd_gpio; + int iovdd_gpio = data->pdata->iovdd_gpio; + + if (!on) + goto power_off; + + if (avdd_gpio > 0) { + gpio_direction_output(avdd_gpio, 1); + dev_info(&data->client->dev, "Set avdd_gpio to 1\n"); + } else if (data->vdd && !regulator_is_enabled(data->vdd)) { + rc = regulator_enable(data->vdd); + if (rc) { + dev_err(&data->client->dev, + "Regulator vdd enable failed rc=%d\n", rc); + goto power_off; + } + dev_info(&data->client->dev, "Enable data->vdd regualtor\n"); + } + + if (iovdd_gpio > 0) { + gpio_direction_output(iovdd_gpio, 1); + dev_info(&data->client->dev, "Set iovdd_gpio to 1\n"); + } else if (data->vcc_i2c && !regulator_is_enabled(data->vcc_i2c)) { + rc = regulator_enable(data->vcc_i2c); + if (rc) { + dev_err(&data->client->dev, + "Regulator vcc_i2c enable failed rc=%d\n", rc); + goto power_off; + } + dev_info(&data->client->dev, "Enable data->vcc_i2c regualtor\n"); + } + data->ic_power_state = TS_MMI_POWER_ON; + + return 0; + +power_off: + if (avdd_gpio > 0) { + gpio_direction_output(avdd_gpio, 0); + dev_info(&data->client->dev, "Set avdd_gpio to 0\n"); + } + else if (data->vdd) { + rc = regulator_disable(data->vdd); + if (rc) { + dev_err(&data->client->dev, + "%s: Regulator vdd disable failed rc=%d\n", __func__, rc); + return rc; + } + dev_info(&data->client->dev, "Disable data->vdd regualtor\n"); + } + + if (iovdd_gpio > 0) { + gpio_direction_output(iovdd_gpio, 0); + dev_info(&data->client->dev, "Set iovdd_gpio to 0\n"); + } + else if (data->vcc_i2c) { + rc = regulator_disable(data->vcc_i2c); + if (rc) { + dev_err(&data->client->dev, + "%s: Regulator vcc_i2c disable failed rc=%d\n", __func__, rc); + return rc; + } + dev_info(&data->client->dev, "Disable data->vcc_i2c regualtor\n"); + } + + if (gpio_is_valid(data->pdata->gpio_reset)) { + gpio_direction_output(data->pdata->gpio_reset, 0); + pr_info("%s: set gpio_reset to 0 success\n", __func__); + } + data->ic_power_state = TS_MMI_POWER_OFF; + + return 0; +} + + + +bool bt541_power_control(struct bt541_ts_info *info, u8 ctl) +{ + pr_info("[TSP] %s, %d\n", __func__, ctl); + + if (ctl == POWER_OFF) { + zinitix_power_control(info, 0); + mdelay(CHIP_OFF_DELAY); + } else if (ctl == POWER_ON_SEQUENCE) { + printk("bt541_power_control: POWER_ON_SEQUENCE\n"); + zinitix_power_control(info, 1); + zinitix_hw_reset(info,true); + mdelay(CHIP_ON_DELAY); + return bt541_power_sequence(info); //zxt power on sequence + } else if (ctl == POWER_ON) { + zinitix_power_control(info, 1); + zinitix_hw_reset(info,true); + mdelay(CHIP_ON_DELAY); + } + return true; +} + +#ifdef USE_TSP_TA_CALLBACKS +static void bt541_set_ta_status(struct bt541_ts_info *info, bool force) +{ + printk("bt541_set ta_connected = %d\n", ta_connected); + + if(info == NULL) + return; + + if (ta_connected) + zinitix_bit_set(m_optional_mode, TS_USB_DETECT_BIT); + else + zinitix_bit_clr(m_optional_mode, TS_USB_DETECT_BIT); +} + +static void bt541_charger_status_cb(struct tsp_callbacks *cb, int status) +{ + printk("bt541_charger status = %d\n", status); + + if (status) + ta_connected = true; + else + ta_connected = false; + + bt541_set_ta_status(misc_info, true); + + dev_info(&misc_info->client->dev, "TA %s\n", + status ? "connected" : "disconnected"); +} +#endif + +static void ts_select_type_hw(struct bt541_ts_info *info) { + +#if (TSP_TYPE_COUNT == 1) + m_FirmwareIdx = 0; +#else + int i; + u16 newHWID; + +/* In case of TSP IC's firmware is broken, + it will always be updated to HW ID 02 firmware even though HW 01 device exists*/ + for(i = 0; i < TSP_TYPE_COUNT; i++) { + newHWID = (u16) (m_pFirmware[i][0x7528] | (m_pFirmware[i][0x7529]<<8)); + + if(info->cap_info.hw_id == newHWID) + break; + } + + m_FirmwareIdx = i; + if(i == TSP_TYPE_COUNT) + m_FirmwareIdx = 1; + zinitix_printk(KERN_INFO "firmwaretype = %d Firmware HWID = %u cap_info.hw_id = %u i = %d \n", + m_FirmwareIdx, newHWID, info->cap_info.hw_id, i); +#endif +} + +#if TOUCH_ONESHOT_UPGRADE +static bool ts_check_need_upgrade(struct bt541_ts_info *info, + u16 cur_version, u16 cur_minor_version, u16 cur_reg_version) +{ + u16 new_version; + u16 new_minor_version; + u16 new_reg_version; + + u8 *firmware_data; + + ts_select_type_hw(info); + firmware_data = (u8*)m_pFirmware[m_FirmwareIdx]; + + new_version = (u16) (firmware_data[52] | (firmware_data[53]<<8)); + new_minor_version = (u16) (firmware_data[56] | (firmware_data[57]<<8)); + new_reg_version = (u16) (firmware_data[60] | (firmware_data[61]<<8)); + + + zinitix_printk("cur version = 0x%x, new version = 0x%x\n", + cur_version, new_version); + if(cur_version > 0xFF) + return true; + if (cur_version < new_version) + return true; + else if (cur_version > new_version) + return false; + + zinitix_printk("cur minor version = 0x%x, new minor version = 0x%x\n", + cur_minor_version, new_minor_version); + if (cur_minor_version < new_minor_version) + return true; + else if (cur_minor_version > new_minor_version) + return false; + + zinitix_printk("cur reg data version = 0x%x, new reg data version = 0x%x\n", + cur_reg_version, new_reg_version); + if (cur_reg_version client, 0xc000, 0x0001) != I2C_SUCCESS){ + dev_err(&info->client->dev,"power sequence error (vendor cmd enable)\n"); + goto fail_check_hwid; + } + udelay(10); + if (read_data(info->client, 0xcc00, (u8 *)&chip_code, 2) < 0) { + dev_err(&info->client->dev,"fail to read chip code\n"); + goto fail_check_hwid; + } + dev_info(&info->client->dev,"chip code = 0x%x\n", chip_code); + udelay(10); + if (write_cmd(info->client, 0xc004) != I2C_SUCCESS){ + dev_err(&info->client->dev,"power sequence error (intn clear)\n"); + goto fail_check_hwid; + } + udelay(10); + if (write_reg(info->client, 0xc002, 0x0001) != I2C_SUCCESS){ + dev_err(&info->client->dev,"power sequence error (nvm init)\n"); + goto fail_check_hwid; + } + //dev_err(&info->client->dev, "init flash\n"); + mdelay(5); + if (write_reg(info->client, 0xc003, 0x0000) != I2C_SUCCESS){ + dev_err(&info->client->dev,"fail to write nvm vpp on\n"); + goto fail_check_hwid; + } + if (write_reg(info->client, 0xc104, 0x0000) != I2C_SUCCESS){ + dev_err(&info->client->dev,"fail to write nvm wp disable\n"); + goto fail_check_hwid; + } + //dev_err(&info->client->dev, "init flash\n"); + if (write_cmd(info->client, BT541_INIT_FLASH) != I2C_SUCCESS) { + dev_err(&info->client->dev, "failed to init flash\n"); + goto fail_check_hwid; + } + //dev_err(&info->client->dev, "read firmware data\n"); + for (i = 0; i < 80; i+=TC_SECTOR_SZ) { + if (read_firmware_data(info->client, + BT541_READ_FLASH, + (u8*)&check_data[i], TC_SECTOR_SZ) < 0) { + dev_err(&info->client->dev, "error : read zinitix tc firmare\n"); + goto fail_check_hwid; + } + } + hw_id0 = check_data[48] + check_data[49]*256; + hw_id1_1 = check_data[70]; + hw_id1_2 = check_data[71]; + + //dev_err(&info->client->dev, "eeprom hw id = %d, %d, %d\n", hw_id0, hw_id1_1, hw_id1_2); + + if(hw_id1_1 == hw_id1_2 && hw_id0 != hw_id1_1) + info->cap_info.hw_id = hw_id1_1; + else + info->cap_info.hw_id = hw_id0; + + dev_err(&info->client->dev, "hw id = %d\n", info->cap_info.hw_id); + mdelay(5); + return; + +fail_check_hwid: + if(retry++ <3) { + dev_info(&info->client->dev, "fail to check hw id, retry cnt = %d\n", retry); + mdelay(5); + goto retry_fatal; + } +} +#endif +//jesse add +static inline s32 write_cc02(struct i2c_client *client, u32 addrL, u32 addrH) +{ + s32 ret; + u8 pkt[10]; /* max packet */ + u16 length; + + // pr_info("bt541 write_cc01 \n"); + printk(KERN_ALERT "bt541:01%s",__func__); + pkt[0] = 0x02; /* command */ + pkt[1] = 0xcc; + pkt[2] = (addrL) & 0xff; /* addrL */ + pkt[3] = (addrL >> 8)&0xff; + pkt[4] = (addrL >> 16)&0xff; /* addrL */ + pkt[5] = (addrL >> 24)&0xff; + pkt[6] = (addrH) & 0xff; /* addrH */ + pkt[7] = (addrH >> 8)&0xff; + pkt[8] = (addrH >>16) & 0xff; /* addrH */ + pkt[9] = (addrH >> 24)&0xff; + length = 8; + +//retry: + ret = i2c_master_send(client , pkt , length + 2); + printk(KERN_ALERT "bt541:02%s",__func__); + if (ret < 0) { + // mdelay(1); + + // if (++count < 8) + // goto retry; + + return ret; + } + printk(KERN_ALERT "bt541:03%s",__func__); + udelay(DELAY_FOR_POST_TRANSCATION); + return length; +} +//jesse end + +static inline s32 write_cc01(struct i2c_client *client, u16 addrL, u16 addrH) +{ + s32 ret; + u8 pkt[10]; /* max packet */ + u16 length; + + pr_info("bt541 write_cc01 \n"); + pkt[0] = 0x01; /* command */ + pkt[1] = 0xcc; + pkt[2] = (addrL) & 0xff; /* addrL */ + pkt[3] = (addrL >> 8)&0xff; + pkt[4] = (addrH) & 0xff; /* addrH */ + pkt[5] = (addrH >> 8)&0xff; + length = 4; + + ret = i2c_master_send(client , pkt , length + 2); + if (ret < 0) { + return ret; + } + + udelay(DELAY_FOR_POST_TRANSCATION); + return length; +} + +static inline s32 read_cc01(struct i2c_client *client, u16 addr, u8 *values) +{ + s32 ret; + u16 length; + /* select register*/ + pr_info("bt541 read_cc01 \n"); + ret = i2c_master_send(client , (u8 *)&addr , 2); + if (ret < 0) + return ret; + + /* for setup tx transaction. */ + mdelay(1); + + length = 4; + ret = i2c_master_recv(client , values , length); + if (ret < 0) + return ret; + + udelay(DELAY_FOR_POST_TRANSCATION); + return length; +} + +static bool ts_upgrade_firmware(struct bt541_ts_info *info, + const u8 *firmware_data, u32 size) +{ + struct i2c_client *client = info->client; + u16 flash_addr; + int retry_cnt = 0; +#ifdef BURST_UPGRADE + struct bt541_ts_platform_data *pdata = info->pdata; +#endif + int page_sz = 64; + u16 chip_code; +#ifdef VERIFY_CHECKSUM + u16 year, chip_fw_year; + u16 month, chip_fw_month; + u16 day, chip_fw_day; + u16 total_version, chip_fw_total_version; + u32 cc01_data = 0; +#else + int i; + u8 *verify_data; + + verify_data = kzalloc(size, GFP_KERNEL); + if (verify_data == NULL) { + zinitix_printk(KERN_ERR "cannot alloc verify buffer\n"); + return false; + } +#endif + +retry_upgrade: + bt541_power_control(info, POWER_OFF); + bt541_power_control(info, POWER_ON); + msleep(10); + //jesse add + + if (write_cmd(client, 0x01f8) != I2C_SUCCESS) { + zinitix_printk("Failed to send 01f8 command \n"); + goto fail_upgrade; + } + msleep(10); + printk("==========01f8 end===============\n"); + + if (write_reg(client, 0xc011, 0x0080) != I2C_SUCCESS) { + zinitix_printk("Failed to send c011 command\\n"); + goto fail_upgrade; + } + msleep(10); + printk("==========c011 end===============\n"); + + if (write_cc02(client, 0x001f0028, 0x0002561A) < 0) { + zinitix_printk("Failed to send 0028 command\nn"); + } + msleep(5); + printk("==========0028 end===============\n"); + + if (write_cc02(client, 0x001f002c, 0x00003bd0) < 0) { + zinitix_printk("Failed to send 002c command\nn"); + } + msleep(5); + printk("==========002c end===============\n"); + + if (write_cmd(client, 0x01d5) != I2C_SUCCESS) { + zinitix_printk("0x01d5 error\n"); + goto fail_upgrade; + } + msleep(10); + //jesse end + + if (write_reg(client, 0xc000, 0x0001) != I2C_SUCCESS) { + zinitix_printk("power sequence error (vendor cmd enable)\n"); + goto fail_upgrade; + } + msleep(1); + + if (read_data(client, 0xcc00, (u8 *)&chip_code, 2) < 0) { + zinitix_printk("failed to read chip code\n"); + goto fail_upgrade; + } + zinitix_printk("chip code = 0x%x\n", chip_code); + udelay(10); + + if (write_cmd(client, 0xc004) != I2C_SUCCESS) { + zinitix_printk("power sequence error (intn clear)\n"); + goto fail_upgrade; + } + msleep(10); + + if (write_reg(client, 0xc002, 0x0001) != I2C_SUCCESS) { + zinitix_printk("power sequence error (nvm init)\n"); + goto fail_upgrade; + } + msleep(5); + + zinitix_printk("init flash\n"); + + if (write_reg(client, 0xc003, 0x0001) != I2C_SUCCESS) { + zinitix_printk("failed to write nvm vpp on\n"); + goto fail_upgrade; + } + msleep(10); + +#ifdef BURST_UPGRADE //burst upgrade + // Mass Erase start + if (write_cmd(client, 0x01DF) != I2C_SUCCESS) { + zinitix_printk("failed to mass erase\n"); + goto fail_upgrade; + }else{ + zinitix_printk("bt541 erase ok\n"); + } + msleep(100); + // Mass Erase End + + if (write_reg(client, 0x01D0, 0x0002) != I2C_SUCCESS) { + zinitix_printk("failed to init upgrade mode\n"); + goto fail_upgrade; + } + msleep(5); + if (write_reg(client, 0x01D3, 0x0040) != I2C_SUCCESS) { + zinitix_printk("failed to set package size\n"); + goto fail_upgrade; + } + + if (write_reg(client, 0x01D4, 0x0001) != I2C_SUCCESS) { + zinitix_printk("failed to init repeat size\n"); + goto fail_upgrade; + } + + if (write_reg(client, 0x01DE, 0x0001) != I2C_SUCCESS) { + zinitix_printk("failed to enter upgrade mode\n"); + goto fail_upgrade; + }else{ + zinitix_printk("bt541 enter upgrade mode ok\n"); + } + msleep(1); + + zinitix_printk("writing firmware data\n"); + for (flash_addr = 0; flash_addr < size; ) { + //zinitix_printk(KERN_INFO "flash_addr = 0x%04x\n", flash_addr); // (brian,181204) add + if (write_data(client, + BT541_WRITE_FLASH, + (u8 *)&firmware_data[flash_addr], page_sz) < 0) { + zinitix_printk("error : write zinitix tc firmare\n"); + goto fail_upgrade; + } + while (!gpio_get_value(pdata->gpio_int)); + flash_addr += page_sz; + } + + if (write_cmd(client, 0x01DD) != I2C_SUCCESS) { + zinitix_printk("failed to flush\n"); + goto fail_upgrade; + }else{ + zinitix_printk("bt541 flush ok\n"); + } + while (!gpio_get_value(pdata->gpio_int)); + + zinitix_printk("burst upgrade finished\n"); + + if (write_reg(client, 0xc003, 0x0000) != I2C_SUCCESS) { + zinitix_printk("nvm write disable\n"); + goto fail_upgrade; + } + msleep(10); + zinitix_printk("init flash\n"); + if (write_cmd(client, BT541_INIT_FLASH) != I2C_SUCCESS) { + zinitix_printk("failed to init flash\n"); + goto fail_upgrade; + } + msleep(1); +#else //normal upgrade + if (write_reg(client, 0xc104, 0x0001) != I2C_SUCCESS) { + zinitix_printk("failed to write nvm wp disable\n"); + goto fail_upgrade; + } + + if (write_cmd(client, 0x01DF) != I2C_SUCCESS) { + zinitix_printk("Mass erase fail\n"); + goto fail_upgrade; + } + mdelay(100); + //#define BT541_INIT_FLASH 0x01d0 + if (write_cmd(client, BT541_INIT_FLASH) != I2C_SUCCESS) { + zinitix_printk("failed to init flash\n"); + goto fail_upgrade; + } + + for (flash_addr = 0; flash_addr < size; ) { + for (i = 0; i < page_sz/TC_SECTOR_SZ; i++) { + //zinitix_debug_msg("write :addr=%04x, len=%d\n", flash_addr, TC_SECTOR_SZ); + if (write_data(client,BT541_WRITE_FLASH,(u8 *)&firmware_data[flash_addr], TC_SECTOR_SZ) < 0) { + zinitix_printk(KERN_INFO"error : write zinitix tc firmare\n"); + goto fail_upgrade; + } + flash_addr += TC_SECTOR_SZ; + udelay(100); + } + + mdelay(10); /*for fuzing delay*/ + } + + if (write_reg(client, 0xc003, 0x0000) != I2C_SUCCESS) { + zinitix_printk("nvm write vpp off\n"); + goto fail_upgrade; + } + + if (write_reg(client, 0xc104, 0x0000) != I2C_SUCCESS) { + zinitix_printk("nvm wp enable\n"); + goto fail_upgrade; + } + + zinitix_printk("init flash\n"); + + if (write_cmd(client, BT541_INIT_FLASH) != I2C_SUCCESS) { + zinitix_printk("failed to init flash\n"); + goto fail_upgrade; + } + msleep(1); +#endif +#ifndef VERIFY_CHECKSUM + zinitix_printk("read firmware data\n"); + + for (flash_addr = 0; flash_addr < size; ) { + for (i = 0; i < page_sz/TC_SECTOR_SZ; i++) { + /*zinitix_debug_msg("read :addr=%04x, len=%d\n", flash_addr, TC_SECTOR_SZ);*/ + if (read_firmware_data(client,BT541_READ_FLASH,(u8*)&verify_data[flash_addr], TC_SECTOR_SZ) < 0) { + dev_err(&client->dev, "Failed to read firmare\n"); + + goto fail_upgrade; + } + + flash_addr += TC_SECTOR_SZ; + } + } + /* verify */ + dev_info(&client->dev, "verify firmware data\n"); + if (memcmp((u8 *)&firmware_data[0], (u8 *)&verify_data[0], size) == 0) { + dev_info(&client->dev, "upgrade finished\n"); + kfree(verify_data); + bt541_power_control(info, POWER_OFF); + bt541_power_control(info, POWER_ON_SEQUENCE); + + return true; + } +#else + zinitix_printk("verify firmware enter\n"); + bt541_power_control(info, POWER_OFF); + bt541_power_control(info, POWER_ON_SEQUENCE); + msleep(10); + + /* get chip firmware version */ + if (read_data(client, BT541_FIRMWARE_VERSION, + (u8 *)&chip_fw_year, 2) < 0) { + zinitix_printk("read chip_fw_year fail\n"); + goto fail_upgrade; + } + + if (read_data(client, BT541_MINOR_FW_VERSION, + (u8 *)&chip_fw_month, 2) < 0) { + zinitix_printk("read chip_fw_month fail\n"); + goto fail_upgrade; + } + + if (read_data(client, BT541_FW_DAY_VERSION, + (u8 *)&chip_fw_day, 2) < 0) { + zinitix_printk("read chip_fw_day fail\n"); + goto fail_upgrade; + } + + if (read_data(client, BT541_DATA_VERSION_REG, + (u8 *)&chip_fw_total_version, 2) < 0) { + zinitix_printk("read chip_fw_total_version fail\n"); + goto fail_upgrade; + } + + zinitix_printk("chip_fw_year = %d, \ + chip_fw_month = %d, \ + chip_fw_day = %d, \ + chip_fw_total_version = %d\n", + chip_fw_year, chip_fw_month, chip_fw_day, chip_fw_total_version); + + year = (u16) ((firmware_data[FW_OFFSET_YEAR_MSB] << 8) + | firmware_data[FW_OFFSET_YEAR_LSB]); + month = (u16) ((firmware_data[FW_OFFSET_MONTH_MSB] << 8) + | firmware_data[FW_OFFSET_MONTH_LSB]); + day = (u16) ((firmware_data[FW_OFFSET_DAY_MSB] << 8) + | firmware_data[FW_OFFSET_DAY_LSB]); + total_version = (u16) ((firmware_data[FW_OFFSET_TOTAL_VERSION_MSB] << 8) + | firmware_data[FW_OFFSET_TOTAL_VERSION_LSB]); + zinitix_printk("Year = %d, Month = %d, Day = %d, total_version = %d\n", + year, month, day, total_version); + if ((year == chip_fw_year) && (month == chip_fw_month) && + (day == chip_fw_day) && (total_version == chip_fw_total_version)) { + zinitix_printk("verify firmware version pass!\n"); + } else { + zinitix_printk("verify firmware version fail!\n"); + goto fail_upgrade; + } + + if (write_cc01(client, 0xb6fc, 0x0018) < 0) {//0xa9fc 0xa45c + dev_err(&client->dev, "Failed to send cc01 command\n"); + goto fail_upgrade; + } + udelay(10); + if (read_cc01(client, 0xcc01, (u8 *)&cc01_data) < 0) { + dev_err(&client->dev, "Failed to read cc01_data\n"); + goto fail_upgrade; + } + if (cc01_data != CHECKSUM_VALUE) { + zinitix_printk("Failed verify last 4 bytes cc01_data = 0x%x, retry !\n", cc01_data); + goto fail_upgrade; + } + udelay(10); + zinitix_printk("verify last 4 bytes success! cc01_data = 0x%x\n", cc01_data); + + return true; +#endif + +fail_upgrade: + if (retry_cnt++ < INIT_RETRY_CNT) { + dev_err(&client->dev, "upgrade failed : so retry... (%d)\n", retry_cnt); + goto retry_upgrade; + } + +#ifndef BURST_UPGRADE + if (verify_data != NULL) + kfree(verify_data); +#endif + + dev_info(&client->dev, "Failed to upgrade\n"); + + return false; +} + +static bool init_touch(struct bt541_ts_info *info) +{ + struct bt541_ts_platform_data *pdata = info->pdata; + struct i2c_client *client = info->client; + struct capa_info *cap = &(info->cap_info); + u16 reg_val; + int i; + u16 chip_eeprom_info; + +#if USE_CHECKSUM + u16 chip_check_sum; + u8 checksum_err; +#endif + int retry_cnt = 0; +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + char* productionMode = "androidboot.bsp=2"; +#endif + char* checkMode = NULL; + + dev_info(&client->dev, "init_touch: B\n"); + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + checkMode = strstr(saved_command_line, productionMode); +#endif +retry_init: + for (i = 0; i < INIT_RETRY_CNT; i++) { + if (read_data(client, BT541_EEPROM_INFO_REG, + (u8 *)&chip_eeprom_info, 2) < 0) { + dev_err(&client->dev, "Failed to read eeprom info(%d)\n", i); + mdelay(10); + continue; + } else + break; + } + + if (i == INIT_RETRY_CNT) + goto fail_init; + +#if USE_CHECKSUM + dev_info(&client->dev, "%s: Check checksum\n", __func__); + + checksum_err = 0; + + for (i = 0; i < INIT_RETRY_CNT; i++) { + if (read_data(client, BT541_CHECKSUM_RESULT, + (u8 *)&chip_check_sum, 2) < 0) { + mdelay(10); + continue; + } + +#if defined(TSP_VERBOSE_DEBUG) + dev_info(&client->dev, "0x%04X\n", chip_check_sum); +#endif + + if(chip_check_sum == 0x55aa) + break; + else { + checksum_err = 1; + break; + } + } + + if (i == INIT_RETRY_CNT || checksum_err) { + dev_err(&client->dev, "Failed to check firmware data\n"); + + goto fail_init; + } +#endif + + if (write_cmd(client, BT541_SWRESET_CMD) != I2C_SUCCESS) { + dev_err(&client->dev, "Failed to write reset command\n"); + goto fail_init; + } + +#ifdef SUPPORTED_TOUCH_KEY + cap->button_num = SUPPORTED_BUTTON_NUM; +#endif + + reg_val = 0; + zinitix_bit_set(reg_val, BIT_PT_CNT_CHANGE); + zinitix_bit_set(reg_val, BIT_DOWN); + zinitix_bit_set(reg_val, BIT_MOVE); + zinitix_bit_set(reg_val, BIT_UP); +#if SUPPORTED_PALM_TOUCH + zinitix_bit_set(reg_val, BIT_PALM); + //zinitix_bit_set(reg_val, BIT_PALM_REJECT); +#endif +// zinitix_bit_set(reg_val, BIT_PT_EXIST); + + + if (cap->button_num > 0) + zinitix_bit_set(reg_val, BIT_ICON_EVENT); + + cap->ic_int_mask = reg_val; + + dev_info(&client->dev, "ic_int_mask=%d:\n", cap->ic_int_mask); + + if (write_reg(client, BT541_INT_ENABLE_FLAG, 0x0) != I2C_SUCCESS) + goto fail_init; +#if USE_WAKEUP_GESTURE + if (write_reg(client, 0x126, 0) != I2C_SUCCESS) + goto fail_init; +#endif + + dev_info(&client->dev, "%s: Send reset command\n", __func__); + if (write_cmd(client, BT541_SWRESET_CMD) != I2C_SUCCESS) + goto fail_init; + + /* get chip information */ + if (read_data(client, BT541_VENDOR_ID, + (u8 *)&cap->vendor_id, 2) < 0) { + zinitix_printk("failed to read chip revision\n"); + goto fail_init; + } + + if (read_data(client, BT541_CHIP_REVISION, + (u8 *)&cap->ic_revision, 2) < 0) { + zinitix_printk("failed to read chip revision\n"); + goto fail_init; + } + +// cap->ic_fw_size = 44*1024; + + if (read_data(client, BT541_HW_ID, (u8 *)&cap->hw_id, 2) < 0) { + dev_err(&client->dev, "Failed to read hw id\n"); + goto fail_init; + } + if (read_data(client, BT541_THRESHOLD, (u8 *)&cap->threshold, 2) < 0) + goto fail_init; + + + if (read_data(client, BT541_BUTTON_SENSITIVITY, + (u8 *)&cap->key_threshold, 2) < 0) + goto fail_init; + + if (read_data(client, BT541_DUMMY_BUTTON_SENSITIVITY, + (u8 *)&cap->dummy_threshold, 2) < 0) + goto fail_init; + + if (read_data(client, BT541_TOTAL_NUMBER_OF_X, + (u8 *)&cap->x_node_num, 2) < 0) + goto fail_init; + + if (read_data(client, BT541_TOTAL_NUMBER_OF_Y, + (u8 *)&cap->y_node_num, 2) < 0) + goto fail_init; + + cap->total_node_num = cap->x_node_num * cap->y_node_num; + + if (read_data(client, BT541_DND_N_COUNT, + (u8 *)&cap->N_cnt, 2) < 0) + goto fail_init; + + zinitix_debug_msg("N count = %d\n", cap->N_cnt); + + if (read_data(client, BT541_DND_U_COUNT, + (u8 *)&cap->u_cnt, 2) < 0) + goto fail_init; + + zinitix_debug_msg("U count = %d\n", cap->u_cnt); + + if (read_data(client, BT541_AFE_FREQUENCY, + (u8 *)&cap->afe_frequency, 2) < 0) + goto fail_init; + + zinitix_debug_msg("AFE frequency = %d\n", cap->afe_frequency); + + + /* get chip firmware version */ + if (read_data(client, BT541_FIRMWARE_VERSION, + (u8 *)&cap->fw_version, 2) < 0) + goto fail_init; + + if (read_data(client, BT541_MINOR_FW_VERSION, + (u8 *)&cap->fw_minor_version, 2) < 0) + goto fail_init; + + if (read_data(client, BT541_FW_DAY_VERSION, + (u8 *)&cap->fw_day_version, 2) < 0) + goto fail_init; + + if (read_data(client, BT541_DATA_VERSION_REG, + (u8 *)&cap->reg_data_version, 2) < 0) + goto fail_init; + + zinitix_printk("fw_version = 0x%x, fw_minor_version = 0x%x, fw_day_version = 0x%x, reg_data_version = 0x%x\n", + cap->fw_version, cap->fw_minor_version, cap->fw_day_version, cap->reg_data_version); + +#if TOUCH_ONESHOT_UPGRADE + if ((checkMode == NULL) &&(ts_check_need_upgrade(info, cap->fw_version, + cap->fw_minor_version, cap->reg_data_version) == true) && (info->checkUMSmode == false)) { + zinitix_printk("start upgrade firmware\n"); + + if (ts_upgrade_firmware(info, m_pFirmware[m_FirmwareIdx], + cap->ic_fw_size) == false) + goto fail_init; + + /* disable chip interrupt */ + if (write_reg(client, BT541_INT_ENABLE_FLAG, 0) != I2C_SUCCESS) + goto fail_init; + + /* get chip firmware version */ + if (read_data(client, BT541_FIRMWARE_VERSION, + (u8 *)&cap->fw_version, 2) < 0) + goto fail_init; + + if (read_data(client, BT541_MINOR_FW_VERSION, + (u8 *)&cap->fw_minor_version, 2) < 0) + goto fail_init; + + if (read_data(client, BT541_DATA_VERSION_REG, + (u8 *)&cap->reg_data_version, 2) < 0) + goto fail_init; + } +#endif + + if (read_data(client, BT541_EEPROM_INFO_REG, + (u8 *)&chip_eeprom_info, 2) < 0) + goto fail_init; + + /* initialize */ + if (write_reg(client, BT541_X_RESOLUTION, + (u16)pdata->x_resolution) != I2C_SUCCESS) + goto fail_init; + + if (write_reg(client, BT541_Y_RESOLUTION, + (u16)pdata->y_resolution) != I2C_SUCCESS) + goto fail_init; + + cap->MinX = (u32)0; + cap->MinY = (u32)0; + cap->MaxX = (u32)pdata->x_resolution; + cap->MaxY = (u32)pdata->y_resolution; + + if (write_reg(client, BT541_BUTTON_SUPPORTED_NUM, + (u16)cap->button_num) != I2C_SUCCESS) + goto fail_init; + + if (write_reg(client, BT541_SUPPORTED_FINGER_NUM, + (u16)MAX_SUPPORTED_FINGER_NUM) != I2C_SUCCESS) + goto fail_init; + + cap->multi_fingers = MAX_SUPPORTED_FINGER_NUM; + + zinitix_debug_msg("max supported finger num = %d\r\n", + cap->multi_fingers); + cap->gesture_support = 0; + zinitix_debug_msg("set other configuration\r\n"); + + if (write_reg(client, BT541_INITIAL_TOUCH_MODE, + TOUCH_POINT_MODE) != I2C_SUCCESS) + goto fail_init; + + if (write_reg(client, BT541_TOUCH_MODE, info->touch_mode) != I2C_SUCCESS) + goto fail_init; + + if (read_data(client, ZINITIX_INTERNAL_FLAG_02, + (u8 *)®_val, 2) < 0) + goto fail_init; + + cap->i2s_checksum = 0; + + if (write_reg(client, BT541_INT_ENABLE_FLAG, + cap->ic_int_mask) != I2C_SUCCESS) + goto fail_init; + + /* Clear interrupt flag */ + for (i = 0; i < 10; i++) { + write_cmd(client, BT541_CLEAR_INT_STATUS_CMD); + udelay(10); + } + + if (info->touch_mode != TOUCH_POINT_MODE) { /* Test Mode */ + if (write_reg(client, BT541_DELAY_RAW_FOR_HOST, + RAWDATA_DELAY_FOR_HOST) != I2C_SUCCESS) { + dev_err(&client->dev, "%s: Failed to set DELAY_RAW_FOR_HOST\n", + __func__); + + goto fail_init; + } + } +#if ESD_TIMER_INTERVAL + if (write_reg(client, BT541_PERIODICAL_INTERRUPT_INTERVAL, + SCAN_RATE_HZ * ESD_TIMER_INTERVAL) != I2C_SUCCESS) + goto fail_init; + + read_data(client, BT541_PERIODICAL_INTERRUPT_INTERVAL, (u8 *)®_val, 2); +#if defined(TSP_VERBOSE_DEBUG) + dev_info(&client->dev, "Esd timer register = %d\n", reg_val); +#endif +#endif + /* Clear interrupt flag */ + for (i = 0; i < 10; i++) { + write_cmd(client, BT541_CLEAR_INT_STATUS_CMD); + udelay(10); + } + + zinitix_debug_msg("successfully initialized\r\n"); + return true; + +fail_init: + if (++retry_cnt < INIT_RETRY_CNT) { + bt541_power_control(info, POWER_OFF); + bt541_power_control(info, POWER_ON_SEQUENCE); + + zinitix_debug_msg("retry to initiallize(retry cnt = %d)\r\n", + retry_cnt); + goto retry_init; + + } else if (retry_cnt == INIT_RETRY_CNT) { + + zinitix_debug_msg("retry to initiallize(retry cnt = %d)\r\n", retry_cnt); +#if TOUCH_FORCE_UPGRADE + if(checkMode == NULL) { + + ts_check_hwid_in_fatal_state(info); + ts_select_type_hw(info); + if (ts_upgrade_firmware(info, m_pFirmware[m_FirmwareIdx], + cap->ic_fw_size) == false) { + zinitix_printk("upgrade failed\n"); + return false; + } + } + else + return true; + mdelay(100); + + goto retry_init; +#endif + } + + dev_err(&client->dev, "Failed to initiallize\n"); + + return false; +} + +bool mini_init_touch(struct bt541_ts_info *info) +{ + struct bt541_ts_platform_data *pdata = info->pdata; + struct i2c_client *client = info->client; + int i; + + if (write_cmd(client, BT541_SWRESET_CMD) != I2C_SUCCESS) { + dev_info(&client->dev, "Failed to write reset command\n"); + + goto fail_mini_init; + } + + /* initialize */ + if (write_reg(client, BT541_X_RESOLUTION, + (u16)(pdata->x_resolution)) != I2C_SUCCESS) + goto fail_mini_init; + + if (write_reg(client, BT541_Y_RESOLUTION, + (u16)(pdata->y_resolution)) != I2C_SUCCESS) + goto fail_mini_init; + + dev_info(&client->dev, "touch max x = %d\r\n", pdata->x_resolution); + dev_info(&client->dev, "touch max y = %d\r\n", pdata->y_resolution); + + if (write_reg(client, BT541_BUTTON_SUPPORTED_NUM, + (u16)info->cap_info.button_num) != I2C_SUCCESS) + goto fail_mini_init; + + if (write_reg(client, BT541_SUPPORTED_FINGER_NUM, + (u16)MAX_SUPPORTED_FINGER_NUM) != I2C_SUCCESS) + goto fail_mini_init; + + if (write_reg(client, BT541_INITIAL_TOUCH_MODE, + TOUCH_POINT_MODE) != I2C_SUCCESS) + goto fail_mini_init; + + if (write_reg(client, BT541_TOUCH_MODE, + info->touch_mode) != I2C_SUCCESS) + goto fail_mini_init; + + + if (write_reg(client, BT541_INT_ENABLE_FLAG, + info->cap_info.ic_int_mask) != I2C_SUCCESS) + goto fail_mini_init; + + /* read garbage data */ + for (i = 0; i < 10; i++) { + write_cmd(client, BT541_CLEAR_INT_STATUS_CMD); + udelay(10); + } + + if (info->touch_mode != TOUCH_POINT_MODE) { + if (write_reg(client, BT541_DELAY_RAW_FOR_HOST, + RAWDATA_DELAY_FOR_HOST) != I2C_SUCCESS){ + dev_err(&client->dev, "Failed to set BT541_DELAY_RAW_FOR_HOST\n"); + + goto fail_mini_init; + } + } + +#if ESD_TIMER_INTERVAL + if (write_reg(client, BT541_PERIODICAL_INTERRUPT_INTERVAL, + SCAN_RATE_HZ * ESD_TIMER_INTERVAL) != I2C_SUCCESS) + goto fail_mini_init; + + esd_timer_start(CHECK_ESD_TIMER, info); +#if defined(TSP_VERBOSE_DEBUG) + dev_info(&client->dev, "Started esd timer\n"); +#endif +#endif + dev_err(&client->dev, "Successfully mini initialized\r\n"); + return true; + +fail_mini_init: + dev_err(&client->dev, "Failed to initialize mini init\n"); +#if 0 + bt541_power_control(info, POWER_OFF); + bt541_power_control(info, POWER_ON_SEQUENCE); + + if (init_touch(info) == false) { + dev_err(&client->dev, "Failed to initialize\n"); + + return false; + } + +#if ESD_TIMER_INTERVAL + esd_timer_start(CHECK_ESD_TIMER, info); +#if defined(TSP_VERBOSE_DEBUG) + dev_info(&client->dev, "Started esd timer\n"); +#endif +#endif + return true; +#else + return false; + +#endif +} + + +void clear_report_data(struct bt541_ts_info *info) +{ + int i; + u8 reported = 0; + u8 sub_status; + +#ifdef SUPPORTED_TOUCH_KEY + for (i = 0; i < info->cap_info.button_num; i++) { + if (info->button[i] == ICON_BUTTON_DOWN) { + info->button[i] = ICON_BUTTON_UP; + input_report_key(info->input_dev, BUTTON_MAPPING_KEY[i], 0); + reported = true; +#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP) + printk(KERN_INFO "Button up = %d\n", i); +#else + printk(KERN_INFO "Button up\n"); +#endif + } + } +#endif + + input_report_key(info->input_dev, BTN_TOUCH, 0); + + for (i = 0; i < info->cap_info.multi_fingers; i++) { + sub_status = info->reported_touch_info.coord[i].sub_status; + if (zinitix_bit_test(sub_status, SUB_BIT_EXIST)) { + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 0); + reported = true; + if (!m_ts_debug_mode && TSP_NORMAL_EVENT_MSG) + printk(KERN_INFO "[TSP] R %02d\r\n", i); + } + info->reported_touch_info.coord[i].sub_status = 0; + } + + if (reported) + input_sync(info->input_dev); + + info->finger_cnt1=0; +} + +#define PALM_REPORT_WIDTH 200 +#define PALM_REJECT_WIDTH 255 + +static irqreturn_t bt541_touch_work(int irq, void *data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)data; + struct bt541_ts_platform_data *pdata = info->pdata; + struct i2c_client *client = info->client; + int i; +#ifdef SUPPORTED_TOUCH_KEY + u8 reported = false; +#endif + u8 sub_status; + u8 prev_sub_status; + u32 x, y, maxX, maxY; + u32 w; + u32 tmp; + //u8 read_result = 1; + u8 palm = 0; +#if defined(USE_WAKEUP_GESTURE) || defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + u16 gesture_flag=0; + int ret =0; +#endif + + if (down_trylock(&info->work_lock)) { + dev_err(&client->dev, "%s: Failed to occupy work lock\n", __func__); + write_cmd(client, BT541_CLEAR_INT_STATUS_CMD); + + return IRQ_HANDLED; + } + +#if ESD_TIMER_INTERVAL + esd_timer_stop(info); +#endif +#if USE_WAKEUP_GESTURE + if((tpd_halt)&&(info->work_state == SUSPEND)) //suspend state support gesture + { + dev_err(&client->dev," gesture wakeup "); + if (read_data(client, 0x126,(u8 *)&gesture_flag, 2) < 0) //0x126 + { + dev_err(&client->dev," gesture read reg error!!!\r\n"); + + ret=0; + /*eric add 20160920*/ + write_cmd(client, BT541_CLEAR_INT_STATUS_CMD); + } + else + { + /*wake up*/ + /*eric add 20160920*/ + write_cmd(client, BT541_CLEAR_INT_STATUS_CMD); + + dev_err(&client->dev," gesture wakeup flag:%d\r\n",gesture_flag); + //write_reg(client, 0x3da, gesture_flag); + //if(gesture_flag==2)/*1 double click*/ + if(gesture_flag==1)/*1 double click*/ + { + dev_err(&client->dev,"[bt541_touch_work]USE_WAKEUP_GESTURE---powerkey__start\n"); + input_report_key(info->input_dev, KEY_POWER, 1); + input_sync(info->input_dev); + input_report_key(info->input_dev, KEY_POWER, 0); + input_sync(info->input_dev); + dev_err(&client->dev,"[bt541_touch_work]USE_WAKEUP_GESTURE---powerkey__end\n"); + + if (write_reg(client, 0x126, 0) != 0) + { + dev_err(&client->dev," gesture write reg error!!!\r\n"); + + ret=0; + } + } + } + goto out; + } +#elif defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + if((tpd_halt)&&(info->work_state == SUSPEND)) //suspend state support gesture + { + if (info->pm_suspend) { + ret = wait_for_completion_timeout( + &info->pm_completion, + msecs_to_jiffies(ZINITIX_TIMEOUT_COMERR_PM)); + if (!ret) { + dev_err(&client->dev, "Bus don't resume from pm(deep),timeout,skip irq"); + goto out; + } + } + + dev_info(&client->dev," gesture wakeup "); + if (read_data(client, 0x126,(u8 *)&gesture_flag, 2) < 0) //0x126 + { + dev_err(&client->dev," gesture read reg error!!!\r\n"); + ret=0; + /*eric add 20160920*/ + write_cmd(client, BT541_CLEAR_INT_STATUS_CMD); + } + else + { + /*wake up*/ + /*eric add 20160920*/ + write_cmd(client, BT541_CLEAR_INT_STATUS_CMD); + + /*1 single click*/ + if(gesture_flag==1) + { + dev_info(&client->dev,"%s: got single tap event\n", __func__); + if (info->imports && info->imports->report_gesture) { + struct gesture_event_data event; + + dev_info(&client->dev,"%s: report single tap gesture event\n", __func__); + event.evcode = 1; + /* call class method */ + ret = info->imports->report_gesture(&event); + } + } + /*1 double click*/ + else if (gesture_flag==2) { + dev_info(&client->dev,"%s: got double tap event\n", __func__); + if (info->imports && info->imports->report_gesture) { + struct gesture_event_data event; + + dev_info(&client->dev,"%s: report double tap gesture event\n", __func__); + event.evcode = 4; + /* call class method */ + ret = info->imports->report_gesture(&event); + } + } + if (!ret) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&info->gesture_wakelock, msecs_to_jiffies(3000)); +#else + PM_WAKEUP_EVENT(info->gesture_wakelock, 3000); +#endif + } + + if (write_reg(client, 0x126, 0) != 0) + { + dev_err(&client->dev," gesture write reg error!!!\r\n"); + ret=0; + } + } + goto out; + } +#endif + + if (info->work_state != NOTHING) { + dev_err(&client->dev, "%s: Other process occupied\n", __func__); + udelay(DELAY_FOR_SIGNAL_DELAY); + write_cmd(client, BT541_CLEAR_INT_STATUS_CMD); + goto out; + } + + info->work_state = NORMAL; + + if (ts_read_coord(info) == false ) + { + dev_err(&client->dev, "couldn't read touch_dev coord. read fail\r\n"); + + bt541_power_control(info, POWER_ON_SEQUENCE); + mini_init_touch(info); + goto out; + } + + if(write_cmd(client, BT541_CLEAR_INT_STATUS_CMD)!=0) + dev_err(&client->dev, "BT541_CLEAR_INT_STATUS_CMD error 11\n"); + + /* invalid : maybe periodical repeated int. */ + if (info->touch_info.status == 0x0 || info->touch_info.status == 0xffff) + { + dev_err(&client->dev, "periodical interrupt\r\n"); + + goto out; + } + +#ifdef SUPPORTED_TOUCH_KEY + reported = false; + + if (zinitix_bit_test(info->touch_info.status, BIT_ICON_EVENT)) { + if (read_data(info->client, BT541_ICON_STATUS_REG, + (u8 *)(&info->icon_event_reg), 2) < 0) { + dev_err(&client->dev, "Failed to read button info\n"); + write_cmd(client, BT541_CLEAR_INT_STATUS_CMD); + + goto out; + } + + for (i = 0; i < info->cap_info.button_num; i++) { + if (zinitix_bit_test(info->icon_event_reg, + (BIT_O_ICON0_DOWN + i))) { + info->button[i] = ICON_BUTTON_DOWN; + input_report_key(info->input_dev, BUTTON_MAPPING_KEY[i], 1); + reported = true; +#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP) + dev_info(&client->dev, "Button down = %d\n", i); +#else + dev_info(&client->dev, "Button down\n"); +#endif + } + } + + for (i = 0; i < info->cap_info.button_num; i++) { + if (zinitix_bit_test(info->icon_event_reg, + (BIT_O_ICON0_UP + i))) { + info->button[i] = ICON_BUTTON_UP; + input_report_key(info->input_dev, BUTTON_MAPPING_KEY[i], 0); + reported = true; +#if !defined(CONFIG_SAMSUNG_PRODUCT_SHIP) + dev_info(&client->dev, "Button up = %d\n", i); +#else + dev_info(&client->dev, "Button up\n"); +#endif + } + } + } + + /* if button press or up event occured... */ + if (reported == true || + !zinitix_bit_test(info->touch_info.status, BIT_PT_EXIST)) { + for (i = 0; i < info->cap_info.multi_fingers; i++) { + prev_sub_status = info->reported_touch_info.coord[i].sub_status; + if (zinitix_bit_test(prev_sub_status, SUB_BIT_EXIST)) { + //dev_info(&client->dev, "Finger [%02d] up ver0x%02x hw0x%02x mode0x%02x\n", + // i, info->cap_info.reg_data_version, + // info->cap_info.hw_id, m_optional_mode); + info->finger_cnt1--; + if (info->finger_cnt1 == 0) + input_report_key(info->input_dev, BTN_TOUCH, 0); + + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, + MT_TOOL_FINGER, 0); + } + } + memset(&info->reported_touch_info, 0x0, sizeof(struct point_info)); + input_sync(info->input_dev); + + if (reported == true) /* for button event */ + udelay(100); + + goto out; + } +#endif + + +#if SUPPORTED_PALM_TOUCH + if (zinitix_bit_test(info->touch_info.status, BIT_PALM)) { + /*Link modified here on 20190418 palm down sleep,no need detect the up event + if(zinitix_bit_test(info->touch_info.status, BIT_UP)) { + dev_err(&client->dev, "large touch palm enter\n"); + input_report_key(info->input_dev, KEY_POWER, 1); + input_sync(info->input_dev); + input_report_key(info->input_dev, KEY_POWER, 0); + input_sync(info->input_dev); + palm = 1; + goto out; + }*/ +/* + input_report_key(info->input_dev, KEY_LARGE, 1); + input_sync(info->input_dev); + input_report_key(info->input_dev, KEY_LARGE, 0); + input_sync(info->input_dev); +*/ + write_cmd(client, BT541_SWRESET_CMD); + msleep(20); + write_cmd(client, BT541_CLEAR_INT_STATUS_CMD); + dev_err(&client->dev, "large touch palm enter\n"); +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + input_report_key(info->input_dev, KEY_POWER, 1); + input_sync(info->input_dev); + input_report_key(info->input_dev, KEY_POWER, 0); + input_sync(info->input_dev); + palm = 1; +#endif + clear_report_data(info); + goto out; + } +#if 0 + if (zinitix_bit_test(info->touch_info.status, BIT_PALM_REJECT)) { + palm = 2; + dev_err(&client->dev, "large touch palm reject\n"); + } +#endif +#endif + + for (i = 0; i < info->cap_info.multi_fingers; i++) { + sub_status = info->touch_info.coord[i].sub_status; + prev_sub_status = info->reported_touch_info.coord[i].sub_status; + + if (zinitix_bit_test(sub_status, SUB_BIT_EXIST)) { + x = info->touch_info.coord[i].x; + y = info->touch_info.coord[i].y; + w = info->touch_info.coord[i].width; + + /* transformation from touch to screen orientation */ + if (pdata->orientation & TOUCH_V_FLIP) + y = info->cap_info.MaxY + + info->cap_info.MinY - y; + + if (pdata->orientation & TOUCH_H_FLIP) + x = info->cap_info.MaxX + + info->cap_info.MinX - x; + + maxX = info->cap_info.MaxX; + maxY = info->cap_info.MaxY; + + if (pdata->orientation & TOUCH_XY_SWAP) { + zinitix_swap_v(x, y, tmp); + zinitix_swap_v(maxX, maxY, tmp); + } + + if (x > maxX || y > maxY) { + dev_err(&client->dev, + "Invalid coord %d : x=%d, y=%d\n", i, x, y); + continue; + } + + info->touch_info.coord[i].x = x; + info->touch_info.coord[i].y = y; + + //dev_err(&client->dev, "Finger [%02d] x = %d, y = %d," + // " w = %d, p = %d\n", i, x, y, w, palm); + + if (zinitix_bit_test(sub_status, SUB_BIT_DOWN)){ + + dev_info(&client->dev, "Finger [%02d] down\n", i); + + info->finger_cnt1++; + } + + if (w == 0) + w = 1; + + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 1); + +#if (TOUCH_POINT_MODE == 2) + if (palm == 0) { + if (w >= PALM_REPORT_WIDTH) + w = PALM_REPORT_WIDTH - 10; + } else if (palm == 1) { //palm report + w = PALM_REPORT_WIDTH; +// info->touch_info.coord[i].minor_width = PALM_REPORT_WIDTH; + } else if (palm == 2){ // palm reject +// x = y = 0; + w = PALM_REJECT_WIDTH; +// info->touch_info.coord[i].minor_width = PALM_REJECT_WIDTH; + } +#endif + + input_report_abs(info->input_dev, ABS_MT_TOUCH_MAJOR, (u32)w); + input_report_abs(info->input_dev, ABS_MT_PRESSURE, (u32)w); + input_report_abs(info->input_dev, ABS_MT_WIDTH_MAJOR, + (u32)((palm == 1) ? w-40 : w)); +#if (TOUCH_POINT_MODE == 2) + input_report_abs(info->input_dev, + ABS_MT_TOUCH_MINOR, (u32)info->touch_info.coord[i].minor_width); +#endif + + input_report_abs(info->input_dev, ABS_MT_POSITION_X, x); + input_report_abs(info->input_dev, ABS_MT_POSITION_Y, y); + input_report_key(info->input_dev, BTN_TOUCH, 1); + } else if (zinitix_bit_test(sub_status, SUB_BIT_UP) || + zinitix_bit_test(prev_sub_status, SUB_BIT_EXIST)) { + dev_info(&client->dev, "Finger [%02d] up ver0x%02x hw0x%02x mode0x%02x\n", + i, info->cap_info.reg_data_version, + info->cap_info.hw_id, m_optional_mode); + info->finger_cnt1--; + if (info->finger_cnt1 == 0) + input_report_key(info->input_dev, BTN_TOUCH, 0); + + memset(&info->touch_info.coord[i], 0x0, sizeof(struct coord)); + input_mt_slot(info->input_dev, i); + input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER, 0); + + } else { + memset(&info->touch_info.coord[i], 0x0, sizeof(struct coord)); + } + } + memcpy((char *)&info->reported_touch_info, (char *)&info->touch_info, + sizeof(struct point_info)); + input_sync(info->input_dev); + +out: + + if (info->work_state == NORMAL) { +#if ESD_TIMER_INTERVAL + esd_timer_start(CHECK_ESD_TIMER, info); + +#endif + info->work_state = NOTHING; + } + + up(&info->work_lock); + + return IRQ_HANDLED; +} + +#if 0 +static int bt541_pinctrl_configure(struct bt541_ts_info *info, bool active) +{ + struct i2c_client *client = info->client; + struct pinctrl_state *set_state_tsp; + + int retval; + + dev_info(&client->dev,"%s: %s\n", __func__, active ? "ACTIVE" : "SUSPEND"); + + if (active) { + set_state_tsp = + pinctrl_lookup_state(info->pinctrl, + "tsp_en_gpio_active"); + if (IS_ERR(set_state_tsp)) { + dev_info(&client->dev,"%s: cannot get pinctrl(tsp_en_gpio) active state\n", __func__); + return PTR_ERR(set_state_tsp); + } + + } else { + set_state_tsp = + pinctrl_lookup_state(info->pinctrl, + "tsp_en_gpio_suspend"); + if (IS_ERR(set_state_tsp)) { + dev_info(&client->dev,"%s: cannot get pinctrl(tsp_en_gpio) sleep state\n", __func__); + return PTR_ERR(set_state_tsp); + } + } + + retval = pinctrl_select_state(info->pinctrl, set_state_tsp); + if (retval) { + dev_info(&client->dev,"%s: cannot set pinctrl %s state\n", + __func__, active ? "active" : "suspend"); + return retval; + } + + return 0; +} +#endif + +int zinitix_ts_mmi_disable_gesture(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bt541_ts_info *info = i2c_get_clientdata(client); + + down(&info->work_lock); + if (write_reg(client, BT541_GESTURE_CONTROL_REG, BT541_CLEAR_GESTURE_CMD) != I2C_SUCCESS) { + zinitix_printk("Disable gestures failed\n"); + up(&info->work_lock); + return -1; + } + up(&info->work_lock); + + return 0; +} + +int zinitix_ts_mmi_restore_gesture(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bt541_ts_info *info = i2c_get_clientdata(client); + + down(&info->work_lock); + if (write_reg(client, BT541_GESTURE_CONTROL_REG, info->gesture_command) != I2C_SUCCESS) { + zinitix_printk("Restore gestures failed\n"); + up(&info->work_lock); + return -1; + } + up(&info->work_lock); + + return 0; +} + +int zinitix_ts_mmi_gesture_suspend(struct device *dev) +{ + int i = 0; + struct i2c_client *client = to_i2c_client(dev); + struct bt541_ts_info *info = i2c_get_clientdata(client); + + down(&info->work_lock); + //software reset + for(i = 0; i < 3; i++) { + if (write_cmd(client, BT541_SWRESET_CMD) < 0) { + dev_err(&client->dev,"tpd_suspend fail to sft reset(%d)\r\n", i); + mdelay(10); + continue; + } else + break; + } + if (i == 3) { + up(&info->work_lock); + return -1; + } + msleep(20); + //enable irq wake + enable_irq_wake(info->irq); + info->irq_enabled = true; + //send enable easy wake gesture cmd + if (write_reg(client, 0x011D, info->gesture_command) != I2C_SUCCESS) { + zinitix_printk("easy_wakeup_gesture write_reg[0x0089] failed!!!-------------\n"); + up(&info->work_lock); + return -1; + } + //send idle cmd + for(i = 0; i < 3; i++) { + if (write_cmd(client, BT541_IDLE_CMD) < 0) { + dev_err(&client->dev,"tpd_suspend fail to send sleep cmd(%d)\r\n", i); + mdelay(10); + continue; + } else + break; + } + if (i == 3) { + up(&info->work_lock); + return -1; + } + up(&info->work_lock); + + return 0; +} + +int zinitix_ts_mmi_gesture_resume(struct device *dev) +{ + int err = 0; + struct i2c_client *client = to_i2c_client(dev); + struct bt541_ts_info *info = i2c_get_clientdata(client); + + down(&info->work_lock); + //disable irq wake + disable_irq_wake(info->irq); + + //reset touch IC when resume + zinitix_hw_reset(info,true); + mdelay(CHIP_ON_DELAY); + bt541_power_sequence(info); + err = mini_init_touch(info); + if(err < 0){ + zinitix_printk("resume_reset : zinitix_resume_proc error\n"); + up(&info->work_lock); + return -1; + } + up(&info->work_lock); + + return 0; +} + +#if !defined(CONFIG_INPUT_TOUCHSCREEN_MMI) +static int bt541_ts_resume(struct device *dev) +{ + int i = 0; + int err = 0; + struct i2c_client *client = to_i2c_client(dev); + struct bt541_ts_info *info = i2c_get_clientdata(client); + + zinitix_printk("[TSP]: %s\n", __func__); + + dev_err(&client->dev, "bt541_ts_resume start \n"); + + if(info->device_enabled) { + dev_err(&client->dev, "%s: already enabled\n", __func__); + return 0; + } + info->device_enabled = 1; + + down(&info->work_lock); + if (info->work_state != SUSPEND) { + dev_err(&client->dev, "%s: Invalid work proceedure (%d)\n", + __func__, info->work_state); + up(&info->work_lock); + + return 0; + } + info->work_state = RESUME; + + +#if 0 + bt541_power_control(info, POWER_ON_SEQUENCE); + + if (mini_init_touch(info) == false) + dev_err(&client->dev, "Failed to resume\n"); +#else + + for(i = 0; i < 3; i++) { + if (write_cmd(client, BT541_WAKEUP_CMD) < 0) { + zinitix_printk("tpd_resume fail to send wakeup_cmd(%d)\r\n", i); + mdelay(10); + continue; + } else + break; + } + + if (i == 3) { + bt541_power_control(info, POWER_ON_SEQUENCE); + err = mini_init_touch(info); + if(err < 0){ + zinitix_printk("resume_reset : zinitix_resume_proc error\n"); + } + goto reset_exit; + } + + write_cmd(client ,BT541_SWRESET_CMD); + msleep(20); + for(i = 0; i < 3; i++) { + if (write_cmd(client, BT541_CLEAR_INT_STATUS_CMD) < 0) { + zinitix_printk("tpd_resume fail to send wakeup_cmd(%d)\r\n", i); + mdelay(10); + continue; + } else + break; + } + + + #if ESD_TIMER_INTERVAL + esd_timer_start(CHECK_ESD_TIMER, info); + #endif + +reset_exit: +#endif + + info->work_state = NOTHING; + +#if !USE_WAKEUP_GESTURE/*eric add 20170208*/ + enable_irq(info->irq); + info->irq_enabled = true; +#endif + + if (easy_wake) { + if (write_reg(client, 0x011D, 0x0089) != I2C_SUCCESS) + zinitix_printk("[resume]easy_wakeup_gesture write_reg[0x0089] failed!!!-----------------------\n"); + } + else { + if (write_reg(client, 0x011D, 0x0080) != I2C_SUCCESS) + zinitix_printk("[resume]easy_wakeup_gesture write_reg[0x0080] failed!!!-----------------------\n"); + } + + if (big_object) { + if (write_reg(client, 0x0017, 0x8A08) != I2C_SUCCESS) + zinitix_printk("[resume]bigobject_off write_reg[0x8A08] failed!!!-----------------------\n"); + } + else { + if (write_reg(client, 0x0017, 0x8A00) != I2C_SUCCESS) + zinitix_printk("[resume]bigobject_off write_reg[0x8A00] failed!!!-----------------------\n"); + } + + up(&info->work_lock); + dev_err(&client->dev, "bt541_ts_resume end \n"); + + tpd_halt = 0; + + return 0; +} + +static int bt541_ts_suspend(struct device *dev) +{ + int i =0; + struct i2c_client *client = to_i2c_client(dev); + struct bt541_ts_info *info = i2c_get_clientdata(client); + + zinitix_printk("[TSP]: %s\n", __func__); + tpd_halt = 1; + + dev_err(&client->dev, "bt541_ts_suspend 11\n"); + +#if !USE_WAKEUP_GESTURE/*eric add 20170208*/ + disable_irq(info->irq); + info->irq_enabled = false; +#else + disable_irq_wake(info->irq); + enable_irq_wake(info->irq); + info->irq_enabled = true; +#endif + + if(!info->device_enabled) { + dev_err(&client->dev, "%s: already disabled\n", __func__); + return 0; + } + info->device_enabled = 0; + +#if ESD_TIMER_INTERVAL + flush_work(&info->tmr_work); +#endif + + down(&info->work_lock); + if (info->work_state != NOTHING + && info->work_state != SUSPEND) { + dev_err(&client->dev, "%s: Invalid work proceedure (%d)\n", + __func__, info->work_state); + up(&info->work_lock); + #if !USE_WAKEUP_GESTURE/*eric add 20170208*/ + enable_irq(info->irq); + info->irq_enabled = true; + #endif + return 0; + } + + clear_report_data(info); + +#if ESD_TIMER_INTERVAL + esd_timer_stop(info); +#endif + +#if 0 + write_cmd(info->client, BT541_IDLE_CMD); + bt541_power_control(info, POWER_OFF); + + gpio_direction_output(info->pdata->gpio_reset, 0);//????????? +#else +/*eric start */ + //write_cmd(client, 0x000a); + //mdelay(10); + + for(i = 0; i < 3; i++) { + if (write_cmd(client, BT541_CLEAR_INT_STATUS_CMD) < 0) { + dev_err(&client->dev,"tpd_suspend fail to clear int(%d)\r\n", i); + mdelay(10); + continue; + } else + break; + } + + for(i = 0; i < 3; i++) { + if (write_cmd(client, BT541_IDLE_CMD) < 0) { + dev_err(&client->dev,"tpd_suspend fail to send sleep cmd(%d)\r\n", i); + mdelay(10); + continue; + } else + break; + } + //write_cmd(client, 0x000b); + //mdelay(10); +#endif + + info->work_state = SUSPEND; + + + up(&info->work_lock); + + dev_err(&client->dev, "bt541_ts_suspend end \n"); + + return 0; +} + +#if defined(CONFIG_FB) +static int fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct fb_event *evdata = data; + int *blank; + + if (evdata && evdata->data && event == FB_EVENT_BLANK && + misc_touch_dev && misc_touch_dev->client) { + blank = evdata->data; + if (*blank == FB_BLANK_UNBLANK) + bt541_ts_resume(&misc_touch_dev->client->dev); + else if (*blank == FB_BLANK_POWERDOWN) + bt541_ts_suspend(&misc_touch_dev->client->dev); + } + + return 0; +} +#elif defined(CONFIG_HAS_EARLYSUSPEND) +static void zinitix_late_resume(struct early_suspend *h) +{ + dev_err(&client->dev, "zinitix_late_resume\n"); + + bt541_ts_resume(&misc_touch_dev->client->dev); + dev_err(&client->dev, "zinitix_late_resume end\n"); +} + + +static void zinitix_early_suspend(struct early_suspend *h) +{ + dev_err(&client->dev, "zinitix_early_suspend\n"); + bt541_ts_suspend(&misc_touch_dev->client->dev); + dev_err(&client->dev, "zinitix_early_suspend end\n"); +} + + +#endif /* CONFIG_FB */ +#else +static int bt541_ts_resume(struct device *dev) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + + zinitix_printk("system resumes from pm_suspend"); + info->pm_suspend = false; + complete(&info->pm_completion); + return 0; +} + +static int bt541_ts_suspend(struct device *dev) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + + zinitix_printk("system enters into pm_suspend"); + info->pm_suspend = true; + reinit_completion(&info->pm_completion); + return 0; +} +#endif /* TOUCHSCREEN_MMI */ + +static bool ts_set_touchmode(u16 value) +{ + int i; + + if (misc_info->irq_enabled == true) { + disable_irq(misc_info->irq); + misc_info->irq_enabled = false; + zinitix_printk("Disable irq.\n"); + } + + down(&misc_info->work_lock); + if (misc_info->work_state != NOTHING) { + printk(KERN_INFO "other process occupied.. (%d)\n", + misc_info->work_state); + enable_irq(misc_info->irq); + misc_info->irq_enabled = true; + up(&misc_info->work_lock); + return -1; + } + + misc_info->work_state = SET_MODE; + + if (value == TOUCH_DND_MODE) { + if (write_reg(misc_info->client, BT541_DND_N_COUNT, + SEC_DND_N_COUNT) != I2C_SUCCESS) + printk(KERN_INFO "[zinitix_touch] TEST Mode : " + "Fail to set BT541_DND_N_COUNT %d.\n", SEC_DND_N_COUNT); + if (write_reg(misc_info->client, BT541_DND_U_COUNT, + SEC_DND_U_COUNT) != I2C_SUCCESS) + printk(KERN_INFO "[zinitix_touch] TEST Mode : " + "Fail to set BT541_DND_U_COUNT %d.\n", SEC_DND_U_COUNT); + if (write_reg(misc_info->client, BT541_AFE_FREQUENCY, + SEC_DND_FREQUENCY) != I2C_SUCCESS) + printk(KERN_INFO "[zinitix_touch] TEST Mode : " + "Fail to set BT541_AFE_FREQUENCY %d.\n", SEC_DND_FREQUENCY); + } + if (value == TOUCH_PDND_MODE) { + if (write_reg(misc_info->client, BT541_DND_N_COUNT, + SEC_PDND_N_COUNT) != I2C_SUCCESS) + printk(KERN_INFO "[zinitix_touch] TEST Mode : " + "Fail to set BT541_DND_N_COUNT %d.\n", SEC_PDND_N_COUNT); + if (write_reg(misc_info->client, BT541_DND_U_COUNT, + SEC_PDND_U_COUNT) != I2C_SUCCESS) + printk(KERN_INFO "[zinitix_touch] TEST Mode : " + "Fail to set BT541_DND_U_COUNT %d.\n", SEC_PDND_U_COUNT); + if (write_reg(misc_info->client, BT541_AFE_FREQUENCY, + SEC_PDND_FREQUENCY) != I2C_SUCCESS) + printk(KERN_INFO "[zinitix_touch] TEST Mode : " + "Fail to set BT541_AFE_FREQUENCY %d.\n", SEC_PDND_FREQUENCY); + } + else if(misc_info->touch_mode == TOUCH_DND_MODE || misc_info->touch_mode == TOUCH_PDND_MODE) { + if (write_reg(misc_info->client, BT541_DND_N_COUNT, + misc_info->cap_info.N_cnt) != I2C_SUCCESS) + printk(KERN_INFO "[zinitix_touch] TEST Mode : " + "Fail to reset BT541_AFE_FREQUENCY %d.\n", + misc_info->cap_info.N_cnt); + if (write_reg(misc_info->client, BT541_DND_U_COUNT, + misc_info->cap_info.u_cnt) != I2C_SUCCESS) + printk(KERN_INFO "[zinitix_touch] TEST Mode : " + "Fail to reset BT541_DND_U_COUNT %d.\n", + misc_info->cap_info.u_cnt); + if (write_reg(misc_info->client, BT541_AFE_FREQUENCY, + misc_info->cap_info.afe_frequency) != I2C_SUCCESS) + printk(KERN_INFO "[zinitix_touch] TEST Mode : " + "Fail to reset BT541_AFE_FREQUENCY %d.\n", + misc_info->cap_info.afe_frequency); + } + + if (value == TOUCH_SEC_MODE) + misc_info->touch_mode = TOUCH_POINT_MODE; + else + misc_info->touch_mode = value; + + printk(KERN_INFO "[zinitix_touch] tsp_set_testmode, " + "touchkey_testmode = %d\r\n", misc_info->touch_mode); + + if (misc_info->touch_mode != TOUCH_POINT_MODE) { + if (write_reg(misc_info->client, BT541_DELAY_RAW_FOR_HOST, + RAWDATA_DELAY_FOR_HOST) != I2C_SUCCESS) + zinitix_printk("Fail to set BT541_DELAY_RAW_FOR_HOST.\r\n"); + } + + if (write_reg(misc_info->client, BT541_TOUCH_MODE, + misc_info->touch_mode) != I2C_SUCCESS) + printk(KERN_INFO "[zinitix_touch] TEST Mode : " + "Fail to set ZINITX_TOUCH_MODE %d.\r\n", misc_info->touch_mode); + + if (write_cmd(misc_info->client, BT541_SWRESET_CMD) != I2C_SUCCESS) { + zinitix_printk("Failed to write reset command\n"); + } + mdelay(400); + + zinitix_printk("clear garbage data\n"); + /* clear garbage data */ + for (i = 0; i < 10; i++) { + mdelay(20); + write_cmd(misc_info->client, BT541_CLEAR_INT_STATUS_CMD); + } + + if (misc_info->touch_mode == TOUCH_POINT_MODE) { + if (misc_info->irq_enabled == false) { + enable_irq(misc_info->irq); + misc_info->irq_enabled = true; + zinitix_printk("Enable irq.\n"); + } + } + misc_info->work_state = NOTHING; + up(&misc_info->work_lock); + zinitix_printk("Set touch mode to %d success.\n", misc_info->touch_mode); + return 1; +} + +int ts_upgrade_sequence(const u8 *firmware_data) +{ + disable_irq(misc_info->irq); + misc_info->irq_enabled = false; + down(&misc_info->work_lock); + misc_info->work_state = UPGRADE; + +#if ESD_TIMER_INTERVAL + esd_timer_stop(misc_info); +#endif + zinitix_debug_msg("clear all reported points\r\n"); + clear_report_data(misc_info); + + printk(KERN_INFO "start upgrade firmware\n"); + if (ts_upgrade_firmware(misc_info, + firmware_data, + misc_info->cap_info.ic_fw_size) == false) { + enable_irq(misc_info->irq); + misc_info->irq_enabled = true; + misc_info->work_state = NOTHING; + up(&misc_info->work_lock); + return -1; + } + + if (init_touch(misc_info) == false) { + enable_irq(misc_info->irq); + misc_info->irq_enabled = true; + misc_info->work_state = NOTHING; + up(&misc_info->work_lock); + return -1; + } + +#if ESD_TIMER_INTERVAL + esd_timer_start(CHECK_ESD_TIMER, misc_info); +#if defined(TSP_VERBOSE_DEBUG) + dev_info(&misc_info->client->dev, "Started esd timer\n"); +#endif +#endif + + enable_irq(misc_info->irq); + misc_info->irq_enabled = true; + misc_info->work_state = NOTHING; + up(&misc_info->work_lock); + return 0; +} + +#ifdef SEC_FACTORY_TEST +static inline void set_cmd_result(struct bt541_ts_info *info, char *buff, int len) +{ + strncat(info->factory_info->cmd_result, buff, len); +} + +static inline void set_default_result(struct bt541_ts_info *info) +{ + char delim = ':'; + memset(info->factory_info->cmd_result, 0x00, ARRAY_SIZE(info->factory_info->cmd_result)); + memcpy(info->factory_info->cmd_result, info->factory_info->cmd, strlen(info->factory_info->cmd)); + strncat(info->factory_info->cmd_result, &delim, 1); +} + +static int zinitix_read_file_request_firmware(char *file_name, u8 **file_buf) +{ + int ret = 0; + const struct firmware *fw = NULL; + char fwname[FILE_NAME_LENGTH] = { 0 }; + + snprintf(fwname, FILE_NAME_LENGTH, "%s", file_name); + ret = request_firmware(&fw, fwname, &misc_touch_dev->client->dev); + if (0 == ret) { + zinitix_printk("firmware(%s) request successfully\n", fwname); + *file_buf = vmalloc(fw->size); + if (NULL == *file_buf) { + zinitix_printk("fw buffer vmalloc fail\n"); + ret = -ENOMEM; + } else { + memcpy(*file_buf, fw->data, fw->size); + ret = fw->size; + } + } else { + zinitix_printk("firmware(%s) request fail,ret=%d\n", fwname, ret); + ret = -EIO; + } + + if (fw != NULL) { + release_firmware(fw); + fw = NULL; + } + + return ret; +} + +int zinitix_read_file(char *file_name, u8 **file_buf) +{ + int ret = 0; + int count = 0; + +retry: + ret = zinitix_read_file_request_firmware(file_name, file_buf); + if (ret < 0) { + zinitix_printk("get fw file(default) fail: count = %d\n", count); + if (++count < 3) + goto retry; + } + + return ret; +} + +#define MAX_FW_PATH 255 +#define TSP_FW_FILENAME "zinitix_fw.bin" + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +static void fw_update(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + struct i2c_client *client = info->client; + int ret = 0; + char result[16] = {0}; + u8 *fw_file_buf = NULL; + + set_default_result(info); + + switch (info->factory_info->cmd_param[0]) { + case BUILT_IN: + ts_select_type_hw(info); + ret = ts_upgrade_sequence((u8*)m_pFirmware[m_FirmwareIdx]); + if(ret<0) { + info->factory_info->cmd_state = 3; + return; + } + break; + + case UMS: + ret= zinitix_read_file(TSP_FW_FILENAME, &fw_file_buf); + if (ret != info->cap_info.ic_fw_size) { + dev_err(&client->dev, "invalid fw size!!\n"); + goto error_upgrade; + } + + dev_info(&client->dev, "ums fw is loaded!!\n"); + info->checkUMSmode = true; + ret = ts_upgrade_sequence((u8 *)fw_file_buf); + info->checkUMSmode = false; + if(ret<0) { + dev_err(&client->dev, "fw upgrade fail!!\n"); + goto error_upgrade; + } + break; + + default: + dev_err(&client->dev, "invalid fw file type!!\n"); + goto error_upgrade; + } + + if (fw_file_buf) { + vfree(fw_file_buf); + fw_file_buf = NULL; + } + info->factory_info->cmd_state = 2; + snprintf(result, sizeof(result) , "%s", "OK"); + set_cmd_result(info, result, + strnlen(result, sizeof(result))); + return; + +error_upgrade: + if (fw_file_buf) { + vfree(fw_file_buf); + fw_file_buf = NULL; + } + info->factory_info->cmd_state = 3; + snprintf(result, sizeof(result) , "%s", "NG"); + set_cmd_result(info, result, strnlen(result, sizeof(result))); + + return; +} +#else +static void fw_update(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + struct i2c_client *client = info->client; + int ret = 0; + const u8 *buff = 0; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + mm_segment_t old_fs = {0}; +#endif + struct file *fp = NULL; + long fsize = 0, nread = 0; + char fw_path[MAX_FW_PATH + 1]; + char result[16] = {0}; + + set_default_result(info); + + switch (info->factory_info->cmd_param[0]) { + case BUILT_IN: + ts_select_type_hw(info); + ret = ts_upgrade_sequence((u8*)m_pFirmware[m_FirmwareIdx]); + if(ret<0) { + info->factory_info->cmd_state = 3; + return; + } + break; + + case UMS: +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + old_fs = get_fs(); + //set_fs(get_ds()); + set_fs(KERNEL_DS); +#endif + snprintf(fw_path, MAX_FW_PATH, "/vendor/firmware/%s", TSP_FW_FILENAME); + fp = filp_open(fw_path, O_RDONLY, 0); + if (IS_ERR(fp)) { + dev_err(&client->dev, + "file %s open \n", fw_path); + info->factory_info->cmd_state = 3; + goto err_open; + } + + fsize = fp->f_path.dentry->d_inode->i_size; + + if (fsize != info->cap_info.ic_fw_size) { + dev_err(&client->dev, "invalid fw size!!\n"); + info->factory_info->cmd_state = 3; + goto err_open; + } + + buff = kzalloc((size_t)fsize, GFP_KERNEL); + if (!buff) { + dev_err(&client->dev, "failed to alloc buffer for fw\n"); + info->factory_info->cmd_state = 3; + goto err_alloc; + } + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + nread = vfs_read(fp, (char __user *)buff, fsize, &fp->f_pos); +#else + nread = kernel_read(fp, (char __user *)buff, fsize, &fp->f_pos); +#endif + if (nread != fsize) { + info->factory_info->cmd_state = 3; + goto err_fw_size; + } + + filp_close(fp, current->files); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + set_fs(old_fs); +#endif + dev_info(&client->dev, "ums fw is loaded!!\n"); + info->checkUMSmode = true; + ret = ts_upgrade_sequence((u8 *)buff); + info->checkUMSmode = false; + if(ret<0) { + kfree(buff); + info->factory_info->cmd_state = 3; + return; + } + break; + + default: + dev_err(&client->dev, "invalid fw file type!!\n"); + goto not_support; + } + + info->factory_info->cmd_state = 2; + snprintf(result, sizeof(result) , "%s", "OK"); + set_cmd_result(info, result, + strnlen(result, sizeof(result))); + if (buff) { + kfree(buff); + buff = NULL; + } + return; + +if (fp != NULL) { +err_fw_size: + kfree(buff); +err_alloc: + filp_close(fp, NULL); +err_open: +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + set_fs(old_fs); +#else + ; +#endif +} +not_support: + snprintf(result, sizeof(result) , "%s", "NG"); + set_cmd_result(info, result, strnlen(result, sizeof(result))); + return; +} +#endif + +static void get_fw_ver_bin(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + //struct i2c_client *client = info->client; + struct tsp_factory_info *finfo = info->factory_info; + u16 fw_version, fw_minor_version, reg_version, hw_id; + u32 version; + u8 *firmware_data; + + set_default_result(info); + + /* To Do */ + /* modify m_firmware_data */ + ts_select_type_hw(info); + firmware_data = (u8*)m_pFirmware[m_FirmwareIdx]; + + fw_version = (u16)(firmware_data[52] | (firmware_data[53] << 8)); + fw_minor_version = (u16)(firmware_data[56] | (firmware_data[57] << 8)); + reg_version = (u16)(firmware_data[60] | (firmware_data[61] << 8)); + hw_id = (u16)(firmware_data[0x7528] | (firmware_data[0x7529] << 8)); + version = (u32)((u32)(hw_id & 0xff) << 16) | ((fw_version & 0xf ) << 12) + | ((fw_minor_version & 0xf) << 8) | (reg_version & 0xff); + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "ZI%06X", version); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + finfo->cmd_state = OK; + + //dev_info(&client->dev, "%s: %s\n", __func__, finfo->cmd_buff, + // strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + + return; +} + +static void get_fw_ver_ic(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + //struct i2c_client *client = info->client; + struct tsp_factory_info *finfo = info->factory_info; + u16 fw_version, fw_minor_version, reg_version, hw_id; + u32 version; + + set_default_result(info); + + fw_version = info->cap_info.fw_version; + fw_minor_version = info->cap_info.fw_minor_version; + reg_version = info->cap_info.reg_data_version; + hw_id = info->cap_info.hw_id; + version = (u32)((u32)(hw_id & 0xff) << 16) | ((fw_version & 0xf) << 12) + | ((fw_minor_version & 0xf) << 8) | (reg_version & 0xff); + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "ZI%06X", version); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + finfo->cmd_state = OK; + + //dev_info(&client->dev, "%s: %s(%d)\n", __func__, finfo->cmd_buff, + // strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + + return; +} + +static void get_threshold(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + //struct i2c_client *client = info->client; + struct tsp_factory_info *finfo = info->factory_info; + + set_default_result(info); + + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), + "%d", info->cap_info.threshold); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + finfo->cmd_state = OK; + + //dev_info(&client->dev, "%s: %s(%d)\n", __func__, finfo->cmd_buff, + // strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + + return; +} + +static void module_off_master(void *device_data) +{ + return; +} + +static void module_on_master(void *device_data) +{ + return; +} + +static void module_off_slave(void *device_data) +{ + return; +} + +static void module_on_slave(void *device_data) +{ + return; +} + +static void get_module_vendor(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + struct i2c_client *client = info->client; + struct tsp_factory_info *fdata = info->factory_info; + char buff[16] = {0}; + int val,val2; + + set_default_result(info); + if (!(gpio_get_value(info->pdata->tsp_en_gpio)) ) { + dev_err(&client->dev, "%s: [ERROR] Touch is stopped\n", + __func__); + snprintf(buff, sizeof(buff), "%s", "TSP turned off"); + set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); + fdata->cmd_state = NOT_APPLICABLE; + return; + } + if (info->pdata->tsp_vendor1 > 0 && info->pdata->tsp_vendor2 > 0 ) { + val = gpio_get_value(info->pdata->tsp_vendor1); + val2 = gpio_get_value(info->pdata->tsp_vendor2); + dev_info(&info->client->dev, + "%s: TSP_ID: %d[%d]%d[%d]\n", __func__, + info->pdata->tsp_vendor1, val,info->pdata->tsp_vendor2, val2); + snprintf(buff, sizeof(buff), "%s,%d%d", tostring(OK), val,val2); + fdata->cmd_state = OK; + set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); + return; + } + snprintf(buff, sizeof(buff), "%s", tostring(NG)); + fdata->cmd_state = FAIL; + set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); +} + + +#define BT541_VENDOR_NAME "zinitix" + +static void get_chip_vendor(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + //struct i2c_client *client = info->client; + struct tsp_factory_info *finfo = info->factory_info; + + set_default_result(info); + + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), + "%s", BT541_VENDOR_NAME); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + finfo->cmd_state = OK; + + //dev_info(&client->dev, "%s: %s(%d)\n", __func__, finfo->cmd_buff, + // strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + + return; +} + +static void get_config_ver(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + //struct i2c_client *client = info->client; + struct tsp_factory_info *finfo = info->factory_info; + const char *model_name = info->pdata->pname; + + set_default_result(info); + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff),"%s_ZI_%s", model_name, CONFIG_DATE); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + finfo->cmd_state = OK; + + //dev_info(&client->dev, "%s: %s(%d)\n", __func__, finfo->cmd_buff, + // strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + + return; +} + +#define BT541_CHIP_NAME "BT541" + +static void get_chip_name(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + //struct i2c_client *client = info->client; + struct tsp_factory_info *finfo = info->factory_info; + + set_default_result(info); + + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "%s", BT541_CHIP_NAME); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + finfo->cmd_state = OK; + + //dev_info(&client->dev, "%s: %s(%d)\n", __func__, finfo->cmd_buff, + // strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + + return; +} + +static void get_x_num(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + //struct i2c_client *client = info->client; + struct tsp_factory_info *finfo = info->factory_info; + + set_default_result(info); + + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), + "%u", info->cap_info.x_node_num); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + finfo->cmd_state = OK; + + //dev_info(&client->dev, "%s: %s(%d)\n", __func__, finfo->cmd_buff, + // strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + + return; +} + +static void get_y_num(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + //struct i2c_client *client = info->client; + struct tsp_factory_info *finfo = info->factory_info; + + set_default_result(info); + + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), + "%u", info->cap_info.y_node_num); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + finfo->cmd_state = OK; + + //dev_info(&client->dev, "%s: %s(%d)\n", __func__, finfo->cmd_buff, + // strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + + return; +} + +static void not_support_cmd(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + //struct i2c_client *client = info->client; + struct tsp_factory_info *finfo = info->factory_info; + + set_default_result(info); + + sprintf(finfo->cmd_buff, "%s", "NA"); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + + mutex_lock(&finfo->cmd_lock); + finfo->cmd_is_running = false; + mutex_unlock(&finfo->cmd_lock); + info->factory_info->cmd_state = WAITING; + + //dev_info(&client->dev, "%s: \"%s(%d)\"\n", __func__, finfo->cmd_buff, + // strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + + return; +} + +static void get_reference(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + //struct i2c_client *client = info->client; + struct tsp_factory_info *finfo = info->factory_info; + struct tsp_raw_data *raw_data = info->raw_data; + unsigned int val; + int x_node, y_node; + int node_num; + + set_default_result(info); + + x_node = finfo->cmd_param[0]; + y_node = finfo->cmd_param[1]; + + if (x_node < 0 || x_node >= info->cap_info.x_node_num || + y_node < 0 || y_node >= info->cap_info.y_node_num) { + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "%s", "abnormal"); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + info->factory_info->cmd_state = FAIL; + return; + } + + node_num = x_node * info->cap_info.y_node_num + y_node; + + val = raw_data->ref_data[node_num]; + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "%u", val); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + finfo->cmd_state = OK; + + //dev_info(&client->dev, "%s: %s(%d)\n", __func__, finfo->cmd_buff, + // strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + + return; +} + +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 == NULL){ + zinitix_printk("get space kzalloc error\n"); + 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 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 void goto_next_line(char **ptr) +{ + do { + *ptr = *ptr + 1; + } while (**ptr != '\n' && **ptr != '\0'); + if (**ptr == '\0') { + return; + } + *ptr = *ptr + 1; +} + +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) { + zinitix_printk("ptr is NULL\n"); + return -EINVAL; + } + if (!data) { + zinitix_printk("data is NULL\n"); + return -EINVAL; + } + + row_data = (char *)kzalloc(MAX_LINE_LEN, GFP_KERNEL); + if (!row_data) { + zinitix_printk("alloc bytes %d failed.\n", 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 || (0 == strlen(ptr)) || (ptr >= (buf_start + buf_size))) { + zinitix_printk("invalid ptr, return\n"); + 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) { + zinitix_printk("load %s failed 1, maybe not this item\n", target_name); + return -EINTR; + } + + goto_next_line(&ptr); + if (!ptr || (0 == strlen(ptr))) { + zinitix_printk("load %s failed 2!\n", target_name); + return -EIO; + } + + if (data) { + ret = parse_valid_data(buf, size, ptr, data, rows); + } else { + zinitix_printk("load %s failed 3!\n", target_name); + return -EINTR; + } + } else { + zinitix_printk("ret=%d, read_ret=%d\n", ret, read_ret); + ret = -ENXIO; + } + + return ret; +} + +static void zinitix_cmcp_parse_threshold_file(const struct firmware *fw, + void *context) +{ + struct device *dev = context; + struct bt541_ts_info *info = dev_get_drvdata(dev); + struct ts_test_params *test_params = &info->test_params; + char *temp_buf = NULL; + int ret; + u32 tx = 0; + u32 rx = 1; + int i; + + if (!fw) { + dev_err(dev, "%s: No builtin cmcp threshold file\n", __func__); + goto exit_free; + } + + if (!fw->data || !fw->size) { + dev_err(dev, "%s: Invalid builtin cmcp threshold file\n", + __func__); + goto exit_free; + } + zinitix_printk("Found cmcp threshold file.\n"); + + temp_buf = kzalloc(fw->size + 1, GFP_KERNEL); + if (!temp_buf) { + dev_err(dev, "%s: kzalloc bytes failed.", __func__); + goto exit_free; + } + memcpy(temp_buf, fw->data, fw->size); + + /* obtain raw data min */ + ret = parse_csvfile(temp_buf, fw->size, CSV_TP_RAW_DATA_MIN, + test_params->min_raw_limits, rx, tx); + if (ret < 0) { + zinitix_printk("Failed get min_raw_limits\n"); + goto exit_free; + } else { + for (i = 0; i < ret; i++) { + dev_dbg(dev, "zinitix raw_data_min[%d] = %d\n", i, test_params->min_raw_limits[i]); + } + zinitix_printk("parse_csvfile %s OK, ret = %d\n", CSV_TP_RAW_DATA_MIN, ret); + } + + /* obtain raw data max */ + ret = parse_csvfile(temp_buf, fw->size, CSV_TP_RAW_DATA_MAX, + test_params->max_raw_limits, rx, tx); + if (ret < 0) { + zinitix_printk("Failed get max_raw_limits\n"); + goto exit_free; + } else { + for (i = 0; i < ret; i++) { + dev_dbg(dev, "zinitix raw_data_max[%d] = %d\n", i, test_params->max_raw_limits[i]); + } + zinitix_printk("parse_csvfile %s OK, ret = %d\n", CSV_TP_RAW_DATA_MAX, ret); + } + + /* obtain short data threshold */ + ret = parse_csvfile(temp_buf, fw->size, CSV_TP_SHORT_DATA_THRESHOLD, + &test_params->short_threshold, rx, tx); + if (ret < 0) { + zinitix_printk("Failed get short data threshold\n"); + goto exit_free; + } else { + for (i = 0; i < ret; i++) { + dev_info(dev, "zinitix short_data_threshold = %d\n", test_params->short_threshold); + } + zinitix_printk("parse_csvfile %s OK, ret = %d\n", CSV_TP_SHORT_DATA_THRESHOLD, ret); + } + + info->builtin_cmcp_threshold_status = 0; + complete(&info->builtin_cmcp_threshold_complete); + return; + +exit_free: + if (!temp_buf) + kfree(temp_buf); + if (fw) + release_firmware(fw); + + info->builtin_cmcp_threshold_status = -EINVAL; + complete(&info->builtin_cmcp_threshold_complete); + + return; +} + +static void zinitix_parse_cmcp_threshold_builtin( + struct work_struct *cmcp_threshold_update) +{ + int retval; + char limit_file[100] = {0}; + struct bt541_ts_info *info = + container_of(cmcp_threshold_update, + struct bt541_ts_info, + cmcp_threshold_update); + struct device *dev = &info->client->dev; + + if (info->supplier) + snprintf(limit_file, sizeof(limit_file), "%s_%s_test_limits.csv", + info->supplier, BT541_VENDOR_NAME); + else + snprintf(limit_file, sizeof(limit_file), "%s", + ZINITIX_THRESHOLD_FILE_NAME); + dev_info(dev, "limit_file_name:%s", limit_file); + + /* Open threshold file */ + retval = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + limit_file, dev, GFP_KERNEL, dev, + zinitix_cmcp_parse_threshold_file); + if (retval < 0) { + dev_err(dev, "%s: Failed loading cmcp threshold file\n", + __func__); + goto exit; + } + + /* wait until cmcp threshold upgrade finishes */ + wait_for_completion(&info->builtin_cmcp_threshold_complete); + + retval = info->builtin_cmcp_threshold_status; + +exit: + return; +} + +static void run_preference_read(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + //struct i2c_client *client = info->client; + struct tsp_factory_info *finfo = info->factory_info; + struct tsp_raw_data *raw_data = info->raw_data; + //u16 min, max; + s32 i; + + set_default_result(info); + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + s16 min[64] = {163,905,2592,2982,3037,2707,1498,397, +911,2772,3006,2830,2733,2960,3189,1243, +2531,2750,3009,2824,2692,2911,3387,2790, +2839,2758,3016,2827,2687,2907,3454,3203, +2939,2878,3143,2960,2692,2911,3508,3413, +2492,2863,3134,2962,2682,2911,3495,3241, +594,3116,3205,3056,2682,2954,3352,928, +121,797,3163,3474,2842,2420,874,119, + }; + s16 max[64] = {303,1681,4814,5538,5639,5027,2782,737, +1691,5148,5582,5256,5075,5498,5923,2309, +4701,5106,5587,5244,5000,5405,6291,5182, +5272,5122,5602,5249,4989,5399,6414,5949, +5457,5346,5837,5496,5000,5407,6516,6339, +4628,5317,5820,5502,4982,5405,6491,6019, +1104,5786,5953,5676,4980,5486,6226,1724, +225,1479,5873,6452,5278,4494,1624,221, + }; +#else + if (info->builtin_cmcp_threshold_status < 0) { + zinitix_printk("No cmcp threshold file.\n"); + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "%s", "NG"); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + finfo->cmd_state = FAIL; + return; + } +#endif + + ts_set_touchmode(TOUCH_PDND_MODE); + get_raw_data(info, (u8 *)raw_data->pref_data, 10); + ts_set_touchmode(TOUCH_POINT_MODE); + + zinitix_printk("==========raw_data start===============\n"); + for(i = 0; i < (info->cap_info.x_node_num * info->cap_info.y_node_num); i++) + { + printk("zinitix raw_data[%d] = %d\n",i,raw_data->pref_data[i]); + } + zinitix_printk("==========raw_data end===============\n"); + + for(i = 0; i < (info->cap_info.x_node_num * info->cap_info.y_node_num); i++) + { +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + if(((raw_data->pref_data[i]) < info->test_params.min_raw_limits[i]) || + ((raw_data->pref_data[i]) > info->test_params.max_raw_limits[i])) +#else + if(((raw_data->pref_data[i]) < min[i]) || ((raw_data->pref_data[i]) > max[i])) +#endif + { + printk("zinitix fail raw_data[%d] = %d\n",i,raw_data->pref_data[i]); + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "%s", "NG"); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + finfo->cmd_state = FAIL; + info->test_params.raw_data_pass = 0; + return; + }else + { + continue; + + } + + } + printk("zinitix Pass raw_data\n"); + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "%s", "Pass"); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + finfo->cmd_state = OK; + info->test_params.raw_data_pass = 1; + + return; + + + /* + min = 0xFFFF; + max = 0x0000; + + for(i = 0; i < info->cap_info.x_node_num; i++) + { + for(j = 0; j < info->cap_info.y_node_num; j++) + { + pr_info("pref_data : %d ", + raw_data->pref_data[i * info->cap_info.y_node_num + j]);*/ + /* + if (raw_data->pref_data[i * info->cap_info.y_node_num + j] < min && + raw_data->pref_data[i * info->cap_info.y_node_num + j] != 0) + min = raw_data->pref_data[i * info->cap_info.y_node_num + j]; + + if (raw_data->pref_data[i * info->cap_info.y_node_num + j] > max) + max = raw_data->pref_data[i * info->cap_info.y_node_num + j]; + + } + //pr_info("\n"); + } + *//* + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "%d,%d\n", min, max); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + finfo->cmd_state = OK; + + //dev_info(&client->dev, "%s: \"%s\"(%d)\n", __func__, finfo->cmd_buff, + // strlen(finfo->cmd_buff)); + + return;*/ +} + +static void run_short_data_read(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + + struct tsp_factory_info *finfo = info->factory_info; + struct tsp_raw_data *raw_data = info->raw_data; +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI + const u16 NORMAL_SHORT_VALUE=1000; +#endif + u16 i; + int sz = info->cap_info.x_node_num + info->cap_info.y_node_num; + + set_default_result(info); + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + if (info->builtin_cmcp_threshold_status < 0) { + zinitix_printk("No cmcp threshold file.\n"); + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "%s", "NG"); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + finfo->cmd_state = FAIL; + return; + } +#endif + + ts_set_touchmode(TOUCH_CHECK_SHORT_MODE); + get_short_data(info, (u8 *)raw_data->ref_data, 10); + ts_set_touchmode(TOUCH_POINT_MODE); + + zinitix_printk("==========short_data start===============\n"); + for(i = 0; i < sz; i++) + { + printk("zinitix short_data[%d] = %d\n",i,raw_data->ref_data[i]); + } + zinitix_printk("==========short_data end===============\n"); + + for(i = 0; i < sz; i++) + { +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + if (raw_data->ref_data[i] != info->test_params.short_threshold) { +#else + if (raw_data->ref_data[i] != NORMAL_SHORT_VALUE) { +#endif + zinitix_printk("Invalid Check Short Value,Test Short!\n"); + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "%s", "NG"); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + finfo->cmd_state = FAIL; + info->test_params.short_data_pass = 0; + return; + } + } + + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "%s", "Pass"); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + finfo->cmd_state = OK; + info->test_params.short_data_pass = 1; + return; +} + +static void get_preference(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + //struct i2c_client *client = info->client; + struct tsp_factory_info *finfo = info->factory_info; + struct tsp_raw_data *raw_data = info->raw_data; + unsigned int val; + int x_node, y_node; + int node_num; + + set_default_result(info); + + x_node = finfo->cmd_param[0]; + y_node = finfo->cmd_param[1]; + + if (x_node < 0 || x_node >= info->cap_info.x_node_num || + y_node < 0 || y_node >= info->cap_info.y_node_num) { + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "%s", "abnormal"); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + info->factory_info->cmd_state = FAIL; + + return; + } + + node_num = x_node * info->cap_info.y_node_num + y_node; + + val = raw_data->pref_data[node_num]; + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "%u", val); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + finfo->cmd_state = OK; + + //dev_info(&client->dev, "%s: %s(%d)\n", __func__, finfo->cmd_buff, + // strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + + return; +} + +static void run_delta_read(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + //struct i2c_client *client = info->client; + struct tsp_factory_info *finfo = info->factory_info; + struct tsp_raw_data *raw_data = info->raw_data; + s16 min, max; + s32 i, j; + + set_default_result(info); + + ts_set_touchmode(TOUCH_DELTA_MODE); + get_raw_data(info, (u8 *)(u8 *)raw_data->delta_data, 10); + ts_set_touchmode(TOUCH_POINT_MODE); + finfo->cmd_state = OK; + + min = (s16)0x7FFF; + max = (s16)0x8000; + + for(i = 0; i < info->cap_info.x_node_num; i++) + { + for(j = 0; j < info->cap_info.y_node_num; j++) + { + /*printk("delta_data : %d \n", raw_data->delta_data[j+i]);*/ + + if (raw_data->delta_data[i * info->cap_info.y_node_num + j] < min && + raw_data->delta_data[i * info->cap_info.y_node_num + j] != 0) + min = raw_data->delta_data[i * info->cap_info.y_node_num + j]; + + if (raw_data->delta_data[i * info->cap_info.y_node_num + j] > max) + max = raw_data->delta_data[i * info->cap_info.y_node_num + j]; + + } + /*printk("\n");*/ + } + + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "%d,%d\n", min, max); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + finfo->cmd_state = OK; + + //dev_info(&client->dev, "%s: \"%s\"(%d)\n", __func__, finfo->cmd_buff, + // strlen(finfo->cmd_buff)); + + return; +} + +static void get_delta(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + //struct i2c_client *client = info->client; + struct tsp_factory_info *finfo = info->factory_info; + struct tsp_raw_data *raw_data = info->raw_data; + unsigned int val; + int x_node, y_node; + int node_num; + + set_default_result(info); + + x_node = finfo->cmd_param[0]; + y_node = finfo->cmd_param[1]; + + if (x_node < 0 || x_node >= info->cap_info.x_node_num || + y_node < 0 || y_node >= info->cap_info.y_node_num) { + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "%s", "abnormal"); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + info->factory_info->cmd_state = FAIL; + + return; + } + + node_num = x_node * info->cap_info.y_node_num + y_node; + + val = raw_data->delta_data[node_num]; + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "%u", val); + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + info->factory_info->cmd_state = OK; + + //dev_info(&client->dev, "%s: %s(%d)\n", __func__, finfo->cmd_buff, + // strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + + return; +} + +/* +static void run_intensity_read(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + + set_default_result(info); + + ts_set_touchmode(TOUCH_DND_MODE); + get_raw_data(info, (u8 *)info->dnd_data, 10); + ts_set_touchmode(TOUCH_POINT_MODE); + + //////test//////////////////////////////////////////////////// + int i,j; + + for(i=0; i<30; i++) + { + for (j = 0; j < 18; j++) + printk("[TSP] info->dnd_data : %d ", info->dnd_data[j+i]); + + printk("\n"); + } + //////test//////////////////////////////////////////////////// + + info->factory_info->cmd_state = 2; +} + +static void get_normal(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + char buff[16] = {0}; + unsigned int val; + int x_node, y_node; + int node_num; + + set_default_result(info); + + x_node = info->factory_info->cmd_param[0]; + y_node = info->factory_info->cmd_param[1]; + + if (x_node < 0 || x_node > info->cap_info.x_node_num || + y_node < 0 || y_node > info->cap_info.y_node_num) { + snprintf(buff, sizeof(buff), "%s", "abnormal"); + set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); + info->factory_info->cmd_state = 3; + return; + } + + node_num = x_node*info->cap_info.x_node_num + y_node; + + val = info->normal_data[node_num]; + snprintf(buff, sizeof(buff), "%u", val); + set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); + info->factory_info->cmd_state = 2; + + dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, + buff, strnlen(buff, sizeof(buff))); +} + +static void get_tkey_delta(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + char buff[16] = {0}; + u16 val; + int btn_node; + int ret; + + set_default_result(info); + + btn_node = info->factory_info->cmd_param[0]; + + if (btn_node < 0 || btn_node > MAX_SUPPORTED_BUTTON_NUM) + goto err_out; + + disable_irq(misc_info->irq); + down(&misc_info->work_lock); + if (misc_info->work_state != NOTHING) { + printk(KERN_INFO "other process occupied.. (%d)\n", + misc_info->work_state); + enable_irq(misc_info->irq); + up(&misc_info->work_lock); + goto err_out; + } + misc_info->work_state = SET_MODE; + + ret = read_data(misc_info->client, BT541_BTN_WIDTH + btn_node, (u8*)&val, 2); + + if (ret < 0) { + printk(KERN_INFO "read error..\n"); + enable_irq(misc_info->irq); + misc_info->work_state = NOTHING; + up(&misc_info->work_lock); + goto err_out; + } + misc_info->work_state = NOTHING; + enable_irq(misc_info->irq); + up(&misc_info->work_lock); + + snprintf(buff, sizeof(buff), "%u", val); + set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); + info->factory_info->cmd_state = 2; + + dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, + buff, strnlen(buff, sizeof(buff))); + return; + +err_out: + snprintf(buff, sizeof(buff), "%s", "abnormal"); + set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); + info->factory_info->cmd_state = 3; +} + +static void get_intensity(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + char buff[16] = {0}; + unsigned int val; + int x_node, y_node; + int node_num; + + set_default_result(info); + + x_node = info->factory_info->cmd_param[0]; + y_node = info->factory_info->cmd_param[1]; + + if (x_node < 0 || x_node > info->cap_info.x_node_num || + y_node < 0 || y_node > info->cap_info.y_node_num) { + snprintf(buff, sizeof(buff), "%s", "abnormal"); + set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); + info->factory_info->cmd_state = 3; + return; + } + + node_num = x_node*info->cap_info.x_node_num + y_node; + + val = info->dnd_data[node_num]; + snprintf(buff, sizeof(buff), "%u", val); + set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); + info->factory_info->cmd_state = 2; + + dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, + buff, strnlen(buff, sizeof(buff))); +} + +static void run_normal_read(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + + set_default_result(info); + + ts_set_touchmode(TOUCH_NORMAL_MODE); + get_raw_data(info, (u8 *)info->normal_data, 10); + ts_set_touchmode(TOUCH_POINT_MODE); + + info->factory_info->cmd_state = 2; +} + +static void get_key_threshold(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + int ret = 0; + u16 threshold; + char buff[16] = {0}; + + set_default_result(info); + + ret = read_data(misc_info->client, BT541_BUTTON_SENSITIVITY, (u8*)&threshold, 2); + + if (ret < 0) { + snprintf(buff, sizeof(buff), "%s", "failed"); + set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); + info->factory_info->cmd_state = 3; + return; + } + + snprintf(buff, sizeof(buff), "%u", threshold); + + set_cmd_result(info, buff, strnlen(buff, sizeof(buff))); + info->factory_info->cmd_state = 2; + dev_info(&info->client->dev, "%s: %s(%d)\n", __func__, + buff, strnlen(buff, sizeof(buff))); +} +*/ + +#ifdef GLOVE_MODE +static void glove_mode(void *device_data) +{ + struct bt541_ts_info *info = (struct bt541_ts_info *)device_data; + //struct i2c_client *client = info->client; + struct tsp_factory_info *finfo = info->factory_info; + + set_default_result(info); + + if (finfo->cmd_param[0] < 0 || finfo->cmd_param[0] > 1) { + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "%s", "NG"); + finfo->cmd_state = FAIL; + } else { + if (finfo->cmd_param[0]) + zinitix_bit_set(m_optional_mode, TS_SENSIVE_MODE_BIT); + else + zinitix_bit_clr(m_optional_mode, TS_SENSIVE_MODE_BIT); + + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "%s", "OK"); + finfo->cmd_state = OK; + } + + set_cmd_result(info, finfo->cmd_buff, + strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + + mutex_lock(&finfo->cmd_lock); + finfo->cmd_is_running = false; + mutex_unlock(&finfo->cmd_lock); + + finfo->cmd_state = WAITING; + //dev_info(&client->dev, "%s: %s(%d)\n", __func__, finfo->cmd_buff, + // strnlen(finfo->cmd_buff, sizeof(finfo->cmd_buff))); + + return; +} +#endif + +static ssize_t store_cmd(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + struct i2c_client *client = info->client; + struct tsp_factory_info *finfo = info->factory_info; + char *cur, *start, *end; + char buff[TSP_CMD_STR_LEN] = {0}; + int len, i; + struct tsp_cmd *tsp_cmd_ptr = NULL; + char delim = ','; + bool cmd_found = false; + int param_cnt = 0; + + if (finfo->cmd_is_running == true) { + dev_err(&client->dev, "%s: other cmd is running\n", __func__); + goto err_out; + } + + /* check lock */ + mutex_lock(&finfo->cmd_lock); + finfo->cmd_is_running = true; + mutex_unlock(&finfo->cmd_lock); + + finfo->cmd_state = RUNNING; + + for (i = 0; i < ARRAY_SIZE(finfo->cmd_param); i++) + finfo->cmd_param[i] = 0; + + len = (int)count; + if (*(buf + len - 1) == '\n') + len--; + + memset(finfo->cmd, 0x00, ARRAY_SIZE(finfo->cmd)); + memcpy(finfo->cmd, buf, len); + + cur = strchr(buf, (int)delim); + if (cur) + memcpy(buff, buf, cur - buf); + else + memcpy(buff, buf, len); + + /* find command */ + list_for_each_entry(tsp_cmd_ptr, &finfo->cmd_list_head, list) { + if (!strcmp(buff, tsp_cmd_ptr->cmd_name)) { + cmd_found = true; + break; + } + } + + /* set not_support_cmd */ + if (!cmd_found) { + list_for_each_entry(tsp_cmd_ptr, &finfo->cmd_list_head, list) { + if (!strcmp("not_support_cmd", tsp_cmd_ptr->cmd_name)) + break; + } + } + + /* parsing parameters */ + if (cur && cmd_found) { + cur++; + start = cur; + memset(buff, 0x00, ARRAY_SIZE(buff)); + do { + if (*cur == delim || cur - buf == len) { + end = cur; + memcpy(buff, start, end - start); + *(buff + strlen(buff)) = '\0'; + finfo->cmd_param[param_cnt] = + (int)simple_strtol(buff, NULL, 10); + start = cur + 1; + memset(buff, 0x00, ARRAY_SIZE(buff)); + param_cnt++; + } + cur++; + } while (cur - buf <= len); + } + + dev_info(&client->dev, "cmd = %s\n", tsp_cmd_ptr->cmd_name); +/* for (i = 0; i < param_cnt; i++) + dev_info(&client->dev, "cmd param %d= %d\n", i, finfo->cmd_param[i]);*/ + + tsp_cmd_ptr->cmd_func(info); + +err_out: + return count; +} + +static ssize_t show_cmd_status(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + struct i2c_client *client = info->client; + struct tsp_factory_info *finfo = info->factory_info; + + dev_info(&client->dev, "tsp cmd: status:%d\n", finfo->cmd_state); + + if (finfo->cmd_state == WAITING) + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "WAITING"); + + else if (finfo->cmd_state == RUNNING) + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "RUNNING"); + + else if (finfo->cmd_state == OK) + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "OK"); + + else if (finfo->cmd_state == FAIL) + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "FAIL"); + + else if (finfo->cmd_state == NOT_APPLICABLE) + snprintf(finfo->cmd_buff, sizeof(finfo->cmd_buff), "NOT_APPLICABLE"); + + return snprintf(buf, sizeof(finfo->cmd_buff), + "%s\n", finfo->cmd_buff); +} + +static ssize_t show_cmd_result(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + struct i2c_client *client = info->client; + struct tsp_factory_info *finfo = info->factory_info; + + dev_info(&client->dev, "tsp cmd: result: %s\n", finfo->cmd_result); + + mutex_lock(&finfo->cmd_lock); + finfo->cmd_is_running = false; + mutex_unlock(&finfo->cmd_lock); + + finfo->cmd_state = WAITING; + + return snprintf(buf, sizeof(finfo->cmd_result), + "%s\n", finfo->cmd_result); +} + +static ssize_t show_easy_wakeup_gesture(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + struct tsp_factory_info *finfo = info->factory_info; + ssize_t ret; + + mutex_lock(&finfo->cmd_lock); + ret = snprintf(buf, PIPE_BUF, "%d\n", easy_wake); + mutex_unlock(&finfo->cmd_lock); + + return ret; +} + +static ssize_t store_easy_wakeup_gesture(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + struct i2c_client *client = info->client; + int rc = 0; + + rc = kstrtouint(buf, 0, &easy_wake); + if (rc < 0) + return rc; + + if(easy_wake == 1) { + if (write_reg(client, 0x011D, 0x0089) != I2C_SUCCESS) + zinitix_printk("store_easy_wakeup_gesture write_reg[0x0089] failed!!!-----------------------\n"); + } + else { + if (write_reg(client, 0x011D, 0x0080) != I2C_SUCCESS) + zinitix_printk("store_easy_wakeup_gesture write_reg[0x0080] failed!!!-----------------------\n"); + } + + return count; +} + +static ssize_t show_bigobject_off(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + struct tsp_factory_info *finfo = info->factory_info; + ssize_t ret; + + mutex_lock(&finfo->cmd_lock); + ret = snprintf(buf, PIPE_BUF, "%d\n", big_object); + mutex_unlock(&finfo->cmd_lock); + + return ret; +} + +static ssize_t store_bigobject_off(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + struct i2c_client *client = info->client; + int rc = 0; + + rc = kstrtouint(buf, 0, &big_object); + if (rc < 0) + return rc; + + if (big_object) { + if (write_reg(client, 0x0017, 0x8A08) != I2C_SUCCESS) + zinitix_printk("store_bigobject_off write_reg[0x0080] failed!!!-----------------------\n"); + } + else { + if (write_reg(client, 0x0017, 0x8A00) != I2C_SUCCESS) + zinitix_printk("store_bigobject_off write_reg[0x0080] failed!!!-----------------------\n"); + } + + return count; +} + +static ssize_t raw_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + struct tsp_raw_data *raw_data = info->raw_data; + u32 total_node = info->cap_info.total_node_num; + int sz; + int i = 0; + char csv_buffer[RAW_DATA_BUFFER_SIZE] = { 0 }; + int csv_length = 0; + + sz = total_node * 2; + zinitix_printk("sz = %d\n", sz); + + /* line 1 */ + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "raw data\n"); + + /* line 2 */ + for (i = 0; i < total_node; i++) + { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "%d, ", raw_data->pref_data[i]); + } + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "\n"); + + /* line 3 */ + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "raw data min diff\n"); + + /* line 4 */ + for (i = 0; i < total_node; i++) + { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "%d, ", raw_data->pref_data[i] - info->test_params.min_raw_limits[i]); + } + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "\n"); + + /* line 5 */ + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "raw data max diff\n"); + + /* line 6 */ + for (i = 0; i < total_node; i++) + { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "%d, ", info->test_params.max_raw_limits[i] - raw_data->pref_data[i]); + } + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "\n"); + + /* line 7 */ + if (info->test_params.raw_data_pass) + { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "raw data test pass\n"); + } + else + { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "raw data test fail\n"); + } + + /* line 8 ~ 10 "\n" */ + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "\n\n\n"); + + return snprintf(buf, ZINITIX_MAX_PRBUF_SIZE, "%s", csv_buffer); +} + +static ssize_t short_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + struct tsp_raw_data *raw_data = info->raw_data; + int sz = info->cap_info.x_node_num + info->cap_info.y_node_num; + int i = 0; + char csv_buffer[RAW_DATA_BUFFER_SIZE] = { 0 }; + int csv_length = 0; + + zinitix_printk("sz = %d\n", sz); + + /* line 11 */ + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "short data\n"); + + /* line 12 */ + for (i = 0; i < sz; i++) + { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "%d, ", raw_data->ref_data[i]); + } + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "\n"); + + /* line 13 */ + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "short data diff\n"); + + /* line 14 */ + for (i = 0; i < sz; i++) + { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "%d, ", raw_data->ref_data[i] - info->test_params.short_threshold); + } + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "\n"); + + /* line 15 */ + if (info->test_params.short_data_pass) + { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "short data test pass\n"); + } + else + { + csv_length += snprintf(csv_buffer + csv_length, \ + CSV_BUFFER_LEN - csv_length, \ + "short data test fail\n"); + } + + return snprintf(buf, ZINITIX_MAX_PRBUF_SIZE, "%s", csv_buffer); +} + +static DEVICE_ATTR(cmd, S_IWUSR | S_IWGRP, NULL, store_cmd); +static DEVICE_ATTR(cmd_status, S_IRUGO, show_cmd_status, NULL); +static DEVICE_ATTR(cmd_result, S_IRUSR | S_IRGRP, show_cmd_result, NULL); +static DEVICE_ATTR(easy_wakeup_gesture, 0664, show_easy_wakeup_gesture, store_easy_wakeup_gesture); +static DEVICE_ATTR(bigobject_off, 0664, show_bigobject_off, store_bigobject_off); +static DEVICE_ATTR(raw_data, S_IRUSR | S_IRGRP, raw_data_show, NULL); +static DEVICE_ATTR(short_data, S_IRUSR | S_IRGRP, short_data_show, NULL); + +static struct attribute *touchscreen_attributes[] = { + &dev_attr_cmd.attr, + &dev_attr_cmd_status.attr, + &dev_attr_cmd_result.attr, + &dev_attr_easy_wakeup_gesture.attr, + &dev_attr_bigobject_off.attr, + &dev_attr_raw_data.attr, + &dev_attr_short_data.attr, + NULL, +}; + +static struct attribute_group touchscreen_attr_group = { + .attrs = touchscreen_attributes, +}; + +#ifdef SUPPORTED_TOUCH_KEY +static ssize_t show_touchkey_threshold(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + struct i2c_client *client = info->client; + struct capa_info *cap = &(info->cap_info); + +#ifdef NOT_SUPPORTED_TOUCH_DUMMY_KEY + dev_info(&client->dev, "%s: key threshold = %d\n", __func__, + cap->key_threshold); + + return snprintf(buf, 41, "%d", cap->key_threshold); +#else + dev_info(&client->dev, "%s: key threshold = %d %d %d %d\n", __func__, + cap->dummy_threshold, cap->key_threshold, cap->key_threshold, cap->dummy_threshold); + + return snprintf(buf, 41, "%d %d %d %d", cap->dummy_threshold, + cap->key_threshold, cap->key_threshold, + cap->dummy_threshold); +#endif +} + +static ssize_t show_touchkey_sensitivity(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + struct i2c_client *client = info->client; + u16 val = 0; + int ret; + int i; + +#ifdef NOT_SUPPORTED_TOUCH_DUMMY_KEY + if (!strcmp(attr->attr.name, "touchkey_menu")) + i = 0; + else if (!strcmp(attr->attr.name, "touchkey_back")) + i = 1; + else if (!strcmp(attr->attr.name, "touchkey_recent")) + i = 0; + else { + dev_err(&client->dev, "%s: Invalid attribute\n",__func__); + + goto err_out; + } + +#else + if (!strcmp(attr->attr.name, "touchkey_dummy_btn1")) + i = 0; + else if (!strcmp(attr->attr.name, "touchkey_menu")) + i = 1; + else if (!strcmp(attr->attr.name, "touchkey_recent")) + i = 1; + else if (!strcmp(attr->attr.name, "touchkey_back")) + i = 2; + else if (!strcmp(attr->attr.name, "touchkey_dummy_btn4")) + i = 3; + else if (!strcmp(attr->attr.name, "touchkey_dummy_btn5")) + i = 4; + else if (!strcmp(attr->attr.name, "touchkey_dummy_btn6")) + i = 5; + else { + dev_err(&client->dev, "%s: Invalid attribute\n", __func__); + + goto err_out; + } +#endif + ret = read_data(client, BT541_BTN_WIDTH + i, (u8 *)&val, 2); + if (ret < 0) { + dev_err(&client->dev, "%s:Failed to read %d's key sensitivity\n", + __func__, i); + + goto err_out; + } + + dev_info(&client->dev, "%s: %d's key sensitivity = %d\n", + __func__, i, val); + + return snprintf(buf, 6, "%d", val); + +err_out: + return sprintf(buf, "NG"); +} + +static ssize_t show_back_key_raw_data(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return 0; +} + +static ssize_t show_menu_key_raw_data(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return 0; +} +/* +static ssize_t show_back_key_idac_data(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return 0; +} + +static ssize_t show_menu_key_idac_data(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return 0; +} +*/ +#ifdef SUPPORTED_TOUCH_KEY_LED +static ssize_t touch_led_control(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + u8 data; + + sscanf(buf, "%hhu", &data); + + printk("[TKEY] %s : %d _ %d\n",__func__,data,__LINE__); + + if(data == 1) + gpio_direction_output(info->pdata->gpio_keyled, 1); + else + gpio_direction_output(info->pdata->gpio_keyled, 0); + + return size; +} +#endif + +static DEVICE_ATTR(touchkey_threshold, S_IRUGO, show_touchkey_threshold, NULL); +/*static DEVICE_ATTR(touch_sensitivity, S_IRUGO, back_key_state_show, NULL);*/ +//static DEVICE_ATTR(extra_button_event, S_IWUSR | S_IWGRP | S_IRUGO, NULL, enable_dummy_key ); +static DEVICE_ATTR(touchkey_menu, S_IRUGO, show_touchkey_sensitivity, NULL); +static DEVICE_ATTR(touchkey_back, S_IRUGO, show_touchkey_sensitivity, NULL); +static DEVICE_ATTR(touchkey_recent, S_IRUGO, show_touchkey_sensitivity, NULL); +#ifndef NOT_SUPPORTED_TOUCH_DUMMY_KEY +static DEVICE_ATTR(touchkey_dummy_btn1, S_IRUGO, + show_touchkey_sensitivity, NULL); +static DEVICE_ATTR(touchkey_dummy_btn3, S_IRUGO, + show_touchkey_sensitivity, NULL); +static DEVICE_ATTR(touchkey_dummy_btn4, S_IRUGO, + show_touchkey_sensitivity, NULL); +static DEVICE_ATTR(touchkey_dummy_btn6, S_IRUGO, + show_touchkey_sensitivity, NULL); +#endif +/*static DEVICE_ATTR(autocal_stat, S_IRUGO, show_autocal_status, NULL);*/ +static DEVICE_ATTR(touchkey_raw_back, S_IRUGO, show_back_key_raw_data, NULL); +static DEVICE_ATTR(touchkey_raw_menu, S_IRUGO, show_menu_key_raw_data, NULL); +/*static DEVICE_ATTR(touchkey_idac_back, S_IRUGO, show_back_key_idac_data, NULL); +static DEVICE_ATTR(touchkey_idac_menu, S_IRUGO, show_menu_key_idac_data, NULL);*/ +#ifdef SUPPORTED_TOUCH_KEY_LED +static DEVICE_ATTR(brightness, 0664, NULL, touch_led_control); +#endif + +static struct attribute *touchkey_attributes[] = { + &dev_attr_touchkey_threshold.attr, + /*&dev_attr_touch_sensitivity.attr,*/ + &dev_attr_touchkey_back.attr, + &dev_attr_touchkey_menu.attr, + &dev_attr_touchkey_recent.attr, + //&dev_attr_autocal_stat.attr, + //&dev_attr_extra_button_event.attr, + &dev_attr_touchkey_raw_menu.attr, + &dev_attr_touchkey_raw_back.attr, +#ifndef NOT_SUPPORTED_TOUCH_DUMMY_KEY + &dev_attr_touchkey_dummy_btn1.attr, + &dev_attr_touchkey_dummy_btn3.attr, + &dev_attr_touchkey_dummy_btn4.attr, + &dev_attr_touchkey_dummy_btn6.attr, +#endif + //&dev_attr_touchkey_idac_back.attr, + //&dev_attr_touchkey_idac_menu.attr, +#ifdef SUPPORTED_TOUCH_KEY_LED + &dev_attr_brightness.attr, +#endif + NULL, +}; +static struct attribute_group touchkey_attr_group = { + .attrs = touchkey_attributes, +}; +#endif + +static int init_sec_factory(struct bt541_ts_info *info) +{ + struct device *factory_ts_dev; +#ifdef SUPPORTED_TOUCH_KEY + struct device *factory_tk_dev; +#endif + struct tsp_factory_info *factory_info; + struct tsp_raw_data *raw_data; + int ret; + int i; + + factory_info = kzalloc(sizeof(struct tsp_factory_info), GFP_KERNEL); + if (unlikely(!factory_info)) { + dev_err(&info->client->dev, "%s: Failed to allocate memory\n", + __func__); + ret = -ENOMEM; + + goto err_alloc1; + } + raw_data = kzalloc(sizeof(struct tsp_raw_data), GFP_KERNEL); + if (unlikely(!raw_data)) { + dev_err(&info->client->dev, "%s: Failed to allocate memory\n", + __func__); + ret = -ENOMEM; + + goto err_alloc2; + } + + INIT_LIST_HEAD(&factory_info->cmd_list_head); + for (i = 0; i < ARRAY_SIZE(tsp_cmds); i++) + list_add_tail(&tsp_cmds[i].list, &factory_info->cmd_list_head); + + + sec_class = class_create(THIS_MODULE, "tsp"); + + factory_ts_dev = device_create(sec_class, NULL, 0, info, "tsp"); + if (unlikely(!factory_ts_dev)) { + dev_err(&info->client->dev, "Failed to create factory dev\n"); + ret = -ENODEV; + goto err_create_device; + } + + ret = sysfs_create_link(&factory_ts_dev->kobj, + &info->input_dev->dev.kobj, "input"); + if (ret < 0) { + dev_err(&info->client->dev, + "%s: Failed to create input symbolic link %d\n", + __func__, ret); + } + +#ifdef SUPPORTED_TOUCH_KEY + factory_tk_dev = device_create(sec_class, NULL, 0, info, "sec_touchkey"); + if (IS_ERR(factory_tk_dev)) { + dev_err(&info->client->dev, "Failed to create factory dev\n"); + ret = -ENODEV; + goto err_create_device; + } +#endif + + ret = sysfs_create_group(&factory_ts_dev->kobj, &touchscreen_attr_group); + if (unlikely(ret)) { + dev_err(&info->client->dev, "Failed to create touchscreen sysfs group\n"); + goto err_create_sysfs; + } + +#ifdef SUPPORTED_TOUCH_KEY + ret = sysfs_create_group(&factory_tk_dev->kobj, &touchkey_attr_group); + if (unlikely(ret)) { + dev_err(&info->client->dev, "Failed to create touchkey sysfs group\n"); + goto err_create_sysfs; + } +#endif + + mutex_init(&factory_info->cmd_lock); + factory_info->cmd_is_running = false; + + info->factory_info = factory_info; + info->raw_data = raw_data; + + return ret; + +err_create_sysfs: +err_create_device: + kfree(raw_data); +err_alloc2: + kfree(factory_info); +err_alloc1: + + return ret; +} +#endif + +static int ts_misc_fops_open(struct inode *inode, struct file *filp) +{ + return 0; +} + +static int ts_misc_fops_close(struct inode *inode, struct file *filp) +{ + return 0; +} + +static long ts_misc_fops_ioctl(struct file *filp, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + struct raw_ioctl raw_ioctl; + u8 *u8Data; + int ret = 0; + size_t sz = 0; + u16 version; + u16 mode; + + struct reg_ioctl reg_ioctl; + u16 val; + int nval = 0; + + if (misc_info == NULL) + { + zinitix_debug_msg("misc device NULL?\n"); + return -1; + } + + switch (cmd) { + + case TOUCH_IOCTL_GET_DEBUGMSG_STATE: + ret = m_ts_debug_mode; + if (copy_to_user(argp, &ret, sizeof(ret))) + return -1; + break; + + case TOUCH_IOCTL_SET_DEBUGMSG_STATE: + if (copy_from_user(&nval, argp, 4)) { + pr_info("[zinitix_touch] error : copy_from_user\n"); + return -1; + } + if (nval) + pr_info("[zinitix_touch] on debug mode (%d)\n", + nval); + else + pr_info("[zinitix_touch] off debug mode (%d)\n", + nval); + m_ts_debug_mode = nval; + break; + + case TOUCH_IOCTL_GET_CHIP_REVISION: + ret = misc_info->cap_info.ic_revision; + if (copy_to_user(argp, &ret, sizeof(ret))) + return -1; + break; + + case TOUCH_IOCTL_GET_FW_VERSION: + ret = misc_info->cap_info.fw_version; + if (copy_to_user(argp, &ret, sizeof(ret))) + return -1; + break; + + case TOUCH_IOCTL_GET_REG_DATA_VERSION: + ret = misc_info->cap_info.reg_data_version; + if (copy_to_user(argp, &ret, sizeof(ret))) + return -1; + break; + + case TOUCH_IOCTL_VARIFY_UPGRADE_SIZE: + if (copy_from_user(&sz, argp, sizeof(size_t))) + return -1; + + //printk(KERN_INFO "[zinitix_touch]: firmware size = %l\r\n", sz); + if (misc_info->cap_info.ic_fw_size != sz) { + pr_info("[zinitix_touch]: firmware size error\r\n"); + return -1; + } + break; + + case TOUCH_IOCTL_VARIFY_UPGRADE_DATA: + ts_select_type_hw(misc_info); + if (copy_from_user(m_pFirmware[m_FirmwareIdx], + argp, misc_info->cap_info.ic_fw_size)) + return -1; + + version = (u16) (m_pFirmware[m_FirmwareIdx][52] | (m_pFirmware[m_FirmwareIdx][53]<<8)); + + pr_info("[zinitix_touch]: firmware version = %x\r\n", version); + + if (copy_to_user(argp, &version, sizeof(version))) + return -1; + break; + + case TOUCH_IOCTL_START_UPGRADE: + ts_select_type_hw(misc_info); + return ts_upgrade_sequence((u8*)m_pFirmware[m_FirmwareIdx]); + + case TOUCH_IOCTL_GET_X_RESOLUTION: + ret = misc_info->pdata->x_resolution; + if (copy_to_user(argp, &ret, sizeof(ret))) + return -1; + break; + + case TOUCH_IOCTL_GET_Y_RESOLUTION: + ret = misc_info->pdata->y_resolution; + if (copy_to_user(argp, &ret, sizeof(ret))) + return -1; + break; + + case TOUCH_IOCTL_GET_X_NODE_NUM: + ret = misc_info->cap_info.x_node_num; + if (copy_to_user(argp, &ret, sizeof(ret))) + return -1; + break; + + case TOUCH_IOCTL_GET_Y_NODE_NUM: + ret = misc_info->cap_info.y_node_num; + if (copy_to_user(argp, &ret, sizeof(ret))) + return -1; + break; + + case TOUCH_IOCTL_GET_TOTAL_NODE_NUM: + ret = misc_info->cap_info.total_node_num; + if (copy_to_user(argp, &ret, sizeof(ret))) + return -1; + break; + + case TOUCH_IOCTL_HW_CALIBRAION: + ret = -1; + disable_irq(misc_info->irq); + misc_info->irq_enabled = false; + down(&misc_info->work_lock); + if (misc_info->work_state != NOTHING) { + pr_info("[zinitix_touch]: other process occupied.. (%d)\r\n", + misc_info->work_state); + up(&misc_info->work_lock); + return -1; + } + misc_info->work_state = HW_CALIBRAION; + mdelay(100); + + mode = misc_info->touch_mode; + if (write_reg(misc_info->client, + BT541_TOUCH_MODE, mode) != I2C_SUCCESS) { + pr_err("[zinitix_touch]: failed to set touch mode %d.\n", + mode); + goto fail_hw_cal; + } + + if (write_cmd(misc_info->client, + BT541_SWRESET_CMD) != I2C_SUCCESS) + goto fail_hw_cal; + + enable_irq(misc_info->irq); + misc_info->irq_enabled = true; + misc_info->work_state = NOTHING; + up(&misc_info->work_lock); + return ret; +fail_hw_cal: + enable_irq(misc_info->irq); + misc_info->irq_enabled = true; + misc_info->work_state = NOTHING; + up(&misc_info->work_lock); + return -1; + + case TOUCH_IOCTL_SET_RAW_DATA_MODE: + if (misc_info == NULL) { + zinitix_debug_msg("misc device NULL?\n"); + return -1; + } + if (copy_from_user(&nval, argp, 4)) { + pr_info("[zinitix_touch] error : copy_from_user\r\n"); + misc_info->work_state = NOTHING; + return -1; + } + ts_set_touchmode((u16)nval); + + return 0; + + case TOUCH_IOCTL_GET_REG: + if (misc_info == NULL) { + zinitix_debug_msg("misc device NULL?\n"); + return -1; + } + down(&misc_info->work_lock); + if (misc_info->work_state != NOTHING) { + pr_info("[zinitix_touch]:other process occupied.. (%d)\n", + misc_info->work_state); + up(&misc_info->work_lock); + return -1; + } + + misc_info->work_state = SET_MODE; + + if (copy_from_user(®_ioctl, + argp, sizeof(struct reg_ioctl))) { + misc_info->work_state = NOTHING; + up(&misc_info->work_lock); + pr_info("[zinitix_touch] error : copy_from_user\n"); + return -1; + } + + if (read_data(misc_info->client, + reg_ioctl.addr, (u8 *)&val, 2) < 0) + ret = -1; + + nval = (int)val; + + if (copy_to_user(reg_ioctl.val, (u8 *)&nval, 4)) { + misc_info->work_state = NOTHING; + up(&misc_info->work_lock); + pr_info("[zinitix_touch] error : copy_to_user\n"); + return -1; + } + + zinitix_debug_msg("read : reg addr = 0x%x, val = 0x%x\n", + reg_ioctl.addr, nval); + + misc_info->work_state = NOTHING; + up(&misc_info->work_lock); + return ret; + + case TOUCH_IOCTL_SET_REG: + + down(&misc_info->work_lock); + if (misc_info->work_state != NOTHING) { + pr_info("[zinitix_touch]: other process occupied.. (%d)\n", + misc_info->work_state); + up(&misc_info->work_lock); + return -1; + } + + misc_info->work_state = SET_MODE; + if (copy_from_user(®_ioctl, + argp, sizeof(struct reg_ioctl))) { + misc_info->work_state = NOTHING; + up(&misc_info->work_lock); + pr_info("[zinitix_touch] error : copy_from_user\n"); + return -1; + } + + if (copy_from_user(&val, reg_ioctl.val, 4)) { + misc_info->work_state = NOTHING; + up(&misc_info->work_lock); + pr_info("[zinitix_touch] error : copy_from_user\n"); + return -1; + } + + if (write_reg(misc_info->client, + reg_ioctl.addr, val) != I2C_SUCCESS) + ret = -1; + + zinitix_debug_msg("write : reg addr = 0x%x, val = 0x%x\r\n", + reg_ioctl.addr, val); + misc_info->work_state = NOTHING; + up(&misc_info->work_lock); + return ret; + + case TOUCH_IOCTL_DONOT_TOUCH_EVENT: + + if (misc_info == NULL) { + zinitix_debug_msg("misc device NULL?\n"); + return -1; + } + down(&misc_info->work_lock); + if (misc_info->work_state != NOTHING) { + pr_info("[zinitix_touch]: other process occupied.. (%d)\r\n", + misc_info->work_state); + up(&misc_info->work_lock); + return -1; + } + + misc_info->work_state = SET_MODE; + if (write_reg(misc_info->client, + BT541_INT_ENABLE_FLAG, 0) != I2C_SUCCESS) + ret = -1; + zinitix_debug_msg("write : reg addr = 0x%x, val = 0x0\r\n", + BT541_INT_ENABLE_FLAG); + + misc_info->work_state = NOTHING; + up(&misc_info->work_lock); + return ret; + + case TOUCH_IOCTL_SEND_SAVE_STATUS: + if (misc_info == NULL) { + zinitix_debug_msg("misc device NULL?\n"); + return -1; + } + down(&misc_info->work_lock); + if (misc_info->work_state != NOTHING) { + pr_info("[zinitix_touch]: other process occupied.." \ + "(%d)\r\n", misc_info->work_state); + up(&misc_info->work_lock); + return -1; + } + misc_info->work_state = SET_MODE; + ret = 0; + write_reg(misc_info->client, 0xc003, 0x0001); + write_reg(misc_info->client, 0xc104, 0x0001); + if (write_cmd(misc_info->client, + BT541_SAVE_STATUS_CMD) != I2C_SUCCESS) + ret = -1; + + mdelay(1000); /* for fusing eeprom */ + write_reg(misc_info->client, 0xc003, 0x0000); + write_reg(misc_info->client, 0xc104, 0x0000); + + misc_info->work_state = NOTHING; + up(&misc_info->work_lock); + return ret; + + case TOUCH_IOCTL_GET_RAW_DATA: + if (misc_info == NULL) { + zinitix_debug_msg("misc device NULL?\n"); + return -1; + } + + if (misc_info->touch_mode == TOUCH_POINT_MODE) + return -1; + + down(&misc_info->raw_data_lock); + if (misc_info->update == 0) { + up(&misc_info->raw_data_lock); + return -2; + } + + if (copy_from_user(&raw_ioctl, + argp, sizeof(struct raw_ioctl))) { + up(&misc_info->raw_data_lock); + pr_info("[zinitix_touch] error : copy_from_user\r\n"); + return -1; + } + + misc_info->update = 0; + + u8Data = (u8 *)&misc_info->cur_data[0]; + if (raw_ioctl.sz > MAX_TRAW_DATA_SZ*2) + raw_ioctl.sz = MAX_TRAW_DATA_SZ*2; + if (copy_to_user(raw_ioctl.buf, (u8 *)u8Data, + raw_ioctl.sz)) { + up(&misc_info->raw_data_lock); + return -1; + } + + up(&misc_info->raw_data_lock); + return 0; + + default: + break; + } + return 0; +} + + +static int zinitix_power_init(struct bt541_ts_info *data, bool on) +{ + struct bt541_ts_platform_data *pdata = data->pdata; + int rc; + + if (!on) + goto pwr_deinit; + + if (strlen(pdata->avdd_name)) { + data->vdd = regulator_get(&data->client->dev, pdata->avdd_name); + if (IS_ERR_OR_NULL(data->vdd)) { + rc = PTR_ERR(data->vdd); + dev_err(&data->client->dev, + "Regulator get failed vdd rc=%d\n", rc); + data->vdd = NULL; + return rc; + } + + if (regulator_count_voltages(data->vdd) > 0) { + rc = regulator_set_voltage(data->vdd, FT_VTG_MIN_UV, + FT_VTG_MAX_UV); + if (rc) { + dev_err(&data->client->dev, + "Regulator set_vtg failed vdd rc=%d\n", rc); + goto reg_vdd_put; + } + } + } else { + pr_info("%s: Avdd name is NULL", __func__); + } + + if (strlen(pdata->iovdd_name)) { + data->vcc_i2c = regulator_get(&data->client->dev, pdata->iovdd_name); + if (IS_ERR_OR_NULL(data->vcc_i2c)) { + rc = PTR_ERR(data->vcc_i2c); + dev_err(&data->client->dev, + "Regulator get failed vcc_i2c rc=%d\n", rc); + data->vcc_i2c = NULL; + goto reg_vdd_set_vtg; + } + + if (regulator_count_voltages(data->vcc_i2c) > 0) { + rc = regulator_set_voltage(data->vcc_i2c, FT_I2C_VTG_MIN_UV, + FT_I2C_VTG_MAX_UV); + if (rc) { + dev_err(&data->client->dev, + "Regulator set_vtg failed vcc_i2c rc=%d\n", rc); + goto reg_vcc_i2c_put; + } + } + } else { + pr_info("%s: iovdd name is NULL", __func__); + } + + return 0; + +reg_vcc_i2c_put: + if (!IS_ERR_OR_NULL(data->vcc_i2c)) + regulator_put(data->vcc_i2c); +reg_vdd_set_vtg: + if (!IS_ERR_OR_NULL(data->vdd)) { + if (regulator_count_voltages(data->vdd) > 0) + regulator_set_voltage(data->vdd, 0, FT_VTG_MAX_UV); + } +reg_vdd_put: + if (!IS_ERR_OR_NULL(data->vdd)) + regulator_put(data->vdd); + return rc; + +pwr_deinit: + if (!IS_ERR_OR_NULL(data->vdd)) { + if (regulator_count_voltages(data->vdd) > 0) + regulator_set_voltage(data->vdd, 0, FT_VTG_MAX_UV); + + regulator_put(data->vdd); + } + + if (!IS_ERR_OR_NULL(data->vcc_i2c)) { + if (regulator_count_voltages(data->vcc_i2c) > 0) + regulator_set_voltage(data->vcc_i2c, 0, FT_I2C_VTG_MAX_UV); + + regulator_put(data->vcc_i2c); + } + return 0; +} + +static int zinitix_ts_pinctrl_init(struct bt541_ts_info *bt541_data) +{ + int retval; + + /* Get pinctrl if target uses pinctrl */ + bt541_data->ts_pinctrl = devm_pinctrl_get(&(bt541_data->client->dev)); + if (IS_ERR_OR_NULL(bt541_data->ts_pinctrl)) { + retval = PTR_ERR(bt541_data->ts_pinctrl); + dev_dbg(&bt541_data->client->dev, + "Target does not use pinctrl %d\n", retval); + goto err_pinctrl_get; + } + + bt541_data->pinctrl_state_active + = pinctrl_lookup_state(bt541_data->ts_pinctrl, + PINCTRL_STATE_ACTIVE); + if (IS_ERR_OR_NULL(bt541_data->pinctrl_state_active)) { + retval = PTR_ERR(bt541_data->pinctrl_state_active); + dev_err(&bt541_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_ACTIVE, retval); + goto err_pinctrl_lookup; + } + + bt541_data->pinctrl_state_suspend + = pinctrl_lookup_state(bt541_data->ts_pinctrl, + PINCTRL_STATE_SUSPEND); + if (IS_ERR_OR_NULL(bt541_data->pinctrl_state_suspend)) { + retval = PTR_ERR(bt541_data->pinctrl_state_suspend); + dev_err(&bt541_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_SUSPEND, retval); + goto err_pinctrl_lookup; + } + + bt541_data->pinctrl_state_release + = pinctrl_lookup_state(bt541_data->ts_pinctrl, + PINCTRL_STATE_RELEASE); + if (IS_ERR_OR_NULL(bt541_data->pinctrl_state_release)) { + retval = PTR_ERR(bt541_data->pinctrl_state_release); + dev_dbg(&bt541_data->client->dev, + "Can not lookup %s pinstate %d\n", + PINCTRL_STATE_RELEASE, retval); + } + + return 0; + +err_pinctrl_lookup: + devm_pinctrl_put(bt541_data->ts_pinctrl); +err_pinctrl_get: + bt541_data->ts_pinctrl = NULL; + return retval; +} + +#ifdef CONFIG_OF +static const struct of_device_id zinitix_match_table[] = { + { .compatible = "zinitix,bt541_ts_device",}, + {}, +}; +int zinitix_hw_reset( struct bt541_ts_info* data,bool on ) +{ + int err = 0; + printk("zinitix_hw_reset: on = %d, gpio_reset = %d\n", on, data->pdata->gpio_reset); + if(on) + { + if (gpio_is_valid(data->pdata->gpio_reset)) { + /* + * This is intended to save leakage current + * only. Even if the call(gpio_direction_input) + * fails, only leakage current will be more but + * functionality will not be affected. + */ + + + gpio_direction_output(data->pdata->gpio_reset, 1); + mdelay(20); + gpio_direction_output(data->pdata->gpio_reset, 0); + mdelay(100)/*eric modify 20170109*/; + gpio_direction_output(data->pdata->gpio_reset, 1); + //mdelay(10); + //err = gpio_direction_input(data->pdata->gpio_reset); + if (err) { + dev_err(&data->client->dev, + "unable to set direction for gpio " + "[%d]\n", data->pdata->gpio_reset); + } + //gpio_free(data->pdata->gpio_reset); + } + } + + return err; +} + +static int zinitix_init_gpio(struct bt541_ts_info* data,bool on ) +{ + int err = 0; + + if (on) { + if (gpio_is_valid(data->pdata->gpio_int)) { + err = gpio_request(data->pdata->gpio_int, + "bt541_irq_gpio"); + if (err) { + dev_err(&data->client->dev, + "irq gpio request failed"); + zinitix_printk("irq gpio request failed is %d\n",err); + goto err_irq_gpio_req; + } + + err = gpio_direction_input(data->pdata->gpio_int); + if (err) { + dev_err(&data->client->dev, + "set_direction for irq gpio failed\n"); + goto err_irq_gpio_req; + } + } + + //init power gpio + if (data->pdata->avdd_gpio > 0) { + if (gpio_is_valid(data->pdata->avdd_gpio)) { + err = gpio_request(data->pdata->avdd_gpio, + "bt541_avdd_gpio"); + if (err) { + dev_err(&data->client->dev, + "avdd gpio request failed"); + zinitix_printk("avdd gpio request failed is %d\n",err); + goto err_avdd_gpio_req; + } + + err = gpio_direction_output(data->pdata->avdd_gpio, 0); + if (err) { + dev_err(&data->client->dev, + "set_direction for avdd gpio failed\n"); + goto err_avdd_gpio_req; + } + } + } + + if (data->pdata->iovdd_gpio > 0) { + if (gpio_is_valid(data->pdata->iovdd_gpio)) { + err = gpio_request(data->pdata->iovdd_gpio, + "bt541_iovdd_gpio"); + if (err) { + dev_err(&data->client->dev, + "iovdd gpio request failed"); + zinitix_printk("iovdd gpio request failed is %d\n",err); + goto err_iovdd_gpio_req; + } + + err = gpio_direction_output(data->pdata->iovdd_gpio, 0); + if (err) { + dev_err(&data->client->dev, + "set_direction for avdd gpio failed\n"); + goto err_iovdd_gpio_req; + } + } + } + + return 0; + } + else { + if (gpio_is_valid(data->pdata->gpio_int)) + gpio_free(data->pdata->gpio_int); + if (gpio_is_valid(data->pdata->avdd_gpio)) + gpio_free(data->pdata->avdd_gpio); + if (gpio_is_valid(data->pdata->iovdd_gpio)) + gpio_free(data->pdata->iovdd_gpio); + + return 0; + } + +err_iovdd_gpio_req: + if (gpio_is_valid(data->pdata->iovdd_gpio)) + gpio_free(data->pdata->iovdd_gpio); +err_avdd_gpio_req: + if (gpio_is_valid(data->pdata->avdd_gpio)) + gpio_free(data->pdata->avdd_gpio); +err_irq_gpio_req: + if (gpio_is_valid(data->pdata->gpio_reset)) + gpio_free(data->pdata->gpio_reset); + + return err; +} +#endif + +static int bt541_ts_probe_dt(struct device_node *np, + struct device *dev, + struct bt541_ts_platform_data *pdata) +{ +#ifdef SUPPORTED_TOUCH_KEY_LED + int keyled_n = -1; + int size_p; +#endif + int ret = 0; + u32 temp; + const char *name_tmp; + + ret = of_property_read_u32(np, "zinitix,x_resolution", &temp); + if (ret) { + dev_err(dev, "Unable to read controller version zinitix,x_resolution\n"); + return ret; + } else + pdata->x_resolution = (u16) temp; + ret = of_property_read_u32(np, "zinitix,y_resolution", &temp); + if (ret) { + dev_err(dev, "Unable to read controller version zinitix,y_resolution\n"); + return ret; + } else + pdata->y_resolution = (u16) temp; + ret = of_property_read_u32(np, "zinitix,page_size", &temp); + if (ret) { + dev_err(dev, "Unable to read controller version zinitix,page_size\n"); + return ret; + } else + pdata->page_size = (u16) temp; + ret = of_property_read_u32(np, "zinitix,orientation", &temp); + if (ret) { + dev_err(dev, "Unable to read controller version zinitix,orientation\n"); + return ret; + } else + pdata->orientation = (u8) temp; + + pdata->tsp_vendor1 = of_get_named_gpio(np, "zinitix,vendor1", 0); + pdata->tsp_vendor2 = of_get_named_gpio(np, "zinitix,vendor2", 0); + +#ifdef SUPPORTED_TOUCH_KEY_LED + if (of_find_property(np, "keyled_gpio", &size_p)) { + keyled_n = of_get_named_gpio(np, "keyled_gpio", 0); + if (keyled_n < 0) { + pr_err("%s: of_get_named_gpio failed: keyled_gpio %d\n", __func__, + keyled_n); + return -EINVAL; + } + } + pdata->gpio_keyled = keyled_n; +#endif + pdata->gpio_reset = of_get_named_gpio(np, "zinitix,reset-gpio", 0); + if (pdata->gpio_reset < 0) { + pr_err("%s: of_get_named_gpio failed: tsp_gpio %d\n", __func__, + pdata->gpio_reset); + return -EINVAL; + } + + pdata->gpio_int = of_get_named_gpio(np, "zinitix,irq-gpio", 0); + if (pdata->gpio_int < 0) { + pr_err("%s: of_get_named_gpio failed: tsp_gpio %d\n", __func__, + pdata->gpio_int); + return -EINVAL; + } + + /* get ic name */ + ret = of_property_read_string(np, "ic_name", &name_tmp); + if (ret) { + pr_err("%s, get ic_name failed", __func__); + return -EINVAL; + } else { + pr_info("%s, ic_name from dt: %s", __func__, name_tmp); + strncpy(pdata->ic_name, name_tmp, sizeof(pdata->ic_name)); + } + + //Get power resource + pdata->avdd_gpio = of_get_named_gpio(np, "zinitix,avdd-gpio", 0); + if (pdata->avdd_gpio < 0) { + pr_err("%s: can't find avdd-gpio, use other power supply", __func__); + pdata->avdd_gpio = 0; + } else { + pr_info("get avdd-gpio[%d] from dt", pdata->avdd_gpio); + } + + pdata->iovdd_gpio = of_get_named_gpio(np, "zinitix,iovdd-gpio", 0); + if (pdata->iovdd_gpio < 0) { + pr_err("%s: can't find iovdd-gpio, use other power supply", __func__); + pdata->iovdd_gpio = 0; + } else { + pr_info("%s: get iovdd-gpio[%d] from dt", __func__, pdata->iovdd_gpio); + } + + memset(pdata->avdd_name, 0, sizeof(pdata->avdd_name)); + ret = of_property_read_string(np, "zinitix,avdd-name", &name_tmp); + if (!ret) { + pr_info("%s: avdd name from dt: %s", __func__, name_tmp); + if (strlen(name_tmp) < sizeof(pdata->avdd_name)) + strncpy(pdata->avdd_name, name_tmp, sizeof(pdata->avdd_name)); + else + pr_info("%s, invalied avdd name length: %ld > %ld", __func__, + strlen(name_tmp), sizeof(pdata->avdd_name)); + } + + memset(pdata->iovdd_name, 0, sizeof(pdata->iovdd_name)); + ret = of_property_read_string(np, "zinitix,iovdd-name", &name_tmp); + if (!ret) { + pr_info("%s: iovdd name from dt: %s", __func__, name_tmp); + if (strlen(name_tmp) < sizeof(pdata->iovdd_name)) + strncpy(pdata->iovdd_name, name_tmp, sizeof(pdata->iovdd_name)); + else + pr_info("%s, invalied iovdd name length: %ld > %ld", __func__, + strlen(name_tmp), sizeof(pdata->iovdd_name)); + } + + ret = of_property_read_u32(np, "zinitix,tsp_vdd_supply_type", + &pdata->tsp_supply_type); + if (ret < 0) { + pr_err("%s: failed to read property tsp_vdd_supply_type\n", + __func__); + return ret; + } + + ret = of_property_read_string(np, "zinitix,pname", + &pdata->pname); + if (ret < 0) { + pr_err("%s: failed to read property config_ver\n", + __func__); + return ret; + } + + pdata->stow_mode_ctrl = of_property_read_bool(np, "zinitix,stow-mode-ctrl"); + if (pdata->stow_mode_ctrl) + pr_info("%s: Support zinitix touch stow mode", __func__); + + if (pdata->tsp_supply_type == TSP_LDO_SUPPLY) { + pdata->tsp_en_gpio = of_get_named_gpio(np, "zinitix,vdd_en-gpio", 0); + if (pdata->tsp_en_gpio < 0) { + pr_err("%s: of_get_named_gpio failed: %d\n", __func__, + pdata->tsp_en_gpio); + return -EINVAL; + } + } + + pr_err("%s: x_resolution :%d, y_resolution :%d, page_size :%d, orientation :%d, gpio_int :%d, vcc_en :%d\n", + __func__, pdata->x_resolution, pdata->y_resolution, pdata->page_size, + pdata->orientation, pdata->gpio_int, pdata->tsp_en_gpio); + + return 0; +} + +#ifdef USE_TSP_TA_CALLBACKS +void bt541_register_callback(struct tsp_callbacks *cb) +{ + charger_callbacks = cb; + pr_info("%s\n", __func__); +} +#endif + +static int bt541_ts_probe(struct i2c_client *client, + const struct i2c_device_id *i2c_id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct bt541_ts_platform_data *pdata = NULL; + struct bt541_ts_info *info; + struct input_dev *input_dev; + int ret = 0; +#ifdef SUPPORTED_TOUCH_KEY + int i; +#endif + + struct device_node *np = client->dev.of_node; + zinitix_printk("[TSP]: %s\n", __func__); + + + if (client->dev.of_node) { + if (!pdata) { + pdata = devm_kzalloc(&client->dev, + sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + } + ret = bt541_ts_probe_dt(np, &client->dev, pdata); + if (ret){ + dev_err(&client->dev, "Error parsing dt %d\n", ret); + goto err_no_platform_data; + } + +#ifdef USE_TSP_TA_CALLBACKS + pdata->register_cb = bt541_register_callback; +#endif + + } else if (!pdata) { + dev_err(&client->dev, "%s: no platform data defined\n", + __func__); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "Not compatible i2c function\n"); + ret = -EIO; + goto err_no_platform_data; + } + + info = kzalloc(sizeof(struct bt541_ts_info), GFP_KERNEL); + if (!info) { + dev_err(&client->dev, "%s: Failed to allocate memory\n", __func__); + ret = -ENOMEM; + goto err_mem_alloc; + } + + i2c_set_clientdata(client, info); + info->client = client; + info->pdata = pdata; + info->device_enabled = 1; + misc_touch_dev = info; + + ret = zinitix_ts_pinctrl_init(info); + if (!ret && info->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. + */ + ret = pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_active); + if (ret < 0) { + dev_err(&client->dev, + "failed to select pin to active state"); + } + } + + ret = gpio_request(info->pdata->gpio_reset, + "bt541_reset_gpio"); + if (ret) { + dev_err(&info->client->dev, + "reset gpio request failed"); + zinitix_printk("reset gpio request failed is %d\n",ret); + } + + ret = zinitix_init_gpio(info,true); + if(ret < 0) + goto err_gpio_request; +#if 0 +/* Get pinctrl if target uses pinctrl */ + info->pinctrl = devm_pinctrl_get(&client->dev); + if (IS_ERR(info->pinctrl)) { + if (PTR_ERR(info->pinctrl) == -EPROBE_DEFER) + goto err_alloc; + + dev_info(&client->dev,"%s: Target does not use pinctrl\n", __func__); + info->pinctrl = NULL; + } + + if (info->pinctrl) { + ret = bt541_pinctrl_configure(info, true); + if (ret) + dev_info(&client->dev,"%s: cannot set pinctrl state\n", __func__); + } +#endif + + input_dev = input_allocate_device(); + if (!input_dev) { + dev_err(&client->dev, "Failed to allocate input device\n"); + ret = -ENOMEM; + goto err_alloc; + } + +#ifdef USE_TSP_TA_CALLBACKS + info->register_cb = info->pdata->register_cb; +#endif + + info->input_dev = input_dev; + info->work_state = PROBE; + + ret = zinitix_power_init(info,true); + if (ret) { + dev_err(&client->dev, "power init failed"); + goto err_input_register_device; + } + #if 0 + info->vddo_vreg = regulator_get(&info->client->dev,"vddo"); + if (IS_ERR(info->vddo_vreg)){ + info->vddo_vreg = NULL; + printk(KERN_INFO "info->vddo_vreg error\n"); + ret = -EPERM; + goto err_power_sequence; + } + if (pdata->tsp_supply_type == TSP_REGULATOR_SUPPLY) + { + info->vdd_en = regulator_get(&info->client->dev,"vdd_en"); + if (IS_ERR(info->vdd_en)){ + info->vdd_en = NULL; + printk(KERN_INFO "info->vdd_en error\n"); + ret = -EPERM; + goto err_power_sequence; + } + } + #endif + /* power on */ + if (bt541_power_control(info, POWER_ON_SEQUENCE) == false) { + ret = -EPERM; + goto err_power_sequence; + } + + /* To Do */ + /* FW version read from tsp */ + memset(&info->reported_touch_info, + 0x0, sizeof(struct point_info)); + + /* init touch mode */ + info->touch_mode = TOUCH_POINT_MODE; + + misc_info = info; + info->checkUMSmode = false; + + if (init_touch(info) == false) { + ret = -EPERM; + goto err_touch_init; + } + +#ifdef SUPPORTED_TOUCH_KEY + for (i = 0; i < MAX_SUPPORTED_BUTTON_NUM; i++) + info->button[i] = ICON_BUTTON_UNCHANGE; +#endif + +#ifdef USE_TSP_TA_CALLBACKS + info->callbacks.inform_charger = bt541_charger_status_cb; + if (info->register_cb) + info->register_cb(&info->callbacks); +#endif + + snprintf(info->phys, sizeof(info->phys), + "%s/input0", dev_name(&client->dev)); + input_dev->name = "zinitix_mt"; + input_dev->id.bustype = BUS_I2C; + input_dev->phys = info->phys; + input_dev->dev.parent = &client->dev; + + set_bit(EV_SYN, info->input_dev->evbit); + set_bit(EV_KEY, info->input_dev->evbit); + set_bit(EV_ABS, info->input_dev->evbit); + set_bit(BTN_TOUCH, info->input_dev->keybit); + +#ifndef CONFIG_INPUT_TOUCHSCREEN_MMI +#if SUPPORTED_PALM_TOUCH + //set_bit(KEY_POWER, info->input_dev->evbit); + //input_set_capability(info->input_dev, EV_KEY, KEY_LARGE); + input_set_capability(info->input_dev, EV_KEY, KEY_POWER); + dev_err(&client->dev,"KEY_POWER register finish\n"); +#endif +#endif + + set_bit(EV_LED, info->input_dev->evbit); + set_bit(LED_MISC, info->input_dev->ledbit); + + set_bit(INPUT_PROP_DIRECT, info->input_dev->propbit); + +#ifdef SUPPORTED_TOUCH_KEY + for (i = 0; i < MAX_SUPPORTED_BUTTON_NUM; i++) + set_bit(BUTTON_MAPPING_KEY[i], info->input_dev->keybit); +#endif + + if (pdata->orientation & TOUCH_XY_SWAP) { + input_set_abs_params(info->input_dev, ABS_MT_POSITION_Y, + info->cap_info.MinX, + info->cap_info.MaxX + ABS_PT_OFFSET, + 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_POSITION_X, + info->cap_info.MinY, + info->cap_info.MaxY + ABS_PT_OFFSET, + 0, 0); + } else { + input_set_abs_params(info->input_dev, ABS_MT_POSITION_X, + info->cap_info.MinX, + info->cap_info.MaxX + ABS_PT_OFFSET, + 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_POSITION_Y, + info->cap_info.MinY, + info->cap_info.MaxY + ABS_PT_OFFSET, + 0, 0); + } + + input_set_abs_params(info->input_dev, ABS_MT_TOUCH_MAJOR, + 0, 255, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_WIDTH_MAJOR, + 0, 255, 0, 0); + +#if (TOUCH_POINT_MODE == 2) + input_set_abs_params(info->input_dev, ABS_MT_TOUCH_MINOR, + 0, 255, 0, 0); + input_set_abs_params(info->input_dev, ABS_MT_PALM, + 0, 1, 0, 0); +#endif + + //info->input_dev->open = bt541_input_open; + //info->input_dev->close = bt541_input_close; + set_bit(MT_TOOL_FINGER, info->input_dev->keybit); + //wanzhiming + input_mt_init_slots(info->input_dev, info->cap_info.multi_fingers, INPUT_MT_DIRECT); + + zinitix_debug_msg("register %s input device \r\n", + info->input_dev->name); + input_set_drvdata(info->input_dev, info); + ret = input_register_device(info->input_dev); + if (ret) { + pr_err("unable to register %s input device\r\n", + info->input_dev->name); + goto err_touch_init; + } + + info->work_state = NOTHING; + sema_init(&info->work_lock, 1); + +#if ESD_TIMER_INTERVAL + spin_lock_init(&info->lock); + INIT_WORK(&info->tmr_work, ts_tmr_work); + esd_tmr_workqueue = + create_singlethread_workqueue("esd_tmr_workqueue"); + + if (!esd_tmr_workqueue) { + dev_err(&client->dev, "Failed to create esd tmr work queue\n"); + ret = -EPERM; + + goto err_input_register_device2; + } + + esd_timer_init(info); + esd_timer_start(CHECK_ESD_TIMER, info); +#if defined(TSP_VERBOSE_DEBUG) + dev_info(&client->dev, "Started esd timer\n"); +#endif +#endif +#if 0 + info->irq = irq_of_parse_and_map(client->dev.of_node, 0); + if (!info->irq) { + dev_err(&client->dev, "Failed to retrieve IRQ from device tree.\n"); + ret = -ENODEV; + goto err_irq_of_parse; + } +#endif + + /* configure irq */ + info->irq = gpio_to_irq(pdata->gpio_int); + if (info->irq < 0){ + dev_err(&client->dev, "Invalid GPIO_TOUCH_IRQ\n"); + ret = -ENODEV; + goto err_irq_of_parse; + } + info->irq_enabled = true; + + pdata->tsp_irq = info->irq; + ret = request_threaded_irq(info->irq, NULL, bt541_touch_work, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT , BT541_TS_DEVICE, info); + + if (ret) { + printk(KERN_ERR "unable to register irq.(%s)\r\n", + info->input_dev->name); + goto err_request_irq; + } +#if defined(CONFIG_FB) + info->fb_notif.notifier_call = fb_notifier_callback; + ret = fb_register_client(&info->fb_notif); + if (ret) + dev_err(&info->client->dev, + "Unable to register fb_notifier: %d\n", + ret); +#elif defined(CONFIG_HAS_EARLYSUSPEND) + info->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1; + info->early_suspend.suspend = zinitix_early_suspend; + info->early_suspend.resume = zinitix_late_resume; + register_early_suspend(&info->early_suspend); +#endif + + dev_info(&client->dev, "zinitix touch probe.\r\n"); + +#if defined(CONFIG_PM_RUNTIME) + //pm_runtime_enable(&client->dev); +#endif + + sema_init(&info->raw_data_lock, 1); + + ret = misc_register(&touch_misc_device); + if (ret) { + dev_err(&client->dev, "Failed to register touch misc device\n"); + goto err_misc_register; + } + +#ifdef SEC_FACTORY_TEST + ret = init_sec_factory(info); + if (ret) { + dev_err(&client->dev, "Failed to init sec factory device\n"); + + goto err_kthread_create_failed; + } +#endif + +#if USE_WAKEUP_GESTURE/*eric add 20170208*/ + + enable_irq_wake(misc_info->irq); +#endif + + init_completion(&info->pm_completion); + info->pm_suspend = false; + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + dev_info(&client->dev, "%s:zinitix_ts_mmi_dev_register", __func__); + ret = zinitix_ts_mmi_dev_register(&client->dev); + if (ret) { + dev_info(&client->dev, "%s: Failed register touchscreen mmi.", __func__); + goto err_ts_mmi_register; + } +#endif + + PM_WAKEUP_REGISTER(&client->dev, info->gesture_wakelock, + "zinitix_gesture_wakelock"); + if (!info->gesture_wakelock) { + dev_info(&client->dev, "%s: allocate gesture wakeup source err!\n", __func__); + ret = -ENOMEM; + goto err_register_gesture_wakelock; + } + +#ifdef SEC_FACTORY_TEST + init_completion(&info->builtin_cmcp_threshold_complete); + INIT_WORK(&info->cmcp_threshold_update, + zinitix_parse_cmcp_threshold_builtin); + schedule_work(&info->cmcp_threshold_update); +#endif + + dev_info(&client->dev, "bt541_ts_probe: SUCCESS\n"); + + return ret; + +err_register_gesture_wakelock: + PM_WAKEUP_UNREGISTER(info->gesture_wakelock); +err_ts_mmi_register: +#ifdef SEC_FACTORY_TEST +err_kthread_create_failed: + kfree(info->factory_info); + kfree(info->raw_data); +#endif +err_misc_register: + free_irq(info->irq, info); +err_irq_of_parse: +err_request_irq: +#if ESD_TIMER_INTERVAL +err_input_register_device2: +#endif + + input_unregister_device(info->input_dev); +err_touch_init: + bt541_power_control(info, POWER_OFF); +err_power_sequence: + zinitix_power_init(info, false); +err_input_register_device: + input_free_device(info->input_dev); +err_alloc: + if (gpio_is_valid(pdata->gpio_reset)) + gpio_free(pdata->gpio_reset); + if (gpio_is_valid(pdata->gpio_int)) + gpio_free(pdata->gpio_int); +err_gpio_request: +#ifdef SUPPORTED_TOUCH_KEY_LED + if (pdata->gpio_keyled >= 0) + gpio_free(pdata->gpio_keyled); +#endif + + if (info->ts_pinctrl) { + if (IS_ERR_OR_NULL(info->pinctrl_state_release)) { + devm_pinctrl_put(info->ts_pinctrl); + info->ts_pinctrl = NULL; + } else { + ret = pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_release); + if (ret) + pr_err("failed to select relase pinctrl state\n"); + } + } + kfree(info); +err_mem_alloc: +err_no_platform_data: + if (IS_ENABLED(CONFIG_OF)) + devm_kfree(&client->dev, (void *)pdata); + + dev_info(&client->dev, "Failed to probe\n"); + return ret; +} + +static int bt541_ts_remove(struct i2c_client *client) +{ + int err = 0; + struct bt541_ts_info *info = i2c_get_clientdata(client); + struct bt541_ts_platform_data *pdata = info->pdata; + + disable_irq(info->irq); + info->irq_enabled = false; + down(&info->work_lock); + + info->work_state = REMOVE; + +#ifdef SEC_FACTORY_TEST + kfree(info->factory_info); + kfree(info->raw_data); +#endif +#if ESD_TIMER_INTERVAL + flush_work(&info->tmr_work); + write_reg(info->client, BT541_PERIODICAL_INTERRUPT_INTERVAL, 0); + esd_timer_stop(info); +#if defined(TSP_VERBOSE_DEBUG) + dev_info(&client->dev, "Stopped esd timer\n"); +#endif + destroy_workqueue(esd_tmr_workqueue); +#endif + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI + dev_info(&client->dev, "%s:zinitix_ts_mmi_dev_unregister", __func__); + zinitix_ts_mmi_dev_unregister(&client->dev); +#endif + + if (info->irq) + free_irq(info->irq, info); + + misc_deregister(&touch_misc_device); + + bt541_power_control(info, POWER_OFF); + zinitix_power_init(info, false); + + if (gpio_is_valid(pdata->gpio_reset)) + gpio_free(pdata->gpio_reset); + if (gpio_is_valid(pdata->gpio_int)) + gpio_free(pdata->gpio_int); + +#ifdef SUPPORTED_TOUCH_KEY_LED + if ( gpio_is_valid(pdata->gpio_keyled)) + gpio_free(pdata->gpio_keyled); +#endif + + if (info->ts_pinctrl) { + if (IS_ERR_OR_NULL(info->pinctrl_state_release)) { + devm_pinctrl_put(info->ts_pinctrl); + info->ts_pinctrl = NULL; + } else { + err = pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_release); + if (err) + pr_err("failed to select relase pinctrl state\n"); + } + } + + + input_unregister_device(info->input_dev); + input_free_device(info->input_dev); + up(&info->work_lock); + kfree(info); + + return 0; +} + +/*void bt541_ts_shutdown(struct i2c_client *client) +{ + struct bt541_ts_info *info = i2c_get_clientdata(client); + + dev_info(&client->dev, "%s++\n", __func__); + disable_irq(info->irq); + down(&info->work_lock); +#if ESD_TIMER_INTERVAL + flush_work(&info->tmr_work); + esd_timer_stop(info); +#endif + up(&info->work_lock); + bt541_power_control(info, POWER_OFF); + dev_info(&client->dev, "%s--\n", __func__); +} +*/ +static struct i2c_device_id bt541_idtable[] = { + {BT541_TS_DEVICE, 0}, + { } +}; +#if (!defined(CONFIG_FB) && !defined(CONFIG_HAS_EARLYSUSPEND) \ + && !defined(CONFIG_INPUT_TOUCHSCREEN_MMI)) +static const struct dev_pm_ops zinitix_ts_dev_pm_ops = { + .suspend = bt541_ts_suspend, + .resume = bt541_ts_resume, +}; +#else +static const struct dev_pm_ops zinitix_ts_dev_pm_ops = { + .suspend = bt541_ts_suspend, + .resume = bt541_ts_resume, +}; +#endif +static struct i2c_driver bt541_ts_driver = { + .probe = bt541_ts_probe, + .remove = bt541_ts_remove, + #ifdef CONFIG_HAS_EARLYSUSPEND + .suspend = zinitix_early_suspend, + .resume = zinitix_late_resume, + #endif +// .shutdown = bt541_ts_shutdown, + .id_table = bt541_idtable, + .driver = { + .owner = THIS_MODULE, + .name = BT541_TS_DEVICE, + .of_match_table = zinitix_match_table, + #ifdef CONFIG_PM + .pm = &zinitix_ts_dev_pm_ops, + #endif + }, +}; + +static int __init bt541_ts_init(void) +{ + zinitix_printk("[TSP]: %s\n", __func__); + if (mmi_check_dynamic_device_node("bt541_ts_device")) + return i2c_add_driver(&bt541_ts_driver); + return -ENODEV; +} + +static void __exit bt541_ts_exit(void) +{ + zinitix_printk("[TSP]: %s\n", __func__); + i2c_del_driver(&bt541_ts_driver); +} + +module_init(bt541_ts_init); +//late_initcall(bt541_ts_init); +module_exit(bt541_ts_exit); + +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +MODULE_DESCRIPTION("touch-screen device driver using i2c interface"); +MODULE_AUTHOR(""); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/touchscreen/zinitix_ztw62x_mmi/zinitix_touch.h b/drivers/input/touchscreen/zinitix_ztw62x_mmi/zinitix_touch.h new file mode 100644 index 000000000000..2c29bac02b7b --- /dev/null +++ b/drivers/input/touchscreen/zinitix_ztw62x_mmi/zinitix_touch.h @@ -0,0 +1,347 @@ +/* + * + * Zinitix bt532 touch driver + * + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * + * 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. + * + */ + + +#ifndef _LINUX_BT541_TS_H +#define _LINUX_BT541_TS_H + +#include +#include +#include + +#define TS_DRVIER_VERSION "1.0.18_1" + +#define CONFIG_DATE "0304" + +#define TSP_TYPE_COUNT 1 + + +/* Upgrade Method*/ +#define TOUCH_ONESHOT_UPGRADE 0 +#define TOUCH_FORCE_UPGRADE 1 +#define USE_CHECKSUM 0 + + +#define USE_WAKEUP_GESTURE 0 + +#define SUPPORTED_PALM_TOUCH 1 + +#define MAX_SUPPORTED_FINGER_NUM 3 /* max 10 */ + +//#define TOUCH_POINT_FLAG +#ifdef TOUCH_POINT_FLAG +#define TOUCH_POINT_MODE 1 +#else +#define TOUCH_POINT_MODE 0 +#endif + + +#define CHIP_OFF_DELAY 70 /*ms*/ +#define CHIP_ON_DELAY 50 //100 /*ms*/ +#define FIRMWARE_ON_DELAY 50 //40 /*ms*/ + +#define DELAY_FOR_SIGNAL_DELAY 30 /*us*/ +#define DELAY_FOR_TRANSCATION 50 +#define DELAY_FOR_POST_TRANSCATION 10 + + +/* ESD Protection */ +/*second : if 0, no use. if you have to use, 3 is recommended*/ +#define ESD_TIMER_INTERVAL 0 +#define SCAN_RATE_HZ 100 +#define CHECK_ESD_TIMER 3 + + + +#define BT541_TS_DEVICE "bt541_ts_device" + + + /*Test Mode (Monitoring Raw Data) */ +#define SEC_DND_N_COUNT 10 +#define SEC_DND_U_COUNT 2 +#define SEC_DND_FREQUENCY 99 /* 200khz */ +#define SEC_PDND_N_COUNT 41 //>12 +#define SEC_PDND_U_COUNT 9 //6~12 +#define SEC_PDND_FREQUENCY 240 //79 + + +#ifdef SUPPORTED_TOUCH_KEY +#define NOT_SUPPORTED_TOUCH_DUMMY_KEY +#ifdef NOT_SUPPORTED_TOUCH_DUMMY_KEY +#define MAX_SUPPORTED_BUTTON_NUM 6 /* max 8 */ +#define SUPPORTED_BUTTON_NUM 4 +#else +#define MAX_SUPPORTED_BUTTON_NUM 6 /* max 8 */ +#define SUPPORTED_BUTTON_NUM 4 +#endif +#endif + +#define ZINITIX_MAX_STR_LABLE_LEN 64 +#define RAW_DATA_BUFFER_SIZE 1024 + +#define ZINITIX_DEBUG 0 +#define TSP_VERBOSE_DEBUG + +#define SEC_FACTORY_TEST +#define BURST_UPGRADE +#define VERIFY_CHECKSUM + +/*version offset on firmware*/ +#define FW_OFFSET_YEAR_MSB 44905 +#define FW_OFFSET_YEAR_LSB 44904 +#define FW_OFFSET_MONTH_MSB 44907 +#define FW_OFFSET_MONTH_LSB 44906 +#define FW_OFFSET_DAY_MSB 44909 +#define FW_OFFSET_DAY_LSB 44908 +#define FW_OFFSET_TOTAL_VERSION_MSB 61 +#define FW_OFFSET_TOTAL_VERSION_LSB 60 + +#define zinitix_debug_msg(fmt, args...) \ + do { \ + if (m_ts_debug_mode) \ + printk(KERN_ERR "bt541_ts[%-18s:%5d] " fmt, \ + __func__, __LINE__, ## args); \ + } while (0); + +#define zinitix_printk(fmt, args...) \ + do { \ + printk(KERN_ERR "bt541_ts[%-18s:%5d] " fmt, \ + __func__, __LINE__, ## args); \ + } while (0); + +#define bt541_err(fmt) \ + do { \ + pr_err("bt541_ts : %s " fmt, __func__); \ + } while (0); + +#define FILE_NAME_LENGTH 128 +#define MAX_RAW_DATA_SZ 576 /* 32x18 */ +#define MAX_TRAW_DATA_SZ \ + (MAX_RAW_DATA_SZ + 4*MAX_SUPPORTED_FINGER_NUM + 2) + +/* For commnication error in PM(deep sleep) state */ +#define ZINITIX_TIMEOUT_COMERR_PM 700 + +extern volatile int tpd_halt; + +enum power_control { + POWER_OFF, + POWER_ON, + POWER_ON_SEQUENCE, +}; + +enum work_state { + NOTHING = 0, + NORMAL, + ESD_TIMER, + SUSPEND, + RESUME, + UPGRADE, + REMOVE, + SET_MODE, + HW_CALIBRAION, + RAW_DATA, + PROBE, +}; + +struct bt541_ts_platform_data { + int gpio_reset; + int gpio_int; + //u32 gpio_scl; + //u32 gpio_sda; + //u32 gpio_ldo_en; + u32 tsp_irq; +#ifdef SUPPORTED_TOUCH_KEY_LED + int gpio_keyled; +#endif + int tsp_vendor1; + int tsp_vendor2; + int tsp_en_gpio; + u32 tsp_supply_type; + //int (*tsp_power)(int on); + u16 x_resolution; + u16 y_resolution; + u16 page_size; + u8 orientation; + const char *pname; +#ifdef USE_TSP_TA_CALLBACKS + void (*register_cb) (struct tsp_callbacks *); + struct tsp_callbacks callbacks; +#endif + char avdd_name[ZINITIX_MAX_STR_LABLE_LEN]; + char iovdd_name[ZINITIX_MAX_STR_LABLE_LEN]; + int avdd_gpio; + int iovdd_gpio; + char ic_name[ZINITIX_MAX_STR_LABLE_LEN]; + bool stow_mode_ctrl; +}; + +struct coord { + u16 x; + u16 y; + u8 width; + u8 sub_status; +#if (TOUCH_POINT_MODE == 2) + u8 minor_width; + u8 angle; +#endif +}; + +struct point_info { + u16 status; +#if (TOUCH_POINT_MODE == 1) + u16 event_flag; +#else + u8 finger_cnt; + u8 time_stamp; +#endif + struct coord coord[MAX_SUPPORTED_FINGER_NUM]; +}; + +struct capa_info { + u16 vendor_id; + u16 ic_revision; + u16 fw_version; + u16 fw_minor_version; + u16 fw_day_version; + u16 reg_data_version; + u16 threshold; + u16 key_threshold; + u16 dummy_threshold; + u32 ic_fw_size; + u32 MaxX; + u32 MaxY; + u32 MinX; + u32 MinY; + u8 gesture_support; + u16 multi_fingers; + u16 button_num; + u16 ic_int_mask; + u16 x_node_num; + u16 y_node_num; + u16 total_node_num; + u16 hw_id; + u16 afe_frequency; + u16 i2s_checksum; + u16 shift_value; + u16 N_cnt; + u16 u_cnt; +}; + +struct ts_test_params { + s32 max_raw_limits[RAW_DATA_BUFFER_SIZE]; + s32 min_raw_limits[RAW_DATA_BUFFER_SIZE]; + s32 short_threshold; + u8 raw_data_pass; + u8 short_data_pass; +}; + +struct bt541_ts_info { + struct i2c_client *client; + struct input_dev *input_dev; + struct bt541_ts_platform_data *pdata; + struct pinctrl *ts_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct pinctrl_state *pinctrl_state_release; + + char phys[32]; + + struct capa_info cap_info; + struct point_info touch_info; + struct point_info reported_touch_info; + u16 icon_event_reg; + u16 prev_icon_event; + + int irq; +#ifdef SUPPORTED_TOUCH_KEY + u8 button[MAX_SUPPORTED_BUTTON_NUM]; +#endif + u8 work_state; + struct semaphore work_lock; + + u8 finger_cnt1; + +#ifdef USE_TSP_TA_CALLBACKS + void (*register_cb) (struct tsp_callbacks *tsp_cb); + struct tsp_callbacks callbacks; +#endif + +#if ESD_TIMER_INTERVAL + struct work_struct tmr_work; + struct timer_list esd_timeout_tmr; + struct timer_list *p_esd_timeout_tmr; + spinlock_t lock; +#endif +#if defined(CONFIG_FB) + struct notifier_block fb_notif; +#elif defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_suspend; +#endif + + struct semaphore raw_data_lock; + u16 touch_mode; + s16 cur_data[MAX_TRAW_DATA_SZ]; + u8 update; +#ifdef SEC_FACTORY_TEST + struct tsp_factory_info *factory_info; + struct tsp_raw_data *raw_data; +#endif + struct regulator *vdd; + struct regulator *vcc_i2c; + bool device_enabled; + bool checkUMSmode; + bool irq_enabled; + bool gesture_enabled; + u16 gesture_command; +#ifdef CONFIG_HAS_WAKELOCK + struct wake_lock gesture_wakelock; +#else + struct wakeup_source *gesture_wakelock; +#endif + +#if defined(CONFIG_INPUT_TOUCHSCREEN_MMI) + struct ts_mmi_class_methods *imports; +#endif + struct work_struct cmcp_threshold_update; + struct completion builtin_cmcp_threshold_complete; + struct ts_test_params test_params; + int builtin_cmcp_threshold_status; + const char *supplier; + struct delayed_work work; + int ts_mmi_power_state; + struct completion pm_completion; + bool pm_suspend; + atomic_t set_stowed_state; + atomic_t get_stowed_state; + int ic_power_state; + struct mutex mode_lock; +}; + +extern int zinitix_hw_reset( struct bt541_ts_info* data,bool on ); +extern bool bt541_power_control(struct bt541_ts_info *info, u8 ctl); +extern int zinitix_read_file(char *file_name, u8 **file_buf); +extern int ts_upgrade_sequence(const u8 *firmware_data); +extern void clear_report_data(struct bt541_ts_info *info); +extern int zinitix_ts_mmi_gesture_suspend(struct device *dev); +extern int zinitix_ts_mmi_gesture_resume(struct device *dev); +extern bool mini_init_touch(struct bt541_ts_info *info); +extern int zinitix_ts_mmi_disable_gesture(struct device *dev); +extern int zinitix_ts_mmi_restore_gesture(struct device *dev); + +#endif /* LINUX_BT541_TS_H */ + diff --git a/drivers/input/touchscreen/zinitix_ztw62x_mmi/zinitix_touch_zxt_firmware.h b/drivers/input/touchscreen/zinitix_ztw62x_mmi/zinitix_touch_zxt_firmware.h new file mode 100644 index 000000000000..1decdb70eaef --- /dev/null +++ b/drivers/input/touchscreen/zinitix_ztw62x_mmi/zinitix_touch_zxt_firmware.h @@ -0,0 +1,5663 @@ +/* +* +* Zinitix touch driver +* +* Copyright (C) 2009 Zinitix, Inc. +* +* 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. +* +*/ +/* +Release Date : 16/10/13, 10:42 + +Version Info : + Chip Code : 0xE532 + Firmware Version : 0x0005 + Firmware Minor Version : 0x0009 + Register Version : 82 +*/ +#ifndef _ZINITIX_ZXT_CODE_H +#define _ZINITIX_ZXT_CODE_H + +u8 m_firmware_data[] = { +0x48, 0x00, 0x02, 0x4E, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x80, 0x05, 0x05, 0x00, 0x00, 0x00, +0x09, 0x00, 0x80, 0x09, 0x52, 0x00, 0x00, 0x00, +0x32, 0xE5, 0xE5, 0x32, 0x44, 0x00, 0x00, 0x00, +0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, +0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, +0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, +0x44, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xFC, 0x40, 0xEE, 0x20, 0x3C, 0x03, 0xFD, 0x3C, +0x96, 0x43, 0x8E, 0x26, 0xE6, 0x2D, 0x4E, 0xF2, +0x00, 0xD9, 0x46, 0xF0, 0x02, 0x00, 0x58, 0xF7, +0x80, 0xA4, 0x38, 0x07, 0x85, 0x01, 0x40, 0xF0, +0x3C, 0x00, 0xDD, 0x0F, 0x2A, 0x00, 0x2A, 0x00, +0xEC, 0x01, 0x3C, 0x00, 0x9C, 0x01, 0x4C, 0x00, +0x7A, 0x01, 0x82, 0x01, 0x90, 0x01, 0x96, 0x01, +0x9C, 0x01, 0x1A, 0x00, 0x1A, 0x00, 0x84, 0x00, +0x3C, 0x13, 0xF8, 0x33, 0x3E, 0x58, 0x02, 0x80, +0x80, 0x40, 0x48, 0x00, 0x00, 0x7C, 0x3E, 0x08, +0x02, 0x80, 0x3C, 0x13, 0xF8, 0x33, 0x49, 0x00, +0x3C, 0x1F, 0x49, 0x00, 0x39, 0xCC, 0xD5, 0x6E, +0x3E, 0x08, 0x02, 0x80, 0x3C, 0x13, 0xF8, 0x33, +0x49, 0x00, 0x3C, 0x16, 0x48, 0x00, 0x00, 0x96, +0x3C, 0xFD, 0xFA, 0xAE, 0x3D, 0xED, 0xFA, 0xAA, +0x3C, 0x4D, 0xFA, 0xAC, 0x80, 0x0F, 0x80, 0x3E, +0x84, 0x7F, 0x84, 0xC0, 0x04, 0x87, 0x80, 0x06, +0x04, 0xA7, 0x80, 0x07, 0x04, 0x92, 0x00, 0x03, +0xA1, 0xCB, 0xA8, 0xC6, 0xA8, 0xC7, 0xA9, 0xA3, +0xA9, 0x8B, 0x49, 0x00, 0x39, 0xAC, 0x80, 0x1F, +0x3C, 0x13, 0xF8, 0x33, 0x49, 0x00, 0x3B, 0xF8, +0x49, 0x00, 0x39, 0xA5, 0x3E, 0x08, 0x04, 0x60, +0x3C, 0x13, 0xF8, 0x33, 0x49, 0x00, 0x3B, 0xF0, +0x3C, 0x33, 0xF8, 0x33, 0x3E, 0x08, 0x04, 0x60, +0xE0, 0xC3, 0xE8, 0x0B, 0x23, 0xE0, 0x00, 0x00, +0x38, 0x1F, 0x99, 0x11, 0x8C, 0xC1, 0x88, 0x3E, +0x90, 0x21, 0x1A, 0x10, 0x00, 0x01, 0xD5, 0xF5, +0x3C, 0x0D, 0xFA, 0xAE, 0x14, 0x80, 0x00, 0x06, +0x3C, 0x8D, 0xFA, 0xAC, 0x14, 0xA0, 0x00, 0x07, +0x14, 0x94, 0x00, 0x03, 0x3C, 0x9D, 0xFA, 0xAA, +0x14, 0x74, 0x80, 0x03, 0x49, 0x00, 0x39, 0x7F, +0x80, 0x1F, 0x3C, 0x13, 0xF8, 0x33, 0x49, 0x00, +0x3B, 0xCB, 0x49, 0x00, 0x39, 0x78, 0x3C, 0x13, +0xF8, 0x33, 0x3E, 0x08, 0x02, 0x80, 0x49, 0x00, +0x3B, 0xC3, 0x84, 0xE0, 0x3C, 0xA3, 0xF8, 0x33, +0x3E, 0x18, 0x02, 0x80, 0xE0, 0xEA, 0xE8, 0x0E, +0x22, 0x50, 0x80, 0x00, 0x38, 0x2F, 0x9D, 0x11, +0x02, 0x40, 0x80, 0xF0, 0x88, 0x45, 0x90, 0x41, +0x8A, 0x44, 0x1A, 0x20, 0x80, 0x01, 0x8C, 0xE1, +0xD5, 0xF2, 0x84, 0x00, 0x49, 0x00, 0x3C, 0xD3, +0xD5, 0x68, 0xE0, 0x01, 0x3E, 0x68, 0x02, 0x80, +0xE8, 0x05, 0x38, 0x22, 0x81, 0x09, 0x8C, 0x01, +0xD5, 0xF9, 0x50, 0x03, 0x05, 0xA0, 0x50, 0x13, +0x05, 0xC8, 0x49, 0x00, 0x3C, 0x50, 0x3C, 0x23, +0xF8, 0x35, 0x81, 0x46, 0x80, 0xA2, 0x85, 0x20, +0xE1, 0x22, 0xE8, 0x07, 0x03, 0xE5, 0x02, 0xE4, +0x8D, 0x21, 0x1B, 0xE5, 0x00, 0x01, 0xD5, 0xF9, +0x3C, 0x33, 0xF8, 0x34, 0x88, 0x62, 0xE0, 0xA3, +0xE8, 0x0A, 0x9B, 0x2A, 0x95, 0xE1, 0x88, 0xE6, +0x22, 0x83, 0x82, 0xD0, 0x38, 0x83, 0x15, 0x09, +0x8C, 0xA1, 0xD5, 0xF6, 0x49, 0x00, 0x38, 0x58, +0x49, 0x00, 0x39, 0x2D, 0xD5, 0x3A, 0x84, 0x00, +0x80, 0x20, 0x80, 0x40, 0xD5, 0x05, 0x3C, 0x10, +0x00, 0x3A, 0x84, 0x01, 0x84, 0x5F, 0x49, 0x00, +0x08, 0x76, 0xD5, 0x2F, 0x49, 0x00, 0x0A, 0xE2, +0xD5, 0x2C, 0x49, 0x00, 0x07, 0xD9, 0xD5, 0x29, +0x49, 0x00, 0x06, 0x09, 0x49, 0x00, 0x3D, 0xE0, +0x80, 0xC0, 0x49, 0x00, 0x31, 0xED, 0x3C, 0x33, +0xFA, 0xC6, 0x2E, 0x27, 0xFA, 0x1E, 0x2E, 0x0F, +0xFA, 0x1F, 0x96, 0xE6, 0x97, 0x13, 0x97, 0xD9, +0x3C, 0x48, 0x00, 0xBC, 0x3C, 0x08, 0x00, 0xBD, +0xC7, 0x05, 0x85, 0xEA, 0x3C, 0xF8, 0x00, 0xBE, +0xD5, 0x03, 0x3C, 0x78, 0x00, 0xBE, 0xCE, 0x06, +0x3C, 0x80, 0x01, 0x31, 0x8D, 0x01, 0x3C, 0x88, +0x01, 0x31, 0x3C, 0x94, 0x07, 0x9B, 0x3C, 0x9B, +0xFD, 0x49, 0x49, 0x00, 0x10, 0x95, 0xD5, 0x0C, +0x44, 0x00, 0x00, 0x64, 0x49, 0x00, 0x03, 0x94, +0x49, 0x00, 0x10, 0x8E, 0x3C, 0x53, 0xFD, 0xAB, +0x96, 0x2B, 0x49, 0x00, 0x01, 0x6E, 0xED, 0xE0, +0xFC, 0xC0, 0x3C, 0x00, 0x01, 0x2D, 0xC8, 0x08, +0x3C, 0x43, 0xFE, 0x42, 0x44, 0x5F, 0xFF, 0xEF, +0x96, 0xE3, 0xFE, 0xEE, 0xD5, 0x06, 0x3C, 0x13, +0xFE, 0x42, 0x96, 0x8B, 0x58, 0x31, 0x00, 0x10, +0x2E, 0x07, 0xF5, 0xED, 0x3C, 0x3B, 0xFE, 0x42, +0xC8, 0x08, 0x3C, 0x43, 0xFE, 0x42, 0x44, 0x5F, +0xFE, 0xFF, 0x96, 0xE3, 0xFE, 0xEE, 0xD5, 0x06, +0x3C, 0x13, 0xFE, 0x42, 0x96, 0x8B, 0x58, 0x31, +0x01, 0x00, 0x2E, 0x07, 0xF5, 0xF9, 0x3C, 0x3B, +0xFE, 0x42, 0xC8, 0x08, 0x3C, 0x43, 0xFE, 0x42, +0x44, 0x5F, 0xFD, 0xFF, 0x96, 0xE3, 0xFE, 0xEE, +0xD5, 0x06, 0x3C, 0x13, 0xFE, 0x42, 0x96, 0x8B, +0x58, 0x31, 0x02, 0x00, 0x3C, 0x3B, 0xFE, 0x42, +0xDD, 0x9E, 0xFC, 0x20, 0x49, 0x00, 0x0B, 0x76, +0x84, 0x00, 0x3E, 0x4F, 0xF5, 0x8C, 0x80, 0x60, +0x98, 0x84, 0x8C, 0x06, 0x84, 0x20, 0x10, 0x31, +0x00, 0x09, 0x5A, 0x08, 0x3C, 0xFB, 0x84, 0xA0, +0x84, 0xC0, 0x3C, 0x5B, 0xFA, 0xC3, 0x3C, 0x6F, +0xFD, 0x5F, 0x3C, 0x6F, 0xFD, 0x60, 0x3E, 0x57, +0xF5, 0x84, 0x3E, 0x17, 0xEA, 0x71, 0x3E, 0x17, +0xEA, 0x70, 0x84, 0xE0, 0x49, 0x00, 0x37, 0xC2, +0x49, 0x00, 0x37, 0xB9, 0x49, 0x00, 0x2E, 0xB2, +0x3E, 0x77, 0xF5, 0xCE, 0x49, 0x00, 0x38, 0x8B, +0x84, 0xEA, 0x49, 0xFF, 0xFF, 0xA4, 0x49, 0x00, +0x05, 0x76, 0x96, 0x00, 0x5A, 0x00, 0x01, 0xEF, +0x5A, 0x00, 0x02, 0xF9, 0x2E, 0x67, 0xF5, 0xCD, +0xC6, 0x07, 0x87, 0xC0, 0x3F, 0xE7, 0xF5, 0xCD, +0x49, 0x00, 0x06, 0x8B, 0xD5, 0xE3, 0x2E, 0x87, +0xF5, 0xCE, 0x5A, 0x80, 0x02, 0xE0, 0x5A, 0x88, +0x03, 0x0B, 0x49, 0x00, 0x3E, 0xF6, 0x3E, 0x67, +0xF5, 0xCE, 0x3C, 0x83, 0xFD, 0x3C, 0x5A, 0x80, +0x07, 0xFE, 0xD5, 0xE0, 0x5A, 0x88, 0x01, 0x12, +0x3C, 0xF3, 0xFD, 0x3C, 0x5A, 0xF0, 0x07, 0x0E, +0x3C, 0x43, 0xFD, 0xA7, 0x96, 0x23, 0x49, 0x00, +0x2B, 0x1F, 0x80, 0x06, 0x3E, 0x67, 0xF5, 0xCE, +0x3C, 0x6B, 0xFA, 0xC6, 0x49, 0x00, 0x2E, 0x29, +0x3C, 0x33, 0xFD, 0x3C, 0x96, 0x9B, 0xE4, 0x43, +0xE9, 0x04, 0x49, 0xFF, 0xFE, 0x57, 0xD5, 0xC6, +0x49, 0x00, 0x3D, 0x16, 0x80, 0xC0, 0x49, 0x00, +0x31, 0x23, 0x3C, 0x03, 0xFA, 0xC6, 0x2E, 0x17, +0xFA, 0x1E, 0x2F, 0xCF, 0xFA, 0x1F, 0x96, 0x26, +0x97, 0x4B, 0x97, 0x01, 0x3C, 0x58, 0x00, 0xBC, +0x3D, 0xC8, 0x00, 0xBD, 0xC4, 0x04, 0x3C, 0x78, +0x00, 0xBE, 0xD5, 0x03, 0x3C, 0x48, 0x00, 0xBE, +0xCE, 0x06, 0x3C, 0x80, 0x01, 0x31, 0x8D, 0x01, +0x3C, 0x88, 0x01, 0x31, 0x3D, 0xE4, 0x07, 0x9B, +0x3C, 0x34, 0x08, 0xD0, 0x3D, 0xEB, 0xFD, 0x49, +0x4E, 0x36, 0xFF, 0xA1, 0x49, 0x00, 0x02, 0x7D, +0x96, 0x80, 0x5A, 0x20, 0x01, 0x90, 0x5A, 0x20, +0x02, 0x9A, 0x3C, 0x60, 0x07, 0x99, 0x97, 0x86, +0xC6, 0x05, 0x2E, 0xF7, 0xEA, 0x79, 0x5A, 0xF0, +0x02, 0x92, 0x3C, 0x10, 0x07, 0x97, 0x96, 0x46, +0xC1, 0x23, 0x3C, 0x60, 0x01, 0x2D, 0xCE, 0x20, +0x49, 0x00, 0x37, 0x3A, 0x2E, 0x57, 0xEA, 0x71, +0x5A, 0x58, 0x01, 0x08, 0x2E, 0x27, 0xEA, 0x70, +0xCA, 0x17, 0xD5, 0x11, 0x8C, 0xC1, 0x97, 0xB3, +0x3C, 0x03, 0xFD, 0xA8, 0x97, 0x03, 0xE0, 0xC4, +0xE8, 0x0F, 0x49, 0x00, 0x37, 0x29, 0x2F, 0xC7, +0xEA, 0x71, 0x5B, 0xC8, 0x01, 0xF5, 0x2E, 0x37, +0xEA, 0x70, 0xCB, 0xF1, 0x3C, 0x10, 0x00, 0xE1, +0x96, 0x0B, 0x49, 0x00, 0x04, 0x65, 0x49, 0x00, +0x0F, 0x79, 0x49, 0x00, 0x0F, 0x4D, 0x48, 0xFF, +0xFF, 0x66, 0x92, 0x00, 0x47, 0xD0, 0x04, 0x02, +0x59, 0xDE, 0x8E, 0x90, 0x3F, 0xC8, 0x00, 0x00, +0x3F, 0xF8, 0x21, 0x60, 0x49, 0x00, 0x0B, 0x18, +0x48, 0xFF, 0xFF, 0x31, 0xEF, 0xF8, 0x3A, 0x1F, +0x88, 0x20, 0x4E, 0x05, 0x00, 0x19, 0x3C, 0x13, +0xF8, 0x35, 0x20, 0xFF, 0x80, 0x00, 0x40, 0x40, +0x04, 0x16, 0xE0, 0x8F, 0xE9, 0x10, 0x20, 0x2F, +0x80, 0x01, 0xE0, 0x44, 0xE9, 0x0C, 0x20, 0x1F, +0x80, 0x02, 0x97, 0x41, 0xE0, 0xA1, 0xE9, 0x07, +0x20, 0x3F, 0x80, 0x03, 0xE0, 0x65, 0x56, 0x07, +0x80, 0x01, 0xD5, 0x02, 0x84, 0x00, 0xEC, 0x08, +0xDD, 0x9E, 0xE6, 0x02, 0xE9, 0x0A, 0xFA, 0x4E, +0x84, 0x21, 0x40, 0x30, 0x04, 0x77, 0x88, 0x23, +0x8E, 0x41, 0x92, 0x21, 0xCA, 0xFB, 0x80, 0x01, +0xDD, 0x9E, 0x96, 0x83, 0x96, 0xCB, 0x90, 0x10, +0x8A, 0x43, 0x90, 0x30, 0x9A, 0x41, 0xEF, 0xF8, +0x42, 0x01, 0x08, 0x24, 0x42, 0x00, 0x84, 0x73, +0xEC, 0x08, 0x48, 0xFF, 0xFF, 0xE8, 0xEF, 0xF0, +0xB1, 0x02, 0x44, 0xF0, 0x00, 0x64, 0x3A, 0x02, +0x04, 0x20, 0x44, 0x4F, 0xFF, 0x9C, 0x3A, 0x2F, +0x8C, 0x20, 0x40, 0x00, 0x3C, 0x16, 0x40, 0x21, +0x10, 0x56, 0x88, 0x02, 0x40, 0x10, 0xBC, 0x36, +0x40, 0x31, 0x90, 0x76, 0x88, 0x61, 0xFE, 0x04, +0x42, 0x01, 0x8C, 0x73, 0xEC, 0x10, 0x48, 0xFF, +0xFF, 0xCE, 0x96, 0x03, 0x96, 0x4B, 0x8A, 0x01, +0xEF, 0xF8, 0x4E, 0x04, 0x00, 0x03, 0xFE, 0x02, +0xEC, 0x08, 0xDD, 0x9E, 0x90, 0x10, 0x90, 0x30, +0x8A, 0x01, 0xEF, 0xF8, 0x4E, 0x04, 0x00, 0x03, +0xFE, 0x02, 0xEC, 0x08, 0xDD, 0x9E, 0xEF, 0xF0, +0x12, 0x0F, 0x80, 0x03, 0x22, 0x0F, 0x80, 0x03, +0x4E, 0x06, 0x00, 0x03, 0xD5, 0x18, 0xD5, 0x13, +0xFA, 0x04, 0xF0, 0x83, 0xD5, 0x06, 0x40, 0x00, +0x00, 0x09, 0xF0, 0x03, 0x8E, 0x01, 0xF0, 0x83, +0xF0, 0x03, 0x4E, 0x06, 0xFF, 0xFA, 0x22, 0x0F, +0x80, 0x03, 0x96, 0x01, 0x8E, 0x01, 0x96, 0x01, +0x12, 0x0F, 0x80, 0x03, 0x22, 0x0F, 0x80, 0x03, +0x4E, 0x06, 0xFF, 0xEC, 0xEC, 0x10, 0xDD, 0x9E, +0xEF, 0xF0, 0x12, 0x0F, 0x80, 0x03, 0x22, 0x0F, +0x80, 0x03, 0x4E, 0x06, 0x00, 0x03, 0xD5, 0x18, +0xD5, 0x13, 0x84, 0x0A, 0xF0, 0x83, 0xD5, 0x06, +0x40, 0x00, 0x00, 0x09, 0xF0, 0x03, 0x8E, 0x01, +0xF0, 0x83, 0xF0, 0x03, 0x4E, 0x06, 0xFF, 0xFA, +0x22, 0x0F, 0x80, 0x03, 0x96, 0x01, 0x8E, 0x01, +0x96, 0x01, 0x12, 0x0F, 0x80, 0x03, 0x22, 0x0F, +0x80, 0x03, 0x4E, 0x06, 0xFF, 0xEC, 0xEC, 0x10, +0xDD, 0x9E, 0x80, 0x60, 0x9B, 0x18, 0x97, 0x61, +0xE2, 0xA2, 0xE8, 0x08, 0xB4, 0x81, 0x8C, 0x61, +0x97, 0x60, 0x10, 0x51, 0xFF, 0xFF, 0x8C, 0x24, +0xD5, 0xF6, 0xDD, 0x9E, 0x80, 0x61, 0x9B, 0x19, +0x97, 0x61, 0xE2, 0xA2, 0xE8, 0x08, 0xA7, 0x18, +0x8C, 0x04, 0x97, 0x60, 0x14, 0x50, 0x7F, 0xFF, +0x8C, 0x61, 0xD5, 0xF6, 0xDD, 0x9E, 0x80, 0x62, +0x9B, 0x1A, 0x97, 0x61, 0xE2, 0xA0, 0xE8, 0x08, +0xB4, 0x81, 0x8C, 0x61, 0x97, 0x60, 0x10, 0x51, +0xFF, 0xFF, 0x8C, 0x24, 0xD5, 0xF6, 0xDD, 0x9E, +0x92, 0x01, 0x84, 0x60, 0x8A, 0x41, 0xE0, 0x60, +0x99, 0x4A, 0xE8, 0x07, 0xA5, 0x08, 0x8C, 0x61, +0x97, 0x21, 0xAD, 0x28, 0x8C, 0x22, 0xD5, 0xF8, +0xDD, 0x9E, 0x00, 0x00, 0x3E, 0x08, 0x0F, 0x2C, +0x44, 0x2F, 0xFE, 0xFF, 0xA4, 0x40, 0xA4, 0xC1, +0x85, 0xE0, 0x58, 0x40, 0x80, 0xC4, 0xFE, 0x9E, +0xFA, 0x2E, 0x44, 0x50, 0x27, 0x10, 0xAC, 0x81, +0xAD, 0x00, 0x12, 0x50, 0x00, 0x1D, 0x12, 0xF0, +0x00, 0x19, 0x12, 0xF0, 0x00, 0x1A, 0x12, 0xF0, +0x00, 0x1E, 0x12, 0x10, 0x00, 0x14, 0x12, 0x10, +0x00, 0x15, 0x12, 0xF0, 0x00, 0x18, 0x12, 0xF0, +0x00, 0x21, 0xDD, 0x9E, 0x3E, 0x0F, 0xF3, 0x60, +0x84, 0x40, 0x84, 0x20, 0x10, 0x10, 0x02, 0x24, +0x14, 0x20, 0x00, 0x87, 0x14, 0x20, 0x00, 0x88, +0xDD, 0x9E, 0xFC, 0x00, 0x3E, 0x1F, 0xF5, 0x8C, +0x84, 0x00, 0x80, 0x60, 0x80, 0x81, 0x84, 0xA6, +0x3C, 0x23, 0xFD, 0x41, 0x97, 0x93, 0xE0, 0x66, +0xE8, 0x23, 0x83, 0x84, 0x43, 0xC1, 0x94, 0x73, +0x02, 0x2E, 0x00, 0x04, 0x92, 0x48, 0x96, 0x86, +0xC2, 0x18, 0x3C, 0x23, 0xFD, 0xFB, 0xA5, 0x8A, +0x96, 0x93, 0xE0, 0xC2, 0xE9, 0x12, 0x3C, 0x23, +0xFD, 0xFE, 0x96, 0x93, 0xE0, 0x46, 0xE9, 0x0D, +0x3C, 0x23, 0xFD, 0x9C, 0xA5, 0x8B, 0x96, 0x93, +0xE0, 0xC2, 0xE9, 0x07, 0x3C, 0x23, 0xFD, 0x9D, +0x96, 0x93, 0xE0, 0x46, 0xE9, 0x02, 0x8C, 0x01, +0x8C, 0x61, 0x8C, 0x26, 0xD5, 0xDA, 0xFC, 0x80, +0x84, 0x00, 0x80, 0x40, 0x3E, 0x3F, 0xF5, 0x8C, +0x84, 0x86, 0x3C, 0x13, 0xFD, 0x41, 0x97, 0x4B, +0xE0, 0x45, 0xE8, 0x0B, 0x80, 0x23, 0x42, 0x11, +0x10, 0x73, 0xA5, 0x4C, 0x92, 0xA8, 0x97, 0x46, +0xC5, 0x02, 0x8C, 0x01, 0x8C, 0x41, 0xD5, 0xF2, +0xDD, 0x9E, 0x84, 0x40, 0x3E, 0x4F, 0xF5, 0x8C, +0x84, 0xA6, 0x3C, 0x13, 0xFD, 0x41, 0x96, 0xCB, +0xE0, 0x43, 0xE8, 0x0F, 0x80, 0x64, 0x42, 0x31, +0x14, 0x73, 0xA4, 0x5C, 0x92, 0x28, 0x96, 0x46, +0xC1, 0x06, 0xA4, 0x9A, 0xA5, 0x1B, 0xAC, 0x80, +0xAD, 0x01, 0xDD, 0x9E, 0x8C, 0x41, 0xD5, 0xEE, +0xDD, 0x9E, 0xFC, 0x01, 0x3C, 0x07, 0xFA, 0xC6, +0x4E, 0x05, 0x00, 0x42, 0x84, 0x00, 0x3D, 0xE3, +0xF8, 0x33, 0x3E, 0x5F, 0xF0, 0x8C, 0x80, 0x20, +0xE0, 0x1E, 0xE8, 0x0A, 0x38, 0x22, 0x81, 0x11, +0x4E, 0x24, 0x00, 0x04, 0x38, 0x12, 0x81, 0x09, +0x8C, 0x01, 0x96, 0x03, 0xD5, 0xF6, 0x49, 0xFF, +0xFF, 0x8E, 0xE4, 0x02, 0xE8, 0x2C, 0x2E, 0x67, +0xF5, 0x84, 0xE6, 0xC7, 0x4E, 0xF2, 0x00, 0x9B, +0x46, 0xF0, 0x02, 0x00, 0x58, 0xF7, 0x87, 0xCC, +0x38, 0x17, 0x99, 0x01, 0x40, 0xF0, 0xBC, 0x00, +0x4A, 0x00, 0x3C, 0x00, 0x0E, 0x00, 0x38, 0x00, +0x48, 0x00, 0x92, 0x00, 0xCC, 0x00, 0x1C, 0x01, +0x1C, 0x01, 0x5A, 0x00, 0x01, 0x04, 0x48, 0x00, +0x00, 0x86, 0x3E, 0x0F, 0xF5, 0x70, 0x49, 0xFF, +0xFF, 0xB2, 0x3C, 0x33, 0xFE, 0x55, 0x87, 0x80, +0x84, 0x02, 0x3D, 0xCF, 0xFD, 0x5F, 0x3D, 0xCF, +0xFD, 0x60, 0x3E, 0x07, 0xF5, 0x84, 0x3C, 0x3B, +0xFA, 0xC4, 0xD5, 0x75, 0x84, 0xC0, 0x3C, 0x6F, +0xFD, 0x5F, 0x3C, 0x6F, 0xFD, 0x60, 0x3E, 0x67, +0xF5, 0x84, 0xD5, 0x6D, 0x5A, 0x08, 0x01, 0x1E, +0xB0, 0x01, 0x49, 0xFF, 0xFF, 0x98, 0x3C, 0x4D, +0xFD, 0x5F, 0x3C, 0x53, 0xFD, 0x42, 0x8C, 0x81, +0x97, 0xAB, 0xE0, 0x86, 0x3C, 0x4F, 0xFD, 0x5F, +0xE9, 0x04, 0x87, 0xC5, 0x3F, 0xE7, 0xF5, 0x84, +0xF0, 0x01, 0x3C, 0x1D, 0xFD, 0x5C, 0x49, 0xFF, +0xFE, 0x66, 0x3C, 0x13, 0xFD, 0x5B, 0x84, 0x86, +0x96, 0x8B, 0xE2, 0x40, 0xE9, 0x23, 0xD5, 0x4E, +0x84, 0xC0, 0x3C, 0x6F, 0xFD, 0x5F, 0x3C, 0x6F, +0xFD, 0x60, 0x84, 0x63, 0xD5, 0x0D, 0x5A, 0x08, +0x01, 0x0F, 0x84, 0xC0, 0x3E, 0x0F, 0xF5, 0x74, +0x49, 0xFF, 0xFF, 0x71, 0x3C, 0x6F, 0xFD, 0x5F, +0x3C, 0x6F, 0xFD, 0x60, 0x84, 0x64, 0x3E, 0x37, +0xF5, 0x84, 0xD5, 0x39, 0x3C, 0x0D, 0xFD, 0x60, +0x3C, 0x13, 0xFD, 0x47, 0x8C, 0x01, 0x96, 0x8B, +0xE0, 0x40, 0x3C, 0x0F, 0xFD, 0x60, 0xE8, 0x2E, +0x84, 0x81, 0x3E, 0x47, 0xF5, 0x84, 0xD5, 0x2A, +0x5A, 0x08, 0x01, 0x16, 0xB0, 0x01, 0x49, 0xFF, +0xFF, 0x56, 0x3C, 0x4D, 0xFD, 0x5F, 0x3C, 0x53, +0xFD, 0x42, 0x8C, 0x81, 0x97, 0xAB, 0xE0, 0x86, +0x3C, 0x4F, 0xFD, 0x5F, 0xE9, 0x04, 0x87, 0x85, +0x3F, 0xC7, 0xF5, 0x84, 0xF0, 0x01, 0x3C, 0x1D, +0xFD, 0x5D, 0xD5, 0xBE, 0x3C, 0x1D, 0xFD, 0x5C, +0x3C, 0x0D, 0xFD, 0x5D, 0x49, 0xFF, 0xFE, 0x1F, +0x3C, 0x13, 0xFD, 0x5B, 0x96, 0x8B, 0xE2, 0x40, +0xE9, 0xDC, 0x84, 0x60, 0x84, 0xC1, 0x3E, 0x37, +0xF5, 0x84, 0x3C, 0x6F, 0xFD, 0x51, 0xD5, 0x03, +0xC0, 0xD4, 0x84, 0xC0, 0x80, 0x06, 0xFC, 0x81, +0x3E, 0x0F, 0xF3, 0x60, 0x3E, 0x2F, 0xFA, 0x58, +0x84, 0x20, 0x84, 0x61, 0x12, 0x11, 0x01, 0x27, +0x14, 0x00, 0x00, 0x78, 0x14, 0x10, 0x00, 0x80, +0x14, 0x10, 0x00, 0x79, 0x14, 0x10, 0x00, 0x3E, +0x14, 0x10, 0x00, 0x40, 0x12, 0x11, 0x01, 0x2A, +0x10, 0x30, 0x02, 0x2A, 0xDD, 0x9E, 0x3C, 0x03, +0xFA, 0xC3, 0xC0, 0x10, 0x3C, 0x13, 0xFA, 0xC4, +0xC1, 0x05, 0x8E, 0x21, 0x3C, 0x1B, 0xFA, 0xC4, +0xD5, 0x09, 0x3C, 0x1B, 0xFA, 0xC3, 0x3C, 0x1F, +0xFD, 0x5F, 0x3C, 0x1F, 0xFD, 0x60, 0x3E, 0x17, +0xF5, 0x84, 0x3C, 0x23, 0xFE, 0x4B, 0x96, 0x86, +0xC2, 0x38, 0x2E, 0x37, 0xEA, 0x79, 0x5A, 0x38, +0x02, 0x35, 0xFC, 0x40, 0x49, 0xFF, 0xFF, 0x13, +0xC0, 0x16, 0x84, 0x00, 0x49, 0x00, 0x00, 0x30, +0x3C, 0x0D, 0xFD, 0x51, 0x85, 0x00, 0x8C, 0x01, +0x96, 0x43, 0x3C, 0x1B, 0xFE, 0x52, 0x3C, 0x23, +0xFE, 0x52, 0x96, 0x10, 0x49, 0x00, 0x0C, 0xBE, +0x49, 0x00, 0x0A, 0xF2, 0x3E, 0x87, 0xF5, 0xCC, +0x84, 0x01, 0xD5, 0x1A, 0x2E, 0x67, 0xFA, 0x1E, +0xC6, 0x03, 0x84, 0x00, 0xD5, 0x15, 0x49, 0xFF, +0xFF, 0xB1, 0x3E, 0x78, 0x0F, 0x80, 0x3E, 0x98, +0x02, 0x80, 0x3C, 0x43, 0xF8, 0x33, 0xE0, 0xC4, +0xE8, 0xF5, 0x94, 0x31, 0x40, 0x50, 0x24, 0x00, +0x88, 0x07, 0x22, 0x12, 0x83, 0x10, 0x49, 0x00, +0x29, 0x79, 0x8C, 0xC1, 0xD5, 0xF3, 0xFC, 0xC0, +0x84, 0x00, 0xDD, 0x9E, 0x3E, 0x1F, 0xEA, 0x74, +0x3E, 0x3F, 0xF3, 0x60, 0x84, 0x40, 0x84, 0x80, +0xAC, 0x08, 0x84, 0x00, 0xAE, 0x0E, 0xAE, 0x0D, +0xAC, 0x89, 0x12, 0x21, 0x81, 0x13, 0x14, 0x41, +0x80, 0x87, 0x14, 0x41, 0x80, 0x88, 0x10, 0x21, +0x82, 0x24, 0xDD, 0x9E, 0x3A, 0x6F, 0xAA, 0xBC, +0x80, 0xE0, 0x3C, 0x00, 0x00, 0x00, 0xEF, 0xBC, +0x96, 0x06, 0x80, 0xC1, 0xC0, 0x45, 0x3C, 0x80, +0x00, 0x0E, 0x3C, 0x10, 0x00, 0x02, 0x3C, 0x20, +0x00, 0x03, 0x41, 0xE0, 0x80, 0x11, 0x3C, 0x30, +0x00, 0x06, 0x3C, 0x40, 0x00, 0x07, 0x3C, 0x50, +0x00, 0x08, 0x3C, 0x00, 0x00, 0x09, 0x3C, 0x10, +0x00, 0x0A, 0x41, 0xC1, 0x00, 0x11, 0x40, 0xF1, +0x80, 0x11, 0x3C, 0x20, 0x00, 0x0B, 0x96, 0xC3, +0x40, 0x92, 0x80, 0x11, 0x3C, 0x00, 0x00, 0x0C, +0x97, 0x4B, 0x3C, 0x10, 0x00, 0x0D, 0x40, 0xA2, +0x00, 0x11, 0x96, 0x4B, 0x97, 0x13, 0x96, 0x83, +0x84, 0x00, 0x15, 0xEF, 0x80, 0x01, 0xF3, 0x89, +0x87, 0xC3, 0xF5, 0x8A, 0xF4, 0x8B, 0xF2, 0x8C, +0xF1, 0x8D, 0x14, 0x9F, 0x80, 0x08, 0xF0, 0x8E, +0x80, 0x20, 0x84, 0x41, 0x40, 0x34, 0x00, 0x11, +0x80, 0x80, 0x80, 0xA0, 0xB6, 0x1F, 0x15, 0xEF, +0x80, 0x02, 0x15, 0xCF, 0x80, 0x03, 0xF7, 0x84, +0xF6, 0x85, 0x14, 0xFF, 0x80, 0x06, 0x14, 0xAF, +0x80, 0x07, 0x49, 0x00, 0x2F, 0x13, 0x3C, 0x30, +0x00, 0x00, 0x96, 0xCE, 0xC3, 0x45, 0x3C, 0x80, +0x00, 0x1B, 0x3D, 0xC0, 0x00, 0x0F, 0x3C, 0xF0, +0x00, 0x10, 0x3C, 0xA0, 0x00, 0x13, 0x3C, 0x90, +0x00, 0x14, 0x3C, 0x50, 0x00, 0x15, 0x3C, 0x40, +0x00, 0x16, 0x3C, 0x00, 0x00, 0x17, 0x96, 0xE3, +0x3C, 0x20, 0x00, 0x18, 0x96, 0x43, 0x3C, 0x40, +0x00, 0x19, 0x3C, 0x00, 0x00, 0x1A, 0x41, 0xEE, +0x00, 0x11, 0x97, 0x23, 0x41, 0xC7, 0x80, 0x11, +0xF1, 0x8A, 0x40, 0xF5, 0x00, 0x11, 0x84, 0x21, +0x40, 0xA4, 0x80, 0x11, 0x40, 0x92, 0x80, 0x11, +0x97, 0x53, 0x96, 0x83, 0x84, 0x00, 0x15, 0xEF, +0x80, 0x01, 0xF3, 0x89, 0x87, 0xC4, 0xF5, 0x8B, +0xF4, 0x8C, 0xF2, 0x8D, 0x14, 0x9F, 0x80, 0x08, +0xF0, 0x8E, 0x80, 0x41, 0x40, 0x34, 0x00, 0x11, +0x80, 0x80, 0x80, 0xA0, 0xB6, 0x1F, 0x15, 0xEF, +0x80, 0x02, 0x15, 0xCF, 0x80, 0x03, 0xF7, 0x84, +0xF6, 0x85, 0x14, 0xFF, 0x80, 0x06, 0x14, 0xAF, +0x80, 0x07, 0x49, 0x00, 0x2E, 0xCB, 0xEC, 0x44, +0x3A, 0x6F, 0xAA, 0x84, 0xDD, 0x9E, 0x3B, 0xFF, +0xFC, 0xBC, 0xEF, 0xEC, 0xF0, 0x81, 0x80, 0x01, +0x80, 0x23, 0x10, 0x0F, 0x80, 0x03, 0x80, 0x02, +0x10, 0x0F, 0x80, 0x02, 0x80, 0x01, 0x10, 0x0F, +0x80, 0x01, 0xF0, 0x01, 0xC8, 0x0A, 0x2E, 0x07, +0xEA, 0x79, 0x5A, 0x08, 0x02, 0x04, 0x49, 0x00, +0x34, 0x9A, 0x84, 0x08, 0x48, 0x00, 0x01, 0x04, +0x49, 0x00, 0x33, 0xBE, 0x49, 0x00, 0x0A, 0x08, +0x00, 0x0F, 0x80, 0x03, 0xC0, 0x11, 0x3C, 0x03, +0xFE, 0x60, 0x96, 0x03, 0x96, 0x40, 0x3C, 0x03, +0xFE, 0x64, 0x96, 0x03, 0x96, 0x80, 0x3C, 0x03, +0xFE, 0x65, 0x96, 0x03, 0x96, 0xC0, 0x84, 0x01, +0x49, 0x00, 0x30, 0x5E, 0xD5, 0x1B, 0x2E, 0x07, +0xEA, 0x79, 0x5A, 0x08, 0x01, 0x12, 0x3C, 0x03, +0xFE, 0x60, 0x96, 0x03, 0x96, 0x40, 0x3C, 0x03, +0xFE, 0x64, 0x96, 0x03, 0x96, 0x80, 0x3C, 0x03, +0xFE, 0x65, 0x96, 0x03, 0x96, 0xC0, 0x84, 0x01, +0x49, 0x00, 0x30, 0x4A, 0xD5, 0x07, 0x84, 0x00, +0x84, 0x20, 0x84, 0x40, 0x84, 0x60, 0x49, 0x00, +0x30, 0x43, 0x3C, 0x0D, 0xFA, 0xA9, 0x44, 0x10, +0x00, 0x80, 0xB6, 0x20, 0x3C, 0x0D, 0xFA, 0xA9, +0x84, 0x22, 0xA8, 0x41, 0x3C, 0x0D, 0xFA, 0xA9, +0x84, 0x22, 0xA8, 0x42, 0x3C, 0x0D, 0xFA, 0xA9, +0x84, 0x21, 0xA8, 0x45, 0x3C, 0x0D, 0xFA, 0xA9, +0x84, 0x28, 0xA8, 0x44, 0x3C, 0x0D, 0xFA, 0xA8, +0xF1, 0x01, 0xA8, 0x42, 0x3C, 0x0D, 0xFA, 0xA9, +0x84, 0x21, 0x14, 0x10, 0x00, 0x74, 0x00, 0x0F, +0x80, 0x02, 0x5A, 0x08, 0x01, 0x1A, 0x3C, 0x03, +0xFE, 0x4B, 0x96, 0x03, 0x96, 0x01, 0x96, 0x06, +0xC0, 0x0A, 0x3C, 0x0D, 0xFA, 0xA8, 0x3C, 0x1D, +0xFA, 0xA8, 0xA0, 0x49, 0x58, 0x10, 0x80, 0x10, +0xA8, 0x41, 0xD5, 0x12, 0x3C, 0x0D, 0xFA, 0xA8, +0x3C, 0x1D, 0xFA, 0xA8, 0xA0, 0x49, 0x58, 0x10, +0x80, 0xEF, 0xA8, 0x41, 0xD5, 0x09, 0x3C, 0x0D, +0xFA, 0xA8, 0x3C, 0x1D, 0xFA, 0xA8, 0xA0, 0x49, +0x58, 0x10, 0x80, 0x10, 0xA8, 0x41, 0x00, 0x0F, +0x80, 0x01, 0x5A, 0x08, 0x01, 0x09, 0x3C, 0x0D, +0xFA, 0xA5, 0x3C, 0x10, 0x00, 0xEB, 0x96, 0x4B, +0xA8, 0x47, 0xD5, 0x05, 0x3C, 0x0D, 0xFA, 0xA5, +0x84, 0x20, 0xA8, 0x47, 0x49, 0x00, 0x07, 0x9C, +0x3C, 0x0D, 0xFA, 0xA1, 0x84, 0x20, 0xA8, 0x42, +0x3C, 0x0D, 0xFA, 0xA1, 0x84, 0x21, 0x14, 0x10, +0x00, 0x08, 0x3C, 0x0D, 0xFA, 0xA5, 0x3C, 0x1D, +0xFA, 0xA5, 0xA0, 0x49, 0x58, 0x10, 0x90, 0x00, +0xA8, 0x41, 0x84, 0x00, 0x3E, 0x07, 0xEA, 0x7B, +0x3C, 0x0D, 0xFA, 0xA5, 0x84, 0x23, 0xA8, 0x45, +0x64, 0x00, 0x00, 0x40, 0x3C, 0x0D, 0xFA, 0xA5, +0x3C, 0x1D, 0xFA, 0xA5, 0xA0, 0x49, 0x44, 0x20, +0xEF, 0xFF, 0xFE, 0x56, 0xA8, 0x41, 0x3C, 0x0D, +0xFA, 0xA9, 0x84, 0x20, 0x14, 0x10, 0x00, 0x74, +0x3C, 0x0D, 0xFA, 0xA8, 0x3C, 0x1D, 0xFA, 0xA8, +0xA0, 0x49, 0x54, 0x10, 0x80, 0xEF, 0xA8, 0x41, +0x3C, 0x0D, 0xFA, 0xA5, 0x46, 0x12, 0x00, 0x00, +0xA8, 0x43, 0x3C, 0x0D, 0xFA, 0xA5, 0x84, 0x20, +0xA8, 0x43, 0x3C, 0x0D, 0xFA, 0xA5, 0x04, 0x00, +0x00, 0x08, 0x96, 0x27, 0xF0, 0x83, 0xF0, 0x03, +0x5A, 0x08, 0x01, 0x04, 0x49, 0x00, 0x07, 0x58, +0xF0, 0x03, 0x5A, 0x08, 0x01, 0x08, 0x3C, 0x0D, +0xFA, 0xA1, 0x84, 0x20, 0x14, 0x10, 0x00, 0x08, +0xD5, 0x0A, 0x3C, 0x0D, 0xFA, 0xA1, 0xFA, 0x30, +0xA8, 0x42, 0x3C, 0x0D, 0xFA, 0xA1, 0x84, 0x20, +0x14, 0x10, 0x00, 0x08, 0x49, 0x00, 0x32, 0xE4, +0x3C, 0x0D, 0xFA, 0xA9, 0x44, 0x10, 0x00, 0x80, +0xB6, 0x20, 0x3C, 0x0D, 0xFA, 0xA9, 0x84, 0x20, +0xA8, 0x44, 0x3C, 0x0D, 0xFA, 0xA9, 0x84, 0x20, +0xA8, 0x45, 0x3C, 0x0D, 0xFA, 0xA9, 0x84, 0x20, +0xA8, 0x42, 0x3C, 0x0D, 0xFA, 0xA9, 0x84, 0x23, +0xA8, 0x41, 0x00, 0x0F, 0x80, 0x03, 0xC8, 0x05, +0x2E, 0x07, 0xEA, 0x79, 0x5A, 0x08, 0x01, 0x08, +0x84, 0x00, 0x84, 0x20, 0x84, 0x40, 0x84, 0x60, +0x49, 0x00, 0x2F, 0x76, 0x84, 0x00, 0x3E, 0x07, +0xF5, 0x8B, 0xF0, 0x03, 0xEC, 0x14, 0x3B, 0xFF, +0xFC, 0x84, 0xDD, 0x9E, 0x3B, 0xFF, 0xFC, 0xBC, +0xEF, 0xEC, 0xF0, 0x81, 0x49, 0x00, 0x32, 0xB4, +0x49, 0x00, 0x08, 0xFE, 0x3C, 0x0D, 0xFA, 0xA9, +0x44, 0x10, 0x00, 0x80, 0xB6, 0x20, 0x3C, 0x0D, +0xFA, 0xA9, 0x84, 0x22, 0xA8, 0x41, 0x3C, 0x0D, +0xFA, 0xA8, 0xF1, 0x01, 0xA8, 0x42, 0x3C, 0x0D, +0xFA, 0xA8, 0x3C, 0x1D, 0xFA, 0xA8, 0xA0, 0x49, +0x58, 0x10, 0x80, 0x10, 0xA8, 0x41, 0x3C, 0x0D, +0xFA, 0xA9, 0x84, 0x21, 0x14, 0x10, 0x00, 0x74, +0x3C, 0x0D, 0xFA, 0xA5, 0x84, 0x21, 0xA8, 0x47, +0x49, 0x00, 0x06, 0xF2, 0x3C, 0x0D, 0xFA, 0xA1, +0x84, 0x20, 0xA8, 0x42, 0x3C, 0x0D, 0xFA, 0xA1, +0x84, 0x21, 0x14, 0x10, 0x00, 0x08, 0x3C, 0x0D, +0xFA, 0xA5, 0x3C, 0x1D, 0xFA, 0xA5, 0xA0, 0x49, +0x58, 0x10, 0x90, 0x00, 0xA8, 0x41, 0x3C, 0x0D, +0xFA, 0xA5, 0x84, 0x23, 0xA8, 0x45, 0x64, 0x00, +0x00, 0x40, 0x3C, 0x0D, 0xFA, 0xA5, 0x3C, 0x1D, +0xFA, 0xA5, 0xA0, 0x49, 0x44, 0x20, 0xEF, 0xFF, +0xFE, 0x56, 0xA8, 0x41, 0x3C, 0x0D, 0xFA, 0xA9, +0x84, 0x20, 0x14, 0x10, 0x00, 0x74, 0x3C, 0x0D, +0xFA, 0xA8, 0x3C, 0x1D, 0xFA, 0xA8, 0xA0, 0x49, +0x54, 0x10, 0x80, 0xEF, 0xA8, 0x41, 0x3C, 0x0D, +0xFA, 0xA5, 0x46, 0x12, 0x00, 0x00, 0xA8, 0x43, +0x3C, 0x0D, 0xFA, 0xA5, 0x84, 0x20, 0xA8, 0x43, +0x3C, 0x0D, 0xFA, 0xA5, 0x04, 0x00, 0x00, 0x08, +0x96, 0x27, 0xF0, 0x83, 0xF0, 0x03, 0x5A, 0x08, +0x01, 0x04, 0x49, 0x00, 0x06, 0xB1, 0xF0, 0x03, +0x5A, 0x08, 0x01, 0x08, 0x3C, 0x0D, 0xFA, 0xA1, +0x84, 0x20, 0x14, 0x10, 0x00, 0x08, 0xD5, 0x0A, +0x3C, 0x0D, 0xFA, 0xA1, 0xFA, 0x30, 0xA8, 0x42, +0x3C, 0x0D, 0xFA, 0xA1, 0x84, 0x20, 0x14, 0x10, +0x00, 0x08, 0xEC, 0x14, 0x3B, 0xFF, 0xFC, 0x84, +0xDD, 0x9E, 0x3C, 0x04, 0x08, 0xD0, 0x3A, 0x6F, +0xA0, 0xBC, 0x4E, 0x07, 0x00, 0x07, 0x49, 0xFF, +0xFD, 0x49, 0x84, 0x00, 0x48, 0x00, 0x01, 0x16, +0x2E, 0x27, 0xFA, 0x1E, 0x2E, 0x67, 0xEA, 0x79, +0xCA, 0x04, 0x2E, 0x17, 0xFA, 0x28, 0xC9, 0x10, +0x5A, 0x68, 0x01, 0x0C, 0x84, 0x00, 0x49, 0xFF, +0xFD, 0x9F, 0x3C, 0x03, 0xFE, 0x1D, 0x3C, 0x08, +0x07, 0x9A, 0x84, 0x00, 0x48, 0x00, 0x01, 0x02, +0x84, 0x60, 0x3C, 0x3B, 0xF5, 0x3B, 0x2E, 0x47, +0xF5, 0xCC, 0x4E, 0x42, 0x00, 0x9B, 0x3C, 0x73, +0xFE, 0x4B, 0x97, 0xC6, 0xC7, 0x73, 0x5A, 0x60, +0x02, 0x0C, 0x85, 0x02, 0x49, 0xFF, 0xFD, 0x22, +0x3E, 0x87, 0xEA, 0x79, 0x49, 0xFF, 0xFB, 0xFC, +0x84, 0x01, 0x48, 0x00, 0x00, 0xEB, 0x2E, 0x57, +0xF5, 0x8B, 0x5A, 0x58, 0x01, 0x04, 0x48, 0x00, +0x00, 0xCD, 0x3C, 0x0D, 0xFD, 0x56, 0xCA, 0x28, +0x2E, 0x17, 0xFA, 0x28, 0xC1, 0x25, 0xC8, 0x24, +0x3C, 0x67, 0xFA, 0xAE, 0xCE, 0x21, 0x3C, 0x53, +0xFA, 0xC3, 0xCD, 0x15, 0x3C, 0x13, 0xFE, 0x93, +0x96, 0x0B, 0x84, 0x21, 0x80, 0x61, 0x49, 0xFF, +0xFE, 0x14, 0x5A, 0x00, 0x08, 0x04, 0x48, 0x00, +0x00, 0xB5, 0x3C, 0x73, 0xFE, 0x55, 0x84, 0xC1, +0x3C, 0x6B, 0xFA, 0xC3, 0x3C, 0x7B, 0xFA, 0xC4, +0x48, 0x00, 0x00, 0xAC, 0x3C, 0x03, 0xFE, 0x5F, +0x80, 0x22, 0x84, 0x61, 0x96, 0x03, 0x49, 0xFF, +0xFE, 0x00, 0x48, 0x00, 0x00, 0xA3, 0x8E, 0x01, +0x3C, 0x0F, 0xFD, 0x56, 0x4E, 0x07, 0x00, 0x0C, +0x49, 0x00, 0x32, 0xA5, 0x3C, 0x2D, 0xFD, 0x56, +0x4E, 0x24, 0x00, 0x0F, 0x85, 0xE0, 0x3C, 0xFF, +0xFD, 0x56, 0xD5, 0x0A, 0x3C, 0x73, 0xFA, 0xAE, +0x8E, 0xE1, 0x96, 0xFB, 0x3C, 0x3B, 0xFA, 0xAE, +0x4E, 0x36, 0xFF, 0xF0, 0xD5, 0x0A, 0x3D, 0xE7, +0xFA, 0xAE, 0x4F, 0xE4, 0x00, 0x87, 0x85, 0x00, +0x3C, 0x8B, 0xFA, 0xAE, 0x48, 0x00, 0x00, 0x82, +0x3C, 0x43, 0xFE, 0x5F, 0x3C, 0x23, 0xFA, 0xC3, +0x84, 0x20, 0x3C, 0x1F, 0xFD, 0x56, 0x3C, 0x1B, +0xFA, 0xAE, 0x96, 0x23, 0xCA, 0x06, 0x80, 0x22, +0x80, 0x62, 0x49, 0xFF, 0xFD, 0xCE, 0xD5, 0x71, +0x80, 0x41, 0x84, 0x61, 0x49, 0xFF, 0xFD, 0xC9, +0xD5, 0x6C, 0x5A, 0x60, 0x02, 0x19, 0x3C, 0x23, +0xFE, 0x93, 0x84, 0x82, 0x96, 0x13, 0x80, 0x27, +0x80, 0x47, 0x80, 0x67, 0x3E, 0x47, 0xEA, 0x79, +0x49, 0xFF, 0xFD, 0xBB, 0x81, 0x00, 0x84, 0x02, +0x5A, 0x88, 0x01, 0x74, 0x44, 0x00, 0x00, 0x64, +0x49, 0xFF, 0xFD, 0x06, 0x3E, 0x77, 0xF5, 0xCC, +0x80, 0x08, 0xD5, 0x6B, 0x3C, 0x33, 0xFE, 0x93, +0x80, 0x27, 0x96, 0x1B, 0x84, 0x41, 0x80, 0x67, +0x49, 0xFF, 0xFD, 0xA7, 0x80, 0x06, 0xD5, 0x61, +0x5A, 0x68, 0x02, 0x08, 0x44, 0x00, 0x00, 0x64, +0x49, 0xFF, 0xFC, 0xF2, 0x84, 0x01, 0xD5, 0x59, +0x3C, 0x20, 0x07, 0x97, 0x54, 0x51, 0x40, 0x00, +0xC5, 0x3C, 0x3C, 0x73, 0xFD, 0xAA, 0x97, 0xDF, +0xCF, 0x05, 0x84, 0x00, 0x49, 0xFF, 0xFC, 0xE4, +0xD5, 0x34, 0x3C, 0x03, 0xFD, 0xAA, 0x96, 0x1F, +0x5A, 0x08, 0x01, 0x07, 0x3E, 0x07, 0xEA, 0x79, +0x3C, 0x4B, 0xF5, 0x3B, 0xD5, 0x2A, 0x3C, 0x13, +0xFD, 0xAA, 0x96, 0x5F, 0x5A, 0x18, 0x03, 0x26, +0xCE, 0x1E, 0x3C, 0x33, 0xFD, 0x3C, 0x97, 0x1B, +0xE4, 0x86, 0xE8, 0x04, 0x3C, 0x20, 0x01, 0x2D, +0xC2, 0x05, 0x84, 0xC0, 0x3C, 0x6B, 0xF5, 0x3B, +0xD5, 0x18, 0x3C, 0x03, 0xFE, 0x00, 0x3C, 0x57, +0xF5, 0x3B, 0x96, 0x43, 0x9D, 0xE9, 0xE0, 0x25, +0x3C, 0x7B, 0xF5, 0x3B, 0xE8, 0x0E, 0x85, 0x00, +0x87, 0xC1, 0x3C, 0x8B, 0xF5, 0x3B, 0x3F, 0xE7, +0xEA, 0x79, 0xD5, 0x07, 0x5A, 0x68, 0x01, 0xEB, +0x3C, 0x60, 0x01, 0x2D, 0xC6, 0xE7, 0xD5, 0xCA, +0x2E, 0x37, 0xEA, 0x79, 0x5A, 0x38, 0x01, 0x0D, +0x3C, 0x23, 0xFE, 0x1D, 0x97, 0x53, 0x4E, 0x57, +0x00, 0x10, 0x3C, 0xF3, 0xFE, 0x6E, 0x84, 0x00, +0x3C, 0xF8, 0x07, 0x9A, 0xD5, 0x0A, 0x5A, 0x30, +0x02, 0x08, 0x3C, 0x43, 0xFE, 0x1D, 0x84, 0x00, +0x3C, 0x48, 0x07, 0x9A, 0xD5, 0x02, 0x84, 0x00, +0x3A, 0x6F, 0xA0, 0x84, 0xDD, 0x9E, 0x3E, 0x3F, +0xFA, 0x58, 0x84, 0x40, 0x12, 0x21, 0x80, 0x19, +0x02, 0x11, 0x83, 0x00, 0x84, 0x01, 0x97, 0x0B, +0x12, 0x41, 0x81, 0x00, 0x02, 0x51, 0x83, 0x01, +0x3E, 0x07, 0xF0, 0x65, 0x96, 0x2B, 0x12, 0x01, +0x80, 0x17, 0x02, 0x11, 0x83, 0x07, 0x97, 0x0B, +0x12, 0x41, 0x81, 0x1D, 0x02, 0x51, 0x83, 0xA4, +0x96, 0x2B, 0x12, 0x01, 0x81, 0x1E, 0x02, 0x11, +0x83, 0xA5, 0x97, 0x0B, 0x12, 0x41, 0x81, 0x1F, +0x02, 0x51, 0x83, 0x02, 0x96, 0x2B, 0x12, 0x01, +0x80, 0x20, 0x02, 0x11, 0x83, 0x02, 0x97, 0x0B, +0x12, 0x41, 0x80, 0x21, 0x02, 0x51, 0x83, 0x02, +0x96, 0x2B, 0x12, 0x01, 0x80, 0x22, 0x02, 0x11, +0x83, 0x02, 0x97, 0x0B, 0x12, 0x41, 0x80, 0x23, +0x02, 0x51, 0x83, 0x02, 0x96, 0x2B, 0x12, 0x01, +0x80, 0x24, 0x02, 0x11, 0x83, 0x10, 0x97, 0x0B, +0x12, 0x41, 0x80, 0xB2, 0x02, 0xF1, 0x83, 0x0D, +0x02, 0x51, 0x83, 0x0F, 0x3C, 0xF8, 0x07, 0xB4, +0x96, 0x2B, 0x12, 0x01, 0x80, 0x35, 0x02, 0x11, +0x83, 0x11, 0x97, 0x0B, 0x12, 0x41, 0x80, 0x3B, +0x02, 0x51, 0x83, 0x12, 0x96, 0x2B, 0x12, 0x01, +0x80, 0xB1, 0x02, 0x11, 0x83, 0x13, 0x97, 0x0B, +0x12, 0x41, 0x80, 0xD5, 0x02, 0x51, 0x83, 0x14, +0x96, 0x2B, 0x12, 0x01, 0x80, 0xD6, 0x02, 0x11, +0x83, 0x15, 0x97, 0x0B, 0x12, 0x41, 0x80, 0xD7, +0x02, 0x51, 0x83, 0x08, 0x96, 0x2B, 0x12, 0x01, +0x80, 0xEB, 0x02, 0x11, 0x83, 0x09, 0x97, 0x0B, +0x12, 0x41, 0x80, 0xEC, 0x02, 0x51, 0x83, 0x0A, +0x96, 0x2B, 0x12, 0x01, 0x80, 0xED, 0x02, 0x11, +0x83, 0x0B, 0x97, 0x0B, 0x12, 0x41, 0x80, 0xEE, +0x02, 0x51, 0x83, 0x0C, 0x96, 0x2B, 0x12, 0x01, +0x80, 0xEF, 0x02, 0x11, 0x83, 0xA0, 0x97, 0x0B, +0x12, 0x41, 0x80, 0xCA, 0x02, 0x51, 0x83, 0xA1, +0x96, 0x2B, 0x12, 0x01, 0x80, 0xCB, 0x02, 0x11, +0x83, 0xA2, 0x97, 0x0B, 0x12, 0x41, 0x80, 0xCC, +0x02, 0x51, 0x83, 0xA3, 0x96, 0x2B, 0x12, 0x01, +0x80, 0xCD, 0x02, 0x11, 0x80, 0xF0, 0x97, 0x0B, +0x58, 0xF2, 0x08, 0x00, 0x12, 0xF1, 0x80, 0xF0, +0x12, 0x21, 0x81, 0x16, 0xDD, 0x9E, 0x3B, 0xFF, +0xFC, 0xBC, 0xEF, 0xFC, 0x49, 0x00, 0x30, 0x7C, +0x49, 0x00, 0x31, 0x51, 0x3E, 0x08, 0x02, 0x80, +0x3C, 0x13, 0xF8, 0x33, 0x49, 0x00, 0x33, 0x9C, +0xEC, 0x04, 0x84, 0x00, 0x3B, 0xFF, 0xFC, 0x84, +0x48, 0x00, 0x34, 0xBD, 0x3C, 0x03, 0xFD, 0x4B, +0xC0, 0x62, 0x3A, 0x6F, 0xA8, 0xBC, 0x85, 0x40, +0x49, 0xFF, 0xFF, 0xE7, 0x80, 0xCA, 0x49, 0xFF, +0xFF, 0xE4, 0x3C, 0x23, 0xF8, 0x33, 0x3E, 0x18, +0x02, 0x80, 0x3E, 0x3F, 0xF0, 0x8C, 0xE1, 0x42, +0xE8, 0x09, 0x2A, 0xF0, 0x80, 0x01, 0x38, 0x61, +0xA9, 0x09, 0x12, 0xF0, 0x81, 0xDF, 0x8D, 0x41, +0xD5, 0xF7, 0x3E, 0x8F, 0xF0, 0x8C, 0x84, 0xE0, +0x80, 0xC8, 0x3C, 0x13, 0xFD, 0x4B, 0x96, 0x8B, +0xE0, 0xE2, 0xE8, 0x1E, 0x49, 0xFF, 0xFF, 0xC9, +0x3C, 0x93, 0xF8, 0x33, 0x3E, 0x28, 0x02, 0x80, +0x84, 0x20, 0xE0, 0x29, 0xE8, 0x13, 0x2A, 0x41, +0x00, 0x01, 0x95, 0x49, 0x22, 0x01, 0x01, 0xDF, +0x9A, 0x20, 0x4E, 0x04, 0x00, 0x03, 0xFE, 0x02, +0x39, 0xE3, 0x14, 0x01, 0x12, 0x41, 0x01, 0xDF, +0x88, 0x1E, 0x38, 0x04, 0x14, 0x09, 0x8C, 0x21, +0xD5, 0xED, 0x8C, 0xE1, 0xD5, 0xDF, 0x3C, 0x63, +0xF8, 0x33, 0x85, 0x00, 0x3E, 0x0F, 0xF0, 0x8C, +0xE1, 0x06, 0xE8, 0x0C, 0x3C, 0x13, 0xFD, 0x4B, +0x38, 0x90, 0x21, 0x11, 0x96, 0xCB, 0x40, 0xA4, +0x8D, 0x56, 0x38, 0xA0, 0x21, 0x09, 0x8D, 0x01, +0xD5, 0xF4, 0x84, 0x80, 0x3E, 0x5F, 0xF0, 0x8C, +0xE0, 0x86, 0xE8, 0x08, 0x38, 0x72, 0x91, 0x11, +0x90, 0xE1, 0x38, 0x72, 0x91, 0x09, 0x8C, 0x81, +0xD5, 0xF8, 0x84, 0x60, 0x3C, 0x3B, 0xFD, 0x4B, +0x3A, 0x6F, 0xA8, 0x84, 0xDD, 0x9E, 0x3C, 0x5D, +0xFA, 0xA3, 0x84, 0x20, 0x50, 0x30, 0x85, 0x34, +0x94, 0x89, 0x38, 0xF2, 0x8D, 0x01, 0x3C, 0x43, +0xFE, 0x7C, 0x88, 0x40, 0x40, 0x37, 0x90, 0x0D, +0x97, 0x19, 0xAD, 0x10, 0xA4, 0xD0, 0x3C, 0xF3, +0xFF, 0x19, 0x8C, 0x21, 0x88, 0x6F, 0x97, 0x19, +0xAD, 0x10, 0x5A, 0x18, 0x14, 0xED, 0xDD, 0x9E, +0x3C, 0x5D, 0xFA, 0xA3, 0x84, 0x20, 0x50, 0x30, +0x85, 0x48, 0x94, 0x89, 0x38, 0xF2, 0x8D, 0x01, +0x3C, 0x43, 0xFE, 0x92, 0x88, 0x40, 0x40, 0x37, +0x90, 0x0D, 0x97, 0x19, 0xAD, 0x10, 0xA4, 0xD0, +0x3C, 0xF3, 0xFF, 0x1A, 0x8C, 0x21, 0x88, 0x6F, +0x97, 0x19, 0xAD, 0x10, 0x5A, 0x18, 0x0C, 0xED, +0xDD, 0x9E, 0x3A, 0x6F, 0xA8, 0xBC, 0x80, 0xC0, +0x49, 0x00, 0x2F, 0xD2, 0x3C, 0x03, 0xFE, 0x61, +0x49, 0x00, 0x2B, 0xF6, 0x3C, 0x03, 0xFE, 0x4E, +0x49, 0x00, 0x2B, 0xFF, 0x84, 0xE0, 0x84, 0x00, +0x49, 0x00, 0x2C, 0x08, 0x3C, 0xAC, 0x02, 0xC0, +0x80, 0x47, 0x3C, 0x13, 0xF8, 0x33, 0xE0, 0xE1, +0xE8, 0x06, 0x94, 0x79, 0x88, 0x2A, 0x8C, 0xE1, +0xAC, 0x88, 0xD5, 0xF8, 0xCE, 0x5C, 0x3C, 0x00, +0x00, 0x2B, 0x49, 0x00, 0x2C, 0x18, 0x84, 0x0A, +0x49, 0x00, 0x2C, 0x21, 0x85, 0x41, 0x3E, 0x88, +0x02, 0x80, 0x3C, 0xFD, 0xFA, 0xA9, 0x3E, 0x78, +0x02, 0x80, 0x14, 0xA7, 0x80, 0x58, 0x49, 0x00, +0x30, 0x7E, 0x49, 0x00, 0x2F, 0xA5, 0x3E, 0x08, +0x08, 0x48, 0x49, 0xFF, 0xFF, 0xB3, 0x84, 0x00, +0x49, 0x00, 0x33, 0xED, 0x3C, 0x3C, 0x02, 0xC0, +0x40, 0x43, 0x20, 0x00, 0x88, 0x66, 0x22, 0x52, +0x02, 0xE4, 0x8C, 0xC2, 0xAD, 0x58, 0x40, 0xA5, +0x04, 0x08, 0x5A, 0x68, 0x18, 0xE4, 0x3C, 0x00, +0x00, 0x2A, 0x49, 0x00, 0x2B, 0xF0, 0x84, 0x0A, +0x49, 0x00, 0x2B, 0xF9, 0x84, 0xC0, 0x85, 0x21, +0x3D, 0xED, 0xFA, 0xA9, 0x14, 0x9F, 0x00, 0x58, +0x49, 0x00, 0x30, 0x59, 0x49, 0x00, 0x2F, 0x80, +0x3E, 0x08, 0x08, 0x48, 0x49, 0xFF, 0xFF, 0x8E, +0x84, 0x00, 0x49, 0x00, 0x33, 0xC8, 0x3C, 0x2C, +0x02, 0xC0, 0x98, 0xFE, 0x88, 0x46, 0x22, 0x81, +0x82, 0xE4, 0xA4, 0x50, 0x96, 0x0B, 0x88, 0x08, +0x4E, 0x04, 0x00, 0x03, 0xFE, 0x02, 0x97, 0x03, +0x8C, 0xC2, 0xAD, 0x10, 0x40, 0x94, 0x84, 0x08, +0x5A, 0x68, 0x18, 0xE0, 0x3C, 0x5D, 0xFA, 0xA9, +0x44, 0xF0, 0xFF, 0xFF, 0x14, 0xF2, 0x80, 0x58, +0x48, 0x00, 0x00, 0xA1, 0x5A, 0x60, 0x01, 0x04, +0x48, 0x00, 0x00, 0x9D, 0x3C, 0x00, 0x00, 0x2B, +0x49, 0x00, 0x2B, 0xB9, 0x84, 0x0A, 0x49, 0x00, +0x2B, 0xC2, 0x85, 0x40, 0x3E, 0x88, 0x02, 0x80, +0xE7, 0x46, 0xE9, 0x0D, 0x50, 0x05, 0x7F, 0xFA, +0xE6, 0x08, 0xE8, 0x05, 0x40, 0x53, 0x18, 0x0A, +0x84, 0x80, 0xD5, 0x07, 0x40, 0x43, 0x20, 0x0A, +0x84, 0xA0, 0xD5, 0x03, 0x80, 0x86, 0x84, 0xA0, +0x3C, 0x2D, 0xFA, 0xA9, 0x44, 0x70, 0xAA, 0xAA, +0x54, 0x32, 0x55, 0x55, 0x54, 0x92, 0xD5, 0x55, +0xFF, 0x3E, 0xFF, 0x7E, 0x14, 0x31, 0x00, 0x51, +0x14, 0x41, 0x00, 0x52, 0x14, 0x91, 0x00, 0x55, +0x14, 0x51, 0x00, 0x56, 0x49, 0x00, 0x30, 0x03, +0x49, 0x00, 0x2F, 0x2A, 0x3E, 0x08, 0x08, 0x20, +0x49, 0xFF, 0xFF, 0x1F, 0x84, 0x00, 0x49, 0x00, +0x33, 0x72, 0x3C, 0x1C, 0x02, 0xC0, 0x40, 0x05, +0x04, 0x08, 0x88, 0x20, 0x88, 0x08, 0x8D, 0x41, +0x22, 0x40, 0x02, 0xD0, 0x95, 0xB1, 0xAD, 0x08, +0x3E, 0x98, 0x02, 0x80, 0x5A, 0xA8, 0x14, 0xC6, +0x3C, 0x00, 0x00, 0x2A, 0x49, 0x00, 0x2B, 0x73, +0x84, 0x0A, 0x49, 0x00, 0x2B, 0x7C, 0x84, 0xC0, +0x85, 0x41, 0xE6, 0xC6, 0xE9, 0x0C, 0x9F, 0x76, +0xE6, 0xA8, 0xE8, 0x05, 0x40, 0x05, 0x18, 0x0A, +0x84, 0x20, 0xD5, 0x07, 0x40, 0x15, 0x20, 0x0A, +0x84, 0x00, 0xD5, 0x03, 0x80, 0x2A, 0x84, 0x00, +0x3D, 0xED, 0xFA, 0xA9, 0x54, 0x20, 0x55, 0x55, +0x54, 0x80, 0xD5, 0x55, 0xFE, 0x3E, 0xFE, 0x7E, +0x14, 0x8F, 0x00, 0x51, 0x14, 0x1F, 0x00, 0x52, +0x14, 0x2F, 0x00, 0x55, 0x14, 0x0F, 0x00, 0x56, +0x49, 0x00, 0x2F, 0xC1, 0x49, 0x00, 0x2E, 0xE8, +0x3E, 0x08, 0x08, 0x20, 0x49, 0xFF, 0xFE, 0xDD, +0x84, 0x00, 0x49, 0x00, 0x33, 0x30, 0x3C, 0x4C, +0x02, 0xC0, 0x94, 0xF1, 0x88, 0x83, 0x88, 0x69, +0xA5, 0x60, 0x22, 0x11, 0x82, 0xD0, 0x96, 0x2B, +0x88, 0x01, 0x4E, 0x04, 0x00, 0x03, 0xFE, 0x02, +0x96, 0x83, 0x8C, 0xC1, 0xAC, 0xA0, 0x40, 0xA5, +0x04, 0x08, 0x5A, 0x68, 0x14, 0xC4, 0x3C, 0x7D, +0xFA, 0xA9, 0x44, 0x90, 0x55, 0x55, 0x44, 0x60, +0xAA, 0xAA, 0x14, 0x93, 0x80, 0x51, 0x14, 0x63, +0x80, 0x52, 0x14, 0x93, 0x80, 0x55, 0x14, 0x63, +0x80, 0x56, 0x3A, 0x6F, 0xA8, 0x84, 0x48, 0x00, +0x2E, 0xC1, 0x2E, 0x07, 0xF5, 0xD4, 0x4E, 0x02, +0x00, 0x9B, 0x3A, 0x6F, 0xA8, 0xBC, 0x84, 0x00, +0x84, 0x2C, 0x3E, 0x07, 0xF5, 0xD4, 0x3C, 0x1B, +0xFD, 0x3C, 0x49, 0x00, 0x2E, 0xB3, 0x49, 0x00, +0x2E, 0xAA, 0x84, 0x00, 0x80, 0x20, 0x80, 0x40, +0x85, 0x20, 0x49, 0xFF, 0xFE, 0xD0, 0x80, 0xE9, +0x3C, 0xA3, 0xF8, 0x35, 0x3C, 0x4C, 0x02, 0xC0, +0x80, 0x09, 0x84, 0xA1, 0xE0, 0x0A, 0xE8, 0x18, +0x94, 0x41, 0x88, 0x24, 0xA5, 0x88, 0x3C, 0x30, +0x00, 0x2F, 0x96, 0xB3, 0x97, 0x9B, 0xE0, 0xC2, +0xE8, 0x07, 0x41, 0xE2, 0x80, 0x0C, 0x40, 0x9F, +0x24, 0x04, 0x40, 0x94, 0x80, 0x13, 0xA4, 0x88, +0x96, 0xD3, 0xE0, 0xE3, 0xE8, 0x03, 0xA5, 0xC8, +0x97, 0xFB, 0x8C, 0x01, 0xD5, 0xE8, 0x84, 0x4D, +0x84, 0xC0, 0x3C, 0x2B, 0xFD, 0x3C, 0x85, 0x01, +0x49, 0x00, 0x2E, 0x84, 0x49, 0x00, 0x2E, 0x7B, +0x3C, 0x13, 0xF8, 0x34, 0x80, 0xA6, 0xE0, 0xA1, +0xE8, 0x06, 0x40, 0x34, 0x14, 0x0C, 0xFF, 0x9F, +0x8C, 0xA1, 0xD5, 0xFA, 0x85, 0x45, 0x85, 0x01, +0x84, 0x01, 0x80, 0x20, 0x80, 0x46, 0x49, 0xFF, +0xFE, 0x96, 0x84, 0x00, 0x3C, 0x33, 0xF8, 0x34, +0x3C, 0x5C, 0x02, 0xC0, 0x80, 0x20, 0xE0, 0x23, +0xE8, 0x18, 0x94, 0x89, 0x88, 0x45, 0xA5, 0x10, +0x40, 0xF2, 0x00, 0x11, 0x3C, 0x40, 0x00, 0x4D, +0x97, 0x23, 0x40, 0xF7, 0x90, 0x07, 0xE8, 0x05, +0x40, 0x44, 0x04, 0x0C, 0xFF, 0x23, 0xFF, 0xA6, +0xA5, 0x10, 0x97, 0x23, 0xE0, 0x04, 0xE8, 0x03, +0xA4, 0x10, 0x96, 0x03, 0x8C, 0x21, 0xD5, 0xE8, +0xC6, 0x04, 0x8F, 0x41, 0x4E, 0xA3, 0xFF, 0xDA, +0x85, 0x40, 0x3D, 0xE3, 0xF8, 0x33, 0x3E, 0x48, +0x02, 0x80, 0x80, 0xAA, 0xE1, 0x5E, 0xE8, 0x05, +0x38, 0x52, 0x29, 0x09, 0x8D, 0x41, 0xD5, 0xFB, +0x84, 0x20, 0x3C, 0x98, 0x01, 0x40, 0x80, 0xA1, +0x84, 0x81, 0xD3, 0x0D, 0x40, 0x22, 0x14, 0x0C, +0xFE, 0xB6, 0xC2, 0x07, 0x40, 0x82, 0x84, 0x0A, +0x40, 0x22, 0x20, 0x0C, 0xFE, 0x57, 0x96, 0x49, +0x8C, 0xA1, 0xD5, 0xF4, 0x84, 0x6E, 0x3C, 0x78, +0x01, 0x42, 0x3C, 0x18, 0x01, 0x41, 0x3C, 0x3B, +0xFD, 0x3C, 0x3A, 0x6F, 0xA8, 0x84, 0x3C, 0x08, +0x01, 0x43, 0x84, 0x01, 0xDD, 0x9E, 0x3C, 0x10, +0x00, 0x30, 0xC9, 0x02, 0xDD, 0x9E, 0x3A, 0x6F, +0xAA, 0xBC, 0xEF, 0xF4, 0x81, 0x20, 0x44, 0xA0, +0x03, 0x23, 0x85, 0x00, 0x3F, 0xCF, 0xFA, 0x58, +0x3C, 0x00, 0x00, 0x4E, 0x96, 0x83, 0xE1, 0x02, +0xE8, 0x4B, 0x50, 0x35, 0x00, 0x01, 0x38, 0x7E, +0x29, 0x01, 0x38, 0x6E, 0x0D, 0x01, 0x02, 0x44, +0x80, 0x00, 0x02, 0x54, 0x80, 0x01, 0x97, 0xFB, +0x97, 0xB3, 0x12, 0x4F, 0x80, 0x02, 0x12, 0x5F, +0x80, 0x03, 0x12, 0x7F, 0x80, 0x00, 0x12, 0x6F, +0x80, 0x01, 0xF1, 0x01, 0xB4, 0x1F, 0x49, 0xFF, +0xF7, 0x16, 0x3C, 0x10, 0x00, 0x30, 0x96, 0x8B, +0xE2, 0x02, 0xE8, 0x2B, 0x02, 0x34, 0x80, 0x00, +0x9A, 0x1F, 0x97, 0xC3, 0x80, 0x87, 0x4E, 0x74, +0x00, 0x03, 0xFF, 0x3A, 0xE4, 0x83, 0xE9, 0x0C, +0x3C, 0x50, 0x00, 0x31, 0x45, 0xE0, 0x00, 0x64, +0x96, 0x6B, 0xFF, 0xCC, 0x40, 0x23, 0xF8, 0x56, +0x8A, 0x62, 0x12, 0x34, 0x80, 0x00, 0x02, 0x74, +0x80, 0x01, 0x9B, 0xBE, 0x97, 0x33, 0x80, 0x04, +0x4E, 0x44, 0x00, 0x03, 0xFE, 0x22, 0xE4, 0x03, +0xE9, 0x0C, 0x3C, 0x50, 0x00, 0x31, 0x45, 0xE0, +0x00, 0x64, 0x96, 0x6B, 0xFF, 0x0C, 0x40, 0x22, +0x78, 0x56, 0x9A, 0xFA, 0x12, 0x34, 0x80, 0x01, +0x8D, 0x01, 0x8D, 0x42, 0xD5, 0xB2, 0xEC, 0x0C, +0x3A, 0x6F, 0xAA, 0x84, 0xDD, 0x9E, 0x3C, 0x20, +0x00, 0x4C, 0x3C, 0x30, 0x00, 0x4C, 0x54, 0x21, +0x0F, 0x00, 0x90, 0x48, 0x96, 0xDF, 0x99, 0x53, +0xC5, 0x10, 0x3C, 0x40, 0x00, 0x4A, 0x50, 0x00, +0x03, 0x16, 0x97, 0x23, 0xFE, 0xA4, 0x42, 0x20, +0x8C, 0x73, 0x40, 0x11, 0x14, 0x36, 0x3E, 0x3F, +0xFA, 0x58, 0x96, 0x8B, 0x38, 0x21, 0x81, 0x09, +0xDD, 0x9E, 0x3C, 0x20, 0x00, 0x4C, 0x3C, 0x10, +0x00, 0x4C, 0x54, 0x21, 0x0F, 0x00, 0x90, 0x48, +0x96, 0x5F, 0x3A, 0x6F, 0xA4, 0x3C, 0x99, 0xD1, +0xC7, 0x1C, 0x44, 0x90, 0x00, 0x38, 0x3E, 0x4F, +0xF6, 0x04, 0x42, 0x40, 0x24, 0x73, 0x3C, 0x30, +0x00, 0x4B, 0x3C, 0x53, 0xFD, 0x61, 0xA1, 0x25, +0x97, 0x6B, 0x40, 0x52, 0x14, 0xB6, 0x96, 0xDB, +0xFE, 0x6C, 0x42, 0x11, 0x88, 0x73, 0x40, 0x20, +0x9C, 0x56, 0x50, 0x60, 0x03, 0xA6, 0x3E, 0x7F, +0xFA, 0x58, 0x96, 0x53, 0x38, 0x13, 0x99, 0x09, +0x3A, 0x6F, 0xA4, 0x04, 0xDD, 0x9E, 0xE6, 0x14, +0xE8, 0x0F, 0x84, 0x20, 0x3E, 0x3F, 0xFA, 0x58, +0x99, 0x0B, 0x96, 0x88, 0x00, 0x52, 0x04, 0x80, +0xD0, 0x06, 0x8C, 0x21, 0x5A, 0x18, 0x14, 0xFA, +0x80, 0x01, 0xDD, 0x9E, 0x80, 0x02, 0xDD, 0x9E, +0x3A, 0x6F, 0xAA, 0xBC, 0xEF, 0x94, 0x3F, 0xCF, +0xEA, 0xA8, 0xB9, 0x04, 0xBC, 0x02, 0xBE, 0x00, +0xA0, 0x0E, 0xA0, 0x8F, 0x04, 0x92, 0x00, 0x02, +0xB4, 0x64, 0xF0, 0x81, 0x04, 0x52, 0x00, 0x08, +0xF2, 0x82, 0xA1, 0xF2, 0xF3, 0x83, 0xB4, 0x06, +0x84, 0x7F, 0x04, 0x23, 0x00, 0x08, 0x04, 0x82, +0x00, 0x03, 0x04, 0xA3, 0x00, 0x03, 0xA8, 0xCE, +0xA8, 0xCF, 0x84, 0x20, 0xF7, 0x85, 0xA8, 0x63, +0xF5, 0x84, 0xA8, 0x73, 0xF0, 0x86, 0xF2, 0x87, +0x80, 0xE1, 0x84, 0xC1, 0x3C, 0xFD, 0xFA, 0xAC, +0x3D, 0xED, 0xFA, 0xAA, 0x40, 0x43, 0x1C, 0x0C, +0xFF, 0x63, 0x84, 0x00, 0x44, 0x20, 0xFF, 0xFF, +0x14, 0x47, 0x80, 0x02, 0xB6, 0x8F, 0x14, 0x57, +0x80, 0x08, 0x14, 0x0F, 0x00, 0x02, 0xB6, 0x1E, +0x14, 0x2F, 0x00, 0x08, 0x44, 0x00, 0x07, 0xD0, +0x49, 0xFF, 0xF6, 0xA8, 0xBB, 0x02, 0xBA, 0x00, +0xB0, 0x4A, 0xA1, 0x59, 0x44, 0x00, 0x03, 0xE8, +0xA1, 0x11, 0x38, 0x00, 0x9D, 0x09, 0xCD, 0x02, +0xC4, 0x14, 0x84, 0x00, 0x40, 0x43, 0x00, 0x0C, +0xFF, 0x2E, 0xC4, 0x0C, 0x96, 0x00, 0xF2, 0x89, +0xF3, 0x88, 0x49, 0xFF, 0xFF, 0x9E, 0xB0, 0xCA, +0xF2, 0x09, 0x38, 0x01, 0x9D, 0x09, 0xF3, 0x08, +0xD5, 0x04, 0x8C, 0x01, 0x5A, 0x08, 0x20, 0xF0, +0x8C, 0xE1, 0x5A, 0x78, 0x20, 0xC9, 0xF7, 0x03, +0xF6, 0x04, 0x14, 0x81, 0x80, 0x03, 0x14, 0xA1, +0x00, 0x03, 0x14, 0x91, 0x80, 0x02, 0x3C, 0x9D, +0xFA, 0xAE, 0xB6, 0xE3, 0xF1, 0x06, 0x14, 0x61, +0x80, 0x08, 0xF0, 0x07, 0xF5, 0x05, 0xF4, 0x01, +0xF3, 0x02, 0xA9, 0x52, 0xB6, 0x22, 0x14, 0x01, +0x00, 0x08, 0xB0, 0x4A, 0x14, 0x44, 0x80, 0x06, +0x44, 0x00, 0x00, 0x40, 0x14, 0x34, 0x80, 0x07, +0x3C, 0x2C, 0x02, 0xC0, 0x49, 0xFF, 0xF6, 0xAE, +0xEC, 0x6C, 0x3A, 0x6F, 0xAA, 0x84, 0xDD, 0x9E, +0x3B, 0xFF, 0xFC, 0xBC, 0xEF, 0xF4, 0x12, 0x0F, +0x80, 0x03, 0x22, 0x0F, 0x80, 0x03, 0x5E, 0xF0, +0x01, 0x00, 0xE9, 0x06, 0x44, 0x00, 0x00, 0xFF, +0x12, 0x0F, 0x80, 0x03, 0xD5, 0x09, 0x22, 0x0F, +0x80, 0x03, 0x4E, 0x06, 0x00, 0x06, 0x44, 0x00, +0x00, 0xFF, 0x12, 0x0F, 0x80, 0x03, 0x3C, 0x0D, +0xFA, 0xA5, 0x22, 0x1F, 0x80, 0x03, 0xB6, 0x20, +0x3C, 0x0D, 0xFA, 0xA4, 0x84, 0x21, 0x12, 0x10, +0x00, 0x17, 0x3C, 0x0D, 0xFA, 0xAB, 0x04, 0x00, +0x00, 0x09, 0xDD, 0x20, 0xEC, 0x0C, 0x3B, 0xFF, +0xFC, 0x84, 0xDD, 0x9E, 0x3B, 0xFF, 0xFC, 0xBC, +0xEF, 0xFC, 0x49, 0x00, 0x2D, 0xB8, 0x49, 0x00, +0x2E, 0x5C, 0x84, 0x00, 0x3E, 0x00, 0x0D, 0x40, +0x84, 0x00, 0x3E, 0x00, 0x0D, 0x41, 0x84, 0x00, +0x3C, 0x0F, 0xFC, 0x18, 0x84, 0x00, 0x3E, 0x07, +0xF0, 0x64, 0x84, 0x00, 0x3E, 0x07, 0xF0, 0x65, +0x84, 0x00, 0x3E, 0x07, 0xF5, 0xCC, 0x84, 0x00, +0x3E, 0x07, 0xEA, 0x79, 0x3C, 0x03, 0xFD, 0x8C, +0x96, 0x03, 0x4E, 0x06, 0x00, 0x06, 0xFA, 0x04, +0x3C, 0x0B, 0xFD, 0x8C, 0xD5, 0x09, 0x3C, 0x03, +0xFD, 0x8C, 0x96, 0x03, 0xE4, 0x15, 0xE9, 0x04, +0xFA, 0x04, 0x3C, 0x0B, 0xFD, 0x8C, 0x3C, 0x03, +0xFD, 0x8D, 0x96, 0x03, 0x4E, 0x06, 0x00, 0x06, +0x84, 0x0C, 0x3C, 0x0B, 0xFD, 0x8D, 0xD5, 0x09, +0x3C, 0x03, 0xFD, 0x8D, 0x96, 0x03, 0xE4, 0x0D, +0xE9, 0x04, 0x84, 0x0C, 0x3C, 0x0B, 0xFD, 0x8D, +0x49, 0x00, 0x2E, 0x0A, 0x84, 0x01, 0x49, 0x00, +0x2E, 0x48, 0x3C, 0x03, 0xFE, 0x51, 0x96, 0x03, +0x49, 0xFF, 0xFF, 0x90, 0x49, 0x00, 0x2D, 0xBC, +0x3E, 0x08, 0x0D, 0x4C, 0x3C, 0x0E, 0x02, 0xC0, +0x3E, 0x08, 0x0D, 0x4C, 0x3C, 0x0F, 0xFC, 0xC5, +0xEC, 0x04, 0x3B, 0xFF, 0xFC, 0x84, 0xDD, 0x9E, +0x3C, 0x03, 0xFD, 0x8C, 0x3A, 0x6F, 0x98, 0xBC, +0x96, 0x43, 0x4E, 0x17, 0x00, 0x07, 0x3C, 0x23, +0xFD, 0x8C, 0x96, 0xD3, 0xE4, 0x75, 0xE9, 0x04, +0xFA, 0x84, 0x3C, 0x4B, 0xFD, 0x8C, 0x3C, 0x53, +0xFD, 0x8D, 0x97, 0xAB, 0x4E, 0x67, 0x00, 0x07, +0x3C, 0x03, 0xFD, 0x8D, 0x96, 0x43, 0xE4, 0x2D, +0xE9, 0x04, 0x87, 0xCC, 0x3D, 0xEB, 0xFD, 0x8D, +0x3C, 0x23, 0xFD, 0xEC, 0x96, 0xD3, 0x4E, 0x36, +0x00, 0x06, 0x44, 0x40, 0x02, 0xD0, 0x3C, 0x4B, +0xFD, 0xEC, 0x3C, 0x53, 0xFD, 0xED, 0x97, 0xAB, +0x4E, 0x66, 0x00, 0x06, 0x44, 0x00, 0x05, 0x00, +0x3C, 0x0B, 0xFD, 0xED, 0x3C, 0x13, 0xFD, 0x41, +0x96, 0x8B, 0xE4, 0x4B, 0xE8, 0x06, 0x3C, 0x33, +0xFD, 0x41, 0x97, 0x1B, 0x4E, 0x44, 0x00, 0x05, +0x87, 0xCA, 0x3D, 0xEB, 0xFD, 0x41, 0x3C, 0x53, +0xFD, 0x45, 0x44, 0x0F, 0xE5, 0x32, 0x97, 0xAB, +0x3C, 0x6B, 0xFD, 0x3C, 0x84, 0x25, 0x84, 0xC0, +0x84, 0x49, 0x44, 0xF0, 0x5A, 0x49, 0x3C, 0x0B, +0xFE, 0x4C, 0x3C, 0x1B, 0xFD, 0x3E, 0x3C, 0x2B, +0xFE, 0x4D, 0x3C, 0xFB, 0xFD, 0x48, 0x3C, 0x6B, +0xFE, 0x96, 0x49, 0x00, 0x23, 0x0B, 0x3C, 0x6B, +0xFE, 0x42, 0x3A, 0x6F, 0x98, 0x84, 0xDD, 0x9E, +0x3B, 0xFF, 0xFC, 0xBC, 0xEF, 0xF4, 0x3E, 0x18, +0x15, 0x98, 0x3E, 0x0F, 0xEA, 0x70, 0x9A, 0x08, +0xF0, 0x81, 0xF1, 0x01, 0x3E, 0x0F, 0xEA, 0x70, +0x80, 0x41, 0x84, 0x20, 0x49, 0x00, 0x37, 0xAC, +0x49, 0xFF, 0xFF, 0x42, 0xEC, 0x0C, 0x3B, 0xFF, +0xFC, 0x84, 0xDD, 0x9E, 0x3B, 0xFF, 0xFC, 0xBC, +0xEF, 0xFC, 0x49, 0xFF, 0xFF, 0xE7, 0xEC, 0x04, +0x3B, 0xFF, 0xFC, 0x84, 0xDD, 0x9E, 0x00, 0x00, +0x3C, 0x0D, 0xFA, 0xA4, 0xA0, 0x47, 0x8E, 0x24, +0x5C, 0x00, 0x80, 0x01, 0xDD, 0x9E, 0x3B, 0xFF, +0xFC, 0xBC, 0xEF, 0xFC, 0x49, 0xFF, 0xFF, 0xF6, +0xC0, 0x14, 0x3C, 0x0D, 0xFA, 0xA4, 0x02, 0x00, +0x00, 0x24, 0x96, 0x01, 0x80, 0x20, 0x3C, 0x0D, +0xFA, 0xA4, 0x02, 0x00, 0x00, 0x25, 0x96, 0x01, +0x96, 0x83, 0x94, 0x49, 0x3E, 0x0F, 0xFA, 0x58, +0x88, 0x01, 0x80, 0x22, 0xAC, 0x40, 0xD5, 0x14, +0x3C, 0x03, 0xFE, 0x4A, 0x3C, 0x0D, 0xFA, 0xA1, +0x50, 0x20, 0x00, 0x40, 0x3C, 0x0D, 0xFA, 0xA4, +0x02, 0x00, 0x00, 0x24, 0x96, 0x01, 0x94, 0x41, +0x3E, 0x0F, 0xFA, 0x58, 0x88, 0x20, 0x80, 0x02, +0xFA, 0x40, 0x49, 0xFF, 0xF5, 0x65, 0xEC, 0x04, +0x3B, 0xFF, 0xFC, 0x84, 0xDD, 0x9E, 0x3C, 0x03, +0xFE, 0x4A, 0x3C, 0x0D, 0xFA, 0xA1, 0xFA, 0x31, +0xB6, 0x20, 0xDD, 0x9E, 0x3C, 0x2D, 0xFA, 0xA1, +0x46, 0x10, 0x04, 0x01, 0x94, 0x06, 0x50, 0x30, +0x86, 0x4C, 0x88, 0x03, 0xFA, 0x91, 0xA8, 0x15, +0xB6, 0x82, 0xDD, 0x9E, 0x3C, 0x1D, 0xFA, 0xA1, +0x04, 0x00, 0x80, 0x32, 0x96, 0x06, 0xC8, 0xFD, +0xDD, 0x9E, 0x3C, 0x1D, 0xFA, 0xA1, 0x84, 0x00, +0x3C, 0x0B, 0xFA, 0xE9, 0x04, 0x20, 0x80, 0x0A, +0x58, 0x31, 0x00, 0x01, 0x14, 0x30, 0x80, 0x0A, +0xDD, 0x9E, 0x3C, 0x1D, 0xFA, 0xA1, 0x84, 0x1E, +0x04, 0x20, 0x80, 0x0A, 0xFE, 0x16, 0x14, 0x00, +0x80, 0x0A, 0xDD, 0x9E, 0x3B, 0xFF, 0xFC, 0xBC, +0xEF, 0xF4, 0x84, 0x01, 0x3E, 0x07, 0xEA, 0x7B, +0x3C, 0x0D, 0xFA, 0xA4, 0xA0, 0x07, 0xC0, 0x09, +0x3C, 0x0D, 0xFA, 0xA4, 0x02, 0x00, 0x00, 0x24, +0x96, 0x01, 0x5C, 0xF0, 0x04, 0x00, 0xE9, 0x03, +0x48, 0x00, 0x01, 0xBA, 0x3C, 0x0D, 0xFA, 0xA4, +0x02, 0x00, 0x00, 0x24, 0x96, 0x01, 0xE6, 0x10, +0x4E, 0xF2, 0x00, 0xBD, 0x3C, 0x0D, 0xFA, 0xA4, +0x02, 0x00, 0x00, 0x24, 0x96, 0x01, 0xE6, 0x10, +0x4E, 0xF2, 0x01, 0xA9, 0x46, 0xF0, 0x02, 0x01, +0x58, 0xF7, 0x8C, 0x14, 0x38, 0x07, 0x82, 0x02, +0x4A, 0x00, 0x00, 0x00, 0x54, 0x1C, 0x20, 0x00, +0x5A, 0x1C, 0x20, 0x00, 0x52, 0x1F, 0x20, 0x00, +0x6A, 0x1C, 0x20, 0x00, 0x74, 0x1C, 0x20, 0x00, +0x7E, 0x1C, 0x20, 0x00, 0xA6, 0x1C, 0x20, 0x00, +0xC4, 0x1C, 0x20, 0x00, 0x00, 0x1D, 0x20, 0x00, +0x4C, 0x1D, 0x20, 0x00, 0x56, 0x1D, 0x20, 0x00, +0x60, 0x1D, 0x20, 0x00, 0x52, 0x1F, 0x20, 0x00, +0x52, 0x1F, 0x20, 0x00, 0x52, 0x1F, 0x20, 0x00, +0x4A, 0x1F, 0x20, 0x00, 0x84, 0x02, 0x3E, 0x07, +0xF5, 0xCE, 0x84, 0x00, 0x3E, 0x07, 0xEA, 0x70, +0x84, 0x00, 0x3E, 0x07, 0xF5, 0xCC, 0x48, 0x00, +0x01, 0x73, 0x84, 0x01, 0x3E, 0x07, 0xEA, 0x71, +0x48, 0x00, 0x01, 0x6E, 0x84, 0x01, 0x3E, 0x07, +0xF5, 0xCC, 0x48, 0x00, 0x01, 0x69, 0x84, 0x00, +0x3E, 0x07, 0xEA, 0x70, 0x84, 0x00, 0x3C, 0x0B, +0xFA, 0xC3, 0x84, 0x00, 0x3C, 0x0F, 0xFD, 0x5F, +0x84, 0x00, 0x3C, 0x0F, 0xFD, 0x60, 0x84, 0x00, +0x3E, 0x07, 0xF5, 0x84, 0x84, 0x01, 0x3E, 0x07, +0xF5, 0xCC, 0x48, 0x00, 0x01, 0x55, 0x3C, 0x03, +0xFD, 0x3C, 0x96, 0x03, 0x5A, 0x08, 0x07, 0x07, +0x84, 0x03, 0x3E, 0x07, 0xF5, 0xCE, 0x48, 0x00, +0x01, 0x4B, 0x84, 0x01, 0x3E, 0x07, 0xF5, 0xCE, +0x48, 0x00, 0x01, 0x46, 0x49, 0x00, 0x2B, 0x00, +0x3C, 0x03, 0xFD, 0x44, 0x96, 0x03, 0x84, 0x3D, +0xFE, 0x0E, 0x96, 0x03, 0x3C, 0x0B, 0xFD, 0x44, +0x84, 0x00, 0x3C, 0x0B, 0xFE, 0x5A, 0x49, 0x00, +0x2C, 0xB0, 0x49, 0x00, 0x2C, 0x45, 0x12, 0x0F, +0x80, 0x03, 0x02, 0x0F, 0x80, 0x03, 0xFE, 0x02, +0x96, 0x01, 0x96, 0x03, 0x3C, 0x0B, 0xFE, 0x5A, +0x49, 0x00, 0x2C, 0xA3, 0x48, 0x00, 0x01, 0x28, +0x2E, 0x00, 0x0D, 0x47, 0xC8, 0x0E, 0x3C, 0x03, +0xFD, 0x44, 0x96, 0x03, 0x58, 0x00, 0x00, 0x01, +0x96, 0x03, 0x3C, 0x0B, 0xFD, 0x44, 0x84, 0x00, +0x3E, 0x00, 0x0D, 0x46, 0x48, 0x00, 0x01, 0x18, +0x49, 0x00, 0x2D, 0x29, 0x84, 0x00, 0x3C, 0x0B, +0xFE, 0x5A, 0x49, 0x00, 0x2C, 0x8A, 0x49, 0x00, +0x2C, 0x1F, 0x12, 0x0F, 0x80, 0x03, 0x02, 0x0F, +0x80, 0x03, 0xFE, 0x02, 0x96, 0x01, 0x96, 0x03, +0x3C, 0x0B, 0xFE, 0x5A, 0x49, 0x00, 0x2C, 0x7D, +0x48, 0x00, 0x01, 0x02, 0x84, 0x01, 0x3E, 0x07, +0xF5, 0xCD, 0x48, 0x00, 0x00, 0xFD, 0x84, 0x01, +0x3E, 0x07, 0xEA, 0x70, 0x48, 0x00, 0x00, 0xF8, +0x84, 0x00, 0x3E, 0x07, 0xEA, 0x70, 0x48, 0x00, +0x00, 0xF3, 0x49, 0xFF, 0xFE, 0xC3, 0xC8, 0x21, +0x3C, 0x0D, 0xFA, 0xA4, 0x02, 0x00, 0x00, 0x24, +0x96, 0x01, 0x5C, 0xF0, 0x00, 0x80, 0xE9, 0x19, +0x3C, 0x0D, 0xFA, 0xA4, 0x02, 0x00, 0x00, 0x24, +0x96, 0x01, 0x5C, 0xF0, 0x00, 0xA0, 0xE8, 0x11, +0x3C, 0x03, 0xFD, 0x45, 0x96, 0x03, 0x5A, 0x08, +0x01, 0x06, 0x49, 0xFF, 0xFE, 0xB2, 0x48, 0x00, +0x00, 0xDB, 0x84, 0x00, 0x3E, 0x07, 0xEA, 0x71, +0x49, 0xFF, 0xFE, 0xDB, 0x48, 0x00, 0x00, 0xD4, +0x49, 0xFF, 0xFE, 0xA0, 0xC8, 0x1F, 0x3C, 0x0D, +0xFA, 0xA4, 0x02, 0x00, 0x00, 0x24, 0x96, 0x01, +0x5C, 0xF0, 0x02, 0x00, 0xE9, 0x17, 0x49, 0xFF, +0xFE, 0x95, 0xC8, 0x14, 0x3C, 0x0D, 0xFA, 0xA4, +0x02, 0x00, 0x00, 0x24, 0x96, 0x01, 0x5C, 0xF0, +0x02, 0x20, 0xE8, 0x0C, 0x3C, 0x0D, 0xFA, 0xA4, +0x02, 0x00, 0x00, 0x24, 0x96, 0x01, 0x50, 0x00, +0x7E, 0x00, 0x49, 0xFF, 0xFE, 0xC1, 0x48, 0x00, +0x00, 0xB3, 0x3C, 0x0D, 0xFA, 0xA4, 0x02, 0x00, +0x00, 0x24, 0x96, 0x01, 0x5C, 0xF0, 0x04, 0x00, +0xE9, 0x03, 0x48, 0x00, 0x00, 0xA9, 0x49, 0xFF, +0xFE, 0x75, 0xC0, 0x4A, 0x3C, 0x0D, 0xFA, 0xA4, +0x02, 0x00, 0x00, 0x24, 0x96, 0x01, 0x5A, 0x08, +0x10, 0x44, 0x3C, 0x0D, 0xFA, 0xA4, 0x02, 0x00, +0x00, 0x25, 0x12, 0x0F, 0x80, 0x03, 0x3C, 0x03, +0xFD, 0x3C, 0x96, 0x03, 0x12, 0x0F, 0x80, 0x02, +0x49, 0xFF, 0xFE, 0x67, 0x02, 0x1F, 0x80, 0x03, +0x02, 0x0F, 0x80, 0x02, 0x4C, 0x10, 0x00, 0x05, +0x84, 0x01, 0x3E, 0x07, 0xF5, 0xD4, 0x02, 0x0F, +0x80, 0x03, 0xE6, 0x06, 0xE9, 0x12, 0x02, 0x0F, +0x80, 0x03, 0x5A, 0x00, 0x10, 0x24, 0x02, 0x0F, +0x80, 0x03, 0x5A, 0x00, 0x14, 0x20, 0x02, 0x0F, +0x80, 0x03, 0x5A, 0x00, 0x0A, 0x1C, 0x84, 0x02, +0x3E, 0x07, 0xF5, 0xCE, 0x48, 0x00, 0x00, 0x70, +0x02, 0x0F, 0x80, 0x02, 0xE6, 0x06, 0xE9, 0x12, +0x02, 0x0F, 0x80, 0x03, 0x5A, 0x00, 0x10, 0x0F, +0x02, 0x0F, 0x80, 0x03, 0x5A, 0x00, 0x14, 0x0B, +0x02, 0x0F, 0x80, 0x03, 0x5A, 0x00, 0x0A, 0x07, +0x84, 0x02, 0x3E, 0x07, 0xF5, 0xCE, 0x48, 0x00, +0x00, 0x5B, 0x48, 0x00, 0x00, 0x59, 0x49, 0xFF, +0xFE, 0x30, 0x49, 0xFF, 0xFE, 0x27, 0xC0, 0x51, +0x3C, 0x0D, 0xFA, 0xA4, 0x02, 0x00, 0x00, 0x24, +0x96, 0x01, 0x5A, 0x00, 0x60, 0x45, 0x3C, 0x0D, +0xFA, 0xA4, 0x02, 0x00, 0x00, 0x24, 0x96, 0x01, +0x5A, 0x00, 0x61, 0x3E, 0x3C, 0x0D, 0xFA, 0xA4, +0x02, 0x00, 0x00, 0x24, 0x96, 0x01, 0x5A, 0x00, +0xB0, 0x37, 0x3C, 0x0D, 0xFA, 0xA4, 0x02, 0x00, +0x00, 0x24, 0x96, 0x01, 0x5A, 0x00, 0xF0, 0x30, +0x3C, 0x0D, 0xFA, 0xA4, 0x02, 0x00, 0x00, 0x24, +0x96, 0x01, 0x5A, 0x01, 0x00, 0x29, 0x3C, 0x0D, +0xFA, 0xA4, 0x02, 0x00, 0x00, 0x24, 0x96, 0x01, +0x5C, 0xF0, 0x02, 0x40, 0xE9, 0x09, 0x3C, 0x0D, +0xFA, 0xA4, 0x02, 0x00, 0x00, 0x24, 0x96, 0x01, +0x5C, 0xF0, 0x02, 0x5B, 0xE9, 0x18, 0x3C, 0x0D, +0xFA, 0xA4, 0x02, 0x00, 0x00, 0x24, 0x96, 0x01, +0x5C, 0xF0, 0x02, 0xA0, 0xE9, 0x09, 0x3C, 0x0D, +0xFA, 0xA4, 0x02, 0x00, 0x00, 0x24, 0x96, 0x01, +0x5C, 0xF0, 0x03, 0x00, 0xE9, 0x08, 0x3C, 0x0D, +0xFA, 0xA4, 0x02, 0x00, 0x00, 0x24, 0x96, 0x01, +0x5A, 0x0B, 0x00, 0x08, 0x84, 0x02, 0x3E, 0x07, +0xF5, 0xCE, 0x49, 0xFF, 0xFE, 0x34, 0xD5, 0x07, +0xD5, 0x06, 0x92, 0x00, 0x49, 0xFF, 0xFE, 0x2F, +0xD5, 0x02, 0x92, 0x00, 0xEC, 0x0C, 0x3B, 0xFF, +0xFC, 0x84, 0xDD, 0x9E, 0x3A, 0x6F, 0xA0, 0xBC, +0x3C, 0x63, 0xFE, 0x2B, 0x97, 0xB3, 0xCE, 0x08, +0x3C, 0x2D, 0xFA, 0xA1, 0x04, 0x31, 0x00, 0x0A, +0x96, 0xC6, 0xCB, 0xFD, 0xD5, 0x14, 0x44, 0x70, +0x03, 0xE8, 0x3C, 0x0D, 0xFA, 0xA1, 0x04, 0x10, +0x00, 0x0A, 0x96, 0x46, 0xC1, 0x0C, 0x84, 0x01, +0x8E, 0xE1, 0x49, 0xFF, 0xF2, 0xFA, 0xCF, 0xF6, +0x8E, 0xC1, 0xCE, 0xF2, 0x3A, 0x6F, 0xA0, 0x84, +0x48, 0xFF, 0xFE, 0x09, 0x3A, 0x6F, 0xA0, 0x84, +0xDD, 0x9E, 0x92, 0x00, 0xFC, 0x40, 0x81, 0x40, +0x81, 0x21, 0x80, 0xC2, 0x49, 0xFF, 0xFD, 0xEC, +0x3C, 0x2D, 0xFA, 0xA1, 0x46, 0x00, 0x04, 0x01, +0x50, 0x10, 0x06, 0x4C, 0x46, 0x50, 0x02, 0x00, +0xA8, 0x55, 0x50, 0x42, 0x8B, 0x26, 0x84, 0x20, +0xE0, 0x29, 0x80, 0x64, 0xE8, 0x08, 0x99, 0xCC, +0x38, 0x05, 0x05, 0x01, 0x94, 0xF9, 0x8C, 0x21, +0xAC, 0x18, 0xD5, 0xF7, 0x80, 0x89, 0x4E, 0x94, +0x00, 0x03, 0x84, 0x80, 0xC6, 0x25, 0x88, 0x83, +0x94, 0x61, 0x3C, 0x63, 0xFD, 0xAC, 0x8C, 0x24, +0x12, 0x60, 0xFF, 0xFE, 0x3C, 0x03, 0xFD, 0xAD, +0x44, 0x70, 0x00, 0x82, 0x12, 0x00, 0xFF, 0xFF, +0x85, 0x00, 0x3E, 0x3F, 0xFA, 0x58, 0x3C, 0x23, +0xFD, 0x41, 0x97, 0x53, 0xE1, 0x05, 0xE8, 0x14, +0x38, 0x41, 0x9D, 0x01, 0x9D, 0xB9, 0xAD, 0x08, +0x38, 0x01, 0x99, 0x01, 0x9C, 0xBA, 0xAC, 0x09, +0x38, 0x51, 0x89, 0x01, 0x8D, 0x01, 0xAD, 0x4A, +0x8C, 0xE4, 0x8C, 0x26, 0xD5, 0xED, 0x88, 0x64, +0x95, 0xD9, 0xAD, 0xB8, 0xAD, 0xB9, 0xFC, 0xC0, +0xFC, 0x40, 0x83, 0x80, 0x80, 0xC1, 0x81, 0x22, +0x80, 0xE3, 0x49, 0xFF, 0xFD, 0xA1, 0x3C, 0x1D, +0xFA, 0xA1, 0x46, 0x00, 0x04, 0x01, 0x50, 0x20, +0x06, 0x4C, 0x46, 0x30, 0x02, 0x00, 0xA8, 0x8D, +0x84, 0x80, 0x50, 0x11, 0x8B, 0x26, 0x3C, 0x53, +0xF8, 0x33, 0xE0, 0x85, 0xE8, 0x08, 0x98, 0x21, +0x38, 0x2E, 0x11, 0x01, 0x95, 0x41, 0x8C, 0x81, +0xAC, 0xA8, 0xD5, 0xF6, 0x46, 0x80, 0x02, 0x00, +0x84, 0x00, 0x50, 0x14, 0x0B, 0x26, 0x88, 0x24, +0x80, 0xA0, 0x3C, 0xA3, 0xF8, 0x35, 0x98, 0x84, +0xE0, 0x0A, 0xE8, 0x06, 0x98, 0x88, 0x94, 0xD1, +0x8C, 0x01, 0xAD, 0x58, 0xD5, 0xF7, 0x80, 0xA9, +0x95, 0x11, 0x46, 0x90, 0x04, 0x01, 0x9A, 0xE5, +0x50, 0x04, 0x86, 0x4C, 0x84, 0x20, 0x88, 0x03, +0x3D, 0xC3, 0xF8, 0x35, 0x98, 0xCA, 0xE0, 0x3C, +0xE8, 0x07, 0x98, 0xC5, 0x0A, 0x42, 0x80, 0x01, +0xAD, 0x18, 0x8C, 0x21, 0xD5, 0xF6, 0x47, 0xE0, +0x02, 0x00, 0x50, 0x0F, 0x0B, 0x26, 0x88, 0x03, +0x84, 0x80, 0x94, 0x81, 0x80, 0x04, 0x3C, 0x13, +0xF8, 0x35, 0x99, 0x63, 0x94, 0x49, 0xE0, 0x81, +0xE8, 0x0D, 0x3C, 0xA3, 0xF8, 0x34, 0xE0, 0x8A, +0xE8, 0x05, 0x38, 0x13, 0x11, 0x01, 0xAC, 0x50, +0xD5, 0x02, 0xAC, 0x10, 0x8C, 0x81, 0x8C, 0x42, +0xD5, 0xEF, 0x46, 0x60, 0x02, 0x00, 0x50, 0x23, +0x0B, 0x26, 0x88, 0x45, 0x94, 0x11, 0xC7, 0x23, +0x3C, 0x73, 0xFD, 0xAC, 0x8C, 0x04, 0x12, 0x70, +0x7F, 0xFE, 0x3C, 0x43, 0xFD, 0xAD, 0x44, 0x10, +0x00, 0x82, 0x12, 0x40, 0x7F, 0xFF, 0x85, 0x00, +0x3E, 0x3F, 0xFA, 0x58, 0x3C, 0x53, 0xFD, 0x41, +0x97, 0xAB, 0xE1, 0x06, 0xE8, 0x12, 0x38, 0x21, +0x85, 0x01, 0x9D, 0xC9, 0xAC, 0x80, 0x38, 0x41, +0x9D, 0x01, 0x9D, 0x4A, 0xAD, 0x01, 0x38, 0x61, +0x95, 0x01, 0x8D, 0x01, 0xAD, 0x82, 0x8C, 0x24, +0x8C, 0x06, 0xD5, 0xED, 0xAD, 0xC0, 0xAD, 0xC1, +0xFC, 0xC0, 0xFC, 0x41, 0x3C, 0x17, 0xFA, 0xC6, +0x3E, 0x7F, 0xF5, 0x8C, 0x3C, 0x1B, 0xFD, 0xAC, +0x3C, 0x23, 0xFD, 0x45, 0xC2, 0x05, 0x3C, 0x33, +0xFD, 0x45, 0x5A, 0x38, 0x02, 0x77, 0x2E, 0x17, +0xF5, 0x8F, 0x2E, 0x2F, 0xF5, 0x8E, 0x40, 0x30, +0xA0, 0x08, 0xFE, 0xD7, 0x97, 0x5B, 0x3C, 0x5B, +0xFD, 0xAD, 0xC0, 0x17, 0xF0, 0x81, 0x49, 0xFF, +0xFD, 0x03, 0x3C, 0x9D, 0xFA, 0xA1, 0x46, 0x60, +0x04, 0x01, 0x50, 0x73, 0x06, 0x4C, 0x14, 0x74, +0x80, 0x05, 0x3C, 0x83, 0xFD, 0xAC, 0x84, 0x62, +0x12, 0x83, 0x03, 0x26, 0x3C, 0xA3, 0xFD, 0xAD, +0xF0, 0x01, 0x12, 0xA3, 0x03, 0x27, 0xD5, 0x02, +0x80, 0x60, 0x46, 0xF0, 0x02, 0x00, 0x3E, 0x1F, +0xF5, 0x8C, 0x44, 0x40, 0x00, 0x82, 0x84, 0xE0, +0x3E, 0x6F, 0xFA, 0x58, 0x51, 0xE7, 0x8B, 0x26, +0x3C, 0x23, 0xFD, 0x41, 0x97, 0x53, 0xE0, 0xE5, +0x4E, 0xF2, 0x00, 0x8F, 0x02, 0x90, 0x80, 0x02, +0x9C, 0xA1, 0x22, 0xA0, 0x80, 0x03, 0x01, 0xC0, +0x80, 0x09, 0x40, 0x84, 0x80, 0x11, 0x00, 0x50, +0x80, 0x08, 0x38, 0x83, 0x11, 0x09, 0x38, 0xA3, +0x09, 0x09, 0x40, 0x2E, 0x20, 0x08, 0x50, 0xF2, +0x00, 0x02, 0xFE, 0xAF, 0x96, 0x93, 0x38, 0x23, +0x3D, 0x09, 0x3C, 0x83, 0xFD, 0x45, 0x5A, 0x88, +0x02, 0x08, 0x9C, 0xA3, 0x40, 0x52, 0xA0, 0x08, +0x97, 0x6B, 0x38, 0x53, 0x09, 0x09, 0xC0, 0x1D, +0x40, 0x21, 0xF8, 0x00, 0x94, 0x91, 0x12, 0x91, +0x00, 0x00, 0xA5, 0x4B, 0xAD, 0x51, 0x00, 0x90, +0x80, 0x09, 0x00, 0xF0, 0x80, 0x08, 0x40, 0xA4, +0xA0, 0x08, 0x40, 0x55, 0x3C, 0x04, 0xAD, 0x52, +0x3D, 0xC3, 0xFD, 0x45, 0x5B, 0xC0, 0x02, 0x04, +0x8C, 0x63, 0xD5, 0x07, 0x00, 0x80, 0x80, 0x08, +0x8C, 0x64, 0x40, 0x54, 0x20, 0x08, 0xAD, 0x53, +0x8C, 0xE1, 0x8C, 0x26, 0x8C, 0x84, 0xD5, 0xB9, +0x3C, 0x03, 0xFD, 0x45, 0x96, 0x43, 0x5A, 0x18, +0x01, 0x48, 0x84, 0x00, 0x80, 0x67, 0x44, 0x50, +0x00, 0x82, 0x80, 0x40, 0x3E, 0x6F, 0xFA, 0x58, +0x85, 0x26, 0x3C, 0x43, 0xFD, 0x41, 0x97, 0x23, +0xE0, 0x44, 0xE8, 0x2C, 0x22, 0x81, 0x80, 0x02, +0x01, 0xE1, 0x80, 0x09, 0x38, 0x83, 0x15, 0x09, +0x9D, 0x29, 0x22, 0xA1, 0x80, 0x03, 0x00, 0x81, +0x80, 0x08, 0x40, 0xFF, 0x20, 0x08, 0x38, 0xA3, +0x11, 0x09, 0x51, 0xC2, 0x80, 0x02, 0x40, 0x47, +0xA0, 0x04, 0x81, 0x47, 0x97, 0x23, 0x42, 0xA1, +0x24, 0x73, 0x38, 0x43, 0x71, 0x09, 0x02, 0x45, +0x00, 0x04, 0x41, 0xC2, 0x20, 0x09, 0x54, 0xFE, +0x00, 0x01, 0xE9, 0x04, 0x92, 0x8B, 0x97, 0x06, +0xC4, 0x05, 0x40, 0x40, 0x88, 0x0C, 0xFE, 0x27, +0x96, 0x01, 0x8C, 0x41, 0x8C, 0x66, 0x8C, 0xA4, +0xD5, 0xD1, 0x97, 0x43, 0x3C, 0x5B, 0xFD, 0xAD, +0x49, 0xFF, 0xFC, 0x5A, 0x3C, 0x6D, 0xFA, 0xA1, +0x3E, 0x1F, 0xFB, 0x58, 0x50, 0x03, 0x00, 0x40, +0xFA, 0x40, 0x49, 0xFF, 0xF1, 0x9D, 0x3D, 0xC7, +0xF9, 0x9E, 0x3D, 0xCB, 0xFD, 0xD6, 0xFC, 0xC1, +0xFC, 0x00, 0x80, 0xC0, 0x49, 0xFF, 0xFE, 0x34, +0x49, 0xFF, 0xFC, 0x46, 0x3C, 0x0D, 0xFA, 0xA1, +0x46, 0x10, 0x04, 0x01, 0x44, 0x30, 0x00, 0x40, +0x97, 0x31, 0x50, 0x20, 0x86, 0x4C, 0xA8, 0x85, +0x12, 0x30, 0x83, 0x26, 0x50, 0x00, 0x00, 0x40, +0x12, 0x40, 0x83, 0x27, 0xFA, 0x40, 0x3E, 0x1F, +0xFB, 0x58, 0x49, 0xFF, 0xF1, 0x7D, 0x49, 0xFF, +0xFC, 0x36, 0xFC, 0x80, 0x3C, 0x00, 0x07, 0x9A, +0xC0, 0x27, 0x3C, 0x13, 0xFA, 0xE9, 0x9C, 0x89, +0xE2, 0x20, 0x3C, 0x2B, 0xFA, 0xE9, 0xE9, 0x20, +0xFC, 0x00, 0x49, 0xFF, 0xFE, 0x0D, 0x49, 0xFF, +0xFC, 0x1F, 0x3C, 0x5D, 0xFA, 0xA1, 0x46, 0x60, +0x04, 0x01, 0x84, 0x80, 0x50, 0x33, 0x06, 0x4C, +0x3C, 0x4B, 0xFD, 0xAC, 0x50, 0x02, 0x80, 0x40, +0x3C, 0x4B, 0xFD, 0xAD, 0x3E, 0x1F, 0xFB, 0x58, +0xA8, 0xED, 0xFA, 0x40, 0x12, 0x43, 0x03, 0x26, +0x12, 0x43, 0x03, 0x27, 0x49, 0xFF, 0xF1, 0x54, +0x49, 0xFF, 0xFC, 0x0D, 0xFC, 0x80, 0xDD, 0x9E, +0xFC, 0x00, 0x2E, 0x07, 0xFA, 0x1E, 0xC8, 0x03, +0x3E, 0x07, 0xF5, 0xCF, 0x49, 0xFF, 0xFD, 0xE8, +0x84, 0x01, 0x49, 0xFF, 0xFE, 0xDC, 0x3C, 0x13, +0xFE, 0x1C, 0x3C, 0x23, 0xFA, 0xC6, 0x96, 0x0B, +0xFE, 0x16, 0xC0, 0x08, 0x49, 0xFF, 0xFB, 0xF7, +0x2E, 0x37, 0xFA, 0x1E, 0x84, 0x01, 0x3E, 0x37, +0xF5, 0xCF, 0xFC, 0x80, 0xFC, 0x00, 0x49, 0xFF, +0xFD, 0xD3, 0x3C, 0x03, 0xFE, 0x1C, 0x3C, 0x13, +0xFA, 0xC6, 0x96, 0x83, 0xFE, 0x8E, 0xC2, 0x04, +0x84, 0x00, 0x49, 0xFF, 0xFE, 0xC0, 0x3C, 0x33, +0xFD, 0x3C, 0x97, 0x1B, 0x8E, 0x83, 0xE6, 0x92, +0x4E, 0xF2, 0x00, 0xE5, 0x46, 0xF0, 0x02, 0x02, +0x58, 0xF7, 0x83, 0xF0, 0x38, 0x07, 0x91, 0x01, +0x40, 0xF0, 0x3C, 0x00, 0x4A, 0x00, 0x3C, 0x00, +0x30, 0x00, 0xE8, 0x00, 0x06, 0x01, 0xB2, 0x01, +0xB2, 0x01, 0x16, 0x01, 0xB2, 0x01, 0x2C, 0x01, +0xB2, 0x01, 0xA2, 0x01, 0xA2, 0x01, 0xA2, 0x01, +0x3C, 0x01, 0x44, 0x00, 0xB2, 0x01, 0xB2, 0x01, +0x24, 0x00, 0xC0, 0x00, 0x84, 0x00, 0x3C, 0x3C, +0x02, 0xC0, 0x80, 0xC0, 0x48, 0x00, 0x00, 0x90, +0x49, 0x00, 0x15, 0x52, 0x3C, 0x33, 0xFE, 0x1C, +0x3C, 0x63, 0xFA, 0xC6, 0x96, 0x9B, 0x3E, 0x08, +0x08, 0xA0, 0xD5, 0x39, 0x49, 0x00, 0x15, 0x48, +0x84, 0x00, 0x3C, 0x3C, 0x02, 0xC0, 0x80, 0xA0, +0x3D, 0xE3, 0xF8, 0x33, 0xE0, 0x1E, 0xE8, 0x06, +0x95, 0x81, 0x88, 0xC3, 0x8C, 0x01, 0xAD, 0x70, +0xD5, 0xF8, 0x84, 0x00, 0x3E, 0x68, 0x02, 0x80, +0x3D, 0xC3, 0xF8, 0x35, 0x94, 0x41, 0xE0, 0x1C, +0xE8, 0x08, 0x98, 0xB1, 0x99, 0x19, 0x22, 0x11, +0x04, 0x14, 0x8C, 0x01, 0xAC, 0x60, 0xD5, 0xF5, +0x88, 0x23, 0x3E, 0x48, 0x02, 0x80, 0x3C, 0x53, +0xF8, 0x35, 0x3C, 0x33, 0xF8, 0x34, 0x88, 0x65, +0xE0, 0x03, 0xE8, 0x0A, 0x9B, 0x45, 0x51, 0xE2, +0x84, 0x00, 0x8C, 0x01, 0x39, 0xC2, 0x79, 0x11, +0x1B, 0xC0, 0x80, 0x01, 0xD5, 0xF1, 0x3C, 0x23, +0xFE, 0x1C, 0x3C, 0x63, 0xFA, 0xC6, 0x96, 0x93, +0x3C, 0x0C, 0x02, 0xC0, 0x3C, 0x13, 0xF8, 0x33, +0xFE, 0xB6, 0x49, 0xFF, 0xFD, 0x7D, 0xD5, 0x11, +0x49, 0x00, 0x15, 0x0A, 0x3C, 0x53, 0xFE, 0x1C, +0x3C, 0x63, 0xFA, 0xC6, 0x3E, 0x08, 0x08, 0xA0, +0x96, 0xEB, 0x50, 0x10, 0x01, 0xE0, 0x50, 0x20, +0x02, 0x08, 0xFE, 0xF6, 0x49, 0xFF, 0xFD, 0xB6, +0x49, 0x00, 0x15, 0xC9, 0x48, 0x00, 0x00, 0x73, +0x3C, 0x53, 0xFE, 0x1C, 0x3C, 0x63, 0xFA, 0xC6, +0x96, 0xEB, 0x3E, 0x1F, 0xF2, 0x6C, 0x3E, 0x08, +0x11, 0xB8, 0x50, 0x20, 0x80, 0x28, 0xFE, 0xF6, +0x49, 0xFF, 0xFD, 0xA4, 0xD5, 0x63, 0x3C, 0x03, +0xFE, 0x1C, 0x3C, 0x53, 0xFA, 0xC6, 0x96, 0x83, +0x3E, 0x08, 0x08, 0xA0, 0xD5, 0x56, 0x84, 0x01, +0x49, 0x00, 0x28, 0xAF, 0x3C, 0x13, 0xFE, 0x1C, +0x3C, 0x53, 0xFA, 0xC6, 0x96, 0x8B, 0x3E, 0x08, +0x0B, 0x20, 0xD5, 0x4B, 0x3C, 0x23, 0xFE, 0x1C, +0x3C, 0x53, 0xFA, 0xC6, 0x96, 0x93, 0x3E, 0x08, +0x0F, 0x80, 0xD5, 0x43, 0x3C, 0x33, 0xFE, 0x1C, +0x3C, 0x53, 0xFA, 0xC6, 0x96, 0x9B, 0x3E, 0x0F, +0xF0, 0x8C, 0xD5, 0x3B, 0x3D, 0xC3, 0xF8, 0x33, +0xE0, 0x1C, 0xE8, 0x06, 0x95, 0x01, 0x88, 0x83, +0x8C, 0x01, 0xAD, 0xA0, 0xD5, 0xF8, 0x84, 0x00, +0x3E, 0x48, 0x0B, 0x20, 0x3D, 0xE3, 0xF8, 0x35, +0x94, 0x41, 0xE0, 0x1E, 0xE8, 0x08, 0x98, 0xA1, +0x99, 0x59, 0x22, 0x11, 0x01, 0x04, 0x8C, 0x01, +0xAC, 0x68, 0xD5, 0xF5, 0x88, 0x23, 0x3E, 0x48, +0x0B, 0x20, 0x3C, 0x23, 0xF8, 0x35, 0x3C, 0x33, +0xF8, 0x34, 0x88, 0x62, 0xE0, 0x03, 0xE8, 0x0A, +0x9B, 0x42, 0x50, 0x62, 0x80, 0xF0, 0x8C, 0x01, +0x39, 0xC2, 0x19, 0x11, 0x1B, 0xC0, 0x80, 0x01, +0xD5, 0xF1, 0x3C, 0x43, 0xFE, 0x1C, 0x3C, 0x53, +0xFA, 0xC6, 0x96, 0xA3, 0x3C, 0x0C, 0x02, 0xC0, +0xD5, 0x08, 0x3C, 0x43, 0xFE, 0x1C, 0x3C, 0x53, +0xFA, 0xC6, 0x96, 0xA3, 0x3E, 0x08, 0x02, 0x80, +0x3C, 0x13, 0xF8, 0x33, 0xFE, 0xAE, 0x49, 0xFF, +0xFC, 0xF7, 0x49, 0xFF, 0xFA, 0xEC, 0xFC, 0x80, +0x84, 0x00, 0x3E, 0x2F, 0xEA, 0xC8, 0x44, 0x30, +0x00, 0x60, 0x80, 0x22, 0x42, 0x10, 0x0C, 0x73, +0xA1, 0x09, 0x97, 0x06, 0xC4, 0x04, 0x8C, 0x01, +0x5A, 0x08, 0x0F, 0xF9, 0xDD, 0x9E, 0x3E, 0x2F, +0xF6, 0x04, 0x44, 0x30, 0x00, 0x38, 0x80, 0x82, +0x42, 0x40, 0x8C, 0x73, 0x00, 0x52, 0x00, 0x30, +0x3E, 0x3F, 0xEA, 0xC0, 0x44, 0xF0, 0x00, 0x60, +0xCD, 0x08, 0x80, 0x83, 0x42, 0x40, 0x3C, 0x73, +0x84, 0xA1, 0x10, 0x52, 0x00, 0x54, 0xD5, 0x14, +0x80, 0x83, 0x42, 0x40, 0x3C, 0x73, 0x50, 0xF2, +0x00, 0x50, 0x02, 0x47, 0x80, 0x03, 0x8E, 0x81, +0x97, 0x63, 0x12, 0x57, 0x80, 0x03, 0x4E, 0x54, +0x00, 0x08, 0x84, 0x80, 0x84, 0xA0, 0x10, 0x47, +0x80, 0x04, 0x12, 0x57, 0x80, 0x03, 0x44, 0x40, +0x00, 0x60, 0x42, 0x30, 0x10, 0x73, 0x00, 0xF1, +0x80, 0x54, 0x80, 0x03, 0x44, 0x30, 0x00, 0x38, +0x42, 0x20, 0x8C, 0x73, 0x5A, 0xF8, 0x01, 0x04, +0xA0, 0x51, 0xD5, 0x02, 0xB4, 0x22, 0xB6, 0x20, +0xDD, 0x9E, 0xFC, 0x40, 0x44, 0x70, 0x00, 0x60, +0xFF, 0xC4, 0x3E, 0x9F, 0xEA, 0xC0, 0x40, 0x64, +0x9C, 0x00, 0x50, 0x23, 0x00, 0x50, 0x85, 0x40, +0x84, 0x60, 0x12, 0xA1, 0x00, 0x03, 0xAE, 0xD4, +0x81, 0x01, 0x49, 0xFF, 0xFF, 0xB6, 0x44, 0x10, +0x00, 0x38, 0x3E, 0x0F, 0xF6, 0x04, 0x42, 0x04, +0x04, 0x73, 0x50, 0x84, 0x80, 0x08, 0xB4, 0x60, +0x50, 0x53, 0x00, 0x20, 0x38, 0x34, 0x1C, 0x0A, +0x22, 0x10, 0x00, 0x01, 0x22, 0x70, 0x00, 0x00, +0x10, 0xA2, 0x80, 0x04, 0x01, 0xC0, 0x00, 0x31, +0x44, 0x50, 0x00, 0x64, 0x50, 0xA3, 0x00, 0x48, +0xA5, 0x04, 0xA0, 0x85, 0xFF, 0xEC, 0xFE, 0x6C, +0xA8, 0xF1, 0x50, 0x33, 0x00, 0x4C, 0x11, 0xC3, +0x00, 0x5E, 0x50, 0x93, 0x00, 0x40, 0x14, 0x75, +0x00, 0x01, 0x14, 0x13, 0x00, 0x14, 0x51, 0xC3, +0x00, 0x34, 0xA8, 0xB5, 0xA8, 0xB7, 0x85, 0x00, +0x3A, 0x21, 0x8C, 0x00, 0xFF, 0x64, 0xAD, 0x37, +0x12, 0x43, 0x00, 0x08, 0x14, 0x74, 0x80, 0x01, +0x14, 0x13, 0x00, 0x12, 0x3A, 0x2E, 0x0C, 0x20, +0x14, 0x83, 0x00, 0x0A, 0x14, 0x53, 0x00, 0x16, +0xFC, 0xC0, 0x44, 0x20, 0x00, 0x60, 0x3E, 0x1F, +0xEA, 0xC0, 0x42, 0x10, 0x08, 0x73, 0x80, 0x01, +0x50, 0x50, 0x80, 0x20, 0x50, 0x30, 0x80, 0x08, +0x84, 0x80, 0x50, 0xF0, 0x00, 0x58, 0x10, 0x40, +0x80, 0x20, 0x84, 0x5F, 0x84, 0x20, 0xAF, 0x1C, +0xAE, 0xA9, 0xAC, 0x69, 0x12, 0x17, 0x80, 0x02, +0xDD, 0x9E, 0xFC, 0x41, 0x85, 0x00, 0x80, 0xE0, +0x81, 0x21, 0x3E, 0xAF, 0xEA, 0xC8, 0x2E, 0xFF, +0xFA, 0x1F, 0xE1, 0x0F, 0xE8, 0x4B, 0xA4, 0xB8, +0x87, 0x81, 0x40, 0x61, 0x20, 0x0E, 0x40, 0x63, +0x70, 0x02, 0xCE, 0x42, 0xF2, 0x81, 0x49, 0xFF, +0xFF, 0x3D, 0x5A, 0x00, 0x0F, 0x40, 0xF3, 0x01, +0x40, 0x4E, 0x20, 0x0C, 0xFE, 0xE7, 0xAC, 0xF8, +0x03, 0xE4, 0x80, 0x00, 0x40, 0x5E, 0x00, 0x0C, +0x40, 0xF2, 0xF8, 0x04, 0x12, 0xF4, 0x80, 0x00, +0xF0, 0x81, 0x49, 0xFF, 0xFF, 0xC4, 0xF2, 0x01, +0x44, 0x00, 0x00, 0x60, 0x43, 0xC1, 0x00, 0x24, +0x40, 0x15, 0x70, 0x00, 0x80, 0x02, 0xA7, 0x0C, +0xF1, 0x81, 0x58, 0x32, 0x00, 0x01, 0xAE, 0xCC, +0x80, 0x28, 0x49, 0xFF, 0xFF, 0x64, 0x3E, 0x5F, +0xEA, 0xC0, 0x40, 0x22, 0xF0, 0x00, 0x2F, 0xC8, +0x0F, 0x58, 0x00, 0x01, 0x00, 0x20, 0x50, 0x31, +0x00, 0x58, 0x8C, 0x01, 0x96, 0x42, 0xE0, 0x3C, +0x10, 0x11, 0x00, 0x20, 0xF4, 0x01, 0xE9, 0x0B, +0xA7, 0xA4, 0x11, 0xC1, 0x00, 0x20, 0x2E, 0x28, +0x0F, 0x59, 0x58, 0x53, 0x00, 0x40, 0xAF, 0x64, +0xAC, 0x9A, 0xD5, 0x02, 0xAD, 0x9A, 0x8D, 0x01, +0xD5, 0xB3, 0xFC, 0xC1, 0x80, 0x80, 0xCA, 0x0D, +0xFC, 0x00, 0x44, 0x60, 0x00, 0x60, 0x80, 0x01, +0x3E, 0x1F, 0xEA, 0xC0, 0x42, 0x12, 0x18, 0x73, +0x84, 0x44, 0x49, 0x00, 0x31, 0x17, 0xFC, 0x80, +0x5A, 0x28, 0x01, 0x1F, 0x44, 0x00, 0x00, 0x60, +0x3E, 0x3F, 0xEA, 0xC0, 0x42, 0x32, 0x00, 0x73, +0x50, 0x51, 0x80, 0x48, 0x50, 0xF1, 0x80, 0x30, +0xA1, 0x29, 0x04, 0x27, 0x80, 0x01, 0xA1, 0x6A, +0x9A, 0x22, 0x88, 0x80, 0x44, 0x00, 0x00, 0x64, +0x40, 0x32, 0x00, 0x76, 0x04, 0x47, 0x80, 0x02, +0xAC, 0xC8, 0x9A, 0xAC, 0x88, 0xA2, 0x40, 0x02, +0x80, 0x16, 0xAC, 0x09, 0xDD, 0x9E, 0xDD, 0x9E, +0xFC, 0x44, 0x81, 0x00, 0x46, 0x07, 0xFF, 0xFF, +0xB6, 0x3F, 0x50, 0x10, 0x0F, 0xFF, 0x83, 0x82, +0x81, 0x43, 0xF4, 0x81, 0x84, 0xE0, 0xF1, 0x83, +0x2E, 0xFF, 0xFA, 0x1F, 0xE0, 0xEF, 0xE8, 0x3C, +0x40, 0x6E, 0x1C, 0x0E, 0x97, 0x86, 0xCE, 0x36, +0xB4, 0x9F, 0x4C, 0x72, 0x00, 0x34, 0x45, 0xE0, +0x00, 0x38, 0x3E, 0x0F, 0xF6, 0x04, 0x42, 0x03, +0xF8, 0x73, 0x85, 0x3F, 0xF3, 0x03, 0x3E, 0x4F, +0xEA, 0xC8, 0xF0, 0x82, 0x44, 0xF0, 0x00, 0x60, +0x80, 0x24, 0x42, 0x13, 0x3C, 0x73, 0xA1, 0x49, +0x97, 0x46, 0xC5, 0x19, 0x40, 0x05, 0x18, 0x0E, +0x96, 0x06, 0xC8, 0x15, 0x80, 0x06, 0xB0, 0x47, +0x00, 0x2F, 0x80, 0x04, 0xF4, 0x85, 0xF3, 0x84, +0x49, 0xFF, 0xFF, 0x9E, 0xF3, 0x02, 0xF1, 0x07, +0xB4, 0x03, 0x49, 0xFF, 0xEE, 0x38, 0xF3, 0x04, +0xF4, 0x05, 0xE0, 0x03, 0xE8, 0x04, 0x40, 0x93, +0x00, 0x10, 0x80, 0x60, 0x8C, 0xC1, 0x5A, 0x68, +0x0F, 0xDF, 0x4C, 0x94, 0x40, 0x04, 0x84, 0x00, +0xD5, 0x48, 0x8C, 0xE1, 0xD5, 0xC2, 0x46, 0x97, +0xFF, 0xFF, 0x50, 0x24, 0x8F, 0xFF, 0x84, 0xE0, +0xF2, 0x82, 0x44, 0x30, 0x00, 0x60, 0x3E, 0x4F, +0xEA, 0xC8, 0x42, 0x43, 0x8C, 0x73, 0xA1, 0x61, +0x97, 0x46, 0xCD, 0x06, 0x8C, 0xE1, 0x5A, 0x78, +0x0F, 0xF6, 0x84, 0x01, 0xD5, 0x32, 0x40, 0x65, +0x1C, 0x0E, 0x97, 0x86, 0xCE, 0xF8, 0x4C, 0x74, +0x3F, 0xF7, 0x00, 0x2F, 0x80, 0x04, 0x80, 0x07, +0xB0, 0x47, 0x49, 0xFF, 0xFF, 0x69, 0x84, 0xBF, +0x04, 0x9F, 0x80, 0x02, 0x3E, 0x3F, 0xF6, 0x04, +0x44, 0x20, 0x00, 0x38, 0x2F, 0xEF, 0xFA, 0x1F, +0xE0, 0xDE, 0xE8, 0x18, 0x40, 0x0E, 0x18, 0x0E, +0x96, 0x06, 0xC8, 0x12, 0x80, 0x23, 0x42, 0x13, +0x08, 0x73, 0xB4, 0x01, 0xF1, 0x07, 0xF5, 0x85, +0xF3, 0x84, 0xF2, 0x83, 0x49, 0xFF, 0xED, 0xEF, +0xE0, 0x09, 0xF2, 0x03, 0xF3, 0x04, 0xF5, 0x05, +0xE8, 0x03, 0x97, 0x72, 0x81, 0x20, 0x8C, 0xC1, +0xD5, 0xE6, 0xB4, 0xDF, 0xDE, 0xCC, 0xD5, 0xB8, +0xFC, 0xC4, 0xFC, 0x00, 0x44, 0x30, 0x00, 0x38, +0x3E, 0x2F, 0xF6, 0x04, 0x42, 0x20, 0x8C, 0x73, +0x44, 0x40, 0x00, 0x60, 0x3E, 0x1F, 0xEA, 0xC0, +0x42, 0x10, 0x10, 0x73, 0xB4, 0x02, 0xB4, 0x21, +0x49, 0xFF, 0xED, 0xD1, 0xFC, 0x80, 0xFC, 0x42, +0xF2, 0x81, 0x4E, 0x05, 0x00, 0x2A, 0x81, 0x01, +0x96, 0x98, 0xB0, 0x43, 0x46, 0x97, 0xFF, 0xFF, +0x49, 0xFF, 0xFF, 0x26, 0x84, 0xFF, 0x50, 0x94, +0x8F, 0xFF, 0x84, 0xC0, 0x3F, 0xCF, 0xF6, 0x04, +0x44, 0xA0, 0x00, 0x38, 0x2E, 0xFF, 0xFA, 0x1F, +0xE0, 0xCF, 0xE8, 0x12, 0x40, 0x14, 0x18, 0x0E, +0x96, 0x46, 0xC9, 0x0C, 0x80, 0x5C, 0x42, 0x23, +0x28, 0x73, 0xF1, 0x03, 0xB4, 0x02, 0x49, 0xFF, +0xED, 0xAE, 0xE0, 0x09, 0xE8, 0x03, 0x97, 0xF2, +0x81, 0x20, 0x8C, 0xC1, 0xD5, 0xEC, 0xF0, 0x01, +0xB7, 0x20, 0x80, 0x07, 0xD5, 0x02, 0x84, 0x1F, +0xFC, 0xC2, 0xFC, 0x42, 0xB6, 0x5F, 0x4E, 0x05, +0x00, 0x33, 0x83, 0x81, 0x46, 0x97, 0xFF, 0xFF, +0x44, 0x10, 0x00, 0x38, 0x3E, 0xAF, 0xF6, 0x04, +0x84, 0xFF, 0x50, 0x94, 0x8F, 0xFF, 0x84, 0xC0, +0x3E, 0x8F, 0xEA, 0xC8, 0x42, 0xA0, 0x04, 0x73, +0x44, 0x00, 0x00, 0x60, 0x80, 0x48, 0x42, 0x23, +0x00, 0x73, 0xA1, 0x51, 0x97, 0x46, 0xC5, 0x14, +0x40, 0x1E, 0x18, 0x0E, 0x96, 0x46, 0xC9, 0x10, +0x96, 0x98, 0x80, 0x06, 0xB0, 0x43, 0xF3, 0x81, +0x49, 0xFF, 0xFE, 0xDE, 0xB4, 0x0A, 0xF1, 0x03, +0x49, 0xFF, 0xED, 0x79, 0xE0, 0x09, 0xF3, 0x01, +0xE8, 0x03, 0x97, 0xF2, 0x81, 0x20, 0x8C, 0xC1, +0x5A, 0x68, 0x0F, 0xE4, 0xB4, 0x7F, 0x80, 0x07, +0xB7, 0x23, 0xD5, 0x02, 0x84, 0x1F, 0xFC, 0xC2, +0x3C, 0x2D, 0xFA, 0x9F, 0x84, 0x00, 0x3E, 0x3F, +0xF5, 0x8C, 0x84, 0x86, 0xE0, 0x02, 0xE8, 0x0A, +0x80, 0x23, 0x42, 0x10, 0x10, 0x73, 0xA5, 0x4C, +0x92, 0xA8, 0x97, 0x46, 0xC5, 0x03, 0x8C, 0x01, +0xD5, 0xF6, 0xDD, 0x9E, 0xFC, 0x42, 0x3C, 0x03, +0xFD, 0x41, 0x3C, 0x23, 0xFD, 0x61, 0x96, 0x43, +0x96, 0xD3, 0x3C, 0x1F, 0xFA, 0x9F, 0x3E, 0xAF, +0xFA, 0x58, 0x4E, 0x36, 0x00, 0x05, 0x84, 0x81, +0x3C, 0x4B, 0xFD, 0x61, 0x84, 0xE0, 0x3E, 0x6F, +0xEA, 0xC0, 0x81, 0x27, 0x44, 0x50, 0x00, 0x60, +0x3E, 0x0F, 0xEA, 0xC8, 0x42, 0x04, 0x94, 0x73, +0xA0, 0x81, 0x54, 0x11, 0x00, 0x01, 0xC9, 0x07, +0x92, 0x46, 0x96, 0x86, 0xC2, 0x04, 0x80, 0x09, +0x49, 0xFF, 0xFE, 0x29, 0x44, 0xF0, 0x00, 0x60, +0x3E, 0x3F, 0xEA, 0xC8, 0x42, 0x34, 0xBC, 0x73, +0xA1, 0x19, 0x54, 0x52, 0x00, 0x01, 0x4E, 0x52, +0x00, 0xC2, 0x92, 0x86, 0x97, 0x06, 0x4E, 0x42, +0x00, 0xBE, 0x3C, 0x03, 0xFD, 0x61, 0x05, 0xE3, +0x00, 0x16, 0x44, 0x20, 0x00, 0x64, 0x96, 0xC3, +0x40, 0x1F, 0x08, 0x37, 0x41, 0xC0, 0x8F, 0x97, +0x5C, 0xFE, 0x01, 0x00, 0x20, 0x83, 0x00, 0x21, +0xE8, 0x05, 0x4F, 0xC3, 0x00, 0x06, 0x87, 0x81, +0xD5, 0x03, 0x45, 0xC0, 0x00, 0xFF, 0x5A, 0x87, +0xFF, 0x64, 0x5A, 0x8F, 0x80, 0x04, 0x48, 0x00, +0x00, 0xA2, 0x3C, 0x33, 0xFA, 0xC6, 0x84, 0x06, +0x3E, 0x2F, 0xF5, 0x8C, 0x42, 0x24, 0x00, 0x73, +0x04, 0x43, 0x00, 0x13, 0x44, 0x50, 0x00, 0x64, +0x58, 0x11, 0x89, 0x00, 0x04, 0x33, 0x00, 0x14, +0x40, 0xF2, 0x15, 0xF6, 0xA4, 0x12, 0xA5, 0x13, +0x3C, 0x1B, 0xFA, 0xC6, 0x40, 0x11, 0x94, 0x36, +0x12, 0xFF, 0x80, 0x04, 0x12, 0x1F, 0x80, 0x05, +0x12, 0x0F, 0x80, 0x06, 0x87, 0xC1, 0x12, 0x4F, +0x80, 0x07, 0x11, 0xE1, 0x00, 0x09, 0xF0, 0x02, +0xF1, 0x03, 0xF5, 0x81, 0xB6, 0x5F, 0x49, 0xFF, +0xEC, 0xE2, 0x3C, 0x20, 0x07, 0xAE, 0xB4, 0x7F, +0xE0, 0x40, 0xF5, 0x01, 0xE8, 0x19, 0x3D, 0xE3, +0xFA, 0xC6, 0x00, 0x01, 0x80, 0x09, 0x04, 0x23, +0x00, 0x13, 0x04, 0xF3, 0x00, 0x14, 0x58, 0x1F, +0x00, 0x04, 0x3C, 0x1B, 0xFA, 0xC6, 0x58, 0x40, +0x00, 0x04, 0x40, 0x11, 0x14, 0x36, 0x40, 0x57, +0x94, 0xB6, 0x10, 0x41, 0x80, 0x09, 0xAC, 0x5A, +0xAD, 0x5B, 0x11, 0xC1, 0x80, 0x08, 0x2F, 0xC7, +0xF0, 0x65, 0x5B, 0xC8, 0x01, 0x06, 0x80, 0x08, +0x49, 0xFF, 0xF5, 0xF9, 0xD5, 0x07, 0x50, 0x34, +0x03, 0xA6, 0x23, 0xE3, 0x00, 0x0A, 0x39, 0xE5, +0x0D, 0x09, 0x2E, 0x07, 0xF5, 0x8E, 0x84, 0x81, +0x8C, 0x01, 0x40, 0x22, 0x20, 0x0C, 0x3E, 0x07, +0xF5, 0x8E, 0xFF, 0xD7, 0xD5, 0x42, 0x49, 0xFF, +0xFF, 0x41, 0x3C, 0x4D, 0xFA, 0x9F, 0x80, 0xA0, +0xDC, 0x06, 0x44, 0xFF, 0xFF, 0x80, 0x10, 0xF3, +0x00, 0x21, 0xD5, 0x38, 0x3C, 0x83, 0xFA, 0xC6, +0x87, 0xC6, 0x3E, 0x2F, 0xF5, 0x8C, 0x42, 0x20, +0x78, 0x73, 0x58, 0x34, 0x08, 0x03, 0x04, 0x43, +0x00, 0x13, 0x04, 0x83, 0x00, 0x14, 0x84, 0x23, +0x44, 0xF0, 0x00, 0x64, 0x11, 0xC1, 0x00, 0x08, +0x2F, 0xC7, 0xF0, 0x65, 0x3C, 0x3B, 0xFA, 0xC6, +0x10, 0x11, 0x00, 0x09, 0x40, 0x32, 0x3C, 0x76, +0x40, 0x14, 0x3C, 0x36, 0x10, 0x03, 0x00, 0x21, +0xAC, 0xD2, 0xAC, 0x53, 0x5B, 0xC8, 0x01, 0x07, +0xB6, 0x1F, 0x49, 0xFF, 0xF5, 0xB8, 0xB4, 0xBF, +0xD5, 0x07, 0x50, 0x00, 0x03, 0xA6, 0x23, 0xE3, +0x00, 0x0A, 0x39, 0xE5, 0x01, 0x09, 0x2E, 0x27, +0xF5, 0x8E, 0x84, 0x81, 0x8C, 0x41, 0x40, 0x52, +0x14, 0x0C, 0x3E, 0x27, 0xF5, 0x8E, 0xFF, 0xEF, +0x97, 0xF9, 0x8D, 0x21, 0x50, 0x63, 0x00, 0x60, +0x5A, 0x90, 0x0F, 0x04, 0x48, 0xFF, 0xFF, 0x20, +0x3E, 0x3F, 0xF5, 0x8C, 0x3D, 0xE3, 0xFA, 0xC6, +0x85, 0x00, 0x80, 0x23, 0x85, 0x26, 0x40, 0x43, +0xA0, 0x0E, 0x97, 0x06, 0xCC, 0x1A, 0x81, 0xE1, +0x42, 0xF4, 0x24, 0x73, 0x02, 0x57, 0x80, 0x04, +0x92, 0xA8, 0x97, 0x46, 0xCD, 0x04, 0x10, 0x51, +0x80, 0x09, 0xD5, 0x0F, 0x10, 0x41, 0x80, 0x09, +0x00, 0x07, 0x80, 0x09, 0x51, 0xC4, 0x03, 0xA6, +0x58, 0x20, 0x00, 0x08, 0x38, 0x45, 0x71, 0x09, +0x59, 0xEF, 0x00, 0x09, 0x10, 0x27, 0x80, 0x09, +0x8D, 0x01, 0x8C, 0x66, 0x5A, 0x88, 0x0A, 0xE1, +0x54, 0x7F, 0x08, 0x00, 0x97, 0xB9, 0x3D, 0xEB, +0xFA, 0xC6, 0xC6, 0x06, 0x2E, 0x67, 0xF5, 0x8F, +0x5A, 0x60, 0xFF, 0x05, 0x8C, 0xC1, 0x3E, 0x67, +0xF5, 0x8F, 0xFC, 0xC2, 0xFC, 0x43, 0x84, 0x00, +0x3C, 0x0F, 0xFA, 0xA0, 0x12, 0x0F, 0x80, 0x08, +0x12, 0x0F, 0x80, 0x09, 0x80, 0x20, 0x3E, 0x3F, +0xEA, 0xC8, 0x44, 0x40, 0x00, 0x60, 0x80, 0x43, +0x42, 0x20, 0x90, 0x73, 0xA1, 0x51, 0x3E, 0x9F, +0xEA, 0xC8, 0x97, 0x46, 0xC5, 0x03, 0x8C, 0x01, +0x96, 0x00, 0x8C, 0x21, 0x5A, 0x18, 0x0F, 0xF5, +0x2E, 0x6F, 0xFA, 0x1F, 0xC8, 0x0F, 0x4E, 0x67, +0x00, 0x0E, 0x50, 0x0F, 0x80, 0x12, 0xB0, 0x44, +0x49, 0xFF, 0xFD, 0x15, 0x3E, 0x7F, 0xEA, 0xC0, +0x84, 0xC0, 0x45, 0xC0, 0x00, 0x60, 0x48, 0x00, +0x01, 0x19, 0xCE, 0x0B, 0xC8, 0x0A, 0x81, 0x00, +0x80, 0x08, 0x8D, 0x01, 0x49, 0xFF, 0xFC, 0xEF, +0x5A, 0x88, 0x0F, 0xFC, 0x48, 0x00, 0x01, 0x19, +0x3E, 0x7F, 0xEA, 0xC0, 0xB6, 0xFF, 0x81, 0x07, +0x02, 0xFF, 0x80, 0x08, 0x85, 0x40, 0x45, 0xC0, +0x00, 0x60, 0x41, 0xE7, 0xA8, 0x0E, 0x54, 0x1F, +0x00, 0x01, 0xC9, 0x07, 0x80, 0x69, 0x42, 0x35, +0x70, 0x73, 0xA1, 0x19, 0x97, 0x06, 0xCC, 0x05, +0x8D, 0x41, 0x5A, 0xA8, 0x0F, 0xF4, 0xD5, 0xD2, +0x83, 0x81, 0x80, 0x0A, 0x02, 0x1F, 0x80, 0x09, +0xB0, 0x85, 0x80, 0x7C, 0x49, 0xFF, 0xFE, 0x09, +0x80, 0xE0, 0x5A, 0x07, 0xFF, 0xC8, 0x80, 0x4A, +0xB6, 0x5F, 0x80, 0x07, 0x02, 0x1F, 0x80, 0x08, +0xB0, 0x85, 0x80, 0x7C, 0x49, 0xFF, 0xFE, 0x2B, +0xB4, 0xBF, 0x80, 0xC0, 0x81, 0x40, 0xD0, 0x12, +0x5A, 0x07, 0xFF, 0xB9, 0x02, 0x1F, 0x80, 0x09, +0xB0, 0x85, 0x80, 0x7C, 0x49, 0xFF, 0xFD, 0xF1, +0x4C, 0x70, 0x00, 0x07, 0x80, 0x46, 0x5A, 0x07, +0xFF, 0x04, 0x80, 0xE0, 0xD5, 0xE6, 0x80, 0xE0, +0xD5, 0x03, 0x5A, 0x07, 0xFF, 0xA8, 0x5A, 0x77, +0xFF, 0xA6, 0x4F, 0xC2, 0x00, 0x09, 0x5B, 0xC0, +0x01, 0x04, 0x87, 0x81, 0xD5, 0xCF, 0x5A, 0x6F, +0xFF, 0x0D, 0xD5, 0x9C, 0xFD, 0x03, 0x02, 0x2F, +0x80, 0x09, 0x02, 0x3F, 0x80, 0x08, 0x80, 0x9C, +0x49, 0xFF, 0xFD, 0x30, 0x5A, 0x08, 0x01, 0xF3, +0x44, 0x10, 0x00, 0x60, 0x42, 0x43, 0x04, 0x24, +0x3E, 0x3F, 0xF6, 0x04, 0x3E, 0x5F, 0xEA, 0xC0, +0x40, 0xA2, 0x90, 0x00, 0x81, 0xE3, 0x44, 0x00, +0x00, 0x38, 0x80, 0x4A, 0x42, 0xF3, 0x80, 0x73, +0xA2, 0x52, 0xB4, 0x0F, 0x87, 0x81, 0xF3, 0x82, +0xF4, 0x81, 0xB6, 0x5F, 0xF2, 0x83, 0x49, 0xFF, +0xEB, 0x86, 0x02, 0xFF, 0x80, 0x08, 0x40, 0x3E, +0x18, 0x0C, 0x40, 0x11, 0xBC, 0x04, 0xB4, 0x7F, +0x12, 0x1F, 0x80, 0x08, 0xF1, 0x01, 0x02, 0x5F, +0x80, 0x09, 0x84, 0x40, 0xAE, 0x9C, 0x40, 0x4E, +0x1C, 0x0C, 0x00, 0x25, 0x00, 0x20, 0x41, 0xC4, +0x84, 0x00, 0xFF, 0x2F, 0x12, 0x4F, 0x80, 0x09, +0x8C, 0x41, 0x00, 0x4E, 0x00, 0x04, 0x2E, 0x18, +0x0F, 0x58, 0x96, 0xD2, 0x54, 0x52, 0x00, 0xBF, +0xE0, 0x61, 0x10, 0x5E, 0x00, 0x04, 0xF0, 0x85, +0x50, 0x45, 0x00, 0x20, 0x10, 0x35, 0x00, 0x20, +0xF5, 0x02, 0xE9, 0x0E, 0x00, 0x4E, 0x00, 0x04, +0x2E, 0x08, 0x0F, 0x59, 0x58, 0x32, 0x00, 0x40, +0x10, 0x15, 0x00, 0x20, 0x12, 0x05, 0x00, 0x2E, +0x10, 0x3E, 0x00, 0x04, 0xD5, 0x05, 0xA4, 0xA1, +0xE0, 0x40, 0xE8, 0x02, 0xAC, 0x21, 0x44, 0x10, +0x00, 0x60, 0x42, 0xA3, 0x04, 0x24, 0x41, 0xE4, +0xA8, 0x00, 0xF5, 0x81, 0x00, 0x5F, 0x00, 0x04, +0xB6, 0x3F, 0x58, 0x22, 0x80, 0x01, 0x10, 0x2F, +0x00, 0x04, 0xFD, 0x03, 0x49, 0xFF, 0xFB, 0xA5, +0xF5, 0x01, 0x45, 0xC0, 0x00, 0x38, 0x80, 0x65, +0x42, 0x33, 0xF0, 0x73, 0x40, 0x04, 0x28, 0x00, +0xA5, 0x1C, 0xA0, 0x5D, 0xAD, 0x07, 0x12, 0x40, +0x00, 0x08, 0xA8, 0x45, 0x8C, 0x18, 0xA0, 0x81, +0xB4, 0x7F, 0xE0, 0x41, 0xE8, 0x02, 0xA8, 0x41, +0x45, 0xE0, 0x00, 0x38, 0x42, 0x53, 0xF8, 0x73, +0x42, 0xA3, 0x0C, 0x24, 0x40, 0x44, 0x28, 0x00, +0x00, 0x72, 0x80, 0x31, 0x97, 0xB0, 0x10, 0x72, +0x00, 0x5E, 0x80, 0x06, 0x84, 0x20, 0xB6, 0x9F, +0x49, 0x00, 0x0D, 0x04, 0xB4, 0xBF, 0x5A, 0x08, +0x01, 0x07, 0xA0, 0x29, 0xB4, 0x25, 0x38, 0x04, +0xA8, 0x0A, 0xA8, 0x69, 0x80, 0x06, 0x49, 0x00, +0x0F, 0x91, 0x48, 0xFF, 0xFF, 0x17, 0x81, 0xE9, +0x42, 0xF3, 0x70, 0x73, 0x04, 0x47, 0x80, 0x01, +0x40, 0x52, 0x18, 0x09, 0x97, 0x46, 0xC5, 0x1C, +0x97, 0x06, 0xC4, 0x1A, 0x02, 0x03, 0x80, 0x2E, +0x8E, 0x01, 0x96, 0x43, 0x12, 0x13, 0x80, 0x2E, +0x4E, 0x17, 0x00, 0x13, 0x2E, 0xA7, 0xFA, 0x1F, +0x8D, 0x41, 0x3E, 0xA7, 0xFA, 0x1F, 0x8C, 0xC1, +0x50, 0x73, 0x80, 0x60, 0x5A, 0x60, 0x0F, 0x0D, +0x02, 0x2F, 0x80, 0x08, 0x40, 0x31, 0x18, 0x0E, +0x96, 0xC6, 0xCB, 0xF6, 0xD5, 0xDD, 0x80, 0x06, +0x49, 0xFF, 0xFB, 0xD5, 0xD5, 0xF1, 0x84, 0x01, +0xFC, 0xC3, 0xFC, 0x01, 0x3C, 0x23, 0xF8, 0x3A, +0x96, 0x41, 0x3C, 0x53, 0xF8, 0x3C, 0x92, 0x10, +0x4C, 0x20, 0xC0, 0x08, 0xD0, 0x16, 0x3C, 0x53, +0xF8, 0x3D, 0xD0, 0x15, 0x84, 0x02, 0xD5, 0x1C, +0x3C, 0x33, 0xF8, 0x3B, 0x4C, 0x30, 0xC0, 0x08, +0xD0, 0x10, 0x3C, 0x53, 0xF8, 0x3D, 0xD0, 0x0F, +0x84, 0x06, 0xD5, 0x12, 0xD0, 0x0E, 0x3C, 0x53, +0xF8, 0x3D, 0xD8, 0x0D, 0x84, 0x04, 0xD5, 0x0C, +0x84, 0x01, 0xD5, 0x0A, 0x84, 0x03, 0xD5, 0x08, +0x84, 0x07, 0xD5, 0x06, 0x84, 0x05, 0xD5, 0x04, +0x84, 0x08, 0xD5, 0x02, 0x84, 0x00, 0xFC, 0x81, +0xFC, 0x20, 0x80, 0x81, 0x3C, 0x6D, 0xFC, 0xC6, +0x2A, 0x22, 0x00, 0x01, 0x84, 0xA0, 0x38, 0x33, +0x09, 0x11, 0x84, 0x41, 0x2A, 0x72, 0x00, 0x01, +0x38, 0x83, 0x1D, 0x11, 0xE1, 0x03, 0x40, 0x51, +0x3C, 0x1B, 0x8C, 0x41, 0x96, 0x90, 0x40, 0x34, +0x3C, 0x1B, 0x5A, 0x28, 0x14, 0xF5, 0x39, 0xC3, +0x01, 0x11, 0xE0, 0x7C, 0xE8, 0x03, 0x38, 0x00, +0x95, 0x09, 0xFC, 0xA0, 0x84, 0x01, 0xDD, 0x9E, +0x3C, 0x30, 0x07, 0x97, 0x54, 0x41, 0x81, 0x00, +0xC4, 0x47, 0x80, 0xA0, 0x80, 0x02, 0x3C, 0x20, +0x01, 0x2D, 0xCA, 0x42, 0xFC, 0x40, 0x3C, 0x63, +0xFF, 0xAC, 0x97, 0xB3, 0xCE, 0x3F, 0x3C, 0x70, +0x07, 0xB7, 0xC7, 0x3C, 0x3C, 0x93, 0xF8, 0x35, +0x5A, 0x08, 0x01, 0x39, 0x3C, 0x33, 0xFF, 0x91, +0x3C, 0x43, 0xFF, 0x92, 0xC4, 0x18, 0x54, 0x23, +0x80, 0x10, 0xC2, 0x15, 0x40, 0x02, 0xA4, 0x57, +0x96, 0x11, 0x50, 0x80, 0x01, 0x04, 0x3E, 0x2F, +0xF0, 0x8C, 0xFF, 0x0C, 0x38, 0xA1, 0x21, 0x11, +0x44, 0xF0, 0x00, 0x64, 0x41, 0xC2, 0x3F, 0x96, +0x41, 0xEE, 0x28, 0x07, 0xE0, 0xCA, 0x40, 0x6F, +0x3C, 0x02, 0xD5, 0x02, 0x80, 0xC0, 0xC3, 0x1C, +0x97, 0xEE, 0xC7, 0x1A, 0x40, 0x52, 0xA4, 0xB7, +0x3E, 0x0F, 0xF0, 0x8C, 0x50, 0x72, 0x80, 0xF0, +0xFE, 0xCC, 0x38, 0x90, 0x1D, 0x11, 0x44, 0x10, +0x00, 0x64, 0x40, 0x31, 0x84, 0x76, 0x85, 0x00, +0xE0, 0x69, 0x40, 0x04, 0x24, 0x07, 0x40, 0x00, +0x3C, 0x02, 0xFE, 0x36, 0xD5, 0x06, 0x84, 0x01, +0xDD, 0x9E, 0x84, 0x01, 0xD5, 0x02, 0x80, 0x06, +0xFC, 0xC0, 0xFC, 0x41, 0x3C, 0x63, 0xF8, 0x3C, +0x3C, 0xA3, 0xF8, 0x3A, 0x83, 0x80, 0xF1, 0x81, +0x42, 0x05, 0x18, 0x24, 0x84, 0x20, 0x46, 0x27, +0xFF, 0xFF, 0x97, 0x81, 0x3C, 0x1B, 0xFD, 0x0B, +0x85, 0x00, 0x50, 0x91, 0x0F, 0xFF, 0x3C, 0x33, +0xF8, 0x3B, 0xE2, 0x6A, 0x4E, 0xF3, 0x01, 0x3E, +0x3C, 0x73, 0xF8, 0x3C, 0x3C, 0x03, 0xF8, 0x3D, +0xE2, 0x07, 0x4E, 0xF3, 0x00, 0xED, 0x3C, 0x3D, +0xFC, 0xC6, 0xF1, 0x01, 0x95, 0x31, 0x38, 0x21, +0x99, 0x11, 0xE0, 0x41, 0x4E, 0xF3, 0x00, 0xDE, +0x3D, 0xE3, 0xF8, 0x3A, 0x3C, 0x53, 0xF8, 0x3C, +0x4C, 0xAF, 0x00, 0x45, 0xD7, 0x66, 0x3C, 0x13, +0xF8, 0x3B, 0x4C, 0x15, 0x00, 0x63, 0x4C, 0x70, +0x00, 0x61, 0x3C, 0x53, 0xF8, 0x35, 0x9E, 0x31, +0x9A, 0x45, 0x39, 0xE1, 0x85, 0x11, 0xE0, 0x5E, +0xE9, 0x29, 0x9A, 0x75, 0x39, 0xE1, 0x85, 0x11, +0xE0, 0x5E, 0xE9, 0x24, 0x9C, 0x71, 0x41, 0xE0, +0x94, 0x01, 0x38, 0xF1, 0xF9, 0x11, 0x40, 0xF7, +0x88, 0x07, 0xE8, 0x1C, 0x41, 0xE1, 0x90, 0x00, +0x22, 0xFF, 0x7F, 0xFF, 0xE0, 0x4F, 0xE9, 0x16, +0x23, 0xEF, 0x00, 0x01, 0x40, 0xFF, 0x08, 0x07, +0xE8, 0x11, 0x88, 0x05, 0x38, 0x01, 0x81, 0x11, +0xE0, 0x40, 0xE9, 0x0C, 0x98, 0x35, 0x38, 0x01, +0x81, 0x11, 0xE0, 0x02, 0xE8, 0x07, 0x88, 0xA1, +0x38, 0x51, 0x95, 0x11, 0xE0, 0xA2, 0x4E, 0xF3, +0x00, 0xAD, 0x88, 0x64, 0x22, 0x31, 0x80, 0x01, +0xE0, 0x62, 0x4E, 0xF2, 0x00, 0x9B, 0x8C, 0xE1, +0x8C, 0xC1, 0x97, 0xF9, 0x97, 0xB1, 0x48, 0x00, +0x00, 0x95, 0xDF, 0x12, 0x88, 0x83, 0x22, 0x02, +0x00, 0x01, 0xE0, 0x02, 0x4E, 0xF2, 0x00, 0x8E, +0x3C, 0x43, 0xF8, 0x35, 0x88, 0x86, 0x94, 0x61, +0x38, 0x51, 0x91, 0x11, 0xE0, 0xA2, 0x4E, 0xF2, +0x00, 0x85, 0x88, 0x61, 0xD5, 0x25, 0x4C, 0x70, +0x40, 0x11, 0x88, 0x83, 0x22, 0x02, 0x7F, 0xFF, +0xE0, 0x40, 0xE9, 0x7B, 0x3C, 0x43, 0xF8, 0x35, +0x88, 0x86, 0x94, 0x61, 0x38, 0x51, 0x91, 0x11, +0xE0, 0xA2, 0xE8, 0x73, 0x88, 0x61, 0xD5, 0x28, +0x3C, 0xF3, 0xF8, 0x3B, 0x4C, 0xF5, 0x40, 0x47, +0xDF, 0x12, 0x3C, 0x03, 0xF8, 0x35, 0x9B, 0x70, +0x94, 0x69, 0x39, 0xE1, 0x95, 0x11, 0xE0, 0x5E, +0xE9, 0x64, 0x88, 0x23, 0x22, 0x00, 0x80, 0x01, +0xE0, 0x02, 0xE8, 0x5F, 0x88, 0x64, 0x22, 0x01, +0x80, 0x01, 0xD5, 0x31, 0x4C, 0x70, 0x40, 0x14, +0x3C, 0x53, 0xF8, 0x35, 0x9A, 0x35, 0x88, 0x09, +0x94, 0x41, 0x39, 0xE1, 0x81, 0x11, 0xE0, 0x5E, +0xE9, 0x50, 0x88, 0x23, 0x22, 0x50, 0x80, 0x01, +0xE0, 0x45, 0xE9, 0x4B, 0x88, 0x64, 0x22, 0x31, +0xFF, 0xFF, 0xD5, 0x3F, 0x4C, 0xAF, 0x00, 0x41, +0x88, 0x83, 0x22, 0x02, 0x7F, 0xFF, 0xE0, 0x40, +0xE9, 0x40, 0x3C, 0x13, 0xF8, 0x35, 0x9F, 0x71, +0x8A, 0xA1, 0x39, 0xE1, 0x95, 0x11, 0xE0, 0x5E, +0xE9, 0x38, 0x9A, 0x31, 0x38, 0x51, 0x81, 0x11, +0xE0, 0x45, 0xE9, 0x33, 0x9C, 0x31, 0x8A, 0x01, +0x38, 0x31, 0x81, 0x11, 0xE0, 0x62, 0xE8, 0x2D, +0x22, 0x02, 0x00, 0x01, 0xE0, 0x02, 0xE9, 0x35, +0xD5, 0x28, 0x4C, 0xAF, 0x00, 0x22, 0xD7, 0x5A, +0x4C, 0x70, 0x40, 0x24, 0x3C, 0x03, 0xF8, 0x35, +0x9B, 0x70, 0x39, 0xE1, 0x95, 0x11, 0xE0, 0x5E, +0xE9, 0x1C, 0x98, 0x70, 0x38, 0x51, 0x85, 0x11, +0xE0, 0xA2, 0xE8, 0x17, 0x9E, 0x71, 0x9B, 0x48, +0x39, 0xE1, 0x95, 0x11, 0xE0, 0x5E, 0xE9, 0x11, +0x88, 0x83, 0x22, 0x42, 0x7F, 0xFF, 0xE0, 0x44, +0xE9, 0x0C, 0x88, 0x01, 0x38, 0x31, 0x81, 0x11, +0xE0, 0x43, 0xE8, 0x13, 0xD5, 0x06, 0x88, 0x83, +0x22, 0x12, 0x7F, 0xFF, 0xE0, 0x41, 0xE8, 0x24, +0x8C, 0xE1, 0x8C, 0xC1, 0x97, 0xF9, 0x97, 0xB1, +0x48, 0xFF, 0xFF, 0x12, 0x50, 0x55, 0x00, 0x01, +0x40, 0xA2, 0x80, 0x13, 0x48, 0xFF, 0xFF, 0x05, +0x3C, 0x47, 0xFD, 0x0B, 0x3C, 0x03, 0xFF, 0x97, +0xE0, 0x82, 0xE8, 0x05, 0x3C, 0x2B, 0xFD, 0x0B, +0x3C, 0x6B, 0xFD, 0x0A, 0x5A, 0x88, 0x14, 0x07, +0x80, 0x06, 0x80, 0x3C, 0x49, 0xFF, 0xFE, 0x72, +0xD5, 0xE4, 0x38, 0x6E, 0x21, 0x09, 0x8D, 0x01, +0x54, 0x84, 0x00, 0xFF, 0xD5, 0xDE, 0x22, 0x42, +0x00, 0x01, 0xE0, 0x82, 0xE8, 0xDA, 0x3C, 0x03, +0xF8, 0x35, 0x88, 0x06, 0x9F, 0x41, 0x39, 0xE1, +0x95, 0x11, 0xE0, 0x5E, 0xE9, 0xD2, 0x38, 0x11, +0x81, 0x11, 0xE0, 0x22, 0xE8, 0xCE, 0x8C, 0x01, +0xD5, 0x19, 0x3C, 0x03, 0xF8, 0x35, 0x9B, 0x70, +0x94, 0x69, 0x39, 0xE1, 0x95, 0x11, 0xE0, 0x5E, +0xE9, 0xC4, 0x88, 0x23, 0x22, 0x50, 0x80, 0x01, +0xE0, 0xA2, 0xE8, 0xBF, 0x88, 0x83, 0x22, 0x42, +0x00, 0x01, 0xE0, 0x82, 0xE8, 0xBA, 0x88, 0x06, +0x9C, 0x41, 0x38, 0x51, 0x85, 0x11, 0xE0, 0xA2, +0xE8, 0xB4, 0x38, 0x01, 0x81, 0x11, 0xD5, 0x87, +0x80, 0x08, 0xFC, 0xC1, 0xFC, 0x46, 0x3C, 0x83, +0xF8, 0x35, 0xB1, 0x8A, 0x80, 0xE2, 0x3A, 0x03, +0x04, 0x20, 0x40, 0x01, 0xA0, 0x77, 0x96, 0x81, +0x96, 0x52, 0xF4, 0x81, 0x96, 0x90, 0x97, 0x19, +0xAE, 0x78, 0xAE, 0x79, 0x85, 0xFE, 0x96, 0x60, +0xF2, 0x83, 0x8E, 0x41, 0xF5, 0x87, 0x40, 0x04, +0x04, 0x08, 0x97, 0x62, 0x42, 0x34, 0x3C, 0x24, +0xF1, 0x82, 0x97, 0x13, 0x8E, 0x21, 0x20, 0x9F, +0x80, 0x28, 0x21, 0xCF, 0x80, 0x29, 0x20, 0x6F, +0x80, 0x2A, 0x20, 0xAF, 0x80, 0x2B, 0xAF, 0x7A, +0xAF, 0x7B, 0xF0, 0x84, 0xF3, 0x85, 0xF1, 0x88, +0xF4, 0x89, 0x3C, 0x53, 0xF8, 0x3C, 0xF0, 0x02, +0xE2, 0xA0, 0xE8, 0x2F, 0x42, 0x34, 0xA0, 0x24, +0x22, 0x0F, 0x80, 0x10, 0xF3, 0x86, 0x3C, 0x4D, +0xFC, 0xC6, 0x84, 0x20, 0x97, 0xB3, 0x40, 0x54, +0x80, 0x11, 0x41, 0xEE, 0x00, 0x11, 0xE0, 0x06, +0xE9, 0x1D, 0xF1, 0x06, 0x80, 0x45, 0x88, 0x20, +0x94, 0x49, 0x88, 0x24, 0x40, 0xFF, 0x08, 0x07, +0xE9, 0x0E, 0xF3, 0x04, 0x04, 0xFF, 0x80, 0x05, +0x88, 0x23, 0xF3, 0x01, 0x38, 0xF0, 0xBC, 0x11, +0x40, 0xF7, 0x8C, 0x07, 0xE8, 0x07, 0x8C, 0x41, +0x96, 0x93, 0xD5, 0xF1, 0x9D, 0x81, 0x97, 0xB2, +0xD5, 0x08, 0x8E, 0x01, 0x96, 0x03, 0x84, 0x22, +0xD5, 0xE3, 0xC9, 0xF9, 0x20, 0x63, 0x80, 0x02, +0x3D, 0xE3, 0xF8, 0x3D, 0xF2, 0x02, 0xE2, 0x5E, +0xE8, 0x32, 0x80, 0x82, 0x8C, 0x81, 0x40, 0x35, +0x00, 0x11, 0x96, 0x23, 0x3C, 0x5D, 0xFC, 0xC6, +0x42, 0x44, 0xA0, 0x24, 0x84, 0x20, 0xF3, 0x86, +0x40, 0xA4, 0x80, 0x11, 0x41, 0xEE, 0x00, 0x11, +0xF2, 0x06, 0xE0, 0x40, 0xE9, 0x1D, 0x98, 0x60, +0x94, 0x49, 0x80, 0x4A, 0x88, 0x25, 0x40, 0xFF, +0x08, 0x07, 0xE9, 0x0E, 0xF3, 0x04, 0x04, 0xFF, +0x80, 0x05, 0x88, 0x23, 0xF3, 0x01, 0x38, 0xF0, +0xBC, 0x11, 0x40, 0xF7, 0x8C, 0x07, 0xE8, 0x08, +0x8C, 0x41, 0x96, 0x93, 0xD5, 0xF1, 0x8E, 0x01, +0x40, 0xA0, 0x00, 0x10, 0xD5, 0x08, 0x8C, 0x01, +0x96, 0x03, 0x84, 0x22, 0xD5, 0xE2, 0xC9, 0xF8, +0x20, 0xA3, 0x80, 0x03, 0x3C, 0x53, 0xF8, 0x3A, +0xF0, 0x03, 0xE2, 0xA0, 0xE8, 0x29, 0x40, 0x34, +0x80, 0x11, 0xF0, 0x09, 0x3C, 0x4D, 0xFC, 0xC6, +0x84, 0x20, 0x97, 0x73, 0x40, 0x95, 0x00, 0x11, +0xE0, 0x03, 0xE9, 0x1B, 0x80, 0x26, 0x42, 0x10, +0x20, 0x73, 0x94, 0x49, 0x80, 0x45, 0x88, 0x24, +0xE1, 0x22, 0xE9, 0x0B, 0x05, 0xEF, 0x80, 0x01, +0x2A, 0xF0, 0x80, 0x01, 0x40, 0xF7, 0xF8, 0x07, +0xE8, 0x08, 0x8C, 0x41, 0x96, 0x93, 0xD5, 0xF5, +0x8C, 0x01, 0x40, 0x90, 0x00, 0x10, 0xD5, 0x08, +0x8E, 0x01, 0x96, 0x03, 0x84, 0x22, 0xD5, 0xE5, +0xC9, 0xF8, 0x20, 0x93, 0x80, 0x00, 0x3D, 0xE3, +0xF8, 0x3B, 0xF4, 0x03, 0xE2, 0x9E, 0xE8, 0x2C, +0x80, 0x64, 0x8C, 0x61, 0x96, 0x1B, 0x3C, 0x4D, +0xFC, 0xC6, 0x84, 0x20, 0x40, 0x3E, 0x00, 0x11, +0x97, 0x73, 0x41, 0xE5, 0x00, 0x11, 0xE0, 0x60, +0xE9, 0x1C, 0x80, 0x26, 0x42, 0x10, 0x20, 0x73, +0x94, 0x49, 0x80, 0x45, 0x88, 0x24, 0x40, 0xFF, +0x08, 0x07, 0xE9, 0x0B, 0x05, 0xCF, 0x80, 0x01, +0x2A, 0xF0, 0x80, 0x01, 0x40, 0xF7, 0xF0, 0x07, +0xE8, 0x08, 0x8C, 0x41, 0x96, 0x93, 0xD5, 0xF4, +0x8E, 0x01, 0x41, 0xC0, 0x00, 0x10, 0xD5, 0x08, +0x8C, 0x01, 0x96, 0x03, 0x84, 0x22, 0xD5, 0xE4, +0xC9, 0xF8, 0x21, 0xC3, 0x80, 0x01, 0x10, 0x9F, +0x80, 0x28, 0x11, 0xCF, 0x80, 0x29, 0x22, 0x23, +0x80, 0x00, 0x22, 0x5F, 0x80, 0x14, 0xD2, 0x10, +0x80, 0x07, 0xB0, 0x4A, 0x84, 0x46, 0x10, 0x6F, +0x80, 0x2A, 0x10, 0xAF, 0x80, 0x2B, 0x49, 0x00, +0x2A, 0x6D, 0xF4, 0x07, 0x5A, 0x40, 0x01, 0x04, +0x48, 0xFF, 0xFF, 0x25, 0xD5, 0x0A, 0x10, 0x6F, +0x80, 0x2A, 0x10, 0xAF, 0x80, 0x2B, 0x22, 0x03, +0x80, 0x01, 0x22, 0x5F, 0x80, 0x15, 0xD8, 0xE9, +0xFC, 0xC6, 0xFC, 0x48, 0xB1, 0x0E, 0xF5, 0x83, +0x3A, 0x02, 0x04, 0x20, 0x20, 0x0F, 0x80, 0x38, +0x20, 0x1F, 0x80, 0x39, 0x20, 0x5F, 0x80, 0x3B, +0x80, 0xE3, 0x20, 0x3F, 0x80, 0x3A, 0xF2, 0x87, +0xF0, 0x86, 0xF1, 0x84, 0x80, 0x02, 0x80, 0x24, +0x84, 0x46, 0xF3, 0x82, 0xF5, 0x81, 0x49, 0x00, +0x2A, 0x45, 0xF2, 0x03, 0x4E, 0x22, 0x01, 0x46, +0x3C, 0x63, 0xF8, 0x35, 0xF0, 0x03, 0x40, 0x43, +0x98, 0xF7, 0x96, 0x60, 0xF1, 0x88, 0xFE, 0x74, +0x96, 0xC9, 0xF3, 0x89, 0x8A, 0x66, 0x97, 0x59, +0xF5, 0x8A, 0x85, 0x1E, 0xF2, 0x0A, 0x90, 0x02, +0x42, 0x23, 0x20, 0x75, 0x97, 0xF8, 0xF0, 0x85, +0x96, 0x11, 0xF7, 0x8B, 0xF0, 0x8C, 0x44, 0x70, +0x00, 0x64, 0x03, 0xCF, 0x80, 0x0C, 0xF5, 0x0B, +0x42, 0x9E, 0x18, 0x24, 0x40, 0x84, 0x80, 0x13, +0x3C, 0x0D, 0xFC, 0xC6, 0x81, 0x25, 0x51, 0xE4, +0x7F, 0xFF, 0xF1, 0x02, 0xE0, 0x29, 0xE8, 0x34, +0x40, 0x34, 0xF8, 0x00, 0x84, 0x20, 0x96, 0x99, +0x81, 0x41, 0x40, 0x4E, 0x28, 0x00, 0x96, 0xE1, +0xF4, 0x04, 0xE0, 0x83, 0xE9, 0x1A, 0x9D, 0x11, +0x96, 0xE1, 0x8D, 0x41, 0x38, 0x40, 0x0D, 0x11, +0xC4, 0x04, 0x38, 0x30, 0x09, 0x11, 0xCB, 0x03, +0xF3, 0x05, 0xD5, 0x06, 0xE0, 0x83, 0xE8, 0x06, +0xFF, 0x3C, 0x40, 0x32, 0x0C, 0x76, 0x88, 0x23, +0xD5, 0x05, 0xFE, 0xFC, 0x40, 0x41, 0x90, 0x96, +0x88, 0x24, 0x88, 0x46, 0x96, 0x91, 0xD5, 0xE2, +0x4E, 0xA3, 0x00, 0x06, 0x40, 0x14, 0x80, 0x10, +0xF1, 0x82, 0xD5, 0x0A, 0xF2, 0x03, 0x42, 0xA5, +0x08, 0x24, 0xE1, 0x41, 0xE8, 0xF8, 0x8F, 0x21, +0x40, 0x94, 0x80, 0x13, 0xD5, 0xCB, 0x3D, 0xE3, +0xF8, 0x33, 0x8D, 0x01, 0xF3, 0x01, 0xE0, 0xA3, +0xE8, 0x35, 0x40, 0x42, 0xA0, 0x00, 0x84, 0x40, +0x96, 0x61, 0x81, 0x5C, 0x81, 0x22, 0xF3, 0x04, +0xE0, 0x6A, 0xE9, 0x1F, 0x9E, 0xC9, 0xE2, 0x3E, +0x97, 0x19, 0xE8, 0x15, 0x38, 0x40, 0x11, 0x11, +0x8D, 0x21, 0xC4, 0x04, 0x38, 0x30, 0x05, 0x11, +0xCB, 0x03, 0xF3, 0x05, 0xD5, 0x06, 0xE0, 0x83, +0xE8, 0x06, 0xFF, 0x3C, 0x40, 0x32, 0x0C, 0x76, +0x88, 0x43, 0xD5, 0x05, 0xFE, 0xFC, 0x40, 0x41, +0x90, 0x96, 0x88, 0x44, 0x8D, 0x41, 0x88, 0x26, +0x40, 0xA5, 0x00, 0x13, 0x96, 0x49, 0xD5, 0xE0, +0x4E, 0x93, 0x00, 0x05, 0x96, 0xAA, 0xF2, 0x81, +0xD5, 0x09, 0xF1, 0x03, 0x42, 0xA4, 0x84, 0x24, +0xE1, 0x42, 0xE8, 0xF9, 0x8C, 0xA1, 0x97, 0x69, +0xD5, 0xCA, 0xF5, 0x08, 0x85, 0x20, 0x02, 0x8F, +0x80, 0x04, 0xF4, 0x06, 0xE0, 0x85, 0xE8, 0x3D, +0xF3, 0x09, 0xF2, 0x0A, 0x88, 0x69, 0x96, 0x59, +0xF1, 0x8D, 0x88, 0x49, 0x84, 0x20, 0x80, 0x88, +0x41, 0xC1, 0x00, 0x13, 0x81, 0x41, 0xF3, 0x01, +0xE0, 0x64, 0xE9, 0x1E, 0xF3, 0x0D, 0x8D, 0x41, +0x88, 0x64, 0x96, 0x99, 0x38, 0x30, 0x09, 0x11, +0xC3, 0x07, 0x40, 0x22, 0x70, 0x00, 0x96, 0x91, +0x38, 0x20, 0x09, 0x11, 0xCA, 0x03, 0xF2, 0x05, +0xD5, 0x06, 0xE0, 0x62, 0xE8, 0x06, 0xFE, 0xFC, +0x40, 0x21, 0x88, 0x56, 0x88, 0x22, 0xD5, 0x05, +0xFE, 0xBC, 0x40, 0x31, 0x0C, 0x76, 0x88, 0x23, +0x8C, 0x81, 0x97, 0x21, 0xD5, 0xE1, 0x4E, 0xA3, +0x00, 0x05, 0x96, 0x6A, 0xF1, 0x86, 0xD5, 0x0D, +0xF4, 0x03, 0x41, 0xC4, 0x98, 0x01, 0x42, 0xA5, +0x10, 0x24, 0xE1, 0x41, 0x40, 0x9E, 0x00, 0x13, +0xE8, 0xF5, 0x8E, 0xA1, 0x97, 0x69, 0xD5, 0xC2, +0x04, 0x9F, 0x80, 0x0C, 0xF5, 0x08, 0x03, 0xCF, +0x80, 0x04, 0xF2, 0x04, 0xE0, 0xA2, 0xE8, 0x3A, +0x40, 0x84, 0x98, 0x01, 0x84, 0x20, 0x80, 0x9C, +0x40, 0x84, 0x00, 0x13, 0x81, 0x41, 0xF3, 0x01, +0xE0, 0x64, 0xE9, 0x20, 0x40, 0x22, 0x20, 0x00, +0x96, 0xD1, 0x40, 0x22, 0x24, 0x00, 0x96, 0x91, +0xE2, 0x5E, 0xE8, 0x15, 0x38, 0x30, 0x0D, 0x11, +0x8D, 0x41, 0xC3, 0x04, 0x38, 0x20, 0x09, 0x11, +0xCA, 0x03, 0xF2, 0x05, 0xD5, 0x06, 0xE0, 0x62, +0xE8, 0x06, 0xFE, 0xFC, 0x40, 0x21, 0x88, 0x56, +0x88, 0x22, 0xD5, 0x05, 0xFE, 0xBC, 0x40, 0x31, +0x0C, 0x76, 0x88, 0x23, 0x8C, 0x81, 0x97, 0x21, +0xD5, 0xDF, 0x4E, 0xA3, 0x00, 0x05, 0x96, 0x2A, +0xF0, 0x84, 0xD5, 0x0C, 0xF4, 0x03, 0x89, 0x26, +0x42, 0xA5, 0x10, 0x24, 0xE1, 0x41, 0x40, 0x94, +0x80, 0x13, 0xE8, 0xF6, 0x8C, 0xA1, 0x97, 0x69, +0xD5, 0xC5, 0xF1, 0x07, 0x01, 0xEF, 0x80, 0x18, +0x01, 0xCF, 0x80, 0x10, 0x11, 0xEF, 0x80, 0x38, +0x11, 0xCF, 0x80, 0x39, 0x22, 0x20, 0x80, 0x00, +0x22, 0x5F, 0x80, 0x1C, 0xDA, 0x0F, 0xF3, 0x07, +0x00, 0x8F, 0x80, 0x08, 0x00, 0x9F, 0x80, 0x04, +0x10, 0x8F, 0x80, 0x3A, 0x10, 0x9F, 0x80, 0x3B, +0x22, 0x41, 0x80, 0x01, 0x22, 0x5F, 0x80, 0x1D, +0xD4, 0x10, 0x00, 0xAF, 0x80, 0x08, 0x00, 0xFF, +0x80, 0x04, 0xF0, 0x07, 0xB0, 0x4E, 0x84, 0x46, +0x10, 0xAF, 0x80, 0x3A, 0x10, 0xFF, 0x80, 0x3B, +0x49, 0x00, 0x29, 0x00, 0x48, 0xFF, 0xFE, 0xD7, +0xFC, 0xC8, 0xB4, 0x61, 0xA0, 0x89, 0xA1, 0x0A, +0xA1, 0x4E, 0x88, 0x43, 0xA0, 0xCF, 0x88, 0x44, +0x88, 0xA3, 0xA1, 0x0B, 0xA0, 0xCC, 0x04, 0xF0, +0x80, 0x08, 0x88, 0x64, 0xA1, 0x0D, 0x88, 0xAF, +0x88, 0x64, 0x88, 0x62, 0x88, 0x65, 0xB6, 0x60, +0x9A, 0xEA, 0x88, 0x45, 0xA8, 0xC1, 0xA8, 0x83, +0xB4, 0xA1, 0xA0, 0x8B, 0xA0, 0xCD, 0x88, 0x45, +0xA1, 0x4A, 0xA1, 0x0E, 0x04, 0x10, 0x80, 0x08, +0x88, 0xA3, 0x88, 0x44, 0x99, 0x29, 0x9B, 0x62, +0x88, 0x44, 0xA9, 0x42, 0xA8, 0x84, 0xDD, 0x9E, +0xFC, 0x22, 0x97, 0x4B, 0x40, 0x40, 0x40, 0x0A, +0x90, 0x30, 0x96, 0x03, 0xFF, 0x1C, 0x44, 0x70, +0x00, 0x64, 0x44, 0x6F, 0xFF, 0x9C, 0xFE, 0x14, +0xFE, 0xCC, 0x42, 0x22, 0x88, 0x24, 0x40, 0x82, +0x1D, 0x16, 0x40, 0xF0, 0x1D, 0xF6, 0x40, 0x31, +0x98, 0x76, 0x40, 0x41, 0x18, 0x96, 0x88, 0x68, +0x88, 0x8F, 0x88, 0x85, 0x88, 0x61, 0x96, 0x21, +0x40, 0x11, 0xC0, 0x08, 0xFE, 0x0F, 0xFC, 0xA2, +0xFC, 0x57, 0xB1, 0x4C, 0xF4, 0x88, 0x3A, 0x02, +0x84, 0x20, 0x20, 0x0F, 0x80, 0x31, 0x20, 0x1F, +0x80, 0x33, 0x84, 0x80, 0xF2, 0x83, 0xF3, 0x84, +0x20, 0x2F, 0x80, 0x30, 0xF0, 0x81, 0x20, 0xAF, +0x80, 0x32, 0xF1, 0x82, 0xB1, 0x9A, 0x80, 0x64, +0x38, 0x33, 0x12, 0x0A, 0x8C, 0x81, 0x5A, 0x48, +0x14, 0xFD, 0x84, 0xE0, 0xB1, 0x4E, 0x81, 0x07, +0x38, 0x82, 0x9E, 0x0A, 0x8C, 0xE1, 0x5A, 0x78, +0x0C, 0xFD, 0x3E, 0x5F, 0xF6, 0x04, 0x2E, 0x7F, +0xFA, 0x1F, 0x44, 0x10, 0x00, 0x38, 0x3C, 0x4D, +0xFC, 0xC6, 0x81, 0x25, 0x42, 0x93, 0x84, 0x73, +0x3C, 0x03, 0xF8, 0x35, 0x44, 0xFF, 0x80, 0x00, +0x45, 0xC0, 0x7F, 0xFF, 0x96, 0xD1, 0xF4, 0x87, +0x40, 0x45, 0x00, 0x13, 0x14, 0xF4, 0x80, 0x05, +0x15, 0xC4, 0x80, 0x08, 0xF0, 0x85, 0x84, 0xC0, +0x80, 0x03, 0xF4, 0x89, 0xF5, 0x8A, 0xF1, 0x8B, +0x50, 0x94, 0x80, 0x20, 0xF5, 0x01, 0xE0, 0xA0, +0xE9, 0x31, 0xF4, 0x05, 0xF5, 0x0B, 0xFF, 0x04, +0x05, 0xCF, 0x80, 0x0A, 0xF4, 0x86, 0xF1, 0x09, +0x43, 0xC3, 0x94, 0x73, 0xF4, 0x02, 0xE0, 0x81, +0xE9, 0x22, 0xF5, 0x06, 0xF4, 0x07, 0x88, 0xA1, +0x51, 0xEE, 0x00, 0x10, 0x38, 0x52, 0x15, 0x11, +0xB1, 0x1A, 0x88, 0xC5, 0x38, 0x82, 0x02, 0x02, +0x89, 0x05, 0x38, 0x82, 0x02, 0x0A, 0xB1, 0x0E, +0x38, 0x82, 0x06, 0x02, 0x89, 0x05, 0x38, 0x82, +0x06, 0x0A, 0x04, 0x4F, 0x00, 0x01, 0xE0, 0x85, +0xE8, 0x03, 0x14, 0x5F, 0x00, 0x01, 0xB5, 0x09, +0xE0, 0xA8, 0xE8, 0x02, 0xB6, 0xA9, 0x8C, 0x21, +0x96, 0x49, 0xD5, 0xDD, 0x8C, 0x01, 0x96, 0x01, +0xD5, 0xCE, 0x3C, 0x57, 0xFD, 0x0A, 0xF1, 0x08, +0xD9, 0x4C, 0x45, 0xE0, 0x00, 0x38, 0x3E, 0x0F, +0xF6, 0x04, 0x42, 0x03, 0xF8, 0x73, 0xA1, 0x05, +0xFA, 0xAE, 0xFF, 0x64, 0x44, 0x80, 0x00, 0x64, +0x40, 0x12, 0xA0, 0x36, 0xF4, 0x01, 0xF5, 0x02, +0x40, 0x82, 0x08, 0x01, 0x40, 0x92, 0xA8, 0x01, +0x96, 0x0B, 0x50, 0x24, 0x00, 0x01, 0x50, 0x14, +0x80, 0x01, 0xFE, 0x8C, 0x97, 0x13, 0xF0, 0x85, +0x3D, 0xC3, 0xF8, 0x35, 0x3D, 0xED, 0xFC, 0xC6, +0x84, 0xA0, 0x40, 0x25, 0x00, 0x13, 0xF0, 0x01, +0xE0, 0x03, 0xE9, 0x16, 0x42, 0xA1, 0xF0, 0x24, +0x80, 0x22, 0xF0, 0x02, 0xE0, 0x01, 0xE9, 0x0D, +0x40, 0x05, 0x04, 0x00, 0x38, 0xFF, 0x01, 0x11, +0xF0, 0x05, 0xE0, 0x0F, 0xE8, 0x03, 0x8C, 0xA1, +0x97, 0x6B, 0x8C, 0x21, 0x96, 0x49, 0xD5, 0xF2, +0x8C, 0x61, 0x96, 0xD9, 0xD5, 0xE9, 0x44, 0x30, +0x00, 0x64, 0xFF, 0x5C, 0x40, 0x42, 0x90, 0x96, +0x96, 0xA3, 0x50, 0xA4, 0x00, 0x01, 0x8D, 0x21, +0x3C, 0x2B, 0xFA, 0xF4, 0x3C, 0x2B, 0xFE, 0xA1, +0x3C, 0xAB, 0xFA, 0xEC, 0x3C, 0x9B, 0xFA, 0xED, +0x44, 0x90, 0x00, 0x38, 0x3E, 0x5F, 0xF6, 0x0C, +0xB0, 0xCC, 0x42, 0x13, 0xA4, 0x24, 0x98, 0x29, +0x3B, 0xF1, 0xFE, 0x00, 0x8C, 0x04, 0x51, 0xE2, +0xFF, 0xF8, 0x3B, 0xF0, 0x7E, 0x20, 0x81, 0x5E, +0x02, 0x4F, 0x80, 0x1A, 0x44, 0x80, 0x7F, 0xFF, +0x42, 0xA3, 0xA4, 0x73, 0x88, 0x3E, 0x80, 0x46, +0xE1, 0x06, 0x50, 0x35, 0x00, 0x20, 0xAD, 0x02, +0x40, 0x24, 0x3C, 0x1B, 0x84, 0x00, 0xAC, 0x8C, +0xA8, 0x19, 0x80, 0x20, 0xB1, 0x5A, 0x38, 0x42, +0x82, 0x02, 0x04, 0x91, 0x80, 0x01, 0x8C, 0x01, +0xE3, 0x24, 0x42, 0x10, 0x10, 0x73, 0xE8, 0x02, +0xA9, 0x19, 0x5A, 0x08, 0x14, 0xF6, 0x45, 0xC0, +0x00, 0x38, 0x80, 0x5E, 0x42, 0x23, 0xF0, 0x73, +0x85, 0x00, 0x14, 0x81, 0x00, 0x0A, 0x80, 0x08, +0xB1, 0xCE, 0x50, 0xA1, 0x00, 0x28, 0x38, 0x33, +0xA2, 0x02, 0xB4, 0xAA, 0x8D, 0x01, 0xE2, 0xA3, +0x42, 0x04, 0x0C, 0x73, 0xE8, 0x02, 0xB6, 0x6A, +0x5A, 0x88, 0x0C, 0xF7, 0x44, 0x40, 0x00, 0x64, +0xFE, 0x64, 0x40, 0x10, 0x98, 0x37, 0x50, 0x90, +0x80, 0x32, 0x40, 0x24, 0x90, 0x56, 0xF3, 0x04, +0x8E, 0x41, 0x97, 0xD1, 0x50, 0x50, 0xFF, 0x9C, +0xAD, 0xD8, 0x94, 0x6F, 0xF3, 0x03, 0x41, 0xC0, +0x93, 0x96, 0xFE, 0x24, 0x40, 0x60, 0x18, 0xD7, +0x50, 0x2E, 0x00, 0x40, 0xAC, 0x98, 0x50, 0x83, +0x00, 0x32, 0x50, 0x23, 0x7F, 0x9C, 0x40, 0x04, +0x10, 0x16, 0xF1, 0x04, 0x95, 0x97, 0x8E, 0x01, +0x40, 0xA3, 0x11, 0x56, 0x97, 0x41, 0x50, 0x45, +0x00, 0x40, 0xAD, 0x49, 0xAD, 0x19, 0x2E, 0x9F, +0xFA, 0x1F, 0x45, 0xC0, 0x00, 0x38, 0x3C, 0x33, +0xF8, 0x35, 0x43, 0xE4, 0xF0, 0x73, 0x42, 0x53, +0x8C, 0x73, 0x12, 0x5F, 0x00, 0x05, 0xFC, 0xD7, +0xFC, 0x41, 0x84, 0x60, 0xF1, 0x81, 0x97, 0xC9, +0x50, 0x20, 0x00, 0x32, 0x92, 0x30, 0x80, 0xA0, +0x1A, 0x32, 0x80, 0x01, 0xDA, 0xFE, 0x3C, 0x9D, +0xFC, 0xC5, 0x84, 0x9E, 0x3C, 0x83, 0xF8, 0x3A, +0x99, 0xA7, 0xE0, 0xC8, 0xE8, 0x06, 0x8C, 0x81, +0x8C, 0x0A, 0x5A, 0x48, 0x03, 0xF9, 0xFC, 0xC1, +0x3C, 0xA3, 0xF8, 0x3B, 0xE1, 0x46, 0xE9, 0xF8, +0x80, 0xA0, 0x84, 0x7E, 0x3D, 0xC3, 0xF8, 0x3C, +0x98, 0x99, 0xE0, 0x5C, 0xE9, 0x10, 0x3C, 0x83, +0xF8, 0x3D, 0xE1, 0x02, 0xE9, 0x0C, 0x3D, 0xE3, +0xF8, 0x35, 0x81, 0x41, 0x42, 0xA3, 0x78, 0x73, +0x80, 0x4A, 0x88, 0x43, 0x94, 0x91, 0x88, 0x49, +0xA4, 0x90, 0xAC, 0xA8, 0x8C, 0x61, 0x8C, 0xA2, +0x5A, 0x38, 0x03, 0xEA, 0xD5, 0xDD, 0x50, 0x20, +0x00, 0x0A, 0x80, 0xA0, 0x84, 0x20, 0x1A, 0x12, +0x80, 0x01, 0x8C, 0x0A, 0x12, 0x12, 0x80, 0x13, +0x12, 0x10, 0x7F, 0xFB, 0x12, 0x10, 0x7F, 0xFF, +0xDA, 0xF7, 0xDD, 0x9E, 0xFC, 0x40, 0x50, 0x20, +0x00, 0x0A, 0x81, 0x41, 0x50, 0x50, 0x00, 0x28, +0x50, 0x81, 0x00, 0x0A, 0x50, 0x71, 0x7F, 0xF6, +0x80, 0xC8, 0x85, 0x20, 0x3C, 0x03, 0xFE, 0x01, +0x3C, 0x33, 0xFE, 0x02, 0x3C, 0x43, 0xFE, 0x03, +0x41, 0xE1, 0x80, 0x11, 0x40, 0xF2, 0x00, 0x11, +0x22, 0x33, 0x00, 0x01, 0x22, 0x43, 0x80, 0x01, +0x23, 0xC1, 0x00, 0x01, 0x88, 0x64, 0x22, 0x41, +0x00, 0x00, 0x96, 0x03, 0x88, 0x64, 0x22, 0x41, +0x00, 0x02, 0x8C, 0xE2, 0x88, 0x64, 0x43, 0xEF, +0x0C, 0x24, 0x43, 0xE0, 0x70, 0x73, 0x23, 0xC3, +0xFF, 0xFF, 0x22, 0x03, 0x80, 0x01, 0x22, 0x33, +0x00, 0x00, 0x22, 0x43, 0x00, 0x02, 0x88, 0x1C, +0x88, 0x03, 0x88, 0x04, 0x43, 0xE7, 0x80, 0x73, +0x39, 0xE5, 0x25, 0x0A, 0x8D, 0x22, 0x8C, 0x42, +0x8C, 0xC2, 0x5A, 0x98, 0x06, 0xD1, 0x80, 0x48, +0x8D, 0x4C, 0xDA, 0xC7, 0x84, 0xE1, 0x85, 0x41, +0x84, 0xA0, 0x3C, 0x7B, 0xFD, 0x17, 0x80, 0x0A, +0x80, 0x45, 0x85, 0x2C, 0x81, 0x01, 0x42, 0x81, +0x24, 0x73, 0x84, 0x60, 0x97, 0xD3, 0x38, 0x64, +0x0E, 0x02, 0xE0, 0xA6, 0xE8, 0x04, 0x81, 0x47, +0x96, 0x1B, 0x80, 0xA6, 0x8C, 0x61, 0x5A, 0x38, +0x03, 0xF8, 0x8C, 0x41, 0x5A, 0x28, 0x03, 0xF0, +0x8F, 0x41, 0x8E, 0x01, 0x3C, 0xAB, 0xFD, 0x17, +0x3C, 0x0B, 0xFD, 0x18, 0xFC, 0xC0, 0xFC, 0x20, +0xB4, 0xE0, 0xC1, 0x1D, 0xC7, 0x1C, 0x80, 0xC0, +0x4E, 0x77, 0x00, 0x07, 0x4E, 0x16, 0x00, 0x10, +0xFE, 0x4A, 0xFE, 0x7C, 0xD5, 0x05, 0x4E, 0x17, +0x00, 0x0B, 0xFE, 0x3A, 0xFE, 0x44, 0x84, 0x4A, +0x40, 0x00, 0x88, 0x16, 0x49, 0xFF, 0xE4, 0x2B, +0x9A, 0x38, 0xD5, 0x08, 0xFE, 0x7C, 0x84, 0x6A, +0x40, 0x00, 0x8C, 0x16, 0x49, 0xFF, 0xE4, 0x23, +0x88, 0x07, 0xB6, 0x06, 0xFC, 0xA0, 0xFC, 0x51, +0xB1, 0x82, 0x81, 0x43, 0x3A, 0x03, 0x04, 0x20, +0x40, 0x91, 0x00, 0x13, 0x40, 0x71, 0x40, 0x09, +0x12, 0x9F, 0x80, 0x02, 0x12, 0x7F, 0x80, 0x03, +0xF0, 0x01, 0x49, 0xFF, 0xF9, 0x40, 0x81, 0x00, +0xF1, 0x01, 0xB0, 0x15, 0x49, 0xFF, 0xFF, 0x26, +0x4E, 0x83, 0x00, 0x09, 0x3C, 0x00, 0x07, 0x96, +0x96, 0x1E, 0xC8, 0x04, 0x2E, 0x17, 0xEA, 0x72, +0xC1, 0x04, 0xB0, 0x15, 0x49, 0xFF, 0xFF, 0x51, +0xB0, 0x15, 0xB0, 0x4C, 0x49, 0xFF, 0xFF, 0x5C, +0x3C, 0x27, 0xFD, 0x17, 0xCA, 0x04, 0x3C, 0x37, +0xFD, 0x18, 0xC3, 0x22, 0x97, 0x11, 0x40, 0x54, +0x90, 0x00, 0x3D, 0xC3, 0xFD, 0x18, 0x20, 0xF3, +0x00, 0x00, 0x40, 0x92, 0x80, 0x13, 0x88, 0xFC, +0xE1, 0x2F, 0x97, 0xF9, 0xE8, 0x08, 0x40, 0x64, +0x90, 0x01, 0x8A, 0xFC, 0x40, 0x93, 0x00, 0x13, +0x97, 0xF9, 0xD5, 0x0E, 0x21, 0xE3, 0x00, 0x02, +0xE0, 0xFE, 0xE9, 0xF6, 0x20, 0x83, 0x00, 0x01, +0xE1, 0x09, 0xE9, 0xF2, 0x20, 0x03, 0x00, 0x03, +0xE0, 0x07, 0xE8, 0xBF, 0xD5, 0xED, 0xB0, 0x07, +0xB0, 0x4C, 0x49, 0xFF, 0xFD, 0x64, 0xF1, 0x07, +0x84, 0x9E, 0xFF, 0x0C, 0xF2, 0x0B, 0x84, 0xA3, +0x3C, 0x03, 0xFD, 0xFC, 0x80, 0x64, 0x42, 0x32, +0x88, 0x73, 0xF1, 0x08, 0xF2, 0x0A, 0x97, 0x83, +0x3C, 0x03, 0xFD, 0xFD, 0xFF, 0x8C, 0x42, 0x42, +0x88, 0x73, 0xF1, 0x09, 0x40, 0x43, 0x10, 0x96, +0x3C, 0x23, 0xFD, 0xFC, 0x97, 0x83, 0xFF, 0x8C, +0xFF, 0x62, 0x40, 0x33, 0x0C, 0x76, 0x96, 0x13, +0xFF, 0x1A, 0xE0, 0x05, 0xF5, 0x85, 0xF4, 0x86, +0x87, 0x81, 0xE9, 0x07, 0x3C, 0x63, 0xFD, 0xFC, +0x96, 0x73, 0xFE, 0xCA, 0x41, 0xC2, 0x8C, 0x07, +0x3C, 0x53, 0xFD, 0xFD, 0x96, 0xAB, 0xE0, 0x44, +0xE9, 0x07, 0x3C, 0x03, 0xFD, 0xFC, 0x96, 0x43, +0xFE, 0xCA, 0xE0, 0x83, 0xE8, 0x03, 0x59, 0xCE, +0x00, 0x02, 0x3C, 0x43, 0xFE, 0x7E, 0xB0, 0x06, +0x96, 0x63, 0x49, 0xFF, 0xFF, 0x56, 0x3C, 0x63, +0xFE, 0x7F, 0xB0, 0x05, 0x96, 0x73, 0x49, 0xFF, +0xFF, 0x50, 0xF5, 0x05, 0x5E, 0xF2, 0x80, 0x41, +0xE9, 0x04, 0x44, 0x20, 0x00, 0x40, 0xF2, 0x85, +0xF0, 0x05, 0x5E, 0xF0, 0x7F, 0xC0, 0xE8, 0x04, +0x44, 0x1F, 0xFF, 0xC0, 0xF1, 0x85, 0xF3, 0x06, +0x5E, 0xF1, 0x80, 0x41, 0xE9, 0x04, 0x44, 0x40, +0x00, 0x40, 0xF4, 0x86, 0xF6, 0x06, 0x5E, 0xF3, +0x7F, 0xC0, 0xE8, 0x04, 0x44, 0x5F, 0xFF, 0xC0, +0xF5, 0x86, 0xF0, 0x06, 0xF2, 0x05, 0x41, 0xE4, +0x9C, 0x08, 0x50, 0x81, 0x00, 0x40, 0x50, 0x30, +0x00, 0x40, 0x94, 0x7F, 0x89, 0x1E, 0x88, 0x61, +0x12, 0x85, 0x00, 0x00, 0x12, 0x35, 0x00, 0x01, +0x80, 0x1C, 0x3C, 0x9B, 0xFD, 0x15, 0x3C, 0x7B, +0xFD, 0x16, 0xFC, 0xD1, 0x84, 0x00, 0xDD, 0x9E, +0xFC, 0x43, 0x50, 0x9F, 0x80, 0x10, 0x84, 0xFF, +0x3A, 0x24, 0x8C, 0x20, 0x81, 0x00, 0xF1, 0x81, +0x80, 0x60, 0x81, 0x47, 0x83, 0x87, 0x84, 0xC0, +0xF0, 0x01, 0xE0, 0xC0, 0xE8, 0x1E, 0x2A, 0x01, +0x80, 0x01, 0x4E, 0x05, 0x00, 0x19, 0x3A, 0x14, +0x88, 0x00, 0xF4, 0x83, 0xF3, 0x82, 0x49, 0xFF, +0xE3, 0x23, 0xF3, 0x02, 0xF4, 0x03, 0x5A, 0x08, +0x01, 0x0F, 0x22, 0x21, 0xFF, 0xFF, 0x3C, 0x5D, +0xFC, 0xC6, 0x38, 0x02, 0x89, 0x11, 0xE0, 0xE0, +0xE8, 0x06, 0xE0, 0x04, 0xE9, 0x04, 0x83, 0x82, +0x80, 0xE0, 0x81, 0x46, 0x8C, 0xC1, 0xD5, 0xE1, +0x5A, 0xA7, 0xFF, 0x05, 0x84, 0x3F, 0x38, 0x14, +0x29, 0x09, 0x80, 0x1C, 0xFC, 0xC3, 0xFC, 0x41, +0x84, 0xE1, 0x81, 0x01, 0x80, 0xDF, 0x3A, 0x2F, +0x8C, 0x20, 0x83, 0x84, 0x81, 0x40, 0x85, 0x20, +0xE1, 0x28, 0xE8, 0x16, 0x2A, 0x05, 0x00, 0x01, +0x5A, 0x07, 0xFF, 0x11, 0x3A, 0x13, 0x08, 0x00, +0x49, 0xFF, 0xE2, 0xF6, 0x5A, 0x08, 0x01, 0x0B, +0x22, 0x15, 0x7F, 0xFF, 0x3C, 0x0D, 0xFC, 0xC6, +0x38, 0x20, 0x05, 0x11, 0xE0, 0x5C, 0xE9, 0x02, +0x8C, 0xE1, 0x8D, 0x21, 0xD5, 0xEA, 0x80, 0x07, +0xFC, 0xC1, 0xFC, 0x41, 0x3A, 0x0F, 0x84, 0x20, +0x20, 0x3F, 0x80, 0x02, 0x20, 0x0F, 0x80, 0x00, +0x20, 0x4F, 0x80, 0x01, 0x96, 0x01, 0x20, 0x5F, +0x80, 0x03, 0x3C, 0x6D, 0xFC, 0xC6, 0x97, 0xD9, +0x85, 0x20, 0xE0, 0x80, 0xE9, 0x14, 0x3C, 0x13, +0xF8, 0x35, 0x81, 0x03, 0x42, 0x80, 0x04, 0x73, +0x80, 0x28, 0x94, 0x49, 0x80, 0x47, 0x88, 0x26, +0xE0, 0xA2, 0xE9, 0x06, 0x8C, 0x41, 0x1A, 0x90, +0x80, 0x01, 0x96, 0x91, 0xD5, 0xFA, 0x8C, 0x01, +0x96, 0x01, 0xD5, 0xEC, 0xFC, 0xC1, 0xFC, 0x21, +0x3C, 0x43, 0xF8, 0x38, 0x22, 0xF0, 0x00, 0x00, +0x80, 0xC0, 0xE0, 0x8F, 0xE8, 0x02, 0xAD, 0x00, +0x3C, 0x53, 0xF8, 0x39, 0x22, 0x03, 0x00, 0x01, +0xE0, 0xA0, 0xE8, 0x02, 0xAD, 0x71, 0x3C, 0x23, +0xFE, 0x18, 0x22, 0x13, 0x00, 0x00, 0x96, 0xD3, +0xE0, 0x23, 0xE8, 0x04, 0x3C, 0x33, 0xFE, 0x18, +0xD5, 0x0A, 0x3C, 0x73, 0xFE, 0x19, 0x96, 0x3B, +0x9A, 0xA0, 0xE0, 0x41, 0xE8, 0x05, 0x3C, 0x13, +0xFE, 0x19, 0x9A, 0xE1, 0xAC, 0xF0, 0x3C, 0x73, +0xFE, 0x1A, 0x22, 0x83, 0x00, 0x01, 0x96, 0x3B, +0xE1, 0x00, 0xE8, 0x04, 0x3C, 0x03, 0xFE, 0x1A, +0xD5, 0x0A, 0x3C, 0x23, 0xFE, 0x1B, 0x96, 0x53, +0x9A, 0xE9, 0xE0, 0x68, 0xE8, 0x05, 0x3C, 0x73, +0xFE, 0x1B, 0x9A, 0x2F, 0xAC, 0x31, 0x22, 0x83, +0x00, 0x00, 0x5E, 0xF4, 0x00, 0x80, 0xE8, 0x14, +0x3C, 0x73, 0xFE, 0x17, 0x3C, 0x23, 0xFE, 0x18, +0x96, 0x3B, 0x52, 0x14, 0x00, 0x80, 0x97, 0xD3, +0x52, 0x30, 0x00, 0x80, 0x96, 0x89, 0xFE, 0x9C, +0x53, 0xC3, 0x80, 0x80, 0x40, 0xF1, 0x71, 0xF7, +0x52, 0x37, 0x80, 0x80, 0xD5, 0x19, 0x51, 0xC2, +0x7F, 0x81, 0xE1, 0x1C, 0xE9, 0x16, 0x3C, 0x23, +0xFE, 0x17, 0x8B, 0x04, 0x3C, 0x33, 0xFE, 0x19, +0x96, 0x53, 0x50, 0x04, 0x00, 0x80, 0x97, 0xDB, +0x96, 0x81, 0x52, 0x10, 0x80, 0x80, 0xFE, 0x54, +0x53, 0xE3, 0x80, 0x80, 0x40, 0x30, 0xF8, 0x77, +0x50, 0x82, 0x7F, 0x80, 0x88, 0x68, 0xAC, 0xF0, +0x22, 0x03, 0x00, 0x01, 0x5E, 0xF0, 0x00, 0x80, +0xE8, 0x15, 0x3C, 0x23, 0xFE, 0x17, 0x3C, 0x33, +0xFE, 0x1A, 0x96, 0x53, 0x52, 0x00, 0x00, 0x80, +0x97, 0xDB, 0x52, 0x20, 0x80, 0x80, 0x96, 0x41, +0xFE, 0x54, 0x53, 0xE3, 0x80, 0x80, 0x40, 0xF0, +0xF9, 0xF7, 0x52, 0x37, 0x80, 0x80, 0xAC, 0xF1, +0xD5, 0x1A, 0x51, 0xE2, 0xFF, 0x81, 0xE0, 0x1E, +0xE9, 0x16, 0x3C, 0x73, 0xFE, 0x17, 0x9A, 0x85, +0x3C, 0x13, 0xFE, 0x1B, 0x96, 0xFB, 0x50, 0x01, +0x00, 0x80, 0x97, 0xCB, 0x52, 0x31, 0x80, 0x80, +0x96, 0x41, 0xFE, 0xCC, 0x52, 0x83, 0x80, 0x80, +0x40, 0x71, 0xA0, 0xF7, 0x51, 0xC2, 0xFF, 0x80, +0x88, 0xFC, 0xAD, 0xF1, 0x22, 0x73, 0x00, 0x00, +0x40, 0x22, 0x04, 0x09, 0x50, 0x81, 0x7F, 0xE0, +0xE0, 0xE8, 0xE8, 0x09, 0x3C, 0x33, 0xFD, 0xF6, +0xC3, 0x15, 0x3C, 0x03, 0xFD, 0xF6, 0x96, 0x43, +0x88, 0x22, 0xD5, 0x0C, 0x50, 0x01, 0x00, 0x20, +0xE0, 0x07, 0xE8, 0x0C, 0x3C, 0x13, 0xFD, 0xF7, +0xC1, 0x09, 0x3C, 0x33, 0xFD, 0xF7, 0x96, 0x1B, +0x9A, 0x50, 0xFF, 0xCC, 0x40, 0x73, 0x88, 0xF6, +0xAD, 0xF0, 0x22, 0x33, 0x00, 0x01, 0x40, 0x22, +0x84, 0x09, 0x51, 0xC1, 0x7F, 0xE0, 0xE0, 0x7C, +0xE8, 0x09, 0x3C, 0x03, 0xFD, 0xF8, 0xC0, 0x15, +0x3C, 0x73, 0xFD, 0xF8, 0x96, 0x7B, 0x88, 0x22, +0xD5, 0x0C, 0x50, 0x81, 0x00, 0x20, 0xE1, 0x03, +0xE8, 0x0C, 0x3C, 0x13, 0xFD, 0xF9, 0xC1, 0x09, +0x3C, 0x03, 0xFD, 0xF9, 0x97, 0xC3, 0x9A, 0x57, +0xFE, 0xCC, 0x40, 0x31, 0x88, 0x76, 0xAC, 0xF1, +0x22, 0x23, 0x00, 0x00, 0x22, 0x13, 0x00, 0x01, +0xAC, 0xB1, 0xAC, 0x70, 0x3C, 0x03, 0xFD, 0xEC, +0x87, 0x82, 0x97, 0xC3, 0xFE, 0x7C, 0x40, 0x50, +0x94, 0xB7, 0x96, 0xEB, 0xAC, 0xF0, 0x3C, 0x13, +0xFD, 0xED, 0x96, 0x0B, 0xFE, 0x84, 0x40, 0x41, +0x10, 0x97, 0xAD, 0x31, 0x3C, 0x23, 0xFD, 0xEC, +0x3C, 0x73, 0xFE, 0x80, 0x97, 0x53, 0x41, 0xE2, +0xF3, 0xD6, 0x8A, 0x7E, 0xB0, 0x01, 0x96, 0x7B, +0xF3, 0x81, 0x49, 0xFF, 0xFD, 0xA6, 0x3C, 0x33, +0xFD, 0xEC, 0xF4, 0x01, 0x96, 0x5B, 0x40, 0x00, +0xF0, 0x16, 0x88, 0x04, 0x4E, 0x04, 0x00, 0x03, +0x84, 0x00, 0xF0, 0x81, 0x3C, 0x23, 0xFD, 0xEC, +0xF7, 0x01, 0x97, 0x53, 0xE0, 0xA7, 0xE8, 0x05, +0x3C, 0x33, 0xFD, 0xEC, 0x96, 0x5B, 0xF1, 0x81, +0xF4, 0x01, 0x22, 0x23, 0x00, 0x01, 0xAD, 0x30, +0x3C, 0x03, 0xFD, 0xED, 0x85, 0x02, 0x97, 0x43, +0x3C, 0x13, 0xFE, 0x81, 0x40, 0x72, 0xA0, 0xF6, +0x9A, 0xD7, 0xB0, 0x01, 0x96, 0x4B, 0xF3, 0x81, +0x49, 0xFF, 0xFD, 0x7F, 0x3C, 0x43, 0xFD, 0xED, +0xF5, 0x01, 0x96, 0x23, 0x40, 0x20, 0x20, 0x56, +0x88, 0x45, 0x4E, 0x24, 0x00, 0x03, 0x84, 0x40, +0xF2, 0x81, 0x3C, 0x73, 0xFD, 0xED, 0xF1, 0x01, +0x96, 0xFB, 0xE0, 0x61, 0xE8, 0x05, 0x3C, 0x43, +0xFD, 0xED, 0x96, 0x23, 0xF0, 0x81, 0xF5, 0x01, +0xAD, 0x71, 0x3C, 0x23, 0xFD, 0xEE, 0x96, 0x86, +0xC2, 0x06, 0x3C, 0x73, 0xFD, 0xEC, 0xA4, 0xF0, +0x8A, 0xE3, 0xAD, 0xF0, 0x3C, 0x13, 0xFD, 0xEE, +0x96, 0x4E, 0xC1, 0x05, 0x3C, 0x43, 0xFD, 0xED, +0x8A, 0x85, 0xAD, 0x31, 0x3C, 0x03, 0xFD, 0xEE, +0x96, 0x16, 0xC0, 0x06, 0x22, 0x53, 0x00, 0x00, +0xA4, 0xB1, 0xAD, 0x71, 0xAC, 0xB0, 0xFC, 0xA1, +0xFC, 0x01, 0xF0, 0x81, 0x49, 0xFF, 0xFE, 0xA9, +0x2E, 0x07, 0xF0, 0x65, 0x5A, 0x08, 0x01, 0x05, +0xF0, 0x01, 0x49, 0xFF, 0xEA, 0x4E, 0x2E, 0x2F, +0xFA, 0x1F, 0x44, 0x10, 0x00, 0x38, 0x3E, 0x0F, +0xF6, 0x04, 0x42, 0x01, 0x04, 0x73, 0xF1, 0x01, +0x84, 0x44, 0x49, 0x00, 0x24, 0x03, 0xFC, 0x81, +0xFC, 0x47, 0x3E, 0x0F, 0xF9, 0x74, 0x3C, 0x10, +0x07, 0x9B, 0x49, 0xFF, 0xF7, 0x38, 0x3E, 0x07, +0xFA, 0x19, 0x4E, 0x02, 0x02, 0x4F, 0x3C, 0x60, +0x07, 0x98, 0x97, 0x86, 0xC6, 0x09, 0x2E, 0x0F, +0xFA, 0x22, 0xE4, 0x02, 0xE8, 0x14, 0x84, 0x40, +0x3E, 0x20, 0x0F, 0x5B, 0xD5, 0x10, 0x3C, 0x9D, +0xFC, 0xC6, 0x3D, 0xED, 0xFC, 0xC5, 0x3C, 0x13, +0xF8, 0x33, 0xE0, 0xC1, 0xE8, 0xF1, 0x94, 0xF1, +0x88, 0x7E, 0xA4, 0x98, 0x38, 0x24, 0x99, 0x09, +0x8C, 0xC1, 0xD5, 0xF6, 0x3E, 0x9F, 0xF6, 0x04, +0x50, 0xAF, 0x80, 0x28, 0x3C, 0x33, 0xF8, 0x3A, +0x3C, 0x43, 0xF8, 0x3B, 0x3C, 0x53, 0xF8, 0x3C, +0x3C, 0x63, 0xF8, 0x3D, 0x10, 0x45, 0x00, 0x01, +0x10, 0x3F, 0x80, 0x28, 0x10, 0x55, 0x00, 0x02, +0x10, 0x65, 0x00, 0x03, 0x3E, 0x0F, 0xF9, 0x74, +0x2E, 0x17, 0xFA, 0x19, 0x3A, 0x25, 0x0C, 0x00, +0x3C, 0x40, 0x07, 0x9B, 0x49, 0xFF, 0xFD, 0xD6, +0x80, 0xE0, 0x5A, 0x0F, 0xFF, 0x04, 0x48, 0x00, +0x02, 0x11, 0x3C, 0x83, 0xF8, 0x35, 0x41, 0xC0, +0x23, 0xD6, 0x13, 0xCF, 0x80, 0x0E, 0x13, 0xEF, +0x80, 0x0F, 0xF0, 0x07, 0x49, 0xFF, 0xF6, 0x4F, +0xC0, 0x27, 0xE6, 0x03, 0xE8, 0x08, 0x3C, 0x6D, +0xFC, 0xC6, 0x3C, 0x30, 0x07, 0x9C, 0x38, 0x23, +0x1D, 0x11, 0xD5, 0x09, 0xE6, 0x05, 0xE8, 0x0A, +0x3C, 0x5D, 0xFC, 0xC6, 0x3C, 0x30, 0x07, 0x9D, +0x38, 0x22, 0x9D, 0x11, 0xE0, 0x43, 0xE9, 0xC1, +0xD5, 0x13, 0xE6, 0x07, 0xE8, 0x08, 0x3C, 0x4D, +0xFC, 0xC6, 0x3C, 0x30, 0x07, 0x9E, 0x38, 0x22, +0x1D, 0x11, 0xD5, 0xF5, 0xE6, 0x09, 0xE8, 0x08, +0x3C, 0x1D, 0xFC, 0xC6, 0x3C, 0x30, 0x07, 0x9F, +0x38, 0x20, 0x9D, 0x11, 0xD5, 0xEC, 0x3C, 0x57, +0xFD, 0x0A, 0xDF, 0x03, 0x3E, 0x07, 0xF5, 0xEA, +0x3C, 0x03, 0xFD, 0x82, 0x2E, 0x80, 0x0F, 0x5B, +0x96, 0x43, 0xF1, 0x82, 0x5A, 0x88, 0x01, 0x06, +0x3C, 0x23, 0xFD, 0xF1, 0x96, 0xD3, 0xF3, 0x82, +0x3C, 0x4D, 0xFC, 0xC5, 0x41, 0xC3, 0x84, 0x08, +0x88, 0x9C, 0x45, 0xE0, 0x00, 0x64, 0xA5, 0xA0, +0x3C, 0x03, 0xFD, 0x7C, 0x97, 0x73, 0x96, 0x43, +0xFE, 0x6C, 0x40, 0x20, 0xF8, 0x56, 0x3C, 0x53, +0xFD, 0x7C, 0x96, 0xD3, 0x3C, 0x60, 0x07, 0xA1, +0xF3, 0x81, 0x96, 0x2B, 0xFE, 0x34, 0xF1, 0x01, +0x40, 0xF0, 0x79, 0xF6, 0xE0, 0x2F, 0xE8, 0x09, +0x3C, 0x23, 0xFD, 0x7C, 0x96, 0xD3, 0xFF, 0x9C, +0x40, 0x63, 0x78, 0xD6, 0x97, 0x73, 0xF5, 0x81, +0xA5, 0x20, 0x3C, 0x03, 0xFD, 0x84, 0x96, 0x63, +0x96, 0x83, 0xFE, 0x8C, 0x44, 0x80, 0x00, 0x64, +0x40, 0x31, 0x20, 0x76, 0x3D, 0xE0, 0x07, 0x9B, +0x97, 0x99, 0xE2, 0xDE, 0x40, 0x6F, 0x3C, 0x1B, +0xF6, 0x83, 0xB1, 0x88, 0x3A, 0x05, 0x04, 0x00, +0x80, 0x46, 0x96, 0xF9, 0xF4, 0x01, 0x84, 0xA0, +0x49, 0xFF, 0xF7, 0xCA, 0x50, 0x8F, 0x80, 0x30, +0x3A, 0x53, 0x14, 0x00, 0x3A, 0x54, 0x14, 0x20, +0x02, 0x4F, 0x80, 0x12, 0x20, 0x1F, 0x80, 0x20, +0x12, 0x4F, 0x80, 0x1A, 0x20, 0x33, 0x00, 0x01, +0x20, 0x03, 0x00, 0x02, 0x20, 0x53, 0x00, 0x03, +0x8A, 0x61, 0x3A, 0x13, 0x04, 0x00, 0x8A, 0xA0, +0x3A, 0x15, 0x04, 0x20, 0x9C, 0xA9, 0x9D, 0x19, +0xFF, 0x14, 0x3C, 0x27, 0xFA, 0xEE, 0x96, 0x21, +0x02, 0xAF, 0x80, 0x12, 0xE0, 0x40, 0x12, 0xAF, +0x80, 0x16, 0xE8, 0x03, 0x3C, 0x0B, 0xFA, 0xEE, +0x3C, 0x43, 0xFD, 0x7E, 0x96, 0x63, 0xE0, 0x61, +0xE8, 0x1A, 0x3C, 0x33, 0xFD, 0x7F, 0x96, 0x9B, +0xE0, 0xA2, 0xE8, 0x15, 0x3C, 0x53, 0xFD, 0x80, +0x97, 0x2B, 0xE0, 0x80, 0xE9, 0x10, 0x3C, 0x13, +0xFD, 0x83, 0x96, 0xCB, 0xE0, 0x03, 0xE9, 0x0B, +0x3D, 0xED, 0xFC, 0xC6, 0x3C, 0x03, 0xFD, 0x81, +0x39, 0xCF, 0x70, 0x11, 0x96, 0x83, 0x41, 0xCE, +0x08, 0x07, 0xD5, 0x02, 0x87, 0x81, 0xB1, 0x4A, +0x2E, 0x17, 0xFA, 0x19, 0x3A, 0x22, 0x8C, 0x00, +0xF4, 0x03, 0x3E, 0x0F, 0xF9, 0x74, 0x49, 0xFF, +0xFD, 0x34, 0x20, 0x43, 0x00, 0x01, 0x20, 0xA3, +0x00, 0x00, 0x3C, 0x13, 0xFF, 0x1B, 0x8A, 0x8A, +0x96, 0xCB, 0xE0, 0x83, 0xE8, 0x15, 0x20, 0x23, +0x00, 0x03, 0x21, 0xE3, 0x00, 0x02, 0x3C, 0x13, +0xFF, 0x1B, 0x40, 0x51, 0x78, 0x01, 0x96, 0xCB, +0xE0, 0xA3, 0xE8, 0x0A, 0x3C, 0x23, 0xFF, 0x1C, +0x8C, 0x81, 0x8C, 0xA1, 0x96, 0x53, 0xFF, 0x64, +0x40, 0xA0, 0x94, 0x07, 0xD5, 0x02, 0x85, 0x41, +0x4F, 0xC3, 0x00, 0x07, 0x5A, 0x00, 0x01, 0x05, +0xF0, 0x02, 0x4E, 0x03, 0x00, 0x7E, 0x87, 0xC0, +0x3A, 0x03, 0x04, 0x00, 0xB0, 0x84, 0xB0, 0xC6, +0x97, 0x3B, 0x3F, 0xE7, 0xEA, 0x72, 0x49, 0xFF, +0xFA, 0x05, 0xF2, 0x06, 0x3A, 0x03, 0x04, 0x00, +0xB0, 0xC5, 0x49, 0xFF, 0xFC, 0x06, 0x3C, 0x53, +0xF8, 0x35, 0x2E, 0x2F, 0xFA, 0x1F, 0xC8, 0x17, +0x20, 0x13, 0x00, 0x01, 0x21, 0xC3, 0x00, 0x00, +0x3C, 0x03, 0xFD, 0x88, 0x8A, 0x3C, 0x97, 0x03, +0x8C, 0x21, 0xE0, 0x24, 0xE8, 0x0C, 0x20, 0x03, +0x00, 0x03, 0x20, 0x63, 0x00, 0x02, 0x3C, 0x33, +0xFD, 0x89, 0x8A, 0x06, 0x97, 0x1B, 0x8C, 0x01, +0xE0, 0x04, 0xE9, 0x0A, 0x44, 0x00, 0x00, 0x38, +0x80, 0xC9, 0x42, 0x61, 0x00, 0x73, 0x87, 0xC0, +0x11, 0xE3, 0x00, 0x30, 0xD5, 0x10, 0x45, 0xE0, +0x00, 0x38, 0x80, 0x29, 0x3D, 0xC3, 0xFD, 0x15, +0x42, 0x11, 0x78, 0x73, 0x3C, 0x33, 0xFD, 0x16, +0x42, 0x32, 0xF0, 0x73, 0x84, 0x81, 0xAC, 0xCD, +0x10, 0x40, 0x80, 0x30, 0x40, 0x73, 0x94, 0xB6, +0x45, 0xC0, 0x00, 0x38, 0x80, 0x69, 0x42, 0x31, +0x70, 0x73, 0x50, 0x12, 0x81, 0x04, 0x3E, 0x2F, +0xF0, 0x8C, 0x50, 0x03, 0x80, 0xF0, 0x38, 0x41, +0x05, 0x11, 0x38, 0x61, 0x01, 0x11, 0xA9, 0x1F, +0xA9, 0x9E, 0xB0, 0x05, 0x49, 0xFF, 0xFE, 0x52, +0xB0, 0x04, 0x49, 0xFF, 0xFC, 0xFA, 0x2E, 0x3F, +0xFA, 0x1F, 0x80, 0x49, 0x42, 0x21, 0xF0, 0x73, +0xF5, 0x04, 0x10, 0xA1, 0x00, 0x31, 0xA9, 0x51, +0x3A, 0x04, 0x04, 0x00, 0x49, 0xFF, 0xFC, 0xC7, +0x2E, 0x4F, 0xFA, 0x1F, 0x81, 0x49, 0x42, 0xA2, +0x70, 0x73, 0x8C, 0x81, 0x96, 0x22, 0x50, 0x85, +0x00, 0x30, 0x84, 0x20, 0xE4, 0x0F, 0x12, 0x14, +0x00, 0x02, 0x3E, 0x07, 0xFA, 0x1F, 0x4E, 0xF3, +0xFE, 0x71, 0x48, 0x00, 0x00, 0x9F, 0x85, 0x01, +0x3E, 0x87, 0xEA, 0x72, 0x3E, 0x88, 0x02, 0x80, +0xB1, 0x0A, 0xF5, 0x02, 0x3A, 0x02, 0x04, 0x00, +0x80, 0x46, 0x96, 0xF9, 0xF4, 0x01, 0x49, 0xFF, +0xF7, 0xDE, 0x3A, 0x03, 0x04, 0x00, 0xB0, 0x84, +0xB0, 0xC6, 0x97, 0x3B, 0x49, 0xFF, 0xF9, 0x7E, +0x3A, 0x03, 0x04, 0x00, 0xF2, 0x06, 0xB0, 0xC5, +0x49, 0xFF, 0xFB, 0x7F, 0x3C, 0x53, 0xF8, 0x35, +0xC8, 0x17, 0x20, 0x13, 0x00, 0x01, 0x21, 0xC3, +0x00, 0x00, 0x3C, 0x33, 0xFD, 0x88, 0x8A, 0x3C, +0x96, 0x9B, 0x8C, 0x21, 0xE0, 0x22, 0xE8, 0x0C, +0x20, 0x33, 0x00, 0x03, 0x21, 0xE3, 0x00, 0x02, +0x3C, 0x03, 0xFD, 0x89, 0x8A, 0x7E, 0x97, 0x03, +0x8C, 0x61, 0xE0, 0x64, 0xE9, 0x0C, 0x2E, 0xFF, +0xFA, 0x1F, 0x44, 0x20, 0x00, 0x38, 0x80, 0x29, +0x42, 0x17, 0x88, 0x73, 0x84, 0x80, 0x10, 0x40, +0x80, 0x30, 0xD5, 0x13, 0x2E, 0x2F, 0xFA, 0x1F, +0x44, 0x10, 0x00, 0x38, 0x83, 0x89, 0x3C, 0x33, +0xFD, 0x15, 0x43, 0xC1, 0x04, 0x73, 0x3C, 0x03, +0xFD, 0x16, 0x42, 0x02, 0x8C, 0x73, 0x87, 0xC1, +0x12, 0x0E, 0x00, 0x05, 0x11, 0xEE, 0x00, 0x30, +0x40, 0x73, 0x94, 0xB6, 0x50, 0xF2, 0x84, 0x14, +0x2E, 0x3F, 0xFA, 0x1F, 0x50, 0x13, 0x84, 0x00, +0x45, 0xC0, 0x00, 0x38, 0x80, 0x09, 0x42, 0x01, +0xF0, 0x73, 0x38, 0x24, 0x3D, 0x11, 0x38, 0x44, +0x05, 0x11, 0xA8, 0x87, 0xA9, 0x06, 0xB0, 0x05, +0x49, 0xFF, 0xFD, 0xC8, 0xB0, 0x04, 0x49, 0xFF, +0xFC, 0x70, 0x2E, 0x3F, 0xFA, 0x1F, 0x80, 0x09, +0x42, 0x01, 0xF0, 0x73, 0xF5, 0x04, 0x10, 0xA0, +0x00, 0x31, 0xA9, 0x41, 0x3A, 0x03, 0x04, 0x00, +0x49, 0xFF, 0xFC, 0x3D, 0x2E, 0x4F, 0xFA, 0x1F, +0x80, 0x49, 0x42, 0x22, 0x70, 0x73, 0x8C, 0x81, +0x96, 0xE2, 0x51, 0xC1, 0x00, 0x30, 0x84, 0x20, +0xE4, 0x6F, 0x12, 0x1E, 0x00, 0x02, 0x3E, 0x37, +0xFA, 0x1F, 0xE8, 0x17, 0xB1, 0xCA, 0x3E, 0x0F, +0xF9, 0x74, 0x3A, 0x23, 0x8C, 0x00, 0x2E, 0x17, +0xFA, 0x19, 0xF4, 0x03, 0x49, 0xFF, 0xFB, 0xCE, +0x80, 0xE0, 0x5A, 0x07, 0xFF, 0x04, 0x48, 0xFF, +0xFF, 0x71, 0xB1, 0x8C, 0x3A, 0x03, 0x04, 0x00, +0x49, 0xFF, 0xFC, 0x19, 0x48, 0xFF, 0xFD, 0xD2, +0x84, 0x01, 0xFC, 0xC7, 0x84, 0x00, 0xDD, 0x9E, +0xFC, 0x00, 0x3E, 0x0F, 0xF5, 0xD8, 0x3E, 0x2F, +0xF0, 0x8C, 0x84, 0x20, 0xAC, 0x42, 0xAC, 0x40, +0xAC, 0x41, 0x12, 0x10, 0x00, 0x08, 0x14, 0x21, +0x00, 0xA3, 0x49, 0xFF, 0xFD, 0x97, 0xFC, 0x80, +0xFC, 0x00, 0x49, 0xFF, 0xFF, 0xED, 0xFC, 0x80, +0xFC, 0x20, 0x38, 0x60, 0x05, 0x11, 0x44, 0x50, +0x00, 0x64, 0xFE, 0xB4, 0xE0, 0x61, 0x80, 0xE1, +0x40, 0x21, 0x14, 0x56, 0xE8, 0x10, 0x9F, 0x89, +0xE0, 0xC3, 0xE9, 0x09, 0x38, 0x80, 0x19, 0x11, +0xE1, 0x02, 0xE8, 0x03, 0x8C, 0xC1, 0xD5, 0x03, +0x8E, 0xC1, 0xD5, 0xF7, 0xE0, 0xC3, 0x40, 0x61, +0xBC, 0x1B, 0xD5, 0x02, 0x80, 0xC1, 0xE0, 0x24, +0xE8, 0x0D, 0x8C, 0x21, 0xE0, 0x81, 0xE9, 0x06, +0x38, 0x30, 0x05, 0x11, 0xE0, 0x62, 0xE8, 0xFA, +0x8E, 0x21, 0xE0, 0x81, 0x40, 0x40, 0xBC, 0x1A, +0x80, 0xE4, 0x52, 0x03, 0x00, 0x01, 0x98, 0x47, +0x96, 0x09, 0xFC, 0xA0, 0xFC, 0x20, 0x3C, 0x30, +0x07, 0xAF, 0x3C, 0x50, 0x07, 0xB1, 0x3C, 0x60, +0x07, 0xB0, 0x40, 0x41, 0xA0, 0x09, 0xE2, 0xA4, +0x40, 0x73, 0x20, 0x09, 0x40, 0x52, 0x3C, 0x1A, +0xE2, 0xE1, 0xE9, 0x17, 0x97, 0xB0, 0xE2, 0xC1, +0xE8, 0x0C, 0x9A, 0xCE, 0x9A, 0xA5, 0xFE, 0xD4, +0x9B, 0xBE, 0x40, 0x71, 0x98, 0xF7, 0x9A, 0xE7, +0xE2, 0x83, 0x40, 0x32, 0x3C, 0x1B, 0xD5, 0x0E, +0x97, 0xD8, 0x9A, 0xBC, 0xFE, 0x54, 0x40, 0x10, +0x98, 0x37, 0x9A, 0xF9, 0xE2, 0x64, 0xE8, 0x03, +0xB6, 0x80, 0xD5, 0x05, 0xE2, 0xE3, 0x40, 0x33, +0xBC, 0x1B, 0xB6, 0x60, 0xB4, 0x80, 0xE2, 0x85, +0xE8, 0x02, 0xB6, 0xA0, 0xFC, 0xA0, 0xFC, 0x01, +0x96, 0x8B, 0x96, 0xC3, 0xF0, 0x81, 0xB6, 0x3F, +0x90, 0x10, 0x90, 0x30, 0x8A, 0x43, 0x9A, 0x08, +0x4E, 0x25, 0x00, 0x06, 0x84, 0xC0, 0x4E, 0x04, +0x00, 0x06, 0xD5, 0x08, 0x4E, 0x05, 0x00, 0x44, +0x84, 0xC3, 0xC8, 0x0C, 0xCA, 0x06, 0x84, 0x00, +0xD5, 0x40, 0x84, 0xC1, 0xC2, 0x23, 0xC8, 0x0A, +0xE4, 0xC2, 0x84, 0x04, 0xE9, 0x3A, 0x84, 0x0A, +0xD5, 0x38, 0xCA, 0x04, 0xCE, 0x19, 0x84, 0x01, +0xD5, 0x34, 0x44, 0x10, 0x00, 0x64, 0xFE, 0x8C, +0x40, 0x41, 0x00, 0x96, 0xFF, 0x24, 0x50, 0x02, +0x27, 0x10, 0x49, 0xFF, 0xDE, 0x78, 0xC0, 0xE8, +0x44, 0x51, 0x86, 0xA0, 0x41, 0xC2, 0x83, 0x96, +0x5E, 0xFE, 0x01, 0x03, 0xE9, 0xE6, 0x5E, 0xFE, +0x03, 0xC5, 0xE9, 0x06, 0xD5, 0xE8, 0x5A, 0x60, +0x03, 0xE8, 0x84, 0x07, 0xD5, 0x1A, 0x5E, 0xFE, +0x02, 0xC3, 0xE9, 0x0B, 0x84, 0x02, 0xC6, 0x15, +0x84, 0x06, 0x5A, 0x60, 0x01, 0x13, 0x84, 0x0C, +0x5A, 0x68, 0x02, 0x10, 0x84, 0x08, 0xD5, 0x0D, +0x84, 0x03, 0xC6, 0x0B, 0x84, 0x05, 0x5A, 0x60, +0x01, 0x09, 0x84, 0x0B, 0x5A, 0x68, 0x02, 0x06, +0x84, 0x09, 0xD5, 0x03, 0x84, 0xC2, 0xD5, 0xC4, +0xFC, 0x81, 0xC0, 0x1B, 0xC1, 0x18, 0x4C, 0x00, +0x80, 0x17, 0x5A, 0x08, 0x01, 0x07, 0x5A, 0x10, +0x0C, 0x18, 0x5A, 0x18, 0x02, 0x15, 0xDD, 0x9E, +0x5A, 0x08, 0x0C, 0x07, 0x5A, 0x10, 0x0B, 0x0E, +0x5A, 0x10, 0x01, 0x0C, 0xD5, 0x0C, 0x9E, 0x81, +0x4C, 0x11, 0x00, 0x08, 0x8C, 0x01, 0x4C, 0x10, +0x00, 0x05, 0xD5, 0x05, 0x84, 0x00, 0xDD, 0x9E, +0x84, 0x01, 0xDD, 0x9E, 0x84, 0x1F, 0xDD, 0x9E, +0xE2, 0x20, 0x3C, 0x23, 0xFD, 0xFA, 0xE8, 0x0D, +0x96, 0x90, 0xC2, 0x22, 0x3C, 0x43, 0xFD, 0xFA, +0x3C, 0x33, 0xFD, 0xFA, 0x97, 0x60, 0x8E, 0xA1, +0x42, 0x02, 0x84, 0x73, 0x96, 0x58, 0xD5, 0x16, +0x96, 0xD3, 0x90, 0x68, 0x4E, 0x37, 0x00, 0x07, +0x3C, 0x43, 0xFD, 0xFA, 0x97, 0x63, 0x90, 0xA8, +0xC5, 0x0F, 0x3C, 0x23, 0xFD, 0xFA, 0x3C, 0x33, +0xFD, 0xFA, 0x97, 0x13, 0x90, 0x88, 0x97, 0x5B, +0x8E, 0x81, 0x42, 0x02, 0x04, 0x73, 0x40, 0x12, +0xA0, 0x0A, 0x40, 0x00, 0x04, 0x16, 0xDD, 0x9E, +0xFC, 0x46, 0x3C, 0x23, 0xFE, 0x4B, 0x80, 0x61, +0x96, 0x86, 0xC2, 0x0A, 0x2E, 0x17, 0xEA, 0x79, +0x5A, 0x18, 0x02, 0x07, 0x84, 0x80, 0x3C, 0x48, +0x07, 0xAF, 0x3C, 0x48, 0x07, 0xB1, 0x44, 0x50, +0x00, 0x60, 0x3E, 0x4F, 0xEA, 0xC0, 0x43, 0xE0, +0x14, 0x24, 0x40, 0x82, 0x78, 0x00, 0x50, 0xF4, +0x00, 0x20, 0x00, 0x67, 0x80, 0x04, 0x80, 0x24, +0x50, 0xA2, 0x00, 0x40, 0x50, 0x92, 0x00, 0x48, +0xCE, 0x20, 0x89, 0x5E, 0x40, 0x74, 0xF8, 0x00, +0x8D, 0x44, 0x50, 0x14, 0x00, 0x2C, 0x9C, 0x3C, +0x3A, 0x25, 0x0C, 0x00, 0x50, 0x44, 0x00, 0x34, +0x3A, 0x20, 0x8C, 0x20, 0x2E, 0x90, 0x0F, 0x5E, +0x3A, 0x20, 0x0C, 0x00, 0x51, 0xE4, 0x00, 0x38, +0x3A, 0x22, 0x0C, 0x20, 0x84, 0x61, 0x12, 0x97, +0x80, 0x03, 0x12, 0x6F, 0x00, 0x02, 0x10, 0x37, +0x80, 0x04, 0x14, 0x64, 0x00, 0x10, 0xD5, 0x2F, +0x3C, 0x70, 0x07, 0xB0, 0xC7, 0x04, 0x3C, 0x20, +0x07, 0xAF, 0xCA, 0x2C, 0x44, 0x70, 0x00, 0x60, +0xFF, 0xC4, 0x3E, 0x4F, 0xEA, 0xF0, 0x89, 0x27, +0x98, 0x27, 0x8D, 0x24, 0x89, 0x47, 0x88, 0x87, +0x8D, 0x44, 0x8C, 0x04, 0x88, 0xE1, 0x3A, 0x24, +0x8C, 0x00, 0x8E, 0x84, 0x3A, 0x20, 0x0C, 0x20, +0x3A, 0x25, 0x0C, 0x00, 0x3A, 0x22, 0x0C, 0x20, +0x22, 0x23, 0x80, 0x01, 0x22, 0x33, 0x80, 0x00, +0x44, 0x50, 0x00, 0x64, 0x50, 0x83, 0x80, 0x40, +0xFE, 0xEC, 0xFF, 0x54, 0x14, 0x34, 0x00, 0x01, +0x14, 0x53, 0x80, 0x12, 0x3A, 0x25, 0x0C, 0x00, +0x3A, 0x24, 0x8C, 0x20, 0x84, 0x01, 0x48, 0x00, +0x02, 0x28, 0x51, 0xC4, 0x00, 0x48, 0xBD, 0x01, +0x44, 0xF0, 0x00, 0x64, 0x22, 0x64, 0x00, 0x00, +0x40, 0x72, 0xBC, 0xF6, 0x4C, 0x73, 0x40, 0x28, +0x04, 0x24, 0x00, 0x14, 0x22, 0x54, 0x00, 0x01, +0x41, 0xC1, 0x3F, 0x96, 0x4D, 0xC2, 0xC0, 0x20, +0x40, 0x05, 0x78, 0x00, 0x8C, 0x04, 0x88, 0x9E, +0x50, 0x42, 0x00, 0x2C, 0x3A, 0x20, 0x0C, 0x00, +0x3A, 0x22, 0x0C, 0x20, 0x50, 0x54, 0x00, 0x40, +0x42, 0x23, 0xBC, 0x24, 0x42, 0xFE, 0x3C, 0x24, +0x50, 0x61, 0x00, 0x32, 0x50, 0x17, 0x80, 0x32, +0xA9, 0xA9, 0x14, 0x14, 0x00, 0x12, 0x40, 0x74, +0xF8, 0x00, 0x8C, 0xE4, 0x3A, 0x20, 0x0C, 0x00, +0x48, 0x00, 0x00, 0xEB, 0x80, 0xE0, 0x44, 0x00, +0x00, 0x60, 0x42, 0x63, 0x80, 0x24, 0x40, 0x90, +0x98, 0x00, 0x50, 0xF4, 0x80, 0x40, 0xB6, 0x7F, +0x50, 0x34, 0x80, 0x28, 0x04, 0x54, 0x80, 0x12, +0x04, 0x84, 0x80, 0x0C, 0x04, 0x27, 0x80, 0x01, +0x44, 0xA0, 0x00, 0x64, 0xA1, 0x19, 0x40, 0x02, +0xA8, 0x16, 0x40, 0x14, 0x28, 0x36, 0x41, 0xE2, +0x2B, 0xD6, 0x41, 0xC1, 0x2B, 0x96, 0x12, 0x1F, +0x80, 0x17, 0x12, 0x0F, 0x80, 0x15, 0xB4, 0x29, +0x04, 0x04, 0x80, 0x01, 0x13, 0xEF, 0x80, 0x16, +0x13, 0xCF, 0x80, 0x14, 0x49, 0xFF, 0xDD, 0x47, +0xF0, 0x87, 0x04, 0x14, 0x80, 0x01, 0xF0, 0x0A, +0x49, 0xFF, 0xDD, 0x41, 0xF0, 0x81, 0xB4, 0x29, +0xF0, 0x0A, 0x49, 0xFF, 0xDD, 0x3C, 0xF0, 0x82, +0xF1, 0x0B, 0xF0, 0x0A, 0x49, 0xFF, 0xDD, 0x37, +0x3E, 0x4F, 0xEB, 0x08, 0x50, 0x82, 0x7F, 0xF8, +0x98, 0xA6, 0x40, 0x64, 0x18, 0x00, 0x8C, 0x44, +0x8C, 0xC4, 0xF0, 0x83, 0x3A, 0x01, 0x04, 0x00, +0x3A, 0x23, 0x0C, 0x00, 0xF4, 0x86, 0x49, 0xFF, +0xDD, 0x34, 0x83, 0x89, 0xF1, 0x0A, 0x80, 0xC0, +0xF0, 0x0B, 0x49, 0xFF, 0xFE, 0x72, 0xF0, 0x84, +0x0C, 0x1E, 0x00, 0x0C, 0xF0, 0x0A, 0x49, 0xFF, +0xFE, 0x6C, 0xF0, 0x85, 0xF1, 0x05, 0xF0, 0x04, +0x49, 0xFF, 0xFE, 0xBD, 0x50, 0x44, 0x80, 0x48, +0xB9, 0x01, 0xA0, 0xA1, 0x40, 0x50, 0xA8, 0xB6, +0x81, 0x00, 0x04, 0x14, 0x80, 0x14, 0xB8, 0x02, +0x41, 0xC1, 0x2B, 0x96, 0x40, 0x30, 0x28, 0x76, +0x40, 0xA0, 0xA9, 0x56, 0x12, 0x5F, 0x80, 0x16, +0x12, 0x3F, 0x80, 0x17, 0x13, 0xCF, 0x80, 0x14, +0x12, 0xAF, 0x80, 0x15, 0xF1, 0x0A, 0xF0, 0x0B, +0x49, 0xFF, 0xFE, 0x4B, 0xF0, 0x84, 0x0C, 0x14, +0x80, 0x16, 0xF0, 0x0A, 0x49, 0xFF, 0xFE, 0x45, +0xF0, 0x85, 0xF1, 0x05, 0xF0, 0x04, 0x49, 0xFF, +0xFE, 0x96, 0x83, 0x80, 0x46, 0x50, 0x02, 0x08, +0x58, 0x52, 0x8A, 0x34, 0x00, 0x04, 0x80, 0x06, +0xF3, 0x06, 0xB5, 0x45, 0x81, 0x23, 0xF4, 0x07, +0xC8, 0x1D, 0x3C, 0x23, 0xFD, 0x8F, 0x96, 0x53, +0xE0, 0x81, 0xE8, 0x16, 0x3C, 0x53, 0xFD, 0x8F, +0xF4, 0x01, 0x96, 0xEB, 0xE0, 0x83, 0xE8, 0x10, +0x3C, 0x23, 0xFD, 0x8F, 0xF5, 0x02, 0x96, 0x53, +0xE0, 0xA1, 0xE8, 0x0A, 0x3C, 0x33, 0xFD, 0x8F, +0xF2, 0x03, 0x97, 0x1B, 0xE0, 0x44, 0xE8, 0x04, +0x3C, 0x43, 0xFD, 0x8F, 0xD5, 0x1C, 0x81, 0xE0, +0xD5, 0x1E, 0x3C, 0x23, 0xFF, 0x1E, 0x96, 0x53, +0xE0, 0x81, 0xE8, 0x18, 0x3C, 0x03, 0xFF, 0x1E, +0xF3, 0x01, 0x97, 0x43, 0xE0, 0x65, 0xE8, 0x12, +0x3C, 0x43, 0xFF, 0x1E, 0xF1, 0x02, 0x96, 0xA3, +0xE0, 0x22, 0xE8, 0x0C, 0x3C, 0x03, 0xFF, 0x1E, +0xF3, 0x03, 0x97, 0x43, 0xE0, 0x65, 0xE8, 0x06, +0x3C, 0x43, 0xFF, 0x1E, 0x96, 0x23, 0xE0, 0xC0, +0xD5, 0x02, 0x85, 0xE0, 0x54, 0x67, 0x80, 0xFF, +0xF6, 0x81, 0x3E, 0x6F, 0xEA, 0xC0, 0x45, 0xE0, +0x00, 0x60, 0x80, 0x26, 0x42, 0x13, 0xF8, 0x73, +0xF0, 0x0A, 0xB4, 0x21, 0x49, 0xFF, 0xDC, 0x9B, +0xF5, 0x01, 0xC5, 0x4F, 0x5B, 0xC7, 0xFF, 0x04, +0x5A, 0x8F, 0xFF, 0x4C, 0xB4, 0x7F, 0x44, 0x40, +0x00, 0x60, 0xFF, 0x3C, 0xC3, 0x0E, 0x3E, 0x7F, +0xEA, 0xF4, 0x88, 0xE4, 0x88, 0x89, 0x50, 0xA2, +0x00, 0x04, 0x3A, 0x25, 0x0C, 0x00, 0x3A, 0x23, +0x8C, 0x20, 0x84, 0x00, 0x48, 0x00, 0x01, 0x09, +0x99, 0x74, 0x51, 0xC2, 0x80, 0x48, 0xBA, 0x01, +0x44, 0x10, 0x00, 0x64, 0x04, 0xF2, 0x80, 0x14, +0x3E, 0x8F, 0xEA, 0xEC, 0x40, 0x01, 0x04, 0x16, +0x89, 0x04, 0x40, 0x45, 0x10, 0x00, 0xFE, 0x0C, +0x40, 0x27, 0x84, 0x56, 0x8C, 0x84, 0x50, 0x30, +0x00, 0x32, 0xFE, 0x8C, 0xBB, 0x81, 0x51, 0xE1, +0x00, 0x32, 0x3A, 0x22, 0x0C, 0x00, 0x22, 0x02, +0x80, 0x00, 0x3A, 0x24, 0x0C, 0x20, 0x22, 0x32, +0x80, 0x01, 0xFE, 0x0C, 0xFE, 0x5C, 0x51, 0xC2, +0x80, 0x40, 0x15, 0xE2, 0x80, 0x14, 0x14, 0x12, +0x80, 0x12, 0x50, 0x52, 0x80, 0x38, 0x3C, 0x43, +0xFD, 0x8E, 0x22, 0x12, 0x80, 0x02, 0xB8, 0x81, +0x96, 0x23, 0x9C, 0x89, 0xE0, 0x20, 0xAC, 0xAA, +0x85, 0x01, 0xE8, 0x04, 0x48, 0x00, 0x00, 0xD0, +0x85, 0x00, 0x44, 0x30, 0x00, 0x60, 0xFE, 0xFC, +0x3E, 0x4F, 0xEA, 0xEC, 0x99, 0x73, 0x88, 0x83, +0x88, 0x6A, 0x8C, 0x64, 0x51, 0xC2, 0x80, 0x48, +0x3A, 0x21, 0x8C, 0x00, 0x51, 0xE2, 0x80, 0x38, +0x3A, 0x22, 0x0C, 0x20, 0xB8, 0x01, 0x04, 0x32, +0x80, 0x14, 0x44, 0x20, 0x00, 0x64, 0x84, 0x20, +0x12, 0x1F, 0x00, 0x02, 0x40, 0xF0, 0x09, 0xF6, +0x41, 0xE1, 0x8B, 0xD6, 0x12, 0xFF, 0x80, 0x14, +0x13, 0xEF, 0x80, 0x15, 0xF1, 0x0A, 0xB4, 0x05, +0x49, 0xFF, 0xDC, 0x25, 0xF0, 0x81, 0xF1, 0x01, +0xB0, 0x09, 0x49, 0xFF, 0xFD, 0x41, 0x4E, 0x82, +0x00, 0x04, 0xFA, 0xAF, 0xD5, 0x06, 0x2E, 0x50, +0x0F, 0x5E, 0xF1, 0x09, 0xE2, 0xA1, 0xE8, 0x02, +0xF5, 0x89, 0x44, 0x80, 0x00, 0x60, 0x80, 0x86, +0x42, 0x43, 0xA0, 0x73, 0xF0, 0x09, 0x02, 0x12, +0x00, 0x13, 0x49, 0xFF, 0xFD, 0xD3, 0xF0, 0x89, +0xB4, 0x1F, 0x5A, 0x08, 0x01, 0x06, 0x3C, 0x23, +0xFE, 0xA3, 0x96, 0xD3, 0xF3, 0x89, 0x45, 0xC0, +0x00, 0x60, 0x83, 0xC6, 0x43, 0xE3, 0xF0, 0x73, +0x00, 0x1F, 0x00, 0x5E, 0x5A, 0x18, 0x01, 0x0C, +0x3C, 0x43, 0xFF, 0x1D, 0xF2, 0x09, 0x96, 0x23, +0xE2, 0x40, 0xE8, 0x05, 0x3C, 0x33, 0xFF, 0x1D, +0x97, 0x5B, 0xF5, 0x89, 0xF1, 0x09, 0x5C, 0xF0, +0x80, 0x20, 0xE9, 0x03, 0xFA, 0x8F, 0xF4, 0x89, +0x44, 0x20, 0x00, 0x60, 0xFE, 0xBC, 0x99, 0x72, +0x81, 0x05, 0xF1, 0x09, 0x2B, 0xC4, 0x00, 0x24, +0x52, 0x40, 0x80, 0x20, 0x04, 0x04, 0x00, 0x01, +0x43, 0xEE, 0x10, 0x24, 0x44, 0x30, 0x00, 0x64, +0xFE, 0x0C, 0x42, 0x0F, 0x0C, 0x73, 0x41, 0xC0, +0x14, 0x09, 0x22, 0x02, 0x80, 0x01, 0x12, 0x12, +0x80, 0x13, 0xFE, 0x24, 0x04, 0x44, 0x00, 0x02, +0xF5, 0x81, 0xFF, 0x0C, 0x42, 0x40, 0x0C, 0x73, +0x50, 0x11, 0x00, 0x40, 0x88, 0x26, 0x81, 0x04, +0x40, 0x54, 0x88, 0x00, 0x88, 0x4A, 0x8C, 0xA4, +0x93, 0x05, 0x8C, 0x44, 0x15, 0xC0, 0x80, 0x01, +0x14, 0x80, 0x80, 0x02, 0x3A, 0x21, 0x0C, 0x00, +0x3A, 0x02, 0x84, 0x00, 0x49, 0xFF, 0xDB, 0xC5, +0xB4, 0x5F, 0xF3, 0x01, 0xCA, 0x09, 0x04, 0x41, +0x80, 0x10, 0x51, 0xE1, 0x80, 0x38, 0xE0, 0x80, +0xE8, 0x03, 0x14, 0x0F, 0x00, 0x02, 0x44, 0x00, +0x00, 0x60, 0x42, 0x63, 0x80, 0x73, 0x50, 0x23, +0x00, 0x48, 0xA0, 0x51, 0x44, 0x30, 0x00, 0x64, +0x40, 0x50, 0x8C, 0xB6, 0x40, 0x4E, 0x0C, 0x96, +0xDC, 0x08, 0xA0, 0x12, 0x40, 0x24, 0x0C, 0x56, +0x40, 0x50, 0x0C, 0xB6, 0x87, 0x81, 0xD2, 0x0E, +0x44, 0x60, 0x00, 0x60, 0xFF, 0xBC, 0x3E, 0x1F, +0xEA, 0xF4, 0x88, 0x26, 0x88, 0xC9, 0x8C, 0xC4, +0x87, 0x80, 0x3A, 0x23, 0x0C, 0x00, 0x3A, 0x20, +0x8C, 0x20, 0x44, 0x30, 0x00, 0x60, 0xFF, 0xDC, +0x89, 0x47, 0x89, 0x27, 0x8D, 0x44, 0x8D, 0x24, +0x3A, 0x25, 0x0C, 0x00, 0x3A, 0x24, 0x8C, 0x20, +0x80, 0x1C, 0xD5, 0x02, 0xB4, 0x1F, 0xFC, 0xC6, +0x44, 0x30, 0x00, 0x60, 0x3E, 0x1F, 0xEA, 0xC0, +0x42, 0x10, 0x0C, 0x73, 0x3C, 0x20, 0x07, 0xB2, +0x04, 0x00, 0x80, 0x16, 0x9F, 0x51, 0xA5, 0x0F, +0xFE, 0x2C, 0x44, 0xF0, 0x00, 0x64, 0x42, 0x02, +0x3C, 0x73, 0x40, 0x20, 0x08, 0x56, 0x14, 0x20, +0x80, 0x16, 0xDD, 0x9E, 0xFC, 0x20, 0x3C, 0x13, +0xF8, 0x37, 0x84, 0xA0, 0x3E, 0x28, 0x0A, 0xA8, +0x94, 0x49, 0x3E, 0x68, 0x0F, 0x80, 0x3E, 0x4F, +0xF0, 0x8C, 0x81, 0xE5, 0xD1, 0x14, 0x99, 0xEE, +0xA4, 0x10, 0x03, 0xC3, 0x81, 0x04, 0x8A, 0x1C, +0x97, 0xC3, 0x1A, 0x71, 0x00, 0x01, 0x4E, 0x77, +0x00, 0x06, 0x98, 0x25, 0x12, 0x70, 0x01, 0x04, +0xD5, 0x04, 0x98, 0xE5, 0x12, 0xF1, 0x81, 0x04, +0x8C, 0xA2, 0xD5, 0xED, 0x3C, 0x03, 0xF8, 0x36, +0x84, 0xA0, 0x3E, 0x28, 0x0A, 0x80, 0x94, 0x41, +0x3E, 0x68, 0x0F, 0x80, 0x3E, 0x4F, 0xF0, 0x8C, +0x80, 0xE5, 0xD1, 0x14, 0x98, 0xEE, 0xA4, 0x10, +0x02, 0x81, 0x80, 0xF0, 0x8A, 0x08, 0x96, 0x03, +0x1A, 0x01, 0x00, 0x01, 0x4E, 0x07, 0x00, 0x06, +0x98, 0xE5, 0x12, 0x01, 0x80, 0xF0, 0xD5, 0x04, +0x98, 0xE5, 0x12, 0x71, 0x80, 0xF0, 0x8C, 0xA2, +0xD5, 0xED, 0xFC, 0xA0, 0xFC, 0x40, 0x49, 0xFF, +0xFF, 0xBF, 0x84, 0x00, 0x3C, 0x6D, 0xFC, 0xC5, +0x3E, 0x38, 0x08, 0xA0, 0x3E, 0x78, 0x0F, 0x80, +0x3E, 0x4F, 0xF0, 0x8C, 0x80, 0xA0, 0x3C, 0x13, +0xF8, 0x33, 0xE0, 0x01, 0xE8, 0x15, 0xA4, 0x58, +0x38, 0x93, 0x81, 0x01, 0x94, 0x81, 0x8A, 0x29, +0x96, 0x4B, 0x1A, 0x11, 0x80, 0x01, 0x88, 0x46, +0x4E, 0x15, 0x00, 0x06, 0xAC, 0x50, 0x38, 0x12, +0x01, 0x09, 0xD5, 0x04, 0xAD, 0x50, 0x38, 0x52, +0x01, 0x09, 0x8C, 0x01, 0xD5, 0xE9, 0xFC, 0xC0, +0xFC, 0x20, 0x44, 0x0F, 0x80, 0x00, 0x44, 0x10, +0x7F, 0xFF, 0x3C, 0x0B, 0xF9, 0x94, 0x3C, 0x0B, +0xF9, 0x97, 0x3C, 0x1B, 0xF9, 0x95, 0x3C, 0x1B, +0xF9, 0x98, 0x3D, 0xC3, 0xF8, 0x37, 0x44, 0x8F, +0x80, 0x00, 0x3C, 0x37, 0xF9, 0x96, 0x45, 0xE0, +0x7F, 0xFF, 0x3E, 0x18, 0x0A, 0xA8, 0x84, 0x00, +0x3E, 0x6F, 0xF0, 0x8C, 0xE0, 0x1C, 0xE8, 0x11, +0x95, 0xC1, 0x88, 0xE6, 0x22, 0x43, 0x81, 0x04, +0xE1, 0x04, 0xE8, 0x04, 0x22, 0x80, 0x80, 0x00, +0x96, 0xC3, 0xE0, 0x9E, 0xE8, 0x03, 0x23, 0xE0, +0x80, 0x00, 0x8C, 0x01, 0x8C, 0x22, 0xD5, 0xEF, +0x3C, 0x3B, 0xF9, 0x96, 0x3C, 0x8B, 0xF9, 0x94, +0x3D, 0xEB, 0xF9, 0x95, 0x3C, 0x63, 0xF8, 0x36, +0x3C, 0x37, 0xF9, 0x97, 0x3C, 0x57, 0xF9, 0x99, +0x3C, 0x27, 0xF9, 0x98, 0x3E, 0x18, 0x0A, 0x80, +0x84, 0x00, 0x3E, 0x7F, 0xF0, 0x8C, 0xE0, 0x06, +0xE8, 0x11, 0x95, 0x01, 0x88, 0x87, 0x22, 0x82, +0x00, 0xF0, 0xE0, 0x68, 0xE8, 0x04, 0x22, 0x30, +0x80, 0x00, 0x97, 0x43, 0xE1, 0x02, 0xE8, 0x03, +0x22, 0x20, 0x80, 0x00, 0x8C, 0x01, 0x8C, 0x22, +0xD5, 0xEF, 0x3C, 0x3B, 0xF9, 0x97, 0x3C, 0x5B, +0xF9, 0x99, 0x3C, 0x2B, 0xF9, 0x98, 0xFC, 0xA0, +0xFC, 0x00, 0x49, 0xFF, 0xFF, 0xA7, 0x44, 0x00, +0x7F, 0xFF, 0x44, 0x1F, 0x80, 0x00, 0x3C, 0x0B, +0xF9, 0x92, 0x3C, 0x1B, 0xF9, 0x91, 0x3C, 0x53, +0xF8, 0x33, 0x3C, 0x43, 0xF9, 0x93, 0x45, 0xC0, +0x7F, 0xFF, 0x44, 0x3F, 0x80, 0x00, 0x84, 0x00, +0x3E, 0x6F, 0xF0, 0x8C, 0xE0, 0x05, 0xE8, 0x0C, +0x38, 0x23, 0x01, 0x11, 0xE0, 0x62, 0xE8, 0x03, +0x97, 0x01, 0x80, 0x62, 0xE0, 0x5C, 0x41, 0xC1, +0x3C, 0x1B, 0x8C, 0x01, 0xD5, 0xF4, 0x3C, 0x3B, +0xF9, 0x91, 0x3C, 0x4B, 0xF9, 0x93, 0x3D, 0xCB, +0xF9, 0x92, 0xFC, 0x80, 0x3C, 0x33, 0xF8, 0x36, +0x3E, 0x18, 0x0A, 0x80, 0x84, 0x00, 0x3E, 0x48, +0x0F, 0x80, 0xE0, 0x03, 0xE8, 0x0B, 0x95, 0x41, +0x88, 0xA4, 0xA4, 0x88, 0x02, 0xF2, 0x80, 0xF0, +0x8C, 0x01, 0x88, 0x4F, 0x1A, 0x20, 0x80, 0x01, +0xD5, 0xF5, 0x3C, 0x33, 0xF8, 0x37, 0x3E, 0x18, +0x0A, 0xA8, 0x84, 0x00, 0x3E, 0x48, 0x0F, 0x80, +0xE0, 0x03, 0xE8, 0x0B, 0x94, 0x81, 0x88, 0x44, +0x8C, 0x01, 0x02, 0x51, 0x01, 0x04, 0xA4, 0x88, +0x88, 0x45, 0x1A, 0x20, 0x80, 0x01, 0xD5, 0xF5, +0xDD, 0x9E, 0x3C, 0x33, 0xF8, 0x33, 0x3E, 0x18, +0x08, 0xA0, 0x84, 0x00, 0x3E, 0x48, 0x0F, 0x80, +0xE0, 0x03, 0xE8, 0x09, 0x38, 0x52, 0x01, 0x01, +0xA4, 0x88, 0x8C, 0x01, 0x88, 0x45, 0x1A, 0x20, +0x80, 0x01, 0xD5, 0xF7, 0xFC, 0x00, 0x49, 0xFF, +0xFF, 0xC7, 0xFC, 0x80, 0xFC, 0x40, 0x84, 0x60, +0x3C, 0x5D, 0xFC, 0xC5, 0x3E, 0x48, 0x02, 0x80, +0x3E, 0x2F, 0xF0, 0x8C, 0x80, 0xC3, 0x3C, 0x03, +0xF8, 0x35, 0xE0, 0x60, 0x4E, 0xF2, 0x00, 0x8C, +0x84, 0x20, 0x40, 0xA0, 0x04, 0x08, 0x3D, 0xE3, +0xF8, 0x34, 0x41, 0xC1, 0x84, 0x08, 0x81, 0x21, +0x81, 0x01, 0x80, 0x01, 0xE0, 0x1E, 0xE8, 0x1D, +0x40, 0x7E, 0x04, 0x00, 0x40, 0xF0, 0x04, 0x08, +0x88, 0xE4, 0x40, 0xF7, 0x88, 0x00, 0x22, 0x73, +0x83, 0x10, 0x22, 0xF7, 0x81, 0x30, 0x8A, 0xEF, +0x4E, 0x74, 0x00, 0x08, 0xFF, 0xFA, 0xE1, 0x07, +0xE8, 0x09, 0x40, 0x83, 0x80, 0x11, 0xD5, 0x06, +0xC7, 0x05, 0xE1, 0x27, 0xE8, 0x03, 0x40, 0x93, +0x80, 0x11, 0x8C, 0x01, 0x88, 0x2A, 0xD5, 0xE3, +0x3C, 0x70, 0x07, 0x96, 0x97, 0xD6, 0x96, 0x39, +0xC0, 0x0B, 0x3C, 0x13, 0xFD, 0x7D, 0x44, 0xA0, +0x00, 0x64, 0x96, 0x0B, 0x43, 0xE4, 0x00, 0x24, +0x40, 0x7F, 0x28, 0xF7, 0x96, 0x3B, 0x3C, 0x13, +0xFD, 0x92, 0x97, 0xCB, 0x4E, 0x74, 0x00, 0x0C, +0x3C, 0x13, 0xFD, 0x92, 0xFF, 0xC2, 0x96, 0x4B, +0xE0, 0xE1, 0xE8, 0x05, 0x3C, 0x03, 0xFD, 0x92, +0xFE, 0x42, 0x96, 0x0B, 0x3D, 0xC0, 0x07, 0x97, +0x54, 0x7E, 0x08, 0x00, 0x96, 0x79, 0xC1, 0x0B, +0x3C, 0x13, 0xFE, 0x1E, 0x44, 0x80, 0x00, 0x64, +0x97, 0xCB, 0x42, 0x94, 0x9C, 0x24, 0x40, 0x14, +0xA0, 0x37, 0x96, 0x4B, 0x3E, 0x9F, 0xF2, 0xEC, +0x80, 0xE3, 0x85, 0x40, 0x88, 0x01, 0x3D, 0xE3, +0xF8, 0x34, 0xE1, 0x5E, 0xE8, 0x29, 0x3C, 0x13, +0xFD, 0xA6, 0x96, 0x4B, 0x4E, 0x14, 0x00, 0x07, +0x51, 0xC3, 0x83, 0x10, 0x38, 0x12, 0x71, 0x01, +0xD5, 0x03, 0x38, 0x11, 0x1D, 0x01, 0x02, 0x84, +0x80, 0x00, 0x8A, 0x20, 0x8A, 0x28, 0x38, 0x11, +0x1D, 0x09, 0x39, 0xE1, 0x1D, 0x11, 0x4F, 0xE4, +0x00, 0x04, 0x38, 0x61, 0x1D, 0x09, 0x3C, 0x10, +0x07, 0x98, 0x96, 0x46, 0xC1, 0x07, 0x94, 0x79, +0x88, 0x25, 0x38, 0xF1, 0x1D, 0x11, 0x12, 0xF0, +0x80, 0x00, 0x3D, 0xC3, 0xF8, 0x35, 0x8D, 0x41, +0x88, 0xFC, 0x8D, 0x22, 0xD5, 0xD5, 0x8C, 0x61, +0x48, 0xFF, 0xFF, 0x73, 0xFC, 0xC0, 0x3C, 0x03, +0xF8, 0x37, 0x3E, 0x5F, 0xF2, 0x94, 0x84, 0x80, +0xE0, 0x80, 0xE8, 0x07, 0x2A, 0xF2, 0x80, 0x01, +0x8C, 0x81, 0x12, 0xF2, 0x80, 0x1F, 0xD5, 0xF9, +0x3C, 0x23, 0xF8, 0x36, 0x3E, 0x1F, 0xF2, 0x6C, +0x84, 0x00, 0xE0, 0x02, 0xE8, 0x07, 0x2A, 0x30, +0x80, 0x01, 0x8C, 0x01, 0x12, 0x30, 0x80, 0x1F, +0xD5, 0xF9, 0xDD, 0x9E, 0xFC, 0x49, 0x3C, 0x23, +0xF8, 0x35, 0x3C, 0x43, 0xF8, 0x3C, 0x3C, 0xA3, +0xF8, 0x3A, 0x3C, 0x03, 0xF8, 0x3B, 0x3C, 0x83, +0xF8, 0x3D, 0x42, 0x35, 0x08, 0x24, 0x50, 0x9F, +0x80, 0x18, 0x95, 0xA2, 0x95, 0xE1, 0x8E, 0xE4, +0xF0, 0x81, 0x50, 0x14, 0x7F, 0xFF, 0x41, 0xE1, +0x04, 0x08, 0x94, 0x19, 0x3E, 0x5F, 0xF0, 0x8C, +0x88, 0xC9, 0x9C, 0xA1, 0xF7, 0x84, 0xF1, 0x82, +0x88, 0x05, 0xF6, 0x83, 0xF2, 0x85, 0x45, 0xC0, +0x00, 0x64, 0x84, 0xE2, 0xF1, 0x01, 0xE0, 0x2A, +0xE9, 0x4B, 0xF3, 0x04, 0xF6, 0x03, 0x98, 0x98, +0x80, 0xA4, 0xE1, 0x05, 0xE9, 0x36, 0xDC, 0x0B, +0x22, 0xF0, 0x00, 0x02, 0x22, 0x10, 0x00, 0x04, +0x88, 0x2F, 0x40, 0x30, 0x9C, 0x76, 0x38, 0x34, +0x96, 0x0A, 0xD5, 0x27, 0xF1, 0x05, 0xD9, 0x06, +0x22, 0x30, 0x00, 0x03, 0x22, 0x10, 0x00, 0x05, +0xD5, 0x1C, 0xF3, 0x02, 0xD3, 0x03, 0x4C, 0x54, +0x40, 0x07, 0x22, 0x31, 0x7F, 0xFE, 0x22, 0x11, +0x00, 0x00, 0xD5, 0x13, 0x3C, 0x13, 0xFD, 0xA6, +0x22, 0xF1, 0x00, 0x00, 0x96, 0xCB, 0x3C, 0x13, +0xFD, 0xA6, 0x42, 0x37, 0x8C, 0x24, 0x22, 0xF1, +0x00, 0x04, 0x96, 0x4B, 0x42, 0x17, 0x84, 0x24, +0x40, 0x31, 0xF0, 0x76, 0x40, 0x10, 0xF0, 0x36, +0x88, 0x23, 0x40, 0x30, 0x9C, 0x76, 0xB6, 0x66, +0x8C, 0xA1, 0x8C, 0xC4, 0x8C, 0x42, 0xD5, 0xCA, +0x80, 0xA4, 0xE1, 0x05, 0xE9, 0x0A, 0x38, 0x20, +0x15, 0x01, 0x38, 0x64, 0x96, 0x02, 0x8A, 0x46, +0x38, 0x20, 0x15, 0x09, 0x8C, 0xA1, 0xD5, 0xF6, +0x8D, 0x41, 0x88, 0x1E, 0xD5, 0xB4, 0xFC, 0xC9, +0xFC, 0x40, 0x84, 0x00, 0x3E, 0x3F, 0xF0, 0x8C, +0x80, 0x40, 0x98, 0x43, 0x8C, 0x02, 0x12, 0x20, +0x81, 0x30, 0x3E, 0x7F, 0xF0, 0x8C, 0x5A, 0x08, +0x28, 0xFA, 0x3C, 0x40, 0x07, 0x96, 0x97, 0x36, +0xC4, 0x5D, 0x2E, 0x57, 0xEA, 0x79, 0xCD, 0x0A, +0x3C, 0x63, 0xFD, 0xA6, 0x96, 0x33, 0x4E, 0x04, +0x00, 0x03, 0xFE, 0x02, 0x96, 0x43, 0xC1, 0x52, +0xD5, 0x03, 0x44, 0x10, 0x00, 0x64, 0x3C, 0x53, +0xF8, 0x35, 0x44, 0x60, 0x00, 0x64, 0x84, 0x60, +0x3C, 0x93, 0xF8, 0x34, 0xFF, 0xAC, 0x80, 0x43, +0x3E, 0xA8, 0x02, 0x80, 0xE0, 0x49, 0xE8, 0x42, +0x84, 0x00, 0x80, 0x80, 0xE0, 0x85, 0xE8, 0x34, +0x41, 0xC2, 0x0C, 0x00, 0x40, 0x8E, 0x04, 0x08, +0x89, 0x0A, 0x22, 0x84, 0x03, 0x10, 0x4E, 0x85, +0x00, 0x16, 0x3D, 0xE3, 0xFF, 0x0C, 0x40, 0xFF, +0x00, 0x11, 0x4E, 0xF7, 0x00, 0x0D, 0x3C, 0x83, +0xFF, 0x0C, 0x41, 0xE4, 0x00, 0x11, 0x38, 0x83, +0xF1, 0x11, 0xE1, 0x1E, 0xE9, 0x04, 0x3C, 0x83, +0xFF, 0x0C, 0xD5, 0x15, 0x38, 0x83, 0xF1, 0x11, +0xD5, 0x14, 0x4E, 0x14, 0x00, 0x14, 0x3D, 0xE3, +0xFF, 0x0D, 0x40, 0xFF, 0x00, 0x11, 0x4E, 0xF4, +0x00, 0x0D, 0x3D, 0xC3, 0xFF, 0x0D, 0x41, 0xEE, +0x00, 0x11, 0x40, 0xFF, 0x20, 0x07, 0xE9, 0x05, +0x3C, 0x83, 0xFF, 0x0D, 0x40, 0x84, 0x00, 0x11, +0x88, 0x08, 0x8C, 0x81, 0xD5, 0xCC, 0x95, 0x11, +0xFE, 0x0C, 0x88, 0x87, 0x40, 0x80, 0x19, 0x16, +0x12, 0x82, 0x01, 0x30, 0x8C, 0x41, 0x88, 0x65, +0xD5, 0xBE, 0xFC, 0xC0, 0xFC, 0x00, 0x49, 0xFF, +0xFF, 0x91, 0x49, 0xFF, 0xFE, 0x69, 0x49, 0xFF, +0xFF, 0x00, 0x49, 0xFF, 0xFD, 0xFF, 0xFC, 0x80, +0xDD, 0x9E, 0x3E, 0x0F, 0xF5, 0xD8, 0x84, 0x20, +0x84, 0x40, 0x10, 0x10, 0x00, 0x15, 0x10, 0x10, +0x00, 0x17, 0x10, 0x10, 0x00, 0x16, 0x10, 0x10, +0x00, 0x14, 0x10, 0x10, 0x00, 0x21, 0x10, 0x10, +0x00, 0x20, 0x3C, 0x28, 0x07, 0xA8, 0xDD, 0x9E, +0x3C, 0x10, 0x07, 0x96, 0x84, 0x00, 0x96, 0x6E, +0x3E, 0x07, 0xF6, 0x01, 0xC1, 0x54, 0x3C, 0x23, +0xFD, 0x4C, 0x3C, 0x43, 0xFD, 0x69, 0x96, 0xD3, +0x97, 0x63, 0xFF, 0x5C, 0x44, 0x40, 0x00, 0x64, +0x40, 0x02, 0x90, 0x16, 0x3C, 0x14, 0x05, 0x8A, +0x96, 0x83, 0xE0, 0x22, 0x3E, 0x38, 0x02, 0x80, +0xE9, 0x42, 0xFC, 0x00, 0x3C, 0x64, 0x05, 0x8D, +0xE0, 0xC2, 0xE9, 0x3C, 0x44, 0x50, 0x00, 0x5A, +0xFE, 0x6C, 0x40, 0x40, 0x90, 0x96, 0x96, 0x23, +0x84, 0x80, 0x3D, 0xC3, 0xF8, 0x34, 0x80, 0x44, +0xE0, 0x9C, 0xE8, 0x0B, 0x95, 0x61, 0x88, 0xA3, +0x22, 0x12, 0x84, 0x00, 0xE0, 0x20, 0xE9, 0x03, +0x8C, 0x41, 0x96, 0x90, 0x8C, 0x81, 0xD5, 0xF5, +0x44, 0x00, 0x00, 0x5A, 0xFE, 0x34, 0x44, 0x60, +0x00, 0x64, 0x40, 0x10, 0x18, 0x36, 0x84, 0x80, +0x96, 0x0B, 0x3D, 0xE3, 0xF8, 0x35, 0x80, 0x24, +0xE0, 0x9E, 0xE8, 0x0B, 0x95, 0xA1, 0x88, 0xC3, +0x22, 0x53, 0x04, 0x14, 0xE0, 0xA0, 0xE9, 0x03, +0x8C, 0x21, 0x96, 0x48, 0x8C, 0x81, 0xD5, 0xF5, +0x3C, 0x33, 0xFD, 0xA1, 0x97, 0x5B, 0xE0, 0x45, +0xE8, 0x06, 0x3C, 0x23, 0xFD, 0xA1, 0x97, 0x13, +0xE0, 0x24, 0xE9, 0x04, 0x87, 0x81, 0x3F, 0xC7, +0xF6, 0x01, 0xFC, 0x80, 0xDD, 0x9E, 0xFC, 0x41, +0x85, 0x40, 0x80, 0x8A, 0x81, 0x0A, 0x83, 0x8A, +0x3E, 0x9F, 0xF6, 0x04, 0x84, 0xE0, 0x80, 0xC7, +0x2E, 0xFF, 0xFA, 0x1F, 0xE0, 0xCF, 0xE8, 0x14, +0x44, 0x00, 0x00, 0x38, 0x80, 0x29, 0x42, 0x13, +0x00, 0x73, 0x3C, 0x20, 0x07, 0xA8, 0xA0, 0xCD, +0xE0, 0x62, 0xE8, 0x0D, 0x80, 0x06, 0xF4, 0x81, +0x49, 0x00, 0x07, 0x77, 0x2E, 0x5F, 0xFA, 0x1F, +0x8E, 0xC1, 0xF4, 0x01, 0xCD, 0x04, 0xC4, 0x6D, +0x48, 0x00, 0x00, 0x8B, 0x45, 0xE0, 0x00, 0x38, +0x3E, 0x5F, 0xF6, 0x04, 0x42, 0x53, 0x78, 0x73, +0x50, 0x02, 0x80, 0x08, 0x20, 0x10, 0x00, 0x05, +0x20, 0x20, 0x00, 0x04, 0x20, 0xF0, 0x00, 0x06, +0x9A, 0xCA, 0x20, 0x10, 0x00, 0x07, 0x3D, 0xE0, +0x07, 0xBF, 0xA0, 0x2D, 0x8A, 0x2F, 0x8C, 0x61, +0x8C, 0x21, 0xE0, 0x1E, 0x42, 0x21, 0x84, 0x24, +0x3C, 0x50, 0x07, 0x96, 0xE8, 0x23, 0x97, 0x66, +0xC5, 0x49, 0x3C, 0x50, 0x01, 0x2D, 0xCD, 0x10, +0x3C, 0x53, 0xFD, 0x86, 0x97, 0x6B, 0xE0, 0xA3, +0xE9, 0x0F, 0x3C, 0x33, 0xFD, 0x86, 0x97, 0x5B, +0xE0, 0xA1, 0xE9, 0x0A, 0x3C, 0x10, 0x07, 0xB6, +0xE0, 0x22, 0xE9, 0x06, 0xD5, 0x37, 0x3C, 0x10, +0x07, 0xB6, 0xE0, 0x41, 0xE9, 0x33, 0x40, 0xFE, +0x00, 0x07, 0xE8, 0x03, 0x41, 0xC0, 0x00, 0x11, +0x84, 0x41, 0x3E, 0x27, 0xF5, 0xF8, 0x8D, 0x41, +0xD5, 0x29, 0x97, 0x6E, 0xC5, 0x27, 0x3C, 0x50, +0x07, 0xB5, 0xE0, 0xA2, 0xE8, 0x07, 0x87, 0x81, +0x3F, 0xC7, 0xF5, 0xEC, 0x3F, 0xC7, 0xF5, 0xEF, +0xD5, 0x6C, 0x3C, 0x53, 0xFD, 0xA1, 0x97, 0x6B, +0xE0, 0x65, 0xE8, 0x06, 0x3C, 0x33, 0xFD, 0xA1, +0x97, 0x5B, 0xE0, 0x25, 0xE9, 0x05, 0x2F, 0xE7, +0xF6, 0x01, 0x5B, 0xE0, 0x01, 0x0B, 0x3C, 0x13, +0xFD, 0x6A, 0x96, 0xCB, 0xE0, 0x43, 0xE9, 0x0A, +0x2F, 0xE7, 0xF6, 0x01, 0x5B, 0xE8, 0x01, 0x07, +0xE1, 0x00, 0xE8, 0x03, 0x40, 0x80, 0x00, 0x11, +0x80, 0xFE, 0x8C, 0xC1, 0x48, 0xFF, 0xFF, 0x7E, +0x2E, 0x67, 0xF5, 0xF8, 0x5A, 0x68, 0x01, 0x1D, +0xCF, 0x1B, 0x3C, 0x03, 0xFD, 0x71, 0x3C, 0x50, +0x07, 0xA8, 0x97, 0xC3, 0x43, 0xEE, 0x1C, 0x24, +0x44, 0x30, 0x00, 0x64, 0x40, 0x2F, 0x0C, 0x56, +0xE0, 0xA2, 0xE9, 0x04, 0x84, 0x81, 0x48, 0xFF, +0xFF, 0x67, 0x3C, 0x13, 0xFD, 0x71, 0x97, 0x8B, +0x42, 0x4E, 0x18, 0x24, 0x40, 0x02, 0x0C, 0x16, +0x3C, 0x08, 0x07, 0xA8, 0xD5, 0xF4, 0x2E, 0x97, +0xF6, 0x01, 0x5A, 0x98, 0x01, 0x0B, 0x3C, 0x43, +0xFD, 0x6A, 0x3D, 0xE7, 0xFA, 0xEE, 0x96, 0x23, +0xE0, 0x1E, 0xE9, 0x04, 0xE5, 0x42, 0xE8, 0x02, +0xC7, 0x20, 0x3C, 0x73, 0xFD, 0x72, 0x3C, 0x23, +0xFE, 0x78, 0x96, 0xFB, 0x2E, 0x1F, 0xFA, 0x1F, +0x42, 0xA4, 0x0C, 0x24, 0x97, 0x90, 0x44, 0x80, +0x00, 0x64, 0x40, 0x55, 0x20, 0xB6, 0x87, 0x81, +0xE0, 0x26, 0x3C, 0x58, 0x07, 0xA9, 0x3F, 0xC7, +0xF5, 0xEC, 0xE9, 0x05, 0x3F, 0xC7, 0xF5, 0xEF, +0x87, 0x80, 0xD5, 0x05, 0x3C, 0x43, 0xFE, 0x1C, +0x97, 0x26, 0xC4, 0x03, 0x3F, 0xC7, 0xF5, 0xEE, +0xFC, 0xC1, 0xFC, 0x00, 0x3C, 0x03, 0xFA, 0xF7, +0x5A, 0x09, 0x01, 0x05, 0x84, 0x20, 0x3E, 0x17, +0xF5, 0xEE, 0x3C, 0x20, 0x07, 0x96, 0x54, 0x31, +0x00, 0x10, 0x97, 0x19, 0xCC, 0x05, 0x3E, 0x47, +0xF5, 0xF9, 0x3E, 0x47, 0xF5, 0xF8, 0x96, 0xAE, +0x97, 0x51, 0xCD, 0x09, 0x3E, 0x57, 0xF5, 0xED, +0x3E, 0x57, 0xF5, 0xEC, 0x3E, 0x57, 0xF5, 0xEF, +0x3E, 0x57, 0xF5, 0xEE, 0x3C, 0x60, 0x07, 0x97, +0x97, 0xA6, 0xC6, 0x0E, 0x3C, 0x00, 0x01, 0x2D, +0xE6, 0x02, 0xE9, 0x0A, 0x87, 0x80, 0x3F, 0xC7, +0xF5, 0xED, 0x3F, 0xC7, 0xF5, 0xEC, 0x3F, 0xC7, +0xF5, 0xEF, 0x3F, 0xC7, 0xF5, 0xEE, 0x3C, 0x13, +0xFE, 0x1C, 0x96, 0x66, 0x96, 0x89, 0xCA, 0x05, +0x3E, 0x27, 0xF5, 0xEF, 0x3E, 0x27, 0xF5, 0xEE, +0x3C, 0x63, 0xFA, 0xF6, 0x5A, 0x68, 0x01, 0x21, +0x3C, 0x43, 0xFE, 0x1C, 0x84, 0x60, 0x87, 0xC0, +0x97, 0x26, 0x3F, 0xE7, 0xF5, 0xF9, 0x3C, 0x3B, +0xFA, 0xF9, 0x3C, 0x38, 0x08, 0xD6, 0xC4, 0x04, +0x3E, 0x67, 0xF5, 0xED, 0xD5, 0x11, 0x2E, 0x07, +0xF5, 0xCF, 0xC0, 0x04, 0x49, 0xFF, 0xFC, 0xB7, +0xD5, 0x03, 0x49, 0x00, 0x05, 0x53, 0x3E, 0x67, +0xF5, 0xED, 0x3C, 0x63, 0xFA, 0xF8, 0x8C, 0xC1, +0x3C, 0x6B, 0xFA, 0xF8, 0xD5, 0x6B, 0x2E, 0x57, +0xF5, 0xED, 0xC5, 0x75, 0x3C, 0x03, 0xFE, 0x1C, +0x96, 0x26, 0xC8, 0x0E, 0x2E, 0x17, 0xF5, 0xEC, +0xC9, 0x0B, 0x2E, 0x27, 0xF5, 0xF8, 0xCA, 0x08, +0x2E, 0x6F, 0xFA, 0x1F, 0xCE, 0x05, 0x84, 0x00, +0x49, 0x00, 0x05, 0x38, 0xD5, 0x5A, 0x3C, 0x33, +0xFD, 0x54, 0x3C, 0x50, 0x05, 0x84, 0x97, 0x1B, +0xE0, 0xA4, 0xE8, 0x1F, 0x3C, 0x23, 0xFD, 0x7A, +0x3C, 0x10, 0x08, 0xD6, 0x97, 0x93, 0x9C, 0x09, +0xE0, 0xC1, 0x3C, 0x08, 0x08, 0xD6, 0xE8, 0x18, +0x84, 0x00, 0x49, 0x00, 0x05, 0x23, 0x3D, 0xC3, +0xF8, 0x33, 0x84, 0xA0, 0x3E, 0x38, 0x0F, 0x80, +0x3E, 0x48, 0x02, 0x80, 0xE0, 0xBC, 0xE8, 0x3D, +0x94, 0x69, 0x88, 0x24, 0x02, 0x00, 0x83, 0x10, +0x38, 0x01, 0x95, 0x09, 0x8C, 0xA1, 0xD5, 0xF7, +0x87, 0x80, 0x3D, 0xC8, 0x08, 0xD6, 0x3C, 0x33, +0xFE, 0x1C, 0x96, 0xE6, 0xCB, 0x30, 0x2E, 0x37, +0xF5, 0xEC, 0xCB, 0x1F, 0x2E, 0x47, 0xF5, 0xEE, +0xCC, 0x1C, 0x2E, 0x67, 0xF5, 0xF8, 0xCE, 0x19, +0x3C, 0x03, 0xFD, 0x74, 0x3C, 0x57, 0xFA, 0xF9, +0x96, 0x83, 0x9C, 0x69, 0xE0, 0xA2, 0x3C, 0x1B, +0xFA, 0xF9, 0xE9, 0x0A, 0x3E, 0x67, 0xF5, 0xED, +0x3E, 0x67, 0xF5, 0xF9, 0x3C, 0x6B, 0xFA, 0xF9, +0x3E, 0x67, 0xFA, 0x1E, 0xD5, 0x35, 0x49, 0xFF, +0xFC, 0x4E, 0x3E, 0x67, 0xF5, 0xF9, 0xD5, 0x9A, +0x87, 0x80, 0x3D, 0xCB, 0xFA, 0xF9, 0x3F, 0xC7, +0xF5, 0xF9, 0x49, 0xFF, 0xFC, 0x44, 0x3D, 0xCB, +0xFA, 0xF8, 0x87, 0xC7, 0x3F, 0xE7, 0xFA, 0x1E, +0x84, 0xC4, 0xD5, 0x22, 0x87, 0xC0, 0x84, 0x87, +0x3F, 0xE7, 0xF5, 0xF9, 0x3F, 0xE7, 0xF5, 0xF8, +0x3E, 0x47, 0xFA, 0x1E, 0x3C, 0x53, 0xFA, 0xFC, +0x5A, 0x58, 0x01, 0x04, 0x3E, 0x57, 0xF5, 0xF9, +0x2E, 0x17, 0xF5, 0xF9, 0xC1, 0x0A, 0x2E, 0x6F, +0xFA, 0x1F, 0xCE, 0x07, 0x2E, 0x27, 0xF5, 0xF8, +0x3C, 0x6B, 0xFA, 0xFD, 0xCA, 0x09, 0xD5, 0x88, +0x87, 0x80, 0x84, 0x00, 0x3F, 0xC7, 0xF5, 0xF9, +0x3C, 0x0B, 0xFA, 0xFD, 0x84, 0xC0, 0x80, 0x06, +0xFC, 0x80, 0x3C, 0x00, 0x07, 0x96, 0x96, 0x3E, +0x96, 0x01, 0xC0, 0x69, 0xFC, 0x40, 0x3C, 0x13, +0xFD, 0x77, 0x3C, 0x34, 0x05, 0x87, 0x96, 0x8B, +0x3C, 0x63, 0xFD, 0x78, 0xE0, 0x43, 0x97, 0xB3, +0x3F, 0xC8, 0x02, 0x80, 0xE9, 0x58, 0x3C, 0x47, +0xF9, 0x9C, 0xE0, 0x44, 0xE9, 0x54, 0x3C, 0x03, +0xFD, 0x76, 0x3C, 0x70, 0x05, 0x89, 0x3C, 0x53, +0xF8, 0x35, 0x96, 0x43, 0xFE, 0x5C, 0x45, 0xE0, +0x00, 0x64, 0x40, 0x83, 0x95, 0x57, 0x40, 0x20, +0xF8, 0x56, 0x84, 0x80, 0x40, 0x94, 0x00, 0x13, +0x96, 0x53, 0x40, 0xA5, 0x00, 0x13, 0x3C, 0x83, +0xF8, 0x33, 0x80, 0x44, 0xE0, 0x48, 0xE8, 0x21, +0x4C, 0x71, 0x00, 0x1E, 0x94, 0x11, 0x88, 0x1C, +0x22, 0x00, 0x03, 0x10, 0xE0, 0x01, 0xE9, 0x17, +0x40, 0xF1, 0x17, 0xD6, 0x40, 0x05, 0x78, 0x01, +0x3D, 0xE3, 0xFD, 0x79, 0x40, 0xF4, 0xBC, 0x01, +0x41, 0xEF, 0x00, 0x11, 0x4E, 0xF4, 0x00, 0x04, +0x52, 0xF7, 0x80, 0x00, 0x4E, 0x04, 0x00, 0x03, +0xFE, 0x02, 0x88, 0x0F, 0x40, 0xFF, 0x00, 0x07, +0xE8, 0x02, 0x8C, 0x81, 0x8C, 0x41, 0xD5, 0xDF, +0xE0, 0x86, 0xE9, 0x19, 0x3C, 0x73, 0xFD, 0x71, +0x3C, 0x90, 0x07, 0xA8, 0x97, 0xBB, 0xFF, 0x9C, +0x45, 0xC0, 0x00, 0x64, 0x40, 0x43, 0x70, 0x96, +0x84, 0xA1, 0xE1, 0x24, 0x3E, 0x57, 0xF5, 0xF9, +0xE8, 0x0C, 0x3C, 0x03, 0xFD, 0x71, 0x96, 0x43, +0xFE, 0xCC, 0x40, 0x31, 0xF0, 0x76, 0x3C, 0x38, +0x07, 0xA8, 0xD5, 0x03, 0x84, 0x00, 0xD5, 0x02, +0x84, 0x01, 0xFC, 0xC0, 0xDD, 0x9E, 0x3C, 0x10, +0x07, 0x96, 0x3C, 0x00, 0x07, 0xAA, 0x54, 0x20, +0xC0, 0x00, 0xCA, 0x12, 0x3C, 0x37, 0xF9, 0x91, +0x44, 0x40, 0x00, 0x64, 0xFE, 0x1C, 0x40, 0x50, +0x10, 0xB6, 0x3C, 0x00, 0x07, 0x9B, 0xE0, 0x05, +0xE8, 0x03, 0x3C, 0x58, 0x07, 0x9B, 0x2E, 0x1F, +0xFA, 0x1F, 0x4E, 0x16, 0x00, 0x03, 0xDD, 0x9E, +0xFC, 0x40, 0x84, 0xC0, 0x3E, 0x7F, 0xF6, 0x04, +0x44, 0x90, 0x00, 0x38, 0x2E, 0x8F, 0xFA, 0x1F, +0xE0, 0xC8, 0xE8, 0x12, 0x83, 0x87, 0x43, 0xC3, +0x24, 0x73, 0x3C, 0xA0, 0x07, 0x9B, 0xBA, 0x05, +0xE0, 0x4A, 0xE8, 0x08, 0x80, 0x06, 0x49, 0x00, +0x05, 0x30, 0x2E, 0x3F, 0xFA, 0x1F, 0x8E, 0xC1, +0xC3, 0x03, 0x8C, 0xC1, 0xD5, 0xEC, 0xFC, 0xC0, +0x3C, 0x00, 0x07, 0x97, 0x96, 0x16, 0x96, 0x01, +0xC8, 0x08, 0x2E, 0x3F, 0xFA, 0x1F, 0xE4, 0x62, +0xE9, 0x04, 0x3C, 0x10, 0x07, 0xAB, 0xC9, 0x02, +0xDD, 0x9E, 0xFC, 0x40, 0x3E, 0x5F, 0xF6, 0x04, +0x80, 0x40, 0x44, 0x60, 0x00, 0x38, 0xE0, 0x43, +0xE8, 0x0A, 0x80, 0xE5, 0x42, 0x71, 0x18, 0x73, +0xA5, 0x3C, 0x8C, 0x41, 0xE0, 0x04, 0x40, 0x02, +0x3C, 0x1B, 0xD5, 0xF6, 0xFE, 0x0C, 0x44, 0x70, +0x00, 0x64, 0x41, 0xC0, 0x1F, 0x96, 0x85, 0x00, +0x3E, 0x9F, 0xF6, 0x04, 0x44, 0xA0, 0x00, 0x38, +0x2E, 0x4F, 0xFA, 0x1F, 0xE1, 0x04, 0xE8, 0x12, +0x83, 0xC9, 0x43, 0xE4, 0x28, 0x73, 0x02, 0x0F, +0x00, 0x04, 0xE0, 0x1C, 0xE8, 0x09, 0x80, 0x08, +0x49, 0x00, 0x04, 0xF3, 0x2E, 0x3F, 0xFA, 0x1F, +0x8F, 0x01, 0x5A, 0x30, 0x01, 0x04, 0x8D, 0x01, +0xD5, 0xEC, 0xFC, 0xC0, 0x96, 0x03, 0x3C, 0x50, +0x06, 0xA1, 0x3C, 0x33, 0xF8, 0x3F, 0x84, 0x40, +0x9C, 0x41, 0x3C, 0x08, 0x08, 0xD0, 0x3C, 0x1F, +0xFD, 0x56, 0x3C, 0x2B, 0xFA, 0xAE, 0x3E, 0x20, +0x11, 0xB3, 0xDB, 0x09, 0x3C, 0x50, 0x06, 0xA2, +0x3C, 0x43, 0xF8, 0x41, 0xDC, 0x04, 0x2E, 0x10, +0x0D, 0x46, 0xC9, 0x09, 0x85, 0xE0, 0x84, 0x40, +0x3E, 0xF0, 0x0D, 0x46, 0x3C, 0x28, 0x06, 0xA1, +0x3C, 0x08, 0x06, 0xA4, 0xDD, 0x9E, 0xFC, 0x20, +0x3C, 0x14, 0x06, 0xA4, 0x3E, 0x38, 0x0B, 0x20, +0xC9, 0x07, 0x2E, 0x00, 0x0D, 0x46, 0x5A, 0x08, +0x01, 0x04, 0x48, 0x00, 0x00, 0x9E, 0x3C, 0x23, +0xF8, 0x3F, 0x3C, 0x50, 0x06, 0xA1, 0xDA, 0x07, +0x3C, 0x50, 0x06, 0xA2, 0x3C, 0x43, 0xF8, 0x41, +0x4C, 0x52, 0x00, 0x93, 0x3C, 0x63, 0xFD, 0x44, +0x97, 0x86, 0xCE, 0x15, 0x3C, 0x53, 0xF8, 0x41, +0x3C, 0x73, 0xF8, 0x40, 0xDF, 0x10, 0x3C, 0x53, +0xF8, 0x3E, 0xDA, 0x0D, 0x3C, 0x03, 0xFE, 0x2C, +0x97, 0x03, 0x4C, 0x22, 0x40, 0x09, 0x2E, 0x60, +0x0D, 0x41, 0xCE, 0x05, 0x84, 0x01, 0x49, 0x00, +0x0D, 0xEC, 0xD5, 0x2E, 0x3C, 0x60, 0x07, 0x97, +0x97, 0xAE, 0xC6, 0x08, 0x3C, 0x73, 0xFE, 0x28, +0x3C, 0x80, 0x05, 0x82, 0x97, 0x7B, 0xE0, 0xA8, +0xE9, 0x6F, 0x3C, 0x63, 0xFD, 0x44, 0x97, 0x86, +0xCE, 0x22, 0x3C, 0x03, 0xFE, 0x4A, 0x96, 0x0E, +0xC0, 0x1E, 0x3C, 0x43, 0xFE, 0x94, 0x97, 0xE3, +0x4C, 0x23, 0xC0, 0x1A, 0x3C, 0x50, 0x00, 0x1F, +0x3C, 0x03, 0xFE, 0x95, 0x97, 0x2B, 0x97, 0xC3, +0x3C, 0x53, 0xF8, 0x41, 0x88, 0xE4, 0xDF, 0x0F, +0x80, 0x06, 0x49, 0x00, 0x0D, 0xC6, 0x3C, 0x13, +0xF8, 0x3F, 0x3C, 0x23, 0xF8, 0x41, 0x3C, 0x18, +0x06, 0xA1, 0x3C, 0x28, 0x06, 0xA2, 0x3C, 0x68, +0x06, 0xA4, 0xD5, 0x4A, 0x3C, 0x63, 0xFD, 0x54, +0x3D, 0xC0, 0x05, 0x84, 0x96, 0x33, 0xE0, 0x1C, +0xE9, 0x43, 0x3D, 0xE3, 0xF8, 0x33, 0x85, 0x00, +0x3E, 0x08, 0x02, 0x80, 0xE1, 0x1E, 0xE8, 0x07, +0x38, 0xF0, 0x21, 0x01, 0x38, 0xF1, 0xA1, 0x09, +0x8D, 0x01, 0xD5, 0xF9, 0x3C, 0x43, 0xFD, 0x44, +0x97, 0x06, 0xCC, 0x14, 0x8E, 0x21, 0x96, 0x4B, +0x3C, 0x18, 0x06, 0xA4, 0x4E, 0x16, 0x00, 0x2D, +0x3C, 0x28, 0x06, 0xA1, 0x3C, 0x23, 0xF8, 0x41, +0x84, 0x60, 0x87, 0xC1, 0x3C, 0x28, 0x06, 0xA2, +0x3C, 0x38, 0x06, 0xA4, 0x3F, 0xE0, 0x0D, 0x46, +0xD5, 0x1F, 0x3C, 0x73, 0xF8, 0x36, 0x84, 0xA0, +0x94, 0x39, 0x3E, 0x48, 0x02, 0x80, 0xD0, 0x09, +0x99, 0xEC, 0x99, 0x9D, 0x03, 0xC3, 0x82, 0xD0, +0x8C, 0xA2, 0x13, 0xC3, 0x00, 0xF0, 0xD5, 0xF8, +0x3C, 0x53, 0xF8, 0x37, 0x3E, 0x48, 0x02, 0x80, +0x94, 0x29, 0x84, 0xA0, 0xD0, 0xD8, 0x99, 0xEC, +0x99, 0x9D, 0x02, 0x83, 0x82, 0xE4, 0x8C, 0xA2, +0x12, 0x83, 0x01, 0x04, 0xD5, 0xF8, 0xFC, 0xA0, +0x3C, 0x10, 0x08, 0xD3, 0x3C, 0x23, 0xFD, 0x56, +0x8C, 0x21, 0x96, 0xC9, 0x97, 0x13, 0xE0, 0x83, +0x3C, 0x38, 0x08, 0xD3, 0xE9, 0x03, 0x5A, 0x08, +0x01, 0x27, 0x3C, 0x03, 0xF8, 0x36, 0x84, 0xA0, +0x94, 0x01, 0x3E, 0x18, 0x0F, 0x80, 0x3E, 0x28, +0x02, 0x80, 0xD0, 0x09, 0x99, 0x2A, 0x98, 0xE9, +0x02, 0xF2, 0x04, 0x00, 0x8C, 0xA2, 0x12, 0xF1, +0x80, 0xF0, 0xD5, 0xF8, 0x3C, 0x53, 0xF8, 0x37, +0x3E, 0x18, 0x0F, 0x80, 0x94, 0x29, 0x3E, 0x28, +0x02, 0x80, 0x84, 0xA0, 0xD0, 0x09, 0x99, 0x2A, +0x98, 0xE9, 0x02, 0xF2, 0x04, 0x14, 0x8C, 0xA2, +0x12, 0xF1, 0x81, 0x04, 0xD5, 0xF8, 0x84, 0x20, +0x3C, 0x18, 0x08, 0xD3, 0xDD, 0x9E, 0xFC, 0x00, +0x80, 0xC0, 0x3C, 0x04, 0x08, 0xD0, 0xC8, 0x07, +0x3C, 0x10, 0x07, 0x96, 0x96, 0x46, 0xC1, 0x03, +0x84, 0x00, 0xD5, 0x4E, 0x3C, 0x23, 0xFD, 0x54, +0x3C, 0x40, 0x05, 0x84, 0x96, 0xD3, 0xE0, 0x64, +0xE9, 0x07, 0x2E, 0x57, 0xFA, 0x1E, 0xCD, 0x04, +0x2E, 0x07, 0xFA, 0x28, 0xC8, 0x07, 0xCE, 0x06, +0x3C, 0x68, 0x08, 0xD1, 0x3C, 0x68, 0x08, 0xD3, +0xD5, 0xEC, 0x80, 0x06, 0x49, 0xFF, 0xFF, 0xAE, +0x3C, 0x10, 0x07, 0x96, 0x96, 0x66, 0xC1, 0x08, +0x2E, 0x27, 0xF5, 0xF9, 0xC2, 0x05, 0xCE, 0x04, +0x3C, 0x68, 0x08, 0xD1, 0xD5, 0xDE, 0x3C, 0x30, +0x08, 0xD1, 0x3C, 0x53, 0xFD, 0x56, 0x8C, 0x61, +0x97, 0x19, 0x96, 0x2B, 0xE0, 0x04, 0x3C, 0x48, +0x08, 0xD1, 0xE9, 0x03, 0x5A, 0x68, 0x01, 0xD2, +0x3C, 0x23, 0xF8, 0x33, 0x84, 0xA0, 0x3E, 0x38, +0x0F, 0x80, 0x3E, 0x48, 0x02, 0x80, 0xE0, 0xA2, +0xE8, 0x09, 0x94, 0x69, 0x88, 0x24, 0x02, 0x00, +0x83, 0x10, 0x38, 0x01, 0x95, 0x09, 0x8C, 0xA1, +0xD5, 0xF7, 0x3C, 0x64, 0x08, 0xD0, 0x87, 0x80, +0x3D, 0xC8, 0x08, 0xD1, 0x4E, 0x67, 0x00, 0x05, +0x8E, 0xC1, 0x3C, 0x68, 0x08, 0xD0, 0x87, 0xC1, +0x3F, 0xE0, 0x11, 0xB3, 0x84, 0x01, 0xFC, 0x80, +0x3C, 0x33, 0xFD, 0x59, 0x22, 0x20, 0x00, 0x00, +0x97, 0x1B, 0x88, 0x81, 0xE0, 0x82, 0xE8, 0x05, +0x3C, 0x53, 0xFD, 0x59, 0x8A, 0x45, 0xD5, 0x12, +0x3C, 0x53, 0xFD, 0x59, 0x96, 0xEB, 0x9B, 0x0B, +0xE0, 0x44, 0xE8, 0x05, 0x3C, 0x13, 0xFD, 0x59, +0x88, 0x41, 0xD5, 0x08, 0xE0, 0x22, 0xE8, 0x03, +0x8E, 0x41, 0xD5, 0x04, 0xE0, 0x41, 0xE8, 0x03, +0x8C, 0x41, 0xAC, 0x80, 0xDD, 0x9E, 0x3C, 0x00, +0x08, 0xD4, 0x3C, 0x13, 0xFD, 0x57, 0x8C, 0x01, +0x96, 0x81, 0x96, 0xCB, 0xE0, 0x62, 0x3C, 0x28, +0x08, 0xD4, 0xE9, 0x02, 0xDD, 0x9E, 0xFC, 0x40, +0x84, 0xC0, 0x3E, 0x78, 0x0F, 0x80, 0x3E, 0x98, +0x02, 0x80, 0x3C, 0x43, 0xF8, 0x36, 0xE0, 0xC4, +0xE8, 0x0D, 0x50, 0x23, 0x00, 0xF0, 0x94, 0x11, +0x40, 0x14, 0x80, 0x00, 0x88, 0x07, 0x22, 0x10, +0x83, 0x10, 0x49, 0xFF, 0xFF, 0xC3, 0x8C, 0xC1, +0xD5, 0xF1, 0x85, 0x40, 0x3E, 0x68, 0x0F, 0x80, +0x3F, 0xC8, 0x02, 0x80, 0x3C, 0x53, 0xF8, 0x37, +0xE1, 0x45, 0xE8, 0x0D, 0x50, 0x05, 0x01, 0x04, +0x94, 0x01, 0x41, 0xEE, 0x00, 0x00, 0x88, 0x06, +0x22, 0x1F, 0x03, 0x10, 0x49, 0xFF, 0xFF, 0xAE, +0x8D, 0x41, 0xD5, 0xF1, 0x85, 0x00, 0x3C, 0x88, +0x08, 0xD4, 0xFC, 0xC0, 0xFC, 0x40, 0x3C, 0x00, +0x07, 0x96, 0x96, 0x0E, 0xC8, 0x10, 0x3C, 0x13, +0xFD, 0x55, 0x3C, 0x30, 0x05, 0x84, 0x96, 0x8B, +0xE0, 0x43, 0xE9, 0x04, 0x2E, 0x67, 0xFA, 0x1E, +0xC6, 0x08, 0x87, 0xC0, 0x3D, 0xE8, 0x08, 0xD4, +0x3D, 0xE8, 0x08, 0xD2, 0x84, 0xC0, 0xD5, 0x2F, +0x49, 0xFF, 0xFF, 0xAF, 0x3C, 0x40, 0x07, 0x96, +0x97, 0x26, 0xC4, 0x07, 0x2E, 0x57, 0xF5, 0xF9, +0xC5, 0x04, 0x3C, 0x68, 0x08, 0xD2, 0xD5, 0x23, +0x3C, 0x60, 0x08, 0xD2, 0x3C, 0x03, 0xFD, 0x57, +0x8C, 0xC1, 0x97, 0xF1, 0x96, 0x43, 0xE0, 0x27, +0x3C, 0x78, 0x08, 0xD2, 0xE8, 0xE8, 0x84, 0xC0, +0x3E, 0x78, 0x0F, 0x80, 0x3E, 0x98, 0x02, 0x80, +0x3C, 0x83, 0xF8, 0x33, 0xE0, 0xC8, 0xE8, 0x0B, +0x94, 0x31, 0x41, 0xC0, 0x24, 0x00, 0x88, 0x07, +0x22, 0x1E, 0x03, 0x10, 0x49, 0xFF, 0xFF, 0x6A, +0x8C, 0xC1, 0xD5, 0xF3, 0x85, 0x40, 0x3C, 0xA8, +0x08, 0xD2, 0x84, 0xC1, 0x80, 0x06, 0xFC, 0xC0, +0x3C, 0x03, 0xFD, 0x4C, 0x3C, 0x13, 0xFD, 0x67, +0x3C, 0x33, 0xFD, 0x4D, 0x3C, 0x43, 0xFD, 0x4F, +0x84, 0x40, 0x3C, 0x53, 0xFD, 0x4E, 0x3E, 0x10, +0x0F, 0x58, 0x3C, 0xF3, 0xFD, 0x50, 0x3E, 0x20, +0x0F, 0x59, 0x3C, 0x13, 0xFD, 0xDE, 0x3C, 0x38, +0x07, 0xA2, 0x3C, 0x23, 0xFD, 0x6F, 0x3C, 0x48, +0x07, 0xA3, 0x3C, 0x33, 0xFD, 0x70, 0x3C, 0x43, +0xFD, 0xDD, 0x3C, 0x58, 0x07, 0xA4, 0x3C, 0xF8, +0x07, 0xA5, 0x3C, 0x53, 0xFD, 0xEF, 0x3C, 0x18, +0x07, 0xA6, 0x3C, 0xF3, 0xFD, 0x5F, 0x3C, 0x28, +0x07, 0xAA, 0x3C, 0x13, 0xFD, 0x63, 0x3C, 0x38, +0x07, 0xAB, 0x3C, 0x23, 0xFE, 0xEB, 0x3E, 0x40, +0x0F, 0x5A, 0x3C, 0x33, 0xFD, 0x60, 0x3C, 0x43, +0xFD, 0xF2, 0x3C, 0x08, 0x07, 0xA1, 0x3C, 0xF8, +0x07, 0xAF, 0x3C, 0x18, 0x07, 0xB0, 0x3C, 0x28, +0x07, 0xB1, 0x3C, 0x38, 0x07, 0xB2, 0x3C, 0x48, +0x07, 0xB3, 0x3C, 0x58, 0x07, 0xAE, 0x3C, 0x53, +0xFD, 0x5C, 0x3C, 0xF3, 0xFD, 0x6E, 0x3C, 0x13, +0xFE, 0x79, 0x3C, 0x08, 0x07, 0xBA, 0x3C, 0x23, +0xFD, 0x68, 0x3C, 0x18, 0x07, 0xB5, 0x3C, 0x33, +0xFF, 0x8C, 0x3C, 0x08, 0x07, 0xBB, 0x3C, 0x43, +0xFD, 0x69, 0x3C, 0x08, 0x07, 0xBC, 0x2E, 0x17, +0xEA, 0x79, 0x3C, 0x03, 0xFD, 0x41, 0x3C, 0x58, +0x07, 0xB4, 0x84, 0xA0, 0x3C, 0xF8, 0x07, 0xBF, +0x3C, 0x28, 0x07, 0xB6, 0x3C, 0x38, 0x07, 0xB7, +0x3C, 0x48, 0x07, 0xB9, 0x3C, 0x58, 0x07, 0xBD, +0x3C, 0x08, 0x07, 0xBE, 0x5A, 0x18, 0x02, 0x11, +0x3C, 0xF3, 0xFE, 0x6F, 0x3C, 0x58, 0x07, 0xB7, +0x3C, 0xF8, 0x07, 0xA5, 0x3C, 0xF8, 0x07, 0xA4, +0x3C, 0xF8, 0x07, 0xA3, 0x3C, 0xF8, 0x07, 0xA2, +0x3C, 0xF8, 0x07, 0xA1, 0xDD, 0x9E, 0xC9, 0x03, +0x3C, 0x1B, 0xFE, 0x52, 0xDD, 0x9E, 0x3E, 0x0F, +0xFA, 0x58, 0x02, 0x10, 0x00, 0x20, 0x02, 0x20, +0x01, 0x0F, 0x02, 0x30, 0x00, 0x21, 0x88, 0x41, +0x02, 0x40, 0x01, 0x0F, 0x3E, 0x18, 0x0F, 0x2C, +0x97, 0x51, 0x02, 0x20, 0x00, 0x23, 0x02, 0xF0, +0x01, 0x0F, 0x88, 0x64, 0x88, 0x4F, 0x12, 0x30, +0x80, 0x0C, 0x02, 0x30, 0x00, 0x22, 0x02, 0x40, +0x01, 0x0F, 0x12, 0x20, 0x80, 0x0D, 0x02, 0x20, +0x00, 0x24, 0x02, 0xF0, 0x01, 0x0F, 0x88, 0x64, +0x88, 0x4F, 0x12, 0x30, 0x80, 0x0E, 0x02, 0x30, +0x00, 0xB2, 0x02, 0x40, 0x01, 0x0F, 0x12, 0x20, +0x80, 0x0F, 0x02, 0x20, 0x00, 0x42, 0x88, 0x64, +0x12, 0x50, 0x80, 0x0B, 0x12, 0x30, 0x80, 0x10, +0x12, 0x20, 0x80, 0x29, 0x12, 0x50, 0x80, 0x24, +0x12, 0x50, 0x80, 0x25, 0x12, 0x50, 0x80, 0x26, +0x02, 0x50, 0x01, 0x14, 0x02, 0xF0, 0x01, 0x6B, +0x02, 0x30, 0x00, 0xC3, 0x02, 0x40, 0x01, 0x12, +0x02, 0x20, 0x01, 0x13, 0x12, 0xF0, 0x80, 0x15, +0x12, 0x30, 0x80, 0x18, 0x12, 0x40, 0x80, 0x19, +0x12, 0x20, 0x80, 0x1A, 0x12, 0x50, 0x80, 0x14, +0x02, 0x50, 0x01, 0xF3, 0x02, 0xF0, 0x00, 0x34, +0x12, 0x50, 0x80, 0x1B, 0x02, 0x30, 0x01, 0xFF, +0x02, 0x40, 0x01, 0xFE, 0x02, 0x50, 0x00, 0x3D, +0x02, 0x00, 0x00, 0x15, 0x12, 0xF0, 0x80, 0x1C, +0x84, 0x40, 0x85, 0xE0, 0x12, 0x30, 0x80, 0x1F, +0x12, 0x40, 0x80, 0x20, 0x10, 0x20, 0x80, 0x2D, +0x12, 0x50, 0x80, 0x23, 0x12, 0xF0, 0x80, 0x27, +0x12, 0x00, 0x80, 0x28, 0xDD, 0x9E, 0xFC, 0x00, +0x3C, 0x00, 0x01, 0x2D, 0xC8, 0x04, 0x49, 0xFF, +0xFF, 0x11, 0xD5, 0x03, 0x49, 0xFF, 0xFF, 0x8D, +0x3C, 0x13, 0xFD, 0x43, 0x3C, 0x23, 0xFE, 0x49, +0x3C, 0x33, 0xFE, 0x4A, 0x3C, 0x43, 0xFE, 0x4B, +0x3C, 0x18, 0x07, 0x96, 0x3C, 0x28, 0x07, 0x97, +0x3C, 0x38, 0x07, 0x98, 0x3C, 0x48, 0x07, 0x99, +0xFC, 0x80, 0x3E, 0x0F, 0xF0, 0x60, 0x84, 0x20, +0xA4, 0x84, 0xA5, 0x05, 0x9E, 0xD1, 0x12, 0x10, +0x00, 0x0A, 0x12, 0x10, 0x00, 0x0C, 0x9F, 0x61, +0x94, 0x57, 0x94, 0xA7, 0x12, 0x30, 0x00, 0x0B, +0x12, 0x50, 0x00, 0x0D, 0x12, 0x10, 0x00, 0x08, +0x12, 0x20, 0x00, 0x09, 0xDD, 0x9E, 0xFC, 0x40, +0x84, 0x20, 0x81, 0x40, 0x3E, 0x9F, 0xEA, 0xC0, +0x84, 0x00, 0x84, 0xE0, 0x3E, 0x00, 0x0F, 0x5B, +0x3E, 0x07, 0xFA, 0x18, 0x3C, 0x1B, 0xFA, 0xFF, +0x80, 0xC7, 0x50, 0x84, 0x80, 0x08, 0x45, 0xC0, +0x00, 0x60, 0x3C, 0x24, 0x08, 0xD0, 0xCA, 0x18, +0x2E, 0x30, 0x11, 0xB2, 0xCB, 0x15, 0x80, 0x88, +0x42, 0x43, 0xF0, 0x73, 0xA1, 0x61, 0x54, 0x02, +0x80, 0x01, 0xC0, 0x0E, 0x92, 0xA6, 0x97, 0x46, +0xC5, 0x0B, 0x02, 0x14, 0x80, 0x2E, 0x8E, 0x21, +0x96, 0x8B, 0x12, 0x24, 0x80, 0x2E, 0x4E, 0x27, +0x00, 0x04, 0x8C, 0xC1, 0xD5, 0x04, 0x80, 0x07, +0x49, 0xFF, 0xE3, 0x41, 0x8C, 0xE1, 0x50, 0x94, +0x80, 0x60, 0x5A, 0x78, 0x0F, 0xE0, 0xC6, 0x07, +0x49, 0xFF, 0xE4, 0xEE, 0x3E, 0x67, 0xFA, 0x22, +0x80, 0x06, 0xD5, 0x16, 0x3E, 0x67, 0xFA, 0x21, +0x3E, 0x67, 0xFA, 0x20, 0x3E, 0x67, 0xFA, 0x1F, +0x3E, 0x67, 0xFA, 0x22, 0x3E, 0x67, 0xFA, 0x18, +0x3C, 0x68, 0x01, 0x31, 0x3C, 0x6B, 0xFA, 0xF8, +0x4E, 0xA3, 0x00, 0x04, 0x3E, 0xA7, 0xFA, 0x1E, +0x49, 0xFF, 0xE4, 0xD6, 0x84, 0x00, 0xFC, 0xC0, +0xFC, 0x00, 0x3C, 0x03, 0xFD, 0x43, 0x3C, 0x13, +0xFE, 0x49, 0x3C, 0x23, 0xFE, 0x4A, 0x3C, 0x33, +0xFE, 0x4B, 0x3C, 0x08, 0x07, 0x96, 0x3C, 0x18, +0x07, 0x97, 0x3C, 0x28, 0x07, 0x98, 0x3C, 0x38, +0x07, 0x99, 0x84, 0xC0, 0x80, 0x06, 0x8C, 0xC1, +0x49, 0xFF, 0xE3, 0x09, 0x5A, 0x68, 0x0F, 0xFC, +0x3C, 0x43, 0xFD, 0x8C, 0x3C, 0x53, 0xFD, 0x8D, +0x3C, 0x4B, 0xF8, 0x36, 0x3C, 0x4B, 0xF8, 0x34, +0x87, 0x80, 0xFF, 0x2C, 0x3C, 0x5B, 0xF8, 0x37, +0x3C, 0x5B, 0xF8, 0x35, 0x3C, 0x4B, 0xF8, 0x33, +0x49, 0xFF, 0xFF, 0x71, 0x3D, 0xCB, 0xFA, 0xC6, +0x49, 0xFF, 0xFE, 0x68, 0x84, 0x00, 0x49, 0xFF, +0xFF, 0x80, 0x3C, 0x03, 0xFD, 0xA7, 0x3D, 0xCB, +0xFA, 0xE9, 0x96, 0x43, 0x4E, 0x16, 0x00, 0x04, +0x84, 0x01, 0xD5, 0x04, 0x3C, 0x23, 0xFD, 0xA7, +0x96, 0x13, 0x84, 0xC0, 0x49, 0xFF, 0xFC, 0x60, +0x3E, 0x60, 0x11, 0xB2, 0x49, 0xFF, 0xF9, 0x47, +0x3C, 0x53, 0xFE, 0x1D, 0x3C, 0x03, 0xFD, 0xFE, +0x84, 0x60, 0x84, 0x81, 0x3C, 0x3B, 0xFA, 0xF3, +0x3E, 0x47, 0xFA, 0x28, 0x3C, 0x3B, 0xFD, 0x13, +0x3C, 0x38, 0x01, 0x2D, 0x3C, 0x38, 0x01, 0x2E, +0x3E, 0x30, 0x02, 0x70, 0x3C, 0x3B, 0xF5, 0x3B, +0x3E, 0x67, 0xF5, 0xCF, 0x3E, 0x67, 0xF5, 0xD0, +0x3C, 0x38, 0x01, 0x32, 0x3C, 0x58, 0x07, 0x9A, +0xC8, 0x06, 0x3C, 0x13, 0xFD, 0xEC, 0x96, 0x8B, +0x3C, 0x2B, 0xFD, 0xFE, 0x3C, 0x63, 0xFD, 0x9D, +0xCE, 0x06, 0x3C, 0x33, 0xFD, 0xED, 0x97, 0x1B, +0x3C, 0x4B, 0xFD, 0x9D, 0x85, 0xE0, 0x3C, 0xF8, +0x01, 0x37, 0x3C, 0xFB, 0xFE, 0x52, 0xFC, 0x80, +0xFC, 0x40, 0x81, 0x20, 0x49, 0xFF, 0xF7, 0x5F, +0x3C, 0x04, 0x07, 0x99, 0x4E, 0x04, 0x00, 0xA7, +0x4E, 0x93, 0x00, 0xA5, 0x2E, 0x17, 0xF5, 0xED, +0xC1, 0x06, 0x3C, 0x23, 0xFE, 0x1C, 0x96, 0xA6, +0x4E, 0x22, 0x00, 0x9D, 0x3E, 0x6F, 0xEA, 0xF4, +0x84, 0xE0, 0x83, 0x87, 0x50, 0xA3, 0x7F, 0xFC, +0x50, 0x83, 0x00, 0x14, 0x44, 0x30, 0x00, 0x60, +0x3E, 0x4F, 0xEA, 0xC8, 0x42, 0x4E, 0x0C, 0x73, +0xA1, 0x61, 0x54, 0x02, 0x80, 0x01, 0xC0, 0x50, +0x92, 0xA6, 0x97, 0x46, 0xC5, 0x4D, 0x84, 0x21, +0x54, 0x0E, 0x00, 0xFF, 0x49, 0xFF, 0xF3, 0x7A, +0xB4, 0x26, 0xA0, 0xF6, 0x44, 0x20, 0x00, 0x64, +0x40, 0x50, 0x88, 0xB6, 0x40, 0x41, 0x88, 0x96, +0xDC, 0x0E, 0xA0, 0x31, 0xA0, 0x77, 0x40, 0x50, +0x08, 0xB6, 0x40, 0x20, 0x88, 0x56, 0xDA, 0x07, +0x2E, 0x47, 0xFA, 0x1F, 0x8C, 0x81, 0x3E, 0x47, +0xFA, 0x1F, 0xD5, 0x32, 0x44, 0xF0, 0x00, 0x60, +0x42, 0x3E, 0x3C, 0x24, 0x40, 0x45, 0x0C, 0x00, +0x88, 0x68, 0x8C, 0x84, 0x8C, 0x64, 0x3A, 0x02, +0x04, 0x00, 0x3A, 0x21, 0x8C, 0x00, 0x49, 0xFF, +0xD1, 0x74, 0x3C, 0x13, 0xFD, 0x90, 0xA1, 0x73, +0x96, 0x8B, 0xE0, 0xA2, 0x2E, 0x47, 0xFA, 0x1F, +0xE9, 0x10, 0x3C, 0x33, 0xFD, 0x90, 0x96, 0x5B, +0xE0, 0x01, 0xE9, 0x0B, 0x3C, 0x23, 0xFE, 0xA4, +0x45, 0xE0, 0x00, 0x64, 0x96, 0xD3, 0xFE, 0xEC, +0x40, 0x51, 0xF8, 0xB6, 0xE0, 0xA0, 0xE9, 0x08, +0x50, 0x03, 0x00, 0x18, 0x3A, 0x23, 0x0C, 0x00, +0x3A, 0x20, 0x0C, 0x20, 0xD5, 0x02, 0x84, 0xE1, +0x8C, 0x81, 0x3E, 0x47, 0xFA, 0x1F, 0x51, 0xCE, +0x00, 0x01, 0x50, 0x63, 0x00, 0x60, 0x5B, 0xC8, +0x0F, 0xA3, 0x2E, 0xAF, 0xFA, 0x1F, 0x4E, 0xA7, +0x00, 0x32, 0x5A, 0x78, 0x01, 0x30, 0x49, 0xFF, +0xE3, 0xDF, 0x2E, 0x67, 0xF5, 0xED, 0xC6, 0x16, +0x3C, 0x13, 0xFE, 0x1C, 0x96, 0x66, 0xC1, 0x12, +0x2E, 0x27, 0xF5, 0xEF, 0xC2, 0x06, 0x3C, 0x53, +0xFA, 0xC6, 0x58, 0x82, 0x80, 0x20, 0xD5, 0x08, +0x2E, 0x37, 0xF5, 0xEE, 0xC3, 0x07, 0x3C, 0xA3, +0xFA, 0xC6, 0x58, 0x85, 0x00, 0x10, 0x3C, 0x8B, +0xFA, 0xC6, 0x85, 0x20, 0x80, 0x09, 0x8D, 0x21, +0x49, 0xFF, 0xE2, 0x0D, 0x5A, 0x98, 0x0F, 0xFC, +0x87, 0xC0, 0x85, 0xE0, 0x3F, 0xE7, 0xFA, 0x22, +0x3F, 0xE7, 0xFA, 0x1F, 0x3F, 0xE7, 0xFA, 0x1E, +0x3C, 0xF8, 0x01, 0x31, 0x3C, 0xFB, 0xFA, 0xF8, +0xD5, 0x34, 0x85, 0x00, 0x80, 0x09, 0x3E, 0x87, +0xFA, 0x1F, 0x49, 0xFF, 0xFE, 0x8A, 0x4E, 0x06, +0x00, 0x2D, 0x49, 0xFF, 0xFD, 0x25, 0x2E, 0x17, +0xF5, 0xED, 0x3E, 0x87, 0xFA, 0x22, 0xC1, 0x1D, +0x3C, 0x23, 0xFE, 0x1C, 0x96, 0xA6, 0xC2, 0x12, +0x2E, 0x37, 0xF5, 0xEF, 0xC3, 0x06, 0x3C, 0xF3, +0xFA, 0xC6, 0x59, 0xE7, 0x80, 0x20, 0xD5, 0x08, +0x2E, 0x57, 0xF5, 0xEE, 0xC5, 0x07, 0x3C, 0x93, +0xFA, 0xC6, 0x59, 0xE4, 0x80, 0x10, 0x3D, 0xEB, +0xFA, 0xC6, 0x3C, 0x73, 0xFD, 0x6D, 0x84, 0x00, +0x3C, 0x7B, 0xFA, 0xF3, 0x3E, 0x07, 0xF5, 0xED, +0x84, 0x80, 0x87, 0x80, 0x3E, 0x47, 0xF5, 0xEF, +0x3E, 0x47, 0xF5, 0xEE, 0x3D, 0xCB, 0xFA, 0xF9, +0xFC, 0xC0, 0xFC, 0x40, 0x3C, 0x13, 0xF8, 0x3A, +0x3C, 0x73, 0xF8, 0x35, 0x3C, 0x23, 0xF8, 0x3D, +0x3C, 0x43, 0xF8, 0x3C, 0x42, 0x53, 0x84, 0x24, +0x44, 0x0F, 0x80, 0x00, 0x88, 0x85, 0x3C, 0x0B, +0xF9, 0x9A, 0x88, 0xA2, 0x3C, 0xA3, 0xF8, 0x3B, +0x3C, 0x63, 0xF9, 0x9B, 0x84, 0x40, 0x44, 0x3F, +0x80, 0x00, 0x3E, 0x8F, 0xF0, 0x8C, 0xE1, 0x41, +0xE9, 0x12, 0x98, 0x14, 0x41, 0xC1, 0x14, 0x00, +0x40, 0xFE, 0x00, 0x07, 0xE9, 0x09, 0x38, 0x94, +0x01, 0x11, 0xE0, 0x69, 0xE8, 0x03, 0x97, 0x81, +0x80, 0x69, 0x8C, 0x01, 0xD5, 0xF6, 0x8C, 0x21, +0x88, 0x47, 0xD5, 0xEE, 0x3C, 0x3B, 0xF9, 0x9A, +0x3C, 0x6B, 0xF9, 0x9B, 0xFC, 0xC0, 0xFC, 0x40, +0x3E, 0x7F, 0xF6, 0x04, 0x44, 0x10, 0x00, 0x38, +0x42, 0x70, 0x04, 0x73, 0x84, 0xC0, 0x2E, 0x2F, +0xFA, 0x1F, 0x50, 0x93, 0x00, 0x38, 0x8E, 0x41, +0xE0, 0x02, 0xE8, 0x17, 0x40, 0x83, 0xA4, 0x00, +0x88, 0xC7, 0x3A, 0x24, 0x14, 0x04, 0x3A, 0x23, +0x14, 0x24, 0x3A, 0x24, 0x14, 0x04, 0x3A, 0x23, +0x14, 0x24, 0x3A, 0x24, 0x14, 0x04, 0x3A, 0x23, +0x14, 0x24, 0x3A, 0x24, 0x0C, 0x00, 0x3A, 0x23, +0x0C, 0x20, 0x8C, 0x01, 0x80, 0xC9, 0xD5, 0xE4, +0x96, 0x12, 0x4E, 0x05, 0x00, 0x05, 0x3E, 0x07, +0xFA, 0x1F, 0xD5, 0x04, 0x84, 0x60, 0x3E, 0x37, +0xFA, 0x1F, 0xFC, 0xC0, 0xFC, 0x20, 0x2E, 0x17, +0xF5, 0xF9, 0x84, 0x00, 0x3C, 0x08, 0x07, 0xA7, +0xC9, 0x03, 0x3C, 0x18, 0x07, 0xA8, 0x2E, 0x57, +0xFA, 0x1E, 0x84, 0x40, 0x54, 0x62, 0x80, 0x01, +0x3C, 0x28, 0x07, 0xA9, 0x3C, 0x40, 0x07, 0xA1, +0x3C, 0x30, 0x07, 0xA2, 0x3C, 0x20, 0x07, 0xA3, +0x3C, 0x10, 0x07, 0xA4, 0x3C, 0x70, 0x07, 0xA5, +0xC6, 0x1F, 0x3C, 0x00, 0x07, 0xB9, 0x44, 0x80, +0x00, 0x64, 0xFF, 0x04, 0xFE, 0xC4, 0xFE, 0x84, +0xFE, 0x44, 0xFE, 0x3C, 0x40, 0xF2, 0x21, 0xF7, +0x41, 0xC1, 0xA3, 0x97, 0x41, 0xE1, 0x23, 0xD7, +0x40, 0x60, 0xA0, 0xD7, 0x40, 0x40, 0x20, 0x97, +0x3C, 0xF8, 0x07, 0x9B, 0x3D, 0xC8, 0x07, 0x9C, +0x3D, 0xE8, 0x07, 0x9D, 0x3C, 0x68, 0x07, 0x9E, +0x3C, 0x48, 0x07, 0x9F, 0xD5, 0x0B, 0x3C, 0x48, +0x07, 0x9B, 0x3C, 0x38, 0x07, 0x9C, 0x3C, 0x28, +0x07, 0x9D, 0x3C, 0x18, 0x07, 0x9E, 0x3C, 0x78, +0x07, 0x9F, 0x3C, 0x30, 0x07, 0xA6, 0x97, 0x4E, +0x3C, 0x38, 0x07, 0xA0, 0xC5, 0x0A, 0x3C, 0x50, +0x07, 0xB9, 0x44, 0x20, 0x00, 0x64, 0xFE, 0xEC, +0x40, 0x11, 0x88, 0x37, 0x3C, 0x18, 0x07, 0xA0, +0xFC, 0xA0, 0x2E, 0x07, 0xFA, 0x1E, 0xC8, 0x05, +0x3C, 0x17, 0xFA, 0xF3, 0x4E, 0x17, 0x00, 0x04, +0x84, 0x43, 0xD5, 0x04, 0x3C, 0x23, 0xFD, 0x13, +0x8E, 0x41, 0x3C, 0x2B, 0xFD, 0x13, 0x3C, 0x37, +0xFD, 0x13, 0x4E, 0x36, 0x00, 0x07, 0x84, 0xA0, +0x3C, 0x5B, 0xFD, 0x13, 0x84, 0x81, 0xD5, 0x02, +0x84, 0x80, 0x3E, 0x47, 0xFA, 0x28, 0xDD, 0x9E, +0x3E, 0x2F, 0xF5, 0xD8, 0x3E, 0x1F, 0xF5, 0x8C, +0x84, 0x00, 0xAC, 0x08, 0xAE, 0x0A, 0x3E, 0x07, +0xFA, 0x1F, 0x10, 0x01, 0x00, 0x14, 0x10, 0x01, +0x00, 0x20, 0xAC, 0x10, 0xAC, 0x11, 0xAC, 0x12, +0x3C, 0x0B, 0xF9, 0x9A, 0xDD, 0x9E, 0xFC, 0x00, +0x3C, 0x03, 0xFA, 0xF3, 0x8E, 0x01, 0x96, 0x43, +0x3C, 0x1B, 0xFA, 0xF3, 0x4E, 0x15, 0x00, 0x11, +0x3C, 0x24, 0x05, 0x87, 0x3C, 0x30, 0x07, 0x9B, +0xE0, 0x62, 0xE9, 0x05, 0x3C, 0x40, 0x07, 0xA0, +0xE0, 0x82, 0xE8, 0x06, 0x84, 0x00, 0x49, 0xFF, +0xFE, 0x29, 0x84, 0x04, 0xD5, 0x67, 0x3C, 0x60, +0x07, 0x97, 0x84, 0xA0, 0x54, 0x03, 0x01, 0x00, +0x3C, 0x5B, 0xFA, 0xF3, 0xC0, 0x3A, 0x2F, 0xC7, +0xEA, 0x79, 0x5B, 0xC0, 0x02, 0x37, 0x2E, 0x17, +0xFA, 0x1E, 0xC9, 0x07, 0x3C, 0x63, 0xFF, 0x98, +0x3C, 0x33, 0xFF, 0x99, 0x97, 0x73, 0xD5, 0x06, +0x3C, 0x23, 0xFF, 0x8D, 0x3C, 0x33, 0xFF, 0x8E, +0x97, 0x53, 0x97, 0x1B, 0x4E, 0x47, 0x00, 0x12, +0x3C, 0x00, 0x07, 0xB7, 0x96, 0x0E, 0xC0, 0x0D, +0x3C, 0x14, 0x05, 0x87, 0x3D, 0xE4, 0x05, 0x8D, +0xFF, 0x0C, 0x44, 0xF0, 0x00, 0x64, 0x41, 0xC2, +0x3F, 0x96, 0x40, 0x4E, 0x78, 0x07, 0xD5, 0x02, +0x84, 0x81, 0x4E, 0x57, 0x00, 0x14, 0x3C, 0x20, +0x07, 0xB7, 0x96, 0x86, 0xC2, 0x0F, 0x3C, 0x34, +0x05, 0x87, 0x3C, 0x64, 0x05, 0x8A, 0xFF, 0x5C, +0x44, 0x00, 0x00, 0x64, 0x40, 0x52, 0x80, 0xB6, +0xE0, 0xA6, 0x40, 0x42, 0x3C, 0x04, 0xD5, 0x02, +0x84, 0x81, 0x3C, 0x10, 0x01, 0x2D, 0xC1, 0x04, +0x2E, 0x20, 0x02, 0x6A, 0xC2, 0x09, 0x3C, 0x63, +0xFE, 0x28, 0x3D, 0xE0, 0x05, 0x82, 0x96, 0xF3, +0xE0, 0x7E, 0xE9, 0x02, 0xC4, 0x0B, 0x3C, 0x47, +0xF9, 0x91, 0x3D, 0xC0, 0x07, 0x9B, 0xE0, 0x9C, +0xE8, 0x10, 0x3C, 0x00, 0x07, 0xA0, 0xE0, 0x80, +0xE8, 0x0C, 0x2E, 0x27, 0xF5, 0xED, 0x84, 0xA0, +0x84, 0x20, 0x3E, 0x57, 0xF5, 0xF9, 0x3C, 0x1B, +0xF9, 0x9C, 0xCA, 0x99, 0x84, 0x03, 0xD5, 0x02, +0x84, 0x00, 0xFC, 0x80, 0xFC, 0x00, 0x80, 0xC0, +0x49, 0xFF, 0xFF, 0x6C, 0x49, 0xFF, 0xFC, 0xC5, +0x3C, 0x00, 0x07, 0x99, 0x96, 0x06, 0xC0, 0x07, +0x2E, 0x17, 0xEA, 0x79, 0x5A, 0x18, 0x02, 0x04, +0x49, 0xFF, 0xD0, 0x16, 0x49, 0xFF, 0xFF, 0x43, +0x3C, 0x24, 0x08, 0xD0, 0x4E, 0x26, 0x00, 0x05, +0x2E, 0x00, 0x11, 0xB2, 0xC0, 0x12, 0x84, 0x01, +0x49, 0xFF, 0xFA, 0xCB, 0x5A, 0x00, 0x01, 0x04, +0x48, 0x00, 0x00, 0xD8, 0x87, 0x80, 0x84, 0x00, +0x3F, 0xC0, 0x11, 0xB2, 0x49, 0xFF, 0xFC, 0xD5, +0x49, 0xFF, 0xF6, 0xAD, 0x48, 0x00, 0x00, 0xCE, +0x84, 0x61, 0x3E, 0x37, 0xF0, 0x64, 0xCE, 0x0D, +0x3C, 0x68, 0x08, 0xD8, 0x3C, 0x68, 0x08, 0xD7, +0x3C, 0x68, 0x08, 0xD6, 0x3C, 0x68, 0x08, 0xD2, +0x3C, 0x68, 0x08, 0xD1, 0x48, 0x00, 0x00, 0xBE, +0x49, 0xFF, 0xFA, 0xAB, 0x5A, 0x08, 0x01, 0x04, +0x48, 0x00, 0x00, 0xB8, 0x49, 0xFF, 0xF4, 0x10, +0x49, 0xFF, 0xFC, 0xA1, 0x49, 0xFF, 0xF6, 0x84, +0x49, 0xFF, 0xFE, 0xBA, 0x49, 0xFF, 0xF6, 0x9E, +0x49, 0xFF, 0xFF, 0x37, 0x5A, 0x08, 0x04, 0x04, +0x48, 0x00, 0x00, 0xA8, 0x5A, 0x00, 0x03, 0x6B, +0x49, 0xFF, 0xFE, 0x4D, 0x2E, 0x47, 0xEA, 0x79, +0x5A, 0x48, 0x02, 0x0A, 0x3C, 0x27, 0xF9, 0x9A, +0x3D, 0xC0, 0x07, 0x9B, 0xE0, 0x5C, 0xE8, 0x18, +0x84, 0x00, 0xD5, 0x64, 0x49, 0xFF, 0xF8, 0xA7, +0xC0, 0x0A, 0x2E, 0x67, 0xF5, 0xCF, 0xC6, 0xF9, +0x3C, 0x03, 0xF9, 0x9A, 0x94, 0x41, 0x3C, 0x1B, +0xF9, 0x9A, 0xD5, 0x22, 0x3C, 0x57, 0xF9, 0x9A, +0x3C, 0xF0, 0x07, 0x9B, 0xE0, 0xAF, 0xE8, 0x04, +0x49, 0xFF, 0xF6, 0x60, 0xD5, 0x47, 0x3C, 0x37, +0xF9, 0x9A, 0x3C, 0x40, 0x07, 0x9B, 0x87, 0xC0, +0xE0, 0x64, 0x3D, 0xE8, 0x08, 0xD8, 0x3E, 0x6F, +0xF0, 0x8C, 0xE9, 0x3C, 0x84, 0x00, 0x49, 0xFF, +0xEF, 0xD1, 0xC0, 0x07, 0x3C, 0x23, 0xF8, 0x33, +0x3D, 0xCD, 0xFC, 0xC5, 0x84, 0x80, 0xD5, 0x07, +0x2E, 0x57, 0xF5, 0xEC, 0xCD, 0x24, 0x49, 0xFF, +0xF4, 0x92, 0xD5, 0x67, 0xE0, 0x82, 0xE8, 0x08, +0x94, 0x61, 0x88, 0x3C, 0xA4, 0xC8, 0x38, 0x33, +0x11, 0x09, 0x8C, 0x81, 0xD5, 0xF8, 0x2E, 0x57, +0xEA, 0x79, 0x5A, 0x58, 0x02, 0x0B, 0x49, 0xFF, +0xF8, 0xD8, 0x49, 0xFF, 0xF9, 0x0B, 0x2E, 0x07, +0xEA, 0x79, 0x5A, 0x08, 0x02, 0x12, 0xD5, 0x16, +0x2E, 0x67, 0xF5, 0xED, 0x5A, 0x68, 0x01, 0x06, +0x3C, 0x03, 0xFE, 0x1C, 0x96, 0x26, 0xC8, 0xF0, +0x49, 0xFF, 0xF6, 0x93, 0x49, 0xFF, 0xF7, 0x6B, +0x5A, 0x08, 0x04, 0xEB, 0xD5, 0x42, 0x2F, 0xE7, +0xF5, 0xEC, 0x5B, 0xE8, 0x01, 0x04, 0x3C, 0x03, +0xFE, 0x1C, 0x2E, 0x4F, 0xFA, 0x1F, 0x2E, 0x27, +0xFA, 0x1E, 0xCC, 0x07, 0x96, 0x8E, 0xC2, 0x9D, +0x84, 0x01, 0x49, 0xFF, 0xFC, 0xFB, 0xD5, 0x31, +0x58, 0xF1, 0x00, 0x01, 0x3E, 0xF7, 0xFA, 0x1E, +0x49, 0xFF, 0xF4, 0x55, 0x49, 0xFF, 0xE2, 0x7C, +0x2F, 0xC7, 0xFA, 0x1F, 0x3F, 0xC7, 0xFA, 0x22, +0xC0, 0x24, 0x84, 0xC0, 0x3C, 0x6B, 0xFA, 0xF8, +0x49, 0xFF, 0xE1, 0x46, 0x2E, 0x17, 0xF5, 0xED, +0xC1, 0x17, 0x3C, 0x33, 0xFE, 0x1C, 0x96, 0xE6, +0xC3, 0x13, 0x2E, 0x07, 0xF5, 0xEF, 0xC0, 0x06, +0x3C, 0xF3, 0xFA, 0xC6, 0x58, 0x27, 0x80, 0x20, +0xD5, 0x08, 0x2E, 0x47, 0xF5, 0xEE, 0xC4, 0x0D, +0x3D, 0xE3, 0xFA, 0xC6, 0x58, 0x2F, 0x00, 0x10, +0x3C, 0x2B, 0xFA, 0xC6, 0xD5, 0x06, 0x3C, 0x53, +0xFA, 0xFF, 0x8C, 0xA1, 0x3C, 0x5B, 0xFA, 0xFF, +0xFC, 0x80, 0xFC, 0x42, 0x46, 0x50, 0x00, 0xFF, +0xF0, 0x83, 0x46, 0x90, 0x02, 0x08, 0x58, 0x94, +0x8A, 0x88, 0xF1, 0x82, 0x84, 0x00, 0x10, 0x2F, +0x80, 0x07, 0x50, 0x82, 0x8F, 0xFF, 0x3C, 0x2D, +0xFA, 0xA3, 0x46, 0x70, 0x02, 0x08, 0x58, 0x73, +0x8A, 0x38, 0x44, 0x67, 0xFF, 0xFF, 0xF1, 0x03, +0x38, 0xA4, 0x82, 0x02, 0x88, 0x20, 0xF5, 0x03, +0x38, 0xA1, 0x86, 0x0A, 0xF1, 0x02, 0x88, 0xA0, +0x88, 0x20, 0x38, 0xF3, 0x82, 0x02, 0x51, 0xC0, +0x80, 0x88, 0x38, 0x82, 0x16, 0x0A, 0x38, 0xF1, +0x72, 0x0A, 0xF5, 0x02, 0x88, 0xA0, 0x51, 0xE2, +0x80, 0xA8, 0x8C, 0x01, 0x38, 0x61, 0x7A, 0x0A, +0x5A, 0x08, 0x13, 0xE7, 0xF0, 0x02, 0x85, 0x00, +0x50, 0x90, 0x00, 0x9B, 0x44, 0xA7, 0xFF, 0xFF, +0x38, 0x81, 0x26, 0x0A, 0xF7, 0x02, 0x50, 0x63, +0x80, 0xBB, 0x38, 0xA1, 0x1A, 0x0A, 0xF2, 0x03, +0x8C, 0x53, 0x38, 0x81, 0x8A, 0x0A, 0xF3, 0x03, +0x8C, 0x73, 0x38, 0x82, 0x0E, 0x0A, 0xFC, 0xC2, +0xFC, 0x47, 0xF4, 0x8C, 0x02, 0x4F, 0x80, 0x3A, +0x02, 0x7F, 0x80, 0x3C, 0xF0, 0x89, 0xF3, 0x82, +0x00, 0x0F, 0x80, 0x58, 0x00, 0x3F, 0x80, 0x68, +0xF4, 0x85, 0x3C, 0x43, 0xFD, 0x3C, 0xF0, 0x83, +0xF3, 0x84, 0x02, 0x0F, 0x80, 0x3E, 0xF7, 0x86, +0x02, 0x3F, 0x80, 0x40, 0x97, 0xE3, 0xE4, 0xEB, +0xF1, 0x8A, 0xF2, 0x8B, 0xF5, 0x8D, 0xF0, 0x87, +0x00, 0x6F, 0x80, 0x5C, 0x00, 0x1F, 0x80, 0x60, +0x00, 0x2F, 0x80, 0x64, 0x01, 0xEF, 0x80, 0x6C, +0x02, 0x5F, 0x80, 0x38, 0xF3, 0x88, 0x03, 0xCF, +0x80, 0x42, 0x02, 0x0F, 0x80, 0x44, 0xE9, 0x0D, +0x3C, 0x33, 0xFD, 0x3C, 0x97, 0x1B, 0xE4, 0x8F, +0xE8, 0x08, 0x9E, 0x8B, 0xE6, 0x42, 0x4E, 0xF3, +0x01, 0x01, 0x45, 0xC0, 0x04, 0x44, 0xD5, 0x07, +0x85, 0x43, 0x5A, 0x20, 0x01, 0x06, 0x85, 0x41, +0x5A, 0x20, 0x02, 0x03, 0x85, 0x40, 0xC9, 0x05, +0xE2, 0x26, 0x84, 0x8F, 0x80, 0x41, 0xD5, 0x69, +0x84, 0x40, 0x5A, 0x18, 0x01, 0x10, 0x46, 0x70, +0x20, 0x40, 0x50, 0x33, 0x84, 0x05, 0xE2, 0x46, +0x84, 0x8F, 0x80, 0x22, 0x81, 0x22, 0x44, 0x60, +0x0F, 0xFF, 0xF3, 0x81, 0x44, 0x70, 0x00, 0xEA, +0xD5, 0x6F, 0x5A, 0x18, 0x02, 0x16, 0x80, 0x82, +0xE2, 0x46, 0x80, 0x22, 0x81, 0x24, 0xFA, 0x43, +0x80, 0xC4, 0xF4, 0x81, 0x80, 0xE4, 0x80, 0x04, +0x83, 0x84, 0xF4, 0x88, 0xF4, 0x87, 0xF4, 0x86, +0xF4, 0x85, 0x80, 0xA4, 0x83, 0xC4, 0xF4, 0x84, +0xF4, 0x83, 0xF4, 0x82, 0xD5, 0x59, 0x5A, 0x18, +0x03, 0x1D, 0x3C, 0x10, 0x00, 0x00, 0x96, 0x56, +0xC9, 0x04, 0x44, 0x70, 0x00, 0xD7, 0xD5, 0x03, +0x44, 0x70, 0x00, 0xE7, 0x50, 0x40, 0x7F, 0xAB, +0x84, 0x40, 0x46, 0x81, 0x21, 0x21, 0x40, 0x11, +0x10, 0x06, 0x96, 0x00, 0x50, 0x34, 0x02, 0x53, +0x84, 0x8F, 0xC6, 0x35, 0x81, 0x22, 0x44, 0x60, +0x0F, 0xFF, 0xF3, 0x81, 0x85, 0xEA, 0xD5, 0x3C, +0x5A, 0x18, 0x04, 0x1D, 0x3C, 0x70, 0x00, 0x00, +0x97, 0xDE, 0xCF, 0x04, 0x44, 0x70, 0x00, 0xD7, +0xD5, 0x03, 0x44, 0x70, 0x00, 0xE7, 0x50, 0x80, +0x7F, 0xAB, 0x84, 0x40, 0x46, 0x91, 0x21, 0x21, +0x40, 0x11, 0x20, 0x06, 0x96, 0x00, 0x50, 0x34, +0x82, 0x53, 0x84, 0x8F, 0xC6, 0x1F, 0x81, 0x22, +0x44, 0x60, 0x0F, 0xFF, 0xF3, 0x81, 0x81, 0xE4, +0xD5, 0x1F, 0x50, 0x10, 0x7F, 0xAB, 0x40, 0x11, +0x04, 0x06, 0x96, 0x00, 0xE2, 0x46, 0x84, 0x8F, +0x46, 0x80, 0x20, 0x40, 0x50, 0x74, 0x04, 0x4C, +0xF7, 0x81, 0xFB, 0x23, 0x44, 0x60, 0x0F, 0xFF, +0x84, 0xEA, 0xD5, 0x0E, 0x80, 0x46, 0x81, 0x26, +0xF3, 0x81, 0x44, 0x60, 0x0F, 0xFF, 0x85, 0xEB, +0xD5, 0x07, 0x80, 0x46, 0x81, 0x26, 0xF3, 0x81, +0x44, 0x60, 0x0F, 0xFF, 0x85, 0xEE, 0x40, 0x32, +0xC0, 0x08, 0x40, 0x5F, 0x20, 0x08, 0x41, 0xE1, +0x94, 0x04, 0xF3, 0x04, 0x3C, 0x5D, 0xFA, 0xA3, +0x40, 0x8F, 0x0C, 0x04, 0xF3, 0x09, 0x45, 0xE0, +0x00, 0x24, 0x42, 0x51, 0xF8, 0x73, 0xF3, 0x06, +0xB7, 0x05, 0x40, 0x81, 0xC0, 0x08, 0xF3, 0x05, +0x40, 0x73, 0xC0, 0x08, 0x41, 0xE4, 0x0C, 0x04, +0xF3, 0x07, 0x15, 0xE2, 0x80, 0x01, 0xFF, 0xDF, +0xA9, 0xEA, 0xF3, 0x08, 0xF7, 0x01, 0x50, 0x82, +0x80, 0x08, 0x40, 0x00, 0x30, 0x08, 0x14, 0x74, +0x00, 0x01, 0x40, 0x71, 0xD0, 0x08, 0xFE, 0x3F, +0x40, 0x30, 0x70, 0x04, 0xF7, 0x02, 0xF0, 0x03, +0x40, 0xA5, 0x64, 0x08, 0xA8, 0xEC, 0x51, 0xC2, +0x80, 0x10, 0x40, 0x94, 0xA8, 0x04, 0x40, 0x33, +0xBC, 0x08, 0x40, 0xF7, 0xF0, 0x08, 0x40, 0x70, +0x40, 0x08, 0xFE, 0xFF, 0x41, 0xE4, 0xBC, 0x04, +0x40, 0x10, 0xE0, 0x08, 0xBB, 0x81, 0x40, 0x42, +0x40, 0x08, 0x40, 0x3F, 0x04, 0x04, 0xFF, 0x1F, +0x40, 0x21, 0x20, 0x08, 0xF0, 0x0D, 0xF1, 0x0B, +0xFF, 0x17, 0xA9, 0x2E, 0xF4, 0x0C, 0x40, 0x30, +0xF0, 0x08, 0x40, 0x70, 0x74, 0x08, 0xF0, 0x0A, +0xFF, 0xDF, 0x40, 0x22, 0x78, 0x08, 0xFE, 0xBF, +0x50, 0x82, 0x80, 0x18, 0x40, 0x70, 0x7C, 0x08, +0xFF, 0xD7, 0x14, 0x74, 0x00, 0x01, 0x04, 0xA2, +0x80, 0x08, 0x47, 0xCF, 0x00, 0x0F, 0x93, 0x4C, +0x40, 0x15, 0x30, 0x08, 0xFE, 0x77, 0x14, 0x12, +0x80, 0x08, 0x04, 0x32, 0x80, 0x08, 0x50, 0x4E, +0x0F, 0xFF, 0xFF, 0x1E, 0x40, 0x63, 0x40, 0x08, +0xFF, 0xA7, 0x14, 0x62, 0x80, 0x08, 0xFC, 0xC7, +0x44, 0x00, 0x00, 0x55, 0x45, 0xC0, 0x01, 0x41, +0x48, 0xFF, 0xFF, 0x06, 0x3C, 0x1D, 0xFA, 0xA3, +0x96, 0x00, 0xAE, 0x08, 0x10, 0x00, 0x80, 0x24, +0x10, 0x00, 0x80, 0x48, 0x10, 0x00, 0x80, 0x6C, +0x10, 0x00, 0x80, 0x90, 0xDD, 0x9E, 0x3C, 0x1D, +0xFA, 0xA3, 0x96, 0x00, 0xAE, 0x09, 0x10, 0x00, +0x80, 0x25, 0x10, 0x00, 0x80, 0x49, 0x10, 0x00, +0x80, 0x6D, 0x10, 0x00, 0x80, 0x91, 0xDD, 0x9E, +0x3C, 0x10, 0x00, 0x00, 0x3C, 0x20, 0x00, 0x00, +0x3C, 0x0B, 0xF8, 0x41, 0x96, 0x8E, 0x96, 0x46, +0xC2, 0x02, 0x8C, 0x21, 0x3C, 0x4D, 0xFA, 0xA3, +0xFA, 0x74, 0x42, 0x40, 0x8C, 0x73, 0x96, 0x00, +0xAE, 0x21, 0xDD, 0x9E, 0x3C, 0x00, 0x00, 0x00, +0x3C, 0x10, 0x00, 0x00, 0x96, 0x06, 0x96, 0x4E, +0xC1, 0x02, 0x8C, 0x01, 0xFA, 0x54, 0x3C, 0x3D, +0xFA, 0xA3, 0x42, 0x30, 0x08, 0x73, 0xA6, 0x19, +0xDD, 0x9E, 0x3C, 0x1D, 0xFA, 0xA3, 0xAC, 0x09, +0x12, 0x00, 0x80, 0x13, 0x12, 0x00, 0x80, 0x25, +0x12, 0x00, 0x80, 0x37, 0x12, 0x00, 0x80, 0x49, +0xDD, 0x9E, 0x96, 0x43, 0x3C, 0x1B, 0xFE, 0x8B, +0x3C, 0x40, 0x00, 0x00, 0x3C, 0x20, 0x00, 0x00, +0x3C, 0x0B, 0xF8, 0x3F, 0x96, 0x8E, 0x97, 0x06, +0xC2, 0x02, 0x8C, 0x81, 0x3C, 0x5D, 0xFA, 0xA3, +0xFA, 0x74, 0x42, 0x52, 0x0C, 0x73, 0xAC, 0x29, +0xDD, 0x9E, 0x3C, 0x00, 0x00, 0x00, 0x3C, 0x10, +0x00, 0x00, 0x96, 0x06, 0x96, 0x4E, 0xC1, 0x02, +0x8C, 0x01, 0xFA, 0x54, 0x3C, 0x3D, 0xFA, 0xA3, +0x42, 0x30, 0x08, 0x73, 0xA4, 0x19, 0xDD, 0x9E, +0x3C, 0x10, 0x00, 0x00, 0x3C, 0x20, 0x00, 0x00, +0x3C, 0x0B, 0xF8, 0x43, 0x96, 0x8E, 0x96, 0x46, +0xC2, 0x02, 0x8C, 0x21, 0xFA, 0x74, 0x3C, 0x4D, +0xFA, 0xA3, 0x42, 0x40, 0x8C, 0x73, 0xAC, 0x22, +0xDD, 0x9E, 0x3C, 0x00, 0x00, 0x00, 0x3C, 0x10, +0x00, 0x00, 0x96, 0x06, 0x96, 0x4E, 0xC1, 0x02, +0x8C, 0x01, 0xFA, 0x54, 0x3C, 0x3D, 0xFA, 0xA3, +0x42, 0x30, 0x08, 0x73, 0xA4, 0x1A, 0xDD, 0x9E, +0xDD, 0x9E, 0xDD, 0x9E, 0xFC, 0x4B, 0xF3, 0x95, +0x2E, 0x37, 0xEA, 0x79, 0xF2, 0x94, 0x5A, 0x38, +0x02, 0x11, 0x3C, 0x20, 0x00, 0xE3, 0x3C, 0x40, +0x00, 0xE4, 0x3C, 0x50, 0x00, 0xE5, 0xF4, 0x93, +0xF5, 0x91, 0xC8, 0x0E, 0x3C, 0x40, 0x00, 0x0A, +0x3C, 0x60, 0x00, 0x17, 0xF6, 0x92, 0xD5, 0x0C, +0x84, 0x00, 0xF0, 0x91, 0xF0, 0x92, 0x80, 0x80, +0xF0, 0x93, 0x80, 0x40, 0xD5, 0x05, 0x44, 0x70, +0x04, 0x04, 0xF7, 0x92, 0x80, 0x87, 0x3C, 0x83, +0xFF, 0xDD, 0x3C, 0x9D, 0xFA, 0xA9, 0x3C, 0x53, +0xFF, 0xDE, 0x40, 0x04, 0x40, 0x08, 0xFE, 0x2F, +0x14, 0x04, 0x80, 0x28, 0x3C, 0x60, 0x00, 0x00, +0x97, 0x86, 0xC6, 0x74, 0x5A, 0x38, 0x02, 0x30, +0x3C, 0x30, 0x00, 0x0E, 0x3C, 0x00, 0x00, 0x02, +0x3C, 0x60, 0x00, 0xFD, 0x3D, 0xE0, 0x00, 0x06, +0x3D, 0xC0, 0x00, 0x07, 0x3C, 0xF0, 0x00, 0x08, +0x3C, 0x80, 0x00, 0x09, 0x3C, 0xA0, 0x00, 0x0B, +0x3C, 0x70, 0x00, 0x0C, 0x3C, 0x50, 0x00, 0x0D, +0xB6, 0x3F, 0x96, 0x40, 0x84, 0x03, 0xF0, 0x82, +0x97, 0xB0, 0x84, 0x00, 0x96, 0x90, 0xF4, 0x8A, +0x97, 0x28, 0xF1, 0x81, 0xF0, 0x83, 0xF6, 0x84, +0xF2, 0x85, 0x15, 0xEF, 0x80, 0x06, 0x15, 0xCF, +0x80, 0x07, 0x14, 0xFF, 0x80, 0x08, 0x14, 0x8F, +0x80, 0x09, 0x14, 0xAF, 0x80, 0x0B, 0xF7, 0x8C, +0xF4, 0x8D, 0xD5, 0x36, 0x3C, 0x30, 0x00, 0x0E, +0x3C, 0x60, 0x00, 0x02, 0x3C, 0x50, 0x00, 0x03, +0x3C, 0x40, 0x00, 0x04, 0x3C, 0x20, 0x00, 0x05, +0x3D, 0xE0, 0x00, 0x06, 0x3D, 0xC0, 0x00, 0x07, +0x3C, 0xF0, 0x00, 0x08, 0x3C, 0x80, 0x00, 0x09, +0x3C, 0xA0, 0x00, 0x0A, 0x3C, 0x90, 0x00, 0x0B, +0x3C, 0x70, 0x00, 0x0C, 0x3C, 0x00, 0x00, 0x0D, +0xB6, 0x3F, 0x97, 0x68, 0x96, 0x70, 0x97, 0x20, +0x84, 0xC3, 0x96, 0x90, 0xF7, 0x8C, 0x97, 0xC0, +0xF1, 0x81, 0xF6, 0x82, 0xF5, 0x83, 0xF4, 0x84, +0xF2, 0x85, 0x15, 0xEF, 0x80, 0x06, 0x15, 0xCF, +0x80, 0x07, 0x14, 0xFF, 0x80, 0x08, 0x14, 0x8F, +0x80, 0x09, 0x14, 0xAF, 0x80, 0x0A, 0x14, 0x9F, +0x80, 0x0B, 0xF7, 0x8D, 0x84, 0x00, 0x96, 0xD8, +0xF0, 0x8E, 0x80, 0x20, 0x80, 0x40, 0x80, 0x80, +0x80, 0xA0, 0x49, 0xFF, 0xFD, 0x9F, 0x46, 0x30, +0xC2, 0x80, 0x51, 0xC1, 0x8C, 0x50, 0x84, 0xC1, +0xD5, 0x05, 0x46, 0x10, 0xC2, 0x80, 0x51, 0xC0, +0x8C, 0x28, 0x3C, 0x70, 0x00, 0x00, 0x97, 0xCE, +0xC7, 0x73, 0x2E, 0x97, 0xEA, 0x79, 0x3C, 0x30, +0x00, 0x1B, 0x5A, 0x98, 0x02, 0x30, 0xF4, 0x14, +0x3C, 0x10, 0x00, 0x0F, 0x3C, 0x20, 0x00, 0xFE, +0x3D, 0xE0, 0x00, 0x13, 0x3C, 0x80, 0x00, 0x14, +0x3C, 0xA0, 0x00, 0x15, 0x3C, 0x70, 0x00, 0x16, +0xB6, 0x9F, 0x3C, 0x50, 0x00, 0x18, 0x84, 0x84, +0x3C, 0xF0, 0x00, 0x19, 0x3C, 0x00, 0x00, 0x1A, +0x96, 0x48, 0xF4, 0x82, 0xF7, 0x89, 0x00, 0x4F, +0x80, 0x4C, 0xF7, 0x12, 0xF1, 0x81, 0x96, 0x90, +0x84, 0x20, 0xF5, 0x8B, 0x97, 0x40, 0xF1, 0x83, +0xF2, 0x84, 0xF4, 0x85, 0x15, 0xEF, 0x80, 0x06, +0x14, 0x8F, 0x80, 0x07, 0x14, 0xAF, 0x80, 0x08, +0xF7, 0x8A, 0x14, 0xFF, 0x80, 0x0C, 0xF5, 0x8D, +0xD5, 0x34, 0x3C, 0x50, 0x00, 0x0F, 0x3C, 0x40, +0x00, 0x10, 0x3C, 0x20, 0x00, 0x11, 0x3C, 0x10, +0x00, 0x12, 0x3D, 0xE0, 0x00, 0x13, 0x3C, 0x00, +0x00, 0x14, 0x3C, 0xF0, 0x00, 0x15, 0x3C, 0x80, +0x00, 0x16, 0x3C, 0xA0, 0x00, 0x17, 0x3C, 0x90, +0x00, 0x18, 0x3C, 0x70, 0x00, 0x19, 0xF0, 0x87, +0xF7, 0x8C, 0x3C, 0x00, 0x00, 0x1A, 0xF7, 0x14, +0x96, 0x48, 0xB6, 0xFF, 0x97, 0x68, 0x84, 0xE4, +0x97, 0x20, 0x96, 0x90, 0x96, 0x00, 0xF1, 0x85, +0xF5, 0x81, 0xF7, 0x82, 0xF4, 0x83, 0xF2, 0x84, +0x15, 0xEF, 0x80, 0x06, 0x14, 0xFF, 0x80, 0x08, +0x14, 0x8F, 0x80, 0x09, 0x14, 0xAF, 0x80, 0x0A, +0x14, 0x9F, 0x80, 0x0B, 0xF0, 0x8D, 0x84, 0x20, +0x80, 0x06, 0xF1, 0x8E, 0x80, 0x41, 0x96, 0xD8, +0x80, 0x81, 0x80, 0xA1, 0x49, 0xFF, 0xFD, 0x22, +0x8C, 0xC1, 0x51, 0xCE, 0x00, 0x18, 0x2E, 0x97, +0xEA, 0x79, 0x3C, 0x30, 0x00, 0x28, 0x3C, 0x70, +0x00, 0x1C, 0x5A, 0x98, 0x02, 0x71, 0x00, 0x5F, +0x80, 0x44, 0x3C, 0x10, 0x00, 0x1E, 0xF5, 0x92, +0x3D, 0xE3, 0xFE, 0x2C, 0x3C, 0xF0, 0x00, 0x21, +0x3C, 0x80, 0x00, 0x22, 0x3C, 0xA0, 0x00, 0x23, +0x3C, 0x50, 0x00, 0x24, 0x3C, 0x40, 0x00, 0x25, +0x3C, 0x20, 0x00, 0x26, 0x3C, 0x00, 0x00, 0x27, +0xF2, 0x8C, 0xF2, 0x15, 0x97, 0xF8, 0xB6, 0x5F, +0xF2, 0x12, 0xF7, 0x81, 0x96, 0x48, 0x84, 0xE0, +0xF5, 0x8A, 0x97, 0x40, 0xF1, 0x84, 0xF2, 0x85, +0xF4, 0x8B, 0xF5, 0x8D, 0x80, 0x06, 0x84, 0x21, +0x80, 0x47, 0x96, 0xD8, 0x80, 0x87, 0x80, 0xA7, +0xF7, 0x8E, 0x15, 0xEF, 0x80, 0x06, 0x14, 0xFF, +0x80, 0x07, 0x14, 0x8F, 0x80, 0x08, 0x14, 0xAF, +0x80, 0x09, 0xF7, 0x82, 0xF7, 0x83, 0x49, 0xFF, +0xFC, 0xE1, 0x02, 0x3F, 0x80, 0x22, 0xB6, 0xFF, +0x3C, 0x3B, 0xF8, 0x41, 0x3C, 0x30, 0x00, 0x28, +0x3C, 0x20, 0x00, 0x1C, 0x3C, 0x10, 0x00, 0x1E, +0x3D, 0xE3, 0xFE, 0x2C, 0x3C, 0x40, 0x00, 0x21, +0x96, 0x90, 0x3C, 0xF0, 0x00, 0x22, 0xF4, 0x87, +0x3C, 0x80, 0x00, 0x23, 0x3C, 0xA0, 0x00, 0x24, +0x3C, 0x50, 0x00, 0x25, 0x3C, 0x40, 0x00, 0x26, +0x3C, 0x00, 0x00, 0x27, 0xF2, 0x81, 0xF2, 0x12, +0x96, 0x48, 0xF5, 0x8B, 0x97, 0x40, 0xF1, 0x84, +0xF2, 0x85, 0xF4, 0x8C, 0xF5, 0x8D, 0x14, 0x9F, +0x80, 0x02, 0xF7, 0x83, 0x15, 0xEF, 0x80, 0x06, +0x14, 0xFF, 0x80, 0x08, 0x14, 0x8F, 0x80, 0x09, +0x14, 0xAF, 0x80, 0x0A, 0xF7, 0x8E, 0x9C, 0x31, +0x84, 0x21, 0x80, 0x47, 0x96, 0xD8, 0x80, 0x87, +0x80, 0xA7, 0xD5, 0x78, 0x3C, 0x40, 0x00, 0x1D, +0x3C, 0x20, 0x00, 0x1E, 0x3C, 0x10, 0x00, 0x1F, +0x3D, 0xE3, 0xFE, 0x2C, 0x3C, 0x00, 0x00, 0x21, +0x3C, 0xF0, 0x00, 0x22, 0x3C, 0x80, 0x00, 0x23, +0x3C, 0xA0, 0x00, 0x24, 0x3C, 0x90, 0x00, 0x25, +0x3C, 0x50, 0x00, 0x26, 0xF0, 0x87, 0x3C, 0x00, +0x00, 0x27, 0xF5, 0x8C, 0x97, 0xF8, 0xF5, 0x15, +0xF7, 0x81, 0x97, 0x20, 0x84, 0xE0, 0x96, 0x90, +0x96, 0x48, 0x96, 0x00, 0xB6, 0xBF, 0xF4, 0x83, +0xF2, 0x84, 0xF1, 0x85, 0x14, 0x8F, 0x80, 0x09, +0x14, 0xAF, 0x80, 0x0A, 0x14, 0x9F, 0x80, 0x0B, +0xF0, 0x8D, 0x84, 0x21, 0x80, 0x06, 0x80, 0x47, +0x96, 0xD8, 0x80, 0x87, 0x80, 0xA7, 0xF7, 0x8E, +0x15, 0xEF, 0x80, 0x06, 0x14, 0xFF, 0x80, 0x08, +0xF7, 0x82, 0x49, 0xFF, 0xFC, 0x6F, 0x3C, 0x30, +0x00, 0x1F, 0xB6, 0xFF, 0x3C, 0x3B, 0xF8, 0x41, +0x3C, 0x30, 0x00, 0x28, 0x3C, 0x50, 0x00, 0x1C, +0x3C, 0x40, 0x00, 0x1D, 0x3C, 0x20, 0x00, 0x1E, +0x3C, 0x10, 0x00, 0x1F, 0x3D, 0xE3, 0xFE, 0x2C, +0x3C, 0x00, 0x00, 0x21, 0x3C, 0xF0, 0x00, 0x22, +0xF0, 0x87, 0x3C, 0x80, 0x00, 0x23, 0x3C, 0xA0, +0x00, 0x24, 0x3C, 0x90, 0x00, 0x25, 0x3C, 0x00, +0x00, 0x26, 0x97, 0x68, 0xF0, 0x8C, 0x3C, 0x00, +0x00, 0x27, 0x96, 0x48, 0x97, 0x20, 0x96, 0x90, +0x96, 0x00, 0xF5, 0x81, 0xF1, 0x85, 0x84, 0xA2, +0x84, 0x21, 0xF5, 0x82, 0xF4, 0x83, 0xF2, 0x84, +0xF0, 0x8D, 0x15, 0xEF, 0x80, 0x06, 0x14, 0xFF, +0x80, 0x08, 0x14, 0x8F, 0x80, 0x09, 0x14, 0xAF, +0x80, 0x0A, 0x14, 0x9F, 0x80, 0x0B, 0xF7, 0x8E, +0x9C, 0x31, 0x80, 0x41, 0x96, 0xD8, 0x80, 0x87, +0x80, 0xA7, 0x49, 0xFF, 0xFC, 0x2F, 0x3C, 0x60, +0x00, 0x21, 0x3C, 0x73, 0xFF, 0xCC, 0x3C, 0x9D, +0xFA, 0xA9, 0x3C, 0x33, 0xFF, 0xCD, 0x40, 0x43, +0xC0, 0x08, 0xFF, 0x1F, 0x3C, 0x6B, 0xF8, 0x43, +0x14, 0x44, 0x80, 0x03, 0x3C, 0x03, 0xFF, 0xD7, +0x96, 0x43, 0x14, 0x14, 0x80, 0x24, 0x3C, 0x83, +0xFF, 0xDD, 0x3C, 0x23, 0xFF, 0xDE, 0x40, 0x54, +0x40, 0x08, 0xFF, 0x57, 0x14, 0x54, 0x80, 0x28, +0x3C, 0xA3, 0xFF, 0xE8, 0x3C, 0x63, 0xFF, 0xE9, +0x40, 0x75, 0x40, 0x08, 0xFF, 0xF7, 0x14, 0x74, +0x80, 0x32, 0x15, 0xC4, 0x80, 0x1C, 0xFC, 0xCB, +0xFC, 0x40, 0x84, 0xA0, 0x84, 0xC1, 0x3E, 0x9F, +0xFA, 0x58, 0x84, 0x60, 0x99, 0xC5, 0x80, 0x43, +0xB4, 0x87, 0x40, 0xA3, 0x08, 0x0C, 0x40, 0x45, +0x10, 0x02, 0xC4, 0x0A, 0x40, 0x81, 0x04, 0x0A, +0x50, 0xF4, 0x02, 0x40, 0x39, 0xC4, 0xBD, 0x01, +0x40, 0x43, 0x70, 0x0C, 0xFE, 0xE7, 0x9D, 0x11, +0xB5, 0x47, 0x41, 0xE3, 0x10, 0x0C, 0x40, 0xFF, +0x28, 0x02, 0xE8, 0x0A, 0x90, 0x81, 0x50, 0xF2, +0x02, 0x40, 0x38, 0x44, 0xBD, 0x01, 0x92, 0x88, +0x40, 0x43, 0x10, 0x0C, 0xFE, 0xE7, 0x8C, 0x42, +0x5A, 0x28, 0x20, 0xE0, 0x99, 0xCD, 0x8C, 0xA4, +0xB6, 0x67, 0x5A, 0x58, 0x80, 0xD8, 0xFC, 0xC0, +0x3C, 0x13, 0xFF, 0xCC, 0x3C, 0x0D, 0xFA, 0xA9, +0x3C, 0x23, 0xFF, 0xCD, 0x40, 0x30, 0xC0, 0x08, +0xFE, 0xD7, 0xA8, 0xC3, 0x3C, 0x43, 0xFF, 0xCE, +0x3C, 0x53, 0xFF, 0xCF, 0x40, 0x12, 0x40, 0x08, +0xFE, 0x6F, 0xA8, 0x44, 0x3C, 0xF3, 0xFF, 0xD0, +0x3C, 0x23, 0xFF, 0xD1, 0x40, 0x37, 0xC0, 0x08, +0xFE, 0xD7, 0xA8, 0xC5, 0x3C, 0x43, 0xFF, 0xD2, +0x14, 0x40, 0x00, 0x20, 0x3C, 0x53, 0xFF, 0xD3, +0x3C, 0x23, 0xFF, 0xD4, 0x40, 0x12, 0xC0, 0x08, +0xFE, 0x57, 0x14, 0x10, 0x00, 0x21, 0x3C, 0xF3, +0xFF, 0xD5, 0x3C, 0x33, 0xFF, 0xD6, 0x40, 0x47, +0xC0, 0x08, 0xFF, 0x1F, 0x14, 0x40, 0x00, 0x22, +0x3C, 0x53, 0xFF, 0xD7, 0x96, 0xAB, 0x14, 0x20, +0x00, 0x24, 0x3C, 0x13, 0xFF, 0xD8, 0x3C, 0x33, +0xFF, 0xD9, 0x40, 0x40, 0xC0, 0x08, 0xFF, 0x1F, +0x14, 0x40, 0x00, 0x25, 0x3C, 0xF3, 0xFF, 0xDA, +0x3C, 0x53, 0xFF, 0xDB, 0x40, 0x27, 0xC0, 0x08, +0xFE, 0xAF, 0x14, 0x20, 0x00, 0x26, 0x3C, 0x13, +0xFF, 0xDC, 0x14, 0x10, 0x00, 0x27, 0x3C, 0x33, +0xFF, 0xDD, 0x3C, 0x43, 0xFF, 0xDE, 0x40, 0x51, +0xC0, 0x08, 0xFF, 0x67, 0x14, 0x50, 0x00, 0x28, +0x3C, 0xF3, 0xFF, 0xDF, 0x3C, 0x23, 0xFF, 0xE0, +0x40, 0x17, 0xC0, 0x08, 0xFE, 0x57, 0x14, 0x10, +0x00, 0x29, 0x3C, 0x33, 0xFF, 0xE1, 0x3C, 0x43, +0xFF, 0xE2, 0x40, 0x51, 0xC0, 0x08, 0xFF, 0x67, +0x14, 0x50, 0x00, 0x2A, 0x3C, 0xF3, 0xFF, 0xE3, +0x14, 0xF0, 0x00, 0x2B, 0x3C, 0x13, 0xFF, 0xE6, +0x3C, 0x23, 0xFF, 0xE7, 0x40, 0x30, 0xC0, 0x08, +0xFE, 0xD7, 0x14, 0x30, 0x00, 0x31, 0x3C, 0x43, +0xFF, 0xEA, 0x14, 0x40, 0x00, 0x33, 0x3C, 0x53, +0xFF, 0xEF, 0x97, 0x46, 0x14, 0x50, 0x00, 0x3F, +0x3C, 0xF3, 0xFF, 0xF0, 0x3C, 0x23, 0xFF, 0xF1, +0x40, 0x17, 0xC0, 0x08, 0xFE, 0x57, 0x14, 0x10, +0x00, 0x50, 0x3C, 0x33, 0xFF, 0xF9, 0x3C, 0x43, +0xFF, 0xFA, 0x40, 0x51, 0xC0, 0x08, 0xFF, 0x67, +0x14, 0x50, 0x00, 0x58, 0x3C, 0xF3, 0xFF, 0xF6, +0x14, 0xF0, 0x00, 0x54, 0x3C, 0x13, 0xFF, 0xF2, +0x3C, 0x23, 0xFF, 0xF3, 0x40, 0x30, 0xC0, 0x08, +0xFE, 0xD7, 0x14, 0x30, 0x00, 0x51, 0x3C, 0x43, +0xFF, 0xF4, 0x3C, 0x53, 0xFF, 0xF5, 0x40, 0x12, +0x40, 0x08, 0xFE, 0x6F, 0x14, 0x10, 0x00, 0x52, +0x3C, 0xF3, 0xFF, 0xFB, 0x3C, 0x23, 0xFF, 0xFC, +0x40, 0x37, 0xC0, 0x08, 0xFE, 0xD7, 0x14, 0x30, +0x00, 0x59, 0x3C, 0x43, 0xFF, 0xFD, 0x3C, 0x53, +0xFF, 0xFE, 0x40, 0x12, 0x40, 0x08, 0xFE, 0x6F, +0x14, 0x10, 0x00, 0x5A, 0x3C, 0xF3, 0xFF, 0xF7, +0x14, 0xF0, 0x00, 0x55, 0x3C, 0x23, 0xFF, 0xF8, +0x14, 0x20, 0x00, 0x56, 0x3C, 0x33, 0xFF, 0xEC, +0x97, 0x5B, 0xC5, 0x06, 0x3C, 0x43, 0xFF, 0xEC, +0x46, 0x58, 0x00, 0x00, 0xFF, 0x67, 0x14, 0x50, +0x00, 0x3C, 0x3C, 0x13, 0xFF, 0xED, 0x3C, 0x23, +0xFF, 0xEE, 0x40, 0x30, 0xC0, 0x08, 0x44, 0x53, +0xFF, 0xFF, 0xFE, 0xD7, 0x85, 0xE0, 0x44, 0x40, +0x01, 0x11, 0x14, 0x30, 0x00, 0x3D, 0x14, 0xF0, +0x00, 0x75, 0x14, 0x40, 0x00, 0x64, 0x14, 0x50, +0x00, 0x7E, 0x14, 0x50, 0x00, 0x7F, 0x3C, 0x03, +0xFF, 0xE4, 0x96, 0x43, 0x3C, 0x1F, 0xFC, 0x22, +0xDD, 0x9E, 0xDD, 0x9E, 0x3C, 0x1D, 0xFA, 0xA9, +0xB4, 0x01, 0x96, 0x06, 0xC8, 0xFE, 0xDD, 0x9E, +0xFC, 0x20, 0x49, 0xFF, 0xFF, 0xF9, 0x3C, 0x0D, +0xFA, 0xA9, 0x46, 0x20, 0x36, 0x00, 0x50, 0x31, +0x02, 0xE0, 0x46, 0x40, 0x46, 0x00, 0x44, 0x10, +0x01, 0xC0, 0x46, 0x60, 0x88, 0x40, 0x14, 0x10, +0x00, 0x18, 0x50, 0x52, 0x03, 0xE0, 0x14, 0x30, +0x00, 0x19, 0x50, 0x73, 0x04, 0xE0, 0x44, 0x10, +0x0F, 0xCC, 0x44, 0x20, 0x00, 0x40, 0x84, 0x61, +0x44, 0x80, 0x10, 0x4C, 0x14, 0x50, 0x00, 0x1A, +0x14, 0x70, 0x00, 0x1B, 0x14, 0x80, 0x00, 0x1E, +0x14, 0x10, 0x00, 0x1D, 0x14, 0x20, 0x00, 0x73, +0xA8, 0xC7, 0x84, 0x00, 0x80, 0x20, 0x80, 0x40, +0x80, 0x60, 0x49, 0xFF, 0xFC, 0x81, 0x3C, 0x00, +0x00, 0x1F, 0x3C, 0x43, 0xFE, 0x2C, 0x3C, 0x63, +0xFE, 0x2C, 0x97, 0x63, 0x3C, 0x5B, 0xFE, 0x8B, +0x3C, 0x73, 0xFD, 0x3C, 0x3C, 0x0B, 0xF8, 0x40, +0x3C, 0x5B, 0xF8, 0x3E, 0x3C, 0x6B, 0xF8, 0x3F, +0x5A, 0x70, 0x06, 0x10, 0x3C, 0x13, 0xFD, 0x3C, +0x96, 0x8B, 0xE4, 0x4B, 0xE9, 0x06, 0x3C, 0x33, +0xFD, 0x3C, 0x96, 0x1B, 0xE4, 0x0F, 0xE9, 0x05, +0x3D, 0xE3, 0xFD, 0x3C, 0x5B, 0xE8, 0x11, 0x10, +0x3C, 0x03, 0xFE, 0x2C, 0x49, 0xFF, 0xFC, 0x0B, +0x3C, 0x03, 0xFE, 0x61, 0x49, 0xFF, 0xFB, 0xCC, +0x3C, 0x03, 0xFE, 0x4E, 0x49, 0xFF, 0xFB, 0xD5, +0x3C, 0x03, 0xFE, 0x4F, 0x49, 0xFF, 0xFE, 0xB6, +0x3C, 0x43, 0xFD, 0x3C, 0x85, 0x00, 0x97, 0x63, +0xE4, 0xAB, 0xE9, 0x06, 0x3C, 0x63, 0xFD, 0x3C, +0x97, 0xF3, 0x5E, 0x83, 0x80, 0x0F, 0x3C, 0x60, +0x00, 0x00, 0x84, 0x20, 0x97, 0x86, 0xC6, 0x06, +0x3C, 0x1E, 0x05, 0x07, 0x3C, 0x1E, 0x04, 0xE7, +0x84, 0xC1, 0x3C, 0x20, 0x00, 0x00, 0x96, 0x8E, +0xC2, 0x0C, 0x8C, 0xC1, 0x97, 0xB1, 0x3E, 0x08, +0x14, 0x18, 0x3E, 0x48, 0x13, 0x98, 0x84, 0x60, +0x38, 0x30, 0x1A, 0x0A, 0x38, 0x32, 0x1A, 0x0A, +0x3C, 0xFD, 0xFA, 0xA9, 0x47, 0xE0, 0xB1, 0x30, +0x50, 0x5F, 0x09, 0x05, 0x14, 0x57, 0x80, 0x11, +0x80, 0x06, 0x84, 0x20, 0xFA, 0x44, 0x3E, 0x38, +0x14, 0x18, 0x3E, 0x48, 0x13, 0x98, 0x49, 0xFF, +0xFA, 0x0E, 0x5A, 0x80, 0x01, 0x16, 0x3E, 0x08, +0x14, 0x18, 0x3E, 0x18, 0x15, 0x18, 0x49, 0xFF, +0xFE, 0x49, 0x3E, 0x08, 0x13, 0x98, 0x3E, 0x18, +0x14, 0x98, 0x49, 0xFF, 0xFE, 0x43, 0x84, 0xC0, +0x3C, 0x8D, 0xFA, 0xA3, 0x3E, 0x78, 0x15, 0x18, +0x3E, 0x38, 0x14, 0x98, 0xD5, 0x19, 0x3C, 0x7D, +0xFA, 0xA3, 0x84, 0x60, 0x3E, 0x08, 0x14, 0x18, +0x3E, 0x48, 0x13, 0x98, 0x95, 0x5A, 0x98, 0x5E, +0x88, 0xA7, 0x40, 0x24, 0x0C, 0x0C, 0x8C, 0x61, +0x14, 0x22, 0x80, 0x88, 0x38, 0x20, 0x06, 0x0A, +0x38, 0x22, 0x06, 0x0A, 0x14, 0x22, 0x80, 0xA8, +0x5A, 0x38, 0x14, 0xF2, 0xD5, 0xD5, 0x94, 0x32, +0x39, 0xE3, 0x9A, 0x02, 0x88, 0x08, 0x38, 0xF1, +0x9A, 0x02, 0x8C, 0xC1, 0x15, 0xE0, 0x00, 0x48, +0x14, 0xF0, 0x00, 0x68, 0x5A, 0x68, 0x20, 0xF5, +0xFC, 0xA0, 0xFC, 0x00, 0x49, 0xFF, 0xFF, 0x28, +0x3C, 0x0D, 0xFA, 0xA9, 0x84, 0x21, 0xB6, 0x20, +0xFC, 0x80, 0x3C, 0x1D, 0xFA, 0xA9, 0xB4, 0x01, +0x96, 0x0E, 0xC0, 0xFE, 0xDD, 0x9E, 0xFC, 0x00, +0x49, 0xFF, 0xFF, 0x1A, 0x3C, 0x0D, 0xFA, 0xA9, +0x44, 0x10, 0x00, 0x80, 0xB6, 0x20, 0xFC, 0x80, +0x3C, 0x0D, 0xFA, 0xA3, 0x04, 0x00, 0x03, 0x98, +0xDD, 0x9E, 0x3C, 0x3D, 0xFA, 0xA3, 0x84, 0x00, +0x84, 0x22, 0x94, 0x8A, 0x88, 0x43, 0x04, 0x41, +0x03, 0x83, 0xE2, 0x04, 0xE8, 0x03, 0x04, 0x01, +0x03, 0x83, 0x8C, 0x21, 0x5A, 0x18, 0x15, 0xF7, +0xDD, 0x9E, 0x46, 0x00, 0x08, 0x20, 0x3C, 0x0F, +0xFA, 0xAE, 0x46, 0x00, 0x08, 0x22, 0x3C, 0x0F, +0xFA, 0xA1, 0x46, 0x00, 0x04, 0x00, 0x3C, 0x0F, +0xFA, 0xA4, 0x46, 0x00, 0x02, 0x20, 0x3C, 0x0F, +0xFA, 0xA6, 0x46, 0x00, 0x08, 0x20, 0x50, 0x00, +0x08, 0x00, 0x3C, 0x0F, 0xFA, 0xA5, 0x46, 0x00, +0x08, 0x23, 0x3C, 0x0F, 0xFA, 0xA9, 0x46, 0x00, +0x04, 0x00, 0x50, 0x00, 0x01, 0xC0, 0x3C, 0x0F, +0xFA, 0xA3, 0x46, 0x00, 0x08, 0x21, 0x3C, 0x0F, +0xFA, 0xAD, 0x46, 0x00, 0x08, 0x20, 0x50, 0x00, +0x0C, 0x00, 0x3C, 0x0F, 0xFA, 0xA7, 0x46, 0x00, +0x08, 0x22, 0x50, 0x00, 0x08, 0x00, 0x3C, 0x0F, +0xFA, 0xAC, 0x46, 0x00, 0x08, 0x22, 0x50, 0x00, +0x0C, 0x00, 0x3C, 0x0F, 0xFA, 0xAA, 0x46, 0x00, +0x04, 0x00, 0x50, 0x00, 0x01, 0x40, 0x3C, 0x0F, +0xFA, 0xAB, 0x46, 0x00, 0x04, 0x00, 0x50, 0x00, +0x01, 0x8C, 0x3C, 0x0F, 0xFA, 0xA2, 0x46, 0x00, +0x08, 0x23, 0x50, 0x00, 0x04, 0x00, 0x3C, 0x0F, +0xFA, 0xA8, 0xDD, 0x9E, 0x3C, 0x0D, 0xFA, 0xA2, +0x46, 0x10, 0x02, 0x01, 0x58, 0x10, 0x8B, 0xBC, +0xA8, 0x41, 0xDD, 0x9E, 0x3B, 0xFF, 0xFC, 0xBC, +0xEF, 0xEC, 0x84, 0x00, 0x12, 0x0F, 0x80, 0x07, +0x84, 0x00, 0xF0, 0x82, 0xD5, 0x29, 0x3C, 0x0D, +0xFA, 0xAB, 0x04, 0x30, 0x00, 0x12, 0xF0, 0x02, +0x46, 0x10, 0x02, 0x00, 0x88, 0x01, 0x3E, 0x18, +0x02, 0x80, 0x44, 0x20, 0x02, 0x00, 0xDD, 0x23, +0x84, 0x00, 0xF0, 0x81, 0xD5, 0x11, 0xF0, 0x01, +0x94, 0x41, 0x3E, 0x08, 0x02, 0x80, 0x88, 0x01, +0x22, 0x00, 0x00, 0x00, 0x96, 0x41, 0x02, 0x0F, +0x80, 0x07, 0x88, 0x01, 0x12, 0x0F, 0x80, 0x07, +0xF0, 0x01, 0x8C, 0x01, 0xF0, 0x81, 0xF0, 0x01, +0x5E, 0xF0, 0x01, 0x00, 0xE9, 0xED, 0xF0, 0x02, +0x50, 0x00, 0x02, 0x00, 0xF0, 0x82, 0xF0, 0x02, +0x44, 0x10, 0xAF, 0xFF, 0xE0, 0x20, 0xE8, 0xD4, +0x02, 0x0F, 0x80, 0x07, 0xEC, 0x14, 0x3B, 0xFF, +0xFC, 0x84, 0xDD, 0x9E, 0x3B, 0xFF, 0xFC, 0xBC, +0xEF, 0xF4, 0x84, 0x00, 0x12, 0x0F, 0x80, 0x03, +0x49, 0xFF, 0xFF, 0xBE, 0x12, 0x0F, 0x80, 0x03, +0x02, 0x0F, 0x80, 0x03, 0xC8, 0x06, 0x44, 0x00, +0x55, 0xAA, 0x3C, 0x0B, 0xFE, 0x58, 0xD5, 0x04, +0x84, 0x00, 0x3C, 0x0B, 0xFE, 0x58, 0xEC, 0x0C, +0x3B, 0xFF, 0xFC, 0x84, 0xDD, 0x9E, 0x3B, 0xFF, +0xFC, 0xBC, 0xEF, 0xFC, 0x3C, 0x0D, 0xFA, 0xAB, +0x04, 0x30, 0x00, 0x12, 0x3E, 0x1F, 0xFA, 0x58, +0x46, 0x00, 0x02, 0x0A, 0x50, 0x00, 0x02, 0x00, +0x44, 0x20, 0x08, 0x00, 0xDD, 0x23, 0xEC, 0x04, +0x3B, 0xFF, 0xFC, 0x84, 0xDD, 0x9E, 0x3B, 0xFF, +0xFC, 0xBC, 0xEF, 0xFC, 0x3C, 0x0D, 0xFA, 0xAB, +0x04, 0x30, 0x00, 0x11, 0x3E, 0x0F, 0xFA, 0x58, +0x46, 0x10, 0x02, 0x0A, 0x50, 0x10, 0x82, 0x00, +0x44, 0x20, 0x08, 0x00, 0xDD, 0x23, 0xEC, 0x04, +0x3B, 0xFF, 0xFC, 0x84, 0xDD, 0x9E, 0x3B, 0xFF, +0xFC, 0xBC, 0xEF, 0xEC, 0x10, 0x0F, 0x80, 0x07, +0x3C, 0x03, 0xFD, 0x44, 0x96, 0x03, 0x96, 0x01, +0x96, 0x06, 0xC0, 0x0C, 0x84, 0x00, 0x3C, 0x08, +0x06, 0xA1, 0x84, 0x00, 0x3C, 0x08, 0x06, 0xA2, +0x84, 0x00, 0x3E, 0x00, 0x0D, 0x46, 0x48, 0x00, +0x00, 0x6E, 0x00, 0x0F, 0x80, 0x07, 0xC0, 0x0F, +0x3C, 0x0D, 0xFA, 0xAB, 0x04, 0x30, 0x00, 0x12, +0x3E, 0x18, 0x0B, 0x20, 0x46, 0x00, 0x02, 0x0A, +0x50, 0x00, 0x0A, 0x00, 0x44, 0x20, 0x01, 0xE0, +0xDD, 0x23, 0xD5, 0x0E, 0x3C, 0x0D, 0xFA, 0xAB, +0x04, 0x30, 0x00, 0x12, 0x3E, 0x18, 0x0B, 0x20, +0x46, 0x00, 0x02, 0x0A, 0x50, 0x00, 0x0C, 0x00, +0x44, 0x20, 0x01, 0xE0, 0xDD, 0x23, 0x3C, 0x0D, +0xFA, 0xAB, 0x04, 0x30, 0x00, 0x12, 0x3E, 0x18, +0x0D, 0x4C, 0x46, 0x00, 0x02, 0x0A, 0x50, 0x00, +0x0E, 0x00, 0x44, 0x20, 0x00, 0x40, 0xDD, 0x23, +0x84, 0x00, 0xF0, 0x83, 0xD5, 0x14, 0xF0, 0x03, +0x94, 0x41, 0x3E, 0x08, 0x0D, 0x4C, 0x88, 0x01, +0x22, 0x20, 0x00, 0x00, 0xF0, 0x03, 0x50, 0x00, +0x01, 0x04, 0x94, 0x41, 0x3E, 0x08, 0x0B, 0x20, +0x88, 0x01, 0x80, 0x22, 0xAC, 0x40, 0xF0, 0x03, +0x8C, 0x01, 0xF0, 0x83, 0xF0, 0x03, 0xE4, 0x0C, +0xE9, 0xEB, 0xD5, 0x15, 0xF0, 0x03, 0x8E, 0x0C, +0xF1, 0x03, 0x94, 0x89, 0x3E, 0x18, 0x0D, 0x4C, +0x88, 0x22, 0x22, 0x20, 0x80, 0x00, 0x50, 0x00, +0x00, 0xF0, 0x94, 0x41, 0x3E, 0x08, 0x0B, 0x20, +0x88, 0x01, 0x80, 0x22, 0xAC, 0x40, 0xF0, 0x03, +0x8C, 0x01, 0xF0, 0x83, 0xF0, 0x03, 0x5E, 0xF0, +0x00, 0x20, 0xE9, 0xE9, 0x3C, 0x03, 0xFE, 0x2C, +0x96, 0x03, 0x96, 0x01, 0x3C, 0x08, 0x06, 0xA1, +0x3C, 0x00, 0x00, 0x1F, 0x96, 0x03, 0x96, 0x01, +0x3C, 0x08, 0x06, 0xA2, 0x84, 0x01, 0x3E, 0x00, +0x0D, 0x46, 0xEC, 0x14, 0x3B, 0xFF, 0xFC, 0x84, +0xDD, 0x9E, 0x3B, 0xFF, 0xFC, 0xBC, 0xEF, 0xF4, +0x3C, 0x0D, 0xFA, 0xAB, 0x04, 0x30, 0x00, 0x11, +0x3E, 0x08, 0x0B, 0x20, 0x46, 0x10, 0x02, 0x0A, +0x50, 0x10, 0x8A, 0x00, 0x44, 0x20, 0x01, 0xE0, +0xDD, 0x23, 0x84, 0x00, 0xF0, 0x81, 0xD5, 0x14, +0xF0, 0x01, 0x50, 0x00, 0x01, 0x04, 0x94, 0x41, +0x3E, 0x08, 0x0B, 0x20, 0x88, 0x01, 0x22, 0x20, +0x00, 0x00, 0xF0, 0x01, 0x94, 0x41, 0x3E, 0x08, +0x0D, 0x4C, 0x88, 0x01, 0x80, 0x22, 0xAC, 0x40, +0xF0, 0x01, 0x8C, 0x01, 0xF0, 0x81, 0xF0, 0x01, +0xE4, 0x0C, 0xE9, 0xEB, 0xD5, 0x15, 0xF0, 0x01, +0x8E, 0x0C, 0x50, 0x00, 0x00, 0xF0, 0x94, 0x41, +0x3E, 0x08, 0x0B, 0x20, 0x88, 0x01, 0x22, 0x20, +0x00, 0x00, 0xF0, 0x01, 0x94, 0x41, 0x3E, 0x08, +0x0D, 0x4C, 0x88, 0x01, 0x80, 0x22, 0xAC, 0x40, +0xF0, 0x01, 0x8C, 0x01, 0xF0, 0x81, 0xF0, 0x01, +0x5E, 0xF0, 0x00, 0x20, 0xE9, 0xE9, 0x3C, 0x0D, +0xFA, 0xAB, 0x04, 0x30, 0x00, 0x11, 0x3E, 0x08, +0x0D, 0x4C, 0x46, 0x10, 0x02, 0x0A, 0x50, 0x10, +0x8E, 0x00, 0x44, 0x20, 0x00, 0x40, 0xDD, 0x23, +0xEC, 0x0C, 0x3B, 0xFF, 0xFC, 0x84, 0xDD, 0x9E, +0x3C, 0x03, 0xFE, 0x42, 0x2E, 0x50, 0x02, 0x7B, +0x96, 0x06, 0x3E, 0x00, 0x02, 0x7C, 0xD0, 0x07, +0x84, 0x62, 0x3E, 0x30, 0x02, 0x7D, 0x3E, 0x00, +0x02, 0x7B, 0xDD, 0x9E, 0x2E, 0x10, 0x02, 0x7D, +0xC9, 0x07, 0x84, 0x41, 0x40, 0x51, 0x14, 0x1B, +0x3E, 0x50, 0x02, 0x7A, 0xDD, 0x9E, 0x8E, 0x21, +0x3E, 0x10, 0x02, 0x7D, 0xDD, 0x9E, 0xFC, 0x46, +0x3C, 0x00, 0x00, 0x00, 0x96, 0x06, 0xC0, 0x02, +0xFA, 0x04, 0x3C, 0x10, 0x00, 0x00, 0x96, 0x4E, +0xC1, 0x03, 0x8C, 0x0C, 0x96, 0x01, 0x3C, 0x5D, +0xFA, 0xA3, 0x84, 0x40, 0x3E, 0x6F, 0xFA, 0x58, +0x84, 0x60, 0x99, 0xD0, 0x40, 0x81, 0x84, 0x0A, +0x50, 0xA4, 0x02, 0x50, 0x54, 0xF1, 0x80, 0x01, +0x99, 0x1A, 0x38, 0x13, 0x29, 0x01, 0xE8, 0x04, +0x96, 0x4B, 0x90, 0x28, 0xD5, 0x02, 0x96, 0x48, +0xE4, 0x2C, 0xE8, 0x1A, 0x3D, 0xC3, 0xFD, 0x3C, +0x40, 0x9E, 0x00, 0x11, 0xE5, 0x2B, 0xE9, 0x0D, +0x3D, 0xE3, 0xFD, 0x3C, 0x40, 0x8F, 0x00, 0x11, +0xE5, 0x0F, 0xE8, 0x07, 0x99, 0x1F, 0x50, 0xA2, +0x05, 0x34, 0x38, 0x42, 0xA9, 0x01, 0xD5, 0x06, +0x50, 0x42, 0x03, 0xA2, 0x95, 0x22, 0x88, 0x85, +0xA1, 0x21, 0x38, 0x4F, 0x86, 0x0A, 0x8C, 0x61, +0x5A, 0x38, 0x0C, 0xD6, 0x84, 0x20, 0x99, 0x10, +0x3C, 0x73, 0xFD, 0x3C, 0x98, 0xCA, 0x97, 0xFB, +0xE4, 0xEB, 0xE9, 0x0E, 0x3C, 0x73, 0xFD, 0x3C, +0x97, 0xFB, 0xE4, 0xEF, 0xE8, 0x09, 0x98, 0xCC, +0x39, 0xCF, 0x86, 0x01, 0x50, 0x91, 0x85, 0x34, +0x39, 0xC2, 0xA5, 0x09, 0xD5, 0x08, 0x50, 0x31, +0x83, 0xA2, 0x94, 0xDA, 0x38, 0x7F, 0x86, 0x02, +0x88, 0x65, 0xA9, 0xD9, 0x8C, 0x21, 0x5A, 0x18, +0x0C, 0xE5, 0x8C, 0x4C, 0x5A, 0x28, 0xF0, 0xB2, +0xFC, 0xC6, 0xDD, 0x9E, 0xFC, 0x40, 0x81, 0x00, +0x80, 0xE1, 0x49, 0xFF, 0xFC, 0xD5, 0x3C, 0x60, +0x00, 0x00, 0x97, 0x86, 0xC6, 0x02, 0xFA, 0xC4, +0x3C, 0x00, 0x00, 0x00, 0x96, 0x0E, 0xC0, 0x03, +0x8C, 0xCC, 0x97, 0xB1, 0x3C, 0x13, 0xFD, 0x3C, +0x5A, 0x18, 0x09, 0x16, 0x84, 0x80, 0x3D, 0xCD, +0xFA, 0xA3, 0x80, 0xA4, 0xE0, 0x87, 0xE8, 0x51, +0xE4, 0x97, 0xE8, 0x09, 0x50, 0x62, 0x03, 0x82, +0x94, 0xB2, 0x88, 0x5C, 0xA0, 0x11, 0x38, 0x04, +0x11, 0x09, 0xD5, 0x03, 0x38, 0x54, 0x11, 0x09, +0x8C, 0x81, 0xD5, 0xF1, 0x49, 0xFF, 0xFF, 0x75, +0x3C, 0x33, 0xF8, 0x37, 0x84, 0xA0, 0x50, 0x91, +0xFF, 0xFF, 0x53, 0xC1, 0x80, 0x0C, 0x3C, 0xAD, +0xFA, 0xA3, 0x80, 0x85, 0xE0, 0x87, 0xE8, 0x2D, +0x3C, 0x23, 0xFD, 0x3C, 0x96, 0x13, 0xE4, 0x0B, +0xE9, 0x0E, 0x3C, 0x13, 0xFD, 0x3C, 0x96, 0xCB, +0xE4, 0x6F, 0xE8, 0x09, 0x98, 0x35, 0x50, 0x10, +0x05, 0x34, 0x38, 0x35, 0x05, 0x01, 0x12, 0x34, +0x00, 0x00, 0xD5, 0x12, 0x50, 0x22, 0x83, 0xA2, +0x94, 0x12, 0x88, 0x0A, 0xA0, 0x41, 0x3D, 0xE3, +0xFE, 0x57, 0x40, 0x30, 0xF8, 0x0D, 0x96, 0x99, +0x12, 0x24, 0x00, 0x00, 0x3C, 0xF3, 0xFF, 0x20, +0x88, 0x4F, 0x12, 0x24, 0x00, 0x00, 0x4C, 0x54, +0xC0, 0x05, 0x50, 0x92, 0x80, 0x0C, 0x88, 0xBC, +0x8C, 0xA1, 0x8C, 0x81, 0x8D, 0x02, 0xD5, 0xD3, +0x49, 0xFF, 0xFD, 0x69, 0x3C, 0x08, 0x05, 0x83, +0x49, 0xFF, 0xFD, 0x60, 0x3C, 0x08, 0x05, 0x82, +0xFC, 0xC0, 0xFC, 0x03, 0x84, 0x40, 0x3E, 0x4F, +0xFA, 0x58, 0x40, 0x11, 0x04, 0x0A, 0x50, 0x50, +0x82, 0x50, 0x54, 0x31, 0x00, 0x01, 0x38, 0x62, +0x15, 0x01, 0xC3, 0x04, 0x96, 0x73, 0x90, 0x28, +0xD5, 0x02, 0x96, 0x70, 0xE4, 0x2C, 0xE8, 0x06, +0x94, 0xD1, 0x88, 0x60, 0xA5, 0x58, 0x38, 0x5F, +0x85, 0x09, 0x8C, 0x41, 0x5A, 0x28, 0x0C, 0xEB, +0x84, 0x80, 0x98, 0x84, 0x38, 0x62, 0x7C, 0x11, +0x8C, 0x82, 0xAD, 0x90, 0x5A, 0x48, 0x18, 0xFB, +0xFC, 0x83, 0xFC, 0x05, 0x84, 0x40, 0x3E, 0x4F, +0xFA, 0x58, 0x40, 0x11, 0x04, 0x0A, 0x50, 0x50, +0x82, 0x40, 0x54, 0x31, 0x00, 0x01, 0x38, 0x62, +0x15, 0x01, 0xC3, 0x04, 0x96, 0xF3, 0x90, 0x68, +0xD5, 0x02, 0x96, 0xF0, 0xE4, 0x74, 0xE8, 0x06, +0x94, 0x59, 0x88, 0x20, 0xA5, 0x48, 0x38, 0x5F, +0x89, 0x09, 0x8C, 0x41, 0x5A, 0x28, 0x14, 0xEB, +0x84, 0x80, 0x98, 0x84, 0x38, 0x62, 0x7C, 0x11, +0x8C, 0x82, 0xAD, 0x90, 0x5A, 0x48, 0x28, 0xFB, +0xFC, 0x85, 0xFC, 0x20, 0xFD, 0x30, 0x49, 0xFF, +0xFC, 0x23, 0x3C, 0x20, 0x00, 0x00, 0x96, 0x8E, +0x96, 0xD1, 0xC3, 0x36, 0x3C, 0x20, 0x00, 0x00, +0x96, 0x86, 0xC2, 0x02, 0xFA, 0x44, 0x3C, 0x8D, +0xFA, 0xA3, 0x80, 0xA7, 0x50, 0x43, 0x80, 0x18, +0x3C, 0x33, 0xFD, 0x3C, 0x51, 0xC1, 0x05, 0x34, +0x96, 0x5B, 0xE4, 0x2B, 0xE9, 0x0B, 0x3C, 0x03, +0xFD, 0x3C, 0x96, 0xC3, 0xE4, 0x6F, 0xE8, 0x06, +0x38, 0x14, 0x71, 0x01, 0x96, 0x09, 0xAC, 0x28, +0xD5, 0x0F, 0x39, 0xE4, 0x71, 0x01, 0x3C, 0x13, +0xFE, 0x92, 0x40, 0x0F, 0x04, 0x0D, 0x96, 0xC1, +0xAC, 0xE8, 0xA4, 0x68, 0x3C, 0x03, 0xFF, 0x1A, +0x88, 0x01, 0x96, 0xC1, 0xAC, 0xE8, 0x8C, 0xA2, +0x8C, 0x41, 0xDC, 0xDF, 0x80, 0x07, 0x49, 0xFF, +0xFF, 0x82, 0x3C, 0x70, 0x00, 0x00, 0x97, 0xC6, +0xCF, 0x0A, 0x80, 0x47, 0xD5, 0x31, 0x80, 0x23, +0x98, 0x3B, 0x8C, 0x62, 0xAC, 0x40, 0x5A, 0x38, +0x18, 0xFD, 0xD5, 0xF1, 0x3C, 0x8D, 0xFA, 0xA3, +0x80, 0x66, 0x84, 0x00, 0x3C, 0x43, 0xFD, 0x3C, +0x51, 0xC0, 0x05, 0x34, 0x96, 0x63, 0xE4, 0x2B, +0xE9, 0x09, 0x3C, 0x73, 0xFD, 0x3C, 0x96, 0xBB, +0xE4, 0x4F, 0xE8, 0x04, 0x38, 0x74, 0x71, 0x01, +0xD5, 0x0D, 0x39, 0xE4, 0x71, 0x01, 0x3C, 0x53, +0xFE, 0x7C, 0x40, 0x4F, 0x14, 0x0D, 0x96, 0x61, +0xAC, 0x58, 0xA5, 0xD8, 0x3D, 0xC3, 0xFF, 0x19, +0x88, 0xFC, 0x96, 0xB9, 0x8C, 0x01, 0xAC, 0x98, +0x8C, 0x62, 0x5A, 0x08, 0x14, 0xE1, 0x80, 0x06, +0x49, 0xFF, 0xFF, 0x71, 0xFC, 0xA0, 0x99, 0x77, +0x8C, 0xE2, 0xAC, 0xA8, 0x5A, 0x78, 0x28, 0xFD, +0xD5, 0xF7, 0xDD, 0x9E, 0xFC, 0x40, 0x3D, 0xC3, +0xF8, 0x36, 0x3C, 0x34, 0x08, 0xD0, 0x2E, 0x40, +0x11, 0xB2, 0x3E, 0x08, 0x0A, 0x80, 0x85, 0x40, +0xE1, 0x5C, 0xE8, 0x1C, 0xCB, 0x05, 0xCC, 0x04, +0x3C, 0x53, 0xFE, 0x1F, 0xCD, 0x04, 0x02, 0x10, +0x7E, 0xD0, 0xD5, 0x10, 0x3C, 0x73, 0xFE, 0x1F, +0x22, 0xF0, 0x00, 0x00, 0x96, 0xBB, 0x3C, 0x63, +0xFE, 0x1F, 0x23, 0xE0, 0x7E, 0xD0, 0x8E, 0x41, +0x97, 0x73, 0x43, 0xE1, 0x3C, 0x73, 0x40, 0x1F, +0x14, 0x36, 0xAC, 0x40, 0x8D, 0x41, 0x8C, 0x02, +0xD5, 0xE4, 0x3C, 0x73, 0xF8, 0x37, 0x3E, 0x08, +0x0A, 0xA8, 0x84, 0xA0, 0xE0, 0xA7, 0xE8, 0x1C, +0xCB, 0x05, 0xCC, 0x04, 0x3C, 0x13, 0xFE, 0x1F, +0xC9, 0x04, 0x02, 0x10, 0x7E, 0xD0, 0xD5, 0x10, +0x3C, 0x23, 0xFE, 0x1F, 0x22, 0x90, 0x00, 0x00, +0x96, 0x93, 0x3C, 0x63, 0xFE, 0x1F, 0x22, 0x80, +0x7E, 0xD0, 0x8E, 0x41, 0x97, 0xB3, 0x42, 0x81, +0x24, 0x73, 0x40, 0x14, 0x18, 0x36, 0xAC, 0x40, +0x8C, 0xA1, 0x8C, 0x02, 0xD5, 0xE4, 0xFC, 0xC0, +0xFC, 0x20, 0x3C, 0x00, 0x01, 0x2D, 0xC8, 0x04, +0x2E, 0x00, 0x0F, 0x68, 0xD5, 0x0D, 0x3C, 0x24, +0x05, 0x87, 0x3C, 0x40, 0x07, 0x9B, 0x3C, 0x13, +0xFE, 0x3D, 0xE0, 0x44, 0x96, 0xCB, 0xE9, 0x03, +0x96, 0x18, 0xD5, 0x02, 0x84, 0x00, 0x2E, 0x57, +0xFA, 0x1E, 0xCD, 0x07, 0x2E, 0x67, 0xFA, 0x28, +0xC6, 0x04, 0x3C, 0x00, 0x07, 0xB4, 0x92, 0x08, +0x3C, 0x74, 0x08, 0xD0, 0x3C, 0x83, 0xF8, 0x33, +0xCF, 0x0B, 0x2E, 0x20, 0x11, 0xB2, 0xCA, 0x08, +0xC0, 0x07, 0x3E, 0x38, 0x02, 0x80, 0x44, 0x40, +0x7F, 0xFF, 0x9F, 0x41, 0xD5, 0x12, 0x3E, 0x08, +0x02, 0x80, 0x84, 0xE0, 0x44, 0x30, 0x7F, 0xFF, +0xE0, 0xE8, 0xE8, 0x08, 0x2A, 0x20, 0x00, 0x01, +0x9B, 0x1A, 0x12, 0x40, 0x03, 0x0F, 0x8C, 0xE1, +0xD5, 0xF8, 0x49, 0xFF, 0xFF, 0x7D, 0xFC, 0xA0, +0xE0, 0x48, 0xE8, 0xFC, 0x2A, 0x11, 0x80, 0x01, +0x9B, 0xA1, 0x23, 0xC1, 0x83, 0x0F, 0x96, 0x73, +0x42, 0x12, 0xF0, 0x73, 0x41, 0xE0, 0x83, 0xD6, +0x13, 0xE1, 0x83, 0x0F, 0x8C, 0x41, 0xD5, 0xF1, +0xFC, 0x00, 0x2E, 0x67, 0xFA, 0x1E, 0xCE, 0x15, +0x2E, 0x07, 0xFA, 0x28, 0xC0, 0x12, 0x3C, 0x20, +0x01, 0x2D, 0x3C, 0x10, 0x00, 0x1F, 0xC2, 0x06, +0x3C, 0x33, 0xFE, 0x95, 0x88, 0x23, 0x96, 0x09, +0xD5, 0x02, 0x80, 0x01, 0x3C, 0x53, 0xF8, 0x41, +0xD0, 0x04, 0x49, 0xFF, 0xF7, 0x47, 0xD5, 0x02, +0x84, 0xC1, 0x80, 0x06, 0xFC, 0x80, 0x3C, 0x03, +0xFE, 0x05, 0x3C, 0x10, 0x05, 0x84, 0x96, 0x83, +0xE0, 0x22, 0xE8, 0x1A, 0x3C, 0x34, 0x08, 0xD0, +0xCB, 0x17, 0x2E, 0x40, 0x11, 0xB2, 0xCC, 0x14, +0x3C, 0x23, 0xFE, 0x04, 0x3C, 0x54, 0x08, 0xDB, +0x96, 0x53, 0x9C, 0x29, 0xE0, 0x25, 0x3C, 0x08, +0x08, 0xDB, 0xE8, 0x0A, 0xFC, 0x00, 0x84, 0xC1, +0x3E, 0x60, 0x11, 0xB2, 0x3C, 0x48, 0x08, 0xDB, +0x49, 0xFF, 0xFF, 0x7C, 0xFC, 0x80, 0xDD, 0x9E, +0xFC, 0x00, 0x3E, 0x68, 0x02, 0x80, 0x80, 0x06, +0x3C, 0x13, 0xF8, 0x33, 0x49, 0xFF, 0xFE, 0x00, +0x50, 0x03, 0x05, 0xA0, 0x50, 0x13, 0x05, 0xC8, +0x49, 0xFF, 0xFE, 0xAD, 0x3E, 0x0F, 0xFA, 0x58, +0x22, 0x13, 0x04, 0x42, 0x22, 0x23, 0x04, 0x43, +0x12, 0x10, 0x01, 0x04, 0x12, 0x20, 0x01, 0x03, +0xFC, 0x80, 0x3E, 0x28, 0x02, 0x58, 0x3E, 0x48, +0x02, 0x80, 0x00, 0x11, 0x00, 0x14, 0x3E, 0x0F, +0xFA, 0x58, 0x22, 0x52, 0x04, 0x47, 0x96, 0xCB, +0x12, 0x30, 0x01, 0x19, 0x22, 0xF2, 0x04, 0x48, +0x12, 0x50, 0x00, 0x73, 0x22, 0x12, 0x04, 0x44, +0x22, 0x32, 0x04, 0x4D, 0x22, 0x52, 0x04, 0x4E, +0x12, 0xF0, 0x00, 0x74, 0x12, 0x10, 0x00, 0x72, +0x22, 0xF2, 0x04, 0x45, 0x12, 0x30, 0x02, 0x70, +0x22, 0x12, 0x04, 0x4A, 0x12, 0x50, 0x02, 0x71, +0x22, 0x32, 0x04, 0x4B, 0x22, 0x51, 0x00, 0x01, +0x22, 0x42, 0x04, 0x46, 0x22, 0x21, 0x00, 0x02, +0x12, 0xF0, 0x02, 0x72, 0x12, 0x10, 0x02, 0x73, +0x12, 0x30, 0x02, 0x74, 0x12, 0x40, 0x02, 0x75, +0x12, 0x50, 0x01, 0x01, 0x12, 0x20, 0x01, 0x02, +0xDD, 0x9E, 0xFC, 0x00, 0x84, 0xC0, 0x3C, 0x68, +0x01, 0x2D, 0x49, 0xFF, 0xFF, 0xAB, 0x3C, 0x04, +0x08, 0xD0, 0x4E, 0x06, 0x00, 0x07, 0x3C, 0x13, +0xFD, 0x3C, 0x96, 0x8B, 0xE4, 0x43, 0xE9, 0x07, +0x49, 0xFF, 0xFB, 0x55, 0x84, 0x61, 0x3E, 0x37, +0xF5, 0x8B, 0xD5, 0x03, 0x3E, 0x67, 0xF5, 0x8B, +0x49, 0x00, 0x05, 0x8C, 0x49, 0xFF, 0xFF, 0xAF, +0x49, 0xFF, 0xFF, 0x0C, 0x49, 0x00, 0x03, 0xE1, +0x5C, 0x60, 0x00, 0x01, 0xCE, 0x0A, 0x49, 0xFF, +0xFB, 0x42, 0x3C, 0x43, 0xFD, 0xA7, 0x84, 0xA1, +0x3C, 0x4B, 0xFA, 0xAE, 0x3E, 0x57, 0xF5, 0x8B, +0x80, 0x06, 0xFC, 0x80, 0xFC, 0x40, 0x2E, 0x07, +0xEA, 0x79, 0x5A, 0x08, 0x02, 0x07, 0x49, 0xFF, +0xFF, 0xCE, 0x80, 0xC0, 0x48, 0x00, 0x01, 0xB3, +0x3C, 0x10, 0x01, 0x2D, 0xC9, 0x08, 0x84, 0x80, +0x84, 0xA0, 0x3E, 0x40, 0x02, 0x6A, 0x3C, 0x58, +0x01, 0x34, 0xD5, 0x08, 0x3C, 0x24, 0x01, 0x34, +0x9E, 0xD1, 0x3C, 0x38, 0x01, 0x34, 0x4E, 0x27, +0xFF, 0xF4, 0x84, 0xC0, 0x3C, 0x68, 0x01, 0x33, +0x49, 0xFF, 0xFC, 0xEC, 0x49, 0xFF, 0xFF, 0x62, +0x3C, 0x70, 0x01, 0x2D, 0xCF, 0x0F, 0x49, 0xFF, +0xED, 0xF4, 0x49, 0x00, 0x05, 0x4F, 0x49, 0xFF, +0xFF, 0x72, 0x3C, 0x20, 0x07, 0x97, 0x96, 0xAE, +0x96, 0xD1, 0x4E, 0x33, 0x00, 0xB7, 0x48, 0x00, +0x00, 0xBF, 0x49, 0xFF, 0xFB, 0x04, 0x84, 0xC0, +0x3C, 0x93, 0xF8, 0x33, 0x3E, 0x7F, 0xF0, 0x8C, +0x3E, 0x48, 0x02, 0x80, 0x84, 0x44, 0xE0, 0xC9, +0xE8, 0x09, 0x39, 0xE2, 0x19, 0x11, 0x40, 0xFF, +0x09, 0xF6, 0x38, 0xF3, 0x99, 0x09, 0x8C, 0xC1, +0xD5, 0xF7, 0x3C, 0x03, 0xF8, 0x36, 0x84, 0xA0, +0x95, 0xC1, 0x3E, 0x2F, 0xF0, 0x8C, 0x3E, 0x08, +0x02, 0x80, 0x85, 0x04, 0xD7, 0x0B, 0x98, 0x68, +0x98, 0xEA, 0x22, 0xA0, 0x84, 0x00, 0x8C, 0xA2, +0x41, 0xC5, 0x23, 0x96, 0x13, 0xC1, 0x80, 0xF0, +0xD5, 0xF6, 0x3C, 0x13, 0xF8, 0x37, 0x84, 0xA0, +0x95, 0xC9, 0x3E, 0x0F, 0xF0, 0x8C, 0x3E, 0x38, +0x02, 0x80, 0x87, 0xC4, 0xD7, 0x0B, 0x99, 0x2B, +0x99, 0xA8, 0x22, 0x92, 0x04, 0x14, 0x8C, 0xA2, +0x40, 0xF4, 0xF9, 0xF6, 0x12, 0xF3, 0x01, 0x04, +0xD5, 0xF6, 0x84, 0xC0, 0x3E, 0x7F, 0xF0, 0x8C, +0x49, 0xFF, 0xFF, 0x10, 0x5A, 0x60, 0x02, 0x04, +0x49, 0xFF, 0xFA, 0xC1, 0x3D, 0xE3, 0xF8, 0x33, +0x85, 0x40, 0x3E, 0x08, 0x02, 0x80, 0x87, 0x84, +0xE1, 0x5E, 0xE8, 0x0C, 0x38, 0x10, 0x29, 0x11, +0x38, 0x23, 0xA9, 0x01, 0x40, 0x50, 0xF0, 0xB6, +0x88, 0xA2, 0x38, 0x53, 0xA9, 0x09, 0x8D, 0x41, +0xD5, 0xF4, 0x3C, 0x13, 0xF8, 0x36, 0x3E, 0x2F, +0xF2, 0x6C, 0x84, 0x00, 0x3E, 0x58, 0x02, 0x80, +0x85, 0x24, 0xE0, 0x01, 0xE8, 0x0D, 0x94, 0xC1, +0x88, 0x65, 0xA5, 0x10, 0x22, 0xF1, 0x84, 0x00, +0x8C, 0x01, 0x40, 0x87, 0xA5, 0x16, 0x89, 0x04, +0x1A, 0x81, 0x00, 0x01, 0xD5, 0xF3, 0x3C, 0x03, +0xF8, 0x37, 0x3E, 0x3F, 0xF2, 0x94, 0x84, 0x40, +0x3F, 0xC8, 0x02, 0x80, 0x85, 0x04, 0xE0, 0x40, +0xE8, 0x0D, 0x95, 0x11, 0x88, 0x9C, 0x8C, 0x41, +0x22, 0x52, 0x04, 0x14, 0xA5, 0x18, 0x40, 0xA2, +0xA1, 0x56, 0x89, 0x44, 0x1A, 0xA1, 0x80, 0x01, +0xD5, 0xF3, 0x8C, 0xC1, 0x5A, 0x68, 0x03, 0xBA, +0x85, 0x40, 0x3E, 0x28, 0x02, 0x80, 0x3E, 0x3F, +0xF0, 0x8C, 0x4C, 0xAF, 0x00, 0x08, 0x38, 0x91, +0xA9, 0x01, 0x38, 0x91, 0x29, 0x09, 0x8D, 0x41, +0xD5, 0xF9, 0x94, 0x49, 0x84, 0xA0, 0x3E, 0x28, +0x02, 0x80, 0x3E, 0x3F, 0xF0, 0x8C, 0xD1, 0x09, +0x99, 0xAB, 0x99, 0xEA, 0x02, 0xF3, 0x00, 0xF0, +0x8C, 0xA2, 0x12, 0xF3, 0x84, 0x00, 0xD5, 0xF8, +0x95, 0x81, 0x84, 0xA0, 0x3E, 0x18, 0x02, 0x80, +0x3E, 0x7F, 0xF0, 0x8C, 0x4C, 0x53, 0x3F, 0x49, +0x99, 0x2F, 0x98, 0x29, 0x03, 0xC2, 0x01, 0x04, +0x8C, 0xA2, 0x13, 0xC0, 0x04, 0x14, 0xD5, 0xF7, +0x3C, 0x44, 0x08, 0xD0, 0xCC, 0x0A, 0x2E, 0x50, +0x11, 0xB2, 0xCD, 0x07, 0x49, 0x00, 0x03, 0x6C, +0x80, 0xE0, 0xD5, 0x04, 0x3C, 0x38, 0x01, 0x2D, +0x84, 0xE1, 0x2E, 0x67, 0xFA, 0x1E, 0xCE, 0x32, +0x2E, 0x07, 0xFA, 0x28, 0xC0, 0x2F, 0x3C, 0x10, +0x01, 0x37, 0x8E, 0x21, 0x96, 0x8B, 0x3C, 0x28, +0x01, 0x37, 0x4E, 0x24, 0x00, 0x28, 0x3C, 0x34, +0x08, 0xD0, 0xCB, 0x24, 0x2E, 0x60, 0x11, 0xB2, +0xCE, 0x21, 0x3C, 0x68, 0x01, 0x37, 0x49, 0xFF, +0xFE, 0x3D, 0x96, 0x06, 0xC8, 0x1B, 0x85, 0x42, +0x3C, 0xAB, 0xFE, 0x33, 0x3C, 0x03, 0xFE, 0xF8, +0x49, 0xFF, 0xEC, 0xE6, 0x49, 0xFF, 0xFA, 0x23, +0x49, 0xFF, 0xFE, 0x6C, 0x49, 0xFF, 0xFA, 0x1F, +0x49, 0x00, 0x04, 0x5C, 0x49, 0xFF, 0xFD, 0xDE, +0x3C, 0x23, 0xFE, 0x42, 0x96, 0xD3, 0x58, 0x81, +0x80, 0x40, 0x3C, 0x8B, 0xFE, 0x42, 0x48, 0x00, +0x00, 0x96, 0x3C, 0x43, 0xFE, 0x42, 0x44, 0x6F, +0xFF, 0xBF, 0x97, 0x63, 0x3C, 0x84, 0x01, 0x37, +0xFF, 0x76, 0x3C, 0x5B, 0xFE, 0x42, 0x4E, 0x84, +0x00, 0x05, 0x85, 0x20, 0x3C, 0x98, 0x01, 0x37, +0x3C, 0x00, 0x01, 0x2D, 0xC0, 0x0E, 0x3C, 0x43, +0xFD, 0x67, 0x3C, 0xA3, 0xFE, 0x3C, 0x3C, 0x13, +0xFD, 0xDD, 0x3D, 0xC3, 0xFE, 0x3C, 0x88, 0x8A, +0x88, 0x3C, 0x96, 0x21, 0x96, 0x49, 0xD5, 0x05, +0x3C, 0x03, 0xFD, 0x67, 0x3C, 0x13, 0xFD, 0xDD, +0x49, 0x00, 0x02, 0xC9, 0x2E, 0x27, 0xEA, 0x79, +0x5A, 0x28, 0x01, 0x16, 0x2E, 0x37, 0xFA, 0x1E, +0xCB, 0x12, 0x2E, 0x57, 0xFA, 0x28, 0xC5, 0x0F, +0x3C, 0x64, 0x05, 0x87, 0x3D, 0xE0, 0x07, 0x9B, +0xE0, 0xDE, 0xE8, 0x09, 0x3C, 0x84, 0x08, 0xD0, +0x4E, 0x86, 0x00, 0x06, 0x2E, 0x90, 0x11, 0xB2, +0x5A, 0x98, 0x01, 0x04, 0x49, 0xFF, 0xF9, 0xD3, +0xCF, 0x0B, 0x3C, 0x04, 0x08, 0xD0, 0xC8, 0x08, +0x2E, 0x60, 0x11, 0xB2, 0xCE, 0x05, 0x49, 0xFF, +0xFD, 0xF4, 0x84, 0xE3, 0xD5, 0x49, 0x3C, 0x40, +0x07, 0x97, 0x97, 0x2E, 0xC4, 0x27, 0x3C, 0x14, +0x08, 0xD0, 0xC9, 0x24, 0x2E, 0x60, 0x11, 0xB2, +0xCE, 0x21, 0x3C, 0x23, 0xFE, 0x29, 0x3C, 0xA4, +0x05, 0x87, 0x96, 0xD3, 0xE0, 0x6A, 0xE9, 0x0F, +0x3C, 0x53, 0xFE, 0x35, 0x3C, 0x84, 0x05, 0x88, +0x96, 0x2B, 0xE1, 0x00, 0xE9, 0x08, 0x3C, 0x43, +0xFE, 0x31, 0x3D, 0xC0, 0x05, 0x84, 0x96, 0xA3, +0xE0, 0x5C, 0xE8, 0x0C, 0x3C, 0x03, 0xFE, 0x42, +0x84, 0xE4, 0x97, 0x03, 0x58, 0x92, 0x00, 0x20, +0x3C, 0x9B, 0xFE, 0x42, 0x49, 0xFF, 0xFD, 0xC9, +0xD5, 0x1F, 0x3C, 0x63, 0xFE, 0x42, 0x44, 0x1F, +0xFF, 0xDF, 0x96, 0xF3, 0xFE, 0xCE, 0x3C, 0x3B, +0xFE, 0x42, 0x49, 0x00, 0x01, 0xA1, 0x49, 0x00, +0x02, 0x2C, 0x5C, 0x50, 0x00, 0x01, 0xFF, 0x7E, +0x84, 0xC0, 0xC5, 0x05, 0x49, 0x00, 0x04, 0x57, +0x5C, 0x60, 0x00, 0x01, 0x49, 0xFF, 0xFD, 0x4A, +0xC6, 0x06, 0x87, 0xC0, 0x3D, 0xEB, 0xFE, 0x33, +0x84, 0xC1, 0xD5, 0x04, 0x84, 0xE5, 0x3C, 0x7B, +0xFE, 0x33, 0x80, 0x06, 0xFC, 0xC0, 0x3C, 0x03, +0xFD, 0xA5, 0xC0, 0x04, 0x3C, 0x13, 0xFD, 0xA3, +0xC9, 0x0C, 0x3C, 0x53, 0xFD, 0x44, 0x84, 0x5E, +0x96, 0xEB, 0x84, 0x01, 0xFE, 0xD6, 0x3E, 0x00, +0x0D, 0x47, 0x3C, 0x3B, 0xFD, 0x44, 0xDD, 0x9E, +0xFC, 0x40, 0x3C, 0x23, 0xFD, 0x44, 0x84, 0xA0, +0x96, 0xD3, 0x58, 0x41, 0x80, 0x01, 0x3E, 0x78, +0x0B, 0x20, 0x3C, 0x4B, 0xFD, 0x44, 0x3E, 0x50, +0x0D, 0x47, 0x85, 0x40, 0x49, 0xFF, 0xF8, 0x84, +0x81, 0x27, 0x49, 0xFF, 0xF8, 0x87, 0x3E, 0x68, +0x02, 0x80, 0x49, 0xFF, 0xF9, 0x54, 0x3E, 0x08, +0x02, 0x80, 0x3C, 0x13, 0xF8, 0x33, 0x49, 0xFF, +0xFB, 0x9F, 0x49, 0xFF, 0xF9, 0x4C, 0x3C, 0x13, +0xF8, 0x33, 0x3E, 0x08, 0x02, 0x80, 0x49, 0xFF, +0xFB, 0x97, 0x85, 0x00, 0x3C, 0x13, 0xF8, 0x33, +0xE1, 0x01, 0xE8, 0x07, 0x38, 0xF3, 0x21, 0x11, +0x38, 0xF3, 0xA1, 0x09, 0x8D, 0x01, 0xD5, 0xF9, +0x3C, 0x03, 0xFD, 0xA2, 0x96, 0x43, 0x4E, 0x17, +0x00, 0x0B, 0x3C, 0x23, 0xFD, 0xA2, 0x22, 0x83, +0x80, 0x00, 0x96, 0xD3, 0xE1, 0x03, 0xE8, 0x03, +0x4E, 0xA2, 0x00, 0x4D, 0x3C, 0x43, 0xF8, 0x36, +0x84, 0xA0, 0x94, 0x21, 0xD0, 0x0A, 0x98, 0xEE, +0x41, 0xE4, 0x94, 0x00, 0x02, 0x21, 0x82, 0xD0, +0x8C, 0xA2, 0x12, 0x2F, 0x00, 0xF0, 0xD5, 0xF7, +0x3C, 0x53, 0xF8, 0x37, 0x95, 0x29, 0x84, 0xA0, +0xD4, 0x0A, 0x98, 0x6E, 0x40, 0x84, 0x94, 0x00, +0x03, 0xC0, 0x82, 0xE4, 0x8C, 0xA2, 0x13, 0xC4, +0x01, 0x04, 0xD5, 0xF7, 0x49, 0xFF, 0xF9, 0x0F, +0x85, 0x00, 0xD5, 0x02, 0x8D, 0x01, 0x3C, 0x03, +0xFD, 0xA3, 0x96, 0x43, 0xE1, 0x01, 0x3C, 0x13, +0xF8, 0x33, 0xE8, 0x45, 0x3E, 0x08, 0x02, 0x80, +0x49, 0xFF, 0xFB, 0x52, 0x3E, 0x08, 0x08, 0x20, +0x50, 0x10, 0x00, 0x28, 0x49, 0xFF, 0xFB, 0xFF, +0x49, 0xFF, 0xF8, 0xF9, 0x3C, 0x43, 0xF8, 0x33, +0x84, 0x20, 0xE0, 0x24, 0xE8, 0x18, 0x3C, 0x23, +0xFD, 0xA5, 0x96, 0x13, 0x4E, 0x07, 0x00, 0x12, +0x38, 0x23, 0x85, 0x11, 0x38, 0xF3, 0x05, 0x11, +0x3C, 0x53, 0xFD, 0xA5, 0x8A, 0x4F, 0x96, 0xEB, +0x4E, 0x24, 0x00, 0x03, 0xFE, 0x92, 0xE0, 0x62, +0xE8, 0x04, 0x49, 0xFF, 0xF8, 0x09, 0xD5, 0x53, +0x8C, 0x21, 0xD5, 0xE8, 0x3C, 0x53, 0xF8, 0x36, +0x94, 0x29, 0x84, 0xA0, 0xD0, 0x0A, 0x98, 0xEE, +0x41, 0xC4, 0x94, 0x00, 0x03, 0xE1, 0x82, 0xD0, +0x8C, 0xA2, 0x13, 0xEE, 0x00, 0xF0, 0xD5, 0xF7, +0x3C, 0x03, 0xF8, 0x37, 0x84, 0xA0, 0x94, 0xC1, +0xD3, 0xBE, 0x98, 0xAE, 0x40, 0x14, 0x94, 0x00, +0x02, 0x41, 0x02, 0xE4, 0x8C, 0xA2, 0x12, 0x40, +0x81, 0x04, 0xD5, 0xF7, 0x85, 0x00, 0xE1, 0x01, +0xE8, 0x07, 0x39, 0xE3, 0x21, 0x11, 0x39, 0xE3, +0xA1, 0x09, 0x8D, 0x01, 0xD5, 0xF9, 0x3C, 0x70, +0x07, 0x97, 0x97, 0xEE, 0xC7, 0x20, 0x3C, 0x23, +0xFE, 0x95, 0x96, 0xD3, 0x4E, 0x37, 0x00, 0x1C, +0x4E, 0xA3, 0x00, 0x1A, 0x49, 0xFF, 0xF7, 0xD4, +0x85, 0x41, 0x3C, 0x03, 0xFE, 0x94, 0x3C, 0xA8, +0x01, 0x2D, 0x49, 0xFF, 0xF4, 0x3C, 0x3C, 0x40, +0x00, 0x1F, 0x3D, 0xC3, 0xFE, 0x95, 0x3E, 0x78, +0x08, 0xA0, 0x88, 0x9C, 0x96, 0x21, 0x49, 0xFF, +0xF4, 0x05, 0x85, 0x41, 0x49, 0xFF, 0xF8, 0x97, +0x48, 0xFF, 0xFF, 0x41, 0x84, 0xC1, 0x85, 0x20, +0x49, 0xFF, 0xF7, 0xBA, 0x3E, 0x60, 0x0D, 0x47, +0x3C, 0x98, 0x01, 0x2D, 0x3C, 0x73, 0xFD, 0x44, +0x84, 0x9E, 0x97, 0xBB, 0xFF, 0xA6, 0x3C, 0x6B, +0xFD, 0x44, 0xFC, 0xC0, 0x84, 0x01, 0xDD, 0x9E, +0x84, 0x01, 0xDD, 0x9E, 0xFC, 0x40, 0x3C, 0x43, +0xF8, 0x35, 0x44, 0x3F, 0x80, 0x00, 0x50, 0x92, +0x7F, 0xFF, 0x84, 0xA1, 0x8E, 0x82, 0x3E, 0x68, +0x02, 0x80, 0x3E, 0x78, 0x0B, 0x20, 0x85, 0x42, +0xE0, 0xA9, 0xE8, 0x35, 0x96, 0x2B, 0x96, 0xA9, +0x51, 0xC0, 0x02, 0xE4, 0x50, 0x80, 0x01, 0x04, +0x9E, 0x11, 0x40, 0xF0, 0x00, 0x11, 0x39, 0xE3, +0x71, 0x11, 0x8C, 0x41, 0x51, 0xC7, 0x82, 0xE4, +0x96, 0x93, 0x38, 0x13, 0xA1, 0x11, 0x38, 0x03, +0x71, 0x11, 0x50, 0x87, 0x81, 0x04, 0x51, 0xC1, +0x02, 0xE4, 0x50, 0x21, 0x01, 0x04, 0x38, 0xF3, +0xA1, 0x11, 0x38, 0x83, 0x71, 0x11, 0x39, 0xC3, +0x89, 0x11, 0xD4, 0x13, 0x5A, 0x50, 0x01, 0x12, +0x8A, 0x0F, 0x8B, 0x1C, 0x88, 0x08, 0x40, 0x1F, +0x04, 0x01, 0x40, 0x00, 0x28, 0x16, 0x9A, 0x88, +0x4E, 0x24, 0x00, 0x03, 0xFE, 0x92, 0x96, 0x53, +0xE0, 0x23, 0x40, 0x11, 0xBC, 0x1B, 0x80, 0x61, +0x8C, 0xA1, 0xD5, 0xCB, 0x3C, 0x43, 0xF8, 0x34, +0x3C, 0x3B, 0xFF, 0x93, 0x50, 0x92, 0x7F, 0xFF, +0x44, 0x3F, 0x80, 0x00, 0x8E, 0x82, 0x84, 0xA1, +0x3E, 0x68, 0x02, 0x80, 0x3E, 0x78, 0x0B, 0x20, +0x85, 0x42, 0xE0, 0xA9, 0xE8, 0x35, 0x96, 0x2B, +0x96, 0xA9, 0x50, 0x10, 0x02, 0xD0, 0x50, 0x80, +0x00, 0xF0, 0x9E, 0x11, 0x40, 0xF0, 0x00, 0x11, +0x39, 0xC3, 0x05, 0x11, 0x8C, 0x41, 0x50, 0x17, +0x82, 0xD0, 0x96, 0x93, 0x39, 0xE3, 0xA1, 0x11, +0x38, 0x03, 0x05, 0x11, 0x50, 0x87, 0x80, 0xF0, +0x50, 0x11, 0x02, 0xD0, 0x50, 0x21, 0x00, 0xF0, +0x38, 0xF3, 0xA1, 0x11, 0x38, 0x23, 0x89, 0x11, +0x38, 0x83, 0x05, 0x11, 0xD4, 0x13, 0x5A, 0x50, +0x01, 0x12, 0x8B, 0x02, 0x8A, 0x0F, 0x88, 0x08, +0x40, 0x1E, 0x78, 0x01, 0x40, 0x00, 0x28, 0x16, +0x9A, 0x88, 0x4E, 0x24, 0x00, 0x03, 0xFE, 0x92, +0x96, 0x53, 0xE0, 0x23, 0x40, 0x11, 0xBC, 0x1B, +0x80, 0x61, 0x8C, 0xA1, 0xD5, 0xCB, 0x3C, 0x3B, +0xFF, 0x94, 0xFC, 0xC0, 0x2E, 0x00, 0x0D, 0x46, +0xC8, 0x0A, 0x44, 0x40, 0x7F, 0xFF, 0x3C, 0x4B, +0xFE, 0x0D, 0x3C, 0x4B, 0xFF, 0x93, 0x3C, 0x4B, +0xFF, 0x94, 0xDD, 0x9E, 0xFC, 0x41, 0x49, 0xFF, +0xFF, 0x67, 0x2E, 0x17, 0xF5, 0xCF, 0xC9, 0x05, +0x2E, 0x27, 0xFA, 0x1E, 0x5A, 0x28, 0x07, 0x2F, +0x3C, 0x33, 0xFE, 0x05, 0x3C, 0x50, 0x05, 0x84, +0x97, 0x1B, 0xE0, 0x85, 0x3F, 0xC8, 0x02, 0x80, +0xE9, 0x25, 0x3C, 0x64, 0x08, 0xD0, 0x4E, 0x66, +0x00, 0x22, 0x2E, 0x70, 0x11, 0xB2, 0x5A, 0x70, +0x01, 0x1E, 0x2E, 0x07, 0xFA, 0x1E, 0xC8, 0x0A, +0x2E, 0x17, 0xFA, 0x28, 0xC1, 0x07, 0x3C, 0x84, +0x05, 0x87, 0x3C, 0x90, 0x07, 0x9B, 0xE1, 0x09, +0xE9, 0x11, 0x3C, 0x23, 0xF8, 0x34, 0x3C, 0x43, +0xF8, 0x35, 0x8E, 0x41, 0xB6, 0x5F, 0x84, 0x60, +0x44, 0x2F, 0x80, 0x00, 0x3E, 0x88, 0x02, 0x80, +0x44, 0x60, 0x7F, 0xFF, 0x3E, 0x78, 0x0B, 0x20, +0xD5, 0x16, 0x44, 0x30, 0x7F, 0xFF, 0x3C, 0x3B, +0xFE, 0x0D, 0xD5, 0x3F, 0x98, 0x21, 0x38, 0xFE, +0x05, 0x11, 0x96, 0x01, 0x95, 0x49, 0x40, 0xA0, +0x00, 0x11, 0xF5, 0x81, 0x4C, 0xF3, 0x40, 0x0D, +0x8D, 0x21, 0x80, 0x2A, 0xB4, 0xBF, 0xE1, 0x25, +0xE9, 0xF2, 0x8C, 0x61, 0xE0, 0x64, 0xE8, 0x27, +0x96, 0x5B, 0x85, 0x21, 0xD5, 0xF8, 0x38, 0x54, +0x29, 0x11, 0xD6, 0xF3, 0x88, 0x04, 0x96, 0x03, +0x38, 0x14, 0x01, 0x11, 0x4C, 0x13, 0x3F, 0xEE, +0x39, 0xE3, 0xA9, 0x11, 0x38, 0x03, 0x81, 0x11, +0x8A, 0xBE, 0x05, 0xEF, 0x80, 0x01, 0x8A, 0x20, +0x39, 0xE3, 0xF8, 0x11, 0x40, 0xF7, 0xF8, 0x01, +0x88, 0x2F, 0x87, 0xC2, 0x40, 0x10, 0xF8, 0x36, +0x8A, 0xA1, 0x4E, 0x54, 0x00, 0x03, 0xFF, 0x6A, +0x96, 0x2B, 0xE0, 0x02, 0x40, 0x01, 0x3C, 0x1B, +0x80, 0x40, 0xD5, 0xD3, 0x3C, 0xA7, 0xFD, 0x23, +0x3C, 0x2B, 0xFE, 0x0D, 0x3C, 0xAB, 0xFE, 0xEA, +0xFC, 0xC1, 0xC1, 0x04, 0x84, 0x00, 0x3C, 0x0B, +0xFE, 0x0C, 0x84, 0x01, 0xDD, 0x9E, 0x3C, 0x03, +0xFE, 0x05, 0x3C, 0x10, 0x05, 0x84, 0x96, 0x83, +0xE0, 0x41, 0xE8, 0x05, 0x84, 0x20, 0x3C, 0x18, +0x08, 0xDB, 0xD5, 0x14, 0x3C, 0x34, 0x08, 0xD0, +0xCB, 0x10, 0x2E, 0x40, 0x11, 0xB2, 0xCC, 0x0D, +0x2E, 0x57, 0xFA, 0x1E, 0xCD, 0x04, 0x2E, 0x07, +0xFA, 0x28, 0xC8, 0x0A, 0x2E, 0x27, 0xEA, 0x79, +0x5A, 0x20, 0x02, 0x04, 0x3C, 0x03, 0xFE, 0x70, +0x84, 0x20, 0x3C, 0x18, 0x08, 0xDA, 0x84, 0x00, +0xDD, 0x9E, 0x3C, 0x03, 0xFF, 0x28, 0x96, 0x43, +0xE4, 0x23, 0xE8, 0x1B, 0xFC, 0x00, 0x3C, 0x23, +0xFF, 0x28, 0x3C, 0x40, 0x01, 0x2D, 0x96, 0xD3, +0xE0, 0x64, 0xE8, 0x09, 0x3C, 0x54, 0x08, 0xD0, +0x4E, 0x56, 0x00, 0x06, 0x2E, 0x60, 0x11, 0xB2, +0x5A, 0x68, 0x01, 0x06, 0x3C, 0x13, 0xFE, 0x50, +0x84, 0x00, 0xD5, 0x04, 0x3C, 0x13, 0xFE, 0x50, +0x84, 0x01, 0x49, 0xFF, 0xF3, 0x03, 0xFC, 0x80, +0xDD, 0x9E, 0xFC, 0x40, 0x2E, 0x58, 0x0F, 0x58, +0xD0, 0x1E, 0x3E, 0x3F, 0xEA, 0xC0, 0x84, 0x40, +0x50, 0x61, 0x80, 0x08, 0x44, 0x70, 0x00, 0x60, +0x81, 0x22, 0x80, 0x86, 0x42, 0x41, 0x1C, 0x73, +0xA1, 0x21, 0x97, 0x06, 0xC4, 0x09, 0x20, 0x81, +0x80, 0x20, 0x81, 0x40, 0xE1, 0x05, 0x40, 0xA4, +0xBC, 0x1B, 0x10, 0xA1, 0x80, 0x20, 0x8C, 0x41, +0x50, 0x31, 0x80, 0x60, 0x5A, 0x28, 0x0F, 0xEF, +0x3E, 0x00, 0x0F, 0x58, 0x2E, 0x58, 0x0F, 0x5A, +0xD1, 0x17, 0x84, 0x00, 0x3E, 0x2F, 0xF3, 0x40, +0x83, 0x80, 0x3C, 0x63, 0xFD, 0xDC, 0x97, 0xF3, +0xE0, 0x07, 0xE8, 0x0C, 0x28, 0x91, 0x00, 0x01, +0xE1, 0x25, 0xE9, 0x04, 0x10, 0x11, 0x7F, 0xFF, +0xD5, 0x03, 0x11, 0xC1, 0x7F, 0xFF, 0x8C, 0x01, +0xD5, 0xF1, 0x3E, 0x10, 0x0F, 0x5A, 0xFC, 0xC0, +0x3E, 0x18, 0x02, 0x58, 0x3C, 0x23, 0xFE, 0x34, +0x10, 0x00, 0x80, 0x14, 0x84, 0x01, 0xAC, 0x8A, +0xAC, 0x09, 0xDD, 0x9E, 0xFC, 0x00, 0x3C, 0x03, +0xFD, 0x66, 0x3C, 0x10, 0x05, 0x83, 0x96, 0x83, +0xE0, 0x41, 0xE9, 0x08, 0x3C, 0x33, 0xFD, 0x65, +0x3C, 0x50, 0x05, 0x82, 0x97, 0x1B, 0xE0, 0x85, +0xE8, 0x16, 0x3C, 0x60, 0x01, 0x2D, 0x5A, 0x68, +0x01, 0x13, 0x3D, 0xC3, 0xFE, 0x34, 0x80, 0x06, +0x3D, 0xC8, 0x01, 0x2E, 0x49, 0xFF, 0xFF, 0xDE, +0x49, 0xFF, 0xFF, 0x81, 0x84, 0x02, 0x84, 0x40, +0x3C, 0x08, 0x01, 0x2D, 0x3C, 0x28, 0x01, 0x32, +0x3C, 0x68, 0x01, 0x37, 0x3C, 0x30, 0x01, 0x2D, +0x3D, 0xE0, 0x05, 0x83, 0x5A, 0x38, 0x02, 0x0C, +0x3C, 0x43, 0xFE, 0x74, 0x97, 0x63, 0xE0, 0xBE, +0xE8, 0x03, 0x84, 0x00, 0xD5, 0x67, 0x3C, 0x13, +0xFE, 0x2A, 0xD5, 0x0A, 0x5A, 0x38, 0x01, 0x0F, +0x3C, 0x23, 0xFE, 0x3A, 0x96, 0xD3, 0xE0, 0x7E, +0xE9, 0xF5, 0x3C, 0x13, 0xFE, 0x38, 0x3D, 0xC0, +0x05, 0x82, 0x97, 0x8B, 0xE0, 0xDC, 0xE8, 0x2E, +0xD5, 0xED, 0x3C, 0x13, 0xFE, 0x36, 0x97, 0x0B, +0xE0, 0x9E, 0xE9, 0x08, 0x3C, 0x53, 0xFE, 0x37, +0x3D, 0xC0, 0x05, 0x82, 0x97, 0xAB, 0xE0, 0xDC, +0xE8, 0x20, 0x3C, 0x03, 0xFE, 0x34, 0x3C, 0x08, +0x01, 0x2E, 0x49, 0xFF, 0xFF, 0x48, 0x84, 0x01, +0x49, 0xFF, 0xFF, 0xA0, 0x3C, 0x13, 0xFE, 0x3A, +0x3C, 0x50, 0x05, 0x83, 0x97, 0x0B, 0x84, 0x40, +0x84, 0x61, 0xE0, 0x85, 0x3C, 0x28, 0x01, 0x32, +0x3C, 0x38, 0x01, 0x37, 0xE9, 0xCB, 0x3C, 0x63, +0xFE, 0x38, 0x3D, 0xE0, 0x05, 0x82, 0x96, 0x33, +0xE0, 0x1E, 0x56, 0x07, 0x80, 0x01, 0xD5, 0x2A, +0xC3, 0x1D, 0x3C, 0x03, 0xFE, 0x28, 0x3C, 0x30, +0x05, 0x82, 0x96, 0x83, 0xE0, 0x62, 0xE8, 0x16, +0x3C, 0x64, 0x01, 0x2E, 0x9E, 0x31, 0x3C, 0x08, +0x01, 0x2E, 0x4E, 0x66, 0x00, 0x1B, 0x87, 0x80, +0x3F, 0xC0, 0x02, 0x6C, 0x49, 0xFF, 0xFF, 0x1B, +0x84, 0x40, 0x84, 0x61, 0x3C, 0x28, 0x01, 0x2D, +0x3C, 0x28, 0x01, 0x2E, 0x3C, 0x38, 0x01, 0x37, +0xD5, 0x0C, 0x3C, 0x13, 0xFE, 0x28, 0x3D, 0xE0, +0x05, 0x82, 0x97, 0x0B, 0xE0, 0x9E, 0xE8, 0x05, +0x3C, 0x53, 0xFE, 0x34, 0x3C, 0x58, 0x01, 0x2E, +0x84, 0x01, 0xFC, 0x80, 0xFC, 0x40, 0x84, 0x40, +0x44, 0x1F, 0x80, 0x00, 0x44, 0x00, 0x7F, 0xFF, +0x85, 0x40, 0x3C, 0x28, 0x05, 0x86, 0x3C, 0x28, +0x05, 0x85, 0x3C, 0x18, 0x05, 0x8A, 0x3C, 0x08, +0x05, 0x8B, 0x3C, 0x18, 0x05, 0x8D, 0x3C, 0x08, +0x05, 0x8E, 0x3D, 0xE3, 0xF8, 0x37, 0x44, 0x9F, +0x80, 0x00, 0x3C, 0x70, 0x05, 0x8F, 0x45, 0xC0, +0x7F, 0xFF, 0x3E, 0x38, 0x08, 0x88, 0x80, 0x4A, +0x3E, 0x48, 0x0F, 0x80, 0xE0, 0x5E, 0xE8, 0x22, +0x94, 0x11, 0x88, 0x04, 0x22, 0x61, 0xFF, 0xE0, +0x02, 0x10, 0x01, 0x04, 0x9A, 0x31, 0x96, 0x43, +0xE1, 0x21, 0x12, 0x11, 0x81, 0x30, 0xE8, 0x03, +0x97, 0xD1, 0x81, 0x21, 0x22, 0x51, 0x80, 0x00, +0xE0, 0x3C, 0x9A, 0x35, 0x41, 0xC0, 0xBC, 0x1B, +0x4E, 0x04, 0x00, 0x03, 0xFE, 0x02, 0x96, 0x41, +0xE3, 0x41, 0x40, 0x05, 0x3C, 0x1A, 0x40, 0xA0, +0x00, 0x13, 0x1A, 0x61, 0x80, 0x01, 0x8C, 0x41, +0xD5, 0xDE, 0x3C, 0x98, 0x05, 0x8D, 0x3C, 0x78, +0x05, 0x8F, 0x3C, 0xA8, 0x05, 0x85, 0x3D, 0xC8, +0x05, 0x8E, 0x3C, 0x93, 0xF8, 0x36, 0x3C, 0x54, +0x05, 0x8A, 0x3C, 0x70, 0x05, 0x8C, 0x3C, 0x44, +0x05, 0x8B, 0x3C, 0x80, 0x05, 0x86, 0x3E, 0x38, +0x08, 0x60, 0x84, 0x40, 0x3E, 0xA8, 0x0F, 0x80, +0xE0, 0x49, 0xE8, 0x22, 0x94, 0x51, 0x88, 0x2A, +0x22, 0x61, 0xFF, 0xE0, 0x02, 0x00, 0x80, 0xF0, +0x9A, 0x70, 0x96, 0x0B, 0xE0, 0xA0, 0x12, 0x01, +0x81, 0x30, 0xE8, 0x03, 0x97, 0xD1, 0x80, 0xA0, +0x22, 0x11, 0x80, 0x00, 0xE0, 0x04, 0x40, 0x40, +0x3C, 0x1B, 0x9A, 0x31, 0x4E, 0x04, 0x00, 0x03, +0xFE, 0x02, 0x96, 0x41, 0xE3, 0x01, 0x40, 0x04, +0x3C, 0x1A, 0x40, 0x80, 0x00, 0x13, 0x1A, 0x61, +0x80, 0x01, 0x8C, 0x41, 0xD5, 0xDE, 0x3C, 0x58, +0x05, 0x8A, 0x3C, 0x78, 0x05, 0x8C, 0x3C, 0x48, +0x05, 0x8B, 0x3C, 0x88, 0x05, 0x86, 0xFC, 0xC0, +0xFC, 0x40, 0x49, 0xFF, 0xFF, 0x79, 0x44, 0x10, +0x7F, 0xFF, 0x44, 0x0F, 0x80, 0x00, 0x84, 0x40, +0x3C, 0x18, 0x05, 0x88, 0x3C, 0x08, 0x05, 0x87, +0x3C, 0x28, 0x05, 0x84, 0x3D, 0xCC, 0x02, 0xC0, +0x84, 0x20, 0x3E, 0x38, 0x0F, 0x80, 0x3E, 0x48, +0x02, 0x80, 0x44, 0x60, 0x7F, 0xFF, 0x3C, 0x73, +0xF8, 0x33, 0xE0, 0x27, 0xE8, 0x0D, 0x39, 0xE1, +0x85, 0x01, 0x38, 0x72, 0x05, 0x01, 0x94, 0x89, +0x88, 0xFE, 0x88, 0x5C, 0x9B, 0x77, 0x96, 0x2B, +0xAC, 0x10, 0x8C, 0x21, 0xD5, 0xF1, 0x84, 0x40, +0x3C, 0xA3, 0xF8, 0x34, 0x3D, 0xE3, 0xF8, 0x35, +0x80, 0x82, 0x3E, 0x98, 0x02, 0x80, 0xE0, 0x8A, +0xE8, 0x36, 0x94, 0x51, 0x88, 0x29, 0x84, 0x60, +0xE0, 0x7E, 0xE8, 0x2E, 0x40, 0x81, 0x88, 0x00, +0x40, 0x04, 0x04, 0x08, 0x88, 0x1C, 0x3C, 0x54, +0x05, 0x87, 0xA5, 0x80, 0x97, 0xF3, 0xE0, 0xA7, +0xE8, 0x06, 0xA5, 0x80, 0x3C, 0x88, 0x05, 0x89, +0x3C, 0x68, 0x05, 0x87, 0xA5, 0xC0, 0x3C, 0x84, +0x05, 0x88, 0x97, 0x7B, 0xE0, 0xA8, 0xE8, 0x04, +0xA4, 0x00, 0x3C, 0x08, 0x05, 0x88, 0x80, 0xE1, +0x2A, 0x60, 0x80, 0x01, 0x22, 0x53, 0x81, 0xE0, +0x8A, 0xA6, 0x4E, 0x54, 0x00, 0x03, 0xFF, 0x6A, +0x3C, 0x80, 0x05, 0x84, 0x96, 0x29, 0xE3, 0x00, +0xE8, 0x03, 0x3C, 0x08, 0x05, 0x84, 0x12, 0x60, +0x81, 0xDF, 0x8C, 0x61, 0xD5, 0xD2, 0x8C, 0x81, +0x88, 0x5E, 0xD5, 0xCA, 0x3C, 0x30, 0x01, 0x2D, +0xC3, 0x06, 0x3C, 0x43, 0xFE, 0x3D, 0x97, 0x60, +0x3C, 0x58, 0x01, 0x33, 0xFC, 0xC0, 0x3C, 0x53, +0xF8, 0x3F, 0xD8, 0x06, 0x84, 0x40, 0x3E, 0x20, +0x02, 0x78, 0x84, 0x01, 0xDD, 0x9E, 0xFC, 0x00, +0x80, 0xC0, 0x49, 0xFF, 0xF4, 0x71, 0x80, 0x06, +0x49, 0xFF, 0xF0, 0xDD, 0x3C, 0x13, 0xFE, 0xFA, +0x84, 0x00, 0x3C, 0x08, 0x01, 0x3B, 0x3E, 0x00, +0x02, 0x78, 0x3C, 0x18, 0x01, 0x39, 0x84, 0x00, +0xFC, 0x80, 0x84, 0x01, 0xDD, 0x9E, 0x84, 0x01, +0xDD, 0x9E, 0x84, 0x00, 0xDD, 0x9E, 0x00, 0x00, +0x80, 0x60, 0xC2, 0x07, 0x99, 0x4A, 0x08, 0x40, +0x80, 0x01, 0x18, 0x41, 0x80, 0x01, 0xD9, 0xFC, +0xDD, 0x9E, 0x92, 0x00, 0x80, 0x60, 0x99, 0x5A, +0xD3, 0x04, 0x18, 0x11, 0x80, 0x01, 0xD5, 0xFD, +0xDD, 0x9E, 0x92, 0x00, 0x90, 0x19, 0x40, 0x00, +0x9B, 0x57, 0x00, 0x00, 0xCD, 0x2B, 0x04, 0x00, +0xE6, 0x15, 0x06, 0x00, 0xF3, 0x0A, 0x03, 0x00, +0x79, 0x85, 0x05, 0x00, 0xBC, 0xC2, 0x06, 0x00, +0x5E, 0x61, 0x03, 0x00, 0xAF, 0xB0, 0x01, 0x00, +0x57, 0xD8, 0x04, 0x00, 0x2B, 0x6C, 0x06, 0x00, +0x15, 0x36, 0x07, 0x00, 0x0A, 0x9B, 0x07, 0x00, +0x85, 0xCD, 0x03, 0x00, 0xC2, 0xE6, 0x05, 0x00, +0x61, 0xF3, 0x02, 0x00, 0xB0, 0x79, 0x05, 0x00, +0xD8, 0xBC, 0x02, 0x00, 0x6C, 0x5E, 0x01, 0x00, +0x36, 0xAF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x9B, 0x57, 0x00, 0x00, 0xCD, 0x2B, 0x04, 0x00, +0xE6, 0x15, 0x06, 0x00, 0xF3, 0x0A, 0x03, 0x00, +0x79, 0x85, 0x05, 0x00, 0xBC, 0xC2, 0x06, 0x00, +0x5E, 0x61, 0x03, 0x00, 0xAF, 0xB0, 0x01, 0x00, +0x57, 0xD8, 0x04, 0x00, 0x2B, 0x6C, 0x06, 0x00, +0x15, 0x36, 0x07, 0x00, 0x0A, 0x9B, 0x07, 0x00, +0x85, 0xCD, 0x03, 0x00, 0xC2, 0xE6, 0x05, 0x00, +0x61, 0xF3, 0x02, 0x00, 0xB0, 0x79, 0x05, 0x00, +0xD8, 0xBC, 0x02, 0x00, 0x6C, 0x5E, 0x01, 0x00, +0x36, 0xAF, 0x00, 0x00, 0x9B, 0x57, 0x00, 0x00, +0xCD, 0x2B, 0x04, 0x00, 0xE6, 0x15, 0x06, 0x00, +0xF3, 0x0A, 0x03, 0x00, 0x79, 0x85, 0x05, 0x00, +0xBC, 0xC2, 0x06, 0x00, 0x5E, 0x61, 0x03, 0x00, +0xAF, 0xB0, 0x01, 0x00, 0x57, 0xD8, 0x04, 0x00, +0x2B, 0x6C, 0x06, 0x00, 0x15, 0x36, 0x07, 0x00, +0x0A, 0x9B, 0x07, 0x00, 0x85, 0xCD, 0x03, 0x00, +0xC2, 0xE6, 0x05, 0x00, 0x61, 0xF3, 0x02, 0x00, +0xB0, 0x79, 0x05, 0x00, 0xD8, 0xBC, 0x02, 0x00, +0x6C, 0x5E, 0x01, 0x00, 0x36, 0xAF, 0x00, 0x00, +0x9B, 0x57, 0x00, 0x00, 0xCD, 0x2B, 0x04, 0x00, +0xE6, 0x15, 0x06, 0x00, 0xF3, 0x0A, 0x03, 0x00, +0x79, 0x85, 0x05, 0x00, 0xBC, 0xC2, 0x06, 0x00, +0x5E, 0x61, 0x03, 0x00, 0xAF, 0xB0, 0x01, 0x00, +0x57, 0xD8, 0x04, 0x00, 0x2B, 0x6C, 0x06, 0x00, +0x15, 0x36, 0x07, 0x00, 0x0A, 0x9B, 0x07, 0x00, +0x85, 0xCD, 0x03, 0x00, 0xC2, 0xE6, 0x05, 0x00, +0x61, 0xF3, 0x02, 0x00, 0xB0, 0x79, 0x05, 0x00, +0xD8, 0xBC, 0x02, 0x00, 0x6C, 0x5E, 0x01, 0x00, +0x36, 0xAF, 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, +0x14, 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0x52, 0x00, +0x00, 0x00, 0x05, 0x00, 0x19, 0x00, 0x60, 0x81, +0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, +0x49, 0x5A, 0xFF, 0xFF, 0x0F, 0x00, 0x64, 0x00, +0x50, 0x00, 0x50, 0x00, 0x50, 0x00, 0x50, 0x00, +0x50, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, +0x1E, 0x00, 0x23, 0x00, 0x1E, 0x00, 0x01, 0x00, +0xE8, 0x03, 0x02, 0x00, 0x0F, 0x00, 0x50, 0x00, +0x00, 0x00, 0x64, 0x00, 0x64, 0x00, 0x1F, 0x18, +0x0F, 0x00, 0xFA, 0x00, 0x24, 0x00, 0x20, 0x80, +0x2D, 0x00, 0xC8, 0x00, 0xD0, 0x07, 0x02, 0x00, +0x10, 0x00, 0x46, 0x00, 0x2E, 0x00, 0xF4, 0x01, +0x0C, 0x0C, 0x05, 0x00, 0x96, 0x00, 0x2D, 0x00, +0x2D, 0x00, 0xC8, 0x00, 0xC8, 0x00, 0x96, 0x00, +0xF4, 0x01, 0xE8, 0x03, 0x1E, 0x00, 0xFF, 0x7F, +0x01, 0x00, 0x03, 0x00, 0xE8, 0x03, 0x64, 0x00, +0x1E, 0x00, 0x32, 0x00, 0x0F, 0x00, 0x0F, 0x00, +0xE0, 0x01, 0xFA, 0x00, 0x32, 0x00, 0x19, 0x00, +0x1E, 0x00, 0x0A, 0x00, 0x04, 0x00, 0x01, 0x00, +0x02, 0x00, 0x02, 0x00, 0xE8, 0x03, 0x0A, 0x00, +0x07, 0x00, 0x07, 0x00, 0x03, 0x00, 0x10, 0x00, +0x20, 0x00, 0x00, 0x00, 0x6A, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x00, 0x00, 0x68, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0x06, 0x00, 0xAC, 0x0D, 0x0A, 0x00, +0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, +0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x01, 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, 0x64, 0x00, +0x41, 0x79, 0x94, 0x82, 0xB5, 0xC5, 0x65, 0x9C, +0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, +0x1F, 0x00, 0x01, 0x00, 0x03, 0x00, 0x12, 0x00, +0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x64, 0x00, +0x40, 0x01, 0x68, 0x01, 0x01, 0x00, 0x00, 0x00, +0x04, 0x00, 0x32, 0x00, 0x78, 0x00, 0x32, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, +0x3C, 0x00, 0x40, 0x00, 0x40, 0x01, 0x0F, 0x00, +0x3C, 0x00, 0x06, 0x00, 0x05, 0x00, 0x03, 0x00, +0x00, 0x00, 0x3C, 0x00, 0x2C, 0x01, 0x64, 0x00, +0x64, 0x00, 0x28, 0x00, 0x23, 0x00, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xDC, 0x05, 0x64, 0x00, +0x3C, 0x00, 0x1E, 0x00, 0xFF, 0xFF, 0xD8, 0xFF, +0x28, 0x00, 0xFF, 0xFF, 0x1E, 0x00, 0x00, 0x00, +0x48, 0x00, 0x48, 0x00, 0x48, 0x00, 0x48, 0x00, +0x1F, 0x08, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x05, 0x00, 0xB8, 0x0B, 0x32, 0x00, 0xD0, 0x07, +0x32, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xB8, 0x0B, 0x5C, 0x00, 0xFF, 0xFF, +0xF4, 0x01, 0x48, 0xF4, 0xE8, 0x03, 0x0E, 0x00, +0x10, 0x27, 0xB8, 0x0B, 0x10, 0x27, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x1F, 0x18, 0x20, 0x80, +0x1E, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x44, 0x00, +0x4D, 0x00, 0xFF, 0xFF, 0x90, 0x01, 0x64, 0x00, +0xFF, 0xFF, 0x21, 0x01, 0x00, 0x00, 0x01, 0x60, +0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0x00, 0xFF, 0xFF, +0x0F, 0x00, 0xA4, 0x00, 0xFF, 0xFF, 0x00, 0x00, +0x01, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01, 0x00, +0xFF, 0xFF, 0x0F, 0x00, 0x88, 0x1D, 0x30, 0x75, +0x00, 0x00, 0xFA, 0x00, 0xFF, 0xFF, 0xF4, 0x01, +0x05, 0x00, 0x0A, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, +0x05, 0x00, 0x05, 0x00, 0x01, 0x00, 0x30, 0x75, +0x00, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x00, 0x00, +0x00, 0x01, 0x60, 0x00, 0x00, 0x00, 0x0A, 0x00, +0x50, 0x00, 0x1E, 0x00, 0xFF, 0xFF, 0x00, 0x00, +0x32, 0x00, 0xFF, 0xFF, 0xE8, 0x03, 0x01, 0x00, +0x0A, 0x0A, 0x38, 0x00, 0x28, 0x00, 0x32, 0x00, +0x01, 0x00, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x00, +0x0F, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x0F, 0x00, +0x0F, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0xFF, 0xFF, +0x0F, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x0F, 0x00, +0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0xB8, 0x0B, +0x7D, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x05, 0x00, +0xFF, 0xFF, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, +0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x96, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x08, 0x00, +0x0A, 0x00, 0x64, 0x00, 0x11, 0x00, 0x53, 0x00, +0x08, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x15, 0x00, +0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xE8, 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, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 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, 0xFF, 0x08, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0x2C, 0x01, 0x0A, 0x00, +0x03, 0x00, 0x03, 0x00, 0xB8, 0x0B, 0x28, 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, +0xF4, 0x01, 0x38, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0x15, 0x00, 0x16, 0x00, 0x17, 0x00, +0x18, 0x00, 0x19, 0x00, 0x1A, 0x00, 0x1B, 0x00, +0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, +0x24, 0x00, 0x10, 0x00, 0x40, 0x00, 0x08, 0x00, +0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x27, 0x00, +0x28, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, +0x03, 0x00, 0x00, 0x00, 0x24, 0x00, 0x40, 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, +0x01, 0x00, 0x04, 0x00, 0xA4, 0x91, 0x20, 0x01, +0x0A, 0x00, 0x24, 0x00, 0x28, 0x00, 0x5A, 0x00, +0x02, 0x03, 0x0A, 0x00, 0x28, 0x00, 0x96, 0x00, +0x50, 0x00, 0x50, 0x00, 0x1E, 0x00, 0x03, 0x00, +0x08, 0x00, 0x5E, 0x01, 0xB8, 0x0B, 0x96, 0x00, +0x64, 0x00, 0x05, 0x00, 0xFA, 0x00, 0x02, 0x00, +0x07, 0x00, 0x96, 0x00, 0x32, 0x00, 0x2C, 0x01, +0x66, 0xBC, 0x27, 0xA4, 0x1F, 0xDE, 0x31, 0x00, +0x00, 0x01, 0x02, 0x03, 0x04, 0x0E, 0x0F, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0x00, 0xFF, 0x01, 0x02, 0x03, 0xFF, 0x04, 0x05, +0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x60, 0x46, +0x8A, 0x03, 0xC6, 0x97, 0xDD, 0xBB, 0xA0, 0x85, +0x33, 0x00, 0x1E, 0x00, 0x1E, 0x00, 0x14, 0x00, +0x14, 0x00, 0x0A, 0x00, 0x0A, 0x00, 0xFF, 0xFF, +0xFF, 0xFF, 0x64, 0x00, 0xFE, 0x07, 0x01, 0x00, +0x19, 0x00, 0x19, 0x00, 0x84, 0x85, 0xC4, 0x5C, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xB2, 0x26, 0xF4, 0xD4, +0x18, 0x87, 0x75, 0x36, 0x27, 0x2C, 0x5D, 0xB2, +0x46, 0x00, 0x64, 0x00, 0xFA, 0x00, 0x04, 0x00, +0xFF, 0xFF, 0x20, 0x01, 0x32, 0x00, 0x03, 0x00, +0x3C, 0x00, 0x64, 0x00, 0x46, 0x00, 0xC8, 0x00, +0x02, 0x00, 0x96, 0x00, 0x32, 0x00, 0x02, 0x03, +0x33, 0x00, 0x1E, 0x00, 0x1E, 0x00, 0x0A, 0x00, +0xB8, 0x0B, 0x50, 0x00, 0x01, 0x00, 0x78, 0x00, +0x64, 0x00, 0x64, 0x00, 0x00, 0x00, 0x1E, 0x00, +0x1E, 0x00, 0x02, 0x03, 0x03, 0x00, 0x0A, 0x00, +0x0A, 0x00, 0x40, 0x00, 0xE8, 0x03, 0x70, 0x17, +0xFF, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x03, +0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, +0x00, 0x00, 0x07, 0x00, 0x51, 0x00, 0x00, 0x00, +0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0xA0, 0x03, 0x00, 0x0F, +0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xFF, 0xFF, 0xFF, 0xFF, 0x55, 0x55, 0x55, 0x55, +0xAA, 0xAA, 0xAA, 0xAA, 0xFF, 0xFF, 0x55, 0x55, +0xAA, 0xAA, 0x00, 0x00, 0xFF, 0xFF, 0x55, 0x55, +0x55, 0x55, 0xAA, 0xAA, 0xAA, 0xAA, 0xFF, 0xFF, +0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x03, 0x00, 0x0B, 0x00, 0xF0, 0x00, 0xFF, 0x00, +0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x32, 0x02, +0x22, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x03, 0x00, 0x0B, 0x00, 0xF0, 0x00, +0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, +0x32, 0x02, 0x22, 0x00, 0xFF, 0xFF, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x07, 0x00, +0xFF, 0xFF, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, +0x07, 0x07, 0x35, 0x05, 0x55, 0x00, 0xFF, 0xFF, +0x00, 0x00, 0xFF, 0xFF, 0x32, 0x00, 0xDC, 0x05, +0x77, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x1E, 0x00, 0x1E, 0x00, 0x1E, 0x00, +0x1E, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, +0xC8, 0x00, 0x02, 0x00, 0x02, 0x00, 0x06, 0x00, +0x05, 0x00, 0x03, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0xDC, 0xF2, 0xBC, 0xBF, 0x73, 0x61, +0xC5, 0x6B, 0x95, 0xF7, 0x80, 0xC9, 0xA1, 0x10, +0xFF, 0x9E, 0xA3, 0x1A, 0x81, 0x59, 0xF9, 0x75, +0xD9, 0xBA, 0x61, 0xAF, 0x3D, 0x3B, 0xCC, 0x54, +0xAC, 0x7A, 0xB9, 0x6C, 0x33, 0x7F, 0xB7, 0x31, +0xC8, 0x6F, 0x9E, 0x13, 0x10, 0xCC, 0x0C, 0xBE, +0x49, 0xE3, 0xA1, 0xC9, 0xEE, 0x45, 0x73, 0xA9, +0xB9, 0xDA, 0x2A, 0x1B, 0xF8, 0x61, 0x46, 0xAD, +0xED, 0x4F, 0xCD, 0xD5, 0x1D, 0xC3, 0x38, 0x52, +0x93, 0x03, 0x55, 0x1F, 0x9A, 0x66, 0x80, 0x40, +0x14, 0xF9, 0x2F, 0xE4, 0x33, 0x4D, 0x69, 0x33, +0x89, 0x5D, 0xBC, 0xF5, 0x5B, 0xE7, 0xB9, 0x34, +0x7B, 0x90, 0x0B, 0xBA, 0x30, 0xCE, 0x45, 0x68, +0xA0, 0x32, 0x39, 0x69, 0x04, 0x77, 0x65, 0x61, +0xBD, 0xFB, 0x00, 0x00, 0xB7, 0xE3, 0x76, 0xF3, +0x02, 0x07, 0x5C, 0xC6, 0xAF, 0x7E, 0xBB, 0xDD, +0x86, 0x16, 0x0F, 0x16, 0x1C, 0xF8, 0x50, 0xAF, +0x06, 0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, +0xC9, 0x24, 0x02, 0xBC, 0x55, 0x92, 0xE0, 0x77, +0xA2, 0xE4, 0x5B, 0x5E, 0xA9, 0x3C, 0x1F, 0x11, +0xBB, 0x9A, 0xC4, 0x77, 0xF6, 0x7E, 0xE7, 0x38, +0x2D, 0x83, 0x97, 0xDA, 0x70, 0x92, 0x28, 0x4E, +0x05, 0xEB, 0x05, 0xEB, 0x00, 0x00, 0x05, 0xEB, +0x05, 0xEB, 0x05, 0xEB, 0x05, 0xEB, 0x05, 0xEB, +0x05, 0xEB, 0x05, 0xEB, 0x05, 0xEB, 0x05, 0xEB, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xEB, +0x05, 0xEB, 0x05, 0xEB, 0x05, 0xEB, 0x05, 0xEB, +0xD9, 0xDF, 0x93, 0x7B, 0xC8, 0x7C, 0xFF, 0x80, +0xCE, 0x8B, 0x7E, 0x45, 0xD0, 0x8C, 0xFF, 0xFF, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xE9, 0x9D, 0x73, 0xB6, 0xE5, 0x8E, 0x1E, 0xF9, +0xC1, 0xCA, 0xEE, 0x02, 0x02, 0x00, 0x02, 0x00, +0x02, 0x00, 0x01, 0x00, 0x28, 0x93, 0x64, 0x00, +0x0A, 0x00, 0x05, 0x00, 0x6E, 0x00, 0x1E, 0x00, +0x8A, 0x89, 0x58, 0xD0, 0xA2, 0x0B, 0x9D, 0x3D, +0xD6, 0xFF, 0xEA, 0x5E, 0xE0, 0x23, 0xDA, 0x60, +0x19, 0x0B, 0xD5, 0xEC, 0xF7, 0x50, 0xF8, 0xB0, +0x5F, 0x73, 0x24, 0xE3, 0x18, 0x77, 0xF6, 0x6E, +0x0F, 0x00, 0x03, 0x00, 0x03, 0x00, 0x46, 0x00, +0xC8, 0x00, 0x07, 0x00, 0x00, 0x00, 0x55, 0xB1, +0x92, 0xF1, 0xF6, 0xFA, 0x00, 0x00, 0x0F, 0x7F, +0xF8, 0xC7, 0x6A, 0xAA, 0x9D, 0x99, 0xAB, 0xE0, +0xCA, 0x59, 0xF1, 0xCF, 0xDF, 0xB7, 0x4D, 0xD7, +0x73, 0xEF, 0x78, 0x32, 0x4B, 0x00, 0x93, 0xD3, +0xAC, 0xB6, 0x91, 0xCA, 0x5F, 0x0E, 0xCB, 0xC0, +0x37, 0xD0, 0xB6, 0x14, 0xD6, 0x81, 0xAA, 0x27, +0xBD, 0xF0, 0xE7, 0x55, 0x56, 0x0C, 0x0D, 0xE1, +0x44, 0x07, 0x35, 0xE4, 0xA3, 0x35, 0x01, 0x01, +0x15, 0x00, 0x09, 0x00, 0x46, 0x23, 0x09, 0x23, +0xE3, 0x65, 0x87, 0xEE, 0x00, 0x00, 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, +}; +#endif diff --git a/drivers/input/touchscreen/zinitix_ztw62x_mmi/zinitix_ts_mmi.c b/drivers/input/touchscreen/zinitix_ztw62x_mmi/zinitix_ts_mmi.c new file mode 100644 index 000000000000..44d4eccadf98 --- /dev/null +++ b/drivers/input/touchscreen/zinitix_ztw62x_mmi/zinitix_ts_mmi.c @@ -0,0 +1,548 @@ +/* + * Zinitix touchscreen driver MMI class extention + * + * Copyright (C) 2023 Motorola Mobility + * + * 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 "zinitix_ts_mmi.h" + +#define ASSERT_PTR(p) if (p == NULL) { \ + dev_err(dev, "Failed to get driver data"); \ + return -ENODEV; \ + } + +#define ADD_ATTR(name) { \ + if (idx < MAX_ATTRS_ENTRIES) { \ + dev_info(dev, "%s: [%d] adding %p\n", __func__, idx, &dev_attr_##name.attr); \ + ext_attributes[idx] = &dev_attr_##name.attr; \ + idx++; \ + } else { \ + dev_err(dev, "%s: cannot add attribute '%s'\n", __func__, #name); \ + } \ +} + +#define GET_TS_DATA(dev) { \ + ts_info = dev_get_drvdata(dev); \ + if (!ts_info) { \ + dev_err(dev, "Failed to get driver data"); \ + return -ENODEV; \ + } \ +} + + +static struct attribute *ext_attributes[MAX_ATTRS_ENTRIES]; +static struct attribute_group ext_attr_group = { + .attrs = ext_attributes, +}; + +volatile int tpd_halt = 0; + +static int zinitix_ts_mmi_methods_get_vendor(struct device *dev, void *cdata) +{ + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", "zinitix"); +} + +static int zinitix_ts_mmi_methods_get_productinfo(struct device *dev, void *cdata) +{ + char* ic_info; + struct bt541_ts_info *info = dev_get_drvdata(dev); + struct bt541_ts_platform_data *pdata = info->pdata; + ASSERT_PTR(pdata); + + ic_info = strstr(pdata->ic_name, ","); + ic_info++; + + return scnprintf(TO_CHARP(cdata), TS_MMI_MAX_VENDOR_LEN, "%s", ic_info); +} + +static int zinitix_ts_mmi_methods_get_build_id(struct device *dev, void *cdata) { + return snprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%04x", 0); +} + +/*return firmware version*/ +static int zinitix_ts_mmi_methods_get_config_id(struct device *dev, void *cdata) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + struct capa_info *cap = &(info->cap_info); + ASSERT_PTR(cap); + + zinitix_printk("firmware_version = 0x%x, minor_fw_version = 0x%x, data_version_reg = 0x%x\n", + cap->fw_version, cap->fw_minor_version, cap->reg_data_version); + + return snprintf(TO_CHARP(cdata), TS_MMI_MAX_ID_LEN, "%02d%02d%02d%02x", + cap->fw_version, + cap->fw_minor_version, + cap->fw_day_version, + le32_to_cpu(cap->reg_data_version)); +} + +static int zinitix_ts_mmi_methods_get_bus_type(struct device *dev, void *idata) +{ + TO_INT(idata) = TOUCHSCREEN_MMI_BUS_TYPE_I2C; + return 0; +} + +static int zinitix_ts_mmi_methods_get_irq_status(struct device *dev, void *idata) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + struct bt541_ts_platform_data *pdata = info->pdata; + ASSERT_PTR(pdata); + + TO_INT(idata) = gpio_get_value(pdata->gpio_int); + return 0; +} + +static int zinitix_ts_mmi_methods_get_drv_irq(struct device *dev, void *idata) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + ASSERT_PTR(info); + + TO_INT(idata) = info->irq_enabled; + return 0; +} + +static int zinitix_ts_mmi_methods_get_poweron(struct device *dev, void *idata) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + int avdd_gpio = info->pdata->avdd_gpio; + int iovdd_gpio = info->pdata->iovdd_gpio; + + if (avdd_gpio > 0) { + if (gpio_get_value(avdd_gpio) == 0) { + TO_INT(idata) = 0; + return 0; + } + } + if (iovdd_gpio > 0) { + if (gpio_get_value(iovdd_gpio) == 0) { + TO_INT(idata) = 0; + return 0; + } + } + + if (info->vdd != NULL) { + if(!regulator_is_enabled(info->vdd)) { + TO_INT(idata) = 0; + return 0; + } + } + if (info->vcc_i2c != NULL) { + if(!regulator_is_enabled(info->vcc_i2c)) { + TO_INT(idata) = 0; + return 0; + } + } + + TO_INT(idata) = 1; + return 0; +} + +static int zinitix_ts_mmi_methods_get_flashprog(struct device *dev, void *idata) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + ASSERT_PTR(info); + + TO_INT(idata) = info->work_state == UPGRADE ? 1 : 0; + return 0; +} + +static int zinitix_ts_mmi_methods_reset(struct device *dev, int type) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + ASSERT_PTR(info); + + zinitix_hw_reset(info,true); + return 0; +} + +static int zinitix_ts_firmware_update(struct device *dev, char *fwname) +{ + int ret = 0; + u8 *fw_file_buf = NULL; + struct bt541_ts_info *info = dev_get_drvdata(dev); + ASSERT_PTR(info); + + dev_info(dev, "%s, HW request update fw, %s", __func__, fwname); + + if (!fwname) { + dev_err(dev, "%s: Error, firmware name is null\n", + __func__); + return -EINVAL; + } + + ret= zinitix_read_file(fwname, &fw_file_buf); + if (ret != info->cap_info.ic_fw_size) { + dev_err(dev, "%s: invalid fw size!!\n", __func__); + goto error_upgrade; + } + + dev_info(dev, "%s: fw is loaded!!\n", __func__); + info->checkUMSmode = true; + ret = ts_upgrade_sequence((u8 *)fw_file_buf); + info->checkUMSmode = false; + if(ret<0) { + dev_err(dev, "%s: fw upgrade fail!!\n", __func__); + goto error_upgrade; + } + dev_info(dev, "%s: fw upgrade success!!\n", __func__); + +error_upgrade: + if (fw_file_buf) { + vfree(fw_file_buf); + fw_file_buf = NULL; + } + + return ret; +} + +static int zinitix_ts_mmi_methods_power(struct device *dev, int on) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + ASSERT_PTR(info); + + info->ts_mmi_power_state = on; + schedule_delayed_work(&info->work, 0); + + return 0; +} + +static int zinitix_ts_mmi_methods_drv_irq(struct device *dev, int state) +{ + int ret = 0; + struct bt541_ts_info *info = dev_get_drvdata(dev); + ASSERT_PTR(info); + + if (state == 1) { + dev_info(dev, "%s: enable irq \n", __func__); + if (info->irq_enabled == false) { + enable_irq(info->irq); + info->irq_enabled = true; + } + } else if (state == 0) { + dev_info(dev, "%s: disable irq \n", __func__); + if (info->irq_enabled == true) { + disable_irq(info->irq); + info->irq_enabled = false; + } + } else { + dev_err(dev, "%s: invalid value\n", __func__); + ret = -EINVAL; + } + + return ret; +} + +static int zinitix_ts_mmi_panel_state(struct device *dev, + enum ts_mmi_pm_mode from, enum ts_mmi_pm_mode to) +{ + unsigned char gesture_type = 0; + struct bt541_ts_info *info = dev_get_drvdata(dev); + ASSERT_PTR(info); + + switch (to) { + case TS_MMI_PM_GESTURE: + if (info->imports && info->imports->get_gesture_type) { + if (info->imports->get_gesture_type(dev, &gesture_type) < 0) { + dev_err(dev, "%s: get gesture type error.\n", __func__); + return -EINVAL; + } + } + + info->gesture_command = 0; + + if (gesture_type & TS_MMI_GESTURE_SINGLE) { + info->gesture_command |= 0x0089; + dev_info(dev, "Enable GESTURE_CLI_SINGLE command: 0x%04x", info->gesture_command); + } + if (gesture_type & TS_MMI_GESTURE_DOUBLE) { + info->gesture_command |= 0x0091; + dev_info(dev, "Enable GESTURE_CLI_DOUBLE command: 0x%04x", info->gesture_command); + } + dev_info(dev, "CLI GESTURE SWITCH command: 0x%04x", info->gesture_command); + + if (zinitix_ts_mmi_gesture_suspend(dev) < 0) { + dev_err(dev, "%s: Gesture suspend error.\n", __func__); + return -EINVAL; + } + dev_info(dev, "%s: Gesture suspend success.\n", __func__); + info->gesture_enabled = true; + break; + + case TS_MMI_PM_DEEPSLEEP: + dev_info(dev, "%s: Enter TS_MMI_PM_DEEPSLEEP mode\n", __func__); + info->gesture_enabled = false; + break; + + case TS_MMI_PM_ACTIVE: + if (info->gesture_enabled == true) { + if (zinitix_ts_mmi_gesture_resume(dev) < 0) { + dev_err(dev, "%s: Gesture resume error.\n", __func__); + return -EINVAL; + } + dev_info(dev, "%s: Gesture resume success.\n", __func__); + info->gesture_enabled = false; + } + break; + + default: + dev_err(dev, "%s: Invalid power state parameter %d.\n", __func__, to); + return -EINVAL; + } + + return 0; +} + +static int zinitix_ts_mmi_pre_suspend(struct device *dev) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + ASSERT_PTR(info); + + dev_info(dev, "%s: Suspend start \n", __func__); + + down(&info->work_lock); + tpd_halt = 1; + //stop ESD + #if ESD_TIMER_INTERVAL + flush_work(&info->tmr_work); + esd_timer_stop(info); + #endif + up(&info->work_lock); + + return 0; +} + +static int zinitix_ts_mmi_post_suspend(struct device *dev) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + ASSERT_PTR(info); + + down(&info->work_lock); + //clear report data + clear_report_data(info); + //set work state + info->work_state = SUSPEND; + up(&info->work_lock); + + //set stow mode + mutex_lock(&info->mode_lock); + if ((info->pdata->stow_mode_ctrl) && (info->ic_power_state == TS_MMI_POWER_ON) && + atomic_read(&info->get_stowed_state)) { + dev_info(dev, "%s: stowed, disable all gestures after suspend 0x%04x\n", __func__, + info->gesture_command); + zinitix_ts_mmi_disable_gesture(dev); + atomic_set(&info->set_stowed_state, 1); + } + mutex_unlock(&info->mode_lock); + + dev_info(dev, "%s: Suspend end \n", __func__); + + return 0; +} + +static int zinitix_ts_mmi_pre_resume(struct device *dev) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + ASSERT_PTR(info); + + down(&info->work_lock); + dev_info(dev, "%s: Resume start\n", __func__); + info->work_state = RESUME; + up(&info->work_lock); + + return 0; +} + +static int zinitix_ts_mmi_post_resume(struct device *dev) +{ + struct bt541_ts_info *info = dev_get_drvdata(dev); + ASSERT_PTR(info); + + down(&info->work_lock); + #if ESD_TIMER_INTERVAL + esd_timer_start(CHECK_ESD_TIMER, info); + #endif + tpd_halt = 0; + info->work_state = NOTHING; + //Clear stowed state + atomic_set(&info->set_stowed_state, 0); + up(&info->work_lock); + dev_info(dev, "%s: Resume end\n", __func__); + return 0; +} + +static ssize_t zinitix_ts_stowed_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bt541_ts_info *ts_info; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + ASSERT_PTR(ts_info); + + return scnprintf(buf, PAGE_SIZE, "stowed state:%d\n", atomic_read(&ts_info->set_stowed_state)); +} + +static ssize_t zinitix_ts_stowed_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + unsigned int mode = 0; + int err = 0; + struct bt541_ts_info *ts_info; + + dev = MMI_DEV_TO_TS_DEV(dev); + GET_TS_DATA(dev); + + err = sscanf(buf, "%d", &mode); + if (err < 0) { + dev_err(dev, "stowed: Failed to convert value\n"); + return -EINVAL; + } + + mutex_lock(&ts_info->mode_lock); + + atomic_set(&ts_info->get_stowed_state, !!mode); + + if (atomic_read(&ts_info->set_stowed_state) == atomic_read(&ts_info->get_stowed_state)) { + dev_info(dev, "%s: Stowed mode is same, not to set \n", __func__); + mutex_unlock(&ts_info->mode_lock); + return size; + } + + if ((ts_info->ic_power_state == TS_MMI_POWER_ON) && (ts_info->work_state == SUSPEND)) { + if (mode) { + dev_info(dev, "%s: stowed, disable all gestures 0x%04x\n", __func__, ts_info->gesture_command); + zinitix_ts_mmi_disable_gesture(dev); + } else { + dev_info(dev, "%s: unstowed, restore gestures 0x%04x\n", __func__, ts_info->gesture_command); + zinitix_ts_mmi_restore_gesture(dev); + } + atomic_set(&ts_info->set_stowed_state, !!mode); + } else { + dev_info(dev, "%s: touch state is not proper to enter stow mode, work state:%d, \ + ic power state: %d\n", __func__, ts_info->work_state, ts_info->ic_power_state); + } + mutex_unlock(&ts_info->mode_lock); + + return size; +} +static DEVICE_ATTR(stowed, (S_IWUSR | S_IWGRP | S_IRUGO), zinitix_ts_stowed_show, + zinitix_ts_stowed_store); + +static int zinitix_mmi_extend_attribute_group(struct device *dev, + struct attribute_group **group) +{ + int idx = 0; + struct bt541_ts_info *info = dev_get_drvdata(dev); + struct bt541_ts_platform_data *pdata = info->pdata; + ASSERT_PTR(pdata); + + if (pdata->stow_mode_ctrl) + ADD_ATTR(stowed); + + if (idx) { + ext_attributes[idx] = NULL; + *group = &ext_attr_group; + } else + *group = NULL; + + return 0; +} + +static struct ts_mmi_methods zinitix_ts_mmi_methods = { + .get_vendor = zinitix_ts_mmi_methods_get_vendor, + .get_productinfo = zinitix_ts_mmi_methods_get_productinfo, + .get_build_id = zinitix_ts_mmi_methods_get_build_id, + .get_config_id = zinitix_ts_mmi_methods_get_config_id, + .get_bus_type = zinitix_ts_mmi_methods_get_bus_type, + .get_irq_status = zinitix_ts_mmi_methods_get_irq_status, + .get_drv_irq = zinitix_ts_mmi_methods_get_drv_irq, + .get_poweron = zinitix_ts_mmi_methods_get_poweron, + .get_flashprog = zinitix_ts_mmi_methods_get_flashprog, + /* SET methods */ + .reset = zinitix_ts_mmi_methods_reset, + .power = zinitix_ts_mmi_methods_power, + .drv_irq = zinitix_ts_mmi_methods_drv_irq, + /* Firmware */ + .firmware_update = zinitix_ts_firmware_update, + /* vendor specific attribute group */ + .extend_attribute_group = zinitix_mmi_extend_attribute_group, + /* PM callback */ + .panel_state = zinitix_ts_mmi_panel_state, + .pre_suspend = zinitix_ts_mmi_pre_suspend, + .post_suspend = zinitix_ts_mmi_post_suspend, + .pre_resume = zinitix_ts_mmi_pre_resume, + .post_resume = zinitix_ts_mmi_post_resume, +}; + +static void ts_mmi_worker_func(struct work_struct *w) +{ + struct delayed_work *dw = + container_of(w, struct delayed_work, work); + struct bt541_ts_info *info = + container_of(dw, struct bt541_ts_info, work); + struct i2c_client *client = info->client; + + down(&info->work_lock); + + if (info->ts_mmi_power_state == TS_MMI_POWER_ON) + { + dev_info(&client->dev, "%s: Power on touch IC \n", __func__); + bt541_power_control(info, POWER_ON_SEQUENCE); + mini_init_touch(info); + } + else if(info->ts_mmi_power_state == TS_MMI_POWER_OFF) + { + dev_info(&client->dev, "%s: Power off touch IC \n", __func__); + bt541_power_control(info, POWER_OFF); + } + + up(&info->work_lock); +} + +int zinitix_ts_mmi_dev_register(struct device *dev) { + int ret; + struct bt541_ts_info *info = dev_get_drvdata(dev); + ASSERT_PTR(info); + + INIT_DELAYED_WORK(&info->work, ts_mmi_worker_func); + + mutex_init(&info->mode_lock); + ret = ts_mmi_dev_register(dev, &zinitix_ts_mmi_methods); + if (ret) { + dev_err(dev, "%s, Failed to register ts mmi\n", __func__); + mutex_destroy(&info->mode_lock); + return ret; + } + dev_info(dev, "%s, Register ts mmi success\n", __func__); + + info->imports = &zinitix_ts_mmi_methods.exports; + +#if defined(CONFIG_GTP_LIMIT_USE_SUPPLIER) + if (info->imports && info->imports->get_supplier) { + ret = info->imports->get_supplier(dev, &info->supplier); + } +#endif + + return 0; +} + +void zinitix_ts_mmi_dev_unregister(struct device *dev) { + struct bt541_ts_info *info = dev_get_drvdata(dev); + + if (!info) + dev_err(dev ,"Failed to get driver data"); + mutex_destroy(&info->mode_lock); + ts_mmi_dev_unregister(dev); +} + diff --git a/drivers/input/touchscreen/zinitix_ztw62x_mmi/zinitix_ts_mmi.h b/drivers/input/touchscreen/zinitix_ztw62x_mmi/zinitix_ts_mmi.h new file mode 100644 index 000000000000..010ebb946800 --- /dev/null +++ b/drivers/input/touchscreen/zinitix_ztw62x_mmi/zinitix_ts_mmi.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 Motorola Mobility LLC + * + * 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. + */ +#ifndef __ZINITIX_TS_MMI_H__ +#define __ZINITIX_TS_MMI_H__ + +#include +#include +#include "zinitix_touch.h" + +#define MAX_ATTRS_ENTRIES 10 + +#ifdef CONFIG_INPUT_TOUCHSCREEN_MMI +int zinitix_ts_mmi_dev_register(struct device *dev); +void zinitix_ts_mmi_dev_unregister(struct device *dev); +#else +static int inline zinitix_ts_mmi_dev_register(struct device *dev) { + return -ENOSYS; +} +static void inline zinitix_ts_mmi_dev_unregister(struct device *dev) { + return ; +} +#endif + +#endif diff --git a/drivers/leds/Android.mk b/drivers/leds/Android.mk new file mode 100755 index 000000000000..7a3fdf084441 --- /dev/null +++ b/drivers/leds/Android.mk @@ -0,0 +1,10 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := leds-indicator-pwm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/drivers/leds/Kbuild b/drivers/leds/Kbuild new file mode 100755 index 000000000000..eb1a7d0ae20c --- /dev/null +++ b/drivers/leds/Kbuild @@ -0,0 +1,6 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + + +obj-m += leds-indicator-pwm.o diff --git a/drivers/leds/aw2033/Android.mk b/drivers/leds/aw2033/Android.mk new file mode 100755 index 000000000000..7e9711239eb3 --- /dev/null +++ b/drivers/leds/aw2033/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := leds_aw2033.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/leds/aw2033/Kbuild b/drivers/leds/aw2033/Kbuild new file mode 100755 index 000000000000..5091ce3fb4ae --- /dev/null +++ b/drivers/leds/aw2033/Kbuild @@ -0,0 +1,6 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + + +obj-m += leds_aw2033.o diff --git a/drivers/leds/aw2033/leds_aw2033.c b/drivers/leds/aw2033/leds_aw2033.c new file mode 100644 index 000000000000..d0d2f4219628 --- /dev/null +++ b/drivers/leds/aw2033/leds_aw2033.c @@ -0,0 +1,657 @@ +/* + * Copyright (c) 2017, The Linux Foundation. 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 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "leds_aw2033.h" + +#define AW2033_DRIVER_VERSION "V1.0.2" + +/* register address */ +#define AW2033_REG_RESET 0x00 +#define AW2033_REG_GCR1 0x01 +#define AW2033_REG_STATUS 0x02 +#define AW2033_REG_PATST 0x03 +#define AW2033_REG_GCR2 0x04 +#define AW2033_REG_LEDEN 0x30 +#define AW2033_REG_LCFG0 0x31 +#define AW2033_REG_LCFG1 0x32 +#define AW2033_REG_LCFG2 0x33 +#define AW2033_REG_PWM0 0x34 +#define AW2033_REG_PWM1 0x35 +#define AW2033_REG_PWM2 0x36 +#define AW2033_REG_LED0T0 0x37 +#define AW2033_REG_LED0T1 0x38 +#define AW2033_REG_LED0T2 0x39 +#define AW2033_REG_LED1T0 0x3A +#define AW2033_REG_LED1T1 0x3B +#define AW2033_REG_LED1T2 0x3C +#define AW2033_REG_LED2T0 0x3D +#define AW2033_REG_LED2T1 0x3E +#define AW2033_REG_LED2T2 0x3F + +/* register bits */ +#define AW2033_CHIPID 0x09 +#define AW2033_RESET_MASK 0x55 +#define AW2033_CHIP_DISABLE_MASK 0x00 +#define AW2033_CHIP_ENABLE_MASK 0x01 +#define AW2033_CHARGE_DISABLE_MASK 0x02 +#define AW2033_LED_BREATH_MODE_MASK 0x10 +#define AW2033_LED_MANUAL_MODE_MASK 0x00 +#define AW2033_LED_BREATHE_PWM_MASK 0xFF +#define AW2033_LED_MANUAL_PWM_MASK 0xFF +#define AW2033_LED_FADEIN_MODE_MASK 0x20 +#define AW2033_LED_FADEOUT_MODE_MASK 0x40 +#define AW2033_CLEAR_MASK 0x00 + +#define MAX_RISE_TIME_MS 15 +#define MAX_HOLD_TIME_MS 15 +#define MAX_FALL_TIME_MS 15 +#define MAX_OFF_TIME_MS 15 + +/* aw2033 register read/write access*/ +#define REG_NONE_ACCESS 0 +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) +#define AW2033_REG_MAX 0x7F + +const unsigned char aw2033_reg_access[AW2033_REG_MAX] = { + [AW2033_REG_RESET] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW2033_REG_GCR1] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW2033_REG_STATUS] = REG_RD_ACCESS, + [AW2033_REG_PATST] = REG_RD_ACCESS, + [AW2033_REG_GCR2] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW2033_REG_LEDEN] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW2033_REG_LCFG0] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW2033_REG_LCFG1] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW2033_REG_LCFG2] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW2033_REG_PWM0] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW2033_REG_PWM1] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW2033_REG_PWM2] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW2033_REG_LED0T0] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW2033_REG_LED0T1] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW2033_REG_LED0T2] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW2033_REG_LED1T0] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW2033_REG_LED1T1] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW2033_REG_LED1T2] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW2033_REG_LED2T0] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW2033_REG_LED2T1] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW2033_REG_LED2T2] = REG_RD_ACCESS|REG_WR_ACCESS, +}; +struct aw2033_led { + struct i2c_client *client; + struct led_classdev cdev; + struct aw2033_platform_data *pdata; + struct work_struct brightness_work; + struct mutex lock; + int num_leds; + int id; +}; + +static int aw2033_write(struct aw2033_led *led, u8 reg, u8 val) +{ + int ret = -EINVAL, retry_times = 0; + + do { + ret = i2c_smbus_write_byte_data(led->client, reg, val); + retry_times++; + if (retry_times == 5) + break; + } while (ret < 0); + + return ret; +} + +static int aw2033_read(struct aw2033_led *led, u8 reg, u8 *val) +{ + int ret = -EINVAL, retry_times = 0; + + do { + ret = i2c_smbus_read_byte_data(led->client, reg); + retry_times++; + if (retry_times == 5) + break; + } while (ret < 0); + if (ret < 0) + return ret; + + *val = ret; + return 0; +} + +static void aw2033_soft_reset(struct aw2033_led *led) +{ + int reg; + + /* don't use reset reg, it cause CHGDIS enable */ + /* aw2033_write(led, AW2033_REG_RESET, AW2033_RESET_MASK); */ + aw2033_write(led, AW2033_REG_GCR1, AW2033_CHARGE_DISABLE_MASK); + aw2033_write(led, AW2033_REG_GCR2, AW2033_CLEAR_MASK); + + for (reg = AW2033_REG_LEDEN; reg <= AW2033_REG_LED2T2; reg++) + aw2033_write(led, reg, AW2033_CLEAR_MASK); + + /* msleep(5); */ /* don't need delay anymore*/ +} + +static void aw2033_brightness_work(struct work_struct *work) +{ + struct aw2033_led *led = container_of(work, struct aw2033_led, + brightness_work); + u8 val; + + mutex_lock(&led->pdata->led->lock); + + /* enable aw2033 if disabled */ + aw2033_read(led, AW2033_REG_GCR1, &val); + /*only enable when brightness is bigger than 0, avoid blink when boot */ + if ((led->cdev.brightness > 0) && !(val&AW2033_CHIP_ENABLE_MASK)) { + aw2033_write(led, AW2033_REG_GCR1, + AW2033_CHARGE_DISABLE_MASK | AW2033_CHIP_ENABLE_MASK); + msleep(2); + } + + if (led->cdev.brightness > 0) { + if (led->cdev.brightness > led->cdev.max_brightness) + led->cdev.brightness = led->cdev.max_brightness; + aw2033_write(led, AW2033_REG_GCR2, led->pdata->imax); + aw2033_write(led, AW2033_REG_LCFG0 + led->id, + (AW2033_LED_MANUAL_MODE_MASK | led->pdata->led_current)); + aw2033_write(led, AW2033_REG_PWM0 + led->id, led->cdev.brightness); + aw2033_read(led, AW2033_REG_LEDEN, &val); + aw2033_write(led, AW2033_REG_LEDEN, val | (1 << led->id)); + } else { + aw2033_read(led, AW2033_REG_LEDEN, &val); + aw2033_write(led, AW2033_REG_LEDEN, val & (~(1 << led->id))); + } + + /* + * If value in AW_REG_LED_ENABLE is 0, it means the RGB leds are + * all off. So we need to power it off. + */ + aw2033_read(led, AW2033_REG_LEDEN, &val); + if (val == 0) { + aw2033_write(led, AW2033_REG_GCR1, + AW2033_CHARGE_DISABLE_MASK | AW2033_CHIP_DISABLE_MASK); + mutex_unlock(&led->pdata->led->lock); + return; + } + + mutex_unlock(&led->pdata->led->lock); +} + +static void aw2033_led_blink_set(struct aw2033_led *led, unsigned long blinking) +{ + u8 val; + + + /* enable aw2033 if disabled */ + aw2033_read(led, AW2033_REG_GCR1, &val); + /*only enable when blinking is bigger than 0, avoid blink when boot */ + if ((blinking > 0) && !(val&AW2033_CHIP_ENABLE_MASK)) { + aw2033_write(led, AW2033_REG_GCR1, + AW2033_CHARGE_DISABLE_MASK | AW2033_CHIP_ENABLE_MASK); + msleep(2); + } + + led->cdev.brightness = blinking; + if (led->cdev.brightness > led->cdev.max_brightness) + led->cdev.brightness = led->cdev.max_brightness; + pr_info("aw2033_led_blink_set %ld %d\n", blinking, led->cdev.brightness); + if (blinking > 0) { + aw2033_write(led, AW2033_REG_GCR2, led->pdata->imax); + aw2033_write(led, AW2033_REG_PWM0 + led->id, led->cdev.brightness); + aw2033_write(led, AW2033_REG_LED0T0 + led->id*3, + (led->pdata->rise_time_ms << 4 | led->pdata->hold_time_ms)); + aw2033_write(led, AW2033_REG_LED0T1 + led->id*3, + (led->pdata->fall_time_ms << 4 | led->pdata->off_time_ms)); + aw2033_write(led, AW2033_REG_LCFG0 + led->id, + (AW2033_LED_BREATH_MODE_MASK | led->pdata->led_current)); + aw2033_read(led, AW2033_REG_LEDEN, &val); + aw2033_write(led, AW2033_REG_LEDEN, val | (1 << led->id)); + } else { + aw2033_read(led, AW2033_REG_LEDEN, &val); + aw2033_write(led, AW2033_REG_LEDEN, val & (~(1 << led->id))); + } + + /* + * If value in AW_REG_LED_ENABLE is 0, it means the RGB leds are + * all off. So we need to power it off. + */ + aw2033_read(led, AW2033_REG_LEDEN, &val); + if (val == 0) { + aw2033_write(led, AW2033_REG_GCR1, + AW2033_CHARGE_DISABLE_MASK | AW2033_CHIP_DISABLE_MASK); + } +} + +static void aw2033_set_brightness(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct aw2033_led *led = container_of(cdev, struct aw2033_led, cdev); + + led->cdev.brightness = brightness; + pr_info("aw2033_set_brightness %d\n", brightness); + + schedule_work(&led->brightness_work); +} + +static ssize_t aw2033_store_blink(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + unsigned long blinking; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct aw2033_led *led = + container_of(led_cdev, struct aw2033_led, cdev); + ssize_t ret = -EINVAL; + + ret = kstrtoul(buf, 10, &blinking); + if (ret) + return ret; + mutex_lock(&led->pdata->led->lock); + aw2033_led_blink_set(led, blinking); + mutex_unlock(&led->pdata->led->lock); + + return len; +} + +static ssize_t aw2033_led_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct aw2033_led *led = + container_of(led_cdev, struct aw2033_led, cdev); + + return snprintf(buf, PAGE_SIZE, "%d %d %d %d\n", + led->pdata->rise_time_ms, led->pdata->hold_time_ms, + led->pdata->fall_time_ms, led->pdata->off_time_ms); +} + +static ssize_t aw2033_led_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct aw2033_led *led = + container_of(led_cdev, struct aw2033_led, cdev); + int rc, rise_time_ms, hold_time_ms, fall_time_ms, off_time_ms; + + rc = sscanf(buf, "%d %d %d %d", + &rise_time_ms, &hold_time_ms, + &fall_time_ms, &off_time_ms); + + mutex_lock(&led->pdata->led->lock); + led->pdata->rise_time_ms = (rise_time_ms > MAX_RISE_TIME_MS) ? + MAX_RISE_TIME_MS : rise_time_ms; + led->pdata->hold_time_ms = (hold_time_ms > MAX_HOLD_TIME_MS) ? + MAX_HOLD_TIME_MS : hold_time_ms; + led->pdata->fall_time_ms = (fall_time_ms > MAX_FALL_TIME_MS) ? + MAX_FALL_TIME_MS : fall_time_ms; + led->pdata->off_time_ms = (off_time_ms > MAX_OFF_TIME_MS) ? + MAX_OFF_TIME_MS : off_time_ms; + aw2033_led_blink_set(led, 1); + mutex_unlock(&led->pdata->led->lock); + return len; +} + +static ssize_t aw2033_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct aw2033_led *led = + container_of(led_cdev, struct aw2033_led, cdev); + + unsigned char i, reg_val; + ssize_t len = 0; + + for (i = 0; i < AW2033_REG_MAX; i++) { + if (!(aw2033_reg_access[i] & REG_RD_ACCESS)) + continue; + aw2033_read(led, i, ®_val); + len += snprintf(buf + len, PAGE_SIZE - len, "reg:0x%02x=0x%02x\n", i, reg_val); + } + + return len; +} + +static ssize_t aw2033_reg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct aw2033_led *led = + container_of(led_cdev, struct aw2033_led, cdev); + + unsigned int databuf[2]; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) + aw2033_write(led, (unsigned char)databuf[0], (unsigned char)databuf[1]); + + return len; +} +static DEVICE_ATTR(blink, 0664, NULL, aw2033_store_blink); +static DEVICE_ATTR(led_time, 0664, aw2033_led_time_show, aw2033_led_time_store); +static DEVICE_ATTR(reg, 0664, aw2033_reg_show, aw2033_reg_store); + +static struct attribute *aw2033_led_attributes[] = { + &dev_attr_blink.attr, + &dev_attr_led_time.attr, + &dev_attr_reg.attr, + NULL, +}; + +static struct attribute_group aw2033_led_attr_group = { + .attrs = aw2033_led_attributes +}; + +static int aw2033_check_chipid(struct aw2033_led *led) +{ + u8 val; + u8 cnt; + + for (cnt = 5; cnt > 0; cnt--) { + aw2033_read(led, AW2033_REG_RESET, &val); + dev_notice(&led->client->dev, "aw2033 chip id %0x", val); + if (val == AW2033_CHIPID) + return 0; + } + return -EINVAL; +} + +static int aw2033_led_err_handle(struct aw2033_led *led_array, + int parsed_leds) +{ + int i; + /* + * If probe fails, cannot free resource of all LEDs, only free + * resources of LEDs which have allocated these resource really. + */ + for (i = 0; i < parsed_leds; i++) { + sysfs_remove_group(&led_array[i].cdev.dev->kobj, + &aw2033_led_attr_group); + led_classdev_unregister(&led_array[i].cdev); + cancel_work_sync(&led_array[i].brightness_work); + devm_kfree(&led_array->client->dev, led_array[i].pdata); + led_array[i].pdata = NULL; + } + return i; +} + +static int aw2033_led_parse_child_node(struct aw2033_led *led_array, + struct device_node *node) +{ + struct aw2033_led *led; + struct device_node *temp; + struct aw2033_platform_data *pdata; + int rc = 0, parsed_leds = 0; + + for_each_child_of_node(node, temp) { + led = &led_array[parsed_leds]; + led->client = led_array->client; + + pdata = devm_kzalloc(&led->client->dev, + sizeof(struct aw2033_platform_data), + GFP_KERNEL); + if (!pdata) { + dev_err(&led->client->dev, + "Failed to allocate memory\n"); + goto free_err; + } + pdata->led = led_array; + led->pdata = pdata; + + rc = of_property_read_string(temp, "aw2033,name", + &led->cdev.name); + if (rc < 0) { + dev_err(&led->client->dev, + "Failure reading led name, rc = %d\n", rc); + goto free_pdata; + } + + rc = of_property_read_u32(temp, "aw2033,id", + &led->id); + if (rc < 0) { + dev_err(&led->client->dev, + "Failure reading id, rc = %d\n", rc); + goto free_pdata; + } + + rc = of_property_read_u32(temp, "aw2033,imax", + &led->pdata->imax); + if (rc < 0) { + dev_err(&led->client->dev, + "Failure reading id, rc = %d\n", rc); + goto free_pdata; + } + + rc = of_property_read_u32(temp, "aw2033,led-current", + &led->pdata->led_current); + if (rc < 0) { + dev_err(&led->client->dev, + "Failure reading led-current, rc = %d\n", rc); + goto free_pdata; + } + + rc = of_property_read_u32(temp, "aw2033,max-brightness", + &led->cdev.max_brightness); + if (rc < 0) { + dev_err(&led->client->dev, + "Failure reading max-brightness, rc = %d\n", + rc); + goto free_pdata; + } + + rc = of_property_read_u32(temp, "aw2033,rise-time-ms", + &led->pdata->rise_time_ms); + if (rc < 0) { + dev_err(&led->client->dev, + "Failure reading rise-time-ms, rc = %d\n", rc); + goto free_pdata; + } + + rc = of_property_read_u32(temp, "aw2033,hold-time-ms", + &led->pdata->hold_time_ms); + if (rc < 0) { + dev_err(&led->client->dev, + "Failure reading hold-time-ms, rc = %d\n", rc); + goto free_pdata; + } + + rc = of_property_read_u32(temp, "aw2033,fall-time-ms", + &led->pdata->fall_time_ms); + if (rc < 0) { + dev_err(&led->client->dev, + "Failure reading fall-time-ms, rc = %d\n", rc); + goto free_pdata; + } + + rc = of_property_read_u32(temp, "aw2033,off-time-ms", + &led->pdata->off_time_ms); + if (rc < 0) { + dev_err(&led->client->dev, + "Failure reading off-time-ms, rc = %d\n", rc); + goto free_pdata; + } + + INIT_WORK(&led->brightness_work, aw2033_brightness_work); + + led->cdev.brightness_set = aw2033_set_brightness; + + rc = led_classdev_register(&led->client->dev, &led->cdev); + if (rc) { + dev_err(&led->client->dev, + "unable to register led %d,rc=%d\n", + led->id, rc); + goto free_pdata; + } + + rc = sysfs_create_group(&led->cdev.dev->kobj, + &aw2033_led_attr_group); + if (rc) { + dev_err(&led->client->dev, "led sysfs rc: %d\n", rc); + goto free_class; + } + parsed_leds++; + } + + return 0; + +free_class: + aw2033_led_err_handle(led_array, parsed_leds); + led_classdev_unregister(&led_array[parsed_leds].cdev); + cancel_work_sync(&led_array[parsed_leds].brightness_work); + devm_kfree(&led->client->dev, led_array[parsed_leds].pdata); + led_array[parsed_leds].pdata = NULL; + return rc; + +free_pdata: + aw2033_led_err_handle(led_array, parsed_leds); + devm_kfree(&led->client->dev, led_array[parsed_leds].pdata); + return rc; + +free_err: + aw2033_led_err_handle(led_array, parsed_leds); + return rc; +} + +static int aw2033_led_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct aw2033_led *led_array; + struct device_node *node; + int ret, num_leds = 0; + + node = client->dev.of_node; + if (node == NULL) + return -EINVAL; + + num_leds = of_get_child_count(node); + + if (!num_leds) + return -EINVAL; + + led_array = devm_kzalloc(&client->dev, + (sizeof(struct aw2033_led) * num_leds), GFP_KERNEL); + if (!led_array) + return -ENOMEM; + + led_array->client = client; + led_array->num_leds = num_leds; + + mutex_init(&led_array->lock); + + ret = aw2033_led_parse_child_node(led_array, node); + if (ret) { + dev_err(&client->dev, "parsed node error\n"); + goto free_led_arry; + } + + i2c_set_clientdata(client, led_array); + + ret = aw2033_check_chipid(led_array); + if (ret) { + dev_err(&client->dev, "Check chip id error\n"); + goto fail_parsed_node; + } + + /* soft rst */ + aw2033_soft_reset(led_array); + + return 0; + +fail_parsed_node: + aw2033_led_err_handle(led_array, num_leds); +free_led_arry: + mutex_destroy(&led_array->lock); + devm_kfree(&client->dev, led_array); + led_array = NULL; + return ret; +} + +static int aw2033_led_remove(struct i2c_client *client) +{ + struct aw2033_led *led_array = i2c_get_clientdata(client); + int i, parsed_leds = led_array->num_leds; + + for (i = 0; i < parsed_leds; i++) { + sysfs_remove_group(&led_array[i].cdev.dev->kobj, + &aw2033_led_attr_group); + led_classdev_unregister(&led_array[i].cdev); + cancel_work_sync(&led_array[i].brightness_work); + devm_kfree(&client->dev, led_array[i].pdata); + led_array[i].pdata = NULL; + } + mutex_destroy(&led_array->lock); + devm_kfree(&client->dev, led_array); + led_array = NULL; + return 0; +} + +static void aw2033_led_shutdown(struct i2c_client *client) +{ + struct aw2033_led *led_array = i2c_get_clientdata(client); + + pr_info("aw2033 shutdown start.\n"); + aw2033_write(led_array, AW2033_REG_GCR1, + AW2033_CHARGE_DISABLE_MASK | AW2033_CHIP_DISABLE_MASK); + pr_info("aw2033 shutdown done.\n"); +} + +static const struct i2c_device_id aw2033_led_id[] = { + {"aw2033_led", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, aw2033_led_id); + +static struct of_device_id aw2033_match_table[] = { + { .compatible = "awinic,aw2033_led",}, + { }, +}; + +static struct i2c_driver aw2033_led_driver = { + .probe = aw2033_led_probe, + .remove = aw2033_led_remove, + .shutdown = aw2033_led_shutdown, + .driver = { + .name = "aw2033_led", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw2033_match_table), + }, + .id_table = aw2033_led_id, +}; + +static int __init aw2033_led_init(void) +{ + pr_info("%s: driver version: %s\n", __func__, AW2033_DRIVER_VERSION); + return i2c_add_driver(&aw2033_led_driver); +} +module_init(aw2033_led_init); + +static void __exit aw2033_led_exit(void) +{ + i2c_del_driver(&aw2033_led_driver); +} +module_exit(aw2033_led_exit); + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("AWINIC AW2033 LED driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/leds/aw2033/leds_aw2033.h b/drivers/leds/aw2033/leds_aw2033.h new file mode 100644 index 000000000000..c26748e3ec66 --- /dev/null +++ b/drivers/leds/aw2033/leds_aw2033.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2015, The Linux Foundation. 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 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. + * + */ + +#ifndef __LINUX_AW2033_LED_H__ +#define __LINUX_AW2033_LED_H__ + +/* The definition of each time described as shown in figure. + * /-----------\ + * / | \ + * /| | |\ + * / | | | \----------- + * |hold_time_ms | | + * | | | + * rise_time_ms fall_time_ms | + * off_time_ms + */ + +struct aw2033_platform_data { + int imax; + int led_current; + int rise_time_ms; + int hold_time_ms; + int fall_time_ms; + int off_time_ms; + struct aw2033_led *led; +}; + +#endif diff --git a/drivers/leds/leds-indicator-pwm.c b/drivers/leds/leds-indicator-pwm.c new file mode 100644 index 000000000000..bfa2030a4f81 --- /dev/null +++ b/drivers/leds/leds-indicator-pwm.c @@ -0,0 +1,512 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2018-2019, 2021, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TRILED_NUM_MAX 3 + +#define PWM_PERIOD_DEFAULT_NS 1000000 + +struct pwm_setting { + u64 pre_period_ns; + u64 period_ns; + u64 duty_ns; +}; + +struct led_setting { + u64 on_ms; + u64 off_ms; + enum led_brightness brightness; + bool blink; + bool breath; +}; + +struct qpnp_led_dev { + struct led_classdev cdev; + struct pwm_device *pwm_dev; + struct pwm_setting pwm_setting; + struct led_setting led_setting; + struct indicator_led_chip *chip; + struct mutex lock; + const char *label; + const char *default_trigger; + u8 id; + bool blinking; + bool breathing; +}; + +struct indicator_led_chip { + struct device *dev; + struct qpnp_led_dev *leds; + struct nvmem_device *pbs_nvmem; + struct mutex bus_lock; + int num_leds; + u16 reg_base; + u8 subtype; + u8 bitmap; + struct pinctrl *pinctrl; + struct pinctrl_state *pin_sta_default; +}; + + +static int __indicator_led_config_pwm(struct qpnp_led_dev *led, + struct pwm_setting *pwm) +{ + struct pwm_state pstate; + int rc; + + pwm_get_state(led->pwm_dev, &pstate); + pstate.enabled = !!(pwm->duty_ns != 0); + pstate.period = pwm->period_ns; + pstate.duty_cycle = pwm->duty_ns; + pstate.output_type = led->led_setting.breath ? + PWM_OUTPUT_MODULATED : PWM_OUTPUT_FIXED; + + rc = pwm_apply_state(led->pwm_dev, &pstate); + + if (rc < 0) + dev_err(led->chip->dev, "Apply PWM state for %s led failed, rc=%d\n", + led->cdev.name, rc); + + return rc; +} + +static int __indicator_led_set(struct qpnp_led_dev *led) +{ + int rc = 0; + + rc = __indicator_led_config_pwm(led, &led->pwm_setting); + if (rc < 0) { + dev_err(led->chip->dev, "Configure PWM for %s led failed, rc=%d\n", + led->cdev.name, rc); + return rc; + } + + return rc; +} + +static int indicator_led_set(struct qpnp_led_dev *led) +{ + u64 on_ms, off_ms, period_ns, duty_ns; + enum led_brightness brightness = led->led_setting.brightness; + int rc = 0; + + if (led->led_setting.blink) { + on_ms = led->led_setting.on_ms; + off_ms = led->led_setting.off_ms; + + duty_ns = on_ms * NSEC_PER_MSEC; + period_ns = (on_ms + off_ms) * NSEC_PER_MSEC; + + if (period_ns < duty_ns && duty_ns != 0) + period_ns = duty_ns + 1; + } else { + /* Use initial period if no blinking is required */ + period_ns = led->pwm_setting.pre_period_ns; + + if (brightness == LED_OFF) + duty_ns = 0; + + duty_ns = period_ns * brightness; + do_div(duty_ns, LED_FULL); + + if (period_ns < duty_ns && duty_ns != 0) + period_ns = duty_ns + 1; + } + dev_dbg(led->chip->dev, "PWM settings for %s led: period = %lluns, duty = %lluns\n", + led->cdev.name, period_ns, duty_ns); + + led->pwm_setting.duty_ns = duty_ns; + led->pwm_setting.period_ns = period_ns; + + rc = __indicator_led_set(led); + if (rc < 0) { + dev_err(led->chip->dev, "__indicator_led_set %s failed, rc=%d\n", + led->cdev.name, rc); + return rc; + } + + if (led->led_setting.blink) { + led->cdev.brightness = LED_FULL; + led->blinking = true; + led->breathing = false; + } else if (led->led_setting.breath) { + led->cdev.brightness = LED_FULL; + led->blinking = false; + led->breathing = true; + } else { + led->cdev.brightness = led->led_setting.brightness; + led->blinking = false; + led->breathing = false; + } + + return rc; +} + +static int indicator_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct qpnp_led_dev *led = + container_of(led_cdev, struct qpnp_led_dev, cdev); + int rc = 0; + + mutex_lock(&led->lock); + if (brightness > LED_FULL) + brightness = LED_FULL; + + if (brightness == led->led_setting.brightness && + !led->blinking && !led->breathing) { + mutex_unlock(&led->lock); + return 0; + } + + led->led_setting.brightness = brightness; + if (!!brightness) + led->led_setting.off_ms = 0; + else + led->led_setting.on_ms = 0; + led->led_setting.blink = false; + led->led_setting.breath = false; + + rc = indicator_led_set(led); + if (rc) + dev_err(led->chip->dev, "Set led failed for %s, rc=%d\n", + led->label, rc); + + mutex_unlock(&led->lock); + + return rc; +} + +static enum led_brightness indicator_led_get_brightness( + struct led_classdev *led_cdev) +{ + return led_cdev->brightness; +} + +static int indicator_led_set_blink(struct led_classdev *led_cdev, + unsigned long *on_ms, unsigned long *off_ms) +{ + struct qpnp_led_dev *led = + container_of(led_cdev, struct qpnp_led_dev, cdev); + int rc = 0; + + mutex_lock(&led->lock); + if (led->blinking && *on_ms == led->led_setting.on_ms && + *off_ms == led->led_setting.off_ms) { + dev_dbg(led_cdev->dev, "Ignore, on/off setting is not changed: on %lums, off %lums\n", + *on_ms, *off_ms); + mutex_unlock(&led->lock); + return 0; + } + + if (*on_ms == 0) { + led->led_setting.blink = false; + led->led_setting.breath = false; + led->led_setting.brightness = LED_OFF; + } else if (*off_ms == 0) { + led->led_setting.blink = false; + led->led_setting.breath = false; + led->led_setting.brightness = led->cdev.brightness; + } else { + led->led_setting.on_ms = *on_ms; + led->led_setting.off_ms = *off_ms; + led->led_setting.blink = true; + led->led_setting.breath = false; + } + + rc = indicator_led_set(led); + if (rc) + dev_err(led->chip->dev, "Set led failed for %s, rc=%d\n", + led->label, rc); + + mutex_unlock(&led->lock); + return rc; +} + +static ssize_t breath_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct qpnp_led_dev *led = + container_of(led_cdev, struct qpnp_led_dev, cdev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", led->led_setting.breath); +} + +static ssize_t breath_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + bool breath; + struct led_classdev *led_cdev = dev_get_drvdata(dev); + struct qpnp_led_dev *led = + container_of(led_cdev, struct qpnp_led_dev, cdev); + + rc = kstrtobool(buf, &breath); + if (rc < 0) + return rc; + + cancel_work_sync(&led_cdev->set_brightness_work); + + mutex_lock(&led->lock); + if (led->breathing == breath) + goto unlock; + + led->led_setting.blink = false; + led->led_setting.breath = breath; + led->led_setting.brightness = breath ? LED_FULL : LED_OFF; + rc = indicator_led_set(led); + if (rc < 0) + dev_err(led->chip->dev, "Set led failed for %s, rc=%d\n", + led->label, rc); + +unlock: + mutex_unlock(&led->lock); + return (rc < 0) ? rc : count; +} + +static DEVICE_ATTR_RW(breath); +static const struct attribute *breath_attrs[] = { + &dev_attr_breath.attr, + NULL +}; + +static int indicator_led_register(struct indicator_led_chip *chip) +{ + struct qpnp_led_dev *led; + int rc, i, j; + + + for (i = 0; i < chip->num_leds; i++) { + led = &chip->leds[i]; + mutex_init(&led->lock); + led->cdev.name = led->label; + led->cdev.max_brightness = LED_FULL; + led->cdev.brightness_set_blocking = indicator_led_set_brightness; + led->cdev.brightness_get = indicator_led_get_brightness; + led->cdev.blink_set = indicator_led_set_blink; + led->cdev.default_trigger = led->default_trigger; + led->cdev.brightness = LED_OFF; + + rc = devm_led_classdev_register(chip->dev, &led->cdev); + if (rc < 0) { + dev_err(chip->dev, "%s led class device registering failed, rc=%d\n", + led->label, rc); + goto err_out; + } + + if (pwm_get_output_type_supported(led->pwm_dev) + & PWM_OUTPUT_MODULATED) { + rc = sysfs_create_files(&led->cdev.dev->kobj, + breath_attrs); + if (rc < 0) { + dev_err(chip->dev, "Create breath file for %s led failed, rc=%d\n", + led->label, rc); + goto err_out; + } + } + } + + return 0; + +err_out: + for (j = 0; j <= i; j++) { + if (j < i) + sysfs_remove_files(&chip->leds[j].cdev.dev->kobj, + breath_attrs); + mutex_destroy(&chip->leds[j].lock); + } + return rc; +} + +static int indicator_led_parse_dt(struct indicator_led_chip *chip) +{ + struct device_node *node = chip->dev->of_node, *child_node; + struct qpnp_led_dev *led; + struct pwm_args pargs; + int rc = 0, id, i = 0; + + chip->num_leds = of_get_available_child_count(node); + if (chip->num_leds == 0) { + dev_err(chip->dev, "No led child node defined\n"); + return -ENODEV; + } + + if (chip->num_leds > TRILED_NUM_MAX) { + dev_err(chip->dev, "can't support %d leds(max %d)\n", + chip->num_leds, TRILED_NUM_MAX); + return -EINVAL; + } + + chip->leds = devm_kcalloc(chip->dev, chip->num_leds, + sizeof(struct qpnp_led_dev), GFP_KERNEL); + if (!chip->leds) + return -ENOMEM; + + for_each_available_child_of_node(node, child_node) { + rc = of_property_read_u32(child_node, "led-sources", &id); + if (rc) { + dev_err(chip->dev, "Get led-sources failed, rc=%d\n", + rc); + return rc; + } + + if (id >= TRILED_NUM_MAX) { + dev_err(chip->dev, "only support 0~%d current source\n", + TRILED_NUM_MAX - 1); + return -EINVAL; + } + + led = &chip->leds[i++]; + led->chip = chip; + led->id = id; + led->label = + of_get_property(child_node, "label", NULL) ? : + child_node->name; + + led->pwm_dev = + devm_of_pwm_get(chip->dev, child_node, NULL); + if (IS_ERR(led->pwm_dev)) { + rc = PTR_ERR(led->pwm_dev); + if (rc != -EPROBE_DEFER) + dev_err(chip->dev, "Get pwm device for %s led failed, rc=%d\n", + led->label, rc); + return rc; + } + + pwm_get_args(led->pwm_dev, &pargs); + if (pargs.period == 0) + led->pwm_setting.pre_period_ns = PWM_PERIOD_DEFAULT_NS; + else + led->pwm_setting.pre_period_ns = pargs.period; + + led->default_trigger = of_get_property(child_node, + "linux,default-trigger", NULL); + } + + return rc; +} +static int indicator_led_pinctrl_init(struct indicator_led_chip *chip) +{ + int rc = 0; + + chip->pinctrl = devm_pinctrl_get(chip->dev); + if (IS_ERR_OR_NULL(chip->pinctrl)) { + dev_err(chip->dev, "Failed to get pinctrl handler"); + chip->pinctrl = NULL; + return -EINVAL; + } + chip->pin_sta_default = pinctrl_lookup_state(chip->pinctrl, PINCTRL_STATE_DEFAULT); + if (IS_ERR_OR_NULL(chip->pin_sta_default)) { + rc = PTR_ERR(chip->pin_sta_default); + dev_err(chip->dev, "Failed to get pinctrl state:%s, r:%d", + PINCTRL_STATE_DEFAULT, rc); + chip->pin_sta_default = NULL; + goto exit_pinctrl_put; + } + rc = pinctrl_select_state(chip->pinctrl, chip->pin_sta_default); + if (rc < 0) { + dev_err(chip->dev, "Failed to select default pinstate, rc:%d", rc); + } + +exit_pinctrl_put: + devm_pinctrl_put(chip->pinctrl); + chip->pinctrl = NULL; + + return rc; +} + + +static int indicator_led_probe(struct platform_device *pdev) +{ + struct indicator_led_chip *chip; + int rc = 0; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &pdev->dev; + + rc = indicator_led_parse_dt(chip); + if (rc < 0) { + if (rc != -EPROBE_DEFER) + dev_err(chip->dev, "Devicetree properties parsing failed, rc=%d\n", + rc); + return rc; + } + + rc = indicator_led_pinctrl_init(chip); + if(rc < 0) { + dev_err(chip->dev, "Indicator led pinctrl init failed, rc=%d\n", rc); + return rc; + } + + mutex_init(&chip->bus_lock); + + dev_set_drvdata(chip->dev, chip); + + rc = indicator_led_register(chip); + if (rc < 0) { + dev_err(chip->dev, "Registering LED class devices failed, rc=%d\n", + rc); + goto destroy; + } + + dev_err(chip->dev, "%s has been finished\n", __func__); + return 0; +destroy: + mutex_destroy(&chip->bus_lock); + dev_set_drvdata(chip->dev, NULL); + + return rc; +} + +static int indicator_led_remove(struct platform_device *pdev) +{ + int i; + struct indicator_led_chip *chip = dev_get_drvdata(&pdev->dev); + + mutex_destroy(&chip->bus_lock); + for (i = 0; i < chip->num_leds; i++) { + sysfs_remove_files(&chip->leds[i].cdev.dev->kobj, breath_attrs); + mutex_destroy(&chip->leds[i].lock); + } + dev_set_drvdata(chip->dev, NULL); + return 0; +} + +static const struct of_device_id indicator_led_of_match[] = { + { .compatible = "qcom,indicator-pwm-led",}, + { }, +}; + +static struct platform_driver indicator_led_driver = { + .driver = { + .name = "qcom,indicator-pwm-led", + .of_match_table = indicator_led_of_match, + }, + .probe = indicator_led_probe, + .remove = indicator_led_remove, +}; +module_platform_driver(indicator_led_driver); + +MODULE_DESCRIPTION("PWM LED driver"); +MODULE_LICENSE("GPL v2"); +MODULE_SOFTDEP("pre: pwm-qti-lpg"); diff --git a/drivers/leds/trigger/Android.mk b/drivers/leds/trigger/Android.mk new file mode 100644 index 000000000000..433f30937611 --- /dev/null +++ b/drivers/leds/trigger/Android.mk @@ -0,0 +1,12 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := ledtrig-sol.ko +LOCAL_MODULE_TAGS := optional +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/leds/trigger/Kbuild b/drivers/leds/trigger/Kbuild new file mode 100644 index 000000000000..1983f4f1e905 --- /dev/null +++ b/drivers/leds/trigger/Kbuild @@ -0,0 +1,5 @@ +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + + +obj-m += ledtrig-sol.o diff --git a/drivers/leds/trigger/ledtrig-sol.c b/drivers/leds/trigger/ledtrig-sol.c new file mode 100644 index 000000000000..bfa2104d20b6 --- /dev/null +++ b/drivers/leds/trigger/ledtrig-sol.c @@ -0,0 +1,445 @@ +/* + * Description: Sign-of-life LED trigger + * + * Copyright (c) 2021 Lenovo, Inc. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CHARGING_DELAY_ON_DEFAULT (700) +#define CHARGING_DELAY_OFF_DEFAULT (700) +#define LOW_CAP_DELAY_ON_DEFAULT (3800) +#define LOW_CAP_DELAY_OFF_DEFAULT (200) + +#define LOW_CAP_THRESHOLD_DEFAULT (15) +#define BATT_CAP_THRESHOLD_HALF (50) +#define BATT_CAP_THRESHOLD_HIGH (75) +#define BATT_CAP_THRESHOLD_FULL (100) + +struct sol_trig_data { + struct led_classdev *led_cdev; + struct notifier_block nb; + unsigned long charging_delay_on; + unsigned long charging_delay_off; + unsigned long low_cap_delay_on; + unsigned long low_cap_delay_off; + int low_cap_threshold; + bool mmi_battery_psy_present; + bool battery_psy_present; +}; + +static int multi_led_enable = 0; +module_param(multi_led_enable, int, 0644); + +static ssize_t charging_delay_on_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = led_trigger_get_led(dev); + struct sol_trig_data *sol_data = led_get_trigger_data(led_cdev); + + return sprintf(buf, "%lu\n", sol_data->charging_delay_on); +} + +static ssize_t charging_delay_on_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = led_trigger_get_led(dev); + struct sol_trig_data *sol_data = led_get_trigger_data(led_cdev); + unsigned long val; + ssize_t ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + sol_data->charging_delay_on = val; + + return size; +} + +static ssize_t charging_delay_off_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = led_trigger_get_led(dev); + struct sol_trig_data *sol_data = led_get_trigger_data(led_cdev); + + return sprintf(buf, "%lu\n", sol_data->charging_delay_off); +} + +static ssize_t charging_delay_off_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = led_trigger_get_led(dev); + struct sol_trig_data *sol_data = led_get_trigger_data(led_cdev); + unsigned long val; + ssize_t ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + sol_data->charging_delay_off = val; + + return size; +} + +static ssize_t low_cap_delay_on_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = led_trigger_get_led(dev); + struct sol_trig_data *sol_data = led_get_trigger_data(led_cdev); + + return sprintf(buf, "%lu\n", sol_data->low_cap_delay_on); +} + +static ssize_t low_cap_delay_on_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = led_trigger_get_led(dev); + struct sol_trig_data *sol_data = led_get_trigger_data(led_cdev); + unsigned long val; + ssize_t ret; + + ret = kstrtoul(buf, 10, &val); + if (ret) + return ret; + + sol_data->low_cap_delay_on = val; + + return size; +} + +static ssize_t low_cap_delay_off_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = led_trigger_get_led(dev); + struct sol_trig_data *sol_data = led_get_trigger_data(led_cdev); + + return sprintf(buf, "%lu\n", sol_data->low_cap_delay_off); +} + +static ssize_t low_cap_delay_off_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = led_trigger_get_led(dev); + struct sol_trig_data *sol_data = led_get_trigger_data(led_cdev); + unsigned long val; + ssize_t ret; + + ret = kstrtoul(buf, 0, &val); + if (ret) + return ret; + + sol_data->low_cap_delay_off = val; + + return size; +} + +static ssize_t low_cap_threshold_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *led_cdev = led_trigger_get_led(dev); + struct sol_trig_data *sol_data = led_get_trigger_data(led_cdev); + + return sprintf(buf, "%d\n", sol_data->low_cap_threshold); +} + +static ssize_t low_cap_threshold_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct led_classdev *led_cdev = led_trigger_get_led(dev); + struct sol_trig_data *sol_data = led_get_trigger_data(led_cdev); + long val; + ssize_t ret; + + ret = kstrtol(buf, 0, &val); + if (ret) + return ret; + + if (val > 100) /* Max capacity is 100% */ + return -EINVAL; + + sol_data->low_cap_threshold = val; + + return size; +} + +static DEVICE_ATTR(charging_delay_on, 0644, charging_delay_on_show, charging_delay_on_store); +static DEVICE_ATTR(charging_delay_off, 0644, charging_delay_off_show, charging_delay_off_store); +static DEVICE_ATTR(low_capacity_delay_on, 0644, low_cap_delay_on_show, low_cap_delay_on_store); +static DEVICE_ATTR(low_capacity_delay_off, 0644, low_cap_delay_off_show, low_cap_delay_off_store); +static DEVICE_ATTR(low_capacity_threshold, 0644, low_cap_threshold_show, low_cap_threshold_store); + +static struct attribute *sol_trig_attrs[] = { + &dev_attr_charging_delay_on.attr, + &dev_attr_charging_delay_off.attr, + &dev_attr_low_capacity_delay_on.attr, + &dev_attr_low_capacity_delay_off.attr, + &dev_attr_low_capacity_threshold.attr, + NULL +}; +ATTRIBUTE_GROUPS(sol_trig); + +static int sol_trig_activate(struct led_classdev *led_cdev); +static void sol_trig_deactivate(struct led_classdev *led_cdev); + +static struct led_trigger sol_led_trigger = { + .name = "sign-of-life", + .activate = sol_trig_activate, + .deactivate = sol_trig_deactivate, + .groups = sol_trig_groups, +}; + +static int is_multi_led_enabled () { + return multi_led_enable; +} + +/* API to set sol led colour, brightness etc based on battery status */ +static int sol_led_update_status(const char *led_name, bool blink, unsigned int brightness, struct sol_trig_data *sol_data) +{ + + struct led_classdev *led_cdev = sol_data->led_cdev; + + dev_dbg(led_cdev->dev, "Entry sol_led_update_status name:%s, blink = %d, brightness = %u\n",led_name,blink,brightness); + if(!strcmp(led_name, "charging_full") && !strcmp(led_cdev->name, "charging_full")) { + dev_dbg(led_cdev->dev, "POWER_STATUS_FULL\n"); + led_trigger_event(&sol_led_trigger, LED_OFF); + flush_work(&led_cdev->set_brightness_work); + led_set_brightness_sync(led_cdev, brightness); + if(blink){ + led_blink_set(led_cdev, &sol_data->charging_delay_on, &sol_data->charging_delay_off); + dev_dbg(led_cdev->dev, "SETTING DONE FOR charging full blink delay_on=%lu,delay_off=%lu\n",sol_data->charging_delay_on, sol_data->charging_delay_off); + } + } + + if(!strcmp(led_name, "charging") && !strcmp(led_cdev->name, "charging")) { + dev_dbg(led_cdev->dev, "POWER_STATUS_CHARGING\n"); + led_trigger_event(&sol_led_trigger, LED_OFF); + flush_work(&led_cdev->set_brightness_work); + led_set_brightness_sync(led_cdev, brightness); + if(blink){ + led_blink_set(led_cdev, &sol_data->charging_delay_on, &sol_data->charging_delay_off); + dev_dbg(led_cdev->dev, "SETTING DONE FOR charging blink delay_on=%lu,delay_off=%lu\n",sol_data->charging_delay_on, sol_data->charging_delay_off); + } + } + + if(!strcmp(led_name, "charging_low") && !strcmp(led_cdev->name, "charging_low")) { + dev_dbg(led_cdev->dev, "POWER_STATUS_LOW\n"); + led_trigger_event(&sol_led_trigger, LED_OFF); + flush_work(&led_cdev->set_brightness_work); + led_set_brightness_sync(led_cdev, brightness); + if(blink){ + led_blink_set(led_cdev, &sol_data->low_cap_delay_on, &sol_data->low_cap_delay_off); + dev_dbg(led_cdev->dev, "SETTING DONE FOR low cap blink delay_on= %lu,delay_off=%lu\n",sol_data->low_cap_delay_on,sol_data->low_cap_delay_off); + } + } + return 0; +} + +static int sol_trig_notifier_fn(struct notifier_block *nb, unsigned long action, void *data) +{ + union power_supply_propval status; + union power_supply_propval capacity; + struct sol_trig_data *sol_data = container_of(nb, struct sol_trig_data, nb); + struct led_classdev *led_cdev = sol_data->led_cdev; + struct power_supply *psy = data; + + /* We want to key the SOL LED off "mmi_battery" if possible. If mmi_battery is + * unavailable, try the "battery" PSY. If neither of those are available, just + * key off any battery PSY available. + */ + if (sol_data->mmi_battery_psy_present) + { + if (strcmp(psy->desc->name, "mmi_battery")) + return NOTIFY_OK; + } + else if (sol_data->battery_psy_present) + { + if (strcmp(psy->desc->name, "battery")) + return NOTIFY_OK; + } + else if (psy->desc->type != POWER_SUPPLY_TYPE_BATTERY) + { + return NOTIFY_OK; + } + + if (power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, &status)) { + dev_err(led_cdev->dev, "Failed to get PSY status.\n"); + return NOTIFY_OK; + } + dev_dbg(led_cdev->dev, "%s: PSY status: %d\n", __func__, status.intval); + + switch (status.intval) { + case POWER_SUPPLY_STATUS_UNKNOWN: /* Fall-through */ + case POWER_SUPPLY_STATUS_FULL: + if(is_multi_led_enabled()) + sol_led_update_status("charging_full", false, LED_FULL, sol_data); + else + led_set_brightness(led_cdev, LED_FULL); + break; + + case POWER_SUPPLY_STATUS_CHARGING: + if(is_multi_led_enabled()) { + + if (!power_supply_get_property(psy, POWER_SUPPLY_PROP_CAPACITY, &capacity)) + dev_err(led_cdev->dev, "%s: Battery capacity: %d\n", __func__, capacity.intval); + + /* charging_low led blink: Charging capacity <= 15% */ + if (capacity.intval <= sol_data->low_cap_threshold) + sol_led_update_status("charging_low", true, LED_FULL, sol_data); + + /* charging led blink: Charging capacity >15% and <75% */ + if (capacity.intval > sol_data->low_cap_threshold && capacity.intval < BATT_CAP_THRESHOLD_HIGH) + sol_led_update_status("charging", true, LED_FULL, sol_data); + + /* charging_full led blink: Charging capacity >=75 and < 100% */ + if (capacity.intval >= BATT_CAP_THRESHOLD_HIGH && capacity.intval <= BATT_CAP_THRESHOLD_FULL) + sol_led_update_status("charging_full", true, LED_FULL, sol_data); + + } else + led_blink_set(led_cdev, &sol_data->charging_delay_on, &sol_data->charging_delay_off); + break; + + case POWER_SUPPLY_STATUS_DISCHARGING: /* Fall-through */ + case POWER_SUPPLY_STATUS_NOT_CHARGING: /* Fall-through */ + default: + /* + * If PSY capacity is equal-to or below a specified threshold, use a special low-capacity blink pattern. + * Otherwise, set LED to full brightness by default. + */ + if (!power_supply_get_property(psy, POWER_SUPPLY_PROP_CAPACITY, &capacity)) + dev_dbg(led_cdev->dev, "%s: Battery capacity: %d\n", __func__, capacity.intval); + + if(is_multi_led_enabled()) { + + /* charging_low led blink: Charging capacity <= 15% */ + if (capacity.intval <= sol_data->low_cap_threshold) + sol_led_update_status("charging_low", true, LED_FULL, sol_data); + + /* charging led solid: Charging capacity >15% and <75% */ + if (capacity.intval > sol_data->low_cap_threshold && capacity.intval < BATT_CAP_THRESHOLD_HIGH) + sol_led_update_status("charging", false, LED_HALF, sol_data); + + /* charging_full led solid: Charging capacity >=75 and < 100% */ + if (capacity.intval >= BATT_CAP_THRESHOLD_HIGH && capacity.intval <= BATT_CAP_THRESHOLD_FULL) + sol_led_update_status("charging_full", false, LED_HALF, sol_data); + } + else + if(capacity.intval <= sol_data->low_cap_threshold) + led_blink_set(led_cdev, &sol_data->low_cap_delay_on, &sol_data->low_cap_delay_off); + else + led_set_brightness(led_cdev, LED_FULL); + break; + } + + return NOTIFY_OK; +} + +static int sol_trig_identify_psy(struct device *dev, void *data) +{ + struct sol_trig_data *sol_data = data; + struct power_supply *psy = dev_get_drvdata(dev); + + if (!strcmp(psy->desc->name, "mmi_battery")) + sol_data->mmi_battery_psy_present = true; + + if (!strcmp(psy->desc->name, "battery")) + sol_data->battery_psy_present = true; + + return 0; +} + +static int sol_trig_activate(struct led_classdev *led_cdev) +{ + int ret; + struct sol_trig_data *sol_data; + + sol_data = kzalloc(sizeof(*sol_data), GFP_KERNEL); + if (!sol_data) + return -ENOMEM; + + sol_data->nb.notifier_call = sol_trig_notifier_fn; + ret = power_supply_reg_notifier(&sol_data->nb); + if (ret) { + dev_err(led_cdev->dev, "Failed to register notifier: %d\n", ret); + goto error; + } + + sol_data->led_cdev = led_cdev; + + sol_data->charging_delay_on = CHARGING_DELAY_ON_DEFAULT; + sol_data->charging_delay_off = CHARGING_DELAY_OFF_DEFAULT; + sol_data->low_cap_delay_on = LOW_CAP_DELAY_ON_DEFAULT; + sol_data->low_cap_delay_off = LOW_CAP_DELAY_OFF_DEFAULT; + sol_data->low_cap_threshold = LOW_CAP_THRESHOLD_DEFAULT; + sol_data->mmi_battery_psy_present = false; + sol_data->battery_psy_present = false; + + class_for_each_device(power_supply_class, NULL, sol_data, sol_trig_identify_psy); + dev_err(led_cdev->dev, " %s, Initial brightness = %d\n",sol_data->led_cdev->name, sol_data->led_cdev->brightness); + + led_set_trigger_data(led_cdev, sol_data); + + led_set_brightness(led_cdev, LED_FULL); /* Set default LED state */ + + return 0; + +error: + kfree(sol_data); + return ret; +} + +static void sol_trig_deactivate(struct led_classdev *led_cdev) +{ + struct sol_trig_data *sol_data = led_get_trigger_data(led_cdev); + + power_supply_unreg_notifier(&sol_data->nb); + + kfree(sol_data); + + return; +} + +static int __init sol_trig_init(void) +{ + pr_err("multiled: multi_led_enable = %d\n",multi_led_enable); + return led_trigger_register(&sol_led_trigger); +} + +static void __exit sol_trig_exit(void) +{ + led_trigger_unregister(&sol_led_trigger); +} + +module_init(sol_trig_init); +module_exit(sol_trig_exit); + +MODULE_AUTHOR("Andy White "); +MODULE_DESCRIPTION("Sign-of-life LED trigger"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/Android.mk b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/Android.mk new file mode 100644 index 000000000000..e673b4c906d5 --- /dev/null +++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := cci_intf.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/Kbuild b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/Kbuild new file mode 100644 index 000000000000..0be27cdfd1af --- /dev/null +++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/Kbuild @@ -0,0 +1,35 @@ +HAVE_KERNEL_4_19 = $(shell test -d $(ANDROID_BUILD_TOP)/kernel/msm-4.19 && echo 1) + +HAVE_KERNEL_5_4 = $(shell test -d $(ANDROID_BUILD_TOP)/kernel/msm-5.4 && echo 1) + +ifeq ($(HAVE_KERNEL_4_19),1) + KERNEL_SOURCE_PATH ?= kernel/msm-4.19 +else ifeq ($(HAVE_KERNEL_5_4),1) + KERNEL_SOURCE_PATH ?= kernel/msm-5.4 +else + KERNEL_SOURCE_PATH ?= kernel/msm-4.9 +endif + +HAVE_TECHPACK_CAMERA = $(shell test -d $(ANDROID_BUILD_TOP)/$(KERNEL_SOURCE_PATH)/techpack/camera && echo 1) + +ifeq ($(HAVE_TECHPACK_CAMERA),1) + KERNEL_CAMERA_DRIVER_PATH ?= $(ANDROID_BUILD_TOP)/$(KERNEL_SOURCE_PATH)/techpack/camera/drivers + EXTRA_CFLAGS += -DCONFIG_SPECTRA_CAMERA=1 + EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/../include + EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/../include/uapi + EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/../include/uapi/camera +else + KERNEL_CAMERA_DRIVER_PATH ?= $(ANDROID_BUILD_TOP)/$(KERNEL_SOURCE_PATH)/drivers/media/platform/msm/camera +endif + +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include +EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/cam_utils +EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/cam_cpas/include +EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/cam_sensor_module/cam_sensor_io +EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/cam_sensor_module/cam_sensor_utils +EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/cam_sensor_module/cam_cci +EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/cam_req_mgr +EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/cam_smmu + +obj-m += cci_intf.o diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/Makefile b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cci_intf.c b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cci_intf.c new file mode 100644 index 000000000000..dcda77c41688 --- /dev/null +++ b/drivers/media/platform/msm/camera/cam_sensor_module/cam_cci/cci_intf.c @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2018 Motorola Mobility LLC. + * + * 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. + * + */ + +//#define DEBUG + +#include +#include +#include +#include +#include +#include + +static int32_t cci_intf_xfer( + struct msm_cci_intf_xfer *xfer, + unsigned int cmd) +{ + int32_t rc, rc2; + uint16_t addr; + struct cam_sensor_cci_client cci_info = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0) + .cci_subdev = cam_cci_get_subdev(xfer->cci_device), +#else + .cci_subdev = cam_cci_get_subdev(), +#endif + .cci_i2c_master = xfer->cci_bus, + .sid = xfer->slave_addr, + }; + struct cam_cci_ctrl cci_ctrl = { + .cci_info = &cci_info, + }; + int i; + struct cam_sensor_i2c_reg_array *reg_conf_tbl; + + pr_debug("%s cmd:%d bus:%d devaddr:%02x regw:%d rega:%04x count:%d\n", + __func__, cmd, xfer->cci_bus, xfer->slave_addr, + xfer->reg.width, xfer->reg.addr, xfer->data.count); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0) + if (xfer->cci_device > 1 || xfer->cci_bus > 1 || xfer->slave_addr > 0x7F || +#else + if (xfer->cci_bus > 1 || xfer->slave_addr > 0x7F || +#endif + xfer->reg.width < 1 || xfer->reg.width > 2 || + xfer->reg.addr > ((1<<(8*xfer->reg.width))-1) || + xfer->data.count < 1 || + xfer->data.count > MSM_CCI_INTF_MAX_XFER) + return -EINVAL; + + /* init */ + cci_ctrl.cmd = MSM_CCI_INIT; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0) + rc = v4l2_subdev_call(cam_cci_get_subdev(xfer->cci_device), +#else + rc = v4l2_subdev_call(cam_cci_get_subdev(), +#endif + core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl); + if (rc < 0) { + pr_err("%s: cci init fail (%d)\n", __func__, rc); + return rc; + } + + switch (cmd) { + case MSM_CCI_INTF_READ: + /* read */ + cci_ctrl.cmd = MSM_CCI_I2C_READ; + cci_ctrl.cfg.cci_i2c_read_cfg.addr = xfer->reg.addr; + cci_ctrl.cfg.cci_i2c_read_cfg.addr_type = + (xfer->reg.width == 1 ? + CAMERA_SENSOR_I2C_TYPE_BYTE : + CAMERA_SENSOR_I2C_TYPE_WORD); + cci_ctrl.cfg.cci_i2c_read_cfg.data = xfer->data.buf; + cci_ctrl.cfg.cci_i2c_read_cfg.num_byte = xfer->data.count; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0) + rc = v4l2_subdev_call(cam_cci_get_subdev(xfer->cci_device), +#else + rc = v4l2_subdev_call(cam_cci_get_subdev(), +#endif + core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl); + if (rc < 0) { + pr_err("%s: cci read fail (%d)\n", __func__, rc); + goto release; + } + rc = cci_ctrl.status; + break; + case MSM_CCI_INTF_WRITE: + /* write */ + reg_conf_tbl = kzalloc(xfer->data.count * + sizeof(struct cam_sensor_i2c_reg_array), + GFP_KERNEL); + if (!reg_conf_tbl) { + rc = -ENOMEM; + goto release; + } + addr = xfer->reg.addr; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0) + for (i = 0; i < xfer->data.count; i += xfer->data.width) { + reg_conf_tbl[i].reg_addr = addr++; + if(xfer->data.width == 4) { + reg_conf_tbl[i].reg_data = + ((uint32_t)(xfer->data.buf[i]) << 24) | + ((uint32_t)(xfer->data.buf[i+1]) << 16) | + ((uint32_t)(xfer->data.buf[i+2]) << 8) | + ((uint32_t)(xfer->data.buf[i+3])); + pr_err("%s: cci writing %x", __func__, reg_conf_tbl[i].reg_data); + } else if(xfer->data.width == 2) { + reg_conf_tbl[i].reg_data = + ((uint32_t)(xfer->data.buf[i]) << 8) | + ((uint32_t)(xfer->data.buf[i+1])); + } else { + reg_conf_tbl[i].reg_data = xfer->data.buf[i]; + } + reg_conf_tbl[i].delay = 0; + } +#else + for (i = 0; i < xfer->data.count; i += 1) { + reg_conf_tbl[i].reg_addr = addr++; + reg_conf_tbl[i].reg_data = xfer->data.buf[i]; + reg_conf_tbl[i].delay = 0; + } +#endif + cci_ctrl.cmd = MSM_CCI_I2C_WRITE; + cci_ctrl.cfg.cci_i2c_write_cfg.reg_setting = reg_conf_tbl; + cci_ctrl.cfg.cci_i2c_write_cfg.addr_type = + (xfer->reg.width == 1 ? + CAMERA_SENSOR_I2C_TYPE_BYTE : + CAMERA_SENSOR_I2C_TYPE_WORD); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0) + cci_ctrl.cfg.cci_i2c_write_cfg.data_type = xfer->data.width; +#else + cci_ctrl.cfg.cci_i2c_write_cfg.data_type = + CAMERA_SENSOR_I2C_TYPE_BYTE; +#endif + cci_ctrl.cfg.cci_i2c_write_cfg.size = xfer->data.count; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0) + rc = v4l2_subdev_call(cam_cci_get_subdev(xfer->cci_device), +#else + rc = v4l2_subdev_call(cam_cci_get_subdev(), +#endif + core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl); + kfree(reg_conf_tbl); + if (rc < 0) { + pr_err("%s: cci write fail (%d)\n", __func__, rc); + goto release; + } + rc = cci_ctrl.status; + break; + default: + pr_err("%s: Unknown command (%d)\n", __func__, cmd); + rc = -EINVAL; + break; + } + +release: + /* release */ + cci_ctrl.cmd = MSM_CCI_RELEASE; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,19,0) + rc2 = v4l2_subdev_call(cam_cci_get_subdev(xfer->cci_device), +#else + rc2 = v4l2_subdev_call(cam_cci_get_subdev(), +#endif + core, ioctl, VIDIOC_MSM_CCI_CFG, &cci_ctrl); + if (rc2 < 0) { + pr_err("%s: cci release fail (%d)\n", __func__, rc2); + return rc2; + } + + return rc; +} + +static long cci_intf_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct msm_cci_intf_xfer xfer; + int rc; + + pr_debug("%s cmd=%x arg=%lx\n", __func__, cmd, arg); + + switch (cmd) { + case MSM_CCI_INTF_READ: + case MSM_CCI_INTF_WRITE: + if (copy_from_user(&xfer, (void __user *)arg, sizeof(xfer))) + return -EFAULT; + rc = cci_intf_xfer(&xfer, cmd); + if (copy_to_user((void __user *)arg, &xfer, sizeof(xfer))) + return -EFAULT; + return rc; + default: + return -ENOIOCTLCMD; + } +} + +#ifdef CONFIG_COMPAT +static long cci_intf_ioctl_compat(struct file *file, unsigned int cmd, + unsigned long arg) +{ + pr_debug("%s cmd=%x\n", __func__, cmd); + + switch (cmd) { + case MSM_CCI_INTF_READ32: + cmd = MSM_CCI_INTF_READ; + break; + case MSM_CCI_INTF_WRITE32: + cmd = MSM_CCI_INTF_WRITE; + break; + default: + return -ENOIOCTLCMD; + } + return cci_intf_ioctl(file, cmd, arg); +} +#endif + +static const struct file_operations cci_intf_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = cci_intf_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = cci_intf_ioctl_compat, +#endif +}; + +static struct miscdevice cci_intf_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "cci_intf", + .fops = &cci_intf_fops, +}; + +static int __init cci_intf_init(void) +{ + int rc; + + pr_debug("%s\n", __func__); + + rc = misc_register(&cci_intf_misc); + if (unlikely(rc)) { + pr_err("failed to register misc device %s\n", cci_intf_misc.name); + return rc; + } + + return 0; +} + +static void __exit cci_intf_exit(void) +{ + pr_debug("%s\n", __func__); + + misc_deregister(&cci_intf_misc); +} + +module_init(cci_intf_init); +module_exit(cci_intf_exit); +MODULE_DESCRIPTION("CCI DEBUG INTF"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/Android.mk b/drivers/misc/Android.mk new file mode 100644 index 000000000000..69874a867bcc --- /dev/null +++ b/drivers/misc/Android.mk @@ -0,0 +1,10 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := cirrus_cs40l2x.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/drivers/misc/Kbuild b/drivers/misc/Kbuild new file mode 100644 index 000000000000..e49d268d867a --- /dev/null +++ b/drivers/misc/Kbuild @@ -0,0 +1,6 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +cirrus_cs40l2x-objs = cs40l2x.o cs40l2x-tables.o +obj-m += cirrus_cs40l2x.o diff --git a/drivers/misc/abov/Android.mk b/drivers/misc/abov/Android.mk new file mode 100644 index 000000000000..53eb253f5757 --- /dev/null +++ b/drivers/misc/abov/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := abov_sar.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/abov/Kbuild b/drivers/misc/abov/Kbuild new file mode 100644 index 000000000000..84959597c531 --- /dev/null +++ b/drivers/misc/abov/Kbuild @@ -0,0 +1,17 @@ +# add -Wall to try to catch everything we can. +KERNEL_DIR_PATH = $(shell find $(TOP)/kernel/ -name "msm-*" | head -1 | grep -i "kernel/msm-*") + +KERNEL_VERSION_NAME = $(shell basename $(KERNEL_DIR_PATH)) +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +ifneq (,$(KERNEL_DIR_PATH)) + EXTRA_CFLAGS += -I$(TOP)/kernel/$(KERNEL_VERSION_NAME)/drivers/base +else + EXTRA_CFLAGS += -I$(TOP)/kernel/drivers/base +endif +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../sensors/Module.symvers + +abov_sar-y := abov_sar_mmi.o + +obj-m += abov_sar.o diff --git a/drivers/misc/abov/Makefile b/drivers/misc/abov/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/misc/abov/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/misc/abov/abov_sar_mmi.c b/drivers/misc/abov/abov_sar_mmi.c new file mode 100644 index 000000000000..3ed476edae4c --- /dev/null +++ b/drivers/misc/abov/abov_sar_mmi.c @@ -0,0 +1,2147 @@ +/* + * file abov_sar.c + * brief abov Driver for two channel SAP using + * + * Driver for the ABOV + * Copyright (c) 2015-2016 ABOV Corp + * + * 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. + */ +//#define DEBUG +#define DRIVER_NAME "abov_sar" +#define USE_SENSORS_CLASS +#define USE_KERNEL_SUSPEND + +#define MAX_WRITE_ARRAY_SIZE 32 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* main struct, interrupt,init,pointers */ + +#define BOOT_UPDATE_ABOV_FIRMWARE 1 + +#include +#include +#include +#include +#include +#include "base.h" +#define SLEEP(x) mdelay(x) + +#define C_I2C_FIFO_SIZE 8 + +#define I2C_MASK_FLAG (0x00ff) + +static u8 checksum_h; +static u8 checksum_h_bin; +static u8 checksum_l; +static u8 checksum_l_bin; +static char _Buffer[128]; + +#define IDLE 0 +#define ACTIVE 1 +#define S_PROX 1 +#define S_BODY 2 + +#define LOG_TAG "" + +#define LOG_INFO(fmt, args...) pr_info(LOG_TAG "[INFO]" "<%s><%d>"fmt, __func__, __LINE__, ##args) +#define LOG_DBG(fmt, args...) pr_debug(LOG_TAG "[DBG]" "<%s><%d>"fmt, __func__, __LINE__, ##args) +#define LOG_ERR(fmt, args...) pr_err(LOG_TAG "[ERR]" "<%s><%d>"fmt, __func__, __LINE__, ##args) + +static int last_val; +static int mEnabled; +static int programming_done; +static int fw_dl_status; +static bool first_report = false; +pabovXX_t abov_sar_ptr; + +/** + * struct abov + * Specialized struct containing input event data, platform data, and + * last cap state read if needed. + */ +typedef struct abov { + pbuttonInformation_t pbuttonInformation; + pabov_platform_data_t hw; /* specific platform data settings */ +} abov_t, *pabov_t; + +static void ForcetoTouched(pabovXX_t this) +{ + pabov_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + struct _buttonInfo *pCurrentButton = NULL; + + pDevice = this->pDevice; + if (this && pDevice) { + LOG_DBG("ForcetoTouched()\n"); + + pCurrentButton = pDevice->pbuttonInformation->buttons; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + pCurrentButton->state = ACTIVE; + last_val = 1; + if (mEnabled) { + input_report_abs(input_top, ABS_DISTANCE, 1); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, 1); + input_sync(input_bottom); + } + LOG_DBG("Leaving ForcetoTouched()\n"); + } +} + +/** + * fn static int write_register(pabovXX_t this, u8 address, u8 value) + * brief Sends a write register to the device + * param this Pointer to main parent struct + * param address 8-bit register address + * param value 8-bit register value to write to address + * return Value from i2c_master_send + */ +static int write_register(pabovXX_t this, u8 address, u8 value) +{ + struct i2c_client *i2c = 0; + char buffer[2]; + int returnValue = 0; + + buffer[0] = address; + buffer[1] = value; + returnValue = -ENOMEM; + if (this && this->bus) { + i2c = this->bus; + + returnValue = i2c_master_send(i2c, buffer, 2); + LOG_DBG("write_register Addr: 0x%x Val: 0x%x Return: %d\n", + address, value, returnValue); + } + if (returnValue < 0) { + ForcetoTouched(this); + LOG_ERR("Write_register-ForcetoTouched()\n"); + } + return returnValue; +} + +/** + * fn static int read_register(pabovXX_t this, u8 address, u8 *value) + * brief Reads a register's value from the device + * param this Pointer to main parent struct + * param address 8-Bit address to read from + * param value Pointer to 8-bit value to save register value to + * return Value from i2c_smbus_read_byte_data if < 0. else 0 + */ +static int read_register(pabovXX_t this, u8 address, u8 *value) +{ + struct i2c_client *i2c = 0; + s32 returnValue = 0; + + if (this && value && this->bus) { + i2c = this->bus; + returnValue = i2c_smbus_read_byte_data(i2c, address); + LOG_DBG("read_register Addr: 0x%x Return: 0x%x\n", + address, returnValue); + if (returnValue >= 0) { + *value = returnValue; + return 0; + } else { + return returnValue; + } + } + ForcetoTouched(this); + LOG_DBG("read_register-ForcetoTouched()\n"); + return -ENOMEM; +} + +/** + * detect if abov exist or not + * return 1 if chip exist else return 0 + */ +static int abov_detect(struct i2c_client *client) +{ + s32 returnValue = 0, i; + u8 address = ABOV_ABOV_WHOAMI_REG; + u8 value = 0xAB; + + if (client) { + for (i = 0; i < 3; i++) { + returnValue = i2c_smbus_read_byte_data(client, address); + if (value == returnValue) { + LOG_INFO("abov detect success!\n"); + return NORMAL_MODE; + } + + if (abov_tk_fw_mode_enter(client) == 0) { + LOG_INFO("abov boot detect success!\n"); + return BOOTLOADER_MODE; + } + SLEEP(100); + } + } + LOG_ERR("abov detect failed!!!\n"); + return UNKONOW_MODE; +} + +/** + * brief Perform a manual offset calibration + * param this Pointer to main parent struct + * return Value return value from the write register + */ +static int manual_offset_calibration(pabovXX_t this) +{ + s32 returnValue = 0; + //pabovXX_t this = abov_sar_ptr; + pabov_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + + pDevice = this->pDevice; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + + returnValue = write_register(this, ABOV_RECALI_REG, 0x01); + + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + return returnValue; +} + +/** + * brief sysfs show function for manual calibration which currently just + * returns register value. + */ +static ssize_t manual_offset_calibration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 reg_value = 0; + pabovXX_t this = dev_get_drvdata(dev); + + LOG_DBG("Reading IRQSTAT_REG\n"); + read_register(this, ABOV_IRQSTAT_REG, ®_value); + return scnprintf(buf, PAGE_SIZE, "%d\n", reg_value); +} + +/* brief sysfs store function for manual calibration */ +static ssize_t manual_offset_calibration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = dev_get_drvdata(dev); + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + if (val) { + LOG_DBG("Performing manual_offset_calibration()\n"); + manual_offset_calibration(this); + } + return count; +} +static DEVICE_ATTR(calibrate, 0644, manual_offset_calibration_show, + manual_offset_calibration_store); +static struct attribute *abov_attributes[] = { + &dev_attr_calibrate.attr, + NULL, +}; +static struct attribute_group abov_attr_group = { + .attrs = abov_attributes, +}; + +/** + * fn static int read_regStat(pabovXX_t this) + * brief Shortcut to read what caused interrupt. + * details This is to keep the drivers a unified + * function that will read whatever register(s) + * provide information on why the interrupt was caused. + * param this Pointer to main parent struct + * return If successful, Value of bit(s) that cause interrupt, else 0 + */ +static int read_regStat(pabovXX_t this) +{ + u8 data = 0; + + if (this) { + if (read_register(this, ABOV_IRQSTAT_REG, &data) == 0) + return (data & 0x00FF); + } + return 0; +} + +/** + * brief Initialize I2C config from platform data + * param this Pointer to main parent struct + * + */ +static void hw_init(pabovXX_t this) +{ + pabov_t pDevice = 0; + pabov_platform_data_t pdata = 0; + int i = 0; + /* configure device */ + LOG_INFO("Going to Setup I2C Registers\n"); + pDevice = this->pDevice; + pdata = pDevice->hw; + if (this && pDevice && pdata) { + while (i < pdata->i2c_reg_num) { + /* Write all registers/values contained in i2c_reg */ + LOG_INFO("Going to Write Reg: 0x%x Value: 0x%x\n", + pdata->pi2c_reg[i].reg, pdata->pi2c_reg[i].val); + write_register(this, pdata->pi2c_reg[i].reg, + pdata->pi2c_reg[i].val); + i++; + } + } else { + LOG_ERR("ERROR! platform data 0x%p\n", pDevice->hw); + /* Force to touched if error */ + ForcetoTouched(this); + LOG_ERR("Hardware_init-ForcetoTouched()\n"); + } +} + +/** + * fn static int initialize(pabovXX_t this) + * brief Performs all initialization needed to configure the device + * param this Pointer to main parent struct + * return Last used command's return value (negative if error) + */ +static int initialize(pabovXX_t this) +{ + int ret; + + if (this) { + /* prepare reset by disabling any irq handling */ + this->irq_disabled = 1; + disable_irq(this->irq); + /* perform a reset */ + ret = write_register(this, ABOV_SOFTRESET_REG, + 0x10); + if (ret < 0) + goto error_exit; + /* wait until the reset has finished by monitoring NIRQ */ + LOG_INFO("Software Reset. Waiting device back to continue.\n"); + /* just sleep for awhile instead of using a loop with reading irq status */ + msleep(300); + /* + * while(this->get_nirq_low && this->get_nirq_low()) { read_regStat(this); } + */ + LOG_INFO("Device back from the reset, continuing. NIRQ = %d\n", + this->get_nirq_low(this->board->irq_gpio)); + hw_init(this); + msleep(100); /* make sure everything is running */ + ret = manual_offset_calibration(this); + if (ret < 0) + goto error_exit; + /* re-enable interrupt handling */ + enable_irq(this->irq); + this->irq_disabled = 0; + + /* make sure no interrupts are pending since enabling irq will only + * work on next falling edge */ + read_regStat(this); + LOG_INFO("Exiting initialize(). NIRQ = %d\n", + this->get_nirq_low(this->board->irq_gpio)); + programming_done = ACTIVE; + return 0; + } + return -ENOMEM; + +error_exit: + programming_done = IDLE; + return ret; +} + +/** + * brief Handle what to do when a touch occurs + * param this Pointer to main parent struct + */ +static void touchProcess(pabovXX_t this) +{ + int counter = 0; + u8 i = 0; + int numberOfButtons = 0; + pabov_t pDevice = NULL; + struct _buttonInfo *buttons = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + struct _buttonInfo *pCurrentButton = NULL; + struct abov_platform_data *board; + + pDevice = this->pDevice; + board = this->board; + if (this && pDevice) { + LOG_DBG("Inside touchProcess()\n"); + read_register(this, ABOV_IRQSTAT_REG, &i); + + buttons = pDevice->pbuttonInformation->buttons; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + numberOfButtons = pDevice->pbuttonInformation->buttonSize; + + if (unlikely((buttons == NULL) || (input_top == NULL) || (input_bottom == NULL))) { + LOG_ERR("ERROR!! buttons or input NULL!!!\n"); + return; + } + + for (counter = 0; counter < numberOfButtons; counter++) { + pCurrentButton = &buttons[counter]; + if (pCurrentButton == NULL) { + LOG_ERR("ERR!current button index: %d NULL!\n", + counter); + return; /* ERRORR!!!! */ + } + switch (pCurrentButton->state) { + case IDLE: /* Button is being in far state! */ + if ((i & pCurrentButton->mask) == pCurrentButton->mask) { + LOG_DBG("CS %d State=BODY.\n", + counter); + if (board->cap_channel_top == counter) { + input_report_abs(input_top, ABS_DISTANCE, 2); + input_sync(input_top); + } else if (board->cap_channel_bottom == counter) { + input_report_abs(input_bottom, ABS_DISTANCE, 2); + input_sync(input_bottom); + } + pCurrentButton->state = S_BODY; + last_val = 2; + } else if ((i & pCurrentButton->mask) == (pCurrentButton->mask & 0x05)) { + LOG_DBG("CS %d State=PROX.\n", + counter); + if (board->cap_channel_top == counter) { + input_report_abs(input_top, ABS_DISTANCE, 1); + input_sync(input_top); + } else if (board->cap_channel_bottom == counter) { + input_report_abs(input_bottom, ABS_DISTANCE, 1); + input_sync(input_bottom); + } + pCurrentButton->state = S_PROX; + last_val = 0; + } else { + LOG_DBG("CS %d still in IDLE State.\n", + counter); + } + break; + case S_PROX: /* Button is being in proximity! */ + if ((i & pCurrentButton->mask) == pCurrentButton->mask) { + LOG_DBG("CS %d State=BODY.\n", + counter); + if (board->cap_channel_top == counter) { + input_report_abs(input_top, ABS_DISTANCE, 2); + input_sync(input_top); + } else if (board->cap_channel_bottom == counter) { + input_report_abs(input_bottom, ABS_DISTANCE, 2); + input_sync(input_bottom); + } + pCurrentButton->state = S_BODY; + last_val = 2; + } else if ((i & pCurrentButton->mask) == (pCurrentButton->mask & 0x05)) { + LOG_DBG("CS %d still in PROX State.\n", + counter); + } else{ + LOG_DBG("CS %d State=IDLE.\n", + counter); + if (board->cap_channel_top == counter) { + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + } else if (board->cap_channel_bottom == counter) { + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + } + pCurrentButton->state = IDLE; + last_val = 0; + } + break; + case S_BODY: /* Button is being in 0mm! */ + if ((i & pCurrentButton->mask) == pCurrentButton->mask) { + LOG_DBG("CS %d still in BODY State.\n", + counter); + } else if ((i & pCurrentButton->mask) == (pCurrentButton->mask & 0x05)) { + LOG_DBG("CS %d State=PROX.\n", + counter); + if (board->cap_channel_top == counter) { + input_report_abs(input_top, ABS_DISTANCE, 1); + input_sync(input_top); + } else if (board->cap_channel_bottom == counter) { + input_report_abs(input_bottom, ABS_DISTANCE, 1); + input_sync(input_bottom); + } + pCurrentButton->state = S_PROX; + last_val = 1; + } else{ + LOG_DBG("CS %d State=IDLE.\n", + counter); + if (board->cap_channel_top == counter) { + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + } else if (board->cap_channel_bottom == counter) { + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + } + pCurrentButton->state = IDLE; + last_val = 0; + } + break; + default: /* Shouldn't be here, device only allowed ACTIVE or IDLE */ + break; + }; + } + LOG_DBG("Leaving touchProcess()\n"); + } +} + +static int abov_get_nirq_state(unsigned irq_gpio) +{ + if (irq_gpio) { + return !gpio_get_value(irq_gpio); + } else { + LOG_ERR("abov irq_gpio is not set."); + return -EINVAL; + } +} + +static struct _totalButtonInformation smtcButtonInformation = { + .buttons = psmtcButtons, + .buttonSize = ARRAY_SIZE(psmtcButtons), +}; + +/** + *fn static void abov_reg_setup_init(struct i2c_client *client) + *brief read reg val form dts + * reg_array_len for regs needed change num + * data_array_val's format + */ +static void abov_reg_setup_init(struct i2c_client *client) +{ + u32 data_array_len = 0; + u32 *data_array; + int ret, i, j; + struct device_node *np = client->dev.of_node; + + ret = of_property_read_u32(np, "reg_array_len", &data_array_len); + if (ret < 0) { + LOG_ERR("data_array_len read error"); + return; + } + data_array = kmalloc(data_array_len * 2 * sizeof(u32), GFP_KERNEL); + ret = of_property_read_u32_array(np, "reg_array_val", + data_array, + data_array_len*2); + if (ret < 0) { + LOG_ERR("data_array_val read error"); + return; + } + for (i = 0; i < ARRAY_SIZE(abov_i2c_reg_setup); i++) { + for (j = 0; j < data_array_len*2; j += 2) { + if (data_array[j] == abov_i2c_reg_setup[i].reg) { + abov_i2c_reg_setup[i].val = data_array[j+1]; + LOG_INFO("read dtsi 0x%02x:0x%02x set reg\n", + data_array[j], data_array[j+1]); + } + } + } + kfree(data_array); +} + +static void abov_platform_data_of_init(struct i2c_client *client, + pabov_platform_data_t pplatData) +{ + struct device_node *np = client->dev.of_node; + u32 cap_channel_top, cap_channel_bottom; + int ret; + + client->irq = of_get_gpio(np, 0); + pplatData->irq_gpio = client->irq; + ret = of_property_read_u32(np, "cap,use_channel_top", &cap_channel_top); + + ret = of_property_read_u32(np, "cap,use_channel_bottom", &cap_channel_bottom); + pplatData->cap_channel_top = (int)cap_channel_top; + pplatData->cap_channel_bottom = (int)cap_channel_bottom; + + pplatData->get_is_nirq_low = abov_get_nirq_state; + pplatData->init_platform_hw = NULL; + /* pointer to an exit function. Here in case needed in the future */ + /* + *.exit_platform_hw = abov_exit_ts, + */ + pplatData->exit_platform_hw = NULL; + abov_reg_setup_init(client); + pplatData->pi2c_reg = abov_i2c_reg_setup; + pplatData->i2c_reg_num = ARRAY_SIZE(abov_i2c_reg_setup); + + pplatData->pbuttonInformation = &smtcButtonInformation; + + ret = of_property_read_string(np, "label", &pplatData->fw_name); + if (ret < 0) { + LOG_ERR("firmware name read error!\n"); + return; + } + LOG_INFO("firmware name = %s\n", pplatData->fw_name); + + return; +} + +static ssize_t capsense_soft_reset_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 8, "%d\n", programming_done); +} + +static ssize_t capsense_soft_reset_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + pabov_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + + pDevice = this->pDevice; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + + if (!count || (this == NULL)) + return -EINVAL; + + if (!strncmp(buf, "reset", 5) || !strncmp(buf, "1", 1)) + write_register(this, ABOV_SOFTRESET_REG, 0x10); + + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + + return count; +} + +static ssize_t capsense_reset_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + pabov_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + int ret = 0; + + pDevice = this->pDevice; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + + LOG_DBG("headset insert,going to force calibrate\n"); + if (!count || (this == NULL)) + return -EINVAL; + + if (!strncmp(buf, "1", 1)) + ret = write_register(this, ABOV_RECALI_REG, 0x01); + if (ret < 0) + LOG_ERR(" headset insert,calibrate cap sensor failed\n"); + + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + return count; +} + +static ssize_t capsense_enable_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 8, "%d\n", mEnabled); +} + +static ssize_t capsense_enable_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + pabov_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + + pDevice = this->pDevice; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + + if (!count || (this == NULL)) + return -EINVAL; + + if ((!strncmp(buf, "1", 1)) && (mEnabled == 0)) { + LOG_INFO("enable cap sensor\n"); + initialize(this); + + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + mEnabled = 1; + } else if ((!strncmp(buf, "0", 1)) && (mEnabled == 1)) { + LOG_INFO("disable cap sensor\n"); + + write_register(this, ABOV_CTRL_MODE_RET, 0x02); + + input_report_abs(input_top, ABS_DISTANCE, -1); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, -1); + input_sync(input_bottom); + mEnabled = 0; + } else { + LOG_ERR("unknown enable symbol\n"); + } + + return count; +} + +#ifdef USE_SENSORS_CLASS +static int capsensor_set_enable(struct sensors_classdev *sensors_cdev, unsigned int enable) +{ + pabovXX_t this = abov_sar_ptr; + pabov_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + + pDevice = this->pDevice; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + + if(first_report == false) { + first_report = true; + input_report_abs(input_top, ABS_DISTANCE, -1); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, -1); + input_sync(input_bottom); + } + + if ((enable == 1) && (mEnabled == 0)) { + LOG_INFO("enable cap sensor\n"); + initialize(this); + + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + mEnabled = 1; + } else if ((enable == 0) && (mEnabled == 1)) { + LOG_INFO("disable cap sensor\n"); + + write_register(this, ABOV_CTRL_MODE_RET, 0x02); + input_report_abs(input_top, ABS_DISTANCE, -1); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, -1); + input_sync(input_bottom); + mEnabled = 0; + } else { + LOG_ERR("unknown enable symbol\n"); + } + + return 0; +} +#endif + +static ssize_t capsense_calibrate_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + pabovXX_t this = abov_sar_ptr; + u8 reg_value = 0; + + read_register(this, ABOV_IRQSTAT_REG, ®_value); + return snprintf(buf, 8, "%d\n", reg_value); +} + +static ssize_t capsense_calibrate_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + + if (!count || (this == NULL)) + return -EINVAL; + + if (!strncmp(buf, "1", 1)) + manual_offset_calibration(this); + + LOG_DBG("calibration done\n"); + + return count; +} +static ssize_t reg_dump_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u8 reg_value = 0, i; + u16 offset_value = 0, diff_value = 0; + pabovXX_t this = abov_sar_ptr; + char *p = buf; + + if (this->read_flag) { + this->read_flag = 0; + read_register(this, this->read_reg, ®_value); + p += snprintf(p, PAGE_SIZE, "%02x\n", reg_value); + return (p-buf); + } + + for (i = 0; i < 0x26; i++) { + read_register(this, i, ®_value); + p += snprintf(p, PAGE_SIZE, "(0x%02x)=0x%02x\n", + i, reg_value); + } + + for (i = 0x80; i < 0x8C; i++) { + read_register(this, i, ®_value); + p += snprintf(p, PAGE_SIZE, "(0x%02x)=0x%02x\n", + i, reg_value); + } + if (this->get_ch0_flag) { + /* diff */ + read_register(this, 0x1C, ®_value); + diff_value = reg_value; + diff_value <<= 8; + read_register(this, 0x1D, ®_value); + diff_value += reg_value; + + /* offset */ + read_register(this, 0x20, ®_value); + offset_value = reg_value; + offset_value <<= 8; + read_register(this, 0x21, ®_value); + offset_value += reg_value; + } else { + + /* diff */ + read_register(this, 0x1E, ®_value); + diff_value = reg_value; + diff_value <<= 8; + read_register(this, 0x1F, ®_value); + diff_value += reg_value; + + /* offset */ + read_register(this, 0x22, ®_value); + offset_value = reg_value; + offset_value <<= 8; + read_register(this, 0x23, ®_value); + offset_value += reg_value; + } + + reg_value = gpio_get_value(this->board->irq_gpio); + p += snprintf(p, PAGE_SIZE, "NIRQ=%d\n", reg_value); + + p += snprintf(p, PAGE_SIZE, "diff_value=0x%04x\n", + diff_value); + p += snprintf(p, PAGE_SIZE, "offset_value=0x%04x\n", + offset_value); + + return (p-buf); +} + +static ssize_t reg_dump_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + unsigned int val, reg, opt; + if (strcmp("select_ch0\n", buf) == 0) { + this->get_ch0_flag = 1; + } else if (strcmp("select_ch1\n", buf) == 0) { + this->get_ch0_flag = 0; + } else if (strcmp("select_ch2\n", buf) == 0) { + this->get_ch0_flag = 0; + } else if (strcmp("calibrate\n", buf) == 0) { + write_register(this, ABOV_RECALI_REG, 0x01); + } else if (sscanf(buf, "%x,%x,%x", ®, &val, &opt) == 3) { + LOG_DBG(" read reg = 0x%02x\n", *(u8 *)®); + this->read_reg = *((u8 *)®); + this->read_flag = 1; + } else if (sscanf(buf, "%x,%x", ®, &val) == 2) { + LOG_DBG("reg = 0x%02x, val = 0x%02x\n", + *(u8 *)®, *(u8 *)&val); + write_register(this, *((u8 *)®), *((u8 *)&val)); + } + + return count; +} + +static void ps_notify_callback_work(struct work_struct *work) +{ + pabovXX_t this = container_of(work, abovXX_t, ps_notify_work); + pabov_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + int ret = 0; + + pDevice = this->pDevice; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + + LOG_DBG("Usb insert,going to force calibrate\n"); + ret = write_register(this, ABOV_RECALI_REG, 0x01); + if (ret < 0) + LOG_ERR(" Usb insert,calibrate cap sensor failed\n"); + + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); +} + +static int ps_get_state(struct power_supply *psy, bool *present) +{ + union power_supply_propval pval = { 0 }; + int retval; + + retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, + &pval); + if (retval) { + LOG_ERR("%s psy get property failed\n", psy->desc->name); + return retval; + } + *present = (pval.intval) ? true : false; + LOG_DBG("%s is %s\n", psy->desc->name, + (*present) ? "present" : "not present"); + return 0; +} + +static int ps_notify_callback(struct notifier_block *self, + unsigned long event, void *p) +{ + pabovXX_t this = container_of(self, abovXX_t, ps_notif); + struct power_supply *psy = p; + bool present; + int retval; + + if(!psy) + return -ENODEV; + if(!psy->desc) + return -ENODEV; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0) + if (event == PSY_EVENT_PROP_CHANGED +#else + if ((event == PSY_EVENT_PROP_ADDED || event == PSY_EVENT_PROP_CHANGED) +#endif + && psy && psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, "usb", sizeof("usb"))) { + LOG_DBG("ps notification: event = %lu\n", event); + retval = ps_get_state(psy, &present); + if (retval) { + LOG_ERR("psy get property failed\n"); + return retval; + } + + if (event == PSY_EVENT_PROP_CHANGED) { + if (this->ps_is_present == present) { + LOG_DBG("ps present state not change\n"); + return 0; + } + } + this->ps_is_present = present; + schedule_work(&this->ps_notify_work); + } + + return 0; +} + +static char *toString(u8 *buffer, int len) +{ + int i = 0; + int h = 0; + int l = 0; + + if (buffer == 0 || len < 1) + _Buffer[0] = 0x00; + else + for (i = 0; i < len && i < 40; i++) { + h = (buffer[i] & 0xf0) >> 4; + l = buffer[i] & 0x0f; + _Buffer[i * 3 + 0] = h < 10 ? '0' + h : 'A' + (h - 10); + _Buffer[i * 3 + 1] = l < 10 ? '0' + l : 'A' + (l - 10); + _Buffer[i * 3 + 2] = ' '; + _Buffer[i * 3 + 3] = 0x00; + } + + return _Buffer; +} + +static int _i2c_adapter_block_write(struct i2c_client *client, u8 *data, u8 len, int outData) +{ + u8 buffer[C_I2C_FIFO_SIZE]; + u8 left = len; + u8 offset = 0; + u8 retry = 0; + + struct i2c_msg msg = { + .addr = client->addr & I2C_MASK_FLAG, + .flags = 0, + .buf = buffer, + }; + + if (data == NULL || len < 1) { + LOG_ERR("Invalid : data is null or len=%d\n", len); + return -EINVAL; + } + + while (left > 0) { + retry = 0; + if (left >= C_I2C_FIFO_SIZE) { + msg.buf = &data[offset]; + msg.len = C_I2C_FIFO_SIZE; + left -= C_I2C_FIFO_SIZE; + offset += C_I2C_FIFO_SIZE; + } else { + msg.buf = &data[offset]; + msg.len = left; + left = 0; + } + + while (i2c_transfer(client->adapter, &msg, 1) != 1) { + retry++; + if (retry > 10) { + if (outData) + LOG_ERR("OUT : fail - addr:%#x len:%d data:%s\n", client->addr, msg.len, toString(msg.buf, msg.len)); + else + LOG_ERR("OUT : fail - addr:%#x len:%d \n", client->addr, msg.len); + return -EIO; + } + } + } + return 0; +} + +static int i2c_adapter_block_write_nodatalog(struct i2c_client *client, u8 *data, u8 len) +{ + return _i2c_adapter_block_write(client, data, len, 0); +} + +static int abov_tk_check_busy(struct i2c_client *client) +{ + int ret, count = 0; + unsigned char val = 0x00; + + do { + ret = i2c_master_recv(client, &val, sizeof(val)); + if (val & 0x01) { + count++; + if (count > 1000) { + LOG_ERR("val = 0x%x\r\n", val); + return ret; + } + } else { + break; + } + } while (1); + + return ret; +} + +static int abov_tk_fw_write(struct i2c_client *client, unsigned char *addrH, + unsigned char *addrL, unsigned char *val) +{ + int length = 36, ret = 0; + unsigned char buf[40] = {0, }; + + buf[0] = 0xAC; + buf[1] = 0x7A; + memcpy(&buf[2], addrH, 1); + memcpy(&buf[3], addrL, 1); + memcpy(&buf[4], val, 32); + ret = i2c_adapter_block_write_nodatalog(client, buf, length); + if (ret < 0) { + LOG_ERR("Firmware write fail ...\n"); + return ret; + } + + SLEEP(3); + abov_tk_check_busy(client); + + return 0; +} + +static int abov_tk_reset_for_bootmode(struct i2c_client *client) +{ + int ret, retry_count = 10; + unsigned char buf[16] = {0, }; + +retry: + buf[0] = 0xF0; + buf[1] = 0xAA; + ret = i2c_master_send(client, buf, 2); + if (ret < 0) { + LOG_ERR("write fail(retry:%d)\n", retry_count); + if (retry_count-- > 0) { + goto retry; + } + return -EIO; + } else { + LOG_INFO("success reset & boot mode\n"); + return 0; + } +} + +static int i2c_adapter_read_raw(struct i2c_client *client, u8 *data, u8 len) +{ + struct i2c_msg msg; + int ret; + int retry = 3; + + msg.addr = client->addr; + msg.flags = 1; + msg.len = len; + msg.buf = data; + while (retry--) { + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret == 1) { + break; + } + + if (ret < 0) { + LOG_ERR("fail(data read)(%d)\n", retry); + SLEEP(10); + } + } + + if (retry) { + LOG_INFO("TRAN : succ - addr:%#x ... len:%d data:%s\n", client->addr, msg.len, toString(msg.buf, msg.len)); + return 0; + } else { + LOG_ERR("TRAN : fail - addr:%#x len:%d \n", client->addr, msg.len); + return -EIO; + } +} + +static int abov_tk_fw_mode_enter(struct i2c_client *client) +{ + int ret = 0; + unsigned char buf[40] = {0, }; + + buf[0] = 0xAC; + buf[1] = 0x5B; + ret = i2c_master_send(client, buf, 2); + if (ret != 2) { + LOG_ERR("SEND : fail - addr:%#x data:%#x %#x... ret:%d\n", client->addr, buf[0], buf[1], ret); + return -EIO; + } + LOG_INFO("SEND : succ - addr:%#x data:%#x %#x... ret:%d\n", client->addr, buf[0], buf[1], ret); + SLEEP(5); + + ret = i2c_adapter_read_raw(client, buf, 1); + if (ret < 0) { + LOG_ERR("Enter fw mode fail ...\n"); + return -EIO; + } + + LOG_INFO("succ ... data:%#x\n", buf[0]); + + return 0; +} + +static int abov_tk_fw_mode_exit(struct i2c_client *client) +{ + int ret = 0; + unsigned char buf[40] = {0, }; + + buf[0] = 0xAC; + buf[1] = 0xE1; + ret = i2c_master_send(client, buf, 2); + if (ret != 2) { + LOG_ERR("SEND : fail - addr:%#x data:%#x %#x ... ret:%d\n", client->addr, buf[0], buf[1], ret); + return -EIO; + } + LOG_INFO("SEND : succ - addr:%#x data:%#x %#x ... ret:%d\n", client->addr, buf[0], buf[1], ret); + + return 0; +} + +static int abov_tk_flash_erase(struct i2c_client *client) +{ + int ret = 0; + unsigned char buf[16] = {0, }; + + buf[0] = 0xAC; +#ifdef ABOV_POWER_CONFIG + buf[1] = 0x2D; +#else + buf[1] = 0x2E; +#endif + + ret = i2c_master_send(client, buf, 2); + if (ret != 2) { + LOG_ERR("SEND : fail - addr:%#x data:%#x %#x ... ret:%d\n", client->addr, buf[0], buf[1], ret); + return -EIO; + } + + LOG_INFO("SEND : succ - addr:%#x data:%#x %#x ... ret:%d\n", client->addr, buf[0], buf[1], ret); + + return 0; +} + +static int abov_tk_i2c_read_checksum(struct i2c_client *client) +{ + unsigned char checksum[6] = {0, }; + unsigned char buf[16] = {0, }; + int ret; + + checksum_h = 0; + checksum_l = 0; + +#ifdef ABOV_POWER_CONFIG + buf[0] = 0xAC; + buf[1] = 0x9E; + buf[2] = 0x10; + buf[3] = 0x00; + buf[4] = 0x3F; + buf[5] = 0xFF; + ret = i2c_master_send(client, buf, 6); +#else + buf[0] = 0xAC; + buf[1] = 0x9E; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = checksum_h_bin; + buf[5] = checksum_l_bin; + ret = i2c_master_send(client, buf, 6); +#endif + if (ret != 6) { + LOG_ERR("SEND : fail - addr:%#x len:%d data:%s ... ret:%d\n", client->addr, 6, toString(buf, 6), ret); + return -EIO; + } + SLEEP(5); + + buf[0] = 0x00; + ret = i2c_master_send(client, buf, 1); + if (ret != 1) { + LOG_ERR("SEND : fail - addr:%#x data:%#x ... ret:%d\n", client->addr, buf[0], ret); + return -EIO; + } + SLEEP(5); + + ret = i2c_adapter_read_raw(client, checksum, 6); + if (ret < 0) { + LOG_ERR("Read raw fail ... \n"); + return -EIO; + } + + checksum_h = checksum[4]; + checksum_l = checksum[5]; + + return 0; +} + +static int _abov_fw_update(struct i2c_client *client, const u8 *image, u32 size) +{ + int ret, ii = 0; + int count; + unsigned short address; + unsigned char addrH, addrL; + unsigned char data[32] = {0, }; + pabovXX_t this = abov_sar_ptr; + + LOG_INFO(": call in\r\n"); + + if (abov_tk_reset_for_bootmode(client) < 0) { + LOG_ERR("don't reset(enter boot mode)!"); + return -EIO; + } + + SLEEP(45); + + for (ii = 0; ii < 10; ii++) { + if (abov_tk_fw_mode_enter(client) < 0) { + LOG_ERR("don't enter the download mode! %d", ii); + SLEEP(40); + continue; + } + break; + } + + if (10 <= ii) { + return -EAGAIN; + } + + if (abov_tk_flash_erase(client) < 0) { + LOG_ERR("don't erase flash data!"); + return -EIO; + } + + SLEEP(1400); + + address = 0x800; + count = size / 32; + + for (ii = 0; ii < count; ii++) { + /* first 32byte is header */ + addrH = (unsigned char)((address >> 8) & 0xFF); + addrL = (unsigned char)(address & 0xFF); + memcpy(data, &image[ii * 32], 32); + ret = abov_tk_fw_write(client, &addrH, &addrL, data); + if (ret < 0) { + LOG_ERR("fw_write.. ii = 0x%x err\r\n", ii); + return ret; + } + + address += 0x20; + memset(data, 0, 32); + } + + ret = abov_tk_i2c_read_checksum(client); + ret = abov_tk_fw_mode_exit(client); + if ((checksum_h == checksum_h_bin) && (checksum_l == checksum_l_bin)) { + LOG_INFO("checksum successful. checksum_h:%x=%x && checksum_l:%x=%x\n", + checksum_h, checksum_h_bin, checksum_l, checksum_l_bin); + } else { + LOG_ERR("checksum error. checksum_h:%x=%x && checksum_l:%x=%x\n", + checksum_h, checksum_h_bin, checksum_l, checksum_l_bin); + ret = -1; + } + SLEEP(100); + + hw_init(this); + + return ret; +} + +static int abov_fw_update(bool force) +{ + int update_loop; + pabovXX_t this = abov_sar_ptr; + struct i2c_client *client = this->bus; + int rc = 0; + bool fw_upgrade = false; + u8 fw_version = 0, fw_file_version = 0; + u8 fw_modelno = 0, fw_file_modeno = 0; + const struct firmware *fw = NULL; + char fw_name[32] = {0}; + + strcpy(fw_name, this->board->fw_name); + strlcat(fw_name, ".BIN", NAME_MAX); + rc = request_firmware(&fw, fw_name, this->pdev); + if (rc < 0) { + LOG_ERR("Request firmware failed - %s (%d)\n", + this->board->fw_name, rc); + return rc; + } + + if (force == false) { + read_register(this, ABOV_VERSION_REG, &fw_version); + read_register(this, ABOV_MODELNO_REG, &fw_modelno); + } + + fw_file_modeno = fw->data[1]; + fw_file_version = fw->data[5]; + checksum_h_bin = fw->data[8]; + checksum_l_bin = fw->data[9]; + + if ((force) || (fw_version < fw_file_version) || (fw_modelno != fw_file_modeno)) + fw_upgrade = true; + else { + LOG_INFO("Exiting fw upgrade...\n"); + fw_upgrade = false; + fw_dl_status = 0; + goto rel_fw; + } + + if (fw_upgrade) { + fw_dl_status = 2; + for (update_loop = 0; update_loop < 10; update_loop++) { + rc = _abov_fw_update(client, &fw->data[32], fw->size-32); + if (rc < 0) + LOG_ERR("retry : %d times!\n", update_loop); + else + break; + SLEEP(400); + } + if (update_loop >= 10) { + fw_dl_status = 1; + rc = -EIO; + } + fw_dl_status = 0; + } + +rel_fw: + release_firmware(fw); + return rc; +} + +static ssize_t capsense_fw_ver_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u8 fw_version = 0; + pabovXX_t this = abov_sar_ptr; + + read_register(this, ABOV_VERSION_REG, &fw_version); + + return snprintf(buf, 16, "ABOV:0x%x\n", fw_version); +} + +static ssize_t capsense_update_fw_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + unsigned long val; + int rc; + + if (count > 2) + return -EINVAL; + + rc = kstrtoul(buf, 10, &val); + if (rc != 0) + return rc; + + this->irq_disabled = 1; + disable_irq(this->irq); + + mutex_lock(&this->mutex); + if (!this->loading_fw && val) { + this->loading_fw = true; + abov_fw_update(false); + this->loading_fw = false; + } + mutex_unlock(&this->mutex); + + enable_irq(this->irq); + this->irq_disabled = 0; + + return count; +} + +static ssize_t capsense_force_update_fw_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + unsigned long val; + int rc; + + if (count > 2) + return -EINVAL; + + rc = kstrtoul(buf, 10, &val); + if (rc != 0) + return rc; + + this->irq_disabled = 1; + disable_irq(this->irq); + + mutex_lock(&this->mutex); + if (!this->loading_fw && val) { + this->loading_fw = true; + abov_fw_update(true); + this->loading_fw = false; + } + mutex_unlock(&this->mutex); + + enable_irq(this->irq); + this->irq_disabled = 0; + + return count; +} + +static ssize_t capsense_fw_download_status_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 8, "%d", fw_dl_status); +} + +static void capsense_update_work(struct work_struct *work) +{ + pabovXX_t this = container_of(work, abovXX_t, fw_update_work.worker); + + LOG_INFO(": start update firmware\n"); + mutex_lock(&this->mutex); + this->loading_fw = true; + abov_fw_update(this->fw_update_work.force_update); + this->loading_fw = false; + mutex_unlock(&this->mutex); + LOG_INFO(": update firmware end\n"); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) +static struct class_attribute class_attr_soft_reset = + __ATTR(soft_reset, 0660, capsense_soft_reset_show, capsense_soft_reset_store); + +static struct class_attribute class_attr_reset = + __ATTR(reset, 0660, NULL, capsense_reset_store); + +static struct class_attribute class_attr_enable = + __ATTR(enable, 0660, capsense_enable_show, capsense_enable_store); + +static struct class_attribute class_attr_reg = + __ATTR(reg, 0660, reg_dump_show, reg_dump_store); + +static struct class_attribute class_attr_update_fw = + __ATTR(update_fw, 0660, capsense_fw_ver_show, capsense_update_fw_store); + +static struct class_attribute class_attr_force_update_fw = + __ATTR(force_update_fw, 0660, capsense_fw_ver_show, capsense_force_update_fw_store); + +static struct class_attribute class_attr_fw_download_status = + __ATTR(fw_download_status, 0660, capsense_fw_download_status_show, NULL); +static struct class_attribute class_attr_calibrate = + __ATTR(calibrate, 0660, capsense_calibrate_show, capsense_calibrate_store); + +static struct attribute *capsense_class_attrs[] = { + &class_attr_soft_reset.attr, + &class_attr_reset.attr, + &class_attr_enable.attr, + &class_attr_reg.attr, + &class_attr_update_fw.attr, + &class_attr_force_update_fw.attr, + &class_attr_fw_download_status.attr, + &class_attr_calibrate.attr, + NULL, +}; +ATTRIBUTE_GROUPS(capsense_class); +#else +static struct class_attribute capsense_class_attributes[] = { + __ATTR(soft_reset, 0660, capsense_soft_reset_show, capsense_soft_reset_store), + __ATTR(reset, 0660, NULL, capsense_reset_store), + __ATTR(enable, 0660, capsense_enable_show, capsense_enable_store), + __ATTR(reg, 0660, reg_dump_show, reg_dump_store), + __ATTR(update_fw, 0660, capsense_fw_ver_show, capsense_update_fw_store), + __ATTR(force_update_fw, 0660, capsense_fw_ver_show, capsense_force_update_fw_store), + __ATTR(fw_download_status, 0660, capsense_fw_download_status_show, NULL), + __ATTR(calibrate, 0660, capsense_calibrate_show, capsense_calibrate_store), + __ATTR_NULL, +}; +#endif + +static struct class capsense_class = { + .name = "capsense", + .owner = THIS_MODULE, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + .class_groups = capsense_class_groups, +#else + .class_attrs = capsense_class_attributes, +#endif +}; + +/** + * fn static int abov_probe(struct i2c_client *client, const struct i2c_device_id *id) + * brief Probe function + * param client pointer to i2c_client + * param id pointer to i2c_device_id + * return Whether probe was successful + */ +static int abov_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + pabovXX_t this = 0; + pabov_t pDevice = 0; + pabov_platform_data_t pplatData = 0; + int ret; + bool isForceUpdate = false; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + struct power_supply *psy = NULL; + + LOG_INFO("abov_probe()\n"); + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) { + ret = -EIO; + goto err_this_device; + } + + pplatData = kzalloc(sizeof(struct abov_platform_data), GFP_KERNEL); + if (!pplatData) { + LOG_ERR("platform data is required!\n"); + return -EINVAL; + } + + pplatData->cap_vdd = regulator_get(&client->dev, "cap_vdd"); + if (IS_ERR(pplatData->cap_vdd)) { + if (PTR_ERR(pplatData->cap_vdd) == -EPROBE_DEFER) { + ret = PTR_ERR(pplatData->cap_vdd); + goto err_vdd_defer; + } + LOG_ERR("Failed to get regulator\n"); + } else { + ret = regulator_enable(pplatData->cap_vdd); + + if (ret) { + regulator_put(pplatData->cap_vdd); + LOG_ERR("Error %d enable regulator\n", + ret); + goto err_vdd_defer; + } + pplatData->cap_vdd_en = true; + LOG_INFO("cap_vdd regulator is %s\n", + regulator_is_enabled(pplatData->cap_vdd) ? + "on" : "off"); + } + + /* detect if abov exist or not */ + ret = abov_detect(client); + if (ret == UNKONOW_MODE) { + ret = -ENODEV; + goto err_this_device; + } else if (ret == BOOTLOADER_MODE) { + LOG_INFO("abov enter boot mode\n"); + isForceUpdate = true; + } + abov_platform_data_of_init(client, pplatData); + client->dev.platform_data = pplatData; + + this = kzalloc(sizeof(abovXX_t), GFP_KERNEL); + LOG_INFO("Initialized Main Memory: 0x%p\n", this); + + if (this) { + /* In case we need to reinitialize data + * (e.q. if suspend reset device) */ + this->init = initialize; + /* shortcut to read status of interrupt */ + this->refreshStatus = read_regStat; + /* pointer to function from platform data to get pendown + * (1->NIRQ=0, 0->NIRQ=1) */ + this->get_nirq_low = pplatData->get_is_nirq_low; + /* save irq in case we need to reference it */ + this->irq = gpio_to_irq(client->irq); + /* do we need to create an irq timer after interrupt ? */ + this->useIrqTimer = 0; + this->board = pplatData; + /* Setup function to call on corresponding reg irq source bit */ + if (MAX_NUM_STATUS_BITS >= 8) { + this->statusFunc[0] = 0; /* TXEN_STAT */ + this->statusFunc[1] = 0; /* UNUSED */ + this->statusFunc[2] = 0; /* UNUSED */ + this->statusFunc[3] = 0;/*read_rawData; CONV_STAT */ + this->statusFunc[4] = 0; /* COMP_STAT */ + this->statusFunc[5] = touchProcess; /* RELEASE_STAT */ + this->statusFunc[6] = touchProcess; /* TOUCH_STAT */ + this->statusFunc[7] = 0; /* RESET_STAT */ + } + + /* setup i2c communication */ + this->bus = client; + i2c_set_clientdata(client, this); + + /* record device struct */ + this->pdev = &client->dev; + + /* create memory for device specific struct */ + this->pDevice = pDevice = kzalloc(sizeof(abov_t), GFP_KERNEL); + LOG_INFO("\t Initialized Device Specific Memory: 0x%p\n", + pDevice); + abov_sar_ptr = this; + if (pDevice) { + /* for accessing items in user data (e.g. calibrate) */ + ret = sysfs_create_group(&client->dev.kobj, &abov_attr_group); + + + /* Check if we hava a platform initialization function to call*/ + if (pplatData->init_platform_hw) + pplatData->init_platform_hw(); + + /* Add Pointer to main platform data struct */ + pDevice->hw = pplatData; + + /* Initialize the button information initialized with keycodes */ + pDevice->pbuttonInformation = pplatData->pbuttonInformation; + + /* Create the input device */ + input_top = input_allocate_device(); + if (!input_top) { + ret = -ENOMEM; + goto err_top_alloc; + } + + /* Set all the keycodes */ + __set_bit(EV_ABS, input_top->evbit); + input_set_abs_params(input_top, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_top = input_top; + input_top->name = "ABOV Cap Touch top"; + if (input_register_device(input_top)) { + LOG_ERR("add top cap touch unsuccess\n"); + ret = -ENOMEM; + goto err_top_register; + } + /* Create the input device */ + input_bottom = input_allocate_device(); + if (!input_bottom) { + ret = -ENOMEM; + goto err_bottom_alloc; + } + /* Set all the keycodes */ + __set_bit(EV_ABS, input_bottom->evbit); + input_set_abs_params(input_bottom, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_bottom = input_bottom; + /* save the input pointer and finish initialization */ + input_bottom->name = "ABOV Cap Touch bottom"; + if (input_register_device(input_bottom)) { + LOG_ERR("add bottom cap touch unsuccess\n"); + ret = -ENOMEM; + goto err_bottom_register; + } + } else { + ret = -ENOMEM; + goto err_pDevice; + } + + ret = class_register(&capsense_class); + if (ret < 0) { + LOG_ERR("Create fsys class failed (%d)\n", ret); + goto err_class_register; + } + + /*restore sys/class/capsense label*/ + kobject_uevent(&capsense_class.p->subsys.kobj, KOBJ_CHANGE); + +#ifdef USE_SENSORS_CLASS + sensors_capsensor_top_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_top_cdev.sensors_poll_delay = NULL; + ret = sensors_classdev_register(&input_top->dev, &sensors_capsensor_top_cdev); + if (ret < 0) { + LOG_ERR("create top cap sensor_class file failed (%d)\n", ret); + goto err_class_creat; + } + sensors_capsensor_bottom_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_bottom_cdev.sensors_poll_delay = NULL; + ret = sensors_classdev_register(&input_bottom->dev, &sensors_capsensor_bottom_cdev); + if (ret < 0) { + LOG_ERR("create bottom cap sensor_class file failed (%d)\n", ret); + goto err_bottom_classdev; + } +#endif + + abovXX_sar_init(this); + + write_register(this, ABOV_CTRL_MODE_RET, 0x02); + mEnabled = 0; + + INIT_WORK(&this->ps_notify_work, ps_notify_callback_work); + this->ps_notif.notifier_call = ps_notify_callback; + ret = power_supply_reg_notifier(&this->ps_notif); + if (ret) { + LOG_ERR( + "Unable to register ps_notifier: %d\n", ret); + goto free_ps_notifier; + } + + psy = power_supply_get_by_name("usb"); + if (psy) { + ret = ps_get_state(psy, &this->ps_is_present); + if (ret) { + LOG_ERR( + "psy get property failed rc=%d\n", + ret); + goto free_ps_notifier; + } + } + + this->loading_fw = false; + fw_dl_status = 1; + this->fw_update_work.force_update = isForceUpdate; + INIT_WORK(&this->fw_update_work.worker, capsense_update_work); + schedule_work(&this->fw_update_work.worker); + + LOG_INFO("abov_probe() SUCCESS\n"); + return 0; + } + ret = -ENOMEM; + goto err_this_device; + +free_ps_notifier: + LOG_ERR("unregister bottom classdev and ps_notif.\n"); + power_supply_unreg_notifier(&this->ps_notif); + sensors_classdev_unregister(&sensors_capsensor_bottom_cdev); + +err_bottom_classdev: + LOG_ERR("unregister top classdev.\n"); + sensors_classdev_unregister(&sensors_capsensor_top_cdev); + +err_class_creat: + LOG_ERR("unregister capsense class.\n"); + class_unregister(&capsense_class); + +err_class_register: + LOG_ERR("unregister bottom device.\n"); + input_unregister_device(input_bottom); + +err_bottom_register: + LOG_ERR("input free bottom device.\n"); + input_free_device(input_bottom); + +err_bottom_alloc: + LOG_ERR("unregister top device.\n"); + input_unregister_device(input_top); + +err_top_register: + LOG_ERR("input free top device.\n"); + input_free_device(input_top); + +err_top_alloc: + LOG_ERR("input free pDevice.\n"); + kfree(this->pDevice); + +err_pDevice: + LOG_ERR("free device this.\n"); + kfree(this); + +err_this_device: + LOG_ERR("device this defer.\n"); +#if 0 + regulator_disable(pplatData->cap_svdd); + regulator_put(pplatData->cap_svdd); + +err_svdd_error: + LOG_DBG("svdd defer.\n"); + regulator_disable(pplatData->cap_vdd); + regulator_put(pplatData->cap_vdd); +#endif + +err_vdd_defer: + LOG_ERR("free pplatData.\n"); + + + kfree(pplatData); + return ret; +} + +/** + * fn static int abov_remove(struct i2c_client *client) + * brief Called when device is to be removed + * param client Pointer to i2c_client struct + * return Value from abovXX_sar_remove() + */ +static int abov_remove(struct i2c_client *client) +{ + pabov_platform_data_t pplatData = 0; + pabov_t pDevice = 0; + pabovXX_t this = i2c_get_clientdata(client); + + pDevice = this->pDevice; + pplatData = client->dev.platform_data; + if (this && pDevice) { + power_supply_unreg_notifier(&this->ps_notif); + cancel_work_sync(&this->fw_update_work.worker); + cancel_work_sync(&this->ps_notify_work); +#ifdef USE_SENSORS_CLASS + sensors_classdev_unregister(&sensors_capsensor_top_cdev); + sensors_classdev_unregister(&sensors_capsensor_bottom_cdev); +#endif + input_unregister_device(pDevice->pbuttonInformation->input_top); + input_unregister_device(pDevice->pbuttonInformation->input_bottom); +#if 0 + if (this->board->cap_svdd_en) { + regulator_disable(this->board->cap_svdd); + regulator_put(this->board->cap_svdd); + } +#endif + if (gpio_is_valid(pplatData ->irq_gpio)) + gpio_free(pplatData ->irq_gpio); + if (this->board->cap_vdd_en) { + regulator_disable(this->board->cap_vdd); + regulator_put(this->board->cap_vdd); + } + sysfs_remove_group(&client->dev.kobj, &abov_attr_group); + class_unregister(&capsense_class); + if (pplatData && pplatData->exit_platform_hw) + pplatData->exit_platform_hw(); + kfree(this->pDevice); + } + return abovXX_sar_remove(this); +} +#if defined(USE_KERNEL_SUSPEND) +/*====================================================*/ +/***** Kernel Suspend *****/ +static int abov_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + pabovXX_t this = i2c_get_clientdata(client); + + abovXX_suspend(this); + return 0; +} +/***** Kernel Resume *****/ +static int abov_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + pabovXX_t this = i2c_get_clientdata(client); + + abovXX_resume(this); + return 0; +} +/*====================================================*/ +#endif + +#ifdef CONFIG_OF +static const struct of_device_id abov_match_table[] = { + { .compatible = "abov,abov_sar" DRIVER_NAME }, + { }, +}; +#endif + +#if defined(USE_KERNEL_SUSPEND) +static const struct dev_pm_ops abov_pm_ops = { + .suspend = abov_suspend, + .resume = abov_resume, +}; +#endif + +static struct i2c_device_id abov_idtable[] = { + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, abov_idtable); + +static struct i2c_driver abov_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = abov_match_table, +#if defined(USE_KERNEL_SUSPEND) + .pm = &abov_pm_ops, +#endif + }, + .id_table = abov_idtable, + .probe = abov_probe, + .remove = abov_remove, +}; +static int __init abov_init(void) +{ + return i2c_add_driver(&abov_driver); +} +static void __exit abov_exit(void) +{ + i2c_del_driver(&abov_driver); +} + +module_init(abov_init); +module_exit(abov_exit); + +MODULE_AUTHOR("ABOV Corp."); +MODULE_DESCRIPTION("ABOV Capacitive Touch Controller Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.1"); + +#ifdef USE_THREADED_IRQ +static void abovXX_process_interrupt(pabovXX_t this, u8 nirqlow) +{ + int status = 0; + + if (!this) { + LOG_ERR("abovXX_worker_func, NULL abovXX_t\n"); + return; + } + /* since we are not in an interrupt don't need to disable irq. */ + status = this->refreshStatus(this); + LOG_DBG("Worker - Refresh Status %d\n", status); + this->statusFunc[6](this); + if (unlikely(this->useIrqTimer && nirqlow)) { + /* In case we need to send a timer for example on a touchscreen + * checking penup, perform this here + */ + cancel_delayed_work(&this->dworker); + schedule_delayed_work(&this->dworker, msecs_to_jiffies(this->irqTimeout)); + LOG_DBG("Schedule Irq timer"); + } +} + + +static void abovXX_worker_func(struct work_struct *work) +{ + pabovXX_t this = 0; + + if (work) { + this = container_of(work, abovXX_t, dworker.work); + if (!this) { + LOG_ERR("abovXX_worker_func, NULL abovXX_t\n"); + return; + } + if ((!this->get_nirq_low) || (!this->get_nirq_low(this->board->irq_gpio))) { + /* only run if nirq is high */ + abovXX_process_interrupt(this, 0); + } + } else { + LOG_ERR("abovXX_worker_func, NULL work_struct\n"); + } +} +static irqreturn_t abovXX_interrupt_thread(int irq, void *data) +{ + pabovXX_t this = 0; + this = data; + + mutex_lock(&this->mutex); + LOG_DBG("abovXX_irq\n"); + if ((!this->get_nirq_low) || this->get_nirq_low(this->board->irq_gpio)) + abovXX_process_interrupt(this, 1); + else + LOG_ERR("abovXX_irq - nirq read high\n"); + mutex_unlock(&this->mutex); + return IRQ_HANDLED; +} +#else +static void abovXX_schedule_work(pabovXX_t this, unsigned long delay) +{ + unsigned long flags; + + if (this) { + LOG_DBG("abovXX_schedule_work()\n"); + spin_lock_irqsave(&this->lock, flags); + /* Stop any pending penup queues */ + cancel_delayed_work(&this->dworker); + /* + *after waiting for a delay, this put the job in the kernel-global + workqueue. so no need to create new thread in work queue. + */ + schedule_delayed_work(&this->dworker, delay); + spin_unlock_irqrestore(&this->lock, flags); + } else + LOG_ERR("abovXX_schedule_work, NULL pabovXX_t\n"); +} + +static irqreturn_t abovXX_irq(int irq, void *pvoid) +{ + pabovXX_t this = 0; +return IRQ_HANDLED; + if (pvoid) { + this = (pabovXX_t)pvoid; + LOG_DBG("abovXX_irq\n"); + if ((!this->get_nirq_low) || this->get_nirq_low(this->board->irq_gpio)) { + LOG_DBG("abovXX_irq - Schedule Work\n"); + abovXX_schedule_work(this, 0); + } else + LOG_ERR("abovXX_irq - nirq read high\n"); + } else + LOG_ERR("abovXX_irq, NULL pvoid\n"); + return IRQ_HANDLED; +} + +static void abovXX_worker_func(struct work_struct *work) +{ + pabovXX_t this = 0; + int status = 0; + int counter = 0; + u8 nirqLow = 0; + + if (work) { + this = container_of(work, abovXX_t, dworker.work); + + if (!this) { + LOG_ERR("abovXX_worker_func, NULL abovXX_t\n"); + return; + } + if (unlikely(this->useIrqTimer)) { + if ((!this->get_nirq_low) || this->get_nirq_low(this->board->irq_gpio)) + nirqLow = 1; + } + /* since we are not in an interrupt don't need to disable irq. */ + status = this->refreshStatus(this); + counter = -1; + LOG_DBG("Worker - Refresh Status %d\n", status); + while ((++counter) < MAX_NUM_STATUS_BITS) { /* counter start from MSB */ + LOG_DBG("Looping Counter %d\n", counter); + if (((status >> counter) & 0x01) && (this->statusFunc[counter])) { + LOG_DBG("Function Pointer Found. Calling\n"); + this->statusFunc[counter](this); + } + } + if (unlikely(this->useIrqTimer && nirqLow)) { + /* Early models and if RATE=0 for newer models require a penup timer */ + /* Queue up the function again for checking on penup */ + abovXX_schedule_work(this, msecs_to_jiffies(this->irqTimeout)); + } + } else { + LOG_ERR("abovXX_worker_func, NULL work_struct\n"); + } +} +#endif + +void abovXX_suspend(pabovXX_t this) +{ + if (this) { + LOG_DBG("ABOV suspend: disable irq!\n"); + disable_irq(this->irq); + /* if upper layer don't disable capsensor, */ + /* we should let it enter sleep in suspend. */ + if (mEnabled) + write_register(this, ABOV_CTRL_MODE_RET, 0x02); + } +} +void abovXX_resume(pabovXX_t this) +{ + if (this) { + LOG_DBG("ABOV resume: enable irq!\n"); + /* we should let capsensor enter active in resume*/ + if (mEnabled) + write_register(this, ABOV_CTRL_MODE_RET, 0x00); + enable_irq(this->irq); + } +} + +int abovXX_sar_init(pabovXX_t this) +{ + int err = 0; + + if (this && this->pDevice) { +#ifdef USE_THREADED_IRQ + + /* initialize worker function */ + INIT_DELAYED_WORK(&this->dworker, abovXX_worker_func); + + + /* initialize mutex */ + mutex_init(&this->mutex); + /* initailize interrupt reporting */ + this->irq_disabled = 0; + err = request_threaded_irq(this->irq, NULL, abovXX_interrupt_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, this->pdev->driver->name, + this); +#else + /* initialize spin lock */ + spin_lock_init(&this->lock); + + /* initialize worker function */ + INIT_DELAYED_WORK(&this->dworker, abovXX_worker_func); + + /* initailize interrupt reporting */ + this->irq_disabled = 0; + err = request_irq(this->irq, abovXX_irq, IRQF_TRIGGER_FALLING, + this->pdev->driver->name, this); +#endif + if (err) { + LOG_ERR("irq %d busy?\n", this->irq); + return err; + } +#ifdef USE_THREADED_IRQ + LOG_INFO("registered with threaded irq (%d)\n", this->irq); +#else + LOG_INFO("registered with irq (%d)\n", this->irq); +#endif + /* call init function pointer (this should initialize all registers */ + if (this->init) + return this->init(this); + LOG_ERR("No init function!!!!\n"); + } + return -ENOMEM; +} + +int abovXX_sar_remove(pabovXX_t this) +{ + if (this) { + cancel_delayed_work_sync(&this->dworker); /* Cancel the Worker Func */ + free_irq(this->irq, this); + kfree(this); + return 0; + } + return -ENOMEM; +} diff --git a/drivers/misc/abov_a96t/Android.mk b/drivers/misc/abov_a96t/Android.mk new file mode 100644 index 000000000000..ca0e0c0641f8 --- /dev/null +++ b/drivers/misc/abov_a96t/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := abov_sar_a96t.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/abov_a96t/Kbuild b/drivers/misc/abov_a96t/Kbuild new file mode 100644 index 000000000000..c01e49fd6065 --- /dev/null +++ b/drivers/misc/abov_a96t/Kbuild @@ -0,0 +1,14 @@ +# add -Wall to try to catch everything we can. +KERNEL_DIR_PATH = $(shell find $(TOP)/kernel/ -name "msm-*" | head -1 | grep -i "kernel/msm-*") + +KERNEL_VERSION_NAME = $(shell basename $(KERNEL_DIR_PATH)) +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +ifneq (,$(KERNEL_DIR_PATH)) + EXTRA_CFLAGS += -I$(TOP)/kernel/$(KERNEL_VERSION_NAME)/drivers/base +else + EXTRA_CFLAGS += -I$(TOP)/kernel/drivers/base +endif +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../sensors/Module.symvers +obj-m = abov_sar_a96t.o diff --git a/drivers/misc/abov_a96t/Makefile b/drivers/misc/abov_a96t/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/misc/abov_a96t/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/misc/abov_a96t/abov_sar_a96t.c b/drivers/misc/abov_a96t/abov_sar_a96t.c new file mode 100644 index 000000000000..7fe3f15a5f1a --- /dev/null +++ b/drivers/misc/abov_a96t/abov_sar_a96t.c @@ -0,0 +1,2183 @@ +/* + * file abov_sar.c + * brief abov Driver for two channel SAP using + * + * Driver for the ABOV + * Copyright (c) 2015-2016 ABOV Corp + * + * 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. + */ +#define DRIVER_NAME "abov_sar" +#define USE_SENSORS_CLASS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* main struct, interrupt,init,pointers */ + +#include +#include +#include +#include +#include +#include +#include "base.h" + +#define SLEEP(x) mdelay(x) + +#define C_I2C_FIFO_SIZE 8 + +#define I2C_MASK_FLAG (0x00ff) + +static u8 checksum_h; +static u8 checksum_h_bin; +static u8 checksum_l; +static u8 checksum_l_bin; +static u32 abov_channel_number = 0; + +#define IDLE 0 +#define ACTIVE 1 +#define S_PROX 1 +#define S_BODY 2 + +#define ABOV_DEBUG 1 +#define LOG_TAG "ABOV" + +#if ABOV_DEBUG +#define LOG_INFO(fmt, args...) pr_info(LOG_TAG fmt, ##args) +#else +#define LOG_INFO(fmt, args...) +#endif + +#define LOG_DBG(fmt, args...) pr_info(LOG_TAG fmt, ##args) +#define LOG_ERR(fmt, args...) pr_err(LOG_TAG fmt, ##args) + +static int last_val; +static int mEnabled; +static int programming_done; +pabovXX_t abov_sar_ptr; + +/** + * struct abov + * Specialized struct containing input event data, platform data, and + * last cap state read if needed. + */ +typedef struct abov { + pbuttonInformation_t pbuttonInformation; + pabov_platform_data_t hw; /* specific platform data settings */ +} abov_t, *pabov_t; + +static void ForcetoTouched(pabovXX_t this) +{ + pabov_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + struct input_dev *input_bottom_left = NULL; + struct input_dev *input_bottom_right = NULL; + struct _buttonInfo *pCurrentButton = NULL; + + pDevice = this->pDevice; + if (this && pDevice) { + LOG_INFO("ForcetoTouched()\n"); + + pCurrentButton = pDevice->pbuttonInformation->buttons; + input_top = pDevice->pbuttonInformation->input_top; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) + input_bottom = pDevice->pbuttonInformation->input_bottom; + else { + input_bottom_left = pDevice->pbuttonInformation->input_bottom_left; + input_bottom_right = pDevice->pbuttonInformation->input_bottom_right; + } + pCurrentButton->state = ACTIVE; + last_val = 1; + if (mEnabled) { + input_report_abs(input_top, ABS_DISTANCE, 1); + input_sync(input_top); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_report_abs(input_bottom, ABS_DISTANCE, 1); + input_sync(input_bottom); + } + else { + input_report_abs(input_bottom_left, ABS_DISTANCE, 1); + input_sync(input_bottom_left); + input_report_abs(input_bottom_right, ABS_DISTANCE, 1); + input_sync(input_bottom_right); + } + } + LOG_INFO("Leaving ForcetoTouched()\n"); + } +} + +/** + * fn static int write_register(pabovXX_t this, u8 address, u8 value) + * brief Sends a write register to the device + * param this Pointer to main parent struct + * param address 8-bit register address + * param value 8-bit register value to write to address + * return Value from i2c_master_send + */ +static int write_register(pabovXX_t this, u8 address, u8 value) +{ + struct i2c_client *i2c = 0; + char buffer[2]; + int returnValue = 0; + + buffer[0] = address; + buffer[1] = value; + returnValue = -ENOMEM; + if (this && this->bus) { + i2c = this->bus; + + returnValue = i2c_master_send(i2c, buffer, 2); + LOG_INFO("write_register Addr: 0x%x Val: 0x%x Return: %d\n", + address, value, returnValue); + } + if (returnValue < 0) { + ForcetoTouched(this); + LOG_DBG("Write_register-ForcetoTouched()\n"); + } + return returnValue; +} + +/** + * fn static int read_register(pabovXX_t this, u8 address, u8 *value) + * brief Reads a register's value from the device + * param this Pointer to main parent struct + * param address 8-Bit address to read from + * param value Pointer to 8-bit value to save register value to + * return Value from i2c_smbus_read_byte_data if < 0. else 0 + */ +static int read_register(pabovXX_t this, u8 address, u8 *value) +{ + struct i2c_client *i2c = 0; + s32 returnValue = 0; + + if (this && value && this->bus) { + i2c = this->bus; + returnValue = i2c_smbus_read_byte_data(i2c, address); + LOG_INFO("read_register Addr: 0x%x Return: 0x%x\n", + address, returnValue); + if (returnValue >= 0) { + *value = returnValue; + return 0; + } else { + return returnValue; + } + } + ForcetoTouched(this); + LOG_INFO("read_register-ForcetoTouched()\n"); + return -ENOMEM; +} + + +/** + * brief Perform a manual offset calibration + * param this Pointer to main parent struct + * return Value return value from the write register + */ +static int manual_offset_calibration(pabovXX_t this) +{ + s32 returnValue = 0; + + returnValue = write_register(this, ABOV_RECALI_REG, 0x01); + return returnValue; +} + +/** + * brief sysfs show function for manual calibration which currently just + * returns register value. + */ +static ssize_t manual_offset_calibration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 reg_value = 0; + pabovXX_t this = dev_get_drvdata(dev); + + LOG_INFO("Reading IRQSTAT_REG\n"); + read_register(this, ABOV_IRQSTAT_REG, ®_value); + return scnprintf(buf, PAGE_SIZE, "%d\n", reg_value); +} + +/* brief sysfs store function for manual calibration */ +static ssize_t manual_offset_calibration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = dev_get_drvdata(dev); + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + if (val) { + LOG_INFO("Performing manual_offset_calibration()\n"); + manual_offset_calibration(this); + } + return count; +} + +static DEVICE_ATTR(calibrate, 0644, manual_offset_calibration_show, + manual_offset_calibration_store); +static struct attribute *abov_attributes[] = { + &dev_attr_calibrate.attr, + NULL, +}; +static struct attribute_group abov_attr_group = { + .attrs = abov_attributes, +}; + + +/** + * brief Initialize I2C config from platform data + * param this Pointer to main parent struct + * + */ +static void hw_init(pabovXX_t this) +{ + pabov_t pDevice = 0; + pabov_platform_data_t pdata = 0; + int i = 0; + /* configure device */ + LOG_INFO("Inside hw_init().\n"); + pDevice = this->pDevice; + pdata = pDevice->hw; + if (this && pDevice && pdata) { + while (i < pdata->i2c_reg_num) { + /* Write all registers/values contained in i2c_reg */ + write_register(this, pdata->pi2c_reg[i].reg, + pdata->pi2c_reg[i].val); + i++; + } + } else { + LOG_DBG("ERROR! platform data 0x%p\n", pDevice->hw); + /* Force to touched if error */ + ForcetoTouched(this); + LOG_INFO("Hardware_init-ForcetoTouched()\n"); + } + LOG_INFO("Exiting hw_init().\n"); +} + +/** + * fn static int initialize(pabovXX_t this) + * brief Performs all initialization needed to configure the device + * param this Pointer to main parent struct + * return Last used command's return value (negative if error) + */ +static int initialize(pabovXX_t this) +{ + if (this) { + LOG_INFO("Inside initialize().\n"); + /* prepare reset by disabling any irq handling */ + if(this->irq_disabled == 0) { + disable_irq(this->irq); + this->irq_disabled = 1; + } + hw_init(this); + msleep(300); /* make sure everything is running */ + /* re-enable interrupt handling */ + if(this->irq_disabled == 1) { + enable_irq(this->irq); + this->irq_disabled = 0; + } + /* make sure no interrupts are pending since enabling irq will only + * work on next falling edge */ + LOG_INFO("Exiting initialize().\n"); + programming_done = ACTIVE; + return 0; + } + programming_done = IDLE; + return -ENOMEM; +} + +/** + * brief Handle what to do when a touch occurs + * param this Pointer to main parent struct + */ +static void touchProcess(pabovXX_t this) +{ + int counter = 0; + u8 i = 0; + int numberOfButtons = 0; + pabov_t pDevice = NULL; + struct _buttonInfo *buttons = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + struct input_dev *input_bottom_left = NULL; + struct input_dev *input_bottom_right = NULL; + struct _buttonInfo *pCurrentButton = NULL; + struct abov_platform_data *board; + + pDevice = this->pDevice; + board = this->board; + if (this && pDevice) { + LOG_INFO("Inside touchProcess()\n"); + read_register(this, ABOV_IRQSTAT_REG, &i); + + buttons = pDevice->pbuttonInformation->buttons; + input_top = pDevice->pbuttonInformation->input_top; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) + input_bottom = pDevice->pbuttonInformation->input_bottom; + else { + input_bottom_left = pDevice->pbuttonInformation->input_bottom_left; + input_bottom_right = pDevice->pbuttonInformation->input_bottom_right; + } + numberOfButtons = pDevice->pbuttonInformation->buttonSize; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (unlikely((buttons == NULL) || (input_top == NULL) || (input_bottom == NULL))) { + LOG_DBG("ERROR!! buttons or input NULL!!!\n"); + return; + } + } else { + if (unlikely((buttons == NULL) || (input_top == NULL) || (input_bottom_left == NULL) || (input_bottom_right == NULL))) { + LOG_DBG("ERROR!! buttons or input NULL!!!\n"); + return; + } + } + + for (counter = 0; counter < numberOfButtons; counter++) { + pCurrentButton = &buttons[counter]; + if (pCurrentButton == NULL) { + LOG_DBG("ERR!current button index: %d NULL!\n", + counter); + return; /* ERRORR!!!! */ + } + switch (pCurrentButton->state) { + case IDLE: /* Button is being in far state! */ + if ((i & pCurrentButton->mask) == pCurrentButton->mask) { + LOG_INFO("CS %d State=BODY.\n", + counter); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (board->cap_channel_top == counter) { + input_report_abs(input_top, ABS_DISTANCE, 2); + input_sync(input_top); + } else if (board->cap_channel_bottom == counter) { + input_report_abs(input_bottom, ABS_DISTANCE, 2); + input_sync(input_bottom); + } + } else { + if (board->cap_channel_top == counter) { + input_report_abs(input_top, ABS_DISTANCE, 2); + input_sync(input_top); + } else if (board->cap_channel_bottom_left == counter) { + input_report_abs(input_bottom_left, ABS_DISTANCE, 2); + input_sync(input_bottom_left); + } else if (board->cap_channel_bottom_right == counter) { + input_report_abs(input_bottom_right, ABS_DISTANCE, 2); + input_sync(input_bottom_right); + } + } + pCurrentButton->state = S_BODY; + last_val = 2; + } else if ((i & pCurrentButton->mask) == (pCurrentButton->mask & 0x15)) { + LOG_INFO("CS %d State=PROX.\n", + counter); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (board->cap_channel_top == counter) { + input_report_abs(input_top, ABS_DISTANCE, 1); + input_sync(input_top); + } else if (board->cap_channel_bottom == counter) { + input_report_abs(input_bottom, ABS_DISTANCE, 1); + input_sync(input_bottom); + } + } + else { + if (board->cap_channel_top == counter) { + input_report_abs(input_top, ABS_DISTANCE, 1); + input_sync(input_top); + } else if (board->cap_channel_bottom_left == counter) { + input_report_abs(input_bottom_left, ABS_DISTANCE, 1); + input_sync(input_bottom_left); + } else if (board->cap_channel_bottom_right == counter) { + input_report_abs(input_bottom_right, ABS_DISTANCE, 1); + input_sync(input_bottom_right); + } + } + pCurrentButton->state = S_PROX; + last_val = 0; + } else { + LOG_INFO("CS %d still in IDLE State.\n", + counter); + } + break; + case S_PROX: /* Button is being in proximity! */ + if ((i & pCurrentButton->mask) == pCurrentButton->mask) { + LOG_INFO("CS %d State=BODY.\n", + counter); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (board->cap_channel_top == counter) { + input_report_abs(input_top, ABS_DISTANCE, 2); + input_sync(input_top); + } else if (board->cap_channel_bottom == counter) { + input_report_abs(input_bottom, ABS_DISTANCE, 2); + input_sync(input_bottom); + } + } else { + if (board->cap_channel_top == counter) { + input_report_abs(input_top, ABS_DISTANCE, 2); + input_sync(input_top); + } else if (board->cap_channel_bottom_left == counter) { + input_report_abs(input_bottom_left, ABS_DISTANCE, 2); + input_sync(input_bottom_left); + } else if (board->cap_channel_bottom_right == counter) { + input_report_abs(input_bottom_right, ABS_DISTANCE, 2); + input_sync(input_bottom_right); + } + } + pCurrentButton->state = S_BODY; + last_val = 2; + } else if ((i & pCurrentButton->mask) == (pCurrentButton->mask & 0x15)) { + LOG_INFO("CS %d still in PROX State.\n", + counter); + } else{ + LOG_INFO("CS %d State=IDLE.\n", + counter); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (board->cap_channel_top == counter) { + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + } else if (board->cap_channel_bottom == counter) { + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + } + } else { + if (board->cap_channel_top == counter) { + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + } else if (board->cap_channel_bottom_left == counter) { + input_report_abs(input_bottom_left, ABS_DISTANCE, 0); + input_sync(input_bottom_left); + } else if (board->cap_channel_bottom_right == counter) { + input_report_abs(input_bottom_right, ABS_DISTANCE, 0); + input_sync(input_bottom_right); + } + } + pCurrentButton->state = IDLE; + last_val = 0; + } + break; + case S_BODY: /* Button is being in 0mm! */ + if ((i & pCurrentButton->mask) == pCurrentButton->mask) { + LOG_INFO("CS %d still in BODY State.\n", + counter); + } else if ((i & pCurrentButton->mask) == (pCurrentButton->mask & 0x15)) { + LOG_INFO("CS %d State=PROX.\n", + counter); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (board->cap_channel_top == counter) { + input_report_abs(input_top, ABS_DISTANCE, 1); + input_sync(input_top); + } else if (board->cap_channel_bottom == counter) { + input_report_abs(input_bottom, ABS_DISTANCE, 1); + input_sync(input_bottom); + } + } else { + if (board->cap_channel_top == counter) { + input_report_abs(input_top, ABS_DISTANCE, 1); + input_sync(input_top); + } else if (board->cap_channel_bottom_left == counter) { + input_report_abs(input_bottom_left, ABS_DISTANCE, 1); + input_sync(input_bottom_left); + } else if (board->cap_channel_bottom_right == counter) { + input_report_abs(input_bottom_right, ABS_DISTANCE, 1); + input_sync(input_bottom_right); + } + } + pCurrentButton->state = S_PROX; + last_val = 1; + } else{ + LOG_INFO("CS %d State=IDLE.\n", + counter); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (board->cap_channel_top == counter) { + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + } else if (board->cap_channel_bottom == counter) { + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + } + } else { + if (board->cap_channel_top == counter) { + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + } else if (board->cap_channel_bottom_left == counter) { + input_report_abs(input_bottom_left, ABS_DISTANCE, 0); + input_sync(input_bottom_left); + } else if (board->cap_channel_bottom_right == counter) { + input_report_abs(input_bottom_right, ABS_DISTANCE, 0); + input_sync(input_bottom_right); + } + } + pCurrentButton->state = IDLE; + last_val = 0; + } + break; + default: /* Shouldn't be here, device only allowed ACTIVE or IDLE */ + break; + }; + } + LOG_INFO("Leaving touchProcess()\n"); + } +} + +static int abov_get_nirq_state(unsigned irq_gpio) +{ + if (irq_gpio) { + return !gpio_get_value(irq_gpio); + } else { + LOG_INFO("abov irq_gpio is not set."); + return -EINVAL; + } +} + +static struct _totalButtonInformation smtcButtonInformation = { + .buttons = psmtcButtons, + .buttonSize = ARRAY_SIZE(psmtcButtons), +}; + +/** + *fn static void abov_reg_setup_init(struct i2c_client *client) + *brief read reg val form dts + * reg_array_len for regs needed change num + * data_array_val's format + */ +static void abov_reg_setup_init(struct i2c_client *client) +{ + u32 data_array_len = 0; + u32 *data_array; + int ret, i, j; + struct device_node *np = client->dev.of_node; + + ret = of_property_read_u32(np, "reg_array_len", &data_array_len); + if (ret < 0) { + LOG_DBG("data_array_len read error"); + return; + } + data_array = kmalloc(data_array_len * 2 * sizeof(u32), GFP_KERNEL); + ret = of_property_read_u32_array(np, "reg_array_val", + data_array, + data_array_len*2); + if (ret < 0) { + LOG_DBG("data_array_val read error"); + return; + } + for (i = 0; i < ARRAY_SIZE(abov_i2c_reg_setup); i++) { + for (j = 0; j < data_array_len*2; j += 2) { + if (data_array[j] == abov_i2c_reg_setup[i].reg) { + abov_i2c_reg_setup[i].val = data_array[j+1]; + LOG_DBG("read dtsi 0x%02x:0x%02x set reg\n", + data_array[j], data_array[j+1]); + } + } + } + kfree(data_array); +} + +static void abov_platform_data_of_init(struct i2c_client *client, + pabov_platform_data_t pplatData) +{ + struct device_node *np = client->dev.of_node; + u32 cap_channel_top; + u32 cap_channel_bottom; + u32 cap_channel_bottom_left, cap_channel_bottom_right; + int ret; + + client->irq = of_get_gpio(np, 0); + pplatData->irq_gpio = client->irq; + + ret = of_property_read_u32(np, "cap,channel_number", &abov_channel_number); + if (ret < 0) { + LOG_DBG("get cap,channel number node error!\n"); + } + ret = of_property_read_u32(np, "cap,use_channel_top", &cap_channel_top); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) + ret = of_property_read_u32(np, "cap,use_channel_bottom", &cap_channel_bottom); + else { + ret = of_property_read_u32(np, "cap,use_channel_bottom_left", &cap_channel_bottom_left); + ret = of_property_read_u32(np, "cap,use_channel_bottom_right", &cap_channel_bottom_right); + } + pplatData->cap_channel_top = (int)cap_channel_top; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) + pplatData->cap_channel_bottom = (int)cap_channel_bottom; + else { + pplatData->cap_channel_bottom_left = (int)cap_channel_bottom_left; + pplatData->cap_channel_bottom_right = (int)cap_channel_bottom_right; + } + + pplatData->get_is_nirq_low = abov_get_nirq_state; + pplatData->init_platform_hw = NULL; + /* pointer to an exit function. Here in case needed in the future */ + /* + *.exit_platform_hw = abov_exit_ts, + */ + pplatData->exit_platform_hw = NULL; + abov_reg_setup_init(client); + pplatData->pi2c_reg = abov_i2c_reg_setup; + pplatData->i2c_reg_num = ARRAY_SIZE(abov_i2c_reg_setup); + + pplatData->pbuttonInformation = &smtcButtonInformation; + + ret = of_property_read_string(np, "label", &pplatData->fw_name); + if (ret < 0) { + LOG_DBG("firmware name read error!\n"); + return; + } +} + +static ssize_t reset_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 8, "%d\n", programming_done); +} + +static ssize_t reset_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + pabov_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + struct input_dev *input_bottom_left = NULL; + struct input_dev *input_bottom_right = NULL; + + pDevice = this->pDevice; + input_top = pDevice->pbuttonInformation->input_top; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) + input_bottom = pDevice->pbuttonInformation->input_bottom; + else { + input_bottom_left = pDevice->pbuttonInformation->input_bottom_left; + input_bottom_right = pDevice->pbuttonInformation->input_bottom_right; + } + + if (!count || (this == NULL)) + return -EINVAL; + + if (!strncmp(buf, "reset", 5) || !strncmp(buf, "1", 1)) + write_register(this, ABOV_SOFTRESET_REG, 0x10); + + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + } else { + input_report_abs(input_bottom_left, ABS_DISTANCE, 0); + input_sync(input_bottom_left); + input_report_abs(input_bottom_right, ABS_DISTANCE, 0); + input_sync(input_bottom_right); + } + + return count; +} + +static CLASS_ATTR_RW(reset); + +static ssize_t enable_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 8, "%d\n", mEnabled); +} + +static ssize_t enable_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + pabov_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + struct input_dev *input_bottom_left = NULL; + struct input_dev *input_bottom_right = NULL; + + pDevice = this->pDevice; + input_top = pDevice->pbuttonInformation->input_top; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) + input_bottom = pDevice->pbuttonInformation->input_bottom; + else { + input_bottom_left = pDevice->pbuttonInformation->input_bottom_left; + input_bottom_right = pDevice->pbuttonInformation->input_bottom_right; + } + + if (!count || (this == NULL)) + return -EINVAL; + + if (!strncmp(buf, "1", 1)) { + LOG_DBG("enable cap sensor\n"); + initialize(this); + + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + } else { + input_report_abs(input_bottom_left, ABS_DISTANCE, 0); + input_sync(input_bottom_left); + input_report_abs(input_bottom_right, ABS_DISTANCE, 0); + input_sync(input_bottom_right); + } + mEnabled = 1; + } else if (!strncmp(buf, "0", 1)) { + LOG_DBG("disable cap sensor\n"); + + write_register(this, ABOV_CTRL_MODE_REG, 0x02); + + input_report_abs(input_top, ABS_DISTANCE, -1); + input_sync(input_top); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_report_abs(input_bottom, ABS_DISTANCE, -1); + input_sync(input_bottom); + } else { + input_report_abs(input_bottom_left, ABS_DISTANCE, -1); + input_sync(input_bottom_left); + input_report_abs(input_bottom_right, ABS_DISTANCE, -1); + input_sync(input_bottom_right); + } + mEnabled = 0; + } else { + LOG_DBG("unknown enable symbol\n"); + } + + return count; +} + +#ifdef USE_SENSORS_CLASS +static int capsensor_set_enable(struct sensors_classdev *sensors_cdev, unsigned int enable) +{ + pabovXX_t this = abov_sar_ptr; + pabov_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + struct input_dev *input_bottom_left = NULL; + struct input_dev *input_bottom_right = NULL; + + pDevice = this->pDevice; + input_top = pDevice->pbuttonInformation->input_top; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) + input_bottom = pDevice->pbuttonInformation->input_bottom; + else { + input_bottom_left = pDevice->pbuttonInformation->input_bottom_left; + input_bottom_right = pDevice->pbuttonInformation->input_bottom_right; + } + + if (enable == 1) { + LOG_DBG("enable cap sensor: %s\n",sensors_cdev->name); + if(mEnabled == 0){ + initialize(this); + mEnabled = 1; + } + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if(!strcmp(sensors_cdev->name, "capsense_top")){ + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_TOP; + }else if(!strcmp(sensors_cdev->name, "capsense_bottom")){ + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_BOTTOM; + } + } else { + if(!strcmp(sensors_cdev->name, "capsense_top")){ + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_TOP; + }else if(!strcmp(sensors_cdev->name, "capsense_bottom_left")){ + input_report_abs(input_bottom_left, ABS_DISTANCE, 0); + input_sync(input_bottom_left); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_BOTTOM_LEFT; + }else if(!strcmp(sensors_cdev->name, "capsense_bottom_right")){ + input_report_abs(input_bottom_right, ABS_DISTANCE, 0); + input_sync(input_bottom_right); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_BOTTOM_RIGHT; + } + } + } else if (enable == 0) { + LOG_DBG("disable cap sensor: %s\n",sensors_cdev->name); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if(!strcmp(sensors_cdev->name, "capsense_top")){ + input_report_abs(input_top, ABS_DISTANCE, -1); + input_sync(input_top); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_TOP; + }else if(!strcmp(sensors_cdev->name, "capsense_bottom")){ + input_report_abs(input_bottom, ABS_DISTANCE, -1); + input_sync(input_bottom); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_BOTTOM; + } + } else { + if(!strcmp(sensors_cdev->name, "capsense_top")){ + input_report_abs(input_top, ABS_DISTANCE, -1); + input_sync(input_top); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_TOP; + }else if(!strcmp(sensors_cdev->name, "capsense_bottom_left")){ + input_report_abs(input_bottom_left, ABS_DISTANCE, -1); + input_sync(input_bottom_left); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_BOTTOM_LEFT; + }else if(!strcmp(sensors_cdev->name, "capsense_bottom_right")){ + input_report_abs(input_bottom_right, ABS_DISTANCE, -1); + input_sync(input_bottom_right); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_BOTTOM_RIGHT; + } + } + if(!(this->enable_flag &0x07)){ + mEnabled = 0; + write_register(this, ABOV_CTRL_MODE_REG, 0x02); + LOG_DBG("all cap sensor disable,change to STOP mode.\n"); + } + } else { + LOG_DBG("unknown enable symbol\n"); + } + + return 0; +} +#endif + +static CLASS_ATTR_RW(enable); + +static ssize_t reg_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u8 reg_value = 0, i; + pabovXX_t this = abov_sar_ptr; + char *p = buf; + + if (this->read_flag) { + this->read_flag = 0; + read_register(this, this->read_reg, ®_value); + p += snprintf(p, PAGE_SIZE, "(0x%02x)=0x%02x\n", this->read_reg, reg_value); + return (p-buf); + } + + for (i = 0; i < 0x40; i++) { + read_register(this, i, ®_value); + p += snprintf(p, PAGE_SIZE, "(0x%02x)=0x%02x\n", + i, reg_value); + } + + for (i = 0x80; i < 0x8C; i++) { + read_register(this, i, ®_value); + p += snprintf(p, PAGE_SIZE, "(0x%02x)=0x%02x\n", + i, reg_value); + } + + return (p-buf); +} + +static ssize_t reg_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + unsigned int val, reg, opt; + if (sscanf(buf, "%x,%x,%x", ®, &val, &opt) == 3) { + LOG_INFO("%s, read reg = 0x%02x\n", __func__, *(u8 *)®); + this->read_reg = *((u8 *)®); + this->read_flag = 1; + } else if (sscanf(buf, "%x,%x", ®, &val) == 2) { + LOG_INFO("%s,reg = 0x%02x, val = 0x%02x\n", + __func__, *(u8 *)®, *(u8 *)&val); + write_register(this, *((u8 *)®), *((u8 *)&val)); + } + + return count; +} + + +static CLASS_ATTR_RW(reg); + +static struct class capsense_class = { + .name = "capsense", + .owner = THIS_MODULE, +}; + +static void ps_notify_callback_work(struct work_struct *work) +{ + pabovXX_t this = container_of(work, abovXX_t, ps_notify_work); + pabov_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + struct input_dev *input_bottom_left = NULL; + struct input_dev *input_bottom_right = NULL; + int ret = 0; + + pDevice = this->pDevice; + input_top = pDevice->pbuttonInformation->input_top; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) + input_bottom = pDevice->pbuttonInformation->input_bottom; + else { + input_bottom_left = pDevice->pbuttonInformation->input_bottom_left; + input_bottom_right = pDevice->pbuttonInformation->input_bottom_right; + } + + LOG_INFO("Usb insert,going to force calibrate\n"); + ret = write_register(this, ABOV_RECALI_REG, 0x01); + if (ret < 0) + LOG_DBG(" Usb insert,calibrate cap sensor failed\n"); + + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + } else { + input_report_abs(input_bottom_left, ABS_DISTANCE, 0); + input_sync(input_bottom_left); + input_report_abs(input_bottom_right, ABS_DISTANCE, 0); + input_sync(input_bottom_right); + } +} + +static int ps_get_state(struct power_supply *psy, bool *present) +{ + union power_supply_propval pval = { 0 }; + int retval; + + retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, + &pval); + if (retval) { + LOG_DBG("%s psy get property failed\n", psy->desc->name); + return retval; + } + *present = (pval.intval) ? true : false; + LOG_INFO("%s is %s\n", psy->desc->name, + (*present) ? "present" : "not present"); + return 0; +} + +static int ps_notify_callback(struct notifier_block *self, + unsigned long event, void *p) +{ + pabovXX_t this = container_of(self, abovXX_t, ps_notif); + struct power_supply *psy = p; + bool present; + int retval; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0) + if (event == PSY_EVENT_PROP_CHANGED +#else + if ((event == PSY_EVENT_PROP_ADDED || event == PSY_EVENT_PROP_CHANGED) +#endif + && psy && psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, "usb", sizeof("usb"))){ + LOG_INFO("ps notification: event = %lu\n", event); + retval = ps_get_state(psy, &present); + if (retval) { + LOG_DBG("psy get property failed\n"); + return retval; + } + + if (event == PSY_EVENT_PROP_CHANGED) { + if (this->ps_is_present == present) { + LOG_INFO("ps present state not change\n"); + return 0; + } + } + this->ps_is_present = present; + schedule_work(&this->ps_notify_work); + } + + return 0; +} + + +static int _i2c_adapter_block_write(struct i2c_client *client, u8 *data, u8 len) +{ + u8 buffer[C_I2C_FIFO_SIZE]; + u8 left = len; + u8 offset = 0; + u8 retry = 0; + + struct i2c_msg msg = { + .addr = client->addr & I2C_MASK_FLAG, + .flags = 0, + .buf = buffer, + }; + + if (data == NULL || len < 1) { + LOG_ERR("Invalid : data is null or len=%d\n", len); + return -EINVAL; + } + + while (left > 0) { + retry = 0; + if (left >= C_I2C_FIFO_SIZE) { + msg.buf = &data[offset]; + msg.len = C_I2C_FIFO_SIZE; + left -= C_I2C_FIFO_SIZE; + offset += C_I2C_FIFO_SIZE; + } else { + msg.buf = &data[offset]; + msg.len = left; + left = 0; + } + + while (i2c_transfer(client->adapter, &msg, 1) != 1) { + retry++; + if (retry > 10) { + LOG_ERR("OUT : fail - addr:%#x len:%d \n", client->addr, msg.len); + return -EIO; + } + } + } + return 0; +} + +static int i2c_adapter_block_write_nodatalog(struct i2c_client *client, u8 *data, u8 len) +{ + return _i2c_adapter_block_write(client, data, len); +} + +static int abov_tk_check_busy(struct i2c_client *client) +{ + int ret, count = 0; + unsigned char val = 0x00; + + do { + ret = i2c_master_recv(client, &val, sizeof(val)); + if (val & 0x01) { + count++; + if (count > 1000) { + LOG_INFO("%s: val = 0x%x\r\n", __func__, val); + return ret; + } + } else { + break; + } + } while (1); + + return ret; +} + +static int abov_tk_fw_write(struct i2c_client *client, unsigned char *addrH, + unsigned char *addrL, unsigned char *val) +{ + int length = 36, ret = 0; + unsigned char buf[40] = {0, }; + + buf[0] = 0xAC; + buf[1] = 0x7A; + memcpy(&buf[2], addrH, 1); + memcpy(&buf[3], addrL, 1); + memcpy(&buf[4], val, 32); + ret = i2c_adapter_block_write_nodatalog(client, buf, length); + if (ret < 0) { + LOG_ERR("Firmware write fail ...\n"); + return ret; + } + + SLEEP(3); + abov_tk_check_busy(client); + + return 0; +} + +static int abov_tk_reset_for_bootmode(struct i2c_client *client) +{ + int ret, retry_count = 10; + unsigned char buf[16] = {0, }; + +retry: + buf[0] = 0xF0; + buf[1] = 0xAA; + ret = i2c_master_send(client, buf, 2); + if (ret < 0) { + LOG_INFO("write fail(retry:%d)\n", retry_count); + if (retry_count-- > 0) { + goto retry; + } + return -EIO; + } else { + LOG_INFO("success reset & boot mode\n"); + return 0; + } +} + +static int abov_tk_fw_mode_enter(struct i2c_client *client) +{ + int ret = 0; + unsigned char buf[40] = {0, }; + + buf[0] = 0xAC; + buf[1] = 0x5B; + ret = i2c_master_send(client, buf, 2); + if (ret != 2) { + LOG_ERR("SEND : fail - addr:0x%02x data:0x%02x 0x%02x... ret:%d\n", client->addr, buf[0], buf[1], ret); + return -EIO; + } + LOG_INFO("SEND : succ - addr:0x%02x data:0x%02x 0x%02x... ret:%d\n", client->addr, buf[0], buf[1], ret); + SLEEP(5); + + ret = i2c_master_recv(client, buf, 1); + if (buf[0] != 0x39) { + LOG_ERR("Enter fw mode fail ...device id:0x%02x\n", buf[0]); + return -EIO; + } + + LOG_INFO("Enter fw mode success ... device id:0x%02x\n", buf[0]); + + return 0; +} + +static int abov_tk_fw_mode_exit(struct i2c_client *client) +{ + int ret = 0; + unsigned char buf[40] = {0, }; + + buf[0] = 0xAC; + buf[1] = 0xE1; + ret = i2c_master_send(client, buf, 2); + if (ret != 2) { + LOG_ERR("SEND : fail - addr:0x%02x data:0x%02x 0x%02x ... ret:%d\n", client->addr, buf[0], buf[1], ret); + return -EIO; + } + LOG_INFO("SEND : succ - addr:0x%02x data:0x%02x 0x%02x ... ret:%d\n", client->addr, buf[0], buf[1], ret); + + return 0; +} + +static int abov_tk_flash_erase(struct i2c_client *client) +{ + int ret = 0; + unsigned char buf[16] = {0, }; + + buf[0] = 0xAC; + buf[1] = 0x2E; + + ret = i2c_master_send(client, buf, 2); + if (ret != 2) { + LOG_ERR("SEND : fail - addr:0x%02x data:0x%02x 0x%02x ... ret:%d\n", client->addr, buf[0], buf[1], ret); + return -EIO; + } + + LOG_INFO("SEND : succ - addr:0x%02x data:0x%02x 0x%02x ... ret:%d\n", client->addr, buf[0], buf[1], ret); + + return 0; +} + +static int abov_tk_i2c_read_checksum(struct i2c_client *client) +{ + unsigned char checksum[6] = {0, }; + unsigned char buf[16] = {0, }; + int ret; + + checksum_h = 0; + checksum_l = 0; + + buf[0] = 0xAC; + buf[1] = 0x9E; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = checksum_h_bin; + buf[5] = checksum_l_bin; + ret = i2c_master_send(client, buf, 6); + + if (ret != 6) { + LOG_ERR("SEND : fail - addr:0x%02x len:%d ... ret:%d\n", client->addr, 6, ret); + return -EIO; + } + SLEEP(5); + + buf[0] = 0x00; + ret = i2c_master_send(client, buf, 1); + if (ret != 1) { + LOG_ERR("SEND : fail - addr:0x%02x data:0x%02x ... ret:%d\n", client->addr, buf[0], ret); + return -EIO; + } + SLEEP(5); + + ret = i2c_master_recv(client, checksum, 6); + if (ret < 0) { + LOG_ERR("Read checksum fail ... \n"); + return -EIO; + } + + checksum_h = checksum[4]; + checksum_l = checksum[5]; + + return 0; +} + +static int _abov_fw_update(struct i2c_client *client, const u8 *image, u32 size) +{ + int ret, ii = 0; + int count; + unsigned short address; + unsigned char addrH, addrL; + unsigned char data[32] = {0, }; + + LOG_INFO("%s: call in\r\n", __func__); + + if (abov_tk_reset_for_bootmode(client) < 0) { + LOG_ERR("don't reset(enter boot mode)!"); + return -EIO; + } + + SLEEP(45); + + for (ii = 0; ii < 10; ii++) { + if (abov_tk_fw_mode_enter(client) < 0) { + LOG_ERR("don't enter the download mode! %d", ii); + SLEEP(40); + continue; + } + break; + } + + if (10 <= ii) { + return -EAGAIN; + } + + if (abov_tk_flash_erase(client) < 0) { + LOG_ERR("don't erase flash data!"); + return -EIO; + } + + SLEEP(1400); + + address = 0x800; + count = size / 32; + + for (ii = 0; ii < count; ii++) { + /* first 32byte is header */ + addrH = (unsigned char)((address >> 8) & 0xFF); + addrL = (unsigned char)(address & 0xFF); + memcpy(data, &image[ii * 32], 32); + ret = abov_tk_fw_write(client, &addrH, &addrL, data); + if (ret < 0) { + LOG_INFO("fw_write.. ii = 0x%x err\r\n", ii); + return ret; + } + + address += 0x20; + memset(data, 0, 32); + } + + ret = abov_tk_i2c_read_checksum(client); + ret = abov_tk_fw_mode_exit(client); + if ((checksum_h == checksum_h_bin) && (checksum_l == checksum_l_bin)) { + LOG_INFO("Firmware update success. checksum_h=0x%02x,checksum_h_bin=0x%02x,checksum_l=0x%02x,checksum_l_bin=0x%02x\n", + checksum_h, checksum_h_bin, checksum_l, checksum_l_bin); + } else { + LOG_INFO("Firmware update fail. checksum_h=0x%02x,checksum_h_bin=0x%02x,checksum_l=0x%02x,checksum_l_bin=0x%02x\n", + checksum_h, checksum_h_bin, checksum_l, checksum_l_bin); + ret = -1; + } + SLEEP(100); + + return ret; +} + +static int abov_fw_update(bool force) +{ + int update_loop; + pabovXX_t this = abov_sar_ptr; + struct i2c_client *client = this->bus; + int rc; + bool fw_upgrade = false; + u8 fw_version = 0, fw_file_version = 0; + u8 fw_modelno = 0, fw_file_modeno = 0; + const struct firmware *fw = NULL; + char fw_name[32] = {0}; + + strcpy(fw_name, this->board->fw_name); + strlcat(fw_name, ".BIN", NAME_MAX); + rc = request_firmware(&fw, fw_name, this->pdev); + if (rc < 0) { + read_register(this, ABOV_VERSION_REG, &fw_version); + read_register(this, ABOV_MODELNO_REG, &fw_modelno); + LOG_INFO("Request firmware failed - %s (%d),current fw info inside IC:Version=0x%02x,ModelNo=0x%02x\n", + this->board->fw_name, rc, fw_version, fw_modelno); + return rc; + } + + if (force == false) { + read_register(this, ABOV_VERSION_REG, &fw_version); + read_register(this, ABOV_MODELNO_REG, &fw_modelno); + LOG_INFO("Version in sensor is:0x%02x ,ModelNo in sensor is:0x%02x\n" ,fw_version ,fw_modelno); + } + + fw_file_modeno = fw->data[1]; + fw_file_version = fw->data[5]; + checksum_h_bin = fw->data[8]; + checksum_l_bin = fw->data[9]; + LOG_INFO("Version in file is:0x%02x ,ModelNo in file is:0x%02x\n" ,fw_file_version ,fw_file_version); + + if ((force) || (fw_version < fw_file_version) || (fw_modelno != fw_file_modeno)) { + LOG_INFO("Firmware is not latest,going to fw upgrade...\n"); + fw_upgrade = true; + } else { + LOG_INFO("Firmware is latest,exiting fw upgrade...\n"); + fw_upgrade = false; + rc = -EIO; + goto rel_fw; + } + + if (fw_upgrade) { + for (update_loop = 0; update_loop < 10; update_loop++) { + rc = _abov_fw_update(client, &fw->data[32], fw->size-32); + if (rc < 0) + LOG_INFO("retry : %d times!\n", update_loop); + else { + //this funtion under the mutex lock, and the initialize will enable irq, + //if trigger the interrupts, the cap-sensor will no work, so, initialize() no stay here. + //initialize(this); + break; + } + } + if (update_loop >= 10) + rc = -EIO; + } + +rel_fw: + release_firmware(fw); + return rc; +} + + + +static ssize_t update_fw_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u8 fw_version = 0; + pabovXX_t this = abov_sar_ptr; + + read_register(this, ABOV_VERSION_REG, &fw_version); + + return snprintf(buf, 37, "ABOV CapSensor Firmware Version:0x%02x\n", fw_version); +} + +static ssize_t update_fw_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + unsigned long val; + int rc; + + if (count > 2) + return -EINVAL; + + rc = kstrtoul(buf, 10, &val); + if (rc != 0) + return rc; + + if(this->irq_disabled == 0) { + disable_irq(this->irq); + this->irq_disabled = 1; + } + mutex_lock(&this->mutex); + if (!this->loading_fw && val) { + this->loading_fw = true; + abov_fw_update(false); + this->loading_fw = false; + } + mutex_unlock(&this->mutex); + + if(this->irq_disabled == 1) { + enable_irq(this->irq); + this->irq_disabled = 0; + } + + return count; +} +static CLASS_ATTR_RW(update_fw); + + +static ssize_t force_update_fw_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u8 fw_version = 0; + pabovXX_t this = abov_sar_ptr; + + read_register(this, ABOV_VERSION_REG, &fw_version); + + return snprintf(buf, 37, "ABOV CapSensor Firmware Version:0x%02x\n", fw_version); +} + +static ssize_t force_update_fw_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + unsigned long val; + int rc; + + if (count > 2) + return -EINVAL; + + rc = kstrtoul(buf, 10, &val); + if (rc != 0) + return rc; + + if(this->irq_disabled == 0) { + disable_irq(this->irq); + this->irq_disabled = 1; + } + + mutex_lock(&this->mutex); + if (!this->loading_fw && val) { + this->loading_fw = true; + abov_fw_update(true); + this->loading_fw = false; + } + mutex_unlock(&this->mutex); + + if(this->irq_disabled == 1) { + enable_irq(this->irq); + this->irq_disabled = 0; + } + + return count; +} +static CLASS_ATTR_RW(force_update_fw); + +static void capsense_update_work(struct work_struct *work) +{ + pabovXX_t this = container_of(work, abovXX_t, fw_update_work); + + LOG_INFO("%s: start update firmware\n", __func__); + if(this->irq_disabled == 0) { + disable_irq(this->irq); + this->irq_disabled = 1; + } + mutex_lock(&this->mutex); + this->loading_fw = true; + abov_fw_update(false); + this->loading_fw = false; + mutex_unlock(&this->mutex); + if(this->irq_disabled == 1) { + enable_irq(this->irq); + this->irq_disabled = 0; + } + + if(mEnabled){ + initialize(this); + } + LOG_INFO("%s: update firmware end\n", __func__); +} + +static void capsense_fore_update_work(struct work_struct *work) +{ + pabovXX_t this = container_of(work, abovXX_t, fw_update_work); + + LOG_INFO("%s: start force update firmware\n", __func__); + if(this->irq_disabled == 0) { + disable_irq(this->irq); + this->irq_disabled = 1; + } + mutex_lock(&this->mutex); + this->loading_fw = true; + abov_fw_update(true); + this->loading_fw = false; + mutex_unlock(&this->mutex); + if(this->irq_disabled == 1) { + enable_irq(this->irq); + this->irq_disabled = 0; + } + + if(mEnabled){ + initialize(this); + } + LOG_INFO("%s: force update firmware end\n", __func__); +} + + +/** + * detect if abov exist or not + * return 1 if chip exist else return 0 + */ +static int abov_detect(struct i2c_client *client) +{ + s32 returnValue = 0, i; + u8 address = ABOV_VENDOR_ID_REG; + u8 value = 0xAB; + + if (client) { + for (i = 0; i < 10; i++) { + returnValue = i2c_smbus_read_byte_data(client, address); + LOG_INFO("abov read_register for %d time Addr: 0x%02x Return: 0x%02x\n", + i, address, returnValue); + if (returnValue >= 0) { + if (value == returnValue) { + LOG_INFO("abov detect success!\n"); + return 1; + } + } + msleep(10); + } + LOG_INFO("abov boot detect start\n"); + for (i = 0; i < 3; i++) { + if(abov_tk_fw_mode_enter(client) == 0) { + LOG_INFO("abov boot detect success!\n"); + return 2; + } + } + } + LOG_INFO("abov detect failed!!!\n"); + return 0; +} + + +/** + * fn static int abov_probe(struct i2c_client *client, const struct i2c_device_id *id) + * brief Probe function + * param client pointer to i2c_client + * param id pointer to i2c_device_id + * return Whether probe was successful + */ +static int abov_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + pabovXX_t this = 0; + pabov_t pDevice = 0; + pabov_platform_data_t pplatData = 0; + bool isForceUpdate = false; + int ret; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + struct input_dev *input_bottom_left = NULL; + struct input_dev *input_bottom_right = NULL; + struct power_supply *psy = NULL; + + LOG_INFO("abov_probe() start\n"); + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -EIO; + + pplatData = kzalloc(sizeof(struct abov_platform_data), GFP_KERNEL); + if (!pplatData) { + LOG_DBG("platform data is required!\n"); + return -EINVAL; + } + abov_platform_data_of_init(client, pplatData); + client->dev.platform_data = pplatData; + + pplatData->cap_vdd = regulator_get(&client->dev, "cap_vdd"); + if (IS_ERR(pplatData->cap_vdd)) { + if (PTR_ERR(pplatData->cap_vdd) == -EPROBE_DEFER) { + ret = PTR_ERR(pplatData->cap_vdd); + goto err_vdd_defer; + } + LOG_ERR("%s: Failed to get regulator\n", __func__); + } else { + ret = regulator_enable(pplatData->cap_vdd); + + if (ret) { + regulator_put(pplatData->cap_vdd); + LOG_ERR("%s: Error %d enable regulator\n", + __func__, ret); + goto err_vdd_defer; + } + pplatData->cap_vdd_en = true; + LOG_INFO("cap_vdd regulator is %s\n", + regulator_is_enabled(pplatData->cap_vdd) ? + "on" : "off"); + } + + pplatData->cap_svdd = regulator_get(&client->dev, "cap_svdd"); + if (IS_ERR(pplatData->cap_svdd)) { + if (PTR_ERR(pplatData->cap_svdd) == -EPROBE_DEFER) { + ret = PTR_ERR(pplatData->cap_svdd); + goto err_svdd_error; + } + LOG_ERR("%s: Failed to get regulator\n", __func__); + } else { + ret = regulator_enable(pplatData->cap_svdd); + + if (ret) { + regulator_put(pplatData->cap_svdd); + LOG_ERR("%s: Error %d enable regulator\n", + __func__, ret); + goto err_svdd_error; + } + pplatData->cap_svdd_en = true; + LOG_INFO("cap_svdd regulator is %s\n", + regulator_is_enabled(pplatData->cap_svdd) ? + "on" : "off"); + } + + /* detect if abov exist or not */ + ret = abov_detect(client); + if (ret == 0) { + return -ENODEV; + } + if (ret == 2) { + isForceUpdate = true; + } + + this = kzalloc(sizeof(abovXX_t), GFP_KERNEL); /* create memory for main struct */ + LOG_INFO("\t Initialized Main Memory: 0x%p\n", this); + + if (this) { + /* In case we need to reinitialize data + * (e.q. if suspend reset device) */ + this->init = initialize; + /* pointer to function from platform data to get pendown + * (1->NIRQ=0, 0->NIRQ=1) */ + this->get_nirq_low = pplatData->get_is_nirq_low; + /* save irq in case we need to reference it */ + this->irq = gpio_to_irq(client->irq); + /* do we need to create an irq timer after interrupt ? */ + this->useIrqTimer = 0; + this->board = pplatData; + /* Setup function to call on corresponding reg irq source bit */ + if (MAX_NUM_STATUS_BITS >= 8) { + this->statusFunc[0] = 0; /* TXEN_STAT */ + this->statusFunc[1] = 0; /* UNUSED */ + this->statusFunc[2] = 0; /* UNUSED */ + this->statusFunc[3] = 0;/*read_rawData; CONV_STAT */ + this->statusFunc[4] = 0; /* COMP_STAT */ + this->statusFunc[5] = touchProcess; /* RELEASE_STAT */ + this->statusFunc[6] = touchProcess; /* TOUCH_STAT */ + this->statusFunc[7] = 0; /* RESET_STAT */ + } + + /* setup i2c communication */ + this->bus = client; + i2c_set_clientdata(client, this); + + /* record device struct */ + this->pdev = &client->dev; + + /* create memory for device specific struct */ + this->pDevice = pDevice = kzalloc(sizeof(abov_t), GFP_KERNEL); + LOG_INFO("\t Initialized Device Specific Memory: 0x%p\n", + pDevice); + abov_sar_ptr = this; + if (pDevice) { + /* for accessing items in user data (e.g. calibrate) */ + ret = sysfs_create_group(&client->dev.kobj, &abov_attr_group); + + + /* Check if we hava a platform initialization function to call*/ + if (pplatData->init_platform_hw) + pplatData->init_platform_hw(); + + /* Add Pointer to main platform data struct */ + pDevice->hw = pplatData; + + /* Initialize the button information initialized with keycodes */ + pDevice->pbuttonInformation = pplatData->pbuttonInformation; + + /* Create the input device */ + input_top = input_allocate_device(); + if (!input_top) + return -ENOMEM; + + /* Set all the keycodes */ + __set_bit(EV_ABS, input_top->evbit); + input_set_abs_params(input_top, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_top = input_top; + input_top->name = "ABOV Cap Touch top"; + if (input_register_device(input_top)) { + LOG_INFO("add top cap touch unsuccess\n"); + return -ENOMEM; + } + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + /* Create the input device */ + input_bottom = input_allocate_device(); + if (!input_bottom) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_bottom->evbit); + input_set_abs_params(input_bottom, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_bottom = input_bottom; + /* save the input pointer and finish initialization */ + input_bottom->name = "ABOV Cap Touch bottom"; + if (input_register_device(input_bottom)) { + LOG_INFO("add bottom cap touch unsuccess\n"); + return -ENOMEM; + } + } else { + /* Create the input device */ + input_bottom_left = input_allocate_device(); + if (!input_bottom_left) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_bottom_left->evbit); + input_set_abs_params(input_bottom_left, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_bottom_left = input_bottom_left; + /* save the input pointer and finish initialization */ + input_bottom_left->name = "ABOV Cap Touch bottom_left"; + if (input_register_device(input_bottom_left)) { + LOG_INFO("add bottom_left cap touch unsuccess\n"); + return -ENOMEM; + } + /* Create the input device */ + input_bottom_right = input_allocate_device(); + if (!input_bottom_right) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_bottom_right->evbit); + input_set_abs_params(input_bottom_right, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_bottom_right = input_bottom_right; + /* save the input pointer and finish initialization */ + input_bottom_right->name = "ABOV Cap Touch bottom_right"; + if (input_register_device(input_bottom_right)) { + LOG_INFO("add bottom_right cap touch unsuccess\n"); + return -ENOMEM; + } + } + } + + ret = class_register(&capsense_class); + if (ret < 0) { + LOG_DBG("Create fsys class failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_reset); + if (ret < 0) { + LOG_DBG("Create reset file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_enable); + if (ret < 0) { + LOG_DBG("Create enable file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_reg); + if (ret < 0) { + LOG_DBG("Create reg file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_update_fw); + if (ret < 0) { + LOG_DBG("Create update_fw file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_force_update_fw); + if (ret < 0) { + LOG_DBG("Create update_fw file failed (%d)\n", ret); + return ret; + } + /*restore sys/class/capsense label*/ + kobject_uevent(&capsense_class.p->subsys.kobj, KOBJ_CHANGE); + +#ifdef USE_SENSORS_CLASS + sensors_capsensor_top_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_top_cdev.sensors_poll_delay = NULL; + ret = sensors_classdev_register(&input_top->dev, &sensors_capsensor_top_cdev); + if (ret < 0) + LOG_DBG("create top cap sensor_class file failed (%d)\n", ret); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + sensors_capsensor_bottom_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_bottom_cdev.sensors_poll_delay = NULL; + ret = sensors_classdev_register(&input_bottom->dev, &sensors_capsensor_bottom_cdev); + if (ret < 0) + LOG_DBG("create bottom cap sensor_class file failed (%d)\n", ret); + } else { + sensors_capsensor_bottom_left_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_bottom_left_cdev.sensors_poll_delay = NULL; + ret = sensors_classdev_register(&input_bottom_left->dev, &sensors_capsensor_bottom_left_cdev); + if (ret < 0) + LOG_DBG("create bottom_left cap sensor_class file failed (%d)\n", ret); + sensors_capsensor_bottom_right_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_bottom_right_cdev.sensors_poll_delay = NULL; + ret = sensors_classdev_register(&input_bottom_right->dev, &sensors_capsensor_bottom_right_cdev); + if (ret < 0) + LOG_DBG("create bottom_right cap sensor_class file failed (%d)\n", ret); + } +#endif + + abovXX_sar_init(this); + + write_register(this, ABOV_CTRL_MODE_REG, 0x02); + mEnabled = 0; + + INIT_WORK(&this->ps_notify_work, ps_notify_callback_work); + this->ps_notif.notifier_call = ps_notify_callback; + ret = power_supply_reg_notifier(&this->ps_notif); + if (ret) { + LOG_DBG( + "Unable to register ps_notifier: %d\n", ret); + goto free_ps_notifier; + } + + psy = power_supply_get_by_name("usb"); + if (psy) { + ret = ps_get_state(psy, &this->ps_is_present); + if (ret) { + LOG_DBG( + "psy get property failed rc=%d\n", + ret); + goto free_ps_notifier; + } + } + + this->loading_fw = false; + if (isForceUpdate == true) { + INIT_WORK(&this->fw_update_work, capsense_fore_update_work); + } else { + INIT_WORK(&this->fw_update_work, capsense_update_work); + } + schedule_work(&this->fw_update_work); + + + LOG_INFO("abov_probe() end\n"); + return 0; + } + return -ENOMEM; + +free_ps_notifier: + LOG_DBG("%s free ps notifier:.\n", __func__); + power_supply_unreg_notifier(&this->ps_notif); + regulator_disable(pplatData->cap_svdd); + regulator_put(pplatData->cap_svdd); + +err_svdd_error: + LOG_DBG("%s svdd defer.\n", __func__); + regulator_disable(pplatData->cap_vdd); + regulator_put(pplatData->cap_vdd); + +err_vdd_defer: + LOG_DBG("%s input free device.\n", __func__); + input_free_device(input_top); + if(abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) + input_free_device(input_bottom); + else { + input_free_device(input_bottom_left); + input_free_device(input_bottom_right); + } + + return ret; +} + +/** + * fn static int abov_remove(struct i2c_client *client) + * brief Called when device is to be removed + * param client Pointer to i2c_client struct + * return Value from abovXX_sar_remove() + */ +static int abov_remove(struct i2c_client *client) +{ + pabov_platform_data_t pplatData = 0; + pabov_t pDevice = 0; + pabovXX_t this = i2c_get_clientdata(client); + + pDevice = this->pDevice; + if (this && pDevice) { +#ifdef USE_SENSORS_CLASS + sensors_classdev_unregister(&sensors_capsensor_top_cdev); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) + sensors_classdev_unregister(&sensors_capsensor_bottom_cdev); + else { + sensors_classdev_unregister(&sensors_capsensor_bottom_left_cdev); + sensors_classdev_unregister(&sensors_capsensor_bottom_right_cdev); + } +#endif + input_unregister_device(pDevice->pbuttonInformation->input_top); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) + input_unregister_device(pDevice->pbuttonInformation->input_bottom); + else { + input_unregister_device(pDevice->pbuttonInformation->input_bottom_left); + input_unregister_device(pDevice->pbuttonInformation->input_bottom_right); + } + if (this->board->cap_svdd_en) { + regulator_disable(this->board->cap_svdd); + regulator_put(this->board->cap_svdd); + } + + if (this->board->cap_vdd_en) { + regulator_disable(this->board->cap_vdd); + regulator_put(this->board->cap_vdd); + } + + sysfs_remove_group(&client->dev.kobj, &abov_attr_group); + pplatData = client->dev.platform_data; + if (pplatData && pplatData->exit_platform_hw) + pplatData->exit_platform_hw(); + kfree(this->pDevice); + } + return abovXX_sar_remove(this); +} + +static int abov_suspend(struct device *dev) +{ + pabovXX_t this = dev_get_drvdata(dev); + + abovXX_suspend(this); + return 0; +} + +static int abov_resume(struct device *dev) +{ + pabovXX_t this = dev_get_drvdata(dev); + + abovXX_resume(this); + return 0; +} +static SIMPLE_DEV_PM_OPS(abov_pm_ops, abov_suspend, abov_resume); + + +#ifdef CONFIG_OF +static const struct of_device_id abov_match_tbl[] = { + { .compatible = "abov,abov_sar" }, + { }, +}; +MODULE_DEVICE_TABLE(of, abov_match_tbl); +#endif + +static struct i2c_device_id abov_idtable[] = { + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, abov_idtable); + +static struct i2c_driver abov_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(abov_match_tbl), + .pm = &abov_pm_ops, + }, + .id_table = abov_idtable, + .probe = abov_probe, + .remove = abov_remove, +}; +static int __init abov_init(void) +{ + return i2c_add_driver(&abov_driver); +} +static void __exit abov_exit(void) +{ + i2c_del_driver(&abov_driver); +} + +module_init(abov_init); +module_exit(abov_exit); + +MODULE_AUTHOR("ABOV Corp."); +MODULE_DESCRIPTION("ABOV Capacitive Touch Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); + +#ifdef USE_THREADED_IRQ +static void abovXX_process_interrupt(pabovXX_t this, u8 nirqlow) +{ + if (!this) { + pr_err("abovXX_worker_func, NULL abovXX_t\n"); + return; + } + /* since we are not in an interrupt don't need to disable irq. */ + this->statusFunc[6](this); + if (unlikely(this->useIrqTimer && nirqlow)) { + /* In case we need to send a timer for example on a touchscreen + * checking penup, perform this here + */ + cancel_delayed_work(&this->dworker); + schedule_delayed_work(&this->dworker, msecs_to_jiffies(this->irqTimeout)); + LOG_INFO("Schedule Irq timer"); + } +} + + +static void abovXX_worker_func(struct work_struct *work) +{ + pabovXX_t this = 0; + + if (work) { + this = container_of(work, abovXX_t, dworker.work); + if (!this) { + LOG_DBG("abovXX_worker_func, NULL abovXX_t\n"); + return; + } + if ((!this->get_nirq_low) || (!this->get_nirq_low(this->board->irq_gpio))) { + /* only run if nirq is high */ + abovXX_process_interrupt(this, 0); + } + } else { + LOG_INFO("abovXX_worker_func, NULL work_struct\n"); + } +} +static irqreturn_t abovXX_interrupt_thread(int irq, void *data) +{ + pabovXX_t this = 0; + this = data; + + mutex_lock(&this->mutex); + LOG_INFO("abovXX_irq\n"); + if ((!this->get_nirq_low) || this->get_nirq_low(this->board->irq_gpio)) + abovXX_process_interrupt(this, 1); + else + LOG_DBG("abovXX_irq - nirq read high\n"); + mutex_unlock(&this->mutex); + return IRQ_HANDLED; +} +#else +static void abovXX_schedule_work(pabovXX_t this, unsigned long delay) +{ + unsigned long flags; + + if (this) { + LOG_INFO("abovXX_schedule_work()\n"); + spin_lock_irqsave(&this->lock, flags); + /* Stop any pending penup queues */ + cancel_delayed_work(&this->dworker); + /* + *after waiting for a delay, this put the job in the kernel-global + workqueue. so no need to create new thread in work queue. + */ + schedule_delayed_work(&this->dworker, delay); + spin_unlock_irqrestore(&this->lock, flags); + } else + LOG_DBG("abovXX_schedule_work, NULL pabovXX_t\n"); +} + +static irqreturn_t abovXX_irq(int irq, void *pvoid) +{ + pabovXX_t this = 0; + if (pvoid) { + this = (pabovXX_t)pvoid; + LOG_INFO("abovXX_irq\n"); + if ((!this->get_nirq_low) || this->get_nirq_low(this->board->irq_gpio)) { + LOG_INFO("abovXX_irq - Schedule Work\n"); + abovXX_schedule_work(this, 0); + } else + LOG_INFO("abovXX_irq - nirq read high\n"); + } else + LOG_INFO("abovXX_irq, NULL pvoid\n"); + return IRQ_HANDLED; +} + +static void abovXX_worker_func(struct work_struct *work) +{ + pabovXX_t this = 0; + int counter = 0; + u8 nirqLow = 0; + + if (work) { + this = container_of(work, abovXX_t, dworker.work); + + if (!this) { + LOG_INFO("abovXX_worker_func, NULL abovXX_t\n"); + return; + } + if (unlikely(this->useIrqTimer)) { + if ((!this->get_nirq_low) || this->get_nirq_low(this->board->irq_gpio)) + nirqLow = 1; + } + /* since we are not in an interrupt don't need to disable irq. */ + counter = -1; + while ((++counter) < MAX_NUM_STATUS_BITS) { /* counter start from MSB */ + LOG_INFO("Looping Counter %d\n", counter); + if (((status >> counter) & 0x01) && (this->statusFunc[counter])) { + LOG_INFO("Function Pointer Found. Calling\n"); + this->statusFunc[counter](this); + } + } + if (unlikely(this->useIrqTimer && nirqLow)) { + /* Early models and if RATE=0 for newer models require a penup timer */ + /* Queue up the function again for checking on penup */ + abovXX_schedule_work(this, msecs_to_jiffies(this->irqTimeout)); + } + } else { + LOG_INFO("abovXX_worker_func, NULL work_struct\n"); + } +} +#endif + +void abovXX_suspend(pabovXX_t this) +{ + if (this) { + LOG_INFO("ABOV suspend[%d]: disable irq!\n",mEnabled); + disable_irq(this->irq); + /* if upper layer don't disable capsensor, */ + /* we should let it enter sleep in suspend. */ + if (mEnabled) + write_register(this, ABOV_CTRL_MODE_REG, 0x01); + } +} +void abovXX_resume(pabovXX_t this) +{ + if (this) { + LOG_INFO("ABOV resume[%d]: enable irq!\n",mEnabled); + /* we should let capsensor enter active in resume*/ + if (mEnabled) + write_register(this, ABOV_CTRL_MODE_REG, 0x00); + enable_irq(this->irq); + } +} + +int abovXX_sar_init(pabovXX_t this) +{ + int err = 0; + + if (this && this->pDevice) { +#ifdef USE_THREADED_IRQ + + /* initialize worker function */ + INIT_DELAYED_WORK(&this->dworker, abovXX_worker_func); + + + /* initialize mutex */ + mutex_init(&this->mutex); + /* initailize interrupt reporting */ + this->irq_disabled = 0; + err = request_threaded_irq(this->irq, NULL, abovXX_interrupt_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, this->pdev->driver->name, + this); +#else + /* initialize spin lock */ + spin_lock_init(&this->lock); + + /* initialize worker function */ + INIT_DELAYED_WORK(&this->dworker, abovXX_worker_func); + + /* initailize interrupt reporting */ + this->irq_disabled = 0; + err = request_irq(this->irq, abovXX_irq, IRQF_TRIGGER_FALLING, + this->pdev->driver->name, this); +#endif + if (err) { + LOG_DBG("irq %d busy?\n", this->irq); + return err; + } +#ifdef USE_THREADED_IRQ + LOG_DBG("registered with threaded irq (%d)\n", this->irq); +#else + LOG_DBG("registered with irq (%d)\n", this->irq); +#endif + /* call init function pointer (this should initialize all registers */ + if (this->init) + return this->init(this); + LOG_DBG("No init function!!!!\n"); + } + return -ENOMEM; +} + +int abovXX_sar_remove(pabovXX_t this) +{ + if (this) { + cancel_delayed_work_sync(&this->dworker); /* Cancel the Worker Func */ + free_irq(this->irq, this); + kfree(this); + return 0; + } + return -ENOMEM; +} diff --git a/drivers/misc/abov_mmi_overlay/Android.mk b/drivers/misc/abov_mmi_overlay/Android.mk new file mode 100644 index 000000000000..80c0429dbdc4 --- /dev/null +++ b/drivers/misc/abov_mmi_overlay/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := abov_sar_mmi_overlay.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/abov_mmi_overlay/Kbuild b/drivers/misc/abov_mmi_overlay/Kbuild new file mode 100644 index 000000000000..ab286a799765 --- /dev/null +++ b/drivers/misc/abov_mmi_overlay/Kbuild @@ -0,0 +1,14 @@ +# add -Wall to try to catch everything we can. +KERNEL_DIR_PATH = $(shell find $(TOP)/kernel/ -name "msm-*" | head -1 | grep -i "kernel/msm-*") + +KERNEL_VERSION_NAME = $(shell basename $(KERNEL_DIR_PATH)) +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +ifneq (,$(KERNEL_DIR_PATH)) + EXTRA_CFLAGS += -I$(TOP)/kernel/$(KERNEL_VERSION_NAME)/drivers/base +else + EXTRA_CFLAGS += -I$(TOP)/kernel/drivers/base +endif +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../sensors/Module.symvers +obj-m = abov_sar_mmi_overlay.o diff --git a/drivers/misc/abov_mmi_overlay/Makefile b/drivers/misc/abov_mmi_overlay/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/misc/abov_mmi_overlay/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/misc/abov_mmi_overlay/abov_sar_mmi_overlay.c b/drivers/misc/abov_mmi_overlay/abov_sar_mmi_overlay.c new file mode 100644 index 000000000000..5a166f763d0d --- /dev/null +++ b/drivers/misc/abov_mmi_overlay/abov_sar_mmi_overlay.c @@ -0,0 +1,2416 @@ +/* + * file abov_sar.c + * brief abov Driver for two channel SAP using + * + * Driver for the ABOV + * Copyright (c) 2015-2016 ABOV Corp + * + * 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. + */ +#define DRIVER_NAME "abov_sar" +#define USE_SENSORS_CLASS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* main struct, interrupt,init,pointers */ + +#include +#include +#include +#include +#include +#include +#include "base.h" + +#define SLEEP(x) mdelay(x) + +#define C_I2C_FIFO_SIZE 8 + +#define I2C_MASK_FLAG (0x00ff) + +static u8 checksum_h; +static u8 checksum_h_bin; +static u8 checksum_l; +static u8 checksum_l_bin; +static u32 abov_channel_number = 0; + +#define IDLE 0 +#define ACTIVE 1 +#define S_PROX 1 +#define S_BODY 2 + +#define LOG_TAG "" + +#define LOG_INFO(fmt, args...) pr_info(LOG_TAG "[INFO]" "<%s><%d>"fmt, __func__, __LINE__, ##args) +#define LOG_DBG(fmt, args...) pr_debug(LOG_TAG "[DBG]" "<%s><%d>"fmt, __func__, __LINE__, ##args) +#define LOG_ERR(fmt, args...) pr_err(LOG_TAG "[ERR]" "<%s><%d>"fmt, __func__, __LINE__, ##args) + +static int last_val; +static int mEnabled; +static int programming_done; +pabovXX_t abov_sar_ptr; + +/** + * struct abov + * Specialized struct containing input event data, platform data, and + * last cap state read if needed. + */ +typedef struct abov { + pbuttonInformation_t pbuttonInformation; + pabov_platform_data_t hw; /* specific platform data settings */ +} abov_t, *pabov_t; + +static void ForcetoTouched(pabovXX_t this) +{ + pabov_t pDevice = NULL; + struct input_dev *input_ch0 = NULL; + struct input_dev *input_ch1 = NULL; + struct input_dev *input_ch2 = NULL; + struct input_dev *input_ch3 = NULL; + struct _buttonInfo *pCurrentButton = NULL; + + pDevice = this->pDevice; + if (this && pDevice) { + LOG_DBG("ForcetoTouched()\n"); + + pCurrentButton = pDevice->pbuttonInformation->buttons; + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + if (abov_channel_number == 3) + input_ch2 = pDevice->pbuttonInformation->input_ch2; + else if (abov_channel_number == 4) { + input_ch2 = pDevice->pbuttonInformation->input_ch2; + input_ch3 = pDevice->pbuttonInformation->input_ch3; + } + pCurrentButton->state = ACTIVE; + last_val = 1; + if (mEnabled) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + if (abov_channel_number == 3) { + input_report_abs(input_ch2, ABS_DISTANCE, 1); + input_sync(input_ch2); + } else if (abov_channel_number == 4) { + input_report_abs(input_ch2, ABS_DISTANCE, 1); + input_sync(input_ch2); + input_report_abs(input_ch3, ABS_DISTANCE, 1); + input_sync(input_ch3); + } + } + LOG_DBG("Leaving ForcetoTouched()\n"); + } +} + +/** + * fn static int write_register(pabovXX_t this, u8 address, u8 value) + * brief Sends a write register to the device + * param this Pointer to main parent struct + * param address 8-bit register address + * param value 8-bit register value to write to address + * return Value from i2c_master_send + */ +static int write_register(pabovXX_t this, u8 address, u8 value) +{ + struct i2c_client *i2c = 0; + char buffer[2]; + int returnValue = 0; + + buffer[0] = address; + buffer[1] = value; + returnValue = -ENOMEM; + if (this && this->bus) { + i2c = this->bus; + + returnValue = i2c_master_send(i2c, buffer, 2); + LOG_DBG("write_register Addr: 0x%x Val: 0x%x Return: %d\n", + address, value, returnValue); + } + if (returnValue < 0) { + ForcetoTouched(this); + LOG_ERR("Write_register-ForcetoTouched()\n"); + } + return returnValue; +} + +/** + * fn static int read_register(pabovXX_t this, u8 address, u8 *value) + * brief Reads a register's value from the device + * param this Pointer to main parent struct + * param address 8-Bit address to read from + * param value Pointer to 8-bit value to save register value to + * return Value from i2c_smbus_read_byte_data if < 0. else 0 + */ +static int read_register(pabovXX_t this, u8 address, u8 *value) +{ + struct i2c_client *i2c = 0; + s32 returnValue = 0; + + if (this && value && this->bus) { + i2c = this->bus; + returnValue = i2c_smbus_read_byte_data(i2c, address); + LOG_DBG("read_register Addr: 0x%x Return: 0x%x\n", + address, returnValue); + } + + if (returnValue >= 0) { + *value = returnValue; + return 0; + } else { + ForcetoTouched(this); + LOG_ERR("read_register-ForcetoTouched()\n"); + return returnValue; + } + return returnValue; +} + + +/** + * brief Perform a manual offset calibration + * param this Pointer to main parent struct + * return Value return value from the write register + */ +static int manual_offset_calibration(pabovXX_t this) +{ + s32 returnValue = 0; + + returnValue = write_register(this, ABOV_RECALI_REG, 0x01); + return returnValue; +} + +/** + * brief sysfs show function for manual calibration which currently just + * returns register value. + */ +static ssize_t manual_offset_calibration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 reg_value = 0; + pabovXX_t this = dev_get_drvdata(dev); + + LOG_DBG("Reading IRQSTAT_REG\n"); + read_register(this, ABOV_IRQSTAT_REG, ®_value); + return scnprintf(buf, PAGE_SIZE, "%d\n", reg_value); +} + +/* brief sysfs store function for manual calibration */ +static ssize_t manual_offset_calibration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = dev_get_drvdata(dev); + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + if (val) { + LOG_DBG("Performing manual_offset_calibration()\n"); + manual_offset_calibration(this); + } + return count; +} + +static DEVICE_ATTR(calibrate, 0644, manual_offset_calibration_show, + manual_offset_calibration_store); +static struct attribute *abov_attributes[] = { + &dev_attr_calibrate.attr, + NULL, +}; +static struct attribute_group abov_attr_group = { + .attrs = abov_attributes, +}; + + +/** + * brief Initialize I2C config from platform data + * param this Pointer to main parent struct + * + */ +static void hw_init(pabovXX_t this) +{ + pabov_t pDevice = 0; + pabov_platform_data_t pdata = 0; + int i = 0; + /* configure device */ + LOG_DBG("Inside hw_init().\n"); + pDevice = this->pDevice; + pdata = pDevice->hw; + if (this && pDevice && pdata) { + while (i < pdata->i2c_reg_num) { + /* Write all registers/values contained in i2c_reg */ + write_register(this, pdata->pi2c_reg[i].reg, + pdata->pi2c_reg[i].val); + i++; + } + } else { + LOG_ERR("ERROR! platform data 0x%p\n", pDevice->hw); + /* Force to touched if error */ + ForcetoTouched(this); + } + LOG_DBG("Exiting hw_init().\n"); +} + +/** + * fn static int initialize(pabovXX_t this) + * brief Performs all initialization needed to configure the device + * param this Pointer to main parent struct + * return Last used command's return value (negative if error) + */ +static int initialize(pabovXX_t this) +{ + if (this) { + LOG_DBG("Inside initialize().\n"); + /* prepare reset by disabling any irq handling */ + if(this->irq_disabled == 0) { + disable_irq(this->irq); + this->irq_disabled = 1; + } + hw_init(this); + msleep(300); /* make sure everything is running */ + /* re-enable interrupt handling */ + if(this->irq_disabled == 1) { + enable_irq(this->irq); + this->irq_disabled = 0; + } + /* make sure no interrupts are pending since enabling irq will only + * work on next falling edge */ + LOG_DBG("Exiting initialize().\n"); + programming_done = ACTIVE; + return 0; + } + programming_done = IDLE; + return -ENOMEM; +} + +/** + * brief Handle what to do when a touch occurs + * param this Pointer to main parent struct + */ +static void touchProcess(pabovXX_t this) +{ + int counter = 0; + u8 i = 0; + int numberOfButtons = 0; + pabov_t pDevice = NULL; + struct _buttonInfo *buttons = NULL; + struct input_dev *input_ch0 = NULL; + struct input_dev *input_ch1 = NULL; + struct input_dev *input_ch2 = NULL; + struct input_dev *input_ch3 = NULL; + struct _buttonInfo *pCurrentButton = NULL; + struct abov_platform_data *board; + + pDevice = this->pDevice; + board = this->board; + if (this && pDevice) { + LOG_DBG("Inside touchProcess()\n"); + read_register(this, ABOV_IRQSTAT_REG, &i); + + buttons = pDevice->pbuttonInformation->buttons; + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + if (abov_channel_number == 3) + input_ch2 = pDevice->pbuttonInformation->input_ch2; + else if (abov_channel_number == 4) { + input_ch2 = pDevice->pbuttonInformation->input_ch2; + input_ch3 = pDevice->pbuttonInformation->input_ch3; + } + numberOfButtons = pDevice->pbuttonInformation->buttonSize; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (unlikely((buttons == NULL) || (input_ch0 == NULL) || (input_ch1 == NULL))) { + LOG_ERR("ERROR!! buttons or input NULL!!!\n"); + return; + } + } else if (abov_channel_number == 3) { + if (unlikely((buttons == NULL) || (input_ch0 == NULL) || (input_ch1 == NULL) || (input_ch2 == NULL))) { + LOG_ERR("ERROR!! buttons or input NULL!!!\n"); + return; + } + } else if (abov_channel_number == 4) { + if (unlikely((buttons == NULL) || (input_ch0 == NULL) || (input_ch1 == NULL) || (input_ch2 == NULL) || (input_ch3 == NULL))) { + LOG_ERR("ERROR!! buttons or input NULL!!!\n"); + return; + } + } + + for (counter = 0; counter < numberOfButtons; counter++) { + pCurrentButton = &buttons[counter]; + if (pCurrentButton == NULL) { + LOG_ERR("ERR!current button index: %d NULL!\n", + counter); + return; /* ERRORR!!!! */ + } + switch (pCurrentButton->state) { + case IDLE: /* Button is being in far state! */ + if ((i & pCurrentButton->mask) == pCurrentButton->mask) { + LOG_DBG("CS %d State=BODY.\n", + counter); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 2); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 2); + input_sync(input_ch1); + } + } else if (abov_channel_number == 3) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 2); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 2); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 2); + input_sync(input_ch2); + } + } else if (abov_channel_number == 4) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 2); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 2); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 2); + input_sync(input_ch2); + } else if (board->cap_channel_ch3 == counter) { + input_report_abs(input_ch3, ABS_DISTANCE, 2); + input_sync(input_ch3); + } + } + pCurrentButton->state = S_BODY; + last_val = 2; + } else if ((i & pCurrentButton->mask) == (pCurrentButton->mask & board->cap_button_mask )) { + LOG_DBG("CS %d State=PROX.\n", + counter); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + } + } else if (abov_channel_number == 3) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 1); + input_sync(input_ch2); + } + } else if (abov_channel_number == 4) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 1); + input_sync(input_ch2); + } else if (board->cap_channel_ch3 == counter) { + input_report_abs(input_ch3, ABS_DISTANCE, 1); + input_sync(input_ch3); + } + } + pCurrentButton->state = S_PROX; + last_val = 0; + } else { + LOG_DBG("CS %d still in IDLE State.\n", + counter); + } + break; + case S_PROX: /* Button is being in proximity! */ + if ((i & pCurrentButton->mask) == pCurrentButton->mask) { + LOG_DBG("CS %d State=BODY.\n", + counter); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 2); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 2); + input_sync(input_ch1); + } + } else if (abov_channel_number == 3) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 2); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 2); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 2); + input_sync(input_ch2); + } + } else if (abov_channel_number == 4) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 2); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 2); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 2); + input_sync(input_ch2); + } else if (board->cap_channel_ch3 == counter) { + input_report_abs(input_ch3, ABS_DISTANCE, 2); + input_sync(input_ch3); + } + } + pCurrentButton->state = S_BODY; + last_val = 2; + } else if ((i & pCurrentButton->mask) == (pCurrentButton->mask & board->cap_button_mask)) { + LOG_DBG("CS %d still in PROX State.\n", + counter); + } else{ + LOG_DBG("CS %d State=IDLE.\n", + counter); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + } + } else if (abov_channel_number == 3) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + } + } else if (abov_channel_number == 4) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + } else if (board->cap_channel_ch3 == counter) { + input_report_abs(input_ch3, ABS_DISTANCE, 0); + input_sync(input_ch3); + } + } + pCurrentButton->state = IDLE; + last_val = 0; + } + break; + case S_BODY: /* Button is being in 0mm! */ + if ((i & pCurrentButton->mask) == pCurrentButton->mask) { + LOG_DBG("CS %d still in BODY State.\n", + counter); + } else if ((i & pCurrentButton->mask) == (pCurrentButton->mask & board->cap_button_mask)) { + LOG_DBG("CS %d State=PROX.\n", + counter); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + } + } else if (abov_channel_number == 3) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 1); + input_sync(input_ch2); + } + } else if (abov_channel_number == 4) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 1); + input_sync(input_ch2); + } else if (board->cap_channel_ch3 == counter) { + input_report_abs(input_ch3, ABS_DISTANCE, 1); + input_sync(input_ch3); + } + } + pCurrentButton->state = S_PROX; + last_val = 1; + } else{ + LOG_DBG("CS %d State=IDLE.\n", + counter); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + } + } else if (abov_channel_number == 3){ + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + } + } else if (abov_channel_number == 4) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + } else if (board->cap_channel_ch3 == counter) { + input_report_abs(input_ch3, ABS_DISTANCE, 0); + input_sync(input_ch3); + } + } + pCurrentButton->state = IDLE; + last_val = 0; + } + break; + default: /* Shouldn't be here, device only allowed ACTIVE or IDLE */ + break; + }; + } + LOG_DBG("Leaving touchProcess()\n"); + } +} + +static int abov_get_nirq_state(unsigned irq_gpio) +{ + if (irq_gpio) { + return !gpio_get_value(irq_gpio); + } else { + LOG_ERR("abov irq_gpio is not set."); + return -EINVAL; + } +} + +static struct _totalButtonInformation smtcButtonInformation = { + .buttons = psmtcButtons, + .buttonSize = ARRAY_SIZE(psmtcButtons), +}; + +/** + *fn static void abov_reg_setup_init(struct i2c_client *client) + *brief read reg val form dts + * reg_array_len for regs needed change num + * data_array_val's format + */ +static void abov_reg_setup_init(struct i2c_client *client) +{ + u32 data_array_len = 0; + u32 *data_array; + int ret, i, j; + struct device_node *np = client->dev.of_node; + + ret = of_property_read_u32(np, "reg_array_len", &data_array_len); + if (ret < 0) { + LOG_ERR("data_array_len read error"); + return; + } + data_array = kmalloc(data_array_len * 2 * sizeof(u32), GFP_KERNEL); + ret = of_property_read_u32_array(np, "reg_array_val", + data_array, + data_array_len*2); + if (ret < 0) { + LOG_ERR("data_array_val read error"); + return; + } + for (i = 0; i < ARRAY_SIZE(abov_i2c_reg_setup); i++) { + for (j = 0; j < data_array_len*2; j += 2) { + if (data_array[j] == abov_i2c_reg_setup[i].reg) { + abov_i2c_reg_setup[i].val = data_array[j+1]; + LOG_DBG("read dtsi 0x%02x:0x%02x set reg\n", + data_array[j], data_array[j+1]); + } + } + } + kfree(data_array); +} + +static void abov_platform_data_of_init(struct i2c_client *client, + pabov_platform_data_t pplatData) +{ + struct device_node *np = client->dev.of_node; + u32 cap_channel_ch0,cap_channel_ch1,cap_channel_ch2,cap_channel_ch3; + int ret; + + client->irq = of_get_gpio(np, 0); + pplatData->irq_gpio = client->irq; + + ret = of_property_read_u32(np, "cap,channel_number", &abov_channel_number); + if (ret < 0) { + LOG_ERR("get cap,channel number node error!\n"); + } + ret = of_property_read_u32(np, "cap,use_channel_ch0", &cap_channel_ch0); + ret = of_property_read_string(np, "ch0_name", &pplatData->cap_ch0_name); + ret = of_property_read_u32(np, "cap,use_channel_ch1", &cap_channel_ch1); + ret = of_property_read_string(np, "ch1_name", &pplatData->cap_ch1_name); + if (abov_channel_number == 3) { + ret = of_property_read_u32(np, "cap,use_channel_ch2", &cap_channel_ch2); + ret = of_property_read_string(np, "ch2_name", &pplatData->cap_ch2_name); + } else if (abov_channel_number == 4) { + ret = of_property_read_u32(np, "cap,use_channel_ch2", &cap_channel_ch2); + ret = of_property_read_string(np, "ch2_name", &pplatData->cap_ch2_name); + ret = of_property_read_u32(np, "cap,use_channel_ch3", &cap_channel_ch3); + ret = of_property_read_string(np, "ch3_name", &pplatData->cap_ch3_name); + } + pplatData->cap_channel_ch0 = (int)cap_channel_ch0; + pplatData->cap_channel_ch1 = (int)cap_channel_ch1; + if (abov_channel_number == 3) + pplatData->cap_channel_ch2 = (int)cap_channel_ch2; + else if (abov_channel_number == 4) { + pplatData->cap_channel_ch2 = (int)cap_channel_ch2; + pplatData->cap_channel_ch3 = (int)cap_channel_ch3; + } + + pplatData->get_is_nirq_low = abov_get_nirq_state; + pplatData->init_platform_hw = NULL; + /* pointer to an exit function. Here in case needed in the future */ + /* + *.exit_platform_hw = abov_exit_ts, + */ + pplatData->exit_platform_hw = NULL; + abov_reg_setup_init(client); + pplatData->pi2c_reg = abov_i2c_reg_setup; + pplatData->i2c_reg_num = ARRAY_SIZE(abov_i2c_reg_setup); + + pplatData->pbuttonInformation = &smtcButtonInformation; + + ret = of_property_read_string(np, "label", &pplatData->fw_name); + if (ret < 0) { + LOG_ERR("firmware name read error!\n"); + return; + } + + ret = of_property_read_u32(np, "cap,fw_mode_ret", &pplatData->fw_mode_ret); + if (ret < 0) { + LOG_ERR("get cap,fw_mode_ret error,set default value.\n"); + pplatData->fw_mode_ret = ABOV_DF_FW_MODE; + } + ret = of_property_read_u32(np, "button_mask", &pplatData->cap_button_mask); + if (ret < 0) { + LOG_ERR("get button_mask,set default value.\n"); + pplatData->cap_button_mask = 0x15; + } +} + +static ssize_t reset_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 8, "%d\n", programming_done); +} + +static ssize_t reset_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + pabov_t pDevice = NULL; + struct input_dev *input_ch0 = NULL; + struct input_dev *input_ch1 = NULL; + struct input_dev *input_ch2 = NULL; + struct input_dev *input_ch3 = NULL; + + pDevice = this->pDevice; + input_ch0 = pDevice->pbuttonInformation->input_ch0; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) + input_ch1 = pDevice->pbuttonInformation->input_ch1; + else if (abov_channel_number == 3) { + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + } else if (abov_channel_number == 4) { + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + input_ch3 = pDevice->pbuttonInformation->input_ch3; + } + + if (!count || (this == NULL)) + return -EINVAL; + + if (!strncmp(buf, "reset", 5) || !strncmp(buf, "1", 1)) + write_register(this, ABOV_SOFTRESET_REG, 0x10); + + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + if (abov_channel_number == 3) { + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + } else if (abov_channel_number == 4) { + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + input_report_abs(input_ch3, ABS_DISTANCE, 0); + input_sync(input_ch3); + } + + return count; +} + +static CLASS_ATTR_RW(reset); + +static ssize_t int_state_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + pabovXX_t this = abov_sar_ptr; + LOG_DBG("Reading INT line state\n"); + return snprintf(buf, 8, "%d\n", this->int_state); +} +static CLASS_ATTR_RO(int_state); + +static ssize_t fw_download_status_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + pabovXX_t this = abov_sar_ptr; + LOG_DBG("Reading fw download state\n"); + return snprintf(buf, 8, "%d", this->fw_dl_status); +} +static CLASS_ATTR_RO(fw_download_status); + +static ssize_t enable_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 8, "%d\n", mEnabled); +} + +static ssize_t enable_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + pabov_t pDevice = NULL; + struct input_dev *input_ch0 = NULL; + struct input_dev *input_ch1 = NULL; + struct input_dev *input_ch2 = NULL; + struct input_dev *input_ch3 = NULL; + + pDevice = this->pDevice; + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + if (abov_channel_number == 3) + input_ch2 = pDevice->pbuttonInformation->input_ch2; + else if (abov_channel_number == 4) { + input_ch2 = pDevice->pbuttonInformation->input_ch2; + input_ch3 = pDevice->pbuttonInformation->input_ch3; + } + + if (!count || (this == NULL)) + return -EINVAL; + + if (!strncmp(buf, "1", 1)) { + LOG_INFO("enable cap sensor\n"); + initialize(this); + + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + if (abov_channel_number == 3) { + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + } else if (abov_channel_number == 4) { + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + input_report_abs(input_ch3, ABS_DISTANCE, 0); + input_sync(input_ch3); + } + mEnabled = 1; + } else if (!strncmp(buf, "0", 1)) { + LOG_INFO("disable cap sensor\n"); + + write_register(this, ABOV_CTRL_MODE_REG, 0x02); + + input_report_abs(input_ch0, ABS_DISTANCE, -1); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, -1); + input_sync(input_ch1); + if (abov_channel_number == 3) { + input_report_abs(input_ch2, ABS_DISTANCE, -1); + input_sync(input_ch2); + } else if (abov_channel_number == 4) { + input_report_abs(input_ch2, ABS_DISTANCE, -1); + input_sync(input_ch2); + input_report_abs(input_ch3, ABS_DISTANCE, -1); + input_sync(input_ch3); + } + mEnabled = 0; + } else { + LOG_ERR("unknown enable symbol\n"); + } + + return count; +} + +#ifdef USE_SENSORS_CLASS +static int capsensor_set_enable(struct sensors_classdev *sensors_cdev, unsigned int enable) +{ + pabovXX_t this = abov_sar_ptr; + pabov_t pDevice = NULL; + struct input_dev *input_ch0 = NULL; + struct input_dev *input_ch1 = NULL; + struct input_dev *input_ch2 = NULL; + struct input_dev *input_ch3 = NULL; + + pDevice = this->pDevice; + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + if (abov_channel_number == 3) + input_ch2 = pDevice->pbuttonInformation->input_ch2; + else if (abov_channel_number == 4) { + input_ch2 = pDevice->pbuttonInformation->input_ch2; + input_ch3 = pDevice->pbuttonInformation->input_ch3; + } + + if (enable == 1) { + LOG_INFO("enable cap sensor: %s\n",sensors_cdev->name); + if(mEnabled == 0){ + initialize(this); + mEnabled = 1; + } + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if(!strcmp(sensors_cdev->name, this->board->cap_ch0_name)){ + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH0; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch1_name)){ + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH1; + } + } else if(abov_channel_number == 3){ + if(!strcmp(sensors_cdev->name, this->board->cap_ch0_name)){ + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH0; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch1_name)){ + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH1; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch2_name)){ + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH2; + } + } else if(abov_channel_number == 4) { + if(!strcmp(sensors_cdev->name, this->board->cap_ch0_name)){ + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH0; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch1_name)){ + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH1; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch2_name)){ + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH2; + } else if(!strcmp(sensors_cdev->name, this->board->cap_ch3_name)){ + input_report_abs(input_ch3, ABS_DISTANCE, 0); + input_sync(input_ch3); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH3; + } + } + } else if (enable == 0) { + LOG_INFO("disable cap sensor: %s\n",sensors_cdev->name); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if(!strcmp(sensors_cdev->name, this->board->cap_ch0_name)){ + input_report_abs(input_ch0, ABS_DISTANCE, -1); + input_sync(input_ch0); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH0; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch1_name)){ + input_report_abs(input_ch1, ABS_DISTANCE, -1); + input_sync(input_ch1); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH1; + } + } else if(abov_channel_number == 3){ + if(!strcmp(sensors_cdev->name, this->board->cap_ch0_name)){ + input_report_abs(input_ch0, ABS_DISTANCE, -1); + input_sync(input_ch0); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH0; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch1_name)){ + input_report_abs(input_ch1, ABS_DISTANCE, -1); + input_sync(input_ch1); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH1; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch2_name)){ + input_report_abs(input_ch2, ABS_DISTANCE, -1); + input_sync(input_ch2); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH2; + } + } else if (abov_channel_number == 4){ + if(!strcmp(sensors_cdev->name, this->board->cap_ch0_name)){ + input_report_abs(input_ch0, ABS_DISTANCE, -1); + input_sync(input_ch0); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH0; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch1_name)){ + input_report_abs(input_ch1, ABS_DISTANCE, -1); + input_sync(input_ch1); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH1; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch2_name)){ + input_report_abs(input_ch2, ABS_DISTANCE, -1); + input_sync(input_ch2); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH2; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch3_name)){ + input_report_abs(input_ch3, ABS_DISTANCE, -1); + input_sync(input_ch3); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH3; + } + } + if(!(this->enable_flag &0x0F)){ + mEnabled = 0; + write_register(this, ABOV_CTRL_MODE_REG, 0x02); + LOG_INFO("all cap sensor disable,change to STOP mode.\n"); + } + } else { + LOG_ERR("unknown enable symbol\n"); + } + + return 0; +} +#endif + +static CLASS_ATTR_RW(enable); + +static ssize_t reg_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u8 reg_value = 0, i; + pabovXX_t this = abov_sar_ptr; + char *p = buf; + + if (this->read_flag) { + this->read_flag = 0; + read_register(this, this->read_reg, ®_value); + p += snprintf(p, PAGE_SIZE, "(0x%02x)=0x%02x\n", this->read_reg, reg_value); + return (p-buf); + } + + for (i = 0; i < 0x50; i++) { + read_register(this, i, ®_value); + p += snprintf(p, PAGE_SIZE, "(0x%02x)=0x%02x\n", + i, reg_value); + } + + for (i = 0x80; i < 0xB0; i++) { + read_register(this, i, ®_value); + p += snprintf(p, PAGE_SIZE, "(0x%02x)=0x%02x\n", + i, reg_value); + } + + return (p-buf); +} + +static ssize_t reg_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + unsigned int val, reg, opt; + if (sscanf(buf, "%x,%x,%x", ®, &val, &opt) == 3) { + LOG_DBG("%s, read reg = 0x%02x\n", __func__, *(u8 *)®); + this->read_reg = *((u8 *)®); + this->read_flag = 1; + } else if (sscanf(buf, "%x,%x", ®, &val) == 2) { + LOG_DBG("%s,reg = 0x%02x, val = 0x%02x\n", + __func__, *(u8 *)®, *(u8 *)&val); + write_register(this, *((u8 *)®), *((u8 *)&val)); + } + + return count; +} + + +static CLASS_ATTR_RW(reg); + +static struct class capsense_class = { + .name = "capsense", + .owner = THIS_MODULE, +}; + +static void ps_notify_callback_work(struct work_struct *work) +{ + pabovXX_t this = container_of(work, abovXX_t, ps_notify_work); + pabov_t pDevice = NULL; + struct input_dev *input_ch0 = NULL; + struct input_dev *input_ch1 = NULL; + struct input_dev *input_ch2 = NULL; + struct input_dev *input_ch3 = NULL; + int ret = 0; + + pDevice = this->pDevice; + input_ch0 = pDevice->pbuttonInformation->input_ch0; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) + input_ch1 = pDevice->pbuttonInformation->input_ch1; + else if (abov_channel_number == 3) { + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + } else if (abov_channel_number == 4) { + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + input_ch3 = pDevice->pbuttonInformation->input_ch3; + } + + LOG_INFO("Usb insert,going to force calibrate\n"); + ret = write_register(this, ABOV_RECALI_REG, 0x01); + if (ret < 0) + LOG_ERR(" Usb insert,calibrate cap sensor failed\n"); + + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + } else if (abov_channel_number == 3) { + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + } else if (abov_channel_number == 4) { + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + input_report_abs(input_ch3, ABS_DISTANCE, 0); + input_sync(input_ch3); + } +} + +static int ps_get_state(struct power_supply *psy, bool *present) +{ + union power_supply_propval pval = { 0 }; + int retval; + + retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, + &pval); + if (retval) { + LOG_ERR("%s psy get property failed\n", psy->desc->name); + return retval; + } + *present = (pval.intval) ? true : false; + LOG_INFO("%s is %s\n", psy->desc->name, + (*present) ? "present" : "not present"); + return 0; +} + +static int ps_notify_callback(struct notifier_block *self, + unsigned long event, void *p) +{ + pabovXX_t this = container_of(self, abovXX_t, ps_notif); + struct power_supply *psy = p; + bool present; + int retval; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0) + if (event == PSY_EVENT_PROP_CHANGED +#else + if ((event == PSY_EVENT_PROP_ADDED || event == PSY_EVENT_PROP_CHANGED) +#endif + && psy && psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, "usb", sizeof("usb"))){ + LOG_INFO("ps notification: event = %lu\n", event); + retval = ps_get_state(psy, &present); + if (retval) { + LOG_ERR("psy get property failed\n"); + return retval; + } + + if (event == PSY_EVENT_PROP_CHANGED) { + if (this->ps_is_present == present) { + LOG_INFO("ps present state not change\n"); + return 0; + } + } + this->ps_is_present = present; + schedule_work(&this->ps_notify_work); + } + + return 0; +} + + +static int _i2c_adapter_block_write(struct i2c_client *client, u8 *data, u8 len) +{ + u8 buffer[C_I2C_FIFO_SIZE]; + u8 left = len; + u8 offset = 0; + u8 retry = 0; + + struct i2c_msg msg = { + .addr = client->addr & I2C_MASK_FLAG, + .flags = 0, + .buf = buffer, + }; + + if (data == NULL || len < 1) { + LOG_ERR("Invalid : data is null or len=%d\n", len); + return -EINVAL; + } + + while (left > 0) { + retry = 0; + if (left >= C_I2C_FIFO_SIZE) { + msg.buf = &data[offset]; + msg.len = C_I2C_FIFO_SIZE; + left -= C_I2C_FIFO_SIZE; + offset += C_I2C_FIFO_SIZE; + } else { + msg.buf = &data[offset]; + msg.len = left; + left = 0; + } + + while (i2c_transfer(client->adapter, &msg, 1) != 1) { + retry++; + if (retry > 10) { + LOG_ERR("OUT : fail - addr:%#x len:%d \n", client->addr, msg.len); + return -EIO; + } + } + } + return 0; +} + +static int i2c_adapter_block_write_nodatalog(struct i2c_client *client, u8 *data, u8 len) +{ + return _i2c_adapter_block_write(client, data, len); +} + +static int abov_tk_check_busy(struct i2c_client *client) +{ + int ret, count = 0; + unsigned char val = 0x00; + + do { + ret = i2c_master_recv(client, &val, sizeof(val)); + if (val & 0x01) { + count++; + if (count > 1000) { + LOG_INFO("%s: val = 0x%x\r\n", __func__, val); + return ret; + } + } else { + break; + } + } while (1); + + return ret; +} + +static int abov_tk_fw_write(struct i2c_client *client, unsigned char *addrH, + unsigned char *addrL, unsigned char *val) +{ + int length = 36, ret = 0; + unsigned char buf[40] = {0, }; + + buf[0] = 0xAC; + buf[1] = 0x7A; + memcpy(&buf[2], addrH, 1); + memcpy(&buf[3], addrL, 1); + memcpy(&buf[4], val, 32); + ret = i2c_adapter_block_write_nodatalog(client, buf, length); + if (ret < 0) { + LOG_ERR("Firmware write fail ...\n"); + return ret; + } + + SLEEP(3); + abov_tk_check_busy(client); + + return 0; +} + +static int abov_tk_reset_for_bootmode(struct i2c_client *client) +{ + int ret, retry_count = 10; + unsigned char buf[16] = {0, }; + +retry: + buf[0] = 0xF0; + buf[1] = 0xAA; + ret = i2c_master_send(client, buf, 2); + if (ret < 0) { + LOG_DBG("write fail(retry:%d)\n", retry_count); + if (retry_count-- > 0) { + goto retry; + } + return -EIO; + } else { + LOG_INFO("success reset & boot mode\n"); + return 0; + } +} + +static int abov_tk_fw_mode_enter(struct i2c_client *client) +{ + int ret = 0; + unsigned char buf[40] = {0, }; + pabov_platform_data_t pplatData = client->dev.platform_data; + + buf[0] = 0xAC; + buf[1] = 0x5B; + ret = i2c_master_send(client, buf, 2); + if (ret != 2) { + LOG_ERR("SEND : fail - addr:0x%02x data:0x%02x 0x%02x... ret:%d\n", client->addr, buf[0], buf[1], ret); + return -EIO; + } + LOG_INFO("SEND : succ - addr:0x%02x data:0x%02x 0x%02x... ret:%d\n", client->addr, buf[0], buf[1], ret); + SLEEP(5); + + ret = i2c_master_recv(client, buf, 1); + if (buf[0] != pplatData->fw_mode_ret) { + LOG_ERR("Enter fw mode fail,device id:0x%02x\n", buf[0]); + return -EIO; + } + + LOG_INFO("Enter fw mode success ... device id:0x%02x\n", buf[0]); + + return 0; +} + +static int abov_tk_fw_mode_exit(struct i2c_client *client) +{ + int ret = 0; + unsigned char buf[40] = {0, }; + + buf[0] = 0xAC; + buf[1] = 0xE1; + ret = i2c_master_send(client, buf, 2); + if (ret != 2) { + LOG_ERR("SEND : fail - addr:0x%02x data:0x%02x 0x%02x ... ret:%d\n", client->addr, buf[0], buf[1], ret); + return -EIO; + } + LOG_INFO("SEND : succ - addr:0x%02x data:0x%02x 0x%02x ... ret:%d\n", client->addr, buf[0], buf[1], ret); + + return 0; +} + +static int abov_tk_flash_erase(struct i2c_client *client) +{ + int ret = 0; + unsigned char buf[16] = {0, }; + + buf[0] = 0xAC; + buf[1] = 0x2E; + + ret = i2c_master_send(client, buf, 2); + if (ret != 2) { + LOG_ERR("SEND : fail - addr:0x%02x data:0x%02x 0x%02x ... ret:%d\n", client->addr, buf[0], buf[1], ret); + return -EIO; + } + + LOG_INFO("SEND : succ - addr:0x%02x data:0x%02x 0x%02x ... ret:%d\n", client->addr, buf[0], buf[1], ret); + + return 0; +} + +static int abov_tk_i2c_read_checksum(struct i2c_client *client) +{ + unsigned char checksum[6] = {0, }; + unsigned char buf[16] = {0, }; + int ret; + + checksum_h = 0; + checksum_l = 0; + + buf[0] = 0xAC; + buf[1] = 0x9E; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = checksum_h_bin; + buf[5] = checksum_l_bin; + ret = i2c_master_send(client, buf, 6); + + if (ret != 6) { + LOG_ERR("SEND : fail - addr:0x%02x len:%d ... ret:%d\n", client->addr, 6, ret); + return -EIO; + } + SLEEP(5); + + buf[0] = 0x00; + ret = i2c_master_send(client, buf, 1); + if (ret != 1) { + LOG_ERR("SEND : fail - addr:0x%02x data:0x%02x ... ret:%d\n", client->addr, buf[0], ret); + return -EIO; + } + SLEEP(5); + + ret = i2c_master_recv(client, checksum, 6); + if (ret < 0) { + LOG_ERR("Read checksum fail ... \n"); + return -EIO; + } + + checksum_h = checksum[4]; + checksum_l = checksum[5]; + + return 0; +} + +static int _abov_fw_update(struct i2c_client *client, const u8 *image, u32 size) +{ + int ret, ii = 0; + int count; + unsigned short address; + unsigned char addrH, addrL; + unsigned char data[32] = {0, }; + + LOG_INFO("%s: call in\r\n", __func__); + + if (abov_tk_reset_for_bootmode(client) < 0) { + LOG_ERR("don't reset(enter boot mode)!"); + return -EIO; + } + + SLEEP(45); + + for (ii = 0; ii < 10; ii++) { + if (abov_tk_fw_mode_enter(client) < 0) { + LOG_ERR("don't enter the download mode! %d", ii); + SLEEP(40); + continue; + } + break; + } + + if (10 <= ii) { + return -EAGAIN; + } + + if (abov_tk_flash_erase(client) < 0) { + LOG_ERR("don't erase flash data!"); + return -EIO; + } + + SLEEP(1400); + + address = 0x800; + count = size / 32; + + for (ii = 0; ii < count; ii++) { + /* first 32byte is header */ + addrH = (unsigned char)((address >> 8) & 0xFF); + addrL = (unsigned char)(address & 0xFF); + memcpy(data, &image[ii * 32], 32); + ret = abov_tk_fw_write(client, &addrH, &addrL, data); + if (ret < 0) { + LOG_ERR("fw_write.. ii = 0x%x err\r\n", ii); + return ret; + } + + address += 0x20; + memset(data, 0, 32); + } + + ret = abov_tk_i2c_read_checksum(client); + ret = abov_tk_fw_mode_exit(client); + if ((checksum_h == checksum_h_bin) && (checksum_l == checksum_l_bin)) { + LOG_INFO("Firmware update success. checksum_h=0x%02x,checksum_h_bin=0x%02x,checksum_l=0x%02x,checksum_l_bin=0x%02x\n", + checksum_h, checksum_h_bin, checksum_l, checksum_l_bin); + } else { + LOG_ERR("Firmware update fail. checksum_h=0x%02x,checksum_h_bin=0x%02x,checksum_l=0x%02x,checksum_l_bin=0x%02x\n", + checksum_h, checksum_h_bin, checksum_l, checksum_l_bin); + ret = -1; + } + SLEEP(100); + + return ret; +} + +static int abov_fw_update(bool force) +{ + int update_loop; + pabovXX_t this = abov_sar_ptr; + struct i2c_client *client = this->bus; + int rc; + bool fw_upgrade = false; + u8 fw_version = 0, fw_file_version = 0; + u8 fw_modelno = 0, fw_file_modeno = 0; + const struct firmware *fw = NULL; + char fw_name[32] = {0}; + + strcpy(fw_name, this->board->fw_name); + strlcat(fw_name, ".BIN", NAME_MAX); + rc = request_firmware(&fw, fw_name, this->pdev); + if (rc < 0) { + read_register(this, ABOV_VERSION_REG, &fw_version); + read_register(this, ABOV_MODELNO_REG, &fw_modelno); + LOG_ERR("Request firmware failed - %s (%d),current fw info inside IC:Version=0x%02x,ModelNo=0x%02x\n", + this->board->fw_name, rc, fw_version, fw_modelno); + return rc; + } + + if (force == false) { + read_register(this, ABOV_VERSION_REG, &fw_version); + read_register(this, ABOV_MODELNO_REG, &fw_modelno); + LOG_INFO("Version in sensor is:0x%02x ,ModelNo in sensor is:0x%02x\n" ,fw_version ,fw_modelno); + } + + fw_file_modeno = fw->data[1]; + fw_file_version = fw->data[5]; + checksum_h_bin = fw->data[8]; + checksum_l_bin = fw->data[9]; + LOG_INFO("Version in file is:0x%02x ,ModelNo in file is:0x%02x\n" ,fw_file_version ,fw_file_version); + + if ((force) || (fw_version < fw_file_version) || (fw_modelno != fw_file_modeno)) { + LOG_INFO("Firmware is not latest,going to fw upgrade...\n"); + fw_upgrade = true; + } else { + LOG_INFO("Firmware is latest,exiting fw upgrade...\n"); + fw_upgrade = false; + this->fw_dl_status = 0; + rc = -EIO; + goto rel_fw; + } + + if (fw_upgrade) { + this->fw_dl_status = 2; + for (update_loop = 0; update_loop < 10; update_loop++) { + rc = _abov_fw_update(client, &fw->data[32], fw->size-32); + if (rc < 0) + LOG_ERR("retry : %d times!\n", update_loop); + else { + //this funtion under the mutex lock, and the initialize will enable irq, + //if trigger the interrupts, the cap-sensor will no work, so, initialize() no stay here. + //initialize(this); + break; + } + } + if (update_loop >= 10) { + this->fw_dl_status = 1; + rc = -EIO; + } + this->fw_dl_status = 0; + } + +rel_fw: + release_firmware(fw); + return rc; +} + + + +static ssize_t update_fw_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u8 fw_version = 0; + pabovXX_t this = abov_sar_ptr; + + read_register(this, ABOV_VERSION_REG, &fw_version); + + return snprintf(buf, 37, "ABOV CapSensor Firmware Version:0x%02x\n", fw_version); +} + +static ssize_t update_fw_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + unsigned long val; + int rc; + + if (count > 2) + return -EINVAL; + + rc = kstrtoul(buf, 10, &val); + if (rc != 0) + return rc; + + if(this->irq_disabled == 0) { + disable_irq(this->irq); + this->irq_disabled = 1; + } + mutex_lock(&this->mutex); + if (!this->loading_fw && val) { + this->loading_fw = true; + abov_fw_update(false); + this->loading_fw = false; + } + mutex_unlock(&this->mutex); + + if(this->irq_disabled == 1) { + enable_irq(this->irq); + this->irq_disabled = 0; + } + + return count; +} +static CLASS_ATTR_RW(update_fw); + + +static ssize_t force_update_fw_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u8 fw_version = 0; + pabovXX_t this = abov_sar_ptr; + + read_register(this, ABOV_VERSION_REG, &fw_version); + + return snprintf(buf, 37, "ABOV CapSensor Firmware Version:0x%02x\n", fw_version); +} + +static ssize_t force_update_fw_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + unsigned long val; + int rc; + + if (count > 2) + return -EINVAL; + + rc = kstrtoul(buf, 10, &val); + if (rc != 0) + return rc; + + if(this->irq_disabled == 0) { + disable_irq(this->irq); + this->irq_disabled = 1; + } + + mutex_lock(&this->mutex); + if (!this->loading_fw && val) { + this->loading_fw = true; + abov_fw_update(true); + this->loading_fw = false; + } + mutex_unlock(&this->mutex); + + if(this->irq_disabled == 1) { + enable_irq(this->irq); + this->irq_disabled = 0; + } + + return count; +} +static CLASS_ATTR_RW(force_update_fw); + +static void capsense_update_work(struct work_struct *work) +{ + pabovXX_t this = container_of(work, abovXX_t, fw_update_work); + + LOG_DBG("%s: start update firmware\n", __func__); + if(this->irq_disabled == 0) { + disable_irq(this->irq); + this->irq_disabled = 1; + } + mutex_lock(&this->mutex); + this->loading_fw = true; + abov_fw_update(false); + this->loading_fw = false; + mutex_unlock(&this->mutex); + if(this->irq_disabled == 1) { + enable_irq(this->irq); + this->irq_disabled = 0; + } + + if(mEnabled){ + initialize(this); + } + LOG_INFO("%s: update firmware end\n", __func__); +} + +static void capsense_fore_update_work(struct work_struct *work) +{ + pabovXX_t this = container_of(work, abovXX_t, fw_update_work); + + LOG_DBG("%s: start force update firmware\n", __func__); + if(this->irq_disabled == 0) { + disable_irq(this->irq); + this->irq_disabled = 1; + } + mutex_lock(&this->mutex); + this->loading_fw = true; + abov_fw_update(true); + this->loading_fw = false; + mutex_unlock(&this->mutex); + if(this->irq_disabled == 1) { + enable_irq(this->irq); + this->irq_disabled = 0; + } + + if(mEnabled){ + initialize(this); + } + LOG_INFO("%s: force update firmware end\n", __func__); +} + + +/** + * detect if abov exist or not + * return 1 if chip exist else return 0 + */ +static int abov_detect(struct i2c_client *client) +{ + s32 returnValue = 0, i; + u8 address = ABOV_VENDOR_ID_REG; + u8 value = 0xAB; + + if (client) { + for (i = 0; i < 10; i++) { + returnValue = i2c_smbus_read_byte_data(client, address); + LOG_DBG("abov read_register for %d time Addr: 0x%02x Return: 0x%02x\n", + i, address, returnValue); + if (returnValue >= 0) { + if (value == returnValue) { + LOG_INFO("abov detect success!\n"); + return 1; + } + } + msleep(10); + } + LOG_INFO("abov boot detect start\n"); + for (i = 0; i < 3; i++) { + if(abov_tk_fw_mode_enter(client) == 0) { + LOG_INFO("abov boot detect success!\n"); + return 2; + } + } + } + LOG_INFO("abov detect failed!!!\n"); + return 0; +} + + +/** + * fn static int abov_probe(struct i2c_client *client, const struct i2c_device_id *id) + * brief Probe function + * param client pointer to i2c_client + * param id pointer to i2c_device_id + * return Whether probe was successful + */ +static int abov_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + pabovXX_t this = 0; + pabov_t pDevice = 0; + pabov_platform_data_t pplatData = 0; + bool isForceUpdate = false; + int ret; + struct input_dev *input_ch0 = NULL; + struct input_dev *input_ch1 = NULL; + struct input_dev *input_ch2 = NULL; + struct input_dev *input_ch3 = NULL; + struct power_supply *psy = NULL; + + LOG_INFO("abov_probe() start\n"); + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -EIO; + + pplatData = kzalloc(sizeof(struct abov_platform_data), GFP_KERNEL); + if (!pplatData) { + LOG_ERR("platform data is required!\n"); + return -EINVAL; + } + abov_platform_data_of_init(client, pplatData); + client->dev.platform_data = pplatData; + + pplatData->cap_vdd = regulator_get(&client->dev, "cap_vdd"); + if (IS_ERR(pplatData->cap_vdd)) { + if (PTR_ERR(pplatData->cap_vdd) == -EPROBE_DEFER) { + ret = PTR_ERR(pplatData->cap_vdd); + goto err_vdd_defer; + } + LOG_ERR("%s: Failed to get regulator\n", __func__); + } else { + ret = regulator_enable(pplatData->cap_vdd); + + if (ret) { + regulator_put(pplatData->cap_vdd); + LOG_ERR("%s: Error %d enable regulator\n", + __func__, ret); + goto err_vdd_defer; + } + pplatData->cap_vdd_en = true; + LOG_INFO("cap_vdd regulator is %s\n", + regulator_is_enabled(pplatData->cap_vdd) ? + "on" : "off"); + } + + pplatData->cap_svdd = regulator_get(&client->dev, "cap_svdd"); + if (IS_ERR(pplatData->cap_svdd)) { + if (PTR_ERR(pplatData->cap_svdd) == -EPROBE_DEFER) { + ret = PTR_ERR(pplatData->cap_svdd); + goto err_svdd_error; + } + LOG_ERR("%s: Failed to get regulator\n", __func__); + } else { + ret = regulator_enable(pplatData->cap_svdd); + + if (ret) { + regulator_put(pplatData->cap_svdd); + LOG_ERR("%s: Error %d enable regulator\n", + __func__, ret); + goto err_svdd_error; + } + pplatData->cap_svdd_en = true; + LOG_INFO("cap_svdd regulator is %s\n", + regulator_is_enabled(pplatData->cap_svdd) ? + "on" : "off"); + } + + /* detect if abov exist or not */ + ret = abov_detect(client); + if (ret == 0) { + return -ENODEV; + } + if (ret == 2) { + isForceUpdate = true; + } + + this = kzalloc(sizeof(abovXX_t), GFP_KERNEL); /* create memory for main struct */ + LOG_DBG("\t Initialized Main Memory: 0x%p\n", this); + + if (this) { + /* In case we need to reinitialize data + * (e.q. if suspend reset device) */ + this->init = initialize; + /* pointer to function from platform data to get pendown + * (1->NIRQ=0, 0->NIRQ=1) */ + this->get_nirq_low = pplatData->get_is_nirq_low; + /* save irq in case we need to reference it */ + this->irq = gpio_to_irq(client->irq); + /* do we need to create an irq timer after interrupt ? */ + this->useIrqTimer = 0; + this->board = pplatData; + /* Setup function to call on corresponding reg irq source bit */ + if (MAX_NUM_STATUS_BITS >= 8) { + this->statusFunc[0] = 0; /* TXEN_STAT */ + this->statusFunc[1] = 0; /* UNUSED */ + this->statusFunc[2] = 0; /* UNUSED */ + this->statusFunc[3] = 0;/*read_rawData; CONV_STAT */ + this->statusFunc[4] = 0; /* COMP_STAT */ + this->statusFunc[5] = touchProcess; /* RELEASE_STAT */ + this->statusFunc[6] = touchProcess; /* TOUCH_STAT */ + this->statusFunc[7] = 0; /* RESET_STAT */ + } + + /* setup i2c communication */ + this->bus = client; + i2c_set_clientdata(client, this); + + /* record device struct */ + this->pdev = &client->dev; + + /* create memory for device specific struct */ + this->pDevice = pDevice = kzalloc(sizeof(abov_t), GFP_KERNEL); + LOG_DBG("\t Initialized Device Specific Memory: 0x%p\n", + pDevice); + abov_sar_ptr = this; + if (pDevice) { + /* for accessing items in user data (e.g. calibrate) */ + ret = sysfs_create_group(&client->dev.kobj, &abov_attr_group); + + + /* Check if we hava a platform initialization function to call*/ + if (pplatData->init_platform_hw) + pplatData->init_platform_hw(); + + /* Add Pointer to main platform data struct */ + pDevice->hw = pplatData; + + /* Initialize the button information initialized with keycodes */ + pDevice->pbuttonInformation = pplatData->pbuttonInformation; + + /* Create the input device */ + input_ch0 = input_allocate_device(); + if (!input_ch0) + return -ENOMEM; + + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch0->evbit); + input_set_abs_params(input_ch0, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch0 = input_ch0; + input_ch0->name = this->board->cap_ch0_name; + if (input_register_device(input_ch0)) { + LOG_ERR("add ch0 cap touch unsuccess\n"); + return -ENOMEM; + } + /* Create the input device */ + input_ch1 = input_allocate_device(); + if (!input_ch1) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch1->evbit); + input_set_abs_params(input_ch1, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch1 = input_ch1; + /* save the input pointer and finish initialization */ + input_ch1->name = this->board->cap_ch1_name; + if (input_register_device(input_ch1)) { + LOG_INFO("add ch1 cap touch unsuccess\n"); + return -ENOMEM; + } + if (abov_channel_number == 3) { + /* Create the input device */ + input_ch2 = input_allocate_device(); + if (!input_ch2) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch2->evbit); + input_set_abs_params(input_ch2, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch2 = input_ch2; + /* save the input pointer and finish initialization */ + input_ch2->name = this->board->cap_ch2_name; + if (input_register_device(input_ch2)) { + LOG_ERR("add ch2t cap touch unsuccess\n"); + return -ENOMEM; + } + } else if (abov_channel_number == 4) { + /* Create the input device */ + input_ch2 = input_allocate_device(); + if (!input_ch2) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch2->evbit); + input_set_abs_params(input_ch2, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch2 = input_ch2; + /* save the input pointer and finish initialization */ + input_ch2->name = this->board->cap_ch2_name; + if (input_register_device(input_ch2)) { + LOG_ERR("add ch2t cap touch unsuccess\n"); + return -ENOMEM; + } + input_ch3 = input_allocate_device(); + if (!input_ch3) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch3->evbit); + input_set_abs_params(input_ch3, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch3 = input_ch3; + /* save the input pointer and finish initialization */ + input_ch3->name = this->board->cap_ch3_name; + if (input_register_device(input_ch3)) { + LOG_ERR("add ch3t cap touch unsuccess\n"); + return -ENOMEM; + } + } + } + + ret = class_register(&capsense_class); + if (ret < 0) { + LOG_ERR("Create fsys class failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_reset); + if (ret < 0) { + LOG_ERR("Create reset file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_int_state); + if (ret < 0) { + LOG_DBG("Create int_state file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_fw_download_status); + if (ret < 0) { + LOG_DBG("Create fw_download_status file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_enable); + if (ret < 0) { + LOG_ERR("Create enable file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_reg); + if (ret < 0) { + LOG_DBG("Create reg file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_update_fw); + if (ret < 0) { + LOG_ERR("Create update_fw file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_force_update_fw); + if (ret < 0) { + LOG_ERR("Create update_fw file failed (%d)\n", ret); + return ret; + } + /*restore sys/class/capsense label*/ + kobject_uevent(&capsense_class.p->subsys.kobj, KOBJ_CHANGE); + +#ifdef USE_SENSORS_CLASS + sensors_capsensor_ch0_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch0_cdev.sensors_poll_delay = NULL; + sensors_capsensor_ch0_cdev.name = this->board->cap_ch0_name; + ret = sensors_classdev_register(&input_ch0->dev, &sensors_capsensor_ch0_cdev); + if (ret < 0) + LOG_ERR("create ch0 cap sensor_class file failed (%d)\n", ret); + + sensors_capsensor_ch1_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch1_cdev.sensors_poll_delay = NULL;; + sensors_capsensor_ch1_cdev.name = this->board->cap_ch1_name; + ret = sensors_classdev_register(&input_ch1->dev, &sensors_capsensor_ch1_cdev); + if (ret < 0) + LOG_ERR("create ch1 cap sensor_class file failed (%d)\n", ret); + + if (abov_channel_number == 3) { + sensors_capsensor_ch2_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch2_cdev.sensors_poll_delay = NULL; + sensors_capsensor_ch2_cdev.name = this->board->cap_ch2_name; + ret = sensors_classdev_register(&input_ch2->dev, &sensors_capsensor_ch2_cdev); + if (ret < 0) + LOG_ERR("create ch2 cap sensor_class file failed (%d)\n", ret); + } else if (abov_channel_number == 4) { + sensors_capsensor_ch2_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch2_cdev.sensors_poll_delay = NULL; + sensors_capsensor_ch2_cdev.name = this->board->cap_ch2_name; + ret = sensors_classdev_register(&input_ch2->dev, &sensors_capsensor_ch2_cdev); + if (ret < 0) + LOG_ERR("create ch2 cap sensor_class file failed (%d)\n", ret); + + sensors_capsensor_ch3_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch3_cdev.sensors_poll_delay = NULL; + sensors_capsensor_ch3_cdev.name = this->board->cap_ch3_name; + ret = sensors_classdev_register(&input_ch3->dev, &sensors_capsensor_ch3_cdev); + if (ret < 0) + LOG_ERR("create ch3 cap sensor_class file failed (%d)\n", ret); + } +#endif + + abovXX_sar_init(this); + + write_register(this, ABOV_CTRL_MODE_REG, 0x02); + mEnabled = 0; + + INIT_WORK(&this->ps_notify_work, ps_notify_callback_work); + this->ps_notif.notifier_call = ps_notify_callback; + ret = power_supply_reg_notifier(&this->ps_notif); + if (ret) { + LOG_ERR( + "Unable to register ps_notifier: %d\n", ret); + goto free_ps_notifier; + } + + psy = power_supply_get_by_name("usb"); + if (psy) { + ret = ps_get_state(psy, &this->ps_is_present); + if (ret) { + LOG_ERR( + "psy get property failed rc=%d\n", + ret); + goto free_ps_notifier; + } + } + + this->loading_fw = false; + this->fw_dl_status = 1; + if (isForceUpdate == true) { + INIT_WORK(&this->fw_update_work, capsense_fore_update_work); + } else { + INIT_WORK(&this->fw_update_work, capsense_update_work); + } + schedule_work(&this->fw_update_work); + + + LOG_INFO("abov_probe() end\n"); + return 0; + } + return -ENOMEM; + +free_ps_notifier: + LOG_ERR("%s free ps notifier:.\n", __func__); + power_supply_unreg_notifier(&this->ps_notif); + regulator_disable(pplatData->cap_svdd); + regulator_put(pplatData->cap_svdd); + +err_svdd_error: + LOG_ERR("%s svdd defer.\n", __func__); + regulator_disable(pplatData->cap_vdd); + regulator_put(pplatData->cap_vdd); + +err_vdd_defer: + LOG_ERR("%s input free device.\n", __func__); + input_free_device(input_ch0); + input_free_device(input_ch1); + if (abov_channel_number == 3) + input_free_device(input_ch2); + else if (abov_channel_number == 4) { + input_free_device(input_ch2); + input_free_device(input_ch3); + } + + return ret; +} + +/** + * fn static int abov_remove(struct i2c_client *client) + * brief Called when device is to be removed + * param client Pointer to i2c_client struct + * return Value from abovXX_sar_remove() + */ +static int abov_remove(struct i2c_client *client) +{ + pabov_platform_data_t pplatData = 0; + pabov_t pDevice = 0; + pabovXX_t this = i2c_get_clientdata(client); + + pDevice = this->pDevice; + if (this && pDevice) { +#ifdef USE_SENSORS_CLASS + sensors_classdev_unregister(&sensors_capsensor_ch0_cdev); + sensors_classdev_unregister(&sensors_capsensor_ch1_cdev); + if (abov_channel_number == 3) + sensors_classdev_unregister(&sensors_capsensor_ch2_cdev); + else if (abov_channel_number == 4) { + sensors_classdev_unregister(&sensors_capsensor_ch2_cdev); + sensors_classdev_unregister(&sensors_capsensor_ch3_cdev); + } +#endif + input_unregister_device(pDevice->pbuttonInformation->input_ch0); + input_unregister_device(pDevice->pbuttonInformation->input_ch1); + if (abov_channel_number == 3) + input_unregister_device(pDevice->pbuttonInformation->input_ch2); + else if (abov_channel_number == 4) { + input_unregister_device(pDevice->pbuttonInformation->input_ch2); + input_unregister_device(pDevice->pbuttonInformation->input_ch3); + } + + if (this->board->cap_svdd_en) { + regulator_disable(this->board->cap_svdd); + regulator_put(this->board->cap_svdd); + } + + if (this->board->cap_vdd_en) { + regulator_disable(this->board->cap_vdd); + regulator_put(this->board->cap_vdd); + } + + sysfs_remove_group(&client->dev.kobj, &abov_attr_group); + pplatData = client->dev.platform_data; + if (pplatData && pplatData->exit_platform_hw) + pplatData->exit_platform_hw(); + kfree(this->pDevice); + } + return abovXX_sar_remove(this); +} + +static int abov_suspend(struct device *dev) +{ + pabovXX_t this = dev_get_drvdata(dev); + + abovXX_suspend(this); + return 0; +} + +static int abov_resume(struct device *dev) +{ + pabovXX_t this = dev_get_drvdata(dev); + + abovXX_resume(this); + return 0; +} +static SIMPLE_DEV_PM_OPS(abov_pm_ops, abov_suspend, abov_resume); + + +#ifdef CONFIG_OF +static const struct of_device_id abov_match_tbl[] = { + { .compatible = "abov,abov_sar_mmi_overlay" }, + { }, +}; +MODULE_DEVICE_TABLE(of, abov_match_tbl); +#endif + +static struct i2c_device_id abov_idtable[] = { + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, abov_idtable); + +static struct i2c_driver abov_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(abov_match_tbl), + .pm = &abov_pm_ops, + }, + .id_table = abov_idtable, + .probe = abov_probe, + .remove = abov_remove, +}; +static int __init abov_init(void) +{ + return i2c_add_driver(&abov_driver); +} +static void __exit abov_exit(void) +{ + i2c_del_driver(&abov_driver); +} + +module_init(abov_init); +module_exit(abov_exit); + +MODULE_AUTHOR("ABOV Corp."); +MODULE_DESCRIPTION("ABOV Capacitive Touch Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); + +#ifdef USE_THREADED_IRQ +static void abovXX_process_interrupt(pabovXX_t this, u8 nirqlow) +{ + if (!this) { + pr_err("abovXX_worker_func, NULL abovXX_t\n"); + return; + } + /* since we are not in an interrupt don't need to disable irq. */ + this->statusFunc[6](this); + if (unlikely(this->useIrqTimer && nirqlow)) { + /* In case we need to send a timer for example on a touchscreen + * checking penup, perform this here + */ + cancel_delayed_work(&this->dworker); + schedule_delayed_work(&this->dworker, msecs_to_jiffies(this->irqTimeout)); + LOG_INFO("Schedule Irq timer"); + } +} + + +static void abovXX_worker_func(struct work_struct *work) +{ + pabovXX_t this = 0; + + if (work) { + this = container_of(work, abovXX_t, dworker.work); + if (!this) { + LOG_ERR("abovXX_worker_func, NULL abovXX_t\n"); + return; + } + if ((!this->get_nirq_low) || (!this->get_nirq_low(this->board->irq_gpio))) { + /* only run if nirq is high */ + abovXX_process_interrupt(this, 0); + } + } else { + LOG_ERR("abovXX_worker_func, NULL work_struct\n"); + } +} +static irqreturn_t abovXX_interrupt_thread(int irq, void *data) +{ + pabovXX_t this = 0; + this = data; + + mutex_lock(&this->mutex); + LOG_DBG("abovXX_irq\n"); + this->int_state = 1; + if ((!this->get_nirq_low) || this->get_nirq_low(this->board->irq_gpio)) + abovXX_process_interrupt(this, 1); + else + LOG_ERR("abovXX_irq - nirq read high\n"); + mutex_unlock(&this->mutex); + return IRQ_HANDLED; +} +#else +static void abovXX_schedule_work(pabovXX_t this, unsigned long delay) +{ + unsigned long flags; + + if (this) { + LOG_DBG("abovXX_schedule_work()\n"); + spin_lock_irqsave(&this->lock, flags); + /* Stop any pending penup queues */ + cancel_delayed_work(&this->dworker); + /* + *after waiting for a delay, this put the job in the kernel-global + workqueue. so no need to create new thread in work queue. + */ + schedule_delayed_work(&this->dworker, delay); + spin_unlock_irqrestore(&this->lock, flags); + } else + LOG_ERR("abovXX_schedule_work, NULL pabovXX_t\n"); +} + +static irqreturn_t abovXX_irq(int irq, void *pvoid) +{ + pabovXX_t this = 0; + if (pvoid) { + this = (pabovXX_t)pvoid; + LOG_DBG("abovXX_irq\n"); + this->int_state = 1; + if ((!this->get_nirq_low) || this->get_nirq_low(this->board->irq_gpio)) { + LOG_DBG("abovXX_irq - Schedule Work\n"); + abovXX_schedule_work(this, 0); + } else + LOG_ERR("abovXX_irq - nirq read high\n"); + } else + LOG_ERR("abovXX_irq, NULL pvoid\n"); + return IRQ_HANDLED; +} + +static void abovXX_worker_func(struct work_struct *work) +{ + pabovXX_t this = 0; + int counter = 0; + u8 nirqLow = 0; + + if (work) { + this = container_of(work, abovXX_t, dworker.work); + + if (!this) { + LOG_ERR("abovXX_worker_func, NULL abovXX_t\n"); + return; + } + if (unlikely(this->useIrqTimer)) { + if ((!this->get_nirq_low) || this->get_nirq_low(this->board->irq_gpio)) + nirqLow = 1; + } + /* since we are not in an interrupt don't need to disable irq. */ + counter = -1; + while ((++counter) < MAX_NUM_STATUS_BITS) { /* counter start from MSB */ + LOG_DBG("Looping Counter %d\n", counter); + if (((status >> counter) & 0x01) && (this->statusFunc[counter])) { + LOG_DBG("Function Pointer Found. Calling\n"); + this->statusFunc[counter](this); + } + } + if (unlikely(this->useIrqTimer && nirqLow)) { + /* Early models and if RATE=0 for newer models require a penup timer */ + /* Queue up the function again for checking on penup */ + abovXX_schedule_work(this, msecs_to_jiffies(this->irqTimeout)); + } + } else { + LOG_ERR("abovXX_worker_func, NULL work_struct\n"); + } +} +#endif + +void abovXX_suspend(pabovXX_t this) +{ + if (this) { + LOG_DBG("ABOV suspend[%d]: disable irq!\n",mEnabled); + disable_irq(this->irq); + /* if upper layer don't disable capsensor, */ + /* we should let it enter sleep in suspend. */ + if (mEnabled) + write_register(this, ABOV_CTRL_MODE_REG, 0x01); + } +} +void abovXX_resume(pabovXX_t this) +{ + if (this) { + LOG_DBG("ABOV resume[%d]: enable irq!\n",mEnabled); + /* we should let capsensor enter active in resume*/ + if (mEnabled) + write_register(this, ABOV_CTRL_MODE_REG, 0x00); + enable_irq(this->irq); + } +} + +int abovXX_sar_init(pabovXX_t this) +{ + int err = 0; + + if (this && this->pDevice) { +#ifdef USE_THREADED_IRQ + + /* initialize worker function */ + INIT_DELAYED_WORK(&this->dworker, abovXX_worker_func); + + + /* initialize mutex */ + mutex_init(&this->mutex); + /* initailize interrupt reporting */ + this->irq_disabled = 0; + err = request_threaded_irq(this->irq, NULL, abovXX_interrupt_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, this->pdev->driver->name, + this); +#else + /* initialize spin lock */ + spin_lock_init(&this->lock); + + /* initialize worker function */ + INIT_DELAYED_WORK(&this->dworker, abovXX_worker_func); + + /* initailize interrupt reporting */ + this->irq_disabled = 0; + err = request_irq(this->irq, abovXX_irq, IRQF_TRIGGER_FALLING, + this->pdev->driver->name, this); +#endif + if (err) { + LOG_ERR("irq %d busy?\n", this->irq); + return err; + } + this->int_state = 0; +#ifdef USE_THREADED_IRQ + LOG_INFO("registered with threaded irq (%d)\n", this->irq); +#else + LOG_INFO("registered with irq (%d)\n", this->irq); +#endif + /* call init function pointer (this should initialize all registers */ + if (this->init) + return this->init(this); + LOG_ERR("No init function!!!!\n"); + } + return -ENOMEM; +} + +int abovXX_sar_remove(pabovXX_t this) +{ + if (this) { + cancel_delayed_work_sync(&this->dworker); /* Cancel the Worker Func */ + free_irq(this->irq, this); + kfree(this); + return 0; + } + return -ENOMEM; +} diff --git a/drivers/misc/abov_mmi_overlay_7ch/Android.mk b/drivers/misc/abov_mmi_overlay_7ch/Android.mk new file mode 100644 index 000000000000..eacfda5cbf77 --- /dev/null +++ b/drivers/misc/abov_mmi_overlay_7ch/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := abov_sar_mmi_overlay_7ch.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/abov_mmi_overlay_7ch/Kbuild b/drivers/misc/abov_mmi_overlay_7ch/Kbuild new file mode 100644 index 000000000000..69fa395e6167 --- /dev/null +++ b/drivers/misc/abov_mmi_overlay_7ch/Kbuild @@ -0,0 +1,14 @@ +# add -Wall to try to catch everything we can. +KERNEL_DIR_PATH = $(shell find $(TOP)/kernel/ -name "msm-*" | head -1 | grep -i "kernel/msm-*") + +KERNEL_VERSION_NAME = $(shell basename $(KERNEL_DIR_PATH)) +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +ifneq (,$(KERNEL_DIR_PATH)) + EXTRA_CFLAGS += -I$(TOP)/kernel/$(KERNEL_VERSION_NAME)/drivers/base +else + EXTRA_CFLAGS += -I$(TOP)/kernel/drivers/base +endif +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../sensors/Module.symvers +obj-m = abov_sar_mmi_overlay_7ch.o diff --git a/drivers/misc/abov_mmi_overlay_7ch/Makefile b/drivers/misc/abov_mmi_overlay_7ch/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/misc/abov_mmi_overlay_7ch/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/misc/abov_mmi_overlay_7ch/abov_sar_mmi_overlay_7ch.c b/drivers/misc/abov_mmi_overlay_7ch/abov_sar_mmi_overlay_7ch.c new file mode 100644 index 000000000000..23b29ce89b32 --- /dev/null +++ b/drivers/misc/abov_mmi_overlay_7ch/abov_sar_mmi_overlay_7ch.c @@ -0,0 +1,3016 @@ +/* + * file abov_sar.c + * brief abov Driver for two channel SAP using + * + * Driver for the ABOV + * Copyright (c) 2015-2016 ABOV Corp + * + * 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. + */ +#define DRIVER_NAME "abov_sar" +#define USE_SENSORS_CLASS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* main struct, interrupt,init,pointers */ + +#include +#include +#include +#include +#include +#include +#include "base.h" + +#define SLEEP(x) mdelay(x) + +#define C_I2C_FIFO_SIZE 8 + +#define I2C_MASK_FLAG (0x00ff) + +static u8 checksum_h; +static u8 checksum_h_bin; +static u8 checksum_l; +static u8 checksum_l_bin; +static u32 abov_channel_number = 0; +static u32 abov_flash_erase_time = 0; + + +#define IDLE 0 +#define ACTIVE 1 +#define S_PROX 1 +#define S_BODY 2 + +#define LOG_TAG "" + +#define LOG_INFO(fmt, args...) pr_info(LOG_TAG "[INFO]" "<%s><%d>"fmt, __func__, __LINE__, ##args) +#define LOG_DBG(fmt, args...) pr_debug(LOG_TAG "[DBG]" "<%s><%d>"fmt, __func__, __LINE__, ##args) +#define LOG_ERR(fmt, args...) pr_err(LOG_TAG "[ERR]" "<%s><%d>"fmt, __func__, __LINE__, ##args) + +static int last_val; +static int mEnabled; +static int programming_done; +pabovXX_t abov_sar_ptr; + +/** + * struct abov + * Specialized struct containing input event data, platform data, and + * last cap state read if needed. + */ +typedef struct abov { + pbuttonInformation_t pbuttonInformation; + pabov_platform_data_t hw; /* specific platform data settings */ +} abov_t, *pabov_t; + +static void ForcetoTouched(pabovXX_t this) +{ + pabov_t pDevice = NULL; + struct input_dev *input_ch0 = NULL; + struct input_dev *input_ch1 = NULL; + struct input_dev *input_ch2 = NULL; + struct input_dev *input_ch3 = NULL; + struct input_dev *input_ch4 = NULL; + struct _buttonInfo *pCurrentButton = NULL; + + pDevice = this->pDevice; + if (this && pDevice) { + LOG_DBG("ForcetoTouched()\n"); + + pCurrentButton = pDevice->pbuttonInformation->buttons; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + input_ch3 = pDevice->pbuttonInformation->input_ch3; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + input_ch3 = pDevice->pbuttonInformation->input_ch3; + input_ch4 = pDevice->pbuttonInformation->input_ch4; + } + + pCurrentButton->state = ACTIVE; + last_val = 1; + if (mEnabled) { + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + input_report_abs(input_ch2, ABS_DISTANCE, 1); + input_sync(input_ch2); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + input_report_abs(input_ch2, ABS_DISTANCE, 1); + input_sync(input_ch2); + input_report_abs(input_ch3, ABS_DISTANCE, 1); + input_sync(input_ch3); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + input_report_abs(input_ch2, ABS_DISTANCE, 1); + input_sync(input_ch2); + input_report_abs(input_ch3, ABS_DISTANCE, 1); + input_sync(input_ch3); + input_report_abs(input_ch4, ABS_DISTANCE, 1); + input_sync(input_ch4); + } + } + LOG_DBG("Leaving ForcetoTouched()\n"); + } +} + +/** + * fn static int write_register(pabovXX_t this, u8 address, u8 value) + * brief Sends a write register to the device + * param this Pointer to main parent struct + * param address 8-bit register address + * param value 8-bit register value to write to address + * return Value from i2c_master_send + */ +static int write_register(pabovXX_t this, u8 address, u8 value) +{ + struct i2c_client *i2c = 0; + char buffer[2]; + int returnValue = 0; + + buffer[0] = address; + buffer[1] = value; + returnValue = -ENOMEM; + if (this && this->bus) { + i2c = this->bus; + + returnValue = i2c_master_send(i2c, buffer, 2); + LOG_DBG("write_register Addr: 0x%x Val: 0x%x Return: %d\n", + address, value, returnValue); + } + if (returnValue < 0) { + ForcetoTouched(this); + LOG_ERR("Write_register-ForcetoTouched()\n"); + } + return returnValue; +} + +/** + * fn static int read_register(pabovXX_t this, u8 address, u8 *value) + * brief Reads a register's value from the device + * param this Pointer to main parent struct + * param address 8-Bit address to read from + * param value Pointer to 8-bit value to save register value to + * return Value from i2c_smbus_read_byte_data if < 0. else 0 + */ +static int read_register(pabovXX_t this, u8 address, u8 *value) +{ + struct i2c_client *i2c = 0; + s32 returnValue = 0; + + if (this && value && this->bus) { + i2c = this->bus; + returnValue = i2c_smbus_read_byte_data(i2c, address); + LOG_DBG("read_register Addr: 0x%x Return: 0x%x\n", + address, returnValue); + } + + if (returnValue >= 0) { + *value = returnValue; + return 0; + } else { + ForcetoTouched(this); + LOG_ERR("read_register-ForcetoTouched()\n"); + return returnValue; + } + return returnValue; +} + + +/** + * brief Perform a manual offset calibration + * param this Pointer to main parent struct + * return Value return value from the write register + */ +static int manual_offset_calibration(pabovXX_t this) +{ + s32 returnValue = 0; + + returnValue = write_register(this, ABOV_RECALI_REG, 0x01); + return returnValue; +} + +/** + * brief sysfs show function for manual calibration which currently just + * returns register value. + */ +static ssize_t manual_offset_calibration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 reg_value = 0; + u16 returnValue = 0; + pabovXX_t this = dev_get_drvdata(dev); + + LOG_DBG("Reading IRQSTAT_REG\n"); + read_register(this, ABOV_IRQSTAT_REG, ®_value); + returnValue = reg_value; + if (abov_channel_number == 5) { + read_register(this, ABOV_IRQSTAT1_REG, ®_value); + returnValue |= (u16)reg_value << 8; + } + return scnprintf(buf, PAGE_SIZE, "%d\n", returnValue); +} + +/* brief sysfs store function for manual calibration */ +static ssize_t manual_offset_calibration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = dev_get_drvdata(dev); + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + if (val) { + LOG_DBG("Performing manual_offset_calibration()\n"); + manual_offset_calibration(this); + } + return count; +} + +static DEVICE_ATTR(calibrate, 0644, manual_offset_calibration_show, + manual_offset_calibration_store); +static struct attribute *abov_attributes[] = { + &dev_attr_calibrate.attr, + NULL, +}; +static struct attribute_group abov_attr_group = { + .attrs = abov_attributes, +}; + + +/** + * brief Initialize I2C config from platform data + * param this Pointer to main parent struct + * + */ +static void hw_init(pabovXX_t this) +{ + pabov_t pDevice = 0; + pabov_platform_data_t pdata = 0; + int i = 0; + /* configure device */ + LOG_DBG("Inside hw_init().\n"); + pDevice = this->pDevice; + pdata = pDevice->hw; + if (this && pDevice && pdata) { + while (i < pdata->i2c_reg_num) { + /* Write all registers/values contained in i2c_reg */ + write_register(this, pdata->pi2c_reg[i].reg, + pdata->pi2c_reg[i].val); + i++; + } + } else { + LOG_ERR("ERROR! platform data 0x%p\n", pDevice->hw); + /* Force to touched if error */ + ForcetoTouched(this); + } + LOG_DBG("Exiting hw_init().\n"); +} + +/** + * fn static int initialize(pabovXX_t this) + * brief Performs all initialization needed to configure the device + * param this Pointer to main parent struct + * return Last used command's return value (negative if error) + */ +static int initialize(pabovXX_t this) +{ + if (this) { + LOG_DBG("Inside initialize().\n"); + /* prepare reset by disabling any irq handling */ + if(this->irq_disabled == 0) { + disable_irq(this->irq); + this->irq_disabled = 1; + } + hw_init(this); + msleep(300); /* make sure everything is running */ + /* re-enable interrupt handling */ + if(this->irq_disabled == 1) { + enable_irq(this->irq); + this->irq_disabled = 0; + } + /* make sure no interrupts are pending since enabling irq will only + * work on next falling edge */ + LOG_DBG("Exiting initialize().\n"); + programming_done = ACTIVE; + return 0; + } + programming_done = IDLE; + return -ENOMEM; +} + +/** + * brief Handle what to do when a touch occurs + * param this Pointer to main parent struct + */ +static void touchProcess(pabovXX_t this) +{ + int counter = 0; + u8 i = 0; + u16 capsense_state = 0; + int numberOfButtons = 0; + pabov_t pDevice = NULL; + struct _buttonInfo *buttons = NULL; + struct input_dev *input_ch0 = NULL; + struct input_dev *input_ch1 = NULL; + struct input_dev *input_ch2 = NULL; + struct input_dev *input_ch3 = NULL; + struct input_dev *input_ch4 = NULL; + struct _buttonInfo *pCurrentButton = NULL; + struct abov_platform_data *board; + + pDevice = this->pDevice; + board = this->board; + if (this && pDevice) { + LOG_DBG("Inside touchProcess()\n"); + read_register(this, ABOV_IRQSTAT_REG, &i); + capsense_state = i; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + read_register(this, ABOV_IRQSTAT1_REG, &i); + capsense_state |= (u16)i << 8; + } + + buttons = pDevice->pbuttonInformation->buttons; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + input_ch3 = pDevice->pbuttonInformation->input_ch3; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + input_ch3 = pDevice->pbuttonInformation->input_ch3; + input_ch4 = pDevice->pbuttonInformation->input_ch4; + } + numberOfButtons = pDevice->pbuttonInformation->buttonSize; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (unlikely((buttons == NULL) || (input_ch0 == NULL) || (input_ch1 == NULL))) { + LOG_ERR("ERROR!! buttons or input NULL!!!\n"); + return; + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + if (unlikely((buttons == NULL) || (input_ch0 == NULL) || (input_ch1 == NULL) || (input_ch2 == NULL))) { + LOG_ERR("ERROR!! buttons or input NULL!!!\n"); + return; + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + if (unlikely((buttons == NULL) || (input_ch0 == NULL) || (input_ch1 == NULL) || (input_ch2 == NULL) || (input_ch3 == NULL))) { + LOG_ERR("ERROR!! buttons or input NULL!!!\n"); + return; + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + if (unlikely((buttons == NULL) || (input_ch0 == NULL) || (input_ch1 == NULL) || (input_ch2 == NULL) || (input_ch3 == NULL) || (input_ch4 == NULL))) { + LOG_ERR("ERROR!! buttons or input NULL!!!\n"); + return; + } + } + + for (counter = 0; counter < numberOfButtons; counter++) { + pCurrentButton = &buttons[counter]; + if (pCurrentButton == NULL) { + LOG_ERR("ERR!current button index: %d NULL!\n", + counter); + return; /* ERRORR!!!! */ + } + switch (pCurrentButton->state) { + case IDLE: /* Button is being in far state! */ + if ((capsense_state & pCurrentButton->mask) == pCurrentButton->mask) { + LOG_DBG("CS %d State=BODY.\n", + counter); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 2); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 2); + input_sync(input_ch1); + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 2); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 2); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 2); + input_sync(input_ch2); + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 2); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 2); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 2); + input_sync(input_ch2); + } else if (board->cap_channel_ch3 == counter) { + input_report_abs(input_ch3, ABS_DISTANCE, 2); + input_sync(input_ch3); + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 2); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 2); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 2); + input_sync(input_ch2); + } else if (board->cap_channel_ch3 == counter) { + input_report_abs(input_ch3, ABS_DISTANCE, 2); + input_sync(input_ch3); + } else if (board->cap_channel_ch4 == counter) { + input_report_abs(input_ch4, ABS_DISTANCE, 2); + input_sync(input_ch4); + } + } + pCurrentButton->state = S_BODY; + last_val = 2; + } else if ((capsense_state & pCurrentButton->mask) == (pCurrentButton->mask & board->cap_button_mask )) { + LOG_DBG("CS %d State=PROX.\n", + counter); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 1); + input_sync(input_ch2); + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 1); + input_sync(input_ch2); + } else if (board->cap_channel_ch3 == counter) { + input_report_abs(input_ch3, ABS_DISTANCE, 1); + input_sync(input_ch3); + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 1); + input_sync(input_ch2); + } else if (board->cap_channel_ch3 == counter) { + input_report_abs(input_ch3, ABS_DISTANCE, 1); + input_sync(input_ch3); + } else if (board->cap_channel_ch4 == counter) { + input_report_abs(input_ch4, ABS_DISTANCE, 1); + input_sync(input_ch4); + } + } + pCurrentButton->state = S_PROX; + last_val = 0; + } else { + LOG_DBG("CS %d still in IDLE State.\n", + counter); + } + break; + case S_PROX: /* Button is being in proximity! */ + if ((capsense_state & pCurrentButton->mask) == pCurrentButton->mask) { + LOG_DBG("CS %d State=BODY.\n", + counter); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 2); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 2); + input_sync(input_ch1); + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 2); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 2); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 2); + input_sync(input_ch2); + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 2); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 2); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 2); + input_sync(input_ch2); + } else if (board->cap_channel_ch3 == counter) { + input_report_abs(input_ch3, ABS_DISTANCE, 2); + input_sync(input_ch3); + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 2); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 2); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 2); + input_sync(input_ch2); + } else if (board->cap_channel_ch3 == counter) { + input_report_abs(input_ch3, ABS_DISTANCE, 2); + input_sync(input_ch3); + } else if (board->cap_channel_ch4 == counter) { + input_report_abs(input_ch4, ABS_DISTANCE, 2); + input_sync(input_ch4); + } + } + pCurrentButton->state = S_BODY; + last_val = 2; + } else if ((capsense_state & pCurrentButton->mask) == (pCurrentButton->mask & board->cap_button_mask)) { + LOG_DBG("CS %d still in PROX State.\n", + counter); + } else{ + LOG_DBG("CS %d State=IDLE.\n", + counter); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + } else if (board->cap_channel_ch3 == counter) { + input_report_abs(input_ch3, ABS_DISTANCE, 0); + input_sync(input_ch3); + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + } else if (board->cap_channel_ch3 == counter) { + input_report_abs(input_ch3, ABS_DISTANCE, 0); + input_sync(input_ch3); + } else if (board->cap_channel_ch4 == counter) { + input_report_abs(input_ch4, ABS_DISTANCE, 0); + input_sync(input_ch4); + } + } + pCurrentButton->state = IDLE; + last_val = 0; + } + break; + case S_BODY: /* Button is being in 0mm! */ + if ((capsense_state & pCurrentButton->mask) == pCurrentButton->mask) { + LOG_DBG("CS %d still in BODY State.\n", + counter); + } else if ((capsense_state & pCurrentButton->mask) == (pCurrentButton->mask & board->cap_button_mask)) { + LOG_DBG("CS %d State=PROX.\n", + counter); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 1); + input_sync(input_ch2); + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 1); + input_sync(input_ch2); + } else if (board->cap_channel_ch3 == counter) { + input_report_abs(input_ch3, ABS_DISTANCE, 1); + input_sync(input_ch3); + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 1); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 1); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 1); + input_sync(input_ch2); + } else if (board->cap_channel_ch3 == counter) { + input_report_abs(input_ch3, ABS_DISTANCE, 1); + input_sync(input_ch3); + } else if (board->cap_channel_ch4 == counter) { + input_report_abs(input_ch4, ABS_DISTANCE, 1); + input_sync(input_ch4); + } + } + pCurrentButton->state = S_PROX; + last_val = 1; + } else{ + LOG_DBG("CS %d State=IDLE.\n", + counter); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE){ + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + } else if (board->cap_channel_ch3 == counter) { + input_report_abs(input_ch3, ABS_DISTANCE, 0); + input_sync(input_ch3); + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + if (board->cap_channel_ch0 == counter) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + } else if (board->cap_channel_ch1 == counter) { + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + } else if (board->cap_channel_ch2 == counter) { + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + } else if (board->cap_channel_ch3 == counter) { + input_report_abs(input_ch3, ABS_DISTANCE, 0); + input_sync(input_ch3); + } else if (board->cap_channel_ch4 == counter) { + input_report_abs(input_ch4, ABS_DISTANCE, 0); + input_sync(input_ch4); + } + } + pCurrentButton->state = IDLE; + last_val = 0; + } + break; + default: /* Shouldn't be here, device only allowed ACTIVE or IDLE */ + break; + }; + } + LOG_DBG("Leaving touchProcess()\n"); + } +} + +static int abov_get_nirq_state(unsigned irq_gpio) +{ + if (irq_gpio) { + return !gpio_get_value(irq_gpio); + } else { + LOG_ERR("abov irq_gpio is not set."); + return -EINVAL; + } +} + +static struct _totalButtonInformation smtcButtonInformation = { + .buttons = psmtcButtons, + .buttonSize = ARRAY_SIZE(psmtcButtons), +}; + +/** + *fn static void abov_reg_setup_init(struct i2c_client *client) + *brief read reg val form dts + * reg_array_len for regs needed change num + * data_array_val's format + */ +static void abov_reg_setup_init(struct i2c_client *client) +{ + u32 data_array_len = 0; + u32 *data_array; + int ret, i, j; + struct device_node *np = client->dev.of_node; + + ret = of_property_read_u32(np, "reg_array_len", &data_array_len); + if (ret < 0) { + LOG_ERR("data_array_len read error"); + return; + } + data_array = kmalloc(data_array_len * 2 * sizeof(u32), GFP_KERNEL); + ret = of_property_read_u32_array(np, "reg_array_val", + data_array, + data_array_len*2); + if (ret < 0) { + LOG_ERR("data_array_val read error"); + return; + } + for (i = 0; i < ARRAY_SIZE(abov_i2c_reg_setup); i++) { + for (j = 0; j < data_array_len*2; j += 2) { + if (data_array[j] == abov_i2c_reg_setup[i].reg) { + abov_i2c_reg_setup[i].val = data_array[j+1]; + LOG_DBG("read dtsi 0x%02x:0x%02x set reg\n", + data_array[j], data_array[j+1]); + } + } + } + kfree(data_array); +} + +static void abov_platform_data_of_init(struct i2c_client *client, + pabov_platform_data_t pplatData) +{ + struct device_node *np = client->dev.of_node; + u32 cap_channel_ch0,cap_channel_ch1,cap_channel_ch2,cap_channel_ch3,cap_channel_ch4; + int ret; + + client->irq = of_get_gpio(np, 0); + pplatData->irq_gpio = client->irq; + + ret = of_property_read_u32(np, "cap,channel_number", &abov_channel_number); + if (ret < 0) { + LOG_ERR("get cap,channel number node error,set default value!\n"); + abov_channel_number = ABOV_CHANNEL_NUMBER_TWO; + } + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + ret = of_property_read_u32(np, "cap,use_channel_ch0", &cap_channel_ch0); + ret = of_property_read_string(np, "ch0_name", &pplatData->cap_ch0_name); + ret = of_property_read_u32(np, "cap,use_channel_ch1", &cap_channel_ch1); + ret = of_property_read_string(np, "ch1_name", &pplatData->cap_ch1_name); + pplatData->cap_channel_ch0 = (int)cap_channel_ch0; + pplatData->cap_channel_ch1 = (int)cap_channel_ch1; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + ret = of_property_read_u32(np, "cap,use_channel_ch0", &cap_channel_ch0); + ret = of_property_read_string(np, "ch0_name", &pplatData->cap_ch0_name); + ret = of_property_read_u32(np, "cap,use_channel_ch1", &cap_channel_ch1); + ret = of_property_read_string(np, "ch1_name", &pplatData->cap_ch1_name); + ret = of_property_read_u32(np, "cap,use_channel_ch2", &cap_channel_ch2); + ret = of_property_read_string(np, "ch2_name", &pplatData->cap_ch2_name); + pplatData->cap_channel_ch0 = (int)cap_channel_ch0; + pplatData->cap_channel_ch1 = (int)cap_channel_ch1; + pplatData->cap_channel_ch2 = (int)cap_channel_ch2; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + ret = of_property_read_u32(np, "cap,use_channel_ch0", &cap_channel_ch0); + ret = of_property_read_string(np, "ch0_name", &pplatData->cap_ch0_name); + ret = of_property_read_u32(np, "cap,use_channel_ch1", &cap_channel_ch1); + ret = of_property_read_string(np, "ch1_name", &pplatData->cap_ch1_name); + ret = of_property_read_u32(np, "cap,use_channel_ch2", &cap_channel_ch2); + ret = of_property_read_string(np, "ch2_name", &pplatData->cap_ch2_name); + ret = of_property_read_u32(np, "cap,use_channel_ch3", &cap_channel_ch3); + ret = of_property_read_string(np, "ch3_name", &pplatData->cap_ch3_name); + pplatData->cap_channel_ch0 = (int)cap_channel_ch0; + pplatData->cap_channel_ch1 = (int)cap_channel_ch1; + pplatData->cap_channel_ch2 = (int)cap_channel_ch2; + pplatData->cap_channel_ch3 = (int)cap_channel_ch3; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + ret = of_property_read_u32(np, "cap,use_channel_ch0", &cap_channel_ch0); + ret = of_property_read_string(np, "ch0_name", &pplatData->cap_ch0_name); + ret = of_property_read_u32(np, "cap,use_channel_ch1", &cap_channel_ch1); + ret = of_property_read_string(np, "ch1_name", &pplatData->cap_ch1_name); + ret = of_property_read_u32(np, "cap,use_channel_ch2", &cap_channel_ch2); + ret = of_property_read_string(np, "ch2_name", &pplatData->cap_ch2_name); + ret = of_property_read_u32(np, "cap,use_channel_ch3", &cap_channel_ch3); + ret = of_property_read_string(np, "ch3_name", &pplatData->cap_ch3_name); + ret = of_property_read_u32(np, "cap,use_channel_ch4", &cap_channel_ch4); + ret = of_property_read_string(np, "ch4_name", &pplatData->cap_ch4_name); + pplatData->cap_channel_ch0 = (int)cap_channel_ch0; + pplatData->cap_channel_ch1 = (int)cap_channel_ch1; + pplatData->cap_channel_ch2 = (int)cap_channel_ch2; + pplatData->cap_channel_ch3 = (int)cap_channel_ch3; + pplatData->cap_channel_ch4 = (int)cap_channel_ch4; + } + + pplatData->get_is_nirq_low = abov_get_nirq_state; + pplatData->init_platform_hw = NULL; + /* pointer to an exit function. Here in case needed in the future */ + /* + *.exit_platform_hw = abov_exit_ts, + */ + pplatData->exit_platform_hw = NULL; + abov_reg_setup_init(client); + pplatData->pi2c_reg = abov_i2c_reg_setup; + pplatData->i2c_reg_num = ARRAY_SIZE(abov_i2c_reg_setup); + + pplatData->pbuttonInformation = &smtcButtonInformation; + + ret = of_property_read_string(np, "label", &pplatData->fw_name); + if (ret < 0) { + LOG_ERR("firmware name read error!\n"); + return; + } + + ret = of_property_read_u32(np, "cap,fw_mode_ret", &pplatData->fw_mode_ret); + if (ret < 0) { + LOG_ERR("get cap,fw_mode_ret error,set default value.\n"); + pplatData->fw_mode_ret = ABOV_T376_FW_MODE; + } + ret = of_property_read_u32(np, "button_mask", &pplatData->cap_button_mask); + if (ret < 0) { + LOG_ERR("get button_mask,set default value.\n"); + pplatData->cap_button_mask = ABOV_CAP_BUTTON_5CH_MASK; + } + + ret = of_property_read_u32(np, "cap,flash_erase_time", &abov_flash_erase_time); + if (ret < 0) { + LOG_ERR("cap,flash_erase_time error!,set default value.\n"); + abov_flash_erase_time = ABOV_T376_FLASH_ERASE_TIME; + } + +} + +static ssize_t reset_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 8, "%d\n", programming_done); +} + +static ssize_t reset_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + pabov_t pDevice = NULL; + struct input_dev *input_ch0 = NULL; + struct input_dev *input_ch1 = NULL; + struct input_dev *input_ch2 = NULL; + struct input_dev *input_ch3 = NULL; + struct input_dev *input_ch4 = NULL; + + pDevice = this->pDevice; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + input_ch3 = pDevice->pbuttonInformation->input_ch3; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + input_ch3 = pDevice->pbuttonInformation->input_ch3; + input_ch4 = pDevice->pbuttonInformation->input_ch4; + } + + if (!count || (this == NULL)) + return -EINVAL; + + if (!strncmp(buf, "reset", 5) || !strncmp(buf, "1", 1)) + write_register(this, ABOV_SOFTRESET_REG, 0x10); + + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + input_report_abs(input_ch3, ABS_DISTANCE, 0); + input_sync(input_ch3); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + input_report_abs(input_ch3, ABS_DISTANCE, 0); + input_sync(input_ch3); + input_report_abs(input_ch4, ABS_DISTANCE, 0); + input_sync(input_ch4); + } + + return count; +} + +static CLASS_ATTR_RW(reset); + +static ssize_t int_state_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + pabovXX_t this = abov_sar_ptr; + LOG_DBG("Reading INT line state\n"); + return snprintf(buf, 8, "%d\n", this->int_state); +} +static CLASS_ATTR_RO(int_state); + +static ssize_t fw_download_status_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + pabovXX_t this = abov_sar_ptr; + LOG_DBG("Reading fw download state\n"); + return snprintf(buf, 8, "%d", this->fw_dl_status); +} +static CLASS_ATTR_RO(fw_download_status); + +static ssize_t enable_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 8, "%d\n", mEnabled); +} + +static ssize_t enable_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + pabov_t pDevice = NULL; + struct input_dev *input_ch0 = NULL; + struct input_dev *input_ch1 = NULL; + struct input_dev *input_ch2 = NULL; + struct input_dev *input_ch3 = NULL; + struct input_dev *input_ch4 = NULL; + + pDevice = this->pDevice; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + input_ch3 = pDevice->pbuttonInformation->input_ch3; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + input_ch3 = pDevice->pbuttonInformation->input_ch3; + input_ch4 = pDevice->pbuttonInformation->input_ch4; + } + + if (!count || (this == NULL)) + return -EINVAL; + + if (!strncmp(buf, "1", 1)) { + LOG_INFO("enable cap sensor\n"); + initialize(this); + + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + input_report_abs(input_ch3, ABS_DISTANCE, 0); + input_sync(input_ch3); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + input_report_abs(input_ch3, ABS_DISTANCE, 0); + input_sync(input_ch3); + input_report_abs(input_ch4, ABS_DISTANCE, 0); + input_sync(input_ch4); + } + mEnabled = 1; + } else if (!strncmp(buf, "0", 1)) { + LOG_INFO("disable cap sensor\n"); + + write_register(this, ABOV_CTRL_MODE_REG, ABOV_CTRL_MODE_STOP); + + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_report_abs(input_ch0, ABS_DISTANCE, -1); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, -1); + input_sync(input_ch1); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + input_report_abs(input_ch0, ABS_DISTANCE, -1); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, -1); + input_sync(input_ch1); + input_report_abs(input_ch2, ABS_DISTANCE, -1); + input_sync(input_ch2); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + input_report_abs(input_ch0, ABS_DISTANCE, -1); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, -1); + input_sync(input_ch1); + input_report_abs(input_ch2, ABS_DISTANCE, -1); + input_sync(input_ch2); + input_report_abs(input_ch3, ABS_DISTANCE, -1); + input_sync(input_ch3); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + input_report_abs(input_ch0, ABS_DISTANCE, -1); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, -1); + input_sync(input_ch1); + input_report_abs(input_ch2, ABS_DISTANCE, -1); + input_sync(input_ch2); + input_report_abs(input_ch3, ABS_DISTANCE, -1); + input_sync(input_ch3); + input_report_abs(input_ch4, ABS_DISTANCE, -1); + input_sync(input_ch4); + } + mEnabled = 0; + } else { + LOG_ERR("unknown enable symbol\n"); + } + + return count; +} + +#ifdef USE_SENSORS_CLASS +static int capsensor_set_enable(struct sensors_classdev *sensors_cdev, unsigned int enable) +{ + pabovXX_t this = abov_sar_ptr; + pabov_t pDevice = NULL; + struct input_dev *input_ch0 = NULL; + struct input_dev *input_ch1 = NULL; + struct input_dev *input_ch2 = NULL; + struct input_dev *input_ch3 = NULL; + struct input_dev *input_ch4 = NULL; + + pDevice = this->pDevice; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + input_ch3 = pDevice->pbuttonInformation->input_ch3; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + input_ch3 = pDevice->pbuttonInformation->input_ch3; + input_ch4 = pDevice->pbuttonInformation->input_ch4; + } + + if (enable == 1) { + LOG_INFO("enable cap sensor: %s\n",sensors_cdev->name); + if(mEnabled == 0){ + initialize(this); + mEnabled = 1; + } + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if(!strcmp(sensors_cdev->name, this->board->cap_ch0_name)){ + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH0; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch1_name)){ + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH1; + } + } else if(abov_channel_number == ABOV_CHANNEL_NUMBER_THREE){ + if(!strcmp(sensors_cdev->name, this->board->cap_ch0_name)){ + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH0; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch1_name)){ + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH1; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch2_name)){ + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH2; + } + } else if(abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + if(!strcmp(sensors_cdev->name, this->board->cap_ch0_name)){ + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH0; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch1_name)){ + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH1; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch2_name)){ + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH2; + } else if(!strcmp(sensors_cdev->name, this->board->cap_ch3_name)){ + input_report_abs(input_ch3, ABS_DISTANCE, 0); + input_sync(input_ch3); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH3; + } + } else if(abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + if(!strcmp(sensors_cdev->name, this->board->cap_ch0_name)){ + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH0; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch1_name)){ + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH1; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch2_name)){ + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH2; + } else if(!strcmp(sensors_cdev->name, this->board->cap_ch3_name)){ + input_report_abs(input_ch3, ABS_DISTANCE, 0); + input_sync(input_ch3); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH3; + } else if(!strcmp(sensors_cdev->name, this->board->cap_ch4_name)){ + input_report_abs(input_ch4, ABS_DISTANCE, 0); + input_sync(input_ch4); + this->enable_flag |= CAPSENSOR_ENABLE_FLAG_CH4; + } + } + LOG_INFO(" cap sensor enable 0x%02x \n", this->enable_flag); + } else if (enable == 0) { + LOG_INFO("disable cap sensor: %s\n",sensors_cdev->name); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + if(!strcmp(sensors_cdev->name, this->board->cap_ch0_name)){ + input_report_abs(input_ch0, ABS_DISTANCE, -1); + input_sync(input_ch0); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH0; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch1_name)){ + input_report_abs(input_ch1, ABS_DISTANCE, -1); + input_sync(input_ch1); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH1; + } + } else if(abov_channel_number == ABOV_CHANNEL_NUMBER_THREE){ + if(!strcmp(sensors_cdev->name, this->board->cap_ch0_name)){ + input_report_abs(input_ch0, ABS_DISTANCE, -1); + input_sync(input_ch0); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH0; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch1_name)){ + input_report_abs(input_ch1, ABS_DISTANCE, -1); + input_sync(input_ch1); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH1; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch2_name)){ + input_report_abs(input_ch2, ABS_DISTANCE, -1); + input_sync(input_ch2); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH2; + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR){ + if(!strcmp(sensors_cdev->name, this->board->cap_ch0_name)){ + input_report_abs(input_ch0, ABS_DISTANCE, -1); + input_sync(input_ch0); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH0; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch1_name)){ + input_report_abs(input_ch1, ABS_DISTANCE, -1); + input_sync(input_ch1); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH1; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch2_name)){ + input_report_abs(input_ch2, ABS_DISTANCE, -1); + input_sync(input_ch2); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH2; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch3_name)){ + input_report_abs(input_ch3, ABS_DISTANCE, -1); + input_sync(input_ch3); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH3; + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE){ + if(!strcmp(sensors_cdev->name, this->board->cap_ch0_name)){ + input_report_abs(input_ch0, ABS_DISTANCE, -1); + input_sync(input_ch0); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH0; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch1_name)){ + input_report_abs(input_ch1, ABS_DISTANCE, -1); + input_sync(input_ch1); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH1; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch2_name)){ + input_report_abs(input_ch2, ABS_DISTANCE, -1); + input_sync(input_ch2); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH2; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch3_name)){ + input_report_abs(input_ch3, ABS_DISTANCE, -1); + input_sync(input_ch3); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH3; + }else if(!strcmp(sensors_cdev->name, this->board->cap_ch4_name)){ + input_report_abs(input_ch4, ABS_DISTANCE, -1); + input_sync(input_ch4); + this->enable_flag &= ~CAPSENSOR_ENABLE_FLAG_CH4; + } + } + LOG_INFO(" cap sensor disable 0x%02x \n", this->enable_flag); + if(!(this->enable_flag & 0x1F)){ + mEnabled = 0; + write_register(this, ABOV_CTRL_MODE_REG, ABOV_CTRL_MODE_STOP); + LOG_INFO("all cap sensor disable,change to STOP mode.\n"); + } + } else { + LOG_ERR("unknown enable symbol\n"); + } + + return 0; +} +#endif + +static CLASS_ATTR_RW(enable); + +static ssize_t reg_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u8 reg_value = 0, i; + pabovXX_t this = abov_sar_ptr; + char *p = buf; + + if (this->read_flag) { + this->read_flag = 0; + read_register(this, this->read_reg, ®_value); + p += snprintf(p, PAGE_SIZE, "(0x%02x)=0x%02x\n", this->read_reg, reg_value); + return (p-buf); + } + + for (i = 0; i < 0x56; i++) { + read_register(this, i, ®_value); + p += snprintf(p, PAGE_SIZE, "(0x%02x)=0x%02x\n", + i, reg_value); + } + + for (i = 0x80; i < 0xB0; i++) { + read_register(this, i, ®_value); + p += snprintf(p, PAGE_SIZE, "(0x%02x)=0x%02x\n", + i, reg_value); + } + + return (p-buf); +} + +static ssize_t reg_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + unsigned int val, reg, opt; + if (sscanf(buf, "%x,%x,%x", ®, &val, &opt) == 3) { + LOG_DBG("%s, read reg = 0x%02x\n", __func__, *(u8 *)®); + this->read_reg = *((u8 *)®); + this->read_flag = 1; + } else if (sscanf(buf, "%x,%x", ®, &val) == 2) { + LOG_DBG("%s,reg = 0x%02x, val = 0x%02x\n", + __func__, *(u8 *)®, *(u8 *)&val); + write_register(this, *((u8 *)®), *((u8 *)&val)); + } + + return count; +} + + +static CLASS_ATTR_RW(reg); + +static struct class capsense_class = { + .name = "capsense", + .owner = THIS_MODULE, +}; + +static void ps_notify_callback_work(struct work_struct *work) +{ + pabovXX_t this = container_of(work, abovXX_t, ps_notify_work); + pabov_t pDevice = NULL; + struct input_dev *input_ch0 = NULL; + struct input_dev *input_ch1 = NULL; + struct input_dev *input_ch2 = NULL; + struct input_dev *input_ch3 = NULL; + struct input_dev *input_ch4 = NULL; + int ret = 0; + + pDevice = this->pDevice; + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + input_ch3 = pDevice->pbuttonInformation->input_ch3; + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + input_ch0 = pDevice->pbuttonInformation->input_ch0; + input_ch1 = pDevice->pbuttonInformation->input_ch1; + input_ch2 = pDevice->pbuttonInformation->input_ch2; + input_ch3 = pDevice->pbuttonInformation->input_ch3; + input_ch4 = pDevice->pbuttonInformation->input_ch4; + } + + LOG_INFO("Usb insert,going to force calibrate\n"); + ret = write_register(this, ABOV_RECALI_REG, 0x01); + if (ret < 0) + LOG_ERR(" Usb insert,calibrate cap sensor failed\n"); + + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + input_report_abs(input_ch3, ABS_DISTANCE, 0); + input_sync(input_ch3); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + input_report_abs(input_ch0, ABS_DISTANCE, 0); + input_sync(input_ch0); + input_report_abs(input_ch1, ABS_DISTANCE, 0); + input_sync(input_ch1); + input_report_abs(input_ch2, ABS_DISTANCE, 0); + input_sync(input_ch2); + input_report_abs(input_ch3, ABS_DISTANCE, 0); + input_sync(input_ch3); + input_report_abs(input_ch4, ABS_DISTANCE, 0); + input_sync(input_ch4); + } +} + +static int ps_get_state(struct power_supply *psy, bool *present) +{ + union power_supply_propval pval = { 0 }; + int retval; + + retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, + &pval); + if (retval) { + LOG_ERR("%s psy get property failed\n", psy->desc->name); + return retval; + } + *present = (pval.intval) ? true : false; + LOG_INFO("%s is %s\n", psy->desc->name, + (*present) ? "present" : "not present"); + return 0; +} + +static int ps_notify_callback(struct notifier_block *self, + unsigned long event, void *p) +{ + pabovXX_t this = container_of(self, abovXX_t, ps_notif); + struct power_supply *psy = p; + bool present; + int retval; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0) + if (event == PSY_EVENT_PROP_CHANGED +#else + if ((event == PSY_EVENT_PROP_ADDED || event == PSY_EVENT_PROP_CHANGED) +#endif + && psy && psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, "usb", sizeof("usb"))){ + LOG_INFO("ps notification: event = %lu\n", event); + retval = ps_get_state(psy, &present); + if (retval) { + LOG_ERR("psy get property failed\n"); + return retval; + } + + if (event == PSY_EVENT_PROP_CHANGED) { + if (this->ps_is_present == present) { + LOG_INFO("ps present state not change\n"); + return 0; + } + } + this->ps_is_present = present; + schedule_work(&this->ps_notify_work); + } + + return 0; +} + + +static int _i2c_adapter_block_write(struct i2c_client *client, u8 *data, u8 len) +{ + u8 buffer[C_I2C_FIFO_SIZE]; + u8 left = len; + u8 offset = 0; + u8 retry = 0; + + struct i2c_msg msg = { + .addr = client->addr & I2C_MASK_FLAG, + .flags = 0, + .buf = buffer, + }; + + if (data == NULL || len < 1) { + LOG_ERR("Invalid : data is null or len=%d\n", len); + return -EINVAL; + } + + while (left > 0) { + retry = 0; + if (left >= C_I2C_FIFO_SIZE) { + msg.buf = &data[offset]; + msg.len = C_I2C_FIFO_SIZE; + left -= C_I2C_FIFO_SIZE; + offset += C_I2C_FIFO_SIZE; + } else { + msg.buf = &data[offset]; + msg.len = left; + left = 0; + } + + while (i2c_transfer(client->adapter, &msg, 1) != 1) { + retry++; + if (retry > 10) { + LOG_ERR("OUT : fail - addr:%#x len:%d \n", client->addr, msg.len); + return -EIO; + } + } + } + return 0; +} + +static int i2c_adapter_block_write_nodatalog(struct i2c_client *client, u8 *data, u8 len) +{ + return _i2c_adapter_block_write(client, data, len); +} + +static int abov_tk_check_busy(struct i2c_client *client) +{ + int ret, count = 0; + unsigned char val = 0x00; + + do { + ret = i2c_master_recv(client, &val, sizeof(val)); + if (val & 0x01) { + count++; + if (count > 1000) { + LOG_INFO("%s: val = 0x%x\r\n", __func__, val); + return ret; + } + } else { + break; + } + } while (1); + + return ret; +} + +static int abov_tk_fw_write(struct i2c_client *client, unsigned char *addrH, + unsigned char *addrL, unsigned char *val) +{ + int length = 36, ret = 0; + unsigned char buf[40] = {0, }; + + buf[0] = 0xAC; + buf[1] = 0x7A; + memcpy(&buf[2], addrH, 1); + memcpy(&buf[3], addrL, 1); + memcpy(&buf[4], val, 32); + ret = i2c_adapter_block_write_nodatalog(client, buf, length); + if (ret < 0) { + LOG_ERR("Firmware write fail ...\n"); + return ret; + } + + SLEEP(3); + abov_tk_check_busy(client); + + return 0; +} + +static int abov_tk_reset_for_bootmode(struct i2c_client *client) +{ + int ret, retry_count = 10; + unsigned char buf[16] = {0, }; + +retry: + buf[0] = 0xF0; + buf[1] = 0xAA; + ret = i2c_master_send(client, buf, 2); + if (ret < 0) { + LOG_DBG("write fail(retry:%d)\n", retry_count); + if (retry_count-- > 0) { + goto retry; + } + return -EIO; + } else { + LOG_INFO("success reset & boot mode\n"); + return 0; + } +} + +static int abov_tk_fw_mode_enter(struct i2c_client *client) +{ + int ret = 0; + unsigned char buf[40] = {0, }; + pabov_platform_data_t pplatData = client->dev.platform_data; + + buf[0] = 0xAC; + buf[1] = 0x5B; + ret = i2c_master_send(client, buf, 2); + if (ret != 2) { + LOG_ERR("SEND : fail - addr:0x%02x data:0x%02x 0x%02x... ret:%d\n", client->addr, buf[0], buf[1], ret); + return -EIO; + } + LOG_INFO("SEND : succ - addr:0x%02x data:0x%02x 0x%02x... ret:%d\n", client->addr, buf[0], buf[1], ret); + SLEEP(5); + + ret = i2c_master_recv(client, buf, 1); + if (buf[0] != pplatData->fw_mode_ret) { + LOG_ERR("Enter fw mode fail,device id:0x%02x\n", buf[0]); + return -EIO; + } + + LOG_INFO("Enter fw mode success ... device id:0x%02x\n", buf[0]); + + return 0; +} + +static int abov_tk_fw_mode_exit(struct i2c_client *client) +{ + int ret = 0; + unsigned char buf[40] = {0, }; + + buf[0] = 0xAC; + buf[1] = 0xE1; + ret = i2c_master_send(client, buf, 2); + if (ret != 2) { + LOG_ERR("SEND : fail - addr:0x%02x data:0x%02x 0x%02x ... ret:%d\n", client->addr, buf[0], buf[1], ret); + return -EIO; + } + LOG_INFO("SEND : succ - addr:0x%02x data:0x%02x 0x%02x ... ret:%d\n", client->addr, buf[0], buf[1], ret); + + return 0; +} + +static int abov_tk_flash_erase(struct i2c_client *client) +{ + int ret = 0; + unsigned char buf[16] = {0, }; + + buf[0] = 0xAC; + buf[1] = 0x2E; + + ret = i2c_master_send(client, buf, 2); + if (ret != 2) { + LOG_ERR("SEND : fail - addr:0x%02x data:0x%02x 0x%02x ... ret:%d\n", client->addr, buf[0], buf[1], ret); + return -EIO; + } + + LOG_INFO("SEND : succ - addr:0x%02x data:0x%02x 0x%02x ... ret:%d\n", client->addr, buf[0], buf[1], ret); + + return 0; +} + +static int abov_tk_i2c_read_checksum(struct i2c_client *client) +{ + unsigned char checksum[6] = {0, }; + unsigned char buf[16] = {0, }; + int ret; + + checksum_h = 0; + checksum_l = 0; + + buf[0] = 0xAC; + buf[1] = 0x9E; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = checksum_h_bin; + buf[5] = checksum_l_bin; + ret = i2c_master_send(client, buf, 6); + + if (ret != 6) { + LOG_ERR("SEND : fail - addr:0x%02x len:%d ... ret:%d\n", client->addr, 6, ret); + return -EIO; + } + SLEEP(5); + + buf[0] = 0x00; + ret = i2c_master_send(client, buf, 1); + if (ret != 1) { + LOG_ERR("SEND : fail - addr:0x%02x data:0x%02x ... ret:%d\n", client->addr, buf[0], ret); + return -EIO; + } + SLEEP(5); + + ret = i2c_master_recv(client, checksum, 6); + if (ret < 0) { + LOG_ERR("Read checksum fail ... \n"); + return -EIO; + } + + checksum_h = checksum[4]; + checksum_l = checksum[5]; + + return 0; +} + +static int _abov_fw_update(struct i2c_client *client, const u8 *image, u32 size) +{ + int ret, ii = 0; + int count; + unsigned short address; + unsigned char addrH, addrL; + unsigned char data[32] = {0, }; + + LOG_INFO("%s: call in\r\n", __func__); + + if (abov_tk_reset_for_bootmode(client) < 0) { + LOG_ERR("don't reset(enter boot mode)!"); + return -EIO; + } + + SLEEP(45); + + for (ii = 0; ii < 10; ii++) { + if (abov_tk_fw_mode_enter(client) < 0) { + LOG_ERR("don't enter the download mode! %d", ii); + SLEEP(40); + continue; + } + break; + } + + if (10 <= ii) { + return -EAGAIN; + } + + if (abov_tk_flash_erase(client) < 0) { + LOG_ERR("don't erase flash data!"); + return -EIO; + } + + SLEEP(abov_flash_erase_time); + + address = 0x800; + count = size / 32; + + for (ii = 0; ii < count; ii++) { + /* first 32byte is header */ + addrH = (unsigned char)((address >> 8) & 0xFF); + addrL = (unsigned char)(address & 0xFF); + memcpy(data, &image[ii * 32], 32); + ret = abov_tk_fw_write(client, &addrH, &addrL, data); + if (ret < 0) { + LOG_ERR("fw_write.. ii = 0x%x err\r\n", ii); + return ret; + } + + address += 0x20; + memset(data, 0, 32); + } + + ret = abov_tk_i2c_read_checksum(client); + ret = abov_tk_fw_mode_exit(client); + if ((checksum_h == checksum_h_bin) && (checksum_l == checksum_l_bin)) { + LOG_INFO("Firmware update success. checksum_h=0x%02x,checksum_h_bin=0x%02x,checksum_l=0x%02x,checksum_l_bin=0x%02x\n", + checksum_h, checksum_h_bin, checksum_l, checksum_l_bin); + } else { + LOG_ERR("Firmware update fail. checksum_h=0x%02x,checksum_h_bin=0x%02x,checksum_l=0x%02x,checksum_l_bin=0x%02x\n", + checksum_h, checksum_h_bin, checksum_l, checksum_l_bin); + ret = -1; + } + SLEEP(100); + + return ret; +} + +static int abov_fw_update(bool force) +{ + int update_loop; + pabovXX_t this = abov_sar_ptr; + struct i2c_client *client = this->bus; + int rc; + bool fw_upgrade = false; + u8 fw_version = 0, fw_file_version = 0; + u8 fw_modelno = 0, fw_file_modeno = 0; + const struct firmware *fw = NULL; + char fw_name[32] = {0}; + + strcpy(fw_name, this->board->fw_name); + strlcat(fw_name, ".BIN", NAME_MAX); + rc = request_firmware(&fw, fw_name, this->pdev); + if (rc < 0) { + read_register(this, ABOV_VERSION_REG, &fw_version); + read_register(this, ABOV_MODELNO_REG, &fw_modelno); + LOG_ERR("Request firmware failed - %s (%d),current fw info inside IC:Version=0x%02x,ModelNo=0x%02x\n", + this->board->fw_name, rc, fw_version, fw_modelno); + return rc; + } + + if (force == false) { + read_register(this, ABOV_VERSION_REG, &fw_version); + read_register(this, ABOV_MODELNO_REG, &fw_modelno); + LOG_INFO("Version in sensor is:0x%02x ,ModelNo in sensor is:0x%02x\n" ,fw_version ,fw_modelno); + } + + fw_file_modeno = fw->data[1]; + fw_file_version = fw->data[5]; + checksum_h_bin = fw->data[8]; + checksum_l_bin = fw->data[9]; + LOG_INFO("Version in file is:0x%02x ,ModelNo in file is:0x%02x\n" ,fw_file_version ,fw_file_version); + + if ((force) || (fw_version < fw_file_version) || (fw_modelno != fw_file_modeno)) { + LOG_INFO("Firmware is not latest,going to fw upgrade...\n"); + fw_upgrade = true; + } else { + LOG_INFO("Firmware is latest,exiting fw upgrade...\n"); + fw_upgrade = false; + this->fw_dl_status = 0; + rc = -EIO; + goto rel_fw; + } + + if (fw_upgrade) { + this->fw_dl_status = 2; + for (update_loop = 0; update_loop < 10; update_loop++) { + rc = _abov_fw_update(client, &fw->data[32], fw->size-32); + if (rc < 0) + LOG_ERR("retry : %d times!\n", update_loop); + else { + //this funtion under the mutex lock, and the initialize will enable irq, + //if trigger the interrupts, the cap-sensor will no work, so, initialize() no stay here. + //initialize(this); + break; + } + } + if (update_loop >= 10) { + this->fw_dl_status = 1; + rc = -EIO; + } + this->fw_dl_status = 0; + } + +rel_fw: + release_firmware(fw); + return rc; +} + + + +static ssize_t update_fw_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u8 fw_version = 0; + pabovXX_t this = abov_sar_ptr; + + read_register(this, ABOV_VERSION_REG, &fw_version); + + return snprintf(buf, 37, "ABOV CapSensor Firmware Version:0x%02x\n", fw_version); +} + +static ssize_t update_fw_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + unsigned long val; + int rc; + + if (count > 2) + return -EINVAL; + + rc = kstrtoul(buf, 10, &val); + if (rc != 0) + return rc; + + if(this->irq_disabled == 0) { + disable_irq(this->irq); + this->irq_disabled = 1; + } + mutex_lock(&this->mutex); + if (!this->loading_fw && val) { + this->loading_fw = true; + abov_fw_update(false); + this->loading_fw = false; + } + mutex_unlock(&this->mutex); + + if(this->irq_disabled == 1) { + enable_irq(this->irq); + this->irq_disabled = 0; + } + + return count; +} +static CLASS_ATTR_RW(update_fw); + + +static ssize_t force_update_fw_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u8 fw_version = 0; + pabovXX_t this = abov_sar_ptr; + + read_register(this, ABOV_VERSION_REG, &fw_version); + + return snprintf(buf, 37, "ABOV CapSensor Firmware Version:0x%02x\n", fw_version); +} + +static ssize_t force_update_fw_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + pabovXX_t this = abov_sar_ptr; + unsigned long val; + int rc; + + if (count > 2) + return -EINVAL; + + rc = kstrtoul(buf, 10, &val); + if (rc != 0) + return rc; + + if(this->irq_disabled == 0) { + disable_irq(this->irq); + this->irq_disabled = 1; + } + + mutex_lock(&this->mutex); + if (!this->loading_fw && val) { + this->loading_fw = true; + abov_fw_update(true); + this->loading_fw = false; + } + mutex_unlock(&this->mutex); + + if(this->irq_disabled == 1) { + enable_irq(this->irq); + this->irq_disabled = 0; + } + + return count; +} +static CLASS_ATTR_RW(force_update_fw); + +static void capsense_update_work(struct work_struct *work) +{ + pabovXX_t this = container_of(work, abovXX_t, fw_update_work); + + LOG_DBG("%s: start update firmware\n", __func__); + if(this->irq_disabled == 0) { + disable_irq(this->irq); + this->irq_disabled = 1; + } + mutex_lock(&this->mutex); + this->loading_fw = true; + abov_fw_update(false); + this->loading_fw = false; + mutex_unlock(&this->mutex); + if(this->irq_disabled == 1) { + enable_irq(this->irq); + this->irq_disabled = 0; + } + + if(mEnabled){ + initialize(this); + } + LOG_INFO("%s: update firmware end\n", __func__); +} + +static void capsense_fore_update_work(struct work_struct *work) +{ + pabovXX_t this = container_of(work, abovXX_t, fw_update_work); + + LOG_DBG("%s: start force update firmware\n", __func__); + if(this->irq_disabled == 0) { + disable_irq(this->irq); + this->irq_disabled = 1; + } + mutex_lock(&this->mutex); + this->loading_fw = true; + abov_fw_update(true); + this->loading_fw = false; + mutex_unlock(&this->mutex); + if(this->irq_disabled == 1) { + enable_irq(this->irq); + this->irq_disabled = 0; + } + + if(mEnabled){ + initialize(this); + } + LOG_INFO("%s: force update firmware end\n", __func__); +} + + +/** + * detect if abov exist or not + * return 1 if chip exist else return 0 + */ +static int abov_detect(struct i2c_client *client) +{ + s32 returnValue = 0, i; + u8 address = ABOV_VENDOR_ID_REG; + u8 value = 0xAB; + + if (client) { + for (i = 0; i < 10; i++) { + returnValue = i2c_smbus_read_byte_data(client, address); + LOG_DBG("abov read_register for %d time Addr: 0x%02x Return: 0x%02x\n", + i, address, returnValue); + if (returnValue >= 0) { + if (value == returnValue) { + LOG_INFO("abov detect success!\n"); + return 1; + } + } + msleep(10); + } + LOG_INFO("abov boot detect start\n"); + for (i = 0; i < 3; i++) { + if(abov_tk_fw_mode_enter(client) == 0) { + LOG_INFO("abov boot detect success!\n"); + return 2; + } + } + } + LOG_INFO("abov detect failed!!!\n"); + return 0; +} + + +/** + * fn static int abov_probe(struct i2c_client *client, const struct i2c_device_id *id) + * brief Probe function + * param client pointer to i2c_client + * param id pointer to i2c_device_id + * return Whether probe was successful + */ +static int abov_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + pabovXX_t this = 0; + pabov_t pDevice = 0; + pabov_platform_data_t pplatData = 0; + bool isForceUpdate = false; + int ret; + struct input_dev *input_ch0 = NULL; + struct input_dev *input_ch1 = NULL; + struct input_dev *input_ch2 = NULL; + struct input_dev *input_ch3 = NULL; + struct input_dev *input_ch4 = NULL; + struct power_supply *psy = NULL; + + LOG_INFO("abov_probe() start\n"); + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -EIO; + + pplatData = kzalloc(sizeof(struct abov_platform_data), GFP_KERNEL); + if (!pplatData) { + LOG_ERR("platform data is required!\n"); + return -EINVAL; + } + abov_platform_data_of_init(client, pplatData); + client->dev.platform_data = pplatData; + + pplatData->cap_vdd = regulator_get(&client->dev, "cap_vdd"); + if (IS_ERR(pplatData->cap_vdd)) { + if (PTR_ERR(pplatData->cap_vdd) == -EPROBE_DEFER) { + ret = PTR_ERR(pplatData->cap_vdd); + goto err_vdd_defer; + } + LOG_ERR("%s: Failed to get regulator\n", __func__); + } else { + ret = regulator_enable(pplatData->cap_vdd); + + if (ret) { + regulator_put(pplatData->cap_vdd); + LOG_ERR("%s: Error %d enable regulator\n", + __func__, ret); + goto err_vdd_defer; + } + pplatData->cap_vdd_en = true; + LOG_INFO("cap_vdd regulator is %s\n", + regulator_is_enabled(pplatData->cap_vdd) ? + "on" : "off"); + } + + pplatData->cap_svdd = regulator_get(&client->dev, "cap_svdd"); + if (IS_ERR(pplatData->cap_svdd)) { + if (PTR_ERR(pplatData->cap_svdd) == -EPROBE_DEFER) { + ret = PTR_ERR(pplatData->cap_svdd); + goto err_svdd_error; + } + LOG_ERR("%s: Failed to get regulator\n", __func__); + } else { + ret = regulator_enable(pplatData->cap_svdd); + + if (ret) { + regulator_put(pplatData->cap_svdd); + LOG_ERR("%s: Error %d enable regulator\n", + __func__, ret); + goto err_svdd_error; + } + pplatData->cap_svdd_en = true; + LOG_INFO("cap_svdd regulator is %s\n", + regulator_is_enabled(pplatData->cap_svdd) ? + "on" : "off"); + } + + /* detect if abov exist or not */ + ret = abov_detect(client); + if (ret == 0) { + return -ENODEV; + } + if (ret == 2) { + isForceUpdate = true; + } + + this = kzalloc(sizeof(abovXX_t), GFP_KERNEL); /* create memory for main struct */ + LOG_DBG("\t Initialized Main Memory: 0x%p\n", this); + + if (this) { + /* In case we need to reinitialize data + * (e.q. if suspend reset device) */ + this->init = initialize; + /* pointer to function from platform data to get pendown + * (1->NIRQ=0, 0->NIRQ=1) */ + this->get_nirq_low = pplatData->get_is_nirq_low; + /* save irq in case we need to reference it */ + this->irq = gpio_to_irq(client->irq); + /* do we need to create an irq timer after interrupt ? */ + this->useIrqTimer = 0; + this->board = pplatData; + /* Setup function to call on corresponding reg irq source bit */ + if (MAX_NUM_STATUS_BITS >= 8) { + this->statusFunc[0] = 0; /* TXEN_STAT */ + this->statusFunc[1] = 0; /* UNUSED */ + this->statusFunc[2] = 0; /* UNUSED */ + this->statusFunc[3] = 0;/*read_rawData; CONV_STAT */ + this->statusFunc[4] = 0; /* COMP_STAT */ + this->statusFunc[5] = touchProcess; /* RELEASE_STAT */ + this->statusFunc[6] = touchProcess; /* TOUCH_STAT */ + this->statusFunc[7] = 0; /* RESET_STAT */ + } + + /* setup i2c communication */ + this->bus = client; + i2c_set_clientdata(client, this); + + /* record device struct */ + this->pdev = &client->dev; + + /* create memory for device specific struct */ + this->pDevice = pDevice = kzalloc(sizeof(abov_t), GFP_KERNEL); + LOG_DBG("\t Initialized Device Specific Memory: 0x%p\n", + pDevice); + abov_sar_ptr = this; + if (pDevice) { + /* for accessing items in user data (e.g. calibrate) */ + ret = sysfs_create_group(&client->dev.kobj, &abov_attr_group); + + + /* Check if we hava a platform initialization function to call*/ + if (pplatData->init_platform_hw) + pplatData->init_platform_hw(); + + /* Add Pointer to main platform data struct */ + pDevice->hw = pplatData; + + /* Initialize the button information initialized with keycodes */ + pDevice->pbuttonInformation = pplatData->pbuttonInformation; + + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + /* Create the input device */ + input_ch0 = input_allocate_device(); + if (!input_ch0) + return -ENOMEM; + + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch0->evbit); + input_set_abs_params(input_ch0, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch0 = input_ch0; + input_ch0->name = this->board->cap_ch0_name; + if (input_register_device(input_ch0)) { + LOG_ERR("add ch0 cap touch unsuccess\n"); + return -ENOMEM; + } + /* Create the input device */ + input_ch1 = input_allocate_device(); + if (!input_ch1) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch1->evbit); + input_set_abs_params(input_ch1, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch1 = input_ch1; + /* save the input pointer and finish initialization */ + input_ch1->name = this->board->cap_ch1_name; + if (input_register_device(input_ch1)) { + LOG_INFO("add ch1 cap touch unsuccess\n"); + return -ENOMEM; + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + /* Create the input device */ + input_ch0 = input_allocate_device(); + if (!input_ch0) + return -ENOMEM; + + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch0->evbit); + input_set_abs_params(input_ch0, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch0 = input_ch0; + input_ch0->name = this->board->cap_ch0_name; + if (input_register_device(input_ch0)) { + LOG_ERR("add ch0 cap touch unsuccess\n"); + return -ENOMEM; + } + /* Create the input device */ + input_ch1 = input_allocate_device(); + if (!input_ch1) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch1->evbit); + input_set_abs_params(input_ch1, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch1 = input_ch1; + /* save the input pointer and finish initialization */ + input_ch1->name = this->board->cap_ch1_name; + if (input_register_device(input_ch1)) { + LOG_INFO("add ch1 cap touch unsuccess\n"); + return -ENOMEM; + } + /* Create the input device */ + input_ch2 = input_allocate_device(); + if (!input_ch2) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch2->evbit); + input_set_abs_params(input_ch2, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch2 = input_ch2; + /* save the input pointer and finish initialization */ + input_ch2->name = this->board->cap_ch2_name; + if (input_register_device(input_ch2)) { + LOG_ERR("add ch2t cap touch unsuccess\n"); + return -ENOMEM; + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + /* Create the input device */ + input_ch0 = input_allocate_device(); + if (!input_ch0) + return -ENOMEM; + + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch0->evbit); + input_set_abs_params(input_ch0, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch0 = input_ch0; + input_ch0->name = this->board->cap_ch0_name; + if (input_register_device(input_ch0)) { + LOG_ERR("add ch0 cap touch unsuccess\n"); + return -ENOMEM; + } + /* Create the input device */ + input_ch1 = input_allocate_device(); + if (!input_ch1) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch1->evbit); + input_set_abs_params(input_ch1, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch1 = input_ch1; + /* save the input pointer and finish initialization */ + input_ch1->name = this->board->cap_ch1_name; + if (input_register_device(input_ch1)) { + LOG_INFO("add ch1 cap touch unsuccess\n"); + return -ENOMEM; + } + + /* Create the input device */ + input_ch2 = input_allocate_device(); + if (!input_ch2) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch2->evbit); + input_set_abs_params(input_ch2, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch2 = input_ch2; + /* save the input pointer and finish initialization */ + input_ch2->name = this->board->cap_ch2_name; + if (input_register_device(input_ch2)) { + LOG_ERR("add ch2t cap touch unsuccess\n"); + return -ENOMEM; + } + input_ch3 = input_allocate_device(); + if (!input_ch3) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch3->evbit); + input_set_abs_params(input_ch3, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch3 = input_ch3; + /* save the input pointer and finish initialization */ + input_ch3->name = this->board->cap_ch3_name; + if (input_register_device(input_ch3)) { + LOG_ERR("add ch3t cap touch unsuccess\n"); + return -ENOMEM; + } + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + /* Create the input device */ + input_ch0 = input_allocate_device(); + if (!input_ch0) + return -ENOMEM; + + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch0->evbit); + input_set_abs_params(input_ch0, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch0 = input_ch0; + input_ch0->name = this->board->cap_ch0_name; + if (input_register_device(input_ch0)) { + LOG_ERR("add ch0 cap touch unsuccess\n"); + return -ENOMEM; + } + /* Create the input device */ + input_ch1 = input_allocate_device(); + if (!input_ch1) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch1->evbit); + input_set_abs_params(input_ch1, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch1 = input_ch1; + /* save the input pointer and finish initialization */ + input_ch1->name = this->board->cap_ch1_name; + if (input_register_device(input_ch1)) { + LOG_INFO("add ch1 cap touch unsuccess\n"); + return -ENOMEM; + } + + /* Create the input device */ + input_ch2 = input_allocate_device(); + if (!input_ch2) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch2->evbit); + input_set_abs_params(input_ch2, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch2 = input_ch2; + /* save the input pointer and finish initialization */ + input_ch2->name = this->board->cap_ch2_name; + if (input_register_device(input_ch2)) { + LOG_ERR("add ch2t cap touch unsuccess\n"); + return -ENOMEM; + } + /* Create the input device */ + input_ch3 = input_allocate_device(); + if (!input_ch3) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch3->evbit); + input_set_abs_params(input_ch3, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch3 = input_ch3; + /* save the input pointer and finish initialization */ + input_ch3->name = this->board->cap_ch3_name; + if (input_register_device(input_ch3)) { + LOG_ERR("add ch3t cap touch unsuccess\n"); + return -ENOMEM; + } + /* Create the input device */ + input_ch4 = input_allocate_device(); + if (!input_ch4) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_ch4->evbit); + input_set_abs_params(input_ch4, ABS_DISTANCE, -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_ch4 = input_ch4; + /* save the input pointer and finish initialization */ + input_ch4->name = this->board->cap_ch4_name; + if (input_register_device(input_ch4)) { + LOG_ERR("add ch4t cap touch unsuccess\n"); + return -ENOMEM; + } + } + } + + ret = class_register(&capsense_class); + if (ret < 0) { + LOG_ERR("Create fsys class failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_reset); + if (ret < 0) { + LOG_ERR("Create reset file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_int_state); + if (ret < 0) { + LOG_DBG("Create int_state file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_fw_download_status); + if (ret < 0) { + LOG_DBG("Create fw_download_status file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_enable); + if (ret < 0) { + LOG_ERR("Create enable file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_reg); + if (ret < 0) { + LOG_DBG("Create reg file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_update_fw); + if (ret < 0) { + LOG_ERR("Create update_fw file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_force_update_fw); + if (ret < 0) { + LOG_ERR("Create update_fw file failed (%d)\n", ret); + return ret; + } + /*restore sys/class/capsense label*/ + kobject_uevent(&capsense_class.p->subsys.kobj, KOBJ_CHANGE); + +#ifdef USE_SENSORS_CLASS + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + sensors_capsensor_ch0_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch0_cdev.sensors_poll_delay = NULL; + sensors_capsensor_ch0_cdev.name = this->board->cap_ch0_name; + ret = sensors_classdev_register(&input_ch0->dev, &sensors_capsensor_ch0_cdev); + if (ret < 0) + LOG_ERR("create ch0 cap sensor_class file failed (%d)\n", ret); + + sensors_capsensor_ch1_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch1_cdev.sensors_poll_delay = NULL;; + sensors_capsensor_ch1_cdev.name = this->board->cap_ch1_name; + ret = sensors_classdev_register(&input_ch1->dev, &sensors_capsensor_ch1_cdev); + if (ret < 0) + LOG_ERR("create ch1 cap sensor_class file failed (%d)\n", ret); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + sensors_capsensor_ch0_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch0_cdev.sensors_poll_delay = NULL; + sensors_capsensor_ch0_cdev.name = this->board->cap_ch0_name; + ret = sensors_classdev_register(&input_ch0->dev, &sensors_capsensor_ch0_cdev); + if (ret < 0) + LOG_ERR("create ch0 cap sensor_class file failed (%d)\n", ret); + + sensors_capsensor_ch1_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch1_cdev.sensors_poll_delay = NULL;; + sensors_capsensor_ch1_cdev.name = this->board->cap_ch1_name; + ret = sensors_classdev_register(&input_ch1->dev, &sensors_capsensor_ch1_cdev); + if (ret < 0) + LOG_ERR("create ch1 cap sensor_class file failed (%d)\n", ret); + + sensors_capsensor_ch2_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch2_cdev.sensors_poll_delay = NULL; + sensors_capsensor_ch2_cdev.name = this->board->cap_ch2_name; + ret = sensors_classdev_register(&input_ch2->dev, &sensors_capsensor_ch2_cdev); + if (ret < 0) + LOG_ERR("create ch2 cap sensor_class file failed (%d)\n", ret); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + sensors_capsensor_ch0_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch0_cdev.sensors_poll_delay = NULL; + sensors_capsensor_ch0_cdev.name = this->board->cap_ch0_name; + ret = sensors_classdev_register(&input_ch0->dev, &sensors_capsensor_ch0_cdev); + if (ret < 0) + LOG_ERR("create ch0 cap sensor_class file failed (%d)\n", ret); + + sensors_capsensor_ch1_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch1_cdev.sensors_poll_delay = NULL;; + sensors_capsensor_ch1_cdev.name = this->board->cap_ch1_name; + ret = sensors_classdev_register(&input_ch1->dev, &sensors_capsensor_ch1_cdev); + if (ret < 0) + LOG_ERR("create ch1 cap sensor_class file failed (%d)\n", ret); + + sensors_capsensor_ch2_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch2_cdev.sensors_poll_delay = NULL; + sensors_capsensor_ch2_cdev.name = this->board->cap_ch2_name; + ret = sensors_classdev_register(&input_ch2->dev, &sensors_capsensor_ch2_cdev); + if (ret < 0) + LOG_ERR("create ch2 cap sensor_class file failed (%d)\n", ret); + + sensors_capsensor_ch3_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch3_cdev.sensors_poll_delay = NULL; + sensors_capsensor_ch3_cdev.name = this->board->cap_ch3_name; + ret = sensors_classdev_register(&input_ch3->dev, &sensors_capsensor_ch3_cdev); + if (ret < 0) + LOG_ERR("create ch3 cap sensor_class file failed (%d)\n", ret); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + sensors_capsensor_ch0_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch0_cdev.sensors_poll_delay = NULL; + sensors_capsensor_ch0_cdev.name = this->board->cap_ch0_name; + ret = sensors_classdev_register(&input_ch0->dev, &sensors_capsensor_ch0_cdev); + if (ret < 0) + LOG_ERR("create ch0 cap sensor_class file failed (%d)\n", ret); + + sensors_capsensor_ch1_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch1_cdev.sensors_poll_delay = NULL;; + sensors_capsensor_ch1_cdev.name = this->board->cap_ch1_name; + ret = sensors_classdev_register(&input_ch1->dev, &sensors_capsensor_ch1_cdev); + if (ret < 0) + LOG_ERR("create ch1 cap sensor_class file failed (%d)\n", ret); + + sensors_capsensor_ch2_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch2_cdev.sensors_poll_delay = NULL; + sensors_capsensor_ch2_cdev.name = this->board->cap_ch2_name; + ret = sensors_classdev_register(&input_ch2->dev, &sensors_capsensor_ch2_cdev); + if (ret < 0) + LOG_ERR("create ch2 cap sensor_class file failed (%d)\n", ret); + + sensors_capsensor_ch3_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch3_cdev.sensors_poll_delay = NULL; + sensors_capsensor_ch3_cdev.name = this->board->cap_ch3_name; + ret = sensors_classdev_register(&input_ch3->dev, &sensors_capsensor_ch3_cdev); + if (ret < 0) + LOG_ERR("create ch3 cap sensor_class file failed (%d)\n", ret); + + sensors_capsensor_ch4_cdev.sensors_enable = capsensor_set_enable; + sensors_capsensor_ch4_cdev.sensors_poll_delay = NULL; + sensors_capsensor_ch4_cdev.name = this->board->cap_ch4_name; + ret = sensors_classdev_register(&input_ch4->dev, &sensors_capsensor_ch4_cdev); + if (ret < 0) + LOG_ERR("create ch4 cap sensor_class file failed (%d)\n", ret); + } +#endif + + abovXX_sar_init(this); + + write_register(this, ABOV_CTRL_MODE_REG, ABOV_CTRL_MODE_STOP); + mEnabled = 0; + + INIT_WORK(&this->ps_notify_work, ps_notify_callback_work); + this->ps_notif.notifier_call = ps_notify_callback; + ret = power_supply_reg_notifier(&this->ps_notif); + if (ret) { + LOG_ERR( + "Unable to register ps_notifier: %d\n", ret); + goto free_ps_notifier; + } + + psy = power_supply_get_by_name("usb"); + if (psy) { + ret = ps_get_state(psy, &this->ps_is_present); + if (ret) { + LOG_ERR( + "psy get property failed rc=%d\n", + ret); + goto free_ps_notifier; + } + } + + this->loading_fw = false; + this->fw_dl_status = 1; + if (isForceUpdate == true) { + INIT_WORK(&this->fw_update_work, capsense_fore_update_work); + } else { + INIT_WORK(&this->fw_update_work, capsense_update_work); + } + schedule_work(&this->fw_update_work); + + + LOG_INFO("abov_probe() end\n"); + return 0; + } + return -ENOMEM; + +free_ps_notifier: + LOG_ERR("%s free ps notifier:.\n", __func__); + power_supply_unreg_notifier(&this->ps_notif); + regulator_disable(pplatData->cap_svdd); + regulator_put(pplatData->cap_svdd); + +err_svdd_error: + LOG_ERR("%s svdd defer.\n", __func__); + regulator_disable(pplatData->cap_vdd); + regulator_put(pplatData->cap_vdd); + +err_vdd_defer: + LOG_ERR("%s input free device.\n", __func__); + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_free_device(input_ch0); + input_free_device(input_ch1); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + input_free_device(input_ch0); + input_free_device(input_ch1); + input_free_device(input_ch2); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + input_free_device(input_ch0); + input_free_device(input_ch1); + input_free_device(input_ch2); + input_free_device(input_ch3); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + input_free_device(input_ch0); + input_free_device(input_ch1); + input_free_device(input_ch2); + input_free_device(input_ch3); + input_free_device(input_ch4); + } + + return ret; +} + +/** + * fn static int abov_remove(struct i2c_client *client) + * brief Called when device is to be removed + * param client Pointer to i2c_client struct + * return Value from abovXX_sar_remove() + */ +static int abov_remove(struct i2c_client *client) +{ + pabov_platform_data_t pplatData = 0; + pabov_t pDevice = 0; + pabovXX_t this = i2c_get_clientdata(client); + + pDevice = this->pDevice; + if (this && pDevice) { +#ifdef USE_SENSORS_CLASS + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + sensors_classdev_unregister(&sensors_capsensor_ch0_cdev); + sensors_classdev_unregister(&sensors_capsensor_ch1_cdev); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + sensors_classdev_unregister(&sensors_capsensor_ch0_cdev); + sensors_classdev_unregister(&sensors_capsensor_ch1_cdev); + sensors_classdev_unregister(&sensors_capsensor_ch2_cdev); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + sensors_classdev_unregister(&sensors_capsensor_ch0_cdev); + sensors_classdev_unregister(&sensors_capsensor_ch1_cdev); + sensors_classdev_unregister(&sensors_capsensor_ch2_cdev); + sensors_classdev_unregister(&sensors_capsensor_ch3_cdev); + }else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + sensors_classdev_unregister(&sensors_capsensor_ch0_cdev); + sensors_classdev_unregister(&sensors_capsensor_ch1_cdev); + sensors_classdev_unregister(&sensors_capsensor_ch2_cdev); + sensors_classdev_unregister(&sensors_capsensor_ch3_cdev); + sensors_classdev_unregister(&sensors_capsensor_ch4_cdev); + } +#endif + if (abov_channel_number == ABOV_CHANNEL_NUMBER_TWO) { + input_unregister_device(pDevice->pbuttonInformation->input_ch0); + input_unregister_device(pDevice->pbuttonInformation->input_ch1); + } + if (abov_channel_number == ABOV_CHANNEL_NUMBER_THREE) { + input_unregister_device(pDevice->pbuttonInformation->input_ch0); + input_unregister_device(pDevice->pbuttonInformation->input_ch1); + input_unregister_device(pDevice->pbuttonInformation->input_ch2); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FOUR) { + input_unregister_device(pDevice->pbuttonInformation->input_ch0); + input_unregister_device(pDevice->pbuttonInformation->input_ch1); + input_unregister_device(pDevice->pbuttonInformation->input_ch2); + input_unregister_device(pDevice->pbuttonInformation->input_ch3); + } else if (abov_channel_number == ABOV_CHANNEL_NUMBER_FIVE) { + input_unregister_device(pDevice->pbuttonInformation->input_ch0); + input_unregister_device(pDevice->pbuttonInformation->input_ch1); + input_unregister_device(pDevice->pbuttonInformation->input_ch2); + input_unregister_device(pDevice->pbuttonInformation->input_ch3); + input_unregister_device(pDevice->pbuttonInformation->input_ch4); + } + + if (this->board->cap_svdd_en) { + regulator_disable(this->board->cap_svdd); + regulator_put(this->board->cap_svdd); + } + + if (this->board->cap_vdd_en) { + regulator_disable(this->board->cap_vdd); + regulator_put(this->board->cap_vdd); + } + + sysfs_remove_group(&client->dev.kobj, &abov_attr_group); + pplatData = client->dev.platform_data; + if (pplatData && pplatData->exit_platform_hw) + pplatData->exit_platform_hw(); + kfree(this->pDevice); + } + return abovXX_sar_remove(this); +} + +static int abov_suspend(struct device *dev) +{ + pabovXX_t this = dev_get_drvdata(dev); + + abovXX_suspend(this); + return 0; +} + +static int abov_resume(struct device *dev) +{ + pabovXX_t this = dev_get_drvdata(dev); + + abovXX_resume(this); + return 0; +} +static SIMPLE_DEV_PM_OPS(abov_pm_ops, abov_suspend, abov_resume); + + +#ifdef CONFIG_OF +static const struct of_device_id abov_match_tbl[] = { + { .compatible = "abov,abov_sar_mmi_overlay_7ch" }, + { }, +}; +MODULE_DEVICE_TABLE(of, abov_match_tbl); +#endif + +static struct i2c_device_id abov_idtable[] = { + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, abov_idtable); + +static struct i2c_driver abov_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = of_match_ptr(abov_match_tbl), + .pm = &abov_pm_ops, + }, + .id_table = abov_idtable, + .probe = abov_probe, + .remove = abov_remove, +}; +static int __init abov_init(void) +{ + return i2c_add_driver(&abov_driver); +} +static void __exit abov_exit(void) +{ + i2c_del_driver(&abov_driver); +} + +module_init(abov_init); +module_exit(abov_exit); + +MODULE_AUTHOR("ABOV Corp."); +MODULE_DESCRIPTION("ABOV Capacitive Touch Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.2"); + +#ifdef USE_THREADED_IRQ +static void abovXX_process_interrupt(pabovXX_t this, u8 nirqlow) +{ + if (!this) { + pr_err("abovXX_worker_func, NULL abovXX_t\n"); + return; + } + /* since we are not in an interrupt don't need to disable irq. */ + this->statusFunc[6](this); + if (unlikely(this->useIrqTimer && nirqlow)) { + /* In case we need to send a timer for example on a touchscreen + * checking penup, perform this here + */ + cancel_delayed_work(&this->dworker); + schedule_delayed_work(&this->dworker, msecs_to_jiffies(this->irqTimeout)); + LOG_INFO("Schedule Irq timer"); + } +} + + +static void abovXX_worker_func(struct work_struct *work) +{ + pabovXX_t this = 0; + + if (work) { + this = container_of(work, abovXX_t, dworker.work); + if (!this) { + LOG_ERR("abovXX_worker_func, NULL abovXX_t\n"); + return; + } + if ((!this->get_nirq_low) || (!this->get_nirq_low(this->board->irq_gpio))) { + /* only run if nirq is high */ + abovXX_process_interrupt(this, 0); + } + } else { + LOG_ERR("abovXX_worker_func, NULL work_struct\n"); + } +} +static irqreturn_t abovXX_interrupt_thread(int irq, void *data) +{ + pabovXX_t this = 0; + this = data; + + mutex_lock(&this->mutex); + LOG_DBG("abovXX_irq\n"); + this->int_state = 1; + if ((!this->get_nirq_low) || this->get_nirq_low(this->board->irq_gpio)) + abovXX_process_interrupt(this, 1); + else + LOG_ERR("abovXX_irq - nirq read high\n"); + mutex_unlock(&this->mutex); + return IRQ_HANDLED; +} +#else +static void abovXX_schedule_work(pabovXX_t this, unsigned long delay) +{ + unsigned long flags; + + if (this) { + LOG_DBG("abovXX_schedule_work()\n"); + spin_lock_irqsave(&this->lock, flags); + /* Stop any pending penup queues */ + cancel_delayed_work(&this->dworker); + /* + *after waiting for a delay, this put the job in the kernel-global + workqueue. so no need to create new thread in work queue. + */ + schedule_delayed_work(&this->dworker, delay); + spin_unlock_irqrestore(&this->lock, flags); + } else + LOG_ERR("abovXX_schedule_work, NULL pabovXX_t\n"); +} + +static irqreturn_t abovXX_irq(int irq, void *pvoid) +{ + pabovXX_t this = 0; + if (pvoid) { + this = (pabovXX_t)pvoid; + LOG_DBG("abovXX_irq\n"); + this->int_state = 1; + if ((!this->get_nirq_low) || this->get_nirq_low(this->board->irq_gpio)) { + LOG_DBG("abovXX_irq - Schedule Work\n"); + abovXX_schedule_work(this, 0); + } else + LOG_ERR("abovXX_irq - nirq read high\n"); + } else + LOG_ERR("abovXX_irq, NULL pvoid\n"); + return IRQ_HANDLED; +} + +static void abovXX_worker_func(struct work_struct *work) +{ + pabovXX_t this = 0; + int counter = 0; + u8 nirqLow = 0; + + if (work) { + this = container_of(work, abovXX_t, dworker.work); + + if (!this) { + LOG_ERR("abovXX_worker_func, NULL abovXX_t\n"); + return; + } + if (unlikely(this->useIrqTimer)) { + if ((!this->get_nirq_low) || this->get_nirq_low(this->board->irq_gpio)) + nirqLow = 1; + } + /* since we are not in an interrupt don't need to disable irq. */ + counter = -1; + while ((++counter) < MAX_NUM_STATUS_BITS) { /* counter start from MSB */ + LOG_DBG("Looping Counter %d\n", counter); + if (((status >> counter) & 0x01) && (this->statusFunc[counter])) { + LOG_DBG("Function Pointer Found. Calling\n"); + this->statusFunc[counter](this); + } + } + if (unlikely(this->useIrqTimer && nirqLow)) { + /* Early models and if RATE=0 for newer models require a penup timer */ + /* Queue up the function again for checking on penup */ + abovXX_schedule_work(this, msecs_to_jiffies(this->irqTimeout)); + } + } else { + LOG_ERR("abovXX_worker_func, NULL work_struct\n"); + } +} +#endif + +void abovXX_suspend(pabovXX_t this) +{ + if (this) { + LOG_DBG("ABOV suspend[%d]: disable irq!\n",mEnabled); + disable_irq(this->irq); + /* if upper layer don't disable capsensor, */ + /* we should let it enter sleep in suspend. */ + if (mEnabled) + write_register(this, ABOV_CTRL_MODE_REG, ABOV_CTRL_MODE_SLEEP); + } +} +void abovXX_resume(pabovXX_t this) +{ + if (this) { + LOG_DBG("ABOV resume[%d]: enable irq!\n",mEnabled); + /* we should let capsensor enter active in resume*/ + if (mEnabled) + write_register(this, ABOV_CTRL_MODE_REG, ABOV_CTRL_MODE_ACTIVE); + enable_irq(this->irq); + } +} + +int abovXX_sar_init(pabovXX_t this) +{ + int err = 0; + + if (this && this->pDevice) { +#ifdef USE_THREADED_IRQ + + /* initialize worker function */ + INIT_DELAYED_WORK(&this->dworker, abovXX_worker_func); + + + /* initialize mutex */ + mutex_init(&this->mutex); + /* initailize interrupt reporting */ + this->irq_disabled = 0; + err = request_threaded_irq(this->irq, NULL, abovXX_interrupt_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, this->pdev->driver->name, + this); +#else + /* initialize spin lock */ + spin_lock_init(&this->lock); + + /* initialize worker function */ + INIT_DELAYED_WORK(&this->dworker, abovXX_worker_func); + + /* initailize interrupt reporting */ + this->irq_disabled = 0; + err = request_irq(this->irq, abovXX_irq, IRQF_TRIGGER_FALLING, + this->pdev->driver->name, this); +#endif + if (err) { + LOG_ERR("irq %d busy?\n", this->irq); + return err; + } + this->int_state = 0; +#ifdef USE_THREADED_IRQ + LOG_INFO("registered with threaded irq (%d)\n", this->irq); +#else + LOG_INFO("registered with irq (%d)\n", this->irq); +#endif + /* call init function pointer (this should initialize all registers */ + if (this->init) + return this->init(this); + LOG_ERR("No init function!!!!\n"); + } + return -ENOMEM; +} + +int abovXX_sar_remove(pabovXX_t this) +{ + if (this) { + cancel_delayed_work_sync(&this->dworker); /* Cancel the Worker Func */ + free_irq(this->irq, this); + kfree(this); + return 0; + } + return -ENOMEM; +} diff --git a/drivers/misc/aw9610x/Android.mk b/drivers/misc/aw9610x/Android.mk new file mode 100644 index 000000000000..a8820e94d353 --- /dev/null +++ b/drivers/misc/aw9610x/Android.mk @@ -0,0 +1,19 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(AW9610X_USB_USE_ONLINE),true) + KBUILD_OPTIONS += CONFIG_AW9610X_POWER_SUPPLY_ONLINE=y +endif + +ifeq ($(AW9610X_HARDWARE_VERSION),true) + KBUILD_OPTIONS += CONFIG_AW9610X_HARDWARE_VERSION=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := aw9610x.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/sensors_class.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +KBUILD_OPTIONS += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/aw9610x/Kbuild b/drivers/misc/aw9610x/Kbuild new file mode 100644 index 000000000000..e1ad7ee0d2cf --- /dev/null +++ b/drivers/misc/aw9610x/Kbuild @@ -0,0 +1,34 @@ +KERNEL_DIR_PATH = $(shell find $(ANDROID_BUILD_TOP)/kernel/ -name "msm-*" -type d | head -1 | grep -i "kernel/msm-*") + +KERNEL_VERSION_NAME = $(shell basename $(KERNEL_DIR_PATH)) + +KERNEL_PLATFORM_PATH = $(shell find $(ANDROID_BUILD_TOP) -maxdepth 1 -name "kernel_platform" -type d) + +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +ifneq ($(filter m y,$(CONFIG_AW9610X_POWER_SUPPLY_ONLINE)),) + EXTRA_CFLAGS += -DCONFIG_USE_POWER_SUPPLY_ONLINE +endif + +ifneq ($(filter m y,$(CONFIG_AW9610X_HARDWARE_VERSION)),) + EXTRA_CFLAGS += -DCONFIG_USE_HARDWARE_VERSION +endif + + +ifneq (,$(KERNEL_DIR_PATH)) + EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/kernel/$(KERNEL_VERSION_NAME)/drivers/base +else ifneq (,$(KERNEL_PLATFORM_PATH)) + EXTRA_CFLAGS += -I$(KERNEL_PLATFORM_PATH)/common/drivers/base +else + EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/kernel/drivers/base +endif + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers + +obj-m := aw9610x.o +aw9610x-objs += aw9610x_sar.o +aw9610x-objs += aw_bin_parse.o + + diff --git a/drivers/misc/aw9610x/Makefile b/drivers/misc/aw9610x/Makefile new file mode 100644 index 000000000000..13af5267daf0 --- /dev/null +++ b/drivers/misc/aw9610x/Makefile @@ -0,0 +1,33 @@ +ifeq ($(strip $(MODULE_KERNEL_VERSION)), 5.15) + +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers + +else + +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + +endif diff --git a/drivers/misc/aw9610x/aw9610x_sar.c b/drivers/misc/aw9610x/aw9610x_sar.c new file mode 100644 index 000000000000..ee891c870cfe --- /dev/null +++ b/drivers/misc/aw9610x/aw9610x_sar.c @@ -0,0 +1,2389 @@ +/* +* aw9610x.c +* +* Copyright (c) 2020 AWINIC Technology CO., LTD +* +* Author: Alex +* +* 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. +*/ +#define USE_SENSORS_CLASS + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "base.h" + +#define AW9610X_I2C_NAME "aw9610x_sar" +#define AW9610X_DRIVER_VERSION "v0.1.9.3" + +#define AW_READ_CHIPID_RETRIES (5) +#define AW_I2C_RETRIES (5) +#define AW9610X_SCAN_DEFAULT_TIME (10000) +#define CALI_FILE_MAX_SIZE (128) + +#define LOG_TAG "" + +#define LOG_INFO(fmt, args...) pr_info(LOG_TAG "[INFO]" "<%s><%d>"fmt, __func__, __LINE__, ##args) +#define LOG_DBG(fmt, args...) pr_debug(LOG_TAG "[DBG]" "<%s><%d>"fmt, __func__, __LINE__, ##args) +#define LOG_ERR(fmt, args...) pr_err(LOG_TAG "[ERR]" "<%s><%d>"fmt, __func__, __LINE__, ##args) + +struct aw9610x *g_aw9610x; + +/****************************************************** +* +* aw9610x i2c write/read +* +******************************************************/ +static int32_t +i2c_write(struct aw9610x *aw9610x, uint16_t reg_addr16, uint32_t reg_data32) +{ + int32_t ret = -ENOMEM; + struct i2c_client *i2c = aw9610x->i2c; + struct i2c_msg msg; + uint8_t w_buf[6]; + + /*reg_addr*/ + w_buf[0] = (u8)(reg_addr16 >> 8); + w_buf[1] = (u8)(reg_addr16); + /*data*/ + w_buf[2] = (u8)(reg_data32 >> 24); + w_buf[3] = (u8)(reg_data32 >> 16); + w_buf[4] = (u8)(reg_data32 >> 8); + w_buf[5] = (u8)(reg_data32); + + msg.addr = i2c->addr; + msg.flags = AW9610X_I2C_WR; + msg.len = 6; + /*2 bytes regaddr + 4 bytes data*/ + msg.buf = (unsigned char *)w_buf; + + ret = i2c_transfer(i2c->adapter, &msg, 1); + if (ret < 0) + LOG_ERR("Write reg is 0x%x,error value = %d", reg_addr16, ret); + + return ret; +} + +static int32_t +i2c_read(struct aw9610x *aw9610x, uint16_t reg_addr16, uint32_t *reg_data32) +{ + int32_t ret = -ENOMEM; + struct i2c_client *i2c = aw9610x->i2c; + struct i2c_msg msg[2]; + uint8_t w_buf[2]; + uint8_t buf[4]; + + w_buf[0] = (unsigned char)(reg_addr16 >> 8); + w_buf[1] = (unsigned char)(reg_addr16); + msg[0].addr = i2c->addr; + msg[0].flags = AW9610X_I2C_WR; + msg[0].len = 2; + msg[0].buf = (unsigned char *)w_buf; + + msg[1].addr = i2c->addr; + msg[1].flags = AW9610X_I2C_RD; + msg[1].len = 4; + msg[1].buf = (unsigned char *)buf; + + ret = i2c_transfer(i2c->adapter, msg, 2); + if (ret < 0) + LOG_ERR("Read reg is 0x%x,error value = %d", reg_addr16, ret); + + reg_data32[0] = ((u32)buf[3]) | ((u32)buf[2]<<8) | + ((u32)buf[1]<<16) | ((u32)buf[0]<<24); + + return ret; +} + +static int32_t aw9610x_i2c_write(struct aw9610x *aw9610x, + uint16_t reg_addr16, uint32_t reg_data32) +{ + int32_t ret = -1; + uint8_t cnt = 0; + + while (cnt < AW_I2C_RETRIES) { + ret = i2c_write(aw9610x, reg_addr16, reg_data32); + if (ret < 0) + LOG_ERR("write cnt = %d,error = %d", cnt, ret); + else + break; + + cnt++; + } + + return ret; +} + +static int32_t aw9610x_i2c_read(struct aw9610x *aw9610x, + uint16_t reg_addr16, uint32_t *reg_data32) +{ + int32_t ret = -1; + uint8_t cnt = 0; + + while (cnt < AW_I2C_RETRIES) { + ret = i2c_read(aw9610x, reg_addr16, reg_data32); + if (ret < 0) + LOG_ERR("i2c_read cnt=%d error=%d", cnt, ret); + else + break; + cnt++; + } + + return ret; +} + +static int32_t +aw9610x_i2c_write_bits(struct aw9610x *aw9610x, uint16_t reg_addr16, + uint32_t mask, uint32_t reg_data32) +{ + uint32_t reg_val; + + aw9610x_i2c_read(aw9610x, reg_addr16, ®_val); + reg_val &= mask; + reg_val |= reg_data32; + aw9610x_i2c_write(aw9610x, reg_addr16, reg_val); + + return 0; +} + +/****************************************************************************** +* +* aw9610x i2c sequential write/read --- one first addr with multiple data. +* +******************************************************************************/ +static int32_t i2c_write_seq(struct aw9610x *aw9610x) +{ + int32_t ret = -ENOMEM; + struct i2c_client *i2c = aw9610x->i2c; + struct i2c_msg msg; + uint8_t w_buf[228]; + uint8_t addr_bytes = aw9610x->aw_i2c_package.addr_bytes; + uint8_t msg_cnt = 0; + uint8_t data_bytes = aw9610x->aw_i2c_package.data_bytes; + uint8_t reg_num = aw9610x->aw_i2c_package.reg_num; + uint8_t *p_reg_data = aw9610x->aw_i2c_package.p_reg_data; + uint8_t msg_idx = 0; + + for (msg_idx = 0; msg_idx < addr_bytes; msg_idx++) { + w_buf[msg_idx] = aw9610x->aw_i2c_package.init_addr[msg_idx]; + LOG_DBG("w_buf_addr[%d] = 0x%02x",msg_idx, w_buf[msg_idx]); + } + msg_cnt = addr_bytes; + for (msg_idx = 0; msg_idx < data_bytes * reg_num; msg_idx++) { + w_buf[msg_cnt] = *p_reg_data++; + msg_cnt++; + } + LOG_DBG("%d", msg_cnt); + p_reg_data = aw9610x->aw_i2c_package.p_reg_data; + msg.addr = i2c->addr; + msg.flags = AW9610X_I2C_WR; + msg.len = msg_cnt; + msg.buf = (uint8_t *)w_buf; + ret = i2c_transfer(i2c->adapter, &msg, 1); + if (ret < 0) + LOG_ERR("i2c write seq error %d", ret); + + return ret; +} + +static int32_t i2c_read_seq(struct aw9610x *aw9610x, uint8_t *reg_data) +{ + int32_t ret = -ENOMEM; + struct i2c_client *i2c = aw9610x->i2c; + struct i2c_msg msg[2]; + uint8_t w_buf[4]; + uint8_t buf[228]; + uint8_t data_bytes = aw9610x->aw_i2c_package.data_bytes; + uint8_t reg_num = aw9610x->aw_i2c_package.reg_num; + uint8_t addr_bytes = aw9610x->aw_i2c_package.addr_bytes; + uint8_t msg_idx = 0; + uint8_t msg_cnt = 0; + + /* + * step 1 : according to addr_bytes assemble first_addr. + * step 2 : initialize msg[0] including first_addr transfer to client. + * step 3 : wait for client return reg_data. + */ + for (msg_idx = 0; msg_idx < addr_bytes; msg_idx++) { + w_buf[msg_idx] = aw9610x->aw_i2c_package.init_addr[msg_idx]; + LOG_DBG("w_buf_addr[%d] = 0x%02x",msg_idx, w_buf[msg_idx]); + } + msg[0].addr = i2c->addr; + msg[0].flags = AW9610X_I2C_WR; + msg[0].len = msg_idx; + msg[0].buf = (uint8_t *)w_buf; + + /* + * recieve client to msg[1].buf. + */ + msg_cnt = data_bytes * reg_num; + msg[1].addr = i2c->addr; + msg[1].flags = AW9610X_I2C_RD; + msg[1].len = msg_cnt; + msg[1].buf = (uint8_t *)buf; + + ret = i2c_transfer(i2c->adapter, msg, 2); + if (ret < 0) { + LOG_ERR("i2c write error %d", ret); + return ret; + } + + for (msg_idx = 0; msg_idx < msg_cnt; msg_idx++) { + reg_data[msg_idx] = buf[msg_idx]; + LOG_DBG("buf = 0x%02x", buf[msg_idx]); + } + + return ret; +} + +static void +aw9610x_addrblock_load(struct device *dev, const char *buf) +{ + uint32_t addrbuf[4] = { 0 }; + uint8_t temp_buf[2] = { 0 }; + uint32_t i = 0; + struct aw9610x *aw9610x = dev_get_drvdata(dev); + uint8_t addr_bytes = aw9610x->aw_i2c_package.addr_bytes; + uint8_t reg_num = aw9610x->aw_i2c_package.reg_num; + + for (i = 0; i < addr_bytes; i++) { + if (reg_num < attr_buf[1]) { + temp_buf[0] = buf[attr_buf[0] + i * 5]; + temp_buf[1] = buf[attr_buf[0] + i * 5 + 1]; + } else if (reg_num >= attr_buf[1] && reg_num < attr_buf[3]) { + temp_buf[0] = buf[attr_buf[2] + i * 5]; + temp_buf[1] = buf[attr_buf[2] + i * 5 + 1]; + } else if (reg_num >= attr_buf[3] && reg_num < attr_buf[5]) { + temp_buf[0] = buf[attr_buf[4] + i * 5]; + temp_buf[1] = buf[attr_buf[4] + i * 5 + 1]; + } + if (sscanf(temp_buf, "%02x", &addrbuf[i]) == 1) + aw9610x->aw_i2c_package.init_addr[i] = + (uint8_t)addrbuf[i]; + } +} +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) +/****************************************************** + * + *the document of storage_spedata + * + ******************************************************/ +static int32_t aw9610x_filedata_deal(struct aw9610x *aw9610x) +{ + struct file *fp = NULL; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + mm_segment_t fs; +#endif + int8_t *buf; + int8_t temp_buf[8] = { 0 }; + uint8_t i = 0; + uint8_t j = 0; + int32_t ret; + uint32_t nv_flag = 0; + uint8_t cali_file_name[20] = { 0 }; + + snprintf(cali_file_name, sizeof(cali_file_name), "aw_cali_%d.bin", aw9610x->sar_num); + LOG_INFO("cali_file_name : %s", cali_file_name); + + fp = filp_open(cali_file_name, O_RDWR | O_CREAT, 0644); + if (IS_ERR(fp)) { + LOG_ERR("open failed!"); + return -EINVAL; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + fs = get_fs(); + set_fs(KERNEL_DS); +#endif + + buf = (char *)kzalloc(CALI_FILE_MAX_SIZE, GFP_KERNEL); + if (!buf) { + LOG_ERR("malloc failed!"); + filp_close(fp, NULL); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + set_fs(fs); +#endif + return -EINVAL; + } + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + ret = vfs_read(fp, buf, CALI_FILE_MAX_SIZE, &(fp->f_pos)); +#else + ret = kernel_read(fp, buf, CALI_FILE_MAX_SIZE, &(fp->f_pos)); +#endif + + if (ret < 0) { + LOG_ERR("read failed"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + set_fs(fs); +#endif + aw9610x->cali_flag = AW_CALI; + return ret; + } else if (ret == 0) { + LOG_ERR("read len = 0"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + set_fs(fs); +#endif + aw9610x->cali_flag = AW_CALI; + return ret; + } else { + for (i = 0; i < AW_SPE_REG_NUM; i++) { + for (j = 0; j < AW_SPE_REG_DWORD; j++) + temp_buf[j] = buf[AW_SPE_REG_DWORD * i + j]; + + if (sscanf(temp_buf, "%08x", + &aw9610x->nvspe_data[i]) == 1) + LOG_DBG("nv_spe_data[%d] = 0x%08x",i, aw9610x->nvspe_data[i]); + } + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + set_fs(fs); +#endif + filp_close(fp, NULL); + kfree(buf); + + /* nvspe_datas come from nv*/ + for (i = 0; i < AW_SPE_REG_NUM; i++) { + nv_flag |= aw9610x->nvspe_data[i]; + if (nv_flag != 0) + break; + } + + if (nv_flag == 0) { + aw9610x->cali_flag = AW_CALI; + LOG_INFO("the chip need to cali! nv_flag = 0x%08x", nv_flag); + } else { + aw9610x->cali_flag = AW_NO_CALI; + LOG_INFO("chip not need to cali! nv_flag = 0x%08x", nv_flag); + } + + return 0; +} + +static int32_t +aw9610x_store_spedata_to_file(struct aw9610x *aw9610x, char *buf) +{ + struct file *fp = NULL; + loff_t pos = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + mm_segment_t fs; +#endif + uint8_t cali_file_name[20] = { 0 }; + + LOG_DBG("buf = %s", buf); + + snprintf(cali_file_name, 20, "aw_cali_%d.bin", aw9610x->sar_num); + LOG_INFO("cali_file_name : %s", cali_file_name); + + fp = filp_open(cali_file_name, O_RDWR | O_CREAT, 0644); + if (IS_ERR(fp)) { + LOG_ERR("open failed!"); + return -EINVAL; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + fs = get_fs(); + set_fs(KERNEL_DS); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + vfs_write(fp, (char __user *)buf, strlen(buf), &pos); +#else + kernel_write(fp, (char __user *)buf, strlen(buf), &pos); +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + set_fs(fs); +#endif + + LOG_INFO("write successfully!"); + + filp_close(fp, NULL); + return 0; +} + +/****************************************************** + * + *configuration of special reg + * + ******************************************************/ +static void aw9610x_get_calidata(struct aw9610x *aw9610x) +{ + uint8_t i = 0; + uint32_t buf_size = 0; + int32_t ret; + uint32_t reg_val = 0; + uint8_t temp_buf[9] = { 0 }; + uint8_t buf[CALI_FILE_MAX_SIZE] = { 0 }; + + LOG_DBG("enter"); + + /*class 1 special reg*/ + for (i = 0; i < AW_CLA1_SPE_REG_NUM; i++) + aw9610x_i2c_read(aw9610x, + REG_AFECFG1_CH0 + i * AW_CL1SPE_CALI_OS, &aw9610x->spedata[i]); + + /*class 2 special reg*/ + for (; i < AW_SPE_REG_NUM; i++) + aw9610x_i2c_read(aw9610x, + REG_REFACFG + (i - aw9610x->aw_channel_number) * + AW_CL2SPE_CALI_OS, &aw9610x->spedata[i]); + + for (i = AW_CLA1_SPE_REG_NUM; i < AW_SPE_REG_NUM; i++) { + ret = aw9610x->spedata[i] & 0x07; + if (aw9610x->aw_channel_number == AW_CHANNEL_NUMBER_TWO) { + if(AW_CHANNEL0 == ret){ + aw9610x_i2c_read(aw9610x, REG_VALID_CH0, ®_val); + }else if(AW_CHANNEL1 == ret){ + aw9610x_i2c_read(aw9610x, REG_VALID_CH1, ®_val); + } + } else if (aw9610x->aw_channel_number == AW_CHANNEL_NUMBER_THREE) { + if(AW_CHANNEL0 == ret){ + aw9610x_i2c_read(aw9610x, REG_VALID_CH0, ®_val); + }else if(AW_CHANNEL1 == ret){ + aw9610x_i2c_read(aw9610x, REG_VALID_CH1, ®_val); + }else if(AW_CHANNEL2 == ret){ + aw9610x_i2c_read(aw9610x, REG_VALID_CH2, ®_val); + } + } else if (aw9610x->aw_channel_number == AW_CHANNEL_NUMBER_FOUR) { + if(AW_CHANNEL0 == ret){ + aw9610x_i2c_read(aw9610x, REG_VALID_CH0, ®_val); + }else if(AW_CHANNEL1 == ret){ + aw9610x_i2c_read(aw9610x, REG_VALID_CH1, ®_val); + }else if(AW_CHANNEL2 == ret){ + aw9610x_i2c_read(aw9610x, REG_VALID_CH2, ®_val); + }else if(AW_CHANNEL3 == ret){ + aw9610x_i2c_read(aw9610x, REG_VALID_CH3, ®_val); + } + } else if (aw9610x->aw_channel_number == AW_CHANNEL_NUMBER_FIVE) { + if(AW_CHANNEL0 == ret){ + aw9610x_i2c_read(aw9610x, REG_VALID_CH0, ®_val); + }else if(AW_CHANNEL1 == ret){ + aw9610x_i2c_read(aw9610x, REG_VALID_CH1, ®_val); + }else if(AW_CHANNEL2 == ret){ + aw9610x_i2c_read(aw9610x, REG_VALID_CH2, ®_val); + }else if(AW_CHANNEL3 == ret){ + aw9610x_i2c_read(aw9610x, REG_VALID_CH3, ®_val); + }else if(AW_CHANNEL4 == ret){ + aw9610x_i2c_read(aw9610x, REG_VALID_CH4, ®_val); + } + } + aw9610x->spedata[i] = ((reg_val >> 6) & 0x03fffff0) | + (aw9610x->spedata[i] & 0xfc00000f); + } + /* spedatas come from register*/ + + /* write spedatas to nv */ + for (i = 0; i < AW_SPE_REG_NUM; i++) { + snprintf(temp_buf, sizeof(temp_buf), "%08x", + aw9610x->spedata[i]); + memcpy(buf + buf_size, temp_buf, strlen(temp_buf)); + buf_size = strlen(buf); + } + ret = aw9610x_store_spedata_to_file(aw9610x, buf); + if (ret < 0) { + LOG_ERR("store spedata failed"); + return; + } + + LOG_DBG("successfully write_spereg_to_file"); +} + +static void aw9610x_class1_reg(struct aw9610x *aw9610x) +{ + int32_t i = 0; + uint32_t reg_val; + + LOG_DBG("enter"); + + for (i = 0; i < AW_CLA1_SPE_REG_NUM; i++) { + reg_val = (aw9610x->nvspe_data[i] >> 16) & 0x0000ffff; + aw9610x_i2c_write_bits(aw9610x, REG_INITPROX0_CH0 + + i * AW_CL1SPE_DEAL_OS, ~(0xffff), reg_val); + } +} + +static void aw9610x_class2_reg(struct aw9610x *aw9610x) +{ + int32_t i = 0; + + LOG_DBG("enter"); + + for (i = AW_CLA1_SPE_REG_NUM; i < AW_SPE_REG_NUM; i++) { + aw9610x_i2c_write(aw9610x, + REG_REFACFG + (i - AW_CLA1_SPE_REG_NUM) * AW_CL2SPE_DEAL_OS, + aw9610x->nvspe_data[i]); + } +} + +static void aw9610x_spereg_deal(struct aw9610x *aw9610x) +{ + LOG_DBG("enter!"); + + aw9610x_class1_reg(aw9610x); + aw9610x_class2_reg(aw9610x); +} +#endif + +static void aw9610x_datablock_load(struct device *dev, const char *buf) +{ + uint32_t i = 0; + uint8_t reg_data[220] = { 0 }; + uint32_t databuf[220] = { 0 }; + uint8_t temp_buf[2] = { 0 }; + struct aw9610x *aw9610x = dev_get_drvdata(dev); + uint8_t addr_bytes = aw9610x->aw_i2c_package.addr_bytes; + uint8_t data_bytes = aw9610x->aw_i2c_package.data_bytes; + uint8_t reg_num = aw9610x->aw_i2c_package.reg_num; + + for (i = 0; i < data_bytes * reg_num; i++) { + if (reg_num < attr_buf[1]) { + temp_buf[0] = buf[attr_buf[0] + (addr_bytes + i) * 5]; + temp_buf[1] = + buf[attr_buf[0] + (addr_bytes + i) * 5 + 1]; + } else if (reg_num >= attr_buf[1] && reg_num < attr_buf[3]) { + temp_buf[0] = buf[attr_buf[2] + (addr_bytes + i) * 5]; + temp_buf[1] = + buf[attr_buf[2] + (addr_bytes + i) * 5 + 1]; + } else if (reg_num >= attr_buf[3] && reg_num < attr_buf[5]) { + temp_buf[0] = buf[attr_buf[4] + (addr_bytes + i) * 5]; + temp_buf[1] = + buf[attr_buf[4] + (addr_bytes + i) * 5 + 1]; + } + sscanf(temp_buf, "%02x", &databuf[i]); + reg_data[i] = (uint8_t)databuf[i]; + } + aw9610x->aw_i2c_package.p_reg_data = reg_data; + i2c_write_seq(aw9610x); +} +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) +static void aw9610x_power_on_prox_detection(struct aw9610x *aw9610x) +{ + int32_t ret = 0; + uint32_t reg_data = 0; + uint32_t temp_time = AW9610X_SCAN_DEFAULT_TIME; + + LOG_DBG("enter"); + + ret = aw9610x_filedata_deal(aw9610x); + if ((aw9610x->cali_flag == AW_NO_CALI) && (ret >= 0)) + aw9610x_spereg_deal(aw9610x); + + aw9610x_i2c_write(aw9610x, REG_HOSTIRQEN, 0); + aw9610x_i2c_write(aw9610x, REG_CMD, 0x0001); + while ((temp_time)--) { + aw9610x_i2c_read(aw9610x, REG_HOSTIRQSRC, ®_data); + reg_data = (reg_data >> 4) & 0x01; + if (reg_data == 1) { + LOG_DBG("time = %d", temp_time); + if ((aw9610x->cali_flag == AW_CALI) && ret >= 0) + aw9610x_get_calidata(aw9610x); + break; + } + msleep(1); + } + aw9610x_i2c_read(aw9610x, REG_STAT2, ®_data); + if (reg_data & 0x10000) + aw9610x->power_prox = 1; +} +#endif + +static void aw9610x_channel_scan_start(struct aw9610x *aw9610x) +{ + LOG_DBG("enter"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + if (aw9610x->pwprox_dete == true) { + aw9610x_power_on_prox_detection(aw9610x); + } else { + aw9610x_i2c_write(aw9610x, REG_CMD, AW9610X_ACTIVE_MODE); + } +#else + aw9610x_i2c_write(aw9610x, REG_CMD, AW9610X_ACTIVE_MODE); +#endif + aw9610x_i2c_write(aw9610x, REG_HOSTIRQEN, aw9610x->hostirqen); + aw9610x->mode = AW9610X_ACTIVE_MODE; +} + +static void aw9610x_bin_valid_loaded(struct aw9610x *aw9610x, + struct aw_bin *aw_bin_data_s) +{ + uint32_t i; + int32_t ret = 0; + uint16_t reg_addr; + uint32_t reg_data; + uint32_t start_addr = aw_bin_data_s->header_info[0].valid_data_addr; + + for (i = 0; i < aw_bin_data_s->header_info[0].valid_data_len; + i += 6, start_addr += 6) { + reg_addr = (aw_bin_data_s->info.data[start_addr]) | + aw_bin_data_s->info.data[start_addr + 1] << 8; + reg_data = aw_bin_data_s->info.data[start_addr + 2] | + (aw_bin_data_s->info.data[start_addr + 3] << 8) | + (aw_bin_data_s->info.data[start_addr + 4] << 16) | + (aw_bin_data_s->info.data[start_addr + 5] << 24); + if ((reg_addr == REG_EEDA0) || (reg_addr == REG_EEDA1)) + continue; + if (reg_addr == REG_HOSTIRQEN) { + aw9610x->hostirqen = reg_data; + continue; + } + ret = aw9610x_i2c_write(aw9610x, reg_addr, reg_data); + if (ret < 0) + return ; + + LOG_DBG("reg_addr = 0x%04x, reg_data = 0x%08x", + reg_addr, reg_data); + } + LOG_INFO("bin writen completely"); + + aw9610x_channel_scan_start(aw9610x); +} + +/*************************************************************************** +* para loaded +****************************************************************************/ +static int32_t aw9610x_para_loaded(struct aw9610x *aw9610x) +{ + int32_t i = 0; + int32_t len = ARRAY_SIZE(aw9610x_reg_default); + + LOG_DBG("start to download para!"); + + for (i = 0; i < len; i = i + 2) { + aw9610x_i2c_write(aw9610x, + (uint16_t)aw9610x_reg_default[i], + aw9610x_reg_default[i+1]); + if (aw9610x_reg_default[i] == REG_HOSTIRQEN) + aw9610x->hostirqen = aw9610x_reg_default[i+1]; + LOG_DBG("reg_addr = 0x%04x, reg_data = 0x%08x",aw9610x_reg_default[i],aw9610x_reg_default[i+1]); + } + LOG_INFO("para writen completely"); + + aw9610x_channel_scan_start(aw9610x); + + return 0; +} + +static void +aw9610x_cfg_all_loaded(const struct firmware *cont, void *context) +{ + int32_t ret; + struct aw_bin *aw_bin; + struct aw9610x *aw9610x = context; + + LOG_DBG("enter"); + + if (!cont) { + LOG_ERR("%s request failed", aw9610x->cfg_name); + release_firmware(cont); + return; + } else { + LOG_INFO("%s request successfully", aw9610x->cfg_name); + } + + aw_bin = kzalloc(cont->size + sizeof(struct aw_bin), GFP_KERNEL); + if (!aw_bin) { + kfree(aw_bin); + release_firmware(cont); + LOG_ERR("failed to allcating memory!"); + return; + } + aw_bin->info.len = cont->size; + memcpy(aw_bin->info.data, cont->data, cont->size); + ret = aw_parsing_bin_file(aw_bin); + if (ret < 0) { + LOG_ERR("[:aw9610x parse bin fail! ret = %d", ret); + kfree(aw_bin); + release_firmware(cont); + return; + } + + aw9610x_bin_valid_loaded(aw9610x, aw_bin); + kfree(aw_bin); + release_firmware(cont); +} + +static int32_t aw9610x_cfg_update(struct aw9610x *aw9610x) +{ + LOG_DBG("enter"); + + if (aw9610x->firmware_flag == true) { +#ifdef CONFIG_USE_HARDWARE_VERSION + snprintf(aw9610x->cfg_name, sizeof(aw9610x->cfg_name), + "aw9610x_%d.bin", aw9610x->hw_version); +#else + snprintf(aw9610x->cfg_name, sizeof(aw9610x->cfg_name), + "aw9610x_%d.bin", aw9610x->sar_num); +#endif + + LOG_INFO("aw9610x_cfg_update cfg_name = %s", aw9610x->cfg_name); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, +#else + request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, +#endif + aw9610x->cfg_name, + aw9610x->dev, + GFP_KERNEL, + aw9610x, + aw9610x_cfg_all_loaded); + } else { + aw9610x_para_loaded(aw9610x); + } + + return AW_SAR_SUCCESS; +} + +static void aw9610x_cfg_work_routine(struct work_struct *work) +{ + struct aw9610x + *aw9610x = container_of(work, struct aw9610x, cfg_work.work); + + LOG_DBG("enter"); + + aw9610x_cfg_update(aw9610x); +} + +static int32_t +aw9610x_sar_cfg_init(struct aw9610x *aw9610x, int32_t flag) +{ + uint32_t cfg_timer_val = 0; + uint32_t i = 0; + + LOG_DBG("enter"); + + if (flag == AW_CFG_LOADED) + cfg_timer_val = 20; + else if (flag == AW_CFG_UNLOAD) + cfg_timer_val = 5000; + else + return -AW_CFG_LOAD_TIME_FAILED; + + INIT_DELAYED_WORK(&aw9610x->cfg_work, aw9610x_cfg_work_routine); + schedule_delayed_work(&aw9610x->cfg_work, + msecs_to_jiffies(cfg_timer_val)); + + for (i = 0; i < aw9610x->aw_channel_number; i++) { + aw9610x->aw_pad[i].curr_state = 0; + aw9610x->aw_pad[i].last_state = 0; + } + + return AW_SAR_SUCCESS; +} + +/***************************************************** + * + * first irq clear + * + *****************************************************/ +static int32_t aw9610x_init_irq_handle(struct aw9610x *aw9610x) +{ + uint8_t cnt = 20; + uint32_t reg_data; + + LOG_DBG("enter"); + + while (cnt--) { + aw9610x_i2c_read(aw9610x, REG_HOSTIRQSRC, ®_data); + aw9610x->first_irq_flag = reg_data & 0x01; + if (aw9610x->first_irq_flag == 1) { + LOG_DBG("cnt = %d", cnt); + return AW_SAR_SUCCESS; + } + } + LOG_ERR("hardware has trouble!"); + + return -AW_IRQIO_FAILED; +} + +/***************************************************** + * + * software reset + * + *****************************************************/ +static void aw9610x_sw_reset(struct aw9610x *aw9610x) +{ + LOG_DBG("enter"); + + aw9610x_i2c_write(aw9610x, REG_HOSTCTRL2, 0); + msleep(20); +} + +static int32_t aw9610x_baseline_filter(struct aw9610x *aw9610x) +{ + int32_t ret = 0; + uint8_t i = 0; + uint32_t status0 = 0; + uint32_t status1 = 0; + + ret = aw9610x_i2c_read(aw9610x, REG_STAT1, &status1); + if (ret < 0) + return ret; + ret = aw9610x_i2c_read(aw9610x, REG_STAT0, &status0); + if (ret < 0) + return ret; + + for (i = 0; i < aw9610x->aw_channel_number; i++) { + if (((status1 >> i) & 0x01) == 1) { + if (aw9610x->satu_flag[i] == 0) { + ret = aw9610x_i2c_read(aw9610x, + REG_BLFILT1_CH0 + i * AW_CL1SPE_DEAL_OS, + &aw9610x->satu_data[i]); + if (ret < 0) + return ret; + ret = aw9610x_i2c_write(aw9610x, + REG_BLFILT1_CH0 + i * AW_CL1SPE_DEAL_OS, + ((aw9610x->satu_data[i] | 0x1fc) & 0x3fffffff)); + if (ret < 0) + return ret; + aw9610x->satu_flag[i] = 1; + } + } else if (((status1 >> i) & 0x01) == 0) { + if (aw9610x->satu_flag[i] == 1) { + if (((status0 >> (i + 24)) & 0x01) == 0) { + ret = aw9610x_i2c_write(aw9610x, + REG_BLFILT1_CH0 + i * AW_CL1SPE_DEAL_OS, + aw9610x->satu_data[i]); + if (ret < 0) + return ret; + aw9610x->satu_flag[i] = 0; + } + } + } + } + + return ret; +} + +static void aw9610x_saturat_release_handle(struct aw9610x *aw9610x) +{ + uint32_t satu_irq = 0; + uint8_t i = 0; + int32_t ret = 0; + uint32_t status0 = 0; + + LOG_DBG("enter"); + + satu_irq = (aw9610x->irq_status >> 7) & 0x01; + if (satu_irq == 1) { + ret = aw9610x_baseline_filter(aw9610x); + if (ret < 0) + return; + } else { + ret = aw9610x_i2c_read(aw9610x, REG_STAT0, &status0); + if (ret < 0) + return; + for (i = 0; i < aw9610x->aw_channel_number; i++) { + if (aw9610x->satu_flag[i] == 1) { + if (((status0 >> (i + 24)) & 0x01) == 0) { + ret = aw9610x_i2c_write(aw9610x, + REG_BLFILT1_CH0 + i * AW_CL1SPE_DEAL_OS, + aw9610x->satu_data[i]); + if (ret < 0) + return; + aw9610x->satu_flag[i] = 0; + } + } + } + } + + LOG_INFO("satu_irq handle over!"); +} + +/****************************************************** + * + * sys group attribute + * + ******************************************************/ +static ssize_t aw9610x_set_reg(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw9610x *aw9610x = dev_get_drvdata(dev); + uint32_t databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) + aw9610x_i2c_write(aw9610x, (uint16_t)databuf[0], + (uint32_t)databuf[1]); + + return count; +} + +static ssize_t aw9610x_get_reg(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw9610x *aw9610x = dev_get_drvdata(dev); + ssize_t len = 0; + uint32_t i = 0; + uint32_t reg_val = 0; + uint32_t reg_num = 0; + + reg_num = ARRAY_SIZE(aw9610x_reg_access); + for (i = 0; i < reg_num; i++) { + if (aw9610x_reg_access[i].rw & REG_RD_ACCESS) { + aw9610x_i2c_read(aw9610x, aw9610x_reg_access[i].reg, + ®_val); + len += snprintf(buf + len, PAGE_SIZE - len, + "reg:0x%04x=0x%08x\n", + aw9610x_reg_access[i].reg, + reg_val); + } + } + + return len; +} + +static ssize_t aw9610x_valid_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw9610x *aw9610x = dev_get_drvdata(dev); + ssize_t len = 0; + uint8_t i = 0; + int32_t reg_val = 0; + + for (i = 0; i < aw9610x->aw_channel_number; i++) { + aw9610x_i2c_read(aw9610x, REG_VALID_CH0 + i * 4, ®_val); + reg_val /= AW_DATA_PROCESS_FACTOR; + len += snprintf(buf+len, PAGE_SIZE-len, "VALID_CH%d = %d\n", i, + reg_val); + } + + return len; +} + +static ssize_t aw9610x_baseline_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw9610x *aw9610x = dev_get_drvdata(dev); + ssize_t len = 0; + uint8_t i = 0; + int32_t reg_val = 0; + + for (i = 0; i < aw9610x->aw_channel_number; i++) { + aw9610x_i2c_read(aw9610x, REG_BASELINE_CH0 + i * 4, ®_val); + reg_val /= AW_DATA_PROCESS_FACTOR; + len += snprintf(buf+len, PAGE_SIZE-len, "BASELINE_CH%d = %d\n", + i, reg_val); + } + + return len; +} + +static ssize_t aw9610x_diff_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw9610x *aw9610x = dev_get_drvdata(dev); + ssize_t len = 0; + uint8_t i = 0; + int32_t reg_val = 0; + + for (i = 0; i < aw9610x->aw_channel_number; i++) { + aw9610x_i2c_read(aw9610x, REG_DIFF_CH0 + i * 4, ®_val); + reg_val /= AW_DATA_PROCESS_FACTOR; + len += snprintf(buf+len, PAGE_SIZE-len, "DIFF_CH%d = %d\n", i, + reg_val); + } + + return len; +} + +static ssize_t aw9610x_raw_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw9610x *aw9610x = dev_get_drvdata(dev); + ssize_t len = 0; + uint8_t i = 0; + int32_t reg_val = 0; + + for (i = 0; i < aw9610x->aw_channel_number; i++) { + aw9610x_i2c_read(aw9610x, REG_RAW_CH0 + i * 4, ®_val); + reg_val /= AW_DATA_PROCESS_FACTOR; + len += snprintf(buf+len, PAGE_SIZE-len, "RAW_DATA_CH%d = %d\n", + i, reg_val); + } + + return len; +} + +static ssize_t aw9610x_psc_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw9610x *aw9610x = dev_get_drvdata(dev); + ssize_t len = 0; + uint8_t i = 0; + int32_t reg_val = 0; + + for (i = 0; i < aw9610x->aw_channel_number; i++) { + aw9610x_i2c_read(aw9610x, REG_PSCBD_CH0 + i * 4, ®_val); + reg_val /= AW_DATA_PROCESS_FACTOR; + len += snprintf(buf+len, PAGE_SIZE-len, "PSC_DATA_CH%d = %d\n", + i, reg_val); + } + + return len; +} + +static ssize_t aw9610x_parasitic_data_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw9610x *aw9610x = dev_get_drvdata(dev); + ssize_t len = 0; + uint8_t i = 0; + uint32_t reg_val = 0; + uint32_t coff_data = 0; + uint32_t coff_data_int = 0; + uint32_t coff_data_dec = 0; + uint8_t temp_data[20] = { 0 }; + + for (i = 0; i < aw9610x->aw_channel_number; i++) { + aw9610x_i2c_read(aw9610x, + REG_AFECFG1_CH0 + i * AW_CL1SPE_CALI_OS, ®_val); + coff_data = (reg_val >> 24) * 900 + + ((reg_val >> 16) & 0xff) * 13; + coff_data_int = coff_data / 1000; + coff_data_dec = coff_data % 1000; + snprintf(temp_data, sizeof(temp_data), "%d.%d", coff_data_int, + coff_data_dec); + len += snprintf(buf+len, PAGE_SIZE-len, + "PARASITIC_DATA_CH%d = %s pf\n", i, temp_data); + } + + return len; +} + +static ssize_t aw9610x_awrw_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw9610x *aw9610x = dev_get_drvdata(dev); + uint8_t reg_data[228] = { 0 }; + uint8_t i = 0; + ssize_t len = 0; + uint8_t reg_num = aw9610x->aw_i2c_package.reg_num; + uint8_t data_bytes = aw9610x->aw_i2c_package.data_bytes; + + i2c_read_seq(aw9610x, reg_data); + for (i = 0; i < reg_num * data_bytes; i++) + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%02x,", reg_data[i]); + + len += snprintf(buf + len - 1, PAGE_SIZE - len, "\n"); + + return len; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) +static ssize_t aw9610x_factory_cali_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw9610x *aw9610x = dev_get_drvdata(dev); + uint32_t databuf[1] = { 0 }; + + if (sscanf(buf, "%d", &databuf[0]) == 1) { + if ((databuf[0] == 1) && (aw9610x->pwprox_dete == true)) { + aw9610x_get_calidata(aw9610x); + } else { + LOG_ERR("aw_unsupport the pw_prox_dete=%d",aw9610x->pwprox_dete); + return count; + } + aw9610x_sw_reset(aw9610x); + aw9610x->cali_flag = AW_NO_CALI; + aw9610x_sar_cfg_init(aw9610x, AW_CFG_LOADED); + } + + return count; +} + +static ssize_t aw9610x_power_prox_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw9610x *aw9610x = dev_get_drvdata(dev); + ssize_t len = 0; + + if (aw9610x->pwprox_dete == false) { + len += snprintf(buf + len, PAGE_SIZE - len, + "unsupport powerprox!"); + return len; + } + + len += snprintf(buf + len, PAGE_SIZE - len, "power_prox: "); + len += snprintf(buf + len, PAGE_SIZE - len, "%d\n", + aw9610x->power_prox); + + return len; +} +#endif + +static ssize_t aw9610x_awrw_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw9610x *aw9610x = dev_get_drvdata(dev); + uint32_t datatype[3] = { 0 }; + + if (sscanf(buf, "%d %d %d", &datatype[0], &datatype[1], + &datatype[2]) == 3) { + aw9610x->aw_i2c_package.addr_bytes = (uint8_t)datatype[0]; + aw9610x->aw_i2c_package.data_bytes = (uint8_t)datatype[1]; + aw9610x->aw_i2c_package.reg_num = (uint8_t)datatype[2]; + + aw9610x_addrblock_load(dev, buf); + if (count > 7 + 5 * aw9610x->aw_i2c_package.addr_bytes) + aw9610x_datablock_load(dev, buf); + } + + return count; +} + +static ssize_t aw9610x_set_update(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t ret; + uint32_t state; + int32_t cfg_timer_val = 10; + struct aw9610x *aw9610x = dev_get_drvdata(dev); + + ret = kstrtouint(buf, 10, &state); + if (ret) { + LOG_ERR("fail to set update"); + return ret; + } + if (state) { + aw9610x_i2c_write(aw9610x, REG_HOSTIRQEN, 0); + aw9610x_sw_reset(aw9610x); + schedule_delayed_work(&aw9610x->cfg_work, + msecs_to_jiffies(cfg_timer_val)); + } + + return count; +} + +static ssize_t aw9610x_aot_cali_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t ret; + uint32_t state; + uint32_t data_en = 0; + struct aw9610x *aw9610x = dev_get_drvdata(dev); + + ret = kstrtouint(buf, 10, &state); + if (ret) { + LOG_ERR("fail to set aot cali"); + return ret; + } + aw9610x_i2c_read(aw9610x, REG_SCANCTRL0, &data_en); + + if (state != 0) + aw9610x_i2c_write_bits(aw9610x, REG_SCANCTRL0, ~(0x3f << 8), + (data_en & 0x3f) << 8); + else + LOG_ERR("fail to set aot cali"); + + return count; +} + +static ssize_t aw9610x_get_satu(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw9610x *aw9610x = dev_get_drvdata(dev); + ssize_t len = 0; + + if (aw9610x->satu_release != 0) + len += snprintf(buf + len, PAGE_SIZE - len, + "satu_ralease function is supporting! the flag = %d\n", + aw9610x->satu_release); + else + len += snprintf(buf + len, PAGE_SIZE - len, + "satu_ralease function unsupport! the flag = %d\n", + aw9610x->satu_release); + + return len; +} + +static ssize_t aw9610x_set_satu(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t ret; + uint32_t state; + struct aw9610x *aw9610x = dev_get_drvdata(dev); + + ret = kstrtouint(buf, 10, &state); + if (ret) { + LOG_ERR("fail to set satu"); + return ret; + } + if (state && (aw9610x->vers == AW9610X)) { + aw9610x_saturat_release_handle(aw9610x); + aw9610x->satu_release = AW9610X_FUNC_ON; + } else { + aw9610x->satu_release = AW9610X_FUNC_OFF; + } + + return count; +} + +static ssize_t aw9610x_operation_mode_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + ssize_t ret; + struct aw9610x *aw9610x = dev_get_drvdata(dev); + + ret = kstrtouint(buf, 10, &aw9610x->mode); + if (ret) { + LOG_ERR("fail to set operation mode"); + return ret; + } + + if (aw9610x->mode == AW9610X_ACTIVE_AP_MODE) { + if (aw9610x->mode_flag1 == AW9610X_FUNC_ON) + aw9610x_i2c_write(aw9610x, REG_HOSTCTRL1, 1); + aw9610x_i2c_write(aw9610x, REG_CMD, AW9610X_ACTIVE_MODE); + } else if (aw9610x->mode == AW9610X_SLEEP_AP_MODE) { + if (aw9610x->mode_flag1 == AW9610X_FUNC_ON) + aw9610x_i2c_write(aw9610x, REG_HOSTCTRL1, 1); + aw9610x_i2c_write(aw9610x, REG_CMD, AW9610X_SLEEP_MODE); + } else if ((aw9610x->mode == AW9610X_DEEPSLEEP_AP_MODE) && + (aw9610x->vers == AW9610XA)) { + aw9610x_i2c_write(aw9610x, REG_CMD, AW9610X_DEEPSLEEP_MODE); + aw9610x->mode_flag1 = AW9610X_FUNC_ON; + } else { + LOG_ERR("failed to operation mode!"); + return aw9610x->mode; + } + + return count; +} + +static ssize_t aw9610x_operation_mode_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw9610x *aw9610x = dev_get_drvdata(dev); + ssize_t len = 0; + + if (aw9610x->mode == AW9610X_ACTIVE_AP_MODE) + len += snprintf(buf + len, PAGE_SIZE - len, + "operation mode: Active\n"); + else if (aw9610x->mode == AW9610X_SLEEP_AP_MODE) + len += snprintf(buf + len, PAGE_SIZE - len, + "operation mode: Sleep\n"); + else if ((aw9610x->mode == AW9610X_DEEPSLEEP_AP_MODE) && + (aw9610x->vers == AW9610XA)) + len += snprintf(buf + len, PAGE_SIZE - len, + "operation mode: DeepSleep\n"); + else + len += snprintf(buf + len, PAGE_SIZE - len, + "operation mode: Unconfirmed\n"); + + return len; +} + +static DEVICE_ATTR(reg, 0664, aw9610x_get_reg, aw9610x_set_reg); +static DEVICE_ATTR(valid, 0664, aw9610x_valid_show, NULL); +static DEVICE_ATTR(baseline, 0664, aw9610x_baseline_show, NULL); +static DEVICE_ATTR(diff, 0664, aw9610x_diff_show, NULL); +static DEVICE_ATTR(raw_data, 0664, aw9610x_raw_data_show, NULL); +static DEVICE_ATTR(psc_data, 0664, aw9610x_psc_data_show, NULL); +static DEVICE_ATTR(parasitic_data, 0664, aw9610x_parasitic_data_show, NULL); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) +static DEVICE_ATTR(factory_cali, 0664, NULL, aw9610x_factory_cali_set); +#endif +static DEVICE_ATTR(aot_cali, 0664, NULL, aw9610x_aot_cali_set); +static DEVICE_ATTR(awrw, 0664, aw9610x_awrw_get, aw9610x_awrw_set); +static DEVICE_ATTR(update, 0644, NULL, aw9610x_set_update); +static DEVICE_ATTR(satu, 0644, aw9610x_get_satu, aw9610x_set_satu); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) +static DEVICE_ATTR(prox, 0644, aw9610x_power_prox_get, NULL); +#endif +static DEVICE_ATTR(operation_mode, 0644, aw9610x_operation_mode_get, + aw9610x_operation_mode_set); + +static struct attribute *aw9610x_sar_attributes[] = { + &dev_attr_reg.attr, + &dev_attr_valid.attr, + &dev_attr_baseline.attr, + &dev_attr_diff.attr, + &dev_attr_raw_data.attr, + &dev_attr_psc_data.attr, + &dev_attr_parasitic_data.attr, + &dev_attr_awrw.attr, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + &dev_attr_factory_cali.attr, +#endif + &dev_attr_aot_cali.attr, + &dev_attr_update.attr, + &dev_attr_satu.attr, +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + &dev_attr_prox.attr, +#endif + &dev_attr_operation_mode.attr, + NULL +}; + +static struct attribute_group aw9610x_sar_attribute_group = { + .attrs = aw9610x_sar_attributes +}; + +static ssize_t reset_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 8, "%d\n", g_aw9610x->mode); +} + +static ssize_t reset_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + uint32_t data_en = 0; + int i; + if (!strncmp(buf, "reset", 5) || !strncmp(buf, "1", 1)) { + aw9610x_i2c_read(g_aw9610x, REG_SCANCTRL0, &data_en); + aw9610x_i2c_write_bits(g_aw9610x, REG_SCANCTRL0, ~(0x3f << 8), + (data_en & 0x3f) << 8); + //aw9610x_i2c_write(g_aw9610x, REG_CMD, AW9610X_ACTIVE_MODE); + //g_aw9610x->mode = AW9610X_ACTIVE_MODE; + } + + for (i = 0; i < g_aw9610x->aw_channel_number; i++) + { + input_report_abs(g_aw9610x->aw_pad[i].input, ABS_DISTANCE, 0); + input_sync(g_aw9610x->aw_pad[i].input); + } + + return count; +} + +static CLASS_ATTR_RW(reset); + +static ssize_t enable_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 8, "%d\n", g_aw9610x->mode); +} + +static ssize_t enable_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + ssize_t ret; + int i; + struct aw9610x *aw9610x = g_aw9610x; + + ret = kstrtouint(buf, 10, &aw9610x->mode); + if (ret) { + LOG_ERR("fail to set operation mode"); + return count; + } + + for (i = 0; i < aw9610x->aw_channel_number; i++) + { + if (!strncmp(buf, "1", 1)){ + input_report_abs(aw9610x->aw_pad[i].input, ABS_DISTANCE, 0); + input_sync(aw9610x->aw_pad[i].input); + }else if (!strncmp(buf, "0", 1)){ + input_report_abs(aw9610x->aw_pad[i].input, ABS_DISTANCE, -1); + input_sync(aw9610x->aw_pad[i].input); + } + LOG_DBG("enable cap sensor: %s\n",buf); + } + + if (aw9610x->mode == AW9610X_ACTIVE_AP_MODE) { + if (aw9610x->mode_flag1 == AW9610X_FUNC_ON) + aw9610x_i2c_write(aw9610x, REG_HOSTCTRL1, 1); + aw9610x_i2c_write(aw9610x, REG_CMD, AW9610X_ACTIVE_MODE); + } else if (aw9610x->mode == AW9610X_SLEEP_AP_MODE) { + if (aw9610x->mode_flag1 == AW9610X_FUNC_ON) + aw9610x_i2c_write(aw9610x, REG_HOSTCTRL1, 1); + aw9610x_i2c_write(aw9610x, REG_CMD, AW9610X_SLEEP_MODE); + } else if ((aw9610x->mode == AW9610X_DEEPSLEEP_AP_MODE) && + (aw9610x->vers == AW9610XA)) { + aw9610x_i2c_write(aw9610x, REG_CMD, AW9610X_DEEPSLEEP_MODE); + aw9610x->mode_flag1 = AW9610X_FUNC_ON; + } else { + LOG_ERR("failed to operation mode!"); + return count; + } + + return count; +} + +#ifdef USE_SENSORS_CLASS +static int capsensor_set_enable(struct sensors_classdev *sensors_cdev, unsigned int enable) +{ + struct aw9610x *aw9610x = g_aw9610x; + uint8_t i = 0; + uint32_t data_en = 0; + bool enableFlag = false; + + for (i = 0; i < aw9610x->aw_channel_number; i++) + { + if ((aw9610x->aw_ref_channel >> i) & 0x1) continue; + if (!strcmp(sensors_cdev->name, aw9610x->aw_ch_name[aw9610x->sar_num * AW_CHANNEL_MAX + i])) { + if (enable == 1){ + sensors_capsensor_chs[i].enabled = 1; + input_report_abs(aw9610x->aw_pad[i].input, ABS_DISTANCE, 0); + input_sync(aw9610x->aw_pad[i].input); + }else if (enable == 0){ + sensors_capsensor_chs[i].enabled = 0; + input_report_abs(aw9610x->aw_pad[i].input, ABS_DISTANCE, -1); + input_sync(aw9610x->aw_pad[i].input); + } + LOG_DBG("enable cap sensor: %s\n",sensors_cdev->name); + } + } + + //if all chs disabled, then disable all + for (i = 0; i < aw9610x->aw_channel_number; i++) + { + if ((aw9610x->aw_ref_channel >> i) & 0x1) continue; + if (sensors_capsensor_chs[i].enabled) { + enableFlag = true; + break; + } + } + + aw9610x->mode = enable; + if (enableFlag == AW9610X_ACTIVE_AP_MODE) { + aw9610x_i2c_read(aw9610x, REG_SCANCTRL0, &data_en); + aw9610x_i2c_write_bits(aw9610x, REG_SCANCTRL0, ~(0x3f << 8), (data_en & 0x3f) << 8); + if (aw9610x->mode_flag1 == AW9610X_FUNC_ON) + aw9610x_i2c_write(aw9610x, REG_HOSTCTRL1, 1); + aw9610x_i2c_write(aw9610x, REG_CMD, AW9610X_ACTIVE_MODE); + } else if (enableFlag == AW9610X_SLEEP_AP_MODE) { + if (aw9610x->mode_flag1 == AW9610X_FUNC_ON) + aw9610x_i2c_write(aw9610x, REG_HOSTCTRL1, 1); + aw9610x_i2c_write(aw9610x, REG_CMD, AW9610X_SLEEP_MODE); + } else if ((aw9610x->mode == AW9610X_DEEPSLEEP_AP_MODE) && + (aw9610x->vers == AW9610XA)) { + aw9610x_i2c_write(aw9610x, REG_CMD, AW9610X_DEEPSLEEP_MODE); + aw9610x->mode_flag1 = AW9610X_FUNC_ON; + } else { + LOG_ERR("failed to operation mode!"); + return -1; + } + + return 0; +} +#endif + +static CLASS_ATTR_RW(enable); + +static ssize_t reg_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u32 *p = (u32*)buf; + struct aw9610x *aw9610x = g_aw9610x; + if(aw9610x->read_flag){ + aw9610x->read_flag = 0; + aw9610x_i2c_read(aw9610x, aw9610x->read_reg, p); + LOG_DBG("%s : read_reg = 0x%x, val = 0x%x\n",__func__,aw9610x->read_reg,*p); + return 4; + } + return -1; +} + +static ssize_t reg_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + struct aw9610x *aw9610x = g_aw9610x; + uint16_t regaddr = 0; + uint32_t val = 0; + int i = 0; + + if( count != 7){ + LOG_ERR("%s :params error[ count == %ld !=2]\n",__func__,count); + return -1; + } + for(i = 0 ; i < count ; i++) + LOG_DBG("%s : buf[%d] = 0x%x\n",__func__,i,buf[i]); + + if(buf[6] == 0){ + regaddr = ((uint16_t)buf[0]<<8) | (uint16_t)buf[1]; + val= ((uint32_t)buf[2]<<24) | ((uint32_t)buf[3]<<16) | ((uint32_t)buf[4]<<8) | ((uint32_t)buf[5]); + aw9610x_i2c_write(aw9610x, regaddr, val); + } else if(buf[6] == 1) { + aw9610x->read_reg = ((uint16_t)buf[0]<<8) | (uint16_t)buf[1]; + aw9610x->read_flag = true; + LOG_DBG("-----------\n"); + } + return count; +} +static CLASS_ATTR_RW(reg); + +static ssize_t int_state_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + struct aw9610x *aw9610x = g_aw9610x; + return snprintf(buf, 8, "%d\n", aw9610x->int_state); +} +static CLASS_ATTR_RO(int_state); + +static struct class capsense_class = { + .name = "capsense", + .owner = THIS_MODULE, +}; + +/***************************************************** +* +* USB charging calibra +* +*****************************************************/ +static void ps_notify_callback_work(struct work_struct *work) +{ + struct aw9610x *aw9610x = container_of(work, struct aw9610x, ps_notify_work); + uint32_t data_en = 0; + int ret = 0; + uint8_t i; + + + LOG_INFO("Usb insert,going to force calibrate\n"); + aw9610x_i2c_read(aw9610x, REG_SCANCTRL0, &data_en); + + ret = aw9610x_i2c_write_bits(aw9610x, REG_SCANCTRL0, ~(0x3f << 8), + (data_en & 0x3f) << 8); + + if (ret < 0){ + LOG_ERR(" Usb insert,calibrate cap sensor failed\n"); + return; + } + + for (i = 0; i < aw9610x->aw_channel_number; i++){ + input_report_abs( + aw9610x->aw_pad[i].input, ABS_DISTANCE, 0); + input_sync(aw9610x->aw_pad[i].input); + } + +} + +static int ps_get_state(struct power_supply *psy, bool *present) +{ + union power_supply_propval pval = { 0 }; + int retval; + +#ifdef CONFIG_USE_POWER_SUPPLY_ONLINE + retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, + &pval); +#else + retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, + &pval); +#endif + if (retval) { + LOG_ERR( "aw9610x %s psy get property failed\n", psy->desc->name); + return retval; + } + *present = (pval.intval) ? true : false; + LOG_INFO( "aw9610x %s is %s\n", psy->desc->name, + (*present) ? "present" : "not present"); + return 0; +} + +static int ps_notify_callback(struct notifier_block *self, + unsigned long event, void *p) +{ + struct aw9610x *aw9610x = container_of(self, struct aw9610x, ps_notif); + struct power_supply *psy = p; + bool present; + int retval; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0) + if (event == PSY_EVENT_PROP_CHANGED +#else + if ((event == PSY_EVENT_PROP_ADDED || event == PSY_EVENT_PROP_CHANGED) +#endif + && psy && psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, "usb", sizeof("usb"))){ + LOG_INFO("ps notification: event = %lu\n", event); + retval = ps_get_state(psy, &present); + if (retval) { + LOG_ERR("psy get property failed\n"); + return retval; + } + + if (event == PSY_EVENT_PROP_CHANGED) { + if (aw9610x->ps_is_present == present) { + LOG_INFO("ps present state not change\n"); + return 0; + } + } + aw9610x->ps_is_present = present; + schedule_work(&aw9610x->ps_notify_work); + } + + return 0; +} + +/***************************************************** +* +* irq init +* +*****************************************************/ +static void aw9610x_irq_handle(struct aw9610x *aw9610x) +{ + uint32_t curr_status = 0; + uint8_t i = 0; + uint8_t j = 0; + + LOG_DBG("enter"); + + aw9610x_i2c_read(aw9610x, REG_STAT0, &curr_status); + j = aw9610x->sar_num; + LOG_DBG("pad = 0x%08x", curr_status); + for (i = 0; i < aw9610x->aw_channel_number; i++) { + aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].curr_state = + (((uint8_t)(curr_status >> (24 + i)) & 0x1)) | + (((uint8_t)(curr_status >> (16 + i)) & 0x1) << 1) | + (((uint8_t)(curr_status >> (8 + i)) & 0x1) << 2) | + (((uint8_t)(curr_status >> (i)) & 0x1) << 3); + LOG_DBG("curr_state[%d] = 0x%x", j * AW_CHANNEL_MAX + i,aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].curr_state); + + if (aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].curr_state != + aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].last_state) { + switch (aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].curr_state) { + case FAR: + input_report_abs( + aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input, + ABS_DISTANCE, 0); + break; + case TRIGGER_TH0: + input_report_abs( + aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input, + ABS_DISTANCE, 1); + break; + case TRIGGER_TH1: + input_report_abs( + aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input, + ABS_DISTANCE, 2); + break; + case TRIGGER_TH2: + input_report_abs( + aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input, + ABS_DISTANCE, 3); + break; + case TRIGGER_TH3: + input_report_abs( + aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input, + ABS_DISTANCE, 4); + break; + default: + LOG_ERR("error abs distance"); + return; + } + input_sync(aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input); + } + aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].last_state = + aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].curr_state; + } +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) +static void aw9610x_farirq_handle(struct aw9610x *aw9610x) +{ + uint8_t th0_far = 0; + + th0_far = (aw9610x->irq_status >> 2) & 0x1; + if (th0_far == 1) + aw9610x->power_prox = AW9610X_FUNC_OFF; +} +#endif + +static void aw9610x_interrupt_clear(struct aw9610x *aw9610x) +{ + int32_t ret = 0; + + LOG_DBG("enter"); + + ret = aw9610x_i2c_read(aw9610x, REG_HOSTIRQSRC, &aw9610x->irq_status); + if (ret < 0) { + LOG_ERR("i2c IO error"); + return; + } else { + if ((aw9610x->satu_release == AW9610X_FUNC_ON) && (aw9610x->vers == AW9610X)) + aw9610x_saturat_release_handle(aw9610x); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + if (aw9610x->pwprox_dete == true) + aw9610x_farirq_handle(aw9610x); +#endif + } + + LOG_INFO("IRQSRC = 0x%x", aw9610x->irq_status); + + /* multiple sar handle IO */ + switch (aw9610x->sar_num) { + case AW_SAR0: + break; + case AW_SAR1: + break; + default: + return; + } + + aw9610x_irq_handle(aw9610x); +} + +static irqreturn_t aw9610x_irq(int32_t irq, void *data) +{ + struct aw9610x *aw9610x = data; + + LOG_DBG("enter"); + + aw9610x_interrupt_clear(aw9610x); + LOG_DBG("exit"); + + return IRQ_HANDLED; +} + +static int32_t aw9610x_interrupt_init(struct aw9610x *aw9610x) +{ + int32_t irq_flags = 0; + int32_t ret = 0; + uint8_t i = 0; + int8_t irq_gpio_name[100] = { 0 }; + + LOG_DBG("enter"); + + for (i = 0; i < aw9610x->aw_channel_number; i++) + aw9610x->satu_flag[i] = 0; + + snprintf(irq_gpio_name, sizeof(irq_gpio_name), + "aw9610x_irq_gpio%d", aw9610x->sar_num); + + if (gpio_is_valid(aw9610x->irq_gpio)) { + aw9610x->to_irq = gpio_to_irq(aw9610x->irq_gpio); + + ret = devm_gpio_request_one(aw9610x->dev, + aw9610x->irq_gpio, + GPIOF_DIR_IN | GPIOF_INIT_HIGH, + irq_gpio_name); + + if (ret) { + LOG_ERR("request irq gpio failed, ret = %d", ret); + ret = -AW_IRQIO_FAILED; + } else { + /* register irq handler */ + irq_flags = IRQF_TRIGGER_LOW | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&aw9610x->i2c->dev, + aw9610x->to_irq, NULL, + aw9610x_irq, irq_flags, + "aw9610x_irq", aw9610x); + if (ret != 0) { + LOG_ERR("failed to request IRQ %d: %d",aw9610x->to_irq, ret); + ret = -AW_IRQ_REQUEST_FAILED; + } else { + LOG_INFO("IRQ request successfully!"); + aw9610x->int_state = 1; + ret = AW_SAR_SUCCESS; + } + } + } else { + LOG_ERR("irq gpio invalid!"); + return -AW_IRQIO_FAILED; + } + + return ret; +} + +/***************************************************** + * + * parse dts + * + *****************************************************/ +static int32_t aw9610x_parse_dt(struct device *dev, struct aw9610x *aw9610x, + struct device_node *np) +{ + uint32_t val = 0; + + aw9610x->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (aw9610x->irq_gpio < 0) { + aw9610x->irq_gpio = -1; + LOG_ERR("no irq gpio provided."); + return -AW_IRQGPIO_FAILED; + } else { + LOG_INFO("irq gpio provided ok."); + } + + val = of_property_read_u32(np, "sar-num", &aw9610x->sar_num); + if (val != 0) { + LOG_ERR("multiple sar failed!"); + return -AW_MULTIPLE_SAR_FAILED; + } else { + LOG_INFO("sar num = %d", aw9610x->sar_num); + } + +#ifdef CONFIG_USE_HARDWARE_VERSION + val = of_property_read_u32(np, "hardware_version", &aw9610x->hw_version); + if (val != 0) { + LOG_ERR("hardware_version failed!"); + return -AW_MULTIPLE_SAR_FAILED; + } else { + LOG_INFO("hardware version = %d", aw9610x->hw_version); + } +#endif + + val = of_property_read_u32(np, "aw9610x,channel_number", &aw9610x->aw_channel_number); + if (val != 0) { + LOG_ERR("aw_channel_number failed!"); + return -AW_MULTIPLE_SAR_FAILED; + } else { + if (aw9610x->aw_channel_number > AW_CHANNEL_MAX) + { + aw9610x->aw_channel_number = AW_CHANNEL_MAX; + } + LOG_INFO("aw_channel_number = %d", aw9610x->aw_channel_number); + } + val = of_property_read_u32(np, "ref_channel", &aw9610x->aw_ref_channel); + if (val != 0) { + LOG_ERR("aw_ref_channel failed!"); + return -AW_MULTIPLE_SAR_FAILED; + } else { + LOG_INFO("aw_ref_channel = %d", aw9610x->aw_ref_channel); + } + + if (aw9610x->aw_channel_number == AW_CHANNEL_NUMBER_TWO) { + of_property_read_string(np, "ch0_name", &aw9610x->aw_ch_name[aw9610x->sar_num * AW_CHANNEL_MAX + 0]); + of_property_read_string(np, "ch1_name", &aw9610x->aw_ch_name[aw9610x->sar_num * AW_CHANNEL_MAX + 1]); + } else if (aw9610x->aw_channel_number == AW_CHANNEL_NUMBER_THREE) { + of_property_read_string(np, "ch0_name", &aw9610x->aw_ch_name[aw9610x->sar_num * AW_CHANNEL_MAX + 0]); + of_property_read_string(np, "ch1_name", &aw9610x->aw_ch_name[aw9610x->sar_num * AW_CHANNEL_MAX + 1]); + of_property_read_string(np, "ch2_name", &aw9610x->aw_ch_name[aw9610x->sar_num * AW_CHANNEL_MAX + 2]); + } else if (aw9610x->aw_channel_number == AW_CHANNEL_NUMBER_FOUR) { + of_property_read_string(np, "ch0_name", &aw9610x->aw_ch_name[aw9610x->sar_num * AW_CHANNEL_MAX + 0]); + of_property_read_string(np, "ch1_name", &aw9610x->aw_ch_name[aw9610x->sar_num * AW_CHANNEL_MAX + 1]); + of_property_read_string(np, "ch2_name", &aw9610x->aw_ch_name[aw9610x->sar_num * AW_CHANNEL_MAX + 2]); + of_property_read_string(np, "ch3_name", &aw9610x->aw_ch_name[aw9610x->sar_num * AW_CHANNEL_MAX + 3]); + } else if (aw9610x->aw_channel_number == AW_CHANNEL_NUMBER_FIVE) { + of_property_read_string(np, "ch0_name", &aw9610x->aw_ch_name[aw9610x->sar_num * AW_CHANNEL_MAX + 0]); + of_property_read_string(np, "ch1_name", &aw9610x->aw_ch_name[aw9610x->sar_num * AW_CHANNEL_MAX + 1]); + of_property_read_string(np, "ch2_name", &aw9610x->aw_ch_name[aw9610x->sar_num * AW_CHANNEL_MAX + 2]); + of_property_read_string(np, "ch3_name", &aw9610x->aw_ch_name[aw9610x->sar_num * AW_CHANNEL_MAX + 3]); + of_property_read_string(np, "ch4_name", &aw9610x->aw_ch_name[aw9610x->sar_num * AW_CHANNEL_MAX + 4]); + } + + aw9610x->firmware_flag = + of_property_read_bool(np, "aw9610x,using-firmware"); + LOG_INFO("firmware_flag = <%d>", aw9610x->firmware_flag); + + aw9610x->pwprox_dete = + of_property_read_bool(np, "aw9610x,using-pwon-prox-dete"); + LOG_INFO("pwprox_dete = <%d>", aw9610x->pwprox_dete); + + aw9610x->satu_release = + of_property_read_bool(np, "aw9610x,using-satu"); + LOG_INFO("satu_release = <%d>", aw9610x->satu_release); + + return AW_SAR_SUCCESS; +} + +#ifdef AW_POWER_ON +static int32_t aw9610x_power_init(struct aw9610x *aw9610x) +{ + int32_t rc = 0; + + LOG_DBG("aw9610x power init enter"); + + aw9610x->vcc = regulator_get(aw9610x->dev, "cap_vcc"); + if (IS_ERR(aw9610x->vcc)) { + rc = PTR_ERR(aw9610x->vcc); + LOG_ERR("regulator get failed vcc rc = %d", rc); + return rc; + } + + if (regulator_count_voltages(aw9610x->vcc) > 0) { + rc = regulator_set_voltage(aw9610x->vcc, + AW_VCC_MIN_UV, AW_VCC_MAX_UV); + if (rc) { + LOG_ERR("regulator set vol failed rc = %d", rc); + goto reg_vcc_put; + } + } + + return rc; + +reg_vcc_put: + regulator_put(aw9610x->vcc); + return rc; +} + +static void aw9610x_power_enable(struct aw9610x *aw9610x, bool on) +{ + int32_t rc = 0; + + LOG_DBG("aw9610x power enable enter"); + + if (on) { + rc = regulator_enable(aw9610x->vcc); + if (rc) { + LOG_ERR("regulator_enable vol failed rc = %d", rc); + } else { + aw9610x->power_enable = true; + msleep(20); + } + } else { + rc = regulator_disable(aw9610x->vcc); + if (rc) + LOG_ERR("regulator_disable vol failed rc = %d", rc); + else + aw9610x->power_enable = false; + } +} +#endif + +/***************************************************** + * + * check chip id + * + *****************************************************/ +static int32_t aw9610x_read_chipid(struct aw9610x *aw9610x) +{ + int32_t ret = -1; + uint8_t cnt = 0; + uint32_t reg_val = 0; + + while (cnt < AW_READ_CHIPID_RETRIES) { + ret = aw9610x_i2c_read(aw9610x, REG_CHIP_ID, ®_val); + if (ret < 0) { + LOG_ERR("read CHIP ID failed: %d", ret); + } else { + reg_val = reg_val >> 16; + break; + } + + cnt++; + usleep_range(2000, 3000); + } + + if (reg_val == AW9610X_CHIP_ID) { + LOG_INFO("aw9610x detected"); + return AW_SAR_SUCCESS; + } else { + LOG_ERR("unsupport dev, chipid is (0x%04x)", reg_val); + } + + return -AW_CHIPID_FAILED; +} + +static void aw9610x_i2c_set(struct i2c_client *i2c, + struct aw9610x *aw9610x) +{ + aw9610x->dev = &i2c->dev; + aw9610x->i2c = i2c; + i2c_set_clientdata(i2c, aw9610x); +} + +static int32_t aw9610x_version_init(struct aw9610x *aw9610x) +{ + uint32_t firmvers = 0; + + aw9610x_i2c_read(aw9610x, REG_FIRMVERSION, &firmvers); + aw9610x->pad = firmvers >> 28; + switch (aw9610x->pad) { + case 4: + aw9610x->pad -= 1; + break; + case 5: + break; + default: + return -AW_VERS_ERR; + } + snprintf(aw9610x->chip_name, sizeof(aw9610x->chip_name), + "AW9610%d", aw9610x->pad); + aw9610x->chip_name[7] = '\0'; + + aw9610x->vers = (firmvers >> 24) & 0xf; + if (aw9610x->vers == AW9610XA) + memcpy(aw9610x->chip_name + strlen(aw9610x->chip_name), "A", 1); + + aw9610x->chip_name[8] = '\0'; + + LOG_INFO("REG_FIRMVERSION = 0x%x", firmvers); + + return AW_SAR_SUCCESS; +} + +static int32_t +aw9610x_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct aw9610x *aw9610x; + struct device_node *np = i2c->dev.of_node; + struct power_supply *psy = NULL; + int32_t ret = 0; + uint32_t i = 0; + uint32_t j = 0; + uint32_t err_num = 0; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + LOG_ERR("check_functionality failed"); + return -EIO; + } + + aw9610x = devm_kzalloc(&i2c->dev, sizeof(struct aw9610x), GFP_KERNEL); + if (aw9610x == NULL) { + LOG_ERR("failed to malloc memory!"); + ret = -AW_MALLOC_FAILED; + goto err_malloc; + } + + aw9610x_i2c_set(i2c, aw9610x); + + aw9610x->pwr_by_gpio = of_property_read_bool(np, "sar-gpio-vcc-enable"); + if (aw9610x->pwr_by_gpio) + { + aw9610x->vcc_3v0_Pin = of_get_named_gpio(np, "sar-gpio_3v0_en", 0); + if (!gpio_is_valid(aw9610x->vcc_3v0_Pin)) + { + LOG_ERR("vcc_3v0_Pin gpio is invalid."); + return -ENODEV; + } + ret = gpio_request(aw9610x->vcc_3v0_Pin, "vcc_3v0_Pin"); + if (ret < 0) + { + LOG_ERR("vcc_3v0_Pin Request gpio fail ret = %d", ret); + return ret; + } + ret = gpio_direction_output(aw9610x->vcc_3v0_Pin, 1); + if(ret < 0){ + LOG_ERR("vcc_3v0_Pin gpio direction set fail ret = %d", ret); + return ret; + } + LOG_ERR("vcc_3v0_Pin gpio num is %d", aw9610x->vcc_3v0_Pin); + msleep(20); + }else{ +#ifdef AW_POWER_ON + /* aw9610x power init */ + ret = aw9610x_power_init(aw9610x); + if (ret) + LOG_ERR("aw9610x power init failed"); + else + aw9610x_power_enable(aw9610x, true); +#endif + } + + /* aw9610x chip id */ + ret = aw9610x_read_chipid(aw9610x); + if (ret != AW_SAR_SUCCESS) { + LOG_ERR( "read chipid failed, ret=%d", ret); + goto err_chipid; + } + + ret = aw9610x_version_init(aw9610x); + if (ret < 0) + goto err_vers_load; + + ret = aw9610x_parse_dt(&i2c->dev, aw9610x, np); + if (ret != AW_SAR_SUCCESS) { + LOG_ERR("irq gpio error!, ret = %d", ret); + goto err_pase_dt; + } + + aw9610x_sw_reset(aw9610x); + + ret = aw9610x_init_irq_handle(aw9610x); + if (ret != AW_SAR_SUCCESS) { + LOG_ERR("the trouble ret = %d", ret); + goto err_first_irq; + } + + ret = aw9610x_interrupt_init(aw9610x); + if (ret == -AW_IRQ_REQUEST_FAILED) { + LOG_ERR("request irq failed ret = %d", ret); + goto err_requst_irq; + } + + g_aw9610x = aw9610x; + aw9610x->aw_pad = pad_event; + /* input device */ + j = aw9610x->sar_num; + for (i = 0; i < aw9610x->aw_channel_number; i++) { + aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input = input_allocate_device(); + if (!(aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input)) { + err_num = i; + goto exit_input_alloc_failed; + } + aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input->name = aw9610x->aw_ch_name[j * AW_CHANNEL_MAX + i]; + __set_bit(EV_KEY, aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input->evbit); + __set_bit(EV_SYN, aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input->evbit); + __set_bit(KEY_F1, aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input->keybit); + input_set_abs_params(aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input, + ABS_DISTANCE, -1, 100, 0, 0); + + ret = input_register_device(aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input); + if (ret) { + LOG_ERR("failed to register input device"); + input_free_device(aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input); + err_num = i; + goto exit_input_register_device_failed; + } + } + + ret = class_register(&capsense_class); + if (ret < 0) { + LOG_ERR("Create fsys class failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_reset); + if (ret < 0) { + LOG_ERR("Create reset file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_int_state); + if (ret < 0) { + LOG_DBG("Create int_state file failed (%d)\n", ret); + return ret; + } +/* + ret = class_create_file(&capsense_class, &class_attr_fw_download_status); + if (ret < 0) { + LOG_DBG("Create fw_download_status file failed (%d)\n", ret); + return ret; + } +*/ + ret = class_create_file(&capsense_class, &class_attr_enable); + if (ret < 0) { + LOG_ERR("Create enable file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_reg); + if (ret < 0) { + LOG_DBG("Create reg file failed (%d)\n", ret); + return ret; + } + + /*restore sys/class/capsense label*/ + //kobject_uevent(&capsense_class.p->subsys.kobj, KOBJ_CHANGE); + +#ifdef USE_SENSORS_CLASS + for (i = 0; i < aw9610x->aw_channel_number; i++){ + if ((aw9610x->aw_ref_channel >> i) & 0x1) continue; + sensors_capsensor_chs[i].sensors_enable = capsensor_set_enable; + sensors_capsensor_chs[i].sensors_poll_delay = NULL; + sensors_capsensor_chs[i].name = aw9610x->aw_ch_name[aw9610x->sar_num * AW_CHANNEL_MAX + i]; + sensors_capsensor_chs[i].enabled = 0; + LOG_INFO("cap sensor_class channel_name:%s\n", aw9610x->aw_ch_name[aw9610x->sar_num * AW_CHANNEL_MAX + i]); + ret = sensors_classdev_register(&aw9610x->aw_pad[i].input->dev, &sensors_capsensor_chs[i]); + if (ret < 0) + LOG_ERR("create ch0 cap sensor_class file failed (%d)\n", ret); + } +#endif + + /* attribute */ + ret = sysfs_create_group(&i2c->dev.kobj, &aw9610x_sar_attribute_group); + if (ret < 0) { + LOG_ERR( "error creating sysfs attr files"); + goto err_sysfs; + } + + INIT_WORK(&aw9610x->ps_notify_work, ps_notify_callback_work); + aw9610x->ps_notif.notifier_call = ps_notify_callback; + ret = power_supply_reg_notifier(&aw9610x->ps_notif); + if (ret) { + LOG_ERR("Unable to register ps_notifier: %d\n", ret); + power_supply_unreg_notifier(&aw9610x->ps_notif); + } + + psy = power_supply_get_by_name("usb"); + if (psy) { + ret = ps_get_state(psy, &aw9610x->ps_is_present); + if (ret) { + LOG_ERR("psy get property failed rc=%d\n",ret); + power_supply_unreg_notifier(&aw9610x->ps_notif); + } + } + + ret = aw9610x_sar_cfg_init(aw9610x, AW_CFG_UNLOAD); + if (ret < 0) { + LOG_ERR("cfg situation not confirmed!"); + goto err_cfg; + } + + return AW_SAR_SUCCESS; + +err_cfg: +err_sysfs: + sysfs_remove_group(&i2c->dev.kobj, &aw9610x_sar_attribute_group); +exit_input_register_device_failed: + for (i = 0; i < err_num; i++) + input_unregister_device(aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input); +exit_input_alloc_failed: + for (i = 0; i < err_num; i++) + input_free_device(aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input); +err_requst_irq: + if (gpio_is_valid(aw9610x->irq_gpio)) + devm_gpio_free(&i2c->dev, aw9610x->irq_gpio); +err_first_irq: +err_pase_dt: +err_vers_load: +err_chipid: + if (aw9610x->pwr_by_gpio){ + gpio_free(aw9610x->vcc_3v0_Pin); + }else{ + if (aw9610x->power_enable) { + regulator_disable(aw9610x->vcc); + regulator_put(aw9610x->vcc); + } + } +err_malloc: + return ret; +} + +static int32_t aw9610x_i2c_remove(struct i2c_client *i2c) +{ + struct aw9610x *aw9610x = i2c_get_clientdata(i2c); + uint32_t i = 0; + uint32_t j = aw9610x->sar_num; + + if (aw9610x->pwr_by_gpio){ + gpio_free(aw9610x->vcc_3v0_Pin); + }else{ + if (aw9610x->power_enable) { + regulator_disable(aw9610x->vcc); + regulator_put(aw9610x->vcc); + } + } + +#ifdef USE_SENSORS_CLASS + for (i = 0; i < aw9610x->aw_channel_number; i++) + { + if ((aw9610x->aw_ref_channel >> i) & 0x1) continue; + sensors_classdev_unregister(&sensors_capsensor_chs[i]); + } +#endif + if (gpio_is_valid(aw9610x->irq_gpio)) + devm_gpio_free(&i2c->dev, aw9610x->irq_gpio); + + for (i = 0; i < aw9610x->aw_channel_number; i++) + input_free_device(aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input); + + for (i = 0; i < aw9610x->aw_channel_number; i++) + input_unregister_device(aw9610x->aw_pad[j * AW_CHANNEL_MAX + i].input); + + sysfs_remove_group(&i2c->dev.kobj, &aw9610x_sar_attribute_group); + return 0; +} + +static int aw9610x_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct aw9610x *aw9610x = i2c_get_clientdata(client); + + LOG_DBG("suspend enter"); + + disable_irq(aw9610x->to_irq); + aw9610x_i2c_write(aw9610x, REG_CMD, AW9610X_SLEEP_MODE); + + return 0; +} + +static int aw9610x_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct aw9610x *aw9610x = i2c_get_clientdata(client); + + LOG_DBG("resume enter"); + + aw9610x_i2c_write(aw9610x, REG_CMD, AW9610X_ACTIVE_MODE); + enable_irq(aw9610x->to_irq); + + return 0; +} + +static void aw9610x_i2c_shutdown(struct i2c_client *i2c) +{ + struct aw9610x *aw9610x = i2c_get_clientdata(i2c); + + pr_info("%s enter", __func__); + + disable_irq(aw9610x->to_irq); + aw9610x_i2c_write(aw9610x, REG_CMD, AW9610X_SLEEP_MODE); +} + +static const struct dev_pm_ops aw9610x_pm_ops = { + .suspend = aw9610x_suspend, + .resume = aw9610x_resume, +}; + +static const struct of_device_id aw9610x_dt_match[] = { + { .compatible = "awinic,aw9610x_sar_0" }, + { .compatible = "awinic,aw9610x_sar_1" }, + { }, +}; + +static const struct i2c_device_id aw9610x_i2c_id[] = { + { AW9610X_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw9610x_i2c_id); + +static struct i2c_driver aw9610x_i2c_driver = { + .driver = { + .name = AW9610X_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw9610x_dt_match), + .pm = &aw9610x_pm_ops, + }, + .probe = aw9610x_i2c_probe, + .remove = aw9610x_i2c_remove, + .shutdown = aw9610x_i2c_shutdown, + .id_table = aw9610x_i2c_id, +}; + +static int32_t __init aw9610x_i2c_init(void) +{ + int32_t ret = 0; + + pr_info("aw9610x driver version %s\n", AW9610X_DRIVER_VERSION); + + ret = i2c_add_driver(&aw9610x_i2c_driver); + if (ret) { + pr_err("fail to add aw9610x device into i2c\n"); + return ret; + } + + return 0; +} + +late_initcall(aw9610x_i2c_init); +static void __exit aw9610x_i2c_exit(void) +{ + i2c_del_driver(&aw9610x_i2c_driver); +} +module_exit(aw9610x_i2c_exit); +MODULE_DESCRIPTION("AW9610X SAR Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("2"); diff --git a/drivers/misc/aw9610x/aw_bin_parse.c b/drivers/misc/aw9610x/aw_bin_parse.c new file mode 100644 index 000000000000..12b067ef3c3f --- /dev/null +++ b/drivers/misc/aw9610x/aw_bin_parse.c @@ -0,0 +1,516 @@ +/* +* aw_bin_parse.c +* +* Copyright (c) 2020 AWINIC Technology CO., 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AWINIC_CODE_VERSION "V0.0.7-V1.0.4" /* "code version"-"excel version" */ + +#define DEBUG_LOG_LEVEL +#ifdef DEBUG_LOG_LEVEL +#define DBG(fmt, arg...) do {\ +printk("AWINIC_BIN %s,line= %d,"fmt, __func__, __LINE__, ##arg);\ +} while (0) +#define DBG_ERR(fmt, arg...) do {\ +printk("AWINIC_BIN_ERR %s,line= %d,"fmt, __func__, __LINE__, ##arg);\ +} while (0) +#else +#define DBG(fmt, arg...) do {} while (0) +#define DBG_ERR(fmt, arg...) do {} while (0) +#endif + +#define printing_data_code + +typedef unsigned short int aw_uint16; +typedef unsigned long int aw_uint32; + +#define BigLittleSwap16(A) ((((aw_uint16)(A) & 0xff00) >> 8) | \ + (((aw_uint16)(A) & 0x00ff) << 8)) + +#define BigLittleSwap32(A) ((((aw_uint32)(A) & 0xff000000) >> 24) | \ + (((aw_uint32)(A) & 0x00ff0000) >> 8) | \ + (((aw_uint32)(A) & 0x0000ff00) << 8) | \ + (((aw_uint32)(A) & 0x000000ff) << 24)) + +/** +* +* Interface function +* +* return value: +* value = 0 :success; +* value = -1 :check bin header version +* value = -2 :check bin data type +* value = -3 :check sum or check bin data len error +* value = -4 :check data version +* value = -5 :check register num +* value = -6 :check dsp reg num +* value = -7 :check soc app num +* value = -8 :bin is NULL point +* +**/ + +/******************************************************** +* +* check sum data +* +********************************************************/ +int aw_check_sum(struct aw_bin *bin, int bin_num) +{ + unsigned int i = 0; + unsigned int sum_data = 0; + unsigned int check_sum = 0; + char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr - + bin->header_info[bin_num].header_len)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + check_sum = GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + + for (i = 4; + i < + bin->header_info[bin_num].bin_data_len + + bin->header_info[bin_num].header_len; i++) { + sum_data += *(p_check_sum + i); + } + DBG("aw_bin_parse bin_num=%d, check_sum = 0x%x, sum_data = 0x%x\n", + bin_num, check_sum, sum_data); + if (sum_data != check_sum) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse check sum or check bin data len error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, check_sum = 0x%x, sum_data = 0x%x\n", bin_num, check_sum, sum_data); + return -3; + } + p_check_sum = NULL; + + return 0; +} + +int aw_check_data_version(struct aw_bin *bin, int bin_num) +{ + int i = 0; + DBG("enter\n"); + + for (i = DATA_VERSION_V1; i < DATA_VERSION_MAX; i++) { + if (bin->header_info[bin_num].bin_data_ver == i) { + return 0; + } + } + DBG_ERR("aw_bin_parse Unrecognized this bin data version\n"); + return -4; +} + +int aw_check_register_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_register_num = 0; + unsigned int parse_register_num = 0; + char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + parse_register_num = GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + check_register_num = (bin->header_info[bin_num].bin_data_len - 4) / + (bin->header_info[bin_num].reg_byte_len + + bin->header_info[bin_num].data_byte_len); + DBG + ("aw_bin_parse bin_num=%d, parse_register_num = 0x%x, check_register_num = 0x%x\n", + bin_num, parse_register_num, check_register_num); + if (parse_register_num != check_register_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse register num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_register_num = 0x%x, check_register_num = 0x%x\n", bin_num, parse_register_num, check_register_num); + return -5; + } + bin->header_info[bin_num].reg_num = parse_register_num; + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 4; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 4; + return 0; +} + +int aw_check_dsp_reg_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_dsp_reg_num = 0; + unsigned int parse_dsp_reg_num = 0; + char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + parse_dsp_reg_num = GET_32_DATA(*(p_check_sum + 7), + *(p_check_sum + 6), + *(p_check_sum + 5), *(p_check_sum + 4)); + bin->header_info[bin_num].reg_data_byte_len = + GET_32_DATA(*(p_check_sum + 11), *(p_check_sum + 10), + *(p_check_sum + 9), *(p_check_sum + 8)); + check_dsp_reg_num = + (bin->header_info[bin_num].bin_data_len - + 12) / bin->header_info[bin_num].reg_data_byte_len; + DBG + ("aw_bin_parse bin_num=%d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x\n", + bin_num, parse_dsp_reg_num, check_dsp_reg_num); + if (parse_dsp_reg_num != check_dsp_reg_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse dsp reg num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x\n", bin_num, parse_dsp_reg_num, check_dsp_reg_num); + return -6; + } + bin->header_info[bin_num].download_addr = + GET_32_DATA(*(p_check_sum + 3), *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + bin->header_info[bin_num].reg_num = parse_dsp_reg_num; + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 12; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 12; + return 0; +} + +int aw_check_soc_app_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_soc_app_num = 0; + unsigned int parse_soc_app_num = 0; + char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + bin->header_info[bin_num].app_version = GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), + *(p_check_sum)); + parse_soc_app_num = GET_32_DATA(*(p_check_sum + 11), + *(p_check_sum + 10), + *(p_check_sum + 9), *(p_check_sum + 8)); + check_soc_app_num = bin->header_info[bin_num].bin_data_len - 12; + DBG + ("aw_bin_parse bin_num=%d, parse_soc_app_num = 0x%x, check_soc_app_num = 0x%x\n", + bin_num, parse_soc_app_num, check_soc_app_num); + if (parse_soc_app_num != check_soc_app_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse soc app num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_soc_app_num = 0x%x, check_soc_app_num = 0x%x\n", bin_num, parse_soc_app_num, check_soc_app_num); + return -7; + } + bin->header_info[bin_num].reg_num = parse_soc_app_num; + bin->header_info[bin_num].download_addr = + GET_32_DATA(*(p_check_sum + 7), *(p_check_sum + 6), + *(p_check_sum + 5), *(p_check_sum + 4)); + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 12; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 12; + return 0; +} + +/************************ +*** +***bin header 1_0_0 +*** +************************/ +void aw_get_single_bin_header_1_0_0(struct aw_bin *bin) +{ + int i; + DBG("enter %s\n", __func__); + bin->header_info[bin->all_bin_parse_num].header_len = 60; + bin->header_info[bin->all_bin_parse_num].check_sum = + GET_32_DATA(*(bin->p_addr + 3), *(bin->p_addr + 2), + *(bin->p_addr + 1), *(bin->p_addr)); + bin->header_info[bin->all_bin_parse_num].header_ver = + GET_32_DATA(*(bin->p_addr + 7), *(bin->p_addr + 6), + *(bin->p_addr + 5), *(bin->p_addr + 4)); + bin->header_info[bin->all_bin_parse_num].bin_data_type = + GET_32_DATA(*(bin->p_addr + 11), *(bin->p_addr + 10), + *(bin->p_addr + 9), *(bin->p_addr + 8)); + bin->header_info[bin->all_bin_parse_num].bin_data_ver = + GET_32_DATA(*(bin->p_addr + 15), *(bin->p_addr + 14), + *(bin->p_addr + 13), *(bin->p_addr + 12)); + bin->header_info[bin->all_bin_parse_num].bin_data_len = + GET_32_DATA(*(bin->p_addr + 19), *(bin->p_addr + 18), + *(bin->p_addr + 17), *(bin->p_addr + 16)); + bin->header_info[bin->all_bin_parse_num].ui_ver = + GET_32_DATA(*(bin->p_addr + 23), *(bin->p_addr + 22), + *(bin->p_addr + 21), *(bin->p_addr + 20)); + bin->header_info[bin->all_bin_parse_num].reg_byte_len = + GET_32_DATA(*(bin->p_addr + 35), *(bin->p_addr + 34), + *(bin->p_addr + 33), *(bin->p_addr + 32)); + bin->header_info[bin->all_bin_parse_num].data_byte_len = + GET_32_DATA(*(bin->p_addr + 39), *(bin->p_addr + 38), + *(bin->p_addr + 37), *(bin->p_addr + 36)); + bin->header_info[bin->all_bin_parse_num].device_addr = + GET_32_DATA(*(bin->p_addr + 43), *(bin->p_addr + 42), + *(bin->p_addr + 41), *(bin->p_addr + 40)); + for (i = 0; i < 8; i++) { + bin->header_info[bin->all_bin_parse_num].chip_type[i] = + *(bin->p_addr + 24 + i); + } + bin->header_info[bin->all_bin_parse_num].chip_type[i] = '\0'; + DBG("%s : enter chip_type is %s\n", __func__, + bin->header_info[bin->all_bin_parse_num].chip_type); + + bin->header_info[bin->all_bin_parse_num].reg_num = 0x00000000; + bin->header_info[bin->all_bin_parse_num].reg_data_byte_len = 0x00000000; + bin->header_info[bin->all_bin_parse_num].download_addr = 0x00000000; + bin->header_info[bin->all_bin_parse_num].app_version = 0x00000000; + bin->header_info[bin->all_bin_parse_num].valid_data_len = 0x00000000; + bin->all_bin_parse_num += 1; +} + +int aw_parse_each_of_multi_bins_1_0_0(unsigned int bin_num, int bin_serial_num, + struct aw_bin *bin) +{ + int ret = 0; + unsigned int bin_start_addr = 0; + unsigned int valid_data_len = 0; + DBG("aw_bin_parse enter multi bin branch -- %s\n", __func__); + if (!bin_serial_num) { + bin_start_addr = GET_32_DATA(*(bin->p_addr + 67), + *(bin->p_addr + 66), + *(bin->p_addr + 65), + *(bin->p_addr + 64)); + bin->p_addr += (60 + bin_start_addr); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + bin->header_info[bin->all_bin_parse_num - + 1].valid_data_addr + 4 + 8 * bin_num + 60; + } else { + valid_data_len = + bin->header_info[bin->all_bin_parse_num - 1].bin_data_len; + bin->p_addr += (60 + valid_data_len); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + bin->header_info[bin->all_bin_parse_num - + 1].valid_data_addr + + bin->header_info[bin->all_bin_parse_num - 1].bin_data_len + + 60; + } + + ret = aw_parse_bin_header_1_0_0(bin); + return ret; +} + +/* Get the number of bins in multi bins, and set a for loop, loop processing each bin data */ +int aw_get_multi_bin_header_1_0_0(struct aw_bin *bin) +{ + int i = 0; + int ret = 0; + unsigned int bin_num = 0; + DBG("aw_bin_parse enter multi bin branch -- %s\n", __func__); + bin_num = GET_32_DATA(*(bin->p_addr + 63), + *(bin->p_addr + 62), + *(bin->p_addr + 61), *(bin->p_addr + 60)); + if (bin->multi_bin_parse_num == 1) { + bin->header_info[bin->all_bin_parse_num].valid_data_addr = 60; + } + aw_get_single_bin_header_1_0_0(bin); + + for (i = 0; i < bin_num; i++) { + DBG("aw_bin_parse enter multi bin for is %d\n", i); + ret = aw_parse_each_of_multi_bins_1_0_0(bin_num, i, bin); + if (ret < 0) { + return ret; + } + } + return 0; +} + +/******************************************************** +* +* If the bin framework header version is 1.0.0, + determine the data type of bin, and then perform different processing + according to the data type + If it is a single bin data type, write the data directly into the structure array + If it is a multi-bin data type, first obtain the number of bins, + and then recursively call the bin frame header processing function + according to the bin number to process the frame header information of each bin separately +* +********************************************************/ +int aw_parse_bin_header_1_0_0(struct aw_bin *bin) +{ + int ret = 0; + unsigned int bin_data_type; + DBG("enter %s\n", __func__); + bin_data_type = GET_32_DATA(*(bin->p_addr + 11), + *(bin->p_addr + 10), + *(bin->p_addr + 9), *(bin->p_addr + 8)); + DBG("aw_bin_parse bin_data_type 0x%x\n", bin_data_type); + switch (bin_data_type) { + case DATA_TYPE_REGISTER: + case DATA_TYPE_DSP_REG: + case DATA_TYPE_SOC_APP: + /* Divided into two processing methods, + one is single bin processing, + and the other is single bin processing in multi bin */ + DBG("aw_bin_parse enter single bin branch\n"); + bin->single_bin_parse_num += 1; + DBG("%s bin->single_bin_parse_num is %d\n", __func__, + bin->single_bin_parse_num); + if (!bin->multi_bin_parse_num) { + bin->header_info[bin-> + all_bin_parse_num].valid_data_addr = + 60; + } + aw_get_single_bin_header_1_0_0(bin); + break; + case DATA_TYPE_MULTI_BINS: + /* Get the number of times to enter multi bins */ + DBG("aw_bin_parse enter multi bin branch\n"); + bin->multi_bin_parse_num += 1; + DBG("%s bin->multi_bin_parse_num is %d\n", __func__, + bin->multi_bin_parse_num); + ret = aw_get_multi_bin_header_1_0_0(bin); + if (ret < 0) { + return ret; + } + break; + default: + DBG_ERR("aw_bin_parse Unrecognized this bin data type\n"); + return -2; + } + return 0; +} + +/* get the bin's header version */ +static int aw_check_bin_header_version(struct aw_bin *bin) +{ + int ret = 0; + unsigned int header_version = 0; + + header_version = GET_32_DATA(*(bin->p_addr + 7), + *(bin->p_addr + 6), + *(bin->p_addr + 5), *(bin->p_addr + 4)); + + DBG("aw_bin_parse header_version 0x%x\n", header_version); + + /* Write data to the corresponding structure array + according to different formats of the bin frame header version */ + switch (header_version) { + case HEADER_VERSION_1_0_0: + ret = aw_parse_bin_header_1_0_0(bin); + return ret; + default: + DBG_ERR("aw_bin_parse Unrecognized this bin header version \n"); + return -1; + } +} + +int aw_parsing_bin_file(struct aw_bin *bin) +{ + int i = 0; + int ret = 0; + + DBG("aw_bin_parse code version:%s\n", AWINIC_CODE_VERSION); + if (!bin) { + DBG_ERR("aw_bin_parse bin is NULL\n"); + return -8; + } + bin->p_addr = bin->info.data; + bin->all_bin_parse_num = 0; + bin->multi_bin_parse_num = 0; + bin->single_bin_parse_num = 0; + + /* filling bins header info */ + ret = aw_check_bin_header_version(bin); + if (ret < 0) { + DBG_ERR("aw_bin_parse check bin header version error\n"); + return ret; + } + bin->p_addr = NULL; + + /* check bin header info */ + for (i = 0; i < bin->all_bin_parse_num; i++) { + /* check sum */ + ret = aw_check_sum(bin, i); + if (ret < 0) { + DBG_ERR("aw_bin_parse check sum data error\n"); + return ret; + } + /* check bin data version */ + ret = aw_check_data_version(bin, i); + if (ret < 0) { + DBG_ERR("aw_bin_parse check data version error\n"); + return ret; + } + /* check valid data */ + if (bin->header_info[i].bin_data_ver == DATA_VERSION_V1) { + /* check register num */ + if (bin->header_info[i].bin_data_type == + DATA_TYPE_REGISTER) { + ret = aw_check_register_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check register num error\n"); + return ret; + } + /* check dsp reg num */ + } else if (bin->header_info[i].bin_data_type == + DATA_TYPE_DSP_REG) { + ret = aw_check_dsp_reg_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check dsp reg num error\n"); + return ret; + } + /* check soc app num */ + } else if (bin->header_info[i].bin_data_type == + DATA_TYPE_SOC_APP) { + ret = aw_check_soc_app_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check soc app num error\n"); + return ret; + } + } else { + bin->header_info[i].valid_data_len = + bin->header_info[i].bin_data_len; + } + } + } + DBG("aw_bin_parse parsing success\n"); + + return 0; +} diff --git a/drivers/misc/awinic/aw8624/Android.mk b/drivers/misc/awinic/aw8624/Android.mk new file mode 100644 index 000000000000..cbe49f2f90d5 --- /dev/null +++ b/drivers/misc/awinic/aw8624/Android.mk @@ -0,0 +1,12 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := aw8624.ko +LOCAL_MODULE_TAGS := optional +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/awinic/aw8624/Kbuild b/drivers/misc/awinic/aw8624/Kbuild new file mode 100644 index 000000000000..8cf9a56f32f5 --- /dev/null +++ b/drivers/misc/awinic/aw8624/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +obj-m += aw8624.o diff --git a/drivers/misc/awinic/aw8624/Makefile b/drivers/misc/awinic/aw8624/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/misc/awinic/aw8624/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/misc/awinic/aw8624/aw8624.c b/drivers/misc/awinic/aw8624/aw8624.c new file mode 100644 index 000000000000..f7114932fc39 --- /dev/null +++ b/drivers/misc/awinic/aw8624/aw8624.c @@ -0,0 +1,4106 @@ +/* +* aw8624.c aw8624 haptic module +* +* Version: v1.3.9 +* +* Copyright (c) 2018 AWINIC Technology CO., LTD +* +* Author: Joseph +* +* 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw8624.h" +#include "aw8624_reg.h" +#include + +/****************************************************** + * + * Marco + * + ******************************************************/ +#define AW8624_I2C_NAME "aw8624_haptic" +#define AW8624_HAPTIC_NAME "aw8624_haptic" +#define AW8624_VERSION "v1.3.9" +#define AWINIC_RAM_UPDATE_DELAY +#define AW_I2C_RETRIES 2 +#define AW_I2C_RETRY_DELAY 2 +#define AW_READ_CHIPID_RETRIES 5 +#define AW_READ_CHIPID_RETRY_DELAY 2 +#define AW8624_MAX_DSP_START_TRY_COUNT 10 +#define AW8624_MAX_FIRMWARE_LOAD_CNT 20 +#define OSC_CALIBRATION_T_LENGTH 5100000 +#define PM_QOS_VALUE_VB 400 +struct pm_qos_request pm_qos_req_vb; + +/****************************************************** + * + * variable + * + ******************************************************/ +static char *aw8624_ram_name = "aw8624_haptic.bin"; + +#define AW8624_RTP_NAME_MAX 64 +static char aw8624_rtp_name[][AW8624_RTP_NAME_MAX] = { + {"aw8624_osc_rtp_24K_5s.bin"}, + {"aw8624_rtp.bin"}, + {"aw8624_rtp_lighthouse.bin"}, + {"aw8624_rtp_silk.bin"}, +}; +struct aw8624_container *aw8624_rtp; +struct aw8624 *g_aw8624; +struct aw8624_dts_info aw8624_dts_data; + +/****************************************************** +* +* functions +* +******************************************************/ +static void aw8624_interrupt_clear(struct aw8624 *aw8624); +static int +aw8624_haptic_trig1_config(struct aw8624 *aw8624, unsigned char flag); + +/****************************************************** +* +* aw8624 i2c write/read +* +******************************************************/ +static int aw8624_i2c_write(struct aw8624 *aw8624, + unsigned char reg_addr, unsigned char reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + while (cnt < AW_I2C_RETRIES) { + ret = + i2c_smbus_write_byte_data(aw8624->i2c, reg_addr, reg_data); + if (ret < 0) { + pr_err("%s: i2c_write cnt=%d error=%d\n", + __func__, cnt, ret); + } else { + break; + } + cnt++; + usleep_range(2000, 3000); + } + + return ret; +} + +static int aw8624_i2c_read(struct aw8624 *aw8624, + unsigned char reg_addr, unsigned char *reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + while (cnt < AW_I2C_RETRIES) { + ret = i2c_smbus_read_byte_data(aw8624->i2c, reg_addr); + if (ret < 0) { + pr_err("%s: i2c_read cnt=%d error=%d\n", + __func__, cnt, ret); + } else { + *reg_data = ret; + break; + } + cnt++; + usleep_range(2000, 3000); + } + + return ret; +} + +static int aw8624_i2c_write_bits(struct aw8624 *aw8624, + unsigned char reg_addr, unsigned int mask, unsigned char reg_data) +{ + unsigned char reg_val = 0; + + aw8624_i2c_read(aw8624, reg_addr, ®_val); + reg_val &= mask; + reg_val |= reg_data; + aw8624_i2c_write(aw8624, reg_addr, reg_val); + + return 0; +} + + +static int aw8624_i2c_writes(struct aw8624 *aw8624, + unsigned char reg_addr, unsigned char *buf, unsigned int len) +{ + int ret = -1; + unsigned char *data; + + data = kmalloc(len+1, GFP_KERNEL); + if (data == NULL) { + pr_err("%s: can not allocate memory\n", __func__); + return -ENOMEM; + } + + data[0] = reg_addr; + memcpy(&data[1], buf, len); + + ret = i2c_master_send(aw8624->i2c, data, len+1); + if (ret < 0) + pr_err("%s: i2c master send error\n", __func__); + + kfree(data); + + return ret; +} + +static void aw8624_interrupt_clear(struct aw8624 *aw8624) +{ + unsigned char reg_val = 0; + + aw8624_i2c_read(aw8624, AW8624_REG_SYSINT, ®_val); + pr_info("%s: reg SYSINT=0x%x\n", __func__, reg_val); +} + +static void aw8624_init_extra(struct aw8624 *aw8624) +{ + if (aw8624->IsUsedIRQ == true) { + aw8624_haptic_trig1_config(aw8624, true); + pr_info("enter %s\n", __func__); + } +} + +/***************************************************** + * + * ram update + * + *****************************************************/ +static void aw8624_rtp_loaded(const struct firmware *cont, void *context) +{ + struct aw8624 *aw8624 = context; + + if (!cont) { + pr_err("%s: failed to read %s\n", + __func__, aw8624_rtp_name[aw8624->rtp_file_num]); + release_firmware(cont); + return; + } + + pr_info("%s: loaded %s - size: %zu\n", __func__, + aw8624_rtp_name[aw8624->rtp_file_num], cont ? cont->size : 0); + + /* aw8624 rtp update */ + aw8624_rtp = vmalloc(cont->size+sizeof(int)); + if (!aw8624_rtp) { + release_firmware(cont); + pr_err("%s: Error allocating memory\n", __func__); + return; + } + aw8624_rtp->len = cont->size; + pr_info("%s: rtp size = %d\n", __func__, aw8624_rtp->len); + memcpy(aw8624_rtp->data, cont->data, cont->size); + release_firmware(cont); + + aw8624->rtp_init = 1; + aw8624_init_extra(aw8624); + pr_info("%s: rtp update complete\n", __func__); +} + +static int aw8624_rtp_update(struct aw8624 *aw8624) +{ + pr_info("%s enter\n", __func__); + + return request_firmware_nowait(THIS_MODULE, + FW_ACTION_HOTPLUG, + aw8624_rtp_name[aw8624->rtp_file_num], + aw8624->dev, + GFP_KERNEL, + aw8624, + aw8624_rtp_loaded); +} + +static void aw8624_container_update(struct aw8624 *aw8624, + struct aw8624_container *aw8624_cont) +{ + int i = 0; + unsigned int shift = 0; + + mutex_lock(&aw8624->lock); + + aw8624->ram.baseaddr_shift = 2; + aw8624->ram.ram_shift = 4; + + /* RAMINIT Enable */ + aw8624_i2c_write_bits(aw8624, AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_RAMINIT_MASK, AW8624_BIT_SYSCTRL_RAMINIT_EN); + + /* base addr */ + shift = aw8624->ram.baseaddr_shift; + aw8624->ram.base_addr = (unsigned int)((aw8624_cont->data[0+shift]<<8) | + (aw8624_cont->data[1+shift])); + pr_info("%s: base_addr=0x%4x\n", __func__, aw8624->ram.base_addr); + + aw8624_i2c_write(aw8624, + AW8624_REG_BASE_ADDRH, + aw8624_cont->data[0+shift]); + aw8624_i2c_write(aw8624, + AW8624_REG_BASE_ADDRL, + aw8624_cont->data[1+shift]); + + aw8624_i2c_write(aw8624, + AW8624_REG_FIFO_AEH, + (unsigned char)((aw8624->ram.base_addr>>2)>>8)); + aw8624_i2c_write(aw8624, + AW8624_REG_FIFO_AEL, + (unsigned char)((aw8624->ram.base_addr>>2)&0x00FF)); + aw8624_i2c_write(aw8624, + AW8624_REG_FIFO_AFH, + (unsigned char)((aw8624->ram.base_addr + - (aw8624->ram.base_addr>>2))>>8)); + aw8624_i2c_write(aw8624, + AW8624_REG_FIFO_AFL, + (unsigned char)((aw8624->ram.base_addr + -(aw8624->ram.base_addr>>2))&0x00FF)); + + /* ram */ + shift = aw8624->ram.baseaddr_shift; + aw8624_i2c_write(aw8624, + AW8624_REG_RAMADDRH, aw8624_cont->data[0+shift]); + aw8624_i2c_write(aw8624, + AW8624_REG_RAMADDRL, aw8624_cont->data[1+shift]); + shift = aw8624->ram.ram_shift; + for (i = shift; i < aw8624_cont->len; i++) { + aw8624_i2c_write(aw8624, + AW8624_REG_RAMDATA, aw8624_cont->data[i]); + } + + /* RAMINIT Disable */ + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_RAMINIT_MASK, + AW8624_BIT_SYSCTRL_RAMINIT_OFF); + + mutex_unlock(&aw8624->lock); + + pr_info("%s exit\n", __func__); +} + +static void aw8624_ram_loaded(const struct firmware *cont, void *context) +{ + struct aw8624 *aw8624 = context; + struct aw8624_container *aw8624_fw; + int i = 0; + unsigned short check_sum = 0; + + if (!cont) { + pr_err("%s: failed to read %s\n", __func__, aw8624_ram_name); + release_firmware(cont); + return; + } + + pr_info("%s: loaded %s - size: %zu\n", __func__, aw8624_ram_name, + cont ? cont->size : 0); + + /* check sum */ + for (i = 2; i < cont->size; i++) + check_sum += cont->data[i]; + + if (check_sum == (unsigned short)((cont->data[0]<<8)|(cont->data[1]))) { + pr_info("%s: check sum pass : 0x%04x\n", __func__, check_sum); + aw8624->ram.check_sum = check_sum; + } else { + pr_err("%s: check sum err: check_sum=0x%04x\n", + __func__, check_sum); + return; + } + + /* aw8624 ram update */ + aw8624_fw = kzalloc(cont->size+sizeof(int), GFP_KERNEL); + if (!aw8624_fw) { + release_firmware(cont); + pr_err("%s: Error allocating memory\n", __func__); + return; + } + aw8624_fw->len = cont->size; + memcpy(aw8624_fw->data, cont->data, cont->size); + release_firmware(cont); + + aw8624_container_update(aw8624, aw8624_fw); + + aw8624->ram.len = aw8624_fw->len; + + kfree(aw8624_fw); + + aw8624->ram_init = 1; + pr_info("%s: fw update complete\n", __func__); + + if (aw8624->IsUsedIRQ) + aw8624_rtp_update(aw8624); + +} + +static int aw8624_ram_update(struct aw8624 *aw8624) +{ + aw8624->ram_init = 0; + aw8624->rtp_init = 0; + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw8624_ram_name, aw8624->dev, GFP_KERNEL, + aw8624, aw8624_ram_loaded); +} + +static void aw8624_ram_work_routine(struct work_struct *work) +{ + struct aw8624 *aw8624 = + container_of(work, struct aw8624, ram_work.work); + + aw8624_ram_update(aw8624); + +} + +static int aw8624_ram_init(struct aw8624 *aw8624) +{ + if (aw8624->ram_update_delay) { + int ram_timer_val = 5000; + + INIT_DELAYED_WORK(&aw8624->ram_work, aw8624_ram_work_routine); + schedule_delayed_work(&aw8624->ram_work, + msecs_to_jiffies(ram_timer_val)); + } else { + aw8624_ram_update(aw8624); + } + return 0; +} + +//Daniel 20200622 modify start +/*static void aw8624_stop_work_routine(struct work_struct *work) +{ + struct aw8624 *aw8624 = + container_of(work, struct aw8624, stop_work.work); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_CONT_CTRL, + AW8624_BIT_CONT_CTRL_EN_CLOSE_MASK, + AW8624_BIT_CONT_CTRL_CLOSE_PLAYBACK); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_CONT_CTRL, + AW8624_BIT_CONT_CTRL_F0_DETECT_MASK, + AW8624_BIT_CONT_CTRL_F0_DETECT_DISABLE); +}*/ +//Daniel 20200622 modify end + + +/***************************************************** + * + * haptic control + * + *****************************************************/ +static int aw8624_haptic_softreset(struct aw8624 *aw8624) +{ + aw8624_i2c_write(aw8624, AW8624_REG_ID, 0xAA); + usleep_range(2000, 2500); + return 0; +} + +static int aw8624_haptic_play_init(struct aw8624 *aw8624) +{ + if (aw8624->play_mode == AW8624_HAPTIC_CONT_MODE) + { + aw8624_i2c_write(aw8624, + AW8624_REG_SW_BRAKE, + (unsigned char)(aw8624_dts_data.aw8624_sw_brake[0])); + } else { + aw8624_i2c_write(aw8624, + AW8624_REG_SW_BRAKE, + (unsigned char)(aw8624_dts_data.aw8624_sw_brake[1])); + } + return 0; +} + +static int aw8624_haptic_active(struct aw8624 *aw8624) +{ + aw8624_haptic_play_init(aw8624); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_WORK_MODE_MASK, + AW8624_BIT_SYSCTRL_ACTIVE); + aw8624_interrupt_clear(aw8624); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSINTM, + AW8624_BIT_SYSINTM_UVLO_MASK, + AW8624_BIT_SYSINTM_UVLO_EN); + return 0; +} + +static int aw8624_haptic_play_mode(struct aw8624 *aw8624, + unsigned char play_mode) +{ + switch (play_mode) { + case AW8624_HAPTIC_STANDBY_MODE: + aw8624->play_mode = AW8624_HAPTIC_STANDBY_MODE; + aw8624_i2c_write_bits(aw8624, AW8624_REG_SYSINTM, + AW8624_BIT_SYSINTM_UVLO_MASK, + AW8624_BIT_SYSINTM_UVLO_OFF); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_WORK_MODE_MASK, + AW8624_BIT_SYSCTRL_STANDBY); + break; + case AW8624_HAPTIC_RAM_MODE: + aw8624->play_mode = AW8624_HAPTIC_RAM_MODE; + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8624_BIT_SYSCTRL_PLAY_MODE_RAM); + aw8624_haptic_active(aw8624); + break; + case AW8624_HAPTIC_RAM_LOOP_MODE: + aw8624->play_mode = AW8624_HAPTIC_RAM_LOOP_MODE; + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8624_BIT_SYSCTRL_PLAY_MODE_RAM); + aw8624_haptic_active(aw8624); + break; + case AW8624_HAPTIC_RTP_MODE: + aw8624->play_mode = AW8624_HAPTIC_RTP_MODE; + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8624_BIT_SYSCTRL_PLAY_MODE_RTP); + aw8624_haptic_active(aw8624); + break; + case AW8624_HAPTIC_TRIG_MODE: + aw8624->play_mode = AW8624_HAPTIC_TRIG_MODE; + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8624_BIT_SYSCTRL_PLAY_MODE_RAM); + aw8624_haptic_active(aw8624); + break; + case AW8624_HAPTIC_CONT_MODE: + aw8624->play_mode = AW8624_HAPTIC_CONT_MODE; + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8624_BIT_SYSCTRL_PLAY_MODE_CONT); + aw8624_haptic_active(aw8624); + break; + default: + dev_err(aw8624->dev, "%s: play mode %d err", + __func__, play_mode); + break; + } + return 0; +} + +static int aw8624_haptic_play_go(struct aw8624 *aw8624, bool flag) +{ + pr_debug("%s enter, flag = %d\n", __func__, flag); + if (!flag) { + do_gettimeofday(&aw8624->current_time); + aw8624->interval_us = (aw8624->current_time.tv_sec - + aw8624->pre_enter_time.tv_sec) * 1000000 + + (aw8624->current_time.tv_usec-aw8624->pre_enter_time.tv_usec); + if (aw8624->interval_us < 2000) { + pr_info("%s:aw8624->interval_us=%d\n", + __func__, aw8624->interval_us); + mdelay(2); + } + } + if (flag == true) { + aw8624_i2c_write_bits(aw8624, AW8624_REG_GO, + AW8624_BIT_GO_MASK, AW8624_BIT_GO_ENABLE); + do_gettimeofday(&aw8624->pre_enter_time); + } else { + aw8624_i2c_write_bits(aw8624, AW8624_REG_GO, + AW8624_BIT_GO_MASK, AW8624_BIT_GO_DISABLE); + } + return 0; +} + +static int aw8624_haptic_stop_delay(struct aw8624 *aw8624) +{ + unsigned char reg_val = 0; + unsigned int cnt = 100; + + while (cnt--) { + aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, ®_val); + if ((reg_val&0x0f) == 0x00) + return 0; + mdelay(2); + + pr_info("%s wait for standby, reg glb_state=0x%02x\n", + __func__, reg_val); + } + pr_err("%s do not enter standby automatically\n", __func__); + return 0; +} + +static int aw8624_haptic_stop(struct aw8624 *aw8624) +{ + aw8624_haptic_play_go(aw8624, false); + aw8624_haptic_stop_delay(aw8624); + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_STANDBY_MODE); + + return 0; +} + +static int aw8624_haptic_start(struct aw8624 *aw8624) +{ + aw8624_haptic_active(aw8624); + aw8624_haptic_play_go(aw8624, true); + + return 0; +} + +static int aw8624_haptic_set_wav_seq(struct aw8624 *aw8624, + unsigned char wav, unsigned char seq) +{ + aw8624_i2c_write(aw8624, AW8624_REG_WAVSEQ1+wav, seq); + return 0; +} + +static int aw8624_haptic_set_wav_loop(struct aw8624 *aw8624, + unsigned char wav, unsigned char loop) +{ + unsigned char tmp = 0; + + if (wav%2) { + tmp = loop<<0; + aw8624_i2c_write_bits(aw8624, AW8624_REG_WAVLOOP1+(wav/2), + AW8624_BIT_WAVLOOP_SEQNP1_MASK, tmp); + } else { + tmp = loop<<4; + aw8624_i2c_write_bits(aw8624, AW8624_REG_WAVLOOP1+(wav/2), + AW8624_BIT_WAVLOOP_SEQN_MASK, tmp); + } + + return 0; +} + +static int +aw8624_haptic_set_repeat_wav_seq(struct aw8624 *aw8624, unsigned char seq) +{ + aw8624_haptic_set_wav_seq(aw8624, 0x00, seq); + aw8624_haptic_set_wav_loop(aw8624, + 0x00, + AW8624_BIT_WAVLOOP_INIFINITELY); + + return 0; +} + +static int aw8624_haptic_set_gain(struct aw8624 *aw8624, unsigned char gain) +{ + aw8624_i2c_write(aw8624, AW8624_REG_DATDBG, gain); + return 0; +} + +static int aw8624_haptic_set_pwm(struct aw8624 *aw8624, unsigned char mode) +{ + switch (mode) { + case AW8624_PWM_48K: + aw8624_i2c_write_bits(aw8624, + AW8624_REG_PWMDBG, + AW8624_BIT_PWMDBG_PWM_MODE_MASK, + AW8624_BIT_PWMDBG_PWM_48K); + break; + case AW8624_PWM_24K: + aw8624_i2c_write_bits(aw8624, + AW8624_REG_PWMDBG, + AW8624_BIT_PWMDBG_PWM_MODE_MASK, + AW8624_BIT_PWMDBG_PWM_24K); + break; + case AW8624_PWM_12K: + aw8624_i2c_write_bits(aw8624, + AW8624_REG_PWMDBG, + AW8624_BIT_PWMDBG_PWM_MODE_MASK, + AW8624_BIT_PWMDBG_PWM_12K); + break; + default: + break; + } + return 0; +} + +static int +aw8624_haptic_play_repeat_seq(struct aw8624 *aw8624, unsigned char flag) +{ + if (flag) { + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_RAM_LOOP_MODE); + aw8624_haptic_start(aw8624); + } + + return 0; +} + +static int aw8624_haptic_swicth_motorprotect_config(struct aw8624 *aw8624, + unsigned char addr, unsigned char val) +{ + if (addr == 1) { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DETCTRL, + AW8624_BIT_DETCTRL_PROTECT_MASK, + AW8624_BIT_DETCTRL_PROTECT_SHUTDOWN); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_PWMPRC, + AW8624_BIT_PWMPRC_PRC_EN_MASK, + AW8624_BIT_PWMPRC_PRC_ENABLE); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_PRLVL, + AW8624_BIT_PRLVL_PR_EN_MASK, + AW8624_BIT_PRLVL_PR_ENABLE); + } else if (addr == 0) { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DETCTRL, + AW8624_BIT_DETCTRL_PROTECT_MASK, + AW8624_BIT_DETCTRL_PROTECT_NO_ACTION); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_PWMPRC, + AW8624_BIT_PWMPRC_PRC_EN_MASK, + AW8624_BIT_PWMPRC_PRC_DISABLE); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_PRLVL, + AW8624_BIT_PRLVL_PR_EN_MASK, + AW8624_BIT_PRLVL_PR_DISABLE); + } else if (addr == 0x2d) { + aw8624_i2c_write_bits(aw8624, AW8624_REG_PWMPRC, + AW8624_BIT_PWMPRC_PRCTIME_MASK, val); + } else if (addr == 0x3e) { + aw8624_i2c_write_bits(aw8624, AW8624_REG_PRLVL, + AW8624_BIT_PRLVL_PRLVL_MASK, val); + } else if (addr == 0x3f) { + aw8624_i2c_write_bits(aw8624, AW8624_REG_PRTIME, + AW8624_BIT_PRTIME_PRTIME_MASK, val); + } else { + /*nothing to do;*/ + } + + return 0; +} + +static int aw8624_haptic_trig1_config(struct aw8624 *aw8624, unsigned char flag) +{ + if (flag) { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DBGCTRL, + AW8624_BIT_DBGCTRL_INTN_TRG_SEL_MASK, + AW8624_BIT_DBGCTRL_TRG_SEL_ENABLE); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_TRG_CFG2, + AW8624_BIT_TRGCFG2_TRG1_ENABLE_MASK, + AW8624_BIT_TRGCFG2_TRG1_ENABLE); + } else { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DBGCTRL, + AW8624_BIT_DBGCTRL_INTN_TRG_SEL_MASK, + AW8624_BIT_DBGCTRL_INTN_SEL_ENABLE); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_TRG_CFG2, + AW8624_BIT_TRGCFG2_TRG1_ENABLE_MASK, + AW8624_BIT_TRGCFG2_TRG1_DISABLE); + } + return 0; +} + +static int aw8624_haptic_vbat_mode(struct aw8624 *aw8624, unsigned char flag) +{ + if (flag == AW8624_HAPTIC_VBAT_HW_COMP_MODE) { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_ADCTEST, + AW8624_BIT_DETCTRL_VBAT_MODE_MASK, + AW8624_BIT_DETCTRL_VBAT_HW_COMP); + } else { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_ADCTEST, + AW8624_BIT_DETCTRL_VBAT_MODE_MASK, + AW8624_BIT_DETCTRL_VBAT_SW_COMP); + } + return 0; +} + +static int aw8624_haptic_set_f0_preset(struct aw8624 *aw8624) +{ + unsigned int f0_reg = 0; + + f0_reg = 1000000000/(aw8624->f0_pre*aw8624_dts_data.aw8624_f0_coeff); + aw8624_i2c_write(aw8624, + AW8624_REG_F_PRE_H, + (unsigned char)((f0_reg>>8)&0xff)); + aw8624_i2c_write(aw8624, + AW8624_REG_F_PRE_L, + (unsigned char)((f0_reg>>0)&0xff)); + + return 0; +} + +static int aw8624_haptic_read_f0(struct aw8624 *aw8624) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned int f0_reg = 0; + unsigned long f0_tmp = 0; + + ret = aw8624_i2c_read(aw8624, AW8624_REG_F_LRA_F0_H, ®_val); + f0_reg = (reg_val<<8); + ret = aw8624_i2c_read(aw8624, AW8624_REG_F_LRA_F0_L, ®_val); + f0_reg |= (reg_val<<0); + if (!f0_reg) { + aw8624->f0 = 0; + pr_info("%s : get f0 failed with the value becoming 0!\n", + __func__); + return 0; + } + + f0_tmp = 1000000000 / (f0_reg * aw8624_dts_data.aw8624_f0_coeff); + aw8624->f0 = (unsigned int)f0_tmp; + pr_info("%s f0=%d\n", __func__, aw8624->f0); + return 0; +} + +static int aw8624_haptic_read_cont_f0(struct aw8624 *aw8624) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned int f0_reg = 0; + unsigned long f0_tmp = 0; + + ret = aw8624_i2c_read(aw8624, AW8624_REG_F_LRA_CONT_H, ®_val); + f0_reg = (reg_val<<8); + ret = aw8624_i2c_read(aw8624, AW8624_REG_F_LRA_CONT_L, ®_val); + f0_reg |= (reg_val<<0); + if (!f0_reg) { + aw8624->cont_f0 = 0; + pr_info("%s: failed to reading cont f0 with 0\n", __func__); + return 0; + } + + f0_tmp = 1000000000/(f0_reg*aw8624_dts_data.aw8624_f0_coeff); + aw8624->cont_f0 = (unsigned int)f0_tmp; + pr_info("%s cont_f0=%d\n", __func__, aw8624->cont_f0); + return 0; +} + +static int aw8624_haptic_read_beme(struct aw8624 *aw8624) +{ + int ret = 0; + unsigned char reg_val = 0; + + ret = aw8624_i2c_read(aw8624, AW8624_REG_WAIT_VOL_MP, ®_val); + aw8624->max_pos_beme = (reg_val<<0); + ret = aw8624_i2c_read(aw8624, AW8624_REG_WAIT_VOL_MN, ®_val); + aw8624->max_neg_beme = (reg_val<<0); + + pr_info("%s max_pos_beme=%d\n", __func__, aw8624->max_pos_beme); + pr_info("%s max_neg_beme=%d\n", __func__, aw8624->max_neg_beme); + + return 0; +} + +static int aw8624_haptic_ram_config(struct aw8624 *aw8624) +{ + unsigned char wavseq = 0; + unsigned char wavloop = 0; + + if(aw8624->singlecycle != 0) { + aw8624->singlecycle = 0; + return 0; + } + + if ((aw8624->duration > 0) && (aw8624->duration < + aw8624_dts_data.aw8624_duration_time[0])) { + wavseq = aw8624_dts_data.aw8624_wavseq[2]; /*3*/ + wavloop = 0; + } else if ( + (aw8624->duration >= aw8624_dts_data.aw8624_duration_time[0]) && + (aw8624->duration < aw8624_dts_data.aw8624_duration_time[1])) { + wavseq = aw8624_dts_data.aw8624_wavseq[1]; /*2*/ + wavloop = 0; + } else if ( + (aw8624->duration >= aw8624_dts_data.aw8624_duration_time[1]) && + (aw8624->duration < aw8624_dts_data.aw8624_duration_time[2])) { + wavseq = aw8624_dts_data.aw8624_wavseq[0]; /*1*/ + wavloop = 0; + } else if + (aw8624->duration >= aw8624_dts_data.aw8624_duration_time[2]) { + wavseq = aw8624_dts_data.aw8624_wavseq[3]; /*4*/ + wavloop = 15; /*long vibration*/ + } else { + wavseq = 0; + wavloop = 0; + } + + aw8624_haptic_set_wav_seq(aw8624, 0, wavseq); + aw8624_haptic_set_wav_loop(aw8624, 0, wavloop); + aw8624_haptic_set_wav_seq(aw8624, 1, 0); + aw8624_haptic_set_wav_loop(aw8624, 1, 0); + + return 0; +} + +static int aw8624_vbat_monitor_detector(struct aw8624 *aw8624) +{ + unsigned char reg_val = 0; + unsigned char reg_val_sysctrl = 0; + unsigned char reg_val_detctrl = 0; + unsigned int vbat = 0; + + aw8624_haptic_stop(aw8624); + aw8624_i2c_read(aw8624, AW8624_REG_SYSCTRL, ®_val_sysctrl); + aw8624_i2c_read(aw8624, AW8624_REG_DETCTRL, ®_val_detctrl); + /*step 1:EN_RAMINIT*/ + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_RAMINIT_MASK, + AW8624_BIT_SYSCTRL_RAMINIT_EN); + + /*step 2 :launch offset cali */ + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DETCTRL, + AW8624_BIT_DETCTRL_DIAG_GO_MASK, + AW8624_BIT_DETCTRL_DIAG_GO_ENABLE); + /*step 3 :delay */ + usleep_range(2000, 2500); + + /*step 4 :launch power supply testing */ + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DETCTRL, + AW8624_BIT_DETCTRL_VBAT_GO_MASK, + AW8624_BIT_DETCTRL_VABT_GO_ENABLE); + usleep_range(2000, 2500); + + aw8624_i2c_read(aw8624, AW8624_REG_VBATDET, ®_val); + vbat = 6100 * reg_val / 256; + + /*step 5: return val*/ + aw8624_i2c_write(aw8624, AW8624_REG_SYSCTRL, reg_val_sysctrl); + + return vbat; +} + +static int aw8624_lra_resistance_detector(struct aw8624 *aw8624) +{ + unsigned char reg_val = 0; + unsigned char reg_val_sysctrl = 0; + unsigned char reg_val_anactrl = 0; + unsigned char reg_val_d2scfg = 0; + unsigned int r_lra = 0; + + mutex_lock(&aw8624->lock); + aw8624_i2c_read(aw8624, AW8624_REG_SYSCTRL, ®_val_sysctrl); + aw8624_i2c_read(aw8624, AW8624_REG_ANACTRL, ®_val_anactrl); + aw8624_i2c_read(aw8624, AW8624_REG_D2SCFG, ®_val_d2scfg); + aw8624_haptic_stop(aw8624); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_RAMINIT_MASK, + AW8624_BIT_SYSCTRL_RAMINIT_EN); + + + aw8624_i2c_write_bits(aw8624, + AW8624_REG_ANACTRL, + AW8624_BIT_ANACTRL_EN_IO_PD1_MASK, + AW8624_BIT_ANACTRL_EN_IO_PD1_HIGH); + + aw8624_i2c_write_bits(aw8624, + AW8624_REG_D2SCFG, + AW8624_BIT_D2SCFG_CLK_ADC_MASK, + AW8624_BIT_D2SCFG_CLK_ASC_1P5MHZ); + + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DETCTRL, + AW8624_BIT_DETCTRL_RL_OS_MASK, + AW8624_BIT_DETCTRL_RL_DETECT); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DETCTRL, + AW8624_BIT_DETCTRL_DIAG_GO_MASK, + AW8624_BIT_DETCTRL_DIAG_GO_ENABLE); + usleep_range(3000, 3500); + aw8624_i2c_read(aw8624, AW8624_REG_RLDET, ®_val); + r_lra = 298 * reg_val; + /*len += snprintf(buf+len, PAGE_SIZE-len, "r_lra=%dmohm\n", r_lra);*/ + + aw8624_i2c_write(aw8624, AW8624_REG_D2SCFG, reg_val_d2scfg); + aw8624_i2c_write(aw8624, AW8624_REG_ANACTRL, reg_val_anactrl); + aw8624_i2c_write(aw8624, AW8624_REG_SYSCTRL, reg_val_sysctrl); + + mutex_unlock(&aw8624->lock); + + return r_lra; +} + +static int aw8624_haptic_ram_vbat_comp(struct aw8624 *aw8624, bool flag) +{ + int temp_gain = 0; + int vbat = 0; + + if (flag) { + if (aw8624->ram_vbat_comp == + AW8624_HAPTIC_RAM_VBAT_COMP_ENABLE) { + vbat = aw8624_vbat_monitor_detector(aw8624); + temp_gain = aw8624->gain * AW8624_VBAT_REFER / vbat; + if + (temp_gain > (128*AW8624_VBAT_REFER/AW8624_VBAT_MIN)) { + temp_gain = + 128*AW8624_VBAT_REFER/AW8624_VBAT_MIN; + pr_debug("%s gain limit=%d\n", + __func__, temp_gain); + } + aw8624_haptic_set_gain(aw8624, temp_gain); + } else { + aw8624_haptic_set_gain(aw8624, aw8624->gain); + } + } else { + aw8624_haptic_set_gain(aw8624, aw8624->gain); + } + + return 0; +} + +static void aw8624_haptic_set_rtp_aei(struct aw8624 *aw8624, bool flag) +{ + if (flag) { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSINTM, + AW8624_BIT_SYSINTM_FF_AE_MASK, + AW8624_BIT_SYSINTM_FF_AE_EN); + } else { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSINTM, + AW8624_BIT_SYSINTM_FF_AE_MASK, + AW8624_BIT_SYSINTM_FF_AE_OFF); + } +} + +static unsigned char aw8624_haptic_rtp_get_fifo_afi(struct aw8624 *aw8624) +{ + unsigned char ret = 0; + unsigned char reg_val = 0; + + if (aw8624->osc_cali_flag == 1) { + aw8624_i2c_read(aw8624, AW8624_REG_SYSST, ®_val); + reg_val &= AW8624_BIT_SYSST_FF_AFS; + ret = reg_val >> 3; + } else { + aw8624_i2c_read(aw8624, AW8624_REG_SYSINT, ®_val); + reg_val &= AW8624_BIT_SYSINT_FF_AFI; + ret = reg_val >> 3; + } + return ret; +} + +static unsigned char aw8624_haptic_rtp_get_fifo_afs(struct aw8624 *aw8624) +{ + unsigned char ret = 0; + unsigned char reg_val = 0; + + aw8624_i2c_read(aw8624, AW8624_REG_SYSST, ®_val); + reg_val &= AW8624_BIT_SYSST_FF_AFS; + ret = reg_val >> 3; + + return ret; +} + +static int aw8624_haptic_rtp_init(struct aw8624 *aw8624) +{ + unsigned int buf_len = 0; + unsigned char glb_st = 0; + + pr_info("%s enter\n", __func__); + aw8624->rtp_cnt = 0; + mutex_lock(&aw8624->rtp_lock); + while ((!aw8624_haptic_rtp_get_fifo_afs(aw8624)) && + (aw8624->play_mode == AW8624_HAPTIC_RTP_MODE)) { + pr_info("%s rtp cnt = %d\n", __func__, aw8624->rtp_cnt); + if (!aw8624_rtp) { + pr_info("%s:aw8624_rtp is null break\n", __func__); + break; + } + + if ((aw8624->rtp_cnt < aw8624->ram.base_addr)) { + if((aw8624_rtp->len-aw8624->rtp_cnt) < + (aw8624->ram.base_addr)) { + buf_len = aw8624_rtp->len-aw8624->rtp_cnt; + } else { + buf_len = (aw8624->ram.base_addr); + } + } else if ((aw8624_rtp->len - aw8624->rtp_cnt) < + (aw8624->ram.base_addr >> 2)) { + buf_len = aw8624_rtp->len - aw8624->rtp_cnt; + } else { + buf_len = (aw8624->ram.base_addr >> 2); + } + pr_info("%s buf_len = %d\n", __func__, buf_len); + aw8624_i2c_writes(aw8624, AW8624_REG_RTP_DATA, + &aw8624_rtp->data[aw8624->rtp_cnt], + buf_len); + + aw8624->rtp_cnt += buf_len; + aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, &glb_st); + if (aw8624->rtp_cnt == aw8624_rtp->len || + ((glb_st & 0x0f) == 0x00)) { + pr_info("%s: rtp update complete. glb_st: 0x%x\n", + __func__, glb_st); + aw8624->rtp_cnt = 0; + mutex_unlock(&aw8624->rtp_lock); + return 0; + } + } + mutex_unlock(&aw8624->rtp_lock); + + if (aw8624->play_mode == AW8624_HAPTIC_RTP_MODE) + aw8624_haptic_set_rtp_aei(aw8624, true); + + pr_info("%s exit\n", __func__); + + return 0; +} + +static void aw8624_rtp_work_routine(struct work_struct *work) +{ + const struct firmware *rtp_file; + int ret = -1; + struct aw8624 *aw8624 = container_of(work, struct aw8624, rtp_work); + + /* fw loaded */ + pr_info("%s enter\n", __func__); + mutex_lock(&aw8624->rtp_lock); + ret = request_firmware(&rtp_file, + aw8624_rtp_name[aw8624->rtp_file_num], aw8624->dev); + if (ret < 0) { + pr_err("%s: failed to read %s\n", __func__, + aw8624_rtp_name[aw8624->rtp_file_num]); + mutex_unlock(&aw8624->rtp_lock); + return; + } + aw8624->rtp_init = 0; + vfree(aw8624_rtp); + aw8624_rtp = vmalloc(rtp_file->size+sizeof(int)); + if (!aw8624_rtp) { + release_firmware(rtp_file); + pr_err("%s: error allocating memory\n", __func__); + mutex_unlock(&aw8624->rtp_lock); + return; + } + aw8624_rtp->len = rtp_file->size; + pr_info("%s: rtp file [%s] size = %d\n", __func__, + aw8624_rtp_name[aw8624->rtp_file_num], aw8624_rtp->len); + memcpy(aw8624_rtp->data, rtp_file->data, rtp_file->size); + mutex_unlock(&aw8624->rtp_lock); + release_firmware(rtp_file); + mutex_lock(&aw8624->lock); + aw8624->rtp_init = 1; + + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DBGCTRL, + AW8624_BIT_DBGCTRL_INTN_TRG_SEL_MASK, + AW8624_BIT_DBGCTRL_INTN_SEL_ENABLE); + + /* rtp mode config */ + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_RTP_MODE); + + /* haptic start */ + aw8624_haptic_start(aw8624); + aw8624_haptic_rtp_init(aw8624); + mutex_unlock(&aw8624->lock); +} + +/***************************************************** + * + * haptic - audio + * + *****************************************************/ +static enum hrtimer_restart +aw8624_haptic_audio_timer_func(struct hrtimer *timer) +{ + struct aw8624 *aw8624 = + container_of(timer, struct aw8624, haptic_audio.timer); + + schedule_work(&aw8624->haptic_audio.work); + hrtimer_start(&aw8624->haptic_audio.timer, + ktime_set(aw8624->haptic_audio.timer_val/1000, + (aw8624->haptic_audio.timer_val%1000)*1000000), + HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +static void aw8624_haptic_audio_work_routine(struct work_struct *work) +{ + struct aw8624 *aw8624 = + container_of(work, struct aw8624, haptic_audio.work); + + mutex_lock(&aw8624->haptic_audio.lock); + if (aw8624->haptic_audio.ctr[aw8624->haptic_audio.cnt].cmd == 0x01) { + if (aw8624->haptic_audio.ctr[aw8624->haptic_audio.cnt].play == + 0x01) { + pr_info("%s: haptic audio play start\n", __func__); + mutex_lock(&aw8624->lock); + aw8624_haptic_stop(aw8624); + + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_RAM_MODE); + + aw8624_haptic_set_wav_seq(aw8624, 0x00, + aw8624->haptic_audio.ctr[aw8624->haptic_audio.cnt].wavseq); + aw8624_haptic_set_wav_seq(aw8624, 0x01, 0x00); + + aw8624_haptic_set_wav_loop(aw8624, 0x00, + aw8624->haptic_audio.ctr[aw8624->haptic_audio.cnt].loop); + + aw8624_haptic_set_gain(aw8624, + aw8624->haptic_audio.ctr[aw8624->haptic_audio.cnt].gain); + + aw8624_haptic_start(aw8624); + mutex_unlock(&aw8624->lock); + } + aw8624->haptic_audio.ctr[aw8624->haptic_audio.cnt].cmd = 0; + } + + aw8624->haptic_audio.cnt++; + if (aw8624->haptic_audio.ctr[aw8624->haptic_audio.cnt].cmd == 0) { + aw8624->haptic_audio.cnt = 0; + pr_debug("%s: haptic play buffer restart\n", __func__); + } + mutex_unlock(&aw8624->haptic_audio.lock); +} + +/***************************************************** + * + * haptic cont + * + *****************************************************/ +static int aw8624_haptic_cont(struct aw8624 *aw8624) +{ + unsigned char brake0_level = 0; + unsigned char en_brake1 = 0; + unsigned char brake1_level = 0; + unsigned char en_brake2 = 0; + unsigned char brake2_level = 0; + unsigned char brake2_p_num = 0; + unsigned char brake1_p_num = 0; + unsigned char brake0_p_num = 0; + + pr_info("%s enter\n.", __func__); + /* work mode */ + aw8624_haptic_active(aw8624); + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_CONT_MODE); + + /* preset f0 */ + aw8624->f0_pre = aw8624->f0; + aw8624_haptic_set_f0_preset(aw8624); + + /* lpf */ + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DATCTRL, + AW8624_BIT_DATCTRL_FC_MASK, + AW8624_BIT_DATCTRL_FC_1000HZ); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DATCTRL, + AW8624_BIT_DATCTRL_LPF_ENABLE_MASK, + AW8624_BIT_DATCTRL_LPF_ENABLE); + + /* brake */ + en_brake1 = aw8624_dts_data.aw8624_cont_brake[0][0]; + en_brake2 = aw8624_dts_data.aw8624_cont_brake[0][1]; + brake0_level = aw8624_dts_data.aw8624_cont_brake[0][2]; + brake1_level = aw8624_dts_data.aw8624_cont_brake[0][3]; + brake2_level = aw8624_dts_data.aw8624_cont_brake[0][4]; + brake0_p_num = aw8624_dts_data.aw8624_cont_brake[0][5]; + brake1_p_num = aw8624_dts_data.aw8624_cont_brake[0][6]; + brake2_p_num = aw8624_dts_data.aw8624_cont_brake[0][7]; + + aw8624_i2c_write(aw8624, + AW8624_REG_BRAKE0_CTRL, + (brake0_level << 0)); + aw8624_i2c_write(aw8624, + AW8624_REG_BRAKE1_CTRL, + (en_brake1 << 7)|(brake1_level << 0)); + aw8624_i2c_write(aw8624, + AW8624_REG_BRAKE2_CTRL, + (en_brake2 << 7)|(brake2_level << 0)); + aw8624_i2c_write(aw8624, + AW8624_REG_BRAKE_NUM, + ((brake2_p_num << 6)|(brake1_p_num << 3) | + (brake0_p_num << 0))); + + /* cont config */ + aw8624_i2c_write_bits(aw8624, + AW8624_REG_CONT_CTRL, + AW8624_BIT_CONT_CTRL_ZC_DETEC_MASK, + AW8624_BIT_CONT_CTRL_ZC_DETEC_ENABLE); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_CONT_CTRL, + AW8624_BIT_CONT_CTRL_WAIT_PERIOD_MASK, + AW8624_BIT_CONT_CTRL_WAIT_1PERIOD); + + aw8624_i2c_write_bits(aw8624, + AW8624_REG_CONT_CTRL, + AW8624_BIT_CONT_CTRL_MODE_MASK, + AW8624_BIT_CONT_CTRL_BY_GO_SIGNAL); + + aw8624_i2c_write_bits(aw8624, + AW8624_REG_CONT_CTRL, + AW8624_BIT_CONT_CTRL_EN_CLOSE_MASK, + AW8624_BIT_CONT_CTRL_CLOSE_PLAYBACK); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_CONT_CTRL, + AW8624_BIT_CONT_CTRL_F0_DETECT_MASK, + AW8624_BIT_CONT_CTRL_F0_DETECT_DISABLE); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_CONT_CTRL, + AW8624_BIT_CONT_CTRL_O2C_MASK, + AW8624_BIT_CONT_CTRL_O2C_DISABLE); + + /* TD time */ + aw8624_i2c_write(aw8624, + AW8624_REG_TD_H, + (unsigned char)(aw8624->cont_td>>8)); + aw8624_i2c_write(aw8624, + AW8624_REG_TD_L, + (unsigned char)(aw8624->cont_td>>0)); + + + aw8624_i2c_write_bits(aw8624, + AW8624_REG_BEMF_NUM, + AW8624_BIT_BEMF_NUM_BRK_MASK, + aw8624->cont_num_brk); + aw8624_i2c_write(aw8624, + AW8624_REG_TIME_NZC, + 0x1f); + + /* f0 driver level */ + aw8624_i2c_write(aw8624, + AW8624_REG_DRV_LVL, + aw8624->cont_drv_lvl); + aw8624_i2c_write(aw8624, + AW8624_REG_DRV_LVL_OV, + aw8624->cont_drv_lvl_ov); + + /* cont play go */ + aw8624_haptic_play_go(aw8624, true); + + return 0; +} + +/***************************************************** + * + * haptic f0 cali + * + *****************************************************/ +static int aw8624_haptic_get_f0(struct aw8624 *aw8624) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned char f0_pre_num = 0; + unsigned char f0_wait_num = 0; + int f0_repeat_num = 0; + unsigned char f0_trace_num = 0; + unsigned int t_f0_ms = 0; + unsigned int t_f0_trace_ms = 0; + unsigned char i = 0; + unsigned int f0_cali_cnt = 50; + + + pr_info("%s enter\n", __func__); + + aw8624->f0 = aw8624->f0_pre; + /* f0 calibrate work mode */ + aw8624_haptic_stop(aw8624); + aw8624_i2c_write(aw8624, AW8624_REG_TRIM_LRA, 0x00); + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_CONT_MODE); + + + aw8624_i2c_write_bits(aw8624, + AW8624_REG_CONT_CTRL, + AW8624_BIT_CONT_CTRL_EN_CLOSE_MASK, + AW8624_BIT_CONT_CTRL_OPEN_PLAYBACK); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_CONT_CTRL, + AW8624_BIT_CONT_CTRL_F0_DETECT_MASK, + AW8624_BIT_CONT_CTRL_F0_DETECT_ENABLE); + + /* LPF */ + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DATCTRL, + AW8624_BIT_DATCTRL_FC_MASK, + AW8624_BIT_DATCTRL_FC_1000HZ); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DATCTRL, + AW8624_BIT_DATCTRL_LPF_ENABLE_MASK, + AW8624_BIT_DATCTRL_LPF_ENABLE); + + /* LRA OSC Source */ + if (aw8624->f0_cali_flag == AW8624_HAPTIC_CALI_F0) { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_ANACTRL, + AW8624_BIT_ANACTRL_LRA_SRC_MASK, + AW8624_BIT_ANACTRL_LRA_SRC_REG); + } else { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_ANACTRL, + AW8624_BIT_ANACTRL_LRA_SRC_MASK, + AW8624_BIT_ANACTRL_LRA_SRC_EFUSE); + } + + /* preset f0 */ + aw8624_haptic_set_f0_preset(aw8624); + /* f0 driver level */ + aw8624_i2c_write(aw8624, AW8624_REG_DRV_LVL, aw8624->cont_drv_lvl); + /* f0 trace parameter */ + if (!aw8624->f0_pre) { + pr_info("%s:fail to get t_f0_ms\n", __func__); + return 0; + } + + t_f0_ms = 1000*10 / aw8624->f0_pre; + f0_pre_num = aw8624_dts_data.aw8624_f0_trace_parameter[0]; + f0_wait_num = aw8624_dts_data.aw8624_f0_trace_parameter[1]; + f0_trace_num = aw8624_dts_data.aw8624_f0_trace_parameter[3]; + //Daniel 20200622 modify start + f0_repeat_num = aw8624_dts_data.aw8624_f0_trace_parameter[2]; + /*((aw8624_dts_data.aw8624_parameter1 * aw8624->f0_pre * 100) / 5000 - + 100 * (f0_pre_num + f0_wait_num + 2)) / (f0_trace_num+f0_wait_num + 2); + + if (f0_repeat_num % 100 >= 50) + f0_repeat_num = f0_repeat_num / 100 + 1; + else + f0_repeat_num = f0_repeat_num / 100;*/ + + aw8624_i2c_write(aw8624, + AW8624_REG_NUM_F0_1, + (f0_pre_num<<4)|(f0_wait_num<<0)); + aw8624_i2c_write(aw8624, + AW8624_REG_NUM_F0_2, + (char)(f0_repeat_num<<0)); + aw8624_i2c_write(aw8624, + AW8624_REG_NUM_F0_3, + (f0_trace_num<<0)); + + /* clear aw8624 interrupt */ + ret = aw8624_i2c_read(aw8624, AW8624_REG_SYSINT, ®_val); + + /* play go and start f0 calibration */ + aw8624_haptic_play_go(aw8624, true); + + /* f0 trace time */ + t_f0_trace_ms = t_f0_ms * (f0_pre_num + f0_wait_num + + (f0_trace_num + f0_wait_num ) * f0_repeat_num); + mdelay(t_f0_trace_ms + 10); + + pr_info("%s t_f0_trace_ms:%d\r\n", __func__,t_f0_trace_ms); + for (i = 0; i < f0_cali_cnt; i++) { + ret = aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, ®_val); + /* f0 calibrate done */ + if ((reg_val & 0x0f) == 0x00) { + aw8624_haptic_read_f0(aw8624); + aw8624_haptic_read_beme(aw8624); + break; + } + usleep_range(10000, 10500); + pr_info("%s f0 cali sleep 10ms,glb_state=0x%x\n", __func__, reg_val); + } + + if (i == f0_cali_cnt) + ret = -1; + else + ret = 0; + + aw8624_i2c_write_bits(aw8624, + AW8624_REG_CONT_CTRL, + AW8624_BIT_CONT_CTRL_EN_CLOSE_MASK, + AW8624_BIT_CONT_CTRL_CLOSE_PLAYBACK); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_CONT_CTRL, + AW8624_BIT_CONT_CTRL_F0_DETECT_MASK, + AW8624_BIT_CONT_CTRL_F0_DETECT_DISABLE); + //Daniel 20200622 modify end + return ret; +} + +static int aw8624_haptic_f0_calibration(struct aw8624 *aw8624) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned int f0_limit = 0; + char f0_cali_lra = 0; + int f0_cali_step = 0; + /*int f0_dft_step = 0;*/ + + aw8624->f0_cali_flag = AW8624_HAPTIC_CALI_F0; + + if (aw8624_haptic_get_f0(aw8624)) { + pr_err("%s get f0 error, user defafult f0\n", __func__); + } else { + /* max and min limit */ + f0_limit = aw8624->f0; + if (aw8624->f0*100 < aw8624_dts_data.aw8624_f0_pre * + (100-aw8624_dts_data.aw8624_f0_cali_percen)) { + f0_limit = aw8624_dts_data.aw8624_f0_pre; + } + if (aw8624->f0*100 > aw8624_dts_data.aw8624_f0_pre * + (100+aw8624_dts_data.aw8624_f0_cali_percen)) { + f0_limit = aw8624_dts_data.aw8624_f0_pre; + } + /* calculate cali step */ + f0_cali_step = + 100000*((int)f0_limit-(int)aw8624->f0_pre)/((int)f0_limit*25); + + if (f0_cali_step >= 0) { /*f0_cali_step >= 0*/ + if (f0_cali_step % 10 >= 5) { + f0_cali_step = f0_cali_step/10 + 1 + + (aw8624->chipid_flag == 1 ? 32 : 16); + } else { + f0_cali_step = f0_cali_step/10 + + (aw8624->chipid_flag == 1 ? 32 : 16); + } + } else { /*f0_cali_step < 0*/ + if (f0_cali_step % 10 <= -5) { + f0_cali_step = + (aw8624->chipid_flag == 1 ? 32 : 16) + + (f0_cali_step/10 - 1); + } else { + f0_cali_step = + (aw8624->chipid_flag == 1 ? 32 : 16) + + f0_cali_step/10; + } + } + + if (aw8624->chipid_flag == 1) { + if (f0_cali_step > 31) + f0_cali_lra = (char)f0_cali_step - 32; + else + f0_cali_lra = (char)f0_cali_step + 32; + } else { + if (f0_cali_step < 16 || + (f0_cali_step > 31 && f0_cali_step < 48)) { + f0_cali_lra = (char)f0_cali_step + 16; + } else { + f0_cali_lra = (char)f0_cali_step - 16; + } + } + /* update cali step */ + aw8624_i2c_write(aw8624, + AW8624_REG_TRIM_LRA, + (char)f0_cali_lra); + aw8624_i2c_read(aw8624, + AW8624_REG_TRIM_LRA, + ®_val); + pr_info("%s final trim_lra=0x%02x\n", __func__, reg_val); + } + + /* if (aw8624_haptic_get_f0(aw8624)) { */ + /* pr_err("%s get f0 error, user defafult f0\n", __func__); */ + /* } */ + + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_STANDBY_MODE); + aw8624_haptic_stop(aw8624); + + return ret; +} + +/***************************************************** + * + * haptic fops + * + *****************************************************/ +static int aw8624_file_open(struct inode *inode, struct file *file) +{ + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + file->private_data = (void *)g_aw8624; + + return 0; +} + +static int aw8624_file_release(struct inode *inode, struct file *file) +{ + file->private_data = (void *)NULL; + + module_put(THIS_MODULE); + + return 0; +} + +static long aw8624_file_unlocked_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct aw8624 *aw8624 = (struct aw8624 *)file->private_data; + int ret = 0; + + dev_info(aw8624->dev, "%s: cmd=0x%x, arg=0x%lx\n", + __func__, cmd, arg); + + mutex_lock(&aw8624->lock); + + if (_IOC_TYPE(cmd) != AW8624_HAPTIC_IOCTL_MAGIC) { + dev_err(aw8624->dev, "%s: cmd magic err\n", + __func__); + mutex_unlock(&aw8624->lock); + return -EINVAL; + } + + switch (cmd) { + default: + dev_err(aw8624->dev, "%s, unknown cmd\n", __func__); + break; + } + + mutex_unlock(&aw8624->lock); + + return ret; +} + +static ssize_t aw8624_file_read(struct file *filp, + char *buff, size_t len, loff_t *offset) +{ + struct aw8624 *aw8624 = (struct aw8624 *)filp->private_data; + int ret = 0; + int i = 0; + unsigned char reg_val = 0; + unsigned char *pbuff = NULL; + + mutex_lock(&aw8624->lock); + + dev_info(aw8624->dev, "%s: len=%zu\n", __func__, len); + + switch (aw8624->fileops.cmd) { + case AW8624_HAPTIC_CMD_READ_REG: + pbuff = kzalloc(len, GFP_KERNEL); + if (pbuff != NULL) { + for (i = 0; i < len; i++) { + aw8624_i2c_read(aw8624, + aw8624->fileops.reg+i, + ®_val); + pbuff[i] = reg_val; + } + ret = copy_to_user(buff, pbuff, len); + if (ret) { + dev_err + (aw8624->dev, "%s: copy to user fail\n", + __func__); + } + kfree(pbuff); + } else { + dev_err(aw8624->dev, "%s: alloc memory fail\n", + __func__); + } + break; + default: + dev_err(aw8624->dev, "%s, unknown cmd %d\n", + __func__, aw8624->fileops.cmd); + break; + } + + mutex_unlock(&aw8624->lock); + + for (i = 0; i < len; i++) { + dev_info(aw8624->dev, "%s: buff[%d]=0x%02x\n", + __func__, i, buff[i]); + } + + return len; +} + +static ssize_t aw8624_file_write(struct file *filp, + const char *buff, size_t len, loff_t *off) +{ + struct aw8624 *aw8624 = (struct aw8624 *)filp->private_data; + int i = 0; + int ret = 0; + unsigned char *pbuff = NULL; + + + mutex_lock(&aw8624->lock); + + aw8624->fileops.cmd = buff[0]; + + switch (aw8624->fileops.cmd) { + case AW8624_HAPTIC_CMD_READ_REG: + if (len == 2) { + aw8624->fileops.reg = buff[1]; + } else { + dev_err(aw8624->dev, + "%s: read cmd len %zu err\n", + __func__, len); + } + break; + case AW8624_HAPTIC_CMD_WRITE_REG: + if (len > 2) { + pbuff = kzalloc(len, GFP_KERNEL); + if (pbuff != NULL) { + ret = copy_from_user(pbuff, buff, len); + if (ret) { + dev_err(aw8624->dev, + "%s: copy from user fail\n", + __func__); + } else { + for (i = 0; i < len - 2; i++) { + dev_info + (aw8624->dev, + "%s: write reg0x%x = 0x%x\n", + __func__, + pbuff[1]+i, pbuff[i+2]); + aw8624_i2c_write(aw8624, + pbuff[1]+i, + pbuff[2+i]); + } + } + kfree(pbuff); + } else { + dev_err(aw8624->dev, + "%s: alloc memory fail\n", + __func__); + } + } else { + dev_err(aw8624->dev, + "%s: write cmd len %zu err\n", + __func__, len); + } + break; + default: + dev_err(aw8624->dev, + "%s, unknown cmd %d\n", + __func__, aw8624->fileops.cmd); + break; + } + + mutex_unlock(&aw8624->lock); + + return len; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .read = aw8624_file_read, + .write = aw8624_file_write, + .unlocked_ioctl = aw8624_file_unlocked_ioctl, + .open = aw8624_file_open, + .release = aw8624_file_release, +}; + +static struct miscdevice aw8624_haptic_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = AW8624_HAPTIC_NAME, + .fops = &fops, +}; + +static int aw8624_haptic_init(struct aw8624 *aw8624) +{ + int ret = 0; + unsigned char i = 0; + unsigned char reg_val = 0, reg_flag = 0; + unsigned char bemf_config = 0; + + ret = misc_register(&aw8624_haptic_misc); + if (ret) { + dev_err(aw8624->dev, "%s: misc fail: %d\n", __func__, ret); + return ret; + } + + /* haptic audio */ + aw8624->haptic_audio.delay_val = 23; + aw8624->haptic_audio.timer_val = 23; + + hrtimer_init(&aw8624->haptic_audio.timer, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + aw8624->haptic_audio.timer.function = aw8624_haptic_audio_timer_func; + INIT_WORK(&aw8624->haptic_audio.work, aw8624_haptic_audio_work_routine); + + mutex_init(&aw8624->haptic_audio.lock); + + /* haptic init */ + mutex_lock(&aw8624->lock); + ret = aw8624_i2c_read(aw8624, AW8624_REG_EF_RDATAH, ®_flag); + if ((ret >= 0) && ((reg_flag & 0x1) == 1)) { + aw8624->chipid_flag = 1; + } else { + dev_err(aw8624->dev, + "%s: to read register AW8624_REG_EF_RDATAH: %d\n", + __func__, ret); + } + + aw8624->activate_mode = aw8624_dts_data.aw8624_mode; + ret = aw8624_i2c_read(aw8624, AW8624_REG_WAVSEQ1, ®_val); + aw8624->index = reg_val & 0x7F; + ret = aw8624_i2c_read(aw8624, AW8624_REG_DATDBG, ®_val); + aw8624->gain = reg_val & 0xFF; + for (i = 0; i < AW8624_SEQUENCER_SIZE; i++) { + ret = aw8624_i2c_read(aw8624, AW8624_REG_WAVSEQ1+i, ®_val); + aw8624->seq[i] = reg_val; + } + + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_STANDBY_MODE); + aw8624_haptic_set_pwm(aw8624, AW8624_PWM_24K); + + aw8624_haptic_swicth_motorprotect_config(aw8624, 0x0, 0x0); + aw8624_haptic_vbat_mode(aw8624, AW8624_HAPTIC_VBAT_HW_COMP_MODE); + mutex_unlock(&aw8624->lock); + + /* f0 calibration */ + aw8624->f0_pre = aw8624_dts_data.aw8624_f0_pre; + aw8624->cont_drv_lvl = aw8624_dts_data.aw8624_cont_drv_lvl; + aw8624->cont_drv_lvl_ov = aw8624_dts_data.aw8624_cont_drv_lvl_ov; + aw8624->cont_td = aw8624_dts_data.aw8624_cont_td; + aw8624->cont_zc_thr = aw8624_dts_data.aw8624_cont_zc_thr; + aw8624->cont_num_brk = aw8624_dts_data.aw8624_cont_num_brk; + aw8624->ram_vbat_comp = AW8624_HAPTIC_RAM_VBAT_COMP_ENABLE; + mutex_lock(&aw8624->lock); + aw8624_i2c_write_bits(aw8624, AW8624_REG_R_SPARE, + AW8624_BIT_R_SPARE_MASK, AW8624_BIT_R_SPARE_ENABLE); + aw8624_haptic_f0_calibration(aw8624); + mutex_unlock(&aw8624->lock); + + /*brake*/ + mutex_lock(&aw8624->lock); + aw8624_i2c_write(aw8624, + AW8624_REG_SW_BRAKE, + (unsigned char)(aw8624_dts_data.aw8624_sw_brake[0])); + aw8624_i2c_write(aw8624, AW8624_REG_THRS_BRA_END, 0x00); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_WAVECTRL, + AW8624_BIT_WAVECTRL_NUM_OV_DRIVER_MASK, + AW8624_BIT_WAVECTRL_NUM_OV_DRIVER); + aw8624->f0_value = 20000 / aw8624_dts_data.aw8624_f0_pre + 1; + /* zero cross */ + aw8624_i2c_write(aw8624, + AW8624_REG_ZC_THRSH_H, + (unsigned char)(aw8624->cont_zc_thr>>8)); + aw8624_i2c_write(aw8624, + AW8624_REG_ZC_THRSH_L, + (unsigned char)(aw8624->cont_zc_thr>>0)); + aw8624_i2c_write(aw8624, + AW8624_REG_TSET, + aw8624_dts_data.aw8624_tset); + + /* bemf */ + bemf_config = aw8624_dts_data.aw8624_bemf_config[0]; + aw8624_i2c_write(aw8624, AW8624_REG_BEMF_VTHH_H, bemf_config); + bemf_config = aw8624_dts_data.aw8624_bemf_config[1]; + aw8624_i2c_write(aw8624, AW8624_REG_BEMF_VTHH_L, bemf_config); + bemf_config = aw8624_dts_data.aw8624_bemf_config[2]; + aw8624_i2c_write(aw8624, AW8624_REG_BEMF_VTHL_H, bemf_config); + bemf_config = aw8624_dts_data.aw8624_bemf_config[3]; + aw8624_i2c_write(aw8624, AW8624_REG_BEMF_VTHL_L, bemf_config); + mutex_unlock(&aw8624->lock); + + return ret; +} + +/***************************************************** + * + * vibrator + * + *****************************************************/ +#ifdef TIMED_OUTPUT +static int aw8624_vibrator_get_time(struct timed_output_dev *to_dev) +{ + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); + + if (hrtimer_active(&aw8624->timer)) { + ktime_t r = hrtimer_get_remaining(&aw8624->timer); + + return ktime_to_ms(r); + } + + return 0; +} + +static void aw8624_vibrator_enable(struct timed_output_dev *to_dev, int value) +{ + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); + + pr_info("%s: time value = %d\n", __func__, value); + + mutex_lock(&aw8624->lock); + hrtimer_cancel(&aw8624->timer); + + aw8624->state = value; + aw8624->duration = value; + + /* aw8624_haptic_stop(aw8624); */ + + mutex_unlock(&aw8624->lock); + + schedule_work(&aw8624->vibrator_work); +} + +#else +static void +aw8624_vibrator_enable(struct led_classdev *dev, enum led_brightness value) +{ + struct aw8624 *aw8624 = container_of(dev, struct aw8624, cdev); + + pr_info("%s: time value = %d\n", __func__, value); + + mutex_lock(&aw8624->lock); + hrtimer_cancel(&aw8624->timer); + + aw8624->state = value; + aw8624->duration = value; + + /* aw8624_haptic_stop(aw8624); */ + + mutex_unlock(&aw8624->lock); + + schedule_work(&aw8624->vibrator_work); +} + +#endif + +static ssize_t aw8624_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + + return snprintf(buf, PAGE_SIZE, "%d\n", aw8624->state); +} + +static ssize_t aw8624_state_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw8624_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + ktime_t time_rem; + s64 time_ms = 0; + + if (hrtimer_active(&aw8624->timer)) { + time_rem = hrtimer_get_remaining(&aw8624->timer); + time_ms = ktime_to_ms(time_rem); + } + + return snprintf(buf, PAGE_SIZE, "%lld\n", time_ms); +} + +static ssize_t aw8624_duration_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + pr_debug("%s: value=%d\n", __func__, val); + /* setting 0 on duration is NOP for now */ + if (val <= 0) + return count; + + if (val <= aw8624->f0_value) + val = aw8624->f0_value; + + aw8624->duration = val; + return count; +} + +static ssize_t aw8624_activate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + /* For now nothing to show */ + return snprintf(buf, PAGE_SIZE, "%d\n", aw8624->state); +} + +static ssize_t aw8624_activate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + if (val != 0 && val != 1) + return count; + + pr_info("%s: value=%d\n", __func__, val); + + if (val == 0) + msleep(5); + mutex_lock(&aw8624->lock); + hrtimer_cancel(&aw8624->timer); + + aw8624->state = val; + + /*aw8624_haptic_stop(aw8624);*/ + + mutex_unlock(&aw8624->lock); + schedule_work(&aw8624->vibrator_work); + + return count; +} + +static ssize_t aw8624_activate_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + return snprintf(buf, PAGE_SIZE, "activate_mode=%d\n", + aw8624->activate_mode); +} + +static ssize_t aw8624_activate_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw8624->lock); + aw8624->activate_mode = val; + mutex_unlock(&aw8624->lock); + return count; +} + + +static ssize_t aw8624_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + unsigned char reg_val = 0; + + aw8624_i2c_read(aw8624, AW8624_REG_WAVSEQ1, ®_val); + aw8624->index = reg_val; + + return snprintf(buf, PAGE_SIZE, "%d\n", aw8624->index); +} + +static ssize_t aw8624_index_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + pr_info("%s: value=%d\n", __func__, val); + + mutex_lock(&aw8624->lock); + aw8624->index = val; + aw8624_haptic_set_repeat_wav_seq(aw8624, aw8624->index); + mutex_unlock(&aw8624->lock); + return count; +} + +static ssize_t aw8624_gain_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + #ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + return snprintf(buf, PAGE_SIZE, "0x%02x\n", aw8624->gain); +} + +static ssize_t aw8624_gain_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + pr_info("%s: value=%d\n", __func__, val); + + mutex_lock(&aw8624->lock); + aw8624->gain = val; + aw8624_haptic_set_gain(aw8624, aw8624->gain); + mutex_unlock(&aw8624->lock); + return count; +} + +static ssize_t aw8624_seq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + size_t count = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + for (i = 0; i < AW8624_SEQUENCER_SIZE; i++) { + aw8624_i2c_read(aw8624, AW8624_REG_WAVSEQ1+i, ®_val); + count += snprintf(buf+count, PAGE_SIZE-count, + "seq%d: 0x%02x\n", i+1, reg_val); + aw8624->seq[i] |= reg_val; + } + return count; +} + +static ssize_t aw8624_seq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + unsigned int databuf[2] = {0, 0}; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + pr_info("%s: seq%d=0x%x\n", + __func__, databuf[0], databuf[1]); + mutex_lock(&aw8624->lock); + aw8624->seq[databuf[0]] = (unsigned char)databuf[1]; + aw8624_haptic_set_wav_seq(aw8624, (unsigned char)databuf[0], + aw8624->seq[databuf[0]]); + mutex_unlock(&aw8624->lock); + } + return count; +} + +static ssize_t aw8624_loop_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + size_t count = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + for (i = 0; i < AW8624_SEQUENCER_LOOP_SIZE; i++) { + aw8624_i2c_read(aw8624, AW8624_REG_WAVLOOP1+i, ®_val); + aw8624->loop[i*2+0] = (reg_val>>4)&0x0F; + aw8624->loop[i*2+1] = (reg_val>>0)&0x0F; + + count += snprintf(buf+count, PAGE_SIZE-count, + "seq%d loop: 0x%02x\n", i*2+1, aw8624->loop[i*2+0]); + count += snprintf(buf+count, PAGE_SIZE-count, + "seq%d loop: 0x%02x\n", i*2+2, aw8624->loop[i*2+1]); + } + + aw8624->singlecycle = 1; + + return count; +} + +static ssize_t aw8624_loop_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + unsigned int databuf[2] = {0, 0}; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + pr_info("%s: seq%d loop=0x%x\n", + __func__, databuf[0], databuf[1]); + mutex_lock(&aw8624->lock); + aw8624->loop[databuf[0]] = (unsigned char)databuf[1]; + aw8624_haptic_set_wav_loop(aw8624, (unsigned char)databuf[0], + aw8624->loop[databuf[0]]); + mutex_unlock(&aw8624->lock); + } + + return count; +} + +static ssize_t aw8624_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + ssize_t len = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + for (i = 0; i < AW8624_REG_MAX; i++) { + if (!(aw8624_reg_access[i]®_RD_ACCESS)) + continue; + aw8624_i2c_read(aw8624, i, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, "reg:0x%02x=0x%02x\n", + i, reg_val); + } + return len; +} + +static ssize_t aw8624_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + unsigned int databuf[2] = {0, 0}; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw8624_i2c_write(aw8624, + (unsigned char)databuf[0], + (unsigned char)databuf[1]); + } + + return count; +} + +static ssize_t aw8624_rtp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, "rtp play: %d\n", + aw8624->rtp_cnt); + + return len; +} + +static ssize_t aw8624_rtp_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + + unsigned int val = 0; + int rc = 0; + + if (!(aw8624->IsUsedIRQ)) + return rc; + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + aw8624_haptic_stop(aw8624); + aw8624_haptic_set_rtp_aei(aw8624, false); + aw8624_interrupt_clear(aw8624); + if (val < (sizeof(aw8624_rtp_name)/AW8624_RTP_NAME_MAX)) { + aw8624->rtp_file_num = val; + if (val) + schedule_work(&aw8624->rtp_work); + } else { + pr_err("%s: rtp_file_num 0x%02x over max value\n", + __func__, aw8624->rtp_file_num); + } + + return count; +} + + +static ssize_t aw8624_ram_update_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, "sram update mode\n"); + return len; +} + +static ssize_t aw8624_ram_update_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + if (val) + aw8624_ram_update(aw8624); + + return count; +} + +static ssize_t aw8624_f0_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + + ssize_t len = 0; + + mutex_lock(&aw8624->lock); + aw8624->f0_cali_flag = AW8624_HAPTIC_LRA_F0; + aw8624_haptic_get_f0(aw8624); + mutex_unlock(&aw8624->lock); + len += snprintf(buf+len, PAGE_SIZE-len, "aw8624 lra f0 = %d\n", + aw8624->f0); + return len; +} + +static ssize_t aw8624_f0_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + return count; +} + + +static ssize_t aw8624_cali_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + ssize_t len = 0; + + mutex_lock(&aw8624->lock); + aw8624->f0_cali_flag = AW8624_HAPTIC_CALI_F0; + aw8624_haptic_get_f0(aw8624); + mutex_unlock(&aw8624->lock); + + len += snprintf(buf+len, PAGE_SIZE-len, "aw8624 f0 = %d\n", aw8624->f0); + return len; +} + +static ssize_t +aw8624_cali_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + if (val) { + mutex_lock(&aw8624->lock); + aw8624_haptic_f0_calibration(aw8624); + mutex_unlock(&aw8624->lock); + } + return count; +} + +static ssize_t +aw8624_cont_show(struct device *dev, struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + ssize_t len = 0; + + aw8624_haptic_read_cont_f0(aw8624); + len += snprintf(buf+len, PAGE_SIZE-len, "aw8624 f0 = %d\n", aw8624->f0); + return len; +} + +static ssize_t +aw8624_cont_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + aw8624_haptic_stop(aw8624); + + if (val) + aw8624_haptic_cont(aw8624); + return count; +} + + +static ssize_t +aw8624_cont_td_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw8624 cont delay time = 0x%04x\n", + aw8624->cont_td); + return len; +} + +static ssize_t +aw8624_cont_td_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + int err, val; + + err = kstrtoint(buf, 16, &val); + if (err != 0) { + pr_err("%s format not match!", __func__); + return count; + } + + aw8624->cont_td = val; + aw8624_i2c_write(aw8624, + AW8624_REG_TD_H, + (unsigned char)(val >> 8)); + aw8624_i2c_write(aw8624, + AW8624_REG_TD_L, + (unsigned char)(val >> 0)); + + return count; +} + +static ssize_t +aw8624_cont_drv_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw8624 cont drv level = 0x%02x\n", + aw8624->cont_drv_lvl); + len += snprintf(buf+len, PAGE_SIZE-len, + "aw8624 cont drv level overdrive= 0x%02x\n", + aw8624->cont_drv_lvl_ov); + return len; +} + +static ssize_t +aw8624_cont_drv_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + unsigned int databuf[2] = {0, 0}; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw8624->cont_drv_lvl = databuf[0]; + aw8624_i2c_write(aw8624, + AW8624_REG_DRV_LVL, + aw8624->cont_drv_lvl); + aw8624->cont_drv_lvl_ov = databuf[1]; + aw8624_i2c_write(aw8624, + AW8624_REG_DRV_LVL_OV, + aw8624->cont_drv_lvl_ov); + } + return count; +} + +static ssize_t +aw8624_cont_num_brk_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw8624 cont break num = 0x%02x\n", + aw8624->cont_num_brk); + return len; +} + +static ssize_t +aw8624_cont_num_brk_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + int err, val; + + err = kstrtoint(buf, 16, &val); + if (err != 0) { + pr_err("%s format not match!", __func__); + return count; + } + + aw8624->cont_num_brk = val; + if (aw8624->cont_num_brk > 7) + aw8624->cont_num_brk = 7; + + aw8624_i2c_write_bits(aw8624, AW8624_REG_BEMF_NUM, + AW8624_BIT_BEMF_NUM_BRK_MASK, aw8624->cont_num_brk); + + return count; +} + +static ssize_t +aw8624_cont_zc_thr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw8624 cont zero cross thr = 0x%04x\n", + aw8624->cont_zc_thr); + return len; +} + +static ssize_t +aw8624_cont_zc_thr_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + int err, val; + + err = kstrtoint(buf, 0, &val); + if (err != 0) { + pr_err("%s format not match!", __func__); + return count; + } + pr_info("%s: val=%d\n", __func__, val); + if (val == 1) { + aw8624_i2c_write(aw8624, + AW8624_REG_ZC_THRSH_H, + (unsigned char)(val >> 8)); + aw8624_i2c_write(aw8624, + AW8624_REG_ZC_THRSH_L, + (unsigned char)(val >> 0)); + } + return count; +} + +static ssize_t +aw8624_vbat_monitor_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + ssize_t len = 0; + unsigned int vbat = 0; + + mutex_lock(&aw8624->lock); + vbat = aw8624_vbat_monitor_detector(aw8624); + mutex_unlock(&aw8624->lock); + len += snprintf(buf+len, PAGE_SIZE-len, "vbat=%dmV\n", vbat); + + return len; +} + +static ssize_t +aw8624_vbat_monitor_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t +aw8624_lra_resistance_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + ssize_t len = 0; + unsigned int r_lra = 0; + + r_lra = aw8624_lra_resistance_detector(aw8624); + + len += snprintf(buf+len, PAGE_SIZE-len, "r_lra=%dmohm\n", r_lra); + return len; +} + + +static ssize_t +aw8624_lra_resistance_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t +aw8624_trig_boost_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + return len; +} +static ssize_t +aw8624_trig_boost_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + if (!(aw8624->IsUsedIRQ)) + return rc; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw8624->lock); + aw8624_haptic_stop(aw8624); + aw8624_haptic_trig1_config(aw8624, val); + mutex_unlock(&aw8624->lock); + + return count; +} + +static ssize_t +aw8624_prctmode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + ssize_t len = 0; + unsigned char reg_val = 0; + + aw8624_i2c_read(aw8624, AW8624_REG_RLDET, ®_val); + + len += snprintf(buf+len, PAGE_SIZE-len, "prctmode=%d\n", reg_val&0x20); + return len; +} + + +static ssize_t +aw8624_prctmode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + unsigned int databuf[2] = {0, 0}; + unsigned int addr = 0; + unsigned int val = 0; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + addr = databuf[0]; + val = databuf[1]; + mutex_lock(&aw8624->lock); + aw8624_haptic_swicth_motorprotect_config(aw8624, addr, val); + mutex_unlock(&aw8624->lock); + } + return count; +} + +static ssize_t +aw8624_ram_vbat_comp_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "ram_vbat_comp=%d\n", + aw8624->ram_vbat_comp); + + return len; +} + + +static ssize_t +aw8624_ram_vbat_comp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw8624->lock); + if (val) + aw8624->ram_vbat_comp = AW8624_HAPTIC_RAM_VBAT_COMP_ENABLE; + else + aw8624->ram_vbat_comp = AW8624_HAPTIC_RAM_VBAT_COMP_DISABLE; + + mutex_unlock(&aw8624->lock); + + return count; +} + +static ssize_t +aw8624_haptic_audio_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", + aw8624->haptic_audio.cnt); + return len; +} + +static ssize_t +aw8624_haptic_audio_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + unsigned int databuf[6] = {0}; + + if (sscanf(buf, "%d %d %d %d %d %d", &databuf[0], &databuf[1], + &databuf[2], &databuf[3], &databuf[4], &databuf[5]) == 6) { + pr_debug + ("%s: cnt=%d, cmd=%d, play=%d, wavseq=%d, loop=%d, gain=%d\n", + __func__, + databuf[0], databuf[1], databuf[2], + databuf[3], databuf[4], databuf[5]); + mutex_lock(&aw8624->haptic_audio.lock); + aw8624->haptic_audio.cnt = (unsigned char)databuf[0]; + aw8624->haptic_audio.ctr[aw8624->haptic_audio.cnt].cmd = + (unsigned char)databuf[1]; + aw8624->haptic_audio.ctr[aw8624->haptic_audio.cnt].play = + (unsigned char)databuf[2]; + aw8624->haptic_audio.ctr[aw8624->haptic_audio.cnt].wavseq = + (unsigned char)databuf[3]; + aw8624->haptic_audio.ctr[aw8624->haptic_audio.cnt].loop = + (unsigned char)databuf[4]; + aw8624->haptic_audio.ctr[aw8624->haptic_audio.cnt].gain = + (unsigned char)databuf[5]; + mutex_unlock(&aw8624->haptic_audio.lock); + + if (aw8624->haptic_audio.ctr[aw8624->haptic_audio.cnt].cmd == + 0xff) { + pr_info("%s: haptic_audio stop\n", __func__); + if (hrtimer_active(&aw8624->haptic_audio.timer)) { + pr_info + ("%s: cancel haptic_audio_timer\n", __func__); + hrtimer_cancel(&aw8624->haptic_audio.timer); + aw8624->haptic_audio.cnt = 0; + } + } else { + if (hrtimer_active(&aw8624->haptic_audio.timer)) { + } else { + pr_info + ("%s: start haptic_audio_timer\n", __func__); + hrtimer_start(&aw8624->haptic_audio.timer, + ktime_set + (aw8624->haptic_audio.delay_val/1000, + (aw8624->haptic_audio.delay_val%1000)*1000000), + HRTIMER_MODE_REL); + } + } + } + return count; +} + +static ssize_t +aw8624_haptic_audio_time_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "haptic_audio.delay_val=%dus\n", + aw8624->haptic_audio.delay_val); + len += snprintf(buf+len, PAGE_SIZE-len, + "haptic_audio.timer_val=%dus\n", + aw8624->haptic_audio.timer_val); + return len; +} + +static ssize_t +aw8624_haptic_audio_time_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + + unsigned int databuf[2] = {0}; + + if (sscanf(buf, "%d %d", &databuf[0], &databuf[1]) == 2) { + aw8624->haptic_audio.delay_val = databuf[0]; + aw8624->haptic_audio.timer_val = databuf[1]; + } + return count; +} + +static int aw8624_clock_OSC_trim_calibration + (unsigned long int theory_time, unsigned long int real_time) +{ + unsigned int real_code = 0; + unsigned int LRA_TRIM_CODE = 0; + unsigned int DFT_LRA_TRIM_CODE = 0; + unsigned int Not_need_cali_threshold = 10;/*0.1 percent not need cali*/ + + pr_info("%s enter\n", __func__); + if (theory_time == real_time) { + pr_info("aw_osctheory_time == real_time:%ld,", real_time); + pr_info("theory_time = %ld not need to cali\n", theory_time); + return 0; + } else if (theory_time < real_time) { + if ((real_time - theory_time) > (theory_time / 25)) { + pr_info("%s: failed not to cali\n", __func__); + return DFT_LRA_TRIM_CODE; + } + + if ((real_time - theory_time) < + (Not_need_cali_threshold*theory_time/10000)) { + pr_info("aw_oscmicrosecond:%ld,theory_time = %ld\n", + real_time, theory_time); + return DFT_LRA_TRIM_CODE; + } + + real_code = ((real_time - theory_time) * 4000) / theory_time; + real_code = ((real_code%10 < 5) ? 0 : 1) + real_code/10; + real_code = 32 + real_code; + } else if (theory_time > real_time) { + if ((theory_time - real_time) > (theory_time / 25)) { + pr_info("failed not to cali\n"); + return DFT_LRA_TRIM_CODE; + } + if ((theory_time - real_time) < + (Not_need_cali_threshold * theory_time/10000)) { + pr_info("aw_oscmicrosecond:%ld,theory_time = %ld\n", + real_time, theory_time); + return DFT_LRA_TRIM_CODE; + } + real_code = ((theory_time - real_time) * 4000) / theory_time; + real_code = ((real_code%10 < 5) ? 0 : 1) + real_code/10; + real_code = 32 - real_code; + } + if (real_code > 31) + LRA_TRIM_CODE = real_code - 32; + else + LRA_TRIM_CODE = real_code + 32; + + pr_info("aw_oscmicrosecond:%ld,theory_time = %ld,real_code =0X%02X,", + real_time, theory_time, real_code); + pr_info("LRA_TRIM_CODE 0X%02X\n", LRA_TRIM_CODE); + + return LRA_TRIM_CODE; +} + +static int aw8624_rtp_trim_lra_calibration(struct aw8624 *aw8624) +{ + unsigned char reg_val = 0; + unsigned int fre_val = 0; + unsigned int theory_time = 0; + unsigned int lra_rtim_code = 0; + + pr_info("%s enter\n", __func__); + aw8624_i2c_read(aw8624, AW8624_REG_PWMDBG, ®_val); + fre_val = (reg_val & 0x006f) >> 5; + + if (fre_val == 3) + theory_time = (aw8624->rtp_len / 12000) * 1000000; /*12K */ + if (fre_val == 2) + theory_time = (aw8624->rtp_len / 24000) * 1000000; /*24K */ + if (fre_val == 1 || fre_val == 0) + theory_time = (aw8624->rtp_len / 48000) * 1000000; /*48K */ + + pr_info("microsecond:%ld theory_time = %d\n", + aw8624->microsecond, theory_time); + + lra_rtim_code = aw8624_clock_OSC_trim_calibration(theory_time, + aw8624->microsecond); + if (lra_rtim_code >= 0) { + aw8624->lra_calib_data = lra_rtim_code; + aw8624_i2c_write(aw8624, AW8624_REG_TRIM_LRA, + (char)lra_rtim_code); + } + return 0; +} + +static unsigned char aw8624_haptic_osc_read_int(struct aw8624 *aw8624) +{ + unsigned char reg_val = 0; + + aw8624_i2c_read(aw8624, AW8624_REG_DBGSTAT, ®_val); + return reg_val; +} + +static int aw8624_rtp_osc_calibration(struct aw8624 *aw8624) +{ + const struct firmware *rtp_file; + int ret = -1; + unsigned int buf_len = 0; + unsigned char osc_int_state = 0; + aw8624->rtp_cnt = 0; + aw8624->timeval_flags = 1; + aw8624->osc_cali_flag = 1; + + pr_info("%s enter\n", __func__); + /* fw loaded */ + ret = request_firmware(&rtp_file, + aw8624_rtp_name[0],/*aw8624->rtp_file_num */ + aw8624->dev); + if (ret < 0) { + /*aw8624->rtp_file_num */ + pr_err("%s: failed to read %s\n", __func__, aw8624_rtp_name[0]); + return ret; + } + + /*awinic add stop,for irq interrupt during calibrate*/ + aw8624_haptic_stop(aw8624); + aw8624_i2c_write(aw8624, AW8624_REG_TRIM_LRA, 0x00); + aw8624->rtp_init = 0; + mutex_lock(&aw8624->rtp_lock); + vfree(aw8624_rtp); + aw8624_rtp = vmalloc(rtp_file->size + sizeof(int)); + if (!aw8624_rtp) { + release_firmware(rtp_file); + mutex_unlock(&aw8624->rtp_lock); + pr_err("%s: error allocating memory\n", __func__); + return -ENOMEM; + } + aw8624_rtp->len = rtp_file->size; + aw8624->rtp_len = rtp_file->size; + pr_info("%s: rtp file [%s] size = %d\n", __func__, + aw8624_rtp_name[0], aw8624_rtp->len);/*aw8624->rtp_file_num */ + memcpy(aw8624_rtp->data, rtp_file->data, rtp_file->size); + release_firmware(rtp_file); + mutex_unlock(&aw8624->rtp_lock); + + /* gain */ + aw8624_haptic_ram_vbat_comp(aw8624, false); + + /* rtp mode config */ + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_RTP_MODE); + + aw8624_i2c_write_bits(aw8624, AW8624_REG_DBGCTRL, + AW8624_BIT_DBGCTRL_INT_MODE_MASK, + AW8624_BIT_DBGCTRL_INT_MODE_EDGE); + disable_irq(gpio_to_irq(aw8624->irq_gpio)); + /* haptic start */ + aw8624_haptic_start(aw8624); + pm_qos_add_request(&pm_qos_req_vb, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_VALUE_VB); + while (1) { + if (!aw8624_haptic_rtp_get_fifo_afi(aw8624)) { + pr_info("%s: haptic_rtp_get_fifo_afi, rtp_cnt= %d \n", + __func__, aw8624->rtp_cnt); + mutex_lock(&aw8624->rtp_lock); + if ((aw8624_rtp->len - aw8624->rtp_cnt) < + (aw8624->ram.base_addr >> 2)) + buf_len = aw8624_rtp->len - aw8624->rtp_cnt; + else + buf_len = (aw8624->ram.base_addr >> 2); + + if (aw8624->rtp_cnt != aw8624_rtp->len) { + if (aw8624->timeval_flags == 1) { + do_gettimeofday(&aw8624->start); + aw8624->timeval_flags = 0; + } + aw8624->rtpupdate_flag = + aw8624_i2c_writes(aw8624, + AW8624_REG_RTP_DATA, + &aw8624_rtp->data[aw8624-> + rtp_cnt], buf_len); + aw8624->rtp_cnt += buf_len; + } + mutex_unlock(&aw8624->rtp_lock); + } + + osc_int_state = aw8624_haptic_osc_read_int(aw8624); + if (osc_int_state&AW8624_BIT_SYSINT_DONEI) { + do_gettimeofday(&aw8624->end); + pr_info("%s vincent playback aw8624->rtp_cnt= %d \n", + __func__, aw8624->rtp_cnt); + break; + } + + do_gettimeofday(&aw8624->end); + aw8624->microsecond = + (aw8624->end.tv_sec - aw8624->start.tv_sec)*1000000 + + (aw8624->end.tv_usec - aw8624->start.tv_usec); + if (aw8624->microsecond > OSC_CALIBRATION_T_LENGTH) { + pr_info("%s:vincent time out aw8624->rtp_cnt %d,", + __func__, aw8624->rtp_cnt); + pr_info("osc_int_state %02x\n", osc_int_state); + break; + } + } + pm_qos_remove_request(&pm_qos_req_vb); + enable_irq(gpio_to_irq(aw8624->irq_gpio)); + + aw8624->osc_cali_flag = 0; + aw8624->microsecond = + (aw8624->end.tv_sec - aw8624->start.tv_sec)*1000000 + + (aw8624->end.tv_usec - aw8624->start.tv_usec); + /*calibration osc*/ + pr_info("%s awinic_microsecond:%ld \n", __func__, aw8624->microsecond); + pr_info("%s exit\n", __func__); + return 0; +} + +static ssize_t aw8624_osc_cali_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + ssize_t len = 0; + + len += + snprintf(buf + len, PAGE_SIZE - len, "lra_calib_data=%d\n", + aw8624->lra_calib_data); + + return len; +} + +static ssize_t aw8624_osc_cali_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(to_dev, struct aw8624, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, cdev); +#endif + unsigned int val = 0; + + int rc = 0; + rc = kstrtouint(buf, 0, &val); + if (rc < 0) { + return rc; + } + mutex_lock(&aw8624->lock); + /* osc calibration flag start,Other behaviors are forbidden */ + aw8624->osc_cali_run = 1; + if (val == 1) { + aw8624_rtp_osc_calibration(aw8624); + aw8624_rtp_trim_lra_calibration(aw8624); + } + aw8624->osc_cali_run = 0; + /* osc calibration flag end,Other behaviors are permitted */ + mutex_unlock(&aw8624->lock); + + return count; +} + +static DEVICE_ATTR(state, S_IWUSR | S_IRUGO, aw8624_state_show, aw8624_state_store); +static DEVICE_ATTR(duration, S_IWUSR | S_IRUGO, aw8624_duration_show, aw8624_duration_store); +static DEVICE_ATTR(activate, S_IWUSR | S_IRUGO, aw8624_activate_show, aw8624_activate_store); +static DEVICE_ATTR(activate_mode, S_IWUSR | S_IRUGO, + aw8624_activate_mode_show, aw8624_activate_mode_store); +static DEVICE_ATTR(index, S_IWUSR | S_IRUGO, aw8624_index_show, aw8624_index_store); +static DEVICE_ATTR(gain, S_IWUSR | S_IRUGO, aw8624_gain_show, aw8624_gain_store); +static DEVICE_ATTR(seq, S_IWUSR | S_IRUGO, aw8624_seq_show, aw8624_seq_store); +static DEVICE_ATTR(loop, S_IWUSR | S_IRUGO, aw8624_loop_show, aw8624_loop_store); +static DEVICE_ATTR(register, S_IWUSR | S_IRUGO, aw8624_reg_show, aw8624_reg_store); +static DEVICE_ATTR(ram_update, S_IWUSR | S_IRUGO, + aw8624_ram_update_show, aw8624_ram_update_store); +static DEVICE_ATTR(f0, S_IWUSR | S_IRUGO, aw8624_f0_show, aw8624_f0_store); +static DEVICE_ATTR(cali, S_IWUSR | S_IRUGO, aw8624_cali_show, aw8624_cali_store); +static DEVICE_ATTR(cont, S_IWUSR | S_IRUGO, aw8624_cont_show, aw8624_cont_store); +static DEVICE_ATTR(cont_td, S_IWUSR | S_IRUGO, aw8624_cont_td_show, aw8624_cont_td_store); +static DEVICE_ATTR(cont_drv, S_IWUSR | S_IRUGO, aw8624_cont_drv_show, aw8624_cont_drv_store); +static DEVICE_ATTR(cont_num_brk, S_IWUSR | S_IRUGO, + aw8624_cont_num_brk_show, aw8624_cont_num_brk_store); +static DEVICE_ATTR(cont_zc_thr, S_IWUSR | S_IRUGO, + aw8624_cont_zc_thr_show, aw8624_cont_zc_thr_store); +static DEVICE_ATTR(vbat_monitor, S_IWUSR | S_IRUGO, + aw8624_vbat_monitor_show, aw8624_vbat_monitor_store); +static DEVICE_ATTR(lra_resistance, S_IWUSR | S_IRUGO, + aw8624_lra_resistance_show, aw8624_lra_resistance_store); +static DEVICE_ATTR(prctmode, S_IWUSR | S_IRUGO, aw8624_prctmode_show, aw8624_prctmode_store); +static DEVICE_ATTR(haptic_audio, S_IWUSR | S_IRUGO, + aw8624_haptic_audio_show, aw8624_haptic_audio_store); +static DEVICE_ATTR(haptic_audio_time, S_IWUSR | S_IRUGO, + aw8624_haptic_audio_time_show, aw8624_haptic_audio_time_store); +static DEVICE_ATTR(trig_boost, S_IWUSR | S_IRUGO, + aw8624_trig_boost_show, aw8624_trig_boost_store); +static DEVICE_ATTR(ram_vbat_comp, S_IWUSR | S_IRUGO, + aw8624_ram_vbat_comp_show, aw8624_ram_vbat_comp_store); +static DEVICE_ATTR(rtp, S_IWUSR | S_IRUGO, aw8624_rtp_show, aw8624_rtp_store); +static DEVICE_ATTR(osc_cali, S_IWUSR | S_IRUGO, aw8624_osc_cali_show, aw8624_osc_cali_store); +static struct attribute *aw8624_vibrator_attributes[] = { + &dev_attr_state.attr, + &dev_attr_duration.attr, + &dev_attr_activate.attr, + &dev_attr_activate_mode.attr, + &dev_attr_index.attr, + &dev_attr_gain.attr, + &dev_attr_seq.attr, + &dev_attr_loop.attr, + &dev_attr_register.attr, + &dev_attr_ram_update.attr, + &dev_attr_f0.attr, + &dev_attr_cali.attr, + &dev_attr_cont.attr, + &dev_attr_cont_td.attr, + &dev_attr_cont_drv.attr, + &dev_attr_cont_num_brk.attr, + &dev_attr_cont_zc_thr.attr, + &dev_attr_vbat_monitor.attr, + &dev_attr_lra_resistance.attr, + &dev_attr_prctmode.attr, + &dev_attr_haptic_audio.attr, + &dev_attr_ram_vbat_comp.attr, + &dev_attr_haptic_audio_time.attr, + &dev_attr_trig_boost.attr, + &dev_attr_rtp.attr, + &dev_attr_osc_cali.attr, + NULL +}; + +static struct attribute_group aw8624_vibrator_attribute_group = { + .attrs = aw8624_vibrator_attributes +}; + +static enum hrtimer_restart aw8624_vibrator_timer_func(struct hrtimer *timer) +{ + struct aw8624 *aw8624 = container_of(timer, struct aw8624, timer); + + pr_info("%s enter\n", __func__); + aw8624->state = 0; + schedule_work(&aw8624->vibrator_work); + + return HRTIMER_NORESTART; +} + +static irqreturn_t aw8624_irq(int irq, void *data) +{ + struct aw8624 *aw8624 = data; + unsigned char reg_val = 0; + unsigned char glb_st = 0; + unsigned int buf_len = 0; + + /*pr_info("%s enter\n", __func__);*/ + aw8624_i2c_read(aw8624, AW8624_REG_SYSINT, ®_val); + /*pr_info("%s: reg SYSINT=0x%x\n", __func__, reg_val);*/ + /*if (reg_val & AW8624_BIT_SYSINT_UVLI)*/ + /*pr_err("%s chip uvlo int error\n", __func__);*/ + /*if (reg_val & AW8624_BIT_SYSINT_OCDI)*/ + /*pr_err("%s chip over current int error\n", __func__);*/ + /*if (reg_val & AW8624_BIT_SYSINT_OTI)*/ + /*pr_err("%s chip over temperature int error\n", __func__);*/ + /*if (reg_val & AW8624_BIT_SYSINT_DONEI)*/ + /*pr_info("%s chip playback done\n", __func__);*/ + /*if (reg_val & AW8624_BIT_SYSINT_FF_AFI)*/ + /*pr_info("%s: aw8624 rtp fifo almost full int\n", __func__);*/ + + if (reg_val & AW8624_BIT_SYSINT_UVLI) { + aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, &glb_st); + if (glb_st == 0) { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSINTM, + AW8624_BIT_SYSINTM_UVLO_MASK, + AW8624_BIT_SYSINTM_UVLO_OFF); + } + } + + if (reg_val & AW8624_BIT_SYSINT_FF_AEI) { + /*pr_info("%s: aw8624 rtp fifo almost empty int\n", __func__);*/ + if (aw8624->rtp_init) { + while ((!aw8624_haptic_rtp_get_fifo_afs(aw8624)) && + (aw8624->play_mode == AW8624_HAPTIC_RTP_MODE)) { + pr_info + ("%s: aw8624 rtp mode fifo update, cnt=%d\n", + __func__, aw8624->rtp_cnt); + if ((aw8624_rtp->len-aw8624->rtp_cnt) < + (aw8624->ram.base_addr>>3)) { + buf_len = + aw8624_rtp->len-aw8624->rtp_cnt; + } else { + buf_len = (aw8624->ram.base_addr>>3); + } + aw8624_i2c_writes(aw8624, + AW8624_REG_RTP_DATA, + &aw8624_rtp->data[aw8624->rtp_cnt], + buf_len); + aw8624->rtp_cnt += buf_len; + aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, + &glb_st); + if ((aw8624->rtp_cnt == aw8624_rtp->len) || + ((glb_st & 0x0f) == 0x00)) { + pr_info("%s:rtp load completely!", + __func__); + pr_info(" glb_st:0x%x\n", glb_st); + aw8624_haptic_set_rtp_aei(aw8624, + false); + aw8624->rtp_cnt = 0; + aw8624->rtp_init = 0; + break; + } + } + } + } + + if (reg_val & AW8624_BIT_SYSINT_FF_AFI) + pr_info("%s: aw8624 rtp mode fifo full empty\n", __func__); + + if (aw8624->play_mode != AW8624_HAPTIC_RTP_MODE) + aw8624_haptic_set_rtp_aei(aw8624, false); + + /*aw8624_i2c_read(aw8624, AW8624_REG_SYSINT, ®_val);*/ + /*aw8624_i2c_read(aw8624, AW8624_REG_SYSST, ®_val);*/ + + return IRQ_HANDLED; +} + +static void aw8624_vibrator_work_routine(struct work_struct *work) +{ + struct aw8624 *aw8624 = + container_of(work, struct aw8624, vibrator_work); + + pr_info("%s enter\n", __func__); + + mutex_lock(&aw8624->lock); + + aw8624_haptic_stop(aw8624); + if (aw8624->state) { + if (aw8624->activate_mode == AW8624_HAPTIC_ACTIVATE_RAM_MODE) { + aw8624_haptic_ram_config(aw8624); + if (aw8624->duration > 60) { + aw8624_haptic_ram_vbat_comp(aw8624, true); + aw8624_haptic_play_repeat_seq(aw8624, true); + } else { + aw8624_haptic_ram_vbat_comp(aw8624, false); + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_RAM_MODE); + aw8624_haptic_play_go(aw8624, true); + } + } else if (aw8624->activate_mode == AW8624_HAPTIC_ACTIVATE_CONT_MODE) { + aw8624_haptic_cont(aw8624); + } else { + /*other mode*/ + } + /* run ms timer */ + hrtimer_start(&aw8624->timer, + ktime_set(aw8624->duration / 1000, + (aw8624->duration % 1000) * 1000000), + HRTIMER_MODE_REL); + PM_STAY_AWAKE(aw8624->ws); + aw8624->wk_lock_flag = 1; + } else { + if (aw8624->wk_lock_flag == 1) { + PM_RELAX(aw8624->ws); + aw8624->wk_lock_flag = 0; + } + /*aw8624_haptic_stop(aw8624);*/ + } + + mutex_unlock(&aw8624->lock); +} + +static int aw8624_vibrator_init(struct aw8624 *aw8624) +{ + int ret = 0; + + pr_info("%s enter\n", __func__); + +#ifdef TIMED_OUTPUT + aw8624->to_dev.name = "vibrator"; + aw8624->to_dev.get_time = aw8624_vibrator_get_time; + aw8624->to_dev.enable = aw8624_vibrator_enable; + + ret = timed_output_dev_register(&(aw8624->to_dev)); + if (ret < 0) { + dev_err(aw8624->dev, "%s: fail to create timed output dev\n", + __func__); + return ret; + } + ret = sysfs_create_group(&aw8624->to_dev.dev->kobj, + &aw8624_vibrator_attribute_group); + if (ret < 0) { + dev_err(aw8624->dev, + "%s error creating sysfs attr files\n", + __func__); + return ret; + } +#else + aw8624->cdev.name = "vibrator"; + aw8624->cdev.brightness_set = aw8624_vibrator_enable; + + ret = devm_led_classdev_register(&aw8624->i2c->dev, &(aw8624->cdev)); + if (ret < 0) { + dev_err(aw8624->dev, "%s: fail to create leds dev\n", + __func__); + return ret; + } + + ret = sysfs_create_group(&aw8624->cdev.dev->kobj, + &aw8624_vibrator_attribute_group); + if (ret < 0) { + dev_err(aw8624->dev, "%s error creating sysfs attr files\n", + __func__); + return ret; + } +#endif + hrtimer_init(&aw8624->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + aw8624->timer.function = aw8624_vibrator_timer_func; + INIT_WORK(&aw8624->vibrator_work, aw8624_vibrator_work_routine); + + if (aw8624->IsUsedIRQ) + INIT_WORK(&aw8624->rtp_work, aw8624_rtp_work_routine); + + mutex_init(&aw8624->lock); + mutex_init(&aw8624->rtp_lock); + + return 0; +} + +/****************************************************** + * + * irq + * + ******************************************************/ +static void aw8624_interrupt_setup(struct aw8624 *aw8624) +{ + unsigned char reg_val = 0; + + aw8624_i2c_read(aw8624, AW8624_REG_SYSINT, ®_val); + pr_info("%s: reg SYSINT=0x%x\n", __func__, reg_val); + + aw8624_i2c_write_bits(aw8624, AW8624_REG_DBGCTRL, + AW8624_BIT_DBGCTRL_INT_MODE_MASK, + AW8624_BIT_DBGCTRL_INT_MODE_EDGE); + + aw8624_i2c_write_bits(aw8624, AW8624_REG_SYSINTM, + AW8624_BIT_SYSINTM_UVLO_MASK, AW8624_BIT_SYSINTM_UVLO_EN); + aw8624_i2c_write_bits(aw8624, AW8624_REG_SYSINTM, + AW8624_BIT_SYSINTM_OCD_MASK, AW8624_BIT_SYSINTM_OCD_EN); + aw8624_i2c_write_bits(aw8624, AW8624_REG_SYSINTM, + AW8624_BIT_SYSINTM_OT_MASK, AW8624_BIT_SYSINTM_OT_EN); +} + +#if 0 +/* +* schedule_work is low priority. +* aw8624 rtp mode can`t be interrupted. +*/ +static irqreturn_t aw8624_irq(int irq, void *data) +{ + struct aw8624 *aw8624 = (struct aw8624 *)data; + + schedule_work(&aw8624->irq_work); + return IRQ_HANDLED; +} + +#endif +/***************************************************** + * + * device tree + * + *****************************************************/ +static int aw8624_parse_dt(struct device *dev, struct aw8624 *aw8624, + struct device_node *np) { + unsigned int val = 0; + /*unsigned int brake_ram_config[24];*/ + unsigned int brake_cont_config[24]; + unsigned int f0_trace_parameter[4]; + unsigned int bemf_config[4]; + unsigned int duration_time[5]; + unsigned int sw_brake[2]; + unsigned int wavseq_config[16]; + unsigned int wavloop_config[10]; + unsigned int td_brake_config[3]; + + aw8624->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (aw8624->reset_gpio >= 0) { + dev_info(dev, "%s: reset gpio provided ok\n", __func__); + } else { + aw8624->reset_gpio = -1; + dev_err(dev, + "%s: no reset gpio provided, will not HW reset device\n", + __func__); + return -1; + } + + aw8624->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (aw8624->irq_gpio < 0) { + dev_err(dev, "%s: no irq gpio provided.\n", __func__); + aw8624->IsUsedIRQ = false; + } else { + dev_info(dev, "%s: irq gpio provided ok.\n", __func__); + aw8624->IsUsedIRQ = true; + } + aw8624->ram_update_delay = + of_property_read_bool(np, "ram-update-delay"); + val = of_property_read_u32(np, "reg-real-addr", &aw8624->reg_real_addr); + if (val) { + dev_info(dev, "configure dts no the reg-real-addr\n"); + aw8624->dts_add_real = false; + } else { + aw8624->dts_add_real = true; + } + + val = + of_property_read_u32(np, "vib_mode", &aw8624_dts_data.aw8624_mode); + if (val != 0) + pr_info("vib_mode not found\n"); + val = + of_property_read_u32(np, "vib_f0_pre", &aw8624_dts_data.aw8624_f0_pre); + if (val != 0) + pr_info("vib_f0_pre not found\n"); + val = + of_property_read_u32(np, "vib_f0_cali_percen", + &aw8624_dts_data.aw8624_f0_cali_percen); + if (val != 0) + pr_info("vib_f0_cali_percen not found\n"); + val = + of_property_read_u32(np, "vib_cont_drv_lev", + &aw8624_dts_data.aw8624_cont_drv_lvl); + if (val != 0) + pr_info("vib_cont_drv_lev not found\n"); + val = + of_property_read_u32(np, "vib_cont_drv_lvl_ov", + &aw8624_dts_data.aw8624_cont_drv_lvl_ov); + if (val != 0) + pr_info("vib_cont_drv_lvl_ov not found\n"); + val = + of_property_read_u32(np, "vib_cont_td", + &aw8624_dts_data.aw8624_cont_td); + if (val != 0) + pr_info("vib_cont_td not found\n"); + val = + of_property_read_u32(np, "vib_cont_zc_thr", + &aw8624_dts_data.aw8624_cont_zc_thr); + if (val != 0) + pr_info("vib_cont_zc_thr not found\n"); + val = + of_property_read_u32(np, "vib_cont_num_brk", + &aw8624_dts_data.aw8624_cont_num_brk); + if (val != 0) + pr_info("vib_cont_num_brk not found\n"); + val = + of_property_read_u32(np, "vib_f0_coeff", + &aw8624_dts_data.aw8624_f0_coeff); + if (val != 0) + pr_info("vib_f0_coeff not found\n"); + + val = of_property_read_u32_array(np, "vib_duration_time", + duration_time, ARRAY_SIZE(duration_time)); + if (val != 0) + pr_info("%s vib_duration_time not found\n", __func__); + memcpy(aw8624_dts_data.aw8624_duration_time, + duration_time, sizeof(duration_time)); + + val = of_property_read_u32_array(np, "vib_brake_cont_config", + brake_cont_config, ARRAY_SIZE(brake_cont_config)); + if (val != 0) + pr_info("%s vib_brake_cont_config not found\n", __func__); + memcpy(aw8624_dts_data.aw8624_cont_brake, + brake_cont_config, sizeof(brake_cont_config)); + + val = of_property_read_u32_array(np, "vib_f0_trace_parameter", + f0_trace_parameter, ARRAY_SIZE(f0_trace_parameter)); + if (val != 0) + pr_info("%s vib_f0_trace_parameter not found\n", __func__); + memcpy(aw8624_dts_data.aw8624_f0_trace_parameter, + f0_trace_parameter, sizeof(f0_trace_parameter)); + + val = of_property_read_u32_array(np, "vib_bemf_config", + bemf_config, ARRAY_SIZE(bemf_config)); + if (val != 0) + pr_info("%s vib_bemf_config not found\n", __func__); + memcpy(aw8624_dts_data.aw8624_bemf_config, + bemf_config, sizeof(bemf_config)); + + val = + of_property_read_u32_array(np, "vib_sw_brake", + sw_brake, ARRAY_SIZE(sw_brake)); + if (val != 0) + pr_info("%s vib_wavseq not found\n", __func__); + memcpy(aw8624_dts_data.aw8624_sw_brake, + sw_brake, sizeof(sw_brake)); + + val = of_property_read_u32_array(np, "vib_wavseq", + wavseq_config, ARRAY_SIZE(wavseq_config)); + if (val != 0) + pr_info("%s vib_wavseq not found\n", __func__); + memcpy(aw8624_dts_data.aw8624_wavseq, + wavseq_config, sizeof(wavseq_config)); + + val = of_property_read_u32_array(np, "vib_wavloop", + wavloop_config, ARRAY_SIZE(wavloop_config)); + if (val != 0) + pr_info("%s vib_wavloop not found\n", __func__); + memcpy(aw8624_dts_data.aw8624_wavloop, + wavloop_config, sizeof(wavloop_config)); + + val = of_property_read_u32_array(np, "vib_td_brake", + td_brake_config, ARRAY_SIZE(td_brake_config)); + if (val != 0) + pr_info("%s vib_td_brake not found\n", __func__); + memcpy(aw8624_dts_data.aw8624_td_brake, + td_brake_config, sizeof(td_brake_config)); + + val = of_property_read_u32(np, "vib_tset", + &aw8624_dts_data.aw8624_tset); + if (val != 0) + pr_info("%s vib_tset not found\n", __func__); + val = of_property_read_u32(np, "vib_func_parameter1", + &aw8624_dts_data.aw8624_parameter1); + if (val != 0) + pr_info("%s vib_func_parameter1 not found\n", __func__); + + + + return 0; +} + +static int aw8624_hw_reset(struct aw8624 *aw8624) +{ + pr_info("%s enter\n", __func__); + + if (aw8624 && gpio_is_valid(aw8624->reset_gpio)) { + gpio_set_value_cansleep(aw8624->reset_gpio, 0); + usleep_range(1000, 2000); + gpio_set_value_cansleep(aw8624->reset_gpio, 1); + usleep_range(3500, 4000); + } else { + dev_err(aw8624->dev, "%s: failed\n", __func__); + } + return 0; +} + +/***************************************************** + * + * check chip id + * + *****************************************************/ +static int aw8624_read_chipid(struct aw8624 *aw8624) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char reg = 0; + + while (cnt < AW_READ_CHIPID_RETRIES) { + /* hardware reset */ + aw8624_hw_reset(aw8624); + + ret = aw8624_i2c_read(aw8624, AW8624_REG_ID, ®); + if (ret < 0) { + dev_err(aw8624->dev, + "%s: failed to read register AW8624_REG_ID: %d\n", + __func__, ret); + } + + switch (reg) { + case 0x24: + pr_info("%s aw8624 detected\n", __func__); + aw8624->chipid = AW8624_ID; + aw8624_haptic_softreset(aw8624); + return 0; + default: + pr_info("%s unsupported device revision (0x%x)\n", + __func__, reg); + break; + } + cnt++; + + usleep_range(2000, 3000); + } + + return -EINVAL; +} + + +/****************************************************** + * + * sys group attribute: reg + * + ******************************************************/ +static ssize_t +aw8624_i2c_reg_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw8624 *aw8624 = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0, 0}; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) + aw8624_i2c_write(aw8624, + (unsigned char)databuf[0], + (unsigned char)databuf[1]); + + return count; +} + +static ssize_t +aw8624_i2c_reg_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct aw8624 *aw8624 = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + for (i = 0; i < AW8624_REG_MAX; i++) { + if (!(aw8624_reg_access[i]®_RD_ACCESS)) + continue; + aw8624_i2c_read(aw8624, i, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, + "reg:0x%02x=0x%02x\n", i, reg_val); + } + return len; +} +static ssize_t +aw8624_i2c_ram_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw8624 *aw8624 = dev_get_drvdata(dev); + int err, val; + + err = kstrtoint(buf, 0, &val); + if (err != 0) { + pr_err("%s format not match!", __func__); + return count; + } + pr_info("%s: value=%d\n", __func__, val); + if (val == 1) + aw8624_ram_update(aw8624); + + return count; +} + +static ssize_t +aw8624_i2c_ram_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct aw8624 *aw8624 = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned int i = 0; + unsigned char reg_val = 0; + + aw8624_haptic_stop(aw8624); + /* RAMINIT Enable */ + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_RAMINIT_MASK, + AW8624_BIT_SYSCTRL_RAMINIT_EN); + + aw8624_i2c_write(aw8624, + AW8624_REG_RAMADDRH, + (unsigned char)(aw8624->ram.base_addr>>8)); + aw8624_i2c_write(aw8624, + AW8624_REG_RAMADDRL, + (unsigned char)(aw8624->ram.base_addr&0x00ff)); + len += snprintf(buf+len, PAGE_SIZE-len, "aw8624_haptic_ram:\n"); + for (i = 0; i < aw8624->ram.len; i++) { + aw8624_i2c_read(aw8624, AW8624_REG_RAMDATA, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, "0x%02x,", reg_val); + } + len += snprintf(buf+len, PAGE_SIZE-len, "\n"); + /* RAMINIT Disable */ + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_RAMINIT_MASK, + AW8624_BIT_SYSCTRL_RAMINIT_OFF); + + return len; +} + +static DEVICE_ATTR(reg, 0664, aw8624_i2c_reg_show, aw8624_i2c_reg_store); +static DEVICE_ATTR(ram, 0664, aw8624_i2c_ram_show, aw8624_i2c_ram_store); + +static struct attribute *aw8624_attributes[] = { + &dev_attr_reg.attr, + &dev_attr_ram.attr, + NULL +}; + +static struct attribute_group aw8624_attribute_group = { + .attrs = aw8624_attributes +}; + +#ifdef CONFIG_PM_SLEEP +static int aw8624_suspend(struct device *dev) +{ + struct aw8624 *aw8624 = dev_get_drvdata(dev); + + pr_info("%s enter\n",__func__); + mutex_lock(&aw8624->lock); + hrtimer_cancel(&aw8624->timer); + aw8624->state = 0; + aw8624_haptic_stop(aw8624); + mutex_unlock(&aw8624->lock); + + return 0; +} + +static int aw8624_resume(struct device *dev) +{ + return 0; +} +#endif + +/****************************************************** + * + * i2c driver + * + ******************************************************/ +static int +aw8624_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct aw8624 *aw8624; + struct device_node *np = i2c->dev.of_node; + int ret = -1; + int irq_flags = 0; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed\n"); + return -EIO; + } + + aw8624 = devm_kzalloc(&i2c->dev, sizeof(struct aw8624), GFP_KERNEL); + if (aw8624 == NULL) + return -ENOMEM; + + aw8624->dev = &i2c->dev; + aw8624->i2c = i2c; + + i2c_set_clientdata(i2c, aw8624); + + /* aw8624 rst & int */ + if (np) { + ret = aw8624_parse_dt(&i2c->dev, aw8624, np); + if (ret) { + dev_err(&i2c->dev, + "%s: failed to parse device tree node\n", __func__); + goto err_parse_dt; + } + } + if (aw8624->dts_add_real) + i2c->addr = (u16)aw8624->reg_real_addr; + + if (gpio_is_valid(aw8624->reset_gpio)) { + aw8624->reset_gpio_ret = + devm_gpio_request_one(&i2c->dev, aw8624->reset_gpio, + GPIOF_OUT_INIT_LOW, "aw8624_rst"); + if (aw8624->reset_gpio_ret) { + dev_err(&i2c->dev, + "%s: rst request failed\n", __func__); + return aw8624->reset_gpio_ret; + } + } + + if (gpio_is_valid(aw8624->irq_gpio)) { + aw8624->irq_gpio_ret = + devm_gpio_request_one(&i2c->dev, aw8624->irq_gpio, + GPIOF_DIR_IN, "aw8624_int"); + if (aw8624->irq_gpio_ret) { + dev_err(&i2c->dev, + "%s: int request failed\n", __func__); + return aw8624->irq_gpio_ret; + } + } + + /* aw8624 chip id */ + ret = aw8624_read_chipid(aw8624); + if (ret < 0) { + dev_err(&i2c->dev, + "%s: aw8624_read_chipid failed ret=%d\n", __func__, ret); + goto err_id; + } + + /* aw8624 irq */ + if (gpio_is_valid(aw8624->irq_gpio) && + !(aw8624->flags & AW8624_FLAG_SKIP_INTERRUPTS)) { + /* register irq handler */ + aw8624_interrupt_setup(aw8624); + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, + gpio_to_irq(aw8624->irq_gpio), + NULL, aw8624_irq, irq_flags, + "aw8624", aw8624); + if (ret != 0) { + dev_err(&i2c->dev, + "%s: failed to request IRQ %d: %d\n", + __func__, gpio_to_irq(aw8624->irq_gpio), ret); + goto err_irq; + } + } + dev_set_drvdata(&i2c->dev, aw8624); + + ret = sysfs_create_group(&i2c->dev.kobj, &aw8624_attribute_group); + if (ret < 0) { + dev_info(&i2c->dev, + "%s error creating sysfs attr files\n", __func__); + goto err_sysfs; + } + + g_aw8624 = aw8624; + + PM_WAKEUP_REGISTER(NULL, aw8624->ws, "vibrator"); + if (!aw8624->ws) + return -ENOMEM; + aw8624_vibrator_init(aw8624); + aw8624_haptic_init(aw8624); + aw8624_ram_init(aw8624); + usleep_range(100000, 150000); + if (aw8624->IsUsedIRQ) + aw8624_haptic_trig1_config(aw8624, true); + pr_info("%s probe completed successfully!\n", __func__); + + return 0; + +err_sysfs: + sysfs_remove_group(&i2c->dev.kobj, &aw8624_attribute_group); +err_irq: +err_id: +err_parse_dt: + return ret; +} + +static int aw8624_i2c_remove(struct i2c_client *i2c) +{ + struct aw8624 *aw8624 = i2c_get_clientdata(i2c); + + cancel_delayed_work_sync(&aw8624->ram_work); + misc_deregister(&aw8624_haptic_misc); + + cancel_work_sync(&aw8624->haptic_audio.work); + hrtimer_cancel(&aw8624->haptic_audio.timer); + if (aw8624->IsUsedIRQ) + cancel_work_sync(&aw8624->rtp_work); + cancel_work_sync(&aw8624->vibrator_work); + hrtimer_cancel(&aw8624->timer); + + mutex_destroy(&aw8624->lock); + mutex_destroy(&aw8624->rtp_lock); + mutex_destroy(&aw8624->haptic_audio.lock); + + PM_WAKEUP_UNREGISTER(aw8624->ws); + + sysfs_remove_group(&i2c->dev.kobj, &aw8624_attribute_group); + sysfs_remove_group(&i2c->dev.kobj, &aw8624_vibrator_attribute_group); + + devm_free_irq(&i2c->dev, gpio_to_irq(aw8624->irq_gpio), aw8624); + + return 0; +} + +static const struct i2c_device_id aw8624_i2c_id[] = { + { AW8624_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw8624_i2c_id); + +#ifdef CONFIG_PM_SLEEP +static const struct dev_pm_ops aw8624_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(aw8624_suspend, aw8624_resume) +}; +#endif + +static const struct of_device_id aw8624_dt_match[] = { + { .compatible = "awinic,aw8624_haptic" }, + { }, +}; + +static struct i2c_driver aw8624_i2c_driver = { + .driver = { + .name = AW8624_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw8624_dt_match), +#ifdef CONFIG_PM_SLEEP + .pm = &aw8624_pm_ops, +#endif + }, + .probe = aw8624_i2c_probe, + .remove = aw8624_i2c_remove, + .id_table = aw8624_i2c_id, +}; + +static int __init aw8624_i2c_init(void) +{ + int ret = 0; + + pr_info("aw8624 driver version %s\n", AW8624_VERSION); + + ret = i2c_add_driver(&aw8624_i2c_driver); + if (ret) { + pr_err("fail to add aw8624 device into i2c\n"); + return ret; + } + + return 0; +} + +module_init(aw8624_i2c_init); + +static void __exit aw8624_i2c_exit(void) +{ + i2c_del_driver(&aw8624_i2c_driver); +} +module_exit(aw8624_i2c_exit); + +MODULE_DESCRIPTION("AW8624 Haptic Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/awinic/aw8624/aw8624.h b/drivers/misc/awinic/aw8624/aw8624.h new file mode 100644 index 000000000000..c510dba87b4e --- /dev/null +++ b/drivers/misc/awinic/aw8624/aw8624.h @@ -0,0 +1,305 @@ +#ifndef _AW8624_H_ +#define _AW8624_H_ + +/********************************************************* + * + * kernel version + * + ********************************************************/ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 4, 1) +#define TIMED_OUTPUT +#endif + +/********************************************************* + * + * aw8624.h + * + ********************************************************/ +#include +#include +#include +#include +#include +#include +#ifdef TIMED_OUTPUT +#include <../../../drivers/staging/android/timed_output.h> +#else +#include +#endif +#include + +/********************************************************* + * + * marco + * + ********************************************************/ +#define MAX_I2C_BUFFER_SIZE 65536 + +#define AW8624_REG_MAX 0xff + +#define AW8624_SEQUENCER_SIZE 8 +#define AW8624_SEQUENCER_LOOP_SIZE 4 + +#define AW8624_RTP_I2C_SINGLE_MAX_NUM 512 + +#define HAPTIC_MAX_TIMEOUT 10000 + +#define AW8624_VBAT_REFER 4200 +#define AW8624_VBAT_MIN 3000 +#define AW8624_VBAT_MAX 4500 + +/* +* aw8624 dts info +* +* aw8624_brake_local[3][8] +* [0][0-7] is en_brake1,en_brake2,brake0_level, +* brake1_level,brake2_level,brake0_p_num,brake1_p_num,brake0_level; +*/ +struct aw8624_dts_info { + int aw8624_mode; + int aw8624_f0_pre; /* AW8624_HAPTIC_F0_PRE 2600 */ + int aw8624_f0_cali_percen; /* AW8624_HAPTIC_F0_CALI_PERCEN 7 */ + int aw8624_cont_drv_lvl; /* AW8624_HAPTIC_CONT_DRV_LVL 125 */ + int aw8624_cont_drv_lvl_ov; /* AW8624_HAPTIC_CONT_DRV_LVL_OV 155 */ + int aw8624_cont_td; /* AW8624_HAPTIC_CONT_TD 0xf06c */ + int aw8624_cont_zc_thr; /* AW8624_HAPTIC_CONT_ZC_THR 0x0ff1 */ + int aw8624_cont_num_brk; /* AW8624_HAPTIC_CONT_NUM_BRK 3 */ + int aw8624_f0_coeff; /* AW8624_HAPTIC_F0_COEFF 260 */ + + int aw8624_duration_time[5]; + int aw8624_cont_brake[3][8]; + int aw8624_f0_trace_parameter[4]; + int aw8624_bemf_config[4]; + int aw8624_sw_brake[2]; + int aw8624_wavseq[16]; + int aw8624_wavloop[10]; + int aw8624_td_brake[3]; + int aw8624_tset; + int aw8624_parameter1; +}; + +enum aw8624_flags { + AW8624_FLAG_NONR = 0, + AW8624_FLAG_SKIP_INTERRUPTS = 1, +}; + +enum aw8624_chipids { + AW8624_ID = 1, +}; + +enum aw8624_haptic_read_write { + AW8624_HAPTIC_CMD_READ_REG = 0, + AW8624_HAPTIC_CMD_WRITE_REG = 1, +}; + +enum aw8624_haptic_work_mode { + AW8624_HAPTIC_STANDBY_MODE = 0, + AW8624_HAPTIC_RAM_MODE = 1, + AW8624_HAPTIC_RTP_MODE = 2, + AW8624_HAPTIC_TRIG_MODE = 3, + AW8624_HAPTIC_CONT_MODE = 4, + AW8624_HAPTIC_RAM_LOOP_MODE = 5, +}; + +enum aw8624_haptic_bst_mode { + AW8624_HAPTIC_BYPASS_MODE = 0, + AW8624_HAPTIC_BOOST_MODE = 1, +}; + +enum aw8624_haptic_activate_mode { + AW8624_HAPTIC_ACTIVATE_RAM_MODE = 0, + AW8624_HAPTIC_ACTIVATE_CONT_MODE = 1, +}; + +enum aw8624_haptic_vbat_comp_mode { + AW8624_HAPTIC_VBAT_SW_COMP_MODE = 0, + AW8624_HAPTIC_VBAT_HW_COMP_MODE = 1, +}; + +enum aw8624_haptic_ram_vbat_comp_mode { + AW8624_HAPTIC_RAM_VBAT_COMP_DISABLE = 0, + AW8624_HAPTIC_RAM_VBAT_COMP_ENABLE = 1, +}; + +enum aw8624_haptic_f0_flag { + AW8624_HAPTIC_LRA_F0 = 0, + AW8624_HAPTIC_CALI_F0 = 1, +}; + +enum aw8624_haptic_pwm_mode { + AW8624_PWM_48K = 0, + AW8624_PWM_24K = 1, + AW8624_PWM_12K = 2, +}; + +/********************************************************* + * + * struct + * + ********************************************************/ +struct fileops { + unsigned char cmd; + unsigned char reg; + unsigned char ram_addrh; + unsigned char ram_addrl; +}; + +struct ram { + unsigned int len; + unsigned int check_sum; + unsigned int base_addr; + unsigned char version; + unsigned char ram_shift; + unsigned char baseaddr_shift; +}; + +struct haptic_ctr { + unsigned char cnt; + unsigned char cmd; + unsigned char play; + unsigned char wavseq; + unsigned char loop; + unsigned char gain; + struct list_head list; +}; + +struct haptic_audio { + struct mutex lock; + struct hrtimer timer; + struct work_struct work; + int delay_val; + int timer_val; + unsigned char cnt; + struct haptic_ctr ctr[256]; +}; + +struct aw8624 { + struct regmap *regmap; + struct i2c_client *i2c; + struct device *dev; + struct input_dev *input; + + struct mutex lock; + struct mutex rtp_lock; + struct wakeup_source *ws; + unsigned char wk_lock_flag; + struct hrtimer timer; + struct work_struct vibrator_work; + struct work_struct irq_work; + struct work_struct rtp_work; + struct delayed_work ram_work; + struct delayed_work stop_work; +#ifdef TIMED_OUTPUT + struct timed_output_dev to_dev; +#else + struct led_classdev cdev; +#endif + struct fileops fileops; + struct ram ram; + bool haptic_ready; + bool audio_ready; + int pre_haptic_number; + struct timeval current_time; + struct timeval pre_enter_time; + struct timeval start, end; + unsigned int timeval_flags; + unsigned int osc_cali_flag; + unsigned long int microsecond; + unsigned int theory_time; + unsigned int rtp_len; + int reset_gpio; + int irq_gpio; + int reset_gpio_ret; + int irq_gpio_ret; + + unsigned char hwen_flag; + unsigned char flags; + unsigned char chipid; + unsigned char chipid_flag; + unsigned char singlecycle; + unsigned char play_mode; + unsigned char activate_mode; + unsigned char auto_boost; + + int state; + int duration; + int amplitude; + int index; + int vmax; + int gain; + int f0_value; + + unsigned char seq[AW8624_SEQUENCER_SIZE]; + unsigned char loop[AW8624_SEQUENCER_SIZE]; + + unsigned int rtp_cnt; + unsigned int rtp_file_num; + + unsigned char rtp_init; + unsigned char ram_init; + unsigned char rtp_routine_on; + + unsigned int f0; + unsigned int f0_pre; + unsigned int cont_td; + unsigned int cont_f0; + unsigned int cont_zc_thr; + unsigned char cont_drv_lvl; + unsigned char cont_drv_lvl_ov; + unsigned char cont_num_brk; + unsigned char max_pos_beme; + unsigned char max_neg_beme; + unsigned char f0_cali_flag; + bool IsUsedIRQ; + bool ram_update_delay; + bool dts_add_real; + unsigned int reg_real_addr; + struct haptic_audio haptic_audio; + + unsigned char ram_vbat_comp; + unsigned int vbat; + unsigned int lra; + unsigned int interval_us; + unsigned int ramupdate_flag; + unsigned int rtpupdate_flag; + unsigned int osc_cali_run; + unsigned int lra_calib_data; + unsigned int f0_calib_data; +}; + +struct aw8624_container { + int len; + unsigned char data[]; +}; + +/********************************************************* + * + * ioctl + * + ********************************************************/ +struct aw8624_seq_loop { + unsigned char loop[AW8624_SEQUENCER_SIZE]; +}; + +struct aw8624_que_seq { + unsigned char index[AW8624_SEQUENCER_SIZE]; +}; + +#define AW8624_HAPTIC_IOCTL_MAGIC 'h' +#define AW8624_HAPTIC_SET_QUE_SEQ \ + _IOWR(AW8624_HAPTIC_IOCTL_MAGIC, 1, struct aw8624_que_seq*) +#define AW8624_HAPTIC_SET_SEQ_LOOP \ + _IOWR(AW8624_HAPTIC_IOCTL_MAGIC, 2, struct aw8624_seq_loop*) +#define AW8624_HAPTIC_PLAY_QUE_SEQ \ + _IOWR(AW8624_HAPTIC_IOCTL_MAGIC, 3, unsigned int) +#define AW8624_HAPTIC_SET_BST_VOL \ + _IOWR(AW8624_HAPTIC_IOCTL_MAGIC, 4, unsigned int) +#define AW8624_HAPTIC_SET_BST_PEAK_CUR \ + _IOWR(AW8624_HAPTIC_IOCTL_MAGIC, 5, unsigned int) +#define AW8624_HAPTIC_SET_GAIN \ + _IOWR(AW8624_HAPTIC_IOCTL_MAGIC, 6, unsigned int) +#define AW8624_HAPTIC_PLAY_REPEAT_SEQ \ + _IOWR(AW8624_HAPTIC_IOCTL_MAGIC, 7, unsigned int) + +#endif + diff --git a/drivers/misc/awinic/aw8624/aw8624_reg.h b/drivers/misc/awinic/aw8624/aw8624_reg.h new file mode 100644 index 000000000000..987ed1f3a972 --- /dev/null +++ b/drivers/misc/awinic/aw8624/aw8624_reg.h @@ -0,0 +1,606 @@ +#ifndef _AW8624_REG_H_ +#define _AW8624_REG_H_ + +/******************************************** + * Register List + *******************************************/ +#define AW8624_REG_ID 0x00 +#define AW8624_REG_SYSST 0x01 +#define AW8624_REG_SYSINT 0x02 +#define AW8624_REG_SYSINTM 0x03 +#define AW8624_REG_SYSCTRL 0x04 +#define AW8624_REG_GO 0x05 +#define AW8624_REG_RTP_DATA 0x06 +#define AW8624_REG_WAVSEQ1 0x07 +#define AW8624_REG_WAVSEQ2 0x08 +#define AW8624_REG_WAVSEQ3 0x09 +#define AW8624_REG_WAVSEQ4 0x0a +#define AW8624_REG_WAVSEQ5 0x0b +#define AW8624_REG_WAVSEQ6 0x0c +#define AW8624_REG_WAVSEQ7 0x0d +#define AW8624_REG_WAVSEQ8 0x0e +#define AW8624_REG_WAVLOOP1 0x0f +#define AW8624_REG_WAVLOOP2 0x10 +#define AW8624_REG_WAVLOOP3 0x11 +#define AW8624_REG_WAVLOOP4 0x12 +#define AW8624_REG_MAIN_LOOP 0x13 +#define AW8624_REG_TRG1_SEQP 0x14 +#define AW8624_REG_TRG1_SEQN 0x17 +#define AW8624_REG_PLAY_PRIO 0x1a +#define AW8624_REG_TRG_CFG1 0x1b +#define AW8624_REG_TRG_CFG2 0x1c +#define AW8624_REG_DBGCTRL 0x20 +#define AW8624_REG_BASE_ADDRH 0x21 +#define AW8624_REG_BASE_ADDRL 0x22 +#define AW8624_REG_FIFO_AEH 0x23 +#define AW8624_REG_FIFO_AEL 0x24 +#define AW8624_REG_FIFO_AFH 0x25 +#define AW8624_REG_FIFO_AFL 0x26 +#define AW8624_REG_WAKE_DLY 0x27 +#define AW8624_REG_START_DLY 0x28 +#define AW8624_REG_END_DLY_H 0x29 +#define AW8624_REG_END_DLY_L 0x2a +#define AW8624_REG_DATCTRL 0x2b +#define AW8624_REG_PWMDEL 0x2c +#define AW8624_REG_PWMPRC 0x2d +#define AW8624_REG_PWMDBG 0x2e +#define AW8624_REG_LDOCTRL 0x2f +#define AW8624_REG_DBGSTAT 0x30 +#define AW8624_REG_WAVECTRL 0x31 +#define AW8624_REG_BRAKE0_CTRL 0x32 +#define AW8624_REG_BRAKE1_CTRL 0x33 +#define AW8624_REG_BRAKE2_CTRL 0x34 +#define AW8624_REG_BRAKE_NUM 0x35 +#define AW8624_REG_ANADBG1 0x36 +#define AW8624_REG_ANADBG2 0x37 +#define AW8624_REG_ANACTRL 0x38 +#define AW8624_REG_SW_BRAKE 0x39 +#define AW8624_REG_GLBDBG 0x3a +#define AW8624_REG_DATDBG 0x3b +#define AW8624_REG_WDCTRL 0x3c +#define AW8624_REG_HDRVDBG 0x3d +#define AW8624_REG_PRLVL 0x3e +#define AW8624_REG_PRTIME 0x3f +#define AW8624_REG_RAMADDRH 0x40 +#define AW8624_REG_RAMADDRL 0x41 +#define AW8624_REG_RAMDATA 0x42 +#define AW8624_REG_TM 0x43 +#define AW8624_REG_BRA_MAX_NUM 0x44 +#define AW8624_REG_BEMF_ERM_FAC 0x45 +#define AW8624_REG_BEMF_BRA_FAC 0x46 +#define AW8624_REG_GLB_STATE 0x47 +#define AW8624_REG_CONT_CTRL 0x48 +#define AW8624_REG_F_PRE_H 0x49 +#define AW8624_REG_F_PRE_L 0x4a +#define AW8624_REG_TD_H 0x4b +#define AW8624_REG_TD_L 0x4c +#define AW8624_REG_TSET 0x4d +#define AW8624_REG_THRS_BRA_RAP 0x4e +#define AW8624_REG_THRS_BRA_END 0x4f +#define AW8624_REG_EF_CTRL 0x50 +#define AW8624_REG_EF_WDATAH 0x53 +#define AW8624_REG_EF_WDATAL 0x54 +#define AW8624_REG_EF_RDATAH 0x55 +#define AW8624_REG_EF_RDATAL 0x56 +#define AW8624_REG_DLY 0x57 +#define AW8624_REG_EF_WR_WIDTH 0x58 +#define AW8624_REG_EF_RD_WIDTH 0x59 +#define AW8624_REG_TRIM_LRA 0x5b +#define AW8624_REG_TRIM_OSC 0x5c +#define AW8624_REG_R_SPARE 0x5d +#define AW8624_REG_D2SCFG 0x5e +#define AW8624_REG_DETCTRL 0x5f +#define AW8624_REG_RLDET 0x60 +#define AW8624_REG_OSDET 0x61 +#define AW8624_REG_VBATDET 0x62 +#define AW8624_REG_TESTDET 0x63 +#define AW8624_REG_DETLO 0x64 +#define AW8624_REG_BEMFDBG 0x65 +#define AW8624_REG_ADCTEST 0x66 +#define AW8624_REG_BEMFTEST 0x67 +#define AW8624_REG_F_LRA_F0_H 0x68 +#define AW8624_REG_F_LRA_F0_L 0x69 +#define AW8624_REG_F_LRA_CONT_H 0x6a +#define AW8624_REG_F_LRA_CONT_L 0x6b +#define AW8624_REG_WAIT_VOL_MP 0x6e +#define AW8624_REG_WAIT_VOL_MN 0x6f +#define AW8624_REG_BEMF_VOL_H 0x70 +#define AW8624_REG_BEMF_VOL_L 0x71 +#define AW8624_REG_ZC_THRSH_H 0x72 +#define AW8624_REG_ZC_THRSH_L 0x73 +#define AW8624_REG_BEMF_VTHH_H 0x74 +#define AW8624_REG_BEMF_VTHH_L 0x75 +#define AW8624_REG_BEMF_VTHL_H 0x76 +#define AW8624_REG_BEMF_VTHL_L 0x77 +#define AW8624_REG_BEMF_NUM 0x78 +#define AW8624_REG_DRV_TIME 0x79 +#define AW8624_REG_TIME_NZC 0x7a +#define AW8624_REG_DRV_LVL 0x7b +#define AW8624_REG_DRV_LVL_OV 0x7c +#define AW8624_REG_NUM_F0_1 0x7d +#define AW8624_REG_NUM_F0_2 0x7e +#define AW8624_REG_NUM_F0_3 0x7f + + + +/******************************************** + * Register Access + *******************************************/ +#define REG_NONE_ACCESS 0 +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) + + +const unsigned char aw8624_reg_access[AW8624_REG_MAX] = { + [AW8624_REG_ID ] = REG_RD_ACCESS, + [AW8624_REG_SYSST ] = REG_RD_ACCESS, + [AW8624_REG_SYSINT ] = REG_RD_ACCESS, + [AW8624_REG_SYSINTM ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_SYSCTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_GO ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_RTP_DATA ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_WAVSEQ1 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_WAVSEQ2 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_WAVSEQ3 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_WAVSEQ4 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_WAVSEQ5 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_WAVSEQ6 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_WAVSEQ7 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_WAVSEQ8 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_WAVLOOP1 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_WAVLOOP2 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_WAVLOOP3 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_WAVLOOP4 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_MAIN_LOOP ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_TRG1_SEQP ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_TRG1_SEQN ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_PLAY_PRIO ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_TRG_CFG1 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_TRG_CFG2 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_DBGCTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_BASE_ADDRH ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_BASE_ADDRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_FIFO_AEH ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_FIFO_AEL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_FIFO_AFH ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_FIFO_AFL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_WAKE_DLY ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_START_DLY ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_END_DLY_H ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_END_DLY_L ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_DATCTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_PWMDEL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_PWMPRC ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_PWMDBG ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_LDOCTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_DBGSTAT ] = REG_RD_ACCESS, + [AW8624_REG_WAVECTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_BRAKE0_CTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_BRAKE1_CTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_BRAKE2_CTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_BRAKE_NUM ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_ANADBG1 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_ANADBG2 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_ANACTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_SW_BRAKE ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_GLBDBG ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_DATDBG ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_WDCTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_HDRVDBG ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_PRLVL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_PRTIME ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_RAMADDRH ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_RAMADDRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_RAMDATA ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_TM ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_BRA_MAX_NUM ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_BEMF_ERM_FAC ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_BEMF_BRA_FAC ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_GLB_STATE ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_CONT_CTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_F_PRE_H ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_F_PRE_L ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_TD_H ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_TD_L ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_TSET ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_THRS_BRA_RAP ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_THRS_BRA_END ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_EF_CTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_EF_WDATAH ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_EF_WDATAL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_EF_RDATAH ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_EF_RDATAL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_DLY ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_EF_WR_WIDTH ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_EF_RD_WIDTH ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_TRIM_LRA ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_TRIM_OSC ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_R_SPARE ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_D2SCFG ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_DETCTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_RLDET ] = REG_RD_ACCESS, + [AW8624_REG_OSDET ] = REG_RD_ACCESS, + [AW8624_REG_VBATDET ] = REG_RD_ACCESS, + [AW8624_REG_TESTDET ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_DETLO ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_BEMFDBG ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_ADCTEST ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_BEMFTEST ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_F_LRA_F0_H ] = REG_RD_ACCESS, + [AW8624_REG_F_LRA_F0_L ] = REG_RD_ACCESS, + [AW8624_REG_F_LRA_CONT_H ] = REG_RD_ACCESS, + [AW8624_REG_F_LRA_CONT_L ] = REG_RD_ACCESS, + [AW8624_REG_WAIT_VOL_MP ] = REG_RD_ACCESS, + [AW8624_REG_WAIT_VOL_MN ] = REG_RD_ACCESS, + [AW8624_REG_BEMF_VOL_H ] = REG_RD_ACCESS, + [AW8624_REG_BEMF_VOL_L ] = REG_RD_ACCESS, + [AW8624_REG_ZC_THRSH_H ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_ZC_THRSH_L ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_BEMF_VTHH_H ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_BEMF_VTHH_L ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_BEMF_VTHL_H ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_BEMF_VTHL_L ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_BEMF_NUM ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_DRV_TIME ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_TIME_NZC ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_DRV_LVL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_DRV_LVL_OV ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_NUM_F0_1 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_NUM_F0_2 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8624_REG_NUM_F0_3 ] = REG_RD_ACCESS | REG_WR_ACCESS, +}; + + + + +/****************************************************** + * Register Detail + *****************************************************/ +/* + * SYSST 0x01 + */ +#define AW8624_BIT_SYSST_OVS (1<<6) +#define AW8624_BIT_SYSST_UVLS (1<<5) +#define AW8624_BIT_SYSST_FF_AES (1<<4) +#define AW8624_BIT_SYSST_FF_AFS (1<<3) +#define AW8624_BIT_SYSST_OCDS (1<<2) +#define AW8624_BIT_SYSST_OTS (1<<1) +#define AW8624_BIT_SYSST_DONES (1<<0) + +/* + * SYSINT 0x02 + */ +#define AW8624_BIT_SYSINT_OVI (1<<6) +#define AW8624_BIT_SYSINT_UVLI (1<<5) +#define AW8624_BIT_SYSINT_FF_AEI (1<<4) +#define AW8624_BIT_SYSINT_FF_AFI (1<<3) +#define AW8624_BIT_SYSINT_OCDI (1<<2) +#define AW8624_BIT_SYSINT_OTI (1<<1) +#define AW8624_BIT_SYSINT_DONEI (1<<0) + +/* + * SYSINTM 0x03 + */ +#define AW8624_BIT_SYSINTM_OV_MASK (~(1<<6)) +#define AW8624_BIT_SYSINTM_OV_OFF (1<<6) +#define AW8624_BIT_SYSINTM_OV_EN (0<<6) +#define AW8624_BIT_SYSINTM_UVLO_MASK (~(1<<5)) +#define AW8624_BIT_SYSINTM_UVLO_OFF (1<<5) +#define AW8624_BIT_SYSINTM_UVLO_EN (0<<5) +#define AW8624_BIT_SYSINTM_FF_AE_MASK (~(1<<4)) +#define AW8624_BIT_SYSINTM_FF_AE_OFF (1<<4) +#define AW8624_BIT_SYSINTM_FF_AE_EN (0<<4) +#define AW8624_BIT_SYSINTM_FF_AF_MASK (~(1<<3)) +#define AW8624_BIT_SYSINTM_FF_AF_OFF (1<<3) +#define AW8624_BIT_SYSINTM_FF_AF_EN (0<<3) +#define AW8624_BIT_SYSINTM_OCD_MASK (~(1<<2)) +#define AW8624_BIT_SYSINTM_OCD_OFF (1<<2) +#define AW8624_BIT_SYSINTM_OCD_EN (0<<2) +#define AW8624_BIT_SYSINTM_OT_MASK (~(1<<1)) +#define AW8624_BIT_SYSINTM_OT_OFF (1<<1) +#define AW8624_BIT_SYSINTM_OT_EN (0<<1) +#define AW8624_BIT_SYSINTM_DONE_MASK (~(1<<0)) +#define AW8624_BIT_SYSINTM_DONE_OFF (1<<0) +#define AW8624_BIT_SYSINTM_DONE_EN (0<<0) + +/* + * SYSCTRL 0x04 + */ +#define AW8624_BIT_SYSCTRL_WAVDAT_MODE_MASK (~(3<<6)) +#define AW8624_BIT_SYSCTRL_WAVDAT_MODE_4X (3<<6) +#define AW8624_BIT_SYSCTRL_WAVDAT_MODE_2X (0<<6) +#define AW8624_BIT_SYSCTRL_WAVDAT_MODE_1X (1<<6) +#define AW8624_BIT_SYSCTRL_RAMINIT_MASK (~(1<<5)) +#define AW8624_BIT_SYSCTRL_RAMINIT_EN (1<<5) +#define AW8624_BIT_SYSCTRL_RAMINIT_OFF (0<<5) +#define AW8624_BIT_SYSCTRL_PLAY_MODE_MASK (~(3<<2)) +#define AW8624_BIT_SYSCTRL_PLAY_MODE_CONT (2<<2) +#define AW8624_BIT_SYSCTRL_PLAY_MODE_RTP (1<<2) +#define AW8624_BIT_SYSCTRL_PLAY_MODE_RAM (0<<2) +#define AW8624_BIT_SYSCTRL_WORK_MODE_MASK (~(1<<0)) +#define AW8624_BIT_SYSCTRL_STANDBY (1<<0) +#define AW8624_BIT_SYSCTRL_ACTIVE (0<<0) + +/* + * GO 0x05 + */ +#define AW8624_BIT_GO_MASK (~(1<<0)) +#define AW8624_BIT_GO_ENABLE (1<<0) +#define AW8624_BIT_GO_DISABLE (0<<0) + +/* + * WAVSEQ1 0x07 + */ +#define AW8624_BIT_WAVSEQ1_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ1_WAV_FRM_SEQ1_MASK (~(127<<0)) + +/* + * WAVSEQ2 0x08 + */ +#define AW8624_BIT_WAVSEQ2_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ2_WAV_FRM_SEQ2_MASK (~(127<<0)) + +/* + * WAVSEQ3 0x09 + */ +#define AW8624_BIT_WAVSEQ3_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ3_WAV_FRM_SEQ3_MASK (~(127<<0)) + +/* + * WAVSEQ4 0x0A + */ +#define AW8624_BIT_WAVSEQ4_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ4_WAV_FRM_SEQ4_MASK (~(127<<0)) + +/* + * WAVSEQ5 0X0B + */ +#define AW8624_BIT_WAVSEQ5_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ5_WAV_FRM_SEQ5_MASK (~(127<<0)) + +/* + * WAVSEQ6 0X0C + */ +#define AW8624_BIT_WAVSEQ6_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ6_WAV_FRM_SEQ6_MASK (~(127<<0)) + +/* + * WAVSEQ7 + */ +#define AW8624_BIT_WAVSEQ7_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ7_WAV_FRM_SEQ7_MASK (~(127<<0)) + +/* + * WAVSEQ8 + */ +#define AW8624_BIT_WAVSEQ8_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ8_WAV_FRM_SEQ8_MASK (~(127<<0)) + +/* + * WAVLOOP + */ +#define AW8624_BIT_WAVLOOP_SEQN_MASK (~(15<<4)) +#define AW8624_BIT_WAVLOOP_SEQNP1_MASK (~(15<<0)) +#define AW8624_BIT_WAVLOOP_INIFINITELY (15<<0) + +/* + * WAVLOOP1 + */ +#define AW8624_BIT_WAVLOOP1_SEQ1_MASK (~(15<<4)) +#define AW8624_BIT_WAVLOOP1_SEQ2_MASK (~(15<<0)) + +/* + * WAVLOOP2 + */ +#define AW8624_BIT_WAVLOOP2_SEQ3_MASK (~(15<<4)) +#define AW8624_BIT_WAVLOOP2_SEQ4_MASK (~(15<<0)) + +/* + * WAVLOOP3 + */ +#define AW8624_BIT_WAVLOOP3_SEQ5_MASK (~(15<<4)) +#define AW8624_BIT_WAVLOOP3_SEQ6_MASK (~(15<<0)) + +/* + * WAVLOOP4 + */ +#define AW8624_BIT_WAVLOOP4_SEQ7_MASK (~(15<<4)) +#define AW8624_BIT_WAVLOOP4_SEQ8_MASK (~(15<<0)) + + +/* + * PLAYPRIO + */ +#define AW8624_BIT_PLAYPRIO_GO_MASK (~(3<<6)) +#define AW8624_BIT_PLAYPRIO_TRIG3_MASK (~(3<<4)) +#define AW8624_BIT_PLAYPRIO_TRIG2_MASK (~(3<<2)) +#define AW8624_BIT_PLAYPRIO_TRIG1_MASK (~(3<<0)) + +/* + * TRGCFG1 + */ +#define AW8624_BIT_TRGCFG1_TRG3_POLAR_MASK (~(1<<5)) +#define AW8624_BIT_TRGCFG1_TRG3_POLAR_NEG (1<<5) +#define AW8624_BIT_TRGCFG1_TRG3_POLAR_POS (0<<5) +#define AW8624_BIT_TRGCFG1_TRG3_EDGE_MASK (~(1<<4)) +#define AW8624_BIT_TRGCFG1_TRG3_EDGE_POS (1<<4) +#define AW8624_BIT_TRGCFG1_TRG3_EDGE_POS_NEG (0<<4) +#define AW8624_BIT_TRGCFG1_TRG2_POLAR_MASK (~(1<<3)) +#define AW8624_BIT_TRGCFG1_TRG2_POLAR_NEG (1<<3) +#define AW8624_BIT_TRGCFG1_TRG2_POLAR_POS (0<<3) +#define AW8624_BIT_TRGCFG1_TRG2_EDGE_MASK (~(1<<2)) +#define AW8624_BIT_TRGCFG1_TRG2_EDGE_POS (1<<2) +#define AW8624_BIT_TRGCFG1_TRG2_EDGE_POS_NEG (0<<2) +#define AW8624_BIT_TRGCFG1_TRG1_POLAR_MASK (~(1<<1)) +#define AW8624_BIT_TRGCFG1_TRG1_POLAR_NEG (1<<1) +#define AW8624_BIT_TRGCFG1_TRG1_POLAR_POS (0<<1) +#define AW8624_BIT_TRGCFG1_TRG1_EDGE_MASK (~(1<<0)) +#define AW8624_BIT_TRGCFG1_TRG1_EDGE_POS (1<<0) +#define AW8624_BIT_TRGCFG1_TRG1_EDGE_POS_NEG (0<<0) + +/* + * TRGCFG2 + */ +#define AW8624_BIT_TRGCFG2_TRG3_ENABLE_MASK (~(1<<2)) +#define AW8624_BIT_TRGCFG2_TRG3_ENABLE (1<<2) +#define AW8624_BIT_TRGCFG2_TRG3_DISABLE (0<<2) +#define AW8624_BIT_TRGCFG2_TRG2_ENABLE_MASK (~(1<<1)) +#define AW8624_BIT_TRGCFG2_TRG2_ENABLE (1<<1) +#define AW8624_BIT_TRGCFG2_TRG2_DISABLE (0<<1) +#define AW8624_BIT_TRGCFG2_TRG1_ENABLE_MASK (~(1<<0)) +#define AW8624_BIT_TRGCFG2_TRG1_ENABLE (1<<0) +#define AW8624_BIT_TRGCFG2_TRG1_DISABLE (0<<0) + +/*DBGCTRL //0X20 */ +#define AW8624_BIT_DBGCTRL_INTN_TRG_SEL_MASK (~(1<<5)) +#define AW8624_BIT_DBGCTRL_INTN_SEL_ENABLE (1<<5) +#define AW8624_BIT_DBGCTRL_TRG_SEL_ENABLE (0<<5) +#define AW8624_BIT_DBGCTRL_INT_MODE_MASK (~(3<<2)) +#define AW8624_BIT_DBGCTRL_INTN_LEVEL_MODE (0<<2) +#define AW8624_BIT_DBGCTRL_INT_MODE_EDGE (1<<2) +#define AW8624_BIT_DBGCTRL_INTN_POSEDGE_MODE (2<<2) +#define AW8624_BIT_DBGCTRL_INTN_BOTH_EDGE_MODE (3<<2) + +/* + * DATCTRL + */ +#define AW8624_BIT_DATCTRL_FC_MASK (~(1<<6)) +#define AW8624_BIT_DATCTRL_FC_1000HZ (3<<6) +#define AW8624_BIT_DATCTRL_FC_800HZ (3<<6) +#define AW8624_BIT_DATCTRL_FC_600HZ (1<<6) +#define AW8624_BIT_DATCTRL_FC_400HZ (0<<6) +#define AW8624_BIT_DATCTRL_LPF_ENABLE_MASK (~(1<<5)) +#define AW8624_BIT_DATCTRL_LPF_ENABLE (1<<5) +#define AW8624_BIT_DATCTRL_LPF_DISABLE (0<<5) + +/*PWMPRC //0X2D */ +#define AW8624_BIT_PWMPRC_PRC_EN_MASK (~(1<<7)) +#define AW8624_BIT_PWMPRC_PRC_ENABLE (1<<7) +#define AW8624_BIT_PWMPRC_PRC_DISABLE (0<<7) +#define AW8624_BIT_PWMPRC_PRCTIME_MASK (~(0x7f<<0)) + +/* + * PWMDBG + */ +#define AW8624_BIT_PWMDBG_PWM_MODE_MASK (~(3<<5)) +#define AW8624_BIT_PWMDBG_PWM_12K (3<<5) +#define AW8624_BIT_PWMDBG_PWM_24K (2<<5) +#define AW8624_BIT_PWMDBG_PWM_48K (0<<5) + +/* + * WAVECTRL + */ +#define AW8624_BIT_WAVECTRL_NUM_OV_DRIVER_MASK (~(0xF<<4)) +#define AW8624_BIT_WAVECTRL_NUM_OV_DRIVER (0<<4) + +/* + * BST_AUTO + */ +#define AW8624_BIT_BST_AUTO_BST_AUTOSW_MASK (~(1<<2)) +#define AW8624_BIT_BST_AUTO_BST_AUTOMATIC_BOOST (1<<2) +#define AW8624_BIT_BST_AUTO_BST_MANUAL_BOOST (0<<2) + +/* + * CONT_CTRL + */ +#define AW8624_BIT_CONT_CTRL_ZC_DETEC_MASK (~(1<<7)) +#define AW8624_BIT_CONT_CTRL_ZC_DETEC_ENABLE (1<<7) +#define AW8624_BIT_CONT_CTRL_ZC_DETEC_DISABLE (0<<7) +#define AW8624_BIT_CONT_CTRL_WAIT_PERIOD_MASK (~(3<<5)) +#define AW8624_BIT_CONT_CTRL_WAIT_8PERIOD (3<<5) +#define AW8624_BIT_CONT_CTRL_WAIT_4PERIOD (2<<5) +#define AW8624_BIT_CONT_CTRL_WAIT_2PERIOD (1<<5) +#define AW8624_BIT_CONT_CTRL_WAIT_1PERIOD (0<<5) +#define AW8624_BIT_CONT_CTRL_MODE_MASK (~(1<<4)) +#define AW8624_BIT_CONT_CTRL_BY_DRV_TIME (1<<4) +#define AW8624_BIT_CONT_CTRL_BY_GO_SIGNAL (0<<4) +#define AW8624_BIT_CONT_CTRL_EN_CLOSE_MASK (~(1<<3)) +#define AW8624_BIT_CONT_CTRL_CLOSE_PLAYBACK (1<<3) +#define AW8624_BIT_CONT_CTRL_OPEN_PLAYBACK (0<<3) +#define AW8624_BIT_CONT_CTRL_F0_DETECT_MASK (~(1<<2)) +#define AW8624_BIT_CONT_CTRL_F0_DETECT_ENABLE (1<<2) +#define AW8624_BIT_CONT_CTRL_F0_DETECT_DISABLE (0<<2) +#define AW8624_BIT_CONT_CTRL_O2C_MASK (~(1<<1)) +#define AW8624_BIT_CONT_CTRL_O2C_ENABLE (1<<1) +#define AW8624_BIT_CONT_CTRL_O2C_DISABLE (0<<1) +#define AW8624_BIT_CONT_CTRL_AUTO_BRK_MASK (~(1<<0)) +#define AW8624_BIT_CONT_CTRL_AUTO_BRK_ENABLE (1<<0) +#define AW8624_BIT_CONT_CTRL_AUTO_BRK_DISABLE (0<<0) + +#define AW8624_BIT_D2SCFG_CLK_ADC_MASK (~(7<<5)) +#define AW8624_BIT_D2SCFG_CLK_ASC_1P5MHZ (3<<5) + +/* + * DETCTRL + */ +#define AW8624_BIT_DETCTRL_RL_OS_MASK (~(1<<6)) +#define AW8624_BIT_DETCTRL_RL_DETECT (1<<6) +#define AW8624_BIT_DETCTRL_OS_DETECT (0<<6) +#define AW8624_BIT_DETCTRL_PROTECT_MASK (~(1<<5)) +#define AW8624_BIT_DETCTRL_PROTECT_NO_ACTION (1<<5) +#define AW8624_BIT_DETCTRL_PROTECT_SHUTDOWN (0<<5) +#define AW8624_BIT_DETCTRL_VBAT_GO_MASK (~(1<<1)) +#define AW8624_BIT_DETCTRL_VABT_GO_ENABLE (1<<1) +#define AW8624_BIT_DETCTRL_VBAT_GO_DISBALE (0<<1) +#define AW8624_BIT_DETCTRL_DIAG_GO_MASK (~(1<<0)) +#define AW8624_BIT_DETCTRL_DIAG_GO_ENABLE (1<<0) +#define AW8624_BIT_DETCTRL_DIAG_GO_DISABLE (0<<0) + + +/* + * VBAT MODE + */ +#define AW8624_BIT_DETCTRL_VBAT_MODE_MASK (~(1<<6)) +#define AW8624_BIT_DETCTRL_VBAT_HW_COMP (1<<6) +#define AW8624_BIT_DETCTRL_VBAT_SW_COMP (0<<6) + + +/* + * ANACTRL + */ +#define AW8624_BIT_ANACTRL_LRA_SRC_MASK (~(1<<5)) +#define AW8624_BIT_ANACTRL_LRA_SRC_REG (1<<5) +#define AW8624_BIT_ANACTRL_LRA_SRC_EFUSE (0<<5) +#define AW8624_BIT_ANACTRL_EN_IO_PD1_MASK (~(1<<0)) +#define AW8624_BIT_ANACTRL_EN_IO_PD1_HIGH (1<<0) +#define AW8624_BIT_ANACTRL_EN_IO_PD1_LOW (0<<0) + +/* + *SW_BRAKE + */ +#define AW8624_BIT_EN_BRAKE_CONT_MASK (~(1<<3)) +#define AW8624_BIT_EN_BRAKE_CONT_ENABLE (1<<3) +#define AW8624_BIT_EN_BRAKE_CONT_DISABLE (0<<3) +#define AW8624_BIT_EN_BRAKE_RAM_MASK (~(1<<2)) +#define AW8624_BIT_EN_BRAKE_RAM_ENABLE (1<<2) +#define AW8624_BIT_EN_BRAKE_RAM_DISABLE (0<<2) +#define AW8624_BIT_EN_BRAKE_RTP_MASK (~(1<<1)) +#define AW8624_BIT_EN_BRAKE_RTP_ENABLE (1<<1) +#define AW8624_BIT_EN_BRAKE_RTP_DISABLE (0<<1) +#define AW8624_BIT_EN_BRAKE_TRIG_MASK (~(1<<0)) +#define AW8624_BIT_EN_BRAKE_TRIG_ENABLE (1<<0) +#define AW8624_BIT_EN_BRAKE_TRIG_DISABLE (0<<0) + +/* + *PRLVL + */ +#define AW8624_BIT_PRLVL_PR_EN_MASK (~(1<<7)) +#define AW8624_BIT_PRLVL_PR_ENABLE (1<<7) +#define AW8624_BIT_PRLVL_PR_DISABLE (0<<7) +#define AW8624_BIT_PRLVL_PRLVL_MASK (~(0x7f<<0)) + +/* + *PRTIME + */ +#define AW8624_BIT_PRTIME_PRTIME_MASK (~(0xff<<0)) + +#define AW8624_BIT_BEMF_NUM_BRK_MASK (~(0xf<<0)) + +/* + *TD_H 0x4b TD_brake + */ +#define AW8624_BIT_TDH_TD_BRAKE_MASK (~(0xF<<4)) +#define AW8624_BIT_R_SPARE_MASK (~(1<<7)) +#define AW8624_BIT_R_SPARE_ENABLE (1<<7) +#endif + diff --git a/drivers/misc/awinic/aw862x/Android.mk b/drivers/misc/awinic/aw862x/Android.mk new file mode 100644 index 000000000000..d1df194319ac --- /dev/null +++ b/drivers/misc/awinic/aw862x/Android.mk @@ -0,0 +1,12 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := aw862x.ko +LOCAL_MODULE_TAGS := optional +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/awinic/aw862x/Kbuild b/drivers/misc/awinic/aw862x/Kbuild new file mode 100644 index 000000000000..f993301e9010 --- /dev/null +++ b/drivers/misc/awinic/aw862x/Kbuild @@ -0,0 +1,12 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/misc/awinic/aw862x + +obj-m += aw862x.o + +aw862x-objs += haptic_nv.o +aw862x-objs += aw8622x.o +aw862x-objs += aw86214.o +aw862x-objs += aw8624.o + diff --git a/drivers/misc/awinic/aw862x/Makefile b/drivers/misc/awinic/aw862x/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/misc/awinic/aw862x/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/misc/awinic/aw862x/aw86214.c b/drivers/misc/awinic/aw862x/aw86214.c new file mode 100644 index 000000000000..43c16b408724 --- /dev/null +++ b/drivers/misc/awinic/aw862x/aw86214.c @@ -0,0 +1,3002 @@ +/* + * aw86214.c + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aw86214.h" +#include "haptic_nv.h" + +/****************************************************** + * + * Register Access + * + ******************************************************/ +const unsigned char aw86214_reg_list[] = { + AW86214_REG_ID, + AW86214_REG_SYSST, + AW86214_REG_SYSINT, + AW86214_REG_SYSINTM, + AW86214_REG_SYSST2, + AW86214_REG_PLAYCFG2, + AW86214_REG_PLAYCFG3, + AW86214_REG_PLAYCFG4, + AW86214_REG_WAVCFG1, + AW86214_REG_WAVCFG2, + AW86214_REG_WAVCFG3, + AW86214_REG_WAVCFG4, + AW86214_REG_WAVCFG5, + AW86214_REG_WAVCFG6, + AW86214_REG_WAVCFG7, + AW86214_REG_WAVCFG8, + AW86214_REG_WAVCFG9, + AW86214_REG_WAVCFG10, + AW86214_REG_WAVCFG11, + AW86214_REG_WAVCFG12, + AW86214_REG_WAVCFG13, + AW86214_REG_CONTCFG1, + AW86214_REG_CONTCFG2, + AW86214_REG_CONTCFG3, + AW86214_REG_CONTCFG4, + AW86214_REG_CONTCFG5, + AW86214_REG_CONTCFG6, + AW86214_REG_CONTCFG7, + AW86214_REG_CONTCFG8, + AW86214_REG_CONTCFG9, + AW86214_REG_CONTCFG10, + AW86214_REG_CONTCFG11, + AW86214_REG_CONTCFG13, + AW86214_REG_CONTRD14, + AW86214_REG_CONTRD15, + AW86214_REG_CONTRD16, + AW86214_REG_CONTRD17, + AW86214_REG_RTPCFG1, + AW86214_REG_RTPCFG2, + AW86214_REG_RTPCFG3, + AW86214_REG_RTPCFG4, + AW86214_REG_RTPCFG5, + AW86214_REG_TRGCFG1, + AW86214_REG_TRGCFG4, + AW86214_REG_TRGCFG7, + AW86214_REG_TRGCFG8, + AW86214_REG_GLBCFG4, + AW86214_REG_GLBRD5, + AW86214_REG_RAMADDRH, + AW86214_REG_RAMADDRL, + AW86214_REG_SYSCTRL1, + AW86214_REG_SYSCTRL2, + AW86214_REG_SYSCTRL3, + AW86214_REG_SYSCTRL4, + AW86214_REG_SYSCTRL5, + AW86214_REG_SYSCTRL6, + AW86214_REG_SYSCTRL7, + AW86214_REG_PWMCFG1, + AW86214_REG_PWMCFG3, + AW86214_REG_PWMCFG4, + AW86214_REG_DETCFG1, + AW86214_REG_DETCFG2, + AW86214_REG_DET_RL, + AW86214_REG_DET_VBAT, + AW86214_REG_DET_LO, + AW86214_REG_TRIMCFG1, + AW86214_REG_TRIMCFG3, + AW86214_REG_ANACFG8, +}; + +/****************************************************** + * + * value + * + ******************************************************/ +static char *aw86214_ram_name = "aw86214_haptic.bin"; + +/****************************************************** + * + * functions declaration + * + ******************************************************/ +static int aw86214_analyse_duration_range(struct aw86214 *aw86214); + + /****************************************************** + * + * aw86214 i2c write/read + * + ******************************************************/ +static int aw86214_i2c_read(struct aw86214 *aw86214, unsigned char reg_addr, + unsigned char *buf, unsigned int len) +{ + int ret = 0; + + struct i2c_msg msg[2] = { + [0] = { + .addr = aw86214->i2c->addr, + .flags = 0, + .len = sizeof(uint8_t), + .buf = ®_addr, + }, + [1] = { + .addr = aw86214->i2c->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + }, + }; + + ret = i2c_transfer(aw86214->i2c->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) { + aw_dev_err("%s: i2c_transfer failed\n", __func__); + return ret; + } else if (ret != AW_I2C_READ_MSG_NUM) { + aw_dev_err("%s: transfer failed(size error)\n", __func__); + return -ENXIO; + } + return ret; +} + +static int aw86214_i2c_write(struct aw86214 *aw86214, unsigned char reg_addr, + unsigned char *buf, unsigned int len) +{ + unsigned char *data = NULL; + int ret = -1; + + data = kmalloc(len + 1, GFP_KERNEL); + data[0] = reg_addr; + memcpy(&data[1], buf, len); + ret = i2c_master_send(aw86214->i2c, data, len + 1); + if (ret < 0) + aw_dev_err("%s: i2c master send 0x%02x error\n", + __func__, reg_addr); + kfree(data); + return ret; +} + +static int +aw86214_i2c_write_bits(struct aw86214 *aw86214, unsigned char reg_addr, + unsigned int mask, + unsigned char reg_data) +{ + unsigned char reg_val = 0; + int ret = -1; + + ret = aw86214_i2c_read(aw86214, reg_addr, ®_val, AW_I2C_BYTE_ONE); + if (ret < 0) { + aw_dev_err("%s: i2c read error, ret=%d\n", __func__, ret); + return ret; + } + reg_val &= mask; + reg_val |= reg_data; + ret = aw86214_i2c_write(aw86214, reg_addr, ®_val, AW_I2C_BYTE_ONE); + if (ret < 0) { + aw_dev_err("%s: i2c write error, ret=%d\n", __func__, ret); + return ret; + } + return 0; +} + +static int aw86214_parse_data(struct aw86214 *aw86214, const char *buf) +{ + unsigned char reg_num = aw86214->aw_i2c_package.reg_num; + unsigned char i = 0; + const char *temp_buf = NULL; + char data_buf[AWRW_CMD_UNIT] = { 0 }; + unsigned int value = 0; + int ret = 0; + int len = strlen(buf) - (AWRW_CMD_UNIT * 3); + + if (len < 0) { + aw_dev_err("%s: parse data error\n", __func__); + return -ERANGE; + } + temp_buf = &buf[AWRW_CMD_UNIT * 3]; + for (i = 0; i < reg_num; i++) { + if (((i + 1) * AWRW_CMD_UNIT) > len) { + aw_dev_err("%s: parse data error\n", __func__); + return -ERANGE; + } + memcpy(data_buf, &temp_buf[i * AWRW_CMD_UNIT], 4); + data_buf[4] = '\0'; + ret = kstrtouint(data_buf, 0, &value); + if (ret < 0) { + aw_dev_err("%s: kstrtouint error\n", __func__); + return ret; + } + aw86214->aw_i2c_package.reg_data[i] = (unsigned char)value; + } + return 0; +} + +irqreturn_t aw86214_irq(int irq, void *data) +{ + struct aw86214 *aw86214 = data; + unsigned char reg_val = 0; + + aw_dev_info("%s enter\n", __func__); + aw86214_i2c_read(aw86214, AW86214_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_dev_info("%s: reg SYSINT=0x%02X\n", __func__, reg_val); + if (reg_val & AW86214_BIT_SYSINT_BST_OVPI) + aw_dev_err("%s chip ov int error\n", __func__); + if (reg_val & AW86214_BIT_SYSINT_UVLI) + aw_dev_err("%s chip uvlo int error\n", __func__); + if (reg_val & AW86214_BIT_SYSINT_OCDI) + aw_dev_err("%s chip over current int error\n", + __func__); + if (reg_val & AW86214_BIT_SYSINT_OTI) + aw_dev_err("%s chip over temperature int error\n", + __func__); + if (reg_val & AW86214_BIT_SYSINT_DONEI) + aw_dev_info("%s chip playback done\n", __func__); + + aw_dev_info("%s exit\n", __func__); + + return IRQ_HANDLED; +} + +static int aw86214_analyse_duration_range(struct aw86214 *aw86214) +{ + int i = 0; + int ret = 0; + int len = 0; + int *duration_time = NULL; + + len = ARRAY_SIZE(aw86214->dts_info.duration_time); + duration_time = aw86214->dts_info.duration_time; + if (len < 2) { + aw_dev_err("%s: duration time range error\n", __func__); + return -ERANGE; + } + for (i = (len - 1); i > 0; i--) { + if (duration_time[i] > duration_time[i-1]) + continue; + else + break; + + } + if (i > 0) { + aw_dev_err("%s: duration time range error\n", __func__); + ret = -ERANGE; + } + return ret; +} + +static int +aw86214_analyse_duration_array_size(struct aw86214 *aw86214, + struct device_node *np) +{ + int ret = 0; + + ret = of_property_count_elems_of_size(np, "aw86214_vib_duration_time", + 4); + if (ret < 0) { + aw86214->duration_time_flag = -1; + aw_dev_info("%s vib_duration_time not found\n", __func__); + return ret; + } + aw86214->duration_time_size = ret; + if (aw86214->duration_time_size > 3) { + aw86214->duration_time_flag = -1; + aw_dev_info("%s vib_duration_time error, array size = %d\n", + __func__, aw86214->duration_time_size); + return -ERANGE; + } + return 0; +} + +/***************************************************** + * + * device tree + * + *****************************************************/ +int aw86214_parse_dt(struct aw86214 *aw86214, struct device *dev, + struct device_node *np) +{ + unsigned int val = 0; + unsigned int prctmode_temp[3]; + unsigned int sine_array_temp[4] = { 0x05, 0xB2, 0xFF, 0xEF }; + unsigned int duration_time[3]; + int ret = 0; + + val = of_property_read_u32(np, "aw86214_vib_lk_f0_cali", + &aw86214->dts_info.lk_f0_cali); + if (val != 0) + aw_dev_info("aw86214_vib_mode not found\n"); + aw_dev_info("%s: aw86214_vib_lk_f0_cali = 0x%02x\n", + __func__, aw86214->dts_info.lk_f0_cali); + val = of_property_read_u32(np, "aw86214_vib_mode", + &aw86214->dts_info.mode); + if (val != 0) + aw_dev_info("%s vib_mode not found\n", __func__); + val = of_property_read_u32(np, "aw86214_vib_f0_ref", + &aw86214->dts_info.f0_ref); + if (val != 0) + aw_dev_info("%s vib_f0_ref not found\n", __func__); + val = + of_property_read_u32(np, "aw86214_vib_f0_cali_percent", + &aw86214->dts_info.f0_cali_percent); + if (val != 0) + aw_dev_info("%s vib_f0_cali_percent not found\n", __func__); + val = of_property_read_u32(np, "aw86214_vib_cont_drv1_lvl", + &aw86214->dts_info.cont_drv1_lvl_dt); + if (val != 0) + aw_dev_info("%s vib_cont_drv1_lvl not found\n", __func__); + val = + of_property_read_u32(np, "aw86214_vib_cont_drv2_lvl", + &aw86214->dts_info.cont_drv2_lvl_dt); + if (val != 0) + aw_dev_info("%s vib_cont_drv2_lvl not found\n", __func__); + val = + of_property_read_u32(np, "aw86214_vib_cont_drv1_time", + &aw86214->dts_info.cont_drv1_time_dt); + if (val != 0) + aw_dev_info("%s vib_cont_drv1_time not found\n", __func__); + val = + of_property_read_u32(np, "aw86214_vib_cont_drv2_time", + &aw86214->dts_info.cont_drv2_time_dt); + if (val != 0) + aw_dev_info("%s vib_cont_drv2_time not found\n", __func__); + val = + of_property_read_u32(np, "aw86214_vib_cont_drv_width", + &aw86214->dts_info.cont_drv_width); + if (val != 0) + aw_dev_info("%s vib_cont_drv_width not found\n", __func__); + val = + of_property_read_u32(np, "aw86214_vib_cont_brk_time", + &aw86214->dts_info.cont_brk_time_dt); + if (val != 0) + aw_dev_info("%s vib_cont_brk_time_dt not found\n", __func__); + val = + of_property_read_u32(np, "aw86214_vib_cont_wait_num", + &aw86214->dts_info.cont_wait_num_dt); + if (val != 0) + aw_dev_info("%s vib_cont_wait_num not found\n", __func__); + val = + of_property_read_u32(np, "aw86214_vib_cont_tset", + &aw86214->dts_info.cont_tset); + if (val != 0) + aw_dev_info("%s vib_cont_tset not found\n", __func__); + val = + of_property_read_u32(np, "aw86214_vib_cont_bemf_set", + &aw86214->dts_info.cont_bemf_set); + if (val != 0) + aw_dev_info("%s vib_cont_bemf_set not found\n", __func__); + val = + of_property_read_u32(np, "aw86214_vib_d2s_gain", + &aw86214->dts_info.d2s_gain); + if (val != 0) + aw_dev_info("%s vib_d2s_gain not found\n", __func__); + val = + of_property_read_u32(np, "aw86214_vib_cont_track_margin", + &aw86214->dts_info.cont_track_margin); + if (val != 0) + aw_dev_info("%s vib_cont_track_margin not found\n", __func__); + val = of_property_read_u32_array(np, "aw86214_vib_prctmode", + prctmode_temp, + ARRAY_SIZE(prctmode_temp)); + if (val != 0) + aw_dev_info("%s vib_prctmode not found\n", __func__); + memcpy(aw86214->dts_info.prctmode, prctmode_temp, + sizeof(prctmode_temp)); + val = + of_property_read_u32_array(np, + "aw86214_vib_sine_array", + sine_array_temp, + ARRAY_SIZE(sine_array_temp)); + if (val != 0) + aw_dev_info("%s vib_sine_array not found\n", __func__); + memcpy(aw86214->dts_info.sine_array, sine_array_temp, + sizeof(sine_array_temp)); + val = of_property_read_u32_array(np, "aw86214_vib_duration_time", + duration_time, ARRAY_SIZE(duration_time)); + if (val != 0) + aw_dev_info("%s vib_duration_time not found\n", __func__); + ret = aw86214_analyse_duration_array_size(aw86214, np); + if (!ret) + memcpy(aw86214->dts_info.duration_time, + duration_time, sizeof(duration_time)); + return 0; +} + +static void aw86214_haptic_upload_lra(struct aw86214 *aw86214, + unsigned int flag) +{ + switch (flag) { + case AW86214_WRITE_ZERO: + aw_dev_info("%s write zero to trim_lra!\n", __func__); + aw86214_i2c_write_bits(aw86214, AW86214_REG_TRIMCFG3, + AW86214_BIT_TRIMCFG3_TRIM_LRA_MASK, + 0x00); + break; + case AW86214_F0_CALI: + aw_dev_info("%s write f0_cali_data to trim_lra = 0x%02X\n", + __func__, aw86214->f0_cali_data); + aw86214_i2c_write_bits(aw86214, AW86214_REG_TRIMCFG3, + AW86214_BIT_TRIMCFG3_TRIM_LRA_MASK, + (char)aw86214->f0_cali_data); + break; + default: + break; + } +} + +static void aw86214_brk_en(struct aw86214 *aw86214, bool isEnable) +{ + unsigned char reg_val = 0; + + if (isEnable == false) + reg_val = 0x00; + else + reg_val = (unsigned char)aw86214->dts_info.cont_brk_time_dt; + + aw86214_i2c_write(aw86214, AW86214_REG_CONTCFG10, ®_val, + AW_I2C_BYTE_ONE); + +} + +static void aw86214_ver_a_init(struct aw86214 *aw86214) +{ + unsigned char reg_val = 0; + + aw86214_i2c_write_bits(aw86214, AW86214_REG_RTPCFG1, + (AW86214_BIT_RTPCFG1_SRAM_SIZE_2K_MASK & + AW86214_BIT_RTPCFG1_SRAM_SIZE_1K_MASK), + (AW86214_BIT_RTPCFG1_SRAM_SIZE_2K_DIS | + AW86214_BIT_RTPCFG1_SRAM_SIZE_1K_EN)); + aw86214_i2c_write_bits(aw86214, AW86214_REG_PLAYCFG3, + AW86214_BIT_PLAYCFG3_BRK_EN_MASK, + AW86214_BIT_PLAYCFG3_BRK_ENABLE); + reg_val = 0x00; + aw86214_i2c_write(aw86214, AW86214_REG_CONTCFG10, ®_val, + AW_I2C_BYTE_ONE); +} + +static int aw86214_haptic_stop(struct aw86214 *aw86214) +{ + unsigned char cnt = 40; + unsigned char reg_val = 0; + bool force_flag = true; + + aw_dev_info("%s enter\n", __func__); + aw86214->play_mode = AW86214_HAPTIC_STANDBY_MODE; + reg_val = AW86214_BIT_PLAYCFG4_STOP_ON; + aw86214_i2c_write(aw86214, AW86214_REG_PLAYCFG4, ®_val, + AW_I2C_BYTE_ONE); + while (cnt) { + aw86214_i2c_read(aw86214, AW86214_REG_GLBRD5, ®_val, + AW_I2C_BYTE_ONE); + if ((reg_val & 0x0f) == 0x00 + || (reg_val & 0x0f) == 0x0A) { + cnt = 0; + force_flag = false; + aw_dev_info("%s entered standby! glb_state=0x%02X\n", + __func__, reg_val); + } else { + cnt--; + pr_debug + ("%s wait for standby, glb_state=0x%02X\n", + __func__, reg_val); + } + usleep_range(AW86214_STOP_DELAY_MIN, AW86214_STOP_DELAY_MAX); + } + if (force_flag) { + aw_dev_err("%s force to enter standby mode!\n", __func__); + aw86214_i2c_write_bits(aw86214, AW86214_REG_SYSCTRL2, + AW86214_BIT_SYSCTRL2_STANDBY_MASK, + AW86214_BIT_SYSCTRL2_STANDBY_ON); + } + return 0; +} + +static void aw86214_haptic_raminit(struct aw86214 *aw86214, bool flag) +{ + if (flag) { + aw86214_i2c_write_bits(aw86214, AW86214_REG_SYSCTRL1, + AW86214_BIT_SYSCTRL1_RAMINIT_MASK, + AW86214_BIT_SYSCTRL1_RAMINIT_ON); + } else { + aw86214_i2c_write_bits(aw86214, AW86214_REG_SYSCTRL1, + AW86214_BIT_SYSCTRL1_RAMINIT_MASK, + AW86214_BIT_SYSCTRL1_RAMINIT_OFF); + } +} + +static int aw86214_haptic_get_vbat(struct aw86214 *aw86214) +{ + unsigned char reg_val = 0; + unsigned int vbat_code = 0; + + aw86214_haptic_stop(aw86214); + aw86214_haptic_raminit(aw86214, true); + aw86214_i2c_write_bits(aw86214, AW86214_REG_DETCFG2, + AW86214_BIT_DETCFG2_VBAT_GO_MASK, + AW86214_BIT_DETCFG2_VABT_GO_ON); + usleep_range(AW86214_VBAT_DELAY_MIN, AW86214_VBAT_DELAY_MAX); + aw86214_i2c_read(aw86214, AW86214_REG_DET_VBAT, ®_val, + AW_I2C_BYTE_ONE); + vbat_code = (vbat_code | reg_val) << 2; + aw86214_i2c_read(aw86214, AW86214_REG_DET_LO, ®_val, + AW_I2C_BYTE_ONE); + vbat_code = vbat_code | ((reg_val & AW86214_BIT_DET_LO_VBAT) >> 4); + aw86214->vbat = AW86214_VBAT_FORMULA(vbat_code); + if (aw86214->vbat > AW86214_VBAT_MAX) { + aw86214->vbat = AW86214_VBAT_MAX; + aw_dev_info("%s vbat max limit = %dmV\n", __func__, + aw86214->vbat); + } + if (aw86214->vbat < AW86214_VBAT_MIN) { + aw86214->vbat = AW86214_VBAT_MIN; + aw_dev_info("%s vbat min limit = %dmV\n", __func__, + aw86214->vbat); + } + aw_dev_info("%s aw86214->vbat=%dmV, vbat_code=0x%02X\n", __func__, + aw86214->vbat, vbat_code); + aw86214_haptic_raminit(aw86214, false); + return 0; +} +static int aw86214_haptic_set_gain(struct aw86214 *aw86214, unsigned char gain) +{ + aw86214_i2c_write(aw86214, AW86214_REG_PLAYCFG2, &gain, + AW_I2C_BYTE_ONE); + return 0; +} + +static int aw86214_haptic_ram_vbat_comp(struct aw86214 *aw86214, + bool flag) +{ + int temp_gain = 0; + + if (flag) { + if (aw86214->ram_vbat_compensate == + AW86214_HAPTIC_RAM_VBAT_COMP_ENABLE) { + aw86214_haptic_get_vbat(aw86214); + temp_gain = + aw86214->gain * AW86214_VBAT_REFER / aw86214->vbat; + if (temp_gain > + (128 * AW86214_VBAT_REFER / AW86214_VBAT_MIN)) { + temp_gain = + 128 * AW86214_VBAT_REFER / AW86214_VBAT_MIN; + aw_dev_dbg("%s gain limit=%d\n", __func__, + temp_gain); + } + aw86214_haptic_set_gain(aw86214, temp_gain); + } else { + aw86214_haptic_set_gain(aw86214, aw86214->gain); + } + } else { + aw86214_haptic_set_gain(aw86214, aw86214->gain); + } + return 0; +} + +static int aw86214_haptic_play_mode(struct aw86214 *aw86214, + unsigned char play_mode) +{ + aw_dev_info("%s enter\n", __func__); + + switch (play_mode) { + case AW86214_HAPTIC_STANDBY_MODE: + aw_dev_info("%s: enter standby mode\n", __func__); + aw86214->play_mode = AW86214_HAPTIC_STANDBY_MODE; + aw86214_haptic_stop(aw86214); + break; + case AW86214_HAPTIC_RAM_MODE: + aw_dev_info("%s: enter ram mode\n", __func__); + aw86214->play_mode = AW86214_HAPTIC_RAM_MODE; + aw86214_i2c_write_bits(aw86214, AW86214_REG_PLAYCFG3, + AW86214_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW86214_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + case AW86214_HAPTIC_RAM_LOOP_MODE: + aw_dev_info("%s: enter ram loop mode\n", __func__); + aw86214->play_mode = AW86214_HAPTIC_RAM_LOOP_MODE; + aw86214_i2c_write_bits(aw86214, AW86214_REG_PLAYCFG3, + AW86214_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW86214_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + case AW86214_HAPTIC_CONT_MODE: + aw_dev_info("%s: enter cont mode\n", __func__); + aw86214->play_mode = AW86214_HAPTIC_CONT_MODE; + aw86214_i2c_write_bits(aw86214, AW86214_REG_PLAYCFG3, + AW86214_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW86214_BIT_PLAYCFG3_PLAY_MODE_CONT); + break; + default: + aw_dev_err("%s: play mode %d error", __func__, play_mode); + break; + } + return 0; +} + +static void aw86214_haptic_play_go(struct aw86214 *aw86214) +{ + unsigned char reg_val = AW86214_BIT_PLAYCFG4_GO_ON; + + aw86214_i2c_write(aw86214, AW86214_REG_PLAYCFG4, ®_val, + AW_I2C_BYTE_ONE); +} + +static int aw86214_haptic_set_wav_seq(struct aw86214 *aw86214, + unsigned char wav, unsigned char seq) +{ + aw86214_i2c_write(aw86214, AW86214_REG_WAVCFG1 + wav, &seq, + AW_I2C_BYTE_ONE); + return 0; +} + +static int aw86214_haptic_set_wav_loop(struct aw86214 *aw86214, + unsigned char wav, unsigned char loop) +{ + unsigned char tmp = 0; + + if (wav % 2) { + tmp = loop << 0; + aw86214_i2c_write_bits(aw86214, AW86214_REG_WAVCFG9 + (wav / 2), + AW86214_BIT_WAVLOOP_SEQ_EVEN_MASK, tmp); + } else { + tmp = loop << 4; + aw86214_i2c_write_bits(aw86214, AW86214_REG_WAVCFG9 + (wav / 2), + AW86214_BIT_WAVLOOP_SEQ_ODD_MASK, tmp); + } + return 0; +} + +/***************************************************** + * + * haptic f0 cali + * + *****************************************************/ +static int aw86214_haptic_read_lra_f0(struct aw86214 *aw86214) +{ + unsigned char reg_val[2] = {0}; + unsigned int f0_reg = 0; + unsigned long f0_tmp = 0; + + aw_dev_info("%s enter\n", __func__); + /* F_LRA_F0 */ + aw86214_i2c_read(aw86214, AW86214_REG_CONTRD14, reg_val, + AW_I2C_BYTE_TWO); + f0_reg = (f0_reg | reg_val[0]) << 8; + f0_reg |= (reg_val[1] << 0); + if (!f0_reg) { + aw_dev_err("%s didn't get lra f0 because f0_reg value is 0!\n", + __func__); + aw86214->f0 = aw86214->dts_info.f0_ref; + return -ERANGE; + } + f0_tmp = 384000 * 10 / f0_reg; + aw86214->f0 = (unsigned int)f0_tmp; + aw_dev_info("%s lra_f0=%d\n", __func__, aw86214->f0); + return 0; +} + +static int aw86214_haptic_read_cont_f0(struct aw86214 *aw86214) +{ + unsigned char reg_val[2] = {0}; + unsigned int f0_reg = 0; + unsigned long f0_tmp = 0; + + aw_dev_info("%s enter\n", __func__); + aw86214_i2c_read(aw86214, AW86214_REG_CONTRD16, reg_val, + AW_I2C_BYTE_TWO); + f0_reg = (f0_reg | reg_val[0]) << 8; + f0_reg |= (reg_val[1] << 0); + if (!f0_reg) { + aw_dev_err("%s didn't get cont f0 because f0_reg value is 0!\n", + __func__); + aw86214->cont_f0 = aw86214->dts_info.f0_ref; + return -ERANGE; + } + f0_tmp = 384000 * 10 / f0_reg; + aw86214->cont_f0 = (unsigned int)f0_tmp; + aw_dev_info("%s cont_f0=%d\n", __func__, aw86214->cont_f0); + return 0; +} + +static int aw86214_haptic_cont_get_f0(struct aw86214 *aw86214) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned char reg_array[3] = {0}; + unsigned int cnt = 200; + bool get_f0_flag = false; + unsigned char brk_en_temp = 0; + + aw_dev_info("%s enter\n", __func__); + aw86214->f0 = aw86214->dts_info.f0_ref; + /* enter standby mode */ + aw86214_haptic_stop(aw86214); + /* f0 calibrate work mode */ + aw86214_haptic_play_mode(aw86214, AW86214_HAPTIC_CONT_MODE); + /* enable f0 detect */ + aw86214_i2c_write_bits(aw86214, AW86214_REG_CONTCFG1, + AW86214_BIT_CONTCFG1_EN_F0_DET_MASK, + AW86214_BIT_CONTCFG1_F0_DET_ENABLE); + /* cont config */ + aw86214_i2c_write_bits(aw86214, AW86214_REG_CONTCFG6, + AW86214_BIT_CONTCFG6_TRACK_EN_MASK, + AW86214_BIT_CONTCFG6_TRACK_ENABLE); + /* enable auto brake */ + aw86214_i2c_read(aw86214, AW86214_REG_PLAYCFG3, ®_val, + AW_I2C_BYTE_ONE); + brk_en_temp = AW86214_BIT_PLAYCFG3_BRK & reg_val; + aw86214_i2c_write_bits(aw86214, AW86214_REG_PLAYCFG3, + AW86214_BIT_PLAYCFG3_BRK_EN_MASK, + AW86214_BIT_PLAYCFG3_BRK_ENABLE); + /* f0 driver level */ + aw86214_i2c_write_bits(aw86214, AW86214_REG_CONTCFG6, + AW86214_BIT_CONTCFG6_DRV1_LVL_MASK, + aw86214->dts_info.cont_drv1_lvl_dt); + reg_array[0] = (unsigned char)aw86214->dts_info.cont_drv2_lvl_dt; + reg_array[1] = (unsigned char)aw86214->dts_info.cont_drv1_time_dt; + reg_array[2] = (unsigned char)aw86214->dts_info.cont_drv2_time_dt; + aw86214_i2c_write(aw86214, AW86214_REG_CONTCFG7, reg_array, + AW_I2C_BYTE_THREE); + /* TRACK_MARGIN */ + if (!aw86214->dts_info.cont_track_margin) { + aw_dev_err("%s aw86214->dts_info.cont_track_margin = 0!\n", + __func__); + } else { + reg_val = (unsigned char)aw86214->dts_info.cont_track_margin; + aw86214_i2c_write(aw86214, AW86214_REG_CONTCFG11, ®_val, + AW_I2C_BYTE_ONE); + } + /* cont play go */ + aw86214_haptic_play_go(aw86214); + /* 300ms */ + while (cnt) { + aw86214_i2c_read(aw86214, AW86214_REG_GLBRD5, ®_val, + AW_I2C_BYTE_ONE); + if ((reg_val & AW86214_BIT_GLBRD5_STATE) == + AW86214_BIT_GLBRD5_STATE_STANDBY) { + cnt = 0; + get_f0_flag = true; + aw_dev_info("%s entered standby mode! glb_state=0x%02X\n", + __func__, reg_val); + } else { + cnt--; + aw_dev_info("%s waitting for standby, glb_state=0x%02X\n", + __func__, reg_val); + } + usleep_range(AW86214_F0_DELAY_MIN, AW86214_F0_DELAY_MAX); + } + if (get_f0_flag) { + aw86214_haptic_read_lra_f0(aw86214); + aw86214_haptic_read_cont_f0(aw86214); + } else { + aw_dev_err("%s enter standby mode failed, stop reading f0!\n", + __func__); + } + /* restore default config */ + aw86214_i2c_write_bits(aw86214, AW86214_REG_CONTCFG1, + AW86214_BIT_CONTCFG1_EN_F0_DET_MASK, + AW86214_BIT_CONTCFG1_F0_DET_DISABLE); + /* recover auto break config */ + aw86214_i2c_write_bits(aw86214, AW86214_REG_PLAYCFG3, + AW86214_BIT_PLAYCFG3_BRK_EN_MASK, + brk_en_temp); + return ret; +} + +static int aw86214_haptic_set_repeat_wav_seq(struct aw86214 *aw86214, + unsigned char seq) +{ + aw86214_haptic_set_wav_seq(aw86214, 0x00, seq); + aw86214_haptic_set_wav_seq(aw86214, 0x01, 0x00); + aw86214_haptic_set_wav_loop(aw86214, 0x00, + AW86214_BIT_WAVLOOP_INIFINITELY); + return 0; +} + +static int aw86214_haptic_get_lra_resistance(struct aw86214 *aw86214) +{ + unsigned char reg_val = 0; + unsigned int lra_code = 0; + unsigned int lra = 0; + unsigned char d2s_gain_temp = 0; + + mutex_lock(&aw86214->lock); + aw86214_haptic_stop(aw86214); + aw86214_i2c_read(aw86214, AW86214_REG_SYSCTRL7, ®_val, + AW_I2C_BYTE_ONE); + d2s_gain_temp = 0x07 & reg_val; + aw86214_i2c_write_bits(aw86214, AW86214_REG_SYSCTRL7, + AW86214_BIT_SYSCTRL7_D2S_GAIN_MASK, + aw86214->dts_info.d2s_gain); + aw86214_haptic_raminit(aw86214, true); + /* enter standby mode */ + aw86214_haptic_stop(aw86214); + usleep_range(AW86214_STOP_DELAY_MIN, AW86214_STOP_DELAY_MAX); + aw86214_i2c_write_bits(aw86214, AW86214_REG_SYSCTRL2, + AW86214_BIT_SYSCTRL2_STANDBY_MASK, + AW86214_BIT_SYSCTRL2_STANDBY_OFF); + aw86214_i2c_write_bits(aw86214, AW86214_REG_DETCFG1, + AW86214_BIT_DETCFG1_RL_OS_MASK, + AW86214_BIT_DETCFG1_RL); + aw86214_i2c_write_bits(aw86214, AW86214_REG_DETCFG2, + AW86214_BIT_DETCFG2_DIAG_GO_MASK, + AW86214_BIT_DETCFG2_DIAG_GO_ON); + usleep_range(AW86214_LRA_DELAY_MIN, AW86214_LRA_DELAY_MAX); + aw86214_i2c_read(aw86214, AW86214_REG_DET_RL, ®_val, + AW_I2C_BYTE_ONE); + lra_code = (lra_code | reg_val) << 2; + aw86214_i2c_read(aw86214, AW86214_REG_DET_LO, ®_val, + AW_I2C_BYTE_ONE); + lra_code = lra_code | (reg_val & AW86214_BIT_DET_LO_RL); + /* 2num */ + lra = AW86214_LRA_FORMULA(lra_code); + /* Keep up with aw8624 driver */ + aw86214->lra = lra * 10; + aw86214_haptic_raminit(aw86214, false); + aw86214_i2c_write_bits(aw86214, AW86214_REG_SYSCTRL7, + AW86214_BIT_SYSCTRL7_D2S_GAIN_MASK, + d2s_gain_temp); + mutex_unlock(&aw86214->lock); + return 0; +} + +static void aw86214_set_ram_addr(struct aw86214 *aw86214) +{ + unsigned char ram_addr[2] = {0}; + + ram_addr[0] = (unsigned char)(aw86214->ram.base_addr >> 8); + ram_addr[1] = (unsigned char)(aw86214->ram.base_addr & 0x00ff); + + aw86214_i2c_write(aw86214, AW86214_REG_RAMADDRH, ram_addr, + AW_I2C_BYTE_TWO); +} + +#ifdef AW_CHECK_RAM_DATA +static int aw86214_check_ram_data(struct aw86214 *aw86214, + unsigned char *cont_data, + unsigned char *ram_data, unsigned int len) +{ + int i = 0; + + for (i = 0; i < len; i++) { + if (ram_data[i] != cont_data[i]) { + aw_dev_err("%s: check ramdata error, addr=0x%04x, ram_data=0x%02x, file_data=0x%02x\n", + __func__, i, ram_data[i], cont_data[i]); + return -ERANGE; + } + } + return 0; +} +#endif + +static int aw86214_container_update(struct aw86214 *aw86214, + struct aw86214_container *aw86214_cont) +{ + unsigned int shift = 0; + int i = 0; + int ret = 0; + int len = 0; +#ifdef AW_CHECK_RAM_DATA + unsigned char ram_data[AW_RAMDATA_RD_BUFFER_SIZE] = {0}; +#endif + + aw_dev_info("%s enter\n", __func__); + mutex_lock(&aw86214->lock); + aw86214->ram.baseaddr_shift = 2; + aw86214->ram.ram_shift = 4; + /* RAMINIT Enable */ + aw86214_haptic_raminit(aw86214, true); + /* Enter standby mode */ + aw86214_haptic_stop(aw86214); + /* set base addr */ + shift = aw86214->ram.baseaddr_shift; + aw86214->ram.base_addr = + (unsigned int)((aw86214_cont->data[0 + shift] << 8) | + (aw86214_cont->data[1 + shift])); + aw86214_i2c_write_bits(aw86214, AW86214_REG_RTPCFG1, /*ADDRH*/ + AW86214_BIT_RTPCFG1_ADDRH_MASK, + aw86214_cont->data[0 + shift]); + + aw86214_i2c_write(aw86214, AW86214_REG_RTPCFG2, /*ADDRL*/ + &aw86214_cont->data[1 + shift], + AW_I2C_BYTE_ONE); + + /* ram */ + aw86214_set_ram_addr(aw86214); + i = aw86214->ram.ram_shift; + while (i < aw86214_cont->len) { + if ((aw86214_cont->len - i) < AW_RAMDATA_WR_BUFFER_SIZE) + len = aw86214_cont->len - i; + else + len = AW_RAMDATA_WR_BUFFER_SIZE; + + aw86214_i2c_write(aw86214, AW86214_REG_RAMDATA, + &aw86214_cont->data[i], len); + i += len; + } + +#ifdef AW_CHECK_RAM_DATA + aw86214_set_ram_addr(aw86214); + i = aw86214->ram.ram_shift; + while (i < aw86214_cont->len) { + if ((aw86214_cont->len - i) < AW_RAMDATA_RD_BUFFER_SIZE) + len = aw86214_cont->len - i; + else + len = AW_RAMDATA_RD_BUFFER_SIZE; + + aw86214_i2c_read(aw86214, AW86214_REG_RAMDATA, ram_data, len); + ret = aw86214_check_ram_data(aw86214, &aw86214_cont->data[i], + ram_data, len); + if (ret < 0) + break; + i += len; + } + if (ret) + aw_dev_err("%s: ram data check sum error\n", __func__); + else + aw_dev_info("%s: ram data check sum pass\n", __func__); + +#endif + /* RAMINIT Disable */ + aw86214_haptic_raminit(aw86214, false); + mutex_unlock(&aw86214->lock); + aw_dev_info("%s exit\n", __func__); + return ret; +} + +static int aw86214_haptic_ram_config(struct aw86214 *aw86214, int duration) +{ + int ret = 0; + + if (aw86214->duration_time_flag < 0) { + aw_dev_err("%s: duration time error, array size = %d\n", + __func__, aw86214->duration_time_size); + return -ERANGE; + } + ret = aw86214_analyse_duration_range(aw86214); + if (ret < 0) + return ret; + + if ((duration > 0) && (duration < + aw86214->dts_info.duration_time[0])) { + aw86214->index = 3; /*3*/ + aw86214->activate_mode = AW86214_HAPTIC_RAM_MODE; + } else if ((duration >= aw86214->dts_info.duration_time[0]) && + (duration < aw86214->dts_info.duration_time[1])) { + aw86214->index = 2; /*2*/ + aw86214->activate_mode = AW86214_HAPTIC_RAM_MODE; + } else if ((duration >= aw86214->dts_info.duration_time[1]) && + (duration < aw86214->dts_info.duration_time[2])) { + aw86214->index = 1; /*1*/ + aw86214->activate_mode = AW86214_HAPTIC_RAM_MODE; + } else if (duration >= aw86214->dts_info.duration_time[2]) { + aw86214->index = 4; /*4*/ + aw86214->activate_mode = AW86214_HAPTIC_RAM_LOOP_MODE; + } else { + aw_dev_err("%s: duration time error, duration= %d\n", + __func__, duration); + aw86214->index = 0; + aw86214->activate_mode = AW86214_HAPTIC_NULL; + ret = -ERANGE; + } + + return ret; +} + +static int aw86214_haptic_get_ram_number(struct aw86214 *aw86214) +{ + unsigned char ram_data[3]; + unsigned int first_wave_addr = 0; + + aw_dev_info("%s enter!\n", __func__); + if (!aw86214->ram_init) { + aw_dev_err("%s: ram init faild, ram_num = 0!\n", __func__); + return -EPERM; + } + + mutex_lock(&aw86214->lock); + /* RAMINIT Enable */ + aw86214_haptic_raminit(aw86214, true); + aw86214_haptic_stop(aw86214); + aw86214_set_ram_addr(aw86214); + aw86214_i2c_read(aw86214, AW86214_REG_RAMDATA, ram_data, + AW_I2C_BYTE_THREE); + first_wave_addr = (ram_data[1] << 8 | ram_data[2]); + aw86214->ram.ram_num = + (first_wave_addr - aw86214->ram.base_addr - 1) / 4; + aw_dev_info("%s: ram_version = 0x%02x\n", __func__, ram_data[0]); + aw_dev_info("%s: first waveform addr = 0x%04x\n", __func__, + first_wave_addr); + aw_dev_info("%s: ram_num = %d\n", __func__, aw86214->ram.ram_num); + /* RAMINIT Disable */ + aw86214_haptic_raminit(aw86214, false); + mutex_unlock(&aw86214->lock); + + return 0; +} + + +static void aw86214_ram_check(const struct firmware *cont, void *context) +{ + struct aw86214 *aw86214 = context; + struct aw86214_container *aw86214_fw; + unsigned short check_sum = 0; + int i = 0; + int ret = 0; +#ifdef AW_READ_BIN_FLEXBALLY + static unsigned char load_cont; + int ram_timer_val = 1000; + + load_cont++; +#endif + aw_dev_info("%s enter\n", __func__); + if (!cont) { + aw_dev_err("%s: failed to read %s\n", __func__, + aw86214_ram_name); + release_firmware(cont); +#ifdef AW_READ_BIN_FLEXBALLY + if (load_cont <= 20) { + schedule_delayed_work(&aw86214->ram_work, + msecs_to_jiffies(ram_timer_val)); + aw_dev_info("%s:start hrtimer: load_cont=%d\n", __func__, + load_cont); + } +#endif + return; + } + aw_dev_info("%s: loaded %s - size: %zu bytes\n", __func__, + aw86214_ram_name, cont ? cont->size : 0); +/* +* for(i=0; i < cont->size; i++) { +* aw_dev_info("%s: addr: 0x%04x, data: 0x%02X\n", +* __func__, i, *(cont->data+i)); +* } +*/ + /* check sum */ + for (i = 2; i < cont->size; i++) + check_sum += cont->data[i]; + if (check_sum == (unsigned short)((cont->data[0]<<8)|(cont->data[1]))) { + + aw_dev_info("%s: check sum pass: 0x%04x\n", __func__, + check_sum); + aw86214->ram.check_sum = check_sum; + } else { + aw_dev_err("%s: check sum err: check_sum=0x%04x\n", __func__, + check_sum); + return; + } + + /* aw86214 ram update less then 128kB */ + aw86214_fw = kzalloc(cont->size + sizeof(int), GFP_KERNEL); + if (!aw86214_fw) { + release_firmware(cont); + aw_dev_err("%s: Error allocating memory\n", __func__); + return; + } + aw86214_fw->len = cont->size; + memcpy(aw86214_fw->data, cont->data, cont->size); + release_firmware(cont); + ret = aw86214_container_update(aw86214, aw86214_fw); + if (ret) { + kfree(aw86214_fw); + aw86214->ram.len = 0; + aw_dev_err("%s: ram firmware update failed!\n", __func__); + } else { + aw86214->ram_init = 1; + aw86214->ram.len = aw86214_fw->len; + kfree(aw86214_fw); + aw_dev_info("%s: ram firmware update complete!\n", __func__); + } + aw86214_haptic_get_ram_number(aw86214); +} + +static int aw86214_ram_update(struct aw86214 *aw86214) +{ + aw86214->ram_init = 0; + + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw86214_ram_name, aw86214->dev, + GFP_KERNEL, aw86214, aw86214_ram_check); +} + +static enum hrtimer_restart aw86214_vibrator_timer_func(struct hrtimer *timer) +{ + struct aw86214 *aw86214 = container_of(timer, struct aw86214, timer); + + aw_dev_info("%s enter\n", __func__); + aw86214->state = 0; + schedule_work(&aw86214->vibrator_work); + + return HRTIMER_NORESTART; +} + +/***************************************************** + * + * motor protect + * + *****************************************************/ +static int aw86214_haptic_swicth_motor_protect_config(struct aw86214 *aw86214, + unsigned char addr, + unsigned char val) +{ + aw_dev_info("%s enter\n", __func__); + if (addr == 1) { + aw86214_i2c_write_bits(aw86214, AW86214_REG_DETCFG1, + AW86214_BIT_DETCFG1_PRCT_MODE_MASK, + AW86214_BIT_DETCFG1_PRCT_MODE_VALID); + aw86214_i2c_write_bits(aw86214, AW86214_REG_PWMCFG1, + AW86214_BIT_PWMCFG1_PRC_EN_MASK, + AW86214_BIT_PWMCFG1_PRC_ENABLE); + aw86214_i2c_write_bits(aw86214, AW86214_REG_PWMCFG3, + AW86214_BIT_PWMCFG3_PR_EN_MASK, + AW86214_BIT_PWMCFG3_PR_ENABLE); + } else if (addr == 0) { + aw86214_i2c_write_bits(aw86214, AW86214_REG_DETCFG1, + AW86214_BIT_DETCFG1_PRCT_MODE_MASK, + AW86214_BIT_DETCFG1_PRCT_MODE_INVALID); + aw86214_i2c_write_bits(aw86214, AW86214_REG_PWMCFG1, + AW86214_BIT_PWMCFG1_PRC_EN_MASK, + AW86214_BIT_PWMCFG1_PRC_DISABLE); + aw86214_i2c_write_bits(aw86214, AW86214_REG_PWMCFG3, + AW86214_BIT_PWMCFG3_PR_EN_MASK, + AW86214_BIT_PWMCFG3_PR_DISABLE); + } else if (addr == 0x2d) { + aw86214_i2c_write_bits(aw86214, AW86214_REG_PWMCFG1, + AW86214_BIT_PWMCFG1_PRCTIME_MASK, val); + } else if (addr == 0x3e) { + aw86214_i2c_write_bits(aw86214, AW86214_REG_PWMCFG3, + AW86214_BIT_PWMCFG3_PRLVL_MASK, val); + } else if (addr == 0x3f) { + aw86214_i2c_write(aw86214, AW86214_REG_PWMCFG4, &val, + AW_I2C_BYTE_ONE); + } + return 0; +} + +static int aw86214_haptic_f0_calibration(struct aw86214 *aw86214) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned int f0_limit = 0; + char f0_cali_lra = 0; + int f0_cali_step = 0; + unsigned int f0_cali_min = aw86214->dts_info.f0_ref * + (100 - aw86214->dts_info.f0_cali_percent) / 100; + unsigned int f0_cali_max = aw86214->dts_info.f0_ref * + (100 + aw86214->dts_info.f0_cali_percent) / 100; + + aw_dev_info("%s enter\n", __func__); + /* + * aw86214_haptic_upload_lra(aw86214, AW86214_WRITE_ZERO); + */ + if (aw86214_haptic_cont_get_f0(aw86214)) { + aw_dev_err("%s get f0 error, user defafult f0\n", __func__); + } else { + /* max and min limit */ + f0_limit = aw86214->f0; + aw_dev_info("%s f0_ref = %d, f0_cali_min = %d, f0_cali_max = %d, f0 = %d\n", + __func__, aw86214->dts_info.f0_ref, + f0_cali_min, f0_cali_max, aw86214->f0); + + if ((aw86214->f0 < f0_cali_min) || aw86214->f0 > f0_cali_max) { + aw_dev_err("%s f0 calibration out of range = %d!\n", + __func__, aw86214->f0); + f0_limit = aw86214->dts_info.f0_ref; + return -ERANGE; + } + aw_dev_info("%s f0_limit = %d\n", __func__, (int)f0_limit); + /* calculate cali step */ + f0_cali_step = 100000 * ((int)f0_limit - + (int)aw86214->dts_info.f0_ref) / + ((int)f0_limit * 24); + aw_dev_info("%s f0_cali_step = %d\n", __func__, f0_cali_step); + if (f0_cali_step >= 0) { /*f0_cali_step >= 0 */ + if (f0_cali_step % 10 >= 5) + f0_cali_step = 32 + (f0_cali_step / 10 + 1); + else + f0_cali_step = 32 + f0_cali_step / 10; + } else { /* f0_cali_step < 0 */ + if (f0_cali_step % 10 <= -5) + f0_cali_step = 32 + (f0_cali_step / 10 - 1); + else + f0_cali_step = 32 + f0_cali_step / 10; + } + if (f0_cali_step > 31) + f0_cali_lra = (char)f0_cali_step - 32; + else + f0_cali_lra = (char)f0_cali_step + 32; + /* update cali step */ + aw86214->f0_cali_data = (int)f0_cali_lra; + aw86214_haptic_upload_lra(aw86214, AW86214_F0_CALI); + aw86214_i2c_read(aw86214, AW86214_REG_TRIMCFG3, ®_val, + AW_I2C_BYTE_ONE); + aw_dev_info("%s: final f0_cali_data = 0x%02X\n", + __func__, reg_val); + } + /* restore standby work mode */ + aw86214_haptic_stop(aw86214); + return ret; +} + +/***************************************************** + * + * haptic cont + * + *****************************************************/ +static int aw86214_haptic_cont(struct aw86214 *aw86214) +{ + unsigned char reg_val = 0; + + aw_dev_info("%s enter\n", __func__); + + /* work mode */ + aw86214_haptic_play_mode(aw86214, AW86214_HAPTIC_CONT_MODE); + /* cont config */ + aw86214_i2c_write_bits(aw86214, AW86214_REG_CONTCFG6, + AW86214_BIT_CONTCFG6_TRACK_EN_MASK, + AW86214_BIT_CONTCFG6_TRACK_ENABLE); + /* f0 driver level */ + aw86214_i2c_write_bits(aw86214, AW86214_REG_CONTCFG6, + AW86214_BIT_CONTCFG6_DRV1_LVL_MASK, + aw86214->cont_drv1_lvl); + reg_val = (unsigned char)aw86214->cont_drv2_lvl; + aw86214_i2c_write(aw86214, AW86214_REG_CONTCFG7, ®_val, + AW_I2C_BYTE_ONE); + /* DRV2_TIME */ + reg_val = 0xFF; + aw86214_i2c_write(aw86214, AW86214_REG_CONTCFG9, ®_val, + AW_I2C_BYTE_ONE); + /* cont play go */ + aw86214_haptic_play_go(aw86214); + return 0; +} + +static int aw86214_haptic_play_wav_seq(struct aw86214 *aw86214, + unsigned char flag) +{ + aw_dev_info("%s enter\n", __func__); + if (flag) { + aw86214_haptic_play_mode(aw86214, AW86214_HAPTIC_RAM_MODE); + aw86214_haptic_play_go(aw86214); + } + return 0; +} + + +static void aw86214_haptic_misc_para_init(struct aw86214 *aw86214) +{ + unsigned char reg_array[4] = {0}; + + aw_dev_info("%s enter\n", __func__); + + aw86214_ver_a_init(aw86214); + + aw86214->cont_drv1_lvl = aw86214->dts_info.cont_drv1_lvl_dt; + aw86214->cont_drv2_lvl = aw86214->dts_info.cont_drv2_lvl_dt; + aw86214->cont_drv1_time = aw86214->dts_info.cont_drv1_time_dt; + aw86214->cont_drv2_time = aw86214->dts_info.cont_drv2_time_dt; + aw86214->cont_wait_num = aw86214->dts_info.cont_wait_num_dt; + + /* SIN_H */ + reg_array[0] = (unsigned char)aw86214->dts_info.sine_array[0]; + /* SIN_L */ + reg_array[1] = (unsigned char)aw86214->dts_info.sine_array[1]; + /* COS_H */ + reg_array[2] = (unsigned char)aw86214->dts_info.sine_array[2]; + /* COS_L */ + reg_array[3] = (unsigned char)aw86214->dts_info.sine_array[3]; + + aw86214_i2c_write(aw86214, AW86214_REG_SYSCTRL3, reg_array, + AW_I2C_BYTE_FOUR); + + /* d2s_gain */ + if (!aw86214->dts_info.d2s_gain) { + aw_dev_err("%s aw86214->dts_info.d2s_gain = 0!\n", __func__); + } else { + aw86214_i2c_write_bits(aw86214, AW86214_REG_SYSCTRL7, + AW86214_BIT_SYSCTRL7_D2S_GAIN_MASK, + aw86214->dts_info.d2s_gain); + } + + if (aw86214->dts_info.cont_tset && aw86214->dts_info.cont_bemf_set) { + reg_array[0] = (unsigned char)aw86214->dts_info.cont_tset; + reg_array[1] = (unsigned char)aw86214->dts_info.cont_bemf_set; + aw86214_i2c_write_bits(aw86214, AW86214_REG_CONTCFG13, + (AW86214_BIT_CONTCFG13_TSET_MASK & + AW86214_BIT_CONTCFG13_BEME_SET_MASK), + ((reg_array[0] << 4)| + reg_array[1])); + } +} + +/***************************************************** + * + * offset calibration + * + *****************************************************/ +static int aw86214_haptic_offset_calibration(struct aw86214 *aw86214) +{ + unsigned int cont = 2000; + unsigned char reg_val = 0; + + aw_dev_info("%s enter\n", __func__); + + aw86214_haptic_raminit(aw86214, true); + + aw86214_i2c_write_bits(aw86214, AW86214_REG_DETCFG2, + AW86214_BIT_DETCFG2_DIAG_GO_MASK, + AW86214_BIT_DETCFG2_DIAG_GO_ON); + while (1) { + aw86214_i2c_read(aw86214, AW86214_REG_DETCFG2, ®_val, + AW_I2C_BYTE_ONE); + if ((reg_val & 0x01) == 0 || cont == 0) + break; + cont--; + } + if (cont == 0) + aw_dev_err("%s calibration offset failed!\n", __func__); + aw86214_haptic_raminit(aw86214, false); + return 0; +} + +static int aw86214_haptic_set_pwm(struct aw86214 *aw86214, unsigned char mode) +{ + switch (mode) { + case AW86214_PWM_48K: + aw86214_i2c_write_bits(aw86214, AW86214_REG_SYSCTRL2, + AW86214_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW86214_BIT_SYSCTRL2_RATE_48K); + break; + case AW86214_PWM_24K: + aw86214_i2c_write_bits(aw86214, AW86214_REG_SYSCTRL2, + AW86214_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW86214_BIT_SYSCTRL2_RATE_24K); + break; + case AW86214_PWM_12K: + aw86214_i2c_write_bits(aw86214, AW86214_REG_SYSCTRL2, + AW86214_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW86214_BIT_SYSCTRL2_RATE_12K); + break; + default: + break; + } + return 0; +} + +/***************************************************** + * + * vbat mode + * + *****************************************************/ +static int aw86214_haptic_vbat_mode_config(struct aw86214 *aw86214, + unsigned char flag) +{ + if (flag == AW86214_HAPTIC_CONT_VBAT_HW_ADJUST_MODE) { + aw86214_i2c_write_bits(aw86214, AW86214_REG_SYSCTRL1, + AW86214_BIT_SYSCTRL1_VBAT_MODE_MASK, + AW86214_BIT_SYSCTRL1_VBAT_MODE_HW); + } else { + aw86214_i2c_write_bits(aw86214, AW86214_REG_SYSCTRL1, + AW86214_BIT_SYSCTRL1_VBAT_MODE_MASK, + AW86214_BIT_SYSCTRL1_VBAT_MODE_SW); + } + return 0; +} + +static void aw86214_ram_work_routine(struct work_struct *work) +{ + struct aw86214 *aw86214 = container_of(work, struct aw86214, + ram_work.work); + + aw_dev_info("%s enter\n", __func__); + aw86214_ram_update(aw86214); +} + +int aw86214_ram_work_init(struct aw86214 *aw86214) +{ + int ram_timer_val = 8000; + + aw_dev_info("%s enter\n", __func__); + INIT_DELAYED_WORK(&aw86214->ram_work, aw86214_ram_work_routine); + schedule_delayed_work(&aw86214->ram_work, + msecs_to_jiffies(ram_timer_val)); + return 0; +} + + +static int +aw86214_haptic_audio_ctr_list_insert(struct haptic_audio *haptic_audio, + struct haptic_ctr *haptic_ctr, + struct device *dev) +{ + struct haptic_ctr *p_new = NULL; + + p_new = kzalloc(sizeof(struct haptic_ctr), GFP_KERNEL); + if (p_new == NULL) + return -ENOMEM; + /* update new list info */ + p_new->cnt = haptic_ctr->cnt; + p_new->cmd = haptic_ctr->cmd; + p_new->play = haptic_ctr->play; + p_new->wavseq = haptic_ctr->wavseq; + p_new->loop = haptic_ctr->loop; + p_new->gain = haptic_ctr->gain; + + INIT_LIST_HEAD(&(p_new->list)); + list_add(&(p_new->list), &(haptic_audio->ctr_list)); + return 0; +} + +static int +aw86214_haptic_audio_ctr_list_clear(struct haptic_audio *haptic_audio) +{ + struct haptic_ctr *p_ctr = NULL; + struct haptic_ctr *p_ctr_bak = NULL; + + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), + list) { + list_del(&p_ctr->list); + kfree(p_ctr); + } + + return 0; +} + +static int aw86214_haptic_audio_off(struct aw86214 *aw86214) +{ + aw_dev_dbg("%s: enter\n", __func__); + mutex_lock(&aw86214->lock); + aw86214_haptic_set_gain(aw86214, 0x80); + aw86214_haptic_stop(aw86214); + aw86214->gun_type = AW_GUN_TYPE_DEF_VAL; + aw86214->bullet_nr = AW_BULLET_NR_DEF_VAL; + aw86214_haptic_audio_ctr_list_clear(&aw86214->haptic_audio); + mutex_unlock(&aw86214->lock); + return 0; +} + +static int aw86214_haptic_audio_init(struct aw86214 *aw86214) +{ + + aw_dev_dbg("%s enter\n", __func__); + aw86214_haptic_set_wav_seq(aw86214, 0x01, 0x00); + + return 0; +} + +static void aw86214_interrupt_clear(struct aw86214 *aw86214) +{ + unsigned char reg_val = 0; + + aw86214_i2c_read(aw86214, AW86214_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_dev_dbg("%s: SYSINT=0x%02X\n", __func__, reg_val); +} + +static int aw86214_haptic_activate(struct aw86214 *aw86214) +{ + aw86214_i2c_write_bits(aw86214, AW86214_REG_SYSCTRL2, + AW86214_BIT_SYSCTRL2_STANDBY_MASK, + AW86214_BIT_SYSCTRL2_STANDBY_OFF); + aw86214_interrupt_clear(aw86214); + aw86214_i2c_write_bits(aw86214, AW86214_REG_SYSINTM, + AW86214_BIT_SYSINTM_UVLM_MASK, + AW86214_BIT_SYSINTM_UVLM_ON); + return 0; +} + +static int aw86214_haptic_start(struct aw86214 *aw86214) +{ + aw86214_haptic_activate(aw86214); + aw86214_haptic_play_go(aw86214); + return 0; +} + +static void aw86214_ctr_list_config(struct aw86214 *aw86214, + struct haptic_ctr *p_ctr, + struct haptic_ctr *p_ctr_bak, + struct haptic_audio *haptic_audio) +{ + unsigned int list_input_cnt = 0; + unsigned int list_output_cnt = 0; + unsigned int list_diff_cnt = 0; + unsigned int list_del_cnt = 0; + + list_for_each_entry_safe(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + list_input_cnt = p_ctr->cnt; + break; + } + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + list_output_cnt = p_ctr->cnt; + break; + } + if (list_input_cnt > list_output_cnt) + list_diff_cnt = list_input_cnt - list_output_cnt; + if (list_input_cnt < list_output_cnt) + list_diff_cnt = 32 + list_input_cnt - list_output_cnt; + if (list_diff_cnt > 2) { + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + if ((p_ctr->play == 0) && + (AW86214_HAPTIC_CMD_ENABLE == + (AW86214_HAPTIC_CMD_HAPTIC & p_ctr->cmd))) { + list_del(&p_ctr->list); + kfree(p_ctr); + list_del_cnt++; + } + if (list_del_cnt == list_diff_cnt) + break; + } + } +} + +static void aw86214_haptic_audio_work_routine(struct work_struct *work) +{ + struct aw86214 *aw86214 = container_of(work, + struct aw86214, + haptic_audio.work); + struct haptic_audio *haptic_audio = NULL; + struct haptic_ctr *p_ctr = NULL; + struct haptic_ctr *p_ctr_bak = NULL; + unsigned int ctr_list_flag = 0; + + aw_dev_dbg("%s enter\n", __func__); + + haptic_audio = &(aw86214->haptic_audio); + mutex_lock(&aw86214->haptic_audio.lock); + memset(&aw86214->haptic_audio.ctr, 0, + sizeof(struct haptic_ctr)); + ctr_list_flag = 0; + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + ctr_list_flag = 1; + break; + } + if (ctr_list_flag == 0) + aw_dev_info("%s: ctr list empty\n", __func__); + + if (ctr_list_flag == 1) + aw86214_ctr_list_config(aw86214, p_ctr, + p_ctr_bak, haptic_audio); + + + /* get the last data from list */ + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + aw86214->haptic_audio.ctr.cnt = p_ctr->cnt; + aw86214->haptic_audio.ctr.cmd = p_ctr->cmd; + aw86214->haptic_audio.ctr.play = p_ctr->play; + aw86214->haptic_audio.ctr.wavseq = p_ctr->wavseq; + aw86214->haptic_audio.ctr.loop = p_ctr->loop; + aw86214->haptic_audio.ctr.gain = p_ctr->gain; + list_del(&p_ctr->list); + kfree(p_ctr); + break; + } + + if (aw86214->haptic_audio.ctr.play) { + aw_dev_info("%s: cnt=%d, cmd=%d, play=%d, wavseq=%d, loop=%d, gain=%d\n", + __func__, + aw86214->haptic_audio.ctr.cnt, + aw86214->haptic_audio.ctr.cmd, + aw86214->haptic_audio.ctr.play, + aw86214->haptic_audio.ctr.wavseq, + aw86214->haptic_audio.ctr.loop, + aw86214->haptic_audio.ctr.gain); + } + mutex_unlock(&aw86214->haptic_audio.lock); + + /*haptic play control*/ + if (AW86214_HAPTIC_CMD_ENABLE == + (AW86214_HAPTIC_CMD_HAPTIC & aw86214->haptic_audio.ctr.cmd)) { + if (aw86214->haptic_audio.ctr.play == + AW86214_HAPTIC_PLAY_ENABLE) { + aw_dev_info("%s: haptic_audio_play_start\n", __func__); + aw_dev_info("%s: normal haptic start\n", __func__); + mutex_lock(&aw86214->lock); + aw86214_haptic_stop(aw86214); + aw86214_haptic_play_mode(aw86214, + AW86214_HAPTIC_RAM_MODE); + aw86214_haptic_set_wav_seq(aw86214, 0x00, + aw86214->haptic_audio.ctr.wavseq); + aw86214_haptic_set_wav_loop(aw86214, 0x00, + aw86214->haptic_audio.ctr.loop); + aw86214_haptic_set_gain(aw86214, + aw86214->haptic_audio.ctr.gain); + aw86214_haptic_start(aw86214); + mutex_unlock(&aw86214->lock); + } else if (AW86214_HAPTIC_PLAY_STOP == + aw86214->haptic_audio.ctr.play) { + mutex_lock(&aw86214->lock); + aw86214_haptic_stop(aw86214); + mutex_unlock(&aw86214->lock); + } else if (AW86214_HAPTIC_PLAY_GAIN == + aw86214->haptic_audio.ctr.play) { + mutex_lock(&aw86214->lock); + aw86214_haptic_set_gain(aw86214, + aw86214->haptic_audio.ctr.gain); + mutex_unlock(&aw86214->lock); + } + } + + +} + +static enum hrtimer_restart +aw86214_haptic_audio_timer_func(struct hrtimer *timer) +{ + struct aw86214 *aw86214 = container_of(timer, + struct aw86214, haptic_audio.timer); + + aw_dev_dbg("%s enter\n", __func__); + schedule_work(&aw86214->haptic_audio.work); + + hrtimer_start(&aw86214->haptic_audio.timer, + ktime_set(aw86214->haptic_audio.timer_val/1000000, + (aw86214->haptic_audio.timer_val%1000000)*1000), + HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + + +#ifdef TIMED_OUTPUT +static int aw86214_vibrator_get_time(struct timed_output_dev *dev) +{ + struct aw86214 *aw86214 = container_of(dev, struct aw86214, vib_dev); + + if (hrtimer_active(&aw86214->timer)) { + ktime_t r = hrtimer_get_remaining(&aw86214->timer); + + return ktime_to_ms(r); + } + return 0; +} + +static void aw86214_vibrator_enable(struct timed_output_dev *dev, int value) +{ + struct aw86214 *aw86214 = container_of(dev, struct aw86214, vib_dev); + + aw_dev_info("%s enter\n", __func__); + if (!aw86214->ram_init) { + aw_dev_err("%s: ram init failed, not allow to play!\n", + __func__); + return; + } + mutex_lock(&aw86214->lock); + aw86214_haptic_stop(aw86214); + if (value > 0) { + aw86214_brk_en(aw86214, false); + aw86214_haptic_ram_vbat_comp(aw86214, false); + aw86214_haptic_play_wav_seq(aw86214, value); + } + mutex_unlock(&aw86214->lock); + aw_dev_info("%s exit\n", __func__); +} +#else +static enum led_brightness aw86214_haptic_brightness_get(struct led_classdev + *cdev) +{ + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + + return aw86214->amplitude; +} + +static void aw86214_haptic_brightness_set(struct led_classdev *cdev, + enum led_brightness level) +{ + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + + aw_dev_info("%s enter\n", __func__); + if (!aw86214->ram_init) { + aw_dev_err("%s: ram init failed, not allow to play!\n", + __func__); + return; + } + if (aw86214->ram_update_flag < 0) + return; + aw86214->amplitude = level; + mutex_lock(&aw86214->lock); + aw86214_haptic_stop(aw86214); + if (aw86214->amplitude > 0) { + aw86214_brk_en(aw86214, false); + aw86214_haptic_ram_vbat_comp(aw86214, false); + aw86214_haptic_play_wav_seq(aw86214, aw86214->amplitude); + } + mutex_unlock(&aw86214->lock); +} +#endif + +static ssize_t aw86214_cont_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + ssize_t len = 0; + + aw86214_haptic_read_cont_f0(aw86214); + len += snprintf(buf + len, PAGE_SIZE - len, + "%d\n", aw86214->cont_f0); + return len; +} + +static ssize_t aw86214_cont_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + aw86214_haptic_stop(aw86214); + aw86214_brk_en(aw86214, true); + if (val) + aw86214_haptic_cont(aw86214); + return count; +} + +static ssize_t aw86214_f0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + ssize_t len = 0; + unsigned char reg = 0; + + mutex_lock(&aw86214->lock); + /* set d2s_gain to max to get better performance when cat f0 .*/ + aw86214_i2c_write_bits(aw86214, AW86214_REG_SYSCTRL7, + AW86214_BIT_SYSCTRL7_D2S_GAIN_MASK, + AW86214_BIT_SYSCTRL7_D2S_GAIN_40); + aw86214_i2c_read(aw86214, AW86214_REG_TRIMCFG3, ®, AW_I2C_BYTE_ONE); + aw86214_haptic_upload_lra(aw86214, AW86214_WRITE_ZERO); + aw86214_haptic_cont_get_f0(aw86214); + aw86214_i2c_write(aw86214, AW86214_REG_TRIMCFG3, ®, AW_I2C_BYTE_ONE); + /* set d2s_gain to default when cat f0 is finished.*/ + aw86214_i2c_write_bits(aw86214, AW86214_REG_SYSCTRL7, + AW86214_BIT_SYSCTRL7_D2S_GAIN_MASK, + aw86214->dts_info.d2s_gain); + mutex_unlock(&aw86214->lock); + len += snprintf(buf + len, PAGE_SIZE - len, + "%d\n", aw86214->f0); + return len; +} + +static ssize_t aw86214_f0_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + return count; +} + +static ssize_t aw86214_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char i = 0; + unsigned char size = 0; + unsigned char cnt = 0; + unsigned char reg_array[AW_REG_MAX] = {0}; + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + + aw_dev_dbg("%s: enter!\n", __func__); + for (i = 0; i <= (AW86214_REG_TRIMCFG3 + 1); i++) { + + if (i == aw86214_reg_list[cnt] && + (cnt < sizeof(aw86214_reg_list))) { + size++; + cnt++; + continue; + } else { + if (size != 0) { + aw86214_i2c_read(aw86214, + aw86214_reg_list[cnt-size], + ®_array[cnt-size], size); + size = 0; + + } + } + } + + for (i = 0; i < sizeof(aw86214_reg_list); i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "reg:0x%02X=0x%02X\n", aw86214_reg_list[i], + reg_array[i]); + } + + return len; +} + +static ssize_t aw86214_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + unsigned char reg_val = 0; + unsigned int databuf[2] = { 0, 0 }; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + reg_val = (unsigned char)databuf[1]; + aw86214_i2c_write(aw86214, (unsigned char)databuf[0], + ®_val, AW_I2C_BYTE_ONE); + } + + return count; +} + +static ssize_t aw86214_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + ktime_t time_rem; + s64 time_ms = 0; + + if (hrtimer_active(&aw86214->timer)) { + time_rem = hrtimer_get_remaining(&aw86214->timer); + time_ms = ktime_to_ms(time_rem); + } + return snprintf(buf, PAGE_SIZE, "%lldms\n", time_ms); +} + +static ssize_t aw86214_duration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + /* setting 0 on duration is NOP for now */ + if (val <= 0) + return count; + rc = aw86214_haptic_ram_config(aw86214, val); + if (rc < 0) { + aw_dev_info("%s: ram config failed!\n", __func__); + return count; + } + aw86214->duration = val; + return count; +} + +static ssize_t aw86214_activate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + + /* For now nothing to show */ + return snprintf(buf, PAGE_SIZE, "%d\n", aw86214->state); +} + +static ssize_t aw86214_activate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned int val = 0; + int rc = 0; + + if (!aw86214->ram_init) { + aw_dev_err("%s: ram init failed, not allow to play!\n", + __func__); + return count; + } + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + aw_dev_info("%s: value=%d\n", __func__, val); + mutex_lock(&aw86214->lock); + hrtimer_cancel(&aw86214->timer); + aw86214->state = val; + mutex_unlock(&aw86214->lock); + schedule_work(&aw86214->vibrator_work); + return count; +} + +static ssize_t aw86214_seq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + size_t count = 0; + unsigned char i = 0; + unsigned char reg_val[AW86214_SEQUENCER_SIZE] = {0}; + + aw86214_i2c_read(aw86214, AW86214_REG_WAVCFG1, reg_val, + AW86214_SEQUENCER_SIZE); + for (i = 0; i < AW86214_SEQUENCER_SIZE; i++) { + count += snprintf(buf+count, PAGE_SIZE-count, + "seq%d: 0x%02x\n", i+1, reg_val[i]); + aw86214->seq[i] = reg_val[i]; + } + return count; +} + +static ssize_t aw86214_seq_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned int databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + if (databuf[0] > AW86214_SEQUENCER_SIZE || + databuf[1] > aw86214->ram.ram_num) { + aw_dev_err("%s input value out of range\n", __func__); + return count; + } + aw_dev_info("%s: seq%d=0x%02X\n", __func__, + databuf[0], databuf[1]); + mutex_lock(&aw86214->lock); + aw86214->seq[databuf[0]] = (unsigned char)databuf[1]; + aw86214_haptic_set_wav_seq(aw86214, (unsigned char)databuf[0], + aw86214->seq[databuf[0]]); + mutex_unlock(&aw86214->lock); + } + return count; +} + +static ssize_t aw86214_loop_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + size_t count = 0; + unsigned char i = 0; + unsigned char reg_val[AW86214_SEQUENCER_LOOP_SIZE] = {0}; + + aw86214_i2c_read(aw86214, AW86214_REG_WAVCFG9, reg_val, + AW86214_SEQUENCER_LOOP_SIZE); + + for (i = 0; i < AW86214_SEQUENCER_LOOP_SIZE; i++) { + aw86214->loop[i*2+0] = (reg_val[i] >> 4) & 0x0F; + aw86214->loop[i*2+1] = (reg_val[i] >> 0) & 0x0F; + + count += snprintf(buf+count, PAGE_SIZE-count, + "seq%d_loop: 0x%02x\n", i*2+1, aw86214->loop[i*2+0]); + count += snprintf(buf+count, PAGE_SIZE-count, + "seq%d_loop: 0x%02x\n", i*2+2, aw86214->loop[i*2+1]); + } + return count; +} + +static ssize_t aw86214_loop_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned int databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_dev_info("%s: seq%d loop=0x%02X\n", __func__, + databuf[0], databuf[1]); + mutex_lock(&aw86214->lock); + aw86214->loop[databuf[0]] = (unsigned char)databuf[1]; + aw86214_haptic_set_wav_loop(aw86214, (unsigned char)databuf[0], + aw86214->loop[databuf[0]]); + mutex_unlock(&aw86214->lock); + } + return count; +} + +static ssize_t aw86214_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", aw86214->state); +} + +static ssize_t aw86214_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw86214_activate_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", + aw86214->activate_mode); +} + +static ssize_t aw86214_activate_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + mutex_lock(&aw86214->lock); + aw86214->activate_mode = val; + mutex_unlock(&aw86214->lock); + return count; +} + +static ssize_t aw86214_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned char reg_val = 0; + + aw86214_i2c_read(aw86214, AW86214_REG_WAVCFG1, ®_val, + AW_I2C_BYTE_ONE); + aw86214->index = reg_val; + return snprintf(buf, PAGE_SIZE, "%d\n", aw86214->index); +} + +static ssize_t aw86214_index_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + if (val > aw86214->ram.ram_num) { + aw_dev_err("%s: input value out of range!\n", __func__); + return count; + } + aw_dev_info("%s: value=%d\n", __func__, val); + mutex_lock(&aw86214->lock); + aw86214->index = val; + aw86214_haptic_set_repeat_wav_seq(aw86214, aw86214->index); + mutex_unlock(&aw86214->lock); + return count; +} + +static ssize_t aw86214_gain_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + + return snprintf(buf, PAGE_SIZE, "0x%02x\n", aw86214->gain); +} + +static ssize_t aw86214_gain_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + aw_dev_info("%s: value=%d\n", __func__, val); + if (val >= 0x80) + val = 0x80; + mutex_lock(&aw86214->lock); + aw86214->gain = val; + aw86214_haptic_set_gain(aw86214, aw86214->gain); + mutex_unlock(&aw86214->lock); + return count; +} + +static ssize_t aw86214_ram_update_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int cnt = 0; + int i = 0; + int size = 0; + ssize_t len = 0; + unsigned char ram_data[AW_RAMDATA_RD_BUFFER_SIZE] = {0}; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + + /* RAMINIT Enable */ + aw86214_haptic_raminit(aw86214, true); + aw86214_haptic_stop(aw86214); + aw86214_set_ram_addr(aw86214); + len += snprintf(buf + len, PAGE_SIZE - len, + "haptic_ram len = %d\n", aw86214->ram.len); + while (cnt < aw86214->ram.len) { + if ((aw86214->ram.len - cnt) < AW_RAMDATA_RD_BUFFER_SIZE) + size = aw86214->ram.len - cnt; + else + size = AW_RAMDATA_RD_BUFFER_SIZE; + + aw86214_i2c_read(aw86214, AW86214_REG_RAMDATA, + ram_data, size); + for (i = 0; i < size; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%02X,", ram_data[i]); + } + cnt += size; + } + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + /* RAMINIT Disable */ + aw86214_haptic_raminit(aw86214, false); + return len; +} + +static ssize_t aw86214_ram_update_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + if (val) + aw86214_ram_update(aw86214); + return count; +} + +static ssize_t aw86214_f0_save_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "f0_cali_data = 0x%02X\n", + aw86214->f0_cali_data); + + return len; +} + +static ssize_t aw86214_f0_save_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + aw86214->f0_cali_data = val; + return count; +} + +static ssize_t aw86214_ram_vbat_compensate_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + ssize_t len = 0; + + len += + snprintf(buf + len, PAGE_SIZE - len, "ram_vbat_compensate = %d\n", + aw86214->ram_vbat_compensate); + + return len; +} + +static ssize_t aw86214_ram_vbat_compensate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + mutex_lock(&aw86214->lock); + if (val) + aw86214->ram_vbat_compensate = + AW86214_HAPTIC_RAM_VBAT_COMP_ENABLE; + else + aw86214->ram_vbat_compensate = + AW86214_HAPTIC_RAM_VBAT_COMP_DISABLE; + mutex_unlock(&aw86214->lock); + + return count; +} + +static ssize_t aw86214_cali_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + ssize_t len = 0; + + mutex_lock(&aw86214->lock); + aw86214_haptic_upload_lra(aw86214, AW86214_F0_CALI); + aw86214_haptic_cont_get_f0(aw86214); + mutex_unlock(&aw86214->lock); + len += snprintf(buf + len, PAGE_SIZE - len, + "%d\n", aw86214->f0); + return len; +} + +static ssize_t aw86214_cali_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned int val = 0; + int i; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + if (val) { + mutex_lock(&aw86214->lock); + aw86214_haptic_upload_lra(aw86214, AW86214_WRITE_ZERO); + for (i = 0; i < 2; i++) { + aw86214_haptic_f0_calibration(aw86214); + mdelay(20); + } + mutex_unlock(&aw86214->lock); + } + return count; +} + +static ssize_t aw86214_cont_wait_num_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_wait_num = 0x%02X\n", aw86214->cont_wait_num); + return len; +} + +static ssize_t aw86214_cont_wait_num_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned char reg_val = 0; + int err, val; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + + err = kstrtoint(buf, 16, &val); + if (err != 0) { + aw_dev_err("%s format not match!", __func__); + return count; + } + aw86214->cont_wait_num = val; + reg_val = aw86214->cont_wait_num; + aw86214_i2c_write(aw86214, AW86214_REG_CONTCFG4, ®_val, + AW_I2C_BYTE_ONE); + return count; +} + +static ssize_t aw86214_cont_drv_lvl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_lvl = 0x%02X, cont_drv2_lvl = 0x%02X\n", + aw86214->cont_drv1_lvl, aw86214->cont_drv2_lvl); + return len; +} + +static ssize_t aw86214_cont_drv_lvl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned char reg_val = 0; + unsigned int databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw86214->cont_drv1_lvl = databuf[0]; + aw86214->cont_drv2_lvl = databuf[1]; + aw86214_i2c_write_bits(aw86214, AW86214_REG_CONTCFG6, + AW86214_BIT_CONTCFG6_DRV1_LVL_MASK, + aw86214->cont_drv1_lvl); + reg_val = (unsigned char)aw86214->cont_drv2_lvl; + aw86214_i2c_write(aw86214, AW86214_REG_CONTCFG7, ®_val, + AW_I2C_BYTE_ONE); + } + return count; +} + +static ssize_t aw86214_cont_drv_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_time = 0x%02X, cont_drv2_time = 0x%02X\n", + aw86214->cont_drv1_time, aw86214->cont_drv2_time); + return len; +} + +static ssize_t aw86214_cont_drv_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned char reg_val[2] = {0}; + unsigned int databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw86214->cont_drv1_time = databuf[0]; + aw86214->cont_drv2_time = databuf[1]; + reg_val[0] = (unsigned char)aw86214->cont_drv1_time; + reg_val[1] = (unsigned char)aw86214->cont_drv2_time; + aw86214_i2c_write(aw86214, AW86214_REG_CONTCFG8, reg_val, + AW_I2C_BYTE_TWO); + } + return count; +} + +static ssize_t aw86214_vbat_monitor_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + ssize_t len = 0; + + mutex_lock(&aw86214->lock); + aw86214_haptic_get_vbat(aw86214); + len += snprintf(buf + len, PAGE_SIZE - len, "vbat_monitor = %d\n", + aw86214->vbat); + mutex_unlock(&aw86214->lock); + + return len; +} + +static ssize_t aw86214_vbat_monitor_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw86214_lra_resistance_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + ssize_t len = 0; + + aw86214_haptic_get_lra_resistance(aw86214); + len += snprintf(buf + len, PAGE_SIZE - len, "lra_resistance = %d\n", + aw86214->lra); + return len; +} + +static ssize_t aw86214_lra_resistance_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw86214_prctmode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + ssize_t len = 0; + unsigned char reg_val = 0; + + aw86214_i2c_read(aw86214, AW86214_REG_DETCFG1, ®_val, + AW_I2C_BYTE_ONE); + + len += snprintf(buf + len, PAGE_SIZE - len, "prctmode = %d\n", + reg_val & 0x08); + return len; +} + +static ssize_t aw86214_prctmode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned int databuf[2] = { 0, 0 }; + unsigned int addr = 0; + unsigned int val = 0; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + addr = databuf[0]; + val = databuf[1]; + mutex_lock(&aw86214->lock); + aw86214_haptic_swicth_motor_protect_config(aw86214, addr, val); + mutex_unlock(&aw86214->lock); + } + return count; +} + +static ssize_t aw86214_gun_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + + return snprintf(buf, PAGE_SIZE, "0x%02x\n", aw86214->gun_type); + +} + +static ssize_t aw86214_gun_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + aw_dev_info("%s: value=%d\n", __func__, val); + mutex_lock(&aw86214->lock); + aw86214->gun_type = val; + mutex_unlock(&aw86214->lock); + return count; +} + +static ssize_t aw86214_bullet_nr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + + return snprintf(buf, PAGE_SIZE, "0x%02x\n", aw86214->bullet_nr); +} + +static ssize_t aw86214_bullet_nr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + aw_dev_info("%s: value=%d\n", __func__, val); + mutex_lock(&aw86214->lock); + aw86214->bullet_nr = val; + mutex_unlock(&aw86214->lock); + return count; +} + +static ssize_t aw86214_haptic_audio_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "%d\n", aw86214->haptic_audio.ctr.cnt); + return len; +} + +static int aw86214_haptic_audio_config(struct aw86214 *aw86214, + unsigned int databuf[]) +{ + struct haptic_ctr *hap_ctr = NULL; + + hap_ctr = kzalloc(sizeof(struct haptic_ctr), GFP_KERNEL); + if (hap_ctr == NULL) + return -ENOMEM; + mutex_lock(&aw86214->haptic_audio.lock); + hap_ctr->cnt = (unsigned char)databuf[0]; + hap_ctr->cmd = (unsigned char)databuf[1]; + hap_ctr->play = (unsigned char)databuf[2]; + hap_ctr->wavseq = (unsigned char)databuf[3]; + hap_ctr->loop = (unsigned char)databuf[4]; + hap_ctr->gain = (unsigned char)databuf[5]; + aw86214_haptic_audio_ctr_list_insert(&aw86214->haptic_audio, + hap_ctr, aw86214->dev); + if (hap_ctr->cmd == 0xff) { + aw_dev_info("%s: haptic_audio stop\n", __func__); + if (hrtimer_active(&aw86214->haptic_audio.timer)) { + aw_dev_info("%s: cancel haptic_audio_timer\n", + __func__); + hrtimer_cancel(&aw86214->haptic_audio.timer); + aw86214->haptic_audio.ctr.cnt = 0; + aw86214_haptic_audio_off(aw86214); + } + } else { + if (hrtimer_active(&aw86214->haptic_audio.timer)) { + } else { + aw_dev_info("%s: start haptic_audio_timer\n", + __func__); + aw86214_haptic_audio_init(aw86214); + hrtimer_start(&aw86214->haptic_audio.timer, + ktime_set(aw86214->haptic_audio.delay_val/1000000, + (aw86214->haptic_audio.delay_val%1000000)*1000), + HRTIMER_MODE_REL); + } + } + mutex_unlock(&aw86214->haptic_audio.lock); + kfree(hap_ctr); + return 0; +} + +static ssize_t aw86214_haptic_audio_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned int databuf[6] = {0}; + + if (!aw86214->ram_init) + return count; + + if (sscanf(buf, "%d %d %d %d %d %d", + &databuf[0], &databuf[1], &databuf[2], + &databuf[3], &databuf[4], &databuf[5]) == 6) { + if (databuf[2]) { + aw_dev_dbg("%s: cnt=%d, cmd=%d, play=%d\n", __func__, + databuf[0], databuf[1], databuf[2]); + aw_dev_dbg("%s: wavseq=%d, loop=%d, gain=%d\n", + __func__, databuf[3], databuf[4], + databuf[5]); + aw86214_haptic_audio_config(aw86214, databuf); + } + + } + return count; +} + +static ssize_t aw86214_ram_num_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + ssize_t len = 0; + + aw86214_haptic_get_ram_number(aw86214); + len += snprintf(buf+len, PAGE_SIZE-len, + "ram_num = %d\n", aw86214->ram.ram_num); + return len; +} + +static ssize_t aw86214_awrw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + unsigned char i = 0; + unsigned char reg_num = aw86214->aw_i2c_package.reg_num; + unsigned char flag = aw86214->aw_i2c_package.flag; + unsigned char *reg_data = aw86214->aw_i2c_package.reg_data; + ssize_t len = 0; + + if (!reg_num) { + aw_dev_err("%s: awrw parameter error\n", __func__); + return len; + } + if (flag == AW86214_READ) { + for (i = 0; i < reg_num; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%02x,", reg_data[i]); + } + + len += snprintf(buf + len - 1, PAGE_SIZE - len, "\n"); + } + + return len; +} + +static ssize_t aw86214_awrw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int datatype[3] = { 0 }; + int ret = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw86214 *aw86214 = container_of(cdev, struct aw86214, vib_dev); + + if (sscanf(buf, "%x %x %x", &datatype[0], &datatype[1], + &datatype[2]) == 3) { + if (!datatype[1]) { + aw_dev_err("%s: awrw parameter error\n", __func__); + return count; + } + aw86214->aw_i2c_package.flag = (unsigned char)datatype[0]; + aw86214->aw_i2c_package.reg_num = (unsigned char)datatype[1]; + aw86214->aw_i2c_package.first_addr = (unsigned char)datatype[2]; + + if (aw86214->aw_i2c_package.flag == AW86214_WRITE) { + ret = aw86214_parse_data(aw86214, buf); + if (ret < 0) + return count; + aw86214_i2c_write(aw86214, + aw86214->aw_i2c_package.first_addr, + aw86214->aw_i2c_package.reg_data, + aw86214->aw_i2c_package.reg_num); + } + if (aw86214->aw_i2c_package.flag == AW86214_READ) + aw86214_i2c_read(aw86214, + aw86214->aw_i2c_package.first_addr, + aw86214->aw_i2c_package.reg_data, + aw86214->aw_i2c_package.reg_num); + } else + aw_dev_err("%s: missing number of parameters\n", __func__); + + return count; +} + +static DEVICE_ATTR(f0, 0644, aw86214_f0_show, aw86214_f0_store); +static DEVICE_ATTR(cont, 0644, aw86214_cont_show, aw86214_cont_store); +static DEVICE_ATTR(reg, 0644, aw86214_reg_show, aw86214_reg_store); +static DEVICE_ATTR(duration, 0644, aw86214_duration_show, + aw86214_duration_store); +static DEVICE_ATTR(index, 0644, aw86214_index_show, aw86214_index_store); +static DEVICE_ATTR(activate, 0644, aw86214_activate_show, + aw86214_activate_store); +static DEVICE_ATTR(activate_mode, 0644, aw86214_activate_mode_show, + aw86214_activate_mode_store); +static DEVICE_ATTR(seq, 0644, aw86214_seq_show, aw86214_seq_store); +static DEVICE_ATTR(loop, 0644, aw86214_loop_show, aw86214_loop_store); +static DEVICE_ATTR(state, 0644, aw86214_state_show, aw86214_state_store); +static DEVICE_ATTR(gain, 0644, aw86214_gain_show, aw86214_gain_store); +static DEVICE_ATTR(ram_update, 0644, aw86214_ram_update_show, + aw86214_ram_update_store); +static DEVICE_ATTR(f0_save, 0644, aw86214_f0_save_show, aw86214_f0_save_store); +static DEVICE_ATTR(ram_vbat_comp, 0644, aw86214_ram_vbat_compensate_show, + aw86214_ram_vbat_compensate_store); +static DEVICE_ATTR(cali, 0644, aw86214_cali_show, aw86214_cali_store); +static DEVICE_ATTR(cont_wait_num, 0644, aw86214_cont_wait_num_show, + aw86214_cont_wait_num_store); +static DEVICE_ATTR(cont_drv_lvl, 0644, aw86214_cont_drv_lvl_show, + aw86214_cont_drv_lvl_store); +static DEVICE_ATTR(cont_drv_time, 0644, aw86214_cont_drv_time_show, + aw86214_cont_drv_time_store); +static DEVICE_ATTR(vbat_monitor, 0644, aw86214_vbat_monitor_show, + aw86214_vbat_monitor_store); +static DEVICE_ATTR(lra_resistance, 0644, aw86214_lra_resistance_show, + aw86214_lra_resistance_store); +static DEVICE_ATTR(prctmode, 0644, aw86214_prctmode_show, + aw86214_prctmode_store); +static DEVICE_ATTR(gun_type, 0644, aw86214_gun_type_show, + aw86214_gun_type_store); +static DEVICE_ATTR(bullet_nr, 0644, aw86214_bullet_nr_show, + aw86214_bullet_nr_store); +static DEVICE_ATTR(haptic_audio, 0644, aw86214_haptic_audio_show, + aw86214_haptic_audio_store); +static DEVICE_ATTR(ram_num, 0644, aw86214_ram_num_show, NULL); +static DEVICE_ATTR(awrw, 0644, aw86214_awrw_show, aw86214_awrw_store); +static struct attribute *aw86214_vibrator_attributes[] = { + &dev_attr_state.attr, + &dev_attr_duration.attr, + &dev_attr_activate.attr, + &dev_attr_activate_mode.attr, + &dev_attr_index.attr, + &dev_attr_gain.attr, + &dev_attr_seq.attr, + &dev_attr_loop.attr, + &dev_attr_reg.attr, + &dev_attr_ram_update.attr, + &dev_attr_f0.attr, + &dev_attr_f0_save.attr, + &dev_attr_cali.attr, + &dev_attr_cont.attr, + &dev_attr_cont_wait_num.attr, + &dev_attr_cont_drv_lvl.attr, + &dev_attr_cont_drv_time.attr, + &dev_attr_vbat_monitor.attr, + &dev_attr_lra_resistance.attr, + &dev_attr_prctmode.attr, + &dev_attr_ram_vbat_comp.attr, + &dev_attr_gun_type.attr, + &dev_attr_bullet_nr.attr, + &dev_attr_haptic_audio.attr, + &dev_attr_ram_num.attr, + &dev_attr_awrw.attr, + NULL +}; + +struct attribute_group aw86214_vibrator_attribute_group = { + .attrs = aw86214_vibrator_attributes +}; + +static void aw86214_haptic_ram_play(struct aw86214 *aw86214, unsigned char type) +{ + aw_dev_info("%s index = %d\n", __func__, aw86214->index); + + mutex_lock(&aw86214->ram_lock); + if (type == AW86214_HAPTIC_RAM_LOOP_MODE) + aw86214_haptic_set_repeat_wav_seq(aw86214, aw86214->index); + + if (type == AW86214_HAPTIC_RAM_MODE) { + aw86214_haptic_set_wav_seq(aw86214, 0x00, aw86214->index); + aw86214_haptic_set_wav_seq(aw86214, 0x01, 0x00); + aw86214_haptic_set_wav_loop(aw86214, 0x00, 0x00); + } + aw86214_haptic_play_mode(aw86214, type); + aw86214_haptic_start(aw86214); + mutex_unlock(&aw86214->ram_lock); +} + +static void aw86214_vibrator_work_routine(struct work_struct *work) +{ + struct aw86214 *aw86214 = container_of(work, struct aw86214, + vibrator_work); + + aw_dev_info("%s enter\n", __func__); + + mutex_lock(&aw86214->lock); + /* Enter standby mode */ + aw86214_haptic_stop(aw86214); + aw86214_haptic_upload_lra(aw86214, AW86214_F0_CALI); + if (aw86214->state) { + if (aw86214->activate_mode == AW86214_HAPTIC_RAM_MODE) { + aw86214_brk_en(aw86214, false); + aw86214_haptic_ram_vbat_comp(aw86214, false); + aw86214_haptic_ram_play(aw86214, + AW86214_HAPTIC_RAM_MODE); + } else if (aw86214->activate_mode == + AW86214_HAPTIC_RAM_LOOP_MODE) { + aw86214_haptic_ram_vbat_comp(aw86214, true); + aw86214_haptic_ram_play(aw86214, + AW86214_HAPTIC_RAM_LOOP_MODE); + /* run ms timer */ + hrtimer_start(&aw86214->timer, + ktime_set(aw86214->duration / 1000, + (aw86214->duration % 1000) * 1000000), + HRTIMER_MODE_REL); + } else if (aw86214->activate_mode == + AW86214_HAPTIC_CONT_MODE) { + aw86214_haptic_cont(aw86214); + /* run ms timer */ + hrtimer_start(&aw86214->timer, + ktime_set(aw86214->duration / 1000, + (aw86214->duration % 1000) * 1000000), + HRTIMER_MODE_REL); + } else { + aw_dev_err("%s: activate_mode error\n", + __func__); + } + } + mutex_unlock(&aw86214->lock); +} + + + +int aw86214_vibrator_init(struct aw86214 *aw86214) +{ + int ret = 0; + + aw_dev_info("%s enter\n", __func__); + +#ifdef TIMED_OUTPUT + aw_dev_info("%s: TIMED_OUT FRAMEWORK!\n", __func__); + aw86214->vib_dev.name = "awinic_vibrator"; + aw86214->vib_dev.get_time = aw86214_vibrator_get_time; + aw86214->vib_dev.enable = aw86214_vibrator_enable; + + ret = timed_output_dev_register(&(aw86214->vib_dev)); + if (ret < 0) { + aw_dev_err("%s: fail to create timed output dev\n", __func__); + return ret; + } + ret = sysfs_create_group(&aw86214->vib_dev.dev->kobj, + &aw86214_vibrator_attribute_group); + if (ret < 0) { + aw_dev_err("%s error creating sysfs attr files\n", + __func__); + return ret; + } +#else + aw_dev_info("%s: loaded in leds_cdev framework!\n", + __func__); + aw86214->vib_dev.name = "awinic_vibrator"; + aw86214->vib_dev.brightness_get = aw86214_haptic_brightness_get; + aw86214->vib_dev.brightness_set = aw86214_haptic_brightness_set; + + ret = devm_led_classdev_register(&aw86214->i2c->dev, &aw86214->vib_dev); + if (ret < 0) { + aw_dev_err("%s: fail to create led dev\n", __func__); + return ret; + } + ret = sysfs_create_group(&aw86214->vib_dev.dev->kobj, + &aw86214_vibrator_attribute_group); + if (ret < 0) { + aw_dev_err("%s error creating sysfs attr files\n", __func__); + return ret; + } +#endif + hrtimer_init(&aw86214->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + aw86214->timer.function = aw86214_vibrator_timer_func; + INIT_WORK(&aw86214->vibrator_work, + aw86214_vibrator_work_routine); + + mutex_init(&aw86214->lock); + mutex_init(&aw86214->ram_lock); + return 0; +} + +int aw86214_haptic_init(struct aw86214 *aw86214) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned char reg_array[8] = {0}; + + aw_dev_info("%s enter\n", __func__); + /* haptic audio */ + aw86214->haptic_audio.delay_val = 1; + aw86214->haptic_audio.timer_val = 21318; + INIT_LIST_HEAD(&(aw86214->haptic_audio.ctr_list)); + hrtimer_init(&aw86214->haptic_audio.timer, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + aw86214->haptic_audio.timer.function = aw86214_haptic_audio_timer_func; + INIT_WORK(&aw86214->haptic_audio.work, + aw86214_haptic_audio_work_routine); + mutex_init(&aw86214->haptic_audio.lock); + aw86214->gun_type = AW_GUN_TYPE_DEF_VAL; + aw86214->bullet_nr = AW_BULLET_NR_DEF_VAL; + /* haptic init */ + mutex_lock(&aw86214->lock); + aw86214->activate_mode = aw86214->dts_info.mode; + ret = aw86214_i2c_read(aw86214, AW86214_REG_WAVCFG1, ®_val, + AW_I2C_BYTE_ONE); + aw86214->index = reg_val & 0x7F; + ret = aw86214_i2c_read(aw86214, AW86214_REG_PLAYCFG2, ®_val, + AW_I2C_BYTE_ONE); + aw86214->gain = reg_val & 0xFF; + aw_dev_info("%s aw86214->gain =0x%02X\n", __func__, aw86214->gain); + aw86214_i2c_read(aw86214, AW86214_REG_WAVCFG1, reg_array, + AW86214_SEQUENCER_SIZE); + memcpy(aw86214->seq, reg_array, AW86214_SEQUENCER_SIZE); + aw86214_haptic_play_mode(aw86214, AW86214_HAPTIC_STANDBY_MODE); + aw86214_haptic_set_pwm(aw86214, AW86214_PWM_12K); + /* misc value init */ + aw86214_haptic_misc_para_init(aw86214); + /* set motor protect */ + aw86214_haptic_swicth_motor_protect_config(aw86214, 0x00, 0x00); + aw86214_haptic_offset_calibration(aw86214); + /* vbat compensation */ + aw86214_haptic_vbat_mode_config(aw86214, + AW86214_HAPTIC_CONT_VBAT_HW_ADJUST_MODE); + aw86214->ram_vbat_compensate = AW86214_HAPTIC_RAM_VBAT_COMP_ENABLE; + + /* f0 calibration */ + /*LRA trim source select register*/ + aw86214_i2c_write_bits(aw86214, + AW86214_REG_TRIMCFG1, + AW86214_BIT_TRIMCFG1_RL_TRIM_SRC_MASK, + AW86214_BIT_TRIMCFG1_RL_TRIM_SRC_REG); + if (aw86214->dts_info.lk_f0_cali) { + aw86214->f0_cali_data = aw86214->dts_info.lk_f0_cali; + aw86214_haptic_upload_lra(aw86214, AW86214_F0_CALI); + } else { + aw86214_haptic_upload_lra(aw86214, AW86214_WRITE_ZERO); + aw86214_haptic_f0_calibration(aw86214); + } + mutex_unlock(&aw86214->lock); + return ret; +} + +void aw86214_interrupt_setup(struct aw86214 *aw86214) +{ + unsigned char reg_val = 0; + + aw_dev_info("%s enter\n", __func__); + + aw86214_i2c_read(aw86214, AW86214_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_dev_info("%s: reg SYSINT=0x%02X\n", __func__, reg_val); + + /* edge int mode */ + aw86214_i2c_write_bits(aw86214, AW86214_REG_SYSCTRL7, + (AW86214_BIT_SYSCTRL7_INT_MODE_MASK & + AW86214_BIT_SYSCTRL7_INT_EDGE_MODE_MASK), + (AW86214_BIT_SYSCTRL7_INT_MODE_EDGE | + AW86214_BIT_SYSCTRL7_INT_EDGE_MODE_POS)); + /* int enable */ + aw86214_i2c_write_bits(aw86214, AW86214_REG_SYSINTM, + (AW86214_BIT_SYSINTM_UVLM_MASK & + AW86214_BIT_SYSINTM_OCDM_MASK & + AW86214_BIT_SYSINTM_OTM_MASK), + (AW86214_BIT_SYSINTM_UVLM_ON | + AW86214_BIT_SYSINTM_OCDM_ON | + AW86214_BIT_SYSINTM_OTM_ON)); +} + +char aw86214_check_qualify(struct aw86214 *aw86214) +{ + unsigned char reg = 0; + int ret = 0; + + ret = aw86214_i2c_read(aw86214, AW86214_REG_EFCFG5, ®, + AW_I2C_BYTE_ONE); + if (ret < 0) { + aw_dev_err("%s: failed to read register 0x64: %d\n", + __func__, ret); + return ret; + } + if ((reg & 0x80) == 0x80) + return 1; + + return 0; +} diff --git a/drivers/misc/awinic/aw862x/aw86214.h b/drivers/misc/awinic/aw862x/aw86214.h new file mode 100644 index 000000000000..8fdd50ab504a --- /dev/null +++ b/drivers/misc/awinic/aw862x/aw86214.h @@ -0,0 +1,618 @@ +#ifndef _AW86214_H_ +#define _AW86214_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "haptic_nv.h" + +/********************************************************* + * + * marco + * + ********************************************************/ +#define AW86214_I2C_RETRIES (5) +#define AW86214_I2C_RETRY_DELAY (2) +#define AW86214_VBAT_REFER (4200) +#define AW86214_VBAT_MIN (3000) +#define AW86214_VBAT_MAX (5500) +#define AW86214_SEQUENCER_LOOP_SIZE (4) +#define AW86214_SEQUENCER_SIZE (8) +#define AW86214_VBAT_FORMULA(code) (6100 * (code) / 1024) +#define AW86214_LRA_FORMULA(lra_code) (((lra_code) * 678 * 100) / (1024 * 10)) +#define AW86214_STOP_DELAY_MIN (2000) +#define AW86214_STOP_DELAY_MAX (2500) +#define AW86214_VBAT_DELAY_MIN (20000) +#define AW86214_VBAT_DELAY_MAX (25000) +#define AW86214_F0_DELAY_MIN (10000) +#define AW86214_F0_DELAY_MAX (10500) +#define AW86214_LRA_DELAY_MIN (30000) +#define AW86214_LRA_DELAY_MAX (35000) +/********************************************************* + * + * enum + * + ********************************************************/ +enum aw86214_flags { + AW86214_FLAG_NONR = 0, + AW86214_FLAG_SKIP_INTERRUPTS = 1, +}; + +enum aw86214_haptic_work_mode { + AW86214_HAPTIC_STANDBY_MODE = 0, + AW86214_HAPTIC_RAM_MODE = 1, + AW86214_HAPTIC_RAM_LOOP_MODE = 2, + AW86214_HAPTIC_CONT_MODE = 3, + AW86214_HAPTIC_RTP_MODE = 4, + AW86214_HAPTIC_TRIG_MODE = 5, + AW86214_HAPTIC_NULL = 6, +}; + +enum aw86214_haptic_cont_vbat_comp_mode { + AW86214_HAPTIC_CONT_VBAT_SW_ADJUST_MODE = 0, + AW86214_HAPTIC_CONT_VBAT_HW_ADJUST_MODE = 1, +}; + +enum aw86214_haptic_ram_vbat_compensate_mode { + AW86214_HAPTIC_RAM_VBAT_COMP_DISABLE = 0, + AW86214_HAPTIC_RAM_VBAT_COMP_ENABLE = 1, +}; + +enum aw86214_haptic_f0_flag { + AW86214_HAPTIC_LRA_F0 = 0, + AW86214_HAPTIC_CALI_F0 = 1, +}; + +enum aw86214_sram_size_flag { + AW86214_HAPTIC_SRAM_1K = 0, + AW86214_HAPTIC_SRAM_2K = 1, + AW86214_HAPTIC_SRAM_3K = 2, +}; + +enum aw86214_haptic_pwm_mode { + AW86214_PWM_48K = 0, + AW86214_PWM_24K = 1, + AW86214_PWM_12K = 2, +}; + +enum aw86214_haptic_play { + AW86214_HAPTIC_PLAY_NULL = 0, + AW86214_HAPTIC_PLAY_ENABLE = 1, + AW86214_HAPTIC_PLAY_STOP = 2, + AW86214_HAPTIC_PLAY_GAIN = 8, +}; + +enum aw86214_haptic_cmd { + AW86214_HAPTIC_CMD_NULL = 0, + AW86214_HAPTIC_CMD_ENABLE = 1, + AW86214_HAPTIC_CMD_HAPTIC = 0x0f, + AW86214_HAPTIC_CMD_TP = 0x10, + AW86214_HAPTIC_CMD_SYS = 0xf0, + AW86214_HAPTIC_CMD_STOP = 255, +}; + +enum aw86214_haptic_cali_lra { + AW86214_WRITE_ZERO = 0, + AW86214_F0_CALI = 1, +}; + +enum aw86214_haptic_rtp_mode { + AW86214_RTP_SHORT = 4, + AW86214_RTP_LONG = 5, + AW86214_RTP_SEGMENT = 6, +}; + +enum aw86214_awrw_flag { + AW86214_WRITE = 0, + AW86214_READ = 1, +}; +/********************************************************* + * + * struct + * + ********************************************************/ +struct aw86214_dts_info { + unsigned int lk_f0_cali; + unsigned int mode; + unsigned int f0_ref; + unsigned int f0_cali_percent; + unsigned int cont_drv1_lvl_dt; + unsigned int cont_drv2_lvl_dt; + unsigned int cont_drv1_time_dt; + unsigned int cont_drv2_time_dt; + unsigned int cont_wait_num_dt; + unsigned int cont_brk_time_dt; + unsigned int cont_track_margin; + unsigned int cont_tset; + unsigned int cont_drv_width; + unsigned int cont_bemf_set; + unsigned int cont_bst_brk_gain; + unsigned int d2s_gain; + unsigned int bstcfg[5]; + unsigned int prctmode[3]; + unsigned int duration_time[3]; + unsigned int sine_array[4]; +}; + +struct aw86214 { + cdev_t vib_dev; + + unsigned char seq[AW86214_SEQUENCER_SIZE]; + unsigned char loop[AW86214_SEQUENCER_SIZE]; + unsigned char ram_init; + unsigned char f0_cali_flag; + unsigned char ram_vbat_compensate; + unsigned char hwen_flag; + unsigned char flags; + unsigned char chipid; + unsigned char play_mode; + unsigned char activate_mode; + unsigned char duration_time_size; + + char duration_time_flag; + + int reset_gpio; + int irq_gpio; + int state; + int duration; + int amplitude; + int index; + int vmax; + int gain; + int sysclk; + int rate; + int width; + int pstream; + int cstream; + + unsigned int gun_type; + unsigned int bullet_nr; + unsigned int ram_num; + unsigned int f0; + unsigned int cont_f0; + unsigned int cont_drv1_lvl; + unsigned int cont_drv2_lvl; + unsigned int cont_brk_time; + unsigned int cont_wait_num; + unsigned int cont_drv1_time; + unsigned int cont_drv2_time; + unsigned int vbat; + unsigned int lra; + unsigned int ram_update_flag; + unsigned int f0_cali_data; + unsigned int sys_frequency; + + struct regmap *regmap; + struct i2c_client *i2c; + struct device *dev; + struct mutex lock; + struct mutex ram_lock; + struct hrtimer timer; + struct work_struct vibrator_work; + struct delayed_work ram_work; + struct aw86214_dts_info dts_info; + struct ram ram; + struct haptic_audio haptic_audio; + struct aw_i2c_package aw_i2c_package; +}; + +struct aw86214_container { + int len; + unsigned char data[]; +}; + +/********************************************************* + * + * extern + * + ********************************************************/ +extern char aw86214_check_qualify(struct aw86214 *aw86214); +extern int aw86214_parse_dt(struct aw86214 *aw86214, struct device *dev, + struct device_node *np); +extern void aw86214_interrupt_setup(struct aw86214 *aw86214); +extern int aw86214_vibrator_init(struct aw86214 *aw86214); +extern int aw86214_haptic_init(struct aw86214 *aw86214); +extern int aw86214_ram_work_init(struct aw86214 *aw86214); +extern irqreturn_t aw86214_irq(int irq, void *data); + +extern struct attribute_group aw86214_vibrator_attribute_group; + +/******************************************** + * Register List + *******************************************/ +#define AW86214_REG_ID 0x00 +#define AW86214_REG_SYSST 0x01 +#define AW86214_REG_SYSINT 0x02 +#define AW86214_REG_SYSINTM 0x03 +#define AW86214_REG_SYSST2 0x04 +#define AW86214_REG_PLAYCFG2 0x07 +#define AW86214_REG_PLAYCFG3 0x08 +#define AW86214_REG_PLAYCFG4 0x09 +#define AW86214_REG_WAVCFG1 0x0A +#define AW86214_REG_WAVCFG2 0x0B +#define AW86214_REG_WAVCFG3 0x0C +#define AW86214_REG_WAVCFG4 0x0D +#define AW86214_REG_WAVCFG5 0x0E +#define AW86214_REG_WAVCFG6 0x0F +#define AW86214_REG_WAVCFG7 0x10 +#define AW86214_REG_WAVCFG8 0x11 +#define AW86214_REG_WAVCFG9 0x12 +#define AW86214_REG_WAVCFG10 0x13 +#define AW86214_REG_WAVCFG11 0x14 +#define AW86214_REG_WAVCFG12 0x15 +#define AW86214_REG_WAVCFG13 0x16 +#define AW86214_REG_CONTCFG1 0x18 +#define AW86214_REG_CONTCFG2 0x19 +#define AW86214_REG_CONTCFG3 0x1A +#define AW86214_REG_CONTCFG4 0x1B +#define AW86214_REG_CONTCFG5 0x1C +#define AW86214_REG_CONTCFG6 0x1D +#define AW86214_REG_CONTCFG7 0x1E +#define AW86214_REG_CONTCFG8 0x1F +#define AW86214_REG_CONTCFG9 0x20 +#define AW86214_REG_CONTCFG10 0x21 +#define AW86214_REG_CONTCFG11 0x22 +#define AW86214_REG_CONTCFG13 0x24 +#define AW86214_REG_CONTRD14 0x25 +#define AW86214_REG_CONTRD15 0x26 +#define AW86214_REG_CONTRD16 0x27 +#define AW86214_REG_CONTRD17 0x28 +#define AW86214_REG_RTPCFG1 0x2D +#define AW86214_REG_RTPCFG2 0x2E +#define AW86214_REG_RTPCFG3 0x2F +#define AW86214_REG_RTPCFG4 0x30 +#define AW86214_REG_RTPCFG5 0x31 +#define AW86214_REG_TRGCFG1 0x33 +#define AW86214_REG_TRGCFG4 0x36 +#define AW86214_REG_TRGCFG7 0x39 +#define AW86214_REG_TRGCFG8 0x3A +#define AW86214_REG_GLBCFG4 0x3E +#define AW86214_REG_GLBRD5 0x3F +#define AW86214_REG_RAMADDRH 0x40 +#define AW86214_REG_RAMADDRL 0x41 +#define AW86214_REG_RAMDATA 0x42 +#define AW86214_REG_SYSCTRL1 0x43 +#define AW86214_REG_SYSCTRL2 0x44 +#define AW86214_REG_SYSCTRL3 0x45 +#define AW86214_REG_SYSCTRL4 0x46 +#define AW86214_REG_SYSCTRL5 0x47 +#define AW86214_REG_SYSCTRL6 0x48 +#define AW86214_REG_SYSCTRL7 0x49 +#define AW86214_REG_PWMCFG1 0x4C +#define AW86214_REG_PWMCFG3 0x4E +#define AW86214_REG_PWMCFG4 0x4F +#define AW86214_REG_DETCFG1 0x51 +#define AW86214_REG_DETCFG2 0x52 +#define AW86214_REG_DET_RL 0x53 +#define AW86214_REG_DET_VBAT 0x55 +#define AW86214_REG_DET_LO 0x57 +#define AW86214_REG_TRIMCFG1 0x58 +#define AW86214_REG_TRIMCFG3 0x5A +#define AW86214_REG_EFCFG5 0x60 +#define AW86214_REG_ANACFG8 0x77 +/****************************************************** + * Register Detail + *****************************************************/ +/* SYSST: reg 0x01 RO */ +#define AW86214_BIT_SYSST_BST_SCPS (1<<7) +#define AW86214_BIT_SYSST_BST_OVPS (1<<6) +#define AW86214_BIT_SYSST_UVLS (1<<5) +#define AW86214_BIT_SYSST_FF_AES (1<<4) +#define AW86214_BIT_SYSST_FF_AFS (1<<3) +#define AW86214_BIT_SYSST_OCDS (1<<2) +#define AW86214_BIT_SYSST_OTS (1<<1) +#define AW86214_BIT_SYSST_DONES (1<<0) + +/* SYSINT: reg 0x02 RC */ +#define AW86214_BIT_SYSINT_BST_SCPI (1<<7) +#define AW86214_BIT_SYSINT_BST_OVPI (1<<6) +#define AW86214_BIT_SYSINT_UVLI (1<<5) +#define AW86214_BIT_SYSINT_FF_AEI (1<<4) +#define AW86214_BIT_SYSINT_FF_AFI (1<<3) +#define AW86214_BIT_SYSINT_OCDI (1<<2) +#define AW86214_BIT_SYSINT_OTI (1<<1) +#define AW86214_BIT_SYSINT_DONEI (1<<0) + +/* SYSINTM: reg 0x03 RW */ +#define AW86214_BIT_SYSINTM_BST_SCPM_MASK (~(1<<7)) +#define AW86214_BIT_SYSINTM_BST_SCPM_OFF (1<<7) +#define AW86214_BIT_SYSINTM_BST_SCPM_ON (0<<7) +#define AW86214_BIT_SYSINTM_BST_OVPM_MASK (~(1<<6)) +#define AW86214_BIT_SYSINTM_BST_OVPM_OFF (1<<6) +#define AW86214_BIT_SYSINTM_BST_OVPM_ON (0<<6) +#define AW86214_BIT_SYSINTM_UVLM_MASK (~(1<<5)) +#define AW86214_BIT_SYSINTM_UVLM_OFF (1<<5) +#define AW86214_BIT_SYSINTM_UVLM_ON (0<<5) +#define AW86214_BIT_SYSINTM_FF_AEM_MASK (~(1<<4)) +#define AW86214_BIT_SYSINTM_FF_AEM_OFF (1<<4) +#define AW86214_BIT_SYSINTM_FF_AEM_ON (0<<4) +#define AW86214_BIT_SYSINTM_FF_AFM_MASK (~(1<<3)) +#define AW86214_BIT_SYSINTM_FF_AFM_OFF (1<<3) +#define AW86214_BIT_SYSINTM_FF_AFM_ON (0<<3) +#define AW86214_BIT_SYSINTM_OCDM_MASK (~(1<<2)) +#define AW86214_BIT_SYSINTM_OCDM_OFF (1<<2) +#define AW86214_BIT_SYSINTM_OCDM_ON (0<<2) +#define AW86214_BIT_SYSINTM_OTM_MASK (~(1<<1)) +#define AW86214_BIT_SYSINTM_OTM_OFF (1<<1) +#define AW86214_BIT_SYSINTM_OTM_ON (0<<1) +#define AW86214_BIT_SYSINTM_DONEM_MASK (~(1<<0)) +#define AW86214_BIT_SYSINTM_DONEM_OFF (1<<0) +#define AW86214_BIT_SYSINTM_DONEM_ON (0<<0) + +/* PLAYCFG3: reg 0x08 RW */ +#define AW86214_BIT_PLAYCFG3_AUTO_BST_MASK (~(1<<6)) +#define AW86214_BIT_PLAYCFG3_AUTO_BST_ENABLE (1<<6) +#define AW86214_BIT_PLAYCFG3_AUTO_BST_DISABLE (0<<6) +#define AW86214_BIT_PLAYCFG3_STOP_MODE_MASK (~(1<<5)) +#define AW86214_BIT_PLAYCFG3_STOP_MODE_NOW (1<<5) +#define AW86214_BIT_PLAYCFG3_STOP_MODE_LATER (0<<5) +#define AW86214_BIT_PLAYCFG3_BRK_EN_MASK (~(1<<2)) +#define AW86214_BIT_PLAYCFG3_BRK (1<<2) +#define AW86214_BIT_PLAYCFG3_BRK_ENABLE (1<<2) +#define AW86214_BIT_PLAYCFG3_BRK_DISABLE (0<<2) +#define AW86214_BIT_PLAYCFG3_PLAY_MODE_MASK (~(3<<0)) +#define AW86214_BIT_PLAYCFG3_PLAY_MODE_STOP (3<<0) +#define AW86214_BIT_PLAYCFG3_PLAY_MODE_CONT (2<<0) +#define AW86214_BIT_PLAYCFG3_PLAY_MODE_RTP (1<<0) +#define AW86214_BIT_PLAYCFG3_PLAY_MODE_RAM (0<<0) + +/* PLAYCFG4: reg 0x09 RW */ +#define AW86214_BIT_PLAYCFG4_STOP_MASK (~(1<<1)) +#define AW86214_BIT_PLAYCFG4_STOP_ON (1<<1) +#define AW86214_BIT_PLAYCFG4_STOP_OFF (0<<1) +#define AW86214_BIT_PLAYCFG4_GO_MASK (~(1<<0)) +#define AW86214_BIT_PLAYCFG4_GO_ON (1<<0) +#define AW86214_BIT_PLAYCFG4_GO_OFF (0<<0) + +/* WAVCFG1-8: reg 0x0A - reg 0x11 RW */ +#define AW86214_BIT_WAVCFG_SEQWAIT_MASK (~(1<<7)) +#define AW86214_BIT_WAVCFG_SEQWAIT_TIME (1<<7) +#define AW86214_BIT_WAVCFG_SEQWAIT_NUMBER (0<<7) + +/* WAVCFG9-12: reg 0x12 - reg 0x15 RW */ +#define AW86214_BIT_WAVLOOP_SEQ_ODD_MASK (~(0x0F<<4)) +#define AW86214_BIT_WAVLOOP_SEQ_ODD_INIFINITELY (0x0F<<4) +#define AW86214_BIT_WAVLOOP_SEQ_EVEN_MASK (~(0x0F<<0)) +#define AW86214_BIT_WAVLOOP_SEQ_EVEN_INIFINITELY (0x0F<<0) +#define AW86214_BIT_WAVLOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG9: reg 0x12 RW */ +#define AW86214_BIT_WAVCFG9_SEQ1LOOP_MASK (~(0x0F<<4)) +#define AW86214_BIT_WAVCFG9_SEQ1LOOP_INIFINITELY (0x0F<<4) +#define AW86214_BIT_WAVCFG9_SEQ2LOOP_MASK (~(0x0F<<0)) +#define AW86214_BIT_WAVCFG9_SEQ2LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG10: reg 0x13 RW */ +#define AW86214_BIT_WAVCFG10_SEQ3LOOP_MASK (~(0x0F<<4)) +#define AW86214_BIT_WAVCFG10_SEQ3LOOP_INIFINITELY (0x0F<<4) +#define AW86214_BIT_WAVCFG10_SEQ4LOOP_MASK (~(0x0F<<0)) +#define AW86214_BIT_WAVCFG10_SEQ4LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG11: reg 0x14 RW */ +#define AW86214_BIT_WAVCFG11_SEQ5LOOP_MASK (~(0x0F<<4)) +#define AW86214_BIT_WAVCFG11_SEQ5LOOP_INIFINITELY (0x0F<<4) +#define AW86214_BIT_WAVCFG11_SEQ6LOOP_MASK (~(0x0F<<0)) +#define AW86214_BIT_WAVCFG11_SEQ6LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG12: reg 0x15 RW */ +#define AW86214_BIT_WAVCFG12_SEQ7LOOP_MASK (~(0x0F<<4)) +#define AW86214_BIT_WAVCFG12_SEQ7LOOP_INIFINITELY (0x0F<<4) +#define AW86214_BIT_WAVCFG12_SEQ8LOOP_MASK (~(0x0F<<0)) +#define AW86214_BIT_WAVCFG12_SEQ8LOOP_INIFINITELY (0x0F<<0) + +/***************** CONT *****************/ +/* CONTCFG1: reg 0x18 RW */ +#define AW86214_BIT_CONTCFG1_EDGE_FRE_MASK (~(0x0F<<4)) +#define AW86214_BIT_CONTCFG1_EN_F0_DET_MASK (~(1<<3)) +#define AW86214_BIT_CONTCFG1_F0_DET_ENABLE (1<<3) +#define AW86214_BIT_CONTCFG1_F0_DET_DISABLE (0<<3) +#define AW86214_BIT_CONTCFG1_MBRK_MASK (~(1<<2)) +#define AW86214_BIT_CONTCFG1_MBRK_ENABLE (1<<2) +#define AW86214_BIT_CONTCFG1_MBRK_DISABLE (0<<2) +#define AW86214_BIT_CONTCFG1_BRK_BST_MD_MASK (~(1<<1)) +#define AW86214_BIT_CONTCFG1_BRK_BST_MD_ENABLE (1<<1) +#define AW86214_BIT_CONTCFG1_BRK_BST_MD_DISABLE (0<<1) +#define AW86214_BIT_CONTCFG1_SIN_MODE_MASK (~(1<<0)) +#define AW86214_BIT_CONTCFG1_SIN_MODE_COS (1<<0) +#define AW86214_BIT_CONTCFG1_SIN_MODE_SINE (0<<0) + +/* CONTCFG5: reg 0x1C RW */ +#define AW86214_BIT_CONTCFG5_BST_BRK_GAIN_MASK (~(0x0F<<4)) +#define AW86214_BIT_CONTCFG5_BRK_GAIN_MASK (~(0x0F<<0)) + +/* CONTCFG6: reg 0x1D RW */ +#define AW86214_BIT_CONTCFG6_TRACK_EN_MASK (~(1<<7)) +#define AW86214_BIT_CONTCFG6_TRACK_ENABLE (1<<7) +#define AW86214_BIT_CONTCFG6_TRACK_DISABLE (0<<7) +#define AW86214_BIT_CONTCFG6_DRV1_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG7: reg 0x1E RW */ +#define AW86214_BIT_CONTCFG7_DRV2_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG13: reg 0x24 RW */ +#define AW86214_BIT_CONTCFG13_TSET_MASK (~(0x0F<<4)) +#define AW86214_BIT_CONTCFG13_BEME_SET_MASK (~(0x0F<<0)) + +/***************** RTP *****************/ +/* RTPCFG1: reg 0x2D RW */ +#define AW86214_BIT_RTPCFG1_ADDRH_MASK (~(0x0F<<0)) + +#define AW86214_BIT_RTPCFG1_SRAM_SIZE_2K_MASK (~(1<<5)) +#define AW86214_BIT_RTPCFG1_SRAM_SIZE_2K_EN (1<<5) +#define AW86214_BIT_RTPCFG1_SRAM_SIZE_2K_DIS (0<<5) + +#define AW86214_BIT_RTPCFG1_SRAM_SIZE_1K_MASK (~(1<<4)) +#define AW86214_BIT_RTPCFG1_SRAM_SIZE_1K_EN (1<<4) +#define AW86214_BIT_RTPCFG1_SRAM_SIZE_1K_DIS (0<<4) + +/* GLBRD5: reg 0x3F R0 */ +/* GLB_STATE */ +#define AW86214_BIT_GLBRD5_STATE (15<<0) +#define AW86214_BIT_GLBRD5_STATE_STANDBY (0<<0) +#define AW86214_BIT_GLBRD5_STATE_WAKEUP (1<<0) +#define AW86214_BIT_GLBRD5_STATE_STARTUP (2<<0) +#define AW86214_BIT_GLBRD5_STATE_WAIT (3<<0) +#define AW86214_BIT_GLBRD5_STATE_CONT_GO (6<<0) +#define AW86214_BIT_GLBRD5_STATE_RAM_GO (7<<0) +#define AW86214_BIT_GLBRD5_STATE_RTP_GO (8<<0) +#define AW86214_BIT_GLBRD5_STATE_TRIG_GO (9<<0) +#define AW86214_BIT_GLBRD5_STATE_I2S_GO (10<<0) +#define AW86214_BIT_GLBRD5_STATE_BRAKE (11<<0) +#define AW86214_BIT_GLBRD5_STATE_END (12<<0) +/* RAMADDRH: reg 0x40 RWS */ +#define AW86214_BIT_RAMADDRH_MASK (~(63<<0)) + +/* RAMADDRL: reg 0x41 RWS */ +/* RAMADDRL */ + +/* RAMDATA: reg 0x42 RWS */ +/* RAMDATA */ + +/***************** SYSCTRL *****************/ +/* SYSCTRL1: reg 0x43 RW */ +#define AW86214_BIT_SYSCTRL1_VBAT_MODE_MASK (~(1<<7)) +#define AW86214_BIT_SYSCTRL1_VBAT_MODE_HW (1<<7) +#define AW86214_BIT_SYSCTRL1_VBAT_MODE_SW (0<<7) +#define AW86214_BIT_SYSCTRL1_PERP_MASK (~(1<<6)) +#define AW86214_BIT_SYSCTRL1_PERP_ON (1<<6) +#define AW86214_BIT_SYSCTRL1_PERP_OFF (0<<6) +#define AW86214_BIT_SYSCTRL1_CLK_SEL_MASK (~(3<<4)) +#define AW86214_BIT_SYSCTRL1_CLK_SEL_OSC (1<<4) +#define AW86214_BIT_SYSCTRL1_CLK_SEL_AUTO (0<<4) +#define AW86214_BIT_SYSCTRL1_RAMINIT_MASK (~(1<<3)) +#define AW86214_BIT_SYSCTRL1_RAMINIT_ON (1<<3) +#define AW86214_BIT_SYSCTRL1_RAMINIT_OFF (0<<3) +#define AW86214_BIT_SYSCTRL1_EN_FIR_MASK (~(1<<2)) +#define AW86214_BIT_SYSCTRL1_FIR_ENABLE (0<<2) +#define AW86214_BIT_SYSCTRL1_WAKE_MODE_MASK (~(1<<1)) +#define AW86214_BIT_SYSCTRL1_WAKE_MODE_WAKEUP (1<<1) +#define AW86214_BIT_SYSCTRL1_WAKE_MODE_BST (0<<1) +#define AW86214_BIT_SYSCTRL1_RTP_CLK_MASK (~(1<<0)) +#define AW86214_BIT_SYSCTRL1_RTP_PLL (1<<0) +#define AW86214_BIT_SYSCTRL1_RTP_OSC (0<<0) + +/* SYSCTRL2: reg 0x44 RW */ +#define AW86214_BIT_SYSCTRL2_WAKE_MASK (~(1<<7)) +#define AW86214_BIT_SYSCTRL2_WAKE_ON (1<<7) +#define AW86214_BIT_SYSCTRL2_WAKE_OFF (0<<7) +#define AW86214_BIT_SYSCTRL2_STANDBY_MASK (~(1<<6)) +#define AW86214_BIT_SYSCTRL2_STANDBY_ON (1<<6) +#define AW86214_BIT_SYSCTRL2_STANDBY_OFF (0<<6) +#define AW86214_BIT_SYSCTRL2_RTP_DLY_MASK (~(3<<4)) +#define AW86214_BIT_SYSCTRL2_PLL_PIN_MASK (~(1<<3)) +#define AW86214_BIT_SYSCTRL2_PLL_PIN_TEST (1<<3) +#define AW86214_BIT_SYSCTRL2_I2S_PIN_MASK (~(1<<2)) +#define AW86214_BIT_SYSCTRL2_I2S_PIN_I2S (1<<2) +#define AW86214_BIT_SYSCTRL2_I2S_PIN_TRIG (0<<2) +#define AW86214_BIT_SYSCTRL2_WAVDAT_MODE_MASK (~(3<<0)) +#define AW86214_BIT_SYSCTRL2_RATE_12K (2<<0) +#define AW86214_BIT_SYSCTRL2_RATE_24K (0<<0) +#define AW86214_BIT_SYSCTRL2_RATE_48K (1<<0) + +/* SYSCTRL7: reg 0x49 RW */ +#define AW86214_BIT_SYSCTRL7_GAIN_BYPASS_MASK (~(1<<6)) +#define AW86214_BIT_SYSCTRL7_GAIN_CHANGEABLE (1<<6) +#define AW86214_BIT_SYSCTRL7_GAIN_FIXED (0<<6) + +#define AW86214_BIT_SYSCTRL7_INT_EDGE_MODE_MASK (~(1<<5)) +#define AW86214_BIT_SYSCTRL7_INT_EDGE_MODE_POS (0<<5) +#define AW86214_BIT_SYSCTRL7_INT_EDGE_MODE_BOTH (1<<5) +#define AW86214_BIT_SYSCTRL7_INT_MODE_MASK (~(1<<4)) +#define AW86214_BIT_SYSCTRL7_INT_MODE_EDGE (1<<4) +#define AW86214_BIT_SYSCTRL7_INT_MODE_LEVEL (0<<4) + +#define AW86214_BIT_SYSCTRL7_INTP_MASK (~(1<<3)) +#define AW86214_BIT_SYSCTRL7_INTP_HIGH (1<<3) +#define AW86214_BIT_SYSCTRL7_INTP_LOW (0<<3) +#define AW86214_BIT_SYSCTRL7_D2S_GAIN_MASK (~(7<<0)) +#define AW86214_BIT_SYSCTRL7_D2S_GAIN_1 (0<<0) +#define AW86214_BIT_SYSCTRL7_D2S_GAIN_2 (1<<0) +#define AW86214_BIT_SYSCTRL7_D2S_GAIN_4 (2<<0) +#define AW86214_BIT_SYSCTRL7_D2S_GAIN_8 (3<<0) +#define AW86214_BIT_SYSCTRL7_D2S_GAIN_10 (4<<0) +#define AW86214_BIT_SYSCTRL7_D2S_GAIN_16 (5<<0) +#define AW86214_BIT_SYSCTRL7_D2S_GAIN_20 (6<<0) +#define AW86214_BIT_SYSCTRL7_D2S_GAIN_40 (7<<0) + +/* PWMCFG1: reg 0x4C RW */ +#define AW86214_BIT_PWMCFG1_PRC_EN_MASK (~(1<<7)) +#define AW86214_BIT_PWMCFG1_PRC_ENABLE (1<<7) +#define AW86214_BIT_PWMCFG1_PRC_DISABLE (0<<7) +#define AW86214_BIT_PWMCFG1_PRCTIME_MASK (~(0x7F<<0)) + +/* PWMCFG2: reg 0x4D RW */ +#define AW86214_BIT_PWMCFG2_REF_SEL_MASK (~(1<<5)) +#define AW86214_BIT_PWMCFG2_REF_SEL_TRIANGLE (1<<5) +#define AW86214_BIT_PWMCFG2_REF_SEL_SAWTOOTH (0<<5) +#define AW86214_BIT_PWMCFG2_PD_HWM_MASK (~(1<<4)) +#define AW86214_BIT_PWMCFG2_PD_HWM_ON (1<<4) +#define AW86214_BIT_PWMCFG2_PWMOE_MASK (~(1<<3)) +#define AW86214_BIT_PWMCFG2_PWMOE_ON (1<<3) +#define AW86214_BIT_PWMCFG2_PWMFRC_MASK (~(7<<0)) + +/* PWMCFG3: reg 0x4E RW */ +#define AW86214_BIT_PWMCFG3_PR_EN_MASK (~(1<<7)) +#define AW86214_BIT_PWMCFG3_PR_ENABLE (1<<7) +#define AW86214_BIT_PWMCFG3_PR_DISABLE (0<<7) +#define AW86214_BIT_PWMCFG3_PRLVL_MASK (~(0x7F<<0)) + +/* DETCFG1: reg 0x51 RW */ +#define AW86214_BIT_DETCFG1_FTS_GO_MASK (~(1<<7)) +#define AW86214_BIT_DETCFG1_FTS_GO_ENABLE (1<<7) +#define AW86214_BIT_DETCFG1_TEST_GO_MASK (~(1<<6)) +#define AW86214_BIT_DETCFG1_TEST_GO_ENABLE (1<<6) +#define AW86214_BIT_DETCFG1_ADO_SLOT_MODE_MASK (~(1<<5)) +#define AW86214_BIT_DETCFG1_ADO_SLOT_ADC_32 (1<<5) +#define AW86214_BIT_DETCFG1_ADO_SLOT_ADC_256 (0<<5) +#define AW86214_BIT_DETCFG1_RL_OS_MASK (~(1<<4)) +#define AW86214_BIT_DETCFG1_RL (1<<4) +#define AW86214_BIT_DETCFG1_OS (0<<4) +#define AW86214_BIT_DETCFG1_PRCT_MODE_MASK (~(1<<3)) +#define AW86214_BIT_DETCFG1_PRCT_MODE_INVALID (1<<3) +#define AW86214_BIT_DETCFG1_PRCT_MODE_VALID (0<<3) +#define AW86214_BIT_DETCFG1_CLK_ADC_MASK (~(7<<0)) +#define AW86214_BIT_DETCFG1_CLK_ADC_12M (0<<0) +#define AW86214_BIT_DETCFG1_CLK_ADC_6M (1<<0) +#define AW86214_BIT_DETCFG1_CLK_ADC_3M (2<<0) +#define AW86214_BIT_DETCFG1_CLK_ADC_1M5 (3<<0) +#define AW86214_BIT_DETCFG1_CLK_ADC_M75 (4<<0) +#define AW86214_BIT_DETCFG1_CLK_ADC_M37 (5<<0) +#define AW86214_BIT_DETCFG1_CLK_ADC_M18 (6<<0) +#define AW86214_BIT_DETCFG1_CLK_ADC_M09 (7<<0) + +/* DETCFG2: reg 0x52 RW */ +#define AW86214_BIT_DETCFG2_VBAT_GO_MASK (~(1<<1)) +#define AW86214_BIT_DETCFG2_VABT_GO_ON (1<<1) +#define AW86214_BIT_DETCFG2_DIAG_GO_MASK (~(1<<0)) +#define AW86214_BIT_DETCFG2_DIAG_GO_ON (1<<0) + +/* DET_LO: reg 0x57 RW */ +#define AW86214_BIT_DET_LO_TEST_MASK (~(3<<6)) +#define AW86214_BIT_DET_LO_VBAT_MASK (~(3<<4)) +#define AW86214_BIT_DET_LO_OS_MASK (~(3<<2)) +#define AW86214_BIT_DET_LO_RL_MASK (~(3<<0)) +#define AW86214_BIT_DET_LO_VBAT (3<<4) +#define AW86214_BIT_DET_LO_RL (3<<0) + +/* TRIMCFG1: reg:0x58 RW */ +#define AW86214_BIT_TRIMCFG1_RL_TRIM_SRC_MASK (~(1<<6)) +#define AW86214_BIT_TRIMCFG1_RL_TRIM_SRC_REG (1<<6) +#define AW86214_BIT_TRIMCFG1_RL_TRIM_SRC_EFUSE (0<<6) +#define AW86214_BIT_TRIMCFG1_TRIM_RL_MASK (~(63<<0)) + +/* TRIMCFG3: reg:0x5A RW */ +#define AW86214_BIT_TRIMCFG3_OSC_TRIM_SRC_MASK (~(1<<7)) +#define AW86214_BIT_TRIMCFG3_OSC_TRIM_SRC_REG (1<<7) +#define AW86214_BIT_TRIMCFG3_OSC_TRIM_SRC_EFUSE (0<<7) +#define AW86214_BIT_TRIMCFG3_LRA_TRIM_SRC_MASK (~(1<<6)) +#define AW86214_BIT_TRIMCFG3_LRA_TRIM_SRC_REG (1<<6) +#define AW86214_BIT_TRIMCFG3_LRA_TRIM_SRC_EFUSE (0<<6) +#define AW86214_BIT_TRIMCFG3_TRIM_LRA_MASK (~(63<<0)) + +#endif diff --git a/drivers/misc/awinic/aw862x/aw8622x.c b/drivers/misc/awinic/aw862x/aw8622x.c new file mode 100644 index 000000000000..67393bb56d17 --- /dev/null +++ b/drivers/misc/awinic/aw862x/aw8622x.c @@ -0,0 +1,4339 @@ +/* + * aw8622x.c + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aw8622x.h" +#include "haptic_nv.h" + +#define AW8622X_Driver "V1.5.1.1" + +/****************************************************** + * + * Register Access + * + ******************************************************/ +const unsigned char aw8622x_reg_list[] = { + AW8622X_REG_ID, + AW8622X_REG_SYSST, + AW8622X_REG_SYSINT, + AW8622X_REG_SYSINTM, + AW8622X_REG_SYSST2, + AW8622X_REG_SYSER, + AW8622X_REG_PLAYCFG2, + AW8622X_REG_PLAYCFG3, + AW8622X_REG_PLAYCFG4, + AW8622X_REG_WAVCFG1, + AW8622X_REG_WAVCFG2, + AW8622X_REG_WAVCFG3, + AW8622X_REG_WAVCFG4, + AW8622X_REG_WAVCFG5, + AW8622X_REG_WAVCFG6, + AW8622X_REG_WAVCFG7, + AW8622X_REG_WAVCFG8, + AW8622X_REG_WAVCFG9, + AW8622X_REG_WAVCFG10, + AW8622X_REG_WAVCFG11, + AW8622X_REG_WAVCFG12, + AW8622X_REG_WAVCFG13, + AW8622X_REG_CONTCFG1, + AW8622X_REG_CONTCFG2, + AW8622X_REG_CONTCFG3, + AW8622X_REG_CONTCFG4, + AW8622X_REG_CONTCFG5, + AW8622X_REG_CONTCFG6, + AW8622X_REG_CONTCFG7, + AW8622X_REG_CONTCFG8, + AW8622X_REG_CONTCFG9, + AW8622X_REG_CONTCFG10, + AW8622X_REG_CONTCFG11, + AW8622X_REG_CONTCFG13, + AW8622X_REG_CONTRD14, + AW8622X_REG_CONTRD15, + AW8622X_REG_CONTRD16, + AW8622X_REG_CONTRD17, + AW8622X_REG_RTPCFG1, + AW8622X_REG_RTPCFG2, + AW8622X_REG_RTPCFG3, + AW8622X_REG_RTPCFG4, + AW8622X_REG_RTPCFG5, + AW8622X_REG_TRGCFG1, + AW8622X_REG_TRGCFG2, + AW8622X_REG_TRGCFG3, + AW8622X_REG_TRGCFG4, + AW8622X_REG_TRGCFG5, + AW8622X_REG_TRGCFG6, + AW8622X_REG_TRGCFG7, + AW8622X_REG_TRGCFG8, + AW8622X_REG_GLBCFG4, + AW8622X_REG_GLBRD5, + AW8622X_REG_RAMADDRH, + AW8622X_REG_RAMADDRL, + AW8622X_REG_SYSCTRL1, + AW8622X_REG_SYSCTRL2, + AW8622X_REG_SYSCTRL3, + AW8622X_REG_SYSCTRL4, + AW8622X_REG_SYSCTRL5, + AW8622X_REG_SYSCTRL6, + AW8622X_REG_SYSCTRL7, + AW8622X_REG_PWMCFG1, + AW8622X_REG_PWMCFG3, + AW8622X_REG_PWMCFG4, + AW8622X_REG_DETCFG1, + AW8622X_REG_DETCFG2, + AW8622X_REG_DET_RL, + AW8622X_REG_DET_VBAT, + AW8622X_REG_DET_LO, + AW8622X_REG_TRIMCFG1, + AW8622X_REG_TRIMCFG3, +}; + +/****************************************************** + * + * value + * + ******************************************************/ +static char *aw8622x_ram_name = "aw8622x_haptic.bin"; +static char aw8622x_rtp_name[][AW8622X_RTP_NAME_MAX] = { + {"aw8622x_osc_rtp_12K_10s.bin"}, + {"aw8622x_rtp.bin"}, + {"aw8622x_rtp_lighthouse.bin"}, + {"aw8622x_rtp_silk.bin"}, +}; + + +static char aw8622x_moto_rtp_name[][AW8622X_RTP_NAME_MAX] = { + {"aw8622x_rtp.bin"}, + {"aw8622x_rtp_Argo_Navis.bin"}, + {"aw8622x_rtp_Attentive.bin"}, + {"aw8622x_rtp_Awake.bin"}, + {"aw8622x_rtp_Bird_Loop.bin"}, + {"aw8622x_rtp_Brilliant_Times.bin"}, + {"aw8622x_rtp_Chimey_Phone.bin"}, + {"aw8622x_rtp_Complex.bin"}, + {"aw8622x_rtp_Crazy_Dream.bin"}, + {"aw8622x_rtp_Curve_Ball_Blend.bin"}, + {"aw8622x_rtp_Digital_Phone.bin"}, + {"aw8622x_rtp_Electrovision.bin"}, + {"aw8622x_rtp_Ether_Shake.bin"}, + {"aw8622x_rtp_Fateful_Words.bin"}, + {"aw8622x_rtp_Flutey_Phone.bin"}, + {"aw8622x_rtp_Future_Funk.bin"}, + {"aw8622x_rtp_Future_Hi_Tech.bin"}, + {"aw8622x_rtp_Girtab.bin"}, + {"aw8622x_rtp_Hello.bin"}, + {"aw8622x_rtp_Hexagon.bin"}, + {"aw8622x_rtp_Hydra.bin"}, + {"aw8622x_rtp_Insert_Coin.bin"}, + {"aw8622x_rtp_Jumping_Dots.bin"}, + {"aw8622x_rtp_Keys.bin"}, + {"aw8622x_rtp_Loopy.bin"}, + {"aw8622x_rtp_Loopy_Lounge.bin"}, + {"aw8622x_rtp_Modular.bin"}, + {"aw8622x_rtp_Momentum.bin"}, + {"aw8622x_rtp_Morning.bin"}, + {"aw8622x_rtp_Moto.bin"}, + {"aw8622x_rtp_Natural.bin"}, + {"aw8622x_rtp_New_Player.bin"}, + {"aw8622x_rtp_Onward.bin"}, + {"aw8622x_rtp_Organ_Dub.bin"}, + {"aw8622x_rtp_Overclocked.bin"}, + {"aw8622x_rtp_Pegasus.bin"}, + {"aw8622x_rtp_Pyxis.bin"}, + {"aw8622x_rtp_Regrade.bin"}, + {"aw8622x_rtp_Scarabaeus.bin"}, + {"aw8622x_rtp_Sceptrum.bin"}, + {"aw8622x_rtp_Simple.bin"}, + {"aw8622x_rtp_Solarium.bin"}, + {"aw8622x_rtp_Sparse.bin"}, + {"aw8622x_rtp_Terrabytes.bin"}, + {"aw8622x_rtp_Zero_Hour.bin"}, + {"aw8622x_rtp_Play.bin"}, + {"aw8622x_rtp_TJINGLE.bin"}, + {"aw8622x_rtp_Verizon_Airwaves.bin"}, + {"aw8622x_rtp_City_Lights.bin"}, + {"aw8622x_rtp_Firefly.bin"}, + {"aw8622x_rtp_Now_or_Never.bin"}, + {"aw8622x_rtp_Moto_Retro.bin"}, + {"aw8622x_rtp_rtp_Moto_Original.bin"}, +}; + +struct pm_qos_request aw8622x_pm_qos_req_vb; + static void + aw8622x_haptic_auto_brk_enable(struct aw8622x *aw8622x, unsigned char flag); + + + /****************************************************** + * + * aw8622x i2c write/read + * + ******************************************************/ +static int aw8622x_i2c_read(struct aw8622x *aw8622x, unsigned char reg_addr, + unsigned char *buf, unsigned int len) +{ + int ret = 0; + + struct i2c_msg msg[2] = { + [0] = { + .addr = aw8622x->i2c->addr, + .flags = 0, + .len = sizeof(uint8_t), + .buf = ®_addr, + }, + [1] = { + .addr = aw8622x->i2c->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + }, + }; + + ret = i2c_transfer(aw8622x->i2c->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) { + aw_dev_err("%s: i2c_transfer failed\n", __func__); + return ret; + } else if (ret != AW_I2C_READ_MSG_NUM) { + aw_dev_err("%s: transfer failed(size error)\n", __func__); + return -ENXIO; + } + return ret; +} + +static int aw8622x_i2c_write(struct aw8622x *aw8622x, unsigned char reg_addr, + unsigned char *buf, unsigned int len) +{ + unsigned char *data = NULL; + int ret = -1; + + data = kmalloc(len + 1, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + data[0] = reg_addr; + memcpy(&data[1], buf, len); + ret = i2c_master_send(aw8622x->i2c, data, len + 1); + if (ret < 0) + aw_dev_err("%s: i2c master send 0x%02x error\n", + __func__, reg_addr); + kfree(data); + return ret; +} + +static int +aw8622x_i2c_write_bits(struct aw8622x *aw8622x, unsigned char reg_addr, + unsigned int mask, + unsigned char reg_data) +{ + unsigned char reg_val = 0; + int ret = -1; + + ret = aw8622x_i2c_read(aw8622x, reg_addr, ®_val, AW_I2C_BYTE_ONE); + if (ret < 0) { + aw_dev_err("%s: i2c read error, ret=%d\n", __func__, ret); + return ret; + } + reg_val &= mask; + reg_val |= reg_data; + ret = aw8622x_i2c_write(aw8622x, reg_addr, ®_val, AW_I2C_BYTE_ONE); + if (ret < 0) { + aw_dev_err("%s: i2c write error, ret=%d\n", __func__, ret); + return ret; + } + return 0; +} + +static int aw8622x_parse_data(struct aw8622x *aw8622x, const char *buf) +{ + unsigned char reg_num = aw8622x->aw_i2c_package.reg_num; + unsigned char i = 0; + const char *temp_buf = NULL; + char data_buf[AWRW_CMD_UNIT] = { 0 }; + unsigned int value = 0; + int ret = 0; + int len = strlen(buf) - (AWRW_CMD_UNIT * 3); + + if (len < 0) { + aw_dev_err("%s: parse data error\n", __func__); + return -ERANGE; + } + temp_buf = &buf[AWRW_CMD_UNIT * 3]; + for (i = 0; i < reg_num; i++) { + if (((i + 1) * AWRW_CMD_UNIT) > len) { + aw_dev_err("%s: parse data error\n", __func__); + return -ERANGE; + } + memcpy(data_buf, &temp_buf[i * AWRW_CMD_UNIT], 4); + data_buf[4] = '\0'; + ret = kstrtouint(data_buf, 0, &value); + if (ret < 0) { + aw_dev_err("%s: kstrtouint error\n", + __func__); + return ret; + } + aw8622x->aw_i2c_package.reg_data[i] = (unsigned char)value; + } + return 0; +} + +static unsigned char aw8622x_haptic_rtp_get_fifo_afs(struct aw8622x *aw8622x) +{ + unsigned char ret = 0; + unsigned char reg_val = 0; + + aw8622x_i2c_read(aw8622x, AW8622X_REG_SYSST, ®_val, AW_I2C_BYTE_ONE); + reg_val &= AW8622X_BIT_SYSST_FF_AFS; + ret = reg_val >> 3; + return ret; +} + +/***************************************************** + * + * rtp + * + *****************************************************/ +static void aw8622x_haptic_set_rtp_aei(struct aw8622x *aw8622x, bool flag) +{ + if (flag) { + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSINTM, + AW8622X_BIT_SYSINTM_FF_AEM_MASK, + AW8622X_BIT_SYSINTM_FF_AEM_ON); + } else { + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSINTM, + AW8622X_BIT_SYSINTM_FF_AEM_MASK, + AW8622X_BIT_SYSINTM_FF_AEM_OFF); + } +} + +static int aw8622x_analyse_duration_range(struct aw8622x *aw8622x) +{ + int i = 0; + int ret = 0; + int len = 0; + int *duration_time = NULL; + + len = ARRAY_SIZE(aw8622x->dts_info.duration_time); + duration_time = aw8622x->dts_info.duration_time; + if (len < 2) { + aw_dev_err("%s: duration time range error\n", __func__); + return -ERANGE; + } + for (i = (len - 1); i > 0; i--) { + if (duration_time[i] > duration_time[i-1]) + continue; + else + break; + + } + if (i > 0) { + aw_dev_err("%s: duration time range error\n", __func__); + ret = -ERANGE; + } + return ret; +} + +static int aw8622x_analyse_duration_array_size(struct aw8622x *aw8622x, + struct device_node *np) +{ + int ret = 0; + + ret = of_property_count_elems_of_size(np, "aw8622x_vib_duration_time", + 4); + if (ret < 0) { + aw8622x->duration_time_flag = -1; + aw_dev_info("%s vib_duration_time not found\n", __func__); + return ret; + } + aw8622x->duration_time_size = ret; + if (aw8622x->duration_time_size > 3) { + aw8622x->duration_time_flag = -1; + aw_dev_info("%s vib_duration_time error, array size = %d\n", + __func__, aw8622x->duration_time_size); + return -ERANGE; + } + return 0; +} + +/***************************************************** + * + * device tree + * + *****************************************************/ +int aw8622x_parse_dt(struct aw8622x *aw8622x, struct device *dev, + struct device_node *np) +{ + unsigned int val = 0; + unsigned int prctmode_temp[3]; + unsigned int sine_array_temp[4]; + unsigned int trig_config_temp[21]; + unsigned int duration_time[3]; + int ret = 0; + + val = of_property_read_u32(np, "aw8622x_vib_lk_f0_cali", + &aw8622x->dts_info.lk_f0_cali); + if (val != 0) + aw_dev_info("aw8622x_vib_mode not found\n"); + aw_dev_info("%s: aw8622x_vib_lk_f0_cali = 0x%02x\n", __func__, + aw8622x->dts_info.lk_f0_cali); + val = of_property_read_u32(np, + "aw8622x_vib_mode", + &aw8622x->dts_info.mode); + if (val != 0) + aw_dev_info("%s aw8622x_vib_mode not found\n", __func__); + val = of_property_read_u32(np, + "aw8622x_vib_f0_pre", + &aw8622x->dts_info.f0_ref); + if (val != 0) + aw_dev_info("%s vib_f0_ref not found\n", __func__); + val = + of_property_read_u32(np, "aw8622x_vib_f0_cali_percen", + &aw8622x->dts_info.f0_cali_percent); + if (val != 0) + aw_dev_info("%s vib_f0_cali_percent not found\n", __func__); + + val = of_property_read_u32(np, "aw8622x_vib_cont_drv1_lvl", + &aw8622x->dts_info.cont_drv1_lvl_dt); + if (val != 0) + aw_dev_info("%s vib_cont_drv1_lvl not found\n", __func__); + val = + of_property_read_u32(np, "aw8622x_vib_cont_drv2_lvl", + &aw8622x->dts_info.cont_drv2_lvl_dt); + if (val != 0) + aw_dev_info("%s vib_cont_drv2_lvl not found\n", __func__); + val = + of_property_read_u32(np, "aw8622x_vib_cont_drv1_time", + &aw8622x->dts_info.cont_drv1_time_dt); + if (val != 0) + aw_dev_info("%s vib_cont_drv1_time not found\n", __func__); + val = + of_property_read_u32(np, "aw8622x_vib_cont_drv2_time", + &aw8622x->dts_info.cont_drv2_time_dt); + if (val != 0) + aw_dev_info("%s vib_cont_drv2_time not found\n", __func__); + val = + of_property_read_u32(np, "aw8622x_vib_cont_drv_width", + &aw8622x->dts_info.cont_drv_width); + if (val != 0) + aw_dev_info("%s vib_cont_drv_width not found\n", __func__); + val = + of_property_read_u32(np, "aw8622x_vib_cont_wait_num", + &aw8622x->dts_info.cont_wait_num_dt); + if (val != 0) + aw_dev_info("%s vib_cont_wait_num not found\n", __func__); + val = + of_property_read_u32(np, "aw8622x_vib_cont_brk_gain", + &aw8622x->dts_info.cont_brk_gain); + if (val != 0) + aw_dev_info("%s vib_cont_brk_gain not found\n", __func__); + val = + of_property_read_u32(np, "aw8622x_vib_cont_tset", + &aw8622x->dts_info.cont_tset); + if (val != 0) + aw_dev_info("%s vib_cont_tset not found\n", __func__); + val = + of_property_read_u32(np, "aw8622x_vib_cont_bemf_set", + &aw8622x->dts_info.cont_bemf_set); + if (val != 0) + aw_dev_info("%s vib_cont_bemf_set not found\n", __func__); + val = + of_property_read_u32(np, "aw8622x_vib_d2s_gain", + &aw8622x->dts_info.d2s_gain); + if (val != 0) + aw_dev_info("%s vib_d2s_gain not found\n", __func__); + val = + of_property_read_u32(np, "aw8622x_vib_cont_brk_time", + &aw8622x->dts_info.cont_brk_time_dt); + if (val != 0) + aw_dev_info("%s vib_cont_brk_time not found\n", __func__); + val = + of_property_read_u32(np, "aw8622x_vib_cont_track_margin", + &aw8622x->dts_info.cont_track_margin); + if (val != 0) + aw_dev_info("%s vib_cont_track_margin not found\n", __func__); + + val = of_property_read_u32_array(np, "aw8622x_vib_prctmode", + prctmode_temp, + ARRAY_SIZE(prctmode_temp)); + if (val != 0) + aw_dev_info("%s vib_prctmode not found\n", __func__); + memcpy(aw8622x->dts_info.prctmode, prctmode_temp, + sizeof(prctmode_temp)); + val = of_property_read_u32_array(np, + "aw8622x_vib_sine_array", + sine_array_temp, + ARRAY_SIZE(sine_array_temp)); + if (val != 0) + aw_dev_info("%s vib_sine_array not found\n", __func__); + memcpy(aw8622x->dts_info.sine_array, sine_array_temp, + sizeof(sine_array_temp)); + val = + of_property_read_u32_array(np, + "aw8622x_vib_trig_config", + trig_config_temp, + ARRAY_SIZE(trig_config_temp)); + if (val != 0) + aw_dev_info("%s vib_trig_config not found\n", __func__); + memcpy(aw8622x->dts_info.trig_config, trig_config_temp, + sizeof(trig_config_temp)); + val = of_property_read_u32_array(np, "aw8622x_vib_duration_time", + duration_time, ARRAY_SIZE(duration_time)); + if (val != 0) + aw_dev_info("%s vib_duration_time not found\n", __func__); + ret = aw8622x_analyse_duration_array_size(aw8622x, np); + if (!ret) + memcpy(aw8622x->dts_info.duration_time, + duration_time, sizeof(duration_time)); + aw8622x->dts_info.is_enabled_auto_brk = + of_property_read_bool(np, + "aw8622x_vib_is_enabled_auto_brk"); + aw_dev_info("%s aw8622x->info.is_enabled_auto_brk = %d\n", __func__, + aw8622x->dts_info.is_enabled_auto_brk); + + return 0; +} + +static void aw8622x_haptic_upload_lra(struct aw8622x *aw8622x, + unsigned int flag) +{ + switch (flag) { + case AW8622X_WRITE_ZERO: + aw_dev_info("%s write zero to trim_lra!\n", __func__); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_TRIMCFG3, + AW8622X_BIT_TRIMCFG3_TRIM_LRA_MASK, + 0x00); + break; + case AW8622X_F0_CALI: + aw_dev_info("%s write f0_cali_data to trim_lra = 0x%02X\n", + __func__, aw8622x->f0_cali_data); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_TRIMCFG3, + AW8622X_BIT_TRIMCFG3_TRIM_LRA_MASK, + (char)aw8622x->f0_cali_data); + break; + case AW8622X_OSC_CALI: + aw_dev_info("%s write osc_cali_data to trim_lra = 0x%02X\n", + __func__, aw8622x->osc_cali_data); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_TRIMCFG3, + AW8622X_BIT_TRIMCFG3_TRIM_LRA_MASK, + (char)aw8622x->osc_cali_data); + break; + default: + break; + } +} + +/***************************************************** + * + * sram size, normally 3k(2k fifo, 1k ram) + * + *****************************************************/ +static int aw8622x_sram_size(struct aw8622x *aw8622x, int size_flag) +{ + if (size_flag == AW8622X_HAPTIC_SRAM_2K) { + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_RTPCFG1, + AW8622X_BIT_RTPCFG1_SRAM_SIZE_2K_MASK, + AW8622X_BIT_RTPCFG1_SRAM_SIZE_2K_EN); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_RTPCFG1, + AW8622X_BIT_RTPCFG1_SRAM_SIZE_1K_MASK, + AW8622X_BIT_RTPCFG1_SRAM_SIZE_1K_DIS); + } else if (size_flag == AW8622X_HAPTIC_SRAM_1K) { + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_RTPCFG1, + AW8622X_BIT_RTPCFG1_SRAM_SIZE_2K_MASK, + AW8622X_BIT_RTPCFG1_SRAM_SIZE_2K_DIS); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_RTPCFG1, + AW8622X_BIT_RTPCFG1_SRAM_SIZE_1K_MASK, + AW8622X_BIT_RTPCFG1_SRAM_SIZE_1K_EN); + } else if (size_flag == AW8622X_HAPTIC_SRAM_3K) { + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_RTPCFG1, + AW8622X_BIT_RTPCFG1_SRAM_SIZE_1K_MASK, + AW8622X_BIT_RTPCFG1_SRAM_SIZE_1K_EN); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_RTPCFG1, + AW8622X_BIT_RTPCFG1_SRAM_SIZE_2K_MASK, + AW8622X_BIT_RTPCFG1_SRAM_SIZE_2K_EN); + } + return 0; +} + +static int aw8622x_haptic_stop(struct aw8622x *aw8622x) +{ + bool force_flag = true; + unsigned char cnt = 40; + unsigned char reg_val = 0; + + aw_dev_info("%s enter\n", __func__); + aw8622x->play_mode = AW8622X_HAPTIC_STANDBY_MODE; + reg_val = AW8622X_BIT_PLAYCFG4_STOP_ON; + aw8622x_i2c_write(aw8622x, AW8622X_REG_PLAYCFG4, ®_val, + AW_I2C_BYTE_ONE); + while (cnt) { + aw8622x_i2c_read(aw8622x, AW8622X_REG_GLBRD5, ®_val, + AW_I2C_BYTE_ONE); + if ((reg_val & AW8622X_BIT_GLBRD5_STATE) == + AW8622X_BIT_GLBRD5_STATE_STANDBY) { + cnt = 0; + force_flag = false; + aw_dev_info("%s entered standby! glb_state=0x%02X\n", + __func__, reg_val); + } else { + cnt--; + aw_dev_dbg("%s wait for standby, glb_state=0x%02X\n", + __func__, reg_val); + } + usleep_range(AW8622X_STOP_DELAY_MIN, AW8622X_STOP_DELAY_MAX); + } + + if (force_flag) { + aw_dev_err("%s force to enter standby mode!\n", __func__); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL2, + AW8622X_BIT_SYSCTRL2_STANDBY_MASK, + AW8622X_BIT_SYSCTRL2_STANDBY_ON); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL2, + AW8622X_BIT_SYSCTRL2_STANDBY_MASK, + AW8622X_BIT_SYSCTRL2_STANDBY_OFF); + } + aw8622x_haptic_auto_brk_enable(aw8622x, false); + return 0; +} + +static void aw8622x_haptic_raminit(struct aw8622x *aw8622x, bool flag) +{ + if (flag) { + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL1, + AW8622X_BIT_SYSCTRL1_RAMINIT_MASK, + AW8622X_BIT_SYSCTRL1_RAMINIT_ON); + } else { + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL1, + AW8622X_BIT_SYSCTRL1_RAMINIT_MASK, + AW8622X_BIT_SYSCTRL1_RAMINIT_OFF); + } +} + +static int aw8622x_haptic_get_vbat(struct aw8622x *aw8622x) +{ + unsigned char reg_val = 0; + unsigned int vbat_code = 0; + /*unsigned int cont = 2000;*/ + + aw8622x_haptic_stop(aw8622x); + aw8622x_haptic_raminit(aw8622x, true); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_DETCFG2, + AW8622X_BIT_DETCFG2_VBAT_GO_MASK, + AW8622X_BIT_DETCFG2_VABT_GO_ON); + usleep_range(AW8622X_VBAT_DELAY_MIN, AW8622X_VBAT_DELAY_MAX); + aw8622x_i2c_read(aw8622x, AW8622X_REG_DET_VBAT, ®_val, + AW_I2C_BYTE_ONE); + vbat_code = (vbat_code | reg_val) << 2; + aw8622x_i2c_read(aw8622x, AW8622X_REG_DET_LO, ®_val, + AW_I2C_BYTE_ONE); + vbat_code = vbat_code | ((reg_val & AW8622X_BIT_DET_LO_VBAT) >> 4); + aw8622x->vbat = AW8622X_VBAT_FORMULA(vbat_code); + if (aw8622x->vbat > AW8622X_VBAT_MAX) { + aw8622x->vbat = AW8622X_VBAT_MAX; + aw_dev_info("%s vbat max limit = %dmV\n", __func__, + aw8622x->vbat); + } + if (aw8622x->vbat < AW8622X_VBAT_MIN) { + aw8622x->vbat = AW8622X_VBAT_MIN; + aw_dev_info("%s vbat min limit = %dmV\n", __func__, + aw8622x->vbat); + } + aw_dev_info("%s aw8622x->vbat=%dmV, vbat_code=0x%02X\n", + __func__, aw8622x->vbat, vbat_code); + aw8622x_haptic_raminit(aw8622x, false); + return 0; +} + +static void aw8622x_interrupt_clear(struct aw8622x *aw8622x) +{ + unsigned char reg_val = 0; + + aw8622x_i2c_read(aw8622x, AW8622X_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_dev_dbg("%s: SYSINT=0x%02X\n", __func__, reg_val); +} + +static void aw8622x_haptic_set_gain(struct aw8622x *aw8622x, unsigned char gain) +{ + aw8622x_i2c_write(aw8622x, AW8622X_REG_PLAYCFG2, &gain, + AW_I2C_BYTE_ONE); +} + +static int aw8622x_haptic_ram_vbat_comp(struct aw8622x *aw8622x, + bool flag) +{ + int temp_gain = 0; + + if (flag) { + if (aw8622x->ram_vbat_compensate == + AW8622X_HAPTIC_RAM_VBAT_COMP_ENABLE) { + aw8622x_haptic_get_vbat(aw8622x); + temp_gain = + aw8622x->gain * AW8622X_VBAT_REFER / aw8622x->vbat; + if (temp_gain > + (128 * AW8622X_VBAT_REFER / AW8622X_VBAT_MIN)) { + temp_gain = + 128 * AW8622X_VBAT_REFER / AW8622X_VBAT_MIN; + aw_dev_dbg("%s gain limit=%d\n", __func__, + temp_gain); + } + aw8622x_haptic_set_gain(aw8622x, temp_gain); + } else { + aw8622x_haptic_set_gain(aw8622x, aw8622x->gain); + } + } else { + aw8622x_haptic_set_gain(aw8622x, aw8622x->gain); + } + return 0; +} + +static int aw8622x_haptic_play_mode(struct aw8622x *aw8622x, + unsigned char play_mode) +{ + aw_dev_info("%s enter\n", __func__); + + switch (play_mode) { + case AW8622X_HAPTIC_STANDBY_MODE: + aw_dev_info("%s: enter standby mode\n", __func__); + aw8622x->play_mode = AW8622X_HAPTIC_STANDBY_MODE; + aw8622x_haptic_stop(aw8622x); + break; + case AW8622X_HAPTIC_RAM_MODE: + aw_dev_info("%s: enter ram mode\n", __func__); + aw8622x->play_mode = AW8622X_HAPTIC_RAM_MODE; + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_PLAYCFG3, + AW8622X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8622X_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + case AW8622X_HAPTIC_RAM_LOOP_MODE: + aw_dev_info("%s: enter ram loop mode\n", + __func__); + aw8622x->play_mode = AW8622X_HAPTIC_RAM_LOOP_MODE; + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_PLAYCFG3, + AW8622X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8622X_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + case AW8622X_HAPTIC_RTP_MODE: + aw_dev_info("%s: enter rtp mode\n", __func__); + aw8622x->play_mode = AW8622X_HAPTIC_RTP_MODE; + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_PLAYCFG3, + AW8622X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8622X_BIT_PLAYCFG3_PLAY_MODE_RTP); + break; + case AW8622X_HAPTIC_TRIG_MODE: + aw_dev_info("%s: enter trig mode\n", __func__); + aw8622x->play_mode = AW8622X_HAPTIC_TRIG_MODE; + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_PLAYCFG3, + AW8622X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8622X_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + case AW8622X_HAPTIC_CONT_MODE: + aw_dev_info("%s: enter cont mode\n", __func__); + aw8622x->play_mode = AW8622X_HAPTIC_CONT_MODE; + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_PLAYCFG3, + AW8622X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8622X_BIT_PLAYCFG3_PLAY_MODE_CONT); + break; + default: + aw_dev_err("%s: play mode %d error", + __func__, play_mode); + break; + } + return 0; +} + +static int aw8622x_haptic_play_go(struct aw8622x *aw8622x, bool flag) +{ + unsigned char reg_val = 0; + + aw_dev_dbg("%s enter\n", __func__); + if (flag == true) { + reg_val = AW8622X_BIT_PLAYCFG4_GO_ON; + aw8622x_i2c_write(aw8622x, AW8622X_REG_PLAYCFG4, ®_val, + AW_I2C_BYTE_ONE); + mdelay(2); + } else { + reg_val = AW8622X_BIT_PLAYCFG4_STOP_ON; + aw8622x_i2c_write(aw8622x, AW8622X_REG_PLAYCFG4, ®_val, + AW_I2C_BYTE_ONE); + } + return 0; +} + +static void aw8622x_haptic_set_wav_seq(struct aw8622x *aw8622x, + unsigned char wav, unsigned char seq) +{ + aw8622x_i2c_write(aw8622x, AW8622X_REG_WAVCFG1 + wav, &seq, + AW_I2C_BYTE_ONE); +} + +static int aw8622x_haptic_set_wav_loop(struct aw8622x *aw8622x, + unsigned char wav, unsigned char loop) +{ + unsigned char tmp = 0; + + if (wav % 2) { + tmp = loop << 0; + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_WAVCFG9 + (wav / 2), + AW8622X_BIT_WAVLOOP_SEQ_EVEN_MASK, tmp); + } else { + tmp = loop << 4; + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_WAVCFG9 + (wav / 2), + AW8622X_BIT_WAVLOOP_SEQ_ODD_MASK, tmp); + } + return 0; +} + +/***************************************************** + * + * haptic f0 cali + * + *****************************************************/ +static int aw8622x_haptic_read_lra_f0(struct aw8622x *aw8622x) +{ + unsigned char reg_val[2] = {0}; + unsigned int f0_reg = 0; + unsigned long f0_tmp = 0; + + aw_dev_info("%s enter\n", __func__); + /* F_LRA_F0 */ + aw8622x_i2c_read(aw8622x, AW8622X_REG_CONTRD14, reg_val, + AW_I2C_BYTE_TWO); + f0_reg = (f0_reg | reg_val[0]) << 8; + f0_reg |= (reg_val[1] << 0); + + + if (!f0_reg) { + aw_dev_err("%s didn't get lra f0 because f0_reg value is 0!\n", + __func__); + aw8622x->f0 = aw8622x->dts_info.f0_ref; + return -ERANGE; + } + f0_tmp = AW8622X_LRA_F0_FORMULA(f0_reg); + aw8622x->f0 = (unsigned int)f0_tmp; + aw_dev_info("%s lra_f0=%d\n", __func__, aw8622x->f0); + return 0; +} + +static int aw8622x_haptic_read_cont_f0(struct aw8622x *aw8622x) +{ + unsigned char reg_val[2] = {0}; + unsigned int f0_reg = 0; + unsigned long f0_tmp = 0; + + aw_dev_info("%s enter\n", __func__); + aw8622x_i2c_read(aw8622x, AW8622X_REG_CONTRD16, reg_val, + AW_I2C_BYTE_TWO); + f0_reg = (f0_reg | reg_val[0]) << 8; + f0_reg |= (reg_val[1] << 0); + + if (!f0_reg) { + aw_dev_err("%s didn't get cont f0 because f0_reg value is 0!\n", + __func__); + aw8622x->cont_f0 = aw8622x->dts_info.f0_ref; + return -ERANGE; + } + f0_tmp = AW8622X_CONT_F0_FORMULA(f0_reg); + aw8622x->cont_f0 = (unsigned int)f0_tmp; + aw_dev_info("%s cont_f0=%d\n", + __func__, aw8622x->cont_f0); + return 0; +} + +static int aw8622x_haptic_cont_get_f0(struct aw8622x *aw8622x) +{ + bool get_f0_flag = false; + unsigned char reg_val = 0; + unsigned char brk_en_temp = 0; + unsigned char reg_array[3] = {0}; + unsigned int cnt = 200; + int ret = 0; + + aw_dev_info("%s enter\n", __func__); + aw8622x->f0 = aw8622x->dts_info.f0_ref; + /* enter standby mode */ + aw8622x_haptic_stop(aw8622x); + /* f0 calibrate work mode */ + aw8622x_haptic_play_mode(aw8622x, AW8622X_HAPTIC_CONT_MODE); + /* enable f0 detect */ + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_CONTCFG1, + AW8622X_BIT_CONTCFG1_EN_F0_DET_MASK, + AW8622X_BIT_CONTCFG1_F0_DET_ENABLE); + /* cont config */ + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_CONTCFG6, + AW8622X_BIT_CONTCFG6_TRACK_EN_MASK, + AW8622X_BIT_CONTCFG6_TRACK_ENABLE); + /* enable auto brake */ + aw8622x_i2c_read(aw8622x, AW8622X_REG_PLAYCFG3, ®_val, + AW_I2C_BYTE_ONE); + brk_en_temp = AW8622X_BIT_PLAYCFG3_BRK & reg_val; + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_PLAYCFG3, + AW8622X_BIT_PLAYCFG3_BRK_EN_MASK, + AW8622X_BIT_PLAYCFG3_BRK_ENABLE); + /* f0 driver level */ + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_CONTCFG6, + AW8622X_BIT_CONTCFG6_DRV1_LVL_MASK, + aw8622x->dts_info.cont_drv1_lvl_dt); + reg_array[0] = (unsigned char)aw8622x->dts_info.cont_drv2_lvl_dt; + reg_array[1] = (unsigned char)aw8622x->dts_info.cont_drv1_time_dt; + reg_array[2] = (unsigned char)aw8622x->dts_info.cont_drv2_time_dt; + aw8622x_i2c_write(aw8622x, AW8622X_REG_CONTCFG7, reg_array, + AW_I2C_BYTE_THREE); + /* TRACK_MARGIN */ + if (!aw8622x->dts_info.cont_track_margin) { + aw_dev_err("%s aw8622x->dts_info.cont_track_margin = 0!\n", + __func__); + } else { + reg_val = (unsigned char)aw8622x->dts_info.cont_track_margin; + aw8622x_i2c_write(aw8622x, AW8622X_REG_CONTCFG11, ®_val, + AW_I2C_BYTE_ONE); + } + /* cont play go */ + aw8622x_haptic_play_go(aw8622x, true); + /* 300ms */ + while (cnt) { + aw8622x_i2c_read(aw8622x, AW8622X_REG_GLBRD5, ®_val, + AW_I2C_BYTE_ONE); + if ((reg_val & AW8622X_BIT_GLBRD5_STATE) == + AW8622X_BIT_GLBRD5_STATE_STANDBY) { + cnt = 0; + get_f0_flag = true; + aw_dev_info("%s entered standby mode! glb_state=0x%02X\n", + __func__, reg_val); + } else { + cnt--; + aw_dev_dbg("%s waitting for standby, glb_state=0x%02X\n", + __func__, reg_val); + } + usleep_range(AW8622X_F0_DELAY_MIN, AW8622X_F0_DELAY_MAX); + } + if (get_f0_flag) { + aw8622x_haptic_read_lra_f0(aw8622x); + aw8622x_haptic_read_cont_f0(aw8622x); + } else { + aw_dev_err("%s enter standby mode failed, stop reading f0!\n", + __func__); + } + /* restore default config */ + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_CONTCFG1, + AW8622X_BIT_CONTCFG1_EN_F0_DET_MASK, + AW8622X_BIT_CONTCFG1_F0_DET_DISABLE); + /* recover auto break config */ + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_PLAYCFG3, + AW8622X_BIT_PLAYCFG3_BRK_EN_MASK, + brk_en_temp); + return ret; +} + +static int aw8622x_haptic_rtp_init(struct aw8622x *aw8622x) +{ + unsigned int buf_len = 0; + unsigned char glb_state_val = 0; + struct aw8622x_container *rtp_container = aw8622x->rtp_container; + + aw_dev_dbg("%s enter\n", __func__); + pm_qos_add_request(&aw8622x_pm_qos_req_vb, PM_QOS_CPU_DMA_LATENCY, + AW8622X_PM_QOS_VALUE_VB); + aw8622x->rtp_cnt = 0; + mutex_lock(&aw8622x->rtp_lock); + while ((!aw8622x_haptic_rtp_get_fifo_afs(aw8622x)) + && (aw8622x->play_mode == AW8622X_HAPTIC_RTP_MODE)) { +#ifdef AW_ENABLE_RTP_PRINT_LOG + aw_dev_info("%s rtp cnt = %d\n", __func__, aw8622x->rtp_cnt); +#endif + if (!rtp_container) { + aw_dev_info("%s:rtp_container is null, break!\n", + __func__); + break; + } + if (aw8622x->rtp_cnt < (aw8622x->ram.base_addr)) { + if ((rtp_container->len - aw8622x->rtp_cnt) < + (aw8622x->ram.base_addr)) { + buf_len = rtp_container->len - aw8622x->rtp_cnt; + } else { + buf_len = aw8622x->ram.base_addr; + } + } else if ((rtp_container->len - aw8622x->rtp_cnt) < + (aw8622x->ram.base_addr >> 2)) { + buf_len = rtp_container->len - aw8622x->rtp_cnt; + } else { + buf_len = aw8622x->ram.base_addr >> 2; + } +#ifdef AW_ENABLE_RTP_PRINT_LOG + aw_dev_info("%s buf_len = %d\n", __func__, + buf_len); +#endif + aw8622x_i2c_write(aw8622x, AW8622X_REG_RTPDATA, + &rtp_container->data[aw8622x->rtp_cnt], + buf_len); + aw8622x->rtp_cnt += buf_len; + aw8622x_i2c_read(aw8622x, AW8622X_REG_GLBRD5, &glb_state_val, + AW_I2C_BYTE_ONE); + if ((aw8622x->rtp_cnt == rtp_container->len) + || ((glb_state_val & AW8622X_BIT_GLBRD5_STATE) == + AW8622X_BIT_GLBRD5_STATE_STANDBY)) { + if (aw8622x->rtp_cnt == rtp_container->len) + aw_dev_info("%s: rtp load completely! glb_state_val=%02x aw8622x->rtp_cnt=%02x\n", + __func__, glb_state_val, + aw8622x->rtp_cnt); + else + aw_dev_err("%s rtp load failed!! glb_state_val=%02x aw8622x->rtp_cnt=%02x\n", + __func__, glb_state_val, + aw8622x->rtp_cnt); + aw8622x->rtp_cnt = 0; + pm_qos_remove_request(&aw8622x_pm_qos_req_vb); + mutex_unlock(&aw8622x->rtp_lock); + return 0; + } + } + + if (aw8622x->play_mode == AW8622X_HAPTIC_RTP_MODE) + aw8622x_haptic_set_rtp_aei(aw8622x, true); + + aw_dev_dbg("%s exit\n", __func__); + mutex_unlock(&aw8622x->rtp_lock); + pm_qos_remove_request(&aw8622x_pm_qos_req_vb); + return 0; +} + +static int aw8622x_haptic_ram_config(struct aw8622x *aw8622x, int duration) +{ + int ret = 0; + + if (aw8622x->duration_time_flag < 0) { + aw_dev_err("%s: duration time error, array size = %d\n", + __func__, aw8622x->duration_time_size); + return -ERANGE; + } + ret = aw8622x_analyse_duration_range(aw8622x); + if (ret < 0) + return ret; + +#if 0 + if ((duration > 0) && (duration < + aw8622x->dts_info.duration_time[0])) { + aw8622x->index = 3; /*3*/ + aw8622x->activate_mode = AW8622X_HAPTIC_RAM_MODE; + } else if ((duration >= aw8622x->dts_info.duration_time[0]) && + (duration < aw8622x->dts_info.duration_time[1])) { + aw8622x->index = 2; /*2*/ + aw8622x->activate_mode = AW8622X_HAPTIC_RAM_MODE; + } else if ((duration >= aw8622x->dts_info.duration_time[1]) && + (duration < aw8622x->dts_info.duration_time[2])) { + aw8622x->index = 1; /*1*/ + aw8622x->activate_mode = AW8622X_HAPTIC_RAM_MODE; + } else if (duration >= aw8622x->dts_info.duration_time[2]) { + aw8622x->index = 4; /*4*/ + aw8622x->activate_mode = AW8622X_HAPTIC_RAM_LOOP_MODE; + } else { + aw_dev_err("%s: duration time error, duration= %d\n", + __func__, duration); + aw8622x->index = 0; + aw8622x->activate_mode = AW8622X_HAPTIC_NULL; + ret = -ERANGE; + } + + + /* custom for moto */ + if (duration <= aw8622x->dts_info.duration_time[2] && duration >= 0) { + aw8622x->index = 1; /*1 short */ + aw8622x->activate_mode = AW8622X_HAPTIC_RAM_MODE; + } else if (duration > aw8622x->dts_info.duration_time[2]) { + aw8622x->index = 4; /*4 long haptic */ + aw8622x->activate_mode = AW8622X_HAPTIC_RAM_LOOP_MODE; + } else { + aw_dev_err("%s: duration time error, duration= %d\n", + __func__, duration); + aw8622x->index = 0; + aw8622x->activate_mode = AW8622X_HAPTIC_NULL; + ret = -ERANGE; + } +#endif + + return ret; +} + +static unsigned char aw8622x_haptic_osc_read_status(struct aw8622x *aw8622x) +{ + unsigned char reg_val = 0; + + aw8622x_i2c_read(aw8622x, AW8622X_REG_SYSST2, ®_val, + AW_I2C_BYTE_ONE); + return reg_val; +} + +static int aw8622x_haptic_set_repeat_wav_seq(struct aw8622x *aw8622x, + unsigned char seq) +{ + aw8622x_haptic_set_wav_seq(aw8622x, 0x00, seq); + aw8622x_haptic_set_wav_seq(aw8622x, 0x01, 0x00); + aw8622x_haptic_set_wav_loop(aw8622x, 0x00, + AW8622X_BIT_WAVLOOP_INIFINITELY); + return 0; +} + +static void aw8622x_rtp_work_routine(struct work_struct *work) +{ + bool rtp_work_flag = false; + unsigned char reg_val = 0; + unsigned int cnt = 200; + int ret = -1; + const struct firmware *rtp_file; + struct aw8622x *aw8622x = container_of(work, struct aw8622x, rtp_work); + char * p_rtp_file_name = NULL; + + aw_dev_info("%s enter\n", __func__); + mutex_lock(&aw8622x->rtp_lock); + if (aw8622x->moto_rtp_runing) { + p_rtp_file_name = aw8622x_moto_rtp_name[aw8622x->rtp_file_num]; + } else { + p_rtp_file_name = aw8622x_rtp_name[aw8622x->rtp_file_num]; + } + if (p_rtp_file_name == NULL) { + aw_dev_err("%s: failed to read %s\n", __func__, + p_rtp_file_name); + mutex_unlock(&aw8622x->rtp_lock); + return; + } + + aw_dev_info("%s request file : %s\n", __func__, p_rtp_file_name); + /* fw loaded */ + ret = request_firmware(&rtp_file, + p_rtp_file_name, + aw8622x->dev); + if (ret < 0) { + aw_dev_err("%s: failed to read %s\n", __func__, + p_rtp_file_name); + mutex_unlock(&aw8622x->rtp_lock); + return; + } + aw8622x->rtp_init = 0; + vfree(aw8622x->rtp_container); + aw8622x->rtp_container = vmalloc(rtp_file->size + sizeof(int)); + if (!aw8622x->rtp_container) { + release_firmware(rtp_file); + aw_dev_err("%s: error allocating memory\n", __func__); + mutex_unlock(&aw8622x->rtp_lock); + return; + } + aw8622x->rtp_container->len = rtp_file->size; + aw_dev_info("%s: rtp file:[%s] size = %dbytes\n", + __func__, p_rtp_file_name, + aw8622x->rtp_container->len); + memcpy(aw8622x->rtp_container->data, rtp_file->data, rtp_file->size); + mutex_unlock(&aw8622x->rtp_lock); + release_firmware(rtp_file); + mutex_lock(&aw8622x->lock); + aw8622x->rtp_init = 1; + aw8622x_haptic_upload_lra(aw8622x, AW8622X_OSC_CALI); + /* gain */ + aw8622x_haptic_ram_vbat_comp(aw8622x, false); + /* rtp mode config */ + aw8622x_haptic_play_mode(aw8622x, AW8622X_HAPTIC_RTP_MODE); + /* haptic go */ + aw8622x_haptic_play_go(aw8622x, true); + mutex_unlock(&aw8622x->lock); + usleep_range(AW8622X_STOP_DELAY_MIN, AW8622X_STOP_DELAY_MAX); + while (cnt) { + aw8622x_i2c_read(aw8622x, AW8622X_REG_GLBRD5, ®_val, + AW_I2C_BYTE_ONE); + if ((reg_val & AW8622X_BIT_GLBRD5_STATE) == + AW8622X_BIT_GLBRD5_STATE_RTP_GO) { + cnt = 0; + rtp_work_flag = true; + aw_dev_info("%s RTP_GO! glb_state=0x08\n", __func__); + } else { + cnt--; + aw_dev_dbg("%s wait for RTP_GO, glb_state=0x%02X\n", + __func__, reg_val); + } + usleep_range(AW8622X_STOP_DELAY_MIN, AW8622X_STOP_DELAY_MAX); + } + if (rtp_work_flag) { + aw8622x_haptic_rtp_init(aw8622x); + } else { + /* enter standby mode */ + aw8622x_haptic_stop(aw8622x); + aw_dev_err("%s failed to enter RTP_GO status!\n", __func__); + } +} + +static int aw8622x_osc_calculation_time(struct aw8622x *aw8622x) +{ + unsigned char osc_int_state = 0; + unsigned int base_addr = aw8622x->ram.base_addr; + unsigned int buf_len = 0; + int ret = -1; + struct aw8622x_container *rtp_container = aw8622x->rtp_container; + + if (!aw8622x_haptic_rtp_get_fifo_afs(aw8622x)) { + aw_dev_dbg("%s: haptic_rtp_get_fifo_afi, rtp_cnt= %d\n", + __func__, aw8622x->rtp_cnt); + + mutex_lock(&aw8622x->rtp_lock); + if ((rtp_container->len - aw8622x->rtp_cnt) < (base_addr >> 2)) + buf_len = rtp_container->len - aw8622x->rtp_cnt; + else + buf_len = (base_addr >> 2); + + if (aw8622x->rtp_cnt != rtp_container->len) { + if (aw8622x->timeval_flags == 1) { + aw8622x->kstart = ktime_get(); + aw8622x->timeval_flags = 0; + } + aw8622x_i2c_write(aw8622x, AW8622X_REG_RTPDATA, + &rtp_container->data[aw8622x->rtp_cnt], + buf_len); + aw8622x->rtp_cnt += buf_len; + } + mutex_unlock(&aw8622x->rtp_lock); + } + + osc_int_state = aw8622x_haptic_osc_read_status(aw8622x); + if (osc_int_state & AW8622X_BIT_SYSST2_FF_EMPTY) { + aw8622x->kend = ktime_get(); + aw_dev_info("%s osc trim playback done aw8622x->rtp_cnt= %d\n", + __func__, aw8622x->rtp_cnt); + return ret; + } + aw8622x->kend = ktime_get(); + aw8622x->microsecond = ktime_to_us(ktime_sub(aw8622x->kend, + aw8622x->kstart)); + + if (aw8622x->microsecond > AW8622X_OSC_CALI_MAX_LENGTH) { + aw_dev_info("%s osc trim time out! aw8622x->rtp_cnt %d\n", + __func__, aw8622x->rtp_cnt); + aw_dev_info("%s osc_int_state %02x\n", __func__, osc_int_state); + return ret; + } + return 0; +} + +static int aw8622x_rtp_osc_calibration(struct aw8622x *aw8622x) +{ + int ret = -1; + const struct firmware *rtp_file; + + aw8622x->rtp_cnt = 0; + aw8622x->timeval_flags = 1; + + aw_dev_info("%s enter\n", __func__); + /* fw loaded */ + ret = request_firmware(&rtp_file, aw8622x_rtp_name[0], aw8622x->dev); + if (ret < 0) { + aw_dev_err("%s: failed to read %s\n", __func__, + aw8622x_rtp_name[0]); + return ret; + } + /*awinic add stop,for irq interrupt during calibrate */ + aw8622x_haptic_stop(aw8622x); + aw8622x->rtp_init = 0; + mutex_lock(&aw8622x->rtp_lock); + vfree(aw8622x->rtp_container); + aw8622x->rtp_container = vmalloc(rtp_file->size + sizeof(int)); + if (!aw8622x->rtp_container) { + release_firmware(rtp_file); + mutex_unlock(&aw8622x->rtp_lock); + aw_dev_err("%s: error allocating memory\n", + __func__); + return -ENOMEM; + } + aw8622x->rtp_container->len = rtp_file->size; + aw8622x->rtp_len = rtp_file->size; + aw_dev_info("%s: rtp file:[%s] size = %dbytes\n", + __func__, aw8622x_rtp_name[0], aw8622x->rtp_container->len); + + memcpy(aw8622x->rtp_container->data, rtp_file->data, rtp_file->size); + release_firmware(rtp_file); + mutex_unlock(&aw8622x->rtp_lock); + /* gain */ + aw8622x_haptic_ram_vbat_comp(aw8622x, false); + /* rtp mode config */ + aw8622x_haptic_play_mode(aw8622x, AW8622X_HAPTIC_RTP_MODE); + + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL7, + AW8622X_BIT_SYSCTRL7_INT_MODE_MASK, + AW8622X_BIT_SYSCTRL7_INT_MODE_EDGE); + disable_irq(gpio_to_irq(aw8622x->irq_gpio)); + /* haptic go */ + aw8622x_haptic_play_go(aw8622x, true); + /* require latency of CPU & DMA not more then PM_QOS_VALUE_VB us */ + pm_qos_add_request(&aw8622x_pm_qos_req_vb, PM_QOS_CPU_DMA_LATENCY, + AW8622X_PM_QOS_VALUE_VB); + while (1) { + ret = aw8622x_osc_calculation_time(aw8622x); + if (ret < 0) + break; + } + pm_qos_remove_request(&aw8622x_pm_qos_req_vb); + enable_irq(gpio_to_irq(aw8622x->irq_gpio)); + aw8622x->microsecond = ktime_to_us(ktime_sub(aw8622x->kend, + aw8622x->kstart)); + /*calibration osc */ + aw_dev_info("%s awinic_microsecond: %ld\n", __func__, + aw8622x->microsecond); + aw_dev_info("%s exit\n", __func__); + return 0; +} + +static int aw8622x_osc_trim_calculation(struct aw8622x *aw8622x, + unsigned long int theory_time) +{ + unsigned int real_code = 0; + unsigned int lra_code = 0; + unsigned int DFT_LRA_TRIM_CODE = 0; + /*0.1 percent below no need to calibrate */ + unsigned int osc_cali_threshold = 10; + unsigned long int real_time = aw8622x->microsecond; + + + aw_dev_info("%s enter\n", __func__); + if (theory_time == real_time) { + aw_dev_info("%s theory_time == real_time: %ld, no need to calibrate!\n", + __func__, real_time); + return 0; + } else if (theory_time < real_time) { + if ((real_time - theory_time) > (theory_time / 50)) { + aw_dev_info("%s (real_time - theory_time) > (theory_time/50), can't calibrate!\n", + __func__); + return DFT_LRA_TRIM_CODE; + } + + if ((real_time - theory_time) < + (osc_cali_threshold * theory_time / 10000)) { + aw_dev_info("%s real_time: %ld, theory_time: %ld, no need to calibrate!\n", + __func__, real_time, theory_time); + return DFT_LRA_TRIM_CODE; + } + + real_code = ((real_time - theory_time) * 4000) / theory_time; + real_code = ((real_code % 10 < 5) ? 0 : 1) + real_code / 10; + real_code = 32 + real_code; + } else if (theory_time > real_time) { + if ((theory_time - real_time) > (theory_time / 50)) { + aw_dev_info("%s (theory_time - real_time) > (theory_time / 50), can't calibrate!\n", + __func__); + return DFT_LRA_TRIM_CODE; + } + if ((theory_time - real_time) < + (osc_cali_threshold * theory_time / 10000)) { + aw_dev_info("%s real_time: %ld, theory_time: %ld, no need to calibrate!\n", + __func__, real_time, theory_time); + return DFT_LRA_TRIM_CODE; + } + real_code = ((theory_time - real_time) * 4000) / theory_time; + real_code = ((real_code % 10 < 5) ? 0 : 1) + real_code / 10; + real_code = 32 - real_code; + } + if (real_code > 31) + lra_code = real_code - 32; + else + lra_code = real_code + 32; + aw_dev_info("%s real_time: %ld, theory_time: %ld\n", __func__, + real_time, theory_time); + aw_dev_info("%s real_code: %02X, trim_lra: 0x%02X\n", __func__, + real_code, lra_code); + return lra_code; +} + +static int aw8622x_haptic_get_lra_resistance(struct aw8622x *aw8622x) +{ + unsigned char reg_val = 0; + unsigned char d2s_gain_temp = 0; + unsigned int lra_code = 0; + unsigned int lra = 0; + + mutex_lock(&aw8622x->lock); + aw8622x_haptic_stop(aw8622x); + aw8622x_i2c_read(aw8622x, AW8622X_REG_SYSCTRL7, ®_val, + AW_I2C_BYTE_ONE); + d2s_gain_temp = AW8622X_BIT_SYSCTRL7_GAIN & reg_val; + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL7, + AW8622X_BIT_SYSCTRL7_D2S_GAIN_MASK, + aw8622x->dts_info.d2s_gain); + aw8622x_haptic_raminit(aw8622x, true); + /* enter standby mode */ + aw8622x_haptic_stop(aw8622x); + usleep_range(AW8622X_STOP_DELAY_MIN, AW8622X_STOP_DELAY_MAX); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL2, + AW8622X_BIT_SYSCTRL2_STANDBY_MASK, + AW8622X_BIT_SYSCTRL2_STANDBY_OFF); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_DETCFG1, + AW8622X_BIT_DETCFG1_RL_OS_MASK, + AW8622X_BIT_DETCFG1_RL); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_DETCFG2, + AW8622X_BIT_DETCFG2_DIAG_GO_MASK, + AW8622X_BIT_DETCFG2_DIAG_GO_ON); + usleep_range(AW8622X_LRA_DELAY_MIN, AW8622X_LRA_DELAY_MAX); + aw8622x_i2c_read(aw8622x, AW8622X_REG_DET_RL, ®_val, + AW_I2C_BYTE_ONE); + lra_code = (lra_code | reg_val) << 2; + aw8622x_i2c_read(aw8622x, AW8622X_REG_DET_LO, ®_val, + AW_I2C_BYTE_ONE); + lra_code = lra_code | (reg_val & AW8622X_BIT_DET_LO_RL); + /* 2num */ + lra = AW8622X_LRA_FORMULA(lra_code); + /* Keep up with aw8624 driver */ + aw8622x->lra = lra * 10; + aw8622x_haptic_raminit(aw8622x, false); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL7, + AW8622X_BIT_SYSCTRL7_D2S_GAIN_MASK, + d2s_gain_temp); + mutex_unlock(&aw8622x->lock); + return 0; +} + +static int aw8622x_haptic_juge_RTP_is_going_on(struct aw8622x *aw8622x) +{ + unsigned char rtp_state = 0; + unsigned char mode = 0; + unsigned char glb_st = 0; + + aw8622x_i2c_read(aw8622x, AW8622X_REG_PLAYCFG3, &mode, + AW_I2C_BYTE_ONE); + aw8622x_i2c_read(aw8622x, AW8622X_REG_GLBRD5, &glb_st, + AW_I2C_BYTE_ONE); + if ((mode & AW8622X_BIT_PLAYCFG3_PLAY_MODE_RTP) && + (glb_st == AW8622X_BIT_GLBRD5_STATE_RTP_GO)) { + rtp_state = 1; + } + return rtp_state; +} + +static void aw8622x_set_ram_addr(struct aw8622x *aw8622x) +{ + unsigned char ram_addr[2] = {0}; + unsigned int base_addr = aw8622x->ram.base_addr; + + ram_addr[0] = (unsigned char)AW_SET_RAMADDR_H(base_addr); + ram_addr[1] = (unsigned char)AW_SET_RAMADDR_L(base_addr); + aw8622x_i2c_write(aw8622x, AW8622X_REG_RAMADDRH, ram_addr, + AW_I2C_BYTE_TWO); +} + +#ifdef AW_CHECK_RAM_DATA +static int aw8622x_check_ram_data(struct aw8622x *aw8622x, + unsigned char *cont_data, + unsigned char *ram_data, unsigned int len) +{ + int i = 0; + + for (i = 0; i < len; i++) { + if (ram_data[i] != cont_data[i]) { + aw_dev_err("%s: check ramdata error, addr=0x%04x, ram_data=0x%02x, file_data=0x%02x\n", + __func__, i, ram_data[i], cont_data[i]); + return -ERANGE; + } + } + return 0; +} +#endif + +static int aw8622x_container_update(struct aw8622x *aw8622x, + struct aw8622x_container *aw8622x_cont) +{ + unsigned char ae_addr_h = 0; + unsigned char af_addr_h = 0; + unsigned char ae_addr_l = 0; + unsigned char af_addr_l = 0; + unsigned char reg_array[3] = {0}; + unsigned int shift = 0; + unsigned int base_addr = 0; + int i = 0; + int ret = 0; + int len = 0; +#ifdef AW_CHECK_RAM_DATA + unsigned char ram_data[AW_RAMDATA_RD_BUFFER_SIZE] = {0}; +#endif + + aw_dev_info("%s enter\n", __func__); + mutex_lock(&aw8622x->lock); + aw8622x->ram.baseaddr_shift = 2; + aw8622x->ram.ram_shift = 4; + /* RAMINIT Enable */ + aw8622x_haptic_raminit(aw8622x, true); + /* Enter standby mode */ + aw8622x_haptic_stop(aw8622x); + /* default 3k SRAM */ + aw8622x_sram_size(aw8622x, AW8622X_HAPTIC_SRAM_3K); + /* set base addr */ + shift = aw8622x->ram.baseaddr_shift; + aw8622x->ram.base_addr = + (unsigned int)((aw8622x_cont->data[0 + shift] << 8) | + (aw8622x_cont->data[1 + shift])); + base_addr = aw8622x->ram.base_addr; + aw_dev_info("%s: base_addr=%d\n", __func__, base_addr); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_RTPCFG1, + AW8622X_BIT_RTPCFG1_ADDRH_MASK, + (unsigned char)AW_SET_BASEADDR_H(base_addr)); + reg_array[0] = (unsigned char)AW_SET_BASEADDR_L(base_addr); + aw8622x_i2c_write(aw8622x, AW8622X_REG_RTPCFG2, + ®_array[0], AW_I2C_BYTE_ONE); + /* set FIFO_AE and FIFO_AF addr */ + ae_addr_h = (unsigned char)AW8622X_SET_AEADDR_H(base_addr); + af_addr_h = (unsigned char)AW8622X_SET_AFADDR_H(base_addr); + reg_array[0] = ae_addr_h | af_addr_h; + reg_array[1] = (unsigned char)AW8622X_SET_AEADDR_L(base_addr); + reg_array[2] = (unsigned char)AW8622X_SET_AFADDR_L(base_addr); + aw8622x_i2c_write(aw8622x, AW8622X_REG_RTPCFG3, reg_array, + AW_I2C_BYTE_THREE); + /* get FIFO_AE and FIFO_AF addr */ + aw8622x_i2c_read(aw8622x, AW8622X_REG_RTPCFG3, reg_array, + AW_I2C_BYTE_THREE); + ae_addr_h = ((reg_array[0]) & AW8622X_BIT_RTPCFG3_FIFO_AEH) >> 4; + ae_addr_l = reg_array[1]; + aw_dev_info("%s: almost_empty_threshold = %d\n", __func__, + (unsigned short)((ae_addr_h << 8) | ae_addr_l)); + af_addr_h = ((reg_array[0]) & AW8622X_BIT_RTPCFG3_FIFO_AFH); + af_addr_l = reg_array[2]; + aw_dev_info("%s: almost_full_threshold = %d\n", __func__, + (unsigned short)((af_addr_h << 8) | af_addr_l)); + + /* ram */ + aw8622x_set_ram_addr(aw8622x); + i = aw8622x->ram.ram_shift; + while (i < aw8622x_cont->len) { + if ((aw8622x_cont->len - i) < AW_RAMDATA_WR_BUFFER_SIZE) + len = aw8622x_cont->len - i; + else + len = AW_RAMDATA_WR_BUFFER_SIZE; + + aw8622x_i2c_write(aw8622x, AW8622X_REG_RAMDATA, + &aw8622x_cont->data[i], len); + i += len; + } +#ifdef AW_CHECK_RAM_DATA + aw8622x_set_ram_addr(aw8622x); + i = aw8622x->ram.ram_shift; + while (i < aw8622x_cont->len) { + if ((aw8622x_cont->len - i) < AW_RAMDATA_RD_BUFFER_SIZE) + len = aw8622x_cont->len - i; + else + len = AW_RAMDATA_RD_BUFFER_SIZE; + + aw8622x_i2c_read(aw8622x, AW8622X_REG_RAMDATA, ram_data, len); + ret = aw8622x_check_ram_data(aw8622x, &aw8622x_cont->data[i], + ram_data, len); + if (ret < 0) + break; + i += len; + } + if (ret) + aw_dev_err("%s: ram data check sum error\n", __func__); + else + aw_dev_info("%s: ram data check sum pass\n", __func__); + + +#endif + /* RAMINIT Disable */ + aw8622x_haptic_raminit(aw8622x, false); + mutex_unlock(&aw8622x->lock); + aw_dev_info("%s exit\n", __func__); + return ret; +} + +static int aw8622x_haptic_get_ram_number(struct aw8622x *aw8622x) +{ + unsigned char ram_data[3]; + unsigned int first_wave_addr = 0; + + aw_dev_info("%s enter!\n", __func__); + if (!aw8622x->ram_init) { + aw_dev_err("%s: ram init faild, ram_num = 0!\n", + __func__); + return -EPERM; + } + + mutex_lock(&aw8622x->lock); + /* RAMINIT Enable */ + aw8622x_haptic_raminit(aw8622x, true); + aw8622x_haptic_stop(aw8622x); + aw8622x_set_ram_addr(aw8622x); + aw8622x_i2c_read(aw8622x, AW8622X_REG_RAMDATA, ram_data, + AW_I2C_BYTE_THREE); + first_wave_addr = (ram_data[1] << 8 | ram_data[2]); + aw8622x->ram.ram_num = + (first_wave_addr - aw8622x->ram.base_addr - 1) / 4; + aw_dev_info("%s: ram_version = 0x%02x\n", __func__, ram_data[0]); + aw_dev_info("%s: first waveform addr = 0x%04x\n", __func__, + first_wave_addr); + aw_dev_info("%s: ram_num = %d\n", __func__, aw8622x->ram.ram_num); + /* RAMINIT Disable */ + aw8622x_haptic_raminit(aw8622x, false); + mutex_unlock(&aw8622x->lock); + + return 0; +} + +static void aw8622x_ram_check(const struct firmware *cont, void *context) +{ + unsigned short check_sum = 0; + int i = 0; + int ret = 0; + struct aw8622x *aw8622x = context; + struct aw8622x_container *aw8622x_fw; +#ifdef AW_READ_BIN_FLEXBALLY + static unsigned char load_cont; + int ram_timer_val = 1000; + + load_cont++; +#endif + aw_dev_info("%s enter\n", __func__); + if (!cont) { + aw_dev_err("%s: failed to read %s\n", __func__, + aw8622x_ram_name); + release_firmware(cont); +#ifdef AW_READ_BIN_FLEXBALLY + if (load_cont <= 20) { + schedule_delayed_work(&aw8622x->ram_work, + msecs_to_jiffies(ram_timer_val)); + aw_dev_info("%s:start hrtimer: load_cont=%d\n", + __func__, load_cont); + } +#endif + return; + } + aw_dev_info("%s: loaded %s - size: %zu bytes\n", __func__, + aw8622x_ram_name, cont ? cont->size : 0); +/* +* for(i=0; i < cont->size; i++) { +* aw_dev_info("%s: addr: 0x%04x, data: 0x%02X\n", +* __func__, i, *(cont->data+i)); +* } +*/ + /* check sum */ + for (i = 2; i < cont->size; i++) + check_sum += cont->data[i]; + if (check_sum == (unsigned short)((cont->data[0]<<8)|(cont->data[1]))) { + + aw_dev_info("%s: check sum pass: 0x%04x\n", + __func__, check_sum); + aw8622x->ram.check_sum = check_sum; + } else { + aw_dev_err("%s: check sum err: check_sum=0x%04x\n", __func__, + check_sum); + return; + } + + /* aw8622x ram update less then 128kB */ + aw8622x_fw = kzalloc(cont->size + sizeof(int), GFP_KERNEL); + if (!aw8622x_fw) { + release_firmware(cont); + aw_dev_err("%s: Error allocating memory\n", + __func__); + return; + } + aw8622x_fw->len = cont->size; + memcpy(aw8622x_fw->data, cont->data, cont->size); + release_firmware(cont); + ret = aw8622x_container_update(aw8622x, aw8622x_fw); + if (ret) { + kfree(aw8622x_fw); + aw8622x->ram.len = 0; + aw_dev_err("%s: ram firmware update failed!\n", __func__); + } else { + aw8622x->ram_init = 1; + aw8622x->ram.len = aw8622x_fw->len; + kfree(aw8622x_fw); + aw_dev_info("%s: ram firmware update complete!\n", __func__); + } + aw8622x_haptic_get_ram_number(aw8622x); + +} + +static int aw8622x_ram_update(struct aw8622x *aw8622x) +{ + aw8622x->ram_init = 0; + aw8622x->rtp_init = 0; + + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw8622x_ram_name, aw8622x->dev, + GFP_KERNEL, aw8622x, aw8622x_ram_check); +} + +static int aw8622x_rtp_trim_lra_calibration(struct aw8622x *aw8622x) +{ + unsigned char reg_val = 0; + unsigned int fre_val = 0; + unsigned int theory_time = 0; + unsigned int lra_trim_code = 0; + unsigned int rate = 0; + + aw8622x_i2c_read(aw8622x, AW8622X_REG_SYSCTRL2, ®_val, + AW_I2C_BYTE_ONE); + fre_val = (reg_val & AW8622X_BIT_SYSCTRL2_RATE) >> 0; + + if (fre_val == AW8622X_BIT_SYSCTRL2_RATE_48K) + rate = 48000; + else if (fre_val == AW8622X_BIT_SYSCTRL2_RATE_24K) + rate = 24000; + else + rate = 12000; + + theory_time = (aw8622x->rtp_len / rate) * 1000000; + + aw_dev_info("%s microsecond:%ld theory_time = %d\n", __func__, + aw8622x->microsecond, theory_time); + + lra_trim_code = aw8622x_osc_trim_calculation(aw8622x, theory_time); + if (lra_trim_code >= 0) { + aw8622x->osc_cali_data = lra_trim_code; + aw8622x_haptic_upload_lra(aw8622x, AW8622X_OSC_CALI); + } + return 0; +} + +static enum hrtimer_restart aw8622x_vibrator_timer_func(struct hrtimer *timer) +{ + struct aw8622x *aw8622x = container_of(timer, struct aw8622x, timer); + + aw_dev_info("%s enter\n", __func__); + aw8622x->state = 0; + schedule_work(&aw8622x->vibrator_work); + + return HRTIMER_NORESTART; +} + +static int aw8622x_haptic_trig_param_init(struct aw8622x *aw8622x) +{ + aw_dev_info("%s enter\n", __func__); + + if ((aw8622x->name == AW86224_5) && (aw8622x->isUsedIntn)) + return 0; + /* trig1 date */ + aw8622x->trig[0].trig_level = aw8622x->dts_info.trig_config[0]; + aw8622x->trig[0].trig_polar = aw8622x->dts_info.trig_config[1]; + aw8622x->trig[0].pos_enable = aw8622x->dts_info.trig_config[2]; + aw8622x->trig[0].pos_sequence = aw8622x->dts_info.trig_config[3]; + aw8622x->trig[0].neg_enable = aw8622x->dts_info.trig_config[4]; + aw8622x->trig[0].neg_sequence = aw8622x->dts_info.trig_config[5]; + aw8622x->trig[0].trig_brk = aw8622x->dts_info.trig_config[6]; + aw_dev_info("%s: trig1 date init ok!\n", __func__); + if ((aw8622x->name == AW86224_5) && (!aw8622x->isUsedIntn)) { + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL2, + AW8622X_BIT_SYSCTRL2_INTN_PIN_MASK, + AW8622X_BIT_SYSCTRL2_TRIG1); + return 0; + } + + /* trig2 date */ + aw8622x->trig[1].trig_level = aw8622x->dts_info.trig_config[7 + 0]; + aw8622x->trig[1].trig_polar = aw8622x->dts_info.trig_config[7 + 1]; + aw8622x->trig[1].pos_enable = aw8622x->dts_info.trig_config[7 + 2]; + aw8622x->trig[1].pos_sequence = aw8622x->dts_info.trig_config[7 + 3]; + aw8622x->trig[1].neg_enable = aw8622x->dts_info.trig_config[7 + 4]; + aw8622x->trig[1].neg_sequence = aw8622x->dts_info.trig_config[7 + 5]; + aw8622x->trig[1].trig_brk = aw8622x->dts_info.trig_config[7 + 6]; + aw_dev_info("%s: trig2 date init ok!\n", __func__); + + /* trig3 date */ + aw8622x->trig[2].trig_level = aw8622x->dts_info.trig_config[14 + 0]; + aw8622x->trig[2].trig_polar = aw8622x->dts_info.trig_config[14 + 1]; + aw8622x->trig[2].pos_enable = aw8622x->dts_info.trig_config[14 + 2]; + aw8622x->trig[2].pos_sequence = aw8622x->dts_info.trig_config[14 + 3]; + aw8622x->trig[2].neg_enable = aw8622x->dts_info.trig_config[14 + 4]; + aw8622x->trig[2].neg_sequence = aw8622x->dts_info.trig_config[14 + 5]; + aw8622x->trig[2].trig_brk = aw8622x->dts_info.trig_config[14 + 6]; + aw_dev_info("%s: trig3 date init ok!\n", __func__); + + return 0; +} + +static int aw8622x_haptic_trig_param_config(struct aw8622x *aw8622x) +{ + unsigned char trig1_polar_lev_brk = 0x00; + unsigned char trig2_polar_lev_brk = 0x00; + unsigned char trig3_polar_lev_brk = 0x00; + unsigned char trig1_pos_seq = 0x00; + unsigned char trig2_pos_seq = 0x00; + unsigned char trig3_pos_seq = 0x00; + unsigned char trig1_neg_seq = 0x00; + unsigned char trig2_neg_seq = 0x00; + unsigned char trig3_neg_seq = 0x00; + + aw_dev_info("%s enter\n", __func__); + + if ((aw8622x->name == AW86224_5) && (aw8622x->isUsedIntn)) + return 0; + /* trig1 config */ + trig1_polar_lev_brk = aw8622x->trig[0].trig_polar << 2 | + aw8622x->trig[0].trig_level << 1 | + aw8622x->trig[0].trig_brk; + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_TRGCFG7, + AW8622X_BIT_TRGCFG7_TRG1_POR_LEV_BRK_MASK, + trig1_polar_lev_brk << 5); + + trig1_pos_seq = aw8622x->trig[0].pos_enable << 7 | + aw8622x->trig[0].pos_sequence; + aw8622x_i2c_write(aw8622x, AW8622X_REG_TRGCFG1, &trig1_pos_seq, + AW_I2C_BYTE_ONE); + + trig1_neg_seq = aw8622x->trig[0].neg_enable << 7 | + aw8622x->trig[0].neg_sequence; + aw8622x_i2c_write(aw8622x, AW8622X_REG_TRGCFG4, &trig1_neg_seq, + AW_I2C_BYTE_ONE); + + aw_dev_info("%s: trig1 date config ok!\n", __func__); + + if ((aw8622x->name == AW86224_5) && (!aw8622x->isUsedIntn)) { + aw_dev_info("%s: intn pin is trig.\n", __func__); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL2, + AW8622X_BIT_SYSCTRL2_INTN_PIN_MASK, + AW8622X_BIT_SYSCTRL2_TRIG1); + return 0; + } + /* trig2 config */ + trig2_polar_lev_brk = aw8622x->trig[1].trig_polar << 2 | + aw8622x->trig[1].trig_level << 1 | + aw8622x->trig[1].trig_brk; + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_TRGCFG7, + AW8622X_BIT_TRGCFG7_TRG2_POR_LEV_BRK_MASK, + trig2_polar_lev_brk << 1); + trig2_pos_seq = aw8622x->trig[1].pos_enable << 7 | + aw8622x->trig[1].pos_sequence; + aw8622x_i2c_write(aw8622x, AW8622X_REG_TRGCFG2, &trig2_pos_seq, + AW_I2C_BYTE_ONE); + trig2_neg_seq = aw8622x->trig[1].neg_enable << 7 | + aw8622x->trig[1].neg_sequence; + aw8622x_i2c_write(aw8622x, AW8622X_REG_TRGCFG5, &trig2_neg_seq, + AW_I2C_BYTE_ONE); + aw_dev_info("%s: trig2 date config ok!\n", __func__); + + /* trig3 config */ + trig3_polar_lev_brk = aw8622x->trig[2].trig_polar << 2 | + aw8622x->trig[2].trig_level << 1 | + aw8622x->trig[2].trig_brk; + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_TRGCFG8, + AW8622X_BIT_TRGCFG8_TRG3_POR_LEV_BRK_MASK, + trig3_polar_lev_brk << 5); + trig3_pos_seq = aw8622x->trig[2].pos_enable << 7 | + aw8622x->trig[2].pos_sequence; + aw8622x_i2c_write(aw8622x, AW8622X_REG_TRGCFG3, &trig3_pos_seq, + AW_I2C_BYTE_ONE); + trig3_neg_seq = aw8622x->trig[2].neg_enable << 7 | + aw8622x->trig[2].neg_sequence; + aw8622x_i2c_write(aw8622x, AW8622X_REG_TRGCFG6, &trig3_neg_seq, + AW_I2C_BYTE_ONE); + aw_dev_info("%s: trig3 date config ok!\n", __func__); + + return 0; +} + +/***************************************************** + * + * motor protect + * + *****************************************************/ +static int aw8622x_haptic_swicth_motor_protect_config(struct aw8622x *aw8622x, + unsigned char addr, + unsigned char val) +{ + aw_dev_info("%s enter\n", __func__); + if (addr == 1) { + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_DETCFG1, + AW8622X_BIT_DETCFG1_PRCT_MODE_MASK, + AW8622X_BIT_DETCFG1_PRCT_MODE_VALID); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_PWMCFG1, + AW8622X_BIT_PWMCFG1_PRC_EN_MASK, + AW8622X_BIT_PWMCFG1_PRC_ENABLE); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_PWMCFG3, + AW8622X_BIT_PWMCFG3_PR_EN_MASK, + AW8622X_BIT_PWMCFG3_PR_ENABLE); + } else if (addr == 0) { + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_DETCFG1, + AW8622X_BIT_DETCFG1_PRCT_MODE_MASK, + AW8622X_BIT_DETCFG1_PRCT_MODE_INVALID); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_PWMCFG1, + AW8622X_BIT_PWMCFG1_PRC_EN_MASK, + AW8622X_BIT_PWMCFG1_PRC_DISABLE); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_PWMCFG3, + AW8622X_BIT_PWMCFG3_PR_EN_MASK, + AW8622X_BIT_PWMCFG3_PR_DISABLE); + } else if (addr == 0x2d) { + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_PWMCFG1, + AW8622X_BIT_PWMCFG1_PRCTIME_MASK, val); + } else if (addr == 0x3e) { + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_PWMCFG3, + AW8622X_BIT_PWMCFG3_PRLVL_MASK, val); + } else if (addr == 0x3f) { + aw8622x_i2c_write(aw8622x, AW8622X_REG_PWMCFG4, &val, + AW_I2C_BYTE_ONE); + } + return 0; +} + +static int aw8622x_haptic_f0_calibration(struct aw8622x *aw8622x) +{ + unsigned char reg_val = 0; + char f0_cali_lra = 0; + unsigned int f0_limit = 0; + unsigned int f0_cali_min = aw8622x->dts_info.f0_ref * + (100 - aw8622x->dts_info.f0_cali_percent) / 100; + unsigned int f0_cali_max = aw8622x->dts_info.f0_ref * + (100 + aw8622x->dts_info.f0_cali_percent) / 100; + int ret = 0; + int f0_cali_step = 0; + + aw_dev_info("%s enter\n", __func__); + /* + * aw8622x_haptic_upload_lra(aw8622x, AW8622X_WRITE_ZERO); + */ + if (aw8622x_haptic_cont_get_f0(aw8622x)) { + aw_dev_err("%s get f0 error, user defafult f0\n", + __func__); + } else { + /* max and min limit */ + f0_limit = aw8622x->f0; + aw_dev_info("%s f0_ref = %d, f0_cali_min = %d, f0_cali_max = %d, f0 = %d\n", + __func__, aw8622x->dts_info.f0_ref, + f0_cali_min, f0_cali_max, aw8622x->f0); + + if ((aw8622x->f0 < f0_cali_min) || aw8622x->f0 > f0_cali_max) { + aw_dev_err("%s f0 calibration out of range = %d!\n", + __func__, aw8622x->f0); + f0_limit = aw8622x->dts_info.f0_ref; + return -ERANGE; + } + aw_dev_info("%s f0_limit = %d\n", __func__, + (int)f0_limit); + /* calculate cali step */ + f0_cali_step = 100000 * ((int)f0_limit - + (int)aw8622x->dts_info.f0_ref) / + ((int)f0_limit * 24); + aw_dev_info("%s f0_cali_step = %d\n", __func__, + f0_cali_step); + if (f0_cali_step >= 0) { /*f0_cali_step >= 0 */ + if (f0_cali_step % 10 >= 5) + f0_cali_step = 32 + (f0_cali_step / 10 + 1); + else + f0_cali_step = 32 + f0_cali_step / 10; + } else { /* f0_cali_step < 0 */ + if (f0_cali_step % 10 <= -5) + f0_cali_step = 32 + (f0_cali_step / 10 - 1); + else + f0_cali_step = 32 + f0_cali_step / 10; + } + if (f0_cali_step > 31) + f0_cali_lra = (char)f0_cali_step - 32; + else + f0_cali_lra = (char)f0_cali_step + 32; + /* update cali step */ + aw8622x->f0_cali_data = (int)f0_cali_lra; + aw8622x_haptic_upload_lra(aw8622x, AW8622X_F0_CALI); + + aw8622x_i2c_read(aw8622x, AW8622X_REG_TRIMCFG3, ®_val, + AW_I2C_BYTE_ONE); + + aw_dev_info("%s final trim_lra=0x%02x\n", + __func__, reg_val); + } + /* restore standby work mode */ + aw8622x_haptic_stop(aw8622x); + return ret; +} + +/***************************************************** + * + * haptic cont + * + *****************************************************/ +static int aw8622x_haptic_cont(struct aw8622x *aw8622x) +{ + unsigned char reg_val = 0; + + aw_dev_info("%s enter\n", __func__); + + /* work mode */ + aw8622x_haptic_play_mode(aw8622x, AW8622X_HAPTIC_CONT_MODE); + /* cont config */ + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_CONTCFG6, + (AW8622X_BIT_CONTCFG6_TRACK_EN_MASK & + AW8622X_BIT_CONTCFG6_DRV1_LVL_MASK), + (AW8622X_BIT_CONTCFG6_TRACK_ENABLE | + (unsigned char)aw8622x->cont_drv1_lvl)); + reg_val = (unsigned char)aw8622x->cont_drv2_lvl; + aw8622x_i2c_write(aw8622x, AW8622X_REG_CONTCFG7, ®_val, + AW_I2C_BYTE_ONE); + /* DRV2_TIME */ + reg_val = 0xFF; + aw8622x_i2c_write(aw8622x, AW8622X_REG_CONTCFG9, ®_val, + AW_I2C_BYTE_ONE); + /* cont play go */ + aw8622x_haptic_play_go(aw8622x, true); + return 0; +} + +static int aw8622x_haptic_play_wav_seq(struct aw8622x *aw8622x, + unsigned char flag) +{ + aw_dev_info("%s enter\n", __func__); + if (flag) { + aw8622x_haptic_play_mode(aw8622x, AW8622X_HAPTIC_RAM_MODE); + aw8622x_haptic_play_go(aw8622x, true); + } + return 0; +} + +#ifdef TIMED_OUTPUT +static int aw8622x_vibrator_get_time(struct timed_output_dev *dev) +{ + struct aw8622x *aw8622x = container_of(dev, struct aw8622x, vib_dev); + + if (hrtimer_active(&aw8622x->timer)) { + ktime_t r = hrtimer_get_remaining(&aw8622x->timer); + + return ktime_to_ms(r); + } + return 0; +} + +static void aw8622x_vibrator_enable(struct timed_output_dev *dev, int value) +{ + struct aw8622x *aw8622x = container_of(dev, struct aw8622x, vib_dev); + + aw_dev_info("%s enter\n", __func__); + if (!aw8622x->ram_init) { + aw_dev_err("%s: ram init failed, not allow to play!\n", + __func__); + return; + } + mutex_lock(&aw8622x->lock); + aw8622x_haptic_stop(aw8622x); + if (value > 0) { + aw8622x_haptic_ram_vbat_comp(aw8622x, false); + aw8622x_haptic_play_wav_seq(aw8622x, value); + } + mutex_unlock(&aw8622x->lock); + aw_dev_info("%s exit\n", __func__); +} +#else +static enum led_brightness aw8622x_haptic_brightness_get(struct led_classdev + *cdev) +{ + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + return aw8622x->amplitude; +} + +static void aw8622x_haptic_brightness_set(struct led_classdev *cdev, + enum led_brightness level) +{ + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + aw_dev_info("%s enter\n", __func__); + if (!aw8622x->ram_init) { + aw_dev_err("%s: ram init failed, not allow to play!\n", + __func__); + return; + } + if (aw8622x->ram_update_flag < 0) + return; + aw8622x->amplitude = level; + mutex_lock(&aw8622x->lock); + aw8622x_haptic_stop(aw8622x); + if (aw8622x->amplitude > 0) { + aw8622x_haptic_upload_lra(aw8622x, AW8622X_F0_CALI); + aw8622x_haptic_ram_vbat_comp(aw8622x, false); + aw8622x_haptic_play_wav_seq(aw8622x, aw8622x->amplitude); + } + mutex_unlock(&aw8622x->lock); +} +#endif + +static int +aw8622x_haptic_audio_ctr_list_insert(struct haptic_audio *haptic_audio, + struct haptic_ctr *haptic_ctr, + struct device *dev) +{ + struct haptic_ctr *p_new = NULL; + + p_new = kzalloc(sizeof(struct haptic_ctr), GFP_KERNEL); + if (p_new == NULL) + return -ENOMEM; + /* update new list info */ + p_new->cnt = haptic_ctr->cnt; + p_new->cmd = haptic_ctr->cmd; + p_new->play = haptic_ctr->play; + p_new->wavseq = haptic_ctr->wavseq; + p_new->loop = haptic_ctr->loop; + p_new->gain = haptic_ctr->gain; + + INIT_LIST_HEAD(&(p_new->list)); + list_add(&(p_new->list), &(haptic_audio->ctr_list)); + return 0; +} + +static int +aw8622x_haptic_audio_ctr_list_clear(struct haptic_audio *haptic_audio) +{ + struct haptic_ctr *p_ctr = NULL; + struct haptic_ctr *p_ctr_bak = NULL; + + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), + list) { + list_del(&p_ctr->list); + kfree(p_ctr); + } + + return 0; +} + +static int aw8622x_haptic_audio_off(struct aw8622x *aw8622x) +{ + aw_dev_dbg("%s: enter\n", __func__); + mutex_lock(&aw8622x->lock); + aw8622x_haptic_set_gain(aw8622x, 0x80); + aw8622x_haptic_stop(aw8622x); + aw8622x->gun_type = AW_GUN_TYPE_DEF_VAL; + aw8622x->bullet_nr = AW_BULLET_NR_DEF_VAL; + aw8622x_haptic_audio_ctr_list_clear(&aw8622x->haptic_audio); + mutex_unlock(&aw8622x->lock); + return 0; +} + +static int aw8622x_haptic_audio_init(struct aw8622x *aw8622x) +{ + + aw_dev_dbg("%s enter\n", __func__); + aw8622x_haptic_set_wav_seq(aw8622x, 0x01, 0x00); + + return 0; +} + +static int aw8622x_haptic_activate(struct aw8622x *aw8622x) +{ + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL2, + AW8622X_BIT_SYSCTRL2_STANDBY_MASK, + AW8622X_BIT_SYSCTRL2_STANDBY_OFF); + aw8622x_interrupt_clear(aw8622x); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSINTM, + AW8622X_BIT_SYSINTM_UVLM_MASK, + AW8622X_BIT_SYSINTM_UVLM_ON); + return 0; +} + +static int aw8622x_haptic_start(struct aw8622x *aw8622x) +{ + aw8622x_haptic_activate(aw8622x); + aw8622x_haptic_play_go(aw8622x, true); + return 0; +} + +static void aw8622x_ctr_list_config(struct aw8622x *aw8622x, + struct haptic_ctr *p_ctr, + struct haptic_ctr *p_ctr_bak, + struct haptic_audio *haptic_audio) +{ + unsigned int list_input_cnt = 0; + unsigned int list_output_cnt = 0; + unsigned int list_diff_cnt = 0; + unsigned int list_del_cnt = 0; + + list_for_each_entry_safe(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + list_input_cnt = p_ctr->cnt; + break; + } + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + list_output_cnt = p_ctr->cnt; + break; + } + if (list_input_cnt > list_output_cnt) + list_diff_cnt = list_input_cnt - list_output_cnt; + if (list_input_cnt < list_output_cnt) + list_diff_cnt = 32 + list_input_cnt - list_output_cnt; + if (list_diff_cnt > 2) { + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + if ((p_ctr->play == 0) && + (AW8622X_HAPTIC_CMD_ENABLE == + (AW8622X_HAPTIC_CMD_HAPTIC & p_ctr->cmd))) { + list_del(&p_ctr->list); + kfree(p_ctr); + list_del_cnt++; + } + if (list_del_cnt == list_diff_cnt) + break; + } + } +} + +static void aw8622x_haptic_audio_work_routine(struct work_struct *work) +{ + struct aw8622x *aw8622x = container_of(work, + struct aw8622x, + haptic_audio.work); + struct haptic_audio *haptic_audio = NULL; + struct haptic_ctr *p_ctr = NULL; + struct haptic_ctr *p_ctr_bak = NULL; + unsigned int ctr_list_flag = 0; + int rtp_is_going_on = 0; + + aw_dev_dbg("%s enter\n", __func__); + + haptic_audio = &(aw8622x->haptic_audio); + mutex_lock(&aw8622x->haptic_audio.lock); + memset(&aw8622x->haptic_audio.ctr, 0, sizeof(struct haptic_ctr)); + ctr_list_flag = 0; + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + ctr_list_flag = 1; + break; + } + if (ctr_list_flag == 0) + aw_dev_info("%s: ctr list empty\n", __func__); + + if (ctr_list_flag == 1) + aw8622x_ctr_list_config(aw8622x, p_ctr, + p_ctr_bak, haptic_audio); + + /* get the last data from list */ + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + aw8622x->haptic_audio.ctr.cnt = p_ctr->cnt; + aw8622x->haptic_audio.ctr.cmd = p_ctr->cmd; + aw8622x->haptic_audio.ctr.play = p_ctr->play; + aw8622x->haptic_audio.ctr.wavseq = p_ctr->wavseq; + aw8622x->haptic_audio.ctr.loop = p_ctr->loop; + aw8622x->haptic_audio.ctr.gain = p_ctr->gain; + list_del(&p_ctr->list); + kfree(p_ctr); + break; + } + + if (aw8622x->haptic_audio.ctr.play) { + aw_dev_info("%s: cnt=%d, cmd=%d, play=%d, wavseq=%d, loop=%d, gain=%d\n", + __func__, + aw8622x->haptic_audio.ctr.cnt, + aw8622x->haptic_audio.ctr.cmd, + aw8622x->haptic_audio.ctr.play, + aw8622x->haptic_audio.ctr.wavseq, + aw8622x->haptic_audio.ctr.loop, + aw8622x->haptic_audio.ctr.gain); + } + + /* rtp mode jump */ + rtp_is_going_on = aw8622x_haptic_juge_RTP_is_going_on(aw8622x); + if (rtp_is_going_on) { + mutex_unlock(&aw8622x->haptic_audio.lock); + return; + } + mutex_unlock(&aw8622x->haptic_audio.lock); + + /*haptic play control*/ + if (AW8622X_HAPTIC_CMD_ENABLE == + (AW8622X_HAPTIC_CMD_HAPTIC & aw8622x->haptic_audio.ctr.cmd)) { + if (aw8622x->haptic_audio.ctr.play == + AW8622X_HAPTIC_PLAY_ENABLE) { + aw_dev_info("%s: haptic_audio_play_start\n", __func__); + aw_dev_info("%s: normal haptic start\n", __func__); + mutex_lock(&aw8622x->lock); + aw8622x_haptic_stop(aw8622x); + aw8622x_haptic_play_mode(aw8622x, + AW8622X_HAPTIC_RAM_MODE); + aw8622x_haptic_set_wav_seq(aw8622x, 0x00, + aw8622x->haptic_audio.ctr.wavseq); + aw8622x_haptic_set_wav_loop(aw8622x, 0x00, + aw8622x->haptic_audio.ctr.loop); + aw8622x_haptic_set_gain(aw8622x, + aw8622x->haptic_audio.ctr.gain); + aw8622x_haptic_start(aw8622x); + mutex_unlock(&aw8622x->lock); + } else if (AW8622X_HAPTIC_PLAY_STOP == + aw8622x->haptic_audio.ctr.play) { + mutex_lock(&aw8622x->lock); + aw8622x_haptic_stop(aw8622x); + mutex_unlock(&aw8622x->lock); + } else if (AW8622X_HAPTIC_PLAY_GAIN == + aw8622x->haptic_audio.ctr.play) { + mutex_lock(&aw8622x->lock); + aw8622x_haptic_set_gain(aw8622x, + aw8622x->haptic_audio.ctr.gain); + mutex_unlock(&aw8622x->lock); + } + } +} + +static ssize_t aw8622x_cont_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + aw8622x_haptic_read_cont_f0(aw8622x); + len += snprintf(buf + len, PAGE_SIZE - len, + "%d\n", aw8622x->cont_f0); + return len; +} + +static ssize_t aw8622x_cont_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + aw8622x_haptic_stop(aw8622x); + if (val) + aw8622x_haptic_cont(aw8622x); + return count; +} + +static ssize_t aw8622x_f0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char reg = 0; + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + mutex_lock(&aw8622x->lock); + + /* set d2s_gain to max to get better performance when cat f0 .*/ + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL7, + AW8622X_BIT_SYSCTRL7_D2S_GAIN_MASK, + AW8622X_BIT_SYSCTRL7_D2S_GAIN_40); + aw8622x_i2c_read(aw8622x, AW8622X_REG_TRIMCFG3, ®, AW_I2C_BYTE_ONE); + aw8622x_haptic_upload_lra(aw8622x, AW8622X_WRITE_ZERO); + aw8622x_haptic_cont_get_f0(aw8622x); + aw8622x_i2c_write(aw8622x, AW8622X_REG_TRIMCFG3, ®, AW_I2C_BYTE_ONE); + /* set d2s_gain to default when cat f0 is finished.*/ + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL7, + AW8622X_BIT_SYSCTRL7_D2S_GAIN_MASK, + aw8622x->dts_info.d2s_gain); + mutex_unlock(&aw8622x->lock); + len += snprintf(buf + len, PAGE_SIZE - len, + "%d\n", aw8622x->f0); + return len; +} + +static ssize_t aw8622x_f0_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + return count; +} + +static ssize_t aw8622x_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char i = 0; + unsigned char size = 0; + unsigned char cnt = 0; + unsigned char reg_array[AW_REG_MAX] = {0}; + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + aw_dev_dbg("%s: enter!\n", __func__); + + for (i = 0; i <= (AW8622X_REG_TRIMCFG3 + 1); i++) { + + if (i == aw8622x_reg_list[cnt] && + (cnt < sizeof(aw8622x_reg_list))) { + size++; + cnt++; + continue; + } else { + if (size != 0) { + aw8622x_i2c_read(aw8622x, + aw8622x_reg_list[cnt-size], + ®_array[cnt-size], size); + size = 0; + + } + } + } + + for (i = 0; i < sizeof(aw8622x_reg_list); i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "reg:0x%02X=0x%02X\n", aw8622x_reg_list[i], + reg_array[i]); + } + return len; +} + +static ssize_t aw8622x_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + unsigned char reg_val = 0; + unsigned int databuf[2] = { 0, 0 }; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + reg_val = (unsigned char)databuf[1]; + aw8622x_i2c_write(aw8622x, (unsigned char)databuf[0], + ®_val, AW_I2C_BYTE_ONE); + } + + return count; +} + +static ssize_t aw8622x_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + ktime_t time_rem; + s64 time_ms = 0; + + if (hrtimer_active(&aw8622x->timer)) { + time_rem = hrtimer_get_remaining(&aw8622x->timer); + time_ms = ktime_to_ms(time_rem); + } + return snprintf(buf, PAGE_SIZE, "%lld\n", time_ms); +} + +static ssize_t aw8622x_duration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + /* setting 0 on duration is NOP for now */ + if (val <= 0) + return count; + rc = aw8622x_haptic_ram_config(aw8622x, val); + if (rc < 0) { + aw_dev_info("%s: ram config failed!\n", __func__); + return count; + } + aw8622x->duration = val; + aw_dev_info("%s: duration %d !\n", __func__, aw8622x->duration); + return count; +} + +static ssize_t aw8622x_activate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + /* For now nothing to show */ + return snprintf(buf, PAGE_SIZE, "activate = %d\n", aw8622x->state); +} + +static ssize_t aw8622x_activate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + if (!aw8622x->ram_init) { + aw_dev_err("%s: ram init failed, not allow to play!\n", + __func__); + return count; + } + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + if (val != 0 && val != 1) + return count; + aw_dev_info("%s: value=%d\n", __func__, val); + mutex_lock(&aw8622x->lock); + hrtimer_cancel(&aw8622x->timer); + aw8622x->state = val; + mutex_unlock(&aw8622x->lock); + schedule_work(&aw8622x->vibrator_work); + return count; +} + +static ssize_t aw8622x_seq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char i = 0; + unsigned char reg_val[AW8622X_SEQUENCER_SIZE] = {0}; + size_t count = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + aw8622x_i2c_read(aw8622x, AW8622X_REG_WAVCFG1, reg_val, + AW8622X_SEQUENCER_SIZE); + for (i = 0; i < AW8622X_SEQUENCER_SIZE; i++) { + count += snprintf(buf+count, PAGE_SIZE-count, + "seq%d: 0x%02x\n", i+1, reg_val[i]); + aw8622x->seq[i] = reg_val[i]; + } + return count; +} + +static ssize_t aw8622x_seq_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + int i = 0; + int rc = 0; + unsigned int val = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + +#if 0 + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + if (databuf[0] > AW8622X_SEQUENCER_SIZE || + databuf[1] > aw8622x->ram.ram_num) { + aw_dev_err("%s input value out of range\n", + __func__); + return count; + } + aw_dev_info("%s: seq%d=0x%02X\n", __func__, + databuf[0], databuf[1]); + mutex_lock(&aw8622x->lock); + aw8622x->seq[databuf[0]] = (unsigned char)databuf[1]; + aw8622x_haptic_set_wav_seq(aw8622x, (unsigned char)databuf[0], + aw8622x->seq[databuf[0]]); + mutex_unlock(&aw8622x->lock); + } +#endif + /* custom for moto */ + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + //val = val << 24; + aw_dev_info("%s: seq=%d\n", __func__,val); + + mutex_lock(&aw8622x->lock); + for(i=0; i<4; i++) { + /* moto_mode_ctl[0] get databuf[0] byte4 */ + aw8622x->moto_mode_ctl[i] = (val>>((AW8622X_WAV_SEQ_SIZE-i-1)*8)) & 0xFF; + aw8622x_haptic_set_wav_seq(aw8622x, i, aw8622x->moto_mode_ctl[i]); + aw_dev_info("%s: moto_mode_ctl[%d]=%d\n", __func__,i, aw8622x->moto_mode_ctl[i]); + } + mutex_unlock(&aw8622x->lock); + + return count; +} + +static ssize_t aw8622x_loop_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char i = 0; + unsigned char reg_val[AW8622X_SEQUENCER_LOOP_SIZE] = {0}; + size_t count = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + aw8622x_i2c_read(aw8622x, AW8622X_REG_WAVCFG9, reg_val, + AW8622X_SEQUENCER_LOOP_SIZE); + + for (i = 0; i < AW8622X_SEQUENCER_LOOP_SIZE; i++) { + aw8622x->loop[i*2+0] = (reg_val[i] >> 4) & 0x0F; + aw8622x->loop[i*2+1] = (reg_val[i] >> 0) & 0x0F; + + count += snprintf(buf+count, PAGE_SIZE-count, + "seq%d_loop: 0x%02x\n", i*2+1, aw8622x->loop[i*2+0]); + count += snprintf(buf+count, PAGE_SIZE-count, + "seq%d_loop: 0x%02x\n", i*2+2, aw8622x->loop[i*2+1]); + } + return count; +} + +static ssize_t aw8622x_loop_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int databuf[2] = { 0, 0 }; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_dev_info("%s: seq%d loop=0x%02X\n", __func__, + databuf[0], databuf[1]); + mutex_lock(&aw8622x->lock); + aw8622x->loop[databuf[0]] = (unsigned char)databuf[1]; + aw8622x_haptic_set_wav_loop(aw8622x, (unsigned char)databuf[0], + aw8622x->loop[databuf[0]]); + mutex_unlock(&aw8622x->lock); + } + return count; +} + +static ssize_t aw8622x_rtp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "rtp_cnt = %d\n", + aw8622x->rtp_cnt); + return len; +} + +static ssize_t aw8622x_rtp_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, + struct aw8622x, vib_dev); + + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) { + aw_dev_info("%s: kstrtouint fail\n", __func__); + return count; + } + + mutex_lock(&aw8622x->lock); + aw8622x_haptic_stop(aw8622x); + aw8622x_haptic_set_rtp_aei(aw8622x, false); + aw8622x_interrupt_clear(aw8622x); + aw8622x->moto_rtp_runing = false; + if (val < (sizeof(aw8622x_rtp_name) / AW8622X_RTP_NAME_MAX)) { + aw8622x->rtp_file_num = val; + if (val) { + aw_dev_info("%s: aw8622x_rtp_name[%d]: %s\n", __func__, + val, aw8622x_rtp_name[val]); + + schedule_work(&aw8622x->rtp_work); + } else { + aw_dev_err("%s: rtp_file_num 0x%02X over max value\n", + __func__, aw8622x->rtp_file_num); + } + } + mutex_unlock(&aw8622x->lock); + return count; +} + +static ssize_t aw8622x_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", aw8622x->state); +} + +static ssize_t aw8622x_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw8622x_activate_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", + aw8622x->activate_mode); +} + +static ssize_t aw8622x_activate_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + mutex_lock(&aw8622x->lock); + aw8622x->activate_mode = val; + mutex_unlock(&aw8622x->lock); + return count; +} + +static ssize_t aw8622x_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char reg_val = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + aw8622x_i2c_read(aw8622x, AW8622X_REG_WAVCFG1, ®_val, + AW_I2C_BYTE_ONE); + aw8622x->index = reg_val; + return snprintf(buf, PAGE_SIZE, "index = %d\n", aw8622x->index); +} + +static ssize_t aw8622x_index_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + if (val > aw8622x->ram.ram_num) { + aw_dev_err("%s: input value out of range!\n", __func__); + return count; + } + aw_dev_info("%s: value=%d\n", __func__, val); + mutex_lock(&aw8622x->lock); + aw8622x->index = val; + aw8622x_haptic_set_repeat_wav_seq(aw8622x, aw8622x->index); + mutex_unlock(&aw8622x->lock); + return count; +} + +static ssize_t aw8622x_sram_size_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char reg_val = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + aw8622x_i2c_read(aw8622x, AW8622X_REG_RTPCFG1, ®_val, + AW_I2C_BYTE_ONE); + if ((reg_val & 0x30) == 0x20) + return snprintf(buf, PAGE_SIZE, "sram_size = 2K\n"); + else if ((reg_val & 0x30) == 0x10) + return snprintf(buf, PAGE_SIZE, "sram_size = 1K\n"); + else if ((reg_val & 0x30) == 0x30) + return snprintf(buf, PAGE_SIZE, "sram_size = 3K\n"); + return snprintf(buf, PAGE_SIZE, + "sram_size = 0x%02x error, plz check reg.\n", + reg_val & 0x30); +} + +static ssize_t aw8622x_sram_size_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + aw_dev_info("%s: value=%d\n", __func__, val); + mutex_lock(&aw8622x->lock); + if (val == AW8622X_HAPTIC_SRAM_2K) + aw8622x_sram_size(aw8622x, AW8622X_HAPTIC_SRAM_2K); + else if (val == AW8622X_HAPTIC_SRAM_1K) + aw8622x_sram_size(aw8622x, AW8622X_HAPTIC_SRAM_1K); + else if (val == AW8622X_HAPTIC_SRAM_3K) + aw8622x_sram_size(aw8622x, AW8622X_HAPTIC_SRAM_3K); + mutex_unlock(&aw8622x->lock); + return count; +} + +static ssize_t aw8622x_osc_cali_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "osc_cali_data = 0x%02X\n", + aw8622x->osc_cali_data); + + return len; +} + +static ssize_t aw8622x_osc_cali_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + mutex_lock(&aw8622x->lock); + if (val == 1) { + aw8622x_haptic_upload_lra(aw8622x, AW8622X_WRITE_ZERO); + aw8622x_rtp_osc_calibration(aw8622x); + aw8622x_rtp_trim_lra_calibration(aw8622x); + } else if (val == 2) { + aw8622x_haptic_upload_lra(aw8622x, AW8622X_OSC_CALI); + aw8622x_rtp_osc_calibration(aw8622x); + } else { + aw_dev_err("%s input value out of range\n", __func__); + } + /* osc calibration flag end, other behaviors are permitted */ + mutex_unlock(&aw8622x->lock); + + return count; +} + +static ssize_t aw8622x_gain_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char reg = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + aw8622x_i2c_read(aw8622x, AW8622X_REG_PLAYCFG2, ®, AW_I2C_BYTE_ONE); + + return snprintf(buf, PAGE_SIZE, "0x%02X\n", reg); +} + +static ssize_t aw8622x_gain_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + aw_dev_info("%s: value=%d\n", __func__, val); + if (val >= AW8622X_GAIN_MAX) + val = AW8622X_GAIN_MAX; + mutex_lock(&aw8622x->lock); + aw8622x->gain = val; + aw8622x_haptic_set_gain(aw8622x, aw8622x->gain); + mutex_unlock(&aw8622x->lock); + return count; +} + +static ssize_t aw8622x_ram_update_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int cnt = 0; + int i = 0; + int size = 0; + ssize_t len = 0; + unsigned char ram_data[AW_RAMDATA_RD_BUFFER_SIZE] = {0}; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + /* RAMINIT Enable */ + aw8622x_haptic_raminit(aw8622x, true); + aw8622x_haptic_stop(aw8622x); + aw8622x_set_ram_addr(aw8622x); + len += snprintf(buf + len, PAGE_SIZE - len, + "haptic_ram len = %d\n", aw8622x->ram.len); + while (cnt < aw8622x->ram.len) { + if ((aw8622x->ram.len - cnt) < AW_RAMDATA_RD_BUFFER_SIZE) + size = aw8622x->ram.len - cnt; + else + size = AW_RAMDATA_RD_BUFFER_SIZE; + + aw8622x_i2c_read(aw8622x, AW8622X_REG_RAMDATA, + ram_data, size); + for (i = 0; i < size; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%02X,", ram_data[i]); + } + cnt += size; + } + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + /* RAMINIT Disable */ + aw8622x_haptic_raminit(aw8622x, false); + return len; +} + +static ssize_t aw8622x_ram_update_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + if (val) + aw8622x_ram_update(aw8622x); + return count; +} + +static ssize_t aw8622x_f0_save_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "f0_cali_data = 0x%02X\n", + aw8622x->f0_cali_data); + + return len; +} + +static ssize_t aw8622x_f0_save_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + aw8622x->f0_cali_data = val; + return count; +} + +static ssize_t aw8622x_osc_save_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "osc_cali_data = 0x%02X\n", + aw8622x->osc_cali_data); + + return len; +} + +static ssize_t aw8622x_osc_save_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + aw8622x->osc_cali_data = val; + return count; +} + +static ssize_t aw8622x_trig_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + unsigned char i = 0; + unsigned char trig_num = 3; + + if (aw8622x->name == AW86224_5) + trig_num = 1; + + for (i = 0; i < trig_num; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "trig%d: trig_level=%d, trig_polar=%d", + i + 1, + aw8622x->trig[i].trig_level, + aw8622x->trig[i].trig_polar); + len += snprintf(buf + len, PAGE_SIZE - len, + "pos_enable=%d, pos_sequence=%d,", + aw8622x->trig[i].pos_enable, + aw8622x->trig[i].pos_sequence); + len += snprintf(buf + len, PAGE_SIZE - len, + "neg_enable=%d, neg_sequence=%d trig_brk=%d\n", + aw8622x->trig[i].neg_enable, + aw8622x->trig[i].neg_sequence, + aw8622x->trig[i].trig_brk); + } + + return len; +} + + + +static ssize_t aw8622x_trig_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + unsigned int databuf[9] = { 0 }; + + if (sscanf(buf, "%d %d %d %d %d %d %d %d", + &databuf[0], &databuf[1], &databuf[2], + &databuf[3], &databuf[4], &databuf[5], + &databuf[6], &databuf[7]) == 8) { + aw_dev_info("%s: %d, %d, %d, %d, %d, %d, %d, %d\n", + __func__, databuf[0], databuf[1], databuf[2], + databuf[3], databuf[4], databuf[5], databuf[6], + databuf[7]); + if ((aw8622x->name == AW86224_5) && (databuf[0])) { + aw_dev_err("%s: input seq value out of range!\n", + __func__); + return count; + } + if (databuf[0] < 0 || databuf[0] > 2) { + aw_dev_err("%s: input seq value out of range!\n", + __func__); + return count; + } + if (!aw8622x->ram_init) { + aw_dev_err("%s: ram init failed, not allow to play!\n", + __func__); + return count; + } + if (databuf[4] > aw8622x->ram.ram_num || + databuf[6] > aw8622x->ram.ram_num) { + aw_dev_err("%s: input seq value out of range!\n", + __func__); + return count; + } + aw8622x->trig[databuf[0]].trig_level = databuf[1]; + aw8622x->trig[databuf[0]].trig_polar = databuf[2]; + aw8622x->trig[databuf[0]].pos_enable = databuf[3]; + aw8622x->trig[databuf[0]].pos_sequence = databuf[4]; + aw8622x->trig[databuf[0]].neg_enable = databuf[5]; + aw8622x->trig[databuf[0]].neg_sequence = databuf[6]; + aw8622x->trig[databuf[0]].trig_brk = databuf[7]; + mutex_lock(&aw8622x->lock); + aw8622x_haptic_trig_param_config(aw8622x); + mutex_unlock(&aw8622x->lock); + } else + aw_dev_err("%s: please input eight parameters\n", __func__); + return count; +} + +static ssize_t aw8622x_ram_vbat_compensate_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + len += + snprintf(buf + len, PAGE_SIZE - len, "ram_vbat_comp = %d\n", + aw8622x->ram_vbat_compensate); + + return len; +} + +static ssize_t aw8622x_ram_vbat_compensate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + mutex_lock(&aw8622x->lock); + if (val) + aw8622x->ram_vbat_compensate = + AW8622X_HAPTIC_RAM_VBAT_COMP_ENABLE; + else + aw8622x->ram_vbat_compensate = + AW8622X_HAPTIC_RAM_VBAT_COMP_DISABLE; + mutex_unlock(&aw8622x->lock); + + return count; +} + +static ssize_t aw8622x_cali_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + unsigned char reg = 0; + + mutex_lock(&aw8622x->lock); + aw8622x_i2c_read(aw8622x, AW8622X_REG_TRIMCFG3, ®, + AW_I2C_BYTE_ONE); + aw8622x_haptic_upload_lra(aw8622x, AW8622X_F0_CALI); + aw8622x_haptic_cont_get_f0(aw8622x); + aw8622x_i2c_write(aw8622x, AW8622X_REG_TRIMCFG3, ®, + AW_I2C_BYTE_ONE); + mutex_unlock(&aw8622x->lock); + len += snprintf(buf + len, PAGE_SIZE - len, "%d\n", aw8622x->f0); + return len; +} + +static ssize_t aw8622x_cali_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + if (val) { + mutex_lock(&aw8622x->lock); + aw8622x_haptic_upload_lra(aw8622x, AW8622X_WRITE_ZERO); + aw8622x_haptic_f0_calibration(aw8622x); + mutex_unlock(&aw8622x->lock); + } + return count; +} + +static ssize_t aw8622x_cont_wait_num_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_wait_num = 0x%02X\n", aw8622x->cont_wait_num); + return len; +} + +static ssize_t aw8622x_cont_wait_num_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned char reg_val = 0; + int err, val; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + err = kstrtoint(buf, 16, &val); + if (err != 0) { + aw_dev_err("%s format not match!", __func__); + return count; + } + aw8622x->cont_wait_num = val; + reg_val = aw8622x->cont_wait_num; + aw8622x_i2c_write(aw8622x, AW8622X_REG_CONTCFG4, ®_val, + AW_I2C_BYTE_ONE); + return count; +} + +static ssize_t aw8622x_cont_drv_lvl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_lvl = 0x%02X\n", + aw8622x->cont_drv1_lvl); + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv2_lvl = 0x%02X\n", + aw8622x->cont_drv2_lvl); + return len; +} + +static ssize_t aw8622x_cont_drv_lvl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned char reg_val = 0; + unsigned int databuf[2] = { 0, 0 }; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw8622x->cont_drv1_lvl = databuf[0]; + aw8622x->cont_drv2_lvl = databuf[1]; + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_CONTCFG6, + AW8622X_BIT_CONTCFG6_DRV1_LVL_MASK, + aw8622x->cont_drv1_lvl); + reg_val = (unsigned char)aw8622x->cont_drv2_lvl; + aw8622x_i2c_write(aw8622x, AW8622X_REG_CONTCFG7, ®_val, + AW_I2C_BYTE_ONE); + } + return count; +} + +static ssize_t aw8622x_cont_drv_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_time = 0x%02X\n", + aw8622x->cont_drv1_time); + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv2_time = 0x%02X\n", + aw8622x->cont_drv2_time); + return len; +} + +static ssize_t aw8622x_cont_drv_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned char reg_val[2] = {0}; + unsigned int databuf[2] = { 0, 0 }; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw8622x->cont_drv1_time = databuf[0]; + aw8622x->cont_drv2_time = databuf[1]; + reg_val[0] = (unsigned char)aw8622x->cont_drv1_time; + reg_val[1] = (unsigned char)aw8622x->cont_drv2_time; + aw8622x_i2c_write(aw8622x, AW8622X_REG_CONTCFG8, reg_val, + AW_I2C_BYTE_TWO); + } + return count; +} + +static ssize_t aw8622x_cont_brk_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "cont_brk_time = 0x%02X\n", + aw8622x->cont_brk_time); + return len; +} + +static ssize_t aw8622x_cont_brk_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned char reg_val = 0; + int err, val; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + err = kstrtoint(buf, 16, &val); + if (err != 0) { + aw_dev_err("%s format not match!", __func__); + return count; + } + aw8622x->cont_brk_time = val; + reg_val = aw8622x->cont_brk_time; + aw8622x_i2c_write(aw8622x, AW8622X_REG_CONTCFG10, ®_val, + AW_I2C_BYTE_ONE); + return count; +} + +static ssize_t aw8622x_vbat_monitor_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + mutex_lock(&aw8622x->lock); + aw8622x_haptic_get_vbat(aw8622x); + len += snprintf(buf + len, PAGE_SIZE - len, "vbat_monitor = %d\n", + aw8622x->vbat); + mutex_unlock(&aw8622x->lock); + + return len; +} + +static ssize_t aw8622x_vbat_monitor_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw8622x_lra_resistance_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + aw8622x_haptic_get_lra_resistance(aw8622x); + len += snprintf(buf + len, PAGE_SIZE - len, "lra_resistance = %d\n", + aw8622x->lra); + return len; +} + +static ssize_t aw8622x_lra_resistance_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw8622x_prctmode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + unsigned char reg_val = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + aw8622x_i2c_read(aw8622x, AW8622X_REG_DETCFG1, ®_val, + AW_I2C_BYTE_ONE); + + len += snprintf(buf + len, PAGE_SIZE - len, "prctmode = %d\n", + reg_val & 0x08); + return len; +} + +static ssize_t aw8622x_prctmode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int databuf[2] = { 0, 0 }; + unsigned int addr = 0; + unsigned int val = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + addr = databuf[0]; + val = databuf[1]; + mutex_lock(&aw8622x->lock); + aw8622x_haptic_swicth_motor_protect_config(aw8622x, addr, val); + mutex_unlock(&aw8622x->lock); + } + return count; +} + +static ssize_t aw8622x_gun_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + return snprintf(buf, PAGE_SIZE, "0x%02x\n", aw8622x->gun_type); + +} + +static ssize_t aw8622x_gun_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + aw_dev_info("%s: value=%d\n", __func__, val); + mutex_lock(&aw8622x->lock); + aw8622x->gun_type = val; + mutex_unlock(&aw8622x->lock); + return count; +} + +static ssize_t aw8622x_bullet_nr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + return snprintf(buf, PAGE_SIZE, "0x%02x\n", aw8622x->bullet_nr); +} + +static ssize_t aw8622x_bullet_nr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + aw_dev_info("%s: value=%d\n", __func__, val); + mutex_lock(&aw8622x->lock); + aw8622x->bullet_nr = val; + mutex_unlock(&aw8622x->lock); + return count; +} + +static ssize_t aw8622x_haptic_audio_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + len += snprintf(buf+len, PAGE_SIZE-len, + "%d\n", aw8622x->haptic_audio.ctr.cnt); + return len; +} + +static int aw8622x_audio_config(struct aw8622x *aw8622x, + unsigned int databuf[]) +{ + struct haptic_ctr *hap_ctr = NULL; + + hap_ctr = kzalloc(sizeof(struct haptic_ctr), GFP_KERNEL); + if (hap_ctr == NULL) + return -ENOMEM; + mutex_lock(&aw8622x->haptic_audio.lock); + hap_ctr->cnt = (unsigned char)databuf[0]; + hap_ctr->cmd = (unsigned char)databuf[1]; + hap_ctr->play = (unsigned char)databuf[2]; + hap_ctr->wavseq = (unsigned char)databuf[3]; + hap_ctr->loop = (unsigned char)databuf[4]; + hap_ctr->gain = (unsigned char)databuf[5]; + aw8622x_haptic_audio_ctr_list_insert(&aw8622x->haptic_audio, + hap_ctr, aw8622x->dev); + if (hap_ctr->cmd == 0xff) { + aw_dev_info("%s: haptic_audio stop\n", __func__); + if (hrtimer_active(&aw8622x->haptic_audio.timer)) { + aw_dev_info( "%s: cancel haptic_audio_timer\n", + __func__); + hrtimer_cancel(&aw8622x->haptic_audio.timer); + aw8622x->haptic_audio.ctr.cnt = 0; + aw8622x_haptic_audio_off(aw8622x); + } + } else { + if (hrtimer_active(&aw8622x->haptic_audio.timer)) { + } else { + aw_dev_info( "%s: start haptic_audio_timer\n", + __func__); + aw8622x_haptic_audio_init(aw8622x); + hrtimer_start(&aw8622x->haptic_audio.timer, + ktime_set(aw8622x->haptic_audio.delay_val/1000000, + (aw8622x->haptic_audio.delay_val%1000000)*1000), + HRTIMER_MODE_REL); + } + } + kfree(hap_ctr); + mutex_unlock(&aw8622x->haptic_audio.lock); + return 0; +} + +static ssize_t aw8622x_haptic_audio_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int databuf[6] = {0}; + int rtp_is_going_on = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + + rtp_is_going_on = aw8622x_haptic_juge_RTP_is_going_on(aw8622x); + if (rtp_is_going_on) { + aw_dev_info("%s: RTP is runing, stop audio haptic\n", __func__); + return count; + } + if (!aw8622x->ram_init) + return count; + + if (sscanf(buf, "%d %d %d %d %d %d", + &databuf[0], &databuf[1], &databuf[2], + &databuf[3], &databuf[4], &databuf[5]) == 6) { + if (databuf[2]) { + aw_dev_info("%s: cnt=%d, cmd=%d, play=%d\n", + __func__, databuf[0], databuf[1], databuf[2]); + aw_dev_info("%s: wavseq=%d, loop=%d, gain=%d\n", + __func__, databuf[3], databuf[4], databuf[5]); + aw8622x_audio_config(aw8622x, databuf); + + } + } + return count; +} + +static ssize_t aw8622x_ram_num_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + aw8622x_haptic_get_ram_number(aw8622x); + len += snprintf(buf+len, PAGE_SIZE-len, + "ram_num = %d\n", aw8622x->ram.ram_num); + return len; +} + +static ssize_t aw8622x_awrw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + unsigned char i = 0; + unsigned char reg_num = aw8622x->aw_i2c_package.reg_num; + unsigned char flag = aw8622x->aw_i2c_package.flag; + unsigned char *reg_data = aw8622x->aw_i2c_package.reg_data; + ssize_t len = 0; + + if (!reg_num) { + aw_dev_err("%s: awrw parameter error\n", + __func__); + return len; + } + if (flag == AW8622X_READ) { + for (i = 0; i < reg_num; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%02x,", reg_data[i]); + } + + len += snprintf(buf + len - 1, PAGE_SIZE - len, "\n"); + } + + return len; +} + +static ssize_t aw8622x_awrw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int datatype[3] = { 0 }; + int ret = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8622x *aw8622x = container_of(cdev, struct aw8622x, vib_dev); + + if (sscanf(buf, "%x %x %x", &datatype[0], &datatype[1], + &datatype[2]) == 3) { + if (!datatype[1]) { + aw_dev_err("%s: awrw parameter error\n", + __func__); + return count; + } + aw8622x->aw_i2c_package.flag = (unsigned char)datatype[0]; + aw8622x->aw_i2c_package.reg_num = (unsigned char)datatype[1]; + aw8622x->aw_i2c_package.first_addr = (unsigned char)datatype[2]; + if (aw8622x->aw_i2c_package.flag == AW8622X_WRITE) { + ret = aw8622x_parse_data(aw8622x, buf); + if (ret < 0) + return count; + aw8622x_i2c_write(aw8622x, + aw8622x->aw_i2c_package.first_addr, + aw8622x->aw_i2c_package.reg_data, + aw8622x->aw_i2c_package.reg_num); + } + if (aw8622x->aw_i2c_package.flag == AW8622X_READ) + aw8622x_i2c_read(aw8622x, + aw8622x->aw_i2c_package.first_addr, + aw8622x->aw_i2c_package.reg_data, + aw8622x->aw_i2c_package.reg_num); + } else + aw_dev_err("%s: missing number of parameters\n", + __func__); + + return count; +} + +static DEVICE_ATTR(f0, 0644, aw8622x_f0_show, aw8622x_f0_store); +static DEVICE_ATTR(cont, 0644, aw8622x_cont_show, aw8622x_cont_store); +static DEVICE_ATTR(register, 0644, aw8622x_reg_show, aw8622x_reg_store); +static DEVICE_ATTR(duration, 0644, aw8622x_duration_show, + aw8622x_duration_store); +static DEVICE_ATTR(index, 0644, aw8622x_index_show, aw8622x_index_store); +static DEVICE_ATTR(activate, 0644, aw8622x_activate_show, + aw8622x_activate_store); +static DEVICE_ATTR(activate_mode, 0644, aw8622x_activate_mode_show, + aw8622x_activate_mode_store); +static DEVICE_ATTR(seq, 0644, aw8622x_seq_show, aw8622x_seq_store); +static DEVICE_ATTR(loop, 0644, aw8622x_loop_show, aw8622x_loop_store); +static DEVICE_ATTR(rtp, 0644, aw8622x_rtp_show, aw8622x_rtp_store); +static DEVICE_ATTR(state, 0644, aw8622x_state_show, aw8622x_state_store); +static DEVICE_ATTR(sram_size, 0644, aw8622x_sram_size_show, + aw8622x_sram_size_store); +static DEVICE_ATTR(osc_cali, 0644, aw8622x_osc_cali_show, + aw8622x_osc_cali_store); +static DEVICE_ATTR(gain, 0644, aw8622x_gain_show, aw8622x_gain_store); +static DEVICE_ATTR(ram_update, 0644, aw8622x_ram_update_show, + aw8622x_ram_update_store); +static DEVICE_ATTR(f0_save, 0644, aw8622x_f0_save_show, aw8622x_f0_save_store); +static DEVICE_ATTR(osc_save, 0644, aw8622x_osc_save_show, + aw8622x_osc_save_store); +static DEVICE_ATTR(trig, 0644, aw8622x_trig_show, aw8622x_trig_store); +static DEVICE_ATTR(ram_vbat_comp, 0644, aw8622x_ram_vbat_compensate_show, + aw8622x_ram_vbat_compensate_store); +static DEVICE_ATTR(cali, 0644, aw8622x_cali_show, aw8622x_cali_store); +static DEVICE_ATTR(cont_wait_num, 0644, aw8622x_cont_wait_num_show, + aw8622x_cont_wait_num_store); +static DEVICE_ATTR(cont_drv_lvl, 0644, aw8622x_cont_drv_lvl_show, + aw8622x_cont_drv_lvl_store); +static DEVICE_ATTR(cont_drv_time, 0644, aw8622x_cont_drv_time_show, + aw8622x_cont_drv_time_store); +static DEVICE_ATTR(cont_brk_time, 0644, aw8622x_cont_brk_time_show, + aw8622x_cont_brk_time_store); +static DEVICE_ATTR(vbat_monitor, 0644, aw8622x_vbat_monitor_show, + aw8622x_vbat_monitor_store); +static DEVICE_ATTR(lra_resistance, 0644, aw8622x_lra_resistance_show, + aw8622x_lra_resistance_store); +static DEVICE_ATTR(prctmode, 0644, aw8622x_prctmode_show, + aw8622x_prctmode_store); +static DEVICE_ATTR(gun_type, 0644, aw8622x_gun_type_show, + aw8622x_gun_type_store); +static DEVICE_ATTR(bullet_nr, 0644, aw8622x_bullet_nr_show, + aw8622x_bullet_nr_store); +static DEVICE_ATTR(haptic_audio, 0644, aw8622x_haptic_audio_show, + aw8622x_haptic_audio_store); +static DEVICE_ATTR(ram_num, 0644, aw8622x_ram_num_show, NULL); +static DEVICE_ATTR(awrw, 0644, aw8622x_awrw_show, aw8622x_awrw_store); +static struct attribute *aw8622x_vibrator_attributes[] = { + &dev_attr_state.attr, + &dev_attr_duration.attr, + &dev_attr_activate.attr, + &dev_attr_activate_mode.attr, + &dev_attr_index.attr, + &dev_attr_gain.attr, + &dev_attr_seq.attr, + &dev_attr_loop.attr, + &dev_attr_register.attr, + &dev_attr_rtp.attr, + &dev_attr_ram_update.attr, + &dev_attr_f0.attr, + &dev_attr_cali.attr, + &dev_attr_f0_save.attr, + &dev_attr_osc_save.attr, + &dev_attr_cont.attr, + &dev_attr_cont_wait_num.attr, + &dev_attr_cont_drv_lvl.attr, + &dev_attr_cont_drv_time.attr, + &dev_attr_cont_brk_time.attr, + &dev_attr_vbat_monitor.attr, + &dev_attr_lra_resistance.attr, + &dev_attr_prctmode.attr, + &dev_attr_sram_size.attr, + &dev_attr_ram_vbat_comp.attr, + &dev_attr_osc_cali.attr, + &dev_attr_gun_type.attr, + &dev_attr_bullet_nr.attr, + &dev_attr_haptic_audio.attr, + &dev_attr_trig.attr, + &dev_attr_ram_num.attr, + &dev_attr_awrw.attr, + NULL +}; + +struct attribute_group aw8622x_vibrator_attribute_group = { + .attrs = aw8622x_vibrator_attributes +}; + +static void aw8622x_haptic_ram_play(struct aw8622x *aw8622x, unsigned char type) +{ + aw_dev_info("%s index = %d\n", __func__, aw8622x->index); + + mutex_lock(&aw8622x->ram_lock); + if (type == AW8622X_HAPTIC_RAM_LOOP_MODE) + aw8622x_haptic_set_repeat_wav_seq(aw8622x, aw8622x->index); + + if (type == AW8622X_HAPTIC_RAM_MODE) { + aw8622x_haptic_set_wav_seq(aw8622x, 0x00, aw8622x->index); + aw8622x_haptic_set_wav_seq(aw8622x, 0x01, 0x00); + aw8622x_haptic_set_wav_loop(aw8622x, 0x00, 0x00); + } + aw8622x_haptic_play_mode(aw8622x, type); + aw8622x_haptic_start(aw8622x); + mutex_unlock(&aw8622x->ram_lock); +} + +static void aw8622x_vibrator_work_routine(struct work_struct *work) +{ + struct aw8622x *aw8622x = container_of(work, struct aw8622x, + vibrator_work); + + int moto_mode = 0; + aw_dev_info("%s enter\n", __func__); + + mutex_lock(&aw8622x->lock); + /* Enter standby mode */ + aw8622x_haptic_stop(aw8622x); + aw8622x_haptic_upload_lra(aw8622x, AW8622X_F0_CALI); + + /* moto mode select */ + moto_mode = aw8622x->moto_mode_ctl[0]; + if (moto_mode > 2 || aw8622x->duration > 0) { + /* rtp mode */ + if (moto_mode > AW8622X_SEQ_NO_RTP_BASE) { + aw8622x->rtp_file_num = moto_mode - AW8622X_SEQ_NO_RTP_BASE; + aw8622x->activate_mode = AW8622X_HAPTIC_RTP_MODE; + /* rtp mode and no stop */ + if (moto_mode > (AW8622X_SEQ_NO_RTP_BASE + AW8622X_SEQ_NO_RTP_REPEAT)) { + aw8622x->rtp_file_num -= AW8622X_SEQ_NO_RTP_REPEAT; + aw8622x->activate_mode = AW8622X_HAPTIC_LOOP_RTP_MODE; + } + } else if ((aw8622x->duration < aw8622x->dts_info.duration_time[2]) || moto_mode > 2) { + /* mode -> index:3 -> 1 : simulate Home key or other hardware key down*/ + /* mode -> index:4 -> 2 : simulate Home key or other hardware key up */ + /* mode -> index:5 -> 3 : AOSP VIRTUAL_KEY */ + /* mode -> index:6 -> 4 : AOSP CLOCK_TICK/GESTURE_END/GESTURE_START */ + if (moto_mode > 2) { /* moto custom */ + if (moto_mode == 3) { + aw8622x->index = 1; + } else if (moto_mode == 4) { + aw8622x->index = 3; + } else if (moto_mode == 5) { + aw8622x->index = 1; + } else if (moto_mode == 6) { + aw8622x->index = 2; + } else { + aw8622x->index = 1; + } + } else { /* duration ctl */ + if ((aw8622x->duration > 0) && (aw8622x->duration < + aw8622x->dts_info.duration_time[0])) { + aw8622x->index = 3; /* 1 level shrot shake */ + } else if ((aw8622x->duration >= aw8622x->dts_info.duration_time[0]) && + (aw8622x->duration < aw8622x->dts_info.duration_time[1])) { + aw8622x->index = 2; /*2 level shrot shake*/ + } else if ((aw8622x->duration >= aw8622x->dts_info.duration_time[1]) && + (aw8622x->duration < aw8622x->dts_info.duration_time[2])) { + aw8622x->index = 1; /*3 level shrot shake*/ + } + } + if (aw8622x->index > AW8622x_MAX_RAM_MODE_NUM) { + aw_dev_err("%s index %d out of range use default 1\n", __func__, aw8622x->index); + aw8622x->index = 1; + } + + aw8622x->activate_mode = AW8622X_HAPTIC_RAM_MODE; + } else { + aw8622x->index = 4; /* long */ + aw8622x->activate_mode = AW8622X_HAPTIC_RAM_LOOP_MODE; + } + aw_dev_info("%s index = %d \n", __func__, aw8622x->index); + /* moto mode select end */ + + aw_dev_info("%s activate_mode = %d \n", __func__, aw8622x->activate_mode); + if (aw8622x->state) { + if (aw8622x->activate_mode == AW8622X_HAPTIC_RAM_MODE) { + aw8622x_haptic_ram_vbat_comp(aw8622x, false); + aw8622x_haptic_ram_play(aw8622x, + AW8622X_HAPTIC_RAM_MODE); + } else if (aw8622x->activate_mode == + AW8622X_HAPTIC_RAM_LOOP_MODE) { + aw8622x_haptic_ram_vbat_comp(aw8622x, true); + aw8622x_haptic_auto_brk_enable(aw8622x, true); + aw8622x_haptic_ram_play(aw8622x, + AW8622X_HAPTIC_RAM_LOOP_MODE); + /* run ms timer */ + hrtimer_start(&aw8622x->timer, + ktime_set(aw8622x->duration / 1000, + (aw8622x->duration % 1000) * 1000000), + HRTIMER_MODE_REL); + } else if (aw8622x->activate_mode == + AW8622X_HAPTIC_CONT_MODE) { + aw8622x_haptic_cont(aw8622x); + /* run ms timer */ + hrtimer_start(&aw8622x->timer, + ktime_set(aw8622x->duration / 1000, + (aw8622x->duration % 1000) * 1000000), + HRTIMER_MODE_REL); + } else if (aw8622x->activate_mode == + AW8622X_HAPTIC_RTP_MODE || + aw8622x->activate_mode == + AW8622X_HAPTIC_LOOP_RTP_MODE) { + if (aw8622x->activate_mode == + AW8622X_HAPTIC_LOOP_RTP_MODE) { + /* enable timer to auto stop */ + hrtimer_start(&aw8622x->timer, + ktime_set(AW8622X_SEQ_NO_RTP_STOP / 1000, + (AW8622X_SEQ_NO_RTP_STOP % 1000) * 1000000), + HRTIMER_MODE_REL); + } + /* call moto rtp mode */ + aw8622x->moto_rtp_runing = true; + aw8622x_haptic_set_rtp_aei(aw8622x, false); + aw8622x_interrupt_clear(aw8622x); + if (aw8622x->rtp_file_num < (sizeof(aw8622x_moto_rtp_name) / AW8622X_RTP_NAME_MAX)) { + schedule_work(&aw8622x->rtp_work); + } else { + aw_dev_err("rtp file num error, %d\n", aw8622x->rtp_file_num); + } + } else { + aw_dev_err("%s: activate_mode error\n", + __func__); + } + + if (aw8622x->moto_mode_ctl[0] > 2) + aw8622x->moto_mode_ctl[0] = 0; + } + } + mutex_unlock(&aw8622x->lock); +} + +int aw8622x_vibrator_init(struct aw8622x *aw8622x) +{ + int ret = 0; + + aw_dev_info("%s enter\n", __func__); + + aw_dev_info("%s Custom Driver version %s\n", __func__, AW8622X_Driver); +#ifdef TIMED_OUTPUT + aw_dev_info("%s: TIMED_OUT FRAMEWORK!\n", __func__); + aw8622x->vib_dev.name = "awinic_vibrator"; + aw8622x->vib_dev.get_time = aw8622x_vibrator_get_time; + aw8622x->vib_dev.enable = aw8622x_vibrator_enable; + + ret = timed_output_dev_register(&(aw8622x->vib_dev)); + if (ret < 0) { + aw_dev_err("%s: fail to create timed output dev\n", __func__); + return ret; + } + ret = sysfs_create_group(&aw8622x->vib_dev.dev->kobj, + &aw8622x_vibrator_attribute_group); + if (ret < 0) { + aw_dev_err("%s error creating sysfs attr files\n", __func__); + return ret; + } +#else + aw_dev_info("%s: loaded in leds_cdev framework!\n", + __func__); + aw8622x->vib_dev.name = "vibrator"; + aw8622x->vib_dev.brightness_get = aw8622x_haptic_brightness_get; + aw8622x->vib_dev.brightness_set = aw8622x_haptic_brightness_set; + + ret = devm_led_classdev_register(&aw8622x->i2c->dev, &aw8622x->vib_dev); + if (ret < 0) { + aw_dev_err("%s: fail to create led dev\n", __func__); + return ret; + } + ret = sysfs_create_group(&aw8622x->vib_dev.dev->kobj, + &aw8622x_vibrator_attribute_group); + if (ret < 0) { + aw_dev_err("%s error creating sysfs attr files\n", + __func__); + return ret; + } +#endif + hrtimer_init(&aw8622x->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + aw8622x->timer.function = aw8622x_vibrator_timer_func; + INIT_WORK(&aw8622x->vibrator_work, + aw8622x_vibrator_work_routine); + INIT_WORK(&aw8622x->rtp_work, aw8622x_rtp_work_routine); + mutex_init(&aw8622x->lock); + mutex_init(&aw8622x->rtp_lock); + mutex_init(&aw8622x->ram_lock); + + return 0; +} + +static int aw8622x_haptic_set_pwm(struct aw8622x *aw8622x, unsigned char mode) +{ + switch (mode) { + case AW8622X_PWM_48K: + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL2, + AW8622X_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW8622X_BIT_SYSCTRL2_RATE_48K); + break; + case AW8622X_PWM_24K: + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL2, + AW8622X_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW8622X_BIT_SYSCTRL2_RATE_24K); + break; + case AW8622X_PWM_12K: + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL2, + AW8622X_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW8622X_BIT_SYSCTRL2_RATE_12K); + break; + default: + break; + } + return 0; +} + +static void aw8622x_haptic_misc_para_init(struct aw8622x *aw8622x) +{ + unsigned char reg_array[4] = {0}; + unsigned char reg_val = 0; + + aw_dev_info("%s enter\n", __func__); + aw8622x->cont_drv1_lvl = aw8622x->dts_info.cont_drv1_lvl_dt; + aw8622x->cont_drv2_lvl = aw8622x->dts_info.cont_drv2_lvl_dt; + aw8622x->cont_drv1_time = aw8622x->dts_info.cont_drv1_time_dt; + aw8622x->cont_drv2_time = aw8622x->dts_info.cont_drv2_time_dt; + aw8622x->cont_brk_time = aw8622x->dts_info.cont_brk_time_dt; + aw8622x->cont_wait_num = aw8622x->dts_info.cont_wait_num_dt; + aw8622x->cont_drv_width = aw8622x->dts_info.cont_drv_width; + + /* SIN_H */ + reg_array[0] = (unsigned char)aw8622x->dts_info.sine_array[0]; + /* SIN_L */ + reg_array[1] = (unsigned char)aw8622x->dts_info.sine_array[1]; + /* COS_H */ + reg_array[2] = (unsigned char)aw8622x->dts_info.sine_array[2]; + /* COS_L */ + reg_array[3] = (unsigned char)aw8622x->dts_info.sine_array[3]; + + aw8622x_i2c_write(aw8622x, AW8622X_REG_SYSCTRL3, reg_array, + AW_I2C_BYTE_FOUR); + + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_TRGCFG8, + AW8622X_BIT_TRGCFG8_TRG_TRIG1_MODE_MASK, + AW8622X_BIT_TRGCFG8_TRIG1); + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_ANACFG8, + AW8622X_BIT_ANACFG8_TRTF_CTRL_HDRV_MASK, + AW8622X_BIT_ANACFG8_TRTF_CTRL_HDRV); + + /* d2s_gain */ + if (!aw8622x->dts_info.d2s_gain) { + aw_dev_err("%s aw8622x->dts_info.d2s_gain = 0!\n", + __func__); + } else { + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL7, + AW8622X_BIT_SYSCTRL7_D2S_GAIN_MASK, + aw8622x->dts_info.d2s_gain); + } + + if (aw8622x->dts_info.cont_tset && aw8622x->dts_info.cont_bemf_set) { + reg_array[0] = (unsigned char)aw8622x->dts_info.cont_tset; + reg_array[1] = (unsigned char)aw8622x->dts_info.cont_bemf_set; + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_CONTCFG13, + (AW8622X_BIT_CONTCFG13_TSET_MASK & + AW8622X_BIT_CONTCFG13_BEME_SET_MASK), + ((reg_array[0] << 4)| + reg_array[1])); + } + + /* cont_brk_time */ + if (!aw8622x->cont_brk_time) { + aw_dev_err("%s aw8622x->cont_brk_time = 0!\n", + __func__); + } else { + reg_val = (unsigned char)aw8622x->cont_brk_time; + aw8622x_i2c_write(aw8622x, AW8622X_REG_CONTCFG10, ®_val, + AW_I2C_BYTE_ONE); + } + + /* cont_brk_width */ + if (!aw8622x->cont_drv_width) { + aw_dev_err("%s aw8622x->cont_drv_width = 0!\n", + __func__); + } else { + reg_val = (unsigned char)aw8622x->cont_drv_width; + aw8622x_i2c_write(aw8622x, AW8622X_REG_CONTCFG3, ®_val, + AW_I2C_BYTE_ONE); + } + + /* cont_bst_brk_gain */ + /* + ** if (!aw8622x->dts_info.cont_bst_brk_gain) { + ** aw_dev_err("%s aw8622x->dts_info.cont_bst_brk_gain = 0!\n", + ** __func__); + ** } else { + ** aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_CONTCFG5, + ** AW8622X_BIT_CONTCFG5_BST_BRK_GAIN_MASK, + ** aw8622x->dts_info.cont_bst_brk_gain); + ** } + */ + + /* cont_brk_gain */ + if (!aw8622x->dts_info.cont_brk_gain) { + aw_dev_err("%s aw8622x->dts_info.cont_brk_gain = 0!\n", + __func__); + } else { + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_CONTCFG5, + AW8622X_BIT_CONTCFG5_BRK_GAIN_MASK, + aw8622x->dts_info.cont_brk_gain); + } +} + +/***************************************************** + * + * offset calibration + * + *****************************************************/ +static int aw8622x_haptic_offset_calibration(struct aw8622x *aw8622x) +{ + unsigned int cont = 2000; + unsigned char reg_val = 0; + + aw_dev_info("%s enter\n", __func__); + + aw8622x_haptic_raminit(aw8622x, true); + + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_DETCFG2, + AW8622X_BIT_DETCFG2_DIAG_GO_MASK, + AW8622X_BIT_DETCFG2_DIAG_GO_ON); + while (1) { + aw8622x_i2c_read(aw8622x, AW8622X_REG_DETCFG2, ®_val, + AW_I2C_BYTE_ONE); + if ((reg_val & 0x01) == 0 || cont == 0) + break; + cont--; + } + if (cont == 0) + aw_dev_err("%s calibration offset failed!\n", + __func__); + aw8622x_haptic_raminit(aw8622x, false); + return 0; +} + +/***************************************************** + * + * vbat mode + * + *****************************************************/ +static int aw8622x_haptic_vbat_mode_config(struct aw8622x *aw8622x, + unsigned char flag) +{ + if (flag == AW8622X_HAPTIC_CONT_VBAT_HW_ADJUST_MODE) { + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL1, + AW8622X_BIT_SYSCTRL1_VBAT_MODE_MASK, + AW8622X_BIT_SYSCTRL1_VBAT_MODE_HW); + } else { + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL1, + AW8622X_BIT_SYSCTRL1_VBAT_MODE_MASK, + AW8622X_BIT_SYSCTRL1_VBAT_MODE_SW); + } + return 0; +} + +static void aw8622x_ram_work_routine(struct work_struct *work) +{ + struct aw8622x *aw8622x = container_of(work, struct aw8622x, + ram_work.work); + + aw_dev_info("%s enter\n", __func__); + aw8622x_ram_update(aw8622x); +} + +int aw8622x_ram_work_init(struct aw8622x *aw8622x) +{ + int ram_timer_val = 8000; + + aw_dev_info("%s enter\n", __func__); + INIT_DELAYED_WORK(&aw8622x->ram_work, aw8622x_ram_work_routine); + schedule_delayed_work(&aw8622x->ram_work, + msecs_to_jiffies(ram_timer_val)); + return 0; +} +static enum hrtimer_restart +aw8622x_haptic_audio_timer_func(struct hrtimer *timer) +{ + struct aw8622x *aw8622x = container_of(timer, + struct aw8622x, haptic_audio.timer); + + aw_dev_dbg("%s enter\n", __func__); + schedule_work(&aw8622x->haptic_audio.work); + + hrtimer_start(&aw8622x->haptic_audio.timer, + ktime_set(aw8622x->haptic_audio.timer_val/1000000, + (aw8622x->haptic_audio.timer_val%1000000)*1000), + HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +static void +aw8622x_haptic_auto_brk_enable(struct aw8622x *aw8622x, unsigned char flag) +{ + if (flag) { + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_PLAYCFG3, + AW8622X_BIT_PLAYCFG3_BRK_EN_MASK, + AW8622X_BIT_PLAYCFG3_BRK_ENABLE); + } else { + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_PLAYCFG3, + AW8622X_BIT_PLAYCFG3_BRK_EN_MASK, + AW8622X_BIT_PLAYCFG3_BRK_DISABLE); + } +} +int aw8622x_haptic_init(struct aw8622x *aw8622x) +{ + unsigned char reg_val = 0; + unsigned char reg_array[8] = {0}; + int ret = 0; + + aw_dev_info("%s enter\n", __func__); + /* haptic audio */ + aw8622x->haptic_audio.delay_val = 1; + aw8622x->haptic_audio.timer_val = 21318; + INIT_LIST_HEAD(&(aw8622x->haptic_audio.ctr_list)); + hrtimer_init(&aw8622x->haptic_audio.timer, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + aw8622x->haptic_audio.timer.function = aw8622x_haptic_audio_timer_func; + INIT_WORK(&aw8622x->haptic_audio.work, + aw8622x_haptic_audio_work_routine); + mutex_init(&aw8622x->haptic_audio.lock); + aw8622x->gun_type = AW_GUN_TYPE_DEF_VAL; + aw8622x->bullet_nr = AW_BULLET_NR_DEF_VAL; + + mutex_lock(&aw8622x->lock); + /* haptic init */ + aw8622x->ram_state = 0; + aw8622x->activate_mode = aw8622x->dts_info.mode; + ret = aw8622x_i2c_read(aw8622x, AW8622X_REG_WAVCFG1, ®_val, + AW_I2C_BYTE_ONE); + aw8622x->index = reg_val & AW8622X_BIT_WAVCFG_SEQ; + ret = aw8622x_i2c_read(aw8622x, AW8622X_REG_PLAYCFG2, ®_val, + AW_I2C_BYTE_ONE); + aw8622x->gain = reg_val; + aw_dev_info("%s aw8622x->gain =0x%02X\n", __func__, aw8622x->gain); + aw8622x_i2c_read(aw8622x, AW8622X_REG_WAVCFG1, reg_array, + AW8622X_SEQUENCER_SIZE); + memcpy(aw8622x->seq, reg_array, AW8622X_SEQUENCER_SIZE); + + aw8622x_haptic_play_mode(aw8622x, AW8622X_HAPTIC_STANDBY_MODE); + aw8622x_haptic_set_pwm(aw8622x, AW8622X_PWM_12K); + /* misc value init */ + aw8622x_haptic_misc_para_init(aw8622x); + /* set motor protect */ + aw8622x_haptic_swicth_motor_protect_config(aw8622x, 0x00, 0x00); + aw8622x_haptic_trig_param_init(aw8622x); + aw8622x_haptic_trig_param_config(aw8622x); + aw8622x_haptic_offset_calibration(aw8622x); + /*config auto_brake*/ + aw8622x_haptic_auto_brk_enable(aw8622x, + aw8622x->dts_info.is_enabled_auto_brk); + /* vbat compensation */ + aw8622x_haptic_vbat_mode_config(aw8622x, + AW8622X_HAPTIC_CONT_VBAT_HW_ADJUST_MODE); + aw8622x->ram_vbat_compensate = AW8622X_HAPTIC_RAM_VBAT_COMP_ENABLE; + + /* f0 calibration */ + /*LRA trim source select register*/ + aw8622x_i2c_write_bits(aw8622x, + AW8622X_REG_TRIMCFG1, + AW8622X_BIT_TRIMCFG1_RL_TRIM_SRC_MASK, + AW8622X_BIT_TRIMCFG1_RL_TRIM_SRC_REG); + if (aw8622x->dts_info.lk_f0_cali) { + aw8622x->f0_cali_data = aw8622x->dts_info.lk_f0_cali; + aw8622x_haptic_upload_lra(aw8622x, AW8622X_F0_CALI); + } else { + aw8622x_haptic_upload_lra(aw8622x, AW8622X_WRITE_ZERO); + aw8622x_haptic_f0_calibration(aw8622x); + } + mutex_unlock(&aw8622x->lock); + return ret; +} + +void aw8622x_interrupt_setup(struct aw8622x *aw8622x) +{ + unsigned char reg_val = 0; + + aw_dev_info("%s enter\n", __func__); + + aw8622x_i2c_read(aw8622x, AW8622X_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + + aw_dev_info("%s: reg SYSINT=0x%02X\n", __func__, reg_val); + + /* edge int mode */ + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSCTRL7, + (AW8622X_BIT_SYSCTRL7_INT_MODE_MASK & + AW8622X_BIT_SYSCTRL7_INT_EDGE_MODE_MASK), + (AW8622X_BIT_SYSCTRL7_INT_MODE_EDGE | + AW8622X_BIT_SYSCTRL7_INT_EDGE_MODE_POS)); + /* int enable */ + /* + *aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSINTM, + * AW8622X_BIT_SYSINTM_BST_SCPM_MASK, + * AW8622X_BIT_SYSINTM_BST_SCPM_OFF); + *aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSINTM, + * AW8622X_BIT_SYSINTM_BST_OVPM_MASK, + * AW8622X_BIT_SYSINTM_BST_OVPM_ON); + */ + aw8622x_i2c_write_bits(aw8622x, AW8622X_REG_SYSINTM, + (AW8622X_BIT_SYSINTM_UVLM_MASK & + AW8622X_BIT_SYSINTM_OCDM_MASK & + AW8622X_BIT_SYSINTM_OTM_MASK), + (AW8622X_BIT_SYSINTM_UVLM_ON | + AW8622X_BIT_SYSINTM_OCDM_ON | + AW8622X_BIT_SYSINTM_OTM_ON)); +} +static int aw8622x_is_rtp_load_end(struct aw8622x *aw8622x) +{ + unsigned char glb_st = 0; + int ret = -1; + + aw8622x_i2c_read(aw8622x, AW8622X_REG_GLBRD5, + &glb_st, AW_I2C_BYTE_ONE); + if ((aw8622x->rtp_cnt == aw8622x->rtp_container->len) + || ((glb_st & 0x0f) == 0)) { + if (aw8622x->rtp_cnt == + aw8622x->rtp_container->len) { + aw_dev_info("%s: rtp load completely, mode = %d!\n", + __func__, aw8622x->activate_mode); + /* custom for moto repeat play */ + if (aw8622x->activate_mode == AW8622X_HAPTIC_RTP_MODE) { + schedule_work(&aw8622x->rtp_work); + } + } + else + aw_dev_err("%s rtp load failed!!\n", + __func__); + aw8622x_haptic_set_rtp_aei(aw8622x, + false); + aw8622x->rtp_cnt = 0; + aw8622x->rtp_init = 0; + ret = 0; + } + return ret; + +} + +static int aw8622x_write_rtp_data(struct aw8622x *aw8622x) +{ + unsigned int buf_len = 0; + int ret = -1; + + if (!aw8622x->rtp_cnt) { + aw_dev_info("%s:aw8622x->rtp_cnt is 0!\n", + __func__); + return ret; + } + if (!aw8622x->rtp_container) { + aw_dev_info("%s:aw8622x->rtp_container is null, break!\n", + __func__); + return ret; + } + if ((aw8622x->rtp_container->len - aw8622x->rtp_cnt) < + (aw8622x->ram.base_addr >> 1)) { + buf_len = + aw8622x->rtp_container->len - aw8622x->rtp_cnt; + } else { + buf_len = (aw8622x->ram.base_addr >> 2); + } + aw8622x_i2c_write(aw8622x, AW8622X_REG_RTPDATA, + &aw8622x->rtp_container->data[aw8622x->rtp_cnt], + buf_len); + aw8622x->rtp_cnt += buf_len; + return 0; +} + +irqreturn_t aw8622x_irq(int irq, void *data) +{ + struct aw8622x *aw8622x = data; + unsigned char reg_val = 0; + int ret = 0; + + aw_dev_dbg("%s enter\n", __func__); + aw8622x_i2c_read(aw8622x, AW8622X_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_dev_dbg("%s: reg SYSINT=0x%02X\n", __func__, reg_val); + if (reg_val & AW8622X_BIT_SYSINT_UVLI) + aw_dev_err("%s chip uvlo int error\n", __func__); + if (reg_val & AW8622X_BIT_SYSINT_OCDI) + aw_dev_err("%s chip over current int error\n", __func__); + if (reg_val & AW8622X_BIT_SYSINT_OTI) + aw_dev_err("%s chip over temperature int error\n", __func__); + if (reg_val & AW8622X_BIT_SYSINT_DONEI) + aw_dev_info("%s chip playback done\n", __func__); + + if (reg_val & AW8622X_BIT_SYSINT_FF_AEI) { + aw_dev_dbg("%s: aw8622x rtp fifo almost empty\n", __func__); + if (aw8622x->rtp_init) { + while ((!aw8622x_haptic_rtp_get_fifo_afs(aw8622x)) && + (aw8622x->play_mode == + AW8622X_HAPTIC_RTP_MODE)) { + mutex_lock(&aw8622x->rtp_lock); +#ifdef AW_ENABLE_RTP_PRINT_LOG + aw_dev_info( "%s: aw8622x rtp mode fifo update, cnt=%d\n", + __func__, aw8622x->rtp_cnt); +#endif + ret = aw8622x_write_rtp_data(aw8622x); + if (ret < 0) { + mutex_unlock(&aw8622x->rtp_lock); + break; + } + ret = aw8622x_is_rtp_load_end(aw8622x); + if (!ret) { + mutex_unlock(&aw8622x->rtp_lock); + break; + } + mutex_unlock(&aw8622x->rtp_lock); + } + } else { + aw_dev_info("%s: aw8622x rtp init = %d, init error\n", + __func__, aw8622x->rtp_init); + } + } + + if (reg_val & AW8622X_BIT_SYSINT_FF_AFI) + aw_dev_dbg("%s: aw8622x rtp mode fifo almost full!\n", + __func__); + + if (aw8622x->play_mode != AW8622X_HAPTIC_RTP_MODE) + aw8622x_haptic_set_rtp_aei(aw8622x, false); + + aw_dev_dbg("%s exit\n", __func__); + + return IRQ_HANDLED; +} + +char aw8622x_check_qualify(struct aw8622x *aw8622x) +{ + unsigned char reg = 0; + int ret = 0; + + ret = aw8622x_i2c_read(aw8622x, AW8622X_REG_EFRD9, ®, + AW_I2C_BYTE_ONE); + if (ret < 0) { + aw_dev_err("%s: failed to read register 0x64: %d\n", + __func__, ret); + return ret; + } + if ((reg & 0x80) == 0x80) + return 1; + aw_dev_err("%s: register 0x64 error: 0x%02x\n", + __func__, reg); + return 0; +} diff --git a/drivers/misc/awinic/aw862x/aw8622x.h b/drivers/misc/awinic/aw862x/aw8622x.h new file mode 100644 index 000000000000..3f365e0c2ec0 --- /dev/null +++ b/drivers/misc/awinic/aw862x/aw8622x.h @@ -0,0 +1,788 @@ +#ifndef _AW8622X_H_ +#define _AW8622X_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "haptic_nv.h" + +/********************************************************* + * + * marco + * + ********************************************************/ +#define AW8622X_I2C_RETRIES (5) +#define AW8622X_RTP_NAME_MAX (128) +#define AW8622X_SEQUENCER_SIZE (8) +#define AW8622X_SEQUENCER_LOOP_SIZE (4) +#define AW8622X_OSC_CALI_MAX_LENGTH (11000000) +#define AW8622X_PM_QOS_VALUE_VB (400) +#define AW8622X_VBAT_REFER (4200) +#define AW8622X_VBAT_MIN (3000) +#define AW8622X_VBAT_MAX (5500) +#define AW8622X_TRIG_NUM (3) +#define AW8622X_I2C_RETRY_DELAY (2) +#define AW8622X_GAIN_MAX (0x80) +#define AW8622X_VBAT_FORMULA(code) (6100 * (code) / 1024) +#define AW8622X_LRA_F0_FORMULA(f0_reg) (384000 * 10 / (f0_reg)) +#define AW8622X_CONT_F0_FORMULA(f0_reg) (384000 * 10 / (f0_reg)) +#define AW8622X_LRA_FORMULA(lra_code) (((lra_code) * 678 * 100) / (1024 * 10)) +#define AW8622X_SET_AEADDR_H(addr) ((((addr) >> 1) >> 4) & 0xF0) +#define AW8622X_SET_AEADDR_L(addr) (((addr) >> 1) & 0x00FF) +#define AW8622X_SET_AFADDR_H(addr) ((((addr) - ((addr) >> 2)) >> 8) & 0x0F) +#define AW8622X_SET_AFADDR_L(addr) (((addr) - ((addr) >> 2)) & 0x00FF) +#define AW8622X_STOP_DELAY_MIN (2000) +#define AW8622X_STOP_DELAY_MAX (2500) +#define AW8622X_VBAT_DELAY_MIN (20000) +#define AW8622X_VBAT_DELAY_MAX (25000) +#define AW8622X_F0_DELAY_MIN (10000) +#define AW8622X_F0_DELAY_MAX (10500) +#define AW8622X_LRA_DELAY_MIN (30000) +#define AW8622X_LRA_DELAY_MAX (35000) +/********************************************************* + * + * enum + * + ********************************************************/ +enum aw8622x_flags { + AW8622X_FLAG_NONR = 0, + AW8622X_FLAG_SKIP_INTERRUPTS = 1, +}; + +enum aw8622x_haptic_work_mode { + AW8622X_HAPTIC_STANDBY_MODE = 0, + AW8622X_HAPTIC_RAM_MODE = 1, + AW8622X_HAPTIC_RAM_LOOP_MODE = 2, + AW8622X_HAPTIC_CONT_MODE = 3, + AW8622X_HAPTIC_RTP_MODE = 4, + AW8622X_HAPTIC_TRIG_MODE = 5, + AW8622X_HAPTIC_LOOP_RTP_MODE = 6, + AW8622X_HAPTIC_NULL = 7, +}; + +enum aw8622x_haptic_cont_vbat_comp_mode { + AW8622X_HAPTIC_CONT_VBAT_SW_ADJUST_MODE = 0, + AW8622X_HAPTIC_CONT_VBAT_HW_ADJUST_MODE = 1, +}; + +enum aw8622x_haptic_ram_vbat_compensate_mode { + AW8622X_HAPTIC_RAM_VBAT_COMP_DISABLE = 0, + AW8622X_HAPTIC_RAM_VBAT_COMP_ENABLE = 1, +}; + +enum aw8622x_haptic_f0_flag { + AW8622X_HAPTIC_LRA_F0 = 0, + AW8622X_HAPTIC_CALI_F0 = 1, +}; + +enum aw8622x_sram_size_flag { + AW8622X_HAPTIC_SRAM_1K = 0, + AW8622X_HAPTIC_SRAM_2K = 1, + AW8622X_HAPTIC_SRAM_3K = 2, +}; + +enum aw8622x_haptic_pwm_mode { + AW8622X_PWM_48K = 0, + AW8622X_PWM_24K = 1, + AW8622X_PWM_12K = 2, +}; + +enum aw8622x_haptic_play { + AW8622X_HAPTIC_PLAY_NULL = 0, + AW8622X_HAPTIC_PLAY_ENABLE = 1, + AW8622X_HAPTIC_PLAY_STOP = 2, + AW8622X_HAPTIC_PLAY_GAIN = 8, +}; + +enum aw8622x_haptic_cmd { + AW8622X_HAPTIC_CMD_NULL = 0, + AW8622X_HAPTIC_CMD_ENABLE = 1, + AW8622X_HAPTIC_CMD_HAPTIC = 0x0f, + AW8622X_HAPTIC_CMD_TP = 0x10, + AW8622X_HAPTIC_CMD_SYS = 0xf0, + AW8622X_HAPTIC_CMD_STOP = 255, +}; + +enum aw8622x_haptic_cali_lra { + AW8622X_WRITE_ZERO = 0, + AW8622X_F0_CALI = 1, + AW8622X_OSC_CALI = 2, +}; + +enum aw8622x_haptic_rtp_mode { + AW8622X_RTP_SHORT = 4, + AW8622X_RTP_LONG = 5, + AW8622X_RTP_SEGMENT = 6, +}; + +enum aw8622X_awrw_flag { + AW8622X_WRITE = 0, + AW8622X_READ = 1, +}; + +#define AW8622X_WAV_SEQ_SIZE (4) +#define AW8622X_MAX_FIRMWARE_LOAD_CNT (20) +#define AW8622X_SEQ_NO_RTP_BASE (102) +#define AW8622X_SEQ_NO_RTP_REPEAT (100) +#define AW8622X_SEQ_NO_RTP_STOP (120000) +#define AW8622x_MAX_RAM_MODE_NUM (4) + +#define AW8622X_REPEAT_RTP_PLAYING + +/********************************************************* + * + * struct + * + ********************************************************/ +struct aw862xx_trig { + unsigned char trig_level; + unsigned char trig_polar; + unsigned char pos_enable; + unsigned char pos_sequence; + unsigned char neg_enable; + unsigned char neg_sequence; + unsigned char trig_brk; +}; + +struct aw8622x_dts_info { + unsigned int lk_f0_cali; + unsigned int mode; + unsigned int f0_ref; + unsigned int f0_cali_percent; + unsigned int cont_drv1_lvl_dt; + unsigned int cont_drv2_lvl_dt; + unsigned int cont_drv1_time_dt; + unsigned int cont_drv2_time_dt; + unsigned int cont_wait_num_dt; + unsigned int cont_brk_time_dt; + unsigned int cont_track_margin; + unsigned int cont_tset; + unsigned int cont_drv_width; + unsigned int cont_bemf_set; + unsigned int cont_brk_gain; + unsigned int d2s_gain; + unsigned int prctmode[3]; + unsigned int sine_array[4]; + unsigned int trig_config[24]; + unsigned int duration_time[3]; + bool is_enabled_powerup_f0_cali; + bool is_enabled_auto_brk; +}; + +struct aw8622x { + unsigned char rtp_init; + unsigned char ram_init; + unsigned char rtp_routine_on; + unsigned char max_pos_beme; + unsigned char max_neg_beme; + unsigned char f0_cali_flag; + unsigned char ram_vbat_compensate; + unsigned char hwen_flag; + unsigned char flags; + unsigned char chipid; + unsigned char play_mode; + unsigned char activate_mode; + unsigned char ram_state; + unsigned char duration_time_size; + unsigned char moto_mode_ctl[4]; + unsigned moto_rtp_runing; + unsigned char seq[AW8622X_SEQUENCER_SIZE]; + unsigned char loop[AW8622X_SEQUENCER_SIZE]; + + char duration_time_flag; + + bool isUsedIntn; + bool haptic_ready; + + int name; + int reset_gpio; + int irq_gpio; + int state; + int duration; + int amplitude; + int index; + int rtp_index; + int vmax; + int gain; + int sysclk; + int rate; + int width; + int pstream; + int cstream; + + unsigned int gun_type; + unsigned int bullet_nr; + unsigned int rtp_cnt; + unsigned int rtp_file_num; + unsigned int f0; + unsigned int cont_f0; + unsigned int cont_drv1_lvl; + unsigned int cont_drv2_lvl; + unsigned int cont_brk_time; + unsigned int cont_wait_num; + unsigned int cont_drv1_time; + unsigned int cont_drv2_time; + unsigned int cont_drv_width; + unsigned int theory_time; + unsigned int vbat; + unsigned int lra; + unsigned int ram_update_flag; + unsigned int rtp_update_flag; + unsigned int osc_cali_data; + unsigned int f0_cali_data; + unsigned int timeval_flags; + unsigned int osc_cali_flag; + unsigned int sys_frequency; + unsigned int rtp_len; + unsigned long int microsecond; + + struct haptic_audio haptic_audio; + struct regmap *regmap; + struct i2c_client *i2c; + struct device *dev; + struct input_dev *input; + struct mutex lock; + struct mutex rtp_lock; + struct mutex ram_lock; + struct hrtimer timer; + struct work_struct vibrator_work; + struct work_struct rtp_work; + struct delayed_work ram_work; + struct aw862xx_trig trig[AW8622X_TRIG_NUM]; + struct aw8622x_dts_info dts_info; + struct aw_i2c_package aw_i2c_package; + struct ram ram; + struct aw8622x_container *rtp_container; + ktime_t kstart, kend; + + cdev_t vib_dev; +}; + +struct aw8622x_container { + int len; + unsigned char data[]; +}; +/********************************************************* + * + * extern + * + ********************************************************/ +extern char aw8622x_check_qualify(struct aw8622x *aw8622x); +extern int aw8622x_parse_dt(struct aw8622x *aw8622x, struct device *dev, + struct device_node *np); +extern void aw8622x_interrupt_setup(struct aw8622x *aw8622x); +extern int aw8622x_vibrator_init(struct aw8622x *aw8622x); +extern int aw8622x_haptic_init(struct aw8622x *aw8622x); +extern int aw8622x_ram_work_init(struct aw8622x *aw8622x); +extern irqreturn_t aw8622x_irq(int irq, void *data); +extern struct attribute_group aw8622x_vibrator_attribute_group; + +/******************************************** + * Register List + *******************************************/ +#define AW8622X_REG_ID (0x00) +#define AW8622X_REG_SYSST (0x01) +#define AW8622X_REG_SYSINT (0x02) +#define AW8622X_REG_SYSINTM (0x03) +#define AW8622X_REG_SYSST2 (0x04) +#define AW8622X_REG_SYSER (0x05) +#define AW8622X_REG_PLAYCFG2 (0x07) +#define AW8622X_REG_PLAYCFG3 (0x08) +#define AW8622X_REG_PLAYCFG4 (0x09) +#define AW8622X_REG_WAVCFG1 (0x0A) +#define AW8622X_REG_WAVCFG2 (0x0B) +#define AW8622X_REG_WAVCFG3 (0x0C) +#define AW8622X_REG_WAVCFG4 (0x0D) +#define AW8622X_REG_WAVCFG5 (0x0E) +#define AW8622X_REG_WAVCFG6 (0x0F) +#define AW8622X_REG_WAVCFG7 (0x10) +#define AW8622X_REG_WAVCFG8 (0x11) +#define AW8622X_REG_WAVCFG9 (0x12) +#define AW8622X_REG_WAVCFG10 (0x13) +#define AW8622X_REG_WAVCFG11 (0x14) +#define AW8622X_REG_WAVCFG12 (0x15) +#define AW8622X_REG_WAVCFG13 (0x16) +#define AW8622X_REG_CONTCFG1 (0x18) +#define AW8622X_REG_CONTCFG2 (0x19) +#define AW8622X_REG_CONTCFG3 (0x1A) +#define AW8622X_REG_CONTCFG4 (0x1B) +#define AW8622X_REG_CONTCFG5 (0x1C) +#define AW8622X_REG_CONTCFG6 (0x1D) +#define AW8622X_REG_CONTCFG7 (0x1E) +#define AW8622X_REG_CONTCFG8 (0x1F) +#define AW8622X_REG_CONTCFG9 (0x20) +#define AW8622X_REG_CONTCFG10 (0x21) +#define AW8622X_REG_CONTCFG11 (0x22) +#define AW8622X_REG_CONTCFG12 (0x23) +#define AW8622X_REG_CONTCFG13 (0x24) +#define AW8622X_REG_CONTRD14 (0x25) +#define AW8622X_REG_CONTRD15 (0x26) +#define AW8622X_REG_CONTRD16 (0x27) +#define AW8622X_REG_CONTRD17 (0x28) +#define AW8622X_REG_CONTRD18 (0x29) +#define AW8622X_REG_CONTRD19 (0x2A) +#define AW8622X_REG_CONTRD20 (0x2B) +#define AW8622X_REG_CONTRD21 (0x2C) +#define AW8622X_REG_RTPCFG1 (0x2D) +#define AW8622X_REG_RTPCFG2 (0x2E) +#define AW8622X_REG_RTPCFG3 (0x2F) +#define AW8622X_REG_RTPCFG4 (0x30) +#define AW8622X_REG_RTPCFG5 (0x31) +#define AW8622X_REG_RTPDATA (0x32) +#define AW8622X_REG_TRGCFG1 (0x33) +#define AW8622X_REG_TRGCFG2 (0x34) +#define AW8622X_REG_TRGCFG3 (0x35) +#define AW8622X_REG_TRGCFG4 (0x36) +#define AW8622X_REG_TRGCFG5 (0x37) +#define AW8622X_REG_TRGCFG6 (0x38) +#define AW8622X_REG_TRGCFG7 (0x39) +#define AW8622X_REG_TRGCFG8 (0x3A) +#define AW8622X_REG_GLBCFG4 (0x3E) +#define AW8622X_REG_GLBRD5 (0x3F) +#define AW8622X_REG_RAMADDRH (0x40) +#define AW8622X_REG_RAMADDRL (0x41) +#define AW8622X_REG_RAMDATA (0x42) +#define AW8622X_REG_SYSCTRL1 (0x43) +#define AW8622X_REG_SYSCTRL2 (0x44) +#define AW8622X_REG_SYSCTRL3 (0x45) +#define AW8622X_REG_SYSCTRL4 (0x46) +#define AW8622X_REG_SYSCTRL5 (0x47) +#define AW8622X_REG_SYSCTRL6 (0x48) +#define AW8622X_REG_SYSCTRL7 (0x49) +#define AW8622X_REG_PWMCFG1 (0x4C) +#define AW8622X_REG_PWMCFG3 (0x4E) +#define AW8622X_REG_PWMCFG4 (0x4F) +#define AW8622X_REG_DETCFG1 (0x51) +#define AW8622X_REG_DETCFG2 (0x52) +#define AW8622X_REG_DET_RL (0x53) +#define AW8622X_REG_DET_VBAT (0x55) +#define AW8622X_REG_DET_LO (0x57) +#define AW8622X_REG_TRIMCFG1 (0x58) +#define AW8622X_REG_TRIMCFG3 (0x5A) +#define AW8622X_REG_EFRD9 (0x64) +#define AW8622X_REG_ANACFG8 (0x77) +/****************************************************** + * Register Detail + *****************************************************/ +/* SYSST: reg 0x01 RO */ +#define AW8622X_BIT_SYSST_UVLS (1<<5) +#define AW8622X_BIT_SYSST_FF_AES (1<<4) +#define AW8622X_BIT_SYSST_FF_AFS (1<<3) +#define AW8622X_BIT_SYSST_OCDS (1<<2) +#define AW8622X_BIT_SYSST_OTS (1<<1) +#define AW8622X_BIT_SYSST_DONES (1<<0) + +/* SYSINT: reg 0x02 RC */ +#define AW8622X_BIT_SYSINT_UVLI (1<<5) +#define AW8622X_BIT_SYSINT_FF_AEI (1<<4) +#define AW8622X_BIT_SYSINT_FF_AFI (1<<3) +#define AW8622X_BIT_SYSINT_OCDI (1<<2) +#define AW8622X_BIT_SYSINT_OTI (1<<1) +#define AW8622X_BIT_SYSINT_DONEI (1<<0) + +/* SYSINTM: reg 0x03 RW */ +#define AW8622X_BIT_SYSINTM_UVLM_MASK (~(1<<5)) +#define AW8622X_BIT_SYSINTM_UVLM_OFF (1<<5) +#define AW8622X_BIT_SYSINTM_UVLM_ON (0<<5) +#define AW8622X_BIT_SYSINTM_FF_AEM_MASK (~(1<<4)) +#define AW8622X_BIT_SYSINTM_FF_AEM_OFF (1<<4) +#define AW8622X_BIT_SYSINTM_FF_AEM_ON (0<<4) +#define AW8622X_BIT_SYSINTM_FF_AFM_MASK (~(1<<3)) +#define AW8622X_BIT_SYSINTM_FF_AFM_OFF (1<<3) +#define AW8622X_BIT_SYSINTM_FF_AFM_ON (0<<3) +#define AW8622X_BIT_SYSINTM_OCDM_MASK (~(1<<2)) +#define AW8622X_BIT_SYSINTM_OCDM_OFF (1<<2) +#define AW8622X_BIT_SYSINTM_OCDM_ON (0<<2) +#define AW8622X_BIT_SYSINTM_OTM_MASK (~(1<<1)) +#define AW8622X_BIT_SYSINTM_OTM_OFF (1<<1) +#define AW8622X_BIT_SYSINTM_OTM_ON (0<<1) +#define AW8622X_BIT_SYSINTM_DONEM_MASK (~(1<<0)) +#define AW8622X_BIT_SYSINTM_DONEM_OFF (1<<0) +#define AW8622X_BIT_SYSINTM_DONEM_ON (0<<0) + +/* SYSST2: reg 0x04 RO */ +#define AW8622X_BIT_SYSST2_RAM_ADDR_ER (1<<7) +#define AW8622X_BIT_SYSST2_TRG_ADDR_ER (1<<6) +#define AW8622X_BIT_SYSST2_VBG_OK (1<<3) +#define AW8622X_BIT_SYSST2_LDO_OK (1<<2) +#define AW8622X_BIT_SYSST2_FF_FULL (1<<1) +#define AW8622X_BIT_SYSST2_FF_EMPTY (1<<0) + +/* SYSER: reg 0x05 RC */ +#define AW8622X_BIT_SYSER_I2S_ERR (1<<7) +#define AW8622X_BIT_SYSER_TRIG1_EVENT (1<<6) +#define AW8622X_BIT_SYSER_TRIG2_EVENT (1<<5) +#define AW8622X_BIT_SYSER_TRIG3_EVENT (1<<4) +#define AW8622X_BIT_SYSER_OV (1<<3) +#define AW8622X_BIT_SYSER_ADDR_ER (1<<2) +#define AW8622X_BIT_SYSER_FF_ER (1<<1) +#define AW8622X_BIT_SYSER_PLL_REF_ER (1<<0) + +/* PLAYCFG3: reg 0x08 RW */ +#define AW8622X_BIT_PLAYCFG3_STOP_MODE_MASK (~(1<<5)) +#define AW8622X_BIT_PLAYCFG3_STOP_MODE_NOW (1<<5) +#define AW8622X_BIT_PLAYCFG3_STOP_MODE_LATER (0<<5) +#define AW8622X_BIT_PLAYCFG3_BRK_EN_MASK (~(1<<2)) +#define AW8622X_BIT_PLAYCFG3_BRK (1<<2) +#define AW8622X_BIT_PLAYCFG3_BRK_ENABLE (1<<2) +#define AW8622X_BIT_PLAYCFG3_BRK_DISABLE (0<<2) +#define AW8622X_BIT_PLAYCFG3_PLAY_MODE_MASK (~(3<<0)) +#define AW8622X_BIT_PLAYCFG3_PLAY_MODE_STOP (3<<0) +#define AW8622X_BIT_PLAYCFG3_PLAY_MODE_CONT (2<<0) +#define AW8622X_BIT_PLAYCFG3_PLAY_MODE_RTP (1<<0) +#define AW8622X_BIT_PLAYCFG3_PLAY_MODE_RAM (0<<0) + +/* PLAYCFG4: reg 0x09 RW */ +#define AW8622X_BIT_PLAYCFG4_STOP_MASK (~(1<<1)) +#define AW8622X_BIT_PLAYCFG4_STOP_ON (1<<1) +#define AW8622X_BIT_PLAYCFG4_STOP_OFF (0<<1) +#define AW8622X_BIT_PLAYCFG4_GO_MASK (~(1<<0)) +#define AW8622X_BIT_PLAYCFG4_GO_ON (1<<0) +#define AW8622X_BIT_PLAYCFG4_GO_OFF (0<<0) + +/* WAVCFG1-8: reg 0x0A - reg 0x11 RW */ +#define AW8622X_BIT_WAVCFG_SEQWAIT_MASK (~(1<<7)) +#define AW8622X_BIT_WAVCFG_SEQWAIT_TIME (1<<7) +#define AW8622X_BIT_WAVCFG_SEQWAIT_NUMBER (0<<7) +#define AW8622X_BIT_WAVCFG_SEQ (0x7F) + +/* WAVCFG9-12: reg 0x12 - reg 0x15 RW */ +#define AW8622X_BIT_WAVLOOP_SEQ_ODD_MASK (~(0x0F<<4)) +#define AW8622X_BIT_WAVLOOP_SEQ_ODD_INIFINITELY (0x0F<<4) +#define AW8622X_BIT_WAVLOOP_SEQ_EVEN_MASK (~(0x0F<<0)) +#define AW8622X_BIT_WAVLOOP_SEQ_EVEN_INIFINITELY (0x0F<<0) +#define AW8622X_BIT_WAVLOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG9: reg 0x12 RW */ +#define AW8622X_BIT_WAVCFG9_SEQ1LOOP_MASK (~(0x0F<<4)) +#define AW8622X_BIT_WAVCFG9_SEQ1LOOP_INIFINITELY (0x0F<<4) +#define AW8622X_BIT_WAVCFG9_SEQ2LOOP_MASK (~(0x0F<<0)) +#define AW8622X_BIT_WAVCFG9_SEQ2LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG10: reg 0x13 RW */ +#define AW8622X_BIT_WAVCFG10_SEQ3LOOP_MASK (~(0x0F<<4)) +#define AW8622X_BIT_WAVCFG10_SEQ3LOOP_INIFINITELY (0x0F<<4) +#define AW8622X_BIT_WAVCFG10_SEQ4LOOP_MASK (~(0x0F<<0)) +#define AW8622X_BIT_WAVCFG10_SEQ4LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG11: reg 0x14 RW */ +#define AW8622X_BIT_WAVCFG11_SEQ5LOOP_MASK (~(0x0F<<4)) +#define AW8622X_BIT_WAVCFG11_SEQ5LOOP_INIFINITELY (0x0F<<4) +#define AW8622X_BIT_WAVCFG11_SEQ6LOOP_MASK (~(0x0F<<0)) +#define AW8622X_BIT_WAVCFG11_SEQ6LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG12: reg 0x15 RW */ +#define AW8622X_BIT_WAVCFG12_SEQ7LOOP_MASK (~(0x0F<<4)) +#define AW8622X_BIT_WAVCFG12_SEQ7LOOP_INIFINITELY (0x0F<<4) +#define AW8622X_BIT_WAVCFG12_SEQ8LOOP_MASK (~(0x0F<<0)) +#define AW8622X_BIT_WAVCFG12_SEQ8LOOP_INIFINITELY (0x0F<<0) + +/***************** CONT *****************/ +/* CONTCFG1: reg 0x18 RW */ +#define AW8622X_BIT_CONTCFG1_EDGE_FRE_MASK (~(0x0F<<4)) +#define AW8622X_BIT_CONTCFG1_EN_F0_DET_MASK (~(1<<3)) +#define AW8622X_BIT_CONTCFG1_F0_DET_ENABLE (1<<3) +#define AW8622X_BIT_CONTCFG1_F0_DET_DISABLE (0<<3) +#define AW8622X_BIT_CONTCFG1_SIN_MODE_MASK (~(1<<0)) +#define AW8622X_BIT_CONTCFG1_SIN_MODE_COS (1<<0) +#define AW8622X_BIT_CONTCFG1_SIN_MODE_SINE (0<<0) + +/* CONTCFG5: reg 0x1C RW */ +#define AW8622X_BIT_CONTCFG5_BRK_GAIN_MASK (~(0x0F<<0)) + +/* CONTCFG6: reg 0x1D RW */ +#define AW8622X_BIT_CONTCFG6_TRACK_EN_MASK (~(1<<7)) +#define AW8622X_BIT_CONTCFG6_TRACK_ENABLE (1<<7) +#define AW8622X_BIT_CONTCFG6_TRACK_DISABLE (0<<7) +#define AW8622X_BIT_CONTCFG6_DRV1_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG7: reg 0x1E RW */ +#define AW8622X_BIT_CONTCFG7_DRV2_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG13: reg 0x24 RW */ +#define AW8622X_BIT_CONTCFG13_TSET_MASK (~(0x0F<<4)) +#define AW8622X_BIT_CONTCFG13_BEME_SET_MASK (~(0x0F<<0)) + +/***************** RTP *****************/ +/* RTPCFG1: reg 0x2D RW */ +#define AW8622X_BIT_RTPCFG1_ADDRH_MASK (~(0x0F<<0)) + +#define AW8622X_BIT_RTPCFG1_SRAM_SIZE_2K_MASK (~(1<<5)) +#define AW8622X_BIT_RTPCFG1_SRAM_SIZE_2K_EN (1<<5) +#define AW8622X_BIT_RTPCFG1_SRAM_SIZE_2K_DIS (0<<5) + +#define AW8622X_BIT_RTPCFG1_SRAM_SIZE_1K_MASK (~(1<<4)) +#define AW8622X_BIT_RTPCFG1_SRAM_SIZE_1K_EN (1<<4) +#define AW8622X_BIT_RTPCFG1_SRAM_SIZE_1K_DIS (0<<4) + +/* RTPCFG3: reg 0x2F RW */ +#define AW8622X_BIT_RTPCFG3_FIFO_AEH_MASK (~(0x0F<<4)) +#define AW8622X_BIT_RTPCFG3_FIFO_AFH_MASK (~(0x0F<<0)) +#define AW8622X_BIT_RTPCFG3_FIFO_AEH (0x0F<<4) +#define AW8622X_BIT_RTPCFG3_FIFO_AFH (0x0F<<0) + +/***************** TRIGGER *****************/ +#define AW8622X_BIT_TRG_ENABLE_MASK (~(1<<7)) +#define AW8622X_BIT_TRG_ENABLE (1<<7) +#define AW8622X_BIT_TRG_DISABLE (0<<7) +#define AW8622X_BIT_TRG_SEQ_MASK (~(0x7F<<0)) + +/* TRGCFG1: reg 0x33 RW */ +#define AW8622X_BIT_TRGCFG1_TRG1_POS_MASK (~(1<<7)) +#define AW8622X_BIT_TRGCFG1_TRG1_POS_ENABLE (1<<7) +#define AW8622X_BIT_TRGCFG1_TRG1_POS_DISABLE (0<<7) +#define AW8622X_BIT_TRGCFG1_TRG1SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG2: reg 0x34 RW */ +#define AW8622X_BIT_TRGCFG2_TRG2_POS_MASK (~(1<<7)) +#define AW8622X_BIT_TRGCFG2_TRG2_POS_ENABLE (1<<7) +#define AW8622X_BIT_TRGCFG2_TRG2_POS_DISABLE (0<<7) +#define AW8622X_BIT_TRGCFG2_TRG2SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG3: reg 0x35 RW */ +#define AW8622X_BIT_TRGCFG3_TRG3_POS_MASK (~(1<<7)) +#define AW8622X_BIT_TRGCFG3_TRG3_POS_ENABLE (1<<7) +#define AW8622X_BIT_TRGCFG3_TRG3_POS_DISABLE (0<<7) +#define AW8622X_BIT_TRGCFG3_TRG3SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG4: reg 0x36 RW */ +#define AW8622X_BIT_TRGCFG4_TRG1_NEG_MASK (~(1<<7)) +#define AW8622X_BIT_TRGCFG4_TRG1_NEG_ENABLE (1<<7) +#define AW8622X_BIT_TRGCFG4_TRG1_NEG_DISABLE (0<<7) +#define AW8622X_BIT_TRGCFG4_TRG1SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG5: reg 0x37 RW */ +#define AW8622X_BIT_TRGCFG5_TRG2_NEG_MASK (~(1<<7)) +#define AW8622X_BIT_TRGCFG5_TRG2_NEG_ENABLE (1<<7) +#define AW8622X_BIT_TRGCFG5_TRG2_NEG_DISABLE (0<<7) +#define AW8622X_BIT_TRGCFG5_TRG2SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG6: reg 0x38 RW */ +#define AW8622X_BIT_TRGCFG6_TRG3_NEG_MASK (~(1<<7)) +#define AW8622X_BIT_TRGCFG6_TRG3_NEG_ENABLE (1<<7) +#define AW8622X_BIT_TRGCFG6_TRG3_NEG_DISABLE (0<<7) +#define AW8622X_BIT_TRGCFG6_TRG3SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG7: reg 0x39 RW */ +#define AW8622X_BIT_TRGCFG7_TRG1_POR_LEV_BRK_MASK (~(7<<5)) +#define AW8622X_BIT_TRGCFG7_TRG2_POR_LEV_BRK_MASK (~(7<<1)) +#define AW8622X_BIT_TRGCFG7_TRG1_POLAR_MASK (~(1<<7)) +#define AW8622X_BIT_TRGCFG7_TRG1_POLAR_NEG (1<<7) +#define AW8622X_BIT_TRGCFG7_TRG1_POLAR_POS (0<<7) +#define AW8622X_BIT_TRGCFG7_TRG1_MODE_MASK (~(1<<6)) +#define AW8622X_BIT_TRGCFG7_TRG1_MODE_LEVEL (1<<6) +#define AW8622X_BIT_TRGCFG7_TRG1_MODE_EDGE (0<<6) +#define AW8622X_BIT_TRGCFG7_TRG1_AUTO_BRK_MASK (~(1<<5)) +#define AW8622X_BIT_TRGCFG7_TRG1_AUTO_BRK_ENABLE (1<<5) +#define AW8622X_BIT_TRGCFG7_TRG1_AUTO_BRK_DISABLE (0<<5) +#define AW8622X_BIT_TRGCFG7_TRG2_POLAR_MASK (~(1<<3)) +#define AW8622X_BIT_TRGCFG7_TRG2_POLAR_NEG (1<<3) +#define AW8622X_BIT_TRGCFG7_TRG2_POLAR_POS (0<<3) +#define AW8622X_BIT_TRGCFG7_TRG2_MODE_MASK (~(1<<2)) +#define AW8622X_BIT_TRGCFG7_TRG2_MODE_LEVEL (1<<2) +#define AW8622X_BIT_TRGCFG7_TRG2_MODE_EDGE (0<<2) +#define AW8622X_BIT_TRGCFG7_TRG2_AUTO_BRK_MASK (~(1<<1)) +#define AW8622X_BIT_TRGCFG7_TRG2_AUTO_BRK_ENABLE (1<<1) +#define AW8622X_BIT_TRGCFG7_TRG2_AUTO_BRK_DISABLE (0<<1) + +/* TRGCFG8: reg 0x3A RW */ +#define AW8622X_BIT_TRGCFG8_TRG3_POR_LEV_BRK_MASK (~(7<<5)) +#define AW8622X_BIT_TRGCFG8_TRG3_POLAR_MASK (~(1<<7)) +#define AW8622X_BIT_TRGCFG8_TRG3_POLAR_NEG (1<<7) +#define AW8622X_BIT_TRGCFG8_TRG3_POLAR_POS (0<<7) +#define AW8622X_BIT_TRGCFG8_TRG3_MODE_MASK (~(1<<6)) +#define AW8622X_BIT_TRGCFG8_TRG3_MODE_LEVEL (1<<6) +#define AW8622X_BIT_TRGCFG8_TRG3_MODE_EDGE (0<<6) +#define AW8622X_BIT_TRGCFG8_TRG3_AUTO_BRK_MASK (~(1<<5)) +#define AW8622X_BIT_TRGCFG8_TRG3_AUTO_BRK_ENABLE (1<<5) +#define AW8622X_BIT_TRGCFG8_TRG3_AUTO_BRK_DISABLE (0<<5) +#define AW8622X_BIT_TRGCFG8_TRG_TRIG1_MODE_MASK (~(3<<3)) +#define AW8622X_BIT_TRGCFG8_PWM_LRA (0<<3) +#define AW8622X_BIT_TRGCFG8_PWM_ERA (1<<3) +#define AW8622X_BIT_TRGCFG8_TRIG1 (2<<3) +#define AW8622X_BIT_TRGCFG8_DISABLE (3<<3) +#define AW8622X_BIT_TRGCFG8_TRG1_STOP_MASK (~(1<<2)) +#define AW8622X_BIT_TRGCFG8_TRG1_STOP (1<<2) +#define AW8622X_BIT_TRGCFG8_TRG2_STOP_MASK (~(1<<1)) +#define AW8622X_BIT_TRGCFG8_TRG2_STOP (1<<1) +#define AW8622X_BIT_TRGCFG8_TRG3_STOP_MASK (~(1<<0)) +#define AW8622X_BIT_TRGCFG8_TRG3_STOP (1<<0) + +/* GLBCFG4: reg 0x3E RW */ +#define AW8622X_BIT_GLBCFG4_GO_PRIO_MASK (~(3<<6)) +#define AW8622X_BIT_GLBCFG4_TRG3_PRIO_MASK (~(3<<4)) +#define AW8622X_BIT_GLBCFG4_TRG2_PRIO_MASK (~(3<<2)) +#define AW8622X_BIT_GLBCFG4_TRG1_PRIO_MASK (~(3<<0)) + +/* GLBRD5: reg 0x3F R0 */ +/* GLB_STATE */ +#define AW8622X_BIT_GLBRD5_STATE (15<<0) +#define AW8622X_BIT_GLBRD5_STATE_STANDBY (0<<0) +#define AW8622X_BIT_GLBRD5_STATE_WAKEUP (1<<0) +#define AW8622X_BIT_GLBRD5_STATE_STARTUP (2<<0) +#define AW8622X_BIT_GLBRD5_STATE_WAIT (3<<0) +#define AW8622X_BIT_GLBRD5_STATE_CONT_GO (6<<0) +#define AW8622X_BIT_GLBRD5_STATE_RAM_GO (7<<0) +#define AW8622X_BIT_GLBRD5_STATE_RTP_GO (8<<0) +#define AW8622X_BIT_GLBRD5_STATE_TRIG_GO (9<<0) +#define AW8622X_BIT_GLBRD5_STATE_I2S_GO (10<<0) +#define AW8622X_BIT_GLBRD5_STATE_BRAKE (11<<0) +#define AW8622X_BIT_GLBRD5_STATE_END (12<<0) +/* RAMADDRH: reg 0x40 RWS */ +#define AW8622X_BIT_RAMADDRH_MASK (~(63<<0)) + +/***************** SYSCTRL *****************/ +/* SYSCTRL1: reg 0x43 RW */ +#define AW8622X_BIT_SYSCTRL1_VBAT_MODE_MASK (~(1<<7)) +#define AW8622X_BIT_SYSCTRL1_VBAT_MODE_HW (1<<7) +#define AW8622X_BIT_SYSCTRL1_VBAT_MODE_SW (0<<7) +#define AW8622X_BIT_SYSCTRL1_PERP_MASK (~(1<<6)) +#define AW8622X_BIT_SYSCTRL1_PERP_ON (1<<6) +#define AW8622X_BIT_SYSCTRL1_PERP_OFF (0<<6) +#define AW8622X_BIT_SYSCTRL1_CLK_SEL_MASK (~(3<<4)) +#define AW8622X_BIT_SYSCTRL1_CLK_SEL_OSC (1<<4) +#define AW8622X_BIT_SYSCTRL1_CLK_SEL_AUTO (0<<4) +#define AW8622X_BIT_SYSCTRL1_RAMINIT_MASK (~(1<<3)) +#define AW8622X_BIT_SYSCTRL1_RAMINIT_ON (1<<3) +#define AW8622X_BIT_SYSCTRL1_RAMINIT_OFF (0<<3) +#define AW8622X_BIT_SYSCTRL1_EN_FIR_MASK (~(1<<2)) +#define AW8622X_BIT_SYSCTRL1_FIR_ENABLE (0<<2) +#define AW8622X_BIT_SYSCTRL1_WAKE_MODE_MASK (~(1<<1)) +#define AW8622X_BIT_SYSCTRL1_WAKE_MODE_WAKEUP (1<<1) +#define AW8622X_BIT_SYSCTRL1_WAKE_MODE_BST (0<<1) +#define AW8622X_BIT_SYSCTRL1_RTP_CLK_MASK (~(1<<0)) +#define AW8622X_BIT_SYSCTRL1_RTP_PLL (1<<0) +#define AW8622X_BIT_SYSCTRL1_RTP_OSC (0<<0) + +/* SYSCTRL2: reg 0x44 RW */ +#define AW8622X_BIT_SYSCTRL2_WAKE_MASK (~(1<<7)) +#define AW8622X_BIT_SYSCTRL2_WAKE_ON (1<<7) +#define AW8622X_BIT_SYSCTRL2_WAKE_OFF (0<<7) +#define AW8622X_BIT_SYSCTRL2_STANDBY_MASK (~(1<<6)) +#define AW8622X_BIT_SYSCTRL2_STANDBY_ON (1<<6) +#define AW8622X_BIT_SYSCTRL2_STANDBY_OFF (0<<6) +#define AW8622X_BIT_SYSCTRL2_RTP_DLY_MASK (~(3<<4)) +#define AW8622X_BIT_SYSCTRL2_INTN_PIN_MASK (~(1<<3)) +#define AW8622X_BIT_SYSCTRL2_INTN (1<<3) +#define AW8622X_BIT_SYSCTRL2_TRIG1 (0<<3) +#define AW8622X_BIT_SYSCTRL2_WCK_PIN_MASK (~(1<<2)) +#define AW8622X_BIT_SYSCTRL2_ENABLE_TRIG2 (1<<2) +#define AW8622X_BIT_SYSCTRL2_DISENABLE_TRIG2 (0<<2) +#define AW8622X_BIT_SYSCTRL2_WAVDAT_MODE_MASK (~(3<<0)) +#define AW8622X_BIT_SYSCTRL2_RATE (3<<0) +#define AW8622X_BIT_SYSCTRL2_RATE_12K (2<<0) +#define AW8622X_BIT_SYSCTRL2_RATE_24K (0<<0) +#define AW8622X_BIT_SYSCTRL2_RATE_48K (1<<0) + +/* SYSCTRL7: reg 0x49 RW */ +#define AW8622X_BIT_SYSCTRL7_GAIN_BYPASS_MASK (~(1<<6)) +#define AW8622X_BIT_SYSCTRL7_GAIN_CHANGEABLE (1<<6) +#define AW8622X_BIT_SYSCTRL7_GAIN_FIXED (0<<6) +#define AW8622X_BIT_SYSCTRL7_GAIN (0x07) + +#define AW8622X_BIT_SYSCTRL7_INT_EDGE_MODE_MASK (~(1<<5)) +#define AW8622X_BIT_SYSCTRL7_INT_EDGE_MODE_POS (0<<5) +#define AW8622X_BIT_SYSCTRL7_INT_EDGE_MODE_BOTH (1<<5) +#define AW8622X_BIT_SYSCTRL7_INT_MODE_MASK (~(1<<4)) +#define AW8622X_BIT_SYSCTRL7_INT_MODE_EDGE (1<<4) +#define AW8622X_BIT_SYSCTRL7_INT_MODE_LEVEL (0<<4) + +#define AW8622X_BIT_SYSCTRL7_INTP_MASK (~(1<<3)) +#define AW8622X_BIT_SYSCTRL7_INTP_HIGH (1<<3) +#define AW8622X_BIT_SYSCTRL7_INTP_LOW (0<<3) +#define AW8622X_BIT_SYSCTRL7_D2S_GAIN_MASK (~(7<<0)) +#define AW8622X_BIT_SYSCTRL7_D2S_GAIN_1 (0<<0) +#define AW8622X_BIT_SYSCTRL7_D2S_GAIN_2 (1<<0) +#define AW8622X_BIT_SYSCTRL7_D2S_GAIN_4 (2<<0) +#define AW8622X_BIT_SYSCTRL7_D2S_GAIN_5 (3<<0) +#define AW8622X_BIT_SYSCTRL7_D2S_GAIN_8 (4<<0) +#define AW8622X_BIT_SYSCTRL7_D2S_GAIN_10 (5<<0) +#define AW8622X_BIT_SYSCTRL7_D2S_GAIN_20 (6<<0) +#define AW8622X_BIT_SYSCTRL7_D2S_GAIN_40 (7<<0) + +/* PWMCFG1: reg 0x4C RW */ +#define AW8622X_BIT_PWMCFG1_PRC_EN_MASK (~(1<<7)) +#define AW8622X_BIT_PWMCFG1_PRC_ENABLE (1<<7) +#define AW8622X_BIT_PWMCFG1_PRC_DISABLE (0<<7) +#define AW8622X_BIT_PWMCFG1_PRCTIME_MASK (~(0x7F<<0)) + +/* PWMCFG2: reg 0x4D RW */ +#define AW8622X_BIT_PWMCFG2_REF_SEL_MASK (~(1<<5)) +#define AW8622X_BIT_PWMCFG2_REF_SEL_TRIANGLE (1<<5) +#define AW8622X_BIT_PWMCFG2_REF_SEL_SAWTOOTH (0<<5) +#define AW8622X_BIT_PWMCFG2_PD_HWM_MASK (~(1<<4)) +#define AW8622X_BIT_PWMCFG2_PD_HWM_ON (1<<4) +#define AW8622X_BIT_PWMCFG2_PWMOE_MASK (~(1<<3)) +#define AW8622X_BIT_PWMCFG2_PWMOE_ON (1<<3) +#define AW8622X_BIT_PWMCFG2_PWMFRC_MASK (~(7<<0)) + +/* PWMCFG3: reg 0x4E RW */ +#define AW8622X_BIT_PWMCFG3_PR_EN_MASK (~(1<<7)) +#define AW8622X_BIT_PWMCFG3_PR_ENABLE (1<<7) +#define AW8622X_BIT_PWMCFG3_PR_DISABLE (0<<7) +#define AW8622X_BIT_PWMCFG3_PRLVL_MASK (~(0x7F<<0)) + +/* DETCFG1: reg 0x51 RW */ +#define AW8622X_BIT_DETCFG1_FTS_GO_MASK (~(1<<7)) +#define AW8622X_BIT_DETCFG1_FTS_GO_ENABLE (1<<7) +#define AW8622X_BIT_DETCFG1_TEST_GO_MASK (~(1<<6)) +#define AW8622X_BIT_DETCFG1_TEST_GO_ENABLE (1<<6) +#define AW8622X_BIT_DETCFG1_ADO_SLOT_MODE_MASK (~(1<<5)) +#define AW8622X_BIT_DETCFG1_ADO_SLOT_ADC_32 (1<<5) +#define AW8622X_BIT_DETCFG1_ADO_SLOT_ADC_256 (0<<5) +#define AW8622X_BIT_DETCFG1_RL_OS_MASK (~(1<<4)) +#define AW8622X_BIT_DETCFG1_RL (1<<4) +#define AW8622X_BIT_DETCFG1_OS (0<<4) +#define AW8622X_BIT_DETCFG1_PRCT_MODE_MASK (~(1<<3)) +#define AW8622X_BIT_DETCFG1_PRCT_MODE_INVALID (1<<3) +#define AW8622X_BIT_DETCFG1_PRCT_MODE_VALID (0<<3) +#define AW8622X_BIT_DETCFG1_CLK_ADC_MASK (~(7<<0)) +#define AW8622X_BIT_DETCFG1_CLK_ADC_12M (0<<0) +#define AW8622X_BIT_DETCFG1_CLK_ADC_6M (1<<0) +#define AW8622X_BIT_DETCFG1_CLK_ADC_3M (2<<0) +#define AW8622X_BIT_DETCFG1_CLK_ADC_1M5 (3<<0) +#define AW8622X_BIT_DETCFG1_CLK_ADC_M75 (4<<0) +#define AW8622X_BIT_DETCFG1_CLK_ADC_M37 (5<<0) +#define AW8622X_BIT_DETCFG1_CLK_ADC_M18 (6<<0) +#define AW8622X_BIT_DETCFG1_CLK_ADC_M09 (7<<0) + +/* DETCFG2: reg 0x52 RW */ +#define AW8622X_BIT_DETCFG2_VBAT_GO_MASK (~(1<<1)) +#define AW8622X_BIT_DETCFG2_VABT_GO_ON (1<<1) +#define AW8622X_BIT_DETCFG2_DIAG_GO_MASK (~(1<<0)) +#define AW8622X_BIT_DETCFG2_DIAG_GO_ON (1<<0) + +/* DET_LO: reg 0x57 RW */ +#define AW8622X_BIT_DET_LO_TEST_MASK (~(3<<6)) +#define AW8622X_BIT_DET_LO_VBAT_MASK (~(3<<4)) +#define AW8622X_BIT_DET_LO_VBAT (3<<4) +#define AW8622X_BIT_DET_LO_OS_MASK (~(3<<2)) +#define AW8622X_BIT_DET_LO_RL_MASK (~(3<<0)) +#define AW8622X_BIT_DET_LO_RL (3<<0) + +/* TRIMCFG1: reg:0x58 RW */ +#define AW8622X_BIT_TRIMCFG1_RL_TRIM_SRC_MASK (~(1<<6)) +#define AW8622X_BIT_TRIMCFG1_RL_TRIM_SRC_REG (1<<6) +#define AW8622X_BIT_TRIMCFG1_RL_TRIM_SRC_EFUSE (0<<6) +#define AW8622X_BIT_TRIMCFG1_TRIM_RL_MASK (~(63<<0)) + +/* TRIMCFG3: reg:0x5A RW */ +#define AW8622X_BIT_TRIMCFG3_OSC_TRIM_SRC_MASK (~(1<<7)) +#define AW8622X_BIT_TRIMCFG3_OSC_TRIM_SRC_REG (1<<7) +#define AW8622X_BIT_TRIMCFG3_OSC_TRIM_SRC_EFUSE (0<<7) +#define AW8622X_BIT_TRIMCFG3_TRIM_LRA_MASK (~(63<<0)) + +/* TRIMCFG4: reg:0x5B RW */ +/* TRIM_OSC */ + +/* ANACFG8: reg:0x77 RW */ +#define AW8622X_BIT_ANACFG8_TRTF_CTRL_HDRV_MASK (~(1<<6)) +#define AW8622X_BIT_ANACFG8_TRTF_CTRL_HDRV (3<<6) + +#endif diff --git a/drivers/misc/awinic/aw862x/aw8624.c b/drivers/misc/awinic/aw862x/aw8624.c new file mode 100644 index 000000000000..11a453f3bad9 --- /dev/null +++ b/drivers/misc/awinic/aw862x/aw8624.c @@ -0,0 +1,4081 @@ +/* + * aw8624.c + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aw8624.h" +#include "haptic_nv.h" + +/****************************************************** + * + * Register Access + * + ******************************************************/ +const unsigned char aw8624_reg_list[] = { + AW8624_REG_ID, + AW8624_REG_SYSST, + AW8624_REG_SYSINT, + AW8624_REG_SYSINTM, + AW8624_REG_SYSCTRL, + AW8624_REG_GO, + AW8624_REG_WAVSEQ1, + AW8624_REG_WAVSEQ2, + AW8624_REG_WAVSEQ3, + AW8624_REG_WAVSEQ4, + AW8624_REG_WAVSEQ5, + AW8624_REG_WAVSEQ6, + AW8624_REG_WAVSEQ7, + AW8624_REG_WAVSEQ8, + AW8624_REG_WAVLOOP1, + AW8624_REG_WAVLOOP2, + AW8624_REG_WAVLOOP3, + AW8624_REG_WAVLOOP4, + AW8624_REG_MANLOOP, + AW8624_REG_TRG1_SEQP, + AW8624_REG_TRG1_SEQN, + AW8624_REG_TRG_CFG1, + AW8624_REG_TRG_CFG2, + AW8624_REG_DBGCTRL, + AW8624_REG_BASE_ADDRH, + AW8624_REG_BASE_ADDRL, + AW8624_REG_FIFO_AEH, + AW8624_REG_FIFO_AEL, + AW8624_REG_FIFO_AFH, + AW8624_REG_FIFO_AFL, + AW8624_REG_DATCTRL, + AW8624_REG_PWMPRC, + AW8624_REG_PWMDBG, + AW8624_REG_DBGSTAT, + AW8624_REG_WAVECTRL, + AW8624_REG_BRAKE0_CTRL, + AW8624_REG_BRAKE1_CTRL, + AW8624_REG_BRAKE2_CTRL, + AW8624_REG_BRAKE_NUM, + AW8624_REG_ANACTRL, + AW8624_REG_SW_BRAKE, + AW8624_REG_DATDBG, + AW8624_REG_PRLVL, + AW8624_REG_PRTIME, + AW8624_REG_RAMADDRH, + AW8624_REG_RAMADDRL, + AW8624_REG_BRA_MAX_NUM, + AW8624_REG_GLB_STATE, + AW8624_REG_CONT_CTRL, + AW8624_REG_F_PRE_H, + AW8624_REG_F_PRE_L, + AW8624_REG_TD_H, + AW8624_REG_TD_L, + AW8624_REG_TSET, + AW8624_REG_THRS_BRA_END, + AW8624_REG_EF_RDATAH, + AW8624_REG_TRIM_LRA, + AW8624_REG_R_SPARE, + AW8624_REG_D2SCFG, + AW8624_REG_DETCTRL, + AW8624_REG_RLDET, + AW8624_REG_OSDET, + AW8624_REG_VBATDET, + AW8624_REG_ADCTEST, + AW8624_REG_F_LRA_F0_H, + AW8624_REG_F_LRA_F0_L, + AW8624_REG_F_LRA_CONT_H, + AW8624_REG_F_LRA_CONT_L, + AW8624_REG_WAIT_VOL_MP, + AW8624_REG_WAIT_VOL_MN, + AW8624_REG_ZC_THRSH_H, + AW8624_REG_ZC_THRSH_L, + AW8624_REG_BEMF_VTHH_H, + AW8624_REG_BEMF_VTHH_L, + AW8624_REG_BEMF_VTHL_H, + AW8624_REG_BEMF_VTHL_L, + AW8624_REG_BEMF_NUM, + AW8624_REG_DRV_TIME, + AW8624_REG_TIME_NZC, + AW8624_REG_DRV_LVL, + AW8624_REG_DRV_LVL_OV, + AW8624_REG_NUM_F0_1, + AW8624_REG_NUM_F0_2, + AW8624_REG_NUM_F0_3, +}; + +/****************************************************** + * + * value + * + ******************************************************/ +static char *aw8624_ram_name = "aw8624_haptic.bin"; +static char aw8624_rtp_name[][AW8624_RTP_NAME_MAX] = { + {"aw8624_osc_rtp_24K_5s.bin"}, + {"aw8624_rtp.bin"}, + {"aw8624_rtp_lighthouse.bin"}, + {"aw8624_rtp_silk.bin"}, +}; +struct aw8624_dts_info aw8624_dts_data; +struct pm_qos_request aw8624_pm_qos_req_vb; + +/****************************************************** + * + * function declaration + * + ******************************************************/ +static void aw8624_interrupt_clear(struct aw8624 *aw8624); +static void aw8624_haptic_upload_lra(struct aw8624 *aw8624, unsigned int flag); +static int aw8624_haptic_stop(struct aw8624 *aw8624); +static int aw8624_analyse_duration_range(struct aw8624 *aw8624); + +/****************************************************** + * + * aw8624 i2c write/read + * + ******************************************************/ +static int aw8624_i2c_read(struct aw8624 *aw8624, unsigned char reg_addr, + unsigned char *buf, unsigned int len) +{ + int ret = 0; + + struct i2c_msg msg[2] = { + [0] = { + .addr = aw8624->i2c->addr, + .flags = 0, + .len = sizeof(uint8_t), + .buf = ®_addr, + }, + [1] = { + .addr = aw8624->i2c->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + }, + }; + + ret = i2c_transfer(aw8624->i2c->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) { + aw_dev_err("%s: i2c_transfer failed\n", __func__); + return ret; + } else if (ret != AW_I2C_READ_MSG_NUM) { + aw_dev_err("%s: transfer failed(size error)\n", __func__); + return -ENXIO; + } + return ret; +} + +static int aw8624_i2c_write(struct aw8624 *aw8624, unsigned char reg_addr, + unsigned char *buf, unsigned int len) +{ + unsigned char *data = NULL; + int ret = -1; + + data = kmalloc(len + 1, GFP_KERNEL); + data[0] = reg_addr; + memcpy(&data[1], buf, len); + ret = i2c_master_send(aw8624->i2c, data, len + 1); + if (ret < 0) + aw_dev_err("%s: i2c master send 0x%02x error\n", + __func__, reg_addr); + kfree(data); + return ret; +} + +static int aw8624_i2c_write_bits(struct aw8624 *aw8624, unsigned char reg_addr, + unsigned int mask, + unsigned char reg_data) +{ + unsigned char reg_val = 0; + int ret = -1; + + ret = aw8624_i2c_read(aw8624, reg_addr, ®_val, AW_I2C_BYTE_ONE); + if (ret < 0) { + aw_dev_err("%s: i2c read error, ret=%d\n", __func__, ret); + return ret; + } + reg_val &= mask; + reg_val |= reg_data; + ret = aw8624_i2c_write(aw8624, reg_addr, ®_val, AW_I2C_BYTE_ONE); + if (ret < 0) { + aw_dev_err("%s: i2c write error, ret=%d\n", __func__, ret); + return ret; + } + return 0; +} + +static int aw8624_parse_data(struct aw8624 *aw8624, const char *buf) +{ + unsigned char reg_num = aw8624->aw_i2c_package.reg_num; + unsigned char i = 0; + const char *temp_buf = NULL; + char data_buf[AWRW_CMD_UNIT] = { 0 }; + unsigned int value = 0; + int ret = 0; + int len = strlen(buf) - (AWRW_CMD_UNIT * 3); + + if (len < 0) { + aw_dev_err("%s: parse data error\n", __func__); + return -ERANGE; + } + temp_buf = &buf[AWRW_CMD_UNIT * 3]; + for (i = 0; i < reg_num; i++) { + if (((i + 1) * AWRW_CMD_UNIT) > len) { + aw_dev_err("%s: parse data error\n", __func__); + return -ERANGE; + } + memcpy(data_buf, &temp_buf[i * AWRW_CMD_UNIT], 4); + data_buf[4] = '\0'; + ret = kstrtouint(data_buf, 0, &value); + if (ret < 0) { + aw_dev_err("%s: kstrtouint error\n", __func__); + return ret; + } + aw8624->aw_i2c_package.reg_data[i] = (unsigned char)value; + } + return 0; +} + +static void aw8624_interrupt_clear(struct aw8624 *aw8624) +{ + unsigned char reg_val = 0; + + aw8624_i2c_read(aw8624, AW8624_REG_SYSINT, ®_val, AW_I2C_BYTE_ONE); + aw_dev_info("%s: reg SYSINT=0x%x\n", __func__, reg_val); +} + +/***************************************************** + * + * ram update + * + *****************************************************/ +static int aw8624_haptic_juge_RTP_is_going_on(struct aw8624 *aw8624) +{ + unsigned char rtp_state = 0; + unsigned char mode = 0; + unsigned char glb_st = 0; + + aw8624_i2c_read(aw8624, AW8624_REG_SYSCTRL, &mode, AW_I2C_BYTE_ONE); + aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, &glb_st, AW_I2C_BYTE_ONE); + if ((mode & AW8624_BIT_SYSCTRL_PLAY_MODE_RTP) && + (glb_st == AW8624_BIT_GLBRD5_STATE_RTP_GO)) { + rtp_state = 1; + } + return rtp_state; +} + +static void aw8624_haptic_raminit(struct aw8624 *aw8624, bool flag) +{ + if (flag) { + aw8624_i2c_write_bits(aw8624, AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_RAMINIT_MASK, + AW8624_BIT_SYSCTRL_RAMINIT_EN); + } else { + aw8624_i2c_write_bits(aw8624, AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_RAMINIT_MASK, + AW8624_BIT_SYSCTRL_RAMINIT_OFF); + } +} + +#ifdef AW_CHECK_RAM_DATA +static int aw8624_check_ram_data(struct aw8624 *aw8624, + unsigned char *cont_data, + unsigned char *ram_data, unsigned int len) +{ + int i = 0; + + for (i = 0; i < len; i++) { + if (ram_data[i] != cont_data[i]) { + aw_dev_err("%s: check ramdata error, addr=0x%04x, ram_data=0x%02x, file_data=0x%02x\n", + __func__, i, ram_data[i], cont_data[i]); + return -ERANGE; + } + } + return 0; +} +#endif + +static void aw8624_set_ram_addr(struct aw8624 *aw8624) +{ + unsigned char ram_addr[2] = {0}; + unsigned int base_addr = aw8624->ram.base_addr; + + ram_addr[0] = (unsigned char)AW_SET_RAMADDR_H(base_addr); + ram_addr[1] = (unsigned char)AW_SET_RAMADDR_L(base_addr); + + aw8624_i2c_write(aw8624, AW8624_REG_RAMADDRH, ram_addr, + AW_I2C_BYTE_TWO); +} + +static int aw8624_container_update(struct aw8624 *aw8624, + struct aw8624_container *aw8624_cont) +{ + unsigned char reg_array[4] = {0}; + unsigned int shift = 0; + unsigned int base_addr = 0; + int i = 0; + int ret = 0; + int len = 0; +#ifdef AW_CHECK_RAM_DATA + unsigned char ram_data[AW_RAMDATA_RD_BUFFER_SIZE] = {0}; +#endif + mutex_lock(&aw8624->lock); + + aw8624->ram.baseaddr_shift = 2; + aw8624->ram.ram_shift = 4; + + /* RAMINIT Enable */ + aw8624_haptic_raminit(aw8624, true); + + /* set base addr */ + shift = aw8624->ram.baseaddr_shift; + aw8624->ram.base_addr = (unsigned int)((aw8624_cont->data[0+shift]<<8) | + (aw8624_cont->data[1+shift])); + base_addr = aw8624->ram.base_addr; + reg_array[0] = (unsigned char)AW_SET_BASEADDR_H(base_addr); + reg_array[1] = (unsigned char)AW_SET_BASEADDR_L(base_addr); + aw8624_i2c_write(aw8624, AW8624_REG_BASE_ADDRH, reg_array, + AW_I2C_BYTE_TWO); + aw_dev_info("%s: base_addr=0x%04x\n", __func__, + aw8624->ram.base_addr); + + /* set FIFO_AE and FIFO_AF addr */ + reg_array[0] = (unsigned char)AW8624_SET_AEADDR_H(base_addr); + reg_array[1] = (unsigned char)AW8624_SET_AEADDR_L(base_addr); + reg_array[2] = (unsigned char)AW8624_SET_AFADDR_H(base_addr); + reg_array[3] = (unsigned char)AW8624_SET_AFADDR_L(base_addr); + aw8624_i2c_write(aw8624, AW8624_REG_FIFO_AEH, reg_array, + AW_I2C_BYTE_FOUR); + /* get FIFO_AE and FIFO_AF addr */ + aw8624_i2c_read(aw8624, AW8624_REG_FIFO_AEH, reg_array, + AW_I2C_BYTE_FOUR); + aw_dev_info("%s: almost_empty_threshold = %d\n", __func__, + (unsigned short)((reg_array[0] << 8) | reg_array[1])); + aw_dev_info("%s: almost_empty_threshold = %d\n", __func__, + (unsigned short)((reg_array[2] << 8) | reg_array[3])); + + /* write ram data */ + aw8624_set_ram_addr(aw8624); + i = aw8624->ram.ram_shift; + while (i < aw8624_cont->len) { + if ((aw8624_cont->len - i) < AW_RAMDATA_WR_BUFFER_SIZE) + len = aw8624_cont->len - i; + else + len = AW_RAMDATA_WR_BUFFER_SIZE; + + aw8624_i2c_write(aw8624, AW8624_REG_RAMDATA, + &aw8624_cont->data[i], len); + i += len; + } + + +#ifdef AW_CHECK_RAM_DATA + aw8624_set_ram_addr(aw8624); + i = aw8624->ram.ram_shift; + while (i < aw8624_cont->len) { + if ((aw8624_cont->len - i) < AW_RAMDATA_RD_BUFFER_SIZE) + len = aw8624_cont->len - i; + else + len = AW_RAMDATA_RD_BUFFER_SIZE; + + aw8624_i2c_read(aw8624, AW8624_REG_RAMDATA, ram_data, len); + ret = aw8624_check_ram_data(aw8624, &aw8624_cont->data[i], + ram_data, len); + if (ret < 0) + break; + i += len; + } + if (ret) + aw_dev_err("%s: ram data check sum error\n", __func__); + else + aw_dev_info("%s: ram data check sum pass\n", __func__); + + +#endif + /* RAMINIT Disable */ + aw8624_haptic_raminit(aw8624, false); + + mutex_unlock(&aw8624->lock); + aw_dev_info("%s exit\n", __func__); + return ret; +} + +static int aw8624_haptic_get_ram_number(struct aw8624 *aw8624) +{ + unsigned char ram_data[3]; + unsigned int first_wave_addr = 0; + + aw_dev_info("%s enter!\n", __func__); + if (!aw8624->ram_init) { + aw_dev_err("%s: ram init faild, ram_num = 0!\n", __func__); + return -EPERM; + } + + mutex_lock(&aw8624->lock); + /* RAMINIT Enable */ + aw8624_haptic_raminit(aw8624, true); + aw8624_haptic_stop(aw8624); + aw8624_set_ram_addr(aw8624); + + aw8624_i2c_read(aw8624, AW8624_REG_RAMDATA, ram_data, + AW_I2C_BYTE_THREE); + first_wave_addr = (ram_data[1] << 8 | ram_data[2]); + aw8624->ram.ram_num = + (first_wave_addr - aw8624->ram.base_addr - 1) / 4; + aw_dev_info("%s: ram_version = 0x%02x\n", __func__, ram_data[0]); + aw_dev_info("%s: first waveform addr = 0x%04x\n", __func__, + first_wave_addr); + aw_dev_info("%s: ram_num = %d\n", __func__, aw8624->ram.ram_num); + /* RAMINIT Disable */ + aw8624_haptic_raminit(aw8624, false); + mutex_unlock(&aw8624->lock); + + return 0; +} + +static void aw8624_ram_check(const struct firmware *cont, void *context) +{ + struct aw8624 *aw8624 = context; + struct aw8624_container *aw8624_fw; + unsigned short check_sum = 0; + int i = 0; + int ret = 0; +#ifdef AW_READ_BIN_FLEXBALLY + static unsigned char load_cont; + int ram_timer_val = 1000; + + load_cont++; +#endif + aw_dev_dbg("%s enter\n", __func__); + if (!cont) { + aw_dev_err("%s: failed to read %s\n", __func__, + aw8624_ram_name); + release_firmware(cont); +#ifdef AW_READ_BIN_FLEXBALLY + if (load_cont <= 20) { + schedule_delayed_work(&aw8624->ram_work, + msecs_to_jiffies(ram_timer_val)); + aw_dev_info("%s:start hrtimer: load_cont=%d\n", + __func__, load_cont); + } +#endif + return; + } + + aw_dev_info("%s: loaded %s - size: %zu\n", __func__, aw8624_ram_name, + cont ? cont->size : 0); + + /* check sum */ + for (i = 2; i < cont->size; i++) + check_sum += cont->data[i]; + + if (check_sum == (unsigned short)((cont->data[0]<<8)|(cont->data[1]))) { + aw_dev_info("%s: check sum pass : 0x%04x\n", + __func__, check_sum); + aw8624->ram.check_sum = check_sum; + } else { + aw_dev_err("%s: check sum err: check_sum=0x%04x\n", + __func__, check_sum); + return; + } + + /* aw8624 ram update */ + aw8624_fw = kzalloc(cont->size+sizeof(int), GFP_KERNEL); + if (!aw8624_fw) { + release_firmware(cont); + aw_dev_err("%s: Error allocating memory\n", __func__); + return; + } + aw8624_fw->len = cont->size; + memcpy(aw8624_fw->data, cont->data, cont->size); + release_firmware(cont); + + ret = aw8624_container_update(aw8624, aw8624_fw); + if (ret) { + kfree(aw8624_fw); + aw8624->ram.len = 0; + aw_dev_err("%s: ram firmware update failed!\n", __func__); + } else { + aw8624->ram_init = 1; + aw8624->ram.len = aw8624_fw->len; + kfree(aw8624_fw); + aw_dev_info("%s: ram firmware update complete\n", __func__); + } + aw8624_haptic_get_ram_number(aw8624); + +} + +static int aw8624_ram_update(struct aw8624 *aw8624) +{ + aw8624->ram_init = 0; + aw8624->rtp_init = 0; + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw8624_ram_name, aw8624->dev, GFP_KERNEL, + aw8624, aw8624_ram_check); +} + +static void aw8624_ram_work_routine(struct work_struct *work) +{ + struct aw8624 *aw8624 = + container_of(work, struct aw8624, ram_work.work); + + aw8624_ram_update(aw8624); + +} + +int aw8624_ram_init(struct aw8624 *aw8624) +{ + int ram_timer_val = 8000; + + aw_dev_dbg("%s enter\n", __func__); + INIT_DELAYED_WORK(&aw8624->ram_work, aw8624_ram_work_routine); + schedule_delayed_work(&aw8624->ram_work, + msecs_to_jiffies(ram_timer_val)); + + return 0; +} + +/***************************************************** + * + * haptic control + * + *****************************************************/ + +static int aw8624_haptic_play_init(struct aw8624 *aw8624) +{ + unsigned char reg_val = 0; + + if (aw8624->play_mode == AW8624_HAPTIC_CONT_MODE) + reg_val = (unsigned char)(aw8624_dts_data.aw8624_sw_brake[0]); + else + reg_val = (unsigned char)(aw8624_dts_data.aw8624_sw_brake[1]); + + aw8624_i2c_write(aw8624, + AW8624_REG_SW_BRAKE, ®_val, AW_I2C_BYTE_ONE); + return 0; +} + +static int aw8624_haptic_active(struct aw8624 *aw8624) +{ + aw8624_haptic_play_init(aw8624); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_WORK_MODE_MASK, + AW8624_BIT_SYSCTRL_ACTIVE); + aw8624_interrupt_clear(aw8624); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSINTM, + AW8624_BIT_SYSINTM_UVLO_MASK, + AW8624_BIT_SYSINTM_UVLO_EN); + return 0; +} + +static int aw8624_haptic_play_mode(struct aw8624 *aw8624, + unsigned char play_mode) +{ + switch (play_mode) { + case AW8624_HAPTIC_STANDBY_MODE: + aw_dev_info("%s: enter standby mode\n", __func__); + aw8624->play_mode = AW8624_HAPTIC_STANDBY_MODE; + aw8624_i2c_write_bits(aw8624, AW8624_REG_SYSINTM, + AW8624_BIT_SYSINTM_UVLO_MASK, + AW8624_BIT_SYSINTM_UVLO_OFF); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_WORK_MODE_MASK, + AW8624_BIT_SYSCTRL_STANDBY); + break; + case AW8624_HAPTIC_RAM_MODE: + aw_dev_info("%s: enter ram mode\n", __func__); + aw8624->play_mode = AW8624_HAPTIC_RAM_MODE; + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8624_BIT_SYSCTRL_PLAY_MODE_RAM); + aw8624_haptic_active(aw8624); + break; + case AW8624_HAPTIC_RAM_LOOP_MODE: + aw_dev_info("%s: enter ram loop mode\n", __func__); + aw8624->play_mode = AW8624_HAPTIC_RAM_LOOP_MODE; + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8624_BIT_SYSCTRL_PLAY_MODE_RAM); + aw8624_haptic_active(aw8624); + break; + case AW8624_HAPTIC_RTP_MODE: + aw_dev_info("%s: enter rtp mode\n", __func__); + aw8624->play_mode = AW8624_HAPTIC_RTP_MODE; + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8624_BIT_SYSCTRL_PLAY_MODE_RTP); + aw8624_haptic_active(aw8624); + break; + case AW8624_HAPTIC_TRIG_MODE: + aw_dev_info("%s: enter trig mode\n", __func__); + aw8624->play_mode = AW8624_HAPTIC_TRIG_MODE; + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8624_BIT_SYSCTRL_PLAY_MODE_RAM); + aw8624_haptic_active(aw8624); + break; + case AW8624_HAPTIC_CONT_MODE: + aw_dev_info("%s: enter cont mode\n", __func__); + aw8624->play_mode = AW8624_HAPTIC_CONT_MODE; + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8624_BIT_SYSCTRL_PLAY_MODE_CONT); + aw8624_haptic_active(aw8624); + break; + default: + aw_dev_err("%s: play mode %d err", __func__, play_mode); + break; + } + return 0; +} + +static int aw8624_haptic_play_go(struct aw8624 *aw8624, bool flag) +{ + aw_dev_dbg("%s enter, flag = %d\n", __func__, flag); + if (!flag) { + aw8624->current_t = ktime_get(); + aw8624->interval_us = ktime_to_us(ktime_sub(aw8624->current_t, + aw8624->pre_enter_t)); + if (aw8624->interval_us < 2000) { + aw_dev_info("%s:aw8624->interval_us=%d\n", + __func__, aw8624->interval_us); + mdelay(2); + } + } + if (flag == true) { + aw8624_i2c_write_bits(aw8624, AW8624_REG_GO, + AW8624_BIT_GO_MASK, AW8624_BIT_GO_ENABLE); + aw8624->pre_enter_t = ktime_get(); + } else { + aw8624_i2c_write_bits(aw8624, AW8624_REG_GO, + AW8624_BIT_GO_MASK, AW8624_BIT_GO_DISABLE); + } + return 0; +} + +static int aw8624_haptic_stop_delay(struct aw8624 *aw8624) +{ + unsigned char reg_val = 0; + unsigned int cnt = 100; + + while (cnt--) { + aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, ®_val, + AW_I2C_BYTE_ONE); + if ((reg_val&0x0f) == AW8624_BIT_GLBRD5_STATE_STANDBY) { + aw_dev_info("%s enter standby, reg glb_state=0x%02x\n", + __func__, reg_val); + return 0; + } + mdelay(2); + + aw_dev_dbg("%s wait for standby, reg glb_state=0x%02x\n", + __func__, reg_val); + } + aw_dev_err("%s do not enter standby automatically\n", __func__); + return 0; +} + +static int aw8624_haptic_stop(struct aw8624 *aw8624) +{ + aw_dev_dbg("%s enter\n", __func__); + aw8624_haptic_play_go(aw8624, false); + aw8624_haptic_stop_delay(aw8624); + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_STANDBY_MODE); + + return 0; +} + +static int aw8624_haptic_start(struct aw8624 *aw8624) +{ + aw8624_haptic_active(aw8624); + aw8624_haptic_play_go(aw8624, true); + + return 0; +} + +static int aw8624_haptic_set_wav_seq(struct aw8624 *aw8624, + unsigned char wav, unsigned char seq) +{ + aw8624_i2c_write(aw8624, AW8624_REG_WAVSEQ1+wav, &seq, AW_I2C_BYTE_ONE); + return 0; +} + +static int aw8624_haptic_set_wav_loop(struct aw8624 *aw8624, + unsigned char wav, unsigned char loop) +{ + unsigned char tmp = 0; + + if (wav%2) { + tmp = loop<<0; + aw8624_i2c_write_bits(aw8624, AW8624_REG_WAVLOOP1+(wav/2), + AW8624_BIT_WAVLOOP_SEQNP1_MASK, tmp); + } else { + tmp = loop<<4; + aw8624_i2c_write_bits(aw8624, AW8624_REG_WAVLOOP1+(wav/2), + AW8624_BIT_WAVLOOP_SEQN_MASK, tmp); + } + + return 0; +} + +static int +aw8624_haptic_set_repeat_wav_seq(struct aw8624 *aw8624, unsigned char seq) +{ + aw8624_haptic_set_wav_seq(aw8624, 0x00, seq); + aw8624_haptic_set_wav_seq(aw8624, 0x01, 0x00); + aw8624_haptic_set_wav_loop(aw8624, 0x00, + AW8624_BIT_WAVLOOP_INIFINITELY); + + return 0; +} + +static int aw8624_haptic_set_gain(struct aw8624 *aw8624, unsigned char gain) +{ + aw8624_i2c_write(aw8624, AW8624_REG_DATDBG, &gain, AW_I2C_BYTE_ONE); + return 0; +} + +static int aw8624_haptic_set_pwm(struct aw8624 *aw8624, unsigned char mode) +{ + switch (mode) { + case AW8624_PWM_48K: + aw8624_i2c_write_bits(aw8624, + AW8624_REG_PWMDBG, + AW8624_BIT_PWMDBG_PWM_MODE_MASK, + AW8624_BIT_PWMDBG_PWM_48K); + break; + case AW8624_PWM_24K: + aw8624_i2c_write_bits(aw8624, + AW8624_REG_PWMDBG, + AW8624_BIT_PWMDBG_PWM_MODE_MASK, + AW8624_BIT_PWMDBG_PWM_24K); + break; + case AW8624_PWM_12K: + aw8624_i2c_write_bits(aw8624, + AW8624_REG_PWMDBG, + AW8624_BIT_PWMDBG_PWM_MODE_MASK, + AW8624_BIT_PWMDBG_PWM_12K); + break; + default: + break; + } + return 0; +} + +static int aw8624_haptic_play_wav_seq(struct aw8624 *aw8624, + unsigned char flag) +{ + aw_dev_dbg("%s enter\n", __func__); + if (flag) { + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_RAM_MODE); + aw8624_haptic_start(aw8624); + } + return 0; +} + + +static int aw8624_haptic_swicth_motorprotect_config(struct aw8624 *aw8624, + unsigned char addr, unsigned char val) +{ + if (addr == 1) { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DETCTRL, + AW8624_BIT_DETCTRL_PROTECT_MASK, + AW8624_BIT_DETCTRL_PROTECT_SHUTDOWN); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_PWMPRC, + AW8624_BIT_PWMPRC_PRC_EN_MASK, + AW8624_BIT_PWMPRC_PRC_ENABLE); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_PRLVL, + AW8624_BIT_PRLVL_PR_EN_MASK, + AW8624_BIT_PRLVL_PR_ENABLE); + } else if (addr == 0) { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DETCTRL, + AW8624_BIT_DETCTRL_PROTECT_MASK, + AW8624_BIT_DETCTRL_PROTECT_NO_ACTION); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_PWMPRC, + AW8624_BIT_PWMPRC_PRC_EN_MASK, + AW8624_BIT_PWMPRC_PRC_DISABLE); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_PRLVL, + AW8624_BIT_PRLVL_PR_EN_MASK, + AW8624_BIT_PRLVL_PR_DISABLE); + } else if (addr == 0x2d) { + aw8624_i2c_write_bits(aw8624, AW8624_REG_PWMPRC, + AW8624_BIT_PWMPRC_PRCTIME_MASK, val); + } else if (addr == 0x3e) { + aw8624_i2c_write_bits(aw8624, AW8624_REG_PRLVL, + AW8624_BIT_PRLVL_PRLVL_MASK, val); + } else if (addr == 0x3f) { + aw8624_i2c_write_bits(aw8624, AW8624_REG_PRTIME, + AW8624_BIT_PRTIME_PRTIME_MASK, val); + } else { + /*nothing to do;*/ + } + + return 0; +} + +static int aw8624_haptic_ram_config(struct aw8624 *aw8624, int duration) +{ + int ret = 0; + + if (aw8624->duration_time_flag < 0) { + aw_dev_err("%s: duration time error, array size = %d\n", + __func__, aw8624->duration_time_size); + return -ERANGE; + } + ret = aw8624_analyse_duration_range(aw8624); + if (ret < 0) + return ret; + if ((duration > 0) && (duration < + aw8624_dts_data.aw8624_duration_time[0])) { + aw8624->index = 3; /*3*/ + aw8624->activate_mode = AW8624_HAPTIC_RAM_MODE; + } else if ((duration >= aw8624_dts_data.aw8624_duration_time[0]) && + (duration < aw8624_dts_data.aw8624_duration_time[1])) { + aw8624->index = 2; /*2*/ + aw8624->activate_mode = AW8624_HAPTIC_RAM_MODE; + } else if ((duration >= aw8624_dts_data.aw8624_duration_time[1]) && + (duration < aw8624_dts_data.aw8624_duration_time[2])) { + aw8624->index = 1; /*1*/ + aw8624->activate_mode = AW8624_HAPTIC_RAM_MODE; + } else if (duration >= aw8624_dts_data.aw8624_duration_time[2]) { + aw8624->index = 4; /*4*/ + aw8624->activate_mode = AW8624_HAPTIC_RAM_LOOP_MODE; + } else { + aw_dev_err("%s: duration time error, duration= %d\n", + __func__, duration); + aw8624->index = 0; + aw8624->activate_mode = AW8624_HAPTIC_NULL; + ret = -ERANGE; + } + + return ret; +} + +static int aw8624_haptic_select_pin(struct aw8624 *aw8624, unsigned char pin) +{ + if (pin == AW8624_TRIG1) { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DBGCTRL, + AW8624_BIT_DBGCTRL_INTN_TRG_SEL_MASK, + AW8624_BIT_DBGCTRL_TRG_SEL_ENABLE); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_TRG_CFG2, + AW8624_BIT_TRGCFG2_TRG1_ENABLE_MASK, + AW8624_BIT_TRGCFG2_TRG1_ENABLE); + aw_dev_info("%s: select TRIG1 pin\n", __func__); + } else if (pin == AW8624_IRQ) { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DBGCTRL, + AW8624_BIT_DBGCTRL_INTN_TRG_SEL_MASK, + AW8624_BIT_DBGCTRL_INTN_SEL_ENABLE); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_TRG_CFG2, + AW8624_BIT_TRGCFG2_TRG1_ENABLE_MASK, + AW8624_BIT_TRGCFG2_TRG1_DISABLE); + aw_dev_info("%s: select INIT pin\n", __func__); + } else + aw_dev_err("%s: There is no such option\n", __func__); + return 0; +} +static int aw8624_haptic_trig1_param_init(struct aw8624 *aw8624) +{ + if (aw8624->IsUsedIRQ) { + aw8624_haptic_select_pin(aw8624, AW8624_IRQ); + return 0; + } + aw8624->trig.trig_enable = aw8624_dts_data.trig_config[0]; + aw8624->trig.trig_edge = aw8624_dts_data.trig_config[1]; + aw8624->trig.trig_polar = aw8624_dts_data.trig_config[2]; + aw8624->trig.pos_sequence = aw8624_dts_data.trig_config[3]; + aw8624->trig.neg_sequence = aw8624_dts_data.trig_config[4]; + aw_dev_info("%s: trig1 date init ok!\n", __func__); + return 0; +} +static int aw8624_haptic_tirg1_param_config(struct aw8624 *aw8624) +{ + if (aw8624->IsUsedIRQ) { + aw8624_haptic_select_pin(aw8624, AW8624_IRQ); + return 0; + } + if (aw8624->trig.trig_enable) + aw8624_haptic_select_pin(aw8624, AW8624_TRIG1); + else + aw8624_haptic_select_pin(aw8624, AW8624_IRQ); + + aw8624_i2c_write_bits(aw8624, AW8624_REG_TRG_CFG1, + AW8624_BIT_TRGCFG1_TRG1_EDGE_MASK, + aw8624->trig.trig_edge); + aw8624_i2c_write_bits(aw8624, AW8624_REG_TRG_CFG1, + AW8624_BIT_TRGCFG1_TRG1_POLAR_MASK, + aw8624->trig.trig_polar << 1); + aw8624_i2c_write(aw8624, AW8624_REG_TRG1_SEQP, + &aw8624->trig.pos_sequence, AW_I2C_BYTE_ONE); + aw8624_i2c_write(aw8624, AW8624_REG_TRG1_SEQN, + &aw8624->trig.neg_sequence, AW_I2C_BYTE_ONE); + return 0; +} +static int aw8624_haptic_vbat_mode(struct aw8624 *aw8624, unsigned char flag) +{ + if (flag == AW8624_HAPTIC_VBAT_HW_COMP_MODE) { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_ADCTEST, + AW8624_BIT_DETCTRL_VBAT_MODE_MASK, + AW8624_BIT_DETCTRL_VBAT_HW_COMP); + } else { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_ADCTEST, + AW8624_BIT_DETCTRL_VBAT_MODE_MASK, + AW8624_BIT_DETCTRL_VBAT_SW_COMP); + } + return 0; +} + +static int aw8624_haptic_set_f0_preset(struct aw8624 *aw8624, unsigned int f0_pre) +{ + unsigned int f0_reg = 0; + unsigned char reg_array[2] = {0}; + + f0_reg = 1000000000 / (f0_pre * aw8624_dts_data.aw8624_f0_coeff); + reg_array[0] = (unsigned char)((f0_reg >> 8) & 0xff); + reg_array[1] = (unsigned char)((f0_reg >> 0) & 0xff); + + aw8624_i2c_write(aw8624, AW8624_REG_F_PRE_H, reg_array, + AW_I2C_BYTE_TWO); + return 0; +} + +static int aw8624_haptic_read_f0(struct aw8624 *aw8624) +{ + unsigned char reg_val[2] = {0}; + unsigned int f0_reg = 0; + unsigned long f0_tmp = 0; + + aw8624_i2c_read(aw8624, AW8624_REG_F_LRA_F0_H, reg_val, + AW_I2C_BYTE_TWO); + f0_reg = (reg_val[0] << 8) | reg_val[1]; + + if (!f0_reg || !aw8624_dts_data.aw8624_f0_coeff) { + aw8624->f0 = 0; + aw_dev_info("%s : get f0 failed with the value becoming 0!\n", + __func__); + return -EPERM; + } + + f0_tmp = AW8624_F0_FORMULA(f0_reg, aw8624_dts_data.aw8624_f0_coeff); + aw8624->f0 = (unsigned int)f0_tmp; + aw_dev_info("%s f0=%d\n", __func__, aw8624->f0); + return 0; +} + +static int aw8624_haptic_read_cont_f0(struct aw8624 *aw8624) +{ + unsigned char reg_val[2] = {0}; + unsigned int f0_reg = 0; + unsigned long f0_tmp = 0; + + aw8624_i2c_read(aw8624, AW8624_REG_F_LRA_CONT_H, reg_val, + AW_I2C_BYTE_TWO); + f0_reg = (reg_val[0] << 8) | reg_val[1]; + + if (!f0_reg) { + aw8624->cont_f0 = 0; + aw_dev_info("%s: failed to reading cont f0 with 0\n", __func__); + return 0; + } + + f0_tmp = AW8624_F0_FORMULA(f0_reg, aw8624_dts_data.aw8624_f0_coeff); + aw8624->cont_f0 = (unsigned int)f0_tmp; + aw_dev_info("%s cont_f0=%d\n", __func__, aw8624->cont_f0); + return 0; +} + +static int aw8624_haptic_read_beme(struct aw8624 *aw8624) +{ + unsigned char reg_val[2] = {0}; + + aw8624_i2c_read(aw8624, AW8624_REG_WAIT_VOL_MP, reg_val, + AW_I2C_BYTE_TWO); + aw8624->max_pos_beme = reg_val[0]; + aw8624->max_neg_beme = reg_val[1]; + + aw_dev_info("%s max_pos_beme=%d\n", __func__, aw8624->max_pos_beme); + aw_dev_info("%s max_neg_beme=%d\n", __func__, aw8624->max_neg_beme); + + return 0; +} + +static int aw8624_vbat_monitor_detector(struct aw8624 *aw8624) +{ + unsigned char reg_val = 0; + unsigned int vbat = 0; + + aw8624_haptic_stop(aw8624); + /*step 1:EN_RAMINIT*/ + aw8624_haptic_raminit(aw8624, true); + + /*step 2 :launch power supply testing */ + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DETCTRL, + AW8624_BIT_DETCTRL_VBAT_GO_MASK, + AW8624_BIT_DETCTRL_VABT_GO_ENABLE); + usleep_range(AW8624_VBAT_DELAY_MIN, AW8624_VBAT_DELAY_MAX); + + aw8624_i2c_read(aw8624, AW8624_REG_VBATDET, ®_val, + AW_I2C_BYTE_ONE); + vbat = AW8624_VBAT_FORMULA(reg_val); + aw_dev_info("%s get_vbat=%dmV\n", __func__, vbat); + /*step 3: return val*/ + aw8624_haptic_raminit(aw8624, false); + + return vbat; +} + +static int aw8624_lra_resistance_detector(struct aw8624 *aw8624) +{ + unsigned char reg_val = 0; + unsigned char reg_val_anactrl = 0; + unsigned char reg_val_d2scfg = 0; + unsigned int r_lra = 0; + + mutex_lock(&aw8624->lock); + aw8624_i2c_read(aw8624, AW8624_REG_ANACTRL, ®_val_anactrl, + AW_I2C_BYTE_ONE); + aw8624_i2c_read(aw8624, AW8624_REG_D2SCFG, ®_val_d2scfg, + AW_I2C_BYTE_ONE); + aw8624_haptic_stop(aw8624); + aw8624_haptic_raminit(aw8624, true); + + + aw8624_i2c_write_bits(aw8624, + AW8624_REG_ANACTRL, + AW8624_BIT_ANACTRL_EN_IO_PD1_MASK, + AW8624_BIT_ANACTRL_EN_IO_PD1_HIGH); + + aw8624_i2c_write_bits(aw8624, + AW8624_REG_D2SCFG, + AW8624_BIT_D2SCFG_CLK_ADC_MASK, + AW8624_BIT_D2SCFG_CLK_ASC_1P5MHZ); + + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DETCTRL, + (AW8624_BIT_DETCTRL_RL_OS_MASK & + AW8624_BIT_DETCTRL_DIAG_GO_MASK), + (AW8624_BIT_DETCTRL_RL_DETECT | + AW8624_BIT_DETCTRL_DIAG_GO_ENABLE)); + usleep_range(AW8624_LRA_DELAY_MIN, AW8624_LRA_DELAY_MAX); + aw8624_i2c_read(aw8624, AW8624_REG_RLDET, ®_val, + AW_I2C_BYTE_ONE); + r_lra = AW8624_LRA_FORMULA(reg_val); + + aw8624_i2c_write(aw8624, AW8624_REG_D2SCFG, ®_val_d2scfg, + AW_I2C_BYTE_ONE); + aw8624_i2c_write(aw8624, AW8624_REG_ANACTRL, ®_val_anactrl, + AW_I2C_BYTE_ONE); + aw8624_haptic_raminit(aw8624, false); + mutex_unlock(&aw8624->lock); + + return r_lra; +} + +static int aw8624_haptic_ram_vbat_comp(struct aw8624 *aw8624, bool flag) +{ + int temp_gain = 0; + int vbat = 0; + + if (flag) { + if (aw8624->ram_vbat_comp == + AW8624_HAPTIC_RAM_VBAT_COMP_ENABLE) { + vbat = aw8624_vbat_monitor_detector(aw8624); + temp_gain = aw8624->gain * AW8624_VBAT_REFER / vbat; + if + (temp_gain > (128*AW8624_VBAT_REFER/AW8624_VBAT_MIN)) { + temp_gain = + 128*AW8624_VBAT_REFER/AW8624_VBAT_MIN; + aw_dev_dbg("%s gain limit=%d\n", __func__, + temp_gain); + } + aw8624_haptic_set_gain(aw8624, temp_gain); + } else { + aw8624_haptic_set_gain(aw8624, aw8624->gain); + } + } else { + aw8624_haptic_set_gain(aw8624, aw8624->gain); + } + + return 0; +} + +void aw8624_haptic_set_rtp_aei(struct aw8624 *aw8624, bool flag) +{ + if (flag) { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSINTM, + AW8624_BIT_SYSINTM_FF_AE_MASK, + AW8624_BIT_SYSINTM_FF_AE_EN); + } else { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSINTM, + AW8624_BIT_SYSINTM_FF_AE_MASK, + AW8624_BIT_SYSINTM_FF_AE_OFF); + } +} + +static unsigned char aw8624_haptic_rtp_get_fifo_afi(struct aw8624 *aw8624) +{ + unsigned char ret = 0; + unsigned char reg_val = 0; + + if (aw8624->osc_cali_flag == 1) { + aw8624_i2c_read(aw8624, AW8624_REG_SYSST, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= AW8624_BIT_SYSST_FF_AFS; + ret = reg_val >> 3; + } else { + aw8624_i2c_read(aw8624, AW8624_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= AW8624_BIT_SYSINT_FF_AFI; + ret = reg_val >> 3; + } + return ret; +} + +unsigned char aw8624_haptic_rtp_get_fifo_afs(struct aw8624 *aw8624) +{ + unsigned char ret = 0; + unsigned char reg_val = 0; + + aw8624_i2c_read(aw8624, AW8624_REG_SYSST, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= AW8624_BIT_SYSST_FF_AFS; + ret = reg_val >> 3; + + return ret; +} + +static int aw8624_haptic_rtp_init(struct aw8624 *aw8624) +{ + unsigned int buf_len = 0; + unsigned char glb_st = 0; + struct aw8624_container *rtp_container = aw8624->rtp_container; + + aw_dev_dbg("%s enter\n", __func__); + aw8624->rtp_cnt = 0; + mutex_lock(&aw8624->rtp_lock); + while ((!aw8624_haptic_rtp_get_fifo_afs(aw8624)) && + (aw8624->play_mode == AW8624_HAPTIC_RTP_MODE)) { +#ifdef AW_ENABLE_RTP_PRINT_LOG + aw_dev_info("%s rtp cnt = %d\n", __func__, aw8624->rtp_cnt); +#endif + if (!rtp_container) { + aw_dev_info("%s:aw8624_rtp is null break\n", __func__); + break; + } + + if (aw8624->rtp_cnt < aw8624->ram.base_addr) { + if ((rtp_container->len-aw8624->rtp_cnt) < + (aw8624->ram.base_addr)) { + buf_len = rtp_container->len - aw8624->rtp_cnt; + } else { + buf_len = (aw8624->ram.base_addr); + } + } else if ((rtp_container->len - aw8624->rtp_cnt) < + (aw8624->ram.base_addr >> 2)) { + buf_len = rtp_container->len - aw8624->rtp_cnt; + } else { + buf_len = (aw8624->ram.base_addr >> 2); + } +#ifdef AW_ENABLE_RTP_PRINT_LOG + aw_dev_info("%s buf_len = %d\n", __func__, buf_len); +#endif + aw8624_i2c_write(aw8624, AW8624_REG_RTP_DATA, + &rtp_container->data[aw8624->rtp_cnt], + buf_len); + + aw8624->rtp_cnt += buf_len; + aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, &glb_st, + AW_I2C_BYTE_ONE); + if (aw8624->rtp_cnt == rtp_container->len || + (glb_st == AW8624_BIT_GLBRD5_STATE_STANDBY)) { + if (aw8624->rtp_cnt == rtp_container->len) + aw_dev_info("%s: rtp load completely!\n", + __func__); + else + aw_dev_err("%s rtp load failed!!\n", __func__); + aw8624->rtp_cnt = 0; + mutex_unlock(&aw8624->rtp_lock); + return 0; + } + } + + if (aw8624->play_mode == AW8624_HAPTIC_RTP_MODE) + aw8624_haptic_set_rtp_aei(aw8624, true); + + aw_dev_dbg("%s exit\n", __func__); + mutex_unlock(&aw8624->rtp_lock); + return 0; +} + +static void aw8624_rtp_work_routine(struct work_struct *work) +{ + const struct firmware *rtp_file; + int ret = -1; + unsigned char reg_val = 0; + bool rtp_work_flag = false; + unsigned int cnt = 200; + + struct aw8624 *aw8624 = container_of(work, struct aw8624, rtp_work); + + /* fw loaded */ + aw_dev_dbg("%s enter\n", __func__); + mutex_lock(&aw8624->rtp_lock); + ret = request_firmware(&rtp_file, + aw8624_rtp_name[aw8624->rtp_file_num], aw8624->dev); + if (ret < 0) { + aw_dev_err("%s: failed to read %s\n", __func__, + aw8624_rtp_name[aw8624->rtp_file_num]); + mutex_unlock(&aw8624->rtp_lock); + return; + } + aw8624->rtp_init = 0; + vfree(aw8624->rtp_container); + aw8624->rtp_container = vmalloc(rtp_file->size+sizeof(int)); + if (!aw8624->rtp_container) { + release_firmware(rtp_file); + aw_dev_err("%s: error allocating memory\n", __func__); + mutex_unlock(&aw8624->rtp_lock); + return; + } + aw8624->rtp_container->len = rtp_file->size; + aw_dev_info("%s: rtp file [%s] size = %d\n", __func__, + aw8624_rtp_name[aw8624->rtp_file_num], + aw8624->rtp_container->len); + memcpy(aw8624->rtp_container->data, rtp_file->data, rtp_file->size); + mutex_unlock(&aw8624->rtp_lock); + release_firmware(rtp_file); + mutex_lock(&aw8624->lock); + aw8624->rtp_init = 1; + if (aw8624->IsUsedIRQ) + aw8624_haptic_select_pin(aw8624, AW8624_IRQ); + aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_RTP_CALI_LRA); + /* rtp mode config */ + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_RTP_MODE); + + /* haptic start */ + aw8624_haptic_start(aw8624); + mutex_unlock(&aw8624->lock); + usleep_range(AW8624_STOP_DELAY_MIN, AW8624_STOP_DELAY_MAX); + while (cnt) { + aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, ®_val, + AW_I2C_BYTE_ONE); + if (reg_val == AW8624_BIT_GLBRD5_STATE_RTP_GO) { + cnt = 0; + rtp_work_flag = true; + aw_dev_info("%s RTP_GO! glb_state=0x08\n", __func__); + } else { + cnt--; + aw_dev_dbg("%s wait for RTP_GO, glb_state=0x%02X\n", + __func__, reg_val); + } + usleep_range(AW8624_STOP_DELAY_MIN, AW8624_STOP_DELAY_MAX); + } + if (rtp_work_flag) { + aw8624_haptic_rtp_init(aw8624); + } else { + /* enter standby mode */ + aw8624_haptic_stop(aw8624); + aw_dev_err("%s failed to enter RTP_GO status!\n", + __func__); + } + +} + +/***************************************************** + * + * haptic - audio + * + *****************************************************/ +static enum hrtimer_restart +aw8624_haptic_audio_timer_func(struct hrtimer *timer) +{ + struct aw8624 *aw8624 = + container_of(timer, struct aw8624, haptic_audio.timer); + + schedule_work(&aw8624->haptic_audio.work); + hrtimer_start(&aw8624->haptic_audio.timer, + ktime_set(aw8624->haptic_audio.timer_val/1000, + (aw8624->haptic_audio.timer_val%1000)*1000000), + HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +static void aw8624_ctr_list_config(struct aw8624 *aw8624, + struct haptic_ctr *p_ctr, + struct haptic_ctr *p_ctr_bak, + struct haptic_audio *haptic_audio) +{ + unsigned int list_input_cnt = 0; + unsigned int list_output_cnt = 0; + unsigned int list_diff_cnt = 0; + unsigned int list_del_cnt = 0; + + list_for_each_entry_safe(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + list_input_cnt = p_ctr->cnt; + break; + } + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + list_output_cnt = p_ctr->cnt; + break; + } + if (list_input_cnt > list_output_cnt) + list_diff_cnt = list_input_cnt - list_output_cnt; + + if (list_input_cnt < list_output_cnt) + list_diff_cnt = 32 + list_input_cnt - list_output_cnt; + + if (list_diff_cnt > 2) { + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + if ((p_ctr->play == 0) && + (AW8624_HAPTIC_CMD_ENABLE == + (AW8624_HAPTIC_CMD_HAPTIC & p_ctr->cmd))) { + list_del(&p_ctr->list); + kfree(p_ctr); + list_del_cnt++; + } + if (list_del_cnt == list_diff_cnt) + break; + } + } +} + +static void aw8624_haptic_audio_work_routine(struct work_struct *work) +{ + struct aw8624 *aw8624 = + container_of(work, struct aw8624, haptic_audio.work); + struct haptic_audio *haptic_audio = NULL; + struct haptic_ctr *p_ctr = NULL; + struct haptic_ctr *p_ctr_bak = NULL; + unsigned int ctr_list_flag = 0; + + int rtp_is_going_on = 0; + + aw_dev_dbg("%s enter\n", __func__); + haptic_audio = &(aw8624->haptic_audio); + mutex_lock(&aw8624->haptic_audio.lock); + memset(&aw8624->haptic_audio.ctr, 0, sizeof(struct haptic_ctr)); + ctr_list_flag = 0; + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + ctr_list_flag = 1; + break; + } + if (ctr_list_flag == 0) + aw_dev_info("%s: ctr list empty\n", __func__); + if (ctr_list_flag == 1) + aw8624_ctr_list_config(aw8624, p_ctr, p_ctr_bak, haptic_audio); + + /* get the last data from list */ + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + aw8624->haptic_audio.ctr.cnt = p_ctr->cnt; + aw8624->haptic_audio.ctr.cmd = p_ctr->cmd; + aw8624->haptic_audio.ctr.play = p_ctr->play; + aw8624->haptic_audio.ctr.wavseq = p_ctr->wavseq; + aw8624->haptic_audio.ctr.loop = p_ctr->loop; + aw8624->haptic_audio.ctr.gain = p_ctr->gain; + list_del(&p_ctr->list); + kfree(p_ctr); + break; + } + + if (aw8624->haptic_audio.ctr.play) { + aw_dev_info("%s: cnt=%d, cmd=%d, play=%d, wavseq=%d, loop=%d, gain=%d\n", + __func__, + aw8624->haptic_audio.ctr.cnt, + aw8624->haptic_audio.ctr.cmd, + aw8624->haptic_audio.ctr.play, + aw8624->haptic_audio.ctr.wavseq, + aw8624->haptic_audio.ctr.loop, + aw8624->haptic_audio.ctr.gain); + } + + /* rtp mode jump */ + rtp_is_going_on = aw8624_haptic_juge_RTP_is_going_on(aw8624); + if (rtp_is_going_on) { + mutex_unlock(&aw8624->haptic_audio.lock); + return; + } + mutex_unlock(&aw8624->haptic_audio.lock); + + if ((AW8624_HAPTIC_CMD_HAPTIC & aw8624->haptic_audio.ctr.cmd) == + AW8624_HAPTIC_CMD_ENABLE) { + if (aw8624->haptic_audio.ctr.play == + AW8624_HAPTIC_PLAY_ENABLE) { + aw_dev_info("%s: haptic audio play start\n", __func__); + mutex_lock(&aw8624->lock); + aw8624_haptic_stop(aw8624); + + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_RAM_MODE); + + aw8624_haptic_set_wav_seq(aw8624, 0x00, + aw8624->haptic_audio.ctr.wavseq); + + aw8624_haptic_set_wav_loop(aw8624, 0x00, + aw8624->haptic_audio.ctr.loop); + + aw8624_haptic_set_gain(aw8624, + aw8624->haptic_audio.ctr.gain); + + aw8624_haptic_start(aw8624); + mutex_unlock(&aw8624->lock); + } else if (AW8624_HAPTIC_PLAY_STOP == + aw8624->haptic_audio.ctr.play) { + mutex_lock(&aw8624->lock); + aw8624_haptic_stop(aw8624); + mutex_unlock(&aw8624->lock); + + } else if (AW8624_HAPTIC_PLAY_GAIN == + aw8624->haptic_audio.ctr.play) { + mutex_lock(&aw8624->lock); + aw8624_haptic_set_gain(aw8624, + aw8624->haptic_audio.ctr.gain); + mutex_unlock(&aw8624->lock); + } + } +} + +/***************************************************** + * + * haptic cont + * + *****************************************************/ +static int aw8624_haptic_cont(struct aw8624 *aw8624) +{ + unsigned char brake0_level = 0; + unsigned char time_nzc = 0; + unsigned char en_brake1 = 0; + unsigned char brake1_level = 0; + unsigned char en_brake2 = 0; + unsigned char brake2_level = 0; + unsigned char brake2_p_num = 0; + unsigned char brake1_p_num = 0; + unsigned char brake0_p_num = 0; + unsigned char reg_array[4] = {0}; + + aw_dev_dbg("%s enter\n", __func__); + /* work mode */ + aw8624_haptic_active(aw8624); + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_CONT_MODE); + + /* preset f0 */ + if (aw8624->f0 <= 0) + aw8624_haptic_set_f0_preset(aw8624, aw8624_dts_data.aw8624_f0_pre); + else + aw8624_haptic_set_f0_preset(aw8624, aw8624->f0); + + /* lpf */ + aw8624_i2c_write_bits(aw8624, AW8624_REG_DATCTRL, + (AW8624_BIT_DATCTRL_FC_MASK & + AW8624_BIT_DATCTRL_LPF_ENABLE_MASK), + (AW8624_BIT_DATCTRL_FC_1000HZ | + AW8624_BIT_DATCTRL_LPF_ENABLE)); + + /* brake */ + en_brake1 = aw8624_dts_data.aw8624_cont_brake[0][0]; + en_brake2 = aw8624_dts_data.aw8624_cont_brake[0][1]; + brake0_level = aw8624_dts_data.aw8624_cont_brake[0][2]; + brake1_level = aw8624_dts_data.aw8624_cont_brake[0][3]; + brake2_level = aw8624_dts_data.aw8624_cont_brake[0][4]; + brake0_p_num = aw8624_dts_data.aw8624_cont_brake[0][5]; + brake1_p_num = aw8624_dts_data.aw8624_cont_brake[0][6]; + brake2_p_num = aw8624_dts_data.aw8624_cont_brake[0][7]; + + reg_array[0] = brake0_level << 0; + reg_array[1] = (en_brake1 << 7)|(brake1_level << 0); + reg_array[2] = (en_brake2 << 7)|(brake2_level << 0); + reg_array[3] = (brake2_p_num << 6) | (brake1_p_num << 3) | + (brake0_p_num << 0); + aw8624_i2c_write(aw8624, AW8624_REG_BRAKE0_CTRL, reg_array, + AW_I2C_BYTE_FOUR); + + /* cont config */ + reg_array[0] = AW8624_BIT_CONT_CTRL_ZC_DETEC_ENABLE | + AW8624_BIT_CONT_CTRL_WAIT_1PERIOD | + AW8624_BIT_CONT_CTRL_BY_GO_SIGNAL | + AW8624_BIT_CONT_CTRL_CLOSE_PLAYBACK | + AW8624_BIT_CONT_CTRL_F0_DETECT_DISABLE | + AW8624_BIT_CONT_CTRL_O2C_DISABLE; + aw8624_i2c_write(aw8624, AW8624_REG_CONT_CTRL, reg_array, + AW_I2C_BYTE_ONE); + /* TD time */ + reg_array[0] = (unsigned char)(aw8624->cont_td>>8); + reg_array[1] = (unsigned char)(aw8624->cont_td>>0); + aw8624_i2c_write(aw8624, AW8624_REG_TD_H, reg_array, AW_I2C_BYTE_TWO); + + + aw8624_i2c_write_bits(aw8624, AW8624_REG_BEMF_NUM, + AW8624_BIT_BEMF_NUM_BRK_MASK, + aw8624->cont_num_brk); + time_nzc = AW8624_BIT_TIME_NZC_DEF_VAL; + aw8624_i2c_write(aw8624, AW8624_REG_TIME_NZC, &time_nzc, + AW_I2C_BYTE_ONE); + + /* f0 driver level */ + reg_array[0] = aw8624->cont_drv_lvl; + reg_array[1] = aw8624->cont_drv_lvl_ov; + aw8624_i2c_write(aw8624, AW8624_REG_DRV_LVL, reg_array, + AW_I2C_BYTE_TWO); + /* cont play go */ + aw8624_haptic_play_go(aw8624, true); + + return 0; +} + +/***************************************************** + * + * haptic f0 cali + * + *****************************************************/ +static void aw8624_haptic_upload_lra(struct aw8624 *aw8624, unsigned int flag) +{ + unsigned char reg_val = 0x00; + + switch (flag) { + case AW8624_HAPTIC_ZERO: + aw_dev_info("%s write zero to trim_lra!\n", __func__); + reg_val = 0x00; + break; + case AW8624_HAPTIC_F0_CALI_LRA: + aw_dev_info("%s f0_cali_lra=%d\n", __func__, + aw8624->f0_calib_data); + reg_val = (unsigned char)aw8624->f0_calib_data; + break; + case AW8624_HAPTIC_RTP_CALI_LRA: + aw_dev_info("%s rtp_cali_lra=%d\n", __func__, + aw8624->lra_calib_data); + reg_val = (unsigned char)aw8624->lra_calib_data; + break; + default: + break; + } + aw8624_i2c_write(aw8624, AW8624_REG_TRIM_LRA, ®_val, + AW_I2C_BYTE_ONE); +} + +static int aw8624_haptic_get_f0(struct aw8624 *aw8624) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned char reg_array[3] = {0}; + unsigned char f0_pre_num = 0; + unsigned char f0_wait_num = 0; + unsigned char f0_repeat_num = 0; + unsigned char f0_trace_num = 0; + unsigned int t_f0_ms = 0; + unsigned int t_f0_trace_ms = 0; + unsigned char i = 0; + unsigned int f0_cali_cnt = 50; + + + aw_dev_dbg("%s enter\n", __func__); + + /* f0 calibrate work mode */ + aw8624_haptic_stop(aw8624); + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_CONT_MODE); + + + aw8624_i2c_write_bits(aw8624, + AW8624_REG_CONT_CTRL, + (AW8624_BIT_CONT_CTRL_EN_CLOSE_MASK & + AW8624_BIT_CONT_CTRL_F0_DETECT_MASK), + (AW8624_BIT_CONT_CTRL_OPEN_PLAYBACK | + AW8624_BIT_CONT_CTRL_F0_DETECT_ENABLE)); + + /* LPF */ + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DATCTRL, + (AW8624_BIT_DATCTRL_FC_MASK & + AW8624_BIT_DATCTRL_LPF_ENABLE_MASK), + (AW8624_BIT_DATCTRL_FC_1000HZ | + AW8624_BIT_DATCTRL_LPF_ENABLE)); + + /* preset f0 */ + aw8624_haptic_set_f0_preset(aw8624, aw8624->f0_pre); + /* f0 driver level */ + aw8624_i2c_write(aw8624, AW8624_REG_DRV_LVL, &aw8624->cont_drv_lvl, + AW_I2C_BYTE_ONE); + /* f0 trace parameter */ + if (!aw8624->f0_pre) { + aw_dev_info("%s:fail to get t_f0_ms\n", __func__); + return 0; + } + + f0_pre_num = aw8624_dts_data.aw8624_f0_trace_parameter[0]; + f0_wait_num = aw8624_dts_data.aw8624_f0_trace_parameter[1]; + f0_repeat_num = aw8624_dts_data.aw8624_f0_trace_parameter[2]; + f0_trace_num = aw8624_dts_data.aw8624_f0_trace_parameter[3]; + reg_array[0] = (f0_pre_num << 4)|(f0_wait_num << 0); + reg_array[1] = f0_repeat_num << 0; + reg_array[2] = f0_trace_num << 0; + + aw8624_i2c_write(aw8624, AW8624_REG_NUM_F0_1, reg_array, + AW_I2C_BYTE_THREE); + + /* clear aw8624 interrupt */ + ret = aw8624_i2c_read(aw8624, AW8624_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + + /* play go and start f0 calibration */ + aw8624_haptic_play_go(aw8624, true); + + /* f0 trace time */ + t_f0_ms = 1000*10 / aw8624->f0_pre; + t_f0_trace_ms = + t_f0_ms * (f0_pre_num + f0_wait_num + + (f0_trace_num + f0_wait_num) * (f0_repeat_num - 1)); + aw_dev_info("%s: t_f0_trace_ms = %dms\n", __func__, t_f0_trace_ms); + usleep_range(t_f0_trace_ms * 1000, t_f0_trace_ms * 1000 + 500); + + for (i = 0; i < f0_cali_cnt; i++) { + ret = aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, ®_val, + AW_I2C_BYTE_ONE); + /* f0 calibrate done */ + if (reg_val == AW8624_BIT_GLBRD5_STATE_STANDBY) { + aw8624_haptic_read_f0(aw8624); + aw8624_haptic_read_beme(aw8624); + break; + } + usleep_range(AW8624_F0_DELAY_MIN, AW8624_F0_DELAY_MAX); + aw_dev_info("%s f0 cali sleep 10ms,glb_state=0x%x\n", + __func__, reg_val); + } + + if (i == f0_cali_cnt) + ret = -ERANGE; + else + ret = 0; + + aw8624_i2c_write_bits(aw8624, AW8624_REG_CONT_CTRL, + (AW8624_BIT_CONT_CTRL_EN_CLOSE_MASK & + AW8624_BIT_CONT_CTRL_F0_DETECT_MASK), + (AW8624_BIT_CONT_CTRL_CLOSE_PLAYBACK | + AW8624_BIT_CONT_CTRL_F0_DETECT_DISABLE)); + + return ret; +} + +#ifdef AW8624_MUL_GET_F0 +static int aw8624_multiple_get_f0(struct aw8624 *aw8624) +{ + int f0_max = aw8624_dts_data.aw8624_f0_pre + AW8624_MUL_GET_F0_RANGE; + int f0_min = aw8624_dts_data.aw8624_f0_pre - AW8624_MUL_GET_F0_RANGE; + int i = 0; + int ret = 0; + + aw8624->f0_pre = aw8624_dts_data.aw8624_f0_pre; + for (i = 0; i < AW8624_MUL_GET_F0_NUM; i++) { + aw_dev_info("%s aw8624->f0_pre=%d", __func__, aw8624->f0_pre); + ret = aw8624_haptic_get_f0(aw8624); + if (ret) + return ret; + if (aw8624->f0 >= f0_max || aw8624->f0 <= f0_min) + break; + aw8624->f0_pre = aw8624->f0; + usleep_range(4000, 4500); + } + return 0; +} +#endif + +static int aw8624_haptic_f0_calibration(struct aw8624 *aw8624) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned int f0_limit = 0; + char f0_cali_lra = 0; + int f0_cali_step = 0; +#ifdef AW8624_MUL_GET_F0 + ret = aw8624_multiple_get_f0(aw8624); +#else + ret = aw8624_haptic_get_f0(aw8624); +#endif + + if (ret) { + aw_dev_err("%s get f0 error, user defafult f0\n", __func__); + } else { + /* max and min limit */ + f0_limit = aw8624->f0; + if (aw8624->f0*100 < aw8624_dts_data.aw8624_f0_pre * + (100-aw8624_dts_data.aw8624_f0_cali_percen)) { + f0_limit = aw8624_dts_data.aw8624_f0_pre; + } + if (aw8624->f0*100 > aw8624_dts_data.aw8624_f0_pre * + (100+aw8624_dts_data.aw8624_f0_cali_percen)) { + f0_limit = aw8624_dts_data.aw8624_f0_pre; + } + /* calculate cali step */ + f0_cali_step = 100000*((int)f0_limit-aw8624_dts_data.aw8624_f0_pre)/((int)f0_limit*25); + + if (f0_cali_step >= 0) { /*f0_cali_step >= 0*/ + if (f0_cali_step % 10 >= 5) { + f0_cali_step = f0_cali_step/10 + 1 + + (aw8624->chipid_flag == 1 ? 32 : 16); + } else { + f0_cali_step = f0_cali_step/10 + + (aw8624->chipid_flag == 1 ? 32 : 16); + } + } else { /*f0_cali_step < 0*/ + if (f0_cali_step % 10 <= -5) { + f0_cali_step = + (aw8624->chipid_flag == 1 ? 32 : 16) + + (f0_cali_step/10 - 1); + } else { + f0_cali_step = + (aw8624->chipid_flag == 1 ? 32 : 16) + + f0_cali_step/10; + } + } + + if (aw8624->chipid_flag == 1) { + if (f0_cali_step > 31) + f0_cali_lra = (char)f0_cali_step - 32; + else + f0_cali_lra = (char)f0_cali_step + 32; + } else { + if (f0_cali_step < 16 || + (f0_cali_step > 31 && f0_cali_step < 48)) { + f0_cali_lra = (char)f0_cali_step + 16; + } else { + f0_cali_lra = (char)f0_cali_step - 16; + } + } + + aw8624->f0_calib_data = (int)f0_cali_lra; + /* update cali step */ + aw8624_haptic_upload_lra(aw8624, + AW8624_HAPTIC_F0_CALI_LRA); + aw8624_i2c_read(aw8624, + AW8624_REG_TRIM_LRA, + ®_val, AW_I2C_BYTE_ONE); + aw_dev_info("%s final trim_lra=0x%02x\n", __func__, reg_val); + } + + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_STANDBY_MODE); + aw8624_haptic_stop(aw8624); + + return ret; +} + +static int +aw8624_haptic_audio_ctr_list_insert(struct haptic_audio *haptic_audio, + struct haptic_ctr *haptic_ctr, + struct device *dev) +{ + struct haptic_ctr *p_new = NULL; + + p_new = kzalloc(sizeof(struct haptic_ctr), GFP_KERNEL); + if (p_new == NULL) + return -ERANGE; + /* update new list info */ + p_new->cnt = haptic_ctr->cnt; + p_new->cmd = haptic_ctr->cmd; + p_new->play = haptic_ctr->play; + p_new->wavseq = haptic_ctr->wavseq; + p_new->loop = haptic_ctr->loop; + p_new->gain = haptic_ctr->gain; + + INIT_LIST_HEAD(&(p_new->list)); + list_add(&(p_new->list), &(haptic_audio->ctr_list)); + return 0; +} + +static int aw8624_haptic_audio_ctr_list_clear(struct haptic_audio *haptic_audio) +{ + struct haptic_ctr *p_ctr = NULL; + struct haptic_ctr *p_ctr_bak = NULL; + + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + list_del(&p_ctr->list); + kfree(p_ctr); + } + + return 0; +} + +static int aw8624_haptic_audio_off(struct aw8624 *aw8624) +{ + aw_dev_info("%s: enter\n", __func__); + mutex_lock(&aw8624->lock); + aw8624_haptic_set_gain(aw8624, 0x80); + aw8624_haptic_stop(aw8624); + aw8624->gun_type = AW_GUN_TYPE_DEF_VAL; + aw8624->bullet_nr =AW_BULLET_NR_DEF_VAL; + aw8624_haptic_audio_ctr_list_clear(&aw8624->haptic_audio); + mutex_unlock(&aw8624->lock); + return 0; +} + +static int aw8624_haptic_audio_init(struct aw8624 *aw8624) +{ + + aw_dev_info("%s: enter\n", __func__); + aw8624_haptic_set_wav_seq(aw8624, 0x01, 0x00); + + return 0; +} + +static int aw8624_haptic_offset_calibration(struct aw8624 *aw8624) +{ + unsigned int cont = 2000; + unsigned char reg_val = 0; + unsigned char reg_val_sysctrl = 0; + + aw_dev_dbg("%s enter\n", __func__); + aw8624_i2c_read(aw8624, AW8624_REG_SYSCTRL, ®_val_sysctrl, + AW_I2C_BYTE_ONE); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_RAMINIT_MASK, + AW8624_BIT_SYSCTRL_RAMINIT_EN); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_DETCTRL, + AW8624_BIT_DETCTRL_DIAG_GO_MASK, + AW8624_BIT_DETCTRL_DIAG_GO_ENABLE); + while (1) { + aw8624_i2c_read(aw8624, AW8624_REG_DETCTRL, ®_val, + AW_I2C_BYTE_ONE); + if ((reg_val & 0x01) == 0 || cont == 0) + break; + cont--; + } + if (cont == 0) + aw_dev_err("%s calibration offset failed!\n", + __func__); + + aw8624_i2c_write(aw8624, AW8624_REG_SYSCTRL, ®_val_sysctrl, + AW_I2C_BYTE_ONE); + return 0; + +} + +int aw8624_haptic_init(struct aw8624 *aw8624) +{ + int ret = 0; + unsigned char reg_val = 0, reg_flag = 0; + unsigned char reg_array[8] = {0}; + /* haptic audio */ + aw8624->haptic_audio.delay_val = 23; + aw8624->haptic_audio.timer_val = 23; + INIT_LIST_HEAD(&(aw8624->haptic_audio.ctr_list)); + hrtimer_init(&aw8624->haptic_audio.timer, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + aw8624->haptic_audio.timer.function = aw8624_haptic_audio_timer_func; + INIT_WORK(&aw8624->haptic_audio.work, aw8624_haptic_audio_work_routine); + aw8624->gun_type = AW_GUN_TYPE_DEF_VAL; + aw8624->bullet_nr = AW_BULLET_NR_DEF_VAL; + mutex_init(&aw8624->haptic_audio.lock); + /* haptic init */ + mutex_lock(&aw8624->lock); + ret = aw8624_i2c_read(aw8624, AW8624_REG_EF_RDATAH, ®_flag, + AW_I2C_BYTE_ONE); + if ((ret >= 0) && ((reg_flag & 0x1) == 1)) { + aw8624->chipid_flag = 1; + } else { + aw_dev_err("%s: to read register AW8624_REG_EF_RDATAH: %d\n", + __func__, ret); + } + + aw8624->activate_mode = aw8624_dts_data.aw8624_mode; + ret = aw8624_i2c_read(aw8624, AW8624_REG_WAVSEQ1, ®_val, + AW_I2C_BYTE_ONE); + aw8624->index = reg_val & 0x7F; + ret = aw8624_i2c_read(aw8624, AW8624_REG_DATDBG, ®_val, + AW_I2C_BYTE_ONE); + aw8624->gain = reg_val & 0xFF; + aw8624_i2c_read(aw8624, AW8624_REG_WAVSEQ1, reg_array, + AW8624_SEQUENCER_SIZE); + memcpy(aw8624->seq, reg_array, AW8624_SEQUENCER_SIZE); + + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_STANDBY_MODE); + aw8624_haptic_set_pwm(aw8624, AW8624_PWM_24K); + + aw8624_haptic_swicth_motorprotect_config(aw8624, 0x0, 0x0); + /*trig config*/ + aw8624_haptic_trig1_param_init(aw8624); + aw8624_haptic_tirg1_param_config(aw8624); + aw8624_haptic_offset_calibration(aw8624); + aw8624_haptic_vbat_mode(aw8624, AW8624_HAPTIC_VBAT_HW_COMP_MODE); + mutex_unlock(&aw8624->lock); + + /* f0 calibration */ + aw8624->f0_pre = aw8624_dts_data.aw8624_f0_pre; + aw8624->cont_drv_lvl = aw8624_dts_data.aw8624_cont_drv_lvl; + aw8624->cont_drv_lvl_ov = aw8624_dts_data.aw8624_cont_drv_lvl_ov; + aw8624->cont_td = aw8624_dts_data.aw8624_cont_td; + aw8624->cont_zc_thr = aw8624_dts_data.aw8624_cont_zc_thr; + aw8624->cont_num_brk = aw8624_dts_data.aw8624_cont_num_brk; + aw8624->ram_vbat_comp = AW8624_HAPTIC_RAM_VBAT_COMP_ENABLE; + mutex_lock(&aw8624->lock); + aw8624_i2c_write_bits(aw8624, AW8624_REG_R_SPARE, + AW8624_BIT_R_SPARE_MASK, AW8624_BIT_R_SPARE_ENABLE); + /*LRA trim source select register*/ + aw8624_i2c_write_bits(aw8624, + AW8624_REG_ANACTRL, + AW8624_BIT_ANACTRL_LRA_SRC_MASK, + AW8624_BIT_ANACTRL_LRA_SRC_REG); + if (aw8624_dts_data.aw8624_lk_f0_cali) { + aw8624->f0_calib_data = aw8624_dts_data.aw8624_lk_f0_cali; + aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_F0_CALI_LRA); + } else { + aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_ZERO); + aw8624_haptic_f0_calibration(aw8624); + } + mutex_unlock(&aw8624->lock); + + /*brake*/ + mutex_lock(&aw8624->lock); + reg_val = (unsigned char)(aw8624_dts_data.aw8624_sw_brake[0]); + aw8624_i2c_write(aw8624, AW8624_REG_SW_BRAKE, ®_val, + AW_I2C_BYTE_ONE); + reg_val = 0x00; + aw8624_i2c_write(aw8624, AW8624_REG_THRS_BRA_END, ®_val, + AW_I2C_BYTE_ONE); + aw8624_i2c_write_bits(aw8624, + AW8624_REG_WAVECTRL, + AW8624_BIT_WAVECTRL_NUM_OV_DRIVER_MASK, + AW8624_BIT_WAVECTRL_NUM_OV_DRIVER); + /* zero cross */ + reg_array[0] = (unsigned char)(aw8624->cont_zc_thr>>8); + reg_array[1] = (unsigned char)(aw8624->cont_zc_thr>>0); + aw8624_i2c_write(aw8624, + AW8624_REG_ZC_THRSH_H, reg_array, + AW_I2C_BYTE_TWO); + reg_val = (unsigned char)aw8624_dts_data.aw8624_tset; + aw8624_i2c_write(aw8624, AW8624_REG_TSET, + ®_val, AW_I2C_BYTE_ONE); + + /* bemf */ + reg_array[0] = (unsigned char)aw8624_dts_data.aw8624_bemf_config[0]; + reg_array[1] = (unsigned char)aw8624_dts_data.aw8624_bemf_config[1]; + reg_array[2] = (unsigned char)aw8624_dts_data.aw8624_bemf_config[2]; + reg_array[3] = (unsigned char)aw8624_dts_data.aw8624_bemf_config[3]; + aw8624_i2c_write(aw8624, AW8624_REG_BEMF_VTHH_H, reg_array, + AW_I2C_BYTE_FOUR); + mutex_unlock(&aw8624->lock); + + return ret; +} + +/***************************************************** + * + * vibrator + * + *****************************************************/ +#ifdef TIMED_OUTPUT +static int aw8624_vibrator_get_time(struct timed_output_dev *vib_dev) +{ + struct aw8624 *aw8624 = container_of(vib_dev, struct aw8624, vib_dev); + + if (hrtimer_active(&aw8624->timer)) { + ktime_t r = hrtimer_get_remaining(&aw8624->timer); + + return ktime_to_ms(r); + } + + return 0; +} + +static void aw8624_vibrator_enable(struct timed_output_dev *vib_dev, int value) +{ + struct aw8624 *aw8624 = container_of(vib_dev, struct aw8624, vib_dev); + + aw_dev_dbg("%s enter\n", __func__); + if (!aw8624->ram_init) { + aw_dev_err("%s: ram init failed, not allow to play!\n", + __func__); + return; + } + mutex_lock(&aw8624->lock); + aw8624_haptic_stop(aw8624); + if (value > 0) { + aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_F0_CALI_LRA); + aw8624_haptic_ram_vbat_comp(aw8624, false); + aw8624_haptic_play_wav_seq(aw8624, value); + } + + mutex_unlock(&aw8624->lock); +} + +#else +static void +aw8624_vibrator_enable(struct led_classdev *dev, enum led_brightness value) +{ + struct aw8624 *aw8624 = container_of(dev, struct aw8624, vib_dev); + + aw_dev_dbg("%s enter\n", __func__); + if (!aw8624->ram_init) { + aw_dev_err("%s: ram init failed, not allow to play!\n", + __func__); + return; + } + mutex_lock(&aw8624->lock); + aw8624_haptic_stop(aw8624); + if (value > 0) { + aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_F0_CALI_LRA); + aw8624_haptic_ram_vbat_comp(aw8624, false); + aw8624_haptic_play_wav_seq(aw8624, value); + } + + mutex_unlock(&aw8624->lock); + + +} + +#endif + +static ssize_t aw8624_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + + return snprintf(buf, PAGE_SIZE, "%d\n", aw8624->state); +} + +static ssize_t aw8624_state_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw8624_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + ktime_t time_rem; + s64 time_ms = 0; + + if (hrtimer_active(&aw8624->timer)) { + time_rem = hrtimer_get_remaining(&aw8624->timer); + time_ms = ktime_to_ms(time_rem); + } + + return snprintf(buf, PAGE_SIZE, "%lld\n", time_ms); +} + +static ssize_t aw8624_duration_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + /* setting 0 on duration is NOP for now */ + if (val <= 0) + return count; + rc = aw8624_haptic_ram_config(aw8624, val); + if (rc < 0) + return count; + aw8624->duration = val; + return count; +} + +static ssize_t aw8624_activate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + /* For now nothing to show */ + return snprintf(buf, PAGE_SIZE, "%d\n", aw8624->state); +} + +static ssize_t aw8624_activate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + unsigned int val = 0; + int rc = 0; + + if (!aw8624->ram_init) { + aw_dev_err("%s: ram init failed, not allow to play!\n", + __func__); + return count; + } + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + if (val != 0 && val != 1) + return count; + + aw_dev_info("%s: value=%d\n", __func__, val); + + mutex_lock(&aw8624->lock); + hrtimer_cancel(&aw8624->timer); + + aw8624->state = val; + + /*aw8624_haptic_stop(aw8624);*/ + + mutex_unlock(&aw8624->lock); + schedule_work(&aw8624->vibrator_work); + + return count; +} + +static ssize_t aw8624_activate_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", + aw8624->activate_mode); +} + +static ssize_t aw8624_activate_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + mutex_lock(&aw8624->lock); + aw8624->activate_mode = val; + mutex_unlock(&aw8624->lock); + return count; +} + + +static ssize_t aw8624_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + unsigned char reg_val = 0; + + aw8624_i2c_read(aw8624, AW8624_REG_WAVSEQ1, ®_val, + AW_I2C_BYTE_ONE); + aw8624->index = reg_val; + + return snprintf(buf, PAGE_SIZE, "%d\n", aw8624->index); +} + +static ssize_t aw8624_index_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + if (val > aw8624->ram.ram_num) { + aw_dev_err("%s: input value out of range!\n", __func__); + return count; + } + aw_dev_info("%s: value=%d\n", __func__, val); + + mutex_lock(&aw8624->lock); + aw8624->index = val; + aw8624_haptic_set_repeat_wav_seq(aw8624, aw8624->index); + mutex_unlock(&aw8624->lock); + return count; +} + +static ssize_t aw8624_gain_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + return snprintf(buf, PAGE_SIZE, "0x%02x\n", aw8624->gain); +} + +static ssize_t aw8624_gain_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + aw_dev_info("%s: value=%d\n", __func__, val); + + mutex_lock(&aw8624->lock); + aw8624->gain = val; + aw8624_haptic_set_gain(aw8624, aw8624->gain); + mutex_unlock(&aw8624->lock); + return count; +} + +static ssize_t aw8624_seq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + size_t count = 0; + unsigned char i = 0; + unsigned char reg_val[AW8624_SEQUENCER_SIZE] = {0}; + + aw8624_i2c_read(aw8624, AW8624_REG_WAVSEQ1, reg_val, + AW8624_SEQUENCER_SIZE); + + for (i = 0; i < AW8624_SEQUENCER_SIZE; i++) { + count += snprintf(buf+count, PAGE_SIZE-count, + "seq%d: 0x%02x\n", i+1, reg_val[i]); + aw8624->seq[i] = reg_val[i]; + } + return count; +} + +static ssize_t aw8624_seq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + unsigned int databuf[2] = {0, 0}; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + if (databuf[0] > AW8624_SEQUENCER_SIZE || + databuf[1] > aw8624->ram.ram_num) { + aw_dev_err("%s input value out of range\n", + __func__); + return count; + } + aw_dev_info("%s: seq%d=0x%x\n", __func__, + databuf[0], databuf[1]); + mutex_lock(&aw8624->lock); + aw8624->seq[databuf[0]] = (unsigned char)databuf[1]; + aw8624_haptic_set_wav_seq(aw8624, (unsigned char)databuf[0], + aw8624->seq[databuf[0]]); + mutex_unlock(&aw8624->lock); + } + return count; +} + +static ssize_t aw8624_loop_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + size_t count = 0; + unsigned char i = 0; + unsigned char reg_val[AW8624_SEQUENCER_LOOP_SIZE] = {0}; + + aw8624_i2c_read(aw8624, AW8624_REG_WAVLOOP1, reg_val, + AW8624_SEQUENCER_LOOP_SIZE); + + for (i = 0; i < AW8624_SEQUENCER_LOOP_SIZE; i++) { + aw8624->loop[i*2+0] = (reg_val[i] >> 4) & 0x0F; + aw8624->loop[i*2+1] = (reg_val[i] >> 0) & 0x0F; + + count += snprintf(buf+count, PAGE_SIZE-count, + "seq%d_loop: 0x%02x\n", i*2+1, aw8624->loop[i*2+0]); + count += snprintf(buf+count, PAGE_SIZE-count, + "seq%d_loop: 0x%02x\n", i*2+2, aw8624->loop[i*2+1]); + } + return count; +} + +static ssize_t aw8624_loop_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + unsigned int databuf[2] = {0, 0}; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_dev_info("%s: seq%d loop=0x%x\n", __func__, + databuf[0], databuf[1]); + mutex_lock(&aw8624->lock); + aw8624->loop[databuf[0]] = (unsigned char)databuf[1]; + aw8624_haptic_set_wav_loop(aw8624, (unsigned char)databuf[0], + aw8624->loop[databuf[0]]); + mutex_unlock(&aw8624->lock); + } + + return count; +} + +static ssize_t aw8624_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char i = 0; + unsigned char size = 0; + unsigned char cnt = 0; + unsigned char reg_array[AW_REG_MAX] = {0}; + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + aw_dev_dbg("%s: enter!\n", __func__); + for (i = 0; i <= (AW8624_REG_NUM_F0_3 + 1); i++) { + + if (i == aw8624_reg_list[cnt] && + (cnt < sizeof(aw8624_reg_list))) { + size++; + cnt++; + continue; + } else { + if (size != 0) { + aw8624_i2c_read(aw8624, + aw8624_reg_list[cnt-size], + ®_array[cnt-size], size); + size = 0; + + } + } + } + + for (i = 0; i < sizeof(aw8624_reg_list); i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "reg:0x%02X=0x%02X\n", aw8624_reg_list[i], + reg_array[i]); + } + + return len; +} + +static ssize_t aw8624_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned char val = 0; + unsigned int databuf[2] = {0, 0}; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + val = (unsigned char)databuf[1]; + aw8624_i2c_write(aw8624, (unsigned char)databuf[0], &val, + AW_I2C_BYTE_ONE); + } + + return count; +} + +static ssize_t aw8624_rtp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + len += snprintf(buf+len, PAGE_SIZE-len, "rtp_cnt = %d\n", + aw8624->rtp_cnt); + + return len; +} + +static ssize_t aw8624_rtp_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + if (!(aw8624->IsUsedIRQ)) + return count; + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + mutex_lock(&aw8624->lock); + aw8624_haptic_stop(aw8624); + aw8624_haptic_set_rtp_aei(aw8624, false); + aw8624_interrupt_clear(aw8624); + if (val < (sizeof(aw8624_rtp_name)/AW8624_RTP_NAME_MAX)) { + aw8624->rtp_file_num = val; + if (val) { + aw_dev_info("%s: aw8624_rtp_name[%d]: %s\n", __func__, + val, aw8624_rtp_name[val]); + schedule_work(&aw8624->rtp_work); + } else + aw_dev_err("%s: rtp_file_num 0x%02X over max value\n", + __func__, aw8624->rtp_file_num); + } else { + aw_dev_err("%s: rtp_file_num 0x%02x over max value\n", + __func__, aw8624->rtp_file_num); + } + mutex_unlock(&aw8624->lock); + return count; +} + + +static ssize_t aw8624_ram_update_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned char reg_val_sysctrl = 0; + int cnt = 0; + int i = 0; + int size = 0; + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + unsigned char ram_data[AW_RAMDATA_RD_BUFFER_SIZE] = {0}; + + aw8624_i2c_read(aw8624, AW8624_REG_SYSCTRL, ®_val_sysctrl, + AW_I2C_BYTE_ONE); + /* RAMINIT Enable */ + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_RAMINIT_MASK, + AW8624_BIT_SYSCTRL_RAMINIT_EN); + aw8624_set_ram_addr(aw8624); + len += snprintf(buf + len, PAGE_SIZE - len, + "haptic_ram len = %d\n", aw8624->ram.len); + while (cnt < aw8624->ram.len) { + if ((aw8624->ram.len - cnt) < AW_RAMDATA_RD_BUFFER_SIZE) + size = aw8624->ram.len - cnt; + else + size = AW_RAMDATA_RD_BUFFER_SIZE; + + aw8624_i2c_read(aw8624, AW8624_REG_RAMDATA, + ram_data, size); + for (i = 0; i < size; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%02X,", ram_data[i]); + } + cnt += size; + } + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + + /* RAMINIT Disable */ + aw8624_i2c_write(aw8624, AW8624_REG_SYSCTRL, ®_val_sysctrl, + AW_I2C_BYTE_ONE); + return len; +} + +static ssize_t aw8624_ram_update_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + if (val) + aw8624_ram_update(aw8624); + + return count; +} + +static ssize_t aw8624_f0_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + unsigned char temp = 0; + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + mutex_lock(&aw8624->lock); + aw8624_i2c_read(aw8624, AW8624_REG_TRIM_LRA, &temp, + AW_I2C_BYTE_ONE); + aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_ZERO); +#ifdef AW8624_MUL_GET_F0 + aw8624_multiple_get_f0(aw8624); +#else + aw8624_haptic_get_f0(aw8624); +#endif + aw8624_i2c_write(aw8624, AW8624_REG_TRIM_LRA, &temp, AW_I2C_BYTE_ONE); + mutex_unlock(&aw8624->lock); + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", aw8624->f0); + aw_dev_info("len = %zd, buf=%s", len, buf); + return len; +} + +static ssize_t aw8624_f0_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + return count; +} + + +static ssize_t aw8624_cali_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + unsigned char temp = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + mutex_lock(&aw8624->lock); + aw8624_i2c_read(aw8624, AW8624_REG_TRIM_LRA, &temp, + AW_I2C_BYTE_ONE); + aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_F0_CALI_LRA); + aw8624_haptic_get_f0(aw8624); + aw8624_i2c_write(aw8624, AW8624_REG_TRIM_LRA, &temp, AW_I2C_BYTE_ONE); + mutex_unlock(&aw8624->lock); + + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", aw8624->f0); + return len; +} + +static ssize_t +aw8624_cali_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + if (val) { + mutex_lock(&aw8624->lock); + aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_ZERO); + aw8624_haptic_f0_calibration(aw8624); + mutex_unlock(&aw8624->lock); + } + return count; +} + +static ssize_t +aw8624_cont_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + aw8624_haptic_read_cont_f0(aw8624); + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", aw8624->cont_f0); + return len; +} + +static ssize_t +aw8624_cont_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + aw8624_haptic_stop(aw8624); + + if (val) + aw8624_haptic_cont(aw8624); + return count; +} + + +static ssize_t +aw8624_cont_td_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + len += snprintf(buf+len, PAGE_SIZE-len, + "cont_delay_time = 0x%04x\n", + aw8624->cont_td); + return len; +} + +static ssize_t +aw8624_cont_td_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + int err, val; + unsigned char reg_array[2] = {0}; + + err = kstrtoint(buf, 16, &val); + if (err != 0) { + aw_dev_err("%s format not match!", __func__); + return count; + } + + aw8624->cont_td = val; + reg_array[0] = (unsigned char)(val >> 8); + reg_array[1] = (unsigned char)(val >> 0); + aw8624_i2c_write(aw8624, AW8624_REG_TD_H, reg_array, AW_I2C_BYTE_TWO); + + return count; +} + +static ssize_t +aw8624_cont_drv_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + len += snprintf(buf+len, PAGE_SIZE-len, + "cont drv level = 0x%02x\n", + aw8624->cont_drv_lvl); + len += snprintf(buf+len, PAGE_SIZE-len, + "cont drv level overdrive= 0x%02x\n", + aw8624->cont_drv_lvl_ov); + return len; +} + +static ssize_t +aw8624_cont_drv_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned char reg_array[2] = {0}; + unsigned int databuf[2] = {0}; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw8624->cont_drv_lvl = databuf[0]; + aw8624->cont_drv_lvl_ov = databuf[1]; + reg_array[0] = aw8624->cont_drv_lvl; + reg_array[1] = aw8624->cont_drv_lvl_ov; + aw8624_i2c_write(aw8624, AW8624_REG_DRV_LVL, reg_array, + AW_I2C_BYTE_TWO); + } + return count; +} + +static ssize_t +aw8624_cont_num_brk_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + len += snprintf(buf+len, PAGE_SIZE-len, + "cont_brk_num = 0x%02x\n", + aw8624->cont_num_brk); + return len; +} + +static ssize_t +aw8624_cont_num_brk_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err, val; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + err = kstrtoint(buf, 16, &val); + if (err != 0) { + aw_dev_err("%s format not match!", __func__); + return count; + } + + aw8624->cont_num_brk = val; + if (aw8624->cont_num_brk > 7) + aw8624->cont_num_brk = 7; + + aw8624_i2c_write_bits(aw8624, AW8624_REG_BEMF_NUM, + AW8624_BIT_BEMF_NUM_BRK_MASK, aw8624->cont_num_brk); + + return count; +} + +static ssize_t +aw8624_cont_zc_thr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "cont_zero_cross_thr = 0x%04x\n", + aw8624->cont_zc_thr); + return len; +} + +static ssize_t +aw8624_cont_zc_thr_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned char reg_array[2] = {0}; + int err, val; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + err = kstrtoint(buf, 0, &val); + if (err != 0) { + aw_dev_err("%s format not match!", __func__); + return count; + } + aw8624->cont_num_brk = val; + aw_dev_info("%s: val=%d\n", __func__, val); + if (val > 0) { + reg_array[0] = (unsigned char)(val >> 8); + reg_array[1] = (unsigned char)(val >> 0); + aw8624_i2c_write(aw8624, AW8624_REG_ZC_THRSH_H, reg_array, + AW_I2C_BYTE_TWO); + } + return count; +} + +static ssize_t +aw8624_vbat_monitor_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + unsigned int vbat = 0; + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + mutex_lock(&aw8624->lock); + vbat = aw8624_vbat_monitor_detector(aw8624); + mutex_unlock(&aw8624->lock); + len += snprintf(buf+len, PAGE_SIZE-len, "vbat_monitor = %d\n", vbat); + + return len; +} + +static ssize_t +aw8624_vbat_monitor_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t +aw8624_lra_resistance_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + ssize_t len = 0; + unsigned int r_lra = 0; + + r_lra = aw8624_lra_resistance_detector(aw8624); + + len += snprintf(buf+len, PAGE_SIZE-len, "lra_resistance = %d\n", r_lra); + return len; +} + + +static ssize_t +aw8624_lra_resistance_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + + + +static ssize_t +aw8624_prctmode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + unsigned char reg_val = 0; + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + + aw8624_i2c_read(aw8624, AW8624_REG_RLDET, ®_val, + AW_I2C_BYTE_ONE); + + len += snprintf(buf+len, PAGE_SIZE-len, + "prctmode = %d\n", reg_val&0x20); + return len; +} + + +static ssize_t +aw8624_prctmode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int databuf[2] = {0, 0}; + unsigned int addr = 0; + unsigned int val = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + addr = databuf[0]; + val = databuf[1]; + mutex_lock(&aw8624->lock); + aw8624_haptic_swicth_motorprotect_config(aw8624, addr, val); + mutex_unlock(&aw8624->lock); + } + return count; +} + +static ssize_t +aw8624_ram_vbat_comp_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + len += snprintf(buf+len, PAGE_SIZE-len, + "ram_vbat_comp = %d\n", + aw8624->ram_vbat_comp); + + return len; +} + + +static ssize_t +aw8624_ram_vbat_comp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + mutex_lock(&aw8624->lock); + if (val) + aw8624->ram_vbat_comp = AW8624_HAPTIC_RAM_VBAT_COMP_ENABLE; + else + aw8624->ram_vbat_comp = AW8624_HAPTIC_RAM_VBAT_COMP_DISABLE; + + mutex_unlock(&aw8624->lock); + + return count; +} + +static ssize_t +aw8624_haptic_audio_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", + aw8624->haptic_audio.ctr.cnt); + return len; +} + +static int aw8624_haptic_audio_config(struct aw8624 *aw8624, + unsigned int databuf[]) +{ + struct haptic_ctr *hap_ctr = NULL; + + hap_ctr = kzalloc(sizeof(struct haptic_ctr), GFP_KERNEL); + if (hap_ctr == NULL) + return -ENOMEM; + mutex_lock(&aw8624->haptic_audio.lock); + hap_ctr->cnt = (unsigned char)databuf[0]; + hap_ctr->cmd = (unsigned char)databuf[1]; + hap_ctr->play = (unsigned char)databuf[2]; + hap_ctr->wavseq = (unsigned char)databuf[3]; + hap_ctr->loop = (unsigned char)databuf[4]; + hap_ctr->gain = (unsigned char)databuf[5]; + aw8624_haptic_audio_ctr_list_insert(&aw8624->haptic_audio, hap_ctr, + aw8624->dev); + if (hap_ctr->cmd == AW8624_HAPTIC_CMD_STOP) { + aw_dev_info("%s: haptic_audio stop\n", __func__); + if (hrtimer_active(&aw8624->haptic_audio.timer)) { + aw_dev_info("%s:cancel haptic_audio_timer\n", __func__); + hrtimer_cancel(&aw8624->haptic_audio.timer); + aw8624->haptic_audio.ctr.cnt = 0; + aw8624_haptic_audio_off(aw8624); + } + } else { + if (hrtimer_active(&aw8624->haptic_audio.timer)) { + } else { + aw_dev_info("%s:start haptic_audio_timer\n", __func__); + aw8624_haptic_audio_init(aw8624); + hrtimer_start(&aw8624->haptic_audio.timer, + ktime_set(aw8624->haptic_audio.delay_val/1000, + (aw8624->haptic_audio.delay_val%1000)*1000000), + HRTIMER_MODE_REL); + } + } + kfree(hap_ctr); + mutex_unlock(&aw8624->haptic_audio.lock); + return 0; +} +static ssize_t +aw8624_haptic_audio_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int databuf[6] = {0}; + int rtp_is_going_on = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + rtp_is_going_on = aw8624_haptic_juge_RTP_is_going_on(aw8624); + if (rtp_is_going_on) { + aw_dev_info("%s: RTP is runing, stop audio haptic\n", __func__); + return count; + } + if (!aw8624->ram_init) { + aw_dev_err("%s: ram init failed, not allow to play!\n", + __func__); + return count; + } + + if (sscanf(buf, "%d %d %d %d %d %d", + &databuf[0], &databuf[1], &databuf[2], + &databuf[3], &databuf[4], &databuf[5]) == 6) { + if (databuf[2]) { + aw_dev_dbg("%s: cnt=%d, cmd=%d, play=%d\n", __func__, + databuf[0], databuf[1], databuf[2]); + aw_dev_dbg("%s: wavseq=%d, loop=%d, gain=%d\n", + __func__, + databuf[3], databuf[4], databuf[5]); + aw8624_haptic_audio_config(aw8624, databuf); + } + + } + return count; +} + +static ssize_t +aw8624_haptic_audio_time_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + len += snprintf(buf+len, PAGE_SIZE-len, + "haptic_audio.delay_val=%dus\n", + aw8624->haptic_audio.delay_val); + len += snprintf(buf+len, PAGE_SIZE-len, + "haptic_audio.timer_val=%dus\n", + aw8624->haptic_audio.timer_val); + return len; +} + +static ssize_t +aw8624_haptic_audio_time_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + unsigned int databuf[2] = {0}; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + if (sscanf(buf, "%d %d", &databuf[0], &databuf[1]) == 2) { + aw8624->haptic_audio.delay_val = databuf[0]; + aw8624->haptic_audio.timer_val = databuf[1]; + } + return count; +} + +static int aw8624_clock_OSC_trim_calibration(struct aw8624 *aw8624, + unsigned long int theory_time) +{ + unsigned int real_code = 0; + unsigned int LRA_TRIM_CODE = 0; + unsigned int DFT_LRA_TRIM_CODE = 0; + unsigned int Not_need_cali_threshold = 10;/*0.1 percent not need cali*/ + unsigned long int real_time = aw8624->microsecond; + + aw_dev_dbg("%s enter\n", __func__); + if (theory_time == real_time) { + aw_dev_info("aw_osctheory_time == real_time:%ld,", real_time); + aw_dev_info("theory_time = %ld not need to cali\n", + theory_time); + return 0; + } else if (theory_time < real_time) { + if ((real_time - theory_time) > (theory_time / 25)) { + aw_dev_info("%s: failed not to cali\n", __func__); + return DFT_LRA_TRIM_CODE; + } + + if ((real_time - theory_time) < + (Not_need_cali_threshold*theory_time/10000)) { + aw_dev_info("aw_oscmicrosecond:%ld,theory_time = %ld\n", + real_time, theory_time); + return DFT_LRA_TRIM_CODE; + } + + real_code = ((real_time - theory_time) * 4) / (theory_time / 1000); + real_code = ((real_code%10 < 5) ? 0 : 1) + real_code/10; + real_code = 32 + real_code; + } else if (theory_time > real_time) { + if ((theory_time - real_time) > (theory_time / 25)) { + aw_dev_info("failed not to cali\n"); + return DFT_LRA_TRIM_CODE; + } + if ((theory_time - real_time) < + (Not_need_cali_threshold * theory_time/10000)) { + aw_dev_info("aw_oscmicrosecond:%ld,theory_time = %ld\n", + real_time, theory_time); + return DFT_LRA_TRIM_CODE; + } + real_code = ((theory_time - real_time) * 4) / (theory_time / 1000); + real_code = ((real_code%10 < 5) ? 0 : 1) + real_code/10; + real_code = 32 - real_code; + } + if (real_code > 31) + LRA_TRIM_CODE = real_code - 32; + else + LRA_TRIM_CODE = real_code + 32; + + aw_dev_info("aw_oscmicrosecond:%ld,theory_time = %ld,real_code =0X%02X,", + real_time, theory_time, real_code); + aw_dev_info("LRA_TRIM_CODE 0X%02X\n", LRA_TRIM_CODE); + + return LRA_TRIM_CODE; +} + +static int aw8624_rtp_trim_lra_calibration(struct aw8624 *aw8624) +{ + unsigned char reg_val = 0; + unsigned int fre_val = 0; + unsigned int theory_time = 0; + unsigned int lra_rtim_code = 0; + unsigned int rate = 0; + + aw_dev_dbg("%s enter\n", __func__); + aw8624_i2c_read(aw8624, AW8624_REG_PWMDBG, ®_val, + AW_I2C_BYTE_ONE); + fre_val = (reg_val & 0x006f) >> 5; + + if (fre_val == AW8624_CLK_12K) + rate = 12000; + else if (fre_val == AW8624_CLK_24K) + rate = 24000; + else + rate = 48000; + theory_time = (aw8624->rtp_len / rate) * 1000000; + aw_dev_info("microsecond:%ld theory_time = %d\n", + aw8624->microsecond, theory_time); + + lra_rtim_code = aw8624_clock_OSC_trim_calibration(aw8624, theory_time); + if (lra_rtim_code >= 0) { + aw8624->lra_calib_data = lra_rtim_code; + reg_val = (unsigned char)lra_rtim_code; + aw8624_i2c_write(aw8624, AW8624_REG_TRIM_LRA, + ®_val, AW_I2C_BYTE_ONE); + } + return 0; +} + +static unsigned char aw8624_haptic_osc_read_int(struct aw8624 *aw8624) +{ + unsigned char reg_val = 0; + + aw8624_i2c_read(aw8624, AW8624_REG_DBGSTAT, ®_val, + AW_I2C_BYTE_ONE); + return reg_val; +} + +static int aw8624_osc_calculation_time(struct aw8624 *aw8624) +{ + unsigned char osc_int_state = 0; + unsigned int base_addr = aw8624->ram.base_addr; + unsigned int buf_len = 0; + int ret = -1; + struct aw8624_container *rtp_container = aw8624->rtp_container; + + if (!aw8624_haptic_rtp_get_fifo_afi(aw8624)) { + aw_dev_dbg("%s: haptic_rtp_get_fifo_afi, rtp_cnt= %d\n", + __func__, aw8624->rtp_cnt); + + mutex_lock(&aw8624->rtp_lock); + if ((rtp_container->len - aw8624->rtp_cnt) < (base_addr >> 2)) + buf_len = rtp_container->len - aw8624->rtp_cnt; + else + buf_len = (base_addr >> 2); + + if (aw8624->rtp_cnt != rtp_container->len) { + if (aw8624->timeval_flags == 1) { + aw8624->kstart = ktime_get(); + aw8624->timeval_flags = 0; + } + aw8624_i2c_write(aw8624, + AW8624_REG_RTP_DATA, + &rtp_container->data[aw8624->rtp_cnt], + buf_len); + aw8624->rtp_cnt += buf_len; + } + mutex_unlock(&aw8624->rtp_lock); + } + + osc_int_state = aw8624_haptic_osc_read_int(aw8624); + if (osc_int_state&AW8624_BIT_SYSINT_DONEI) { + aw8624->kend = ktime_get(); + aw_dev_info("%s vincent playback aw8624->rtp_cnt= %d\n", + __func__, aw8624->rtp_cnt); + return ret; + } + + aw8624->kend = ktime_get(); + aw8624->microsecond = ktime_to_us(ktime_sub(aw8624->kend, + aw8624->kstart)); + if (aw8624->microsecond > AW8624_OSC_CALIBRATION_T_LENGTH) { + aw_dev_info("%s:vincent time out aw8624->rtp_cnt %d,", + __func__, aw8624->rtp_cnt); + aw_dev_info("%s: osc_int_state %02x\n", __func__, osc_int_state); + return ret; + } + return 0; +} + +static int aw8624_rtp_osc_calibration(struct aw8624 *aw8624) +{ + int ret = -1; + const struct firmware *rtp_file; + + aw8624->rtp_cnt = 0; + aw8624->timeval_flags = 1; + aw8624->osc_cali_flag = 1; + + aw_dev_dbg("%s enter\n", __func__); + /* fw loaded */ + ret = request_firmware(&rtp_file, + aw8624_rtp_name[0],/*aw8624->rtp_file_num */ + aw8624->dev); + if (ret < 0) { + /*aw8624->rtp_file_num */ + aw_dev_err("%s: failed to read %s\n", __func__, + aw8624_rtp_name[0]); + return ret; + } + + /*awinic add stop,for irq interrupt during calibrate*/ + aw8624_haptic_stop(aw8624); + aw8624->rtp_init = 0; + mutex_lock(&aw8624->rtp_lock); + vfree(aw8624->rtp_container); + aw8624->rtp_container = vmalloc(rtp_file->size + sizeof(int)); + if (!aw8624->rtp_container) { + release_firmware(rtp_file); + mutex_unlock(&aw8624->rtp_lock); + aw_dev_err("%s: error allocating memory\n", __func__); + return -ENOMEM; + } + aw8624->rtp_container->len = rtp_file->size; + aw8624->rtp_len = rtp_file->size; + /*aw8624->rtp_file_num */ + aw_dev_info("%s: rtp file [%s] size = %d\n", __func__, + aw8624_rtp_name[0], aw8624->rtp_container->len); + memcpy(aw8624->rtp_container->data, rtp_file->data, rtp_file->size); + release_firmware(rtp_file); + mutex_unlock(&aw8624->rtp_lock); + + /* gain */ + aw8624_haptic_ram_vbat_comp(aw8624, false); + + /* rtp mode config */ + aw8624_haptic_play_mode(aw8624, AW8624_HAPTIC_RTP_MODE); + + aw8624_i2c_write_bits(aw8624, AW8624_REG_DBGCTRL, + AW8624_BIT_DBGCTRL_INT_MODE_MASK, + AW8624_BIT_DBGCTRL_INT_MODE_EDGE); + disable_irq(gpio_to_irq(aw8624->irq_gpio)); + /* haptic start */ + aw8624_haptic_start(aw8624); + pm_qos_add_request(&aw8624_pm_qos_req_vb, PM_QOS_CPU_DMA_LATENCY, + AW8624_PM_QOS_VALUE_VB); + while (1) { + ret = aw8624_osc_calculation_time(aw8624); + if (ret < 0) + break; + } + pm_qos_remove_request(&aw8624_pm_qos_req_vb); + enable_irq(gpio_to_irq(aw8624->irq_gpio)); + + aw8624->osc_cali_flag = 0; + aw8624->microsecond = ktime_to_us(ktime_sub(aw8624->kend, + aw8624->kstart)); + /*calibration osc*/ + aw_dev_info("%s awinic_microsecond:%ld\n", __func__, + aw8624->microsecond); + aw_dev_info("%s exit\n", __func__); + return 0; +} + +static ssize_t aw8624_osc_cali_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + len += + snprintf(buf + len, PAGE_SIZE - len, "lra_calib_data=%d\n", + aw8624->lra_calib_data); + + return len; +} + +static ssize_t aw8624_osc_cali_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + mutex_lock(&aw8624->lock); + /* osc calibration flag start,Other behaviors are forbidden */ + aw8624->osc_cali_run = 1; + if (val == 1) { + aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_ZERO); + aw8624_rtp_osc_calibration(aw8624); + aw8624_rtp_trim_lra_calibration(aw8624); + } else if (val == 2) { + aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_RTP_CALI_LRA); + aw8624_rtp_osc_calibration(aw8624); + } else { + aw_dev_err("%s input value out of range\n", __func__); + } + aw8624->osc_cali_run = 0; + /* osc calibration flag end,Other behaviors are permitted */ + mutex_unlock(&aw8624->lock); + + return count; +} + + + + +static enum hrtimer_restart aw8624_vibrator_timer_func(struct hrtimer *timer) +{ + struct aw8624 *aw8624 = container_of(timer, struct aw8624, timer); + + aw_dev_dbg("%s enter\n", __func__); + aw8624->state = 0; + schedule_work(&aw8624->vibrator_work); + + return HRTIMER_NORESTART; +} + + +static void aw8624_haptic_ram_play(struct aw8624 *aw8624, unsigned char type) +{ + aw_dev_info("%s index = %d\n", __func__, aw8624->index); + + mutex_lock(&aw8624->ram_lock); + if (type == AW8624_HAPTIC_RAM_LOOP_MODE) + aw8624_haptic_set_repeat_wav_seq(aw8624, aw8624->index); + + if (type == AW8624_HAPTIC_RAM_MODE) { + aw8624_haptic_set_wav_seq(aw8624, 0x00, aw8624->index); + aw8624_haptic_set_wav_seq(aw8624, 0x01, 0x00); + aw8624_haptic_set_wav_loop(aw8624, 0x00, 0x00); + } + aw8624_haptic_play_mode(aw8624, type); + aw8624_haptic_start(aw8624); + mutex_unlock(&aw8624->ram_lock); +} +static void aw8624_vibrator_work_routine(struct work_struct *work) +{ + struct aw8624 *aw8624 = container_of(work, struct aw8624, + vibrator_work); + + aw_dev_dbg("%s enter\n", __func__); + + mutex_lock(&aw8624->lock); + aw8624_haptic_stop(aw8624); + aw8624_haptic_upload_lra(aw8624, AW8624_HAPTIC_F0_CALI_LRA); + if (aw8624->state) { + if (aw8624->activate_mode == AW8624_HAPTIC_RAM_MODE) { + aw8624_haptic_ram_vbat_comp(aw8624, true); + aw8624_haptic_ram_play(aw8624, AW8624_HAPTIC_RAM_MODE); + } else if (aw8624->activate_mode == + AW8624_HAPTIC_RAM_LOOP_MODE) { + aw8624_haptic_ram_vbat_comp(aw8624, true); + aw8624_haptic_ram_play(aw8624, + AW8624_HAPTIC_RAM_LOOP_MODE); + /* run ms timer */ + hrtimer_start(&aw8624->timer, + ktime_set(aw8624->duration / 1000, + (aw8624->duration % 1000) * 1000000), + HRTIMER_MODE_REL); + } else if (aw8624->activate_mode == + AW8624_HAPTIC_CONT_MODE) { + aw8624_haptic_cont(aw8624); + /* run ms timer */ + hrtimer_start(&aw8624->timer, + ktime_set(aw8624->duration / 1000, + (aw8624->duration % 1000) * 1000000), + HRTIMER_MODE_REL); + } else { + aw_dev_err("%s: activate_mode error\n", + __func__); + } + } + mutex_unlock(&aw8624->lock); +} + + + +/****************************************************** + * + * irq + * + ******************************************************/ +void aw8624_interrupt_setup(struct aw8624 *aw8624) +{ + unsigned char reg_val = 0; + + aw8624_i2c_read(aw8624, AW8624_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_dev_info("%s: reg SYSINT=0x%x\n", __func__, reg_val); + + aw8624_i2c_write_bits(aw8624, AW8624_REG_DBGCTRL, + AW8624_BIT_DBGCTRL_INT_MODE_MASK, + AW8624_BIT_DBGCTRL_INT_MODE_EDGE); + + aw8624_i2c_write_bits(aw8624, AW8624_REG_SYSINTM, + (AW8624_BIT_SYSINTM_UVLO_MASK & + AW8624_BIT_SYSINTM_OCD_MASK & + AW8624_BIT_SYSINTM_OT_MASK), + (AW8624_BIT_SYSINTM_UVLO_EN | + AW8624_BIT_SYSINTM_OCD_EN | + AW8624_BIT_SYSINTM_OT_EN)); +} + +static ssize_t aw8624_gun_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + return snprintf(buf, PAGE_SIZE, "0x%02x\n", aw8624->gun_type); +} + +static ssize_t aw8624_gun_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + aw_dev_dbg("%s: value=%d\n", __func__, val); + + mutex_lock(&aw8624->lock); + aw8624->gun_type = val; + mutex_unlock(&aw8624->lock); + return count; +} + +static ssize_t aw8624_bullet_nr_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + return snprintf(buf, PAGE_SIZE, "0x%02x\n", aw8624->bullet_nr); +} + +static ssize_t aw8624_bullet_nr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + aw_dev_dbg("%s: value=%d\n", __func__, val); + + mutex_lock(&aw8624->lock); + aw8624->bullet_nr = val; + mutex_unlock(&aw8624->lock); + return count; +} + +static ssize_t aw8624_trig_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "trig: trig_enable=%d, trig_edge=%d", + aw8624->trig.trig_enable, aw8624->trig.trig_edge); + len += snprintf(buf + len, PAGE_SIZE - len, + "trig_polar=%d, pos_sequence=%d, neg_sequence=%d\n", + aw8624->trig.trig_polar, aw8624->trig.pos_sequence, + aw8624->trig.neg_sequence); + return len; + +} + +static ssize_t aw8624_trig_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int databuf[5] = { 0 }; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + if (!aw8624->ram_init) { + aw_dev_err("%s: ram init failed, not allow to play!\n", + __func__); + return count; + } + if (sscanf(buf, "%d %d %d %d %d", + &databuf[0], &databuf[1], + &databuf[2], &databuf[3], &databuf[4]) == 5) { + if (databuf[0] > 1) + databuf[0] = 1; + if (databuf[0] < 0) + databuf[0] = 0; + if (databuf[1] > 1) + databuf[0] = 1; + if (databuf[1] < 0) + databuf[0] = 0; + if (databuf[2] > 1) + databuf[0] = 1; + if (databuf[2] < 0) + databuf[0] = 0; + if (databuf[3] > aw8624->ram.ram_num || + databuf[4] > aw8624->ram.ram_num) { + aw_dev_err("%s: input seq value out of range!\n", + __func__); + return count; + } + aw8624->trig.trig_enable = databuf[0]; + aw8624->trig.trig_edge = databuf[1]; + aw8624->trig.trig_polar = databuf[2]; + aw8624->trig.pos_sequence = databuf[3]; + aw8624->trig.neg_sequence = databuf[4]; + mutex_lock(&aw8624->lock); + aw8624_haptic_tirg1_param_config(aw8624); + mutex_unlock(&aw8624->lock); + } else + aw_dev_err("%s: please input five parameters\n", + __func__); + return count; +} + +static ssize_t aw8624_ram_num_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + aw8624_haptic_get_ram_number(aw8624); + + len += snprintf(buf + len, PAGE_SIZE - len, + "ram_num = %d\n", aw8624->ram.ram_num); + return len; + +} + +static ssize_t aw8624_awrw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + unsigned char reg_num = aw8624->aw_i2c_package.reg_num; + unsigned char flag = aw8624->aw_i2c_package.flag; + unsigned char *reg_data = aw8624->aw_i2c_package.reg_data; + unsigned char i = 0; + ssize_t len = 0; + + + if (!reg_num) { + aw_dev_err("%s: awrw parameter error\n", + __func__); + return len; + } + if (flag == AW8624_READ) { + for (i = 0; i < reg_num; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%02x,", reg_data[i]); + } + + len += snprintf(buf + len - 1, PAGE_SIZE - len, "\n"); + } + + return len; +} + +static ssize_t aw8624_awrw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int datatype[3] = { 0 }; + int ret = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw8624 *aw8624 = container_of(cdev, struct aw8624, vib_dev); + + if (sscanf(buf, "%x %x %x", &datatype[0], &datatype[1], + &datatype[2]) == 3) { + if (!datatype[1]) { + aw_dev_err("%s: awrw parameter error\n", + __func__); + return count; + } + aw8624->aw_i2c_package.flag = (unsigned char)datatype[0]; + aw8624->aw_i2c_package.reg_num = (unsigned char)datatype[1]; + aw8624->aw_i2c_package.first_addr = (unsigned char)datatype[2]; + + if (aw8624->aw_i2c_package.flag == AW8624_WRITE) { + ret = aw8624_parse_data(aw8624, buf); + if (ret < 0) + return count; + aw8624_i2c_write(aw8624, + aw8624->aw_i2c_package.first_addr, + aw8624->aw_i2c_package.reg_data, + aw8624->aw_i2c_package.reg_num); + } + if (aw8624->aw_i2c_package.flag == AW8624_READ) + aw8624_i2c_read(aw8624, + aw8624->aw_i2c_package.first_addr, + aw8624->aw_i2c_package.reg_data, + aw8624->aw_i2c_package.reg_num); + } else + aw_dev_err("%s: missing number of parameters\n", + __func__); + + return count; +} + +#if 0 +/* +* schedule_work is low priority. +* aw8624 rtp mode can`t be interrupted. +*/ +static irqreturn_t aw8624_irq(int irq, void *data) +{ + struct aw8624 *aw8624 = (struct aw8624 *)data; + + schedule_work(&aw8624->irq_work); + return IRQ_HANDLED; +} + +#endif +/***************************************************** + * + * device tree + * + *****************************************************/ +static DEVICE_ATTR(state, 0664, aw8624_state_show, aw8624_state_store); +static DEVICE_ATTR(duration, 0664, aw8624_duration_show, aw8624_duration_store); +static DEVICE_ATTR(activate, 0664, aw8624_activate_show, aw8624_activate_store); +static DEVICE_ATTR(activate_mode, 0664, + aw8624_activate_mode_show, aw8624_activate_mode_store); +static DEVICE_ATTR(index, 0664, aw8624_index_show, aw8624_index_store); +static DEVICE_ATTR(gain, 0664, aw8624_gain_show, aw8624_gain_store); +static DEVICE_ATTR(seq, 0664, aw8624_seq_show, aw8624_seq_store); +static DEVICE_ATTR(loop, 0664, aw8624_loop_show, aw8624_loop_store); +static DEVICE_ATTR(register, 0664, aw8624_reg_show, aw8624_reg_store); +static DEVICE_ATTR(ram_update, 0664, + aw8624_ram_update_show, aw8624_ram_update_store); +static DEVICE_ATTR(f0, 0664, aw8624_f0_show, aw8624_f0_store); +static DEVICE_ATTR(cali, 0664, aw8624_cali_show, aw8624_cali_store); +static DEVICE_ATTR(cont, 0664, aw8624_cont_show, aw8624_cont_store); +static DEVICE_ATTR(cont_td, 0664, aw8624_cont_td_show, aw8624_cont_td_store); +static DEVICE_ATTR(cont_drv, 0664, aw8624_cont_drv_show, aw8624_cont_drv_store); +static DEVICE_ATTR(cont_num_brk, 0664, + aw8624_cont_num_brk_show, aw8624_cont_num_brk_store); +static DEVICE_ATTR(cont_zc_thr, 0664, + aw8624_cont_zc_thr_show, aw8624_cont_zc_thr_store); +static DEVICE_ATTR(vbat_monitor, 0664, + aw8624_vbat_monitor_show, aw8624_vbat_monitor_store); +static DEVICE_ATTR(lra_resistance, 0664, + aw8624_lra_resistance_show, aw8624_lra_resistance_store); +static DEVICE_ATTR(prctmode, 0664, aw8624_prctmode_show, aw8624_prctmode_store); +static DEVICE_ATTR(haptic_audio, 0664, + aw8624_haptic_audio_show, aw8624_haptic_audio_store); +static DEVICE_ATTR(haptic_audio_time, 0664, + aw8624_haptic_audio_time_show, aw8624_haptic_audio_time_store); +static DEVICE_ATTR(ram_vbat_comp, 0664, + aw8624_ram_vbat_comp_show, aw8624_ram_vbat_comp_store); +static DEVICE_ATTR(rtp, 0664, aw8624_rtp_show, aw8624_rtp_store); +static DEVICE_ATTR(osc_cali, 0664, aw8624_osc_cali_show, aw8624_osc_cali_store); +static DEVICE_ATTR(gun_type, 0664, aw8624_gun_type_show, aw8624_gun_type_store); +static DEVICE_ATTR(bullet_nr, 0664, + aw8624_bullet_nr_show, aw8624_bullet_nr_store); +static DEVICE_ATTR(trig, 0664, + aw8624_trig_show, aw8624_trig_store); +static DEVICE_ATTR(ram_num, 0664, aw8624_ram_num_show, NULL); +static DEVICE_ATTR(awrw, 0644, aw8624_awrw_show, aw8624_awrw_store); +static struct attribute *aw8624_vibrator_attributes[] = { + &dev_attr_state.attr, + &dev_attr_duration.attr, + &dev_attr_activate.attr, + &dev_attr_activate_mode.attr, + &dev_attr_index.attr, + &dev_attr_gain.attr, + &dev_attr_seq.attr, + &dev_attr_loop.attr, + &dev_attr_register.attr, + &dev_attr_ram_update.attr, + &dev_attr_f0.attr, + &dev_attr_cali.attr, + &dev_attr_cont.attr, + &dev_attr_cont_td.attr, + &dev_attr_cont_drv.attr, + &dev_attr_cont_num_brk.attr, + &dev_attr_cont_zc_thr.attr, + &dev_attr_vbat_monitor.attr, + &dev_attr_lra_resistance.attr, + &dev_attr_prctmode.attr, + &dev_attr_haptic_audio.attr, + &dev_attr_ram_vbat_comp.attr, + &dev_attr_haptic_audio_time.attr, + &dev_attr_rtp.attr, + &dev_attr_osc_cali.attr, + &dev_attr_gun_type.attr, + &dev_attr_bullet_nr.attr, + &dev_attr_trig.attr, + &dev_attr_ram_num.attr, + &dev_attr_awrw.attr, + NULL +}; + +struct attribute_group aw8624_vibrator_attribute_group = { + .attrs = aw8624_vibrator_attributes +}; +int aw8624_vibrator_init(struct aw8624 *aw8624) +{ + int ret = 0; + + aw_dev_dbg("%s enter\n", __func__); + +#ifdef TIMED_OUTPUT + aw8624->vib_dev.name = "awinic_vibrator"; + aw8624->vib_dev.get_time = aw8624_vibrator_get_time; + aw8624->vib_dev.enable = aw8624_vibrator_enable; + + ret = timed_output_dev_register(&(aw8624->vib_dev)); + if (ret < 0) { + dev_err(aw8624->dev, "%s: fail to create timed output dev\n", + __func__); + return ret; + } + ret = sysfs_create_group(&aw8624->vib_dev.dev->kobj, + &aw8624_vibrator_attribute_group); + if (ret < 0) { + dev_err(aw8624->dev, + "%s error creating sysfs attr files\n", + __func__); + return ret; + } +#else + aw8624->vib_dev.name = "awinic_vibrator"; + aw8624->vib_dev.brightness_set = aw8624_vibrator_enable; + + + ret = devm_led_classdev_register(&aw8624->i2c->dev, &(aw8624->vib_dev)); + if (ret < 0) { + dev_err(aw8624->dev, "%s: fail to create leds dev\n", + __func__); + return ret; + } + + ret = sysfs_create_group(&aw8624->vib_dev.dev->kobj, + &aw8624_vibrator_attribute_group); + if (ret < 0) { + dev_err(aw8624->dev, "%s error creating sysfs attr files\n", + __func__); + return ret; + } +#endif + hrtimer_init(&aw8624->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + aw8624->timer.function = aw8624_vibrator_timer_func; + INIT_WORK(&aw8624->vibrator_work, aw8624_vibrator_work_routine); + + if (aw8624->IsUsedIRQ) + INIT_WORK(&aw8624->rtp_work, aw8624_rtp_work_routine); + + mutex_init(&aw8624->lock); + mutex_init(&aw8624->rtp_lock); + mutex_init(&aw8624->ram_lock); + + + return 0; +} + + +static int aw8624_is_rtp_load_end(struct aw8624 *aw8624) +{ + unsigned char glb_st = 0; + int ret = -1; + + aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, &glb_st, + AW_I2C_BYTE_ONE); + if ((aw8624->rtp_cnt == aw8624->rtp_container->len) || + ((glb_st & 0x0f) == 0x00)) { + if (aw8624->rtp_cnt == + aw8624->rtp_container->len) + aw_dev_info("%s:rtp load completely.", + __func__); + else + aw_dev_err("%s rtp load failed!!\n", + __func__); + aw8624_haptic_set_rtp_aei(aw8624, + false); + aw8624->rtp_cnt = 0; + aw8624->rtp_init = 0; + ret = 0; + } + return ret; +} + +static int aw8624_write_rtp_data(struct aw8624 *aw8624) +{ + unsigned int buf_len = 0; + int ret = -1; + + if (!aw8624->rtp_cnt) { + aw_dev_info("%s:aw8624->rtp_cnt is 0!\n", + __func__); + return ret; + } + if (!aw8624->rtp_container) { + aw_dev_info("%s:aw8624->rtp_container is null, break!\n", + __func__); + return ret; + } + if ((aw8624->rtp_container->len - aw8624->rtp_cnt) < + (aw8624->ram.base_addr >> 2)) { + buf_len = + aw8624->rtp_container->len - aw8624->rtp_cnt; + } else { + buf_len = (aw8624->ram.base_addr >> 2); + } + aw8624_i2c_write(aw8624, AW8624_REG_RTP_DATA, + &aw8624->rtp_container->data[aw8624->rtp_cnt], + buf_len); + aw8624->rtp_cnt += buf_len; + + return 0; +} + + +irqreturn_t aw8624_irq(int irq, void *data) +{ + unsigned char reg_val = 0; + unsigned char glb_st = 0; + int ret = 0; + struct aw8624 *aw8624 = data; + + aw8624_i2c_read(aw8624, AW8624_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + if (reg_val & AW8624_BIT_SYSINT_UVLI) + aw_dev_err("%s chip uvlo int error\n", __func__); + if (reg_val & AW8624_BIT_SYSINT_OCDI) + aw_dev_err("%s chip over current int error\n", + __func__); + if (reg_val & AW8624_BIT_SYSINT_OTI) + aw_dev_err("%s chip over temperature int error\n", + __func__); + if (reg_val & AW8624_BIT_SYSINT_DONEI) + aw_dev_info("%s chip playback done\n", __func__); + + + if (reg_val & AW8624_BIT_SYSINT_UVLI) { + aw8624_i2c_read(aw8624, AW8624_REG_GLB_STATE, &glb_st, + AW_I2C_BYTE_ONE); + if (glb_st == AW8624_BIT_GLBRD5_STATE_STANDBY) { + aw8624_i2c_write_bits(aw8624, + AW8624_REG_SYSINTM, + AW8624_BIT_SYSINTM_UVLO_MASK, + AW8624_BIT_SYSINTM_UVLO_OFF); + } + } + + if (reg_val & AW8624_BIT_SYSINT_FF_AEI) { + aw_dev_dbg("%s: aw8624 rtp fifo almost empty\n", + __func__); + if (aw8624->rtp_init) { + while ((!aw8624_haptic_rtp_get_fifo_afs(aw8624)) && + (aw8624->play_mode == AW8624_HAPTIC_RTP_MODE)) { + mutex_lock(&aw8624->rtp_lock); +#ifdef AW_ENABLE_RTP_PRINT_LOG + aw_dev_info("%s: aw8624 rtp mode fifo update, cnt=%d\n", + __func__, aw8624->rtp_cnt); +#endif + ret = aw8624_write_rtp_data(aw8624); + if (ret < 0) { + mutex_unlock(&aw8624->rtp_lock); + break; + } + ret = aw8624_is_rtp_load_end(aw8624); + if (!ret) { + mutex_unlock(&aw8624->rtp_lock); + break; + } + mutex_unlock(&aw8624->rtp_lock); + } + } else { + aw_dev_err( "%s: aw8624 rtp init = %d, init error\n", + __func__, aw8624->rtp_init); + } + } + + if (reg_val & AW8624_BIT_SYSINT_FF_AFI) + aw_dev_dbg("%s: aw8624 rtp mode fifo full\n", __func__); + + if (aw8624->play_mode != AW8624_HAPTIC_RTP_MODE) + aw8624_haptic_set_rtp_aei(aw8624, false); + + return IRQ_HANDLED; +} + +static int aw8624_analyse_duration_range(struct aw8624 *aw8624) +{ + int i = 0; + int ret = 0; + int len = 0; + int *duration_time = NULL; + + len = ARRAY_SIZE(aw8624_dts_data.aw8624_duration_time); + duration_time = aw8624_dts_data.aw8624_duration_time; + if (len < 2) { + aw_dev_err("%s: duration time range error\n", __func__); + return -ERANGE; + } + for (i = (len - 1); i > 0; i--) { + if (duration_time[i] > duration_time[i-1]) + continue; + else + break; + + } + if (i > 0) { + aw_dev_err("%s: duration time range error\n", __func__); + ret = -ERANGE; + } + return ret; +} + +static int +aw8624_analyse_duration_array_size(struct aw8624 *aw8624, + struct device_node *np) +{ + int ret = 0; + + ret = of_property_count_elems_of_size(np, "aw8624_vib_duration_time", + 4); + if (ret < 0) { + aw8624->duration_time_flag = -1; + aw_dev_info("%s vib_duration_time not found\n", __func__); + return ret; + } + aw8624->duration_time_size = ret; + if (aw8624->duration_time_size > 3) { + aw8624->duration_time_flag = -1; + aw_dev_info("%s vib_duration_time error, array size = %d\n", + __func__, aw8624->duration_time_size); + return -ERANGE; + } + return 0; +} + +int aw8624_parse_dt(struct aw8624 *aw8624, struct device *dev, + struct device_node *np) { + unsigned int val = 0; + /*unsigned int brake_ram_config[24];*/ + unsigned int brake_cont_config[24]; + unsigned int f0_trace_parameter[4]; + unsigned int bemf_config[4]; + unsigned int duration_time[3]; + unsigned int sw_brake[2]; + unsigned int trig_config_temp[5]; + int ret = 0; + + val = + of_property_read_u32(np, "aw8624_vib_lk_f0_cali", + &aw8624_dts_data.aw8624_lk_f0_cali); + if (val != 0) + aw_dev_info("aw8624_vib_mode not found\n"); + aw_dev_info("%s: aw8624_vib_lk_f0_cali = 0x%02x\n", + __func__, aw8624_dts_data.aw8624_lk_f0_cali); + val = + of_property_read_u32(np, "aw8624_vib_mode", + &aw8624_dts_data.aw8624_mode); + if (val != 0) + aw_dev_info("aw8624_vib_mode not found\n"); + val = + of_property_read_u32(np, "aw8624_vib_f0_pre", + &aw8624_dts_data.aw8624_f0_pre); + if (val != 0) + aw_dev_info("aw8624_vib_f0_pre not found\n"); + val = + of_property_read_u32(np, "aw8624_vib_f0_cali_percen", + &aw8624_dts_data.aw8624_f0_cali_percen); + if (val != 0) + aw_dev_info("aw8624_vib_f0_cali_percen not found\n"); + val = + of_property_read_u32(np, "aw8624_vib_cont_drv_lev", + &aw8624_dts_data.aw8624_cont_drv_lvl); + if (val != 0) + aw_dev_info("aw8624_vib_cont_drv_lev not found\n"); + val = + of_property_read_u32(np, "aw8624_vib_cont_drv_lvl_ov", + &aw8624_dts_data.aw8624_cont_drv_lvl_ov); + if (val != 0) + aw_dev_info("aw8624_vib_cont_drv_lvl_ov not found\n"); + val = + of_property_read_u32(np, "aw8624_vib_cont_td", + &aw8624_dts_data.aw8624_cont_td); + if (val != 0) + aw_dev_info("aw8624_vib_cont_td not found\n"); + val = + of_property_read_u32(np, "aw8624_vib_cont_zc_thr", + &aw8624_dts_data.aw8624_cont_zc_thr); + if (val != 0) + aw_dev_info("aw8624_vib_cont_zc_thr not found\n"); + val = + of_property_read_u32(np, "aw8624_vib_cont_num_brk", + &aw8624_dts_data.aw8624_cont_num_brk); + if (val != 0) + aw_dev_info("aw8624_vib_cont_num_brk not found\n"); + val = + of_property_read_u32(np, "aw8624_vib_f0_coeff", + &aw8624_dts_data.aw8624_f0_coeff); + if (val != 0) + aw_dev_info("aw8624_vib_f0_coeff not found\n"); + val = of_property_read_u32_array(np, "aw8624_vib_brake_cont_config", + brake_cont_config, ARRAY_SIZE(brake_cont_config)); + if (val != 0) + aw_dev_info("%s vib_brake_cont_config not found\n", __func__); + memcpy(aw8624_dts_data.aw8624_cont_brake, + brake_cont_config, sizeof(brake_cont_config)); + + val = of_property_read_u32_array(np, "aw8624_vib_f0_trace_parameter", + f0_trace_parameter, ARRAY_SIZE(f0_trace_parameter)); + if (val != 0) + aw_dev_info("%s vib_f0_trace_parameter not found\n", __func__); + memcpy(aw8624_dts_data.aw8624_f0_trace_parameter, + f0_trace_parameter, sizeof(f0_trace_parameter)); + + val = of_property_read_u32_array(np, "aw8624_vib_bemf_config", + bemf_config, ARRAY_SIZE(bemf_config)); + if (val != 0) + aw_dev_info("%s vib_bemf_config not found\n", __func__); + memcpy(aw8624_dts_data.aw8624_bemf_config, + bemf_config, sizeof(bemf_config)); + + val = + of_property_read_u32_array(np, "aw8624_vib_sw_brake", + sw_brake, ARRAY_SIZE(sw_brake)); + if (val != 0) + aw_dev_info("%s vib_wavseq not found\n", __func__); + memcpy(aw8624_dts_data.aw8624_sw_brake, + sw_brake, sizeof(sw_brake)); + + val = of_property_read_u32(np, "aw8624_vib_tset", + &aw8624_dts_data.aw8624_tset); + if (val != 0) + aw_dev_info("%s vib_tset not found\n", __func__); + val = of_property_read_u32_array(np, "aw8624_vib_duration_time", + duration_time, ARRAY_SIZE(duration_time)); + if (val != 0) + aw_dev_info("%s vib_duration_time not found\n", __func__); + ret = aw8624_analyse_duration_array_size(aw8624, np); + if (!ret) + memcpy(aw8624_dts_data.aw8624_duration_time, + duration_time, sizeof(duration_time)); + val = + of_property_read_u32_array(np, + "aw8624_vib_trig_config", + trig_config_temp, + ARRAY_SIZE(trig_config_temp)); + if (val != 0) + aw_dev_info("%s vib_trig_config not found\n", + __func__); + memcpy(aw8624_dts_data.trig_config, trig_config_temp, + sizeof(trig_config_temp)); + return 0; +} diff --git a/drivers/misc/awinic/aw862x/aw8624.h b/drivers/misc/awinic/aw862x/aw8624.h new file mode 100644 index 000000000000..ba7cd5fec37c --- /dev/null +++ b/drivers/misc/awinic/aw862x/aw8624.h @@ -0,0 +1,652 @@ +#ifndef _AW8624_H_ +#define _AW8624_H_ + +#include +#include +#include +#include +#include +#include +#include "haptic_nv.h" + +/********************************************************* + * + * marco + * + ********************************************************/ +#define AW8624_I2C_RETRIES (2) +#define AW8624_RTP_NAME_MAX (64) +#define AW8624_SEQUENCER_SIZE (8) +#define AW8624_SEQUENCER_LOOP_SIZE (4) +#define AW8624_OSC_CALIBRATION_T_LENGTH (5100000) +#define AW8624_PM_QOS_VALUE_VB (400) +#define AW8624_VBAT_REFER (4200) +#define AW8624_VBAT_MIN (3000) +#define AW8624_VBAT_MAX (4500) +#define AW8624_HAPTIC_NAME ("aw8624_haptic") +#define AW8624_SET_AEADDR_H(addr) ((((addr) >> 1) >> 8)) +#define AW8624_SET_AEADDR_L(addr) (((addr) >> 1) & 0x00FF) +#define AW8624_SET_AFADDR_H(addr) (((addr) - ((addr) >> 2)) >> 8) +#define AW8624_SET_AFADDR_L(addr) (((addr) - ((addr) >> 2)) & 0x00FF) +#define AW8624_F0_FORMULA(f0_reg, f0_coeff) (1000000000 / ((f0_reg) * (f0_coeff))) +#define AW8624_VBAT_FORMULA(reg_val) (6100 * (reg_val) / 256) +#define AW8624_LRA_FORMULA(reg_val) (298 * (reg_val)) +#define AW8624_VBAT_DELAY_MIN (2000) +#define AW8624_VBAT_DELAY_MAX (2500) +#define AW8624_LRA_DELAY_MIN (3000) +#define AW8624_LRA_DELAY_MAX (3500) +#define AW8624_STOP_DELAY_MIN (2000) +#define AW8624_STOP_DELAY_MAX (2500) +#define AW8624_F0_DELAY_MIN (10000) +#define AW8624_F0_DELAY_MAX (10500) +#define AW8624_MUL_GET_F0_RANGE (150) +#define AW8624_MUL_GET_F0_NUM (3) +/********************************************************* + * + * enum + * + ********************************************************/ +enum aw8624_flags { + AW8624_FLAG_NONR = 0, + AW8624_FLAG_SKIP_INTERRUPTS = 1, +}; + +enum aw8624_chipids { + AW8624_ID = 1, +}; + +enum aw8624_haptic_read_write { + AW8624_HAPTIC_CMD_READ_REG = 0, + AW8624_HAPTIC_CMD_WRITE_REG = 1, +}; + +enum aw8624_haptic_play { + AW8624_HAPTIC_PLAY_NULL = 0, + AW8624_HAPTIC_PLAY_ENABLE = 1, + AW8624_HAPTIC_PLAY_STOP = 2, + AW8624_HAPTIC_PLAY_GAIN = 8, +}; + +enum aw8624_haptic_cmd { + AW8624_HAPTIC_CMD_NULL = 0, + AW8624_HAPTIC_CMD_ENABLE = 1, + AW8624_HAPTIC_CMD_HAPTIC = 0x0f, + AW8624_HAPTIC_CMD_TP = 0x10, + AW8624_HAPTIC_CMD_SYS = 0xf0, + AW8624_HAPTIC_CMD_STOP = 255, +}; +enum aw8624_haptic_work_mode { + AW8624_HAPTIC_STANDBY_MODE = 0, + AW8624_HAPTIC_RAM_MODE = 1, + AW8624_HAPTIC_RAM_LOOP_MODE = 2, + AW8624_HAPTIC_CONT_MODE = 3, + AW8624_HAPTIC_RTP_MODE = 4, + AW8624_HAPTIC_TRIG_MODE = 5, + AW8624_HAPTIC_NULL = 6, +}; + +enum aw8624_haptic_bst_mode { + AW8624_HAPTIC_BYPASS_MODE = 0, + AW8624_HAPTIC_BOOST_MODE = 1, +}; + +enum aw8624_haptic_vbat_comp_mode { + AW8624_HAPTIC_VBAT_SW_COMP_MODE = 0, + AW8624_HAPTIC_VBAT_HW_COMP_MODE = 1, +}; + +enum aw8624_haptic_ram_vbat_comp_mode { + AW8624_HAPTIC_RAM_VBAT_COMP_DISABLE = 0, + AW8624_HAPTIC_RAM_VBAT_COMP_ENABLE = 1, +}; + +enum aw8624_haptic_f0_flag { + AW8624_HAPTIC_LRA_F0 = 0, + AW8624_HAPTIC_CALI_F0 = 1, +}; + +enum aw8624_haptic_pwm_mode { + AW8624_PWM_48K = 0, + AW8624_PWM_24K = 1, + AW8624_PWM_12K = 2, +}; + +enum aw8624_haptic_cali_lra { + AW8624_HAPTIC_ZERO = 0, + AW8624_HAPTIC_F0_CALI_LRA = 1, + AW8624_HAPTIC_RTP_CALI_LRA = 2, +}; + +enum aw8624_haptic_pin { + AW8624_TRIG1 = 0, + AW8624_IRQ = 1, +}; + +enum aw8624_awrw_flag { + AW8624_WRITE = 0, + AW8624_READ = 1, +}; + +enum aw8624_pwm_clk { + AW8624_CLK_24K = 2, + AW8624_CLK_12K = 3, +}; + +/********************************************************* + * + * struct + * + ********************************************************/ +struct fileops { + unsigned char cmd; + unsigned char reg; + unsigned char ram_addrh; + unsigned char ram_addrl; +}; + +struct aw8624_trig { + unsigned char trig_enable; + unsigned char trig_edge; + unsigned char trig_polar; + unsigned char pos_sequence; + unsigned char neg_sequence; +}; + +struct aw8624_dts_info { + int aw8624_lk_f0_cali; + int aw8624_mode; + int aw8624_f0_pre; + int aw8624_f0_cali_percen; + int aw8624_cont_drv_lvl; + int aw8624_cont_drv_lvl_ov; + int aw8624_cont_td; + int aw8624_cont_zc_thr; + int aw8624_cont_num_brk; + int aw8624_f0_coeff; + int aw8624_duration_time[3]; + int aw8624_cont_brake[3][8]; + int aw8624_bemf_config[4]; + int aw8624_sw_brake[2]; + int aw8624_wavseq[16]; + int aw8624_wavloop[10]; + int aw8624_td_brake[3]; + int aw8624_tset; + unsigned int aw8624_f0_trace_parameter[4]; + unsigned int trig_config[5]; +}; + +struct aw8624 { + bool haptic_ready; + bool audio_ready; + bool IsUsedIRQ; + bool ram_update_delay; + + ktime_t current_t; + ktime_t pre_enter_t; + ktime_t kstart, kend; + + cdev_t vib_dev; + + unsigned char hwen_flag; + unsigned char flags; + unsigned char chipid; + unsigned char chipid_flag; + unsigned char singlecycle; + unsigned char play_mode; + unsigned char activate_mode; + unsigned char auto_boost; + unsigned char duration_time_size; + unsigned char cont_drv_lvl; + unsigned char cont_drv_lvl_ov; + unsigned char cont_num_brk; + unsigned char max_pos_beme; + unsigned char max_neg_beme; + unsigned char f0_cali_flag; + unsigned char ram_vbat_comp; + unsigned char rtp_init; + unsigned char ram_init; + unsigned char rtp_routine_on; + unsigned char seq[AW8624_SEQUENCER_SIZE]; + unsigned char loop[AW8624_SEQUENCER_SIZE]; + + char duration_time_flag; + + int state; + int duration; + int amplitude; + int index; + int vmax; + int gain; + int f0_value; + int pre_haptic_number; + int reset_gpio; + int irq_gpio; + int reset_gpio_ret; + int irq_gpio_ret; + + unsigned int f0; + unsigned int f0_pre; + unsigned int cont_td; + unsigned int cont_f0; + unsigned int cont_zc_thr; + unsigned int timeval_flags; + unsigned int osc_cali_flag; + unsigned int theory_time; + unsigned int rtp_len; + unsigned int gun_type; + unsigned int bullet_nr; + unsigned int rtp_cnt; + unsigned int rtp_file_num; + unsigned int vbat; + unsigned int lra; + unsigned int interval_us; + unsigned int ramupdate_flag; + unsigned int rtpupdate_flag; + unsigned int osc_cali_run; + unsigned int lra_calib_data; + unsigned int f0_calib_data; + unsigned long int microsecond; + + struct haptic_audio haptic_audio; + struct regmap *regmap; + struct i2c_client *i2c; + struct device *dev; + struct input_dev *input; + struct aw8624_container *rtp_container; + struct mutex lock; + struct mutex rtp_lock; + struct mutex ram_lock; + struct hrtimer timer; + struct work_struct vibrator_work; + struct work_struct irq_work; + struct work_struct rtp_work; + struct delayed_work ram_work; + struct delayed_work stop_work; + struct fileops fileops; + struct ram ram; + struct aw8624_trig trig; + struct aw_i2c_package aw_i2c_package; +}; + +struct aw8624_container { + int len; + unsigned char data[]; +}; + +/********************************************************* + * + * ioctl + * + ********************************************************/ +struct aw8624_seq_loop { + unsigned char loop[AW8624_SEQUENCER_SIZE]; +}; + +struct aw8624_que_seq { + unsigned char index[AW8624_SEQUENCER_SIZE]; +}; + +/********************************************************* + * + * extern + * + ********************************************************/ +extern int aw8624_parse_dt(struct aw8624 *aw8624, struct device *dev, + struct device_node *np); +extern void aw8624_interrupt_setup(struct aw8624 *aw8624); +extern int aw8624_vibrator_init(struct aw8624 *aw8624); +extern int aw8624_haptic_init(struct aw8624 *aw8624); +extern int aw8624_ram_init(struct aw8624 *aw8624); +extern irqreturn_t aw8624_irq(int irq, void *data); +extern struct attribute_group aw8624_vibrator_attribute_group; + +/******************************************** + * Register List + *******************************************/ +#define AW8624_REG_ID (0x00) +#define AW8624_REG_SYSST (0x01) +#define AW8624_REG_SYSINT (0x02) +#define AW8624_REG_SYSINTM (0x03) +#define AW8624_REG_SYSCTRL (0x04) +#define AW8624_REG_GO (0x05) +#define AW8624_REG_RTP_DATA (0x06) +#define AW8624_REG_WAVSEQ1 (0x07) +#define AW8624_REG_WAVSEQ2 (0x08) +#define AW8624_REG_WAVSEQ3 (0x09) +#define AW8624_REG_WAVSEQ4 (0x0a) +#define AW8624_REG_WAVSEQ5 (0x0b) +#define AW8624_REG_WAVSEQ6 (0x0c) +#define AW8624_REG_WAVSEQ7 (0x0d) +#define AW8624_REG_WAVSEQ8 (0x0e) +#define AW8624_REG_WAVLOOP1 (0x0f) +#define AW8624_REG_WAVLOOP2 (0x10) +#define AW8624_REG_WAVLOOP3 (0x11) +#define AW8624_REG_WAVLOOP4 (0x12) +#define AW8624_REG_MANLOOP (0x13) +#define AW8624_REG_TRG1_SEQP (0x14) +#define AW8624_REG_TRG1_SEQN (0x17) +#define AW8624_REG_TRG_CFG1 (0x1b) +#define AW8624_REG_TRG_CFG2 (0x1c) +#define AW8624_REG_DBGCTRL (0x20) +#define AW8624_REG_BASE_ADDRH (0x21) +#define AW8624_REG_BASE_ADDRL (0x22) +#define AW8624_REG_FIFO_AEH (0x23) +#define AW8624_REG_FIFO_AEL (0x24) +#define AW8624_REG_FIFO_AFH (0x25) +#define AW8624_REG_FIFO_AFL (0x26) +#define AW8624_REG_DATCTRL (0x2b) +#define AW8624_REG_PWMPRC (0x2d) +#define AW8624_REG_PWMDBG (0x2e) +#define AW8624_REG_DBGSTAT (0x30) +#define AW8624_REG_WAVECTRL (0x31) +#define AW8624_REG_BRAKE0_CTRL (0x32) +#define AW8624_REG_BRAKE1_CTRL (0x33) +#define AW8624_REG_BRAKE2_CTRL (0x34) +#define AW8624_REG_BRAKE_NUM (0x35) +#define AW8624_REG_ANACTRL (0x38) +#define AW8624_REG_SW_BRAKE (0x39) +#define AW8624_REG_DATDBG (0x3b) +#define AW8624_REG_PRLVL (0x3e) +#define AW8624_REG_PRTIME (0x3f) +#define AW8624_REG_RAMADDRH (0x40) +#define AW8624_REG_RAMADDRL (0x41) +#define AW8624_REG_RAMDATA (0x42) +#define AW8624_REG_BRA_MAX_NUM (0x44) +#define AW8624_REG_GLB_STATE (0x47) +#define AW8624_REG_CONT_CTRL (0x48) +#define AW8624_REG_F_PRE_H (0x49) +#define AW8624_REG_F_PRE_L (0x4a) +#define AW8624_REG_TD_H (0x4b) +#define AW8624_REG_TD_L (0x4c) +#define AW8624_REG_TSET (0x4d) +#define AW8624_REG_THRS_BRA_END (0x4f) +#define AW8624_REG_EF_RDATAH (0x55) +#define AW8624_REG_TRIM_LRA (0x5b) +#define AW8624_REG_R_SPARE (0x5d) +#define AW8624_REG_D2SCFG (0x5e) +#define AW8624_REG_DETCTRL (0x5f) +#define AW8624_REG_RLDET (0x60) +#define AW8624_REG_OSDET (0x61) +#define AW8624_REG_VBATDET (0x62) +#define AW8624_REG_ADCTEST (0x66) +#define AW8624_REG_F_LRA_F0_H (0x68) +#define AW8624_REG_F_LRA_F0_L (0x69) +#define AW8624_REG_F_LRA_CONT_H (0x6a) +#define AW8624_REG_F_LRA_CONT_L (0x6b) +#define AW8624_REG_WAIT_VOL_MP (0x6e) +#define AW8624_REG_WAIT_VOL_MN (0x6f) +#define AW8624_REG_ZC_THRSH_H (0x72) +#define AW8624_REG_ZC_THRSH_L (0x73) +#define AW8624_REG_BEMF_VTHH_H (0x74) +#define AW8624_REG_BEMF_VTHH_L (0x75) +#define AW8624_REG_BEMF_VTHL_H (0x76) +#define AW8624_REG_BEMF_VTHL_L (0x77) +#define AW8624_REG_BEMF_NUM (0x78) +#define AW8624_REG_DRV_TIME (0x79) +#define AW8624_REG_TIME_NZC (0x7a) +#define AW8624_REG_DRV_LVL (0x7b) +#define AW8624_REG_DRV_LVL_OV (0x7c) +#define AW8624_REG_NUM_F0_1 (0x7d) +#define AW8624_REG_NUM_F0_2 (0x7e) +#define AW8624_REG_NUM_F0_3 (0x7f) +/****************************************************** + * Register Detail + *****************************************************/ + /* SYSST 0x01 */ +#define AW8624_BIT_SYSST_OVS (1<<6) +#define AW8624_BIT_SYSST_UVLS (1<<5) +#define AW8624_BIT_SYSST_FF_AES (1<<4) +#define AW8624_BIT_SYSST_FF_AFS (1<<3) +#define AW8624_BIT_SYSST_OCDS (1<<2) +#define AW8624_BIT_SYSST_OTS (1<<1) +#define AW8624_BIT_SYSST_DONES (1<<0) + + /* SYSINT 0x02 */ +#define AW8624_BIT_SYSINT_OVI (1<<6) +#define AW8624_BIT_SYSINT_UVLI (1<<5) +#define AW8624_BIT_SYSINT_FF_AEI (1<<4) +#define AW8624_BIT_SYSINT_FF_AFI (1<<3) +#define AW8624_BIT_SYSINT_OCDI (1<<2) +#define AW8624_BIT_SYSINT_OTI (1<<1) +#define AW8624_BIT_SYSINT_DONEI (1<<0) + + /* SYSINTM 0x03 */ +#define AW8624_BIT_SYSINTM_OV_MASK (~(1<<6)) +#define AW8624_BIT_SYSINTM_OV_OFF (1<<6) +#define AW8624_BIT_SYSINTM_OV_EN (0<<6) +#define AW8624_BIT_SYSINTM_UVLO_MASK (~(1<<5)) +#define AW8624_BIT_SYSINTM_UVLO_OFF (1<<5) +#define AW8624_BIT_SYSINTM_UVLO_EN (0<<5) +#define AW8624_BIT_SYSINTM_FF_AE_MASK (~(1<<4)) +#define AW8624_BIT_SYSINTM_FF_AE_OFF (1<<4) +#define AW8624_BIT_SYSINTM_FF_AE_EN (0<<4) +#define AW8624_BIT_SYSINTM_FF_AF_MASK (~(1<<3)) +#define AW8624_BIT_SYSINTM_FF_AF_OFF (1<<3) +#define AW8624_BIT_SYSINTM_FF_AF_EN (0<<3) +#define AW8624_BIT_SYSINTM_OCD_MASK (~(1<<2)) +#define AW8624_BIT_SYSINTM_OCD_OFF (1<<2) +#define AW8624_BIT_SYSINTM_OCD_EN (0<<2) +#define AW8624_BIT_SYSINTM_OT_MASK (~(1<<1)) +#define AW8624_BIT_SYSINTM_OT_OFF (1<<1) +#define AW8624_BIT_SYSINTM_OT_EN (0<<1) +#define AW8624_BIT_SYSINTM_DONE_MASK (~(1<<0)) +#define AW8624_BIT_SYSINTM_DONE_OFF (1<<0) +#define AW8624_BIT_SYSINTM_DONE_EN (0<<0) + + /* SYSCTRL 0x04 */ +#define AW8624_BIT_SYSCTRL_WAVDAT_MODE_MASK (~(3<<6)) +#define AW8624_BIT_SYSCTRL_WAVDAT_MODE_4X (3<<6) +#define AW8624_BIT_SYSCTRL_WAVDAT_MODE_2X (0<<6) +#define AW8624_BIT_SYSCTRL_WAVDAT_MODE_1X (1<<6) +#define AW8624_BIT_SYSCTRL_RAMINIT_MASK (~(1<<5)) +#define AW8624_BIT_SYSCTRL_RAMINIT_EN (1<<5) +#define AW8624_BIT_SYSCTRL_RAMINIT_OFF (0<<5) +#define AW8624_BIT_SYSCTRL_PLAY_MODE_MASK (~(3<<2)) +#define AW8624_BIT_SYSCTRL_PLAY_MODE_CONT (2<<2) +#define AW8624_BIT_SYSCTRL_PLAY_MODE_RTP (1<<2) +#define AW8624_BIT_SYSCTRL_PLAY_MODE_RAM (0<<2) +#define AW8624_BIT_SYSCTRL_WORK_MODE_MASK (~(1<<0)) +#define AW8624_BIT_SYSCTRL_STANDBY (1<<0) +#define AW8624_BIT_SYSCTRL_ACTIVE (0<<0) + + /* GO 0x05 */ +#define AW8624_BIT_GO_MASK (~(1<<0)) +#define AW8624_BIT_GO_ENABLE (1<<0) +#define AW8624_BIT_GO_DISABLE (0<<0) + + /* WAVSEQ1 0x07 */ +#define AW8624_BIT_WAVSEQ1_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ1_WAV_FRM_SEQ1_MASK (~(127<<0)) + + /* WAVSEQ2 0x08 */ +#define AW8624_BIT_WAVSEQ2_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ2_WAV_FRM_SEQ2_MASK (~(127<<0)) + + /* WAVSEQ3 0x09 */ +#define AW8624_BIT_WAVSEQ3_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ3_WAV_FRM_SEQ3_MASK (~(127<<0)) + + /* WAVSEQ4 0x0A */ +#define AW8624_BIT_WAVSEQ4_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ4_WAV_FRM_SEQ4_MASK (~(127<<0)) + + /* WAVSEQ5 0X0B */ +#define AW8624_BIT_WAVSEQ5_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ5_WAV_FRM_SEQ5_MASK (~(127<<0)) + + /* WAVSEQ6 0X0C */ +#define AW8624_BIT_WAVSEQ6_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ6_WAV_FRM_SEQ6_MASK (~(127<<0)) + + /* WAVSEQ7 */ +#define AW8624_BIT_WAVSEQ7_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ7_WAV_FRM_SEQ7_MASK (~(127<<0)) + + /* WAVSEQ8 */ +#define AW8624_BIT_WAVSEQ8_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ8_WAV_FRM_SEQ8_MASK (~(127<<0)) + + /* WAVLOOP */ +#define AW8624_BIT_WAVLOOP_SEQN_MASK (~(15<<4)) +#define AW8624_BIT_WAVLOOP_SEQNP1_MASK (~(15<<0)) +#define AW8624_BIT_WAVLOOP_INIFINITELY (15<<0) + + /* WAVLOOP1 */ +#define AW8624_BIT_WAVLOOP1_SEQ1_MASK (~(15<<4)) +#define AW8624_BIT_WAVLOOP1_SEQ2_MASK (~(15<<0)) + + /* WAVLOOP2 */ +#define AW8624_BIT_WAVLOOP2_SEQ3_MASK (~(15<<4)) +#define AW8624_BIT_WAVLOOP2_SEQ4_MASK (~(15<<0)) + + /* WAVLOOP3 */ +#define AW8624_BIT_WAVLOOP3_SEQ5_MASK (~(15<<4)) +#define AW8624_BIT_WAVLOOP3_SEQ6_MASK (~(15<<0)) + + /* WAVLOOP4 */ +#define AW8624_BIT_WAVLOOP4_SEQ7_MASK (~(15<<4)) +#define AW8624_BIT_WAVLOOP4_SEQ8_MASK (~(15<<0)) + + /* TRGCFG1 */ +#define AW8624_BIT_TRGCFG1_TRG1_POLAR_MASK (~(1<<1)) +#define AW8624_BIT_TRGCFG1_TRG1_POLAR_NEG (1<<1) +#define AW8624_BIT_TRGCFG1_TRG1_POLAR_POS (0<<1) +#define AW8624_BIT_TRGCFG1_TRG1_EDGE_MASK (~(1<<0)) +#define AW8624_BIT_TRGCFG1_TRG1_EDGE_POS (1<<0) +#define AW8624_BIT_TRGCFG1_TRG1_EDGE_POS_NEG (0<<0) + + /* TRGCFG2 */ +#define AW8624_BIT_TRGCFG2_TRG1_ENABLE_MASK (~(1<<0)) +#define AW8624_BIT_TRGCFG2_TRG1_ENABLE (1<<0) +#define AW8624_BIT_TRGCFG2_TRG1_DISABLE (0<<0) + + /*DBGCTRL 0X20 */ +#define AW8624_BIT_DBGCTRL_INTN_TRG_SEL_MASK (~(1<<5)) +#define AW8624_BIT_DBGCTRL_INTN_SEL_ENABLE (1<<5) +#define AW8624_BIT_DBGCTRL_TRG_SEL_ENABLE (0<<5) +#define AW8624_BIT_DBGCTRL_INT_MODE_MASK (~(3<<2)) +#define AW8624_BIT_DBGCTRL_INTN_LEVEL_MODE (0<<2) +#define AW8624_BIT_DBGCTRL_INT_MODE_EDGE (1<<2) +#define AW8624_BIT_DBGCTRL_INTN_POSEDGE_MODE (2<<2) +#define AW8624_BIT_DBGCTRL_INTN_BOTH_EDGE_MODE (3<<2) + + /* DATCTRL */ +#define AW8624_BIT_DATCTRL_FC_MASK (~(1<<6)) +#define AW8624_BIT_DATCTRL_FC_1000HZ (3<<6) +#define AW8624_BIT_DATCTRL_FC_800HZ (3<<6) +#define AW8624_BIT_DATCTRL_FC_600HZ (1<<6) +#define AW8624_BIT_DATCTRL_FC_400HZ (0<<6) +#define AW8624_BIT_DATCTRL_LPF_ENABLE_MASK (~(1<<5)) +#define AW8624_BIT_DATCTRL_LPF_ENABLE (1<<5) +#define AW8624_BIT_DATCTRL_LPF_DISABLE (0<<5) + + /*PWMPRC 0X2D */ +#define AW8624_BIT_PWMPRC_PRC_EN_MASK (~(1<<7)) +#define AW8624_BIT_PWMPRC_PRC_ENABLE (1<<7) +#define AW8624_BIT_PWMPRC_PRC_DISABLE (0<<7) +#define AW8624_BIT_PWMPRC_PRCTIME_MASK (~(0x7f<<0)) + + /* PWMDBG */ +#define AW8624_BIT_PWMDBG_PWM_MODE_MASK (~(3<<5)) +#define AW8624_BIT_PWMDBG_PWM_12K (3<<5) +#define AW8624_BIT_PWMDBG_PWM_24K (2<<5) +#define AW8624_BIT_PWMDBG_PWM_48K (0<<5) + +/* GLB_STATE 0x47*/ +#define AW8624_BIT_GLBRD5_STATE_MASK (~(15<<0)) +#define AW8624_BIT_GLBRD5_STATE_STANDBY (0<<0) +#define AW8624_BIT_GLBRD5_STATE_WAKEUP (1<<0) +#define AW8624_BIT_GLBRD5_STATE_STARTUP (2<<0) +#define AW8624_BIT_GLBRD5_STATE_WAIT (3<<0) +#define AW8624_BIT_GLBRD5_STATE_CONT_GO (6<<0) +#define AW8624_BIT_GLBRD5_STATE_RAM_GO (7<<0) +#define AW8624_BIT_GLBRD5_STATE_RTP_GO (8<<0) +#define AW8624_BIT_GLBRD5_STATE_TRIG_GO (9<<0) +#define AW8624_BIT_GLBRD5_STATE_I2S_GO (10<<0) +#define AW8624_BIT_GLBRD5_STATE_BRAKE (11<<0) +#define AW8624_BIT_GLBRD5_STATE_END (12<<0) + + /* WAVECTRL */ +#define AW8624_BIT_WAVECTRL_NUM_OV_DRIVER_MASK (~(0xF<<4)) +#define AW8624_BIT_WAVECTRL_NUM_OV_DRIVER (0<<4) + + /* CONT_CTRL */ +#define AW8624_BIT_CONT_CTRL_ZC_DETEC_MASK (~(1<<7)) +#define AW8624_BIT_CONT_CTRL_ZC_DETEC_ENABLE (1<<7) +#define AW8624_BIT_CONT_CTRL_ZC_DETEC_DISABLE (0<<7) +#define AW8624_BIT_CONT_CTRL_WAIT_PERIOD_MASK (~(3<<5)) +#define AW8624_BIT_CONT_CTRL_WAIT_8PERIOD (3<<5) +#define AW8624_BIT_CONT_CTRL_WAIT_4PERIOD (2<<5) +#define AW8624_BIT_CONT_CTRL_WAIT_2PERIOD (1<<5) +#define AW8624_BIT_CONT_CTRL_WAIT_1PERIOD (0<<5) +#define AW8624_BIT_CONT_CTRL_MODE_MASK (~(1<<4)) +#define AW8624_BIT_CONT_CTRL_BY_DRV_TIME (1<<4) +#define AW8624_BIT_CONT_CTRL_BY_GO_SIGNAL (0<<4) +#define AW8624_BIT_CONT_CTRL_EN_CLOSE_MASK (~(1<<3)) +#define AW8624_BIT_CONT_CTRL_CLOSE_PLAYBACK (1<<3) +#define AW8624_BIT_CONT_CTRL_OPEN_PLAYBACK (0<<3) +#define AW8624_BIT_CONT_CTRL_F0_DETECT_MASK (~(1<<2)) +#define AW8624_BIT_CONT_CTRL_F0_DETECT_ENABLE (1<<2) +#define AW8624_BIT_CONT_CTRL_F0_DETECT_DISABLE (0<<2) +#define AW8624_BIT_CONT_CTRL_O2C_MASK (~(1<<1)) +#define AW8624_BIT_CONT_CTRL_O2C_ENABLE (1<<1) +#define AW8624_BIT_CONT_CTRL_O2C_DISABLE (0<<1) +#define AW8624_BIT_CONT_CTRL_AUTO_BRK_MASK (~(1<<0)) +#define AW8624_BIT_CONT_CTRL_AUTO_BRK_ENABLE (1<<0) +#define AW8624_BIT_CONT_CTRL_AUTO_BRK_DISABLE (0<<0) + +#define AW8624_BIT_D2SCFG_CLK_ADC_MASK (~(7<<5)) +#define AW8624_BIT_D2SCFG_CLK_ASC_1P5MHZ (3<<5) + +#define AW8624_BIT_D2SCFG_GAIN_MASK (~(7<<0)) +#define AW8624_BIT_D2SCFG_GAIN_40 (7<<0) + /* DETCTRL */ +#define AW8624_BIT_DETCTRL_RL_OS_MASK (~(1<<6)) +#define AW8624_BIT_DETCTRL_RL_DETECT (1<<6) +#define AW8624_BIT_DETCTRL_OS_DETECT (0<<6) +#define AW8624_BIT_DETCTRL_PROTECT_MASK (~(1<<5)) +#define AW8624_BIT_DETCTRL_PROTECT_NO_ACTION (1<<5) +#define AW8624_BIT_DETCTRL_PROTECT_SHUTDOWN (0<<5) +#define AW8624_BIT_DETCTRL_VBAT_GO_MASK (~(1<<1)) +#define AW8624_BIT_DETCTRL_VABT_GO_ENABLE (1<<1) +#define AW8624_BIT_DETCTRL_VBAT_GO_DISBALE (0<<1) +#define AW8624_BIT_DETCTRL_DIAG_GO_MASK (~(1<<0)) +#define AW8624_BIT_DETCTRL_DIAG_GO_ENABLE (1<<0) +#define AW8624_BIT_DETCTRL_DIAG_GO_DISABLE (0<<0) + + +#define AW8624_BIT_RAMADDRH_MASK (~(63<<0)) + + /* VBAT MODE */ +#define AW8624_BIT_DETCTRL_VBAT_MODE_MASK (~(1<<6)) +#define AW8624_BIT_DETCTRL_VBAT_HW_COMP (1<<6) +#define AW8624_BIT_DETCTRL_VBAT_SW_COMP (0<<6) + + + /* ANACTRL */ +#define AW8624_BIT_ANACTRL_LRA_SRC_MASK (~(1<<5)) +#define AW8624_BIT_ANACTRL_LRA_SRC_REG (1<<5) +#define AW8624_BIT_ANACTRL_LRA_SRC_EFUSE (0<<5) +#define AW8624_BIT_ANACTRL_EN_IO_PD1_MASK (~(1<<0)) +#define AW8624_BIT_ANACTRL_EN_IO_PD1_HIGH (1<<0) +#define AW8624_BIT_ANACTRL_EN_IO_PD1_LOW (0<<0) + +/* PRLVL */ +#define AW8624_BIT_PRLVL_PR_EN_MASK (~(1<<7)) +#define AW8624_BIT_PRLVL_PR_ENABLE (1<<7) +#define AW8624_BIT_PRLVL_PR_DISABLE (0<<7) +#define AW8624_BIT_PRLVL_PRLVL_MASK (~(0x7f<<0)) + +/* PRTIME */ +#define AW8624_BIT_PRTIME_PRTIME_MASK (~(0xff<<0)) + +#define AW8624_BIT_BEMF_NUM_BRK_MASK (~(0xf<<0)) + +/* TD_H 0x4b TD_brake */ +#define AW8624_BIT_R_SPARE_MASK (~(1<<7)) +#define AW8624_BIT_R_SPARE_ENABLE (1<<7) + +/* TIME_NZC */ +#define AW8624_BIT_TIME_NZC_DEF_VAL (0x1F) + +#endif diff --git a/drivers/misc/awinic/aw862x/haptic_nv.c b/drivers/misc/awinic/aw862x/haptic_nv.c new file mode 100644 index 000000000000..2629bc474dfd --- /dev/null +++ b/drivers/misc/awinic/aw862x/haptic_nv.c @@ -0,0 +1,617 @@ +/* + * haptic_nv.c + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "haptic_nv.h" +#include "aw8624.h" +#include "aw8622x.h" +#include "aw86214.h" + +#define HAPTIC_NV_DRIVER_VERSION ("v1.5.1") + +/****************************************************** + * + * i2c read/write + * + ******************************************************/ +static int haptic_nv_i2c_read(struct awinic *awinic, + unsigned char reg_addr, unsigned char *reg_data) +{ + unsigned char cnt = 0; + int ret = -1; + + while (cnt < HAPTIC_NV_I2C_RETRIES) { + ret = i2c_smbus_read_byte_data(awinic->i2c, reg_addr); + if (ret < 0) { + aw_dev_err("%s: i2c_read cnt=%d error=%d\n", + __func__, cnt, ret); + } else { + *reg_data = ret; + break; + } + cnt++; + usleep_range(2000, 3000); + } + + return ret; +} + +static int haptic_nv_i2c_write(struct awinic *awinic, + unsigned char reg_addr, unsigned char reg_data) +{ + unsigned char cnt = 0; + int ret = -1; + + while (cnt < HAPTIC_NV_I2C_RETRIES) { + ret = + i2c_smbus_write_byte_data(awinic->i2c, reg_addr, reg_data); + if (ret < 0) { + aw_dev_err("%s: i2c_write cnt=%d error=%d\n", + __func__, cnt, ret); + } else { + break; + } + cnt++; + usleep_range(2000, 3000); + } + + return ret; +} + +static int haptic_nv_hw_reset(struct awinic *awinic) +{ + aw_dev_info("%s enter\n", __func__); + + if (awinic && gpio_is_valid(awinic->reset_gpio)) { + gpio_set_value_cansleep(awinic->reset_gpio, 0); + usleep_range(1000, 2000); + gpio_set_value_cansleep(awinic->reset_gpio, 1); + usleep_range(3500, 4000); + } else { + aw_dev_err("%s: failed\n", __func__); + } + return 0; +} + +static int haptic_nv_haptic_softreset(struct awinic *awinic) +{ + aw_dev_info("%s enter\n", __func__); + haptic_nv_i2c_write(awinic, HAPTIC_NV_REG_ID, 0xAA); + usleep_range(2000, 2500); + return 0; +} + +static int haptic_nv_read_chipid(struct awinic *awinic, + unsigned char *reg, unsigned char type) +{ + unsigned char cnt = 0; + int ret = -1; + + while (cnt < HAPTIC_NV_I2C_RETRIES) { + ret = i2c_smbus_read_byte_data(awinic->i2c, HAPTIC_NV_REG_ID); + if (ret < 0) { + if (type == HAPTIC_NV_FIRST_TRY) { + aw_dev_info("%s: reading chip id\n", __func__); + } else if (type == HAPTIC_NV_LAST_TRY) { + aw_dev_err("%s: i2c_read cnt=%d error=%d\n", + __func__, cnt, ret); + } else { + aw_dev_err("%s: type is error\n", __func__); + } + } else { + *reg = ret; + break; + } + cnt++; + usleep_range(2000, 3000); + } + + return ret; +} + +static int haptic_nv_parse_chipid(struct awinic *awinic) +{ + unsigned char cnt = 0; + unsigned char reg = 0; + unsigned char ef_id = 0xff; + int ret = -1; + + while (cnt < HAPTIC_NV_CHIPID_RETRIES) { + /* hardware reset */ + haptic_nv_hw_reset(awinic); + + ret = haptic_nv_read_chipid(awinic, ®, HAPTIC_NV_FIRST_TRY); + if (ret < 0) { + awinic->i2c->addr = (u16)awinic->aw862xx_i2c_addr; + aw_dev_info("%s: try to replace i2c addr [(0x%02X)] to read chip id again\n", + __func__, awinic->i2c->addr); + ret = haptic_nv_read_chipid(awinic, ®, + HAPTIC_NV_LAST_TRY); + if (ret < 0) + break; + } + switch (reg) { + case AW8624_CHIP_ID: + aw_dev_info("%s aw8624 detected\n", __func__); + awinic->name = AW8624; + haptic_nv_haptic_softreset(awinic); + return 0; + case AW8622X_CHIP_ID: + /* Distinguish products by AW862XX_REG_EFRD9. */ + haptic_nv_i2c_read(awinic, AW862XX_REG_EFRD9, &ef_id); + if ((ef_id & 0x41) == AW86224_5_EF_ID) { + awinic->name = AW86224_5; + aw_dev_info("%s aw86224_5 detected\n", + __func__); + haptic_nv_haptic_softreset(awinic); + return 0; + } + if ((ef_id & 0x41) == AW86223_EF_ID) { + awinic->name = AW86223; + aw_dev_info("%s aw86223 detected\n", + __func__); + haptic_nv_haptic_softreset(awinic); + return 0; + } + aw_dev_info("%s unsupported ef_id = (0x%02X)\n", + __func__, ef_id); + break; + case AW86214_CHIP_ID: + if ((ef_id & 0x41) == AW86214_EF_ID) { + awinic->name = AW86214; + aw_dev_info("%s aw86214 detected\n", __func__); + haptic_nv_haptic_softreset(awinic); + return 0; + } + aw_dev_info("%s unsupported ef_id = (0x%02X)\n", + __func__, ef_id); + break; + default: + aw_dev_info("%s unsupported device revision (0x%x)\n", + __func__, reg); + break; + } + cnt++; + + usleep_range(2000, 3000); + } + + return -EINVAL; +} +static int haptic_nv_parse_dt(struct awinic *awinic, struct device *dev, + struct device_node *np) +{ + unsigned int val = 0; + + awinic->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (awinic->reset_gpio >= 0) { + aw_dev_info("%s: reset gpio provided ok\n", __func__); + } else { + awinic->reset_gpio = -1; + aw_dev_err("%s: no reset gpio provided, will not HW reset device\n", + __func__); + return -ERANGE; + } + + awinic->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (awinic->irq_gpio < 0) { + aw_dev_err("%s: no irq gpio provided.\n", __func__); + awinic->IsUsedIRQ = false; + } else { + aw_dev_info("%s: irq gpio provided ok.\n", __func__); + awinic->IsUsedIRQ = true; + } + + val = of_property_read_u32(np, + "aw862xx_i2c_addr", &awinic->aw862xx_i2c_addr); + if (val) + aw_dev_err("%s:configure aw862xx_i2c_addr error\n", __func__); + else + aw_dev_info("%s: configure aw862xx_i2c_addr ok\n", __func__); + return 0; +} + +static int +haptic_nv_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + int ret = -1; + int irq_flags = 0; + struct awinic *awinic; + struct device_node *np = i2c->dev.of_node; + + aw_dev_info("%s enter\n", __func__); + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + aw_dev_err("check_functionality failed\n"); + return -EIO; + } + + awinic = devm_kzalloc(&i2c->dev, sizeof(struct awinic), GFP_KERNEL); + if (awinic == NULL) + return -ENOMEM; + + awinic->dev = &i2c->dev; + awinic->i2c = i2c; + + i2c_set_clientdata(i2c, awinic); + /* aw862xx rst & int */ + if (np) { + ret = haptic_nv_parse_dt(awinic, &i2c->dev, np); + if (ret) { + aw_dev_err("%s: failed to parse device tree node\n", + __func__); + goto err_parse_dt; + } + } + if (gpio_is_valid(awinic->reset_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, awinic->reset_gpio, + GPIOF_OUT_INIT_LOW, "haptic_nv_rst"); + if (ret) { + aw_dev_err("%s: rst request failed\n", __func__); + goto err_reset_gpio_request; + } + } + + if (gpio_is_valid(awinic->irq_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, awinic->irq_gpio, + GPIOF_DIR_IN, "haptic_nv_int"); + if (ret) { + aw_dev_err("%s: int request failed\n", __func__); + goto err_irq_gpio_request; + } + } + /* parse chip id */ + ret = haptic_nv_parse_chipid(awinic); + if (ret < 0) { + aw_dev_err("%s: haptic_nv_read_chipid failed\n", __func__); + goto err_id; + } + /*aw8624*/ + if (awinic->name == AW8624) { + awinic->aw8624 = devm_kzalloc(&i2c->dev, + sizeof(struct aw8624), GFP_KERNEL); + if (awinic->aw8624 == NULL) { + if (gpio_is_valid(awinic->irq_gpio)) + devm_gpio_free(&i2c->dev, awinic->irq_gpio); + if (gpio_is_valid(awinic->reset_gpio)) + devm_gpio_free(&i2c->dev, awinic->reset_gpio); + devm_kfree(&i2c->dev, awinic); + awinic = NULL; + return -ENOMEM; + } + awinic->aw8624->dev = awinic->dev; + awinic->aw8624->i2c = awinic->i2c; + awinic->aw8624->reset_gpio = awinic->reset_gpio; + awinic->aw8624->irq_gpio = awinic->irq_gpio; + awinic->aw8624->IsUsedIRQ = awinic->IsUsedIRQ; + if (np) { + ret = aw8624_parse_dt(awinic->aw8624, &i2c->dev, np); + if (ret) { + aw_dev_err("%s: failed to parse device tree node\n", + __func__); + goto err_aw8624_parse_dt; + } + } + + /* aw8624 irq */ + if (gpio_is_valid(awinic->aw8624->irq_gpio) && + !(awinic->aw8624->flags & AW8624_FLAG_SKIP_INTERRUPTS)) { + /* register irq handler */ + aw8624_interrupt_setup(awinic->aw8624); + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, + gpio_to_irq(awinic->aw8624->irq_gpio), + NULL, aw8624_irq, irq_flags, + "aw8624", awinic->aw8624); + if (ret != 0) { + aw_dev_err("%s: failed to request IRQ %d: %d\n", + __func__, + gpio_to_irq(awinic->aw8624->irq_gpio), + ret); + goto err_aw8624_irq; + } + } + aw8624_vibrator_init(awinic->aw8624); + aw8624_haptic_init(awinic->aw8624); + aw8624_ram_init(awinic->aw8624); + + } + /* aw8622x */ + if (awinic->name == AW86223 || awinic->name == AW86224_5) { + awinic->aw8622x = devm_kzalloc(&i2c->dev, + sizeof(struct aw8622x), GFP_KERNEL); + if (awinic->aw8622x == NULL) { + if (gpio_is_valid(awinic->irq_gpio)) + devm_gpio_free(&i2c->dev, awinic->irq_gpio); + if (gpio_is_valid(awinic->reset_gpio)) + devm_gpio_free(&i2c->dev, awinic->reset_gpio); + devm_kfree(&i2c->dev, awinic); + awinic = NULL; + return -ENOMEM; + } + awinic->aw8622x->dev = awinic->dev; + awinic->aw8622x->i2c = awinic->i2c; + awinic->aw8622x->reset_gpio = awinic->reset_gpio; + awinic->aw8622x->irq_gpio = awinic->irq_gpio; + awinic->aw8622x->isUsedIntn = awinic->IsUsedIRQ; + awinic->aw8622x->name = awinic->name; + /* chip qualify */ + if (!aw8622x_check_qualify(awinic->aw8622x)) { + aw_dev_err("%s:unqualified chip!\n", __func__); + goto err_aw8622x_check_qualify; + } + if (np) { + ret = aw8622x_parse_dt(awinic->aw8622x, &i2c->dev, np); + if (ret) { + aw_dev_err("%s: failed to parse device tree node\n", + __func__); + goto err_aw8622x_parse_dt; + } + } + /* aw8622x irq */ + if (gpio_is_valid(awinic->aw8622x->irq_gpio) && + !(awinic->aw8622x->flags & AW8622X_FLAG_SKIP_INTERRUPTS)) { + /* register irq handler */ + aw8622x_interrupt_setup(awinic->aw8622x); + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, + gpio_to_irq(awinic->aw8622x->irq_gpio), + NULL, aw8622x_irq, irq_flags, + "aw8622x", awinic->aw8622x); + if (ret != 0) { + aw_dev_err("%s: failed to request IRQ %d: %d\n", + __func__, + gpio_to_irq(awinic->aw8622x->irq_gpio), + ret); + goto err_aw8622x_irq; + } + } else { + aw_dev_info("%s skipping IRQ registration\n", __func__); + /* disable feature support if gpio was invalid */ + awinic->aw8622x->flags |= AW8622X_FLAG_SKIP_INTERRUPTS; + } + aw8622x_vibrator_init(awinic->aw8622x); + aw8622x_haptic_init(awinic->aw8622x); + aw8622x_ram_work_init(awinic->aw8622x); + } + /* aw86214 */ + if (awinic->name == AW86214) { + awinic->aw86214 = devm_kzalloc(&i2c->dev, + sizeof(struct aw86214), GFP_KERNEL); + if (awinic->aw86214 == NULL) { + if (gpio_is_valid(awinic->reset_gpio)) + devm_gpio_free(&i2c->dev, awinic->reset_gpio); + devm_kfree(&i2c->dev, awinic); + awinic = NULL; + return -ENOMEM; + } + awinic->aw86214->dev = awinic->dev; + awinic->aw86214->i2c = awinic->i2c; + awinic->aw86214->reset_gpio = awinic->reset_gpio; + awinic->aw86214->irq_gpio = awinic->irq_gpio; + /* chip qualify */ + if (!aw86214_check_qualify(awinic->aw86214)) { + aw_dev_err("%s:unqualified chip!\n", __func__); + goto err_aw86214_check_qualify; + } + if (np) { + ret = aw86214_parse_dt(awinic->aw86214, &i2c->dev, np); + if (ret) { + aw_dev_err("%s: failed to parse device tree node\n", + __func__); + goto err_aw86214_parse_dt; + } + } + /* aw86214 irq */ + if (gpio_is_valid(awinic->aw86214->irq_gpio) && + !(awinic->aw86214->flags & AW8622X_FLAG_SKIP_INTERRUPTS)) { + /* register irq handler */ + aw86214_interrupt_setup(awinic->aw86214); + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, + gpio_to_irq(awinic->aw86214->irq_gpio), + NULL, aw86214_irq, irq_flags, + "aw86214", awinic->aw86214); + if (ret != 0) { + aw_dev_err("%s: failed to request IRQ %d: %d\n", + __func__, + gpio_to_irq(awinic->aw86214->irq_gpio), + ret); + goto err_aw86214_irq; + } + } else { + aw_dev_info("%s skipping IRQ registration\n", __func__); + /* disable feature support if gpio was invalid */ + awinic->aw86214->flags |= AW8622X_FLAG_SKIP_INTERRUPTS; + } + aw86214_vibrator_init(awinic->aw86214); + aw86214_haptic_init(awinic->aw86214); + aw86214_ram_work_init(awinic->aw86214); + } + dev_set_drvdata(&i2c->dev, awinic); + aw_dev_info("%s probe completed successfully!\n", __func__); + + return 0; + +err_aw86214_irq: +err_aw86214_parse_dt: +err_aw86214_check_qualify: + if (awinic->name == AW86214) { + devm_kfree(&i2c->dev, awinic->aw86214); + awinic->aw8622x = NULL; + } +err_aw8622x_irq: +err_aw8622x_parse_dt: +err_aw8622x_check_qualify: + if (awinic->name == AW86223 || awinic->name == AW86224_5) { + devm_kfree(&i2c->dev, awinic->aw8622x); + awinic->aw8622x = NULL; + } +err_aw8624_irq: +err_aw8624_parse_dt: + if (awinic->name == AW8624) { + devm_kfree(&i2c->dev, awinic->aw8624); + awinic->aw8624 = NULL; + } + +err_id: + if (gpio_is_valid(awinic->irq_gpio)) + devm_gpio_free(&i2c->dev, awinic->irq_gpio); +err_irq_gpio_request: + if (gpio_is_valid(awinic->reset_gpio)) + devm_gpio_free(&i2c->dev, awinic->reset_gpio); +err_reset_gpio_request: +err_parse_dt: + devm_kfree(&i2c->dev, awinic); + awinic = NULL; + return ret; + +} + +static int haptic_nv_i2c_remove(struct i2c_client *i2c) +{ + struct awinic *awinic = i2c_get_clientdata(i2c); + + aw_dev_info("%s enter\n", __func__); + + if (awinic->name == AW8624) { + aw_dev_info("%s remove aw86214\n", __func__); + cancel_delayed_work_sync(&awinic->aw8624->ram_work); + cancel_work_sync(&awinic->aw8624->haptic_audio.work); + hrtimer_cancel(&awinic->aw8624->haptic_audio.timer); + if (awinic->aw8624->IsUsedIRQ) + cancel_work_sync(&awinic->aw8624->rtp_work); + cancel_work_sync(&awinic->aw8624->vibrator_work); + hrtimer_cancel(&awinic->aw8624->timer); + mutex_destroy(&awinic->aw8624->lock); + mutex_destroy(&awinic->aw8624->rtp_lock); + mutex_destroy(&awinic->aw8624->haptic_audio.lock); + sysfs_remove_group(&awinic->aw8624->i2c->dev.kobj, + &aw8624_vibrator_attribute_group); +#ifdef TIMED_OUTPUT + timed_output_dev_unregister(&awinic->aw8624->vib_dev); +#endif + devm_free_irq(&awinic->aw8624->i2c->dev, + gpio_to_irq(awinic->aw8624->irq_gpio), awinic->aw8624); + + } else if (awinic->name == AW86223 || awinic->name == AW86224_5) { + aw_dev_info("%s remove aw8622x\n", __func__); + cancel_delayed_work_sync(&awinic->aw8622x->ram_work); + cancel_work_sync(&awinic->aw8622x->haptic_audio.work); + hrtimer_cancel(&awinic->aw8622x->haptic_audio.timer); + if (awinic->aw8622x->isUsedIntn) + cancel_work_sync(&awinic->aw8622x->rtp_work); + cancel_work_sync(&awinic->aw8622x->vibrator_work); + + hrtimer_cancel(&awinic->aw8622x->timer); + mutex_destroy(&awinic->aw8622x->lock); + mutex_destroy(&awinic->aw8622x->rtp_lock); + mutex_destroy(&awinic->aw8622x->haptic_audio.lock); + sysfs_remove_group(&awinic->aw8622x->i2c->dev.kobj, + &aw8622x_vibrator_attribute_group); +#ifdef TIMED_OUTPUT + timed_output_dev_unregister(&awinic->aw8622x->vib_dev); +#endif + devm_free_irq(&awinic->aw8622x->i2c->dev, + gpio_to_irq(awinic->aw8622x->irq_gpio), + awinic->aw8622x); + + } else if (awinic->name == AW86214) { + aw_dev_info("%s remove aw86214\n", __func__); + cancel_delayed_work_sync(&awinic->aw86214->ram_work); + cancel_work_sync(&awinic->aw86214->haptic_audio.work); + hrtimer_cancel(&awinic->aw86214->haptic_audio.timer); + cancel_work_sync(&awinic->aw86214->vibrator_work); + + hrtimer_cancel(&awinic->aw86214->timer); + mutex_destroy(&awinic->aw86214->lock); + mutex_destroy(&awinic->aw86214->haptic_audio.lock); + sysfs_remove_group(&awinic->aw86214->i2c->dev.kobj, + &aw86214_vibrator_attribute_group); +#ifdef TIMED_OUTPUT + timed_output_dev_unregister(&awinic->aw86214->vib_dev); +#endif + devm_free_irq(&awinic->aw86214->i2c->dev, + gpio_to_irq(awinic->aw86214->irq_gpio), + awinic->aw86214); + + } else { + aw_dev_err("%s no chip\n", __func__); + return -ERANGE; + } + aw_dev_info("%s exit\n", __func__); + return 0; +} + +static const struct i2c_device_id haptic_nv_i2c_id[] = { + { HAPTIC_NV_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, haptic_nv_i2c_id); + +static const struct of_device_id haptic_nv_dt_match[] = { + { .compatible = "awinic,haptic_nv" }, + { }, +}; + +static struct i2c_driver haptic_nv_i2c_driver = { + .driver = { + .name = HAPTIC_NV_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(haptic_nv_dt_match), + }, + .probe = haptic_nv_i2c_probe, + .remove = haptic_nv_i2c_remove, + .id_table = haptic_nv_i2c_id, +}; + +static int __init haptic_nv_i2c_init(void) +{ + int ret = 0; + + aw_dev_info("awinic driver version %s\n", HAPTIC_NV_DRIVER_VERSION); + + ret = i2c_add_driver(&haptic_nv_i2c_driver); + if (ret) { + aw_dev_err("fail to add awinic device into i2c\n"); + return ret; + } + + return 0; +} + +late_initcall(haptic_nv_i2c_init); + +static void __exit haptic_nv_i2c_exit(void) +{ + i2c_del_driver(&haptic_nv_i2c_driver); +} +module_exit(haptic_nv_i2c_exit); + +MODULE_DESCRIPTION("Awinic Haptic Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/awinic/aw862x/haptic_nv.h b/drivers/misc/awinic/aw862x/haptic_nv.h new file mode 100644 index 000000000000..b61e85d6fda7 --- /dev/null +++ b/drivers/misc/awinic/aw862x/haptic_nv.h @@ -0,0 +1,164 @@ +#ifndef _HAPTIC_NV_H_ +#define _HAPTIC_NV_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +/********************************************************* + * + * kernel marco + * + ********************************************************/ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 4, 1) +#define TIMED_OUTPUT +#endif + +#ifdef TIMED_OUTPUT +#include <../../../drivers/staging/android/timed_output.h> +typedef struct timed_output_dev cdev_t; +#else +typedef struct led_classdev cdev_t; +#endif + +/********************************************************* + * + * normal marco + * + ********************************************************/ +#define HAPTIC_NV_I2C_NAME ("awinic_haptic") +#define HAPTIC_NV_AWRW_SIZE (220) +#define HAPTIC_NV_CHIPID_RETRIES (5) +#define HAPTIC_NV_I2C_RETRIES (2) +#define HAPTIC_NV_REG_ID (0x00) +#define AW8624_CHIP_ID (0x24) +#define AW8622X_CHIP_ID (0x00) +#define AW86214_CHIP_ID (0x01) +#define AW862XX_REG_EFRD9 (0x64) +#define REG_NONE_ACCESS (0) +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) +#define AW_REG_MAX (0xFF) +#define AW_RAMDATA_RD_BUFFER_SIZE (1024) +#define AW_RAMDATA_WR_BUFFER_SIZE (2048) +#define AW_GUN_TYPE_DEF_VAL (0xFF) +#define AW_BULLET_NR_DEF_VAL (0) +#define AW_I2C_READ_MSG_NUM (2) +#define AW_I2C_BYTE_ONE (1) +#define AW_I2C_BYTE_TWO (2) +#define AW_I2C_BYTE_THREE (3) +#define AW_I2C_BYTE_FOUR (4) +#define AW_I2C_BYTE_FIVE (5) +#define AW_I2C_BYTE_SIX (6) +#define AW_I2C_BYTE_SEVEN (7) +#define AW_I2C_BYTE_EIGHT (8) +#define AWRW_CMD_UNIT (5) +#define AW_SET_RAMADDR_H(addr) ((addr) >> 8) +#define AW_SET_RAMADDR_L(addr) ((addr) & 0x00FF) +#define AW_SET_BASEADDR_H(addr) ((addr) >> 8) +#define AW_SET_BASEADDR_L(addr) ((addr) & 0x00FF) +/********************************************************* + * + * macro control + * + ********************************************************/ +#define AW_CHECK_RAM_DATA +#define AW_READ_BIN_FLEXBALLY +#define AW_ENABLE_RTP_PRINT_LOG +/* #define AW8624_MUL_GET_F0 */ + +#define aw_dev_err(format, ...) \ + pr_err("[haptic_nv]" format, ##__VA_ARGS__) + +#define aw_dev_info(format, ...) \ + pr_info("[haptic_nv]" format, ##__VA_ARGS__) + +#define aw_dev_dbg(format, ...) \ + pr_debug("[haptic_nv]" format, ##__VA_ARGS__) +/********************************************************* + * + * enum + * + ********************************************************/ +enum haptic_nv_chip_name { + AW_CHIP_NULL = 0, + AW86223 = 1, + AW86224_5 = 2, + AW86214 = 3, + AW8624 = 4, +}; + +enum haptic_nv_read_chip_type { + HAPTIC_NV_FIRST_TRY = 0, + HAPTIC_NV_LAST_TRY = 1, +}; + +enum aw862xx_ef_id { + AW86223_EF_ID = 0x01, + AW86224_5_EF_ID = 0x00, + AW86214_EF_ID = 0x41, +}; +/********************************************************* + * + * struct + * + ********************************************************/ +struct awinic { + bool IsUsedIRQ; + unsigned char name; + + unsigned int aw862xx_i2c_addr; + int reset_gpio; + int irq_gpio; + int reset_gpio_ret; + int irq_gpio_ret; + + struct i2c_client *i2c; + struct device *dev; + struct aw8624 *aw8624; + struct aw8622x *aw8622x; + struct aw86214 *aw86214; +}; + +struct ram { + unsigned int len; + unsigned int check_sum; + unsigned int base_addr; + unsigned char version; + unsigned char ram_shift; + unsigned char baseaddr_shift; + unsigned char ram_num; +}; + +struct haptic_ctr { + unsigned char cnt; + unsigned char cmd; + unsigned char play; + unsigned char wavseq; + unsigned char loop; + unsigned char gain; + struct list_head list; +}; + +struct haptic_audio { + struct mutex lock; + struct hrtimer timer; + struct work_struct work; + int delay_val; + int timer_val; + struct haptic_ctr ctr; + struct list_head ctr_list; +}; + +struct aw_i2c_package { + unsigned char flag; + unsigned char reg_num; + unsigned char first_addr; + unsigned char reg_data[HAPTIC_NV_AWRW_SIZE]; +}; +#endif diff --git a/drivers/misc/awinic/aw862x_haptic_nv/Android.mk b/drivers/misc/awinic/aw862x_haptic_nv/Android.mk new file mode 100644 index 000000000000..4274108c555e --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv/Android.mk @@ -0,0 +1,12 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := aw862x_haptic_nv.ko +LOCAL_MODULE_TAGS := optional +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/awinic/aw862x_haptic_nv/Kbuild b/drivers/misc/awinic/aw862x_haptic_nv/Kbuild new file mode 100644 index 000000000000..81ab1ba45fb5 --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv/Kbuild @@ -0,0 +1,12 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/misc/awinic/aw862x_haptic_nv + +obj-m += aw862x_haptic_nv.o + +aw862x_haptic_nv-objs += haptic_nv.o +aw862x_haptic_nv-objs += aw862xx.o +aw862x_haptic_nv-objs += aw8623x.o +aw862x_haptic_nv-objs += aw8624.o + diff --git a/drivers/misc/awinic/aw862x_haptic_nv/Makefile b/drivers/misc/awinic/aw862x_haptic_nv/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/misc/awinic/aw862x_haptic_nv/aw8623x.c b/drivers/misc/awinic/aw862x_haptic_nv/aw8623x.c new file mode 100755 index 000000000000..a6a148033238 --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv/aw8623x.c @@ -0,0 +1,1477 @@ +/* + * aw8623x.c + * + * Copyright (c) 2022 AWINIC Technology CO., LTD + * + * Author: + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "haptic_nv.h" +#include "haptic_nv_reg.h" + +static void aw8623x_interrupt_setup(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_info("reg SYSINT=0x%02X", reg_val); + /* edge int mode */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL3, + AW8623X_BIT_SYSCTRL3_INTMODE_MASK, + AW8623X_BIT_SYSCTRL3_INTMODE_POS_EDGE); + /* int enable */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSINTM, + (AW8623X_BIT_SYSINTM_UVLM_MASK & + AW8623X_BIT_SYSINTM_FF_AEM_MASK & + AW8623X_BIT_SYSINTM_FF_AFM_MASK & + AW8623X_BIT_SYSINTM_OCDM_MASK & + AW8623X_BIT_SYSINTM_OTM_MASK & + AW8623X_BIT_SYSINTM_DONEM_MASK), + (AW8623X_BIT_SYSINTM_UVLM_ON | + AW8623X_BIT_SYSINTM_FF_AEM_OFF | + AW8623X_BIT_SYSINTM_FF_AFM_OFF | + AW8623X_BIT_SYSINTM_OCDM_ON | + AW8623X_BIT_SYSINTM_OTM_ON | + AW8623X_BIT_SYSINTM_DONEM_OFF)); +} + +static void aw8623x_set_rtp_data(struct aw_haptic *aw_haptic, uint8_t *data, + uint32_t len) +{ + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_RTPDATA, data, len); +} + +static uint8_t aw8623x_get_glb_state(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_GLBRD5, &state, + AW_I2C_BYTE_ONE); + return state; +} + +static int aw8623x_get_irq_state(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int ret = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_dbg("reg SYSINT=0x%02X", reg_val); + if (reg_val & AW8623X_BIT_SYSINT_UVLI) + aw_err("chip uvlo int error"); + if (reg_val & AW8623X_BIT_SYSINT_OCDI) + aw_err("chip over current int error"); + if (reg_val & AW8623X_BIT_SYSINT_OTI) + aw_err("chip over temperature int error"); + if (reg_val & AW8623X_BIT_SYSINT_DONEI) + aw_info("chip playback done"); + if (reg_val & AW8623X_BIT_SYSINT_FF_AFI) + aw_info("rtp mode fifo almost full!"); + if (reg_val & AW8623X_BIT_SYSINT_FF_AEI) { + aw_info("rtp fifo almost empty"); + ret = AW_IRQ_ALMOST_EMPTY; + } + return ret; +} + +static uint8_t aw8623x_rtp_get_fifo_afs(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_SYSST, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= AW8623X_BIT_SYSST_FF_AFS; + reg_val = reg_val >> 3; + return reg_val; +} + +static void aw8623x_set_rtp_aei(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSINTM, + AW8623X_BIT_SYSINTM_FF_AEM_MASK, + AW8623X_BIT_SYSINTM_FF_AEM_ON); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSINTM, + AW8623X_BIT_SYSINTM_FF_AEM_MASK, + AW8623X_BIT_SYSINTM_FF_AEM_OFF); + } +} +static void aw8623x_sram_size(struct aw_haptic *aw_haptic, uint8_t size_flag) +{ + switch (size_flag) { + case AW8623X_HAPTIC_SRAM_1K: + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_RTPCFG1, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_2K_MASK, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_2K_DIS); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_RTPCFG1, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_1K_MASK, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_1K_EN); + break; + case AW8623X_HAPTIC_SRAM_2K: + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_RTPCFG1, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_2K_MASK, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_2K_EN); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_RTPCFG1, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_1K_MASK, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_1K_DIS); + break; + case AW8623X_HAPTIC_SRAM_3K: + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_RTPCFG1, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_1K_MASK, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_1K_EN); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_RTPCFG1, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_2K_MASK, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_2K_EN); + break; + default: + aw_err("size_flag is error"); + break; + } +} + +static void aw8623x_auto_brk_config(struct aw_haptic *aw_haptic, uint8_t flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PLAYCFG3, + AW8623X_BIT_PLAYCFG3_BRK_EN_MASK, + AW8623X_BIT_PLAYCFG3_BRK_ENABLE); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PLAYCFG3, + AW8623X_BIT_PLAYCFG3_BRK_EN_MASK, + AW8623X_BIT_PLAYCFG3_BRK_DISABLE); + } +} + +static void aw8623x_play_stop(struct aw_haptic *aw_haptic) +{ + bool force_flag = true; + uint8_t val = 0; + int cnt = 40; + + aw_haptic->play_mode = AW_STANDBY_MODE; + val = AW8623X_BIT_PLAYCFG4_STOP_ON; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_PLAYCFG4, &val, + AW_I2C_BYTE_ONE); + while (cnt) { + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_GLBRD5, &val, + AW_I2C_BYTE_ONE); + if ((val & AW_BIT_GLBRD_STATE_MASK) == AW_BIT_STATE_STANDBY) { + force_flag = false; + aw_info("entered standby! glb_state=0x%02X", val); + break; + } + cnt--; + usleep_range(AW_STOP_DELAY_MIN, AW_STOP_DELAY_MAX); + } + if (force_flag) { + aw_err("force to enter standby mode!"); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL2, + AW8623X_BIT_SYSCTRL2_STANDBY_MASK, + AW8623X_BIT_SYSCTRL2_STANDBY_ON); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL2, + AW8623X_BIT_SYSCTRL2_STANDBY_MASK, + AW8623X_BIT_SYSCTRL2_STANDBY_OFF); + } +} + +static void aw8623x_set_pwm(struct aw_haptic *aw_haptic, uint8_t mode) +{ + switch (mode) { + case AW_PWM_48K: + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL2, + AW8623X_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW8623X_BIT_SYSCTRL2_RATE_48K); + break; + case AW_PWM_24K: + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL2, + AW8623X_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW8623X_BIT_SYSCTRL2_RATE_24K); + break; + case AW_PWM_12K: + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL2, + AW8623X_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW8623X_BIT_SYSCTRL2_RATE_12K); + break; + default: + break; + } +} + +static void aw8623x_play_mode(struct aw_haptic *aw_haptic, uint8_t play_mode) +{ + switch (play_mode) { + case AW_STANDBY_MODE: + aw_info("enter standby mode"); + aw_haptic->play_mode = AW_STANDBY_MODE; + aw8623x_play_stop(aw_haptic); + break; + case AW_RAM_MODE: + aw_info("enter ram mode"); + aw_haptic->play_mode = AW_RAM_MODE; + aw8623x_set_pwm(aw_haptic, AW_PWM_12K); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PLAYCFG3, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + case AW_RAM_LOOP_MODE: + aw_info("enter ram loop mode"); + aw_haptic->play_mode = AW_RAM_LOOP_MODE; + aw8623x_set_pwm(aw_haptic, AW_PWM_12K); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PLAYCFG3, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + case AW_CONT_MODE: + aw_info("enter cont mode"); + aw_haptic->play_mode = AW_CONT_MODE; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PLAYCFG3, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_CONT); + break; + case AW_RTP_MODE: + aw_info("enter rtp mode"); + aw_haptic->play_mode = AW_RTP_MODE; + aw8623x_set_pwm(aw_haptic, AW_PWM_24K); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PLAYCFG3, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_RTP); + break; + case AW_TRIG_MODE: + aw_info("enter trig mode"); + aw_haptic->play_mode = AW_TRIG_MODE; + aw8623x_set_pwm(aw_haptic, AW_PWM_12K); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PLAYCFG3, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + default: + aw_err("play mode %d error", play_mode); + break; + } +} + +static void aw8623x_irq_clear(struct aw_haptic *aw_haptic) +{ + uint8_t val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_SYSINT, &val, + AW_I2C_BYTE_ONE); + aw_info("SYSINT=0x%02X", val); +} + +static void aw8623x_play_go(struct aw_haptic *aw_haptic) +{ + uint8_t val = 0; + + aw_info("enter"); + val = AW8623X_BIT_PLAYCFG4_GO_ON; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_PLAYCFG4, &val, + AW_I2C_BYTE_ONE); + usleep_range(AW_PLAY_DELAY_MIN, AW_PLAY_DELAY_MAX); +} + +static void aw8623x_raminit(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL1, + AW8623X_BIT_SYSCTRL1_RAMINIT_MASK, + AW8623X_BIT_SYSCTRL1_RAMINIT_ON); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL1, + AW8623X_BIT_SYSCTRL1_RAMINIT_MASK, + AW8623X_BIT_SYSCTRL1_RAMINIT_OFF); + } +} + +static int aw8623x_select_d2s_gain(uint8_t reg) +{ + int d2s_gain = 0; + + switch (reg) { + case AW8623X_BIT_DETCFG2_D2S_GAIN_1: + d2s_gain = 1; + break; + case AW8623X_BIT_DETCFG2_D2S_GAIN_2: + d2s_gain = 2; + break; + case AW8623X_BIT_DETCFG2_D2S_GAIN_4: + d2s_gain = 4; + break; + case AW8623X_BIT_DETCFG2_D2S_GAIN_5: + d2s_gain = 5; + break; + case AW8623X_BIT_DETCFG2_D2S_GAIN_8: + d2s_gain = 8; + break; + case AW8623X_BIT_DETCFG2_D2S_GAIN_10: + d2s_gain = 10; + break; + case AW8623X_BIT_DETCFG2_D2S_GAIN_20: + d2s_gain = 20; + break; + case AW8623X_BIT_DETCFG2_D2S_GAIN_40: + d2s_gain = 40; + break; + default: + d2s_gain = -1; + break; + } + return d2s_gain; +} + +static void aw8623x_get_vbat(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val[2] = {0}; + uint32_t vbat_code = 0; + + aw_info("enter"); + aw8623x_play_stop(aw_haptic); + aw8623x_raminit(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG2, + AW8623X_BIT_DETCFG2_DET_SEQ0_MASK, + AW8623X_BIT_DETCFG2_DET_SEQ0_VBAT); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG1, + AW8623X_BIT_DETCFG1_DET_GO_MASK, + AW8623X_BIT_DETCFG1_DET_GO_ON); + usleep_range(3000, 3500); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG1, + AW8623X_BIT_DETCFG1_DET_GO_MASK, + AW8623X_BIT_DETCFG1_DET_GO_OFF); + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_DETRD1, reg_val, + AW_I2C_BYTE_TWO); + vbat_code = reg_val[0] & (~AW8623X_BIT_DETRD1_AVG_DATA_H_MASK); + vbat_code = (vbat_code << 8) | reg_val[1]; + aw_haptic->vbat = AW8623X_VBAT_FORMULA(vbat_code); + if (aw_haptic->vbat > AW_VBAT_MAX) { + aw_haptic->vbat = AW_VBAT_MAX; + aw_info("vbat max limit = %dmV", aw_haptic->vbat); + } + if (aw_haptic->vbat < AW_VBAT_MIN) { + aw_haptic->vbat = AW_VBAT_MIN; + aw_info("vbat min limit = %dmV", aw_haptic->vbat); + } + aw_info("vbat=%dmV, vbat_code=0x%02X", aw_haptic->vbat, vbat_code); + aw8623x_raminit(aw_haptic, false); +} + +static void aw8623x_set_gain(struct aw_haptic *aw_haptic, uint8_t gain) +{ + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_PLAYCFG2, &gain, + AW_I2C_BYTE_ONE); +} + +static void aw8623x_set_trim_lra(struct aw_haptic *aw_haptic, uint8_t val) +{ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_TRIMCFG2, + AW8623X_BIT_TRIMCFG2_TRIM_LRA_MASK, val); +} + +static void aw8623x_cont_config(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + aw_info("enter"); + /* work mode */ + aw8623x_play_mode(aw_haptic, AW_CONT_MODE); + /* cont config */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_CONTCFG6, + (AW8623X_BIT_CONTCFG6_TRACK_EN_MASK & + AW8623X_BIT_CONTCFG6_DRV1_LVL_MASK), + ((aw_haptic->info.is_enabled_track_en << 7) | + (uint8_t)aw_haptic->info.cont_drv1_lvl)); + reg_val = (uint8_t)aw_haptic->info.cont_drv2_lvl; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG7, ®_val, + AW_I2C_BYTE_ONE); + /* DRV2_TIME */ + reg_val = 0xFF; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG9, ®_val, + AW_I2C_BYTE_ONE); + /* cont play go */ + aw8623x_play_go(aw_haptic); +} + +static ssize_t aw8623x_get_reg(struct aw_haptic *aw_haptic, ssize_t len, + char *buf) +{ + len = haptic_nv_read_reg_array(aw_haptic, buf, len, AW8623X_REG_RSTCFG, + AW8623X_REG_RTPDATA - 1); + if (!len) + return len; + len = haptic_nv_read_reg_array(aw_haptic, buf, len, + AW8623X_REG_RTPDATA + 1, + AW8623X_REG_RAMDATA - 1); + if (!len) + return len; + len = haptic_nv_read_reg_array(aw_haptic, buf, len, + AW8623X_REG_RAMDATA + 1, + AW8623X_REG_ANACFG10); + return len; +} + +static void aw8623x_protect_config(struct aw_haptic *aw_haptic, uint8_t prtime, + uint8_t prlvl) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PWMCFG1, + AW8623X_BIT_PWMCFG1_PRC_EN_MASK, + AW8623X_BIT_PWMCFG1_PRC_DISABLE); + if (prlvl != 0) { + /* Enable protection mode */ + aw_info("enable protection mode"); + reg_val = AW8623X_BIT_PWMCFG3_PR_ENABLE | + (prlvl & (~AW8623X_BIT_PWMCFG3_PRLVL_MASK)); + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_PWMCFG3, ®_val, + AW_I2C_BYTE_ONE); + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_PWMCFG4, &prtime, + AW_I2C_BYTE_ONE); + } else { + /* Disable */ + aw_info("disable protection mode"); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PWMCFG3, + AW8623X_BIT_PWMCFG3_PR_EN_MASK, + AW8623X_BIT_PWMCFG3_PR_DISABLE); + } +} + +static void aw8623x_misc_para_init(struct aw_haptic *aw_haptic) +{ + uint8_t val = 0; + uint8_t array[8] = {0}; + + aw_info("enter"); + /* Get seq and gain */ + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_WAVCFG1, &val, + AW_I2C_BYTE_ONE); + aw_haptic->index = val & AW8623X_BIT_WAVCFG_SEQ; + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_PLAYCFG2, &val, + AW_I2C_BYTE_ONE); + aw_haptic->gain = val; + aw_info("gain=0x%02X, index=0x%02X", aw_haptic->gain, aw_haptic->index); + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_WAVCFG1, array, + AW_SEQUENCER_SIZE); + memcpy(aw_haptic->seq, array, AW_SEQUENCER_SIZE); + /* GAIN_BYPASS config */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL1, + AW8623X_BIT_SYSCTRL1_GAIN_BYPASS_MASK, + AW8623X_BIT_SYSCTRL1_GAIN_CHANGE_ENABLE); + /* Open the input OVP function of ADC */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_ANACFG3, + AW8623X_BIT_ANACFG3_EN_OVP_SAR_MASK, + AW8623X_BIT_ANACFG3_EN_OVP_SAR_ENABLE); + + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL1, + AW8623X_BIT_SYSCTRL1_TRIG1_MODE_MASK, + AW8623X_BIT_SYSCTRL1_TRIG1_TRIG); + + if (aw_haptic->info.d2s_gain) { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG2, + AW8623X_BIT_DETCFG2_D2S_GAIN_MASK, + aw_haptic->info.d2s_gain); + } + if (aw_haptic->info.cont_tset && aw_haptic->info.cont_bemf_set) { + array[0] = (uint8_t)aw_haptic->info.cont_tset; + array[1] = (uint8_t)aw_haptic->info.cont_bemf_set; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_CONTCFG13, + (AW8623X_BIT_CONTCFG13_TSET_MASK & + AW8623X_BIT_CONTCFG13_BEME_SET_MASK), + ((array[0] << 4) | array[1])); + } + + if (aw_haptic->info.cont_brk_time) { + val = (uint8_t)aw_haptic->info.cont_brk_time; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG10, + &val, AW_I2C_BYTE_ONE); + } + + if (aw_haptic->info.cont_brk_gain) { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_CONTCFG5, + AW8623X_BIT_CONTCFG5_BRK_GAIN_MASK, + aw_haptic->info.cont_brk_gain); + } + + aw_haptic->info.cont_drv2_lvl = AW8623X_DRV2_LVL_FORMULA( + aw_haptic->info.f0_pre, aw_haptic->info.lra_vrms); + if (aw_haptic->info.cont_drv2_lvl > AW8623X_DRV2_LVL_MAX) + aw_haptic->info.cont_drv2_lvl = AW8623X_DRV2_LVL_MAX; + + aw8623x_sram_size(aw_haptic, AW8623X_HAPTIC_SRAM_3K); + aw8623x_auto_brk_config(aw_haptic, aw_haptic->info.is_enabled_auto_brk); + aw8623x_set_pwm(aw_haptic, AW_PWM_12K); + aw8623x_protect_config(aw_haptic, AW8623X_PWMCFG4_PRTIME_DEFAULT_VALUE, + AW8623X_BIT_PWMCFG3_PRLVL_DEFAULT_VALUE); +} + +static void aw8623x_offset_cali(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val[2] = {0}; + uint32_t os_code = 0; + uint32_t os_data = 0; + int d2s_gain = 0; + + aw_info("enter"); + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_DETCFG2, reg_val, + AW_I2C_BYTE_ONE); + reg_val[0] = (~AW8623X_BIT_DETCFG2_D2S_GAIN_MASK) & reg_val[0]; + d2s_gain = aw8623x_select_d2s_gain(reg_val[0]); + if (d2s_gain < 0) { + aw_err("d2s_gain is error"); + return; + } + aw8623x_raminit(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG2, + AW8623X_BIT_DETCFG2_DET_SEQ0_MASK, + AW8623X_BIT_DETCFG2_DET_SEQ0_OS); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG1, + AW8623X_BIT_DETCFG1_DET_GO_MASK, + AW8623X_BIT_DETCFG1_DET_GO_ON); + usleep_range(3000, 3500); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG1, + AW8623X_BIT_DETCFG1_DET_GO_MASK, + AW8623X_BIT_DETCFG1_DET_GO_OFF); + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_DETRD1, reg_val, + AW_I2C_BYTE_TWO); + os_code = reg_val[0] & (~AW8623X_BIT_DETRD1_AVG_DATA_H_MASK); + os_code = (os_code << 8) | reg_val[1]; + os_data = AW8623X_OS_FORMULA(os_code, d2s_gain); + aw_info("os_data is %d mV", os_data); + aw8623x_raminit(aw_haptic, false); +} + +static void aw8623x_vbat_mode_config(struct aw_haptic *aw_haptic, uint8_t flag) +{ + aw_info("enter"); + if (flag == AW_CONT_VBAT_HW_COMP_MODE) { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_VBATCTRL, + AW8623X_BIT_VBATCTRL_VBAT_MODE_MASK, + AW8623X_BIT_VBATCTRL_VBAT_MODE_HW); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_VBATCTRL, + AW8623X_BIT_VBATCTRL_VBAT_MODE_MASK, + AW8623X_BIT_VBATCTRL_VBAT_MODE_SW); + } +} + +static void aw8623x_calculate_cali_data(struct aw_haptic *aw_haptic) +{ + char f0_cali_lra = 0; + int f0_cali_step = 0; + + f0_cali_step = 100000 * ((int)aw_haptic->f0 - + (int)aw_haptic->info.f0_pre) / + ((int)aw_haptic->f0 * AW8623X_F0_CALI_ACCURACY); + aw_info("f0_cali_step=%d", f0_cali_step); + if (f0_cali_step >= 0) { /*f0_cali_step >= 0 */ + if (f0_cali_step % 10 >= 5) + f0_cali_step = 32 + (f0_cali_step / 10 + 1); + else + f0_cali_step = 32 + f0_cali_step / 10; + } else { /* f0_cali_step < 0 */ + if (f0_cali_step % 10 <= -5) + f0_cali_step = 32 + (f0_cali_step / 10 - 1); + else + f0_cali_step = 32 + f0_cali_step / 10; + } + if (f0_cali_step > 31) + f0_cali_lra = (char)f0_cali_step - 32; + else + f0_cali_lra = (char)f0_cali_step + 32; + /* update cali step */ + aw_haptic->f0_cali_data = (int)f0_cali_lra; + aw_info("f0_cali_data=0x%02X", aw_haptic->f0_cali_data); +} + +static void aw8623x_read_cont_f0(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t f0_reg = 0; + uint64_t f0_tmp = 0; + + aw_info("enter"); + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_CONTRD16, val, + AW_I2C_BYTE_TWO); + f0_reg = (f0_reg | val[0]) << 8; + f0_reg |= (val[1] << 0); + if (!f0_reg) { + aw_err("didn't get cont f0 because f0_reg value is 0!"); + aw_haptic->cont_f0 = 0; + return; + } + f0_tmp = AW8623X_F0_FORMULA(f0_reg); + aw_haptic->cont_f0 = (uint32_t)f0_tmp; + aw_info("cont_f0=%d", aw_haptic->cont_f0); +} + +static int aw8623x_read_lra_f0(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t f0_reg = 0; + uint64_t f0_tmp = 0; + + aw_info("enter"); + /* F_LRA_F0 */ + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_CONTRD14, val, + AW_I2C_BYTE_TWO); + f0_reg = (f0_reg | val[0]) << 8; + f0_reg |= (val[1] << 0); + if (!f0_reg) { + aw_haptic->f0 = 0; + aw_err("didn't get lra f0 because f0_reg value is 0!"); + return -ERANGE; + } + f0_tmp = AW8623X_F0_FORMULA(f0_reg); + aw_haptic->f0 = (uint32_t)f0_tmp; + aw_info("lra_f0=%d", aw_haptic->f0); + return 0; +} + +static int aw8623x_get_f0(struct aw_haptic *aw_haptic) +{ + bool get_f0_flag = false; + uint8_t brk_en_temp = 0; + uint8_t val[3] = {0}; + int cnt = 200; + int ret = 0; + + aw_info("enter"); + aw_haptic->f0 = aw_haptic->info.f0_pre; + /* enter standby mode */ + aw8623x_play_stop(aw_haptic); + /* f0 calibrate work mode */ + aw8623x_play_mode(aw_haptic, AW_CONT_MODE); + /* enable f0 detect */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_CONTCFG1, + AW8623X_BIT_CONTCFG1_EN_F0_DET_MASK, + AW8623X_BIT_CONTCFG1_F0_DET_ENABLE); + /* cont config */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_CONTCFG6, + AW8623X_BIT_CONTCFG6_TRACK_EN_MASK, + (aw_haptic->info.is_enabled_track_en << 7)); + /* enable auto brake */ + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_PLAYCFG3, &val[0], + AW_I2C_BYTE_ONE); + brk_en_temp = AW8623X_BIT_PLAYCFG3_BRK & val[0]; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PLAYCFG3, + AW8623X_BIT_PLAYCFG3_BRK_EN_MASK, + AW8623X_BIT_PLAYCFG3_BRK_ENABLE); + /* f0 driver level */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_CONTCFG6, + AW8623X_BIT_CONTCFG6_DRV1_LVL_MASK, + aw_haptic->info.cont_drv1_lvl); + val[0] = (uint8_t)aw_haptic->info.cont_drv2_lvl; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_CONTCFG7, + AW8623X_BIT_CONTCFG7_DRV2_LVL_MASK, val[0]); + val[0] = (uint8_t)aw_haptic->info.cont_drv1_time; + val[1] = (uint8_t)aw_haptic->info.cont_drv2_time; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG8, val, + AW_I2C_BYTE_TWO); + /* TRACK_MARGIN */ + if (!aw_haptic->info.cont_track_margin) { + aw_err("dts_info.cont_track_margin = 0!"); + } else { + val[0] = (uint8_t)aw_haptic->info.cont_track_margin; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG11, &val[0], + AW_I2C_BYTE_ONE); + } + /* DRV_WIDTH */ + if (aw_haptic->info.f0_pre) + val[0] = AW8623X_DRV_WIDTH_FORMULA(aw_haptic->info.f0_pre, + aw_haptic->info.cont_track_margin, + aw_haptic->info.cont_brk_gain); + else + val[0] = AW8623X_BIT_DRV_WIDTH_DEF_VAL; + aw_info("cont_drv_width=0x%02X", val[0]); + if (val[0] < AW8623X_DRV_WIDTH_MIN || val[0] > AW8623X_DRV_WIDTH_MAX) { + aw_err("cont_drv_width[0x%02X] is error, restore default vale[0x%02X]", + val[0], AW8623X_BIT_DRV_WIDTH_DEF_VAL); + val[0] = AW8623X_BIT_DRV_WIDTH_DEF_VAL; + } + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG3, &val[0], + AW_I2C_BYTE_ONE); + /* cont play go */ + aw8623x_play_go(aw_haptic); + usleep_range(20000, 20500); + while (cnt) { + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_GLBRD5, &val[0], + AW_I2C_BYTE_ONE); + if ((val[0] & AW_BIT_GLBRD_STATE_MASK) == AW_BIT_STATE_STANDBY) { + get_f0_flag = true; + aw_info("entered standby! glb_state=0x%02X", val[0]); + break; + } + cnt--; + usleep_range(AW_F0_DELAY_MIN, AW_F0_DELAY_MAX); + } + if (get_f0_flag) { + ret = aw8623x_read_lra_f0(aw_haptic); + if (ret < 0) + aw_err("read lra f0 is failed"); + aw8623x_read_cont_f0(aw_haptic); + } else { + ret = -ERANGE; + aw_err("enter standby mode failed, stop reading f0!"); + } + /* disable f0 detect */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_CONTCFG1, + AW8623X_BIT_CONTCFG1_EN_F0_DET_MASK, + AW8623X_BIT_CONTCFG1_F0_DET_DISABLE); + /* recover auto break config */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PLAYCFG3, + AW8623X_BIT_PLAYCFG3_BRK_EN_MASK, brk_en_temp); + return ret; +} + +static void aw8623x_set_base_addr(struct aw_haptic *aw_haptic) +{ + uint32_t base_addr = 0; + uint8_t val = 0; + + aw_info("enter"); + base_addr = aw_haptic->ram.base_addr; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_RTPCFG1, + AW8623X_BIT_RTPCFG1_ADDRH_MASK, + (uint8_t)AW_SET_BASEADDR_H(base_addr)); + val = (uint8_t)AW_SET_BASEADDR_L(base_addr); + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_RTPCFG2, &val, + AW_I2C_BYTE_ONE); +} + +static void aw8623x_set_fifo_addr(struct aw_haptic *aw_haptic) +{ + uint8_t ae_addr_h = 0; + uint8_t af_addr_h = 0; + uint8_t val[3] = {0}; + + aw_info("enter"); + ae_addr_h = (uint8_t)AW8623X_SET_AEADDR_H(aw_haptic->ram.base_addr); + af_addr_h = (uint8_t)AW8623X_SET_AFADDR_H(aw_haptic->ram.base_addr); + val[0] = ae_addr_h | af_addr_h; + val[1] = (uint8_t)AW8623X_SET_AEADDR_L(aw_haptic->ram.base_addr); + val[2] = (uint8_t)AW8623X_SET_AFADDR_L(aw_haptic->ram.base_addr); + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_RTPCFG3, val, + AW_I2C_BYTE_THREE); +} + +static void aw8623x_get_fifo_addr(struct aw_haptic *aw_haptic) +{ + uint8_t ae_addr_h = 0; + uint8_t af_addr_h = 0; + uint8_t ae_addr_l = 0; + uint8_t af_addr_l = 0; + uint8_t val[3] = {0}; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_RTPCFG3, val, + AW_I2C_BYTE_THREE); + ae_addr_h = ((val[0]) & AW8623X_BIT_RTPCFG3_FIFO_AEH) >> 4; + ae_addr_l = val[1]; + af_addr_h = ((val[0]) & AW8623X_BIT_RTPCFG3_FIFO_AFH); + af_addr_l = val[2]; + aw_info("almost_empty_threshold = %d,almost_full_threshold = %d", + (uint16_t)((ae_addr_h << 8) | ae_addr_l), + (uint16_t)((af_addr_h << 8) | af_addr_l)); +} + +static void aw8623x_set_ram_addr(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t base_addr = aw_haptic->ram.base_addr; + + val[0] = (uint8_t)AW_SET_RAMADDR_H(base_addr); + val[1] = (uint8_t)AW_SET_RAMADDR_L(base_addr); + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_RAMADDRH, val, + AW_I2C_BYTE_TWO); +} + +static void aw8623x_set_ram_data(struct aw_haptic *aw_haptic, uint8_t *data, + int len) +{ + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_RAMDATA, data, len); +} + +static void aw8623x_get_first_wave_addr(struct aw_haptic *aw_haptic, + uint32_t *first_wave_addr) +{ + uint8_t val[3] = {0}; + + aw8623x_set_ram_addr(aw_haptic); + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_RAMDATA, val, + AW_I2C_BYTE_THREE); + *first_wave_addr = (val[1] << 8 | val[2]); +} + +static void aw8623x_select_pin(struct aw_haptic *aw_haptic, uint8_t pin) +{ + if (pin == AW_TRIG1) { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL1, + AW8623X_BIT_SYSCTRL1_INTN_PIN_MASK, + AW8623X_BIT_SYSCTRL1_TRIG1); + aw_info("TRIG1 pin used as TRIG1"); + } else if (pin == AW_IRQ) { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL1, + AW8623X_BIT_SYSCTRL1_INTN_PIN_MASK, + AW8623X_BIT_SYSCTRL1_INTN); + aw_info("TRIG1 pin used as INIT"); + } else { + aw_err("There is no such option"); + } +} + +static void aw8623x_trig_param_init(struct aw_haptic *aw_haptic, uint8_t pin) +{ + struct aw_haptic_dts_info info = aw_haptic->info; + + switch (pin) { + case AW_TRIG1: + aw_haptic->trig[0].trig_level = info.trig_cfg[0]; + aw_haptic->trig[0].trig_polar = info.trig_cfg[1]; + aw_haptic->trig[0].pos_enable = info.trig_cfg[2]; + aw_haptic->trig[0].pos_sequence = info.trig_cfg[3]; + aw_haptic->trig[0].neg_enable = info.trig_cfg[4]; + aw_haptic->trig[0].neg_sequence = info.trig_cfg[5]; + aw_haptic->trig[0].trig_brk = info.trig_cfg[6]; + break; + case AW_TRIG2: + aw_haptic->trig[1].trig_level = info.trig_cfg[7 + 0]; + aw_haptic->trig[1].trig_polar = info.trig_cfg[7 + 1]; + aw_haptic->trig[1].pos_enable = info.trig_cfg[7 + 2]; + aw_haptic->trig[1].pos_sequence = info.trig_cfg[7 + 3]; + aw_haptic->trig[1].neg_enable = info.trig_cfg[7 + 4]; + aw_haptic->trig[1].neg_sequence = info.trig_cfg[7 + 5]; + aw_haptic->trig[1].trig_brk = info.trig_cfg[7 + 6]; + break; + case AW_TRIG3: + aw_haptic->trig[2].trig_level = info.trig_cfg[14 + 0]; + aw_haptic->trig[2].trig_polar = info.trig_cfg[14 + 1]; + aw_haptic->trig[2].pos_enable = info.trig_cfg[14 + 2]; + aw_haptic->trig[2].pos_sequence = info.trig_cfg[14 + 3]; + aw_haptic->trig[2].neg_enable = info.trig_cfg[14 + 4]; + aw_haptic->trig[2].neg_sequence = info.trig_cfg[14 + 5]; + aw_haptic->trig[2].trig_brk = info.trig_cfg[14 + 6]; + break; + default: + break; + } +} + +static int aw8623x_trig_param_config(struct aw_haptic *aw_haptic, uint8_t pin) +{ + uint8_t trig_polar_lev_brk = 0x00; + uint8_t trig_pos_seq = 0x00; + uint8_t trig_neg_seq = 0x00; + + switch (pin) { + case AW_TRIG1: + if (aw_haptic->is_used_irq_pin) { + aw8623x_trig_param_init(aw_haptic, AW_TRIG1); + aw8623x_select_pin(aw_haptic, AW_IRQ); + return 0; + } + aw8623x_select_pin(aw_haptic, AW_TRIG1); + trig_polar_lev_brk = aw_haptic->trig[0].trig_polar << 2 | + aw_haptic->trig[0].trig_level << 1 | + aw_haptic->trig[0].trig_brk; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_TRGCFG7, + AW8623X_BIT_TRGCFG7_TRG1_POR_LEV_BRK_MASK, + trig_polar_lev_brk << 3); + trig_pos_seq = aw_haptic->trig[0].pos_enable << 7 | + aw_haptic->trig[0].pos_sequence; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_TRGCFG1, + &trig_pos_seq, AW_I2C_BYTE_ONE); + trig_neg_seq = aw_haptic->trig[0].neg_enable << 7 | + aw_haptic->trig[0].neg_sequence; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_TRGCFG4, + &trig_neg_seq, AW_I2C_BYTE_ONE); + aw_info("trig1 config ok!"); + break; + case AW_TRIG2: + trig_polar_lev_brk = aw_haptic->trig[1].trig_polar << 2 | + aw_haptic->trig[1].trig_level << 1 | + aw_haptic->trig[1].trig_brk; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_TRGCFG7, + AW8623X_BIT_TRGCFG7_TRG2_POR_LEV_BRK_MASK, + trig_polar_lev_brk << 0); + trig_pos_seq = aw_haptic->trig[1].pos_enable << 7 | + aw_haptic->trig[1].pos_sequence; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_TRGCFG2, + &trig_pos_seq, AW_I2C_BYTE_ONE); + trig_neg_seq = aw_haptic->trig[1].neg_enable << 7 | + aw_haptic->trig[1].neg_sequence; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_TRGCFG5, + &trig_neg_seq, AW_I2C_BYTE_ONE); + aw_info("trig2 config ok!"); + break; + case AW_TRIG3: + trig_polar_lev_brk = aw_haptic->trig[2].trig_polar << 2 | + aw_haptic->trig[2].trig_level << 1 | + aw_haptic->trig[2].trig_brk; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_TRGCFG8, + AW8623X_BIT_TRGCFG8_TRG3_POR_LEV_BRK_MASK, + trig_polar_lev_brk << 3); + trig_pos_seq = aw_haptic->trig[2].pos_enable << 7 | + aw_haptic->trig[2].pos_sequence; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_TRGCFG3, + &trig_pos_seq, AW_I2C_BYTE_ONE); + trig_neg_seq = aw_haptic->trig[2].neg_enable << 7 | + aw_haptic->trig[2].neg_sequence; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_TRGCFG6, + &trig_neg_seq, AW_I2C_BYTE_ONE); + aw_info("trig3 config ok!"); + break; + default: + break; + } + return 0; +} + +static void aw8623x_set_trig(struct aw_haptic *aw_haptic, uint8_t pin) +{ + aw8623x_trig_param_init(aw_haptic, pin); + aw8623x_trig_param_config(aw_haptic, pin); +} + +static void aw8623x_trig_init(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + aw8623x_set_trig(aw_haptic, AW_TRIG1); + aw8623x_set_trig(aw_haptic, AW_TRIG2); + aw8623x_set_trig(aw_haptic, AW_TRIG3); +} + +static void aw8623x_get_wav_seq(struct aw_haptic *aw_haptic, uint32_t len) +{ + if (len > AW_SEQUENCER_SIZE) + len = AW_SEQUENCER_SIZE; + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_WAVCFG1, aw_haptic->seq, len); +} + +static void aw8623x_set_wav_seq(struct aw_haptic *aw_haptic, uint8_t wav, + uint8_t seq) +{ + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_WAVCFG1 + wav, &seq, + AW_I2C_BYTE_ONE); +} + +static void aw8623x_set_wav_loop(struct aw_haptic *aw_haptic, uint8_t wav, + uint8_t loop) +{ + uint8_t tmp = 0; + + if (wav % 2) { + tmp = loop << 0; + haptic_nv_i2c_write_bits(aw_haptic, + AW8623X_REG_WAVCFG9 + (wav / 2), + AW8623X_BIT_WAVLOOP_SEQ_EVEN_MASK, tmp); + } else { + tmp = loop << 4; + haptic_nv_i2c_write_bits(aw_haptic, + AW8623X_REG_WAVCFG9 + (wav / 2), + AW8623X_BIT_WAVLOOP_SEQ_ODD_MASK, tmp); + } +} + +static void aw8623x_set_repeat_seq(struct aw_haptic *aw_haptic, uint8_t seq) +{ + aw8623x_set_wav_seq(aw_haptic, 0x00, seq); + aw8623x_set_wav_seq(aw_haptic, 0x01, 0x00); + aw8623x_set_wav_loop(aw_haptic, 0x00, AW8623X_BIT_WAVLOOP_INIFINITELY); +} + +static void aw8623x_get_gain(struct aw_haptic *aw_haptic, uint8_t *gain) +{ + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_PLAYCFG2, gain, + AW_I2C_BYTE_ONE); +} + +static void aw8623x_get_wav_loop(struct aw_haptic *aw_haptic, uint8_t *val) +{ + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_WAVCFG9, val, + AW_SEQUENCER_LOOP_SIZE); +} + +static void aw8623x_get_ram_data(struct aw_haptic *aw_haptic, uint8_t *ram_data, + int size) +{ + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_RAMDATA, ram_data, size); +} + +static void aw8623x_get_lra_resistance(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val[2] = {0}; + uint8_t adc_fs = 0; + uint32_t lra_code = 0; + int d2s_gain = 0; + + /* enter standby mode */ + aw8623x_play_stop(aw_haptic); + aw8623x_raminit(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG2, + AW8623X_BIT_DETCFG2_DET_SEQ0_MASK, + AW8623X_BIT_DETCFG2_DET_SEQ0_RL); + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_DETCFG1, &adc_fs, + AW_I2C_BYTE_ONE); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG1, + AW8623X_BIT_DETCFG1_ADC_FS_MASK, + AW8623X_BIT_DETCFG1_ADC_FS_24K); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG1, + AW8623X_BIT_DETCFG1_DET_GO_MASK, + AW8623X_BIT_DETCFG1_DET_GO_ON); + usleep_range(AW_RL_DELAY_MIN, AW_RL_DELAY_MAX); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG1, + AW8623X_BIT_DETCFG1_DET_GO_MASK, + AW8623X_BIT_DETCFG1_DET_GO_OFF); + aw8623x_raminit(aw_haptic, false); + /* restore adc fs */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG1, + AW8623X_BIT_DETCFG1_ADC_FS_MASK, + adc_fs); + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_DETCFG2, reg_val, + AW_I2C_BYTE_ONE); + reg_val[0] = (~AW8623X_BIT_DETCFG2_D2S_GAIN_MASK) & reg_val[0]; + d2s_gain = aw8623x_select_d2s_gain(reg_val[0]); + if (d2s_gain < 0) { + aw_err("d2s_gain is error"); + return; + } + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_DETRD1, reg_val, + AW_I2C_BYTE_TWO); + lra_code =reg_val[0] & (~AW8623X_BIT_DETRD1_AVG_DATA_H_MASK); + lra_code = (lra_code << 8) | reg_val[1]; + /* 2num */ + aw_haptic->lra = AW8623X_RL_FORMULA(lra_code, d2s_gain); +} + +static uint8_t aw8623x_get_prctmode(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_PWMCFG3, ®_val, + AW_I2C_BYTE_ONE); + reg_val >>= 7; + return reg_val; +} + +static uint8_t aw8623x_judge_rtp_going(struct aw_haptic *aw_haptic) +{ + uint8_t glb_state = 0; + uint8_t rtp_state = 0; + + glb_state = aw8623x_get_glb_state(aw_haptic); + if (glb_state == AW_BIT_STATE_RTP_GO) { + rtp_state = 1; /*is going on */ + aw_info("rtp_routine_on"); + } + return rtp_state; +} + +static uint64_t aw8623x_get_theory_time(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t fre_val = 0; + uint64_t theory_time = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_SYSCTRL2, ®_val, + AW_I2C_BYTE_ONE); + fre_val = reg_val & AW8623X_BIT_SYSCTRL2_RATE; + if (fre_val == AW8623X_BIT_SYSCTRL2_RATE_48K) + theory_time = (aw_haptic->rtp_len / 48000) * 1000000; /*48K*/ + else if (fre_val == AW8623X_BIT_SYSCTRL2_RATE_24K) + theory_time = (aw_haptic->rtp_len / 24000) * 1000000; /*24K*/ + else + theory_time = (aw_haptic->rtp_len / 12000) * 1000000; /*12K*/ + aw_info("microsecond:%llu theory_time = %llu", + aw_haptic->microsecond, theory_time); + return theory_time; +} + +static uint8_t aw8623x_get_osc_status(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_SYSST2, &state, + AW_I2C_BYTE_ONE); + state &= AW8623X_BIT_SYSST2_FF_EMPTY; + return state; +} + +static int aw8623x_check_qualify(struct aw_haptic *aw_haptic) +{ +//TODO +#if 0 + uint8_t reg = 0; + int ret = 0; + + aw_info("enter"); + ret = haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_EFRD9, ®, + AW_I2C_BYTE_ONE); + if (ret < 0) + return ret; + if ((reg & 0x80) == 0x80) + return 0; + aw_err("register 0x64 error: 0x%02X", reg); + return -ERANGE; +#endif + return 0; +} + +static ssize_t cont_wait_num_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_wait_num = 0x%02X\n", + aw_haptic->info.cont_wait_num); + return len; +} + +static ssize_t cont_wait_num_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t reg_val = 0; + int err, val; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + err = kstrtoint(buf, 16, &val); + if (err != 0) { + aw_err("format not match!"); + return count; + } + mutex_lock(&aw_haptic->lock); + aw_haptic->info.cont_wait_num = val; + reg_val = aw_haptic->info.cont_wait_num; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG4, ®_val, + AW_I2C_BYTE_ONE); + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t cont_drv_lvl_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_lvl = 0x%02X, cont_drv2_lvl = 0x%02X\n", + aw_haptic->info.cont_drv1_lvl, + aw_haptic->info.cont_drv2_lvl); + return len; +} + +static ssize_t cont_drv_lvl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint8_t reg_val = 0; + uint32_t databuf[2] = { 0, 0 }; + + mutex_lock(&aw_haptic->lock); + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_haptic->info.cont_drv1_lvl = databuf[0]; + aw_haptic->info.cont_drv2_lvl = databuf[1]; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_CONTCFG6, + AW8623X_BIT_CONTCFG6_DRV1_LVL_MASK, + aw_haptic->info.cont_drv1_lvl); + reg_val = (uint8_t)aw_haptic->info.cont_drv2_lvl; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG7, ®_val, + AW_I2C_BYTE_ONE); + } + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t cont_drv_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_time = 0x%02X, cont_drv2_time = 0x%02X\n", + aw_haptic->info.cont_drv1_time, + aw_haptic->info.cont_drv2_time); + return len; +} + +static ssize_t cont_drv_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint8_t reg_val[2] = {0}; + uint32_t databuf[2] = { 0, 0 }; + + mutex_lock(&aw_haptic->lock); + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_haptic->info.cont_drv1_time = databuf[0]; + aw_haptic->info.cont_drv2_time = databuf[1]; + reg_val[0] = (uint8_t)aw_haptic->info.cont_drv1_time; + reg_val[1] = (uint8_t)aw_haptic->info.cont_drv2_time; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG8, reg_val, + AW_I2C_BYTE_TWO); + } + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t cont_brk_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "cont_brk_time = 0x%02X\n", + aw_haptic->info.cont_brk_time); + return len; +} + +static ssize_t cont_brk_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t reg_val = 0; + int err, val; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + err = kstrtoint(buf, 16, &val); + if (err != 0) { + aw_err("format not match!"); + return count; + } + mutex_lock(&aw_haptic->lock); + aw_haptic->info.cont_brk_time = val; + reg_val = aw_haptic->info.cont_brk_time; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG10, ®_val, + AW_I2C_BYTE_ONE); + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t trig_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i = 0; + ssize_t len = 0; + uint8_t trig_num = 3; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + for (i = 0; i < trig_num; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "trig%d: trig_level=%d, trig_polar=%d", + i + 1, + aw_haptic->trig[i].trig_level, + aw_haptic->trig[i].trig_polar); + len += snprintf(buf + len, PAGE_SIZE - len, + "pos_enable=%d, pos_sequence=%d,", + aw_haptic->trig[i].pos_enable, + aw_haptic->trig[i].pos_sequence); + len += snprintf(buf + len, PAGE_SIZE - len, + "neg_enable=%d, neg_sequence=%d trig_brk=%d\n", + aw_haptic->trig[i].neg_enable, + aw_haptic->trig[i].neg_sequence, + aw_haptic->trig[i].trig_brk); + } + return len; +} + +static ssize_t trig_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t databuf[9] = { 0 }; + + if (sscanf(buf, "%d %d %d %d %d %d %d %d", + &databuf[0], &databuf[1], &databuf[2], + &databuf[3], &databuf[4], &databuf[5], + &databuf[6], &databuf[7]) == 8) { + aw_info("%d, %d, %d, %d, %d, %d, %d, %d", + databuf[0], databuf[1], databuf[2], databuf[3], + databuf[4], databuf[5], databuf[6], databuf[7]); + if (databuf[0] < 0 || databuf[0] > 2) { + aw_err("input seq value out of range!"); + return count; + } + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + if (databuf[4] > aw_haptic->ram.ram_num || + databuf[6] > aw_haptic->ram.ram_num) { + aw_err("input seq value out of range!"); + return count; + } + aw_haptic->trig[databuf[0]].trig_level = databuf[1]; + aw_haptic->trig[databuf[0]].trig_polar = databuf[2]; + aw_haptic->trig[databuf[0]].pos_enable = databuf[3]; + aw_haptic->trig[databuf[0]].pos_sequence = databuf[4]; + aw_haptic->trig[databuf[0]].neg_enable = databuf[5]; + aw_haptic->trig[databuf[0]].neg_sequence = databuf[6]; + aw_haptic->trig[databuf[0]].trig_brk = databuf[7]; + mutex_lock(&aw_haptic->lock); + aw8623x_trig_param_config(aw_haptic, databuf[0]); + mutex_unlock(&aw_haptic->lock); + } else + aw_err("please input eight parameters"); + return count; +} + +static DEVICE_ATTR(cont_wait_num, 0664, cont_wait_num_show, cont_wait_num_store); +static DEVICE_ATTR(cont_drv_lvl, 0664, cont_drv_lvl_show, cont_drv_lvl_store); +static DEVICE_ATTR(cont_drv_time, 0664, cont_drv_time_show, cont_drv_time_store); +static DEVICE_ATTR(cont_brk_time, 0664, cont_brk_time_show, cont_brk_time_store); +static DEVICE_ATTR(trig, 0664, trig_show, trig_store); + +static struct attribute *aw8623x_vibrator_attributes[] = { + &dev_attr_cont_wait_num.attr, + &dev_attr_cont_drv_lvl.attr, + &dev_attr_cont_drv_time.attr, + &dev_attr_cont_brk_time.attr, + &dev_attr_trig.attr, + NULL +}; + +static struct attribute_group aw8623x_vibrator_attribute_group = { + .attrs = aw8623x_vibrator_attributes +}; + +static void aw8623x_creat_node(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + ret = sysfs_create_group(&aw_haptic->vib_dev.dev->kobj, + &aw8623x_vibrator_attribute_group); + if (ret < 0) + aw_err("error create aw8623x sysfs attr files"); +} + +struct aw_haptic_func aw8623x_func_list = { + .ram_init = aw8623x_raminit, + .trig_init = aw8623x_trig_init, + .play_mode = aw8623x_play_mode, + .play_stop = aw8623x_play_stop, + .irq_clear = aw8623x_irq_clear, + .creat_node = aw8623x_creat_node, + .cont_config = aw8623x_cont_config, + .offset_cali = aw8623x_offset_cali, + .haptic_start = aw8623x_play_go, + .read_cont_f0 = aw8623x_read_cont_f0, + .check_qualify = aw8623x_check_qualify, + .judge_rtp_going = aw8623x_judge_rtp_going, + .protect_config = aw8623x_protect_config, + .misc_para_init = aw8623x_misc_para_init, + .interrupt_setup = aw8623x_interrupt_setup, + .rtp_get_fifo_afs = aw8623x_rtp_get_fifo_afs, + .vbat_mode_config = aw8623x_vbat_mode_config, + .calculate_cali_data = aw8623x_calculate_cali_data, + .set_gain = aw8623x_set_gain, + .get_gain = aw8623x_get_gain, + .set_wav_seq = aw8623x_set_wav_seq, + .get_wav_seq = aw8623x_get_wav_seq, + .set_wav_loop = aw8623x_set_wav_loop, + .get_wav_loop = aw8623x_get_wav_loop, + .set_ram_data = aw8623x_set_ram_data, + .get_ram_data = aw8623x_get_ram_data, + .set_fifo_addr = aw8623x_set_fifo_addr, + .get_fifo_addr = aw8623x_get_fifo_addr, + .set_rtp_aei = aw8623x_set_rtp_aei, + .set_rtp_data = aw8623x_set_rtp_data, + .set_ram_addr = aw8623x_set_ram_addr, + .set_trim_lra = aw8623x_set_trim_lra, + .set_base_addr = aw8623x_set_base_addr, + .set_repeat_seq = aw8623x_set_repeat_seq, + .get_f0 = aw8623x_get_f0, + .get_reg = aw8623x_get_reg, + .get_vbat = aw8623x_get_vbat, + .get_prctmode = aw8623x_get_prctmode, + .get_irq_state = aw8623x_get_irq_state, + .get_glb_state = aw8623x_get_glb_state, + .get_osc_status = aw8623x_get_osc_status, + .get_theory_time = aw8623x_get_theory_time, + .get_lra_resistance = aw8623x_get_lra_resistance, + .get_first_wave_addr = aw8623x_get_first_wave_addr, +}; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/drivers/misc/awinic/aw862x_haptic_nv/aw8624.c b/drivers/misc/awinic/aw862x_haptic_nv/aw8624.c new file mode 100755 index 000000000000..581d536b0c40 --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv/aw8624.c @@ -0,0 +1,1407 @@ +/* + * aw8624.c + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "haptic_nv.h" +#include "haptic_nv_reg.h" +static int aw8624_get_irq_state(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint8_t glb_st = 0; + int ret = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_dbg("reg SYSINT=0x%02X", reg_val); + if (reg_val & AW8624_BIT_SYSINT_OCDI) + aw_err("chip over current int error"); + if (reg_val & AW8624_BIT_SYSINT_OTI) + aw_err("chip over temperature int error"); + if (reg_val & AW8624_BIT_SYSINT_DONEI) + aw_info("chip playback done"); + if (reg_val & AW8624_BIT_SYSINT_UVLI) { + aw_err("chip uvlo int error"); + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_GLB_STATE, &glb_st, + AW_I2C_BYTE_ONE); + if (glb_st == 0) { + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_SYSINTM, + AW8624_BIT_SYSINTM_UVLO_MASK, + AW8624_BIT_SYSINTM_UVLO_OFF); + } + } + if (reg_val & AW8624_BIT_SYSINT_FF_AFI) + aw_info("rtp mode fifo full"); + if (reg_val & AW8624_BIT_SYSINT_FF_AEI) { + aw_info("rtp fifo almost empty"); + ret = AW_IRQ_ALMOST_EMPTY; + } + return ret; +} + +static void aw8624_interrupt_setup(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_info("reg SYSINT=0x%X", reg_val); + + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_DBGCTRL, + AW8624_BIT_DBGCTRL_INT_MODE_MASK, + AW8624_BIT_DBGCTRL_INT_MODE_EDGE); + + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_SYSINTM, + (AW8624_BIT_SYSINTM_OV_MASK & + AW8624_BIT_SYSINTM_UVLO_MASK & + AW8624_BIT_SYSINTM_FF_AE_MASK & + AW8624_BIT_SYSINTM_FF_AF_MASK & + AW8624_BIT_SYSINTM_OCD_MASK & + AW8624_BIT_SYSINTM_OT_MASK & + AW8624_BIT_SYSINTM_DONE_MASK), + (AW8624_BIT_SYSINTM_OV_OFF | + AW8624_BIT_SYSINTM_UVLO_EN | + AW8624_BIT_SYSINTM_FF_AE_OFF | + AW8624_BIT_SYSINTM_FF_AF_OFF | + AW8624_BIT_SYSINTM_OCD_EN | + AW8624_BIT_SYSINTM_OT_EN | + AW8624_BIT_SYSINTM_DONE_OFF)); +} + +static void aw8624_set_rtp_data(struct aw_haptic *aw_haptic, uint8_t *data, + uint32_t len) +{ + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_RTP_DATA, data, len); +} + +static uint8_t aw8624_get_glb_state(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_GLB_STATE, &state, + AW_I2C_BYTE_ONE); + return state; +} + +static uint8_t aw8624_rtp_get_fifo_afs(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_SYSST, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= AW8624_BIT_SYSST_FF_AFS; + reg_val = reg_val >> 3; + return reg_val; +} + +static void aw8624_set_rtp_aei(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_SYSINTM, + AW8624_BIT_SYSINTM_FF_AE_MASK, + AW8624_BIT_SYSINTM_FF_AE_EN); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_SYSINTM, + AW8624_BIT_SYSINTM_FF_AE_MASK, + AW8624_BIT_SYSINTM_FF_AE_OFF); + } +} + +static int aw8624_haptic_play_init(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + if (aw_haptic->play_mode == AW_CONT_MODE) + reg_val = (uint8_t)(aw_haptic->info.sw_brake[0]); + else + reg_val = (uint8_t)(aw_haptic->info.sw_brake[1]); + + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_SW_BRAKE, ®_val, + AW_I2C_BYTE_ONE); + return 0; +} + +static void aw8624_interrupt_clear(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_info("reg SYSINT=0x%2X", reg_val); +} + +static void aw8624_haptic_active(struct aw_haptic *aw_haptic) +{ + aw8624_haptic_play_init(aw_haptic); + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_WORK_MODE_MASK, + AW8624_BIT_SYSCTRL_ACTIVE); + aw8624_interrupt_clear(aw_haptic); + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_SYSINTM, + AW8624_BIT_SYSINTM_UVLO_MASK, + AW8624_BIT_SYSINTM_UVLO_EN); +} + +static void aw8624_play_mode(struct aw_haptic *aw_haptic, uint8_t play_mode) +{ + switch (play_mode) { + case AW_STANDBY_MODE: + aw_info("enter standby mode"); + aw_haptic->play_mode = AW_STANDBY_MODE; + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_SYSINTM, + AW8624_BIT_SYSINTM_UVLO_MASK, + AW8624_BIT_SYSINTM_UVLO_OFF); + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_WORK_MODE_MASK, + AW8624_BIT_SYSCTRL_STANDBY); + break; + case AW_RAM_MODE: + aw_info("enter ram mode"); + aw_haptic->play_mode = AW_RAM_MODE; + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8624_BIT_SYSCTRL_PLAY_MODE_RAM); + aw8624_haptic_active(aw_haptic); + break; + case AW_RAM_LOOP_MODE: + aw_info("enter ram loop mode"); + aw_haptic->play_mode = AW_RAM_LOOP_MODE; + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8624_BIT_SYSCTRL_PLAY_MODE_RAM); + aw8624_haptic_active(aw_haptic); + break; + case AW_RTP_MODE: + aw_info("enter rtp mode"); + aw_haptic->play_mode = AW_RTP_MODE; + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8624_BIT_SYSCTRL_PLAY_MODE_RTP); + aw8624_haptic_active(aw_haptic); + break; + case AW_TRIG_MODE: + aw_info("enter trig mode"); + aw_haptic->play_mode = AW_TRIG_MODE; + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8624_BIT_SYSCTRL_PLAY_MODE_RAM); + aw8624_haptic_active(aw_haptic); + break; + case AW_CONT_MODE: + aw_info("enter cont mode"); + aw_haptic->play_mode = AW_CONT_MODE; + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8624_BIT_SYSCTRL_PLAY_MODE_CONT); + aw8624_haptic_active(aw_haptic); + break; + default: + aw_err("play mode %d error", play_mode); + break; + } +} + +static int aw8624_play_go(struct aw_haptic *aw_haptic, bool flag) +{ + uint8_t val = 0; + + aw_info("enter, flag = %d", flag); + if (!flag) { + aw_haptic->current_t = ktime_get(); + aw_haptic->interval_us = ktime_to_us(ktime_sub(aw_haptic->current_t, + aw_haptic->pre_enter_t)); + if (aw_haptic->interval_us < 2000) { + aw_info("interval_us=%d", aw_haptic->interval_us); + usleep_range(AW_PLAY_DELAY_MIN, AW_PLAY_DELAY_MAX); + } + } + if (flag == true) { + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_GO, + AW8624_BIT_GO_MASK, + AW8624_BIT_GO_ENABLE); + aw_haptic->pre_enter_t = ktime_get(); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_GO, + AW8624_BIT_GO_MASK, + AW8624_BIT_GO_DISABLE); + } + val = aw8624_get_glb_state(aw_haptic); + aw_info("reg:0x%02X=0x%02X", AW8624_REG_GLB_STATE, val); + return 0; +} + +static int aw8624_stop_delay(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int cnt = 100; + + while (cnt--) { + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_GLB_STATE, ®_val, + AW_I2C_BYTE_ONE); + if ((reg_val & AW_BIT_GLBRD_STATE_MASK) == AW_BIT_STATE_STANDBY) { + aw_info("enter standby,reg glb_state=0x%02X", reg_val); + return 0; + } + usleep_range(AW_STOP_DELAY_MIN, AW_STOP_DELAY_MAX); + aw_info("wait for standby,reg glb_state=0x%02X", reg_val); + } + aw_err("do not enter standby automatically"); + return 0; +} + +static void aw8624_play_stop(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + aw8624_play_go(aw_haptic, false); + aw8624_stop_delay(aw_haptic); + aw8624_play_mode(aw_haptic, AW_STANDBY_MODE); +} + +static void aw8624_haptic_start(struct aw_haptic *aw_haptic) +{ + aw8624_haptic_active(aw_haptic); + aw8624_play_go(aw_haptic, true); +} + +static void aw8624_raminit(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_RAMINIT_MASK, + AW8624_BIT_SYSCTRL_RAMINIT_EN); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_SYSCTRL, + AW8624_BIT_SYSCTRL_RAMINIT_MASK, + AW8624_BIT_SYSCTRL_RAMINIT_OFF); + } +} + +static void aw8624_get_vbat(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + aw8624_play_stop(aw_haptic); + /*step 1:EN_RAMINIT*/ + aw8624_raminit(aw_haptic, true); + /*step 2 :launch power supply testing */ + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_DETCTRL, + AW8624_BIT_DETCTRL_VBAT_GO_MASK, + AW8624_BIT_DETCTRL_VABT_GO_ENABLE); + usleep_range(AW_VBAT_DELAY_MIN, AW_VBAT_DELAY_MAX); + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_VBATDET, ®_val, + AW_I2C_BYTE_ONE); + aw_haptic->vbat = AW8624_VBAT_FORMULA(reg_val); + aw_info("get_vbat=%dmV, vbat_code=0x%02X", aw_haptic->vbat, reg_val); + /*step 3: return val*/ + aw8624_raminit(aw_haptic, false); +} + +static void aw8624_set_gain(struct aw_haptic *aw_haptic, uint8_t gain) +{ + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_DATDBG, &gain, + AW_I2C_BYTE_ONE); +} + +static void aw8624_set_trim_lra(struct aw_haptic *aw_haptic, uint8_t val) +{ + uint8_t reg_val = 0; + + reg_val = val & AW8624_BIT_TRIM_LRA; + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_TRIM_LRA, ®_val, + AW_I2C_BYTE_ONE); +} + +static int aw8624_set_f0_preset(struct aw_haptic *aw_haptic, uint32_t f0_pre) +{ + uint32_t f0_reg = 0; + uint8_t reg_array[2] = {0}; + + if (!f0_pre || !aw_haptic->info.f0_coeff) { + aw_err("f0_pre or f0_coeff is error, f0_pre=%d, f0_coeff=%d", + f0_pre, aw_haptic->info.f0_coeff); + return -ERANGE; + } + f0_reg = 1000000000 / (f0_pre * aw_haptic->info.f0_coeff); + reg_array[0] = (uint8_t)((f0_reg >> 8) & 0xff); + reg_array[1] = (uint8_t)((f0_reg >> 0) & 0xff); + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_F_PRE_H, reg_array, + AW_I2C_BYTE_TWO); + return 0; +} + +static void aw8624_cont_config(struct aw_haptic *aw_haptic) +{ + uint8_t brake0_level = 0; + uint8_t time_nzc = 0; + uint8_t en_brake1 = 0; + uint8_t brake1_level = 0; + uint8_t en_brake2 = 0; + uint8_t brake2_level = 0; + uint8_t brake2_num = 0; + uint8_t brake1_num = 0; + uint8_t brake0_num = 0; + uint8_t val[4] = {0}; + int ret = 0; + + aw_info("enter"); + aw8624_haptic_active(aw_haptic); + aw8624_play_mode(aw_haptic, AW_CONT_MODE); + /* preset f0 */ + if (aw_haptic->f0 <= 0) + ret = aw8624_set_f0_preset(aw_haptic, aw_haptic->info.f0_pre); + else + ret = aw8624_set_f0_preset(aw_haptic, aw_haptic->f0); + if (ret < 0) + return; + /* lpf */ + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_DATCTRL, + (AW8624_BIT_DATCTRL_FC_MASK & + AW8624_BIT_DATCTRL_LPF_ENABLE_MASK), + (AW8624_BIT_DATCTRL_FC_1000HZ | + AW8624_BIT_DATCTRL_LPF_ENABLE)); + /* brake */ + en_brake1 = aw_haptic->info.cont_brake[0]; + en_brake2 = aw_haptic->info.cont_brake[1]; + brake0_level = aw_haptic->info.cont_brake[2]; + brake1_level = aw_haptic->info.cont_brake[3]; + brake2_level = aw_haptic->info.cont_brake[4]; + brake0_num = aw_haptic->info.cont_brake[5]; + brake1_num = aw_haptic->info.cont_brake[6]; + brake2_num = aw_haptic->info.cont_brake[7]; + val[0] = brake0_level << 0; + val[1] = (en_brake1 << 7)|(brake1_level << 0); + val[2] = (en_brake2 << 7)|(brake2_level << 0); + val[3] = (brake2_num << 6) | (brake1_num << 3) | (brake0_num << 0); + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_BRAKE0_CTRL, val, + AW_I2C_BYTE_FOUR); + /* cont config */ + val[0] = AW8624_BIT_CONT_CTRL_ZC_DETEC_ENABLE | + AW8624_BIT_CONT_CTRL_WAIT_1PERIOD | + AW8624_BIT_CONT_CTRL_BY_GO_SIGNAL | + AW8624_BIT_CONT_CTRL_CLOSE_PLAYBACK | + AW8624_BIT_CONT_CTRL_F0_DETECT_DISABLE | + AW8624_BIT_CONT_CTRL_O2C_DISABLE; + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_CONT_CTRL, val, + AW_I2C_BYTE_ONE); + /* TD time */ + val[0] = (uint8_t)(aw_haptic->info.cont_td>>8); + val[1] = (uint8_t)(aw_haptic->info.cont_td>>0); + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_TD_H, val, + AW_I2C_BYTE_TWO); + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_BEMF_NUM, + AW8624_BIT_BEMF_NUM_BRK_MASK, + aw_haptic->info.cont_num_brk); + time_nzc = AW8624_BIT_TIME_NZC_DEF_VAL; + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_TIME_NZC, &time_nzc, + AW_I2C_BYTE_ONE); + /* f0 driver level */ + val[0] = aw_haptic->info.cont_drv1_lvl; + val[1] = aw_haptic->info.cont_drv2_lvl; + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_DRV_LVL, val, + AW_I2C_BYTE_TWO); + aw8624_play_go(aw_haptic, true); +} + +static ssize_t aw8624_get_reg(struct aw_haptic *aw_haptic, ssize_t len, + char *buf) +{ + len = haptic_nv_read_reg_array(aw_haptic, buf, len, AW8624_REG_ID, + AW8624_REG_RTP_DATA - 1); + if (!len) + return len; + len = haptic_nv_read_reg_array(aw_haptic, buf, len, + AW8624_REG_RTP_DATA + 1, + AW8624_REG_RAMDATA - 1); + if (!len) + return len; + len = haptic_nv_read_reg_array(aw_haptic, buf, len, + AW8624_REG_RAMDATA + 1, + AW8624_REG_NUM_F0_3); + return len; +} + +static void aw8624_set_pwm(struct aw_haptic *aw_haptic, uint8_t mode) +{ + switch (mode) { + case AW_PWM_48K: + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_PWMDBG, + AW8624_BIT_PWMDBG_PWM_MODE_MASK, + AW8624_BIT_PWMDBG_PWM_48K); + break; + case AW_PWM_24K: + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_PWMDBG, + AW8624_BIT_PWMDBG_PWM_MODE_MASK, + AW8624_BIT_PWMDBG_PWM_24K); + break; + case AW_PWM_12K: + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_PWMDBG, + AW8624_BIT_PWMDBG_PWM_MODE_MASK, + AW8624_BIT_PWMDBG_PWM_12K); + break; + default: + break; + } +} + +static void aw8624_protect_config(struct aw_haptic *aw_haptic, uint8_t prtime, + uint8_t prlvl) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_PWMPRC, + AW8624_BIT_PWMPRC_PRC_EN_MASK, + AW8624_BIT_PWMPRC_PRC_DISABLE); + if (prlvl != 0) { + /* Enable protection mode */ + aw_info("enable protection mode"); + reg_val = AW8624_BIT_PRLVL_PR_ENABLE | + (prlvl & (~AW8624_BIT_PRLVL_PRLVL_MASK)); + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_PRLVL, ®_val, + AW_I2C_BYTE_ONE); + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_PRTIME, &prtime, + AW_I2C_BYTE_ONE); + } else { + /* Disable */ + aw_info("disable protection mode"); + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_PRLVL, + AW8624_BIT_PRLVL_PR_EN_MASK, + AW8624_BIT_PRLVL_PR_DISABLE); + } +} + +static void aw8624_misc_para_init(struct aw_haptic *aw_haptic) +{ + int ret = 0; + uint8_t reg_val = 0; + uint8_t reg_flag = 0; + uint8_t reg_array[8] = {0}; + + /* Get chipid_flag */ + ret = haptic_nv_i2c_reads(aw_haptic, AW8624_REG_EF_RDATAH, ®_flag, + AW_I2C_BYTE_ONE); + if ((ret >= 0) && ((reg_flag & 0x1) == 1)) + aw_haptic->chipid_flag = 1; + else + aw_err("to read register AW8624_REG_EF_RDATAH: %d", ret); + /* Get seq and gain */ + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_DATDBG, ®_val, + AW_I2C_BYTE_ONE); + aw_haptic->gain = reg_val & 0xFF; + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_WAVSEQ1, reg_array, + AW_SEQUENCER_SIZE); + aw_haptic->index = reg_array[0] & 0x7F; + memcpy(aw_haptic->seq, reg_array, AW_SEQUENCER_SIZE); + /* r_spare */ + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_R_SPARE, + AW8624_BIT_R_SPARE_MASK, + AW8624_BIT_R_SPARE_ENABLE); + /* LRA trim source select register */ + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_ANACTRL, + AW8624_BIT_ANACTRL_LRA_SRC_MASK, + AW8624_BIT_ANACTRL_LRA_SRC_REG); + /* brake */ + reg_val = (uint8_t)aw_haptic->info.sw_brake[0]; + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_SW_BRAKE, ®_val, + AW_I2C_BYTE_ONE); + reg_val = 0x00; + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_THRS_BRA_END, ®_val, + AW_I2C_BYTE_ONE); + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_WAVECTRL, + AW8624_BIT_WAVECTRL_NUM_OV_DRIVER_MASK, + AW8624_BIT_WAVECTRL_NUM_OV_DRIVER); + /* zero cross */ + reg_array[0] = (uint8_t)(aw_haptic->info.cont_zc_thr >> 8); + reg_array[1] = (uint8_t)(aw_haptic->info.cont_zc_thr >> 0); + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_ZC_THRSH_H, reg_array, + AW_I2C_BYTE_TWO); + /* cont_tset */ + reg_val = (uint8_t)aw_haptic->info.cont_tset; + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_TSET, ®_val, + AW_I2C_BYTE_ONE); + /* bemf */ + reg_array[0] = (uint8_t)(aw_haptic->info.bemf_config[0]); + reg_array[1] = (uint8_t)(aw_haptic->info.bemf_config[1]); + reg_array[2] = (uint8_t)(aw_haptic->info.bemf_config[2]); + reg_array[3] = (uint8_t)(aw_haptic->info.bemf_config[3]); + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_BEMF_VTHH_H, reg_array, + AW_I2C_BYTE_FOUR); + aw8624_set_pwm(aw_haptic, AW_PWM_24K); + aw8624_protect_config(aw_haptic, AW8624_REG_PRTIME_DEFAULT_VALUE, + AW8624_BIT_PRLVL_PRLVL_DEFAULT_VALUE); +} + +static void aw8624_offset_cali(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int cont = 2000; + + aw_info("enter"); + + aw8624_raminit(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_DETCTRL, + AW8624_BIT_DETCTRL_DIAG_GO_MASK, + AW8624_BIT_DETCTRL_DIAG_GO_ENABLE); + while (cont) { + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_DETCTRL, ®_val, + AW_I2C_BYTE_ONE); + if ((reg_val & 0x01) == 0 || cont == 0) + break; + cont--; + } + if (cont == 0) + aw_err("calibration offset failed!"); + aw8624_raminit(aw_haptic, false); +} + +static void aw8624_vbat_mode_config(struct aw_haptic *aw_haptic, uint8_t flag) +{ + if (flag == AW_CONT_VBAT_HW_COMP_MODE) { + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_ADCTEST, + AW8624_BIT_DETCTRL_VBAT_MODE_MASK, + AW8624_BIT_DETCTRL_VBAT_HW_COMP); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_ADCTEST, + AW8624_BIT_DETCTRL_VBAT_MODE_MASK, + AW8624_BIT_DETCTRL_VBAT_SW_COMP); + } +} + +static void aw8624_calculate_cali_data(struct aw_haptic *aw_haptic) +{ + char f0_cali_lra = 0; + int f0_cali_step = 0; + uint8_t chipid_flag = aw_haptic->chipid_flag; + + f0_cali_step = 100000 * ((int)aw_haptic->f0 - + (int)aw_haptic->info.f0_pre) / + ((int)aw_haptic->f0 * AW8624_F0_CALI_ACCURACY); + if (f0_cali_step >= 0) { + if (f0_cali_step % 10 >= 5) { + f0_cali_step = f0_cali_step / 10 + 1 + + (chipid_flag == 1 ? 32 : 16); + } else { + f0_cali_step = f0_cali_step / 10 + + (chipid_flag == 1 ? 32 : 16); + } + } else { + if (f0_cali_step % 10 <= -5) { + f0_cali_step = (chipid_flag == 1 ? 32 : 16) + + (f0_cali_step / 10 - 1); + } else { + f0_cali_step = (chipid_flag == 1 ? 32 : 16) + + f0_cali_step / 10; + } + } + if (chipid_flag == 1) { + if (f0_cali_step > 31) + f0_cali_lra = (char)f0_cali_step - 32; + else + f0_cali_lra = (char)f0_cali_step + 32; + } else { + if (f0_cali_step < 16 || + (f0_cali_step > 31 && f0_cali_step < 48)) { + f0_cali_lra = (char)f0_cali_step + 16; + } else { + f0_cali_lra = (char)f0_cali_step - 16; + } + } + if ((chipid_flag != 1) && + (aw_haptic->f0 <= aw_haptic->info.f0_pre * 97 / 100)) + f0_cali_lra = 16; + aw_haptic->f0_cali_data = (int)f0_cali_lra; + aw_info("f0_cali_data=0x%02X", aw_haptic->f0_cali_data); +} + +static int aw8624_read_f0(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t f0_reg = 0; + uint64_t f0_tmp = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_F_LRA_F0_H, val, + AW_I2C_BYTE_TWO); + f0_reg = (val[0] << 8) | val[1]; + if (!f0_reg || !aw_haptic->info.f0_coeff) { + aw_haptic->f0 = 0; + aw_info("get f0 failed with the value becoming 0!"); + return -ERANGE; + } + f0_tmp = AW8624_F0_FORMULA(f0_reg, aw_haptic->info.f0_coeff); + aw_haptic->f0 = (uint32_t)f0_tmp; + aw_info("f0=%d", aw_haptic->f0); + return 0; +} + +static void aw8624_read_beme(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_WAIT_VOL_MP, val, + AW_I2C_BYTE_TWO); + aw_haptic->max_pos_beme = val[0]; + aw_haptic->max_neg_beme = val[1]; + aw_info("max_pos_beme=%d", aw_haptic->max_pos_beme); + aw_info("max_neg_beme=%d", aw_haptic->max_neg_beme); +} + +static int aw8624_get_f0(struct aw_haptic *aw_haptic) +{ + bool get_f0_flag = false; + uint8_t val[3] = {0}; + uint8_t f0_pre_num = 0; + uint8_t f0_wait_num = 0; + uint8_t f0_repeat_num = 0; + uint8_t f0_trace_num = 0; + uint32_t t_f0_ms = 0; + uint32_t t_f0_trace_ms = 0; + int ret = 0; + int cnt = 50; + + aw_info("enter"); + /* f0 calibrate work mode */ + aw8624_play_stop(aw_haptic); + aw8624_play_mode(aw_haptic, AW_CONT_MODE); + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_CONT_CTRL, + (AW8624_BIT_CONT_CTRL_EN_CLOSE_MASK & + AW8624_BIT_CONT_CTRL_F0_DETECT_MASK), + (AW8624_BIT_CONT_CTRL_OPEN_PLAYBACK | + AW8624_BIT_CONT_CTRL_F0_DETECT_ENABLE)); + /* LPF */ + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_DATCTRL, + (AW8624_BIT_DATCTRL_FC_MASK & + AW8624_BIT_DATCTRL_LPF_ENABLE_MASK), + (AW8624_BIT_DATCTRL_FC_1000HZ | + AW8624_BIT_DATCTRL_LPF_ENABLE)); + /* preset f0 */ + ret = aw8624_set_f0_preset(aw_haptic, aw_haptic->f0_pre); + if (ret < 0) + return -ERANGE; + /* f0 driver level */ + val[0] = (uint8_t)(aw_haptic->info.cont_drv1_lvl); + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_DRV_LVL, &val[0], + AW_I2C_BYTE_ONE); + /* f0 trace parameter */ + if (!aw_haptic->f0_pre) { + aw_info("fail to get t_f0_ms"); + return 0; + } + f0_pre_num = aw_haptic->info.f0_trace_parameter[0]; + f0_wait_num = aw_haptic->info.f0_trace_parameter[1]; + f0_repeat_num = aw_haptic->info.f0_trace_parameter[2]; + f0_trace_num = aw_haptic->info.f0_trace_parameter[3]; + val[0] = (f0_pre_num << 4)|(f0_wait_num << 0); + val[1] = f0_repeat_num << 0; + val[2] = f0_trace_num << 0; + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_NUM_F0_1, val, + AW_I2C_BYTE_THREE); + /* clear aw8624 interrupt */ + ret = haptic_nv_i2c_reads(aw_haptic, AW8624_REG_SYSINT, &val[0], + AW_I2C_BYTE_ONE); + /* play go and start f0 calibration */ + aw8624_play_go(aw_haptic, true); + /* f0 trace time */ + t_f0_ms = 1000*10 / aw_haptic->f0_pre; + t_f0_trace_ms = t_f0_ms * (f0_pre_num + f0_wait_num + + (f0_trace_num + f0_wait_num) * (f0_repeat_num - 1)); + aw_info("t_f0_trace_ms = %dms", t_f0_trace_ms); + usleep_range(t_f0_trace_ms * 1000, t_f0_trace_ms * 1000 + 500); + while (cnt) { + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_GLB_STATE, &val[0], + AW_I2C_BYTE_ONE); + if (val[0] == AW_BIT_STATE_STANDBY) { + get_f0_flag = true; + aw_info("entered standby! glb_state=0x%02X", val[0]); + break; + } + cnt--; + aw_dbg("waitting for standby,glb_state=0x%02X", val[0]); + usleep_range(AW_F0_DELAY_MIN, AW_F0_DELAY_MAX); + } + if (get_f0_flag) { + ret = aw8624_read_f0(aw_haptic); + if (ret < 0) + aw_err("read lra f0 is failed"); + aw8624_read_beme(aw_haptic); + } else { + ret = -ERANGE; + aw_err("enter standby mode failed, stop reading f0!"); + } + + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_CONT_CTRL, + (AW8624_BIT_CONT_CTRL_EN_CLOSE_MASK & + AW8624_BIT_CONT_CTRL_F0_DETECT_MASK), + (AW8624_BIT_CONT_CTRL_CLOSE_PLAYBACK | + AW8624_BIT_CONT_CTRL_F0_DETECT_DISABLE)); + + return ret; +} + +#ifdef AW8624_MUL_GET_F0 +static int aw8624_multiple_get_f0(struct aw_haptic *aw_haptic) +{ + int i = 0; + int ret = 0; + int f0_max = aw_haptic->info.f0_pre + AW8624_MUL_GET_F0_RANGE; + int f0_min = aw_haptic->info.f0_pre - AW8624_MUL_GET_F0_RANGE; + + aw_haptic->f0_pre = aw_haptic->info.f0_pre; + for (i = 0; i < AW8624_MUL_GET_F0_NUM; i++) { + aw_info("f0_pre=%d", aw_haptic->f0_pre); + ret = aw8624_get_f0(aw_haptic); + if (ret) + return ret; + if (aw_haptic->f0 >= f0_max || aw_haptic->f0 <= f0_min) + break; + aw_haptic->f0_pre = aw_haptic->f0; + usleep_range(4000, 4500); + } + return 0; +} +#endif + +static void aw8624_get_wav_seq(struct aw_haptic *aw_haptic, uint32_t len) +{ + if (len > AW_SEQUENCER_SIZE) + len = AW_SEQUENCER_SIZE; + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_WAVSEQ1, aw_haptic->seq, len); +} + +static void aw8624_set_wav_seq(struct aw_haptic *aw_haptic, + uint8_t wav, uint8_t seq) +{ + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_WAVSEQ1 + wav, &seq, + AW_I2C_BYTE_ONE); +} + +static void aw8624_set_wav_loop(struct aw_haptic *aw_haptic, + uint8_t wav, uint8_t loop) +{ + uint8_t tmp = 0; + + if (wav % 2) { + tmp = loop << 0; + haptic_nv_i2c_write_bits(aw_haptic, + AW8624_REG_WAVLOOP1 + (wav / 2), + AW8624_BIT_WAVLOOP_SEQNP1_MASK, tmp); + } else { + tmp = loop << 4; + haptic_nv_i2c_write_bits(aw_haptic, + AW8624_REG_WAVLOOP1 + (wav / 2), + AW8624_BIT_WAVLOOP_SEQN_MASK, tmp); + } + +} + +static void aw8624_set_repeat_seq(struct aw_haptic *aw_haptic, uint8_t seq) +{ + aw8624_set_wav_seq(aw_haptic, 0x00, seq); + aw8624_set_wav_seq(aw_haptic, 0x01, 0x00); + aw8624_set_wav_loop(aw_haptic, 0x00, AW8624_BIT_WAVLOOP_INIFINITELY); +} + +static void aw8624_get_gain(struct aw_haptic *aw_haptic, uint8_t *gain) +{ + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_DATDBG, gain, AW_I2C_BYTE_ONE); +} + +static void aw8624_get_wav_loop(struct aw_haptic *aw_haptic, uint8_t *val) +{ + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_WAVLOOP1, val, + AW_SEQUENCER_LOOP_SIZE); +} + +static void aw8624_get_ram_data(struct aw_haptic *aw_haptic, uint8_t *ram_data, + int size) +{ + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_RAMDATA, ram_data, size); +} + +static void aw8624_read_cont_f0(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t f0_reg = 0; + uint64_t f0_tmp = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_F_LRA_CONT_H, val, + AW_I2C_BYTE_TWO); + f0_reg = (val[0] << 8) | val[1]; + + if (!f0_reg) { + aw_haptic->cont_f0 = 0; + aw_info("failed to reading cont f0 with 0"); + return; + } + + f0_tmp = AW8624_F0_FORMULA(f0_reg, aw_haptic->info.f0_coeff); + aw_haptic->cont_f0 = (uint32_t)f0_tmp; + aw_info("cont_f0=%d", aw_haptic->cont_f0); +} + +static void aw8624_get_lra_resistance(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint8_t anactrl = 0; + uint8_t d2scfg = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_ANACTRL, &anactrl, + AW_I2C_BYTE_ONE); + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_D2SCFG, &d2scfg, + AW_I2C_BYTE_ONE); + aw8624_play_stop(aw_haptic); + aw8624_raminit(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_ANACTRL, + AW8624_BIT_ANACTRL_EN_IO_PD1_MASK, + AW8624_BIT_ANACTRL_EN_IO_PD1_HIGH); + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_D2SCFG, + AW8624_BIT_D2SCFG_CLK_ADC_MASK, + AW8624_BIT_D2SCFG_CLK_ASC_1P5MHZ); + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_DETCTRL, + (AW8624_BIT_DETCTRL_RL_OS_MASK & + AW8624_BIT_DETCTRL_DIAG_GO_MASK), + (AW8624_BIT_DETCTRL_RL_DETECT | + AW8624_BIT_DETCTRL_DIAG_GO_ENABLE)); + usleep_range(AW_RL_DELAY_MIN, AW_RL_DELAY_MAX); + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_RLDET, ®_val, + AW_I2C_BYTE_ONE); + aw_haptic->lra = AW8624_RL_FORMULA(reg_val); + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_D2SCFG, &d2scfg, + AW_I2C_BYTE_ONE); + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_ANACTRL, &anactrl, + AW_I2C_BYTE_ONE); + aw8624_raminit(aw_haptic, false); +} + +static uint8_t aw8624_get_prctmode(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_PRLVL, ®_val, + AW_I2C_BYTE_ONE); + reg_val >>= 7; + return reg_val; +} + +static uint8_t aw8624_judge_rtp_going(struct aw_haptic *aw_haptic) +{ + uint8_t glb_state = 0; + uint8_t rtp_state = 0; + + glb_state = aw8624_get_glb_state(aw_haptic); + if (glb_state == AW_BIT_STATE_RTP_GO) { + rtp_state = 1; /*is going on */ + aw_info("rtp_routine_on"); + } + return rtp_state; +} + +static uint64_t aw8624_get_theory_time(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t fre_val = 0; + uint64_t theory_time = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_PWMDBG, ®_val, + AW_I2C_BYTE_ONE); + fre_val = (reg_val & 0x006f) >> 5; + if (fre_val == AW8624_CLK_12K) + theory_time = (aw_haptic->rtp_len / 12000) * 1000000; /*12K*/ + else if (fre_val == AW8624_CLK_24K) + theory_time = (aw_haptic->rtp_len / 24000) * 1000000; /*24K*/ + else + theory_time = (aw_haptic->rtp_len / 48000) * 1000000; /*48K*/ + aw_info("microsecond:%llu theory_time = %llu", + aw_haptic->microsecond, theory_time); + return theory_time; +} + +static uint8_t aw8624_get_osc_status(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_DBGSTAT, &state, + AW_I2C_BYTE_ONE); + state &= AW8624_BIT_SYSINT_DONEI; + return state; +} + +static int aw8624_check_qualify(struct aw_haptic *aw_haptic) +{ + return 0; +} + +static void aw8624_irq_clear(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_info("SYSINT=0x%02X", reg_val); +} + +static void aw8624_set_base_addr(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t base_addr = aw_haptic->ram.base_addr; + + aw_info("enter"); + val[0] = (uint8_t)AW_SET_BASEADDR_H(base_addr); + val[1] = (uint8_t)AW_SET_BASEADDR_L(base_addr); + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_BASE_ADDRH, val, + AW_I2C_BYTE_TWO); +} + +static void aw8624_set_fifo_addr(struct aw_haptic *aw_haptic) +{ + uint8_t val[4] = {0}; + uint32_t base_addr = aw_haptic->ram.base_addr; + + aw_info("enter"); + val[0] = (uint8_t)AW8624_SET_AEADDR_H(base_addr); + val[1] = (uint8_t)AW8624_SET_AEADDR_L(base_addr); + val[2] = (uint8_t)AW8624_SET_AFADDR_H(base_addr); + val[3] = (uint8_t)AW8624_SET_AFADDR_L(base_addr); + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_FIFO_AEH, val, + AW_I2C_BYTE_FOUR); +} + +static void aw8624_get_fifo_addr(struct aw_haptic *aw_haptic) +{ + uint8_t val[4] = {0}; + + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_FIFO_AEH, val, + AW_I2C_BYTE_FOUR); + aw_info("almost_empty_threshold = %d, almost_full_threshold = %d", + (uint16_t)((val[0] << 8) | val[1]), + (uint16_t)((val[2] << 8) | val[3])); +} + +static void aw8624_set_ram_addr(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t base_addr = aw_haptic->ram.base_addr; + + aw_info("enter"); + val[0] = (uint8_t)AW_SET_RAMADDR_H(base_addr); + val[1] = (uint8_t)AW_SET_RAMADDR_L(base_addr); + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_RAMADDRH, val, + AW_I2C_BYTE_TWO); +} + +static void aw8624_set_ram_data(struct aw_haptic *aw_haptic, uint8_t *data, + int len) +{ + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_RAMDATA, data, len); +} + +static void aw8624_get_first_wave_addr(struct aw_haptic *aw_haptic, + uint32_t *first_wave_addr) +{ + uint8_t val[3] = {0}; + + aw_info("enter"); + aw8624_set_ram_addr(aw_haptic); + haptic_nv_i2c_reads(aw_haptic, AW8624_REG_RAMDATA, val, + AW_I2C_BYTE_THREE); + *first_wave_addr = (val[1] << 8 | val[2]); +} + +static void aw8624_haptic_select_pin(struct aw_haptic *aw_haptic, uint8_t pin) +{ + if (pin == AW_TRIG1) { + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_DBGCTRL, + AW8624_BIT_DBGCTRL_INTN_TRG_SEL_MASK, + AW8624_BIT_DBGCTRL_TRG_SEL_ENABLE); + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_TRG_CFG2, + AW8624_BIT_TRGCFG2_TRG1_ENABLE_MASK, + AW8624_BIT_TRGCFG2_TRG1_ENABLE); + aw_info("select TRIG1 pin"); + } else if (pin == AW_IRQ) { + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_DBGCTRL, + AW8624_BIT_DBGCTRL_INTN_TRG_SEL_MASK, + AW8624_BIT_DBGCTRL_INTN_SEL_ENABLE); + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_TRG_CFG2, + AW8624_BIT_TRGCFG2_TRG1_ENABLE_MASK, + AW8624_BIT_TRGCFG2_TRG1_DISABLE); + aw_info("select INIT pin"); + } else + aw_err("There is no such option"); +} + +static void aw8624_haptic_trig1_param_init(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + aw_haptic->trig[0].enable = aw_haptic->info.trig_cfg[0]; + aw_haptic->trig[0].trig_edge = aw_haptic->info.trig_cfg[1]; + aw_haptic->trig[0].trig_polar = aw_haptic->info.trig_cfg[2]; + aw_haptic->trig[0].pos_sequence = aw_haptic->info.trig_cfg[3]; + aw_haptic->trig[0].neg_sequence = aw_haptic->info.trig_cfg[4]; +} + +static void aw8624_haptic_trig1_param_config(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + if (!(aw_haptic->trig[0].enable) || aw_haptic->is_used_irq_pin) { + aw8624_haptic_trig1_param_init(aw_haptic); + aw8624_haptic_select_pin(aw_haptic, AW_IRQ); + return; + } + aw8624_haptic_select_pin(aw_haptic, AW_TRIG1); + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_TRG_CFG1, + AW8624_BIT_TRGCFG1_TRG1_EDGE_MASK, + aw_haptic->trig[0].trig_edge); + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_TRG_CFG1, + AW8624_BIT_TRGCFG1_TRG1_POLAR_MASK, + aw_haptic->trig[0].trig_polar << 1); + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_TRG1_SEQP, + &aw_haptic->trig[0].pos_sequence, AW_I2C_BYTE_ONE); + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_TRG1_SEQN, + &aw_haptic->trig[0].neg_sequence, AW_I2C_BYTE_ONE); +} + +static void aw8624_haptic_set_trig1(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + aw8624_haptic_trig1_param_init(aw_haptic); + aw8624_haptic_trig1_param_config(aw_haptic); +} + +static void aw8624_trig_init(struct aw_haptic *aw_haptic) +{ + if (aw_haptic->is_used_irq_pin) { + aw8624_haptic_select_pin(aw_haptic, AW_IRQ); + return; + } + aw_info("enter"); + aw8624_haptic_set_trig1(aw_haptic); +} + + +static ssize_t cont_td_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf+len, PAGE_SIZE-len, + "cont_delay_time = 0x%04X\n", + aw_haptic->info.cont_td); + return len; +} + +static ssize_t cont_td_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + int err, val; + uint8_t reg_array[2] = {0}; + + err = kstrtoint(buf, 16, &val); + if (err != 0) { + aw_err("format not match!"); + return count; + } + mutex_lock(&aw_haptic->lock); + aw_haptic->info.cont_td = val; + reg_array[0] = (uint8_t)(val >> 8); + reg_array[1] = (uint8_t)(val >> 0); + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_TD_H, reg_array, + AW_I2C_BYTE_TWO); + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t cont_drv_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf+len, PAGE_SIZE-len, "cont drv level = 0x%02X\n", + aw_haptic->info.cont_drv1_lvl); + len += snprintf(buf+len, PAGE_SIZE-len, + "cont drv level overdrive= 0x%02X\n", + aw_haptic->info.cont_drv2_lvl); + return len; +} + +static ssize_t cont_drv_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t reg_array[2] = {0}; + uint32_t databuf[2] = {0}; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + mutex_lock(&aw_haptic->lock); + if (sscanf(buf, "%X %X", &databuf[0], &databuf[1]) == 2) { + aw_haptic->info.cont_drv1_lvl = databuf[0]; + aw_haptic->info.cont_drv2_lvl = databuf[1]; + reg_array[0] = aw_haptic->info.cont_drv1_lvl; + reg_array[1] = aw_haptic->info.cont_drv2_lvl; + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_DRV_LVL, reg_array, + AW_I2C_BYTE_TWO); + } + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t cont_num_brk_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf+len, PAGE_SIZE-len, + "cont_brk_num = 0x%02x\n", + aw_haptic->info.cont_num_brk); + return len; +} + +static ssize_t cont_num_brk_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int err, val; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + err = kstrtoint(buf, 16, &val); + if (err != 0) { + aw_err("format not match!"); + return count; + } + mutex_lock(&aw_haptic->lock); + aw_haptic->info.cont_num_brk = val; + if (aw_haptic->info.cont_num_brk > 7) + aw_haptic->info.cont_num_brk = 7; + + haptic_nv_i2c_write_bits(aw_haptic, AW8624_REG_BEMF_NUM, + AW8624_BIT_BEMF_NUM_BRK_MASK, + aw_haptic->info.cont_num_brk); + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t cont_zc_thr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, + "cont_zero_cross_thr = 0x%04X\n", + aw_haptic->info.cont_zc_thr); + return len; +} + +static ssize_t cont_zc_thr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t reg_array[2] = {0}; + int err, val; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + err = kstrtoint(buf, 0, &val); + if (err != 0) { + aw_err("format not match!"); + return count; + } + mutex_lock(&aw_haptic->lock); + aw_haptic->info.cont_num_brk = val; + aw_info("val=%d", val); + if (val > 0) { + reg_array[0] = (uint8_t)(val >> 8); + reg_array[1] = (uint8_t)(val >> 0); + haptic_nv_i2c_writes(aw_haptic, AW8624_REG_ZC_THRSH_H, + reg_array, AW_I2C_BYTE_TWO); + } + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t trig_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "trig: trig_enable=%d, trig_edge=%d\n", + aw_haptic->trig[0].enable, + aw_haptic->trig[0].trig_edge); + len += snprintf(buf + len, PAGE_SIZE - len, + "trig_polar=%d, pos_sequence=%d, neg_sequence=%d\n", + aw_haptic->trig[0].trig_polar, + aw_haptic->trig[0].pos_sequence, + aw_haptic->trig[0].neg_sequence); + return len; + +} + +static ssize_t trig_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t databuf[5] = { 0 }; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + if (sscanf(buf, "%d %d %d %d %d", + &databuf[0], &databuf[1], + &databuf[2], &databuf[3], &databuf[4]) == 5) { + if (databuf[0] > 1) + databuf[0] = 1; + if (databuf[0] < 0) + databuf[0] = 0; + if (databuf[1] > 1) + databuf[0] = 1; + if (databuf[1] < 0) + databuf[0] = 0; + if (databuf[2] > 1) + databuf[0] = 1; + if (databuf[2] < 0) + databuf[0] = 0; + if (databuf[3] > aw_haptic->ram.ram_num || + databuf[4] > aw_haptic->ram.ram_num) { + aw_err("input seq value out of range!"); + return count; + } + aw_haptic->trig[0].enable = databuf[0]; + aw_haptic->trig[0].trig_edge = databuf[1]; + aw_haptic->trig[0].trig_polar = databuf[2]; + aw_haptic->trig[0].pos_sequence = databuf[3]; + aw_haptic->trig[0].neg_sequence = databuf[4]; + mutex_lock(&aw_haptic->lock); + aw8624_haptic_trig1_param_config(aw_haptic); + mutex_unlock(&aw_haptic->lock); + } else + aw_err("please input five parameters"); + return count; +} + +static DEVICE_ATTR(cont_td, 0664, cont_td_show, cont_td_store); +static DEVICE_ATTR(cont_drv, 0664, cont_drv_show, cont_drv_store); +static DEVICE_ATTR(cont_num_brk, 0664, cont_num_brk_show, cont_num_brk_store); +static DEVICE_ATTR(cont_zc_thr, 0664, cont_zc_thr_show, cont_zc_thr_store); +static DEVICE_ATTR(trig, 0664, trig_show, trig_store); +static struct attribute *aw8624_vibrator_attributes[] = { + &dev_attr_cont_td.attr, + &dev_attr_cont_drv.attr, + &dev_attr_cont_num_brk.attr, + &dev_attr_cont_zc_thr.attr, + &dev_attr_trig.attr, + NULL +}; + +static struct attribute_group aw8624_vibrator_attribute_group = { + .attrs = aw8624_vibrator_attributes +}; + +static void aw8624_creat_node(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + ret = sysfs_create_group(&aw_haptic->vib_dev.dev->kobj, + &aw8624_vibrator_attribute_group); + if (ret < 0) + aw_err("error create aw862xx sysfs attr files"); +} + +struct aw_haptic_func aw8624_func_list = { + .ram_init = aw8624_raminit, + .trig_init = aw8624_trig_init, + .play_mode = aw8624_play_mode, + .play_stop = aw8624_play_stop, + .irq_clear = aw8624_irq_clear, + .creat_node = aw8624_creat_node, + .cont_config = aw8624_cont_config, + .offset_cali = aw8624_offset_cali, + .haptic_start = aw8624_haptic_start, + .read_cont_f0 = aw8624_read_cont_f0, + .check_qualify = aw8624_check_qualify, + .judge_rtp_going = aw8624_judge_rtp_going, + .protect_config = aw8624_protect_config, + .misc_para_init = aw8624_misc_para_init, + .interrupt_setup = aw8624_interrupt_setup, + .rtp_get_fifo_afs = aw8624_rtp_get_fifo_afs, + .vbat_mode_config = aw8624_vbat_mode_config, + .calculate_cali_data = aw8624_calculate_cali_data, + .set_gain = aw8624_set_gain, + .get_gain = aw8624_get_gain, + .set_wav_seq = aw8624_set_wav_seq, + .get_wav_seq = aw8624_get_wav_seq, + .set_wav_loop = aw8624_set_wav_loop, + .get_wav_loop = aw8624_get_wav_loop, + .set_ram_data = aw8624_set_ram_data, + .get_ram_data = aw8624_get_ram_data, + .set_fifo_addr = aw8624_set_fifo_addr, + .get_fifo_addr = aw8624_get_fifo_addr, + .set_rtp_aei = aw8624_set_rtp_aei, + .set_rtp_data = aw8624_set_rtp_data, + .set_ram_addr = aw8624_set_ram_addr, + .set_trim_lra = aw8624_set_trim_lra, + .set_base_addr = aw8624_set_base_addr, + .set_repeat_seq = aw8624_set_repeat_seq, +#ifdef AW8624_MUL_GET_F0 + .get_f0 = aw8624_multiple_get_f0, +#else + .get_f0 = aw8624_get_f0, +#endif + .get_reg = aw8624_get_reg, + .get_vbat = aw8624_get_vbat, + .get_prctmode = aw8624_get_prctmode, + .get_irq_state = aw8624_get_irq_state, + .get_glb_state = aw8624_get_glb_state, + .get_osc_status = aw8624_get_osc_status, + .get_theory_time = aw8624_get_theory_time, + .get_lra_resistance = aw8624_get_lra_resistance, + .get_first_wave_addr = aw8624_get_first_wave_addr, +}; diff --git a/drivers/misc/awinic/aw862x_haptic_nv/aw862xx.c b/drivers/misc/awinic/aw862x_haptic_nv/aw862xx.c new file mode 100755 index 000000000000..a34c80dafe0b --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv/aw862xx.c @@ -0,0 +1,1612 @@ +/* + * aw862xx.c + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "haptic_nv.h" +#include "haptic_nv_reg.h" + +static void aw862xx_interrupt_setup(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + aw_info("enter"); + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_info("reg SYSINT=0x%02X", reg_val); + /* edge int mode */ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL7, + (AW862XX_BIT_SYSCTRL7_INT_MODE_MASK & + AW862XX_BIT_SYSCTRL7_INT_EDGE_MODE_MASK), + (AW862XX_BIT_SYSCTRL7_INT_MODE_EDGE | + AW862XX_BIT_SYSCTRL7_INT_EDGE_MODE_POS)); + /* int enable */ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSINTM, + (AW862XX_BIT_SYSINTM_UVLM_MASK & + AW862XX_BIT_SYSINTM_FF_AEM_MASK & + AW862XX_BIT_SYSINTM_FF_AFM_MASK & + AW862XX_BIT_SYSINTM_OCDM_MASK & + AW862XX_BIT_SYSINTM_OTM_MASK & + AW862XX_BIT_SYSINTM_DONEM_MASK), + (AW862XX_BIT_SYSINTM_UVLM_ON | + AW862XX_BIT_SYSINTM_FF_AEM_OFF | + AW862XX_BIT_SYSINTM_FF_AFM_OFF | + AW862XX_BIT_SYSINTM_OCDM_ON | + AW862XX_BIT_SYSINTM_OTM_ON | + AW862XX_BIT_SYSINTM_DONEM_OFF)); +} + +static void aw862xx_set_rtp_data(struct aw_haptic *aw_haptic, uint8_t *data, + uint32_t len) +{ + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_RTPDATA, data, len); +} + +static uint8_t aw862xx_get_glb_state(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_GLBRD5, &state, + AW_I2C_BYTE_ONE); + return state; +} + +static int aw862xx_get_irq_state(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int ret = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_dbg("reg SYSINT=0x%02X", reg_val); + if (reg_val & AW862XX_BIT_SYSINT_UVLI) + aw_err("chip uvlo int error"); + if (reg_val & AW862XX_BIT_SYSINT_OCDI) + aw_err("chip over current int error"); + if (reg_val & AW862XX_BIT_SYSINT_OTI) + aw_err("chip over temperature int error"); + if (reg_val & AW862XX_BIT_SYSINT_DONEI) + aw_info("chip playback done"); + if (reg_val & AW862XX_BIT_SYSINT_FF_AFI) + aw_info("rtp mode fifo almost full!"); + if (reg_val & AW862XX_BIT_SYSINT_FF_AEI) { + aw_info("rtp fifo almost empty"); + ret = AW_IRQ_ALMOST_EMPTY; + } + return ret; +} + +static uint8_t aw862xx_rtp_get_fifo_afs(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_SYSST, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= AW862XX_BIT_SYSST_FF_AFS; + reg_val = reg_val >> 3; + return reg_val; +} + +static void aw862xx_set_rtp_aei(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSINTM, + AW862XX_BIT_SYSINTM_FF_AEM_MASK, + AW862XX_BIT_SYSINTM_FF_AEM_ON); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSINTM, + AW862XX_BIT_SYSINTM_FF_AEM_MASK, + AW862XX_BIT_SYSINTM_FF_AEM_OFF); + } +} + +#ifdef AW862XX_COMMON +static void aw862xx_sram_size(struct aw_haptic *aw_haptic, uint8_t size_flag) +{ + switch (size_flag) { + case AW862XX_HAPTIC_SRAM_1K: + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_RTPCFG1, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_2K_MASK, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_2K_DIS); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_RTPCFG1, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_1K_MASK, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_1K_EN); + break; + case AW862XX_HAPTIC_SRAM_2K: + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_RTPCFG1, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_2K_MASK, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_2K_EN); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_RTPCFG1, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_1K_MASK, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_1K_DIS); + break; + case AW862XX_HAPTIC_SRAM_3K: + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_RTPCFG1, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_1K_MASK, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_1K_EN); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_RTPCFG1, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_2K_MASK, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_2K_EN); + break; + default: + aw_err("size_flag is error"); + break; + } +} + +static void aw862xx_auto_brk_config(struct aw_haptic *aw_haptic, uint8_t flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PLAYCFG3, + AW862XX_BIT_PLAYCFG3_BRK_EN_MASK, + AW862XX_BIT_PLAYCFG3_BRK_ENABLE); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PLAYCFG3, + AW862XX_BIT_PLAYCFG3_BRK_EN_MASK, + AW862XX_BIT_PLAYCFG3_BRK_DISABLE); + } +} +#endif + +#ifdef AW86214_DRIVER +static void aw86214_brk_en(struct aw_haptic *aw_haptic, bool is_enable) +{ + uint8_t reg_val = 0; + + aw_info("enter"); + if (is_enable == false) + reg_val = 0x00; + else + reg_val = (uint8_t)aw_haptic->info.cont_brk_time; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG10, ®_val, + AW_I2C_BYTE_ONE); +} + +static int aw86214_play_mode_config(struct aw_haptic *aw_haptic, + uint8_t play_mode) +{ + int ret = 0; + + aw_info("enter"); + switch (play_mode) { + case AW_RAM_MODE: + case AW_RAM_LOOP_MODE: + aw86214_brk_en(aw_haptic, false); + break; + case AW_CONT_MODE: + aw86214_brk_en(aw_haptic, true); + break; + case AW_RTP_MODE: + case AW_TRIG_MODE: + ret = -ERANGE; + break; + default: + ret = -ERANGE; + break; + } + return ret; +} + +static void aw86214_config(struct aw_haptic *aw_haptic) +{ + uint8_t val = 0; + + aw_info("enter"); + aw862xx_sram_size(aw_haptic, AW862XX_HAPTIC_SRAM_1K); + aw862xx_auto_brk_config(aw_haptic, true); + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG10, &val, + AW_I2C_BYTE_ONE); +} +#endif + +#ifdef AW8622X_DRIVER +static void aw8622x_config(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + aw_info("enter"); + aw862xx_sram_size(aw_haptic, AW862XX_HAPTIC_SRAM_3K); + aw862xx_auto_brk_config(aw_haptic, aw_haptic->info.is_enabled_auto_brk); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_TRGCFG8, + AW862XX_BIT_TRGCFG8_TRG_TRIG1_MODE_MASK, + AW862XX_BIT_TRGCFG8_TRIG1); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_ANACFG8, + AW862XX_BIT_ANACFG8_TRTF_CTRL_HDRV_MASK, + AW862XX_BIT_ANACFG8_TRTF_CTRL_HDRV); + if (aw_haptic->info.cont_brk_time) { + reg_val = (uint8_t)aw_haptic->info.cont_brk_time; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG10, + ®_val, AW_I2C_BYTE_ONE); + } else { + aw_err("dts_info.cont_brk_time=0"); + } + if (aw_haptic->info.cont_brk_gain) { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_CONTCFG5, + AW862XX_BIT_CONTCFG5_BRK_GAIN_MASK, + aw_haptic->info.cont_brk_gain); + } else { + aw_err("dts_info.cont_brk_gain=0"); + } + +} +#endif + +static void aw862xx_play_stop(struct aw_haptic *aw_haptic) +{ + bool force_flag = true; + uint8_t val = 0; + int cnt = 40; + + aw_info("enter"); + aw_haptic->play_mode = AW_STANDBY_MODE; + val = AW862XX_BIT_PLAYCFG4_STOP_ON; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_PLAYCFG4, &val, + AW_I2C_BYTE_ONE); + while (cnt) { + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_GLBRD5, &val, + AW_I2C_BYTE_ONE); + if ((val & AW_BIT_GLBRD_STATE_MASK) == AW_BIT_STATE_STANDBY) { + force_flag = false; + aw_info("entered standby! glb_state=0x%02X", val); + break; + } + cnt--; + aw_info("wait for standby, glb_state=0x%02X", val); + usleep_range(AW_STOP_DELAY_MIN, AW_STOP_DELAY_MAX); + } + if (force_flag) { + aw_err("force to enter standby mode!"); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL2, + AW862XX_BIT_SYSCTRL2_STANDBY_MASK, + AW862XX_BIT_SYSCTRL2_STANDBY_ON); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL2, + AW862XX_BIT_SYSCTRL2_STANDBY_MASK, + AW862XX_BIT_SYSCTRL2_STANDBY_OFF); + } +} + +static void aw862xx_set_pwm(struct aw_haptic *aw_haptic, uint8_t mode) +{ + switch (mode) { + case AW_PWM_48K: + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL2, + AW862XX_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW862XX_BIT_SYSCTRL2_RATE_48K); + break; + case AW_PWM_24K: + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL2, + AW862XX_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW862XX_BIT_SYSCTRL2_RATE_24K); + break; + case AW_PWM_12K: + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL2, + AW862XX_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW862XX_BIT_SYSCTRL2_RATE_12K); + break; + default: + break; + } +} + +static void aw862xx_play_mode(struct aw_haptic *aw_haptic, uint8_t play_mode) +{ +#ifdef AW86214_DRIVER + if (aw_haptic->name == AW86214) { + if (aw86214_play_mode_config(aw_haptic, play_mode)) + return; + } +#endif + + switch (play_mode) { + case AW_STANDBY_MODE: + aw_info("enter standby mode"); + aw_haptic->play_mode = AW_STANDBY_MODE; + aw862xx_play_stop(aw_haptic); + break; + case AW_RAM_MODE: + aw_info("enter ram mode"); + aw_haptic->play_mode = AW_RAM_MODE; + aw862xx_set_pwm(aw_haptic, AW_PWM_12K); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PLAYCFG3, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + case AW_RAM_LOOP_MODE: + aw_info("enter ram loop mode"); + aw_haptic->play_mode = AW_RAM_LOOP_MODE; + aw862xx_set_pwm(aw_haptic, AW_PWM_12K); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PLAYCFG3, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + case AW_CONT_MODE: + aw_info("enter cont mode"); + aw_haptic->play_mode = AW_CONT_MODE; + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PLAYCFG3, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_CONT); + break; + case AW_RTP_MODE: + aw_info("enter rtp mode"); + aw_haptic->play_mode = AW_RTP_MODE; + aw862xx_set_pwm(aw_haptic, AW_PWM_24K); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PLAYCFG3, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_RTP); + break; + case AW_TRIG_MODE: + aw_info("enter trig mode"); + aw_haptic->play_mode = AW_TRIG_MODE; + aw862xx_set_pwm(aw_haptic, AW_PWM_12K); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PLAYCFG3, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + default: + aw_err("play mode %d error", play_mode); + break; + } +} + +static void aw862xx_irq_clear(struct aw_haptic *aw_haptic) +{ + uint8_t val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_SYSINT, &val, + AW_I2C_BYTE_ONE); + aw_info("SYSINT=0x%02X", val); +} + +static void aw862xx_play_go(struct aw_haptic *aw_haptic, bool flag) +{ + uint8_t val = 0; + + aw_info("enter"); + if (flag == true) { + val = AW862XX_BIT_PLAYCFG4_GO_ON; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_PLAYCFG4, &val, + AW_I2C_BYTE_ONE); + usleep_range(AW_PLAY_DELAY_MIN, AW_PLAY_DELAY_MAX); + } else { + val = AW862XX_BIT_PLAYCFG4_STOP_ON; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_PLAYCFG4, &val, + AW_I2C_BYTE_ONE); + } + val = aw862xx_get_glb_state(aw_haptic); + aw_info("reg:0x%02X=0x%02X", AW862XX_REG_GLBRD5, val); +} + +static void aw862xx_haptic_start(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + aw862xx_play_go(aw_haptic, true); +} + +static void aw862xx_raminit(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL1, + AW862XX_BIT_SYSCTRL1_RAMINIT_MASK, + AW862XX_BIT_SYSCTRL1_RAMINIT_ON); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL1, + AW862XX_BIT_SYSCTRL1_RAMINIT_MASK, + AW862XX_BIT_SYSCTRL1_RAMINIT_OFF); + } +} + +static void aw862xx_get_vbat(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t vbat_code = 0; + + aw_info("enter"); + aw862xx_play_stop(aw_haptic); + aw862xx_raminit(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_DETCFG2, + AW862XX_BIT_DETCFG2_VBAT_GO_MASK, + AW862XX_BIT_DETCFG2_VABT_GO_ON); + usleep_range(AW_VBAT_DELAY_MIN, AW_VBAT_DELAY_MAX); + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_DET_VBAT, ®_val, + AW_I2C_BYTE_ONE); + vbat_code = (vbat_code | reg_val) << 2; + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_DET_LO, ®_val, + AW_I2C_BYTE_ONE); + vbat_code = vbat_code | ((reg_val & AW862XX_BIT_DET_LO_VBAT) >> 4); + aw_haptic->vbat = AW862XX_VBAT_FORMULA(vbat_code); + if (aw_haptic->vbat > AW_VBAT_MAX) { + aw_haptic->vbat = AW_VBAT_MAX; + aw_info("vbat max limit = %dmV", aw_haptic->vbat); + } + if (aw_haptic->vbat < AW_VBAT_MIN) { + aw_haptic->vbat = AW_VBAT_MIN; + aw_info("vbat min limit = %dmV", aw_haptic->vbat); + } + aw_info("vbat=%dmV, vbat_code=0x%02X", aw_haptic->vbat, vbat_code); + aw862xx_raminit(aw_haptic, false); +} + +static void aw862xx_set_gain(struct aw_haptic *aw_haptic, uint8_t gain) +{ + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_PLAYCFG2, &gain, + AW_I2C_BYTE_ONE); +} + +static void aw862xx_set_trim_lra(struct aw_haptic *aw_haptic, uint8_t val) +{ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_TRIMCFG3, + AW862XX_BIT_TRIMCFG3_TRIM_LRA_MASK, val); +} + +static void aw862xx_cont_config(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + aw_info("enter"); + /* work mode */ + aw862xx_play_mode(aw_haptic, AW_CONT_MODE); + /* cont config */ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_CONTCFG6, + (AW862XX_BIT_CONTCFG6_TRACK_EN_MASK & + AW862XX_BIT_CONTCFG6_DRV1_LVL_MASK), + ((aw_haptic->info.is_enabled_track_en << 7) | + (uint8_t)aw_haptic->info.cont_drv1_lvl)); + reg_val = (uint8_t)aw_haptic->info.cont_drv2_lvl; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG7, ®_val, + AW_I2C_BYTE_ONE); + /* DRV2_TIME */ + reg_val = 0xFF; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG9, ®_val, + AW_I2C_BYTE_ONE); + /* cont play go */ + aw862xx_play_go(aw_haptic, true); +} + +static ssize_t aw862xx_get_reg(struct aw_haptic *aw_haptic, ssize_t len, + char *buf) +{ + len = haptic_nv_read_reg_array(aw_haptic, buf, len, AW862XX_REG_ID, + AW862XX_REG_RTPDATA - 1); + if (!len) + return len; + len = haptic_nv_read_reg_array(aw_haptic, buf, len, + AW862XX_REG_RTPDATA + 1, + AW862XX_REG_RAMDATA - 1); + if (!len) + return len; + len = haptic_nv_read_reg_array(aw_haptic, buf, len, + AW862XX_REG_RAMDATA + 1, + AW862XX_REG_ANACFG8); + return len; +} + +static void aw862xx_protect_config(struct aw_haptic *aw_haptic, uint8_t prtime, + uint8_t prlvl) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PWMCFG1, + AW862XX_BIT_PWMCFG1_PRC_EN_MASK, + AW862XX_BIT_PWMCFG1_PRC_DISABLE); + if (prlvl != 0) { + /* Enable protection mode */ + aw_info("enable protection mode"); + reg_val = AW862XX_BIT_PWMCFG3_PR_ENABLE | + (prlvl & (~AW862XX_BIT_PWMCFG3_PRLVL_MASK)); + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_PWMCFG3, ®_val, + AW_I2C_BYTE_ONE); + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_PWMCFG4, &prtime, + AW_I2C_BYTE_ONE); + } else { + /* Disable */ + aw_info("disable protection mode"); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PWMCFG3, + AW862XX_BIT_PWMCFG3_PR_EN_MASK, + AW862XX_BIT_PWMCFG3_PR_DISABLE); + } +} + +static void aw862xx_misc_para_init(struct aw_haptic *aw_haptic) +{ + uint8_t val = 0; + uint8_t array[8] = {0}; + + aw_info("enter"); + /* Get seq and gain */ + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_WAVCFG1, &val, + AW_I2C_BYTE_ONE); + aw_haptic->index = val & AW862XX_BIT_WAVCFG_SEQ; + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_PLAYCFG2, &val, + AW_I2C_BYTE_ONE); + aw_haptic->gain = val; + aw_info("gain=0x%02X, index=0x%02X", aw_haptic->gain, aw_haptic->index); + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_WAVCFG1, array, + AW_SEQUENCER_SIZE); + memcpy(aw_haptic->seq, array, AW_SEQUENCER_SIZE); + /* GAIN_BYPASS config */ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL7, + AW862XX_BIT_SYSCTRL7_GAIN_BYPASS_MASK, + AW862XX_BIT_SYSCTRL7_GAIN_CHANGEABLE); + /* SIN_H */ + array[0] = (uint8_t)aw_haptic->info.sine_array[0]; + /* SIN_L */ + array[1] = (uint8_t)aw_haptic->info.sine_array[1]; + /* COS_H */ + array[2] = (uint8_t)aw_haptic->info.sine_array[2]; + /* COS_L */ + array[3] = (uint8_t)aw_haptic->info.sine_array[3]; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_SYSCTRL3, array, + AW_I2C_BYTE_FOUR); + if (!aw_haptic->info.d2s_gain) { + aw_err("dts_info.d2s_gain = 0!"); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL7, + AW862XX_BIT_SYSCTRL7_D2S_GAIN_MASK, + aw_haptic->info.d2s_gain); + } + if (aw_haptic->info.cont_tset && aw_haptic->info.cont_bemf_set) { + array[0] = (uint8_t)aw_haptic->info.cont_tset; + array[1] = (uint8_t)aw_haptic->info.cont_bemf_set; + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_CONTCFG13, + (AW862XX_BIT_CONTCFG13_TSET_MASK & + AW862XX_BIT_CONTCFG13_BEME_SET_MASK), + ((array[0] << 4) | array[1])); + } else { + aw_err("dts_info.cont_tset = 0x%02X", + aw_haptic->info.cont_tset); + aw_err("dts_info.cont_bemf_set = 0x%02X", + aw_haptic->info.cont_bemf_set); + } + aw_haptic->info.cont_drv2_lvl = AW862XX_DRV2_LVL_FORMULA( + aw_haptic->info.f0_pre, aw_haptic->info.lra_vrms); + aw_info("lra_vrms=%d, cont_drv2_lvl=0x%02X", aw_haptic->info.lra_vrms, + aw_haptic->info.cont_drv2_lvl); + if (aw_haptic->info.cont_drv2_lvl > AW862XX_DRV2_LVL_MAX) { + aw_err("cont_drv2_lvl[0x%02X] is error, restore max vale[0x%02X]", + aw_haptic->info.cont_drv2_lvl, AW862XX_DRV2_LVL_MAX); + aw_haptic->info.cont_drv2_lvl = AW862XX_DRV2_LVL_MAX; + } + + switch (aw_haptic->name) { +#ifdef AW86214_DRIVER + case AW86214: + aw86214_config(aw_haptic); + break; +#endif + +#ifdef AW8622X_DRIVER + case AW86223: + case AW86224: + case AW86225: + aw8622x_config(aw_haptic); + break; +#endif + default: + break; + } + aw862xx_set_pwm(aw_haptic, AW_PWM_12K); + aw862xx_protect_config(aw_haptic, AW862XX_PWMCFG4_PRTIME_DEFAULT_VALUE, + AW862XX_BIT_PWMCFG3_PRLVL_DEFAULT_VALUE); +} + +static void aw862xx_offset_cali(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int cont = 2000; + + aw_info("enter"); + aw862xx_raminit(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_DETCFG2, + AW862XX_BIT_DETCFG2_DIAG_GO_MASK, + AW862XX_BIT_DETCFG2_DIAG_GO_ON); + while (cont) { + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_DETCFG2, ®_val, + AW_I2C_BYTE_ONE); + if ((reg_val & 0x01) == 0 || cont == 0) + break; + cont--; + } + if (cont == 0) + aw_err("calibration offset failed!"); + aw862xx_raminit(aw_haptic, false); +} + +static void aw862xx_vbat_mode_config(struct aw_haptic *aw_haptic, uint8_t flag) +{ + uint8_t val = 0; + + aw_info("enter"); + if (flag == AW_CONT_VBAT_HW_COMP_MODE) { + val = AW862XX_BIT_GLBCFG2_START_DLY_250US; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_GLBCFG2, &val, + AW_I2C_BYTE_ONE); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL1, + AW862XX_BIT_SYSCTRL1_VBAT_MODE_MASK, + AW862XX_BIT_SYSCTRL1_VBAT_MODE_HW); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL1, + AW862XX_BIT_SYSCTRL1_VBAT_MODE_MASK, + AW862XX_BIT_SYSCTRL1_VBAT_MODE_SW); + } +} + +static void aw862xx_calculate_cali_data(struct aw_haptic *aw_haptic) +{ + char f0_cali_lra = 0; + int f0_cali_step = 0; + + f0_cali_step = 100000 * ((int)aw_haptic->f0 - + (int)aw_haptic->info.f0_pre) / + ((int)aw_haptic->f0 * AW862XX_F0_CALI_ACCURACY); + aw_info("f0_cali_step=%d", f0_cali_step); + if (f0_cali_step >= 0) { /*f0_cali_step >= 0 */ + if (f0_cali_step % 10 >= 5) + f0_cali_step = 32 + (f0_cali_step / 10 + 1); + else + f0_cali_step = 32 + f0_cali_step / 10; + } else { /* f0_cali_step < 0 */ + if (f0_cali_step % 10 <= -5) + f0_cali_step = 32 + (f0_cali_step / 10 - 1); + else + f0_cali_step = 32 + f0_cali_step / 10; + } + if (f0_cali_step > 31) + f0_cali_lra = (char)f0_cali_step - 32; + else + f0_cali_lra = (char)f0_cali_step + 32; + /* update cali step */ + aw_haptic->f0_cali_data = (int)f0_cali_lra; + aw_info("f0_cali_data=0x%02X", aw_haptic->f0_cali_data); +} + +static void aw862xx_read_cont_f0(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t f0_reg = 0; + uint64_t f0_tmp = 0; + + aw_info("enter"); + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_CONTRD16, val, + AW_I2C_BYTE_TWO); + f0_reg = (f0_reg | val[0]) << 8; + f0_reg |= (val[1] << 0); + if (!f0_reg) { + aw_err("didn't get cont f0 because f0_reg value is 0!"); + aw_haptic->cont_f0 = 0; + return; + } + f0_tmp = AW862XX_F0_FORMULA(f0_reg); + aw_haptic->cont_f0 = (uint32_t)f0_tmp; + aw_info("cont_f0=%d", aw_haptic->cont_f0); +} + +static int aw862xx_read_lra_f0(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t f0_reg = 0; + uint64_t f0_tmp = 0; + + aw_info("enter"); + /* F_LRA_F0 */ + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_CONTRD14, val, + AW_I2C_BYTE_TWO); + f0_reg = (f0_reg | val[0]) << 8; + f0_reg |= (val[1] << 0); + if (!f0_reg) { + aw_haptic->f0 = 0; + aw_err("didn't get lra f0 because f0_reg value is 0!"); + return -ERANGE; + } + f0_tmp = AW862XX_F0_FORMULA(f0_reg); + aw_haptic->f0 = (uint32_t)f0_tmp; + aw_info("lra_f0=%d", aw_haptic->f0); + return 0; +} + +static int aw862xx_get_f0(struct aw_haptic *aw_haptic) +{ + bool get_f0_flag = false; + uint8_t brk_en_temp = 0; + uint8_t val[3] = {0}; + int cnt = 200; + int ret = 0; + + aw_info("enter"); + aw_haptic->f0 = aw_haptic->info.f0_pre; + /* enter standby mode */ + aw862xx_play_stop(aw_haptic); + /* f0 calibrate work mode */ + aw862xx_play_mode(aw_haptic, AW_CONT_MODE); + /* enable f0 detect */ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_CONTCFG1, + AW862XX_BIT_CONTCFG1_EN_F0_DET_MASK, + AW862XX_BIT_CONTCFG1_F0_DET_ENABLE); + /* cont config */ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_CONTCFG6, + AW862XX_BIT_CONTCFG6_TRACK_EN_MASK, + (aw_haptic->info.is_enabled_track_en << 7)); + /* enable auto brake */ + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_PLAYCFG3, &val[0], + AW_I2C_BYTE_ONE); + brk_en_temp = AW862XX_BIT_PLAYCFG3_BRK & val[0]; + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PLAYCFG3, + AW862XX_BIT_PLAYCFG3_BRK_EN_MASK, + AW862XX_BIT_PLAYCFG3_BRK_ENABLE); + /* f0 driver level */ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_CONTCFG6, + AW862XX_BIT_CONTCFG6_DRV1_LVL_MASK, + aw_haptic->info.cont_drv1_lvl); + val[0] = (uint8_t)aw_haptic->info.cont_drv2_lvl; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG7, val, + AW_I2C_BYTE_ONE); + val[0] = (uint8_t)aw_haptic->info.cont_drv1_time; + val[1] = (uint8_t)aw_haptic->info.cont_drv2_time; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG8, val, + AW_I2C_BYTE_TWO); + /* TRACK_MARGIN */ + if (!aw_haptic->info.cont_track_margin) { + aw_err("dts_info.cont_track_margin = 0!"); + } else { + val[0] = (uint8_t)aw_haptic->info.cont_track_margin; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG11, &val[0], + AW_I2C_BYTE_ONE); + } + /* DRV_WIDTH */ + if (aw_haptic->info.f0_pre) + val[0] = AW862XX_DRV_WIDTH_FORMULA(aw_haptic->info.f0_pre, + aw_haptic->info.cont_track_margin, + aw_haptic->info.cont_brk_gain); + else + val[0] = AW862XX_BIT_DRV_WIDTH_DEF_VAL; + aw_info("cont_drv_width=0x%02X", val[0]); + if (val[0] < AW862XX_DRV_WIDTH_MIN || val[0] > AW862XX_DRV_WIDTH_MAX) { + aw_err("cont_drv_width[0x%02X] is error, restore default vale[0x%02X]", + val[0], AW862XX_BIT_DRV_WIDTH_DEF_VAL); + val[0] = AW862XX_BIT_DRV_WIDTH_DEF_VAL; + } + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG3, &val[0], + AW_I2C_BYTE_ONE); + /* cont play go */ + aw862xx_play_go(aw_haptic, true); + usleep_range(20000, 20500); + while (cnt) { + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_GLBRD5, &val[0], + AW_I2C_BYTE_ONE); + if ((val[0] & AW_BIT_GLBRD_STATE_MASK) == + AW_BIT_STATE_STANDBY) { + get_f0_flag = true; + aw_info("entered standby! glb_state=0x%02X", val[0]); + break; + } + cnt--; + aw_dbg("waitting for standby,glb_state=0x%02X", val[0]); + usleep_range(AW_F0_DELAY_MIN, AW_F0_DELAY_MAX); + } + if (get_f0_flag) { + ret = aw862xx_read_lra_f0(aw_haptic); + if (ret < 0) + aw_err("read lra f0 is failed"); + aw862xx_read_cont_f0(aw_haptic); + } else { + ret = -ERANGE; + aw_err("enter standby mode failed, stop reading f0!"); + } + /* disable f0 detect */ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_CONTCFG1, + AW862XX_BIT_CONTCFG1_EN_F0_DET_MASK, + AW862XX_BIT_CONTCFG1_F0_DET_DISABLE); + /* recover auto break config */ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PLAYCFG3, + AW862XX_BIT_PLAYCFG3_BRK_EN_MASK, brk_en_temp); + return ret; +} + +static void aw862xx_set_base_addr(struct aw_haptic *aw_haptic) +{ + uint32_t base_addr = 0; + uint8_t val = 0; + + aw_info("enter"); + base_addr = aw_haptic->ram.base_addr; + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_RTPCFG1, + AW862XX_BIT_RTPCFG1_ADDRH_MASK, + (uint8_t)AW_SET_BASEADDR_H(base_addr)); + val = (uint8_t)AW_SET_BASEADDR_L(base_addr); + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_RTPCFG2, &val, + AW_I2C_BYTE_ONE); +} + +static void aw862xx_set_fifo_addr(struct aw_haptic *aw_haptic) +{ + uint8_t ae_addr_h = 0; + uint8_t af_addr_h = 0; + uint8_t val[3] = {0}; + + aw_info("enter"); + ae_addr_h = (uint8_t)AW862XX_SET_AEADDR_H(aw_haptic->ram.base_addr); + af_addr_h = (uint8_t)AW862XX_SET_AFADDR_H(aw_haptic->ram.base_addr); + val[0] = ae_addr_h | af_addr_h; + val[1] = (uint8_t)AW862XX_SET_AEADDR_L(aw_haptic->ram.base_addr); + val[2] = (uint8_t)AW862XX_SET_AFADDR_L(aw_haptic->ram.base_addr); + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_RTPCFG3, val, + AW_I2C_BYTE_THREE); +} + +static void aw862xx_get_fifo_addr(struct aw_haptic *aw_haptic) +{ + uint8_t ae_addr_h = 0; + uint8_t af_addr_h = 0; + uint8_t ae_addr_l = 0; + uint8_t af_addr_l = 0; + uint8_t val[3] = {0}; + + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_RTPCFG3, val, + AW_I2C_BYTE_THREE); + ae_addr_h = ((val[0]) & AW862XX_BIT_RTPCFG3_FIFO_AEH) >> 4; + ae_addr_l = val[1]; + af_addr_h = ((val[0]) & AW862XX_BIT_RTPCFG3_FIFO_AFH); + af_addr_l = val[2]; + aw_info("almost_empty_threshold = %d,almost_full_threshold = %d", + (uint16_t)((ae_addr_h << 8) | ae_addr_l), + (uint16_t)((af_addr_h << 8) | af_addr_l)); +} + +static void aw862xx_set_ram_addr(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t base_addr = aw_haptic->ram.base_addr; + + aw_info("enter"); + val[0] = (uint8_t)AW_SET_RAMADDR_H(base_addr); + val[1] = (uint8_t)AW_SET_RAMADDR_L(base_addr); + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_RAMADDRH, val, + AW_I2C_BYTE_TWO); +} + +static void aw862xx_set_ram_data(struct aw_haptic *aw_haptic, uint8_t *data, + int len) +{ + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_RAMDATA, data, len); +} + +static void aw862xx_get_first_wave_addr(struct aw_haptic *aw_haptic, + uint32_t *first_wave_addr) +{ + uint8_t val[3] = {0}; + + aw_info("enter"); + aw862xx_set_ram_addr(aw_haptic); + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_RAMDATA, val, + AW_I2C_BYTE_THREE); + *first_wave_addr = (val[1] << 8 | val[2]); +} + +#ifdef AW8622X_DRIVER +static void aw862xx_haptic_select_pin(struct aw_haptic *aw_haptic, uint8_t pin) +{ + if (pin == AW_TRIG1) { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL2, + AW862XX_BIT_SYSCTRL2_INTN_PIN_MASK, + AW862XX_BIT_SYSCTRL2_TRIG1); + aw_info("select TRIG1 pin"); + } else if (pin == AW_IRQ) { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL2, + AW862XX_BIT_SYSCTRL2_INTN_PIN_MASK, + AW862XX_BIT_SYSCTRL2_INTN); + aw_info("select INIT pin"); + } else + aw_err("There is no such option"); +} + +static void aw862xx_haptic_trig_param_init(struct aw_haptic *aw_haptic, + uint8_t pin) +{ + struct aw_haptic_dts_info info = aw_haptic->info; + + switch (pin) { + case AW_TRIG1: + aw_haptic->trig[0].trig_level = info.trig_cfg[0]; + aw_haptic->trig[0].trig_polar = info.trig_cfg[1]; + aw_haptic->trig[0].pos_enable = info.trig_cfg[2]; + aw_haptic->trig[0].pos_sequence = info.trig_cfg[3]; + aw_haptic->trig[0].neg_enable = info.trig_cfg[4]; + aw_haptic->trig[0].neg_sequence = info.trig_cfg[5]; + aw_haptic->trig[0].trig_brk = info.trig_cfg[6]; + break; + case AW_TRIG2: + aw_haptic->trig[1].trig_level = info.trig_cfg[7 + 0]; + aw_haptic->trig[1].trig_polar = info.trig_cfg[7 + 1]; + aw_haptic->trig[1].pos_enable = info.trig_cfg[7 + 2]; + aw_haptic->trig[1].pos_sequence = info.trig_cfg[7 + 3]; + aw_haptic->trig[1].neg_enable = info.trig_cfg[7 + 4]; + aw_haptic->trig[1].neg_sequence = info.trig_cfg[7 + 5]; + aw_haptic->trig[1].trig_brk = info.trig_cfg[7 + 6]; + break; + case AW_TRIG3: + aw_haptic->trig[2].trig_level = info.trig_cfg[14 + 0]; + aw_haptic->trig[2].trig_polar = info.trig_cfg[14 + 1]; + aw_haptic->trig[2].pos_enable = info.trig_cfg[14 + 2]; + aw_haptic->trig[2].pos_sequence = info.trig_cfg[14 + 3]; + aw_haptic->trig[2].neg_enable = info.trig_cfg[14 + 4]; + aw_haptic->trig[2].neg_sequence = info.trig_cfg[14 + 5]; + aw_haptic->trig[2].trig_brk = info.trig_cfg[14 + 6]; + break; + default: + break; + } +} + +static int aw862xx_haptic_trig_param_config(struct aw_haptic *aw_haptic, + uint8_t pin) +{ + uint8_t trig_polar_lev_brk = 0x00; + uint8_t trig_pos_seq = 0x00; + uint8_t trig_neg_seq = 0x00; + + if ((aw_haptic->name == AW86224 || aw_haptic->name == AW86225) && + aw_haptic->is_used_irq_pin) { + aw862xx_haptic_trig_param_init(aw_haptic, AW_TRIG1); + aw862xx_haptic_select_pin(aw_haptic, AW_IRQ); + return -ERANGE; + } + switch (pin) { + case AW_TRIG1: + if (aw_haptic->name == AW86224 || aw_haptic->name == AW86225) + aw862xx_haptic_select_pin(aw_haptic, AW_TRIG1); + trig_polar_lev_brk = aw_haptic->trig[0].trig_polar << 2 | + aw_haptic->trig[0].trig_level << 1 | + aw_haptic->trig[0].trig_brk; + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_TRGCFG7, + AW862XX_BIT_TRGCFG7_TRG1_POR_LEV_BRK_MASK, + trig_polar_lev_brk << 5); + trig_pos_seq = aw_haptic->trig[0].pos_enable << 7 | + aw_haptic->trig[0].pos_sequence; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_TRGCFG1, + &trig_pos_seq, AW_I2C_BYTE_ONE); + trig_neg_seq = aw_haptic->trig[0].neg_enable << 7 | + aw_haptic->trig[0].neg_sequence; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_TRGCFG4, + &trig_neg_seq, AW_I2C_BYTE_ONE); + aw_info("trig1 config ok!"); + break; + case AW_TRIG2: + trig_polar_lev_brk = aw_haptic->trig[1].trig_polar << 2 | + aw_haptic->trig[1].trig_level << 1 | + aw_haptic->trig[1].trig_brk; + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_TRGCFG7, + AW862XX_BIT_TRGCFG7_TRG2_POR_LEV_BRK_MASK, + trig_polar_lev_brk << 1); + trig_pos_seq = aw_haptic->trig[1].pos_enable << 7 | + aw_haptic->trig[1].pos_sequence; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_TRGCFG2, + &trig_pos_seq, AW_I2C_BYTE_ONE); + trig_neg_seq = aw_haptic->trig[1].neg_enable << 7 | + aw_haptic->trig[1].neg_sequence; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_TRGCFG5, + &trig_neg_seq, AW_I2C_BYTE_ONE); + aw_info("trig2 config ok!"); + break; + case AW_TRIG3: + trig_polar_lev_brk = aw_haptic->trig[2].trig_polar << 2 | + aw_haptic->trig[2].trig_level << 1 | + aw_haptic->trig[2].trig_brk; + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_TRGCFG8, + AW862XX_BIT_TRGCFG8_TRG3_POR_LEV_BRK_MASK, + trig_polar_lev_brk << 5); + trig_pos_seq = aw_haptic->trig[2].pos_enable << 7 | + aw_haptic->trig[2].pos_sequence; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_TRGCFG3, + &trig_pos_seq, AW_I2C_BYTE_ONE); + trig_neg_seq = aw_haptic->trig[2].neg_enable << 7 | + aw_haptic->trig[2].neg_sequence; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_TRGCFG6, + &trig_neg_seq, AW_I2C_BYTE_ONE); + aw_info("trig3 config ok!"); + break; + default: + break; + } + return 0; +} + +static void aw862xx_set_trig(struct aw_haptic *aw_haptic, uint8_t pin) +{ + aw_info("enter"); + aw862xx_haptic_trig_param_init(aw_haptic, pin); + aw862xx_haptic_trig_param_config(aw_haptic, pin); +} +#endif + +static void aw862xx_trig_init(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); +#ifdef AW8622X_DRIVER + switch (aw_haptic->name) { + case AW86223: + aw862xx_set_trig(aw_haptic, AW_TRIG1); + aw862xx_set_trig(aw_haptic, AW_TRIG2); + aw862xx_set_trig(aw_haptic, AW_TRIG3); + break; + case AW86224: + case AW86225: + aw862xx_set_trig(aw_haptic, AW_TRIG1); + break; + default: + break; + } +#endif +} + +static void aw862xx_get_wav_seq(struct aw_haptic *aw_haptic, uint32_t len) +{ + if (len > AW_SEQUENCER_SIZE) + len = AW_SEQUENCER_SIZE; + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_WAVCFG1, + aw_haptic->seq, len); +} + +static void aw862xx_set_wav_seq(struct aw_haptic *aw_haptic, uint8_t wav, + uint8_t seq) +{ + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_WAVCFG1 + wav, &seq, + AW_I2C_BYTE_ONE); +} + +static void aw862xx_set_wav_loop(struct aw_haptic *aw_haptic, uint8_t wav, + uint8_t loop) +{ + uint8_t tmp = 0; + + if (wav % 2) { + tmp = loop << 0; + haptic_nv_i2c_write_bits(aw_haptic, + AW862XX_REG_WAVCFG9 + (wav / 2), + AW862XX_BIT_WAVLOOP_SEQ_EVEN_MASK, tmp); + } else { + tmp = loop << 4; + haptic_nv_i2c_write_bits(aw_haptic, + AW862XX_REG_WAVCFG9 + (wav / 2), + AW862XX_BIT_WAVLOOP_SEQ_ODD_MASK, tmp); + } +} + +static void aw862xx_set_repeat_seq(struct aw_haptic *aw_haptic, uint8_t seq) +{ + aw862xx_set_wav_seq(aw_haptic, 0x00, seq); + aw862xx_set_wav_seq(aw_haptic, 0x01, 0x00); + aw862xx_set_wav_loop(aw_haptic, 0x00, AW862XX_BIT_WAVLOOP_INIFINITELY); +} + +static void aw862xx_get_gain(struct aw_haptic *aw_haptic, uint8_t *gain) +{ + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_PLAYCFG2, gain, + AW_I2C_BYTE_ONE); +} + +static void aw862xx_get_wav_loop(struct aw_haptic *aw_haptic, uint8_t *val) +{ + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_WAVCFG9, val, + AW_SEQUENCER_LOOP_SIZE); +} + +static void aw862xx_get_ram_data(struct aw_haptic *aw_haptic, uint8_t *ram_data, + int size) +{ + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_RAMDATA, ram_data, size); +} + + +static int aw862xx_select_d2s_gain(uint8_t reg) +{ + int d2s_gain = 0; + + switch (reg) { + case AW862XX_BIT_SYSCTRL7_D2S_GAIN_1: + d2s_gain = 1; + break; + case AW862XX_BIT_SYSCTRL7_D2S_GAIN_2: + d2s_gain = 2; + break; + case AW862XX_BIT_SYSCTRL7_D2S_GAIN_4: + d2s_gain = 4; + break; + case AW862XX_BIT_SYSCTRL7_D2S_GAIN_5: + d2s_gain = 5; + break; + case AW862XX_BIT_SYSCTRL7_D2S_GAIN_8: + d2s_gain = 8; + break; + case AW862XX_BIT_SYSCTRL7_D2S_GAIN_10: + d2s_gain = 10; + break; + case AW862XX_BIT_SYSCTRL7_D2S_GAIN_20: + d2s_gain = 20; + break; + case AW862XX_BIT_SYSCTRL7_D2S_GAIN_40: + d2s_gain = 40; + break; + default: + d2s_gain = -1; + break; + } + return d2s_gain; +} + +static void aw862xx_get_lra_resistance(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint8_t d2s_gain_temp = 0; + uint32_t lra_code = 0; + uint32_t lra = 0; + int d2s_gain = 0; + + aw862xx_play_stop(aw_haptic); + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_SYSCTRL7, ®_val, + AW_I2C_BYTE_ONE); + d2s_gain_temp = AW862XX_BIT_SYSCTRL7_GAIN & reg_val; + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL7, + AW862XX_BIT_SYSCTRL7_D2S_GAIN_MASK, + aw_haptic->info.d2s_gain); + d2s_gain = aw862xx_select_d2s_gain(aw_haptic->info.d2s_gain); + if (d2s_gain < 0) { + aw_err("d2s_gain is error"); + return; + } + aw862xx_raminit(aw_haptic, true); + /* enter standby mode */ + aw862xx_play_stop(aw_haptic); + usleep_range(AW_STOP_DELAY_MIN, AW_STOP_DELAY_MAX); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL2, + AW862XX_BIT_SYSCTRL2_STANDBY_MASK, + AW862XX_BIT_SYSCTRL2_STANDBY_OFF); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_DETCFG1, + AW862XX_BIT_DETCFG1_RL_OS_MASK, + AW862XX_BIT_DETCFG1_RL); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_DETCFG2, + AW862XX_BIT_DETCFG2_DIAG_GO_MASK, + AW862XX_BIT_DETCFG2_DIAG_GO_ON); + usleep_range(AW_RL_DELAY_MIN, AW_RL_DELAY_MAX); + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_DET_RL, ®_val, + AW_I2C_BYTE_ONE); + lra_code = (lra_code | reg_val) << 2; + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_DET_LO, ®_val, + AW_I2C_BYTE_ONE); + lra_code = lra_code | (reg_val & AW862XX_BIT_DET_LO_RL); + /* 2num */ + lra = AW862XX_RL_FORMULA(lra_code, d2s_gain); + /* Keep up with aw8624 driver */ + aw_haptic->lra = lra * 10; + aw862xx_raminit(aw_haptic, false); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL7, + AW862XX_BIT_SYSCTRL7_D2S_GAIN_MASK, + d2s_gain_temp); +} + +static uint8_t aw862xx_get_prctmode(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_PWMCFG3, ®_val, + AW_I2C_BYTE_ONE); + reg_val >>= 7; + return reg_val; +} + +static uint8_t aw862xx_judge_rtp_going(struct aw_haptic *aw_haptic) +{ + uint8_t glb_state = 0; + uint8_t rtp_state = 0; + + glb_state = aw862xx_get_glb_state(aw_haptic); + if (glb_state == AW_BIT_STATE_RTP_GO) { + rtp_state = 1; /*is going on */ + aw_info("rtp_routine_on"); + } + return rtp_state; +} + +static uint64_t aw862xx_get_theory_time(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t fre_val = 0; + uint64_t theory_time = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_SYSCTRL2, ®_val, + AW_I2C_BYTE_ONE); + fre_val = reg_val & AW862XX_BIT_SYSCTRL2_RATE; + if (fre_val == AW862XX_BIT_SYSCTRL2_RATE_48K) + theory_time = (aw_haptic->rtp_len / 48000) * 1000000; /*48K*/ + else if (fre_val == AW862XX_BIT_SYSCTRL2_RATE_24K) + theory_time = (aw_haptic->rtp_len / 24000) * 1000000; /*24K*/ + else + theory_time = (aw_haptic->rtp_len / 12000) * 1000000; /*12K*/ + aw_info("microsecond:%llu theory_time = %llu", + aw_haptic->microsecond, theory_time); + return theory_time; +} + +static uint8_t aw862xx_get_osc_status(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_SYSST2, &state, + AW_I2C_BYTE_ONE); + state &= AW862XX_BIT_SYSST2_FF_EMPTY; + return state; +} + +static int aw862xx_check_qualify(struct aw_haptic *aw_haptic) +{ + uint8_t reg = 0; + int ret = 0; + + aw_info("enter"); + ret = haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_EFRD9, ®, + AW_I2C_BYTE_ONE); + if (ret < 0) + return ret; + if ((reg & 0x80) == 0x80) + return 0; + aw_err("register 0x64 error: 0x%02X", reg); + return -ERANGE; +} + +static ssize_t cont_wait_num_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_wait_num = 0x%02X\n", + aw_haptic->info.cont_wait_num); + return len; +} + +static ssize_t cont_wait_num_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t reg_val = 0; + int err, val; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + err = kstrtoint(buf, 16, &val); + if (err != 0) { + aw_err("format not match!"); + return count; + } + mutex_lock(&aw_haptic->lock); + aw_haptic->info.cont_wait_num = val; + reg_val = aw_haptic->info.cont_wait_num; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG4, ®_val, + AW_I2C_BYTE_ONE); + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t cont_drv_lvl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_lvl = 0x%02X, cont_drv2_lvl = 0x%02X\n", + aw_haptic->info.cont_drv1_lvl, + aw_haptic->info.cont_drv2_lvl); + return len; +} + +static ssize_t cont_drv_lvl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint8_t reg_val = 0; + uint32_t databuf[2] = { 0, 0 }; + + mutex_lock(&aw_haptic->lock); + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_haptic->info.cont_drv1_lvl = databuf[0]; + aw_haptic->info.cont_drv2_lvl = databuf[1]; + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_CONTCFG6, + AW862XX_BIT_CONTCFG6_DRV1_LVL_MASK, + aw_haptic->info.cont_drv1_lvl); + reg_val = (uint8_t)aw_haptic->info.cont_drv2_lvl; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG7, ®_val, + AW_I2C_BYTE_ONE); + } + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t cont_drv_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_time = 0x%02X, cont_drv2_time = 0x%02X\n", + aw_haptic->info.cont_drv1_time, + aw_haptic->info.cont_drv2_time); + return len; +} + +static ssize_t cont_drv_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint8_t reg_val[2] = {0}; + uint32_t databuf[2] = { 0, 0 }; + + mutex_lock(&aw_haptic->lock); + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_haptic->info.cont_drv1_time = databuf[0]; + aw_haptic->info.cont_drv2_time = databuf[1]; + reg_val[0] = (uint8_t)aw_haptic->info.cont_drv1_time; + reg_val[1] = (uint8_t)aw_haptic->info.cont_drv2_time; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG8, reg_val, + AW_I2C_BYTE_TWO); + } + mutex_unlock(&aw_haptic->lock); + return count; +} + +static DEVICE_ATTR(cont_wait_num, 0664, cont_wait_num_show, + cont_wait_num_store); +static DEVICE_ATTR(cont_drv_lvl, 0664, cont_drv_lvl_show, cont_drv_lvl_store); +static DEVICE_ATTR(cont_drv_time, 0664, cont_drv_time_show, + cont_drv_time_store); + +static struct attribute *aw862xx_vibrator_attributes[] = { + &dev_attr_cont_wait_num.attr, + &dev_attr_cont_drv_lvl.attr, + &dev_attr_cont_drv_time.attr, + NULL +}; + +static struct attribute_group aw862xx_vibrator_attribute_group = { + .attrs = aw862xx_vibrator_attributes +}; + +#ifdef AW8622X_DRIVER +static ssize_t cont_brk_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "cont_brk_time = 0x%02X\n", + aw_haptic->info.cont_brk_time); + return len; +} + +static ssize_t cont_brk_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t reg_val = 0; + int err, val; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + err = kstrtoint(buf, 16, &val); + if (err != 0) { + aw_err("format not match!"); + return count; + } + mutex_lock(&aw_haptic->lock); + aw_haptic->info.cont_brk_time = val; + reg_val = aw_haptic->info.cont_brk_time; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG10, ®_val, + AW_I2C_BYTE_ONE); + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t trig_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i = 0; + ssize_t len = 0; + uint8_t trig_num = 3; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (aw_haptic->name == AW86224 || aw_haptic->name == AW86225) + trig_num = 1; + for (i = 0; i < trig_num; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "trig%d: trig_level=%d, trig_polar=%d", + i + 1, + aw_haptic->trig[i].trig_level, + aw_haptic->trig[i].trig_polar); + len += snprintf(buf + len, PAGE_SIZE - len, + "pos_enable=%d, pos_sequence=%d,", + aw_haptic->trig[i].pos_enable, + aw_haptic->trig[i].pos_sequence); + len += snprintf(buf + len, PAGE_SIZE - len, + "neg_enable=%d, neg_sequence=%d trig_brk=%d\n", + aw_haptic->trig[i].neg_enable, + aw_haptic->trig[i].neg_sequence, + aw_haptic->trig[i].trig_brk); + } + return len; +} + +static ssize_t trig_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t databuf[9] = { 0 }; + + if (sscanf(buf, "%d %d %d %d %d %d %d %d", + &databuf[0], &databuf[1], &databuf[2], + &databuf[3], &databuf[4], &databuf[5], + &databuf[6], &databuf[7]) == 8) { + aw_info("%d, %d, %d, %d, %d, %d, %d, %d", + databuf[0], databuf[1], databuf[2], databuf[3], + databuf[4], databuf[5], databuf[6], databuf[7]); + if ((aw_haptic->name == AW86224 || + aw_haptic->name == AW86225) && (databuf[0])) { + aw_err("input seq value out of range!"); + return count; + } + if (databuf[0] < 0 || databuf[0] > 2) { + aw_err("input seq value out of range!"); + return count; + } + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + if (databuf[4] > aw_haptic->ram.ram_num || + databuf[6] > aw_haptic->ram.ram_num) { + aw_err("input seq value out of range!"); + return count; + } + aw_haptic->trig[databuf[0]].trig_level = databuf[1]; + aw_haptic->trig[databuf[0]].trig_polar = databuf[2]; + aw_haptic->trig[databuf[0]].pos_enable = databuf[3]; + aw_haptic->trig[databuf[0]].pos_sequence = databuf[4]; + aw_haptic->trig[databuf[0]].neg_enable = databuf[5]; + aw_haptic->trig[databuf[0]].neg_sequence = databuf[6]; + aw_haptic->trig[databuf[0]].trig_brk = databuf[7]; + mutex_lock(&aw_haptic->lock); + aw862xx_haptic_trig_param_config(aw_haptic, databuf[0]); + mutex_unlock(&aw_haptic->lock); + } else + aw_err("please input eight parameters"); + return count; +} + +static DEVICE_ATTR(cont_brk_time, 0664, cont_brk_time_show, + cont_brk_time_store); +static DEVICE_ATTR(trig, 0664, trig_show, trig_store); + +static struct attribute *aw8622x_vibrator_attributes[] = { + &dev_attr_cont_brk_time.attr, + &dev_attr_trig.attr, + NULL +}; + +static struct attribute_group aw8622x_vibrator_attribute_group = { + .attrs = aw8622x_vibrator_attributes +}; +#endif + +static void aw862xx_creat_node(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + ret = sysfs_create_group(&aw_haptic->vib_dev.dev->kobj, + &aw862xx_vibrator_attribute_group); + if (ret < 0) + aw_err("error create aw862xx sysfs attr files"); +#ifdef AW8622X_DRIVER + if (aw_haptic->name == AW86223 || aw_haptic->name == AW86224 || + aw_haptic->name == AW86225) { + ret = sysfs_create_group(&aw_haptic->vib_dev.dev->kobj, + &aw8622x_vibrator_attribute_group); + if (ret < 0) + aw_err("error create aw8622x sysfs attr files"); + } +#endif +} + +struct aw_haptic_func aw862xx_func_list = { + .ram_init = aw862xx_raminit, + .trig_init = aw862xx_trig_init, + .play_mode = aw862xx_play_mode, + .play_stop = aw862xx_play_stop, + .irq_clear = aw862xx_irq_clear, + .creat_node = aw862xx_creat_node, + .cont_config = aw862xx_cont_config, + .offset_cali = aw862xx_offset_cali, + .haptic_start = aw862xx_haptic_start, + .read_cont_f0 = aw862xx_read_cont_f0, + .check_qualify = aw862xx_check_qualify, + .judge_rtp_going = aw862xx_judge_rtp_going, + .protect_config = aw862xx_protect_config, + .misc_para_init = aw862xx_misc_para_init, + .interrupt_setup = aw862xx_interrupt_setup, + .rtp_get_fifo_afs = aw862xx_rtp_get_fifo_afs, + .vbat_mode_config = aw862xx_vbat_mode_config, + .calculate_cali_data = aw862xx_calculate_cali_data, + .set_gain = aw862xx_set_gain, + .get_gain = aw862xx_get_gain, + .set_wav_seq = aw862xx_set_wav_seq, + .get_wav_seq = aw862xx_get_wav_seq, + .set_wav_loop = aw862xx_set_wav_loop, + .get_wav_loop = aw862xx_get_wav_loop, + .set_ram_data = aw862xx_set_ram_data, + .get_ram_data = aw862xx_get_ram_data, + .set_fifo_addr = aw862xx_set_fifo_addr, + .get_fifo_addr = aw862xx_get_fifo_addr, + .set_rtp_aei = aw862xx_set_rtp_aei, + .set_rtp_data = aw862xx_set_rtp_data, + .set_ram_addr = aw862xx_set_ram_addr, + .set_trim_lra = aw862xx_set_trim_lra, + .set_base_addr = aw862xx_set_base_addr, + .set_repeat_seq = aw862xx_set_repeat_seq, + .get_f0 = aw862xx_get_f0, + .get_reg = aw862xx_get_reg, + .get_vbat = aw862xx_get_vbat, + .get_prctmode = aw862xx_get_prctmode, + .get_irq_state = aw862xx_get_irq_state, + .get_glb_state = aw862xx_get_glb_state, + .get_osc_status = aw862xx_get_osc_status, + .get_theory_time = aw862xx_get_theory_time, + .get_lra_resistance = aw862xx_get_lra_resistance, + .get_first_wave_addr = aw862xx_get_first_wave_addr, +}; diff --git a/drivers/misc/awinic/aw862x_haptic_nv/haptic_nv.c b/drivers/misc/awinic/aw862x_haptic_nv/haptic_nv.c new file mode 100755 index 000000000000..90c50df3517f --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv/haptic_nv.c @@ -0,0 +1,3536 @@ +/* + * File: haptic_nv.c + * + * Author: + * + * Copyright (c) 2021 AWINIC Technology CO., 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "haptic_nv.h" +#include "haptic_nv_reg.h" +#include "haptic_nv_dts.h" + +#define HAPTIC_NV_DRIVER_VERSION "v1.9.0" + +static char aw_ram_name[][AW_NAME_MAX] = { + {"aw8624_haptic.bin"}, + {"aw8622x_haptic.bin"}, + {"aw86214_haptic.bin"}, + {"aw8623x_haptic.bin"}, +}; + +static char aw_rtp_name[][AW_NAME_MAX] = { + {"haptic_nv_rtp_osc_24K_5s.bin"}, + {"haptic_nv_rtp.bin"}, + {"haptic_nv_rtp_lighthouse.bin"}, + {"haptic_nv_rtp_silk.bin"}, +}; + +struct transient_trig_data { + int activate; + int state; + int restore_state; + unsigned long duration; + struct timer_list timer; + struct led_classdev *led_cdev; +}; + +/********************************************************* + * + * I2C Read/Write + * + *********************************************************/ +int haptic_nv_i2c_reads(struct aw_haptic *aw_haptic, uint8_t reg_addr, + uint8_t *buf, uint32_t len) +{ + int ret = -1; + struct i2c_msg msg[] = { + [0] = { + .addr = aw_haptic->i2c->addr, + .flags = 0, + .len = sizeof(uint8_t), + .buf = ®_addr, + }, + [1] = { + .addr = aw_haptic->i2c->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + }, + }; + + ret = i2c_transfer(aw_haptic->i2c->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) { + aw_err("transfer failed. reg_addr=0x%02X", reg_addr); + return ret; + } else if (ret != AW_I2C_READ_MSG_NUM) { + aw_err("transfer failed(size error). reg_addr=0x%02X", + reg_addr); + return -ENXIO; + } + + return ret; +} + +int haptic_nv_i2c_writes(struct aw_haptic *aw_haptic, uint8_t reg_addr, + uint8_t *buf, uint32_t len) +{ + uint8_t *data = NULL; + int ret = -1; + + data = kmalloc(len + 1, GFP_KERNEL); + data[0] = reg_addr; + memcpy(&data[1], buf, len); + ret = i2c_master_send(aw_haptic->i2c, data, len + 1); + if (ret < 0) + aw_err("i2c master send 0x%02X err", reg_addr); + kfree(data); + return ret; +} + +int haptic_nv_i2c_write_bits(struct aw_haptic *aw_haptic, uint8_t reg_addr, + uint32_t mask, uint8_t reg_data) +{ + uint8_t reg_val = 0; + int ret = -1; + + ret = haptic_nv_i2c_reads(aw_haptic, reg_addr, ®_val, + AW_I2C_BYTE_ONE); + if (ret < 0) { + aw_err("i2c read error, ret=%d", ret); + return ret; + } + reg_val &= mask; + reg_val |= (reg_data & (~mask)); + ret = haptic_nv_i2c_writes(aw_haptic, reg_addr, ®_val, + AW_I2C_BYTE_ONE); + if (ret < 0) { + aw_err("i2c write error, ret=%d", ret); + return ret; + } + return 0; +} + +ssize_t haptic_nv_read_reg_array(struct aw_haptic *aw_haptic, char *buf, + ssize_t len, uint8_t head_reg_addr, + uint8_t tail_reg_addr) +{ + int reg_num = 0; + int i = 0; + uint8_t reg_array[AW_REG_MAX] = {0}; + + reg_num = tail_reg_addr - head_reg_addr + 1; + haptic_nv_i2c_reads(aw_haptic, head_reg_addr, reg_array, reg_num); + for (i = 0 ; i < reg_num; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "reg:0x%02X=0x%02X\n", + head_reg_addr + i, reg_array[i]); + } + return len; +} + +static void pm_qos_enable(struct aw_haptic *aw_haptic, bool enable) +{ +#ifdef KERNEL_OVER_5_10 + if (enable) { + if (!cpu_latency_qos_request_active(&aw_haptic->aw_pm_qos_req_vb)) + cpu_latency_qos_add_request(&aw_haptic->aw_pm_qos_req_vb, CPU_LATENCY_QOC_VALUE); + } else { + if (cpu_latency_qos_request_active(&aw_haptic->aw_pm_qos_req_vb)) + cpu_latency_qos_remove_request(&aw_haptic->aw_pm_qos_req_vb); + } +#else + if (enable) { + if (!pm_qos_request_active(&aw_haptic->aw_pm_qos_req_vb)) + pm_qos_add_request(&aw_haptic->aw_pm_qos_req_vb, + PM_QOS_CPU_DMA_LATENCY, + AW_PM_QOS_VALUE_VB); + } else { + if (pm_qos_request_active(&aw_haptic->aw_pm_qos_req_vb)) + pm_qos_remove_request(&aw_haptic->aw_pm_qos_req_vb); + } +#endif +} + +#ifdef AW_CHECK_RAM_DATA +static int parse_ram_data(struct aw_haptic *aw_haptic, uint32_t len, + uint8_t *cont_data, uint8_t *ram_data) +{ + int i = 0; + + aw_info("enter"); + for (i = 0; i < len; i++) { + if (ram_data[i] != cont_data[i]) { + aw_err("check ramdata error, addr=0x%04X, ram_data=0x%02X, file_data=0x%02X", + i, ram_data[i], cont_data[i]); + return -ERANGE; + } + } + return 0; +} + +static int check_ram_data(struct aw_haptic *aw_haptic, + struct aw_haptic_container *aw_fw) +{ + int i = 0; + int len = 0; + int ret = 0; + uint8_t ram_data[AW_RAMDATA_RD_BUFFER_SIZE] = {0}; + + aw_haptic->func->set_ram_addr(aw_haptic); + i = aw_haptic->ram.ram_shift; + while (i < aw_fw->len) { + if ((aw_fw->len - i) < AW_RAMDATA_RD_BUFFER_SIZE) + len = aw_fw->len - i; + else + len = AW_RAMDATA_RD_BUFFER_SIZE; + + aw_haptic->func->get_ram_data(aw_haptic, ram_data, len); + ret = parse_ram_data(aw_haptic, len, &aw_fw->data[i], ram_data); + if (ret < 0) + break; + i += len; + } + return ret; +} +#endif + +static void write_ram_data(struct aw_haptic *aw_haptic, + struct aw_haptic_container *aw_fw) +{ + int i = 0; + int len = 0; + + aw_haptic->func->set_ram_addr(aw_haptic); + i = aw_haptic->ram.ram_shift; + while (i < aw_fw->len) { + if ((aw_fw->len - i) < AW_RAMDATA_WR_BUFFER_SIZE) + len = aw_fw->len - i; + else + len = AW_RAMDATA_WR_BUFFER_SIZE; + + aw_haptic->func->set_ram_data(aw_haptic, &aw_fw->data[i], len); + i += len; + } +} + +static int parse_dt_gpio(struct aw_haptic *aw_haptic, struct device *dev, + struct device_node *np) +{ + aw_haptic->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (aw_haptic->reset_gpio < 0) { + aw_err("no reset gpio provide"); + return -EPERM; + } + aw_info("reset gpio provide ok %d", aw_haptic->reset_gpio); + + aw_haptic->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (aw_haptic->irq_gpio < 0) { + aw_err("no irq gpio provided."); + aw_haptic->is_used_irq_pin = false; + } else { + aw_info("irq gpio provide ok irq = %d.", aw_haptic->irq_gpio); + aw_haptic->is_used_irq_pin = true; + } + + return 0; +} + +static void parse_dts_i2c_addr(struct aw_haptic *aw_haptic, struct device *dev, + struct device_node *np) +{ + uint32_t val = 0; + + val = of_property_read_u32(np, "aw862xx_i2c_addr", + &aw_haptic->aw862xx_i2c_addr); + if (val) + aw_err("configure aw862xx_i2c_addr error"); + else + aw_info("configure aw862xx_i2c_addr ok"); +} + +static void hw_reset(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + if (aw_haptic && gpio_is_valid(aw_haptic->reset_gpio)) { + gpio_set_value_cansleep(aw_haptic->reset_gpio, 0); + usleep_range(1000, 2000); + gpio_set_value_cansleep(aw_haptic->reset_gpio, 1); + usleep_range(8000, 8500); + } else { + aw_err("failed"); + } +} + +static void sw_reset(struct aw_haptic *aw_haptic) +{ + uint8_t reset = AW_BIT_RESET; + + aw_info("enter"); + haptic_nv_i2c_writes(aw_haptic, AW_REG_ID, &reset, AW_I2C_BYTE_ONE); + usleep_range(2000, 2500); +} + +static int read_chipid(struct aw_haptic *aw_haptic, uint8_t *reg_val, + uint8_t type) +{ + int cnt = 0; + int ret = -1; + + while (cnt < AW_I2C_RETRIES) { + ret = haptic_nv_i2c_reads(aw_haptic, AW_REG_CHIPIDH, reg_val, + AW_I2C_BYTE_ONE); + if (*reg_val == AW8623X_CHIP_ID_H) + ret = haptic_nv_i2c_reads(aw_haptic, AW_REG_CHIPIDL, + reg_val, AW_I2C_BYTE_ONE); + else + ret = haptic_nv_i2c_reads(aw_haptic, AW_REG_ID, reg_val, + AW_I2C_BYTE_ONE); + if (ret < 0) { + if (type == AW_FIRST_TRY) + aw_info("reading chip id"); + else if (type == AW_LAST_TRY) + aw_err("i2c_read cnt=%d error=%d", cnt, ret); + else + aw_err("type is error"); + } else { + break; + } + cnt++; + usleep_range(2000, 3000); + } + + return ret; +} + +static int parse_chipid(struct aw_haptic *aw_haptic) +{ + int ret = -1; + uint8_t reg[2] = { 0 }; + uint8_t cnt = 0; + + for (cnt = 0; cnt < AW_READ_CHIPID_RETRIES; cnt++) { + /* hardware reset */ + hw_reset(aw_haptic); + + ret = read_chipid(aw_haptic, ®[0], AW_FIRST_TRY); + if (ret < 0) { + aw_haptic->i2c->addr = (u16)aw_haptic->aw862xx_i2c_addr; + aw_info("try to replace i2c addr [(0x%02X)] to read chip id again", + aw_haptic->i2c->addr); + ret = read_chipid(aw_haptic, ®[0], AW_LAST_TRY); + if (ret < 0) + break; + } + + switch (reg[0]) { +#ifdef AW8624_DRIVER + case AW8624_CHIP_ID: + aw_haptic->name = AW8624; + aw_info("detected aw8624."); + return 0; +#endif + +#ifdef AW8622X_DRIVER + case AW8622X_CHIP_ID: + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_EFRD9, + ®[1], AW_I2C_BYTE_ONE); + if ((reg[1] & 0x41) == AW86223_EF_ID) { + aw_haptic->name = AW86223; + aw_info("aw86223 detected"); + return 0; + } + if ((reg[1] & 0x41) == AW86224_EF_ID) { + aw_haptic->name = AW86224; + aw_info("aw86224 or aw86225 detected"); + return 0; + } + aw_info("unsupported ef_id = (0x%02X)", reg[1]); + break; +#endif + +#ifdef AW86214_DRIVER + case AW86214_CHIP_ID: + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_EFRD9, + ®[1], AW_I2C_BYTE_ONE); + if ((reg[1] & 0x41) == AW86214_EF_ID) { + aw_haptic->name = AW86214; + aw_info("aw86214 detected"); + return 0; + } + aw_info("unsupported ef_id = (0x%02X)", reg[1]); + break; +#endif + +#ifdef AW8623X_DRIVER + case AW86233_CHIP_ID_L: + aw_haptic->name = AW86233; + aw_info("aw86233 detected"); + return 0; + case AW86234_CHIP_ID_L: + aw_haptic->name = AW86234; + aw_info("aw86234 detected"); + return 0; + case AW86235_CHIP_ID_L: + aw_haptic->name = AW86235; + aw_info("aw86235 detected"); + return 0; +#endif + + default: + aw_info("unsupport device revision (0x%02X)", reg[0]); + break; + } + usleep_range(AW_READ_CHIPID_RETRY_DELAY * 1000, + AW_READ_CHIPID_RETRY_DELAY * 1000 + 500); + } + return -EINVAL; +} + +static int chip_private_init(struct aw_haptic *aw_haptic) +{ + switch (aw_haptic->name) { +#ifdef AW8624_DRIVER + case AW8624: + aw_haptic->is_supported_rtp = true; + aw_haptic->is_supported_trig = true; + aw_haptic->dts_name_array = aw8624_dts_name; + aw_haptic->dts_name_len = ARRAY_SIZE(aw8624_dts_name); + aw_haptic->ram_file_num = AW8624_RAM_FILE; + break; +#endif + +#ifdef AW8622X_DRIVER + case AW86223: + case AW86224: + case AW86225: + aw_haptic->is_supported_rtp = true; + aw_haptic->is_supported_trig = true; + aw_haptic->dts_name_array = aw8622x_dts_name; + aw_haptic->dts_name_len = ARRAY_SIZE(aw8622x_dts_name); + aw_haptic->ram_file_num = AW8622X_RAM_FILE; + break; +#endif + +#ifdef AW86214_DRIVER + case AW86214: + aw_haptic->is_supported_rtp = false; + aw_haptic->is_supported_trig = false; + aw_haptic->dts_name_array = aw86214_dts_name; + aw_haptic->dts_name_len = ARRAY_SIZE(aw86214_dts_name); + aw_haptic->ram_file_num = AW86214_RAM_FILE; + break; +#endif + +#ifdef AW8623X_DRIVER + case AW86233: + case AW86234: + case AW86235: + aw_haptic->is_supported_rtp = true; + aw_haptic->is_supported_trig = true; + aw_haptic->dts_name_array = aw8623x_dts_name; + aw_haptic->dts_name_len = ARRAY_SIZE(aw8623x_dts_name); + aw_haptic->ram_file_num = AW8623X_RAM_FILE; + break; +#endif + + default: + aw_info("unexpected chip!"); + return -EINVAL; + } + return 0; +} + +static int func_ptr_init(struct aw_haptic *aw_haptic, struct device *dev) +{ + switch (aw_haptic->name) { +#ifdef AW8624_DRIVER + case AW8624: + aw_haptic->func = &aw8624_func_list; + break; +#endif + +#ifdef AW8622X_DRIVER + case AW86223: + case AW86224: + case AW86225: + aw_haptic->func = &aw862xx_func_list; + break; +#endif + +#ifdef AW86214_DRIVER + case AW86214: + aw_haptic->func = &aw862xx_func_list; + break; +#endif + +#ifdef AW8623X_DRIVER + case AW86233: + case AW86234: + case AW86235: + aw_haptic->func = &aw8623x_func_list; + break; +#endif + + default: + aw_info("unexpected chip!"); + return -EINVAL; + } + return 0; +} + +static int write_rtp_data(struct aw_haptic *aw_haptic) +{ + uint32_t buf_len = 0; + int ret = -ERANGE; + struct aw_haptic_container *aw_rtp = aw_haptic->aw_rtp; + + if (!aw_rtp) { + aw_info("aw_rtp is null, break!"); + return ret; + } + + if (aw_haptic->rtp_cnt < (aw_haptic->ram.base_addr)) { + if ((aw_rtp->len - aw_haptic->rtp_cnt) < + (aw_haptic->ram.base_addr)) { + buf_len = aw_rtp->len - aw_haptic->rtp_cnt; + } else { + if (aw_haptic->rtp_loop) + buf_len = aw_haptic->ram.base_addr >> 2; + else + buf_len = aw_haptic->ram.base_addr; + } + } else if ((aw_rtp->len - aw_haptic->rtp_cnt) < + (aw_haptic->ram.base_addr >> 2)) { + buf_len = aw_rtp->len - aw_haptic->rtp_cnt; + } else { + buf_len = aw_haptic->ram.base_addr >> 2; + } + +#ifdef AW_ENABLE_RTP_PRINT_LOG + aw_info("buf_len = %d", buf_len); +#endif + + aw_haptic->func->set_rtp_data(aw_haptic, + &aw_rtp->data[aw_haptic->rtp_cnt], + buf_len); + aw_haptic->rtp_cnt += buf_len; + return 0; + +} + +static int judge_rtp_load_end(struct aw_haptic *aw_haptic) +{ + uint8_t glb_st = 0; + int ret = -ERANGE; + struct aw_haptic_container *aw_rtp = aw_haptic->aw_rtp; + struct transient_trig_data *transient_data = led_get_trigger_data(&aw_haptic->vib_dev); + + glb_st = aw_haptic->func->get_glb_state(aw_haptic); + + if ((aw_haptic->rtp_cnt == aw_rtp->len) || + ((glb_st & AW_BIT_GLBRD_STATE_MASK) == AW_BIT_STATE_STANDBY)) { + if (aw_haptic->rtp_cnt != aw_rtp->len) { + aw_haptic->rtp_cnt = 0; + aw_haptic->rtp_init = false; + if (transient_data) { + transient_data->activate = 0; + del_timer(&transient_data->timer); + } + aw_err("rtp play suspend!"); + aw_haptic->func->set_rtp_aei(aw_haptic, false); + ret = 0; + } else if (aw_haptic->rtp_file_num >= 1 && aw_haptic->rtp_file_num <= 50) { + aw_haptic->rtp_cnt = 0; + aw_haptic->rtp_init = true; + aw_haptic->rtp_loop = true; + pm_qos_enable(aw_haptic, true); + aw_info("rtp update loop!"); + } else { + aw_haptic->rtp_cnt = 0; + aw_haptic->rtp_init = false; + if (transient_data) { + transient_data->activate = 0; + del_timer(&transient_data->timer); + } + aw_info("rtp update complete!,cnt=%d", + aw_haptic->rtp_cnt); + aw_haptic->func->set_rtp_aei(aw_haptic, false); + ret = 0; + } + } + + return ret; +} + +static void rtp_play(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + while ((!aw_haptic->func->rtp_get_fifo_afs(aw_haptic)) + && (aw_haptic->play_mode == AW_RTP_MODE)) { + +#ifdef AW_ENABLE_RTP_PRINT_LOG + aw_info("rtp cnt = %d", aw_haptic->rtp_cnt); +#endif + ret = write_rtp_data(aw_haptic); + if (ret != 0) + break; + ret = judge_rtp_load_end(aw_haptic); + if (ret == 0) + break; + } +} + +static irqreturn_t irq_handle(int irq, void *data) +{ + int irq_state = 0; + struct aw_haptic *aw_haptic = data; + struct transient_trig_data *transient_data = led_get_trigger_data(&aw_haptic->vib_dev); + + aw_info("enter"); + + do { + irq_state = aw_haptic->func->get_irq_state(aw_haptic); + + if (aw_haptic->is_supported_rtp == false) + return IRQ_HANDLED; + + if (irq_state == AW_IRQ_ALMOST_EMPTY) { + if (aw_haptic->rtp_init) { + mutex_lock(&aw_haptic->rtp_lock); + rtp_play(aw_haptic); + mutex_unlock(&aw_haptic->rtp_lock); + } else { + aw_info("rtp_init: %d", aw_haptic->rtp_init); + if (transient_data) { + transient_data->activate = 0; + del_timer(&transient_data->timer); + } + } + } + + if (aw_haptic->play_mode != AW_RTP_MODE) + aw_haptic->func->set_rtp_aei(aw_haptic, false); + } while (irq_state); + + aw_info("exit"); + return IRQ_HANDLED; +} + +static int irq_config(struct device *dev, struct aw_haptic *aw_haptic) +{ + int ret = -1; + int irq_flags = 0; + + aw_info("enter"); + if (gpio_is_valid(aw_haptic->irq_gpio) && + !(aw_haptic->flags & AW_FLAG_SKIP_INTERRUPTS)) { + /* register irq handler */ + aw_haptic->func->interrupt_setup(aw_haptic); + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(dev, + gpio_to_irq(aw_haptic->irq_gpio), + NULL, irq_handle, irq_flags, + "aw_haptic", aw_haptic); + if (ret != 0) { + aw_err("failed to request IRQ %d: %d", + gpio_to_irq(aw_haptic->irq_gpio), ret); + return ret; + } + } else { + aw_info("skipping IRQ registration"); + /* disable feature support if gpio was invalid */ + aw_haptic->flags |= AW_FLAG_SKIP_INTERRUPTS; + } + + return 0; +} + +static int get_ram_num(struct aw_haptic *aw_haptic) +{ + uint32_t first_wave_addr = 0; + + aw_info("enter"); + if (!aw_haptic->ram_init) { + aw_err("ram init faild, ram_num = 0!"); + return -EPERM; + } + + mutex_lock(&aw_haptic->lock); + /* RAMINIT Enable */ + aw_haptic->func->play_stop(aw_haptic); + aw_haptic->func->ram_init(aw_haptic, true); + aw_haptic->func->set_ram_addr(aw_haptic); + aw_haptic->func->get_first_wave_addr(aw_haptic, &first_wave_addr); + aw_haptic->ram.ram_num = + (first_wave_addr - aw_haptic->ram.base_addr - 1) / 4; + aw_info("first wave addr = 0x%04X, ram_num = %d", + first_wave_addr, aw_haptic->ram.ram_num); + /* RAMINIT Disable */ + aw_haptic->func->ram_init(aw_haptic, false); + mutex_unlock(&aw_haptic->lock); + + return 0; +} + +static int container_update(struct aw_haptic *aw_haptic, + struct aw_haptic_container *aw_fw) +{ + uint32_t shift = 0; + int ret = 0; + + aw_info("enter"); + + mutex_lock(&aw_haptic->lock); + aw_haptic->ram.baseaddr_shift = 2; + aw_haptic->ram.ram_shift = 4; + aw_haptic->func->play_stop(aw_haptic); + aw_haptic->func->ram_init(aw_haptic, true); + shift = aw_haptic->ram.baseaddr_shift; + aw_haptic->ram.base_addr = (uint32_t)((aw_fw->data[0 + shift] << 8) | + (aw_fw->data[1 + shift])); + aw_info("base_addr = %d", aw_haptic->ram.base_addr); + if (!aw_haptic->ram.base_addr) { + aw_haptic->is_supported_rtp = false; + aw_info("no set FIFO space for rtp play!"); + } + aw_haptic->func->set_base_addr(aw_haptic); + aw_haptic->func->set_fifo_addr(aw_haptic); + aw_haptic->func->get_fifo_addr(aw_haptic); + write_ram_data(aw_haptic, aw_fw); + +#ifdef AW_CHECK_RAM_DATA + ret = check_ram_data(aw_haptic, aw_fw); + if (ret) + aw_err("ram data check sum error"); + else + aw_info("ram data check sum pass"); +#endif + + aw_haptic->func->ram_init(aw_haptic, false); + mutex_unlock(&aw_haptic->lock); + + aw_info("exit"); + return ret; +} + +static void ram_load(const struct firmware *cont, void *context) +{ + uint16_t check_sum = 0; + int i = 0; + int ret = 0; + struct aw_haptic *aw_haptic = context; + struct aw_haptic_container *aw_fw; + uint32_t ram_file_num = aw_haptic->ram_file_num; + +#ifdef AW_READ_BIN_FLEXBALLY + static uint8_t load_cont; + int ram_timer_val = 1000; + + load_cont++; +#endif + + if (!cont) { + aw_err("failed to read %s", aw_ram_name[ram_file_num]); + release_firmware(cont); + +#ifdef AW_READ_BIN_FLEXBALLY + if (load_cont <= 20) { + schedule_delayed_work(&aw_haptic->ram_work, + msecs_to_jiffies(ram_timer_val)); + aw_info("start hrtimer:load_cont%d", load_cont); + } +#endif + + return; + } + + aw_info("loaded %s - size: %zu", aw_ram_name[ram_file_num], + cont ? cont->size : 0); + + /* check sum */ + for (i = 2; i < cont->size; i++) + check_sum += cont->data[i]; + if (check_sum != (uint16_t)((cont->data[0] << 8) | (cont->data[1]))) { + aw_err("check sum err: check_sum=0x%04X", check_sum); + release_firmware(cont); + return; + } + aw_info("check sum pass."); + aw_haptic->ram.check_sum = check_sum; + + /* aw ram update */ + aw_fw = kzalloc(cont->size + sizeof(int), GFP_KERNEL); + if (!aw_fw) { + release_firmware(cont); + aw_err("Error allocating memory"); + return; + } + aw_fw->len = cont->size; + memcpy(aw_fw->data, cont->data, cont->size); + release_firmware(cont); + ret = container_update(aw_haptic, aw_fw); + if (ret) { + aw_err("ram firmware update failed!"); + } else { + aw_haptic->ram_init = true; + aw_haptic->ram.len = aw_fw->len - aw_haptic->ram.ram_shift; + + mutex_lock(&aw_haptic->lock); + if (aw_haptic->is_supported_trig) + aw_haptic->func->trig_init(aw_haptic); + mutex_unlock(&aw_haptic->lock); + + aw_info("ram firmware update complete!"); + get_ram_num(aw_haptic); + } + kfree(aw_fw); + aw_fw = NULL; + +} + +static int ram_update(struct aw_haptic *aw_haptic) +{ + aw_haptic->ram_init = false; + aw_haptic->rtp_init = false; + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw_ram_name[aw_haptic->ram_file_num], + aw_haptic->dev, GFP_KERNEL, + aw_haptic, ram_load); +} + +static void ram_play(struct aw_haptic *aw_haptic) +{ + aw_haptic->func->play_mode(aw_haptic, aw_haptic->activate_mode); + aw_haptic->func->haptic_start(aw_haptic); +} + +#ifdef AW_DURATION_DECIDE_WAVEFORM +static int analyse_duration_range(struct aw_haptic *aw_haptic) +{ + int i = 0; + int ret = 0; + int len = 0; + int *duration_time = NULL; + + aw_info("enter"); + len = ARRAY_SIZE(aw_haptic->info.duration_time); + duration_time = aw_haptic->info.duration_time; + if (len < 2) { + aw_err("duration time range error"); + return -ERANGE; + } + + for (i = (len - 1); i > 0; i--) { + if (duration_time[i] > duration_time[i-1]) + continue; + else + break; + + } + + if (i > 0) { + aw_err("duration time range error"); + ret = -ERANGE; + } + + return ret; +} + +static int ram_config(struct aw_haptic *aw_haptic, int duration) +{ + uint8_t wavseq = 0; + uint8_t wavloop = 0; + int ret = 0; + + aw_info("enter"); + ret = analyse_duration_range(aw_haptic); + if (ret < 0) + return ret; + + if ((duration > 0) && (duration < aw_haptic->info.duration_time[0])) { + wavseq = 3; + aw_haptic->activate_mode = AW_RAM_MODE; + } else if ((duration >= aw_haptic->info.duration_time[0]) && + (duration < aw_haptic->info.duration_time[1])) { + wavseq = 2; + aw_haptic->activate_mode = AW_RAM_MODE; + } else if ((duration >= aw_haptic->info.duration_time[1]) && + (duration < aw_haptic->info.duration_time[2])) { + wavseq = 1; + aw_haptic->activate_mode = AW_RAM_MODE; + } else if (duration >= aw_haptic->info.duration_time[2]) { + wavseq = 4; + wavloop = 15; + aw_haptic->activate_mode = AW_RAM_LOOP_MODE; + } else { + aw_err("duration time error, duration= %d", duration); + aw_haptic->activate_mode = AW_NULL; + return -ERANGE; + } + aw_info("duration %d, select index %d", aw_haptic->duration, wavseq); + aw_haptic->func->set_wav_seq(aw_haptic, 0, wavseq); + aw_haptic->func->set_wav_loop(aw_haptic, 0, wavloop); + aw_haptic->func->set_wav_seq(aw_haptic, 1, 0); + aw_haptic->func->set_wav_loop(aw_haptic, 1, 0); + return 0; +} +#endif + +static void upload_lra(struct aw_haptic *aw_haptic, uint8_t flag) +{ + uint8_t reg_val; + + switch (flag) { + case AW_WRITE_ZERO: + aw_info("write zero to trim_lra!"); + reg_val = 0x00; + break; + case AW_F0_CALI_LRA: + aw_info("write f0_cali_data to trim_lra = 0x%02X", + aw_haptic->f0_cali_data); + reg_val = (uint8_t)aw_haptic->f0_cali_data; + break; + case AW_OSC_CALI_LRA: + aw_info("write osc_cali_data to trim_lra = 0x%02X", + aw_haptic->osc_cali_data); + reg_val = (uint8_t)aw_haptic->osc_cali_data; + break; + default: + aw_err("flag is error"); + reg_val = 0x00; + break; + } + aw_haptic->func->set_trim_lra(aw_haptic, reg_val); +} + +static int judge_within_cali_range(struct aw_haptic *aw_haptic) +{ + uint32_t f0_cali_min = 0; + uint32_t f0_cali_max = 0; + + aw_info("enter"); + f0_cali_min = aw_haptic->info.f0_pre * + (100 - aw_haptic->info.f0_cali_percent) / 100; + f0_cali_max = aw_haptic->info.f0_pre * + (100 + aw_haptic->info.f0_cali_percent) / 100; + + aw_info("f0_pre = %d, f0_cali_min = %d, f0_cali_max = %d, f0 = %d", + aw_haptic->info.f0_pre, f0_cali_min, f0_cali_max, + aw_haptic->f0); + + if (aw_haptic->f0 < f0_cali_min) { + aw_err("lra f0 is too small, lra_f0 = %d!", aw_haptic->f0); + aw_haptic->f0_cali_data = AW_F0_CALI_DATA_MIN; + upload_lra(aw_haptic, AW_F0_CALI_LRA); + return -ERANGE; + } + + if (aw_haptic->f0 > f0_cali_max) { + aw_err("lra f0 is too large, lra_f0 = %d!", aw_haptic->f0); + aw_haptic->f0_cali_data = AW_F0_CALI_DATA_MAX; + upload_lra(aw_haptic, AW_F0_CALI_LRA); + return -ERANGE; + } + return 0; +} + +static int f0_cali(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + aw_info("enter"); + upload_lra(aw_haptic, AW_WRITE_ZERO); + if (aw_haptic->func->get_f0(aw_haptic)) { + aw_err("get f0 failed"); + } else { + /* max and min limit */ + ret = judge_within_cali_range(aw_haptic); + if (ret < 0) + return -ERANGE; + /* calculate cali step */ + aw_haptic->func->calculate_cali_data(aw_haptic); + upload_lra(aw_haptic, AW_F0_CALI_LRA); + } + aw_haptic->func->play_stop(aw_haptic); + return ret; +} + +static void ram_vbat_comp(struct aw_haptic *aw_haptic, bool flag) +{ + int temp_gain = 0; + + aw_info("enter"); + if (flag) { + if (aw_haptic->ram_vbat_comp == AW_RAM_VBAT_COMP_ENABLE) { + aw_haptic->func->get_vbat(aw_haptic); + temp_gain = aw_haptic->gain * AW_VBAT_REFER / + aw_haptic->vbat; + if (temp_gain > + (AW_DEFAULT_GAIN * AW_VBAT_REFER / AW_VBAT_MIN)) { + temp_gain = AW_DEFAULT_GAIN * AW_VBAT_REFER / + AW_VBAT_MIN; + aw_info("gain limit=%d", temp_gain); + } + aw_haptic->func->set_gain(aw_haptic, temp_gain); + aw_info("ram vbat comp open, set_gain 0x%02X", + temp_gain); + } else { + aw_haptic->func->set_gain(aw_haptic, aw_haptic->gain); + aw_info("ram vbat comp close, set_gain 0x%02X", + aw_haptic->gain); + } + } else { + aw_haptic->func->set_gain(aw_haptic, aw_haptic->gain); + aw_info("ram vbat comp close, set_gain 0x%02X", + aw_haptic->gain); + } +} + +static int osc_calculation_time(struct aw_haptic *aw_haptic) +{ + uint32_t base_addr = aw_haptic->ram.base_addr; + uint32_t buf_len = 0; + struct aw_haptic_container *aw_rtp = aw_haptic->aw_rtp; + + if (!aw_haptic->func->rtp_get_fifo_afs(aw_haptic)) { + aw_dbg("haptic_rtp_get_fifo_afi, rtp_cnt= %d", + aw_haptic->rtp_cnt); + + mutex_lock(&aw_haptic->rtp_lock); + pm_qos_enable(aw_haptic, true); + if ((aw_rtp->len - aw_haptic->rtp_cnt) < (base_addr >> 2)) + buf_len = aw_rtp->len - aw_haptic->rtp_cnt; + else + buf_len = (base_addr >> 2); + + if (aw_haptic->rtp_cnt != aw_rtp->len) { + if (aw_haptic->timeval_flags == 1) { + aw_haptic->kstart = ktime_get(); + aw_haptic->timeval_flags = 0; + } + aw_haptic->func->set_rtp_data(aw_haptic, + &aw_rtp->data[aw_haptic->rtp_cnt], buf_len); + aw_haptic->rtp_cnt += buf_len; + } + pm_qos_enable(aw_haptic, false); + mutex_unlock(&aw_haptic->rtp_lock); + + } + + if (aw_haptic->func->get_osc_status(aw_haptic)) { + aw_haptic->kend = ktime_get(); + aw_info("osc trim playback done aw_haptic->rtp_cnt= %d", + aw_haptic->rtp_cnt); + return 0; + } + aw_haptic->kend = ktime_get(); + aw_haptic->microsecond = ktime_to_us(ktime_sub(aw_haptic->kend, + aw_haptic->kstart)); + if (aw_haptic->microsecond > AW_OSC_CALI_MAX_LENGTH) { + aw_info("osc trim time out! aw_haptic->rtp_cnt %d", + aw_haptic->rtp_cnt); + return 0; + } + return -ERANGE; +} + +static int rtp_fw_load(struct aw_haptic *aw_haptic, uint32_t file_num) +{ + int ret = 0; + const struct firmware *rtp_file; + + ret = request_firmware(&rtp_file, aw_rtp_name[file_num], + aw_haptic->dev); + if (ret < 0) { + aw_err("failed to read %s", aw_rtp_name[file_num]); + return ret; + } + + vfree(aw_haptic->aw_rtp); + aw_haptic->aw_rtp = NULL; + aw_haptic->aw_rtp = vmalloc(rtp_file->size + sizeof(int)); + if (!aw_haptic->aw_rtp) { + release_firmware(rtp_file); + aw_err("error allocating memory"); + return -ENOMEM; + } + aw_haptic->aw_rtp->len = rtp_file->size; + aw_haptic->rtp_len = rtp_file->size; + aw_info("rtp file:[%s] size = %dbytes", aw_rtp_name[file_num], + aw_haptic->aw_rtp->len); + memcpy(aw_haptic->aw_rtp->data, rtp_file->data, rtp_file->size); + release_firmware(rtp_file); + return 0; +} + +static int rtp_osc_cali(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + aw_info("enter"); + /*aw_haptic add stop,for irq interrupt during calibrate */ + aw_haptic->func->play_stop(aw_haptic); + + mutex_lock(&aw_haptic->rtp_lock); + ret = rtp_fw_load(aw_haptic, 0); + if (ret < 0) { + mutex_unlock(&aw_haptic->rtp_lock); + return ret; + } + mutex_unlock(&aw_haptic->rtp_lock); + + aw_haptic->rtp_init = false; + aw_haptic->rtp_cnt = 0; + aw_haptic->timeval_flags = 1; + + ram_vbat_comp(aw_haptic, false); + aw_haptic->func->play_mode(aw_haptic, AW_RTP_MODE); + disable_irq(gpio_to_irq(aw_haptic->irq_gpio)); + aw_haptic->func->haptic_start(aw_haptic); + while (1) { + ret = osc_calculation_time(aw_haptic); + if (!ret) + break; + } + enable_irq(gpio_to_irq(aw_haptic->irq_gpio)); + aw_haptic->microsecond = ktime_to_us(ktime_sub(aw_haptic->kend, + aw_haptic->kstart)); + /*calibration osc */ + aw_info("microsecond: %llu", aw_haptic->microsecond); + return 0; +} + +static void rtp_trim_lra_cali(struct aw_haptic *aw_haptic) +{ + uint32_t lra_trim_code = 0; + /*0.1 percent below no need to calibrate */ + uint32_t osc_cali_threshold = 10; + uint32_t code = 0; + unsigned long real_time = 0; + unsigned long theory_time = 0; + + upload_lra(aw_haptic, AW_WRITE_ZERO); + if (rtp_osc_cali(aw_haptic)) + return; + real_time = aw_haptic->microsecond; + theory_time = aw_haptic->func->get_theory_time(aw_haptic); + if (theory_time == real_time) { + aw_info("theory_time == real_time: %ld, no need to calibrate!", + real_time); + return; + } else if (theory_time < real_time) { + if ((real_time - theory_time) > + (theory_time / AW_OSC_TRIM_PARAM)) { + aw_info("(real_time - theory_time) > (theory_time/50), can't calibrate!"); + return; + } + + if ((real_time - theory_time) < + (osc_cali_threshold * theory_time / 10000)) { + aw_info("real_time: %ld, theory_time: %ld, no need to calibrate!", + real_time, theory_time); + return; + } + + code = ((real_time - theory_time) * 4) / (theory_time / 1000); + code = ((code % 10 < 5) ? 0 : 1) + code / 10; + code = 32 + code; + } else if (theory_time > real_time) { + if ((theory_time - real_time) > + (theory_time / AW_OSC_TRIM_PARAM)) { + aw_info("(theory_time - real_time) > (theory_time / 50), can't calibrate!"); + return; + } + if ((theory_time - real_time) < + (osc_cali_threshold * theory_time / 10000)) { + aw_info("real_time: %ld, theory_time: %ld, no need to calibrate!", + real_time, theory_time); + return; + } + + code = ((theory_time - real_time) * 4) / (theory_time / 1000); + code = ((code % 10 < 5) ? 0 : 1) + code / 10; + code = 32 - code; + } + if (code > 31) + lra_trim_code = code - 32; + else + lra_trim_code = code + 32; + + aw_haptic->osc_cali_data = lra_trim_code; + upload_lra(aw_haptic, AW_OSC_CALI_LRA); + aw_info("real_time: %ld, theory_time: %ld", real_time, theory_time); + aw_info("code: %d, trim_lra: 0x%02X", code, lra_trim_code); +} + +static int parse_awrw_data(struct aw_haptic *aw_haptic, const char *buf) +{ + uint8_t i = 0; + uint8_t reg_num = aw_haptic->i2c_info.reg_num; + const char *temp_buf = NULL; + char data_buf[AWRW_CMD_UNIT] = { 0 }; + uint32_t value = 0; + int ret = 0; + int len = strlen(buf) - (AWRW_CMD_UNIT * 3); + + if (len < 0) { + aw_err("parse data error"); + return -ERANGE; + } + temp_buf = &buf[AWRW_CMD_UNIT * 3]; + for (i = 0; i < reg_num; i++) { + if (((i + 1) * AWRW_CMD_UNIT) > len) { + aw_err("parse data error"); + return -ERANGE; + } + memcpy(data_buf, &temp_buf[i * AWRW_CMD_UNIT], 4); + data_buf[4] = '\0'; + ret = kstrtouint(data_buf, 0, &value); + if (ret < 0) { + aw_err("kstrtouint error"); + return ret; + } + aw_haptic->i2c_info.reg_data[i] = (uint8_t)value; + } + return 0; +} + +static int haptic_audio_ctr_list_insert(struct aw_haptic *aw_haptic, + struct aw_haptic_ctr *haptic_ctr) +{ + struct aw_haptic_ctr *p_new = NULL; + struct aw_haptic_audio *haptic_audio = &aw_haptic->haptic_audio; + + aw_info("enter"); + p_new = kzalloc(sizeof(struct aw_haptic_ctr), GFP_KERNEL); + if (p_new == NULL) + return -ENOMEM; + + /* update new list info */ + p_new->cnt = haptic_ctr->cnt; + p_new->cmd = haptic_ctr->cmd; + p_new->play = haptic_ctr->play; + p_new->wavseq = haptic_ctr->wavseq; + p_new->loop = haptic_ctr->loop; + p_new->gain = haptic_ctr->gain; + INIT_LIST_HEAD(&(p_new->list)); + list_add(&(p_new->list), &(haptic_audio->ctr_list)); + aw_info("exit"); + return 0; +} + +static void haptic_audio_ctrl_list_clr(struct aw_haptic_audio *haptic_audio) +{ + struct aw_haptic_ctr *p_ctr = NULL; + struct aw_haptic_ctr *p_ctr_bak = NULL; + + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + list_del(&p_ctr->list); + kfree(p_ctr); + p_ctr = NULL; + } +} + +static void haptic_audio_init(struct aw_haptic *aw_haptic) +{ + aw_info("enter!"); + aw_haptic->func->set_wav_seq(aw_haptic, 0x01, 0x00); +} + +static void haptic_audio_off(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->set_gain(aw_haptic, 0x80); + aw_haptic->func->play_stop(aw_haptic); + haptic_audio_ctrl_list_clr(&aw_haptic->haptic_audio); + mutex_unlock(&aw_haptic->lock); +} + +static void haptic_ctr_list_config(struct aw_haptic *aw_haptic, + struct aw_haptic_ctr *p_ctr, + struct aw_haptic_ctr *p_ctr_bak, + struct aw_haptic_audio *haptic_audio) +{ + uint32_t list_input_cnt = 0; + uint32_t list_output_cnt = 0; + uint32_t list_diff_cnt = 0; + uint32_t list_del_cnt = 0; + + list_for_each_entry_safe(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + list_input_cnt = p_ctr->cnt; + break; + } + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + list_output_cnt = p_ctr->cnt; + break; + } + + if (list_input_cnt > list_output_cnt) + list_diff_cnt = list_input_cnt - list_output_cnt; + + if (list_input_cnt < list_output_cnt) + list_diff_cnt = 32 + list_input_cnt - list_output_cnt; + + if (list_diff_cnt > 2) { + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + if ((p_ctr->play == 0) && + (AW_CMD_ENABLE == (AW_CMD_ENABLE & p_ctr->cmd))) { + list_del(&p_ctr->list); + kfree(p_ctr); + p_ctr = NULL; + list_del_cnt++; + } + if (list_del_cnt == list_diff_cnt) + break; + } + } +} + +static void parse_haptic_audio_data(struct aw_haptic *aw_haptic) +{ + bool ctr_list_flag = false; + struct aw_haptic_ctr *p_ctr = NULL; + struct aw_haptic_ctr *p_ctr_bak = NULL; + struct aw_haptic_audio *haptic_audio = &(aw_haptic->haptic_audio); + + memset(&(haptic_audio->ctr), 0, sizeof(struct aw_haptic_ctr)); + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + ctr_list_flag = true; + break; + } + + if (ctr_list_flag == false) + aw_dbg("ctr list empty"); + if (ctr_list_flag == true) + haptic_ctr_list_config(aw_haptic, p_ctr, p_ctr_bak, + haptic_audio); + + /* get the last data from list */ + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + haptic_audio->ctr.cnt = p_ctr->cnt; + haptic_audio->ctr.cmd = p_ctr->cmd; + haptic_audio->ctr.play = p_ctr->play; + haptic_audio->ctr.wavseq = p_ctr->wavseq; + haptic_audio->ctr.loop = p_ctr->loop; + haptic_audio->ctr.gain = p_ctr->gain; + list_del(&p_ctr->list); + kfree(p_ctr); + p_ctr = NULL; + break; + } + + if (haptic_audio->ctr.play) { + aw_info("cnt=%d, cmd=%d, play=%d, wavseq=%d, loop=%d, gain=%d", + haptic_audio->ctr.cnt, haptic_audio->ctr.cmd, + haptic_audio->ctr.play, haptic_audio->ctr.wavseq, + haptic_audio->ctr.loop, haptic_audio->ctr.gain); + } + +} + +static void haptic_audio_work_routine(struct work_struct *work) +{ + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, + haptic_audio.work); + + int rtp_is_going_on = 0; + + aw_dbg("enter"); + + mutex_lock(&aw_haptic->haptic_audio.lock); + parse_haptic_audio_data(aw_haptic); + /* rtp mode jump */ + rtp_is_going_on = aw_haptic->func->judge_rtp_going(aw_haptic); + if (rtp_is_going_on) { + mutex_unlock(&aw_haptic->haptic_audio.lock); + return; + } + mutex_unlock(&aw_haptic->haptic_audio.lock); + + if ((AW_CMD_HAPTIC & aw_haptic->haptic_audio.ctr.cmd) == + AW_CMD_ENABLE) { + if (aw_haptic->haptic_audio.ctr.play == AW_PLAY_ENABLE) { + aw_info("haptic audio play start"); + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + aw_haptic->func->play_mode(aw_haptic, AW_RAM_MODE); + aw_haptic->func->set_wav_seq(aw_haptic, 0x00, + aw_haptic->haptic_audio.ctr.wavseq); + aw_haptic->func->set_wav_loop(aw_haptic, 0x00, + aw_haptic->haptic_audio.ctr.loop); + aw_haptic->func->set_gain(aw_haptic, + aw_haptic->haptic_audio.ctr.gain); + aw_haptic->func->haptic_start(aw_haptic); + mutex_unlock(&aw_haptic->lock); + + } else if (aw_haptic->haptic_audio.ctr.play == AW_PLAY_STOP) { + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + mutex_unlock(&aw_haptic->lock); + + } else if (aw_haptic->haptic_audio.ctr.play == AW_PLAY_GAIN) { + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->set_gain(aw_haptic, + aw_haptic->haptic_audio.ctr.gain); + mutex_unlock(&aw_haptic->lock); + } + } +} + +static enum hrtimer_restart haptic_audio_timer_func(struct hrtimer *timer) +{ + struct aw_haptic *aw_haptic = container_of(timer, struct aw_haptic, + haptic_audio.timer); + int time = aw_haptic->haptic_audio.timer_val; + + schedule_work(&aw_haptic->haptic_audio.work); + hrtimer_start(&aw_haptic->haptic_audio.timer, + ktime_set(time / 1000000, (time % 1000000) * 1000), + HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +static ssize_t state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", aw_haptic->state); +} + +static ssize_t state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t duration_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ktime_t time_rem; + s64 time_ms = 0; + + if (hrtimer_active(&aw_haptic->timer)) { + time_rem = hrtimer_get_remaining(&aw_haptic->timer); + time_ms = ktime_to_ms(time_rem); + } + + return snprintf(buf, PAGE_SIZE, "%lld\n", time_ms); +} + +static ssize_t duration_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + /* setting 0 on duration is NOP for now */ + if (val <= 0) + return count; + + aw_haptic->duration = val; + aw_info("duration=%d", aw_haptic->duration); + return count; +} + +static ssize_t activate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + /* For now nothing to show */ + return snprintf(buf, PAGE_SIZE, "activate = %d\n", aw_haptic->state); +} + +static ssize_t activate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc = 0; + uint32_t val = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + if (val != 0 && val != 1) + return count; + + aw_info("value=%d", val); + + mutex_lock(&aw_haptic->lock); + hrtimer_cancel(&aw_haptic->timer); + aw_haptic->state = val; + aw_haptic->activate_mode = aw_haptic->info.mode; + mutex_unlock(&aw_haptic->lock); + + schedule_work(&aw_haptic->vibrator_work); + return count; +} + +static ssize_t activate_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", aw_haptic->activate_mode); +} + +static ssize_t activate_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + mutex_lock(&aw_haptic->lock); + aw_haptic->activate_mode = val; + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + aw_haptic->func->get_wav_seq(aw_haptic, 1); + aw_haptic->index = aw_haptic->seq[0]; + return snprintf(buf, PAGE_SIZE, "index = %d\n", aw_haptic->index); +} + +static ssize_t index_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + if (val > aw_haptic->ram.ram_num) { + aw_err("input value out of range!"); + return count; + } + + aw_info("value=%d", val); + + mutex_lock(&aw_haptic->lock); + aw_haptic->index = val; + aw_haptic->func->set_repeat_seq(aw_haptic, aw_haptic->index); + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t gain_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint8_t gain = 0; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->get_gain(aw_haptic, &gain); + mutex_unlock(&aw_haptic->lock); + + return snprintf(buf, PAGE_SIZE, "gain = 0x%02X\n", gain); +} + +static ssize_t gain_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + aw_info("value=0x%02X", val); + + mutex_lock(&aw_haptic->lock); + aw_haptic->gain = val; + aw_haptic->func->set_gain(aw_haptic, aw_haptic->gain); + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t seq_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + uint8_t i = 0; + size_t count = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + mutex_lock(&aw_haptic->lock); + aw_haptic->func->get_wav_seq(aw_haptic, AW_SEQUENCER_SIZE); + for (i = 0; i < AW_SEQUENCER_SIZE; i++) + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d = %d\n", i + 1, aw_haptic->seq[i]); + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t seq_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%X %X", &databuf[0], &databuf[1]) == 2) { + if (databuf[0] >= AW_SEQUENCER_SIZE || + databuf[1] > aw_haptic->ram.ram_num) { + aw_err("input value out of range!"); + return count; + } + aw_info("seq%d=0x%02X", databuf[0], databuf[1]); + + mutex_lock(&aw_haptic->lock); + aw_haptic->seq[databuf[0]] = (uint8_t)databuf[1]; + aw_haptic->func->set_wav_seq(aw_haptic, (uint8_t)databuf[0], + aw_haptic->seq[databuf[0]]); + mutex_unlock(&aw_haptic->lock); + } + return count; +} + +static ssize_t loop_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + uint8_t i = 0; + size_t count = 0; + cdev_t *cdev = dev_get_drvdata(dev); + uint8_t reg_val[AW_SEQUENCER_LOOP_SIZE] = {0}; + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + mutex_lock(&aw_haptic->lock); + aw_haptic->func->get_wav_loop(aw_haptic, reg_val); + for (i = 0; i < AW_SEQUENCER_LOOP_SIZE; i++) { + aw_haptic->loop[i * 2 + 0] = (reg_val[i] >> 4) & 0x0F; + aw_haptic->loop[i * 2 + 1] = (reg_val[i] >> 0) & 0x0F; + + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d loop: 0x%02X\n", i * 2 + 1, + aw_haptic->loop[i * 2 + 0]); + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d loop: 0x%02X\n", i * 2 + 2, + aw_haptic->loop[i * 2 + 1]); + } + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t loop_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_info("seq%d loop=0x%02X", databuf[0], databuf[1]); + + mutex_lock(&aw_haptic->lock); + aw_haptic->loop[databuf[0]] = (uint8_t)databuf[1]; + aw_haptic->func->set_wav_loop(aw_haptic, (uint8_t)databuf[0], + aw_haptic->loop[databuf[0]]); + mutex_unlock(&aw_haptic->lock); + } + + return count; +} + +static ssize_t reg_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + len = aw_haptic->func->get_reg(aw_haptic, len, buf); + mutex_unlock(&aw_haptic->lock); + + return len; +} + +static ssize_t reg_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t val = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%X %X", &databuf[0], &databuf[1]) == 2) { + val = (uint8_t)databuf[1]; + + mutex_lock(&aw_haptic->lock); + haptic_nv_i2c_writes(aw_haptic, (uint8_t)databuf[0], &val, + AW_I2C_BYTE_ONE); + mutex_unlock(&aw_haptic->lock); + } + return count; +} + +static ssize_t ram_update_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + int i = 0; + int j = 0; + int size = 0; + ssize_t len = 0; + uint8_t ram_data[AW_RAMDATA_RD_BUFFER_SIZE] = {0}; + uint8_t buffer[AW_RAMDATA_SHOW_LINE_BUFFER_SZIE] = {0}; + uint8_t unit[AW_RAMDATA_SHOW_UINT_SIZE] = {0}; + + mutex_lock(&aw_haptic->lock); + len += snprintf(buf + len, PAGE_SIZE - len, "Please cat log\n"); + /* RAMINIT Enable */ + aw_haptic->func->ram_init(aw_haptic, true); + aw_haptic->func->play_stop(aw_haptic); + aw_haptic->func->set_ram_addr(aw_haptic); + while (i < aw_haptic->ram.len) { + if ((aw_haptic->ram.len - i) < AW_RAMDATA_RD_BUFFER_SIZE) + size = aw_haptic->ram.len - i; + else + size = AW_RAMDATA_RD_BUFFER_SIZE; + + aw_haptic->func->get_ram_data(aw_haptic, ram_data, size); + for (j = 0; j < size; j++) { + sprintf(unit, "0x%02X ", ram_data[j]); + strcat(buffer, unit); + if ((j + 1) % AW_RAMDATA_SHOW_COLUMN == 0 || j == size - 1) { + aw_info("[ram_data]:%s", buffer); + memset(buffer, 0, + AW_RAMDATA_SHOW_LINE_BUFFER_SZIE); + } + } + i += size; + } + /* RAMINIT Disable */ + aw_haptic->func->ram_init(aw_haptic, false); + mutex_unlock(&aw_haptic->lock); + + return len; +} + +static ssize_t ram_update_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + if (val) + ram_update(aw_haptic); + return count; +} + +static ssize_t f0_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + upload_lra(aw_haptic, AW_WRITE_ZERO); + aw_haptic->func->get_f0(aw_haptic); + upload_lra(aw_haptic, AW_F0_CALI_LRA); + mutex_unlock(&aw_haptic->lock); + + len += snprintf(buf + len, PAGE_SIZE - len, "%d\n", aw_haptic->f0); + return len; +} + +static ssize_t f0_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + + +static ssize_t cali_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + upload_lra(aw_haptic, AW_F0_CALI_LRA); + aw_haptic->func->get_f0(aw_haptic); + mutex_unlock(&aw_haptic->lock); + + len += snprintf(buf + len, PAGE_SIZE - len, "%d\n", aw_haptic->f0); + return len; +} + +static ssize_t cali_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + if (val) { + + mutex_lock(&aw_haptic->lock); + f0_cali(aw_haptic); + mutex_unlock(&aw_haptic->lock); + } + return count; +} + + +static ssize_t f0_save_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "f0_cali_data = 0x%02X\n", + aw_haptic->f0_cali_data); + + return len; +} + +static ssize_t f0_save_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + aw_haptic->f0_cali_data = val; + return count; +} + +static ssize_t cont_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->read_cont_f0(aw_haptic); + len += snprintf(buf + len, PAGE_SIZE - len, "%d\n", aw_haptic->cont_f0); + mutex_unlock(&aw_haptic->lock); + + return len; +} + +static ssize_t cont_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + if (val) + aw_haptic->func->cont_config(aw_haptic); + mutex_unlock(&aw_haptic->lock); + + return count; +} + + +static ssize_t vbat_monitor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->get_vbat(aw_haptic); + len += snprintf(buf + len, PAGE_SIZE - len, "vbat_monitor = %d\n", + aw_haptic->vbat); + mutex_unlock(&aw_haptic->lock); + + return len; +} + +static ssize_t vbat_monitor_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + + +static ssize_t lra_resistance_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->get_lra_resistance(aw_haptic); + len += snprintf(buf + len, PAGE_SIZE - len, "lra_resistance = %d\n", + aw_haptic->lra); + mutex_unlock(&aw_haptic->lock); + + return len; +} + +static ssize_t lra_resistance_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t prctmode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + uint8_t reg_val = 0; + + mutex_lock(&aw_haptic->lock); + reg_val = aw_haptic->func->get_prctmode(aw_haptic); + len += snprintf(buf + len, PAGE_SIZE - len, "prctmode = %d\n", reg_val); + mutex_unlock(&aw_haptic->lock); + + return len; +} + +static ssize_t prctmode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t databuf[2] = { 0, 0 }; + uint32_t prtime = 0; + uint32_t prlvl = 0; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + prtime = databuf[0]; + prlvl = databuf[1]; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->protect_config(aw_haptic, prtime, prlvl); + mutex_unlock(&aw_haptic->lock); + + } + return count; +} + +static ssize_t ram_vbat_comp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "ram_vbat_comp = %d\n", + aw_haptic->ram_vbat_comp); + + return len; +} + +static ssize_t ram_vbat_comp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw_haptic->lock); + if (val) + aw_haptic->ram_vbat_comp = AW_RAM_VBAT_COMP_ENABLE; + else + aw_haptic->ram_vbat_comp = AW_RAM_VBAT_COMP_DISABLE; + mutex_unlock(&aw_haptic->lock); + + return count; +} + + +static ssize_t ram_num_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + get_ram_num(aw_haptic); + len += snprintf(buf + len, PAGE_SIZE - len, + "ram_num = %d\n", aw_haptic->ram.ram_num); + return len; +} + + +static ssize_t awrw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint8_t reg_num = aw_haptic->i2c_info.reg_num; + uint8_t flag = aw_haptic->i2c_info.flag; + uint8_t *reg_data = aw_haptic->i2c_info.reg_data; + uint8_t i = 0; + ssize_t len = 0; + + if (!reg_num) { + aw_err("awrw parameter error"); + return len; + } + if (flag == AW_READ) { + for (i = 0; i < reg_num; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%02X,", reg_data[i]); + } + + len += snprintf(buf + len - 1, PAGE_SIZE - len, "\n"); + } + + return len; +} + +static ssize_t awrw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t datatype[3] = { 0 }; + int ret = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (sscanf(buf, "%x %x %x", &datatype[0], &datatype[1], + &datatype[2]) == 3) { + if (!datatype[1]) { + aw_err("awrw parameter error"); + return count; + } + aw_haptic->i2c_info.flag = (uint8_t)datatype[0]; + aw_haptic->i2c_info.reg_num = (uint8_t)datatype[1]; + aw_haptic->i2c_info.first_addr = (uint8_t)datatype[2]; + + mutex_lock(&aw_haptic->lock); + if (aw_haptic->i2c_info.flag == AW_WRITE) { + ret = parse_awrw_data(aw_haptic, buf); + if (ret < 0) { + mutex_unlock(&aw_haptic->lock); + return count; + } + haptic_nv_i2c_writes(aw_haptic, + aw_haptic->i2c_info.first_addr, + aw_haptic->i2c_info.reg_data, + aw_haptic->i2c_info.reg_num); + } else if (aw_haptic->i2c_info.flag == AW_READ) { + haptic_nv_i2c_reads(aw_haptic, + aw_haptic->i2c_info.first_addr, + aw_haptic->i2c_info.reg_data, + aw_haptic->i2c_info.reg_num); + } else { + aw_err("flag is error"); + } + mutex_unlock(&aw_haptic->lock); + + } else + aw_err("missing number of parameters"); + + return count; +} + +static ssize_t gun_type_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + return snprintf(buf, PAGE_SIZE, "0x%02X\n", aw_haptic->gun_type); +} + +static ssize_t gun_type_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + aw_info("value=%d", val); + + mutex_lock(&aw_haptic->lock); + aw_haptic->gun_type = val; + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t bullet_nr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + return snprintf(buf, PAGE_SIZE, "0x%02X\n", aw_haptic->bullet_nr); +} + +static ssize_t bullet_nr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + aw_info("value=%d", val); + + mutex_lock(&aw_haptic->lock); + aw_haptic->bullet_nr = val; + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t haptic_audio_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "haptic_audio.delay_val=%dus\n", + aw_haptic->haptic_audio.delay_val); + len += snprintf(buf + len, PAGE_SIZE - len, + "haptic_audio.timer_val=%dus\n", + aw_haptic->haptic_audio.timer_val); + return len; +} + +static ssize_t haptic_audio_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t databuf[2] = { 0 }; + + if (sscanf(buf, "%d %d", &databuf[0], &databuf[1]) == 2) { + aw_haptic->haptic_audio.delay_val = databuf[0]; + aw_haptic->haptic_audio.timer_val = databuf[1]; + } + return count; +} + +static ssize_t haptic_audio_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", + aw_haptic->haptic_audio.ctr.cnt); + return len; +} + +static int haptic_audio_config(struct aw_haptic *aw_haptic, uint32_t databuf[]) +{ + struct aw_haptic_ctr *hap_ctr = NULL; + int delay = aw_haptic->haptic_audio.delay_val; + + aw_info("enter"); + hap_ctr = kzalloc(sizeof(struct aw_haptic_ctr), GFP_KERNEL); + if (hap_ctr == NULL) + return -ENOMEM; + + mutex_lock(&aw_haptic->haptic_audio.lock); + hap_ctr->cnt = (uint8_t)databuf[0]; + hap_ctr->cmd = (uint8_t)databuf[1]; + hap_ctr->play = (uint8_t)databuf[2]; + hap_ctr->wavseq = (uint8_t)databuf[3]; + hap_ctr->loop = (uint8_t)databuf[4]; + hap_ctr->gain = (uint8_t)databuf[5]; + + haptic_audio_ctr_list_insert(aw_haptic, hap_ctr); + + if (hap_ctr->cmd == 0xff) { + aw_info("haptic_audio stop"); + if (hrtimer_active(&aw_haptic->haptic_audio.timer)) { + aw_info("cancel haptic_audio_timer"); + hrtimer_cancel(&aw_haptic->haptic_audio.timer); + aw_haptic->haptic_audio.ctr.cnt = 0; + haptic_audio_off(aw_haptic); + } + } else { + if (hrtimer_active(&aw_haptic->haptic_audio.timer)) { + /* */ + } else { + aw_info("start haptic_audio_timer"); + haptic_audio_init(aw_haptic); + hrtimer_start(&aw_haptic->haptic_audio.timer, + ktime_set(delay / 1000000, + (delay % 1000000) * 1000), + HRTIMER_MODE_REL); + } + } + kfree(hap_ctr); + mutex_unlock(&aw_haptic->haptic_audio.lock); + return 0; +} + +static ssize_t haptic_audio_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t databuf[6] = { 0 }; + int rtp_is_going_on = 0; + + rtp_is_going_on = aw_haptic->func->judge_rtp_going(aw_haptic); + + if (rtp_is_going_on) { + aw_info("RTP is runing, stop audio haptic"); + return count; + } + + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + + if (sscanf(buf, "%d %d %d %d %d %d", &databuf[0], &databuf[1], + &databuf[2], &databuf[3], &databuf[4], &databuf[5]) == 6) { + if (databuf[2]) { + aw_info("cnt=%d, cmd=%d, play=%d", + databuf[0], databuf[1], databuf[2]); + aw_info("wavseq=%d, loop=%d, gain=%d", + databuf[3], databuf[4], databuf[5]); + haptic_audio_config(aw_haptic, databuf); + } + } + return count; +} + +static DEVICE_ATTR(aw_state, 0664, state_show, state_store); +static DEVICE_ATTR(reg, 0664, reg_show, reg_store); +static DEVICE_ATTR(aw_duration, 0664, duration_show, duration_store); +static DEVICE_ATTR(aw_activate, 0644, activate_show, activate_store); +static DEVICE_ATTR(activate_mode, 0644, activate_mode_show, + activate_mode_store); +static DEVICE_ATTR(index, 0644, index_show, index_store); +static DEVICE_ATTR(gain, 0644, gain_show, gain_store); +static DEVICE_ATTR(seq, 0644, seq_show, seq_store); +static DEVICE_ATTR(loop, 0644, loop_show, loop_store); +static DEVICE_ATTR(ram_update, 0644, ram_update_show, ram_update_store); +static DEVICE_ATTR(f0, 0644, f0_show, f0_store); +static DEVICE_ATTR(cali, 0644, cali_show, cali_store); +static DEVICE_ATTR(f0_save, 0644, f0_save_show, f0_save_store); +static DEVICE_ATTR(cont, 0644, cont_show, cont_store); +static DEVICE_ATTR(vbat_monitor, 0644, vbat_monitor_show, vbat_monitor_store); +static DEVICE_ATTR(lra_resistance, 0644, lra_resistance_show, + lra_resistance_store); +static DEVICE_ATTR(prctmode, 0644, prctmode_show, prctmode_store); +static DEVICE_ATTR(ram_vbat_comp, 0644, ram_vbat_comp_show, + ram_vbat_comp_store); +static DEVICE_ATTR(ram_num, 0664, ram_num_show, NULL); +static DEVICE_ATTR(awrw, 0664, awrw_show, awrw_store); +static DEVICE_ATTR(gun_type, 0664, gun_type_show, gun_type_store); +static DEVICE_ATTR(bullet_nr, 0664, bullet_nr_show, bullet_nr_store); +static DEVICE_ATTR(haptic_audio_time, 0664, haptic_audio_time_show, + haptic_audio_time_store); +static DEVICE_ATTR(haptic_audio, 0664, haptic_audio_show, haptic_audio_store); +static struct attribute *vibrator_attributes[] = { + &dev_attr_aw_state.attr, + &dev_attr_aw_duration.attr, + &dev_attr_aw_activate.attr, + &dev_attr_activate_mode.attr, + &dev_attr_index.attr, + &dev_attr_gain.attr, + &dev_attr_seq.attr, + &dev_attr_loop.attr, + &dev_attr_reg.attr, + &dev_attr_ram_update.attr, + &dev_attr_f0.attr, + &dev_attr_cali.attr, + &dev_attr_f0_save.attr, + &dev_attr_cont.attr, + &dev_attr_vbat_monitor.attr, + &dev_attr_lra_resistance.attr, + &dev_attr_prctmode.attr, + &dev_attr_ram_vbat_comp.attr, + &dev_attr_ram_num.attr, + &dev_attr_awrw.attr, + &dev_attr_gun_type.attr, + &dev_attr_bullet_nr.attr, + &dev_attr_haptic_audio_time.attr, + &dev_attr_haptic_audio.attr, + NULL +}; + +static struct attribute_group vibrator_attribute_group = { + .attrs = vibrator_attributes +}; + +static ssize_t rtp_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "rtp_cnt = %d", + aw_haptic->rtp_cnt); + return len; +} + +static ssize_t rtp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + uint32_t rtp_num_max = sizeof(aw_rtp_name) / AW_NAME_MAX; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) { + aw_err("kstrtouint fail"); + return count; + } + + if (!aw_haptic->ram.base_addr) { + aw_err("no set FIFO space for rtp play!"); + return count; + } + + mutex_lock(&aw_haptic->lock); + if ((val > 0) && (val < rtp_num_max)) { + aw_haptic->state = 1; + aw_haptic->rtp_file_num = val; + } else if (val == 0) { + aw_haptic->state = 0; + } else { + aw_haptic->state = 0; + aw_err("input number error:%d", val); + } + mutex_unlock(&aw_haptic->lock); + + schedule_work(&aw_haptic->rtp_work); + return count; +} + +static ssize_t osc_cali_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "osc_cali_data = 0x%02X\n", + aw_haptic->osc_cali_data); + + return len; +} + +static ssize_t osc_cali_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw_haptic->lock); + if (val == 1) { + rtp_trim_lra_cali(aw_haptic); + } else if (val == 2) { + upload_lra(aw_haptic, AW_OSC_CALI_LRA); + rtp_osc_cali(aw_haptic); + } + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t osc_save_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "osc_cali_data = 0x%02X\n", + aw_haptic->osc_cali_data); + + return len; +} + +static ssize_t osc_save_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + aw_haptic->osc_cali_data = val; + return count; +} + +static DEVICE_ATTR(rtp, 0664, rtp_show, rtp_store); +static DEVICE_ATTR(osc_cali, 0664, osc_cali_show, osc_cali_store); +static DEVICE_ATTR(osc_save, 0664, osc_save_show, osc_save_store); + +static struct attribute *rtp_attributes[] = { + &dev_attr_rtp.attr, + &dev_attr_osc_cali.attr, + &dev_attr_osc_save.attr, + NULL +}; + +static struct attribute_group rtp_attribute_group = { + .attrs = rtp_attributes +}; + +static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) +{ + struct aw_haptic *aw_haptic = container_of(timer, struct aw_haptic, + timer); + aw_info("enter"); + aw_haptic->state = 0; + schedule_work(&aw_haptic->vibrator_work); + return HRTIMER_NORESTART; +} + +static void vibrator_work_routine(struct work_struct *work) +{ + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, + vibrator_work); + + aw_info("enter"); + + mutex_lock(&aw_haptic->lock); + /* Enter standby mode */ + aw_haptic->func->play_stop(aw_haptic); + if (aw_haptic->state) { +#ifdef AW_DURATION_DECIDE_WAVEFORM + if (ram_config(aw_haptic, aw_haptic->duration) < 0) { + mutex_unlock(&aw_haptic->lock); + return; + } +#endif + upload_lra(aw_haptic, AW_F0_CALI_LRA); + if (aw_haptic->activate_mode == AW_RAM_MODE) { + ram_vbat_comp(aw_haptic, false); + ram_play(aw_haptic); + } else if (aw_haptic->activate_mode == AW_RAM_LOOP_MODE) { + ram_vbat_comp(aw_haptic, true); + ram_play(aw_haptic); +#if 0 + /* run ms timer */ + hrtimer_start(&aw_haptic->timer, + ktime_set(aw_haptic->duration / 1000, + (aw_haptic->duration % 1000) * + 1000000), HRTIMER_MODE_REL); +#endif + } else if (aw_haptic->activate_mode == AW_CONT_MODE) { + aw_haptic->func->cont_config(aw_haptic); +#if 0 + /* run ms timer */ + hrtimer_start(&aw_haptic->timer, + ktime_set(aw_haptic->duration / 1000, + (aw_haptic->duration % 1000) * + 1000000), HRTIMER_MODE_REL); +#endif + } else { + aw_err("activate_mode error"); + } + } + mutex_unlock(&aw_haptic->lock); +} + +static int wait_enter_rtp_mode(struct aw_haptic *aw_haptic, int cnt) +{ + bool rtp_work_flag = false; + uint8_t ret = 0; + + while (cnt) { + ret = aw_haptic->func->judge_rtp_going(aw_haptic); + if (ret) { + rtp_work_flag = true; + aw_info("RTP_GO!"); + break; + } + cnt--; + aw_info("wait for RTP_GO, glb_state=0x%02X", ret); + usleep_range(AW_RTP_DELAY_MIN, AW_RTP_DELAY_MAX); + } + if (!rtp_work_flag) { + aw_haptic->func->play_stop(aw_haptic); + aw_err("failed to enter RTP_GO status!"); + return -ERANGE; + } + return 0; +} + +static void rtp_work_routine(struct work_struct *work) +{ + int ret = 0; + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, + rtp_work); + + aw_info("enter"); + + if (aw_haptic->is_supported_rtp == false) + return; + + mutex_lock(&aw_haptic->lock); + if (hrtimer_active(&aw_haptic->timer)) + hrtimer_cancel(&aw_haptic->timer); + aw_haptic->func->play_stop(aw_haptic); + aw_haptic->func->set_rtp_aei(aw_haptic, false); + aw_haptic->func->irq_clear(aw_haptic); + if (!aw_haptic->state) { + mutex_unlock(&aw_haptic->lock); + return; + } + mutex_unlock(&aw_haptic->lock); + + /* fw loaded */ + mutex_lock(&aw_haptic->rtp_lock); + ret = rtp_fw_load(aw_haptic, aw_haptic->rtp_file_num); + if (ret < 0) { + mutex_unlock(&aw_haptic->rtp_lock); + return; + } + mutex_unlock(&aw_haptic->rtp_lock); + + /* rtp config */ + mutex_lock(&aw_haptic->lock); + aw_haptic->rtp_init = true; + upload_lra(aw_haptic, AW_OSC_CALI_LRA); + aw_haptic->func->play_mode(aw_haptic, AW_RTP_MODE); + aw_haptic->func->haptic_start(aw_haptic); + usleep_range(AW_RTP_DELAY_MIN, AW_RTP_DELAY_MAX); + ret = wait_enter_rtp_mode(aw_haptic, 200); + if (ret < 0) { + mutex_unlock(&aw_haptic->lock); + return; + } + mutex_unlock(&aw_haptic->lock); + + mutex_lock(&aw_haptic->rtp_lock); + pm_qos_enable(aw_haptic, true); + aw_haptic->rtp_cnt = 0; + rtp_play(aw_haptic); + if (aw_haptic->play_mode == AW_RTP_MODE) + aw_haptic->func->set_rtp_aei(aw_haptic, true); + pm_qos_enable(aw_haptic, false); + mutex_unlock(&aw_haptic->rtp_lock); +} + +#ifdef TIMED_OUTPUT +static int vibrator_get_time(struct timed_output_dev *dev) +{ + struct aw_haptic *aw_haptic = container_of(dev, struct aw_haptic, + vib_dev); + + if (hrtimer_active(&aw_haptic->timer)) { + ktime_t r = hrtimer_get_remaining(&aw_haptic->timer); + + return ktime_to_ms(r); + } + return 0; +} + +static void vibrator_enable(struct timed_output_dev *dev, int value) +{ + struct aw_haptic *aw_haptic = container_of(dev, struct aw_haptic, + vib_dev); + + aw_info("enter"); + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return; + } + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + if (value > 0) { + upload_lra(aw_haptic, AW_F0_CALI_LRA); + ram_vbat_comp(aw_haptic, false); + aw_haptic->func->play_mode(aw_haptic, AW_RAM_MODE); + aw_haptic->func->haptic_start(aw_haptic); + } + mutex_unlock(&aw_haptic->lock); + + aw_info("exit"); +} +#else +static enum led_brightness brightness_get(struct led_classdev *cdev) +{ + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + return aw_haptic->amplitude; +} + +static void brightness_set(struct led_classdev *cdev, enum led_brightness level) +{ + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + struct transient_trig_data *transient_data = led_get_trigger_data(cdev); + uint64_t duration = 0; + + aw_info("enter"); + if (!transient_data) + return; + del_timer(&transient_data->timer); + if (level == 0) { + aw_haptic->state = 0; + pm_qos_enable(aw_haptic, false); + schedule_work(&aw_haptic->vibrator_work); + return; + } + if (!aw_haptic->ram_init) { + transient_data->activate = 0; + aw_err("ram init failed, not allow to play!"); + return; + } + duration = transient_data->duration; + aw_info("duration is %llu", duration); + if (duration == 0) { + transient_data->activate = 0; + aw_info("duration is 0, no work"); + return; + } else if (duration > 100 && duration <= 120) { + if (duration - 100 > aw_haptic->ram.ram_num) { + transient_data->activate = 0; + aw_err("waveform out of range!"); + return; + } + mutex_lock(&aw_haptic->lock); + aw_haptic->state = level; + aw_haptic->activate_mode = AW_RAM_MODE; + aw_haptic->func->set_wav_seq(aw_haptic, 0, duration - 100); + aw_haptic->func->set_wav_seq(aw_haptic, 1, 0); + aw_haptic->func->set_wav_loop(aw_haptic, 0, 0); + mutex_unlock(&aw_haptic->lock); + schedule_work(&aw_haptic->vibrator_work); + } else if (duration > 5000000 && duration <= 5000050) { + if ((duration - 5000000) >= (sizeof(aw_rtp_name) / AW_NAME_MAX)) { + transient_data->activate = 0; + aw_err("rtp file out of range!"); + return; + } + mutex_lock(&aw_haptic->lock); + aw_haptic->state = level; + aw_haptic->rtp_file_num = duration - 5000000; + aw_info("aw_rtp_name[%d]: %s", aw_haptic->rtp_file_num, aw_rtp_name[aw_haptic->rtp_file_num]); + mutex_unlock(&aw_haptic->lock); + schedule_work(&aw_haptic->rtp_work); + } else { + mutex_lock(&aw_haptic->lock); + aw_haptic->state = level; + aw_haptic->activate_mode = AW_RAM_LOOP_MODE; + aw_haptic->func->set_wav_seq(aw_haptic, 0, 4); + aw_haptic->func->set_wav_seq(aw_haptic, 1, 0); + aw_haptic->func->set_wav_loop(aw_haptic, 0, 0x0F); + mutex_unlock(&aw_haptic->lock); + schedule_work(&aw_haptic->vibrator_work); + } +} +#endif + +static int vibrator_init(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + aw_info("enter"); + +#ifdef TIMED_OUTPUT + aw_info("TIMED_OUT FRAMEWORK!"); + aw_haptic->vib_dev.name = "vibrator"; + aw_haptic->vib_dev.get_time = vibrator_get_time; + aw_haptic->vib_dev.enable = vibrator_enable; + + ret = timed_output_dev_register(&(aw_haptic->vib_dev)); + if (ret < 0) { + aw_err("fail to create timed output dev"); + return ret; + } +#else + aw_info("loaded in leds_cdev framework!"); +#ifdef KERNEL_OVER_5_10 + aw_haptic->vib_dev.name = "vibrator"; +#else + aw_haptic->vib_dev.name = "vibrator"; +#endif + aw_haptic->vib_dev.brightness_get = brightness_get; + aw_haptic->vib_dev.brightness_set = brightness_set; + ret = devm_led_classdev_register(&aw_haptic->i2c->dev, + &aw_haptic->vib_dev); + if (ret < 0) { + aw_err("fail to create led dev"); + return ret; + } +#endif + + ret = sysfs_create_group(&aw_haptic->vib_dev.dev->kobj, + &vibrator_attribute_group); + if (ret < 0) { + aw_err("error creating sysfs common attr files"); + return ret; + } + if (aw_haptic->is_supported_rtp && aw_haptic->is_used_irq_pin) { + ret = sysfs_create_group(&aw_haptic->vib_dev.dev->kobj, + &rtp_attribute_group); + if (ret < 0) { + aw_err("error creating sysfs rtp attr files"); + return ret; + } + } + hrtimer_init(&aw_haptic->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + aw_haptic->timer.function = vibrator_timer_func; + INIT_WORK(&aw_haptic->vibrator_work, vibrator_work_routine); + INIT_WORK(&aw_haptic->rtp_work, rtp_work_routine); + mutex_init(&aw_haptic->lock); + mutex_init(&aw_haptic->rtp_lock); + + return 0; +} + +static void haptic_init(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + aw_haptic->haptic_audio.delay_val = 1; + aw_haptic->haptic_audio.timer_val = 21318; + INIT_LIST_HEAD(&(aw_haptic->haptic_audio.ctr_list)); + hrtimer_init(&aw_haptic->haptic_audio.timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + aw_haptic->haptic_audio.timer.function = haptic_audio_timer_func; + INIT_WORK(&aw_haptic->haptic_audio.work, haptic_audio_work_routine); + mutex_init(&aw_haptic->haptic_audio.lock); + + mutex_lock(&aw_haptic->lock); + aw_haptic->activate_mode = aw_haptic->info.mode; + aw_haptic->f0_pre = aw_haptic->info.f0_pre; + aw_haptic->gun_type = AW_GUN_TYPE_DEF_VAL; + aw_haptic->bullet_nr = AW_BULLET_NR_DEF_VAL; + aw_haptic->func->play_mode(aw_haptic, AW_STANDBY_MODE); + aw_haptic->func->misc_para_init(aw_haptic); + aw_haptic->func->offset_cali(aw_haptic); + aw_haptic->func->vbat_mode_config(aw_haptic, AW_CONT_VBAT_HW_COMP_MODE); + aw_haptic->ram_vbat_comp = AW_RAM_VBAT_COMP_ENABLE; + + if (aw_haptic->info.lk_f0_cali) { + aw_haptic->f0_cali_data = aw_haptic->info.lk_f0_cali; + upload_lra(aw_haptic, AW_F0_CALI_LRA); + } else { + f0_cali(aw_haptic); + } + mutex_unlock(&aw_haptic->lock); + + aw_info("exit"); +} + +static void ram_work_routine(struct work_struct *work) +{ + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, + ram_work.work); + + ram_update(aw_haptic); +} + +static void ram_work_init(struct aw_haptic *aw_haptic) +{ + int ram_timer_val = AW_RAM_WORK_DELAY_INTERVAL; + + INIT_DELAYED_WORK(&aw_haptic->ram_work, ram_work_routine); + schedule_delayed_work(&aw_haptic->ram_work, + msecs_to_jiffies(ram_timer_val)); +} + +static int get_dts_data(struct aw_haptic *aw_haptic, uint8_t *dts_name, + struct aw_haptic_dts_data *dts_data, + struct device_node *np) +{ + int ret = 0; + uint32_t *dts_val = NULL; + bool *dts_bool = NULL; + uint8_t type = dts_data->type; + + if (dts_name == NULL) + return -ERANGE; + switch (type) { + case AW_DTS_DATA_U32: + dts_val = (uint32_t *)(dts_data->dts_val); + ret = of_property_read_u32(np, dts_name, dts_val); + if (ret != 0) + aw_info("%s not found", dts_name); + break; + case AW_DTS_DATA_ARRAY: + dts_val = (uint32_t *)(dts_data->dts_val); + ret = of_property_read_u32_array(np, dts_name, dts_val, + dts_data->len); + if (ret != 0) + aw_info("%s not found", dts_name); + break; + case AW_DTS_DATA_BOOL: + dts_bool = (bool *)(dts_data->dts_val); + *dts_bool = of_property_read_bool(np, dts_name); + break; + default: + break; + } + return 0; +} + +static void parse_dts(struct aw_haptic *aw_haptic, struct device_node *np) +{ + int i = 0; + struct aw_haptic_dts_info *info = &(aw_haptic->info); + struct aw_haptic_dts_data *dts_data = NULL; + struct aw_haptic_dts_name *dts_name_array = aw_haptic->dts_name_array; + + dts_data = devm_kzalloc(aw_haptic->dev, + sizeof(struct aw_haptic_dts_data), + GFP_KERNEL); + if (dts_data == NULL) + return; + for (i = 0; i < aw_haptic->dts_name_len; i++) { + switch (dts_name_array[i].flag) { + case AW_LK_F0_CALI: + dts_data->dts_val = &(info->lk_f0_cali); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_MODE: + dts_data->dts_val = &(info->mode); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_F0_PRE: + dts_data->dts_val = &(info->f0_pre); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_F0_CALI_PERCENT: + dts_data->dts_val = &(info->f0_cali_percent); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_CONT_DRV1_LVL: + dts_data->dts_val = &(info->cont_drv1_lvl); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_CONT_DRV2_LVL: + dts_data->dts_val = &(info->cont_drv2_lvl); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_LRA_VRMS: + dts_data->dts_val = &(info->lra_vrms); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_CONT_DRV1_TIME: + dts_data->dts_val = &(info->cont_drv1_time); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_CONT_DRV2_TIME: + dts_data->dts_val = &(info->cont_drv2_time); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_CONT_TD: + dts_data->dts_val = &(info->cont_td); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_CONT_ZC_THR: + dts_data->dts_val = &(info->cont_zc_thr); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_CONT_NUM_BRK: + dts_data->dts_val = &(info->cont_num_brk); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_CONT_WAIT_NUM: + dts_data->dts_val = &(info->cont_wait_num); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_CONT_BRK_GAIN: + dts_data->dts_val = &(info->cont_brk_gain); + dts_data->len = 1; + break; + case AW_CONT_BEMF_SET: + dts_data->dts_val = &(info->cont_bemf_set); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_D2S_GAIN: + dts_data->dts_val = &(info->d2s_gain); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_CONT_BRK_TIME: + dts_data->dts_val = &(info->cont_brk_time); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_CONT_TRACK_MARGIN: + dts_data->dts_val = &(info->cont_track_margin); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_CONT_TSET: + dts_data->dts_val = &(info->cont_tset); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_F0_COEFF: + dts_data->dts_val = &(info->f0_coeff); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_U32; + break; + case AW_SINE_ARRAY: + dts_data->dts_val = info->sine_array; + dts_data->len = ARRAY_SIZE(info->sine_array); + dts_data->type = AW_DTS_DATA_ARRAY; + break; + case AW_SW_BRAKE: + dts_data->dts_val = info->sw_brake; + dts_data->len = ARRAY_SIZE(info->sw_brake); + dts_data->type = AW_DTS_DATA_ARRAY; + break; + case AW_DURATION_TIME: + dts_data->dts_val = info->duration_time; + dts_data->len = ARRAY_SIZE(info->duration_time); + dts_data->type = AW_DTS_DATA_ARRAY; + break; + case AW_TRIG_CONFIG: + dts_data->dts_val = info->trig_cfg; + if (aw_haptic->name == AW8624) + dts_data->len = 5; + else + dts_data->len = ARRAY_SIZE(info->trig_cfg); + dts_data->type = AW_DTS_DATA_ARRAY; + break; + case AW_BRAKE_CONT_CONFIG: + dts_data->dts_val = info->cont_brake; + dts_data->len = ARRAY_SIZE(info->cont_brake); + dts_data->type = AW_DTS_DATA_ARRAY; + break; + case AW_F0_TRACE_PARAM: + dts_data->dts_val = info->f0_trace_parameter; + dts_data->len = ARRAY_SIZE(info->f0_trace_parameter); + dts_data->type = AW_DTS_DATA_ARRAY; + break; + case AW_BEMF_CONFIG: + dts_data->dts_val = info->bemf_config; + dts_data->len = ARRAY_SIZE(info->bemf_config); + dts_data->type = AW_DTS_DATA_ARRAY; + break; + case AW_ENABLE_AUTO_BRK: + dts_data->dts_val = &(info->is_enabled_auto_brk); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_BOOL; + break; + case AW_ENABLE_TRACK_EN: + dts_data->dts_val = &(info->is_enabled_track_en); + dts_data->len = 1; + dts_data->type = AW_DTS_DATA_BOOL; + break; + default: + dts_data->type = AW_DTS_NULL; + break; + } + if (dts_data->type) + get_dts_data(aw_haptic, dts_name_array[i].name, + dts_data, np); + } + + devm_kfree(aw_haptic->dev, dts_data); + dts_data = NULL; +} + +#ifdef AW_INPUT_FRAMEWORK +static void input_gain_work_routine(struct work_struct *work) +{ + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, + input_gain_work); + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->set_gain(aw_haptic, aw_haptic->gain); + mutex_unlock(&aw_haptic->lock); +} + +static void input_vib_work_routine(struct work_struct *work) +{ + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, + input_vib_work); + + mutex_lock(&aw_haptic->lock); + /* Enter standby mode */ + hrtimer_cancel(&aw_haptic->timer); + aw_haptic->func->play_stop(aw_haptic); + if (aw_haptic->state) { + upload_lra(aw_haptic, AW_F0_CALI_LRA); + if (aw_haptic->activate_mode == AW_RAM_MODE) { + ram_vbat_comp(aw_haptic, false); + aw_haptic->func->set_wav_seq(aw_haptic, 0x00, + aw_haptic->index); + aw_haptic->func->set_wav_seq(aw_haptic, 0x01, 0x00); + aw_haptic->func->set_wav_loop(aw_haptic, 0x00, 0x00); + ram_play(aw_haptic); + } else if (aw_haptic->activate_mode == AW_RAM_LOOP_MODE) { + ram_vbat_comp(aw_haptic, true); + aw_haptic->func->set_repeat_seq(aw_haptic, + aw_haptic->index); + ram_play(aw_haptic); + /* run ms timer */ + hrtimer_start(&aw_haptic->timer, + ktime_set(aw_haptic->duration / 1000, + (aw_haptic->duration % 1000) * + 1000000), HRTIMER_MODE_REL); + } else { + aw_err("activate_mode error"); + } + } + mutex_unlock(&aw_haptic->lock); +} + +static int input_upload_effect(struct input_dev *dev, struct ff_effect *effect, + struct ff_effect *old) +{ + struct aw_haptic *aw_haptic = input_get_drvdata(dev); + short wav_id = 0; + int wav_id_max = 0; + int ret = 0; + + mutex_lock(&aw_haptic->lock); + switch (effect->type) { + case FF_CONSTANT: + aw_haptic->activate_mode = AW_RAM_LOOP_MODE; + aw_haptic->duration = effect->replay.length; + aw_haptic->index = aw_haptic->ram.ram_num; + aw_info("waveform id = %d", aw_haptic->index); + break; + case FF_PERIODIC: + ret = copy_from_user(&wav_id, effect->u.periodic.custom_data, + sizeof(short)); + if (ret) { + aw_err("copy from user error %d!!", ret); + mutex_unlock(&aw_haptic->lock); + return -ERANGE; + } + aw_info("waveform id = %d", wav_id); + wav_id_max = sizeof(aw_rtp_name) / sizeof(*aw_rtp_name) + + aw_haptic->ram.ram_num - 1; + if (wav_id > 0 && wav_id < aw_haptic->ram.ram_num) { + aw_haptic->activate_mode = AW_RAM_MODE; + aw_haptic->index = wav_id; + } else if (wav_id > aw_haptic->ram.ram_num && wav_id <= wav_id_max) { + aw_haptic->activate_mode = AW_RTP_MODE; + aw_haptic->rtp_file_num = wav_id - aw_haptic->ram.ram_num; + } else { + aw_haptic->activate_mode = AW_STANDBY_MODE; + aw_err("waveform id is error"); + mutex_unlock(&aw_haptic->lock); + return -ERANGE; + } + break; + default: + aw_err("Unsupported effect type: %d", effect->type); + break; + } + mutex_unlock(&aw_haptic->lock); + + return 0; +} + +static int input_playback(struct input_dev *dev, int effect_id, int val) +{ + struct aw_haptic *aw_haptic = input_get_drvdata(dev); + + if (val > 0) + aw_haptic->state = 1; + if (val <= 0) + aw_haptic->state = 0; + switch (aw_haptic->activate_mode) { + case AW_RAM_MODE: + schedule_work(&aw_haptic->input_vib_work); + break; + case AW_RAM_LOOP_MODE: + schedule_work(&aw_haptic->input_vib_work); + break; + case AW_RTP_MODE: + schedule_work(&aw_haptic->rtp_work); + break; + default: + aw_err("Unsupported mode: %d", aw_haptic->activate_mode); + break; + } + + return 0; +} + +static int input_erase(struct input_dev *dev, int effect_id) +{ + struct aw_haptic *aw_haptic = input_get_drvdata(dev); + + aw_haptic->duration = 0; + return 0; +} + +static void input_set_gain(struct input_dev *dev, uint16_t gain) +{ + struct aw_haptic *aw_haptic = input_get_drvdata(dev); + + if (gain > 0x7fff) + gain = 0x7fff; + aw_haptic->gain = gain * 0x80 / 0x7fff; + schedule_work(&aw_haptic->input_gain_work); + aw_info("aw_haptic->gain = 0x%02x", aw_haptic->gain); +} + +static int input_framework_init(struct aw_haptic *aw_haptic) +{ + struct input_dev *input_dev; + int ret = 0; + + input_dev = devm_input_allocate_device(aw_haptic->dev); + if (input_dev == NULL) + return -ENOMEM; + input_dev->name = "aw-haptic-hv"; + input_set_drvdata(input_dev, aw_haptic); + aw_haptic->input_dev = input_dev; + input_set_capability(input_dev, EV_FF, FF_GAIN); + input_set_capability(input_dev, EV_FF, FF_CONSTANT); + input_set_capability(input_dev, EV_FF, FF_PERIODIC); + input_set_capability(input_dev, EV_FF, FF_CUSTOM); + ret = input_ff_create(input_dev, AW_EFFECT_NUMBER); + if (ret < 0) { + aw_err("create input FF device failed, rc=%d\n", ret); + return ret; + } + input_dev->ff->upload = input_upload_effect; + input_dev->ff->playback = input_playback; + input_dev->ff->erase = input_erase; + input_dev->ff->set_gain = input_set_gain; + INIT_WORK(&aw_haptic->input_gain_work, input_gain_work_routine); + INIT_WORK(&aw_haptic->input_vib_work, input_vib_work_routine); + ret = input_register_device(input_dev); + if (ret < 0) { + aw_err("register input device failed, rc=%d\n", ret); + input_ff_destroy(aw_haptic->input_dev); + return ret; + } + return ret; +} +#endif + +static int aw_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + int ret = 0; + struct aw_haptic *aw_haptic; + struct device_node *np = i2c->dev.of_node; + + aw_info("enter"); + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + aw_err("check_functionality failed"); + return -EIO; + } + + aw_haptic = devm_kzalloc(&i2c->dev, sizeof(struct aw_haptic), + GFP_KERNEL); + if (aw_haptic == NULL) + return -ENOMEM; + + aw_haptic->dev = &i2c->dev; + aw_haptic->i2c = i2c; + + i2c_set_clientdata(i2c, aw_haptic); +#ifdef AW_INPUT_FRAMEWORK + ret = input_framework_init(aw_haptic); + if (ret < 0) + goto err_input_config; +#endif + /* aw_haptic rst & int */ + if (np) { + ret = parse_dt_gpio(aw_haptic, &i2c->dev, np); + if (ret) { + aw_err("failed to parse gpio"); + goto err_parse_dt; + } + } else { + aw_haptic->reset_gpio = -1; + aw_haptic->irq_gpio = -1; + } + + if (gpio_is_valid(aw_haptic->reset_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw_haptic->reset_gpio, + GPIOF_OUT_INIT_LOW, "awinic_rst"); + if (ret) { + aw_err("rst request failed"); + goto err_reset_gpio_request; + } + } + + if (gpio_is_valid(aw_haptic->irq_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw_haptic->irq_gpio, + GPIOF_DIR_IN, "awinic_int"); + if (ret) { + aw_err("int request failed"); + goto err_irq_gpio_request; + } + } + + parse_dts_i2c_addr(aw_haptic, &i2c->dev, np); + /* aw_haptic chip id */ + ret = parse_chipid(aw_haptic); + if (ret < 0) { + aw_err("read_chipid failed ret=%d", ret); + goto err_id; + } + + sw_reset(aw_haptic); + + ret = chip_private_init(aw_haptic); + if (ret < 0) { + aw_err("chip_private_init failed ret=%d", ret); + goto err_chip_private_init; + } + + ret = func_ptr_init(aw_haptic, &i2c->dev); + if (ret < 0) { + aw_err("ctrl_init failed ret=%d", ret); + goto err_ctrl_init; + } + + ret = aw_haptic->func->check_qualify(aw_haptic); + if (ret < 0) { + aw_err("qualify check failed ret=%d", ret); + goto err_ctrl_init; + } + + parse_dts(aw_haptic, np); + ret = irq_config(&i2c->dev, aw_haptic); + if (ret != 0) { + aw_err("irq_config failed ret=%d", ret); + goto err_irq_config; + } + + vibrator_init(aw_haptic); + haptic_init(aw_haptic); + aw_haptic->func->creat_node(aw_haptic); + ram_work_init(aw_haptic); + dev_set_drvdata(&i2c->dev, aw_haptic); + aw_info("probe completed successfully!"); + return 0; + +err_input_config: +err_id: +err_ctrl_init: +err_chip_private_init: +err_irq_config: + if (gpio_is_valid(aw_haptic->irq_gpio)) + devm_gpio_free(&i2c->dev, aw_haptic->irq_gpio); + +err_irq_gpio_request: + if (gpio_is_valid(aw_haptic->reset_gpio)) + devm_gpio_free(&i2c->dev, aw_haptic->reset_gpio); + +err_parse_dt: +err_reset_gpio_request: + devm_kfree(&i2c->dev, aw_haptic); + aw_haptic = NULL; + return ret; +} + +static int aw_i2c_remove(struct i2c_client *i2c) +{ + struct aw_haptic *aw_haptic = i2c_get_clientdata(i2c); + + aw_info("enter"); + cancel_delayed_work_sync(&aw_haptic->ram_work); + cancel_work_sync(&aw_haptic->haptic_audio.work); + hrtimer_cancel(&aw_haptic->haptic_audio.timer); + cancel_work_sync(&aw_haptic->rtp_work); + cancel_work_sync(&aw_haptic->vibrator_work); + hrtimer_cancel(&aw_haptic->timer); + mutex_destroy(&aw_haptic->lock); + mutex_destroy(&aw_haptic->rtp_lock); + mutex_destroy(&aw_haptic->haptic_audio.lock); +#ifdef TIMED_OUTPUT + timed_output_dev_unregister(&aw_haptic->vib_dev); +#endif + devm_free_irq(&i2c->dev, gpio_to_irq(aw_haptic->irq_gpio), aw_haptic); + if (gpio_is_valid(aw_haptic->irq_gpio)) + devm_gpio_free(&i2c->dev, aw_haptic->irq_gpio); + + if (gpio_is_valid(aw_haptic->reset_gpio)) + devm_gpio_free(&i2c->dev, aw_haptic->reset_gpio); + + return 0; +} + +static int aw_i2c_suspend(struct device *dev) +{ + int ret = 0; + struct aw_haptic *aw_haptic = dev_get_drvdata(dev); + + aw_info("enter"); + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + mutex_unlock(&aw_haptic->lock); + + return ret; +} + +static int aw_i2c_resume(struct device *dev) +{ + aw_info("enter"); + return 0; +} + +static SIMPLE_DEV_PM_OPS(aw_pm_ops, aw_i2c_suspend, aw_i2c_resume); + +static const struct i2c_device_id aw_i2c_id[] = { + {AW_I2C_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, aw_i2c_id); + +static const struct of_device_id aw_dt_match[] = { + {.compatible = "awinic,haptic_nv"}, + {}, +}; + +static struct i2c_driver aw_i2c_driver = { + .driver = { + .name = AW_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw_dt_match), +#ifdef CONFIG_PM_SLEEP + .pm = &aw_pm_ops, +#endif + }, + .probe = aw_i2c_probe, + .remove = aw_i2c_remove, + .id_table = aw_i2c_id, +}; + +static int __init aw_i2c_init(void) +{ + int ret = 0; + + aw_info("aw_haptic driver version %s", HAPTIC_NV_DRIVER_VERSION); + ret = i2c_add_driver(&aw_i2c_driver); + if (ret) { + aw_err("fail to add aw_haptic device into i2c"); + return ret; + } + return 0; +} +module_init(aw_i2c_init); + +static void __exit aw_i2c_exit(void) +{ + i2c_del_driver(&aw_i2c_driver); +} +module_exit(aw_i2c_exit); + +MODULE_DESCRIPTION("AWINIC Haptic Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/awinic/aw862x_haptic_nv/haptic_nv.h b/drivers/misc/awinic/aw862x_haptic_nv/haptic_nv.h new file mode 100755 index 000000000000..3c0c1aeed055 --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv/haptic_nv.h @@ -0,0 +1,645 @@ +#ifndef _HAPTIC_NV_H_ +#define _HAPTIC_NV_H_ +/********************************************************* + * + * Macro Control + * + *********************************************************/ +#define AW_CHECK_RAM_DATA +#define AW_READ_BIN_FLEXBALLY +#define AW_INPUT_FRAMEWORK +/* #define AW_DURATION_DECIDE_WAVEFORM */ +/* #define AW_ENABLE_RTP_PRINT_LOG */ +/* #define AW8624_MUL_GET_F0 */ + +#define AW8624_DRIVER +#define AW8622X_DRIVER +#define AW86214_DRIVER +#define AW8623X_DRIVER +/********************************************************* + * + * Haptic_NV CHIPID + * + *********************************************************/ +#define AW8624_CHIP_ID (0x24) +#define AW8622X_CHIP_ID (0x00) +#define AW86214_CHIP_ID (0x01) +#define AW8623X_CHIP_ID_H (0x23) +#define AW86233_CHIP_ID_L (0x30) +#define AW86234_CHIP_ID_L (0x40) +#define AW86235_CHIP_ID_L (0x50) +/********************************************************* + * + * Marco + * + *********************************************************/ +#ifdef AW8622X_DRIVER +#define AW862XX_COMMON +#endif + +#ifdef AW86214_DRIVER +#define AW862XX_COMMON +#endif + +#define AW_I2C_NAME "haptic_nv" +#define AW_I2C_RETRIES (5) +#define AW_I2C_READ_MSG_NUM (2) +#define AW_I2C_BYTE_ONE (1) +#define AW_I2C_BYTE_TWO (2) +#define AW_I2C_BYTE_THREE (3) +#define AW_I2C_BYTE_FOUR (4) +#define AW_I2C_BYTE_FIVE (5) +#define AW_I2C_BYTE_SIX (6) +#define AW_I2C_BYTE_SEVEN (7) +#define AW_I2C_BYTE_EIGHT (8) + +#define AWRW_SIZE (220) +#define AW_REG_MAX (0xFF) +#define AW_NAME_MAX (64) +#define AW_VBAT_MIN (3000) +#define AW_VBAT_MAX (4500) +#define AW_TRIG_NUM (3) +#define AWRW_CMD_UNIT (5) +#define AW_VBAT_REFER (4200) +#define AW_DEFAULT_GAIN (0x80) +#define AW_SEQUENCER_SIZE (8) +#define AW_PM_QOS_VALUE_VB (400) +#define AW_GUN_TYPE_DEF_VAL (0xFF) +#define AW_DTS_NAME_NUM_MAX (40) +#define AW_F0_CALI_DATA_MIN (0x20) +#define AW_F0_CALI_DATA_MAX (0x1F) +#define AW_BULLET_NR_DEF_VAL (0) +#define CPU_LATENCY_QOC_VALUE (0) +#define AW_SEQUENCER_LOOP_SIZE (4) +#define AW_RAMDATA_SHOW_COLUMN (16) +#define AW_READ_CHIPID_RETRIES (5) +#define AW_RAMDATA_RD_BUFFER_SIZE (1024) +#define AW_RAMDATA_WR_BUFFER_SIZE (2048) +#define AW_RAMDATA_SHOW_UINT_SIZE (6) +#define AW_READ_CHIPID_RETRY_DELAY (2) +#define AW_EFFECT_NUMBER (3) +#define AW_RAM_WORK_DELAY_INTERVAL (8000) +#define AW_RAMDATA_SHOW_LINE_BUFFER_SZIE (100) + +#define AW_RL_DELAY_MIN (3000) +#define AW_RL_DELAY_MAX (3500) +#define AW_F0_DELAY_MIN (10000) +#define AW_F0_DELAY_MAX (10500) +#define AW_RTP_DELAY_MIN (2000) +#define AW_RTP_DELAY_MAX (2500) +#define AW_PLAY_DELAY_MIN (2000) +#define AW_PLAY_DELAY_MAX (2500) +#define AW_STOP_DELAY_MIN (2000) +#define AW_STOP_DELAY_MAX (2500) +#define AW_VBAT_DELAY_MIN (2000) +#define AW_VBAT_DELAY_MAX (2500) +#define AW_OSC_TRIM_PARAM (50) +#define AW_OSC_CALI_MAX_LENGTH (5100000) + +#define AW_SET_RAMADDR_H(addr) ((addr) >> 8) +#define AW_SET_RAMADDR_L(addr) ((addr) & 0x00FF) +#define AW_SET_BASEADDR_H(addr) ((addr) >> 8) +#define AW_SET_BASEADDR_L(addr) ((addr) & 0x00FF) +/********************************************************* + * + * aw8624 marco + * + ********************************************************/ +#define AW8624_F0_CALI_ACCURACY (25) +#define AW8624_MUL_GET_F0_RANGE (150) +#define AW8624_MUL_GET_F0_NUM (3) + +#define AW8624_VBAT_FORMULA(code) (6100 * (code) / 256) +#define AW8624_F0_FORMULA(reg, coeff) (1000000000 / ((reg) * (coeff))) +#define AW8624_RL_FORMULA(reg_val) (298 * (reg_val)) +#define AW8624_SET_AEADDR_H(addr) ((((addr) >> 1) >> 8)) +#define AW8624_SET_AEADDR_L(addr) (((addr) >> 1) & 0x00FF) +#define AW8624_SET_AFADDR_H(addr) (((addr) - ((addr) >> 2)) >> 8) +#define AW8624_SET_AFADDR_L(addr) (((addr) - ((addr) >> 2)) & 0x00FF) +/********************************************************* + * + * aw862xx marco + * + ********************************************************/ +#define AW862XX_DRV2_LVL_MAX (127) +#define AW862XX_DRV_WIDTH_MIN (40) +#define AW862XX_DRV_WIDTH_MAX (255) +#define AW862XX_F0_CALI_ACCURACY (24) + +#define AW862XX_VBAT_FORMULA(code) (6100 * (code) / 1024) +#define AW862XX_F0_FORMULA(code) (384000 * 10 / (code)) +#define AW862XX_RL_FORMULA(code, d2s_gain) (((code) * 678 * 100) / (1024 * (d2s_gain))) +#define AW862XX_SET_AEADDR_H(addr) ((((addr) >> 1) >> 4) & 0xF0) +#define AW862XX_SET_AEADDR_L(addr) (((addr) >> 1) & 0x00FF) +#define AW862XX_SET_AFADDR_H(addr) ((((addr) - ((addr) >> 2)) >> 8) & 0x0F) +#define AW862XX_SET_AFADDR_L(addr) (((addr) - ((addr) >> 2)) & 0x00FF) +#define AW862XX_DRV2_LVL_FORMULA(f0, vrms) ((((f0) < 1800) ? 1809920 : 1990912) / 1000 * (vrms) / 61000) +#define AW862XX_DRV_WIDTH_FORMULA(f0, margin, brk_gain) \ + ((240000 / (f0)) - (margin) - (brk_gain) - 8) + +/********************************************************* + * + * aw8623x marco + * + ********************************************************/ +#define AW8623X_DRV2_LVL_MAX (127) +#define AW8623X_DRV_WIDTH_MIN (40) +#define AW8623X_DRV_WIDTH_MAX (255) +#define AW8623X_F0_CALI_ACCURACY (24) + +#define AW8623X_VBAT_FORMULA(code) (6100 * (code) / 1023) +#define AW8623X_F0_FORMULA(code) (384000 * 10 / (code)) +#define AW8623X_RL_FORMULA(code, d2s_gain) (((code) * 678 * 1000) / (1023 * (d2s_gain))) +#define AW8623X_OS_FORMULA(code, d2s_gain) (2440 * ((code) - 512) / (1023 * (1 + d2s_gain))) +#define AW8623X_SET_AEADDR_H(addr) ((((addr) >> 1) >> 4) & 0xF0) +#define AW8623X_SET_AEADDR_L(addr) (((addr) >> 1) & 0x00FF) +#define AW8623X_SET_AFADDR_H(addr) ((((addr) - ((addr) >> 2)) >> 8) & 0x0F) +#define AW8623X_SET_AFADDR_L(addr) (((addr) - ((addr) >> 2)) & 0x00FF) +#define AW8623X_DRV2_LVL_FORMULA(f0, vrms) ((((f0) < 1800) ? 1809920 : 1990912) / 1000 * (vrms) / 61000) +#define AW8623X_DRV_WIDTH_FORMULA(f0, margin, brk_gain) \ + ((240000 / (f0)) - (margin) - (brk_gain) - 8) + +/********************************************************* + * + * Conditional Marco + * + *********************************************************/ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 4, 1) +#define TIMED_OUTPUT +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) +#define KERNEL_OVER_5_10 +#endif + +#ifdef TIMED_OUTPUT +#include <../../../drivers/staging/android/timed_output.h> +typedef struct timed_output_dev cdev_t; +#else +typedef struct led_classdev cdev_t; +#endif +/********************************************************* + * + * Log Format + * + *********************************************************/ +#define aw_err(format, ...) \ + pr_err("[%s][%04d]%s: " format "\n", AW_I2C_NAME, __LINE__, __func__, ##__VA_ARGS__) + +#define aw_info(format, ...) \ + pr_info("[%s][%04d]%s: " format "\n", AW_I2C_NAME, __LINE__, __func__, ##__VA_ARGS__) + +#define aw_dbg(format, ...) \ + pr_debug("[%s][%04d]%s: " format "\n", AW_I2C_NAME, __LINE__, __func__, ##__VA_ARGS__) + +/********************************************************* + * + * Enum Define + * + *********************************************************/ +enum awrw_flag { + AW_WRITE = 0, + AW_READ = 1, +}; + +enum aw_haptic_flags { + AW_FLAG_NONR = 0, + AW_FLAG_SKIP_INTERRUPTS = 1, +}; + +enum aw_haptic_work_mode { + AW_STANDBY_MODE = 0, + AW_RAM_MODE = 1, + AW_RAM_LOOP_MODE = 2, + AW_CONT_MODE = 3, + AW_RTP_MODE = 4, + AW_TRIG_MODE = 5, + AW_NULL = 6, +}; + +enum aw_haptic_cont_vbat_comp_mode { + AW_CONT_VBAT_SW_COMP_MODE = 0, + AW_CONT_VBAT_HW_COMP_MODE = 1, +}; + +enum aw_haptic_ram_vbat_comp_mode { + AW_RAM_VBAT_COMP_DISABLE = 0, + AW_RAM_VBAT_COMP_ENABLE = 1, +}; + +enum aw_haptic_pwm_mode { + AW_PWM_48K = 0, + AW_PWM_24K = 1, + AW_PWM_12K = 2, +}; + +enum aw_haptic_play { + AW_PLAY_NULL = 0, + AW_PLAY_ENABLE = 1, + AW_PLAY_STOP = 2, + AW_PLAY_GAIN = 8, +}; + +enum aw_haptic_cmd { + AW_CMD_NULL = 0, + AW_CMD_ENABLE = 1, + AW_CMD_HAPTIC = 0x0F, + AW_CMD_TP = 0x10, + AW_CMD_SYS = 0xF0, + AW_CMD_STOP = 255, +}; + +enum aw_haptic_cali_lra { + AW_WRITE_ZERO = 0, + AW_F0_CALI_LRA = 1, + AW_OSC_CALI_LRA = 2, +}; + +enum aw_haptic_awrw_flag { + AW_SEQ_WRITE = 0, + AW_SEQ_READ = 1, +}; + +enum aw_haptic_read_chip_type { + AW_FIRST_TRY = 0, + AW_LAST_TRY = 1, +}; + +enum aw_haptic_chip_name { + AW_CHIP_NULL = 0, + AW86223 = 1, + AW86224 = 2, + AW86225 = 3, + AW86214 = 4, + AW8624 = 5, + AW86233 = 6, + AW86234 = 7, + AW86235 = 8, +}; + +enum aw_haptic_irq_state { + AW_IRQ_ALMOST_EMPTY = 1, +}; + +enum aw_haptic_dts_data_type { + AW_DTS_NULL = 0, + AW_DTS_DATA_U32 = 1, + AW_DTS_DATA_ARRAY = 2, + AW_DTS_DATA_BOOL = 3, +}; + +enum aw_haptic_protect_config { + AW_PROTECT_OFF = 0, + AW_PROTECT_EN = 1, + AW_PROTECT_CFG_1 = 0x2D, + AW_PROTECT_CFG_2 = 0x3E, + AW_PROTECT_CFG_3 = 0X3F, +}; + +enum aw_haptic_ram_file_num { + AW8624_RAM_FILE = 0, + AW8622X_RAM_FILE = 1, + AW86214_RAM_FILE = 2, + AW8623X_RAM_FILE = 3, +}; + +enum aw_haptic_pin { + AW_TRIG1 = 0, + AW_TRIG2 = 1, + AW_TRIG3 = 2, + AW_IRQ = 3, +}; + +enum aw_haptic_dts_name_flag { + AW_MODE = 0, + AW_F0_PRE = 1, + AW_CONT_TD = 2, + AW_F0_COEFF = 3, + AW_D2S_GAIN = 4, + AW_CONT_TSET = 5, + AW_LK_F0_CALI = 6, + AW_CONT_ZC_THR = 7, + AW_CONT_NUM_BRK = 8, + AW_CONT_DRV1_LVL = 9, + AW_CONT_DRV2_LVL = 10, + AW_CONT_WAIT_NUM = 11, + AW_CONT_BRK_GAIN = 12, + AW_CONT_BEMF_SET = 13, + AW_CONT_BRK_TIME = 14, + AW_CONT_DRV1_TIME = 15, + AW_CONT_DRV2_TIME = 16, + AW_F0_CALI_PERCENT = 17, + AW_CONT_TRACK_MARGIN = 18, + AW_LRA_VRMS = 19, +/*------------bool---------------*/ + AW_ENABLE_AUTO_BRK = 25, + AW_ENABLE_TRACK_EN = 26, +/*------------array---------------*/ + AW_SW_BRAKE = 31, + AW_SINE_ARRAY = 32, + AW_TRIG_CONFIG = 33, + AW_BEMF_CONFIG = 34, + AW_DURATION_TIME = 35, + AW_F0_TRACE_PARAM = 36, + AW_BRAKE_CONT_CONFIG = 37, +}; + +/********************************************************* + * + * Enum aw8623x + * + *********************************************************/ +enum aw8623x_sram_size_flag { + AW8623X_HAPTIC_SRAM_1K = 0, + AW8623X_HAPTIC_SRAM_2K = 1, + AW8623X_HAPTIC_SRAM_3K = 2, +}; + +/********************************************************* + * + * Enum aw862xx + * + *********************************************************/ +enum aw862xx_ef_id { + AW86223_EF_ID = 0x01, + AW86224_EF_ID = 0x00, + AW86225_EF_ID = 0x00, + AW86214_EF_ID = 0x41, +}; + +enum aw862xx_sram_size_flag { + AW862XX_HAPTIC_SRAM_1K = 0, + AW862XX_HAPTIC_SRAM_2K = 1, + AW862XX_HAPTIC_SRAM_3K = 2, +}; +/********************************************************* + * + * Enum aw8624 + * + *********************************************************/ +enum aw8624_pwm_clk { + AW8624_CLK_24K = 2, + AW8624_CLK_12K = 3, +}; +/********************************************************* + * + * Struct Define + * + *********************************************************/ +struct trig { + uint8_t enable; + uint8_t trig_edge; + uint8_t trig_brk; + uint8_t trig_level; + uint8_t trig_polar; + uint8_t pos_enable; + uint8_t neg_enable; + uint8_t pos_sequence; + uint8_t neg_sequence; +}; + +struct aw_haptic_ram { + uint8_t ram_num; + uint8_t version; + uint8_t ram_shift; + uint8_t baseaddr_shift; + + uint32_t len; + uint32_t check_sum; + uint32_t base_addr; +}; + +struct aw_haptic_ctr { + uint8_t cnt; + uint8_t cmd; + uint8_t play; + uint8_t loop; + uint8_t gain; + uint8_t wavseq; + struct list_head list; +}; + +struct aw_i2c_info { + uint8_t flag; + uint8_t reg_num; + uint8_t first_addr; + uint8_t reg_data[AWRW_SIZE]; +}; + +struct aw_haptic_audio { + int delay_val; + int timer_val; + struct mutex lock; + struct hrtimer timer; + struct list_head list; + struct work_struct work; + struct aw_haptic_ctr ctr; + struct list_head ctr_list; +}; + +struct aw_haptic_dts_data { + void *dts_val; + uint8_t type; + uint32_t len; +}; + + +struct aw_haptic_dts_name { + uint8_t *name; + uint8_t flag; +}; + +struct aw_haptic_dts_info { + bool is_enabled_auto_brk; + bool is_enabled_track_en; + + uint32_t mode; + uint32_t f0_cali_percent; + uint32_t f0_pre; + uint32_t lra_vrms; + uint32_t lk_f0_cali; + uint32_t cont_tset; + uint32_t cont_drv1_lvl; + uint32_t cont_drv2_lvl; + uint32_t duration_time[3]; + uint32_t trig_cfg[21]; + /* aw8624 */ + uint32_t cont_td; + uint32_t cont_zc_thr; + uint32_t cont_num_brk; + uint32_t f0_coeff; + uint32_t cont_brake[24]; + uint32_t bemf_config[4]; + uint32_t sw_brake[2]; + uint32_t wavseq[16]; + uint32_t wavloop[10]; + uint32_t td_brake[3]; + uint32_t f0_trace_parameter[4]; + /* aw862xx */ + uint32_t cont_drv1_time; + uint32_t cont_drv2_time; + uint32_t cont_wait_num; + uint32_t cont_brk_time; + uint32_t cont_track_margin; + uint32_t cont_bemf_set; + uint32_t cont_brk_gain; + uint32_t d2s_gain; + uint32_t prctmode[3]; + uint32_t sine_array[4]; +}; + +struct aw_haptic { + bool rtp_init; + bool rtp_loop; + bool ram_init; + bool is_used_irq_pin; + bool is_supported_rtp; + bool is_supported_trig; + + uint8_t flags; + uint8_t play_mode; + uint8_t chipid_flag; + uint8_t dts_name_len; + uint8_t max_pos_beme; + uint8_t max_neg_beme; + uint8_t activate_mode; + uint8_t ram_vbat_comp; + uint8_t seq[AW_SEQUENCER_SIZE]; + uint8_t loop[AW_SEQUENCER_SIZE]; + + int name; + int gain; + int state; + int index; + int irq_gpio; + int duration; + int effect_id; + int amplitude; + int reset_gpio; + + uint32_t f0; + uint32_t lra; + uint32_t vbat; + uint32_t level; + uint32_t f0_pre; + uint32_t cont_f0; + uint32_t rtp_cnt; + uint32_t rtp_len; + uint32_t gun_type; + uint32_t bullet_nr; + uint32_t theory_time; + uint32_t interval_us; + uint32_t f0_cali_data; + uint32_t rtp_file_num; + uint32_t ram_file_num; + uint32_t timeval_flags; + uint32_t osc_cali_data; + uint32_t aw862xx_i2c_addr; + uint64_t microsecond; + + cdev_t vib_dev; + + ktime_t kend; + ktime_t kstart; + ktime_t current_t; + ktime_t pre_enter_t; + + struct mutex lock; + struct device *dev; + struct hrtimer timer; + struct mutex rtp_lock; + struct i2c_client *i2c; + struct aw_haptic_ram ram; + struct aw_haptic_func *func; + struct aw_i2c_info i2c_info; + struct input_dev *input_dev; + struct work_struct rtp_work; + struct delayed_work ram_work; + struct trig trig[AW_TRIG_NUM]; + struct aw_haptic_dts_info info; + struct work_struct vibrator_work; + struct work_struct input_gain_work; + struct work_struct input_vib_work; + struct aw_haptic_container *aw_rtp; + struct aw_haptic_audio haptic_audio; + struct workqueue_struct *work_queue; + struct aw_haptic_dts_name *dts_name_array; + struct pm_qos_request aw_pm_qos_req_vb; +}; + +struct aw_haptic_container { + int len; + uint8_t data[]; +}; + +struct aw_haptic_func { + int (*check_qualify)(struct aw_haptic *); + int (*container_update)(struct aw_haptic *, + struct aw_haptic_container *); + int (*get_irq_state)(struct aw_haptic *); + int (*get_f0)(struct aw_haptic *); + void (*creat_node)(struct aw_haptic *); + void (*read_cont_f0)(struct aw_haptic *); + void (*trig_init)(struct aw_haptic *); + void (*irq_clear)(struct aw_haptic *); + void (*haptic_start)(struct aw_haptic *); + void (*play_stop)(struct aw_haptic *); + void (*play_mode)(struct aw_haptic *, uint8_t); + void (*cont_config)(struct aw_haptic *); + void (*offset_cali)(struct aw_haptic *); + void (*ram_init)(struct aw_haptic *, bool); + void (*misc_para_init)(struct aw_haptic *); + void (*interrupt_setup)(struct aw_haptic *); + void (*vbat_mode_config)(struct aw_haptic *, uint8_t); + void (*protect_config)(struct aw_haptic *, uint8_t, uint8_t); + void (*calculate_cali_data)(struct aw_haptic *); + void (*set_gain)(struct aw_haptic *, uint8_t); + void (*get_gain)(struct aw_haptic *, uint8_t *); + void (*set_wav_seq)(struct aw_haptic *, uint8_t, uint8_t); + void (*get_wav_seq)(struct aw_haptic *, uint32_t); + void (*set_wav_loop)(struct aw_haptic *, uint8_t, uint8_t); + void (*get_wav_loop)(struct aw_haptic *, uint8_t *); + void (*set_rtp_data)(struct aw_haptic *, uint8_t *, uint32_t); + void (*set_fifo_addr)(struct aw_haptic *); + void (*get_fifo_addr)(struct aw_haptic *); + void (*set_ram_data)(struct aw_haptic *, uint8_t *, int); + void (*get_ram_data)(struct aw_haptic *, uint8_t *, int); + void (*set_ram_addr)(struct aw_haptic *); + void (*set_repeat_seq)(struct aw_haptic *, uint8_t); + void (*set_base_addr)(struct aw_haptic *); + void (*set_trim_lra)(struct aw_haptic *, uint8_t); + void (*set_rtp_aei)(struct aw_haptic *, bool); + void (*get_vbat)(struct aw_haptic *); + void (*get_lra_resistance)(struct aw_haptic *); + void (*get_first_wave_addr)(struct aw_haptic *, uint32_t *); + ssize_t (*get_reg)(struct aw_haptic *, ssize_t, char *); + uint8_t (*get_prctmode)(struct aw_haptic *); + uint8_t (*get_glb_state)(struct aw_haptic *); + uint8_t (*get_osc_status)(struct aw_haptic *); + uint8_t (*judge_rtp_going)(struct aw_haptic *); + uint8_t (*rtp_get_fifo_afs)(struct aw_haptic *); + uint64_t (*get_theory_time)(struct aw_haptic *); +}; +/********************************************************* + * + * Function Call + * + *********************************************************/ +#ifdef AW8624_DRIVER +extern struct aw_haptic_func aw8624_func_list; +#endif + +#ifdef AW862XX_COMMON +extern struct aw_haptic_func aw862xx_func_list; +#endif + +#ifdef AW8623X_DRIVER +extern struct aw_haptic_func aw8623x_func_list; +#endif + +extern int haptic_nv_i2c_reads(struct aw_haptic *, uint8_t, uint8_t *, uint32_t); +extern int haptic_nv_i2c_writes(struct aw_haptic *, uint8_t, uint8_t *, uint32_t); +extern int haptic_nv_i2c_write_bits(struct aw_haptic *, uint8_t, uint32_t, uint8_t); +extern ssize_t haptic_nv_read_reg_array(struct aw_haptic *, char *, ssize_t, + uint8_t, uint8_t); +#endif diff --git a/drivers/misc/awinic/aw862x_haptic_nv/haptic_nv_dts.h b/drivers/misc/awinic/aw862x_haptic_nv/haptic_nv_dts.h new file mode 100755 index 000000000000..ddea27cc3ef3 --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv/haptic_nv_dts.h @@ -0,0 +1,101 @@ +#ifndef HAPTIC_NV_DTS_H +#define HAPTIC_NV_DTS_H +#include "haptic_nv.h" + +#ifdef AW8624_DRIVER +struct aw_haptic_dts_name aw8624_dts_name[] = { + {"aw8624_vib_lk_f0_cali", AW_LK_F0_CALI}, + {"aw8624_vib_mode", AW_MODE}, + {"aw8624_vib_f0_pre", AW_F0_PRE}, + {"aw8624_vib_f0_cali_percen", AW_F0_CALI_PERCENT}, + {"aw8624_vib_cont_drv_lev", AW_CONT_DRV1_LVL}, + {"aw8624_vib_cont_drv_lvl_ov", AW_CONT_DRV2_LVL}, + {"aw8624_vib_cont_td", AW_CONT_TD}, + {"aw8624_vib_cont_zc_thr", AW_CONT_ZC_THR}, + {"aw8624_vib_cont_num_brk", AW_CONT_NUM_BRK}, + {"aw8624_vib_f0_coeff", AW_F0_COEFF}, + {"aw8624_vib_tset", AW_CONT_TSET}, + {"aw8624_vib_sw_brake", AW_SW_BRAKE}, + {"aw8624_vib_bemf_config", AW_BEMF_CONFIG}, + {"aw8624_vib_duration_time", AW_DURATION_TIME}, + {"aw8624_vib_trig_config", AW_TRIG_CONFIG}, + {"aw8624_vib_brake_cont_config", AW_BRAKE_CONT_CONFIG}, + {"aw8624_vib_f0_trace_parameter", AW_F0_TRACE_PARAM}, +}; +extern struct aw_haptic_dts_name aw8624_dts_name[]; +#endif + +#ifdef AW8622X_DRIVER +struct aw_haptic_dts_name aw8622x_dts_name[] = { + {"aw8622x_vib_lk_f0_cali", AW_LK_F0_CALI}, + {"aw8622x_vib_mode", AW_MODE}, + {"aw8622x_vib_f0_pre", AW_F0_PRE}, + {"aw8622x_vib_f0_cali_percen", AW_F0_CALI_PERCENT}, + {"aw8622x_vib_cont_drv1_lvl", AW_CONT_DRV1_LVL}, + {"aw8622x_vib_lra_vrms", AW_LRA_VRMS}, + {"aw8622x_vib_cont_drv1_time", AW_CONT_DRV1_TIME}, + {"aw8622x_vib_cont_drv2_time", AW_CONT_DRV2_TIME}, + {"aw8622x_vib_cont_wait_num", AW_CONT_WAIT_NUM}, + {"aw8622x_vib_cont_brk_gain", AW_CONT_BRK_GAIN}, + {"aw8622x_vib_cont_tset", AW_CONT_TSET}, + {"aw8622x_vib_cont_bemf_set", AW_CONT_BEMF_SET}, + {"aw8622x_vib_d2s_gain", AW_D2S_GAIN}, + {"aw8622x_vib_cont_brk_time", AW_CONT_BRK_TIME}, + {"aw8622x_vib_cont_track_margin", AW_CONT_TRACK_MARGIN}, + {"aw8622x_vib_sine_array", AW_SINE_ARRAY}, + {"aw8622x_vib_trig_config", AW_TRIG_CONFIG}, + {"aw8622x_vib_duration_time", AW_DURATION_TIME}, + {"aw8622x_vib_is_enabled_auto_brk", AW_ENABLE_AUTO_BRK}, + {"aw8622x_vib_is_enabled_track_en", AW_ENABLE_TRACK_EN}, +}; +extern struct aw_haptic_dts_name aw8622x_dts_name[]; +#endif + +#ifdef AW86214_DRIVER +struct aw_haptic_dts_name aw86214_dts_name[] = { + {"aw86214_vib_lk_f0_cali", AW_LK_F0_CALI}, + {"aw86214_vib_mode", AW_MODE}, + {"aw86214_vib_f0_ref", AW_F0_PRE}, + {"aw86214_vib_f0_cali_percent", AW_F0_CALI_PERCENT}, + {"aw86214_vib_cont_drv1_lvl", AW_CONT_DRV1_LVL}, + {"aw86214_vib_lra_vrms", AW_LRA_VRMS}, + {"aw86214_vib_cont_drv1_time", AW_CONT_DRV1_TIME}, + {"aw86214_vib_cont_drv2_time", AW_CONT_DRV2_TIME}, + {"aw86214_vib_cont_brk_time", AW_CONT_BRK_TIME}, + {"aw86214_vib_cont_wait_num", AW_CONT_WAIT_NUM}, + {"aw86214_vib_cont_tset", AW_CONT_TSET}, + {"aw86214_vib_cont_bemf_set", AW_CONT_BEMF_SET}, + {"aw86214_vib_d2s_gain", AW_D2S_GAIN}, + {"aw86214_vib_cont_track_margin", AW_CONT_TRACK_MARGIN}, + {"aw86214_vib_sine_array", AW_SINE_ARRAY}, + {"aw86214_vib_duration_time", AW_DURATION_TIME}, + {"aw86214_vib_is_enabled_track_en", AW_ENABLE_TRACK_EN}, +}; +extern struct aw_haptic_dts_name aw86214_dts_name[]; +#endif + +#ifdef AW8623X_DRIVER +struct aw_haptic_dts_name aw8623x_dts_name[] = { + {"aw8623x_vib_lk_f0_cali", AW_LK_F0_CALI}, + {"aw8623x_vib_mode", AW_MODE}, + {"aw8623x_vib_f0_pre", AW_F0_PRE}, + {"aw8623x_vib_f0_cali_percen", AW_F0_CALI_PERCENT}, + {"aw8623x_vib_cont_drv1_lvl", AW_CONT_DRV1_LVL}, + {"aw8623x_vib_lra_vrms", AW_LRA_VRMS}, + {"aw8623x_vib_cont_drv1_time", AW_CONT_DRV1_TIME}, + {"aw8623x_vib_cont_drv2_time", AW_CONT_DRV2_TIME}, + {"aw8623x_vib_cont_wait_num", AW_CONT_WAIT_NUM}, + {"aw8623x_vib_cont_brk_gain", AW_CONT_BRK_GAIN}, + {"aw8623x_vib_cont_tset", AW_CONT_TSET}, + {"aw8623x_vib_cont_bemf_set", AW_CONT_BEMF_SET}, + {"aw8623x_vib_d2s_gain", AW_D2S_GAIN}, + {"aw8623x_vib_cont_brk_time", AW_CONT_BRK_TIME}, + {"aw8623x_vib_cont_track_margin", AW_CONT_TRACK_MARGIN}, + {"aw8623x_vib_trig_config", AW_TRIG_CONFIG}, + {"aw8623x_vib_duration_time", AW_DURATION_TIME}, + {"aw8623x_vib_is_enabled_auto_brk", AW_ENABLE_AUTO_BRK}, + {"aw8623x_vib_is_enabled_track_en", AW_ENABLE_TRACK_EN}, +}; +extern struct aw_haptic_dts_name aw8623x_dts_name[]; +#endif +#endif \ No newline at end of file diff --git a/drivers/misc/awinic/aw862x_haptic_nv/haptic_nv_reg.h b/drivers/misc/awinic/aw862x_haptic_nv/haptic_nv_reg.h new file mode 100755 index 000000000000..375392d42fd0 --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv/haptic_nv_reg.h @@ -0,0 +1,1491 @@ +#ifndef _HAPTIC_NV_REG_H_ +#define _HAPTIC_NV_REG_H_ +/******************************************** + * common Register List + *******************************************/ +#define AW_REG_ID (0x00) +#define AW_REG_CHIPIDH (0x57) /* AW8623X */ +#define AW_REG_CHIPIDL (0x69) /* AW8623X */ +/******************************************** + * AW862XX Register List + *******************************************/ +#define AW862XX_REG_ID (0x00) +#define AW862XX_REG_SYSST (0x01) +#define AW862XX_REG_SYSINT (0x02) +#define AW862XX_REG_SYSINTM (0x03) +#define AW862XX_REG_SYSST2 (0x04) +#define AW862XX_REG_SYSER (0x05) +#define AW862XX_REG_PLAYCFG2 (0x07) +#define AW862XX_REG_PLAYCFG3 (0x08) +#define AW862XX_REG_PLAYCFG4 (0x09) +#define AW862XX_REG_WAVCFG1 (0x0A) +#define AW862XX_REG_WAVCFG2 (0x0B) +#define AW862XX_REG_WAVCFG3 (0x0C) +#define AW862XX_REG_WAVCFG4 (0x0D) +#define AW862XX_REG_WAVCFG5 (0x0E) +#define AW862XX_REG_WAVCFG6 (0x0F) +#define AW862XX_REG_WAVCFG7 (0x10) +#define AW862XX_REG_WAVCFG8 (0x11) +#define AW862XX_REG_WAVCFG9 (0x12) +#define AW862XX_REG_WAVCFG10 (0x13) +#define AW862XX_REG_WAVCFG11 (0x14) +#define AW862XX_REG_WAVCFG12 (0x15) +#define AW862XX_REG_WAVCFG13 (0x16) +#define AW862XX_REG_CONTCFG1 (0x18) +#define AW862XX_REG_CONTCFG2 (0x19) +#define AW862XX_REG_CONTCFG3 (0x1A) +#define AW862XX_REG_CONTCFG4 (0x1B) +#define AW862XX_REG_CONTCFG5 (0x1C) +#define AW862XX_REG_CONTCFG6 (0x1D) +#define AW862XX_REG_CONTCFG7 (0x1E) +#define AW862XX_REG_CONTCFG8 (0x1F) +#define AW862XX_REG_CONTCFG9 (0x20) +#define AW862XX_REG_CONTCFG10 (0x21) +#define AW862XX_REG_CONTCFG11 (0x22) +#define AW862XX_REG_CONTCFG12 (0x23) +#define AW862XX_REG_CONTCFG13 (0x24) +#define AW862XX_REG_CONTRD14 (0x25) +#define AW862XX_REG_CONTRD15 (0x26) +#define AW862XX_REG_CONTRD16 (0x27) +#define AW862XX_REG_CONTRD17 (0x28) +#define AW862XX_REG_CONTRD18 (0x29) +#define AW862XX_REG_CONTRD19 (0x2A) +#define AW862XX_REG_CONTRD20 (0x2B) +#define AW862XX_REG_CONTRD21 (0x2C) +#define AW862XX_REG_RTPCFG1 (0x2D) +#define AW862XX_REG_RTPCFG2 (0x2E) +#define AW862XX_REG_RTPCFG3 (0x2F) +#define AW862XX_REG_RTPCFG4 (0x30) +#define AW862XX_REG_RTPCFG5 (0x31) +#define AW862XX_REG_RTPDATA (0x32) +#define AW862XX_REG_TRGCFG1 (0x33) +#define AW862XX_REG_TRGCFG2 (0x34) +#define AW862XX_REG_TRGCFG3 (0x35) +#define AW862XX_REG_TRGCFG4 (0x36) +#define AW862XX_REG_TRGCFG5 (0x37) +#define AW862XX_REG_TRGCFG6 (0x38) +#define AW862XX_REG_TRGCFG7 (0x39) +#define AW862XX_REG_TRGCFG8 (0x3A) +#define AW862XX_REG_GLBCFG2 (0x3C) +#define AW862XX_REG_GLBCFG4 (0x3E) +#define AW862XX_REG_GLBRD5 (0x3F) +#define AW862XX_REG_RAMADDRH (0x40) +#define AW862XX_REG_RAMADDRL (0x41) +#define AW862XX_REG_RAMDATA (0x42) +#define AW862XX_REG_SYSCTRL1 (0x43) +#define AW862XX_REG_SYSCTRL2 (0x44) +#define AW862XX_REG_SYSCTRL3 (0x45) +#define AW862XX_REG_SYSCTRL4 (0x46) +#define AW862XX_REG_SYSCTRL5 (0x47) +#define AW862XX_REG_SYSCTRL6 (0x48) +#define AW862XX_REG_SYSCTRL7 (0x49) +#define AW862XX_REG_PWMCFG1 (0x4C) +#define AW862XX_REG_PWMCFG3 (0x4E) +#define AW862XX_REG_PWMCFG4 (0x4F) +#define AW862XX_REG_DETCFG1 (0x51) +#define AW862XX_REG_DETCFG2 (0x52) +#define AW862XX_REG_DET_RL (0x53) +#define AW862XX_REG_DET_VBAT (0x55) +#define AW862XX_REG_DET_LO (0x57) +#define AW862XX_REG_TRIMCFG1 (0x58) +#define AW862XX_REG_TRIMCFG3 (0x5A) +#define AW862XX_REG_EFRD9 (0x64) +#define AW862XX_REG_ANACFG8 (0x77) +/******************************************** + * AW8624 Register List + *******************************************/ +#define AW8624_REG_ID (0x00) +#define AW8624_REG_SYSST (0x01) +#define AW8624_REG_SYSINT (0x02) +#define AW8624_REG_SYSINTM (0x03) +#define AW8624_REG_SYSCTRL (0x04) +#define AW8624_REG_GO (0x05) +#define AW8624_REG_RTP_DATA (0x06) +#define AW8624_REG_WAVSEQ1 (0x07) +#define AW8624_REG_WAVSEQ2 (0x08) +#define AW8624_REG_WAVSEQ3 (0x09) +#define AW8624_REG_WAVSEQ4 (0x0a) +#define AW8624_REG_WAVSEQ5 (0x0b) +#define AW8624_REG_WAVSEQ6 (0x0c) +#define AW8624_REG_WAVSEQ7 (0x0d) +#define AW8624_REG_WAVSEQ8 (0x0e) +#define AW8624_REG_WAVLOOP1 (0x0f) +#define AW8624_REG_WAVLOOP2 (0x10) +#define AW8624_REG_WAVLOOP3 (0x11) +#define AW8624_REG_WAVLOOP4 (0x12) +#define AW8624_REG_MANLOOP (0x13) +#define AW8624_REG_TRG1_SEQP (0x14) +#define AW8624_REG_TRG1_SEQN (0x17) +#define AW8624_REG_TRG_CFG1 (0x1b) +#define AW8624_REG_TRG_CFG2 (0x1c) +#define AW8624_REG_DBGCTRL (0x20) +#define AW8624_REG_BASE_ADDRH (0x21) +#define AW8624_REG_BASE_ADDRL (0x22) +#define AW8624_REG_FIFO_AEH (0x23) +#define AW8624_REG_FIFO_AEL (0x24) +#define AW8624_REG_FIFO_AFH (0x25) +#define AW8624_REG_FIFO_AFL (0x26) +#define AW8624_REG_DATCTRL (0x2b) +#define AW8624_REG_PWMPRC (0x2d) +#define AW8624_REG_PWMDBG (0x2e) +#define AW8624_REG_DBGSTAT (0x30) +#define AW8624_REG_WAVECTRL (0x31) +#define AW8624_REG_BRAKE0_CTRL (0x32) +#define AW8624_REG_BRAKE1_CTRL (0x33) +#define AW8624_REG_BRAKE2_CTRL (0x34) +#define AW8624_REG_BRAKE_NUM (0x35) +#define AW8624_REG_ANACTRL (0x38) +#define AW8624_REG_SW_BRAKE (0x39) +#define AW8624_REG_DATDBG (0x3b) +#define AW8624_REG_PRLVL (0x3e) +#define AW8624_REG_PRTIME (0x3f) +#define AW8624_REG_RAMADDRH (0x40) +#define AW8624_REG_RAMADDRL (0x41) +#define AW8624_REG_RAMDATA (0x42) +#define AW8624_REG_BRA_MAX_NUM (0x44) +#define AW8624_REG_GLB_STATE (0x47) +#define AW8624_REG_CONT_CTRL (0x48) +#define AW8624_REG_F_PRE_H (0x49) +#define AW8624_REG_F_PRE_L (0x4a) +#define AW8624_REG_TD_H (0x4b) +#define AW8624_REG_TD_L (0x4c) +#define AW8624_REG_TSET (0x4d) +#define AW8624_REG_THRS_BRA_END (0x4f) +#define AW8624_REG_EF_RDATAH (0x55) +#define AW8624_REG_TRIM_LRA (0x5b) +#define AW8624_REG_R_SPARE (0x5d) +#define AW8624_REG_D2SCFG (0x5e) +#define AW8624_REG_DETCTRL (0x5f) +#define AW8624_REG_RLDET (0x60) +#define AW8624_REG_OSDET (0x61) +#define AW8624_REG_VBATDET (0x62) +#define AW8624_REG_ADCTEST (0x66) +#define AW8624_REG_F_LRA_F0_H (0x68) +#define AW8624_REG_F_LRA_F0_L (0x69) +#define AW8624_REG_F_LRA_CONT_H (0x6a) +#define AW8624_REG_F_LRA_CONT_L (0x6b) +#define AW8624_REG_WAIT_VOL_MP (0x6e) +#define AW8624_REG_WAIT_VOL_MN (0x6f) +#define AW8624_REG_ZC_THRSH_H (0x72) +#define AW8624_REG_ZC_THRSH_L (0x73) +#define AW8624_REG_BEMF_VTHH_H (0x74) +#define AW8624_REG_BEMF_VTHH_L (0x75) +#define AW8624_REG_BEMF_VTHL_H (0x76) +#define AW8624_REG_BEMF_VTHL_L (0x77) +#define AW8624_REG_BEMF_NUM (0x78) +#define AW8624_REG_DRV_TIME (0x79) +#define AW8624_REG_TIME_NZC (0x7a) +#define AW8624_REG_DRV_LVL (0x7b) +#define AW8624_REG_DRV_LVL_OV (0x7c) +#define AW8624_REG_NUM_F0_1 (0x7d) +#define AW8624_REG_NUM_F0_2 (0x7e) +#define AW8624_REG_NUM_F0_3 (0x7f) +/******************************************** + * AW8623X Register List + *******************************************/ +#define AW8623X_REG_RSTCFG (0x00) +#define AW8623X_REG_SYSST (0x01) +#define AW8623X_REG_SYSINT (0x02) +#define AW8623X_REG_SYSINTM (0x03) +#define AW8623X_REG_SYSST2 (0x04) +#define AW8623X_REG_SYSER (0x05) +#define AW8623X_REG_PLAYCFG2 (0x07) +#define AW8623X_REG_PLAYCFG3 (0x08) +#define AW8623X_REG_PLAYCFG4 (0x09) +#define AW8623X_REG_WAVCFG1 (0x0A) +#define AW8623X_REG_WAVCFG2 (0x0B) +#define AW8623X_REG_WAVCFG3 (0x0C) +#define AW8623X_REG_WAVCFG4 (0x0D) +#define AW8623X_REG_WAVCFG5 (0x0E) +#define AW8623X_REG_WAVCFG6 (0x0F) +#define AW8623X_REG_WAVCFG7 (0x10) +#define AW8623X_REG_WAVCFG8 (0x11) +#define AW8623X_REG_WAVCFG9 (0x12) +#define AW8623X_REG_WAVCFG10 (0x13) +#define AW8623X_REG_WAVCFG11 (0x14) +#define AW8623X_REG_WAVCFG12 (0x15) +#define AW8623X_REG_WAVCFG13 (0x16) +#define AW8623X_REG_CONTCFG1 (0x17) +#define AW8623X_REG_CONTCFG2 (0x18) +#define AW8623X_REG_CONTCFG3 (0x19) +#define AW8623X_REG_CONTCFG4 (0x1A) +#define AW8623X_REG_CONTCFG5 (0x1B) +#define AW8623X_REG_CONTCFG6 (0x1C) +#define AW8623X_REG_CONTCFG7 (0x1D) +#define AW8623X_REG_CONTCFG8 (0x1E) +#define AW8623X_REG_CONTCFG9 (0x1F) +#define AW8623X_REG_CONTCFG10 (0x20) +#define AW8623X_REG_CONTCFG11 (0x21) +#define AW8623X_REG_CONTCFG12 (0x22) +#define AW8623X_REG_CONTCFG13 (0x23) +#define AW8623X_REG_CONTRD14 (0x24) +#define AW8623X_REG_CONTRD15 (0x25) +#define AW8623X_REG_CONTRD16 (0x26) +#define AW8623X_REG_CONTRD17 (0x27) +#define AW8623X_REG_CONTRD18 (0x28) +#define AW8623X_REG_CONTRD19 (0x29) +#define AW8623X_REG_CONTRD20 (0x2A) +#define AW8623X_REG_CONTRD21 (0x2B) +#define AW8623X_REG_RTPCFG1 (0x2C) +#define AW8623X_REG_RTPCFG2 (0x2D) +#define AW8623X_REG_RTPCFG3 (0x2E) +#define AW8623X_REG_RTPCFG4 (0x2F) +#define AW8623X_REG_RTPCFG5 (0x30) +#define AW8623X_REG_RTPCFG6 (0x31) +#define AW8623X_REG_RTPDATA (0x32) +#define AW8623X_REG_TRGCFG1 (0x33) +#define AW8623X_REG_TRGCFG2 (0x34) +#define AW8623X_REG_TRGCFG3 (0x35) +#define AW8623X_REG_TRGCFG4 (0x36) +#define AW8623X_REG_TRGCFG5 (0x37) +#define AW8623X_REG_TRGCFG6 (0x38) +#define AW8623X_REG_TRGCFG7 (0x39) +#define AW8623X_REG_TRGCFG8 (0x3A) +#define AW8623X_REG_GLBCFG2 (0x3C) +#define AW8623X_REG_GLBCFG4 (0x3E) +#define AW8623X_REG_GLBRD5 (0x3F) +#define AW8623X_REG_RAMADDRH (0x40) +#define AW8623X_REG_RAMADDRL (0x41) +#define AW8623X_REG_RAMDATA (0x42) +#define AW8623X_REG_TRGCFG9 (0x43) +#define AW8623X_REG_I2SCFG1 (0x44) +#define AW8623X_REG_SYSCTRL1 (0x45) +#define AW8623X_REG_SYSCTRL2 (0x46) +#define AW8623X_REG_SYSCTRL3 (0x47) +#define AW8623X_REG_SYSCTRL4 (0x48) +#define AW8623X_REG_SYSCTRL5 (0x49) +#define AW8623X_REG_PWMCFG1 (0x4A) +#define AW8623X_REG_PWMCFG3 (0x4C) +#define AW8623X_REG_PWMCFG4 (0x4D) +#define AW8623X_REG_VBATCTRL (0x4E) +#define AW8623X_REG_DETCFG1 (0x4F) +#define AW8623X_REG_DETCFG2 (0x50) +#define AW8623X_REG_DETRD1 (0x51) +#define AW8623X_REG_DETRD2 (0x52) +#define AW8623X_REG_DETRD3 (0x53) +#define AW8623X_REG_IDH (0x57) +#define AW8623X_REG_IDL (0x58) +#define AW8623X_REG_TRIMCFG1 (0x59) +#define AW8623X_REG_TRIMCFG2 (0x5A) +#define AW8623X_REG_TRIMCFG3 (0x5B) +#define AW8623X_REG_AUTOSIN1 (0x5C) +#define AW8623X_REG_AUTOSIN2 (0x5D) +#define AW8623X_REG_EFCFG12 (0x6A) +#define AW8623X_REG_ANACFG3 (0x70) +#define AW8623X_REG_ANACFG4 (0x71) +#define AW8623X_REG_ANACFG10 (0x77) +/****************************************************** + * Common Register Detail + *****************************************************/ +#define AW_BIT_RESET (0xAA) +/* GLB_STATE */ +#define AW_BIT_GLBRD_STATE_MASK (15<<0) +#define AW_BIT_STATE_STANDBY (0<<0) +#define AW_BIT_STATE_WAKEUP (1<<0) +#define AW_BIT_STATE_STARTUP (2<<0) +#define AW_BIT_STATE_WAIT (3<<0) +#define AW_BIT_STATE_CONT_GO (6<<0) +#define AW_BIT_STATE_RAM_GO (7<<0) +#define AW_BIT_STATE_RTP_GO (8<<0) +#define AW_BIT_STATE_TRIG_GO (9<<0) +#define AW_BIT_STATE_BRAKE (11<<0) +#define AW_BIT_STATE_END (12<<0) +/****************************************************** + * AW862XX Register Detail + *****************************************************/ +/* SYSST: reg 0x01 RO */ +#define AW862XX_BIT_SYSST_UVLS (1<<5) +#define AW862XX_BIT_SYSST_FF_AES (1<<4) +#define AW862XX_BIT_SYSST_FF_AFS (1<<3) +#define AW862XX_BIT_SYSST_OCDS (1<<2) +#define AW862XX_BIT_SYSST_OTS (1<<1) +#define AW862XX_BIT_SYSST_DONES (1<<0) + +/* SYSINT: reg 0x02 RC */ +#define AW862XX_BIT_SYSINT_UVLI (1<<5) +#define AW862XX_BIT_SYSINT_FF_AEI (1<<4) +#define AW862XX_BIT_SYSINT_FF_AFI (1<<3) +#define AW862XX_BIT_SYSINT_OCDI (1<<2) +#define AW862XX_BIT_SYSINT_OTI (1<<1) +#define AW862XX_BIT_SYSINT_DONEI (1<<0) + +/* SYSINTM: reg 0x03 RW */ +#define AW862XX_BIT_SYSINTM_UVLM_MASK (~(1<<5)) +#define AW862XX_BIT_SYSINTM_UVLM_OFF (1<<5) +#define AW862XX_BIT_SYSINTM_UVLM_ON (0<<5) +#define AW862XX_BIT_SYSINTM_FF_AEM_MASK (~(1<<4)) +#define AW862XX_BIT_SYSINTM_FF_AEM_OFF (1<<4) +#define AW862XX_BIT_SYSINTM_FF_AEM_ON (0<<4) +#define AW862XX_BIT_SYSINTM_FF_AFM_MASK (~(1<<3)) +#define AW862XX_BIT_SYSINTM_FF_AFM_OFF (1<<3) +#define AW862XX_BIT_SYSINTM_FF_AFM_ON (0<<3) +#define AW862XX_BIT_SYSINTM_OCDM_MASK (~(1<<2)) +#define AW862XX_BIT_SYSINTM_OCDM_OFF (1<<2) +#define AW862XX_BIT_SYSINTM_OCDM_ON (0<<2) +#define AW862XX_BIT_SYSINTM_OTM_MASK (~(1<<1)) +#define AW862XX_BIT_SYSINTM_OTM_OFF (1<<1) +#define AW862XX_BIT_SYSINTM_OTM_ON (0<<1) +#define AW862XX_BIT_SYSINTM_DONEM_MASK (~(1<<0)) +#define AW862XX_BIT_SYSINTM_DONEM_OFF (1<<0) +#define AW862XX_BIT_SYSINTM_DONEM_ON (0<<0) + +/* SYSST2: reg 0x04 RO */ +#define AW862XX_BIT_SYSST2_RAM_ADDR_ER (1<<7) +#define AW862XX_BIT_SYSST2_TRG_ADDR_ER (1<<6) +#define AW862XX_BIT_SYSST2_VBG_OK (1<<3) +#define AW862XX_BIT_SYSST2_LDO_OK (1<<2) +#define AW862XX_BIT_SYSST2_FF_FULL (1<<1) +#define AW862XX_BIT_SYSST2_FF_EMPTY (1<<0) + +/* SYSER: reg 0x05 RC */ +#define AW862XX_BIT_SYSER_I2S_ERR (1<<7) +#define AW862XX_BIT_SYSER_TRIG1_EVENT (1<<6) +#define AW862XX_BIT_SYSER_TRIG2_EVENT (1<<5) +#define AW862XX_BIT_SYSER_TRIG3_EVENT (1<<4) +#define AW862XX_BIT_SYSER_OV (1<<3) +#define AW862XX_BIT_SYSER_ADDR_ER (1<<2) +#define AW862XX_BIT_SYSER_FF_ER (1<<1) +#define AW862XX_BIT_SYSER_PLL_REF_ER (1<<0) + +/* PLAYCFG3: reg 0x08 RW */ +#define AW862XX_BIT_PLAYCFG3_STOP_MODE_MASK (~(1<<5)) +#define AW862XX_BIT_PLAYCFG3_STOP_MODE_NOW (1<<5) +#define AW862XX_BIT_PLAYCFG3_STOP_MODE_LATER (0<<5) +#define AW862XX_BIT_PLAYCFG3_BRK_EN_MASK (~(1<<2)) +#define AW862XX_BIT_PLAYCFG3_BRK (1<<2) +#define AW862XX_BIT_PLAYCFG3_BRK_ENABLE (1<<2) +#define AW862XX_BIT_PLAYCFG3_BRK_DISABLE (0<<2) +#define AW862XX_BIT_PLAYCFG3_PLAY_MODE_MASK (~(3<<0)) +#define AW862XX_BIT_PLAYCFG3_PLAY_MODE_STOP (3<<0) +#define AW862XX_BIT_PLAYCFG3_PLAY_MODE_CONT (2<<0) +#define AW862XX_BIT_PLAYCFG3_PLAY_MODE_RTP (1<<0) +#define AW862XX_BIT_PLAYCFG3_PLAY_MODE_RAM (0<<0) + +/* PLAYCFG4: reg 0x09 RW */ +#define AW862XX_BIT_PLAYCFG4_STOP_MASK (~(1<<1)) +#define AW862XX_BIT_PLAYCFG4_STOP_ON (1<<1) +#define AW862XX_BIT_PLAYCFG4_STOP_OFF (0<<1) +#define AW862XX_BIT_PLAYCFG4_GO_MASK (~(1<<0)) +#define AW862XX_BIT_PLAYCFG4_GO_ON (1<<0) +#define AW862XX_BIT_PLAYCFG4_GO_OFF (0<<0) + +/* WAVCFG1-8: reg 0x0A - reg 0x11 RW */ +#define AW862XX_BIT_WAVCFG_SEQWAIT_MASK (~(1<<7)) +#define AW862XX_BIT_WAVCFG_SEQWAIT_TIME (1<<7) +#define AW862XX_BIT_WAVCFG_SEQWAIT_NUMBER (0<<7) +#define AW862XX_BIT_WAVCFG_SEQ (0x7F) + +/* WAVCFG9-12: reg 0x12 - reg 0x15 RW */ +#define AW862XX_BIT_WAVLOOP_SEQ_ODD_MASK (~(0x0F<<4)) +#define AW862XX_BIT_WAVLOOP_SEQ_ODD_INIFINITELY (0x0F<<4) +#define AW862XX_BIT_WAVLOOP_SEQ_EVEN_MASK (~(0x0F<<0)) +#define AW862XX_BIT_WAVLOOP_SEQ_EVEN_INIFINITELY (0x0F<<0) +#define AW862XX_BIT_WAVLOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG9: reg 0x12 RW */ +#define AW862XX_BIT_WAVCFG9_SEQ1LOOP_MASK (~(0x0F<<4)) +#define AW862XX_BIT_WAVCFG9_SEQ1LOOP_INIFINITELY (0x0F<<4) +#define AW862XX_BIT_WAVCFG9_SEQ2LOOP_MASK (~(0x0F<<0)) +#define AW862XX_BIT_WAVCFG9_SEQ2LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG10: reg 0x13 RW */ +#define AW862XX_BIT_WAVCFG10_SEQ3LOOP_MASK (~(0x0F<<4)) +#define AW862XX_BIT_WAVCFG10_SEQ3LOOP_INIFINITELY (0x0F<<4) +#define AW862XX_BIT_WAVCFG10_SEQ4LOOP_MASK (~(0x0F<<0)) +#define AW862XX_BIT_WAVCFG10_SEQ4LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG11: reg 0x14 RW */ +#define AW862XX_BIT_WAVCFG11_SEQ5LOOP_MASK (~(0x0F<<4)) +#define AW862XX_BIT_WAVCFG11_SEQ5LOOP_INIFINITELY (0x0F<<4) +#define AW862XX_BIT_WAVCFG11_SEQ6LOOP_MASK (~(0x0F<<0)) +#define AW862XX_BIT_WAVCFG11_SEQ6LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG12: reg 0x15 RW */ +#define AW862XX_BIT_WAVCFG12_SEQ7LOOP_MASK (~(0x0F<<4)) +#define AW862XX_BIT_WAVCFG12_SEQ7LOOP_INIFINITELY (0x0F<<4) +#define AW862XX_BIT_WAVCFG12_SEQ8LOOP_MASK (~(0x0F<<0)) +#define AW862XX_BIT_WAVCFG12_SEQ8LOOP_INIFINITELY (0x0F<<0) + +/***************** CONT *****************/ +/* CONTCFG1: reg 0x18 RW */ +#define AW862XX_BIT_CONTCFG1_EDGE_FRE_MASK (~(0x0F<<4)) +#define AW862XX_BIT_CONTCFG1_EN_F0_DET_MASK (~(1<<3)) +#define AW862XX_BIT_CONTCFG1_F0_DET_ENABLE (1<<3) +#define AW862XX_BIT_CONTCFG1_F0_DET_DISABLE (0<<3) +#define AW862XX_BIT_CONTCFG1_SIN_MODE_MASK (~(1<<0)) +#define AW862XX_BIT_CONTCFG1_SIN_MODE_COS (1<<0) +#define AW862XX_BIT_CONTCFG1_SIN_MODE_SINE (0<<0) +/* CONTCFG3: reg 0x1A RW */ +/* DRV_WIDTH */ +#define AW862XX_BIT_DRV_WIDTH_DEF_VAL (0x6A) + +/* CONTCFG5: reg 0x1C RW */ +#define AW862XX_BIT_CONTCFG5_BRK_GAIN_MASK (~(0x0F<<0)) + +/* CONTCFG6: reg 0x1D RW */ +#define AW862XX_BIT_CONTCFG6_TRACK_EN_MASK (~(1<<7)) +#define AW862XX_BIT_CONTCFG6_TRACK_ENABLE (1<<7) +#define AW862XX_BIT_CONTCFG6_TRACK_DISABLE (0<<7) +#define AW862XX_BIT_CONTCFG6_DRV1_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG7: reg 0x1E RW */ +#define AW862XX_BIT_CONTCFG7_DRV2_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG13: reg 0x24 RW */ +#define AW862XX_BIT_CONTCFG13_TSET_MASK (~(0x0F<<4)) +#define AW862XX_BIT_CONTCFG13_BEME_SET_MASK (~(0x0F<<0)) + +/***************** RTP *****************/ +/* RTPCFG1: reg 0x2D RW */ +#define AW862XX_BIT_RTPCFG1_ADDRH_MASK (~(0x0F<<0)) + +#define AW862XX_BIT_RTPCFG1_SRAM_SIZE_2K_MASK (~(1<<5)) +#define AW862XX_BIT_RTPCFG1_SRAM_SIZE_2K_EN (1<<5) +#define AW862XX_BIT_RTPCFG1_SRAM_SIZE_2K_DIS (0<<5) + +#define AW862XX_BIT_RTPCFG1_SRAM_SIZE_1K_MASK (~(1<<4)) +#define AW862XX_BIT_RTPCFG1_SRAM_SIZE_1K_EN (1<<4) +#define AW862XX_BIT_RTPCFG1_SRAM_SIZE_1K_DIS (0<<4) + +/* RTPCFG3: reg 0x2F RW */ +#define AW862XX_BIT_RTPCFG3_FIFO_AEH_MASK (~(0x0F<<4)) +#define AW862XX_BIT_RTPCFG3_FIFO_AFH_MASK (~(0x0F<<0)) +#define AW862XX_BIT_RTPCFG3_FIFO_AEH (0x0F<<4) +#define AW862XX_BIT_RTPCFG3_FIFO_AFH (0x0F<<0) + +/***************** TRIGGER *****************/ +#define AW862XX_BIT_TRG_ENABLE_MASK (~(1<<7)) +#define AW862XX_BIT_TRG_ENABLE (1<<7) +#define AW862XX_BIT_TRG_DISABLE (0<<7) +#define AW862XX_BIT_TRG_SEQ_MASK (~(0x7F<<0)) + +/* TRGCFG1: reg 0x33 RW */ +#define AW862XX_BIT_TRGCFG1_TRG1_POS_MASK (~(1<<7)) +#define AW862XX_BIT_TRGCFG1_TRG1_POS_ENABLE (1<<7) +#define AW862XX_BIT_TRGCFG1_TRG1_POS_DISABLE (0<<7) +#define AW862XX_BIT_TRGCFG1_TRG1SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG2: reg 0x34 RW */ +#define AW862XX_BIT_TRGCFG2_TRG2_POS_MASK (~(1<<7)) +#define AW862XX_BIT_TRGCFG2_TRG2_POS_ENABLE (1<<7) +#define AW862XX_BIT_TRGCFG2_TRG2_POS_DISABLE (0<<7) +#define AW862XX_BIT_TRGCFG2_TRG2SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG3: reg 0x35 RW */ +#define AW862XX_BIT_TRGCFG3_TRG3_POS_MASK (~(1<<7)) +#define AW862XX_BIT_TRGCFG3_TRG3_POS_ENABLE (1<<7) +#define AW862XX_BIT_TRGCFG3_TRG3_POS_DISABLE (0<<7) +#define AW862XX_BIT_TRGCFG3_TRG3SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG4: reg 0x36 RW */ +#define AW862XX_BIT_TRGCFG4_TRG1_NEG_MASK (~(1<<7)) +#define AW862XX_BIT_TRGCFG4_TRG1_NEG_ENABLE (1<<7) +#define AW862XX_BIT_TRGCFG4_TRG1_NEG_DISABLE (0<<7) +#define AW862XX_BIT_TRGCFG4_TRG1SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG5: reg 0x37 RW */ +#define AW862XX_BIT_TRGCFG5_TRG2_NEG_MASK (~(1<<7)) +#define AW862XX_BIT_TRGCFG5_TRG2_NEG_ENABLE (1<<7) +#define AW862XX_BIT_TRGCFG5_TRG2_NEG_DISABLE (0<<7) +#define AW862XX_BIT_TRGCFG5_TRG2SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG6: reg 0x38 RW */ +#define AW862XX_BIT_TRGCFG6_TRG3_NEG_MASK (~(1<<7)) +#define AW862XX_BIT_TRGCFG6_TRG3_NEG_ENABLE (1<<7) +#define AW862XX_BIT_TRGCFG6_TRG3_NEG_DISABLE (0<<7) +#define AW862XX_BIT_TRGCFG6_TRG3SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG7: reg 0x39 RW */ +#define AW862XX_BIT_TRGCFG7_TRG1_POR_LEV_BRK_MASK (~(7<<5)) +#define AW862XX_BIT_TRGCFG7_TRG2_POR_LEV_BRK_MASK (~(7<<1)) +#define AW862XX_BIT_TRGCFG7_TRG1_POLAR_MASK (~(1<<7)) +#define AW862XX_BIT_TRGCFG7_TRG1_POLAR_NEG (1<<7) +#define AW862XX_BIT_TRGCFG7_TRG1_POLAR_POS (0<<7) +#define AW862XX_BIT_TRGCFG7_TRG1_MODE_MASK (~(1<<6)) +#define AW862XX_BIT_TRGCFG7_TRG1_MODE_LEVEL (1<<6) +#define AW862XX_BIT_TRGCFG7_TRG1_MODE_EDGE (0<<6) +#define AW862XX_BIT_TRGCFG7_TRG1_AUTO_BRK_MASK (~(1<<5)) +#define AW862XX_BIT_TRGCFG7_TRG1_AUTO_BRK_ENABLE (1<<5) +#define AW862XX_BIT_TRGCFG7_TRG1_AUTO_BRK_DISABLE (0<<5) +#define AW862XX_BIT_TRGCFG7_TRG2_POLAR_MASK (~(1<<3)) +#define AW862XX_BIT_TRGCFG7_TRG2_POLAR_NEG (1<<3) +#define AW862XX_BIT_TRGCFG7_TRG2_POLAR_POS (0<<3) +#define AW862XX_BIT_TRGCFG7_TRG2_MODE_MASK (~(1<<2)) +#define AW862XX_BIT_TRGCFG7_TRG2_MODE_LEVEL (1<<2) +#define AW862XX_BIT_TRGCFG7_TRG2_MODE_EDGE (0<<2) +#define AW862XX_BIT_TRGCFG7_TRG2_AUTO_BRK_MASK (~(1<<1)) +#define AW862XX_BIT_TRGCFG7_TRG2_AUTO_BRK_ENABLE (1<<1) +#define AW862XX_BIT_TRGCFG7_TRG2_AUTO_BRK_DISABLE (0<<1) + +/* TRGCFG8: reg 0x3A RW */ +#define AW862XX_BIT_TRGCFG8_TRG3_POR_LEV_BRK_MASK (~(7<<5)) +#define AW862XX_BIT_TRGCFG8_TRG3_POLAR_MASK (~(1<<7)) +#define AW862XX_BIT_TRGCFG8_TRG3_POLAR_NEG (1<<7) +#define AW862XX_BIT_TRGCFG8_TRG3_POLAR_POS (0<<7) +#define AW862XX_BIT_TRGCFG8_TRG3_MODE_MASK (~(1<<6)) +#define AW862XX_BIT_TRGCFG8_TRG3_MODE_LEVEL (1<<6) +#define AW862XX_BIT_TRGCFG8_TRG3_MODE_EDGE (0<<6) +#define AW862XX_BIT_TRGCFG8_TRG3_AUTO_BRK_MASK (~(1<<5)) +#define AW862XX_BIT_TRGCFG8_TRG3_AUTO_BRK_ENABLE (1<<5) +#define AW862XX_BIT_TRGCFG8_TRG3_AUTO_BRK_DISABLE (0<<5) +#define AW862XX_BIT_TRGCFG8_TRG_TRIG1_MODE_MASK (~(3<<3)) +#define AW862XX_BIT_TRGCFG8_PWM_LRA (0<<3) +#define AW862XX_BIT_TRGCFG8_PWM_ERA (1<<3) +#define AW862XX_BIT_TRGCFG8_TRIG1 (2<<3) +#define AW862XX_BIT_TRGCFG8_DISABLE (3<<3) +#define AW862XX_BIT_TRGCFG8_TRG1_STOP_MASK (~(1<<2)) +#define AW862XX_BIT_TRGCFG8_TRG1_STOP (1<<2) +#define AW862XX_BIT_TRGCFG8_TRG2_STOP_MASK (~(1<<1)) +#define AW862XX_BIT_TRGCFG8_TRG2_STOP (1<<1) +#define AW862XX_BIT_TRGCFG8_TRG3_STOP_MASK (~(1<<0)) +#define AW862XX_BIT_TRGCFG8_TRG3_STOP (1<<0) + +/* GLBCFG2: reg 0x3C RW */ +/* START_DLY */ +#define AW862XX_BIT_GLBCFG2_START_DLY_250US (0x0C) + +/* GLBCFG4: reg 0x3E RW */ +#define AW862XX_BIT_GLBCFG4_GO_PRIO_MASK (~(3<<6)) +#define AW862XX_BIT_GLBCFG4_TRG3_PRIO_MASK (~(3<<4)) +#define AW862XX_BIT_GLBCFG4_TRG2_PRIO_MASK (~(3<<2)) +#define AW862XX_BIT_GLBCFG4_TRG1_PRIO_MASK (~(3<<0)) + +/* GLBRD5: reg 0x3F R0 */ +/* GLB_STATE */ +#define AW862XX_BIT_GLBRD5_STATE (15<<0) +#define AW862XX_BIT_GLBRD5_STATE_STANDBY (0<<0) +#define AW862XX_BIT_GLBRD5_STATE_WAKEUP (1<<0) +#define AW862XX_BIT_GLBRD5_STATE_STARTUP (2<<0) +#define AW862XX_BIT_GLBRD5_STATE_WAIT (3<<0) +#define AW862XX_BIT_GLBRD5_STATE_CONT_GO (6<<0) +#define AW862XX_BIT_GLBRD5_STATE_RAM_GO (7<<0) +#define AW862XX_BIT_GLBRD5_STATE_RTP_GO (8<<0) +#define AW862XX_BIT_GLBRD5_STATE_TRIG_GO (9<<0) +#define AW862XX_BIT_GLBRD5_STATE_I2S_GO (10<<0) +#define AW862XX_BIT_GLBRD5_STATE_BRAKE (11<<0) +#define AW862XX_BIT_GLBRD5_STATE_END (12<<0) +/* RAMADDRH: reg 0x40 RWS */ +#define AW862XX_BIT_RAMADDRH_MASK (~(63<<0)) + +/***************** SYSCTRL *****************/ +/* SYSCTRL1: reg 0x43 RW */ +#define AW862XX_BIT_SYSCTRL1_VBAT_MODE_MASK (~(1<<7)) +#define AW862XX_BIT_SYSCTRL1_VBAT_MODE_HW (1<<7) +#define AW862XX_BIT_SYSCTRL1_VBAT_MODE_SW (0<<7) +#define AW862XX_BIT_SYSCTRL1_PERP_MASK (~(1<<6)) +#define AW862XX_BIT_SYSCTRL1_PERP_ON (1<<6) +#define AW862XX_BIT_SYSCTRL1_PERP_OFF (0<<6) +#define AW862XX_BIT_SYSCTRL1_CLK_SEL_MASK (~(3<<4)) +#define AW862XX_BIT_SYSCTRL1_CLK_SEL_OSC (1<<4) +#define AW862XX_BIT_SYSCTRL1_CLK_SEL_AUTO (0<<4) +#define AW862XX_BIT_SYSCTRL1_RAMINIT_MASK (~(1<<3)) +#define AW862XX_BIT_SYSCTRL1_RAMINIT_ON (1<<3) +#define AW862XX_BIT_SYSCTRL1_RAMINIT_OFF (0<<3) +#define AW862XX_BIT_SYSCTRL1_EN_FIR_MASK (~(1<<2)) +#define AW862XX_BIT_SYSCTRL1_FIR_ENABLE (0<<2) +#define AW862XX_BIT_SYSCTRL1_WAKE_MODE_MASK (~(1<<1)) +#define AW862XX_BIT_SYSCTRL1_WAKE_MODE_WAKEUP (1<<1) +#define AW862XX_BIT_SYSCTRL1_WAKE_MODE_BST (0<<1) +#define AW862XX_BIT_SYSCTRL1_RTP_CLK_MASK (~(1<<0)) +#define AW862XX_BIT_SYSCTRL1_RTP_PLL (1<<0) +#define AW862XX_BIT_SYSCTRL1_RTP_OSC (0<<0) + +/* SYSCTRL2: reg 0x44 RW */ +#define AW862XX_BIT_SYSCTRL2_WAKE_MASK (~(1<<7)) +#define AW862XX_BIT_SYSCTRL2_WAKE_ON (1<<7) +#define AW862XX_BIT_SYSCTRL2_WAKE_OFF (0<<7) +#define AW862XX_BIT_SYSCTRL2_STANDBY_MASK (~(1<<6)) +#define AW862XX_BIT_SYSCTRL2_STANDBY_ON (1<<6) +#define AW862XX_BIT_SYSCTRL2_STANDBY_OFF (0<<6) +#define AW862XX_BIT_SYSCTRL2_RTP_DLY_MASK (~(3<<4)) +#define AW862XX_BIT_SYSCTRL2_INTN_PIN_MASK (~(1<<3)) +#define AW862XX_BIT_SYSCTRL2_INTN (1<<3) +#define AW862XX_BIT_SYSCTRL2_TRIG1 (0<<3) +#define AW862XX_BIT_SYSCTRL2_WCK_PIN_MASK (~(1<<2)) +#define AW862XX_BIT_SYSCTRL2_ENABLE_TRIG2 (1<<2) +#define AW862XX_BIT_SYSCTRL2_DISENABLE_TRIG2 (0<<2) +#define AW862XX_BIT_SYSCTRL2_WAVDAT_MODE_MASK (~(3<<0)) +#define AW862XX_BIT_SYSCTRL2_RATE (3<<0) +#define AW862XX_BIT_SYSCTRL2_RATE_12K (2<<0) +#define AW862XX_BIT_SYSCTRL2_RATE_24K (0<<0) +#define AW862XX_BIT_SYSCTRL2_RATE_48K (1<<0) + +/* SYSCTRL7: reg 0x49 RW */ +#define AW862XX_BIT_SYSCTRL7_GAIN_BYPASS_MASK (~(1<<6)) +#define AW862XX_BIT_SYSCTRL7_GAIN_CHANGEABLE (1<<6) +#define AW862XX_BIT_SYSCTRL7_GAIN_FIXED (0<<6) +#define AW862XX_BIT_SYSCTRL7_GAIN (0x07) + +#define AW862XX_BIT_SYSCTRL7_INT_EDGE_MODE_MASK (~(1<<5)) +#define AW862XX_BIT_SYSCTRL7_INT_EDGE_MODE_POS (0<<5) +#define AW862XX_BIT_SYSCTRL7_INT_EDGE_MODE_BOTH (1<<5) +#define AW862XX_BIT_SYSCTRL7_INT_MODE_MASK (~(1<<4)) +#define AW862XX_BIT_SYSCTRL7_INT_MODE_EDGE (1<<4) +#define AW862XX_BIT_SYSCTRL7_INT_MODE_LEVEL (0<<4) + +#define AW862XX_BIT_SYSCTRL7_INTP_MASK (~(1<<3)) +#define AW862XX_BIT_SYSCTRL7_INTP_HIGH (1<<3) +#define AW862XX_BIT_SYSCTRL7_INTP_LOW (0<<3) +#define AW862XX_BIT_SYSCTRL7_D2S_GAIN_MASK (~(7<<0)) +#define AW862XX_BIT_SYSCTRL7_D2S_GAIN_1 (0<<0) +#define AW862XX_BIT_SYSCTRL7_D2S_GAIN_2 (1<<0) +#define AW862XX_BIT_SYSCTRL7_D2S_GAIN_4 (2<<0) +#define AW862XX_BIT_SYSCTRL7_D2S_GAIN_5 (3<<0) +#define AW862XX_BIT_SYSCTRL7_D2S_GAIN_8 (4<<0) +#define AW862XX_BIT_SYSCTRL7_D2S_GAIN_10 (5<<0) +#define AW862XX_BIT_SYSCTRL7_D2S_GAIN_20 (6<<0) +#define AW862XX_BIT_SYSCTRL7_D2S_GAIN_40 (7<<0) + +/* PWMCFG1: reg 0x4C RW */ +#define AW862XX_BIT_PWMCFG1_PRC_EN_MASK (~(1<<7)) +#define AW862XX_BIT_PWMCFG1_PRC_ENABLE (1<<7) +#define AW862XX_BIT_PWMCFG1_PRC_DISABLE (0<<7) +#define AW862XX_BIT_PWMCFG1_PRCTIME_MASK (~(0x7F<<0)) + +/* PWMCFG2: reg 0x4D RW */ +#define AW862XX_BIT_PWMCFG2_REF_SEL_MASK (~(1<<5)) +#define AW862XX_BIT_PWMCFG2_REF_SEL_TRIANGLE (1<<5) +#define AW862XX_BIT_PWMCFG2_REF_SEL_SAWTOOTH (0<<5) +#define AW862XX_BIT_PWMCFG2_PD_HWM_MASK (~(1<<4)) +#define AW862XX_BIT_PWMCFG2_PD_HWM_ON (1<<4) +#define AW862XX_BIT_PWMCFG2_PWMOE_MASK (~(1<<3)) +#define AW862XX_BIT_PWMCFG2_PWMOE_ON (1<<3) +#define AW862XX_BIT_PWMCFG2_PWMFRC_MASK (~(7<<0)) + +/* PWMCFG3: reg 0x4E RW */ +#define AW862XX_BIT_PWMCFG3_PR_EN_MASK (~(1<<7)) +#define AW862XX_BIT_PWMCFG3_PR_ENABLE (1<<7) +#define AW862XX_BIT_PWMCFG3_PR_DISABLE (0<<7) +#define AW862XX_BIT_PWMCFG3_PRLVL_MASK (~(0x7F<<0)) +#define AW862XX_BIT_PWMCFG3_PRLVL_DEFAULT_VALUE (0x3F) + +/* PWMCFG4: reg 0x4F RW */ +/* PRTIME */ +#define AW862XX_PWMCFG4_PRTIME_DEFAULT_VALUE (0x32) + +/* DETCFG1: reg 0x51 RW */ +#define AW862XX_BIT_DETCFG1_FTS_GO_MASK (~(1<<7)) +#define AW862XX_BIT_DETCFG1_FTS_GO_ENABLE (1<<7) +#define AW862XX_BIT_DETCFG1_TEST_GO_MASK (~(1<<6)) +#define AW862XX_BIT_DETCFG1_TEST_GO_ENABLE (1<<6) +#define AW862XX_BIT_DETCFG1_ADO_SLOT_MODE_MASK (~(1<<5)) +#define AW862XX_BIT_DETCFG1_ADO_SLOT_ADC_32 (1<<5) +#define AW862XX_BIT_DETCFG1_ADO_SLOT_ADC_256 (0<<5) +#define AW862XX_BIT_DETCFG1_RL_OS_MASK (~(1<<4)) +#define AW862XX_BIT_DETCFG1_RL (1<<4) +#define AW862XX_BIT_DETCFG1_OS (0<<4) +#define AW862XX_BIT_DETCFG1_PRCT_MODE_MASK (~(1<<3)) +#define AW862XX_BIT_DETCFG1_PRCT_MODE_INVALID (1<<3) +#define AW862XX_BIT_DETCFG1_PRCT_MODE_VALID (0<<3) +#define AW862XX_BIT_DETCFG1_CLK_ADC_MASK (~(7<<0)) +#define AW862XX_BIT_DETCFG1_CLK_ADC_12M (0<<0) +#define AW862XX_BIT_DETCFG1_CLK_ADC_6M (1<<0) +#define AW862XX_BIT_DETCFG1_CLK_ADC_3M (2<<0) +#define AW862XX_BIT_DETCFG1_CLK_ADC_1M5 (3<<0) +#define AW862XX_BIT_DETCFG1_CLK_ADC_M75 (4<<0) +#define AW862XX_BIT_DETCFG1_CLK_ADC_M37 (5<<0) +#define AW862XX_BIT_DETCFG1_CLK_ADC_M18 (6<<0) +#define AW862XX_BIT_DETCFG1_CLK_ADC_M09 (7<<0) + +/* DETCFG2: reg 0x52 RW */ +#define AW862XX_BIT_DETCFG2_VBAT_GO_MASK (~(1<<1)) +#define AW862XX_BIT_DETCFG2_VABT_GO_ON (1<<1) +#define AW862XX_BIT_DETCFG2_DIAG_GO_MASK (~(1<<0)) +#define AW862XX_BIT_DETCFG2_DIAG_GO_ON (1<<0) + +/* DET_LO: reg 0x57 RW */ +#define AW862XX_BIT_DET_LO_TEST_MASK (~(3<<6)) +#define AW862XX_BIT_DET_LO_VBAT_MASK (~(3<<4)) +#define AW862XX_BIT_DET_LO_VBAT (3<<4) +#define AW862XX_BIT_DET_LO_OS_MASK (~(3<<2)) +#define AW862XX_BIT_DET_LO_RL_MASK (~(3<<0)) +#define AW862XX_BIT_DET_LO_RL (3<<0) + +/* TRIMCFG1: reg:0x58 RW */ +#define AW862XX_BIT_TRIMCFG1_RL_TRIM_SRC_MASK (~(1<<6)) +#define AW862XX_BIT_TRIMCFG1_RL_TRIM_SRC_REG (1<<6) +#define AW862XX_BIT_TRIMCFG1_RL_TRIM_SRC_EFUSE (0<<6) +#define AW862XX_BIT_TRIMCFG1_TRIM_RL_MASK (~(63<<0)) + +/* TRIMCFG3: reg:0x5A RW */ +#define AW862XX_BIT_TRIMCFG3_OSC_TRIM_SRC_MASK (~(1<<7)) +#define AW862XX_BIT_TRIMCFG3_OSC_TRIM_SRC_REG (1<<7) +#define AW862XX_BIT_TRIMCFG3_OSC_TRIM_SRC_EFUSE (0<<7) +#define AW862XX_BIT_TRIMCFG3_TRIM_LRA_MASK (~(63<<0)) + +/* TRIMCFG4: reg:0x5B RW */ +/* TRIM_OSC */ + +/* ANACFG8: reg:0x77 RW */ +#define AW862XX_BIT_ANACFG8_TRTF_CTRL_HDRV_MASK (~(3<<6)) +#define AW862XX_BIT_ANACFG8_TRTF_CTRL_HDRV (3<<6) + +/****************************************************** + * AW8624 Register Detail + *****************************************************/ + /* SYSST 0x01 */ +#define AW8624_BIT_SYSST_OVS (1<<6) +#define AW8624_BIT_SYSST_UVLS (1<<5) +#define AW8624_BIT_SYSST_FF_AES (1<<4) +#define AW8624_BIT_SYSST_FF_AFS (1<<3) +#define AW8624_BIT_SYSST_OCDS (1<<2) +#define AW8624_BIT_SYSST_OTS (1<<1) +#define AW8624_BIT_SYSST_DONES (1<<0) + + /* SYSINT 0x02 */ +#define AW8624_BIT_SYSINT_OVI (1<<6) +#define AW8624_BIT_SYSINT_UVLI (1<<5) +#define AW8624_BIT_SYSINT_FF_AEI (1<<4) +#define AW8624_BIT_SYSINT_FF_AFI (1<<3) +#define AW8624_BIT_SYSINT_OCDI (1<<2) +#define AW8624_BIT_SYSINT_OTI (1<<1) +#define AW8624_BIT_SYSINT_DONEI (1<<0) + + /* SYSINTM 0x03 */ +#define AW8624_BIT_SYSINTM_OV_MASK (~(1<<6)) +#define AW8624_BIT_SYSINTM_OV_OFF (1<<6) +#define AW8624_BIT_SYSINTM_OV_EN (0<<6) +#define AW8624_BIT_SYSINTM_UVLO_MASK (~(1<<5)) +#define AW8624_BIT_SYSINTM_UVLO_OFF (1<<5) +#define AW8624_BIT_SYSINTM_UVLO_EN (0<<5) +#define AW8624_BIT_SYSINTM_FF_AE_MASK (~(1<<4)) +#define AW8624_BIT_SYSINTM_FF_AE_OFF (1<<4) +#define AW8624_BIT_SYSINTM_FF_AE_EN (0<<4) +#define AW8624_BIT_SYSINTM_FF_AF_MASK (~(1<<3)) +#define AW8624_BIT_SYSINTM_FF_AF_OFF (1<<3) +#define AW8624_BIT_SYSINTM_FF_AF_EN (0<<3) +#define AW8624_BIT_SYSINTM_OCD_MASK (~(1<<2)) +#define AW8624_BIT_SYSINTM_OCD_OFF (1<<2) +#define AW8624_BIT_SYSINTM_OCD_EN (0<<2) +#define AW8624_BIT_SYSINTM_OT_MASK (~(1<<1)) +#define AW8624_BIT_SYSINTM_OT_OFF (1<<1) +#define AW8624_BIT_SYSINTM_OT_EN (0<<1) +#define AW8624_BIT_SYSINTM_DONE_MASK (~(1<<0)) +#define AW8624_BIT_SYSINTM_DONE_OFF (1<<0) +#define AW8624_BIT_SYSINTM_DONE_EN (0<<0) + + /* SYSCTRL 0x04 */ +#define AW8624_BIT_SYSCTRL_WAVDAT_MODE_MASK (~(3<<6)) +#define AW8624_BIT_SYSCTRL_WAVDAT_MODE_4X (3<<6) +#define AW8624_BIT_SYSCTRL_WAVDAT_MODE_2X (0<<6) +#define AW8624_BIT_SYSCTRL_WAVDAT_MODE_1X (1<<6) +#define AW8624_BIT_SYSCTRL_RAMINIT_MASK (~(1<<5)) +#define AW8624_BIT_SYSCTRL_RAMINIT_EN (1<<5) +#define AW8624_BIT_SYSCTRL_RAMINIT_OFF (0<<5) +#define AW8624_BIT_SYSCTRL_PLAY_MODE_MASK (~(3<<2)) +#define AW8624_BIT_SYSCTRL_PLAY_MODE_CONT (2<<2) +#define AW8624_BIT_SYSCTRL_PLAY_MODE_RTP (1<<2) +#define AW8624_BIT_SYSCTRL_PLAY_MODE_RAM (0<<2) +#define AW8624_BIT_SYSCTRL_WORK_MODE_MASK (~(1<<0)) +#define AW8624_BIT_SYSCTRL_STANDBY (1<<0) +#define AW8624_BIT_SYSCTRL_ACTIVE (0<<0) + + /* GO 0x05 */ +#define AW8624_BIT_GO_MASK (~(1<<0)) +#define AW8624_BIT_GO_ENABLE (1<<0) +#define AW8624_BIT_GO_DISABLE (0<<0) + + /* WAVSEQ1 0x07 */ +#define AW8624_BIT_WAVSEQ1_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ1_WAV_FRM_SEQ1_MASK (~(127<<0)) + + /* WAVSEQ2 0x08 */ +#define AW8624_BIT_WAVSEQ2_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ2_WAV_FRM_SEQ2_MASK (~(127<<0)) + + /* WAVSEQ3 0x09 */ +#define AW8624_BIT_WAVSEQ3_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ3_WAV_FRM_SEQ3_MASK (~(127<<0)) + + /* WAVSEQ4 0x0A */ +#define AW8624_BIT_WAVSEQ4_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ4_WAV_FRM_SEQ4_MASK (~(127<<0)) + + /* WAVSEQ5 0X0B */ +#define AW8624_BIT_WAVSEQ5_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ5_WAV_FRM_SEQ5_MASK (~(127<<0)) + + /* WAVSEQ6 0X0C */ +#define AW8624_BIT_WAVSEQ6_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ6_WAV_FRM_SEQ6_MASK (~(127<<0)) + + /* WAVSEQ7 */ +#define AW8624_BIT_WAVSEQ7_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ7_WAV_FRM_SEQ7_MASK (~(127<<0)) + + /* WAVSEQ8 */ +#define AW8624_BIT_WAVSEQ8_WAIT (1<<7) +#define AW8624_BIT_WAVSEQ8_WAV_FRM_SEQ8_MASK (~(127<<0)) + + /* WAVLOOP */ +#define AW8624_BIT_WAVLOOP_SEQN_MASK (~(15<<4)) +#define AW8624_BIT_WAVLOOP_SEQNP1_MASK (~(15<<0)) +#define AW8624_BIT_WAVLOOP_INIFINITELY (15<<0) + + /* WAVLOOP1 */ +#define AW8624_BIT_WAVLOOP1_SEQ1_MASK (~(15<<4)) +#define AW8624_BIT_WAVLOOP1_SEQ2_MASK (~(15<<0)) + + /* WAVLOOP2 */ +#define AW8624_BIT_WAVLOOP2_SEQ3_MASK (~(15<<4)) +#define AW8624_BIT_WAVLOOP2_SEQ4_MASK (~(15<<0)) + + /* WAVLOOP3 */ +#define AW8624_BIT_WAVLOOP3_SEQ5_MASK (~(15<<4)) +#define AW8624_BIT_WAVLOOP3_SEQ6_MASK (~(15<<0)) + + /* WAVLOOP4 */ +#define AW8624_BIT_WAVLOOP4_SEQ7_MASK (~(15<<4)) +#define AW8624_BIT_WAVLOOP4_SEQ8_MASK (~(15<<0)) + + /* TRGCFG1 */ +#define AW8624_BIT_TRGCFG1_TRG1_POLAR_MASK (~(1<<1)) +#define AW8624_BIT_TRGCFG1_TRG1_POLAR_NEG (1<<1) +#define AW8624_BIT_TRGCFG1_TRG1_POLAR_POS (0<<1) +#define AW8624_BIT_TRGCFG1_TRG1_EDGE_MASK (~(1<<0)) +#define AW8624_BIT_TRGCFG1_TRG1_EDGE_POS (1<<0) +#define AW8624_BIT_TRGCFG1_TRG1_EDGE_POS_NEG (0<<0) + + /* TRGCFG2 */ +#define AW8624_BIT_TRGCFG2_TRG1_ENABLE_MASK (~(1<<0)) +#define AW8624_BIT_TRGCFG2_TRG1_ENABLE (1<<0) +#define AW8624_BIT_TRGCFG2_TRG1_DISABLE (0<<0) + + /*DBGCTRL 0X20 */ +#define AW8624_BIT_DBGCTRL_INTN_TRG_SEL_MASK (~(1<<5)) +#define AW8624_BIT_DBGCTRL_INTN_SEL_ENABLE (1<<5) +#define AW8624_BIT_DBGCTRL_TRG_SEL_ENABLE (0<<5) +#define AW8624_BIT_DBGCTRL_INT_MODE_MASK (~(3<<2)) +#define AW8624_BIT_DBGCTRL_INTN_LEVEL_MODE (0<<2) +#define AW8624_BIT_DBGCTRL_INT_MODE_EDGE (1<<2) +#define AW8624_BIT_DBGCTRL_INTN_POSEDGE_MODE (2<<2) +#define AW8624_BIT_DBGCTRL_INTN_BOTH_EDGE_MODE (3<<2) + + /* DATCTRL */ +#define AW8624_BIT_DATCTRL_FC_MASK (~(3<<6)) +#define AW8624_BIT_DATCTRL_FC_1000HZ (3<<6) +#define AW8624_BIT_DATCTRL_FC_800HZ (3<<6) +#define AW8624_BIT_DATCTRL_FC_600HZ (1<<6) +#define AW8624_BIT_DATCTRL_FC_400HZ (0<<6) +#define AW8624_BIT_DATCTRL_LPF_ENABLE_MASK (~(1<<5)) +#define AW8624_BIT_DATCTRL_LPF_ENABLE (1<<5) +#define AW8624_BIT_DATCTRL_LPF_DISABLE (0<<5) + + /*PWMPRC 0X2D */ +#define AW8624_BIT_PWMPRC_PRC_EN_MASK (~(1<<7)) +#define AW8624_BIT_PWMPRC_PRC_ENABLE (1<<7) +#define AW8624_BIT_PWMPRC_PRC_DISABLE (0<<7) +#define AW8624_BIT_PWMPRC_PRCTIME_MASK (~(0x7f<<0)) + + /* PWMDBG */ +#define AW8624_BIT_PWMDBG_PWM_MODE_MASK (~(3<<5)) +#define AW8624_BIT_PWMDBG_PWM_12K (3<<5) +#define AW8624_BIT_PWMDBG_PWM_24K (2<<5) +#define AW8624_BIT_PWMDBG_PWM_48K (0<<5) + +/* GLB_STATE 0x47*/ +#define AW8624_BIT_GLBRD5_STATE_MASK (~(15<<0)) +#define AW8624_BIT_GLBRD5_STATE (15<<0) +#define AW8624_BIT_GLBRD5_STATE_STANDBY (0<<0) +#define AW8624_BIT_GLBRD5_STATE_WAKEUP (1<<0) +#define AW8624_BIT_GLBRD5_STATE_STARTUP (2<<0) +#define AW8624_BIT_GLBRD5_STATE_WAIT (3<<0) +#define AW8624_BIT_GLBRD5_STATE_CONT_GO (6<<0) +#define AW8624_BIT_GLBRD5_STATE_RAM_GO (7<<0) +#define AW8624_BIT_GLBRD5_STATE_RTP_GO (8<<0) +#define AW8624_BIT_GLBRD5_STATE_TRIG_GO (9<<0) +#define AW8624_BIT_GLBRD5_STATE_I2S_GO (10<<0) +#define AW8624_BIT_GLBRD5_STATE_BRAKE (11<<0) +#define AW8624_BIT_GLBRD5_STATE_END (12<<0) + + /* WAVECTRL */ +#define AW8624_BIT_WAVECTRL_NUM_OV_DRIVER_MASK (~(0xF<<4)) +#define AW8624_BIT_WAVECTRL_NUM_OV_DRIVER (0<<4) + + /* CONT_CTRL */ +#define AW8624_BIT_CONT_CTRL_ZC_DETEC_MASK (~(1<<7)) +#define AW8624_BIT_CONT_CTRL_ZC_DETEC_ENABLE (1<<7) +#define AW8624_BIT_CONT_CTRL_ZC_DETEC_DISABLE (0<<7) +#define AW8624_BIT_CONT_CTRL_WAIT_PERIOD_MASK (~(3<<5)) +#define AW8624_BIT_CONT_CTRL_WAIT_8PERIOD (3<<5) +#define AW8624_BIT_CONT_CTRL_WAIT_4PERIOD (2<<5) +#define AW8624_BIT_CONT_CTRL_WAIT_2PERIOD (1<<5) +#define AW8624_BIT_CONT_CTRL_WAIT_1PERIOD (0<<5) +#define AW8624_BIT_CONT_CTRL_MODE_MASK (~(1<<4)) +#define AW8624_BIT_CONT_CTRL_BY_DRV_TIME (1<<4) +#define AW8624_BIT_CONT_CTRL_BY_GO_SIGNAL (0<<4) +#define AW8624_BIT_CONT_CTRL_EN_CLOSE_MASK (~(1<<3)) +#define AW8624_BIT_CONT_CTRL_CLOSE_PLAYBACK (1<<3) +#define AW8624_BIT_CONT_CTRL_OPEN_PLAYBACK (0<<3) +#define AW8624_BIT_CONT_CTRL_F0_DETECT_MASK (~(1<<2)) +#define AW8624_BIT_CONT_CTRL_F0_DETECT_ENABLE (1<<2) +#define AW8624_BIT_CONT_CTRL_F0_DETECT_DISABLE (0<<2) +#define AW8624_BIT_CONT_CTRL_O2C_MASK (~(1<<1)) +#define AW8624_BIT_CONT_CTRL_O2C_ENABLE (1<<1) +#define AW8624_BIT_CONT_CTRL_O2C_DISABLE (0<<1) +#define AW8624_BIT_CONT_CTRL_AUTO_BRK_MASK (~(1<<0)) +#define AW8624_BIT_CONT_CTRL_AUTO_BRK_ENABLE (1<<0) +#define AW8624_BIT_CONT_CTRL_AUTO_BRK_DISABLE (0<<0) + +#define AW8624_BIT_D2SCFG_CLK_ADC_MASK (~(7<<5)) +#define AW8624_BIT_D2SCFG_CLK_ASC_1P5MHZ (3<<5) + +#define AW8624_BIT_D2SCFG_GAIN_MASK (~(7<<0)) +#define AW8624_BIT_D2SCFG_GAIN_40 (7<<0) + /* DETCTRL */ +#define AW8624_BIT_DETCTRL_RL_OS_MASK (~(1<<6)) +#define AW8624_BIT_DETCTRL_RL_DETECT (1<<6) +#define AW8624_BIT_DETCTRL_OS_DETECT (0<<6) +#define AW8624_BIT_DETCTRL_PROTECT_MASK (~(1<<5)) +#define AW8624_BIT_DETCTRL_PROTECT_NO_ACTION (1<<5) +#define AW8624_BIT_DETCTRL_PROTECT_SHUTDOWN (0<<5) +#define AW8624_BIT_DETCTRL_VBAT_GO_MASK (~(1<<1)) +#define AW8624_BIT_DETCTRL_VABT_GO_ENABLE (1<<1) +#define AW8624_BIT_DETCTRL_VBAT_GO_DISBALE (0<<1) +#define AW8624_BIT_DETCTRL_DIAG_GO_MASK (~(1<<0)) +#define AW8624_BIT_DETCTRL_DIAG_GO_ENABLE (1<<0) +#define AW8624_BIT_DETCTRL_DIAG_GO_DISABLE (0<<0) + + +#define AW8624_BIT_RAMADDRH_MASK (~(63<<0)) + + /* VBAT MODE */ +#define AW8624_BIT_DETCTRL_VBAT_MODE_MASK (~(1<<6)) +#define AW8624_BIT_DETCTRL_VBAT_HW_COMP (1<<6) +#define AW8624_BIT_DETCTRL_VBAT_SW_COMP (0<<6) + + + /* ANACTRL */ +#define AW8624_BIT_ANACTRL_LRA_SRC_MASK (~(1<<5)) +#define AW8624_BIT_ANACTRL_LRA_SRC_REG (1<<5) +#define AW8624_BIT_ANACTRL_LRA_SRC_EFUSE (0<<5) +#define AW8624_BIT_ANACTRL_EN_IO_PD1_MASK (~(1<<0)) +#define AW8624_BIT_ANACTRL_EN_IO_PD1_HIGH (1<<0) +#define AW8624_BIT_ANACTRL_EN_IO_PD1_LOW (0<<0) + +/* PRLVL */ +#define AW8624_BIT_PRLVL_PR_EN_MASK (~(1<<7)) +#define AW8624_BIT_PRLVL_PR_ENABLE (1<<7) +#define AW8624_BIT_PRLVL_PR_DISABLE (0<<7) +#define AW8624_BIT_PRLVL_PRLVL_MASK (~(0x7f<<0)) +#define AW8624_BIT_PRLVL_PRLVL_DEFAULT_VALUE (0x3F) + +/* PRTIME */ +#define AW8624_REG_PRTIME_DEFAULT_VALUE (0x12) + +#define AW8624_BIT_BEMF_NUM_BRK_MASK (~(0xf<<0)) + +/* TD_H 0x4b TD_brake */ +#define AW8624_BIT_R_SPARE_MASK (~(1<<7)) +#define AW8624_BIT_R_SPARE_ENABLE (1<<7) + +/* TIME_NZC */ +#define AW8624_BIT_TIME_NZC_DEF_VAL (0x1f) + +/* TRIM_LRA */ +#define AW8624_BIT_TRIM_LRA (0x3f<<0) +/****************************************************** + * AW8623X Register Detail + *****************************************************/ +/* SYSST: reg 0x01 RO */ +#define AW8623X_BIT_SYSST_UVLS (1<<5) +#define AW8623X_BIT_SYSST_FF_AES (1<<4) +#define AW8623X_BIT_SYSST_FF_AFS (1<<3) +#define AW8623X_BIT_SYSST_OCDS (1<<2) +#define AW8623X_BIT_SYSST_OTS (1<<1) +#define AW8623X_BIT_SYSST_DONES (1<<0) + +/* SYSINT: reg 0x02 RC */ +#define AW8623X_BIT_SYSINT_UVLI (1<<5) +#define AW8623X_BIT_SYSINT_FF_AEI (1<<4) +#define AW8623X_BIT_SYSINT_FF_AFI (1<<3) +#define AW8623X_BIT_SYSINT_OCDI (1<<2) +#define AW8623X_BIT_SYSINT_OTI (1<<1) +#define AW8623X_BIT_SYSINT_DONEI (1<<0) + +/* SYSINTM: reg 0x03 RW */ +#define AW8623X_BIT_SYSINTM_UVLM_MASK (~(1<<5)) +#define AW8623X_BIT_SYSINTM_UVLM_OFF (1<<5) +#define AW8623X_BIT_SYSINTM_UVLM_ON (0<<5) +#define AW8623X_BIT_SYSINTM_FF_AEM_MASK (~(1<<4)) +#define AW8623X_BIT_SYSINTM_FF_AEM_OFF (1<<4) +#define AW8623X_BIT_SYSINTM_FF_AEM_ON (0<<4) +#define AW8623X_BIT_SYSINTM_FF_AFM_MASK (~(1<<3)) +#define AW8623X_BIT_SYSINTM_FF_AFM_OFF (1<<3) +#define AW8623X_BIT_SYSINTM_FF_AFM_ON (0<<3) +#define AW8623X_BIT_SYSINTM_OCDM_MASK (~(1<<2)) +#define AW8623X_BIT_SYSINTM_OCDM_OFF (1<<2) +#define AW8623X_BIT_SYSINTM_OCDM_ON (0<<2) +#define AW8623X_BIT_SYSINTM_OTM_MASK (~(1<<1)) +#define AW8623X_BIT_SYSINTM_OTM_OFF (1<<1) +#define AW8623X_BIT_SYSINTM_OTM_ON (0<<1) +#define AW8623X_BIT_SYSINTM_DONEM_MASK (~(1<<0)) +#define AW8623X_BIT_SYSINTM_DONEM_OFF (1<<0) +#define AW8623X_BIT_SYSINTM_DONEM_ON (0<<0) + +/* SYSST2: reg 0x04 RO */ +#define AW8623X_BIT_SYSST2_WCK_OK (1<<5) +#define AW8623X_BIT_SYSST2_BCK_OK (1<<4) +#define AW8623X_BIT_SYSST2_LDO_OK (1<<3) +#define AW8623X_BIT_SYSST2_VBG_OK (1<<2) +#define AW8623X_BIT_SYSST2_FF_FULL (1<<1) +#define AW8623X_BIT_SYSST2_FF_EMPTY (1<<0) + +/* SYSER: reg 0x05 RC */ +#define AW8623X_BIT_SYSER_TRIG1_EVENT (1<<5) +#define AW8623X_BIT_SYSER_TRIG2_EVENT (1<<4) +#define AW8623X_BIT_SYSER_TRIG3_EVENT (1<<3) +#define AW8623X_BIT_SYSER_OV (1<<2) +#define AW8623X_BIT_SYSER_ADDR_ER (1<<1) +#define AW8623X_BIT_SYSER_FF_ER (1<<0) + +/* PLAYCFG3: reg 0x08 RW */ +#define AW8623X_BIT_PLAYCFG3_ONEWIRE_COMP_MASK (~(1<<4)) +#define AW8623X_BIT_PLAYCFG3_ONEWIRE_1908_MODE (1<<4) +#define AW8623X_BIT_PLAYCFG3_ONEWIRE_2102_MODE (0<<4) +#define AW8623X_BIT_PLAYCFG3_STOP_MODE_MASK (~(1<<3)) +#define AW8623X_BIT_PLAYCFG3_STOP_MODE_NOW (1<<3) +#define AW8623X_BIT_PLAYCFG3_STOP_MODE_LATER (0<<3) +#define AW8623X_BIT_PLAYCFG3_BRK_EN_MASK (~(1<<2)) +#define AW8623X_BIT_PLAYCFG3_BRK (1<<2) +#define AW8623X_BIT_PLAYCFG3_BRK_ENABLE (1<<2) +#define AW8623X_BIT_PLAYCFG3_BRK_DISABLE (0<<2) +#define AW8623X_BIT_PLAYCFG3_PLAY_MODE_MASK (~(3<<0)) +#define AW8623X_BIT_PLAYCFG3_PLAY_MODE_STOP (3<<0) +#define AW8623X_BIT_PLAYCFG3_PLAY_MODE_CONT (2<<0) +#define AW8623X_BIT_PLAYCFG3_PLAY_MODE_RTP (1<<0) +#define AW8623X_BIT_PLAYCFG3_PLAY_MODE_RAM (0<<0) + +/* PLAYCFG4: reg 0x09 WC */ +#define AW8623X_BIT_PLAYCFG4_STOP_MASK (~(1<<1)) +#define AW8623X_BIT_PLAYCFG4_STOP_ON (1<<1) +#define AW8623X_BIT_PLAYCFG4_STOP_OFF (0<<1) +#define AW8623X_BIT_PLAYCFG4_GO_MASK (~(1<<0)) +#define AW8623X_BIT_PLAYCFG4_GO_ON (1<<0) +#define AW8623X_BIT_PLAYCFG4_GO_OFF (0<<0) + +/* WAVCFG1-8: reg 0x0A - reg 0x11 RW */ +#define AW8623X_BIT_WAVCFG_SEQWAIT_MASK (~(1<<7)) +#define AW8623X_BIT_WAVCFG_SEQWAIT_TIME (1<<7) +#define AW8623X_BIT_WAVCFG_SEQWAIT_NUMBER (0<<7) +#define AW8623X_BIT_WAVCFG_SEQ (0x7F) + +/* WAVCFG9-12: reg 0x12 - reg 0x15 RW */ +#define AW8623X_BIT_WAVLOOP_SEQ_ODD_MASK (~(0x0F<<4)) +#define AW8623X_BIT_WAVLOOP_SEQ_ODD_INIFINITELY (0x0F<<4) +#define AW8623X_BIT_WAVLOOP_SEQ_EVEN_MASK (~(0x0F<<0)) +#define AW8623X_BIT_WAVLOOP_SEQ_EVEN_INIFINITELY (0x0F<<0) +#define AW8623X_BIT_WAVLOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG9: reg 0x12 RW */ +#define AW8623X_BIT_WAVCFG9_SEQ1LOOP_MASK (~(0x0F<<4)) +#define AW8623X_BIT_WAVCFG9_SEQ1LOOP_INIFINITELY (0x0F<<4) +#define AW8623X_BIT_WAVCFG9_SEQ2LOOP_MASK (~(0x0F<<0)) +#define AW8623X_BIT_WAVCFG9_SEQ2LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG10: reg 0x13 RW */ +#define AW8623X_BIT_WAVCFG10_SEQ3LOOP_MASK (~(0x0F<<4)) +#define AW8623X_BIT_WAVCFG10_SEQ3LOOP_INIFINITELY (0x0F<<4) +#define AW8623X_BIT_WAVCFG10_SEQ4LOOP_MASK (~(0x0F<<0)) +#define AW8623X_BIT_WAVCFG10_SEQ4LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG11: reg 0x14 RW */ +#define AW8623X_BIT_WAVCFG11_SEQ5LOOP_MASK (~(0x0F<<4)) +#define AW8623X_BIT_WAVCFG11_SEQ5LOOP_INIFINITELY (0x0F<<4) +#define AW8623X_BIT_WAVCFG11_SEQ6LOOP_MASK (~(0x0F<<0)) +#define AW8623X_BIT_WAVCFG11_SEQ6LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG12: reg 0x15 RW */ +#define AW8623X_BIT_WAVCFG12_SEQ7LOOP_MASK (~(0x0F<<4)) +#define AW8623X_BIT_WAVCFG12_SEQ7LOOP_INIFINITELY (0x0F<<4) +#define AW8623X_BIT_WAVCFG12_SEQ8LOOP_MASK (~(0x0F<<0)) +#define AW8623X_BIT_WAVCFG12_SEQ8LOOP_INIFINITELY (0x0F<<0) + +/***************** CONT *****************/ +/* CONTCFG1: reg 0x17 RW */ +#define AW8623X_BIT_CONTCFG1_EDGE_FRE_MASK (~(0x0F<<0)) +#define AW8623X_BIT_CONTCFG1_EN_F0_DET_MASK (~(1<<5)) +#define AW8623X_BIT_CONTCFG1_F0_DET_ENABLE (1<<5) +#define AW8623X_BIT_CONTCFG1_F0_DET_DISABLE (0<<5) +#define AW8623X_BIT_CONTCFG1_SIN_MODE_MASK (~(1<<4)) +#define AW8623X_BIT_CONTCFG1_SIN_MODE_COS (1<<0) +#define AW8623X_BIT_CONTCFG1_SIN_MODE_SINE (0<<0) +/* CONTCFG3: reg 0x19 RW */ +/* DRV_WIDTH */ +#define AW8623X_BIT_DRV_WIDTH_DEF_VAL (0x6A) + +/* CONTCFG5: reg 0x1B RW */ +#define AW8623X_BIT_CONTCFG5_BRK_GAIN_MASK (~(0x0F<<0)) + +/* CONTCFG6: reg 0x1C RW */ +#define AW8623X_BIT_CONTCFG6_TRACK_EN_MASK (~(1<<7)) +#define AW8623X_BIT_CONTCFG6_TRACK_ENABLE (1<<7) +#define AW8623X_BIT_CONTCFG6_TRACK_DISABLE (0<<7) +#define AW8623X_BIT_CONTCFG6_DRV1_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG7: reg 0x1D RW */ +#define AW8623X_BIT_CONTCFG7_WAITHZ_EN_MASK (~(1<<7)) +#define AW8623X_BIT_CONTCFG7_WAITHZ_ENABLE (1<<7) +#define AW8623X_BIT_CONTCFG7_WAITHZ_DISABLE (0<<7) +#define AW8623X_BIT_CONTCFG7_DRV2_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG13: reg 0x23 RW */ +#define AW8623X_BIT_CONTCFG13_TSET_MASK (~(0x0F<<4)) +#define AW8623X_BIT_CONTCFG13_BEME_SET_MASK (~(0x0F<<0)) + +/***************** RTP *****************/ +/* RTPCFG1: reg 0x2C RW */ +#define AW8623X_BIT_RTPCFG1_ADDRH_MASK (~(0x0F<<0)) + +#define AW8623X_BIT_RTPCFG1_SRAM_SIZE_2K_MASK (~(1<<5)) +#define AW8623X_BIT_RTPCFG1_SRAM_SIZE_2K_EN (1<<5) +#define AW8623X_BIT_RTPCFG1_SRAM_SIZE_2K_DIS (0<<5) + +#define AW8623X_BIT_RTPCFG1_SRAM_SIZE_1K_MASK (~(1<<4)) +#define AW8623X_BIT_RTPCFG1_SRAM_SIZE_1K_EN (1<<4) +#define AW8623X_BIT_RTPCFG1_SRAM_SIZE_1K_DIS (0<<4) + +/* RTPCFG3: reg 0x2E RW */ +#define AW8623X_BIT_RTPCFG3_FIFO_AEH_MASK (~(0x0F<<4)) +#define AW8623X_BIT_RTPCFG3_FIFO_AFH_MASK (~(0x0F<<0)) +#define AW8623X_BIT_RTPCFG3_FIFO_AEH (0x0F<<4) +#define AW8623X_BIT_RTPCFG3_FIFO_AFH (0x0F<<0) + +/* RTPCFG6: reg 0x31 RW */ +#define AW8623X_BIT_RTPCFG6_EN_AUTO_SIN_96K_MASK (~(1<<3)) +#define AW8623X_BIT_RTPCFG6_EN_AUTO_SIN_96K_ENABLE (1<<3) +#define AW8623X_BIT_RTPCFG6_EN_AUTO_SIN_96K_DISABLE (0<<3) +#define AW8623X_BIT_RTPCFG6_EN_RTP_AUTO_SIN_MASK (~(1<<2)) +#define AW8623X_BIT_RTPCFG6_EN_RTP_AUTO_SIN_ENABLE (1<<2) +#define AW8623X_BIT_RTPCFG6_EN_RTP_AUTO_SIN_DISABLE (0<<2) +#define AW8623X_BIT_RTPCFG6_EN_TWORTP_MASK (~(1<<1)) +#define AW8623X_BIT_RTPCFG6_EN_TWORTP_ENABLE (1<<1) +#define AW8623X_BIT_RTPCFG6_EN_TWORTP_DISABLE (0<<1) +#define AW8623X_BIT_RTPCFG6_WCK_PIN_MASK (~(1<<0)) +#define AW8623X_BIT_RTPCFG6_WCK_PIN_ENABLE (1<<0) +#define AW8623X_BIT_RTPCFG6_WCK_PIN_DISABLE (0<<0) + +/***************** TRIGGER *****************/ +#define AW8623X_BIT_TRG_ENABLE_MASK (~(1<<7)) +#define AW8623X_BIT_TRG_ENABLE (1<<7) +#define AW8623X_BIT_TRG_DISABLE (0<<7) +#define AW8623X_BIT_TRG_SEQ_MASK (~(0x7F<<0)) + +/* TRGCFG1: reg 0x33 RW */ +#define AW8623X_BIT_TRGCFG1_TRG1_POS_MASK (~(1<<7)) +#define AW8623X_BIT_TRGCFG1_TRG1_POS_ENABLE (1<<7) +#define AW8623X_BIT_TRGCFG1_TRG1_POS_DISABLE (0<<7) +#define AW8623X_BIT_TRGCFG1_TRG1SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG2: reg 0x34 RW */ +#define AW8623X_BIT_TRGCFG2_TRG2_POS_MASK (~(1<<7)) +#define AW8623X_BIT_TRGCFG2_TRG2_POS_ENABLE (1<<7) +#define AW8623X_BIT_TRGCFG2_TRG2_POS_DISABLE (0<<7) +#define AW8623X_BIT_TRGCFG2_TRG2SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG3: r3g 0x35 RW */ +#define AW8623X_BIT_TRGCFG3_TRG3_POS_MASK (~(1<<7)) +#define AW8623X_BIT_TRGCFG3_TRG3_POS_ENABLE (1<<7) +#define AW8623X_BIT_TRGCFG3_TRG3_POS_DISABLE (0<<7) +#define AW8623X_BIT_TRGCFG3_TRG3SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG4: r3g 0x36 RW */ +#define AW8623X_BIT_TRGCFG4_TRG1_NEG_MASK (~(1<<7)) +#define AW8623X_BIT_TRGCFG4_TRG1_NEG_ENABLE (1<<7) +#define AW8623X_BIT_TRGCFG4_TRG1_NEG_DISABLE (0<<7) +#define AW8623X_BIT_TRGCFG4_TRG1SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG5: r3g 0x37 RW */ +#define AW8623X_BIT_TRGCFG5_TRG2_NEG_MASK (~(1<<7)) +#define AW8623X_BIT_TRGCFG5_TRG2_NEG_ENABLE (1<<7) +#define AW8623X_BIT_TRGCFG5_TRG2_NEG_DISABLE (0<<7) +#define AW8623X_BIT_TRGCFG5_TRG2SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG6: r3g 0x38 RW */ +#define AW8623X_BIT_TRGCFG6_TRG3_NEG_MASK (~(1<<7)) +#define AW8623X_BIT_TRGCFG6_TRG3_NEG_ENABLE (1<<7) +#define AW8623X_BIT_TRGCFG6_TRG3_NEG_DISABLE (0<<7) +#define AW8623X_BIT_TRGCFG6_TRG3SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG7: reg 0x39 RW */ +#define AW8623X_BIT_TRGCFG7_TRG1_POR_LEV_BRK_MASK (~(7<<3)) +#define AW8623X_BIT_TRGCFG7_TRG2_POR_LEV_BRK_MASK (~(7<<0)) +#define AW8623X_BIT_TRGCFG7_TRG1_POLAR_MASK (~(1<<5)) +#define AW8623X_BIT_TRGCFG7_TRG1_POLAR_NEG (1<<5) +#define AW8623X_BIT_TRGCFG7_TRG1_POLAR_POS (0<<5) +#define AW8623X_BIT_TRGCFG7_TRG1_MODE_MASK (~(1<<4)) +#define AW8623X_BIT_TRGCFG7_TRG1_MODE_LEVEL (1<<4) +#define AW8623X_BIT_TRGCFG7_TRG1_MODE_EDGE (0<<4) +#define AW8623X_BIT_TRGCFG7_TRG1_AUTO_BRK_MASK (~(1<<3)) +#define AW8623X_BIT_TRGCFG7_TRG1_AUTO_BRK_ENABLE (1<<3) +#define AW8623X_BIT_TRGCFG7_TRG1_AUTO_BRK_DISABLE (0<<3) +#define AW8623X_BIT_TRGCFG7_TRG2_POLAR_MASK (~(1<<2)) +#define AW8623X_BIT_TRGCFG7_TRG2_POLAR_NEG (1<<2) +#define AW8623X_BIT_TRGCFG7_TRG2_POLAR_POS (0<<2) +#define AW8623X_BIT_TRGCFG7_TRG2_MODE_MASK (~(1<<1)) +#define AW8623X_BIT_TRGCFG7_TRG2_MODE_LEVEL (1<<1) +#define AW8623X_BIT_TRGCFG7_TRG2_MODE_EDGE (0<<1) +#define AW8623X_BIT_TRGCFG7_TRG2_AUTO_BRK_MASK (~(1<<0)) +#define AW8623X_BIT_TRGCFG7_TRG2_AUTO_BRK_ENABLE (1<<0) +#define AW8623X_BIT_TRGCFG7_TRG2_AUTO_BRK_DISABLE (0<<0) + +/* TRGCFG8: reg 0x3A RW */ +#define AW8623X_BIT_TRGCFG8_TRG3_POR_LEV_BRK_MASK (~(7<<3)) +#define AW8623X_BIT_TRGCFG8_TRG3_POLAR_MASK (~(1<<5)) +#define AW8623X_BIT_TRGCFG8_TRG3_POLAR_NEG (1<<5) +#define AW8623X_BIT_TRGCFG8_TRG3_POLAR_POS (0<<5) +#define AW8623X_BIT_TRGCFG8_TRG3_MODE_MASK (~(1<<4)) +#define AW8623X_BIT_TRGCFG8_TRG3_MODE_LEVEL (1<<4) +#define AW8623X_BIT_TRGCFG8_TRG3_MODE_EDGE (0<<4) +#define AW8623X_BIT_TRGCFG8_TRG3_AUTO_BRK_MASK (~(1<<3)) +#define AW8623X_BIT_TRGCFG8_TRG3_AUTO_BRK_ENABLE (1<<3) +#define AW8623X_BIT_TRGCFG8_TRG3_AUTO_BRK_DISABLE (0<<3) +#define AW8623X_BIT_TRGCFG8_TRG1_STOP_MASK (~(1<<2)) +#define AW8623X_BIT_TRGCFG8_TRG1_STOP (1<<2) +#define AW8623X_BIT_TRGCFG8_TRG2_STOP_MASK (~(1<<1)) +#define AW8623X_BIT_TRGCFG8_TRG2_STOP (1<<1) +#define AW8623X_BIT_TRGCFG8_TRG3_STOP_MASK (~(1<<0)) +#define AW8623X_BIT_TRGCFG8_TRG3_STOP (1<<0) + +/* GLBCFG4: reg 0x3E RW */ +#define AW8623X_BIT_GLBCFG4_GO_PRIO_MASK (~(3<<6)) +#define AW8623X_BIT_GLBCFG4_TRG3_PRIO_MASK (~(3<<4)) +#define AW8623X_BIT_GLBCFG4_TRG2_PRIO_MASK (~(3<<2)) +#define AW8623X_BIT_GLBCFG4_TRG1_PRIO_MASK (~(3<<0)) + +/* GLBRD5: reg 0x3F R0 */ +/* GLB_STATE */ +#define AW8623X_BIT_GLBRD5_STATE (15<<0) +#define AW8623X_BIT_GLBRD5_STATE_STANDBY (0<<0) +#define AW8623X_BIT_GLBRD5_STATE_WAKEUP (1<<0) +#define AW8623X_BIT_GLBRD5_STATE_STARTUP (2<<0) +#define AW8623X_BIT_GLBRD5_STATE_WAIT (3<<0) +#define AW8623X_BIT_GLBRD5_STATE_CONT_GO (6<<0) +#define AW8623X_BIT_GLBRD5_STATE_RAM_GO (7<<0) +#define AW8623X_BIT_GLBRD5_STATE_RTP_GO (8<<0) +#define AW8623X_BIT_GLBRD5_STATE_TRIG_GO (9<<0) +#define AW8623X_BIT_GLBRD5_STATE_I2S_GO (10<<0) +#define AW8623X_BIT_GLBRD5_STATE_BRAKE (11<<0) +#define AW8623X_BIT_GLBRD5_STATE_END (12<<0) +/* RAMADDRH: reg 0x40 RWS */ +#define AW8623X_BIT_RAMADDRH_MASK (~(63<<0)) + +/* I2SCFG1: reg 0x44 RW */ +#define AW8623X_BIT_I2SCFG1_I2S_EN_MASK (~(1<<2)) +#define AW8623X_BIT_I2SCFG1_I2S_ENABLE (1<<2) +#define AW8623X_BIT_I2SCFG1_I2S_DISABLE (0<<2) +#define AW8623X_BIT_I2SCFG1_I2S_MD_MASK (~(1<<1)) +#define AW8623X_BIT_I2SCFG1_I2S_MD_MSB (1<<1) +#define AW8623X_BIT_I2SCFG1_I2S_MD_PHILIP (0<<1) +#define AW8623X_BIT_I2SCFG1_I2S_SLOT_CHSEL_MASK (~(1<<0)) +#define AW8623X_BIT_I2SCFG1_I2S_SLOT_CHSEL_R_BEHIND (1<<0) +#define AW8623X_BIT_I2SCFG1_I2S_SLOT_CHSEL_L_BEYOND (0<<0) + +/***************** SYSCTRL *****************/ +/* SYSCTRL1: reg 0x45 RW */ +#define AW8623X_BIT_SYSCTRL1_TRIG1_MODE_MASK (~(7<<4)) +#define AW8623X_BIT_SYSCTRL1_TRIG1_NONE (7<<4) +#define AW8623X_BIT_SYSCTRL1_TRIG1_AD (6<<4) +#define AW8623X_BIT_SYSCTRL1_TRIG1_RCK (5<<4) +#define AW8623X_BIT_SYSCTRL1_TRIG1_BCK (4<<4) +#define AW8623X_BIT_SYSCTRL1_TRIG1_TRIG (3<<4) +#define AW8623X_BIT_SYSCTRL1_TRIG1_ONE_WIRE (2<<4) +#define AW8623X_BIT_SYSCTRL1_TRIG1_PWM_ERM (1<<4) +#define AW8623X_BIT_SYSCTRL1_TRIG1_PWM_LRA (0<<4) +#define AW8623X_BIT_SYSCTRL1_GAIN_BYPASS_MASK (~(1<<3)) +#define AW8623X_BIT_SYSCTRL1_GAIN_CHANGE_ENABLE (1<<3) +#define AW8623X_BIT_SYSCTRL1_GAIN_CHANGE_DISABLE (0<<3) +#define AW8623X_BIT_SYSCTRL1_RAMINIT_MASK (~(1<<2)) +#define AW8623X_BIT_SYSCTRL1_RAMINIT_ON (1<<2) +#define AW8623X_BIT_SYSCTRL1_RAMINIT_OFF (0<<2) +#define AW8623X_BIT_SYSCTRL1_EN_FIR_MASK (~(1<<1)) +#define AW8623X_BIT_SYSCTRL1_FIR_ENABLE (0<<1) +#define AW8623X_BIT_SYSCTRL1_INTN_PIN_MASK (~(1<<0)) +#define AW8623X_BIT_SYSCTRL1_INTN (1<<0) +#define AW8623X_BIT_SYSCTRL1_TRIG1 (0<<0) + +/* SYSCTRL2: reg 0x46 RW */ +#define AW8623X_BIT_SYSCTRL2_WAKE_MASK (~(1<<7)) +#define AW8623X_BIT_SYSCTRL2_WAKE_ON (1<<7) +#define AW8623X_BIT_SYSCTRL2_WAKE_OFF (0<<7) +#define AW8623X_BIT_SYSCTRL2_STANDBY_MASK (~(1<<6)) +#define AW8623X_BIT_SYSCTRL2_STANDBY_ON (1<<6) +#define AW8623X_BIT_SYSCTRL2_STANDBY_OFF (0<<6) +#define AW8623X_BIT_SYSCTRL2_EN_DLL_MASK (~(1<<5)) +#define AW8623X_BIT_SYSCTRL2_EN_DLL (1<<5) +#define AW8623X_BIT_SYSCTRL2_DISENABLE_DLL (0<<5) +#define AW8623X_BIT_SYSCTRL2_RCK_FRE_MASK (~(3<<3)) +#define AW8623X_BIT_SYSCTRL2_RCK_FRE_96K (3<<3) +#define AW8623X_BIT_SYSCTRL2_RCK_FRE_48K (2<<3) +#define AW8623X_BIT_SYSCTRL2_RCK_FRE_32_768K (1<<3) +#define AW8623X_BIT_SYSCTRL2_RCK_FRE_24K (0<<3) +#define AW8623X_BIT_SYSCTRL2_EN_INTN_CLKOUT_MASK (~(1<<2)) +#define AW8623X_BIT_SYSCTRL2_EN_INTN_CLKOUT (1<<2) +#define AW8623X_BIT_SYSCTRL2_DISENABLE_INTN_CLKOUT (0<<2) +#define AW8623X_BIT_SYSCTRL2_WAVDAT_MODE_MASK (~(3<<0)) +#define AW8623X_BIT_SYSCTRL2_RATE (3<<0) +#define AW8623X_BIT_SYSCTRL2_RATE_12K (2<<0) +#define AW8623X_BIT_SYSCTRL2_RATE_24K (0<<0) +#define AW8623X_BIT_SYSCTRL2_RATE_48K (1<<0) + +/* SYSCTRL3: reg 0x47 RW */ +#define AW8623X_BIT_SYSCTRL3_EN_TRIG1_PD_MASK (~(1<<6)) +#define AW8623X_BIT_SYSCTRL3_EN_TRIG1_PD (1<<6) +#define AW8623X_BIT_SYSCTRL3_DISENABLE_TRIG1_PD (0<<6) +#define AW8623X_BIT_SYSCTRL3_RTP_DLY_MASK (~(3<<4)) +#define AW8623X_BIT_SYSCTRL3_RTP_DLY_FIFO_AE (3<<4) +#define AW8623X_BIT_SYSCTRL3_RTP_DLY_FIFO_8 (2<<4) +#define AW8623X_BIT_SYSCTRL3_RTP_DLY_FIFO_5 (1<<4) +#define AW8623X_BIT_SYSCTRL3_RTP_DLY_FIFO_1 (0<<4) +#define AW8623X_BIT_SYSCTRL3_INTMODE_MASK (~(3<<2)) +#define AW8623X_BIT_SYSCTRL3_INTMODE_BOTH_EDGE (3<<2) +#define AW8623X_BIT_SYSCTRL3_INTMODE_LEVEL_1 (2<<2) +#define AW8623X_BIT_SYSCTRL3_INTMODE_POS_EDGE (1<<2) +#define AW8623X_BIT_SYSCTRL3_INTMODE_LEVEL_2 (0<<2) +#define AW8623X_BIT_SYSCTRL3_INTP_MASK (~(1<<1)) +#define AW8623X_BIT_SYSCTRL3_INTP_ACTIVE_HIGH (1<<1) +#define AW8623X_BIT_SYSCTRL3_INTP_ACTIVE_LOW (0<<1) +#define AW8623X_BIT_SYSCTRL3_WAKE_MODE_MASK (~(1<<0)) +#define AW8623X_BIT_SYSCTRL3_WAKE_MODE_WAKEUP (1<<0) +#define AW8623X_BIT_SYSCTRL3_WAKE_MODE_BST (0<<0) + +/* SYSCTRL5: reg 0x49 RW */ +#define AW8623X_BIT_SYSCTRL5_MODULE_SEL_MASK (~(7<<2)) +#define AW8623X_BIT_SYSCTRL5_MODULE_SEL_VBAT_CTRL (5<<2) +#define AW8623X_BIT_SYSCTRL5_MODULE_SEL_TRIG_ENG (4<<2) +#define AW8623X_BIT_SYSCTRL5_MODULE_SEL_CONT_ENG (3<<2) +#define AW8623X_BIT_SYSCTRL5_MODULE_SEL_RTP_ENG (2<<2) +#define AW8623X_BIT_SYSCTRL5_MODULE_SEL_RAM_ENG (1<<2) +#define AW8623X_BIT_SYSCTRL5_MODULE_SEL_EFUSE_ENG (0<<2) +#define AW8623X_BIT_SYSCTRL5_DEV_ADDR_CTR_MASK (~(3<<0)) +#define AW8623X_BIT_SYSCTRL5_DEV_ADDR_CTR_HW_IRSW (3<<0) +#define AW8623X_BIT_SYSCTRL5_DEV_ADDR_CTR_HW_1 (2<<0) +#define AW8623X_BIT_SYSCTRL5_DEV_ADDR_CTR_IRSW (1<<0) +#define AW8623X_BIT_SYSCTRL5_DEV_ADDR_CTR_HW_2 (0<<0) + +/* PWMCFG1: reg 0x4A RW */ +#define AW8623X_BIT_PWMCFG1_PRC_EN_MASK (~(1<<7)) +#define AW8623X_BIT_PWMCFG1_PRC_ENABLE (1<<7) +#define AW8623X_BIT_PWMCFG1_PRC_DISABLE (0<<7) +#define AW8623X_BIT_PWMCFG1_PRCTIME_MASK (~(0x7F<<0)) + +/* PWMCFG2: reg 0x4B RW */ +#define AW8623X_BIT_PWMCFG2_PRCT_MODE_MASK (~(1<<6)) +#define AW8623X_BIT_PWMCFG2_PRCT_MODE_VALID (1<<6) +#define AW8623X_BIT_PWMCFG2_PRCT_MODE_INVALID (0<<6) +#define AW8623X_BIT_PWMCFG2_REF_SEL_MASK (~(1<<5)) +#define AW8623X_BIT_PWMCFG2_REF_SEL_TRIANGLE (1<<5) +#define AW8623X_BIT_PWMCFG2_REF_SEL_SAWTOOTH (0<<5) +#define AW8623X_BIT_PWMCFG2_PD_HWM_MASK (~(1<<4)) +#define AW8623X_BIT_PWMCFG2_PD_HWM_FULL (1<<4) +#define AW8623X_BIT_PWMCFG2_PD_HWM_HALF (0<<4) +#define AW8623X_BIT_PWMCFG2_PWMOE_MASK (~(1<<3)) +#define AW8623X_BIT_PWMCFG2_PWMOE_ON (1<<3) +#define AW8623X_BIT_PWMCFG2_PWMOE_OFF (0<<3) +#define AW8623X_BIT_PWMCFG2_PWMFRC_MASK (~(7<<0)) + +/* PWMCFG3: reg 0x4C RW */ +#define AW8623X_BIT_PWMCFG3_PR_EN_MASK (~(1<<7)) +#define AW8623X_BIT_PWMCFG3_PR_ENABLE (1<<7) +#define AW8623X_BIT_PWMCFG3_PR_DISABLE (0<<7) +#define AW8623X_BIT_PWMCFG3_PRLVL_MASK (~(0x7F<<0)) +#define AW8623X_BIT_PWMCFG3_PRLVL_DEFAULT_VALUE (0x3F) + +/* PWMCFG4: reg 0x4D RW */ +/* PRTIME */ +#define AW8623X_PWMCFG4_PRTIME_DEFAULT_VALUE (0x32) + +/* VBATCTRL: reg 0x4E RW */ +#define AW8623X_BIT_VBATCTRL_VBAT_PRO_MASK (~(1<<7)) +#define AW8623X_BIT_VBATCTRL_VBAT_PRO_ENABLE (1<<7) +#define AW8623X_BIT_VBATCTRL_VBAT_PRO_DISABLE (0<<7) +#define AW8623X_BIT_VBATCTRL_VBAT_MODE_MASK (~(1<<6)) +#define AW8623X_BIT_VBATCTRL_VBAT_MODE_HW (1<<6) +#define AW8623X_BIT_VBATCTRL_VBAT_MODE_SW (0<<6) +#define AW8623X_BIT_VBATCTRL_VBAT_MODE_CON_MASK (~(1<<5)) +#define AW8623X_BIT_VBATCTRL_VBAT_MODE_CON_DURING (1<<5) +#define AW8623X_BIT_VBATCTRL_VBAT_MODE_CON_BEFORE (0<<5) +#define AW8623X_BIT_VBATCTRL_DELTA_VBAT_MASK (~(1<<4)) +#define AW8623X_BIT_VBATCTRL_DELTA_VBAT_0_2 (1<<4) +#define AW8623X_BIT_VBATCTRL_DELTA_VBAT_0_1 (0<<4) +#define AW8623X_BIT_VBATCTRL_REL_VBAT_MASK (~(3<<2)) +#define AW8623X_BIT_VBATCTRL_REL_VBAT_100 (3<<2) +#define AW8623X_BIT_VBATCTRL_REL_VBAT_50 (2<<2) +#define AW8623X_BIT_VBATCTRL_REL_VBAT_25 (1<<2) +#define AW8623X_BIT_VBATCTRL_REL_VBAT_10 (0<<2) +#define AW8623X_BIT_VBATCTRL_ABS_VBAT_MASK (~(3<<0)) +#define AW8623X_BIT_VBATCTRL_ABS_VBAT_3_5 (3<<0) +#define AW8623X_BIT_VBATCTRL_ABS_VBAT_3_4 (2<<0) +#define AW8623X_BIT_VBATCTRL_ABS_VBAT_3_3 (1<<0) +#define AW8623X_BIT_VBATCTRL_ABS_VBAT_3_2 (0<<0) + +/* DETCFG1: reg 0x4F RW */ +#define AW8623X_BIT_DETCFG1_EN_VBAT_AVG_MASK (~(1<<7)) +#define AW8623X_BIT_DETCFG1_EN_VBAT_AVG_16_TIMES (1<<7) +#define AW8623X_BIT_DETCFG1_VBAT_REF_MASK (~(7<<4)) +#define AW8623X_BIT_DETCFG1_VBAT_REF_5_5 (7<<4) +#define AW8623X_BIT_DETCFG1_VBAT_REF_5_0 (6<<4) +#define AW8623X_BIT_DETCFG1_VBAT_REF_4_8 (5<<4) +#define AW8623X_BIT_DETCFG1_VBAT_REF_4_5 (4<<4) +#define AW8623X_BIT_DETCFG1_VBAT_REF_4_2 (3<<4) +#define AW8623X_BIT_DETCFG1_VBAT_REF_4_0 (2<<4) +#define AW8623X_BIT_DETCFG1_VBAT_REF_3_6 (1<<4) +#define AW8623X_BIT_DETCFG1_VBAT_REF_3_3 (0<<4) +#define AW8623X_BIT_DETCFG1_ADC_FS_MASK (~(3<<2)) +#define AW8623X_BIT_DETCFG1_ADC_FS_24K (3<<2) +#define AW8623X_BIT_DETCFG1_ADC_FS_48K (2<<2) +#define AW8623X_BIT_DETCFG1_ADC_FS_96K (1<<2) +#define AW8623X_BIT_DETCFG1_ADC_FS_192K (0<<2) +#define AW8623X_BIT_DETCFG1_DET_GO_MASK (~(3<<0)) +#define AW8623X_BIT_DETCFG1_DET_GO_ON (1<<0) +#define AW8623X_BIT_DETCFG1_DET_GO_OFF (0<<0) + +/* DETCFG2: reg 0x50 RW */ +#define AW8623X_BIT_DETCFG2_DET_SEQ0_MASK (~(0X0F<<3)) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_HDN_1 (0x0E<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_HDN_2 (0x0A<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_HDP_1 (0x0D<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_HDP_2 (9<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_FTS (6<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_VOUT (5<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_OS (4<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_RL (3<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_TRIG1 (2<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_PVDD (1<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_VBAT (0<<3) +#define AW8623X_BIT_DETCFG2_D2S_GAIN_MASK (~(7<<0)) +#define AW8623X_BIT_DETCFG2_D2S_GAIN_40 (7<<0) +#define AW8623X_BIT_DETCFG2_D2S_GAIN_20 (6<<0) +#define AW8623X_BIT_DETCFG2_D2S_GAIN_10 (5<<0) +#define AW8623X_BIT_DETCFG2_D2S_GAIN_8 (4<<0) +#define AW8623X_BIT_DETCFG2_D2S_GAIN_5 (3<<0) +#define AW8623X_BIT_DETCFG2_D2S_GAIN_4 (2<<0) +#define AW8623X_BIT_DETCFG2_D2S_GAIN_2 (1<<0) +#define AW8623X_BIT_DETCFG2_D2S_GAIN_1 (0<<0) + +/* DETRD1: reg 0x51 RO */ +#define AW8623X_BIT_DETRD1_AVG_DATA_H_MASK (~(3<<0)) + +/* TRIMCFG1: reg 0x59 RW */ +#define AW8623X_BIT_TRIMCFG1_RL_TRIM_SRC_MASK (~(1<<7)) +#define AW8623X_BIT_TRIMCFG1_RL_TRIM_SRC_REG (1<<7) +#define AW8623X_BIT_TRIMCFG1_RL_TRIM_SRC_EFUSE (0<<7) +#define AW8623X_BIT_TRIMCFG1_TRIM_RL_MASK (~(0x7F<<0)) + +/* TRIMCFG2: reg 0x5A RW */ +#define AW8623X_BIT_TRIMCFG2_OSC_TRIM_SRC_MASK (~(1<<6)) +#define AW8623X_BIT_TRIMCFG2_OSC_TRIM_SRC_REG (1<<6) +#define AW8623X_BIT_TRIMCFG2_OSC_TRIM_SRC_EFUSE (0<<6) +#define AW8623X_BIT_TRIMCFG2_TRIM_LRA_MASK (~(0x3F<<0)) + +/* ANACFG4: reg:0x70 RW */ +#define AW8623X_BIT_ANACFG3_EN_OVP_SAR_MASK (~(1<<7)) +#define AW8623X_BIT_ANACFG3_EN_OVP_SAR_ENABLE (1<<7) +#define AW8623X_BIT_ANACFG3_EN_OVP_SAR_DISABLE (0<<7) + +/* ANACFG4: reg:0x71 RW */ +#define AW8623X_BIT_ANACFG4_PRO_HDRV_MASK (~(1<<7)) +#define AW8623X_BIT_ANACFG4_PRO_HDRV_ENABLE (1<<7) +#define AW8623X_BIT_ANACFG4_PRO_HDRV_DISABLE (0<<7) + +#endif diff --git a/drivers/misc/awinic/aw862x_haptic_nv_v1/Android.mk b/drivers/misc/awinic/aw862x_haptic_nv_v1/Android.mk new file mode 100644 index 000000000000..3d11361ad3fb --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv_v1/Android.mk @@ -0,0 +1,12 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := aw862x_haptic_nv_v1.ko +LOCAL_MODULE_TAGS := optional +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/awinic/aw862x_haptic_nv_v1/Kbuild b/drivers/misc/awinic/aw862x_haptic_nv_v1/Kbuild new file mode 100644 index 000000000000..3e3ac01952dc --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv_v1/Kbuild @@ -0,0 +1,14 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/misc/awinic/aw862x_haptic_nv_v1 + +obj-m += aw862x_haptic_nv_v1.o + +aw862x_haptic_nv_v1-objs += haptic_nv.o +aw862x_haptic_nv_v1-objs += aw862x.o +aw862x_haptic_nv_v1-objs += aw8623x.o +aw862x_haptic_nv_v1-objs += aw8624x.o +aw862x_haptic_nv_v1-objs += aw862xx.o + + diff --git a/drivers/misc/awinic/aw862x_haptic_nv_v1/Makefile b/drivers/misc/awinic/aw862x_haptic_nv_v1/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv_v1/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/misc/awinic/aw862x_haptic_nv_v1/aw8623x.c b/drivers/misc/awinic/aw862x_haptic_nv_v1/aw8623x.c new file mode 100644 index 000000000000..d5247f07c856 --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv_v1/aw8623x.c @@ -0,0 +1,1439 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * aw8623x.c + * + * Copyright (c) 2022 AWINIC Technology CO., LTD + * + * Author: + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "haptic_nv.h" +#include "haptic_nv_reg.h" + +static void aw8623x_interrupt_setup(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_SYSINT, ®_val, AW_I2C_BYTE_ONE); + aw_info("reg SYSINT=0x%02X", reg_val); + /* edge int mode */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL3, AW8623X_BIT_SYSCTRL3_INTMODE_MASK, + AW8623X_BIT_SYSCTRL3_INTMODE_POS_EDGE); + /* int enable */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSINTM, + (AW8623X_BIT_SYSINTM_UVLM_MASK & AW8623X_BIT_SYSINTM_FF_AEM_MASK & + AW8623X_BIT_SYSINTM_FF_AFM_MASK & AW8623X_BIT_SYSINTM_OCDM_MASK & + AW8623X_BIT_SYSINTM_OTM_MASK & AW8623X_BIT_SYSINTM_DONEM_MASK), + (AW8623X_BIT_SYSINTM_UVLM_ON | AW8623X_BIT_SYSINTM_FF_AEM_OFF | + AW8623X_BIT_SYSINTM_FF_AFM_OFF | AW8623X_BIT_SYSINTM_OCDM_ON | + AW8623X_BIT_SYSINTM_OTM_ON | AW8623X_BIT_SYSINTM_DONEM_OFF)); +} + +static void aw8623x_set_rtp_data(struct aw_haptic *aw_haptic, uint8_t *data, uint32_t len) +{ + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_RTPDATA, data, len); +} + +static uint8_t aw8623x_get_glb_state(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_GLBRD5, &state, AW_I2C_BYTE_ONE); + + return state; +} + +static int aw8623x_get_irq_state(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int ret = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_SYSINT, ®_val, AW_I2C_BYTE_ONE); + aw_dbg("reg SYSINT=0x%02X", reg_val); + if (reg_val & AW8623X_BIT_SYSINT_UVLI) + aw_err("chip uvlo int error"); + if (reg_val & AW8623X_BIT_SYSINT_OCDI) + aw_err("chip over current int error"); + if (reg_val & AW8623X_BIT_SYSINT_OTI) + aw_err("chip over temperature int error"); + if (reg_val & AW8623X_BIT_SYSINT_DONEI) + aw_info("chip playback done"); + if (reg_val & AW8623X_BIT_SYSINT_FF_AFI) + aw_info("rtp mode fifo almost full!"); + if (reg_val & AW8623X_BIT_SYSINT_FF_AEI) { + aw_info("rtp fifo almost empty"); + ret = AW_IRQ_ALMOST_EMPTY; + } + + return ret; +} + +static uint8_t aw8623x_rtp_get_fifo_afs(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_SYSST, ®_val, AW_I2C_BYTE_ONE); + reg_val &= AW8623X_BIT_SYSST_FF_AFS; + reg_val = reg_val >> 3; + + return reg_val; +} + +static uint8_t aw8623x_rtp_get_fifo_aes(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_SYSST, ®_val, AW_I2C_BYTE_ONE); + reg_val &= AW8623X_BIT_SYSST_FF_AES; + reg_val = reg_val >> 4; + + return reg_val; +} + +static void aw8623x_set_rtp_aei(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSINTM, + AW8623X_BIT_SYSINTM_FF_AEM_MASK, + AW8623X_BIT_SYSINTM_FF_AEM_ON); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSINTM, + AW8623X_BIT_SYSINTM_FF_AEM_MASK, + AW8623X_BIT_SYSINTM_FF_AEM_OFF); + } +} + +static void aw8623x_sram_size(struct aw_haptic *aw_haptic, uint8_t size_flag) +{ + switch (size_flag) { + case AW8623X_HAPTIC_SRAM_1K: + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_RTPCFG1, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_2K_MASK, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_2K_DIS); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_RTPCFG1, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_1K_MASK, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_1K_EN); + break; + case AW8623X_HAPTIC_SRAM_2K: + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_RTPCFG1, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_2K_MASK, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_2K_EN); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_RTPCFG1, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_1K_MASK, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_1K_DIS); + break; + case AW8623X_HAPTIC_SRAM_3K: + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_RTPCFG1, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_1K_MASK, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_1K_EN); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_RTPCFG1, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_2K_MASK, + AW8623X_BIT_RTPCFG1_SRAM_SIZE_2K_EN); + break; + default: + aw_err("size_flag is error"); + break; + } +} + +static void aw8623x_auto_brk_config(struct aw_haptic *aw_haptic, uint8_t flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PLAYCFG3, + AW8623X_BIT_PLAYCFG3_BRK_EN_MASK, + AW8623X_BIT_PLAYCFG3_BRK_ENABLE); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PLAYCFG3, + AW8623X_BIT_PLAYCFG3_BRK_EN_MASK, + AW8623X_BIT_PLAYCFG3_BRK_DISABLE); + } +} + +static void aw8623x_play_stop(struct aw_haptic *aw_haptic) +{ + bool force_flag = true; + uint8_t val = 0; + int cnt = 40; + + aw_haptic->play_mode = AW_STANDBY_MODE; + val = AW8623X_BIT_PLAYCFG4_STOP_ON; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_PLAYCFG4, &val, AW_I2C_BYTE_ONE); + while (cnt) { + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_GLBRD5, &val, AW_I2C_BYTE_ONE); + if ((val & AW_BIT_GLBRD_STATE_MASK) == AW_BIT_STATE_STANDBY) { + force_flag = false; + aw_info("entered standby! glb_state=0x%02X", val); + break; + } + cnt--; + usleep_range(AW_STOP_DELAY_MIN, AW_STOP_DELAY_MAX); + } + if (force_flag) { + aw_err("force to enter standby mode!"); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL2, + AW8623X_BIT_SYSCTRL2_STANDBY_MASK, + AW8623X_BIT_SYSCTRL2_STANDBY_ON); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL2, + AW8623X_BIT_SYSCTRL2_STANDBY_MASK, + AW8623X_BIT_SYSCTRL2_STANDBY_OFF); + } +} + +static void aw8623x_set_pwm(struct aw_haptic *aw_haptic, uint8_t mode) +{ + switch (mode) { + case AW_PWM_48K: + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL2, + AW8623X_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW8623X_BIT_SYSCTRL2_RATE_48K); + break; + case AW_PWM_24K: + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL2, + AW8623X_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW8623X_BIT_SYSCTRL2_RATE_24K); + break; + case AW_PWM_12K: + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL2, + AW8623X_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW8623X_BIT_SYSCTRL2_RATE_12K); + break; + default: + break; + } +} + +static void aw8623x_vbat_mode_config(struct aw_haptic *aw_haptic, uint8_t flag) +{ + aw_info("enter"); + if (flag == AW_CONT_VBAT_HW_COMP_MODE) { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_VBATCTRL, + AW8623X_BIT_VBATCTRL_VBAT_MODE_MASK, + AW8623X_BIT_VBATCTRL_VBAT_MODE_HW); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_VBATCTRL, + AW8623X_BIT_VBATCTRL_VBAT_MODE_MASK, + AW8623X_BIT_VBATCTRL_VBAT_MODE_SW); + } +} + +static void aw8623x_play_mode(struct aw_haptic *aw_haptic, uint8_t play_mode) +{ + switch (play_mode) { + case AW_STANDBY_MODE: + aw_info("enter standby mode"); + aw_haptic->play_mode = AW_STANDBY_MODE; + aw8623x_play_stop(aw_haptic); + break; + case AW_RAM_MODE: + aw_info("enter ram mode"); + aw_haptic->play_mode = AW_RAM_MODE; + aw8623x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + aw8623x_set_pwm(aw_haptic, AW_PWM_12K); + aw8623x_auto_brk_config(aw_haptic, false); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PLAYCFG3, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + case AW_RAM_LOOP_MODE: + aw_info("enter ram loop mode"); + aw_haptic->play_mode = AW_RAM_LOOP_MODE; + aw8623x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + aw8623x_set_pwm(aw_haptic, AW_PWM_12K); + aw8623x_auto_brk_config(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PLAYCFG3, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + case AW_CONT_MODE: + aw_info("enter cont mode"); + aw_haptic->play_mode = AW_CONT_MODE; + aw8623x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_HW_COMP_MODE); + aw8623x_auto_brk_config(aw_haptic, aw_haptic->info.is_enabled_auto_brk); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PLAYCFG3, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_CONT); + break; + case AW_RTP_MODE: + aw_info("enter rtp mode"); + aw_haptic->play_mode = AW_RTP_MODE; + aw8623x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + aw8623x_set_pwm(aw_haptic, AW_PWM_24K); + aw8623x_auto_brk_config(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PLAYCFG3, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_RTP); + break; + case AW_TRIG_MODE: + aw_info("enter trig mode"); + aw_haptic->play_mode = AW_TRIG_MODE; + aw8623x_set_pwm(aw_haptic, AW_PWM_12K); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PLAYCFG3, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8623X_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + default: + aw_err("play mode %u error", play_mode); + break; + } +} + +static void aw8623x_irq_clear(struct aw_haptic *aw_haptic) +{ + uint8_t val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_SYSINT, &val, AW_I2C_BYTE_ONE); + aw_info("SYSINT=0x%02X", val); +} + +static void aw8623x_play_go(struct aw_haptic *aw_haptic) +{ + uint8_t val = 0; + + aw_info("enter"); + val = AW8623X_BIT_PLAYCFG4_GO_ON; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_PLAYCFG4, &val, AW_I2C_BYTE_ONE); + usleep_range(AW_PLAY_DELAY_MIN, AW_PLAY_DELAY_MAX); +} + +static void aw8623x_raminit(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL1, + AW8623X_BIT_SYSCTRL1_RAMINIT_MASK, + AW8623X_BIT_SYSCTRL1_RAMINIT_ON); + usleep_range(1000, 1050); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL1, + AW8623X_BIT_SYSCTRL1_RAMINIT_MASK, + AW8623X_BIT_SYSCTRL1_RAMINIT_OFF); + } +} + +static int aw8623x_select_d2s_gain(uint8_t reg) +{ + int d2s_gain = 0; + + switch (reg) { + case AW8623X_BIT_DETCFG2_D2S_GAIN_1: + d2s_gain = 1; + break; + case AW8623X_BIT_DETCFG2_D2S_GAIN_2: + d2s_gain = 2; + break; + case AW8623X_BIT_DETCFG2_D2S_GAIN_4: + d2s_gain = 4; + break; + case AW8623X_BIT_DETCFG2_D2S_GAIN_5: + d2s_gain = 5; + break; + case AW8623X_BIT_DETCFG2_D2S_GAIN_8: + d2s_gain = 8; + break; + case AW8623X_BIT_DETCFG2_D2S_GAIN_10: + d2s_gain = 10; + break; + case AW8623X_BIT_DETCFG2_D2S_GAIN_20: + d2s_gain = 20; + break; + case AW8623X_BIT_DETCFG2_D2S_GAIN_40: + d2s_gain = 40; + break; + default: + d2s_gain = -1; + break; + } + + return d2s_gain; +} + +static void aw8623x_get_vbat(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val[2] = {0}; + uint32_t vbat_code = 0; + + aw_info("enter"); + aw8623x_play_stop(aw_haptic); + aw8623x_raminit(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG2, AW8623X_BIT_DETCFG2_DET_SEQ0_MASK, + AW8623X_BIT_DETCFG2_DET_SEQ0_VBAT); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG1, AW8623X_BIT_DETCFG1_DET_GO_MASK, + AW8623X_BIT_DETCFG1_DET_GO_ON); + usleep_range(3000, 3500); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG1, AW8623X_BIT_DETCFG1_DET_GO_MASK, + AW8623X_BIT_DETCFG1_DET_GO_OFF); + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_DETRD1, reg_val, AW_I2C_BYTE_TWO); + vbat_code = reg_val[0] & (~AW8623X_BIT_DETRD1_AVG_DATA_H_MASK); + vbat_code = (vbat_code << 8) | reg_val[1]; + aw_haptic->vbat = AW8623X_VBAT_FORMULA(vbat_code); + if (aw_haptic->vbat > AW_VBAT_MAX) { + aw_haptic->vbat = AW_VBAT_MAX; + aw_info("vbat max limit = %dmV", aw_haptic->vbat); + } + if (aw_haptic->vbat < AW_VBAT_MIN) { + aw_haptic->vbat = AW_VBAT_MIN; + aw_info("vbat min limit = %dmV", aw_haptic->vbat); + } + aw_info("vbat=%dmV, vbat_code=0x%02X", aw_haptic->vbat, vbat_code); + aw8623x_raminit(aw_haptic, false); +} + +static void aw8623x_set_gain(struct aw_haptic *aw_haptic, uint8_t gain) +{ + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_PLAYCFG2, &gain, AW_I2C_BYTE_ONE); +} + +static void aw8623x_set_trim_lra(struct aw_haptic *aw_haptic, uint8_t val) +{ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_TRIMCFG2, + AW8623X_BIT_TRIMCFG2_TRIM_LRA_MASK, val); +} + +static void aw8623x_cont_config(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + aw_info("enter"); + /* work mode */ + aw8623x_play_mode(aw_haptic, AW_CONT_MODE); + /* cont config */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_CONTCFG6, + (AW8623X_BIT_CONTCFG6_TRACK_EN_MASK & + AW8623X_BIT_CONTCFG6_DRV1_LVL_MASK), + ((aw_haptic->info.is_enabled_track_en << 7) | + (uint8_t)aw_haptic->info.cont_drv1_lvl)); + reg_val = (uint8_t)aw_haptic->info.cont_drv2_lvl; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG7, ®_val, AW_I2C_BYTE_ONE); + /* DRV2_TIME */ + reg_val = 0xFF; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG9, ®_val, AW_I2C_BYTE_ONE); + /* cont play go */ + aw8623x_play_go(aw_haptic); +} + +static ssize_t aw8623x_get_reg(struct aw_haptic *aw_haptic, ssize_t len, char *buf) +{ + len = haptic_nv_read_reg_array(aw_haptic, buf, len, AW8623X_REG_RSTCFG, + AW8623X_REG_RTPDATA - 1); + if (!len) + return len; + len = haptic_nv_read_reg_array(aw_haptic, buf, len, AW8623X_REG_RTPDATA + 1, + AW8623X_REG_RAMDATA - 1); + if (!len) + return len; + len = haptic_nv_read_reg_array(aw_haptic, buf, len, AW8623X_REG_RAMDATA + 1, + AW8623X_REG_ANACFG10); + return len; +} + +static void aw8623x_protect_config(struct aw_haptic *aw_haptic, uint8_t prtime, uint8_t prlvl) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PWMCFG1, AW8623X_BIT_PWMCFG1_PRC_EN_MASK, + AW8623X_BIT_PWMCFG1_PRC_DISABLE); + if (prlvl != 0) { + /* Enable protection mode */ + aw_info("enable protection mode"); + reg_val = AW8623X_BIT_PWMCFG3_PR_ENABLE | + (prlvl & (~AW8623X_BIT_PWMCFG3_PRLVL_MASK)); + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_PWMCFG3, ®_val, AW_I2C_BYTE_ONE); + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_PWMCFG4, &prtime, AW_I2C_BYTE_ONE); + } else { + /* Disable */ + aw_info("disable protection mode"); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PWMCFG3, + AW8623X_BIT_PWMCFG3_PR_EN_MASK, + AW8623X_BIT_PWMCFG3_PR_DISABLE); + } +} + +static void aw8623x_misc_para_init(struct aw_haptic *aw_haptic) +{ + uint8_t val = 0; + uint8_t array[8] = {0}; + + aw_info("enter"); + /* Get seq and gain */ + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_WAVCFG1, &val, AW_I2C_BYTE_ONE); + aw_haptic->index = val & AW8623X_BIT_WAVCFG_SEQ; + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_PLAYCFG2, &val, AW_I2C_BYTE_ONE); + aw_haptic->gain = val; + aw_info("gain=0x%02X, index=0x%02X", aw_haptic->gain, aw_haptic->index); + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_WAVCFG1, array, AW_SEQUENCER_SIZE); + memcpy(aw_haptic->seq, array, AW_SEQUENCER_SIZE); + /* GAIN_BYPASS config */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL1, + AW8623X_BIT_SYSCTRL1_GAIN_BYPASS_MASK, + aw_haptic->info.gain_bypass << 3); + /* Open the input OVP function of ADC */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_ANACFG3, + AW8623X_BIT_ANACFG3_EN_OVP_SAR_MASK, + AW8623X_BIT_ANACFG3_EN_OVP_SAR_ENABLE); + + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL1, + AW8623X_BIT_SYSCTRL1_TRIG1_MODE_MASK, + AW8623X_BIT_SYSCTRL1_TRIG1_TRIG); + + if (aw_haptic->info.d2s_gain) { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG2, + AW8623X_BIT_DETCFG2_D2S_GAIN_MASK, + aw_haptic->info.d2s_gain); + } + + if (aw_haptic->info.cont_brk_time) { + val = (uint8_t)aw_haptic->info.cont_brk_time; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG10, &val, AW_I2C_BYTE_ONE); + } + + if (aw_haptic->info.cont_brk_gain) { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_CONTCFG5, + AW8623X_BIT_CONTCFG5_BRK_GAIN_MASK, + aw_haptic->info.cont_brk_gain); + } + + aw_haptic->info.cont_drv2_lvl = AW8623X_DRV2_LVL_FORMULA( + aw_haptic->info.f0_pre, aw_haptic->info.lra_vrms); + if (aw_haptic->info.cont_drv2_lvl > AW8623X_DRV2_LVL_MAX) + aw_haptic->info.cont_drv2_lvl = AW8623X_DRV2_LVL_MAX; + + aw8623x_sram_size(aw_haptic, AW8623X_HAPTIC_SRAM_3K); + aw8623x_set_pwm(aw_haptic, AW_PWM_12K); + aw8623x_protect_config(aw_haptic, AW8623X_PWMCFG4_PRTIME_DEFAULT_VALUE, + AW8623X_BIT_PWMCFG3_PRLVL_DEFAULT_VALUE); +} + +static int aw8623x_offset_cali(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val[2] = {0}; + int os_code = 0; + int d2s_gain = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_DETCFG2, reg_val, AW_I2C_BYTE_ONE); + reg_val[0] = (~AW8623X_BIT_DETCFG2_D2S_GAIN_MASK) & reg_val[0]; + d2s_gain = aw8623x_select_d2s_gain(reg_val[0]); + if (d2s_gain < 0) { + aw_err("d2s_gain is error"); + return -ERANGE; + } + aw8623x_raminit(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG2, AW8623X_BIT_DETCFG2_DET_SEQ0_MASK, + AW8623X_BIT_DETCFG2_DET_SEQ0_OS); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG1, AW8623X_BIT_DETCFG1_DET_GO_MASK, + AW8623X_BIT_DETCFG1_DET_GO_ON); + usleep_range(3000, 3500); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG1, AW8623X_BIT_DETCFG1_DET_GO_MASK, + AW8623X_BIT_DETCFG1_DET_GO_OFF); + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_DETRD1, reg_val, AW_I2C_BYTE_TWO); + aw8623x_raminit(aw_haptic, false); + os_code = reg_val[0] & (~AW8623X_BIT_DETRD1_AVG_DATA_H_MASK); + os_code = (os_code << 8) | reg_val[1]; + os_code = AW8623X_OS_FORMULA(os_code, d2s_gain); + aw_info("os_code is %d mV", os_code); + if (os_code > 15 || os_code < -15) + return -ERANGE; + + return 0; +} + +static void aw8623x_calculate_cali_data(struct aw_haptic *aw_haptic) +{ + char f0_cali_lra = 0; + int f0_cali_step = 0; + + f0_cali_step = 100000 * ((int)aw_haptic->f0 - (int)aw_haptic->info.f0_pre) / + ((int)aw_haptic->info.f0_pre * AW8623X_F0_CALI_ACCURACY); + aw_info("f0_cali_step=%d", f0_cali_step); + if (f0_cali_step >= 0) { /*f0_cali_step >= 0 */ + if (f0_cali_step % 10 >= 5) + f0_cali_step = 32 + (f0_cali_step / 10 + 1); + else + f0_cali_step = 32 + f0_cali_step / 10; + } else { /* f0_cali_step < 0 */ + if (f0_cali_step % 10 <= -5) + f0_cali_step = 32 + (f0_cali_step / 10 - 1); + else + f0_cali_step = 32 + f0_cali_step / 10; + } + if (f0_cali_step > 31) + f0_cali_lra = (char)f0_cali_step - 32; + else + f0_cali_lra = (char)f0_cali_step + 32; + /* update cali step */ + aw_haptic->f0_cali_data = (int)f0_cali_lra; + aw_info("f0_cali_data=0x%02X", aw_haptic->f0_cali_data); +} + +static void aw8623x_read_cont_f0(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t f0_reg = 0; + uint64_t f0_tmp = 0; + + aw_info("enter"); + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_CONTRD16, val, AW_I2C_BYTE_TWO); + f0_reg = (f0_reg | val[0]) << 8; + f0_reg |= (val[1] << 0); + if (!f0_reg) { + aw_err("didn't get cont f0 because f0_reg value is 0!"); + aw_haptic->cont_f0 = 0; + return; + } + f0_tmp = AW8623X_F0_FORMULA(f0_reg); + aw_haptic->cont_f0 = (uint32_t)f0_tmp; + aw_info("cont_f0=%u", aw_haptic->cont_f0); +} + +static int aw8623x_read_lra_f0(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t f0_reg = 0; + uint64_t f0_tmp = 0; + + aw_info("enter"); + /* F_LRA_F0 */ + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_CONTRD14, val, AW_I2C_BYTE_TWO); + f0_reg = (f0_reg | val[0]) << 8; + f0_reg |= (val[1] << 0); + if (!f0_reg) { + aw_haptic->f0 = 0; + aw_err("didn't get lra f0 because f0_reg value is 0!"); + return -ERANGE; + } + f0_tmp = AW8623X_F0_FORMULA(f0_reg); + aw_haptic->f0 = (uint32_t)f0_tmp; + aw_info("lra_f0=%u", aw_haptic->f0); + + return 0; +} + +static int aw8623x_get_f0(struct aw_haptic *aw_haptic) +{ + bool get_f0_flag = false; + uint8_t brk_en_temp = 0; + uint8_t val[3] = {0}; + int drv_width = 0; + int cnt = 200; + int ret = 0; + + aw_info("enter"); + aw_haptic->f0 = aw_haptic->info.f0_pre; + /* enter standby mode */ + aw8623x_play_stop(aw_haptic); + /* f0 calibrate work mode */ + aw8623x_play_mode(aw_haptic, AW_CONT_MODE); + /* enable f0 detect */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_CONTCFG1, + AW8623X_BIT_CONTCFG1_EN_F0_DET_MASK, + AW8623X_BIT_CONTCFG1_F0_DET_ENABLE); + /* cont config */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_CONTCFG6, + AW8623X_BIT_CONTCFG6_TRACK_EN_MASK, + (aw_haptic->info.is_enabled_track_en << 7)); + /* enable auto brake */ + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_PLAYCFG3, &val[0], AW_I2C_BYTE_ONE); + brk_en_temp = AW8623X_BIT_PLAYCFG3_BRK & val[0]; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PLAYCFG3, AW8623X_BIT_PLAYCFG3_BRK_EN_MASK, + AW8623X_BIT_PLAYCFG3_BRK_ENABLE); + /* f0 driver level */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_CONTCFG6, + AW8623X_BIT_CONTCFG6_DRV1_LVL_MASK, aw_haptic->info.cont_drv1_lvl); + val[0] = (uint8_t)aw_haptic->info.cont_drv2_lvl; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_CONTCFG7, + AW8623X_BIT_CONTCFG7_DRV2_LVL_MASK, val[0]); + val[0] = (uint8_t)aw_haptic->info.cont_drv1_time; + val[1] = (uint8_t)aw_haptic->info.cont_drv2_time; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG8, val, AW_I2C_BYTE_TWO); + /* TRACK_MARGIN */ + if (!aw_haptic->info.cont_track_margin) { + aw_err("dts_info.cont_track_margin = 0!"); + } else { + val[0] = (uint8_t)aw_haptic->info.cont_track_margin; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG11, &val[0], AW_I2C_BYTE_ONE); + } + /* DRV_WIDTH */ + if (!aw_haptic->info.f0_pre) + return -ERANGE; + drv_width = AW8623X_DRV_WIDTH_FORMULA(aw_haptic->info.f0_pre, + aw_haptic->info.cont_track_margin, + aw_haptic->info.cont_brk_gain); + if (drv_width < AW_DRV_WIDTH_MIN) + drv_width = AW_DRV_WIDTH_MIN; + if (drv_width > AW_DRV_WIDTH_MAX) + drv_width = AW_DRV_WIDTH_MAX; + val[0] = (uint8_t)drv_width; + aw_info("cont_drv_width=0x%02X", val[0]); + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG3, &val[0], AW_I2C_BYTE_ONE); + /* cont play go */ + aw8623x_play_go(aw_haptic); + usleep_range(20000, 20500); + while (cnt) { + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_GLBRD5, &val[0], AW_I2C_BYTE_ONE); + if ((val[0] & AW_BIT_GLBRD_STATE_MASK) == AW_BIT_STATE_STANDBY) { + get_f0_flag = true; + aw_info("entered standby! glb_state=0x%02X", val[0]); + break; + } + cnt--; + usleep_range(AW_F0_DELAY_MIN, AW_F0_DELAY_MAX); + } + if (get_f0_flag) { + ret = aw8623x_read_lra_f0(aw_haptic); + if (ret < 0) + aw_err("read lra f0 is failed"); + aw8623x_read_cont_f0(aw_haptic); + } else { + ret = -ERANGE; + aw_err("enter standby mode failed, stop reading f0!"); + } + /* disable f0 detect */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_CONTCFG1, + AW8623X_BIT_CONTCFG1_EN_F0_DET_MASK, + AW8623X_BIT_CONTCFG1_F0_DET_DISABLE); + /* recover auto break config */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_PLAYCFG3, + AW8623X_BIT_PLAYCFG3_BRK_EN_MASK, brk_en_temp); + + return ret; +} + +static void aw8623x_set_base_addr(struct aw_haptic *aw_haptic) +{ + uint32_t base_addr = 0; + uint8_t val = 0; + + aw_info("enter"); + base_addr = aw_haptic->ram.base_addr; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_RTPCFG1, AW8623X_BIT_RTPCFG1_ADDRH_MASK, + (uint8_t)AW_SET_BASEADDR_H(base_addr)); + val = (uint8_t)AW_SET_BASEADDR_L(base_addr); + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_RTPCFG2, &val, AW_I2C_BYTE_ONE); +} + +static void aw8623x_set_fifo_addr(struct aw_haptic *aw_haptic) +{ + uint8_t ae_addr_h = 0; + uint8_t af_addr_h = 0; + uint8_t val[3] = {0}; + + aw_info("enter"); + ae_addr_h = (uint8_t)AW8623X_SET_AEADDR_H(aw_haptic->ram.base_addr); + af_addr_h = (uint8_t)AW8623X_SET_AFADDR_H(aw_haptic->ram.base_addr); + val[0] = ae_addr_h | af_addr_h; + val[1] = (uint8_t)AW8623X_SET_AEADDR_L(aw_haptic->ram.base_addr); + val[2] = (uint8_t)AW8623X_SET_AFADDR_L(aw_haptic->ram.base_addr); + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_RTPCFG3, val, AW_I2C_BYTE_THREE); +} + +static void aw8623x_get_fifo_addr(struct aw_haptic *aw_haptic) +{ + uint8_t ae_addr_h = 0; + uint8_t af_addr_h = 0; + uint8_t ae_addr_l = 0; + uint8_t af_addr_l = 0; + uint8_t val[3] = {0}; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_RTPCFG3, val, AW_I2C_BYTE_THREE); + ae_addr_h = ((val[0]) & AW8623X_BIT_RTPCFG3_FIFO_AEH) >> 4; + ae_addr_l = val[1]; + af_addr_h = ((val[0]) & AW8623X_BIT_RTPCFG3_FIFO_AFH); + af_addr_l = val[2]; + aw_info("almost_empty_threshold = %u,almost_full_threshold = %u", + (uint16_t)((ae_addr_h << 8) | ae_addr_l), + (uint16_t)((af_addr_h << 8) | af_addr_l)); +} + +static void aw8623x_set_ram_addr(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t base_addr = aw_haptic->ram.base_addr; + + val[0] = (uint8_t)AW_SET_RAMADDR_H(base_addr); + val[1] = (uint8_t)AW_SET_RAMADDR_L(base_addr); + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_RAMADDRH, val, AW_I2C_BYTE_TWO); +} + +static void aw8623x_set_ram_data(struct aw_haptic *aw_haptic, uint8_t *data, int len) +{ + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_RAMDATA, data, len); +} + +static void aw8623x_get_first_wave_addr(struct aw_haptic *aw_haptic, uint32_t *first_wave_addr) +{ + uint8_t val[3] = {0}; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_RAMDATA, val, AW_I2C_BYTE_THREE); + *first_wave_addr = (val[1] << 8 | val[2]); +} + +static void aw8623x_select_pin(struct aw_haptic *aw_haptic, uint8_t pin) +{ + if (pin == AW_TRIG1) { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL1, + AW8623X_BIT_SYSCTRL1_INTN_PIN_MASK, + AW8623X_BIT_SYSCTRL1_TRIG1); + aw_info("TRIG1 pin used as TRIG1"); + } else if (pin == AW_IRQ) { + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_SYSCTRL1, + AW8623X_BIT_SYSCTRL1_INTN_PIN_MASK, + AW8623X_BIT_SYSCTRL1_INTN); + aw_info("TRIG1 pin used as INIT"); + } else { + aw_err("There is no such option"); + } +} + +static void aw8623x_trig_param_init(struct aw_haptic *aw_haptic, uint8_t pin) +{ + struct aw_haptic_dts_info info = aw_haptic->info; + + switch (pin) { + case AW_TRIG1: + aw_haptic->trig[0].trig_level = info.trig_cfg[0]; + aw_haptic->trig[0].trig_polar = info.trig_cfg[1]; + aw_haptic->trig[0].pos_enable = info.trig_cfg[2]; + aw_haptic->trig[0].pos_sequence = info.trig_cfg[3]; + aw_haptic->trig[0].neg_enable = info.trig_cfg[4]; + aw_haptic->trig[0].neg_sequence = info.trig_cfg[5]; + aw_haptic->trig[0].trig_brk = info.trig_cfg[6]; + break; + case AW_TRIG2: + aw_haptic->trig[1].trig_level = info.trig_cfg[7 + 0]; + aw_haptic->trig[1].trig_polar = info.trig_cfg[7 + 1]; + aw_haptic->trig[1].pos_enable = info.trig_cfg[7 + 2]; + aw_haptic->trig[1].pos_sequence = info.trig_cfg[7 + 3]; + aw_haptic->trig[1].neg_enable = info.trig_cfg[7 + 4]; + aw_haptic->trig[1].neg_sequence = info.trig_cfg[7 + 5]; + aw_haptic->trig[1].trig_brk = info.trig_cfg[7 + 6]; + break; + case AW_TRIG3: + aw_haptic->trig[2].trig_level = info.trig_cfg[14 + 0]; + aw_haptic->trig[2].trig_polar = info.trig_cfg[14 + 1]; + aw_haptic->trig[2].pos_enable = info.trig_cfg[14 + 2]; + aw_haptic->trig[2].pos_sequence = info.trig_cfg[14 + 3]; + aw_haptic->trig[2].neg_enable = info.trig_cfg[14 + 4]; + aw_haptic->trig[2].neg_sequence = info.trig_cfg[14 + 5]; + aw_haptic->trig[2].trig_brk = info.trig_cfg[14 + 6]; + break; + default: + break; + } +} + +static int aw8623x_trig_param_config(struct aw_haptic *aw_haptic, uint8_t pin) +{ + uint8_t trig_polar_lev_brk = 0x00; + uint8_t trig_pos_seq = 0x00; + uint8_t trig_neg_seq = 0x00; + + switch (pin) { + case AW_TRIG1: + if (aw_haptic->is_used_irq_pin) { + aw8623x_trig_param_init(aw_haptic, AW_TRIG1); + aw8623x_select_pin(aw_haptic, AW_IRQ); + return 0; + } + aw8623x_select_pin(aw_haptic, AW_TRIG1); + trig_polar_lev_brk = aw_haptic->trig[0].trig_polar << 2 | + aw_haptic->trig[0].trig_level << 1 | + aw_haptic->trig[0].trig_brk; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_TRGCFG7, + AW8623X_BIT_TRGCFG7_TRG1_POR_LEV_BRK_MASK, + trig_polar_lev_brk << 3); + trig_pos_seq = aw_haptic->trig[0].pos_enable << 7 | aw_haptic->trig[0].pos_sequence; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_TRGCFG1, + &trig_pos_seq, AW_I2C_BYTE_ONE); + trig_neg_seq = aw_haptic->trig[0].neg_enable << 7 | aw_haptic->trig[0].neg_sequence; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_TRGCFG4, + &trig_neg_seq, AW_I2C_BYTE_ONE); + aw_info("trig1 config ok!"); + break; + case AW_TRIG2: + trig_polar_lev_brk = aw_haptic->trig[1].trig_polar << 2 | + aw_haptic->trig[1].trig_level << 1 | + aw_haptic->trig[1].trig_brk; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_TRGCFG7, + AW8623X_BIT_TRGCFG7_TRG2_POR_LEV_BRK_MASK, + trig_polar_lev_brk << 0); + trig_pos_seq = aw_haptic->trig[1].pos_enable << 7 | aw_haptic->trig[1].pos_sequence; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_TRGCFG2, + &trig_pos_seq, AW_I2C_BYTE_ONE); + trig_neg_seq = aw_haptic->trig[1].neg_enable << 7 | aw_haptic->trig[1].neg_sequence; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_TRGCFG5, + &trig_neg_seq, AW_I2C_BYTE_ONE); + aw_info("trig2 config ok!"); + break; + case AW_TRIG3: + trig_polar_lev_brk = aw_haptic->trig[2].trig_polar << 2 | + aw_haptic->trig[2].trig_level << 1 | + aw_haptic->trig[2].trig_brk; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_TRGCFG8, + AW8623X_BIT_TRGCFG8_TRG3_POR_LEV_BRK_MASK, + trig_polar_lev_brk << 3); + trig_pos_seq = aw_haptic->trig[2].pos_enable << 7 | aw_haptic->trig[2].pos_sequence; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_TRGCFG3, + &trig_pos_seq, AW_I2C_BYTE_ONE); + trig_neg_seq = aw_haptic->trig[2].neg_enable << 7 | aw_haptic->trig[2].neg_sequence; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_TRGCFG6, + &trig_neg_seq, AW_I2C_BYTE_ONE); + aw_info("trig3 config ok!"); + break; + default: + break; + } + + return 0; +} + +static void aw8623x_set_trig(struct aw_haptic *aw_haptic, uint8_t pin) +{ + aw8623x_trig_param_init(aw_haptic, pin); + aw8623x_trig_param_config(aw_haptic, pin); +} + +static void aw8623x_trig_init(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + aw8623x_set_trig(aw_haptic, AW_TRIG1); + aw8623x_set_trig(aw_haptic, AW_TRIG2); + aw8623x_set_trig(aw_haptic, AW_TRIG3); +} + +static void aw8623x_parse_dts(struct aw_haptic *aw_haptic, struct device_node *np) +{ + uint32_t duration_time[3]; + uint32_t trig_config_temp[21]; + uint32_t val = 0; + + val = of_property_read_u32(np, "aw8623x_gain_bypass", &aw_haptic->info.gain_bypass); + if (val != 0) + aw_info("aw8623x_gain_bypass not found"); + val = of_property_read_u32(np, "aw8623x_vib_lk_f0_cali", &aw_haptic->info.lk_f0_cali); + if (val != 0) + aw_info("aw8623x_vib_lk_f0_cali not found"); + val = of_property_read_u32(np, "aw8623x_vib_mode", &aw_haptic->info.mode); + if (val != 0) + aw_info("aw8623x_vib_mode not found"); + val = of_property_read_u32(np, "aw8623x_vib_f0_pre", &aw_haptic->info.f0_pre); + if (val != 0) + aw_info("vib_f0_pre not found"); + val = of_property_read_u32(np, "aw8623x_vib_f0_cali_percen", + &aw_haptic->info.f0_cali_percent); + if (val != 0) + aw_info("vib_f0_cali_percent not found"); + val = of_property_read_u32(np, "aw8623x_vib_cont_drv1_lvl", &aw_haptic->info.cont_drv1_lvl); + if (val != 0) + aw_info("vib_cont_drv1_lvl not found"); + val = of_property_read_u32(np, "aw8623x_vib_lra_vrms", &aw_haptic->info.lra_vrms); + if (val != 0) + aw_info("vib_cont_lra_vrms not found"); + val = of_property_read_u32(np, "aw8623x_vib_cont_brk_time", &aw_haptic->info.cont_brk_time); + if (val != 0) + aw_info("vib_cont_brk_time not found"); + val = of_property_read_u32(np, "aw8623x_vib_cont_brk_gain", &aw_haptic->info.cont_brk_gain); + if (val != 0) + aw_info("vib_cont_brk_gain not found"); + val = of_property_read_u32(np, "aw8623x_vib_cont_drv1_time", + &aw_haptic->info.cont_drv1_time); + if (val != 0) + aw_info("vib_cont_drv1_time not found"); + val = of_property_read_u32(np, "aw8623x_vib_cont_drv2_time", + &aw_haptic->info.cont_drv2_time); + if (val != 0) + aw_info("vib_cont_drv2_time not found"); + val = of_property_read_u32(np, "aw8623x_vib_cont_track_margin", + &aw_haptic->info.cont_track_margin); + if (val != 0) + aw_info("vib_cont_track_margin not found"); + val = of_property_read_u32(np, "aw8623x_vib_d2s_gain", &aw_haptic->info.d2s_gain); + if (val != 0) + aw_info("vib_d2s_gain not found"); + val = of_property_read_u32_array(np, "aw8623x_vib_trig_config", trig_config_temp, + ARRAY_SIZE(trig_config_temp)); + if (val != 0) + aw_info("vib_trig_config not found"); + else + memcpy(aw_haptic->info.trig_cfg, trig_config_temp, sizeof(trig_config_temp)); + val = of_property_read_u32_array(np, "aw8623x_vib_duration_time", duration_time, + ARRAY_SIZE(duration_time)); + if (val != 0) + aw_info("aw8623x_vib_duration_time not found"); + else + memcpy(aw_haptic->info.duration_time, duration_time, sizeof(duration_time)); + aw_haptic->info.is_enabled_track_en = + of_property_read_bool(np, "aw8623x_vib_is_enabled_track_en"); + aw_info("aw_haptic->info.is_enabled_track_en = %d", aw_haptic->info.is_enabled_track_en); + aw_haptic->info.is_enabled_auto_brk = + of_property_read_bool(np, "aw8623x_vib_is_enabled_auto_brk"); + aw_info("aw_haptic->info.is_enabled_auto_brk = %d", + aw_haptic->info.is_enabled_auto_brk); +} + +static void aw8623x_get_wav_seq(struct aw_haptic *aw_haptic, uint32_t len) +{ + if (len > AW_SEQUENCER_SIZE) + len = AW_SEQUENCER_SIZE; + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_WAVCFG1, aw_haptic->seq, len); +} + +static void aw8623x_set_wav_seq(struct aw_haptic *aw_haptic, uint8_t wav, uint8_t seq) +{ + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_WAVCFG1 + wav, &seq, AW_I2C_BYTE_ONE); +} + +static void aw8623x_set_wav_loop(struct aw_haptic *aw_haptic, uint8_t wav, uint8_t loop) +{ + uint8_t tmp = 0; + + if (wav % 2) { + tmp = loop << 0; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_WAVCFG9 + (wav / 2), + AW8623X_BIT_WAVLOOP_SEQ_EVEN_MASK, tmp); + } else { + tmp = loop << 4; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_WAVCFG9 + (wav / 2), + AW8623X_BIT_WAVLOOP_SEQ_ODD_MASK, tmp); + } +} + +static void aw8623x_set_repeat_seq(struct aw_haptic *aw_haptic, uint8_t seq) +{ + aw8623x_set_wav_seq(aw_haptic, 0x00, seq); + aw8623x_set_wav_seq(aw_haptic, 0x01, 0x00); + aw8623x_set_wav_loop(aw_haptic, 0x00, AW8623X_BIT_WAVLOOP_INIFINITELY); +} + +static void aw8623x_get_gain(struct aw_haptic *aw_haptic, uint8_t *gain) +{ + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_PLAYCFG2, gain, AW_I2C_BYTE_ONE); +} + +static void aw8623x_get_wav_loop(struct aw_haptic *aw_haptic, uint8_t *val) +{ + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_WAVCFG9, val, AW_SEQUENCER_LOOP_SIZE); +} + +static void aw8623x_get_ram_data(struct aw_haptic *aw_haptic, uint8_t *ram_data, int size) +{ + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_RAMDATA, ram_data, size); +} + +static void aw8623x_get_lra_resistance(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val[2] = {0}; + uint8_t adc_fs = 0; + uint32_t lra_code = 0; + int d2s_gain = 0; + + /* enter standby mode */ + aw8623x_play_stop(aw_haptic); + aw8623x_raminit(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG2, AW8623X_BIT_DETCFG2_DET_SEQ0_MASK, + AW8623X_BIT_DETCFG2_DET_SEQ0_RL); + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_DETCFG1, &adc_fs, AW_I2C_BYTE_ONE); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG1, AW8623X_BIT_DETCFG1_ADC_FS_MASK, + AW8623X_BIT_DETCFG1_ADC_FS_24K); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG1, AW8623X_BIT_DETCFG1_DET_GO_MASK, + AW8623X_BIT_DETCFG1_DET_GO_ON); + usleep_range(AW_RL_DELAY_MIN, AW_RL_DELAY_MAX); + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG1, AW8623X_BIT_DETCFG1_DET_GO_MASK, + AW8623X_BIT_DETCFG1_DET_GO_OFF); + aw8623x_raminit(aw_haptic, false); + /* restore adc fs */ + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_DETCFG1, AW8623X_BIT_DETCFG1_ADC_FS_MASK, + adc_fs); + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_DETCFG2, reg_val, AW_I2C_BYTE_ONE); + reg_val[0] = (~AW8623X_BIT_DETCFG2_D2S_GAIN_MASK) & reg_val[0]; + d2s_gain = aw8623x_select_d2s_gain(reg_val[0]); + if (d2s_gain <= 0) { + aw_err("d2s_gain is error"); + return; + } + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_DETRD1, reg_val, AW_I2C_BYTE_TWO); + lra_code = reg_val[0] & (~AW8623X_BIT_DETRD1_AVG_DATA_H_MASK); + lra_code = (lra_code << 8) | reg_val[1]; + /* 2num */ + aw_haptic->lra = AW8623X_RL_FORMULA(lra_code, d2s_gain); +} + +static uint8_t aw8623x_get_prctmode(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_PWMCFG3, ®_val, AW_I2C_BYTE_ONE); + reg_val >>= 7; + + return reg_val; +} + +static uint8_t aw8623x_judge_rtp_going(struct aw_haptic *aw_haptic) +{ + uint8_t glb_state = 0; + uint8_t rtp_state = 0; + + glb_state = aw8623x_get_glb_state(aw_haptic); + if (glb_state == AW_BIT_STATE_RTP_GO) { + rtp_state = 1; /*is going on */ + aw_info("rtp_routine_on"); + } + + return rtp_state; +} + +static uint64_t aw8623x_get_theory_time(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t fre_val = 0; + uint64_t theory_time = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_SYSCTRL2, ®_val, AW_I2C_BYTE_ONE); + fre_val = reg_val & AW8623X_BIT_SYSCTRL2_RATE; + if (fre_val == AW8623X_BIT_SYSCTRL2_RATE_48K) + theory_time = (aw_haptic->rtp_len / 48) * 1000; /*48K*/ + else if (fre_val == AW8623X_BIT_SYSCTRL2_RATE_24K) + theory_time = (aw_haptic->rtp_len / 24) * 1000; /*24K*/ + else + theory_time = (aw_haptic->rtp_len / 12) * 1000; /*12K*/ + aw_info("microsecond:%llu theory_time = %llu", aw_haptic->microsecond, theory_time); + + return theory_time; +} + +static uint8_t aw8623x_get_osc_status(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_SYSST2, &state, AW_I2C_BYTE_ONE); + state &= AW8623X_BIT_SYSST2_FF_EMPTY; + + return state; +} + +static int aw8623x_check_qualify(struct aw_haptic *aw_haptic) +{ + uint8_t reg = 0; + int ret = 0; + + aw_info("enter"); + ret = haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_EFCFG6, ®, AW_I2C_BYTE_ONE); + if (ret < 0) + return ret; + if ((reg & 0x80) == 0x80) + return 0; + aw_err("register 0x64 error: 0x%02X", reg); + + return -ERANGE; +} + +static ssize_t cont_drv_lvl_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_lvl = 0x%02X, cont_drv2_lvl = 0x%02X\n", + aw_haptic->info.cont_drv1_lvl, aw_haptic->info.cont_drv2_lvl); + + return len; +} + +static ssize_t cont_drv_lvl_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint8_t reg_val = 0; + uint32_t databuf[2] = { 0, 0 }; + + mutex_lock(&aw_haptic->lock); + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_haptic->info.cont_drv1_lvl = databuf[0]; + aw_haptic->info.cont_drv2_lvl = databuf[1]; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_CONTCFG6, + AW8623X_BIT_CONTCFG6_DRV1_LVL_MASK, + aw_haptic->info.cont_drv1_lvl); + reg_val = (uint8_t)aw_haptic->info.cont_drv2_lvl; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG7, ®_val, AW_I2C_BYTE_ONE); + } + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t cont_drv_time_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_time = 0x%02X, cont_drv2_time = 0x%02X\n", + aw_haptic->info.cont_drv1_time, aw_haptic->info.cont_drv2_time); + + return len; +} + +static ssize_t cont_drv_time_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint8_t reg_val[2] = {0}; + uint32_t databuf[2] = { 0, 0 }; + + mutex_lock(&aw_haptic->lock); + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_haptic->info.cont_drv1_time = databuf[0]; + aw_haptic->info.cont_drv2_time = databuf[1]; + reg_val[0] = (uint8_t)aw_haptic->info.cont_drv1_time; + reg_val[1] = (uint8_t)aw_haptic->info.cont_drv2_time; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG8, reg_val, AW_I2C_BYTE_TWO); + } + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t cont_brk_time_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "cont_brk_time = 0x%02X\n", + aw_haptic->info.cont_brk_time); + + return len; +} + +static ssize_t cont_brk_time_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t reg_val = 0; + int err, val; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + err = kstrtoint(buf, 16, &val); + if (err != 0) { + aw_err("format not match!"); + return count; + } + mutex_lock(&aw_haptic->lock); + aw_haptic->info.cont_brk_time = val; + reg_val = aw_haptic->info.cont_brk_time; + haptic_nv_i2c_writes(aw_haptic, AW8623X_REG_CONTCFG10, ®_val, AW_I2C_BYTE_ONE); + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t trig_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int i = 0; + ssize_t len = 0; + uint8_t trig_num = 3; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + for (i = 0; i < trig_num; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, "trig%d: trig_level=%u, trig_polar=%u", + i + 1, aw_haptic->trig[i].trig_level, + aw_haptic->trig[i].trig_polar); + len += snprintf(buf + len, PAGE_SIZE - len, "pos_enable=%u, pos_sequence=%u,", + aw_haptic->trig[i].pos_enable, aw_haptic->trig[i].pos_sequence); + len += snprintf(buf + len, PAGE_SIZE - len, + "neg_enable=%u, neg_sequence=%u trig_brk=%u\n", + aw_haptic->trig[i].neg_enable, aw_haptic->trig[i].neg_sequence, + aw_haptic->trig[i].trig_brk); + } + + return len; +} + +static ssize_t trig_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t databuf[9] = { 0 }; + + if (sscanf(buf, "%u %u %u %u %u %u %u %u", &databuf[0], &databuf[1], &databuf[2], + &databuf[3], &databuf[4], &databuf[5], &databuf[6], &databuf[7]) == 8) { + aw_info("%u, %u, %u, %u, %u, %u, %u, %u", databuf[0], databuf[1], databuf[2], + databuf[3], databuf[4], databuf[5], databuf[6], databuf[7]); + if (databuf[0] < 0 || databuf[0] > 2) { + aw_err("input seq value out of range!"); + return count; + } + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + if (databuf[4] > aw_haptic->ram.ram_num || databuf[6] > aw_haptic->ram.ram_num) { + aw_err("input seq value out of range!"); + return count; + } + aw_haptic->trig[databuf[0]].trig_level = databuf[1]; + aw_haptic->trig[databuf[0]].trig_polar = databuf[2]; + aw_haptic->trig[databuf[0]].pos_enable = databuf[3]; + aw_haptic->trig[databuf[0]].pos_sequence = databuf[4]; + aw_haptic->trig[databuf[0]].neg_enable = databuf[5]; + aw_haptic->trig[databuf[0]].neg_sequence = databuf[6]; + aw_haptic->trig[databuf[0]].trig_brk = databuf[7]; + mutex_lock(&aw_haptic->lock); + aw8623x_trig_param_config(aw_haptic, databuf[0]); + mutex_unlock(&aw_haptic->lock); + } else + aw_err("please input eight parameters"); + + return count; +} + +static ssize_t rtp_autosin_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + len = snprintf(buf, PAGE_SIZE, "rtp_cnt = %u\n", aw_haptic->rtp_cnt); + + return len; +} + +static ssize_t rtp_autosin_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + uint32_t val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + if (!aw_haptic->ram.base_addr) { + aw_err("no set FIFO space for autosin rtp play!"); + return count; + } + + mutex_lock(&aw_haptic->lock); + if ((val > 0) && (val < aw_haptic->rtp_num_max)) { + aw_haptic->state = 1; + aw_haptic->rtp_file_num = val; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_RTPCFG6, + AW8623X_BIT_RTPCFG6_EN_RTP_AUTO_SIN_MASK, + AW8623X_BIT_RTPCFG6_EN_RTP_AUTO_SIN_ENABLE); + } else if (val == 0) { + aw_haptic->state = 0; + haptic_nv_i2c_write_bits(aw_haptic, AW8623X_REG_RTPCFG6, + AW8623X_BIT_RTPCFG6_EN_RTP_AUTO_SIN_MASK, + AW8623X_BIT_RTPCFG6_EN_RTP_AUTO_SIN_DISABLE); + } else { + aw_haptic->state = 0; + aw_err("input number error:%u", val); + } + mutex_unlock(&aw_haptic->lock); + queue_work(aw_haptic->work_queue, &aw_haptic->rtp_work); + + return count; +} + +static DEVICE_ATTR_RW(cont_drv_lvl); +static DEVICE_ATTR_RW(cont_drv_time); +static DEVICE_ATTR_RW(cont_brk_time); +static DEVICE_ATTR_RW(trig); +static DEVICE_ATTR_RW(rtp_autosin); + +static struct attribute *aw8623x_vibrator_attributes[] = { + &dev_attr_cont_drv_lvl.attr, + &dev_attr_cont_drv_time.attr, + &dev_attr_cont_brk_time.attr, + &dev_attr_trig.attr, + &dev_attr_rtp_autosin.attr, + NULL +}; + +static struct attribute_group aw8623x_vibrator_attribute_group = { + .attrs = aw8623x_vibrator_attributes +}; + +static void aw8623x_creat_node(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + ret = sysfs_create_group(&aw_haptic->vib_dev.dev->kobj, &aw8623x_vibrator_attribute_group); + if (ret < 0) + aw_err("error create aw8623x sysfs attr files"); +} + +struct aw_haptic_func aw8623x_func_list = { + .ram_init = aw8623x_raminit, + .parse_dts = aw8623x_parse_dts, + .trig_init = aw8623x_trig_init, + .play_mode = aw8623x_play_mode, + .play_stop = aw8623x_play_stop, + .irq_clear = aw8623x_irq_clear, + .creat_node = aw8623x_creat_node, + .cont_config = aw8623x_cont_config, + .offset_cali = aw8623x_offset_cali, + .haptic_start = aw8623x_play_go, + .read_cont_f0 = aw8623x_read_cont_f0, + .check_qualify = aw8623x_check_qualify, + .judge_rtp_going = aw8623x_judge_rtp_going, + .protect_config = aw8623x_protect_config, + .misc_para_init = aw8623x_misc_para_init, + .interrupt_setup = aw8623x_interrupt_setup, + .rtp_get_fifo_afs = aw8623x_rtp_get_fifo_afs, + .rtp_get_fifo_aes = aw8623x_rtp_get_fifo_aes, + .vbat_mode_config = aw8623x_vbat_mode_config, + .calculate_cali_data = aw8623x_calculate_cali_data, + .set_gain = aw8623x_set_gain, + .get_gain = aw8623x_get_gain, + .set_wav_seq = aw8623x_set_wav_seq, + .get_wav_seq = aw8623x_get_wav_seq, + .set_wav_loop = aw8623x_set_wav_loop, + .get_wav_loop = aw8623x_get_wav_loop, + .set_ram_data = aw8623x_set_ram_data, + .get_ram_data = aw8623x_get_ram_data, + .set_fifo_addr = aw8623x_set_fifo_addr, + .get_fifo_addr = aw8623x_get_fifo_addr, + .set_rtp_aei = aw8623x_set_rtp_aei, + .set_rtp_data = aw8623x_set_rtp_data, + .set_ram_addr = aw8623x_set_ram_addr, + .set_trim_lra = aw8623x_set_trim_lra, + .set_base_addr = aw8623x_set_base_addr, + .set_repeat_seq = aw8623x_set_repeat_seq, + .get_f0 = aw8623x_get_f0, + .get_reg = aw8623x_get_reg, + .get_vbat = aw8623x_get_vbat, + .get_prctmode = aw8623x_get_prctmode, + .get_irq_state = aw8623x_get_irq_state, + .get_glb_state = aw8623x_get_glb_state, + .get_osc_status = aw8623x_get_osc_status, + .get_theory_time = aw8623x_get_theory_time, + .get_lra_resistance = aw8623x_get_lra_resistance, + .get_first_wave_addr = aw8623x_get_first_wave_addr, +}; diff --git a/drivers/misc/awinic/aw862x_haptic_nv_v1/aw8624x.c b/drivers/misc/awinic/aw862x_haptic_nv_v1/aw8624x.c new file mode 100644 index 000000000000..c13dc2f481b4 --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv_v1/aw8624x.c @@ -0,0 +1,1392 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * aw8624x.c + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "haptic_nv.h" +#include "haptic_nv_reg.h" + +static void aw8624x_interrupt_setup(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + aw_info("enter"); + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_SYSINT, ®_val, AW_I2C_BYTE_ONE); + aw_info("reg SYSINT=0x%02X", reg_val); + /* edge int mode */ + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSCTRL5, + (AW8624X_BIT_SYSCTRL5_INT_MODE_MASK & + AW8624X_BIT_SYSCTRL5_INT_EDGE_MODE_MASK), + (AW8624X_BIT_SYSCTRL5_INT_MODE_EDGE | + AW8624X_BIT_SYSCTRL5_INT_EDGE_MODE_POS)); + /* int enable */ + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSINTM, + (AW8624X_BIT_SYSINTM_UVLM_MASK & AW8624X_BIT_SYSINTM_FF_AEM_MASK & + AW8624X_BIT_SYSINTM_FF_AFM_MASK & AW8624X_BIT_SYSINTM_OCDM_MASK & + AW8624X_BIT_SYSINTM_OTM_MASK & AW8624X_BIT_SYSINTM_DONEM_MASK), + (AW8624X_BIT_SYSINTM_UVLM_ON | AW8624X_BIT_SYSINTM_FF_AEM_OFF | + AW8624X_BIT_SYSINTM_FF_AFM_OFF | AW8624X_BIT_SYSINTM_OCDM_ON | + AW8624X_BIT_SYSINTM_OTM_ON | AW8624X_BIT_SYSINTM_DONEM_OFF)); +} + +static void aw8624x_set_rtp_data(struct aw_haptic *aw_haptic, uint8_t *data, uint32_t len) +{ + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_RTPDATA, data, len); +} + +static uint8_t aw8624x_get_glb_state(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_GLBRD5, &state, AW_I2C_BYTE_ONE); + + return state; +} + +static int aw8624x_get_irq_state(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int ret = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_SYSINT, ®_val, AW_I2C_BYTE_ONE); + aw_dbg("reg SYSINT=0x%02X", reg_val); + if (reg_val & AW8624X_BIT_SYSINT_UVLI) + aw_err("chip uvlo int error"); + if (reg_val & AW8624X_BIT_SYSINT_OCDI) + aw_err("chip over current int error"); + if (reg_val & AW8624X_BIT_SYSINT_OTI) + aw_err("chip over temperature int error"); + if (reg_val & AW8624X_BIT_SYSINT_DONEI) + aw_info("chip playback done"); + if (reg_val & AW8624X_BIT_SYSINT_FF_AFI) + aw_info("rtp mode fifo almost full!"); + if (reg_val & AW8624X_BIT_SYSINT_FF_AEI) { + aw_info("rtp fifo almost empty"); + ret = AW_IRQ_ALMOST_EMPTY; + } + + return ret; +} + +static uint8_t aw8624x_rtp_get_fifo_afs(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_SYSST, ®_val, AW_I2C_BYTE_ONE); + reg_val &= AW8624X_BIT_SYSST_FF_AFS; + reg_val = reg_val >> 3; + + return reg_val; +} + +static uint8_t aw8624x_rtp_get_fifo_aes(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_SYSST, ®_val, AW_I2C_BYTE_ONE); + reg_val &= AW8624X_BIT_SYSST_FF_AES; + reg_val = reg_val >> 4; + + return reg_val; +} + +static void aw8624x_set_rtp_aei(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSINTM, + AW8624X_BIT_SYSINTM_FF_AEM_MASK, + AW8624X_BIT_SYSINTM_FF_AEM_ON); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSINTM, + AW8624X_BIT_SYSINTM_FF_AEM_MASK, + AW8624X_BIT_SYSINTM_FF_AEM_OFF); + } +} + +static void aw8624x_auto_brk_config(struct aw_haptic *aw_haptic, uint8_t flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_PLAYCFG3, + AW8624X_BIT_PLAYCFG3_BRK_EN_MASK, + AW8624X_BIT_PLAYCFG3_BRK_ENABLE); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_PLAYCFG3, + AW8624X_BIT_PLAYCFG3_BRK_EN_MASK, + AW8624X_BIT_PLAYCFG3_BRK_DISABLE); + } +} + +static void aw8624x_config(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + aw_info("enter"); + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_ANACFG8, + AW8624X_BIT_ANACFG8_TRTF_CTRL_HDRV_MASK, + AW8624X_BIT_ANACFG8_TRTF_CTRL_HDRV); + if (aw_haptic->info.cont_brk_time) { + reg_val = (uint8_t)aw_haptic->info.cont_brk_time; + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_CONTCFG10, ®_val, AW_I2C_BYTE_ONE); + } else { + aw_err("dts_info.cont_brk_time=0"); + } + if (aw_haptic->info.cont_brk_gain) { + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_CONTCFG5, + AW8624X_BIT_CONTCFG5_BRK_GAIN_MASK, + aw_haptic->info.cont_brk_gain); + } else { + aw_err("dts_info.cont_brk_gain=0"); + } + +} + +static void aw8624x_play_stop(struct aw_haptic *aw_haptic) +{ + bool force_flag = true; + uint8_t val = 0; + int cnt = 40; + + aw_info("enter"); + aw_haptic->play_mode = AW_STANDBY_MODE; + val = AW8624X_BIT_PLAYCFG4_STOP_ON; + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_PLAYCFG4, &val, AW_I2C_BYTE_ONE); + while (cnt) { + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_GLBRD5, &val, AW_I2C_BYTE_ONE); + if ((val & AW_BIT_GLBRD_STATE_MASK) == AW_BIT_STATE_STANDBY) { + force_flag = false; + aw_info("entered standby! glb_state=0x%02X", val); + break; + } + cnt--; + usleep_range(AW_STOP_DELAY_MIN, AW_STOP_DELAY_MAX); + } + if (force_flag) { + aw_err("force to enter standby mode!"); + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSCTRL2, + AW8624X_BIT_SYSCTRL2_STANDBY_MASK, + AW8624X_BIT_SYSCTRL2_STANDBY_ON); + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSCTRL2, + AW8624X_BIT_SYSCTRL2_STANDBY_MASK, + AW8624X_BIT_SYSCTRL2_STANDBY_OFF); + } +} + +static void aw8624x_set_pwm(struct aw_haptic *aw_haptic, uint8_t mode) +{ + switch (mode) { + case AW_PWM_48K: + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSCTRL2, + AW8624X_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW8624X_BIT_SYSCTRL2_RATE_48K); + break; + case AW_PWM_24K: + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSCTRL2, + AW8624X_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW8624X_BIT_SYSCTRL2_RATE_24K); + break; + case AW_PWM_12K: + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSCTRL2, + AW8624X_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW8624X_BIT_SYSCTRL2_RATE_12K); + break; + default: + break; + } +} + +static void aw8624x_play_mode(struct aw_haptic *aw_haptic, uint8_t play_mode) +{ + switch (play_mode) { + case AW_STANDBY_MODE: + aw_info("enter standby mode"); + aw_haptic->play_mode = AW_STANDBY_MODE; + aw8624x_play_stop(aw_haptic); + break; + case AW_RAM_MODE: + aw_info("enter ram mode"); + aw_haptic->play_mode = AW_RAM_MODE; + aw8624x_set_pwm(aw_haptic, AW_PWM_12K); + aw8624x_auto_brk_config(aw_haptic, false); + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_PLAYCFG3, + AW8624X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8624X_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + case AW_RAM_LOOP_MODE: + aw_info("enter ram loop mode"); + aw_haptic->play_mode = AW_RAM_LOOP_MODE; + aw8624x_set_pwm(aw_haptic, AW_PWM_12K); + aw8624x_auto_brk_config(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_PLAYCFG3, + AW8624X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8624X_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + case AW_CONT_MODE: + aw_info("enter cont mode"); + aw_haptic->play_mode = AW_CONT_MODE; + aw8624x_auto_brk_config(aw_haptic, aw_haptic->info.is_enabled_auto_brk); + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_PLAYCFG3, + AW8624X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8624X_BIT_PLAYCFG3_PLAY_MODE_CONT); + break; + case AW_RTP_MODE: + aw_info("enter rtp mode"); + aw_haptic->play_mode = AW_RTP_MODE; + aw8624x_set_pwm(aw_haptic, AW_PWM_24K); + aw8624x_auto_brk_config(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_PLAYCFG3, + AW8624X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8624X_BIT_PLAYCFG3_PLAY_MODE_RTP); + break; + case AW_TRIG_MODE: + aw_info("enter trig mode"); + aw_haptic->play_mode = AW_TRIG_MODE; + aw8624x_set_pwm(aw_haptic, AW_PWM_12K); + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_PLAYCFG3, + AW8624X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8624X_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + default: + aw_err("play mode %u error", play_mode); + break; + } +} + +static void aw8624x_irq_clear(struct aw_haptic *aw_haptic) +{ + uint8_t val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_SYSINT, &val, AW_I2C_BYTE_ONE); + aw_info("SYSINT=0x%02X", val); +} + +static void aw8624x_play_go(struct aw_haptic *aw_haptic, bool flag) +{ + uint8_t val = 0; + + aw_info("enter"); + if (flag == true) { + val = AW8624X_BIT_PLAYCFG4_GO_ON; + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_PLAYCFG4, &val, AW_I2C_BYTE_ONE); + } else { + val = AW8624X_BIT_PLAYCFG4_STOP_ON; + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_PLAYCFG4, &val, AW_I2C_BYTE_ONE); + } + val = aw8624x_get_glb_state(aw_haptic); + aw_info("reg:0x%02X=0x%02X", AW8624X_REG_GLBRD5, val); +} + +static void aw8624x_haptic_start(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + aw8624x_play_go(aw_haptic, true); +} + +static void aw8624x_raminit(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSCTRL1, + AW8624X_BIT_SYSCTRL1_RAMINIT_MASK, + AW8624X_BIT_SYSCTRL1_RAMINIT_ON); + usleep_range(1000, 1050); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSCTRL1, + AW8624X_BIT_SYSCTRL1_RAMINIT_MASK, + AW8624X_BIT_SYSCTRL1_RAMINIT_OFF); + } +} + +static void aw8624x_get_vbat(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t vbat_code = 0; + + aw_info("enter"); + aw8624x_play_stop(aw_haptic); + aw8624x_raminit(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_DETCFG2, AW8624X_BIT_DETCFG2_VBAT_GO_MASK, + AW8624X_BIT_DETCFG2_VABT_GO_ON); + usleep_range(AW_VBAT_DELAY_MIN, AW_VBAT_DELAY_MAX); + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_DET_VBAT, ®_val, AW_I2C_BYTE_ONE); + vbat_code = (vbat_code | reg_val) << 2; + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_DET_LO, ®_val, AW_I2C_BYTE_ONE); + vbat_code = vbat_code | ((reg_val & AW8624X_BIT_DET_LO_VBAT) >> 4); + aw8624x_raminit(aw_haptic, false); + aw_haptic->vbat = AW8624X_VBAT_FORMULA(vbat_code); + if (aw_haptic->vbat > AW_VBAT_MAX) { + aw_haptic->vbat = AW_VBAT_MAX; + aw_info("vbat max limit = %dmV", aw_haptic->vbat); + } + if (aw_haptic->vbat < AW_VBAT_MIN) { + aw_haptic->vbat = AW_VBAT_MIN; + aw_info("vbat min limit = %dmV", aw_haptic->vbat); + } + aw_info("vbat=%dmV, vbat_code=0x%02X", aw_haptic->vbat, vbat_code); +} + +static void aw8624x_set_gain(struct aw_haptic *aw_haptic, uint8_t gain) +{ + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_PLAYCFG2, &gain, AW_I2C_BYTE_ONE); +} + +static void aw8624x_set_trim_lra(struct aw_haptic *aw_haptic, uint8_t val) +{ + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_TRIMCFG3, + AW8624X_BIT_TRIMCFG3_TRIM_LRA_MASK, val); +} + +static void aw8624x_cont_config(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + aw_info("enter"); + /* work mode */ + aw8624x_play_mode(aw_haptic, AW_CONT_MODE); + /* cont config */ + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_CONTCFG6, + (AW8624X_BIT_CONTCFG6_TRACK_EN_MASK & + AW8624X_BIT_CONTCFG6_DRV1_LVL_MASK), + ((aw_haptic->info.is_enabled_track_en << 7) | + (uint8_t)aw_haptic->info.cont_drv1_lvl)); + reg_val = (uint8_t)aw_haptic->info.cont_drv2_lvl; + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_CONTCFG7, ®_val, AW_I2C_BYTE_ONE); + /* DRV2_TIME */ + reg_val = 0xFF; + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_CONTCFG9, ®_val, AW_I2C_BYTE_ONE); + /* cont play go */ + aw8624x_play_go(aw_haptic, true); +} + +static ssize_t aw8624x_get_reg(struct aw_haptic *aw_haptic, ssize_t len, char *buf) +{ + len = haptic_nv_read_reg_array(aw_haptic, buf, len, AW8624X_REG_ID, + AW8624X_REG_RTPDATA - 1); + if (!len) + return len; + len = haptic_nv_read_reg_array(aw_haptic, buf, len, AW8624X_REG_RTPDATA + 1, + AW8624X_REG_RAMDATA - 1); + if (!len) + return len; + len = haptic_nv_read_reg_array(aw_haptic, buf, len, AW8624X_REG_RAMDATA + 1, + AW8624X_REG_ANACFG8); + + return len; +} + +static void aw8624x_protect_config(struct aw_haptic *aw_haptic, uint8_t prtime, uint8_t prlvl) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_PWMCFG1, AW8624X_BIT_PWMCFG1_PRC_EN_MASK, + AW8624X_BIT_PWMCFG1_PRC_DISABLE); + if (prlvl != 0) { + /* Enable protection mode */ + aw_info("enable protection mode"); + reg_val = AW8624X_BIT_PWMCFG3_PR_ENABLE | + (prlvl & (~AW8624X_BIT_PWMCFG3_PRLVL_MASK)); + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_PWMCFG3, ®_val, AW_I2C_BYTE_ONE); + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_PWMCFG4, &prtime, AW_I2C_BYTE_ONE); + } else { + /* Disable */ + aw_info("disable protection mode"); + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_PWMCFG3, + AW8624X_BIT_PWMCFG3_PR_EN_MASK, + AW8624X_BIT_PWMCFG3_PR_DISABLE); + } +} + +static void aw8624x_misc_para_init(struct aw_haptic *aw_haptic) +{ + uint8_t val = 0; + uint8_t array[8] = {0}; + + aw_info("enter"); + /* Get seq and gain */ + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_WAVCFG1, &val, AW_I2C_BYTE_ONE); + aw_haptic->index = val & AW8624X_BIT_WAVCFG_SEQ; + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_PLAYCFG2, &val, AW_I2C_BYTE_ONE); + aw_haptic->gain = val; + aw_info("gain=0x%02X, index=0x%02X", aw_haptic->gain, aw_haptic->index); + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_WAVCFG1, array, AW_SEQUENCER_SIZE); + memcpy(aw_haptic->seq, array, AW_SEQUENCER_SIZE); + /* GAIN_BYPASS config */ + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSCTRL5, + AW8624X_BIT_SYSCTRL5_GAIN_BYPASS_MASK, + aw_haptic->info.gain_bypass << 6); + /* CONT SMART_LOOP config */ + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_CONTCFG5, + AW8624X_BIT_CONTCFG5_SMART_LOOP_MASK, + aw_haptic->info.is_enabled_smart_loop << 4); + /* RTP INT_BRK config */ + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSCTRL1, + AW8624X_BIT_SYSCTRL1_INT_BRK_MASK, + aw_haptic->info.is_enabled_inter_brake << 6); + + /* d2s_gain */ + if (!aw_haptic->info.d2s_gain) { + aw_err("dts_info.d2s_gain = 0!"); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSCTRL5, + AW8624X_BIT_SYSCTRL5_D2S_GAIN_MASK, + aw_haptic->info.d2s_gain); + } + + if (!aw_haptic->info.f0_d2s_gain) { + aw_err("dts_info.f0_d2s_gain = 0!"); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSCTRL6, + AW8624X_BIT_SYSCTRL6_F0_D2S_GAIN_MASK, + aw_haptic->info.f0_d2s_gain); + } + + aw_haptic->info.cont_drv2_lvl = AW8624X_DRV2_LVL_FORMULA(aw_haptic->info.f0_pre, + aw_haptic->info.lra_vrms); + aw_info("lra_vrms=%u, cont_drv2_lvl=0x%02X", aw_haptic->info.lra_vrms, + aw_haptic->info.cont_drv2_lvl); + if (aw_haptic->info.cont_drv2_lvl > AW8624X_DRV2_LVL_MAX) { + aw_err("cont_drv2_lvl[0x%02X] is error, restore max vale[0x%02X]", + aw_haptic->info.cont_drv2_lvl, AW8624X_DRV2_LVL_MAX); + aw_haptic->info.cont_drv2_lvl = AW8624X_DRV2_LVL_MAX; + } + aw8624x_config(aw_haptic); + aw8624x_set_pwm(aw_haptic, AW_PWM_12K); + aw8624x_protect_config(aw_haptic, AW8624X_PWMCFG4_PRTIME_DEFAULT_VALUE, + AW8624X_BIT_PWMCFG3_PRLVL_DEFAULT_VALUE); +} + +static int aw8624x_select_d2s_gain(uint8_t reg) +{ + int d2s_gain = 0; + + switch (reg) { + case AW8624X_BIT_SYSCTRL5_D2S_GAIN_1: + d2s_gain = 1; + break; + case AW8624X_BIT_SYSCTRL5_D2S_GAIN_2: + d2s_gain = 2; + break; + case AW8624X_BIT_SYSCTRL5_D2S_GAIN_4: + d2s_gain = 4; + break; + case AW8624X_BIT_SYSCTRL5_D2S_GAIN_5: + d2s_gain = 5; + break; + case AW8624X_BIT_SYSCTRL5_D2S_GAIN_8: + d2s_gain = 8; + break; + case AW8624X_BIT_SYSCTRL5_D2S_GAIN_10: + d2s_gain = 10; + break; + case AW8624X_BIT_SYSCTRL5_D2S_GAIN_20: + d2s_gain = 20; + break; + case AW8624X_BIT_SYSCTRL5_D2S_GAIN_40: + d2s_gain = 40; + break; + default: + d2s_gain = -1; + break; + } + + return d2s_gain; +} + +static int aw8624x_offset_cali(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val[2] = {0}; + int os_code = 0; + int d2s_gain = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_SYSCTRL5, reg_val, AW_I2C_BYTE_ONE); + reg_val[0] = (~AW8624X_BIT_SYSCTRL5_D2S_GAIN_MASK) & reg_val[0]; + d2s_gain = aw8624x_select_d2s_gain(reg_val[0]); + if (d2s_gain < 0) { + aw_err("d2s_gain is error"); + return -ERANGE; + } + aw8624x_raminit(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_DETCFG1, AW8624X_BIT_DETCFG1_RL_OS_MASK, + AW8624X_BIT_DETCFG1_OS); + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_DETCFG2, AW8624X_BIT_DETCFG2_DIAG_GO_MASK, + AW8624X_BIT_DETCFG2_DIAG_GO_ON); + usleep_range(3000, 3500); + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_DET_OS, ®_val[0], AW_I2C_BYTE_ONE); + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_DET_LO, ®_val[1], AW_I2C_BYTE_ONE); + aw8624x_raminit(aw_haptic, false); + os_code = (reg_val[1] & (~AW8624X_BIT_DET_LO_OS_MASK)) >> 2; + os_code = (reg_val[0] << 2) | os_code; + os_code = AW8624X_OS_FORMULA(os_code, d2s_gain); + aw_info("os_code is %d mV", os_code); + if (os_code > 15 || os_code < -15) + return -ERANGE; + + return 0; +} + +static void aw8624x_vbat_mode_config(struct aw_haptic *aw_haptic, uint8_t flag) +{ + uint8_t val = 0; + + aw_info("enter"); + if (flag == AW_CONT_VBAT_HW_COMP_MODE) { + val = AW8624X_BIT_GLBCFG2_START_DLY_250US; + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_GLBCFG2, &val, AW_I2C_BYTE_ONE); + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSCTRL1, + AW8624X_BIT_SYSCTRL1_VBAT_MODE_MASK, + AW8624X_BIT_SYSCTRL1_VBAT_MODE_HW); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSCTRL1, + AW8624X_BIT_SYSCTRL1_VBAT_MODE_MASK, + AW8624X_BIT_SYSCTRL1_VBAT_MODE_SW); + } +} + +static void aw8624x_calculate_cali_data(struct aw_haptic *aw_haptic) +{ + char f0_cali_lra = 0; + int f0_cali_step = 0; + + f0_cali_step = 100000 * ((int)aw_haptic->f0 - (int)aw_haptic->info.f0_pre) / + ((int)aw_haptic->info.f0_pre * AW8624X_F0_CALI_ACCURACY); + aw_info("f0_cali_step=%d", f0_cali_step); + if (f0_cali_step >= 0) { /*f0_cali_step >= 0 */ + if (f0_cali_step % 10 >= 5) + f0_cali_step = 64 + (f0_cali_step / 10 + 1); + else + f0_cali_step = 64 + f0_cali_step / 10; + } else { /* f0_cali_step < 0 */ + if (f0_cali_step % 10 <= -5) + f0_cali_step = 64 + (f0_cali_step / 10 - 1); + else + f0_cali_step = 64 + f0_cali_step / 10; + } + if (f0_cali_step >= 64) + f0_cali_lra = (char)f0_cali_step - 64; + else + f0_cali_lra = (char)f0_cali_step + 64; + /* update cali step */ + aw_haptic->f0_cali_data = (int)f0_cali_lra; + aw_info("f0_cali_data=0x%02X", aw_haptic->f0_cali_data); +} + +static void aw8624x_read_cont_f0(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t f0_reg = 0; + + aw_info("enter"); + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_CONTCFG13, val, AW_I2C_BYTE_TWO); + f0_reg = (f0_reg | val[0]) << 8; + f0_reg |= (val[1] << 0); + if (!f0_reg) { + aw_err("didn't get cont f0 because f0_reg value is 0!"); + aw_haptic->cont_f0 = 0; + return; + } + aw_haptic->cont_f0 = AW8624X_F0_FORMULA(f0_reg); + aw_info("cont_f0=%u", aw_haptic->cont_f0); +} + +static int aw8624x_read_lra_f0(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t f0_reg = 0; + + aw_info("enter"); + /* F_LRA_F0 */ + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_CONTCFG11, val, AW_I2C_BYTE_TWO); + f0_reg = (f0_reg | val[0]) << 8; + f0_reg |= (val[1] << 0); + if (!f0_reg) { + aw_haptic->f0 = 0; + aw_err("didn't get lra f0 because f0_reg value is 0!"); + return -ERANGE; + } + aw_haptic->f0 = AW8624X_F0_FORMULA(f0_reg); + aw_info("lra_f0=%u", aw_haptic->f0); + + return 0; +} + +static int aw8624x_get_f0(struct aw_haptic *aw_haptic) +{ + bool get_f0_flag = false; + uint8_t brk_en_temp = 0; + uint8_t val[3] = {0}; + int drv_width = 0; + int cnt = 200; + int ret = 0; + + aw_info("enter"); + aw_haptic->f0 = aw_haptic->info.f0_pre; + /* enter standby mode */ + aw8624x_play_stop(aw_haptic); + /* f0 calibrate work mode */ + aw8624x_play_mode(aw_haptic, AW_CONT_MODE); + /* enable f0 detect */ + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_CONTCFG1, + AW8624X_BIT_CONTCFG1_PEAK_NUM_MASK, + AW8624X_BIT_CONTCFG1_PEAK_NUM_3); + /* cont config */ + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_CONTCFG6, + AW8624X_BIT_CONTCFG6_TRACK_EN_MASK, + (aw_haptic->info.is_enabled_track_en << 7)); + /* enable auto brake */ + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_PLAYCFG3, &val[0], AW_I2C_BYTE_ONE); + brk_en_temp = AW8624X_BIT_PLAYCFG3_BRK & val[0]; + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_PLAYCFG3, AW8624X_BIT_PLAYCFG3_BRK_EN_MASK, + AW8624X_BIT_PLAYCFG3_BRK_ENABLE); + /* f0 driver level */ + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_CONTCFG6, + AW8624X_BIT_CONTCFG6_DRV1_LVL_MASK, aw_haptic->info.cont_drv1_lvl); + val[0] = (uint8_t)aw_haptic->info.cont_drv2_lvl; + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_CONTCFG7, val, AW_I2C_BYTE_ONE); + val[0] = (uint8_t)aw_haptic->info.cont_drv1_time; + val[1] = (uint8_t)aw_haptic->info.cont_drv2_time; + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_CONTCFG8, val, AW_I2C_BYTE_TWO); + /* TRACK_MARGIN */ + if (!aw_haptic->info.cont_track_margin) { + aw_err("dts_info.cont_track_margin = 0!"); + } else { + val[0] = (uint8_t)aw_haptic->info.cont_track_margin; + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_CONTCFG4, &val[0], AW_I2C_BYTE_ONE); + } + /* DRV_WIDTH */ + if (!aw_haptic->info.f0_pre) + return -ERANGE; + drv_width = AW8624X_DRV_WIDTH_FORMULA(aw_haptic->info.f0_pre, + aw_haptic->info.cont_track_margin, + aw_haptic->info.cont_brk_gain); + if (drv_width < AW_DRV_WIDTH_MIN) + drv_width = AW_DRV_WIDTH_MIN; + if (drv_width > AW_DRV_WIDTH_MAX) + drv_width = AW_DRV_WIDTH_MAX; + val[0] = (uint8_t)drv_width; + aw_info("cont_drv_width=0x%02X", val[0]); + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_CONTCFG3, &val[0], AW_I2C_BYTE_ONE); + /* cont play go */ + aw8624x_play_go(aw_haptic, true); + usleep_range(20000, 20500); + while (cnt) { + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_GLBRD5, &val[0], AW_I2C_BYTE_ONE); + if ((val[0] & AW_BIT_GLBRD_STATE_MASK) == AW_BIT_STATE_STANDBY) { + get_f0_flag = true; + aw_info("entered standby! glb_state=0x%02X", val[0]); + break; + } + cnt--; + aw_dbg("waitting for standby,glb_state=0x%02X", val[0]); + usleep_range(AW_F0_DELAY_MIN, AW_F0_DELAY_MAX); + } + if (get_f0_flag) { + ret = aw8624x_read_lra_f0(aw_haptic); + if (ret < 0) + aw_err("read lra f0 is failed"); + aw8624x_read_cont_f0(aw_haptic); + } else { + ret = -ERANGE; + aw_err("enter standby mode failed, stop reading f0!"); + } + /* disable f0 detect */ + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_CONTCFG1, + AW8624X_BIT_CONTCFG1_PEAK_NUM_MASK, + AW8624X_BIT_CONTCFG1_PEAK_NUM_NO_F0_DET); + /* recover auto break config */ + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_PLAYCFG3, + AW8624X_BIT_PLAYCFG3_BRK_EN_MASK, brk_en_temp); + + return ret; +} + +static void aw8624x_set_base_addr(struct aw_haptic *aw_haptic) +{ + uint32_t base_addr = 0; + uint8_t val = 0; + + aw_info("enter"); + base_addr = aw_haptic->ram.base_addr; + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_RTPCFG1, AW8624X_BIT_RTPCFG1_ADDRH_MASK, + (uint8_t)AW_SET_BASEADDR_H(base_addr)); + val = (uint8_t)AW_SET_BASEADDR_L(base_addr); + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_RTPCFG2, &val, AW_I2C_BYTE_ONE); +} + +static void aw8624x_set_fifo_addr(struct aw_haptic *aw_haptic) +{ + uint8_t ae_addr_h = 0; + uint8_t af_addr_h = 0; + uint8_t val[3] = {0}; + + aw_info("enter"); + ae_addr_h = (uint8_t)AW8624X_SET_AEADDR_H(aw_haptic->ram.base_addr); + af_addr_h = (uint8_t)AW8624X_SET_AFADDR_H(aw_haptic->ram.base_addr); + val[0] = ae_addr_h | af_addr_h; + val[1] = (uint8_t)AW8624X_SET_AEADDR_L(aw_haptic->ram.base_addr); + val[2] = (uint8_t)AW8624X_SET_AFADDR_L(aw_haptic->ram.base_addr); + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_RTPCFG3, val, AW_I2C_BYTE_THREE); +} + +static void aw8624x_get_fifo_addr(struct aw_haptic *aw_haptic) +{ + uint8_t ae_addr_h = 0; + uint8_t af_addr_h = 0; + uint8_t ae_addr_l = 0; + uint8_t af_addr_l = 0; + uint8_t val[3] = {0}; + + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_RTPCFG3, val, AW_I2C_BYTE_THREE); + ae_addr_h = ((val[0]) & AW8624X_BIT_RTPCFG3_FIFO_AEH) >> 4; + ae_addr_l = val[1]; + af_addr_h = ((val[0]) & AW8624X_BIT_RTPCFG3_FIFO_AFH); + af_addr_l = val[2]; + aw_info("almost_empty_threshold = %u,almost_full_threshold = %u", + (uint16_t)((ae_addr_h << 8) | ae_addr_l), + (uint16_t)((af_addr_h << 8) | af_addr_l)); +} + +static void aw8624x_set_ram_addr(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t base_addr = aw_haptic->ram.base_addr; + + aw_info("enter"); + val[0] = (uint8_t)AW_SET_RAMADDR_H(base_addr); + val[1] = (uint8_t)AW_SET_RAMADDR_L(base_addr); + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_RAMADDRH, val, AW_I2C_BYTE_TWO); +} + +static void aw8624x_set_ram_data(struct aw_haptic *aw_haptic, uint8_t *data, int len) +{ + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_RAMDATA, data, len); +} + +static void aw8624x_get_first_wave_addr(struct aw_haptic *aw_haptic, uint32_t *first_wave_addr) +{ + uint8_t val[3] = {0}; + + aw_info("enter"); + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_RAMDATA, val, AW_I2C_BYTE_THREE); + *first_wave_addr = (val[1] << 8 | val[2]); +} + +static void aw8624x_haptic_trig_param_init(struct aw_haptic *aw_haptic, uint8_t pin) +{ + struct aw_haptic_dts_info info = aw_haptic->info; + + switch (pin) { + case AW_TRIG1: + aw_haptic->trig[0].trig_level = info.trig_cfg[0]; + aw_haptic->trig[0].trig_polar = info.trig_cfg[1]; + aw_haptic->trig[0].pos_enable = info.trig_cfg[2]; + aw_haptic->trig[0].pos_sequence = info.trig_cfg[3]; + aw_haptic->trig[0].neg_enable = info.trig_cfg[4]; + aw_haptic->trig[0].neg_sequence = info.trig_cfg[5]; + aw_haptic->trig[0].trig_brk = info.trig_cfg[6]; + break; + default: + break; + } +} + +static int aw8624x_haptic_trig_param_config(struct aw_haptic *aw_haptic, uint8_t pin) +{ + uint8_t trig_polar_lev_brk = 0x00; + uint8_t trig_pos_seq = 0x00; + uint8_t trig_neg_seq = 0x00; + + switch (pin) { + case AW_TRIG1: + trig_polar_lev_brk = aw_haptic->trig[0].trig_polar << 2 | + aw_haptic->trig[0].trig_level << 1 | + aw_haptic->trig[0].trig_brk; + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_TRGCFG7, + AW8624X_BIT_TRGCFG7_TRG1_POR_LEV_BRK_MASK, + trig_polar_lev_brk << 5); + trig_pos_seq = aw_haptic->trig[0].pos_enable << 7 | aw_haptic->trig[0].pos_sequence; + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_TRGCFG1, + &trig_pos_seq, AW_I2C_BYTE_ONE); + trig_neg_seq = aw_haptic->trig[0].neg_enable << 7 | aw_haptic->trig[0].neg_sequence; + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_TRGCFG4, + &trig_neg_seq, AW_I2C_BYTE_ONE); + aw_info("trig1 config ok!"); + break; + default: + break; + } + + return 0; +} + +static void aw8624x_set_trig(struct aw_haptic *aw_haptic, uint8_t pin) +{ + aw_info("enter"); + aw8624x_haptic_trig_param_init(aw_haptic, pin); + aw8624x_haptic_trig_param_config(aw_haptic, pin); +} + +static void aw8624x_trig_init(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + aw8624x_set_trig(aw_haptic, AW_TRIG1); +} + +static void aw8624x_parse_dts(struct aw_haptic *aw_haptic, struct device_node *np) +{ + uint32_t duration_time[3]; + uint32_t trig_config_temp[21]; + uint32_t val = 0; + + val = of_property_read_u32(np, "aw8624x_gain_bypass", &aw_haptic->info.gain_bypass); + if (val != 0) + aw_info("aw8624x_gain_bypass not found"); + val = of_property_read_u32(np, "aw8624x_vib_lk_f0_cali", &aw_haptic->info.lk_f0_cali); + if (val != 0) + aw_info("aw8624x_vib_lk_f0_cali not found"); + val = of_property_read_u32(np, "aw8624x_vib_mode", &aw_haptic->info.mode); + if (val != 0) + aw_info("aw8624x_vib_mode not found"); + val = of_property_read_u32(np, "aw8624x_vib_f0_pre", &aw_haptic->info.f0_pre); + if (val != 0) + aw_info("vib_f0_pre not found"); + val = of_property_read_u32(np, "aw8624x_vib_f0_cali_percen", + &aw_haptic->info.f0_cali_percent); + if (val != 0) + aw_info("vib_f0_cali_percent not found"); + val = of_property_read_u32(np, "aw8624x_vib_cont_drv1_lvl", &aw_haptic->info.cont_drv1_lvl); + if (val != 0) + aw_info("vib_cont_drv1_lvl not found"); + val = of_property_read_u32(np, "aw8624x_vib_lra_vrms", &aw_haptic->info.lra_vrms); + if (val != 0) + aw_info("vib_cont_lra_vrms not found"); + val = of_property_read_u32(np, "aw8624x_vib_cont_brk_time", &aw_haptic->info.cont_brk_time); + if (val != 0) + aw_info("vib_cont_brk_time not found"); + val = of_property_read_u32(np, "aw8624x_vib_cont_brk_gain", &aw_haptic->info.cont_brk_gain); + if (val != 0) + aw_info("vib_cont_brk_gain not found"); + val = of_property_read_u32(np, "aw8624x_vib_cont_drv1_time", + &aw_haptic->info.cont_drv1_time); + if (val != 0) + aw_info("vib_cont_drv1_time not found"); + val = of_property_read_u32(np, "aw8624x_vib_cont_drv2_time", + &aw_haptic->info.cont_drv2_time); + if (val != 0) + aw_info("vib_cont_drv2_time not found"); + val = of_property_read_u32(np, "aw8624x_vib_cont_track_margin", + &aw_haptic->info.cont_track_margin); + if (val != 0) + aw_info("vib_cont_track_margin not found"); + val = of_property_read_u32(np, "aw8624x_vib_d2s_gain", &aw_haptic->info.d2s_gain); + if (val != 0) + aw_info("vib_d2s_gain not found"); + val = of_property_read_u32(np, "aw8624x_vib_f0_d2s_gain", &aw_haptic->info.f0_d2s_gain); + if (val != 0) + aw_err("vib_f0_d2s_gain not found"); + val = of_property_read_u32_array(np, "aw8624x_vib_trig_config", trig_config_temp, + ARRAY_SIZE(trig_config_temp)); + if (val != 0) + aw_info("vib_trig_config not found"); + else + memcpy(aw_haptic->info.trig_cfg, trig_config_temp, sizeof(trig_config_temp)); + val = of_property_read_u32_array(np, "aw8624x_vib_duration_time", duration_time, + ARRAY_SIZE(duration_time)); + if (val != 0) + aw_info("aw8624x_duration_time not found"); + else + memcpy(aw_haptic->info.duration_time, duration_time, sizeof(duration_time)); + aw_haptic->info.is_enabled_track_en = + of_property_read_bool(np, "aw8624x_vib_is_enabled_track_en"); + aw_info("aw_haptic->info.is_enabled_track_en = %d", aw_haptic->info.is_enabled_track_en); + aw_haptic->info.is_enabled_smart_loop = + of_property_read_bool(np, "aw8624x_vib_is_enabled_smart_loop"); + aw_info("aw_haptic->info.is_enabled_smart_loop = %d", + aw_haptic->info.is_enabled_smart_loop); + aw_haptic->info.is_enabled_inter_brake = + of_property_read_bool(np, "aw8624x_vib_is_enabled_inter_brake"); + aw_info("aw_haptic->info.is_enabled_inter_brake = %d", + aw_haptic->info.is_enabled_inter_brake); + aw_haptic->info.is_enabled_auto_brk = + of_property_read_bool(np, "aw8624x_vib_is_enabled_auto_brk"); + aw_info("aw_haptic->info.is_enabled_auto_brk = %d", + aw_haptic->info.is_enabled_auto_brk); +} + +static void aw8624x_get_wav_seq(struct aw_haptic *aw_haptic, uint32_t len) +{ + if (len > AW_SEQUENCER_SIZE) + len = AW_SEQUENCER_SIZE; + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_WAVCFG1, aw_haptic->seq, len); +} + +static void aw8624x_set_wav_seq(struct aw_haptic *aw_haptic, uint8_t wav, uint8_t seq) +{ + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_WAVCFG1 + wav, &seq, AW_I2C_BYTE_ONE); +} + +static void aw8624x_set_wav_loop(struct aw_haptic *aw_haptic, uint8_t wav, uint8_t loop) +{ + uint8_t tmp = 0; + + if (wav % 2) { + tmp = loop << 0; + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_WAVCFG9 + (wav / 2), + AW8624X_BIT_WAVLOOP_SEQ_EVEN_MASK, tmp); + } else { + tmp = loop << 4; + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_WAVCFG9 + (wav / 2), + AW8624X_BIT_WAVLOOP_SEQ_ODD_MASK, tmp); + } +} + +static void aw8624x_set_repeat_seq(struct aw_haptic *aw_haptic, uint8_t seq) +{ + aw8624x_set_wav_seq(aw_haptic, 0x00, seq); + aw8624x_set_wav_seq(aw_haptic, 0x01, 0x00); + aw8624x_set_wav_loop(aw_haptic, 0x00, AW8624X_BIT_WAVLOOP_INIFINITELY); +} + +static void aw8624x_get_gain(struct aw_haptic *aw_haptic, uint8_t *gain) +{ + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_PLAYCFG2, gain, AW_I2C_BYTE_ONE); +} + +static void aw8624x_get_wav_loop(struct aw_haptic *aw_haptic, uint8_t *val) +{ + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_WAVCFG9, val, AW_SEQUENCER_LOOP_SIZE); +} + +static void aw8624x_get_ram_data(struct aw_haptic *aw_haptic, uint8_t *ram_data, int size) +{ + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_RAMDATA, ram_data, size); +} + +static void aw8624x_get_lra_resistance(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t lra_code = 0; + int d2s_gain = 0; + + d2s_gain = aw8624x_select_d2s_gain(aw_haptic->info.d2s_gain); + if (d2s_gain <= 0) { + aw_err("d2s_gain is error"); + return; + } + aw8624x_raminit(aw_haptic, true); + /* enter standby mode */ + aw8624x_play_stop(aw_haptic); + usleep_range(AW_STOP_DELAY_MIN, AW_STOP_DELAY_MAX); + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSCTRL2, AW8624X_BIT_SYSCTRL2_STANDBY_MASK, + AW8624X_BIT_SYSCTRL2_STANDBY_OFF); + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_DETCFG1, AW8624X_BIT_DETCFG1_RL_OS_MASK, + AW8624X_BIT_DETCFG1_RL); + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_DETCFG2, AW8624X_BIT_DETCFG2_DIAG_GO_MASK, + AW8624X_BIT_DETCFG2_DIAG_GO_ON); + usleep_range(AW_RL_DELAY_MIN, AW_RL_DELAY_MAX); + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_DET_RL, ®_val, AW_I2C_BYTE_ONE); + lra_code = (lra_code | reg_val) << 2; + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_DET_LO, ®_val, AW_I2C_BYTE_ONE); + lra_code = lra_code | (reg_val & AW8624X_BIT_DET_LO_RL); + aw8624x_raminit(aw_haptic, false); + aw_haptic->lra = AW8624X_RL_FORMULA(lra_code, d2s_gain); + aw_info("aw_haptic->lra = %u", aw_haptic->lra); +} + +static uint8_t aw8624x_get_prctmode(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_PWMCFG3, ®_val, AW_I2C_BYTE_ONE); + reg_val >>= 7; + + return reg_val; +} + +static uint8_t aw8624x_judge_rtp_going(struct aw_haptic *aw_haptic) +{ + uint8_t glb_state = 0; + uint8_t rtp_state = 0; + + glb_state = aw8624x_get_glb_state(aw_haptic); + if (glb_state == AW_BIT_STATE_RTP_GO) { + rtp_state = 1; /*is going on */ + aw_info("rtp_routine_on"); + } + + return rtp_state; +} + +static uint64_t aw8624x_get_theory_time(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t fre_val = 0; + uint64_t theory_time = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_SYSCTRL2, ®_val, AW_I2C_BYTE_ONE); + fre_val = reg_val & AW8624X_BIT_SYSCTRL2_RATE; + if (fre_val == AW8624X_BIT_SYSCTRL2_RATE_48K) + theory_time = aw_haptic->rtp_len * 1000 / 48; /*48K*/ + else if (fre_val == AW8624X_BIT_SYSCTRL2_RATE_24K) + theory_time = aw_haptic->rtp_len * 1000 / 24; /*24K*/ + else + theory_time = aw_haptic->rtp_len * 1000 / 12 ; /*12K*/ + aw_info("microsecond:%llu theory_time = %llu", aw_haptic->microsecond, theory_time); + + return theory_time; +} + +static uint8_t aw8624x_get_osc_status(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_SYSST2, &state, AW_I2C_BYTE_ONE); + state &= AW8624X_BIT_SYSST2_FF_EMPTY; + + return state; +} + +static int aw8624x_check_qualify(struct aw_haptic *aw_haptic) +{ + uint8_t reg = 0; + int ret = 0; + + aw_info("enter"); + ret = haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_EFRD9, ®, AW_I2C_BYTE_ONE); + if (ret < 0) + return ret; + if ((reg & 0x80) == 0x80) + return 0; + aw_err("register 0x64 error: 0x%02X", reg); + + return -ERANGE; +} + +static ssize_t cont_drv_lvl_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_lvl = 0x%02X, cont_drv2_lvl = 0x%02X\n", + aw_haptic->info.cont_drv1_lvl, aw_haptic->info.cont_drv2_lvl); + + return len; +} + +static ssize_t cont_drv_lvl_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t databuf[2] = { 0, 0 }; + + mutex_lock(&aw_haptic->lock); + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_haptic->info.cont_drv1_lvl = databuf[0]; + aw_haptic->info.cont_drv2_lvl = databuf[1]; + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_CONTCFG6, + AW8624X_BIT_CONTCFG6_DRV1_LVL_MASK, + (uint8_t)aw_haptic->info.cont_drv1_lvl); + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_CONTCFG7, + AW8624X_BIT_CONTCFG7_DRV2_LVL_MASK, + (uint8_t)aw_haptic->info.cont_drv2_lvl); + } + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t cont_drv_time_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_time = 0x%02X, cont_drv2_time = 0x%02X\n", + aw_haptic->info.cont_drv1_time, aw_haptic->info.cont_drv2_time); + + return len; +} + +static ssize_t cont_drv_time_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint8_t reg_val[2] = {0}; + uint32_t databuf[2] = { 0, 0 }; + + mutex_lock(&aw_haptic->lock); + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_haptic->info.cont_drv1_time = databuf[0]; + aw_haptic->info.cont_drv2_time = databuf[1]; + reg_val[0] = (uint8_t)aw_haptic->info.cont_drv1_time; + reg_val[1] = (uint8_t)aw_haptic->info.cont_drv2_time; + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_CONTCFG8, reg_val, AW_I2C_BYTE_TWO); + } + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t cont_brk_time_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "cont_brk_time = 0x%02X\n", + aw_haptic->info.cont_brk_time); + + return len; +} + +static ssize_t cont_brk_time_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t reg_val = 0; + int err, val; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + err = kstrtoint(buf, 16, &val); + if (err != 0) { + aw_err("format not match!"); + return count; + } + mutex_lock(&aw_haptic->lock); + aw_haptic->info.cont_brk_time = val; + reg_val = aw_haptic->info.cont_brk_time; + haptic_nv_i2c_writes(aw_haptic, AW8624X_REG_CONTCFG10, ®_val, AW_I2C_BYTE_ONE); + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t trig_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int i = 0; + ssize_t len = 0; + uint8_t trig_num = 3; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + for (i = 0; i < trig_num; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, "trig%d: trig_level=%u, trig_polar=%u", + i + 1, aw_haptic->trig[i].trig_level, + aw_haptic->trig[i].trig_polar); + len += snprintf(buf + len, PAGE_SIZE - len, "pos_enable=%u, pos_sequence=%u,", + aw_haptic->trig[i].pos_enable, aw_haptic->trig[i].pos_sequence); + len += snprintf(buf + len, PAGE_SIZE - len, + "neg_enable=%u, neg_sequence=%u trig_brk=%u\n", + aw_haptic->trig[i].neg_enable, aw_haptic->trig[i].neg_sequence, + aw_haptic->trig[i].trig_brk); + } + + return len; +} + +static ssize_t trig_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t databuf[9] = { 0 }; + + if (sscanf(buf, "%u %u %u %u %u %u %u %u", &databuf[0], &databuf[1], &databuf[2], + &databuf[3], &databuf[4], &databuf[5], &databuf[6], &databuf[7]) == 8) { + aw_info("%u, %u, %u, %u, %u, %u, %u, %u", databuf[0], databuf[1], databuf[2], + databuf[3], databuf[4], databuf[5], databuf[6], databuf[7]); + if (databuf[0] < 0 || databuf[0] > 2) { + aw_err("input seq value out of range!"); + return count; + } + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + if (databuf[4] > aw_haptic->ram.ram_num || databuf[6] > aw_haptic->ram.ram_num) { + aw_err("input seq value out of range!"); + return count; + } + aw_haptic->trig[databuf[0]].trig_level = databuf[1]; + aw_haptic->trig[databuf[0]].trig_polar = databuf[2]; + aw_haptic->trig[databuf[0]].pos_enable = databuf[3]; + aw_haptic->trig[databuf[0]].pos_sequence = databuf[4]; + aw_haptic->trig[databuf[0]].neg_enable = databuf[5]; + aw_haptic->trig[databuf[0]].neg_sequence = databuf[6]; + aw_haptic->trig[databuf[0]].trig_brk = databuf[7]; + mutex_lock(&aw_haptic->lock); + aw8624x_haptic_trig_param_config(aw_haptic, databuf[0]); + mutex_unlock(&aw_haptic->lock); + } else + aw_err("please input eight parameters"); + + return count; +} + +static ssize_t f0_d2s_gain_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw_haptic->lock); + switch (val) { + case 1: + aw_haptic->info.f0_d2s_gain = 0; + break; + case 2: + aw_haptic->info.f0_d2s_gain = 1; + break; + case 4: + aw_haptic->info.f0_d2s_gain = 2; + break; + case 5: + aw_haptic->info.f0_d2s_gain = 3; + break; + case 8: + aw_haptic->info.f0_d2s_gain = 4; + break; + case 10: + aw_haptic->info.f0_d2s_gain = 5; + break; + case 20: + aw_haptic->info.f0_d2s_gain = 6; + break; + case 40: + aw_haptic->info.f0_d2s_gain = 7; + break; + default: + aw_err("err f0_d2s_gain param"); + } + haptic_nv_i2c_write_bits(aw_haptic, AW8624X_REG_SYSCTRL6, + AW8624X_BIT_SYSCTRL6_F0_D2S_GAIN_MASK, + aw_haptic->info.f0_d2s_gain); + mutex_unlock(&aw_haptic->lock); + + return count; +} + + +static ssize_t f0_d2s_gain_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + int f0_d2s_gain = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + f0_d2s_gain = aw8624x_select_d2s_gain(aw_haptic->info.f0_d2s_gain); + len += snprintf(buf + len, PAGE_SIZE - len, "f0_d2s_gain = %d\n", f0_d2s_gain); + + return len; +} + +static DEVICE_ATTR_RW(cont_drv_lvl); +static DEVICE_ATTR_RW(cont_drv_time); +static DEVICE_ATTR_RW(cont_brk_time); +static DEVICE_ATTR_RW(trig); +static DEVICE_ATTR_RW(f0_d2s_gain); + +static struct attribute *aw8624x_vibrator_attributes[] = { + &dev_attr_cont_drv_lvl.attr, + &dev_attr_cont_drv_time.attr, + &dev_attr_cont_brk_time.attr, + &dev_attr_trig.attr, + &dev_attr_f0_d2s_gain.attr, + NULL +}; + +static struct attribute_group aw8624x_vibrator_attribute_group = { + .attrs = aw8624x_vibrator_attributes +}; + +static void aw8624x_creat_node(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + ret = sysfs_create_group(&aw_haptic->vib_dev.dev->kobj, &aw8624x_vibrator_attribute_group); + if (ret < 0) + aw_err("error create aw8624x sysfs attr files"); +} + +struct aw_haptic_func aw8624x_func_list = { + .ram_init = aw8624x_raminit, + .parse_dts = aw8624x_parse_dts, + .trig_init = aw8624x_trig_init, + .play_mode = aw8624x_play_mode, + .play_stop = aw8624x_play_stop, + .irq_clear = aw8624x_irq_clear, + .creat_node = aw8624x_creat_node, + .cont_config = aw8624x_cont_config, + .offset_cali = aw8624x_offset_cali, + .haptic_start = aw8624x_haptic_start, + .read_cont_f0 = aw8624x_read_cont_f0, + .check_qualify = aw8624x_check_qualify, + .judge_rtp_going = aw8624x_judge_rtp_going, + .protect_config = aw8624x_protect_config, + .misc_para_init = aw8624x_misc_para_init, + .interrupt_setup = aw8624x_interrupt_setup, + .rtp_get_fifo_afs = aw8624x_rtp_get_fifo_afs, + .rtp_get_fifo_aes = aw8624x_rtp_get_fifo_aes, + .vbat_mode_config = aw8624x_vbat_mode_config, + .calculate_cali_data = aw8624x_calculate_cali_data, + .set_gain = aw8624x_set_gain, + .get_gain = aw8624x_get_gain, + .set_wav_seq = aw8624x_set_wav_seq, + .get_wav_seq = aw8624x_get_wav_seq, + .set_wav_loop = aw8624x_set_wav_loop, + .get_wav_loop = aw8624x_get_wav_loop, + .set_ram_data = aw8624x_set_ram_data, + .get_ram_data = aw8624x_get_ram_data, + .set_fifo_addr = aw8624x_set_fifo_addr, + .get_fifo_addr = aw8624x_get_fifo_addr, + .set_rtp_aei = aw8624x_set_rtp_aei, + .set_rtp_data = aw8624x_set_rtp_data, + .set_ram_addr = aw8624x_set_ram_addr, + .set_trim_lra = aw8624x_set_trim_lra, + .set_base_addr = aw8624x_set_base_addr, + .set_repeat_seq = aw8624x_set_repeat_seq, + .get_f0 = aw8624x_get_f0, + .get_reg = aw8624x_get_reg, + .get_vbat = aw8624x_get_vbat, + .get_prctmode = aw8624x_get_prctmode, + .get_irq_state = aw8624x_get_irq_state, + .get_glb_state = aw8624x_get_glb_state, + .get_osc_status = aw8624x_get_osc_status, + .get_theory_time = aw8624x_get_theory_time, + .get_lra_resistance = aw8624x_get_lra_resistance, + .get_first_wave_addr = aw8624x_get_first_wave_addr, +}; diff --git a/drivers/misc/awinic/aw862x_haptic_nv_v1/aw862x.c b/drivers/misc/awinic/aw862x_haptic_nv_v1/aw862x.c new file mode 100644 index 000000000000..79ba98ce1853 --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv_v1/aw862x.c @@ -0,0 +1,1392 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * aw862x.c + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "haptic_nv.h" +#include "haptic_nv_reg.h" + +static int aw862x_get_irq_state(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint8_t glb_st = 0; + int ret = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_SYSINT, ®_val, AW_I2C_BYTE_ONE); + aw_dbg("reg SYSINT=0x%02X", reg_val); + if (reg_val & AW862X_BIT_SYSINT_OCDI) + aw_err("chip over current int error"); + if (reg_val & AW862X_BIT_SYSINT_OTI) + aw_err("chip over temperature int error"); + if (reg_val & AW862X_BIT_SYSINT_DONEI) + aw_info("chip playback done"); + if (reg_val & AW862X_BIT_SYSINT_UVLI) { + aw_err("chip uvlo int error"); + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_GLB_STATE, &glb_st, AW_I2C_BYTE_ONE); + if (glb_st == 0) { + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_SYSINTM, + AW862X_BIT_SYSINTM_UVLO_MASK, + AW862X_BIT_SYSINTM_UVLO_OFF); + } + } + if (reg_val & AW862X_BIT_SYSINT_FF_AFI) + aw_info("rtp mode fifo full"); + if (reg_val & AW862X_BIT_SYSINT_FF_AEI) { + aw_info("rtp fifo almost empty"); + ret = AW_IRQ_ALMOST_EMPTY; + } + + return ret; +} + +static void aw862x_interrupt_setup(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_SYSINT, ®_val, AW_I2C_BYTE_ONE); + aw_info("reg SYSINT=0x%X", reg_val); + + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_DBGCTRL, AW862X_BIT_DBGCTRL_INT_MODE_MASK, + AW862X_BIT_DBGCTRL_INT_MODE_EDGE); + + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_SYSINTM, (AW862X_BIT_SYSINTM_OV_MASK & + AW862X_BIT_SYSINTM_UVLO_MASK & AW862X_BIT_SYSINTM_FF_AE_MASK & + AW862X_BIT_SYSINTM_FF_AF_MASK & AW862X_BIT_SYSINTM_OCD_MASK & + AW862X_BIT_SYSINTM_OT_MASK & AW862X_BIT_SYSINTM_DONE_MASK), + (AW862X_BIT_SYSINTM_OV_OFF | AW862X_BIT_SYSINTM_UVLO_EN | + AW862X_BIT_SYSINTM_FF_AE_OFF | AW862X_BIT_SYSINTM_FF_AF_OFF | + AW862X_BIT_SYSINTM_OCD_EN | AW862X_BIT_SYSINTM_OT_EN | + AW862X_BIT_SYSINTM_DONE_OFF)); +} + +static void aw862x_set_rtp_data(struct aw_haptic *aw_haptic, uint8_t *data, uint32_t len) +{ + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_RTP_DATA, data, len); +} + +static uint8_t aw862x_get_glb_state(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_GLB_STATE, &state, AW_I2C_BYTE_ONE); + + return state; +} + +static uint8_t aw862x_rtp_get_fifo_afs(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_SYSST, ®_val, AW_I2C_BYTE_ONE); + reg_val &= AW862X_BIT_SYSST_FF_AFS; + reg_val = reg_val >> 3; + + return reg_val; +} + +static uint8_t aw862x_rtp_get_fifo_aes(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_SYSST, ®_val, AW_I2C_BYTE_ONE); + reg_val &= AW862X_BIT_SYSST_FF_AES; + reg_val = reg_val >> 4; + + return reg_val; +} + +static void aw862x_set_rtp_aei(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_SYSINTM, + AW862X_BIT_SYSINTM_FF_AE_MASK, + AW862X_BIT_SYSINTM_FF_AE_EN); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_SYSINTM, + AW862X_BIT_SYSINTM_FF_AE_MASK, + AW862X_BIT_SYSINTM_FF_AE_OFF); + } +} + +static int aw862x_haptic_play_init(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + if (aw_haptic->play_mode == AW_CONT_MODE) + reg_val = (uint8_t)(aw_haptic->info.sw_brake[0]); + else + reg_val = (uint8_t)(aw_haptic->info.sw_brake[1]); + + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_SW_BRAKE, ®_val, AW_I2C_BYTE_ONE); + + return 0; +} + +static void aw862x_interrupt_clear(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_SYSINT, ®_val, AW_I2C_BYTE_ONE); + aw_info("reg SYSINT=0x%2X", reg_val); +} + +static void aw862x_haptic_active(struct aw_haptic *aw_haptic) +{ + aw862x_haptic_play_init(aw_haptic); + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_SYSCTRL, AW862X_BIT_SYSCTRL_WORK_MODE_MASK, + AW862X_BIT_SYSCTRL_ACTIVE); + aw862x_interrupt_clear(aw_haptic); + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_SYSINTM, AW862X_BIT_SYSINTM_UVLO_MASK, + AW862X_BIT_SYSINTM_UVLO_EN); +} + +static void aw862x_play_mode(struct aw_haptic *aw_haptic, uint8_t play_mode) +{ + switch (play_mode) { + case AW_STANDBY_MODE: + aw_info("enter standby mode"); + aw_haptic->play_mode = AW_STANDBY_MODE; + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_SYSINTM, + AW862X_BIT_SYSINTM_UVLO_MASK, AW862X_BIT_SYSINTM_UVLO_OFF); + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_SYSCTRL, + AW862X_BIT_SYSCTRL_WORK_MODE_MASK, + AW862X_BIT_SYSCTRL_STANDBY); + break; + case AW_RAM_MODE: + aw_info("enter ram mode"); + aw_haptic->play_mode = AW_RAM_MODE; + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_SYSCTRL, + AW862X_BIT_SYSCTRL_PLAY_MODE_MASK, + AW862X_BIT_SYSCTRL_PLAY_MODE_RAM); + aw862x_haptic_active(aw_haptic); + break; + case AW_RAM_LOOP_MODE: + aw_info("enter ram loop mode"); + aw_haptic->play_mode = AW_RAM_LOOP_MODE; + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_SYSCTRL, + AW862X_BIT_SYSCTRL_PLAY_MODE_MASK, + AW862X_BIT_SYSCTRL_PLAY_MODE_RAM); + aw862x_haptic_active(aw_haptic); + break; + case AW_RTP_MODE: + aw_info("enter rtp mode"); + aw_haptic->play_mode = AW_RTP_MODE; + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_SYSCTRL, + AW862X_BIT_SYSCTRL_PLAY_MODE_MASK, + AW862X_BIT_SYSCTRL_PLAY_MODE_RTP); + aw862x_haptic_active(aw_haptic); + break; + case AW_TRIG_MODE: + aw_info("enter trig mode"); + aw_haptic->play_mode = AW_TRIG_MODE; + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_SYSCTRL, + AW862X_BIT_SYSCTRL_PLAY_MODE_MASK, + AW862X_BIT_SYSCTRL_PLAY_MODE_RAM); + aw862x_haptic_active(aw_haptic); + break; + case AW_CONT_MODE: + aw_info("enter cont mode"); + aw_haptic->play_mode = AW_CONT_MODE; + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_SYSCTRL, + AW862X_BIT_SYSCTRL_PLAY_MODE_MASK, + AW862X_BIT_SYSCTRL_PLAY_MODE_CONT); + aw862x_haptic_active(aw_haptic); + break; + default: + aw_err("play mode %u error", play_mode); + break; + } +} + +static int aw862x_play_go(struct aw_haptic *aw_haptic, bool flag) +{ + uint8_t val = 0; + + aw_info("enter, flag = %d", flag); + if (!flag) { + aw_haptic->current_t = ktime_get(); + aw_haptic->interval_us = ktime_to_us(ktime_sub(aw_haptic->current_t, + aw_haptic->pre_enter_t)); + if (aw_haptic->interval_us < 2000) { + aw_info("interval_us=%u", aw_haptic->interval_us); + usleep_range(AW_PLAY_DELAY_MIN, AW_PLAY_DELAY_MAX); + } + } + if (flag == true) { + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_GO, AW862X_BIT_GO_MASK, + AW862X_BIT_GO_ENABLE); + aw_haptic->pre_enter_t = ktime_get(); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_GO, AW862X_BIT_GO_MASK, + AW862X_BIT_GO_DISABLE); + } + val = aw862x_get_glb_state(aw_haptic); + aw_info("reg:0x%02X=0x%02X", AW862X_REG_GLB_STATE, val); + + return 0; +} + +static int aw862x_stop_delay(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int cnt = 100; + + while (cnt--) { + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_GLB_STATE, ®_val, AW_I2C_BYTE_ONE); + if ((reg_val & AW_BIT_GLBRD_STATE_MASK) == AW_BIT_STATE_STANDBY) { + aw_info("enter standby,reg glb_state=0x%02X", reg_val); + return 0; + } + usleep_range(AW_STOP_DELAY_MIN, AW_STOP_DELAY_MAX); + } + aw_err("do not enter standby automatically"); + + return 0; +} + +static void aw862x_play_stop(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + aw862x_play_go(aw_haptic, false); + aw862x_stop_delay(aw_haptic); + aw862x_play_mode(aw_haptic, AW_STANDBY_MODE); +} + +static void aw862x_haptic_start(struct aw_haptic *aw_haptic) +{ + aw862x_haptic_active(aw_haptic); + aw862x_play_go(aw_haptic, true); +} + +static void aw862x_raminit(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_SYSCTRL, + AW862X_BIT_SYSCTRL_RAMINIT_MASK, + AW862X_BIT_SYSCTRL_RAMINIT_EN); + usleep_range(1000, 1050); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_SYSCTRL, + AW862X_BIT_SYSCTRL_RAMINIT_MASK, + AW862X_BIT_SYSCTRL_RAMINIT_OFF); + } +} + +static void aw862x_get_vbat(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + aw862x_play_stop(aw_haptic); + /*step 1:EN_RAMINIT*/ + aw862x_raminit(aw_haptic, true); + /*step 2 :launch power supply testing */ + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_DETCTRL, AW862X_BIT_DETCTRL_VBAT_GO_MASK, + AW862X_BIT_DETCTRL_VABT_GO_ENABLE); + usleep_range(AW_VBAT_DELAY_MIN, AW_VBAT_DELAY_MAX); + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_VBATDET, ®_val, AW_I2C_BYTE_ONE); + aw_haptic->vbat = AW862X_VBAT_FORMULA(reg_val); + aw_info("get_vbat=%dmV, vbat_code=0x%02X", aw_haptic->vbat, reg_val); + /*step 3: return val*/ + aw862x_raminit(aw_haptic, false); +} + +static void aw862x_set_gain(struct aw_haptic *aw_haptic, uint8_t gain) +{ + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_DATDBG, &gain, AW_I2C_BYTE_ONE); +} + +static void aw862x_set_trim_lra(struct aw_haptic *aw_haptic, uint8_t val) +{ + uint8_t reg_val = 0; + + reg_val = val & AW862X_BIT_TRIM_LRA; + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_TRIM_LRA, ®_val, AW_I2C_BYTE_ONE); +} + +static int aw862x_set_f0_preset(struct aw_haptic *aw_haptic, uint32_t f0_pre) +{ + uint32_t f0_reg = 0; + uint8_t reg_array[2] = {0}; + + if (!f0_pre || !aw_haptic->info.f0_coeff) { + aw_err("f0_pre or f0_coeff is error, f0_pre=%u, f0_coeff=%u", + f0_pre, aw_haptic->info.f0_coeff); + return -ERANGE; + } + f0_reg = 1000000000 / (f0_pre * aw_haptic->info.f0_coeff); + reg_array[0] = (uint8_t)((f0_reg >> 8) & 0xff); + reg_array[1] = (uint8_t)((f0_reg >> 0) & 0xff); + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_F_PRE_H, reg_array, AW_I2C_BYTE_TWO); + + return 0; +} + +static void aw862x_cont_config(struct aw_haptic *aw_haptic) +{ + uint8_t brake0_level = 0; + uint8_t time_nzc = 0; + uint8_t en_brake1 = 0; + uint8_t brake1_level = 0; + uint8_t en_brake2 = 0; + uint8_t brake2_level = 0; + uint8_t brake2_num = 0; + uint8_t brake1_num = 0; + uint8_t brake0_num = 0; + uint8_t val[4] = {0}; + int ret = 0; + + aw_info("enter"); + aw862x_haptic_active(aw_haptic); + aw862x_play_mode(aw_haptic, AW_CONT_MODE); + /* preset f0 */ + if (aw_haptic->f0 <= 0) + ret = aw862x_set_f0_preset(aw_haptic, aw_haptic->info.f0_pre); + else + ret = aw862x_set_f0_preset(aw_haptic, aw_haptic->f0); + if (ret < 0) + return; + /* lpf */ + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_DATCTRL, (AW862X_BIT_DATCTRL_FC_MASK & + AW862X_BIT_DATCTRL_LPF_ENABLE_MASK), + (AW862X_BIT_DATCTRL_FC_1000HZ | AW862X_BIT_DATCTRL_LPF_ENABLE)); + /* brake */ + en_brake1 = aw_haptic->info.cont_brake[0]; + en_brake2 = aw_haptic->info.cont_brake[1]; + brake0_level = aw_haptic->info.cont_brake[2]; + brake1_level = aw_haptic->info.cont_brake[3]; + brake2_level = aw_haptic->info.cont_brake[4]; + brake0_num = aw_haptic->info.cont_brake[5]; + brake1_num = aw_haptic->info.cont_brake[6]; + brake2_num = aw_haptic->info.cont_brake[7]; + val[0] = brake0_level << 0; + val[1] = (en_brake1 << 7)|(brake1_level << 0); + val[2] = (en_brake2 << 7)|(brake2_level << 0); + val[3] = (brake2_num << 6) | (brake1_num << 3) | (brake0_num << 0); + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_BRAKE0_CTRL, val, AW_I2C_BYTE_FOUR); + /* cont config */ + val[0] = AW862X_BIT_CONT_CTRL_ZC_DETEC_ENABLE | AW862X_BIT_CONT_CTRL_WAIT_1PERIOD | + AW862X_BIT_CONT_CTRL_BY_GO_SIGNAL | AW862X_BIT_CONT_CTRL_CLOSE_PLAYBACK | + AW862X_BIT_CONT_CTRL_F0_DETECT_DISABLE | AW862X_BIT_CONT_CTRL_O2C_DISABLE; + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_CONT_CTRL, val, AW_I2C_BYTE_ONE); + /* TD time */ + val[0] = (uint8_t)(aw_haptic->info.cont_td>>8); + val[1] = (uint8_t)(aw_haptic->info.cont_td>>0); + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_TD_H, val, AW_I2C_BYTE_TWO); + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_BEMF_NUM, AW862X_BIT_BEMF_NUM_BRK_MASK, + aw_haptic->info.cont_num_brk); + time_nzc = AW862X_BIT_TIME_NZC_DEF_VAL; + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_TIME_NZC, &time_nzc, AW_I2C_BYTE_ONE); + /* f0 driver level */ + val[0] = aw_haptic->info.cont_drv1_lvl; + val[1] = aw_haptic->info.cont_drv2_lvl; + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_DRV_LVL, val, AW_I2C_BYTE_TWO); + aw862x_play_go(aw_haptic, true); +} + +static ssize_t aw862x_get_reg(struct aw_haptic *aw_haptic, ssize_t len, char *buf) +{ + len = haptic_nv_read_reg_array(aw_haptic, buf, len, AW862X_REG_ID, AW862X_REG_RTP_DATA - 1); + if (!len) + return len; + len = haptic_nv_read_reg_array(aw_haptic, buf, len, AW862X_REG_RTP_DATA + 1, + AW862X_REG_RAMDATA - 1); + if (!len) + return len; + len = haptic_nv_read_reg_array(aw_haptic, buf, len, AW862X_REG_RAMDATA + 1, + AW862X_REG_NUM_F0_3); + return len; +} + +static void aw862x_set_pwm(struct aw_haptic *aw_haptic, uint8_t mode) +{ + switch (mode) { + case AW_PWM_48K: + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_PWMDBG, + AW862X_BIT_PWMDBG_PWM_MODE_MASK, + AW862X_BIT_PWMDBG_PWM_48K); + break; + case AW_PWM_24K: + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_PWMDBG, + AW862X_BIT_PWMDBG_PWM_MODE_MASK, + AW862X_BIT_PWMDBG_PWM_24K); + break; + case AW_PWM_12K: + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_PWMDBG, + AW862X_BIT_PWMDBG_PWM_MODE_MASK, + AW862X_BIT_PWMDBG_PWM_12K); + break; + default: + break; + } +} + +static void aw862x_protect_config(struct aw_haptic *aw_haptic, uint8_t prtime, uint8_t prlvl) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_PWMPRC, AW862X_BIT_PWMPRC_PRC_EN_MASK, + AW862X_BIT_PWMPRC_PRC_DISABLE); + if (prlvl != 0) { + /* Enable protection mode */ + aw_info("enable protection mode"); + reg_val = AW862X_BIT_PRLVL_PR_ENABLE | (prlvl & (~AW862X_BIT_PRLVL_PRLVL_MASK)); + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_PRLVL, ®_val, AW_I2C_BYTE_ONE); + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_PRTIME, &prtime, AW_I2C_BYTE_ONE); + } else { + /* Disable */ + aw_info("disable protection mode"); + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_PRLVL, AW862X_BIT_PRLVL_PR_EN_MASK, + AW862X_BIT_PRLVL_PR_DISABLE); + } +} + +static void aw862x_misc_para_init(struct aw_haptic *aw_haptic) +{ + int ret = 0; + uint8_t reg_val = 0; + uint8_t reg_flag = 0; + uint8_t reg_array[8] = {0}; + + /* Get chipid_flag */ + ret = haptic_nv_i2c_reads(aw_haptic, AW862X_REG_EF_RDATAH, ®_flag, AW_I2C_BYTE_ONE); + if ((ret >= 0) && ((reg_flag & 0x1) == 1)) + aw_haptic->chipid_flag = 1; + else + aw_err("to read register AW862X_REG_EF_RDATAH: %d", ret); + /* Get seq and gain */ + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_DATDBG, ®_val, AW_I2C_BYTE_ONE); + aw_haptic->gain = reg_val & 0xFF; + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_WAVSEQ1, reg_array, AW_SEQUENCER_SIZE); + aw_haptic->index = reg_array[0] & 0x7F; + memcpy(aw_haptic->seq, reg_array, AW_SEQUENCER_SIZE); + /* r_spare */ + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_R_SPARE, AW862X_BIT_R_SPARE_MASK, + AW862X_BIT_R_SPARE_ENABLE); + /* LRA trim source select register */ + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_ANACTRL, AW862X_BIT_ANACTRL_LRA_SRC_MASK, + AW862X_BIT_ANACTRL_LRA_SRC_REG); + /* brake */ + reg_val = (uint8_t)aw_haptic->info.sw_brake[0]; + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_SW_BRAKE, ®_val, AW_I2C_BYTE_ONE); + reg_val = 0x00; + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_THRS_BRA_END, ®_val, AW_I2C_BYTE_ONE); + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_WAVECTRL, + AW862X_BIT_WAVECTRL_NUM_OV_DRIVER_MASK, + AW862X_BIT_WAVECTRL_NUM_OV_DRIVER); + /* zero cross */ + reg_array[0] = (uint8_t)(aw_haptic->info.cont_zc_thr >> 8); + reg_array[1] = (uint8_t)(aw_haptic->info.cont_zc_thr >> 0); + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_ZC_THRSH_H, reg_array, AW_I2C_BYTE_TWO); + /* cont_tset */ + reg_val = (uint8_t)aw_haptic->info.cont_tset; + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_TSET, ®_val, AW_I2C_BYTE_ONE); + /* bemf */ + reg_array[0] = (uint8_t)(aw_haptic->info.bemf_config[0]); + reg_array[1] = (uint8_t)(aw_haptic->info.bemf_config[1]); + reg_array[2] = (uint8_t)(aw_haptic->info.bemf_config[2]); + reg_array[3] = (uint8_t)(aw_haptic->info.bemf_config[3]); + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_BEMF_VTHH_H, reg_array, AW_I2C_BYTE_FOUR); + aw862x_set_pwm(aw_haptic, AW_PWM_24K); + aw862x_protect_config(aw_haptic, AW862X_REG_PRTIME_DEFAULT_VALUE, + AW862X_BIT_PRLVL_PRLVL_DEFAULT_VALUE); +} + +static int aw862x_offset_cali(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int cont = 2000; + + aw_info("enter"); + + aw862x_raminit(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_DETCTRL, AW862X_BIT_DETCTRL_DIAG_GO_MASK, + AW862X_BIT_DETCTRL_DIAG_GO_ENABLE); + while (cont) { + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_DETCTRL, ®_val, AW_I2C_BYTE_ONE); + if ((reg_val & 0x01) == 0 || cont == 0) + break; + cont--; + } + aw862x_raminit(aw_haptic, false); + if (cont == 0) { + aw_err("calibration offset failed!"); + return -ERANGE; + } + + return 0; +} + +static void aw862x_vbat_mode_config(struct aw_haptic *aw_haptic, uint8_t flag) +{ + if (flag == AW_CONT_VBAT_HW_COMP_MODE) { + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_ADCTEST, + AW862X_BIT_DETCTRL_VBAT_MODE_MASK, + AW862X_BIT_DETCTRL_VBAT_HW_COMP); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_ADCTEST, + AW862X_BIT_DETCTRL_VBAT_MODE_MASK, + AW862X_BIT_DETCTRL_VBAT_SW_COMP); + } +} + +static void aw862x_calculate_cali_data(struct aw_haptic *aw_haptic) +{ + char f0_cali_lra = 0; + int f0_cali_step = 0; + uint8_t chipid_flag = aw_haptic->chipid_flag; + + f0_cali_step = 100000 * ((int)aw_haptic->f0 - (int)aw_haptic->info.f0_pre) / + ((int)aw_haptic->info.f0_pre * AW862X_F0_CALI_ACCURACY); + if (f0_cali_step >= 0) { + if (f0_cali_step % 10 >= 5) + f0_cali_step = f0_cali_step / 10 + 1 + (chipid_flag == 1 ? 32 : 16); + else + f0_cali_step = f0_cali_step / 10 + (chipid_flag == 1 ? 32 : 16); + } else { + if (f0_cali_step % 10 <= -5) + f0_cali_step = (chipid_flag == 1 ? 32 : 16) + (f0_cali_step / 10 - 1); + else + f0_cali_step = (chipid_flag == 1 ? 32 : 16) + f0_cali_step / 10; + } + if (chipid_flag == 1) { + if (f0_cali_step > 31) + f0_cali_lra = (char)f0_cali_step - 32; + else + f0_cali_lra = (char)f0_cali_step + 32; + } else { + if (f0_cali_step < 16 || (f0_cali_step > 31 && f0_cali_step < 48)) + f0_cali_lra = (char)f0_cali_step + 16; + else + f0_cali_lra = (char)f0_cali_step - 16; + } + if ((chipid_flag != 1) && (aw_haptic->f0 <= aw_haptic->info.f0_pre * 97 / 100)) + f0_cali_lra = 16; + aw_haptic->f0_cali_data = (int)f0_cali_lra; + aw_info("f0_cali_data=0x%02X", aw_haptic->f0_cali_data); +} + +static int aw862x_read_f0(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t f0_reg = 0; + uint64_t f0_tmp = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_F_LRA_F0_H, val, AW_I2C_BYTE_TWO); + f0_reg = (val[0] << 8) | val[1]; + if (!f0_reg || !aw_haptic->info.f0_coeff) { + aw_haptic->f0 = 0; + aw_info("get f0 failed with the value becoming 0!"); + return -ERANGE; + } + f0_tmp = AW862X_F0_FORMULA(f0_reg, aw_haptic->info.f0_coeff); + aw_haptic->f0 = (uint32_t)f0_tmp; + aw_info("f0=%u", aw_haptic->f0); + + return 0; +} + +static void aw862x_read_beme(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_WAIT_VOL_MP, val, AW_I2C_BYTE_TWO); + aw_haptic->max_pos_beme = val[0]; + aw_haptic->max_neg_beme = val[1]; + aw_info("max_pos_beme=%u", aw_haptic->max_pos_beme); + aw_info("max_neg_beme=%u", aw_haptic->max_neg_beme); +} + +static int aw862x_get_f0(struct aw_haptic *aw_haptic) +{ + bool get_f0_flag = false; + uint8_t val[3] = {0}; + uint8_t f0_pre_num = 0; + uint8_t f0_wait_num = 0; + uint8_t f0_repeat_num = 0; + uint8_t f0_trace_num = 0; + uint32_t t_f0_ms = 0; + uint32_t t_f0_trace_ms = 0; + int ret = 0; + int cnt = 50; + + aw_info("enter"); + /* f0 calibrate work mode */ + aw862x_play_stop(aw_haptic); + aw862x_play_mode(aw_haptic, AW_CONT_MODE); + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_CONT_CTRL, + (AW862X_BIT_CONT_CTRL_EN_CLOSE_MASK & + AW862X_BIT_CONT_CTRL_F0_DETECT_MASK), + (AW862X_BIT_CONT_CTRL_OPEN_PLAYBACK | + AW862X_BIT_CONT_CTRL_F0_DETECT_ENABLE)); + /* LPF */ + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_DATCTRL, + (AW862X_BIT_DATCTRL_FC_MASK & AW862X_BIT_DATCTRL_LPF_ENABLE_MASK), + (AW862X_BIT_DATCTRL_FC_1000HZ | AW862X_BIT_DATCTRL_LPF_ENABLE)); + /* preset f0 */ + ret = aw862x_set_f0_preset(aw_haptic, aw_haptic->f0_pre); + if (ret < 0) + return -ERANGE; + /* f0 driver level */ + val[0] = (uint8_t)(aw_haptic->info.cont_drv1_lvl); + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_DRV_LVL, &val[0], AW_I2C_BYTE_ONE); + /* f0 trace parameter */ + if (!aw_haptic->f0_pre) { + aw_info("fail to get t_f0_ms"); + return 0; + } + f0_pre_num = aw_haptic->info.f0_trace_parameter[0]; + f0_wait_num = aw_haptic->info.f0_trace_parameter[1]; + f0_repeat_num = aw_haptic->info.f0_trace_parameter[2]; + f0_trace_num = aw_haptic->info.f0_trace_parameter[3]; + val[0] = (f0_pre_num << 4)|(f0_wait_num << 0); + val[1] = f0_repeat_num << 0; + val[2] = f0_trace_num << 0; + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_NUM_F0_1, val, AW_I2C_BYTE_THREE); + /* clear aw862x interrupt */ + ret = haptic_nv_i2c_reads(aw_haptic, AW862X_REG_SYSINT, &val[0], AW_I2C_BYTE_ONE); + /* play go and start f0 calibration */ + aw862x_play_go(aw_haptic, true); + /* f0 trace time */ + t_f0_ms = 1000*10 / aw_haptic->f0_pre; + t_f0_trace_ms = t_f0_ms * (f0_pre_num + f0_wait_num + + (f0_trace_num + f0_wait_num) * (f0_repeat_num - 1)); + aw_info("t_f0_trace_ms = %dms", t_f0_trace_ms); + usleep_range(t_f0_trace_ms * 1000, t_f0_trace_ms * 1000 + 500); + while (cnt) { + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_GLB_STATE, &val[0], AW_I2C_BYTE_ONE); + if (val[0] == AW_BIT_STATE_STANDBY) { + get_f0_flag = true; + aw_info("entered standby! glb_state=0x%02X", val[0]); + break; + } + cnt--; + aw_dbg("waitting for standby,glb_state=0x%02X", val[0]); + usleep_range(AW_F0_DELAY_MIN, AW_F0_DELAY_MAX); + } + if (get_f0_flag) { + ret = aw862x_read_f0(aw_haptic); + if (ret < 0) + aw_err("read lra f0 is failed"); + aw862x_read_beme(aw_haptic); + } else { + ret = -ERANGE; + aw_err("enter standby mode failed, stop reading f0!"); + } + + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_CONT_CTRL, + (AW862X_BIT_CONT_CTRL_EN_CLOSE_MASK & + AW862X_BIT_CONT_CTRL_F0_DETECT_MASK), + (AW862X_BIT_CONT_CTRL_CLOSE_PLAYBACK | + AW862X_BIT_CONT_CTRL_F0_DETECT_DISABLE)); + + return ret; +} + +#ifdef AW862X_MUL_GET_F0 +static int aw862x_multiple_get_f0(struct aw_haptic *aw_haptic) +{ + int i = 0; + int ret = 0; + int f0_max = aw_haptic->info.f0_pre + AW862X_MUL_GET_F0_RANGE; + int f0_min = aw_haptic->info.f0_pre - AW862X_MUL_GET_F0_RANGE; + + aw_haptic->f0_pre = aw_haptic->info.f0_pre; + for (i = 0; i < AW862X_MUL_GET_F0_NUM; i++) { + aw_info("f0_pre=%u", aw_haptic->f0_pre); + ret = aw862x_get_f0(aw_haptic); + if (ret) + return ret; + if (aw_haptic->f0 >= f0_max || aw_haptic->f0 <= f0_min) + break; + aw_haptic->f0_pre = aw_haptic->f0; + usleep_range(4000, 4500); + } + + return 0; +} +#endif + +static void aw862x_get_wav_seq(struct aw_haptic *aw_haptic, uint32_t len) +{ + if (len > AW_SEQUENCER_SIZE) + len = AW_SEQUENCER_SIZE; + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_WAVSEQ1, aw_haptic->seq, len); +} + +static void aw862x_set_wav_seq(struct aw_haptic *aw_haptic, uint8_t wav, uint8_t seq) +{ + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_WAVSEQ1 + wav, &seq, AW_I2C_BYTE_ONE); +} + +static void aw862x_set_wav_loop(struct aw_haptic *aw_haptic, uint8_t wav, uint8_t loop) +{ + uint8_t tmp = 0; + + if (wav % 2) { + tmp = loop << 0; + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_WAVLOOP1 + (wav / 2), + AW862X_BIT_WAVLOOP_SEQNP1_MASK, tmp); + } else { + tmp = loop << 4; + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_WAVLOOP1 + (wav / 2), + AW862X_BIT_WAVLOOP_SEQN_MASK, tmp); + } + +} + +static void aw862x_set_repeat_seq(struct aw_haptic *aw_haptic, uint8_t seq) +{ + aw862x_set_wav_seq(aw_haptic, 0x00, seq); + aw862x_set_wav_seq(aw_haptic, 0x01, 0x00); + aw862x_set_wav_loop(aw_haptic, 0x00, AW862X_BIT_WAVLOOP_INIFINITELY); +} + +static void aw862x_get_gain(struct aw_haptic *aw_haptic, uint8_t *gain) +{ + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_DATDBG, gain, AW_I2C_BYTE_ONE); +} + +static void aw862x_get_wav_loop(struct aw_haptic *aw_haptic, uint8_t *val) +{ + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_WAVLOOP1, val, AW_SEQUENCER_LOOP_SIZE); +} + +static void aw862x_get_ram_data(struct aw_haptic *aw_haptic, uint8_t *ram_data, int size) +{ + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_RAMDATA, ram_data, size); +} + +static void aw862x_read_cont_f0(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t f0_reg = 0; + uint64_t f0_tmp = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_F_LRA_CONT_H, val, AW_I2C_BYTE_TWO); + f0_reg = (val[0] << 8) | val[1]; + + if (!f0_reg) { + aw_haptic->cont_f0 = 0; + aw_info("failed to reading cont f0 with 0"); + return; + } + + f0_tmp = AW862X_F0_FORMULA(f0_reg, aw_haptic->info.f0_coeff); + aw_haptic->cont_f0 = (uint32_t)f0_tmp; + aw_info("cont_f0=%u", aw_haptic->cont_f0); +} + +static void aw862x_get_lra_resistance(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint8_t anactrl = 0; + uint8_t d2scfg = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_ANACTRL, &anactrl, AW_I2C_BYTE_ONE); + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_D2SCFG, &d2scfg, AW_I2C_BYTE_ONE); + aw862x_play_stop(aw_haptic); + aw862x_raminit(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_ANACTRL, AW862X_BIT_ANACTRL_EN_IO_PD1_MASK, + AW862X_BIT_ANACTRL_EN_IO_PD1_HIGH); + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_D2SCFG, AW862X_BIT_D2SCFG_CLK_ADC_MASK, + AW862X_BIT_D2SCFG_CLK_ASC_1P5MHZ); + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_DETCTRL, (AW862X_BIT_DETCTRL_RL_OS_MASK & + AW862X_BIT_DETCTRL_DIAG_GO_MASK), (AW862X_BIT_DETCTRL_RL_DETECT | + AW862X_BIT_DETCTRL_DIAG_GO_ENABLE)); + usleep_range(AW_RL_DELAY_MIN, AW_RL_DELAY_MAX); + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_RLDET, ®_val, AW_I2C_BYTE_ONE); + aw_haptic->lra = AW862X_RL_FORMULA(reg_val); + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_D2SCFG, &d2scfg, AW_I2C_BYTE_ONE); + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_ANACTRL, &anactrl, AW_I2C_BYTE_ONE); + aw862x_raminit(aw_haptic, false); +} + +static uint8_t aw862x_get_prctmode(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_PRLVL, ®_val, AW_I2C_BYTE_ONE); + reg_val >>= 7; + + return reg_val; +} + +static uint8_t aw862x_judge_rtp_going(struct aw_haptic *aw_haptic) +{ + uint8_t glb_state = 0; + uint8_t rtp_state = 0; + + glb_state = aw862x_get_glb_state(aw_haptic); + if (glb_state == AW_BIT_STATE_RTP_GO) { + rtp_state = 1; /*is going on */ + aw_info("rtp_routine_on"); + } + + return rtp_state; +} + +static uint64_t aw862x_get_theory_time(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t fre_val = 0; + uint64_t theory_time = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_PWMDBG, ®_val, AW_I2C_BYTE_ONE); + fre_val = (reg_val & 0x006f) >> 5; + if (fre_val == AW862X_CLK_12K) + theory_time = (aw_haptic->rtp_len / 12) * 1000; /*12K*/ + else if (fre_val == AW862X_CLK_24K) + theory_time = (aw_haptic->rtp_len / 24) * 1000; /*24K*/ + else + theory_time = (aw_haptic->rtp_len / 48) * 1000; /*48K*/ + aw_info("microsecond:%llu theory_time = %llu", aw_haptic->microsecond, theory_time); + + return theory_time; +} + +static uint8_t aw862x_get_osc_status(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_DBGSTAT, &state, AW_I2C_BYTE_ONE); + state &= AW862X_BIT_SYSINT_DONEI; + + return state; +} + +static int aw862x_check_qualify(struct aw_haptic *aw_haptic) +{ + return 0; +} + +static void aw862x_irq_clear(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_SYSINT, ®_val, AW_I2C_BYTE_ONE); + aw_info("SYSINT=0x%02X", reg_val); +} + +static void aw862x_set_base_addr(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t base_addr = aw_haptic->ram.base_addr; + + aw_info("enter"); + val[0] = (uint8_t)AW_SET_BASEADDR_H(base_addr); + val[1] = (uint8_t)AW_SET_BASEADDR_L(base_addr); + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_BASE_ADDRH, val, AW_I2C_BYTE_TWO); +} + +static void aw862x_set_fifo_addr(struct aw_haptic *aw_haptic) +{ + uint8_t val[4] = {0}; + uint32_t base_addr = aw_haptic->ram.base_addr; + + aw_info("enter"); + val[0] = (uint8_t)AW862X_SET_AEADDR_H(base_addr); + val[1] = (uint8_t)AW862X_SET_AEADDR_L(base_addr); + val[2] = (uint8_t)AW862X_SET_AFADDR_H(base_addr); + val[3] = (uint8_t)AW862X_SET_AFADDR_L(base_addr); + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_FIFO_AEH, val, AW_I2C_BYTE_FOUR); +} + +static void aw862x_get_fifo_addr(struct aw_haptic *aw_haptic) +{ + uint8_t val[4] = {0}; + + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_FIFO_AEH, val, AW_I2C_BYTE_FOUR); + aw_info("almost_empty_threshold = %u, almost_full_threshold = %u", + (uint16_t)((val[0] << 8) | val[1]), (uint16_t)((val[2] << 8) | val[3])); +} + +static void aw862x_set_ram_addr(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t base_addr = aw_haptic->ram.base_addr; + + aw_info("enter"); + val[0] = (uint8_t)AW_SET_RAMADDR_H(base_addr); + val[1] = (uint8_t)AW_SET_RAMADDR_L(base_addr); + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_RAMADDRH, val, AW_I2C_BYTE_TWO); +} + +static void aw862x_set_ram_data(struct aw_haptic *aw_haptic, uint8_t *data, int len) +{ + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_RAMDATA, data, len); +} + +static void aw862x_get_first_wave_addr(struct aw_haptic *aw_haptic, uint32_t *first_wave_addr) +{ + uint8_t val[3] = {0}; + + aw_info("enter"); + haptic_nv_i2c_reads(aw_haptic, AW862X_REG_RAMDATA, val, AW_I2C_BYTE_THREE); + *first_wave_addr = (val[1] << 8 | val[2]); +} + +static void aw862x_haptic_select_pin(struct aw_haptic *aw_haptic, uint8_t pin) +{ + if (pin == AW_TRIG1) { + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_DBGCTRL, + AW862X_BIT_DBGCTRL_INTN_TRG_SEL_MASK, + AW862X_BIT_DBGCTRL_TRG_SEL_ENABLE); + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_TRG_CFG2, + AW862X_BIT_TRGCFG2_TRG1_ENABLE_MASK, + AW862X_BIT_TRGCFG2_TRG1_ENABLE); + aw_info("select TRIG1 pin"); + } else if (pin == AW_IRQ) { + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_DBGCTRL, + AW862X_BIT_DBGCTRL_INTN_TRG_SEL_MASK, + AW862X_BIT_DBGCTRL_INTN_SEL_ENABLE); + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_TRG_CFG2, + AW862X_BIT_TRGCFG2_TRG1_ENABLE_MASK, + AW862X_BIT_TRGCFG2_TRG1_DISABLE); + aw_info("select INIT pin"); + } else + aw_err("There is no such option"); +} + +static void aw862x_haptic_trig1_param_init(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + aw_haptic->trig[0].enable = aw_haptic->info.trig_cfg[0]; + aw_haptic->trig[0].trig_edge = aw_haptic->info.trig_cfg[1]; + aw_haptic->trig[0].trig_polar = aw_haptic->info.trig_cfg[2]; + aw_haptic->trig[0].pos_sequence = aw_haptic->info.trig_cfg[3]; + aw_haptic->trig[0].neg_sequence = aw_haptic->info.trig_cfg[4]; +} + +static void aw862x_haptic_trig1_param_config(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + if (!(aw_haptic->trig[0].enable) || (aw_haptic->is_used_irq_pin && + aw_haptic->name == AW8624)) { + aw862x_haptic_trig1_param_init(aw_haptic); + if (aw_haptic->name == AW8624) + aw862x_haptic_select_pin(aw_haptic, AW_IRQ); + return; + } + aw862x_haptic_select_pin(aw_haptic, AW_TRIG1); + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_TRG_CFG1, AW862X_BIT_TRGCFG1_TRG1_EDGE_MASK, + aw_haptic->trig[0].trig_edge); + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_TRG_CFG1, AW862X_BIT_TRGCFG1_TRG1_POLAR_MASK, + aw_haptic->trig[0].trig_polar << 1); + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_TRG1_SEQP, + &aw_haptic->trig[0].pos_sequence, AW_I2C_BYTE_ONE); + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_TRG1_SEQN, + &aw_haptic->trig[0].neg_sequence, AW_I2C_BYTE_ONE); +} + +static void aw862x_haptic_set_trig1(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + aw862x_haptic_trig1_param_init(aw_haptic); + aw862x_haptic_trig1_param_config(aw_haptic); +} + +static void aw862x_trig_init(struct aw_haptic *aw_haptic) +{ + if (aw_haptic->is_used_irq_pin && aw_haptic->name == AW8624) { + aw862x_haptic_select_pin(aw_haptic, AW_IRQ); + return; + } + aw_info("enter"); + aw862x_haptic_set_trig1(aw_haptic); +} + +static void aw862x_parse_dts(struct aw_haptic *aw_haptic, struct device_node *np) +{ + uint32_t f0_trace_parameter[4]; + uint32_t brake_cont_config[24]; + uint32_t bemf_config[4]; + uint32_t sw_brake[2]; + uint32_t duration_time[3]; + uint32_t trig_config_temp[5]; + uint32_t val = 0; + + val = of_property_read_u32(np, "aw862x_vib_lk_f0_cali", &aw_haptic->info.lk_f0_cali); + if (val != 0) + aw_info("lk_f0_cali not found"); + val = of_property_read_u32(np, "aw862x_vib_mode", &aw_haptic->info.mode); + if (val != 0) + aw_info("vib mode not found"); + val = of_property_read_u32(np, "aw862x_vib_f0_pre", &aw_haptic->info.f0_pre); + if (val != 0) + aw_info("f0_pre not found"); + val = of_property_read_u32(np, "aw862x_vib_f0_cali_percen", + &aw_haptic->info.f0_cali_percent); + if (val != 0) + aw_info("f0_cali_percent not found"); + val = of_property_read_u32(np, "aw862x_vib_cont_drv_lev", &aw_haptic->info.cont_drv1_lvl); + if (val != 0) + aw_info("aw862x_vib_cont_drv_lev not found"); + val = of_property_read_u32(np, "aw862x_vib_cont_drv_lvl_ov", + &aw_haptic->info.cont_drv2_lvl); + if (val != 0) + aw_info("aw862x_vib_cont_drv_lvl_ov not found"); + val = of_property_read_u32(np, "aw862x_vib_cont_td", &aw_haptic->info.cont_td); + if (val != 0) + aw_info("aw862x_vib_cont_td not found"); + val = of_property_read_u32(np, "aw862x_vib_cont_zc_thr", &aw_haptic->info.cont_zc_thr); + if (val != 0) + aw_info("aw862x_vib_cont_zc_thr not found"); + val = of_property_read_u32(np, "aw862x_vib_cont_num_brk", &aw_haptic->info.cont_num_brk); + if (val != 0) + aw_info("aw862x_vib_cont_num_brk not found"); + val = of_property_read_u32(np, "aw862x_vib_f0_coeff", &aw_haptic->info.f0_coeff); + if (val != 0) + aw_info("aw862x_vib_f0_coeff not found"); + val = of_property_read_u32_array(np, "aw862x_vib_brake_cont_config", brake_cont_config, + ARRAY_SIZE(brake_cont_config)); + if (val != 0) + aw_info("aw862x_vib_brake_cont_config not found"); + else + memcpy(aw_haptic->info.cont_brake, brake_cont_config, sizeof(brake_cont_config)); + val = of_property_read_u32_array(np, "aw862x_vib_f0_trace_parameter", f0_trace_parameter, + ARRAY_SIZE(f0_trace_parameter)); + if (val != 0) + aw_info("aw862x_vib_f0_trace_parameter not found"); + else + memcpy(aw_haptic->info.f0_trace_parameter, f0_trace_parameter, + sizeof(f0_trace_parameter)); + val = of_property_read_u32_array(np, "aw862x_vib_bemf_config", bemf_config, + ARRAY_SIZE(bemf_config)); + if (val != 0) + aw_info("aw862x_vib_bemf_config not found"); + else + memcpy(aw_haptic->info.bemf_config, bemf_config, sizeof(bemf_config)); + val = of_property_read_u32_array(np, "aw862x_vib_sw_brake", sw_brake, ARRAY_SIZE(sw_brake)); + if (val != 0) + aw_info("aw862x_vib_sw_brake not found"); + else + memcpy(aw_haptic->info.sw_brake, sw_brake, sizeof(sw_brake)); + val = of_property_read_u32_array(np, "aw862x_vib_trig_config", trig_config_temp, + ARRAY_SIZE(trig_config_temp)); + if (val != 0) + aw_info("vib_trig_config not found"); + else + memcpy(aw_haptic->info.trig_cfg, trig_config_temp, sizeof(trig_config_temp)); + val = of_property_read_u32(np, "aw862x_vib_tset", &aw_haptic->info.cont_tset); + if (val != 0) + aw_info("aw862x_vib_tset not found"); + val = of_property_read_u32_array(np, "aw862x_vib_duration_time", duration_time, + ARRAY_SIZE(duration_time)); + if (val != 0) + aw_info("aw862x_vib_duration_time not found"); + else + memcpy(aw_haptic->info.duration_time, duration_time, sizeof(duration_time)); +} + +static ssize_t cont_td_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + len += snprintf(buf+len, PAGE_SIZE-len, "cont_delay_time = 0x%04X\n", + aw_haptic->info.cont_td); + + return len; +} + +static ssize_t cont_td_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + int err, val; + uint8_t reg_array[2] = {0}; + + err = kstrtoint(buf, 16, &val); + if (err != 0) { + aw_err("format not match!"); + return count; + } + mutex_lock(&aw_haptic->lock); + aw_haptic->info.cont_td = val; + reg_array[0] = (uint8_t)(val >> 8); + reg_array[1] = (uint8_t)(val >> 0); + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_TD_H, reg_array, AW_I2C_BYTE_TWO); + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t cont_drv_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + len += snprintf(buf+len, PAGE_SIZE-len, "cont drv level = 0x%02X\n", + aw_haptic->info.cont_drv1_lvl); + len += snprintf(buf+len, PAGE_SIZE-len, "cont drv level overdrive= 0x%02X\n", + aw_haptic->info.cont_drv2_lvl); + + return len; +} + +static ssize_t cont_drv_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + uint8_t reg_array[2] = {0}; + uint32_t databuf[2] = {0}; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + mutex_lock(&aw_haptic->lock); + if (sscanf(buf, "%X %X", &databuf[0], &databuf[1]) == 2) { + aw_haptic->info.cont_drv1_lvl = databuf[0]; + aw_haptic->info.cont_drv2_lvl = databuf[1]; + reg_array[0] = aw_haptic->info.cont_drv1_lvl; + reg_array[1] = aw_haptic->info.cont_drv2_lvl; + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_DRV_LVL, reg_array, AW_I2C_BYTE_TWO); + } + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t cont_num_brk_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + len += snprintf(buf+len, PAGE_SIZE-len, "cont_brk_num = 0x%02x\n", + aw_haptic->info.cont_num_brk); + + return len; +} + +static ssize_t cont_num_brk_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int err, val; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + err = kstrtoint(buf, 16, &val); + if (err != 0) { + aw_err("format not match!"); + return count; + } + mutex_lock(&aw_haptic->lock); + aw_haptic->info.cont_num_brk = val; + if (aw_haptic->info.cont_num_brk > 7) + aw_haptic->info.cont_num_brk = 7; + + haptic_nv_i2c_write_bits(aw_haptic, AW862X_REG_BEMF_NUM, AW862X_BIT_BEMF_NUM_BRK_MASK, + aw_haptic->info.cont_num_brk); + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t cont_zc_thr_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, "cont_zero_cross_thr = 0x%04X\n", + aw_haptic->info.cont_zc_thr); + + return len; +} + +static ssize_t cont_zc_thr_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t reg_array[2] = {0}; + int err, val; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + err = kstrtoint(buf, 0, &val); + if (err != 0) { + aw_err("format not match!"); + return count; + } + mutex_lock(&aw_haptic->lock); + aw_haptic->info.cont_num_brk = val; + aw_info("val=%d", val); + if (val > 0) { + reg_array[0] = (uint8_t)(val >> 8); + reg_array[1] = (uint8_t)(val >> 0); + haptic_nv_i2c_writes(aw_haptic, AW862X_REG_ZC_THRSH_H, reg_array, AW_I2C_BYTE_TWO); + } + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t trig_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "trig: trig_enable=%u, trig_edge=%u\n", + aw_haptic->trig[0].enable, aw_haptic->trig[0].trig_edge); + len += snprintf(buf + len, PAGE_SIZE - len, + "trig_polar=%u, pos_sequence=%u, neg_sequence=%u\n", + aw_haptic->trig[0].trig_polar, aw_haptic->trig[0].pos_sequence, + aw_haptic->trig[0].neg_sequence); + + return len; + +} + +static ssize_t trig_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + uint32_t databuf[5] = { 0 }; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + if (sscanf(buf, "%u %u %u %u %u", &databuf[0], &databuf[1], &databuf[2], &databuf[3], + &databuf[4]) == 5) { + if (databuf[0] > 1) + databuf[0] = 1; + if (databuf[0] < 0) + databuf[0] = 0; + if (databuf[1] > 1) + databuf[0] = 1; + if (databuf[1] < 0) + databuf[0] = 0; + if (databuf[2] > 1) + databuf[0] = 1; + if (databuf[2] < 0) + databuf[0] = 0; + if (databuf[3] > aw_haptic->ram.ram_num || databuf[4] > aw_haptic->ram.ram_num) { + aw_err("input seq value out of range!"); + return count; + } + aw_haptic->trig[0].enable = databuf[0]; + aw_haptic->trig[0].trig_edge = databuf[1]; + aw_haptic->trig[0].trig_polar = databuf[2]; + aw_haptic->trig[0].pos_sequence = databuf[3]; + aw_haptic->trig[0].neg_sequence = databuf[4]; + mutex_lock(&aw_haptic->lock); + aw862x_haptic_trig1_param_config(aw_haptic); + mutex_unlock(&aw_haptic->lock); + } else + aw_err("please input five parameters"); + + return count; +} + +static DEVICE_ATTR_RW(cont_td); +static DEVICE_ATTR_RW(cont_drv); +static DEVICE_ATTR_RW(cont_num_brk); +static DEVICE_ATTR_RW(cont_zc_thr); +static DEVICE_ATTR_RW(trig); + +static struct attribute *aw862x_vibrator_attributes[] = { + &dev_attr_cont_td.attr, + &dev_attr_cont_drv.attr, + &dev_attr_cont_num_brk.attr, + &dev_attr_cont_zc_thr.attr, + &dev_attr_trig.attr, + NULL +}; + +static struct attribute_group aw862x_vibrator_attribute_group = { + .attrs = aw862x_vibrator_attributes +}; + +static void aw862x_creat_node(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + ret = sysfs_create_group(&aw_haptic->vib_dev.dev->kobj, &aw862x_vibrator_attribute_group); + if (ret < 0) + aw_err("error create aw862x sysfs attr files"); +} + +struct aw_haptic_func aw862x_func_list = { + .ram_init = aw862x_raminit, + .parse_dts = aw862x_parse_dts, + .trig_init = aw862x_trig_init, + .play_mode = aw862x_play_mode, + .play_stop = aw862x_play_stop, + .irq_clear = aw862x_irq_clear, + .creat_node = aw862x_creat_node, + .cont_config = aw862x_cont_config, + .offset_cali = aw862x_offset_cali, + .haptic_start = aw862x_haptic_start, + .read_cont_f0 = aw862x_read_cont_f0, + .check_qualify = aw862x_check_qualify, + .judge_rtp_going = aw862x_judge_rtp_going, + .protect_config = aw862x_protect_config, + .misc_para_init = aw862x_misc_para_init, + .interrupt_setup = aw862x_interrupt_setup, + .rtp_get_fifo_afs = aw862x_rtp_get_fifo_afs, + .rtp_get_fifo_aes = aw862x_rtp_get_fifo_aes, + .vbat_mode_config = aw862x_vbat_mode_config, + .calculate_cali_data = aw862x_calculate_cali_data, + .set_gain = aw862x_set_gain, + .get_gain = aw862x_get_gain, + .set_wav_seq = aw862x_set_wav_seq, + .get_wav_seq = aw862x_get_wav_seq, + .set_wav_loop = aw862x_set_wav_loop, + .get_wav_loop = aw862x_get_wav_loop, + .set_ram_data = aw862x_set_ram_data, + .get_ram_data = aw862x_get_ram_data, + .set_fifo_addr = aw862x_set_fifo_addr, + .get_fifo_addr = aw862x_get_fifo_addr, + .set_rtp_aei = aw862x_set_rtp_aei, + .set_rtp_data = aw862x_set_rtp_data, + .set_ram_addr = aw862x_set_ram_addr, + .set_trim_lra = aw862x_set_trim_lra, + .set_base_addr = aw862x_set_base_addr, + .set_repeat_seq = aw862x_set_repeat_seq, +#ifdef AW862X_MUL_GET_F0 + .get_f0 = aw862x_multiple_get_f0, +#else + .get_f0 = aw862x_get_f0, +#endif + .get_reg = aw862x_get_reg, + .get_vbat = aw862x_get_vbat, + .get_prctmode = aw862x_get_prctmode, + .get_irq_state = aw862x_get_irq_state, + .get_glb_state = aw862x_get_glb_state, + .get_osc_status = aw862x_get_osc_status, + .get_theory_time = aw862x_get_theory_time, + .get_lra_resistance = aw862x_get_lra_resistance, + .get_first_wave_addr = aw862x_get_first_wave_addr, +}; diff --git a/drivers/misc/awinic/aw862x_haptic_nv_v1/aw862xx.c b/drivers/misc/awinic/aw862x_haptic_nv_v1/aw862xx.c new file mode 100644 index 000000000000..6d072b63cb52 --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv_v1/aw862xx.c @@ -0,0 +1,1444 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * aw862xx.c + * + * Copyright (c) 2021 AWINIC Technology CO., LTD + * + * Author: + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "haptic_nv.h" +#include "haptic_nv_reg.h" + +static void aw862xx_interrupt_setup(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + aw_info("enter"); + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_SYSINT, ®_val, AW_I2C_BYTE_ONE); + aw_info("reg SYSINT=0x%02X", reg_val); + /* edge int mode */ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL7, + (AW862XX_BIT_SYSCTRL7_INT_MODE_MASK & + AW862XX_BIT_SYSCTRL7_INT_EDGE_MODE_MASK), + (AW862XX_BIT_SYSCTRL7_INT_MODE_EDGE | + AW862XX_BIT_SYSCTRL7_INT_EDGE_MODE_POS)); + /* int enable */ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSINTM, + (AW862XX_BIT_SYSINTM_UVLM_MASK & AW862XX_BIT_SYSINTM_FF_AEM_MASK & + AW862XX_BIT_SYSINTM_FF_AFM_MASK & AW862XX_BIT_SYSINTM_OCDM_MASK & + AW862XX_BIT_SYSINTM_OTM_MASK & AW862XX_BIT_SYSINTM_DONEM_MASK), + (AW862XX_BIT_SYSINTM_UVLM_ON | AW862XX_BIT_SYSINTM_FF_AEM_OFF | + AW862XX_BIT_SYSINTM_FF_AFM_OFF | AW862XX_BIT_SYSINTM_OCDM_ON | + AW862XX_BIT_SYSINTM_OTM_ON | AW862XX_BIT_SYSINTM_DONEM_OFF)); +} + +static void aw862xx_set_rtp_data(struct aw_haptic *aw_haptic, uint8_t *data, uint32_t len) +{ + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_RTPDATA, data, len); +} + +static uint8_t aw862xx_get_glb_state(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_GLBRD5, &state, AW_I2C_BYTE_ONE); + + return state; +} + +static int aw862xx_get_irq_state(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int ret = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_SYSINT, ®_val, AW_I2C_BYTE_ONE); + aw_dbg("reg SYSINT=0x%02X", reg_val); + if (reg_val & AW862XX_BIT_SYSINT_UVLI) + aw_err("chip uvlo int error"); + if (reg_val & AW862XX_BIT_SYSINT_OCDI) + aw_err("chip over current int error"); + if (reg_val & AW862XX_BIT_SYSINT_OTI) + aw_err("chip over temperature int error"); + if (reg_val & AW862XX_BIT_SYSINT_DONEI) + aw_info("chip playback done"); + if (reg_val & AW862XX_BIT_SYSINT_FF_AFI) + aw_info("rtp mode fifo almost full!"); + if (reg_val & AW862XX_BIT_SYSINT_FF_AEI) { + aw_info("rtp fifo almost empty"); + ret = AW_IRQ_ALMOST_EMPTY; + } + + return ret; +} + +static uint8_t aw862xx_rtp_get_fifo_afs(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_SYSST, ®_val, AW_I2C_BYTE_ONE); + reg_val &= AW862XX_BIT_SYSST_FF_AFS; + reg_val = reg_val >> 3; + + return reg_val; +} + +static uint8_t aw862xx_rtp_get_fifo_aes(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_SYSST, ®_val, AW_I2C_BYTE_ONE); + reg_val &= AW862XX_BIT_SYSST_FF_AES; + reg_val = reg_val >> 4; + + return reg_val; +} + +static void aw862xx_set_rtp_aei(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSINTM, + AW862XX_BIT_SYSINTM_FF_AEM_MASK, + AW862XX_BIT_SYSINTM_FF_AEM_ON); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSINTM, + AW862XX_BIT_SYSINTM_FF_AEM_MASK, + AW862XX_BIT_SYSINTM_FF_AEM_OFF); + } +} + +static void aw862xx_sram_size(struct aw_haptic *aw_haptic, uint8_t size_flag) +{ + switch (size_flag) { + case AW862XX_HAPTIC_SRAM_1K: + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_RTPCFG1, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_2K_MASK, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_2K_DIS); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_RTPCFG1, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_1K_MASK, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_1K_EN); + break; + case AW862XX_HAPTIC_SRAM_2K: + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_RTPCFG1, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_2K_MASK, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_2K_EN); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_RTPCFG1, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_1K_MASK, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_1K_DIS); + break; + case AW862XX_HAPTIC_SRAM_3K: + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_RTPCFG1, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_1K_MASK, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_1K_EN); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_RTPCFG1, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_2K_MASK, + AW862XX_BIT_RTPCFG1_SRAM_SIZE_2K_EN); + break; + default: + aw_err("size_flag is error"); + break; + } +} + +static void aw862xx_auto_brk_config(struct aw_haptic *aw_haptic, uint8_t flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PLAYCFG3, + AW862XX_BIT_PLAYCFG3_BRK_EN_MASK, + AW862XX_BIT_PLAYCFG3_BRK_ENABLE); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PLAYCFG3, + AW862XX_BIT_PLAYCFG3_BRK_EN_MASK, + AW862XX_BIT_PLAYCFG3_BRK_DISABLE); + } +} + +static void aw862xx_config(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + aw_info("enter"); + aw862xx_sram_size(aw_haptic, AW862XX_HAPTIC_SRAM_3K); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_TRGCFG8, + AW862XX_BIT_TRGCFG8_TRG_TRIG1_MODE_MASK, + AW862XX_BIT_TRGCFG8_TRIG1); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_ANACFG8, + AW862XX_BIT_ANACFG8_TRTF_CTRL_HDRV_MASK, + AW862XX_BIT_ANACFG8_TRTF_CTRL_HDRV); + if (aw_haptic->info.cont_brk_time) { + reg_val = (uint8_t)aw_haptic->info.cont_brk_time; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG10, ®_val, AW_I2C_BYTE_ONE); + } else { + aw_err("dts_info.cont_brk_time=0"); + } + if (aw_haptic->info.cont_brk_gain) { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_CONTCFG5, + AW862XX_BIT_CONTCFG5_BRK_GAIN_MASK, + aw_haptic->info.cont_brk_gain); + } else { + aw_err("dts_info.cont_brk_gain=0"); + } + +} + +static void aw862xx_play_stop(struct aw_haptic *aw_haptic) +{ + bool force_flag = true; + uint8_t val = 0; + int cnt = 40; + + aw_info("enter"); + aw_haptic->play_mode = AW_STANDBY_MODE; + val = AW862XX_BIT_PLAYCFG4_STOP_ON; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_PLAYCFG4, &val, AW_I2C_BYTE_ONE); + while (cnt) { + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_GLBRD5, &val, AW_I2C_BYTE_ONE); + if ((val & AW_BIT_GLBRD_STATE_MASK) == AW_BIT_STATE_STANDBY) { + force_flag = false; + aw_info("entered standby! glb_state=0x%02X", val); + break; + } + cnt--; + usleep_range(AW_STOP_DELAY_MIN, AW_STOP_DELAY_MAX); + } + if (force_flag) { + aw_err("force to enter standby mode!"); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL2, + AW862XX_BIT_SYSCTRL2_STANDBY_MASK, + AW862XX_BIT_SYSCTRL2_STANDBY_ON); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL2, + AW862XX_BIT_SYSCTRL2_STANDBY_MASK, + AW862XX_BIT_SYSCTRL2_STANDBY_OFF); + } +} + +static void aw862xx_set_pwm(struct aw_haptic *aw_haptic, uint8_t mode) +{ + switch (mode) { + case AW_PWM_48K: + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL2, + AW862XX_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW862XX_BIT_SYSCTRL2_RATE_48K); + break; + case AW_PWM_24K: + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL2, + AW862XX_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW862XX_BIT_SYSCTRL2_RATE_24K); + break; + case AW_PWM_12K: + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL2, + AW862XX_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW862XX_BIT_SYSCTRL2_RATE_12K); + break; + default: + break; + } +} + +static void aw862xx_play_mode(struct aw_haptic *aw_haptic, uint8_t play_mode) +{ + switch (play_mode) { + case AW_STANDBY_MODE: + aw_info("enter standby mode"); + aw_haptic->play_mode = AW_STANDBY_MODE; + aw862xx_play_stop(aw_haptic); + break; + case AW_RAM_MODE: + aw_info("enter ram mode"); + aw_haptic->play_mode = AW_RAM_MODE; + aw862xx_set_pwm(aw_haptic, AW_PWM_12K); + aw862xx_auto_brk_config(aw_haptic, false); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PLAYCFG3, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + case AW_RAM_LOOP_MODE: + aw_info("enter ram loop mode"); + aw_haptic->play_mode = AW_RAM_LOOP_MODE; + aw862xx_set_pwm(aw_haptic, AW_PWM_12K); + aw862xx_auto_brk_config(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PLAYCFG3, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + case AW_CONT_MODE: + aw_info("enter cont mode"); + aw_haptic->play_mode = AW_CONT_MODE; + aw862xx_auto_brk_config(aw_haptic, aw_haptic->info.is_enabled_auto_brk); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PLAYCFG3, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_CONT); + break; + case AW_RTP_MODE: + aw_info("enter rtp mode"); + aw_haptic->play_mode = AW_RTP_MODE; + aw862xx_set_pwm(aw_haptic, AW_PWM_24K); + aw862xx_auto_brk_config(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PLAYCFG3, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_RTP); + break; + case AW_TRIG_MODE: + aw_info("enter trig mode"); + aw_haptic->play_mode = AW_TRIG_MODE; + aw862xx_set_pwm(aw_haptic, AW_PWM_12K); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PLAYCFG3, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW862XX_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + default: + aw_err("play mode %u error", play_mode); + break; + } +} + +static void aw862xx_irq_clear(struct aw_haptic *aw_haptic) +{ + uint8_t val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_SYSINT, &val, AW_I2C_BYTE_ONE); + aw_info("SYSINT=0x%02X", val); +} + +static void aw862xx_play_go(struct aw_haptic *aw_haptic, bool flag) +{ + uint8_t val = 0; + + aw_info("enter"); + if (flag == true) { + val = AW862XX_BIT_PLAYCFG4_GO_ON; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_PLAYCFG4, &val, AW_I2C_BYTE_ONE); + usleep_range(AW_PLAY_DELAY_MIN, AW_PLAY_DELAY_MAX); + } else { + val = AW862XX_BIT_PLAYCFG4_STOP_ON; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_PLAYCFG4, &val, AW_I2C_BYTE_ONE); + } + val = aw862xx_get_glb_state(aw_haptic); + aw_info("reg:0x%02X=0x%02X", AW862XX_REG_GLBRD5, val); +} + +static void aw862xx_haptic_start(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + aw862xx_play_go(aw_haptic, true); +} + +static void aw862xx_raminit(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL1, + AW862XX_BIT_SYSCTRL1_RAMINIT_MASK, + AW862XX_BIT_SYSCTRL1_RAMINIT_ON); + usleep_range(1000, 1050); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL1, + AW862XX_BIT_SYSCTRL1_RAMINIT_MASK, + AW862XX_BIT_SYSCTRL1_RAMINIT_OFF); + } +} + +static void aw862xx_get_vbat(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t vbat_code = 0; + + aw_info("enter"); + aw862xx_play_stop(aw_haptic); + aw862xx_raminit(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_DETCFG2, AW862XX_BIT_DETCFG2_VBAT_GO_MASK, + AW862XX_BIT_DETCFG2_VABT_GO_ON); + usleep_range(AW_VBAT_DELAY_MIN, AW_VBAT_DELAY_MAX); + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_DET_VBAT, ®_val, AW_I2C_BYTE_ONE); + vbat_code = (vbat_code | reg_val) << 2; + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_DET_LO, ®_val, AW_I2C_BYTE_ONE); + vbat_code = vbat_code | ((reg_val & AW862XX_BIT_DET_LO_VBAT) >> 4); + aw_haptic->vbat = AW862XX_VBAT_FORMULA(vbat_code); + if (aw_haptic->vbat > AW_VBAT_MAX) { + aw_haptic->vbat = AW_VBAT_MAX; + aw_info("vbat max limit = %dmV", aw_haptic->vbat); + } + if (aw_haptic->vbat < AW_VBAT_MIN) { + aw_haptic->vbat = AW_VBAT_MIN; + aw_info("vbat min limit = %dmV", aw_haptic->vbat); + } + aw_info("vbat=%dmV, vbat_code=0x%02X", aw_haptic->vbat, vbat_code); + aw862xx_raminit(aw_haptic, false); +} + +static void aw862xx_set_gain(struct aw_haptic *aw_haptic, uint8_t gain) +{ + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_PLAYCFG2, &gain, AW_I2C_BYTE_ONE); +} + +static void aw862xx_set_trim_lra(struct aw_haptic *aw_haptic, uint8_t val) +{ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_TRIMCFG3, + AW862XX_BIT_TRIMCFG3_TRIM_LRA_MASK, val); +} + +static void aw862xx_cont_config(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + aw_info("enter"); + /* work mode */ + aw862xx_play_mode(aw_haptic, AW_CONT_MODE); + /* cont config */ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_CONTCFG6, + (AW862XX_BIT_CONTCFG6_TRACK_EN_MASK & + AW862XX_BIT_CONTCFG6_DRV1_LVL_MASK), + ((aw_haptic->info.is_enabled_track_en << 7) | + (uint8_t)aw_haptic->info.cont_drv1_lvl)); + reg_val = (uint8_t)aw_haptic->info.cont_drv2_lvl; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG7, ®_val, AW_I2C_BYTE_ONE); + /* DRV2_TIME */ + reg_val = 0xFF; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG9, ®_val, AW_I2C_BYTE_ONE); + /* cont play go */ + aw862xx_play_go(aw_haptic, true); +} + +static ssize_t aw862xx_get_reg(struct aw_haptic *aw_haptic, ssize_t len, char *buf) +{ + len = haptic_nv_read_reg_array(aw_haptic, buf, len, AW862XX_REG_ID, + AW862XX_REG_RTPDATA - 1); + if (!len) + return len; + len = haptic_nv_read_reg_array(aw_haptic, buf, len, AW862XX_REG_RTPDATA + 1, + AW862XX_REG_RAMDATA - 1); + if (!len) + return len; + len = haptic_nv_read_reg_array(aw_haptic, buf, len, AW862XX_REG_RAMDATA + 1, + AW862XX_REG_ANACFG8); + + return len; +} + +static void aw862xx_protect_config(struct aw_haptic *aw_haptic, uint8_t prtime, uint8_t prlvl) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PWMCFG1, AW862XX_BIT_PWMCFG1_PRC_EN_MASK, + AW862XX_BIT_PWMCFG1_PRC_DISABLE); + if (prlvl != 0) { + /* Enable protection mode */ + aw_info("enable protection mode"); + reg_val = AW862XX_BIT_PWMCFG3_PR_ENABLE | + (prlvl & (~AW862XX_BIT_PWMCFG3_PRLVL_MASK)); + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_PWMCFG3, ®_val, AW_I2C_BYTE_ONE); + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_PWMCFG4, &prtime, AW_I2C_BYTE_ONE); + } else { + /* Disable */ + aw_info("disable protection mode"); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PWMCFG3, + AW862XX_BIT_PWMCFG3_PR_EN_MASK, + AW862XX_BIT_PWMCFG3_PR_DISABLE); + } +} + +static void aw862xx_misc_para_init(struct aw_haptic *aw_haptic) +{ + uint8_t val = 0; + uint8_t array[8] = {0}; + + aw_info("enter"); + /* Get seq and gain */ + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_WAVCFG1, &val, AW_I2C_BYTE_ONE); + aw_haptic->index = val & AW862XX_BIT_WAVCFG_SEQ; + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_PLAYCFG2, &val, AW_I2C_BYTE_ONE); + aw_haptic->gain = val; + aw_info("gain=0x%02X, index=0x%02X", aw_haptic->gain, aw_haptic->index); + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_WAVCFG1, array, AW_SEQUENCER_SIZE); + memcpy(aw_haptic->seq, array, AW_SEQUENCER_SIZE); + /* GAIN_BYPASS config */ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL7, + AW862XX_BIT_SYSCTRL7_GAIN_BYPASS_MASK, + aw_haptic->info.gain_bypass << 6); + + if (!aw_haptic->info.d2s_gain) { + aw_err("dts_info.d2s_gain = 0!"); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL7, + AW862XX_BIT_SYSCTRL7_D2S_GAIN_MASK, + aw_haptic->info.d2s_gain); + } + + aw_haptic->info.cont_drv2_lvl = AW862XX_DRV2_LVL_FORMULA(aw_haptic->info.f0_pre, + aw_haptic->info.lra_vrms); + aw_info("lra_vrms=%u, cont_drv2_lvl=0x%02X", aw_haptic->info.lra_vrms, + aw_haptic->info.cont_drv2_lvl); + if (aw_haptic->info.cont_drv2_lvl > AW862XX_DRV2_LVL_MAX) { + aw_err("cont_drv2_lvl[0x%02X] is error, restore max vale[0x%02X]", + aw_haptic->info.cont_drv2_lvl, AW862XX_DRV2_LVL_MAX); + aw_haptic->info.cont_drv2_lvl = AW862XX_DRV2_LVL_MAX; + } + aw862xx_config(aw_haptic); + aw862xx_set_pwm(aw_haptic, AW_PWM_12K); + aw862xx_protect_config(aw_haptic, AW862XX_PWMCFG4_PRTIME_DEFAULT_VALUE, + AW862XX_BIT_PWMCFG3_PRLVL_DEFAULT_VALUE); +} + +static int aw862xx_select_d2s_gain(uint8_t reg) +{ + int d2s_gain = 0; + + switch (reg) { + case AW862XX_BIT_SYSCTRL7_D2S_GAIN_1: + d2s_gain = 1; + break; + case AW862XX_BIT_SYSCTRL7_D2S_GAIN_2: + d2s_gain = 2; + break; + case AW862XX_BIT_SYSCTRL7_D2S_GAIN_4: + d2s_gain = 4; + break; + case AW862XX_BIT_SYSCTRL7_D2S_GAIN_5: + d2s_gain = 5; + break; + case AW862XX_BIT_SYSCTRL7_D2S_GAIN_8: + d2s_gain = 8; + break; + case AW862XX_BIT_SYSCTRL7_D2S_GAIN_10: + d2s_gain = 10; + break; + case AW862XX_BIT_SYSCTRL7_D2S_GAIN_20: + d2s_gain = 20; + break; + case AW862XX_BIT_SYSCTRL7_D2S_GAIN_40: + d2s_gain = 40; + break; + default: + d2s_gain = -1; + break; + } + + return d2s_gain; +} + +static int aw862xx_offset_cali(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val[2] = {0}; + int os_code = 0; + int d2s_gain = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_SYSCTRL7, reg_val, AW_I2C_BYTE_ONE); + reg_val[0] = (~AW862XX_BIT_SYSCTRL7_D2S_GAIN_MASK) & reg_val[0]; + d2s_gain = aw862xx_select_d2s_gain(reg_val[0]); + if (d2s_gain < 0) { + aw_err("d2s_gain is error"); + return -ERANGE; + } + aw862xx_raminit(aw_haptic, true); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_DETCFG1, AW862XX_BIT_DETCFG1_RL_OS_MASK, + AW862XX_BIT_DETCFG1_OS); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_DETCFG2, AW862XX_BIT_DETCFG2_DIAG_GO_MASK, + AW862XX_BIT_DETCFG2_DIAG_GO_ON); + usleep_range(3000, 3500); + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_DET_OS, ®_val[0], AW_I2C_BYTE_ONE); + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_DET_LO, ®_val[1], AW_I2C_BYTE_ONE); + aw862xx_raminit(aw_haptic, false); + os_code = (reg_val[1] & (~AW862XX_BIT_DET_LO_OS_MASK)) >> 2; + os_code = (reg_val[0] << 2) | os_code; + os_code = AW862XX_OS_FORMULA(os_code, d2s_gain); + aw_info("os_code is %d mV", os_code); + if (os_code > 15 || os_code < -15) + return -ERANGE; + + return 0; +} + +static void aw862xx_vbat_mode_config(struct aw_haptic *aw_haptic, uint8_t flag) +{ + uint8_t val = 0; + + aw_info("enter"); + if (flag == AW_CONT_VBAT_HW_COMP_MODE) { + val = AW862XX_BIT_GLBCFG2_START_DLY_250US; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_GLBCFG2, &val, AW_I2C_BYTE_ONE); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL1, + AW862XX_BIT_SYSCTRL1_VBAT_MODE_MASK, + AW862XX_BIT_SYSCTRL1_VBAT_MODE_HW); + } else { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL1, + AW862XX_BIT_SYSCTRL1_VBAT_MODE_MASK, + AW862XX_BIT_SYSCTRL1_VBAT_MODE_SW); + } +} + +static void aw862xx_calculate_cali_data(struct aw_haptic *aw_haptic) +{ + char f0_cali_lra = 0; + int f0_cali_step = 0; + + f0_cali_step = 100000 * ((int)aw_haptic->f0 - (int)aw_haptic->info.f0_pre) / + ((int)aw_haptic->info.f0_pre * AW862XX_F0_CALI_ACCURACY); + aw_info("f0_cali_step=%d", f0_cali_step); + if (f0_cali_step >= 0) { /*f0_cali_step >= 0 */ + if (f0_cali_step % 10 >= 5) + f0_cali_step = 32 + (f0_cali_step / 10 + 1); + else + f0_cali_step = 32 + f0_cali_step / 10; + } else { /* f0_cali_step < 0 */ + if (f0_cali_step % 10 <= -5) + f0_cali_step = 32 + (f0_cali_step / 10 - 1); + else + f0_cali_step = 32 + f0_cali_step / 10; + } + if (f0_cali_step > 31) + f0_cali_lra = (char)f0_cali_step - 32; + else + f0_cali_lra = (char)f0_cali_step + 32; + /* update cali step */ + aw_haptic->f0_cali_data = (int)f0_cali_lra; + aw_info("f0_cali_data=0x%02X", aw_haptic->f0_cali_data); +} + +static void aw862xx_read_cont_f0(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t f0_reg = 0; + uint64_t f0_tmp = 0; + + aw_info("enter"); + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_CONTRD16, val, AW_I2C_BYTE_TWO); + f0_reg = (f0_reg | val[0]) << 8; + f0_reg |= (val[1] << 0); + if (!f0_reg) { + aw_err("didn't get cont f0 because f0_reg value is 0!"); + aw_haptic->cont_f0 = 0; + return; + } + f0_tmp = AW862XX_F0_FORMULA(f0_reg); + aw_haptic->cont_f0 = (uint32_t)f0_tmp; + aw_info("cont_f0=%u", aw_haptic->cont_f0); +} + +static int aw862xx_read_lra_f0(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t f0_reg = 0; + uint64_t f0_tmp = 0; + + aw_info("enter"); + /* F_LRA_F0 */ + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_CONTRD14, val, AW_I2C_BYTE_TWO); + f0_reg = (f0_reg | val[0]) << 8; + f0_reg |= (val[1] << 0); + if (!f0_reg) { + aw_haptic->f0 = 0; + aw_err("didn't get lra f0 because f0_reg value is 0!"); + return -ERANGE; + } + f0_tmp = AW862XX_F0_FORMULA(f0_reg); + aw_haptic->f0 = (uint32_t)f0_tmp; + aw_info("lra_f0=%u", aw_haptic->f0); + + return 0; +} + +static int aw862xx_get_f0(struct aw_haptic *aw_haptic) +{ + bool get_f0_flag = false; + uint8_t brk_en_temp = 0; + uint8_t val[3] = {0}; + int drv_width = 0; + int cnt = 200; + int ret = 0; + + aw_info("enter"); + aw_haptic->f0 = aw_haptic->info.f0_pre; + /* enter standby mode */ + aw862xx_play_stop(aw_haptic); + /* f0 calibrate work mode */ + aw862xx_play_mode(aw_haptic, AW_CONT_MODE); + /* enable f0 detect */ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_CONTCFG1, + AW862XX_BIT_CONTCFG1_EN_F0_DET_MASK, + AW862XX_BIT_CONTCFG1_F0_DET_ENABLE); + /* cont config */ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_CONTCFG6, + AW862XX_BIT_CONTCFG6_TRACK_EN_MASK, + (aw_haptic->info.is_enabled_track_en << 7)); + /* enable auto brake */ + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_PLAYCFG3, &val[0], AW_I2C_BYTE_ONE); + brk_en_temp = AW862XX_BIT_PLAYCFG3_BRK & val[0]; + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PLAYCFG3, AW862XX_BIT_PLAYCFG3_BRK_EN_MASK, + AW862XX_BIT_PLAYCFG3_BRK_ENABLE); + /* f0 driver level */ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_CONTCFG6, + AW862XX_BIT_CONTCFG6_DRV1_LVL_MASK, aw_haptic->info.cont_drv1_lvl); + val[0] = (uint8_t)aw_haptic->info.cont_drv2_lvl; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG7, val, AW_I2C_BYTE_ONE); + val[0] = (uint8_t)aw_haptic->info.cont_drv1_time; + val[1] = (uint8_t)aw_haptic->info.cont_drv2_time; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG8, val, AW_I2C_BYTE_TWO); + /* TRACK_MARGIN */ + if (!aw_haptic->info.cont_track_margin) { + aw_err("dts_info.cont_track_margin = 0!"); + } else { + val[0] = (uint8_t)aw_haptic->info.cont_track_margin; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG11, &val[0], AW_I2C_BYTE_ONE); + } + /* DRV_WIDTH */ + if (!aw_haptic->info.f0_pre) + return -ERANGE; + drv_width = AW862XX_DRV_WIDTH_FORMULA(aw_haptic->info.f0_pre, + aw_haptic->info.cont_track_margin, + aw_haptic->info.cont_brk_gain); + if (drv_width < AW_DRV_WIDTH_MIN) + drv_width = AW_DRV_WIDTH_MIN; + if (drv_width > AW_DRV_WIDTH_MAX) + drv_width = AW_DRV_WIDTH_MAX; + val[0] = (uint8_t)drv_width; + aw_info("cont_drv_width=0x%02X", val[0]); + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG3, &val[0], AW_I2C_BYTE_ONE); + /* cont play go */ + aw862xx_play_go(aw_haptic, true); + usleep_range(20000, 20500); + while (cnt) { + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_GLBRD5, &val[0], AW_I2C_BYTE_ONE); + if ((val[0] & AW_BIT_GLBRD_STATE_MASK) == AW_BIT_STATE_STANDBY) { + get_f0_flag = true; + aw_info("entered standby! glb_state=0x%02X", val[0]); + break; + } + cnt--; + aw_dbg("waitting for standby,glb_state=0x%02X", val[0]); + usleep_range(AW_F0_DELAY_MIN, AW_F0_DELAY_MAX); + } + if (get_f0_flag) { + ret = aw862xx_read_lra_f0(aw_haptic); + if (ret < 0) + aw_err("read lra f0 is failed"); + aw862xx_read_cont_f0(aw_haptic); + } else { + ret = -ERANGE; + aw_err("enter standby mode failed, stop reading f0!"); + } + /* disable f0 detect */ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_CONTCFG1, + AW862XX_BIT_CONTCFG1_EN_F0_DET_MASK, + AW862XX_BIT_CONTCFG1_F0_DET_DISABLE); + /* recover auto break config */ + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_PLAYCFG3, + AW862XX_BIT_PLAYCFG3_BRK_EN_MASK, brk_en_temp); + + return ret; +} + +static void aw862xx_set_base_addr(struct aw_haptic *aw_haptic) +{ + uint32_t base_addr = 0; + uint8_t val = 0; + + aw_info("enter"); + base_addr = aw_haptic->ram.base_addr; + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_RTPCFG1, AW862XX_BIT_RTPCFG1_ADDRH_MASK, + (uint8_t)AW_SET_BASEADDR_H(base_addr)); + val = (uint8_t)AW_SET_BASEADDR_L(base_addr); + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_RTPCFG2, &val, AW_I2C_BYTE_ONE); +} + +static void aw862xx_set_fifo_addr(struct aw_haptic *aw_haptic) +{ + uint8_t ae_addr_h = 0; + uint8_t af_addr_h = 0; + uint8_t val[3] = {0}; + + aw_info("enter"); + ae_addr_h = (uint8_t)AW862XX_SET_AEADDR_H(aw_haptic->ram.base_addr); + af_addr_h = (uint8_t)AW862XX_SET_AFADDR_H(aw_haptic->ram.base_addr); + val[0] = ae_addr_h | af_addr_h; + val[1] = (uint8_t)AW862XX_SET_AEADDR_L(aw_haptic->ram.base_addr); + val[2] = (uint8_t)AW862XX_SET_AFADDR_L(aw_haptic->ram.base_addr); + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_RTPCFG3, val, AW_I2C_BYTE_THREE); +} + +static void aw862xx_get_fifo_addr(struct aw_haptic *aw_haptic) +{ + uint8_t ae_addr_h = 0; + uint8_t af_addr_h = 0; + uint8_t ae_addr_l = 0; + uint8_t af_addr_l = 0; + uint8_t val[3] = {0}; + + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_RTPCFG3, val, AW_I2C_BYTE_THREE); + ae_addr_h = ((val[0]) & AW862XX_BIT_RTPCFG3_FIFO_AEH) >> 4; + ae_addr_l = val[1]; + af_addr_h = ((val[0]) & AW862XX_BIT_RTPCFG3_FIFO_AFH); + af_addr_l = val[2]; + aw_info("almost_empty_threshold = %u,almost_full_threshold = %u", + (uint16_t)((ae_addr_h << 8) | ae_addr_l), + (uint16_t)((af_addr_h << 8) | af_addr_l)); +} + +static void aw862xx_set_ram_addr(struct aw_haptic *aw_haptic) +{ + uint8_t val[2] = {0}; + uint32_t base_addr = aw_haptic->ram.base_addr; + + aw_info("enter"); + val[0] = (uint8_t)AW_SET_RAMADDR_H(base_addr); + val[1] = (uint8_t)AW_SET_RAMADDR_L(base_addr); + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_RAMADDRH, val, AW_I2C_BYTE_TWO); +} + +static void aw862xx_set_ram_data(struct aw_haptic *aw_haptic, uint8_t *data, int len) +{ + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_RAMDATA, data, len); +} + +static void aw862xx_get_first_wave_addr(struct aw_haptic *aw_haptic, uint32_t *first_wave_addr) +{ + uint8_t val[3] = {0}; + + aw_info("enter"); + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_RAMDATA, val, AW_I2C_BYTE_THREE); + *first_wave_addr = (val[1] << 8 | val[2]); +} + +static void aw862xx_haptic_select_pin(struct aw_haptic *aw_haptic, uint8_t pin) +{ + if (pin == AW_TRIG1) { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL2, + AW862XX_BIT_SYSCTRL2_INTN_PIN_MASK, + AW862XX_BIT_SYSCTRL2_TRIG1); + aw_info("select TRIG1 pin"); + } else if (pin == AW_IRQ) { + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL2, + AW862XX_BIT_SYSCTRL2_INTN_PIN_MASK, + AW862XX_BIT_SYSCTRL2_INTN); + aw_info("select INIT pin"); + } else + aw_err("There is no such option"); +} + +static void aw862xx_haptic_trig_param_init(struct aw_haptic *aw_haptic, uint8_t pin) +{ + struct aw_haptic_dts_info info = aw_haptic->info; + + switch (pin) { + case AW_TRIG1: + aw_haptic->trig[0].trig_level = info.trig_cfg[0]; + aw_haptic->trig[0].trig_polar = info.trig_cfg[1]; + aw_haptic->trig[0].pos_enable = info.trig_cfg[2]; + aw_haptic->trig[0].pos_sequence = info.trig_cfg[3]; + aw_haptic->trig[0].neg_enable = info.trig_cfg[4]; + aw_haptic->trig[0].neg_sequence = info.trig_cfg[5]; + aw_haptic->trig[0].trig_brk = info.trig_cfg[6]; + break; + case AW_TRIG2: + aw_haptic->trig[1].trig_level = info.trig_cfg[7 + 0]; + aw_haptic->trig[1].trig_polar = info.trig_cfg[7 + 1]; + aw_haptic->trig[1].pos_enable = info.trig_cfg[7 + 2]; + aw_haptic->trig[1].pos_sequence = info.trig_cfg[7 + 3]; + aw_haptic->trig[1].neg_enable = info.trig_cfg[7 + 4]; + aw_haptic->trig[1].neg_sequence = info.trig_cfg[7 + 5]; + aw_haptic->trig[1].trig_brk = info.trig_cfg[7 + 6]; + break; + case AW_TRIG3: + aw_haptic->trig[2].trig_level = info.trig_cfg[14 + 0]; + aw_haptic->trig[2].trig_polar = info.trig_cfg[14 + 1]; + aw_haptic->trig[2].pos_enable = info.trig_cfg[14 + 2]; + aw_haptic->trig[2].pos_sequence = info.trig_cfg[14 + 3]; + aw_haptic->trig[2].neg_enable = info.trig_cfg[14 + 4]; + aw_haptic->trig[2].neg_sequence = info.trig_cfg[14 + 5]; + aw_haptic->trig[2].trig_brk = info.trig_cfg[14 + 6]; + break; + default: + break; + } +} + +static int aw862xx_haptic_trig_param_config(struct aw_haptic *aw_haptic, uint8_t pin) +{ + uint8_t trig_polar_lev_brk = 0x00; + uint8_t trig_pos_seq = 0x00; + uint8_t trig_neg_seq = 0x00; + + if ((aw_haptic->name == AW86224 || aw_haptic->name == AW86225) && + aw_haptic->is_used_irq_pin) { + aw862xx_haptic_trig_param_init(aw_haptic, AW_TRIG1); + aw862xx_haptic_select_pin(aw_haptic, AW_IRQ); + return -ERANGE; + } + switch (pin) { + case AW_TRIG1: + if (aw_haptic->name == AW86224 || aw_haptic->name == AW86225) + aw862xx_haptic_select_pin(aw_haptic, AW_TRIG1); + trig_polar_lev_brk = aw_haptic->trig[0].trig_polar << 2 | + aw_haptic->trig[0].trig_level << 1 | + aw_haptic->trig[0].trig_brk; + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_TRGCFG7, + AW862XX_BIT_TRGCFG7_TRG1_POR_LEV_BRK_MASK, + trig_polar_lev_brk << 5); + trig_pos_seq = aw_haptic->trig[0].pos_enable << 7 | aw_haptic->trig[0].pos_sequence; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_TRGCFG1, + &trig_pos_seq, AW_I2C_BYTE_ONE); + trig_neg_seq = aw_haptic->trig[0].neg_enable << 7 | aw_haptic->trig[0].neg_sequence; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_TRGCFG4, + &trig_neg_seq, AW_I2C_BYTE_ONE); + aw_info("trig1 config ok!"); + break; + case AW_TRIG2: + trig_polar_lev_brk = aw_haptic->trig[1].trig_polar << 2 | + aw_haptic->trig[1].trig_level << 1 | + aw_haptic->trig[1].trig_brk; + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_TRGCFG7, + AW862XX_BIT_TRGCFG7_TRG2_POR_LEV_BRK_MASK, + trig_polar_lev_brk << 1); + trig_pos_seq = aw_haptic->trig[1].pos_enable << 7 | aw_haptic->trig[1].pos_sequence; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_TRGCFG2, + &trig_pos_seq, AW_I2C_BYTE_ONE); + trig_neg_seq = aw_haptic->trig[1].neg_enable << 7 | aw_haptic->trig[1].neg_sequence; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_TRGCFG5, + &trig_neg_seq, AW_I2C_BYTE_ONE); + aw_info("trig2 config ok!"); + break; + case AW_TRIG3: + trig_polar_lev_brk = aw_haptic->trig[2].trig_polar << 2 | + aw_haptic->trig[2].trig_level << 1 | + aw_haptic->trig[2].trig_brk; + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_TRGCFG8, + AW862XX_BIT_TRGCFG8_TRG3_POR_LEV_BRK_MASK, + trig_polar_lev_brk << 5); + trig_pos_seq = aw_haptic->trig[2].pos_enable << 7 | aw_haptic->trig[2].pos_sequence; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_TRGCFG3, + &trig_pos_seq, AW_I2C_BYTE_ONE); + trig_neg_seq = aw_haptic->trig[2].neg_enable << 7 | aw_haptic->trig[2].neg_sequence; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_TRGCFG6, + &trig_neg_seq, AW_I2C_BYTE_ONE); + aw_info("trig3 config ok!"); + break; + default: + break; + } + + return 0; +} + +static void aw862xx_set_trig(struct aw_haptic *aw_haptic, uint8_t pin) +{ + aw_info("enter"); + aw862xx_haptic_trig_param_init(aw_haptic, pin); + aw862xx_haptic_trig_param_config(aw_haptic, pin); +} + +static void aw862xx_trig_init(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + switch (aw_haptic->name) { + case AW86223: + aw862xx_set_trig(aw_haptic, AW_TRIG1); + aw862xx_set_trig(aw_haptic, AW_TRIG2); + aw862xx_set_trig(aw_haptic, AW_TRIG3); + break; + case AW86214: + case AW86224: + case AW86225: + aw862xx_set_trig(aw_haptic, AW_TRIG1); + break; + default: + break; + } +} + +static void aw862xx_parse_dts(struct aw_haptic *aw_haptic, struct device_node *np) +{ + uint32_t duration_time[3]; + uint32_t trig_config_temp[21]; + uint32_t val = 0; + + val = of_property_read_u32(np, "aw862xx_gain_bypass", &aw_haptic->info.gain_bypass); + if (val != 0) + aw_info("aw862xx_gain_bypass not found"); + val = of_property_read_u32(np, "aw862xx_vib_lk_f0_cali", &aw_haptic->info.lk_f0_cali); + if (val != 0) + aw_info("aw862xx_vib_lk_f0_cali not found"); + val = of_property_read_u32(np, "aw862xx_vib_mode", &aw_haptic->info.mode); + if (val != 0) + aw_info("aw862xx_vib_mode not found"); + val = of_property_read_u32(np, "aw862xx_vib_f0_pre", &aw_haptic->info.f0_pre); + if (val != 0) + aw_info("vib_f0_pre not found"); + val = of_property_read_u32(np, "aw862xx_vib_f0_cali_percen", + &aw_haptic->info.f0_cali_percent); + if (val != 0) + aw_info("vib_f0_cali_percent not found"); + val = of_property_read_u32(np, "aw862xx_vib_cont_drv1_lvl", &aw_haptic->info.cont_drv1_lvl); + if (val != 0) + aw_info("vib_cont_drv1_lvl not found"); + val = of_property_read_u32(np, "aw862xx_vib_lra_vrms", &aw_haptic->info.lra_vrms); + if (val != 0) + aw_info("vib_cont_lra_vrms not found"); + val = of_property_read_u32(np, "aw862xx_vib_cont_brk_time", &aw_haptic->info.cont_brk_time); + if (val != 0) + aw_info("vib_cont_brk_time not found"); + val = of_property_read_u32(np, "aw862xx_vib_cont_brk_gain", &aw_haptic->info.cont_brk_gain); + if (val != 0) + aw_info("vib_cont_brk_gain not found"); + val = of_property_read_u32(np, "aw862xx_vib_cont_drv1_time", + &aw_haptic->info.cont_drv1_time); + if (val != 0) + aw_info("vib_cont_drv1_time not found"); + val = of_property_read_u32(np, "aw862xx_vib_cont_drv2_time", + &aw_haptic->info.cont_drv2_time); + if (val != 0) + aw_info("vib_cont_drv2_time not found"); + val = of_property_read_u32(np, "aw862xx_vib_cont_track_margin", + &aw_haptic->info.cont_track_margin); + if (val != 0) + aw_info("vib_cont_track_margin not found"); + val = of_property_read_u32(np, "aw862xx_vib_d2s_gain", &aw_haptic->info.d2s_gain); + if (val != 0) + aw_info("vib_d2s_gain not found"); + val = of_property_read_u32_array(np, "aw862xx_vib_trig_config", trig_config_temp, + ARRAY_SIZE(trig_config_temp)); + if (val != 0) + aw_info("vib_trig_config not found"); + else + memcpy(aw_haptic->info.trig_cfg, trig_config_temp, sizeof(trig_config_temp)); + val = of_property_read_u32_array(np, "aw862xx_vib_duration_time", duration_time, + ARRAY_SIZE(duration_time)); + if (val != 0) + aw_info("aw862xx_duration_time not found"); + else + memcpy(aw_haptic->info.duration_time, duration_time, sizeof(duration_time)); + aw_haptic->info.is_enabled_track_en = + of_property_read_bool(np, "aw862xx_vib_is_enabled_track_en"); + aw_info("aw_haptic->info.is_enabled_track_en = %d", aw_haptic->info.is_enabled_track_en); + aw_haptic->info.is_enabled_auto_brk = + of_property_read_bool(np, "aw862xx_vib_is_enabled_auto_brk"); + aw_info("aw_haptic->info.is_enabled_auto_brk = %d", + aw_haptic->info.is_enabled_auto_brk); +} + +static void aw862xx_get_wav_seq(struct aw_haptic *aw_haptic, uint32_t len) +{ + if (len > AW_SEQUENCER_SIZE) + len = AW_SEQUENCER_SIZE; + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_WAVCFG1, aw_haptic->seq, len); +} + +static void aw862xx_set_wav_seq(struct aw_haptic *aw_haptic, uint8_t wav, uint8_t seq) +{ + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_WAVCFG1 + wav, &seq, AW_I2C_BYTE_ONE); +} + +static void aw862xx_set_wav_loop(struct aw_haptic *aw_haptic, uint8_t wav, uint8_t loop) +{ + uint8_t tmp = 0; + + if (wav % 2) { + tmp = loop << 0; + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_WAVCFG9 + (wav / 2), + AW862XX_BIT_WAVLOOP_SEQ_EVEN_MASK, tmp); + } else { + tmp = loop << 4; + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_WAVCFG9 + (wav / 2), + AW862XX_BIT_WAVLOOP_SEQ_ODD_MASK, tmp); + } +} + +static void aw862xx_set_repeat_seq(struct aw_haptic *aw_haptic, uint8_t seq) +{ + aw862xx_set_wav_seq(aw_haptic, 0x00, seq); + aw862xx_set_wav_seq(aw_haptic, 0x01, 0x00); + aw862xx_set_wav_loop(aw_haptic, 0x00, AW862XX_BIT_WAVLOOP_INIFINITELY); +} + +static void aw862xx_get_gain(struct aw_haptic *aw_haptic, uint8_t *gain) +{ + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_PLAYCFG2, gain, AW_I2C_BYTE_ONE); +} + +static void aw862xx_get_wav_loop(struct aw_haptic *aw_haptic, uint8_t *val) +{ + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_WAVCFG9, val, AW_SEQUENCER_LOOP_SIZE); +} + +static void aw862xx_get_ram_data(struct aw_haptic *aw_haptic, uint8_t *ram_data, int size) +{ + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_RAMDATA, ram_data, size); +} + +static void aw862xx_get_lra_resistance(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint8_t d2s_gain_temp = 0; + uint32_t lra_code = 0; + uint32_t lra = 0; + int d2s_gain = 0; + + aw862xx_play_stop(aw_haptic); + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_SYSCTRL7, ®_val, AW_I2C_BYTE_ONE); + d2s_gain_temp = AW862XX_BIT_SYSCTRL7_GAIN & reg_val; + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL7, + AW862XX_BIT_SYSCTRL7_D2S_GAIN_MASK, aw_haptic->info.d2s_gain); + d2s_gain = aw862xx_select_d2s_gain(aw_haptic->info.d2s_gain); + if (d2s_gain <= 0) { + aw_err("d2s_gain is error"); + return; + } + aw862xx_raminit(aw_haptic, true); + /* enter standby mode */ + aw862xx_play_stop(aw_haptic); + usleep_range(AW_STOP_DELAY_MIN, AW_STOP_DELAY_MAX); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL2, AW862XX_BIT_SYSCTRL2_STANDBY_MASK, + AW862XX_BIT_SYSCTRL2_STANDBY_OFF); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_DETCFG1, AW862XX_BIT_DETCFG1_RL_OS_MASK, + AW862XX_BIT_DETCFG1_RL); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_DETCFG2, AW862XX_BIT_DETCFG2_DIAG_GO_MASK, + AW862XX_BIT_DETCFG2_DIAG_GO_ON); + usleep_range(AW_RL_DELAY_MIN, AW_RL_DELAY_MAX); + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_DET_RL, ®_val, AW_I2C_BYTE_ONE); + lra_code = (lra_code | reg_val) << 2; + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_DET_LO, ®_val, AW_I2C_BYTE_ONE); + lra_code = lra_code | (reg_val & AW862XX_BIT_DET_LO_RL); + /* 2num */ + lra = AW862XX_RL_FORMULA(lra_code, d2s_gain); + /* Keep up with aw8624 driver */ + aw_haptic->lra = lra * 10; + aw862xx_raminit(aw_haptic, false); + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_SYSCTRL7, + AW862XX_BIT_SYSCTRL7_D2S_GAIN_MASK, d2s_gain_temp); +} + +static uint8_t aw862xx_get_prctmode(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_PWMCFG3, ®_val, AW_I2C_BYTE_ONE); + reg_val >>= 7; + + return reg_val; +} + +static uint8_t aw862xx_judge_rtp_going(struct aw_haptic *aw_haptic) +{ + uint8_t glb_state = 0; + uint8_t rtp_state = 0; + + glb_state = aw862xx_get_glb_state(aw_haptic); + if (glb_state == AW_BIT_STATE_RTP_GO) { + rtp_state = 1; /*is going on */ + aw_info("rtp_routine_on"); + } + + return rtp_state; +} + +static uint64_t aw862xx_get_theory_time(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t fre_val = 0; + uint64_t theory_time = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_SYSCTRL2, ®_val, AW_I2C_BYTE_ONE); + fre_val = reg_val & AW862XX_BIT_SYSCTRL2_RATE; + if (fre_val == AW862XX_BIT_SYSCTRL2_RATE_48K) + theory_time = (aw_haptic->rtp_len / 48) * 1000; /*48K*/ + else if (fre_val == AW862XX_BIT_SYSCTRL2_RATE_24K) + theory_time = (aw_haptic->rtp_len / 24) * 1000; /*24K*/ + else + theory_time = (aw_haptic->rtp_len / 12) * 1000; /*12K*/ + aw_info("microsecond:%llu theory_time = %llu", aw_haptic->microsecond, theory_time); + + return theory_time; +} + +static uint8_t aw862xx_get_osc_status(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_SYSST2, &state, AW_I2C_BYTE_ONE); + state &= AW862XX_BIT_SYSST2_FF_EMPTY; + + return state; +} + +static int aw862xx_check_qualify(struct aw_haptic *aw_haptic) +{ + uint8_t reg = 0; + int ret = 0; + + aw_info("enter"); + ret = haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_EFRD9, ®, AW_I2C_BYTE_ONE); + if (ret < 0) + return ret; + if ((reg & 0x80) == 0x80) + return 0; + aw_err("register 0x64 error: 0x%02X", reg); + + return -ERANGE; +} + +static ssize_t cont_drv_lvl_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_lvl = 0x%02X, cont_drv2_lvl = 0x%02X\n", + aw_haptic->info.cont_drv1_lvl, aw_haptic->info.cont_drv2_lvl); + + return len; +} + +static ssize_t cont_drv_lvl_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint8_t reg_val = 0; + uint32_t databuf[2] = { 0, 0 }; + + mutex_lock(&aw_haptic->lock); + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_haptic->info.cont_drv1_lvl = databuf[0]; + aw_haptic->info.cont_drv2_lvl = databuf[1]; + haptic_nv_i2c_write_bits(aw_haptic, AW862XX_REG_CONTCFG6, + AW862XX_BIT_CONTCFG6_DRV1_LVL_MASK, + aw_haptic->info.cont_drv1_lvl); + reg_val = (uint8_t)aw_haptic->info.cont_drv2_lvl; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG7, ®_val, AW_I2C_BYTE_ONE); + } + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t cont_drv_time_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_time = 0x%02X, cont_drv2_time = 0x%02X\n", + aw_haptic->info.cont_drv1_time, aw_haptic->info.cont_drv2_time); + + return len; +} + +static ssize_t cont_drv_time_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint8_t reg_val[2] = {0}; + uint32_t databuf[2] = { 0, 0 }; + + mutex_lock(&aw_haptic->lock); + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_haptic->info.cont_drv1_time = databuf[0]; + aw_haptic->info.cont_drv2_time = databuf[1]; + reg_val[0] = (uint8_t)aw_haptic->info.cont_drv1_time; + reg_val[1] = (uint8_t)aw_haptic->info.cont_drv2_time; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG8, reg_val, AW_I2C_BYTE_TWO); + } + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t cont_brk_time_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "cont_brk_time = 0x%02X\n", + aw_haptic->info.cont_brk_time); + + return len; +} + +static ssize_t cont_brk_time_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t reg_val = 0; + int err, val; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + err = kstrtoint(buf, 16, &val); + if (err != 0) { + aw_err("format not match!"); + return count; + } + mutex_lock(&aw_haptic->lock); + aw_haptic->info.cont_brk_time = val; + reg_val = aw_haptic->info.cont_brk_time; + haptic_nv_i2c_writes(aw_haptic, AW862XX_REG_CONTCFG10, ®_val, AW_I2C_BYTE_ONE); + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t trig_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int i = 0; + ssize_t len = 0; + uint8_t trig_num = 3; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + if (aw_haptic->name == AW86224 || aw_haptic->name == AW86225 || aw_haptic->name == AW86214) + trig_num = 1; + for (i = 0; i < trig_num; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, "trig%d: trig_level=%u, trig_polar=%u", + i + 1, aw_haptic->trig[i].trig_level, + aw_haptic->trig[i].trig_polar); + len += snprintf(buf + len, PAGE_SIZE - len, "pos_enable=%u, pos_sequence=%u,", + aw_haptic->trig[i].pos_enable, aw_haptic->trig[i].pos_sequence); + len += snprintf(buf + len, PAGE_SIZE - len, + "neg_enable=%u, neg_sequence=%u trig_brk=%u\n", + aw_haptic->trig[i].neg_enable, aw_haptic->trig[i].neg_sequence, + aw_haptic->trig[i].trig_brk); + } + + return len; +} + +static ssize_t trig_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t databuf[9] = { 0 }; + + if (sscanf(buf, "%u %u %u %u %u %u %u %u", &databuf[0], &databuf[1], &databuf[2], + &databuf[3], &databuf[4], &databuf[5], &databuf[6], &databuf[7]) == 8) { + aw_info("%u, %u, %u, %u, %u, %u, %u, %u", databuf[0], databuf[1], databuf[2], + databuf[3], databuf[4], databuf[5], databuf[6], databuf[7]); + if ((aw_haptic->name == AW86214 || aw_haptic->name == AW86224 || + aw_haptic->name == AW86225) && (databuf[0])) { + aw_err("input seq value out of range!"); + return count; + } + if (databuf[0] < 0 || databuf[0] > 2) { + aw_err("input seq value out of range!"); + return count; + } + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + if (databuf[4] > aw_haptic->ram.ram_num || databuf[6] > aw_haptic->ram.ram_num) { + aw_err("input seq value out of range!"); + return count; + } + aw_haptic->trig[databuf[0]].trig_level = databuf[1]; + aw_haptic->trig[databuf[0]].trig_polar = databuf[2]; + aw_haptic->trig[databuf[0]].pos_enable = databuf[3]; + aw_haptic->trig[databuf[0]].pos_sequence = databuf[4]; + aw_haptic->trig[databuf[0]].neg_enable = databuf[5]; + aw_haptic->trig[databuf[0]].neg_sequence = databuf[6]; + aw_haptic->trig[databuf[0]].trig_brk = databuf[7]; + mutex_lock(&aw_haptic->lock); + aw862xx_haptic_trig_param_config(aw_haptic, databuf[0]); + mutex_unlock(&aw_haptic->lock); + } else + aw_err("please input eight parameters"); + + return count; +} + +static DEVICE_ATTR_RW(cont_drv_lvl); +static DEVICE_ATTR_RW(cont_drv_time); +static DEVICE_ATTR_RW(cont_brk_time); +static DEVICE_ATTR_RW(trig); + +static struct attribute *aw862xx_vibrator_attributes[] = { + &dev_attr_cont_drv_lvl.attr, + &dev_attr_cont_drv_time.attr, + &dev_attr_cont_brk_time.attr, + &dev_attr_trig.attr, + NULL +}; + +static struct attribute_group aw862xx_vibrator_attribute_group = { + .attrs = aw862xx_vibrator_attributes +}; + +static void aw862xx_creat_node(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + ret = sysfs_create_group(&aw_haptic->vib_dev.dev->kobj, &aw862xx_vibrator_attribute_group); + if (ret < 0) + aw_err("error create aw862xx sysfs attr files"); +} + +struct aw_haptic_func aw862xx_func_list = { + .ram_init = aw862xx_raminit, + .parse_dts = aw862xx_parse_dts, + .trig_init = aw862xx_trig_init, + .play_mode = aw862xx_play_mode, + .play_stop = aw862xx_play_stop, + .irq_clear = aw862xx_irq_clear, + .creat_node = aw862xx_creat_node, + .cont_config = aw862xx_cont_config, + .offset_cali = aw862xx_offset_cali, + .haptic_start = aw862xx_haptic_start, + .read_cont_f0 = aw862xx_read_cont_f0, + .check_qualify = aw862xx_check_qualify, + .judge_rtp_going = aw862xx_judge_rtp_going, + .protect_config = aw862xx_protect_config, + .misc_para_init = aw862xx_misc_para_init, + .interrupt_setup = aw862xx_interrupt_setup, + .rtp_get_fifo_afs = aw862xx_rtp_get_fifo_afs, + .rtp_get_fifo_aes = aw862xx_rtp_get_fifo_aes, + .vbat_mode_config = aw862xx_vbat_mode_config, + .calculate_cali_data = aw862xx_calculate_cali_data, + .set_gain = aw862xx_set_gain, + .get_gain = aw862xx_get_gain, + .set_wav_seq = aw862xx_set_wav_seq, + .get_wav_seq = aw862xx_get_wav_seq, + .set_wav_loop = aw862xx_set_wav_loop, + .get_wav_loop = aw862xx_get_wav_loop, + .set_ram_data = aw862xx_set_ram_data, + .get_ram_data = aw862xx_get_ram_data, + .set_fifo_addr = aw862xx_set_fifo_addr, + .get_fifo_addr = aw862xx_get_fifo_addr, + .set_rtp_aei = aw862xx_set_rtp_aei, + .set_rtp_data = aw862xx_set_rtp_data, + .set_ram_addr = aw862xx_set_ram_addr, + .set_trim_lra = aw862xx_set_trim_lra, + .set_base_addr = aw862xx_set_base_addr, + .set_repeat_seq = aw862xx_set_repeat_seq, + .get_f0 = aw862xx_get_f0, + .get_reg = aw862xx_get_reg, + .get_vbat = aw862xx_get_vbat, + .get_prctmode = aw862xx_get_prctmode, + .get_irq_state = aw862xx_get_irq_state, + .get_glb_state = aw862xx_get_glb_state, + .get_osc_status = aw862xx_get_osc_status, + .get_theory_time = aw862xx_get_theory_time, + .get_lra_resistance = aw862xx_get_lra_resistance, + .get_first_wave_addr = aw862xx_get_first_wave_addr, +}; diff --git a/drivers/misc/awinic/aw862x_haptic_nv_v1/haptic_nv.c b/drivers/misc/awinic/aw862x_haptic_nv_v1/haptic_nv.c new file mode 100644 index 000000000000..1c00b742f245 --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv_v1/haptic_nv.c @@ -0,0 +1,3821 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * File: haptic_nv.c + * + * Author: + * + * Copyright (c) 2021 AWINIC Technology CO., 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "haptic_nv.h" +#include "haptic_nv_reg.h" + +#define HAPTIC_NV_DRIVER_VERSION "v1.12.0.10" + +static char aw_ram_name[][AW_NAME_MAX] = { + {"aw862x_haptic.bin"}, + {"aw862xx_haptic.bin"}, +}; + +static char aw_rtp_name[][AW_NAME_MAX] = { + {"haptic_nv_rtp_osc_24K_5s.bin"}, + {"haptic_nv_rtp.bin"}, + {"haptic_nv_rtp_lighthouse.bin"}, + {"haptic_nv_rtp_silk.bin"}, + {"haptic_nv_rtp_autosin.bin"}, +}; + +#ifdef AW_TIKTAP +static struct aw_haptic *g_aw_haptic; +#endif +#ifdef AW_DOUBLE +struct aw_haptic *left; +struct aw_haptic *right; +#endif + +/********************************************************* + * + * I2C Read/Write + * + *********************************************************/ +int haptic_nv_i2c_reads(struct aw_haptic *aw_haptic, uint8_t reg_addr, uint8_t *buf, uint32_t len) +{ + int ret = -1; + struct i2c_msg msg[] = { + [0] = { + .addr = aw_haptic->i2c->addr, + .flags = 0, + .len = sizeof(uint8_t), + .buf = ®_addr, + }, + [1] = { + .addr = aw_haptic->i2c->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + }, + }; + + ret = i2c_transfer(aw_haptic->i2c->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) { + aw_err("transfer failed. reg_addr=0x%02X", reg_addr); + return ret; + } else if (ret != AW_I2C_READ_MSG_NUM) { + aw_err("transfer failed(size error). reg_addr=0x%02X", reg_addr); + return -ENXIO; + } + + return ret; +} + +int haptic_nv_i2c_writes(struct aw_haptic *aw_haptic, uint8_t reg_addr, uint8_t *buf, uint32_t len) +{ + uint8_t *data = NULL; + int ret = -1; + + data = kmalloc(len + 1, GFP_KERNEL); + data[0] = reg_addr; + memcpy(&data[1], buf, len); + ret = i2c_master_send(aw_haptic->i2c, data, len + 1); + if (ret < 0) + aw_err("i2c master send 0x%02X err", reg_addr); + kfree(data); + + return ret; +} + +int haptic_nv_i2c_write_bits(struct aw_haptic *aw_haptic, uint8_t reg_addr, uint32_t mask, + uint8_t reg_data) +{ + uint8_t reg_val = 0; + int ret = -1; + + ret = haptic_nv_i2c_reads(aw_haptic, reg_addr, ®_val, AW_I2C_BYTE_ONE); + if (ret < 0) { + aw_err("i2c read error, ret=%d", ret); + return ret; + } + reg_val &= mask; + reg_val |= (reg_data & (~mask)); + ret = haptic_nv_i2c_writes(aw_haptic, reg_addr, ®_val, AW_I2C_BYTE_ONE); + if (ret < 0) { + aw_err("i2c write error, ret=%d", ret); + return ret; + } + + return 0; +} + +ssize_t haptic_nv_read_reg_array(struct aw_haptic *aw_haptic, char *buf, ssize_t len, + uint8_t head_reg_addr, uint8_t tail_reg_addr) +{ + int reg_num = 0; + int i = 0; + uint8_t reg_array[AW_REG_MAX] = {0}; + + reg_num = tail_reg_addr - head_reg_addr + 1; + haptic_nv_i2c_reads(aw_haptic, head_reg_addr, reg_array, reg_num); + for (i = 0 ; i < reg_num; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, "reg:0x%02X=0x%02X\n", + head_reg_addr + i, reg_array[i]); + } + + return len; +} + +static void pm_qos_enable(struct aw_haptic *aw_haptic, bool enable) +{ +#ifdef KERNEL_OVER_5_10 + if (enable) { + if (!cpu_latency_qos_request_active(&aw_haptic->aw_pm_qos_req_vb)) + cpu_latency_qos_add_request(&aw_haptic->aw_pm_qos_req_vb, + CPU_LATENCY_QOC_VALUE); + } else { + if (cpu_latency_qos_request_active(&aw_haptic->aw_pm_qos_req_vb)) + cpu_latency_qos_remove_request(&aw_haptic->aw_pm_qos_req_vb); + } +#else + if (enable) { + if (!pm_qos_request_active(&aw_haptic->aw_pm_qos_req_vb)) + pm_qos_add_request(&aw_haptic->aw_pm_qos_req_vb, PM_QOS_CPU_DMA_LATENCY, + AW_PM_QOS_VALUE_VB); + } else { + if (pm_qos_request_active(&aw_haptic->aw_pm_qos_req_vb)) + pm_qos_remove_request(&aw_haptic->aw_pm_qos_req_vb); + } +#endif +} + +#ifdef AW_CHECK_RAM_DATA +static int parse_ram_data(struct aw_haptic *aw_haptic, uint32_t len, uint8_t *cont_data, + uint8_t *ram_data) +{ + int i = 0; + + aw_info("enter"); + for (i = 0; i < len; i++) { + if (ram_data[i] != cont_data[i]) { + aw_err("check ramdata error, addr=0x%04X, ram_data=0x%02X, file_data=0x%02X", + i, ram_data[i], cont_data[i]); + return -ERANGE; + } + } + + return 0; +} + +static int check_ram_data(struct aw_haptic *aw_haptic, struct aw_haptic_container *aw_fw) +{ + int i = 0; + int len = 0; + int ret = 0; + uint8_t ram_data[AW_RAMDATA_RD_BUFFER_SIZE] = {0}; + + aw_haptic->func->set_ram_addr(aw_haptic); + i = aw_haptic->ram.ram_shift; + while (i < aw_fw->len) { + if ((aw_fw->len - i) < AW_RAMDATA_RD_BUFFER_SIZE) + len = aw_fw->len - i; + else + len = AW_RAMDATA_RD_BUFFER_SIZE; + + aw_haptic->func->get_ram_data(aw_haptic, ram_data, len); + ret = parse_ram_data(aw_haptic, len, &aw_fw->data[i], ram_data); + if (ret < 0) + break; + i += len; + } + + return ret; +} +#endif + +static void write_ram_data(struct aw_haptic *aw_haptic, struct aw_haptic_container *aw_fw) +{ + int i = 0; + int len = 0; + + aw_haptic->func->set_ram_addr(aw_haptic); + i = aw_haptic->ram.ram_shift; + while (i < aw_fw->len) { + if ((aw_fw->len - i) < AW_RAMDATA_WR_BUFFER_SIZE) + len = aw_fw->len - i; + else + len = AW_RAMDATA_WR_BUFFER_SIZE; + + aw_haptic->func->set_ram_data(aw_haptic, &aw_fw->data[i], len); + i += len; + } +} + +static int parse_dt_gpio(struct aw_haptic *aw_haptic, struct device *dev, struct device_node *np) +{ + aw_haptic->is_used_rst_pin = of_property_read_bool(np, "aw8624x_no_reset") ? 0 : 1; + if (aw_haptic->is_used_rst_pin) { + aw_haptic->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (aw_haptic->reset_gpio < 0) { + aw_err("no reset gpio provide"); + return -EPERM; + } + aw_info("reset gpio provide ok %d", aw_haptic->reset_gpio); + } else { + aw_info("not use rst pin"); + } + + aw_haptic->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (aw_haptic->irq_gpio < 0) { + aw_err("no irq gpio provided."); + aw_haptic->is_used_irq_pin = false; + } else { + aw_info("irq gpio provide ok irq = %d.", aw_haptic->irq_gpio); + aw_haptic->is_used_irq_pin = true; + } +#ifdef AW_DOUBLE + if (of_device_is_compatible(np, "awinic,haptic_nv_l")) { + aw_info("compatible left vibrator."); + memcpy(aw_haptic->mark, "left", sizeof("left")); + left = NULL; + left = aw_haptic; + } else if (of_device_is_compatible(np, "awinic,haptic_nv_r")) { + aw_info("compatible right vibrator."); + memcpy(aw_haptic->mark, "right", sizeof("right")); + right = NULL; + right = aw_haptic; + } else { + aw_err("compatible failed."); + return -ERANGE; + } +#endif + + return 0; +} + +static void parse_dts_i2c_addr(struct aw_haptic *aw_haptic, struct device *dev, + struct device_node *np) +{ + uint32_t val = 0; + + val = of_property_read_u32(np, "aw862xx_i2c_addr", &aw_haptic->aw862xx_i2c_addr); + if (val) + aw_err("configure aw862xx_i2c_addr error"); + else + aw_info("configure aw862xx_i2c_addr ok"); +} + +static void hw_reset(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + if (aw_haptic->is_used_rst_pin) { + if (gpio_is_valid(aw_haptic->reset_gpio)) { + gpio_set_value_cansleep(aw_haptic->reset_gpio, 0); + usleep_range(1000, 2000); + gpio_set_value_cansleep(aw_haptic->reset_gpio, 1); + usleep_range(8000, 8500); + } else { + aw_err("failed"); + } + } else { + aw_info("aw_haptic->is_used_rst_pin is %d", aw_haptic->is_used_rst_pin); + } +} + +static void sw_reset(struct aw_haptic *aw_haptic) +{ + uint8_t reset = AW_BIT_RESET; + + aw_info("enter"); + haptic_nv_i2c_writes(aw_haptic, AW_REG_ID, &reset, AW_I2C_BYTE_ONE); + usleep_range(2000, 2500); +} + +static int read_chipid(struct aw_haptic *aw_haptic, uint32_t *reg_val, uint8_t type) +{ + uint8_t value[2] = {0}; + int cnt = 0; + int ret = -1; + + while (cnt < AW_I2C_RETRIES) { + ret = haptic_nv_i2c_reads(aw_haptic, AW_REG_CHIPIDH, &value[0], AW_I2C_BYTE_ONE); + if (value[0] == AW8623X_CHIP_ID_H) { + ret = haptic_nv_i2c_reads(aw_haptic, AW8623X_REG_CHIPIDL, &value[1], + AW_I2C_BYTE_ONE); + *reg_val = value[1] | value[0] << 8; + } else if (value[0] == AW8624X_CHIP_ID_H) { + ret = haptic_nv_i2c_reads(aw_haptic, AW8624X_REG_CHIPIDL, &value[1], + AW_I2C_BYTE_ONE); + *reg_val = value[1] | value[0] << 8; + } else { + ret = haptic_nv_i2c_reads(aw_haptic, AW_REG_ID, &value[0], AW_I2C_BYTE_ONE); + *reg_val = value[0]; + } + if (ret < 0) { + if (type == AW_FIRST_TRY) + aw_info("reading chip id"); + else if (type == AW_LAST_TRY) + aw_err("i2c_read cnt=%d error=%d", cnt, ret); + else + aw_err("type is error"); + } else { + break; + } + cnt++; + usleep_range(2000, 3000); + } + + return ret; +} + +static int parse_chipid(struct aw_haptic *aw_haptic) +{ + int ret = -1; + uint8_t cnt = 0; + uint8_t reg = 0; + uint32_t reg_val = 0; + + for (cnt = 0; cnt < AW_READ_CHIPID_RETRIES; cnt++) { + /* hardware reset */ + hw_reset(aw_haptic); + + ret = read_chipid(aw_haptic, ®_val, AW_FIRST_TRY); + if (ret < 0) { + aw_haptic->i2c->addr = (u16)aw_haptic->aw862xx_i2c_addr; + aw_info("try to replace i2c addr [(0x%02X)] to read chip id again", + aw_haptic->i2c->addr); + ret = read_chipid(aw_haptic, ®_val, AW_LAST_TRY); + if (ret < 0) + break; + } + + switch (reg_val) { +#ifdef AW862X_DRIVER + case AW8623_CHIP_ID: + aw_haptic->name = AW8623; + aw_info("detected aw8623."); + return 0; + case AW8624_CHIP_ID: + aw_haptic->name = AW8624; + aw_info("detected aw8624."); + return 0; +#endif + +#ifdef AW862XX_DRIVER + case AW8622X_CHIP_ID: + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_EFRD9, ®, AW_I2C_BYTE_ONE); + if ((reg & 0x41) == AW86223_EF_ID) { + aw_haptic->name = AW86223; + aw_info("aw86223 detected"); + return 0; + } + if ((reg & 0x41) == AW86224_EF_ID) { + aw_haptic->name = AW86224; + aw_info("aw86224 or aw86225 detected"); + return 0; + } + aw_info("unsupported ef_id = (0x%02X)", reg); + break; + case AW86214_CHIP_ID: + haptic_nv_i2c_reads(aw_haptic, AW862XX_REG_EFRD9, ®, AW_I2C_BYTE_ONE); + if ((reg & 0x41) == AW86214_EF_ID) { + aw_haptic->name = AW86214; + aw_info("aw86214 detected"); + return 0; + } + aw_info("unsupported ef_id = (0x%02X)", reg); + break; +#endif + +#ifdef AW8623X_DRIVER + case AW86233_CHIP_ID: + aw_haptic->name = AW86233; + aw_info("aw86233 detected"); + return 0; + case AW86234_CHIP_ID: + aw_haptic->name = AW86234; + aw_info("aw86234 detected"); + return 0; + case AW86235_CHIP_ID: + aw_haptic->name = AW86235; + aw_info("aw86235 detected"); + return 0; +#endif + +#ifdef AW8624X_DRIVER + case AW86243_CHIP_ID: + aw_haptic->name = AW86243; + aw_info("aw86243 detected"); + return 0; + case AW86245_CHIP_ID: + aw_haptic->name = AW86245; + aw_info("aw86245 detected"); + return 0; +#endif + default: + aw_info("unsupport device revision (0x%02X)", reg_val); + break; + } + usleep_range(AW_READ_CHIPID_RETRY_DELAY * 1000, + AW_READ_CHIPID_RETRY_DELAY * 1000 + 500); + } + + return -EINVAL; +} + +static int chip_private_init(struct aw_haptic *aw_haptic) +{ + switch (aw_haptic->name) { +#ifdef AW862X_DRIVER + case AW8623: + case AW8624: + aw_haptic->trim_lra_boundary = AW_TRIM_LRA_BOUNDARY; + aw_haptic->ram_file_num = AW862X_RAM_FILE; + break; +#endif + +#ifdef AW862XX_DRIVER + case AW86214: + case AW86223: + case AW86224: + case AW86225: + aw_haptic->trim_lra_boundary = AW_TRIM_LRA_BOUNDARY; + aw_haptic->ram_file_num = AW862XX_RAM_FILE; + break; +#endif + +#ifdef AW8623X_DRIVER + case AW86233: + case AW86234: + case AW86235: + aw_haptic->trim_lra_boundary = AW_TRIM_LRA_BOUNDARY; + aw_haptic->ram_file_num = AW862XX_RAM_FILE; + break; +#endif + +#ifdef AW8624X_DRIVER + case AW86243: + case AW86245: + aw_haptic->trim_lra_boundary = AW8624X_TRIM_LRA_BOUNDARY; + aw_haptic->ram_file_num = AW862XX_RAM_FILE; + break; +#endif + + default: + aw_info("unexpected chip!"); + return -EINVAL; + } + + return 0; +} + +static int func_ptr_init(struct aw_haptic *aw_haptic, struct device *dev) +{ + switch (aw_haptic->name) { +#ifdef AW862X_DRIVER + case AW8623: + case AW8624: + aw_haptic->func = &aw862x_func_list; + break; +#endif + +#ifdef AW862XX_DRIVER + case AW86214: + case AW86223: + case AW86224: + case AW86225: + aw_haptic->func = &aw862xx_func_list; + break; +#endif + +#ifdef AW8623X_DRIVER + case AW86233: + case AW86234: + case AW86235: + aw_haptic->func = &aw8623x_func_list; + break; +#endif + +#ifdef AW8624X_DRIVER + case AW86243: + case AW86245: + aw_haptic->func = &aw8624x_func_list; + break; +#endif + + default: + aw_info("unexpected chip!"); + return -EINVAL; + } + + return 0; +} + +static int write_rtp_data(struct aw_haptic *aw_haptic) +{ + uint32_t buf_len = 0; + int ret = -ERANGE; + struct aw_haptic_container *aw_rtp = aw_haptic->aw_rtp; + + if (!aw_rtp) { + aw_info("aw_rtp is null, break!"); + return ret; + } + + if (aw_haptic->rtp_cnt < (aw_haptic->ram.base_addr)) { + if ((aw_rtp->len - aw_haptic->rtp_cnt) < (aw_haptic->ram.base_addr)) + buf_len = aw_rtp->len - aw_haptic->rtp_cnt; + else + buf_len = aw_haptic->ram.base_addr; + } else if ((aw_rtp->len - aw_haptic->rtp_cnt) < (aw_haptic->ram.base_addr >> 2)) { + buf_len = aw_rtp->len - aw_haptic->rtp_cnt; + } else { + buf_len = aw_haptic->ram.base_addr >> 2; + } + +#ifdef AW_ENABLE_RTP_PRINT_LOG + aw_info("buf_len = %u", buf_len); +#endif +#ifdef AW_DOUBLE + if (aw_haptic->rtp_cnt == 0 && aw_haptic->dual_flag) { + aw_haptic->dual_flag = false; + if (down_trylock(&left->sema) == 0) { + ret = down_interruptible(&left->sema); + } else { + up(&left->sema); + up(&left->sema); + } + aw_info("dual rtp play start"); + } +#endif + aw_haptic->func->set_rtp_data(aw_haptic, &aw_rtp->data[aw_haptic->rtp_cnt], buf_len); + aw_haptic->rtp_cnt += buf_len; + + return 0; + +} + +static int judge_rtp_load_end(struct aw_haptic *aw_haptic) +{ + uint8_t glb_st = 0; + int ret = -ERANGE; + struct aw_haptic_container *aw_rtp = aw_haptic->aw_rtp; + + glb_st = aw_haptic->func->get_glb_state(aw_haptic); + + if ((aw_haptic->rtp_cnt == aw_rtp->len) || + ((glb_st & AW_BIT_GLBRD_STATE_MASK) == AW_BIT_STATE_STANDBY)) { + if (aw_haptic->rtp_cnt != aw_rtp->len) + aw_err("rtp play suspend!"); + else + aw_info("rtp update complete!,cnt=%u", aw_haptic->rtp_cnt); + aw_haptic->rtp_cnt = 0; + aw_haptic->rtp_init = false; + aw_haptic->func->set_rtp_aei(aw_haptic, false); + ret = 0; + } + + return ret; +} + +static void rtp_play(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + while ((!aw_haptic->func->rtp_get_fifo_afs(aw_haptic)) + && (aw_haptic->play_mode == AW_RTP_MODE)) { + +#ifdef AW_ENABLE_RTP_PRINT_LOG + aw_info("rtp cnt = %u", aw_haptic->rtp_cnt); +#endif + ret = write_rtp_data(aw_haptic); + if (ret != 0) + break; + ret = judge_rtp_load_end(aw_haptic); + if (ret == 0) + break; + } +} + +static irqreturn_t irq_handle(int irq, void *data) +{ + int irq_state = 0; + struct aw_haptic *aw_haptic = data; + + aw_info("enter"); + + do { + irq_state = aw_haptic->func->get_irq_state(aw_haptic); + + if (irq_state == AW_IRQ_ALMOST_EMPTY) { + if (aw_haptic->rtp_init) { + mutex_lock(&aw_haptic->rtp_lock); + rtp_play(aw_haptic); + mutex_unlock(&aw_haptic->rtp_lock); + } else { + aw_info("rtp_init: %d", aw_haptic->rtp_init); + } + } + + if (aw_haptic->play_mode != AW_RTP_MODE) + aw_haptic->func->set_rtp_aei(aw_haptic, false); + } while (irq_state); + + aw_info("exit"); + + return IRQ_HANDLED; +} + +static int irq_config(struct device *dev, struct aw_haptic *aw_haptic) +{ + int ret = -1; + int irq_flags = 0; + + aw_info("enter"); + if (gpio_is_valid(aw_haptic->irq_gpio) && !(aw_haptic->flags & AW_FLAG_SKIP_INTERRUPTS)) { + /* register irq handler */ + aw_haptic->func->interrupt_setup(aw_haptic); + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(dev, gpio_to_irq(aw_haptic->irq_gpio), NULL, + irq_handle, irq_flags, "aw_haptic", aw_haptic); + if (ret != 0) { + aw_err("failed to request IRQ %d: %d", + gpio_to_irq(aw_haptic->irq_gpio), ret); + return ret; + } + } else { + aw_info("skipping IRQ registration"); + /* disable feature support if gpio was invalid */ + aw_haptic->flags |= AW_FLAG_SKIP_INTERRUPTS; + } + + return 0; +} + +static int get_ram_num(struct aw_haptic *aw_haptic) +{ + uint32_t first_wave_addr = 0; + + aw_info("enter"); + if (!aw_haptic->ram_init) { + aw_err("ram init failed, ram_num = 0!"); + return -EPERM; + } + + mutex_lock(&aw_haptic->lock); + /* RAMINIT Enable */ + aw_haptic->func->play_stop(aw_haptic); + aw_haptic->func->ram_init(aw_haptic, true); + aw_haptic->func->set_ram_addr(aw_haptic); + aw_haptic->func->get_first_wave_addr(aw_haptic, &first_wave_addr); + aw_haptic->ram.ram_num = (first_wave_addr - aw_haptic->ram.base_addr - 1) / 4; + aw_info("first wave addr = 0x%04X, ram_num = %u", first_wave_addr, aw_haptic->ram.ram_num); + /* RAMINIT Disable */ + aw_haptic->func->ram_init(aw_haptic, false); + mutex_unlock(&aw_haptic->lock); + + return 0; +} + +static int container_update(struct aw_haptic *aw_haptic, struct aw_haptic_container *aw_fw) +{ + uint32_t shift = 0; + int ret = 0; + + aw_info("enter"); + + mutex_lock(&aw_haptic->lock); + aw_haptic->ram.baseaddr_shift = 2; + aw_haptic->ram.ram_shift = 4; + aw_haptic->func->play_stop(aw_haptic); + aw_haptic->func->ram_init(aw_haptic, true); + shift = aw_haptic->ram.baseaddr_shift; + aw_haptic->ram.base_addr = (uint32_t)((aw_fw->data[0 + shift] << 8) | + (aw_fw->data[1 + shift])); + aw_info("base_addr = %u", aw_haptic->ram.base_addr); + if (!aw_haptic->ram.base_addr) + aw_info("no set FIFO space for rtp play!"); + aw_haptic->func->set_base_addr(aw_haptic); + aw_haptic->func->set_fifo_addr(aw_haptic); + aw_haptic->func->get_fifo_addr(aw_haptic); + write_ram_data(aw_haptic, aw_fw); + +#ifdef AW_CHECK_RAM_DATA + ret = check_ram_data(aw_haptic, aw_fw); + if (ret) + aw_err("ram data check sum error"); + else + aw_info("ram data check sum pass"); +#endif + + aw_haptic->func->ram_init(aw_haptic, false); + mutex_unlock(&aw_haptic->lock); + + aw_info("exit"); + + return ret; +} + +static void ram_load(const struct firmware *cont, void *context) +{ + uint16_t check_sum = 0; + int i = 0; + int ret = 0; + struct aw_haptic *aw_haptic = context; + struct aw_haptic_container *aw_fw; + uint32_t ram_file_num = aw_haptic->ram_file_num; +#ifdef AW_READ_BIN_FLEXBALLY + static uint8_t load_cont; + int ram_timer_val = 1000; + + load_cont++; +#endif + + if (!cont) { + aw_err("failed to read %s", aw_ram_name[ram_file_num]); + release_firmware(cont); + +#ifdef AW_READ_BIN_FLEXBALLY + if (load_cont <= 20) { + schedule_delayed_work(&aw_haptic->ram_work, + msecs_to_jiffies(ram_timer_val)); + aw_info("start hrtimer:load_cont%u", load_cont); + } +#endif + return; + } + + aw_info("loaded %s - size: %zu", aw_ram_name[ram_file_num], cont ? cont->size : 0); + /* check sum */ + for (i = 2; i < cont->size; i++) + check_sum += cont->data[i]; + if (check_sum != (uint16_t)((cont->data[0] << 8) | (cont->data[1]))) { + aw_err("check sum err: check_sum=0x%04X", check_sum); + release_firmware(cont); + return; + } + aw_info("check sum pass."); + aw_haptic->ram.check_sum = check_sum; + + /* aw ram update */ + aw_fw = kzalloc(cont->size + sizeof(int), GFP_KERNEL); + if (!aw_fw) { + release_firmware(cont); + aw_err("Error allocating memory"); + return; + } + aw_fw->len = cont->size; + memcpy(aw_fw->data, cont->data, cont->size); + release_firmware(cont); + ret = container_update(aw_haptic, aw_fw); + if (ret) { + aw_err("ram firmware update failed!"); + } else { + aw_haptic->ram_init = true; + aw_haptic->ram.len = aw_fw->len - aw_haptic->ram.ram_shift; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->trig_init(aw_haptic); + mutex_unlock(&aw_haptic->lock); + + aw_info("ram firmware update complete!"); + get_ram_num(aw_haptic); + } + kfree(aw_fw); + aw_fw = NULL; + +} + +static int ram_update(struct aw_haptic *aw_haptic) +{ + aw_haptic->ram_init = false; + aw_haptic->rtp_init = false; + + return request_firmware_nowait(THIS_MODULE, 1, aw_ram_name[aw_haptic->ram_file_num], + aw_haptic->dev, GFP_KERNEL, aw_haptic, ram_load); +} + +static void ram_play(struct aw_haptic *aw_haptic) +{ +#ifdef AW_DOUBLE + int ret = -1; + + if (aw_haptic->activate_mode == AW_RAM_MODE) + aw_haptic->func->set_wav_loop(aw_haptic, 0x00, 0x00); + aw_haptic->func->play_mode(aw_haptic, aw_haptic->activate_mode); + if (aw_haptic->dual_flag) { + aw_haptic->dual_flag = false; + if (down_trylock(&left->sema) == 0) { + ret = down_interruptible(&left->sema); + } else { + up(&left->sema); + up(&left->sema); + } + } +#else + aw_haptic->func->play_mode(aw_haptic, aw_haptic->activate_mode); +#endif + aw_haptic->func->haptic_start(aw_haptic); +} + +#ifdef AW_DURATION_DECIDE_WAVEFORM +static int analyse_duration_range(struct aw_haptic *aw_haptic) +{ + int i = 0; + int ret = 0; + int len = 0; + int *duration_time = NULL; + + aw_info("enter"); + len = ARRAY_SIZE(aw_haptic->info.duration_time); + duration_time = aw_haptic->info.duration_time; + if (len < 2) { + aw_err("duration time range error"); + return -ERANGE; + } + + for (i = (len - 1); i > 0; i--) { + if (duration_time[i] > duration_time[i-1]) + continue; + else + break; + + } + + if (i > 0) { + aw_err("duration time range error"); + ret = -ERANGE; + } + + return ret; +} + +static int ram_config(struct aw_haptic *aw_haptic, int duration) +{ + uint8_t wavseq = 0; + uint8_t wavloop = 0; + int ret = 0; + + aw_info("enter"); + ret = analyse_duration_range(aw_haptic); + if (ret < 0) + return ret; + + if ((duration > 0) && (duration < aw_haptic->info.duration_time[0])) { + wavseq = 3; + aw_haptic->activate_mode = AW_RAM_MODE; + } else if ((duration >= aw_haptic->info.duration_time[0]) && + (duration < aw_haptic->info.duration_time[1])) { + wavseq = 2; + aw_haptic->activate_mode = AW_RAM_MODE; + } else if ((duration >= aw_haptic->info.duration_time[1]) && + (duration < aw_haptic->info.duration_time[2])) { + wavseq = 1; + aw_haptic->activate_mode = AW_RAM_MODE; + } else if (duration >= aw_haptic->info.duration_time[2]) { + wavseq = 4; + wavloop = 15; + aw_haptic->activate_mode = AW_RAM_LOOP_MODE; + } else { + aw_err("duration time error, duration= %d", duration); + aw_haptic->activate_mode = AW_NULL; + return -ERANGE; + } + aw_info("duration %d, select index %u", aw_haptic->duration, wavseq); + aw_haptic->func->set_wav_seq(aw_haptic, 0, wavseq); + aw_haptic->func->set_wav_loop(aw_haptic, 0, wavloop); + aw_haptic->func->set_wav_seq(aw_haptic, 1, 0); + aw_haptic->func->set_wav_loop(aw_haptic, 1, 0); + + return 0; +} +#endif + +static void upload_lra(struct aw_haptic *aw_haptic, uint8_t flag) +{ + uint8_t reg_val; + + switch (flag) { + case AW_WRITE_ZERO: + aw_info("write zero to trim_lra!"); + reg_val = 0x00; + break; + case AW_F0_CALI_LRA: + aw_info("write f0_cali_data to trim_lra = 0x%02X", aw_haptic->f0_cali_data); + reg_val = (uint8_t)aw_haptic->f0_cali_data; + break; + case AW_OSC_CALI_LRA: + aw_info("write osc_cali_data to trim_lra = 0x%02X", aw_haptic->osc_cali_data); + reg_val = (uint8_t)aw_haptic->osc_cali_data; + break; + default: + aw_err("flag is error"); + reg_val = 0x00; + break; + } + aw_haptic->func->set_trim_lra(aw_haptic, reg_val); +} + +static int judge_within_cali_range(struct aw_haptic *aw_haptic) +{ + uint32_t f0_cali_min = 0; + uint32_t f0_cali_max = 0; + + aw_info("enter"); + f0_cali_min = aw_haptic->info.f0_pre * (100 - aw_haptic->info.f0_cali_percent) / 100; + f0_cali_max = aw_haptic->info.f0_pre * (100 + aw_haptic->info.f0_cali_percent) / 100; + + aw_info("f0_pre = %u, f0_cali_min = %u, f0_cali_max = %u, f0 = %u", + aw_haptic->info.f0_pre, f0_cali_min, f0_cali_max, aw_haptic->f0); + + if (aw_haptic->f0 < f0_cali_min) { + aw_err("lra f0 is too small, lra_f0 = %u!", aw_haptic->f0); +#ifdef AW_MAXIMUM_F0_CALI_DATA + aw_haptic->f0_cali_data = aw_haptic->trim_lra_boundary; + upload_lra(aw_haptic, AW_F0_CALI_LRA); +#endif + return -ERANGE; + } + + if (aw_haptic->f0 > f0_cali_max) { + aw_err("lra f0 is too large, lra_f0 = %u!", aw_haptic->f0); +#ifdef AW_MAXIMUM_F0_CALI_DATA + aw_haptic->f0_cali_data = aw_haptic->trim_lra_boundary - 1; + upload_lra(aw_haptic, AW_F0_CALI_LRA); +#endif + return -ERANGE; + } + + return 0; +} + +static int f0_cali(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + aw_info("enter"); + upload_lra(aw_haptic, AW_WRITE_ZERO); + if (aw_haptic->func->get_f0(aw_haptic)) { + aw_err("get f0 failed"); + } else { + /* max and min limit */ + ret = judge_within_cali_range(aw_haptic); + if (ret < 0) + return -ERANGE; + /* calculate cali step */ + aw_haptic->func->calculate_cali_data(aw_haptic); + upload_lra(aw_haptic, AW_F0_CALI_LRA); + } + aw_haptic->func->play_stop(aw_haptic); + + return ret; +} + +static void ram_vbat_comp(struct aw_haptic *aw_haptic, bool flag) +{ + int temp_gain = 0; + + aw_info("enter"); + if (flag) { + if (aw_haptic->ram_vbat_comp == AW_RAM_VBAT_COMP_ENABLE) { + aw_haptic->func->get_vbat(aw_haptic); + temp_gain = aw_haptic->gain * AW_VBAT_REFER / aw_haptic->vbat; + if (temp_gain > (AW_DEFAULT_GAIN * AW_VBAT_REFER / AW_VBAT_MIN)) { + temp_gain = AW_DEFAULT_GAIN * AW_VBAT_REFER / AW_VBAT_MIN; + aw_info("gain limit=%d", temp_gain); + } + aw_haptic->func->set_gain(aw_haptic, temp_gain); + aw_info("ram vbat comp open, set_gain 0x%02X", temp_gain); + } else { + aw_haptic->func->set_gain(aw_haptic, aw_haptic->gain); + aw_info("ram vbat comp close, set_gain 0x%02X", aw_haptic->gain); + } + } else { + aw_haptic->func->set_gain(aw_haptic, aw_haptic->gain); + aw_info("ram vbat comp close, set_gain 0x%02X", aw_haptic->gain); + } +} + +static int osc_calculation_time(struct aw_haptic *aw_haptic) +{ + uint32_t base_addr = aw_haptic->ram.base_addr; + uint32_t buf_len = 0; + struct aw_haptic_container *aw_rtp = aw_haptic->aw_rtp; + + if (!aw_haptic->func->rtp_get_fifo_afs(aw_haptic)) { + aw_dbg("haptic_rtp_get_fifo_afi, rtp_cnt= %u", aw_haptic->rtp_cnt); + + mutex_lock(&aw_haptic->rtp_lock); + pm_qos_enable(aw_haptic, true); + if ((aw_rtp->len - aw_haptic->rtp_cnt) < (base_addr >> 2)) + buf_len = aw_rtp->len - aw_haptic->rtp_cnt; + else + buf_len = (base_addr >> 2); + + if (aw_haptic->rtp_cnt != aw_rtp->len) { + if (aw_haptic->timeval_flags == 1) { + aw_haptic->kstart = ktime_get(); + aw_haptic->timeval_flags = 0; + } + aw_haptic->func->set_rtp_data(aw_haptic, &aw_rtp->data[aw_haptic->rtp_cnt], + buf_len); + aw_haptic->rtp_cnt += buf_len; + } + pm_qos_enable(aw_haptic, false); + mutex_unlock(&aw_haptic->rtp_lock); + } + + if (aw_haptic->func->get_osc_status(aw_haptic)) { + aw_haptic->kend = ktime_get(); + aw_info("osc trim playback done aw_haptic->rtp_cnt= %u", aw_haptic->rtp_cnt); + return 0; + } + aw_haptic->kend = ktime_get(); + aw_haptic->microsecond = ktime_to_us(ktime_sub(aw_haptic->kend, aw_haptic->kstart)); + if (aw_haptic->microsecond > AW_OSC_CALI_MAX_LENGTH) { + aw_info("osc trim time out! aw_haptic->rtp_cnt %u", aw_haptic->rtp_cnt); + return 0; + } + + return -ERANGE; +} + +static int rtp_fw_load(struct aw_haptic *aw_haptic, uint32_t file_num) +{ + int ret = 0; + const struct firmware *rtp_file; + + ret = request_firmware(&rtp_file, aw_rtp_name[file_num], aw_haptic->dev); + if (ret < 0) { + aw_err("failed to read %s", aw_rtp_name[file_num]); + return ret; + } + + vfree(aw_haptic->aw_rtp); + aw_haptic->aw_rtp = NULL; + aw_haptic->aw_rtp = vmalloc(rtp_file->size + sizeof(int)); + if (!aw_haptic->aw_rtp) { + release_firmware(rtp_file); + aw_err("error allocating memory"); + return -ENOMEM; + } + aw_haptic->aw_rtp->len = rtp_file->size; + aw_haptic->rtp_len = rtp_file->size; + aw_info("rtp file:[%s] size = %dbytes", aw_rtp_name[file_num], aw_haptic->aw_rtp->len); + memcpy(aw_haptic->aw_rtp->data, rtp_file->data, rtp_file->size); + release_firmware(rtp_file); + + return 0; +} + +static int rtp_osc_cali(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + aw_info("enter"); + /*aw_haptic add stop,for irq interrupt during calibrate */ + aw_haptic->func->play_stop(aw_haptic); + + mutex_lock(&aw_haptic->rtp_lock); + ret = rtp_fw_load(aw_haptic, 0); + if (ret < 0) { + mutex_unlock(&aw_haptic->rtp_lock); + return ret; + } + mutex_unlock(&aw_haptic->rtp_lock); + + aw_haptic->rtp_init = false; + aw_haptic->rtp_cnt = 0; + aw_haptic->timeval_flags = 1; + + ram_vbat_comp(aw_haptic, false); + aw_haptic->func->play_mode(aw_haptic, AW_RTP_MODE); + disable_irq(gpio_to_irq(aw_haptic->irq_gpio)); + aw_haptic->func->haptic_start(aw_haptic); + while (1) { + ret = osc_calculation_time(aw_haptic); + if (!ret) + break; + } + enable_irq(gpio_to_irq(aw_haptic->irq_gpio)); + aw_haptic->microsecond = ktime_to_us(ktime_sub(aw_haptic->kend, aw_haptic->kstart)); + /*calibration osc */ + aw_info("microsecond: %llu", aw_haptic->microsecond); + + return 0; +} + +static void rtp_trim_lra_cali(struct aw_haptic *aw_haptic) +{ + uint32_t lra_trim_code = 0; + /*0.1 percent below no need to calibrate */ + uint32_t osc_cali_threshold = 10; + uint32_t code = 0; + unsigned long real_time = 0; + unsigned long theory_time = 0; + + upload_lra(aw_haptic, AW_WRITE_ZERO); + if (rtp_osc_cali(aw_haptic)) + return; + real_time = aw_haptic->microsecond; + theory_time = aw_haptic->func->get_theory_time(aw_haptic); + if (theory_time == real_time) { + aw_info("theory_time == real_time: %ld, no need to calibrate!", real_time); + return; + } else if (theory_time < real_time) { + if ((real_time - theory_time) > (theory_time / AW_OSC_TRIM_PARAM)) { + aw_info("(real_time - theory_time) > (theory_time/50), can't calibrate!"); + return; + } + + if ((real_time - theory_time) < (osc_cali_threshold * theory_time / 10000)) { + aw_info("real_time: %ld, theory_time: %ld, no need to calibrate!", + real_time, theory_time); + return; + } + + code = ((real_time - theory_time) * 4) / (theory_time / 1000); + code = ((code % 10 < 5) ? 0 : 1) + code / 10; + code = aw_haptic->trim_lra_boundary + code; + } else if (theory_time > real_time) { + if ((theory_time - real_time) > (theory_time / AW_OSC_TRIM_PARAM)) { + aw_info("(theory_time - real_time) > (theory_time / 50), can't calibrate!"); + return; + } + if ((theory_time - real_time) < (osc_cali_threshold * theory_time / 10000)) { + aw_info("real_time: %ld, theory_time: %ld, no need to calibrate!", + real_time, theory_time); + return; + } + + code = ((theory_time - real_time) * 4) / (theory_time / 1000); + code = ((code % 10 < 5) ? 0 : 1) + code / 10; + code = aw_haptic->trim_lra_boundary - code; + } + if (code >= aw_haptic->trim_lra_boundary) + lra_trim_code = code - aw_haptic->trim_lra_boundary; + else + lra_trim_code = code + aw_haptic->trim_lra_boundary; + + aw_haptic->osc_cali_data = lra_trim_code; + upload_lra(aw_haptic, AW_OSC_CALI_LRA); + aw_info("real_time: %lu, theory_time: %lu", real_time, theory_time); + aw_info("code: %u, trim_lra: 0x%02X", code, lra_trim_code); +} + +static int parse_awrw_data(struct aw_haptic *aw_haptic, const char *buf) +{ + uint8_t i = 0; + uint8_t reg_num = aw_haptic->i2c_info.reg_num; + const char *temp_buf = NULL; + char data_buf[AWRW_CMD_UNIT] = { 0 }; + uint32_t value = 0; + int ret = 0; + int len = strlen(buf) - (AWRW_CMD_UNIT * 3); + + if (len < 0) { + aw_err("parse data error"); + return -ERANGE; + } + temp_buf = &buf[AWRW_CMD_UNIT * 3]; + for (i = 0; i < reg_num; i++) { + if (((i + 1) * AWRW_CMD_UNIT) > len) { + aw_err("parse data error"); + return -ERANGE; + } + memcpy(data_buf, &temp_buf[i * AWRW_CMD_UNIT], 4); + data_buf[4] = '\0'; + ret = kstrtouint(data_buf, 0, &value); + if (ret < 0) { + aw_err("kstrtouint error"); + return ret; + } + aw_haptic->i2c_info.reg_data[i] = (uint8_t)value; + } + + return 0; +} + +static int haptic_audio_ctr_list_insert(struct aw_haptic *aw_haptic, + struct aw_haptic_ctr *haptic_ctr) +{ + struct aw_haptic_ctr *p_new = NULL; + struct aw_haptic_audio *haptic_audio = &aw_haptic->haptic_audio; + + aw_info("enter"); + p_new = kzalloc(sizeof(struct aw_haptic_ctr), GFP_KERNEL); + if (p_new == NULL) + return -ENOMEM; + + /* update new list info */ + p_new->cnt = haptic_ctr->cnt; + p_new->cmd = haptic_ctr->cmd; + p_new->play = haptic_ctr->play; + p_new->wavseq = haptic_ctr->wavseq; + p_new->loop = haptic_ctr->loop; + p_new->gain = haptic_ctr->gain; + INIT_LIST_HEAD(&(p_new->list)); + list_add(&(p_new->list), &(haptic_audio->ctr_list)); + aw_info("exit"); + + return 0; +} + +static void haptic_audio_ctrl_list_clr(struct aw_haptic_audio *haptic_audio) +{ + struct aw_haptic_ctr *p_ctr = NULL; + struct aw_haptic_ctr *p_ctr_bak = NULL; + + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, &(haptic_audio->ctr_list), list) { + list_del(&p_ctr->list); + kfree(p_ctr); + p_ctr = NULL; + } +} + +static void haptic_audio_init(struct aw_haptic *aw_haptic) +{ + aw_info("enter!"); + aw_haptic->func->set_wav_seq(aw_haptic, 0x01, 0x00); +} + +static void haptic_audio_off(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->set_gain(aw_haptic, 0x80); + aw_haptic->func->play_stop(aw_haptic); + haptic_audio_ctrl_list_clr(&aw_haptic->haptic_audio); + mutex_unlock(&aw_haptic->lock); +} + +static void haptic_ctr_list_config(struct aw_haptic *aw_haptic, struct aw_haptic_ctr *p_ctr, + struct aw_haptic_ctr *p_ctr_bak, + struct aw_haptic_audio *haptic_audio) +{ + uint32_t list_input_cnt = 0; + uint32_t list_output_cnt = 0; + uint32_t list_diff_cnt = 0; + uint32_t list_del_cnt = 0; + + list_for_each_entry_safe(p_ctr, p_ctr_bak, &(haptic_audio->ctr_list), list) { + list_input_cnt = p_ctr->cnt; + break; + } + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, &(haptic_audio->ctr_list), list) { + list_output_cnt = p_ctr->cnt; + break; + } + + if (list_input_cnt > list_output_cnt) + list_diff_cnt = list_input_cnt - list_output_cnt; + + if (list_input_cnt < list_output_cnt) + list_diff_cnt = 32 + list_input_cnt - list_output_cnt; + + if (list_diff_cnt > 2) { + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + if ((p_ctr->play == 0) && (AW_CMD_ENABLE == (AW_CMD_ENABLE & p_ctr->cmd))) { + list_del(&p_ctr->list); + kfree(p_ctr); + list_del_cnt++; + } + if (list_del_cnt == list_diff_cnt) + break; + } + } +} + +static void parse_haptic_audio_data(struct aw_haptic *aw_haptic) +{ + bool ctr_list_flag = false; + struct aw_haptic_ctr *p_ctr = NULL; + struct aw_haptic_ctr *p_ctr_bak = NULL; + struct aw_haptic_audio *haptic_audio = &(aw_haptic->haptic_audio); + + memset(&(haptic_audio->ctr), 0, sizeof(struct aw_haptic_ctr)); + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, &(haptic_audio->ctr_list), list) { + ctr_list_flag = true; + break; + } + + if (ctr_list_flag == false) + aw_dbg("ctr list empty"); + if (ctr_list_flag == true) + haptic_ctr_list_config(aw_haptic, p_ctr, p_ctr_bak, haptic_audio); + + /* get the last data from list */ + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, &(haptic_audio->ctr_list), list) { + haptic_audio->ctr.cnt = p_ctr->cnt; + haptic_audio->ctr.cmd = p_ctr->cmd; + haptic_audio->ctr.play = p_ctr->play; + haptic_audio->ctr.wavseq = p_ctr->wavseq; + haptic_audio->ctr.loop = p_ctr->loop; + haptic_audio->ctr.gain = p_ctr->gain; + list_del(&p_ctr->list); + kfree(p_ctr); + p_ctr = NULL; + break; + } + + if (haptic_audio->ctr.play) { + aw_info("cnt=%u, cmd=%u, play=%u, wavseq=%u, loop=%u, gain=%u", + haptic_audio->ctr.cnt, haptic_audio->ctr.cmd, haptic_audio->ctr.play, + haptic_audio->ctr.wavseq, haptic_audio->ctr.loop, haptic_audio->ctr.gain); + } + +} + +static void haptic_audio_work_routine(struct work_struct *work) +{ + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, haptic_audio.work); + + int rtp_is_going_on = 0; + + aw_dbg("enter"); + + mutex_lock(&aw_haptic->haptic_audio.lock); + parse_haptic_audio_data(aw_haptic); + /* rtp mode jump */ + rtp_is_going_on = aw_haptic->func->judge_rtp_going(aw_haptic); + if (rtp_is_going_on) { + mutex_unlock(&aw_haptic->haptic_audio.lock); + return; + } + mutex_unlock(&aw_haptic->haptic_audio.lock); + +#ifdef AW_DOUBLE + if (aw_haptic->haptic_audio.ctr.play == AW_PLAY_ENABLE) { + mutex_lock(&aw_haptic->lock); + /* haptic config */ + if (aw_haptic->haptic_audio.ctr.cmd & AW_CMD_L_EN) { + left->func->play_stop(left); + left->func->play_mode(left, AW_RAM_MODE); + left->func->set_wav_seq(left, 0x00, aw_haptic->haptic_audio.ctr.wavseq); + left->func->set_wav_seq(left, 0x01, 0x00); + left->func->set_wav_loop(left, 0x00, aw_haptic->haptic_audio.ctr.loop); + left->func->set_gain(left, aw_haptic->haptic_audio.ctr.gain); + } + if (aw_haptic->haptic_audio.ctr.cmd & AW_CMD_R_EN) { + right->func->play_stop(right); + right->func->play_mode(right, AW_RAM_MODE); + right->func->set_wav_seq(right, 0x00, aw_haptic->haptic_audio.ctr.wavseq); + right->func->set_wav_seq(right, 0x01, 0x00); + right->func->set_wav_loop(right, 0x00, aw_haptic->haptic_audio.ctr.loop); + right->func->set_gain(right, aw_haptic->haptic_audio.ctr.gain); + } + /* play go */ + if (aw_haptic->haptic_audio.ctr.cmd & AW_CMD_L_EN) + left->func->haptic_start(left); + if (aw_haptic->haptic_audio.ctr.cmd & AW_CMD_R_EN) + right->func->haptic_start(right); + mutex_unlock(&aw_haptic->lock); + } else if (aw_haptic->haptic_audio.ctr.play == AW_PLAY_STOP) { + mutex_lock(&aw_haptic->lock); + left->func->play_stop(left); + right->func->play_stop(right); + mutex_unlock(&aw_haptic->lock); + } else if (aw_haptic->haptic_audio.ctr.play == AW_PLAY_GAIN) { + mutex_lock(&aw_haptic->lock); + left->func->set_gain(left, aw_haptic->haptic_audio.ctr.gain); + right->func->set_gain(right, aw_haptic->haptic_audio.ctr.gain); + mutex_unlock(&aw_haptic->lock); + } +#else + if ((AW_CMD_HAPTIC & aw_haptic->haptic_audio.ctr.cmd) == AW_CMD_ENABLE) { + if (aw_haptic->haptic_audio.ctr.play == AW_PLAY_ENABLE) { + aw_info("haptic audio play start"); + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + aw_haptic->func->play_mode(aw_haptic, AW_RAM_MODE); + aw_haptic->func->set_wav_seq(aw_haptic, 0x00, + aw_haptic->haptic_audio.ctr.wavseq); + aw_haptic->func->set_wav_loop(aw_haptic, 0x00, + aw_haptic->haptic_audio.ctr.loop); + aw_haptic->func->set_gain(aw_haptic, aw_haptic->haptic_audio.ctr.gain); + aw_haptic->func->haptic_start(aw_haptic); + mutex_unlock(&aw_haptic->lock); + + } else if (aw_haptic->haptic_audio.ctr.play == AW_PLAY_STOP) { + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + mutex_unlock(&aw_haptic->lock); + + } else if (aw_haptic->haptic_audio.ctr.play == AW_PLAY_GAIN) { + mutex_lock(&aw_haptic->lock); + aw_haptic->func->set_gain(aw_haptic, aw_haptic->haptic_audio.ctr.gain); + mutex_unlock(&aw_haptic->lock); + } + } +#endif +} + +static enum hrtimer_restart haptic_audio_timer_func(struct hrtimer *timer) +{ + struct aw_haptic *aw_haptic = container_of(timer, struct aw_haptic, haptic_audio.timer); + int time = aw_haptic->haptic_audio.timer_val; + + queue_work(aw_haptic->work_queue, &aw_haptic->haptic_audio.work); + hrtimer_start(&aw_haptic->haptic_audio.timer, + ktime_set(time / 1000000, (time % 1000000) * 1000), HRTIMER_MODE_REL); + + return HRTIMER_NORESTART; +} + +static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", aw_haptic->state); +} + +static ssize_t state_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + return count; +} + +static ssize_t duration_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ktime_t time_rem; + s64 time_ms = 0; + + if (hrtimer_active(&aw_haptic->timer)) { + time_rem = hrtimer_get_remaining(&aw_haptic->timer); + time_ms = ktime_to_ms(time_rem); + } + + return snprintf(buf, PAGE_SIZE, "%lld\n", time_ms); +} + +static ssize_t duration_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + /* setting 0 on duration is NOP for now */ + if (val <= 0) + return count; + + aw_haptic->duration = val; + aw_info("duration=%d", aw_haptic->duration); + + return count; +} + +static ssize_t activate_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + /* For now nothing to show */ + return snprintf(buf, PAGE_SIZE, "activate = %d\n", aw_haptic->state); +} + +static ssize_t activate_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + int rc = 0; + uint32_t val = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + if (val != 0 && val != 1) + return count; + + aw_info("value=%u", val); + + mutex_lock(&aw_haptic->lock); + hrtimer_cancel(&aw_haptic->timer); + aw_haptic->state = val; + aw_haptic->activate_mode = aw_haptic->info.mode; + mutex_unlock(&aw_haptic->lock); + queue_work(aw_haptic->work_queue, &aw_haptic->vibrator_work); + + return count; +} + +static ssize_t activate_mode_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + return snprintf(buf, PAGE_SIZE, "%u\n", aw_haptic->activate_mode); +} + +static ssize_t activate_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + mutex_lock(&aw_haptic->lock); + aw_haptic->activate_mode = val; + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t index_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + aw_haptic->func->get_wav_seq(aw_haptic, 1); + aw_haptic->index = aw_haptic->seq[0]; + + return snprintf(buf, PAGE_SIZE, "index = %d\n", aw_haptic->index); +} + +static ssize_t index_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + uint32_t val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + + if (val > aw_haptic->ram.ram_num) { + aw_err("input value out of range!"); + return count; + } + + aw_info("value=%u", val); + + mutex_lock(&aw_haptic->lock); + aw_haptic->index = val; + aw_haptic->func->set_repeat_seq(aw_haptic, aw_haptic->index); + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t gain_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint8_t gain = 0; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->get_gain(aw_haptic, &gain); + mutex_unlock(&aw_haptic->lock); + + return snprintf(buf, PAGE_SIZE, "gain = 0x%02X\n", gain); +} + +static ssize_t gain_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + aw_info("value=0x%02X", val); + + mutex_lock(&aw_haptic->lock); + aw_haptic->gain = val; + aw_haptic->func->set_gain(aw_haptic, aw_haptic->gain); + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t seq_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + uint8_t i = 0; + size_t count = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->get_wav_seq(aw_haptic, AW_SEQUENCER_SIZE); + for (i = 0; i < AW_SEQUENCER_SIZE; i++) + count += snprintf(buf + count, PAGE_SIZE - count, "seq%d = %u\n", i + 1, + aw_haptic->seq[i]); + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t seq_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%X %X", &databuf[0], &databuf[1]) == 2) { + if (databuf[0] >= AW_SEQUENCER_SIZE || databuf[1] > aw_haptic->ram.ram_num) { + aw_err("input value out of range!"); + return count; + } + aw_info("seq%u=0x%02X", databuf[0], databuf[1]); + + mutex_lock(&aw_haptic->lock); + aw_haptic->seq[databuf[0]] = (uint8_t)databuf[1]; + aw_haptic->func->set_wav_seq(aw_haptic, (uint8_t)databuf[0], + aw_haptic->seq[databuf[0]]); + mutex_unlock(&aw_haptic->lock); + } + + return count; +} + +static ssize_t loop_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + uint8_t i = 0; + size_t count = 0; + cdev_t *cdev = dev_get_drvdata(dev); + uint8_t reg_val[AW_SEQUENCER_LOOP_SIZE] = {0}; + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->get_wav_loop(aw_haptic, reg_val); + for (i = 0; i < AW_SEQUENCER_LOOP_SIZE; i++) { + aw_haptic->loop[i * 2 + 0] = (reg_val[i] >> 4) & 0x0F; + aw_haptic->loop[i * 2 + 1] = (reg_val[i] >> 0) & 0x0F; + + count += snprintf(buf + count, PAGE_SIZE - count, "seq%d loop: 0x%02X\n", i * 2 + 1, + aw_haptic->loop[i * 2 + 0]); + count += snprintf(buf + count, PAGE_SIZE - count, "seq%d loop: 0x%02X\n", i * 2 + 2, + aw_haptic->loop[i * 2 + 1]); + } + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t loop_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_info("seq%u loop=0x%02X", databuf[0], databuf[1]); + + mutex_lock(&aw_haptic->lock); + aw_haptic->loop[databuf[0]] = (uint8_t)databuf[1]; + aw_haptic->func->set_wav_loop(aw_haptic, (uint8_t)databuf[0], + aw_haptic->loop[databuf[0]]); + mutex_unlock(&aw_haptic->lock); + } + + return count; +} + +static ssize_t reg_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + len = aw_haptic->func->get_reg(aw_haptic, len, buf); + mutex_unlock(&aw_haptic->lock); + + return len; +} + +static ssize_t reg_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + uint8_t val = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%X %X", &databuf[0], &databuf[1]) == 2) { + val = (uint8_t)databuf[1]; + mutex_lock(&aw_haptic->lock); + haptic_nv_i2c_writes(aw_haptic, (uint8_t)databuf[0], &val, AW_I2C_BYTE_ONE); + mutex_unlock(&aw_haptic->lock); + } + + return count; +} + +static ssize_t ram_update_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + int i = 0; + int j = 0; + int size = 0; + ssize_t len = 0; + uint8_t ram_data[AW_RAMDATA_RD_BUFFER_SIZE] = {0}; + uint8_t buffer[AW_RAMDATA_SHOW_LINE_BUFFER_SZIE] = {0}; + uint8_t unit[AW_RAMDATA_SHOW_UINT_SIZE] = {0}; + + mutex_lock(&aw_haptic->lock); + len += snprintf(buf + len, PAGE_SIZE - len, "Please cat log\n"); + /* RAMINIT Enable */ + aw_haptic->func->ram_init(aw_haptic, true); + aw_haptic->func->play_stop(aw_haptic); + aw_haptic->func->set_ram_addr(aw_haptic); + while (i < aw_haptic->ram.len) { + if ((aw_haptic->ram.len - i) < AW_RAMDATA_RD_BUFFER_SIZE) + size = aw_haptic->ram.len - i; + else + size = AW_RAMDATA_RD_BUFFER_SIZE; + + aw_haptic->func->get_ram_data(aw_haptic, ram_data, size); + for (j = 0; j < size; j++) { + sprintf(unit, "0x%02X ", ram_data[j]); + strcat(buffer, unit); + if ((j + 1) % AW_RAMDATA_SHOW_COLUMN == 0 || j == size - 1) { + aw_info("[ram_data]:%s", buffer); + memset(buffer, 0, AW_RAMDATA_SHOW_LINE_BUFFER_SZIE); + } + } + i += size; + } + /* RAMINIT Disable */ + aw_haptic->func->ram_init(aw_haptic, false); + mutex_unlock(&aw_haptic->lock); + + return len; +} + +static ssize_t ram_update_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + if (val) + ram_update(aw_haptic); + + return count; +} + +static ssize_t f0_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + upload_lra(aw_haptic, AW_WRITE_ZERO); + aw_haptic->func->get_f0(aw_haptic); + upload_lra(aw_haptic, AW_F0_CALI_LRA); + mutex_unlock(&aw_haptic->lock); + + len += snprintf(buf + len, PAGE_SIZE - len, "%u\n", aw_haptic->f0); + + return len; +} + +static ssize_t f0_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + return count; +} + + +static ssize_t cali_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + upload_lra(aw_haptic, AW_F0_CALI_LRA); + aw_haptic->func->get_f0(aw_haptic); + mutex_unlock(&aw_haptic->lock); + + len += snprintf(buf + len, PAGE_SIZE - len, "%u\n", aw_haptic->f0); + + return len; +} + +static ssize_t cali_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + if (val) { + mutex_lock(&aw_haptic->lock); + f0_cali(aw_haptic); + mutex_unlock(&aw_haptic->lock); + } + + return count; +} + + +static ssize_t f0_save_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "f0_cali_data = 0x%02X\n", + aw_haptic->f0_cali_data); + + return len; +} + +static ssize_t f0_save_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + uint32_t val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + aw_haptic->f0_cali_data = val; + + return count; +} + +static ssize_t cont_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->read_cont_f0(aw_haptic); + len += snprintf(buf + len, PAGE_SIZE - len, "%u\n", aw_haptic->cont_f0); + mutex_unlock(&aw_haptic->lock); + + return len; +} + +static ssize_t cont_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + if (val) + aw_haptic->func->cont_config(aw_haptic); + mutex_unlock(&aw_haptic->lock); + + return count; +} + + +static ssize_t vbat_monitor_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->get_vbat(aw_haptic); + len += snprintf(buf + len, PAGE_SIZE - len, "vbat_monitor = %u\n", aw_haptic->vbat); + mutex_unlock(&aw_haptic->lock); + + return len; +} + +static ssize_t vbat_monitor_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + + +static ssize_t lra_resistance_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->get_lra_resistance(aw_haptic); + len += snprintf(buf + len, PAGE_SIZE - len, "lra_resistance = %u\n", aw_haptic->lra); + mutex_unlock(&aw_haptic->lock); + + return len; +} + +static ssize_t lra_resistance_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t prctmode_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + uint8_t reg_val = 0; + + mutex_lock(&aw_haptic->lock); + reg_val = aw_haptic->func->get_prctmode(aw_haptic); + len += snprintf(buf + len, PAGE_SIZE - len, "prctmode = %u\n", reg_val); + mutex_unlock(&aw_haptic->lock); + + return len; +} + +static ssize_t prctmode_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t databuf[2] = { 0, 0 }; + uint32_t prtime = 0; + uint32_t prlvl = 0; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + prtime = databuf[0]; + prlvl = databuf[1]; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->protect_config(aw_haptic, prtime, prlvl); + mutex_unlock(&aw_haptic->lock); + } + + return count; +} + +static ssize_t ram_vbat_comp_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "ram_vbat_comp = %u\n", + aw_haptic->ram_vbat_comp); + + return len; +} + +static ssize_t ram_vbat_comp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw_haptic->lock); + if (val) + aw_haptic->ram_vbat_comp = AW_RAM_VBAT_COMP_ENABLE; + else + aw_haptic->ram_vbat_comp = AW_RAM_VBAT_COMP_DISABLE; + mutex_unlock(&aw_haptic->lock); + + return count; +} + + +static ssize_t ram_num_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + + get_ram_num(aw_haptic); + len += snprintf(buf + len, PAGE_SIZE - len, "ram_num = %u\n", aw_haptic->ram.ram_num); + + return len; +} + + +static ssize_t awrw_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint8_t reg_num = aw_haptic->i2c_info.reg_num; + uint8_t flag = aw_haptic->i2c_info.flag; + uint8_t *reg_data = aw_haptic->i2c_info.reg_data; + uint8_t i = 0; + ssize_t len = 0; + + if (!reg_num) { + aw_err("awrw parameter error"); + return len; + } + if (flag == AW_READ) { + for (i = 0; i < reg_num; i++) + len += snprintf(buf + len, PAGE_SIZE - len, "0x%02X,", reg_data[i]); + len += snprintf(buf + len - 1, PAGE_SIZE - len, "\n"); + } + + return len; +} + +static ssize_t awrw_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + uint32_t datatype[3] = { 0 }; + int ret = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + if (sscanf(buf, "%x %x %x", &datatype[0], &datatype[1], &datatype[2]) == 3) { + if (!datatype[1]) { + aw_err("awrw parameter error"); + return count; + } + aw_haptic->i2c_info.flag = (uint8_t)datatype[0]; + aw_haptic->i2c_info.reg_num = (uint8_t)datatype[1]; + aw_haptic->i2c_info.first_addr = (uint8_t)datatype[2]; + + mutex_lock(&aw_haptic->lock); + if (aw_haptic->i2c_info.flag == AW_WRITE) { + ret = parse_awrw_data(aw_haptic, buf); + if (ret < 0) { + mutex_unlock(&aw_haptic->lock); + return count; + } + haptic_nv_i2c_writes(aw_haptic, aw_haptic->i2c_info.first_addr, + aw_haptic->i2c_info.reg_data, + aw_haptic->i2c_info.reg_num); + } else if (aw_haptic->i2c_info.flag == AW_READ) { + haptic_nv_i2c_reads(aw_haptic, aw_haptic->i2c_info.first_addr, + aw_haptic->i2c_info.reg_data, + aw_haptic->i2c_info.reg_num); + } else { + aw_err("flag is error"); + } + mutex_unlock(&aw_haptic->lock); + + } else + aw_err("missing number of parameters"); + + return count; +} + +static ssize_t gun_type_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + return snprintf(buf, PAGE_SIZE, "0x%02X\n", aw_haptic->gun_type); +} + +static ssize_t gun_type_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + aw_info("value=%u", val); + + mutex_lock(&aw_haptic->lock); + aw_haptic->gun_type = val; + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t bullet_nr_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + return snprintf(buf, PAGE_SIZE, "0x%02X\n", aw_haptic->bullet_nr); +} + +static ssize_t bullet_nr_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + aw_info("value=%u", val); + + mutex_lock(&aw_haptic->lock); + aw_haptic->bullet_nr = val; + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t haptic_audio_time_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "haptic_audio.delay_val=%dus\n", + aw_haptic->haptic_audio.delay_val); + len += snprintf(buf + len, PAGE_SIZE - len, "haptic_audio.timer_val=%dus\n", + aw_haptic->haptic_audio.timer_val); + + return len; +} + +static ssize_t haptic_audio_time_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t databuf[2] = { 0 }; + + if (sscanf(buf, "%u %u", &databuf[0], &databuf[1]) == 2) { + aw_haptic->haptic_audio.delay_val = databuf[0]; + aw_haptic->haptic_audio.timer_val = databuf[1]; + } + + return count; +} + +static ssize_t haptic_audio_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, "%u\n", aw_haptic->haptic_audio.ctr.cnt); + + return len; +} + +static int haptic_audio_config(struct aw_haptic *aw_haptic, uint32_t databuf[]) +{ + struct aw_haptic_ctr *hap_ctr = NULL; + int delay = aw_haptic->haptic_audio.delay_val; + + aw_info("enter"); + hap_ctr = kzalloc(sizeof(struct aw_haptic_ctr), GFP_KERNEL); + if (hap_ctr == NULL) + return -ENOMEM; + + mutex_lock(&aw_haptic->haptic_audio.lock); + hap_ctr->cnt = (uint8_t)databuf[0]; + hap_ctr->cmd = (uint8_t)databuf[1]; + hap_ctr->play = (uint8_t)databuf[2]; + hap_ctr->wavseq = (uint8_t)databuf[3]; + hap_ctr->loop = (uint8_t)databuf[4]; + hap_ctr->gain = (uint8_t)databuf[5]; + + haptic_audio_ctr_list_insert(aw_haptic, hap_ctr); + + if (hap_ctr->cmd == 0xff) { + aw_info("haptic_audio stop"); + if (hrtimer_active(&aw_haptic->haptic_audio.timer)) { + aw_info("cancel haptic_audio_timer"); + hrtimer_cancel(&aw_haptic->haptic_audio.timer); + aw_haptic->haptic_audio.ctr.cnt = 0; + haptic_audio_off(aw_haptic); + } + } else { + if (hrtimer_active(&aw_haptic->haptic_audio.timer)) { + /* */ + } else { + aw_info("start haptic_audio_timer"); + haptic_audio_init(aw_haptic); + hrtimer_start(&aw_haptic->haptic_audio.timer, + ktime_set(delay / 1000000, (delay % 1000000) * 1000), + HRTIMER_MODE_REL); + } + } + kfree(hap_ctr); + mutex_unlock(&aw_haptic->haptic_audio.lock); + + return 0; +} + +static ssize_t haptic_audio_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t databuf[6] = { 0 }; + int rtp_is_going_on = 0; + + rtp_is_going_on = aw_haptic->func->judge_rtp_going(aw_haptic); + + if (rtp_is_going_on) { + aw_info("RTP is running, stop audio haptic"); + return count; + } + + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + + if (sscanf(buf, "%u %u %u %u %u %u", &databuf[0], &databuf[1], + &databuf[2], &databuf[3], &databuf[4], &databuf[5]) == 6) { + if (databuf[2]) { + aw_info("cnt=%u, cmd=%u, play=%u", databuf[0], databuf[1], databuf[2]); + aw_info("wavseq=%u, loop=%u, gain=%u", databuf[3], databuf[4], databuf[5]); + haptic_audio_config(aw_haptic, databuf); + } + } + + return count; +} + +#ifdef AW_DOUBLE +static ssize_t dual_cont_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + right->func->play_stop(right); + left->func->play_stop(left); + if (val) { + upload_lra(right, AW_F0_CALI_LRA); + upload_lra(left, AW_F0_CALI_LRA); + right->func->cont_config(right); + left->func->cont_config(left); + } + + return count; +} + +static ssize_t dual_index_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "%d %d\n", left->index, right->index); + + return len; +} + +static ssize_t dual_index_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + int index_l = 0; + int index_r = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + if (sscanf(buf, "%d %d", &index_l, &index_r) == 2) { + aw_info("index_l=%d index_r=%d", index_l, index_r); + if (index_l > aw_haptic->ram.ram_num || index_r > aw_haptic->ram.ram_num) { + aw_err("input value out of range!"); + return count; + } + mutex_lock(&aw_haptic->lock); + left->index = index_l; + left->func->set_repeat_seq(left, left->index); + right->index = index_r; + right->func->set_repeat_seq(right, right->index); + mutex_unlock(&aw_haptic->lock); + } + + return count; +} + +static ssize_t dual_mode_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + return snprintf(buf, PAGE_SIZE, "dual_mode = %u\n", aw_haptic->activate_mode); +} + +static ssize_t dual_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + mutex_lock(&aw_haptic->lock); + left->activate_mode = val; + right->activate_mode = val; + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t dual_duration_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ktime_t time_rem; + s64 time_ms = 0; + + if (hrtimer_active(&aw_haptic->timer)) { + time_rem = hrtimer_get_remaining(&aw_haptic->timer); + time_ms = ktime_to_ms(time_rem); + } + + return snprintf(buf, PAGE_SIZE, "duration = %lldms\n", time_ms); +} + +static ssize_t dual_duration_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int duration_l = 0; + int duration_r = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + if (sscanf(buf, "%d %d", &duration_l, &duration_r) == 2) { + mutex_lock(&aw_haptic->lock); + left->duration = duration_l; + right->duration = duration_r; + mutex_unlock(&aw_haptic->lock); + } + + return count; +} + +static ssize_t dual_activate_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + return snprintf(buf, PAGE_SIZE, "activate = %d\n", aw_haptic->state); +} + +static ssize_t dual_activate_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + aw_info("value=%u", val); + if (!left->ram_init || !right->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + mutex_lock(&aw_haptic->lock); + left->state = val; + right->state = val; + left->dual_flag = true; + right->dual_flag = true; + mutex_unlock(&aw_haptic->lock); + queue_work(left->work_queue, &left->vibrator_work); + queue_work(right->work_queue, &right->vibrator_work); + + return count; +} +#endif + +static DEVICE_ATTR_RW(state); +static DEVICE_ATTR_RW(reg); +static DEVICE_ATTR_RW(duration); +static DEVICE_ATTR_RW(activate); +static DEVICE_ATTR_RW(activate_mode); +static DEVICE_ATTR_RW(index); +static DEVICE_ATTR_RW(gain); +static DEVICE_ATTR_RW(seq); +static DEVICE_ATTR_RW(loop); +static DEVICE_ATTR_RW(ram_update); +static DEVICE_ATTR_RW(f0); +static DEVICE_ATTR_RW(cali); +static DEVICE_ATTR_RW(f0_save); +static DEVICE_ATTR_RW(cont); +static DEVICE_ATTR_RW(vbat_monitor); +static DEVICE_ATTR_RW(lra_resistance); +static DEVICE_ATTR_RW(prctmode); +static DEVICE_ATTR_RW(ram_vbat_comp); +static DEVICE_ATTR_RO(ram_num); +static DEVICE_ATTR_RW(awrw); +static DEVICE_ATTR_RW(gun_type); +static DEVICE_ATTR_RW(bullet_nr); +static DEVICE_ATTR_RW(haptic_audio_time); +static DEVICE_ATTR_RW(haptic_audio); +#ifdef AW_DOUBLE +static DEVICE_ATTR_WO(dual_cont); +static DEVICE_ATTR_RW(dual_index); +static DEVICE_ATTR_RW(dual_mode); +static DEVICE_ATTR_RW(dual_duration); +static DEVICE_ATTR_RW(dual_activate); +#endif + +static struct attribute *vibrator_attributes[] = { + &dev_attr_state.attr, + &dev_attr_duration.attr, + &dev_attr_activate.attr, + &dev_attr_activate_mode.attr, + &dev_attr_index.attr, + &dev_attr_gain.attr, + &dev_attr_seq.attr, + &dev_attr_loop.attr, + &dev_attr_reg.attr, + &dev_attr_ram_update.attr, + &dev_attr_f0.attr, + &dev_attr_cali.attr, + &dev_attr_f0_save.attr, + &dev_attr_cont.attr, + &dev_attr_vbat_monitor.attr, + &dev_attr_lra_resistance.attr, + &dev_attr_prctmode.attr, + &dev_attr_ram_vbat_comp.attr, + &dev_attr_ram_num.attr, + &dev_attr_awrw.attr, + &dev_attr_gun_type.attr, + &dev_attr_bullet_nr.attr, + &dev_attr_haptic_audio_time.attr, + &dev_attr_haptic_audio.attr, +#ifdef AW_DOUBLE + &dev_attr_dual_cont.attr, + &dev_attr_dual_index.attr, + &dev_attr_dual_mode.attr, + &dev_attr_dual_duration.attr, + &dev_attr_dual_activate.attr, +#endif + NULL +}; + +static struct attribute_group vibrator_attribute_group = { + .attrs = vibrator_attributes +}; + +static ssize_t rtp_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "rtp_cnt = %u", aw_haptic->rtp_cnt); + + return len; +} + +static ssize_t rtp_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) { + aw_err("kstrtouint fail"); + return count; + } + + if (!aw_haptic->ram.base_addr) { + aw_err("no set FIFO space for rtp play!"); + return count; + } + + mutex_lock(&aw_haptic->lock); + if ((val > 0) && (val < aw_haptic->rtp_num_max)) { + aw_haptic->state = 1; + aw_haptic->rtp_file_num = val; + } else if (val == 0) { + aw_haptic->state = 0; + } else { + aw_haptic->state = 0; + aw_err("input number error:%u", val); + } + mutex_unlock(&aw_haptic->lock); + queue_work(aw_haptic->work_queue, &aw_haptic->rtp_work); + + return count; +} + +static ssize_t osc_cali_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "osc_cali_data = 0x%02X\n", + aw_haptic->osc_cali_data); + + return len; +} + +static ssize_t osc_cali_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw_haptic->lock); + if (val == 1) { + rtp_trim_lra_cali(aw_haptic); + } else if (val == 2) { + upload_lra(aw_haptic, AW_OSC_CALI_LRA); + rtp_osc_cali(aw_haptic); + } + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t osc_save_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "osc_cali_data = 0x%02X\n", + aw_haptic->osc_cali_data); + + return len; +} + +static ssize_t osc_save_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + uint32_t val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return count; + aw_haptic->osc_cali_data = val; + + return count; +} + +#ifdef AW_DOUBLE +static ssize_t dual_rtp_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "left_rtp_cnt = %u\n", left->rtp_cnt); + len += snprintf(buf + len, PAGE_SIZE - len, "right_rtp_cnt = %u\n", right->rtp_cnt); + + return len; +} + +static ssize_t dual_rtp_store(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + int rtp_l = 0; + int rtp_r = 0; + + mutex_lock(&aw_haptic->lock); + if (sscanf(buf, "%d %d", &rtp_l, &rtp_r) == 2) { + if (rtp_l > 0 && rtp_l < aw_haptic->rtp_num_max) { + left->state = 1; + left->rtp_file_num = rtp_l; + } else if (rtp_l == 0) { + left->state = 0; + } else { + left->state = 0; + aw_err("input number error:%d", rtp_l); + } + if (rtp_r > 0 && rtp_r < aw_haptic->rtp_num_max) { + right->state = 1; + right->rtp_file_num = rtp_r; + } else if (rtp_r == 0) { + right->state = 0; + } else { + right->state = 0; + aw_err("input number error:%d", rtp_r); + } + } + left->dual_flag = true; + right->dual_flag = true; + mutex_unlock(&aw_haptic->lock); + queue_work(left->work_queue, &left->rtp_work); + queue_work(right->work_queue, &right->rtp_work); + + return count; +} +#endif + +static DEVICE_ATTR_RW(rtp); +static DEVICE_ATTR_RW(osc_cali); +static DEVICE_ATTR_RW(osc_save); +#ifdef AW_DOUBLE +static DEVICE_ATTR_RW(dual_rtp); +#endif + +static struct attribute *rtp_attributes[] = { + &dev_attr_rtp.attr, + &dev_attr_osc_cali.attr, + &dev_attr_osc_save.attr, +#ifdef AW_DOUBLE + &dev_attr_dual_rtp.attr, +#endif + NULL +}; + +static struct attribute_group rtp_attribute_group = { + .attrs = rtp_attributes +}; + +#ifdef AW_TIKTAP +static inline unsigned int tiktap_get_sys_msecs(void) +{ +#if (KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE) + struct timespec64 ts64; + + ktime_get_coarse_real_ts64(&ts64); +#else + struct timespec64 ts64 = current_kernel_time64(); +#endif + + return jiffies_to_msecs(timespec64_to_jiffies(&ts64)); +} + +static void tiktap_work_routine(struct work_struct *work) +{ + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, tiktap_work); + struct mmap_buf_format *tiktap_buf = aw_haptic->start_buf; + int count = 100; + unsigned char reg_val = 0x10; + unsigned char glb_state_val = 0; + unsigned int write_start; + unsigned int buf_cnt = 0; + + mutex_lock(&aw_haptic->lock); + aw_haptic->tiktap_stop_flag = false; + aw_haptic->func->play_mode(aw_haptic, AW_RTP_MODE); + upload_lra(aw_haptic, AW_OSC_CALI_LRA); + while (true && count--) { + if (tiktap_buf->status == MMAP_BUF_DATA_VALID) { + aw_haptic->func->haptic_start(aw_haptic); + mdelay(1); + break; + } else if (aw_haptic->tiktap_stop_flag == true) { + mutex_unlock(&aw_haptic->lock); + return; + } + mdelay(1); + } + if (count <= 0) { + aw_err("wait 100 ms but start_buf->status != VALID! status = 0x%02x", + tiktap_buf->status); + aw_haptic->tiktap_stop_flag = true; + mutex_unlock(&aw_haptic->lock); + return; + } + aw_haptic->tiktap_ready = true; + mutex_unlock(&aw_haptic->lock); + + mutex_lock(&aw_haptic->rtp_lock); + pm_qos_enable(aw_haptic, true); + write_start = tiktap_get_sys_msecs(); + while (true) { + if (tiktap_get_sys_msecs() > (write_start + 800)) { + aw_err("Failed! tiktap endless loop"); + break; + } + reg_val = aw_haptic->func->rtp_get_fifo_aes(aw_haptic); + glb_state_val = aw_haptic->func->get_glb_state(aw_haptic); + if ((glb_state_val & AW_BIT_GLBRD_STATE_MASK) != AW_BIT_STATE_RTP_GO) { + aw_err("tiktap glb_state != RTP_GO!, glb_state = 0x%02x", glb_state_val); + break; + } + if ((aw_haptic->tiktap_stop_flag == true) || + (tiktap_buf->status == MMAP_BUF_DATA_FINISHED) || + (tiktap_buf->status == MMAP_BUF_DATA_INVALID)) { + aw_info("tiktap exit! tiktap_buf->status = 0x%02x", tiktap_buf->status); + break; + } else if ((tiktap_buf->status == MMAP_BUF_DATA_VALID) && (reg_val & 0x01)) { + aw_info("buf_cnt = %u, bit = %u, length = %d!", + buf_cnt, tiktap_buf->bit, tiktap_buf->length); + + aw_haptic->func->set_rtp_data(aw_haptic, tiktap_buf->data, tiktap_buf->length); + tiktap_buf->status = MMAP_BUF_DATA_FINISHED; + + tiktap_buf = tiktap_buf->kernel_next; + write_start = tiktap_get_sys_msecs(); + buf_cnt++; + } else { + mdelay(1); + } + } + pm_qos_enable(aw_haptic, false); + aw_haptic->tiktap_stop_flag = true; + mutex_unlock(&aw_haptic->rtp_lock); +} + +static void tiktap_clean_buf(struct aw_haptic *aw_haptic, int status) +{ + struct mmap_buf_format *tiktap_buf = aw_haptic->start_buf; + int i = 0; + + for (i = 0; i < AW_TIKTAP_MMAP_BUF_SUM; i++) { + tiktap_buf->status = status; + tiktap_buf = tiktap_buf->kernel_next; + } +} + +static long tiktap_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + unsigned int tmp = 0; + int ret = 0; + struct aw_haptic *aw_haptic = (struct aw_haptic *)file->private_data; + + switch (cmd) { + case TIKTAP_GET_HWINFO: + aw_info("cmd = TIKTAP_GET_HWINFO!"); + tmp = aw_haptic->name; + if (copy_to_user((void __user *)arg, &tmp, sizeof(unsigned int))) + ret = -EFAULT; + break; + case TIKTAP_GET_F0: + aw_info("cmd = TIKTAP_GET_F0!"); + tmp = aw_haptic->f0; + if (copy_to_user((void __user *)arg, &tmp, sizeof(unsigned int))) + ret = -EFAULT; + break; + case TIKTAP_STOP_MODE: + aw_info("cmd = TIKTAP_STOP_MODE!"); + tiktap_clean_buf(aw_haptic, MMAP_BUF_DATA_INVALID); + aw_haptic->tiktap_stop_flag = true; + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + mutex_unlock(&aw_haptic->lock); + break; + case TIKTAP_RTP_MODE: + /* aw_info("cmd = TIKTAP_RTP_MODE!"); */ + tiktap_clean_buf(aw_haptic, MMAP_BUF_DATA_INVALID); + aw_haptic->tiktap_stop_flag = true; + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + mutex_unlock(&aw_haptic->lock); + queue_work(aw_haptic->work_queue, &aw_haptic->tiktap_work); + break; + case TIKTAP_SETTING_GAIN: + aw_info("cmd = TIKTAP_SETTING_GAIN!"); + if (arg > 0x80) + arg = 0x80; + mutex_lock(&aw_haptic->lock); + aw_haptic->func->set_gain(aw_haptic, (uint8_t)arg); + mutex_unlock(&aw_haptic->lock); + break; + default: + aw_info("unknown cmd = %u", cmd); + break; + } + + return ret; +} + +#ifdef CONFIG_COMPAT +static long tiktap_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + ret = tiktap_unlocked_ioctl(file, cmd, arg); + + return ret; +} +#endif + +static int tiktap_file_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned long phys; + struct aw_haptic *aw_haptic = (struct aw_haptic *)file->private_data; + int ret = 0; + +#if KERNEL_VERSION(4, 7, 0) < LINUX_VERSION_CODE + vm_flags_t vm_flags = calc_vm_prot_bits(PROT_READ|PROT_WRITE, 0) | + calc_vm_flag_bits(MAP_SHARED); + + vm_flags |= current->mm->def_flags | VM_MAYREAD | VM_MAYWRITE | + VM_MAYEXEC | VM_SHARED | VM_MAYSHARE; + + if (!vma || (pgprot_val(vma->vm_page_prot) != pgprot_val(vm_get_page_prot(vm_flags)))) { + aw_err("vm_page_prot error!"); + return -EPERM; + } + + if (!vma || ((vma->vm_end - vma->vm_start) != (PAGE_SIZE << AW_TIKTAP_MMAP_PAGE_ORDER))) { + aw_err("mmap size check err!"); + return -EPERM; + } +#endif + phys = virt_to_phys(aw_haptic->start_buf); + + ret = remap_pfn_range(vma, vma->vm_start, (phys >> PAGE_SHIFT), + (vma->vm_end - vma->vm_start), vma->vm_page_prot); + if (ret) { + aw_err("mmap failed!"); + return ret; + } + + aw_info("success!"); + + return ret; +} + +static int tiktap_file_open(struct inode *inode, struct file *file) +{ +#ifdef AW_DOUBLE + file->private_data = (void *)left; +#else + file->private_data = (void *)g_aw_haptic; +#endif + return 0; +} + +#ifdef AW_DOUBLE +static int tiktap_file_open_r(struct inode *inode, struct file *file) +{ + file->private_data = (void *)right; + return 0; +} +#endif + +#ifdef KERNEL_OVER_5_10 +static const struct proc_ops tiktap_proc_ops = { + .proc_mmap = tiktap_file_mmap, + .proc_open = tiktap_file_open, + .proc_ioctl = tiktap_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .proc_compat_ioctl = tiktap_compat_ioctl, +#endif +}; +#ifdef AW_DOUBLE +static const struct proc_ops tiktap_proc_ops_r = { + .proc_mmap = tiktap_file_mmap, + .proc_open = tiktap_file_open_r, + .proc_ioctl = tiktap_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .proc_compat_ioctl = tiktap_compat_ioctl, +#endif +}; +#endif +#else +static const struct file_operations tiktap_proc_ops = { + .owner = THIS_MODULE, + .mmap = tiktap_file_mmap, + .open = tiktap_file_open, + .unlocked_ioctl = tiktap_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = tiktap_compat_ioctl, +#endif +}; +#ifdef AW_DOUBLE +static const struct file_operations tiktap_proc_ops_r = { + .owner = THIS_MODULE, + .mmap = tiktap_file_mmap, + .open = tiktap_file_open_r, + .unlocked_ioctl = tiktap_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = tiktap_compat_ioctl, +#endif +}; +#endif +#endif + +static int tiktap_init(struct aw_haptic *aw_haptic) +{ + struct mmap_buf_format *tiktap_start_buf; + struct proc_dir_entry *tiktap_config_proc = NULL; + + /* Create proc file node */ +#ifdef AW_DOUBLE + if (of_device_is_compatible(aw_haptic->i2c->dev.of_node, "awinic,haptic_nv_l")) + tiktap_config_proc = proc_create(AW_TIKTAP_PROCNAME, 0664, NULL, &tiktap_proc_ops); + + if (of_device_is_compatible(aw_haptic->i2c->dev.of_node, "awinic,haptic_nv_r")) + tiktap_config_proc = proc_create(AW_TIKTAP_PROCNAME_R, 0664, NULL, &tiktap_proc_ops_r); +#else + tiktap_config_proc = proc_create(AW_TIKTAP_PROCNAME, 0664, NULL, &tiktap_proc_ops); +#endif + if (tiktap_config_proc == NULL) { + aw_err("create proc file failed!"); + return -EPERM; + } + aw_info("create proc file success!"); + + /* Construct shared memory */ + tiktap_start_buf = (struct mmap_buf_format *)__get_free_pages(GFP_KERNEL, AW_TIKTAP_MMAP_PAGE_ORDER); + if (tiktap_start_buf == NULL) { + aw_err("Error __get_free_pages failed"); + return -ENOMEM; + } + SetPageReserved(virt_to_page(tiktap_start_buf)); + { + struct mmap_buf_format *temp; + unsigned int i = 0; + + temp = tiktap_start_buf; + for (i = 1; i < AW_TIKTAP_MMAP_BUF_SUM; i++) { + temp->kernel_next = (tiktap_start_buf + i); + temp = temp->kernel_next; + } + temp->kernel_next = tiktap_start_buf; + + temp = tiktap_start_buf; + for (i = 0; i < AW_TIKTAP_MMAP_BUF_SUM; i++) { + temp->bit = i; + temp = temp->kernel_next; + } + } + + aw_haptic->aw_config_proc = tiktap_config_proc; + aw_haptic->start_buf = tiktap_start_buf; + /* init flag and work */ + aw_haptic->tiktap_stop_flag = true; + aw_haptic->tiktap_ready = false; + INIT_WORK(&aw_haptic->tiktap_work, tiktap_work_routine); + + return 0; +} +#endif + +static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) +{ + struct aw_haptic *aw_haptic = container_of(timer, struct aw_haptic, timer); + + aw_info("enter"); + aw_haptic->state = 0; + queue_work(aw_haptic->work_queue, &aw_haptic->vibrator_work); + + return HRTIMER_NORESTART; +} + +static void vibrator_work_routine(struct work_struct *work) +{ + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, vibrator_work); + + aw_info("enter"); + + mutex_lock(&aw_haptic->lock); + /* Enter standby mode */ + aw_haptic->func->play_stop(aw_haptic); + if (aw_haptic->state) { +#ifdef AW_DURATION_DECIDE_WAVEFORM + if (ram_config(aw_haptic, aw_haptic->duration) < 0) { + mutex_unlock(&aw_haptic->lock); + return; + } +#endif + upload_lra(aw_haptic, AW_F0_CALI_LRA); + if (aw_haptic->activate_mode == AW_RAM_MODE) { + ram_vbat_comp(aw_haptic, false); + ram_play(aw_haptic); + } else if (aw_haptic->activate_mode == AW_RAM_LOOP_MODE) { + ram_vbat_comp(aw_haptic, true); + ram_play(aw_haptic); + /* run ms timer */ + hrtimer_start(&aw_haptic->timer, ktime_set(aw_haptic->duration / 1000, + (aw_haptic->duration % 1000) * 1000000), HRTIMER_MODE_REL); + } else if (aw_haptic->activate_mode == AW_CONT_MODE) { + aw_haptic->func->cont_config(aw_haptic); + /* run ms timer */ + hrtimer_start(&aw_haptic->timer, ktime_set(aw_haptic->duration / 1000, + (aw_haptic->duration % 1000) * 1000000), HRTIMER_MODE_REL); + } else { + aw_err("activate_mode error"); + } + } + mutex_unlock(&aw_haptic->lock); +} + +static int wait_enter_rtp_mode(struct aw_haptic *aw_haptic, int cnt) +{ + bool rtp_work_flag = false; + uint8_t ret = 0; + + while (cnt) { + ret = aw_haptic->func->judge_rtp_going(aw_haptic); + if (ret) { + rtp_work_flag = true; + aw_info("RTP_GO!"); + break; + } + cnt--; + aw_info("wait for RTP_GO, glb_state=0x%02X", ret); + usleep_range(AW_RTP_DELAY_MIN, AW_RTP_DELAY_MAX); + } + if (!rtp_work_flag) { + aw_haptic->func->play_stop(aw_haptic); + aw_err("failed to enter RTP_GO status!"); + return -ERANGE; + } + + return 0; +} + +static void rtp_work_routine(struct work_struct *work) +{ + int ret = 0; + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, rtp_work); + + aw_info("enter"); + + mutex_lock(&aw_haptic->lock); + if (hrtimer_active(&aw_haptic->timer)) + hrtimer_cancel(&aw_haptic->timer); + aw_haptic->func->play_stop(aw_haptic); + aw_haptic->func->set_rtp_aei(aw_haptic, false); + aw_haptic->func->irq_clear(aw_haptic); + if (!aw_haptic->state) { + mutex_unlock(&aw_haptic->lock); + return; + } + mutex_unlock(&aw_haptic->lock); + + /* fw loaded */ + mutex_lock(&aw_haptic->rtp_lock); + ret = rtp_fw_load(aw_haptic, aw_haptic->rtp_file_num); + if (ret < 0) { + mutex_unlock(&aw_haptic->rtp_lock); + return; + } + mutex_unlock(&aw_haptic->rtp_lock); + + /* rtp config */ + mutex_lock(&aw_haptic->lock); + aw_haptic->rtp_init = true; + upload_lra(aw_haptic, AW_OSC_CALI_LRA); + aw_haptic->func->play_mode(aw_haptic, AW_RTP_MODE); + aw_haptic->func->haptic_start(aw_haptic); + usleep_range(AW_RTP_DELAY_MIN, AW_RTP_DELAY_MAX); + ret = wait_enter_rtp_mode(aw_haptic, 200); + if (ret < 0) { + mutex_unlock(&aw_haptic->lock); + return; + } + mutex_unlock(&aw_haptic->lock); + + mutex_lock(&aw_haptic->rtp_lock); + pm_qos_enable(aw_haptic, true); + aw_haptic->rtp_cnt = 0; + rtp_play(aw_haptic); + if (aw_haptic->play_mode == AW_RTP_MODE) + aw_haptic->func->set_rtp_aei(aw_haptic, true); + pm_qos_enable(aw_haptic, false); + mutex_unlock(&aw_haptic->rtp_lock); +} + +#ifdef TIMED_OUTPUT +static int vibrator_get_time(struct timed_output_dev *dev) +{ + struct aw_haptic *aw_haptic = container_of(dev, struct aw_haptic, vib_dev); + + if (hrtimer_active(&aw_haptic->timer)) { + ktime_t r = hrtimer_get_remaining(&aw_haptic->timer); + + return ktime_to_ms(r); + } + + return 0; +} + +static void vibrator_enable(struct timed_output_dev *dev, int value) +{ + struct aw_haptic *aw_haptic = container_of(dev, struct aw_haptic, vib_dev); + + aw_info("enter"); + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return; + } + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + if (value > 0) { + upload_lra(aw_haptic, AW_F0_CALI_LRA); + ram_vbat_comp(aw_haptic, false); + aw_haptic->func->play_mode(aw_haptic, AW_RAM_MODE); + aw_haptic->func->haptic_start(aw_haptic); + } + mutex_unlock(&aw_haptic->lock); + + aw_info("exit"); +} +#else +static enum led_brightness brightness_get(struct led_classdev *cdev) +{ + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + return aw_haptic->amplitude; +} + +static void brightness_set(struct led_classdev *cdev, enum led_brightness level) +{ + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + aw_info("enter"); + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return; + } + aw_haptic->amplitude = level; + + mutex_lock(&aw_haptic->lock); + aw_haptic->state = aw_haptic->amplitude; + aw_haptic->activate_mode = AW_RAM_MODE; + mutex_unlock(&aw_haptic->lock); + queue_work(aw_haptic->work_queue, &aw_haptic->vibrator_work); +} +#endif + +static int vibrator_init(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + aw_info("enter"); + +#ifdef TIMED_OUTPUT + aw_info("TIMED_OUT FRAMEWORK!"); +#ifdef AW_DOUBLE + ret = memcmp(aw_haptic->mark, "left", sizeof("left")); + if (!ret) + aw_haptic->vib_dev.name = "vibrator_l"; + ret = memcmp(aw_haptic->mark, "right", sizeof("right")); + if (!ret) + aw_haptic->vib_dev.name = "vibrator_r"; +#else + aw_haptic->vib_dev.name = "vibrator"; +#endif + aw_haptic->vib_dev.get_time = vibrator_get_time; + aw_haptic->vib_dev.enable = vibrator_enable; + + ret = timed_output_dev_register(&(aw_haptic->vib_dev)); + if (ret < 0) { + aw_err("fail to create timed output dev"); + return ret; + } +#else + aw_info("loaded in leds_cdev framework!"); +#ifdef AW_DOUBLE + ret = memcmp(aw_haptic->mark, "left", sizeof("left")); + if (!ret) + aw_haptic->vib_dev.name = "vibrator_l"; + ret = memcmp(aw_haptic->mark, "right", sizeof("right")); + if (!ret) + aw_haptic->vib_dev.name = "vibrator_r"; +#else + aw_haptic->vib_dev.name = "vibrator"; +#endif + aw_haptic->vib_dev.brightness_get = brightness_get; + aw_haptic->vib_dev.brightness_set = brightness_set; + ret = devm_led_classdev_register(&aw_haptic->i2c->dev, &aw_haptic->vib_dev); + if (ret < 0) { + aw_err("fail to create led dev"); + return ret; + } +#endif + + ret = sysfs_create_group(&aw_haptic->vib_dev.dev->kobj, &vibrator_attribute_group); + if (ret < 0) { + aw_err("error creating sysfs common attr files"); + return ret; + } + /* + * Android has a habit of trying to set the vibrator trigger to "transient", + * which destroys our own "activate", "duration", and "state" attributes. + * Remove the "trigger" attribute provided by leds_class so that no one can + * change the trigger of the vibrator LED device. + * + * Luckily, `sysfs_remove_file` only uses the `name` field, so we can use a + * compound literal instead of having to find the proper attribute struct + */ + sysfs_remove_file(&aw_haptic->vib_dev.dev->kobj, + &((struct attribute){.name = "trigger"})); + + if (aw_haptic->is_used_irq_pin) { + ret = sysfs_create_group(&aw_haptic->vib_dev.dev->kobj, &rtp_attribute_group); + if (ret < 0) { + aw_err("error creating sysfs rtp attr files"); + return ret; + } + } + hrtimer_init(&aw_haptic->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + aw_haptic->timer.function = vibrator_timer_func; + INIT_WORK(&aw_haptic->vibrator_work, vibrator_work_routine); + INIT_WORK(&aw_haptic->rtp_work, rtp_work_routine); + mutex_init(&aw_haptic->lock); + mutex_init(&aw_haptic->rtp_lock); + sema_init(&aw_haptic->sema, 1); + + return 0; +} + +static void haptic_init(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + aw_haptic->haptic_audio.delay_val = 1; + aw_haptic->haptic_audio.timer_val = 21318; + INIT_LIST_HEAD(&(aw_haptic->haptic_audio.ctr_list)); + hrtimer_init(&aw_haptic->haptic_audio.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + aw_haptic->haptic_audio.timer.function = haptic_audio_timer_func; + INIT_WORK(&aw_haptic->haptic_audio.work, haptic_audio_work_routine); + mutex_init(&aw_haptic->haptic_audio.lock); + + mutex_lock(&aw_haptic->lock); + aw_haptic->rtp_num_max = sizeof(aw_rtp_name) / AW_NAME_MAX; + aw_haptic->activate_mode = aw_haptic->info.mode; + aw_haptic->f0_pre = aw_haptic->info.f0_pre; + aw_haptic->gun_type = AW_GUN_TYPE_DEF_VAL; + aw_haptic->bullet_nr = AW_BULLET_NR_DEF_VAL; + aw_haptic->func->play_mode(aw_haptic, AW_STANDBY_MODE); + aw_haptic->func->misc_para_init(aw_haptic); + aw_haptic->func->vbat_mode_config(aw_haptic, AW_CONT_VBAT_HW_COMP_MODE); + aw_haptic->ram_vbat_comp = AW_RAM_VBAT_COMP_ENABLE; + + if (aw_haptic->info.lk_f0_cali) { + aw_haptic->f0_cali_data = aw_haptic->info.lk_f0_cali; + upload_lra(aw_haptic, AW_F0_CALI_LRA); + } else { + f0_cali(aw_haptic); + } + mutex_unlock(&aw_haptic->lock); + + aw_info("exit"); +} + +static void ram_work_routine(struct work_struct *work) +{ + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, ram_work.work); + + ram_update(aw_haptic); +} + +static void ram_work_init(struct aw_haptic *aw_haptic) +{ + int ram_timer_val = AW_RAM_WORK_DELAY_INTERVAL; + + INIT_DELAYED_WORK(&aw_haptic->ram_work, ram_work_routine); + schedule_delayed_work(&aw_haptic->ram_work, msecs_to_jiffies(ram_timer_val)); +} + +#ifdef AW_INPUT_FRAMEWORK +static void input_stop_work_routine(struct work_struct *work) +{ + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, stop_work); + + mutex_lock(&aw_haptic->lock); + hrtimer_cancel(&aw_haptic->timer); + aw_haptic->func->play_stop(aw_haptic); + mutex_unlock(&aw_haptic->lock); +} + +static void input_gain_work_routine(struct work_struct *work) +{ + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, input_gain_work); + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->set_gain(aw_haptic, aw_haptic->gain); + mutex_unlock(&aw_haptic->lock); +} + +static void input_vib_work_routine(struct work_struct *work) +{ + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, input_vib_work); + + mutex_lock(&aw_haptic->lock); + /* Enter standby mode */ + hrtimer_cancel(&aw_haptic->timer); + aw_haptic->func->play_stop(aw_haptic); + if (aw_haptic->state) { + upload_lra(aw_haptic, AW_F0_CALI_LRA); + if (aw_haptic->activate_mode == AW_RAM_MODE) { + ram_vbat_comp(aw_haptic, false); + aw_haptic->func->set_wav_seq(aw_haptic, 0x00, aw_haptic->index); + aw_haptic->func->set_wav_seq(aw_haptic, 0x01, 0x00); + aw_haptic->func->set_wav_loop(aw_haptic, 0x00, 0x00); + ram_play(aw_haptic); + } else if (aw_haptic->activate_mode == AW_RAM_LOOP_MODE) { + ram_vbat_comp(aw_haptic, true); + aw_haptic->func->set_repeat_seq(aw_haptic, aw_haptic->index); + ram_play(aw_haptic); + /* run ms timer */ + hrtimer_start(&aw_haptic->timer, ktime_set(aw_haptic->duration / 1000, + (aw_haptic->duration % 1000) * 1000000), HRTIMER_MODE_REL); + } else { + aw_err("activate_mode error"); + } + } + mutex_unlock(&aw_haptic->lock); +} + +static int input_upload_effect(struct input_dev *dev, struct ff_effect *effect, + struct ff_effect *old) +{ + struct aw_haptic *aw_haptic = input_get_drvdata(dev); + short wav_id = 0; + int wav_id_max = 0; + int ret = 0; + + mutex_lock(&aw_haptic->lock); + switch (effect->type) { + case FF_CONSTANT: + aw_haptic->activate_mode = AW_RAM_LOOP_MODE; + aw_haptic->duration = effect->replay.length; + aw_haptic->index = aw_haptic->ram.ram_num; + aw_info("waveform id = %d", aw_haptic->index); + break; + case FF_PERIODIC: + ret = copy_from_user(&wav_id, effect->u.periodic.custom_data, sizeof(short)); + if (ret) { + aw_err("copy from user error %d!!", ret); + mutex_unlock(&aw_haptic->lock); + return -ERANGE; + } + aw_info("waveform id = %d", wav_id); + wav_id_max = sizeof(aw_rtp_name) / sizeof(*aw_rtp_name) + + aw_haptic->ram.ram_num - 1; + if (wav_id > 0 && wav_id < aw_haptic->ram.ram_num) { + aw_haptic->activate_mode = AW_RAM_MODE; + aw_haptic->index = wav_id; + } else if (wav_id > aw_haptic->ram.ram_num && wav_id <= wav_id_max) { + aw_haptic->activate_mode = AW_RTP_MODE; + aw_haptic->rtp_file_num = wav_id - aw_haptic->ram.ram_num; + } else { + aw_haptic->activate_mode = AW_STANDBY_MODE; + aw_err("waveform id is error"); + mutex_unlock(&aw_haptic->lock); + return -ERANGE; + } + break; + default: + aw_err("Unsupported effect type: %u", effect->type); + break; + } + mutex_unlock(&aw_haptic->lock); + + return 0; +} + +static int input_playback(struct input_dev *dev, int effect_id, int val) +{ + struct aw_haptic *aw_haptic = input_get_drvdata(dev); + + if (val > 0) { + aw_haptic->state = 1; + } else { + queue_work(aw_haptic->work_queue, &aw_haptic->stop_work); + return 0; + } + + switch (aw_haptic->activate_mode) { + case AW_RAM_MODE: + queue_work(aw_haptic->work_queue, &aw_haptic->input_vib_work); + break; + case AW_RAM_LOOP_MODE: + queue_work(aw_haptic->work_queue, &aw_haptic->input_vib_work); + break; + case AW_RTP_MODE: + queue_work(aw_haptic->work_queue, &aw_haptic->rtp_work); + break; + default: + aw_err("Unsupported mode: %d", aw_haptic->activate_mode); + break; + } + + return 0; +} + +static int input_erase(struct input_dev *dev, int effect_id) +{ + struct aw_haptic *aw_haptic = input_get_drvdata(dev); + + aw_haptic->duration = 0; + + return 0; +} + +static void input_set_gain(struct input_dev *dev, uint16_t gain) +{ + struct aw_haptic *aw_haptic = input_get_drvdata(dev); + + if (gain > 0x7fff) + gain = 0x7fff; + aw_haptic->gain = gain * 0x80 / 0x7fff; + queue_work(aw_haptic->work_queue, &aw_haptic->input_gain_work); + aw_info("aw_haptic->gain = 0x%02x", aw_haptic->gain); +} + +static int input_framework_init(struct aw_haptic *aw_haptic) +{ + struct input_dev *input_dev; + int ret = 0; + + input_dev = devm_input_allocate_device(aw_haptic->dev); + if (input_dev == NULL) + return -ENOMEM; + input_dev->name = "aw-haptic-nv"; + input_set_drvdata(input_dev, aw_haptic); + aw_haptic->input_dev = input_dev; + input_set_capability(input_dev, EV_FF, FF_GAIN); + input_set_capability(input_dev, EV_FF, FF_CONSTANT); + input_set_capability(input_dev, EV_FF, FF_PERIODIC); + input_set_capability(input_dev, EV_FF, FF_CUSTOM); + ret = input_ff_create(input_dev, AW_EFFECT_NUMBER); + if (ret < 0) { + aw_err("create input FF device failed, rc=%d\n", ret); + return ret; + } + input_dev->ff->upload = input_upload_effect; + input_dev->ff->playback = input_playback; + input_dev->ff->erase = input_erase; + input_dev->ff->set_gain = input_set_gain; + INIT_WORK(&aw_haptic->input_gain_work, input_gain_work_routine); + INIT_WORK(&aw_haptic->stop_work, input_stop_work_routine); + INIT_WORK(&aw_haptic->input_vib_work, input_vib_work_routine); + ret = input_register_device(input_dev); + if (ret < 0) { + aw_err("register input device failed, rc=%d\n", ret); + input_ff_destroy(aw_haptic->input_dev); + return ret; + } + + return ret; +} +#endif + +static int aw_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + int ret = 0; + struct aw_haptic *aw_haptic; + struct device_node *np = i2c->dev.of_node; + + pr_info("<%s>%s: enter\n", AW_I2C_NAME, __func__); + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + pr_err("<%s>%s: check_functionality failed\n", AW_I2C_NAME, __func__); + return -EIO; + } + + aw_haptic = devm_kzalloc(&i2c->dev, sizeof(struct aw_haptic), GFP_KERNEL); + if (aw_haptic == NULL) + return -ENOMEM; + + aw_haptic->dev = &i2c->dev; + aw_haptic->i2c = i2c; + + i2c_set_clientdata(i2c, aw_haptic); +#ifdef AW_INPUT_FRAMEWORK + ret = input_framework_init(aw_haptic); + if (ret < 0) + goto err_input_config; +#endif + /* aw_haptic rst & int */ + if (np) { + ret = parse_dt_gpio(aw_haptic, &i2c->dev, np); + if (ret) { + aw_err("failed to parse gpio"); + goto err_parse_dt; + } + } else { + aw_haptic->reset_gpio = -1; + aw_haptic->irq_gpio = -1; + } + + if (aw_haptic->is_used_rst_pin) { + if (gpio_is_valid(aw_haptic->reset_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw_haptic->reset_gpio, + GPIOF_OUT_INIT_LOW, "awinic_rst"); + if (ret) { + aw_err("rst request failed"); + goto err_reset_gpio_request; + } + } + } + + if (gpio_is_valid(aw_haptic->irq_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw_haptic->irq_gpio, + GPIOF_DIR_IN, "awinic_int"); + if (ret) { + aw_err("int request failed"); + goto err_irq_gpio_request; + } + } + + parse_dts_i2c_addr(aw_haptic, &i2c->dev, np); + /* aw_haptic chip id */ + ret = parse_chipid(aw_haptic); + if (ret < 0) { + aw_err("read_chipid failed ret=%d", ret); + goto err_id; + } + + ret = chip_private_init(aw_haptic); + if (ret < 0) { + aw_err("chip_private_init failed ret=%d", ret); + goto err_chip_private_init; + } + + ret = func_ptr_init(aw_haptic, &i2c->dev); + if (ret < 0) { + aw_err("ctrl_init failed ret=%d", ret); + goto err_ctrl_init; + } + + ret = aw_haptic->func->check_qualify(aw_haptic); + if (ret < 0) { + aw_err("qualify check failed ret=%d", ret); + goto err_ctrl_init; + } + + sw_reset(aw_haptic); + ret = aw_haptic->func->offset_cali(aw_haptic); + if (ret < 0) + sw_reset(aw_haptic); + + aw_haptic->func->parse_dts(aw_haptic, np); + + ret = irq_config(&i2c->dev, aw_haptic); + if (ret != 0) { + aw_err("irq_config failed ret=%d", ret); + goto err_irq_config; + } + +#ifdef AW_TIKTAP + g_aw_haptic = aw_haptic; + ret = tiktap_init(aw_haptic); + if (ret) { + aw_err("tiktap_init failed ret = %d", ret); + goto err_irq_config; + } +#endif + vibrator_init(aw_haptic); + haptic_init(aw_haptic); + aw_haptic->work_queue = create_singlethread_workqueue("aw_haptic_vibrator_work_queue"); + if (!aw_haptic->work_queue) { + aw_err("Error creating aw_haptic_vibrator_work_queue"); + goto err_irq_config; + } + aw_haptic->func->creat_node(aw_haptic); + ram_work_init(aw_haptic); + dev_set_drvdata(&i2c->dev, aw_haptic); + aw_info("probe completed successfully!"); + return 0; + +err_irq_config: +err_ctrl_init: +err_chip_private_init: +err_irq_gpio_request: +err_reset_gpio_request: +err_parse_dt: +err_id: +err_input_config: + devm_kfree(&i2c->dev, aw_haptic); + aw_haptic = NULL; + return ret; +} + +#ifdef KERNEL_OVER_6_1 +static void aw_i2c_remove(struct i2c_client *i2c) +{ + struct aw_haptic *aw_haptic = i2c_get_clientdata(i2c); + + aw_info("enter"); +#ifdef TIMED_OUTPUT + timed_output_dev_unregister(&aw_haptic->vib_dev); +#else + sysfs_remove_group(&aw_haptic->vib_dev.dev->kobj, &vibrator_attribute_group); + devm_led_classdev_unregister(&aw_haptic->i2c->dev, &aw_haptic->vib_dev); +#endif +#ifdef AW_INPUT_FRAMEWORK + cancel_work_sync(&aw_haptic->input_gain_work); + cancel_work_sync(&aw_haptic->stop_work); + cancel_work_sync(&aw_haptic->input_vib_work); + input_unregister_device(aw_haptic->input_dev); + input_ff_destroy(aw_haptic->input_dev); +#endif + cancel_delayed_work_sync(&aw_haptic->ram_work); + cancel_work_sync(&aw_haptic->haptic_audio.work); + hrtimer_cancel(&aw_haptic->haptic_audio.timer); + cancel_work_sync(&aw_haptic->rtp_work); + cancel_work_sync(&aw_haptic->vibrator_work); + hrtimer_cancel(&aw_haptic->timer); + mutex_destroy(&aw_haptic->lock); + mutex_destroy(&aw_haptic->rtp_lock); + mutex_destroy(&aw_haptic->haptic_audio.lock); +#ifdef AW_TIKTAP + cancel_work_sync(&aw_haptic->tiktap_work); + ClearPageReserved(virt_to_page(aw_haptic->start_buf)); + free_pages((unsigned long)aw_haptic->start_buf, AW_TIKTAP_MMAP_PAGE_ORDER); + aw_haptic->start_buf = NULL; + proc_remove(aw_haptic->aw_config_proc); + aw_haptic->aw_config_proc = NULL; +#endif + devm_free_irq(&i2c->dev, gpio_to_irq(aw_haptic->irq_gpio), aw_haptic); +} +#else +static int aw_i2c_remove(struct i2c_client *i2c) +{ + struct aw_haptic *aw_haptic = i2c_get_clientdata(i2c); + + aw_info("enter"); +#ifdef TIMED_OUTPUT + timed_output_dev_unregister(&aw_haptic->vib_dev); +#else + sysfs_remove_group(&aw_haptic->vib_dev.dev->kobj, &vibrator_attribute_group); + devm_led_classdev_unregister(&aw_haptic->i2c->dev, &aw_haptic->vib_dev); +#endif +#ifdef AW_INPUT_FRAMEWORK + cancel_work_sync(&aw_haptic->input_gain_work); + cancel_work_sync(&aw_haptic->stop_work); + cancel_work_sync(&aw_haptic->input_vib_work); + input_unregister_device(aw_haptic->input_dev); + input_ff_destroy(aw_haptic->input_dev); +#endif + cancel_delayed_work_sync(&aw_haptic->ram_work); + cancel_work_sync(&aw_haptic->haptic_audio.work); + hrtimer_cancel(&aw_haptic->haptic_audio.timer); + cancel_work_sync(&aw_haptic->rtp_work); + cancel_work_sync(&aw_haptic->vibrator_work); + hrtimer_cancel(&aw_haptic->timer); + mutex_destroy(&aw_haptic->lock); + mutex_destroy(&aw_haptic->rtp_lock); + mutex_destroy(&aw_haptic->haptic_audio.lock); +#ifdef AW_TIKTAP + cancel_work_sync(&aw_haptic->tiktap_work); + ClearPageReserved(virt_to_page(aw_haptic->start_buf)); + free_pages((unsigned long)aw_haptic->start_buf, AW_TIKTAP_MMAP_PAGE_ORDER); + aw_haptic->start_buf = NULL; + proc_remove(aw_haptic->aw_config_proc); + aw_haptic->aw_config_proc = NULL; +#endif + devm_free_irq(&i2c->dev, gpio_to_irq(aw_haptic->irq_gpio), aw_haptic); + + return 0; +} +#endif + +static int aw_i2c_suspend(struct device *dev) +{ + int ret = 0; + struct aw_haptic *aw_haptic = dev_get_drvdata(dev); + + aw_info("enter"); + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + mutex_unlock(&aw_haptic->lock); + + return ret; +} + +static int aw_i2c_resume(struct device *dev) +{ + pr_info("<%s>%s enter\n", AW_I2C_NAME, __func__); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(aw_pm_ops, aw_i2c_suspend, aw_i2c_resume); + +static const struct i2c_device_id aw_i2c_id[] = { + {AW_I2C_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, aw_i2c_id); + +static const struct of_device_id aw_dt_match[] = { +#ifdef AW_DOUBLE + {.compatible = "awinic,haptic_nv_r"}, + {.compatible = "awinic,haptic_nv_l"}, +#else + {.compatible = "awinic,haptic_nv"}, +#endif + {}, +}; + +static struct i2c_driver aw_i2c_driver = { + .driver = { + .name = AW_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw_dt_match), +#ifdef CONFIG_PM_SLEEP + .pm = &aw_pm_ops, +#endif + }, + .probe = aw_i2c_probe, + .remove = aw_i2c_remove, + .id_table = aw_i2c_id, +}; + +static int __init aw_i2c_init(void) +{ + int ret = 0; + + pr_info("<%s>%s: aw_haptic driver version %s\n", AW_I2C_NAME, __func__, + HAPTIC_NV_DRIVER_VERSION); + ret = i2c_add_driver(&aw_i2c_driver); + if (ret) { + pr_err("<%s>%s: fail to add aw_haptic device into i2c\n", AW_I2C_NAME, __func__); + return ret; + } + return 0; +} +module_init(aw_i2c_init); + +static void __exit aw_i2c_exit(void) +{ + i2c_del_driver(&aw_i2c_driver); +} +module_exit(aw_i2c_exit); + +MODULE_DESCRIPTION("AWINIC Haptic Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/awinic/aw862x_haptic_nv_v1/haptic_nv.h b/drivers/misc/awinic/aw862x_haptic_nv_v1/haptic_nv.h new file mode 100644 index 000000000000..5416de234fd5 --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv_v1/haptic_nv.h @@ -0,0 +1,705 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _HAPTIC_NV_H_ +#define _HAPTIC_NV_H_ +/********************************************************* + * + * Macro Control + * + *********************************************************/ +#define AW_CHECK_RAM_DATA +#define AW_READ_BIN_FLEXBALLY +#define AW_INPUT_FRAMEWORK +#define AW_TIKTAP +/* #define AW_DOUBLE */ +#define AW_DURATION_DECIDE_WAVEFORM +/* #define AW_ENABLE_RTP_PRINT_LOG */ +/* #define AW862X_MUL_GET_F0 */ +/* #define AW_MAXIMUM_F0_CALI_DATA */ +#define AW862X_DRIVER +#define AW862XX_DRIVER +#define AW8623X_DRIVER +#define AW8624X_DRIVER +/********************************************************* + * + * Haptic_NV CHIPID + * + *********************************************************/ +#define AW8623_CHIP_ID (0x23) +#define AW8624_CHIP_ID (0x24) +#define AW8622X_CHIP_ID (0x00) +#define AW86214_CHIP_ID (0x01) +#define AW8623X_CHIP_ID_H (0x23) +#define AW86233_CHIP_ID (0x2330) +#define AW86234_CHIP_ID (0x2340) +#define AW86235_CHIP_ID (0x2350) +#define AW8624X_CHIP_ID_H (0x24) +#define AW86243_CHIP_ID (0x2430) +#define AW86245_CHIP_ID (0x2450) +/********************************************************* + * + * Marco + * + *********************************************************/ +#define AW_I2C_NAME "haptic_nv" +#define AW_I2C_RETRIES (5) +#define AW_I2C_READ_MSG_NUM (2) +#define AW_I2C_BYTE_ONE (1) +#define AW_I2C_BYTE_TWO (2) +#define AW_I2C_BYTE_THREE (3) +#define AW_I2C_BYTE_FOUR (4) +#define AW_I2C_BYTE_FIVE (5) +#define AW_I2C_BYTE_SIX (6) +#define AW_I2C_BYTE_SEVEN (7) +#define AW_I2C_BYTE_EIGHT (8) + +#define AWRW_SIZE (220) +#define AW_REG_MAX (0xFF) +#define AW_NAME_MAX (64) +#define AW_VBAT_MIN (3000) +#define AW_VBAT_MAX (4500) +#define AW_TRIG_NUM (3) +#define AWRW_CMD_UNIT (5) +#define AW_VBAT_REFER (4200) +#define AW_DEFAULT_GAIN (0x80) +#define AW_SEQUENCER_SIZE (8) +#define AW_PM_QOS_VALUE_VB (400) +#define AW_GUN_TYPE_DEF_VAL (0xFF) +#define AW_DRV_WIDTH_MIN (0) +#define AW_DRV_WIDTH_MAX (255) +#define AW_BULLET_NR_DEF_VAL (0) +#define CPU_LATENCY_QOC_VALUE (0) +#define AW_SEQUENCER_LOOP_SIZE (4) +#define AW_RAMDATA_SHOW_COLUMN (16) +#define AW_READ_CHIPID_RETRIES (5) +#define AW_RAMDATA_RD_BUFFER_SIZE (1024) +#define AW_RAMDATA_WR_BUFFER_SIZE (2048) +#define AW_RAMDATA_SHOW_UINT_SIZE (6) +#define AW_READ_CHIPID_RETRY_DELAY (2) +#define AW_EFFECT_NUMBER (3) +#define AW_RAM_WORK_DELAY_INTERVAL (8000) +#define AW_RAMDATA_SHOW_LINE_BUFFER_SZIE (100) + +#define AW_RL_DELAY_MIN (3000) +#define AW_RL_DELAY_MAX (3500) +#define AW_F0_DELAY_MIN (10000) +#define AW_F0_DELAY_MAX (10500) +#define AW_RTP_DELAY_MIN (2000) +#define AW_RTP_DELAY_MAX (2500) +#define AW_PLAY_DELAY_MIN (2000) +#define AW_PLAY_DELAY_MAX (2500) +#define AW_STOP_DELAY_MIN (2000) +#define AW_STOP_DELAY_MAX (2500) +#define AW_VBAT_DELAY_MIN (2000) +#define AW_VBAT_DELAY_MAX (2500) +#define AW_OSC_TRIM_PARAM (50) +#define AW_OSC_CALI_MAX_LENGTH (5100000) + +#define AW_SET_RAMADDR_H(addr) ((addr) >> 8) +#define AW_SET_RAMADDR_L(addr) ((addr) & 0x00FF) +#define AW_SET_BASEADDR_H(addr) ((addr) >> 8) +#define AW_SET_BASEADDR_L(addr) ((addr) & 0x00FF) +/********************************************************* + * + * aw862x marco + * + ********************************************************/ +#define AW862X_F0_CALI_ACCURACY (25) +#define AW862X_MUL_GET_F0_RANGE (150) +#define AW862X_MUL_GET_F0_NUM (3) + +#define AW862X_VBAT_FORMULA(code) (6100 * (code) / 256) +#define AW862X_F0_FORMULA(reg, coeff) (1000000000 / ((reg) * (coeff))) +#define AW862X_RL_FORMULA(reg_val) (298 * (reg_val)) +#define AW862X_SET_AEADDR_H(addr) ((((addr) >> 1) >> 8)) +#define AW862X_SET_AEADDR_L(addr) (((addr) >> 1) & 0x00FF) +#define AW862X_SET_AFADDR_H(addr) (((addr) - ((addr) >> 2)) >> 8) +#define AW862X_SET_AFADDR_L(addr) (((addr) - ((addr) >> 2)) & 0x00FF) +/********************************************************* + * + * aw862xx marco + * + ********************************************************/ +#define AW862XX_DRV2_LVL_MAX (127) +#define AW862XX_F0_CALI_ACCURACY (24) +#define AW862XX_VBAT_FORMULA(code) (6100 * (code) / 1024) +#define AW862XX_OS_FORMULA(os_code, d2s_gain) (2440 * ((os_code) - 512) / (1024 * ((d2s_gain) + 1))) +#define AW862XX_F0_FORMULA(code) (384000 * 10 / (code)) +#define AW862XX_RL_FORMULA(code, d2s_gain) (((code) * 678 * 100) / (1024 * (d2s_gain))) +#define AW862XX_SET_AEADDR_H(addr) ((((addr) >> 1) >> 4) & 0xF0) +#define AW862XX_SET_AEADDR_L(addr) (((addr) >> 1) & 0x00FF) +#define AW862XX_SET_AFADDR_H(addr) ((((addr) - ((addr) >> 2)) >> 8) & 0x0F) +#define AW862XX_SET_AFADDR_L(addr) (((addr) - ((addr) >> 2)) & 0x00FF) +#define AW862XX_DRV2_LVL_FORMULA(f0, vrms) ((((f0) < 1800) ? 1809920 : 1990912) / 1000 * (vrms) / 61000) +#define AW862XX_DRV_WIDTH_FORMULA(f0, margin, brk_gain) \ + ((240000 / (f0)) - (margin) - (brk_gain) - 8) + +/********************************************************* + * + * aw8623x marco + * + ********************************************************/ +#define AW8623X_DRV2_LVL_MAX (127) +#define AW8623X_F0_CALI_ACCURACY (24) + +#define AW8623X_VBAT_FORMULA(code) (6100 * (code) / 1023) +#define AW8623X_F0_FORMULA(code) (384000 * 10 / (code)) +#define AW8623X_RL_FORMULA(code, d2s_gain) (((code) * 678 * 1000) / (1023 * (d2s_gain))) +#define AW8623X_OS_FORMULA(code, d2s_gain) (2440 * ((code) - 512) / (1023 * (1 + d2s_gain))) +#define AW8623X_SET_AEADDR_H(addr) ((((addr) >> 1) >> 4) & 0xF0) +#define AW8623X_SET_AEADDR_L(addr) (((addr) >> 1) & 0x00FF) +#define AW8623X_SET_AFADDR_H(addr) ((((addr) - ((addr) >> 2)) >> 8) & 0x0F) +#define AW8623X_SET_AFADDR_L(addr) (((addr) - ((addr) >> 2)) & 0x00FF) +#define AW8623X_DRV2_LVL_FORMULA(f0, vrms) ((((f0) < 1800) ? 1809920 : 1990912) / 1000 * (vrms) / 40000) +#define AW8623X_DRV_WIDTH_FORMULA(f0, margin, brk_gain) \ + ((240000 / (f0)) - (margin) - (brk_gain) - 8) + +/********************************************************* + * + * aw8624x marco + * + ********************************************************/ +#define AW8624X_DRV2_LVL_MAX (127) +#define AW8624X_F0_CALI_ACCURACY (24) + +#define AW8624X_VBAT_FORMULA(code) (6100 * (code) / 1023) +#define AW8624X_F0_FORMULA(code) (384000 * 10 / (code)) +#define AW8624X_RL_FORMULA(code, d2s_gain) (((code) * 610 * 1000) / (1023 * (d2s_gain))) +#define AW8624X_OS_FORMULA(code, d2s_gain) (2440 * ((code) - 512) / (1023 * (1 + d2s_gain))) +#define AW8624X_SET_AEADDR_H(addr) ((((addr) >> 1) >> 4) & 0xF0) +#define AW8624X_SET_AEADDR_L(addr) (((addr) >> 1) & 0x00FF) +#define AW8624X_SET_AFADDR_H(addr) ((((addr) - ((addr) >> 2)) >> 8) & 0x0F) +#define AW8624X_SET_AFADDR_L(addr) (((addr) - ((addr) >> 2)) & 0x00FF) +#define AW8624X_DRV2_LVL_FORMULA(f0, vrms) ((((f0) < 1800) ? 1809920 : 1990912) / 1000 * (vrms) / 61000) +#define AW8624X_DRV_WIDTH_FORMULA(f0, margin, brk_gain) \ + ((240000 / (f0)) - (margin) - (brk_gain) - 8) + +/********************************************************* + * + * Conditional Marco + * + *********************************************************/ + +#ifdef AW_TIKTAP +#define AW_TIKTAP_PROCNAME "tiktap_buf" +#define AW_TIKTAP_PROCNAME_R "tiktap_buf_r" +#define AW_TIKTAP_MMAP_PAGE_ORDER (2) +#define AW_TIKTAP_MMAP_BUF_SUM (16) +#define AW_TIKTAP_MMAP_BUF_SIZE (1000) + +#pragma pack(4) +struct mmap_buf_format { + uint8_t status; + uint8_t bit; + int16_t length; + + struct mmap_buf_format *kernel_next; + struct mmap_buf_format *user_next; + uint8_t reg_addr; + int8_t data[AW_TIKTAP_MMAP_BUF_SIZE]; +}; /* 1024 byte */ +#pragma pack() + +#define TIKTAP_IOCTL_GROUP 0xFF +#define TIKTAP_GET_F0 _IO(TIKTAP_IOCTL_GROUP, 0x01) +#define TIKTAP_GET_HWINFO _IO(TIKTAP_IOCTL_GROUP, 0x02) +#define TIKTAP_SET_FREQ _IO(TIKTAP_IOCTL_GROUP, 0x03) +#define TIKTAP_SETTING_GAIN _IO(TIKTAP_IOCTL_GROUP, 0x04) +#define TIKTAP_SETTING_SPEED _IO(TIKTAP_IOCTL_GROUP, 0x05) +#define TIKTAP_SETTING_BSTVOL _IO(TIKTAP_IOCTL_GROUP, 0x06) +#define TIKTAP_ON_MODE _IO(TIKTAP_IOCTL_GROUP, 0x07) +#define TIKTAP_OFF_MODE _IO(TIKTAP_IOCTL_GROUP, 0x08) +#define TIKTAP_RTP_MODE _IO(TIKTAP_IOCTL_GROUP, 0x09) +#define TIKTAP_RTP_IRQ_MODE _IO(TIKTAP_IOCTL_GROUP, 0x0A) +#define TIKTAP_STOP_MODE _IO(TIKTAP_IOCTL_GROUP, 0x0B) +#define TIKTAP_STOP_RTP_MODE _IO(TIKTAP_IOCTL_GROUP, 0x0C) +#define TIKTAP_WRITE_REG _IO(TIKTAP_IOCTL_GROUP, 0x0D) +#define TIKTAP_READ_REG _IO(TIKTAP_IOCTL_GROUP, 0x0E) +#define TIKTAP_BST_SWITCH _IO(TIKTAP_IOCTL_GROUP, 0x0F) +#define TIKTAP_GET_SPEED _IO(TIKTAP_IOCTL_GROUP, 0x10) + +enum { + MMAP_BUF_DATA_VALID = 0x55, + MMAP_BUF_DATA_FINISHED = 0xAA, + MMAP_BUF_DATA_INVALID = 0xFF, +}; +#endif + +#if KERNEL_VERSION(4, 4, 1) >= LINUX_VERSION_CODE +#define TIMED_OUTPUT +#endif + +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +#define KERNEL_OVER_5_10 +#endif + +#if KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE +#define KERNEL_OVER_6_1 +#endif + +#ifdef TIMED_OUTPUT +#include <../../../drivers/staging/android/timed_output.h> +typedef struct timed_output_dev cdev_t; +#else +typedef struct led_classdev cdev_t; +#endif +/********************************************************* + * + * Log Format + * + *********************************************************/ +#ifdef AW_DOUBLE +#define aw_err(format, ...) \ + pr_err("[haptic_nv]<%s>%s: " format "\n", aw_haptic->mark, __func__, ##__VA_ARGS__) + +#define aw_info(format, ...) \ + pr_info("[haptic_nv]<%s>%s: " format "\n", aw_haptic->mark, __func__, ##__VA_ARGS__) + +#define aw_dbg(format, ...) \ + pr_debug("[haptic_nv]<%s>%s: " format "\n", aw_haptic->mark, __func__, ##__VA_ARGS__) +#else +#define aw_err(format, ...) \ + pr_err("[%s][%04d]%s: " format "\n", AW_I2C_NAME, __LINE__, __func__, ##__VA_ARGS__) + +#define aw_info(format, ...) \ + pr_info("[%s][%04d]%s: " format "\n", AW_I2C_NAME, __LINE__, __func__, ##__VA_ARGS__) + +#define aw_dbg(format, ...) \ + pr_debug("[%s][%04d]%s: " format "\n", AW_I2C_NAME, __LINE__, __func__, ##__VA_ARGS__) +#endif +/********************************************************* + * + * Enum Define + * + *********************************************************/ +enum awrw_flag { + AW_WRITE = 0, + AW_READ = 1, +}; + +enum aw_haptic_flags { + AW_FLAG_NONR = 0, + AW_FLAG_SKIP_INTERRUPTS = 1, +}; + +enum aw_haptic_work_mode { + AW_STANDBY_MODE = 0, + AW_RAM_MODE = 1, + AW_RAM_LOOP_MODE = 2, + AW_CONT_MODE = 3, + AW_RTP_MODE = 4, + AW_TRIG_MODE = 5, + AW_NULL = 6, +}; + +enum aw_haptic_cont_vbat_comp_mode { + AW_CONT_VBAT_SW_COMP_MODE = 0, + AW_CONT_VBAT_HW_COMP_MODE = 1, +}; + +enum aw_haptic_ram_vbat_comp_mode { + AW_RAM_VBAT_COMP_DISABLE = 0, + AW_RAM_VBAT_COMP_ENABLE = 1, +}; + +enum aw_haptic_pwm_mode { + AW_PWM_48K = 0, + AW_PWM_24K = 1, + AW_PWM_12K = 2, +}; + +enum aw_haptic_play { + AW_PLAY_NULL = 0, + AW_PLAY_ENABLE = 1, + AW_PLAY_STOP = 2, + AW_PLAY_GAIN = 8, +}; + +enum aw_haptic_cmd { + AW_CMD_NULL = 0, + AW_CMD_ENABLE = 1, + AW_CMD_HAPTIC = 0x0F, + AW_CMD_TP = 0x10, + AW_CMD_R_EN = 0x40, + AW_CMD_L_EN = 0x80, + AW_CMD_SYS = 0xF0, + AW_CMD_STOP = 255, +}; + +enum aw_haptic_cali_lra { + AW_WRITE_ZERO = 0, + AW_F0_CALI_LRA = 1, + AW_OSC_CALI_LRA = 2, +}; + +enum aw_haptic_awrw_flag { + AW_SEQ_WRITE = 0, + AW_SEQ_READ = 1, +}; + +enum aw_trim_lra { + AW_TRIM_LRA_BOUNDARY = 0x20, + AW8624X_TRIM_LRA_BOUNDARY = 0x40, +}; + +enum aw_haptic_read_chip_type { + AW_FIRST_TRY = 0, + AW_LAST_TRY = 1, +}; + +enum aw_haptic_chip_name { + AW_CHIP_NULL = 0, + AW86223 = 1, + AW86224 = 2, + AW86225 = 3, + AW86214 = 4, + AW8623 = 5, + AW8624 = 6, + AW86233 = 7, + AW86234 = 8, + AW86235 = 9, + AW86243 = 10, + AW86245 = 11, +}; + +enum aw_haptic_irq_state { + AW_IRQ_ALMOST_EMPTY = 1, +}; + +enum aw_haptic_dts_data_type { + AW_DTS_NULL = 0, + AW_DTS_DATA_U32 = 1, + AW_DTS_DATA_ARRAY = 2, + AW_DTS_DATA_BOOL = 3, +}; + +enum aw_haptic_protect_config { + AW_PROTECT_OFF = 0, + AW_PROTECT_EN = 1, + AW_PROTECT_CFG_1 = 0x2D, + AW_PROTECT_CFG_2 = 0x3E, + AW_PROTECT_CFG_3 = 0X3F, +}; + +enum aw_haptic_ram_file_num { + AW862X_RAM_FILE = 0, + AW862XX_RAM_FILE = 1, +}; + +enum aw_haptic_pin { + AW_TRIG1 = 0, + AW_TRIG2 = 1, + AW_TRIG3 = 2, + AW_IRQ = 3, +}; + +/********************************************************* + * + * Enum aw8623x + * + *********************************************************/ +enum aw8623x_sram_size_flag { + AW8623X_HAPTIC_SRAM_1K = 0, + AW8623X_HAPTIC_SRAM_2K = 1, + AW8623X_HAPTIC_SRAM_3K = 2, +}; + +/********************************************************* + * + * Enum aw862xx + * + *********************************************************/ +enum aw862xx_ef_id { + AW86223_EF_ID = 0x01, + AW86224_EF_ID = 0x00, + AW86225_EF_ID = 0x00, + AW86214_EF_ID = 0x41, +}; + +enum aw862xx_sram_size_flag { + AW862XX_HAPTIC_SRAM_1K = 0, + AW862XX_HAPTIC_SRAM_2K = 1, + AW862XX_HAPTIC_SRAM_3K = 2, +}; +/********************************************************* + * + * Enum aw862x + * + *********************************************************/ +enum aw862x_pwm_clk { + AW862X_CLK_24K = 2, + AW862X_CLK_12K = 3, +}; +/********************************************************* + * + * Struct Define + * + *********************************************************/ +struct trig { + uint8_t enable; + uint8_t trig_edge; + uint8_t trig_brk; + uint8_t trig_level; + uint8_t trig_polar; + uint8_t pos_enable; + uint8_t neg_enable; + uint8_t pos_sequence; + uint8_t neg_sequence; +}; + +struct aw_haptic_ram { + uint8_t ram_num; + uint8_t version; + uint8_t ram_shift; + uint8_t baseaddr_shift; + + uint32_t len; + uint32_t check_sum; + uint32_t base_addr; +}; + +struct aw_haptic_ctr { + uint8_t cnt; + uint8_t cmd; + uint8_t play; + uint8_t loop; + uint8_t gain; + uint8_t wavseq; + struct list_head list; +}; + +struct aw_i2c_info { + uint8_t flag; + uint8_t reg_num; + uint8_t first_addr; + uint8_t reg_data[AWRW_SIZE]; +}; + +struct aw_haptic_audio { + int delay_val; + int timer_val; + struct mutex lock; + struct hrtimer timer; + struct list_head list; + struct work_struct work; + struct aw_haptic_ctr ctr; + struct list_head ctr_list; +}; + +struct aw_haptic_dts_data { + void *dts_val; + uint8_t type; + uint32_t len; +}; + +struct aw_haptic_dts_info { + bool is_enabled_auto_brk; + bool is_enabled_track_en; + /* aw8624x */ + bool is_enabled_smart_loop; + bool is_enabled_inter_brake; + + uint32_t mode; + uint32_t f0_cali_percent; + uint32_t f0_pre; + uint32_t lra_vrms; + uint32_t lk_f0_cali; + uint32_t cont_tset; + uint32_t cont_drv1_lvl; + uint32_t cont_drv2_lvl; + uint32_t duration_time[3]; + uint32_t trig_cfg[21]; + /* aw862x */ + uint32_t cont_td; + uint32_t cont_zc_thr; + uint32_t cont_num_brk; + uint32_t f0_coeff; + uint32_t cont_brake[24]; + uint32_t bemf_config[4]; + uint32_t sw_brake[2]; + uint32_t wavseq[16]; + uint32_t wavloop[10]; + uint32_t td_brake[3]; + uint32_t f0_trace_parameter[4]; + /* aw862xx */ + uint32_t gain_bypass; + uint32_t cont_drv1_time; + uint32_t cont_drv2_time; + uint32_t cont_brk_time; + uint32_t cont_track_margin; + uint32_t cont_brk_gain; + uint32_t d2s_gain; + uint32_t prctmode[3]; + /* aw8624x */ + uint32_t f0_d2s_gain; +}; + +struct aw_haptic { + bool rtp_init; + bool ram_init; + bool dual_flag; + bool is_used_irq_pin; + bool is_used_rst_pin; + + uint8_t flags; + uint8_t play_mode; + uint8_t chipid_flag; + uint8_t max_pos_beme; + uint8_t max_neg_beme; + uint8_t activate_mode; + uint8_t ram_vbat_comp; + uint8_t seq[AW_SEQUENCER_SIZE]; + uint8_t loop[AW_SEQUENCER_SIZE]; + uint8_t trim_lra_boundary; +#ifdef AW_DOUBLE + uint8_t mark[15]; +#endif + + int name; + int gain; + int state; + int index; + int irq_gpio; + int duration; + int effect_id; + int amplitude; + int reset_gpio; + + uint32_t f0; + uint32_t lra; + uint32_t vbat; + uint32_t level; + uint32_t f0_pre; + uint32_t cont_f0; + uint32_t rtp_cnt; + uint32_t rtp_len; + uint32_t gun_type; + uint32_t bullet_nr; + uint32_t theory_time; + uint32_t interval_us; + uint32_t f0_cali_data; + uint32_t rtp_file_num; + uint32_t rtp_num_max; + uint32_t ram_file_num; + uint32_t timeval_flags; + uint32_t osc_cali_data; + uint32_t aw862xx_i2c_addr; + uint64_t microsecond; + + cdev_t vib_dev; + + ktime_t kend; + ktime_t kstart; + ktime_t current_t; + ktime_t pre_enter_t; +#ifdef AW_TIKTAP + bool tiktap_ready; + bool vib_stop_flag; + bool tiktap_stop_flag; + struct work_struct tiktap_work; + struct proc_dir_entry *aw_config_proc; + struct mmap_buf_format *start_buf; +#endif + struct mutex lock; + struct device *dev; + struct hrtimer timer; + struct mutex rtp_lock; + struct i2c_client *i2c; + struct aw_haptic_ram ram; + struct aw_haptic_func *func; + struct aw_i2c_info i2c_info; + struct input_dev *input_dev; + struct semaphore sema; + struct work_struct rtp_work; + struct work_struct stop_work; + struct delayed_work ram_work; + struct trig trig[AW_TRIG_NUM]; + struct aw_haptic_dts_info info; + struct work_struct vibrator_work; + struct work_struct input_gain_work; + struct work_struct input_vib_work; + struct aw_haptic_container *aw_rtp; + struct aw_haptic_audio haptic_audio; + struct workqueue_struct *work_queue; + struct pm_qos_request aw_pm_qos_req_vb; +}; + +struct aw_haptic_container { + int len; + uint8_t data[]; +}; + +struct aw_haptic_func { + int (*check_qualify)(struct aw_haptic *aw_haptic); + int (*get_irq_state)(struct aw_haptic *aw_haptic); + int (*get_f0)(struct aw_haptic *aw_haptic); + int (*offset_cali)(struct aw_haptic *aw_haptic); + void (*creat_node)(struct aw_haptic *aw_haptic); + void (*read_cont_f0)(struct aw_haptic *aw_haptic); + void (*parse_dts)(struct aw_haptic *aw_haptic, struct device_node *np); + void (*trig_init)(struct aw_haptic *aw_haptic); + void (*irq_clear)(struct aw_haptic *aw_haptic); + void (*haptic_start)(struct aw_haptic *aw_haptic); + void (*play_stop)(struct aw_haptic *aw_haptic); + void (*play_mode)(struct aw_haptic *aw_haptic, uint8_t play_mode); + void (*cont_config)(struct aw_haptic *aw_haptic); + void (*ram_init)(struct aw_haptic *aw_haptic, bool flag); + void (*misc_para_init)(struct aw_haptic *aw_haptic); + void (*interrupt_setup)(struct aw_haptic *aw_haptic); + void (*vbat_mode_config)(struct aw_haptic *aw_haptic, uint8_t flag); + void (*protect_config)(struct aw_haptic *aw_haptic, uint8_t prtime, uint8_t prlvl); + void (*calculate_cali_data)(struct aw_haptic *aw_haptic); + void (*set_gain)(struct aw_haptic *aw_haptic, uint8_t gain); + void (*get_gain)(struct aw_haptic *aw_haptic, uint8_t *gain); + void (*set_wav_seq)(struct aw_haptic *aw_haptic, uint8_t wav, uint8_t seq); + void (*get_wav_seq)(struct aw_haptic *aw_haptic, uint32_t len); + void (*set_wav_loop)(struct aw_haptic *aw_haptic, uint8_t wav, uint8_t loop); + void (*get_wav_loop)(struct aw_haptic *aw_haptic, uint8_t *val); + void (*set_rtp_data)(struct aw_haptic *aw_haptic, uint8_t *data, uint32_t len); + void (*set_fifo_addr)(struct aw_haptic *aw_haptic); + void (*get_fifo_addr)(struct aw_haptic *aw_haptic); + void (*set_ram_data)(struct aw_haptic *aw_haptic, uint8_t *data, int len); + void (*get_ram_data)(struct aw_haptic *aw_haptic, uint8_t *ram_data, int size); + void (*set_ram_addr)(struct aw_haptic *aw_haptic); + void (*set_repeat_seq)(struct aw_haptic *aw_haptic, uint8_t seq); + void (*set_base_addr)(struct aw_haptic *aw_haptic); + void (*set_trim_lra)(struct aw_haptic *aw_haptic, uint8_t val); + void (*set_rtp_aei)(struct aw_haptic *aw_haptic, bool flag); + void (*get_vbat)(struct aw_haptic *aw_haptic); + void (*get_lra_resistance)(struct aw_haptic *aw_haptic); + void (*get_first_wave_addr)(struct aw_haptic *aw_haptic, uint32_t *first_wave_addr); + ssize_t (*get_reg)(struct aw_haptic *aw_haptic, ssize_t len, char *buf); + uint8_t (*get_prctmode)(struct aw_haptic *aw_haptic); + uint8_t (*get_glb_state)(struct aw_haptic *aw_haptic); + uint8_t (*get_osc_status)(struct aw_haptic *aw_haptic); + uint8_t (*judge_rtp_going)(struct aw_haptic *aw_haptic); + uint8_t (*rtp_get_fifo_afs)(struct aw_haptic *aw_haptic); + uint8_t (*rtp_get_fifo_aes)(struct aw_haptic *aw_haptic); + uint64_t (*get_theory_time)(struct aw_haptic *aw_haptic); +}; +/********************************************************* + * + * Function Call + * + *********************************************************/ +#ifdef AW862X_DRIVER +extern struct aw_haptic_func aw862x_func_list; +#endif + +#ifdef AW862XX_DRIVER +extern struct aw_haptic_func aw862xx_func_list; +#endif + +#ifdef AW8623X_DRIVER +extern struct aw_haptic_func aw8623x_func_list; +#endif + +#ifdef AW8624X_DRIVER +extern struct aw_haptic_func aw8624x_func_list; +#endif + +extern int haptic_nv_i2c_reads(struct aw_haptic *aw_haptic, uint8_t reg_addr, uint8_t *buf, uint32_t len); +extern int haptic_nv_i2c_writes(struct aw_haptic *aw_haptic, uint8_t reg_addr, uint8_t *buf, uint32_t len); +extern int haptic_nv_i2c_write_bits(struct aw_haptic *aw_haptic, uint8_t reg_addr, uint32_t mask, uint8_t reg_data); +extern ssize_t haptic_nv_read_reg_array(struct aw_haptic *aw_haptic, char *buf, ssize_t len, + uint8_t head_reg_addr, uint8_t tail_reg_addr); +#endif diff --git a/drivers/misc/awinic/aw862x_haptic_nv_v1/haptic_nv_reg.h b/drivers/misc/awinic/aw862x_haptic_nv_v1/haptic_nv_reg.h new file mode 100644 index 000000000000..3665dbbec2e5 --- /dev/null +++ b/drivers/misc/awinic/aw862x_haptic_nv_v1/haptic_nv_reg.h @@ -0,0 +1,1990 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _HAPTIC_NV_REG_H_ +#define _HAPTIC_NV_REG_H_ +/******************************************** + * common Register List + *******************************************/ +#define AW_REG_ID (0x00) +#define AW_REG_CHIPIDH (0x57) /* AW8623X AW8624X*/ +/******************************************** + * AW862XX Register List + *******************************************/ +#define AW862XX_REG_ID (0x00) +#define AW862XX_REG_SYSST (0x01) +#define AW862XX_REG_SYSINT (0x02) +#define AW862XX_REG_SYSINTM (0x03) +#define AW862XX_REG_SYSST2 (0x04) +#define AW862XX_REG_SYSER (0x05) +#define AW862XX_REG_PLAYCFG2 (0x07) +#define AW862XX_REG_PLAYCFG3 (0x08) +#define AW862XX_REG_PLAYCFG4 (0x09) +#define AW862XX_REG_WAVCFG1 (0x0A) +#define AW862XX_REG_WAVCFG2 (0x0B) +#define AW862XX_REG_WAVCFG3 (0x0C) +#define AW862XX_REG_WAVCFG4 (0x0D) +#define AW862XX_REG_WAVCFG5 (0x0E) +#define AW862XX_REG_WAVCFG6 (0x0F) +#define AW862XX_REG_WAVCFG7 (0x10) +#define AW862XX_REG_WAVCFG8 (0x11) +#define AW862XX_REG_WAVCFG9 (0x12) +#define AW862XX_REG_WAVCFG10 (0x13) +#define AW862XX_REG_WAVCFG11 (0x14) +#define AW862XX_REG_WAVCFG12 (0x15) +#define AW862XX_REG_WAVCFG13 (0x16) +#define AW862XX_REG_CONTCFG1 (0x18) +#define AW862XX_REG_CONTCFG2 (0x19) +#define AW862XX_REG_CONTCFG3 (0x1A) +#define AW862XX_REG_CONTCFG4 (0x1B) +#define AW862XX_REG_CONTCFG5 (0x1C) +#define AW862XX_REG_CONTCFG6 (0x1D) +#define AW862XX_REG_CONTCFG7 (0x1E) +#define AW862XX_REG_CONTCFG8 (0x1F) +#define AW862XX_REG_CONTCFG9 (0x20) +#define AW862XX_REG_CONTCFG10 (0x21) +#define AW862XX_REG_CONTCFG11 (0x22) +#define AW862XX_REG_CONTCFG12 (0x23) +#define AW862XX_REG_CONTCFG13 (0x24) +#define AW862XX_REG_CONTRD14 (0x25) +#define AW862XX_REG_CONTRD15 (0x26) +#define AW862XX_REG_CONTRD16 (0x27) +#define AW862XX_REG_CONTRD17 (0x28) +#define AW862XX_REG_CONTRD18 (0x29) +#define AW862XX_REG_CONTRD19 (0x2A) +#define AW862XX_REG_CONTRD20 (0x2B) +#define AW862XX_REG_CONTRD21 (0x2C) +#define AW862XX_REG_RTPCFG1 (0x2D) +#define AW862XX_REG_RTPCFG2 (0x2E) +#define AW862XX_REG_RTPCFG3 (0x2F) +#define AW862XX_REG_RTPCFG4 (0x30) +#define AW862XX_REG_RTPCFG5 (0x31) +#define AW862XX_REG_RTPDATA (0x32) +#define AW862XX_REG_TRGCFG1 (0x33) +#define AW862XX_REG_TRGCFG2 (0x34) +#define AW862XX_REG_TRGCFG3 (0x35) +#define AW862XX_REG_TRGCFG4 (0x36) +#define AW862XX_REG_TRGCFG5 (0x37) +#define AW862XX_REG_TRGCFG6 (0x38) +#define AW862XX_REG_TRGCFG7 (0x39) +#define AW862XX_REG_TRGCFG8 (0x3A) +#define AW862XX_REG_GLBCFG2 (0x3C) +#define AW862XX_REG_GLBCFG4 (0x3E) +#define AW862XX_REG_GLBRD5 (0x3F) +#define AW862XX_REG_RAMADDRH (0x40) +#define AW862XX_REG_RAMADDRL (0x41) +#define AW862XX_REG_RAMDATA (0x42) +#define AW862XX_REG_SYSCTRL1 (0x43) +#define AW862XX_REG_SYSCTRL2 (0x44) +#define AW862XX_REG_SYSCTRL3 (0x45) +#define AW862XX_REG_SYSCTRL4 (0x46) +#define AW862XX_REG_SYSCTRL5 (0x47) +#define AW862XX_REG_SYSCTRL6 (0x48) +#define AW862XX_REG_SYSCTRL7 (0x49) +#define AW862XX_REG_PWMCFG1 (0x4C) +#define AW862XX_REG_PWMCFG3 (0x4E) +#define AW862XX_REG_PWMCFG4 (0x4F) +#define AW862XX_REG_DETCFG1 (0x51) +#define AW862XX_REG_DETCFG2 (0x52) +#define AW862XX_REG_DET_RL (0x53) +#define AW862XX_REG_DET_OS (0x54) +#define AW862XX_REG_DET_VBAT (0x55) +#define AW862XX_REG_DET_LO (0x57) +#define AW862XX_REG_TRIMCFG1 (0x58) +#define AW862XX_REG_TRIMCFG3 (0x5A) +#define AW862XX_REG_EFRD9 (0x64) +#define AW862XX_REG_ANACFG8 (0x77) +/******************************************** + * AW862X Register List + *******************************************/ +#define AW862X_REG_ID (0x00) +#define AW862X_REG_SYSST (0x01) +#define AW862X_REG_SYSINT (0x02) +#define AW862X_REG_SYSINTM (0x03) +#define AW862X_REG_SYSCTRL (0x04) +#define AW862X_REG_GO (0x05) +#define AW862X_REG_RTP_DATA (0x06) +#define AW862X_REG_WAVSEQ1 (0x07) +#define AW862X_REG_WAVSEQ2 (0x08) +#define AW862X_REG_WAVSEQ3 (0x09) +#define AW862X_REG_WAVSEQ4 (0x0a) +#define AW862X_REG_WAVSEQ5 (0x0b) +#define AW862X_REG_WAVSEQ6 (0x0c) +#define AW862X_REG_WAVSEQ7 (0x0d) +#define AW862X_REG_WAVSEQ8 (0x0e) +#define AW862X_REG_WAVLOOP1 (0x0f) +#define AW862X_REG_WAVLOOP2 (0x10) +#define AW862X_REG_WAVLOOP3 (0x11) +#define AW862X_REG_WAVLOOP4 (0x12) +#define AW862X_REG_MANLOOP (0x13) +#define AW862X_REG_TRG1_SEQP (0x14) +#define AW862X_REG_TRG1_SEQN (0x17) +#define AW862X_REG_TRG_CFG1 (0x1b) +#define AW862X_REG_TRG_CFG2 (0x1c) +#define AW862X_REG_DBGCTRL (0x20) +#define AW862X_REG_BASE_ADDRH (0x21) +#define AW862X_REG_BASE_ADDRL (0x22) +#define AW862X_REG_FIFO_AEH (0x23) +#define AW862X_REG_FIFO_AEL (0x24) +#define AW862X_REG_FIFO_AFH (0x25) +#define AW862X_REG_FIFO_AFL (0x26) +#define AW862X_REG_DATCTRL (0x2b) +#define AW862X_REG_PWMPRC (0x2d) +#define AW862X_REG_PWMDBG (0x2e) +#define AW862X_REG_DBGSTAT (0x30) +#define AW862X_REG_WAVECTRL (0x31) +#define AW862X_REG_BRAKE0_CTRL (0x32) +#define AW862X_REG_BRAKE1_CTRL (0x33) +#define AW862X_REG_BRAKE2_CTRL (0x34) +#define AW862X_REG_BRAKE_NUM (0x35) +#define AW862X_REG_ANACTRL (0x38) +#define AW862X_REG_SW_BRAKE (0x39) +#define AW862X_REG_DATDBG (0x3b) +#define AW862X_REG_PRLVL (0x3e) +#define AW862X_REG_PRTIME (0x3f) +#define AW862X_REG_RAMADDRH (0x40) +#define AW862X_REG_RAMADDRL (0x41) +#define AW862X_REG_RAMDATA (0x42) +#define AW862X_REG_BRA_MAX_NUM (0x44) +#define AW862X_REG_GLB_STATE (0x47) +#define AW862X_REG_CONT_CTRL (0x48) +#define AW862X_REG_F_PRE_H (0x49) +#define AW862X_REG_F_PRE_L (0x4a) +#define AW862X_REG_TD_H (0x4b) +#define AW862X_REG_TD_L (0x4c) +#define AW862X_REG_TSET (0x4d) +#define AW862X_REG_THRS_BRA_END (0x4f) +#define AW862X_REG_EF_RDATAH (0x55) +#define AW862X_REG_TRIM_LRA (0x5b) +#define AW862X_REG_R_SPARE (0x5d) +#define AW862X_REG_D2SCFG (0x5e) +#define AW862X_REG_DETCTRL (0x5f) +#define AW862X_REG_RLDET (0x60) +#define AW862X_REG_OSDET (0x61) +#define AW862X_REG_VBATDET (0x62) +#define AW862X_REG_ADCTEST (0x66) +#define AW862X_REG_F_LRA_F0_H (0x68) +#define AW862X_REG_F_LRA_F0_L (0x69) +#define AW862X_REG_F_LRA_CONT_H (0x6a) +#define AW862X_REG_F_LRA_CONT_L (0x6b) +#define AW862X_REG_WAIT_VOL_MP (0x6e) +#define AW862X_REG_WAIT_VOL_MN (0x6f) +#define AW862X_REG_ZC_THRSH_H (0x72) +#define AW862X_REG_ZC_THRSH_L (0x73) +#define AW862X_REG_BEMF_VTHH_H (0x74) +#define AW862X_REG_BEMF_VTHH_L (0x75) +#define AW862X_REG_BEMF_VTHL_H (0x76) +#define AW862X_REG_BEMF_VTHL_L (0x77) +#define AW862X_REG_BEMF_NUM (0x78) +#define AW862X_REG_DRV_TIME (0x79) +#define AW862X_REG_TIME_NZC (0x7a) +#define AW862X_REG_DRV_LVL (0x7b) +#define AW862X_REG_DRV_LVL_OV (0x7c) +#define AW862X_REG_NUM_F0_1 (0x7d) +#define AW862X_REG_NUM_F0_2 (0x7e) +#define AW862X_REG_NUM_F0_3 (0x7f) +/******************************************** + * AW8623X Register List + *******************************************/ +#define AW8623X_REG_RSTCFG (0x00) +#define AW8623X_REG_SYSST (0x01) +#define AW8623X_REG_SYSINT (0x02) +#define AW8623X_REG_SYSINTM (0x03) +#define AW8623X_REG_SYSST2 (0x04) +#define AW8623X_REG_SYSER (0x05) +#define AW8623X_REG_PLAYCFG2 (0x07) +#define AW8623X_REG_PLAYCFG3 (0x08) +#define AW8623X_REG_PLAYCFG4 (0x09) +#define AW8623X_REG_WAVCFG1 (0x0A) +#define AW8623X_REG_WAVCFG2 (0x0B) +#define AW8623X_REG_WAVCFG3 (0x0C) +#define AW8623X_REG_WAVCFG4 (0x0D) +#define AW8623X_REG_WAVCFG5 (0x0E) +#define AW8623X_REG_WAVCFG6 (0x0F) +#define AW8623X_REG_WAVCFG7 (0x10) +#define AW8623X_REG_WAVCFG8 (0x11) +#define AW8623X_REG_WAVCFG9 (0x12) +#define AW8623X_REG_WAVCFG10 (0x13) +#define AW8623X_REG_WAVCFG11 (0x14) +#define AW8623X_REG_WAVCFG12 (0x15) +#define AW8623X_REG_WAVCFG13 (0x16) +#define AW8623X_REG_CONTCFG1 (0x17) +#define AW8623X_REG_CONTCFG2 (0x18) +#define AW8623X_REG_CONTCFG3 (0x19) +#define AW8623X_REG_CONTCFG4 (0x1A) +#define AW8623X_REG_CONTCFG5 (0x1B) +#define AW8623X_REG_CONTCFG6 (0x1C) +#define AW8623X_REG_CONTCFG7 (0x1D) +#define AW8623X_REG_CONTCFG8 (0x1E) +#define AW8623X_REG_CONTCFG9 (0x1F) +#define AW8623X_REG_CONTCFG10 (0x20) +#define AW8623X_REG_CONTCFG11 (0x21) +#define AW8623X_REG_CONTCFG12 (0x22) +#define AW8623X_REG_CONTCFG13 (0x23) +#define AW8623X_REG_CONTRD14 (0x24) +#define AW8623X_REG_CONTRD15 (0x25) +#define AW8623X_REG_CONTRD16 (0x26) +#define AW8623X_REG_CONTRD17 (0x27) +#define AW8623X_REG_CONTRD18 (0x28) +#define AW8623X_REG_CONTRD19 (0x29) +#define AW8623X_REG_CONTRD20 (0x2A) +#define AW8623X_REG_CONTRD21 (0x2B) +#define AW8623X_REG_RTPCFG1 (0x2C) +#define AW8623X_REG_RTPCFG2 (0x2D) +#define AW8623X_REG_RTPCFG3 (0x2E) +#define AW8623X_REG_RTPCFG4 (0x2F) +#define AW8623X_REG_RTPCFG5 (0x30) +#define AW8623X_REG_RTPCFG6 (0x31) +#define AW8623X_REG_RTPDATA (0x32) +#define AW8623X_REG_TRGCFG1 (0x33) +#define AW8623X_REG_TRGCFG2 (0x34) +#define AW8623X_REG_TRGCFG3 (0x35) +#define AW8623X_REG_TRGCFG4 (0x36) +#define AW8623X_REG_TRGCFG5 (0x37) +#define AW8623X_REG_TRGCFG6 (0x38) +#define AW8623X_REG_TRGCFG7 (0x39) +#define AW8623X_REG_TRGCFG8 (0x3A) +#define AW8623X_REG_GLBCFG2 (0x3C) +#define AW8623X_REG_GLBCFG4 (0x3E) +#define AW8623X_REG_GLBRD5 (0x3F) +#define AW8623X_REG_RAMADDRH (0x40) +#define AW8623X_REG_RAMADDRL (0x41) +#define AW8623X_REG_RAMDATA (0x42) +#define AW8623X_REG_TRGCFG9 (0x43) +#define AW8623X_REG_I2SCFG1 (0x44) +#define AW8623X_REG_SYSCTRL1 (0x45) +#define AW8623X_REG_SYSCTRL2 (0x46) +#define AW8623X_REG_SYSCTRL3 (0x47) +#define AW8623X_REG_SYSCTRL4 (0x48) +#define AW8623X_REG_SYSCTRL5 (0x49) +#define AW8623X_REG_PWMCFG1 (0x4A) +#define AW8623X_REG_PWMCFG3 (0x4C) +#define AW8623X_REG_PWMCFG4 (0x4D) +#define AW8623X_REG_VBATCTRL (0x4E) +#define AW8623X_REG_DETCFG1 (0x4F) +#define AW8623X_REG_DETCFG2 (0x50) +#define AW8623X_REG_DETRD1 (0x51) +#define AW8623X_REG_DETRD2 (0x52) +#define AW8623X_REG_DETRD3 (0x53) +#define AW8623X_REG_IDH (0x57) +#define AW8623X_REG_IDL (0x58) +#define AW8623X_REG_TRIMCFG1 (0x59) +#define AW8623X_REG_TRIMCFG2 (0x5A) +#define AW8623X_REG_TRIMCFG3 (0x5B) +#define AW8623X_REG_AUTOSIN1 (0x5C) +#define AW8623X_REG_AUTOSIN2 (0x5D) +#define AW8623X_REG_EFCFG6 (0x64) +#define AW8623X_REG_CHIPIDL (0x69) +#define AW8623X_REG_EFCFG12 (0x6A) +#define AW8623X_REG_ANACFG3 (0x70) +#define AW8623X_REG_ANACFG4 (0x71) +#define AW8623X_REG_ANACFG10 (0x77) +/******************************************** + * AW8624X Register List + *******************************************/ +#define AW8624X_REG_ID (0x00) +#define AW8624X_REG_SYSST (0x01) +#define AW8624X_REG_SYSINT (0x02) +#define AW8624X_REG_SYSINTM (0x03) +#define AW8624X_REG_SYSST2 (0x04) +#define AW8624X_REG_SYSER (0x05) +#define AW8624X_REG_PLAYCFG2 (0x07) +#define AW8624X_REG_PLAYCFG3 (0x08) +#define AW8624X_REG_PLAYCFG4 (0x09) +#define AW8624X_REG_WAVCFG1 (0x0A) +#define AW8624X_REG_WAVCFG2 (0x0B) +#define AW8624X_REG_WAVCFG3 (0x0C) +#define AW8624X_REG_WAVCFG4 (0x0D) +#define AW8624X_REG_WAVCFG5 (0x0E) +#define AW8624X_REG_WAVCFG6 (0x0F) +#define AW8624X_REG_WAVCFG7 (0x10) +#define AW8624X_REG_WAVCFG8 (0x11) +#define AW8624X_REG_WAVCFG9 (0x12) +#define AW8624X_REG_WAVCFG10 (0x13) +#define AW8624X_REG_WAVCFG11 (0x14) +#define AW8624X_REG_WAVCFG12 (0x15) +#define AW8624X_REG_WAVCFG13 (0x16) +#define AW8624X_REG_CONTCFG1 (0x18) +#define AW8624X_REG_CONTCFG2 (0x19) +#define AW8624X_REG_CONTCFG3 (0x1A) +#define AW8624X_REG_CONTCFG4 (0x1B) +#define AW8624X_REG_CONTCFG5 (0x1C) +#define AW8624X_REG_CONTCFG6 (0x1D) +#define AW8624X_REG_CONTCFG7 (0x1E) +#define AW8624X_REG_CONTCFG8 (0x1F) +#define AW8624X_REG_CONTCFG9 (0x20) +#define AW8624X_REG_CONTCFG10 (0x21) +#define AW8624X_REG_CONTCFG11 (0x22) +#define AW8624X_REG_CONTCFG12 (0x23) +#define AW8624X_REG_CONTCFG13 (0x24) +#define AW8624X_REG_CONTRD14 (0x25) +#define AW8624X_REG_CONTRD15 (0x26) +#define AW8624X_REG_CONTRD16 (0x27) +#define AW8624X_REG_CONTRD17 (0x28) +#define AW8624X_REG_CONTRD18 (0x29) +#define AW8624X_REG_CONTRD19 (0x2A) +#define AW8624X_REG_CONTRD20 (0x2B) +#define AW8624X_REG_CONTRD21 (0x2C) +#define AW8624X_REG_RTPCFG1 (0x2D) +#define AW8624X_REG_RTPCFG2 (0x2E) +#define AW8624X_REG_RTPCFG3 (0x2F) +#define AW8624X_REG_RTPCFG4 (0x30) +#define AW8624X_REG_RTPCFG5 (0x31) +#define AW8624X_REG_RTPDATA (0x32) +#define AW8624X_REG_TRGCFG1 (0x33) +#define AW8624X_REG_TRGCFG2 (0x34) +#define AW8624X_REG_TRGCFG3 (0x35) +#define AW8624X_REG_TRGCFG4 (0x36) +#define AW8624X_REG_TRGCFG5 (0x37) +#define AW8624X_REG_TRGCFG6 (0x38) +#define AW8624X_REG_TRGCFG7 (0x39) +#define AW8624X_REG_TRGCFG8 (0x3A) +#define AW8624X_REG_GLBCFG2 (0x3C) +#define AW8624X_REG_GLBCFG4 (0x3E) +#define AW8624X_REG_GLBRD5 (0x3F) +#define AW8624X_REG_RAMADDRH (0x40) +#define AW8624X_REG_RAMADDRL (0x41) +#define AW8624X_REG_RAMDATA (0x42) +#define AW8624X_REG_SYSCTRL1 (0x43) +#define AW8624X_REG_SYSCTRL2 (0x44) +#define AW8624X_REG_SYSCTRL3 (0x45) +#define AW8624X_REG_SYSCTRL4 (0x46) +#define AW8624X_REG_SYSCTRL5 (0x49) +#define AW8624X_REG_SYSCTRL6 (0x4A) +#define AW8624X_REG_PWMCFG1 (0x4C) +#define AW8624X_REG_PWMCFG3 (0x4E) +#define AW8624X_REG_PWMCFG4 (0x4F) +#define AW8624X_REG_DET_LO (0x50) +#define AW8624X_REG_DETCFG1 (0x51) +#define AW8624X_REG_DETCFG2 (0x52) +#define AW8624X_REG_DET_RL (0x53) +#define AW8624X_REG_DET_OS (0x54) +#define AW8624X_REG_DET_VBAT (0x55) +#define AW8624X_REG_CHIPIDH (0x57) +#define AW8624X_REG_CHIPIDL (0x58) +#define AW8624X_REG_TRIMCFG1 (0x59) +#define AW8624X_REG_TRIMCFG3 (0x5A) +#define AW8624X_REG_EFRD9 (0x64) +#define AW8624X_REG_ANACFG8 (0x77) +/****************************************************** + * Common Register Detail + *****************************************************/ +#define AW_BIT_RESET (0xAA) +/* GLB_STATE */ +#define AW_BIT_GLBRD_STATE_MASK (15<<0) +#define AW_BIT_STATE_STANDBY (0<<0) +#define AW_BIT_STATE_WAKEUP (1<<0) +#define AW_BIT_STATE_STARTUP (2<<0) +#define AW_BIT_STATE_WAIT (3<<0) +#define AW_BIT_STATE_CONT_GO (6<<0) +#define AW_BIT_STATE_RAM_GO (7<<0) +#define AW_BIT_STATE_RTP_GO (8<<0) +#define AW_BIT_STATE_TRIG_GO (9<<0) +#define AW_BIT_STATE_BRAKE (11<<0) +#define AW_BIT_STATE_END (12<<0) +/****************************************************** + * AW862XX Register Detail + *****************************************************/ +/* SYSST: reg 0x01 RO */ +#define AW862XX_BIT_SYSST_UVLS (1<<5) +#define AW862XX_BIT_SYSST_FF_AES (1<<4) +#define AW862XX_BIT_SYSST_FF_AFS (1<<3) +#define AW862XX_BIT_SYSST_OCDS (1<<2) +#define AW862XX_BIT_SYSST_OTS (1<<1) +#define AW862XX_BIT_SYSST_DONES (1<<0) + +/* SYSINT: reg 0x02 RC */ +#define AW862XX_BIT_SYSINT_UVLI (1<<5) +#define AW862XX_BIT_SYSINT_FF_AEI (1<<4) +#define AW862XX_BIT_SYSINT_FF_AFI (1<<3) +#define AW862XX_BIT_SYSINT_OCDI (1<<2) +#define AW862XX_BIT_SYSINT_OTI (1<<1) +#define AW862XX_BIT_SYSINT_DONEI (1<<0) + +/* SYSINTM: reg 0x03 RW */ +#define AW862XX_BIT_SYSINTM_UVLM_MASK (~(1<<5)) +#define AW862XX_BIT_SYSINTM_UVLM_OFF (1<<5) +#define AW862XX_BIT_SYSINTM_UVLM_ON (0<<5) +#define AW862XX_BIT_SYSINTM_FF_AEM_MASK (~(1<<4)) +#define AW862XX_BIT_SYSINTM_FF_AEM_OFF (1<<4) +#define AW862XX_BIT_SYSINTM_FF_AEM_ON (0<<4) +#define AW862XX_BIT_SYSINTM_FF_AFM_MASK (~(1<<3)) +#define AW862XX_BIT_SYSINTM_FF_AFM_OFF (1<<3) +#define AW862XX_BIT_SYSINTM_FF_AFM_ON (0<<3) +#define AW862XX_BIT_SYSINTM_OCDM_MASK (~(1<<2)) +#define AW862XX_BIT_SYSINTM_OCDM_OFF (1<<2) +#define AW862XX_BIT_SYSINTM_OCDM_ON (0<<2) +#define AW862XX_BIT_SYSINTM_OTM_MASK (~(1<<1)) +#define AW862XX_BIT_SYSINTM_OTM_OFF (1<<1) +#define AW862XX_BIT_SYSINTM_OTM_ON (0<<1) +#define AW862XX_BIT_SYSINTM_DONEM_MASK (~(1<<0)) +#define AW862XX_BIT_SYSINTM_DONEM_OFF (1<<0) +#define AW862XX_BIT_SYSINTM_DONEM_ON (0<<0) + +/* SYSST2: reg 0x04 RO */ +#define AW862XX_BIT_SYSST2_RAM_ADDR_ER (1<<7) +#define AW862XX_BIT_SYSST2_TRG_ADDR_ER (1<<6) +#define AW862XX_BIT_SYSST2_VBG_OK (1<<3) +#define AW862XX_BIT_SYSST2_LDO_OK (1<<2) +#define AW862XX_BIT_SYSST2_FF_FULL (1<<1) +#define AW862XX_BIT_SYSST2_FF_EMPTY (1<<0) + +/* SYSER: reg 0x05 RC */ +#define AW862XX_BIT_SYSER_I2S_ERR (1<<7) +#define AW862XX_BIT_SYSER_TRIG1_EVENT (1<<6) +#define AW862XX_BIT_SYSER_TRIG2_EVENT (1<<5) +#define AW862XX_BIT_SYSER_TRIG3_EVENT (1<<4) +#define AW862XX_BIT_SYSER_OV (1<<3) +#define AW862XX_BIT_SYSER_ADDR_ER (1<<2) +#define AW862XX_BIT_SYSER_FF_ER (1<<1) +#define AW862XX_BIT_SYSER_PLL_REF_ER (1<<0) + +/* PLAYCFG3: reg 0x08 RW */ +#define AW862XX_BIT_PLAYCFG3_STOP_MODE_MASK (~(1<<5)) +#define AW862XX_BIT_PLAYCFG3_STOP_MODE_NOW (1<<5) +#define AW862XX_BIT_PLAYCFG3_STOP_MODE_LATER (0<<5) +#define AW862XX_BIT_PLAYCFG3_BRK_EN_MASK (~(1<<2)) +#define AW862XX_BIT_PLAYCFG3_BRK (1<<2) +#define AW862XX_BIT_PLAYCFG3_BRK_ENABLE (1<<2) +#define AW862XX_BIT_PLAYCFG3_BRK_DISABLE (0<<2) +#define AW862XX_BIT_PLAYCFG3_PLAY_MODE_MASK (~(3<<0)) +#define AW862XX_BIT_PLAYCFG3_PLAY_MODE_STOP (3<<0) +#define AW862XX_BIT_PLAYCFG3_PLAY_MODE_CONT (2<<0) +#define AW862XX_BIT_PLAYCFG3_PLAY_MODE_RTP (1<<0) +#define AW862XX_BIT_PLAYCFG3_PLAY_MODE_RAM (0<<0) + +/* PLAYCFG4: reg 0x09 RW */ +#define AW862XX_BIT_PLAYCFG4_STOP_MASK (~(1<<1)) +#define AW862XX_BIT_PLAYCFG4_STOP_ON (1<<1) +#define AW862XX_BIT_PLAYCFG4_STOP_OFF (0<<1) +#define AW862XX_BIT_PLAYCFG4_GO_MASK (~(1<<0)) +#define AW862XX_BIT_PLAYCFG4_GO_ON (1<<0) +#define AW862XX_BIT_PLAYCFG4_GO_OFF (0<<0) + +/* WAVCFG1-8: reg 0x0A - reg 0x11 RW */ +#define AW862XX_BIT_WAVCFG_SEQWAIT_MASK (~(1<<7)) +#define AW862XX_BIT_WAVCFG_SEQWAIT_TIME (1<<7) +#define AW862XX_BIT_WAVCFG_SEQWAIT_NUMBER (0<<7) +#define AW862XX_BIT_WAVCFG_SEQ (0x7F) + +/* WAVCFG9-12: reg 0x12 - reg 0x15 RW */ +#define AW862XX_BIT_WAVLOOP_SEQ_ODD_MASK (~(0x0F<<4)) +#define AW862XX_BIT_WAVLOOP_SEQ_ODD_INIFINITELY (0x0F<<4) +#define AW862XX_BIT_WAVLOOP_SEQ_EVEN_MASK (~(0x0F<<0)) +#define AW862XX_BIT_WAVLOOP_SEQ_EVEN_INIFINITELY (0x0F<<0) +#define AW862XX_BIT_WAVLOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG9: reg 0x12 RW */ +#define AW862XX_BIT_WAVCFG9_SEQ1LOOP_MASK (~(0x0F<<4)) +#define AW862XX_BIT_WAVCFG9_SEQ1LOOP_INIFINITELY (0x0F<<4) +#define AW862XX_BIT_WAVCFG9_SEQ2LOOP_MASK (~(0x0F<<0)) +#define AW862XX_BIT_WAVCFG9_SEQ2LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG10: reg 0x13 RW */ +#define AW862XX_BIT_WAVCFG10_SEQ3LOOP_MASK (~(0x0F<<4)) +#define AW862XX_BIT_WAVCFG10_SEQ3LOOP_INIFINITELY (0x0F<<4) +#define AW862XX_BIT_WAVCFG10_SEQ4LOOP_MASK (~(0x0F<<0)) +#define AW862XX_BIT_WAVCFG10_SEQ4LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG11: reg 0x14 RW */ +#define AW862XX_BIT_WAVCFG11_SEQ5LOOP_MASK (~(0x0F<<4)) +#define AW862XX_BIT_WAVCFG11_SEQ5LOOP_INIFINITELY (0x0F<<4) +#define AW862XX_BIT_WAVCFG11_SEQ6LOOP_MASK (~(0x0F<<0)) +#define AW862XX_BIT_WAVCFG11_SEQ6LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG12: reg 0x15 RW */ +#define AW862XX_BIT_WAVCFG12_SEQ7LOOP_MASK (~(0x0F<<4)) +#define AW862XX_BIT_WAVCFG12_SEQ7LOOP_INIFINITELY (0x0F<<4) +#define AW862XX_BIT_WAVCFG12_SEQ8LOOP_MASK (~(0x0F<<0)) +#define AW862XX_BIT_WAVCFG12_SEQ8LOOP_INIFINITELY (0x0F<<0) + +/***************** CONT *****************/ +/* CONTCFG1: reg 0x18 RW */ +#define AW862XX_BIT_CONTCFG1_EDGE_FRE_MASK (~(0x0F<<4)) +#define AW862XX_BIT_CONTCFG1_EN_F0_DET_MASK (~(1<<3)) +#define AW862XX_BIT_CONTCFG1_F0_DET_ENABLE (1<<3) +#define AW862XX_BIT_CONTCFG1_F0_DET_DISABLE (0<<3) +#define AW862XX_BIT_CONTCFG1_SIN_MODE_MASK (~(1<<0)) +#define AW862XX_BIT_CONTCFG1_SIN_MODE_COS (1<<0) +#define AW862XX_BIT_CONTCFG1_SIN_MODE_SINE (0<<0) + +/* CONTCFG5: reg 0x1C RW */ +#define AW862XX_BIT_CONTCFG5_BRK_GAIN_MASK (~(0x0F<<0)) + +/* CONTCFG6: reg 0x1D RW */ +#define AW862XX_BIT_CONTCFG6_TRACK_EN_MASK (~(1<<7)) +#define AW862XX_BIT_CONTCFG6_TRACK_ENABLE (1<<7) +#define AW862XX_BIT_CONTCFG6_TRACK_DISABLE (0<<7) +#define AW862XX_BIT_CONTCFG6_DRV1_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG7: reg 0x1E RW */ +#define AW862XX_BIT_CONTCFG7_DRV2_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG13: reg 0x24 RW */ +#define AW862XX_BIT_CONTCFG13_TSET_MASK (~(0x0F<<4)) +#define AW862XX_BIT_CONTCFG13_BEME_SET_MASK (~(0x0F<<0)) + +/***************** RTP *****************/ +/* RTPCFG1: reg 0x2D RW */ +#define AW862XX_BIT_RTPCFG1_ADDRH_MASK (~(0x0F<<0)) + +#define AW862XX_BIT_RTPCFG1_SRAM_SIZE_2K_MASK (~(1<<5)) +#define AW862XX_BIT_RTPCFG1_SRAM_SIZE_2K_EN (1<<5) +#define AW862XX_BIT_RTPCFG1_SRAM_SIZE_2K_DIS (0<<5) + +#define AW862XX_BIT_RTPCFG1_SRAM_SIZE_1K_MASK (~(1<<4)) +#define AW862XX_BIT_RTPCFG1_SRAM_SIZE_1K_EN (1<<4) +#define AW862XX_BIT_RTPCFG1_SRAM_SIZE_1K_DIS (0<<4) + +/* RTPCFG3: reg 0x2F RW */ +#define AW862XX_BIT_RTPCFG3_FIFO_AEH_MASK (~(0x0F<<4)) +#define AW862XX_BIT_RTPCFG3_FIFO_AFH_MASK (~(0x0F<<0)) +#define AW862XX_BIT_RTPCFG3_FIFO_AEH (0x0F<<4) +#define AW862XX_BIT_RTPCFG3_FIFO_AFH (0x0F<<0) + +/***************** TRIGGER *****************/ +#define AW862XX_BIT_TRG_ENABLE_MASK (~(1<<7)) +#define AW862XX_BIT_TRG_ENABLE (1<<7) +#define AW862XX_BIT_TRG_DISABLE (0<<7) +#define AW862XX_BIT_TRG_SEQ_MASK (~(0x7F<<0)) + +/* TRGCFG1: reg 0x33 RW */ +#define AW862XX_BIT_TRGCFG1_TRG1_POS_MASK (~(1<<7)) +#define AW862XX_BIT_TRGCFG1_TRG1_POS_ENABLE (1<<7) +#define AW862XX_BIT_TRGCFG1_TRG1_POS_DISABLE (0<<7) +#define AW862XX_BIT_TRGCFG1_TRG1SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG2: reg 0x34 RW */ +#define AW862XX_BIT_TRGCFG2_TRG2_POS_MASK (~(1<<7)) +#define AW862XX_BIT_TRGCFG2_TRG2_POS_ENABLE (1<<7) +#define AW862XX_BIT_TRGCFG2_TRG2_POS_DISABLE (0<<7) +#define AW862XX_BIT_TRGCFG2_TRG2SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG3: reg 0x35 RW */ +#define AW862XX_BIT_TRGCFG3_TRG3_POS_MASK (~(1<<7)) +#define AW862XX_BIT_TRGCFG3_TRG3_POS_ENABLE (1<<7) +#define AW862XX_BIT_TRGCFG3_TRG3_POS_DISABLE (0<<7) +#define AW862XX_BIT_TRGCFG3_TRG3SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG4: reg 0x36 RW */ +#define AW862XX_BIT_TRGCFG4_TRG1_NEG_MASK (~(1<<7)) +#define AW862XX_BIT_TRGCFG4_TRG1_NEG_ENABLE (1<<7) +#define AW862XX_BIT_TRGCFG4_TRG1_NEG_DISABLE (0<<7) +#define AW862XX_BIT_TRGCFG4_TRG1SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG5: reg 0x37 RW */ +#define AW862XX_BIT_TRGCFG5_TRG2_NEG_MASK (~(1<<7)) +#define AW862XX_BIT_TRGCFG5_TRG2_NEG_ENABLE (1<<7) +#define AW862XX_BIT_TRGCFG5_TRG2_NEG_DISABLE (0<<7) +#define AW862XX_BIT_TRGCFG5_TRG2SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG6: reg 0x38 RW */ +#define AW862XX_BIT_TRGCFG6_TRG3_NEG_MASK (~(1<<7)) +#define AW862XX_BIT_TRGCFG6_TRG3_NEG_ENABLE (1<<7) +#define AW862XX_BIT_TRGCFG6_TRG3_NEG_DISABLE (0<<7) +#define AW862XX_BIT_TRGCFG6_TRG3SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG7: reg 0x39 RW */ +#define AW862XX_BIT_TRGCFG7_TRG1_POR_LEV_BRK_MASK (~(7<<5)) +#define AW862XX_BIT_TRGCFG7_TRG2_POR_LEV_BRK_MASK (~(7<<1)) +#define AW862XX_BIT_TRGCFG7_TRG1_POLAR_MASK (~(1<<7)) +#define AW862XX_BIT_TRGCFG7_TRG1_POLAR_NEG (1<<7) +#define AW862XX_BIT_TRGCFG7_TRG1_POLAR_POS (0<<7) +#define AW862XX_BIT_TRGCFG7_TRG1_MODE_MASK (~(1<<6)) +#define AW862XX_BIT_TRGCFG7_TRG1_MODE_LEVEL (1<<6) +#define AW862XX_BIT_TRGCFG7_TRG1_MODE_EDGE (0<<6) +#define AW862XX_BIT_TRGCFG7_TRG1_AUTO_BRK_MASK (~(1<<5)) +#define AW862XX_BIT_TRGCFG7_TRG1_AUTO_BRK_ENABLE (1<<5) +#define AW862XX_BIT_TRGCFG7_TRG1_AUTO_BRK_DISABLE (0<<5) +#define AW862XX_BIT_TRGCFG7_TRG2_POLAR_MASK (~(1<<3)) +#define AW862XX_BIT_TRGCFG7_TRG2_POLAR_NEG (1<<3) +#define AW862XX_BIT_TRGCFG7_TRG2_POLAR_POS (0<<3) +#define AW862XX_BIT_TRGCFG7_TRG2_MODE_MASK (~(1<<2)) +#define AW862XX_BIT_TRGCFG7_TRG2_MODE_LEVEL (1<<2) +#define AW862XX_BIT_TRGCFG7_TRG2_MODE_EDGE (0<<2) +#define AW862XX_BIT_TRGCFG7_TRG2_AUTO_BRK_MASK (~(1<<1)) +#define AW862XX_BIT_TRGCFG7_TRG2_AUTO_BRK_ENABLE (1<<1) +#define AW862XX_BIT_TRGCFG7_TRG2_AUTO_BRK_DISABLE (0<<1) + +/* TRGCFG8: reg 0x3A RW */ +#define AW862XX_BIT_TRGCFG8_TRG3_POR_LEV_BRK_MASK (~(7<<5)) +#define AW862XX_BIT_TRGCFG8_TRG3_POLAR_MASK (~(1<<7)) +#define AW862XX_BIT_TRGCFG8_TRG3_POLAR_NEG (1<<7) +#define AW862XX_BIT_TRGCFG8_TRG3_POLAR_POS (0<<7) +#define AW862XX_BIT_TRGCFG8_TRG3_MODE_MASK (~(1<<6)) +#define AW862XX_BIT_TRGCFG8_TRG3_MODE_LEVEL (1<<6) +#define AW862XX_BIT_TRGCFG8_TRG3_MODE_EDGE (0<<6) +#define AW862XX_BIT_TRGCFG8_TRG3_AUTO_BRK_MASK (~(1<<5)) +#define AW862XX_BIT_TRGCFG8_TRG3_AUTO_BRK_ENABLE (1<<5) +#define AW862XX_BIT_TRGCFG8_TRG3_AUTO_BRK_DISABLE (0<<5) +#define AW862XX_BIT_TRGCFG8_TRG_TRIG1_MODE_MASK (~(3<<3)) +#define AW862XX_BIT_TRGCFG8_PWM_LRA (0<<3) +#define AW862XX_BIT_TRGCFG8_PWM_ERA (1<<3) +#define AW862XX_BIT_TRGCFG8_TRIG1 (2<<3) +#define AW862XX_BIT_TRGCFG8_DISABLE (3<<3) +#define AW862XX_BIT_TRGCFG8_TRG1_STOP_MASK (~(1<<2)) +#define AW862XX_BIT_TRGCFG8_TRG1_STOP (1<<2) +#define AW862XX_BIT_TRGCFG8_TRG2_STOP_MASK (~(1<<1)) +#define AW862XX_BIT_TRGCFG8_TRG2_STOP (1<<1) +#define AW862XX_BIT_TRGCFG8_TRG3_STOP_MASK (~(1<<0)) +#define AW862XX_BIT_TRGCFG8_TRG3_STOP (1<<0) + +/* GLBCFG2: reg 0x3C RW */ +/* START_DLY */ +#define AW862XX_BIT_GLBCFG2_START_DLY_250US (0x0C) + +/* GLBCFG4: reg 0x3E RW */ +#define AW862XX_BIT_GLBCFG4_GO_PRIO_MASK (~(3<<6)) +#define AW862XX_BIT_GLBCFG4_TRG3_PRIO_MASK (~(3<<4)) +#define AW862XX_BIT_GLBCFG4_TRG2_PRIO_MASK (~(3<<2)) +#define AW862XX_BIT_GLBCFG4_TRG1_PRIO_MASK (~(3<<0)) + +/* GLBRD5: reg 0x3F R0 */ +/* GLB_STATE */ +#define AW862XX_BIT_GLBRD5_STATE (15<<0) +#define AW862XX_BIT_GLBRD5_STATE_STANDBY (0<<0) +#define AW862XX_BIT_GLBRD5_STATE_WAKEUP (1<<0) +#define AW862XX_BIT_GLBRD5_STATE_STARTUP (2<<0) +#define AW862XX_BIT_GLBRD5_STATE_WAIT (3<<0) +#define AW862XX_BIT_GLBRD5_STATE_CONT_GO (6<<0) +#define AW862XX_BIT_GLBRD5_STATE_RAM_GO (7<<0) +#define AW862XX_BIT_GLBRD5_STATE_RTP_GO (8<<0) +#define AW862XX_BIT_GLBRD5_STATE_TRIG_GO (9<<0) +#define AW862XX_BIT_GLBRD5_STATE_I2S_GO (10<<0) +#define AW862XX_BIT_GLBRD5_STATE_BRAKE (11<<0) +#define AW862XX_BIT_GLBRD5_STATE_END (12<<0) +/* RAMADDRH: reg 0x40 RWS */ +#define AW862XX_BIT_RAMADDRH_MASK (~(63<<0)) + +/***************** SYSCTRL *****************/ +/* SYSCTRL1: reg 0x43 RW */ +#define AW862XX_BIT_SYSCTRL1_VBAT_MODE_MASK (~(1<<7)) +#define AW862XX_BIT_SYSCTRL1_VBAT_MODE_HW (1<<7) +#define AW862XX_BIT_SYSCTRL1_VBAT_MODE_SW (0<<7) +#define AW862XX_BIT_SYSCTRL1_PERP_MASK (~(1<<6)) +#define AW862XX_BIT_SYSCTRL1_PERP_ON (1<<6) +#define AW862XX_BIT_SYSCTRL1_PERP_OFF (0<<6) +#define AW862XX_BIT_SYSCTRL1_CLK_SEL_MASK (~(3<<4)) +#define AW862XX_BIT_SYSCTRL1_CLK_SEL_OSC (1<<4) +#define AW862XX_BIT_SYSCTRL1_CLK_SEL_AUTO (0<<4) +#define AW862XX_BIT_SYSCTRL1_RAMINIT_MASK (~(1<<3)) +#define AW862XX_BIT_SYSCTRL1_RAMINIT_ON (1<<3) +#define AW862XX_BIT_SYSCTRL1_RAMINIT_OFF (0<<3) +#define AW862XX_BIT_SYSCTRL1_EN_FIR_MASK (~(1<<2)) +#define AW862XX_BIT_SYSCTRL1_FIR_ENABLE (0<<2) +#define AW862XX_BIT_SYSCTRL1_WAKE_MODE_MASK (~(1<<1)) +#define AW862XX_BIT_SYSCTRL1_WAKE_MODE_WAKEUP (1<<1) +#define AW862XX_BIT_SYSCTRL1_WAKE_MODE_BST (0<<1) +#define AW862XX_BIT_SYSCTRL1_RTP_CLK_MASK (~(1<<0)) +#define AW862XX_BIT_SYSCTRL1_RTP_PLL (1<<0) +#define AW862XX_BIT_SYSCTRL1_RTP_OSC (0<<0) + +/* SYSCTRL2: reg 0x44 RW */ +#define AW862XX_BIT_SYSCTRL2_WAKE_MASK (~(1<<7)) +#define AW862XX_BIT_SYSCTRL2_WAKE_ON (1<<7) +#define AW862XX_BIT_SYSCTRL2_WAKE_OFF (0<<7) +#define AW862XX_BIT_SYSCTRL2_STANDBY_MASK (~(1<<6)) +#define AW862XX_BIT_SYSCTRL2_STANDBY_ON (1<<6) +#define AW862XX_BIT_SYSCTRL2_STANDBY_OFF (0<<6) +#define AW862XX_BIT_SYSCTRL2_RTP_DLY_MASK (~(3<<4)) +#define AW862XX_BIT_SYSCTRL2_INTN_PIN_MASK (~(1<<3)) +#define AW862XX_BIT_SYSCTRL2_INTN (1<<3) +#define AW862XX_BIT_SYSCTRL2_TRIG1 (0<<3) +#define AW862XX_BIT_SYSCTRL2_WCK_PIN_MASK (~(1<<2)) +#define AW862XX_BIT_SYSCTRL2_ENABLE_TRIG2 (1<<2) +#define AW862XX_BIT_SYSCTRL2_DISENABLE_TRIG2 (0<<2) +#define AW862XX_BIT_SYSCTRL2_WAVDAT_MODE_MASK (~(3<<0)) +#define AW862XX_BIT_SYSCTRL2_RATE (3<<0) +#define AW862XX_BIT_SYSCTRL2_RATE_12K (2<<0) +#define AW862XX_BIT_SYSCTRL2_RATE_24K (0<<0) +#define AW862XX_BIT_SYSCTRL2_RATE_48K (1<<0) + +/* SYSCTRL7: reg 0x49 RW */ +#define AW862XX_BIT_SYSCTRL7_GAIN_BYPASS_MASK (~(1<<6)) +#define AW862XX_BIT_SYSCTRL7_GAIN_CHANGEABLE (1<<6) +#define AW862XX_BIT_SYSCTRL7_GAIN_FIXED (0<<6) +#define AW862XX_BIT_SYSCTRL7_GAIN (0x07) + +#define AW862XX_BIT_SYSCTRL7_INT_EDGE_MODE_MASK (~(1<<5)) +#define AW862XX_BIT_SYSCTRL7_INT_EDGE_MODE_POS (0<<5) +#define AW862XX_BIT_SYSCTRL7_INT_EDGE_MODE_BOTH (1<<5) +#define AW862XX_BIT_SYSCTRL7_INT_MODE_MASK (~(1<<4)) +#define AW862XX_BIT_SYSCTRL7_INT_MODE_EDGE (1<<4) +#define AW862XX_BIT_SYSCTRL7_INT_MODE_LEVEL (0<<4) + +#define AW862XX_BIT_SYSCTRL7_INTP_MASK (~(1<<3)) +#define AW862XX_BIT_SYSCTRL7_INTP_HIGH (1<<3) +#define AW862XX_BIT_SYSCTRL7_INTP_LOW (0<<3) +#define AW862XX_BIT_SYSCTRL7_D2S_GAIN_MASK (~(7<<0)) +#define AW862XX_BIT_SYSCTRL7_D2S_GAIN_1 (0<<0) +#define AW862XX_BIT_SYSCTRL7_D2S_GAIN_2 (1<<0) +#define AW862XX_BIT_SYSCTRL7_D2S_GAIN_4 (2<<0) +#define AW862XX_BIT_SYSCTRL7_D2S_GAIN_5 (3<<0) +#define AW862XX_BIT_SYSCTRL7_D2S_GAIN_8 (4<<0) +#define AW862XX_BIT_SYSCTRL7_D2S_GAIN_10 (5<<0) +#define AW862XX_BIT_SYSCTRL7_D2S_GAIN_20 (6<<0) +#define AW862XX_BIT_SYSCTRL7_D2S_GAIN_40 (7<<0) + +/* PWMCFG1: reg 0x4C RW */ +#define AW862XX_BIT_PWMCFG1_PRC_EN_MASK (~(1<<7)) +#define AW862XX_BIT_PWMCFG1_PRC_ENABLE (1<<7) +#define AW862XX_BIT_PWMCFG1_PRC_DISABLE (0<<7) +#define AW862XX_BIT_PWMCFG1_PRCTIME_MASK (~(0x7F<<0)) + +/* PWMCFG2: reg 0x4D RW */ +#define AW862XX_BIT_PWMCFG2_REF_SEL_MASK (~(1<<5)) +#define AW862XX_BIT_PWMCFG2_REF_SEL_TRIANGLE (1<<5) +#define AW862XX_BIT_PWMCFG2_REF_SEL_SAWTOOTH (0<<5) +#define AW862XX_BIT_PWMCFG2_PD_HWM_MASK (~(1<<4)) +#define AW862XX_BIT_PWMCFG2_PD_HWM_ON (1<<4) +#define AW862XX_BIT_PWMCFG2_PWMOE_MASK (~(1<<3)) +#define AW862XX_BIT_PWMCFG2_PWMOE_ON (1<<3) +#define AW862XX_BIT_PWMCFG2_PWMFRC_MASK (~(7<<0)) + +/* PWMCFG3: reg 0x4E RW */ +#define AW862XX_BIT_PWMCFG3_PR_EN_MASK (~(1<<7)) +#define AW862XX_BIT_PWMCFG3_PR_ENABLE (1<<7) +#define AW862XX_BIT_PWMCFG3_PR_DISABLE (0<<7) +#define AW862XX_BIT_PWMCFG3_PRLVL_MASK (~(0x7F<<0)) +#define AW862XX_BIT_PWMCFG3_PRLVL_DEFAULT_VALUE (0x3F) + +/* PWMCFG4: reg 0x4F RW */ +/* PRTIME */ +#define AW862XX_PWMCFG4_PRTIME_DEFAULT_VALUE (0x32) + +/* DETCFG1: reg 0x51 RW */ +#define AW862XX_BIT_DETCFG1_FTS_GO_MASK (~(1<<7)) +#define AW862XX_BIT_DETCFG1_FTS_GO_ENABLE (1<<7) +#define AW862XX_BIT_DETCFG1_TEST_GO_MASK (~(1<<6)) +#define AW862XX_BIT_DETCFG1_TEST_GO_ENABLE (1<<6) +#define AW862XX_BIT_DETCFG1_ADO_SLOT_MODE_MASK (~(1<<5)) +#define AW862XX_BIT_DETCFG1_ADO_SLOT_ADC_32 (1<<5) +#define AW862XX_BIT_DETCFG1_ADO_SLOT_ADC_256 (0<<5) +#define AW862XX_BIT_DETCFG1_RL_OS_MASK (~(1<<4)) +#define AW862XX_BIT_DETCFG1_RL (1<<4) +#define AW862XX_BIT_DETCFG1_OS (0<<4) +#define AW862XX_BIT_DETCFG1_PRCT_MODE_MASK (~(1<<3)) +#define AW862XX_BIT_DETCFG1_PRCT_MODE_INVALID (1<<3) +#define AW862XX_BIT_DETCFG1_PRCT_MODE_VALID (0<<3) +#define AW862XX_BIT_DETCFG1_CLK_ADC_MASK (~(7<<0)) +#define AW862XX_BIT_DETCFG1_CLK_ADC_12M (0<<0) +#define AW862XX_BIT_DETCFG1_CLK_ADC_6M (1<<0) +#define AW862XX_BIT_DETCFG1_CLK_ADC_3M (2<<0) +#define AW862XX_BIT_DETCFG1_CLK_ADC_1M5 (3<<0) +#define AW862XX_BIT_DETCFG1_CLK_ADC_M75 (4<<0) +#define AW862XX_BIT_DETCFG1_CLK_ADC_M37 (5<<0) +#define AW862XX_BIT_DETCFG1_CLK_ADC_M18 (6<<0) +#define AW862XX_BIT_DETCFG1_CLK_ADC_M09 (7<<0) + +/* DETCFG2: reg 0x52 RW */ +#define AW862XX_BIT_DETCFG2_VBAT_GO_MASK (~(1<<1)) +#define AW862XX_BIT_DETCFG2_VABT_GO_ON (1<<1) +#define AW862XX_BIT_DETCFG2_DIAG_GO_MASK (~(1<<0)) +#define AW862XX_BIT_DETCFG2_DIAG_GO_ON (1<<0) + +/* DET_LO: reg 0x57 RW */ +#define AW862XX_BIT_DET_LO_TEST_MASK (~(3<<6)) +#define AW862XX_BIT_DET_LO_VBAT_MASK (~(3<<4)) +#define AW862XX_BIT_DET_LO_VBAT (3<<4) +#define AW862XX_BIT_DET_LO_OS_MASK (~(3<<2)) +#define AW862XX_BIT_DET_LO_RL_MASK (~(3<<0)) +#define AW862XX_BIT_DET_LO_RL (3<<0) + +/* TRIMCFG1: reg:0x58 RW */ +#define AW862XX_BIT_TRIMCFG1_RL_TRIM_SRC_MASK (~(1<<6)) +#define AW862XX_BIT_TRIMCFG1_RL_TRIM_SRC_REG (1<<6) +#define AW862XX_BIT_TRIMCFG1_RL_TRIM_SRC_EFUSE (0<<6) +#define AW862XX_BIT_TRIMCFG1_TRIM_RL_MASK (~(63<<0)) + +/* TRIMCFG3: reg:0x5A RW */ +#define AW862XX_BIT_TRIMCFG3_OSC_TRIM_SRC_MASK (~(1<<7)) +#define AW862XX_BIT_TRIMCFG3_OSC_TRIM_SRC_REG (1<<7) +#define AW862XX_BIT_TRIMCFG3_OSC_TRIM_SRC_EFUSE (0<<7) +#define AW862XX_BIT_TRIMCFG3_TRIM_LRA_MASK (~(63<<0)) + +/* TRIMCFG4: reg:0x5B RW */ +/* TRIM_OSC */ + +/* ANACFG8: reg:0x77 RW */ +#define AW862XX_BIT_ANACFG8_TRTF_CTRL_HDRV_MASK (~(3<<6)) +#define AW862XX_BIT_ANACFG8_TRTF_CTRL_HDRV (3<<6) + +/****************************************************** + * AW862X Register Detail + *****************************************************/ + /* SYSST 0x01 */ +#define AW862X_BIT_SYSST_OVS (1<<6) +#define AW862X_BIT_SYSST_UVLS (1<<5) +#define AW862X_BIT_SYSST_FF_AES (1<<4) +#define AW862X_BIT_SYSST_FF_AFS (1<<3) +#define AW862X_BIT_SYSST_OCDS (1<<2) +#define AW862X_BIT_SYSST_OTS (1<<1) +#define AW862X_BIT_SYSST_DONES (1<<0) + + /* SYSINT 0x02 */ +#define AW862X_BIT_SYSINT_OVI (1<<6) +#define AW862X_BIT_SYSINT_UVLI (1<<5) +#define AW862X_BIT_SYSINT_FF_AEI (1<<4) +#define AW862X_BIT_SYSINT_FF_AFI (1<<3) +#define AW862X_BIT_SYSINT_OCDI (1<<2) +#define AW862X_BIT_SYSINT_OTI (1<<1) +#define AW862X_BIT_SYSINT_DONEI (1<<0) + + /* SYSINTM 0x03 */ +#define AW862X_BIT_SYSINTM_OV_MASK (~(1<<6)) +#define AW862X_BIT_SYSINTM_OV_OFF (1<<6) +#define AW862X_BIT_SYSINTM_OV_EN (0<<6) +#define AW862X_BIT_SYSINTM_UVLO_MASK (~(1<<5)) +#define AW862X_BIT_SYSINTM_UVLO_OFF (1<<5) +#define AW862X_BIT_SYSINTM_UVLO_EN (0<<5) +#define AW862X_BIT_SYSINTM_FF_AE_MASK (~(1<<4)) +#define AW862X_BIT_SYSINTM_FF_AE_OFF (1<<4) +#define AW862X_BIT_SYSINTM_FF_AE_EN (0<<4) +#define AW862X_BIT_SYSINTM_FF_AF_MASK (~(1<<3)) +#define AW862X_BIT_SYSINTM_FF_AF_OFF (1<<3) +#define AW862X_BIT_SYSINTM_FF_AF_EN (0<<3) +#define AW862X_BIT_SYSINTM_OCD_MASK (~(1<<2)) +#define AW862X_BIT_SYSINTM_OCD_OFF (1<<2) +#define AW862X_BIT_SYSINTM_OCD_EN (0<<2) +#define AW862X_BIT_SYSINTM_OT_MASK (~(1<<1)) +#define AW862X_BIT_SYSINTM_OT_OFF (1<<1) +#define AW862X_BIT_SYSINTM_OT_EN (0<<1) +#define AW862X_BIT_SYSINTM_DONE_MASK (~(1<<0)) +#define AW862X_BIT_SYSINTM_DONE_OFF (1<<0) +#define AW862X_BIT_SYSINTM_DONE_EN (0<<0) + + /* SYSCTRL 0x04 */ +#define AW862X_BIT_SYSCTRL_WAVDAT_MODE_MASK (~(3<<6)) +#define AW862X_BIT_SYSCTRL_WAVDAT_MODE_4X (3<<6) +#define AW862X_BIT_SYSCTRL_WAVDAT_MODE_2X (0<<6) +#define AW862X_BIT_SYSCTRL_WAVDAT_MODE_1X (1<<6) +#define AW862X_BIT_SYSCTRL_RAMINIT_MASK (~(1<<5)) +#define AW862X_BIT_SYSCTRL_RAMINIT_EN (1<<5) +#define AW862X_BIT_SYSCTRL_RAMINIT_OFF (0<<5) +#define AW862X_BIT_SYSCTRL_PLAY_MODE_MASK (~(3<<2)) +#define AW862X_BIT_SYSCTRL_PLAY_MODE_CONT (2<<2) +#define AW862X_BIT_SYSCTRL_PLAY_MODE_RTP (1<<2) +#define AW862X_BIT_SYSCTRL_PLAY_MODE_RAM (0<<2) +#define AW862X_BIT_SYSCTRL_WORK_MODE_MASK (~(1<<0)) +#define AW862X_BIT_SYSCTRL_STANDBY (1<<0) +#define AW862X_BIT_SYSCTRL_ACTIVE (0<<0) + + /* GO 0x05 */ +#define AW862X_BIT_GO_MASK (~(1<<0)) +#define AW862X_BIT_GO_ENABLE (1<<0) +#define AW862X_BIT_GO_DISABLE (0<<0) + + /* WAVSEQ1 0x07 */ +#define AW862X_BIT_WAVSEQ1_WAIT (1<<7) +#define AW862X_BIT_WAVSEQ1_WAV_FRM_SEQ1_MASK (~(127<<0)) + + /* WAVSEQ2 0x08 */ +#define AW862X_BIT_WAVSEQ2_WAIT (1<<7) +#define AW862X_BIT_WAVSEQ2_WAV_FRM_SEQ2_MASK (~(127<<0)) + + /* WAVSEQ3 0x09 */ +#define AW862X_BIT_WAVSEQ3_WAIT (1<<7) +#define AW862X_BIT_WAVSEQ3_WAV_FRM_SEQ3_MASK (~(127<<0)) + + /* WAVSEQ4 0x0A */ +#define AW862X_BIT_WAVSEQ4_WAIT (1<<7) +#define AW862X_BIT_WAVSEQ4_WAV_FRM_SEQ4_MASK (~(127<<0)) + + /* WAVSEQ5 0X0B */ +#define AW862X_BIT_WAVSEQ5_WAIT (1<<7) +#define AW862X_BIT_WAVSEQ5_WAV_FRM_SEQ5_MASK (~(127<<0)) + + /* WAVSEQ6 0X0C */ +#define AW862X_BIT_WAVSEQ6_WAIT (1<<7) +#define AW862X_BIT_WAVSEQ6_WAV_FRM_SEQ6_MASK (~(127<<0)) + + /* WAVSEQ7 */ +#define AW862X_BIT_WAVSEQ7_WAIT (1<<7) +#define AW862X_BIT_WAVSEQ7_WAV_FRM_SEQ7_MASK (~(127<<0)) + + /* WAVSEQ8 */ +#define AW862X_BIT_WAVSEQ8_WAIT (1<<7) +#define AW862X_BIT_WAVSEQ8_WAV_FRM_SEQ8_MASK (~(127<<0)) + + /* WAVLOOP */ +#define AW862X_BIT_WAVLOOP_SEQN_MASK (~(15<<4)) +#define AW862X_BIT_WAVLOOP_SEQNP1_MASK (~(15<<0)) +#define AW862X_BIT_WAVLOOP_INIFINITELY (15<<0) + + /* WAVLOOP1 */ +#define AW862X_BIT_WAVLOOP1_SEQ1_MASK (~(15<<4)) +#define AW862X_BIT_WAVLOOP1_SEQ2_MASK (~(15<<0)) + + /* WAVLOOP2 */ +#define AW862X_BIT_WAVLOOP2_SEQ3_MASK (~(15<<4)) +#define AW862X_BIT_WAVLOOP2_SEQ4_MASK (~(15<<0)) + + /* WAVLOOP3 */ +#define AW862X_BIT_WAVLOOP3_SEQ5_MASK (~(15<<4)) +#define AW862X_BIT_WAVLOOP3_SEQ6_MASK (~(15<<0)) + + /* WAVLOOP4 */ +#define AW862X_BIT_WAVLOOP4_SEQ7_MASK (~(15<<4)) +#define AW862X_BIT_WAVLOOP4_SEQ8_MASK (~(15<<0)) + + /* TRGCFG1 */ +#define AW862X_BIT_TRGCFG1_TRG1_POLAR_MASK (~(1<<1)) +#define AW862X_BIT_TRGCFG1_TRG1_POLAR_NEG (1<<1) +#define AW862X_BIT_TRGCFG1_TRG1_POLAR_POS (0<<1) +#define AW862X_BIT_TRGCFG1_TRG1_EDGE_MASK (~(1<<0)) +#define AW862X_BIT_TRGCFG1_TRG1_EDGE_POS (1<<0) +#define AW862X_BIT_TRGCFG1_TRG1_EDGE_POS_NEG (0<<0) + + /* TRGCFG2 */ +#define AW862X_BIT_TRGCFG2_TRG1_ENABLE_MASK (~(1<<0)) +#define AW862X_BIT_TRGCFG2_TRG1_ENABLE (1<<0) +#define AW862X_BIT_TRGCFG2_TRG1_DISABLE (0<<0) + + /*DBGCTRL 0X20 */ +#define AW862X_BIT_DBGCTRL_INTN_TRG_SEL_MASK (~(1<<5)) +#define AW862X_BIT_DBGCTRL_INTN_SEL_ENABLE (1<<5) +#define AW862X_BIT_DBGCTRL_TRG_SEL_ENABLE (0<<5) +#define AW862X_BIT_DBGCTRL_INT_MODE_MASK (~(3<<2)) +#define AW862X_BIT_DBGCTRL_INTN_LEVEL_MODE (0<<2) +#define AW862X_BIT_DBGCTRL_INT_MODE_EDGE (1<<2) +#define AW862X_BIT_DBGCTRL_INTN_POSEDGE_MODE (2<<2) +#define AW862X_BIT_DBGCTRL_INTN_BOTH_EDGE_MODE (3<<2) + + /* DATCTRL */ +#define AW862X_BIT_DATCTRL_FC_MASK (~(3<<6)) +#define AW862X_BIT_DATCTRL_FC_1000HZ (3<<6) +#define AW862X_BIT_DATCTRL_FC_800HZ (3<<6) +#define AW862X_BIT_DATCTRL_FC_600HZ (1<<6) +#define AW862X_BIT_DATCTRL_FC_400HZ (0<<6) +#define AW862X_BIT_DATCTRL_LPF_ENABLE_MASK (~(1<<5)) +#define AW862X_BIT_DATCTRL_LPF_ENABLE (1<<5) +#define AW862X_BIT_DATCTRL_LPF_DISABLE (0<<5) + + /*PWMPRC 0X2D */ +#define AW862X_BIT_PWMPRC_PRC_EN_MASK (~(1<<7)) +#define AW862X_BIT_PWMPRC_PRC_ENABLE (1<<7) +#define AW862X_BIT_PWMPRC_PRC_DISABLE (0<<7) +#define AW862X_BIT_PWMPRC_PRCTIME_MASK (~(0x7f<<0)) + + /* PWMDBG */ +#define AW862X_BIT_PWMDBG_PWM_MODE_MASK (~(3<<5)) +#define AW862X_BIT_PWMDBG_PWM_12K (3<<5) +#define AW862X_BIT_PWMDBG_PWM_24K (2<<5) +#define AW862X_BIT_PWMDBG_PWM_48K (0<<5) + +/* GLB_STATE 0x47*/ +#define AW862X_BIT_GLBRD5_STATE_MASK (~(15<<0)) +#define AW862X_BIT_GLBRD5_STATE (15<<0) +#define AW862X_BIT_GLBRD5_STATE_STANDBY (0<<0) +#define AW862X_BIT_GLBRD5_STATE_WAKEUP (1<<0) +#define AW862X_BIT_GLBRD5_STATE_STARTUP (2<<0) +#define AW862X_BIT_GLBRD5_STATE_WAIT (3<<0) +#define AW862X_BIT_GLBRD5_STATE_CONT_GO (6<<0) +#define AW862X_BIT_GLBRD5_STATE_RAM_GO (7<<0) +#define AW862X_BIT_GLBRD5_STATE_RTP_GO (8<<0) +#define AW862X_BIT_GLBRD5_STATE_TRIG_GO (9<<0) +#define AW862X_BIT_GLBRD5_STATE_I2S_GO (10<<0) +#define AW862X_BIT_GLBRD5_STATE_BRAKE (11<<0) +#define AW862X_BIT_GLBRD5_STATE_END (12<<0) + + /* WAVECTRL */ +#define AW862X_BIT_WAVECTRL_NUM_OV_DRIVER_MASK (~(0xF<<4)) +#define AW862X_BIT_WAVECTRL_NUM_OV_DRIVER (0<<4) + + /* CONT_CTRL */ +#define AW862X_BIT_CONT_CTRL_ZC_DETEC_MASK (~(1<<7)) +#define AW862X_BIT_CONT_CTRL_ZC_DETEC_ENABLE (1<<7) +#define AW862X_BIT_CONT_CTRL_ZC_DETEC_DISABLE (0<<7) +#define AW862X_BIT_CONT_CTRL_WAIT_PERIOD_MASK (~(3<<5)) +#define AW862X_BIT_CONT_CTRL_WAIT_8PERIOD (3<<5) +#define AW862X_BIT_CONT_CTRL_WAIT_4PERIOD (2<<5) +#define AW862X_BIT_CONT_CTRL_WAIT_2PERIOD (1<<5) +#define AW862X_BIT_CONT_CTRL_WAIT_1PERIOD (0<<5) +#define AW862X_BIT_CONT_CTRL_MODE_MASK (~(1<<4)) +#define AW862X_BIT_CONT_CTRL_BY_DRV_TIME (1<<4) +#define AW862X_BIT_CONT_CTRL_BY_GO_SIGNAL (0<<4) +#define AW862X_BIT_CONT_CTRL_EN_CLOSE_MASK (~(1<<3)) +#define AW862X_BIT_CONT_CTRL_CLOSE_PLAYBACK (1<<3) +#define AW862X_BIT_CONT_CTRL_OPEN_PLAYBACK (0<<3) +#define AW862X_BIT_CONT_CTRL_F0_DETECT_MASK (~(1<<2)) +#define AW862X_BIT_CONT_CTRL_F0_DETECT_ENABLE (1<<2) +#define AW862X_BIT_CONT_CTRL_F0_DETECT_DISABLE (0<<2) +#define AW862X_BIT_CONT_CTRL_O2C_MASK (~(1<<1)) +#define AW862X_BIT_CONT_CTRL_O2C_ENABLE (1<<1) +#define AW862X_BIT_CONT_CTRL_O2C_DISABLE (0<<1) +#define AW862X_BIT_CONT_CTRL_AUTO_BRK_MASK (~(1<<0)) +#define AW862X_BIT_CONT_CTRL_AUTO_BRK_ENABLE (1<<0) +#define AW862X_BIT_CONT_CTRL_AUTO_BRK_DISABLE (0<<0) + +#define AW862X_BIT_D2SCFG_CLK_ADC_MASK (~(7<<5)) +#define AW862X_BIT_D2SCFG_CLK_ASC_1P5MHZ (3<<5) + +#define AW862X_BIT_D2SCFG_GAIN_MASK (~(7<<0)) +#define AW862X_BIT_D2SCFG_GAIN_40 (7<<0) + /* DETCTRL */ +#define AW862X_BIT_DETCTRL_RL_OS_MASK (~(1<<6)) +#define AW862X_BIT_DETCTRL_RL_DETECT (1<<6) +#define AW862X_BIT_DETCTRL_OS_DETECT (0<<6) +#define AW862X_BIT_DETCTRL_PROTECT_MASK (~(1<<5)) +#define AW862X_BIT_DETCTRL_PROTECT_NO_ACTION (1<<5) +#define AW862X_BIT_DETCTRL_PROTECT_SHUTDOWN (0<<5) +#define AW862X_BIT_DETCTRL_VBAT_GO_MASK (~(1<<1)) +#define AW862X_BIT_DETCTRL_VABT_GO_ENABLE (1<<1) +#define AW862X_BIT_DETCTRL_VBAT_GO_DISBALE (0<<1) +#define AW862X_BIT_DETCTRL_DIAG_GO_MASK (~(1<<0)) +#define AW862X_BIT_DETCTRL_DIAG_GO_ENABLE (1<<0) +#define AW862X_BIT_DETCTRL_DIAG_GO_DISABLE (0<<0) + + +#define AW862X_BIT_RAMADDRH_MASK (~(63<<0)) + + /* VBAT MODE */ +#define AW862X_BIT_DETCTRL_VBAT_MODE_MASK (~(1<<6)) +#define AW862X_BIT_DETCTRL_VBAT_HW_COMP (1<<6) +#define AW862X_BIT_DETCTRL_VBAT_SW_COMP (0<<6) + + + /* ANACTRL */ +#define AW862X_BIT_ANACTRL_LRA_SRC_MASK (~(1<<5)) +#define AW862X_BIT_ANACTRL_LRA_SRC_REG (1<<5) +#define AW862X_BIT_ANACTRL_LRA_SRC_EFUSE (0<<5) +#define AW862X_BIT_ANACTRL_EN_IO_PD1_MASK (~(1<<0)) +#define AW862X_BIT_ANACTRL_EN_IO_PD1_HIGH (1<<0) +#define AW862X_BIT_ANACTRL_EN_IO_PD1_LOW (0<<0) + +/* PRLVL */ +#define AW862X_BIT_PRLVL_PR_EN_MASK (~(1<<7)) +#define AW862X_BIT_PRLVL_PR_ENABLE (1<<7) +#define AW862X_BIT_PRLVL_PR_DISABLE (0<<7) +#define AW862X_BIT_PRLVL_PRLVL_MASK (~(0x7f<<0)) +#define AW862X_BIT_PRLVL_PRLVL_DEFAULT_VALUE (0x3F) + +/* PRTIME */ +#define AW862X_REG_PRTIME_DEFAULT_VALUE (0x12) + +#define AW862X_BIT_BEMF_NUM_BRK_MASK (~(0xf<<0)) + +/* TD_H 0x4b TD_brake */ +#define AW862X_BIT_R_SPARE_MASK (~(1<<7)) +#define AW862X_BIT_R_SPARE_ENABLE (1<<7) + +/* TIME_NZC */ +#define AW862X_BIT_TIME_NZC_DEF_VAL (0x1f) + +/* TRIM_LRA */ +#define AW862X_BIT_TRIM_LRA (0x3f<<0) +/****************************************************** + * AW8623X Register Detail + *****************************************************/ +/* SYSST: reg 0x01 RO */ +#define AW8623X_BIT_SYSST_UVLS (1<<5) +#define AW8623X_BIT_SYSST_FF_AES (1<<4) +#define AW8623X_BIT_SYSST_FF_AFS (1<<3) +#define AW8623X_BIT_SYSST_OCDS (1<<2) +#define AW8623X_BIT_SYSST_OTS (1<<1) +#define AW8623X_BIT_SYSST_DONES (1<<0) + +/* SYSINT: reg 0x02 RC */ +#define AW8623X_BIT_SYSINT_UVLI (1<<5) +#define AW8623X_BIT_SYSINT_FF_AEI (1<<4) +#define AW8623X_BIT_SYSINT_FF_AFI (1<<3) +#define AW8623X_BIT_SYSINT_OCDI (1<<2) +#define AW8623X_BIT_SYSINT_OTI (1<<1) +#define AW8623X_BIT_SYSINT_DONEI (1<<0) + +/* SYSINTM: reg 0x03 RW */ +#define AW8623X_BIT_SYSINTM_UVLM_MASK (~(1<<5)) +#define AW8623X_BIT_SYSINTM_UVLM_OFF (1<<5) +#define AW8623X_BIT_SYSINTM_UVLM_ON (0<<5) +#define AW8623X_BIT_SYSINTM_FF_AEM_MASK (~(1<<4)) +#define AW8623X_BIT_SYSINTM_FF_AEM_OFF (1<<4) +#define AW8623X_BIT_SYSINTM_FF_AEM_ON (0<<4) +#define AW8623X_BIT_SYSINTM_FF_AFM_MASK (~(1<<3)) +#define AW8623X_BIT_SYSINTM_FF_AFM_OFF (1<<3) +#define AW8623X_BIT_SYSINTM_FF_AFM_ON (0<<3) +#define AW8623X_BIT_SYSINTM_OCDM_MASK (~(1<<2)) +#define AW8623X_BIT_SYSINTM_OCDM_OFF (1<<2) +#define AW8623X_BIT_SYSINTM_OCDM_ON (0<<2) +#define AW8623X_BIT_SYSINTM_OTM_MASK (~(1<<1)) +#define AW8623X_BIT_SYSINTM_OTM_OFF (1<<1) +#define AW8623X_BIT_SYSINTM_OTM_ON (0<<1) +#define AW8623X_BIT_SYSINTM_DONEM_MASK (~(1<<0)) +#define AW8623X_BIT_SYSINTM_DONEM_OFF (1<<0) +#define AW8623X_BIT_SYSINTM_DONEM_ON (0<<0) + +/* SYSST2: reg 0x04 RO */ +#define AW8623X_BIT_SYSST2_WCK_OK (1<<5) +#define AW8623X_BIT_SYSST2_BCK_OK (1<<4) +#define AW8623X_BIT_SYSST2_LDO_OK (1<<3) +#define AW8623X_BIT_SYSST2_VBG_OK (1<<2) +#define AW8623X_BIT_SYSST2_FF_FULL (1<<1) +#define AW8623X_BIT_SYSST2_FF_EMPTY (1<<0) + +/* SYSER: reg 0x05 RC */ +#define AW8623X_BIT_SYSER_TRIG1_EVENT (1<<5) +#define AW8623X_BIT_SYSER_TRIG2_EVENT (1<<4) +#define AW8623X_BIT_SYSER_TRIG3_EVENT (1<<3) +#define AW8623X_BIT_SYSER_OV (1<<2) +#define AW8623X_BIT_SYSER_ADDR_ER (1<<1) +#define AW8623X_BIT_SYSER_FF_ER (1<<0) + +/* PLAYCFG3: reg 0x08 RW */ +#define AW8623X_BIT_PLAYCFG3_ONEWIRE_COMP_MASK (~(1<<4)) +#define AW8623X_BIT_PLAYCFG3_ONEWIRE_1908_MODE (1<<4) +#define AW8623X_BIT_PLAYCFG3_ONEWIRE_2102_MODE (0<<4) +#define AW8623X_BIT_PLAYCFG3_STOP_MODE_MASK (~(1<<3)) +#define AW8623X_BIT_PLAYCFG3_STOP_MODE_NOW (1<<3) +#define AW8623X_BIT_PLAYCFG3_STOP_MODE_LATER (0<<3) +#define AW8623X_BIT_PLAYCFG3_BRK_EN_MASK (~(1<<2)) +#define AW8623X_BIT_PLAYCFG3_BRK (1<<2) +#define AW8623X_BIT_PLAYCFG3_BRK_ENABLE (1<<2) +#define AW8623X_BIT_PLAYCFG3_BRK_DISABLE (0<<2) +#define AW8623X_BIT_PLAYCFG3_PLAY_MODE_MASK (~(3<<0)) +#define AW8623X_BIT_PLAYCFG3_PLAY_MODE_STOP (3<<0) +#define AW8623X_BIT_PLAYCFG3_PLAY_MODE_CONT (2<<0) +#define AW8623X_BIT_PLAYCFG3_PLAY_MODE_RTP (1<<0) +#define AW8623X_BIT_PLAYCFG3_PLAY_MODE_RAM (0<<0) + +/* PLAYCFG4: reg 0x09 WC */ +#define AW8623X_BIT_PLAYCFG4_STOP_MASK (~(1<<1)) +#define AW8623X_BIT_PLAYCFG4_STOP_ON (1<<1) +#define AW8623X_BIT_PLAYCFG4_STOP_OFF (0<<1) +#define AW8623X_BIT_PLAYCFG4_GO_MASK (~(1<<0)) +#define AW8623X_BIT_PLAYCFG4_GO_ON (1<<0) +#define AW8623X_BIT_PLAYCFG4_GO_OFF (0<<0) + +/* WAVCFG1-8: reg 0x0A - reg 0x11 RW */ +#define AW8623X_BIT_WAVCFG_SEQWAIT_MASK (~(1<<7)) +#define AW8623X_BIT_WAVCFG_SEQWAIT_TIME (1<<7) +#define AW8623X_BIT_WAVCFG_SEQWAIT_NUMBER (0<<7) +#define AW8623X_BIT_WAVCFG_SEQ (0x7F) + +/* WAVCFG9-12: reg 0x12 - reg 0x15 RW */ +#define AW8623X_BIT_WAVLOOP_SEQ_ODD_MASK (~(0x0F<<4)) +#define AW8623X_BIT_WAVLOOP_SEQ_ODD_INIFINITELY (0x0F<<4) +#define AW8623X_BIT_WAVLOOP_SEQ_EVEN_MASK (~(0x0F<<0)) +#define AW8623X_BIT_WAVLOOP_SEQ_EVEN_INIFINITELY (0x0F<<0) +#define AW8623X_BIT_WAVLOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG9: reg 0x12 RW */ +#define AW8623X_BIT_WAVCFG9_SEQ1LOOP_MASK (~(0x0F<<4)) +#define AW8623X_BIT_WAVCFG9_SEQ1LOOP_INIFINITELY (0x0F<<4) +#define AW8623X_BIT_WAVCFG9_SEQ2LOOP_MASK (~(0x0F<<0)) +#define AW8623X_BIT_WAVCFG9_SEQ2LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG10: reg 0x13 RW */ +#define AW8623X_BIT_WAVCFG10_SEQ3LOOP_MASK (~(0x0F<<4)) +#define AW8623X_BIT_WAVCFG10_SEQ3LOOP_INIFINITELY (0x0F<<4) +#define AW8623X_BIT_WAVCFG10_SEQ4LOOP_MASK (~(0x0F<<0)) +#define AW8623X_BIT_WAVCFG10_SEQ4LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG11: reg 0x14 RW */ +#define AW8623X_BIT_WAVCFG11_SEQ5LOOP_MASK (~(0x0F<<4)) +#define AW8623X_BIT_WAVCFG11_SEQ5LOOP_INIFINITELY (0x0F<<4) +#define AW8623X_BIT_WAVCFG11_SEQ6LOOP_MASK (~(0x0F<<0)) +#define AW8623X_BIT_WAVCFG11_SEQ6LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG12: reg 0x15 RW */ +#define AW8623X_BIT_WAVCFG12_SEQ7LOOP_MASK (~(0x0F<<4)) +#define AW8623X_BIT_WAVCFG12_SEQ7LOOP_INIFINITELY (0x0F<<4) +#define AW8623X_BIT_WAVCFG12_SEQ8LOOP_MASK (~(0x0F<<0)) +#define AW8623X_BIT_WAVCFG12_SEQ8LOOP_INIFINITELY (0x0F<<0) + +/***************** CONT *****************/ +/* CONTCFG1: reg 0x17 RW */ +#define AW8623X_BIT_CONTCFG1_EDGE_FRE_MASK (~(0x0F<<0)) +#define AW8623X_BIT_CONTCFG1_EN_F0_DET_MASK (~(1<<5)) +#define AW8623X_BIT_CONTCFG1_F0_DET_ENABLE (1<<5) +#define AW8623X_BIT_CONTCFG1_F0_DET_DISABLE (0<<5) +#define AW8623X_BIT_CONTCFG1_SIN_MODE_MASK (~(1<<4)) +#define AW8623X_BIT_CONTCFG1_SIN_MODE_COS (1<<0) +#define AW8623X_BIT_CONTCFG1_SIN_MODE_SINE (0<<0) + +/* CONTCFG5: reg 0x1B RW */ +#define AW8623X_BIT_CONTCFG5_BRK_GAIN_MASK (~(0x0F<<0)) + +/* CONTCFG6: reg 0x1C RW */ +#define AW8623X_BIT_CONTCFG6_TRACK_EN_MASK (~(1<<7)) +#define AW8623X_BIT_CONTCFG6_TRACK_ENABLE (1<<7) +#define AW8623X_BIT_CONTCFG6_TRACK_DISABLE (0<<7) +#define AW8623X_BIT_CONTCFG6_DRV1_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG7: reg 0x1D RW */ +#define AW8623X_BIT_CONTCFG7_WAITHZ_EN_MASK (~(1<<7)) +#define AW8623X_BIT_CONTCFG7_WAITHZ_ENABLE (1<<7) +#define AW8623X_BIT_CONTCFG7_WAITHZ_DISABLE (0<<7) +#define AW8623X_BIT_CONTCFG7_DRV2_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG13: reg 0x23 RW */ +#define AW8623X_BIT_CONTCFG13_TSET_MASK (~(0x0F<<4)) +#define AW8623X_BIT_CONTCFG13_BEME_SET_MASK (~(0x0F<<0)) + +/***************** RTP *****************/ +/* RTPCFG1: reg 0x2C RW */ +#define AW8623X_BIT_RTPCFG1_ADDRH_MASK (~(0x0F<<0)) + +#define AW8623X_BIT_RTPCFG1_SRAM_SIZE_2K_MASK (~(1<<5)) +#define AW8623X_BIT_RTPCFG1_SRAM_SIZE_2K_EN (1<<5) +#define AW8623X_BIT_RTPCFG1_SRAM_SIZE_2K_DIS (0<<5) + +#define AW8623X_BIT_RTPCFG1_SRAM_SIZE_1K_MASK (~(1<<4)) +#define AW8623X_BIT_RTPCFG1_SRAM_SIZE_1K_EN (1<<4) +#define AW8623X_BIT_RTPCFG1_SRAM_SIZE_1K_DIS (0<<4) + +/* RTPCFG3: reg 0x2E RW */ +#define AW8623X_BIT_RTPCFG3_FIFO_AEH_MASK (~(0x0F<<4)) +#define AW8623X_BIT_RTPCFG3_FIFO_AFH_MASK (~(0x0F<<0)) +#define AW8623X_BIT_RTPCFG3_FIFO_AEH (0x0F<<4) +#define AW8623X_BIT_RTPCFG3_FIFO_AFH (0x0F<<0) + +/* RTPCFG6: reg 0x31 RW */ +#define AW8623X_BIT_RTPCFG6_EN_AUTO_SIN_96K_MASK (~(1<<3)) +#define AW8623X_BIT_RTPCFG6_EN_AUTO_SIN_96K_ENABLE (1<<3) +#define AW8623X_BIT_RTPCFG6_EN_AUTO_SIN_96K_DISABLE (0<<3) +#define AW8623X_BIT_RTPCFG6_EN_RTP_AUTO_SIN_MASK (~(1<<2)) +#define AW8623X_BIT_RTPCFG6_EN_RTP_AUTO_SIN_ENABLE (1<<2) +#define AW8623X_BIT_RTPCFG6_EN_RTP_AUTO_SIN_DISABLE (0<<2) +#define AW8623X_BIT_RTPCFG6_EN_TWORTP_MASK (~(1<<1)) +#define AW8623X_BIT_RTPCFG6_EN_TWORTP_ENABLE (1<<1) +#define AW8623X_BIT_RTPCFG6_EN_TWORTP_DISABLE (0<<1) +#define AW8623X_BIT_RTPCFG6_WCK_PIN_MASK (~(1<<0)) +#define AW8623X_BIT_RTPCFG6_WCK_PIN_ENABLE (1<<0) +#define AW8623X_BIT_RTPCFG6_WCK_PIN_DISABLE (0<<0) + +/***************** TRIGGER *****************/ +#define AW8623X_BIT_TRG_ENABLE_MASK (~(1<<7)) +#define AW8623X_BIT_TRG_ENABLE (1<<7) +#define AW8623X_BIT_TRG_DISABLE (0<<7) +#define AW8623X_BIT_TRG_SEQ_MASK (~(0x7F<<0)) + +/* TRGCFG1: reg 0x33 RW */ +#define AW8623X_BIT_TRGCFG1_TRG1_POS_MASK (~(1<<7)) +#define AW8623X_BIT_TRGCFG1_TRG1_POS_ENABLE (1<<7) +#define AW8623X_BIT_TRGCFG1_TRG1_POS_DISABLE (0<<7) +#define AW8623X_BIT_TRGCFG1_TRG1SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG2: reg 0x34 RW */ +#define AW8623X_BIT_TRGCFG2_TRG2_POS_MASK (~(1<<7)) +#define AW8623X_BIT_TRGCFG2_TRG2_POS_ENABLE (1<<7) +#define AW8623X_BIT_TRGCFG2_TRG2_POS_DISABLE (0<<7) +#define AW8623X_BIT_TRGCFG2_TRG2SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG3: r3g 0x35 RW */ +#define AW8623X_BIT_TRGCFG3_TRG3_POS_MASK (~(1<<7)) +#define AW8623X_BIT_TRGCFG3_TRG3_POS_ENABLE (1<<7) +#define AW8623X_BIT_TRGCFG3_TRG3_POS_DISABLE (0<<7) +#define AW8623X_BIT_TRGCFG3_TRG3SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG4: r3g 0x36 RW */ +#define AW8623X_BIT_TRGCFG4_TRG1_NEG_MASK (~(1<<7)) +#define AW8623X_BIT_TRGCFG4_TRG1_NEG_ENABLE (1<<7) +#define AW8623X_BIT_TRGCFG4_TRG1_NEG_DISABLE (0<<7) +#define AW8623X_BIT_TRGCFG4_TRG1SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG5: r3g 0x37 RW */ +#define AW8623X_BIT_TRGCFG5_TRG2_NEG_MASK (~(1<<7)) +#define AW8623X_BIT_TRGCFG5_TRG2_NEG_ENABLE (1<<7) +#define AW8623X_BIT_TRGCFG5_TRG2_NEG_DISABLE (0<<7) +#define AW8623X_BIT_TRGCFG5_TRG2SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG6: r3g 0x38 RW */ +#define AW8623X_BIT_TRGCFG6_TRG3_NEG_MASK (~(1<<7)) +#define AW8623X_BIT_TRGCFG6_TRG3_NEG_ENABLE (1<<7) +#define AW8623X_BIT_TRGCFG6_TRG3_NEG_DISABLE (0<<7) +#define AW8623X_BIT_TRGCFG6_TRG3SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG7: reg 0x39 RW */ +#define AW8623X_BIT_TRGCFG7_TRG1_POR_LEV_BRK_MASK (~(7<<3)) +#define AW8623X_BIT_TRGCFG7_TRG2_POR_LEV_BRK_MASK (~(7<<0)) +#define AW8623X_BIT_TRGCFG7_TRG1_POLAR_MASK (~(1<<5)) +#define AW8623X_BIT_TRGCFG7_TRG1_POLAR_NEG (1<<5) +#define AW8623X_BIT_TRGCFG7_TRG1_POLAR_POS (0<<5) +#define AW8623X_BIT_TRGCFG7_TRG1_MODE_MASK (~(1<<4)) +#define AW8623X_BIT_TRGCFG7_TRG1_MODE_LEVEL (1<<4) +#define AW8623X_BIT_TRGCFG7_TRG1_MODE_EDGE (0<<4) +#define AW8623X_BIT_TRGCFG7_TRG1_AUTO_BRK_MASK (~(1<<3)) +#define AW8623X_BIT_TRGCFG7_TRG1_AUTO_BRK_ENABLE (1<<3) +#define AW8623X_BIT_TRGCFG7_TRG1_AUTO_BRK_DISABLE (0<<3) +#define AW8623X_BIT_TRGCFG7_TRG2_POLAR_MASK (~(1<<2)) +#define AW8623X_BIT_TRGCFG7_TRG2_POLAR_NEG (1<<2) +#define AW8623X_BIT_TRGCFG7_TRG2_POLAR_POS (0<<2) +#define AW8623X_BIT_TRGCFG7_TRG2_MODE_MASK (~(1<<1)) +#define AW8623X_BIT_TRGCFG7_TRG2_MODE_LEVEL (1<<1) +#define AW8623X_BIT_TRGCFG7_TRG2_MODE_EDGE (0<<1) +#define AW8623X_BIT_TRGCFG7_TRG2_AUTO_BRK_MASK (~(1<<0)) +#define AW8623X_BIT_TRGCFG7_TRG2_AUTO_BRK_ENABLE (1<<0) +#define AW8623X_BIT_TRGCFG7_TRG2_AUTO_BRK_DISABLE (0<<0) + +/* TRGCFG8: reg 0x3A RW */ +#define AW8623X_BIT_TRGCFG8_TRG3_POR_LEV_BRK_MASK (~(7<<3)) +#define AW8623X_BIT_TRGCFG8_TRG3_POLAR_MASK (~(1<<5)) +#define AW8623X_BIT_TRGCFG8_TRG3_POLAR_NEG (1<<5) +#define AW8623X_BIT_TRGCFG8_TRG3_POLAR_POS (0<<5) +#define AW8623X_BIT_TRGCFG8_TRG3_MODE_MASK (~(1<<4)) +#define AW8623X_BIT_TRGCFG8_TRG3_MODE_LEVEL (1<<4) +#define AW8623X_BIT_TRGCFG8_TRG3_MODE_EDGE (0<<4) +#define AW8623X_BIT_TRGCFG8_TRG3_AUTO_BRK_MASK (~(1<<3)) +#define AW8623X_BIT_TRGCFG8_TRG3_AUTO_BRK_ENABLE (1<<3) +#define AW8623X_BIT_TRGCFG8_TRG3_AUTO_BRK_DISABLE (0<<3) +#define AW8623X_BIT_TRGCFG8_TRG1_STOP_MASK (~(1<<2)) +#define AW8623X_BIT_TRGCFG8_TRG1_STOP (1<<2) +#define AW8623X_BIT_TRGCFG8_TRG2_STOP_MASK (~(1<<1)) +#define AW8623X_BIT_TRGCFG8_TRG2_STOP (1<<1) +#define AW8623X_BIT_TRGCFG8_TRG3_STOP_MASK (~(1<<0)) +#define AW8623X_BIT_TRGCFG8_TRG3_STOP (1<<0) + +/* GLBCFG4: reg 0x3E RW */ +#define AW8623X_BIT_GLBCFG4_GO_PRIO_MASK (~(3<<6)) +#define AW8623X_BIT_GLBCFG4_TRG3_PRIO_MASK (~(3<<4)) +#define AW8623X_BIT_GLBCFG4_TRG2_PRIO_MASK (~(3<<2)) +#define AW8623X_BIT_GLBCFG4_TRG1_PRIO_MASK (~(3<<0)) + +/* GLBRD5: reg 0x3F R0 */ +/* GLB_STATE */ +#define AW8623X_BIT_GLBRD5_STATE (15<<0) +#define AW8623X_BIT_GLBRD5_STATE_STANDBY (0<<0) +#define AW8623X_BIT_GLBRD5_STATE_WAKEUP (1<<0) +#define AW8623X_BIT_GLBRD5_STATE_STARTUP (2<<0) +#define AW8623X_BIT_GLBRD5_STATE_WAIT (3<<0) +#define AW8623X_BIT_GLBRD5_STATE_CONT_GO (6<<0) +#define AW8623X_BIT_GLBRD5_STATE_RAM_GO (7<<0) +#define AW8623X_BIT_GLBRD5_STATE_RTP_GO (8<<0) +#define AW8623X_BIT_GLBRD5_STATE_TRIG_GO (9<<0) +#define AW8623X_BIT_GLBRD5_STATE_I2S_GO (10<<0) +#define AW8623X_BIT_GLBRD5_STATE_BRAKE (11<<0) +#define AW8623X_BIT_GLBRD5_STATE_END (12<<0) +/* RAMADDRH: reg 0x40 RWS */ +#define AW8623X_BIT_RAMADDRH_MASK (~(63<<0)) + +/* I2SCFG1: reg 0x44 RW */ +#define AW8623X_BIT_I2SCFG1_I2S_EN_MASK (~(1<<2)) +#define AW8623X_BIT_I2SCFG1_I2S_ENABLE (1<<2) +#define AW8623X_BIT_I2SCFG1_I2S_DISABLE (0<<2) +#define AW8623X_BIT_I2SCFG1_I2S_MD_MASK (~(1<<1)) +#define AW8623X_BIT_I2SCFG1_I2S_MD_MSB (1<<1) +#define AW8623X_BIT_I2SCFG1_I2S_MD_PHILIP (0<<1) +#define AW8623X_BIT_I2SCFG1_I2S_SLOT_CHSEL_MASK (~(1<<0)) +#define AW8623X_BIT_I2SCFG1_I2S_SLOT_CHSEL_R_BEHIND (1<<0) +#define AW8623X_BIT_I2SCFG1_I2S_SLOT_CHSEL_L_BEYOND (0<<0) + +/***************** SYSCTRL *****************/ +/* SYSCTRL1: reg 0x45 RW */ +#define AW8623X_BIT_SYSCTRL1_TRIG1_MODE_MASK (~(7<<4)) +#define AW8623X_BIT_SYSCTRL1_TRIG1_NONE (7<<4) +#define AW8623X_BIT_SYSCTRL1_TRIG1_AD (6<<4) +#define AW8623X_BIT_SYSCTRL1_TRIG1_RCK (5<<4) +#define AW8623X_BIT_SYSCTRL1_TRIG1_BCK (4<<4) +#define AW8623X_BIT_SYSCTRL1_TRIG1_TRIG (3<<4) +#define AW8623X_BIT_SYSCTRL1_TRIG1_ONE_WIRE (2<<4) +#define AW8623X_BIT_SYSCTRL1_TRIG1_PWM_ERM (1<<4) +#define AW8623X_BIT_SYSCTRL1_TRIG1_PWM_LRA (0<<4) +#define AW8623X_BIT_SYSCTRL1_GAIN_BYPASS_MASK (~(1<<3)) +#define AW8623X_BIT_SYSCTRL1_GAIN_CHANGE_ENABLE (1<<3) +#define AW8623X_BIT_SYSCTRL1_GAIN_CHANGE_DISABLE (0<<3) +#define AW8623X_BIT_SYSCTRL1_RAMINIT_MASK (~(1<<2)) +#define AW8623X_BIT_SYSCTRL1_RAMINIT_ON (1<<2) +#define AW8623X_BIT_SYSCTRL1_RAMINIT_OFF (0<<2) +#define AW8623X_BIT_SYSCTRL1_EN_FIR_MASK (~(1<<1)) +#define AW8623X_BIT_SYSCTRL1_FIR_ENABLE (0<<1) +#define AW8623X_BIT_SYSCTRL1_INTN_PIN_MASK (~(1<<0)) +#define AW8623X_BIT_SYSCTRL1_INTN (1<<0) +#define AW8623X_BIT_SYSCTRL1_TRIG1 (0<<0) + +/* SYSCTRL2: reg 0x46 RW */ +#define AW8623X_BIT_SYSCTRL2_WAKE_MASK (~(1<<7)) +#define AW8623X_BIT_SYSCTRL2_WAKE_ON (1<<7) +#define AW8623X_BIT_SYSCTRL2_WAKE_OFF (0<<7) +#define AW8623X_BIT_SYSCTRL2_STANDBY_MASK (~(1<<6)) +#define AW8623X_BIT_SYSCTRL2_STANDBY_ON (1<<6) +#define AW8623X_BIT_SYSCTRL2_STANDBY_OFF (0<<6) +#define AW8623X_BIT_SYSCTRL2_EN_DLL_MASK (~(1<<5)) +#define AW8623X_BIT_SYSCTRL2_EN_DLL (1<<5) +#define AW8623X_BIT_SYSCTRL2_DISENABLE_DLL (0<<5) +#define AW8623X_BIT_SYSCTRL2_RCK_FRE_MASK (~(3<<3)) +#define AW8623X_BIT_SYSCTRL2_RCK_FRE_96K (3<<3) +#define AW8623X_BIT_SYSCTRL2_RCK_FRE_48K (2<<3) +#define AW8623X_BIT_SYSCTRL2_RCK_FRE_32_768K (1<<3) +#define AW8623X_BIT_SYSCTRL2_RCK_FRE_24K (0<<3) +#define AW8623X_BIT_SYSCTRL2_EN_INTN_CLKOUT_MASK (~(1<<2)) +#define AW8623X_BIT_SYSCTRL2_EN_INTN_CLKOUT (1<<2) +#define AW8623X_BIT_SYSCTRL2_DISENABLE_INTN_CLKOUT (0<<2) +#define AW8623X_BIT_SYSCTRL2_WAVDAT_MODE_MASK (~(3<<0)) +#define AW8623X_BIT_SYSCTRL2_RATE (3<<0) +#define AW8623X_BIT_SYSCTRL2_RATE_12K (2<<0) +#define AW8623X_BIT_SYSCTRL2_RATE_24K (0<<0) +#define AW8623X_BIT_SYSCTRL2_RATE_48K (1<<0) + +/* SYSCTRL3: reg 0x47 RW */ +#define AW8623X_BIT_SYSCTRL3_EN_TRIG1_PD_MASK (~(1<<6)) +#define AW8623X_BIT_SYSCTRL3_EN_TRIG1_PD (1<<6) +#define AW8623X_BIT_SYSCTRL3_DISENABLE_TRIG1_PD (0<<6) +#define AW8623X_BIT_SYSCTRL3_RTP_DLY_MASK (~(3<<4)) +#define AW8623X_BIT_SYSCTRL3_RTP_DLY_FIFO_AE (3<<4) +#define AW8623X_BIT_SYSCTRL3_RTP_DLY_FIFO_8 (2<<4) +#define AW8623X_BIT_SYSCTRL3_RTP_DLY_FIFO_5 (1<<4) +#define AW8623X_BIT_SYSCTRL3_RTP_DLY_FIFO_1 (0<<4) +#define AW8623X_BIT_SYSCTRL3_INTMODE_MASK (~(3<<2)) +#define AW8623X_BIT_SYSCTRL3_INTMODE_BOTH_EDGE (3<<2) +#define AW8623X_BIT_SYSCTRL3_INTMODE_LEVEL_1 (2<<2) +#define AW8623X_BIT_SYSCTRL3_INTMODE_POS_EDGE (1<<2) +#define AW8623X_BIT_SYSCTRL3_INTMODE_LEVEL_2 (0<<2) +#define AW8623X_BIT_SYSCTRL3_INTP_MASK (~(1<<1)) +#define AW8623X_BIT_SYSCTRL3_INTP_ACTIVE_HIGH (1<<1) +#define AW8623X_BIT_SYSCTRL3_INTP_ACTIVE_LOW (0<<1) +#define AW8623X_BIT_SYSCTRL3_WAKE_MODE_MASK (~(1<<0)) +#define AW8623X_BIT_SYSCTRL3_WAKE_MODE_WAKEUP (1<<0) +#define AW8623X_BIT_SYSCTRL3_WAKE_MODE_BST (0<<0) + +/* SYSCTRL5: reg 0x49 RW */ +#define AW8623X_BIT_SYSCTRL5_MODULE_SEL_MASK (~(7<<2)) +#define AW8623X_BIT_SYSCTRL5_MODULE_SEL_VBAT_CTRL (5<<2) +#define AW8623X_BIT_SYSCTRL5_MODULE_SEL_TRIG_ENG (4<<2) +#define AW8623X_BIT_SYSCTRL5_MODULE_SEL_CONT_ENG (3<<2) +#define AW8623X_BIT_SYSCTRL5_MODULE_SEL_RTP_ENG (2<<2) +#define AW8623X_BIT_SYSCTRL5_MODULE_SEL_RAM_ENG (1<<2) +#define AW8623X_BIT_SYSCTRL5_MODULE_SEL_EFUSE_ENG (0<<2) +#define AW8623X_BIT_SYSCTRL5_DEV_ADDR_CTR_MASK (~(3<<0)) +#define AW8623X_BIT_SYSCTRL5_DEV_ADDR_CTR_HW_IRSW (3<<0) +#define AW8623X_BIT_SYSCTRL5_DEV_ADDR_CTR_HW_1 (2<<0) +#define AW8623X_BIT_SYSCTRL5_DEV_ADDR_CTR_IRSW (1<<0) +#define AW8623X_BIT_SYSCTRL5_DEV_ADDR_CTR_HW_2 (0<<0) + +/* PWMCFG1: reg 0x4A RW */ +#define AW8623X_BIT_PWMCFG1_PRC_EN_MASK (~(1<<7)) +#define AW8623X_BIT_PWMCFG1_PRC_ENABLE (1<<7) +#define AW8623X_BIT_PWMCFG1_PRC_DISABLE (0<<7) +#define AW8623X_BIT_PWMCFG1_PRCTIME_MASK (~(0x7F<<0)) + +/* PWMCFG2: reg 0x4B RW */ +#define AW8623X_BIT_PWMCFG2_PRCT_MODE_MASK (~(1<<6)) +#define AW8623X_BIT_PWMCFG2_PRCT_MODE_VALID (1<<6) +#define AW8623X_BIT_PWMCFG2_PRCT_MODE_INVALID (0<<6) +#define AW8623X_BIT_PWMCFG2_REF_SEL_MASK (~(1<<5)) +#define AW8623X_BIT_PWMCFG2_REF_SEL_TRIANGLE (1<<5) +#define AW8623X_BIT_PWMCFG2_REF_SEL_SAWTOOTH (0<<5) +#define AW8623X_BIT_PWMCFG2_PD_HWM_MASK (~(1<<4)) +#define AW8623X_BIT_PWMCFG2_PD_HWM_FULL (1<<4) +#define AW8623X_BIT_PWMCFG2_PD_HWM_HALF (0<<4) +#define AW8623X_BIT_PWMCFG2_PWMOE_MASK (~(1<<3)) +#define AW8623X_BIT_PWMCFG2_PWMOE_ON (1<<3) +#define AW8623X_BIT_PWMCFG2_PWMOE_OFF (0<<3) +#define AW8623X_BIT_PWMCFG2_PWMFRC_MASK (~(7<<0)) + +/* PWMCFG3: reg 0x4C RW */ +#define AW8623X_BIT_PWMCFG3_PR_EN_MASK (~(1<<7)) +#define AW8623X_BIT_PWMCFG3_PR_ENABLE (1<<7) +#define AW8623X_BIT_PWMCFG3_PR_DISABLE (0<<7) +#define AW8623X_BIT_PWMCFG3_PRLVL_MASK (~(0x7F<<0)) +#define AW8623X_BIT_PWMCFG3_PRLVL_DEFAULT_VALUE (0x3F) + +/* PWMCFG4: reg 0x4D RW */ +/* PRTIME */ +#define AW8623X_PWMCFG4_PRTIME_DEFAULT_VALUE (0x32) + +/* VBATCTRL: reg 0x4E RW */ +#define AW8623X_BIT_VBATCTRL_VBAT_PRO_MASK (~(1<<7)) +#define AW8623X_BIT_VBATCTRL_VBAT_PRO_ENABLE (1<<7) +#define AW8623X_BIT_VBATCTRL_VBAT_PRO_DISABLE (0<<7) +#define AW8623X_BIT_VBATCTRL_VBAT_MODE_MASK (~(1<<6)) +#define AW8623X_BIT_VBATCTRL_VBAT_MODE_HW (1<<6) +#define AW8623X_BIT_VBATCTRL_VBAT_MODE_SW (0<<6) +#define AW8623X_BIT_VBATCTRL_VBAT_MODE_CON_MASK (~(1<<5)) +#define AW8623X_BIT_VBATCTRL_VBAT_MODE_CON_DURING (1<<5) +#define AW8623X_BIT_VBATCTRL_VBAT_MODE_CON_BEFORE (0<<5) +#define AW8623X_BIT_VBATCTRL_DELTA_VBAT_MASK (~(1<<4)) +#define AW8623X_BIT_VBATCTRL_DELTA_VBAT_0_2 (1<<4) +#define AW8623X_BIT_VBATCTRL_DELTA_VBAT_0_1 (0<<4) +#define AW8623X_BIT_VBATCTRL_REL_VBAT_MASK (~(3<<2)) +#define AW8623X_BIT_VBATCTRL_REL_VBAT_100 (3<<2) +#define AW8623X_BIT_VBATCTRL_REL_VBAT_50 (2<<2) +#define AW8623X_BIT_VBATCTRL_REL_VBAT_25 (1<<2) +#define AW8623X_BIT_VBATCTRL_REL_VBAT_10 (0<<2) +#define AW8623X_BIT_VBATCTRL_ABS_VBAT_MASK (~(3<<0)) +#define AW8623X_BIT_VBATCTRL_ABS_VBAT_3_5 (3<<0) +#define AW8623X_BIT_VBATCTRL_ABS_VBAT_3_4 (2<<0) +#define AW8623X_BIT_VBATCTRL_ABS_VBAT_3_3 (1<<0) +#define AW8623X_BIT_VBATCTRL_ABS_VBAT_3_2 (0<<0) + +/* DETCFG1: reg 0x4F RW */ +#define AW8623X_BIT_DETCFG1_EN_VBAT_AVG_MASK (~(1<<7)) +#define AW8623X_BIT_DETCFG1_EN_VBAT_AVG_16_TIMES (1<<7) +#define AW8623X_BIT_DETCFG1_VBAT_REF_MASK (~(7<<4)) +#define AW8623X_BIT_DETCFG1_VBAT_REF_5_5 (7<<4) +#define AW8623X_BIT_DETCFG1_VBAT_REF_5_0 (6<<4) +#define AW8623X_BIT_DETCFG1_VBAT_REF_4_8 (5<<4) +#define AW8623X_BIT_DETCFG1_VBAT_REF_4_5 (4<<4) +#define AW8623X_BIT_DETCFG1_VBAT_REF_4_2 (3<<4) +#define AW8623X_BIT_DETCFG1_VBAT_REF_4_0 (2<<4) +#define AW8623X_BIT_DETCFG1_VBAT_REF_3_6 (1<<4) +#define AW8623X_BIT_DETCFG1_VBAT_REF_3_3 (0<<4) +#define AW8623X_BIT_DETCFG1_ADC_FS_MASK (~(3<<2)) +#define AW8623X_BIT_DETCFG1_ADC_FS_24K (3<<2) +#define AW8623X_BIT_DETCFG1_ADC_FS_48K (2<<2) +#define AW8623X_BIT_DETCFG1_ADC_FS_96K (1<<2) +#define AW8623X_BIT_DETCFG1_ADC_FS_192K (0<<2) +#define AW8623X_BIT_DETCFG1_DET_GO_MASK (~(3<<0)) +#define AW8623X_BIT_DETCFG1_DET_GO_ON (1<<0) +#define AW8623X_BIT_DETCFG1_DET_GO_OFF (0<<0) + +/* DETCFG2: reg 0x50 RW */ +#define AW8623X_BIT_DETCFG2_DET_SEQ0_MASK (~(0X0F<<3)) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_HDN_1 (0x0E<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_HDN_2 (0x0A<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_HDP_1 (0x0D<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_HDP_2 (9<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_FTS (6<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_VOUT (5<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_OS (4<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_RL (3<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_TRIG1 (2<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_PVDD (1<<3) +#define AW8623X_BIT_DETCFG2_DET_SEQ0_VBAT (0<<3) +#define AW8623X_BIT_DETCFG2_D2S_GAIN_MASK (~(7<<0)) +#define AW8623X_BIT_DETCFG2_D2S_GAIN_40 (7<<0) +#define AW8623X_BIT_DETCFG2_D2S_GAIN_20 (6<<0) +#define AW8623X_BIT_DETCFG2_D2S_GAIN_10 (5<<0) +#define AW8623X_BIT_DETCFG2_D2S_GAIN_8 (4<<0) +#define AW8623X_BIT_DETCFG2_D2S_GAIN_5 (3<<0) +#define AW8623X_BIT_DETCFG2_D2S_GAIN_4 (2<<0) +#define AW8623X_BIT_DETCFG2_D2S_GAIN_2 (1<<0) +#define AW8623X_BIT_DETCFG2_D2S_GAIN_1 (0<<0) + +/* DETRD1: reg 0x51 RO */ +#define AW8623X_BIT_DETRD1_AVG_DATA_H_MASK (~(3<<0)) + +/* TRIMCFG1: reg 0x59 RW */ +#define AW8623X_BIT_TRIMCFG1_RL_TRIM_SRC_MASK (~(1<<7)) +#define AW8623X_BIT_TRIMCFG1_RL_TRIM_SRC_REG (1<<7) +#define AW8623X_BIT_TRIMCFG1_RL_TRIM_SRC_EFUSE (0<<7) +#define AW8623X_BIT_TRIMCFG1_TRIM_RL_MASK (~(0x7F<<0)) + +/* TRIMCFG2: reg 0x5A RW */ +#define AW8623X_BIT_TRIMCFG2_OSC_TRIM_SRC_MASK (~(1<<6)) +#define AW8623X_BIT_TRIMCFG2_OSC_TRIM_SRC_REG (1<<6) +#define AW8623X_BIT_TRIMCFG2_OSC_TRIM_SRC_EFUSE (0<<6) +#define AW8623X_BIT_TRIMCFG2_TRIM_LRA_MASK (~(0x3F<<0)) + +/* ANACFG4: reg:0x70 RW */ +#define AW8623X_BIT_ANACFG3_EN_OVP_SAR_MASK (~(1<<7)) +#define AW8623X_BIT_ANACFG3_EN_OVP_SAR_ENABLE (1<<7) +#define AW8623X_BIT_ANACFG3_EN_OVP_SAR_DISABLE (0<<7) + +/* ANACFG4: reg:0x71 RW */ +#define AW8623X_BIT_ANACFG4_PRO_HDRV_MASK (~(1<<7)) +#define AW8623X_BIT_ANACFG4_PRO_HDRV_ENABLE (1<<7) +#define AW8623X_BIT_ANACFG4_PRO_HDRV_DISABLE (0<<7) + +/****************************************************** + * AW8624X Register Detail + *****************************************************/ +/* SYSST: reg 0x01 RO */ +#define AW8624X_BIT_SYSST_UVLS (1<<5) +#define AW8624X_BIT_SYSST_FF_AES (1<<4) +#define AW8624X_BIT_SYSST_FF_AFS (1<<3) +#define AW8624X_BIT_SYSST_OCDS (1<<2) +#define AW8624X_BIT_SYSST_OTS (1<<1) +#define AW8624X_BIT_SYSST_DONES (1<<0) + +/* SYSINT: reg 0x02 RC */ +#define AW8624X_BIT_SYSINT_UVLI (1<<5) +#define AW8624X_BIT_SYSINT_FF_AEI (1<<4) +#define AW8624X_BIT_SYSINT_FF_AFI (1<<3) +#define AW8624X_BIT_SYSINT_OCDI (1<<2) +#define AW8624X_BIT_SYSINT_OTI (1<<1) +#define AW8624X_BIT_SYSINT_DONEI (1<<0) + +/* SYSINTM: reg 0x03 RW */ +#define AW8624X_BIT_SYSINTM_UVLM_MASK (~(1<<5)) +#define AW8624X_BIT_SYSINTM_UVLM_OFF (1<<5) +#define AW8624X_BIT_SYSINTM_UVLM_ON (0<<5) +#define AW8624X_BIT_SYSINTM_FF_AEM_MASK (~(1<<4)) +#define AW8624X_BIT_SYSINTM_FF_AEM_OFF (1<<4) +#define AW8624X_BIT_SYSINTM_FF_AEM_ON (0<<4) +#define AW8624X_BIT_SYSINTM_FF_AFM_MASK (~(1<<3)) +#define AW8624X_BIT_SYSINTM_FF_AFM_OFF (1<<3) +#define AW8624X_BIT_SYSINTM_FF_AFM_ON (0<<3) +#define AW8624X_BIT_SYSINTM_OCDM_MASK (~(1<<2)) +#define AW8624X_BIT_SYSINTM_OCDM_OFF (1<<2) +#define AW8624X_BIT_SYSINTM_OCDM_ON (0<<2) +#define AW8624X_BIT_SYSINTM_OTM_MASK (~(1<<1)) +#define AW8624X_BIT_SYSINTM_OTM_OFF (1<<1) +#define AW8624X_BIT_SYSINTM_OTM_ON (0<<1) +#define AW8624X_BIT_SYSINTM_DONEM_MASK (~(1<<0)) +#define AW8624X_BIT_SYSINTM_DONEM_OFF (1<<0) +#define AW8624X_BIT_SYSINTM_DONEM_ON (0<<0) + +/* SYSST2: reg 0x04 RO */ +#define AW8624X_BIT_SYSST2_RAM_ADDR_ER (1<<5) +#define AW8624X_BIT_SYSST2_TRG_ADDR_ER (1<<4) +#define AW8624X_BIT_SYSST2_DVDD_PORN (1<<3) +#define AW8624X_BIT_SYSST2_VBG_OK (1<<2) +#define AW8624X_BIT_SYSST2_FF_FULL (1<<1) +#define AW8624X_BIT_SYSST2_FF_EMPTY (1<<0) + +/* SYSER: reg 0x05 RC */ +#define AW8624X_BIT_SYSER_TRIG1_EVENT (1<<6) +#define AW8624X_BIT_SYSER_TRIG2_EVENT (1<<5) +#define AW8624X_BIT_SYSER_TRIG3_EVENT (1<<4) +#define AW8624X_BIT_SYSER_OV (1<<3) +#define AW8624X_BIT_SYSER_ADDR_ER (1<<2) +#define AW8624X_BIT_SYSER_FF_ER (1<<1) +#define AW8624X_BIT_SYSER_PLL_REF_ER (1<<0) + +/* PLAYCFG3: reg 0x08 RW */ +#define AW8624X_BIT_PLAYCFG3_STOP_MODE_MASK (~(1<<5)) +#define AW8624X_BIT_PLAYCFG3_STOP_MODE_NOW (1<<5) +#define AW8624X_BIT_PLAYCFG3_STOP_MODE_LATER (0<<5) +#define AW8624X_BIT_PLAYCFG3_BRK_EN_MASK (~(1<<2)) +#define AW8624X_BIT_PLAYCFG3_BRK (1<<2) +#define AW8624X_BIT_PLAYCFG3_BRK_ENABLE (1<<2) +#define AW8624X_BIT_PLAYCFG3_BRK_DISABLE (0<<2) +#define AW8624X_BIT_PLAYCFG3_PLAY_MODE_MASK (~(3<<0)) +#define AW8624X_BIT_PLAYCFG3_PLAY_MODE_STOP (3<<0) +#define AW8624X_BIT_PLAYCFG3_PLAY_MODE_CONT (2<<0) +#define AW8624X_BIT_PLAYCFG3_PLAY_MODE_RTP (1<<0) +#define AW8624X_BIT_PLAYCFG3_PLAY_MODE_RAM (0<<0) + +/* PLAYCFG4: reg 0x09 RW */ +#define AW8624X_BIT_PLAYCFG4_STOP_MASK (~(1<<1)) +#define AW8624X_BIT_PLAYCFG4_STOP_ON (1<<1) +#define AW8624X_BIT_PLAYCFG4_STOP_OFF (0<<1) +#define AW8624X_BIT_PLAYCFG4_GO_MASK (~(1<<0)) +#define AW8624X_BIT_PLAYCFG4_GO_ON (1<<0) +#define AW8624X_BIT_PLAYCFG4_GO_OFF (0<<0) + +/* WAVCFG1-8: reg 0x0A - reg 0x11 RW */ +#define AW8624X_BIT_WAVCFG_SEQWAIT_MASK (~(1<<7)) +#define AW8624X_BIT_WAVCFG_SEQWAIT_TIME (1<<7) +#define AW8624X_BIT_WAVCFG_SEQWAIT_NUMBER (0<<7) +#define AW8624X_BIT_WAVCFG_SEQ (0x7F) + +/* WAVCFG9-12: reg 0x12 - reg 0x15 RW */ +#define AW8624X_BIT_WAVLOOP_SEQ_ODD_MASK (~(0x0F<<4)) +#define AW8624X_BIT_WAVLOOP_SEQ_ODD_INIFINITELY (0x0F<<4) +#define AW8624X_BIT_WAVLOOP_SEQ_EVEN_MASK (~(0x0F<<0)) +#define AW8624X_BIT_WAVLOOP_SEQ_EVEN_INIFINITELY (0x0F<<0) +#define AW8624X_BIT_WAVLOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG9: reg 0x12 RW */ +#define AW8624X_BIT_WAVCFG9_SEQ1LOOP_MASK (~(0x0F<<4)) +#define AW8624X_BIT_WAVCFG9_SEQ1LOOP_INIFINITELY (0x0F<<4) +#define AW8624X_BIT_WAVCFG9_SEQ2LOOP_MASK (~(0x0F<<0)) +#define AW8624X_BIT_WAVCFG9_SEQ2LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG10: reg 0x13 RW */ +#define AW8624X_BIT_WAVCFG10_SEQ3LOOP_MASK (~(0x0F<<4)) +#define AW8624X_BIT_WAVCFG10_SEQ3LOOP_INIFINITELY (0x0F<<4) +#define AW8624X_BIT_WAVCFG10_SEQ4LOOP_MASK (~(0x0F<<0)) +#define AW8624X_BIT_WAVCFG10_SEQ4LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG11: reg 0x14 RW */ +#define AW8624X_BIT_WAVCFG11_SEQ5LOOP_MASK (~(0x0F<<4)) +#define AW8624X_BIT_WAVCFG11_SEQ5LOOP_INIFINITELY (0x0F<<4) +#define AW8624X_BIT_WAVCFG11_SEQ6LOOP_MASK (~(0x0F<<0)) +#define AW8624X_BIT_WAVCFG11_SEQ6LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG12: reg 0x15 RW */ +#define AW8624X_BIT_WAVCFG12_SEQ7LOOP_MASK (~(0x0F<<4)) +#define AW8624X_BIT_WAVCFG12_SEQ7LOOP_INIFINITELY (0x0F<<4) +#define AW8624X_BIT_WAVCFG12_SEQ8LOOP_MASK (~(0x0F<<0)) +#define AW8624X_BIT_WAVCFG12_SEQ8LOOP_INIFINITELY (0x0F<<0) + +/***************** CONT *****************/ +/* CONTCFG1: reg 0x18 RW */ +#define AW8624X_BIT_CONTCFG1_EDGE_FRE_MASK (~(0x0F<<4)) +#define AW8624X_BIT_CONTCFG1_PEAK_NUM_MASK (~(3<<1)) +#define AW8624X_BIT_CONTCFG1_PEAK_NUM_NO_F0_DET (0<<1) +#define AW8624X_BIT_CONTCFG1_PEAK_NUM_1 (1<<1) +#define AW8624X_BIT_CONTCFG1_PEAK_NUM_2 (2<<1) +#define AW8624X_BIT_CONTCFG1_PEAK_NUM_3 (3<<1) +#define AW8624X_BIT_CONTCFG1_SIN_MODE_MASK (~(1<<0)) +#define AW8624X_BIT_CONTCFG1_SIN_MODE_COS (1<<0) +#define AW8624X_BIT_CONTCFG1_SIN_MODE_SINE (0<<0) + +/* CONTCFG5: reg 0x1C RW */ +#define AW8624X_BIT_CONTCFG5_OPEN_LOOP_NUM_MASK (~(7<<5)) +#define AW8624X_BIT_CONTCFG5_SMART_LOOP_MASK (~(1<<4)) +#define AW8624X_BIT_CONTCFG5_SMART_LOOP_ON (1<<4) +#define AW8624X_BIT_CONTCFG5_SMART_LOOP_OFF (0<<4) +#define AW8624X_BIT_CONTCFG5_BRK_GAIN_MASK (~(0x0F<<0)) + +/* CONTCFG6: reg 0x1D RW */ +#define AW8624X_BIT_CONTCFG6_TRACK_EN_MASK (~(1<<7)) +#define AW8624X_BIT_CONTCFG6_TRACK_ENABLE (1<<7) +#define AW8624X_BIT_CONTCFG6_TRACK_DISABLE (0<<7) +#define AW8624X_BIT_CONTCFG6_DRV1_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG7: reg 0x1E RW */ +#define AW8624X_BIT_CONTCFG7_DRV_SWITCH_MASK (~(1<<7)) +#define AW8624X_BIT_CONTCFG7_DRV_SWITCH_ENABLE (1<<7) +#define AW8624X_BIT_CONTCFG7_DRV_SWITCH_DISABLE (0<<7) +#define AW8624X_BIT_CONTCFG7_DRV2_LVL_MASK (~(0x7F<<0)) + +/* CONTRD20: reg 0x2B RO */ +#define AW8624X_BIT_CONTRD20_BEMF_PEAK4_L_MASK (~(3<<6)) +#define AW8624X_BIT_CONTRD20_BEMF_PEAK3_L_MASK (~(3<<4)) +#define AW8624X_BIT_CONTRD20_BEMF_PEAK2_L_MASK (~(3<<2)) +#define AW8624X_BIT_CONTRD20_BEMF_PEAK1_L_MASK (~(3<<0)) + +/* CONTRD21: reg 0x2C RO */ +#define AW8624X_BIT_CONTRD21_BEMF_PEAK5_L_MASK (~(3<<0)) + +/***************** RTP *****************/ +/* RTPCFG1: reg 0x2D RW */ +#define AW8624X_BIT_RTPCFG1_ADDRH_MASK (~(0x0F<<0)) + +/* RTPCFG3: reg 0x2F RW */ +#define AW8624X_BIT_RTPCFG3_FIFO_AEH_MASK (~(0x0F<<4)) +#define AW8624X_BIT_RTPCFG3_FIFO_AFH_MASK (~(0x0F<<0)) +#define AW8624X_BIT_RTPCFG3_FIFO_AEH (0x0F<<4) +#define AW8624X_BIT_RTPCFG3_FIFO_AFH (0x0F<<0) + +/***************** TRIGGER *****************/ +#define AW8624X_BIT_TRG_ENABLE_MASK (~(1<<7)) +#define AW8624X_BIT_TRG_ENABLE (1<<7) +#define AW8624X_BIT_TRG_DISABLE (0<<7) +#define AW8624X_BIT_TRG_SEQ_MASK (~(0x7F<<0)) + +/* TRGCFG1: reg 0x33 RW */ +#define AW8624X_BIT_TRGCFG1_TRG1_POS_MASK (~(1<<7)) +#define AW8624X_BIT_TRGCFG1_TRG1_POS_ENABLE (1<<7) +#define AW8624X_BIT_TRGCFG1_TRG1_POS_DISABLE (0<<7) +#define AW8624X_BIT_TRGCFG1_TRG1SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG2: reg 0x34 RW */ +#define AW8624X_BIT_TRGCFG2_TRG2_POS_MASK (~(1<<7)) +#define AW8624X_BIT_TRGCFG2_TRG2_POS_ENABLE (1<<7) +#define AW8624X_BIT_TRGCFG2_TRG2_POS_DISABLE (0<<7) +#define AW8624X_BIT_TRGCFG2_TRG2SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG3: reg 0x35 RW */ +#define AW8624X_BIT_TRGCFG3_TRG3_POS_MASK (~(1<<7)) +#define AW8624X_BIT_TRGCFG3_TRG3_POS_ENABLE (1<<7) +#define AW8624X_BIT_TRGCFG3_TRG3_POS_DISABLE (0<<7) +#define AW8624X_BIT_TRGCFG3_TRG3SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG4: reg 0x36 RW */ +#define AW8624X_BIT_TRGCFG4_TRG1_NEG_MASK (~(1<<7)) +#define AW8624X_BIT_TRGCFG4_TRG1_NEG_ENABLE (1<<7) +#define AW8624X_BIT_TRGCFG4_TRG1_NEG_DISABLE (0<<7) +#define AW8624X_BIT_TRGCFG4_TRG1SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG5: reg 0x37 RW */ +#define AW8624X_BIT_TRGCFG5_TRG2_NEG_MASK (~(1<<7)) +#define AW8624X_BIT_TRGCFG5_TRG2_NEG_ENABLE (1<<7) +#define AW8624X_BIT_TRGCFG5_TRG2_NEG_DISABLE (0<<7) +#define AW8624X_BIT_TRGCFG5_TRG2SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG6: reg 0x38 RW */ +#define AW8624X_BIT_TRGCFG6_TRG3_NEG_MASK (~(1<<7)) +#define AW8624X_BIT_TRGCFG6_TRG3_NEG_ENABLE (1<<7) +#define AW8624X_BIT_TRGCFG6_TRG3_NEG_DISABLE (0<<7) +#define AW862XX_BIT_TRGCFG6_TRG3SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG7: reg 0x39 RW */ +#define AW8624X_BIT_TRGCFG7_TRG1_POR_LEV_BRK_MASK (~(7<<5)) +#define AW8624X_BIT_TRGCFG7_TRG2_POR_LEV_BRK_MASK (~(7<<1)) +#define AW8624X_BIT_TRGCFG7_TRG1_POLAR_MASK (~(1<<7)) +#define AW8624X_BIT_TRGCFG7_TRG1_POLAR_NEG (1<<7) +#define AW8624X_BIT_TRGCFG7_TRG1_POLAR_POS (0<<7) +#define AW8624X_BIT_TRGCFG7_TRG1_MODE_MASK (~(1<<6)) +#define AW8624X_BIT_TRGCFG7_TRG1_MODE_LEVEL (1<<6) +#define AW8624X_BIT_TRGCFG7_TRG1_MODE_EDGE (0<<6) +#define AW8624X_BIT_TRGCFG7_TRG1_AUTO_BRK_MASK (~(1<<5)) +#define AW8624X_BIT_TRGCFG7_TRG1_AUTO_BRK_ENABLE (1<<5) +#define AW8624X_BIT_TRGCFG7_TRG1_AUTO_BRK_DISABLE (0<<5) +#define AW8624X_BIT_TRGCFG7_TRG2_POLAR_MASK (~(1<<3)) +#define AW8624X_BIT_TRGCFG7_TRG2_POLAR_NEG (1<<3) +#define AW8624X_BIT_TRGCFG7_TRG2_POLAR_POS (0<<3) +#define AW8624X_BIT_TRGCFG7_TRG2_MODE_MASK (~(1<<2)) +#define AW8624X_BIT_TRGCFG7_TRG2_MODE_LEVEL (1<<2) +#define AW8624X_BIT_TRGCFG7_TRG2_MODE_EDGE (0<<2) +#define AW8624X_BIT_TRGCFG7_TRG2_AUTO_BRK_MASK (~(1<<1)) +#define AW8624X_BIT_TRGCFG7_TRG2_AUTO_BRK_ENABLE (1<<1) +#define AW8624X_BIT_TRGCFG7_TRG2_AUTO_BRK_DISABLE (0<<1) + +/* TRGCFG8: reg 0x3A RW */ +#define AW8624X_BIT_TRGCFG8_TRG3_POR_LEV_BRK_MASK (~(7<<5)) +#define AW8624X_BIT_TRGCFG8_TRG3_POLAR_MASK (~(1<<7)) +#define AW8624X_BIT_TRGCFG8_TRG3_POLAR_NEG (1<<7) +#define AW8624X_BIT_TRGCFG8_TRG3_POLAR_POS (0<<7) +#define AW8624X_BIT_TRGCFG8_TRG3_MODE_MASK (~(1<<6)) +#define AW8624X_BIT_TRGCFG8_TRG3_MODE_LEVEL (1<<6) +#define AW8624X_BIT_TRGCFG8_TRG3_MODE_EDGE (0<<6) +#define AW8624X_BIT_TRGCFG8_TRG3_AUTO_BRK_MASK (~(1<<5)) +#define AW8624X_BIT_TRGCFG8_TRG3_AUTO_BRK_ENABLE (1<<5) +#define AW8624X_BIT_TRGCFG8_TRG3_AUTO_BRK_DISABLE (0<<5) +#define AW8624X_BIT_TRGCFG8_TRG1_STOP_MASK (~(1<<2)) +#define AW8624X_BIT_TRGCFG8_TRG1_STOP (1<<2) +#define AW8624X_BIT_TRGCFG8_TRG2_STOP_MASK (~(1<<1)) +#define AW8624X_BIT_TRGCFG8_TRG2_STOP (1<<1) +#define AW8624X_BIT_TRGCFG8_TRG3_STOP_MASK (~(1<<0)) +#define AW8624X_BIT_TRGCFG8_TRG3_STOP (1<<0) + +/* GLBCFG2: reg 0x3C RW */ +/* START_DLY */ +#define AW8624X_BIT_GLBCFG2_START_DLY_250US (0x0C) + +/* GLBCFG4: reg 0x3E RW */ +#define AW8624X_BIT_GLBCFG4_GO_PRIO_MASK (~(3<<6)) +#define AW8624X_BIT_GLBCFG4_TRG3_PRIO_MASK (~(3<<4)) +#define AW8624X_BIT_GLBCFG4_TRG2_PRIO_MASK (~(3<<2)) +#define AW8624X_BIT_GLBCFG4_TRG1_PRIO_MASK (~(3<<0)) + +/* SYSCTRL1: reg 0x43 RW */ +#define AW8624X_BIT_SYSCTRL1_VBAT_MODE_MASK (~(1<<7)) +#define AW8624X_BIT_SYSCTRL1_VBAT_MODE_HW (1<<7) +#define AW8624X_BIT_SYSCTRL1_VBAT_MODE_SW (0<<7) +#define AW8624X_BIT_SYSCTRL1_INT_BRK_MASK (~(1<<6)) +#define AW8624X_BIT_SYSCTRL1_INT_BRK_ON (1<<6) +#define AW8624X_BIT_SYSCTRL1_INT_BRK_OFF (0<<6) +#define AW8624X_BIT_SYSCTRL1_RCK_FRE_MASK (~(3<<4)) +#define AW8624X_BIT_SYSCTRL1_RCK_FRE_127 (3<<4) +#define AW8624X_BIT_SYSCTRL1_RCK_FRE_255 (2<<4) +#define AW8624X_BIT_SYSCTRL1_RCK_FRE_374 (1<<4) +#define AW8624X_BIT_SYSCTRL1_RCK_FRE_511 (0<<4) +#define AW8624X_BIT_SYSCTRL1_RAMINIT_MASK (~(1<<3)) +#define AW8624X_BIT_SYSCTRL1_RAMINIT_ON (1<<3) +#define AW8624X_BIT_SYSCTRL1_RAMINIT_OFF (0<<3) +#define AW8624X_BIT_SYSCTRL1_EN_FIR_MASK (~(1<<2)) +#define AW8624X_BIT_SYSCTRL1_FIR_ENABLE (0<<2) +#define AW8624X_BIT_SYSCTRL1_WAKE_MODE_MASK (~(1<<1)) +#define AW8624X_BIT_SYSCTRL1_WAKE_MODE_WAKEUP (1<<1) +#define AW8624X_BIT_SYSCTRL1_WAKE_MODE_BST (0<<1) +#define AW8624X_BIT_SYSCTRL1_RTP_CLK_MASK (~(1<<0)) +#define AW8624X_BIT_SYSCTRL1_RTP_PLL (1<<0) +#define AW8624X_BIT_SYSCTRL1_RTP_OSC (0<<0) + +/* SYSCTRL2: reg 0x44 RW */ +#define AW8624X_BIT_SYSCTRL2_WAKE_MASK (~(1<<7)) +#define AW8624X_BIT_SYSCTRL2_WAKE_ON (1<<7) +#define AW8624X_BIT_SYSCTRL2_WAKE_OFF (0<<7) +#define AW8624X_BIT_SYSCTRL2_STANDBY_MASK (~(1<<6)) +#define AW8624X_BIT_SYSCTRL2_STANDBY_ON (1<<6) +#define AW8624X_BIT_SYSCTRL2_STANDBY_OFF (0<<6) +#define AW8624X_BIT_SYSCTRL2_RTP_DLY_MASK (~(3<<4)) +#define AW8624X_BIT_SYSCTRL2_WAIT_NHZ_MASK (~(1<<3)) +#define AW8624X_BIT_SYSCTRL2_WAIT_NHZ_ENABLE (0<<3) +#define AW8624X_BIT_SYSCTRL2_WAIT_NHZ_DISABLE (1<<3) +#define AW8624X_BIT_SYSCTRL2_WCK_PIN_MASK (~(1<<2)) +#define AW8624X_BIT_SYSCTRL2_ENABLE_TRIG1 (1<<2) +#define AW8624X_BIT_SYSCTRL2_DISENABLE_TRIG1 (0<<2) +#define AW8624X_BIT_SYSCTRL2_WAVDAT_MODE_MASK (~(3<<0)) +#define AW8624X_BIT_SYSCTRL2_RATE (3<<0) +#define AW8624X_BIT_SYSCTRL2_RATE_12K (2<<0) +#define AW8624X_BIT_SYSCTRL2_RATE_48K (1<<0) +#define AW8624X_BIT_SYSCTRL2_RATE_24K (0<<0) + +/* SYSCTRL3: reg 0x45 RW */ +#define AW8624X_BIT_SYSCTRL3_TSET_MASK (~(0x0F<<4)) +#define AW8624X_BIT_SYSCTRL3_BEMF_SET_MASK (~(0x0F<<0)) + +/* SYSCTRL5: reg 0x49 RW */ +#define AW8624X_BIT_SYSCTRL5_INTBRK_MD_MASK (~(1<<7)) +#define AW8624X_BIT_SYSCTRL5_INTBRK_MD_ENABLE (1<<7) +#define AW8624X_BIT_SYSCTRL5_INTBRK_MD_DISABLE (0<<7) +#define AW8624X_BIT_SYSCTRL5_GAIN_BYPASS_MASK (~(1<<6)) +#define AW8624X_BIT_SYSCTRL5_GAIN_CHANGEABLE (1<<6) +#define AW8624X_BIT_SYSCTRL5_GAIN_FIXED (0<<6) +#define AW8624X_BIT_SYSCTRL5_INT_EDGE_MODE_MASK (~(1<<5)) +#define AW8624X_BIT_SYSCTRL5_INT_EDGE_MODE_POS (0<<5) +#define AW8624X_BIT_SYSCTRL5_INT_EDGE_MODE_BOTH (1<<5) +#define AW8624X_BIT_SYSCTRL5_INT_MODE_MASK (~(1<<4)) +#define AW8624X_BIT_SYSCTRL5_INT_MODE_EDGE (1<<4) +#define AW8624X_BIT_SYSCTRL5_INT_MODE_LEVEL (0<<4) +#define AW8624X_BIT_SYSCTRL5_INTP_MASK (~(1<<3)) +#define AW8624X_BIT_SYSCTRL5_INTP_HIGH (1<<3) +#define AW8624X_BIT_SYSCTRL5_INTP_LOW (0<<3) +#define AW8624X_BIT_SYSCTRL5_D2S_GAIN_MASK (~(7<<0)) +#define AW8624X_BIT_SYSCTRL5_D2S_GAIN_1 (0<<0) +#define AW8624X_BIT_SYSCTRL5_D2S_GAIN_2 (1<<0) +#define AW8624X_BIT_SYSCTRL5_D2S_GAIN_4 (2<<0) +#define AW8624X_BIT_SYSCTRL5_D2S_GAIN_5 (3<<0) +#define AW8624X_BIT_SYSCTRL5_D2S_GAIN_8 (4<<0) +#define AW8624X_BIT_SYSCTRL5_D2S_GAIN_10 (5<<0) +#define AW8624X_BIT_SYSCTRL5_D2S_GAIN_20 (6<<0) +#define AW8624X_BIT_SYSCTRL5_D2S_GAIN_40 (7<<0) + +/* SYSCTRL6: reg 0x4A RW */ +#define AW8624X_BIT_SYSCTRL6_F0_D2S_GAIN_MASK (~(7<<0)) +#define AW8624X_BIT_SYSCTRL6_F0_D2S_GAIN_1 (0<<0) +#define AW8624X_BIT_SYSCTRL6_F0_D2S_GAIN_2 (1<<0) +#define AW8624X_BIT_SYSCTRL6_F0_D2S_GAIN_4 (2<<0) +#define AW8624X_BIT_SYSCTRL6_F0_D2S_GAIN_5 (3<<0) +#define AW8624X_BIT_SYSCTRL6_F0_D2S_GAIN_8 (4<<0) +#define AW8624X_BIT_SYSCTRL6_F0_D2S_GAIN_10 (5<<0) +#define AW8624X_BIT_SYSCTRL6_F0_D2S_GAIN_20 (6<<0) +#define AW8624X_BIT_SYSCTRL6_F0_D2S_GAIN_40 (7<<0) + +/* PWMCFG1: reg 0x4C RW */ +#define AW8624X_BIT_PWMCFG1_PRC_EN_MASK (~(1<<7)) +#define AW8624X_BIT_PWMCFG1_PRC_ENABLE (1<<7) +#define AW8624X_BIT_PWMCFG1_PRC_DISABLE (0<<7) +#define AW8624X_BIT_PWMCFG1_PRCTIME_MASK (~(0x7F<<0)) + +/* PWMCFG3: reg 0x4E RW */ +#define AW8624X_BIT_PWMCFG3_PR_EN_MASK (~(1<<7)) +#define AW8624X_BIT_PWMCFG3_PR_ENABLE (1<<7) +#define AW8624X_BIT_PWMCFG3_PR_DISABLE (0<<7) +#define AW8624X_BIT_PWMCFG3_PRLVL_MASK (~(0x7F<<0)) +#define AW8624X_BIT_PWMCFG3_PRLVL_DEFAULT_VALUE (0x3F) + +/* PWMCFG4: reg 0x4F RW */ +/* PRTIME */ +#define AW8624X_PWMCFG4_PRTIME_DEFAULT_VALUE (0x32) + +/* DET_LO: reg 0x50 RO */ +#define AW8624X_BIT_DET_LO_TEST_LO_MASK (~(3<<6)) +#define AW8624X_BIT_DET_LO_VBAT_LO_MASK (~(3<<4)) +#define AW8624X_BIT_DET_LO_VBAT (3<<4) +#define AW8624X_BIT_DET_LO_OS_MASK (~(3<<2)) +#define AW8624X_BIT_DET_LO_RL_MASK (~(3<<0)) +#define AW8624X_BIT_DET_LO_RL (3<<0) + +/* DETCFG1: reg 0x51 RW */ +#define AW8624X_BIT_DETCFG1_FTS_GO_MASK (~(1<<7)) +#define AW8624X_BIT_DETCFG1_FTS_GO_ENABLE (1<<7) +#define AW8624X_BIT_DETCFG1_TEST_GO_MASK (~(1<<6)) +#define AW8624X_BIT_DETCFG1_TEST_GO_ENABLE (1<<6) +#define AW8624X_BIT_DETCFG1_ADO_SLOT_MODE_MASK (~(1<<5)) +#define AW8624X_BIT_DETCFG1_ADO_SLOT_ADC_32 (1<<5) +#define AW8624X_BIT_DETCFG1_ADO_SLOT_ADC_256 (0<<5) +#define AW8624X_BIT_DETCFG1_RL_OS_MASK (~(1<<4)) +#define AW8624X_BIT_DETCFG1_RL (1<<4) +#define AW8624X_BIT_DETCFG1_OS (0<<4) +#define AW8624X_BIT_DETCFG1_PRCT_MODE_MASK (~(1<<3)) +#define AW8624X_BIT_DETCFG1_PRCT_MODE_INVALID (0<<3) +#define AW8624X_BIT_DETCFG1_PRCT_MODE_VALID (1<<3) +#define AW8624X_BIT_DETCFG1_CLK_ADC_MASK (~(7<<0)) +#define AW8624X_BIT_DETCFG1_CLK_ADC_12M (0<<0) +#define AW8624X_BIT_DETCFG1_CLK_ADC_6M (1<<0) +#define AW8624X_BIT_DETCFG1_CLK_ADC_3M (2<<0) +#define AW8624X_BIT_DETCFG1_CLK_ADC_1M5 (3<<0) +#define AW8624X_BIT_DETCFG1_CLK_ADC_M75 (4<<0) +#define AW8624X_BIT_DETCFG1_CLK_ADC_M37 (5<<0) +#define AW8624X_BIT_DETCFG1_CLK_ADC_M18 (6<<0) +#define AW8624X_BIT_DETCFG1_CLK_ADC_M09 (7<<0) + +/* DETCFG2: reg 0x52 RW */ +#define AW8624X_BIT_DETCFG2_VBAT_GO_MASK (~(1<<1)) +#define AW8624X_BIT_DETCFG2_VABT_GO_ON (1<<1) +#define AW8624X_BIT_DETCFG2_DIAG_GO_MASK (~(1<<0)) +#define AW8624X_BIT_DETCFG2_DIAG_GO_ON (1<<0) + +/* TRIMCFG1: reg:0x59 RW */ +#define AW8624X_BIT_TRIMCFG1_RL_TRIM_SRC_MASK (~(1<<6)) +#define AW8624X_BIT_TRIMCFG1_RL_TRIM_SRC_REG (1<<6) +#define AW8624X_BIT_TRIMCFG1_RL_TRIM_SRC_EFUSE (0<<6) +#define AW8624X_BIT_TRIMCFG1_TRIM_RL_MASK (~(63<<0)) + +/* TRIMCFG3: reg:0x5A RW */ +#define AW8624X_BIT_TRIMCFG3_OSC_TRIM_SRC_MASK (~(1<<7)) +#define AW8624X_BIT_TRIMCFG3_OSC_TRIM_SRC_REG (1<<7) +#define AW8624X_BIT_TRIMCFG3_OSC_TRIM_SRC_EFUSE (0<<7) +#define AW8624X_BIT_TRIMCFG3_TRIM_LRA_MASK (~(0x7F<<0)) + +/* EFRD9: reg:0x64 RO */ + +/* ANACFG8: reg:0x77 RW */ +#define AW8624X_BIT_ANACFG8_TRTF_CTRL_HDRV_MASK (~(3<<6)) +#define AW8624X_BIT_ANACFG8_TRTF_CTRL_HDRV (3<<6) + +#endif diff --git a/drivers/misc/awinic/aw8695/Android.mk b/drivers/misc/awinic/aw8695/Android.mk new file mode 100644 index 000000000000..57cc3cbfb8d7 --- /dev/null +++ b/drivers/misc/awinic/aw8695/Android.mk @@ -0,0 +1,22 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(AF_NOISE_ELIMINATION_ENABLE), true) + KERNEL_CFLAGS += CONFIG_AF_NOISE_ELIMINATION=y +endif + +ifeq ($(AW8965_VIBRATOR_SHORT_WAV_ENABLE), true) + KBUILD_OPTIONS += CONFIG_AW8965_VIBRATOR_SHORT_WAV_ENABLE=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := aw8695.ko +LOCAL_MODULE_TAGS := optional + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/awinic/aw8695/Kbuild b/drivers/misc/awinic/aw8695/Kbuild new file mode 100644 index 000000000000..20db7c5c2ae2 --- /dev/null +++ b/drivers/misc/awinic/aw8695/Kbuild @@ -0,0 +1,13 @@ +# add -Wall to try to catch everything we can. +ifneq ($(filter m y,$(CONFIG_AF_NOISE_ELIMINATION)),) + EXTRA_CFLAGS += -DCONFIG_AF_NOISE_ELIMINATION +endif + +ifneq ($(filter y,$(CONFIG_AW8965_VIBRATOR_SHORT_WAV_ENABLE)),) + EXTRA_CFLAGS += -DCONFIG_AW8965_VIBRATOR_SHORT_WAV_ENABLE +endif + +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += aw8695.o diff --git a/drivers/misc/awinic/aw8695/Makefile b/drivers/misc/awinic/aw8695/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/misc/awinic/aw8695/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/misc/awinic/aw8695/aw8695.c b/drivers/misc/awinic/aw8695/aw8695.c new file mode 100644 index 000000000000..0492eb76ef78 --- /dev/null +++ b/drivers/misc/awinic/aw8695/aw8695.c @@ -0,0 +1,3949 @@ +/* + * aw8695.c aw8695 haptic module + * + * Version: v1.3.6 + * + * Copyright (c) 2018 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw8695.h" +#include "aw8695_reg.h" +#include "aw8695_config.h" + +/****************************************************** + * + * Marco + * + ******************************************************/ +#define AW8695_I2C_NAME "aw8695_haptic" +#define AW8695_HAPTIC_NAME "aw8695_haptic" + +#define AW8695_VERSION "v1.3.6" + + +#define AWINIC_RAM_UPDATE_DELAY + +#define AW_I2C_RETRIES 2 +#define AW_I2C_RETRY_DELAY 2 +#define AW_READ_CHIPID_RETRIES 5 +#define AW_READ_CHIPID_RETRY_DELAY 2 + +#define AW8695_MAX_DSP_START_TRY_COUNT 10 + + + +#define AW8695_MAX_FIRMWARE_LOAD_CNT 20 +#define AW8695_SEQ_NO_RTP_BASE 102 +#define AW8695_SEQ_NO_RTP_REPEAT 100 +#define AW8695_SEQ_NO_RTP_STOP 120000 + +#define AW8695_REPEAT_RTP_PLAYING + +#define PM_QOS_VALUE_VB 400 +struct pm_qos_request pm_qos_req_vb; + +#ifdef CONFIG_AF_NOISE_ELIMINATION +static bool is_af_enabled = false; +#endif +/****************************************************** + * + * variable + * + ******************************************************/ +#define AW8695_RTP_NAME_MAX 64 +static char *aw8695_ram_name = "aw8695_haptic.bin"; +static char aw8695_rtp_name[][AW8695_RTP_NAME_MAX] = { + {"aw8695_rtp.bin"}, + {"aw8695_rtp_Argo_Navis.bin"}, + {"aw8695_rtp_Attentive.bin"}, + {"aw8695_rtp_Awake.bin"}, + {"aw8695_rtp_Bird_Loop.bin"}, + {"aw8695_rtp_Brilliant_Times.bin"}, + {"aw8695_rtp_Chimey_Phone.bin"}, + {"aw8695_rtp_Complex.bin"}, + {"aw8695_rtp_Crazy_Dream.bin"}, + {"aw8695_rtp_Curve_Ball_Blend.bin"}, + {"aw8695_rtp_Digital_Phone.bin"}, + {"aw8695_rtp_Electrovision.bin"}, + {"aw8695_rtp_Ether_Shake.bin"}, + {"aw8695_rtp_Fateful_Words.bin"}, + {"aw8695_rtp_Flutey_Phone.bin"}, + {"aw8695_rtp_Future_Funk.bin"}, + {"aw8695_rtp_Future_Hi_Tech.bin"}, + {"aw8695_rtp_Girtab.bin"}, + {"aw8695_rtp_Hello.bin"}, + {"aw8695_rtp_Hexagon.bin"}, + {"aw8695_rtp_Hydra.bin"}, + {"aw8695_rtp_Insert_Coin.bin"}, + {"aw8695_rtp_Jumping_Dots.bin"}, + {"aw8695_rtp_Keys.bin"}, + {"aw8695_rtp_Loopy.bin"}, + {"aw8695_rtp_Loopy_Lounge.bin"}, + {"aw8695_rtp_Modular.bin"}, + {"aw8695_rtp_Momentum.bin"}, + {"aw8695_rtp_Morning.bin"}, + {"aw8695_rtp_Moto.bin"}, + {"aw8695_rtp_Natural.bin"}, + {"aw8695_rtp_New_Player.bin"}, + {"aw8695_rtp_Onward.bin"}, + {"aw8695_rtp_Organ_Dub.bin"}, + {"aw8695_rtp_Overclocked.bin"}, + {"aw8695_rtp_Pegasus.bin"}, + {"aw8695_rtp_Pyxis.bin"}, + {"aw8695_rtp_Regrade.bin"}, + {"aw8695_rtp_Scarabaeus.bin"}, + {"aw8695_rtp_Sceptrum.bin"}, + {"aw8695_rtp_Simple.bin"}, + {"aw8695_rtp_Solarium.bin"}, + {"aw8695_rtp_Sparse.bin"}, + {"aw8695_rtp_Terrabytes.bin"}, + {"aw8695_rtp_Zero_Hour.bin"}, + {"aw8695_rtp_Play.bin"}, + {"aw8695_rtp_TJINGLE.bin"}, + {"aw8695_rtp_Verizon_Airwaves.bin"}, + {"aw8695_rtp_City_Lights.bin"}, + {"aw8695_rtp_Firefly.bin"}, + {"aw8695_rtp_Now_or_Never.bin"}, + {"aw8695_rtp_Moto_Retro.bin"}, + {"aw8695_rtp_Moto_Original.bin"}, +}; + +struct aw8695_container *aw8695_rtp; +struct aw8695 *g_aw8695; + +/****************************************************** + * + * functions + * + ******************************************************/ +static void aw8695_interrupt_clear(struct aw8695 *aw8695); +static int aw8695_haptic_trig_enable_config(struct aw8695 *aw8695); +static void aw8695_vibrate(struct aw8695 *aw8695, int value); +#ifdef CONFIG_AF_NOISE_ELIMINATION +extern int mot_actuator_on_vibrate_start(void); +extern int mot_actuator_on_vibrate_stop(void); +#endif + +/****************************************************** +* +* aw8695 i2c write/read +* +******************************************************/ +static int aw8695_i2c_write(struct aw8695 *aw8695, + unsigned char reg_addr, unsigned char reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + while (cnt < AW_I2C_RETRIES) { + ret = i2c_smbus_write_byte_data(aw8695->i2c, reg_addr, reg_data); + if (ret < 0) { + pr_err("%s: i2c_write cnt=%d error=%d\n", __func__, cnt, ret); + } else { + break; + } + cnt ++; + msleep(AW_I2C_RETRY_DELAY); + } + + return ret; +} + +static int aw8695_i2c_read(struct aw8695 *aw8695, + unsigned char reg_addr, unsigned char *reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + while (cnt < AW_I2C_RETRIES) { + ret = i2c_smbus_read_byte_data(aw8695->i2c, reg_addr); + if (ret < 0) { + pr_err("%s: i2c_read cnt=%d error=%d\n", __func__, cnt, ret); + } else { + *reg_data = ret; + break; + } + cnt ++; + msleep(AW_I2C_RETRY_DELAY); + } + + return ret; +} + +static int aw8695_i2c_write_bits(struct aw8695 *aw8695, + unsigned char reg_addr, unsigned int mask, unsigned char reg_data) +{ + unsigned char reg_val = 0; + + aw8695_i2c_read(aw8695, reg_addr, ®_val); + reg_val &= mask; + reg_val |= reg_data; + aw8695_i2c_write(aw8695, reg_addr, reg_val); + + return 0; +} + +static int aw8695_i2c_writes(struct aw8695 *aw8695, + unsigned char reg_addr, unsigned char *buf, unsigned int len) +{ + int ret = -1; + unsigned char *data; + + data = kmalloc(len + 1, GFP_KERNEL); + if (data == NULL) { + pr_err("%s: can not allocate memory\n", __func__); + return -ENOMEM; + } + + data[0] = reg_addr; + memcpy(&data[1], buf, len); + + ret = i2c_master_send(aw8695->i2c, data, len + 1); + if (ret < 0) { + pr_err("%s: i2c master send error\n", __func__); + } + + kfree(data); + + return ret; +} + +/***************************************************** + * + * ram update + * + *****************************************************/ +static void aw8695_rtp_loaded(const struct firmware *cont, void *context) +{ + struct aw8695 *aw8695 = context; + pr_info("%s enter\n", __func__); + + if (!cont) { + pr_err("%s: failed to read %s\n", __func__, aw8695_rtp_name[aw8695->rtp_file_num]); + release_firmware(cont); + return; + } + + pr_info("%s: loaded %s - size: %zu\n", __func__, aw8695_rtp_name[aw8695->rtp_file_num], + cont ? cont->size : 0); + + /* aw8695 rtp update */ + aw8695_rtp = vzalloc(cont->size + sizeof(int)); + if (!aw8695_rtp) { + release_firmware(cont); + pr_err("%s: Error allocating memory\n", __func__); + return; + } + aw8695_rtp->len = cont->size; + pr_info("%s: rtp size = %d\n", __func__, aw8695_rtp->len); + memcpy(aw8695_rtp->data, cont->data, cont->size); + release_firmware(cont); + + aw8695->rtp_init = 1; + pr_info("%s: rtp update complete\n", __func__); +} + +static int aw8695_rtp_update(struct aw8695 *aw8695) +{ + pr_info("%s enter\n", __func__); + + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw8695_rtp_name[aw8695->rtp_file_num], aw8695->dev, GFP_KERNEL, + aw8695, aw8695_rtp_loaded); +} + + +static void aw8695_container_update(struct aw8695 *aw8695, + struct aw8695_container *aw8695_cont) +{ + int i = 0; + unsigned int shift = 0; + + pr_info("%s enter\n", __func__); + + mutex_lock(&aw8695->lock); + + aw8695->ram.baseaddr_shift = 2; + aw8695->ram.ram_shift = 4; + + /* RAMINIT Enable */ + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_RAMINIT_MASK, AW8695_BIT_SYSCTRL_RAMINIT_EN); + + /* base addr */ + shift = aw8695->ram.baseaddr_shift; + aw8695->ram.base_addr = (unsigned int)((aw8695_cont->data[0 + shift] << 8) | + (aw8695_cont->data[1 + shift])); + pr_info("%s: base_addr=0x%4x\n", __func__, aw8695->ram.base_addr); + + aw8695_i2c_write(aw8695, AW8695_REG_BASE_ADDRH, aw8695_cont->data[0 + shift]); + aw8695_i2c_write(aw8695, AW8695_REG_BASE_ADDRL, aw8695_cont->data[1 + shift]); + + aw8695_i2c_write(aw8695, AW8695_REG_FIFO_AEH, + (unsigned char)((aw8695->ram.base_addr >> 2) >> 8)); + aw8695_i2c_write(aw8695, AW8695_REG_FIFO_AEL, + (unsigned char)((aw8695->ram.base_addr >> 2) & 0x00FF)); + aw8695_i2c_write(aw8695, AW8695_REG_FIFO_AFH, + (unsigned char)((aw8695->ram.base_addr - (aw8695->ram.base_addr >> 2)) >> 8)); + aw8695_i2c_write(aw8695, AW8695_REG_FIFO_AFL, + (unsigned char)((aw8695->ram.base_addr - (aw8695->ram.base_addr >> 2)) & 0x00FF)); + + /* ram */ + shift = aw8695->ram.baseaddr_shift; + aw8695_i2c_write(aw8695, AW8695_REG_RAMADDRH, aw8695_cont->data[0 + shift]); + aw8695_i2c_write(aw8695, AW8695_REG_RAMADDRL, aw8695_cont->data[1 + shift]); + shift = aw8695->ram.ram_shift; + for (i = shift; i < aw8695_cont->len; i++) { + aw8695_i2c_write(aw8695, AW8695_REG_RAMDATA, aw8695_cont->data[i]); + } + +#if 0 + /* ram check */ + shift = aw8695->ram.baseaddr_shift; + aw8695_i2c_write(aw8695, AW8695_REG_RAMADDRH, aw8695_cont->data[0 + shift]); + aw8695_i2c_write(aw8695, AW8695_REG_RAMADDRL, aw8695_cont->data[1 + shift]); + shift = aw8695->ram.ram_shift; + for (i = shift; i < aw8695_cont->len; i++) { + aw8695_i2c_read(aw8695, AW8695_REG_RAMDATA, ®_val); + if (reg_val != aw8695_cont->data[i]) { + pr_err("%s: ram check error addr=0x%04x, file_data=0x%02x, ram_data=0x%02x\n", + __func__, i, aw8695_cont->data[i], reg_val); + return; + } + } +#endif + + /* RAMINIT Disable */ + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_RAMINIT_MASK, AW8695_BIT_SYSCTRL_RAMINIT_OFF); + + mutex_unlock(&aw8695->lock); + + pr_info("%s exit\n", __func__); +} + + +static void aw8695_ram_loaded(const struct firmware *cont, void *context) +{ + struct aw8695 *aw8695 = context; + struct aw8695_container *aw8695_fw; + int i = 0; + unsigned short check_sum = 0; + + pr_info("%s enter\n", __func__); + + if (!cont) { + pr_err("%s: failed to read %s\n", __func__, aw8695_ram_name); + release_firmware(cont); + return; + } + + pr_info("%s: loaded %s - size: %zu\n", __func__, aw8695_ram_name, + cont ? cont->size : 0); + /* + for(i=0; isize; i++) { + pr_info("%s: addr:0x%04x, data:0x%02x\n", __func__, i, *(cont->data+i)); + } + */ + + /* check sum */ + for (i = 2; i < cont->size; i++) { + check_sum += cont->data[i]; + } + if (check_sum != (unsigned short)((cont->data[0] << 8) | (cont->data[1]))) { + pr_err("%s: check sum err: check_sum=0x%04x\n", __func__, check_sum); + return; + } else { + pr_info("%s: check sum pass : 0x%04x\n", __func__, check_sum); + aw8695->ram.check_sum = check_sum; + } + + /* aw8695 ram update */ + aw8695_fw = kzalloc(cont->size + sizeof(int), GFP_KERNEL); + if (!aw8695_fw) { + release_firmware(cont); + pr_err("%s: Error allocating memory\n", __func__); + return; + } + aw8695_fw->len = cont->size; + memcpy(aw8695_fw->data, cont->data, cont->size); + release_firmware(cont); + + aw8695_container_update(aw8695, aw8695_fw); + + aw8695->ram.len = aw8695_fw->len; + + kfree(aw8695_fw); + + aw8695->ram_init = 1; + pr_info("%s: fw update complete\n", __func__); + + aw8695_haptic_trig_enable_config(aw8695); + + aw8695_rtp_update(aw8695); +} + +static int aw8695_ram_update(struct aw8695 *aw8695) +{ + aw8695->ram_init = 0; + aw8695->rtp_init = 0; + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw8695_ram_name, aw8695->dev, GFP_KERNEL, + aw8695, aw8695_ram_loaded); +} + +#ifdef AWINIC_RAM_UPDATE_DELAY +static void aw8695_ram_work_routine(struct work_struct *work) +{ + struct aw8695 *aw8695 = container_of(work, struct aw8695, ram_work.work); + + pr_info("%s enter\n", __func__); + + aw8695_ram_update(aw8695); + +} +#endif + +static int aw8695_ram_init(struct aw8695 *aw8695) +{ +#ifdef AWINIC_RAM_UPDATE_DELAY + int ram_timer_val = 5000; + INIT_DELAYED_WORK(&aw8695->ram_work, aw8695_ram_work_routine); + schedule_delayed_work(&aw8695->ram_work, msecs_to_jiffies(ram_timer_val)); +#else + aw8695_ram_update(aw8695); +#endif + return 0; +} + + + +/***************************************************** + * + * haptic control + * + *****************************************************/ +static int aw8695_haptic_softreset(struct aw8695 *aw8695) +{ + pr_debug("%s enter\n", __func__); + + aw8695_i2c_write(aw8695, AW8695_REG_ID, 0xAA); + msleep(1); + return 0; +} + +static int aw8695_haptic_active(struct aw8695 *aw8695) +{ + pr_debug("%s enter\n", __func__); + + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_WORK_MODE_MASK, AW8695_BIT_SYSCTRL_ACTIVE); + aw8695_interrupt_clear(aw8695); + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSINTM, + AW8695_BIT_SYSINTM_UVLO_MASK, AW8695_BIT_SYSINTM_UVLO_EN); + return 0; +} + +static int aw8695_haptic_play_mode(struct aw8695 *aw8695, unsigned char play_mode) +{ + pr_debug("%s enter\n", __func__); + + switch (play_mode) { + case AW8695_HAPTIC_STANDBY_MODE: + aw8695->play_mode = AW8695_HAPTIC_STANDBY_MODE; + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSINTM, + AW8695_BIT_SYSINTM_UVLO_MASK, AW8695_BIT_SYSINTM_UVLO_OFF); + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_WORK_MODE_MASK, AW8695_BIT_SYSCTRL_STANDBY); + break; + case AW8695_HAPTIC_RAM_MODE: + aw8695->play_mode = AW8695_HAPTIC_RAM_MODE; + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_PLAY_MODE_MASK, AW8695_BIT_SYSCTRL_PLAY_MODE_RAM); + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_BST_MODE_MASK, AW8695_BIT_SYSCTRL_BST_MODE_BYPASS); + if (aw8695->auto_boost) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_BST_AUTO, + AW8695_BIT_BST_AUTO_BST_RAM_MASK, AW8695_BIT_BST_AUTO_BST_RAM_DISABLE); + } + aw8695_haptic_active(aw8695); + if (aw8695->auto_boost) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_BST_AUTO, + AW8695_BIT_BST_AUTO_BST_RAM_MASK, AW8695_BIT_BST_AUTO_BST_RAM_ENABLE); + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_BST_MODE_MASK & AW8695_BIT_SYSCTRL_WORK_MODE_MASK, + AW8695_BIT_SYSCTRL_BST_MODE_BOOST | AW8695_BIT_SYSCTRL_STANDBY); + aw8695_haptic_active(aw8695); + } else { + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_BST_MODE_MASK, AW8695_BIT_SYSCTRL_BST_MODE_BOOST); + } + msleep(3); + break; + case AW8695_HAPTIC_RAM_LOOP_MODE: + aw8695->play_mode = AW8695_HAPTIC_RAM_LOOP_MODE; + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_PLAY_MODE_MASK, AW8695_BIT_SYSCTRL_PLAY_MODE_RAM); + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_BST_MODE_MASK, AW8695_BIT_SYSCTRL_BST_MODE_BYPASS); + if (aw8695->auto_boost) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_BST_AUTO, + AW8695_BIT_BST_AUTO_BST_RAM_MASK, AW8695_BIT_BST_AUTO_BST_RAM_DISABLE); + } + aw8695_haptic_active(aw8695); + break; + case AW8695_HAPTIC_RTP_MODE: + aw8695->play_mode = AW8695_HAPTIC_RTP_MODE; + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_PLAY_MODE_MASK, AW8695_BIT_SYSCTRL_PLAY_MODE_RTP); + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_BST_MODE_MASK, AW8695_BIT_SYSCTRL_BST_MODE_BYPASS); + if (aw8695->auto_boost) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_BST_AUTO, + AW8695_BIT_BST_AUTO_BST_RAM_MASK, AW8695_BIT_BST_AUTO_BST_RTP_DISABLE); + } + aw8695_haptic_active(aw8695); + if (aw8695->auto_boost) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_BST_AUTO, + AW8695_BIT_BST_AUTO_BST_RAM_MASK, AW8695_BIT_BST_AUTO_BST_RTP_ENABLE); + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_BST_MODE_MASK & AW8695_BIT_SYSCTRL_WORK_MODE_MASK, + AW8695_BIT_SYSCTRL_BST_MODE_BOOST | AW8695_BIT_SYSCTRL_STANDBY); + aw8695_haptic_active(aw8695); + } else { + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_BST_MODE_MASK, AW8695_BIT_SYSCTRL_BST_MODE_BOOST); + } + msleep(3); + break; + case AW8695_HAPTIC_TRIG_MODE: + aw8695->play_mode = AW8695_HAPTIC_TRIG_MODE; + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_PLAY_MODE_MASK, AW8695_BIT_SYSCTRL_PLAY_MODE_RAM); + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_BST_MODE_MASK, AW8695_BIT_SYSCTRL_BST_MODE_BYPASS); + if (aw8695->auto_boost) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_BST_AUTO, + AW8695_BIT_BST_AUTO_BST_RAM_MASK, AW8695_BIT_BST_AUTO_BST_RAM_DISABLE); + } + aw8695_haptic_active(aw8695); + if (aw8695->auto_boost) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_BST_AUTO, + AW8695_BIT_BST_AUTO_BST_RAM_MASK, AW8695_BIT_BST_AUTO_BST_RAM_ENABLE); + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_BST_MODE_MASK & AW8695_BIT_SYSCTRL_WORK_MODE_MASK, + AW8695_BIT_SYSCTRL_BST_MODE_BOOST | AW8695_BIT_SYSCTRL_STANDBY); + aw8695_haptic_active(aw8695); + } else { + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_BST_MODE_MASK, AW8695_BIT_SYSCTRL_BST_MODE_BOOST); + } + msleep(3); + break; + case AW8695_HAPTIC_CONT_MODE: + aw8695->play_mode = AW8695_HAPTIC_CONT_MODE; + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_PLAY_MODE_MASK, AW8695_BIT_SYSCTRL_PLAY_MODE_CONT); + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_BST_MODE_MASK, AW8695_BIT_SYSCTRL_BST_MODE_BYPASS); + if (aw8695->auto_boost) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_BST_AUTO, + AW8695_BIT_BST_AUTO_BST_RAM_MASK, AW8695_BIT_BST_AUTO_BST_RAM_DISABLE); + } + aw8695_haptic_active(aw8695); + break; + default: + dev_err(aw8695->dev, "%s: play mode %d err", + __func__, play_mode); + break; + } + return 0; +} + +static int aw8695_haptic_play_go(struct aw8695 *aw8695, bool flag) +{ + pr_debug("%s enter\n", __func__); + + if (!flag) { + GET_TIME_OF_DAY(&aw8695->current_time); + aw8695->interval_us = (aw8695->current_time.tv_sec - aw8695->pre_enter_time.tv_sec) * 1000000 +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + + (aw8695->current_time.tv_nsec - aw8695->pre_enter_time.tv_nsec) / 1000; +#else + + (aw8695->current_time.tv_usec - aw8695->pre_enter_time.tv_usec); +#endif + + if (aw8695->interval_us < 2000) { + pr_info("aw8695->interval_us t=%u\n", aw8695->interval_us); + mdelay(2); + } + } + if(flag == true) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_GO, + AW8695_BIT_GO_MASK, AW8695_BIT_GO_ENABLE); + GET_TIME_OF_DAY(&aw8695->pre_enter_time); + } else { + aw8695_i2c_write_bits(aw8695, AW8695_REG_GO, + AW8695_BIT_GO_MASK, AW8695_BIT_GO_DISABLE); + } + return 0; +} + +static int aw8695_haptic_stop_delay(struct aw8695 *aw8695) +{ + unsigned char reg_val = 0; + unsigned int cnt = 100; + + while (cnt--) { + aw8695_i2c_read(aw8695, AW8695_REG_GLB_STATE, ®_val); + if ((reg_val & 0x0f) == 0x00) { + return 0; + } + msleep(2); + pr_debug("%s wait for standby, reg glb_state=0x%02x\n", + __func__, reg_val); + } + pr_err("%s do not enter standby automatically\n", __func__); + + return 0; +} + +static int aw8695_haptic_stop(struct aw8695 *aw8695) +{ + pr_debug("%s enter\n", __func__); + + aw8695_haptic_play_go(aw8695, false); + aw8695_haptic_stop_delay(aw8695); + aw8695_haptic_play_mode(aw8695, AW8695_HAPTIC_STANDBY_MODE); + +#ifdef CONFIG_AF_NOISE_ELIMINATION + if ((aw8695->haptic_mode == HAPTIC_LONG) || (is_af_enabled == true)) { + pr_info("%s: %d: mot_actuator_on_vibrate_stop, duration=%d, haptic_mode=%d, play_mode=%hhu \n", __func__,__LINE__ + ,aw8695->duration,aw8695->haptic_mode,aw8695->play_mode); + mot_actuator_on_vibrate_stop(); + is_af_enabled = false; + } +#endif + + return 0; +} + +static int aw8695_haptic_start(struct aw8695 *aw8695) +{ + pr_debug("%s enter\n", __func__); + + aw8695_haptic_play_go(aw8695, true); + + return 0; +} + +static int aw8695_haptic_set_wav_seq(struct aw8695 *aw8695, + unsigned char wav, unsigned char seq) +{ + aw8695_i2c_write(aw8695, AW8695_REG_WAVSEQ1 + wav, + seq); + return 0; +} + +static int aw8695_haptic_set_wav_loop(struct aw8695 *aw8695, + unsigned char wav, unsigned char loop) +{ + unsigned char tmp = 0; + + if (wav % 2) { + tmp = loop << 0; + aw8695_i2c_write_bits(aw8695, AW8695_REG_WAVLOOP1 + (wav / 2), + AW8695_BIT_WAVLOOP_SEQNP1_MASK, tmp); + } else { + tmp = loop << 4; + aw8695_i2c_write_bits(aw8695, AW8695_REG_WAVLOOP1 + (wav / 2), + AW8695_BIT_WAVLOOP_SEQN_MASK, tmp); + } + + return 0; +} +/* +static int aw8695_haptic_set_main_loop(struct aw8695 *aw8695, + unsigned char loop) +{ + aw8695_i2c_write(aw8695, AW8695_REG_MAIN_LOOP, loop); + return 0; +} +*/ + +static int aw8695_haptic_set_repeat_wav_seq(struct aw8695 *aw8695, unsigned char seq) +{ + aw8695_haptic_set_wav_seq(aw8695, 0x00, seq); + aw8695_haptic_set_wav_loop(aw8695, 0x00, AW8695_BIT_WAVLOOP_INIFINITELY); + + return 0; +} + + +static int aw8695_haptic_set_bst_vol(struct aw8695 *aw8695, unsigned char bst_vol) +{ + if (bst_vol & 0xe0) { + bst_vol = 0x1f; + } + aw8695_i2c_write_bits(aw8695, AW8695_REG_BSTDBG4, + AW8695_BIT_BSTDBG4_BSTVOL_MASK, (bst_vol << 1)); + return 0; +} + +static int aw8695_haptic_set_bst_peak_cur(struct aw8695 *aw8695, unsigned char peak_cur) +{ + peak_cur &= AW8695_BSTCFG_PEAKCUR_LIMIT; + aw8695_i2c_write_bits(aw8695, AW8695_REG_BSTCFG, + AW8695_BIT_BSTCFG_PEAKCUR_MASK, peak_cur); + return 0; +} + +static int aw8695_haptic_set_gain(struct aw8695 *aw8695, unsigned char gain) +{ + aw8695_i2c_write(aw8695, AW8695_REG_DATDBG, gain); + return 0; +} + +static int aw8695_haptic_set_pwm(struct aw8695 *aw8695, unsigned char mode) +{ + switch (mode) { + case AW8695_PWM_48K: + aw8695_i2c_write_bits(aw8695, AW8695_REG_PWMDBG, + AW8695_BIT_PWMDBG_PWM_MODE_MASK, AW8695_BIT_PWMDBG_PWM_48K); + break; + case AW8695_PWM_24K: + aw8695_i2c_write_bits(aw8695, AW8695_REG_PWMDBG, + AW8695_BIT_PWMDBG_PWM_MODE_MASK, AW8695_BIT_PWMDBG_PWM_24K); + break; + case AW8695_PWM_12K: + aw8695_i2c_write_bits(aw8695, AW8695_REG_PWMDBG, + AW8695_BIT_PWMDBG_PWM_MODE_MASK, AW8695_BIT_PWMDBG_PWM_12K); + break; + default: + break; + } + return 0; +} + +static int aw8695_haptic_play_wav_seq(struct aw8695 *aw8695, unsigned char flag) +{ + pr_debug("%s enter\n", __func__); + + if (flag) { + aw8695_haptic_play_mode(aw8695, AW8695_HAPTIC_RAM_MODE); + aw8695_haptic_start(aw8695); + } + return 0; +} + +static int aw8695_haptic_play_repeat_seq(struct aw8695 *aw8695, unsigned char flag) +{ + pr_debug("%s enter\n", __func__); + + if (flag) { + aw8695_haptic_play_mode(aw8695, AW8695_HAPTIC_RAM_LOOP_MODE); + aw8695_haptic_start(aw8695); + } + + return 0; +} + +/***************************************************** + * + * motor protect + * + *****************************************************/ +static int aw8695_haptic_swicth_motorprotect_config(struct aw8695 *aw8695, unsigned char addr, unsigned char val) +{ + pr_debug("%s enter\n", __func__); + if (addr == 1) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_DETCTRL, + AW8695_BIT_DETCTRL_PROTECT_MASK, AW8695_BIT_DETCTRL_PROTECT_SHUTDOWN); + aw8695_i2c_write_bits(aw8695, AW8695_REG_PWMPRC, + AW8695_BIT_PWMPRC_PRC_MASK, AW8695_BIT_PWMPRC_PRC_ENABLE); + aw8695_i2c_write_bits(aw8695, AW8695_REG_PRLVL, + AW8695_BIT_PRLVL_PR_MASK, AW8695_BIT_PRLVL_PR_ENABLE); + } else if (addr == 0) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_DETCTRL, + AW8695_BIT_DETCTRL_PROTECT_MASK, AW8695_BIT_DETCTRL_PROTECT_NO_ACTION); + aw8695_i2c_write_bits(aw8695, AW8695_REG_PWMPRC, + AW8695_BIT_PWMPRC_PRC_MASK, AW8695_BIT_PWMPRC_PRC_DISABLE); + aw8695_i2c_write_bits(aw8695, AW8695_REG_PRLVL, + AW8695_BIT_PRLVL_PR_MASK, AW8695_BIT_PRLVL_PR_DISABLE); + } else if (addr == 0x2d) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_PWMPRC, + AW8695_BIT_PWMPRC_PRCTIME_MASK, val); + } else if (addr == 0x3e) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_PRLVL, + AW8695_BIT_PRLVL_PRLVL_MASK, val); + } else if (addr == 0x3f) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_PRTIME, + AW8695_BIT_PRTIME_PRTIME_MASK, val); + } else { + /*nothing to do;*/ + } + return 0; +} + +/***************************************************** + * + * os calibration + * + *****************************************************/ +static int aw8695_haptic_os_calibration(struct aw8695 *aw8695) +{ + unsigned int cont = 2000; + unsigned char reg_val = 0; + pr_debug("%s enter\n", __func__); + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_RAMINIT_MASK, AW8695_BIT_SYSCTRL_RAMINIT_EN); + aw8695_i2c_write_bits(aw8695, AW8695_REG_DETCTRL, + AW8695_BIT_DETCTRL_DIAG_GO_MASK, AW8695_BIT_DETCTRL_DIAG_GO_ENABLE); + while (1) { + aw8695_i2c_read(aw8695, AW8695_REG_DETCTRL, ®_val); + if ((reg_val & 0x01) == 0 || cont == 0) + break; + cont--; + } + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_RAMINIT_MASK, AW8695_BIT_SYSCTRL_RAMINIT_OFF); + return 0; +} +/***************************************************** + * + * trig config + * + *****************************************************/ +static int aw8695_haptic_trig_param_init(struct aw8695 *aw8695) +{ + pr_debug("%s enter\n", __func__); + + aw8695->trig[0].enable = AW8695_TRG1_DISABLE; + aw8695->trig[0].default_level = AW8695_TRG1_DEFAULT_LEVEL; + aw8695->trig[0].dual_edge = AW8695_TRG1_DUAL_EDGE; + aw8695->trig[0].frist_seq = AW8695_TRG1_FIRST_EDGE_SEQ; + aw8695->trig[0].second_seq = AW8695_TRG1_SECOND_EDGE_SEQ; + + aw8695->trig[1].enable = AW8695_TRG2_DISABLE; + aw8695->trig[1].default_level = AW8695_TRG2_DEFAULT_LEVEL; + aw8695->trig[1].dual_edge = AW8695_TRG2_DUAL_EDGE; + aw8695->trig[1].frist_seq = AW8695_TRG2_FIRST_EDGE_SEQ; + aw8695->trig[1].second_seq = AW8695_TRG2_SECOND_EDGE_SEQ; + + aw8695->trig[2].enable = AW8695_TRG3_DISABLE; + aw8695->trig[2].default_level = AW8695_TRG3_DEFAULT_LEVEL; + aw8695->trig[2].dual_edge = AW8695_TRG3_DUAL_EDGE; + aw8695->trig[2].frist_seq = AW8695_TRG3_FIRST_EDGE_SEQ; + aw8695->trig[2].second_seq = AW8695_TRG3_SECOND_EDGE_SEQ; + + return 0; +} + +static int aw8695_haptic_trig_param_config(struct aw8695 *aw8695) +{ + pr_debug("%s enter\n", __func__); + + if (aw8695->trig[0].default_level) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_TRG_CFG1, + AW8695_BIT_TRGCFG1_TRG1_POLAR_MASK, AW8695_BIT_TRGCFG1_TRG1_POLAR_NEG); + } else { + aw8695_i2c_write_bits(aw8695, AW8695_REG_TRG_CFG1, + AW8695_BIT_TRGCFG1_TRG1_POLAR_MASK, AW8695_BIT_TRGCFG1_TRG1_POLAR_POS); + } + if (aw8695->trig[1].default_level) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_TRG_CFG1, + AW8695_BIT_TRGCFG1_TRG2_POLAR_MASK, AW8695_BIT_TRGCFG1_TRG2_POLAR_NEG); + } else { + aw8695_i2c_write_bits(aw8695, AW8695_REG_TRG_CFG1, + AW8695_BIT_TRGCFG1_TRG2_POLAR_MASK, AW8695_BIT_TRGCFG1_TRG2_POLAR_POS); + } + if (aw8695->trig[2].default_level) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_TRG_CFG1, + AW8695_BIT_TRGCFG1_TRG3_POLAR_MASK, AW8695_BIT_TRGCFG1_TRG3_POLAR_NEG); + } else { + aw8695_i2c_write_bits(aw8695, AW8695_REG_TRG_CFG1, + AW8695_BIT_TRGCFG1_TRG3_POLAR_MASK, AW8695_BIT_TRGCFG1_TRG3_POLAR_POS); + } + + if (aw8695->trig[0].dual_edge) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_TRG_CFG1, + AW8695_BIT_TRGCFG1_TRG1_EDGE_MASK, AW8695_BIT_TRGCFG1_TRG1_EDGE_POS_NEG); + } else { + aw8695_i2c_write_bits(aw8695, AW8695_REG_TRG_CFG1, + AW8695_BIT_TRGCFG1_TRG1_EDGE_MASK, AW8695_BIT_TRGCFG1_TRG1_EDGE_POS); + } + if (aw8695->trig[1].dual_edge) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_TRG_CFG1, + AW8695_BIT_TRGCFG1_TRG2_EDGE_MASK, AW8695_BIT_TRGCFG1_TRG2_EDGE_POS_NEG); + } else { + aw8695_i2c_write_bits(aw8695, AW8695_REG_TRG_CFG1, + AW8695_BIT_TRGCFG1_TRG2_EDGE_MASK, AW8695_BIT_TRGCFG1_TRG2_EDGE_POS); + } + if (aw8695->trig[2].dual_edge) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_TRG_CFG1, + AW8695_BIT_TRGCFG1_TRG3_EDGE_MASK, AW8695_BIT_TRGCFG1_TRG3_EDGE_POS_NEG); + } else { + aw8695_i2c_write_bits(aw8695, AW8695_REG_TRG_CFG1, + AW8695_BIT_TRGCFG1_TRG3_EDGE_MASK, AW8695_BIT_TRGCFG1_TRG3_EDGE_POS); + } + + if (aw8695->trig[0].frist_seq) { + aw8695_i2c_write(aw8695, AW8695_REG_TRG1_WAV_P, aw8695->trig[0].frist_seq); + } + if (aw8695->trig[0].second_seq && aw8695->trig[0].dual_edge) { + aw8695_i2c_write(aw8695, AW8695_REG_TRG1_WAV_N, aw8695->trig[0].second_seq); + } + if (aw8695->trig[1].frist_seq) { + aw8695_i2c_write(aw8695, AW8695_REG_TRG2_WAV_P, aw8695->trig[1].frist_seq); + } + if (aw8695->trig[1].second_seq && aw8695->trig[1].dual_edge) { + aw8695_i2c_write(aw8695, AW8695_REG_TRG2_WAV_N, aw8695->trig[1].second_seq); + } + if (aw8695->trig[2].frist_seq) { + aw8695_i2c_write(aw8695, AW8695_REG_TRG3_WAV_P, aw8695->trig[1].frist_seq); + } + if (aw8695->trig[2].second_seq && aw8695->trig[2].dual_edge) { + aw8695_i2c_write(aw8695, AW8695_REG_TRG3_WAV_N, aw8695->trig[1].second_seq); + } + + return 0; +} + +static int aw8695_haptic_trig_enable_config(struct aw8695 *aw8695) +{ + pr_debug("%s enter\n", __func__); + + aw8695_i2c_write_bits(aw8695, AW8695_REG_TRG_CFG2, + AW8695_BIT_TRGCFG2_TRG1_ENABLE_MASK, aw8695->trig[0].enable); + aw8695_i2c_write_bits(aw8695, AW8695_REG_TRG_CFG2, + AW8695_BIT_TRGCFG2_TRG2_ENABLE_MASK, aw8695->trig[1].enable); + aw8695_i2c_write_bits(aw8695, AW8695_REG_TRG_CFG2, + AW8695_BIT_TRGCFG2_TRG3_ENABLE_MASK, aw8695->trig[2].enable); + + return 0; +} + + +static int aw8695_haptic_auto_boost_config(struct aw8695 *aw8695, unsigned char flag) +{ + aw8695->auto_boost = flag; + if (flag) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_BST_AUTO, + AW8695_BIT_BST_AUTO_BST_AUTOSW_MASK, AW8695_BIT_BST_AUTO_BST_AUTOMATIC_BOOST); + } else { + aw8695_i2c_write_bits(aw8695, AW8695_REG_BST_AUTO, + AW8695_BIT_BST_AUTO_BST_AUTOSW_MASK, AW8695_BIT_BST_AUTO_BST_MANUAL_BOOST); + } + return 0; +} + +/***************************************************** + * + * vbat mode + * + *****************************************************/ +static int aw8695_haptic_cont_vbat_mode(struct aw8695 *aw8695, unsigned char flag) +{ + if (flag == AW8695_HAPTIC_CONT_VBAT_HW_COMP_MODE) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_ADCTEST, + AW8695_BIT_ADCTEST_VBAT_MODE_MASK, AW8695_BIT_ADCTEST_VBAT_HW_COMP); + } else { + aw8695_i2c_write_bits(aw8695, AW8695_REG_ADCTEST, + AW8695_BIT_ADCTEST_VBAT_MODE_MASK, AW8695_BIT_ADCTEST_VBAT_SW_COMP); + } + return 0; +} + +static int aw8695_haptic_get_vbat(struct aw8695 *aw8695) +{ + unsigned char reg_val = 0; + unsigned int cont = 2000; + + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_RAMINIT_MASK, AW8695_BIT_SYSCTRL_RAMINIT_EN); + aw8695_i2c_write_bits(aw8695, AW8695_REG_DETCTRL, + AW8695_BIT_DETCTRL_VBAT_GO_MASK, AW8695_BIT_DETCTRL_VABT_GO_ENABLE); + + while (1) { + aw8695_i2c_read(aw8695, AW8695_REG_DETCTRL, ®_val); + if ((reg_val & 0x02) == 0 || cont == 0) + break; + cont--; + } + + aw8695_i2c_read(aw8695, AW8695_REG_VBATDET, ®_val); + aw8695->vbat = 6100 * reg_val / 256; + if (aw8695->vbat > AW8695_VBAT_MAX) { + aw8695->vbat = AW8695_VBAT_MAX; + pr_debug("%s vbat max limit = %dmV\n", __func__, aw8695->vbat); + } + if (aw8695->vbat < AW8695_VBAT_MIN) { + aw8695->vbat = AW8695_VBAT_MIN; + pr_debug("%s vbat min limit = %dmV\n", __func__, aw8695->vbat); + } + + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_RAMINIT_MASK, AW8695_BIT_SYSCTRL_RAMINIT_OFF); + + return 0; +} + +static int aw8695_haptic_ram_vbat_comp(struct aw8695 *aw8695, bool flag) +{ + int temp_gain = 0; + int aw8695_gain = 0; + + if (aw8695->debugfs_debug) + aw8695_gain = aw8695->gain_debug; + else + aw8695_gain = aw8695->gain; + if (flag) { + if (aw8695->ram_vbat_comp == AW8695_HAPTIC_RAM_VBAT_COMP_ENABLE) { + aw8695_haptic_get_vbat(aw8695); + temp_gain = aw8695_gain * AW8695_VBAT_REFER / aw8695->vbat; + if (temp_gain > (128 * AW8695_VBAT_REFER / AW8695_VBAT_MIN)) { + temp_gain = 128 * AW8695_VBAT_REFER / AW8695_VBAT_MIN; + pr_debug("%s gain limit=%d\n", __func__, temp_gain); + } + aw8695_haptic_set_gain(aw8695, temp_gain); + } else { + aw8695_haptic_set_gain(aw8695, aw8695_gain); + } + } else { + aw8695_haptic_set_gain(aw8695, aw8695_gain); + } + + return 0; +} + +/***************************************************** + * + * f0 + * + *****************************************************/ +static int aw8695_haptic_set_f0_preset(struct aw8695 *aw8695) +{ + unsigned int f0_reg = 0; + + pr_debug("%s enter\n", __func__); + + f0_reg = 1000000000 / (aw8695->f0_pre * AW8695_HAPTIC_F0_COEFF); + aw8695_i2c_write(aw8695, AW8695_REG_F_PRE_H, (unsigned char)((f0_reg >> 8) & 0xff)); + aw8695_i2c_write(aw8695, AW8695_REG_F_PRE_L, (unsigned char)((f0_reg >> 0) & 0xff)); + + return 0; +} + +static int aw8695_haptic_read_f0(struct aw8695 *aw8695) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned int f0_reg = 0; + unsigned long f0_tmp = 0; + + pr_debug("%s enter\n", __func__); + + ret = aw8695_i2c_read(aw8695, AW8695_REG_F_LRA_F0_H, ®_val); + f0_reg = (reg_val << 8); + ret = aw8695_i2c_read(aw8695, AW8695_REG_F_LRA_F0_L, ®_val); + f0_reg |= (reg_val << 0); + f0_tmp = 1000000000 / (f0_reg * AW8695_HAPTIC_F0_COEFF); + aw8695->f0 = (unsigned int)f0_tmp; + pr_info("%s f0=%d\n", __func__, aw8695->f0); + + return 0; +} + +static int aw8695_haptic_read_cont_f0(struct aw8695 *aw8695) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned int f0_reg = 0; + unsigned long f0_tmp = 0; + + pr_debug("%s enter\n", __func__); + + ret = aw8695_i2c_read(aw8695, AW8695_REG_F_LRA_CONT_H, ®_val); + f0_reg = (reg_val << 8); + ret = aw8695_i2c_read(aw8695, AW8695_REG_F_LRA_CONT_L, ®_val); + f0_reg |= (reg_val << 0); + f0_tmp = 1000000000 / (f0_reg * AW8695_HAPTIC_F0_COEFF); + aw8695->cont_f0 = (unsigned int)f0_tmp; + pr_info("%s f0=%d\n", __func__, aw8695->cont_f0); + + return 0; +} + +static int aw8695_haptic_read_beme(struct aw8695 *aw8695) +{ + int ret = 0; + unsigned char reg_val = 0; + + ret = aw8695_i2c_read(aw8695, AW8695_REG_WAIT_VOL_MP, ®_val); + aw8695->max_pos_beme = (reg_val << 0); + ret = aw8695_i2c_read(aw8695, AW8695_REG_WAIT_VOL_MN, ®_val); + aw8695->max_neg_beme = (reg_val << 0); + + pr_info("%s max_pos_beme=%d\n", __func__, aw8695->max_pos_beme); + pr_info("%s max_neg_beme=%d\n", __func__, aw8695->max_neg_beme); + + return 0; +} + + + +/***************************************************** + * + * rtp + * + *****************************************************/ +static void aw8695_haptic_set_rtp_aei(struct aw8695 *aw8695, bool flag) +{ + if (flag) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSINTM, + AW8695_BIT_SYSINTM_FF_AE_MASK, AW8695_BIT_SYSINTM_FF_AE_EN); + } else { + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSINTM, + AW8695_BIT_SYSINTM_FF_AE_MASK, AW8695_BIT_SYSINTM_FF_AE_OFF); + } +} +/* +static void aw8695_haptic_set_rtp_afi(struct aw8695 *aw8695, bool flag) +{ + if(flag) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSINTM, + AW8695_BIT_SYSINTM_FF_AF_MASK, AW8695_BIT_SYSINTM_FF_AF_EN); + } else { + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSINTM, + AW8695_BIT_SYSINTM_FF_AF_MASK, AW8695_BIT_SYSINTM_FF_AF_OFF); + } +} +*/ +/* +static unsigned char aw8695_haptic_rtp_get_fifo_aei(struct aw8695 *aw8695) +{ + unsigned char ret; + unsigned char reg_val; + + aw8695_i2c_read(aw8695, AW8695_REG_SYSINT, ®_val); + reg_val &= AW8695_BIT_SYSINT_FF_AEI; + ret = reg_val>>4; + + return ret; +} +*/ + +static unsigned char aw8695_haptic_rtp_get_fifo_afi(struct aw8695 *aw8695) +{ + unsigned char ret = 0; + unsigned char reg_val = 0; + + aw8695_i2c_read(aw8695, AW8695_REG_SYSINT, ®_val); + reg_val &= AW8695_BIT_SYSINT_FF_AFI; + ret = reg_val >> 3; + + return ret; +} + +/***************************************************** + * + * rtp + * + *****************************************************/ +static int aw8695_haptic_rtp_init(struct aw8695 *aw8695) +{ + unsigned int buf_len = 0; + unsigned char reg_val = 0; + + pr_info("%s enter\n", __func__); + pm_qos_add_request(&pm_qos_req_vb, PM_QOS_CPU_DMA_LATENCY, PM_QOS_VALUE_VB); + + aw8695->rtp_cnt = 0; + + while ((!aw8695_haptic_rtp_get_fifo_afi(aw8695)) && + (aw8695->play_mode == AW8695_HAPTIC_RTP_MODE)) { + pr_info("%s rtp cnt = %d\n", __func__, aw8695->rtp_cnt); + if (!aw8695_rtp) { + pr_info("%s:aw8695_rtp is null break\n", __func__); + break; + } + + if ((aw8695->rtp_cnt < aw8695->ram.base_addr)) { + if((aw8695_rtp->len-aw8695->rtp_cnt) < (aw8695->ram.base_addr)){ + buf_len = aw8695_rtp->len-aw8695->rtp_cnt; + } else { + buf_len = (aw8695->ram.base_addr); + } + } else if ((aw8695_rtp->len - aw8695->rtp_cnt) < (aw8695->ram.base_addr >> 2)) { + buf_len = aw8695_rtp->len - aw8695->rtp_cnt; + } else { + buf_len = (aw8695->ram.base_addr >> 2); + } + aw8695_i2c_writes(aw8695, AW8695_REG_RTP_DATA, + &aw8695_rtp->data[aw8695->rtp_cnt], buf_len); + aw8695->rtp_cnt += buf_len; + + aw8695_i2c_read(aw8695, AW8695_REG_GLB_STATE, ®_val); + if ((aw8695->rtp_cnt == aw8695_rtp->len) || ((reg_val & 0x0f) == 0x00)) { + pr_info("%s: rtp update complete\n", __func__); + aw8695->rtp_cnt = 0; + pm_qos_remove_request(&pm_qos_req_vb); + return 0; + } + } + + if (aw8695->play_mode == AW8695_HAPTIC_RTP_MODE) { + aw8695_haptic_set_rtp_aei(aw8695, true); + } + pm_qos_remove_request(&pm_qos_req_vb); + pr_info("%s exit\n", __func__); + + return 0; +} + +static void aw8695_rtp_work_routine(struct work_struct *work) +{ + const struct firmware *rtp_file; + int ret = -1; + struct aw8695 *aw8695 = container_of(work, struct aw8695, rtp_work); + + pr_info("%s enter\n", __func__); + + /* fw loaded */ + ret = request_firmware(&rtp_file, + aw8695_rtp_name[aw8695->rtp_file_num], + aw8695->dev); + if (ret < 0) { + pr_err("%s: failed to read %s\n", __func__, + aw8695_rtp_name[aw8695->rtp_file_num]); + return ; + } + aw8695->rtp_init = 0; + vfree(aw8695_rtp); + aw8695_rtp = vzalloc(rtp_file->size + sizeof(int)); + if (!aw8695_rtp) { + release_firmware(rtp_file); + pr_err("%s: error allocating memory\n", __func__); + return; + } + aw8695_rtp->len = rtp_file->size; + pr_info("%s: rtp file [%s] size = %d\n", __func__, + aw8695_rtp_name[aw8695->rtp_file_num], aw8695_rtp->len); + memcpy(aw8695_rtp->data, rtp_file->data, rtp_file->size); + release_firmware(rtp_file); + + mutex_lock(&aw8695->lock); + + aw8695->rtp_init = 1; + + /* gain */ + aw8695_haptic_ram_vbat_comp(aw8695, false); + + /* rtp mode config */ + aw8695_haptic_play_mode(aw8695, AW8695_HAPTIC_RTP_MODE); + + /* haptic start */ + aw8695_haptic_start(aw8695); + + mutex_unlock(&aw8695->lock); + + aw8695_haptic_rtp_init(aw8695); +} + + +/***************************************************** + * + * haptic - audio + * + *****************************************************/ +static enum hrtimer_restart aw8695_haptic_audio_timer_func(struct hrtimer *timer) +{ + struct aw8695 *aw8695 = container_of(timer, struct aw8695, haptic_audio.timer); + + pr_debug("%s enter\n", __func__); + schedule_work(&aw8695->haptic_audio.work); + + hrtimer_start(&aw8695->haptic_audio.timer, + ktime_set(aw8695->haptic_audio.timer_val / 1000000, + (aw8695->haptic_audio.timer_val % 1000000) * 1000), + HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +static void aw8695_haptic_audio_work_routine(struct work_struct *work) +{ + struct aw8695 *aw8695 = container_of(work, struct aw8695, haptic_audio.work); + + pr_debug("%s enter\n", __func__); + + mutex_lock(&aw8695->haptic_audio.lock); + memcpy(&aw8695->haptic_audio.ctr, + &aw8695->haptic_audio.data[aw8695->haptic_audio.cnt], + sizeof(struct haptic_ctr)); + pr_debug("%s: cnt=%d, cmd=%d, play=%d, wavseq=%d, loop=%d, gain=%d\n", + __func__, + aw8695->haptic_audio.cnt, + aw8695->haptic_audio.ctr.cmd, + aw8695->haptic_audio.ctr.play, + aw8695->haptic_audio.ctr.wavseq, + aw8695->haptic_audio.ctr.loop, + aw8695->haptic_audio.ctr.gain); + mutex_unlock(&aw8695->haptic_audio.lock); + if (aw8695->haptic_audio.ctr.cmd == 0x01) { + mutex_lock(&aw8695->lock); + aw8695_haptic_stop(aw8695); + mutex_unlock(&aw8695->lock); + if (aw8695->haptic_audio.ctr.play == 0x01) { + pr_info("%s: haptic_audio_play_start\n", __func__); + mutex_lock(&aw8695->lock); + aw8695_haptic_play_mode(aw8695, AW8695_HAPTIC_RAM_MODE); + + aw8695_haptic_set_wav_seq(aw8695, 0x00, + aw8695->haptic_audio.ctr.wavseq); + aw8695_haptic_set_wav_seq(aw8695, 0x01, 0x00); + + aw8695_haptic_set_wav_loop(aw8695, 0x00, + aw8695->haptic_audio.ctr.loop); + + aw8695_haptic_set_gain(aw8695, + aw8695->haptic_audio.ctr.gain); + + aw8695_haptic_start(aw8695); + mutex_unlock(&aw8695->lock); + } + mutex_lock(&aw8695->haptic_audio.lock); + memset(&aw8695->haptic_audio.data[aw8695->haptic_audio.cnt], + 0, sizeof(struct haptic_ctr)); + mutex_unlock(&aw8695->haptic_audio.lock); + } + + mutex_lock(&aw8695->haptic_audio.lock); + aw8695->haptic_audio.cnt ++; + if (aw8695->haptic_audio.data[aw8695->haptic_audio.cnt].cmd == 0) { + aw8695->haptic_audio.cnt = 0; + pr_debug("%s: haptic play buffer restart\n", __func__); + } + mutex_unlock(&aw8695->haptic_audio.lock); + +} + + +/***************************************************** + * + * haptic cont + * + *****************************************************/ +static int aw8695_haptic_cont(struct aw8695 *aw8695) +{ + pr_info("%s enter\n", __func__); + + /* work mode */ + aw8695_haptic_play_mode(aw8695, AW8695_HAPTIC_CONT_MODE); + + /* preset f0 */ + aw8695->f0_pre = aw8695->f0; + aw8695_haptic_set_f0_preset(aw8695); + + /* lpf */ + aw8695_i2c_write_bits(aw8695, AW8695_REG_DATCTRL, + AW8695_BIT_DATCTRL_FC_MASK, AW8695_BIT_DATCTRL_FC_1000HZ); + aw8695_i2c_write_bits(aw8695, AW8695_REG_DATCTRL, + AW8695_BIT_DATCTRL_LPF_ENABLE_MASK, AW8695_BIT_DATCTRL_LPF_ENABLE); + + /* cont config */ + aw8695_i2c_write_bits(aw8695, AW8695_REG_CONT_CTRL, + AW8695_BIT_CONT_CTRL_ZC_DETEC_MASK, AW8695_BIT_CONT_CTRL_ZC_DETEC_ENABLE); + aw8695_i2c_write_bits(aw8695, AW8695_REG_CONT_CTRL, + AW8695_BIT_CONT_CTRL_WAIT_PERIOD_MASK, AW8695_BIT_CONT_CTRL_WAIT_1PERIOD); + aw8695_i2c_write_bits(aw8695, AW8695_REG_CONT_CTRL, + AW8695_BIT_CONT_CTRL_MODE_MASK, AW8695_BIT_CONT_CTRL_BY_GO_SIGNAL); + aw8695_i2c_write_bits(aw8695, AW8695_REG_CONT_CTRL, + AW8695_BIT_CONT_CTRL_EN_CLOSE_MASK, AW8695_CONT_PLAYBACK_MODE); + aw8695_i2c_write_bits(aw8695, AW8695_REG_CONT_CTRL, + AW8695_BIT_CONT_CTRL_F0_DETECT_MASK, AW8695_BIT_CONT_CTRL_F0_DETECT_DISABLE); + aw8695_i2c_write_bits(aw8695, AW8695_REG_CONT_CTRL, + AW8695_BIT_CONT_CTRL_O2C_MASK, AW8695_BIT_CONT_CTRL_O2C_DISABLE); + aw8695_i2c_write_bits(aw8695, AW8695_REG_CONT_CTRL, + AW8695_BIT_CONT_CTRL_AUTO_BRK_MASK, AW8695_BIT_CONT_CTRL_AUTO_BRK_ENABLE); + + /* TD time */ + aw8695_i2c_write(aw8695, AW8695_REG_TD_H, (unsigned char)(aw8695->cont_td >> 8)); + aw8695_i2c_write(aw8695, AW8695_REG_TD_L, (unsigned char)(aw8695->cont_td >> 0)); + aw8695_i2c_write(aw8695, AW8695_REG_TSET, 0x12); + + /* zero cross */ + aw8695_i2c_write(aw8695, AW8695_REG_ZC_THRSH_H, (unsigned char)(aw8695->cont_zc_thr >> 8)); + aw8695_i2c_write(aw8695, AW8695_REG_ZC_THRSH_L, (unsigned char)(aw8695->cont_zc_thr >> 0)); + + /* bemf */ + aw8695_i2c_write(aw8695, AW8695_REG_BEMF_VTHH_H, 0x10); + aw8695_i2c_write(aw8695, AW8695_REG_BEMF_VTHH_L, 0x08); + aw8695_i2c_write(aw8695, AW8695_REG_BEMF_VTHL_H, 0x03); + aw8695_i2c_write(aw8695, AW8695_REG_BEMF_VTHL_L, 0xf8); + aw8695_i2c_write_bits(aw8695, AW8695_REG_BEMF_NUM, + AW8695_BIT_BEMF_NUM_BRK_MASK, aw8695->cont_num_brk); + aw8695_i2c_write(aw8695, AW8695_REG_TIME_NZC, 0x23); /* 35*171us=5.985ms */ + + /* f0 driver level */ + aw8695_i2c_write(aw8695, AW8695_REG_DRV_LVL, aw8695->cont_drv_lvl); + aw8695_i2c_write(aw8695, AW8695_REG_DRV_LVL_OV, aw8695->cont_drv_lvl_ov); + + /* cont play go */ + aw8695_haptic_play_go(aw8695, true); + + return 0; +} + +/***************************************************** + * + * haptic f0 cali + * + *****************************************************/ +static int aw8695_haptic_get_f0(struct aw8695 *aw8695) +{ + int ret = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + unsigned char f0_pre_num = 0; + unsigned char f0_wait_num = 0; + unsigned char f0_repeat_num = 0; + unsigned char f0_trace_num = 0; + unsigned int t_f0_ms = 0; + unsigned int t_f0_trace_ms = 0; + unsigned int f0_cali_cnt = 50; + + pr_info("%s enter\n", __func__); + + aw8695->f0 = aw8695->f0_pre; + + /* f0 calibrate work mode */ + aw8695_haptic_stop(aw8695); + aw8695_haptic_play_mode(aw8695, AW8695_HAPTIC_CONT_MODE); + + aw8695_i2c_write_bits(aw8695, AW8695_REG_CONT_CTRL, + AW8695_BIT_CONT_CTRL_EN_CLOSE_MASK, AW8695_BIT_CONT_CTRL_OPEN_PLAYBACK); + aw8695_i2c_write_bits(aw8695, AW8695_REG_CONT_CTRL, + AW8695_BIT_CONT_CTRL_F0_DETECT_MASK, AW8695_BIT_CONT_CTRL_F0_DETECT_ENABLE); + + /* LPF */ + aw8695_i2c_write_bits(aw8695, AW8695_REG_DATCTRL, + AW8695_BIT_DATCTRL_FC_MASK, AW8695_BIT_DATCTRL_FC_1000HZ); + aw8695_i2c_write_bits(aw8695, AW8695_REG_DATCTRL, + AW8695_BIT_DATCTRL_LPF_ENABLE_MASK, AW8695_BIT_DATCTRL_LPF_ENABLE); + + /* LRA OSC Source */ + if (aw8695->f0_cali_flag == AW8695_HAPTIC_CALI_F0) { + aw8695_i2c_write_bits(aw8695, AW8695_REG_ANACTRL, + AW8695_BIT_ANACTRL_LRA_SRC_MASK, AW8695_BIT_ANACTRL_LRA_SRC_REG); + } else { + aw8695_i2c_write_bits(aw8695, AW8695_REG_ANACTRL, + AW8695_BIT_ANACTRL_LRA_SRC_MASK, AW8695_BIT_ANACTRL_LRA_SRC_EFUSE); + } + + /* preset f0 */ + aw8695_haptic_set_f0_preset(aw8695); + + /* beme config */ + aw8695_i2c_write(aw8695, AW8695_REG_BEMF_VTHH_H, 0x10); + aw8695_i2c_write(aw8695, AW8695_REG_BEMF_VTHH_L, 0x08); + aw8695_i2c_write(aw8695, AW8695_REG_BEMF_VTHL_H, 0x03); + aw8695_i2c_write(aw8695, AW8695_REG_BEMF_VTHL_L, 0xf8); + + /* f0 driver level */ + aw8695_i2c_write(aw8695, AW8695_REG_DRV_LVL, aw8695->cont_drv_lvl); + + /* f0 trace parameter */ + f0_pre_num = 0x05; + f0_wait_num = 0x02; + f0_repeat_num = 0x0d; + f0_trace_num = 0x0f; + aw8695_i2c_write(aw8695, AW8695_REG_NUM_F0_1, (f0_pre_num << 4) | (f0_wait_num << 0)); + aw8695_i2c_write(aw8695, AW8695_REG_NUM_F0_2, (f0_repeat_num << 0)); + aw8695_i2c_write(aw8695, AW8695_REG_NUM_F0_3, (f0_trace_num << 0)); + + /* clear aw8695 interrupt */ + ret = aw8695_i2c_read(aw8695, AW8695_REG_SYSINT, ®_val); + +#ifdef CONFIG_AF_NOISE_ELIMINATION + pr_info("%s: %d: mot_actuator_on_vibrate_start, duration=%d, haptic_mode=%d, play_mode=%hhu \n", __func__,__LINE__ + ,aw8695->duration,aw8695->haptic_mode,aw8695->play_mode); + mot_actuator_on_vibrate_start(); +#endif + + /* play go and start f0 calibration */ + aw8695_haptic_play_go(aw8695, true); + + /* f0 trace time */ + t_f0_ms = 1000 * 10 / aw8695->f0_pre; + t_f0_trace_ms = t_f0_ms * (f0_pre_num + f0_wait_num + (f0_trace_num + f0_wait_num) * (f0_repeat_num - 1)); + msleep(t_f0_trace_ms); + + for (i = 0; i < f0_cali_cnt; i++) { + ret = aw8695_i2c_read(aw8695, AW8695_REG_SYSINT, ®_val); + /* f0 calibrate done */ + if (reg_val & 0x01) { + aw8695_haptic_read_f0(aw8695); + aw8695_haptic_read_beme(aw8695); + break; + } + msleep(10); + pr_info("%s f0 cali sleep 10ms\n", __func__); + } + + if (i == f0_cali_cnt) { + ret = -1; + } else { + ret = 0; + } + + /* restore default config */ + aw8695_i2c_write_bits(aw8695, AW8695_REG_CONT_CTRL, + AW8695_BIT_CONT_CTRL_EN_CLOSE_MASK, AW8695_CONT_PLAYBACK_MODE); + aw8695_i2c_write_bits(aw8695, AW8695_REG_CONT_CTRL, + AW8695_BIT_CONT_CTRL_F0_DETECT_MASK, AW8695_BIT_CONT_CTRL_F0_DETECT_DISABLE); + + return ret; +} + +static int aw8695_haptic_f0_calibration(struct aw8695 *aw8695) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned int f0_limit = 0; + char f0_cali_lra = 0; + int f0_cali_step = 0; + int f0_dft_step = 0; + + pr_info("%s enter\n", __func__); + + aw8695->f0_cali_flag = AW8695_HAPTIC_CALI_F0; + + if (aw8695_haptic_get_f0(aw8695)) { + pr_err("%s get f0 error, user defafult f0\n", __func__); + } else { + /* max and min limit */ + f0_limit = aw8695->f0; + if (aw8695->f0 * 100 < aw8695->f0_pre * (100 - aw8695->f0_cali_percen)) { + f0_limit = aw8695->f0_pre * (100 - aw8695->f0_cali_percen) / 100; + } + if (aw8695->f0 * 100 > aw8695->f0_pre * (100 + aw8695->f0_cali_percen)) { + f0_limit = aw8695->f0_pre * (100 + aw8695->f0_cali_percen) / 100; + } + + /* calculate cali step */ + f0_cali_step = 10000 * ((int)f0_limit - (int)aw8695->f0_pre) / ((int)f0_limit * 25); + pr_debug("%s f0_cali_step=%d\n", __func__, f0_cali_step); + + /* get default cali step */ + aw8695_i2c_read(aw8695, AW8695_REG_TRIM_LRA, ®_val); + if (reg_val & 0x20) { + f0_dft_step = reg_val - 0x40; + } else { + f0_dft_step = reg_val; + } + pr_debug("%s f0_dft_step=%d\n", __func__, f0_dft_step); + + /* get new cali step */ + f0_cali_step += f0_dft_step; + pr_debug("%s f0_cali_step=%d\n", __func__, f0_cali_step); + + if (f0_cali_step > 31) { + f0_cali_step = 31; + } else if (f0_cali_step < -32) { + f0_cali_step = -32; + } + f0_cali_lra = (char)f0_cali_step; + pr_debug("%s f0_cali_lra=%d\n", __func__, f0_cali_lra); + + /* get cali step complement code*/ + if (f0_cali_lra < 0) { + f0_cali_lra += 0x40; + } + pr_debug("%s reg f0_cali_lra=%d\n", __func__, f0_cali_lra); + + /* update cali step */ + aw8695_i2c_write(aw8695, AW8695_REG_TRIM_LRA, (char)f0_cali_lra); + aw8695_i2c_read(aw8695, AW8695_REG_TRIM_LRA, ®_val); + pr_info("%s final trim_lra=0x%02x\n", __func__, reg_val); + } + + /* restore default work mode */ + aw8695_haptic_play_mode(aw8695, AW8695_HAPTIC_STANDBY_MODE); + aw8695->play_mode = AW8695_HAPTIC_RAM_MODE; + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_PLAY_MODE_MASK, AW8695_BIT_SYSCTRL_PLAY_MODE_RAM); + aw8695_haptic_stop(aw8695); + +#ifdef CONFIG_AF_NOISE_ELIMINATION + pr_info("%s: %d: mot_actuator_on_vibrate_stop, duration=%d, haptic_mode=%d, play_mode=%hhu \n", __func__,__LINE__ + ,aw8695->duration,aw8695->haptic_mode,aw8695->play_mode); + mot_actuator_on_vibrate_stop(); +#endif + + return ret; +} + +/***************************************************** + * + * haptic fops + * + *****************************************************/ +static int aw8695_file_open(struct inode *inode, struct file *file) +{ + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + file->private_data = (void *)g_aw8695; + + return 0; +} + +static int aw8695_file_release(struct inode *inode, struct file *file) +{ + file->private_data = (void *)NULL; + + module_put(THIS_MODULE); + + return 0; +} + +static long aw8695_file_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct aw8695 *aw8695 = (struct aw8695 *)file->private_data; + + int ret = 0; + + dev_info(aw8695->dev, "%s: cmd=0x%x, arg=0x%lx\n", + __func__, cmd, arg); + + mutex_lock(&aw8695->lock); + + if (_IOC_TYPE(cmd) != AW8695_HAPTIC_IOCTL_MAGIC) { + dev_err(aw8695->dev, "%s: cmd magic err\n", + __func__); + return -EINVAL; + } + + switch (cmd) { + default: + dev_err(aw8695->dev, "%s, unknown cmd\n", __func__); + break; + } + + mutex_unlock(&aw8695->lock); + + return ret; +} + +static ssize_t aw8695_file_read(struct file *filp, char *buff, size_t len, loff_t *offset) +{ + struct aw8695 *aw8695 = (struct aw8695 *)filp->private_data; + int ret = 0; + int i = 0; + unsigned char reg_val = 0; + unsigned char *pbuff = NULL; + + mutex_lock(&aw8695->lock); + + dev_info(aw8695->dev, "%s: len=%zu\n", __func__, len); + + switch (aw8695->fileops.cmd) { + case AW8695_HAPTIC_CMD_READ_REG: + pbuff = (unsigned char *)kzalloc(len, GFP_KERNEL); + if (pbuff != NULL) { + for (i = 0; i < len; i++) { + aw8695_i2c_read(aw8695, aw8695->fileops.reg + i, ®_val); + pbuff[i] = reg_val; + } + for (i = 0; i < len; i++) { + dev_info(aw8695->dev, "%s: pbuff[%d]=0x%02x\n", + __func__, i, pbuff[i]); + } + ret = copy_to_user(buff, pbuff, len); + if (ret) { + dev_err(aw8695->dev, "%s: copy to user fail\n", __func__); + } + kfree(pbuff); + } else { + dev_err(aw8695->dev, "%s: alloc memory fail\n", __func__); + } + break; + default: + dev_err(aw8695->dev, "%s, unknown cmd %d \n", __func__, aw8695->fileops.cmd); + break; + } + + mutex_unlock(&aw8695->lock); + + + return len; +} + +static ssize_t aw8695_file_write(struct file *filp, const char *buff, size_t len, loff_t *off) +{ + struct aw8695 *aw8695 = (struct aw8695 *)filp->private_data; + int i = 0; + int ret = 0; + unsigned char *pbuff = NULL; + + pbuff = (unsigned char *)kzalloc(len, GFP_KERNEL); + if (pbuff == NULL) { + dev_err(aw8695->dev, "%s: alloc memory fail\n", __func__); + return len; + } + ret = copy_from_user(pbuff, buff, len); + if (ret) { + dev_err(aw8695->dev, "%s: copy from user fail\n", __func__); + return len; + } + + for (i = 0; i < len; i++) { + dev_info(aw8695->dev, "%s: pbuff[%d]=0x%02x\n", + __func__, i, pbuff[i]); + } + + mutex_lock(&aw8695->lock); + + aw8695->fileops.cmd = pbuff[0]; + + switch (aw8695->fileops.cmd) { + case AW8695_HAPTIC_CMD_READ_REG: + if (len == 2) { + aw8695->fileops.reg = pbuff[1]; + } else { + dev_err(aw8695->dev, "%s: read cmd len %zu err\n", __func__, len); + } + break; + case AW8695_HAPTIC_CMD_WRITE_REG: + if (len > 2) { + for (i = 0; i < len - 2; i++) { + dev_info(aw8695->dev, "%s: write reg0x%02x=0x%02x\n", + __func__, pbuff[1] + i, pbuff[i + 2]); + aw8695_i2c_write(aw8695, pbuff[1] + i, pbuff[2 + i]); + } + } else { + dev_err(aw8695->dev, "%s: write cmd len %zu err\n", __func__, len); + } + break; + default: + dev_err(aw8695->dev, "%s, unknown cmd %d \n", __func__, aw8695->fileops.cmd); + break; + } + + mutex_unlock(&aw8695->lock); + + if (pbuff != NULL) { + kfree(pbuff); + } + return len; +} + +static struct file_operations fops = { + .owner = THIS_MODULE, + .read = aw8695_file_read, + .write = aw8695_file_write, + .unlocked_ioctl = aw8695_file_unlocked_ioctl, + .open = aw8695_file_open, + .release = aw8695_file_release, +}; + +static struct miscdevice aw8695_haptic_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = AW8695_HAPTIC_NAME, + .fops = &fops, +}; + +static int aw8695_haptic_init(struct aw8695 *aw8695) +{ + int ret = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + pr_info("%s enter\n", __func__); + + ret = misc_register(&aw8695_haptic_misc); + if (ret) { + dev_err(aw8695->dev, "%s: misc fail: %d\n", __func__, ret); + return ret; + } + + /* haptic audio */ + aw8695->haptic_audio.delay_val = 20833; + aw8695->haptic_audio.timer_val = 20833; + + hrtimer_init(&aw8695->haptic_audio.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + aw8695->haptic_audio.timer.function = aw8695_haptic_audio_timer_func; + INIT_WORK(&aw8695->haptic_audio.work, aw8695_haptic_audio_work_routine); + + mutex_init(&aw8695->haptic_audio.lock); + + + /* haptic init */ + mutex_lock(&aw8695->lock); + + aw8695->activate_mode = AW8695_HAPTIC_ACTIVATE_RAM_MODE; + + ret = aw8695_i2c_read(aw8695, AW8695_REG_WAVSEQ1, ®_val); + aw8695->index = reg_val & 0x7F; + ret = aw8695_i2c_read(aw8695, AW8695_REG_DATDBG, ®_val); + aw8695->gain = reg_val & 0xFF; + ret = aw8695_i2c_read(aw8695, AW8695_REG_BSTDBG4, ®_val); + aw8695->vmax = (reg_val >> 1) & 0x1F; + for (i = 0; i < AW8695_SEQUENCER_SIZE; i++) { + ret = aw8695_i2c_read(aw8695, AW8695_REG_WAVSEQ1 + i, ®_val); + aw8695->seq[i] = reg_val; + } + + aw8695_haptic_play_mode(aw8695, AW8695_HAPTIC_STANDBY_MODE); + + aw8695_haptic_set_pwm(aw8695, AW8695_PWM_24K); + + aw8695_i2c_write(aw8695, AW8695_REG_BSTDBG1, 0x30); + aw8695_i2c_write(aw8695, AW8695_REG_BSTDBG2, 0xeb); + aw8695_i2c_write(aw8695, AW8695_REG_BSTDBG3, 0xd4); + aw8695_i2c_write(aw8695, AW8695_REG_TSET, 0x12); + aw8695_i2c_write(aw8695, AW8695_REG_R_SPARE, 0x68); + + aw8695_i2c_write_bits(aw8695, AW8695_REG_ANADBG, + AW8695_BIT_ANADBG_IOC_MASK, AW8695_BIT_ANADBG_IOC_4P65A); + + aw8695_haptic_set_bst_peak_cur(aw8695, AW8695_DEFAULT_PEAKCUR); + + aw8695_haptic_swicth_motorprotect_config(aw8695, 0x00, 0x00); + + aw8695_haptic_auto_boost_config(aw8695, false); + + aw8695_haptic_trig_param_init(aw8695); + aw8695_haptic_trig_param_config(aw8695); + + aw8695_haptic_os_calibration(aw8695); + + aw8695_haptic_cont_vbat_mode(aw8695, + AW8695_HAPTIC_CONT_VBAT_HW_COMP_MODE); + aw8695->ram_vbat_comp = AW8695_HAPTIC_RAM_VBAT_COMP_ENABLE; + + mutex_unlock(&aw8695->lock); + + + /* f0 calibration */ + mutex_lock(&aw8695->lock); + aw8695_haptic_f0_calibration(aw8695); + mutex_unlock(&aw8695->lock); + + return ret; +} + + + +/***************************************************** + * + * vibrator + * + *****************************************************/ + +static void aw8695_rtp_play(struct aw8695 *aw8695, int value) +{ + aw8695_haptic_stop(aw8695); + aw8695_haptic_set_rtp_aei(aw8695, false); + aw8695_interrupt_clear(aw8695); + if (value < (sizeof(aw8695_rtp_name) / AW8695_RTP_NAME_MAX)) { + aw8695->rtp_file_num = value; + if (value) { + schedule_work(&aw8695->rtp_work); + } + } else { + pr_err("%s: rtp_file_num 0x%02x over max value \n", __func__, aw8695->rtp_file_num); + } +} + +static void aw8695_haptic_context(struct aw8695 *aw8695, enum aw8695_haptic_mode cmd) +{ + int t_top = 0; + if (!gpio_is_valid(aw8695->haptic_context_gpio)) { + pr_debug("%s haptic context gpio is invalid \n", __func__); + return; + } + + t_top = gpio_get_value(aw8695->haptic_context_gpio); + if (t_top) { + switch (cmd) { + case HAPTIC_RTP: + aw8695->gain = 0x80; + break; + case HAPTIC_SHORT: + aw8695->gain = 0x80; + break; + case HAPTIC_LONG: + aw8695->gain = aw8695->long_gain_reduced; + break; + default: + break; + } + } +} + +static void aw8695_vibrate(struct aw8695 *aw8695, int value) +{ + int seq = 0; + mutex_lock(&aw8695->lock); + +#ifdef AW8695_REPEAT_RTP_PLAYING + if (aw8695->haptic_mode == HAPTIC_RTP_LOOP) { + aw8695->haptic_mode = HAPTIC_RTP; + hrtimer_cancel(&aw8695->timer); + } +#endif + aw8695_haptic_stop(aw8695); + if (aw8695->index == 0x02) + PM_RELAX(aw8695->ws); + + seq = aw8695->seq[0]; + pr_info("%s: value=%d, seq=%d, index=%x\n", __FUNCTION__, value, seq, aw8695->index); + + if (value > 0 || seq > 2) { + + if (seq >= AW8695_SEQ_NO_RTP_BASE) { + aw8695->haptic_mode = HAPTIC_RTP; + aw8695->gain = 0x80; +#ifdef AW8695_REPEAT_RTP_PLAYING + if (seq >= AW8695_SEQ_NO_RTP_BASE + AW8695_SEQ_NO_RTP_REPEAT) { + aw8695->haptic_mode = HAPTIC_RTP_LOOP; + seq -= AW8695_SEQ_NO_RTP_REPEAT; + + value = AW8695_SEQ_NO_RTP_STOP; + PM_WAKEUP_EVENT(aw8695->ws, value + 100); + /* run ms timer */ + hrtimer_cancel(&aw8695->timer); + hrtimer_start(&aw8695->timer, + ktime_set(value / 1000, (value % 1000) * 1000000), + HRTIMER_MODE_REL); + } +#endif + } else if (value < 100 || seq > 2) { + aw8695->haptic_mode = HAPTIC_SHORT; + aw8695->gain = 0x80; + } else { + aw8695->haptic_mode = HAPTIC_LONG; + aw8695->gain = aw8695->long_gain_normal; + } + if(!aw8695->factory_mode) + aw8695_haptic_context(aw8695,aw8695->haptic_mode); + if (aw8695->debugfs_debug) + aw8695_haptic_set_gain(aw8695, aw8695->gain_debug); + else + aw8695_haptic_set_gain(aw8695, aw8695->gain); + + switch (aw8695->haptic_mode) { + + case HAPTIC_RTP: + case HAPTIC_RTP_LOOP: + aw8695_rtp_play(aw8695, seq - AW8695_SEQ_NO_RTP_BASE); + break; + case HAPTIC_SHORT: + if (aw8695->seq[0] == 0) + aw8695->seq[0] = 0x01; + aw8695->index = 0x01; +#ifdef CONFIG_AW8965_VIBRATOR_SHORT_WAV_ENABLE + /*If duration < 100ms, use four waveforms corresponding to weakest, weak, medium, strong*/ + if(value < 12){ + aw8695_haptic_set_bst_vol(aw8695, 0x2); + aw8695_haptic_set_wav_seq(aw8695, 0x00, 4); + } + else if(value < 25) { + aw8695_haptic_set_bst_vol(aw8695, 0x1e); + aw8695_haptic_set_wav_seq(aw8695, 0x00, 4); + } + else if(value < 38) { + aw8695_haptic_set_bst_vol(aw8695, 0x1); + aw8695_haptic_set_wav_seq(aw8695, 0x00, 3); + } + else if(value < 100) { + aw8695_haptic_set_bst_vol(aw8695, 0x1f); + aw8695_haptic_set_wav_seq(aw8695, 0x00, 1); + } +#else + aw8695_haptic_set_wav_seq(aw8695, 0x00, aw8695->seq[0]); +#endif + aw8695_haptic_set_wav_loop(aw8695, 0x00, 0x00); + aw8695_haptic_ram_vbat_comp(aw8695, false); + aw8695_haptic_play_wav_seq(aw8695, 0x01); + break; + case HAPTIC_LONG: +#ifdef CONFIG_AF_NOISE_ELIMINATION + pr_info("%s: %d: mot_actuator_on_vibrate_start, duration=%d, haptic_mode=%d, play_mode=%hhu \n", __func__,__LINE__ + ,aw8695->duration,aw8695->haptic_mode,aw8695->play_mode); + is_af_enabled = true; + mot_actuator_on_vibrate_start(); +#endif + aw8695->duration = value; + /* wav index config */ + aw8695->index = 0x02; + aw8695_haptic_set_repeat_wav_seq(aw8695, aw8695->index); + PM_WAKEUP_EVENT(aw8695->ws, value + 100); + /* run ms timer */ + hrtimer_cancel(&aw8695->timer); + aw8695->state = 0x01; + if (aw8695->state) + { + hrtimer_start(&aw8695->timer, + ktime_set(aw8695->duration / 1000, (value % 1000) * 1000000), + HRTIMER_MODE_REL); + } + schedule_work(&aw8695->vibrator_work); + break; + default: + break; + } + + /* Restore to default short waveform */ + if (seq > 2) + aw8695->seq[0] = 0; + } + + mutex_unlock(&aw8695->lock); +} +#ifdef TIMED_OUTPUT +static int aw8695_vibrator_get_time(struct timed_output_dev *dev) +{ + struct aw8695 *aw8695 = container_of(dev, struct aw8695, to_dev); + + if (hrtimer_active(&aw8695->timer)) { + ktime_t r = hrtimer_get_remaining(&aw8695->timer); + return ktime_to_ms(r); + } + + return 0; +} + +static void aw8695_vibrator_enable(struct timed_output_dev *dev, int value) +{ + struct aw8695 *aw8695 = container_of(dev, struct aw8695, to_dev); + + mutex_lock(&aw8695->lock); + + pr_debug("%s enter\n", __func__); + + aw8695_haptic_stop(aw8695); + + if (value > 0) { + aw8695_haptic_ram_vbat_comp(aw8695, false); + aw8695_haptic_play_wav_seq(aw8695, value); + } + + mutex_unlock(&aw8695->lock); + + pr_debug("%s exit\n", __func__); +} + +#else +static enum led_brightness aw8695_haptic_brightness_get(struct led_classdev *cdev) +{ + struct aw8695 *aw8695 = + container_of(cdev, struct aw8695, cdev); + + return aw8695->amplitude; +} + +static void aw8695_haptic_brightness_set(struct led_classdev *cdev, + enum led_brightness level) +{ + struct aw8695 *aw8695 = + container_of(cdev, struct aw8695, cdev); + + aw8695->amplitude = level; + + mutex_lock(&aw8695->lock); + + aw8695_haptic_stop(aw8695); + if (aw8695->amplitude > 0) { + aw8695_haptic_ram_vbat_comp(aw8695, false); + aw8695_haptic_play_wav_seq(aw8695, aw8695->amplitude); + } + + mutex_unlock(&aw8695->lock); + +} +#endif + +static ssize_t aw8695_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + + return snprintf(buf, PAGE_SIZE, "%d\n", aw8695->state); +} + +static ssize_t aw8695_state_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw8695_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + ktime_t time_rem; + s64 time_ms = 0; + + if (hrtimer_active(&aw8695->timer)) { + time_rem = hrtimer_get_remaining(&aw8695->timer); + time_ms = ktime_to_ms(time_rem); + } + + return snprintf(buf, PAGE_SIZE, "%lld\n", time_ms); +} + +static ssize_t aw8695_duration_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + /* setting 0 on duration is NOP for now */ + if (val <= 0) + return count; + + aw8695->duration = val; + + return count; +} + +static ssize_t aw8695_activate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + + /* For now nothing to show */ + return snprintf(buf, PAGE_SIZE, "%d\n", aw8695->state); +} + +static ssize_t aw8695_activate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + + unsigned int val = 0; + int rc = 0;; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + if (val != 0 && val != 1) + return count; + + pr_info("%s: value=%d\n", __FUNCTION__, val); + aw8695->state = val; + if (aw8695->state) + aw8695_vibrate(aw8695, aw8695->duration); + else + aw8695_vibrate(aw8695, 0); + + return count; +} + +static ssize_t aw8695_activate_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + + return snprintf(buf, PAGE_SIZE, "activate_mode=%d\n", aw8695->activate_mode); +} + +static ssize_t aw8695_activate_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw8695->lock); + aw8695->activate_mode = val; + mutex_unlock(&aw8695->lock); + return count; +} + +static ssize_t aw8695_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned char reg_val = 0; + aw8695_i2c_read(aw8695, AW8695_REG_WAVSEQ1, ®_val); + aw8695->index = reg_val; + + return snprintf(buf, PAGE_SIZE, "%d\n", aw8695->index); +} + +static ssize_t aw8695_index_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + pr_debug("%s: value=%d\n", __FUNCTION__, val); + + mutex_lock(&aw8695->lock); + aw8695->index = val; + aw8695_haptic_set_repeat_wav_seq(aw8695, aw8695->index); + mutex_unlock(&aw8695->lock); + return count; +} + +static ssize_t aw8695_vmax_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + + return snprintf(buf, PAGE_SIZE, "0x%02x\n", aw8695->vmax); +} + +static ssize_t aw8695_vmax_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + pr_debug("%s: value=%d\n", __FUNCTION__, val); + + mutex_lock(&aw8695->lock); + aw8695->vmax = val; + aw8695_haptic_set_bst_vol(aw8695, aw8695->vmax); + mutex_unlock(&aw8695->lock); + return count; +} + +static ssize_t aw8695_gain_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + return snprintf(buf, PAGE_SIZE, "0x%02x\n", aw8695->gain_debug); +} + +static ssize_t aw8695_gain_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + pr_debug("%s: value=%d\n", __FUNCTION__, val); + + mutex_lock(&aw8695->lock); + if (val > 0) + aw8695->debugfs_debug = true; + else + aw8695->debugfs_debug = false; + aw8695->gain_debug = val; + aw8695_haptic_set_gain(aw8695, aw8695->gain_debug); + mutex_unlock(&aw8695->lock); + return count; +} + +static ssize_t aw8695_seq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + size_t count = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + for (i = 0; i < AW8695_SEQUENCER_SIZE; i++) { + aw8695_i2c_read(aw8695, AW8695_REG_WAVSEQ1 + i, ®_val); + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d: 0x%02x\n", i + 1, reg_val); + aw8695->seq[i] |= reg_val; + } + return count; +} + +static ssize_t aw8695_seq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + + unsigned int i = 0; + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + pr_info("%s: value=%x\n", __FUNCTION__, val); + + mutex_lock(&aw8695->lock); + for(i=0; i<4; i++) { + aw8695->seq[i] = (val>>((AW8695_WAV_SEQ_SIZE-i-1)*8)) & 0xFF; + aw8695_haptic_set_wav_seq(aw8695, i, aw8695->seq[i]); + } + mutex_unlock(&aw8695->lock); + + return count; +} + +static ssize_t aw8695_loop_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + size_t count = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + for (i = 0; i < AW8695_SEQUENCER_LOOP_SIZE; i++) { + aw8695_i2c_read(aw8695, AW8695_REG_WAVLOOP1 + i, ®_val); + aw8695->loop[i * 2 + 0] = (reg_val >> 4) & 0x0F; + aw8695->loop[i * 2 + 1] = (reg_val >> 0) & 0x0F; + + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d loop: 0x%02x\n", i * 2 + 1, aw8695->loop[i * 2 + 0]); + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d loop: 0x%02x\n", i * 2 + 2, aw8695->loop[i * 2 + 1]); + } + return count; +} + +static ssize_t aw8695_loop_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int databuf[2] = {0, 0}; + + if (2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) { + pr_debug("%s: seq%d loop=0x%x\n", __FUNCTION__, databuf[0], databuf[1]); + mutex_lock(&aw8695->lock); + aw8695->loop[databuf[0]] = (unsigned char)databuf[1]; + aw8695_haptic_set_wav_loop(aw8695, (unsigned char)databuf[0], + aw8695->loop[databuf[0]]); + mutex_unlock(&aw8695->lock); + } + + return count; +} + +static ssize_t aw8695_reg_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + ssize_t len = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + for (i = 0; i < AW8695_REG_MAX; i ++) { + if (!(aw8695_reg_access[i]®_RD_ACCESS)) + continue; + aw8695_i2c_read(aw8695, i, ®_val); + len += snprintf(buf + len, PAGE_SIZE - len, "reg:0x%02x=0x%02x \n", i, reg_val); + } + return len; +} + +static ssize_t aw8695_reg_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int databuf[2] = {0, 0}; + + if (2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) { + aw8695_i2c_write(aw8695, (unsigned char)databuf[0], (unsigned char)databuf[1]); + } + + return count; +} + +static ssize_t aw8695_rtp_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + ssize_t len = 0; + len += snprintf(buf + len, PAGE_SIZE - len, "rtp play: %d\n", aw8695->rtp_cnt); + + return len; +} + +static ssize_t aw8695_rtp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + aw8695_rtp_play(aw8695, val); + + return count; +} + +static ssize_t aw8695_ram_update_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + + ssize_t len = 0; + len += snprintf(buf + len, PAGE_SIZE - len, "sram update mode\n"); + return len; +} + +static ssize_t aw8695_ram_update_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + if (val) { + aw8695_ram_update(aw8695); + } + return count; +} + +static ssize_t aw8695_f0_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + ssize_t len = 0; + + mutex_lock(&aw8695->lock); + aw8695->f0_cali_flag = AW8695_HAPTIC_LRA_F0; + aw8695_haptic_get_f0(aw8695); + mutex_unlock(&aw8695->lock); + len += snprintf(buf + len, PAGE_SIZE - len, "aw8695 lra f0 = %d\n", aw8695->f0); + return len; +} + +static ssize_t aw8695_f0_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + return count; +} + +static ssize_t aw8695_cali_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + ssize_t len = 0; + mutex_lock(&aw8695->lock); + aw8695->f0_cali_flag = AW8695_HAPTIC_CALI_F0; + aw8695_haptic_get_f0(aw8695); + mutex_unlock(&aw8695->lock); + len += snprintf(buf + len, PAGE_SIZE - len, "aw8695 cali f0 = %d\n", aw8695->f0); + return len; +} + +static ssize_t aw8695_cali_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + if (val) { + mutex_lock(&aw8695->lock); + aw8695_haptic_f0_calibration(aw8695); + mutex_unlock(&aw8695->lock); + } + return count; +} + +static ssize_t aw8695_cont_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + ssize_t len = 0; + aw8695_haptic_read_cont_f0(aw8695); + len += snprintf(buf + len, PAGE_SIZE - len, "aw8695 cont f0 = %d\n", aw8695->cont_f0); + return len; +} + +static ssize_t aw8695_cont_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + aw8695_haptic_stop(aw8695); + if (val) { + aw8695_haptic_cont(aw8695); + } + return count; +} + + +static ssize_t aw8695_cont_td_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + ssize_t len = 0; + len += snprintf(buf + len, PAGE_SIZE - len, "aw8695 cont delay time = 0x%04x\n", aw8695->cont_td); + return len; +} + +static ssize_t aw8695_cont_td_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int databuf[1] = {0}; + if (1 == sscanf(buf, "%x", &databuf[0])) { + aw8695->cont_td = databuf[0]; + aw8695_i2c_write(aw8695, AW8695_REG_TD_H, (unsigned char)(databuf[0] >> 8)); + aw8695_i2c_write(aw8695, AW8695_REG_TD_L, (unsigned char)(databuf[0] >> 0)); + } + return count; +} + +static ssize_t aw8695_cont_drv_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + ssize_t len = 0; + len += snprintf(buf + len, PAGE_SIZE - len, "aw8695 cont drv level = %d\n", aw8695->cont_drv_lvl); + len += snprintf(buf + len, PAGE_SIZE - len, "aw8695 cont drv level overdrive= %d\n", aw8695->cont_drv_lvl_ov); + return len; +} + +static ssize_t aw8695_cont_drv_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int databuf[2] = {0, 0}; + if (2 == sscanf(buf, "%d %d", &databuf[0], &databuf[1])) { + aw8695->cont_drv_lvl = databuf[0]; + aw8695_i2c_write(aw8695, AW8695_REG_DRV_LVL, aw8695->cont_drv_lvl); + aw8695->cont_drv_lvl_ov = databuf[1]; + aw8695_i2c_write(aw8695, AW8695_REG_DRV_LVL_OV, aw8695->cont_drv_lvl_ov); + } + return count; +} + +static ssize_t aw8695_cont_num_brk_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + ssize_t len = 0; + len += snprintf(buf + len, PAGE_SIZE - len, "aw8695 cont break num = %d\n", aw8695->cont_num_brk); + return len; +} + +static ssize_t aw8695_cont_num_brk_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int databuf[1] = {0}; + if (1 == sscanf(buf, "%d", &databuf[0])) { + aw8695->cont_num_brk = databuf[0]; + if (aw8695->cont_num_brk > 7) { + aw8695->cont_num_brk = 7; + } + aw8695_i2c_write_bits(aw8695, AW8695_REG_BEMF_NUM, + AW8695_BIT_BEMF_NUM_BRK_MASK, aw8695->cont_num_brk); + } + return count; +} + +static ssize_t aw8695_cont_zc_thr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + ssize_t len = 0; + len += snprintf(buf + len, PAGE_SIZE - len, "aw8695 cont zero cross thr = 0x%04x\n", aw8695->cont_zc_thr); + return len; +} + +static ssize_t aw8695_cont_zc_thr_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int databuf[1] = {0}; + if (1 == sscanf(buf, "%x", &databuf[0])) { + aw8695->cont_zc_thr = databuf[0]; + aw8695_i2c_write(aw8695, AW8695_REG_ZC_THRSH_H, (unsigned char)(databuf[0] >> 8)); + aw8695_i2c_write(aw8695, AW8695_REG_ZC_THRSH_L, (unsigned char)(databuf[0] >> 0)); + } + return count; +} + +static ssize_t aw8695_vbat_monitor_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + ssize_t len = 0; + + mutex_lock(&aw8695->lock); + aw8695_haptic_stop(aw8695); + aw8695_haptic_get_vbat(aw8695); + len += snprintf(buf + len, PAGE_SIZE - len, "vbat=%dmV\n", aw8695->vbat); + mutex_unlock(&aw8695->lock); + + return len; +} + +static ssize_t aw8695_vbat_monitor_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw8695_lra_resistance_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + ssize_t len = 0; + unsigned char reg_val = 0; + + mutex_lock(&aw8695->lock); + aw8695_haptic_stop(aw8695); + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_RAMINIT_MASK, AW8695_BIT_SYSCTRL_RAMINIT_EN); + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_BST_MODE_MASK, AW8695_BIT_SYSCTRL_BST_MODE_BYPASS); + + + aw8695_i2c_write_bits(aw8695, AW8695_REG_ANACTRL, + AW8695_BIT_ANACTRL_HD_PD_MASK, AW8695_BIT_ANACTRL_HD_HZ_EN); + aw8695_i2c_write_bits(aw8695, AW8695_REG_D2SCFG, + AW8695_BIT_D2SCFG_CLK_ADC_MASK, AW8695_BIT_D2SCFG_CLK_ASC_1P5MHZ); + + aw8695_i2c_write_bits(aw8695, AW8695_REG_DETCTRL, + AW8695_BIT_DETCTRL_RL_OS_MASK, AW8695_BIT_DETCTRL_RL_DETECT); + aw8695_i2c_write_bits(aw8695, AW8695_REG_DETCTRL, + AW8695_BIT_DETCTRL_DIAG_GO_MASK, AW8695_BIT_DETCTRL_DIAG_GO_ENABLE); + msleep(3); + aw8695_i2c_read(aw8695, AW8695_REG_RLDET, ®_val); + aw8695->lra = 298 * reg_val; + len += snprintf(buf + len, PAGE_SIZE - len, "r_lra=%dmohm\n", aw8695->lra); + + aw8695_i2c_write_bits(aw8695, AW8695_REG_ANACTRL, + AW8695_BIT_ANACTRL_HD_PD_MASK, AW8695_BIT_ANACTRL_HD_PD_EN); + aw8695_i2c_write_bits(aw8695, AW8695_REG_D2SCFG, + AW8695_BIT_D2SCFG_CLK_ADC_MASK, AW8695_BIT_D2SCFG_CLK_ASC_6MHZ); + + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_RAMINIT_MASK, AW8695_BIT_SYSCTRL_RAMINIT_OFF); + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_BST_MODE_MASK, AW8695_BIT_SYSCTRL_BST_MODE_BOOST); + mutex_unlock(&aw8695->lock); + + return len; +} + +static ssize_t aw8695_lra_resistance_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw8695_auto_boost_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "auto_boost=%d\n", aw8695->auto_boost); + + return len; +} + + +static ssize_t aw8695_auto_boost_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw8695->lock); + aw8695_haptic_stop(aw8695); + aw8695_haptic_auto_boost_config(aw8695, val); + mutex_unlock(&aw8695->lock); + + return count; +} + +static ssize_t aw8695_prctmode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + ssize_t len = 0; + unsigned char reg_val = 0; + + aw8695_i2c_read(aw8695, AW8695_REG_RLDET, ®_val); + + len += snprintf(buf + len, PAGE_SIZE - len, "prctmode=%d\n", reg_val & 0x20); + return len; +} + + +static ssize_t aw8695_prctmode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int databuf[2] = {0, 0}; + unsigned int addr = 0; + unsigned int val = 0; + if (2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) { + addr = databuf[0]; + val = databuf[1]; + mutex_lock(&aw8695->lock); + aw8695_haptic_swicth_motorprotect_config(aw8695, addr, val); + mutex_unlock(&aw8695->lock); + } + return count; +} + +static ssize_t aw8695_trig_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + ssize_t len = 0; + unsigned char i = 0; + for (i = 0; i < AW8695_TRIG_NUM; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "trig%d: enable=%d, default_level=%d, dual_edge=%d, frist_seq=%d, second_seq=%d\n", + i + 1, aw8695->trig[i].enable, aw8695->trig[i].default_level, aw8695->trig[i].dual_edge, + aw8695->trig[i].frist_seq, aw8695->trig[i].second_seq); + } + + return len; +} + +static ssize_t aw8695_trig_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int databuf[6] = {0}; + if (sscanf(buf, "%d %d %d %d %d %d", + &databuf[0], &databuf[1], &databuf[2], &databuf[3], &databuf[4], &databuf[5])) { + pr_debug("%s: %d, %d, %d, %d, %d, %d\n", __func__, + databuf[0], databuf[1], databuf[2], databuf[3], databuf[4], databuf[5]); + if (databuf[0] > 3) { + databuf[0] = 3; + } + if (databuf[0] > 0) { + databuf[0] -= 1; + } + aw8695->trig[databuf[0]].enable = databuf[1]; + aw8695->trig[databuf[0]].default_level = databuf[2]; + aw8695->trig[databuf[0]].dual_edge = databuf[3]; + aw8695->trig[databuf[0]].frist_seq = databuf[4]; + aw8695->trig[databuf[0]].second_seq = databuf[5]; + mutex_lock(&aw8695->lock); + aw8695_haptic_trig_param_config(aw8695); + aw8695_haptic_trig_enable_config(aw8695); + mutex_unlock(&aw8695->lock); + } + return count; +} + +static ssize_t aw8695_ram_vbat_comp_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "ram_vbat_comp=%d\n", aw8695->ram_vbat_comp); + + return len; +} + + +static ssize_t aw8695_ram_vbat_comp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw8695->lock); + if (val) { + aw8695->ram_vbat_comp = AW8695_HAPTIC_RAM_VBAT_COMP_ENABLE; + } else { + aw8695->ram_vbat_comp = AW8695_HAPTIC_RAM_VBAT_COMP_DISABLE; + } + mutex_unlock(&aw8695->lock); + + return count; +} + +static ssize_t aw8695_haptic_audio_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + ssize_t len = 0; + len += snprintf(buf + len, PAGE_SIZE - len, "%d\n", aw8695->haptic_audio.cnt); + return len; +} + +static ssize_t aw8695_haptic_audio_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int databuf[6] = {0}; + + if (6 == sscanf(buf, "%d %d %d %d %d %d", &databuf[0], &databuf[1], &databuf[2], + &databuf[3], &databuf[4], &databuf[5])) { + pr_debug("%s: cnt=%d, cmd=%d, play=%d, wavseq=%d, loop=%d, gain=%d\n", + __func__, databuf[0], databuf[1], databuf[2], databuf[3], + databuf[4], databuf[5]); + mutex_lock(&aw8695->haptic_audio.lock); + aw8695->haptic_audio.data[(unsigned char)databuf[0]].cmd = (unsigned char)databuf[1]; + aw8695->haptic_audio.data[(unsigned char)databuf[0]].play = (unsigned char)databuf[2]; + aw8695->haptic_audio.data[(unsigned char)databuf[0]].wavseq = (unsigned char)databuf[3]; + aw8695->haptic_audio.data[(unsigned char)databuf[0]].loop = (unsigned char)databuf[4]; + aw8695->haptic_audio.data[(unsigned char)databuf[0]].gain = (unsigned char)databuf[5]; + mutex_unlock(&aw8695->haptic_audio.lock); + + if (aw8695->haptic_audio.data[aw8695->haptic_audio.cnt].cmd == 0xff) { + pr_info("%s: haptic_audio stop\n", __func__); + if (hrtimer_active(&aw8695->haptic_audio.timer)) { + pr_info("%s: cancel haptic_audio_timer\n", __func__); + hrtimer_cancel(&aw8695->haptic_audio.timer); + aw8695->haptic_audio.cnt = 0; + aw8695_haptic_set_gain(aw8695, 0x80); + } + } else { + if (hrtimer_active(&aw8695->haptic_audio.timer)) { + } else { + pr_info("%s: start haptic_audio_timer\n", __func__); + hrtimer_start(&aw8695->haptic_audio.timer, + ktime_set(aw8695->haptic_audio.delay_val / 1000000, + (aw8695->haptic_audio.delay_val % 1000000) * 1000), + HRTIMER_MODE_REL); + } + } + } + return count; +} + + +static ssize_t aw8695_haptic_audio_time_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + ssize_t len = 0; + len += snprintf(buf + len, PAGE_SIZE - len, "haptic_audio.delay_val=%dus\n", aw8695->haptic_audio.delay_val); + len += snprintf(buf + len, PAGE_SIZE - len, "haptic_audio.timer_val=%dus\n", aw8695->haptic_audio.timer_val); + return len; +} + +static ssize_t aw8695_haptic_audio_time_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(to_dev, struct aw8695, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8695 *aw8695 = container_of(cdev, struct aw8695, cdev); +#endif + unsigned int databuf[2] = {0}; + + if (2 == sscanf(buf, "%d %d", &databuf[0], &databuf[1])) { + aw8695->haptic_audio.delay_val = databuf[0]; + aw8695->haptic_audio.timer_val = databuf[1]; + } + return count; +} + + +static DEVICE_ATTR(state, S_IWUSR | S_IRUGO, aw8695_state_show, aw8695_state_store); +static DEVICE_ATTR(duration, S_IWUSR | S_IRUGO, aw8695_duration_show, aw8695_duration_store); +static DEVICE_ATTR(activate, S_IWUSR | S_IRUGO, aw8695_activate_show, aw8695_activate_store); +static DEVICE_ATTR(activate_mode, S_IWUSR | S_IRUGO, aw8695_activate_mode_show, aw8695_activate_mode_store); +static DEVICE_ATTR(index, S_IWUSR | S_IRUGO, aw8695_index_show, aw8695_index_store); +static DEVICE_ATTR(vmax, S_IWUSR | S_IRUGO, aw8695_vmax_show, aw8695_vmax_store); +static DEVICE_ATTR(gain, S_IWUSR | S_IRUGO, aw8695_gain_show, aw8695_gain_store); +static DEVICE_ATTR(seq, S_IWUSR | S_IRUGO, aw8695_seq_show, aw8695_seq_store); +static DEVICE_ATTR(loop, S_IWUSR | S_IRUGO, aw8695_loop_show, aw8695_loop_store); +static DEVICE_ATTR(register, S_IWUSR | S_IRUGO, aw8695_reg_show, aw8695_reg_store); +static DEVICE_ATTR(rtp, S_IWUSR | S_IRUGO, aw8695_rtp_show, aw8695_rtp_store); +static DEVICE_ATTR(ram_update, S_IWUSR | S_IRUGO, aw8695_ram_update_show, aw8695_ram_update_store); +static DEVICE_ATTR(f0, S_IWUSR | S_IRUGO, aw8695_f0_show, aw8695_f0_store); +static DEVICE_ATTR(cali, S_IWUSR | S_IRUGO, aw8695_cali_show, aw8695_cali_store); +static DEVICE_ATTR(cont, S_IWUSR | S_IRUGO, aw8695_cont_show, aw8695_cont_store); +static DEVICE_ATTR(cont_td, S_IWUSR | S_IRUGO, aw8695_cont_td_show, aw8695_cont_td_store); +static DEVICE_ATTR(cont_drv, S_IWUSR | S_IRUGO, aw8695_cont_drv_show, aw8695_cont_drv_store); +static DEVICE_ATTR(cont_num_brk, S_IWUSR | S_IRUGO, aw8695_cont_num_brk_show, aw8695_cont_num_brk_store); +static DEVICE_ATTR(cont_zc_thr, S_IWUSR | S_IRUGO, aw8695_cont_zc_thr_show, aw8695_cont_zc_thr_store); +static DEVICE_ATTR(vbat_monitor, S_IWUSR | S_IRUGO, aw8695_vbat_monitor_show, aw8695_vbat_monitor_store); +static DEVICE_ATTR(lra_resistance, S_IWUSR | S_IRUGO, aw8695_lra_resistance_show, aw8695_lra_resistance_store); +static DEVICE_ATTR(auto_boost, S_IWUSR | S_IRUGO, aw8695_auto_boost_show, aw8695_auto_boost_store); +static DEVICE_ATTR(prctmode, S_IWUSR | S_IRUGO, aw8695_prctmode_show, aw8695_prctmode_store); +static DEVICE_ATTR(trig, S_IWUSR | S_IRUGO, aw8695_trig_show, aw8695_trig_store); +static DEVICE_ATTR(ram_vbat_comp, S_IWUSR | S_IRUGO, aw8695_ram_vbat_comp_show, aw8695_ram_vbat_comp_store); +static DEVICE_ATTR(haptic_audio, S_IWUSR | S_IRUGO, aw8695_haptic_audio_show, aw8695_haptic_audio_store); +static DEVICE_ATTR(haptic_audio_time, S_IWUSR | S_IRUGO, aw8695_haptic_audio_time_show, aw8695_haptic_audio_time_store); + + +static struct attribute *aw8695_vibrator_attributes[] = { + &dev_attr_state.attr, + &dev_attr_duration.attr, + &dev_attr_activate.attr, + &dev_attr_activate_mode.attr, + &dev_attr_index.attr, + &dev_attr_vmax.attr, + &dev_attr_gain.attr, + &dev_attr_seq.attr, + &dev_attr_loop.attr, + &dev_attr_register.attr, + &dev_attr_rtp.attr, + &dev_attr_ram_update.attr, + &dev_attr_f0.attr, + &dev_attr_cali.attr, + &dev_attr_cont.attr, + &dev_attr_cont_td.attr, + &dev_attr_cont_drv.attr, + &dev_attr_cont_num_brk.attr, + &dev_attr_cont_zc_thr.attr, + &dev_attr_vbat_monitor.attr, + &dev_attr_lra_resistance.attr, + &dev_attr_auto_boost.attr, + &dev_attr_prctmode.attr, + &dev_attr_trig.attr, + &dev_attr_ram_vbat_comp.attr, + &dev_attr_haptic_audio.attr, + &dev_attr_haptic_audio_time.attr, + NULL +}; + +static struct attribute_group aw8695_vibrator_attribute_group = { + .attrs = aw8695_vibrator_attributes +}; + +static enum hrtimer_restart aw8695_vibrator_timer_func(struct hrtimer *timer) +{ + struct aw8695 *aw8695 = container_of(timer, struct aw8695, timer); + + pr_debug("%s enter\n", __func__); + aw8695->state = 0; + schedule_work(&aw8695->vibrator_work); + + return HRTIMER_NORESTART; +} + +static void aw8695_vibrator_work_routine(struct work_struct *work) +{ + struct aw8695 *aw8695 = container_of(work, struct aw8695, vibrator_work); + + pr_debug("%s enter\n", __func__); + + mutex_lock(&aw8695->lock); + +#ifdef AW8695_REPEAT_RTP_PLAYING + if (aw8695->haptic_mode == HAPTIC_RTP_LOOP) + aw8695->haptic_mode = HAPTIC_RTP; +#endif + aw8695_haptic_stop(aw8695); + if (aw8695->state) { + if (aw8695->activate_mode == AW8695_HAPTIC_ACTIVATE_RAM_MODE) { + aw8695_haptic_ram_vbat_comp(aw8695, true); + aw8695_haptic_play_repeat_seq(aw8695, true); + } else if (aw8695->activate_mode == AW8695_HAPTIC_ACTIVATE_CONT_MODE) { + aw8695_haptic_cont(aw8695); + } else { + } + } + mutex_unlock(&aw8695->lock); +} + +static int aw8695_vibrator_init(struct aw8695 *aw8695) +{ + int ret = 0; + + pr_info("%s enter\n", __func__); + +#ifdef TIMED_OUTPUT + aw8695->to_dev.name = "vibrator"; + aw8695->to_dev.get_time = aw8695_vibrator_get_time; + aw8695->to_dev.enable = aw8695_vibrator_enable; + + ret = timed_output_dev_register(&(aw8695->to_dev)); + if (ret < 0) { + dev_err(aw8695->dev, "%s: fail to create timed output dev\n", + __func__); + return ret; + } + ret = sysfs_create_group(&aw8695->to_dev.dev->kobj, &aw8695_vibrator_attribute_group); + if (ret < 0) { + dev_err(aw8695->dev, "%s error creating sysfs attr files\n", __func__); + return ret; + } +#else + aw8695->cdev.name = "vibrator"; + aw8695->cdev.brightness_get = aw8695_haptic_brightness_get; + aw8695->cdev.brightness_set = aw8695_haptic_brightness_set; + + ret = devm_led_classdev_register(&aw8695->i2c->dev, &aw8695->cdev); + if (ret < 0) { + dev_err(aw8695->dev, "%s: fail to create led dev\n", + __func__); + return ret; + } + ret = sysfs_create_group(&aw8695->cdev.dev->kobj, &aw8695_vibrator_attribute_group); + if (ret < 0) { + dev_err(aw8695->dev, "%s error creating sysfs attr files\n", __func__); + return ret; + } +#endif + hrtimer_init(&aw8695->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + aw8695->timer.function = aw8695_vibrator_timer_func; + INIT_WORK(&aw8695->vibrator_work, aw8695_vibrator_work_routine); + + INIT_WORK(&aw8695->rtp_work, aw8695_rtp_work_routine); + + PM_WAKEUP_REGISTER(aw8695->dev, aw8695->ws, "vibrator"); + if (!aw8695->ws) + return -ENOMEM; + + mutex_init(&aw8695->lock); + + return 0; +} + + + + +/****************************************************** + * + * irq + * + ******************************************************/ +static void aw8695_interrupt_clear(struct aw8695 *aw8695) +{ + unsigned char reg_val = 0; + pr_debug("%s enter\n", __func__); + aw8695_i2c_read(aw8695, AW8695_REG_SYSINT, ®_val); + pr_debug("%s: reg SYSINT=0x%x\n", __func__, reg_val); +} + +static void aw8695_interrupt_setup(struct aw8695 *aw8695) +{ + unsigned char reg_val = 0; + + pr_info("%s enter\n", __func__); + + aw8695_i2c_read(aw8695, AW8695_REG_SYSINT, ®_val); + pr_info("%s: reg SYSINT=0x%x\n", __func__, reg_val); + + /* edge int mode */ + aw8695_i2c_write_bits(aw8695, AW8695_REG_DBGCTRL, + AW8695_BIT_DBGCTRL_INT_MODE_MASK, AW8695_BIT_DBGCTRL_INT_MODE_EDGE); + + /* int enable */ + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSINTM, + AW8695_BIT_SYSINTM_BSTERR_MASK, AW8695_BIT_SYSINTM_BSTERR_OFF); + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSINTM, + AW8695_BIT_SYSINTM_OV_MASK, AW8695_BIT_SYSINTM_OV_EN); + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSINTM, + AW8695_BIT_SYSINTM_UVLO_MASK, AW8695_BIT_SYSINTM_UVLO_EN); + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSINTM, + AW8695_BIT_SYSINTM_OCD_MASK, AW8695_BIT_SYSINTM_OCD_EN); + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSINTM, + AW8695_BIT_SYSINTM_OT_MASK, AW8695_BIT_SYSINTM_OT_EN); +} + +static irqreturn_t aw8695_irq(int irq, void *data) +{ + struct aw8695 *aw8695 = data; + unsigned char reg_val = 0; + unsigned char dbg_val = 0; + unsigned int buf_len = 0; + unsigned char glb_state = 0; + + pr_debug("%s enter\n", __func__); + + aw8695_i2c_read(aw8695, AW8695_REG_SYSINT, ®_val); + pr_info("%s: reg SYSINT=0x%x\n", __func__, reg_val); + aw8695_i2c_read(aw8695, AW8695_REG_DBGSTAT, &dbg_val); + pr_info("%s: reg DBGSTAT=0x%x\n", __func__, dbg_val); + + if (reg_val & AW8695_BIT_SYSINT_OVI) { + pr_err("%s chip ov int error\n", __func__); + } + if (reg_val & AW8695_BIT_SYSINT_UVLI) { + pr_err("%s chip uvlo int error\n", __func__); + } + if (reg_val & AW8695_BIT_SYSINT_OCDI) { + pr_err("%s chip over current int error\n", __func__); + } + if (reg_val & AW8695_BIT_SYSINT_OTI) { + pr_err("%s chip over temperature int error\n", __func__); + } + if (reg_val & AW8695_BIT_SYSINT_DONEI) { + pr_info("%s chip playback done\n", __func__); + } + + if (reg_val & AW8695_BIT_SYSINT_FF_AEI) { + pr_debug("%s: aw8695 rtp fifo almost empty int\n", __func__); + if (aw8695->rtp_init) { + while ((!aw8695_haptic_rtp_get_fifo_afi(aw8695)) && + (aw8695->play_mode == AW8695_HAPTIC_RTP_MODE)) { + pr_info("%s: aw8695 rtp mode fifo update, cnt=%d\n", + __func__, aw8695->rtp_cnt); + if ((aw8695_rtp->len - aw8695->rtp_cnt) < (aw8695->ram.base_addr >> 2)) { + buf_len = aw8695_rtp->len - aw8695->rtp_cnt; + } else { + buf_len = (aw8695->ram.base_addr >> 2); + } + aw8695_i2c_writes(aw8695, AW8695_REG_RTP_DATA, + &aw8695_rtp->data[aw8695->rtp_cnt], buf_len); + aw8695->rtp_cnt += buf_len; + aw8695_i2c_read(aw8695, AW8695_REG_GLB_STATE, &glb_state); + if ((aw8695->rtp_cnt == aw8695_rtp->len) || ((glb_state & 0x0f) == 0x00)) { + pr_info("%s: rtp update complete\n", __func__); + aw8695_haptic_set_rtp_aei(aw8695, false); + aw8695->rtp_cnt = 0; + aw8695->rtp_init = 0; +#ifdef AW8695_REPEAT_RTP_PLAYING + if (aw8695->haptic_mode == HAPTIC_RTP_LOOP) + aw8695_rtp_play(aw8695, aw8695->rtp_file_num); +#endif + break; + } + } + } else { + pr_err("%s: aw8695 rtp init = %d, init error\n", __func__, aw8695->rtp_init); + } + } + + if (reg_val & AW8695_BIT_SYSINT_FF_AFI) { + pr_debug("%s: aw8695 rtp mode fifo full empty\n", __func__); + } + + if (aw8695->play_mode != AW8695_HAPTIC_RTP_MODE) { + aw8695_haptic_set_rtp_aei(aw8695, false); + } + + aw8695_i2c_read(aw8695, AW8695_REG_SYSINT, ®_val); + pr_debug("%s: reg SYSINT=0x%x\n", __func__, reg_val); + aw8695_i2c_read(aw8695, AW8695_REG_SYSST, ®_val); + pr_debug("%s: reg SYSST=0x%x\n", __func__, reg_val); + + pr_debug("%s exit\n", __func__); + + return IRQ_HANDLED; +} + +/***************************************************** + * + * device tree + * + *****************************************************/ +static int aw8695_parse_dt(struct device *dev, struct aw8695 *aw8695, + struct device_node *np) +{ + int rc; + + aw8695->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (aw8695->reset_gpio < 0) { + dev_err(dev, "%s: no reset gpio provided, will not HW reset device\n", __func__); + return -1; + } else { + dev_info(dev, "%s: reset gpio provided ok\n", __func__); + } + aw8695->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (aw8695->irq_gpio < 0) { + dev_err(dev, "%s: no irq gpio provided.\n", __func__); + } else { + dev_info(dev, "%s: irq gpio provided ok.\n", __func__); + } + + aw8695->haptic_context_gpio = of_get_named_gpio(np, "haptic-context-gpio", 0); + if (aw8695->haptic_context_gpio < 0) { + dev_err(dev, "%s: no haptic context gpio provided.\n", __func__); + } else { + dev_info(dev, "%s: haptic context gpio provided ok.\n", __func__); + } + + aw8695->factorymode_reduce = of_property_read_bool(np, "factorymode-reduce"); + if (aw8695->factorymode_reduce) { + dev_err(dev, "%s: reduce on factory mode.\n", __func__); + } else { + dev_info(dev, "%s:normal on factory mode.\n", __func__); + } + + rc = of_property_read_u32(np, "vib_f0_pre", &aw8695->f0_pre); + if (rc) { + aw8695->f0_pre = 2050; /* 205HZ as default */ + dev_err(dev, "%s: no f0_pre provided.\n", __func__); + } + + rc = of_property_read_u32(np, "vib_f0_cali_percen", &aw8695->f0_cali_percen); + if (rc) { + aw8695->f0_cali_percen = 7; /* 7 as default */ + dev_err(dev, "%s: no f0_cali_percen provided.\n", __func__); + } + + rc = of_property_read_u32(np, "vib_cont_drv_lvl", &aw8695->cont_drv_lvl); + if (rc) { + aw8695->cont_drv_lvl = 100; /* 100 as default */ + dev_err(dev, "%s: no cont_drv_lvl provided.\n", __func__); + } + + rc = of_property_read_u32(np, "vib_cont_drv_lvl_ov", &aw8695->cont_drv_lvl_ov); + if (rc) { + aw8695->cont_drv_lvl_ov = 120; /* 120 as default */ + dev_err(dev, "%s: no cont_drv_lvl_ov provided.\n", __func__); + } + + rc = of_property_read_u32(np, "vib_cont_td", &aw8695->cont_td); + if (rc) { + aw8695->cont_td = 0x006c; /* 0x006c as default */ + dev_err(dev, "%s: no cont_td provided.\n", __func__); + } + + rc = of_property_read_u32(np, "vib_cont_zc_thr", &aw8695->cont_zc_thr); + if (rc) { + aw8695->cont_zc_thr = 0x0ff1; /* 0x0ff1 as default */ + dev_err(dev, "%s: no cont_zc_thr provided.\n", __func__); + } + + rc = of_property_read_u32(np, "vib_cont_num_brk", &aw8695->cont_num_brk); + if (rc) { + aw8695->cont_num_brk = 3; /* 3 as default */ + dev_err(dev, "%s: no cont_zc_thr provided.\n", __func__); + } + + rc = of_property_read_s32(np, "long-gain-normal", &aw8695->long_gain_normal); + if (rc) { + aw8695->long_gain_normal = 0x80; + dev_err(dev, "%s: no normal gain value for long vibrating provided.\n", __func__); + } + dev_info(dev, "%s: normal gain value for long vibrating is 0x%02x.\n", __func__, aw8695->long_gain_normal); + + rc = of_property_read_s32(np, "long-gain-reduced", &aw8695->long_gain_reduced); + if (rc) { + aw8695->long_gain_reduced = 0x80; + dev_err(dev, "%s: no reduced gain value for long vibrating provided.\n", __func__); + } + dev_info(dev, "%s: reduced gain value for long vibrating is 0x%02x.\n", __func__, aw8695->long_gain_reduced); + return 0; +} + +static int aw8695_hw_reset(struct aw8695 *aw8695) +{ + pr_info("%s enter\n", __func__); + + if (aw8695 && gpio_is_valid(aw8695->reset_gpio)) { + gpio_set_value_cansleep(aw8695->reset_gpio, 0); + msleep(1); + gpio_set_value_cansleep(aw8695->reset_gpio, 1); + msleep(2); + } else { + dev_err(aw8695->dev, "%s: failed\n", __func__); + } + return 0; +} + + +/***************************************************** + * + * check chip id + * + *****************************************************/ +static int aw8695_read_chipid(struct aw8695 *aw8695) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char reg = 0; + + while (cnt < AW_READ_CHIPID_RETRIES) { + /* hardware reset */ + aw8695_hw_reset(aw8695); + + ret = aw8695_i2c_read(aw8695, AW8695_REG_ID, ®); + if (ret < 0) { + dev_err(aw8695->dev, "%s: failed to read register AW8695_REG_ID: %d\n", __func__, ret); + } + switch (reg) { + case AW8695_CHIPID1: + pr_info("%s aw8695 detected\n", __func__); + aw8695->chipid = AW8695_CHIPID1; + /* + *aw8695->flags |= AW8695_FLAG_SKIP_INTERRUPTS; + */ + aw8695_haptic_softreset(aw8695); + return 0; + case AW8695_CHIPID2: + pr_info("%s aw8697 detected\n", __func__); + aw8695->chipid = AW8695_CHIPID2; + aw8695_haptic_softreset(aw8695); + return 0; + default: + pr_info("%s unsupported device revision (0x%x)\n", __func__, reg); + break; + } + cnt ++; + + msleep(AW_READ_CHIPID_RETRY_DELAY); + } + + return -EINVAL; +} + +/****************************************************** + * + * sys group attribute: reg + * + ******************************************************/ +static ssize_t aw8695_i2c_reg_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw8695 *aw8695 = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0, 0}; + + if (2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) { + aw8695_i2c_write(aw8695, (unsigned char)databuf[0], (unsigned char)databuf[1]); + } + + return count; +} + +static ssize_t aw8695_i2c_reg_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct aw8695 *aw8695 = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + for (i = 0; i < AW8695_REG_MAX; i ++) { + if (!(aw8695_reg_access[i]®_RD_ACCESS)) + continue; + aw8695_i2c_read(aw8695, i, ®_val); + len += snprintf(buf + len, PAGE_SIZE - len, "reg:0x%02x=0x%02x \n", i, reg_val); + } + return len; +} +static ssize_t aw8695_i2c_ram_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw8695 *aw8695 = dev_get_drvdata(dev); + + unsigned int databuf[1] = {0}; + + if (1 == sscanf(buf, "%x", &databuf[0])) { + if (1 == databuf[0]) { + aw8695_ram_update(aw8695); + } + } + + return count; +} + +static ssize_t aw8695_i2c_ram_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct aw8695 *aw8695 = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned int i = 0; + unsigned char reg_val = 0; + + aw8695_haptic_stop(aw8695); + /* RAMINIT Enable */ + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_RAMINIT_MASK, AW8695_BIT_SYSCTRL_RAMINIT_EN); + + aw8695_i2c_write(aw8695, AW8695_REG_RAMADDRH, (unsigned char)(aw8695->ram.base_addr >> 8)); + aw8695_i2c_write(aw8695, AW8695_REG_RAMADDRL, (unsigned char)(aw8695->ram.base_addr & 0x00ff)); + len += snprintf(buf + len, PAGE_SIZE - len, "aw8695_haptic_ram:\n"); + for (i = 0; i < aw8695->ram.len; i++) { + aw8695_i2c_read(aw8695, AW8695_REG_RAMDATA, ®_val); + len += snprintf(buf + len, PAGE_SIZE - len, "0x%02x,", reg_val); + } + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + /* RAMINIT Disable */ + aw8695_i2c_write_bits(aw8695, AW8695_REG_SYSCTRL, + AW8695_BIT_SYSCTRL_RAMINIT_MASK, AW8695_BIT_SYSCTRL_RAMINIT_OFF); + + return len; +} + +static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, aw8695_i2c_reg_show, aw8695_i2c_reg_store); +static DEVICE_ATTR(ram, S_IWUSR | S_IRUGO, aw8695_i2c_ram_show, aw8695_i2c_ram_store); + +static struct attribute *aw8695_attributes[] = { + &dev_attr_reg.attr, + &dev_attr_ram.attr, + NULL +}; + +static struct attribute_group aw8695_attribute_group = { + .attrs = aw8695_attributes +}; + +static bool mmi_factory_check(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool factory = false; + if (np) + factory = of_property_read_bool(np, "mmi,factory-cable"); + of_node_put(np); + return factory; +} + +/****************************************************** + * + * i2c driver + * + ******************************************************/ +static int aw8695_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct aw8695 *aw8695; + struct device_node *np = i2c->dev.of_node; + int irq_flags = 0; + int ret = -1; + + pr_info("%s enter\n", __func__); + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed\n"); + return -EIO; + } + + aw8695 = devm_kzalloc(&i2c->dev, sizeof(struct aw8695), GFP_KERNEL); + if (aw8695 == NULL) + return -ENOMEM; + + aw8695->dev = &i2c->dev; + aw8695->i2c = i2c; + + i2c_set_clientdata(i2c, aw8695); + + /* aw8695 rst & int */ + if (np) { + ret = aw8695_parse_dt(&i2c->dev, aw8695, np); + if (ret) { + dev_err(&i2c->dev, "%s: failed to parse device tree node\n", __func__); + goto err_parse_dt; + } + } else { + aw8695->reset_gpio = -1; + aw8695->irq_gpio = -1; + } + + if (gpio_is_valid(aw8695->reset_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw8695->reset_gpio, + GPIOF_OUT_INIT_LOW, "aw8695_rst"); + if (ret) { + dev_err(&i2c->dev, "%s: rst request failed\n", __func__); + goto err_reset_gpio_request; + } + } + + if (gpio_is_valid(aw8695->irq_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw8695->irq_gpio, + GPIOF_DIR_IN, "aw8695_int"); + if (ret) { + dev_err(&i2c->dev, "%s: int request failed\n", __func__); + goto err_irq_gpio_request; + } + } + + /* aw8695 chip id */ + ret = aw8695_read_chipid(aw8695); + if (ret < 0) { + dev_err(&i2c->dev, "%s: aw8695_read_chipid failed ret=%d\n", __func__, ret); + goto err_id; + } + + /* aw8695 irq */ + if (gpio_is_valid(aw8695->irq_gpio) && + !(aw8695->flags & AW8695_FLAG_SKIP_INTERRUPTS)) { + /* register irq handler */ + aw8695_interrupt_setup(aw8695); + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, + gpio_to_irq(aw8695->irq_gpio), + NULL, aw8695_irq, irq_flags, + "aw8695", aw8695); + if (ret != 0) { + dev_err(&i2c->dev, "%s: failed to request IRQ %d: %d\n", + __func__, gpio_to_irq(aw8695->irq_gpio), ret); + goto err_irq; + } + } else { + dev_info(&i2c->dev, "%s skipping IRQ registration\n", __func__); + /* disable feature support if gpio was invalid */ + aw8695->flags |= AW8695_FLAG_SKIP_INTERRUPTS; + } + + dev_set_drvdata(&i2c->dev, aw8695); + + ret = sysfs_create_group(&i2c->dev.kobj, &aw8695_attribute_group); + if (ret < 0) { + dev_info(&i2c->dev, "%s error creating sysfs attr files\n", __func__); + goto err_sysfs; + } + + g_aw8695 = aw8695; + + if (aw8695->factorymode_reduce) + aw8695->factory_mode = false; + else + aw8695->factory_mode = mmi_factory_check(); + + aw8695_vibrator_init(aw8695); + + aw8695_haptic_init(aw8695); + + aw8695_ram_init(aw8695); + + pr_info("%s probe completed successfully!\n", __func__); + + return 0; + +err_sysfs: + devm_free_irq(&i2c->dev, gpio_to_irq(aw8695->irq_gpio), aw8695); +err_irq: +err_id: + if (gpio_is_valid(aw8695->irq_gpio)) + devm_gpio_free(&i2c->dev, aw8695->irq_gpio); +err_irq_gpio_request: + if (gpio_is_valid(aw8695->reset_gpio)) + devm_gpio_free(&i2c->dev, aw8695->reset_gpio); +err_reset_gpio_request: +err_parse_dt: + devm_kfree(&i2c->dev, aw8695); + aw8695 = NULL; + return ret; +} + +static int aw8695_i2c_remove(struct i2c_client *i2c) +{ + struct aw8695 *aw8695 = i2c_get_clientdata(i2c); + + pr_info("%s enter\n", __func__); + + sysfs_remove_group(&i2c->dev.kobj, &aw8695_attribute_group); + + devm_free_irq(&i2c->dev, gpio_to_irq(aw8695->irq_gpio), aw8695); + + if (gpio_is_valid(aw8695->irq_gpio)) + devm_gpio_free(&i2c->dev, aw8695->irq_gpio); + if (gpio_is_valid(aw8695->reset_gpio)) + devm_gpio_free(&i2c->dev, aw8695->reset_gpio); + + PM_WAKEUP_UNREGISTER(aw8695->ws); + + devm_kfree(&i2c->dev, aw8695); + aw8695 = NULL; + + return 0; +} + +static const struct i2c_device_id aw8695_i2c_id[] = { + { AW8695_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw8695_i2c_id); + +static struct of_device_id aw8695_dt_match[] = { + { .compatible = "awinic,aw8695_haptic" }, + { }, +}; + +static struct i2c_driver aw8695_i2c_driver = { + .driver = { + .name = AW8695_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw8695_dt_match), + }, + .probe = aw8695_i2c_probe, + .remove = aw8695_i2c_remove, + .id_table = aw8695_i2c_id, +}; + + +static int __init aw8695_i2c_init(void) +{ + int ret = 0; + + pr_info("aw8695 driver version %s\n", AW8695_VERSION); + + ret = i2c_add_driver(&aw8695_i2c_driver); + if (ret) { + pr_err("fail to add aw8695 device into i2c\n"); + return ret; + } + + return 0; +} + +module_init(aw8695_i2c_init); + + +static void __exit aw8695_i2c_exit(void) +{ + i2c_del_driver(&aw8695_i2c_driver); +} +module_exit(aw8695_i2c_exit); + + +MODULE_DESCRIPTION("AW8695 Haptic Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/awinic/aw8695/aw8695.h b/drivers/misc/awinic/aw8695/aw8695.h new file mode 100644 index 000000000000..4e1a92a11633 --- /dev/null +++ b/drivers/misc/awinic/aw8695/aw8695.h @@ -0,0 +1,374 @@ +#ifndef _AW8695_H_ +#define _AW8695_H_ + +/********************************************************* + * + * kernel version + * + ********************************************************/ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 4, 1) +#define TIMED_OUTPUT +#endif + +/********************************************************* + * + * aw8695.h + * + ********************************************************/ +#include +#include +#include +#include +#include +#include +#ifdef TIMED_OUTPUT +#include <../../../drivers/staging/android/timed_output.h> +#else +#include +#endif +#include + +/********************************************************* + * + * marco + * + ********************************************************/ +#define MAX_I2C_BUFFER_SIZE 65536 + +#define AW8695_REG_MAX 0xff + +#define AW8695_WAV_SEQ_SIZE 4 +#define AW8695_SEQUENCER_SIZE 8 +#define AW8695_SEQUENCER_LOOP_SIZE 4 + +#define AW8695_RTP_I2C_SINGLE_MAX_NUM 512 + +#define HAPTIC_MAX_TIMEOUT 10000 + +#define AW8695_VBAT_REFER 4200 +#define AW8695_VBAT_MIN 3000 +#define AW8695_VBAT_MAX 4500 + +/* motor config */ + +#define AW8695_HAPTIC_F0_COEFF 260 /*2.604167 */ + + +/* trig config */ +#define AW8695_TRIG_NUM 3 +#define AW8695_TRG1_ENABLE 1 +#define AW8695_TRG1_DISABLE 0 +#define AW8695_TRG2_ENABLE 1 +#define AW8695_TRG2_DISABLE 0 +#define AW8695_TRG3_ENABLE 1 +#define AW8695_TRG3_DISABLE 0 + +/* + * trig default high level + * ___________ _________________ + * | | + * | | + * |___________| + * first edge + * second edge + * + * + * trig default low level + * ___________ + * | | + * | | + * __________| |_________________ + * first edge + * second edge + */ +#define AW8695_TRG1_DEFAULT_LEVEL 1 /* 1: high level; 0: low level */ +#define AW8695_TRG2_DEFAULT_LEVEL 1 /* 1: high level; 0: low level */ +#define AW8695_TRG3_DEFAULT_LEVEL 1 /* 1: high level; 0: low level */ + +#define AW8695_TRG1_DUAL_EDGE 1 /* 1: dual edge; 0: first edge */ +#define AW8695_TRG2_DUAL_EDGE 1 /* 1: dual edge; 0: first edge */ +#define AW8695_TRG3_DUAL_EDGE 1 /* 1: dual edge; 0: first edge */ +#define AW8695_TRG1_FIRST_EDGE_SEQ 1 /* trig1: first edge waveform seq */ +#define AW8695_TRG1_SECOND_EDGE_SEQ 2 /* trig1: second edge waveform seq */ +#define AW8695_TRG2_FIRST_EDGE_SEQ 1 /* trig2: first edge waveform seq */ +#define AW8695_TRG2_SECOND_EDGE_SEQ 2 /* trig2: second edge waveform seq */ +#define AW8695_TRG3_FIRST_EDGE_SEQ 1 /* trig3: first edge waveform seq */ +#define AW8695_TRG3_SECOND_EDGE_SEQ 2 /* trig3: second edge waveform seq */ + + +#if AW8695_TRG1_ENABLE +#define AW8695_TRG1_DEFAULT_ENABLE AW8695_BIT_TRGCFG2_TRG1_ENABLE +#else +#define AW8695_TRG1_DEFAULT_ENABLE AW8695_BIT_TRGCFG2_TRG1_DISABLE +#endif + +#if AW8695_TRG2_ENABLE +#define AW8695_TRG2_DEFAULT_ENABLE AW8695_BIT_TRGCFG2_TRG2_ENABLE +#else +#define AW8695_TRG2_DEFAULT_ENABLE AW8695_BIT_TRGCFG2_TRG2_DISABLE +#endif + +#if AW8695_TRG3_ENABLE +#define AW8695_TRG3_DEFAULT_ENABLE AW8695_BIT_TRGCFG2_TRG3_ENABLE +#else +#define AW8695_TRG3_DEFAULT_ENABLE AW8695_BIT_TRGCFG2_TRG3_DISABLE +#endif + +#if AW8695_TRG1_DEFAULT_LEVEL +#define AW8695_TRG1_DEFAULT_POLAR AW8695_BIT_TRGCFG1_TRG1_POLAR_POS +#else +#define AW8695_TRG1_DEFAULT_POLAR AW8695_BIT_TRGCFG1_TRG1_POLAR_NEG +#endif + +#if AW8695_TRG2_DEFAULT_LEVEL +#define AW8695_TRG2_DEFAULT_POLAR AW8695_BIT_TRGCFG1_TRG2_POLAR_POS +#else +#define AW8695_TRG2_DEFAULT_POLAR AW8695_BIT_TRGCFG1_TRG2_POLAR_NEG +#endif + +#if AW8695_TRG3_DEFAULT_LEVEL +#define AW8695_TRG3_DEFAULT_POLAR AW8695_BIT_TRGCFG1_TRG3_POLAR_POS +#else +#define AW8695_TRG3_DEFAULT_POLAR AW8695_BIT_TRGCFG1_TRG3_POLAR_NEG +#endif + +#if AW8695_TRG1_DUAL_EDGE +#define AW8695_TRG1_DEFAULT_EDGE AW8695_BIT_TRGCFG1_TRG1_EDGE_POS_NEG +#else +#define AW8695_TRG1_DEFAULT_EDGE AW8695_BIT_TRGCFG1_TRG1_EDGE_POS +#endif + +#if AW8695_TRG2_DUAL_EDGE +#define AW8695_TRG2_DEFAULT_EDGE AW8695_BIT_TRGCFG1_TRG2_EDGE_POS_NEG +#else +#define AW8695_TRG2_DEFAULT_EDGE AW8695_BIT_TRGCFG1_TRG2_EDGE_POS +#endif + +#if AW8695_TRG3_DUAL_EDGE +#define AW8695_TRG3_DEFAULT_EDGE AW8695_BIT_TRGCFG1_TRG3_EDGE_POS_NEG +#else +#define AW8695_TRG3_DEFAULT_EDGE AW8695_BIT_TRGCFG1_TRG3_EDGE_POS +#endif + + +enum aw8695_flags { + AW8695_FLAG_NONR = 0, + AW8695_FLAG_SKIP_INTERRUPTS = 1, +}; + +enum aw8695_haptic_read_write { + AW8695_HAPTIC_CMD_READ_REG = 0, + AW8695_HAPTIC_CMD_WRITE_REG = 1, +}; + + +enum aw8695_haptic_work_mode { + AW8695_HAPTIC_STANDBY_MODE = 0, + AW8695_HAPTIC_RAM_MODE = 1, + AW8695_HAPTIC_RTP_MODE = 2, + AW8695_HAPTIC_TRIG_MODE = 3, + AW8695_HAPTIC_CONT_MODE = 4, + AW8695_HAPTIC_RAM_LOOP_MODE = 5, +}; + +enum aw8695_haptic_bst_mode { + AW8695_HAPTIC_BYPASS_MODE = 0, + AW8695_HAPTIC_BOOST_MODE = 1, +}; + +enum aw8695_haptic_activate_mode { + AW8695_HAPTIC_ACTIVATE_RAM_MODE = 0, + AW8695_HAPTIC_ACTIVATE_CONT_MODE = 1, +}; + + +enum aw8695_haptic_cont_vbat_comp_mode { + AW8695_HAPTIC_CONT_VBAT_SW_COMP_MODE = 0, + AW8695_HAPTIC_CONT_VBAT_HW_COMP_MODE = 1, +}; + +enum aw8695_haptic_ram_vbat_comp_mode { + AW8695_HAPTIC_RAM_VBAT_COMP_DISABLE = 0, + AW8695_HAPTIC_RAM_VBAT_COMP_ENABLE = 1, +}; + +enum aw8695_haptic_f0_flag { + AW8695_HAPTIC_LRA_F0 = 0, + AW8695_HAPTIC_CALI_F0 = 1, +}; + +enum aw8695_haptic_pwm_mode { + AW8695_PWM_48K = 0, + AW8695_PWM_24K = 1, + AW8695_PWM_12K = 2, +}; + +enum aw8695_haptic_mode{ + HAPTIC_NONE = 0x00, + HAPTIC_SHORT = 0x01, + HAPTIC_LONG = 0x02, + HAPTIC_RTP = 0x03, + HAPTIC_RTP_LOOP = 0x04, +}; +/********************************************************* + * + * struct + * + ********************************************************/ +struct fileops { + unsigned char cmd; + unsigned char reg; + unsigned char ram_addrh; + unsigned char ram_addrl; +}; + +struct ram { + unsigned int len; + unsigned int check_sum; + unsigned int base_addr; + unsigned char version; + unsigned char ram_shift; + unsigned char baseaddr_shift; +}; + +struct haptic_ctr { + unsigned char cmd; + unsigned char play; + unsigned char wavseq; + unsigned char loop; + unsigned char gain; +}; + +struct haptic_audio { + struct mutex lock; + struct hrtimer timer; + struct work_struct work; + int delay_val; + int timer_val; + unsigned char cnt; + struct haptic_ctr data[256]; + struct haptic_ctr ctr; +}; + +struct trig { + unsigned char enable; + unsigned char default_level; + unsigned char dual_edge; + unsigned char frist_seq; + unsigned char second_seq; +}; + +struct aw8695 { + struct regmap *regmap; + struct i2c_client *i2c; + struct device *dev; + struct input_dev *input; + + struct wakeup_source *ws; + struct mutex lock; + struct hrtimer timer; + struct work_struct vibrator_work; + struct work_struct rtp_work; + struct delayed_work ram_work; + struct TIME_TYPE current_time; + struct TIME_TYPE pre_enter_time; +#ifdef TIMED_OUTPUT + struct timed_output_dev to_dev; +#else + struct led_classdev cdev; +#endif + struct fileops fileops; + struct ram ram; + + int reset_gpio; + int irq_gpio; + int haptic_context_gpio; + int long_gain_normal; + int long_gain_reduced; + + enum aw8695_haptic_mode haptic_mode; + bool factorymode_reduce; + bool factory_mode; + bool debugfs_debug; + atomic_t reduce_pwr; + unsigned char hwen_flag; + unsigned char flags; + unsigned char chipid; + + unsigned char play_mode; + + unsigned char activate_mode; + + unsigned char auto_boost; + + int state; + int duration; + int amplitude; + int index; + int vmax; + int gain; + int gain_debug; + unsigned char seq[AW8695_SEQUENCER_SIZE]; + unsigned char loop[AW8695_SEQUENCER_SIZE]; + + unsigned int rtp_cnt; + unsigned int rtp_file_num; + + unsigned char rtp_init; + unsigned char ram_init; + + unsigned int f0; + unsigned int f0_pre; + unsigned int f0_cali_percen; + unsigned int cont_f0; + unsigned int cont_td; + unsigned int cont_zc_thr; + unsigned int cont_drv_lvl; + unsigned int cont_drv_lvl_ov; + unsigned int cont_num_brk; + unsigned char max_pos_beme; + unsigned char max_neg_beme; + unsigned char f0_cali_flag; + + unsigned char ram_vbat_comp; + unsigned int vbat; + unsigned int lra; + unsigned int interval_us; + struct trig trig[AW8695_TRIG_NUM]; + + struct haptic_audio haptic_audio; +}; + +struct aw8695_container { + int len; + unsigned char data[]; +}; + + +/********************************************************* + * + * ioctl + * + ********************************************************/ +struct aw8695_seq_loop { + unsigned char loop[AW8695_SEQUENCER_SIZE]; +}; + +struct aw8695_que_seq { + unsigned char index[AW8695_SEQUENCER_SIZE]; +}; + + +#define AW8695_HAPTIC_IOCTL_MAGIC 'h' + +#define AW8695_HAPTIC_SET_QUE_SEQ _IOWR(AW8695_HAPTIC_IOCTL_MAGIC, 1, struct aw8695_que_seq*) +#define AW8695_HAPTIC_SET_SEQ_LOOP _IOWR(AW8695_HAPTIC_IOCTL_MAGIC, 2, struct aw8695_seq_loop*) +#define AW8695_HAPTIC_PLAY_QUE_SEQ _IOWR(AW8695_HAPTIC_IOCTL_MAGIC, 3, unsigned int) +#define AW8695_HAPTIC_SET_BST_VOL _IOWR(AW8695_HAPTIC_IOCTL_MAGIC, 4, unsigned int) +#define AW8695_HAPTIC_SET_BST_PEAK_CUR _IOWR(AW8695_HAPTIC_IOCTL_MAGIC, 5, unsigned int) +#define AW8695_HAPTIC_SET_GAIN _IOWR(AW8695_HAPTIC_IOCTL_MAGIC, 6, unsigned int) +#define AW8695_HAPTIC_PLAY_REPEAT_SEQ _IOWR(AW8695_HAPTIC_IOCTL_MAGIC, 7, unsigned int) + + +#endif + diff --git a/drivers/misc/awinic/aw8695/aw8695_config.h b/drivers/misc/awinic/aw8695/aw8695_config.h new file mode 100644 index 000000000000..84778028d356 --- /dev/null +++ b/drivers/misc/awinic/aw8695/aw8695_config.h @@ -0,0 +1,13 @@ +#ifndef __AW8695_CONFIG_H__ +#define __AW8695_CONFIG_H__ + +#define AW8695_CHIPID1 0x95 +#define AW8695_CHIPID2 0x97 + +#define AW8695_BSTCFG_PEAKCUR_LIMIT 0x03 +#define AW8695_DEFAULT_PEAKCUR AW8695_BIT_BSTCFG_PEAKCUR_2A + +#define AW8695_CONT_PLAYBACK_MODE AW8695_BIT_CONT_CTRL_CLOSE_PLAYBACK + + +#endif diff --git a/drivers/misc/awinic/aw8695/aw8695_reg.h b/drivers/misc/awinic/aw8695/aw8695_reg.h new file mode 100644 index 000000000000..8b2ec79b692e --- /dev/null +++ b/drivers/misc/awinic/aw8695/aw8695_reg.h @@ -0,0 +1,618 @@ +#ifndef _AW8695_REG_H_ +#define _AW8695_REG_H_ + +/******************************************** + * Register List + *******************************************/ +#define AW8695_REG_ID 0x00 +#define AW8695_REG_SYSST 0x01 +#define AW8695_REG_SYSINT 0x02 +#define AW8695_REG_SYSINTM 0x03 +#define AW8695_REG_SYSCTRL 0x04 +#define AW8695_REG_GO 0x05 +#define AW8695_REG_RTP_DATA 0x06 +#define AW8695_REG_WAVSEQ1 0x07 +#define AW8695_REG_WAVSEQ2 0x08 +#define AW8695_REG_WAVSEQ3 0x09 +#define AW8695_REG_WAVSEQ4 0x0a +#define AW8695_REG_WAVSEQ5 0x0b +#define AW8695_REG_WAVSEQ6 0x0c +#define AW8695_REG_WAVSEQ7 0x0d +#define AW8695_REG_WAVSEQ8 0x0e +#define AW8695_REG_WAVLOOP1 0x0f +#define AW8695_REG_WAVLOOP2 0x10 +#define AW8695_REG_WAVLOOP3 0x11 +#define AW8695_REG_WAVLOOP4 0x12 +#define AW8695_REG_MAIN_LOOP 0x13 +#define AW8695_REG_TRG1_WAV_P 0x14 +#define AW8695_REG_TRG2_WAV_P 0x15 +#define AW8695_REG_TRG3_WAV_P 0x16 +#define AW8695_REG_TRG1_WAV_N 0x17 +#define AW8695_REG_TRG2_WAV_N 0x18 +#define AW8695_REG_TRG3_WAV_N 0x19 +#define AW8695_REG_TRG_PRIO 0x1a +#define AW8695_REG_TRG_CFG1 0x1b +#define AW8695_REG_TRG_CFG2 0x1c +#define AW8695_REG_DBGCTRL 0x20 +#define AW8695_REG_BASE_ADDRH 0x21 +#define AW8695_REG_BASE_ADDRL 0x22 +#define AW8695_REG_FIFO_AEH 0x23 +#define AW8695_REG_FIFO_AEL 0x24 +#define AW8695_REG_FIFO_AFH 0x25 +#define AW8695_REG_FIFO_AFL 0x26 +#define AW8695_REG_WAKE_DLY 0x27 +#define AW8695_REG_START_DLY 0x28 +#define AW8695_REG_END_DLY_H 0x29 +#define AW8695_REG_END_DLY_L 0x2a +#define AW8695_REG_DATCTRL 0x2b +#define AW8695_REG_PWMDEL 0x2c +#define AW8695_REG_PWMPRC 0x2d +#define AW8695_REG_PWMDBG 0x2e +#define AW8695_REG_LDOCTRL 0x2f +#define AW8695_REG_DBGSTAT 0x30 +#define AW8695_REG_BSTDBG1 0x31 +#define AW8695_REG_BSTDBG2 0x32 +#define AW8695_REG_BSTDBG3 0x33 +#define AW8695_REG_BSTCFG 0x34 +#define AW8695_REG_ANADBG 0x35 +#define AW8695_REG_ANACTRL 0x36 +#define AW8695_REG_CPDBG 0x37 +#define AW8695_REG_GLBDBG 0x38 +#define AW8695_REG_DATDBG 0x39 +#define AW8695_REG_BSTDBG4 0x3a +#define AW8695_REG_BSTDBG5 0x3b +#define AW8695_REG_BSTDBG6 0x3c +#define AW8695_REG_HDRVDBG 0x3d +#define AW8695_REG_PRLVL 0x3e +#define AW8695_REG_PRTIME 0x3f +#define AW8695_REG_RAMADDRH 0x40 +#define AW8695_REG_RAMADDRL 0x41 +#define AW8695_REG_RAMDATA 0x42 +#define AW8695_REG_GLB_STATE 0x46 +#define AW8695_REG_BST_AUTO 0x47 +#define AW8695_REG_CONT_CTRL 0x48 +#define AW8695_REG_F_PRE_H 0x49 +#define AW8695_REG_F_PRE_L 0x4a +#define AW8695_REG_TD_H 0x4b +#define AW8695_REG_TD_L 0x4c +#define AW8695_REG_TSET 0x4d +#define AW8695_REG_TRIM_LRA 0x5b +#define AW8695_REG_R_SPARE 0x5d +#define AW8695_REG_D2SCFG 0x5e +#define AW8695_REG_DETCTRL 0x5f +#define AW8695_REG_RLDET 0x60 +#define AW8695_REG_OSDET 0x61 +#define AW8695_REG_VBATDET 0x62 +#define AW8695_REG_TESTDET 0x63 +#define AW8695_REG_DETLO 0x64 +#define AW8695_REG_BEMFDBG 0x65 +#define AW8695_REG_ADCTEST 0x66 +#define AW8695_REG_BEMFTEST 0x67 +#define AW8695_REG_F_LRA_F0_H 0x68 +#define AW8695_REG_F_LRA_F0_L 0x69 +#define AW8695_REG_F_LRA_CONT_H 0x6a +#define AW8695_REG_F_LRA_CONT_L 0x6b +#define AW8695_REG_WAIT_VOL_MP 0x6d +#define AW8695_REG_WAIT_VOL_MN 0x6f +#define AW8695_REG_BEMF_VOL_H 0x70 +#define AW8695_REG_BEMF_VOL_L 0x71 +#define AW8695_REG_ZC_THRSH_H 0x72 +#define AW8695_REG_ZC_THRSH_L 0x73 +#define AW8695_REG_BEMF_VTHH_H 0x74 +#define AW8695_REG_BEMF_VTHH_L 0x75 +#define AW8695_REG_BEMF_VTHL_H 0x76 +#define AW8695_REG_BEMF_VTHL_L 0x77 +#define AW8695_REG_BEMF_NUM 0x78 +#define AW8695_REG_DRV_TIME 0x79 +#define AW8695_REG_TIME_NZC 0x7a +#define AW8695_REG_DRV_LVL 0x7b +#define AW8695_REG_DRV_LVL_OV 0x7c +#define AW8695_REG_NUM_F0_1 0x7d +#define AW8695_REG_NUM_F0_2 0x7e +#define AW8695_REG_NUM_F0_3 0x7f + + + + +/******************************************** + * Register Access + *******************************************/ +#define REG_NONE_ACCESS 0 +#define REG_RD_ACCESS 1 << 0 +#define REG_WR_ACCESS 1 << 1 + +const unsigned char aw8695_reg_access[AW8695_REG_MAX] = { + [AW8695_REG_ID ] = REG_RD_ACCESS, + [AW8695_REG_SYSST ] = REG_RD_ACCESS, + [AW8695_REG_SYSINT ] = REG_RD_ACCESS, + [AW8695_REG_SYSINTM ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_SYSCTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_GO ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_RTP_DATA ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_WAVSEQ1 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_WAVSEQ2 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_WAVSEQ3 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_WAVSEQ4 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_WAVSEQ5 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_WAVSEQ6 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_WAVSEQ7 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_WAVSEQ8 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_WAVLOOP1 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_WAVLOOP2 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_WAVLOOP3 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_WAVLOOP4 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_MAIN_LOOP ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_TRG1_WAV_P ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_TRG2_WAV_P ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_TRG3_WAV_P ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_TRG1_WAV_N ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_TRG2_WAV_N ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_TRG3_WAV_N ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_TRG_PRIO ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_TRG_CFG1 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_TRG_CFG2 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_DBGCTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_BASE_ADDRH ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_BASE_ADDRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_FIFO_AEH ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_FIFO_AEL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_FIFO_AFH ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_FIFO_AFL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_WAKE_DLY ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_START_DLY ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_END_DLY_H ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_END_DLY_L ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_DATCTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_PWMDEL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_PWMPRC ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_PWMDBG ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_LDOCTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_DBGSTAT ] = REG_RD_ACCESS, + [AW8695_REG_BSTDBG1 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_BSTDBG2 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_BSTDBG3 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_BSTCFG ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_ANADBG ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_ANACTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_CPDBG ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_GLBDBG ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_DATDBG ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_BSTDBG4 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_BSTDBG5 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_BSTDBG6 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_HDRVDBG ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_PRLVL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_PRTIME ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_RAMADDRH ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_RAMADDRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_RAMDATA ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_GLB_STATE ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_BST_AUTO ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_CONT_CTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_F_PRE_H ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_F_PRE_L ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_TD_H ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_TD_L ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_TSET ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_TRIM_LRA ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_R_SPARE ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_D2SCFG ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_DETCTRL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_RLDET ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_OSDET ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_VBATDET ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_TESTDET ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_DETLO ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_BEMFDBG ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_ADCTEST ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_BEMFTEST ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_F_LRA_F0_H ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_F_LRA_F0_L ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_F_LRA_CONT_H] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_F_LRA_CONT_L] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_WAIT_VOL_MP ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_WAIT_VOL_MN ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_BEMF_VOL_H ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_BEMF_VOL_L ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_ZC_THRSH_H ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_ZC_THRSH_L ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_BEMF_VTHH_H ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_BEMF_VTHH_L ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_BEMF_VTHL_H ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_BEMF_VTHL_L ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_BEMF_NUM ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_DRV_TIME ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_TIME_NZC ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_DRV_LVL ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_DRV_LVL_OV ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_NUM_F0_1 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_NUM_F0_2 ] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8695_REG_NUM_F0_3 ] = REG_RD_ACCESS | REG_WR_ACCESS, +}; + + +/****************************************************** + * Register Detail + *****************************************************/ +/* + * SYSST: reg0x01 + */ +#define AW8695_BIT_SYSST_BSTERRS (1<<7) +#define AW8695_BIT_SYSST_OVS (1<<6) +#define AW8695_BIT_SYSST_UVLS (1<<5) +#define AW8695_BIT_SYSST_FF_AES (1<<4) +#define AW8695_BIT_SYSST_FF_AFS (1<<3) +#define AW8695_BIT_SYSST_OCDS (1<<2) +#define AW8695_BIT_SYSST_OTS (1<<1) +#define AW8695_BIT_SYSST_DONES (1<<0) + +/* + * SYSINT: reg0x02 + */ +#define AW8695_BIT_SYSINT_BSTERRI (1<<7) +#define AW8695_BIT_SYSINT_OVI (1<<6) +#define AW8695_BIT_SYSINT_UVLI (1<<5) +#define AW8695_BIT_SYSINT_FF_AEI (1<<4) +#define AW8695_BIT_SYSINT_FF_AFI (1<<3) +#define AW8695_BIT_SYSINT_OCDI (1<<2) +#define AW8695_BIT_SYSINT_OTI (1<<1) +#define AW8695_BIT_SYSINT_DONEI (1<<0) + +/* + * SYSINTM: reg0x03 + */ +#define AW8695_BIT_SYSINTM_BSTERR_MASK (~(1<<7)) +#define AW8695_BIT_SYSINTM_BSTERR_OFF (1<<7) +#define AW8695_BIT_SYSINTM_BSTERR_EN (0<<7) +#define AW8695_BIT_SYSINTM_OV_MASK (~(1<<6)) +#define AW8695_BIT_SYSINTM_OV_OFF (1<<6) +#define AW8695_BIT_SYSINTM_OV_EN (0<<6) +#define AW8695_BIT_SYSINTM_UVLO_MASK (~(1<<5)) +#define AW8695_BIT_SYSINTM_UVLO_OFF (1<<5) +#define AW8695_BIT_SYSINTM_UVLO_EN (0<<5) +#define AW8695_BIT_SYSINTM_FF_AE_MASK (~(1<<4)) +#define AW8695_BIT_SYSINTM_FF_AE_OFF (1<<4) +#define AW8695_BIT_SYSINTM_FF_AE_EN (0<<4) +#define AW8695_BIT_SYSINTM_FF_AF_MASK (~(1<<3)) +#define AW8695_BIT_SYSINTM_FF_AF_OFF (1<<3) +#define AW8695_BIT_SYSINTM_FF_AF_EN (0<<3) +#define AW8695_BIT_SYSINTM_OCD_MASK (~(1<<2)) +#define AW8695_BIT_SYSINTM_OCD_OFF (1<<2) +#define AW8695_BIT_SYSINTM_OCD_EN (0<<2) +#define AW8695_BIT_SYSINTM_OT_MASK (~(1<<1)) +#define AW8695_BIT_SYSINTM_OT_OFF (1<<1) +#define AW8695_BIT_SYSINTM_OT_EN (0<<1) +#define AW8695_BIT_SYSINTM_DONE_MASK (~(1<<0)) +#define AW8695_BIT_SYSINTM_DONE_OFF (1<<0) +#define AW8695_BIT_SYSINTM_DONE_EN (0<<0) + +/* + * SYSCTRL: reg0x04 + */ +#define AW8695_BIT_SYSCTRL_WAVDAT_MODE_MASK (~(3<<6)) +#define AW8695_BIT_SYSCTRL_WAVDAT_MODE_4X (3<<6) +#define AW8695_BIT_SYSCTRL_WAVDAT_MODE_2X (0<<6) +#define AW8695_BIT_SYSCTRL_WAVDAT_MODE_1X (1<<6) +#define AW8695_BIT_SYSCTRL_RAMINIT_MASK (~(1<<5)) +#define AW8695_BIT_SYSCTRL_RAMINIT_EN (1<<5) +#define AW8695_BIT_SYSCTRL_RAMINIT_OFF (0<<5) +#define AW8695_BIT_SYSCTRL_PLAY_MODE_MASK (~(3<<2)) +#define AW8695_BIT_SYSCTRL_PLAY_MODE_CONT (2<<2) +#define AW8695_BIT_SYSCTRL_PLAY_MODE_RTP (1<<2) +#define AW8695_BIT_SYSCTRL_PLAY_MODE_RAM (0<<2) +#define AW8695_BIT_SYSCTRL_BST_MODE_MASK (~(1<<1)) +#define AW8695_BIT_SYSCTRL_BST_MODE_BOOST (1<<1) +#define AW8695_BIT_SYSCTRL_BST_MODE_BYPASS (0<<1) +#define AW8695_BIT_SYSCTRL_WORK_MODE_MASK (~(1<<0)) +#define AW8695_BIT_SYSCTRL_STANDBY (1<<0) +#define AW8695_BIT_SYSCTRL_ACTIVE (0<<0) + +/* + * GO: reg0x05 + */ +#define AW8695_BIT_GO_MASK (~(1<<0)) +#define AW8695_BIT_GO_ENABLE (1<<0) +#define AW8695_BIT_GO_DISABLE (0<<0) + +/* + * WAVSEQ1: reg0x07 + */ +#define AW8695_BIT_WAVSEQ1_WAIT (1<<7) +#define AW8695_BIT_WAVSEQ1_WAV_FRM_SEQ1_MASK (~(127<<0)) + +/* + * WAVSEQ2: reg0x08 + */ +#define AW8695_BIT_WAVSEQ2_WAIT (1<<7) +#define AW8695_BIT_WAVSEQ2_WAV_FRM_SEQ2_MASK (~(127<<0)) + +/* + * WAVSEQ3: reg0x09 + */ +#define AW8695_BIT_WAVSEQ3_WAIT (1<<7) +#define AW8695_BIT_WAVSEQ3_WAV_FRM_SEQ3_MASK (~(127<<0)) + +/* + * WAVSEQ4: reg0x0a + */ +#define AW8695_BIT_WAVSEQ4_WAIT (1<<7) +#define AW8695_BIT_WAVSEQ4_WAV_FRM_SEQ4_MASK (~(127<<0)) + +/* + * WAVSEQ5: reg0x0b + */ +#define AW8695_BIT_WAVSEQ5_WAIT (1<<7) +#define AW8695_BIT_WAVSEQ5_WAV_FRM_SEQ5_MASK (~(127<<0)) + +/* + * WAVSEQ6: reg0x0c + */ +#define AW8695_BIT_WAVSEQ6_WAIT (1<<7) +#define AW8695_BIT_WAVSEQ6_WAV_FRM_SEQ6_MASK (~(127<<0)) + +/* + * WAVSEQ7: reg0x0d + */ +#define AW8695_BIT_WAVSEQ7_WAIT (1<<7) +#define AW8695_BIT_WAVSEQ7_WAV_FRM_SEQ7_MASK (~(127<<0)) + +/* + * WAVSEQ8: reg0x0e + */ +#define AW8695_BIT_WAVSEQ8_WAIT (1<<7) +#define AW8695_BIT_WAVSEQ8_WAV_FRM_SEQ8_MASK (~(127<<0)) + +/* + * WAVLOOP: + */ +#define AW8695_BIT_WAVLOOP_SEQN_MASK (~(15<<4)) +#define AW8695_BIT_WAVLOOP_SEQNP1_MASK (~(15<<0)) +#define AW8695_BIT_WAVLOOP_INIFINITELY (15<<0) + +/* + * WAVLOOP1: reg0x0f + */ +#define AW8695_BIT_WAVLOOP1_SEQ1_MASK (~(15<<4)) +#define AW8695_BIT_WAVLOOP1_SEQ2_MASK (~(15<<0)) + +/* + * WAVLOOP2: reg0x10 + */ +#define AW8695_BIT_WAVLOOP2_SEQ3_MASK (~(15<<4)) +#define AW8695_BIT_WAVLOOP2_SEQ4_MASK (~(15<<0)) + +/* + * WAVLOOP3: reg0x11 + */ +#define AW8695_BIT_WAVLOOP3_SEQ5_MASK (~(15<<4)) +#define AW8695_BIT_WAVLOOP3_SEQ6_MASK (~(15<<0)) + +/* + * WAVLOOP4: reg0x12 + */ +#define AW8695_BIT_WAVLOOP4_SEQ7_MASK (~(15<<4)) +#define AW8695_BIT_WAVLOOP4_SEQ8_MASK (~(15<<0)) + + +/* + * PLAYPRIO: reg0x1a + */ +#define AW8695_BIT_PLAYPRIO_GO_MASK (~(3<<6)) +#define AW8695_BIT_PLAYPRIO_TRIG3_MASK (~(3<<4)) +#define AW8695_BIT_PLAYPRIO_TRIG2_MASK (~(3<<2)) +#define AW8695_BIT_PLAYPRIO_TRIG1_MASK (~(3<<0)) + +/* + * TRGCFG1: reg0x1b + */ +#define AW8695_BIT_TRGCFG1_TRG3_POLAR_MASK (~(1<<5)) +#define AW8695_BIT_TRGCFG1_TRG3_POLAR_NEG (1<<5) +#define AW8695_BIT_TRGCFG1_TRG3_POLAR_POS (0<<5) +#define AW8695_BIT_TRGCFG1_TRG3_EDGE_MASK (~(1<<4)) +#define AW8695_BIT_TRGCFG1_TRG3_EDGE_POS (1<<4) +#define AW8695_BIT_TRGCFG1_TRG3_EDGE_POS_NEG (0<<4) +#define AW8695_BIT_TRGCFG1_TRG2_POLAR_MASK (~(1<<3)) +#define AW8695_BIT_TRGCFG1_TRG2_POLAR_NEG (1<<3) +#define AW8695_BIT_TRGCFG1_TRG2_POLAR_POS (0<<3) +#define AW8695_BIT_TRGCFG1_TRG2_EDGE_MASK (~(1<<2)) +#define AW8695_BIT_TRGCFG1_TRG2_EDGE_POS (1<<2) +#define AW8695_BIT_TRGCFG1_TRG2_EDGE_POS_NEG (0<<2) +#define AW8695_BIT_TRGCFG1_TRG1_POLAR_MASK (~(1<<1)) +#define AW8695_BIT_TRGCFG1_TRG1_POLAR_NEG (1<<1) +#define AW8695_BIT_TRGCFG1_TRG1_POLAR_POS (0<<1) +#define AW8695_BIT_TRGCFG1_TRG1_EDGE_MASK (~(1<<0)) +#define AW8695_BIT_TRGCFG1_TRG1_EDGE_POS (1<<0) +#define AW8695_BIT_TRGCFG1_TRG1_EDGE_POS_NEG (0<<0) + +/* + * TRGCFG2: reg0x1c + */ +#define AW8695_BIT_TRGCFG2_TRG3_ENABLE_MASK (~(1<<2)) +#define AW8695_BIT_TRGCFG2_TRG3_ENABLE (1<<2) +#define AW8695_BIT_TRGCFG2_TRG3_DISABLE (0<<2) +#define AW8695_BIT_TRGCFG2_TRG2_ENABLE_MASK (~(1<<1)) +#define AW8695_BIT_TRGCFG2_TRG2_ENABLE (1<<1) +#define AW8695_BIT_TRGCFG2_TRG2_DISABLE (0<<1) +#define AW8695_BIT_TRGCFG2_TRG1_ENABLE_MASK (~(1<<0)) +#define AW8695_BIT_TRGCFG2_TRG1_ENABLE (1<<0) +#define AW8695_BIT_TRGCFG2_TRG1_DISABLE (0<<0) + +/* + * DBGCTRL: reg0x20 + */ +#define AW8695_BIT_DBGCTRL_INT_EDGE_MODE_MASK (~(1<<3)) +#define AW8695_BIT_DBGCTRL_INT_EDGE_MODE_POS (1<<3) +#define AW8695_BIT_DBGCTRL_INT_EDGE_MODE_BOTH (0<<3) +#define AW8695_BIT_DBGCTRL_INT_MODE_MASK (~(1<<2)) +#define AW8695_BIT_DBGCTRL_INT_MODE_EDGE (1<<2) +#define AW8695_BIT_DBGCTRL_INT_MODE_LEVEL (0<<2) + +/* + * DATCTRL: reg0x2b + */ +#define AW8695_BIT_DATCTRL_FC_MASK (~(1<<6)) +#define AW8695_BIT_DATCTRL_FC_1000HZ (3<<6) +#define AW8695_BIT_DATCTRL_FC_800HZ (3<<6) +#define AW8695_BIT_DATCTRL_FC_600HZ (1<<6) +#define AW8695_BIT_DATCTRL_FC_400HZ (0<<6) +#define AW8695_BIT_DATCTRL_LPF_ENABLE_MASK (~(1<<5)) +#define AW8695_BIT_DATCTRL_LPF_ENABLE (1<<5) +#define AW8695_BIT_DATCTRL_LPF_DISABLE (0<<5) +#define AW8695_BIT_DATCTRL_WAKEMODE_ENABLE_MASK (~(1<<0)) +#define AW8695_BIT_DATCTRL_WAKEMODE_ENABLE (1<<0) +#define AW8695_BIT_DATCTRL_WAKEMODE_DISABLE (0<<0) + +/* + * PWMPRC: reg0x2d + */ +#define AW8695_BIT_PWMPRC_PRC_MASK (~(1<<7)) +#define AW8695_BIT_PWMPRC_PRC_ENABLE (1<<7) +#define AW8695_BIT_PWMPRC_PRC_DISABLE (0<<7) +#define AW8695_BIT_PWMPRC_PRCTIME_MASK (~(0x7f<<0)) + +/* + * PWMDBG: reg0x2e + */ +#define AW8695_BIT_PWMDBG_PWM_MODE_MASK (~(3<<5)) +#define AW8695_BIT_PWMDBG_PWM_12K (3<<5) +#define AW8695_BIT_PWMDBG_PWM_24K (2<<5) +#define AW8695_BIT_PWMDBG_PWM_48K (0<<5) + +/* + * BSTCFG: reg0x34 + */ +#define AW8695_BIT_BSTCFG_PEAKCUR_MASK (~(7<<0)) +#define AW8695_BIT_BSTCFG_PEAKCUR_4A (7<<0) +#define AW8695_BIT_BSTCFG_PEAKCUR_3P75A (6<<0) +#define AW8695_BIT_BSTCFG_PEAKCUR_3P5A (5<<0) +#define AW8695_BIT_BSTCFG_PEAKCUR_3P25A (4<<0) +#define AW8695_BIT_BSTCFG_PEAKCUR_3A (3<<0) +#define AW8695_BIT_BSTCFG_PEAKCUR_2P5A (2<<0) +#define AW8695_BIT_BSTCFG_PEAKCUR_2A (1<<0) +#define AW8695_BIT_BSTCFG_PEAKCUR_1P5A (0<<0) + +/* + * ANADBG: reg0x35 + */ +#define AW8695_BIT_ANADBG_IOC_MASK (~(3<<2)) +#define AW8695_BIT_ANADBG_IOC_4P65A (3<<2) +#define AW8695_BIT_ANADBG_IOC_4P15A (2<<2) +#define AW8695_BIT_ANADBG_IOC_3P65A (1<<2) +#define AW8695_BIT_ANADBG_IOC_3P15A (0<<2) + +/* + * ANACTRL: reg0x36 + */ +#define AW8695_BIT_ANACTRL_LRA_SRC_MASK (~(1<<5)) +#define AW8695_BIT_ANACTRL_LRA_SRC_REG (1<<5) +#define AW8695_BIT_ANACTRL_LRA_SRC_EFUSE (0<<5) +#define AW8695_BIT_ANACTRL_HD_PD_MASK (~(1<<3)) +#define AW8695_BIT_ANACTRL_HD_PD_EN (1<<3) +#define AW8695_BIT_ANACTRL_HD_HZ_EN (0<<3) + +/* + * BSTDBG4: reg0x3a + */ +#define AW8695_BIT_BSTDBG4_BSTVOL_MASK (~(31<<1)) + +/* + * PRLVL: reg0x3e + */ +#define AW8695_BIT_PRLVL_PR_MASK (~(1<<7)) +#define AW8695_BIT_PRLVL_PR_ENABLE (1<<7) +#define AW8695_BIT_PRLVL_PR_DISABLE (0<<7) +#define AW8695_BIT_PRLVL_PRLVL_MASK (~(0x7f<<0)) + +/* + *PRTIME: reg0x3f + */ +#define AW8695_BIT_PRTIME_PRTIME_MASK (~(0xff<<0)) + +/* + * BST_AUTO: reg0x47 + */ +#define AW8695_BIT_BST_AUTO_BST_AUTOSW_MASK (~(1<<2)) +#define AW8695_BIT_BST_AUTO_BST_AUTOMATIC_BOOST (1<<2) +#define AW8695_BIT_BST_AUTO_BST_MANUAL_BOOST (0<<2) +#define AW8695_BIT_BST_AUTO_BST_RTP_MASK (~(1<<1)) +#define AW8695_BIT_BST_AUTO_BST_RTP_ENABLE (1<<1) +#define AW8695_BIT_BST_AUTO_BST_RTP_DISABLE (0<<1) +#define AW8695_BIT_BST_AUTO_BST_RAM_MASK (~(1<<0)) +#define AW8695_BIT_BST_AUTO_BST_RAM_ENABLE (1<<0) +#define AW8695_BIT_BST_AUTO_BST_RAM_DISABLE (0<<0) + +/* + * CONT_CTRL: reg0x48 + */ +#define AW8695_BIT_CONT_CTRL_ZC_DETEC_MASK (~(1<<7)) +#define AW8695_BIT_CONT_CTRL_ZC_DETEC_ENABLE (1<<7) +#define AW8695_BIT_CONT_CTRL_ZC_DETEC_DISABLE (0<<7) +#define AW8695_BIT_CONT_CTRL_WAIT_PERIOD_MASK (~(3<<5)) +#define AW8695_BIT_CONT_CTRL_WAIT_8PERIOD (3<<5) +#define AW8695_BIT_CONT_CTRL_WAIT_4PERIOD (2<<5) +#define AW8695_BIT_CONT_CTRL_WAIT_2PERIOD (1<<5) +#define AW8695_BIT_CONT_CTRL_WAIT_1PERIOD (0<<5) +#define AW8695_BIT_CONT_CTRL_MODE_MASK (~(1<<4)) +#define AW8695_BIT_CONT_CTRL_BY_DRV_TIME (1<<4) +#define AW8695_BIT_CONT_CTRL_BY_GO_SIGNAL (0<<4) +#define AW8695_BIT_CONT_CTRL_EN_CLOSE_MASK (~(1<<3)) +#define AW8695_BIT_CONT_CTRL_CLOSE_PLAYBACK (1<<3) +#define AW8695_BIT_CONT_CTRL_OPEN_PLAYBACK (0<<3) +#define AW8695_BIT_CONT_CTRL_F0_DETECT_MASK (~(1<<2)) +#define AW8695_BIT_CONT_CTRL_F0_DETECT_ENABLE (1<<2) +#define AW8695_BIT_CONT_CTRL_F0_DETECT_DISABLE (0<<2) +#define AW8695_BIT_CONT_CTRL_O2C_MASK (~(1<<1)) +#define AW8695_BIT_CONT_CTRL_O2C_ENABLE (1<<1) +#define AW8695_BIT_CONT_CTRL_O2C_DISABLE (0<<1) +#define AW8695_BIT_CONT_CTRL_AUTO_BRK_MASK (~(1<<0)) +#define AW8695_BIT_CONT_CTRL_AUTO_BRK_ENABLE (1<<0) +#define AW8695_BIT_CONT_CTRL_AUTO_BRK_DISABLE (0<<0) + +/* + * D2SCFG: reg0x5e + */ +#define AW8695_BIT_D2SCFG_CLK_ADC_MASK (~(7<<5)) +#define AW8695_BIT_D2SCFG_CLK_ASC_0P09375MHZ (7<<5) +#define AW8695_BIT_D2SCFG_CLK_ASC_0P1875MHZ (6<<5) +#define AW8695_BIT_D2SCFG_CLK_ASC_0P375MHZ (5<<5) +#define AW8695_BIT_D2SCFG_CLK_ASC_0P75MHZ (4<<5) +#define AW8695_BIT_D2SCFG_CLK_ASC_1P5MHZ (3<<5) +#define AW8695_BIT_D2SCFG_CLK_ASC_3MHZ (2<<5) +#define AW8695_BIT_D2SCFG_CLK_ASC_6MHZ (1<<5) +#define AW8695_BIT_D2SCFG_CLK_ASC_12MHZ (0<<5) + +/* + * DETCTRL: reg0x5f + */ +#define AW8695_BIT_DETCTRL_RL_OS_MASK (~(1<<6)) +#define AW8695_BIT_DETCTRL_RL_DETECT (1<<6) +#define AW8695_BIT_DETCTRL_OS_DETECT (0<<6) +#define AW8695_BIT_DETCTRL_PROTECT_MASK (~(1<<5)) +#define AW8695_BIT_DETCTRL_PROTECT_NO_ACTION (1<<5) +#define AW8695_BIT_DETCTRL_PROTECT_SHUTDOWN (0<<5) +#define AW8695_BIT_DETCTRL_ADO_SLOT_MODE_MASK (~(1<<4)) +#define AW8695_BIT_DETCTRL_ADO_SLOT_MODE_ENABLE (1<<4) +#define AW8695_BIT_DETCTRL_ADO_SLOT_MODE_DISABLE (0<<4) +#define AW8695_BIT_DETCTRL_VBAT_GO_MASK (~(1<<1)) +#define AW8695_BIT_DETCTRL_VABT_GO_ENABLE (1<<1) +#define AW8695_BIT_DETCTRL_VBAT_GO_DISBALE (0<<1) +#define AW8695_BIT_DETCTRL_DIAG_GO_MASK (~(1<<0)) +#define AW8695_BIT_DETCTRL_DIAG_GO_ENABLE (1<<0) +#define AW8695_BIT_DETCTRL_DIAG_GO_DISABLE (0<<0) + + +/* + * ADCTEST: reg0x66 + */ +#define AW8695_BIT_ADCTEST_VBAT_MODE_MASK (~(1<<6)) +#define AW8695_BIT_ADCTEST_VBAT_HW_COMP (1<<6) +#define AW8695_BIT_ADCTEST_VBAT_SW_COMP (0<<6) + +/* + * BEMF_NUM: reg0x78 + */ +#define AW8695_BIT_BEMF_NUM_BRK_MASK (~(15<<0)) + + + +#endif diff --git a/drivers/misc/awinic/aw8697/Android.mk b/drivers/misc/awinic/aw8697/Android.mk new file mode 100644 index 000000000000..6579c2c49451 --- /dev/null +++ b/drivers/misc/awinic/aw8697/Android.mk @@ -0,0 +1,18 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := aw8697.ko +LOCAL_MODULE_TAGS := optional + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +ifeq ($(PRODUCT_HAS_AWINIC_HAPTIC),true) + KERNEL_CFLAGS += CONFIG_INPUT_AWINIC_HAPTIC=y +endif + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/awinic/aw8697/Kbuild b/drivers/misc/awinic/aw8697/Kbuild new file mode 100644 index 000000000000..4cb9b63437aa --- /dev/null +++ b/drivers/misc/awinic/aw8697/Kbuild @@ -0,0 +1,13 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +ifneq ($(filter m y,$(CONFIG_INPUT_AWINIC_HAPTIC)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_AWINIC_HAPTIC +endif + +ifneq (,$(filter smith%,$(TARGET_PRODUCT))) + EXTRA_CFLAGS += -DCONFIG_AW8697_BOOT_VIBRATION +endif + +obj-m += aw8697.o diff --git a/drivers/misc/awinic/aw8697/Kconfig b/drivers/misc/awinic/aw8697/Kconfig new file mode 100644 index 000000000000..ff234e746c63 --- /dev/null +++ b/drivers/misc/awinic/aw8697/Kconfig @@ -0,0 +1,5 @@ +config AW8697_HAPTIC + tristate "Haptic driver for awinic aw8697 series" + depends on I2C + help + This option enables support for aw8697 series Haptic Driver. diff --git a/drivers/misc/awinic/aw8697/Makefile b/drivers/misc/awinic/aw8697/Makefile new file mode 100644 index 000000000000..123bea3f132d --- /dev/null +++ b/drivers/misc/awinic/aw8697/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/misc/awinic/aw8697/aw8697.c b/drivers/misc/awinic/aw8697/aw8697.c new file mode 100644 index 000000000000..cc86bf12932e --- /dev/null +++ b/drivers/misc/awinic/aw8697/aw8697.c @@ -0,0 +1,4897 @@ +/* + * aw8697.c aw8697 haptic module + * + * Version: v1.4.2 + * + * Copyright (c) 2018 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw8697_config.h" +#include "aw8697_reg.h" +#include "aw8697.h" + +/****************************************************** + * + * Marco + * + ******************************************************/ +#define AW8697_I2C_NAME "aw8697_haptic" +#define AW8697_HAPTIC_NAME "aw8697_haptic" + +#define AW8697_VERSION "v1.4.3" + +#define AWINIC_RAM_UPDATE_DELAY + +#define AW_I2C_RETRIES 2 +#define AW_I2C_RETRY_DELAY 2 +#define AW_READ_CHIPID_RETRIES 5 +#define AW_READ_CHIPID_RETRY_DELAY 2 +#define AW8697_MAX_DSP_START_TRY_COUNT 10 +#define AWINIC_READ_BIN_FLEXBALLY +#define AW8697_SEQ_NO_RTP_BASE 102 + +#define AW8697_SEQ_NO_RTP_REPEAT 100 +#define AW8697_SEQ_NO_RTP_STOP 120000 + +#define AW8697_REPEAT_RTP_PLAYING + +#define AW8697_MAX_FIRMWARE_LOAD_CNT 20 +#define OSC_CALIBRATION_T_LENGTH 5100000 +#define PM_QOS_VALUE_VB 400 +struct pm_qos_request pm_qos_req_vb; +/****************************************************** + * + * variable + * + ******************************************************/ +#define AW8697_RTP_NAME_MAX 64 +static char *aw8697_ram_name = "aw8697_haptic.bin"; +static char *aw8697_cali_name = "aw8697_osc_rtp_24K_5s.bin"; +static char aw8697_rtp_name[][AW8697_RTP_NAME_MAX] = { + {"aw8697_rtp.bin"}, + {"aw8697_rtp_Argo_Navis.bin"}, + {"aw8697_rtp_Attentive.bin"}, + {"aw8697_rtp_Awake.bin"}, + {"aw8697_rtp_Bird_Loop.bin"}, + {"aw8697_rtp_Brilliant_Times.bin"}, + {"aw8697_rtp_Chimey_Phone.bin"}, + {"aw8697_rtp_Complex.bin"}, + {"aw8697_rtp_Crazy_Dream.bin"}, + {"aw8697_rtp_Curve_Ball_Blend.bin"}, + {"aw8697_rtp_Digital_Phone.bin"}, + {"aw8697_rtp_Electrovision.bin"}, + {"aw8697_rtp_Ether_Shake.bin"}, + {"aw8697_rtp_Fateful_Words.bin"}, + {"aw8697_rtp_Flutey_Phone.bin"}, + {"aw8697_rtp_Future_Funk.bin"}, + {"aw8697_rtp_Future_Hi_Tech.bin"}, + {"aw8697_rtp_Girtab.bin"}, + {"aw8697_rtp_Hello.bin"}, + {"aw8697_rtp_Hexagon.bin"}, + {"aw8697_rtp_Hydra.bin"}, + {"aw8697_rtp_Insert_Coin.bin"}, + {"aw8697_rtp_Jumping_Dots.bin"}, + {"aw8697_rtp_Keys.bin"}, + {"aw8697_rtp_Loopy.bin"}, + {"aw8697_rtp_Loopy_Lounge.bin"}, + {"aw8697_rtp_Modular.bin"}, + {"aw8697_rtp_Momentum.bin"}, + {"aw8697_rtp_Morning.bin"}, + {"aw8697_rtp_Moto.bin"}, + {"aw8697_rtp_Natural.bin"}, + {"aw8697_rtp_New_Player.bin"}, + {"aw8697_rtp_Onward.bin"}, + {"aw8697_rtp_Organ_Dub.bin"}, + {"aw8697_rtp_Overclocked.bin"}, + {"aw8697_rtp_Pegasus.bin"}, + {"aw8697_rtp_Pyxis.bin"}, + {"aw8697_rtp_Regrade.bin"}, + {"aw8697_rtp_Scarabaeus.bin"}, + {"aw8697_rtp_Sceptrum.bin"}, + {"aw8697_rtp_Simple.bin"}, + {"aw8697_rtp_Solarium.bin"}, + {"aw8697_rtp_Sparse.bin"}, + {"aw8697_rtp_Terrabytes.bin"}, + {"aw8697_rtp_Zero_Hour.bin"}, + {"aw8697_rtp_Moto_Retro.bin"}, +}; + +struct aw8697_container *aw8697_rtp; +struct aw8697 *g_aw8697; + +/****************************************************** + * + * functions + * + ******************************************************/ +static void aw8697_interrupt_clear(struct aw8697 *aw8697); +static int aw8697_haptic_trig_enable_config(struct aw8697 *aw8697); +static void aw8697_vibrate(struct aw8697 *aw8697, int value); +static void aw8697_rtp_play(struct aw8697 *aw8697, int value); + +#undef AW8697_I2C_DEBUG + + /****************************************************** + * + * aw8697 i2c write/read + * + ******************************************************/ +static int aw8697_i2c_write(struct aw8697 *aw8697, + unsigned char reg_addr, unsigned char reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + while (cnt < AW_I2C_RETRIES) { + ret = + i2c_smbus_write_byte_data(aw8697->i2c, reg_addr, reg_data); + if (ret < 0) { + pr_err("%s: i2c_write cnt=%d error=%d\n", __func__, cnt, + ret); + } else { + break; + } + cnt++; + usleep_range(AW_I2C_RETRY_DELAY * 1000, + AW_I2C_RETRY_DELAY * 1000 + 500); + } +#ifdef AW8697_I2C_DEBUG + pr_info("aw8697 W: addr=%02X, data=%02X", + (0x000000ff & reg_addr), (0x000000ff & reg_data)); +#endif + return ret; +} + +static int aw8697_i2c_read(struct aw8697 *aw8697, + unsigned char reg_addr, unsigned char *reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + while (cnt < AW_I2C_RETRIES) { + ret = i2c_smbus_read_byte_data(aw8697->i2c, reg_addr); + if (ret < 0) { + pr_err("%s: i2c_read cnt=%d error=%d\n", __func__, cnt, + ret); + } else { + *reg_data = ret; + break; + } + cnt++; + usleep_range(AW_I2C_RETRY_DELAY * 1000, + AW_I2C_RETRY_DELAY * 1000 + 500); + } +#ifdef AW8697_I2C_DEBUG + pr_info("aw8697 R: addr=%02X, data=%02X", + (0x000000ff & reg_addr), (0x000000ff & *reg_data)); +#endif + return ret; +} + +static int aw8697_i2c_write_bits(struct aw8697 *aw8697, + unsigned char reg_addr, unsigned int mask, + unsigned char reg_data) +{ + unsigned char oval = 0; + unsigned char nval; + + aw8697_i2c_read(aw8697, reg_addr, &oval); + nval = oval; + nval &= mask; + nval |= reg_data; + if (oval != nval) + aw8697_i2c_write(aw8697, reg_addr, nval); + + return 0; +} + +static int aw8697_i2c_writes(struct aw8697 *aw8697, + unsigned char reg_addr, unsigned char *buf, + unsigned int len) +{ + int ret = -1; + unsigned char *data; + + data = kmalloc(len + 1, GFP_KERNEL); + if (data == NULL) { + pr_err("%s: can not allocate memory\n", __func__); + return -ENOMEM; + } +#ifdef AW8697_I2C_DEBUG + if (1) { + int i, blen = 0; + char buffer[256]; + unsigned char *val = buf; + + for (i = 0; i < len; i++, val++) { + blen += scnprintf(buffer+blen, 255-blen, "%02x", + (0x000000ff & *val)); + } + pr_info("aw8697 W: addr=%02X, data=%s", + (0x000000ff & reg_addr), buffer); + } +#endif + data[0] = reg_addr; + memcpy(&data[1], buf, len); + + ret = i2c_master_send(aw8697->i2c, data, len + 1); + if (ret < 0) + pr_err("%s: i2c master send error\n", __func__); + + kfree(data); + + return ret; +} + +/***************************************************** + * + * ram update + * + *****************************************************/ +static void aw8697_rtp_loaded(const struct firmware *cont, void *context) +{ + struct aw8697 *aw8697 = context; + + pr_info("%s enter\n", __func__); + + if (!cont) { + pr_err("%s: failed to read %s\n", __func__, + aw8697_rtp_name[aw8697->rtp_file_num]); + release_firmware(cont); + return; + } + + pr_info("%s: loaded %s - size: %zu\n", __func__, + aw8697_rtp_name[aw8697->rtp_file_num], cont ? cont->size : 0); + + /* aw8697 rtp update */ + mutex_lock(&aw8697->rtp_lock); + aw8697_rtp = vzalloc(cont->size + sizeof(int)); + if (!aw8697_rtp) { + release_firmware(cont); + mutex_unlock(&aw8697->rtp_lock); + pr_err("%s: Error allocating memory\n", __func__); + return; + } + aw8697_rtp->len = cont->size; + pr_info("%s: rtp size = %d\n", __func__, aw8697_rtp->len); + memcpy(aw8697_rtp->data, cont->data, cont->size); + release_firmware(cont); + mutex_unlock(&aw8697->rtp_lock); + + aw8697->rtp_init = 1; + pr_info("%s: rtp update complete\n", __func__); +} + +static int aw8697_rtp_update(struct aw8697 *aw8697) +{ + pr_info("%s enter\n", __func__); + + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw8697_rtp_name[aw8697->rtp_file_num], + aw8697->dev, GFP_KERNEL, aw8697, + aw8697_rtp_loaded); +} + +static void aw8697_container_update(struct aw8697 *aw8697, + struct aw8697_container *aw8697_cont) +{ + int i = 0; + unsigned int shift = 0; + + pr_info("%s enter\n", __func__); + + mutex_lock(&aw8697->lock); + + aw8697->ram.baseaddr_shift = 2; + aw8697->ram.ram_shift = 4; + + /* RAMINIT Enable */ + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_RAMINIT_MASK, + AW8697_BIT_SYSCTRL_RAMINIT_EN); + + /* base addr */ + shift = aw8697->ram.baseaddr_shift; + aw8697->ram.base_addr = + (unsigned int)((aw8697_cont->data[0 + shift] << 8) | + (aw8697_cont->data[1 + shift])); + pr_info("%s: base_addr=0x%4x\n", __func__, aw8697->ram.base_addr); + + aw8697_i2c_write(aw8697, AW8697_REG_BASE_ADDRH, + aw8697_cont->data[0 + shift]); + aw8697_i2c_write(aw8697, AW8697_REG_BASE_ADDRL, + aw8697_cont->data[1 + shift]); + /*1/2 FIFO */ + aw8697_i2c_write(aw8697, AW8697_REG_FIFO_AEH, + (unsigned char)((aw8697->ram.base_addr >> 1) >> 8)); + /*1/2 FIFO */ + aw8697_i2c_write(aw8697, AW8697_REG_FIFO_AEL, + (unsigned char)((aw8697->ram.base_addr >> 1) & 0x00FF)); + aw8697_i2c_write(aw8697, AW8697_REG_FIFO_AFH, + (unsigned + char)((aw8697->ram.base_addr - + (aw8697->ram.base_addr >> 2)) >> 8)); + aw8697_i2c_write(aw8697, AW8697_REG_FIFO_AFL, + (unsigned + char)((aw8697->ram.base_addr - + (aw8697->ram.base_addr >> 2)) & 0x00FF)); + + /* ram */ + shift = aw8697->ram.baseaddr_shift; + aw8697_i2c_write(aw8697, AW8697_REG_RAMADDRH, + aw8697_cont->data[0 + shift]); + aw8697_i2c_write(aw8697, AW8697_REG_RAMADDRL, + aw8697_cont->data[1 + shift]); + shift = aw8697->ram.ram_shift; + for (i = shift; i < aw8697_cont->len; i++) { + aw8697->ramupdate_flag = + aw8697_i2c_write(aw8697, AW8697_REG_RAMDATA, + aw8697_cont->data[i]); + } + +#if 0 + /* ram check */ + shift = aw8697->ram.baseaddr_shift; + aw8697_i2c_write(aw8697, AW8697_REG_RAMADDRH, + aw8697_cont->data[0 + shift]); + aw8697_i2c_write(aw8697, AW8697_REG_RAMADDRL, + aw8697_cont->data[1 + shift]); + shift = aw8697->ram.ram_shift; + for (i = shift; i < aw8697_cont->len; i++) { + aw8697_i2c_read(aw8697, AW8697_REG_RAMDATA, ®_val); + if (reg_val != aw8697_cont->data[i]) { + pr_err + ("%s: ram check error addr=0x%04x, file_data=0x%02x, ram_data=0x%02x\n", + __func__, i, aw8697_cont->data[i], reg_val); + return; + } + } +#endif + + /* RAMINIT Disable */ + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_RAMINIT_MASK, + AW8697_BIT_SYSCTRL_RAMINIT_OFF); + + mutex_unlock(&aw8697->lock); + + pr_info("%s exit\n", __func__); +} + +static void aw8697_ram_loaded(const struct firmware *cont, void *context) +{ + struct aw8697 *aw8697 = context; + struct aw8697_container *aw8697_fw; + int i = 0; + unsigned short check_sum = 0; + +#ifdef AWINIC_READ_BIN_FLEXBALLY + static unsigned char load_cont; + int ram_timer_val = 1000; + + load_cont++; +#endif + pr_info("%s enter\n", __func__); + + if (!cont) { + pr_err("%s: failed to read %s\n", __func__, aw8697_ram_name); + release_firmware(cont); +#ifdef AWINIC_READ_BIN_FLEXBALLY + if (load_cont <= 20) { + schedule_delayed_work(&aw8697->ram_work, + msecs_to_jiffies(ram_timer_val)); + pr_info("%s:start hrtimer:load_cont%d\n", __func__, + load_cont); + } +#endif + return; + } + + pr_info("%s: loaded %s - size: %zu\n", __func__, aw8697_ram_name, + cont ? cont->size : 0); +/* + for(i=0; isize; i++) { + pr_info("%s: addr:0x%04x, data:0x%02x\n", __func__, i, *(cont->data+i)); + } +*/ + + /* check sum */ + for (i = 2; i < cont->size; i++) + check_sum += cont->data[i]; + if (check_sum != + (unsigned short)((cont->data[0] << 8) | (cont->data[1]))) { + pr_err("%s: check sum err: check_sum=0x%04x\n", __func__, + check_sum); + return; + } else { + pr_info("%s: check sum pass : 0x%04x\n", __func__, check_sum); + aw8697->ram.check_sum = check_sum; + } + + /* aw8697 ram update */ + aw8697_fw = kzalloc(cont->size + sizeof(int), GFP_KERNEL); + if (!aw8697_fw) { + release_firmware(cont); + pr_err("%s: Error allocating memory\n", __func__); + return; + } + aw8697_fw->len = cont->size; + memcpy(aw8697_fw->data, cont->data, cont->size); + release_firmware(cont); + + aw8697_container_update(aw8697, aw8697_fw); + + aw8697->ram.len = aw8697_fw->len; + + kfree(aw8697_fw); + + aw8697->ram_init = 1; + pr_info("%s: fw update complete\n", __func__); + + aw8697_haptic_trig_enable_config(aw8697); + +#ifdef CONFIG_AW8697_BOOT_VIBRATION + aw8697_vibrate(aw8697, 1000); /* Boot-up vibration */ +#endif + + + aw8697_rtp_update(aw8697); +} + +static int aw8697_ram_update(struct aw8697 *aw8697) +{ + aw8697->ram_init = 0; + aw8697->rtp_init = 0; + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw8697_ram_name, aw8697->dev, GFP_KERNEL, + aw8697, aw8697_ram_loaded); +} + +#ifdef AWINIC_RAM_UPDATE_DELAY +static void aw8697_ram_work_routine(struct work_struct *work) +{ + struct aw8697 *aw8697 = + container_of(work, struct aw8697, ram_work.work); + + pr_info("%s enter\n", __func__); + + aw8697_ram_update(aw8697); + +} +#endif + +static int aw8697_ram_init(struct aw8697 *aw8697) +{ +#ifdef AWINIC_RAM_UPDATE_DELAY + int ram_timer_val = 5000; + + INIT_DELAYED_WORK(&aw8697->ram_work, aw8697_ram_work_routine); + schedule_delayed_work(&aw8697->ram_work, + msecs_to_jiffies(ram_timer_val)); +#else + aw8697_ram_update(aw8697); +#endif + return 0; +} + +/***************************************************** + * + * haptic control + * + *****************************************************/ +static int aw8697_haptic_softreset(struct aw8697 *aw8697) +{ + pr_debug("%s enter\n", __func__); + + aw8697_i2c_write(aw8697, AW8697_REG_ID, 0xAA); + usleep_range(3000, 3500); + return 0; +} + +static int aw8697_haptic_active(struct aw8697 *aw8697) +{ + pr_debug("%s enter\n", __func__); + + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_WORK_MODE_MASK, + AW8697_BIT_SYSCTRL_ACTIVE); + aw8697_interrupt_clear(aw8697); + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSINTM, + AW8697_BIT_SYSINTM_UVLO_MASK, + AW8697_BIT_SYSINTM_UVLO_EN); + return 0; +} + +static int aw8697_haptic_play_mode(struct aw8697 *aw8697, + unsigned char play_mode) +{ + pr_debug("%s enter\n", __func__); + pr_debug("%s: play_mode=%d\n", __func__, play_mode); + + switch (play_mode) { + case AW8697_HAPTIC_STANDBY_MODE: + aw8697->play_mode = AW8697_HAPTIC_STANDBY_MODE; + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSINTM, + AW8697_BIT_SYSINTM_UVLO_MASK, + AW8697_BIT_SYSINTM_UVLO_OFF); + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_WORK_MODE_MASK, + AW8697_BIT_SYSCTRL_STANDBY); + break; + case AW8697_HAPTIC_RAM_MODE: + aw8697->play_mode = AW8697_HAPTIC_RAM_MODE; + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8697_BIT_SYSCTRL_PLAY_MODE_RAM); + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_BST_MODE_MASK, + AW8697_BIT_SYSCTRL_BST_MODE_BYPASS); + if (aw8697->auto_boost) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_BST_AUTO, + AW8697_BIT_BST_AUTO_BST_RAM_MASK, + AW8697_BIT_BST_AUTO_BST_RAM_DISABLE); + } + aw8697_haptic_active(aw8697); + if (aw8697->auto_boost) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_BST_AUTO, + AW8697_BIT_BST_AUTO_BST_RAM_MASK, + AW8697_BIT_BST_AUTO_BST_RAM_ENABLE); + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_BST_MODE_MASK & + AW8697_BIT_SYSCTRL_WORK_MODE_MASK, + AW8697_BIT_SYSCTRL_BST_MODE_BOOST + | AW8697_BIT_SYSCTRL_STANDBY); + aw8697_haptic_active(aw8697); + } else { + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_BST_MODE_MASK, + AW8697_BIT_SYSCTRL_BST_MODE_BOOST); + } + usleep_range(2000, 2500); + break; + case AW8697_HAPTIC_RAM_LOOP_MODE: + aw8697->play_mode = AW8697_HAPTIC_RAM_LOOP_MODE; + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8697_BIT_SYSCTRL_PLAY_MODE_RAM); + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_BST_MODE_MASK, + AW8697_BIT_SYSCTRL_BST_MODE_BYPASS); + if (aw8697->auto_boost) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_BST_AUTO, + AW8697_BIT_BST_AUTO_BST_RAM_MASK, + AW8697_BIT_BST_AUTO_BST_RAM_DISABLE); + } + aw8697_haptic_active(aw8697); + break; + case AW8697_HAPTIC_RTP_MODE: + aw8697->play_mode = AW8697_HAPTIC_RTP_MODE; + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8697_BIT_SYSCTRL_PLAY_MODE_RTP); + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_BST_MODE_MASK, + AW8697_BIT_SYSCTRL_BST_MODE_BYPASS); + if (aw8697->auto_boost) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_BST_AUTO, + AW8697_BIT_BST_AUTO_BST_RAM_MASK, + AW8697_BIT_BST_AUTO_BST_RTP_DISABLE); + } + aw8697_haptic_active(aw8697); + if (aw8697->auto_boost) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_BST_AUTO, + AW8697_BIT_BST_AUTO_BST_RAM_MASK, + AW8697_BIT_BST_AUTO_BST_RTP_ENABLE); + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_BST_MODE_MASK & + AW8697_BIT_SYSCTRL_WORK_MODE_MASK, + AW8697_BIT_SYSCTRL_BST_MODE_BOOST + | AW8697_BIT_SYSCTRL_STANDBY); + aw8697_haptic_active(aw8697); + } else { + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_BST_MODE_MASK, + AW8697_BIT_SYSCTRL_BST_MODE_BOOST); + } + usleep_range(2000, 2500); + break; + case AW8697_HAPTIC_TRIG_MODE: + aw8697->play_mode = AW8697_HAPTIC_TRIG_MODE; + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8697_BIT_SYSCTRL_PLAY_MODE_RAM); + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_BST_MODE_MASK, + AW8697_BIT_SYSCTRL_BST_MODE_BYPASS); + if (aw8697->auto_boost) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_BST_AUTO, + AW8697_BIT_BST_AUTO_BST_RAM_MASK, + AW8697_BIT_BST_AUTO_BST_RAM_DISABLE); + } + aw8697_haptic_active(aw8697); + if (aw8697->auto_boost) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_BST_AUTO, + AW8697_BIT_BST_AUTO_BST_RAM_MASK, + AW8697_BIT_BST_AUTO_BST_RAM_ENABLE); + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_BST_MODE_MASK & + AW8697_BIT_SYSCTRL_WORK_MODE_MASK, + AW8697_BIT_SYSCTRL_BST_MODE_BOOST + | AW8697_BIT_SYSCTRL_STANDBY); + aw8697_haptic_active(aw8697); + } else { + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_BST_MODE_MASK, + AW8697_BIT_SYSCTRL_BST_MODE_BOOST); + } + usleep_range(2000, 2500); + break; + case AW8697_HAPTIC_CONT_MODE: + aw8697->play_mode = AW8697_HAPTIC_CONT_MODE; + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8697_BIT_SYSCTRL_PLAY_MODE_CONT); + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_BST_MODE_MASK, + AW8697_BIT_SYSCTRL_BST_MODE_BYPASS); + if (aw8697->auto_boost) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_BST_AUTO, + AW8697_BIT_BST_AUTO_BST_RAM_MASK, + AW8697_BIT_BST_AUTO_BST_RAM_DISABLE); + } + aw8697_haptic_active(aw8697); + break; + default: + dev_err(aw8697->dev, "%s: play mode %d err", + __func__, play_mode); + break; + } + return 0; +} + +static int aw8697_haptic_juge_RTP_is_going_on(struct aw8697 *aw8697) +{ + unsigned char reg_val = 0; + unsigned char rtp_state = 0; + static unsigned char pre_reg_val; + + aw8697_i2c_read(aw8697, AW8697_REG_SYSCTRL, ®_val); + if ((reg_val & AW8697_BIT_SYSCTRL_PLAY_MODE_RTP) && + (!(reg_val & AW8697_BIT_SYSCTRL_STANDBY))) + rtp_state = 1; /*is going on */ + if (pre_reg_val != reg_val) + pr_info("%s AW8697_REG_SYSCTRL 0x04==%02x rtp_state=%d\n", + __func__, reg_val, rtp_state); + pre_reg_val = reg_val; + if (aw8697->rtp_routine_on) { + pr_info("%s:rtp_routine_on\n", __func__); + rtp_state = 1; /*is going on */ + } + return rtp_state; +} + +#define MICROS_PER_SEC 1000000 + +static unsigned int timeval_diff(struct timeval *begin) +{ + struct timeval curtime; + unsigned int ds, dus = 0; + + do_gettimeofday(&curtime); + ds = curtime.tv_sec - begin->tv_sec; + if ((curtime.tv_usec - begin->tv_usec) < 0) { + ds -= 1; + dus = MICROS_PER_SEC; + } + dus += curtime.tv_usec - begin->tv_usec; + + return ds * MICROS_PER_SEC + dus; +} + +static int aw8697_haptic_play_go(struct aw8697 *aw8697, bool flag) +{ + unsigned int leftover_usec; + static struct timeval start; + + pr_debug("%s enter\n", __func__); + + if (flag == true) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_GO, + AW8697_BIT_GO_MASK, AW8697_BIT_GO_ENABLE); + do_gettimeofday(&start); + } else { + leftover_usec = timeval_diff(&start); + if (leftover_usec < 2000) { + pr_info("%s: time left %uus\n", __func__, leftover_usec); + usleep_range(2000, 2500); + } + aw8697_i2c_write_bits(aw8697, AW8697_REG_GO, + AW8697_BIT_GO_MASK, + AW8697_BIT_GO_DISABLE); + } + + return 0; +} + +static int aw8697_haptic_stop_delay(struct aw8697 *aw8697) +{ + unsigned char reg_val = 0; + unsigned int cnt = 100; + + while (cnt--) { + aw8697_i2c_read(aw8697, AW8697_REG_GLB_STATE, ®_val); + if ((reg_val & 0x0f) == 0x00) + return 0; + usleep_range(2000, 2500); + pr_debug("%s wait for standby, reg glb_state=0x%02x\n", + __func__, reg_val); + } + pr_err("%s do not enter standby automatically\n", __func__); + + return 0; +} + +static int aw8697_haptic_stop(struct aw8697 *aw8697) +{ + pr_debug("%s enter\n", __func__); + + aw8697_haptic_play_go(aw8697, false); + aw8697_haptic_stop_delay(aw8697); + aw8697_haptic_play_mode(aw8697, AW8697_HAPTIC_STANDBY_MODE); + + return 0; +} + +static int aw8697_haptic_start(struct aw8697 *aw8697) +{ + pr_debug("%s enter\n", __func__); + + aw8697_haptic_play_go(aw8697, true); + + return 0; +} + +static int aw8697_haptic_set_wav_seq(struct aw8697 *aw8697, + unsigned char wav, unsigned char seq) +{ + aw8697_i2c_write(aw8697, AW8697_REG_WAVSEQ1 + wav, seq); + return 0; +} + +static int aw8697_haptic_set_wav_loop(struct aw8697 *aw8697, + unsigned char wav, unsigned char loop) +{ + unsigned char tmp = 0; + + if (wav % 2) { + tmp = loop << 0; + aw8697_i2c_write_bits(aw8697, AW8697_REG_WAVLOOP1 + (wav / 2), + AW8697_BIT_WAVLOOP_SEQNP1_MASK, tmp); + } else { + tmp = loop << 4; + aw8697_i2c_write_bits(aw8697, AW8697_REG_WAVLOOP1 + (wav / 2), + AW8697_BIT_WAVLOOP_SEQN_MASK, tmp); + } + + return 0; +} + +/* +static int aw8697_haptic_set_main_loop(struct aw8697 *aw8697, + unsigned char loop) +{ + aw8697_i2c_write(aw8697, AW8697_REG_MAIN_LOOP, loop); + return 0; +} +*/ + +static int aw8697_haptic_set_repeat_wav_seq(struct aw8697 *aw8697, + unsigned char seq) +{ + aw8697_haptic_set_wav_seq(aw8697, 0x00, seq); + aw8697_haptic_set_wav_loop(aw8697, 0x00, + AW8697_BIT_WAVLOOP_INIFINITELY); + + return 0; +} + +static int aw8697_haptic_set_bst_vol(struct aw8697 *aw8697, + unsigned char bst_vol) +{ + if (bst_vol & 0xe0) + bst_vol = 0x1f; + aw8697_i2c_write_bits(aw8697, AW8697_REG_BSTDBG4, + AW8697_BIT_BSTDBG4_BSTVOL_MASK, (bst_vol << 1)); + return 0; +} + +static int aw8697_haptic_set_bst_peak_cur(struct aw8697 *aw8697, + unsigned char peak_cur) +{ + peak_cur &= AW8697_BSTCFG_PEAKCUR_LIMIT; + aw8697_i2c_write_bits(aw8697, AW8697_REG_BSTCFG, + AW8697_BIT_BSTCFG_PEAKCUR_MASK, peak_cur); + return 0; +} + +static int aw8697_haptic_set_gain(struct aw8697 *aw8697, unsigned char gain) +{ + aw8697_i2c_write(aw8697, AW8697_REG_DATDBG, gain); + return 0; +} + +static int aw8697_haptic_set_pwm(struct aw8697 *aw8697, unsigned char mode) +{ + switch (mode) { + case AW8697_PWM_48K: + aw8697_i2c_write_bits(aw8697, AW8697_REG_PWMDBG, + AW8697_BIT_PWMDBG_PWM_MODE_MASK, + AW8697_BIT_PWMDBG_PWM_48K); + break; + case AW8697_PWM_24K: + aw8697_i2c_write_bits(aw8697, AW8697_REG_PWMDBG, + AW8697_BIT_PWMDBG_PWM_MODE_MASK, + AW8697_BIT_PWMDBG_PWM_24K); + break; + case AW8697_PWM_12K: + aw8697_i2c_write_bits(aw8697, AW8697_REG_PWMDBG, + AW8697_BIT_PWMDBG_PWM_MODE_MASK, + AW8697_BIT_PWMDBG_PWM_12K); + break; + default: + break; + } + return 0; +} + +static int aw8697_haptic_play_wav_seq(struct aw8697 *aw8697, unsigned char flag) +{ + pr_debug("%s enter\n", __func__); + + if (flag) { + aw8697_haptic_play_mode(aw8697, AW8697_HAPTIC_RAM_MODE); + aw8697_haptic_start(aw8697); + } + return 0; +} + +static int aw8697_haptic_play_repeat_seq(struct aw8697 *aw8697, + unsigned char flag) +{ + pr_debug("%s enter\n", __func__); + + if (flag) { + aw8697_haptic_play_mode(aw8697, AW8697_HAPTIC_RAM_LOOP_MODE); + aw8697_haptic_start(aw8697); + } + + return 0; +} + +/***************************************************** + * + * motor protect + * + *****************************************************/ +static int aw8697_haptic_swicth_motorprotect_config(struct aw8697 *aw8697, + unsigned char addr, + unsigned char val) +{ + pr_debug("%s enter\n", __func__); + if (addr == 1) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_DETCTRL, + AW8697_BIT_DETCTRL_PROTECT_MASK, + AW8697_BIT_DETCTRL_PROTECT_SHUTDOWN); + aw8697_i2c_write_bits(aw8697, AW8697_REG_PWMPRC, + AW8697_BIT_PWMPRC_PRC_MASK, + AW8697_BIT_PWMPRC_PRC_ENABLE); + aw8697_i2c_write_bits(aw8697, AW8697_REG_PRLVL, + AW8697_BIT_PRLVL_PR_MASK, + AW8697_BIT_PRLVL_PR_ENABLE); + } else if (addr == 0) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_DETCTRL, + AW8697_BIT_DETCTRL_PROTECT_MASK, + AW8697_BIT_DETCTRL_PROTECT_NO_ACTION); + aw8697_i2c_write_bits(aw8697, AW8697_REG_PWMPRC, + AW8697_BIT_PWMPRC_PRC_MASK, + AW8697_BIT_PWMPRC_PRC_DISABLE); + aw8697_i2c_write_bits(aw8697, AW8697_REG_PRLVL, + AW8697_BIT_PRLVL_PR_MASK, + AW8697_BIT_PRLVL_PR_DISABLE); + } else if (addr == 0x2d) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_PWMPRC, + AW8697_BIT_PWMPRC_PRCTIME_MASK, val); + } else if (addr == 0x3e) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_PRLVL, + AW8697_BIT_PRLVL_PRLVL_MASK, val); + } else if (addr == 0x3f) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_PRTIME, + AW8697_BIT_PRTIME_PRTIME_MASK, val); + } else { + /*nothing to do; */ + } + return 0; +} + +/***************************************************** + * + * offset calibration + * + *****************************************************/ +static int aw8697_haptic_offset_calibration(struct aw8697 *aw8697) +{ + unsigned int cont = 2000; + unsigned char reg_val = 0; + + pr_debug("%s enter\n", __func__); + + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_RAMINIT_MASK, + AW8697_BIT_SYSCTRL_RAMINIT_EN); + aw8697_i2c_write_bits(aw8697, AW8697_REG_DETCTRL, + AW8697_BIT_DETCTRL_DIAG_GO_MASK, + AW8697_BIT_DETCTRL_DIAG_GO_ENABLE); + while (1) { + aw8697_i2c_read(aw8697, AW8697_REG_DETCTRL, ®_val); + if ((reg_val & 0x01) == 0 || cont == 0) + break; + cont--; + } + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_RAMINIT_MASK, + AW8697_BIT_SYSCTRL_RAMINIT_OFF); + + return 0; +} + +/***************************************************** + * + * trig config + * + *****************************************************/ +static int aw8697_haptic_trig_param_init(struct aw8697 *aw8697) +{ + pr_debug("%s enter\n", __func__); + + aw8697->trig[0].enable = AW8697_TRG1_ENABLE; + aw8697->trig[0].default_level = AW8697_TRG1_DEFAULT_LEVEL; + aw8697->trig[0].dual_edge = AW8697_TRG1_DUAL_EDGE; + aw8697->trig[0].frist_seq = AW8697_TRG1_FIRST_EDGE_SEQ; + aw8697->trig[0].second_seq = AW8697_TRG1_SECOND_EDGE_SEQ; + + aw8697->trig[1].enable = AW8697_TRG2_ENABLE; + aw8697->trig[1].default_level = AW8697_TRG2_DEFAULT_LEVEL; + aw8697->trig[1].dual_edge = AW8697_TRG2_DUAL_EDGE; + aw8697->trig[1].frist_seq = AW8697_TRG2_FIRST_EDGE_SEQ; + aw8697->trig[1].second_seq = AW8697_TRG2_SECOND_EDGE_SEQ; + + aw8697->trig[2].enable = AW8697_TRG3_ENABLE; + aw8697->trig[2].default_level = AW8697_TRG3_DEFAULT_LEVEL; + aw8697->trig[2].dual_edge = AW8697_TRG3_DUAL_EDGE; + aw8697->trig[2].frist_seq = AW8697_TRG3_FIRST_EDGE_SEQ; + aw8697->trig[2].second_seq = AW8697_TRG3_SECOND_EDGE_SEQ; + + return 0; +} + +static int aw8697_haptic_trig_param_config(struct aw8697 *aw8697) +{ + pr_debug("%s enter\n", __func__); + + if (aw8697->trig[0].default_level) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_TRG_CFG1, + AW8697_BIT_TRGCFG1_TRG1_POLAR_MASK, + AW8697_BIT_TRGCFG1_TRG1_POLAR_NEG); + } else { + aw8697_i2c_write_bits(aw8697, AW8697_REG_TRG_CFG1, + AW8697_BIT_TRGCFG1_TRG1_POLAR_MASK, + AW8697_BIT_TRGCFG1_TRG1_POLAR_POS); + } + if (aw8697->trig[1].default_level) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_TRG_CFG1, + AW8697_BIT_TRGCFG1_TRG2_POLAR_MASK, + AW8697_BIT_TRGCFG1_TRG2_POLAR_NEG); + } else { + aw8697_i2c_write_bits(aw8697, AW8697_REG_TRG_CFG1, + AW8697_BIT_TRGCFG1_TRG2_POLAR_MASK, + AW8697_BIT_TRGCFG1_TRG2_POLAR_POS); + } + if (aw8697->trig[2].default_level) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_TRG_CFG1, + AW8697_BIT_TRGCFG1_TRG3_POLAR_MASK, + AW8697_BIT_TRGCFG1_TRG3_POLAR_NEG); + } else { + aw8697_i2c_write_bits(aw8697, AW8697_REG_TRG_CFG1, + AW8697_BIT_TRGCFG1_TRG3_POLAR_MASK, + AW8697_BIT_TRGCFG1_TRG3_POLAR_POS); + } + + if (aw8697->trig[0].dual_edge) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_TRG_CFG1, + AW8697_BIT_TRGCFG1_TRG1_EDGE_MASK, + AW8697_BIT_TRGCFG1_TRG1_EDGE_POS_NEG); + } else { + aw8697_i2c_write_bits(aw8697, AW8697_REG_TRG_CFG1, + AW8697_BIT_TRGCFG1_TRG1_EDGE_MASK, + AW8697_BIT_TRGCFG1_TRG1_EDGE_POS); + } + if (aw8697->trig[1].dual_edge) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_TRG_CFG1, + AW8697_BIT_TRGCFG1_TRG2_EDGE_MASK, + AW8697_BIT_TRGCFG1_TRG2_EDGE_POS_NEG); + } else { + aw8697_i2c_write_bits(aw8697, AW8697_REG_TRG_CFG1, + AW8697_BIT_TRGCFG1_TRG2_EDGE_MASK, + AW8697_BIT_TRGCFG1_TRG2_EDGE_POS); + } + if (aw8697->trig[2].dual_edge) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_TRG_CFG1, + AW8697_BIT_TRGCFG1_TRG3_EDGE_MASK, + AW8697_BIT_TRGCFG1_TRG3_EDGE_POS_NEG); + } else { + aw8697_i2c_write_bits(aw8697, AW8697_REG_TRG_CFG1, + AW8697_BIT_TRGCFG1_TRG3_EDGE_MASK, + AW8697_BIT_TRGCFG1_TRG3_EDGE_POS); + } + + if (aw8697->trig[0].frist_seq) { + aw8697_i2c_write(aw8697, AW8697_REG_TRG1_WAV_P, + aw8697->trig[0].frist_seq); + } + if (aw8697->trig[0].second_seq && aw8697->trig[0].dual_edge) { + aw8697_i2c_write(aw8697, AW8697_REG_TRG1_WAV_N, + aw8697->trig[0].second_seq); + } + if (aw8697->trig[1].frist_seq) { + aw8697_i2c_write(aw8697, AW8697_REG_TRG2_WAV_P, + aw8697->trig[1].frist_seq); + } + if (aw8697->trig[1].second_seq && aw8697->trig[1].dual_edge) { + aw8697_i2c_write(aw8697, AW8697_REG_TRG2_WAV_N, + aw8697->trig[1].second_seq); + } + if (aw8697->trig[2].frist_seq) { + aw8697_i2c_write(aw8697, AW8697_REG_TRG3_WAV_P, + aw8697->trig[1].frist_seq); + } + if (aw8697->trig[2].second_seq && aw8697->trig[2].dual_edge) { + aw8697_i2c_write(aw8697, AW8697_REG_TRG3_WAV_N, + aw8697->trig[1].second_seq); + } + + return 0; +} + +static int aw8697_haptic_trig_enable_config(struct aw8697 *aw8697) +{ + pr_debug("%s enter\n", __func__); + + aw8697_i2c_write_bits(aw8697, AW8697_REG_TRG_CFG2, + AW8697_BIT_TRGCFG2_TRG1_ENABLE_MASK, + aw8697->trig[0].enable); + aw8697_i2c_write_bits(aw8697, AW8697_REG_TRG_CFG2, + AW8697_BIT_TRGCFG2_TRG2_ENABLE_MASK, + aw8697->trig[1].enable); + aw8697_i2c_write_bits(aw8697, AW8697_REG_TRG_CFG2, + AW8697_BIT_TRGCFG2_TRG3_ENABLE_MASK, + aw8697->trig[2].enable); + + return 0; +} + +static int aw8697_haptic_auto_boost_config(struct aw8697 *aw8697, + unsigned char flag) +{ + aw8697->auto_boost = flag; + if (flag) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_BST_AUTO, + AW8697_BIT_BST_AUTO_BST_AUTOSW_MASK, + AW8697_BIT_BST_AUTO_BST_AUTOMATIC_BOOST); + } else { + aw8697_i2c_write_bits(aw8697, AW8697_REG_BST_AUTO, + AW8697_BIT_BST_AUTO_BST_AUTOSW_MASK, + AW8697_BIT_BST_AUTO_BST_MANUAL_BOOST); + } + return 0; +} + +/***************************************************** + * + * vbat mode + * + *****************************************************/ +static int aw8697_haptic_cont_vbat_mode(struct aw8697 *aw8697, + unsigned char flag) +{ + if (flag == AW8697_HAPTIC_CONT_VBAT_HW_COMP_MODE) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_ADCTEST, + AW8697_BIT_ADCTEST_VBAT_MODE_MASK, + AW8697_BIT_ADCTEST_VBAT_HW_COMP); + } else { + aw8697_i2c_write_bits(aw8697, AW8697_REG_ADCTEST, + AW8697_BIT_ADCTEST_VBAT_MODE_MASK, + AW8697_BIT_ADCTEST_VBAT_SW_COMP); + } + return 0; +} + +static int aw8697_haptic_get_vbat(struct aw8697 *aw8697) +{ + unsigned char reg_val = 0; + unsigned int cont = 2000; + + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_RAMINIT_MASK, + AW8697_BIT_SYSCTRL_RAMINIT_EN); + aw8697_i2c_write_bits(aw8697, AW8697_REG_DETCTRL, + AW8697_BIT_DETCTRL_VBAT_GO_MASK, + AW8697_BIT_DETCTRL_VABT_GO_ENABLE); + + while (1) { + aw8697_i2c_read(aw8697, AW8697_REG_DETCTRL, ®_val); + if ((reg_val & 0x02) == 0 || cont == 0) + break; + cont--; + } + + aw8697_i2c_read(aw8697, AW8697_REG_VBATDET, ®_val); + aw8697->vbat = 6100 * reg_val / 256; + if (aw8697->vbat > AW8697_VBAT_MAX) { + aw8697->vbat = AW8697_VBAT_MAX; + pr_debug("%s vbat max limit = %dmV\n", __func__, aw8697->vbat); + } + if (aw8697->vbat < AW8697_VBAT_MIN) { + aw8697->vbat = AW8697_VBAT_MIN; + pr_debug("%s vbat min limit = %dmV\n", __func__, aw8697->vbat); + } + + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_RAMINIT_MASK, + AW8697_BIT_SYSCTRL_RAMINIT_OFF); + + return 0; +} + +static int aw8697_haptic_ram_vbat_comp(struct aw8697 *aw8697, bool flag) +{ + int temp_gain = 0; + int aw8697_gain; + + if (aw8697->debugfs_debug) + aw8697_gain = aw8697->gain_debug; + else + aw8697_gain = aw8697->gain; + + if (flag) { + if + (aw8697->ram_vbat_comp == AW8697_HAPTIC_RAM_VBAT_COMP_ENABLE) { + aw8697_haptic_get_vbat(aw8697); + temp_gain = + aw8697->gain * AW8697_VBAT_REFER / aw8697->vbat; + if (temp_gain > + (128 * AW8697_VBAT_REFER / AW8697_VBAT_MIN)) { + temp_gain = + 128 * AW8697_VBAT_REFER / AW8697_VBAT_MIN; + pr_debug("%s gain limit=%d\n", __func__, + temp_gain); + } + aw8697_haptic_set_gain(aw8697, temp_gain); + } else { + aw8697_haptic_set_gain(aw8697, aw8697_gain); + } + } else { + aw8697_haptic_set_gain(aw8697, aw8697_gain); + } + + return 0; +} + +/***************************************************** + * + * f0 + * + *****************************************************/ +static int aw8697_haptic_set_f0_preset(struct aw8697 *aw8697) +{ + unsigned int f0_reg = 0; + + pr_debug("%s enter\n", __func__); + + f0_reg = 1000000000 / (aw8697->info.f0_pre * aw8697->info.f0_coeff); + aw8697_i2c_write(aw8697, AW8697_REG_F_PRE_H, + (unsigned char)((f0_reg >> 8) & 0xff)); + aw8697_i2c_write(aw8697, AW8697_REG_F_PRE_L, + (unsigned char)((f0_reg >> 0) & 0xff)); + + return 0; +} + +static int aw8697_haptic_read_f0(struct aw8697 *aw8697) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned int f0_reg = 0; + unsigned long f0_tmp = 0; + + pr_debug("%s enter\n", __func__); + + ret = aw8697_i2c_read(aw8697, AW8697_REG_F_LRA_F0_H, ®_val); + f0_reg = (reg_val << 8); + ret = aw8697_i2c_read(aw8697, AW8697_REG_F_LRA_F0_L, ®_val); + f0_reg |= (reg_val << 0); + if (!f0_reg) { + pr_info("%s not get f0 because f0_reg value is 0!\n", __func__); + return 0; + } + f0_tmp = 1000000000 / (f0_reg * aw8697->info.f0_coeff); + aw8697->f0 = (unsigned int)f0_tmp; + pr_info("%s f0=%d\n", __func__, aw8697->f0); + + return 0; +} + +static int aw8697_haptic_read_cont_f0(struct aw8697 *aw8697) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned int f0_reg = 0; + unsigned long f0_tmp = 0; + + pr_debug("%s enter\n", __func__); + + ret = aw8697_i2c_read(aw8697, AW8697_REG_F_LRA_CONT_H, ®_val); + f0_reg = (reg_val << 8); + ret = aw8697_i2c_read(aw8697, AW8697_REG_F_LRA_CONT_L, ®_val); + f0_reg |= (reg_val << 0); + f0_tmp = 1000000000 / (f0_reg * aw8697->info.f0_coeff); + aw8697->cont_f0 = (unsigned int)f0_tmp; + pr_info("%s f0=%d\n", __func__, aw8697->cont_f0); + + return 0; +} + +static int aw8697_haptic_read_beme(struct aw8697 *aw8697) +{ + int ret = 0; + unsigned char reg_val = 0; + + ret = aw8697_i2c_read(aw8697, AW8697_REG_WAIT_VOL_MP, ®_val); + aw8697->max_pos_beme = (reg_val << 0); + ret = aw8697_i2c_read(aw8697, AW8697_REG_WAIT_VOL_MN, ®_val); + aw8697->max_neg_beme = (reg_val << 0); + + pr_info("%s max_pos_beme=%d\n", __func__, aw8697->max_pos_beme); + pr_info("%s max_neg_beme=%d\n", __func__, aw8697->max_neg_beme); + + return 0; +} + +/***************************************************** + * + * rtp + * + *****************************************************/ +static void aw8697_haptic_set_rtp_aei(struct aw8697 *aw8697, bool flag) +{ + if (flag) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSINTM, + AW8697_BIT_SYSINTM_FF_AE_MASK, + AW8697_BIT_SYSINTM_FF_AE_EN); + } else { + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSINTM, + AW8697_BIT_SYSINTM_FF_AE_MASK, + AW8697_BIT_SYSINTM_FF_AE_OFF); + } +} + +/* +static void aw8697_haptic_set_rtp_afi(struct aw8697 *aw8697, bool flag) +{ + if(flag) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSINTM, + AW8697_BIT_SYSINTM_FF_AF_MASK, AW8697_BIT_SYSINTM_FF_AF_EN); + } else { + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSINTM, + AW8697_BIT_SYSINTM_FF_AF_MASK, AW8697_BIT_SYSINTM_FF_AF_OFF); + } +} +*/ + +/* +static unsigned char aw8697_haptic_rtp_get_fifo_aei(struct aw8697 *aw8697) +{ + unsigned char ret; + unsigned char reg_val; + + aw8697_i2c_read(aw8697, AW8697_REG_SYSINT, ®_val); + reg_val &= AW8697_BIT_SYSINT_FF_AEI; + ret = reg_val>>4; + + return ret; +} +*/ + +/* +static unsigned char aw8697_haptic_rtp_get_fifo_aes(struct aw8697 *aw8697) +{ + unsigned char ret; + unsigned char reg_val; + + aw8697_i2c_read(aw8697, AW8697_REG_SYSST, ®_val); + reg_val &= AW8697_BIT_SYSST_FF_AES; + ret = reg_val>>4; + + return ret; +} +*/ + +static unsigned char aw8697_haptic_rtp_get_fifo_afi(struct aw8697 *aw8697) +{ + unsigned char ret = 0; + unsigned char reg_val = 0; + + if (aw8697->osc_cali_flag == 1) { + aw8697_i2c_read(aw8697, AW8697_REG_SYSST, ®_val); + reg_val &= AW8697_BIT_SYSST_FF_AFS; + ret = reg_val >> 3; + } else { + aw8697_i2c_read(aw8697, AW8697_REG_SYSINT, ®_val); + reg_val &= AW8697_BIT_SYSINT_FF_AFI; + ret = reg_val >> 3; + } + return ret; +} + +/* +static unsigned char aw8697_haptic_rtp_get_fifo_afs(struct aw8697 *aw8697) +{ + unsigned char ret = 0; + unsigned char reg_val = 0; + + aw8697_i2c_read(aw8697, AW8697_REG_SYSST, ®_val); + reg_val &= AW8697_BIT_SYSST_FF_AFS; + ret = reg_val>>3; + + return ret; +} +*/ + +static bool aw8697_check_error(struct aw8697 *aw8697) +{ + unsigned char reg_val; + int rc; + bool ret = false; + + rc = aw8697_i2c_read(aw8697, AW8697_REG_SYSST, ®_val); + if (rc < 0) + return true; + + if (reg_val & AW8697_BIT_SYSST_UVLS) { + pr_err("%s: ERROR: Under-Voltage Lock-Out!!\n", __func__); + ret = true; + } + + if (reg_val & AW8697_BIT_SYSST_OVS) { + pr_err("%s: ERROR: Wave data overflow or DPWM DC error!!\n", __func__); + ret = true; + } + + if (reg_val & AW8697_BIT_SYSST_BSTERRS) { + pr_err("%s: ERROR: Boost SCP/OVP error!!\n", __func__); + ret = true; + } + + if (reg_val & AW8697_BIT_SYSST_OCDS) { + pr_err("%s: ERROR: Over-current!!\n", __func__); + ret = true; + } + + if (reg_val & AW8697_BIT_SYSST_OTS) { + pr_err("%s: ERROR: Over-temperature!!\n", __func__); + ret = true; + } + + return ret; +} + +/***************************************************** + * + * rtp + * + *****************************************************/ +static int aw8697_haptic_rtp_init(struct aw8697 *aw8697) +{ + unsigned int buf_len = 0; + + pr_info("%s enter\n", __func__); + pm_qos_add_request(&pm_qos_req_vb, PM_QOS_CPU_DMA_LATENCY, PM_QOS_VALUE_VB); + aw8697->rtp_cnt = 0; + mutex_lock(&aw8697->rtp_lock); + while ((!aw8697_haptic_rtp_get_fifo_afi(aw8697)) && + (aw8697->play_mode == AW8697_HAPTIC_RTP_MODE) && + (!aw8697_check_error(aw8697))) { + pr_info("%s rtp cnt = %d\n", __func__, aw8697->rtp_cnt); + if (!aw8697_rtp) { + pr_info("%s:aw8697_rtp is null break\n", __func__); + mutex_unlock(&aw8697->rtp_lock); + break; + } + + if ((aw8697->rtp_cnt < aw8697->ram.base_addr)) { + if((aw8697_rtp->len-aw8697->rtp_cnt) < (aw8697->ram.base_addr)){ + buf_len = aw8697_rtp->len-aw8697->rtp_cnt; + } else { + buf_len = (aw8697->ram.base_addr); + } + } + else if ((aw8697_rtp->len - aw8697->rtp_cnt) < + (aw8697->ram.base_addr >> 2)) { + buf_len = aw8697_rtp->len - aw8697->rtp_cnt; + } else { + buf_len = (aw8697->ram.base_addr >> 2); + } + aw8697_i2c_writes(aw8697, AW8697_REG_RTP_DATA, + &aw8697_rtp->data[aw8697->rtp_cnt], buf_len); + aw8697->rtp_cnt += buf_len; + if (aw8697->rtp_cnt == aw8697_rtp->len) { + pr_info("%s: rtp update complete\n", __func__); + aw8697->rtp_cnt = 0; + pm_qos_remove_request(&pm_qos_req_vb); + mutex_unlock(&aw8697->rtp_lock); + return 0; + } + } + mutex_unlock(&aw8697->rtp_lock); + + if (aw8697->play_mode == AW8697_HAPTIC_RTP_MODE) + aw8697_haptic_set_rtp_aei(aw8697, true); + + pr_info("%s exit\n", __func__); + pm_qos_remove_request(&pm_qos_req_vb); + return 0; +} + +static void aw8697_haptic_upload_lra(struct aw8697 *aw8697, unsigned int flag) +{ + switch (flag) { + case 1: + pr_debug("%s f0_cali_lra=%d\n", __func__, aw8697->f0_calib_data); + aw8697_i2c_write(aw8697, AW8697_REG_TRIM_LRA, + (char)aw8697->f0_calib_data); + break; + case 2: + pr_debug("%s rtp_cali_lra=%d\n", __func__, aw8697->lra_calib_data); + aw8697_i2c_write(aw8697, AW8697_REG_TRIM_LRA, + (char)aw8697->lra_calib_data); + break; + default: + break; + } +} + +static int aw8697_clock_OSC_trim_calibration(unsigned long int theory_time, unsigned long int real_time) +{ + unsigned int real_code = 0; + unsigned int LRA_TRIM_CODE = 0; + unsigned int DFT_LRA_TRIM_CODE = 0; + unsigned int Not_need_cali_threshold = 10;/*0.1 percent not need calibrate*/ + + if (theory_time == real_time) { + pr_info("aw_osctheory_time == real_time:%ld theory_time = %ld not need to cali\n", real_time, theory_time); + return 0; + } else if (theory_time < real_time) { + if ((real_time - theory_time) > (theory_time / 25)) { + pr_info("aw_osc(real_time - theory_time) > (theory_time/50) not to cali\n"); + return DFT_LRA_TRIM_CODE; + } + + if ((real_time - theory_time) < (Not_need_cali_threshold*theory_time/10000)) { + pr_info("aw_oscmicrosecond:%ld theory_time = %ld not need to cali\n", real_time, theory_time); + return DFT_LRA_TRIM_CODE; + } + + real_code = ((real_time - theory_time) * 4000) / theory_time; + real_code = ((real_code%10 < 5) ? 0 : 1) + real_code/10; + real_code = 32 + real_code; + } else if (theory_time > real_time) { + if ((theory_time - real_time) > (theory_time / 25)) { + pr_info("aw_osc((theory_time - real_time) > (theory_time / 50)) not to cali\n"); + return DFT_LRA_TRIM_CODE; + } + if ((theory_time - real_time) < (Not_need_cali_threshold * theory_time/10000)) { + pr_info("aw_oscmicrosecond:%ld theory_time = %ld not need to cali\n", real_time, theory_time); + return DFT_LRA_TRIM_CODE; + } + real_code = ((theory_time - real_time) * 4000) / theory_time; + real_code = ((real_code%10 < 5) ? 0 : 1) + real_code/10; + real_code = 32 - real_code; + } + if (real_code > 31) + LRA_TRIM_CODE = real_code - 32; + else + LRA_TRIM_CODE = real_code + 32; + pr_info("aw_oscmicrosecond:%ld theory_time = %ld real_code =0X%02X LRA_TRIM_CODE 0X%02X\n", real_time, theory_time, real_code, LRA_TRIM_CODE); + + return LRA_TRIM_CODE; +} + +static int aw8697_rtp_trim_lra_calibration(struct aw8697 *aw8697) +{ + unsigned char reg_val = 0; + unsigned int fre_val = 0; + unsigned int theory_time = 0; + unsigned int lra_rtim_code = 0; + + aw8697_i2c_read(aw8697, AW8697_REG_PWMDBG, ®_val); + fre_val = (reg_val & 0x006f) >> 5; + + if (fre_val == 3) + theory_time = (aw8697->rtp_len / 12000) * 1000000; /*12K */ + if (fre_val == 2) + theory_time = (aw8697->rtp_len / 24000) * 1000000; /*24K */ + if (fre_val == 1 || fre_val == 0) + theory_time = (aw8697->rtp_len / 48000) * 1000000; /*48K */ + + printk("microsecond:%ld theory_time = %d\n", aw8697->microsecond, theory_time); + + lra_rtim_code = aw8697_clock_OSC_trim_calibration(theory_time, aw8697->microsecond); + if (lra_rtim_code >= 0) { + aw8697->lra_calib_data = lra_rtim_code; + aw8697_i2c_write(aw8697, AW8697_REG_TRIM_LRA, (char)lra_rtim_code); + } + return 0; +} +static unsigned char aw8697_haptic_osc_read_int(struct aw8697 *aw8697) +{ + unsigned char reg_val = 0; + + aw8697_i2c_read(aw8697, AW8697_REG_DBGSTAT, ®_val); + return reg_val; +} + +static int aw8697_rtp_osc_calibration(struct aw8697 *aw8697) +{ + const struct firmware *rtp_file; + int ret = -1; + unsigned int buf_len = 0; + unsigned char osc_int_state = 0; + aw8697->rtp_cnt = 0; + aw8697->timeval_flags = 1; + aw8697->osc_cali_flag = 1; + + pr_info("%s enter\n", __func__); + /* fw loaded */ + ret = request_firmware(&rtp_file, + aw8697_cali_name, + aw8697->dev); + if (ret < 0) { + pr_err("%s: failed to read %s\n", __func__, + aw8697_cali_name); + return ret; + } + /*awinic add stop,for irq interrupt during calibrate*/ + aw8697_haptic_stop(aw8697); + aw8697->rtp_init = 0; + mutex_lock(&aw8697->rtp_lock); + vfree(aw8697_rtp); + aw8697_rtp = vzalloc(rtp_file->size + sizeof(int)); + if (!aw8697_rtp) { + release_firmware(rtp_file); + mutex_unlock(&aw8697->rtp_lock); + pr_err("%s: error allocating memory\n", __func__); + return -ENOMEM; + } + aw8697_rtp->len = rtp_file->size; + aw8697->rtp_len = rtp_file->size; + pr_info("%s: rtp file [%s] size = %d\n", __func__, + aw8697_cali_name, aw8697_rtp->len); + memcpy(aw8697_rtp->data, rtp_file->data, rtp_file->size); + release_firmware(rtp_file); + mutex_unlock(&aw8697->rtp_lock); + + /* gain */ + aw8697_haptic_ram_vbat_comp(aw8697, false); + + /* rtp mode config */ + aw8697_haptic_play_mode(aw8697, AW8697_HAPTIC_RTP_MODE); + + aw8697_i2c_write_bits(aw8697, AW8697_REG_DBGCTRL, + AW8697_BIT_DBGCTRL_INT_MODE_MASK, + AW8697_BIT_DBGCTRL_INT_MODE_EDGE); + disable_irq(gpio_to_irq(aw8697->irq_gpio)); + /* haptic start */ + aw8697_haptic_start(aw8697); + pm_qos_add_request(&pm_qos_req_vb, PM_QOS_CPU_DMA_LATENCY, PM_QOS_VALUE_VB); + while (1) { + if (!aw8697_haptic_rtp_get_fifo_afi(aw8697)) { + pr_info("%s !aw8697_haptic_rtp_get_fifo_afi done aw8697->rtp_cnt= %d \n", __func__, aw8697->rtp_cnt); + mutex_lock(&aw8697->rtp_lock); + if ((aw8697_rtp->len - aw8697->rtp_cnt) < + (aw8697->ram.base_addr >> 2)) + buf_len = aw8697_rtp->len - aw8697->rtp_cnt; + else + buf_len = (aw8697->ram.base_addr >> 2); + + if (aw8697->rtp_cnt != aw8697_rtp->len) { + if (aw8697->timeval_flags == 1) { + do_gettimeofday(&aw8697->start); + aw8697->timeval_flags = 0; + } + aw8697->rtpupdate_flag = + aw8697_i2c_writes(aw8697, + AW8697_REG_RTP_DATA, + &aw8697_rtp->data[aw8697-> + rtp_cnt], buf_len); + aw8697->rtp_cnt += buf_len; + } + mutex_unlock(&aw8697->rtp_lock); + } + + osc_int_state = aw8697_haptic_osc_read_int(aw8697); + if (osc_int_state&AW8697_BIT_SYSINT_DONEI) { + do_gettimeofday(&aw8697->end); + pr_info("%s vincent playback done aw8697->rtp_cnt= %d \n", __func__, aw8697->rtp_cnt); + break; + } + + do_gettimeofday(&aw8697->end); + aw8697->microsecond = (aw8697->end.tv_sec - aw8697->start.tv_sec)*1000000 + + (aw8697->end.tv_usec - aw8697->start.tv_usec); + if (aw8697->microsecond > OSC_CALIBRATION_T_LENGTH) { + pr_info("%s vincent time out aw8697->rtp_cnt %d osc_int_state %02x\n", __func__, aw8697->rtp_cnt, osc_int_state); + break; + } + } + pm_qos_remove_request(&pm_qos_req_vb); + enable_irq(gpio_to_irq(aw8697->irq_gpio)); + + aw8697->osc_cali_flag = 0; + aw8697->microsecond = (aw8697->end.tv_sec - aw8697->start.tv_sec)*1000000 + + (aw8697->end.tv_usec - aw8697->start.tv_usec); + /*calibration osc*/ + pr_info("%s awinic_microsecond:%ld \n", __func__, aw8697->microsecond); + pr_info("%s exit\n", __func__); + return 0; +} + +static void aw8697_op_clean_status(struct aw8697 *aw8697) +{ + aw8697->audio_ready = false; + aw8697->haptic_ready = false; + aw8697->pre_haptic_number = false; + aw8697->rtp_routine_on = 0; + pr_info("%s enter\n", __func__); +} +static void aw8697_rtp_work_routine(struct work_struct *work) +{ + const struct firmware *rtp_file; + int ret = -1; + struct aw8697 *aw8697 = container_of(work, struct aw8697, rtp_work); + + pr_info("%s enter\n", __func__); + mutex_lock(&aw8697->rtp_lock); + aw8697->rtp_routine_on = 1; + /* fw loaded */ + ret = request_firmware(&rtp_file, + aw8697_rtp_name[aw8697->rtp_file_num], + aw8697->dev); + if (ret < 0) { + pr_err("%s: failed to read %s\n", __func__, + aw8697_rtp_name[aw8697->rtp_file_num]); + mutex_unlock(&aw8697->rtp_lock); + return; + } + aw8697->rtp_init = 0; + vfree(aw8697_rtp); + aw8697_rtp = vzalloc(rtp_file->size+sizeof(int)); + if (!aw8697_rtp) { + release_firmware(rtp_file); + pr_err("%s: error allocating memory\n", __func__); + mutex_unlock(&aw8697->rtp_lock); + return; + } + aw8697_rtp->len = rtp_file->size; + pr_info("%s: rtp file [%s] size = %d\n", __func__, + aw8697_rtp_name[aw8697->rtp_file_num], aw8697_rtp->len); + memcpy(aw8697_rtp->data, rtp_file->data, rtp_file->size); + mutex_unlock(&aw8697->rtp_lock); + release_firmware(rtp_file); + mutex_lock(&aw8697->lock); + aw8697->rtp_init = 1; + + aw8697_haptic_upload_lra(aw8697, 2); + + /* gain */ + aw8697_haptic_ram_vbat_comp(aw8697, false); + + /* rtp mode config */ + aw8697_haptic_play_mode(aw8697, AW8697_HAPTIC_RTP_MODE); + + /* haptic start */ + aw8697_haptic_start(aw8697); + + aw8697_haptic_rtp_init(aw8697); + + mutex_unlock(&aw8697->lock); +} + +static void aw8697_rtp_update_work_routine(struct work_struct *work) +{ + unsigned int buf_len; + struct aw8697 *aw8697 = container_of(work, struct aw8697, rtp_update_work); + + atomic_set(&aw8697->rtp_update_routine_on, true); + + while ((!aw8697_haptic_rtp_get_fifo_afi(aw8697)) && + (aw8697->play_mode == AW8697_HAPTIC_RTP_MODE) && + (!aw8697_check_error(aw8697))) { + mutex_lock(&aw8697->rtp_lock); + pr_info("%s: aw8697 rtp mode fifo update, cnt=%d\n", __func__, aw8697->rtp_cnt); + if (!aw8697_rtp) { + pr_warn("%s:aw8697_rtp is null break\n", __func__); + mutex_unlock(&aw8697->rtp_lock); + break; + } + if ((aw8697_rtp->len - aw8697->rtp_cnt) < + (aw8697->ram.base_addr >> 2)) { + buf_len = aw8697_rtp->len - aw8697->rtp_cnt; + } else { + buf_len = (aw8697->ram.base_addr >> 2); + } + aw8697->rtpupdate_flag = aw8697_i2c_writes(aw8697, + AW8697_REG_RTP_DATA, + &aw8697_rtp->data[aw8697->rtp_cnt], + buf_len); + aw8697->rtp_cnt += buf_len; + if (aw8697->rtp_cnt >= aw8697_rtp->len) { + aw8697_op_clean_status(aw8697); + pr_info("%s: rtp update complete\n", __func__); + aw8697_haptic_set_rtp_aei(aw8697, false); + aw8697->rtp_cnt = 0; + aw8697->rtp_init = 0; +#ifdef AW8697_REPEAT_RTP_PLAYING + if (aw8697->haptic_mode == HAPTIC_RTP_LOOP) + aw8697_rtp_play(aw8697, aw8697->rtp_file_num); +#endif + mutex_unlock(&aw8697->rtp_lock); + break; + } + mutex_unlock(&aw8697->rtp_lock); + } + + atomic_set(&aw8697->rtp_update_routine_on, false); +} + + +/***************************************************** + * + * haptic - audio + * + *****************************************************/ +static int aw8697_haptic_audio_ctr_list_insert(struct haptic_audio + *haptic_audio, struct haptic_ctr *haptic_ctr) +{ + struct haptic_ctr *p_new = NULL; + + p_new = + (struct haptic_ctr *)kzalloc(sizeof(struct haptic_ctr), GFP_KERNEL); + if (p_new == NULL) { + pr_err("%s: kzalloc memory fail\n", __func__); + return -1; + } + /* update new list info */ + p_new->cnt = haptic_ctr->cnt; + p_new->cmd = haptic_ctr->cmd; + p_new->play = haptic_ctr->play; + p_new->wavseq = haptic_ctr->wavseq; + p_new->loop = haptic_ctr->loop; + p_new->gain = haptic_ctr->gain; + + INIT_LIST_HEAD(&(p_new->list)); + list_add(&(p_new->list), &(haptic_audio->ctr_list)); + + return 0; +} + +static int aw8697_haptic_audio_ctr_list_clear(struct haptic_audio *haptic_audio) +{ + struct haptic_ctr *p_ctr = NULL; + struct haptic_ctr *p_ctr_bak = NULL; + + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + list_del(&p_ctr->list); + kfree(p_ctr); + } + + return 0; +} + +static int aw8697_haptic_audio_init(struct aw8697 *aw8697) +{ + + /* pr_debug("%s enter\n", __func__); */ + + aw8697_haptic_set_wav_seq(aw8697, 0x01, 0x00); + /* aw8697->haptic_audio.ori_gain = reg_val & 0xFF; */ + + return 0; +} + +static int aw8697_haptic_audio_off(struct aw8697 *aw8697) +{ + pr_debug("%s enter\n", __func__); + + mutex_lock(&aw8697->lock); + aw8697_haptic_set_gain(aw8697, 0x80); + aw8697_haptic_stop(aw8697); + aw8697_haptic_audio_ctr_list_clear(&aw8697->haptic_audio); + mutex_unlock(&aw8697->lock); + + return 0; +} + +static enum hrtimer_restart aw8697_haptic_audio_timer_func(struct hrtimer + *timer) +{ + struct aw8697 *aw8697 = + container_of(timer, struct aw8697, haptic_audio.timer); + + pr_debug("%s enter\n", __func__); + schedule_work(&aw8697->haptic_audio.work); + + hrtimer_start(&aw8697->haptic_audio.timer, + ktime_set(aw8697->haptic_audio.timer_val / 1000000, + (aw8697->haptic_audio.timer_val % 1000000) * + 1000), HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +static void aw8697_haptic_audio_work_routine(struct work_struct *work) +{ + struct aw8697 *aw8697 = + container_of(work, struct aw8697, haptic_audio.work); + struct haptic_audio *haptic_audio = NULL; + struct haptic_ctr *p_ctr = NULL; + struct haptic_ctr *p_ctr_bak = NULL; + unsigned int ctr_list_flag = 0; + unsigned int ctr_list_input_cnt = 0; + unsigned int ctr_list_output_cnt = 0; + unsigned int ctr_list_diff_cnt = 0; + unsigned int ctr_list_del_cnt = 0; + + int rtp_is_going_on = 0; + + pr_debug("%s enter\n", __func__); + + haptic_audio = &(aw8697->haptic_audio); + mutex_lock(&aw8697->haptic_audio.lock); + memset(&aw8697->haptic_audio.ctr, 0, sizeof(struct haptic_ctr)); + ctr_list_flag = 0; + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + ctr_list_flag = 1; + break; + } + if (ctr_list_flag == 0) + pr_info("%s: ctr list empty\n", __func__); + if (ctr_list_flag == 1) { + list_for_each_entry_safe(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + ctr_list_input_cnt = p_ctr->cnt; + break; + } + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), + list) { + ctr_list_output_cnt = p_ctr->cnt; + break; + } + if (ctr_list_input_cnt > ctr_list_output_cnt) { + ctr_list_diff_cnt = + ctr_list_input_cnt - ctr_list_output_cnt; + } + if (ctr_list_input_cnt < ctr_list_output_cnt) { + ctr_list_diff_cnt = + 32 + ctr_list_input_cnt - ctr_list_output_cnt; + } + if (ctr_list_diff_cnt > 2) { + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio-> + ctr_list), list) { + if ((p_ctr->play == 0) + && (AW8697_HAPTIC_CMD_ENABLE == + (AW8697_HAPTIC_CMD_HAPTIC & p_ctr-> + cmd))) { + list_del(&p_ctr->list); + kfree(p_ctr); + ctr_list_del_cnt++; + } + if (ctr_list_del_cnt == ctr_list_diff_cnt) + break; + } + } + + } + + /* get the last data from list */ + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + aw8697->haptic_audio.ctr.cnt = p_ctr->cnt; + aw8697->haptic_audio.ctr.cmd = p_ctr->cmd; + aw8697->haptic_audio.ctr.play = p_ctr->play; + aw8697->haptic_audio.ctr.wavseq = p_ctr->wavseq; + aw8697->haptic_audio.ctr.loop = p_ctr->loop; + aw8697->haptic_audio.ctr.gain = p_ctr->gain; + list_del(&p_ctr->list); + kfree(p_ctr); + break; + } + if (aw8697->haptic_audio.ctr.play) { + pr_info + ("%s: cnt=%d, cmd=%d, play=%d, wavseq=%d, loop=%d, gain=%d\n", + __func__, aw8697->haptic_audio.ctr.cnt, + aw8697->haptic_audio.ctr.cmd, + aw8697->haptic_audio.ctr.play, + aw8697->haptic_audio.ctr.wavseq, + aw8697->haptic_audio.ctr.loop, + aw8697->haptic_audio.ctr.gain); + } + + /* rtp mode jump */ + rtp_is_going_on = aw8697_haptic_juge_RTP_is_going_on(aw8697); + if (rtp_is_going_on) { + mutex_unlock(&aw8697->haptic_audio.lock); + return; + } + mutex_unlock(&aw8697->haptic_audio.lock); + if (aw8697->haptic_audio.ctr.cmd == AW8697_HAPTIC_CMD_ENABLE) { + if (aw8697->haptic_audio.ctr.play == AW8697_HAPTIC_PLAY_ENABLE) { + pr_info("%s: haptic_audio_play_start\n", __func__); + mutex_lock(&aw8697->lock); + aw8697_haptic_stop(aw8697); + aw8697_haptic_play_mode(aw8697, AW8697_HAPTIC_RAM_MODE); + + aw8697_haptic_set_wav_seq(aw8697, 0x00, + aw8697->haptic_audio.ctr. + wavseq); + aw8697_haptic_set_wav_seq(aw8697, 0x01, 0x00); + + aw8697_haptic_set_wav_loop(aw8697, 0x00, + aw8697->haptic_audio.ctr. + loop); + + aw8697_haptic_set_gain(aw8697, + aw8697->haptic_audio.ctr.gain); + + aw8697_haptic_start(aw8697); + mutex_unlock(&aw8697->lock); + } else if (AW8697_HAPTIC_PLAY_STOP == + aw8697->haptic_audio.ctr.play) { + mutex_lock(&aw8697->lock); + aw8697_haptic_stop(aw8697); + mutex_unlock(&aw8697->lock); + } else if (AW8697_HAPTIC_PLAY_GAIN == + aw8697->haptic_audio.ctr.play) { + mutex_lock(&aw8697->lock); + aw8697_haptic_set_gain(aw8697, + aw8697->haptic_audio.ctr.gain); + mutex_unlock(&aw8697->lock); + } + } +} + +/***************************************************** + * + * haptic cont + * + *****************************************************/ +static int aw8697_haptic_cont(struct aw8697 *aw8697) +{ + pr_info("%s enter\n", __func__); + + /* work mode */ + aw8697_haptic_play_mode(aw8697, AW8697_HAPTIC_CONT_MODE); + + /* preset f0 */ + aw8697_haptic_set_f0_preset(aw8697); + + /* lpf */ + aw8697_i2c_write_bits(aw8697, AW8697_REG_DATCTRL, + AW8697_BIT_DATCTRL_FC_MASK, + AW8697_BIT_DATCTRL_FC_1000HZ); + aw8697_i2c_write_bits(aw8697, AW8697_REG_DATCTRL, + AW8697_BIT_DATCTRL_LPF_ENABLE_MASK, + AW8697_BIT_DATCTRL_LPF_ENABLE); + + /* cont config */ + aw8697_i2c_write_bits(aw8697, AW8697_REG_CONT_CTRL, + AW8697_BIT_CONT_CTRL_ZC_DETEC_MASK, + AW8697_BIT_CONT_CTRL_ZC_DETEC_ENABLE); + aw8697_i2c_write_bits(aw8697, AW8697_REG_CONT_CTRL, + AW8697_BIT_CONT_CTRL_WAIT_PERIOD_MASK, + AW8697_BIT_CONT_CTRL_WAIT_1PERIOD); + aw8697_i2c_write_bits(aw8697, AW8697_REG_CONT_CTRL, + AW8697_BIT_CONT_CTRL_MODE_MASK, + AW8697_BIT_CONT_CTRL_BY_GO_SIGNAL); + aw8697_i2c_write_bits(aw8697, AW8697_REG_CONT_CTRL, + AW8697_BIT_CONT_CTRL_EN_CLOSE_MASK, + AW8697_CONT_PLAYBACK_MODE); + aw8697_i2c_write_bits(aw8697, AW8697_REG_CONT_CTRL, + AW8697_BIT_CONT_CTRL_F0_DETECT_MASK, + AW8697_BIT_CONT_CTRL_F0_DETECT_DISABLE); + aw8697_i2c_write_bits(aw8697, AW8697_REG_CONT_CTRL, + AW8697_BIT_CONT_CTRL_O2C_MASK, + AW8697_BIT_CONT_CTRL_O2C_DISABLE); + aw8697_i2c_write_bits(aw8697, AW8697_REG_CONT_CTRL, + AW8697_BIT_CONT_CTRL_AUTO_BRK_MASK, + AW8697_BIT_CONT_CTRL_AUTO_BRK_ENABLE); + + /* TD time */ + aw8697_i2c_write(aw8697, AW8697_REG_TD_H, + (unsigned char)(aw8697->info.cont_td >> 8)); + aw8697_i2c_write(aw8697, AW8697_REG_TD_L, + (unsigned char)(aw8697->info.cont_td >> 0)); + aw8697_i2c_write(aw8697, AW8697_REG_TSET, aw8697->info.tset); + + /* zero cross */ + aw8697_i2c_write(aw8697, AW8697_REG_ZC_THRSH_H, + (unsigned char)(aw8697->info.cont_zc_thr >> 8)); + aw8697_i2c_write(aw8697, AW8697_REG_ZC_THRSH_L, + (unsigned char)(aw8697->info.cont_zc_thr >> 0)); + + aw8697_i2c_write_bits(aw8697, AW8697_REG_BEMF_NUM, + AW8697_BIT_BEMF_NUM_BRK_MASK, + aw8697->info.cont_num_brk); + /* 35*171us=5.985ms */ + aw8697_i2c_write(aw8697, AW8697_REG_TIME_NZC, 0x23); + /* f0 driver level */ + aw8697_i2c_write(aw8697, AW8697_REG_DRV_LVL, aw8697->info.cont_drv_lvl); + aw8697_i2c_write(aw8697, AW8697_REG_DRV_LVL_OV, + aw8697->info.cont_drv_lvl_ov); + + /* cont play go */ + aw8697_haptic_play_go(aw8697, true); + + return 0; +} + +/***************************************************** + * + * haptic f0 cali + * + *****************************************************/ +static int aw8697_haptic_get_f0(struct aw8697 *aw8697) +{ + int ret = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + unsigned char f0_pre_num = 0; + unsigned char f0_wait_num = 0; + unsigned char f0_repeat_num = 0; + unsigned char f0_trace_num = 0; + unsigned int t_f0_ms = 0; + unsigned int t_f0_trace_ms = 0; + unsigned int f0_cali_cnt = 50; + + pr_info("%s enter\n", __func__); + + aw8697->f0 = aw8697->info.f0_pre; + + /* f0 calibrate work mode */ + aw8697_haptic_stop(aw8697); + aw8697_haptic_play_mode(aw8697, AW8697_HAPTIC_CONT_MODE); + + aw8697_i2c_write_bits(aw8697, AW8697_REG_CONT_CTRL, + AW8697_BIT_CONT_CTRL_EN_CLOSE_MASK, + AW8697_BIT_CONT_CTRL_OPEN_PLAYBACK); + aw8697_i2c_write_bits(aw8697, AW8697_REG_CONT_CTRL, + AW8697_BIT_CONT_CTRL_F0_DETECT_MASK, + AW8697_BIT_CONT_CTRL_F0_DETECT_ENABLE); + + /* LPF */ + aw8697_i2c_write_bits(aw8697, AW8697_REG_DATCTRL, + AW8697_BIT_DATCTRL_FC_MASK, + AW8697_BIT_DATCTRL_FC_1000HZ); + aw8697_i2c_write_bits(aw8697, AW8697_REG_DATCTRL, + AW8697_BIT_DATCTRL_LPF_ENABLE_MASK, + AW8697_BIT_DATCTRL_LPF_ENABLE); + + /* LRA OSC Source */ + if (aw8697->f0_cali_flag == AW8697_HAPTIC_CALI_F0) { + aw8697_i2c_write_bits(aw8697, AW8697_REG_ANACTRL, + AW8697_BIT_ANACTRL_LRA_SRC_MASK, + AW8697_BIT_ANACTRL_LRA_SRC_REG); + } else { + aw8697_i2c_write_bits(aw8697, AW8697_REG_ANACTRL, + AW8697_BIT_ANACTRL_LRA_SRC_MASK, + AW8697_BIT_ANACTRL_LRA_SRC_EFUSE); + } + + /* preset f0 */ + aw8697_haptic_set_f0_preset(aw8697); + + /* f0 driver level */ + aw8697_i2c_write(aw8697, AW8697_REG_DRV_LVL, aw8697->info.cont_drv_lvl); + + /* f0 trace parameter */ + f0_pre_num = aw8697->info.f0_trace_parameter[0]; + f0_wait_num = aw8697->info.f0_trace_parameter[1]; + f0_repeat_num = aw8697->info.f0_trace_parameter[2]; + f0_trace_num = aw8697->info.f0_trace_parameter[3]; + aw8697_i2c_write(aw8697, AW8697_REG_NUM_F0_1, + (f0_pre_num << 4) | (f0_wait_num << 0)); + aw8697_i2c_write(aw8697, AW8697_REG_NUM_F0_2, (f0_repeat_num << 0)); + aw8697_i2c_write(aw8697, AW8697_REG_NUM_F0_3, (f0_trace_num << 0)); + + /* clear aw8697 interrupt */ + ret = aw8697_i2c_read(aw8697, AW8697_REG_SYSINT, ®_val); + + /* play go and start f0 calibration */ + aw8697_haptic_play_go(aw8697, true); + + /* f0 trace time */ + t_f0_ms = 1000 * 10 / aw8697->info.f0_pre; + t_f0_trace_ms = + t_f0_ms * (f0_pre_num + f0_wait_num + + (f0_trace_num + f0_wait_num) * (f0_repeat_num - 1)); + usleep_range(t_f0_trace_ms * 1000, t_f0_trace_ms * 1000 + 500); + + for (i = 0; i < f0_cali_cnt; i++) { + ret = aw8697_i2c_read(aw8697, AW8697_REG_SYSINT, ®_val); + /* f0 calibrate done */ + if (reg_val & 0x01) { + aw8697_haptic_read_f0(aw8697); + aw8697_haptic_read_beme(aw8697); + break; + } + usleep_range(10000, 10500); + pr_info("%s f0 cali sleep 10ms\n", __func__); + } + + if (i == f0_cali_cnt) + ret = -1; + else + ret = 0; + + /* restore default config */ + aw8697_i2c_write_bits(aw8697, AW8697_REG_CONT_CTRL, + AW8697_BIT_CONT_CTRL_EN_CLOSE_MASK, + AW8697_CONT_PLAYBACK_MODE); + aw8697_i2c_write_bits(aw8697, AW8697_REG_CONT_CTRL, + AW8697_BIT_CONT_CTRL_F0_DETECT_MASK, + AW8697_BIT_CONT_CTRL_F0_DETECT_DISABLE); + + return ret; +} + +static int aw8697_haptic_f0_calibration(struct aw8697 *aw8697) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned int f0_limit = 0; + char f0_cali_lra = 0; + int f0_cali_step = 0; + + pr_info("%s enter\n", __func__); + + aw8697->f0_cali_flag = AW8697_HAPTIC_CALI_F0; + + aw8697_i2c_write(aw8697, AW8697_REG_TRIM_LRA, 0x00); + if (aw8697_haptic_get_f0(aw8697)) { + pr_err("%s get f0 error, user defafult f0\n", __func__); + } else { + /* max and min limit */ + f0_limit = aw8697->f0; + if (aw8697->f0 * 100 < + aw8697->info.f0_pre * (100 - aw8697->info.f0_cali_percen)) { + f0_limit = aw8697->info.f0_pre; + } + if (aw8697->f0 * 100 > + aw8697->info.f0_pre * (100 + aw8697->info.f0_cali_percen)) { + f0_limit = aw8697->info.f0_pre; + } + + /* calculate cali step */ + f0_cali_step = + 100000 * ((int)f0_limit - + (int)aw8697->info.f0_pre) / ((int)f0_limit * 25); + pr_info("%s line=%d f0_cali_step=%d\n", __func__, __LINE__, + f0_cali_step); + pr_info("%s line=%d f0_limit=%d\n", __func__, __LINE__, + (int)f0_limit); + pr_info("%s line=%d aw8697->info.f0_pre=%d\n", __func__, + __LINE__, (int)aw8697->info.f0_pre); + + if (f0_cali_step >= 0) { /*f0_cali_step >= 0 */ + if (f0_cali_step % 10 >= 5) + f0_cali_step = f0_cali_step / 10 + 1 + 32; + else + f0_cali_step = f0_cali_step / 10 + 32; + } else { /*f0_cali_step < 0 */ + if (f0_cali_step % 10 <= -5) + f0_cali_step = 32 + (f0_cali_step / 10 - 1); + else + f0_cali_step = 32 + f0_cali_step / 10; + } + + if (f0_cali_step > 31) + f0_cali_lra = (char)f0_cali_step - 32; + else + f0_cali_lra = (char)f0_cali_step + 32; + pr_info("%s f0_cali_lra=%d\n", __func__, (int)f0_cali_lra); + + aw8697->f0_calib_data = (int)f0_cali_lra; + printk("%s f0_cali_lra=%d\n", __func__, (int)f0_cali_lra); + + /* update cali step */ + aw8697_i2c_write(aw8697, AW8697_REG_TRIM_LRA, + (char)f0_cali_lra); + aw8697_i2c_read(aw8697, AW8697_REG_TRIM_LRA, ®_val); + pr_info("%s final trim_lra=0x%02x\n", __func__, reg_val); + } + + /* restore default work mode */ + aw8697_haptic_play_mode(aw8697, AW8697_HAPTIC_STANDBY_MODE); + aw8697->play_mode = AW8697_HAPTIC_RAM_MODE; + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_PLAY_MODE_MASK, + AW8697_BIT_SYSCTRL_PLAY_MODE_RAM); + aw8697_haptic_stop(aw8697); + + return ret; +} + +/***************************************************** + * + * haptic fops + * + *****************************************************/ +static int aw8697_file_open(struct inode *inode, struct file *file) +{ + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + file->private_data = (void *)g_aw8697; + + return 0; +} + +static int aw8697_file_release(struct inode *inode, struct file *file) +{ + file->private_data = (void *)NULL; + + module_put(THIS_MODULE); + + return 0; +} + +static long aw8697_file_unlocked_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct aw8697 *aw8697 = (struct aw8697 *)file->private_data; + + int ret = 0; + + dev_info(aw8697->dev, "%s: cmd=0x%x, arg=0x%lx\n", __func__, cmd, arg); + + mutex_lock(&aw8697->lock); + + if (_IOC_TYPE(cmd) != AW8697_HAPTIC_IOCTL_MAGIC) { + dev_err(aw8697->dev, "%s: cmd magic err\n", __func__); + return -EINVAL; + } + + switch (cmd) { + default: + dev_err(aw8697->dev, "%s, unknown cmd\n", __func__); + break; + } + + mutex_unlock(&aw8697->lock); + + return ret; +} + +static ssize_t aw8697_file_read(struct file *filp, char *buff, size_t len, + loff_t *offset) +{ + struct aw8697 *aw8697 = (struct aw8697 *)filp->private_data; + int ret = 0; + int i = 0; + unsigned char reg_val = 0; + unsigned char *pbuff = NULL; + + mutex_lock(&aw8697->lock); + + dev_info(aw8697->dev, "%s: len=%zu\n", __func__, len); + + switch (aw8697->fileops.cmd) { + case AW8697_HAPTIC_CMD_READ_REG: + pbuff = (unsigned char *)kzalloc(len, GFP_KERNEL); + if (pbuff != NULL) { + for (i = 0; i < len; i++) { + aw8697_i2c_read(aw8697, aw8697->fileops.reg + i, + ®_val); + pbuff[i] = reg_val; + } + for (i = 0; i < len; i++) { + dev_info(aw8697->dev, "%s: pbuff[%d]=0x%02x\n", + __func__, i, pbuff[i]); + } + ret = copy_to_user(buff, pbuff, len); + if (ret) { + dev_err(aw8697->dev, "%s: copy to user fail\n", + __func__); + } + kfree(pbuff); + } else { + dev_err(aw8697->dev, "%s: alloc memory fail\n", + __func__); + } + break; + default: + dev_err(aw8697->dev, "%s, unknown cmd %d\n", __func__, + aw8697->fileops.cmd); + break; + } + + mutex_unlock(&aw8697->lock); + + return len; +} + +static ssize_t aw8697_file_write(struct file *filp, const char *buff, + size_t len, loff_t *off) +{ + struct aw8697 *aw8697 = (struct aw8697 *)filp->private_data; + int i = 0; + int ret = 0; + unsigned char *pbuff = NULL; + + pbuff = (unsigned char *)kzalloc(len, GFP_KERNEL); + if (pbuff == NULL) { + dev_err(aw8697->dev, "%s: alloc memory fail\n", __func__); + return len; + } + ret = copy_from_user(pbuff, buff, len); + if (ret) { + kfree(pbuff); + dev_err(aw8697->dev, "%s: copy from user fail\n", __func__); + return len; + } + + for (i = 0; i < len; i++) { + dev_info(aw8697->dev, "%s: pbuff[%d]=0x%02x\n", + __func__, i, pbuff[i]); + } + + mutex_lock(&aw8697->lock); + + aw8697->fileops.cmd = pbuff[0]; + + switch (aw8697->fileops.cmd) { + case AW8697_HAPTIC_CMD_READ_REG: + if (len == 2) { + aw8697->fileops.reg = pbuff[1]; + } else { + dev_err(aw8697->dev, "%s: read cmd len %zu err\n", + __func__, len); + } + break; + case AW8697_HAPTIC_CMD_WRITE_REG: + if (len > 2) { + for (i = 0; i < len - 2; i++) { + dev_info(aw8697->dev, + "%s: write reg0x%02x=0x%02x\n", + __func__, pbuff[1] + i, pbuff[i + 2]); + aw8697_i2c_write(aw8697, pbuff[1] + i, + pbuff[2 + i]); + } + } else { + dev_err(aw8697->dev, "%s: write cmd len %zu err\n", + __func__, len); + } + break; + default: + dev_err(aw8697->dev, "%s, unknown cmd %d\n", __func__, + aw8697->fileops.cmd); + break; + } + + mutex_unlock(&aw8697->lock); + + if (pbuff != NULL) + kfree(pbuff); + return len; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .read = aw8697_file_read, + .write = aw8697_file_write, + .unlocked_ioctl = aw8697_file_unlocked_ioctl, + .open = aw8697_file_open, + .release = aw8697_file_release, +}; + +static struct miscdevice aw8697_haptic_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = AW8697_HAPTIC_NAME, + .fops = &fops, +}; + +static int aw8697_haptic_init(struct aw8697 *aw8697) +{ + int ret = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + unsigned char bemf_config = 0; + + pr_info("%s enter\n", __func__); + + ret = misc_register(&aw8697_haptic_misc); + if (ret) { + dev_err(aw8697->dev, "%s: misc fail: %d\n", __func__, ret); + return ret; + } + + /* haptic audio */ + aw8697->haptic_audio.delay_val = 1; + aw8697->haptic_audio.timer_val = 21318; + INIT_LIST_HEAD(&(aw8697->haptic_audio.ctr_list)); + + hrtimer_init(&aw8697->haptic_audio.timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + aw8697->haptic_audio.timer.function = aw8697_haptic_audio_timer_func; + INIT_WORK(&aw8697->haptic_audio.work, aw8697_haptic_audio_work_routine); + + mutex_init(&aw8697->haptic_audio.lock); + + INIT_LIST_HEAD(&(aw8697->haptic_audio.list)); + aw8697_op_clean_status(aw8697); + /* haptic init */ + mutex_lock(&aw8697->lock); + + aw8697->activate_mode = aw8697->info.mode; + + ret = aw8697_i2c_read(aw8697, AW8697_REG_WAVSEQ1, ®_val); + aw8697->index = reg_val & 0x7F; + ret = aw8697_i2c_read(aw8697, AW8697_REG_DATDBG, ®_val); + aw8697->gain = reg_val & 0xFF; + dev_info(aw8697->dev, "%s: gain set 0x%x\n", __func__, aw8697->gain); + ret = aw8697_i2c_read(aw8697, AW8697_REG_BSTDBG4, ®_val); + aw8697->vmax = (reg_val >> 1) & 0x1F; + for (i = 0; i < AW8697_SEQUENCER_SIZE; i++) { + ret = aw8697_i2c_read(aw8697, AW8697_REG_WAVSEQ1 + i, ®_val); + aw8697->seq[i] = reg_val; + } + + aw8697_haptic_play_mode(aw8697, AW8697_HAPTIC_STANDBY_MODE); + + aw8697_haptic_set_pwm(aw8697, AW8697_PWM_24K); + + aw8697_i2c_write(aw8697, AW8697_REG_BSTDBG1, aw8697->info.bstdbg[0]); + aw8697_i2c_write(aw8697, AW8697_REG_BSTDBG2, aw8697->info.bstdbg[1]); + aw8697_i2c_write(aw8697, AW8697_REG_BSTDBG3, aw8697->info.bstdbg[2]); + aw8697_i2c_write(aw8697, AW8697_REG_TSET, aw8697->info.tset); + aw8697_i2c_write(aw8697, AW8697_REG_R_SPARE, aw8697->info.r_spare); + + aw8697_i2c_write_bits(aw8697, AW8697_REG_ANADBG, + AW8697_BIT_ANADBG_IOC_MASK, + AW8697_BIT_ANADBG_IOC_4P65A); + + aw8697_haptic_set_bst_peak_cur(aw8697, AW8697_DEFAULT_PEAKCUR); + + aw8697_haptic_swicth_motorprotect_config(aw8697, 0x00, 0x00); + + aw8697_haptic_auto_boost_config(aw8697, false); + + aw8697_haptic_trig_param_init(aw8697); + aw8697_haptic_trig_param_config(aw8697); + + aw8697_haptic_offset_calibration(aw8697); + + /* vbat compensation */ + aw8697_haptic_cont_vbat_mode(aw8697, + AW8697_HAPTIC_CONT_VBAT_HW_COMP_MODE); + aw8697->ram_vbat_comp = AW8697_HAPTIC_RAM_VBAT_COMP_ENABLE; + + mutex_unlock(&aw8697->lock); + + /* f0 calibration */ + mutex_lock(&aw8697->lock); + + aw8697_haptic_f0_calibration(aw8697); + mutex_unlock(&aw8697->lock); + + /* beme config */ + bemf_config = aw8697->info.bemf_config[0]; + aw8697_i2c_write(aw8697, AW8697_REG_BEMF_VTHH_H, bemf_config); + bemf_config = aw8697->info.bemf_config[1]; + aw8697_i2c_write(aw8697, AW8697_REG_BEMF_VTHH_L, bemf_config); + bemf_config = aw8697->info.bemf_config[2]; + aw8697_i2c_write(aw8697, AW8697_REG_BEMF_VTHL_H, bemf_config); + bemf_config = aw8697->info.bemf_config[3]; + aw8697_i2c_write(aw8697, AW8697_REG_BEMF_VTHL_L, bemf_config); + return ret; +} + +/***************************************************** + * + * vibrator + * + *****************************************************/ +static void aw8697_rtp_play(struct aw8697 *aw8697, int value) +{ + aw8697_haptic_stop(aw8697); + aw8697_haptic_set_rtp_aei(aw8697, false); + aw8697_interrupt_clear(aw8697); + if (value >= (sizeof(aw8697_rtp_name) / AW8697_RTP_NAME_MAX)) { + pr_err("%s: rtp_file_num 0x%02x over max value \n", + __func__, aw8697->rtp_file_num); + return; + } + aw8697->rtp_file_num = value; + if (value) + schedule_work(&aw8697->rtp_work); +} + +static void __maybe_unused aw8697_haptic_context( + struct aw8697 *aw8697, enum aw8697_haptic_mode cmd) +{ + int t_top; + + if (!gpio_is_valid(aw8697->haptic_context_gpio)) { + pr_debug("%s haptic context gpio is invalid \n", __func__); + return; + } + + t_top = gpio_get_value(aw8697->haptic_context_gpio); + if (!t_top) + return; + + switch (cmd) { + case HAPTIC_RTP: + if (aw8697->info.rtp_gain) + aw8697->gain = aw8697->info.rtp_gain; + break; + case HAPTIC_SHORT: + if (aw8697->info.short_gain) + aw8697->gain = aw8697->info.short_gain; + break; + case HAPTIC_LONG: + if (aw8697->info.long_gain_reduced) + aw8697->gain = aw8697->info.long_gain_reduced; + break; + default: + break; + } +} + +static void aw8697_vibrate(struct aw8697 *aw8697, int value) +{ + int seq; + + mutex_lock(&aw8697->lock); +#ifdef AW8697_REPEAT_RTP_PLAYING + if (aw8697->haptic_mode == HAPTIC_RTP_LOOP) { + aw8697->haptic_mode = HAPTIC_RTP; + hrtimer_cancel(&aw8697->timer); + } +#endif + aw8697_haptic_stop(aw8697); + if (aw8697->index == 0x02) + PM_RELAX(aw8697->ws); + + seq = aw8697->seq[0]; + pr_debug("%s: value=%d, seq=%d, index=%x, duration=%d\n", + __func__, value, seq, aw8697->index, aw8697->duration); + + if (value > 0 || seq > 2) { + if (seq >= AW8697_SEQ_NO_RTP_BASE) { + aw8697->haptic_mode = HAPTIC_RTP; + if (aw8697->info.rtp_gain) + aw8697->gain = aw8697->info.rtp_gain; +#ifdef AW8697_REPEAT_RTP_PLAYING + if (seq >= AW8697_SEQ_NO_RTP_BASE + AW8697_SEQ_NO_RTP_REPEAT) { + aw8697->haptic_mode = HAPTIC_RTP_LOOP; + seq -= AW8697_SEQ_NO_RTP_REPEAT; + + value = AW8697_SEQ_NO_RTP_STOP; + PM_WAKEUP_EVENT(aw8697->ws, value + 100); + /* run ms timer */ + hrtimer_cancel(&aw8697->timer); + hrtimer_start(&aw8697->timer, + ktime_set(value / 1000, (value % 1000) * 1000000), + HRTIMER_MODE_REL); + } +#endif + } else if (value <= 100 || seq > 2) { + aw8697->haptic_mode = HAPTIC_SHORT; + if (aw8697->info.short_gain) + aw8697->gain = aw8697->info.short_gain; + } else { + aw8697->haptic_mode = HAPTIC_LONG; + if (aw8697->info.long_gain_normal) + aw8697->gain = aw8697->info.long_gain_normal; + } + + if (aw8697->debugfs_debug) + aw8697_haptic_set_gain(aw8697, aw8697->gain_debug); + else + aw8697_haptic_set_gain(aw8697, aw8697->gain); + + switch (aw8697->haptic_mode) { + case HAPTIC_RTP: + case HAPTIC_RTP_LOOP: + aw8697_rtp_play(aw8697, seq - AW8697_SEQ_NO_RTP_BASE); + break; + case HAPTIC_SHORT: + if (seq == 0 || aw8697->duration < 0) { + /* implicit seq or duration, use weak wave form */ + aw8697_haptic_set_wav_seq(aw8697, 0x00, 0x05); + } + aw8697->index = 0x01; + aw8697_haptic_set_wav_loop(aw8697, 0x00, 0x00); + aw8697_haptic_ram_vbat_comp(aw8697, false); + aw8697_haptic_play_wav_seq(aw8697, 0x01); + break; + case HAPTIC_LONG: + aw8697->duration = value; + aw8697->activate_mode = AW8697_HAPTIC_ACTIVATE_RAM_MODE; + /* wav index config */ + aw8697->index = 0x02; + aw8697_haptic_set_repeat_wav_seq(aw8697, aw8697->index); + PM_WAKEUP_EVENT(aw8697->ws, value + 100); + /* run ms timer */ + hrtimer_cancel(&aw8697->timer); + aw8697->state = 0x01; + hrtimer_start(&aw8697->timer, + ktime_set(aw8697->duration / 1000, (value % 1000) * 1000000), + HRTIMER_MODE_REL); + schedule_work(&aw8697->vibrator_work); + break; + default: + break; + } + + /* Restore to default short waveform */ + if (seq > 2) + aw8697->seq[0] = 0; + } else + aw8697->duration = -1; + mutex_unlock(&aw8697->lock); +} + +#ifdef TIMED_OUTPUT +static int aw8697_vibrator_get_time(struct timed_output_dev *dev) +{ + struct aw8697 *aw8697 = container_of(dev, struct aw8697, to_dev); + + if (hrtimer_active(&aw8697->timer)) { + ktime_t r = hrtimer_get_remaining(&aw8697->timer); + + return ktime_to_ms(r); + } + + return 0; +} + +static void aw8697_vibrator_enable(struct timed_output_dev *dev, int value) +{ + struct aw8697 *aw8697 = container_of(dev, struct aw8697, to_dev); + + mutex_lock(&aw8697->lock); + + pr_debug("%s enter\n", __func__); + + aw8697_haptic_stop(aw8697); + + if (value > 0) { + aw8697_haptic_ram_vbat_comp(aw8697, false); + aw8697_haptic_play_wav_seq(aw8697, value); + } + + mutex_unlock(&aw8697->lock); + + pr_debug("%s exit\n", __func__); +} + +#else +static enum led_brightness aw8697_haptic_brightness_get(struct led_classdev + *cdev) +{ + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); + + return aw8697->amplitude; +} + +static void aw8697_haptic_brightness_set(struct led_classdev *cdev, + enum led_brightness level) +{ + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); + + if (!aw8697->ram_init) + return; + if (aw8697->ramupdate_flag < 0) + return; + aw8697->amplitude = level; + + mutex_lock(&aw8697->lock); + + aw8697_haptic_stop(aw8697); + if (aw8697->amplitude > 0) { + aw8697_haptic_ram_vbat_comp(aw8697, false); + aw8697_haptic_play_wav_seq(aw8697, aw8697->amplitude); + } + + mutex_unlock(&aw8697->lock); + +} +#endif + +static ssize_t aw8697_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + + return snprintf(buf, PAGE_SIZE, "%d\n", aw8697->state); +} + +static ssize_t aw8697_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw8697_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + ktime_t time_rem; + s64 time_ms = 0; + + if (hrtimer_active(&aw8697->timer)) { + time_rem = hrtimer_get_remaining(&aw8697->timer); + time_ms = ktime_to_ms(time_rem); + } + + return snprintf(buf, PAGE_SIZE, "%lld\n", time_ms); +} + +static ssize_t aw8697_duration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + /* setting 0 on duration is NOP for now */ + if (val <= 0) + return count; + + pr_debug("%s: duration=%d\n", __func__, val); + + aw8697->duration = val; + + return count; +} + +static ssize_t aw8697_activate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + + /* For now nothing to show */ + return snprintf(buf, PAGE_SIZE, "%d\n", aw8697->state); +} + +static ssize_t aw8697_activate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + if (val != 0 && val != 1) + return count; + + pr_debug("%s: value=%d\n", __func__, val); + + aw8697->state = val; + if (aw8697->state) + aw8697_vibrate(aw8697, aw8697->duration); + else + aw8697_vibrate(aw8697, 0); + + return count; +} + +static ssize_t aw8697_activate_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + + return snprintf(buf, PAGE_SIZE, "activate_mode=%d\n", + aw8697->activate_mode); +} + +static ssize_t aw8697_activate_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw8697->lock); + aw8697->activate_mode = val; + mutex_unlock(&aw8697->lock); + return count; +} + +static ssize_t aw8697_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned char reg_val = 0; + + aw8697_i2c_read(aw8697, AW8697_REG_WAVSEQ1, ®_val); + aw8697->index = reg_val; + + return snprintf(buf, PAGE_SIZE, "%d\n", aw8697->index); +} + +static ssize_t aw8697_index_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + pr_debug("%s: value=%d\n", __func__, val); + + mutex_lock(&aw8697->lock); + aw8697->index = val; + aw8697_haptic_set_repeat_wav_seq(aw8697, aw8697->index); + mutex_unlock(&aw8697->lock); + return count; +} + +static ssize_t aw8697_vmax_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + + return snprintf(buf, PAGE_SIZE, "0x%02x\n", aw8697->vmax); +} + +static ssize_t aw8697_vmax_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + pr_debug("%s: value=%d\n", __func__, val); + + mutex_lock(&aw8697->lock); + aw8697->vmax = val; + aw8697_haptic_set_bst_vol(aw8697, aw8697->vmax); + mutex_unlock(&aw8697->lock); + return count; +} + +static ssize_t aw8697_gain_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + return snprintf(buf, PAGE_SIZE, "0x%02x\n", aw8697->gain_debug); +} + +static ssize_t aw8697_gain_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + pr_debug("%s: value=%d\n", __func__, val); + + mutex_lock(&aw8697->lock); + if (val > 0) + aw8697->debugfs_debug = true; + else + aw8697->debugfs_debug = false; + aw8697->gain_debug = val; + aw8697_haptic_set_gain(aw8697, aw8697->gain_debug); + mutex_unlock(&aw8697->lock); + return count; +} + +static ssize_t aw8697_seq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + size_t count = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + for (i = 0; i < AW8697_SEQUENCER_SIZE; i++) { + aw8697_i2c_read(aw8697, AW8697_REG_WAVSEQ1 + i, ®_val); + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d: 0x%02x\n", i + 1, reg_val); + aw8697->seq[i] |= reg_val; + } + return count; +} + +static ssize_t aw8697_seq_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int i, val = 0; + unsigned char data[AW8697_WAV_SEQ_SIZE]; + int rc; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + pr_debug("%s: value=%08x\n", __func__, val); + for (i = 0; i < AW8697_WAV_SEQ_SIZE; i++) { + aw8697->seq[i] = (val >> ((AW8697_WAV_SEQ_SIZE-i-1) * 8)) & 0xFF; + data[i] = aw8697->seq[i]; + } + + mutex_lock(&aw8697->lock); + aw8697_i2c_writes(aw8697, AW8697_REG_WAVSEQ1, data, AW8697_WAV_SEQ_SIZE); + mutex_unlock(&aw8697->lock); + + return count; +} + +static ssize_t aw8697_loop_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + size_t count = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + for (i = 0; i < AW8697_SEQUENCER_LOOP_SIZE; i++) { + aw8697_i2c_read(aw8697, AW8697_REG_WAVLOOP1 + i, ®_val); + aw8697->loop[i * 2 + 0] = (reg_val >> 4) & 0x0F; + aw8697->loop[i * 2 + 1] = (reg_val >> 0) & 0x0F; + + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d loop: 0x%02x\n", i * 2 + 1, + aw8697->loop[i * 2 + 0]); + count += + snprintf(buf + count, PAGE_SIZE - count, + "seq%d loop: 0x%02x\n", i * 2 + 2, + aw8697->loop[i * 2 + 1]); + } + return count; +} + +static ssize_t aw8697_loop_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + pr_debug("%s: seq%d loop=0x%x\n", __func__, databuf[0], + databuf[1]); + mutex_lock(&aw8697->lock); + aw8697->loop[databuf[0]] = (unsigned char)databuf[1]; + aw8697_haptic_set_wav_loop(aw8697, (unsigned char)databuf[0], + aw8697->loop[databuf[0]]); + mutex_unlock(&aw8697->lock); + } + + return count; +} + +static ssize_t aw8697_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + ssize_t len = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + for (i = 0; i < AW8697_REG_MAX; i++) { + if (!(aw8697_reg_access[i] & REG_RD_ACCESS)) + continue; + aw8697_i2c_read(aw8697, i, ®_val); + len += snprintf(buf + len, PAGE_SIZE - len, + "reg:0x%02x=0x%02x\n", i, reg_val); + } + return len; +} + +static ssize_t aw8697_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw8697_i2c_write(aw8697, (unsigned char)databuf[0], + (unsigned char)databuf[1]); + } + + return count; +} + +static ssize_t aw8697_rtp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "rtp play: %d\n", + aw8697->rtp_cnt); + + return len; +} + +#define AUDIO_READY_STATUS 1024 +#define FACTORY_MODE_NORMAL_RTP_NUMBER 73 +#define FACTORY_MODE_HIGH_TEMP_RTP_NUMBER 72 + +static ssize_t aw8697_rtp_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) { + pr_info("%s: kstrtouint fail\n", __func__); + return rc; + } + pr_info("%s: rtp[%d]\n", __func__, val); + + aw8697_rtp_play(aw8697, val); + + return count; +} + +static ssize_t aw8697_ram_update_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + /* struct timed_output_dev *to_dev = dev_get_drvdata(dev); */ + /*struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev);*/ +#else + /* struct led_classdev *cdev = dev_get_drvdata(dev); */ + /* struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); */ +#endif + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "sram update mode\n"); + return len; +} + +static ssize_t aw8697_ram_update_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + if (val) + aw8697_ram_update(aw8697); + return count; +} + +static ssize_t aw8697_f0_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + ssize_t len = 0; + + mutex_lock(&aw8697->lock); + aw8697->f0_cali_flag = AW8697_HAPTIC_LRA_F0; + aw8697_haptic_get_f0(aw8697); + mutex_unlock(&aw8697->lock); + len += + snprintf(buf + len, PAGE_SIZE - len, "aw8697 lra f0 = %d\n", + aw8697->f0); + return len; +} + +static ssize_t aw8697_f0_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ +#ifdef TIMED_OUTPUT + /* struct timed_output_dev *to_dev = dev_get_drvdata(dev); */ + /*struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev);*/ +#else + /* struct led_classdev *cdev = dev_get_drvdata(dev); */ + /* struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); */ +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + return count; +} + +static ssize_t aw8697_cali_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + ssize_t len = 0; + + mutex_lock(&aw8697->lock); + aw8697->f0_cali_flag = AW8697_HAPTIC_CALI_F0; + aw8697_haptic_get_f0(aw8697); + mutex_unlock(&aw8697->lock); + len += + snprintf(buf + len, PAGE_SIZE - len, "aw8697 cali f0 = %d\n", + aw8697->f0); + return len; +} + +static ssize_t aw8697_cali_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + if (val) { + mutex_lock(&aw8697->lock); + aw8697_haptic_f0_calibration(aw8697); + mutex_unlock(&aw8697->lock); + } + return count; +} + +static ssize_t aw8697_cont_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + ssize_t len = 0; + + aw8697_haptic_read_cont_f0(aw8697); + len += snprintf(buf + len, PAGE_SIZE - len, + "aw8697 cont f0 = %d\n", + aw8697->cont_f0); + return len; +} + +static ssize_t aw8697_cont_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + aw8697_haptic_stop(aw8697); + if (val) + aw8697_haptic_cont(aw8697); + return count; +} + +static ssize_t aw8697_cont_td_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "aw8697 cont delay time = 0x%04x\n", + aw8697->info.cont_td); + return len; +} + +static ssize_t aw8697_cont_td_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int databuf[1] = { 0 }; + + if (sscanf(buf, "%x", &databuf[0]) == 1) { + aw8697->info.cont_td = databuf[0]; + aw8697_i2c_write(aw8697, AW8697_REG_TD_H, + (unsigned char)(databuf[0] >> 8)); + aw8697_i2c_write(aw8697, AW8697_REG_TD_L, + (unsigned char)(databuf[0] >> 0)); + } + return count; +} + +static ssize_t aw8697_cont_drv_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "aw8697 cont drv level = %d\n", + aw8697->info.cont_drv_lvl); + len += snprintf(buf + len, PAGE_SIZE - len, + "aw8697 cont drv level overdrive= %d\n", + aw8697->info.cont_drv_lvl_ov); + return len; +} + +static ssize_t aw8697_cont_drv_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%d %d", &databuf[0], &databuf[1]) == 2) { + aw8697->info.cont_drv_lvl = databuf[0]; + aw8697_i2c_write(aw8697, AW8697_REG_DRV_LVL, + aw8697->info.cont_drv_lvl); + aw8697->info.cont_drv_lvl_ov = databuf[1]; + aw8697_i2c_write(aw8697, AW8697_REG_DRV_LVL_OV, + aw8697->info.cont_drv_lvl_ov); + } + return count; +} + +static ssize_t aw8697_cont_num_brk_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "aw8697 cont break num = %d\n", + aw8697->info.cont_num_brk); + return len; +} + +static ssize_t aw8697_cont_num_brk_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int databuf[1] = { 0 }; + + if (sscanf(buf, "%d", &databuf[0]) == 1) { + aw8697->info.cont_num_brk = databuf[0]; + if (aw8697->info.cont_num_brk > 7) + aw8697->info.cont_num_brk = 7; + aw8697_i2c_write_bits(aw8697, AW8697_REG_BEMF_NUM, + AW8697_BIT_BEMF_NUM_BRK_MASK, + aw8697->info.cont_num_brk); + } + return count; +} + +static ssize_t aw8697_cont_zc_thr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "aw8697 cont zero cross thr = 0x%04x\n", + aw8697->info.cont_zc_thr); + return len; +} + +static ssize_t aw8697_cont_zc_thr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int databuf[1] = { 0 }; + + if (sscanf(buf, "%x", &databuf[0]) == 1) { + aw8697->info.cont_zc_thr = databuf[0]; + aw8697_i2c_write(aw8697, AW8697_REG_ZC_THRSH_H, + (unsigned char)(databuf[0] >> 8)); + aw8697_i2c_write(aw8697, AW8697_REG_ZC_THRSH_L, + (unsigned char)(databuf[0] >> 0)); + } + return count; +} + +static ssize_t aw8697_vbat_monitor_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + ssize_t len = 0; + + mutex_lock(&aw8697->lock); + aw8697_haptic_stop(aw8697); + aw8697_haptic_get_vbat(aw8697); + len += + snprintf(buf + len, PAGE_SIZE - len, "vbat=%dmV\n", aw8697->vbat); + mutex_unlock(&aw8697->lock); + + return len; +} + +static ssize_t aw8697_vbat_monitor_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw8697_lra_resistance_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + ssize_t len = 0; + unsigned char reg_val = 0; + + mutex_lock(&aw8697->lock); + aw8697_haptic_stop(aw8697); + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_RAMINIT_MASK, + AW8697_BIT_SYSCTRL_RAMINIT_EN); + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_BST_MODE_MASK, + AW8697_BIT_SYSCTRL_BST_MODE_BYPASS); + + aw8697_i2c_write_bits(aw8697, AW8697_REG_ANACTRL, + AW8697_BIT_ANACTRL_HD_PD_MASK, + AW8697_BIT_ANACTRL_HD_HZ_EN); + aw8697_i2c_write_bits(aw8697, AW8697_REG_D2SCFG, + AW8697_BIT_D2SCFG_CLK_ADC_MASK, + AW8697_BIT_D2SCFG_CLK_ASC_1P5MHZ); + + aw8697_i2c_write_bits(aw8697, AW8697_REG_DETCTRL, + AW8697_BIT_DETCTRL_RL_OS_MASK, + AW8697_BIT_DETCTRL_RL_DETECT); + aw8697_i2c_write_bits(aw8697, AW8697_REG_DETCTRL, + AW8697_BIT_DETCTRL_DIAG_GO_MASK, + AW8697_BIT_DETCTRL_DIAG_GO_ENABLE); + usleep_range(3000, 3500); + aw8697_i2c_read(aw8697, AW8697_REG_RLDET, ®_val); + aw8697->lra = 298 * reg_val; + len += + snprintf(buf + len, PAGE_SIZE - len, "r_lra=%dmohm\n", aw8697->lra); + + aw8697_i2c_write_bits(aw8697, AW8697_REG_ANACTRL, + AW8697_BIT_ANACTRL_HD_PD_MASK, + AW8697_BIT_ANACTRL_HD_PD_EN); + aw8697_i2c_write_bits(aw8697, AW8697_REG_D2SCFG, + AW8697_BIT_D2SCFG_CLK_ADC_MASK, + AW8697_BIT_D2SCFG_CLK_ASC_6MHZ); + + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_RAMINIT_MASK, + AW8697_BIT_SYSCTRL_RAMINIT_OFF); + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_BST_MODE_MASK, + AW8697_BIT_SYSCTRL_BST_MODE_BOOST); + mutex_unlock(&aw8697->lock); + + return len; +} + +static ssize_t aw8697_lra_resistance_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw8697_auto_boost_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + ssize_t len = 0; + + len += + snprintf(buf + len, PAGE_SIZE - len, "auto_boost=%d\n", + aw8697->auto_boost); + + return len; +} + +static ssize_t aw8697_auto_boost_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw8697->lock); + aw8697_haptic_stop(aw8697); + aw8697_haptic_auto_boost_config(aw8697, val); + mutex_unlock(&aw8697->lock); + + return count; +} + +static ssize_t aw8697_prctmode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + ssize_t len = 0; + unsigned char reg_val = 0; + + aw8697_i2c_read(aw8697, AW8697_REG_RLDET, ®_val); + + len += + snprintf(buf + len, PAGE_SIZE - len, "prctmode=%d\n", + reg_val & 0x20); + return len; +} + +static ssize_t aw8697_prctmode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int databuf[2] = { 0, 0 }; + unsigned int addr = 0; + unsigned int val = 0; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + addr = databuf[0]; + val = databuf[1]; + mutex_lock(&aw8697->lock); + aw8697_haptic_swicth_motorprotect_config(aw8697, addr, val); + mutex_unlock(&aw8697->lock); + } + return count; +} + +static ssize_t aw8697_trig_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + ssize_t len = 0; + unsigned char i = 0; + + for (i = 0; i < AW8697_TRIG_NUM; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "trig%d: enable=%d, default_level=%d, dual_edge=%d, frist_seq=%d, second_seq=%d\n", + i + 1, aw8697->trig[i].enable, + aw8697->trig[i].default_level, + aw8697->trig[i].dual_edge, + aw8697->trig[i].frist_seq, + aw8697->trig[i].second_seq); + } + + return len; +} + +static ssize_t aw8697_trig_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int databuf[6] = { 0 }; + + if (sscanf(buf, "%d %d %d %d %d %d", + &databuf[0], &databuf[1], &databuf[2], &databuf[3], + &databuf[4], &databuf[5])) { + pr_debug("%s: %d, %d, %d, %d, %d, %d\n", __func__, databuf[0], + databuf[1], databuf[2], databuf[3], databuf[4], + databuf[5]); + if (databuf[0] > 3) + databuf[0] = 3; + if (databuf[0] > 0) + databuf[0] -= 1; + aw8697->trig[databuf[0]].enable = databuf[1]; + aw8697->trig[databuf[0]].default_level = databuf[2]; + aw8697->trig[databuf[0]].dual_edge = databuf[3]; + aw8697->trig[databuf[0]].frist_seq = databuf[4]; + aw8697->trig[databuf[0]].second_seq = databuf[5]; + mutex_lock(&aw8697->lock); + aw8697_haptic_trig_param_config(aw8697); + aw8697_haptic_trig_enable_config(aw8697); + mutex_unlock(&aw8697->lock); + } + return count; +} + +static ssize_t aw8697_ram_vbat_comp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + ssize_t len = 0; + + len += + snprintf(buf + len, PAGE_SIZE - len, "ram_vbat_comp=%d\n", + aw8697->ram_vbat_comp); + + return len; +} + +static ssize_t aw8697_ram_vbat_comp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw8697->lock); + if (val) + aw8697->ram_vbat_comp = AW8697_HAPTIC_RAM_VBAT_COMP_ENABLE; + else + aw8697->ram_vbat_comp = AW8697_HAPTIC_RAM_VBAT_COMP_DISABLE; + mutex_unlock(&aw8697->lock); + + return count; +} +#if 0 +static ssize_t aw8697_osc_cali_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct aw8697 *aw8697 = dev_get_drvdata(dev); + ssize_t len = 0; + + len += + snprintf(buf + len, PAGE_SIZE - len, "lra_calib_data=%d\n", + aw8697->lra_calib_data); + + return len; +} +#endif +static ssize_t aw8697_osc_cali_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int val = 0; + + int rc = 0; + rc = kstrtouint(buf, 0, &val); + if (rc < 0) { + return rc; + } + mutex_lock(&aw8697->lock); + /* osc calibration flag start,Other behaviors are forbidden */ + aw8697->osc_cali_run = 1; + if (val == 1) { + aw8697_rtp_osc_calibration(aw8697); + aw8697_rtp_trim_lra_calibration(aw8697); + } + aw8697->osc_cali_run = 0; + /* osc calibration flag end,Other behaviors are permitted */ + mutex_unlock(&aw8697->lock); + + return count; +} + +static ssize_t aw8697_haptic_audio_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "%d\n", + aw8697->haptic_audio.ctr.cnt); + return len; +} + +static ssize_t aw8697_haptic_audio_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int databuf[6] = { 0 }; + struct haptic_ctr *hap_ctr = NULL; + + if (!aw8697->ram_init) + return count; + if (6 == + sscanf(buf, "%d %d %d %d %d %d", &databuf[0], &databuf[1], + &databuf[2], &databuf[3], &databuf[4], &databuf[5])) { + if (databuf[2]) { + pr_info + ("%s: cnt=%d, cmd=%d, play=%d, wavseq=%d, loop=%d, gain=%d\n", + __func__, databuf[0], databuf[1], databuf[2], + databuf[3], databuf[4], databuf[5]); + } + + hap_ctr = + (struct haptic_ctr *)kzalloc(sizeof(struct haptic_ctr), + GFP_KERNEL); + if (hap_ctr == NULL) { + pr_err("%s: kzalloc memory fail\n", __func__); + return count; + } + mutex_lock(&aw8697->haptic_audio.lock); + hap_ctr->cnt = (unsigned char)databuf[0]; + hap_ctr->cmd = (unsigned char)databuf[1]; + hap_ctr->play = (unsigned char)databuf[2]; + hap_ctr->wavseq = (unsigned char)databuf[3]; + hap_ctr->loop = (unsigned char)databuf[4]; + hap_ctr->gain = (unsigned char)databuf[5]; + aw8697_haptic_audio_ctr_list_insert(&aw8697->haptic_audio, + hap_ctr); + + if (hap_ctr->cmd == 0xff) { + pr_info("%s: haptic_audio stop\n", __func__); + if (hrtimer_active(&aw8697->haptic_audio.timer)) { + pr_info("%s: cancel haptic_audio_timer\n", + __func__); + hrtimer_cancel(&aw8697->haptic_audio.timer); + aw8697->haptic_audio.ctr.cnt = 0; + aw8697_haptic_audio_off(aw8697); + } + } else { + if (hrtimer_active(&aw8697->haptic_audio.timer)) { + } else { + pr_info("%s: start haptic_audio_timer\n", + __func__); + aw8697_haptic_audio_init(aw8697); + hrtimer_start(&aw8697->haptic_audio.timer, + ktime_set(aw8697->haptic_audio. + delay_val / 1000000, + (aw8697->haptic_audio. + delay_val % 1000000) * + 1000), + HRTIMER_MODE_REL); + } + } + mutex_unlock(&aw8697->haptic_audio.lock); + kfree(hap_ctr); + } + return count; +} + +static ssize_t aw8697_haptic_audio_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "haptic_audio.delay_val=%dus\n", + aw8697->haptic_audio.delay_val); + len += snprintf(buf + len, PAGE_SIZE - len, + "haptic_audio.timer_val=%dus\n", + aw8697->haptic_audio.timer_val); + return len; +} + +static ssize_t aw8697_haptic_audio_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(to_dev, struct aw8697, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw8697 *aw8697 = container_of(cdev, struct aw8697, cdev); +#endif + unsigned int databuf[2] = { 0 }; + + if (sscanf(buf, "%d %d", &databuf[0], &databuf[1]) == 2) { + aw8697->haptic_audio.delay_val = databuf[0]; + aw8697->haptic_audio.timer_val = databuf[1]; + } + return count; +} + +static DEVICE_ATTR(state, S_IWUSR | S_IRUGO, aw8697_state_show, + aw8697_state_store); +static DEVICE_ATTR(duration, S_IWUSR | S_IRUGO, aw8697_duration_show, + aw8697_duration_store); +static DEVICE_ATTR(activate, S_IWUSR | S_IRUGO, aw8697_activate_show, + aw8697_activate_store); +static DEVICE_ATTR(activate_mode, S_IWUSR | S_IRUGO, aw8697_activate_mode_show, + aw8697_activate_mode_store); +static DEVICE_ATTR(index, S_IWUSR | S_IRUGO, aw8697_index_show, + aw8697_index_store); +static DEVICE_ATTR(vmax, S_IWUSR | S_IRUGO, aw8697_vmax_show, + aw8697_vmax_store); +static DEVICE_ATTR(gain, S_IWUSR | S_IRUGO, aw8697_gain_show, + aw8697_gain_store); +static DEVICE_ATTR(seq, S_IWUSR | S_IRUGO, aw8697_seq_show, aw8697_seq_store); +static DEVICE_ATTR(loop, S_IWUSR | S_IRUGO, aw8697_loop_show, + aw8697_loop_store); +static DEVICE_ATTR(register, S_IWUSR | S_IRUGO, aw8697_reg_show, + aw8697_reg_store); +static DEVICE_ATTR(rtp, S_IWUSR | S_IRUGO, aw8697_rtp_show, aw8697_rtp_store); +static DEVICE_ATTR(ram_update, S_IWUSR | S_IRUGO, aw8697_ram_update_show, + aw8697_ram_update_store); +static DEVICE_ATTR(f0, S_IWUSR | S_IRUGO, aw8697_f0_show, aw8697_f0_store); +static DEVICE_ATTR(cali, S_IWUSR | S_IRUGO, aw8697_cali_show, + aw8697_cali_store); +static DEVICE_ATTR(cont, S_IWUSR | S_IRUGO, aw8697_cont_show, + aw8697_cont_store); +static DEVICE_ATTR(cont_td, S_IWUSR | S_IRUGO, aw8697_cont_td_show, + aw8697_cont_td_store); +static DEVICE_ATTR(cont_drv, S_IWUSR | S_IRUGO, aw8697_cont_drv_show, + aw8697_cont_drv_store); +static DEVICE_ATTR(cont_num_brk, S_IWUSR | S_IRUGO, aw8697_cont_num_brk_show, + aw8697_cont_num_brk_store); +static DEVICE_ATTR(cont_zc_thr, S_IWUSR | S_IRUGO, aw8697_cont_zc_thr_show, + aw8697_cont_zc_thr_store); +static DEVICE_ATTR(vbat_monitor, S_IWUSR | S_IRUGO, aw8697_vbat_monitor_show, + aw8697_vbat_monitor_store); +static DEVICE_ATTR(lra_resistance, S_IWUSR | S_IRUGO, + aw8697_lra_resistance_show, aw8697_lra_resistance_store); +static DEVICE_ATTR(auto_boost, S_IWUSR | S_IRUGO, aw8697_auto_boost_show, + aw8697_auto_boost_store); +static DEVICE_ATTR(prctmode, S_IWUSR | S_IRUGO, aw8697_prctmode_show, + aw8697_prctmode_store); +static DEVICE_ATTR(trig, S_IWUSR | S_IRUGO, aw8697_trig_show, + aw8697_trig_store); +static DEVICE_ATTR(ram_vbat_comp, S_IWUSR | S_IRUGO, aw8697_ram_vbat_comp_show, + aw8697_ram_vbat_comp_store); +static DEVICE_ATTR(osc_cali, S_IWUSR | S_IRUGO, NULL, aw8697_osc_cali_store); +static DEVICE_ATTR(haptic_audio, S_IWUSR | S_IRUGO, aw8697_haptic_audio_show, + aw8697_haptic_audio_store); +static DEVICE_ATTR(haptic_audio_time, S_IWUSR | S_IRUGO, + aw8697_haptic_audio_time_show, + aw8697_haptic_audio_time_store); + +static struct attribute *aw8697_vibrator_attributes[] = { + &dev_attr_state.attr, + &dev_attr_duration.attr, + &dev_attr_activate.attr, + &dev_attr_activate_mode.attr, + &dev_attr_index.attr, + &dev_attr_vmax.attr, + &dev_attr_gain.attr, + &dev_attr_seq.attr, + &dev_attr_loop.attr, + &dev_attr_register.attr, + &dev_attr_rtp.attr, + &dev_attr_ram_update.attr, + &dev_attr_f0.attr, + &dev_attr_cali.attr, + &dev_attr_cont.attr, + &dev_attr_cont_td.attr, + &dev_attr_cont_drv.attr, + &dev_attr_cont_num_brk.attr, + &dev_attr_cont_zc_thr.attr, + &dev_attr_vbat_monitor.attr, + &dev_attr_lra_resistance.attr, + &dev_attr_auto_boost.attr, + &dev_attr_prctmode.attr, + &dev_attr_trig.attr, + &dev_attr_ram_vbat_comp.attr, + &dev_attr_osc_cali.attr, + &dev_attr_haptic_audio.attr, + &dev_attr_haptic_audio_time.attr, + NULL +}; + +static struct attribute_group aw8697_vibrator_attribute_group = { + .attrs = aw8697_vibrator_attributes +}; + +static enum hrtimer_restart aw8697_vibrator_timer_func(struct hrtimer *timer) +{ + struct aw8697 *aw8697 = container_of(timer, struct aw8697, timer); + + pr_debug("%s enter\n", __func__); + aw8697->state = 0; + schedule_work(&aw8697->vibrator_work); + + return HRTIMER_NORESTART; +} + +static void aw8697_vibrator_work_routine(struct work_struct *work) +{ + struct aw8697 *aw8697 = + container_of(work, struct aw8697, vibrator_work); + + pr_debug("%s enter\n", __func__); + + mutex_lock(&aw8697->lock); + +#ifdef AW8697_REPEAT_RTP_PLAYING + if (aw8697->haptic_mode == HAPTIC_RTP_LOOP) + aw8697->haptic_mode = HAPTIC_RTP; +#endif + aw8697_haptic_stop(aw8697); + aw8697_haptic_upload_lra(aw8697, 1); + if (aw8697->state) { + if (aw8697->activate_mode == AW8697_HAPTIC_ACTIVATE_RAM_MODE) { + aw8697_haptic_ram_vbat_comp(aw8697, true); + aw8697_haptic_play_repeat_seq(aw8697, true); + } else if (aw8697->activate_mode == + AW8697_HAPTIC_ACTIVATE_CONT_MODE) { + aw8697_haptic_cont(aw8697); + } else { + } + /* run ms timer */ + hrtimer_start(&aw8697->timer, + ktime_set(aw8697->duration / 1000, + (aw8697->duration % 1000) * 1000000), + HRTIMER_MODE_REL); + } + mutex_unlock(&aw8697->lock); +} + +static int aw8697_vibrator_init(struct aw8697 *aw8697) +{ + int ret = 0; + + pr_info("%s enter\n", __func__); + + PM_WAKEUP_REGISTER(aw8697->dev, aw8697->ws, "vibrator"); + if (!aw8697->ws) + return -ENOMEM; + +#ifdef TIMED_OUTPUT + aw8697->to_dev.name = "vibrator"; + aw8697->to_dev.get_time = aw8697_vibrator_get_time; + aw8697->to_dev.enable = aw8697_vibrator_enable; + + ret = timed_output_dev_register(&(aw8697->to_dev)); + if (ret < 0) { + dev_err(aw8697->dev, "%s: fail to create timed output dev\n", + __func__); + return ret; + } + ret = + sysfs_create_group(&aw8697->to_dev.dev->kobj, + &aw8697_vibrator_attribute_group); + if (ret < 0) { + dev_err(aw8697->dev, "%s error creating sysfs attr files\n", + __func__); + return ret; + } +#else + aw8697->cdev.name = "vibrator"; + aw8697->cdev.brightness_get = aw8697_haptic_brightness_get; + aw8697->cdev.brightness_set = aw8697_haptic_brightness_set; + + ret = devm_led_classdev_register(&aw8697->i2c->dev, &aw8697->cdev); + if (ret < 0) { + dev_err(aw8697->dev, "%s: fail to create led dev\n", __func__); + return ret; + } + ret = + sysfs_create_group(&aw8697->cdev.dev->kobj, + &aw8697_vibrator_attribute_group); + if (ret < 0) { + dev_err(aw8697->dev, "%s error creating sysfs attr files\n", + __func__); + return ret; + } +#endif + hrtimer_init(&aw8697->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + aw8697->timer.function = aw8697_vibrator_timer_func; + + INIT_WORK(&aw8697->vibrator_work, aw8697_vibrator_work_routine); + INIT_WORK(&aw8697->rtp_work, aw8697_rtp_work_routine); + INIT_WORK(&aw8697->rtp_update_work, aw8697_rtp_update_work_routine); + + mutex_init(&aw8697->lock); + mutex_init(&aw8697->rtp_lock); + + atomic_set(&aw8697->rtp_update_routine_on, false); + + return 0; +} + +/****************************************************** + * + * irq + * + ******************************************************/ +static void aw8697_interrupt_clear(struct aw8697 *aw8697) +{ + unsigned char reg_val = 0; + + pr_debug("%s enter\n", __func__); + aw8697_i2c_read(aw8697, AW8697_REG_SYSINT, ®_val); + pr_debug("%s: reg SYSINT=0x%x\n", __func__, reg_val); +} + +static void aw8697_interrupt_setup(struct aw8697 *aw8697) +{ + unsigned char reg_val = 0; + + pr_info("%s enter\n", __func__); + + aw8697_i2c_read(aw8697, AW8697_REG_SYSINT, ®_val); + pr_info("%s: reg SYSINT=0x%x\n", __func__, reg_val); + + /* edge int mode */ + aw8697_i2c_write_bits(aw8697, AW8697_REG_DBGCTRL, + AW8697_BIT_DBGCTRL_INT_MODE_MASK, + AW8697_BIT_DBGCTRL_INT_MODE_EDGE); + + /* int enable */ + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSINTM, + AW8697_BIT_SYSINTM_BSTERR_MASK, + AW8697_BIT_SYSINTM_BSTERR_OFF); + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSINTM, + AW8697_BIT_SYSINTM_OV_MASK, + AW8697_BIT_SYSINTM_OV_EN); + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSINTM, + AW8697_BIT_SYSINTM_UVLO_MASK, + AW8697_BIT_SYSINTM_UVLO_EN); + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSINTM, + AW8697_BIT_SYSINTM_OCD_MASK, + AW8697_BIT_SYSINTM_OCD_EN); + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSINTM, + AW8697_BIT_SYSINTM_OT_MASK, + AW8697_BIT_SYSINTM_OT_EN); +} + +static irqreturn_t aw8697_irq(int irq, void *data) +{ + struct aw8697 *aw8697 = data; + unsigned char reg_val = 0; + unsigned char dbg_val = 0; + + pr_debug("%s enter\n", __func__); + + aw8697_i2c_read(aw8697, AW8697_REG_SYSINT, ®_val); + pr_info("%s: reg SYSINT=0x%x\n", __func__, reg_val); + aw8697_i2c_read(aw8697, AW8697_REG_DBGSTAT, &dbg_val); + pr_info("%s: reg DBGSTAT=0x%x\n", __func__, dbg_val); + + if (reg_val & AW8697_BIT_SYSINT_OVI) { + aw8697_op_clean_status(aw8697); + pr_err("%s chip ov int error\n", __func__); + } + if (reg_val & AW8697_BIT_SYSINT_UVLI) { + aw8697_op_clean_status(aw8697); + pr_err("%s chip uvlo int error\n", __func__); + } + if (reg_val & AW8697_BIT_SYSINT_OCDI) { + aw8697_op_clean_status(aw8697); + pr_err("%s chip over current int error\n", __func__); + } + if (reg_val & AW8697_BIT_SYSINT_OTI) { + aw8697_op_clean_status(aw8697); + pr_err("%s chip over temperature int error\n", __func__); + } + if (reg_val & AW8697_BIT_SYSINT_DONEI) { + aw8697_op_clean_status(aw8697); + pr_info("%s chip playback done\n", __func__); + } + + if (reg_val & AW8697_BIT_SYSINT_FF_AEI) { + pr_info("%s: aw8697 rtp fifo almost empty int\n", __func__); + if (aw8697->rtp_init) { + if (!atomic_read(&aw8697->rtp_update_routine_on)) + schedule_work(&aw8697->rtp_update_work); + else + pr_info("%s: aw8697 rtp fifo update in progress\n", __func__); + } else { + pr_info("%s: aw8697 rtp init = %d, init error\n", + __func__, aw8697->rtp_init); + } + } + + if (reg_val & AW8697_BIT_SYSINT_FF_AFI) + pr_info("%s: aw8697 rtp mode fifo full empty\n", __func__); + + if (aw8697->play_mode != AW8697_HAPTIC_RTP_MODE) + aw8697_haptic_set_rtp_aei(aw8697, false); + + aw8697_i2c_read(aw8697, AW8697_REG_SYSINT, ®_val); + pr_info("%s: reg SYSINT=0x%x\n", __func__, reg_val); + aw8697_i2c_read(aw8697, AW8697_REG_SYSST, ®_val); + pr_info("%s: reg SYSST=0x%x\n", __func__, reg_val); + + pr_info("%s exit\n", __func__); + + return IRQ_HANDLED; +} + +/***************************************************** + * + * device tree + * + *****************************************************/ +static int aw8697_parse_dt(struct device *dev, struct aw8697 *aw8697, + struct device_node *np) +{ + unsigned int val = 0; + unsigned int bstdbg[6]; + unsigned int f0_trace_parameter[4]; + unsigned int bemf_config[4]; + + aw8697->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (aw8697->reset_gpio < 0) { + dev_err(dev, + "%s: no reset gpio provided, will not HW reset device\n", + __func__); + return -1; + } else { + dev_info(dev, "%s: reset gpio provided ok\n", __func__); + } + aw8697->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (aw8697->irq_gpio < 0) + dev_err(dev, "%s: no irq gpio provided.\n", __func__); + else + dev_info(dev, "%s: irq gpio provided ok.\n", __func__); + + aw8697->haptic_context_gpio = of_get_named_gpio( + np, "haptic-context-gpio", 0); + if (aw8697->haptic_context_gpio > 0) + pr_info("%s: haptic context gpio %d\n", + __func__, aw8697->haptic_context_gpio); + + val = of_property_read_u32(np, "vib_mode", &aw8697->info.mode); + if (!val) + pr_info("%s vib_mode is 0x%x\n", __func__, aw8697->info.mode); + + val = of_property_read_u32(np, "vib_f0_pre", &aw8697->info.f0_pre); + if (!val) + pr_info("%s vib_f0_pre is 0x%x\n", __func__, aw8697->info.f0_pre); + + val = of_property_read_u32(np, "vib_f0_cali_percen", + &aw8697->info.f0_cali_percen); + if (!val) + pr_info("%s vib_f0_cali_percen is 0x%x\n", + __func__, aw8697->info.f0_cali_percen); + + val = of_property_read_u32(np, "vib_cont_drv_lev", + &aw8697->info.cont_drv_lvl); + if (!val) + pr_info("%s vib_cont_drv_lev is 0x%x\n", + __func__, aw8697->info.cont_drv_lvl); + + val = of_property_read_u32(np, "vib_cont_drv_lvl_ov", + &aw8697->info.cont_drv_lvl_ov); + if (!val) + pr_info("%s vib_cont_drv_lvl_ov is 0x%x\n", + __func__, aw8697->info.cont_drv_lvl_ov); + + val = of_property_read_u32(np, "vib_cont_td", &aw8697->info.cont_td); + if (!val) + pr_info("%s vib_cont_td is 0x%x\n", + __func__, aw8697->info.cont_td); + + val = of_property_read_u32(np, "vib_cont_zc_thr", + &aw8697->info.cont_zc_thr); + if (!val) + pr_info("%s vib_cont_zc_thr is 0x%x\n", + __func__, aw8697->info.cont_zc_thr); + + val = of_property_read_u32(np, "vib_cont_num_brk", + &aw8697->info.cont_num_brk); + if (!val) + pr_info("%s vib_cont_num_brk is 0x%x\n", + __func__, aw8697->info.cont_num_brk); + + val = of_property_read_u32(np, "vib_f0_coeff", &aw8697->info.f0_coeff); + if (!val) + pr_info("%s vib_f0_coeff 0x%x\n", + __func__, aw8697->info.f0_coeff); + + val = of_property_read_u32(np, "vib_tset", &aw8697->info.tset); + if (!val) + pr_info("%s vib_tset is 0x%x\n", __func__, aw8697->info.tset); + + val = of_property_read_u32(np, "vib_r_spare", &aw8697->info.r_spare); + if (!val) + pr_info("%s vib_r_spare is 0x%x\n", + __func__, aw8697->info.r_spare); + + val = of_property_read_u32_array(np, "vib_bstdbg", + bstdbg, ARRAY_SIZE(bstdbg)); + if (!val) { + pr_info("%s vib_bstdbg provided\n", __func__); + memcpy(aw8697->info.bstdbg, bstdbg, sizeof(bstdbg)); + } + + val = of_property_read_u32_array(np, "vib_f0_trace_parameter", + f0_trace_parameter, + ARRAY_SIZE(f0_trace_parameter)); + if (!val) { + pr_info("%s vib_f0_trace_parameter provided\n", __func__); + memcpy(aw8697->info.f0_trace_parameter, + f0_trace_parameter, sizeof(f0_trace_parameter)); + } + + val = of_property_read_u32_array(np, "vib_bemf_config", + bemf_config, ARRAY_SIZE(bemf_config)); + if (!val) { + pr_info("%s vib_bemf_config provided\n", __func__); + memcpy(aw8697->info.bemf_config, + bemf_config, sizeof(bemf_config)); + } + + val = of_property_read_u32(np, "long-gain-normal", + &aw8697->info.long_gain_normal); + if (!val) + pr_info("%s: long_gain_normal is 0x%x\n", + __func__, aw8697->info.long_gain_normal); + + val = of_property_read_u32(np, "long-gain-reduced", + &aw8697->info.long_gain_reduced); + if (!val) + pr_info("%s: long_gain_reduced is 0x%x\n", + __func__, aw8697->info.long_gain_reduced); + + val = of_property_read_u32(np, "short-gain", + &aw8697->info.short_gain); + if (!val) + pr_info("%s: short_gain is 0x%x\n", + __func__, aw8697->info.short_gain); + + val = of_property_read_u32(np, "rtp-gain", + &aw8697->info.rtp_gain); + if (!val) + pr_info("%s: rtp_gain is 0x%x\n", + __func__, aw8697->info.rtp_gain); + return 0; +} + +static int aw8697_hw_reset(struct aw8697 *aw8697) +{ + pr_info("%s enter\n", __func__); + + if (aw8697 && gpio_is_valid(aw8697->reset_gpio)) { + gpio_set_value_cansleep(aw8697->reset_gpio, 0); + usleep_range(1000, 2000); + gpio_set_value_cansleep(aw8697->reset_gpio, 1); + usleep_range(3500, 4000); + } else { + dev_err(aw8697->dev, "%s: failed\n", __func__); + } + return 0; +} + +/***************************************************** + * + * check chip id + * + *****************************************************/ +static int aw8697_read_chipid(struct aw8697 *aw8697) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char reg = 0; + + while (cnt < AW_READ_CHIPID_RETRIES) { + /* hardware reset */ + aw8697_hw_reset(aw8697); + + ret = aw8697_i2c_read(aw8697, AW8697_REG_ID, ®); + if (ret < 0) { + dev_err(aw8697->dev, + "%s: failed to read register AW8697_REG_ID: %d\n", + __func__, ret); + } + switch (reg) { + case AW8697_CHIPID: + pr_info("%s aw8697 detected\n", __func__); + aw8697->chipid = AW8697_CHIPID; + /* aw8697->flags |= AW8697_FLAG_SKIP_INTERRUPTS; */ + aw8697_haptic_softreset(aw8697); + return 0; + default: + pr_info("%s unsupported device revision (0x%x)\n", + __func__, reg); + break; + } + cnt++; + + usleep_range(AW_READ_CHIPID_RETRY_DELAY * 1000, + AW_READ_CHIPID_RETRY_DELAY * 1000 + 500); + } + + return -EINVAL; +} + +/****************************************************** + * + * sys group attribute: reg + * + ******************************************************/ +static ssize_t aw8697_i2c_reg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw8697 *aw8697 = dev_get_drvdata(dev); + + unsigned int databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw8697_i2c_write(aw8697, (unsigned char)databuf[0], + (unsigned char)databuf[1]); + } + + return count; +} + +static ssize_t aw8697_i2c_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw8697 *aw8697 = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + for (i = 0; i < AW8697_REG_MAX; i++) { + if (!(aw8697_reg_access[i] & REG_RD_ACCESS)) + continue; + aw8697_i2c_read(aw8697, i, ®_val); + len += snprintf(buf + len, PAGE_SIZE - len, + "reg:0x%02x=0x%02x\n", i, reg_val); + } + return len; +} + +static ssize_t aw8697_i2c_ram_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw8697 *aw8697 = dev_get_drvdata(dev); + + unsigned int databuf[1] = { 0 }; + + if (sscanf(buf, "%x", &databuf[0]) == 1) { + if (databuf[0] == 1) + aw8697_ram_update(aw8697); + } + + return count; +} + +static ssize_t aw8697_i2c_ram_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw8697 *aw8697 = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned int i = 0; + unsigned char reg_val = 0; + + aw8697_haptic_stop(aw8697); + /* RAMINIT Enable */ + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_RAMINIT_MASK, + AW8697_BIT_SYSCTRL_RAMINIT_EN); + + aw8697_i2c_write(aw8697, AW8697_REG_RAMADDRH, + (unsigned char)(aw8697->ram.base_addr >> 8)); + aw8697_i2c_write(aw8697, AW8697_REG_RAMADDRL, + (unsigned char)(aw8697->ram.base_addr & 0x00ff)); + len += snprintf(buf + len, PAGE_SIZE - len, "aw8697_haptic_ram:\n"); + for (i = 0; i < aw8697->ram.len; i++) { + aw8697_i2c_read(aw8697, AW8697_REG_RAMDATA, ®_val); + len += snprintf(buf + len, PAGE_SIZE - len, "0x%02x,", reg_val); + } + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + /* RAMINIT Disable */ + aw8697_i2c_write_bits(aw8697, AW8697_REG_SYSCTRL, + AW8697_BIT_SYSCTRL_RAMINIT_MASK, + AW8697_BIT_SYSCTRL_RAMINIT_OFF); + + return len; +} + +static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, aw8697_i2c_reg_show, + aw8697_i2c_reg_store); +static DEVICE_ATTR(ram, S_IWUSR | S_IRUGO, aw8697_i2c_ram_show, + aw8697_i2c_ram_store); + +static struct attribute *aw8697_attributes[] = { + &dev_attr_reg.attr, + &dev_attr_ram.attr, + NULL +}; + +static struct attribute_group aw8697_attribute_group = { + .attrs = aw8697_attributes +}; + +/****************************************************** + * + * i2c driver + * + ******************************************************/ +static int aw8697_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct aw8697 *aw8697; + struct device_node *np = i2c->dev.of_node; + int irq_flags = 0; + int ret = -1; + + pr_info("%s enter\n", __func__); + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed\n"); + return -EIO; + } + + aw8697 = devm_kzalloc(&i2c->dev, sizeof(struct aw8697), GFP_KERNEL); + if (aw8697 == NULL) + return -ENOMEM; + + aw8697->dev = &i2c->dev; + aw8697->i2c = i2c; + + i2c_set_clientdata(i2c, aw8697); + + /* aw8697 rst & int */ + if (np) { + ret = aw8697_parse_dt(&i2c->dev, aw8697, np); + if (ret) { + dev_err(&i2c->dev, + "%s: failed to parse device tree node\n", + __func__); + goto err_parse_dt; + } + } else { + aw8697->reset_gpio = -1; + aw8697->irq_gpio = -1; + } + + if (gpio_is_valid(aw8697->reset_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw8697->reset_gpio, + GPIOF_OUT_INIT_LOW, "aw8697_rst"); + if (ret) { + dev_err(&i2c->dev, "%s: rst request failed\n", + __func__); + goto err_reset_gpio_request; + } + } + + if (gpio_is_valid(aw8697->irq_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw8697->irq_gpio, + GPIOF_DIR_IN, "aw8697_int"); + if (ret) { + dev_err(&i2c->dev, "%s: int request failed\n", + __func__); + goto err_irq_gpio_request; + } + } + + /* aw8697 chip id */ + ret = aw8697_read_chipid(aw8697); + if (ret < 0) { + dev_err(&i2c->dev, "%s: aw8697_read_chipid failed ret=%d\n", + __func__, ret); + goto err_id; + } + + /* aw8697 irq */ + if (gpio_is_valid(aw8697->irq_gpio) && + !(aw8697->flags & AW8697_FLAG_SKIP_INTERRUPTS)) { + /* register irq handler */ + aw8697_interrupt_setup(aw8697); + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, + gpio_to_irq(aw8697->irq_gpio), + NULL, aw8697_irq, irq_flags, + "aw8697", aw8697); + if (ret != 0) { + dev_err(&i2c->dev, "%s: failed to request IRQ %d: %d\n", + __func__, gpio_to_irq(aw8697->irq_gpio), ret); + goto err_irq; + } + } else { + dev_info(&i2c->dev, "%s skipping IRQ registration\n", __func__); + /* disable feature support if gpio was invalid */ + aw8697->flags |= AW8697_FLAG_SKIP_INTERRUPTS; + } + + dev_set_drvdata(&i2c->dev, aw8697); + + ret = sysfs_create_group(&i2c->dev.kobj, &aw8697_attribute_group); + if (ret < 0) { + dev_info(&i2c->dev, "%s error creating sysfs attr files\n", + __func__); + goto err_sysfs; + } + + g_aw8697 = aw8697; + + aw8697_vibrator_init(aw8697); + + aw8697_haptic_init(aw8697); + + aw8697_ram_init(aw8697); + + pr_info("%s probe completed successfully!\n", __func__); + + return 0; + + err_sysfs: + devm_free_irq(&i2c->dev, gpio_to_irq(aw8697->irq_gpio), aw8697); + err_irq: + err_id: + if (gpio_is_valid(aw8697->irq_gpio)) + devm_gpio_free(&i2c->dev, aw8697->irq_gpio); + err_irq_gpio_request: + if (gpio_is_valid(aw8697->reset_gpio)) + devm_gpio_free(&i2c->dev, aw8697->reset_gpio); + err_reset_gpio_request: + err_parse_dt: + devm_kfree(&i2c->dev, aw8697); + aw8697 = NULL; + return ret; +} + +static int aw8697_i2c_remove(struct i2c_client *i2c) +{ + struct aw8697 *aw8697 = i2c_get_clientdata(i2c); + + pr_info("%s enter\n", __func__); + + sysfs_remove_group(&i2c->dev.kobj, &aw8697_attribute_group); + + devm_free_irq(&i2c->dev, gpio_to_irq(aw8697->irq_gpio), aw8697); + + if (gpio_is_valid(aw8697->irq_gpio)) + devm_gpio_free(&i2c->dev, aw8697->irq_gpio); + if (gpio_is_valid(aw8697->reset_gpio)) + devm_gpio_free(&i2c->dev, aw8697->reset_gpio); + + PM_WAKEUP_UNREGISTER(aw8697->ws); + + devm_kfree(&i2c->dev, aw8697); + aw8697 = NULL; + + return 0; +} + +static int __maybe_unused aw8697_suspend(struct device *dev) +{ + int ret = 0; + struct aw8697 *aw8697 = dev_get_drvdata(dev); + + mutex_lock(&aw8697->lock); + aw8697_haptic_stop(aw8697); + mutex_unlock(&aw8697->lock); + + return ret; +} + +static int __maybe_unused aw8697_resume(struct device *dev) +{ + int ret = 0; + return ret; +} + +//static SIMPLE_DEV_PM_OPS(aw8697_pm_ops, aw8697_suspend, aw8697_resume); + +static const struct i2c_device_id aw8697_i2c_id[] = { + {AW8697_I2C_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, aw8697_i2c_id); + +static const struct of_device_id aw8697_dt_match[] = { + {.compatible = "awinic,aw8697_haptic"}, + {}, +}; + +static struct i2c_driver aw8697_i2c_driver = { + .driver = { + .name = AW8697_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw8697_dt_match), + }, + .probe = aw8697_i2c_probe, + .remove = aw8697_i2c_remove, + .id_table = aw8697_i2c_id, +}; + +static int __init aw8697_i2c_init(void) +{ + int ret = 0; + + pr_info("aw8697 driver version %s\n", AW8697_VERSION); + + ret = i2c_add_driver(&aw8697_i2c_driver); + if (ret) { + pr_err("fail to add aw8697 device into i2c\n"); + return ret; + } + + return 0; +} + +/* late_initcall(aw8697_i2c_init); */ +module_init(aw8697_i2c_init); + +static void __exit aw8697_i2c_exit(void) +{ + i2c_del_driver(&aw8697_i2c_driver); +} + +module_exit(aw8697_i2c_exit); + +MODULE_DESCRIPTION("AW8697 Haptic Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/awinic/aw8697/aw8697.h b/drivers/misc/awinic/aw8697/aw8697.h new file mode 100644 index 000000000000..559096cb5dec --- /dev/null +++ b/drivers/misc/awinic/aw8697/aw8697.h @@ -0,0 +1,418 @@ +#ifndef _AW8697_H_ +#define _AW8697_H_ + +/********************************************************* + * + * kernel version + * + ********************************************************/ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 4, 1) +#define TIMED_OUTPUT +#endif + +/********************************************************* + * + * aw8697.h + * + ********************************************************/ +#include +#include +#include +#include +#include +#include +#ifdef TIMED_OUTPUT +#include <../../../drivers/staging/android/timed_output.h> +#else +#include +#endif +#include + +/********************************************************* + * + * marco + * + ********************************************************/ +#define AW8697_CHIPID 0x97 + +#define MAX_I2C_BUFFER_SIZE 65536 + +#define AW8697_WAV_SEQ_SIZE 4 +#define AW8697_SEQUENCER_SIZE 8 +#define AW8697_SEQUENCER_LOOP_SIZE 4 + +#define AW8697_RTP_I2C_SINGLE_MAX_NUM 512 + +#define HAPTIC_MAX_TIMEOUT 10000 + +#define AW8697_VBAT_REFER 4200 +#define AW8697_VBAT_MIN 3000 +#define AW8697_VBAT_MAX 4500 + +/* trig config */ +#define AW8697_TRIG_NUM 3 +#define AW8697_TRG1_ENABLE 0 +#define AW8697_TRG2_ENABLE 0 +#define AW8697_TRG3_ENABLE 0 + +/* + * trig default high level + * ___________ _________________ + * | | + * | | + * |___________| + * first edge + * second edge + * + * + * trig default low level + * ___________ + * | | + * | | + * __________| |_________________ + * first edge + * second edge + */ +#define AW8697_TRG1_DEFAULT_LEVEL 1 /* 1: high level; 0: low level */ +#define AW8697_TRG2_DEFAULT_LEVEL 1 /* 1: high level; 0: low level */ +#define AW8697_TRG3_DEFAULT_LEVEL 1 /* 1: high level; 0: low level */ + +#define AW8697_TRG1_DUAL_EDGE 1 /* 1: dual edge; 0: first edge */ +#define AW8697_TRG2_DUAL_EDGE 1 /* 1: dual edge; 0: first edge */ +#define AW8697_TRG3_DUAL_EDGE 1 /* 1: dual edge; 0: first edge */ + +#define AW8697_TRG1_FIRST_EDGE_SEQ 1 /* trig1: first edge waveform seq */ +#define AW8697_TRG1_SECOND_EDGE_SEQ 2 /* trig1: second edge waveform seq */ +#define AW8697_TRG2_FIRST_EDGE_SEQ 1 /* trig2: first edge waveform seq */ +#define AW8697_TRG2_SECOND_EDGE_SEQ 2 /* trig2: second edge waveform seq */ +#define AW8697_TRG3_FIRST_EDGE_SEQ 1 /* trig3: first edge waveform seq */ +#define AW8697_TRG3_SECOND_EDGE_SEQ 2 /* trig3: second edge waveform seq */ + +#if AW8697_TRG1_ENABLE +#define AW8697_TRG1_DEFAULT_ENABLE AW8697_BIT_TRGCFG2_TRG1_ENABLE +#else +#define AW8697_TRG1_DEFAULT_ENABLE AW8697_BIT_TRGCFG2_TRG1_DISABLE +#endif + +#if AW8697_TRG2_ENABLE +#define AW8697_TRG2_DEFAULT_ENABLE AW8697_BIT_TRGCFG2_TRG2_ENABLE +#else +#define AW8697_TRG2_DEFAULT_ENABLE AW8697_BIT_TRGCFG2_TRG2_DISABLE +#endif + +#if AW8697_TRG3_ENABLE +#define AW8697_TRG3_DEFAULT_ENABLE AW8697_BIT_TRGCFG2_TRG3_ENABLE +#else +#define AW8697_TRG3_DEFAULT_ENABLE AW8697_BIT_TRGCFG2_TRG3_DISABLE +#endif + +#if AW8697_TRG1_DEFAULT_LEVEL +#define AW8697_TRG1_DEFAULT_POLAR AW8697_BIT_TRGCFG1_TRG1_POLAR_POS +#else +#define AW8697_TRG1_DEFAULT_POLAR AW8697_BIT_TRGCFG1_TRG1_POLAR_NEG +#endif + +#if AW8697_TRG2_DEFAULT_LEVEL +#define AW8697_TRG2_DEFAULT_POLAR AW8697_BIT_TRGCFG1_TRG2_POLAR_POS +#else +#define AW8697_TRG2_DEFAULT_POLAR AW8697_BIT_TRGCFG1_TRG2_POLAR_NEG +#endif + +#if AW8697_TRG3_DEFAULT_LEVEL +#define AW8697_TRG3_DEFAULT_POLAR AW8697_BIT_TRGCFG1_TRG3_POLAR_POS +#else +#define AW8697_TRG3_DEFAULT_POLAR AW8697_BIT_TRGCFG1_TRG3_POLAR_NEG +#endif + +#if AW8697_TRG1_DUAL_EDGE +#define AW8697_TRG1_DEFAULT_EDGE AW8697_BIT_TRGCFG1_TRG1_EDGE_POS_NEG +#else +#define AW8697_TRG1_DEFAULT_EDGE AW8697_BIT_TRGCFG1_TRG1_EDGE_POS +#endif + +#if AW8697_TRG2_DUAL_EDGE +#define AW8697_TRG2_DEFAULT_EDGE AW8697_BIT_TRGCFG1_TRG2_EDGE_POS_NEG +#else +#define AW8697_TRG2_DEFAULT_EDGE AW8697_BIT_TRGCFG1_TRG2_EDGE_POS +#endif + +#if AW8697_TRG3_DUAL_EDGE +#define AW8697_TRG3_DEFAULT_EDGE AW8697_BIT_TRGCFG1_TRG3_EDGE_POS_NEG +#else +#define AW8697_TRG3_DEFAULT_EDGE AW8697_BIT_TRGCFG1_TRG3_EDGE_POS +#endif + +enum aw8697_flags { + AW8697_FLAG_NONR = 0, + AW8697_FLAG_SKIP_INTERRUPTS = 1, +}; + +enum aw8697_haptic_read_write { + AW8697_HAPTIC_CMD_READ_REG = 0, + AW8697_HAPTIC_CMD_WRITE_REG = 1, +}; + +enum aw8697_haptic_work_mode { + AW8697_HAPTIC_STANDBY_MODE = 0, + AW8697_HAPTIC_RAM_MODE = 1, + AW8697_HAPTIC_RTP_MODE = 2, + AW8697_HAPTIC_TRIG_MODE = 3, + AW8697_HAPTIC_CONT_MODE = 4, + AW8697_HAPTIC_RAM_LOOP_MODE = 5, +}; + +enum aw8697_haptic_bst_mode { + AW8697_HAPTIC_BYPASS_MODE = 0, + AW8697_HAPTIC_BOOST_MODE = 1, +}; + +enum aw8697_haptic_activate_mode { + AW8697_HAPTIC_ACTIVATE_RAM_MODE = 0, + AW8697_HAPTIC_ACTIVATE_CONT_MODE = 1, +}; + +enum aw8697_haptic_cont_vbat_comp_mode { + AW8697_HAPTIC_CONT_VBAT_SW_COMP_MODE = 0, + AW8697_HAPTIC_CONT_VBAT_HW_COMP_MODE = 1, +}; + +enum aw8697_haptic_ram_vbat_comp_mode { + AW8697_HAPTIC_RAM_VBAT_COMP_DISABLE = 0, + AW8697_HAPTIC_RAM_VBAT_COMP_ENABLE = 1, +}; + +enum aw8697_haptic_f0_flag { + AW8697_HAPTIC_LRA_F0 = 0, + AW8697_HAPTIC_CALI_F0 = 1, +}; + +enum aw8697_haptic_pwm_mode { + AW8697_PWM_48K = 0, + AW8697_PWM_24K = 1, + AW8697_PWM_12K = 2, +}; + +enum aw8697_haptic_play { + AW8697_HAPTIC_PLAY_NULL = 0, + AW8697_HAPTIC_PLAY_ENABLE = 1, + AW8697_HAPTIC_PLAY_STOP = 2, + AW8697_HAPTIC_PLAY_GAIN = 8, +}; + +enum aw8697_haptic_cmd { + AW8697_HAPTIC_CMD_NULL = 0, + AW8697_HAPTIC_CMD_ENABLE = 1, + AW8697_HAPTIC_CMD_HAPTIC = 0x0f, + AW8697_HAPTIC_CMD_TP = 0x10, + AW8697_HAPTIC_CMD_SYS = 0xf0, + AW8697_HAPTIC_CMD_STOP = 255, +}; + +enum aw8697_haptic_mode{ + HAPTIC_NONE = 0x00, + HAPTIC_SHORT = 0x01, + HAPTIC_LONG = 0x02, + HAPTIC_RTP = 0x03, + HAPTIC_RTP_LOOP = 0x04, +}; +/********************************************************* + * + * struct + * + ********************************************************/ +struct fileops { + unsigned char cmd; + unsigned char reg; + unsigned char ram_addrh; + unsigned char ram_addrl; +}; + +struct ram { + unsigned int len; + unsigned int check_sum; + unsigned int base_addr; + unsigned char version; + unsigned char ram_shift; + unsigned char baseaddr_shift; +}; + +struct haptic_ctr { + unsigned char cnt; + unsigned char cmd; + unsigned char play; + unsigned char wavseq; + unsigned char loop; + unsigned char gain; + struct list_head list; +}; + +struct haptic_audio { + struct mutex lock; + struct hrtimer timer; + struct work_struct work; + int delay_val; + int timer_val; + struct haptic_ctr ctr; + struct list_head ctr_list; + /* struct tp tp; */ + struct list_head list; + /* struct haptic_audio_tp_size tp_size; */ + /* struct trust_zone_info output_tz_info[10]; */ + int tz_num; + int tz_high_num; + int tz_cnt_thr; + int tz_cnt_max; + unsigned int uevent_report_flag; + unsigned int hap_cnt_outside_tz; + unsigned int hap_cnt_max_outside_tz; +}; + +struct trig { + unsigned char enable; + unsigned char default_level; + unsigned char dual_edge; + unsigned char frist_seq; + unsigned char second_seq; +}; + +struct aw8697_dts_info { + unsigned int mode; + unsigned int f0_pre; + unsigned int f0_cali_percen; + unsigned int cont_drv_lvl; + unsigned int cont_drv_lvl_ov; + unsigned int cont_td; + unsigned int cont_zc_thr; + unsigned int cont_num_brk; + unsigned int f0_coeff; + unsigned int f0_trace_parameter[4]; + unsigned int bemf_config[4]; + unsigned int sw_brake; + unsigned int tset; + unsigned int r_spare; + unsigned int bstdbg[6]; + unsigned int parameter1; + unsigned int long_gain_normal; + unsigned int long_gain_reduced; + unsigned int short_gain; + unsigned int rtp_gain; +}; + +struct aw8697 { + struct regmap *regmap; + struct i2c_client *i2c; + struct device *dev; + struct input_dev *input; + + struct wakeup_source *ws; + + struct mutex lock; + struct mutex rtp_lock; + struct hrtimer timer; + struct work_struct vibrator_work; + struct work_struct rtp_work; + struct work_struct rtp_update_work; + struct delayed_work ram_work; +#ifdef TIMED_OUTPUT + struct timed_output_dev to_dev; +#else + struct led_classdev cdev; +#endif + struct fileops fileops; + struct ram ram; + bool haptic_ready; + bool audio_ready; + int pre_haptic_number; + struct timeval start, end; + unsigned int timeval_flags; + unsigned int osc_cali_flag; + unsigned long int microsecond; + unsigned int sys_frequency; + unsigned int rtp_len; + + int reset_gpio; + int irq_gpio; + int haptic_context_gpio; + + unsigned char hwen_flag; + unsigned char flags; + unsigned char chipid; + + unsigned char play_mode; + + unsigned char activate_mode; + + unsigned char auto_boost; + + int state; + int duration; + int amplitude; + int index; + int vmax; + int gain; + int gain_debug; + bool debugfs_debug; + + unsigned char seq[AW8697_SEQUENCER_SIZE]; + unsigned char loop[AW8697_SEQUENCER_SIZE]; + + unsigned int rtp_cnt; + unsigned int rtp_file_num; + + unsigned char rtp_init; + unsigned char ram_init; + unsigned char rtp_routine_on; + atomic_t rtp_update_routine_on; + + unsigned int f0; + unsigned int cont_f0; + unsigned char max_pos_beme; + unsigned char max_neg_beme; + unsigned char f0_cali_flag; + unsigned int theory_time; + + unsigned char ram_vbat_comp; + unsigned int vbat; + unsigned int lra; + + struct trig trig[AW8697_TRIG_NUM]; + + enum aw8697_haptic_mode haptic_mode; + struct haptic_audio haptic_audio; + struct aw8697_dts_info info; + unsigned int ramupdate_flag; + unsigned int rtpupdate_flag; + unsigned int osc_cali_run; + unsigned int lra_calib_data; + unsigned int f0_calib_data; +}; + +struct aw8697_container { + int len; + unsigned char data[]; +}; + +/********************************************************* + * + * ioctl + * + ********************************************************/ +struct aw8697_seq_loop { + unsigned char loop[AW8697_SEQUENCER_SIZE]; +}; + +struct aw8697_que_seq { + unsigned char index[AW8697_SEQUENCER_SIZE]; +}; + +#define AW8697_HAPTIC_IOCTL_MAGIC 'h' + +#define AW8697_HAPTIC_SET_QUE_SEQ _IOWR(AW8697_HAPTIC_IOCTL_MAGIC, 1, struct aw8697_que_seq*) +#define AW8697_HAPTIC_SET_SEQ_LOOP _IOWR(AW8697_HAPTIC_IOCTL_MAGIC, 2, struct aw8697_seq_loop*) +#define AW8697_HAPTIC_PLAY_QUE_SEQ _IOWR(AW8697_HAPTIC_IOCTL_MAGIC, 3, unsigned int) +#define AW8697_HAPTIC_SET_BST_VOL _IOWR(AW8697_HAPTIC_IOCTL_MAGIC, 4, unsigned int) +#define AW8697_HAPTIC_SET_BST_PEAK_CUR _IOWR(AW8697_HAPTIC_IOCTL_MAGIC, 5, unsigned int) +#define AW8697_HAPTIC_SET_GAIN _IOWR(AW8697_HAPTIC_IOCTL_MAGIC, 6, unsigned int) +#define AW8697_HAPTIC_PLAY_REPEAT_SEQ _IOWR(AW8697_HAPTIC_IOCTL_MAGIC, 7, unsigned int) + +#endif diff --git a/drivers/misc/awinic/aw8697/aw8697_config.h b/drivers/misc/awinic/aw8697/aw8697_config.h new file mode 100644 index 000000000000..223eeeeb0d1f --- /dev/null +++ b/drivers/misc/awinic/aw8697/aw8697_config.h @@ -0,0 +1,9 @@ +#ifndef __AW8697_CONFIG_H__ +#define __AW8697_CONFIG_H__ + +#define AW8697_BSTCFG_PEAKCUR_LIMIT 0x07 +#define AW8697_DEFAULT_PEAKCUR AW8697_BIT_BSTCFG_PEAKCUR_3P5A + +#define AW8697_CONT_PLAYBACK_MODE AW8697_BIT_CONT_CTRL_CLOSE_PLAYBACK + +#endif diff --git a/drivers/misc/awinic/aw8697/aw8697_reg.h b/drivers/misc/awinic/aw8697/aw8697_reg.h new file mode 100644 index 000000000000..2a8289d6106f --- /dev/null +++ b/drivers/misc/awinic/aw8697/aw8697_reg.h @@ -0,0 +1,540 @@ +#ifndef _AW8697_REG_H_ +#define _AW8697_REG_H_ + +/******************************************** + * Register List + *******************************************/ +#define AW8697_REG_ID 0x00 +#define AW8697_REG_SYSST 0x01 +#define AW8697_REG_SYSINT 0x02 +#define AW8697_REG_SYSINTM 0x03 +#define AW8697_REG_SYSCTRL 0x04 +#define AW8697_REG_GO 0x05 +#define AW8697_REG_RTP_DATA 0x06 +#define AW8697_REG_WAVSEQ1 0x07 +#define AW8697_REG_WAVSEQ2 0x08 +#define AW8697_REG_WAVSEQ3 0x09 +#define AW8697_REG_WAVSEQ4 0x0a +#define AW8697_REG_WAVSEQ5 0x0b +#define AW8697_REG_WAVSEQ6 0x0c +#define AW8697_REG_WAVSEQ7 0x0d +#define AW8697_REG_WAVSEQ8 0x0e +#define AW8697_REG_WAVLOOP1 0x0f +#define AW8697_REG_WAVLOOP2 0x10 +#define AW8697_REG_WAVLOOP3 0x11 +#define AW8697_REG_WAVLOOP4 0x12 +#define AW8697_REG_MAIN_LOOP 0x13 +#define AW8697_REG_TRG1_WAV_P 0x14 +#define AW8697_REG_TRG2_WAV_P 0x15 +#define AW8697_REG_TRG3_WAV_P 0x16 +#define AW8697_REG_TRG1_WAV_N 0x17 +#define AW8697_REG_TRG2_WAV_N 0x18 +#define AW8697_REG_TRG3_WAV_N 0x19 +#define AW8697_REG_TRG_PRIO 0x1a +#define AW8697_REG_TRG_CFG1 0x1b +#define AW8697_REG_TRG_CFG2 0x1c +#define AW8697_REG_DBGCTRL 0x20 +#define AW8697_REG_BASE_ADDRH 0x21 +#define AW8697_REG_BASE_ADDRL 0x22 +#define AW8697_REG_FIFO_AEH 0x23 +#define AW8697_REG_FIFO_AEL 0x24 +#define AW8697_REG_FIFO_AFH 0x25 +#define AW8697_REG_FIFO_AFL 0x26 +#define AW8697_REG_WAKE_DLY 0x27 +#define AW8697_REG_START_DLY 0x28 +#define AW8697_REG_END_DLY_H 0x29 +#define AW8697_REG_END_DLY_L 0x2a +#define AW8697_REG_DATCTRL 0x2b +#define AW8697_REG_PWMDEL 0x2c +#define AW8697_REG_PWMPRC 0x2d +#define AW8697_REG_PWMDBG 0x2e +#define AW8697_REG_LDOCTRL 0x2f +#define AW8697_REG_DBGSTAT 0x30 +#define AW8697_REG_BSTDBG1 0x31 +#define AW8697_REG_BSTDBG2 0x32 +#define AW8697_REG_BSTDBG3 0x33 +#define AW8697_REG_BSTCFG 0x34 +#define AW8697_REG_ANADBG 0x35 +#define AW8697_REG_ANACTRL 0x36 +#define AW8697_REG_CPDBG 0x37 +#define AW8697_REG_GLBDBG 0x38 +#define AW8697_REG_DATDBG 0x39 +#define AW8697_REG_BSTDBG4 0x3a +#define AW8697_REG_BSTDBG5 0x3b +#define AW8697_REG_BSTDBG6 0x3c +#define AW8697_REG_HDRVDBG 0x3d +#define AW8697_REG_PRLVL 0x3e +#define AW8697_REG_PRTIME 0x3f +#define AW8697_REG_RAMADDRH 0x40 +#define AW8697_REG_RAMADDRL 0x41 +#define AW8697_REG_RAMDATA 0x42 +#define AW8697_REG_GLB_STATE 0x46 +#define AW8697_REG_BST_AUTO 0x47 +#define AW8697_REG_CONT_CTRL 0x48 +#define AW8697_REG_F_PRE_H 0x49 +#define AW8697_REG_F_PRE_L 0x4a +#define AW8697_REG_TD_H 0x4b +#define AW8697_REG_TD_L 0x4c +#define AW8697_REG_TSET 0x4d +#define AW8697_REG_TRIM_LRA 0x5b +#define AW8697_REG_R_SPARE 0x5d +#define AW8697_REG_D2SCFG 0x5e +#define AW8697_REG_DETCTRL 0x5f +#define AW8697_REG_RLDET 0x60 +#define AW8697_REG_OSDET 0x61 +#define AW8697_REG_VBATDET 0x62 +#define AW8697_REG_TESTDET 0x63 +#define AW8697_REG_DETLO 0x64 +#define AW8697_REG_BEMFDBG 0x65 +#define AW8697_REG_ADCTEST 0x66 +#define AW8697_REG_BEMFTEST 0x67 +#define AW8697_REG_F_LRA_F0_H 0x68 +#define AW8697_REG_F_LRA_F0_L 0x69 +#define AW8697_REG_F_LRA_CONT_H 0x6a +#define AW8697_REG_F_LRA_CONT_L 0x6b +#define AW8697_REG_WAIT_VOL_MP 0x6d +#define AW8697_REG_WAIT_VOL_MN 0x6f +#define AW8697_REG_BEMF_VOL_H 0x70 +#define AW8697_REG_BEMF_VOL_L 0x71 +#define AW8697_REG_ZC_THRSH_H 0x72 +#define AW8697_REG_ZC_THRSH_L 0x73 +#define AW8697_REG_BEMF_VTHH_H 0x74 +#define AW8697_REG_BEMF_VTHH_L 0x75 +#define AW8697_REG_BEMF_VTHL_H 0x76 +#define AW8697_REG_BEMF_VTHL_L 0x77 +#define AW8697_REG_BEMF_NUM 0x78 +#define AW8697_REG_DRV_TIME 0x79 +#define AW8697_REG_TIME_NZC 0x7a +#define AW8697_REG_DRV_LVL 0x7b +#define AW8697_REG_DRV_LVL_OV 0x7c +#define AW8697_REG_NUM_F0_1 0x7d +#define AW8697_REG_NUM_F0_2 0x7e +#define AW8697_REG_NUM_F0_3 0x7f + +/******************************************** + * Register Access + *******************************************/ +#define REG_NONE_ACCESS 0 +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) +#define AW8697_REG_MAX 0xff + +const unsigned char aw8697_reg_access[AW8697_REG_MAX] = { + [AW8697_REG_ID] = REG_RD_ACCESS, + [AW8697_REG_SYSST] = REG_RD_ACCESS, + [AW8697_REG_SYSINT] = REG_RD_ACCESS, + [AW8697_REG_SYSINTM] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_SYSCTRL] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_GO] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_RTP_DATA] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_WAVSEQ1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_WAVSEQ2] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_WAVSEQ3] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_WAVSEQ4] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_WAVSEQ5] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_WAVSEQ6] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_WAVSEQ7] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_WAVSEQ8] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_WAVLOOP1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_WAVLOOP2] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_WAVLOOP3] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_WAVLOOP4] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_MAIN_LOOP] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_TRG1_WAV_P] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_TRG2_WAV_P] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_TRG3_WAV_P] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_TRG1_WAV_N] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_TRG2_WAV_N] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_TRG3_WAV_N] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_TRG_PRIO] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_TRG_CFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_TRG_CFG2] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_DBGCTRL] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_BASE_ADDRH] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_BASE_ADDRL] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_FIFO_AEH] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_FIFO_AEL] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_FIFO_AFH] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_FIFO_AFL] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_WAKE_DLY] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_START_DLY] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_END_DLY_H] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_END_DLY_L] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_DATCTRL] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_PWMDEL] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_PWMPRC] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_PWMDBG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_LDOCTRL] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_DBGSTAT] = REG_RD_ACCESS, + [AW8697_REG_BSTDBG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_BSTDBG2] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_BSTDBG3] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_BSTCFG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_ANADBG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_ANACTRL] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_CPDBG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_GLBDBG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_DATDBG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_BSTDBG4] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_BSTDBG5] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_BSTDBG6] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_HDRVDBG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_PRLVL] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_PRTIME] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_RAMADDRH] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_RAMADDRL] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_RAMDATA] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_GLB_STATE] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_BST_AUTO] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_CONT_CTRL] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_F_PRE_H] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_F_PRE_L] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_TD_H] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_TD_L] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_TSET] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_TRIM_LRA] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_R_SPARE] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_D2SCFG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_DETCTRL] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_RLDET] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_OSDET] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_VBATDET] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_TESTDET] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_DETLO] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_BEMFDBG] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_ADCTEST] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_BEMFTEST] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_F_LRA_F0_H] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_F_LRA_F0_L] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_F_LRA_CONT_H] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_F_LRA_CONT_L] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_WAIT_VOL_MP] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_WAIT_VOL_MN] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_BEMF_VOL_H] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_BEMF_VOL_L] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_ZC_THRSH_H] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_ZC_THRSH_L] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_BEMF_VTHH_H] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_BEMF_VTHH_L] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_BEMF_VTHL_H] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_BEMF_VTHL_L] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_BEMF_NUM] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_DRV_TIME] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_TIME_NZC] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_DRV_LVL] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_DRV_LVL_OV] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_NUM_F0_1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_NUM_F0_2] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW8697_REG_NUM_F0_3] = REG_RD_ACCESS | REG_WR_ACCESS, +}; + +/****************************************************** + * Register Detail + *****************************************************/ +/* SYSST: reg0x01 */ +#define AW8697_BIT_SYSST_BSTERRS (1<<7) +#define AW8697_BIT_SYSST_OVS (1<<6) +#define AW8697_BIT_SYSST_UVLS (1<<5) +#define AW8697_BIT_SYSST_FF_AES (1<<4) +#define AW8697_BIT_SYSST_FF_AFS (1<<3) +#define AW8697_BIT_SYSST_OCDS (1<<2) +#define AW8697_BIT_SYSST_OTS (1<<1) +#define AW8697_BIT_SYSST_DONES (1<<0) + +/* SYSINT: reg0x02 */ +#define AW8697_BIT_SYSINT_BSTERRI (1<<7) +#define AW8697_BIT_SYSINT_OVI (1<<6) +#define AW8697_BIT_SYSINT_UVLI (1<<5) +#define AW8697_BIT_SYSINT_FF_AEI (1<<4) +#define AW8697_BIT_SYSINT_FF_AFI (1<<3) +#define AW8697_BIT_SYSINT_OCDI (1<<2) +#define AW8697_BIT_SYSINT_OTI (1<<1) +#define AW8697_BIT_SYSINT_DONEI (1<<0) + +/* SYSINTM: reg0x03 */ +#define AW8697_BIT_SYSINTM_BSTERR_MASK (~(1<<7)) +#define AW8697_BIT_SYSINTM_BSTERR_OFF (1<<7) +#define AW8697_BIT_SYSINTM_BSTERR_EN (0<<7) +#define AW8697_BIT_SYSINTM_OV_MASK (~(1<<6)) +#define AW8697_BIT_SYSINTM_OV_OFF (1<<6) +#define AW8697_BIT_SYSINTM_OV_EN (0<<6) +#define AW8697_BIT_SYSINTM_UVLO_MASK (~(1<<5)) +#define AW8697_BIT_SYSINTM_UVLO_OFF (1<<5) +#define AW8697_BIT_SYSINTM_UVLO_EN (0<<5) +#define AW8697_BIT_SYSINTM_FF_AE_MASK (~(1<<4)) +#define AW8697_BIT_SYSINTM_FF_AE_OFF (1<<4) +#define AW8697_BIT_SYSINTM_FF_AE_EN (0<<4) +#define AW8697_BIT_SYSINTM_FF_AF_MASK (~(1<<3)) +#define AW8697_BIT_SYSINTM_FF_AF_OFF (1<<3) +#define AW8697_BIT_SYSINTM_FF_AF_EN (0<<3) +#define AW8697_BIT_SYSINTM_OCD_MASK (~(1<<2)) +#define AW8697_BIT_SYSINTM_OCD_OFF (1<<2) +#define AW8697_BIT_SYSINTM_OCD_EN (0<<2) +#define AW8697_BIT_SYSINTM_OT_MASK (~(1<<1)) +#define AW8697_BIT_SYSINTM_OT_OFF (1<<1) +#define AW8697_BIT_SYSINTM_OT_EN (0<<1) +#define AW8697_BIT_SYSINTM_DONE_MASK (~(1<<0)) +#define AW8697_BIT_SYSINTM_DONE_OFF (1<<0) +#define AW8697_BIT_SYSINTM_DONE_EN (0<<0) + +/* SYSCTRL: reg0x04 */ +#define AW8697_BIT_SYSCTRL_WAVDAT_MODE_MASK (~(3<<6)) +#define AW8697_BIT_SYSCTRL_WAVDAT_MODE_4X (3<<6) +#define AW8697_BIT_SYSCTRL_WAVDAT_MODE_2X (0<<6) +#define AW8697_BIT_SYSCTRL_WAVDAT_MODE_1X (1<<6) +#define AW8697_BIT_SYSCTRL_RAMINIT_MASK (~(1<<5)) +#define AW8697_BIT_SYSCTRL_RAMINIT_EN (1<<5) +#define AW8697_BIT_SYSCTRL_RAMINIT_OFF (0<<5) +#define AW8697_BIT_SYSCTRL_PLAY_MODE_MASK (~(3<<2)) +#define AW8697_BIT_SYSCTRL_PLAY_MODE_CONT (2<<2) +#define AW8697_BIT_SYSCTRL_PLAY_MODE_RTP (1<<2) +#define AW8697_BIT_SYSCTRL_PLAY_MODE_RAM (0<<2) +#define AW8697_BIT_SYSCTRL_BST_MODE_MASK (~(1<<1)) +#define AW8697_BIT_SYSCTRL_BST_MODE_BOOST (1<<1) +#define AW8697_BIT_SYSCTRL_BST_MODE_BYPASS (0<<1) +#define AW8697_BIT_SYSCTRL_WORK_MODE_MASK (~(1<<0)) +#define AW8697_BIT_SYSCTRL_STANDBY (1<<0) +#define AW8697_BIT_SYSCTRL_ACTIVE (0<<0) + +/* GO: reg0x05 */ +#define AW8697_BIT_GO_MASK (~(1<<0)) +#define AW8697_BIT_GO_ENABLE (1<<0) +#define AW8697_BIT_GO_DISABLE (0<<0) + +/* WAVSEQ1: reg0x07 */ +#define AW8697_BIT_WAVSEQ1_WAIT (1<<7) +#define AW8697_BIT_WAVSEQ1_WAV_FRM_SEQ1_MASK (~(127<<0)) + +/* WAVSEQ2: reg0x08 */ +#define AW8697_BIT_WAVSEQ2_WAIT (1<<7) +#define AW8697_BIT_WAVSEQ2_WAV_FRM_SEQ2_MASK (~(127<<0)) + +/* WAVSEQ3: reg0x09 */ +#define AW8697_BIT_WAVSEQ3_WAIT (1<<7) +#define AW8697_BIT_WAVSEQ3_WAV_FRM_SEQ3_MASK (~(127<<0)) + +/* WAVSEQ4: reg0x0a */ +#define AW8697_BIT_WAVSEQ4_WAIT (1<<7) +#define AW8697_BIT_WAVSEQ4_WAV_FRM_SEQ4_MASK (~(127<<0)) + +/* WAVSEQ5: reg0x0b */ +#define AW8697_BIT_WAVSEQ5_WAIT (1<<7) +#define AW8697_BIT_WAVSEQ5_WAV_FRM_SEQ5_MASK (~(127<<0)) + +/* WAVSEQ6: reg0x0c */ +#define AW8697_BIT_WAVSEQ6_WAIT (1<<7) +#define AW8697_BIT_WAVSEQ6_WAV_FRM_SEQ6_MASK (~(127<<0)) + +/* WAVSEQ7: reg0x0d */ +#define AW8697_BIT_WAVSEQ7_WAIT (1<<7) +#define AW8697_BIT_WAVSEQ7_WAV_FRM_SEQ7_MASK (~(127<<0)) + +/* WAVSEQ8: reg0x0e */ +#define AW8697_BIT_WAVSEQ8_WAIT (1<<7) +#define AW8697_BIT_WAVSEQ8_WAV_FRM_SEQ8_MASK (~(127<<0)) + +/* WAVLOOP: */ +#define AW8697_BIT_WAVLOOP_SEQN_MASK (~(15<<4)) +#define AW8697_BIT_WAVLOOP_SEQNP1_MASK (~(15<<0)) +#define AW8697_BIT_WAVLOOP_INIFINITELY (15<<0) + +/* WAVLOOP1: reg0x0f */ +#define AW8697_BIT_WAVLOOP1_SEQ1_MASK (~(15<<4)) +#define AW8697_BIT_WAVLOOP1_SEQ2_MASK (~(15<<0)) + +/* WAVLOOP2: reg0x10 */ +#define AW8697_BIT_WAVLOOP2_SEQ3_MASK (~(15<<4)) +#define AW8697_BIT_WAVLOOP2_SEQ4_MASK (~(15<<0)) + +/* WAVLOOP3: reg0x11 */ +#define AW8697_BIT_WAVLOOP3_SEQ5_MASK (~(15<<4)) +#define AW8697_BIT_WAVLOOP3_SEQ6_MASK (~(15<<0)) + +/* WAVLOOP4: reg0x12 */ +#define AW8697_BIT_WAVLOOP4_SEQ7_MASK (~(15<<4)) +#define AW8697_BIT_WAVLOOP4_SEQ8_MASK (~(15<<0)) + +/* PLAYPRIO: reg0x1a */ +#define AW8697_BIT_PLAYPRIO_GO_MASK (~(3<<6)) +#define AW8697_BIT_PLAYPRIO_TRIG3_MASK (~(3<<4)) +#define AW8697_BIT_PLAYPRIO_TRIG2_MASK (~(3<<2)) +#define AW8697_BIT_PLAYPRIO_TRIG1_MASK (~(3<<0)) + +/* TRGCFG1: reg0x1b */ +#define AW8697_BIT_TRGCFG1_TRG3_POLAR_MASK (~(1<<5)) +#define AW8697_BIT_TRGCFG1_TRG3_POLAR_NEG (1<<5) +#define AW8697_BIT_TRGCFG1_TRG3_POLAR_POS (0<<5) +#define AW8697_BIT_TRGCFG1_TRG3_EDGE_MASK (~(1<<4)) +#define AW8697_BIT_TRGCFG1_TRG3_EDGE_POS (1<<4) +#define AW8697_BIT_TRGCFG1_TRG3_EDGE_POS_NEG (0<<4) +#define AW8697_BIT_TRGCFG1_TRG2_POLAR_MASK (~(1<<3)) +#define AW8697_BIT_TRGCFG1_TRG2_POLAR_NEG (1<<3) +#define AW8697_BIT_TRGCFG1_TRG2_POLAR_POS (0<<3) +#define AW8697_BIT_TRGCFG1_TRG2_EDGE_MASK (~(1<<2)) +#define AW8697_BIT_TRGCFG1_TRG2_EDGE_POS (1<<2) +#define AW8697_BIT_TRGCFG1_TRG2_EDGE_POS_NEG (0<<2) +#define AW8697_BIT_TRGCFG1_TRG1_POLAR_MASK (~(1<<1)) +#define AW8697_BIT_TRGCFG1_TRG1_POLAR_NEG (1<<1) +#define AW8697_BIT_TRGCFG1_TRG1_POLAR_POS (0<<1) +#define AW8697_BIT_TRGCFG1_TRG1_EDGE_MASK (~(1<<0)) +#define AW8697_BIT_TRGCFG1_TRG1_EDGE_POS (1<<0) +#define AW8697_BIT_TRGCFG1_TRG1_EDGE_POS_NEG (0<<0) + +/* TRGCFG2: reg0x1c */ +#define AW8697_BIT_TRGCFG2_TRG3_ENABLE_MASK (~(1<<2)) +#define AW8697_BIT_TRGCFG2_TRG3_ENABLE (1<<2) +#define AW8697_BIT_TRGCFG2_TRG3_DISABLE (0<<2) +#define AW8697_BIT_TRGCFG2_TRG2_ENABLE_MASK (~(1<<1)) +#define AW8697_BIT_TRGCFG2_TRG2_ENABLE (1<<1) +#define AW8697_BIT_TRGCFG2_TRG2_DISABLE (0<<1) +#define AW8697_BIT_TRGCFG2_TRG1_ENABLE_MASK (~(1<<0)) +#define AW8697_BIT_TRGCFG2_TRG1_ENABLE (1<<0) +#define AW8697_BIT_TRGCFG2_TRG1_DISABLE (0<<0) + +/* DBGCTRL: reg0x20 */ +#define AW8697_BIT_DBGCTRL_INT_EDGE_MODE_MASK (~(1<<3)) +#define AW8697_BIT_DBGCTRL_INT_EDGE_MODE_POS (1<<3) +#define AW8697_BIT_DBGCTRL_INT_EDGE_MODE_BOTH (0<<3) +#define AW8697_BIT_DBGCTRL_INT_MODE_MASK (~(1<<2)) +#define AW8697_BIT_DBGCTRL_INT_MODE_EDGE (1<<2) +#define AW8697_BIT_DBGCTRL_INT_MODE_LEVEL (0<<2) + +/* DATCTRL: reg0x2b */ +#define AW8697_BIT_DATCTRL_FC_MASK (~(1<<6)) +#define AW8697_BIT_DATCTRL_FC_1000HZ (3<<6) +#define AW8697_BIT_DATCTRL_FC_800HZ (3<<6) +#define AW8697_BIT_DATCTRL_FC_600HZ (1<<6) +#define AW8697_BIT_DATCTRL_FC_400HZ (0<<6) +#define AW8697_BIT_DATCTRL_LPF_ENABLE_MASK (~(1<<5)) +#define AW8697_BIT_DATCTRL_LPF_ENABLE (1<<5) +#define AW8697_BIT_DATCTRL_LPF_DISABLE (0<<5) +#define AW8697_BIT_DATCTRL_WAKEMODE_ENABLE_MASK (~(1<<0)) +#define AW8697_BIT_DATCTRL_WAKEMODE_ENABLE (1<<0) +#define AW8697_BIT_DATCTRL_WAKEMODE_DISABLE (0<<0) + +/* PWMPRC: reg0x2d */ +#define AW8697_BIT_PWMPRC_PRC_MASK (~(1<<7)) +#define AW8697_BIT_PWMPRC_PRC_ENABLE (1<<7) +#define AW8697_BIT_PWMPRC_PRC_DISABLE (0<<7) +#define AW8697_BIT_PWMPRC_PRCTIME_MASK (~(0x7f<<0)) + +/* PWMDBG: reg0x2e */ +#define AW8697_BIT_PWMDBG_PWM_MODE_MASK (~(3<<5)) +#define AW8697_BIT_PWMDBG_PWM_12K (3<<5) +#define AW8697_BIT_PWMDBG_PWM_24K (2<<5) +#define AW8697_BIT_PWMDBG_PWM_48K (0<<5) + +/* DBGST: reg0x30 */ +#define AW8697_BIT_DBGSTAT_FF_EMPTY (1<<0) + +/* BSTCFG: reg0x34 */ +#define AW8697_BIT_BSTCFG_PEAKCUR_MASK (~(7<<0)) +#define AW8697_BIT_BSTCFG_PEAKCUR_4A (7<<0) +#define AW8697_BIT_BSTCFG_PEAKCUR_3P75A (6<<0) +#define AW8697_BIT_BSTCFG_PEAKCUR_3P5A (5<<0) +#define AW8697_BIT_BSTCFG_PEAKCUR_3P25A (4<<0) +#define AW8697_BIT_BSTCFG_PEAKCUR_3A (3<<0) +#define AW8697_BIT_BSTCFG_PEAKCUR_2P5A (2<<0) +#define AW8697_BIT_BSTCFG_PEAKCUR_2A (1<<0) +#define AW8697_BIT_BSTCFG_PEAKCUR_1P5A (0<<0) + +/* ANADBG: reg0x35 */ +#define AW8697_BIT_ANADBG_IOC_MASK (~(3<<2)) +#define AW8697_BIT_ANADBG_IOC_4P65A (3<<2) +#define AW8697_BIT_ANADBG_IOC_4P15A (2<<2) +#define AW8697_BIT_ANADBG_IOC_3P65A (1<<2) +#define AW8697_BIT_ANADBG_IOC_3P15A (0<<2) + +/* ANACTRL: reg0x36 */ +#define AW8697_BIT_ANACTRL_LRA_SRC_MASK (~(1<<5)) +#define AW8697_BIT_ANACTRL_LRA_SRC_REG (1<<5) +#define AW8697_BIT_ANACTRL_LRA_SRC_EFUSE (0<<5) +#define AW8697_BIT_ANACTRL_HD_PD_MASK (~(1<<3)) +#define AW8697_BIT_ANACTRL_HD_PD_EN (1<<3) +#define AW8697_BIT_ANACTRL_HD_HZ_EN (0<<3) + +/* BSTDBG4: reg0x3a */ +#define AW8697_BIT_BSTDBG4_BSTVOL_MASK (~(31<<1)) + +/* PRLVL: reg0x3e */ +#define AW8697_BIT_PRLVL_PR_MASK (~(1<<7)) +#define AW8697_BIT_PRLVL_PR_ENABLE (1<<7) +#define AW8697_BIT_PRLVL_PR_DISABLE (0<<7) +#define AW8697_BIT_PRLVL_PRLVL_MASK (~(0x7f<<0)) + +/*PRTIME: reg0x3f */ +#define AW8697_BIT_PRTIME_PRTIME_MASK (~(0xff<<0)) + +/* BST_AUTO: reg0x47 */ +#define AW8697_BIT_BST_AUTO_BST_AUTOSW_MASK (~(1<<2)) +#define AW8697_BIT_BST_AUTO_BST_AUTOMATIC_BOOST (1<<2) +#define AW8697_BIT_BST_AUTO_BST_MANUAL_BOOST (0<<2) +#define AW8697_BIT_BST_AUTO_BST_RTP_MASK (~(1<<1)) +#define AW8697_BIT_BST_AUTO_BST_RTP_ENABLE (1<<1) +#define AW8697_BIT_BST_AUTO_BST_RTP_DISABLE (0<<1) +#define AW8697_BIT_BST_AUTO_BST_RAM_MASK (~(1<<0)) +#define AW8697_BIT_BST_AUTO_BST_RAM_ENABLE (1<<0) +#define AW8697_BIT_BST_AUTO_BST_RAM_DISABLE (0<<0) + +/* CONT_CTRL: reg0x48 */ +#define AW8697_BIT_CONT_CTRL_ZC_DETEC_MASK (~(1<<7)) +#define AW8697_BIT_CONT_CTRL_ZC_DETEC_ENABLE (1<<7) +#define AW8697_BIT_CONT_CTRL_ZC_DETEC_DISABLE (0<<7) +#define AW8697_BIT_CONT_CTRL_WAIT_PERIOD_MASK (~(3<<5)) +#define AW8697_BIT_CONT_CTRL_WAIT_8PERIOD (3<<5) +#define AW8697_BIT_CONT_CTRL_WAIT_4PERIOD (2<<5) +#define AW8697_BIT_CONT_CTRL_WAIT_2PERIOD (1<<5) +#define AW8697_BIT_CONT_CTRL_WAIT_1PERIOD (0<<5) +#define AW8697_BIT_CONT_CTRL_MODE_MASK (~(1<<4)) +#define AW8697_BIT_CONT_CTRL_BY_DRV_TIME (1<<4) +#define AW8697_BIT_CONT_CTRL_BY_GO_SIGNAL (0<<4) +#define AW8697_BIT_CONT_CTRL_EN_CLOSE_MASK (~(1<<3)) +#define AW8697_BIT_CONT_CTRL_CLOSE_PLAYBACK (1<<3) +#define AW8697_BIT_CONT_CTRL_OPEN_PLAYBACK (0<<3) +#define AW8697_BIT_CONT_CTRL_F0_DETECT_MASK (~(1<<2)) +#define AW8697_BIT_CONT_CTRL_F0_DETECT_ENABLE (1<<2) +#define AW8697_BIT_CONT_CTRL_F0_DETECT_DISABLE (0<<2) +#define AW8697_BIT_CONT_CTRL_O2C_MASK (~(1<<1)) +#define AW8697_BIT_CONT_CTRL_O2C_ENABLE (1<<1) +#define AW8697_BIT_CONT_CTRL_O2C_DISABLE (0<<1) +#define AW8697_BIT_CONT_CTRL_AUTO_BRK_MASK (~(1<<0)) +#define AW8697_BIT_CONT_CTRL_AUTO_BRK_ENABLE (1<<0) +#define AW8697_BIT_CONT_CTRL_AUTO_BRK_DISABLE (0<<0) + +/* D2SCFG: reg0x5e */ +#define AW8697_BIT_D2SCFG_CLK_ADC_MASK (~(7<<5)) +#define AW8697_BIT_D2SCFG_CLK_ASC_0P09375MHZ (7<<5) +#define AW8697_BIT_D2SCFG_CLK_ASC_0P1875MHZ (6<<5) +#define AW8697_BIT_D2SCFG_CLK_ASC_0P375MHZ (5<<5) +#define AW8697_BIT_D2SCFG_CLK_ASC_0P75MHZ (4<<5) +#define AW8697_BIT_D2SCFG_CLK_ASC_1P5MHZ (3<<5) +#define AW8697_BIT_D2SCFG_CLK_ASC_3MHZ (2<<5) +#define AW8697_BIT_D2SCFG_CLK_ASC_6MHZ (1<<5) +#define AW8697_BIT_D2SCFG_CLK_ASC_12MHZ (0<<5) + +/* DETCTRL: reg0x5f */ +#define AW8697_BIT_DETCTRL_RL_OS_MASK (~(1<<6)) +#define AW8697_BIT_DETCTRL_RL_DETECT (1<<6) +#define AW8697_BIT_DETCTRL_OS_DETECT (0<<6) +#define AW8697_BIT_DETCTRL_PROTECT_MASK (~(1<<5)) +#define AW8697_BIT_DETCTRL_PROTECT_NO_ACTION (1<<5) +#define AW8697_BIT_DETCTRL_PROTECT_SHUTDOWN (0<<5) +#define AW8697_BIT_DETCTRL_ADO_SLOT_MODE_MASK (~(1<<4)) +#define AW8697_BIT_DETCTRL_ADO_SLOT_MODE_ENABLE (1<<4) +#define AW8697_BIT_DETCTRL_ADO_SLOT_MODE_DISABLE (0<<4) +#define AW8697_BIT_DETCTRL_VBAT_GO_MASK (~(1<<1)) +#define AW8697_BIT_DETCTRL_VABT_GO_ENABLE (1<<1) +#define AW8697_BIT_DETCTRL_VBAT_GO_DISBALE (0<<1) +#define AW8697_BIT_DETCTRL_DIAG_GO_MASK (~(1<<0)) +#define AW8697_BIT_DETCTRL_DIAG_GO_ENABLE (1<<0) +#define AW8697_BIT_DETCTRL_DIAG_GO_DISABLE (0<<0) + +/* ADCTEST: reg0x66 */ +#define AW8697_BIT_ADCTEST_VBAT_MODE_MASK (~(1<<6)) +#define AW8697_BIT_ADCTEST_VBAT_HW_COMP (1<<6) +#define AW8697_BIT_ADCTEST_VBAT_SW_COMP (0<<6) + +/* BEMF_NUM: reg0x78 */ +#define AW8697_BIT_BEMF_NUM_BRK_MASK (~(15<<0)) + +#endif diff --git a/drivers/misc/awinic/aw869x/Android.mk b/drivers/misc/awinic/aw869x/Android.mk new file mode 100644 index 000000000000..d675aaeab2f6 --- /dev/null +++ b/drivers/misc/awinic/aw869x/Android.mk @@ -0,0 +1,18 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := aw869x.ko +LOCAL_MODULE_TAGS := optional + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +ifeq ($(PRODUCT_HAS_AWINIC_HAPTIC),true) + KERNEL_CFLAGS += CONFIG_INPUT_AWINIC_HAPTIC=y +endif + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/awinic/aw869x/Kbuild b/drivers/misc/awinic/aw869x/Kbuild new file mode 100644 index 000000000000..1f6fbda6387d --- /dev/null +++ b/drivers/misc/awinic/aw869x/Kbuild @@ -0,0 +1,9 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +ifneq ($(filter m y,$(CONFIG_INPUT_AWINIC_HAPTIC)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_AWINIC_HAPTIC +endif + +obj-m += aw869x.o diff --git a/drivers/misc/awinic/aw869x/Makefile b/drivers/misc/awinic/aw869x/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/misc/awinic/aw869x/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/misc/awinic/aw869x/aw869x.c b/drivers/misc/awinic/aw869x/aw869x.c new file mode 100644 index 000000000000..31be64d68adb --- /dev/null +++ b/drivers/misc/awinic/aw869x/aw869x.c @@ -0,0 +1,2759 @@ +/* + * aw869x.c aw869x haptic module + * + * Version: v1.4.0 + * + * Copyright (c) 2017 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw869x.h" +#include "aw869x_reg.h" + +/****************************************************** + * + * Marco + * + ******************************************************/ +#define AW869X_I2C_NAME "aw869x_haptic" +#define AW869X_HAPTIC_NAME "aw869x_haptic" + +#define AW869X_VERSION "v1.4.0" + + +//#define AWINIC_I2C_REGMAP +//#define AWINIC_RAM_UPDATE_DELAY + +#define AW_I2C_RETRIES 2 +#define AW_I2C_RETRY_DELAY 2 +#define AW_READ_CHIPID_RETRIES 5 +#define AW_READ_CHIPID_RETRY_DELAY 2 + +#define AW869X_MAX_DSP_START_TRY_COUNT 10 + + +//#define AW869X_REPEAT_RTP_PLAYING +#define AW869X_MAX_FIRMWARE_LOAD_CNT 20 +#define AW869X_SEQ_NO_RTP_BASE 102 +/****************************************************** + * + * variable + * + ******************************************************/ +#define AW869X_RTP_NAME_MAX 64 +static char *aw869x_ram_name = "aw869x_haptic.bin"; +static char aw869x_rtp_name[][AW869X_RTP_NAME_MAX] = { + {"aw869x_rtp.bin"}, + {"aw869x_rtp_Argo_Navis.bin"}, + {"aw869x_rtp_Attentive.bin"}, + {"aw869x_rtp_Awake.bin"}, + {"aw869x_rtp_Bird_Loop.bin"}, + {"aw869x_rtp_Brilliant_Times.bin"}, + {"aw869x_rtp_Chimey_Phone.bin"}, + {"aw869x_rtp_Complex.bin"}, + {"aw869x_rtp_Crazy_Dream.bin"}, + {"aw869x_rtp_Curve_Ball_Blend.bin"}, + {"aw869x_rtp_Digital_Phone.bin"}, + {"aw869x_rtp_Electrovision.bin"}, + {"aw869x_rtp_Ether_Shake.bin"}, + {"aw869x_rtp_Fateful_Words.bin"}, + {"aw869x_rtp_Flutey_Phone.bin"}, + {"aw869x_rtp_Future_Funk.bin"}, + {"aw869x_rtp_Future_Hi_Tech.bin"}, + {"aw869x_rtp_Girtab.bin"}, + {"aw869x_rtp_Hello.bin"}, + {"aw869x_rtp_Hexagon.bin"}, + {"aw869x_rtp_Hydra.bin"}, + {"aw869x_rtp_Insert_Coin.bin"}, + {"aw869x_rtp_Jumping_Dots.bin"}, + {"aw869x_rtp_Keys.bin"}, + {"aw869x_rtp_Loopy.bin"}, + {"aw869x_rtp_Loopy_Lounge.bin"}, + {"aw869x_rtp_Modular.bin"}, + {"aw869x_rtp_Momentum.bin"}, + {"aw869x_rtp_Morning.bin"}, + {"aw869x_rtp_Moto.bin"}, + {"aw869x_rtp_Natural.bin"}, + {"aw869x_rtp_New_Player.bin"}, + {"aw869x_rtp_Onward.bin"}, + {"aw869x_rtp_Organ_Dub.bin"}, + {"aw869x_rtp_Overclocked.bin"}, + {"aw869x_rtp_Pegasus.bin"}, + {"aw869x_rtp_Pyxis.bin"}, + {"aw869x_rtp_Regrade.bin"}, + {"aw869x_rtp_Scarabaeus.bin"}, + {"aw869x_rtp_Sceptrum.bin"}, + {"aw869x_rtp_Simple.bin"}, + {"aw869x_rtp_Solarium.bin"}, + {"aw869x_rtp_Sparse.bin"}, + {"aw869x_rtp_Terrabytes.bin"}, + {"aw869x_rtp_TJINGLE.bin"}, +}; + +struct aw869x_container *aw869x_rtp; +struct aw869x *g_aw869x; + +/****************************************************** + * + * functions + * + ******************************************************/ +static void aw869x_interrupt_clear(struct aw869x *aw869x); +static void aw869x_vibrate(struct aw869x *aw869x, int value); + + /****************************************************** + * + * aw869x i2c write/read + * + ******************************************************/ +static int aw869x_i2c_write(struct aw869x *aw869x, + unsigned char reg_addr, unsigned char reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + while(cnt < AW_I2C_RETRIES) { +#ifdef AWINIC_I2C_REGMAP + ret = regmap_write(aw869x->regmap, reg_addr, reg_data); + if(ret < 0) { + pr_err("%s: regmap_write cnt=%d error=%d\n", __func__, cnt, ret); + } else { + break; + } +#else + ret = i2c_smbus_write_byte_data(aw869x->i2c, reg_addr, reg_data); + if(ret < 0) { + pr_err("%s: i2c_write cnt=%d error=%d\n", __func__, cnt, ret); + } else { + break; + } +#endif + cnt ++; + msleep(AW_I2C_RETRY_DELAY); + } + + return ret; +} + +static int aw869x_i2c_read(struct aw869x *aw869x, + unsigned char reg_addr, unsigned char *reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + while(cnt < AW_I2C_RETRIES) { +#ifdef AWINIC_I2C_REGMAP + ret = regmap_read(aw869x->regmap, reg_addr, reg_data); + if(ret < 0) { + pr_err("%s: regmap_read cnt=%d error=%d\n", __func__, cnt, ret); + } else { + break; + } +#else + ret = i2c_smbus_read_byte_data(aw869x->i2c, reg_addr); + if(ret < 0) { + pr_err("%s: i2c_read cnt=%d error=%d\n", __func__, cnt, ret); + } else { + *reg_data = ret; + break; + } +#endif + cnt ++; + msleep(AW_I2C_RETRY_DELAY); + } + + return ret; +} + +static int aw869x_i2c_write_bits(struct aw869x *aw869x, + unsigned char reg_addr, unsigned int mask, unsigned char reg_data) +{ + unsigned char reg_val = 0; + + aw869x_i2c_read(aw869x, reg_addr, ®_val); + reg_val &= mask; + reg_val |= reg_data; + aw869x_i2c_write(aw869x, reg_addr, reg_val); + + return 0; +} + +static int aw869x_i2c_writes(struct aw869x *aw869x, + unsigned char reg_addr, unsigned char *buf, unsigned int len) +{ + int ret = -1; + unsigned char *data; + + data = kmalloc(len+1, GFP_KERNEL); + if (data == NULL) { + pr_err("%s: can not allocate memory\n", __func__); + return -ENOMEM; + } + + data[0] = reg_addr; + memcpy(&data[1], buf, len); + + ret = i2c_master_send(aw869x->i2c, data, len+1); + if (ret < 0) { + pr_err("%s: i2c master send error\n", __func__); + } + + kfree(data); + + return ret; +} + +/***************************************************** + * + * ram update + * + *****************************************************/ +static void aw869x_rtp_loaded(const struct firmware *cont, void *context) +{ + struct aw869x *aw869x = context; + pr_debug("%s enter\n", __func__); + + if (!cont) { + pr_err("%s: failed to read %s\n", __func__, aw869x_rtp_name[aw869x->rtp_file_num]); + release_firmware(cont); + return; + } + + pr_debug("%s: loaded %s - size: %zu\n", __func__, aw869x_rtp_name[aw869x->rtp_file_num], + cont ? cont->size : 0); + + /* aw869x rtp update */ + aw869x_rtp = kzalloc(cont->size+sizeof(int), GFP_KERNEL); + if (!aw869x_rtp) { + release_firmware(cont); + pr_err("%s: Error allocating memory\n", __func__); + return; + } + aw869x_rtp->len = cont->size; + pr_debug("%s: rtp size = %d\n", __func__, aw869x_rtp->len); + memcpy(aw869x_rtp->data, cont->data, cont->size); + release_firmware(cont); + + aw869x->rtp_init = 1; + pr_debug("%s: rtp update complete\n", __func__); + + /* Vibrate for 1 second as TI drv2624's auto-calibration does */ + aw869x_vibrate(aw869x, 1000); +} + +static int aw869x_rtp_update(struct aw869x *aw869x) +{ + pr_debug("%s enter\n", __func__); + + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw869x_rtp_name[aw869x->rtp_file_num], aw869x->dev, GFP_KERNEL, + aw869x, aw869x_rtp_loaded); +} + + + static void aw869x_container_update(struct aw869x *aw869x, + struct aw869x_container *aw869x_cont) +{ + int i = 0; + unsigned int shift = 0; + + pr_debug("%s enter\n", __func__); + + aw869x->ram.baseaddr_shift = 2; + aw869x->ram.ram_shift = 4; + + /* RAMINIT Enable */ + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_RAMINIT_MASK, AW869X_BIT_SYSCTRL_RAMINIT_EN); + + /* base addr */ + shift = aw869x->ram.baseaddr_shift; + aw869x->ram.base_addr = (unsigned int)((aw869x_cont->data[0+shift]<<8) | + (aw869x_cont->data[1+shift])); + pr_debug("%s: base_addr=0x%4x\n", __func__, aw869x->ram.base_addr); + + aw869x_i2c_write(aw869x, AW869X_REG_BASE_ADDRH, aw869x_cont->data[0+shift]); + aw869x_i2c_write(aw869x, AW869X_REG_BASE_ADDRL, aw869x_cont->data[1+shift]); + + aw869x_i2c_write(aw869x, AW869X_REG_FIFO_AEH, + (unsigned char)((aw869x->ram.base_addr>>2)>>8)); + aw869x_i2c_write(aw869x, AW869X_REG_FIFO_AEL, + (unsigned char)((aw869x->ram.base_addr>>2)&0x00FF)); + aw869x_i2c_write(aw869x, AW869X_REG_FIFO_AFH, + (unsigned char)((aw869x->ram.base_addr-(aw869x->ram.base_addr>>2))>>8)); + aw869x_i2c_write(aw869x, AW869X_REG_FIFO_AFL, + (unsigned char)((aw869x->ram.base_addr-(aw869x->ram.base_addr>>2))&0x00FF)); + + /* ram */ + shift = aw869x->ram.baseaddr_shift; + aw869x_i2c_write(aw869x, AW869X_REG_RAMADDRH, aw869x_cont->data[0+shift]); + aw869x_i2c_write(aw869x, AW869X_REG_RAMADDRL, aw869x_cont->data[1+shift]); + shift = aw869x->ram.ram_shift; + for(i=shift; ilen; i++) { + aw869x_i2c_write(aw869x, AW869X_REG_RAMDATA, aw869x_cont->data[i]); + } + +#if 0 + /* ram check */ + shift = aw869x->ram.baseaddr_shift; + aw869x_i2c_write(aw869x, AW869X_REG_RAMADDRH, aw869x_cont->data[0+shift]); + aw869x_i2c_write(aw869x, AW869X_REG_RAMADDRL, aw869x_cont->data[1+shift]); + shift = aw869x->ram.ram_shift; + for(i=shift; ilen; i++) { + aw869x_i2c_read(aw869x, AW869X_REG_RAMDATA, ®_val); + if(reg_val != aw869x_cont->data[i]) { + pr_err("%s: ram check error addr=0x%04x, file_data=0x%02x, ram_data=0x%02x\n", + __func__, i, aw869x_cont->data[i], reg_val); + return; + } + } +#endif + + /* RAMINIT Disable */ + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_RAMINIT_MASK, AW869X_BIT_SYSCTRL_RAMINIT_OFF); + + pr_debug("%s exit\n", __func__); +} + + +static void aw869x_ram_loaded(const struct firmware *cont, void *context) +{ + struct aw869x *aw869x = context; + struct aw869x_container *aw869x_fw; + int i = 0; + unsigned short check_sum = 0; + + pr_debug("%s enter\n", __func__); + + if (!cont) { + pr_err("%s: failed to read %s\n", __func__, aw869x_ram_name); + release_firmware(cont); + return; + } + + pr_debug("%s: loaded %s - size: %zu\n", __func__, aw869x_ram_name, + cont ? cont->size : 0); +/* + for(i=0; isize; i++) { + pr_info("%s: addr:0x%04x, data:0x%02x\n", __func__, i, *(cont->data+i)); + } +*/ + + /* check sum */ + for(i=2; isize; i++) { + check_sum += cont->data[i]; + } + if(check_sum != (unsigned short)((cont->data[0]<<8)|(cont->data[1]))) { + pr_err("%s: check sum err: check_sum=0x%04x\n", __func__, check_sum); + return; + } else { + pr_debug("%s: check sum pass : 0x%04x\n", __func__, check_sum); + aw869x->ram.check_sum = check_sum; + } + + /* aw869x ram update */ + aw869x_fw = kzalloc(cont->size+sizeof(int), GFP_KERNEL); + if (!aw869x_fw) { + release_firmware(cont); + pr_err("%s: Error allocating memory\n", __func__); + return; + } + aw869x_fw->len = cont->size; + memcpy(aw869x_fw->data, cont->data, cont->size); + release_firmware(cont); + + aw869x_container_update(aw869x, aw869x_fw); + + aw869x->ram.len = aw869x_fw->len; + + kfree(aw869x_fw); + + aw869x->ram_init = 1; + pr_debug("%s: fw update complete\n", __func__); + + aw869x_rtp_update(aw869x); +} + +static int aw869x_ram_update(struct aw869x *aw869x) +{ + aw869x->ram_init = 0; + aw869x->rtp_init = 0; + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw869x_ram_name, aw869x->dev, GFP_KERNEL, + aw869x, aw869x_ram_loaded); +} + +#ifdef AWINIC_RAM_UPDATE_DELAY +static enum hrtimer_restart aw869x_ram_timer_func(struct hrtimer *timer) +{ + struct aw869x *aw869x = container_of(timer, struct aw869x, ram_timer); + + pr_info("%s enter\n", __func__); + + schedule_work(&aw869x->ram_work); + + return HRTIMER_NORESTART; +} + +static void aw869x_ram_work_routine(struct work_struct *work) +{ + struct aw869x *aw869x = container_of(work, struct aw869x, ram_work); + + pr_info("%s enter\n", __func__); + + aw869x_ram_update(aw869x); + +} +#endif + +static int aw869x_ram_init(struct aw869x *aw869x) +{ +#ifdef AWINIC_RAM_UPDATE_DELAY + int ram_timer_val = 5000; + + hrtimer_init(&aw869x->ram_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + aw869x->ram_timer.function = aw869x_ram_timer_func; + INIT_WORK(&aw869x->ram_work, aw869x_ram_work_routine); + hrtimer_start(&aw869x->ram_timer, + ktime_set(ram_timer_val/1000, (ram_timer_val%1000)*1000000), + HRTIMER_MODE_REL); +#else + aw869x_ram_update(aw869x); +#endif + return 0; +} + + + +/***************************************************** + * + * haptic control + * + *****************************************************/ +static int aw869x_haptic_play_mode(struct aw869x *aw869x, unsigned char play_mode) +{ + switch(play_mode) { + case AW869X_HAPTIC_STANDBY_MODE: + aw869x->play_mode = AW869X_HAPTIC_STANDBY_MODE; + break; + case AW869X_HAPTIC_RAM_MODE: + aw869x->play_mode = AW869X_HAPTIC_RAM_MODE; + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_PLAY_MODE_MASK, AW869X_BIT_SYSCTRL_PLAY_MODE_RAM); + break; + case AW869X_HAPTIC_RTP_MODE: + aw869x->play_mode = AW869X_HAPTIC_RTP_MODE; + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_PLAY_MODE_MASK, AW869X_BIT_SYSCTRL_PLAY_MODE_RTP); + break; + case AW869X_HAPTIC_TRIG_MODE: + aw869x->play_mode = AW869X_HAPTIC_TRIG_MODE; + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_PLAY_MODE_MASK, AW869X_BIT_SYSCTRL_PLAY_MODE_RAM); + break; + default: + dev_err(aw869x->dev, "%s: play mode %d err", + __func__, play_mode); + break; + } + return 0; +} + +static int aw869x_haptic_stop(struct aw869x *aw869x) +{ + aw869x_haptic_play_mode(aw869x, AW869X_HAPTIC_STANDBY_MODE); + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_WORK_MODE_MASK, AW869X_BIT_SYSCTRL_STANDBY); + msleep(1); + /* RAMINIT Disable */ + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_RAMINIT_MASK, AW869X_BIT_SYSCTRL_RAMINIT_OFF); + /* RAMINIT Disable End */ + return 0; +} + +static int aw869x_haptic_start(struct aw869x *aw869x) +{ + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_WORK_MODE_MASK, AW869X_BIT_SYSCTRL_ACTIVE); + /* RAMINIT Enable */ + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_RAMINIT_MASK, AW869X_BIT_SYSCTRL_RAMINIT_EN); + /* RAMINIT Enable End */ + aw869x_interrupt_clear(aw869x); + aw869x_i2c_write(aw869x, AW869X_REG_PROC_CTRL, + AW869X_BIT_PROC_CTRL_GO); + return 0; +} + +static int aw869x_haptic_set_repeat_seq(struct aw869x *aw869x, unsigned char flag) +{ + if(flag) { + aw869x_i2c_write_bits(aw869x, AW869X_REG_DATACTRL, + AW869X_BIT_DATACTRL_WAV_DBG_MASK, AW869X_BIT_DATACTRL_WAV_DBG); + } else { + aw869x_i2c_write_bits(aw869x, AW869X_REG_DATACTRL, + AW869X_BIT_DATACTRL_WAV_DBG_MASK, AW869X_BIT_DATACTRL_WAV_NORMAL); + } + + return 0; +} + +static int aw869x_haptic_set_repeat_que_seq(struct aw869x *aw869x, unsigned char seq) +{ + unsigned char i; + for(i=0; i>((AW869X_SEQUENCER_SIZE-i-1)*8))&0xFF; + } + + for(i=0; i>(24-4))&0xF0; + tmp[0] |= (loop>>(16-0))&0x0F; + tmp[1] |= (loop>>( 8-4))&0xF0; + tmp[1] |= (loop>>( 0-0))&0x0F; + + for(i=0; idev, "%s: seq_loop err loop[%d]=0x%02x\n", + __func__, i, seq_loop.loop[i]); + return -1; + } + } + loop[0] |= (seq_loop.loop[0]<<4); + loop[0] |= (seq_loop.loop[1]<<0); + loop[1] |= (seq_loop.loop[2]<<4); + loop[1] |= (seq_loop.loop[3]<<0); + + for(i=0; i<(AW869X_SEQUENCER_SIZE>>1); i++) { + aw869x_i2c_write(aw869x, AW869X_REG_SEQ_LOOP1+i, loop[i]); + } + + return ret; +} + +static int aw869x_haptic_set_bst_vol(struct aw869x *aw869x, unsigned char bst_vol) +{ + aw869x_i2c_write_bits(aw869x, AW869X_REG_BSTCFG, + AW869X_BIT_BSTCFG_BSTVOL_MASK, bst_vol); + return 0; +} + +static int aw869x_haptic_set_bst_peak_cur(struct aw869x *aw869x, unsigned char peak_cur) +{ + aw869x_i2c_write_bits(aw869x, AW869X_REG_BSTCFG, + AW869X_BIT_BSTCFG_PEAKCUR_MASK, peak_cur); + return 0; +} + +static int aw869x_haptic_set_gain(struct aw869x *aw869x, unsigned char gain) +{ + if(gain & 0x80) { + gain = 0x7F; + } + + aw869x_i2c_write(aw869x, AW869X_REG_DATDBG, gain); + + return 0; +} + +#ifdef AW869X_HAPTIC_VBAT_MONITOR +static int aw869x_haptic_set_bst_mode(struct aw869x *aw869x, unsigned char mode) +{ + if(mode == AW869X_HAPTIC_BYPASS_MODE) { + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_BST_MODE_MASK, AW869X_BIT_SYSCTRL_BST_MODE_BYPASS); + } else if (mode == AW869X_HAPTIC_BOOST_MODE){ + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_BST_MODE_MASK, AW869X_BIT_SYSCTRL_BST_MODE_BOOST); + } + + return 0; +} +#endif + +static int aw869x_haptic_play_que_seq(struct aw869x *aw869x, unsigned char flag) +{ + aw869x_haptic_stop(aw869x); + if(flag) { + aw869x_haptic_play_mode(aw869x, AW869X_HAPTIC_RAM_MODE); + aw869x_haptic_set_repeat_seq(aw869x, 0); + aw869x_haptic_start(aw869x); + } + return 0; +} + +static int aw869x_haptic_play_repeat_seq(struct aw869x *aw869x, unsigned char flag) +{ + aw869x_haptic_stop(aw869x); + if(flag) { + aw869x_haptic_play_mode(aw869x, AW869X_HAPTIC_RAM_MODE); + aw869x_haptic_set_repeat_seq(aw869x, 1); + aw869x_haptic_start(aw869x); + } + + return 0; +} + +static void aw869x_haptic_set_rtp_aei(struct aw869x *aw869x, bool flag) +{ + if(flag) { + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSINTM, + AW869X_BIT_SYSINTM_FF_AE_MASK, AW869X_BIT_SYSINTM_FF_AE_EN); + } else { + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSINTM, + AW869X_BIT_SYSINTM_FF_AE_MASK, AW869X_BIT_SYSINTM_FF_AE_OFF); + } +} +/* +static void aw869x_haptic_set_rtp_afi(struct aw869x *aw869x, bool flag) +{ + if(flag) { + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSINTM, + AW869X_BIT_SYSINTM_FF_AF_MASK, AW869X_BIT_SYSINTM_FF_AF_EN); + } else { + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSINTM, + AW869X_BIT_SYSINTM_FF_AF_MASK, AW869X_BIT_SYSINTM_FF_AF_OFF); + } +} +*/ +/* +static unsigned char aw869x_haptic_rtp_get_fifo_aei(struct aw869x *aw869x) +{ + unsigned char ret; + unsigned char reg_val = 0; + + aw869x_i2c_read(aw869x, AW869X_REG_SYSINT, ®_val); + reg_val &= AW869X_BIT_SYSINT_FF_AEI; + ret = reg_val>>4; + + return ret; +} +*/ +#ifdef AW869X_HAPTIC_VBAT_MONITOR +static int aw869x_get_sys_battery_info(char *dev) +{ +#ifdef AWINIC_GET_BATTERY_READ_NODE + int fd; + int eCheck; + int nReadSize; + char buf[64],*pvalue; + mm_segment_t oldfs; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + fd = sys_open(dev, O_RDONLY, 0); + if (fd < 0) { + pr_err("%s: open fail dev:%s fd:%d\n", __func__, dev, fd); + set_fs(oldfs); + return fd; + } + + nReadSize = sys_read(fd, buf, sizeof(buf) - 1); + pr_debug("%s: nReadSize:%d\n", __func__, nReadSize); + + eCheck = simple_strtoul(buf,&pvalue,10); + pr_debug("%s: eCheck = %d\n",eCheck); + + set_fs(oldfs); + sys_close(fd); + + if (eCheck > 0) + return eCheck; + else + return 0; +#else + struct power_supply *batt_psy; + union power_supply_propval prop = {0, }; + int rc = -1; + + batt_psy = power_supply_get_by_name("battery"); + rc = power_supply_get_property(batt_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, + &prop); + power_supply_put(batt_psy); + if (rc < 0) { + pr_err("Error in getting charging status, rc=%d\n", rc); + return 0; + } + return prop.intval; +#endif +} +#endif + +static unsigned char aw869x_haptic_rtp_get_fifo_afi(struct aw869x *aw869x) +{ + unsigned char ret = 0; + unsigned char reg_val = 0; + + aw869x_i2c_read(aw869x, AW869X_REG_SYSINT, ®_val); + reg_val &= AW869X_BIT_SYSINT_FF_AFI; + ret = reg_val>>3; + + return ret; +} + +static int aw869x_haptic_rtp_init(struct aw869x *aw869x) +{ + unsigned int buf_len = 0; + + pr_debug("%s enter\n", __func__); + + aw869x->rtp_cnt = 0; + + while((!aw869x_haptic_rtp_get_fifo_afi(aw869x)) && + (aw869x->play_mode == AW869X_HAPTIC_RTP_MODE)) { + pr_debug("%s rtp cnt = %d\n", __func__, aw869x->rtp_cnt); + if((aw869x_rtp->len-aw869x->rtp_cnt) < (aw869x->ram.base_addr>>3)) { + buf_len = aw869x_rtp->len-aw869x->rtp_cnt; + } else { + buf_len = (aw869x->ram.base_addr>>3); + } + aw869x_i2c_writes(aw869x, AW869X_REG_RTP_DATA, + &aw869x_rtp->data[aw869x->rtp_cnt], buf_len); + aw869x->rtp_cnt += buf_len; + if(aw869x->rtp_cnt == aw869x_rtp->len) { + pr_debug("%s: rtp update complete\n", __func__); + aw869x->rtp_cnt = 0; +#ifdef AW869X_HAPTIC_VBAT_MONITOR + aw869x_haptic_set_bst_mode(aw869x, AW869X_HAPTIC_BOOST_MODE); +#endif + return 0; + } + } + + if(aw869x->play_mode == AW869X_HAPTIC_RTP_MODE) { + aw869x_haptic_set_rtp_aei(aw869x, true); + } + + pr_debug("%s exit\n", __func__); + + return 0; +} + +static void aw869x_rtp_work_routine(struct work_struct *work) +{ +#ifdef AW869X_HAPTIC_VBAT_MONITOR + int i = 0; + int tmp = 0; + unsigned int vbat = 0; +#endif + const struct firmware *rtp_file; + int ret = -1; + struct aw869x *aw869x = container_of(work, struct aw869x, rtp_work); + + pr_debug("%s enter\n", __func__); + + /* fw loaded */ + ret = request_firmware(&rtp_file, + aw869x_rtp_name[aw869x->rtp_file_num], + aw869x->dev); + if(ret < 0) + { + pr_err("%s: failed to read %s\n", __func__, + aw869x_rtp_name[aw869x->rtp_file_num]); + return ; + } + aw869x->rtp_init = 0; + kfree(aw869x_rtp); + aw869x_rtp = kzalloc(rtp_file->size+sizeof(int), GFP_KERNEL); + if (!aw869x_rtp) { + release_firmware(rtp_file); + pr_err("%s: error allocating memory\n", __func__); + return; + } + aw869x_rtp->len = rtp_file->size; + pr_debug("%s: rtp file [%s] size = %d\n", __func__, + aw869x_rtp_name[aw869x->rtp_file_num], aw869x_rtp->len); + memcpy(aw869x_rtp->data, rtp_file->data, rtp_file->size); + release_firmware(rtp_file); + + aw869x->rtp_init = 1; + + /* rtp mode config */ + aw869x_haptic_play_mode(aw869x, AW869X_HAPTIC_RTP_MODE); + aw869x_i2c_write_bits(aw869x, AW869X_REG_PWMDBG, + AW869X_BIT_PWMDBG_PWMCLK_MODE_MASK, AW869X_BIT_PWMDBG_PWMCLK_MODE_12KB); + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_WORK_MODE_MASK, AW869X_BIT_SYSCTRL_ACTIVE); + msleep(2); +#ifdef AW869X_HAPTIC_VBAT_MONITOR + vbat = aw869x_get_sys_battery_info(SYS_BAT_DEV); + pr_debug("%s: get sys battery = %d\n", __func__, vbat); + if((vbat > AW869X_SYS_VBAT_MIN) && (vbat < AW869X_SYS_VBAT_MAX)) { + for(i=0; ilen; i++) { + tmp = (int)aw869x_rtp->data[i]; + tmp = tmp*AW869X_SYS_VBAT_REFERENCE/vbat; + aw869x_rtp->data[i] = (unsigned char)tmp; + } + } +#endif + aw869x_haptic_rtp_init(aw869x); +} + + +/***************************************************** + * + * haptic fops + * + *****************************************************/ +static int aw869x_file_open(struct inode *inode, struct file *file) +{ + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + file->private_data = (void*)g_aw869x; + + return 0; +} + +static int aw869x_file_release(struct inode *inode, struct file *file) +{ + file->private_data = (void*)NULL; + + module_put(THIS_MODULE); + + return 0; +} + +static long aw869x_file_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct aw869x *aw869x = (struct aw869x *)file->private_data; + + int ret = 0; + unsigned int temp = 0; + + dev_info(aw869x->dev, "%s: cmd=0x%x, arg=0x%lx\n", + __func__, cmd, arg); + + mutex_lock(&aw869x->lock); + + if(_IOC_TYPE(cmd) != AW869X_HAPTIC_IOCTL_MAGIC) { + dev_err(aw869x->dev, "%s: cmd magic err\n", + __func__); + return -EINVAL; + } + + switch (cmd) { + case AW869X_HAPTIC_SET_QUE_SEQ: + ret = aw869x_set_que_seq(aw869x, arg); + break; + case AW869X_HAPTIC_SET_SEQ_LOOP: + ret = aw869x_set_seq_loop(aw869x, arg); + break; + case AW869X_HAPTIC_PLAY_QUE_SEQ: + if (copy_from_user(&temp, (void __user *)arg, sizeof(int))) + return -EFAULT; + aw869x_haptic_play_que_seq(aw869x, (unsigned char)temp); + break; + case AW869X_HAPTIC_SET_BST_VOL: + if (copy_from_user(&temp, (void __user *)arg, sizeof(int))) + return -EFAULT; + aw869x_haptic_set_bst_vol(aw869x, (unsigned char)(temp<<3)); + break; + case AW869X_HAPTIC_SET_BST_PEAK_CUR: + if (copy_from_user(&temp, (void __user *)arg, sizeof(int))) + return -EFAULT; + aw869x_haptic_set_bst_peak_cur(aw869x, (unsigned char)(temp)); + break; + case AW869X_HAPTIC_SET_GAIN: + if (copy_from_user(&temp, (void __user *)arg, sizeof(int))) + return -EFAULT; + aw869x_haptic_set_gain(aw869x, (unsigned char)(temp)); + break; + case AW869X_HAPTIC_PLAY_REPEAT_SEQ: + if (copy_from_user(&temp, (void __user *)arg, sizeof(int))) + return -EFAULT; + aw869x_haptic_play_repeat_seq(aw869x, (unsigned char)(temp)); + break; + default: + dev_err(aw869x->dev, "%s, unknown cmd\n", __func__); + break; + } + + mutex_unlock(&aw869x->lock); + + return ret; +} + +static ssize_t aw869x_file_read(struct file* filp, char* buff, size_t len, loff_t* offset) +{ + struct aw869x *aw869x = (struct aw869x *)filp->private_data; + int ret = 0; + int i = 0; + unsigned char reg_val = 0; + unsigned char *pbuff = NULL; + + mutex_lock(&aw869x->lock); + + dev_info(aw869x->dev, "%s: len=%zu\n", __func__, len); + + switch(aw869x->fileops.cmd) + { + case AW869X_HAPTIC_CMD_READ_REG: + pbuff = (unsigned char *)kzalloc(len, GFP_KERNEL); + if(pbuff != NULL) { + for(i=0; ifileops.reg+i, ®_val); + pbuff[i] = reg_val; + } + ret = copy_to_user(buff, pbuff, len); + if(ret) { + dev_err(aw869x->dev, "%s: copy to user fail\n", __func__); + } + kfree(pbuff); + } else { + dev_err(aw869x->dev, "%s: alloc memory fail\n", __func__); + } + break; + case AW869X_HAPTIC_CMD_READ_FIRMWARE: + pbuff = (unsigned char *)kzalloc(len, GFP_KERNEL); + if(pbuff != NULL) { + aw869x_haptic_stop(aw869x); + /* RAMINIT Enable */ + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_RAMINIT_MASK, AW869X_BIT_SYSCTRL_RAMINIT_EN); + + aw869x_i2c_write(aw869x, AW869X_REG_RAMADDRH, aw869x->fileops.ram_addrh); + aw869x_i2c_write(aw869x, AW869X_REG_RAMADDRL, aw869x->fileops.ram_addrl); + for(i=0; idev, "%s: copy to user fail\n", __func__); + } + kfree(pbuff); + } else { + dev_err(aw869x->dev, "%s: alloc memory fail\n", __func__); + } + break; + default: + dev_err(aw869x->dev, "%s, unknown cmd %d \n", __func__, aw869x->fileops.cmd); + break; + } + + mutex_unlock(&aw869x->lock); + + for(i=0; idev, "%s: buff[%d]=0x%02x\n", + __func__, i, buff[i]); + } + + return len; +} + +static ssize_t aw869x_file_write(struct file* filp, const char* buff, size_t len, loff_t* off) +{ + struct aw869x *aw869x = (struct aw869x *)filp->private_data; + int i = 0; + int ret = 0; + unsigned char *pbuff = NULL; + + for(i=0; idev, "%s: buff[%d]=0x%02x\n", + __func__, i, buff[i]); + } + + mutex_lock(&aw869x->lock); + + aw869x->fileops.cmd = buff[0]; + + switch(aw869x->fileops.cmd) + { + case AW869X_HAPTIC_CMD_READ_REG: + if(len == 2) { + aw869x->fileops.reg = buff[1]; + } else { + dev_err(aw869x->dev, "%s: read cmd len %zu err\n", __func__, len); + } + break; + case AW869X_HAPTIC_CMD_WRITE_REG: + if(len > 2) { + pbuff = (unsigned char *)kzalloc(len, GFP_KERNEL); + if(pbuff != NULL) { + ret = copy_from_user(pbuff, buff, len); + if(ret) { + dev_err(aw869x->dev, "%s: copy from user fail\n", __func__); + } else { + for(i=0; idev, "%s: write reg0x%02x=0x%02x\n", + __func__, pbuff[1]+i, pbuff[i+2]); + aw869x_i2c_write(aw869x, pbuff[1]+i, pbuff[2+i]); + } + } + kfree(pbuff); + } else { + dev_err(aw869x->dev, "%s: alloc memory fail\n", __func__); + } + } else { + dev_err(aw869x->dev, "%s: write cmd len %zu err\n", __func__, len); + } + break; + case AW869X_HAPTIC_CMD_UPDATE_FIRMWARE: + pbuff = (unsigned char *)kzalloc(len-1, GFP_KERNEL); + if(pbuff != NULL) { + ret = copy_from_user(pbuff, &buff[1], len-1); + if(ret) { + dev_err(aw869x->dev, "%s: copy from user fail\n", __func__); + } else { + PM_STAY_AWAKE(aw869x->ws); + + aw869x_haptic_stop(aw869x); + /* RAMINIT Enable */ + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_RAMINIT_MASK, AW869X_BIT_SYSCTRL_RAMINIT_EN); + + aw869x_i2c_write(aw869x, AW869X_REG_RAMADDRH, aw869x->fileops.ram_addrh); + aw869x_i2c_write(aw869x, AW869X_REG_RAMADDRL, aw869x->fileops.ram_addrl); + for(i=0; iws); + } + kfree(pbuff); + } else { + dev_err(aw869x->dev, "%s: alloc memory fail\n", __func__); + } + + break; + case AW869X_HAPTIC_CMD_READ_FIRMWARE: + if(len == 3) { + aw869x->fileops.ram_addrh = buff[1]; + aw869x->fileops.ram_addrl = buff[2]; + } else { + dev_err(aw869x->dev, "%s: read firmware len %zu err\n", __func__, len); + } + break; + default: + dev_err(aw869x->dev, "%s, unknown cmd %d \n", __func__, aw869x->fileops.cmd); + break; + } + + mutex_unlock(&aw869x->lock); + + return len; +} + +static struct file_operations fops = +{ + .owner = THIS_MODULE, + .read = aw869x_file_read, + .write = aw869x_file_write, + .unlocked_ioctl = aw869x_file_unlocked_ioctl, + .open = aw869x_file_open, + .release = aw869x_file_release, +}; + +static struct miscdevice aw869x_haptic_misc = +{ + .minor = MISC_DYNAMIC_MINOR, + .name = AW869X_HAPTIC_NAME, + .fops = &fops, +}; + +static int aw869x_haptic_init(struct aw869x *aw869x) +{ + int ret = 0; + + pr_info("%s enter\n", __func__); + + ret = misc_register(&aw869x_haptic_misc); + if(ret) { + dev_err(aw869x->dev, "%s: misc fail: %d\n", __func__, ret); + return ret; + } + + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_WORK_MODE_MASK, AW869X_BIT_SYSCTRL_STANDBY); + aw869x_i2c_write(aw869x, AW869X_REG_ANACTRL, 0x01); + //aw869x_i2c_write_bits(aw869x, AW869X_REG_PWMDBG, + // AW869X_BIT_PWMDBG_PWMCLK_MODE_MASK, AW869X_BIT_PWMDBG_PWMCLK_MODE_24KB); + return ret; +} + + + +/***************************************************** + * + * vibrator + * + *****************************************************/ +static void aw869x_rtp_play(struct aw869x *aw869x, int value) +{ + aw869x_haptic_stop(aw869x); + aw869x_haptic_set_rtp_aei(aw869x, false); + aw869x_interrupt_clear(aw869x); +#if 0 +#ifdef AW869X_HAPTIC_VBAT_MONITOR + aw869x_haptic_set_bst_mode(aw869x, AW869X_HAPTIC_BYPASS_MODE); +#endif +#else + aw869x_haptic_set_bst_vol(aw869x, AW869X_BIT_BSTCFG_BSTVOL_8V); +#endif + if(value < (sizeof(aw869x_rtp_name)/AW869X_RTP_NAME_MAX)) { + aw869x->rtp_file_num = value; + if(value) { + schedule_work(&aw869x->rtp_work); + } + } else { + pr_err("%s: rtp_file_num 0x%02x over max value \n", __func__, aw869x->rtp_file_num); + } +} + +static void aw869x_haptic_context(struct aw869x *aw869x, enum aw869x_haptic_mode cmd) +{ + int t_top = 0; + if (!gpio_is_valid(aw869x->haptic_context_gpio)) { + pr_debug("%s haptic context gpio is invalid \n", __func__); + return; + } + + t_top = gpio_get_value(aw869x->haptic_context_gpio); + if (t_top) { + switch (cmd) { + case HAPTIC_RTP: + aw869x->gain = 0x20; + break; + case HAPTIC_SHORT: + aw869x->gain = 0x20; + break; + case HAPTIC_LONG: + aw869x->gain = 0x06; + break; + default: + break; + } + } +} + +static void aw869x_vibrate(struct aw869x *aw869x, int value) +{ + int seq; + mutex_lock(&aw869x->lock); + + aw869x_haptic_stop(aw869x); + seq = (aw869x->seq >> ((AW869X_SEQUENCER_SIZE - 1) * 8)) & 0xFF; + pr_debug("%s: value=%d, seq=%d\n", __FUNCTION__, value, seq); + + if (value > 0 || seq > 2) { + + if (seq >= AW869X_SEQ_NO_RTP_BASE) { + aw869x->haptic_mode = HAPTIC_RTP; + aw869x->gain = 0x20; + } else if (value < 100 || seq > 2) { + aw869x->haptic_mode = HAPTIC_SHORT; + aw869x->gain = 0x20; + } else { + aw869x->haptic_mode = HAPTIC_LONG; + aw869x->gain = 0x0e; + } + + if(!aw869x->factory_mode) + aw869x_haptic_context(aw869x,aw869x->haptic_mode); + + if (aw869x->debugfs_debug) + aw869x_haptic_set_gain(aw869x, aw869x->gain_debug); + else + aw869x_haptic_set_gain(aw869x, aw869x->gain); + + switch (aw869x->haptic_mode) { + case HAPTIC_RTP: + aw869x_rtp_play(aw869x, seq - AW869X_SEQ_NO_RTP_BASE); + break; + case HAPTIC_SHORT: + aw869x_i2c_write_bits(aw869x, AW869X_REG_PWMDBG, + AW869X_BIT_PWMDBG_PWMCLK_MODE_MASK, + AW869X_BIT_PWMDBG_PWMCLK_MODE_12KB); + aw869x_haptic_set_bst_vol(aw869x, AW869X_BIT_BSTCFG_BSTVOL_8P75V); + //aw869x_haptic_set_peak_cur(aw869x, AW869X_BIT_BSTCFG_PEAKCUR_3P5A); + + if (aw869x->seq == 0) + aw869x->seq = 0x01000000; + + aw869x_haptic_set_que_seq(aw869x, aw869x->seq); + //aw869x_haptic_set_repeat_seq(aw869x, 0); + //aw869x->index = 0x01; + aw869x_haptic_play_que_seq(aw869x, 0x01); + /* + value = (value>HAPTIC_MAX_TIMEOUT)? HAPTIC_MAX_TIMEOUT:value; + hrtimer_start(&aw869x->timer, + ns_to_ktime((u64)value * NSEC_PER_MSEC), HRTIMER_MODE_REL); + */ + break; + case HAPTIC_LONG: + aw869x_i2c_write_bits(aw869x, AW869X_REG_PWMDBG, + AW869X_BIT_PWMDBG_PWMCLK_MODE_MASK, + AW869X_BIT_PWMDBG_PWMCLK_MODE_12KB); + aw869x_haptic_set_bst_vol(aw869x, AW869X_BIT_BSTCFG_BSTVOL_6P25V); + + aw869x->duration = value; + /* wav index config */ + aw869x->index = 0x02; + aw869x_haptic_set_repeat_que_seq(aw869x, aw869x->index); + + PM_WAKEUP_EVENT(aw869x->ws, value + 100); + /* run ms timer */ + hrtimer_cancel(&aw869x->timer); + aw869x->state = 0x01; + if (aw869x->state) + { + hrtimer_start(&aw869x->timer, + ktime_set(aw869x->duration / 1000, (value % 1000) * 1000000), + HRTIMER_MODE_REL); + } + schedule_work(&aw869x->vibrator_work); + break; + default: + break; + } + + /* Restore to default short waveform */ + if (seq > 2) + aw869x->seq = 0; + } + + mutex_unlock(&aw869x->lock); +} + +#ifdef TIMED_OUTPUT +static int aw869x_vibrator_get_time(struct timed_output_dev *dev) +{ + struct aw869x *aw869x = container_of(dev, struct aw869x, to_dev); + + if (hrtimer_active(&aw869x->timer)) { + ktime_t r = hrtimer_get_remaining(&aw869x->timer); + return ktime_to_ms(r); + } + + return 0; +} + +static void aw869x_vibrator_enable( struct timed_output_dev *dev, int value) +{ + struct aw869x *aw869x = container_of(dev, struct aw869x, to_dev); + + pr_debug("%s enter, value=%d\n", __func__, value); + aw869x_vibrate(aw869x, value); + pr_debug("%s exit\n", __func__); +} + +#else +static enum led_brightness aw869x_haptic_brightness_get(struct led_classdev *cdev) +{ + struct aw869x *aw869x = + container_of(cdev, struct aw869x, cdev); + + return aw869x->amplitude; +} + +static void aw869x_haptic_brightness_set(struct led_classdev *cdev, + enum led_brightness level) +{ + struct aw869x *aw869x = + container_of(cdev, struct aw869x, cdev); + + aw869x->amplitude = level; + + mutex_lock(&aw869x->lock); + + aw869x_haptic_stop(aw869x); + if (aw869x->amplitude > 0) { + aw869x_haptic_play_que_seq(aw869x, aw869x->amplitude); + } + + mutex_unlock(&aw869x->lock); + +} +#endif + +static ssize_t aw869x_extra_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return 0; +} + +static ssize_t aw869x_extra_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw869x_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + + return snprintf(buf, PAGE_SIZE, "%d\n", aw869x->state); +} + +static ssize_t aw869x_state_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw869x_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + ktime_t time_rem; + s64 time_ms = 0; + + if (hrtimer_active(&aw869x->timer)) { + time_rem = hrtimer_get_remaining(&aw869x->timer); + time_ms = ktime_to_ms(time_rem); + } + + return snprintf(buf, PAGE_SIZE, "%lld\n", time_ms); +} + +static ssize_t aw869x_duration_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + pr_debug("%s: value=%d\n", __FUNCTION__, val); + + /* setting 0 on duration is NOP for now */ + if (val <= 0) + return count; + + aw869x->duration = val; + + return count; +} + +static ssize_t aw869x_activate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + + /* For now nothing to show */ + return snprintf(buf, PAGE_SIZE, "%d\n", aw869x->state); +} + +static ssize_t aw869x_activate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + if (val != 0 && val != 1) + return count; + + pr_debug("%s: value=%d\n", __FUNCTION__, val); + + aw869x->state = val; + + if (aw869x->state) + aw869x_vibrate(aw869x, aw869x->duration); + else + aw869x_vibrate(aw869x, 0); + + return count; +} + +static ssize_t aw869x_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned char reg_val = 0; + aw869x_i2c_read(aw869x, AW869X_REG_QUE_SEQ1, ®_val); + aw869x->index = reg_val; + + return snprintf(buf, PAGE_SIZE, "%d\n", aw869x->index); +} + +static ssize_t aw869x_index_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + pr_debug("%s: value=%d\n", __FUNCTION__, val); + + mutex_lock(&aw869x->lock); + aw869x->index = val; + aw869x_haptic_set_repeat_que_seq(aw869x, aw869x->index); + mutex_unlock(&aw869x->lock); + return count; +} + +static ssize_t aw869x_vmax_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + + return snprintf(buf, PAGE_SIZE, "%d\n", aw869x->vmax); +} + +static ssize_t aw869x_vmax_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + pr_debug("%s: value=%d\n", __FUNCTION__, val); + + mutex_lock(&aw869x->lock); + aw869x->vmax = val; + aw869x_haptic_set_bst_vol(aw869x, (unsigned char)(aw869x->vmax<<3)); + mutex_unlock(&aw869x->lock); + return count; +} + +static ssize_t aw869x_gain_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + + return snprintf(buf, PAGE_SIZE, "%d\n", aw869x->gain_debug); +} + +static ssize_t aw869x_gain_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + pr_debug("%s: value=%d\n", __FUNCTION__, val); + + mutex_lock(&aw869x->lock); + if (val > 0) + aw869x->debugfs_debug = true; + else + aw869x->debugfs_debug = false; + aw869x->gain_debug = val; + aw869x_haptic_set_gain(aw869x, (unsigned char)aw869x->gain_debug); + mutex_unlock(&aw869x->lock); + return count; +} + +static ssize_t aw869x_seq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + size_t count = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + aw869x->seq = 0; + for(i=0; iseq |= (reg_val<<((AW869X_SEQUENCER_SIZE-i-1)*8)); + } + return count; +} + +static ssize_t aw869x_seq_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + pr_debug("%s: value=%x\n", __FUNCTION__, val); + + mutex_lock(&aw869x->lock); + aw869x->seq = val; + aw869x_haptic_set_que_seq(aw869x, aw869x->seq); + mutex_unlock(&aw869x->lock); + return count; +} + +static ssize_t aw869x_loop_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + size_t count = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + unsigned char tmp[2]; + unsigned char loop_tmp = 0; + + aw869x->loop = 0; + for(i=0; i>4; + tmp[1] = reg_val&0x0F; + loop_tmp = (tmp[0]<<8)|tmp[1]; + count += snprintf(buf+count, PAGE_SIZE-count, + "seq%d loop: 0x%02x\n", i*2+1, tmp[0]); + count += snprintf(buf+count, PAGE_SIZE-count, + "seq%d loop: 0x%02x\n", i*2+2, tmp[1]); + aw869x->loop |= (loop_tmp<<((AW869X_SEQUENCER_LOOP_SIZE-i-1)*8)); + } + return count; +} + +static ssize_t aw869x_loop_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + pr_debug("%s: value=%x\n", __FUNCTION__, val); + + mutex_lock(&aw869x->lock); + aw869x->loop = val; + aw869x_haptic_set_que_loop(aw869x, aw869x->loop); + mutex_unlock(&aw869x->lock); + return count; +} + +static ssize_t aw869x_reg_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + ssize_t len = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + for(i = 0; i < AW869X_REG_MAX; i ++) { + if(!(aw869x_reg_access[i]®_RD_ACCESS)) + continue; + aw869x_i2c_read(aw869x, i, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, "reg:0x%02x=0x%02x \n", i, reg_val); + } + return len; +} + +static ssize_t aw869x_reg_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned int databuf[2] = {0, 0}; + + if(2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) { + aw869x_i2c_write(aw869x, (unsigned char)databuf[0], (unsigned char)databuf[1]); + } + + return count; +} + +static ssize_t aw869x_rtp_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + //struct timed_output_dev *to_dev = dev_get_drvdata(dev); + //struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + //struct led_classdev *cdev = dev_get_drvdata(dev); + //struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + ssize_t len = 0; + len += snprintf(buf+len, PAGE_SIZE-len, "rtp mode\n"); + return len; +} + +static ssize_t aw869x_rtp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + aw869x_rtp_play(aw869x, val); + + return count; +} + +static ssize_t aw869x_ram_update_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ +#ifdef TIMED_OUTPUT + //struct timed_output_dev *to_dev = dev_get_drvdata(dev); + //struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + //struct led_classdev *cdev = dev_get_drvdata(dev); + //struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + ssize_t len = 0; + len += snprintf(buf+len, PAGE_SIZE-len, "ram/rtp mode\n"); + return len; +} + +static ssize_t aw869x_ram_update_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + if(val) { + aw869x_ram_update(aw869x); + } + return count; +} + +#ifdef CONFIG_INPUT_AWINIC_HAPTIC +static ssize_t aw869x_pwk_p_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned char reg_val = 0; + aw869x_i2c_read(aw869x, AW869X_REG_TRG1_WAV_P, ®_val); + + return snprintf(buf, PAGE_SIZE, "%d\n", reg_val); +} + +static ssize_t aw869x_pwk_p_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned int databuf = 0; + if(1 == sscanf(buf, "%x", &databuf)) { + aw869x_i2c_write(aw869x, AW869X_REG_TRG1_WAV_P, (unsigned char)databuf); + } + + return count; +} + +static ssize_t aw869x_pwk_n_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned char reg_val = 0; + aw869x_i2c_read(aw869x, AW869X_REG_TRG1_WAV_N, ®_val); + + return snprintf(buf, PAGE_SIZE, "%d\n", reg_val); +} + +static ssize_t aw869x_pwk_n_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned int databuf = 0; + if(1 == sscanf(buf, "%x", &databuf)) { + aw869x_i2c_write(aw869x, AW869X_REG_TRG1_WAV_N, (unsigned char)databuf); + } + + return count; +} + +static ssize_t aw869x_voldown_p_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned char reg_val = 0; + aw869x_i2c_read(aw869x, AW869X_REG_TRG2_WAV_P, ®_val); + + return snprintf(buf, PAGE_SIZE, "%d\n", reg_val); +} + +static ssize_t aw869x_voldown_p_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned int databuf = 0; + if(1 == sscanf(buf, "%x", &databuf)) { + aw869x_i2c_write(aw869x, AW869X_REG_TRG2_WAV_P, (unsigned char)databuf); + } + + return count; +} + +static ssize_t aw869x_voldown_n_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned char reg_val = 0; + aw869x_i2c_read(aw869x, AW869X_REG_TRG2_WAV_N, ®_val); + + return snprintf(buf, PAGE_SIZE, "%d\n", reg_val); +} + +static ssize_t aw869x_voldown_n_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned int databuf = 0; + if(1 == sscanf(buf, "%x", &databuf)) { + aw869x_i2c_write(aw869x, AW869X_REG_TRG2_WAV_N, (unsigned char)databuf); + } + + return count; +} +static ssize_t aw869x_volup_p_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned char reg_val = 0; + aw869x_i2c_read(aw869x, AW869X_REG_TRG3_WAV_P, ®_val); + + return snprintf(buf, PAGE_SIZE, "%d\n", reg_val); +} + +static ssize_t aw869x_volup_p_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned int databuf = 0; + if(1 == sscanf(buf, "%x", &databuf)) { + aw869x_i2c_write(aw869x, AW869X_REG_TRG3_WAV_P, (unsigned char)databuf); + } + + return count; +} + +static ssize_t aw869x_volup_n_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned char reg_val = 0; + aw869x_i2c_read(aw869x, AW869X_REG_TRG3_WAV_N, ®_val); + + return snprintf(buf, PAGE_SIZE, "%d\n", reg_val); +} + +static ssize_t aw869x_volup_n_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned int databuf = 0; + if(1 == sscanf(buf, "%x", &databuf)) { + aw869x_i2c_write(aw869x, AW869X_REG_TRG3_WAV_N, (unsigned char)databuf); + } + + return count; +} +static ssize_t aw869x_reset_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned char reg_val = 0; + aw869x_i2c_read(aw869x, AW869X_REG_ID, ®_val); + + return snprintf(buf, PAGE_SIZE, "%d\n", reg_val); +} + +static ssize_t aw869x_reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +#ifdef TIMED_OUTPUT + struct timed_output_dev *to_dev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(to_dev, struct aw869x, to_dev); +#else + struct led_classdev *cdev = dev_get_drvdata(dev); + struct aw869x *aw869x = container_of(cdev, struct aw869x, cdev); +#endif + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + pr_debug("%s: value=%d\n", __FUNCTION__, val); + + if(1 == val) { + aw869x_i2c_write(aw869x, AW869X_REG_ID, 0xAA); + } + + return count; +} +#endif + +static DEVICE_ATTR(extra, S_IWUSR | S_IRUGO, aw869x_extra_show, aw869x_extra_store); +static DEVICE_ATTR(state, S_IWUSR | S_IRUGO, aw869x_state_show, aw869x_state_store); +static DEVICE_ATTR(duration, S_IWUSR | S_IRUGO, aw869x_duration_show, aw869x_duration_store); +static DEVICE_ATTR(activate, S_IWUSR | S_IRUGO, aw869x_activate_show, aw869x_activate_store); +static DEVICE_ATTR(index, S_IWUSR | S_IRUGO, aw869x_index_show, aw869x_index_store); +static DEVICE_ATTR(vmax, S_IWUSR | S_IRUGO, aw869x_vmax_show, aw869x_vmax_store); +static DEVICE_ATTR(gain, S_IWUSR | S_IRUGO, aw869x_gain_show, aw869x_gain_store); +static DEVICE_ATTR(seq, S_IWUSR | S_IRUGO, aw869x_seq_show, aw869x_seq_store); +static DEVICE_ATTR(loop, S_IWUSR | S_IRUGO, aw869x_loop_show, aw869x_loop_store); +static DEVICE_ATTR(register, S_IWUSR | S_IRUGO, aw869x_reg_show, aw869x_reg_store); +static DEVICE_ATTR(rtp, S_IWUSR | S_IRUGO, aw869x_rtp_show, aw869x_rtp_store); +static DEVICE_ATTR(ram_update, S_IWUSR | S_IRUGO, aw869x_ram_update_show, aw869x_ram_update_store); +#ifdef CONFIG_INPUT_AWINIC_HAPTIC +static DEVICE_ATTR(pwk_p, S_IWUSR | S_IRUGO, aw869x_pwk_p_show, aw869x_pwk_p_store);/* Power key for trig1(0x0d) */ +static DEVICE_ATTR(voldown_p, S_IWUSR | S_IRUGO, aw869x_voldown_p_show, aw869x_voldown_p_store);/* Vol down for trig2(0x0e) */ +static DEVICE_ATTR(volup_p, S_IWUSR | S_IRUGO, aw869x_volup_p_show, aw869x_volup_p_store);/* Vol up key for trig3(0x0f) */ +static DEVICE_ATTR(pwk_n, S_IWUSR | S_IRUGO, aw869x_pwk_n_show, aw869x_pwk_n_store);/* Power key for trig1_n(0x10) */ +static DEVICE_ATTR(voldown_n, S_IWUSR | S_IRUGO, aw869x_voldown_n_show, aw869x_voldown_n_store);/* Vol down for trig2_n(0x11) */ +static DEVICE_ATTR(volup_n, S_IWUSR | S_IRUGO, aw869x_volup_n_show, aw869x_volup_n_store);/* Vol up key for trig3_n(0x12) */ +static DEVICE_ATTR(reset, S_IWUSR | S_IRUGO, aw869x_reset_show, aw869x_reset_store);/* Reset device */ +#endif + +static struct attribute *aw869x_vibrator_attributes[] = { + &dev_attr_extra.attr, + &dev_attr_state.attr, + &dev_attr_duration.attr, + &dev_attr_activate.attr, + &dev_attr_index.attr, + &dev_attr_vmax.attr, + &dev_attr_gain.attr, + &dev_attr_seq.attr, + &dev_attr_loop.attr, + &dev_attr_register.attr, + &dev_attr_rtp.attr, + &dev_attr_ram_update.attr, +#ifdef CONFIG_INPUT_AWINIC_HAPTIC + &dev_attr_pwk_p.attr, + &dev_attr_voldown_p.attr, + &dev_attr_volup_p.attr, + &dev_attr_reset.attr, + &dev_attr_pwk_n.attr, + &dev_attr_voldown_n.attr, + &dev_attr_volup_n.attr, +#endif + NULL +}; + +static struct attribute_group aw869x_vibrator_attribute_group = { + .attrs = aw869x_vibrator_attributes +}; + +static enum hrtimer_restart aw869x_vibrator_timer_func(struct hrtimer *timer) +{ + struct aw869x *aw869x = container_of(timer, struct aw869x, timer); + + pr_debug("%s enter\n", __func__); + aw869x->state = 0; + schedule_work(&aw869x->vibrator_work); + + return HRTIMER_NORESTART; +} + +static void aw869x_vibrator_work_routine(struct work_struct *work) +{ + struct aw869x *aw869x = container_of(work, struct aw869x, vibrator_work); + + pr_debug("%s enter\n", __func__); + + mutex_lock(&aw869x->lock); + + if(aw869x->state) { + //aw869x_haptic_set_repeat_que_seq(aw869x, aw869x->index); + aw869x_haptic_play_mode(aw869x, AW869X_HAPTIC_RAM_MODE); + aw869x_haptic_set_repeat_seq(aw869x, 1); + aw869x_haptic_start(aw869x); + } else { + aw869x_haptic_stop(aw869x); + } + mutex_unlock(&aw869x->lock); +} + +static int aw869x_vibrator_init(struct aw869x *aw869x) +{ + int ret = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + pr_info("%s enter\n", __func__); + + ret = aw869x_i2c_read(aw869x, AW869X_REG_QUE_SEQ1, ®_val); + aw869x->index = reg_val & 0x7F; + ret = aw869x_i2c_read(aw869x, AW869X_REG_DATDBG, ®_val); + aw869x->gain = reg_val & 0x7F; + ret = aw869x_i2c_read(aw869x, AW869X_REG_BSTCFG, ®_val); + aw869x->vmax = (reg_val >> 3); + for(i=0; iseq |= (reg_val<<((AW869X_SEQUENCER_SIZE-i-1)*8)); + } +#ifdef TIMED_OUTPUT + aw869x->to_dev.name = "vibrator"; + aw869x->to_dev.get_time = aw869x_vibrator_get_time; + aw869x->to_dev.enable = aw869x_vibrator_enable; + + ret = timed_output_dev_register(&(aw869x->to_dev)); + if ( ret < 0){ + dev_err(aw869x->dev, "%s: fail to create timed output dev\n", + __func__); + return ret; + } + ret = sysfs_create_group(&aw869x->to_dev.dev->kobj, &aw869x_vibrator_attribute_group); + if (ret < 0) { + dev_err(aw869x->dev, "%s error creating sysfs attr files\n", __func__); + return ret; + } +#else + aw869x->cdev.name = "vibrator"; + aw869x->cdev.brightness_get = aw869x_haptic_brightness_get; + aw869x->cdev.brightness_set = aw869x_haptic_brightness_set; + + ret = devm_led_classdev_register(&aw869x->i2c->dev, &aw869x->cdev); + if (ret < 0){ + dev_err(aw869x->dev, "%s: fail to create led dev\n", + __func__); + return ret; + } + ret = sysfs_create_group(&aw869x->cdev.dev->kobj, &aw869x_vibrator_attribute_group); + if (ret < 0) { + dev_err(aw869x->dev, "%s error creating sysfs attr files\n", __func__); + return ret; + } +#endif + hrtimer_init(&aw869x->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + aw869x->timer.function = aw869x_vibrator_timer_func; + INIT_WORK(&aw869x->vibrator_work, aw869x_vibrator_work_routine); + + INIT_WORK(&aw869x->rtp_work, aw869x_rtp_work_routine); + + PM_WAKEUP_REGISTER(NULL, aw869x->ws, "vibrator"); + if (!aw869x->ws) + return -ENOMEM; + + mutex_init(&aw869x->lock); + + return 0; +} + + + +/***************************************************** + * + * regmap + * + *****************************************************/ +static bool aw869x_writeable_register(struct device *dev, unsigned int reg) +{ + /* enable read access for all registers */ + return true; +} + +static bool aw869x_readable_register(struct device *dev, unsigned int reg) +{ + /* enable read access for all registers */ + return true; +} + +static bool aw869x_volatile_register(struct device *dev, unsigned int reg) +{ + /* enable read access for all registers */ + return true; +} + +static const struct regmap_config aw869x_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = AW869X_REG_MAX, + .writeable_reg = aw869x_writeable_register, + .readable_reg = aw869x_readable_register, + .volatile_reg = aw869x_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + + + +/****************************************************** + * + * irq + * + ******************************************************/ +static void aw869x_interrupt_clear(struct aw869x *aw869x) +{ + unsigned char reg_val = 0; + pr_debug("%s enter\n", __func__); + aw869x_i2c_read(aw869x, AW869X_REG_SYSINT, ®_val); + pr_debug("%s: reg SYSINT=0x%x\n", __func__, reg_val); +} + +static void aw869x_interrupt_setup(struct aw869x *aw869x) +{ + unsigned char reg_val = 0; + + pr_info("%s enter\n", __func__); + + aw869x_i2c_read(aw869x, AW869X_REG_SYSINT, ®_val); + pr_info("%s: reg SYSINT=0x%x\n", __func__, reg_val); +/* + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSINTM, + AW869X_BIT_SYSINTM_UVLO_MASK, AW869X_BIT_SYSINTM_UVLO_EN); + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSINTM, + AW869X_BIT_SYSINTM_OCD_MASK, AW869X_BIT_SYSINTM_OCD_EN); + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSINTM, + AW869X_BIT_SYSINTM_OT_MASK, AW869X_BIT_SYSINTM_OT_EN); +*/ +} + +static irqreturn_t aw869x_irq(int irq, void *data) +{ + struct aw869x *aw869x = data; + unsigned char reg_val = 0; + unsigned int buf_len = 0; + + pr_debug("%s enter\n", __func__); + + aw869x_i2c_read(aw869x, AW869X_REG_SYSINT, ®_val); + pr_debug("%s: reg SYSINT=0x%x\n", __func__, reg_val); + + if(reg_val & AW869X_BIT_SYSINT_UVLI) { + pr_err("%s chip uvlo int error\n", __func__); + } + if(reg_val & AW869X_BIT_SYSINT_OCDI) { + pr_err("%s chip over current int error\n", __func__); + } + if(reg_val & AW869X_BIT_SYSINT_OTI) { + pr_err("%s chip over temperature int error\n", __func__); + } + if(reg_val & AW869X_BIT_SYSINT_DONEI) { + pr_debug("%s chip playback done\n", __func__); + } + + if(reg_val & AW869X_BIT_SYSINT_FF_AEI) { + pr_debug("%s: aw869x rtp fifo almost empty int\n", __func__); + if(aw869x->rtp_init) { + while((!aw869x_haptic_rtp_get_fifo_afi(aw869x)) && + (aw869x->play_mode == AW869X_HAPTIC_RTP_MODE)) { + pr_debug("%s: aw869x rtp mode fifo update, cnt=%d\n", + __func__, aw869x->rtp_cnt); + if((aw869x_rtp->len-aw869x->rtp_cnt) < (aw869x->ram.base_addr>>3)) { + buf_len = aw869x_rtp->len-aw869x->rtp_cnt; + } else { + buf_len = (aw869x->ram.base_addr>>3); + } + aw869x_i2c_writes(aw869x, AW869X_REG_RTP_DATA, + &aw869x_rtp->data[aw869x->rtp_cnt], buf_len); + aw869x->rtp_cnt += buf_len; + if(aw869x->rtp_cnt == aw869x_rtp->len) { + pr_debug("%s: rtp update complete\n", __func__); + aw869x_haptic_set_rtp_aei(aw869x, false); + aw869x->rtp_cnt = 0; + aw869x->rtp_init = 0; +#ifdef AW869X_HAPTIC_VBAT_MONITOR + aw869x_haptic_set_bst_mode(aw869x, AW869X_HAPTIC_BOOST_MODE); +#endif +#ifdef AW869X_REPEAT_RTP_PLAYING + aw869x_rtp_play(aw869x, aw869x->rtp_file_num); +#endif + break; + } + } + } else { + pr_err("%s: aw869x rtp init = %d, init error\n", __func__, aw869x->rtp_init); + } + } + + if(reg_val & AW869X_BIT_SYSINT_FF_AFI) { + pr_debug("%s: aw869x rtp mode fifo full empty\n", __func__); + } + + if(aw869x->play_mode != AW869X_HAPTIC_RTP_MODE) { + aw869x_haptic_set_rtp_aei(aw869x, false); + } + + aw869x_i2c_read(aw869x, AW869X_REG_SYSINT, ®_val); + pr_debug("%s: reg SYSINT=0x%x\n", __func__, reg_val); + aw869x_i2c_read(aw869x, AW869X_REG_SYSST, ®_val); + pr_debug("%s: reg SYSST=0x%x\n", __func__, reg_val); + + pr_debug("%s exit\n", __func__); + + return IRQ_HANDLED; +} + +/***************************************************** + * + * device tree + * + *****************************************************/ +static int aw869x_parse_dt(struct device *dev, struct aw869x *aw869x, + struct device_node *np) { + aw869x->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (aw869x->reset_gpio < 0) { + dev_err(dev, "%s: no reset gpio provided, will not HW reset device\n", __func__); + return -1; + } else { + dev_info(dev, "%s: reset gpio provided ok\n", __func__); + } + aw869x->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (aw869x->irq_gpio < 0) { + dev_err(dev, "%s: no irq gpio provided.\n", __func__); + } else { + dev_info(dev, "%s: irq gpio provided ok.\n", __func__); + } + + aw869x->haptic_context_gpio = of_get_named_gpio(np, "haptic-context-gpio", 0); + if (aw869x->haptic_context_gpio < 0) { + dev_err(dev, "%s: no haptic context gpio provided.\n", __func__); + } else { + dev_info(dev, "%s: haptic context gpio provided ok.\n", __func__); + } + + return 0; +} + +static int aw869x_hw_reset(struct aw869x *aw869x) +{ + pr_info("%s enter\n", __func__); + + if (aw869x && gpio_is_valid(aw869x->reset_gpio)) { + gpio_set_value_cansleep(aw869x->reset_gpio, 0); + msleep(1); + gpio_set_value_cansleep(aw869x->reset_gpio, 1); + msleep(1); + } else { + dev_err(aw869x->dev, "%s: failed\n", __func__); + } + return 0; +} + + +/***************************************************** + * + * check chip id + * + *****************************************************/ +static int aw869x_read_chipid(struct aw869x *aw869x) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char reg = 0; + + while(cnt < AW_READ_CHIPID_RETRIES) { + /* hardware reset */ + aw869x_hw_reset(aw869x); + + ret = aw869x_i2c_read(aw869x, AW869X_REG_ID, ®); + if (ret < 0) { + dev_err(aw869x->dev, "%s: failed to read register AW869X_REG_ID: %d\n", __func__, ret); + } + switch (reg) { + case 0x91: + pr_info("%s aw8691 detected\n", __func__); + aw869x->chipid = AW8691_ID; + //aw869x->flags |= AW869X_FLAG_SKIP_INTERRUPTS; + return 0; + default: + pr_info("%s unsupported device revision (0x%x)\n", __func__, reg ); + break; + } + cnt ++; + + msleep(AW_READ_CHIPID_RETRY_DELAY); + } + + return -EINVAL; +} + + +/****************************************************** + * + * sys group attribute: reg + * + ******************************************************/ +static ssize_t aw869x_i2c_reg_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw869x *aw869x = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0, 0}; + + if(2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) { + aw869x_i2c_write(aw869x, (unsigned char)databuf[0], (unsigned char)databuf[1]); + } + + return count; +} + +static ssize_t aw869x_i2c_reg_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct aw869x *aw869x = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + for(i = 0; i < AW869X_REG_MAX; i ++) { + if(!(aw869x_reg_access[i]®_RD_ACCESS)) + continue; + aw869x_i2c_read(aw869x, i, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, "reg:0x%02x=0x%02x \n", i, reg_val); + } + return len; +} +static ssize_t aw869x_i2c_ram_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw869x *aw869x = dev_get_drvdata(dev); + + unsigned int databuf[1] = {0}; + + if(1 == sscanf(buf, "%x", &databuf[0])) { + if(1 == databuf[0]) { + aw869x_ram_update(aw869x); + } + } + + return count; +} + +static ssize_t aw869x_i2c_ram_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct aw869x *aw869x = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned int i = 0; + unsigned char reg_val = 0; + + aw869x_haptic_stop(aw869x); + /* RAMINIT Enable */ + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_RAMINIT_MASK, AW869X_BIT_SYSCTRL_RAMINIT_EN); + + aw869x_i2c_write(aw869x, AW869X_REG_RAMADDRH, (unsigned char)(aw869x->ram.base_addr>>8)); + aw869x_i2c_write(aw869x, AW869X_REG_RAMADDRL, (unsigned char)(aw869x->ram.base_addr&0x00ff)); + len += snprintf(buf+len, PAGE_SIZE-len, "aw869x_haptic_ram:\n"); + for(i=0; iram.len; i++) { + aw869x_i2c_read(aw869x, AW869X_REG_RAMDATA, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, "0x%02x,", reg_val); + } + len += snprintf(buf+len, PAGE_SIZE-len, "\n"); + /* RAMINIT Disable */ + aw869x_i2c_write_bits(aw869x, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_RAMINIT_MASK, AW869X_BIT_SYSCTRL_RAMINIT_OFF); + + return len; +} + +static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, aw869x_i2c_reg_show, aw869x_i2c_reg_store); +static DEVICE_ATTR(ram, S_IWUSR | S_IRUGO, aw869x_i2c_ram_show, aw869x_i2c_ram_store); + +static struct attribute *aw869x_attributes[] = { + &dev_attr_reg.attr, + &dev_attr_ram.attr, + NULL +}; + +static struct attribute_group aw869x_attribute_group = { + .attrs = aw869x_attributes +}; + + +static bool mmi_factory_check(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool factory = false; + + if (np) + factory = of_property_read_bool(np, "mmi,factory-cable"); + + of_node_put(np); + + return factory; +} + +/****************************************************** + * + * i2c driver + * + ******************************************************/ +static int aw869x_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct aw869x *aw869x; + struct device_node *np = i2c->dev.of_node; + int irq_flags = 0; + int ret = -1; + + pr_info("%s enter\n", __func__); + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed\n"); + return -EIO; + } + + aw869x = devm_kzalloc(&i2c->dev, sizeof(struct aw869x), GFP_KERNEL); + if (aw869x == NULL) + return -ENOMEM; + + aw869x->dev = &i2c->dev; + aw869x->i2c = i2c; + + /* aw869x regmap */ + aw869x->regmap = devm_regmap_init_i2c(i2c, &aw869x_regmap); + if (IS_ERR(aw869x->regmap)) { + ret = PTR_ERR(aw869x->regmap); + dev_err(&i2c->dev, "%s: failed to allocate register map: %d\n", __func__, ret); + goto err; + } + + i2c_set_clientdata(i2c, aw869x); + + /* aw869x rst & int */ + if (np) { + ret = aw869x_parse_dt(&i2c->dev, aw869x, np); + if (ret) { + dev_err(&i2c->dev, "%s: failed to parse device tree node\n", __func__); + goto err; + } + } else { + aw869x->reset_gpio = -1; + aw869x->irq_gpio = -1; + } + + if (gpio_is_valid(aw869x->reset_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw869x->reset_gpio, + GPIOF_OUT_INIT_LOW, "aw869x_rst"); + if (ret){ + dev_err(&i2c->dev, "%s: rst request failed\n", __func__); + goto err; + } + } + + if (gpio_is_valid(aw869x->irq_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw869x->irq_gpio, + GPIOF_DIR_IN, "aw869x_int"); + if (ret){ + dev_err(&i2c->dev, "%s: int request failed\n", __func__); + goto err; + } + } + + /* aw869x chip id */ + ret = aw869x_read_chipid(aw869x); + if (ret < 0) { + dev_err(&i2c->dev, "%s: aw869x_read_chipid failed ret=%d\n", __func__, ret); + goto err_id; + } + + /* aw869x irq */ + if (gpio_is_valid(aw869x->irq_gpio) && + !(aw869x->flags & AW869X_FLAG_SKIP_INTERRUPTS)) { + /* register irq handler */ + aw869x_interrupt_setup(aw869x); + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, + gpio_to_irq(aw869x->irq_gpio), + NULL, aw869x_irq, irq_flags, + "aw869x", aw869x); + if (ret != 0) { + dev_err(&i2c->dev, "%s: failed to request IRQ %d: %d\n", + __func__, gpio_to_irq(aw869x->irq_gpio), ret); + goto err_irq; + } + } else { + dev_info(&i2c->dev, "%s skipping IRQ registration\n", __func__); + /* disable feature support if gpio was invalid */ + aw869x->flags |= AW869X_FLAG_SKIP_INTERRUPTS; + } + + dev_set_drvdata(&i2c->dev, aw869x); + + ret = sysfs_create_group(&i2c->dev.kobj, &aw869x_attribute_group); + if (ret < 0) { + dev_info(&i2c->dev, "%s error creating sysfs attr files\n", __func__); + goto err_sysfs; + } + + g_aw869x = aw869x; + + aw869x->factory_mode = mmi_factory_check(); + + aw869x_vibrator_init(aw869x); + + aw869x_haptic_init(aw869x); + + aw869x_ram_init(aw869x); + + pr_info("%s probe completed successfully!\n", __func__); + + return 0; + +err_sysfs: + sysfs_remove_group(&i2c->dev.kobj, &aw869x_attribute_group); +err_irq: +err_id: +err: + return ret; +} + +static int aw869x_i2c_remove(struct i2c_client *i2c) +{ + struct aw869x *aw869x = i2c_get_clientdata(i2c); + + pr_info("%s enter\n", __func__); + + sysfs_remove_group(&i2c->dev.kobj, &aw869x_attribute_group); + + misc_deregister(&aw869x_haptic_misc); + + if (gpio_is_valid(aw869x->irq_gpio)) + devm_gpio_free(&i2c->dev, aw869x->irq_gpio); + if (gpio_is_valid(aw869x->reset_gpio)) + devm_gpio_free(&i2c->dev, aw869x->reset_gpio); + + PM_WAKEUP_UNREGISTER(aw869x->ws); + + return 0; +} + +static const struct i2c_device_id aw869x_i2c_id[] = { + { AW869X_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw869x_i2c_id); + +static struct of_device_id aw869x_dt_match[] = { + { .compatible = "awinic,aw869x_haptic" }, + { }, +}; + +static struct i2c_driver aw869x_i2c_driver = { + .driver = { + .name = AW869X_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw869x_dt_match), + }, + .probe = aw869x_i2c_probe, + .remove = aw869x_i2c_remove, + .id_table = aw869x_i2c_id, +}; + + +static int __init aw869x_i2c_init(void) +{ + int ret = 0; + + pr_info("aw869x driver version %s\n", AW869X_VERSION); + + ret = i2c_add_driver(&aw869x_i2c_driver); + if(ret){ + pr_err("fail to add aw869x device into i2c\n"); + return ret; + } + + return 0; +} +module_init(aw869x_i2c_init); + + +static void __exit aw869x_i2c_exit(void) +{ + i2c_del_driver(&aw869x_i2c_driver); +} +module_exit(aw869x_i2c_exit); + + +MODULE_DESCRIPTION("AW869X Haptic Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/awinic/aw869x/aw869x.h b/drivers/misc/awinic/aw869x/aw869x.h new file mode 100644 index 000000000000..909a8dbe0f21 --- /dev/null +++ b/drivers/misc/awinic/aw869x/aw869x.h @@ -0,0 +1,198 @@ +#ifndef _AW869X_H_ +#define _AW869X_H_ + +/********************************************************* + * + * kernel 3.18 + * + ********************************************************/ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 4, 1) +#define TIMED_OUTPUT +#endif + +/********************************************************* + * + * aw869x.h + * + ********************************************************/ +#include +#include +#include +#include +#include +#include +#include +#ifdef TIMED_OUTPUT +#include <../../../drivers/staging/android/timed_output.h> +#else +#include +#endif +#include + +/********************************************************* + * + * marco + * + ********************************************************/ +#define MAX_I2C_BUFFER_SIZE 65536 + +#define AW869X_REG_MAX 0xff + +#define AW869X_SEQUENCER_SIZE 4 +#define AW869X_SEQUENCER_LOOP_SIZE 2 + +#define AW869X_RTP_I2C_SINGLE_MAX_NUM 512 + +#define HAPTIC_MAX_TIMEOUT 10000 + +//#define AW869X_HAPTIC_VBAT_MONITOR + +#ifdef AW869X_HAPTIC_VBAT_MONITOR +//#define AWINIC_GET_BATTERY_READ_NODE +#define SYS_BAT_DEV "/sys/class/power_supply/battery/voltage_now" +#define AW869X_SYS_VBAT_REFERENCE 4200000 +#define AW869X_SYS_VBAT_MIN 3000000 +#define AW869X_SYS_VBAT_MAX 4500000 +#endif + +enum aw869x_flags { + AW869X_FLAG_NONR = 0, + AW869X_FLAG_SKIP_INTERRUPTS = 1, +}; + +enum aw869x_chipids { + AW8691_ID = 0, +}; + +enum aw869x_haptic_read_write { + AW869X_HAPTIC_CMD_READ_REG = 0, + AW869X_HAPTIC_CMD_WRITE_REG = 1, + AW869X_HAPTIC_CMD_UPDATE_FIRMWARE = 2, + AW869X_HAPTIC_CMD_READ_FIRMWARE = 3, +}; + + +enum aw869x_haptic_work_mode { + AW869X_HAPTIC_STANDBY_MODE = 0, + AW869X_HAPTIC_RAM_MODE = 1, + AW869X_HAPTIC_RTP_MODE = 2, + AW869X_HAPTIC_TRIG_MODE = 3, +}; + +enum aw869x_haptic_bst_mode { + AW869X_HAPTIC_BYPASS_MODE = 0, + AW869X_HAPTIC_BOOST_MODE = 1, +}; + +enum aw869x_haptic_mode{ + HAPTIC_NONE = 0x00, + HAPTIC_SHORT = 0x01, + HAPTIC_LONG = 0x02, + HAPTIC_RTP = 0x03, +}; + +/********************************************************* + * + * struct + * + ********************************************************/ +struct fileops { + unsigned char cmd; + unsigned char reg; + unsigned char ram_addrh; + unsigned char ram_addrl; +}; + +struct ram { + unsigned int len; + unsigned int check_sum; + unsigned int base_addr; + unsigned char version; + unsigned char ram_shift; + unsigned char baseaddr_shift; +}; + +struct aw869x { + struct regmap *regmap; + struct i2c_client *i2c; + struct device *dev; + struct input_dev *input; + + struct wakeup_source *ws; + struct mutex lock; + struct hrtimer timer; + struct work_struct vibrator_work; + struct work_struct rtp_work; +#ifdef TIMED_OUTPUT + struct timed_output_dev to_dev; +#else + struct led_classdev cdev; +#endif + + int reset_gpio; + int irq_gpio; + int haptic_context_gpio; + int state; + int duration; + int amplitude; + int index; + int vmax; + int gain; + int gain_debug; + int seq; + int loop; + + enum aw869x_haptic_mode haptic_mode; + bool factory_mode; + bool debugfs_debug; + unsigned char hwen_flag; + unsigned char flags; + unsigned char chipid; + + unsigned int rtp_cnt; + unsigned int rtp_file_num; + + unsigned char rtp_init; + unsigned char ram_init; + + unsigned char play_mode; + struct fileops fileops; + struct ram ram; + + struct hrtimer ram_timer; + struct work_struct ram_work; +}; + +struct aw869x_container{ + int len; + unsigned char data[]; +}; + + +/********************************************************* + * + * ioctl + * + ********************************************************/ +struct aw869x_seq_loop { + unsigned char loop[AW869X_SEQUENCER_SIZE]; +}; + +struct aw869x_que_seq { + unsigned char index[AW869X_SEQUENCER_SIZE]; +}; + + +#define AW869X_HAPTIC_IOCTL_MAGIC 'h' + +#define AW869X_HAPTIC_SET_QUE_SEQ _IOWR(AW869X_HAPTIC_IOCTL_MAGIC, 1, struct aw869x_que_seq*) +#define AW869X_HAPTIC_SET_SEQ_LOOP _IOWR(AW869X_HAPTIC_IOCTL_MAGIC, 2, struct aw869x_seq_loop*) +#define AW869X_HAPTIC_PLAY_QUE_SEQ _IOWR(AW869X_HAPTIC_IOCTL_MAGIC, 3, unsigned int) +#define AW869X_HAPTIC_SET_BST_VOL _IOWR(AW869X_HAPTIC_IOCTL_MAGIC, 4, unsigned int) +#define AW869X_HAPTIC_SET_BST_PEAK_CUR _IOWR(AW869X_HAPTIC_IOCTL_MAGIC, 5, unsigned int) +#define AW869X_HAPTIC_SET_GAIN _IOWR(AW869X_HAPTIC_IOCTL_MAGIC, 6, unsigned int) +#define AW869X_HAPTIC_PLAY_REPEAT_SEQ _IOWR(AW869X_HAPTIC_IOCTL_MAGIC, 7, unsigned int) + + +#endif + diff --git a/drivers/misc/awinic/aw869x/aw869x_reg.h b/drivers/misc/awinic/aw869x/aw869x_reg.h new file mode 100644 index 000000000000..892fd15da336 --- /dev/null +++ b/drivers/misc/awinic/aw869x/aw869x_reg.h @@ -0,0 +1,294 @@ +#ifndef _AW869X_REG_H_ +#define _AW869X_REG_H_ + +/******************************************** + * Register List + *******************************************/ +#define AW869X_REG_ID 0x00 +#define AW869X_REG_SYSST 0x01 +#define AW869X_REG_SYSINT 0x02 +#define AW869X_REG_SYSINTM 0x03 +#define AW869X_REG_SYSCTRL 0x04 +#define AW869X_REG_PROC_CTRL 0x05 +#define AW869X_REG_RTP_DATA 0x06 +#define AW869X_REG_QUE_SEQ1 0x07 +#define AW869X_REG_QUE_SEQ2 0x08 +#define AW869X_REG_QUE_SEQ3 0x09 +#define AW869X_REG_QUE_SEQ4 0x0A +#define AW869X_REG_SEQ_LOOP1 0x0B +#define AW869X_REG_SEQ_LOOP2 0x0C +#define AW869X_REG_TRG1_WAV_P 0x0D +#define AW869X_REG_TRG2_WAV_P 0x0E +#define AW869X_REG_TRG3_WAV_P 0x0F +#define AW869X_REG_TRG1_WAV_N 0x10 +#define AW869X_REG_TRG2_WAV_N 0x11 +#define AW869X_REG_TRG3_WAV_N 0x12 +#define AW869X_REG_TRG_PRIO 0x13 +#define AW869X_REG_TRG_CFG 0x14 +#define AW869X_REG_DBGCTRL 0x20 +#define AW869X_REG_BASE_ADDRH 0x21 +#define AW869X_REG_BASE_ADDRL 0x22 +#define AW869X_REG_FIFO_AEH 0x23 +#define AW869X_REG_FIFO_AEL 0x24 +#define AW869X_REG_FIFO_AFH 0x25 +#define AW869X_REG_FIFO_AFL 0x26 +#define AW869X_REG_WAKE_DLY 0x27 +#define AW869X_REG_START_DLY 0x28 +#define AW869X_REG_END_DLY_H 0x29 +#define AW869X_REG_END_DLY_L 0x2A +#define AW869X_REG_DATACTRL 0x2B +#define AW869X_REG_PWMDEL 0x2C +#define AW869X_REG_PWMPSC 0x2D +#define AW869X_REG_PWMDBG 0x2E +#define AW869X_REG_LDOCTRL 0x2F +#define AW869X_REG_DBGSTAT 0x30 +#define AW869X_REG_BSTDBG1 0x31 +#define AW869X_REG_BSTDBG2 0x32 +#define AW869X_REG_BSTDBG3 0x33 +#define AW869X_REG_BSTCFG 0x34 +#define AW869X_REG_ANADBG 0x35 +#define AW869X_REG_ANACTRL 0x36 +#define AW869X_REG_OSCDBG 0x37 +#define AW869X_REG_GLBDBG 0x38 +#define AW869X_REG_DATDBG 0x39 +#define AW869X_REG_RAMADDRH 0x40 +#define AW869X_REG_RAMADDRL 0x41 +#define AW869X_REG_RAMDATA 0x42 + + +/******************************************** + * Register Access + *******************************************/ +#define REG_NONE_ACCESS 0 +#define REG_RD_ACCESS 1 << 0 +#define REG_WR_ACCESS 1 << 1 + +const unsigned char aw869x_reg_access[AW869X_REG_MAX]={ + [AW869X_REG_ID ] = REG_RD_ACCESS, + [AW869X_REG_SYSST ] = REG_RD_ACCESS, + [AW869X_REG_SYSINT ] = REG_RD_ACCESS, + [AW869X_REG_SYSINTM ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_SYSCTRL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_PROC_CTRL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_RTP_DATA ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_QUE_SEQ1 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_QUE_SEQ2 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_QUE_SEQ3 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_QUE_SEQ4 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_SEQ_LOOP1 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_SEQ_LOOP2 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_TRG1_WAV_P] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_TRG2_WAV_P] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_TRG3_WAV_P] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_TRG1_WAV_N] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_TRG2_WAV_N] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_TRG3_WAV_N] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_TRG_PRIO ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_TRG_CFG ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_DBGCTRL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_BASE_ADDRH] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_BASE_ADDRL] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_FIFO_AEH ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_FIFO_AEL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_FIFO_AFH ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_FIFO_AFL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_WAKE_DLY ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_START_DLY ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_END_DLY_H ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_END_DLY_L ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_DATACTRL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_PWMDEL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_PWMPSC ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_PWMDBG ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_LDOCTRL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_DBGSTAT ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_BSTDBG1 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_BSTDBG2 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_BSTDBG3 ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_BSTCFG ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_ANADBG ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_ANACTRL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_OSCDBG ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_GLBDBG ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_DATDBG ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_RAMADDRH ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_RAMADDRL ] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW869X_REG_RAMDATA ] = REG_RD_ACCESS|REG_WR_ACCESS, +}; +/****************************************************** + * Register Detail + *****************************************************/ +// SYSST +#define AW869X_BIT_SYSST_UVLS (1<<5) +#define AW869X_BIT_SYSST_FF_AES (1<<4) +#define AW869X_BIT_SYSST_FF_AFS (1<<3) +#define AW869X_BIT_SYSST_OCDS (1<<2) +#define AW869X_BIT_SYSST_OTS (1<<1) +#define AW869X_BIT_SYSST_DONES (1<<0) + +// SYSINT +#define AW869X_BIT_SYSINT_UVLI (1<<5) +#define AW869X_BIT_SYSINT_FF_AEI (1<<4) +#define AW869X_BIT_SYSINT_FF_AFI (1<<3) +#define AW869X_BIT_SYSINT_OCDI (1<<2) +#define AW869X_BIT_SYSINT_OTI (1<<1) +#define AW869X_BIT_SYSINT_DONEI (1<<0) + +// SYSINTM +#define AW869X_BIT_SYSINTM_UVLO_MASK (~(1<<5)) +#define AW869X_BIT_SYSINTM_UVLO_OFF (1<<5) +#define AW869X_BIT_SYSINTM_UVLO_EN (0<<5) +#define AW869X_BIT_SYSINTM_FF_AE_MASK (~(1<<4)) +#define AW869X_BIT_SYSINTM_FF_AE_OFF (1<<4) +#define AW869X_BIT_SYSINTM_FF_AE_EN (0<<4) +#define AW869X_BIT_SYSINTM_FF_AF_MASK (~(1<<3)) +#define AW869X_BIT_SYSINTM_FF_AF_OFF (1<<3) +#define AW869X_BIT_SYSINTM_FF_AF_EN (0<<3) +#define AW869X_BIT_SYSINTM_OCD_MASK (~(1<<2)) +#define AW869X_BIT_SYSINTM_OCD_OFF (1<<2) +#define AW869X_BIT_SYSINTM_OCD_EN (0<<2) +#define AW869X_BIT_SYSINTM_OT_MASK (~(1<<1)) +#define AW869X_BIT_SYSINTM_OT_OFF (1<<1) +#define AW869X_BIT_SYSINTM_OT_EN (0<<1) +#define AW869X_BIT_SYSINTM_DONE_MASK (~(1<<0)) +#define AW869X_BIT_SYSINTM_DONE_OFF (1<<0) +#define AW869X_BIT_SYSINTM_DONE_EN (0<<0) + +// SYSCTRL +#define AW869X_BIT_SYSCTRL_RAMINIT_MASK (~(1<<5)) +#define AW869X_BIT_SYSCTRL_RAMINIT_EN (1<<5) +#define AW869X_BIT_SYSCTRL_RAMINIT_OFF (0<<5) +#define AW869X_BIT_SYSCTRL_PLAY_MODE_MASK (~(1<<3)) +#define AW869X_BIT_SYSCTRL_PLAY_MODE_RTP (1<<3) +#define AW869X_BIT_SYSCTRL_PLAY_MODE_RAM (0<<3) +#define AW869X_BIT_SYSCTRL_BST_MODE_MASK (~(1<<2)) +#define AW869X_BIT_SYSCTRL_BST_MODE_BOOST (1<<2) +#define AW869X_BIT_SYSCTRL_BST_MODE_BYPASS (0<<2) +#define AW869X_BIT_SYSCTRL_WORK_MODE_MASK (~(1<<0)) +#define AW869X_BIT_SYSCTRL_STANDBY (1<<0) +#define AW869X_BIT_SYSCTRL_ACTIVE (0<<0) + +// PROC_CTRL +#define AW869X_BIT_PROC_CTRL_GO (1<<0) + +// WAV_SEQ1 +#define AW869X_BIT_QUE_SEQ1_WAIT (1<<7) +#define AW869X_BIT_QUE_SEQ1_WAV_FRM_SEQ1_MASK (~(128<<0)) + +// WAV_SEQ2 +#define AW869X_BIT_QUE_SEQ2_WAIT (1<<7) +#define AW869X_BIT_QUE_SEQ2_WAV_FRM_SEQ2_MASK (~(128<<0)) + +// WAV_SEQ3 +#define AW869X_BIT_QUE_SEQ3_WAIT (1<<7) +#define AW869X_BIT_QUE_SEQ3_WAV_FRM_SEQ3_MASK (~(128<<0)) + +// WAV_SEQ4 +#define AW869X_BIT_QUE_SEQ4_WAIT (1<<7) +#define AW869X_BIT_QUE_SEQ4_WAV_FRM_SEQ4_MASK (~(128<<0)) + + +// WAV_LOOP1 +#define AW869X_BIT_SEQ_LOOP1_SEQ1_MASK (~(16<<4)) +#define AW869X_BIT_SEQ_LOOP1_SEQ2_MASK (~(16<<0)) + +// WAV_LOOP2 +#define AW869X_BIT_SEQ_LOOP2_SEQ1_MASK (~(16<<4)) +#define AW869X_BIT_SEQ_LOOP2_SEQ2_MASK (~(16<<0)) + +// TRG_PRIO +#define AW869X_BIT_TRG_PRIO_TRIG3_MASK (~(3<<4)) +#define AW869X_BIT_TRG_PRIO_TRIG2_MASK (~(3<<2)) +#define AW869X_BIT_TRG_PRIO_TRIG1_MASK (~(3<<0)) + +// TRG_CFG +#define AW869X_BIT_TRG_CFG_TRG3_PULSE_MASK (~(1<<5)) +#define AW869X_BIT_TRG_CFG_TRG3_PULSE_NEG (1<<5) +#define AW869X_BIT_TRG_CFG_TRG3_PULSE_POS (0<<5) +#define AW869X_BIT_TRG_CFG_TRG3_EDGE_MASK (~(1<<4)) +#define AW869X_BIT_TRG_CFG_TRG3_EDGE_POS (1<<4) +#define AW869X_BIT_TRG_CFG_TRG3_EDGE_POS_NEG (0<<4) +#define AW869X_BIT_TRG_CFG_TRG2_PULSE_MASK (~(1<<3)) +#define AW869X_BIT_TRG_CFG_TRG2_PULSE_NEG (1<<3) +#define AW869X_BIT_TRG_CFG_TRG2_PULSE_POS (0<<3) +#define AW869X_BIT_TRG_CFG_TRG2_EDGE_MASK (~(1<<2)) +#define AW869X_BIT_TRG_CFG_TRG2_EDGE_POS (1<<2) +#define AW869X_BIT_TRG_CFG_TRG2_EDGE_POS_NEG (0<<2) +#define AW869X_BIT_TRG_CFG_TRG1_PULSE_MASK (~(1<<1)) +#define AW869X_BIT_TRG_CFG_TRG1_PULSE_NEG (1<<1) +#define AW869X_BIT_TRG_CFG_TRG1_PULSE_POS (0<<1) +#define AW869X_BIT_TRG_CFG_TRG1_EDGE_MASK (~(1<<0)) +#define AW869X_BIT_TRG_CFG_TRG1_EDGE_POS (1<<0) +#define AW869X_BIT_TRG_CFG_TRG1_EDGE_POS_NEG (0<<0) + +// DBGCTRL +#define AW869X_BIT_DBGCTRL_EN_BST (1<<7) +#define AW869X_BIT_DBGCTRL_EN_HDRIVER (1<<6) +#define AW869X_BIT_DBGCTRL_EN_UVLO (1<<5) +#define AW869X_BIT_DBGCTRL_OVERRIDE_MASK (~(1<<4)) +#define AW869X_BIT_DBGCTRL_EN_OVERRIDE (1<<4) +#define AW869X_BIT_DBGCTRL_DIS_OVERRIDE (1<<4) +#define AW869X_BIT_DBGCTRL_INT_EDGE_MASK (~(1<<3)) +#define AW869X_BIT_DBGCTRL_INT_EDGE_POS_NEG (1<<3) +#define AW869X_BIT_DBGCTRL_INT_EDGE_POS (0<<3) +#define AW869X_BIT_DBGCTRL_INTMODE_MASK (~(1<<2)) +#define AW869X_BIT_DBGCTRL_INTMODE_EDGE (1<<2) +#define AW869X_BIT_DBGCTRL_INTMODE_LEVEL (0<<2) +#define AW869X_BIT_DBGCTRL_WAITSLOT_MASK (~(3<<0)) +#define AW869X_BIT_DBGCTRL_WAITSLOT_10MS (3<<0) +#define AW869X_BIT_DBGCTRL_WAITSLOT_1280US (2<<0) +#define AW869X_BIT_DBGCTRL_WAITSLOT_160US (1<<0) +#define AW869X_BIT_DBGCTRL_WAITSLOT_20US (0<<0) + +// DATACTRL +#define AW869X_BIT_DATACTRL_INTP_MASK (~(1<<4)) +#define AW869X_BIT_DATACTRL_INTP_HIGH (1<<4) +#define AW869X_BIT_DATACTRL_INTP_LOW (0<<4) +#define AW869X_BIT_DATACTRL_WAV_DBG_MASK (~(1<<3)) +#define AW869X_BIT_DATACTRL_WAV_DBG (1<<3) +#define AW869X_BIT_DATACTRL_WAV_NORMAL (0<<3) +#define AW869X_BIT_DATACTRL_WAKE_MASK (~(1<<2)) +#define AW869X_BIT_DATACTRL_WAKE_AF_WAKE_DLY (1<<2) +#define AW869X_BIT_DATACTRL_WAKE_AF_BST_OK (0<<2) +#define AW869X_BIT_DATACTRL_END_MODE_MASK (~(1<<1)) +#define AW869X_BIT_DATACTRL_END_DELAY (1<<1) +#define AW869X_BIT_DATACTRL_END_BACK_WAIT (0<<1) + +// BSTCFG +#define AW869X_BIT_BSTCFG_BSTVOL_MASK (~(31<<3)) +#define AW869X_BIT_BSTCFG_BSTVOL_10P5V (18<<3) +#define AW869X_BIT_BSTCFG_BSTVOL_10P25V (17<<3) +#define AW869X_BIT_BSTCFG_BSTVOL_10V (16<<3) +#define AW869X_BIT_BSTCFG_BSTVOL_9P75V (15<<3) +#define AW869X_BIT_BSTCFG_BSTVOL_9P5V (14<<3) +#define AW869X_BIT_BSTCFG_BSTVOL_9P25V (13<<3) +#define AW869X_BIT_BSTCFG_BSTVOL_9V (12<<3) +#define AW869X_BIT_BSTCFG_BSTVOL_8P75V (11<<3) +#define AW869X_BIT_BSTCFG_BSTVOL_8P5V (10<<3) +#define AW869X_BIT_BSTCFG_BSTVOL_8P25V (9<<3) +#define AW869X_BIT_BSTCFG_BSTVOL_8V (8<<3) +#define AW869X_BIT_BSTCFG_BSTVOL_7P75V (7<<3) +#define AW869X_BIT_BSTCFG_BSTVOL_7P5V (6<<3) +#define AW869X_BIT_BSTCFG_BSTVOL_7P25V (5<<3) +#define AW869X_BIT_BSTCFG_BSTVOL_7V (4<<3) +#define AW869X_BIT_BSTCFG_BSTVOL_6P75V (3<<3) +#define AW869X_BIT_BSTCFG_BSTVOL_6P5V (2<<3) +#define AW869X_BIT_BSTCFG_BSTVOL_6P25V (1<<3) +#define AW869X_BIT_BSTCFG_BSTVOL_6V (0<<3) +#define AW869X_BIT_BSTCFG_PEAKCUR_MASK (~(7<<0)) +#define AW869X_BIT_BSTCFG_PEAKCUR_4A (7<<0) +#define AW869X_BIT_BSTCFG_PEAKCUR_3P75A (6<<0) +#define AW869X_BIT_BSTCFG_PEAKCUR_3P5A (5<<0) +#define AW869X_BIT_BSTCFG_PEAKCUR_3P25A (4<<0) +#define AW869X_BIT_BSTCFG_PEAKCUR_3A (3<<0) +#define AW869X_BIT_BSTCFG_PEAKCUR_2P5A (2<<0) +#define AW869X_BIT_BSTCFG_PEAKCUR_2A (1<<0) +#define AW869X_BIT_BSTCFG_PEAKCUR_1P5A (0<<0) + +// PWMDBG +#define AW869X_BIT_PWMDBG_PWMCLK_MODE_MASK (~(3<<5)) +#define AW869X_BIT_PWMDBG_PWMCLK_MODE_12KB (3<<5) +#define AW869X_BIT_PWMDBG_PWMCLK_MODE_24KB (2<<5) +#define AW869X_BIT_PWMDBG_PWMCLK_MODE_48KB (0<<5) + +#endif diff --git a/drivers/misc/awinic/aw869xx/Android.mk b/drivers/misc/awinic/aw869xx/Android.mk new file mode 100644 index 000000000000..2f44199f159d --- /dev/null +++ b/drivers/misc/awinic/aw869xx/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := aw869xx.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/awinic/aw869xx/Kbuild b/drivers/misc/awinic/aw869xx/Kbuild new file mode 100644 index 000000000000..fdd4ffdd0f12 --- /dev/null +++ b/drivers/misc/awinic/aw869xx/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += aw869xx.o diff --git a/drivers/misc/awinic/aw869xx/Kconfig b/drivers/misc/awinic/aw869xx/Kconfig new file mode 100644 index 000000000000..df4fa4f6f87f --- /dev/null +++ b/drivers/misc/awinic/aw869xx/Kconfig @@ -0,0 +1,13 @@ +# +# Mediatek AW869XX Driver +# + +config AW869XX_HAPTIC + tristate "Awinic AW869XX haptic driver" + depends on I2C + help + Say Y here if you have Awinic AW869XX haptic controller + chip in your system. + If unsure, say N. + To compile this driver as a module, choose M here: the + module will be called awinic_haptic. diff --git a/drivers/misc/awinic/aw869xx/Makefile b/drivers/misc/awinic/aw869xx/Makefile new file mode 100644 index 000000000000..a31fdcfa587d --- /dev/null +++ b/drivers/misc/awinic/aw869xx/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_AW869XX_HAPTIC) += aw869xx.o diff --git a/drivers/misc/awinic/aw869xx/aw869xx.c b/drivers/misc/awinic/aw869xx/aw869xx.c new file mode 100644 index 000000000000..149343c13d13 --- /dev/null +++ b/drivers/misc/awinic/aw869xx/aw869xx.c @@ -0,0 +1,4405 @@ +/* + * aw869xx.c + * + * Copyright (c) 2019 AWINIC Technology CO., LTD + * + * Author: Peacek + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aw869xx_reg.h" +#include "aw869xx.h" + +/****************************************************** + * + * Version Marco + * + ******************************************************/ +#define AW869XX_VERSION "v0.1.2" + +/****************************************************** + * + * aw869xx distinguish between codecs and components by version + * + ******************************************************/ +#ifdef AW_KERNEL_VER_OVER_4_19 +static const struct aw_componet_codec_ops aw_componet_codec_ops = { + .aw_snd_soc_kcontrol_codec = snd_soc_kcontrol_component, + .aw_snd_soc_codec_get_drvdata = snd_soc_component_get_drvdata, + .aw_snd_soc_add_codec_controls = snd_soc_add_component_controls, + .aw_snd_soc_unregister_codec = snd_soc_unregister_component, + .aw_snd_soc_register_codec = snd_soc_register_component, +}; +#else +static const struct aw_componet_codec_ops aw_componet_codec_ops = { + .aw_snd_soc_kcontrol_codec = snd_soc_kcontrol_codec, + .aw_snd_soc_codec_get_drvdata = snd_soc_codec_get_drvdata, + .aw_snd_soc_add_codec_controls = snd_soc_add_codec_controls, + .aw_snd_soc_unregister_codec = snd_soc_unregister_codec, + .aw_snd_soc_register_codec = snd_soc_register_codec, +}; +#endif + +static aw_snd_soc_codec_t *aw_get_codec(struct snd_soc_dai *dai) +{ +#ifdef AW_KERNEL_VER_OVER_4_19 + return dai->component; +#else + return dai->codec; +#endif +} + +static void do_gettimeofday(struct timeval *tv) +{ + struct timespec64 now; + + ktime_get_real_ts64(&now); + tv->tv_sec = now.tv_sec; + tv->tv_usec = now.tv_nsec/1000; +} + +/****************************************************** + * + * variable + * + ******************************************************/ +static char *aw869xx_ram_name = "aw869xx_haptic.bin"; +static char aw869xx_rtp_name[][AW869XX_RTP_NAME_MAX] = { + {"aw869xx_osc_rtp_24K_5s.bin"}, + {"aw869xx_rtp.bin"}, + {"aw869xx_rtp_lighthouse.bin"}, + {"aw869xx_rtp_silk.bin"}, +}; + +struct pm_qos_request pm_qos_req_vb; +struct aw869xx_container *aw869xx_rtp; + +/****************************************************** + * + * functions + * + ******************************************************/ +static void aw869xx_interrupt_clear(struct aw869xx *aw869xx); + /****************************************************** + * + * aw869xx i2c write/read + * + ******************************************************/ +static int aw869xx_i2c_write(struct aw869xx *aw869xx, + unsigned char reg_addr, unsigned char reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + while (cnt < AW_I2C_RETRIES) { + ret = + i2c_smbus_write_byte_data(aw869xx->i2c, reg_addr, reg_data); + if (ret < 0) { + aw_dev_err(aw869xx->dev, + "%s: i2c_write addr=0x%02X, data=0x%02X, cnt=%d, error=%d\n", + __func__, reg_addr, reg_data, cnt, ret); + } else { + break; + } + cnt++; + usleep_range(AW_I2C_RETRY_DELAY * 1000, + AW_I2C_RETRY_DELAY * 1000 + 500); + } + return ret; +} + +static int aw869xx_i2c_read(struct aw869xx *aw869xx, + unsigned char reg_addr, unsigned char *reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + while (cnt < AW_I2C_RETRIES) { + ret = i2c_smbus_read_byte_data(aw869xx->i2c, reg_addr); + if (ret < 0) { + aw_dev_err(aw869xx->dev, + "%s: i2c_read addr=0x%02X, cnt=%d error=%d\n", + __func__, reg_addr, cnt, ret); + } else { + *reg_data = ret; + break; + } + cnt++; + usleep_range(AW_I2C_RETRY_DELAY * 1000, + AW_I2C_RETRY_DELAY * 1000 + 500); + } + return ret; +} + +static int aw869xx_i2c_write_bits(struct aw869xx *aw869xx, + unsigned char reg_addr, unsigned int mask, + unsigned char reg_data) +{ + int ret = -1; + unsigned char reg_val = 0; + + ret = aw869xx_i2c_read(aw869xx, reg_addr, ®_val); + if (ret < 0) { + aw_dev_err(aw869xx->dev, "%s: i2c read error, ret=%d\n", + __func__, ret); + return ret; + } + reg_val &= mask; + reg_val |= reg_data; + ret = aw869xx_i2c_write(aw869xx, reg_addr, reg_val); + if (ret < 0) { + aw_dev_err(aw869xx->dev, "%s: i2c write error, ret=%d\n", + __func__, ret); + return ret; + } + return 0; +} + +static int aw869xx_i2c_writes(struct aw869xx *aw869xx, + unsigned char reg_addr, unsigned char *buf, + unsigned int len) +{ + int ret = -1; + unsigned char *data; + + data = kmalloc(len + 1, GFP_KERNEL); + if (data == NULL) { + aw_dev_err(aw869xx->dev, "%s: can not allocate memory\n", + __func__); + return -ENOMEM; + } + data[0] = reg_addr; + memcpy(&data[1], buf, len); + ret = i2c_master_send(aw869xx->i2c, data, len + 1); + if (ret < 0) + aw_dev_err(aw869xx->dev, "%s: i2c master send error\n", + __func__); + kfree(data); + return ret; +} + +static void aw869xx_haptic_raminit(struct aw869xx *aw869xx, bool flag) +{ + if (flag) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL1, + AW869XX_BIT_SYSCTRL1_RAMINIT_MASK, + AW869XX_BIT_SYSCTRL1_RAMINIT_ON); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL1, + AW869XX_BIT_SYSCTRL1_RAMINIT_MASK, + AW869XX_BIT_SYSCTRL1_RAMINIT_OFF); + } +} + +static void aw869xx_haptic_play_go(struct aw869xx *aw869xx) +{ + aw869xx_i2c_write(aw869xx, AW869XX_REG_PLAYCFG4, 0x01); +} + +static int aw869xx_haptic_stop(struct aw869xx *aw869xx) +{ + unsigned char cnt = 40; + unsigned char reg_val = 0; + bool force_flag = true; + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + aw869xx->play_mode = AW869XX_HAPTIC_STANDBY_MODE; + aw869xx_i2c_read(aw869xx, AW869XX_REG_GLBRD5, ®_val); + if ((reg_val & 0x0f) == 0x00 || (reg_val & 0x0f) == 0x0A) { + force_flag = false; + aw_dev_info(aw869xx->dev, + "%s already in standby mode! glb_state=0x%02X\n", + __func__, reg_val); + } else { + aw869xx_i2c_write(aw869xx, AW869XX_REG_PLAYCFG4, 0x02); + while (cnt) { + aw869xx_i2c_read(aw869xx, AW869XX_REG_GLBRD5, ®_val); + if ((reg_val & 0x0f) == 0x00 + || (reg_val & 0x0f) == 0x0A) { + cnt = 0; + force_flag = false; + aw_dev_info(aw869xx->dev, + "%s entered standby! glb_state=0x%02X\n", + __func__, reg_val); + } else { + cnt--; + pr_debug + ("%s wait for standby, glb_state=0x%02X\n", + __func__, reg_val); + } + usleep_range(2000, 2500); + } + } + if (force_flag) { + aw_dev_err(aw869xx->dev, "%s force to enter standby mode!\n", + __func__); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_STANDBY_MASK, + AW869XX_BIT_SYSCTRL2_STANDBY_ON); + } + return 0; +} + +static void aw869xx_container_update(struct aw869xx *aw869xx, + struct aw869xx_container *aw869xx_cont) +{ + int i = 0; + unsigned int shift = 0; + unsigned char reg_val = 0; + unsigned int temp = 0; + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + mutex_lock(&aw869xx->lock); + aw869xx->ram.baseaddr_shift = 2; + aw869xx->ram.ram_shift = 4; + /* RAMINIT Enable */ + aw869xx_haptic_raminit(aw869xx, true); + /* Enter standby mode */ + aw869xx_haptic_stop(aw869xx); + /* base addr */ + shift = aw869xx->ram.baseaddr_shift; + aw869xx->ram.base_addr = + (unsigned int)((aw869xx_cont->data[0 + shift] << 8) | + (aw869xx_cont->data[1 + shift])); + aw_dev_info(aw869xx->dev, "%s: base_addr = %d\n", __func__, + aw869xx->ram.base_addr); + + aw869xx_i2c_write(aw869xx, AW869XX_REG_RTPCFG1, /*ADDRH*/ + aw869xx_cont->data[0 + shift]); + aw869xx_i2c_write(aw869xx, AW869XX_REG_RTPCFG2, /*ADDRL*/ + aw869xx_cont->data[1 + shift]); + /* FIFO_AEH */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_RTPCFG3, + AW869XX_BIT_RTPCFG3_FIFO_AEH_MASK, + (unsigned + char)(((aw869xx-> + ram.base_addr >> 1) >> 4) & 0xF0)); + /* FIFO AEL */ + aw869xx_i2c_write(aw869xx, AW869XX_REG_RTPCFG4, + (unsigned + char)(((aw869xx->ram.base_addr >> 1) & 0x00FF))); + /* FIFO_AFH */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_RTPCFG3, + AW869XX_BIT_RTPCFG3_FIFO_AFH_MASK, + (unsigned char)(((aw869xx->ram.base_addr - + (aw869xx-> + ram.base_addr >> 2)) >> 8) & + 0x0F)); + /* FIFO_AFL */ + aw869xx_i2c_write(aw869xx, AW869XX_REG_RTPCFG5, + (unsigned char)(((aw869xx->ram.base_addr - + (aw869xx-> + ram.base_addr >> 2)) & 0x00FF))); +/* +* unsigned int temp +* HIGHLOW +* |_ _ _ _AF-12BIT_ _ _ _AE-12BIT| +*/ + aw869xx_i2c_read(aw869xx, AW869XX_REG_RTPCFG3, ®_val); + temp = ((reg_val & 0x0f) << 24) | ((reg_val & 0xf0) << 4); + aw869xx_i2c_read(aw869xx, AW869XX_REG_RTPCFG4, ®_val); + temp = temp | reg_val; + aw_dev_info(aw869xx->dev, "%s: almost_empty_threshold = %d\n", __func__, + (unsigned short)temp); + aw869xx_i2c_read(aw869xx, AW869XX_REG_RTPCFG5, ®_val); + temp = temp | (reg_val << 16); + aw_dev_info(aw869xx->dev, "%s: almost_full_threshold = %d\n", __func__, + temp >> 16); + /* ram */ + shift = aw869xx->ram.baseaddr_shift; + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_RAMADDRH, + AW869XX_BIT_RAMADDRH_MASK, + aw869xx_cont->data[0 + shift]); + aw869xx_i2c_write(aw869xx, AW869XX_REG_RAMADDRL, + aw869xx_cont->data[1 + shift]); + shift = aw869xx->ram.ram_shift; + for (i = shift; i < aw869xx_cont->len; i++) { + aw869xx->ram_update_flag = aw869xx_i2c_write(aw869xx, + AW869XX_REG_RAMDATA, + aw869xx_cont->data + [i]); + } +#ifdef AW_CHECK_RAM_DATA + shift = aw869xx->ram.baseaddr_shift; + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_RAMADDRH, + AW869XX_BIT_RAMADDRH_MASK, + aw869xx_cont->data[0 + shift]); + aw869xx_i2c_write(aw869xx, AW869XX_REG_RAMADDRL, + aw869xx_cont->data[1 + shift]); + shift = aw869xx->ram.ram_shift; + for (i = shift; i < aw869xx_cont->len; i++) { + aw869xx_i2c_read(aw869xx, AW869XX_REG_RAMDATA, ®_val); + /* + aw_dev_info(aw869xx->dev, "%s aw869xx_cont->data=0x%02X, ramdata=0x%02X\n", + __func__,aw869xx_cont->data[i],reg_val); + */ + if (reg_val != aw869xx_cont->data[i]) { + aw_dev_err(aw869xx->dev, + "%s: ram check error addr=0x%04x, file_data=0x%02X, ram_data=0x%02X\n", + __func__, i, aw869xx_cont->data[i], reg_val); + return; + } + } +#endif + /* RAMINIT Disable */ + aw869xx_haptic_raminit(aw869xx, false); + mutex_unlock(&aw869xx->lock); + aw_dev_info(aw869xx->dev, "%s exit\n", __func__); +} + +static void aw869xx_ram_loaded(const struct firmware *cont, void *context) +{ + struct aw869xx *aw869xx = context; + struct aw869xx_container *aw869xx_fw; + int i = 0; + unsigned short check_sum = 0; +#ifdef AW_READ_BIN_FLEXBALLY + static unsigned char load_cont; + int ram_timer_val = 1000; + + load_cont++; +#endif + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + if (!cont) { + aw_dev_err(aw869xx->dev, "%s: failed to read %s\n", __func__, + aw869xx_ram_name); + release_firmware(cont); +#ifdef AW_READ_BIN_FLEXBALLY + if (load_cont <= 20) { + schedule_delayed_work(&aw869xx->ram_work, + msecs_to_jiffies(ram_timer_val)); + aw_dev_info(aw869xx->dev, + "%s:start hrtimer: load_cont=%d\n", + __func__, load_cont); + } +#endif + return; + } + aw_dev_info(aw869xx->dev, "%s: loaded %s - size: %zu bytes\n", __func__, + aw869xx_ram_name, cont ? cont->size : 0); +/* + for(i=0; i < cont->size; i++) { + aw_dev_info(aw869xx->dev, "%s: addr: 0x%04x, data: 0x%02X\n", + __func__, i, *(cont->data+i)); + } +*/ + /* check sum */ + for (i = 2; i < cont->size; i++) + check_sum += cont->data[i]; + if (check_sum != + (unsigned short)((cont->data[0] << 8) | (cont->data[1]))) { + aw_dev_err(aw869xx->dev, + "%s: check sum err: check_sum=0x%04x\n", __func__, + check_sum); + return; + } else { + aw_dev_info(aw869xx->dev, "%s: check sum pass: 0x%04x\n", + __func__, check_sum); + aw869xx->ram.check_sum = check_sum; + } + + /* aw869xx ram update less then 128kB*/ + aw869xx_fw = kzalloc(cont->size + sizeof(int), GFP_KERNEL); + if (!aw869xx_fw) { + release_firmware(cont); + aw_dev_err(aw869xx->dev, "%s: Error allocating memory\n", + __func__); + return; + } + aw869xx_fw->len = cont->size; + memcpy(aw869xx_fw->data, cont->data, cont->size); + release_firmware(cont); + aw869xx_container_update(aw869xx, aw869xx_fw); + aw869xx->ram.len = aw869xx_fw->len; + kfree(aw869xx_fw); + aw869xx->ram_init = 1; + aw_dev_info(aw869xx->dev, "%s: ram firmware update complete!\n", + __func__); +} + +static int aw869xx_ram_update(struct aw869xx *aw869xx) +{ + aw869xx->ram_init = 0; + aw869xx->rtp_init = 0; + + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw869xx_ram_name, aw869xx->dev, + GFP_KERNEL, aw869xx, aw869xx_ram_loaded); +} + +static void aw869xx_ram_work_routine(struct work_struct *work) +{ + struct aw869xx *aw869xx = container_of(work, struct aw869xx, + ram_work.work); + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + aw869xx_ram_update(aw869xx); +} + +static int aw869xx_ram_work_init(struct aw869xx *aw869xx) +{ + int ram_timer_val = 8000; + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + INIT_DELAYED_WORK(&aw869xx->ram_work, aw869xx_ram_work_routine); + schedule_delayed_work(&aw869xx->ram_work, + msecs_to_jiffies(ram_timer_val)); + return 0; +} + +/***************************************************** + * + * haptic control + * + *****************************************************/ +/* static int aw869xx_haptic_softreset(struct aw869xx *aw869xx) +*{ +* aw_dev_info(aw869xx->dev, "%s enter\n", __func__); +* aw869xx_i2c_write(aw869xx, AW869XX_REG_ID, 0xAA); +* usleep_range(3000, 3500); +* return 0; +*} +*/ + +static int aw869xx_haptic_play_mode(struct aw869xx *aw869xx, + unsigned char play_mode) +{ + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + + switch (play_mode) { + case AW869XX_HAPTIC_STANDBY_MODE: + aw_dev_info(aw869xx->dev, "%s: enter standby mode\n", __func__); + aw869xx->play_mode = AW869XX_HAPTIC_STANDBY_MODE; + aw869xx_haptic_stop(aw869xx); + break; + case AW869XX_HAPTIC_RAM_MODE: + aw_dev_info(aw869xx->dev, "%s: enter ram mode\n", __func__); + aw869xx->play_mode = AW869XX_HAPTIC_RAM_MODE; + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + case AW869XX_HAPTIC_RAM_LOOP_MODE: + aw_dev_info(aw869xx->dev, "%s: enter ram loop mode\n", + __func__); + aw869xx->play_mode = AW869XX_HAPTIC_RAM_LOOP_MODE; + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + case AW869XX_HAPTIC_RTP_MODE: + aw_dev_info(aw869xx->dev, "%s: enter rtp mode\n", __func__); + aw869xx->play_mode = AW869XX_HAPTIC_RTP_MODE; + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_RTP); + break; + case AW869XX_HAPTIC_TRIG_MODE: + aw_dev_info(aw869xx->dev, "%s: enter trig mode\n", __func__); + aw869xx->play_mode = AW869XX_HAPTIC_TRIG_MODE; + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_RAM); + break; + case AW869XX_HAPTIC_CONT_MODE: + aw_dev_info(aw869xx->dev, "%s: enter cont mode\n", __func__); + aw869xx->play_mode = AW869XX_HAPTIC_CONT_MODE; + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_CONT); + break; + default: + aw_dev_err(aw869xx->dev, "%s: play mode %d error", + __func__, play_mode); + break; + } + return 0; +} + +static int aw869xx_haptic_set_wav_seq(struct aw869xx *aw869xx, + unsigned char wav, unsigned char seq) +{ + aw869xx_i2c_write(aw869xx, AW869XX_REG_WAVCFG1 + wav, seq); + return 0; +} + +static int aw869xx_haptic_set_wav_loop(struct aw869xx *aw869xx, + unsigned char wav, unsigned char loop) +{ + unsigned char tmp = 0; + + if (wav % 2) { + tmp = loop << 0; + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_WAVCFG9 + (wav / 2), + AW869XX_BIT_WAVLOOP_SEQ_EVEN_MASK, tmp); + } else { + tmp = loop << 4; + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_WAVCFG9 + (wav / 2), + AW869XX_BIT_WAVLOOP_SEQ_ODD_MASK, tmp); + } + return 0; +} + +/* +static int aw869xx_haptic_set_main_loop(struct aw869xx *aw869xx, + unsigned char loop) +{ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_WAVCFG13, + AW869XX_BIT_WAVCFG13_MAINLOOP_MASK, loop); + return 0; +} +*/ + +static int aw869xx_haptic_set_repeat_wav_seq(struct aw869xx *aw869xx, + unsigned char seq) +{ + aw869xx_haptic_set_wav_seq(aw869xx, 0x00, seq); + aw869xx_haptic_set_wav_loop(aw869xx, 0x00, + AW869XX_BIT_WAVLOOP_INIFINITELY); + return 0; +} + +static int aw869xx_haptic_set_bst_vol(struct aw869xx *aw869xx, + unsigned char bst_vol) +{ + if (bst_vol & 0xc0) + bst_vol = 0x3f; + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PLAYCFG1, + AW869XX_BIT_PLAYCFG1_BST_VOUT_RDA_MASK, bst_vol); + return 0; +} + +static int aw869xx_haptic_set_bst_peak_cur(struct aw869xx *aw869xx) +{ + switch (aw869xx->bst_pc) { + case AW869XX_HAPTIC_BST_PC_L1: + aw_dev_info(aw869xx->dev, "%s bst pc = L1\n", __func__); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_BSTCFG1, + AW869XX_BIT_BSTCFG1_BST_PC_MASK, + (0 << 1)); + return 0; + case AW869XX_HAPTIC_BST_PC_L2: + aw_dev_info(aw869xx->dev, "%s bst pc = L2\n", __func__); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_BSTCFG1, + AW869XX_BIT_BSTCFG1_BST_PC_MASK, + (5 << 1)); + return 0; + default: + aw_dev_info(aw869xx->dev, "%s bst pc = L1\n", __func__); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_BSTCFG1, + AW869XX_BIT_BSTCFG1_BST_PC_MASK, + (0 << 1)); + break; + } + return 0; +} + +static int aw869xx_haptic_set_gain(struct aw869xx *aw869xx, unsigned char gain) +{ + aw869xx_i2c_write(aw869xx, AW869XX_REG_PLAYCFG2, gain); + return 0; +} + +static int aw869xx_haptic_set_pwm(struct aw869xx *aw869xx, unsigned char mode) +{ + switch (mode) { + case AW869XX_PWM_48K: + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW869XX_BIT_SYSCTRL2_RATE_48K); + break; + case AW869XX_PWM_24K: + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW869XX_BIT_SYSCTRL2_RATE_24K); + break; + case AW869XX_PWM_12K: + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW869XX_BIT_SYSCTRL2_RATE_12K); + break; + default: + break; + } + return 0; +} + +static int aw869xx_haptic_play_wav_seq(struct aw869xx *aw869xx, + unsigned char flag) +{ +#ifdef AW_RAM_STATE_OUTPUT + int i; + unsigned char reg_val = 0; +#endif + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + if (flag) { + aw869xx_haptic_play_mode(aw869xx, AW869XX_HAPTIC_RAM_MODE); + aw869xx_haptic_play_go(aw869xx); +#ifdef AW_RAM_STATE_OUTPUT + for (i = 0; i < 100; i++) { + aw869xx_i2c_read(aw869xx, AW869XX_REG_GLBRD5, ®_val); + if ((reg_val & 0x0f) == 0x07) { + aw_dev_info(aw869xx->dev, + "%s RAM_GO! glb_state=0x07\n", + __func__); + } else { + aw_dev_dbg(aw869xx->dev, + "%s ram stopped, glb_state=0x%02X\n", + __func__, reg_val); + } + usleep_range(2000, 2500); + + } +#endif + } + return 0; +} + +static int aw869xx_haptic_play_repeat_seq(struct aw869xx *aw869xx, + unsigned char flag) +{ + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + + if (flag) { + aw869xx_haptic_play_mode(aw869xx, AW869XX_HAPTIC_RAM_LOOP_MODE); + aw869xx_haptic_play_go(aw869xx); + } + return 0; +} + +/***************************************************** + * + * motor protect + * + *****************************************************/ +static int aw869xx_haptic_swicth_motor_protect_config(struct aw869xx *aw869xx, + unsigned char addr, + unsigned char val) +{ + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + if (addr == 1) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_DETCFG1, + AW869XX_BIT_DETCFG1_PRCT_MODE_MASK, + AW869XX_BIT_DETCFG1_PRCT_MODE_VALID); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PWMCFG1, + AW869XX_BIT_PWMCFG1_PRC_EN_MASK, + AW869XX_BIT_PWMCFG1_PRC_ENABLE); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PWMCFG3, + AW869XX_BIT_PWMCFG3_PR_EN_MASK, + AW869XX_BIT_PWMCFG3_PR_ENABLE); + } else if (addr == 0) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_DETCFG1, + AW869XX_BIT_DETCFG1_PRCT_MODE_MASK, + AW869XX_BIT_DETCFG1_PRCT_MODE_INVALID); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PWMCFG1, + AW869XX_BIT_PWMCFG1_PRC_EN_MASK, + AW869XX_BIT_PWMCFG1_PRC_DISABLE); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PWMCFG3, + AW869XX_BIT_PWMCFG3_PR_EN_MASK, + AW869XX_BIT_PWMCFG3_PR_DISABLE); + } else if (addr == 0x2d) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PWMCFG1, + AW869XX_BIT_PWMCFG1_PRCTIME_MASK, val); + } else if (addr == 0x3e) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PWMCFG3, + AW869XX_BIT_PWMCFG3_PRLVL_MASK, val); + } else if (addr == 0x3f) { + aw869xx_i2c_write(aw869xx, AW869XX_REG_PWMCFG4, val); + } + return 0; +} + +/***************************************************** + * + * offset calibration + * + *****************************************************/ +static int aw869xx_haptic_offset_calibration(struct aw869xx *aw869xx) +{ + unsigned int cont = 2000; + unsigned char reg_val = 0; + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + + aw869xx_haptic_raminit(aw869xx, true); + + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_DETCFG2, + AW869XX_BIT_DETCFG2_DIAG_GO_MASK, + AW869XX_BIT_DETCFG2_DIAG_GO_ON); + while (1) { + aw869xx_i2c_read(aw869xx, AW869XX_REG_DETCFG2, ®_val); + if ((reg_val & 0x01) == 0 || cont == 0) + break; + cont--; + } + if (cont == 0) + aw_dev_err(aw869xx->dev, "%s calibration offset failed!\n", + __func__); + aw869xx_haptic_raminit(aw869xx, false); + return 0; +} + +/***************************************************** + * + * trig config + * + *****************************************************/ +static int aw869xx_haptic_trig_param_init(struct aw869xx *aw869xx) +{ + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + + aw869xx->trig[0].trig_level = aw869xx->dts_info.trig_config[0]; + aw869xx->trig[0].trig_polar = aw869xx->dts_info.trig_config[1]; + aw869xx->trig[0].pos_enable = aw869xx->dts_info.trig_config[2]; + aw869xx->trig[0].pos_sequence = aw869xx->dts_info.trig_config[3]; + aw869xx->trig[0].neg_enable = aw869xx->dts_info.trig_config[4]; + aw869xx->trig[0].neg_sequence = aw869xx->dts_info.trig_config[5]; + aw869xx->trig[0].trig_brk = aw869xx->dts_info.trig_config[6]; + aw869xx->trig[0].trig_bst = aw869xx->dts_info.trig_config[7]; + if (!aw869xx->dts_info.is_enabled_i2s) { + aw_dev_info(aw869xx->dev, "%s i2s is disabled!\n", __func__); + aw869xx->trig[1].trig_level = + aw869xx->dts_info.trig_config[8 + 0]; + aw869xx->trig[1].trig_polar = + aw869xx->dts_info.trig_config[8 + 1]; + aw869xx->trig[1].pos_enable = + aw869xx->dts_info.trig_config[8 + 2]; + aw869xx->trig[1].pos_sequence = + aw869xx->dts_info.trig_config[8 + 3]; + aw869xx->trig[1].neg_enable = + aw869xx->dts_info.trig_config[8 + 4]; + aw869xx->trig[1].neg_sequence = + aw869xx->dts_info.trig_config[8 + 5]; + aw869xx->trig[1].trig_brk = + aw869xx->dts_info.trig_config[8 + 6]; + aw869xx->trig[1].trig_bst = + aw869xx->dts_info.trig_config[8 + 7]; + + aw869xx->trig[2].trig_level = + aw869xx->dts_info.trig_config[16 + 0]; + aw869xx->trig[2].trig_polar = + aw869xx->dts_info.trig_config[16 + 1]; + aw869xx->trig[2].pos_enable = + aw869xx->dts_info.trig_config[16 + 2]; + aw869xx->trig[2].pos_sequence = + aw869xx->dts_info.trig_config[16 + 3]; + aw869xx->trig[2].neg_enable = + aw869xx->dts_info.trig_config[16 + 4]; + aw869xx->trig[2].neg_sequence = + aw869xx->dts_info.trig_config[16 + 5]; + aw869xx->trig[2].trig_brk = + aw869xx->dts_info.trig_config[16 + 6]; + aw869xx->trig[2].trig_bst = + aw869xx->dts_info.trig_config[16 + 7]; + } + return 0; +} + +static int aw869xx_haptic_trig_param_config(struct aw869xx *aw869xx) +{ + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + + if (aw869xx->trig[0].trig_level) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG7, + AW869XX_BIT_TRGCFG7_TRG1_MODE_MASK, + AW869XX_BIT_TRGCFG7_TRG1_MODE_LEVEL); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG7, + AW869XX_BIT_TRGCFG7_TRG1_MODE_MASK, + AW869XX_BIT_TRGCFG7_TRG1_MODE_EDGE); + } + if (aw869xx->trig[1].trig_level) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG7, + AW869XX_BIT_TRGCFG7_TRG2_MODE_MASK, + AW869XX_BIT_TRGCFG7_TRG2_MODE_LEVEL); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG7, + AW869XX_BIT_TRGCFG7_TRG2_MODE_MASK, + AW869XX_BIT_TRGCFG7_TRG2_MODE_EDGE); + } + if (aw869xx->trig[2].trig_level) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG8, + AW869XX_BIT_TRGCFG8_TRG3_MODE_MASK, + AW869XX_BIT_TRGCFG8_TRG3_MODE_LEVEL); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG8, + AW869XX_BIT_TRGCFG8_TRG3_MODE_MASK, + AW869XX_BIT_TRGCFG8_TRG3_MODE_EDGE); + } + + if (aw869xx->trig[0].trig_polar) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG7, + AW869XX_BIT_TRGCFG7_TRG1_POLAR_MASK, + AW869XX_BIT_TRGCFG7_TRG1_POLAR_NEG); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG7, + AW869XX_BIT_TRGCFG7_TRG1_POLAR_MASK, + AW869XX_BIT_TRGCFG7_TRG1_POLAR_POS); + } + if (aw869xx->trig[1].trig_polar) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG7, + AW869XX_BIT_TRGCFG7_TRG2_POLAR_MASK, + AW869XX_BIT_TRGCFG7_TRG2_POLAR_NEG); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG7, + AW869XX_BIT_TRGCFG7_TRG2_POLAR_MASK, + AW869XX_BIT_TRGCFG7_TRG2_POLAR_POS); + } + if (aw869xx->trig[2].trig_polar) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG7, + AW869XX_BIT_TRGCFG8_TRG3_POLAR_MASK, + AW869XX_BIT_TRGCFG8_TRG3_POLAR_NEG); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG7, + AW869XX_BIT_TRGCFG8_TRG3_POLAR_MASK, + AW869XX_BIT_TRGCFG8_TRG3_POLAR_POS); + } + + if (aw869xx->trig[0].pos_enable) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG1, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_ENABLE); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG1, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_DISABLE); + } + if (aw869xx->trig[1].pos_enable) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG2, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_ENABLE); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG2, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_DISABLE); + } + if (aw869xx->trig[2].pos_enable) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG3, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_ENABLE); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG3, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_DISABLE); + } + + if (aw869xx->trig[0].neg_enable) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG4, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_ENABLE); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG4, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_DISABLE); + } + if (aw869xx->trig[1].neg_enable) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG5, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_ENABLE); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG5, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_DISABLE); + } + if (aw869xx->trig[2].neg_enable) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG6, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_ENABLE); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG6, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_DISABLE); + } + if (aw869xx->trig[0].pos_sequence) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG1, + AW869XX_BIT_TRG_SEQ_MASK, + aw869xx->trig[0].pos_sequence); + } + if (aw869xx->trig[0].neg_sequence) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG4, + AW869XX_BIT_TRG_SEQ_MASK, + aw869xx->trig[0].neg_sequence); + } + if (aw869xx->trig[1].pos_sequence) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG2, + AW869XX_BIT_TRG_SEQ_MASK, + aw869xx->trig[1].pos_sequence); + } + if (aw869xx->trig[1].neg_sequence) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG5, + AW869XX_BIT_TRG_SEQ_MASK, + aw869xx->trig[1].neg_sequence); + } + if (aw869xx->trig[2].pos_sequence) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG3, + AW869XX_BIT_TRG_SEQ_MASK, + aw869xx->trig[2].pos_sequence); + } + if (aw869xx->trig[2].neg_sequence) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG6, + AW869XX_BIT_TRG_SEQ_MASK, + aw869xx->trig[2].neg_sequence); + } + if (aw869xx->trig[0].trig_brk) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG7, + AW869XX_BIT_TRGCFG7_TRG1_AUTO_BRK_MASK, + AW869XX_BIT_TRGCFG7_TRG1_AUTO_BRK_ENABLE); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG7, + AW869XX_BIT_TRGCFG7_TRG1_AUTO_BRK_MASK, + AW869XX_BIT_TRGCFG7_TRG1_AUTO_BRK_DISABLE); + } + if (aw869xx->trig[1].trig_brk) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG7, + AW869XX_BIT_TRGCFG7_TRG2_AUTO_BRK_MASK, + AW869XX_BIT_TRGCFG7_TRG2_AUTO_BRK_ENABLE); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG7, + AW869XX_BIT_TRGCFG7_TRG2_AUTO_BRK_MASK, + AW869XX_BIT_TRGCFG7_TRG2_AUTO_BRK_DISABLE); + } + if (aw869xx->trig[2].trig_brk) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG8, + AW869XX_BIT_TRGCFG8_TRG3_AUTO_BRK_MASK, + AW869XX_BIT_TRGCFG8_TRG3_AUTO_BRK_ENABLE); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG8, + AW869XX_BIT_TRGCFG8_TRG3_AUTO_BRK_MASK, + AW869XX_BIT_TRGCFG8_TRG3_AUTO_BRK_DISABLE); + } + if (aw869xx->trig[0].trig_bst) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG7, + AW869XX_BIT_TRGCFG7_TRG1_BST_MASK, + AW869XX_BIT_TRGCFG7_TRG1_BST_ENABLE); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG7, + AW869XX_BIT_TRGCFG7_TRG1_BST_MASK, + AW869XX_BIT_TRGCFG7_TRG1_BST_DISABLE); + } + if (aw869xx->trig[1].trig_bst) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG7, + AW869XX_BIT_TRGCFG7_TRG2_BST_MASK, + AW869XX_BIT_TRGCFG7_TRG2_BST_ENABLE); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG7, + AW869XX_BIT_TRGCFG7_TRG2_BST_MASK, + AW869XX_BIT_TRGCFG7_TRG2_BST_DISABLE); + } + if (aw869xx->trig[2].trig_bst) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG8, + AW869XX_BIT_TRGCFG8_TRG3_BST_MASK, + AW869XX_BIT_TRGCFG8_TRG3_BST_ENABLE); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRGCFG8, + AW869XX_BIT_TRGCFG8_TRG3_BST_MASK, + AW869XX_BIT_TRGCFG8_TRG3_BST_DISABLE); + } + return 0; +} + +static void aw869xx_haptic_bst_mode_config(struct aw869xx *aw869xx, + unsigned char boost_mode) +{ + aw869xx->boost_mode = boost_mode; + + switch (boost_mode) { + case AW869XX_HAPTIC_BST_MODE_BOOST: + aw_dev_info(aw869xx->dev, "%s haptic boost mode = boost\n", + __func__); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PLAYCFG1, + AW869XX_BIT_PLAYCFG1_BST_MODE_MASK, + AW869XX_BIT_PLAYCFG1_BST_MODE_BOOST); + break; + case AW869XX_HAPTIC_BST_MODE_BYPASS: + aw_dev_info(aw869xx->dev, "%s haptic boost mode = bypass\n", + __func__); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PLAYCFG1, + AW869XX_BIT_PLAYCFG1_BST_MODE_MASK, + AW869XX_BIT_PLAYCFG1_BST_MODE_BYPASS); + break; + default: + aw_dev_err(aw869xx->dev, "%s: boost_mode = %d error", + __func__, boost_mode); + break; + } +} + +static int aw869xx_haptic_auto_bst_enable(struct aw869xx *aw869xx, + unsigned char flag) +{ + aw869xx->auto_boost = flag; + if (flag) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_AUTO_BST_MASK, + AW869XX_BIT_PLAYCFG3_AUTO_BST_ENABLE); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_AUTO_BST_MASK, + AW869XX_BIT_PLAYCFG3_AUTO_BST_DISABLE); + } + return 0; +} + +/***************************************************** + * + * vbat mode + * + *****************************************************/ +static int aw869xx_haptic_vbat_mode_config(struct aw869xx *aw869xx, + unsigned char flag) +{ + if (flag == AW869XX_HAPTIC_CONT_VBAT_HW_ADJUST_MODE) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL1, + AW869XX_BIT_SYSCTRL1_VBAT_MODE_MASK, + AW869XX_BIT_SYSCTRL1_VBAT_MODE_HW); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL1, + AW869XX_BIT_SYSCTRL1_VBAT_MODE_MASK, + AW869XX_BIT_SYSCTRL1_VBAT_MODE_SW); + } + return 0; +} + +static int aw869xx_haptic_get_vbat(struct aw869xx *aw869xx) +{ + unsigned char reg_val = 0; + unsigned int vbat_code = 0; + + aw869xx_haptic_stop(aw869xx); + aw869xx_haptic_raminit(aw869xx, true); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_DETCFG2, + AW869XX_BIT_DETCFG2_VBAT_GO_MASK, + AW869XX_BIT_DETCFG2_VABT_GO_ON); + usleep_range(20000, 25000); + aw869xx_i2c_read(aw869xx, AW869XX_REG_DET_VBAT, ®_val); + vbat_code = (vbat_code | reg_val) << 2; + aw869xx_i2c_read(aw869xx, AW869XX_REG_DET_LO, ®_val); + vbat_code = vbat_code | ((reg_val & 0x30) >> 4); + aw869xx->vbat = 6100 * vbat_code / 1024; + if (aw869xx->vbat > AW869XX_VBAT_MAX) { + aw869xx->vbat = AW869XX_VBAT_MAX; + aw_dev_info(aw869xx->dev, "%s vbat max limit = %dmV\n", + __func__, aw869xx->vbat); + } + if (aw869xx->vbat < AW869XX_VBAT_MIN) { + aw869xx->vbat = AW869XX_VBAT_MIN; + aw_dev_info(aw869xx->dev, "%s vbat min limit = %dmV\n", + __func__, aw869xx->vbat); + } + aw_dev_info(aw869xx->dev, "%s aw869xx->vbat=%dmV, vbat_code=0x%02X\n", + __func__, aw869xx->vbat, vbat_code); + aw869xx_haptic_raminit(aw869xx, false); + return 0; +} + +static int aw869xx_haptic_ram_vbat_compensate(struct aw869xx *aw869xx, + bool flag) +{ + int temp_gain = 0; + + if (flag) { + if (aw869xx->ram_vbat_compensate == + AW869XX_HAPTIC_RAM_VBAT_COMP_ENABLE) { + aw869xx_haptic_get_vbat(aw869xx); + temp_gain = + aw869xx->gain * AW869XX_VBAT_REFER / aw869xx->vbat; + if (temp_gain > + (128 * AW869XX_VBAT_REFER / AW869XX_VBAT_MIN)) { + temp_gain = + 128 * AW869XX_VBAT_REFER / AW869XX_VBAT_MIN; + aw_dev_dbg(aw869xx->dev, "%s gain limit=%d\n", + __func__, temp_gain); + } + aw869xx_haptic_set_gain(aw869xx, temp_gain); + } else { + aw869xx_haptic_set_gain(aw869xx, aw869xx->gain); + } + } else { + aw869xx_haptic_set_gain(aw869xx, aw869xx->gain); + } + return 0; +} + +static int aw869xx_haptic_get_lra_resistance(struct aw869xx *aw869xx) +{ + unsigned char reg_val = 0; + unsigned char d2s_gain_temp = 0; + unsigned int lra_code = 0; + + mutex_lock(&aw869xx->lock); + aw869xx_haptic_stop(aw869xx); + aw869xx_i2c_read(aw869xx, AW869XX_REG_SYSCTRL7, ®_val); + d2s_gain_temp = 0x07 & reg_val; + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL7, + AW869XX_BIT_SYSCTRL7_D2S_GAIN_MASK, + aw869xx->dts_info.d2s_gain); + aw869xx_haptic_raminit(aw869xx, true); + /* enter standby mode */ + aw869xx_haptic_stop(aw869xx); + usleep_range(2000, 2500); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_STANDBY_MASK, + AW869XX_BIT_SYSCTRL2_STANDBY_OFF); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_DETCFG1, + AW869XX_BIT_DETCFG1_RL_OS_MASK, + AW869XX_BIT_DETCFG1_RL); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_DETCFG2, + AW869XX_BIT_DETCFG2_DIAG_GO_MASK, + AW869XX_BIT_DETCFG2_DIAG_GO_ON); + usleep_range(30000, 35000); + aw869xx_i2c_read(aw869xx, AW869XX_REG_DET_RL, ®_val); + lra_code = (lra_code | reg_val) << 2; + aw869xx_i2c_read(aw869xx, AW869XX_REG_DET_LO, ®_val); + lra_code = lra_code | (reg_val & 0x03); + /* 2num */ + aw869xx->lra = (lra_code * 678 * 100) / (1024 * 10); + aw869xx_haptic_raminit(aw869xx, false); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL7, + AW869XX_BIT_SYSCTRL7_D2S_GAIN_MASK, + d2s_gain_temp); + mutex_unlock(&aw869xx->lock); + return 0; +} + +static void aw869xx_haptic_misc_para_init(struct aw869xx *aw869xx) +{ + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + aw869xx->cont_drv1_lvl = aw869xx->dts_info.cont_drv1_lvl_dt; + aw869xx->cont_drv2_lvl = aw869xx->dts_info.cont_drv2_lvl_dt; + aw869xx->cont_drv1_time = aw869xx->dts_info.cont_drv1_time_dt; + aw869xx->cont_drv2_time = aw869xx->dts_info.cont_drv2_time_dt; + aw869xx->cont_brk_time = aw869xx->dts_info.cont_brk_time_dt; + aw869xx->cont_wait_num = aw869xx->dts_info.cont_wait_num_dt; + aw869xx_i2c_write(aw869xx, AW869XX_REG_BSTCFG1, + aw869xx->dts_info.bstcfg[0]); + aw869xx_i2c_write(aw869xx, AW869XX_REG_BSTCFG2, + aw869xx->dts_info.bstcfg[1]); + aw869xx_i2c_write(aw869xx, AW869XX_REG_BSTCFG3, + aw869xx->dts_info.bstcfg[2]); + aw869xx_i2c_write(aw869xx, AW869XX_REG_BSTCFG4, + aw869xx->dts_info.bstcfg[3]); + aw869xx_i2c_write(aw869xx, AW869XX_REG_BSTCFG5, + aw869xx->dts_info.bstcfg[4]); + aw869xx_i2c_write(aw869xx, AW869XX_REG_SYSCTRL3, + aw869xx->dts_info.sine_array[0]); + aw869xx_i2c_write(aw869xx, AW869XX_REG_SYSCTRL4, + aw869xx->dts_info.sine_array[1]); + aw869xx_i2c_write(aw869xx, AW869XX_REG_SYSCTRL5, + aw869xx->dts_info.sine_array[2]); + aw869xx_i2c_write(aw869xx, AW869XX_REG_SYSCTRL6, + aw869xx->dts_info.sine_array[3]); + /* d2s_gain */ + if (!aw869xx->dts_info.d2s_gain) { + aw_dev_err(aw869xx->dev, "%s aw869xx->dts_info.d2s_gain = 0!\n", + __func__); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL7, + AW869XX_BIT_SYSCTRL7_D2S_GAIN_MASK, + aw869xx->dts_info.d2s_gain); + } + + /* cont_tset */ + if (!aw869xx->dts_info.cont_tset) { + aw_dev_err(aw869xx->dev, + "%s aw869xx->dts_info.cont_tset = 0!\n", __func__); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_CONTCFG13, + AW869XX_BIT_CONTCFG13_TSET_MASK, + aw869xx->dts_info.cont_tset << 4); + } + + /* cont_bemf_set */ + if (!aw869xx->dts_info.cont_bemf_set) { + aw_dev_err(aw869xx->dev, + "%s aw869xx->dts_info.cont_bemf_set = 0!\n", + __func__); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_CONTCFG13, + AW869XX_BIT_CONTCFG13_BEME_SET_MASK, + aw869xx->dts_info.cont_bemf_set); + } + + /* cont_brk_time */ + if (!aw869xx->cont_brk_time) { + aw_dev_err(aw869xx->dev, "%s aw869xx->cont_brk_time = 0!\n", + __func__); + } else { + aw869xx_i2c_write(aw869xx, AW869XX_REG_CONTCFG10, + aw869xx->cont_brk_time); + } + + /* cont_bst_brk_gain */ + if (!aw869xx->dts_info.cont_bst_brk_gain) { + aw_dev_err(aw869xx->dev, + "%s aw869xx->dts_info.cont_bst_brk_gain = 0!\n", + __func__); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_CONTCFG5, + AW869XX_BIT_CONTCFG5_BST_BRK_GAIN_MASK, + aw869xx->dts_info.cont_bst_brk_gain); + } + + /* cont_brk_gain */ + if (!aw869xx->dts_info.cont_brk_gain) { + aw_dev_err(aw869xx->dev, + "%s aw869xx->dts_info.cont_brk_gain = 0!\n", + __func__); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_CONTCFG5, + AW869XX_BIT_CONTCFG5_BRK_GAIN_MASK, + aw869xx->dts_info.cont_brk_gain); + } + + /* i2s enbale */ + if (aw869xx->dts_info.is_enabled_i2s) { + aw_dev_info(aw869xx->dev, "%s i2s enabled!\n", __func__); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_I2S_PIN_MASK, + AW869XX_BIT_SYSCTRL2_I2S_PIN_I2S); + } else { + aw_dev_info(aw869xx->dev, "%s i2s disabled!\n", __func__); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_I2S_PIN_MASK, + AW869XX_BIT_SYSCTRL2_I2S_PIN_TRIG); + } +} + +/***************************************************** + * + * rtp + * + *****************************************************/ +static void aw869xx_haptic_set_rtp_aei(struct aw869xx *aw869xx, bool flag) +{ + if (flag) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSINTM, + AW869XX_BIT_SYSINTM_FF_AEM_MASK, + AW869XX_BIT_SYSINTM_FF_AEM_ON); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSINTM, + AW869XX_BIT_SYSINTM_FF_AEM_MASK, + AW869XX_BIT_SYSINTM_FF_AEM_OFF); + } +} + +/* +static void aw869xx_haptic_set_rtp_afi(struct aw869xx *aw869xx, bool flag) +{ + if(flag) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSINTM, + AW869XX_BIT_SYSINTM_FF_AFM_MASK, + AW869XX_BIT_SYSINTM_FF_AFM_ON); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSINTM, + AW869XX_BIT_SYSINTM_FF_AFM_MASK, + AW869XX_BIT_SYSINTM_FF_AFM_OFF); + } +} +*/ + +/* +static unsigned char aw869xx_haptic_rtp_get_fifo_aei(struct aw869xx *aw869xx) +{ + unsigned char ret; + unsigned char reg_val; + + aw869xx_i2c_read(aw869xx, AW869XX_REG_SYSINT, ®_val); + reg_val &= AW869XX_BIT_SYSINT_FF_AEI; + ret = reg_val>>4; + + return ret; +} +*/ + +/* +static unsigned char aw869xx_haptic_rtp_get_fifo_aes(struct aw869xx *aw869xx) +{ + unsigned char ret; + unsigned char reg_val; + + aw869xx_i2c_read(aw869xx, AW869XX_REG_SYSST, ®_val); + reg_val &= AW869XX_BIT_SYSST_FF_AES; + ret = reg_val>>4; + + return ret; +} + +static unsigned char aw869xx_haptic_rtp_get_fifo_afi(struct aw869xx *aw869xx) +{ + unsigned char ret = 0; + unsigned char reg_val = 0; + + aw869xx_i2c_read(aw869xx, AW869XX_REG_SYSINT, ®_val); + reg_val &= AW869XX_BIT_SYSINT_FF_AFI; + ret = reg_val >> 3; + return ret; +} +*/ + +static unsigned char aw869xx_haptic_rtp_get_fifo_afs(struct aw869xx *aw869xx) +{ + unsigned char ret = 0; + unsigned char reg_val = 0; + + aw869xx_i2c_read(aw869xx, AW869XX_REG_SYSST, ®_val); + reg_val &= AW869XX_BIT_SYSST_FF_AFS; + ret = reg_val >> 3; + return ret; +} + +static int aw869xx_haptic_rtp_init(struct aw869xx *aw869xx) +{ + unsigned int buf_len = 0; + unsigned char glb_state_val = 0; + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + pm_qos_add_request(&pm_qos_req_vb, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_VALUE_VB); + aw869xx->rtp_cnt = 0; + mutex_lock(&aw869xx->rtp_lock); + while ((!aw869xx_haptic_rtp_get_fifo_afs(aw869xx)) + && (aw869xx->play_mode == AW869XX_HAPTIC_RTP_MODE)) { + aw_dev_info(aw869xx->dev, "%s rtp cnt = %d\n", __func__, + aw869xx->rtp_cnt); + if (!aw869xx_rtp) { + aw_dev_info(aw869xx->dev, + "%s:aw869xx_rtp is null, break!\n", + __func__); + break; + } + if (aw869xx->rtp_cnt < (aw869xx->ram.base_addr)) { + if ((aw869xx_rtp->len - aw869xx->rtp_cnt) < + (aw869xx->ram.base_addr)) { + buf_len = aw869xx_rtp->len - aw869xx->rtp_cnt; + } else { + buf_len = aw869xx->ram.base_addr; + } + } else if ((aw869xx_rtp->len - aw869xx->rtp_cnt) < + (aw869xx->ram.base_addr >> 2)) { + buf_len = aw869xx_rtp->len - aw869xx->rtp_cnt; + } else { + buf_len = aw869xx->ram.base_addr >> 2; + } + aw_dev_info(aw869xx->dev, "%s buf_len = %d\n", __func__, + buf_len); + aw869xx_i2c_writes(aw869xx, AW869XX_REG_RTPDATA, + &aw869xx_rtp->data[aw869xx->rtp_cnt], + buf_len); + aw869xx->rtp_cnt += buf_len; + aw869xx_i2c_read(aw869xx, AW869XX_REG_GLBRD5, &glb_state_val); + if ((aw869xx->rtp_cnt == aw869xx_rtp->len) + || ((glb_state_val & 0x0f) == 0x00)) { + aw_dev_info(aw869xx->dev, "%s: rtp update complete!\n", + __func__); + aw869xx->rtp_cnt = 0; + pm_qos_remove_request(&pm_qos_req_vb); + mutex_unlock(&aw869xx->rtp_lock); + return 0; + } + } + mutex_unlock(&aw869xx->rtp_lock); + + if (aw869xx->play_mode == AW869XX_HAPTIC_RTP_MODE) + aw869xx_haptic_set_rtp_aei(aw869xx, true); + + aw_dev_info(aw869xx->dev, "%s exit\n", __func__); + pm_qos_remove_request(&pm_qos_req_vb); + return 0; +} + +static void aw869xx_haptic_upload_lra(struct aw869xx *aw869xx, + unsigned int flag) +{ + switch (flag) { + case WRITE_ZERO: + aw_dev_info(aw869xx->dev, "%s write zero to trim_lra!\n", + __func__); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRIMCFG3, + AW869XX_BIT_TRIMCFG3_TRIM_LRA_MASK, + 0x00); + break; + case F0_CALI: + aw_dev_info(aw869xx->dev, + "%s write f0_cali_data to trim_lra = 0x%02X\n", + __func__, aw869xx->f0_cali_data); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRIMCFG3, + AW869XX_BIT_TRIMCFG3_TRIM_LRA_MASK, + (char)aw869xx->f0_cali_data); + break; + case OSC_CALI: + aw_dev_info(aw869xx->dev, + "%s write osc_cali_data to trim_lra = 0x%02X\n", + __func__, aw869xx->osc_cali_data); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRIMCFG3, + AW869XX_BIT_TRIMCFG3_TRIM_LRA_MASK, + (char)aw869xx->osc_cali_data); + break; + default: + break; + } +} + +static int aw869xx_osc_trim_calculation(unsigned long int theory_time, + unsigned long int real_time) +{ + unsigned int real_code = 0; + unsigned int lra_code = 0; + unsigned int DFT_LRA_TRIM_CODE = 0; + /*0.1 percent below no need to calibrate */ + unsigned int osc_cali_threshold = 10; + + pr_info("%s enter\n", __func__); + if (theory_time == real_time) { + pr_info("%s theory_time == real_time: %ld," + " no need to calibrate!\n", __func__, real_time); + return 0; + } else if (theory_time < real_time) { + if ((real_time - theory_time) > (theory_time / 50)) { + pr_info("%s (real_time - theory_time) >" + " (theory_time/50), can't calibrate!\n", + __func__); + return DFT_LRA_TRIM_CODE; + } + + if ((real_time - theory_time) < + (osc_cali_threshold * theory_time / 10000)) { + pr_info("%s real_time: %ld, theory_time: %ld," + " no need to calibrate!\n", __func__, + real_time, theory_time); + return DFT_LRA_TRIM_CODE; + } + + real_code = ((real_time - theory_time) * 4000) / theory_time; + real_code = ((real_code % 10 < 5) ? 0 : 1) + real_code / 10; + real_code = 32 + real_code; + } else if (theory_time > real_time) { + if ((theory_time - real_time) > (theory_time / 50)) { + pr_info("%s (theory_time - real_time) >" + " (theory_time / 50), can't calibrate!\n", + __func__); + return DFT_LRA_TRIM_CODE; + } + if ((theory_time - real_time) < + (osc_cali_threshold * theory_time / 10000)) { + pr_info("%s real_time: %ld, theory_time: %ld," + " no need to calibrate!\n", __func__, + real_time, theory_time); + return DFT_LRA_TRIM_CODE; + } + real_code = ((theory_time - real_time) * 4000) / theory_time; + real_code = ((real_code % 10 < 5) ? 0 : 1) + real_code / 10; + real_code = 32 - real_code; + } + if (real_code > 31) + lra_code = real_code - 32; + else + lra_code = real_code + 32; + pr_info("%s real_time: %ld, theory_time: %ld\n",__func__, real_time, + theory_time); + pr_info("%s real_code: %02X, trim_lra: 0x%02X\n", __func__, real_code, + lra_code); + return lra_code; +} + +static int aw869xx_rtp_trim_lra_calibration(struct aw869xx *aw869xx) +{ + unsigned char reg_val = 0; + unsigned int fre_val = 0; + unsigned int theory_time = 0; + unsigned int lra_trim_code = 0; + + aw869xx_i2c_read(aw869xx, AW869XX_REG_SYSCTRL2, ®_val); + fre_val = (reg_val & 0x03) >> 0; + + if (fre_val == 2 || fre_val == 3) + theory_time = (aw869xx->rtp_len / 12000) * 1000000; /*12K */ + if (fre_val == 0) + theory_time = (aw869xx->rtp_len / 24000) * 1000000; /*24K */ + if (fre_val == 1) + theory_time = (aw869xx->rtp_len / 48000) * 1000000; /*48K */ + + aw_dev_info(aw869xx->dev, "%s microsecond:%ld theory_time = %d\n", + __func__, aw869xx->microsecond, theory_time); + + lra_trim_code = aw869xx_osc_trim_calculation(theory_time, + aw869xx->microsecond); + if (lra_trim_code >= 0) { + aw869xx->osc_cali_data = lra_trim_code; + aw869xx_haptic_upload_lra(aw869xx, OSC_CALI); + } + return 0; +} + +static unsigned char aw869xx_haptic_osc_read_status(struct aw869xx *aw869xx) +{ + unsigned char reg_val = 0; + + aw869xx_i2c_read(aw869xx, AW869XX_REG_SYSST2, ®_val); + return reg_val; +} + +static int aw869xx_rtp_osc_calibration(struct aw869xx *aw869xx) +{ + const struct firmware *rtp_file; + int ret = -1; + unsigned int buf_len = 0; + unsigned char osc_int_state = 0; + + aw869xx->rtp_cnt = 0; + aw869xx->timeval_flags = 1; + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + /* fw loaded */ + ret = request_firmware(&rtp_file, aw869xx_rtp_name[0], aw869xx->dev); + if (ret < 0) { + aw_dev_err(aw869xx->dev, "%s: failed to read %s\n", __func__, + aw869xx_rtp_name[0]); + return ret; + } + /*awinic add stop,for irq interrupt during calibrate */ + aw869xx_haptic_stop(aw869xx); + aw869xx->rtp_init = 0; + mutex_lock(&aw869xx->rtp_lock); + vfree(aw869xx_rtp); + aw869xx_rtp = vmalloc(rtp_file->size + sizeof(int)); + if (!aw869xx_rtp) { + release_firmware(rtp_file); + mutex_unlock(&aw869xx->rtp_lock); + aw_dev_err(aw869xx->dev, "%s: error allocating memory\n", + __func__); + return -ENOMEM; + } + aw869xx_rtp->len = rtp_file->size; + aw869xx->rtp_len = rtp_file->size; + aw_dev_info(aw869xx->dev, "%s: rtp file:[%s] size = %dbytes\n", __func__, + aw869xx_rtp_name[0], aw869xx_rtp->len); + + memcpy(aw869xx_rtp->data, rtp_file->data, rtp_file->size); + release_firmware(rtp_file); + mutex_unlock(&aw869xx->rtp_lock); + /* gain */ + aw869xx_haptic_ram_vbat_compensate(aw869xx, false); + /* rtp mode config */ + aw869xx_haptic_play_mode(aw869xx, AW869XX_HAPTIC_RTP_MODE); + /* bst mode */ + aw869xx_haptic_bst_mode_config(aw869xx, AW869XX_HAPTIC_BST_MODE_BYPASS); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL7, + AW869XX_BIT_SYSCTRL7_INT_MODE_MASK, + AW869XX_BIT_SYSCTRL7_INT_MODE_EDGE); + disable_irq(gpio_to_irq(aw869xx->irq_gpio)); +#ifdef AW_OSC_COARSE_CALI + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRIMCFG3, + AW869XX_BIT_TRIMCFG3_OSC_TRIM_SRC_MASK, + AW869XX_BIT_TRIMCFG3_OSC_TRIM_SRC_REG); + aw869xx_i2c_write(aw869xx, AW869XX_REG_TRIMCFG4, 0xF4); +#endif + /* haptic go */ + aw869xx_haptic_play_go(aw869xx); + /* require latency of CPU & DMA not more then PM_QOS_VALUE_VB us */ + pm_qos_add_request(&pm_qos_req_vb, PM_QOS_CPU_DMA_LATENCY, + PM_QOS_VALUE_VB); + while (1) { + if (!aw869xx_haptic_rtp_get_fifo_afs(aw869xx)) { + aw_dev_info(aw869xx->dev, "%s not almost_full," + " aw869xx->rtp_cnt= %d\n", __func__, + aw869xx->rtp_cnt); + mutex_lock(&aw869xx->rtp_lock); + if ((aw869xx_rtp->len - aw869xx->rtp_cnt) < + (aw869xx->ram.base_addr >> 2)) + buf_len = aw869xx_rtp->len - aw869xx->rtp_cnt; + else + buf_len = (aw869xx->ram.base_addr >> 2); + + if (aw869xx->rtp_cnt != aw869xx_rtp->len) { + if (aw869xx->timeval_flags == 1) { + do_gettimeofday(&aw869xx->start); + aw869xx->timeval_flags = 0; + } + aw869xx->rtp_update_flag = + aw869xx_i2c_writes(aw869xx, + AW869XX_REG_RTPDATA, + &aw869xx_rtp->data + [aw869xx->rtp_cnt], + buf_len); + aw869xx->rtp_cnt += buf_len; + } + mutex_unlock(&aw869xx->rtp_lock); + } + osc_int_state = aw869xx_haptic_osc_read_status(aw869xx); + if (osc_int_state & AW869XX_BIT_SYSST2_FF_EMPTY) { + do_gettimeofday(&aw869xx->end); + pr_info + ("%s osc trim playback done aw869xx->rtp_cnt= %d\n", + __func__, aw869xx->rtp_cnt); + break; + } + do_gettimeofday(&aw869xx->end); + aw869xx->microsecond = + (aw869xx->end.tv_sec - aw869xx->start.tv_sec) * 1000000 + + (aw869xx->end.tv_usec - aw869xx->start.tv_usec); + if (aw869xx->microsecond > OSC_CALI_MAX_LENGTH) { + aw_dev_info(aw869xx->dev, "%s osc trim time out!" + " aw869xx->rtp_cnt %d osc_int_state %02x\n", + __func__, aw869xx->rtp_cnt, osc_int_state); + break; + } + } + pm_qos_remove_request(&pm_qos_req_vb); + enable_irq(gpio_to_irq(aw869xx->irq_gpio)); + + aw869xx->microsecond = + (aw869xx->end.tv_sec - aw869xx->start.tv_sec) * 1000000 + + (aw869xx->end.tv_usec - aw869xx->start.tv_usec); + /*calibration osc */ + aw_dev_info(aw869xx->dev, "%s awinic_microsecond: %ld\n", __func__, + aw869xx->microsecond); + aw_dev_info(aw869xx->dev, "%s exit\n", __func__); + return 0; +} + +static void aw869xx_rtp_work_routine(struct work_struct *work) +{ + const struct firmware *rtp_file; + int ret = -1; + unsigned int cnt = 200; + unsigned char reg_val = 0; + bool rtp_work_flag = false; + struct aw869xx *aw869xx = container_of(work, struct aw869xx, rtp_work); + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + mutex_lock(&aw869xx->rtp_lock); + /* fw loaded */ + ret = request_firmware(&rtp_file, + aw869xx_rtp_name[aw869xx->rtp_file_num], + aw869xx->dev); + if (ret < 0) { + aw_dev_err(aw869xx->dev, "%s: failed to read %s\n", __func__, + aw869xx_rtp_name[aw869xx->rtp_file_num]); + mutex_unlock(&aw869xx->rtp_lock); + return; + } + aw869xx->rtp_init = 0; + vfree(aw869xx_rtp); + aw869xx_rtp = vmalloc(rtp_file->size + sizeof(int)); + if (!aw869xx_rtp) { + release_firmware(rtp_file); + aw_dev_err(aw869xx->dev, "%s: error allocating memory\n", + __func__); + mutex_unlock(&aw869xx->rtp_lock); + return; + } + aw869xx_rtp->len = rtp_file->size; + aw_dev_info(aw869xx->dev, "%s: rtp file:[%s] size = %dbytes\n", __func__, + aw869xx_rtp_name[aw869xx->rtp_file_num], aw869xx_rtp->len); + memcpy(aw869xx_rtp->data, rtp_file->data, rtp_file->size); + mutex_unlock(&aw869xx->rtp_lock); + release_firmware(rtp_file); + mutex_lock(&aw869xx->lock); + aw869xx->rtp_init = 1; + aw869xx_haptic_upload_lra(aw869xx, OSC_CALI); + /* gain */ + aw869xx_haptic_ram_vbat_compensate(aw869xx, false); + /* rtp mode config */ + aw869xx_haptic_play_mode(aw869xx, AW869XX_HAPTIC_RTP_MODE); + /* bst mode config */ + aw869xx_haptic_bst_mode_config(aw869xx, AW869XX_HAPTIC_BST_MODE_BYPASS); + /* haptic go */ + aw869xx_haptic_play_go(aw869xx); + mutex_unlock(&aw869xx->lock); + usleep_range(2000, 2500); + while (cnt) { + aw869xx_i2c_read(aw869xx, AW869XX_REG_GLBRD5, ®_val); + if ((reg_val & 0x0f) == 0x08) { + cnt = 0; + rtp_work_flag = true; + aw_dev_info(aw869xx->dev, "%s RTP_GO! glb_state=0x08\n", + __func__); + } else { + cnt--; + aw_dev_dbg(aw869xx->dev, + "%s wait for RTP_GO, glb_state=0x%02X\n", + __func__, reg_val); + } + usleep_range(2000, 2500); + } + if (rtp_work_flag) { + aw869xx_haptic_rtp_init(aw869xx); + } else { + /* enter standby mode */ + aw869xx_haptic_stop(aw869xx); + aw_dev_err(aw869xx->dev, "%s failed to enter RTP_GO status!\n", + __func__); + } +} + +/***************************************************** + * + * haptic cont + * + *****************************************************/ +static int aw869xx_haptic_cont_config(struct aw869xx *aw869xx) +{ + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + + /* work mode */ + aw869xx_haptic_play_mode(aw869xx, AW869XX_HAPTIC_CONT_MODE); + /* bst mode */ + aw869xx_haptic_bst_mode_config(aw869xx, AW869XX_HAPTIC_BST_MODE_BYPASS); + /* cont config */ + /* aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_CONTCFG1, + ** AW869XX_BIT_CONTCFG1_EN_F0_DET_MASK, + ** AW869XX_BIT_CONTCFG1_F0_DET_ENABLE); + */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_CONTCFG6, + AW869XX_BIT_CONTCFG6_TRACK_EN_MASK, + AW869XX_BIT_CONTCFG6_TRACK_ENABLE); + /* f0 driver level */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_CONTCFG6, + AW869XX_BIT_CONTCFG6_DRV1_LVL_MASK, + aw869xx->cont_drv1_lvl); + aw869xx_i2c_write(aw869xx, AW869XX_REG_CONTCFG7, + aw869xx->cont_drv2_lvl); + /* DRV1_TIME */ + /* aw869xx_i2c_write(aw869xx, AW869XX_REG_CONTCFG8, 0xFF); */ + /* DRV2_TIME */ + aw869xx_i2c_write(aw869xx, AW869XX_REG_CONTCFG9, 0xFF); + /* cont play go */ + aw869xx_haptic_play_go(aw869xx); + return 0; +} + +/***************************************************** + * + * haptic f0 cali + * + *****************************************************/ +static int aw869xx_haptic_read_lra_f0(struct aw869xx *aw869xx) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned int f0_reg = 0; + unsigned long f0_tmp = 0; + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + /* F_LRA_F0_H */ + ret = aw869xx_i2c_read(aw869xx, AW869XX_REG_CONTRD14, ®_val); + f0_reg = (f0_reg | reg_val) << 8; + /* F_LRA_F0_L */ + ret = aw869xx_i2c_read(aw869xx, AW869XX_REG_CONTRD15, ®_val); + f0_reg |= (reg_val << 0); + if (!f0_reg) { + aw_dev_err(aw869xx->dev, + "%s didn't get lra f0 because f0_reg value is 0!\n", + __func__); + aw869xx->f0 = aw869xx->dts_info.f0_ref; + return -1; + } else { + f0_tmp = 384000 * 10 / f0_reg; + aw869xx->f0 = (unsigned int)f0_tmp; + aw_dev_info(aw869xx->dev, "%s lra_f0=%d\n", __func__, + aw869xx->f0); + } + + return 0; +} + +static int aw869xx_haptic_read_cont_f0(struct aw869xx *aw869xx) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned int f0_reg = 0; + unsigned long f0_tmp = 0; + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + ret = aw869xx_i2c_read(aw869xx, AW869XX_REG_CONTRD16, ®_val); + f0_reg = (f0_reg | reg_val) << 8; + ret = aw869xx_i2c_read(aw869xx, AW869XX_REG_CONTRD17, ®_val); + f0_reg |= (reg_val << 0); + if (!f0_reg) { + aw_dev_err(aw869xx->dev, + "%s didn't get cont f0 because f0_reg value is 0!\n", + __func__); + aw869xx->cont_f0 = aw869xx->dts_info.f0_ref; + return -1; + } else { + f0_tmp = 384000 * 10 / f0_reg; + aw869xx->cont_f0 = (unsigned int)f0_tmp; + aw_dev_info(aw869xx->dev, "%s cont_f0=%d\n", __func__, + aw869xx->cont_f0); + } + return 0; +} + +static int aw869xx_haptic_ram_get_f0(struct aw869xx *aw869xx) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned int cnt = 200; + bool get_f0_flag = false; + unsigned char brk_en_temp = 0; + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + aw869xx->f0 = aw869xx->dts_info.f0_ref; + /* enter standby mode */ + aw869xx_haptic_stop(aw869xx); + /* f0 calibrate work mode */ + aw869xx_haptic_play_mode(aw869xx, AW869XX_HAPTIC_RAM_MODE); + /* bst mode */ + aw869xx_haptic_bst_mode_config(aw869xx, AW869XX_HAPTIC_BST_MODE_BYPASS); + /* enable f0 detect */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_CONTCFG1, + AW869XX_BIT_CONTCFG1_EN_F0_DET_MASK, + AW869XX_BIT_CONTCFG1_F0_DET_ENABLE); + /* cont config */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_CONTCFG6, + AW869XX_BIT_CONTCFG6_TRACK_EN_MASK, + AW869XX_BIT_CONTCFG6_TRACK_ENABLE); + /* enable auto break */ + aw869xx_i2c_read(aw869xx, AW869XX_REG_PLAYCFG3, ®_val); + brk_en_temp = 0x04 & reg_val; + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_BRK_EN_MASK, + AW869XX_BIT_PLAYCFG3_BRK_ENABLE); + aw869xx_haptic_set_wav_seq(aw869xx, 0x00, 0x01); + aw869xx_haptic_set_wav_loop(aw869xx, 0x00, 0x0A); + /* ram play go */ + aw869xx_haptic_play_go(aw869xx); + /* 300ms */ + while (cnt) { + aw869xx_i2c_read(aw869xx, AW869XX_REG_GLBRD5, ®_val); + if ((reg_val & 0x0f) == 0x00) { + cnt = 0; + get_f0_flag = true; + aw_dev_info(aw869xx->dev, + "%s entered standby mode! glb_state=0x%02X\n", + __func__, reg_val); + } else { + cnt--; + aw_dev_dbg(aw869xx->dev, + "%s waitting for standby, glb_state=0x%02X\n", + __func__, reg_val); + } + usleep_range(10000, 10500); + } + if (get_f0_flag) { + aw869xx_haptic_read_lra_f0(aw869xx); + } else { + aw_dev_err(aw869xx->dev, + "%s enter standby mode failed, stop reading f0!\n", + __func__); + } + /* restore default config */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_CONTCFG1, + AW869XX_BIT_CONTCFG1_EN_F0_DET_MASK, + AW869XX_BIT_CONTCFG1_F0_DET_DISABLE); + /* recover auto break config */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_BRK_EN_MASK, + brk_en_temp << 2); + return ret; +} + +static int aw869xx_haptic_cont_get_f0(struct aw869xx *aw869xx) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned int cnt = 200; + bool get_f0_flag = false; + unsigned char brk_en_temp = 0; + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + aw869xx->f0 = aw869xx->dts_info.f0_ref; + /* enter standby mode */ + aw869xx_haptic_stop(aw869xx); + /* f0 calibrate work mode */ + aw869xx_haptic_play_mode(aw869xx, AW869XX_HAPTIC_CONT_MODE); + /* bst mode */ + aw869xx_haptic_bst_mode_config(aw869xx, AW869XX_HAPTIC_BST_MODE_BYPASS); + /* enable f0 detect */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_CONTCFG1, + AW869XX_BIT_CONTCFG1_EN_F0_DET_MASK, + AW869XX_BIT_CONTCFG1_F0_DET_ENABLE); + /* cont config */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_CONTCFG6, + AW869XX_BIT_CONTCFG6_TRACK_EN_MASK, + AW869XX_BIT_CONTCFG6_TRACK_ENABLE); + /* enable auto break */ + aw869xx_i2c_read(aw869xx, AW869XX_REG_PLAYCFG3, ®_val); + brk_en_temp = 0x04 & reg_val; + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_BRK_EN_MASK, + AW869XX_BIT_PLAYCFG3_BRK_ENABLE); + /* f0 driver level */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_CONTCFG6, + AW869XX_BIT_CONTCFG6_DRV1_LVL_MASK, + aw869xx->dts_info.cont_drv1_lvl_dt); + aw869xx_i2c_write(aw869xx, AW869XX_REG_CONTCFG7, + aw869xx->dts_info.cont_drv2_lvl_dt); + /* DRV1_TIME */ + aw869xx_i2c_write(aw869xx, AW869XX_REG_CONTCFG8, + aw869xx->dts_info.cont_drv1_time_dt); + /* DRV2_TIME */ + aw869xx_i2c_write(aw869xx, AW869XX_REG_CONTCFG9, + aw869xx->dts_info.cont_drv2_time_dt); + /* TRACK_MARGIN */ + if (!aw869xx->dts_info.cont_track_margin) { + aw_dev_err(aw869xx->dev, + "%s aw869xx->dts_info.cont_track_margin = 0!\n", + __func__); + } else { + aw869xx_i2c_write(aw869xx, AW869XX_REG_CONTCFG11, + (unsigned char)aw869xx-> + dts_info.cont_track_margin); + } + /* DRV_WIDTH */ + /* + * aw869xx_i2c_write(aw869xx, AW869XX_REG_CONTCFG3, + * aw869xx->dts_info.cont_drv_width); + */ + /* cont play go */ + aw869xx_haptic_play_go(aw869xx); + /* 300ms */ + while (cnt) { + aw869xx_i2c_read(aw869xx, AW869XX_REG_GLBRD5, ®_val); + if ((reg_val & 0x0f) == 0x00) { + cnt = 0; + get_f0_flag = true; + aw_dev_info(aw869xx->dev, + "%s entered standby mode! glb_state=0x%02X\n", + __func__, reg_val); + } else { + cnt--; + aw_dev_dbg(aw869xx->dev, + "%s waitting for standby, glb_state=0x%02X\n", + __func__, reg_val); + } + usleep_range(10000, 10500); + } + if (get_f0_flag) { + aw869xx_haptic_read_lra_f0(aw869xx); + aw869xx_haptic_read_cont_f0(aw869xx); + } else { + aw_dev_err(aw869xx->dev, + "%s enter standby mode failed, stop reading f0!\n", + __func__); + } + /* restore default config */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_CONTCFG1, + AW869XX_BIT_CONTCFG1_EN_F0_DET_MASK, + AW869XX_BIT_CONTCFG1_F0_DET_DISABLE); + /* recover auto break config */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_BRK_EN_MASK, + brk_en_temp << 2); + return ret; +} + +static int aw869xx_haptic_f0_calibration(struct aw869xx *aw869xx) +{ + int ret = 0; + unsigned char reg_val = 0; + unsigned int f0_limit = 0; + char f0_cali_lra = 0; + int f0_cali_step = 0; + unsigned int f0_cali_min = aw869xx->dts_info.f0_ref * (100 - + aw869xx->dts_info.f0_cali_percent) / 100; + unsigned int f0_cali_max = aw869xx->dts_info.f0_ref * (100 + + aw869xx->dts_info.f0_cali_percent) / 100; + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + /* + * aw869xx_haptic_upload_lra(aw869xx, WRITE_ZERO); + */ + if (aw869xx_haptic_cont_get_f0(aw869xx)) { + aw_dev_err(aw869xx->dev, "%s get f0 error, user defafult f0\n", + __func__); + } else { + /* max and min limit */ + f0_limit = aw869xx->f0; + aw_dev_info(aw869xx->dev, "%s f0_ref = %d, f0_cali_min = %d," + " f0_cali_max = %d, f0 = %d\n", + __func__, aw869xx->dts_info.f0_ref, + f0_cali_min, f0_cali_max, aw869xx->f0); + + if ((aw869xx->f0 < f0_cali_min) || aw869xx->f0 > f0_cali_max) { + aw_dev_err(aw869xx->dev, + "%s f0 calibration out of range = %d!\n", + __func__, aw869xx->f0); + f0_limit = aw869xx->dts_info.f0_ref; + return -ERANGE; + } + aw_dev_info(aw869xx->dev, "%s f0_limit = %d\n", __func__, + (int)f0_limit); + /* calculate cali step */ + f0_cali_step = 100000 * ((int)f0_limit - + (int)aw869xx->dts_info.f0_ref) / + ((int)f0_limit * 24); + aw_dev_info(aw869xx->dev, "%s f0_cali_step = %d\n", __func__, + f0_cali_step); + if (f0_cali_step >= 0) { /*f0_cali_step >= 0 */ + if (f0_cali_step % 10 >= 5) + f0_cali_step = 32 + (f0_cali_step / 10 + 1); + else + f0_cali_step = 32 + f0_cali_step / 10; + } else { /* f0_cali_step < 0 */ + if (f0_cali_step % 10 <= -5) + f0_cali_step = 32 + (f0_cali_step / 10 - 1); + else + f0_cali_step = 32 + f0_cali_step / 10; + } + if (f0_cali_step > 31) + f0_cali_lra = (char)f0_cali_step - 32; + else + f0_cali_lra = (char)f0_cali_step + 32; + /* update cali step */ + aw869xx_i2c_read(aw869xx, AW869XX_REG_TRIMCFG3, ®_val); + aw869xx->f0_cali_data = ((int)f0_cali_lra + (int)(reg_val & 0x3f)) & 0x3f; + + aw_dev_info(aw869xx->dev, + "%s origin trim_lra = 0x%02X, f0_cali_lra = 0x%02X," + " final f0_cali_data = 0x%02X\n", + __func__, (reg_val & 0x3f), f0_cali_lra, + aw869xx->f0_cali_data); + aw869xx_haptic_upload_lra(aw869xx, F0_CALI); + } + /* restore standby work mode */ + aw869xx_haptic_stop(aw869xx); + return ret; +} + +static int aw869xx_haptic_i2s_init(struct aw869xx *aw869xx) +{ + aw_dev_info(aw869xx->dev, "%s: enter\n", __func__); + + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_I2S_PIN_MASK, + AW869XX_BIT_SYSCTRL2_I2S_PIN_I2S); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_IOCFG1, + AW869XX_BIT_IOCFG1_IO_FAST_MASK, + AW869XX_BIT_IOCFG1_IIS_IO_FAST_ENABLE); + return 0; +} + +static int aw869xx_haptic_init(struct aw869xx *aw869xx) +{ + int ret = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + /* haptic init */ + mutex_lock(&aw869xx->lock); + aw869xx->activate_mode = aw869xx->dts_info.mode; + ret = aw869xx_i2c_read(aw869xx, AW869XX_REG_WAVCFG1, ®_val); + aw869xx->index = reg_val & 0x7F; + ret = aw869xx_i2c_read(aw869xx, AW869XX_REG_PLAYCFG2, ®_val); + aw869xx->gain = reg_val & 0xFF; + aw_dev_info(aw869xx->dev, "%s aw869xx->gain =0x%02X\n", __func__, + aw869xx->gain); + ret = aw869xx_i2c_read(aw869xx, AW869XX_REG_PLAYCFG1, ®_val); + aw869xx->vmax = reg_val & 0x1F; + for (i = 0; i < AW869XX_SEQUENCER_SIZE; i++) { + ret = aw869xx_i2c_read(aw869xx, AW869XX_REG_WAVCFG1 + i, + ®_val); + aw869xx->seq[i] = reg_val; + } + aw869xx_haptic_play_mode(aw869xx, AW869XX_HAPTIC_STANDBY_MODE); + aw869xx_haptic_set_pwm(aw869xx, AW869XX_PWM_24K); + /* misc value init */ + aw869xx_haptic_misc_para_init(aw869xx); + /* set BST_ADJ */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_BSTCFG5, + AW869XX_BIT_BSTCFG5_BST_ADJ_MASK, + AW869XX_BIT_BSTCFG5_BST_ADJ_LOW); + aw869xx_haptic_set_bst_peak_cur(aw869xx); + aw869xx_haptic_swicth_motor_protect_config(aw869xx, 0x00, 0x00); + aw869xx_haptic_auto_bst_enable(aw869xx, + aw869xx->dts_info.is_enabled_auto_bst); + aw869xx_haptic_trig_param_init(aw869xx); + aw869xx_haptic_trig_param_config(aw869xx); + aw869xx_haptic_offset_calibration(aw869xx); + /* vbat compensation */ + aw869xx_haptic_vbat_mode_config(aw869xx, + AW869XX_HAPTIC_CONT_VBAT_HW_ADJUST_MODE); + aw869xx->ram_vbat_compensate = AW869XX_HAPTIC_RAM_VBAT_COMP_ENABLE; + /* i2s config */ + if (aw869xx->dts_info.is_enabled_i2s) { + aw_dev_info(aw869xx->dev, "%s i2s is enabled!\n", __func__); + aw869xx_haptic_i2s_init(aw869xx); + } + mutex_unlock(&aw869xx->lock); +#ifdef AW_F0_COARSE_CALI /* Only Test for F0 calibration offset */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_TRIMCFG3, + AW869XX_BIT_TRIMCFG3_OSC_TRIM_SRC_MASK, + AW869XX_BIT_TRIMCFG3_OSC_TRIM_SRC_REG); + /* aw869xx_i2c_write(aw869xx, AW869XX_REG_TRIMCFG3,0xCF); */ + /* 170-0xEC 235-0x06 260-0xfc */ + aw869xx_i2c_write(aw869xx, AW869XX_REG_TRIMCFG4, 0xEC); +#endif + /* f0 calibration */ + if (aw869xx->dts_info.is_enabled_powerup_f0_cali) { + mutex_lock(&aw869xx->lock); + aw869xx_haptic_upload_lra(aw869xx, WRITE_ZERO); + aw869xx_haptic_f0_calibration(aw869xx); + mutex_unlock(&aw869xx->lock); + } else { + aw_dev_info(aw869xx->dev, + "%s powerup f0 calibration is disabled\n", + __func__); + } + return ret; +} + +/***************************************************** + * + * vibrator + * + *****************************************************/ +#ifdef TIMED_OUTPUT +static int aw869xx_vibrator_get_time(struct timed_output_dev *dev) +{ + struct aw869xx *aw869xx = container_of(dev, struct aw869xx, vib_dev); + + if (hrtimer_active(&aw869xx->timer)) { + ktime_t r = hrtimer_get_remaining(&aw869xx->timer); + + return ktime_to_ms(r); + } + return 0; +} + +static void aw869xx_vibrator_enable(struct timed_output_dev *dev, int value) +{ + struct aw869xx *aw869xx = container_of(dev, struct aw869xx, vib_dev); + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + mutex_lock(&aw869xx->lock); + aw869xx_haptic_stop(aw869xx); + if (value > 0) { + aw869xx_haptic_ram_vbat_compensate(aw869xx, false); + aw869xx_haptic_bst_mode_config(aw869xx, + AW869XX_HAPTIC_BST_MODE_BOOST); + aw869xx_haptic_play_wav_seq(aw869xx, value); + } + mutex_unlock(&aw869xx->lock); + aw_dev_info(aw869xx->dev, "%s exit\n", __func__); +} +#else +static enum led_brightness aw869xx_haptic_brightness_get(struct led_classdev + *cdev) +{ + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + + return aw869xx->amplitude; +} + +static void aw869xx_haptic_brightness_set(struct led_classdev *cdev, + enum led_brightness level) +{ + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + if (!aw869xx->ram_init) + return; + if (aw869xx->ram_update_flag < 0) + return; + aw869xx->amplitude = level; + mutex_lock(&aw869xx->lock); + aw869xx_haptic_stop(aw869xx); + if (aw869xx->amplitude > 0) { + aw869xx_haptic_ram_vbat_compensate(aw869xx, false); + aw869xx_haptic_bst_mode_config(aw869xx, + AW869XX_HAPTIC_BST_MODE_BOOST); + aw869xx_haptic_play_wav_seq(aw869xx, aw869xx->amplitude); + } + mutex_unlock(&aw869xx->lock); +} +#endif + +static ssize_t aw869xx_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + + return snprintf(buf, PAGE_SIZE, "state = %d\n", aw869xx->state); +} + +static ssize_t aw869xx_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw869xx_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ktime_t time_rem; + s64 time_ms = 0; + + if (hrtimer_active(&aw869xx->timer)) { + time_rem = hrtimer_get_remaining(&aw869xx->timer); + time_ms = ktime_to_ms(time_rem); + } + return snprintf(buf, PAGE_SIZE, "duration = %lldms\n", time_ms); +} + +static ssize_t aw869xx_duration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + /* setting 0 on duration is NOP for now */ + if (val <= 0) + return count; + aw869xx->duration = val; + return count; +} + +static ssize_t aw869xx_activate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + + /* For now nothing to show */ + return snprintf(buf, PAGE_SIZE, "activate = %d\n", aw869xx->state); +} + +static ssize_t aw869xx_activate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + aw_dev_info(aw869xx->dev, "%s: value=%d\n", __func__, val); + mutex_lock(&aw869xx->lock); + hrtimer_cancel(&aw869xx->timer); + aw869xx->state = val; + mutex_unlock(&aw869xx->lock); + schedule_work(&aw869xx->long_vibrate_work); + return count; +} + +static ssize_t aw869xx_activate_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + + return snprintf(buf, PAGE_SIZE, "activate_mode = %d\n", + aw869xx->activate_mode); +} + +static ssize_t aw869xx_activate_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + mutex_lock(&aw869xx->lock); + aw869xx->activate_mode = val; + mutex_unlock(&aw869xx->lock); + return count; +} + +static ssize_t aw869xx_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned char reg_val = 0; + + aw869xx_i2c_read(aw869xx, AW869XX_REG_WAVCFG1, ®_val); + aw869xx->index = reg_val; + return snprintf(buf, PAGE_SIZE, "index = %d\n", aw869xx->index); +} + +static ssize_t aw869xx_index_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + aw_dev_info(aw869xx->dev, "%s: value=%d\n", __func__, val); + mutex_lock(&aw869xx->lock); + aw869xx->index = val; + aw869xx_haptic_set_repeat_wav_seq(aw869xx, aw869xx->index); + mutex_unlock(&aw869xx->lock); + return count; +} + +static ssize_t aw869xx_vmax_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + + return snprintf(buf, PAGE_SIZE, "vmax = 0x%02X\n", aw869xx->vmax); +} + +static ssize_t aw869xx_vmax_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + aw_dev_info(aw869xx->dev, "%s: value=%d\n", __func__, val); + + mutex_lock(&aw869xx->lock); + aw869xx->vmax = val; + aw869xx_haptic_set_bst_vol(aw869xx, aw869xx->vmax); + mutex_unlock(&aw869xx->lock); + return count; +} + +static ssize_t aw869xx_gain_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + + return snprintf(buf, PAGE_SIZE, "gain = 0x%02X\n", aw869xx->gain); +} + +static ssize_t aw869xx_gain_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + aw_dev_info(aw869xx->dev, "%s: value=%d\n", __func__, val); + + mutex_lock(&aw869xx->lock); + aw869xx->gain = val; + aw869xx_haptic_set_gain(aw869xx, aw869xx->gain); + mutex_unlock(&aw869xx->lock); + return count; +} + +static ssize_t aw869xx_seq_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + size_t count = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + for (i = 0; i < AW869XX_SEQUENCER_SIZE; i++) { + aw869xx_i2c_read(aw869xx, AW869XX_REG_WAVCFG1 + i, ®_val); + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d = %d\n", i + 1, reg_val); + aw869xx->seq[i] |= reg_val; + } + return count; +} + +static ssize_t aw869xx_seq_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_dev_info(aw869xx->dev, "%s: seq%d=0x%02X\n", __func__, + databuf[0], databuf[1]); + mutex_lock(&aw869xx->lock); + aw869xx->seq[databuf[0]] = (unsigned char)databuf[1]; + aw869xx_haptic_set_wav_seq(aw869xx, (unsigned char)databuf[0], + aw869xx->seq[databuf[0]]); + mutex_unlock(&aw869xx->lock); + } + return count; +} + +static ssize_t aw869xx_loop_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + size_t count = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + for (i = 0; i < AW869XX_SEQUENCER_LOOP_SIZE; i++) { + aw869xx_i2c_read(aw869xx, AW869XX_REG_WAVCFG9 + i, ®_val); + aw869xx->loop[i * 2 + 0] = (reg_val >> 4) & 0x0F; + aw869xx->loop[i * 2 + 1] = (reg_val >> 0) & 0x0F; + + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d_loop = %d\n", i * 2 + 1, + aw869xx->loop[i * 2 + 0]); + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d_loop = %d\n", i * 2 + 2, + aw869xx->loop[i * 2 + 1]); + } + return count; +} + +static ssize_t aw869xx_loop_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_dev_info(aw869xx->dev, "%s: seq%d loop=0x%02X\n", __func__, + databuf[0], databuf[1]); + mutex_lock(&aw869xx->lock); + aw869xx->loop[databuf[0]] = (unsigned char)databuf[1]; + aw869xx_haptic_set_wav_loop(aw869xx, (unsigned char)databuf[0], + aw869xx->loop[databuf[0]]); + mutex_unlock(&aw869xx->lock); + } + + return count; +} + +static ssize_t aw869xx_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + for (i = 0; i < AW869XX_REG_MAX; i++) { + if (!(aw869xx_reg_access[i] & REG_RD_ACCESS)) + continue; + aw869xx_i2c_read(aw869xx, i, ®_val); + len += snprintf(buf + len, PAGE_SIZE - len, + "reg:0x%02X=0x%02X\n", i, reg_val); + } + return len; +} + +static ssize_t aw869xx_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw869xx_i2c_write(aw869xx, (unsigned char)databuf[0], + (unsigned char)databuf[1]); + } + + return count; +} + +static ssize_t aw869xx_rtp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "rtp_cnt = %d\n", + aw869xx->rtp_cnt); + return len; +} + +static ssize_t aw869xx_rtp_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) { + aw_dev_info(aw869xx->dev, "%s: kstrtouint fail\n", __func__); + return rc; + } + mutex_lock(&aw869xx->lock); + aw869xx_haptic_stop(aw869xx); + aw869xx_haptic_set_rtp_aei(aw869xx, false); + aw869xx_interrupt_clear(aw869xx); + if (val < (sizeof(aw869xx_rtp_name) / AW869XX_RTP_NAME_MAX)) { + aw869xx->rtp_file_num = val; + if (val) { + aw_dev_info(aw869xx->dev, + "%s: aw869xx_rtp_name[%d]: %s\n", __func__, + val, aw869xx_rtp_name[val]); + + schedule_work(&aw869xx->rtp_work); + } else { + aw_dev_err(aw869xx->dev, + "%s: rtp_file_num 0x%02X over max value\n", + __func__, aw869xx->rtp_file_num); + } + } + mutex_unlock(&aw869xx->lock); + return count; +} + +static ssize_t aw869xx_ram_update_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + unsigned int i = 0; + unsigned char reg_val = 0; + + /* RAMINIT Enable */ + aw869xx_haptic_raminit(aw869xx, true); + aw869xx_haptic_stop(aw869xx); + aw869xx_i2c_write(aw869xx, AW869XX_REG_RAMADDRH, + (unsigned char)(aw869xx->ram.base_addr >> 8)); + aw869xx_i2c_write(aw869xx, AW869XX_REG_RAMADDRL, + (unsigned char)(aw869xx->ram.base_addr & 0x00ff)); + len += snprintf(buf + len, PAGE_SIZE - len, "aw869xx_haptic_ram:\n"); + for (i = 0; i < aw869xx->ram.len; i++) { + aw869xx_i2c_read(aw869xx, AW869XX_REG_RAMDATA, ®_val); + len += snprintf(buf + len, PAGE_SIZE - len, "0x%02X,", reg_val); + } + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + /* RAMINIT Disable */ + aw869xx_haptic_raminit(aw869xx, false); + return len; +} + +static ssize_t aw869xx_ram_update_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + if (val) + aw869xx_ram_update(aw869xx); + return count; +} + +static ssize_t aw869xx_f0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + + mutex_lock(&aw869xx->lock); + aw869xx_haptic_upload_lra(aw869xx, F0_CALI); + aw869xx_haptic_cont_get_f0(aw869xx); + mutex_unlock(&aw869xx->lock); + len += snprintf(buf + len, PAGE_SIZE - len, + "lra_f0 = %d cont_f0 = %d\n", aw869xx->f0, + aw869xx->cont_f0); + return len; +} + +static ssize_t aw869xx_f0_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + return count; +} + +static ssize_t aw869xx_ram_f0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + + mutex_lock(&aw869xx->lock); + aw869xx_haptic_upload_lra(aw869xx, F0_CALI); + aw869xx_haptic_ram_get_f0(aw869xx); + mutex_unlock(&aw869xx->lock); + len += snprintf(buf + len, PAGE_SIZE - len, "ram_lra_f0 = %d\n", + aw869xx->f0); + return len; +} + +static ssize_t aw869xx_ram_f0_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + return count; +} + +static ssize_t aw869xx_osc_save_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "osc_cali_data = 0x%02X\n", + aw869xx->osc_cali_data); + + return len; +} + +static ssize_t aw869xx_osc_save_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + aw869xx->osc_cali_data = val; + return count; +} + + +static ssize_t aw869xx_f0_save_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "f0_cali_data = 0x%02X\n", + aw869xx->f0_cali_data); + + return len; +} + +static ssize_t aw869xx_f0_save_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + aw869xx->f0_cali_data = val; + return count; +} + +static ssize_t aw869xx_cali_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + + mutex_lock(&aw869xx->lock); + aw869xx_haptic_upload_lra(aw869xx, F0_CALI); + aw869xx_haptic_cont_get_f0(aw869xx); + mutex_unlock(&aw869xx->lock); + len += snprintf(buf + len, PAGE_SIZE - len, + "lra_f0 = %d cont_f0 = %d\n", aw869xx->f0, + aw869xx->cont_f0); + return len; +} + +static ssize_t aw869xx_cali_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int val = 0; + int i; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + if (val) { + mutex_lock(&aw869xx->lock); + aw869xx_haptic_upload_lra(aw869xx, WRITE_ZERO); + for (i = 0; i < 2; i++) { + aw869xx_haptic_f0_calibration(aw869xx); + mdelay(20); + } + mutex_unlock(&aw869xx->lock); + } + return count; +} + +static ssize_t aw869xx_cont_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + + aw869xx_haptic_read_cont_f0(aw869xx); + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_f0 = %d\n", aw869xx->cont_f0); + return len; +} + +static ssize_t aw869xx_cont_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + aw869xx_haptic_stop(aw869xx); + if (val) + aw869xx_haptic_cont_config(aw869xx); + return count; +} + +static ssize_t aw869xx_cont_wait_num_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_wait_num = 0x%02X\n", + aw869xx->cont_wait_num); + return len; +} + +static ssize_t aw869xx_cont_wait_num_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int databuf[1] = { 0 }; + + if (sscanf(buf, "%x", &databuf[0]) == 1) { + aw869xx->cont_wait_num = databuf[0]; + aw869xx_i2c_write(aw869xx, AW869XX_REG_CONTCFG4, databuf[0]); + } + return count; +} + +static ssize_t aw869xx_cont_drv_lvl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_lvl = 0x%02X, cont_drv2_lvl = 0x%02X\n", + aw869xx->cont_drv1_lvl, aw869xx->cont_drv2_lvl); + return len; +} + +static ssize_t aw869xx_cont_drv_lvl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw869xx->cont_drv1_lvl = databuf[0]; + aw869xx->cont_drv2_lvl = databuf[1]; + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_CONTCFG6, + AW869XX_BIT_CONTCFG6_DRV1_LVL_MASK, + aw869xx->cont_drv1_lvl); + aw869xx_i2c_write(aw869xx, AW869XX_REG_CONTCFG7, + aw869xx->cont_drv2_lvl); + } + return count; +} + +static ssize_t aw869xx_cont_drv_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_time = 0x%02X, cont_drv2_time = 0x%02X\n", + aw869xx->cont_drv1_time, aw869xx->cont_drv2_time); + return len; +} + +static ssize_t aw869xx_cont_drv_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw869xx->cont_drv1_time = databuf[0]; + aw869xx->cont_drv2_time = databuf[1]; + aw869xx_i2c_write(aw869xx, AW869XX_REG_CONTCFG8, + aw869xx->cont_drv1_time); + aw869xx_i2c_write(aw869xx, AW869XX_REG_CONTCFG9, + aw869xx->cont_drv2_time); + } + return count; +} + +static ssize_t aw869xx_cont_brk_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "cont_brk_time = 0x%02X\n", + aw869xx->cont_brk_time); + return len; +} + +static ssize_t aw869xx_cont_brk_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int databuf[1] = { 0 }; + + if (sscanf(buf, "%x", &databuf[0]) == 1) { + aw869xx->cont_brk_time = databuf[0]; + aw869xx_i2c_write(aw869xx, AW869XX_REG_CONTCFG10, + aw869xx->cont_brk_time); + } + return count; +} + +static ssize_t aw869xx_vbat_monitor_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + + mutex_lock(&aw869xx->lock); + aw869xx_haptic_get_vbat(aw869xx); + len += snprintf(buf + len, PAGE_SIZE - len, "vbat_monitor = %d\n", + aw869xx->vbat); + mutex_unlock(&aw869xx->lock); + + return len; +} + +static ssize_t aw869xx_vbat_monitor_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw869xx_lra_resistance_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + + aw869xx_haptic_get_lra_resistance(aw869xx); + len += snprintf(buf + len, PAGE_SIZE - len, "lra_resistance = %d\n", + aw869xx->lra); + return len; +} + +static ssize_t aw869xx_lra_resistance_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t aw869xx_auto_boost_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "auto_boost = %d\n", + aw869xx->auto_boost); + + return len; +} + +static ssize_t aw869xx_auto_boost_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw869xx->lock); + aw869xx_haptic_stop(aw869xx); + aw869xx_haptic_auto_bst_enable(aw869xx, val); + mutex_unlock(&aw869xx->lock); + + return count; +} + +static ssize_t aw869xx_prctmode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + unsigned char reg_val = 0; + + aw869xx_i2c_read(aw869xx, AW869XX_REG_DETCFG1, ®_val); + + len += snprintf(buf + len, PAGE_SIZE - len, "prctmode = %d\n", + reg_val & 0x08); + return len; +} + +static ssize_t aw869xx_prctmode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int databuf[2] = { 0, 0 }; + unsigned int addr = 0; + unsigned int val = 0; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + addr = databuf[0]; + val = databuf[1]; + mutex_lock(&aw869xx->lock); + aw869xx_haptic_swicth_motor_protect_config(aw869xx, addr, val); + mutex_unlock(&aw869xx->lock); + } + return count; +} + +static ssize_t aw869xx_trig_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + unsigned char i = 0; + + for (i = 0; i < AW869XX_TRIG_NUM; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "trig%d: trig_level=%d, trig_polar=%d, pos_enable=%d, pos_sequence=%d, neg_enable=%d, neg_sequence=%d trig_brk=%d, trig_bst=%d\n", + i + 1, + aw869xx->trig[i].trig_level, + aw869xx->trig[i].trig_polar, + aw869xx->trig[i].pos_enable, + aw869xx->trig[i].pos_sequence, + aw869xx->trig[i].neg_enable, + aw869xx->trig[i].neg_sequence, + aw869xx->trig[i].trig_brk, + aw869xx->trig[i].trig_bst); + } + + return len; +} + +static ssize_t aw869xx_trig_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int databuf[9] = { 0 }; + + if (sscanf(buf, "%d %d %d %d %d %d %d %d %d", &databuf[0], &databuf[1], + &databuf[2], &databuf[3], &databuf[4], &databuf[5], + &databuf[6], &databuf[7], &databuf[8])) { + aw_dev_info(aw869xx->dev, + "%s: %d, %d, %d, %d, %d, %d, %d, %d, %d\n", + __func__, databuf[0], databuf[1], databuf[2], + databuf[3], databuf[4], databuf[5], databuf[6], + databuf[7], databuf[8]); + if (databuf[0] > 3) + databuf[0] = 3; + if (databuf[0] < 0) + databuf[0] = 1; + aw869xx->trig[databuf[0]].trig_level = databuf[1]; + aw869xx->trig[databuf[0]].trig_polar = databuf[2]; + aw869xx->trig[databuf[0]].pos_enable = databuf[3]; + aw869xx->trig[databuf[0]].pos_sequence = databuf[4]; + aw869xx->trig[databuf[0]].neg_enable = databuf[5]; + aw869xx->trig[databuf[0]].neg_sequence = databuf[6]; + aw869xx->trig[databuf[0]].trig_brk = databuf[7]; + aw869xx->trig[databuf[0]].trig_bst = databuf[8]; + mutex_lock(&aw869xx->lock); + aw869xx_haptic_trig_param_config(aw869xx); + mutex_unlock(&aw869xx->lock); + } + return count; +} + +static ssize_t aw869xx_ram_vbat_compensate_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "ram_vbat_compensate = %d\n", + aw869xx->ram_vbat_compensate); + + return len; +} + +static ssize_t aw869xx_ram_vbat_compensate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw869xx->lock); + if (val) + aw869xx->ram_vbat_compensate = + AW869XX_HAPTIC_RAM_VBAT_COMP_ENABLE; + else + aw869xx->ram_vbat_compensate = + AW869XX_HAPTIC_RAM_VBAT_COMP_DISABLE; + mutex_unlock(&aw869xx->lock); + + return count; +} + +static ssize_t aw869xx_osc_cali_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "osc_cali_data = 0x%02X\n", + aw869xx->osc_cali_data); + + return len; +} + +static ssize_t aw869xx_osc_cali_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw869xx *aw869xx = container_of(cdev, struct aw869xx, vib_dev); + unsigned int val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + mutex_lock(&aw869xx->lock); + if (val == 1) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_D2SCFG1, + AW869XX_BIT_D2SCFG1_CLK_TRIM_MODE_MASK, + AW869XX_BIT_D2SCFG1_CLK_TRIM_MODE_24K); + aw869xx_haptic_upload_lra(aw869xx, WRITE_ZERO); + aw869xx_rtp_osc_calibration(aw869xx); + aw869xx_rtp_trim_lra_calibration(aw869xx); + } + /* osc calibration flag end,Other behaviors are permitted */ + mutex_unlock(&aw869xx->lock); + + return count; +} + +static DEVICE_ATTR(state, S_IWUSR | S_IRUGO, aw869xx_state_show, + aw869xx_state_store); +static DEVICE_ATTR(duration, S_IWUSR | S_IRUGO, aw869xx_duration_show, + aw869xx_duration_store); +static DEVICE_ATTR(activate, S_IWUSR | S_IRUGO, aw869xx_activate_show, + aw869xx_activate_store); +static DEVICE_ATTR(activate_mode, S_IWUSR | S_IRUGO, aw869xx_activate_mode_show, + aw869xx_activate_mode_store); +static DEVICE_ATTR(index, S_IWUSR | S_IRUGO, aw869xx_index_show, + aw869xx_index_store); +static DEVICE_ATTR(vmax, S_IWUSR | S_IRUGO, aw869xx_vmax_show, + aw869xx_vmax_store); +static DEVICE_ATTR(gain, S_IWUSR | S_IRUGO, aw869xx_gain_show, + aw869xx_gain_store); +static DEVICE_ATTR(seq, S_IWUSR | S_IRUGO, aw869xx_seq_show, aw869xx_seq_store); +static DEVICE_ATTR(loop, S_IWUSR | S_IRUGO, aw869xx_loop_show, + aw869xx_loop_store); +static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, aw869xx_reg_show, aw869xx_reg_store); +static DEVICE_ATTR(rtp, S_IWUSR | S_IRUGO, aw869xx_rtp_show, aw869xx_rtp_store); +static DEVICE_ATTR(ram_update, S_IWUSR | S_IRUGO, aw869xx_ram_update_show, + aw869xx_ram_update_store); +static DEVICE_ATTR(f0, S_IWUSR | S_IRUGO, aw869xx_f0_show, aw869xx_f0_store); +static DEVICE_ATTR(f0_save, S_IWUSR | S_IRUGO, aw869xx_f0_save_show, + aw869xx_f0_save_store); +static DEVICE_ATTR(osc_save, S_IWUSR | S_IRUGO, aw869xx_osc_save_show, + aw869xx_osc_save_store); +static DEVICE_ATTR(ram_f0, S_IWUSR | S_IRUGO, aw869xx_ram_f0_show, + aw869xx_ram_f0_store); +static DEVICE_ATTR(cali, S_IWUSR | S_IRUGO, aw869xx_cali_show, + aw869xx_cali_store); +static DEVICE_ATTR(cont, S_IWUSR | S_IRUGO, aw869xx_cont_show, + aw869xx_cont_store); +static DEVICE_ATTR(cont_wait_num, S_IWUSR | S_IRUGO, aw869xx_cont_wait_num_show, + aw869xx_cont_wait_num_store); +static DEVICE_ATTR(cont_drv_lvl, S_IWUSR | S_IRUGO, aw869xx_cont_drv_lvl_show, + aw869xx_cont_drv_lvl_store); +static DEVICE_ATTR(cont_drv_time, S_IWUSR | S_IRUGO, aw869xx_cont_drv_time_show, + aw869xx_cont_drv_time_store); +static DEVICE_ATTR(cont_brk_time, S_IWUSR | S_IRUGO, aw869xx_cont_brk_time_show, + aw869xx_cont_brk_time_store); +static DEVICE_ATTR(vbat_monitor, S_IWUSR | S_IRUGO, aw869xx_vbat_monitor_show, + aw869xx_vbat_monitor_store); +static DEVICE_ATTR(lra_resistance, S_IWUSR | S_IRUGO, + aw869xx_lra_resistance_show, aw869xx_lra_resistance_store); +static DEVICE_ATTR(auto_boost, S_IWUSR | S_IRUGO, aw869xx_auto_boost_show, + aw869xx_auto_boost_store); +static DEVICE_ATTR(prctmode, S_IWUSR | S_IRUGO, aw869xx_prctmode_show, + aw869xx_prctmode_store); +static DEVICE_ATTR(trig, S_IWUSR | S_IRUGO, aw869xx_trig_show, + aw869xx_trig_store); +static DEVICE_ATTR(ram_vbat_comp, S_IWUSR | S_IRUGO, + aw869xx_ram_vbat_compensate_show, + aw869xx_ram_vbat_compensate_store); +static DEVICE_ATTR(osc_cali, S_IWUSR | S_IRUGO, aw869xx_osc_cali_show, + aw869xx_osc_cali_store); +static struct attribute *aw869xx_vibrator_attributes[] = { + &dev_attr_state.attr, + &dev_attr_duration.attr, + &dev_attr_activate.attr, + &dev_attr_activate_mode.attr, + &dev_attr_index.attr, + &dev_attr_vmax.attr, + &dev_attr_gain.attr, + &dev_attr_seq.attr, + &dev_attr_loop.attr, + &dev_attr_reg.attr, + &dev_attr_rtp.attr, + &dev_attr_ram_update.attr, + &dev_attr_f0.attr, + &dev_attr_f0_save.attr, + &dev_attr_osc_save.attr, + &dev_attr_ram_f0.attr, + &dev_attr_cali.attr, + &dev_attr_cont.attr, + &dev_attr_cont_wait_num.attr, + &dev_attr_cont_drv_lvl.attr, + &dev_attr_cont_drv_time.attr, + &dev_attr_cont_brk_time.attr, + &dev_attr_vbat_monitor.attr, + &dev_attr_lra_resistance.attr, + &dev_attr_auto_boost.attr, + &dev_attr_prctmode.attr, + &dev_attr_trig.attr, + &dev_attr_ram_vbat_comp.attr, + &dev_attr_osc_cali.attr, + NULL +}; + +static struct attribute_group aw869xx_vibrator_attribute_group = { + .attrs = aw869xx_vibrator_attributes +}; + +static enum hrtimer_restart aw869xx_vibrator_timer_func(struct hrtimer *timer) +{ + struct aw869xx *aw869xx = container_of(timer, struct aw869xx, timer); + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + aw869xx->state = 0; + schedule_work(&aw869xx->long_vibrate_work); + + return HRTIMER_NORESTART; +} + +static void aw869xx_long_vibrate_work_routine(struct work_struct *work) +{ + struct aw869xx *aw869xx = container_of(work, struct aw869xx, + long_vibrate_work); + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + + mutex_lock(&aw869xx->lock); + /* Enter standby mode */ + aw869xx_haptic_stop(aw869xx); + aw869xx_haptic_upload_lra(aw869xx, F0_CALI); + if (aw869xx->state) { + if (aw869xx->activate_mode == AW869XX_HAPTIC_ACTIVATE_RAM_MODE) { + aw869xx_haptic_ram_vbat_compensate(aw869xx, true); + aw869xx_haptic_bst_mode_config(aw869xx, + AW869XX_HAPTIC_BST_MODE_BYPASS); + aw869xx_haptic_play_repeat_seq(aw869xx, true); + } else if (aw869xx->activate_mode == + AW869XX_HAPTIC_ACTIVATE_CONT_MODE) { + aw869xx_haptic_cont_config(aw869xx); + } else { + aw_dev_err(aw869xx->dev, "%s: activate_mode error\n", + __func__); + } + /* run ms timer */ + hrtimer_start(&aw869xx->timer, + ktime_set(aw869xx->duration / 1000, + (aw869xx->duration % 1000) * 1000000), + HRTIMER_MODE_REL); + } + mutex_unlock(&aw869xx->lock); +} + +static int aw869xx_vibrator_init(struct aw869xx *aw869xx) +{ + int ret = 0; + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + +#ifdef TIMED_OUTPUT + aw_dev_info(aw869xx->dev, "%s: TIMED_OUT FRAMEWORK!\n", __func__); + aw869xx->vib_dev.name = "vibrator"; + aw869xx->vib_dev.get_time = aw869xx_vibrator_get_time; + aw869xx->vib_dev.enable = aw869xx_vibrator_enable; + + ret = timed_output_dev_register(&(aw869xx->vib_dev)); + if (ret < 0) { + aw_dev_err(aw869xx->dev, + "%s: fail to create timed output dev\n", __func__); + return ret; + } + ret = sysfs_create_group(&aw869xx->vib_dev.dev->kobj, + &aw869xx_vibrator_attribute_group); + if (ret < 0) { + aw_dev_err(aw869xx->dev, "%s error creating sysfs attr files\n", + __func__); + return ret; + } +#else + aw_dev_info(aw869xx->dev, "%s: loaded in leds_cdev framework!\n", + __func__); + aw869xx->vib_dev.name = "vibrator"; + aw869xx->vib_dev.brightness_get = aw869xx_haptic_brightness_get; + aw869xx->vib_dev.brightness_set = aw869xx_haptic_brightness_set; + + ret = devm_led_classdev_register(&aw869xx->i2c->dev, &aw869xx->vib_dev); + if (ret < 0) { + aw_dev_err(aw869xx->dev, "%s: fail to create led dev\n", + __func__); + return ret; + } + ret = sysfs_create_group(&aw869xx->vib_dev.dev->kobj, + &aw869xx_vibrator_attribute_group); + if (ret < 0) { + aw_dev_err(aw869xx->dev, "%s error creating sysfs attr files\n", + __func__); + return ret; + } +#endif + hrtimer_init(&aw869xx->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + aw869xx->timer.function = aw869xx_vibrator_timer_func; + INIT_WORK(&aw869xx->long_vibrate_work, + aw869xx_long_vibrate_work_routine); + INIT_WORK(&aw869xx->rtp_work, aw869xx_rtp_work_routine); + mutex_init(&aw869xx->lock); + mutex_init(&aw869xx->rtp_lock); + + return 0; +} + +/****************************************************** + * + * Digital Audio Interface + * + ******************************************************/ +static int aw869xx_i2s_enable(struct aw869xx *aw869xx, bool flag) +{ + if (flag) { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_I2SCFG2, + AW869XX_BIT_I2SCFG2_I2S_EN_MASK, + AW869XX_BIT_I2SCFG2_I2S_ENABLE); + } else { + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_I2SCFG2, + AW869XX_BIT_I2SCFG2_I2S_EN_MASK, + AW869XX_BIT_I2SCFG2_I2S_DISABLE); + } + return 0; +} + +static int aw869xx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw869xx *aw869xx = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + aw_dev_info(aw869xx->dev, "%s: enter\n", __func__); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + mutex_lock(&aw869xx->lock); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_STANDBY_MASK, + AW869XX_BIT_SYSCTRL2_STANDBY_OFF); + mutex_unlock(&aw869xx->lock); + } + + return 0; +} + +static int aw869xx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + /*struct aw869xx *aw869xx = aw_snd_soc_codec_get_drvdata(dai->codec); */ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + + aw_dev_info(codec->dev, "%s: fmt=0x%02X\n", __func__, fmt); + + /* supported mode: regular I2S, slave, or PDM */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != + SND_SOC_DAIFMT_CBS_CFS) { + aw_dev_err(codec->dev, + "%s: invalid codec master mode\n", __func__); + return -EINVAL; + } + break; + default: + aw_dev_err(codec->dev, "%s: unsupported DAI format %d\n", + __func__, fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + return 0; +} + +static int aw869xx_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, unsigned int freq, int dir) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw869xx *aw869xx = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + aw_dev_info(aw869xx->dev, "%s: freq=%d\n", __func__, freq); + + aw869xx->sysclk = freq; + return 0; +} + +static int aw869xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw869xx *aw869xx = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + uint8_t i2sfs_val = 0; + uint8_t i2sbck_val = 0; + uint8_t tmp_val = 0; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + aw_dev_info(aw869xx->dev, "%s: steam is capture\n", __func__); + return 0; + } + + aw_dev_info(aw869xx->dev, "%s: requested rate: %d, sample size: %d\n", + __func__, params_rate(params), + snd_pcm_format_width(params_format(params))); + + /* get rate param */ + aw869xx->rate = params_rate(params); + + mutex_lock(&aw869xx->lock); + + /* get bit width */ + aw869xx->width = params_width(params); + aw_dev_info(aw869xx->dev, "%s: width = %d\n", __func__, aw869xx->width); + i2sbck_val = params_width(params) * params_channels(params); + /* match bit width */ + switch (aw869xx->width) { + case 16: + i2sfs_val = AW869XX_BIT_I2SCFG1_I2SFS_16BIT; + break; + case 24: + i2sfs_val = AW869XX_BIT_I2SCFG1_I2SFS_24BIT; + i2sbck_val = 32 * params_channels(params); + break; + case 32: + i2sfs_val = AW869XX_BIT_I2SCFG1_I2SFS_32BIT; + break; + default: + i2sfs_val = AW869XX_BIT_I2SCFG1_I2SFS_16BIT; + i2sbck_val = 16 * params_channels(params); + aw_dev_err(aw869xx->dev, "%s: width [%d]can not support\n", + __func__, aw869xx->width); + break; + } + aw_dev_info(aw869xx->dev, "%s: i2sfs_val=0x%02X\n", __func__, + i2sfs_val); + /* set width */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_I2SCFG1, + AW869XX_BIT_I2SCFG1_I2SFS_MASK, i2sfs_val); + + /* match bck mode */ + switch (i2sbck_val) { + case 32: + i2sbck_val = AW869XX_BIT_I2SCFG1_I2SBCK_32FS; + break; + case 48: + i2sbck_val = AW869XX_BIT_I2SCFG1_I2SBCK_48FS; + break; + case 64: + i2sbck_val = AW869XX_BIT_I2SCFG1_I2SBCK_64FS; + break; + default: + aw_dev_err(aw869xx->dev, "%s: i2sbck [%d] can not support\n", + __func__, i2sbck_val); + i2sbck_val = AW869XX_BIT_I2SCFG1_I2SBCK_32FS; + break; + } + /* set bck mode */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_I2SCFG1, + AW869XX_BIT_I2SCFG1_I2SBCK_MASK, i2sbck_val); + + /* check i2s cfg */ + aw869xx_i2c_read(aw869xx, AW869XX_REG_I2SCFG1, &tmp_val); + aw_dev_info(aw869xx->dev, "%s: i2scfg1=0x%02X\n", __func__, tmp_val); + + mutex_unlock(&aw869xx->lock); + + return 0; +} + +static int aw869xx_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw869xx *aw869xx = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + uint8_t reg_val = 0; + + if (stream == SNDRV_PCM_STREAM_CAPTURE) { + aw_dev_info(aw869xx->dev, "%s: steam is capture\n", __func__); + return 0; + } + + aw_dev_info(aw869xx->dev, "%s: mute state=%d\n", __func__, mute); + + if (mute) { + mutex_lock(&aw869xx->lock); + aw869xx_i2s_enable(aw869xx, false); + mutex_unlock(&aw869xx->lock); + } else { + mutex_lock(&aw869xx->lock); + aw869xx_i2s_enable(aw869xx, true); + usleep_range(2000, 3000); + aw869xx_i2c_read(aw869xx, AW869XX_REG_SYSST, ®_val); + aw_dev_info(aw869xx->dev, "%s: sysst=0x%02X\n", __func__, + reg_val); + aw869xx_i2c_read(aw869xx, AW869XX_REG_SYSST2, ®_val); + aw_dev_info(aw869xx->dev, "%s: sysst2=0x%02X\n", __func__, + reg_val); + aw869xx_i2c_read(aw869xx, AW869XX_REG_SYSER, ®_val); + aw_dev_info(aw869xx->dev, "%s: syser=0x%02X\n", __func__, + reg_val); + aw869xx_i2c_read(aw869xx, AW869XX_REG_GLBRD5, ®_val); + if (reg_val != 0x0a) { + aw_dev_err(aw869xx->dev, + "%s: i2s config error, glb_state=0x%02X\n", + __func__, reg_val); + aw869xx_i2s_enable(aw869xx, false); + } else { + aw_dev_info(aw869xx->dev, + "%s: i2s config pass, glb_state=0x%02X\n", + __func__, reg_val); + } + mutex_unlock(&aw869xx->lock); + } + + return 0; +} + +static void aw869xx_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw869xx *aw869xx = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + aw_dev_info(aw869xx->dev, "%s: enter\n", __func__); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aw869xx->rate = 0; + mutex_lock(&aw869xx->lock); + /* aw869xx_i2s_enable(aw869xx, false); */ + mutex_unlock(&aw869xx->lock); + } +} + +static const struct snd_soc_dai_ops aw869xx_dai_ops = { + .startup = aw869xx_startup, + .set_fmt = aw869xx_set_fmt, + .set_sysclk = aw869xx_set_dai_sysclk, + .hw_params = aw869xx_hw_params, + .mute_stream = aw869xx_mute, + .shutdown = aw869xx_shutdown, +}; + +static struct snd_soc_dai_driver aw869xx_dai[] = { + { + .name = "aw869xx-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AW869XX_RATES, + .formats = AW869XX_FORMATS, + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AW869XX_RATES, + .formats = AW869XX_FORMATS, + }, + .ops = &aw869xx_dai_ops, + .symmetric_rates = 1, + }, +}; + +/***************************************************** + * + * codec driver + * + *****************************************************/ +static void aw869xx_add_codec_controls(struct aw869xx *aw869xx) +{ + aw_dev_info(aw869xx->dev, "%s: enter\n", __func__); +} + +static int aw869xx_probe(aw_snd_soc_codec_t *codec) +{ + struct aw869xx *aw869xx = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + int ret = -1; + + aw_dev_info(aw869xx->dev, "%s: enter\n", __func__); + + aw869xx->codec = codec; + + aw869xx_add_codec_controls(aw869xx); + + aw_dev_info(aw869xx->dev, "%s: exit\n", __func__); + + ret = 0; + return ret; +} + +#ifdef AW_KERNEL_VER_OVER_4_19 +static void aw869xx_remove(struct snd_soc_component *component) +{ + /*struct aw869xx *aw869xx = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); */ + aw_dev_info(component->dev, "%s: enter\n", __func__); + + return; +} +#else +static int aw869xx_remove(aw_snd_soc_codec_t *codec) +{ + /* struct aw869xx *aw869xx = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); */ + aw_dev_info(codec->dev, "%s: enter\n", __func__); + + return 0; +} +#endif + +static unsigned int aw869xx_codec_read(aw_snd_soc_codec_t *codec, + unsigned int reg) +{ + struct aw869xx *aw869xx = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + uint8_t value = 0; + int ret = -1; + + aw_dev_info(aw869xx->dev, "%s: enter\n", __func__); + + ret = aw869xx_i2c_read(aw869xx, reg, &value); + if (ret < 0) { + aw_dev_err(aw869xx->dev, "%s: read register failed\n", + __func__); + return ret; + } + return ret; +} + +static int aw869xx_codec_write(aw_snd_soc_codec_t *codec, + unsigned int reg, unsigned int value) +{ + int ret = -1; + + struct aw869xx *aw869xx = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + aw_dev_info(aw869xx->dev, "%s: enter ,reg:0x%02X = 0x%02X\n", + __func__, reg, value); + + ret = aw869xx_i2c_write(aw869xx, (uint8_t) reg, (uint8_t) value); + + return ret; +} + +#ifdef AW_KERNEL_VER_OVER_4_19 +static struct snd_soc_component_driver soc_codec_dev_aw869xx = { + .probe = aw869xx_probe, + .remove = aw869xx_remove, + .read = aw869xx_codec_read, + .write = aw869xx_codec_write, +}; +#else +static struct snd_soc_codec_driver soc_codec_dev_aw869xx = { + .probe = aw869xx_probe, + .remove = aw869xx_remove, + .read = aw869xx_codec_read, + .write = aw869xx_codec_write, + .reg_cache_size = AW869XX_REG_MAX, + .reg_word_size = 2, +}; +#endif +/****************************************************** + * + * irq + * + ******************************************************/ +static void aw869xx_interrupt_clear(struct aw869xx *aw869xx) +{ + unsigned char reg_val = 0; + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + aw869xx_i2c_read(aw869xx, AW869XX_REG_SYSINT, ®_val); + aw_dev_dbg(aw869xx->dev, "%s: reg SYSINT=0x%02X\n", __func__, reg_val); +} + +static void aw869xx_interrupt_setup(struct aw869xx *aw869xx) +{ + unsigned char reg_val = 0; + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + + aw869xx_i2c_read(aw869xx, AW869XX_REG_SYSINT, ®_val); + + aw_dev_info(aw869xx->dev, "%s: reg SYSINT=0x%02X\n", __func__, reg_val); + + /* edge int mode */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL7, + AW869XX_BIT_SYSCTRL7_INT_MODE_MASK, + AW869XX_BIT_SYSCTRL7_INT_MODE_EDGE); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSCTRL7, + AW869XX_BIT_SYSCTRL7_INT_EDGE_MODE_MASK, + AW869XX_BIT_SYSCTRL7_INT_EDGE_MODE_POS); + /* int enable */ + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSINTM, + AW869XX_BIT_SYSINTM_BST_SCPM_MASK, + AW869XX_BIT_SYSINTM_BST_SCPM_OFF); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSINTM, + AW869XX_BIT_SYSINTM_BST_OVPM_MASK, + AW869XX_BIT_SYSINTM_BST_OVPM_ON); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSINTM, + AW869XX_BIT_SYSINTM_UVLM_MASK, + AW869XX_BIT_SYSINTM_UVLM_ON); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSINTM, + AW869XX_BIT_SYSINTM_OCDM_MASK, + AW869XX_BIT_SYSINTM_OCDM_ON); + aw869xx_i2c_write_bits(aw869xx, AW869XX_REG_SYSINTM, + AW869XX_BIT_SYSINTM_OTM_MASK, + AW869XX_BIT_SYSINTM_OTM_ON); +} + +static irqreturn_t aw869xx_irq(int irq, void *data) +{ + struct aw869xx *aw869xx = data; + unsigned char reg_val = 0; + unsigned int buf_len = 0; + unsigned char glb_state_val = 0; + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + aw869xx_i2c_read(aw869xx, AW869XX_REG_SYSINT, ®_val); + aw_dev_info(aw869xx->dev, "%s: reg SYSINT=0x%02X\n", __func__, reg_val); + if (reg_val & AW869XX_BIT_SYSINT_BST_OVPI) + aw_dev_err(aw869xx->dev, "%s chip ov int error\n", __func__); + if (reg_val & AW869XX_BIT_SYSINT_UVLI) + aw_dev_err(aw869xx->dev, "%s chip uvlo int error\n", __func__); + if (reg_val & AW869XX_BIT_SYSINT_OCDI) + aw_dev_err(aw869xx->dev, "%s chip over current int error\n", + __func__); + if (reg_val & AW869XX_BIT_SYSINT_OTI) + aw_dev_err(aw869xx->dev, "%s chip over temperature int error\n", + __func__); + if (reg_val & AW869XX_BIT_SYSINT_DONEI) + aw_dev_info(aw869xx->dev, "%s chip playback done\n", __func__); + + if (reg_val & AW869XX_BIT_SYSINT_FF_AEI) { + aw_dev_info(aw869xx->dev, "%s: aw869xx rtp fifo almost empty\n", + __func__); + if (aw869xx->rtp_init) { + while ((!aw869xx_haptic_rtp_get_fifo_afs(aw869xx)) && + (aw869xx->play_mode == + AW869XX_HAPTIC_RTP_MODE)) { + mutex_lock(&aw869xx->rtp_lock); + aw_dev_info(aw869xx->dev, + "%s: aw869xx rtp mode fifo update, cnt=%d\n", + __func__, aw869xx->rtp_cnt); + if (!aw869xx_rtp) { + pr_info + ("%s:aw869xx_rtp is null, break!\n", + __func__); + mutex_unlock(&aw869xx->rtp_lock); + break; + } + if ((aw869xx_rtp->len - aw869xx->rtp_cnt) < + (aw869xx->ram.base_addr >> 2)) { + buf_len = + aw869xx_rtp->len - aw869xx->rtp_cnt; + } else { + buf_len = (aw869xx->ram.base_addr >> 2); + } + aw869xx->rtp_update_flag = + aw869xx_i2c_writes(aw869xx, + AW869XX_REG_RTPDATA, + &aw869xx_rtp->data + [aw869xx->rtp_cnt], + buf_len); + aw869xx->rtp_cnt += buf_len; + aw869xx_i2c_read(aw869xx, AW869XX_REG_GLBRD5, + &glb_state_val); + if ((aw869xx->rtp_cnt == aw869xx_rtp->len) + || ((glb_state_val & 0x0f) == 0)) { + aw_dev_info(aw869xx->dev, + "%s: rtp update complete\n", + __func__); + aw869xx_haptic_set_rtp_aei(aw869xx, + false); + aw869xx->rtp_cnt = 0; + aw869xx->rtp_init = 0; + mutex_unlock(&aw869xx->rtp_lock); + break; + } + mutex_unlock(&aw869xx->rtp_lock); + } + } else { + aw_dev_info(aw869xx->dev, + "%s: aw869xx rtp init = %d, init error\n", + __func__, aw869xx->rtp_init); + } + } + + if (reg_val & AW869XX_BIT_SYSINT_FF_AFI) + aw_dev_info(aw869xx->dev, + "%s: aw869xx rtp mode fifo almost full!\n", + __func__); + + if (aw869xx->play_mode != AW869XX_HAPTIC_RTP_MODE) + aw869xx_haptic_set_rtp_aei(aw869xx, false); + + aw_dev_info(aw869xx->dev, "%s exit\n", __func__); + + return IRQ_HANDLED; +} + +/***************************************************** + * + * device tree + * + *****************************************************/ +static int aw869xx_parse_dt(struct device *dev, struct aw869xx *aw869xx, + struct device_node *np) +{ + unsigned int val = 0; + unsigned int bstcfg_temp[5] = { 0x2a, 0x24, 0x9a, 0x40, 0x91 }; + unsigned int prctmode_temp[3]; + unsigned int sine_array_temp[4] = { 0x05, 0xB2, 0xFF, 0xEF }; + unsigned int trig_config_temp[24] = { 1, 0, 1, 1, 1, 2, 0, 0, + 1, 0, 0, 1, 0, 2, 0, 0, + 1, 0, 0, 1, 0, 2, 0, 0 + }; + + aw869xx->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (aw869xx->reset_gpio < 0) { + aw_dev_err(dev, "%s: no reset gpio provide\n", __func__); + return -1; + } else { + dev_info(dev, "%s: reset gpio provide ok\n", __func__); + } + aw869xx->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (aw869xx->irq_gpio < 0) + aw_dev_err(dev, "%s: no irq gpio provide.\n", __func__); + else + dev_info(dev, "%s: irq gpio provide ok.\n", __func__); + + val = of_property_read_u32(np, "vib_mode", &aw869xx->dts_info.mode); + if (val != 0) + aw_dev_info(aw869xx->dev, "%s vib_mode not found\n", __func__); + val = of_property_read_u32(np, "vib_f0_ref", &aw869xx->dts_info.f0_ref); + if (val != 0) + aw_dev_info(aw869xx->dev, "%s vib_f0_ref not found\n", + __func__); + val = + of_property_read_u32(np, "vib_f0_cali_percent", + &aw869xx->dts_info.f0_cali_percent); + if (val != 0) + aw_dev_info(aw869xx->dev, "%s vib_f0_cali_percent not found\n", + __func__); + + val = of_property_read_u32(np, "vib_cont_drv1_lvl", + &aw869xx->dts_info.cont_drv1_lvl_dt); + if (val != 0) + aw_dev_info(aw869xx->dev, "%s vib_cont_drv1_lvl not found\n", + __func__); + val = + of_property_read_u32(np, "vib_cont_drv2_lvl", + &aw869xx->dts_info.cont_drv2_lvl_dt); + if (val != 0) + aw_dev_info(aw869xx->dev, "%s vib_cont_drv2_lvl not found\n", + __func__); + val = + of_property_read_u32(np, "vib_cont_drv1_time", + &aw869xx->dts_info.cont_drv1_time_dt); + if (val != 0) + aw_dev_info(aw869xx->dev, "%s vib_cont_drv1_time not found\n", + __func__); + val = + of_property_read_u32(np, "vib_cont_drv2_time", + &aw869xx->dts_info.cont_drv2_time_dt); + if (val != 0) + aw_dev_info(aw869xx->dev, "%s vib_cont_drv2_time not found\n", + __func__); + val = + of_property_read_u32(np, "vib_cont_drv_width", + &aw869xx->dts_info.cont_drv_width); + if (val != 0) + aw_dev_info(aw869xx->dev, "%s vib_cont_drv_width not found\n", + __func__); + val = + of_property_read_u32(np, "vib_cont_wait_num", + &aw869xx->dts_info.cont_wait_num_dt); + if (val != 0) + aw_dev_info(aw869xx->dev, "%s vib_cont_wait_num not found\n", + __func__); + val = + of_property_read_u32(np, "vib_cont_bst_brk_gain", + &aw869xx->dts_info.cont_bst_brk_gain); + if (val != 0) + aw_dev_info(aw869xx->dev, + "%s vib_cont_bst_brk_gain not found\n", __func__); + val = + of_property_read_u32(np, "vib_cont_brk_gain", + &aw869xx->dts_info.cont_brk_gain); + if (val != 0) + aw_dev_info(aw869xx->dev, "%s vib_cont_brk_gain not found\n", + __func__); + val = + of_property_read_u32(np, "vib_cont_tset", + &aw869xx->dts_info.cont_tset); + if (val != 0) + aw_dev_info(aw869xx->dev, "%s vib_cont_tset not found\n", + __func__); + val = + of_property_read_u32(np, "vib_cont_bemf_set", + &aw869xx->dts_info.cont_bemf_set); + if (val != 0) + aw_dev_info(aw869xx->dev, "%s vib_cont_bemf_set not found\n", + __func__); + val = + of_property_read_u32(np, "vib_d2s_gain", + &aw869xx->dts_info.d2s_gain); + if (val != 0) + aw_dev_info(aw869xx->dev, "%s vib_d2s_gain not found\n", + __func__); + val = + of_property_read_u32(np, "vib_cont_brk_time", + &aw869xx->dts_info.cont_brk_time_dt); + if (val != 0) + aw_dev_info(aw869xx->dev, "%s vib_cont_brk_time not found\n", + __func__); + val = + of_property_read_u32(np, "vib_cont_track_margin", + &aw869xx->dts_info.cont_track_margin); + if (val != 0) + aw_dev_info(aw869xx->dev, + "%s vib_cont_track_margin not found\n", __func__); + aw869xx->dts_info.is_enabled_auto_bst = + of_property_read_bool(np, "vib_is_enabled_auto_bst"); + aw_dev_info(aw869xx->dev, + "%s aw869xx->dts_info.is_enabled_auto_bst = %d\n", __func__, + aw869xx->dts_info.is_enabled_auto_bst); + aw869xx->dts_info.is_enabled_i2s = + of_property_read_bool(np, "vib_is_enabled_i2s"); + aw_dev_info(aw869xx->dev, "%s aw869xx->dts_info.is_enabled_i2s = %d\n", + __func__, aw869xx->dts_info.is_enabled_i2s); + aw869xx->dts_info.is_enabled_powerup_f0_cali = + of_property_read_bool(np, "vib_is_enabled_powerup_f0_cali"); + aw_dev_info(aw869xx->dev, + "%s aw869xx->dts_info.is_enabled_powerup_f0_cali = %d\n", + __func__, aw869xx->dts_info.is_enabled_powerup_f0_cali); + val = + of_property_read_u32_array(np, "vib_bstcfg", bstcfg_temp, + ARRAY_SIZE(bstcfg_temp)); + if (val != 0) + aw_dev_info(aw869xx->dev, "%s vib_bstcfg not found\n", + __func__); + memcpy(aw869xx->dts_info.bstcfg, bstcfg_temp, sizeof(bstcfg_temp)); + val = of_property_read_u32_array(np, "vib_prctmode", + prctmode_temp, + ARRAY_SIZE(prctmode_temp)); + if (val != 0) + aw_dev_info(aw869xx->dev, "%s vib_prctmode not found\n", + __func__); + memcpy(aw869xx->dts_info.prctmode, prctmode_temp, + sizeof(prctmode_temp)); + val = + of_property_read_u32_array(np, "vib_sine_array", sine_array_temp, + ARRAY_SIZE(sine_array_temp)); + if (val != 0) + aw_dev_info(aw869xx->dev, "%s vib_sine_array not found\n", + __func__); + memcpy(aw869xx->dts_info.sine_array, sine_array_temp, + sizeof(sine_array_temp)); + val = + of_property_read_u32_array(np, "vib_trig_config", trig_config_temp, + ARRAY_SIZE(trig_config_temp)); + if (val != 0) + aw_dev_info(aw869xx->dev, "%s vib_trig_config not found\n", + __func__); + memcpy(aw869xx->dts_info.trig_config, trig_config_temp, + sizeof(trig_config_temp)); + + return 0; +} + +static int aw869xx_hw_reset(struct aw869xx *aw869xx) +{ + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + + if (aw869xx && gpio_is_valid(aw869xx->reset_gpio)) { + gpio_set_value_cansleep(aw869xx->reset_gpio, 0); + usleep_range(1000, 2000); + gpio_set_value_cansleep(aw869xx->reset_gpio, 1); + usleep_range(3500, 4000); + } else { + aw_dev_err(aw869xx->dev, "%s: failed\n", __func__); + } + + return 0; +} + +/***************************************************** + * + * check chip id + * + *****************************************************/ +static int aw869xx_read_chipid(struct aw869xx *aw869xx) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char reg = 0; + + while (cnt < AW_READ_CHIPID_RETRIES) { + /* hardware reset */ + aw869xx_hw_reset(aw869xx); + /* aw869xx_haptic_softreset(aw869xx); */ + ret = aw869xx_i2c_read(aw869xx, AW869XX_REG_ID, ®); + if (ret < 0) { + aw_dev_err(aw869xx->dev, + "%s: failed to read AW869XX_REG_ID: %d\n", + __func__, ret); + } + switch (reg) { + case AW86905_CHIPID: + aw_dev_info(aw869xx->dev, + "%s detected aw86905, ChipID: 0x%02X\n", + __func__, reg); + aw869xx->chipid = AW86905_CHIPID; + aw869xx->bst_pc = AW869XX_HAPTIC_BST_PC_L1; + aw869xx->dts_info.is_enabled_i2s = false; + /* aw869xx->flags |= AW869XX_FLAG_SKIP_INTERRUPTS; */ + return 0; + case AW86907_CHIPID: + aw_dev_info(aw869xx->dev, + "%s detected aw86907, ChipID: 0x%02X\n", + __func__, reg); + aw869xx->chipid = AW86907_CHIPID; + aw869xx->bst_pc = AW869XX_HAPTIC_BST_PC_L2; + aw869xx->dts_info.is_enabled_i2s = false; + /* aw869xx->flags |= AW869XX_FLAG_SKIP_INTERRUPTS; */ + return 0; + case AW86915_CHIPID: + aw_dev_info(aw869xx->dev, + "%s detected aw86915, ChipID: 0x%02X\n", + __func__, reg); + aw869xx->chipid = AW86915_CHIPID; + aw869xx->bst_pc = AW869XX_HAPTIC_BST_PC_L1; + /* aw869xx->flags |= AW869XX_FLAG_SKIP_INTERRUPTS; */ + return 0; + case AW86917_CHIPID: + aw_dev_info(aw869xx->dev, + "%s detected aw86917, ChipID: 0x%02X\n", + __func__, reg); + aw869xx->chipid = AW86917_CHIPID; + aw869xx->bst_pc = AW869XX_HAPTIC_BST_PC_L2; + /* aw869xx->flags |= AW869XX_FLAG_SKIP_INTERRUPTS; */ + return 0; + default: + aw_dev_info(aw869xx->dev, + "%s unsupported device revision (0x%02X)\n", + __func__, reg); + break; + } + cnt++; + usleep_range(AW_READ_CHIPID_RETRY_DELAY * 1000, + AW_READ_CHIPID_RETRY_DELAY * 1000 + 500); + } + return -EINVAL; +} + +/****************************************************** + * + * i2c driver + * + ******************************************************/ +static int aw869xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct aw869xx *aw869xx; + struct device_node *np = i2c->dev.of_node; + struct snd_soc_dai_driver *dai; + int irq_flags = 0; + int ret = -1; + unsigned char reg = 0; + + aw_dev_info(&i2c->dev, "%s enter\n", __func__); + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + aw_dev_err(&i2c->dev, "check_functionality failed\n"); + return -EIO; + } + aw869xx = devm_kzalloc(&i2c->dev, sizeof(struct aw869xx), GFP_KERNEL); + if (aw869xx == NULL) + return -ENOMEM; + aw869xx->dev = &i2c->dev; + aw869xx->i2c = i2c; + i2c_set_clientdata(i2c, aw869xx); + /* aw869xx rst & int */ + if (np) { + ret = aw869xx_parse_dt(&i2c->dev, aw869xx, np); + if (ret) { + aw_dev_err(&i2c->dev, + "%s: failed to parse device tree node\n", + __func__); + goto err_parse_dt; + } + } else { + aw869xx->reset_gpio = -1; + aw869xx->irq_gpio = -1; + } + if (gpio_is_valid(aw869xx->reset_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw869xx->reset_gpio, + GPIOF_OUT_INIT_LOW, "aw869xx_rst"); + if (ret) { + aw_dev_err(&i2c->dev, "%s: rst request failed\n", + __func__); + goto err_reset_gpio_request; + } + } + if (gpio_is_valid(aw869xx->irq_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw869xx->irq_gpio, + GPIOF_DIR_IN, "aw869xx_int"); + if (ret) { + aw_dev_err(&i2c->dev, "%s: int request failed\n", + __func__); + goto err_irq_gpio_request; + } + } + /* aw869xx chip id */ + ret = aw869xx_read_chipid(aw869xx); + if (ret < 0) { + aw_dev_err(&i2c->dev, "%s: aw869xx_read_chipid failed ret=%d\n", + __func__, ret); + goto err_id; + } + /* chip qualify */ + ret = aw869xx_i2c_read(aw869xx, 0x64, ®); + if (ret < 0) { + aw_dev_err(&i2c->dev, + "%s: failed to read register 0x64: %d\n", + __func__, ret); + } + if (!(reg & 0x80)) { + aw_dev_err(&i2c->dev, "%s:unqualified chip!\n", __func__); + goto err_qualify; + } + + /* register codec */ + dai = devm_kzalloc(&i2c->dev, sizeof(aw869xx_dai), GFP_KERNEL); + if (!dai) + goto err_dai_kzalloc; + + memcpy(dai, aw869xx_dai, sizeof(aw869xx_dai)); + aw_dev_info(&i2c->dev, "%s: dai->name(%s)\n", __func__, dai->name); + + ret = aw_componet_codec_ops.aw_snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_aw869xx, + dai, + ARRAY_SIZE + (aw869xx_dai)); + if (ret < 0) { + aw_dev_err(&i2c->dev, "%s failed to register aw869xx: %d\n", + __func__, ret); + goto err_register_codec; + } + /* aw869xx irq */ + if (gpio_is_valid(aw869xx->irq_gpio) && + !(aw869xx->flags & AW869XX_FLAG_SKIP_INTERRUPTS)) { + /* register irq handler */ + aw869xx_interrupt_setup(aw869xx); + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, + gpio_to_irq(aw869xx->irq_gpio), + NULL, aw869xx_irq, irq_flags, + "aw869xx", aw869xx); + if (ret != 0) { + aw_dev_err(&i2c->dev, + "%s: failed to request IRQ %d: %d\n", + __func__, gpio_to_irq(aw869xx->irq_gpio), + ret); + goto err_irq; + } + } else { + dev_info(&i2c->dev, "%s skipping IRQ registration\n", __func__); + /* disable feature support if gpio was invalid */ + aw869xx->flags |= AW869XX_FLAG_SKIP_INTERRUPTS; + } + dev_set_drvdata(&i2c->dev, aw869xx); + aw869xx_vibrator_init(aw869xx); + aw869xx_haptic_init(aw869xx); + aw869xx_ram_work_init(aw869xx); + aw_dev_info(&i2c->dev, "%s probe completed successfully!\n", __func__); + return 0; + err_irq: + aw_componet_codec_ops.aw_snd_soc_unregister_codec(&i2c->dev); + err_register_codec: + devm_kfree(&i2c->dev, dai); + dai = NULL; + err_dai_kzalloc: + err_qualify: + err_id: + if (gpio_is_valid(aw869xx->irq_gpio)) + devm_gpio_free(&i2c->dev, aw869xx->irq_gpio); + err_irq_gpio_request: + if (gpio_is_valid(aw869xx->reset_gpio)) + devm_gpio_free(&i2c->dev, aw869xx->reset_gpio); + err_reset_gpio_request: + err_parse_dt: + devm_kfree(&i2c->dev, aw869xx); + aw869xx = NULL; + return ret; +} + +static int aw869xx_i2c_remove(struct i2c_client *i2c) +{ + struct aw869xx *aw869xx = i2c_get_clientdata(i2c); + + aw_dev_info(aw869xx->dev, "%s enter\n", __func__); + devm_free_irq(&i2c->dev, gpio_to_irq(aw869xx->irq_gpio), aw869xx); + if (gpio_is_valid(aw869xx->irq_gpio)) + devm_gpio_free(&i2c->dev, aw869xx->irq_gpio); + + aw_componet_codec_ops.aw_snd_soc_unregister_codec(&i2c->dev); + + if (gpio_is_valid(aw869xx->reset_gpio)) + devm_gpio_free(&i2c->dev, aw869xx->reset_gpio); + devm_kfree(&i2c->dev, aw869xx); + aw869xx = NULL; + + return 0; +} + +static int __maybe_unused aw869xx_suspend(struct device *dev) +{ + int ret = 0; + struct aw869xx *aw869xx = dev_get_drvdata(dev); + + mutex_lock(&aw869xx->lock); + aw869xx_haptic_stop(aw869xx); + mutex_unlock(&aw869xx->lock); + + return ret; +} + +static int __maybe_unused aw869xx_resume(struct device *dev) +{ + int ret = 0; + return ret; +} + +// static SIMPLE_DEV_PM_OPS(aw869xx_pm_ops, aw869xx_suspend, aw869xx_resume); + +static const struct i2c_device_id aw869xx_i2c_id[] = { + {AW869XX_I2C_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, aw869xx_i2c_id); + +static const struct of_device_id aw869xx_dt_match[] = { + {.compatible = "awinic,awinic_haptic"}, + {}, +}; + +static struct i2c_driver aw869xx_i2c_driver = { + .driver = { + .name = AW869XX_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw869xx_dt_match), + }, + .probe = aw869xx_i2c_probe, + .remove = aw869xx_i2c_remove, + .id_table = aw869xx_i2c_id, +}; + +static int __init aw869xx_i2c_init(void) +{ + int ret = 0; + + pr_info("aw869xx driver version %s\n", AW869XX_VERSION); + ret = i2c_add_driver(&aw869xx_i2c_driver); + if (ret) { + pr_err("fail to add aw869xx device into i2c\n"); + return ret; + } + return 0; +} + +module_init(aw869xx_i2c_init); + +static void __exit aw869xx_i2c_exit(void) +{ + i2c_del_driver(&aw869xx_i2c_driver); +} + +module_exit(aw869xx_i2c_exit); + +MODULE_DESCRIPTION("AW869XX Haptic Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/awinic/aw869xx/aw869xx.h b/drivers/misc/awinic/aw869xx/aw869xx.h new file mode 100644 index 000000000000..7fbcffd19139 --- /dev/null +++ b/drivers/misc/awinic/aw869xx/aw869xx.h @@ -0,0 +1,357 @@ +/********************************************************* + * + * aw869xx.h + * + ********************************************************/ +#ifndef _AW869XX_H_ +#define _AW869XX_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +/********************************************************* + * + * Conditional Compilation Marco + * + ********************************************************/ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 4, 1) +#define TIMED_OUTPUT +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 1) +#define AW_KERNEL_VER_OVER_4_19 +#endif + +#ifdef AW_KERNEL_VER_OVER_4_19 +typedef struct snd_soc_component aw_snd_soc_codec_t; +typedef struct snd_soc_component_driver aw_snd_soc_codec_driver_t; +#else +typedef struct snd_soc_codec aw_snd_soc_codec_t; +typedef struct snd_soc_codec_driver aw_snd_soc_codec_driver_t; +#endif + +struct aw_componet_codec_ops { + aw_snd_soc_codec_t *(*aw_snd_soc_kcontrol_codec) (struct snd_kcontrol *kcontrol); + void *(*aw_snd_soc_codec_get_drvdata) (aw_snd_soc_codec_t *codec); + int (*aw_snd_soc_add_codec_controls) (aw_snd_soc_codec_t *codec, + const struct snd_kcontrol_new *controls, + unsigned int num_controls); + void (*aw_snd_soc_unregister_codec) (struct device *dev); + int (*aw_snd_soc_register_codec) (struct device *dev, + const aw_snd_soc_codec_driver_t *codec_drv, + struct snd_soc_dai_driver *dai_drv, + int num_dai); +}; + +#ifdef TIMED_OUTPUT +#include <../../../drivers/staging/android/timed_output.h> +typedef struct timed_output_dev cdev_t; +#else +typedef struct led_classdev cdev_t; +#endif + +/* #define AW_F0_COARSE_CALI */ +/* #define AW_OSC_COARSE_CALI */ +/* #define AW_RAM_STATE_OUTPUT */ +/* #define AW_CHECK_RAM_DATA */ +#define AW_READ_BIN_FLEXBALLY +/********************************************************* + * + * Normal Marco + * + ********************************************************/ +#define AW86905_CHIPID 0x05 +#define AW86907_CHIPID 0x04 +#define AW86915_CHIPID 0x07 +#define AW86917_CHIPID 0x06 +#define AW869XX_I2C_NAME "awinic_haptic" +#define AW869XX_DEV_NAME "aw869xx_smartpa" +#define AW869XX_RATES SNDRV_PCM_RATE_8000_48000 +#define AW869XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) +#define AW_I2C_RETRIES 5 +#define AW_I2C_RETRY_DELAY 2 +#define AW_READ_CHIPID_RETRIES 5 +#define AW_READ_CHIPID_RETRY_DELAY 2 +#define OSC_CALI_MAX_LENGTH 5100000 +#define PM_QOS_VALUE_VB 400 +#define AW869XX_RTP_NAME_MAX 64 +#define AW869XX_SEQUENCER_SIZE 8 +#define AW869XX_SEQUENCER_LOOP_SIZE 4 +#define AW869XX_VBAT_REFER 4200 +#define AW869XX_VBAT_MIN 3000 +#define AW869XX_VBAT_MAX 5500 +#define AW869XX_FIFO_SIZE 2048 +#define AE_THRESHOLD 1024 +#define AF_THRESHOLD 1536 +#define AW869XX_TRIG_NUM 3 + +/******************************************** + * print information control + *******************************************/ +#define aw_dev_err(dev, format, ...) \ + pr_err("[%s]" format, dev_name(dev), ##__VA_ARGS__) + +#define aw_dev_info(dev, format, ...) \ + pr_info("[%s]" format, dev_name(dev), ##__VA_ARGS__) + +#define aw_dev_dbg(dev, format, ...) \ + pr_debug("[%s]" format, dev_name(dev), ##__VA_ARGS__) + +/********************************************************* + * + * Enum Define + * + ********************************************************/ +enum aw869xx_flags { + AW869XX_FLAG_NONR = 0, + AW869XX_FLAG_SKIP_INTERRUPTS = 1, +}; + +enum aw869xx_haptic_read_write { + AW869XX_HAPTIC_CMD_READ_REG = 0, + AW869XX_HAPTIC_CMD_WRITE_REG = 1, +}; + +enum aw869xx_haptic_work_mode { + AW869XX_HAPTIC_STANDBY_MODE = 0, + AW869XX_HAPTIC_RAM_MODE = 1, + AW869XX_HAPTIC_RTP_MODE = 2, + AW869XX_HAPTIC_TRIG_MODE = 3, + AW869XX_HAPTIC_CONT_MODE = 4, + AW869XX_HAPTIC_RAM_LOOP_MODE = 5, +}; + +enum aw869xx_haptic_bst_pc { + AW869XX_HAPTIC_BST_PC_L1 = 0, + AW869XX_HAPTIC_BST_PC_L2 = 1, +}; + +enum aw869xx_haptic_activate_mode { + AW869XX_HAPTIC_ACTIVATE_RAM_MODE = 0, + AW869XX_HAPTIC_ACTIVATE_CONT_MODE = 1, +}; + +enum aw869xx_haptic_cont_vbat_comp_mode { + AW869XX_HAPTIC_CONT_VBAT_SW_ADJUST_MODE = 0, + AW869XX_HAPTIC_CONT_VBAT_HW_ADJUST_MODE = 1, +}; + +enum aw869xx_haptic_ram_vbat_compensate_mode { + AW869XX_HAPTIC_RAM_VBAT_COMP_DISABLE = 0, + AW869XX_HAPTIC_RAM_VBAT_COMP_ENABLE = 1, +}; + +enum aw869xx_haptic_f0_flag { + AW869XX_HAPTIC_LRA_F0 = 0, + AW869XX_HAPTIC_CALI_F0 = 1, +}; + +enum aw869xx_haptic_pwm_mode { + AW869XX_PWM_48K = 0, + AW869XX_PWM_24K = 1, + AW869XX_PWM_12K = 2, +}; + +enum aw869xx_haptic_play { + AW869XX_HAPTIC_PLAY_NULL = 0, + AW869XX_HAPTIC_PLAY_ENABLE = 1, + AW869XX_HAPTIC_PLAY_STOP = 2, + AW869XX_HAPTIC_PLAY_GAIN = 8, +}; + +enum aw869xx_haptic_cmd { + AW869XX_HAPTIC_CMD_NULL = 0, + AW869XX_HAPTIC_CMD_ENABLE = 1, + AW869XX_HAPTIC_CMD_HAPTIC = 0x0f, + AW869XX_HAPTIC_CMD_TP = 0x10, + AW869XX_HAPTIC_CMD_SYS = 0xf0, + AW869XX_HAPTIC_CMD_STOP = 255, +}; + +enum aw869xx_haptic_bst_mode { + AW869XX_HAPTIC_BST_MODE_BYPASS = 0, + AW869XX_HAPTIC_BST_MODE_BOOST = 1, +}; + +enum aw869xx_haptic_cali_lra { + WRITE_ZERO = 0, + F0_CALI = 1, + OSC_CALI = 2, +}; + +/********************************************************* + * + * Struct Define + * + ********************************************************/ +struct ram { + unsigned int len; + unsigned int check_sum; + unsigned int base_addr; + unsigned char version; + unsigned char ram_shift; + unsigned char baseaddr_shift; +}; + +struct haptic_ctr { + unsigned char cnt; + unsigned char cmd; + unsigned char play; + unsigned char wavseq; + unsigned char loop; + unsigned char gain; + struct list_head list; +}; + +/* trig_config + * trig default high level + * ___________ ___________ + * | | + * | | + * |___________| + * first edge + * second edge + * + * trig default low level + * ___________ + * | | + * | | + * __________| |__________ + * first edge + * second edge + ******************** vib_trig_config ********************* + * level polar pos_en pos_seq neg_en neg_seq brk bst + trig1* 1 0 1 1 1 2 0 0 + trig2* 1 0 0 1 0 2 0 0 + trig3* 1 0 0 1 0 2 0 0 +*/ +struct trig { + unsigned char trig_level; + unsigned char trig_polar; + unsigned char pos_enable; + unsigned char pos_sequence; + unsigned char neg_enable; + unsigned char neg_sequence; + unsigned char trig_brk; + unsigned char trig_bst; +}; + +struct aw869xx_dts_info { + unsigned int mode; + unsigned int f0_ref; + unsigned int f0_cali_percent; + unsigned int cont_drv1_lvl_dt; + unsigned int cont_drv2_lvl_dt; + unsigned int cont_drv1_time_dt; + unsigned int cont_drv2_time_dt; + unsigned int cont_wait_num_dt; + unsigned int cont_brk_time_dt; + unsigned int cont_track_margin; + unsigned int cont_tset; + unsigned int cont_drv_width; + unsigned int cont_bemf_set; + unsigned int cont_brk_gain; + unsigned int cont_bst_brk_gain; + unsigned int d2s_gain; + unsigned int bstcfg[5]; + unsigned int prctmode[3]; + unsigned int sine_array[4]; + unsigned int trig_config[24]; + bool is_enabled_auto_bst; + bool is_enabled_i2s; + bool is_enabled_powerup_f0_cali; +}; + +struct aw869xx { + struct regmap *regmap; + struct i2c_client *i2c; + /*struct snd_soc_codec *codec; */ + aw_snd_soc_codec_t *codec; + struct device *dev; + struct input_dev *input; + struct mutex lock; + struct mutex rtp_lock; + struct hrtimer timer; + struct work_struct long_vibrate_work; + struct work_struct rtp_work; + struct delayed_work ram_work; + struct trig trig[AW869XX_TRIG_NUM]; + struct aw869xx_dts_info dts_info; + struct ram ram; + struct timeval start, end; + + cdev_t vib_dev; + + bool haptic_ready; + bool audio_ready; + + unsigned char seq[AW869XX_SEQUENCER_SIZE]; + unsigned char loop[AW869XX_SEQUENCER_SIZE]; + unsigned char rtp_init; + unsigned char ram_init; + unsigned char rtp_routine_on; + unsigned char max_pos_beme; + unsigned char max_neg_beme; + unsigned char f0_cali_flag; + unsigned char ram_vbat_compensate; + unsigned char hwen_flag; + unsigned char flags; + unsigned char chipid; + unsigned char play_mode; + unsigned char activate_mode; + unsigned char auto_boost; + unsigned char boost_mode; + unsigned char bst_pc; + + int reset_gpio; + int irq_gpio; + int state; + int duration; + int amplitude; + int index; + int vmax; + int gain; + int sysclk; + int rate; + int width; + int pstream; + int cstream; + + unsigned int rtp_cnt; + unsigned int rtp_file_num; + unsigned int f0; + unsigned int cont_f0; + unsigned int cont_drv1_lvl; + unsigned int cont_drv2_lvl; + unsigned int cont_brk_time; + unsigned int cont_wait_num; + unsigned int cont_drv1_time; + unsigned int cont_drv2_time; + unsigned int theory_time; + unsigned int vbat; + unsigned int lra; + unsigned int ram_update_flag; + unsigned int rtp_update_flag; + unsigned int osc_cali_data; + unsigned int f0_cali_data; + unsigned int timeval_flags; + unsigned int osc_cali_flag; + unsigned int sys_frequency; + unsigned int rtp_len; + unsigned long int microsecond; +}; + +struct aw869xx_container { + int len; + unsigned char data[]; +}; + +#endif diff --git a/drivers/misc/awinic/aw869xx/aw869xx_reg.h b/drivers/misc/awinic/aw869xx/aw869xx_reg.h new file mode 100644 index 000000000000..bc10b82d8737 --- /dev/null +++ b/drivers/misc/awinic/aw869xx/aw869xx_reg.h @@ -0,0 +1,897 @@ +#ifndef _AW869XX_REG_H_ +#define _AW869XX_REG_H_ + +/******************************************** + * Register List + *******************************************/ + +#define AW869XX_REG_ID 0x00 +#define AW869XX_REG_SYSST 0x01 +#define AW869XX_REG_SYSINT 0x02 +#define AW869XX_REG_SYSINTM 0x03 +#define AW869XX_REG_SYSST2 0x04 +#define AW869XX_REG_SYSER 0x05 +#define AW869XX_REG_PLAYCFG1 0x06 +#define AW869XX_REG_PLAYCFG2 0x07 +#define AW869XX_REG_PLAYCFG3 0x08 +#define AW869XX_REG_PLAYCFG4 0x09 +#define AW869XX_REG_WAVCFG1 0x0A +#define AW869XX_REG_WAVCFG2 0x0B +#define AW869XX_REG_WAVCFG3 0x0C +#define AW869XX_REG_WAVCFG4 0x0D +#define AW869XX_REG_WAVCFG5 0x0E +#define AW869XX_REG_WAVCFG6 0x0F +#define AW869XX_REG_WAVCFG7 0x10 +#define AW869XX_REG_WAVCFG8 0x11 +#define AW869XX_REG_WAVCFG9 0x12 +#define AW869XX_REG_WAVCFG10 0x13 +#define AW869XX_REG_WAVCFG11 0x14 +#define AW869XX_REG_WAVCFG12 0x15 +#define AW869XX_REG_WAVCFG13 0x16 +#define AW869XX_REG_WAVCFG14 0x17 +#define AW869XX_REG_CONTCFG1 0x18 +#define AW869XX_REG_CONTCFG2 0x19 +#define AW869XX_REG_CONTCFG3 0x1A +#define AW869XX_REG_CONTCFG4 0x1B +#define AW869XX_REG_CONTCFG5 0x1C +#define AW869XX_REG_CONTCFG6 0x1D +#define AW869XX_REG_CONTCFG7 0x1E +#define AW869XX_REG_CONTCFG8 0x1F +#define AW869XX_REG_CONTCFG9 0x20 +#define AW869XX_REG_CONTCFG10 0x21 +#define AW869XX_REG_CONTCFG11 0x22 +#define AW869XX_REG_CONTCFG12 0x23 +#define AW869XX_REG_CONTCFG13 0x24 +#define AW869XX_REG_CONTRD14 0x25 +#define AW869XX_REG_CONTRD15 0x26 +#define AW869XX_REG_CONTRD16 0x27 +#define AW869XX_REG_CONTRD17 0x28 +#define AW869XX_REG_CONTRD18 0x29 +#define AW869XX_REG_CONTRD19 0x2A +#define AW869XX_REG_CONTRD20 0x2B +#define AW869XX_REG_CONTRD21 0x2C +#define AW869XX_REG_RTPCFG1 0x2D +#define AW869XX_REG_RTPCFG2 0x2E +#define AW869XX_REG_RTPCFG3 0x2F +#define AW869XX_REG_RTPCFG4 0x30 +#define AW869XX_REG_RTPCFG5 0x31 +#define AW869XX_REG_RTPDATA 0x32 +#define AW869XX_REG_TRGCFG1 0x33 +#define AW869XX_REG_TRGCFG2 0x34 +#define AW869XX_REG_TRGCFG3 0x35 +#define AW869XX_REG_TRGCFG4 0x36 +#define AW869XX_REG_TRGCFG5 0x37 +#define AW869XX_REG_TRGCFG6 0x38 +#define AW869XX_REG_TRGCFG7 0x39 +#define AW869XX_REG_TRGCFG8 0x3A +#define AW869XX_REG_GLBCFG1 0x3B +#define AW869XX_REG_GLBCFG2 0x3C +#define AW869XX_REG_GLBCFG3 0x3D +#define AW869XX_REG_GLBCFG4 0x3E +#define AW869XX_REG_GLBRD5 0x3F +#define AW869XX_REG_RAMADDRH 0x40 +#define AW869XX_REG_RAMADDRL 0x41 +#define AW869XX_REG_RAMDATA 0x42 +#define AW869XX_REG_SYSCTRL1 0x43 +#define AW869XX_REG_SYSCTRL2 0x44 +#define AW869XX_REG_SYSCTRL3 0x45 +#define AW869XX_REG_SYSCTRL4 0x46 +#define AW869XX_REG_SYSCTRL5 0x47 +#define AW869XX_REG_SYSCTRL6 0x48 +#define AW869XX_REG_SYSCTRL7 0x49 +#define AW869XX_REG_I2SCFG1 0x4A +#define AW869XX_REG_I2SCFG2 0x4B +#define AW869XX_REG_PWMCFG1 0x4C +#define AW869XX_REG_PWMCFG2 0x4D +#define AW869XX_REG_PWMCFG3 0x4E +#define AW869XX_REG_PWMCFG4 0x4F +#define AW869XX_REG_TMCFG 0x50 +#define AW869XX_REG_DETCFG1 0x51 +#define AW869XX_REG_DETCFG2 0x52 +#define AW869XX_REG_DET_RL 0x53 +#define AW869XX_REG_DET_OS 0x54 +#define AW869XX_REG_DET_VBAT 0x55 +#define AW869XX_REG_DET_TEST 0x56 +#define AW869XX_REG_DET_LO 0x57 +#define AW869XX_REG_TRIMCFG1 0x58 +#define AW869XX_REG_TRIMCFG2 0x59 +#define AW869XX_REG_TRIMCFG3 0x5A +#define AW869XX_REG_TRIMCFG4 0x5B +#define AW869XX_REG_PLLCFG1 0x68 +#define AW869XX_REG_PLLCFG2 0x69 +#define AW869XX_REG_HDRVCFG1 0x6A +#define AW869XX_REG_IOCFG1 0x6B +#define AW869XX_REG_BEMFCFG1 0x6C +#define AW869XX_REG_BSTCFG1 0x6D +#define AW869XX_REG_BSTCFG2 0x6E +#define AW869XX_REG_BSTCFG3 0x6F +#define AW869XX_REG_BSTCFG4 0x70 +#define AW869XX_REG_BSTCFG5 0x71 +#define AW869XX_REG_CPCFG1 0x72 +#define AW869XX_REG_LDOCFG1 0x73 +#define AW869XX_REG_OCCFG1 0x74 +#define AW869XX_REG_ADCCFG1 0x75 +#define AW869XX_REG_D2SCFG1 0x76 + +/******************************************** + * Register Access + *******************************************/ +#define REG_NONE_ACCESS 0 +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) +#define AW869XX_REG_MAX 0xff + +const unsigned char aw869xx_reg_access[AW869XX_REG_MAX] = { + [AW869XX_REG_ID] = REG_RD_ACCESS, + [AW869XX_REG_SYSST] = REG_RD_ACCESS, + [AW869XX_REG_SYSINT] = REG_RD_ACCESS, + [AW869XX_REG_SYSINTM] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_SYSST2] = REG_RD_ACCESS, + [AW869XX_REG_SYSER] = REG_RD_ACCESS, + [AW869XX_REG_PLAYCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_PLAYCFG2] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_PLAYCFG3] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_PLAYCFG4] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_WAVCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_WAVCFG2] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_WAVCFG3] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_WAVCFG4] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_WAVCFG5] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_WAVCFG6] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_WAVCFG7] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_WAVCFG8] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_WAVCFG9] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_WAVCFG10] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_WAVCFG11] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_WAVCFG12] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_WAVCFG13] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_WAVCFG14] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_CONTCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_CONTCFG2] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_CONTCFG3] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_CONTCFG4] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_CONTCFG5] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_CONTCFG6] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_CONTCFG7] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_CONTCFG8] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_CONTCFG9] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_CONTCFG10] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_CONTCFG11] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_CONTCFG12] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_CONTCFG13] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_CONTRD14] = REG_RD_ACCESS, + [AW869XX_REG_CONTRD15] = REG_RD_ACCESS, + [AW869XX_REG_CONTRD16] = REG_RD_ACCESS, + [AW869XX_REG_CONTRD17] = REG_RD_ACCESS, + [AW869XX_REG_CONTRD18] = REG_RD_ACCESS, + [AW869XX_REG_CONTRD19] = REG_RD_ACCESS, + [AW869XX_REG_CONTRD20] = REG_RD_ACCESS, + [AW869XX_REG_CONTRD21] = REG_RD_ACCESS, + [AW869XX_REG_RTPCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_RTPCFG2] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_RTPCFG3] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_RTPCFG4] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_RTPCFG5] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_RTPDATA] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_TRGCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_TRGCFG2] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_TRGCFG3] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_TRGCFG4] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_TRGCFG5] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_TRGCFG6] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_TRGCFG7] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_TRGCFG8] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_GLBCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_GLBCFG2] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_GLBCFG3] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_GLBCFG4] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_GLBRD5] = REG_RD_ACCESS, + [AW869XX_REG_RAMADDRH] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_RAMADDRL] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_RAMDATA] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_SYSCTRL1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_SYSCTRL2] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_SYSCTRL3] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_SYSCTRL4] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_SYSCTRL5] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_SYSCTRL6] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_SYSCTRL7] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_I2SCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_I2SCFG2] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_PWMCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_PWMCFG2] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_PWMCFG3] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_PWMCFG4] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_DETCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_DETCFG2] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_DET_RL] = REG_RD_ACCESS, + [AW869XX_REG_DET_OS] = REG_RD_ACCESS, + [AW869XX_REG_DET_VBAT] = REG_RD_ACCESS, + [AW869XX_REG_DET_TEST] = REG_RD_ACCESS, + [AW869XX_REG_DET_LO] = REG_RD_ACCESS, + [AW869XX_REG_TRIMCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_TRIMCFG2] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_TRIMCFG3] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_TRIMCFG4] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_PLLCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_PLLCFG2] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_HDRVCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_IOCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_BEMFCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_BSTCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_BSTCFG2] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_BSTCFG3] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_BSTCFG4] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_BSTCFG5] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_CPCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_LDOCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_OCCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_ADCCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + [AW869XX_REG_D2SCFG1] = REG_RD_ACCESS | REG_WR_ACCESS, + +}; + +/****************************************************** + * Register Detail + *****************************************************/ +/* SYSST: reg 0x01 RO */ +#define AW869XX_BIT_SYSST_BST_SCPS (1<<7) +#define AW869XX_BIT_SYSST_BST_OVPS (1<<6) +#define AW869XX_BIT_SYSST_UVLS (1<<5) +#define AW869XX_BIT_SYSST_FF_AES (1<<4) +#define AW869XX_BIT_SYSST_FF_AFS (1<<3) +#define AW869XX_BIT_SYSST_OCDS (1<<2) +#define AW869XX_BIT_SYSST_OTS (1<<1) +#define AW869XX_BIT_SYSST_DONES (1<<0) + +/* SYSINT: reg 0x02 RC */ +#define AW869XX_BIT_SYSINT_BST_SCPI (1<<7) +#define AW869XX_BIT_SYSINT_BST_OVPI (1<<6) +#define AW869XX_BIT_SYSINT_UVLI (1<<5) +#define AW869XX_BIT_SYSINT_FF_AEI (1<<4) +#define AW869XX_BIT_SYSINT_FF_AFI (1<<3) +#define AW869XX_BIT_SYSINT_OCDI (1<<2) +#define AW869XX_BIT_SYSINT_OTI (1<<1) +#define AW869XX_BIT_SYSINT_DONEI (1<<0) + +/* SYSINTM: reg 0x03 RW */ +#define AW869XX_BIT_SYSINTM_BST_SCPM_MASK (~(1<<7)) +#define AW869XX_BIT_SYSINTM_BST_SCPM_OFF (1<<7) +#define AW869XX_BIT_SYSINTM_BST_SCPM_ON (0<<7) +#define AW869XX_BIT_SYSINTM_BST_OVPM_MASK (~(1<<6)) +#define AW869XX_BIT_SYSINTM_BST_OVPM_OFF (1<<6) +#define AW869XX_BIT_SYSINTM_BST_OVPM_ON (0<<6) +#define AW869XX_BIT_SYSINTM_UVLM_MASK (~(1<<5)) +#define AW869XX_BIT_SYSINTM_UVLM_OFF (1<<5) +#define AW869XX_BIT_SYSINTM_UVLM_ON (0<<5) +#define AW869XX_BIT_SYSINTM_FF_AEM_MASK (~(1<<4)) +#define AW869XX_BIT_SYSINTM_FF_AEM_OFF (1<<4) +#define AW869XX_BIT_SYSINTM_FF_AEM_ON (0<<4) +#define AW869XX_BIT_SYSINTM_FF_AFM_MASK (~(1<<3)) +#define AW869XX_BIT_SYSINTM_FF_AFM_OFF (1<<3) +#define AW869XX_BIT_SYSINTM_FF_AFM_ON (0<<3) +#define AW869XX_BIT_SYSINTM_OCDM_MASK (~(1<<2)) +#define AW869XX_BIT_SYSINTM_OCDM_OFF (1<<2) +#define AW869XX_BIT_SYSINTM_OCDM_ON (0<<2) +#define AW869XX_BIT_SYSINTM_OTM_MASK (~(1<<1)) +#define AW869XX_BIT_SYSINTM_OTM_OFF (1<<1) +#define AW869XX_BIT_SYSINTM_OTM_ON (0<<1) +#define AW869XX_BIT_SYSINTM_DONEM_MASK (~(1<<0)) +#define AW869XX_BIT_SYSINTM_DONEM_OFF (1<<0) +#define AW869XX_BIT_SYSINTM_DONEM_ON (0<<0) + +/* SYSST2: reg 0x04 RO */ +#define AW869XX_BIT_SYSST2_RAM_ADDR_ER (1<<7) +#define AW869XX_BIT_SYSST2_TRG_ADDR_ER (1<<6) +#define AW869XX_BIT_SYSST2_PLL_REF_OK (1<<5) +#define AW869XX_BIT_SYSST2_BST_OK (1<<4) +#define AW869XX_BIT_SYSST2_VBG_OK (1<<3) +#define AW869XX_BIT_SYSST2_LDO_OK (1<<2) +#define AW869XX_BIT_SYSST2_FF_FULL (1<<1) +#define AW869XX_BIT_SYSST2_FF_EMPTY (1<<0) + +/* SYSER: reg 0x05 RC */ +#define AW869XX_BIT_SYSER_I2S_ERR (1<<7) +#define AW869XX_BIT_SYSER_TRIG1_EVENT (1<<6) +#define AW869XX_BIT_SYSER_TRIG2_EVENT (1<<5) +#define AW869XX_BIT_SYSER_TRIG3_EVENT (1<<4) +#define AW869XX_BIT_SYSER_OV (1<<3) +#define AW869XX_BIT_SYSER_ADDR_ER (1<<2) +#define AW869XX_BIT_SYSER_FF_ER (1<<1) +#define AW869XX_BIT_SYSER_PLL_REF_ER (1<<0) + +/* PLAYCFG1: reg 0x06 RW */ +#define AW869XX_BIT_PLAYCFG1_BST_MODE_MASK (~(1<<6)) +#define AW869XX_BIT_PLAYCFG1_BST_MODE_BYPASS (0<<6) +#define AW869XX_BIT_PLAYCFG1_BST_MODE_BOOST (1<<6) +#define AW869XX_BIT_PLAYCFG1_BST_VOUT_RDA_MASK (~(63<<0)) + +/* PLAYCFG2: reg 0x07 RW */ +/* GAIN */ + +/* PLAYCFG3: reg 0x08 RW */ +#define AW869XX_BIT_PLAYCFG3_AUTO_BST_MASK (~(1<<6)) +#define AW869XX_BIT_PLAYCFG3_AUTO_BST_ENABLE (1<<6) +#define AW869XX_BIT_PLAYCFG3_AUTO_BST_DISABLE (0<<6) +#define AW869XX_BIT_PLAYCFG3_STOP_MODE_MASK (~(1<<5)) +#define AW869XX_BIT_PLAYCFG3_STOP_MODE_NOW (1<<5) +#define AW869XX_BIT_PLAYCFG3_STOP_MODE_LATER (0<<5) +#define AW869XX_BIT_PLAYCFG3_BRK_EN_MASK (~(1<<2)) +#define AW869XX_BIT_PLAYCFG3_BRK_ENABLE (1<<2) +#define AW869XX_BIT_PLAYCFG3_BRK_DISABLE (0<<2) +#define AW869XX_BIT_PLAYCFG3_PLAY_MODE_MASK (~(3<<0)) +#define AW869XX_BIT_PLAYCFG3_PLAY_MODE_STOP (3<<0) +#define AW869XX_BIT_PLAYCFG3_PLAY_MODE_CONT (2<<0) +#define AW869XX_BIT_PLAYCFG3_PLAY_MODE_RTP (1<<0) +#define AW869XX_BIT_PLAYCFG3_PLAY_MODE_RAM (0<<0) + +/* PLAYCFG4: reg 0x09 RW */ +#define AW869XX_BIT_PLAYCFG4_STOP_MASK (~(1<<1)) +#define AW869XX_BIT_PLAYCFG4_STOP_ON (1<<1) +#define AW869XX_BIT_PLAYCFG4_STOP_OFF (0<<1) +#define AW869XX_BIT_PLAYCFG4_GO_MASK (~(1<<0)) +#define AW869XX_BIT_PLAYCFG4_GO_ON (1<<0) +#define AW869XX_BIT_PLAYCFG4_GO_OFF (0<<0) + +/* WAVCFG1-8: reg 0x0A - reg 0x11 RW */ +#define AW869XX_BIT_WAVCFG_SEQWAIT_MASK (~(1<<7)) +#define AW869XX_BIT_WAVCFG_SEQWAIT_TIME (1<<7) +#define AW869XX_BIT_WAVCFG_SEQWAIT_NUMBER (0<<7) + +/* WAVCFG9-12: reg 0x12 - reg 0x15 RW */ +#define AW869XX_BIT_WAVLOOP_SEQ_ODD_MASK (~(0x0F<<4)) +#define AW869XX_BIT_WAVLOOP_SEQ_ODD_INIFINITELY (0x0F<<4) +#define AW869XX_BIT_WAVLOOP_SEQ_EVEN_MASK (~(0x0F<<0)) +#define AW869XX_BIT_WAVLOOP_SEQ_EVEN_INIFINITELY (0x0F<<0) +#define AW869XX_BIT_WAVLOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG9: reg 0x12 RW */ +#define AW869XX_BIT_WAVCFG9_SEQ1LOOP_MASK (~(0x0F<<4)) +#define AW869XX_BIT_WAVCFG9_SEQ1LOOP_INIFINITELY (0x0F<<4) +#define AW869XX_BIT_WAVCFG9_SEQ2LOOP_MASK (~(0x0F<<0)) +#define AW869XX_BIT_WAVCFG9_SEQ2LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG10: reg 0x13 RW */ +#define AW869XX_BIT_WAVCFG10_SEQ3LOOP_MASK (~(0x0F<<4)) +#define AW869XX_BIT_WAVCFG10_SEQ3LOOP_INIFINITELY (0x0F<<4) +#define AW869XX_BIT_WAVCFG10_SEQ4LOOP_MASK (~(0x0F<<0)) +#define AW869XX_BIT_WAVCFG10_SEQ4LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG11: reg 0x14 RW */ +#define AW869XX_BIT_WAVCFG11_SEQ5LOOP_MASK (~(0x0F<<4)) +#define AW869XX_BIT_WAVCFG11_SEQ5LOOP_INIFINITELY (0x0F<<4) +#define AW869XX_BIT_WAVCFG11_SEQ6LOOP_MASK (~(0x0F<<0)) +#define AW869XX_BIT_WAVCFG11_SEQ6LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG12: reg 0x15 RW */ +#define AW869XX_BIT_WAVCFG12_SEQ7LOOP_MASK (~(0x0F<<4)) +#define AW869XX_BIT_WAVCFG12_SEQ7LOOP_INIFINITELY (0x0F<<4) +#define AW869XX_BIT_WAVCFG12_SEQ8LOOP_MASK (~(0x0F<<0)) +#define AW869XX_BIT_WAVCFG12_SEQ8LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG13: reg 0x16 RW */ +#define AW869XX_BIT_WAVCFG13_WAITSLOT_MASK (~(3<<5)) +#define AW869XX_BIT_WAVCFG13_WAITSLOT_DIV_1 (0<<5) +#define AW869XX_BIT_WAVCFG13_WAITSLOT_DIV_8 (1<<5) +#define AW869XX_BIT_WAVCFG13_WAITSLOT_DIV_64 (2<<5) +#define AW869XX_BIT_WAVCFG13_WAITSLOT_DIV_512 (3<<5) +#define AW869XX_BIT_WAVCFG13_AUTO_MD_MASK (~(1<<4)) +#define AW869XX_BIT_WAVCFG13_AUTO_MD_CONT_MODE (1<<4) +#define AW869XX_BIT_WAVCFG13_AUTO_MD_SIN_WAV (0<<4) +#define AW869XX_BIT_WAVCFG13_MAINLOOP_MASK (~(0x0F<<0)) +#define AW869XX_BIT_WAVCFG13_MAINLOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG14: reg 0x17 RW */ +/* AUTO_WAV */ + +/***************** CONT *****************/ +/* CONTCFG1: reg 0x18 RW */ +#define AW869XX_BIT_CONTCFG1_EDGE_FRE_MASK (~(0x0F<<4)) +#define AW869XX_BIT_CONTCFG1_EN_F0_DET_MASK (~(1<<3)) +#define AW869XX_BIT_CONTCFG1_F0_DET_ENABLE (1<<3) +#define AW869XX_BIT_CONTCFG1_F0_DET_DISABLE (0<<3) +#define AW869XX_BIT_CONTCFG1_MBRK_MASK (~(1<<2)) +#define AW869XX_BIT_CONTCFG1_MBRK_ENABLE (1<<2) +#define AW869XX_BIT_CONTCFG1_MBRK_DISABLE (0<<2) +#define AW869XX_BIT_CONTCFG1_BRK_BST_MD_MASK (~(1<<1)) +#define AW869XX_BIT_CONTCFG1_BRK_BST_MD_ENABLE (1<<1) +#define AW869XX_BIT_CONTCFG1_BRK_BST_MD_DISABLE (0<<1) +#define AW869XX_BIT_CONTCFG1_SIN_MODE_MASK (~(1<<0)) +#define AW869XX_BIT_CONTCFG1_SIN_MODE_COS (1<<0) +#define AW869XX_BIT_CONTCFG1_SIN_MODE_SINE (0<<0) + +/* CONTCFG2: reg 0x19 RW */ +/* F_PRE */ + +/* CONTCFG3: reg 0x1A RW */ +/* DRV_WIDTH */ + +/* CONTCFG4: reg 0x1B RW */ +/* WAIT_NUM */ + +/* CONTCFG5: reg 0x1C RW */ +#define AW869XX_BIT_CONTCFG5_BST_BRK_GAIN_MASK (~(0x0F<<4)) +#define AW869XX_BIT_CONTCFG5_BRK_GAIN_MASK (~(0x0F<<0)) + +/* CONTCFG6: reg 0x1D RW */ +#define AW869XX_BIT_CONTCFG6_TRACK_EN_MASK (~(1<<7)) +#define AW869XX_BIT_CONTCFG6_TRACK_ENABLE (1<<7) +#define AW869XX_BIT_CONTCFG6_TRACK_DISABLE (0<<7) +#define AW869XX_BIT_CONTCFG6_DRV1_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG7: reg 0x1E RW */ +#define AW869XX_BIT_CONTCFG7_DRV2_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG8: reg 0x1F RW */ +/* DRV1_TIME */ + +/* CONTCFG9: reg 0x20 RW */ +/* DRV2_TIME */ + +/* CONTCFG10: reg 0x21 RW */ +/* BRK_TIME */ + +/* CONTCFG11: reg 0x22 RW */ +/* TRACK_MARGIN */ + +/* CONTCFG12: reg 0x23 RW */ +/* MBRK_TH */ + +/* CONTCFG13: reg 0x24 RW */ +#define AW869XX_BIT_CONTCFG13_TSET_MASK (~(0x0F<<4)) +#define AW869XX_BIT_CONTCFG13_BEME_SET_MASK (~(0x0F<<0)) + +/* CONTRD14: reg 0x25 RO */ +/* F_LRA_F0_H */ + +/* CONTRD15: reg 0x26 RO */ +/* F_LRA_F0_L */ + +/* CONTRD16: reg 0x27 RO */ +/* CONT_F0_H */ + +/* CONTRD17: reg 0x28 RO */ +/* CONT_F0_L */ + +/* CONTRD18: reg 0x29 RO */ +/* ACCELERATION */ + +/* CONTRD19: reg 0x2A RO */ +/* BEMF_PEAK1 */ + +/* CONTRD20: reg 0x2B RO */ +/* BEMF_PEAK2 */ + +/* CONTRD21: reg 0x2C RO */ +/* BEMF_PEAK3 */ + +/***************** RTP *****************/ +/* RTPCFG1: reg 0x2D RW */ +/* BASE_ADDR_H */ + +/* RTPCFG2: reg 0x2E RW */ +/* BASE_ADDR_L */ + +/* RTPCFG3: reg 0x2F RW */ +#define AW869XX_BIT_RTPCFG3_FIFO_AEH_MASK (~(0x0F<<4)) +#define AW869XX_BIT_RTPCFG3_FIFO_AFH_MASK (~(0x0F<<0)) + +/* RTPCFG4: reg 0x30 RW */ +/* FIFO_AEL */ + +/* RTPCFG5: reg 0x31 RW */ +/* FIFO_AFL */ + +/* RTPDATA: reg 0x32 RW */ +/* FIFO_AFL */ + +/***************** TRIGGER *****************/ +#define AW869XX_BIT_TRG_ENABLE_MASK (~(1<<7)) +#define AW869XX_BIT_TRG_ENABLE (1<<7) +#define AW869XX_BIT_TRG_DISABLE (0<<7) +#define AW869XX_BIT_TRG_SEQ_MASK (~(0x7F<<0)) + +/* TRGCFG1: reg 0x33 RW */ +#define AW869XX_BIT_TRGCFG1_TRG1_POS_MASK (~(1<<7)) +#define AW869XX_BIT_TRGCFG1_TRG1_POS_ENABLE (1<<7) +#define AW869XX_BIT_TRGCFG1_TRG1_POS_DISABLE (0<<7) +#define AW869XX_BIT_TRGCFG1_TRG1SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG2: reg 0x34 RW */ +#define AW869XX_BIT_TRGCFG2_TRG2_POS_MASK (~(1<<7)) +#define AW869XX_BIT_TRGCFG2_TRG2_POS_ENABLE (1<<7) +#define AW869XX_BIT_TRGCFG2_TRG2_POS_DISABLE (0<<7) +#define AW869XX_BIT_TRGCFG2_TRG2SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG3: reg 0x35 RW */ +#define AW869XX_BIT_TRGCFG3_TRG3_POS_MASK (~(1<<7)) +#define AW869XX_BIT_TRGCFG3_TRG3_POS_ENABLE (1<<7) +#define AW869XX_BIT_TRGCFG3_TRG3_POS_DISABLE (0<<7) +#define AW869XX_BIT_TRGCFG3_TRG3SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG4: reg 0x36 RW */ +#define AW869XX_BIT_TRGCFG4_TRG1_NEG_MASK (~(1<<7)) +#define AW869XX_BIT_TRGCFG4_TRG1_NEG_ENABLE (1<<7) +#define AW869XX_BIT_TRGCFG4_TRG1_NEG_DISABLE (0<<7) +#define AW869XX_BIT_TRGCFG4_TRG1SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG5: reg 0x37 RW */ +#define AW869XX_BIT_TRGCFG5_TRG2_NEG_MASK (~(1<<7)) +#define AW869XX_BIT_TRGCFG5_TRG2_NEG_ENABLE (1<<7) +#define AW869XX_BIT_TRGCFG5_TRG2_NEG_DISABLE (0<<7) +#define AW869XX_BIT_TRGCFG5_TRG2SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG6: reg 0x38 RW */ +#define AW869XX_BIT_TRGCFG6_TRG3_NEG_MASK (~(1<<7)) +#define AW869XX_BIT_TRGCFG6_TRG3_NEG_ENABLE (1<<7) +#define AW869XX_BIT_TRGCFG6_TRG3_NEG_DISABLE (0<<7) +#define AW869XX_BIT_TRGCFG6_TRG3SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG7: reg 0x39 RW */ +#define AW869XX_BIT_TRGCFG7_TRG1_POLAR_MASK (~(1<<7)) +#define AW869XX_BIT_TRGCFG7_TRG1_POLAR_NEG (1<<7) +#define AW869XX_BIT_TRGCFG7_TRG1_POLAR_POS (0<<7) +#define AW869XX_BIT_TRGCFG7_TRG1_MODE_MASK (~(1<<6)) +#define AW869XX_BIT_TRGCFG7_TRG1_MODE_LEVEL (1<<6) +#define AW869XX_BIT_TRGCFG7_TRG1_MODE_EDGE (0<<6) +#define AW869XX_BIT_TRGCFG7_TRG1_AUTO_BRK_MASK (~(1<<5)) +#define AW869XX_BIT_TRGCFG7_TRG1_AUTO_BRK_ENABLE (1<<5) +#define AW869XX_BIT_TRGCFG7_TRG1_AUTO_BRK_DISABLE (0<<5) +#define AW869XX_BIT_TRGCFG7_TRG1_BST_MASK (~(1<<4)) +#define AW869XX_BIT_TRGCFG7_TRG1_BST_ENABLE (1<<4) +#define AW869XX_BIT_TRGCFG7_TRG1_BST_DISABLE (0<<4) +#define AW869XX_BIT_TRGCFG7_TRG2_POLAR_MASK (~(1<<3)) +#define AW869XX_BIT_TRGCFG7_TRG2_POLAR_NEG (1<<3) +#define AW869XX_BIT_TRGCFG7_TRG2_POLAR_POS (0<<3) +#define AW869XX_BIT_TRGCFG7_TRG2_MODE_MASK (~(1<<2)) +#define AW869XX_BIT_TRGCFG7_TRG2_MODE_LEVEL (1<<2) +#define AW869XX_BIT_TRGCFG7_TRG2_MODE_EDGE (0<<2) +#define AW869XX_BIT_TRGCFG7_TRG2_AUTO_BRK_MASK (~(1<<1)) +#define AW869XX_BIT_TRGCFG7_TRG2_AUTO_BRK_ENABLE (1<<1) +#define AW869XX_BIT_TRGCFG7_TRG2_AUTO_BRK_DISABLE (0<<1) +#define AW869XX_BIT_TRGCFG7_TRG2_BST_MASK (~(1<<0)) +#define AW869XX_BIT_TRGCFG7_TRG2_BST_ENABLE (1<<0) +#define AW869XX_BIT_TRGCFG7_TRG2_BST_DISABLE (0<<0) + +/* TRGCFG8: reg 0x3A RW */ +#define AW869XX_BIT_TRGCFG8_TRG3_POLAR_MASK (~(1<<7)) +#define AW869XX_BIT_TRGCFG8_TRG3_POLAR_NEG (1<<7) +#define AW869XX_BIT_TRGCFG8_TRG3_POLAR_POS (0<<7) +#define AW869XX_BIT_TRGCFG8_TRG3_MODE_MASK (~(1<<6)) +#define AW869XX_BIT_TRGCFG8_TRG3_MODE_LEVEL (1<<6) +#define AW869XX_BIT_TRGCFG8_TRG3_MODE_EDGE (0<<6) +#define AW869XX_BIT_TRGCFG8_TRG3_AUTO_BRK_MASK (~(1<<5)) +#define AW869XX_BIT_TRGCFG8_TRG3_AUTO_BRK_ENABLE (1<<5) +#define AW869XX_BIT_TRGCFG8_TRG3_AUTO_BRK_DISABLE (0<<5) +#define AW869XX_BIT_TRGCFG8_TRG3_BST_MASK (~(1<<4)) +#define AW869XX_BIT_TRGCFG8_TRG3_BST_ENABLE (1<<4) +#define AW869XX_BIT_TRGCFG8_TRG3_BST_DISABLE (0<<4) +#define AW869XX_BIT_TRGCFG8_TRG_ONEWIRE_MASK (~(1<<3)) +#define AW869XX_BIT_TRGCFG8_TRG_ONEWIRE (1<<3) +#define AW869XX_BIT_TRGCFG8_TRG1_STOP_MASK (~(1<<2)) +#define AW869XX_BIT_TRGCFG8_TRG1_STOP (1<<2) +#define AW869XX_BIT_TRGCFG8_TRG2_STOP_MASK (~(1<<1)) +#define AW869XX_BIT_TRGCFG8_TRG2_STOP (1<<1) +#define AW869XX_BIT_TRGCFG8_TRG3_STOP_MASK (~(1<<0)) +#define AW869XX_BIT_TRGCFG8_TRG3_STOP (1<<0) + +/* GLBCFG1: reg 0x3B RW */ +/* WAKE_DLY */ + +/* GLBCFG2: reg 0x3C RW */ +/* START_DLY */ + +/* GLBCFG3: reg 0x3D RW */ +/* END_DLY */ + +/* GLBCFG4: reg 0x3E RW */ +#define AW869XX_BIT_GLBCFG4_GO_PRIO_MASK (~(3<<6)) +#define AW869XX_BIT_GLBCFG4_TRG3_PRIO_MASK (~(3<<4)) +#define AW869XX_BIT_GLBCFG4_TRG2_PRIO_MASK (~(3<<2)) +#define AW869XX_BIT_GLBCFG4_TRG1_PRIO_MASK (~(3<<0)) + +/* GLBRD5: reg 0x3F R0 */ +/* GLB_STATE */ + +/* RAMADDRH: reg 0x40 RWS */ +#define AW869XX_BIT_RAMADDRH_MASK (~(63<<0)) + +/* RAMADDRL: reg 0x41 RWS */ +/* RAMADDRL */ + +/* RAMDATA: reg 0x42 RWS */ +/* RAMDATA */ + +/***************** SYSCTRL *****************/ +/* SYSCTRL1: reg 0x43 RW */ +#define AW869XX_BIT_SYSCTRL1_VBAT_MODE_MASK (~(1<<7)) +#define AW869XX_BIT_SYSCTRL1_VBAT_MODE_HW (1<<7) +#define AW869XX_BIT_SYSCTRL1_VBAT_MODE_SW (0<<7) +#define AW869XX_BIT_SYSCTRL1_PERP_MASK (~(1<<6)) +#define AW869XX_BIT_SYSCTRL1_PERP_ON (1<<6) +#define AW869XX_BIT_SYSCTRL1_PERP_OFF (0<<6) +#define AW869XX_BIT_SYSCTRL1_CLK_SEL_MASK (~(3<<4)) +#define AW869XX_BIT_SYSCTRL1_CLK_SEL_OSC (1<<4) +#define AW869XX_BIT_SYSCTRL1_CLK_SEL_AUTO (0<<4) +#define AW869XX_BIT_SYSCTRL1_RAMINIT_MASK (~(1<<3)) +#define AW869XX_BIT_SYSCTRL1_RAMINIT_ON (1<<3) +#define AW869XX_BIT_SYSCTRL1_RAMINIT_OFF (0<<3) +#define AW869XX_BIT_SYSCTRL1_EN_FIR_MASK (~(1<<2)) +#define AW869XX_BIT_SYSCTRL1_FIR_ENABLE (0<<2) +#define AW869XX_BIT_SYSCTRL1_WAKE_MODE_MASK (~(1<<1)) +#define AW869XX_BIT_SYSCTRL1_WAKE_MODE_WAKEUP (1<<1) +#define AW869XX_BIT_SYSCTRL1_WAKE_MODE_BST (0<<1) +#define AW869XX_BIT_SYSCTRL1_RTP_CLK_MASK (~(1<<0)) +#define AW869XX_BIT_SYSCTRL1_RTP_PLL (1<<0) +#define AW869XX_BIT_SYSCTRL1_RTP_OSC (0<<0) + +/* SYSCTRL2: reg 0x44 RW */ +#define AW869XX_BIT_SYSCTRL2_WAKE_MASK (~(1<<7)) +#define AW869XX_BIT_SYSCTRL2_WAKE_ON (1<<7) +#define AW869XX_BIT_SYSCTRL2_WAKE_OFF (0<<7) +#define AW869XX_BIT_SYSCTRL2_STANDBY_MASK (~(1<<6)) +#define AW869XX_BIT_SYSCTRL2_STANDBY_ON (1<<6) +#define AW869XX_BIT_SYSCTRL2_STANDBY_OFF (0<<6) +#define AW869XX_BIT_SYSCTRL2_RTP_DLY_MASK (~(3<<4)) +#define AW869XX_BIT_SYSCTRL2_PLL_PIN_MASK (~(1<<3)) +#define AW869XX_BIT_SYSCTRL2_PLL_PIN_TEST (1<<3) +#define AW869XX_BIT_SYSCTRL2_I2S_PIN_MASK (~(1<<2)) +#define AW869XX_BIT_SYSCTRL2_I2S_PIN_I2S (1<<2) +#define AW869XX_BIT_SYSCTRL2_I2S_PIN_TRIG (0<<2) +#define AW869XX_BIT_SYSCTRL2_WAVDAT_MODE_MASK (~(3<<0)) +#define AW869XX_BIT_SYSCTRL2_RATE_12K (2<<0) +#define AW869XX_BIT_SYSCTRL2_RATE_24K (0<<0) +#define AW869XX_BIT_SYSCTRL2_RATE_48K (1<<0) + +/* SYSCTRL3: reg 0x45 RW */ +/* SIN_H */ + +/* SYSCTRL4: reg 0x46 RW */ +/* SIN_L */ + +/* SYSCTRL5: reg 0x47 RW */ +/* COS_H */ + +/* SYSCTRL6: reg 0x48 RW */ +/* COS_L */ + +/* SYSCTRL7: reg 0x49 RW */ +#define AW869XX_BIT_SYSCTRL7_GAIN_BYPASS_MASK (~(1<<6)) +#define AW869XX_BIT_SYSCTRL7_GAIN_CHANGEABLE (1<<6) +#define AW869XX_BIT_SYSCTRL7_GAIN_FIXED (0<<6) + +#define AW869XX_BIT_SYSCTRL7_INT_EDGE_MODE_MASK (~(1<<5)) +#define AW869XX_BIT_SYSCTRL7_INT_EDGE_MODE_POS (0<<5) +#define AW869XX_BIT_SYSCTRL7_INT_EDGE_MODE_BOTH (1<<5) +#define AW869XX_BIT_SYSCTRL7_INT_MODE_MASK (~(1<<4)) +#define AW869XX_BIT_SYSCTRL7_INT_MODE_EDGE (1<<4) +#define AW869XX_BIT_SYSCTRL7_INT_MODE_LEVEL (0<<4) + +#define AW869XX_BIT_SYSCTRL7_INTP_MASK (~(1<<3)) +#define AW869XX_BIT_SYSCTRL7_INTP_HIGH (1<<3) +#define AW869XX_BIT_SYSCTRL7_INTP_LOW (0<<3) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN_MASK (~(7<<0)) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN_1 (0<<0) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN_2 (1<<0) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN_4 (2<<0) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN_8 (3<<0) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN_10 (4<<0) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN_16 (5<<0) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN_20 (6<<0) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN_26 (7<<0) + +/***************** I2S *****************/ +/* I2SCFG1: reg 0x4A RW */ +#define AW869XX_BIT_I2SCFG1_I2SMD_MASK (~(3<<6)) +#define AW869XX_BIT_I2SCFG1_I2SFS_MASK (~(3<<4)) +#define AW869XX_BIT_I2SCFG1_I2SFS_16BIT (0<<4) +#define AW869XX_BIT_I2SCFG1_I2SFS_20BIT (1<<4) +#define AW869XX_BIT_I2SCFG1_I2SFS_24BIT (2<<4) +#define AW869XX_BIT_I2SCFG1_I2SFS_32BIT (3<<4) +#define AW869XX_BIT_I2SCFG1_I2SBCK_MASK (~(3<<2)) +#define AW869XX_BIT_I2SCFG1_I2SBCK_32FS (0<<2) +#define AW869XX_BIT_I2SCFG1_I2SBCK_48FS (1<<2) +#define AW869XX_BIT_I2SCFG1_I2SBCK_64FS (2<<2) +#define AW869XX_BIT_I2SCFG1_RX_THRS_MASK (~(3<<0)) + +/* I2SCFG2: reg 0x4B RW */ +#define AW869XX_BIT_I2SCFG2_WSINV_MASK (~(1<<4)) +#define AW869XX_BIT_I2SCFG2_WSINV_SWITCH (1<<4) +#define AW869XX_BIT_I2SCFG2_WSINV_NO_SWITCH (0<<4) +#define AW869XX_BIT_I2SCFG2_BCKINV_MASK (~(1<<3)) +#define AW869XX_BIT_I2SCFG2_BCKINV_INVERT (1<<3) +#define AW869XX_BIT_I2SCFG2_BCKINV_NOTINVT (0<<3) +#define AW869XX_BIT_I2SCFG2_CHSEL_MASK (~(1<<2)) +#define AW869XX_BIT_I2SCFG2_CHSEL_LEFT (1<<2) +#define AW869XX_BIT_I2SCFG2_CHSEL_RIGHT (0<<2) +#define AW869XX_BIT_I2SCFG2_I2S_INT_MASK (~(1<<1)) +#define AW869XX_BIT_I2SCFG2_I2S_INT_ON (1<<1) +#define AW869XX_BIT_I2SCFG2_I2S_INT_OFF (0<<1) +#define AW869XX_BIT_I2SCFG2_I2S_EN_MASK (~(1<<0)) +#define AW869XX_BIT_I2SCFG2_I2S_ENABLE (1<<0) +#define AW869XX_BIT_I2SCFG2_I2S_DISABLE (0<<0) + +/* PWMCFG1: reg 0x4C RW */ +#define AW869XX_BIT_PWMCFG1_PRC_EN_MASK (~(1<<7)) +#define AW869XX_BIT_PWMCFG1_PRC_ENABLE (1<<7) +#define AW869XX_BIT_PWMCFG1_PRC_DISABLE (0<<7) +#define AW869XX_BIT_PWMCFG1_PRCTIME_MASK (~(0x7F<<0)) + +/* PWMCFG2: reg 0x4D RW */ +#define AW869XX_BIT_PWMCFG2_REF_SEL_MASK (~(1<<5)) +#define AW869XX_BIT_PWMCFG2_REF_SEL_TRIANGLE (1<<5) +#define AW869XX_BIT_PWMCFG2_REF_SEL_SAWTOOTH (0<<5) +#define AW869XX_BIT_PWMCFG2_PD_HWM_MASK (~(1<<4)) +#define AW869XX_BIT_PWMCFG2_PD_HWM_ON (1<<4) +#define AW869XX_BIT_PWMCFG2_PWMOE_MASK (~(1<<3)) +#define AW869XX_BIT_PWMCFG2_PWMOE_ON (1<<3) +#define AW869XX_BIT_PWMCFG2_PWMFRC_MASK (~(7<<0)) + +/* PWMCFG3: reg 0x4E RW */ +#define AW869XX_BIT_PWMCFG3_PR_EN_MASK (~(1<<7)) +#define AW869XX_BIT_PWMCFG3_PR_ENABLE (1<<7) +#define AW869XX_BIT_PWMCFG3_PR_DISABLE (0<<7) +#define AW869XX_BIT_PWMCFG3_PRLVL_MASK (~(0x7F<<0)) + +/* PWMCFG4: reg 0x4F RW */ +/* PRTIME */ + +/* TMCFG: reg 0x50 RW */ +/* TM */ + +/* DETCFG1: reg 0x51 RW */ +#define AW869XX_BIT_DETCFG1_FTS_GO_MASK (~(1<<7)) +#define AW869XX_BIT_DETCFG1_FTS_GO_ENABLE (1<<7) +#define AW869XX_BIT_DETCFG1_TEST_GO_MASK (~(1<<6)) +#define AW869XX_BIT_DETCFG1_TEST_GO_ENABLE (1<<6) +#define AW869XX_BIT_DETCFG1_ADO_SLOT_MODE_MASK (~(1<<5)) +#define AW869XX_BIT_DETCFG1_ADO_SLOT_ADC_32 (1<<5) +#define AW869XX_BIT_DETCFG1_ADO_SLOT_ADC_256 (0<<5) +#define AW869XX_BIT_DETCFG1_RL_OS_MASK (~(1<<4)) +#define AW869XX_BIT_DETCFG1_RL (1<<4) +#define AW869XX_BIT_DETCFG1_OS (0<<4) +#define AW869XX_BIT_DETCFG1_PRCT_MODE_MASK (~(1<<3)) +#define AW869XX_BIT_DETCFG1_PRCT_MODE_INVALID (1<<3) +#define AW869XX_BIT_DETCFG1_PRCT_MODE_VALID (0<<3) +#define AW869XX_BIT_DETCFG1_CLK_ADC_MASK (~(7<<0)) +#define AW869XX_BIT_DETCFG1_CLK_ADC_12M (0<<0) +#define AW869XX_BIT_DETCFG1_CLK_ADC_6M (1<<0) +#define AW869XX_BIT_DETCFG1_CLK_ADC_3M (2<<0) +#define AW869XX_BIT_DETCFG1_CLK_ADC_1M5 (3<<0) +#define AW869XX_BIT_DETCFG1_CLK_ADC_M75 (4<<0) +#define AW869XX_BIT_DETCFG1_CLK_ADC_M37 (5<<0) +#define AW869XX_BIT_DETCFG1_CLK_ADC_M18 (6<<0) +#define AW869XX_BIT_DETCFG1_CLK_ADC_M09 (7<<0) + +/* DETCFG2: reg 0x52 RW */ +#define AW869XX_BIT_DETCFG2_VBAT_GO_MASK (~(1<<1)) +#define AW869XX_BIT_DETCFG2_VABT_GO_ON (1<<1) +#define AW869XX_BIT_DETCFG2_DIAG_GO_MASK (~(1<<0)) +#define AW869XX_BIT_DETCFG2_DIAG_GO_ON (1<<0) + +/* DET_RL: reg 0x53 RW */ +/* RL */ + +/* DET_OS: reg 0x54 RW */ +/* OS */ + +/* DET_VBAT: reg 0x55 RW */ +/* VBAT */ + +/* DET_TEST: reg 0x56 RW */ +/* TEST */ + +/* DET_LO: reg 0x57 RW */ +#define AW869XX_BIT_DET_LO_TEST_MASK (~(3<<6)) +#define AW869XX_BIT_DET_LO_VBAT_MASK (~(3<<4)) +#define AW869XX_BIT_DET_LO_OS_MASK (~(3<<2)) +#define AW869XX_BIT_DET_LO_RL_MASK (~(3<<0)) + +/* TRIMCFG1: reg:0x58 RW */ +#define AW869XX_BIT_TRIMCFG1_RL_TRIM_SRC_MASK (~(1<<6)) +#define AW869XX_BIT_TRIMCFG1_RL_TRIM_SRC_REG (1<<6) +#define AW869XX_BIT_TRIMCFG1_RL_TRIM_SRC_EFUSE (0<<6) +#define AW869XX_BIT_TRIMCFG1_TRIM_RL_MASK (~(63<<0)) + +/* TRIMCFG2: reg:0x59 RW */ +#define AW869XX_BIT_TRIMCFG2_BST_TRIM_SRC_MASK (~(1<<6)) +#define AW869XX_BIT_TRIMCFG2_BST_TRIM_SRC_REG (1<<6) +#define AW869XX_BIT_TRIMCFG2_BST_TRIM_SRC_EFUSE (0<<6) +#define AW869XX_BIT_TRIMCFG2_TRIM_BST_MASK (~(63<<0)) + +/* TRIMCFG3: reg:0x5A RW */ +#define AW869XX_BIT_TRIMCFG3_OSC_TRIM_SRC_MASK (~(1<<7)) +#define AW869XX_BIT_TRIMCFG3_OSC_TRIM_SRC_REG (1<<7) +#define AW869XX_BIT_TRIMCFG3_OSC_TRIM_SRC_EFUSE (0<<7) +#define AW869XX_BIT_TRIMCFG3_LRA_TRIM_SRC_MASK (~(1<<6)) +#define AW869XX_BIT_TRIMCFG3_LRA_TRIM_SRC_REG (1<<6) +#define AW869XX_BIT_TRIMCFG3_LRA_TRIM_SRC_EFUSE (0<<6) +#define AW869XX_BIT_TRIMCFG3_TRIM_LRA_MASK (~(63<<0)) + +/* TRIMCFG4: reg:0x5B RW */ +/* TRIM_OSC */ + +/* PLLCFG1: reg:0x68 RW */ +#define AW869XX_BIT_PLLCFG1_PLL_TEST_EN_MASK (~(1<<6)) +#define AW869XX_BIT_PLLCFG1_PLL_TEST_ENABLE (1<<6) +#define AW869XX_BIT_PLLCFG1_PLL_TEST_DIV_MASK (~(3<<4)) +#define AW869XX_BIT_PLLCFG1_PLL_TEST_DIV_1 (0<<4) +#define AW869XX_BIT_PLLCFG1_PLL_TEST_DIV_2 (1<<4) +#define AW869XX_BIT_PLLCFG1_PLL_TEST_DIV_4 (2<<4) +#define AW869XX_BIT_PLLCFG1_PLL_TEST_DIV_8 (3<<4) +#define AW869XX_BIT_PLLCFG1_PLL_BIAS_CP1_IEN_MASK (~(1<<3)) +#define AW869XX_BIT_PLLCFG1_PLL_BIAS_CP1_IENABLE (1<<3) +#define AW869XX_BIT_PLLCFG1_PLL_VTI_CP1_IEN_MASK (~(1<<2)) +#define AW869XX_BIT_PLLCFG1_PLL_VTI_CP1_IENABLE (1<<2) +#define AW869XX_BIT_PLLCFG1_PLL_DELAY_SEL_MASK (~(1<<1)) +#define AW869XX_BIT_PLLCFG1_PLL_R1_SEL_MASK (~(1<<0)) + +/* PLLCFG2: reg:0x69 RW */ +#define AW869XX_BIT_PLLCFG2_PLL_CP1_SEL_MASK (~(0x0F<<4)) +#define AW869XX_BIT_PLLCFG2_PLL_CP1_40UA (4<<4) +#define AW869XX_BIT_PLLCFG2_PLL_CP1_50UA (5<<4) +#define AW869XX_BIT_PLLCFG2_PLL_CP1_80UA (8<<4) +#define AW869XX_BIT_PLLCFG2_PLL_CP1_100UA (10<<4) +#define AW869XX_BIT_PLLCFG2_PLL_CP2_SEL_MASK (~(0x0F<<0)) +#define AW869XX_BIT_PLLCFG2_PLL_CP2_40NA (1<<0) +#define AW869XX_BIT_PLLCFG2_PLL_CP2_600NA (8<<0) +#define AW869XX_BIT_PLLCFG2_PLL_CP2_800NA (10<<0) +#define AW869XX_BIT_PLLCFG2_PLL_CP2_1200NA (12<<0) + +/* HDRVCFG1: reg:0x6A RW */ +#define AW869XX_BIT_HDRVCFG1_EN_HD_LOW_MASK (~(1<<7)) +#define AW869XX_BIT_HDRVCFG1_EN_HD_HZ (0<<7) +#define AW869XX_BIT_HDRVCFG1_EN_HD_PD (1<<7) + +/* IOCFG1: reg:0x6B RW */ +#define AW869XX_BIT_IOCFG1_HSEN_MASK (~(1<<6)) +#define AW869XX_BIT_IOCFG1_HS_ENABLE (1<<6) +#define AW869XX_BIT_IOCFG1_HS_DISABLE (0<<6) +#define AW869XX_BIT_IOCFG1_IO_FAST_MASK (~(3<<4)) +#define AW869XX_BIT_IOCFG1_ALL_IO_FAST_ENABLE (3<<4) +#define AW869XX_BIT_IOCFG1_IIS_IO_FAST_ENABLE (2<<4) +#define AW869XX_BIT_IOCFG1_IIC_IO_FAST_ENABLE (1<<4) +#define AW869XX_BIT_IOCFG1_IO_FAST_DISABLE (0<<4) + +/* BSTCFG1: reg:0x6D RW */ +#define AW869XX_BIT_BSTCFG1_BST_PC_MASK (~(7<<1)) + +/* BSTCFG5: reg:0x71 RW */ +#define AW869XX_BIT_BSTCFG5_BST_ADJ_MASK (~(1<<7)) +#define AW869XX_BIT_BSTCFG5_BST_ADJ_HIGH (1<<7) +#define AW869XX_BIT_BSTCFG5_BST_ADJ_LOW (0<<7) + +/* OCCFG1: reg:0x74 RW */ +#define AW869XX_BIT_OCCFG1_HS_IOC_MASK (~(3<<6)) +#define AW869XX_BIT_OCCFG1_HS_IOC_3A15 (0<<6) +#define AW869XX_BIT_OCCFG1_HS_IOC_3A65 (1<<6) +#define AW869XX_BIT_OCCFG1_HS_IOC_4A15 (2<<6) +#define AW869XX_BIT_OCCFG1_HS_IOC_4A65 (3<<6) +#define AW869XX_BIT_OCCFG1_LS_IOC_MASK (~(3<<4)) +#define AW869XX_BIT_OCCFG1_LS_IOC_3A15 (0<<4) +#define AW869XX_BIT_OCCFG1_LS_IOC_3A65 (1<<4) +#define AW869XX_BIT_OCCFG1_LS_IOC_4A15 (2<<4) +#define AW869XX_BIT_OCCFG1_LS_IOC_4A65 (3<<4) +#define AW869XX_BIT_OCCFG1_OCDT_MASK (~(3<<2)) + +#define AW869XX_BIT_OCCLK_MODE_MASK (~(3<<0)) + +/* ADCCFG1: reg:0x75 RW */ +#define AW869XX_BIT_ADCCFG1_BST_SOFT_DLY_ADJ_MASK (~(1<<7)) +#define AW869XX_BIT_ADCCFG1_AD_SEL_HDP_MASK (~(1<<6)) +#define AW869XX_BIT_ADCCFG1_AD_SEL_HDN_MASK (~(1<<5)) +#define AW869XX_BIT_ADCCFG1_AD_SEL_PVDD_MASK (~(1<<4)) +#define AW869XX_BIT_ADCCFG1_AD_SEL_TEST_MASK (~(1<<3)) +#define AW869XX_BIT_ADCCFG1_AD_SEL_VBAT_MASK (~(1<<2)) +#define AW869XX_BIT_ADCCFG1_PD_D2S_DIV_MASK (~(1<<1)) +#define AW869XX_BIT_ADCCFG1_AD_SEL_D2S_MASK (~(1<<0)) + +/* D2SCFG1: reg:0x76 RW */ +#define AW869XX_BIT_D2SCFG1_CLK_TRIM_MODE_MASK (~(7<<0)) +#define AW869XX_BIT_D2SCFG1_CLK_TRIM_MODE_48K (0<<0) +#define AW869XX_BIT_D2SCFG1_CLK_TRIM_MODE_24K (1<<0) +#define AW869XX_BIT_D2SCFG1_CLK_TRIM_MODE_12K (2<<0) +#define AW869XX_BIT_D2SCFG1_CLK_TRIM_MODE_6K (3<<0) +#define AW869XX_BIT_D2SCFG1_CLK_TRIM_MODE_12M (4<<0) + +#endif diff --git a/drivers/misc/awinic/haptic_hv/Android.mk b/drivers/misc/awinic/haptic_hv/Android.mk new file mode 100644 index 000000000000..c6e310f03a41 --- /dev/null +++ b/drivers/misc/awinic/haptic_hv/Android.mk @@ -0,0 +1,18 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := aw_haptic_hv.ko +LOCAL_MODULE_TAGS := optional + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +ifeq ($(PRODUCT_HAS_AWINIC_HAPTIC),true) + KERNEL_CFLAGS += CONFIG_INPUT_AWINIC_HAPTIC=y +endif + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/awinic/haptic_hv/Kbuild b/drivers/misc/awinic/haptic_hv/Kbuild new file mode 100644 index 000000000000..9836783e34c3 --- /dev/null +++ b/drivers/misc/awinic/haptic_hv/Kbuild @@ -0,0 +1,14 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +ifneq ($(filter m y,$(CONFIG_INPUT_AWINIC_HAPTIC)),) + EXTRA_CFLAGS += -DCONFIG_INPUT_AWINIC_HAPTIC +endif + +ifneq (,$(filter felix%,$(TARGET_PRODUCT))) + EXTRA_CFLAGS += -DHAPTIC_HV_HAL_COMPAT -DAW_DURATION_DECIDE_WAVEFORM -DAW_DURATION_WAVESEQ_2=5 +endif + +obj-m += aw_haptic_hv.o +aw_haptic_hv-objs := aw869x.o aw869xx.o aw8671x.o aw8692x.o haptic_hv.o diff --git a/drivers/misc/awinic/haptic_hv/Kconfig b/drivers/misc/awinic/haptic_hv/Kconfig new file mode 100644 index 000000000000..c3ea3356921f --- /dev/null +++ b/drivers/misc/awinic/haptic_hv/Kconfig @@ -0,0 +1,16 @@ +# +# Qcom AWINIC_HAPTIC_HV Driver +# + +config AWINIC_HAPTIC_HV + tristate "AWINIC_HAPTIC_HV driver" + depends on I2C + help + Say Y here if you have Awinic AW869X/AW869XX haptic controller + chip in your system. + If unsure, say N. + To compile this driver as a module, choose M here: the + module will be called awinic_haptic_hv. + + + diff --git a/drivers/misc/awinic/haptic_hv/Makefile b/drivers/misc/awinic/haptic_hv/Makefile new file mode 100644 index 000000000000..a087dacd15ad --- /dev/null +++ b/drivers/misc/awinic/haptic_hv/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(M) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(M) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/misc/awinic/haptic_hv/aw8671x.c b/drivers/misc/awinic/haptic_hv/aw8671x.c new file mode 100644 index 000000000000..790ac4c58d53 --- /dev/null +++ b/drivers/misc/awinic/haptic_hv/aw8671x.c @@ -0,0 +1,2260 @@ +/* + * File: aw8671x.c + * + * Author: Ethan + * + * Copyright (c) 2021 AWINIC Technology CO., 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "haptic_hv.h" +#include "haptic_hv_reg.h" + +static uint8_t aw8671x_get_glb_state(struct aw_haptic *aw_haptic); +static void aw8671x_vbat_mode_config(struct aw_haptic *, uint8_t); + +/****************************************************** + * + * aw8671x codec + * + ******************************************************/ +#ifdef AW_SND_SOC_CODEC + +#ifdef KERNEL_OVER_4_19 +static const struct aw_componet_codec_ops aw_componet_codec_ops = { + .aw_snd_soc_kcontrol_codec = snd_soc_kcontrol_component, + .aw_snd_soc_codec_get_drvdata = snd_soc_component_get_drvdata, + .aw_snd_soc_add_codec_controls = snd_soc_add_component_controls, + .aw_snd_soc_unregister_codec = snd_soc_unregister_component, + .aw_snd_soc_register_codec = snd_soc_register_component, +}; +#else +static const struct aw_componet_codec_ops aw_componet_codec_ops = { + .aw_snd_soc_kcontrol_codec = snd_soc_kcontrol_codec, + .aw_snd_soc_codec_get_drvdata = snd_soc_codec_get_drvdata, + .aw_snd_soc_add_codec_controls = snd_soc_add_codec_controls, + .aw_snd_soc_unregister_codec = snd_soc_unregister_codec, + .aw_snd_soc_register_codec = snd_soc_register_codec, +}; +#endif + +static aw_snd_soc_codec_t *aw_get_codec(struct snd_soc_dai *dai) +{ +#ifdef KERNEL_OVER_4_19 + return dai->component; +#else + return dai->codec; +#endif +} + +/****************************************************** + * + * Digital Audio Interface + * + ******************************************************/ +static void aw8671x_i2s_enable(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_I2SCFG1, + AW8671X_BIT_I2SCFG1_I2S_EN_MASK, + AW8671X_BIT_I2SCFG1_I2S_ENABLE); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_I2SCFG1, + AW8671X_BIT_I2SCFG1_I2S_EN_MASK, + AW8671X_BIT_I2SCFG1_I2S_DISABLE); + } +} + +static int aw8671x_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw_haptic *aw_haptic = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + aw_info("enter"); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + mutex_lock(&aw_haptic->lock); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_SYSCTRL2, + AW8671X_BIT_SYSCTRL2_STANDBY_MASK, + AW8671X_BIT_SYSCTRL2_STANDBY_OFF); + mutex_unlock(&aw_haptic->lock); + } + + return 0; +} + +static int aw8671x_set_fmt(struct snd_soc_dai *dai, uint32_t fmt) +{ + aw_info("fmt=0x%02X", fmt); + /* supported mode: regular I2S, slave, or PDM */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != + SND_SOC_DAIFMT_CBS_CFS) { + aw_err("invalid codec master mode"); + return -EINVAL; + } + break; + default: + aw_err("unsupported DAI format %d", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + return 0; +} + +static int aw8671x_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, uint32_t freq, int dir) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw_haptic *aw_haptic = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + aw_info("freq=%d", freq); + aw_haptic->sysclk = freq; + return 0; +} + +static int aw8671x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + uint8_t tmp_val = 0; + uint8_t i2sfs_val = 0; + uint8_t i2sbck_val = 0; + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw_haptic *aw_haptic = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + aw_info("steam is capture"); + return 0; + } + + aw_info("requested rate: %d, sample size: %d", params_rate(params), + snd_pcm_format_width(params_format(params))); + + /* get rate param */ + aw_haptic->rate = params_rate(params); + + mutex_lock(&aw_haptic->lock); + + /* get bit width */ + aw_haptic->width = params_width(params); + aw_info("width = %d", aw_haptic->width); + i2sbck_val = params_width(params) * params_channels(params); + /* match bit width */ + switch (aw_haptic->width) { + case 16: + i2sfs_val = AW8671X_BIT_I2SCFG3_I2SFS_16BITS; + break; + case 24: + i2sfs_val = AW8671X_BIT_I2SCFG3_I2SFS_24BITS; + i2sbck_val = 32 * params_channels(params); + break; + case 32: + i2sfs_val = AW8671X_BIT_I2SCFG3_I2SFS_32BITS; + break; + default: + i2sfs_val = AW8671X_BIT_I2SCFG3_I2SFS_16BITS; + i2sbck_val = 16 * params_channels(params); + aw_err("width [%d]can not support", aw_haptic->width); + break; + } + aw_info("i2sfs_val=0x%02X", i2sfs_val); + /* set width */ + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_I2SCFG3, + AW8671X_BIT_I2SCFG3_I2SFS_MASK, i2sfs_val); + + /* match bck mode */ + switch (i2sbck_val) { + case 32: + i2sbck_val = AW8671X_BIT_I2SCFG3_I2SBCK_32FS; + break; + case 48: + i2sbck_val = AW8671X_BIT_I2SCFG3_I2SBCK_48FS; + break; + case 64: + i2sbck_val = AW8671X_BIT_I2SCFG3_I2SBCK_64FS; + break; + default: + aw_err("i2sbck [%d] can not support", i2sbck_val); + i2sbck_val = AW8671X_BIT_I2SCFG3_I2SBCK_32FS; + break; + } + /* set bck mode */ + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_I2SCFG3, + AW8671X_BIT_I2SCFG3_I2SBCK_MASK, i2sbck_val); + + /* check i2s cfg */ + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_I2SCFG3, &tmp_val, + AW_I2C_BYTE_ONE); + aw_info("i2scfg1=0x%02X", tmp_val); + + mutex_unlock(&aw_haptic->lock); + + return 0; +} + +static int aw8671x_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + uint8_t reg_val = 0; + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw_haptic *aw_haptic = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + if (stream == SNDRV_PCM_STREAM_CAPTURE) { + aw_info("steam is capture"); + return 0; + } + aw_info("mute state=%d", mute); + if (mute) { + mutex_lock(&aw_haptic->lock); + aw8671x_i2s_enable(aw_haptic, false); + mutex_unlock(&aw_haptic->lock); + } else { + mutex_lock(&aw_haptic->lock); + aw8671x_i2s_enable(aw_haptic, true); + usleep_range(2000, 3000); + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_SYSST, ®_val, + AW_I2C_BYTE_ONE); + aw_info("sysst=0x%02X", reg_val); + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_SYSST2, ®_val, + AW_I2C_BYTE_ONE); + aw_info("sysst2=0x%02X", reg_val); + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_SYSER, ®_val, + AW_I2C_BYTE_ONE); + aw_info("syser=0x%02X", reg_val); + reg_val = aw8671x_get_glb_state(aw_haptic); + if (reg_val != 0x0a) { + aw_err("i2s config err, glb_state=0x%02X", reg_val); + aw8671x_i2s_enable(aw_haptic, false); + } else { + aw_info("i2s config pass, glb_state=0x%02X", reg_val); + } + mutex_unlock(&aw_haptic->lock); + } + + return 0; +} + +static void aw8671x_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw_haptic *aw_haptic = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + aw_info("enter"); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aw_haptic->rate = 0; + mutex_lock(&aw_haptic->lock); + /* aw8671x_i2s_enable(aw_haptic, false); */ + mutex_unlock(&aw_haptic->lock); + } +} + +static const struct snd_soc_dai_ops aw8671x_dai_ops = { + .startup = aw8671x_startup, + .set_fmt = aw8671x_set_fmt, + .set_sysclk = aw8671x_set_dai_sysclk, + .hw_params = aw8671x_hw_params, + .mute_stream = aw8671x_mute, + .shutdown = aw8671x_shutdown, +}; + +static struct snd_soc_dai_driver aw8671x_dai[] = { + { + .name = "aw8671x-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AW8671X_RATES, + .formats = AW8671X_FORMATS, + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AW8671X_RATES, + .formats = AW8671X_FORMATS, + }, + .ops = &aw8671x_dai_ops, + .symmetric_rates = 1, + }, +}; + +/***************************************************** + * + * codec driver + * + *****************************************************/ +static void aw8671x_add_codec_controls(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); +} + +static int aw8671x_codec_probe(aw_snd_soc_codec_t *codec) +{ + int ret = -1; + struct aw_haptic *aw_haptic = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + aw_info("enter"); + aw_haptic->codec = codec; + aw8671x_add_codec_controls(aw_haptic); + aw_info("exit"); + ret = 0; + return ret; +} + +#ifdef KERNEL_OVER_4_19 +static void aw8671x_codec_remove(struct snd_soc_component *component) +{ + /* + * struct aw_haptic *aw_haptic = + * aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + */ + aw_info("enter"); +} +#else +static int aw8671x_codec_remove(aw_snd_soc_codec_t *codec) +{ + /* + * struct aw_haptic *aw_haptic = + * aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + */ + aw_info("enter"); + + return 0; +} +#endif + +static uint32_t aw8671x_codec_read(aw_snd_soc_codec_t *codec, uint32_t reg) +{ + uint8_t value = 0; + int ret = -1; + struct aw_haptic *aw_haptic = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + aw_info("enter"); + ret = haptic_hv_i2c_reads(aw_haptic, reg, &value, AW_I2C_BYTE_ONE); + if (ret < 0) { + aw_err("read register failed"); + return ret; + } + return ret; +} + +static int aw8671x_codec_write(aw_snd_soc_codec_t *codec, + uint32_t reg, uint32_t value) +{ + uint8_t val = (uint8_t)value; + int ret = -1; + struct aw_haptic *aw_haptic = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + aw_info("enter ,reg:0x%02X = 0x%02X", reg, value); + ret = haptic_hv_i2c_writes(aw_haptic, (uint8_t) reg, &val, + AW_I2C_BYTE_ONE); + + return ret; +} + +#ifdef AW_KERNEL_VER_OVER_4_19 +static struct snd_soc_component_driver soc_codec_dev_aw8671x = { + .probe = aw8671x_codec_probe, + .remove = aw8671x_codec_remove, + .read = aw8671x_codec_read, + .write = aw8671x_codec_write, +}; +#else +static struct snd_soc_codec_driver soc_codec_dev_aw8671x = { + .probe = aw8671x_codec_probe, + .remove = aw8671x_codec_remove, + .read = aw8671x_codec_read, + .write = aw8671x_codec_write, + .reg_cache_size = AW_HAPTIC_REG_MAX, + .reg_word_size = 2, +}; +#endif + +static int aw8671x_snd_soc_init(struct device *dev) +{ + int ret = 0; + struct snd_soc_dai_driver *dai; + + dev_set_name(dev, "%s", AW_I2C_NAME); + /* register codec */ + dai = devm_kzalloc(dev, sizeof(aw8671x_dai), GFP_KERNEL); + if (!dai) { + aw_err("failed to register aw8671x"); + return -ENOMEM; + } + memcpy(dai, aw8671x_dai, sizeof(aw8671x_dai)); + aw_info("dai->name(%s)", dai->name); + + ret = + aw_componet_codec_ops.aw_snd_soc_register_codec(dev, + &soc_codec_dev_aw8671x, + dai, + ARRAY_SIZE + (aw8671x_dai)); + if (ret < 0) { + aw_err("failed to register aw8671x: %d", ret); + return ret; + } + return 0; +} + +#endif + +static int aw8671x_check_qualify(struct aw_haptic *aw_haptic) +{ + int ret = -1; + uint8_t reg_val = 0; + + aw_info("enter"); + ret = haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_EFCFG5, ®_val, + AW_I2C_BYTE_ONE); + if (ret < 0) + return ret; + if (!(reg_val & 0x80)) { + aw_err("unqualified chip!"); + return -ERANGE; + } + return 0; +} + +static void aw8671x_set_pwm(struct aw_haptic *aw_haptic, uint8_t mode) +{ + switch (mode) { + case AW_PWM_48K: + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_SYSCTRL2, + AW8671X_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW8671X_BIT_SYSCTRL2_RATE_48K); + break; + case AW_PWM_24K: + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_SYSCTRL2, + AW8671X_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW8671X_BIT_SYSCTRL2_RATE_24K); + break; + case AW_PWM_12K: + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_SYSCTRL2, + AW8671X_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW8671X_BIT_SYSCTRL2_RATE_12K); + break; + default: + break; + } +} + +static void aw8671x_set_gain(struct aw_haptic *aw_haptic, uint8_t gain) +{ + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_PLAYCFG2, &gain, + AW_I2C_BYTE_ONE); +} + +static void aw8671x_set_bst_peak_cur(struct aw_haptic *aw_haptic) +{ + +} + +static void aw8671x_set_bst_vol(struct aw_haptic *aw_haptic, uint32_t bst_vol) +{ + uint8_t reg_val = 0; + + if (bst_vol < AW8671X_CHARGEPUMP_CODE1) + reg_val = 0x00; + else if (bst_vol < AW8671X_CHARGEPUMP_CODE2) + reg_val = 0x01; + else if (bst_vol < AW8671X_CHARGEPUMP_CODE3) + reg_val = 0x02; + else if (bst_vol < AW8671X_CHARGEPUMP_CODE4) + reg_val = 0x03; + else + reg_val = 0x04; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PLAYCFG1, + AW8671X_BIT_PLAYCFG1_CP_CODE_MASK, reg_val); +} + +static void aw8671x_set_wav_seq(struct aw_haptic *aw_haptic, uint8_t wav, + uint8_t seq) +{ + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_WAVCFG1 + wav, &seq, + AW_I2C_BYTE_ONE); +} + +static void aw8671x_set_wav_loop(struct aw_haptic *aw_haptic, uint8_t wav, + uint8_t loop) +{ + uint8_t tmp = 0; + + if (wav % 2) { + tmp = loop << 0; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_WAVCFG9 + (wav / 2), + AW8671X_BIT_WAVLOOP_SEQ_EVEN_MASK, tmp); + } else { + tmp = loop << 4; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_WAVCFG9 + (wav / 2), + AW8671X_BIT_WAVLOOP_SEQ_ODD_MASK, tmp); + } +} + +static void aw8671x_set_rtp_data(struct aw_haptic *aw_haptic, uint8_t *data, + uint32_t len) +{ + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_RTPDATA, data, len); +} + +static void aw8671x_set_rtp_aei(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_SYSINTM, + AW8671X_BIT_SYSINTM_FF_AEM_MASK, + AW8671X_BIT_SYSINTM_FF_AEM_ON); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_SYSINTM, + AW8671X_BIT_SYSINTM_FF_AEM_MASK, + AW8671X_BIT_SYSINTM_FF_AEM_OFF); + } +} + +static void aw8671x_set_ram_addr(struct aw_haptic *aw_haptic) +{ + uint8_t ram_addr_l = (uint8_t)(aw_haptic->ram.base_addr & 0x00ff); + + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_RAMADDRH, + AW8671X_BIT_RAMADDRH_MASK, + (uint8_t)(aw_haptic->ram.base_addr >> 8)); + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_RAMADDRL, &ram_addr_l, + AW_I2C_BYTE_ONE); +} + +static void aw8671x_auto_break_mode(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PLAYCFG3, + AW8671X_BIT_PLAYCFG3_BRK_EN_MASK, + AW8671X_BIT_PLAYCFG3_BRK_ENABLE); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PLAYCFG3, + AW8671X_BIT_PLAYCFG3_BRK_EN_MASK, + AW8671X_BIT_PLAYCFG3_BRK_DISABLE); + } +} + +static void aw8671x_f0_detect(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_CONTCFG1, + AW8671X_BIT_CONTCFG1_EN_F0_DET_MASK, + AW8671X_BIT_CONTCFG1_F0_DET_ENABLE); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_CONTCFG1, + AW8671X_BIT_CONTCFG1_EN_F0_DET_MASK, + AW8671X_BIT_CONTCFG1_F0_DET_DISABLE); + } +} + +static uint8_t aw8671x_get_glb_state(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_GLBRD5, &state, + AW_I2C_BYTE_ONE); + aw_dbg("glb state value is 0x%02X", state); + return state; +} + +static void aw8671x_play_go(struct aw_haptic *aw_haptic, bool flag) +{ + uint8_t delay_20us = AW8671X_BIT_START_DLY_20US; + uint8_t delay_2p5ms = AW8671X_BIT_START_DLY_2P5MS; + uint8_t go_on = AW8671X_BIT_PLAYCFG4_GO_ON; + uint8_t stop_on = AW8671X_BIT_PLAYCFG4_STOP_ON; + + aw_dbg("enter, flag = %d", flag); + if (flag) { + if (aw_haptic->info.is_enabled_one_wire) { + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_GLBCFG2, + &delay_20us, AW_I2C_BYTE_ONE); + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_PLAYCFG4, + &go_on, AW_I2C_BYTE_ONE); + usleep_range(1000, 1500); + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_GLBCFG2, + &delay_2p5ms, AW_I2C_BYTE_ONE); + } else { + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_PLAYCFG4, + &go_on, AW_I2C_BYTE_ONE); + } + } else { + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_PLAYCFG4, + &stop_on, AW_I2C_BYTE_ONE); + } +} + +static int aw8671x_wait_enter_standby(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int count = 100; + + while (count--) { + reg_val = aw8671x_get_glb_state(aw_haptic); + if (reg_val == AW8671X_BIT_GLBRD5_STATE_STANDBY) { + aw_info("entered standby!"); + return 0; + } + aw_dbg("wait for standby"); + usleep_range(2000, 2500); + } + aw_err("do not enter standby automatically"); + return -ERANGE; +} + +static void aw8671x_bst_mode_config(struct aw_haptic *aw_haptic, uint8_t mode) +{ + switch (mode) { + case AW_BST_BOOST_MODE: + aw_info("haptic bst mode = boost"); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PLAYCFG1, + AW8671X_BIT_PLAYCFG1_CP_MODE_MASK, + AW8671X_BIT_PLAYCFG1_CP_MODE_CHARGEPUMP); + break; + case AW_BST_BYPASS_MODE: + aw_info("haptic bst mode = bypass"); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PLAYCFG1, + AW8671X_BIT_PLAYCFG1_CP_MODE_MASK, + AW8671X_BIT_PLAYCFG1_CP_MODE_BYPASS); + break; + default: + aw_err("error mode %d", mode); + break; + } +} + +static void aw8671x_play_mode(struct aw_haptic *aw_haptic, uint8_t play_mode) +{ + switch (play_mode) { + case AW_STANDBY_MODE: + aw_info("enter standby mode"); + aw_haptic->play_mode = AW_STANDBY_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_SYSCTRL2, + AW8671X_BIT_SYSCTRL2_STANDBY_MASK, + AW8671X_BIT_SYSCTRL2_STANDBY_ON); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_SYSCTRL2, + AW8671X_BIT_SYSCTRL2_STANDBY_MASK, + AW8671X_BIT_SYSCTRL2_STANDBY_OFF); + break; + case AW_RAM_MODE: + aw_info("enter ram mode"); + aw_haptic->play_mode = AW_RAM_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PLAYCFG3, + AW8671X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8671X_BIT_PLAYCFG3_PLAY_MODE_RAM); + /* bst mode */ + aw8671x_bst_mode_config(aw_haptic, AW_BST_BOOST_MODE); + aw8671x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + break; + case AW_RAM_LOOP_MODE: + aw_info("enter ram loop mode"); + aw_haptic->play_mode = AW_RAM_LOOP_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PLAYCFG3, + AW8671X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8671X_BIT_PLAYCFG3_PLAY_MODE_RAM); + /* bst mode */ + aw8671x_bst_mode_config(aw_haptic, AW_BST_BYPASS_MODE); + aw8671x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + break; + case AW_RTP_MODE: + aw_info("enter rtp mode"); + aw_haptic->play_mode = AW_RTP_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PLAYCFG3, + AW8671X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8671X_BIT_PLAYCFG3_PLAY_MODE_RTP); + /* bst mode */ + aw8671x_bst_mode_config(aw_haptic, AW_BST_BOOST_MODE); + aw8671x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + break; + case AW_TRIG_MODE: + aw_info("enter trig mode"); + aw_haptic->play_mode = AW_TRIG_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PLAYCFG3, + AW8671X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8671X_BIT_PLAYCFG3_PLAY_MODE_RAM); + aw8671x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + break; + case AW_CONT_MODE: + aw_info("enter cont mode"); + aw_haptic->play_mode = AW_CONT_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PLAYCFG3, + AW8671X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8671X_BIT_PLAYCFG3_PLAY_MODE_CONT); + /* bst mode */ + aw8671x_bst_mode_config(aw_haptic, AW_BST_BYPASS_MODE); + aw8671x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_HW_COMP_MODE); + break; + default: + aw_err("play mode %d error", play_mode); + break; + } +} + +static void aw8671x_stop(struct aw_haptic *aw_haptic) +{ + aw8671x_play_go(aw_haptic, false); + aw8671x_wait_enter_standby(aw_haptic); + aw8671x_play_mode(aw_haptic, AW_STANDBY_MODE); +} + +static void aw8671x_ram_init(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_SYSCTRL1, + AW8671X_BIT_SYSCTRL1_RAMINIT_MASK, + AW8671X_BIT_SYSCTRL1_RAMINIT_ON); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_SYSCTRL1, + AW8671X_BIT_SYSCTRL1_RAMINIT_MASK, + AW8671X_BIT_SYSCTRL1_RAMINIT_OFF); + } +} + +static void aw8671x_upload_lra(struct aw_haptic *aw_haptic, uint32_t flag) +{ + switch (flag) { + case AW_WRITE_ZERO: + aw_info("write zero to trim_lra!"); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_TRIMCFG2, + AW8671X_BIT_TRIMCFG2_TRIM_LRA_MASK, + 0x00); + break; + case AW_F0_CALI_LRA: + aw_info("write f0_cali_data to trim_lra = 0x%02X", + aw_haptic->f0_cali_data); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_TRIMCFG2, + AW8671X_BIT_TRIMCFG2_TRIM_LRA_MASK, + (char)aw_haptic->f0_cali_data); + break; + case AW_OSC_CALI_LRA: + aw_info("write osc_cali_data to trim_lra = 0x%02X", + aw_haptic->osc_cali_data); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_TRIMCFG2, + AW8671X_BIT_TRIMCFG2_TRIM_LRA_MASK, + (char)aw_haptic->osc_cali_data); + break; + default: + aw_err("error param!"); + break; + } +} + +static uint8_t aw8671x_get_trim_lra(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_TRIMCFG2, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= 0x3F; + return reg_val; +} + +static void aw8671x_vbat_mode_config(struct aw_haptic *aw_haptic, uint8_t flag) +{ + if (flag == AW_CONT_VBAT_HW_COMP_MODE) { + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_VBATCTRL, + AW8671X_BIT_VBATCTRL_VBAT_MODE_MASK, + AW8671X_BIT_VBATCTRL_VBAT_MODE_HW); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_VBATCTRL, + AW8671X_BIT_VBATCTRL_VBAT_MODE_MASK, + AW8671X_BIT_VBATCTRL_VBAT_MODE_SW); + } +} + +static void aw8671x_protect_config(struct aw_haptic *aw_haptic, + uint8_t addr, uint8_t val) +{ + aw_info("enter"); + if (addr == 1) { + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PWMCFG2, + AW8671X_BIT_PWMCFG2_PRCT_MODE_MASK, + AW8671X_BIT_PWMCFG2_PRCT_MODE_VALID); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PWMCFG1, + AW8671X_BIT_PWMCFG1_PRC_EN_MASK, + AW8671X_BIT_PWMCFG1_PRC_ENABLE); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PWMCFG3, + AW8671X_BIT_PWMCFG3_PR_EN_MASK, + AW8671X_BIT_PWMCFG3_PR_ENABLE); + } else if (addr == 0) { + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PWMCFG2, + AW8671X_BIT_PWMCFG2_PRCT_MODE_MASK, + AW8671X_BIT_PWMCFG2_PRCT_MODE_INVALID); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PWMCFG1, + AW8671X_BIT_PWMCFG1_PRC_EN_MASK, + AW8671X_BIT_PWMCFG1_PRC_DISABLE); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PWMCFG3, + AW8671X_BIT_PWMCFG3_PR_EN_MASK, + AW8671X_BIT_PWMCFG3_PR_DISABLE); + } else if (addr == AW8671X_REG_PWMCFG1) { + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PWMCFG1, + AW8671X_BIT_PWMCFG1_PRCTIME_MASK, val); + } else if (addr == AW8671X_REG_PWMCFG3) { + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PWMCFG3, + AW8671X_BIT_PWMCFG3_PRLVL_MASK, val); + } else if (addr == AW8671X_REG_PWMCFG4) { + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_PWMCFG4, &val, + AW_I2C_BYTE_ONE); + } +} + +static void aw8671x_cont_config(struct aw_haptic *aw_haptic) +{ + /* uint8_t drv1_time = 0xFF; */ + uint8_t drv2_time = 0xFF; + + /* work mode */ + aw8671x_play_mode(aw_haptic, AW_CONT_MODE); + /* f0 driver level */ + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_CONTCFG6, + (AW8671X_BIT_CONTCFG6_TRACK_EN_MASK & + AW8671X_BIT_CONTCFG6_DRV1_LVL_MASK), + (AW8671X_BIT_CONTCFG6_TRACK_DISABLE | + aw_haptic->info.cont_drv1_lvl)); + + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_CONTCFG7, + &aw_haptic->info.cont_drv2_lvl, AW_I2C_BYTE_ONE); + /* DRV1_TIME */ + /* haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_CONTCFG8, &drv1_time, + AW_I2C_BYTE_ONE); */ + /* DRV2_TIME */ + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_CONTCFG9, &drv2_time, + AW_I2C_BYTE_ONE); + /* cont play go */ + aw8671x_play_go(aw_haptic, true); +} + +static void aw8671x_one_wire_init(struct aw_haptic *aw_haptic) +{ + uint8_t trig_prio = 0x6c; + uint8_t delay_2p5ms = AW8671X_BIT_START_DLY_2P5MS; + + aw_info("enter"); + /*if enable one-wire, trig1 priority must be less than trig2 and trig3*/ + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_GLBCFG4, &trig_prio, + AW_I2C_BYTE_ONE); + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_GLBCFG2, &delay_2p5ms, + AW_I2C_BYTE_ONE); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_TRGCFG8, + AW8671X_BIT_TRGCFG8_TRG_ONEWIRE_MASK, + AW8671X_BIT_TRGCFG8_TRG_ONEWIRE_ENABLE); +} + +static void aw8671x_i2s_init(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_SYSCTRL2, + AW8671X_BIT_SYSCTRL2_I2S_PIN_MASK, + AW8671X_BIT_SYSCTRL2_I2S_PIN_I2S); +} + +static void aw8671x_trig1_param_init(struct aw_haptic *aw_haptic) +{ + aw_haptic->trig[0].trig_level = aw_haptic->info.trig_cfg[0]; + aw_haptic->trig[0].trig_polar = aw_haptic->info.trig_cfg[1]; + aw_haptic->trig[0].pos_enable = aw_haptic->info.trig_cfg[2]; + aw_haptic->trig[0].pos_sequence = aw_haptic->info.trig_cfg[3]; + aw_haptic->trig[0].neg_enable = aw_haptic->info.trig_cfg[4]; + aw_haptic->trig[0].neg_sequence = aw_haptic->info.trig_cfg[5]; + aw_haptic->trig[0].trig_brk = aw_haptic->info.trig_cfg[6]; + aw_haptic->trig[0].trig_bst = aw_haptic->info.trig_cfg[7]; +} + +static void aw8671x_trig2_param_init(struct aw_haptic *aw_haptic) +{ + aw_haptic->trig[1].trig_level = aw_haptic->info.trig_cfg[8]; + aw_haptic->trig[1].trig_polar = aw_haptic->info.trig_cfg[9]; + aw_haptic->trig[1].pos_enable = aw_haptic->info.trig_cfg[10]; + aw_haptic->trig[1].pos_sequence = aw_haptic->info.trig_cfg[11]; + aw_haptic->trig[1].neg_enable = aw_haptic->info.trig_cfg[12]; + aw_haptic->trig[1].neg_sequence = aw_haptic->info.trig_cfg[13]; + aw_haptic->trig[1].trig_brk = aw_haptic->info.trig_cfg[14]; + aw_haptic->trig[1].trig_bst = aw_haptic->info.trig_cfg[15]; +} + +static void aw8671x_trig3_param_init(struct aw_haptic *aw_haptic) +{ + aw_haptic->trig[2].trig_level = aw_haptic->info.trig_cfg[16]; + aw_haptic->trig[2].trig_polar = aw_haptic->info.trig_cfg[17]; + aw_haptic->trig[2].pos_enable = aw_haptic->info.trig_cfg[18]; + aw_haptic->trig[2].pos_sequence = aw_haptic->info.trig_cfg[19]; + aw_haptic->trig[2].neg_enable = aw_haptic->info.trig_cfg[20]; + aw_haptic->trig[2].neg_sequence = aw_haptic->info.trig_cfg[21]; + aw_haptic->trig[2].trig_brk = aw_haptic->info.trig_cfg[22]; + aw_haptic->trig[2].trig_bst = aw_haptic->info.trig_cfg[23]; +} + +static void aw8671x_trig1_param_config(struct aw_haptic *aw_haptic) +{ + uint8_t trig_config = 0; + + if (aw_haptic->trig[0].trig_level) + trig_config |= AW8671X_BIT_TRGCFG7_TRG1_MODE_LEVEL; + else + trig_config |= AW8671X_BIT_TRGCFG7_TRG1_MODE_EDGE; + if (aw_haptic->trig[0].trig_polar) + trig_config |= AW8671X_BIT_TRGCFG7_TRG1_POLAR_NEG; + else + trig_config |= AW8671X_BIT_TRGCFG7_TRG1_POLAR_POS; + if (aw_haptic->trig[0].trig_brk) + trig_config |= AW8671X_BIT_TRGCFG7_TRG1_AUTO_BRK_ENABLE; + else + trig_config |= AW8671X_BIT_TRGCFG7_TRG1_AUTO_BRK_DISABLE; + if (aw_haptic->trig[0].trig_bst) + trig_config |= AW8671X_BIT_TRGCFG7_TRG1_BST_ENABLE; + else + trig_config |= AW8671X_BIT_TRGCFG7_TRG1_BST_DISABLE; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_TRGCFG7, + (AW8671X_BIT_TRGCFG7_TRG1_MODE_MASK & + AW8671X_BIT_TRGCFG7_TRG1_POLAR_MASK & + AW8671X_BIT_TRGCFG7_TRG1_AUTO_BRK_MASK & + AW8671X_BIT_TRGCFG7_TRG1_BST_MASK), + trig_config); + + trig_config = 0; + if (aw_haptic->trig[0].pos_enable) + trig_config |= AW8671X_BIT_TRG_ENABLE; + else + trig_config |= AW8671X_BIT_TRG_DISABLE; + trig_config |= aw_haptic->trig[0].pos_sequence; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_TRGCFG1, + (AW8671X_BIT_TRG_ENABLE_MASK & + AW8671X_BIT_TRG_SEQ_MASK), trig_config); + + trig_config = 0; + if (aw_haptic->trig[0].neg_enable) + trig_config |= AW8671X_BIT_TRG_ENABLE; + else + trig_config |= AW8671X_BIT_TRG_DISABLE; + trig_config |= aw_haptic->trig[0].neg_sequence; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_TRGCFG4, + (AW8671X_BIT_TRG_ENABLE_MASK & + AW8671X_BIT_TRG_SEQ_MASK), trig_config); +} + +static void aw8671x_trig2_param_config(struct aw_haptic *aw_haptic) +{ + uint8_t trig_config = 0; + + if (aw_haptic->trig[1].trig_level) + trig_config |= AW8671X_BIT_TRGCFG7_TRG2_MODE_LEVEL; + else + trig_config |= AW8671X_BIT_TRGCFG7_TRG2_MODE_EDGE; + if (aw_haptic->trig[1].trig_polar) + trig_config |= AW8671X_BIT_TRGCFG7_TRG2_POLAR_NEG; + else + trig_config |= AW8671X_BIT_TRGCFG7_TRG2_POLAR_POS; + if (aw_haptic->trig[1].trig_brk) + trig_config |= AW8671X_BIT_TRGCFG7_TRG2_AUTO_BRK_ENABLE; + else + trig_config |= AW8671X_BIT_TRGCFG7_TRG2_AUTO_BRK_DISABLE; + if (aw_haptic->trig[1].trig_bst) + trig_config |= AW8671X_BIT_TRGCFG7_TRG2_BST_ENABLE; + else + trig_config |= AW8671X_BIT_TRGCFG7_TRG2_BST_DISABLE; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_TRGCFG7, + (AW8671X_BIT_TRGCFG7_TRG2_MODE_MASK & + AW8671X_BIT_TRGCFG7_TRG2_POLAR_MASK & + AW8671X_BIT_TRGCFG7_TRG2_AUTO_BRK_MASK & + AW8671X_BIT_TRGCFG7_TRG2_BST_MASK), + trig_config); + + trig_config = 0; + if (aw_haptic->trig[1].pos_enable) + trig_config |= AW8671X_BIT_TRG_ENABLE; + else + trig_config |= AW8671X_BIT_TRG_DISABLE; + trig_config |= aw_haptic->trig[1].pos_sequence; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_TRGCFG2, + (AW8671X_BIT_TRG_ENABLE_MASK & + AW8671X_BIT_TRG_SEQ_MASK), trig_config); + + trig_config = 0; + if (aw_haptic->trig[1].neg_enable) + trig_config |= AW8671X_BIT_TRG_ENABLE; + else + trig_config |= AW8671X_BIT_TRG_DISABLE; + trig_config |= aw_haptic->trig[1].neg_sequence; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_TRGCFG5, + (AW8671X_BIT_TRG_ENABLE_MASK & + AW8671X_BIT_TRG_SEQ_MASK), trig_config); +} + +static void aw8671x_trig3_param_config(struct aw_haptic *aw_haptic) +{ + uint8_t trig_config = 0; + + if (aw_haptic->trig[2].trig_level) + trig_config |= AW8671X_BIT_TRGCFG8_TRG3_MODE_LEVEL; + else + trig_config |= AW8671X_BIT_TRGCFG8_TRG3_MODE_EDGE; + if (aw_haptic->trig[2].trig_polar) + trig_config |= AW8671X_BIT_TRGCFG8_TRG3_POLAR_NEG; + else + trig_config |= AW8671X_BIT_TRGCFG8_TRG3_POLAR_POS; + if (aw_haptic->trig[2].trig_brk) + trig_config |= AW8671X_BIT_TRGCFG8_TRG3_AUTO_BRK_ENABLE; + else + trig_config |= AW8671X_BIT_TRGCFG8_TRG3_AUTO_BRK_DISABLE; + if (aw_haptic->trig[2].trig_bst) + trig_config |= AW8671X_BIT_TRGCFG8_TRG3_BST_ENABLE; + else + trig_config |= AW8671X_BIT_TRGCFG8_TRG3_BST_DISABLE; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_TRGCFG8, + (AW8671X_BIT_TRGCFG8_TRG3_MODE_MASK & + AW8671X_BIT_TRGCFG8_TRG3_POLAR_MASK & + AW8671X_BIT_TRGCFG8_TRG3_AUTO_BRK_MASK & + AW8671X_BIT_TRGCFG8_TRG3_BST_MASK), + trig_config); + + trig_config = 0; + if (aw_haptic->trig[2].pos_enable) + trig_config |= AW8671X_BIT_TRG_ENABLE; + else + trig_config |= AW8671X_BIT_TRG_DISABLE; + trig_config |= aw_haptic->trig[2].pos_sequence; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_TRGCFG3, + (AW8671X_BIT_TRG_ENABLE_MASK & + AW8671X_BIT_TRG_SEQ_MASK), trig_config); + + trig_config = 0; + if (aw_haptic->trig[2].neg_enable) + trig_config |= AW8671X_BIT_TRG_ENABLE; + else + trig_config |= AW8671X_BIT_TRG_DISABLE; + trig_config |= aw_haptic->trig[2].neg_sequence; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_TRGCFG6, + (AW8671X_BIT_TRG_ENABLE_MASK & + AW8671X_BIT_TRG_SEQ_MASK), trig_config); +} + +static void aw8671x_auto_bst_enable(struct aw_haptic *aw_haptic, uint8_t flag) +{ + aw_haptic->auto_boost = flag; + + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PLAYCFG3, + AW8671X_BIT_PLAYCFG3_AUTO_BST_MASK, + AW8671X_BIT_PLAYCFG3_AUTO_BST_ENABLE); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_PLAYCFG3, + AW8671X_BIT_PLAYCFG3_AUTO_BST_MASK, + AW8671X_BIT_PLAYCFG3_AUTO_BST_DISABLE); + } +} + +static void aw8671x_interrupt_setup(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_info("reg SYSINT=0x%02X", reg_val); + /* edge int mode */ + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_SYSCTRL3, + (AW8671X_BIT_SYSCTRL3_INT_MODE_MASK & + AW8671X_BIT_SYSCTRL3_INT_EDGE_MODE_MASK), + (AW8671X_BIT_SYSCTRL3_INT_MODE_EDGE | + AW8671X_BIT_SYSCTRL3_INT_EDGE_MODE_POS)); + /* int enable */ + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_SYSINTM, + (AW8671X_BIT_SYSINTM_BST_OVPM_MASK & + AW8671X_BIT_SYSINTM_UVLM_MASK & + AW8671X_BIT_SYSINTM_OCDM_MASK & + AW8671X_BIT_SYSINTM_OTM_MASK), + (AW8671X_BIT_SYSINTM_BST_OVPM_OFF | + AW8671X_BIT_SYSINTM_UVLM_ON | + AW8671X_BIT_SYSINTM_OCDM_ON | + AW8671X_BIT_SYSINTM_OTM_ON)); +} + +static int aw8671x_judge_rtp_going(struct aw_haptic *aw_haptic) +{ + uint8_t glb_state = 0; + uint8_t rtp_state = 0; + + glb_state = aw8671x_get_glb_state(aw_haptic); + if (glb_state == AW8671X_BIT_GLBRD5_STATE_RTP_GO) { + rtp_state = 1; /*is going on */ + aw_info("rtp_routine_on"); + } + return rtp_state; +} + +static void aw8671x_get_ram_data(struct aw_haptic *aw_haptic, char *buf) +{ + int i = 0; + int size = 0; + + while (i < aw_haptic->ram.len) { + if ((aw_haptic->ram.len - i) < AW_RAMDATA_RD_BUFFER_SIZE) + size = aw_haptic->ram.len - i; + else + size = AW_RAMDATA_RD_BUFFER_SIZE; + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_RAMDATA, buf + i, + size); + i += size; + } +} + +static void aw8671x_get_first_wave_addr(struct aw_haptic *aw_haptic, + uint8_t *wave_addr) +{ + uint8_t reg_array[3] = {0}; + + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_RAMDATA, reg_array, + AW_I2C_BYTE_THREE); + wave_addr[0] = reg_array[1]; + wave_addr[1] = reg_array[2]; +} + +static void aw8671x_get_wav_seq(struct aw_haptic *aw_haptic, uint32_t len) +{ + uint8_t i = 0; + uint8_t reg_val[AW_SEQUENCER_SIZE] = {0}; + + if (len > AW_SEQUENCER_SIZE) + len = AW_SEQUENCER_SIZE; + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_WAVCFG1, reg_val, len); + for (i = 0; i < len; i++) + aw_haptic->seq[i] = reg_val[i]; +} + +static size_t aw8671x_get_wav_loop(struct aw_haptic *aw_haptic, char *buf) +{ + uint8_t i = 0; + uint8_t reg_val[AW_SEQUENCER_LOOP_SIZE] = {0}; + size_t count = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_WAVCFG9, reg_val, + AW_SEQUENCER_LOOP_SIZE); + for (i = 0; i < AW_SEQUENCER_LOOP_SIZE; i++) { + aw_haptic->loop[i * 2 + 0] = (reg_val[i] >> 4) & 0x0F; + aw_haptic->loop[i * 2 + 1] = (reg_val[i] >> 0) & 0x0F; + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d loop: 0x%02x\n", i * 2 + 1, + aw_haptic->loop[i * 2 + 0]); + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d loop: 0x%02x\n", i * 2 + 2, + aw_haptic->loop[i * 2 + 1]); + } + return count; +} + +static void aw8671x_irq_clear(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_info("reg SYSINT=0x%02X", reg_val); +} + +static uint8_t aw8671x_get_prctmode(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_DETCFG1, ®_val, + AW_I2C_BYTE_ONE); + return reg_val; + +} + +static int aw8671x_get_irq_state(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int ret = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_dbg("reg SYSINT=0x%02X", reg_val); + + if (reg_val & AW8671X_BIT_SYSINT_LOW_VBATI) { + ret = AW_IRQ_LOW_VBAT; + aw_err("chip low_vbat int error"); + } + + if (reg_val & AW8671X_BIT_SYSINT_UVLI) { + ret = AW_IRQ_UVLO; + aw_err("chip uvlo int error"); + } + + if (reg_val & AW8671X_BIT_SYSINT_OCDI) { + ret = AW_IRQ_OCD; + aw_err("chip over current int error"); + } + + if (reg_val & AW8671X_BIT_SYSINT_OTI) { + ret = AW_IRQ_OT; + aw_err("chip over temperature int error"); + } + + if (reg_val & AW8671X_BIT_SYSINT_DONEI) { + ret = AW_IRQ_DONE; + aw_info("chip playback done"); + } + if (reg_val & AW8671X_BIT_SYSINT_FF_AFI) { + ret = AW_IRQ_ALMOST_FULL; + aw_info("aw_haptic rtp mode fifo almost full!"); + } + + if (reg_val & AW8671X_BIT_SYSINT_FF_AEI) + ret = AW_IRQ_ALMOST_EMPTY; + + return ret; +} + +static int aw8671x_read_f0(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val[2] = {0}; + uint32_t f0_reg = 0; + uint64_t f0_tmp = 0; + +#ifdef AW_LRA_F0_DEFAULT + /* lra_f0 */ + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_CONTCFG14, reg_val, + AW_I2C_BYTE_TWO); + f0_reg = (reg_val[0] << 8) | reg_val[1]; + if (!f0_reg) { + aw_haptic->f0 = 0; + aw_err("lra_f0 is error, f0_reg=0"); + return -ERANGE; + } + f0_tmp = AW8671X_F0_FARMULA(f0_reg); + aw_haptic->f0 = (uint32_t)f0_tmp; + aw_info("lra_f0=%d", aw_haptic->f0); +#else + /* cont_f0 */ + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_CONTCFG16, reg_val, + AW_I2C_BYTE_TWO); + f0_reg = (reg_val[0] << 8) | reg_val[1]; + if (!f0_reg) { + aw_haptic->f0 = 0; + aw_err("cont_f0 is error, f0_reg=0"); + return -ERANGE; + } + f0_tmp = AW8671X_F0_FARMULA(f0_reg); + aw_haptic->f0 = (uint32_t)f0_tmp; + aw_info("cont_f0=%d", aw_haptic->f0); +#endif + return 0; +} + +static int aw8671x_get_f0(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint8_t brk_en_default = 0; + uint8_t d2s_gain_default = 0; + uint8_t cont_config[3] = {0}; + int ret = 0; + + aw_haptic->f0 = aw_haptic->info.f0_pre; + /* enter standby mode */ + aw8671x_stop(aw_haptic); + /* config max d2s_gain */ + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_DETCFG3, ®_val, + AW_I2C_BYTE_ONE); + d2s_gain_default = reg_val & AW8671X_BIT_DETCFG3_D2S_GAIN; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_DETCFG3, + AW8671X_BIT_DETCFG3_D2S_GAIN_MASK, + AW8671X_BIT_DETCFG3_D2S_GAIN_40); + /* f0 calibrate work mode */ + aw8671x_play_mode(aw_haptic, AW_CONT_MODE); + /* enable f0 detect */ + aw8671x_f0_detect(aw_haptic, true); + /* cont config */ + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_CONTCFG6, + AW8671X_BIT_CONTCFG6_TRACK_EN_MASK, + AW8671X_BIT_CONTCFG6_TRACK_DISABLE); + /* enable auto break */ + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_PLAYCFG3, ®_val, + AW_I2C_BYTE_ONE); + brk_en_default = reg_val & AW8671X_BIT_PLAYCFG3_BRK_ENABLE; + aw8671x_auto_break_mode(aw_haptic, true); + /* f0 driver level & time */ + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_CONTCFG6, + AW8671X_BIT_CONTCFG6_DRV1_LVL_MASK, + aw_haptic->info.cont_drv1_lvl); + cont_config[0] = aw_haptic->info.cont_drv2_lvl; + cont_config[1] = aw_haptic->info.cont_drv1_time; + cont_config[2] = aw_haptic->info.cont_drv2_time; + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_CONTCFG7, cont_config, + AW_I2C_BYTE_THREE); + /* TRACK_MARGIN */ + if (!aw_haptic->info.cont_track_margin) { + aw_err("awinic->info.cont_track_margin = 0!"); + } else { + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_CONTCFG11, + &aw_haptic->info.cont_track_margin, + AW_I2C_BYTE_ONE); + } + /* DRV_WIDTH */ + if (!aw_haptic->info.f0_pre) + return -ERANGE; + cont_config[0] = AW_DRV_WIDTH_FARMULA(aw_haptic->info.f0_pre, + aw_haptic->info.cont_brk_gain, + aw_haptic->info.cont_track_margin); + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_CONTCFG3, &cont_config[0], + AW_I2C_BYTE_ONE); + /* play go */ + aw8671x_play_go(aw_haptic, true); + usleep_range(20000, 20500); + aw8671x_wait_enter_standby(aw_haptic); + ret = aw8671x_read_f0(aw_haptic); + /* restore default config */ + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_DETCFG3, + AW8671X_BIT_DETCFG3_D2S_GAIN_MASK, + d2s_gain_default); + aw8671x_f0_detect(aw_haptic, false); + /* recover auto break config */ + if (brk_en_default) + aw8671x_auto_break_mode(aw_haptic, true); + else + aw8671x_auto_break_mode(aw_haptic, false); + return ret; +} + +static int aw8671x_ram_get_f0(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint8_t brk_en_default = 0; + uint8_t d2s_gain_default = 0; + int ret = 0; + + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return -ERANGE; + } + if (aw_haptic->ram.ram_num < AW_RAM_GET_F0_SEQ) { + aw_err("miss ram get f0 waveform!"); + return -ERANGE; + } + aw_haptic->f0 = aw_haptic->info.f0_pre; + /* enter standby mode */ + aw8671x_stop(aw_haptic); + /* config max d2s_gain */ + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_DETCFG3, ®_val, + AW_I2C_BYTE_ONE); + d2s_gain_default = reg_val & AW8671X_BIT_DETCFG3_D2S_GAIN; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_DETCFG3, + AW8671X_BIT_DETCFG3_D2S_GAIN_MASK, + AW8671X_BIT_DETCFG3_D2S_GAIN_40); + /* f0 calibrate work mode */ + aw8671x_play_mode(aw_haptic, AW_RAM_MODE); + /* enable f0 detect */ + aw8671x_f0_detect(aw_haptic, true); + /* enable auto break */ + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_PLAYCFG3, ®_val, + AW_I2C_BYTE_ONE); + brk_en_default = reg_val & AW8671X_BIT_PLAYCFG3_BRK_ENABLE; + aw8671x_auto_break_mode(aw_haptic, true); + aw8671x_set_bst_vol(aw_haptic, 6558); + aw8671x_set_wav_seq(aw_haptic, 0x00, AW_RAM_GET_F0_SEQ); + aw8671x_set_wav_seq(aw_haptic, 0x01, 0x00); + aw8671x_set_wav_loop(aw_haptic, 0x00, 0x02); + /* play go */ + aw8671x_play_go(aw_haptic, true); + usleep_range(20000, 20500); + aw8671x_wait_enter_standby(aw_haptic); + ret = aw8671x_read_f0(aw_haptic); + /* restore default config */ + aw8671x_set_bst_vol(aw_haptic, aw_haptic->vmax); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_DETCFG3, + AW8671X_BIT_DETCFG3_D2S_GAIN_MASK, + d2s_gain_default); + aw8671x_f0_detect(aw_haptic, false); + /* recover auto break config */ + if (brk_en_default) + aw8671x_auto_break_mode(aw_haptic, true); + else + aw8671x_auto_break_mode(aw_haptic, false); + return ret; +} + +static uint8_t aw8671x_rtp_get_fifo_afs(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_SYSST, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= AW8671X_BIT_SYSST_FF_AFS; + reg_val >>= 3; + return reg_val; +} + +static uint8_t aw8671x_rtp_get_fifo_aes(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_SYSST, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= AW8671X_BIT_SYSST_FF_AES; + reg_val >>= 4; + return reg_val; +} + +static uint8_t aw8671x_get_osc_status(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_SYSST2, &state, + AW_I2C_BYTE_ONE); + state &= AW8671X_BIT_SYSST2_FF_EMPTY; + return state; +} + +static int aw8671x_select_d2s_gain(uint8_t reg) +{ + int d2s_gain = 0; + + switch (reg) { + case AW8671X_BIT_DETCFG3_D2S_GAIN_1: + d2s_gain = 1; + break; + case AW8671X_BIT_DETCFG3_D2S_GAIN_2: + d2s_gain = 2; + break; + case AW8671X_BIT_DETCFG3_D2S_GAIN_4: + d2s_gain = 4; + break; + case AW8671X_BIT_DETCFG3_D2S_GAIN_8: + d2s_gain = 8; + break; + case AW8671X_BIT_DETCFG3_D2S_GAIN_10: + d2s_gain = 10; + break; + case AW8671X_BIT_DETCFG3_D2S_GAIN_16: + d2s_gain = 16; + break; + case AW8671X_BIT_DETCFG3_D2S_GAIN_20: + d2s_gain = 20; + break; + case AW8671X_BIT_DETCFG3_D2S_GAIN_40: + d2s_gain = 40; + break; + default: + d2s_gain = -1; + break; + } + return d2s_gain; +} + +static void aw8671x_get_lra_resistance(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val[2] = {0}; + uint8_t adc_fs_default = 0; + uint8_t d2s_gain_default = 0; + uint8_t d2s_gain = 0; + uint32_t lra_code = 0; + + aw8671x_ram_init(aw_haptic, true); + aw8671x_stop(aw_haptic); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_DETCFG3, + AW8671X_BIT_DETCFG3_DET_SEQ0_MASK, + AW8671X_BIT_DETCFG3_DET_SEQ0_RL); + /* ADC_FS */ + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_DETCFG1, ®_val[0], + AW_I2C_BYTE_ONE); + adc_fs_default = reg_val[0] & AW8671X_BIT_DETCFG1_ADC_FS; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_DETCFG1, + AW8671X_BIT_DETCFG1_ADC_FS_MASK, + AW8671X_BIT_DETCFG1_ADC_FS_12KHZ); + /* D2S_GAIN*/ + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_DETCFG3, ®_val[0], + AW_I2C_BYTE_ONE); + d2s_gain_default = reg_val[0] & AW8671X_BIT_DETCFG3_D2S_GAIN; + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_DETCFG3, + AW8671X_BIT_DETCFG3_D2S_GAIN_MASK, + AW8671X_BIT_DETCFG3_D2S_GAIN_10); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_DETCFG1, + AW8671X_BIT_DETCFG1_DET_GO_MASK, + AW8671X_BIT_DETCFG1_DET_GO_DET_SEQ0); + usleep_range(3000, 3500); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_DETCFG1, + AW8671X_BIT_DETCFG1_DET_GO_MASK, + AW8671X_BIT_DETCFG1_DET_GO_NA); + + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_DETCFG3, ®_val[0], + AW_I2C_BYTE_ONE); + d2s_gain = reg_val[0] & AW8671X_BIT_DETCFG3_D2S_GAIN; + d2s_gain = aw8671x_select_d2s_gain(d2s_gain); + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_DETRD1, reg_val, + AW_I2C_BYTE_TWO); + lra_code = ((reg_val[0] & AW8671X_BIT_DETRD1_AVG_DATA) << 8) + + reg_val[1]; + aw_haptic->lra = AW8671X_LRA_FORMULA(lra_code, d2s_gain); + /* restore default config */ + aw8671x_ram_init(aw_haptic, false); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_DETCFG1, + AW8671X_BIT_DETCFG1_ADC_FS_MASK, + adc_fs_default); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_DETCFG3, + AW8671X_BIT_DETCFG3_D2S_GAIN_MASK, + d2s_gain_default); +} + +static void aw8671x_set_repeat_seq(struct aw_haptic *aw_haptic, uint8_t seq) +{ + aw8671x_set_wav_seq(aw_haptic, 0x00, seq); + aw8671x_set_wav_loop(aw_haptic, 0x00, AW8671X_BIT_WAVLOOP_INIFINITELY); +} + +static void aw8671x_get_vbat(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val[2] = {0}; + uint32_t vbat_code = 0; + + aw8671x_stop(aw_haptic); + aw8671x_ram_init(aw_haptic, true); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_DETCFG3, + AW8671X_BIT_DETCFG3_DET_SEQ0_MASK, + AW8671X_BIT_DETCFG3_DET_SEQ0_VBAT); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_DETCFG1, + AW8671X_BIT_DETCFG1_DET_GO_MASK, + AW8671X_BIT_DETCFG1_DET_GO_DET_SEQ0); + usleep_range(3000, 3500); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_DETCFG1, + AW8671X_BIT_DETCFG1_DET_GO_MASK, + AW8671X_BIT_DETCFG1_DET_GO_NA); + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_DETRD1, reg_val, + AW_I2C_BYTE_TWO); + vbat_code = ((reg_val[0] & AW8671X_BIT_DETRD1_AVG_DATA) << 8) + + reg_val[1]; + aw_haptic->vbat = AW8671X_VBAT_FORMULA(vbat_code); + if (aw_haptic->vbat > AW_VBAT_MAX) { + aw_haptic->vbat = AW_VBAT_MAX; + aw_info("vbat max limit = %d", aw_haptic->vbat); + } + if (aw_haptic->vbat < AW_VBAT_MIN) { + aw_haptic->vbat = AW_VBAT_MIN; + aw_info("vbat min limit = %d", aw_haptic->vbat); + } + aw_info("aw_haptic->vbat=%dmV, vbat_code=0x%02X", + aw_haptic->vbat, vbat_code); + aw8671x_ram_init(aw_haptic, false); +} + +static ssize_t aw8671x_get_reg(struct aw_haptic *aw_haptic, ssize_t len, + char *buf) +{ + uint8_t i = 0; + uint8_t reg_array[AW8671X_REG_ANACFG14 + 1] = {0}; + + for (i = 0; i < AW8671X_REG_RTPDATA; i++) + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_RSTCFG, reg_array, + AW8671X_REG_RTPDATA); + for (i = AW8671X_REG_RTPDATA + 1; i < AW8671X_REG_RAMDATA; i++) + haptic_hv_i2c_reads(aw_haptic, (AW8671X_REG_RTPDATA + 1), + ®_array[AW8671X_REG_RTPDATA + 1], + (AW8671X_REG_RAMDATA - AW8671X_REG_RTPDATA - 1)); + for (i = AW8671X_REG_RAMDATA + 1; i <= AW8671X_REG_ANACFG14; i++) + haptic_hv_i2c_reads(aw_haptic, (AW8671X_REG_RAMDATA + 1), + ®_array[AW8671X_REG_RAMDATA + 1], + (AW8671X_REG_ANACFG14 - AW8671X_REG_RAMDATA - 1)); + for (i = 0; i <= AW8671X_REG_ANACFG14; i++) + if ((i != AW8671X_REG_RTPDATA) && (i != AW8671X_REG_RAMDATA)) + len += snprintf(buf + len, PAGE_SIZE - len, + "reg:0x%02X=0x%02X\n", i, reg_array[i]); + return len; +} + +static void aw8671x_offset_cali(struct aw_haptic *aw_haptic) +{ + +} + +static void aw8671x_trig_init(struct aw_haptic *aw_haptic) +{ + aw_info("enter!"); + if (aw_haptic->info.is_enabled_one_wire) { + aw_info("one wire is enabled!"); + aw8671x_one_wire_init(aw_haptic); + } else { + aw8671x_trig1_param_init(aw_haptic); + aw8671x_trig1_param_config(aw_haptic); + } + if (aw_haptic->info.is_enabled_i2s && aw_haptic->i2s_config) { + aw_info("i2s is enabled!"); + aw8671x_i2s_init(aw_haptic); + } else { + aw8671x_trig2_param_init(aw_haptic); + aw8671x_trig3_param_init(aw_haptic); + aw8671x_trig2_param_config(aw_haptic); + aw8671x_trig3_param_config(aw_haptic); + } +} + +#ifdef AW_CHECK_RAM_DATA +static int aw8671x_check_ram_data(struct aw_haptic *aw_haptic, + uint8_t *cont_data, uint8_t *ram_data, + uint32_t len) +{ + int i = 0; + + for (i = 0; i < len; i++) { + if (ram_data[i] != cont_data[i]) { + aw_err("check ramdata error, addr=0x%04x, ram_data=0x%02x, file_data=0x%02x", + i, ram_data[i], cont_data[i]); + return -ERANGE; + } + } + return 0; +} +#endif + +static int aw8671x_container_update(struct aw_haptic *aw_haptic, + struct aw_haptic_container *awinic_cont) +{ + uint8_t reg_val[3] = {0}; + uint8_t rtp_addr[5] = {0}; +#ifdef AW_CHECK_RAM_DATA + uint8_t ram_data[AW_RAMDATA_RD_BUFFER_SIZE] = {0}; +#endif + int i = 0; + int ret = 0; + int len = 0; + uint32_t temp = 0; + uint32_t shift = 0; + uint32_t base_addr = 0; + + mutex_lock(&aw_haptic->lock); + aw_haptic->ram.baseaddr_shift = 2; + aw_haptic->ram.ram_shift = 4; + /* RAMINIT Enable */ + aw8671x_ram_init(aw_haptic, true); + /* Enter standby mode */ + aw8671x_stop(aw_haptic); + /* base addr */ + shift = aw_haptic->ram.baseaddr_shift; + aw_haptic->ram.base_addr = + (uint32_t)((awinic_cont->data[0 + shift] << 8) | + (awinic_cont->data[1 + shift])); + base_addr = aw_haptic->ram.base_addr; + /* ADDRH */ + rtp_addr[0] = awinic_cont->data[0 + shift]; + /* ADDRL */ + rtp_addr[1] = awinic_cont->data[1 + shift]; + /* FIFO AEH FIFO AFH */ + rtp_addr[2] = (uint8_t)(AW8671X_FIFO_AE_ADDR_H(base_addr) | + AW8671X_FIFO_AF_ADDR_H(base_addr)); + /* FIFO AEL */ + rtp_addr[3] = (uint8_t)(AW8671X_FIFO_AE_ADDR_L(base_addr)); + /* FIFO AFL */ + rtp_addr[4] = (uint8_t)(AW8671X_FIFO_AF_ADDR_L(base_addr)); + aw_info("base_addr = %d", base_addr); + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_RTPCFG1, rtp_addr, + AW_I2C_BYTE_FIVE); + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_RTPCFG3, reg_val, + AW_I2C_BYTE_THREE); + temp = ((reg_val[0] & AW8671X_BIT_RTPCFG3_FIFO_AFH) << 24) | + ((reg_val[0] & AW8671X_BIT_RTPCFG3_FIFO_AEH) << 4) | reg_val[1]; + aw_info("almost_empty_threshold = %d", (uint16_t)temp); + temp = temp | (reg_val[2] << 16); + aw_info("almost_full_threshold = %d", temp >> 16); + /* ram */ + aw8671x_set_ram_addr(aw_haptic); + i = aw_haptic->ram.ram_shift; + while (i < awinic_cont->len) { + if ((awinic_cont->len - i) < AW_RAMDATA_WR_BUFFER_SIZE) + len = awinic_cont->len - i; + else + len = AW_RAMDATA_WR_BUFFER_SIZE; + + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_RAMDATA, + &awinic_cont->data[i], len); + i += len; + } +#ifdef AW_CHECK_RAM_DATA + aw8671x_set_ram_addr(aw_haptic); + i = aw_haptic->ram.ram_shift; + while (i < awinic_cont->len) { + if ((awinic_cont->len - i) < AW_RAMDATA_RD_BUFFER_SIZE) + len = awinic_cont->len - i; + else + len = AW_RAMDATA_RD_BUFFER_SIZE; + + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_RAMDATA, + ram_data, len); + ret = aw8671x_check_ram_data(aw_haptic, &awinic_cont->data[i], + ram_data, len); + if (ret < 0) + break; + i += len; + } + if (ret) + aw_err("ram data check sum error"); + else + aw_info("ram data check sum pass"); +#endif + /* RAMINIT Disable */ + aw8671x_ram_init(aw_haptic, false); + mutex_unlock(&aw_haptic->lock); + + return ret; +} + +static uint64_t aw8671x_get_theory_time(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t fre_val = 0; + uint64_t theory_time = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_SYSCTRL2, ®_val, + AW_I2C_BYTE_ONE); + fre_val = reg_val & AW8671X_BIT_SYSCTRL2_WAVDAT_MODE; + if (fre_val == AW8671X_BIT_SYSCTRL2_RATE_24K) + theory_time = (aw_haptic->rtp_len / 24000) * 1000000; /*24K */ + else if (fre_val == AW8671X_BIT_SYSCTRL2_RATE_48K) + theory_time = (aw_haptic->rtp_len / 48000) * 1000000; /*48K */ + else + theory_time = (aw_haptic->rtp_len / 12000) * 1000000; /*12K */ + aw_info("microsecond:%llu theory_time = %llu", + aw_haptic->microsecond, theory_time); + return theory_time; +} + +static void aw8671x_parse_dt(struct device *dev, struct aw_haptic *aw_haptic, + struct device_node *np) +{ + uint8_t duration_time[3]; + uint8_t trig_config_temp[24]; + uint32_t val = 0; + + val = of_property_read_u8(np, "aw8671x_gain_bypass", + &aw_haptic->info.gain_bypass); + if (val != 0) + aw_info("aw8671x_gain_bypass not found"); + val = of_property_read_u8(np, "aw8671x_brk_bst_md", + &aw_haptic->info.brk_bst_md); + if (val != 0) + aw_info("aw8671x_brk_bst_md not found"); + val = of_property_read_u32(np, "f0_pre", &aw_haptic->info.f0_pre); + if (val != 0) + aw_info("f0_pre not found"); + val = of_property_read_u8(np, "f0_cali_percent", + &aw_haptic->info.f0_cali_percent); + if (val != 0) + aw_info("f0_cali_percent not found"); + + val = of_property_read_u8(np, "aw8671x_cont_drv1_lvl", + &aw_haptic->info.cont_drv1_lvl); + if (val != 0) + aw_info("aw8671x_cont_drv1_lvl not found"); + val = of_property_read_u32(np, "aw8671x_cont_lra_vrms", + &aw_haptic->info.cont_lra_vrms); + if (val != 0) + aw_info("aw8671x_cont_lra_vrms not found"); + val = of_property_read_u8(np, "aw8671x_cont_drv1_time", + &aw_haptic->info.cont_drv1_time); + if (val != 0) + aw_info("aw8671x_cont_drv1_time not found"); + val = of_property_read_u8(np, "aw8671x_cont_drv2_time", + &aw_haptic->info.cont_drv2_time); + if (val != 0) + aw_info("aw8671x_cont_drv2_time not found"); + val = of_property_read_u8(np, "aw8671x_cont_wait_num", + &aw_haptic->info.cont_wait_num); + if (val != 0) + aw_info("aw8671x_cont_wait_num not found"); + val = of_property_read_u8(np, "aw8671x_cont_bst_brk_gain", + &aw_haptic->info.cont_bst_brk_gain); + if (val != 0) + aw_info("aw8671x_cont_bst_brk_gain not found"); + val = of_property_read_u8(np, "aw8671x_cont_brk_gain", + &aw_haptic->info.cont_brk_gain); + if (val != 0) + aw_info("aw8671x_cont_brk_gain not found"); + val = of_property_read_u8(np, "aw8671x_cont_tset", + &aw_haptic->info.cont_tset); + if (val != 0) + aw_info("aw8671x_cont_tset not found"); + val = of_property_read_u8(np, "aw8671x_cont_bemf_set", + &aw_haptic->info.cont_bemf_set); + if (val != 0) + aw_info("aw8671x_cont_bemf_set not found"); + val = of_property_read_u8(np, "aw8671x_d2s_gain", + &aw_haptic->info.d2s_gain); + if (val != 0) + aw_info("aw8671x_d2s_gain not found"); + val = of_property_read_u8(np, "aw8671x_cont_brk_time", + &aw_haptic->info.cont_brk_time); + if (val != 0) + aw_info("aw8671x_cont_brk_time not found"); + val = of_property_read_u8(np, "aw8671x_cont_track_margin", + &aw_haptic->info.cont_track_margin); + if (val != 0) + aw_info("aw8671x_cont_track_margin not found"); + aw_haptic->info.is_enabled_auto_bst = + of_property_read_bool(np, "aw8671x_is_enabled_auto_bst"); + aw_info("aw_haptic->info.is_enabled_auto_bst = %d", + aw_haptic->info.is_enabled_auto_bst); + aw_haptic->info.is_enabled_i2s = + of_property_read_bool(np, "aw8671x_is_enabled_i2s"); + aw_info("aw_haptic->info.is_enabled_i2s = %d", + aw_haptic->info.is_enabled_i2s); + aw_haptic->info.is_enabled_one_wire = of_property_read_bool(np, + "aw8671x_is_enabled_one_wire"); + aw_info("aw_haptic->info.is_enabled_one_wire = %d", + aw_haptic->info.is_enabled_one_wire); + val = of_property_read_u8_array(np, "aw8671x_duration_time", + duration_time, + ARRAY_SIZE(duration_time)); + if (val != 0) + aw_info("aw8671x_duration_time not found"); + else + memcpy(aw_haptic->info.duration_time, duration_time, + sizeof(duration_time)); + val = of_property_read_u32(np, "aw8671x_bst_vol_default", + &aw_haptic->info.bst_vol_default); + if (val != 0) + aw_info("aw8671x_bst_vol_default not found"); + val = of_property_read_u8_array(np, "aw8671x_trig_config", + trig_config_temp, + ARRAY_SIZE(trig_config_temp)); + if (val != 0) + aw_info("aw8671x_trig_config not found"); + else + memcpy(aw_haptic->info.trig_cfg, trig_config_temp, + sizeof(trig_config_temp)); + aw_dbg("aw_haptic->info.brk_bst_md: %d", aw_haptic->info.brk_bst_md); + aw_dbg("aw_haptic->info.bst_vol_default: %d", + aw_haptic->info.bst_vol_default); +} + +static void aw8671x_misc_para_init(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val[8] = {0}; + + /* Set I2C broadcast addr */ + reg_val[0] = (uint8_t)aw_haptic->i2c->addr; + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_SYSCTRL4, reg_val, + AW_I2C_BYTE_ONE); + /* Cont drv2 lvl */ + aw_haptic->info.cont_drv2_lvl = AW8671X_DRV2_LVL_FARMULA( + aw_haptic->info.cont_lra_vrms); + if (aw_haptic->info.cont_drv2_lvl > AW_DRV2_LVL_MAX) + aw_haptic->info.cont_drv2_lvl = AW_DRV2_LVL_MAX; + /* Get vmax */ + if (aw_haptic->info.bst_vol_default > 0) + aw_haptic->vmax = aw_haptic->info.bst_vol_default; + /* Get gain */ + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_PLAYCFG2, reg_val, + AW_I2C_BYTE_ONE); + aw_haptic->gain = reg_val[0]; + /* Get wave_seq */ + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_WAVCFG1, reg_val, + AW_I2C_BYTE_EIGHT); + aw_haptic->index = reg_val[0]; + memcpy(aw_haptic->seq, reg_val, AW_SEQUENCER_SIZE); + /* Set gain_bypass */ + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_SYSCTRL3, + AW8671X_BIT_SYSCTRL3_GAIN_BYPASS_MASK, + aw_haptic->info.gain_bypass << 1); + + /* brk_bst_md */ + if (!aw_haptic->info.brk_bst_md) + aw_err("aw_haptic->info.brk_bst_md = 0!"); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_CONTCFG1, + AW8671X_BIT_CONTCFG1_BRK_BST_MD, + (aw_haptic->info.brk_bst_md << 6)); + + /* d2s_gain */ + if (!aw_haptic->info.d2s_gain) + aw_err("aw_haptic->info.d2s_gain = 0!"); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_DETCFG3, + AW8671X_BIT_DETCFG3_D2S_GAIN_MASK, + aw_haptic->info.d2s_gain); + + /* cont_tset */ + if (!aw_haptic->info.cont_tset) + aw_err("aw_haptic->info.cont_tset = 0!"); + /* cont_bemf_set */ + if (!aw_haptic->info.cont_bemf_set) + aw_err("aw_haptic->info.cont_bemf_set = 0!"); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_CONTCFG13, + (AW8671X_BIT_CONTCFG13_TSET_MASK & + AW8671X_BIT_CONTCFG13_BEME_SET_MASK), + (aw_haptic->info.cont_tset << 4 | + aw_haptic->info.cont_bemf_set)); + + /* cont_brk_time */ + if (!aw_haptic->info.cont_brk_time) + aw_err("aw_haptic->info.cont_brk_time = 0!"); + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_CONTCFG10, + &aw_haptic->info.cont_brk_time, AW_I2C_BYTE_ONE); + + /* cont_bst_brk_gain */ + if (!aw_haptic->info.cont_bst_brk_gain) + aw_err("aw_haptic->info.cont_bst_brk_gain = 0"); + /* cont_brk_gain */ + if (!aw_haptic->info.cont_brk_gain) + aw_err("aw_haptic->info.cont_brk_gain = 0!"); + haptic_hv_i2c_write_bits(aw_haptic, AW8671X_REG_CONTCFG5, + (AW8671X_BIT_CONTCFG5_BST_BRK_GAIN_MASK & + AW8671X_BIT_CONTCFG5_BRK_GAIN_MASK), + (aw_haptic->info.cont_bst_brk_gain << 4 | + aw_haptic->info.cont_brk_gain)); +} + +static ssize_t aw8671x_cont_wait_num_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_wait_num = 0x%02X\n", + aw_haptic->info.cont_wait_num); + return len; +} + +static ssize_t aw8671x_cont_wait_num_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + rc = kstrtou8(buf, 0, &aw_haptic->info.cont_wait_num); + if (rc < 0) + return rc; + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_CONTCFG4, + &aw_haptic->info.cont_wait_num, AW_I2C_BYTE_ONE); + + return count; +} + +static ssize_t aw8671x_cont_drv_lvl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_lvl = 0x%02X, cont_drv2_lvl = 0x%02X\n", + aw_haptic->info.cont_drv1_lvl, + aw_haptic->info.cont_drv2_lvl); + return len; +} + +static ssize_t aw8671x_cont_drv_lvl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t reg_array[2] = {0}; + uint32_t databuf[2] = {0}; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_haptic->info.cont_drv1_lvl = databuf[0]; + aw_haptic->info.cont_drv2_lvl = databuf[1]; + haptic_hv_i2c_reads(aw_haptic, AW8671X_REG_CONTCFG6, reg_array, + AW_I2C_BYTE_ONE); + reg_array[0] &= AW8671X_BIT_CONTCFG6_DRV1_LVL_MASK; + reg_array[0] |= aw_haptic->info.cont_drv1_lvl; + reg_array[1] = aw_haptic->info.cont_drv2_lvl; + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_CONTCFG6, reg_array, + AW_I2C_BYTE_TWO); + } + return count; +} + +static ssize_t aw8671x_cont_drv_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_time = 0x%02X, cont_drv2_time = 0x%02X\n", + aw_haptic->info.cont_drv1_time, + aw_haptic->info.cont_drv2_time); + return len; +} + +static ssize_t aw8671x_cont_drv_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t reg_array[2] = {0}; + uint32_t databuf[2] = {0}; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_haptic->info.cont_drv1_time = databuf[0]; + aw_haptic->info.cont_drv2_time = databuf[1]; + reg_array[0] = (uint8_t)aw_haptic->info.cont_drv1_time; + reg_array[1] = (uint8_t)aw_haptic->info.cont_drv2_time; + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_CONTCFG8, reg_array, + AW_I2C_BYTE_TWO); + } + return count; +} + +static ssize_t aw8671x_cont_brk_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "cont_brk_time = 0x%02X\n", + aw_haptic->info.cont_brk_time); + return len; +} + +static ssize_t aw8671x_cont_brk_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + rc = kstrtou8(buf, 0, &aw_haptic->info.cont_brk_time); + if (rc < 0) + return rc; + haptic_hv_i2c_writes(aw_haptic, AW8671X_REG_CONTCFG10, + &aw_haptic->info.cont_brk_time, AW_I2C_BYTE_ONE); + return count; +} + +static ssize_t aw8671x_trig_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint8_t i = 0; + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + for (i = 0; i < AW_TRIG_NUM; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "trig%d: trig_level=%d, trig_polar=%d, pos_enable=%d, pos_sequence=%d, neg_enable=%d, neg_sequence=%d trig_brk=%d, trig_bst=%d\n", + i + 1, + aw_haptic->trig[i].trig_level, + aw_haptic->trig[i].trig_polar, + aw_haptic->trig[i].pos_enable, + aw_haptic->trig[i].pos_sequence, + aw_haptic->trig[i].neg_enable, + aw_haptic->trig[i].neg_sequence, + aw_haptic->trig[i].trig_brk, + aw_haptic->trig[i].trig_bst); + } + return len; +} + +static ssize_t aw8671x_trig_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t databuf[9] = { 0 }; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (sscanf(buf, "%d %d %d %d %d %d %d %d %d", &databuf[0], &databuf[1], + &databuf[2], &databuf[3], &databuf[4], &databuf[5], + &databuf[6], &databuf[7], &databuf[8]) == 9) { + aw_info("%d, %d, %d, %d, %d, %d, %d, %d, %d", + databuf[0], databuf[1], databuf[2], databuf[3], + databuf[4], databuf[5], databuf[6], databuf[7], + databuf[8]); + if (databuf[0] < 1 || databuf[0] > 3) { + aw_info("input trig_num out of range!"); + return count; + } + if (databuf[0] == 1 && aw_haptic->info.is_enabled_one_wire) { + aw_info("trig1 pin used for one wire!"); + return count; + } + if ((databuf[0] == 2 || databuf[0] == 3) && + aw_haptic->info.is_enabled_i2s && aw_haptic->i2s_config) { + aw_info("trig2 and trig3 pin used for i2s!"); + return count; + } + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + if (databuf[4] > aw_haptic->ram.ram_num || + databuf[6] > aw_haptic->ram.ram_num) { + aw_err("input seq value out of range!"); + return count; + } + databuf[0] -= 1; + + aw_haptic->trig[databuf[0]].trig_level = databuf[1]; + aw_haptic->trig[databuf[0]].trig_polar = databuf[2]; + aw_haptic->trig[databuf[0]].pos_enable = databuf[3]; + aw_haptic->trig[databuf[0]].pos_sequence = databuf[4]; + aw_haptic->trig[databuf[0]].neg_enable = databuf[5]; + aw_haptic->trig[databuf[0]].neg_sequence = databuf[6]; + aw_haptic->trig[databuf[0]].trig_brk = databuf[7]; + aw_haptic->trig[databuf[0]].trig_bst = databuf[8]; + mutex_lock(&aw_haptic->lock); + switch (databuf[0]) { + case 0: + aw8671x_trig1_param_config(aw_haptic); + break; + case 1: + aw8671x_trig2_param_config(aw_haptic); + break; + case 2: + aw8671x_trig3_param_config(aw_haptic); + break; + } + mutex_unlock(&aw_haptic->lock); + } + return count; +} + +static DEVICE_ATTR(cont_wait_num, S_IWUSR | S_IRUGO, aw8671x_cont_wait_num_show, + aw8671x_cont_wait_num_store); +static DEVICE_ATTR(cont_drv_lvl, S_IWUSR | S_IRUGO, aw8671x_cont_drv_lvl_show, + aw8671x_cont_drv_lvl_store); +static DEVICE_ATTR(cont_drv_time, S_IWUSR | S_IRUGO, aw8671x_cont_drv_time_show, + aw8671x_cont_drv_time_store); +static DEVICE_ATTR(cont_brk_time, S_IWUSR | S_IRUGO, aw8671x_cont_brk_time_show, + aw8671x_cont_brk_time_store); +static DEVICE_ATTR(trig, S_IWUSR | S_IRUGO, aw8671x_trig_show, + aw8671x_trig_store); + +static struct attribute *aw8671x_vibrator_attributes[] = { + &dev_attr_cont_wait_num.attr, + &dev_attr_cont_drv_lvl.attr, + &dev_attr_cont_drv_time.attr, + &dev_attr_cont_brk_time.attr, + &dev_attr_trig.attr, + NULL +}; + +static struct attribute_group aw8671x_vibrator_attribute_group = { + .attrs = aw8671x_vibrator_attributes +}; + +static int aw8671x_creat_node(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + ret = sysfs_create_group(&aw_haptic->vib_dev.dev->kobj, + &aw8671x_vibrator_attribute_group); + if (ret < 0) { + aw_err("error create aw8671x sysfs attr files"); + return ret; + } + return 0; +} + +struct aw_haptic_func aw8671x_func_list = { + .play_stop = aw8671x_stop, + .ram_init = aw8671x_ram_init, + .get_vbat = aw8671x_get_vbat, + .creat_node = aw8671x_creat_node, + .get_f0 = aw8671x_get_f0, + .ram_get_f0 = aw8671x_ram_get_f0, + .cont_config = aw8671x_cont_config, + .offset_cali = aw8671x_offset_cali, + .check_qualify = aw8671x_check_qualify, + .get_irq_state = aw8671x_get_irq_state, + .judge_rtp_going = aw8671x_judge_rtp_going, + .set_bst_peak_cur = aw8671x_set_bst_peak_cur, + .get_theory_time = aw8671x_get_theory_time, + .get_lra_resistance = aw8671x_get_lra_resistance, + .set_pwm = aw8671x_set_pwm, + .play_mode = aw8671x_play_mode, + .set_bst_vol = aw8671x_set_bst_vol, + .interrupt_setup = aw8671x_interrupt_setup, + .set_repeat_seq = aw8671x_set_repeat_seq, + .auto_bst_enable = aw8671x_auto_bst_enable, + .vbat_mode_config = aw8671x_vbat_mode_config, + .set_wav_seq = aw8671x_set_wav_seq, + .set_wav_loop = aw8671x_set_wav_loop, + .set_ram_addr = aw8671x_set_ram_addr, + .set_rtp_data = aw8671x_set_rtp_data, + .container_update = aw8671x_container_update, + .protect_config = aw8671x_protect_config, + .parse_dt = aw8671x_parse_dt, + .trig_init = aw8671x_trig_init, + .irq_clear = aw8671x_irq_clear, + .get_wav_loop = aw8671x_get_wav_loop, + .play_go = aw8671x_play_go, + .misc_para_init = aw8671x_misc_para_init, + .set_rtp_aei = aw8671x_set_rtp_aei, + .set_gain = aw8671x_set_gain, + .upload_lra = aw8671x_upload_lra, + .bst_mode_config = aw8671x_bst_mode_config, + .get_reg = aw8671x_get_reg, + .get_prctmode = aw8671x_get_prctmode, + .get_trim_lra = aw8671x_get_trim_lra, + .get_ram_data = aw8671x_get_ram_data, + .get_first_wave_addr = aw8671x_get_first_wave_addr, + .get_glb_state = aw8671x_get_glb_state, + .get_osc_status = aw8671x_get_osc_status, + .rtp_get_fifo_afs = aw8671x_rtp_get_fifo_afs, + .rtp_get_fifo_aes = aw8671x_rtp_get_fifo_aes, + .get_wav_seq = aw8671x_get_wav_seq, +#ifdef AW_SND_SOC_CODEC + .snd_soc_init = aw8671x_snd_soc_init, +#endif +}; diff --git a/drivers/misc/awinic/haptic_hv/aw8692x.c b/drivers/misc/awinic/haptic_hv/aw8692x.c new file mode 100644 index 000000000000..d5c3c1cc9ae9 --- /dev/null +++ b/drivers/misc/awinic/haptic_hv/aw8692x.c @@ -0,0 +1,1956 @@ +/* + * File: aw8692x.c + * + * Author: Ethan + * + * Copyright (c) 2021 AWINIC Technology CO., 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "haptic_hv.h" +#include "haptic_hv_reg.h" + +static void aw8692x_ram_init(struct aw_haptic *, bool); +static void aw8692x_vbat_mode_config(struct aw_haptic *, uint8_t); + +static void aw8692x_reg_unlock(struct aw_haptic *aw_haptic, bool flag) +{ + uint8_t reg_val = 0; + + if (flag) { + /* Unlock register */ + reg_val = AW8692X_BIT_TMCFG_TM_UNLOCK; + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_TMCFG, ®_val, + AW_I2C_BYTE_ONE); + } else { + /* Lock register */ + reg_val = AW8692X_BIT_TMCFG_TM_LOCK; + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_TMCFG, ®_val, + AW_I2C_BYTE_ONE); + } +} + +static int aw8692x_check_qualify(struct aw_haptic *aw_haptic) +{ + int ret = -1; + uint8_t reg_val[3] = {0}; + + aw_info("enter"); + ret = haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_EFCFG6, reg_val, + AW_I2C_BYTE_THREE); + if (ret < 0) + return ret; + if (!(reg_val[0] & (~AW8692X_BIT_EFCFG6_LOCK_MASK))) { + aw_err("unqualified chip!"); + return -ERANGE; + } + aw8692x_reg_unlock(aw_haptic, true); + if ((reg_val[1] & (~AW8692X_BIT_EFCFG7_RESERVED_MASK)) || + (reg_val[2] & (~AW8692X_BIT_EFCFG8_EF_TRM_BST_IPEAK_MASK))) { + aw8692x_ram_init(aw_haptic, true); + reg_val[0] = AW8692X_BIT_EFCFG1_INIT_VAL; + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_EFCFG1, reg_val, + AW_I2C_BYTE_ONE); + usleep_range(2500, 3000); + aw8692x_ram_init(aw_haptic, false); + } + ret = haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_EFCFG7, ®_val[1], + AW_I2C_BYTE_TWO); + if ((reg_val[1] & (~AW8692X_BIT_EFCFG7_RESERVED_MASK)) || + (reg_val[2] & (~AW8692X_BIT_EFCFG8_EF_TRM_BST_IPEAK_MASK))) { + return -ERANGE; + } + reg_val[0] = AW8692X_BIT_ANACFG2_INIT_VAL; + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_ANACFG2, reg_val, + AW_I2C_BYTE_ONE); + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_EFCFG9, ®_val[0], + AW_I2C_BYTE_ONE); + if ((reg_val[0] & (~AW8692X_BIT_EFCFG9_EF_TRIM_OSC_MASK)) < + AW8692X_BOOT_DETECTION) + return -ERANGE; + aw8692x_reg_unlock(aw_haptic, false); + return 0; +} + +static void aw8692x_set_pwm(struct aw_haptic *aw_haptic, uint8_t mode) +{ + switch (mode) { + case AW_PWM_48K: + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_SYSCTRL4, + AW8692X_BIT_SYSCTRL4_WAVDAT_MODE_MASK, + AW8692X_BIT_SYSCTRL4_WAVDAT_48K); + break; + case AW_PWM_24K: + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_SYSCTRL4, + AW8692X_BIT_SYSCTRL4_WAVDAT_MODE_MASK, + AW8692X_BIT_SYSCTRL4_WAVDAT_24K); + break; + case AW_PWM_12K: + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_SYSCTRL4, + AW8692X_BIT_SYSCTRL4_WAVDAT_MODE_MASK, + AW8692X_BIT_SYSCTRL4_WAVDAT_12K); + break; + default: + aw_err("error param!"); + break; + } +} + +static void aw8692x_set_gain(struct aw_haptic *aw_haptic, uint8_t gain) +{ + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_PLAYCFG2, &gain, + AW_I2C_BYTE_ONE); +} + +static void aw8692x_set_bst_peak_cur(struct aw_haptic *aw_haptic) +{ + aw8692x_reg_unlock(aw_haptic, true); + switch (aw_haptic->bst_pc) { + case AW_BST_PC_L1: + aw_info("bst pc = L1"); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_ANACFG13, + AW8692X_BIT_ANACFG13_BST_PC_MASK, + AW8692X_BIT_ANACFG13_BST_PEAKCUR_3P45A); + break; + case AW_BST_PC_L2: + aw_info("bst pc = L2"); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_ANACFG13, + AW8692X_BIT_ANACFG13_BST_PC_MASK, + AW8692X_BIT_ANACFG13_BST_PEAKCUR_4A); + break; + default: + aw_info("bst pc = L1"); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_ANACFG13, + AW8692X_BIT_ANACFG13_BST_PC_MASK, + AW8692X_BIT_ANACFG13_BST_PEAKCUR_3P45A); + break; + } + aw8692x_reg_unlock(aw_haptic, false); +} + +static void aw8692x_set_bst_vol(struct aw_haptic *aw_haptic, uint32_t bst_vol) +{ + uint8_t reg_val = 0; + + if (bst_vol < AW8692X_BST_VOL_MIN) + bst_vol = AW8692X_BST_VOL_MIN; + else if (bst_vol > AW8692X_BST_VOL_MAX) + bst_vol = AW8692X_BST_VOL_MAX; + reg_val = AW8692X_BST_VOL_FARMULA(bst_vol); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PLAYCFG1, + AW8692X_BIT_PLAYCFG1_BST_VOUT_VREFSET_MASK, + reg_val); +} + +static void aw8692x_set_wav_seq(struct aw_haptic *aw_haptic, uint8_t wav, + uint8_t seq) +{ + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_WAVCFG1 + wav, &seq, + AW_I2C_BYTE_ONE); +} + +static void aw8692x_set_wav_loop(struct aw_haptic *aw_haptic, uint8_t wav, + uint8_t loop) +{ + uint8_t tmp = 0; + + if (wav % 2) { + tmp = loop << 0; + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_WAVCFG9 + (wav / 2), + AW8692X_BIT_WAVLOOP_SEQ_EVEN_MASK, tmp); + } else { + tmp = loop << 4; + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_WAVCFG9 + (wav / 2), + AW8692X_BIT_WAVLOOP_SEQ_ODD_MASK, tmp); + } +} + +static void aw8692x_set_rtp_data(struct aw_haptic *aw_haptic, uint8_t *data, + uint32_t len) +{ + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_RTPDATA, data, len); +} + +static void aw8692x_set_rtp_aei(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_SYSINTM, + AW8692X_BIT_SYSINTM_FF_AEM_MASK, + AW8692X_BIT_SYSINTM_FF_AEM_ON); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_SYSINTM, + AW8692X_BIT_SYSINTM_FF_AEM_MASK, + AW8692X_BIT_SYSINTM_FF_AEM_OFF); + } +} + +static void aw8692x_set_ram_addr(struct aw_haptic *aw_haptic) +{ + uint8_t ram_addr[2] = {0}; + uint32_t base_addr = aw_haptic->ram.base_addr; + + ram_addr[0] = (uint8_t)AW8692X_RAMADDR_H(base_addr); + ram_addr[1] = (uint8_t)AW8692X_RAMADDR_L(base_addr); + + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_RAMADDRH, ram_addr, + AW_I2C_BYTE_TWO); +} + +static void aw8692x_set_base_addr(struct aw_haptic *aw_haptic) +{ + uint8_t rtp_addr[2] = {0}; + uint32_t base_addr = aw_haptic->ram.base_addr; + + rtp_addr[0] = (uint8_t)AW8692X_BASEADDR_H(base_addr); + rtp_addr[1] = (uint8_t)AW8692X_BASEADDR_L(base_addr); + + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_RTPCFG1, + AW8692X_BIT_RTPCFG1_BASE_ADDR_H_MASK, + rtp_addr[0]); + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_RTPCFG2, &rtp_addr[1], + AW_I2C_BYTE_ONE); +} + +static void aw8692x_auto_break_mode(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PLAYCFG3, + AW8692X_BIT_PLAYCFG3_BRK_EN_MASK, + AW8692X_BIT_PLAYCFG3_BRK_ENABLE); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PLAYCFG3, + AW8692X_BIT_PLAYCFG3_BRK_EN_MASK, + AW8692X_BIT_PLAYCFG3_BRK_DISABLE); + } +} + +static void aw8692x_f0_detect(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_CONTCFG1, + AW8692X_BIT_CONTCFG1_EN_F0_DET_MASK, + AW8692X_BIT_CONTCFG1_F0_DET_ENABLE); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_CONTCFG1, + AW8692X_BIT_CONTCFG1_EN_F0_DET_MASK, + AW8692X_BIT_CONTCFG1_F0_DET_DISABLE); + } +} + +static uint8_t aw8692x_get_glb_state(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_GLBRD5, &state, + AW_I2C_BYTE_ONE); + aw_dbg("glb state value is 0x%02X", state); + return state; +} + +static void aw8692x_play_go(struct aw_haptic *aw_haptic, bool flag) +{ + uint8_t go_on = AW8692X_BIT_PLAYCFG4_GO_ON; + uint8_t stop_on = AW8692X_BIT_PLAYCFG4_STOP_ON; + uint8_t reg_val = 0; + + aw_info("enter, flag = %d", flag); + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_ANACFG20, ®_val, + AW_I2C_BYTE_ONE); + if (flag && (reg_val <= AW8692X_BIT_ANACFG20_TRIM_LRA)) + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_PLAYCFG4, &go_on, + AW_I2C_BYTE_ONE); + else + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_PLAYCFG4, &stop_on, + AW_I2C_BYTE_ONE); +} + +static int aw8692x_wait_enter_standby(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int count = 100; + + while (count--) { + reg_val = aw8692x_get_glb_state(aw_haptic); + if (reg_val == AW8692X_BIT_GLBRD5_STATE_STANDBY) { + aw_info("entered standby!"); + return 0; + } + aw_dbg("wait for standby"); + usleep_range(2000, 2500); + } + aw_err("do not enter standby automatically"); + return -ERANGE; +} + +static void aw8692x_bst_mode_config(struct aw_haptic *aw_haptic, uint8_t mode) +{ + switch (mode) { + case AW_BST_BOOST_MODE: + aw_info("haptic bst mode = bst"); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PLAYCFG1, + AW8692X_BIT_PLAYCFG1_BST_MODE_MASK, + AW8692X_BIT_PLAYCFG1_BST_MODE); + break; + case AW_BST_BYPASS_MODE: + aw_info("haptic bst mode = bypass"); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PLAYCFG1, + AW8692X_BIT_PLAYCFG1_BST_MODE_MASK, + AW8692X_BIT_PLAYCFG1_BST_MODE_BYPASS); + break; + default: + aw_err("bst = %d error", mode); + break; + } +} + +static void aw8692x_play_mode(struct aw_haptic *aw_haptic, uint8_t play_mode) +{ + switch (play_mode) { + case AW_STANDBY_MODE: + aw_info("enter standby mode"); + aw_haptic->play_mode = AW_STANDBY_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_SYSCTRL3, + AW8692X_BIT_SYSCTRL3_STANDBY_MASK, + AW8692X_BIT_SYSCTRL3_STANDBY_ON); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_SYSCTRL3, + AW8692X_BIT_SYSCTRL3_STANDBY_MASK, + AW8692X_BIT_SYSCTRL3_STANDBY_OFF); + break; + case AW_RAM_MODE: + aw_info("enter ram mode"); + aw_haptic->play_mode = AW_RAM_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PLAYCFG3, + AW8692X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8692X_BIT_PLAYCFG3_PLAY_MODE_RAM); + /* bst mode */ + aw8692x_bst_mode_config(aw_haptic, AW_BST_BOOST_MODE); + aw8692x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + break; + case AW_RAM_LOOP_MODE: + aw_info("enter ram loop mode"); + aw_haptic->play_mode = AW_RAM_LOOP_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PLAYCFG3, + AW8692X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8692X_BIT_PLAYCFG3_PLAY_MODE_RAM); + /* bst mode */ + aw8692x_bst_mode_config(aw_haptic, AW_BST_BYPASS_MODE); + aw8692x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + break; + case AW_RTP_MODE: + aw_info("enter rtp mode"); + aw_haptic->play_mode = AW_RTP_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PLAYCFG3, + AW8692X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8692X_BIT_PLAYCFG3_PLAY_MODE_RTP); + /* bst mode */ + aw8692x_bst_mode_config(aw_haptic, AW_BST_BOOST_MODE); + aw8692x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + break; + case AW_TRIG_MODE: + aw_info("enter trig mode"); + aw_haptic->play_mode = AW_TRIG_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PLAYCFG3, + AW8692X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8692X_BIT_PLAYCFG3_PLAY_MODE_RAM); + aw8692x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + break; + case AW_CONT_MODE: + aw_info("enter cont mode"); + aw_haptic->play_mode = AW_CONT_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PLAYCFG3, + AW8692X_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW8692X_BIT_PLAYCFG3_PLAY_MODE_CONT); + /* bst mode */ + aw8692x_bst_mode_config(aw_haptic, AW_BST_BYPASS_MODE); + aw8692x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_HW_COMP_MODE); + break; + default: + aw_err("play mode %d error", play_mode); + break; + } +} + +static void aw8692x_stop(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int ret = 0; + + aw_haptic->play_mode = AW_STANDBY_MODE; + reg_val = AW8692X_BIT_PLAYCFG4_STOP_ON; + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_PLAYCFG4, ®_val, + AW_I2C_BYTE_ONE); + ret = aw8692x_wait_enter_standby(aw_haptic); + if (ret < 0) { + aw_err("force to enter standby mode!"); + aw8692x_play_mode(aw_haptic, AW_STANDBY_MODE); + } +} + +static void aw8692x_ram_init(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_SYSCTRL3, + AW8692X_BIT_SYSCTRL3_EN_RAMINIT_MASK, + AW8692X_BIT_SYSCTRL3_EN_RAMINIT_ON); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_SYSCTRL3, + AW8692X_BIT_SYSCTRL3_EN_RAMINIT_MASK, + AW8692X_BIT_SYSCTRL3_EN_RAMINIT_OFF); + } +} + +static void aw8692x_upload_lra(struct aw_haptic *aw_haptic, uint32_t flag) +{ + uint8_t cali_data = 0; + + aw8692x_reg_unlock(aw_haptic, true); + switch (flag) { + case AW_WRITE_ZERO: + aw_info("write zero to trim_lra!"); + break; + case AW_F0_CALI_LRA: + aw_info("write f0_cali_data to trim_lra = 0x%02X", + aw_haptic->f0_cali_data); + cali_data = (char)aw_haptic->f0_cali_data & + AW8692X_BIT_ANACFG20_TRIM_LRA; + break; + case AW_OSC_CALI_LRA: + aw_info("write osc_cali_data to trim_lra = 0x%02X", + aw_haptic->osc_cali_data); + cali_data = (char)aw_haptic->osc_cali_data & + AW8692X_BIT_ANACFG20_TRIM_LRA; + break; + default: + aw_err("error param, write f0_cali_data to trim_lra!"); + cali_data = (char)aw_haptic->f0_cali_data & + AW8692X_BIT_ANACFG20_TRIM_LRA; + break; + } + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_ANACFG20, &cali_data, + AW_I2C_BYTE_ONE); + aw8692x_reg_unlock(aw_haptic, false); +} + +static uint8_t aw8692x_get_trim_lra(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_ANACFG20, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= AW8692X_BIT_ANACFG20_TRIM_LRA; + return reg_val; +} + +static void aw8692x_vbat_mode_config(struct aw_haptic *aw_haptic, uint8_t flag) +{ + if (flag == AW_CONT_VBAT_HW_COMP_MODE) { + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_VBATCTRL, + AW8692X_BIT_VBATCTRL_VBAT_MODE_MASK, + AW8692X_BIT_VBATCTRL_VBAT_MODE_HW); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_VBATCTRL, + AW8692X_BIT_VBATCTRL_VBAT_MODE_MASK, + AW8692X_BIT_VBATCTRL_VBAT_MODE_SW); + } +} + +static void aw8692x_protect_config(struct aw_haptic *aw_haptic, uint8_t addr, + uint8_t val) +{ + aw_info("enter"); + if (addr == 1) { + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PWMCFG2, + AW8692X_BIT_PWMCFG2_PRCT_MODE_MASK, + AW8692X_BIT_PWMCFG2_PRCT_MODE_VALID); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PWMCFG1, + AW8692X_BIT_PWMCFG1_PRC_EN_MASK, + AW8692X_BIT_PWMCFG1_PRC_ENABLE); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PWMCFG3, + AW8692X_BIT_PWMCFG3_PR_EN_MASK, + AW8692X_BIT_PWMCFG3_PR_ENABLE); + } else if (addr == 0) { + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PWMCFG2, + AW8692X_BIT_PWMCFG2_PRCT_MODE_MASK, + AW8692X_BIT_PWMCFG2_PRCT_MODE_INVALID); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PWMCFG1, + AW8692X_BIT_PWMCFG1_PRC_EN_MASK, + AW8692X_BIT_PWMCFG1_PRC_DISABLE); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PWMCFG3, + AW8692X_BIT_PWMCFG3_PR_EN_MASK, + AW8692X_BIT_PWMCFG3_PR_DISABLE); + } else if (addr == AW8692X_REG_PWMCFG1) { + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PWMCFG1, + AW8692X_BIT_PWMCFG1_PRCTIME_MASK, val); + } else if (addr == AW8692X_REG_PWMCFG3) { + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PWMCFG3, + AW8692X_BIT_PWMCFG3_PRLVL_MASK, val); + } else if (addr == AW8692X_REG_PWMCFG4) { + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_PWMCFG4, &val, + AW_I2C_BYTE_ONE); + } +} + +static void aw8692x_cont_config(struct aw_haptic *aw_haptic) +{ + /* uint8_t drv1_time = 0xFF; */ + uint8_t drv2_time = 0xFF; + + /* work mode */ + aw8692x_play_mode(aw_haptic, AW_CONT_MODE); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_CONTCFG6, + AW8692X_BIT_CONTCFG6_TRACK_EN_MASK, + AW8692X_BIT_CONTCFG6_TRACK_DISABLE); + /* f0 driver level */ + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_CONTCFG6, + AW8692X_BIT_CONTCFG6_DRV1_LVL_MASK, + aw_haptic->info.cont_drv1_lvl); + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_CONTCFG7, + &aw_haptic->info.cont_drv2_lvl, AW_I2C_BYTE_ONE); + /* DRV1_TIME */ + /* haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_CONTCFG8, &drv1_time, + * AW_I2C_BYTE_ONE); + */ + /* DRV2_TIME */ + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_CONTCFG9, &drv2_time, + AW_I2C_BYTE_ONE); + /* cont play go */ + aw8692x_play_go(aw_haptic, true); +} + +static void aw8692x_one_wire_init(struct aw_haptic *aw_haptic) +{ + uint8_t trig_prio = 0x6c; + uint8_t delay_2p5ms = AW8692X_BIT_START_DLY_2P5MS; + + aw_info("enter"); + /*if enable one-wire, trig1 priority must be less than trig2 and trig3*/ + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_GLBCFG4, &trig_prio, + AW_I2C_BYTE_ONE); + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_GLBCFG2, &delay_2p5ms, + AW_I2C_BYTE_ONE); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_TRGCFG8, + AW8692X_BIT_TRGCFG8_TRG_ONEWIRE_MASK, + AW8692X_BIT_TRGCFG8_TRG_ONEWIRE_ENABLE); +} + +static void aw8692x_trig1_param_init(struct aw_haptic *aw_haptic) +{ + aw_haptic->trig[0].trig_level = aw_haptic->info.trig_cfg[0]; + aw_haptic->trig[0].trig_polar = aw_haptic->info.trig_cfg[1]; + aw_haptic->trig[0].pos_enable = aw_haptic->info.trig_cfg[2]; + aw_haptic->trig[0].pos_sequence = aw_haptic->info.trig_cfg[3]; + aw_haptic->trig[0].neg_enable = aw_haptic->info.trig_cfg[4]; + aw_haptic->trig[0].neg_sequence = aw_haptic->info.trig_cfg[5]; + aw_haptic->trig[0].trig_brk = aw_haptic->info.trig_cfg[6]; + aw_haptic->trig[0].trig_bst = aw_haptic->info.trig_cfg[7]; +} + +static void aw8692x_trig2_param_init(struct aw_haptic *aw_haptic) +{ + aw_haptic->trig[1].trig_level = aw_haptic->info.trig_cfg[8]; + aw_haptic->trig[1].trig_polar = aw_haptic->info.trig_cfg[9]; + aw_haptic->trig[1].pos_enable = aw_haptic->info.trig_cfg[10]; + aw_haptic->trig[1].pos_sequence = aw_haptic->info.trig_cfg[11]; + aw_haptic->trig[1].neg_enable = aw_haptic->info.trig_cfg[12]; + aw_haptic->trig[1].neg_sequence = aw_haptic->info.trig_cfg[13]; + aw_haptic->trig[1].trig_brk = aw_haptic->info.trig_cfg[14]; + aw_haptic->trig[1].trig_bst = aw_haptic->info.trig_cfg[15]; +} + +static void aw8692x_trig3_param_init(struct aw_haptic *aw_haptic) +{ + aw_haptic->trig[2].trig_level = aw_haptic->info.trig_cfg[16]; + aw_haptic->trig[2].trig_polar = aw_haptic->info.trig_cfg[17]; + aw_haptic->trig[2].pos_enable = aw_haptic->info.trig_cfg[18]; + aw_haptic->trig[2].pos_sequence = aw_haptic->info.trig_cfg[19]; + aw_haptic->trig[2].neg_enable = aw_haptic->info.trig_cfg[20]; + aw_haptic->trig[2].neg_sequence = aw_haptic->info.trig_cfg[21]; + aw_haptic->trig[2].trig_brk = aw_haptic->info.trig_cfg[22]; + aw_haptic->trig[2].trig_bst = aw_haptic->info.trig_cfg[23]; +} + +static void aw8692x_trig1_param_config(struct aw_haptic *aw_haptic) +{ + uint8_t trig_config = 0; + + if (aw_haptic->trig[0].trig_level) + trig_config |= AW8692X_BIT_TRGCFG7_TRG1_MODE_LEVEL; + else + trig_config |= AW8692X_BIT_TRGCFG7_TRG1_MODE_EDGE; + + if (aw_haptic->trig[0].trig_polar) + trig_config |= AW8692X_BIT_TRGCFG7_TRG1_POLAR_NEG; + else + trig_config |= AW8692X_BIT_TRGCFG7_TRG1_POLAR_POS; + + if (aw_haptic->trig[0].trig_brk) + trig_config |= AW8692X_BIT_TRGCFG7_TRG1_AUTO_BRK_ENABLE; + else + trig_config |= AW8692X_BIT_TRGCFG7_TRG1_AUTO_BRK_DISABLE; + + if (aw_haptic->trig[0].trig_bst) + trig_config |= AW8692X_BIT_TRGCFG7_TRG1_BST_ENABLE; + else + trig_config |= AW8692X_BIT_TRGCFG7_TRG1_BST_DISABLE; + + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_TRGCFG7, + (AW8692X_BIT_TRGCFG7_TRG1_MODE_MASK & + AW8692X_BIT_TRGCFG7_TRG1_POLAR_MASK & + AW8692X_BIT_TRGCFG7_TRG1_AUTO_BRK_MASK & + AW8692X_BIT_TRGCFG7_TRG1_BST_MASK), + trig_config); + + trig_config = 0; + + /* pos config */ + if (aw_haptic->trig[0].pos_enable) + trig_config |= AW8692X_BIT_TRG_ENABLE; + else + trig_config |= AW8692X_BIT_TRG_DISABLE; + + trig_config |= aw_haptic->trig[0].pos_sequence; + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_TRGCFG1, &trig_config, + AW_I2C_BYTE_ONE); + + trig_config = 0; + + /* neq config */ + if (aw_haptic->trig[0].neg_enable) + trig_config |= AW8692X_BIT_TRG_ENABLE; + else + trig_config |= AW8692X_BIT_TRG_DISABLE; + + trig_config |= aw_haptic->trig[0].neg_sequence; + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_TRGCFG4, &trig_config, + AW_I2C_BYTE_ONE); +} + +static void aw8692x_trig2_param_config(struct aw_haptic *aw_haptic) +{ + uint8_t trig_config = 0; + + if (aw_haptic->trig[1].trig_level) + trig_config |= AW8692X_BIT_TRGCFG7_TRG2_MODE_LEVEL; + else + trig_config |= AW8692X_BIT_TRGCFG7_TRG2_MODE_EDGE; + + if (aw_haptic->trig[1].trig_polar) + trig_config |= AW8692X_BIT_TRGCFG7_TRG2_POLAR_NEG; + else + trig_config |= AW8692X_BIT_TRGCFG7_TRG2_POLAR_POS; + + if (aw_haptic->trig[1].trig_brk) + trig_config |= AW8692X_BIT_TRGCFG7_TRG2_AUTO_BRK_ENABLE; + else + trig_config |= AW8692X_BIT_TRGCFG7_TRG2_AUTO_BRK_DISABLE; + + if (aw_haptic->trig[1].trig_bst) + trig_config |= AW8692X_BIT_TRGCFG7_TRG2_BST_ENABLE; + else + trig_config |= AW8692X_BIT_TRGCFG7_TRG2_BST_DISABLE; + + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_TRGCFG7, + (AW8692X_BIT_TRGCFG7_TRG2_MODE_MASK & + AW8692X_BIT_TRGCFG7_TRG2_POLAR_MASK & + AW8692X_BIT_TRGCFG7_TRG2_AUTO_BRK_MASK & + AW8692X_BIT_TRGCFG7_TRG2_BST_MASK), + trig_config); + + trig_config = 0; + + /* pos config */ + if (aw_haptic->trig[1].pos_enable) + trig_config |= AW8692X_BIT_TRG_ENABLE; + else + trig_config |= AW8692X_BIT_TRG_DISABLE; + + trig_config |= aw_haptic->trig[1].pos_sequence; + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_TRGCFG2, &trig_config, + AW_I2C_BYTE_ONE); + + trig_config = 0; + + /* neq config */ + if (aw_haptic->trig[1].neg_enable) + trig_config |= AW8692X_BIT_TRG_ENABLE; + else + trig_config |= AW8692X_BIT_TRG_DISABLE; + + trig_config |= aw_haptic->trig[1].neg_sequence; + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_TRGCFG5, &trig_config, + AW_I2C_BYTE_ONE); +} + +static void aw8692x_trig3_param_config(struct aw_haptic *aw_haptic) +{ + uint8_t trig_config = 0; + + if (aw_haptic->trig[2].trig_level) + trig_config |= AW8692X_BIT_TRGCFG8_TRG3_MODE_LEVEL; + else + trig_config |= AW8692X_BIT_TRGCFG8_TRG3_MODE_EDGE; + + if (aw_haptic->trig[2].trig_polar) + trig_config |= AW8692X_BIT_TRGCFG8_TRG3_POLAR_NEG; + else + trig_config |= AW8692X_BIT_TRGCFG8_TRG3_POLAR_POS; + + if (aw_haptic->trig[2].trig_brk) + trig_config |= AW8692X_BIT_TRGCFG8_TRG3_AUTO_BRK_ENABLE; + else + trig_config |= AW8692X_BIT_TRGCFG8_TRG3_AUTO_BRK_DISABLE; + + if (aw_haptic->trig[2].trig_bst) + trig_config |= AW8692X_BIT_TRGCFG8_TRG3_BST_ENABLE; + else + trig_config |= AW8692X_BIT_TRGCFG8_TRG3_BST_DISABLE; + + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_TRGCFG8, + (AW8692X_BIT_TRGCFG8_TRG3_MODE_MASK & + AW8692X_BIT_TRGCFG8_TRG3_POLAR_MASK & + AW8692X_BIT_TRGCFG8_TRG3_AUTO_BRK_MASK & + AW8692X_BIT_TRGCFG8_TRG3_BST_MASK), + trig_config); + + trig_config = 0; + + /* pos config */ + if (aw_haptic->trig[2].pos_enable) + trig_config |= AW8692X_BIT_TRG_ENABLE; + else + trig_config |= AW8692X_BIT_TRG_DISABLE; + + trig_config |= aw_haptic->trig[2].pos_sequence; + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_TRGCFG3, &trig_config, + AW_I2C_BYTE_ONE); + + trig_config = 0; + + /* neq config */ + if (aw_haptic->trig[2].neg_enable) + trig_config |= AW8692X_BIT_TRG_ENABLE; + else + trig_config |= AW8692X_BIT_TRG_DISABLE; + + trig_config |= aw_haptic->trig[2].neg_sequence; + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_TRGCFG6, &trig_config, + AW_I2C_BYTE_ONE); +} + +static void aw8692x_auto_bst_enable(struct aw_haptic *aw_haptic, uint8_t flag) +{ + aw_haptic->auto_boost = flag; + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PLAYCFG3, + AW8692X_BIT_PLAYCFG3_AUTO_BST_MASK, + AW8692X_BIT_PLAYCFG3_AUTO_BST_ENABLE); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_PLAYCFG3, + AW8692X_BIT_PLAYCFG3_AUTO_BST_MASK, + AW8692X_BIT_PLAYCFG3_AUTO_BST_DISABLE); + } +} + +static void aw8692x_interrupt_setup(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_info("reg SYSINT=0x%02X", reg_val); + /* edge int mode */ + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_SYSCTRL4, + (AW8692X_BIT_SYSCTRL4_INT_MODE_MASK & + AW8692X_BIT_SYSCTRL4_INT_EDGE_MODE_MASK), + (AW8692X_BIT_SYSCTRL4_INT_MODE_EDGE | + AW8692X_BIT_SYSCTRL4_INT_EDGE_MODE_POS)); + /* int enable */ + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_SYSINTM, + (AW8692X_BIT_SYSINTM_BST_SCPM_MASK & + AW8692X_BIT_SYSINTM_BST_OVPM_MASK & + AW8692X_BIT_SYSINTM_UVLM_MASK & + AW8692X_BIT_SYSINTM_OCDM_MASK & + AW8692X_BIT_SYSINTM_OTM_MASK), + (AW8692X_BIT_SYSINTM_BST_SCPM_ON | + AW8692X_BIT_SYSINTM_BST_OVPM_OFF | + AW8692X_BIT_SYSINTM_UVLM_ON | + AW8692X_BIT_SYSINTM_OCDM_ON | + AW8692X_BIT_SYSINTM_OTM_ON)); +} + +static int aw8692x_judge_rtp_going(struct aw_haptic *aw_haptic) +{ + uint8_t glb_state = 0; + uint8_t rtp_state = 0; + + glb_state = aw8692x_get_glb_state(aw_haptic); + if (glb_state == AW8692X_BIT_GLBRD5_STATE_RTP_GO) { + rtp_state = 1; /*is going on */ + aw_info("rtp_routine_on"); + } + return rtp_state; +} + +static void aw8692x_get_ram_data(struct aw_haptic *aw_haptic, char *buf) +{ + int i = 0; + int size = 0; + + while (i < aw_haptic->ram.len) { + if ((aw_haptic->ram.len - i) < AW_RAMDATA_RD_BUFFER_SIZE) + size = aw_haptic->ram.len - i; + else + size = AW_RAMDATA_RD_BUFFER_SIZE; + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_RAMDATA, buf + i, + size); + i += size; + } +} + +static void aw8692x_get_first_wave_addr(struct aw_haptic *aw_haptic, + uint8_t *wave_addr) +{ + uint8_t reg_val[3] = {0}; + + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_RAMDATA, reg_val, + AW_I2C_BYTE_THREE); + wave_addr[0] = reg_val[1]; + wave_addr[1] = reg_val[2]; +} + +static void aw8692x_get_wav_seq(struct aw_haptic *aw_haptic, uint32_t len) +{ + uint8_t i = 0; + uint8_t reg_val[AW_SEQUENCER_SIZE] = {0}; + + if (len > AW_SEQUENCER_SIZE) + len = AW_SEQUENCER_SIZE; + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_WAVCFG1, reg_val, len); + for (i = 0; i < len; i++) + aw_haptic->seq[i] = reg_val[i]; +} + +static size_t aw8692x_get_wav_loop(struct aw_haptic *aw_haptic, char *buf) +{ + uint8_t i = 0; + uint8_t reg_val[AW_SEQUENCER_LOOP_SIZE] = {0}; + size_t count = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_WAVCFG9, reg_val, + AW_SEQUENCER_LOOP_SIZE); + + for (i = 0; i < AW_SEQUENCER_LOOP_SIZE; i++) { + aw_haptic->loop[i * 2 + 0] = (reg_val[i] >> 4) & 0x0F; + aw_haptic->loop[i * 2 + 1] = (reg_val[i] >> 0) & 0x0F; + + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d loop: 0x%02x\n", i * 2 + 1, + aw_haptic->loop[i * 2 + 0]); + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d loop: 0x%02x\n", i * 2 + 2, + aw_haptic->loop[i * 2 + 1]); + } + return count; +} + +static void aw8692x_irq_clear(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_dbg("reg SYSINT=0x%02X", reg_val); +} + +static uint8_t aw8692x_get_prctmode(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_PWMCFG2, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= 0x08; + return reg_val; +} + +static int aw8692x_get_irq_state(struct aw_haptic *aw_haptic) +{ + int ret = 0; + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_dbg("reg SYSINT=0x%02X", reg_val); + + if (reg_val & AW8692X_BIT_SYSINT_BST_SCPI) { + ret = AW_IRQ_BST_SCP; + aw_err("chip scp int error"); + } + + if (reg_val & AW8692X_BIT_SYSINT_BST_OVPI) { + ret = AW_IRQ_BST_OVP; + aw_err("chip ov int error"); + } + + if (reg_val & AW8692X_BIT_SYSINT_UVLI) { + ret = AW_IRQ_UVLO; + aw_err("chip uvlo int error"); + } + + if (reg_val & AW8692X_BIT_SYSINT_OCDI) { + ret = AW_IRQ_OCD; + aw_err("chip over current int error"); + } + + if (reg_val & AW8692X_BIT_SYSINT_OTI) { + ret = AW_IRQ_OT; + aw_err("chip over temperature int error"); + } + + if (reg_val & AW8692X_BIT_SYSINT_DONEI) { + ret = AW_IRQ_DONE; + aw_info("chip playback done"); + } + + if (reg_val & AW8692X_BIT_SYSINT_FF_AFI) { + ret = AW_IRQ_ALMOST_FULL; + aw_info("aw_haptic rtp mode fifo almost full!"); + } + + if (reg_val & AW8692X_BIT_SYSINT_FF_AEI) + ret = AW_IRQ_ALMOST_EMPTY; + + return ret; +} + +static int aw8692x_read_f0(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val[2] = {0}; + uint32_t f0_reg = 0; + +#ifdef AW_LRA_F0_DEFAULT + /* lra_f0 */ + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_CONTCFG14, reg_val, + AW_I2C_BYTE_TWO); + f0_reg = (reg_val[0] << 8) | reg_val[1]; + if (!f0_reg) { + aw_haptic->f0 = 0; + aw_err("lra_f0 is error, f0_reg=0"); + return -ERANGE; + } + aw_haptic->f0 = (uint32_t)AW8692X_F0_FARMULA(f0_reg); + aw_info("lra_f0=%d", aw_haptic->f0); +#else + /* cont_f0 */ + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_CONTCFG16, reg_val, + AW_I2C_BYTE_TWO); + f0_reg = (reg_val[0] << 8) | reg_val[1]; + if (!f0_reg) { + aw_haptic->f0 = 0; + aw_err("cont_f0 is error, f0_reg=0"); + return -ERANGE; + } + aw_haptic->f0 = (uint32_t)AW8692X_F0_FARMULA(f0_reg); + aw_info("cont_f0=%d", aw_haptic->f0); +#endif + return 0; +} + +static int aw8692x_get_f0(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint8_t brk_en_default = 0; + uint8_t d2s_gain_default = 0; + uint8_t cont_config[3] = {0}; + int ret = 0; + + aw_haptic->f0 = aw_haptic->info.f0_pre; + /* enter standby mode */ + aw8692x_stop(aw_haptic); + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_DETCFG2, ®_val, + AW_I2C_BYTE_ONE); + d2s_gain_default = reg_val & AW8692X_BIT_DETCFG2_D2S_GAIN; + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_DETCFG2, + AW8692X_BIT_DETCFG2_D2S_GAIN_MASK, + AW8692X_BIT_DETCFG2_D2S_GAIN_40); + /* f0 calibrate work mode */ + aw8692x_play_mode(aw_haptic, AW_CONT_MODE); + /* enable f0 detect */ + aw8692x_f0_detect(aw_haptic, true); + /* cont config */ + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_CONTCFG6, + AW8692X_BIT_CONTCFG6_TRACK_EN_MASK, + AW8692X_BIT_CONTCFG6_TRACK_DISABLE); + /* enable auto break */ + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_PLAYCFG3, ®_val, + AW_I2C_BYTE_ONE); + brk_en_default = 0x04 & reg_val; + aw8692x_auto_break_mode(aw_haptic, true); + /* f0 driver level & time */ + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_CONTCFG6, + AW8692X_BIT_CONTCFG6_DRV1_LVL_MASK, + aw_haptic->info.cont_drv1_lvl); + cont_config[0] = aw_haptic->info.cont_drv2_lvl; + cont_config[1] = aw_haptic->info.cont_drv1_time; + cont_config[2] = aw_haptic->info.cont_drv2_time; + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_CONTCFG7, cont_config, + AW_I2C_BYTE_THREE); + /* TRACK_MARGIN */ + if (!aw_haptic->info.cont_track_margin) { + aw_err("aw_haptic->info.cont_track_margin = 0"); + } else { + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_CONTCFG11, + &aw_haptic->info.cont_track_margin, + AW_I2C_BYTE_ONE); + } + /* DRV_WIDTH */ + if (!aw_haptic->info.f0_pre) + return -ERANGE; + cont_config[0] = AW_DRV_WIDTH_FARMULA(aw_haptic->info.f0_pre, + aw_haptic->info.cont_brk_gain, + aw_haptic->info.cont_track_margin); + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_CONTCFG3, &cont_config[0], + AW_I2C_BYTE_ONE); + /* play go */ + aw8692x_play_go(aw_haptic, true); + usleep_range(20000, 20500); + aw8692x_wait_enter_standby(aw_haptic); + ret = aw8692x_read_f0(aw_haptic); + /* restore default config */ + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_DETCFG2, + AW8692X_BIT_DETCFG2_D2S_GAIN_MASK, + d2s_gain_default); + aw8692x_f0_detect(aw_haptic, false); + /* recover auto break config */ + if (brk_en_default) + aw8692x_auto_break_mode(aw_haptic, true); + else + aw8692x_auto_break_mode(aw_haptic, false); + return ret; +} + +static int aw8692x_ram_get_f0(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint8_t brk_en_default = 0; + uint8_t d2s_gain_default = 0; + int ret = 0; + + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return -ERANGE; + } + if (aw_haptic->ram.ram_num < AW_RAM_GET_F0_SEQ) { + aw_err("miss ram get f0 waveform!"); + return -ERANGE; + } + aw_haptic->f0 = aw_haptic->info.f0_pre; + /* enter standby mode */ + aw8692x_stop(aw_haptic); + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_DETCFG2, ®_val, + AW_I2C_BYTE_ONE); + d2s_gain_default = reg_val & AW8692X_BIT_DETCFG2_D2S_GAIN; + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_DETCFG2, + AW8692X_BIT_DETCFG2_D2S_GAIN_MASK, + AW8692X_BIT_DETCFG2_D2S_GAIN_40); + /* f0 calibrate work mode */ + aw8692x_play_mode(aw_haptic, AW_RAM_MODE); + /* enable f0 detect */ + aw8692x_f0_detect(aw_haptic, true); + /* enable auto break */ + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_PLAYCFG3, ®_val, + AW_I2C_BYTE_ONE); + brk_en_default = 0x04 & reg_val; + aw8692x_auto_break_mode(aw_haptic, true); + aw8692x_set_bst_vol(aw_haptic, 8000); + aw8692x_set_wav_seq(aw_haptic, 0x00, AW_RAM_GET_F0_SEQ); + aw8692x_set_wav_seq(aw_haptic, 0x01, 0x00); + aw8692x_set_wav_loop(aw_haptic, 0x00, 0x02); + /* play go */ + aw8692x_play_go(aw_haptic, true); + usleep_range(20000, 20500); + aw8692x_wait_enter_standby(aw_haptic); + ret = aw8692x_read_f0(aw_haptic); + /* restore default config */ + aw8692x_set_bst_vol(aw_haptic, aw_haptic->vmax); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_DETCFG2, + AW8692X_BIT_DETCFG2_D2S_GAIN_MASK, + d2s_gain_default); + aw8692x_f0_detect(aw_haptic, false); + /* recover auto break config */ + if (brk_en_default) + aw8692x_auto_break_mode(aw_haptic, true); + else + aw8692x_auto_break_mode(aw_haptic, false); + return ret; +} + +static uint8_t aw8692x_rtp_get_fifo_afs(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_SYSST, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= AW8692X_BIT_SYSST_FF_AFS; + reg_val >>= 3; + return reg_val; +} + +static uint8_t aw8692x_rtp_get_fifo_aes(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_SYSST, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= AW8692X_BIT_SYSST_FF_AES; + reg_val >>= 4; + return reg_val; +} + +static uint8_t aw8692x_get_osc_status(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_SYSST2, &state, + AW_I2C_BYTE_ONE); + state &= AW8692X_BIT_SYSST2_FF_EMPTY; + return state; +} + +static int aw8692x_select_d2s_gain(uint8_t reg) +{ + int d2s_gain = 0; + + switch (reg) { + case AW8692X_BIT_DETCFG2_D2S_GAIN_1: + d2s_gain = 1; + break; + case AW8692X_BIT_DETCFG2_D2S_GAIN_2: + d2s_gain = 2; + break; + case AW8692X_BIT_DETCFG2_D2S_GAIN_4: + d2s_gain = 4; + break; + case AW8692X_BIT_DETCFG2_D2S_GAIN_8: + d2s_gain = 8; + break; + case AW8692X_BIT_DETCFG2_D2S_GAIN_10: + d2s_gain = 10; + break; + case AW8692X_BIT_DETCFG2_D2S_GAIN_16: + d2s_gain = 16; + break; + case AW8692X_BIT_DETCFG2_D2S_GAIN_20: + d2s_gain = 20; + break; + case AW8692X_BIT_DETCFG2_D2S_GAIN_40: + d2s_gain = 40; + break; + default: + d2s_gain = -1; + break; + } + return d2s_gain; +} + +static void aw8692x_get_lra_resistance(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint8_t reg_array[2] = {0}; + uint8_t adc_fs_default = 0; + uint8_t d2s_gain = 0; + uint32_t lra_code = 0; + + aw8692x_ram_init(aw_haptic, true); + aw8692x_stop(aw_haptic); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_DETCFG2, + AW8692X_BIT_DETCFG2_DET_SEQ0_MASK, + AW8692X_BIT_DETCFG2_DET_SEQ0_RL); + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_DETCFG1, ®_val, + AW_I2C_BYTE_ONE); + adc_fs_default = reg_val & AW8692X_BIT_DETCFG1_ADC_FS; + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_DETCFG1, + (AW8692X_BIT_DETCFG1_ADC_FS_MASK & + AW8692X_BIT_DETCFG1_DET_GO_MASK), + (AW8692X_BIT_DETCFG1_ADC_FS_96KHZ | + AW8692X_BIT_DETCFG1_DET_GO_DET_SEQ0)); + usleep_range(3000, 3500); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_DETCFG1, + AW8692X_BIT_DETCFG1_DET_GO_MASK, + AW8692X_BIT_DETCFG1_DET_GO_NA); + /* restore default config*/ + aw8692x_ram_init(aw_haptic, false); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_DETCFG1, + AW8692X_BIT_DETCFG1_ADC_FS_MASK, + adc_fs_default); + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_DETCFG2, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= AW8692X_BIT_DETCFG2_D2S_GAIN; + d2s_gain = aw8692x_select_d2s_gain(reg_val); + if (d2s_gain < 0) { + aw_err("d2s_gain is error"); + return; + } + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_DETRD1, reg_array, + AW_I2C_BYTE_TWO); + lra_code = ((reg_array[0] & AW8692X_BIT_DETRD1_AVG_DATA) << 8) + + reg_array[1]; + aw_haptic->lra = AW8692X_LRA_FORMULA(lra_code, d2s_gain); +} + + +static void aw8692x_set_repeat_seq(struct aw_haptic *aw_haptic, uint8_t seq) +{ + aw8692x_set_wav_seq(aw_haptic, 0x00, seq); + aw8692x_set_wav_loop(aw_haptic, 0x00, AW8692X_WAVLOOP_INIFINITELY); +} + +static void aw8692x_get_vbat(struct aw_haptic *aw_haptic) +{ + uint8_t reg_array[2] = {0}; + uint32_t vbat_code = 0; + + aw8692x_stop(aw_haptic); + aw8692x_ram_init(aw_haptic, true); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_DETCFG2, + AW8692X_BIT_DETCFG2_DET_SEQ0_MASK, + AW8692X_BIT_DETCFG2_DET_SEQ0_VBAT); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_DETCFG1, + AW8692X_BIT_DETCFG1_DET_GO_MASK, + AW8692X_BIT_DETCFG1_DET_GO_DET_SEQ0); + usleep_range(3000, 3500); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_DETCFG1, + AW8692X_BIT_DETCFG1_DET_GO_MASK, + AW8692X_BIT_DETCFG1_DET_GO_NA); + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_DETRD1, reg_array, + AW_I2C_BYTE_TWO); + vbat_code = ((reg_array[0] & AW8692X_BIT_DETRD1_AVG_DATA) << 8) + + reg_array[1]; + aw_haptic->vbat = AW8692X_VBAT_FORMULA(vbat_code); + + if (aw_haptic->vbat > AW_VBAT_MAX) { + aw_haptic->vbat = AW_VBAT_MAX; + aw_info("vbat max limit = %d", aw_haptic->vbat); + } + if (aw_haptic->vbat < AW_VBAT_MIN) { + aw_haptic->vbat = AW_VBAT_MIN; + aw_info("vbat min limit = %d", aw_haptic->vbat); + } + aw_info("awinic->vbat=%dmV, vbat_code=0x%02X", + aw_haptic->vbat, vbat_code); + aw8692x_ram_init(aw_haptic, false); +} + +static ssize_t aw8692x_get_reg(struct aw_haptic *aw_haptic, ssize_t len, + char *buf) +{ + uint8_t i = 0; + uint8_t reg_array[AW8692X_REG_ANACFG22 + 1] = {0}; + + for (i = 0; i < AW8692X_REG_RTPDATA; i++) + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_RSTCFG, reg_array, + AW8692X_REG_RTPDATA); + for (i = AW8692X_REG_RTPDATA + 1; i < AW8692X_REG_RAMDATA; i++) + haptic_hv_i2c_reads(aw_haptic, (AW8692X_REG_RTPDATA + 1), + ®_array[AW8692X_REG_RTPDATA + 1], + (AW8692X_REG_RAMDATA - AW8692X_REG_RTPDATA - 1)); + for (i = AW8692X_REG_RAMDATA + 1; i <= AW8692X_REG_ANACFG22; i++) + haptic_hv_i2c_reads(aw_haptic, (AW8692X_REG_RAMDATA + 1), + ®_array[AW8692X_REG_RAMDATA + 1], + (AW8692X_REG_ANACFG22 - AW8692X_REG_RAMDATA - 1)); + for (i = 0; i <= AW8692X_REG_ANACFG22; i++) + if ((i != AW8692X_REG_RTPDATA) && (i != AW8692X_REG_RAMDATA)) + len += snprintf(buf + len, PAGE_SIZE - len, + "reg:0x%02X=0x%02X\n", i, reg_array[i]); + return len; +} + +static void aw8692x_offset_cali(struct aw_haptic *aw_haptic) +{ + +} + +static void aw8692x_trig_init(struct aw_haptic *aw_haptic) +{ + aw_info("enter!"); + if (aw_haptic->info.is_enabled_one_wire) { + aw_info("one wire is enabled!"); + aw8692x_one_wire_init(aw_haptic); + } else { + aw8692x_trig1_param_init(aw_haptic); + aw8692x_trig1_param_config(aw_haptic); + } + aw8692x_trig2_param_init(aw_haptic); + aw8692x_trig3_param_init(aw_haptic); + aw8692x_trig2_param_config(aw_haptic); + aw8692x_trig3_param_config(aw_haptic); +} + +#ifdef AW_CHECK_RAM_DATA +static int aw8692x_check_ram_data(struct aw_haptic *aw_haptic, + uint8_t *cont_data, + uint8_t *ram_data, uint32_t len) +{ + int i = 0; + + for (i = 0; i < len; i++) { + if (ram_data[i] != cont_data[i]) { + aw_err("check ramdata error, addr=0x%04x, ram_data=0x%02x, file_data=0x%02x", + i, ram_data[i], cont_data[i]); + return -ERANGE; + } + } + return 0; +} +#endif + +static int aw8692x_container_update(struct aw_haptic *aw_haptic, + struct aw_haptic_container *awinic_cont) +{ + uint8_t ae_addr_h = 0; + uint8_t af_addr_h = 0; + uint8_t ae_addr_l = 0; + uint8_t af_addr_l = 0; + uint8_t reg_array[3] = {0}; + uint32_t base_addr = 0; + uint32_t shift = 0; + int i = 0; + int len = 0; + int ret = 0; + +#ifdef AW_CHECK_RAM_DATA + uint8_t ram_data[AW_RAMDATA_RD_BUFFER_SIZE] = {0}; +#endif + mutex_lock(&aw_haptic->lock); + aw_haptic->ram.baseaddr_shift = 2; + aw_haptic->ram.ram_shift = 4; + /* RAMINIT Enable */ + aw8692x_ram_init(aw_haptic, true); + /* Enter standby mode */ + aw8692x_stop(aw_haptic); + /* base addr */ + shift = aw_haptic->ram.baseaddr_shift; + aw_haptic->ram.base_addr = + (uint32_t)((awinic_cont->data[0 + shift] << 8) | + (awinic_cont->data[1 + shift])); + base_addr = aw_haptic->ram.base_addr; + aw_info("base_addr = %d", aw_haptic->ram.base_addr); + + /* set FIFO_AE and FIFO_AF addr */ + ae_addr_h = (uint8_t)AW8692X_FIFO_AE_ADDR_H(base_addr); + af_addr_h = (uint8_t)AW8692X_FIFO_AF_ADDR_H(base_addr); + reg_array[0] = ae_addr_h | af_addr_h; + reg_array[1] = (uint8_t)AW8692X_FIFO_AE_ADDR_L(base_addr); + reg_array[2] = (uint8_t)AW8692X_FIFO_AF_ADDR_L(base_addr); + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_RTPCFG3, reg_array, + AW_I2C_BYTE_THREE); + + /* get FIFO_AE and FIFO_AF addr */ + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_RTPCFG3, reg_array, + AW_I2C_BYTE_THREE); + ae_addr_h = ((reg_array[0]) & AW8692X_BIT_RTPCFG3_FIFO_AEH) >> 4; + ae_addr_l = reg_array[1]; + aw_info("almost_empty_threshold = %d", + (uint16_t)((ae_addr_h << 8) | ae_addr_l)); + af_addr_h = ((reg_array[0]) & AW8692X_BIT_RTPCFG3_FIFO_AFH); + af_addr_l = reg_array[2]; + aw_info("almost_full_threshold = %d", + (uint16_t)((af_addr_h << 8) | af_addr_l)); + + aw8692x_set_base_addr(aw_haptic); + aw8692x_set_ram_addr(aw_haptic); + i = aw_haptic->ram.ram_shift; + while (i < awinic_cont->len) { + if ((awinic_cont->len - i) < AW_RAMDATA_WR_BUFFER_SIZE) + len = awinic_cont->len - i; + else + len = AW_RAMDATA_WR_BUFFER_SIZE; + + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_RAMDATA, + &awinic_cont->data[i], len); + i += len; + } + +#ifdef AW_CHECK_RAM_DATA + aw8692x_set_ram_addr(aw_haptic); + i = aw_haptic->ram.ram_shift; + while (i < awinic_cont->len) { + if ((awinic_cont->len - i) < AW_RAMDATA_RD_BUFFER_SIZE) + len = awinic_cont->len - i; + else + len = AW_RAMDATA_RD_BUFFER_SIZE; + + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_RAMDATA, + ram_data, len); + ret = aw8692x_check_ram_data(aw_haptic, &awinic_cont->data[i], + ram_data, len); + if (ret < 0) + break; + i += len; + } + if (ret) + aw_err("ram data check sum error"); + else + aw_info("ram data check sum pass"); +#endif + /* RAMINIT Disable */ + aw8692x_ram_init(aw_haptic, false); + mutex_unlock(&aw_haptic->lock); + return ret; +} + +static uint64_t aw8692x_get_theory_time(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t fre_val = 0; + uint64_t theory_time = 0; + + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_SYSCTRL2, ®_val, + AW_I2C_BYTE_ONE); + fre_val = reg_val & AW8692X_BIT_SYSCTRL2_RCK_FRE; + if (fre_val == AW8692X_RCK_FRE_24K) + theory_time = (aw_haptic->rtp_len / 24000) * 1000000; /*24K*/ + if (fre_val == AW8692X_RCK_FRE_48K) + theory_time = (aw_haptic->rtp_len / 48000) * 1000000; /*48K*/ + aw_info("microsecond:%llu theory_time = %llu", + aw_haptic->microsecond, theory_time); + return theory_time; +} + +static void aw8692x_parse_dt(struct device *dev, struct aw_haptic *aw_haptic, + struct device_node *np) +{ + uint8_t duration_time[3]; + uint8_t trig_config_temp[24]; + uint32_t val = 0; + + val = of_property_read_u8(np, "aw8692x_gain_bypass", + &aw_haptic->info.gain_bypass); + if (val != 0) + aw_info("aw8692x_gain_bypass not found"); + val = of_property_read_u32(np, "f0_pre", &aw_haptic->info.f0_pre); + if (val != 0) + aw_info("vib_f0_pre not found"); + val = of_property_read_u8(np, "f0_cali_percent", + &aw_haptic->info.f0_cali_percent); + if (val != 0) + aw_info("vib_f0_cali_percent not found"); + val = of_property_read_u8(np, "aw8692x_cont_drv1_lvl", + &aw_haptic->info.cont_drv1_lvl); + if (val != 0) + aw_info("vib_cont_drv1_lvl not found"); + val = of_property_read_u32(np, "aw8692x_cont_lra_vrms", + &aw_haptic->info.cont_lra_vrms); + if (val != 0) + aw_info("vib_cont_lra_vrms not found"); + val = of_property_read_u8(np, "aw8692x_cont_drv1_time", + &aw_haptic->info.cont_drv1_time); + if (val != 0) + aw_info("vib_cont_drv1_time not found"); + val = of_property_read_u8(np, "aw8692x_cont_drv2_time", + &aw_haptic->info.cont_drv2_time); + if (val != 0) + aw_info("vib_cont_drv2_time not found"); + val = of_property_read_u8(np, "aw8692x_cont_wait_num", + &aw_haptic->info.cont_wait_num); + if (val != 0) + aw_info("vib_cont_wait_num not found"); + val = of_property_read_u8(np, "aw8692x_cont_brk_time", + &aw_haptic->info.cont_brk_time); + if (val != 0) + aw_info("vib_cont_brk_time not found"); + val = of_property_read_u8(np, "aw8692x_cont_track_margin", + &aw_haptic->info.cont_track_margin); + if (val != 0) + aw_info("vib_cont_track_margin not found"); + val = of_property_read_u8(np, "aw8692x_brk_bst_md", + &aw_haptic->info.brk_bst_md); + if (val != 0) + aw_info("vib_brk_bst_md not found"); + val = of_property_read_u8(np, "aw8692x_cont_tset", + &aw_haptic->info.cont_tset); + if (val != 0) + aw_info("vib_cont_tset not found"); + val = of_property_read_u8(np, "aw8692x_cont_bemf_set", + &aw_haptic->info.cont_bemf_set); + if (val != 0) + aw_info("vib_cont_bemf_set not found"); + val = of_property_read_u8(np, "aw8692x_cont_bst_brk_gain", + &aw_haptic->info.cont_bst_brk_gain); + if (val != 0) + aw_info("vib_cont_bst_brk_gain not found"); + val = of_property_read_u8(np, "aw8692x_cont_brk_gain", + &aw_haptic->info.cont_brk_gain); + if (val != 0) + aw_info("vib_cont_brk_gain not found"); + val = of_property_read_u8(np, "aw8692x_d2s_gain", + &aw_haptic->info.d2s_gain); + if (val != 0) + aw_info("vib_d2s_gain not found"); + val = of_property_read_u8_array(np, "aw8692x_duration_time", + duration_time, + ARRAY_SIZE(duration_time)); + if (val != 0) + aw_info("aw8692x_duration_time not found"); + else + memcpy(aw_haptic->info.duration_time, duration_time, + sizeof(duration_time)); + val = of_property_read_u8_array(np, "aw8692x_trig_config", + trig_config_temp, + ARRAY_SIZE(trig_config_temp)); + if (val != 0) + aw_info("vib_trig_config not found"); + else + memcpy(aw_haptic->info.trig_cfg, trig_config_temp, + sizeof(trig_config_temp)); + val = of_property_read_u32(np, "aw8692x_bst_vol_default", + &aw_haptic->info.bst_vol_default); + if (val != 0) + aw_info("vib_bst_vol_default not found"); + aw_haptic->info.is_enabled_auto_bst = + of_property_read_bool(np, "aw8692x_is_enabled_auto_bst"); + aw_info("aw_haptic->info.is_enabled_auto_bst = %d", + aw_haptic->info.is_enabled_auto_bst); + aw_haptic->info.is_enabled_one_wire = of_property_read_bool(np, + "aw8692x_is_enabled_one_wire"); + aw_info("aw_haptic->info.is_enabled_one_wire = %d", + aw_haptic->info.is_enabled_one_wire); +} + +static void aw8692x_misc_para_init(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val[8] = {0}; + + /* Set I2C broadcast addr */ + reg_val[0] = (uint8_t)aw_haptic->i2c->addr; + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_SYSCTRL5, reg_val, + AW_I2C_BYTE_ONE); + /* Cont drv2 lvl */ + aw_haptic->info.cont_drv2_lvl = AW8692X_DRV2_LVL_FARMULA( + aw_haptic->info.cont_lra_vrms); + if (aw_haptic->info.cont_drv2_lvl > AW_DRV2_LVL_MAX) + aw_haptic->info.cont_drv2_lvl = AW_DRV2_LVL_MAX; + /* Get vmax */ + if (aw_haptic->info.bst_vol_default > 0) + aw_haptic->vmax = aw_haptic->info.bst_vol_default; + /* Get gain */ + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_PLAYCFG2, reg_val, + AW_I2C_BYTE_ONE); + aw_haptic->gain = reg_val[0]; + /* Get wave_seq */ + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_WAVCFG1, reg_val, + AW_I2C_BYTE_EIGHT); + aw_haptic->index = reg_val[0]; + memcpy(aw_haptic->seq, reg_val, AW_SEQUENCER_SIZE); + /* Set gain_bypass */ + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_SYSCTRL4, + AW8692X_BIT_SYSCTRL4_GAIN_BYPASS_MASK, + aw_haptic->info.gain_bypass); + aw8692x_reg_unlock(aw_haptic, true); + reg_val[0] = AW8692X_BIT_ANACFG11_INIT_VAL; + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_ANACFG11, reg_val, + AW_I2C_BYTE_ONE); + /* Close boost skip */ + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_ANACFG12, + AW8692X_BIT_ANACFG12_BST_SKIP_MASK, + AW8692X_BIT_ANACFG12_BST_SKIP_SHUTDOWN); + /* Open adaptive ipeak current limiting */ + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_ANACFG15, + AW8692X_BIT_ANACFG15_BST_PEAK_MODE_MASK, + AW8692X_BIT_ANACFG15_BST_PEAK_BACK); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_ANACFG16, + AW8692X_BIT_ANACFG16_BST_SRC_MASK, + AW8692X_BIT_ANACFG16_BST_SRC_3NS); + reg_val[0] = AW8692X_BIT_PWMCFG1_INIT_VAL; + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_PWMCFG1, reg_val, + AW_I2C_BYTE_ONE); + aw8692x_reg_unlock(aw_haptic, false); + + if (!aw_haptic->info.brk_bst_md) + aw_err("aw_haptic->info.brk_bst_md = 0!"); + if (!aw_haptic->info.cont_brk_time) + aw_err("aw_haptic->info.cont_brk_time = 0!"); + if (!aw_haptic->info.cont_tset) + aw_err("aw_haptic->info.cont_tset = 0!"); + if (!aw_haptic->info.cont_bemf_set) + aw_err("aw_haptic->info.cont_bemf_set = 0!"); + if (!aw_haptic->info.cont_bst_brk_gain) + aw_err("aw_haptic->info.cont_bst_brk_gain = 0"); + if (!aw_haptic->info.cont_brk_gain) + aw_err("aw_haptic->info.cont_brk_gain = 0!"); + if (!aw_haptic->info.d2s_gain) + aw_err("aw_haptic->info.d2s_gain = 0!"); + + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_CONTCFG1, + AW8692X_BIT_CONTCFG1_BRK_BST_MD_MASK, + (aw_haptic->info.brk_bst_md << 6)); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_CONTCFG5, + AW8692X_BIT_CONTCFG5_BST_BRK_GAIN_MASK & + AW8692X_BIT_CONTCFG5_BRK_GAIN_MASK, + (aw_haptic->info.cont_bst_brk_gain << 4) | + aw_haptic->info.cont_brk_gain); + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_CONTCFG10, + &aw_haptic->info.cont_brk_time, AW_I2C_BYTE_ONE); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_CONTCFG13, + AW8692X_BIT_CONTCFG13_TSET_MASK & + AW8692X_BIT_CONTCFG13_BEME_SET_MASK, + (aw_haptic->info.cont_tset << 4) | + aw_haptic->info.cont_bemf_set); + haptic_hv_i2c_write_bits(aw_haptic, AW8692X_REG_DETCFG2, + AW8692X_BIT_DETCFG2_D2S_GAIN_MASK, + aw_haptic->info.d2s_gain); +} + +/****************************************************** + * + * Extern function : sysfs attr + * + ******************************************************/ +static ssize_t aw8692x_cont_wait_num_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_wait_num = 0x%02X\n", + aw_haptic->info.cont_wait_num); + return len; +} + +static ssize_t aw8692x_cont_wait_num_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + rc = kstrtou8(buf, 0, &aw_haptic->info.cont_wait_num); + if (rc < 0) + return rc; + mutex_lock(&aw_haptic->lock); + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_CONTCFG4, + &aw_haptic->info.cont_wait_num, AW_I2C_BYTE_ONE); + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t aw8692x_cont_drv_lvl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_lvl = 0x%02X, cont_drv2_lvl = 0x%02X\n", + aw_haptic->info.cont_drv1_lvl, + aw_haptic->info.cont_drv2_lvl); + return len; +} + +static ssize_t aw8692x_cont_drv_lvl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t databuf[2] = {0}; + uint8_t reg_array[2] = {0}; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_haptic->info.cont_drv1_lvl = databuf[0]; + aw_haptic->info.cont_drv2_lvl = databuf[1]; + mutex_lock(&aw_haptic->lock); + haptic_hv_i2c_reads(aw_haptic, AW8692X_REG_CONTCFG6, reg_array, + AW_I2C_BYTE_ONE); + reg_array[0] &= AW8692X_BIT_CONTCFG6_DRV1_LVL_MASK; + reg_array[0] |= aw_haptic->info.cont_drv1_lvl; + reg_array[1] = aw_haptic->info.cont_drv2_lvl; + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_CONTCFG6, reg_array, + AW_I2C_BYTE_TWO); + mutex_unlock(&aw_haptic->lock); + } + return count; +} + +static ssize_t aw8692x_cont_drv_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_time = 0x%02X, cont_drv2_time = 0x%02X\n", + aw_haptic->info.cont_drv1_time, + aw_haptic->info.cont_drv2_time); + return len; +} + +static ssize_t aw8692x_cont_drv_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t reg_array[2] = {0}; + uint32_t databuf[2] = {0}; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_haptic->info.cont_drv1_time = databuf[0]; + aw_haptic->info.cont_drv2_time = databuf[1]; + reg_array[0] = (uint8_t)aw_haptic->info.cont_drv1_time; + reg_array[1] = (uint8_t)aw_haptic->info.cont_drv2_time; + mutex_lock(&aw_haptic->lock); + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_CONTCFG8, reg_array, + AW_I2C_BYTE_TWO); + mutex_unlock(&aw_haptic->lock); + } + return count; +} + +static ssize_t aw8692x_cont_brk_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "cont_brk_time = 0x%02X\n", + aw_haptic->info.cont_brk_time); + return len; +} + +static ssize_t aw8692x_cont_brk_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + rc = kstrtou8(buf, 0, &aw_haptic->info.cont_brk_time); + if (rc < 0) + return rc; + mutex_lock(&aw_haptic->lock); + haptic_hv_i2c_writes(aw_haptic, AW8692X_REG_CONTCFG10, + &aw_haptic->info.cont_brk_time, AW_I2C_BYTE_ONE); + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t aw8692x_trig_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint8_t i = 0; + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + for (i = 0; i < AW_TRIG_NUM; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "trig%d: trig_level=%d, trig_polar=%d, pos_enable=%d, pos_sequence=%d, neg_enable=%d, neg_sequence=%d trig_brk=%d, trig_bst=%d\n", + i + 1, + aw_haptic->trig[i].trig_level, + aw_haptic->trig[i].trig_polar, + aw_haptic->trig[i].pos_enable, + aw_haptic->trig[i].pos_sequence, + aw_haptic->trig[i].neg_enable, + aw_haptic->trig[i].neg_sequence, + aw_haptic->trig[i].trig_brk, + aw_haptic->trig[i].trig_bst); + } + + return len; +} + +static ssize_t aw8692x_trig_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t databuf[9] = { 0 }; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (sscanf(buf, "%d %d %d %d %d %d %d %d %d", &databuf[0], &databuf[1], + &databuf[2], &databuf[3], &databuf[4], &databuf[5], + &databuf[6], &databuf[7], &databuf[8]) == 9) { + aw_info("%d, %d, %d, %d, %d, %d, %d, %d, %d", + databuf[0], databuf[1], databuf[2], databuf[3], + databuf[4], databuf[5], databuf[6], databuf[7], + databuf[8]); + if (databuf[0] < 1 || databuf[0] > 3) { + aw_info("input trig_num out of range!"); + return count; + } + if (databuf[0] == 1 && aw_haptic->info.is_enabled_one_wire) { + aw_info("trig1 pin used for one wire!"); + return count; + } + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + if (databuf[4] > aw_haptic->ram.ram_num || + databuf[6] > aw_haptic->ram.ram_num) { + aw_err("input seq value out of range!"); + return count; + } + databuf[0] -= 1; + + aw_haptic->trig[databuf[0]].trig_level = databuf[1]; + aw_haptic->trig[databuf[0]].trig_polar = databuf[2]; + aw_haptic->trig[databuf[0]].pos_enable = databuf[3]; + aw_haptic->trig[databuf[0]].pos_sequence = databuf[4]; + aw_haptic->trig[databuf[0]].neg_enable = databuf[5]; + aw_haptic->trig[databuf[0]].neg_sequence = databuf[6]; + aw_haptic->trig[databuf[0]].trig_brk = databuf[7]; + aw_haptic->trig[databuf[0]].trig_bst = databuf[8]; + mutex_lock(&aw_haptic->lock); + switch (databuf[0]) { + case AW8692X_TRIG1: + aw8692x_trig1_param_config(aw_haptic); + break; + case AW8692X_TRIG2: + aw8692x_trig2_param_config(aw_haptic); + break; + case AW8692X_TRIG3: + aw8692x_trig3_param_config(aw_haptic); + break; + } + mutex_unlock(&aw_haptic->lock); + } + return count; +} + +static DEVICE_ATTR(cont_wait_num, S_IWUSR | S_IRUGO, aw8692x_cont_wait_num_show, + aw8692x_cont_wait_num_store); +static DEVICE_ATTR(cont_drv_lvl, S_IWUSR | S_IRUGO, aw8692x_cont_drv_lvl_show, + aw8692x_cont_drv_lvl_store); +static DEVICE_ATTR(cont_drv_time, S_IWUSR | S_IRUGO, aw8692x_cont_drv_time_show, + aw8692x_cont_drv_time_store); +static DEVICE_ATTR(cont_brk_time, S_IWUSR | S_IRUGO, aw8692x_cont_brk_time_show, + aw8692x_cont_brk_time_store); +static DEVICE_ATTR(trig, S_IWUSR | S_IRUGO, aw8692x_trig_show, + aw8692x_trig_store); + +static struct attribute *aw8692x_vibrator_attributes[] = { + &dev_attr_cont_wait_num.attr, + &dev_attr_cont_drv_lvl.attr, + &dev_attr_cont_drv_time.attr, + &dev_attr_cont_brk_time.attr, + &dev_attr_trig.attr, + NULL +}; + +static struct attribute_group aw8692x_vibrator_attribute_group = { + .attrs = aw8692x_vibrator_attributes +}; + +static int aw8692x_creat_node(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + ret = sysfs_create_group(&aw_haptic->vib_dev.dev->kobj, + &aw8692x_vibrator_attribute_group); + if (ret < 0) { + aw_err("error create aw8692x sysfs attr files"); + return ret; + } + return 0; +} + +struct aw_haptic_func aw8692x_func_list = { + .play_stop = aw8692x_stop, + .ram_init = aw8692x_ram_init, + .get_vbat = aw8692x_get_vbat, + .creat_node = aw8692x_creat_node, + .get_f0 = aw8692x_get_f0, + .ram_get_f0 = aw8692x_ram_get_f0, + .cont_config = aw8692x_cont_config, + .offset_cali = aw8692x_offset_cali, + .get_irq_state = aw8692x_get_irq_state, + .check_qualify = aw8692x_check_qualify, + .judge_rtp_going = aw8692x_judge_rtp_going, + .set_bst_peak_cur = aw8692x_set_bst_peak_cur, + .get_theory_time = aw8692x_get_theory_time, + .get_lra_resistance = aw8692x_get_lra_resistance, + .set_pwm = aw8692x_set_pwm, + .play_mode = aw8692x_play_mode, + .set_bst_vol = aw8692x_set_bst_vol, + .interrupt_setup = aw8692x_interrupt_setup, + .set_repeat_seq = aw8692x_set_repeat_seq, + .auto_bst_enable = aw8692x_auto_bst_enable, + .vbat_mode_config = aw8692x_vbat_mode_config, + .set_wav_seq = aw8692x_set_wav_seq, + .set_wav_loop = aw8692x_set_wav_loop, + .set_ram_addr = aw8692x_set_ram_addr, + .set_rtp_data = aw8692x_set_rtp_data, + .container_update = aw8692x_container_update, + .protect_config = aw8692x_protect_config, + .parse_dt = aw8692x_parse_dt, + .trig_init = aw8692x_trig_init, + .irq_clear = aw8692x_irq_clear, + .get_wav_loop = aw8692x_get_wav_loop, + .play_go = aw8692x_play_go, + .misc_para_init = aw8692x_misc_para_init, + .set_rtp_aei = aw8692x_set_rtp_aei, + .set_gain = aw8692x_set_gain, + .upload_lra = aw8692x_upload_lra, + .bst_mode_config = aw8692x_bst_mode_config, + .get_reg = aw8692x_get_reg, + .get_prctmode = aw8692x_get_prctmode, + .get_trim_lra = aw8692x_get_trim_lra, + .get_ram_data = aw8692x_get_ram_data, + .get_first_wave_addr = aw8692x_get_first_wave_addr, + .get_glb_state = aw8692x_get_glb_state, + .get_osc_status = aw8692x_get_osc_status, + .rtp_get_fifo_afs = aw8692x_rtp_get_fifo_afs, + .rtp_get_fifo_aes = aw8692x_rtp_get_fifo_aes, + .get_wav_seq = aw8692x_get_wav_seq, +}; diff --git a/drivers/misc/awinic/haptic_hv/aw869x.c b/drivers/misc/awinic/haptic_hv/aw869x.c new file mode 100644 index 000000000000..2dfd25e13b88 --- /dev/null +++ b/drivers/misc/awinic/haptic_hv/aw869x.c @@ -0,0 +1,1734 @@ +/* + * File: aw869x.c + * + * Author: Ethan + * + * Copyright (c) 2021 AWINIC Technology CO., 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "haptic_hv_reg.h" +#include "haptic_hv.h" + +static void aw869x_vbat_mode_config(struct aw_haptic *, uint8_t); + +static int aw869x_check_qualify(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + return 0; +} + +static void aw869x_ram_init(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag == true) { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_RAMINIT_MASK, + AW869X_BIT_SYSCTRL_RAMINIT_EN); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_RAMINIT_MASK, + AW869X_BIT_SYSCTRL_RAMINIT_OFF); + } +} + +static void aw869x_play_go(struct aw_haptic *aw_haptic, bool flag) +{ + aw_info("enter, flag = %d", flag); + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_GO, + AW869X_BIT_GO_MASK, + AW869X_BIT_GO_ENABLE); + aw_haptic->kpre_enter_time = ktime_get(); + } else { + aw_haptic->kcurrent_time = ktime_get(); + aw_haptic->interval_us = ktime_to_us( + ktime_sub(aw_haptic->kcurrent_time, + aw_haptic->kpre_enter_time)); + if (aw_haptic->interval_us < 2000) { + aw_info("aw_haptic->interval_us=%d < 2000", + aw_haptic->interval_us); + usleep_range(2000, 2500); + } + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_GO, + AW869X_BIT_GO_MASK, + AW869X_BIT_GO_DISABLE); + } +} + +static uint8_t aw869x_get_glb_state(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_GLB_STATE, &state, + AW_I2C_BYTE_ONE); + aw_dbg("glb state value is 0x%02X", state); + return state; +} + +#ifdef AW_CHECK_RAM_DATA +static int aw869x_check_ram_data(struct aw_haptic *aw_haptic, + uint8_t *cont_data, + uint8_t *ram_data, uint32_t len) +{ + int i = 0; + + for (i = 0; i < len; i++) { + if (ram_data[i] != cont_data[i]) { + aw_err("check ramdata error, addr=0x%04x, ram_data=0x%02x, file_data=0x%02x", + i, ram_data[i], cont_data[i]); + return -ERANGE; + } + } + return 0; +} +#endif + +static int aw869x_container_update(struct aw_haptic *aw_haptic, + struct aw_haptic_container *awinic_cont) +{ + uint8_t fifo_adr[4] = {0}; + uint16_t ae_thr = 0; + uint16_t af_thr = 0; + uint32_t shift = 0; + int i = 0; + int len = 0; + int ret = 0; +#ifdef AW_CHECK_RAM_DATA + uint8_t ram_data[AW_RAMDATA_RD_BUFFER_SIZE] = {0}; +#endif + + mutex_lock(&aw_haptic->lock); + aw_haptic->ram.baseaddr_shift = 2; + aw_haptic->ram.ram_shift = 4; + /* RAMINIT Enable */ + aw869x_ram_init(aw_haptic, true); + /* base addr */ + shift = aw_haptic->ram.baseaddr_shift; + aw_haptic->ram.base_addr = + (uint32_t)((awinic_cont->data[0 + shift] << 8) | + (awinic_cont->data[1 + shift])); + fifo_adr[0] = (uint8_t)AW869X_FIFO_AE_ADDR_H(aw_haptic->ram.base_addr); + fifo_adr[1] = (uint8_t)AW869X_FIFO_AE_ADDR_L(aw_haptic->ram.base_addr); + fifo_adr[2] = (uint8_t)AW869X_FIFO_AF_ADDR_H(aw_haptic->ram.base_addr); + fifo_adr[3] = (uint8_t)AW869X_FIFO_AF_ADDR_L(aw_haptic->ram.base_addr); + aw_info("base_addr=0x%04x", aw_haptic->ram.base_addr); + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_BASE_ADDRH, + &awinic_cont->data[shift], AW_I2C_BYTE_TWO); + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_FIFO_AEH, fifo_adr, + AW_I2C_BYTE_FOUR); + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_FIFO_AEH, fifo_adr, + AW_I2C_BYTE_FOUR); + ae_thr = (((fifo_adr[0] & 0x0f) << 8) | fifo_adr[1]); + aw_info("almost_empty_threshold = %d", ae_thr); + af_thr = (((fifo_adr[2] & 0x0f) << 8) | fifo_adr[3]); + aw_info("almost_full_threshold = %d", af_thr); + /* ram */ + shift = aw_haptic->ram.baseaddr_shift; + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_RAMADDRH, + &awinic_cont->data[shift], AW_I2C_BYTE_TWO); + i = aw_haptic->ram.ram_shift; + while (i < awinic_cont->len) { + if ((awinic_cont->len - i) < AW_RAMDATA_WR_BUFFER_SIZE) + len = awinic_cont->len - i; + else + len = AW_RAMDATA_WR_BUFFER_SIZE; + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_RAMDATA, + &awinic_cont->data[i], len); + i += len; + } +#ifdef AW_CHECK_RAM_DATA + /* ram check */ + shift = aw_haptic->ram.baseaddr_shift; + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_RAMADDRH, + &awinic_cont->data[shift], AW_I2C_BYTE_TWO); + i = aw_haptic->ram.ram_shift; + while (i < awinic_cont->len) { + if ((awinic_cont->len - i) < AW_RAMDATA_RD_BUFFER_SIZE) + len = awinic_cont->len - i; + else + len = AW_RAMDATA_RD_BUFFER_SIZE; + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_RAMDATA, + ram_data, len); + ret = aw869x_check_ram_data(aw_haptic, &awinic_cont->data[i], + ram_data, len); + if (ret < 0) + break; + i += len; + } + if (ret) + aw_err("ram data check sum error"); + else + aw_info("ram data check sum pass"); +#endif + + /* RAMINIT Disable */ + aw869x_ram_init(aw_haptic, false); + + mutex_unlock(&aw_haptic->lock); + + return ret; +} + +/***************************************************** + * + * haptic control + * + *****************************************************/ +static void aw869x_bst_mode_config(struct aw_haptic *aw_haptic, + uint8_t boost_mode) +{ + switch (boost_mode) { + case AW_BST_BOOST_MODE: + aw_info("haptic boost mode = boost"); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_BST_MODE_MASK, + AW869X_BIT_SYSCTRL_BST_MODE_BOOST); + break; + case AW_BST_BYPASS_MODE: + aw_info("haptic boost mode = bypass"); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_BST_MODE_MASK, + AW869X_BIT_SYSCTRL_BST_MODE_BYPASS); + break; + default: + aw_err("boost_mode = %d error", boost_mode); + break; + } +} + +static void aw869x_irq_clear(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_dbg("reg SYSINT=0x%x", reg_val); +} + +static void aw869x_active(struct aw_haptic *aw_haptic) +{ + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_WORK_MODE_MASK, + AW869X_BIT_SYSCTRL_ACTIVE); + aw869x_irq_clear(aw_haptic); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSINTM, + AW869X_BIT_SYSINTM_UVLO_MASK, + AW869X_BIT_SYSINTM_UVLO_EN); +} + +static void aw869x_play_mode(struct aw_haptic *aw_haptic, uint8_t play_mode) +{ + switch (play_mode) { + case AW_STANDBY_MODE: + aw_info("enter standby mode"); + aw_haptic->play_mode = AW_STANDBY_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSINTM, + AW869X_BIT_SYSINTM_UVLO_MASK, + AW869X_BIT_SYSINTM_UVLO_OFF); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_WORK_MODE_MASK, + AW869X_BIT_SYSCTRL_STANDBY); + break; + case AW_RAM_MODE: + aw_info("enter ram mode"); + aw_haptic->play_mode = AW_RAM_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + (AW869X_BIT_SYSCTRL_PLAY_MODE_MASK & + AW869X_BIT_SYSCTRL_BST_MODE_MASK), + (AW869X_BIT_SYSCTRL_PLAY_MODE_RAM | + AW869X_BIT_SYSCTRL_BST_MODE_BYPASS)); + if (aw_haptic->auto_boost) { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_BST_AUTO, + AW869X_BIT_BST_AUTO_BST_RAM_MASK, + AW869X_BIT_BST_AUTO_BST_RAM_DISABLE); + } + aw869x_active(aw_haptic); + if (aw_haptic->auto_boost) { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_BST_AUTO, + AW869X_BIT_BST_AUTO_BST_RAM_MASK, + AW869X_BIT_BST_AUTO_BST_RAM_ENABLE); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + (AW869X_BIT_SYSCTRL_BST_MODE_MASK & + AW869X_BIT_SYSCTRL_WORK_MODE_MASK), + (AW869X_BIT_SYSCTRL_BST_MODE_BOOST | + AW869X_BIT_SYSCTRL_STANDBY)); + aw869x_active(aw_haptic); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_BST_MODE_MASK, + AW869X_BIT_SYSCTRL_BST_MODE_BOOST); + } + usleep_range(2000, 2500); + aw869x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + break; + case AW_RAM_LOOP_MODE: + aw_info("enter ram loop mode"); + aw_haptic->play_mode = AW_RAM_LOOP_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + (AW869X_BIT_SYSCTRL_PLAY_MODE_MASK & + AW869X_BIT_SYSCTRL_BST_MODE_MASK), + (AW869X_BIT_SYSCTRL_PLAY_MODE_RAM | + AW869X_BIT_SYSCTRL_BST_MODE_BYPASS)); + + if (aw_haptic->auto_boost) { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_BST_AUTO, + AW869X_BIT_BST_AUTO_BST_RAM_MASK, + AW869X_BIT_BST_AUTO_BST_RAM_DISABLE); + } + aw869x_active(aw_haptic); + aw869x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + break; + case AW_RTP_MODE: + aw_info("enter rtp mode"); + aw_haptic->play_mode = AW_RTP_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + (AW869X_BIT_SYSCTRL_PLAY_MODE_MASK & + AW869X_BIT_SYSCTRL_BST_MODE_MASK), + (AW869X_BIT_SYSCTRL_PLAY_MODE_RTP | + AW869X_BIT_SYSCTRL_BST_MODE_BYPASS)); + + if (aw_haptic->auto_boost) { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_BST_AUTO, + AW869X_BIT_BST_AUTO_BST_RTP_MASK, + AW869X_BIT_BST_AUTO_BST_RTP_DISABLE); + } + aw869x_active(aw_haptic); + if (aw_haptic->auto_boost) { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_BST_AUTO, + AW869X_BIT_BST_AUTO_BST_RTP_MASK, + AW869X_BIT_BST_AUTO_BST_RTP_ENABLE); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + (AW869X_BIT_SYSCTRL_BST_MODE_MASK & + AW869X_BIT_SYSCTRL_WORK_MODE_MASK), + (AW869X_BIT_SYSCTRL_BST_MODE_BOOST | + AW869X_BIT_SYSCTRL_STANDBY)); + aw869x_active(aw_haptic); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_BST_MODE_MASK, + AW869X_BIT_SYSCTRL_BST_MODE_BOOST); + } + usleep_range(2000, 2500); + aw869x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + break; + case AW_TRIG_MODE: + aw_info("enter trig mode"); + aw_haptic->play_mode = AW_TRIG_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + (AW869X_BIT_SYSCTRL_PLAY_MODE_MASK & + AW869X_BIT_SYSCTRL_BST_MODE_MASK), + (AW869X_BIT_SYSCTRL_PLAY_MODE_RAM | + AW869X_BIT_SYSCTRL_BST_MODE_BYPASS)); + + if (aw_haptic->auto_boost) { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_BST_AUTO, + AW869X_BIT_BST_AUTO_BST_RAM_MASK, + AW869X_BIT_BST_AUTO_BST_RAM_DISABLE); + } + aw869x_active(aw_haptic); + if (aw_haptic->auto_boost) { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_BST_AUTO, + AW869X_BIT_BST_AUTO_BST_RAM_MASK, + AW869X_BIT_BST_AUTO_BST_RAM_ENABLE); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + (AW869X_BIT_SYSCTRL_BST_MODE_MASK & + AW869X_BIT_SYSCTRL_WORK_MODE_MASK), + (AW869X_BIT_SYSCTRL_BST_MODE_BOOST | + AW869X_BIT_SYSCTRL_STANDBY)); + aw869x_active(aw_haptic); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_BST_MODE_MASK, + AW869X_BIT_SYSCTRL_BST_MODE_BOOST); + } + usleep_range(2000, 2500); + aw869x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + break; + case AW_CONT_MODE: + aw_info("enter cont mode"); + aw_haptic->play_mode = AW_CONT_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + (AW869X_BIT_SYSCTRL_PLAY_MODE_MASK & + AW869X_BIT_SYSCTRL_BST_MODE_MASK), + (AW869X_BIT_SYSCTRL_PLAY_MODE_CONT | + AW869X_BIT_SYSCTRL_BST_MODE_BYPASS)); + if (aw_haptic->auto_boost) { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_BST_AUTO, + AW869X_BIT_BST_AUTO_BST_RAM_MASK, + AW869X_BIT_BST_AUTO_BST_RAM_DISABLE); + } + aw869x_active(aw_haptic); + aw869x_vbat_mode_config(aw_haptic, AW_CONT_VBAT_HW_COMP_MODE); + break; + default: + aw_err("play mode %d err", play_mode); + break; + } +} + +static int aw869x_wait_enter_standby(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int count = 100; + + while (count--) { + reg_val = aw869x_get_glb_state(aw_haptic); + if ((reg_val & 0x0f) == AW869X_BIT_GLB_STATE_STANDBY) { + aw_info("entered standby!"); + return 0; + } + aw_dbg("wait for standby"); + usleep_range(2000, 2500); + } + aw_err("do not enter standby automatically"); + return -ERANGE; +} + +static void aw869x_stop(struct aw_haptic *aw_haptic) +{ + aw_haptic->play_mode = AW_STANDBY_MODE; + aw869x_play_go(aw_haptic, false); + aw869x_wait_enter_standby(aw_haptic); + aw869x_play_mode(aw_haptic, AW_STANDBY_MODE); +} + +static int aw869x_judge_rtp_going(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint8_t rtp_state = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_SYSCTRL, ®_val, + AW_I2C_BYTE_ONE); + if ((reg_val & AW869X_BIT_SYSCTRL_PLAY_MODE_RTP) && + (!(reg_val & AW869X_BIT_SYSCTRL_STANDBY))) { + rtp_state = 1; /*is going on */ + aw_info("rtp_routine_on"); + } + return rtp_state; +} + +static void aw869x_set_wav_seq(struct aw_haptic *aw_haptic, uint8_t wav, + uint8_t seq) +{ + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_WAVSEQ1 + wav, &seq, + AW_I2C_BYTE_ONE); +} + +static void aw869x_get_wav_seq(struct aw_haptic *aw_haptic, uint32_t len) +{ + uint8_t i = 0; + uint8_t reg_val[AW_SEQUENCER_SIZE] = {0}; + + if (len > AW_SEQUENCER_SIZE) + len = AW_SEQUENCER_SIZE; + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_WAVSEQ1, reg_val, len); + for (i = 0; i < len; i++) + aw_haptic->seq[i] = reg_val[i]; +} + +static void aw869x_get_ram_data(struct aw_haptic *aw_haptic, char *buf) +{ + int i = 0; + int size = 0; + + while (i < aw_haptic->ram.len) { + if ((aw_haptic->ram.len - i) < AW_RAMDATA_RD_BUFFER_SIZE) + size = aw_haptic->ram.len - i; + else + size = AW_RAMDATA_RD_BUFFER_SIZE; + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_RAMDATA, buf + i, + size); + i += size; + } +} + +static void aw869x_get_first_wave_addr(struct aw_haptic *aw_haptic, + uint8_t *wave_addr) +{ + uint8_t reg_array[3] = {0}; + + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_RAMDATA, reg_array, + AW_I2C_BYTE_THREE); + wave_addr[0] = reg_array[1]; + wave_addr[1] = reg_array[2]; +} + +static void aw869x_set_ram_addr(struct aw_haptic *aw_haptic) +{ + uint8_t ram_addr[2] = {0}; + + ram_addr[0] = (uint8_t)AW869X_RAM_ADDR_H(aw_haptic->ram.base_addr); + ram_addr[1] = (uint8_t)AW869X_RAM_ADDR_L(aw_haptic->ram.base_addr); + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_RAMADDRH, ram_addr, + AW_I2C_BYTE_TWO); +} + +static void aw869x_set_wav_loop(struct aw_haptic *aw_haptic, uint8_t wav, + uint8_t loop) +{ + uint8_t tmp = 0; + + if (wav % 2) { + tmp = loop << 0; + haptic_hv_i2c_write_bits(aw_haptic, + AW869X_REG_WAVLOOP1 + (wav / 2), + AW869X_BIT_WAVLOOP_SEQNP1_MASK, tmp); + } else { + tmp = loop << 4; + haptic_hv_i2c_write_bits(aw_haptic, + AW869X_REG_WAVLOOP1 + (wav / 2), + AW869X_BIT_WAVLOOP_SEQN_MASK, tmp); + } +} + +static size_t aw869x_get_wav_loop(struct aw_haptic *aw_haptic, char *buf) +{ + uint8_t i = 0; + uint8_t reg_val[4] = {0}; + size_t count = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_WAVLOOP1, reg_val, + AW_I2C_BYTE_FOUR); + for (i = 0; i < AW_SEQUENCER_LOOP_SIZE; i++) { + aw_haptic->loop[i * 2 + 0] = (reg_val[i] >> 4) & 0x0F; + aw_haptic->loop[i * 2 + 1] = (reg_val[i] >> 0) & 0x0F; + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d loop: 0x%02x\n", i * 2 + 1, + aw_haptic->loop[i * 2 + 0]); + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d loop: 0x%02x\n", i * 2 + 2, + aw_haptic->loop[i * 2 + 1]); + } + return count; +} + +static uint8_t aw869x_get_prctmode(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_DETCTRL, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= 0x20; + return reg_val; +} + +static void aw869x_set_repeat_seq(struct aw_haptic *aw_haptic, uint8_t seq) +{ + aw869x_set_wav_seq(aw_haptic, 0x00, seq); + aw869x_set_wav_loop(aw_haptic, 0x00, AW869X_BIT_WAVLOOP_INIFINITELY); +} + +static void aw869x_set_rtp_data(struct aw_haptic *aw_haptic, uint8_t *data, + uint32_t len) +{ + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_RTP_DATA, data, len); +} + +static void aw869x_set_bst_vol(struct aw_haptic *aw_haptic, uint32_t bst_vol) +{ + uint8_t reg_val = 0; + + if (bst_vol < AW869X_BST_VOL_MIN) + bst_vol = AW869X_BST_VOL_MIN; + else if (bst_vol > AW869X_BST_VOL_MAX) + bst_vol = AW869X_BST_VOL_MAX; + reg_val = AW869X_BST_VOL_FARMULA(bst_vol); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_BSTDBG4, + AW869X_BIT_BSTDBG4_BSTVOL_MASK, reg_val); +} + +static void aw869x_set_bst_peak_cur(struct aw_haptic *aw_haptic) +{ + switch (aw_haptic->bst_pc) { + case AW_BST_PC_L1: + aw_info("bst pc = L1"); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_BSTCFG, + AW869X_BIT_BSTCFG_PEAKCUR_MASK, + AW869X_BIT_BSTCFG_PEAKCUR_2A); + break; + case AW_BST_PC_L2: + aw_info("bst pc = L2"); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_BSTCFG, + AW869X_BIT_BSTCFG_PEAKCUR_MASK, + AW869X_BIT_BSTCFG_PEAKCUR_3P5A); + break; + default: + aw_info("bst pc = L1"); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_BSTCFG, + AW869X_BIT_BSTCFG_PEAKCUR_MASK, + AW869X_BIT_BSTCFG_PEAKCUR_2A); + break; + } +} + +static void aw869x_set_gain(struct aw_haptic *aw_haptic, uint8_t gain) +{ + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_DATDBG, &gain, + AW_I2C_BYTE_ONE); +} + +static void aw869x_set_pwm(struct aw_haptic *aw_haptic, uint8_t mode) +{ + switch (mode) { + case AW_PWM_48K: + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_PWMDBG, + AW869X_BIT_PWMDBG_PWM_MODE_MASK, + AW869X_BIT_PWMDBG_PWM_48K); + break; + case AW_PWM_24K: + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_PWMDBG, + AW869X_BIT_PWMDBG_PWM_MODE_MASK, + AW869X_BIT_PWMDBG_PWM_24K); + break; + case AW_PWM_12K: + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_PWMDBG, + AW869X_BIT_PWMDBG_PWM_MODE_MASK, + AW869X_BIT_PWMDBG_PWM_12K); + break; + default: + break; + } +} + +/***************************************************** + * + * motor protect + * + *****************************************************/ +static void aw869x_protect_config(struct aw_haptic *aw_haptic, uint8_t addr, + uint8_t val) +{ + aw_dbg("enter"); + if (addr == 1) { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_DETCTRL, + AW869X_BIT_DETCTRL_PROTECT_MASK, + AW869X_BIT_DETCTRL_PROTECT_SHUTDOWN); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_PWMPRC, + AW869X_BIT_PWMPRC_PRC_MASK, + AW869X_BIT_PWMPRC_PRC_ENABLE); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_PRLVL, + AW869X_BIT_PRLVL_PR_MASK, + AW869X_BIT_PRLVL_PR_ENABLE); + } else if (addr == 0) { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_DETCTRL, + AW869X_BIT_DETCTRL_PROTECT_MASK, + AW869X_BIT_DETCTRL_PROTECT_NO_ACTION); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_PWMPRC, + AW869X_BIT_PWMPRC_PRC_MASK, + AW869X_BIT_PWMPRC_PRC_DISABLE); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_PRLVL, + AW869X_BIT_PRLVL_PR_MASK, + AW869X_BIT_PRLVL_PR_DISABLE); + } else if (addr == AW869X_REG_PWMPRC) { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_PWMPRC, + AW869X_BIT_PWMPRC_PRCTIME_MASK, val); + } else if (addr == AW869X_REG_PRLVL) { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_PRLVL, + AW869X_BIT_PRLVL_PRLVL_MASK, val); + } else if (addr == AW869X_REG_PRTIME) { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_PRTIME, + AW869X_BIT_PRTIME_PRTIME_MASK, val); + } +} + +/***************************************************** + * + * offset calibration + * + *****************************************************/ +static void aw869x_offset_cali(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t cnt = 2000; + + aw869x_ram_init(aw_haptic, true); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_DETCTRL, + AW869X_BIT_DETCTRL_DIAG_GO_MASK, + AW869X_BIT_DETCTRL_DIAG_GO_ENABLE); + while (cnt--) { + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_DETCTRL, ®_val, + AW_I2C_BYTE_ONE); + if ((reg_val & 0x01) == 0) { + aw869x_ram_init(aw_haptic, false); + return; + } + } + aw_err("calibration offset failed!"); + aw869x_ram_init(aw_haptic, false); +} + +/***************************************************** + * + * trig config + * + *****************************************************/ +static void aw869x_trig_param_init(struct aw_haptic *aw_haptic) +{ + aw_haptic->trig[0].enable = aw_haptic->info.trig_cfg[0]; + aw_haptic->trig[0].default_level = aw_haptic->info.trig_cfg[1]; + aw_haptic->trig[0].dual_edge = aw_haptic->info.trig_cfg[2]; + aw_haptic->trig[0].frist_seq = aw_haptic->info.trig_cfg[3]; + aw_haptic->trig[0].second_seq = aw_haptic->info.trig_cfg[4]; + aw_haptic->trig[1].enable = aw_haptic->info.trig_cfg[5]; + aw_haptic->trig[1].default_level = aw_haptic->info.trig_cfg[6]; + aw_haptic->trig[1].dual_edge = aw_haptic->info.trig_cfg[7]; + aw_haptic->trig[1].frist_seq = aw_haptic->info.trig_cfg[8]; + aw_haptic->trig[1].second_seq = aw_haptic->info.trig_cfg[9]; + aw_haptic->trig[2].enable = aw_haptic->info.trig_cfg[10]; + aw_haptic->trig[2].default_level = aw_haptic->info.trig_cfg[11]; + aw_haptic->trig[2].dual_edge = aw_haptic->info.trig_cfg[12]; + aw_haptic->trig[2].frist_seq = aw_haptic->info.trig_cfg[13]; + aw_haptic->trig[2].second_seq = aw_haptic->info.trig_cfg[14]; +} + +static void aw869x_trig_param_config(struct aw_haptic *aw_haptic) +{ + uint8_t trig_config = 0; + uint8_t trig_seq[6] = {0}; + + if (aw_haptic->trig[0].default_level) + trig_config |= AW869X_BIT_TRGCFG1_TRG1_POLAR_NEG; + else + trig_config |= AW869X_BIT_TRGCFG1_TRG1_POLAR_POS; + if (aw_haptic->trig[1].default_level) + trig_config |= AW869X_BIT_TRGCFG1_TRG2_POLAR_NEG; + else + trig_config |= AW869X_BIT_TRGCFG1_TRG2_POLAR_POS; + if (aw_haptic->trig[2].default_level) + trig_config |= AW869X_BIT_TRGCFG1_TRG3_POLAR_NEG; + else + trig_config |= AW869X_BIT_TRGCFG1_TRG3_POLAR_POS; + if (aw_haptic->trig[0].dual_edge) + trig_config |= AW869X_BIT_TRGCFG1_TRG1_EDGE_POS_NEG; + else + trig_config |= AW869X_BIT_TRGCFG1_TRG1_EDGE_POS; + if (aw_haptic->trig[1].dual_edge) + trig_config |= AW869X_BIT_TRGCFG1_TRG2_EDGE_POS_NEG; + else + trig_config |= AW869X_BIT_TRGCFG1_TRG2_EDGE_POS; + if (aw_haptic->trig[2].dual_edge) + trig_config |= AW869X_BIT_TRGCFG1_TRG3_EDGE_POS_NEG; + else + trig_config |= AW869X_BIT_TRGCFG1_TRG3_EDGE_POS; + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_TRG_CFG1, + (AW869X_BIT_TRGCFG1_TRG1_POLAR_MASK & + AW869X_BIT_TRGCFG1_TRG2_POLAR_MASK & + AW869X_BIT_TRGCFG1_TRG3_POLAR_MASK & + AW869X_BIT_TRGCFG1_TRG1_EDGE_MASK & + AW869X_BIT_TRGCFG1_TRG2_EDGE_MASK & + AW869X_BIT_TRGCFG1_TRG3_EDGE_MASK), + trig_config); + + trig_seq[0] = aw_haptic->trig[0].frist_seq; + trig_seq[1] = aw_haptic->trig[1].frist_seq; + trig_seq[2] = aw_haptic->trig[2].frist_seq; + trig_seq[3] = aw_haptic->trig[0].second_seq; + trig_seq[4] = aw_haptic->trig[1].second_seq; + trig_seq[5] = aw_haptic->trig[2].second_seq; + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_TRG1_WAV_P, trig_seq, + AW_I2C_BYTE_SIX); +} + +static void aw869x_trig_enable_config(struct aw_haptic *aw_haptic) +{ + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_TRG_CFG2, + (AW869X_BIT_TRGCFG2_TRG1_ENABLE_MASK & + AW869X_BIT_TRGCFG2_TRG2_ENABLE_MASK & + AW869X_BIT_TRGCFG2_TRG3_ENABLE_MASK), + ((aw_haptic->trig[0].enable << 0) | + (aw_haptic->trig[1].enable << 1) | + (aw_haptic->trig[2].enable << 2))); +} + +static void aw869x_auto_bst_enable(struct aw_haptic *aw_haptic, uint8_t flag) +{ + aw_haptic->auto_boost = flag; + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_BST_AUTO, + AW869X_BIT_BST_AUTO_BST_AUTOSW_MASK, + AW869X_BIT_BST_AUTO_BST_AUTOMATIC_BOOST); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_BST_AUTO, + AW869X_BIT_BST_AUTO_BST_AUTOSW_MASK, + AW869X_BIT_BST_AUTO_BST_MANUAL_BOOST); + } +} + +static uint8_t aw869x_get_trim_lra(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_TRIM_LRA, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= 0x3F; + return reg_val; +} + +/***************************************************** + * + * vbat mode + * + *****************************************************/ +static void aw869x_vbat_mode_config(struct aw_haptic *aw_haptic, uint8_t flag) +{ + if (flag == AW_CONT_VBAT_HW_COMP_MODE) { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_ADCTEST, + AW869X_BIT_ADCTEST_VBAT_MODE_MASK, + AW869X_BIT_ADCTEST_VBAT_HW_COMP); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_ADCTEST, + AW869X_BIT_ADCTEST_VBAT_MODE_MASK, + AW869X_BIT_ADCTEST_VBAT_SW_COMP); + } +} + +static void aw869x_get_vbat(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t cont = 2000; + + aw869x_stop(aw_haptic); + aw869x_ram_init(aw_haptic, true); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_DETCTRL, + AW869X_BIT_DETCTRL_VBAT_GO_MASK, + AW869X_BIT_DETCTRL_VABT_GO_ENABLE); + + while (1) { + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_DETCTRL, ®_val, + AW_I2C_BYTE_ONE); + if ((reg_val & 0x02) == 0 || cont == 0) + break; + cont--; + } + + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_VBATDET, ®_val, + AW_I2C_BYTE_ONE); + aw_haptic->vbat = AW869X_VBAT_FORMULA(reg_val); + if (aw_haptic->vbat > AW_VBAT_MAX) { + aw_haptic->vbat = AW_VBAT_MAX; + aw_dbg("vbat max limit = %d", aw_haptic->vbat); + } + if (aw_haptic->vbat < AW_VBAT_MIN) { + aw_haptic->vbat = AW_VBAT_MIN; + aw_dbg("vbat min limit = %d", aw_haptic->vbat); + } + aw_info("aw_haptic->vbat=%dmV", aw_haptic->vbat); + aw869x_ram_init(aw_haptic, false); +} + +static ssize_t aw869x_get_reg(struct aw_haptic *aw_haptic, ssize_t len, + char *buf) +{ + uint8_t i = 0; + uint8_t reg_array[AW869X_REG_NUM_F0_3 + 1] = {0}; + + for (i = 0; i < AW869X_REG_RTP_DATA; i++) + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_ID, reg_array, + AW869X_REG_RTP_DATA); + for (i = AW869X_REG_RTP_DATA + 1; i < AW869X_REG_RAMDATA; i++) + haptic_hv_i2c_reads(aw_haptic, (AW869X_REG_RTP_DATA + 1), + ®_array[AW869X_REG_RTP_DATA + 1], + (AW869X_REG_RAMDATA - AW869X_REG_RTP_DATA - 1)); + for (i = AW869X_REG_RAMDATA + 1; i <= AW869X_REG_NUM_F0_3; i++) + haptic_hv_i2c_reads(aw_haptic, (AW869X_REG_RAMDATA + 1), + ®_array[AW869X_REG_RAMDATA + 1], + (AW869X_REG_NUM_F0_3 - AW869X_REG_RAMDATA - 1)); + for (i = 0; i <= AW869X_REG_NUM_F0_3; i++) + if ((i != AW869X_REG_RTP_DATA) && (i != AW869X_REG_RAMDATA)) + len += snprintf(buf + len, PAGE_SIZE - len, + "reg:0x%02X=0x%02X\n", i, reg_array[i]); + return len; +} + +static void aw869x_get_lra_resistance(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + aw869x_stop(aw_haptic); + aw869x_ram_init(aw_haptic, true); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_BST_MODE_MASK, + AW869X_BIT_SYSCTRL_BST_MODE_BYPASS); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_ANACTRL, + AW869X_BIT_ANACTRL_HD_PD_MASK, + AW869X_BIT_ANACTRL_HD_HZ_EN); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_D2SCFG, + AW869X_BIT_D2SCFG_CLK_ADC_MASK, + AW869X_BIT_D2SCFG_CLK_ASC_1P5MHZ); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_DETCTRL, + (AW869X_BIT_DETCTRL_RL_OS_MASK & + AW869X_BIT_DETCTRL_DIAG_GO_MASK), + (AW869X_BIT_DETCTRL_RL_DETECT | + AW869X_BIT_DETCTRL_DIAG_GO_ENABLE)); + usleep_range(3000, 3500); + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_RLDET, ®_val, + AW_I2C_BYTE_ONE); + aw_haptic->lra = AW869X_LRA_FORMULA(reg_val); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_ANACTRL, + AW869X_BIT_ANACTRL_HD_PD_MASK, + AW869X_BIT_ANACTRL_HD_PD_EN); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_D2SCFG, + AW869X_BIT_D2SCFG_CLK_ADC_MASK, + AW869X_BIT_D2SCFG_CLK_ASC_6MHZ); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSCTRL, + AW869X_BIT_SYSCTRL_BST_MODE_MASK, + AW869X_BIT_SYSCTRL_BST_MODE_BOOST); + aw869x_ram_init(aw_haptic, false); +} + +/***************************************************** + * + * f0 + * + *****************************************************/ +static int aw869x_read_f0(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val[2] = {0}; + uint32_t f0_reg = 0; + +#ifdef AW_LRA_F0_DEFAULT + /* lra_f0 */ + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_F_LRA_F0_H, reg_val, + AW_I2C_BYTE_TWO); + f0_reg = (reg_val[0] << 8) | (reg_val[1] << 0); + if (!(f0_reg * aw_haptic->info.f0_coeff)) { + aw_haptic->f0 = 0; + aw_err("lra_f0 is error, f0_reg=0"); + return -ERANGE; + } + aw_haptic->f0 = (uint32_t)AW869X_F0_FARMULA(f0_reg, + aw_haptic->info.f0_coeff); + aw_info("lra_f0=%d", aw_haptic->f0); +#else + /* cont_f0 */ + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_F_LRA_CONT_H, reg_val, + AW_I2C_BYTE_TWO); + f0_reg = (reg_val[0] << 8) | (reg_val[1] << 0); + if (!(f0_reg * aw_haptic->info.f0_coeff)) { + aw_haptic->f0 = 0; + aw_err("cont_f0 is error, f0_reg=0"); + return -ERANGE; + } + aw_haptic->f0 = (uint32_t)AW869X_F0_FARMULA(f0_reg, + aw_haptic->info.f0_coeff); + aw_info("cont_f0=%d", aw_haptic->f0); +#endif + return 0; +} + +static void aw869x_read_beme(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int ret = 0; + + ret = haptic_hv_i2c_reads(aw_haptic, AW869X_REG_WAIT_VOL_MP, ®_val, + AW_I2C_BYTE_ONE); + aw_haptic->max_pos_beme = (reg_val << 0); + ret = haptic_hv_i2c_reads(aw_haptic, AW869X_REG_WAIT_VOL_MN, ®_val, + AW_I2C_BYTE_ONE); + aw_haptic->max_neg_beme = (reg_val << 0); + aw_info("max_pos_beme=%d", aw_haptic->max_pos_beme); + aw_info("max_neg_beme=%d", aw_haptic->max_neg_beme); +} + +/***************************************************** + * + * rtp + * + *****************************************************/ +static void aw869x_set_rtp_aei(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSINTM, + AW869X_BIT_SYSINTM_FF_AE_MASK, + AW869X_BIT_SYSINTM_FF_AE_EN); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSINTM, + AW869X_BIT_SYSINTM_FF_AE_MASK, + AW869X_BIT_SYSINTM_FF_AE_OFF); + } +} + +static uint8_t aw869x_rtp_get_fifo_afs(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_SYSST, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= AW869X_BIT_SYSST_FF_AFS; + reg_val >>= 3; + return reg_val; +} + +static uint8_t aw869x_rtp_get_fifo_aes(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_SYSST, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= AW869X_BIT_SYSST_FF_AES; + reg_val >>= 4; + return reg_val; +} + +static void aw869x_upload_lra(struct aw_haptic *aw_haptic, uint32_t flag) +{ + uint8_t zero = 0x00; + uint8_t f0 = 0; + uint8_t osc = 0; + + switch (flag) { + case AW_WRITE_ZERO: + aw_info("write zero to trim_lra!"); + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_TRIM_LRA, &zero, + AW_I2C_BYTE_ONE); + break; + case AW_F0_CALI_LRA: + aw_info("f0_cali_lra=%d", aw_haptic->f0_cali_data); + f0 = (uint8_t)aw_haptic->f0_cali_data; + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_TRIM_LRA, &f0, + AW_I2C_BYTE_ONE); + break; + case AW_OSC_CALI_LRA: + aw_info("rtp_cali_lra=%d", aw_haptic->osc_cali_data); + osc = (uint8_t)aw_haptic->osc_cali_data; + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_TRIM_LRA, &osc, + AW_I2C_BYTE_ONE); + break; + default: + break; + } +} + +static uint64_t aw869x_get_theory_time(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t fre_val = 0; + uint64_t theory_time = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_PWMDBG, ®_val, + AW_I2C_BYTE_ONE); + fre_val = (reg_val & 0x006f) >> 5; + if (fre_val == 3) + theory_time = (aw_haptic->rtp_len / 12000) * 1000000; /*12K */ + if (fre_val == 2) + theory_time = (aw_haptic->rtp_len / 24000) * 1000000; /*24K */ + if (fre_val == 1 || fre_val == 0) + theory_time = (aw_haptic->rtp_len / 48000) * 1000000; /*48K */ + + aw_info("microsecond:%llu theory_time = %llu", + aw_haptic->microsecond, theory_time); + + return theory_time; +} + +static uint8_t aw869x_osc_read_status(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_DBGSTAT, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= AW869X_BIT_DBGSTAT_FF_EMPTY; + return reg_val; +} + +/***************************************************** + * + * haptic cont + * + *****************************************************/ +static void aw869x_cont_config(struct aw_haptic *aw_haptic) +{ + uint8_t time_nzc = 0x23; + uint8_t cont_td[2] = {0}; + uint8_t cont_zc[2] = {0}; + uint8_t drv_lvl[2] = {0}; + + /* work mode */ + aw869x_play_mode(aw_haptic, AW_CONT_MODE); + /* lpf */ + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_DATCTRL, + (AW869X_BIT_DATCTRL_FC_MASK & + AW869X_BIT_DATCTRL_LPF_ENABLE_MASK), + (AW869X_BIT_DATCTRL_FC_1000HZ | + AW869X_BIT_DATCTRL_LPF_ENABLE)); + /* cont config */ + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_CONT_CTRL, + (AW869X_BIT_CONT_CTRL_ZC_DETEC_MASK & + AW869X_BIT_CONT_CTRL_WAIT_PERIOD_MASK & + AW869X_BIT_CONT_CTRL_MODE_MASK & + AW869X_BIT_CONT_CTRL_EN_CLOSE_MASK & + AW869X_BIT_CONT_CTRL_F0_DETECT_MASK & + AW869X_BIT_CONT_CTRL_O2C_MASK & + AW869X_BIT_CONT_CTRL_AUTO_BRK_MASK), + (AW869X_BIT_CONT_CTRL_ZC_DETEC_ENABLE | + AW869X_BIT_CONT_CTRL_WAIT_1PERIOD | + AW869X_BIT_CONT_CTRL_BY_GO_SIGNAL | + AW869X_BIT_CONT_CTRL_CLOSE_PLAYBACK | + AW869X_BIT_CONT_CTRL_F0_DETECT_DISABLE | + AW869X_BIT_CONT_CTRL_O2C_DISABLE | + AW869X_BIT_CONT_CTRL_AUTO_BRK_ENABLE)); + /* TD time */ + cont_td[0] = (uint8_t)(aw_haptic->info.cont_td >> 8); /* cont_td_h */ + cont_td[1] = (uint8_t)(aw_haptic->info.cont_td >> 0); /* cont_td_l */ + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_TD_H, cont_td, + AW_I2C_BYTE_TWO); + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_TSET, &aw_haptic->info.tset, + AW_I2C_BYTE_ONE); + /* zero cross */ + cont_zc[0] = (uint8_t)(aw_haptic->info.cont_zc_thr >> 8);/* cont_zc_h */ + cont_zc[1] = (uint8_t)(aw_haptic->info.cont_zc_thr >> 0);/* cont_zc_l */ + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_ZC_THRSH_H, cont_zc, + AW_I2C_BYTE_TWO); + + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_BEMF_NUM, + AW869X_BIT_BEMF_NUM_BRK_MASK, + aw_haptic->info.cont_num_brk); + /* 35*171us=5.985ms */ + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_TIME_NZC, &time_nzc, + AW_I2C_BYTE_ONE); + /* f0 driver level */ + drv_lvl[0] = aw_haptic->info.cont_drv_lvl; + drv_lvl[1] = aw_haptic->info.cont_drv_lvl_ov; + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_DRV_LVL, drv_lvl, + AW_I2C_BYTE_TWO); + /* cont play go */ + aw869x_play_go(aw_haptic, true); +} + +/***************************************************** + * + * haptic f0 cali + * + *****************************************************/ +static int aw869x_get_f0(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint8_t f0_param[5] = {0}; + uint32_t t_f0_ms = 0; + uint32_t t_f0_trace_ms = 0; + int ret = 0; + + aw_haptic->f0 = aw_haptic->info.f0_pre; + /* f0 calibrate work mode */ + aw869x_stop(aw_haptic); + aw869x_play_mode(aw_haptic, AW_CONT_MODE); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_CONT_CTRL, + (AW869X_BIT_CONT_CTRL_EN_CLOSE_MASK & + AW869X_BIT_CONT_CTRL_F0_DETECT_MASK), + (AW869X_BIT_CONT_CTRL_OPEN_PLAYBACK | + AW869X_BIT_CONT_CTRL_F0_DETECT_ENABLE)); + /* LPF */ + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_DATCTRL, + (AW869X_BIT_DATCTRL_FC_MASK & + AW869X_BIT_DATCTRL_LPF_ENABLE_MASK), + (AW869X_BIT_DATCTRL_FC_1000HZ | + AW869X_BIT_DATCTRL_LPF_ENABLE)); + /* f0 driver level */ + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_DRV_LVL, + &aw_haptic->info.cont_drv_lvl, AW_I2C_BYTE_ONE); + /* f0 trace parameter */ + f0_param[1] = aw_haptic->info.f0_trace_parameter[2]; /* f0_repeat_num */ + f0_param[2] = aw_haptic->info.f0_trace_parameter[3]; /* f0_trace_num */ + f0_param[3] = aw_haptic->info.f0_trace_parameter[0]; /* f0_pre_num */ + f0_param[4] = aw_haptic->info.f0_trace_parameter[1]; /* f0_wait_num */ + f0_param[0] = (f0_param[3] << 4) | (f0_param[4] << 0); /* f0_1 */ + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_NUM_F0_1, f0_param, + AW_I2C_BYTE_THREE); + /* clear aw_haptic interrupt */ + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + /* play go */ + aw869x_play_go(aw_haptic, true); + /* f0 trace time */ + if (!aw_haptic->pre_f0) { + aw_err("aw_haptic->pre_f0=0!!"); + return -ERANGE; + } + t_f0_ms = 1000 * 10 / aw_haptic->pre_f0; + t_f0_trace_ms = t_f0_ms * (f0_param[3] + f0_param[4] + + (f0_param[2] + f0_param[4]) * (f0_param[1] - 1)); + usleep_range(t_f0_trace_ms * 1000, t_f0_trace_ms * 1000 + 500); + aw869x_wait_enter_standby(aw_haptic); + ret = aw869x_read_f0(aw_haptic); + aw869x_read_beme(aw_haptic); + /* restore default config */ + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_CONT_CTRL, + (AW869X_BIT_CONT_CTRL_EN_CLOSE_MASK & + AW869X_BIT_CONT_CTRL_F0_DETECT_MASK), + (AW869X_BIT_CONT_CTRL_CLOSE_PLAYBACK | + AW869X_BIT_CONT_CTRL_F0_DETECT_DISABLE)); + return ret; +} + +static int aw869x_ram_get_f0(struct aw_haptic *aw_haptic) +{ + aw_err("aw869x no support ram get f0"); + return -ERANGE; +} + +static int aw869x_set_pref0(struct aw_haptic *aw_haptic, uint32_t f0_pre) +{ + uint8_t reg_val[2] = {0}; + uint32_t f0_reg = 0; + + if (!(f0_pre && aw_haptic->info.f0_coeff)) { + aw_err("Divisor is 0! f0_pre=%d, f0_coeff=%d", + f0_pre, aw_haptic->info.f0_coeff); + return -EPERM; + } + f0_reg = 1000000000 / (f0_pre * aw_haptic->info.f0_coeff); + reg_val[0] = (uint8_t)((f0_reg >> 8) & 0xff); + reg_val[1] = (uint8_t)((f0_reg >> 0) & 0xff); + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_F_PRE_H, reg_val, + AW_I2C_BYTE_TWO); + return 0; +} + +#ifdef AW869X_MUL_GET_F0 +static int aw869x_multiple_get_f0(struct aw_haptic *aw_haptic) +{ + int f0_max = aw_haptic->info.f0_pre + AW869X_MUL_GET_F0_RANGE; + int f0_min = aw_haptic->info.f0_pre - AW869X_MUL_GET_F0_RANGE; + int i = 0; + int ret = 0; + + aw_haptic->pre_f0 = aw_haptic->info.f0_pre; + for (i = 0; i < AW869X_MUL_GET_F0_NUM; i++) { + aw869x_set_pref0(aw_haptic, aw_haptic->pre_f0); + ret = aw869x_get_f0(aw_haptic); + if (ret) + return ret; + aw_haptic->pre_f0 = aw_haptic->f0; + if (aw_haptic->f0 >= f0_max || aw_haptic->f0 <= f0_min) + break; + usleep_range(4000, 4500); + } + return 0; +} +#endif + +static void aw869x_misc_para_init(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val[8] = {0}; + + /* Get vmax */ + if (aw_haptic->info.bst_vol_default > 0) + aw_haptic->vmax = aw_haptic->info.bst_vol_default; + /* Get gain */ + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_DATDBG, reg_val, + AW_I2C_BYTE_ONE); + aw_haptic->gain = reg_val[0]; + /* Get wave_seq */ + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_WAVSEQ1, reg_val, + AW_I2C_BYTE_EIGHT); + aw_haptic->index = reg_val[0]; + memcpy(aw_haptic->seq, reg_val, AW_SEQUENCER_SIZE); + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_BSTDBG1, + aw_haptic->info.bstdbg, AW_I2C_BYTE_THREE); + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_TSET, &aw_haptic->info.tset, + AW_I2C_BYTE_ONE); + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_R_SPARE, + &aw_haptic->info.r_spare, AW_I2C_BYTE_ONE); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_ANADBG, + AW869X_BIT_ANADBG_IOC_MASK, + AW869X_BIT_ANADBG_IOC_4P65A); + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_ANACTRL, + AW869X_BIT_ANACTRL_LRA_SRC_MASK, + AW869X_BIT_ANACTRL_LRA_SRC_REG); + /* beme config */ + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_BEMF_VTHH_H, + aw_haptic->info.bemf_config, AW_I2C_BYTE_FOUR); + aw_haptic->pre_f0 = aw_haptic->info.f0_pre; + aw869x_set_pref0(aw_haptic, aw_haptic->info.f0_pre); +} + +static void aw869x_trig_init(struct aw_haptic *aw_haptic) +{ + aw_info("enter!"); + aw869x_trig_param_init(aw_haptic); + aw869x_trig_param_config(aw_haptic); + aw869x_trig_enable_config(aw_haptic); +} + +static void aw869x_interrupt_setup(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_info("reg SYSINT=0x%x", reg_val); + /* edge int mode */ + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_DBGCTRL, + AW869X_BIT_DBGCTRL_INT_MODE_MASK, + AW869X_BIT_DBGCTRL_INT_MODE_EDGE); + /* int enable */ + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_SYSINTM, + (AW869X_BIT_SYSINTM_BSTERR_MASK & + AW869X_BIT_SYSINTM_OV_MASK & + AW869X_BIT_SYSINTM_UVLO_MASK & + AW869X_BIT_SYSINTM_OCD_MASK & + AW869X_BIT_SYSINTM_OT_MASK), + (AW869X_BIT_SYSINTM_BSTERR_OFF | + AW869X_BIT_SYSINTM_OV_OFF | + AW869X_BIT_SYSINTM_UVLO_EN | + AW869X_BIT_SYSINTM_OCD_EN | + AW869X_BIT_SYSINTM_OT_EN)); +} + +static int aw869x_get_irq_state(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint8_t dbg_val = 0; + uint8_t int_sts = 0; + int ret = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_SYSST, &int_sts, + AW_I2C_BYTE_ONE); + aw_dbg("reg SYSST=0x%x", int_sts); + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_dbg("reg SYSINT=0x%x", reg_val); + haptic_hv_i2c_reads(aw_haptic, AW869X_REG_DBGSTAT, &dbg_val, + AW_I2C_BYTE_ONE); + aw_dbg("reg DBGSTAT=0x%x", dbg_val); + if (reg_val & AW869X_BIT_SYSINT_OVI) { + ret = AW_IRQ_OV; + aw_err("chip ov int error"); + } + if (reg_val & AW869X_BIT_SYSINT_UVLI & int_sts) { + ret = AW_IRQ_UVLO; + aw_err("chip uvlo int error"); + } + if (reg_val & AW869X_BIT_SYSINT_OCDI) { + ret = AW_IRQ_OCD; + aw_err("chip over current int error"); + } + if (reg_val & AW869X_BIT_SYSINT_OTI) { + ret = AW_IRQ_OT; + aw_err("chip over temperature int error"); + } + if (reg_val & AW869X_BIT_SYSINT_DONEI) { + ret = AW_IRQ_DONE; + aw_info("chip playback done"); + } + if (reg_val & AW869X_BIT_SYSINT_FF_AFI) { + ret = AW_IRQ_ALMOST_FULL; + aw_info("aw_haptic rtp mode fifo full empty"); + } + if (reg_val & AW869X_BIT_SYSINT_FF_AEI) + ret = AW_IRQ_ALMOST_EMPTY; + return ret; +} + +/***************************************************** + * + * device tree + * + *****************************************************/ +static void aw869x_parse_dt(struct device *dev, struct aw_haptic *aw_haptic, + struct device_node *np) +{ + uint8_t bstdbg[6]; + uint8_t f0_trace_parameter[4]; + uint8_t bemf_config[4]; + uint8_t duration_time[3]; + uint8_t trig_config_temp[15]; + uint32_t val = 0; + + val = of_property_read_u32(np, "f0_pre", &aw_haptic->info.f0_pre); + if (val != 0) + aw_info("f0_pre not found"); + val = of_property_read_u8(np, "f0_cali_percent", + &aw_haptic->info.f0_cali_percent); + if (val != 0) + aw_info("f0_cali_percent not found"); + val = of_property_read_u8(np, "aw869x_cont_drv_lev", + &aw_haptic->info.cont_drv_lvl); + if (val != 0) + aw_info("aw869x_cont_drv_lev not found"); + val = of_property_read_u8(np, "aw869x_cont_drv_lvl_ov", + &aw_haptic->info.cont_drv_lvl_ov); + if (val != 0) + aw_info("aw869x_cont_drv_lvl_ov not found"); + val = of_property_read_u32(np, "aw869x_cont_td", + &aw_haptic->info.cont_td); + if (val != 0) + aw_info("aw869x_cont_td not found"); + val = of_property_read_u32(np, "aw869x_cont_zc_thr", + &aw_haptic->info.cont_zc_thr); + if (val != 0) + aw_info("aw869x_cont_zc_thr not found"); + val = of_property_read_u8(np, "aw869x_cont_num_brk", + &aw_haptic->info.cont_num_brk); + if (val != 0) + aw_info("aw869x_cont_num_brk not found"); + val = of_property_read_u32(np, "aw869x_f0_coeff", + &aw_haptic->info.f0_coeff); + if (val != 0) + aw_info("aw869x_f0_coeff not found"); + val = of_property_read_u8_array(np, "aw869x_duration_time", + duration_time, + ARRAY_SIZE(duration_time)); + if (val != 0) + aw_info("aw869x_duration_time not found"); + else + memcpy(aw_haptic->info.duration_time, duration_time, + sizeof(duration_time)); + val = of_property_read_u8(np, "aw869x_tset", &aw_haptic->info.tset); + if (val != 0) + aw_info("aw869x_tset not found"); + val = of_property_read_u8(np, "aw869x_r_spare", + &aw_haptic->info.r_spare); + if (val != 0) + aw_info("aw869x_r_spare not found"); + val = of_property_read_u8_array(np, "aw869x_bstdbg", bstdbg, + ARRAY_SIZE(bstdbg)); + if (val != 0) + aw_info("aw869x_bstdbg not found"); + else + memcpy(aw_haptic->info.bstdbg, bstdbg, sizeof(bstdbg)); + val = of_property_read_u8_array(np, "aw869x_f0_trace_parameter", + f0_trace_parameter, + ARRAY_SIZE(f0_trace_parameter)); + if (val != 0) + aw_info("aw869x_f0_trace_parameter not found"); + else + memcpy(aw_haptic->info.f0_trace_parameter, f0_trace_parameter, + sizeof(f0_trace_parameter)); + val = of_property_read_u8_array(np, "aw869x_bemf_config", bemf_config, + ARRAY_SIZE(bemf_config)); + if (val != 0) + aw_info("aw869x_bemf_config not found"); + else + memcpy(aw_haptic->info.bemf_config, bemf_config, + sizeof(bemf_config)); + val = of_property_read_u8_array(np, "aw869x_trig_config", + trig_config_temp, + ARRAY_SIZE(trig_config_temp)); + if (val != 0) + aw_info("vib_trig_config not found"); + else + memcpy(aw_haptic->info.trig_cfg, trig_config_temp, + sizeof(trig_config_temp)); + val = of_property_read_u32(np, "aw869x_bst_vol_default", + &aw_haptic->info.bst_vol_default); + if (val != 0) + aw_info("aw869x_bst_vol_default not found"); +} + +static ssize_t aw869x_cont_td_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "aw869x cont delay time = 0x%04x\n", + aw_haptic->info.cont_td); + return len; +} + +static ssize_t aw869x_cont_td_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t cont_td[2] = {0}; + uint32_t databuf[1] = { 0 }; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (sscanf(buf, "%x", &databuf[0]) == 1) { + aw_haptic->info.cont_td = databuf[0]; + cont_td[0] = (uint8_t)(databuf[0] >> 8); + cont_td[1] = (uint8_t)(databuf[0] >> 0); + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_TD_H, cont_td, + AW_I2C_BYTE_TWO); + } + return count; +} + +static ssize_t aw869x_cont_drv_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "aw869x cont drv level = %d\n", + aw_haptic->info.cont_drv_lvl); + len += snprintf(buf + len, PAGE_SIZE - len, + "aw869x cont drv level overdrive= %d\n", + aw_haptic->info.cont_drv_lvl_ov); + return len; +} + +static ssize_t aw869x_cont_drv_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t drv_lvl[2] = {0}; + uint32_t databuf[2] = { 0, 0 }; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (sscanf(buf, "%d %d", &databuf[0], &databuf[1]) == 2) { + aw_haptic->info.cont_drv_lvl = (uint8_t)databuf[0]; + aw_haptic->info.cont_drv_lvl_ov = (uint8_t)databuf[1]; + drv_lvl[0] = (uint8_t)databuf[0]; + drv_lvl[1] = (uint8_t)databuf[1]; + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_DRV_LVL, drv_lvl, + AW_I2C_BYTE_TWO); + } + return count; +} + +static ssize_t aw869x_cont_num_brk_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "aw869x cont break num = %d\n", + aw_haptic->info.cont_num_brk); + return len; +} + +static ssize_t aw869x_cont_num_brk_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t databuf[1] = { 0 }; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (sscanf(buf, "%d", &databuf[0]) == 1) { + aw_haptic->info.cont_num_brk = databuf[0]; + if (aw_haptic->info.cont_num_brk > 7) + aw_haptic->info.cont_num_brk = 7; + haptic_hv_i2c_write_bits(aw_haptic, AW869X_REG_BEMF_NUM, + AW869X_BIT_BEMF_NUM_BRK_MASK, + aw_haptic->info.cont_num_brk); + } + return count; +} + +static ssize_t aw869x_cont_zc_thr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "aw869x cont zero cross thr = 0x%04x\n", + aw_haptic->info.cont_zc_thr); + return len; +} + +static ssize_t aw869x_cont_zc_thr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t cont_zc[2] = {0}; + uint32_t databuf[1] = { 0 }; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (sscanf(buf, "%x", &databuf[0]) == 1) { + aw_haptic->info.cont_zc_thr = databuf[0]; + cont_zc[0] = (uint8_t)(databuf[0] >> 8); + cont_zc[1] = (uint8_t)(databuf[0] >> 0); + haptic_hv_i2c_writes(aw_haptic, AW869X_REG_ZC_THRSH_H, cont_zc, + AW_I2C_BYTE_TWO); + } + return count; +} + +static ssize_t aw869x_trig_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint8_t i = 0; + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + for (i = 0; i < AW_TRIG_NUM; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "trig%d: enable=%d, default_level=%d, dual_edge=%d, frist_seq=%d, second_seq=%d\n", + i + 1, aw_haptic->trig[i].enable, + aw_haptic->trig[i].default_level, + aw_haptic->trig[i].dual_edge, + aw_haptic->trig[i].frist_seq, + aw_haptic->trig[i].second_seq); + } + + return len; +} + +static ssize_t aw869x_trig_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t databuf[6] = { 0 }; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (sscanf(buf, "%d %d %d %d %d %d", + &databuf[0], &databuf[1], &databuf[2], &databuf[3], + &databuf[4], &databuf[5]) == 6) { + aw_info("%d, %d, %d, %d, %d, %d", databuf[0], + databuf[1], databuf[2], databuf[3], databuf[4], + databuf[5]); + if (databuf[0] > 3) + databuf[0] = 3; + if (databuf[0] > 0) + databuf[0] -= 1; + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + if (databuf[4] > aw_haptic->ram.ram_num || + databuf[5] > aw_haptic->ram.ram_num) { + aw_err("input wave num out of range!"); + return count; + } + aw_haptic->trig[databuf[0]].enable = databuf[1]; + aw_haptic->trig[databuf[0]].default_level = databuf[2]; + aw_haptic->trig[databuf[0]].dual_edge = databuf[3]; + aw_haptic->trig[databuf[0]].frist_seq = databuf[4]; + aw_haptic->trig[databuf[0]].second_seq = databuf[5]; + mutex_lock(&aw_haptic->lock); + aw869x_trig_param_config(aw_haptic); + aw869x_trig_enable_config(aw_haptic); + mutex_unlock(&aw_haptic->lock); + } + return count; +} + +static DEVICE_ATTR(cont_td, S_IWUSR | S_IRUGO, aw869x_cont_td_show, + aw869x_cont_td_store); +static DEVICE_ATTR(cont_drv, S_IWUSR | S_IRUGO, aw869x_cont_drv_show, + aw869x_cont_drv_store); +static DEVICE_ATTR(cont_num_brk, S_IWUSR | S_IRUGO, aw869x_cont_num_brk_show, + aw869x_cont_num_brk_store); +static DEVICE_ATTR(cont_zc_thr, S_IWUSR | S_IRUGO, aw869x_cont_zc_thr_show, + aw869x_cont_zc_thr_store); +static DEVICE_ATTR(trig, S_IWUSR | S_IRUGO, aw869x_trig_show, + aw869x_trig_store); + +static struct attribute *aw869x_vibrator_attributes[] = { + &dev_attr_cont_td.attr, + &dev_attr_cont_drv.attr, + &dev_attr_cont_num_brk.attr, + &dev_attr_cont_zc_thr.attr, + &dev_attr_trig.attr, + NULL +}; + +static struct attribute_group aw869x_vibrator_attribute_group = { + .attrs = aw869x_vibrator_attributes +}; + +static int aw869x_creat_node(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + ret = sysfs_create_group(&aw_haptic->vib_dev.dev->kobj, + &aw869x_vibrator_attribute_group); + if (ret < 0) + aw_err("error creating sysfs attr files"); + return ret; +} + +struct aw_haptic_func aw869x_func_list = { + .play_stop = aw869x_stop, + .ram_init = aw869x_ram_init, + .get_vbat = aw869x_get_vbat, + .creat_node = aw869x_creat_node, + .cont_config = aw869x_cont_config, + .offset_cali = aw869x_offset_cali, + .check_qualify = aw869x_check_qualify, + .get_irq_state = aw869x_get_irq_state, + .judge_rtp_going = aw869x_judge_rtp_going, + .set_bst_peak_cur = aw869x_set_bst_peak_cur, + .get_theory_time = aw869x_get_theory_time, + .get_lra_resistance = aw869x_get_lra_resistance, + .set_pwm = aw869x_set_pwm, + .play_mode = aw869x_play_mode, + .ram_get_f0 = aw869x_ram_get_f0, + .set_bst_vol = aw869x_set_bst_vol, + .interrupt_setup = aw869x_interrupt_setup, + .set_repeat_seq = aw869x_set_repeat_seq, + .auto_bst_enable = aw869x_auto_bst_enable, + .vbat_mode_config = aw869x_vbat_mode_config, + .set_wav_seq = aw869x_set_wav_seq, + .set_wav_loop = aw869x_set_wav_loop, + .set_ram_addr = aw869x_set_ram_addr, + .set_rtp_data = aw869x_set_rtp_data, + .container_update = aw869x_container_update, + .protect_config = aw869x_protect_config, + .parse_dt = aw869x_parse_dt, + .trig_init = aw869x_trig_init, + .irq_clear = aw869x_irq_clear, + .get_wav_loop = aw869x_get_wav_loop, + .play_go = aw869x_play_go, + .misc_para_init = aw869x_misc_para_init, + .set_rtp_aei = aw869x_set_rtp_aei, + .set_gain = aw869x_set_gain, + .upload_lra = aw869x_upload_lra, + .bst_mode_config = aw869x_bst_mode_config, + .get_reg = aw869x_get_reg, + .get_prctmode = aw869x_get_prctmode, + .get_trim_lra = aw869x_get_trim_lra, + .get_ram_data = aw869x_get_ram_data, + .get_first_wave_addr = aw869x_get_first_wave_addr, + .get_glb_state = aw869x_get_glb_state, + .get_osc_status = aw869x_osc_read_status, + .rtp_get_fifo_afs = aw869x_rtp_get_fifo_afs, + .rtp_get_fifo_aes = aw869x_rtp_get_fifo_aes, + .get_wav_seq = aw869x_get_wav_seq, +#ifdef AW869X_MUL_GET_F0 + .get_f0 = aw869x_multiple_get_f0, +#else + .get_f0 = aw869x_get_f0, +#endif +}; diff --git a/drivers/misc/awinic/haptic_hv/aw869xx.c b/drivers/misc/awinic/haptic_hv/aw869xx.c new file mode 100644 index 000000000000..1f25e13f6740 --- /dev/null +++ b/drivers/misc/awinic/haptic_hv/aw869xx.c @@ -0,0 +1,2284 @@ +/* + * File: aw869xx.c + * + * Author: Ethan + * + * Copyright (c) 2021 AWINIC Technology CO., 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "haptic_hv.h" +#include "haptic_hv_reg.h" + +static uint8_t aw869xx_get_glb_state(struct aw_haptic *aw_haptic); +static void aw869xx_vbat_mode_config(struct aw_haptic *, uint8_t); + +/****************************************************** + * + * aw869xx codec + * + ******************************************************/ +#ifdef AW_SND_SOC_CODEC + +#ifdef KERNEL_OVER_4_19 +static const struct aw_componet_codec_ops aw_componet_codec_ops = { + .aw_snd_soc_kcontrol_codec = snd_soc_kcontrol_component, + .aw_snd_soc_codec_get_drvdata = snd_soc_component_get_drvdata, + .aw_snd_soc_add_codec_controls = snd_soc_add_component_controls, + .aw_snd_soc_unregister_codec = snd_soc_unregister_component, + .aw_snd_soc_register_codec = snd_soc_register_component, +}; +#else +static const struct aw_componet_codec_ops aw_componet_codec_ops = { + .aw_snd_soc_kcontrol_codec = snd_soc_kcontrol_codec, + .aw_snd_soc_codec_get_drvdata = snd_soc_codec_get_drvdata, + .aw_snd_soc_add_codec_controls = snd_soc_add_codec_controls, + .aw_snd_soc_unregister_codec = snd_soc_unregister_codec, + .aw_snd_soc_register_codec = snd_soc_register_codec, +}; +#endif + +static aw_snd_soc_codec_t *aw_get_codec(struct snd_soc_dai *dai) +{ +#ifdef KERNEL_OVER_4_19 + return dai->component; +#else + return dai->codec; +#endif +} + +/****************************************************** + * + * Digital Audio Interface + * + ******************************************************/ +static void aw869xx_i2s_enable(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_I2SCFG2, + AW869XX_BIT_I2SCFG2_I2S_EN_MASK, + AW869XX_BIT_I2SCFG2_I2S_ENABLE); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_I2SCFG2, + AW869XX_BIT_I2SCFG2_I2S_EN_MASK, + AW869XX_BIT_I2SCFG2_I2S_DISABLE); + } +} + +static int aw869xx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw_haptic *aw_haptic = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + aw_info("enter"); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + mutex_lock(&aw_haptic->lock); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_STANDBY_MASK, + AW869XX_BIT_SYSCTRL2_STANDBY_OFF); + mutex_unlock(&aw_haptic->lock); + } + + return 0; +} + +static int aw869xx_set_fmt(struct snd_soc_dai *dai, uint32_t fmt) +{ + aw_info("fmt=0x%02X", fmt); + /* supported mode: regular I2S, slave, or PDM */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != + SND_SOC_DAIFMT_CBS_CFS) { + aw_err("invalid codec master mode"); + return -EINVAL; + } + break; + default: + aw_err("unsupported DAI format %d", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + return 0; +} + +static int aw869xx_set_dai_sysclk(struct snd_soc_dai *dai, + int clk_id, uint32_t freq, int dir) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw_haptic *aw_haptic = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + aw_info("freq=%d", freq); + + aw_haptic->sysclk = freq; + return 0; +} + +static int aw869xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + uint8_t i2sfs_val = 0; + uint8_t i2sbck_val = 0; + uint8_t tmp_val = 0; + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw_haptic *aw_haptic = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + aw_info("steam is capture"); + return 0; + } + + aw_info("requested rate: %d, sample size: %d", params_rate(params), + snd_pcm_format_width(params_format(params))); + + /* get rate param */ + aw_haptic->rate = params_rate(params); + + mutex_lock(&aw_haptic->lock); + + /* get bit width */ + aw_haptic->width = params_width(params); + aw_info("width = %d", aw_haptic->width); + i2sbck_val = params_width(params) * params_channels(params); + /* match bit width */ + switch (aw_haptic->width) { + case 16: + i2sfs_val = AW869XX_BIT_I2SCFG1_I2SFS_16BIT; + break; + case 24: + i2sfs_val = AW869XX_BIT_I2SCFG1_I2SFS_24BIT; + i2sbck_val = 32 * params_channels(params); + break; + case 32: + i2sfs_val = AW869XX_BIT_I2SCFG1_I2SFS_32BIT; + break; + default: + i2sfs_val = AW869XX_BIT_I2SCFG1_I2SFS_16BIT; + i2sbck_val = 16 * params_channels(params); + aw_err("width [%d]can not support", aw_haptic->width); + break; + } + aw_info("i2sfs_val=0x%02X", i2sfs_val); + /* set width */ + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_I2SCFG1, + AW869XX_BIT_I2SCFG1_I2SFS_MASK, i2sfs_val); + + /* match bck mode */ + switch (i2sbck_val) { + case 32: + i2sbck_val = AW869XX_BIT_I2SCFG1_I2SBCK_32FS; + break; + case 48: + i2sbck_val = AW869XX_BIT_I2SCFG1_I2SBCK_48FS; + break; + case 64: + i2sbck_val = AW869XX_BIT_I2SCFG1_I2SBCK_64FS; + break; + default: + aw_err("i2sbck [%d] can not support", i2sbck_val); + i2sbck_val = AW869XX_BIT_I2SCFG1_I2SBCK_32FS; + break; + } + /* set bck mode */ + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_I2SCFG1, + AW869XX_BIT_I2SCFG1_I2SBCK_MASK, i2sbck_val); + + /* check i2s cfg */ + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_I2SCFG1, &tmp_val, + AW_I2C_BYTE_ONE); + aw_info("i2scfg1=0x%02X", tmp_val); + + mutex_unlock(&aw_haptic->lock); + + return 0; +} + +static int aw869xx_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + uint8_t reg_val = 0; + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw_haptic *aw_haptic = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + if (stream == SNDRV_PCM_STREAM_CAPTURE) { + aw_info("steam is capture"); + return 0; + } + aw_info("mute state=%d", mute); + if (mute) { + mutex_lock(&aw_haptic->lock); + aw869xx_i2s_enable(aw_haptic, false); + mutex_unlock(&aw_haptic->lock); + } else { + mutex_lock(&aw_haptic->lock); + aw869xx_i2s_enable(aw_haptic, true); + usleep_range(2000, 3000); + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_SYSST, ®_val, + AW_I2C_BYTE_ONE); + aw_info("sysst=0x%02X", reg_val); + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_SYSST2, ®_val, + AW_I2C_BYTE_ONE); + aw_info("sysst2=0x%02X", reg_val); + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_SYSER, ®_val, + AW_I2C_BYTE_ONE); + aw_info("syser=0x%02X", reg_val); + reg_val = aw869xx_get_glb_state(aw_haptic); + if (reg_val != 0x0a) { + aw_err("i2s config err, glb_state=0x%02X", reg_val); + aw869xx_i2s_enable(aw_haptic, false); + } else { + aw_info("i2s config pass, glb_state=0x%02X", reg_val); + } + mutex_unlock(&aw_haptic->lock); + } + return 0; +} + +static void aw869xx_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + aw_snd_soc_codec_t *codec = aw_get_codec(dai); + struct aw_haptic *aw_haptic = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + aw_info("enter"); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aw_haptic->rate = 0; + mutex_lock(&aw_haptic->lock); + /* aw869xx_i2s_enable(aw_haptic, false); */ + mutex_unlock(&aw_haptic->lock); + } +} + +static const struct snd_soc_dai_ops aw869xx_dai_ops = { + .startup = aw869xx_startup, + .set_fmt = aw869xx_set_fmt, + .set_sysclk = aw869xx_set_dai_sysclk, + .hw_params = aw869xx_hw_params, + .mute_stream = aw869xx_mute, + .shutdown = aw869xx_shutdown, +}; + +static struct snd_soc_dai_driver aw869xx_dai[] = { + { + .name = "aw869xx-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AW869XX_RATES, + .formats = AW869XX_FORMATS, + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AW869XX_RATES, + .formats = AW869XX_FORMATS, + }, + .ops = &aw869xx_dai_ops, + .symmetric_rates = 1, + }, +}; + +/***************************************************** + * + * codec driver + * + *****************************************************/ +static void aw869xx_add_codec_controls(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); +} + +static int aw869xx_codec_probe(aw_snd_soc_codec_t *codec) +{ + int ret = -1; + struct aw_haptic *aw_haptic = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + aw_info("enter"); + + aw_haptic->codec = codec; + + aw869xx_add_codec_controls(aw_haptic); + + aw_info("exit"); + + ret = 0; + return ret; +} + +#ifdef KERNEL_OVER_4_19 +static void aw869xx_codec_remove(struct snd_soc_component *component) +{ + /* + * struct aw_haptic *aw_haptic = + * aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + */ + aw_info("enter"); +} +#else +static int aw869xx_codec_remove(aw_snd_soc_codec_t *codec) +{ + /* + * struct aw_haptic *aw_haptic = + * aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + */ + aw_info("enter"); + + return 0; +} +#endif + +static uint32_t aw869xx_codec_read(aw_snd_soc_codec_t *codec, uint32_t reg) +{ + uint8_t value = 0; + int ret = -1; + struct aw_haptic *aw_haptic = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + + aw_info("enter"); + + ret = haptic_hv_i2c_reads(aw_haptic, reg, &value, AW_I2C_BYTE_ONE); + if (ret < 0) { + aw_err("read register failed"); + return ret; + } + return ret; +} + +static int aw869xx_codec_write(aw_snd_soc_codec_t *codec, + uint32_t reg, uint32_t value) +{ + uint8_t val = (uint8_t)value; + int ret = -1; + struct aw_haptic *aw_haptic = + aw_componet_codec_ops.aw_snd_soc_codec_get_drvdata(codec); + aw_info("enter ,reg:0x%02X = 0x%02X", reg, value); + + ret = haptic_hv_i2c_writes(aw_haptic, (uint8_t)reg, &val, AW_I2C_BYTE_ONE); + + return ret; +} + +#ifdef KERNEL_OVER_4_19 +static struct snd_soc_component_driver soc_codec_dev_aw869xx = { + .probe = aw869xx_codec_probe, + .remove = aw869xx_codec_remove, + .read = aw869xx_codec_read, + .write = aw869xx_codec_write, +}; +#else +static struct snd_soc_codec_driver soc_codec_dev_aw869xx = { + .probe = aw869xx_codec_probe, + .remove = aw869xx_codec_remove, + .read = aw869xx_codec_read, + .write = aw869xx_codec_write, + .reg_cache_size = AW_HAPTIC_REG_MAX, + .reg_word_size = 2, +}; +#endif + +static int aw869xx_snd_soc_init(struct device *dev) +{ + int ret = 0; + struct snd_soc_dai_driver *dai; + + dev_set_name(dev, "%s", AW_I2C_NAME); + /* register codec */ + dai = devm_kzalloc(dev, sizeof(aw869xx_dai), GFP_KERNEL); + if (!dai) { + aw_err("failed to register aw869xx"); + return -ENOMEM; + } + memcpy(dai, aw869xx_dai, sizeof(aw869xx_dai)); + aw_info("dai->name(%s)", dai->name); + + ret = + aw_componet_codec_ops.aw_snd_soc_register_codec(dev, + &soc_codec_dev_aw869xx, + dai, + ARRAY_SIZE + (aw869xx_dai)); + if (ret < 0) { + aw_err("failed to register aw869xx: %d", ret); + return ret; + } + return 0; +} + +#endif + +static int aw869xx_check_qualify(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int ret = -1; + + aw_info("enter"); + /* chip qualify */ + ret = haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_RDATA_A, ®_val, + AW_I2C_BYTE_ONE); + if (ret < 0) + return ret; + if (!(reg_val & 0x80)) { + aw_err("unqualified chip!"); + return -ERANGE; + } + return 0; +} + +static void aw869xx_set_pwm(struct aw_haptic *aw_haptic, uint8_t mode) +{ + switch (mode) { + case AW_PWM_48K: + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW869XX_BIT_SYSCTRL2_RATE_48K); + break; + case AW_PWM_24K: + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW869XX_BIT_SYSCTRL2_RATE_24K); + break; + case AW_PWM_12K: + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_WAVDAT_MODE_MASK, + AW869XX_BIT_SYSCTRL2_RATE_12K); + break; + default: + break; + } +} + +static void aw869xx_set_gain(struct aw_haptic *aw_haptic, uint8_t gain) +{ + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_PLAYCFG2, &gain, + AW_I2C_BYTE_ONE); +} + +static void aw869xx_set_bst_peak_cur(struct aw_haptic *aw_haptic) +{ + switch (aw_haptic->bst_pc) { + case AW_BST_PC_L1: + aw_info("bst pc = L1"); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_BSTCFG1, + AW869XX_BIT_BSTCFG1_BST_PC_MASK, + AW869XX_BIT_BSTCFG1_PEAKCUR_2P75A); + return; + case AW_BST_PC_L2: + aw_info("bst pc = L2"); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_BSTCFG1, + AW869XX_BIT_BSTCFG1_BST_PC_MASK, + AW869XX_BIT_BSTCFG1_PEAKCUR_4A); + return; + default: + aw_info("bst pc = L1"); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_BSTCFG1, + AW869XX_BIT_BSTCFG1_BST_PC_MASK, + AW869XX_BIT_BSTCFG1_PEAKCUR_2P75A); + break; + } +} + +static void aw869xx_set_bst_vol(struct aw_haptic *aw_haptic, uint32_t bst_vol) +{ + uint8_t reg_val = 0; + + if (bst_vol < AW869XX_BST_VOL_MIN) + bst_vol = AW869XX_BST_VOL_MIN; + else if (bst_vol > AW869XX_BST_VOL_MAX) + bst_vol = AW869XX_BST_VOL_MAX; + reg_val = AW869XX_BST_VOL_FARMULA(bst_vol); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_PLAYCFG1, + AW869XX_BIT_PLAYCFG1_BST_VOUT_RDA_MASK, + reg_val); +} + +static void aw869xx_set_wav_seq(struct aw_haptic *aw_haptic, uint8_t wav, + uint8_t seq) +{ + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_WAVCFG1 + wav, &seq, + AW_I2C_BYTE_ONE); +} + +static void aw869xx_set_wav_loop(struct aw_haptic *aw_haptic, uint8_t wav, + uint8_t loop) +{ + uint8_t tmp = 0; + + if (wav % 2) { + tmp = loop << 0; + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_WAVCFG9 + (wav / 2), + AW869XX_BIT_WAVLOOP_SEQ_EVEN_MASK, tmp); + } else { + tmp = loop << 4; + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_WAVCFG9 + (wav / 2), + AW869XX_BIT_WAVLOOP_SEQ_ODD_MASK, tmp); + } +} + +static void aw869xx_set_rtp_data(struct aw_haptic *aw_haptic, uint8_t *data, + uint32_t len) +{ + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_RTPDATA, data, len); +} + +static void aw869xx_set_rtp_aei(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSINTM, + AW869XX_BIT_SYSINTM_FF_AEM_MASK, + AW869XX_BIT_SYSINTM_FF_AEM_ON); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSINTM, + AW869XX_BIT_SYSINTM_FF_AEM_MASK, + AW869XX_BIT_SYSINTM_FF_AEM_OFF); + } +} + +static void aw869xx_set_ram_addr(struct aw_haptic *aw_haptic) +{ + uint8_t ram_addr[2] = {0}; + + ram_addr[0] = (uint8_t)AW869XX_RAM_ADDR_H(aw_haptic->ram.base_addr); + ram_addr[1] = (uint8_t)AW869XX_RAM_ADDR_L(aw_haptic->ram.base_addr); + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_RAMADDRH, ram_addr, + AW_I2C_BYTE_TWO); +} + +static void aw869xx_auto_break_mode(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_BRK_EN_MASK, + AW869XX_BIT_PLAYCFG3_BRK_ENABLE); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_BRK_EN_MASK, + AW869XX_BIT_PLAYCFG3_BRK_DISABLE); + } +} + +static uint8_t aw869xx_get_glb_state(struct aw_haptic *aw_haptic) +{ + uint8_t state = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_GLBRD5, &state, + AW_I2C_BYTE_ONE); + aw_dbg("glb state value is 0x%02X", state); + return state; +} + +static void aw869xx_play_go(struct aw_haptic *aw_haptic, bool flag) +{ + uint8_t delay_20us = AW869XX_BIT_START_DLY_20US; + uint8_t delay_2p5ms = AW869XX_BIT_START_DLY_2P5MS; + uint8_t go_on = AW869XX_BIT_PLAYCFG4_GO_ON; + uint8_t stop_on = AW869XX_BIT_PLAYCFG4_STOP_ON; + + aw_info("enter, flag = %d", flag); + if (flag) { + if (aw_haptic->info.is_enabled_one_wire) { + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_GLBCFG2, + &delay_20us, AW_I2C_BYTE_ONE); + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_PLAYCFG4, + &go_on, AW_I2C_BYTE_ONE); + usleep_range(1000, 1500); + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_GLBCFG2, + &delay_2p5ms, AW_I2C_BYTE_ONE); + } else { + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_PLAYCFG4, + &go_on, AW_I2C_BYTE_ONE); + } + } else { + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_PLAYCFG4, &stop_on, + AW_I2C_BYTE_ONE); + } +} + +static void aw869xx_bst_mode_config(struct aw_haptic *aw_haptic, + uint8_t boost_mode) +{ + switch (boost_mode) { + case AW_BST_BOOST_MODE: + aw_info("haptic boost mode = boost"); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_PLAYCFG1, + AW869XX_BIT_PLAYCFG1_BST_MODE_MASK, + AW869XX_BIT_PLAYCFG1_BST_MODE_BOOST); + break; + case AW_BST_BYPASS_MODE: + aw_info("haptic boost mode = bypass"); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_PLAYCFG1, + AW869XX_BIT_PLAYCFG1_BST_MODE_MASK, + AW869XX_BIT_PLAYCFG1_BST_MODE_BYPASS); + break; + default: + aw_err("boost_mode = %d error", boost_mode); + break; + } +} + +static int aw869xx_wait_enter_standby(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int count = 100; + + while (count--) { + reg_val = aw869xx_get_glb_state(aw_haptic); + if ((reg_val & 0x0f) == AW869XX_BIT_GLBRD5_STATE_STANDBY || + (reg_val & 0x0f) == AW869XX_BIT_GLBRD5_STATE_I2S_GO) { + aw_info("entered standby!"); + return 0; + } + aw_dbg("wait for standby"); + usleep_range(2000, 2500); + } + aw_err("do not enter standby automatically"); + return -ERANGE; +} + +static void aw869xx_stop(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int ret = 0; + + aw_haptic->play_mode = AW_STANDBY_MODE; + reg_val = AW869XX_BIT_PLAYCFG4_STOP_ON; + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_PLAYCFG4, ®_val, + AW_I2C_BYTE_ONE); + ret = aw869xx_wait_enter_standby(aw_haptic); + if (ret < 0) { + aw_err("force to enter standby mode!"); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_STANDBY_MASK, + AW869XX_BIT_SYSCTRL2_STANDBY_ON); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_STANDBY_MASK, + AW869XX_BIT_SYSCTRL2_STANDBY_OFF); + } +} + +static void aw869xx_play_mode(struct aw_haptic *aw_haptic, uint8_t play_mode) +{ + switch (play_mode) { + case AW_STANDBY_MODE: + aw_info("enter standby mode"); + aw_haptic->play_mode = AW_STANDBY_MODE; + aw869xx_stop(aw_haptic); + break; + case AW_RAM_MODE: + aw_info("enter ram mode"); + aw_haptic->play_mode = AW_RAM_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_RAM); + /* bst mode */ + aw869xx_bst_mode_config(aw_haptic, AW_BST_BOOST_MODE); + aw869xx_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + break; + case AW_RAM_LOOP_MODE: + aw_info("enter ram loop mode"); + aw_haptic->play_mode = AW_RAM_LOOP_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_RAM); + /* bst mode */ + aw869xx_bst_mode_config(aw_haptic, AW_BST_BYPASS_MODE); + aw869xx_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + break; + case AW_RTP_MODE: + aw_info("enter rtp mode"); + aw_haptic->play_mode = AW_RTP_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_RTP); + /* bst mode */ + aw869xx_bst_mode_config(aw_haptic, AW_BST_BOOST_MODE); + aw869xx_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + break; + case AW_TRIG_MODE: + aw_info("enter trig mode"); + aw_haptic->play_mode = AW_TRIG_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_RAM); + aw869xx_vbat_mode_config(aw_haptic, AW_CONT_VBAT_SW_COMP_MODE); + break; + case AW_CONT_MODE: + aw_info("enter cont mode"); + aw_haptic->play_mode = AW_CONT_MODE; + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_MASK, + AW869XX_BIT_PLAYCFG3_PLAY_MODE_CONT); + /* bst mode */ + aw869xx_bst_mode_config(aw_haptic, AW_BST_BYPASS_MODE); + aw869xx_vbat_mode_config(aw_haptic, AW_CONT_VBAT_HW_COMP_MODE); + break; + default: + aw_err("play mode %d error", play_mode); + break; + } +} + +static void aw869xx_ram_init(struct aw_haptic *aw_haptic, bool flag) +{ + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSCTRL1, + AW869XX_BIT_SYSCTRL1_RAMINIT_MASK, + AW869XX_BIT_SYSCTRL1_RAMINIT_ON); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSCTRL1, + AW869XX_BIT_SYSCTRL1_RAMINIT_MASK, + AW869XX_BIT_SYSCTRL1_RAMINIT_OFF); + } +} + +static void aw869xx_upload_lra(struct aw_haptic *aw_haptic, uint32_t flag) +{ + switch (flag) { + case AW_WRITE_ZERO: + aw_info("write zero to trim_lra!"); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRIMCFG3, + AW869XX_BIT_TRIMCFG3_TRIM_LRA_MASK, + 0x00); + break; + case AW_F0_CALI_LRA: + aw_info("write f0_cali_data to trim_lra = 0x%02X", + aw_haptic->f0_cali_data); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRIMCFG3, + AW869XX_BIT_TRIMCFG3_TRIM_LRA_MASK, + (char)aw_haptic->f0_cali_data); + break; + case AW_OSC_CALI_LRA: + aw_info("write osc_cali_data to trim_lra = 0x%02X", + aw_haptic->osc_cali_data); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRIMCFG3, + AW869XX_BIT_TRIMCFG3_TRIM_LRA_MASK, + (char)aw_haptic->osc_cali_data); + break; + default: + break; + } +} + +static uint8_t aw869xx_get_trim_lra(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_TRIMCFG3, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= 0x3F; + return reg_val; +} + +static void aw869xx_vbat_mode_config(struct aw_haptic *aw_haptic, uint8_t flag) +{ + if (flag == AW_CONT_VBAT_HW_COMP_MODE) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSCTRL1, + AW869XX_BIT_SYSCTRL1_VBAT_MODE_MASK, + AW869XX_BIT_SYSCTRL1_VBAT_MODE_HW); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSCTRL1, + AW869XX_BIT_SYSCTRL1_VBAT_MODE_MASK, + AW869XX_BIT_SYSCTRL1_VBAT_MODE_SW); + } +} + +static void aw869xx_protect_config(struct aw_haptic *aw_haptic, uint8_t addr, + uint8_t val) +{ + aw_info("enter, addr=%d, val=%d", addr, val); + if (addr == 1) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_DETCFG1, + AW869XX_BIT_DETCFG1_PRCT_MODE_MASK, + AW869XX_BIT_DETCFG1_PRCT_MODE_VALID); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_PWMCFG1, + AW869XX_BIT_PWMCFG1_PRC_EN_MASK, + AW869XX_BIT_PWMCFG1_PRC_ENABLE); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_PWMCFG3, + AW869XX_BIT_PWMCFG3_PR_EN_MASK, + AW869XX_BIT_PWMCFG3_PR_ENABLE); + } else if (addr == 0) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_DETCFG1, + AW869XX_BIT_DETCFG1_PRCT_MODE_MASK, + AW869XX_BIT_DETCFG1_PRCT_MODE_INVALID); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_PWMCFG1, + AW869XX_BIT_PWMCFG1_PRC_EN_MASK, + AW869XX_BIT_PWMCFG1_PRC_DISABLE); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_PWMCFG3, + AW869XX_BIT_PWMCFG3_PR_EN_MASK, + AW869XX_BIT_PWMCFG3_PR_DISABLE); + } else if (addr == AW869XX_REG_PWMCFG1) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_PWMCFG1, + AW869XX_BIT_PWMCFG1_PRCTIME_MASK, val); + } else if (addr == AW869XX_REG_PWMCFG3) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_PWMCFG3, + AW869XX_BIT_PWMCFG3_PRLVL_MASK, val); + } else if (addr == AW869XX_REG_PWMCFG4) { + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_PWMCFG4, &val, + AW_I2C_BYTE_ONE); + } +} + +static void aw869xx_cont_config(struct aw_haptic *aw_haptic) +{ + /* uint8_t drv1_time = 0xFF; */ + uint8_t drv2_time = 0xFF; + + /* work mode */ + aw869xx_play_mode(aw_haptic, AW_CONT_MODE); + /* cont config */ + /* haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_CONTCFG1, + * AW869XX_BIT_CONTCFG1_EN_F0_DET_MASK, + * AW869XX_BIT_CONTCFG1_F0_DET_ENABLE); + */ + + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_CONTCFG6, + (AW869XX_BIT_CONTCFG6_TRACK_EN_MASK & + AW869XX_BIT_CONTCFG6_DRV1_LVL_MASK), + (AW869XX_BIT_CONTCFG6_TRACK_DISABLE | + aw_haptic->info.cont_drv1_lvl)); + + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_CONTCFG7, + &aw_haptic->info.cont_drv2_lvl, AW_I2C_BYTE_ONE); + /* DRV1_TIME */ + /* haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_CONTCFG8, &drv1_time, + * AW_I2C_BYTE_ONE); + */ + /* DRV2_TIME */ + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_CONTCFG9, &drv2_time, + AW_I2C_BYTE_ONE); + /* cont play go */ + aw869xx_play_go(aw_haptic, true); +} + +static void aw869xx_one_wire_init(struct aw_haptic *aw_haptic) +{ + uint8_t trig_prio = 0x6c; + uint8_t delay_2p5ms = AW869XX_BIT_START_DLY_2P5MS; + + aw_info("enter"); + /*if enable one-wire, trig1 priority must be less than trig2 and trig3*/ + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_GLBCFG4, &trig_prio, + AW_I2C_BYTE_ONE); + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_GLBCFG2, &delay_2p5ms, + AW_I2C_BYTE_ONE); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG8, + AW869XX_BIT_TRGCFG8_TRG_ONEWIRE_MASK, + AW869XX_BIT_TRGCFG8_TRG_ONEWIRE_ENABLE); +} + +static void aw869xx_i2s_init(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_I2S_PIN_MASK, + AW869XX_BIT_SYSCTRL2_I2S_PIN_I2S); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_IOCFG1, + AW869XX_BIT_IOCFG1_IO_FAST_MASK, + AW869XX_BIT_IOCFG1_IIS_IO_FAST_ENABLE); +} + +static void aw869xx_trig1_param_init(struct aw_haptic *aw_haptic) +{ + aw_haptic->trig[0].trig_level = aw_haptic->info.trig_cfg[0]; + aw_haptic->trig[0].trig_polar = aw_haptic->info.trig_cfg[1]; + aw_haptic->trig[0].pos_enable = aw_haptic->info.trig_cfg[2]; + aw_haptic->trig[0].pos_sequence = aw_haptic->info.trig_cfg[3]; + aw_haptic->trig[0].neg_enable = aw_haptic->info.trig_cfg[4]; + aw_haptic->trig[0].neg_sequence = aw_haptic->info.trig_cfg[5]; + aw_haptic->trig[0].trig_brk = aw_haptic->info.trig_cfg[6]; + aw_haptic->trig[0].trig_bst = aw_haptic->info.trig_cfg[7]; +} + +static void aw869xx_trig2_param_init(struct aw_haptic *aw_haptic) +{ + aw_haptic->trig[1].trig_level = aw_haptic->info.trig_cfg[8]; + aw_haptic->trig[1].trig_polar = aw_haptic->info.trig_cfg[9]; + aw_haptic->trig[1].pos_enable = aw_haptic->info.trig_cfg[10]; + aw_haptic->trig[1].pos_sequence = aw_haptic->info.trig_cfg[11]; + aw_haptic->trig[1].neg_enable = aw_haptic->info.trig_cfg[12]; + aw_haptic->trig[1].neg_sequence = aw_haptic->info.trig_cfg[13]; + aw_haptic->trig[1].trig_brk = aw_haptic->info.trig_cfg[14]; + aw_haptic->trig[1].trig_bst = aw_haptic->info.trig_cfg[15]; +} + +static void aw869xx_trig3_param_init(struct aw_haptic *aw_haptic) +{ + aw_haptic->trig[2].trig_level = aw_haptic->info.trig_cfg[16]; + aw_haptic->trig[2].trig_polar = aw_haptic->info.trig_cfg[17]; + aw_haptic->trig[2].pos_enable = aw_haptic->info.trig_cfg[18]; + aw_haptic->trig[2].pos_sequence = aw_haptic->info.trig_cfg[19]; + aw_haptic->trig[2].neg_enable = aw_haptic->info.trig_cfg[20]; + aw_haptic->trig[2].neg_sequence = aw_haptic->info.trig_cfg[21]; + aw_haptic->trig[2].trig_brk = aw_haptic->info.trig_cfg[22]; + aw_haptic->trig[2].trig_bst = aw_haptic->info.trig_cfg[23]; +} + +static void aw869xx_trig1_param_config(struct aw_haptic *aw_haptic) +{ + uint8_t trig_config = 0; + + if (aw_haptic->trig[0].trig_level) + trig_config |= AW869XX_BIT_TRGCFG7_TRG1_MODE_LEVEL; + else + trig_config |= AW869XX_BIT_TRGCFG7_TRG1_MODE_EDGE; + if (aw_haptic->trig[0].trig_polar) + trig_config |= AW869XX_BIT_TRGCFG7_TRG1_POLAR_NEG; + else + trig_config |= AW869XX_BIT_TRGCFG7_TRG1_POLAR_POS; + if (aw_haptic->trig[0].pos_enable) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG1, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_ENABLE); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG1, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_DISABLE); + } + if (aw_haptic->trig[0].neg_enable) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG4, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_ENABLE); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG4, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_DISABLE); + } + if (aw_haptic->trig[0].pos_sequence) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG1, + AW869XX_BIT_TRG_SEQ_MASK, + aw_haptic->trig[0].pos_sequence); + } + if (aw_haptic->trig[0].neg_sequence) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG4, + AW869XX_BIT_TRG_SEQ_MASK, + aw_haptic->trig[0].neg_sequence); + } + if (aw_haptic->trig[0].trig_brk) + trig_config |= AW869XX_BIT_TRGCFG7_TRG1_AUTO_BRK_ENABLE; + else + trig_config |= AW869XX_BIT_TRGCFG7_TRG1_AUTO_BRK_DISABLE; + if (aw_haptic->trig[0].trig_bst) + trig_config |= AW869XX_BIT_TRGCFG7_TRG1_BST_ENABLE; + else + trig_config |= AW869XX_BIT_TRGCFG7_TRG1_BST_DISABLE; + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG7, + (AW869XX_BIT_TRGCFG7_TRG1_MODE_MASK & + AW869XX_BIT_TRGCFG7_TRG1_POLAR_MASK & + AW869XX_BIT_TRGCFG7_TRG1_AUTO_BRK_MASK & + AW869XX_BIT_TRGCFG7_TRG1_BST_MASK), + trig_config); +} + +static void aw869xx_trig2_param_config(struct aw_haptic *aw_haptic) +{ + uint8_t trig_config = 0; + + if (aw_haptic->trig[1].trig_level) + trig_config |= AW869XX_BIT_TRGCFG7_TRG2_MODE_LEVEL; + else + trig_config |= AW869XX_BIT_TRGCFG7_TRG2_MODE_EDGE; + if (aw_haptic->trig[1].trig_polar) + trig_config |= AW869XX_BIT_TRGCFG7_TRG2_POLAR_NEG; + else + trig_config |= AW869XX_BIT_TRGCFG7_TRG2_POLAR_POS; + if (aw_haptic->trig[1].pos_enable) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG2, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_ENABLE); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG2, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_DISABLE); + } + if (aw_haptic->trig[1].neg_enable) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG5, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_ENABLE); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG5, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_DISABLE); + } + if (aw_haptic->trig[1].pos_sequence) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG2, + AW869XX_BIT_TRG_SEQ_MASK, + aw_haptic->trig[1].pos_sequence); + } + if (aw_haptic->trig[1].neg_sequence) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG5, + AW869XX_BIT_TRG_SEQ_MASK, + aw_haptic->trig[1].neg_sequence); + } + if (aw_haptic->trig[1].trig_brk) + trig_config |= AW869XX_BIT_TRGCFG7_TRG2_AUTO_BRK_ENABLE; + else + trig_config |= AW869XX_BIT_TRGCFG7_TRG2_AUTO_BRK_DISABLE; + if (aw_haptic->trig[1].trig_bst) + trig_config |= AW869XX_BIT_TRGCFG7_TRG2_BST_ENABLE; + else + trig_config |= AW869XX_BIT_TRGCFG7_TRG2_BST_DISABLE; + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG7, + (AW869XX_BIT_TRGCFG7_TRG2_MODE_MASK & + AW869XX_BIT_TRGCFG7_TRG2_POLAR_MASK & + AW869XX_BIT_TRGCFG7_TRG2_AUTO_BRK_MASK & + AW869XX_BIT_TRGCFG7_TRG2_BST_MASK), + trig_config); +} + +static void aw869xx_trig3_param_config(struct aw_haptic *aw_haptic) +{ + uint8_t trig_config = 0; + + if (aw_haptic->trig[2].trig_level) + trig_config |= AW869XX_BIT_TRGCFG8_TRG3_MODE_LEVEL; + else + trig_config |= AW869XX_BIT_TRGCFG8_TRG3_MODE_EDGE; + if (aw_haptic->trig[2].trig_polar) + trig_config |= AW869XX_BIT_TRGCFG8_TRG3_POLAR_NEG; + else + trig_config |= AW869XX_BIT_TRGCFG8_TRG3_POLAR_POS; + if (aw_haptic->trig[2].pos_enable) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG3, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_ENABLE); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG3, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_DISABLE); + } + if (aw_haptic->trig[2].neg_enable) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG6, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_ENABLE); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG6, + AW869XX_BIT_TRG_ENABLE_MASK, + AW869XX_BIT_TRG_DISABLE); + } + if (aw_haptic->trig[2].pos_sequence) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG3, + AW869XX_BIT_TRG_SEQ_MASK, + aw_haptic->trig[2].pos_sequence); + } + if (aw_haptic->trig[2].neg_sequence) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG6, + AW869XX_BIT_TRG_SEQ_MASK, + aw_haptic->trig[2].neg_sequence); + } + if (aw_haptic->trig[2].trig_brk) + trig_config |= AW869XX_BIT_TRGCFG8_TRG3_AUTO_BRK_ENABLE; + else + trig_config |= AW869XX_BIT_TRGCFG8_TRG3_AUTO_BRK_DISABLE; + if (aw_haptic->trig[2].trig_bst) + trig_config |= AW869XX_BIT_TRGCFG8_TRG3_BST_ENABLE; + else + trig_config |= AW869XX_BIT_TRGCFG8_TRG3_BST_DISABLE; + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_TRGCFG8, + (AW869XX_BIT_TRGCFG8_TRG3_MODE_MASK & + AW869XX_BIT_TRGCFG8_TRG3_POLAR_MASK & + AW869XX_BIT_TRGCFG8_TRG3_AUTO_BRK_MASK & + AW869XX_BIT_TRGCFG8_TRG3_BST_MASK), + trig_config); +} + +static void aw869xx_auto_bst_enable(struct aw_haptic *aw_haptic, uint8_t flag) +{ + aw_haptic->auto_boost = flag; + if (flag) { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_AUTO_BST_MASK, + AW869XX_BIT_PLAYCFG3_AUTO_BST_ENABLE); + } else { + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_PLAYCFG3, + AW869XX_BIT_PLAYCFG3_AUTO_BST_MASK, + AW869XX_BIT_PLAYCFG3_AUTO_BST_DISABLE); + } +} + +static void aw869xx_interrupt_setup(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_SYSINT, ®_val, AW_I2C_BYTE_ONE); + aw_info("reg SYSINT=0x%02X", reg_val); + /* edge int mode */ + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSCTRL7, + (AW869XX_BIT_SYSCTRL7_INT_MODE_MASK & + AW869XX_BIT_SYSCTRL7_INT_EDGE_MODE_MASK), + (AW869XX_BIT_SYSCTRL7_INT_MODE_EDGE | + AW869XX_BIT_SYSCTRL7_INT_EDGE_MODE_POS)); + /* int enable */ + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSINTM, + (AW869XX_BIT_SYSINTM_BST_SCPM_MASK & + AW869XX_BIT_SYSINTM_BST_OVPM_MASK & + AW869XX_BIT_SYSINTM_UVLM_MASK & + AW869XX_BIT_SYSINTM_OCDM_MASK & + AW869XX_BIT_SYSINTM_OTM_MASK), + (AW869XX_BIT_SYSINTM_BST_SCPM_ON | + AW869XX_BIT_SYSINTM_BST_OVPM_OFF | + AW869XX_BIT_SYSINTM_UVLM_ON | + AW869XX_BIT_SYSINTM_OCDM_ON | + AW869XX_BIT_SYSINTM_OTM_ON)); +} + +static int aw869xx_judge_rtp_going(struct aw_haptic *aw_haptic) +{ + uint8_t glb_state = 0; + uint8_t rtp_state = 0; + + glb_state = aw869xx_get_glb_state(aw_haptic); + if (glb_state == AW869XX_BIT_GLBRD5_STATE_RTP_GO) { + rtp_state = 1; /*is going on */ + aw_info("rtp_routine_on"); + } + return rtp_state; +} + +static void aw869xx_get_ram_data(struct aw_haptic *aw_haptic, char *buf) +{ + int i = 0; + int size = 0; + + while (i < aw_haptic->ram.len) { + if ((aw_haptic->ram.len - i) < AW_RAMDATA_RD_BUFFER_SIZE) + size = aw_haptic->ram.len - i; + else + size = AW_RAMDATA_RD_BUFFER_SIZE; + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_RAMDATA, buf + i, + size); + i += size; + } +} + +static void aw869xx_get_first_wave_addr(struct aw_haptic *aw_haptic, + uint8_t *wave_addr) +{ + uint8_t reg_array[3] = {0}; + + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_RAMDATA, reg_array, + AW_I2C_BYTE_THREE); + wave_addr[0] = reg_array[1]; + wave_addr[1] = reg_array[2]; +} + +static void aw869xx_get_wav_seq(struct aw_haptic *aw_haptic, uint32_t len) +{ + uint8_t i = 0; + uint8_t reg_val[AW_SEQUENCER_SIZE] = {0}; + + if (len > AW_SEQUENCER_SIZE) + len = AW_SEQUENCER_SIZE; + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_WAVCFG1, reg_val, len); + for (i = 0; i < len; i++) + aw_haptic->seq[i] = reg_val[i]; +} + +static size_t aw869xx_get_wav_loop(struct aw_haptic *aw_haptic, char *buf) +{ + uint8_t i = 0; + uint8_t reg_val[4] = {0}; + size_t count = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_WAVCFG9, reg_val, + AW_I2C_BYTE_FOUR); + for (i = 0; i < AW_SEQUENCER_LOOP_SIZE; i++) { + aw_haptic->loop[i * 2 + 0] = (reg_val[i] >> 4) & 0x0F; + aw_haptic->loop[i * 2 + 1] = (reg_val[i] >> 0) & 0x0F; + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d loop: 0x%02x\n", i * 2 + 1, + aw_haptic->loop[i * 2 + 0]); + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d loop: 0x%02x\n", i * 2 + 2, + aw_haptic->loop[i * 2 + 1]); + } + return count; +} + +static void aw869xx_irq_clear(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_info("reg SYSINT=0x%02X", reg_val); +} + +static uint8_t aw869xx_get_prctmode(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_DETCFG1, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= 0x08; + return reg_val; +} + +static int aw869xx_get_irq_state(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + int ret = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_SYSINT, ®_val, + AW_I2C_BYTE_ONE); + aw_dbg("reg SYSINT=0x%02X", reg_val); + if (reg_val & AW869XX_BIT_SYSINT_BST_SCPI) { + ret = AW_IRQ_BST_SCP; + aw_err("chip scp int error"); + } + if (reg_val & AW869XX_BIT_SYSINT_BST_OVPI) { + ret = AW_IRQ_BST_OVP; + aw_err("chip ov int error"); + } + if (reg_val & AW869XX_BIT_SYSINT_UVLI) { + ret = AW_IRQ_UVLO; + aw_err("chip uvlo int error"); + } + if (reg_val & AW869XX_BIT_SYSINT_OCDI) { + ret = AW_IRQ_OCD; + aw_err("chip over current int error"); + } + if (reg_val & AW869XX_BIT_SYSINT_OTI) { + ret = AW_IRQ_OT; + aw_err("chip over temperature int error"); + } + if (reg_val & AW869XX_BIT_SYSINT_DONEI) { + ret = AW_IRQ_DONE; + aw_info("chip playback done"); + } + if (reg_val & AW869XX_BIT_SYSINT_FF_AFI) { + ret = AW_IRQ_ALMOST_FULL; + aw_info("aw_haptic rtp mode fifo almost full!"); + } + if (reg_val & AW869XX_BIT_SYSINT_FF_AEI) + ret = AW_IRQ_ALMOST_EMPTY; + return ret; +} + +static int aw869xx_read_f0(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val[2] = {0}; + uint32_t f0_reg = 0; + +#ifdef AW_LRA_F0_DEFAULT + /* lra_f0 */ + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_CONTRD14, reg_val, + AW_I2C_BYTE_TWO); + f0_reg = (reg_val[0] << 8) | (reg_val[1] << 0); + if (!f0_reg) { + aw_haptic->f0 = 0; + aw_err("lra_f0 is error, f0_reg=0"); + return -ERANGE; + } + aw_haptic->f0 = (uint32_t)AW869XX_F0_FARMULA(f0_reg); + aw_info("lra_f0=%d", aw_haptic->f0); +#else + /* cont_f0 */ + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_CONTRD16, reg_val, + AW_I2C_BYTE_TWO); + f0_reg = (reg_val[0] << 8) | (reg_val[1] << 0); + if (!f0_reg) { + aw_haptic->f0 = 0; + aw_err("cont_f0 is error, f0_reg=0"); + return -ERANGE; + } + aw_haptic->f0 = (uint32_t)AW869XX_F0_FARMULA(f0_reg); + aw_info("cont_f0=%d", aw_haptic->f0); +#endif + return 0; +} + +static int aw869xx_get_f0(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint8_t brk_en_default = 0; + uint8_t cont_config[3] = {0}; + int ret = 0; + + aw_haptic->f0 = aw_haptic->info.f0_pre; + /* enter standby mode */ + aw869xx_stop(aw_haptic); + /* f0 calibrate work mode */ + aw869xx_play_mode(aw_haptic, AW_CONT_MODE); + /* enable f0 detect */ + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_CONTCFG1, + AW869XX_BIT_CONTCFG1_EN_F0_DET_MASK, + AW869XX_BIT_CONTCFG1_F0_DET_ENABLE); + /* cont config */ + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_CONTCFG6, + AW869XX_BIT_CONTCFG6_TRACK_EN_MASK, + AW869XX_BIT_CONTCFG6_TRACK_DISABLE); + /* enable auto break */ + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_PLAYCFG3, ®_val, + AW_I2C_BYTE_ONE); + brk_en_default = 0x04 & reg_val; + aw869xx_auto_break_mode(aw_haptic, true); + /* f0 driver level & time */ + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_CONTCFG6, + AW869XX_BIT_CONTCFG6_DRV1_LVL_MASK, + aw_haptic->info.cont_drv1_lvl); + cont_config[0] = aw_haptic->info.cont_drv2_lvl; + cont_config[1] = aw_haptic->info.cont_drv1_time; + cont_config[2] = aw_haptic->info.cont_drv2_time; + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_CONTCFG7, cont_config, + AW_I2C_BYTE_THREE); + /* TRACK_MARGIN */ + if (!aw_haptic->info.cont_track_margin) { + aw_err("aw_haptic->info.cont_track_margin = 0!"); + } else { + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_CONTCFG11, + &aw_haptic->info.cont_track_margin, + AW_I2C_BYTE_ONE); + } + /* DRV_WIDTH */ + if (!aw_haptic->info.f0_pre) + return -ERANGE; + cont_config[0] = AW_DRV_WIDTH_FARMULA(aw_haptic->info.f0_pre, + aw_haptic->info.cont_brk_gain, + aw_haptic->info.cont_track_margin); + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_CONTCFG3, &cont_config[0], + AW_I2C_BYTE_ONE); + /* play go */ + aw869xx_play_go(aw_haptic, true); + usleep_range(20000, 20500); + aw869xx_wait_enter_standby(aw_haptic); + ret = aw869xx_read_f0(aw_haptic); + /* restore default config */ + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_CONTCFG1, + AW869XX_BIT_CONTCFG1_EN_F0_DET_MASK, + AW869XX_BIT_CONTCFG1_F0_DET_DISABLE); + /* recover auto break config */ + if (brk_en_default) + aw869xx_auto_break_mode(aw_haptic, true); + else + aw869xx_auto_break_mode(aw_haptic, false); + return ret; +} + +static int aw869xx_ram_get_f0(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint8_t brk_en_default = 0; + int ret = 0; + + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return -ERANGE; + } + if (aw_haptic->ram.ram_num < AW_RAM_GET_F0_SEQ) { + aw_err("miss ram get f0 waveform!"); + return -ERANGE; + } + aw_haptic->f0 = aw_haptic->info.f0_pre; + /* enter standby mode */ + aw869xx_stop(aw_haptic); + /* f0 calibrate work mode */ + aw869xx_play_mode(aw_haptic, AW_RAM_MODE); + /* enable f0 detect */ + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_CONTCFG1, + AW869XX_BIT_CONTCFG1_EN_F0_DET_MASK, + AW869XX_BIT_CONTCFG1_F0_DET_ENABLE); + /* enable auto break */ + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_PLAYCFG3, ®_val, + AW_I2C_BYTE_ONE); + brk_en_default = 0x04 & reg_val; + aw869xx_auto_break_mode(aw_haptic, true); + aw869xx_set_bst_vol(aw_haptic, 8000); + aw869xx_set_wav_seq(aw_haptic, 0x00, AW_RAM_GET_F0_SEQ); + aw869xx_set_wav_seq(aw_haptic, 0x01, 0x00); + aw869xx_set_wav_loop(aw_haptic, 0x00, 0x02); + /* play go */ + aw869xx_play_go(aw_haptic, true); + usleep_range(20000, 20500); + aw869xx_wait_enter_standby(aw_haptic); + ret = aw869xx_read_f0(aw_haptic); + /* restore default config */ + aw869xx_set_bst_vol(aw_haptic, aw_haptic->vmax); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_CONTCFG1, + AW869XX_BIT_CONTCFG1_EN_F0_DET_MASK, + AW869XX_BIT_CONTCFG1_F0_DET_DISABLE); + /* recover auto break config */ + if (brk_en_default) + aw869xx_auto_break_mode(aw_haptic, true); + else + aw869xx_auto_break_mode(aw_haptic, false); + return ret; +} + +static uint8_t aw869xx_rtp_get_fifo_afs(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_SYSST, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= AW869XX_BIT_SYSST_FF_AFS; + reg_val >>= 3; + return reg_val; +} + +static uint8_t aw869xx_rtp_get_fifo_aes(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_SYSST, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= AW869XX_BIT_SYSST_FF_AES; + reg_val >>= 4; + return reg_val; +} + +static uint8_t aw869xx_osc_read_status(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_SYSST2, ®_val, + AW_I2C_BYTE_ONE); + reg_val &= AW869XX_BIT_SYSST2_FF_EMPTY; + return reg_val; +} + +static void aw869xx_get_lra_resistance(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t lra_code = 0; + + aw869xx_stop(aw_haptic); + + aw869xx_ram_init(aw_haptic, true); + /* enter standby mode */ + aw869xx_stop(aw_haptic); + usleep_range(2000, 2500); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_STANDBY_MASK, + AW869XX_BIT_SYSCTRL2_STANDBY_OFF); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_DETCFG1, + AW869XX_BIT_DETCFG1_RL_OS_MASK, + AW869XX_BIT_DETCFG1_RL); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_DETCFG2, + AW869XX_BIT_DETCFG2_DIAG_GO_MASK, + AW869XX_BIT_DETCFG2_DIAG_GO_ON); + usleep_range(30000, 35000); + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_DET_RL, ®_val, + AW_I2C_BYTE_ONE); + lra_code = (lra_code | reg_val) << 2; + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_DET_LO, ®_val, + AW_I2C_BYTE_ONE); + lra_code = lra_code | (reg_val & 0x03); + aw_haptic->lra = AW869XX_LRA_FORMULA(lra_code); + aw869xx_ram_init(aw_haptic, false); +} + +static void aw869xx_set_repeat_seq(struct aw_haptic *aw_haptic, uint8_t seq) +{ + aw869xx_set_wav_seq(aw_haptic, 0x00, seq); + aw869xx_set_wav_loop(aw_haptic, 0x00, AW869XX_BIT_WAVLOOP_INIFINITELY); +} + +static void aw869xx_get_vbat(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t vbat_code = 0; + uint32_t cont = 2000; + + aw869xx_stop(aw_haptic); + aw869xx_ram_init(aw_haptic, true); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_DETCFG2, + AW869XX_BIT_DETCFG2_VBAT_GO_MASK, + AW869XX_BIT_DETCFG2_VABT_GO_ON); + + while (cont--) { + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_DETCFG2, ®_val, + AW_I2C_BYTE_ONE); + if ((reg_val & 0x02) == 0 || cont == 0) + break; + } + + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_DET_VBAT, ®_val, + AW_I2C_BYTE_ONE); + vbat_code = (vbat_code | reg_val) << 2; + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_DET_LO, ®_val, + AW_I2C_BYTE_ONE); + vbat_code = vbat_code | ((reg_val & 0x30) >> 4); + aw_haptic->vbat = AW869XX_VBAT_FORMULA(vbat_code); + if (aw_haptic->vbat > AW_VBAT_MAX) { + aw_haptic->vbat = AW_VBAT_MAX; + aw_info("vbat max limit = %d", aw_haptic->vbat); + } + if (aw_haptic->vbat < AW_VBAT_MIN) { + aw_haptic->vbat = AW_VBAT_MIN; + aw_info("vbat min limit = %d", aw_haptic->vbat); + } + aw_info("aw_haptic->vbat=%dmV, vbat_code=0x%02X", + aw_haptic->vbat, vbat_code); + aw869xx_ram_init(aw_haptic, false); +} + +static ssize_t aw869xx_get_reg(struct aw_haptic *aw_haptic, ssize_t len, + char *buf) +{ + uint8_t i = 0; + uint8_t reg_array[AW869XX_REG_D2SCFG1 + 1] = {0}; + + for (i = 0; i < AW869XX_REG_RTPDATA; i++) + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_ID, reg_array, + AW869XX_REG_RTPDATA); + for (i = AW869XX_REG_RTPDATA + 1; i < AW869XX_REG_RAMDATA; i++) + haptic_hv_i2c_reads(aw_haptic, (AW869XX_REG_RTPDATA + 1), + ®_array[AW869XX_REG_RTPDATA + 1], + (AW869XX_REG_RAMDATA - AW869XX_REG_RTPDATA - 1)); + for (i = AW869XX_REG_RAMDATA + 1; i <= AW869XX_REG_D2SCFG1; i++) + haptic_hv_i2c_reads(aw_haptic, (AW869XX_REG_RAMDATA + 1), + ®_array[AW869XX_REG_RAMDATA + 1], + (AW869XX_REG_D2SCFG1 - AW869XX_REG_RAMDATA - 1)); + for (i = 0; i <= AW869XX_REG_D2SCFG1; i++) + if ((i != AW869XX_REG_RTPDATA) && (i != AW869XX_REG_RAMDATA)) + len += snprintf(buf + len, PAGE_SIZE - len, + "reg:0x%02X=0x%02X\n", i, reg_array[i]); + return len; +} + +static void aw869xx_offset_cali(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t cnt = 2000; + + aw869xx_ram_init(aw_haptic, true); + + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_DETCFG2, + AW869XX_BIT_DETCFG2_DIAG_GO_MASK, + AW869XX_BIT_DETCFG2_DIAG_GO_ON); + while (cnt--) { + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_DETCFG2, ®_val, + AW_I2C_BYTE_ONE); + if ((reg_val & 0x01) == 0) { + aw869xx_ram_init(aw_haptic, false); + return; + } + } + aw_err("calibration offset failed!"); + aw869xx_ram_init(aw_haptic, false); +} + +static void aw869xx_trig_init(struct aw_haptic *aw_haptic) +{ + aw_info("enter!"); + /* i2s config */ + if (aw_haptic->info.is_enabled_i2s && aw_haptic->i2s_config) { + aw_info("i2s is enabled!"); + aw869xx_i2s_init(aw_haptic); + } else { + aw869xx_trig2_param_init(aw_haptic); + aw869xx_trig3_param_init(aw_haptic); + aw869xx_trig2_param_config(aw_haptic); + aw869xx_trig3_param_config(aw_haptic); + } + /* one wire config */ + if (aw_haptic->info.is_enabled_one_wire) { + aw_info("one wire is enabled!"); + aw869xx_one_wire_init(aw_haptic); + } else { + aw869xx_trig1_param_init(aw_haptic); + aw869xx_trig1_param_config(aw_haptic); + } +} + +#ifdef AW_CHECK_RAM_DATA +static int aw869xx_check_ram_data(struct aw_haptic *aw_haptic, + uint8_t *cont_data, + uint8_t *ram_data, uint32_t len) +{ + int i = 0; + + for (i = 0; i < len; i++) { + if (ram_data[i] != cont_data[i]) { + aw_err("check ramdata error, addr=0x%04x, ram_data=0x%02x, file_data=0x%02x", + i, ram_data[i], cont_data[i]); + return -ERANGE; + } + } + return 0; +} +#endif + +static int aw869xx_container_update(struct aw_haptic *aw_haptic, + struct aw_haptic_container *awinic_cont) +{ + uint8_t reg_val[3] = {0}; + uint8_t fifo_adr[4] = {0}; + uint32_t temp = 0; + uint32_t shift = 0; + int i = 0; + int ret = 0; + int len = 0; + +#ifdef AW_CHECK_RAM_DATA + uint8_t ram_data[AW_RAMDATA_RD_BUFFER_SIZE] = {0}; +#endif + + mutex_lock(&aw_haptic->lock); + aw_haptic->ram.baseaddr_shift = 2; + aw_haptic->ram.ram_shift = 4; + /* RAMINIT Enable */ + aw869xx_ram_init(aw_haptic, true); + /* Enter standby mode */ + aw869xx_stop(aw_haptic); + /* base addr */ + shift = aw_haptic->ram.baseaddr_shift; + aw_haptic->ram.base_addr = + (uint32_t)((awinic_cont->data[0 + shift] << 8) | + (awinic_cont->data[1 + shift])); + /* fifo_ael */ + fifo_adr[0] = (uint8_t)AW869XX_FIFO_AE_ADDR_L(aw_haptic->ram.base_addr); + /* fifo_afl */ + fifo_adr[1] = (uint8_t)AW869XX_FIFO_AF_ADDR_L(aw_haptic->ram.base_addr); + /* fifo_aeh */ + fifo_adr[2] = (uint8_t)AW869XX_FIFO_AE_ADDR_H(aw_haptic->ram.base_addr); + /* fifo_afh */ + fifo_adr[3] = (uint8_t)AW869XX_FIFO_AF_ADDR_H(aw_haptic->ram.base_addr); + + aw_info("base_addr = %d", aw_haptic->ram.base_addr); + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_RTPCFG1, &awinic_cont->data[shift], + AW_I2C_BYTE_TWO); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_RTPCFG3, + (AW869XX_BIT_RTPCFG3_FIFO_AEH_MASK & + AW869XX_BIT_RTPCFG3_FIFO_AFH_MASK), + (fifo_adr[2] | fifo_adr[3])); + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_RTPCFG4, fifo_adr, + AW_I2C_BYTE_TWO); + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_RTPCFG3, reg_val, + AW_I2C_BYTE_THREE); + temp = ((reg_val[0] & 0x0f) << 24) | ((reg_val[0] & 0xf0) << 4); + temp = temp | reg_val[1]; + aw_info("almost_empty_threshold = %d", (uint16_t)temp); + temp = temp | (reg_val[2] << 16); + aw_info("almost_full_threshold = %d", temp >> 16); + /* ram */ + shift = aw_haptic->ram.baseaddr_shift; + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_RAMADDRH, + &awinic_cont->data[shift], AW_I2C_BYTE_TWO); + i = aw_haptic->ram.ram_shift; + while (i < awinic_cont->len) { + if ((awinic_cont->len - i) < AW_RAMDATA_WR_BUFFER_SIZE) + len = awinic_cont->len - i; + else + len = AW_RAMDATA_WR_BUFFER_SIZE; + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_RAMDATA, + &awinic_cont->data[i], len); + i += len; + } +#ifdef AW_CHECK_RAM_DATA + shift = aw_haptic->ram.baseaddr_shift; + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_RAMADDRH, + &awinic_cont->data[shift], AW_I2C_BYTE_TWO); + i = aw_haptic->ram.ram_shift; + while (i < awinic_cont->len) { + if ((awinic_cont->len - i) < AW_RAMDATA_RD_BUFFER_SIZE) + len = awinic_cont->len - i; + else + len = AW_RAMDATA_RD_BUFFER_SIZE; + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_RAMDATA, + ram_data, len); + ret = aw869xx_check_ram_data(aw_haptic, &awinic_cont->data[i], + ram_data, len); + if (ret < 0) + break; + i += len; + } + if (ret) + aw_err("ram data check sum error"); + else + aw_info("ram data check sum pass"); + +#endif + /* RAMINIT Disable */ + aw869xx_ram_init(aw_haptic, false); + mutex_unlock(&aw_haptic->lock); + return ret; +} + +static uint64_t aw869xx_get_theory_time(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val = 0; + uint32_t fre_val = 0; + uint64_t theory_time = 0; + + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_SYSCTRL2, ®_val, + AW_I2C_BYTE_ONE); + fre_val = (reg_val & 0x03) >> 0; + + if (fre_val == 2 || fre_val == 3) + theory_time = (aw_haptic->rtp_len / 12000) * 1000000; /*12K */ + if (fre_val == 0) + theory_time = (aw_haptic->rtp_len / 24000) * 1000000; /*24K */ + if (fre_val == 1) + theory_time = (aw_haptic->rtp_len / 48000) * 1000000; /*48K */ + + aw_info("microsecond:%llu theory_time = %llu", aw_haptic->microsecond, + theory_time); + + return theory_time; +} + +static void aw869xx_parse_dt(struct device *dev, struct aw_haptic *aw_haptic, + struct device_node *np) +{ + uint8_t duration_time[3]; + uint8_t sine_array_temp[4]; + uint8_t bstcfg_temp[5]; + uint8_t trig_config_temp[24]; + uint32_t val = 0; + + val = of_property_read_u8(np, "aw869xx_gain_bypass", + &aw_haptic->info.gain_bypass); + if (val != 0) + aw_info("aw869xx_gain_bypass not found"); + val = of_property_read_u8(np, "aw869xx_brk_bst_md", + &aw_haptic->info.brk_bst_md); + if (val != 0) + aw_info("aw869xx_brk_bst_md not found"); + val = of_property_read_u32(np, "f0_pre", &aw_haptic->info.f0_pre); + if (val != 0) + aw_info("f0_pre not found"); + val = of_property_read_u8(np, "f0_cali_percent", + &aw_haptic->info.f0_cali_percent); + if (val != 0) + aw_info("f0_cali_percent not found"); + val = of_property_read_u8(np, "aw869xx_cont_drv1_lvl", + &aw_haptic->info.cont_drv1_lvl); + if (val != 0) + aw_info("aw869xx_cont_drv1_lvl not found"); + val = of_property_read_u32(np, "aw869xx_cont_lra_vrms", + &aw_haptic->info.cont_lra_vrms); + if (val != 0) + aw_info("aw869xx_cont_lra_vrms not found"); + val = of_property_read_u8(np, "aw869xx_cont_drv1_time", + &aw_haptic->info.cont_drv1_time); + if (val != 0) + aw_info("aw869xx_cont_drv1_time not found"); + val = of_property_read_u8(np, "aw869xx_cont_drv2_time", + &aw_haptic->info.cont_drv2_time); + if (val != 0) + aw_info("aw869xx_cont_drv2_time not found"); + val = of_property_read_u8(np, "aw869xx_cont_wait_num", + &aw_haptic->info.cont_wait_num); + if (val != 0) + aw_info("aw869xx_cont_wait_num not found"); + val = of_property_read_u8(np, "aw869xx_cont_bst_brk_gain", + &aw_haptic->info.cont_bst_brk_gain); + if (val != 0) + aw_info("aw869xx_cont_bst_brk_gain not found"); + val = of_property_read_u8(np, "aw869xx_cont_brk_gain", + &aw_haptic->info.cont_brk_gain); + if (val != 0) + aw_info("aw869xx_cont_brk_gain not found"); + val = of_property_read_u8(np, "aw869xx_cont_tset", + &aw_haptic->info.cont_tset); + if (val != 0) + aw_info("aw869xx_cont_tset not found"); + val = of_property_read_u8(np, "aw869xx_cont_bemf_set", + &aw_haptic->info.cont_bemf_set); + if (val != 0) + aw_info("aw869xx_cont_bemf_set not found"); + val = of_property_read_u8(np, "aw869xx_d2s_gain", + &aw_haptic->info.d2s_gain); + if (val != 0) + aw_info("aw869xx_d2s_gain not found"); + val = of_property_read_u8(np, "aw869xx_cont_brk_time", + &aw_haptic->info.cont_brk_time); + if (val != 0) + aw_info("aw869xx_cont_brk_time not found"); + val = of_property_read_u8(np, "aw869xx_cont_track_margin", + &aw_haptic->info.cont_track_margin); + if (val != 0) + aw_info("aw869xx_cont_track_margin not found"); + aw_haptic->info.is_enabled_auto_bst = + of_property_read_bool(np, "aw869xx_is_enabled_auto_bst"); + aw_info("aw_haptic->info.is_enabled_auto_bst = %d", + aw_haptic->info.is_enabled_auto_bst); + aw_haptic->info.is_enabled_i2s = + of_property_read_bool(np, "aw869xx_is_enabled_i2s"); + aw_info("aw_haptic->info.is_enabled_i2s = %d", + aw_haptic->info.is_enabled_i2s); + aw_haptic->info.is_enabled_one_wire = of_property_read_bool(np, + "aw869xx_is_enabled_one_wire"); + aw_info("aw_haptic->info.is_enabled_one_wire = %d", + aw_haptic->info.is_enabled_one_wire); + val = of_property_read_u32(np, "aw869xx_bst_vol_default", + &aw_haptic->info.bst_vol_default); + if (val != 0) + aw_info("aw869xx_bst_vol_default not found"); + val = of_property_read_u8_array(np, "aw869xx_duration_time", + duration_time, + ARRAY_SIZE(duration_time)); + if (val != 0) + aw_info("aw869xx_duration_time not found"); + else + memcpy(aw_haptic->info.duration_time, duration_time, + sizeof(duration_time)); + val = of_property_read_u8_array(np, "aw869xx_bstcfg", bstcfg_temp, + ARRAY_SIZE(bstcfg_temp)); + if (val != 0) + aw_info("aw869xx_bstcfg not found"); + else + memcpy(aw_haptic->info.bstcfg, bstcfg_temp, + sizeof(bstcfg_temp)); + val = of_property_read_u8_array(np, "aw869xx_sine_array", + sine_array_temp, ARRAY_SIZE(sine_array_temp)); + if (val != 0) + aw_info("aw869xx_sine_array not found"); + else + memcpy(aw_haptic->info.sine_array, sine_array_temp, + sizeof(sine_array_temp)); + val = of_property_read_u8_array(np, "aw869xx_trig_config", + trig_config_temp, + ARRAY_SIZE(trig_config_temp)); + if (val != 0) + aw_info("aw869xx_trig_config not found"); + else + memcpy(aw_haptic->info.trig_cfg, trig_config_temp, + sizeof(trig_config_temp)); + aw_dbg("aw_haptic->info.brk_bst_md: %d", aw_haptic->info.brk_bst_md); + aw_dbg("aw_haptic->info.bst_vol_default: %d", + aw_haptic->info.bst_vol_default); +} + +static void aw869xx_misc_para_init(struct aw_haptic *aw_haptic) +{ + uint8_t reg_val[8] = {0}; + + /* Cont drv2 lvl */ + aw_haptic->info.cont_drv2_lvl = AW869XX_DRV2_LVL_FARMULA( + aw_haptic->info.cont_lra_vrms); + if (aw_haptic->info.cont_drv2_lvl > AW_DRV2_LVL_MAX) + aw_haptic->info.cont_drv2_lvl = AW_DRV2_LVL_MAX; + /* Get vmax */ + if (aw_haptic->info.bst_vol_default > 0) + aw_haptic->vmax = aw_haptic->info.bst_vol_default; + /* Get gain */ + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_PLAYCFG2, reg_val, + AW_I2C_BYTE_ONE); + aw_haptic->gain = reg_val[0]; + /* Get wave_seq */ + haptic_hv_i2c_reads(aw_haptic, AW869XX_REG_WAVCFG1, reg_val, + AW_I2C_BYTE_EIGHT); + aw_haptic->index = reg_val[0]; + memcpy(aw_haptic->seq, reg_val, AW_SEQUENCER_SIZE); + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_BSTCFG1, + aw_haptic->info.bstcfg, AW_I2C_BYTE_FIVE); + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_SYSCTRL3, + aw_haptic->info.sine_array, AW_I2C_BYTE_FOUR); + /* Set gain_bypass */ + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSCTRL7, + AW869XX_BIT_SYSCTRL7_GAIN_BYPASS_MASK, + aw_haptic->info.gain_bypass << 6); + + /* brk_bst_md */ + if (!aw_haptic->info.brk_bst_md) + aw_err("aw_haptic->info.brk_bst_md = 0!"); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_CONTCFG1, + AW869XX_BIT_CONTCFG1_BRK_BST_MD_MASK, + aw_haptic->info.brk_bst_md << 1); + /* d2s_gain */ + if (!aw_haptic->info.d2s_gain) + aw_err("aw_haptic->info.d2s_gain = 0!"); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSCTRL7, + AW869XX_BIT_SYSCTRL7_D2S_GAIN_MASK, + aw_haptic->info.d2s_gain); + /* cont_tset */ + if (!aw_haptic->info.cont_tset) + aw_err("aw_haptic->info.cont_tset = 0!"); + /* cont_bemf_set */ + if (!aw_haptic->info.cont_bemf_set) + aw_err("aw_haptic->info.cont_bemf_set = 0!"); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_CONTCFG13, + (AW869XX_BIT_CONTCFG13_TSET_MASK & + AW869XX_BIT_CONTCFG13_BEME_SET_MASK), + ((aw_haptic->info.cont_tset << 4) | + aw_haptic->info.cont_bemf_set)); + /* cont_brk_time */ + if (!aw_haptic->info.cont_brk_time) + aw_err("aw_haptic->info.cont_brk_time = 0!"); + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_CONTCFG10, + &aw_haptic->info.cont_brk_time, AW_I2C_BYTE_ONE); + /* cont_bst_brk_gain */ + if (!aw_haptic->info.cont_bst_brk_gain) + aw_err("aw_haptic->info.cont_bst_brk_gain = 0!"); + /* cont_brk_gain */ + if (!aw_haptic->info.cont_brk_gain) + aw_err("aw_haptic->info.cont_brk_gain = 0!"); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_CONTCFG5, + (AW869XX_BIT_CONTCFG5_BST_BRK_GAIN_MASK & + AW869XX_BIT_CONTCFG5_BRK_GAIN_MASK), + ((aw_haptic->info.cont_bst_brk_gain << 4) | + aw_haptic->info.cont_brk_gain)); + /* i2s enbale */ + if (aw_haptic->info.is_enabled_i2s && aw_haptic->i2s_config) { + aw_info("i2s enabled!"); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_I2S_PIN_MASK, + AW869XX_BIT_SYSCTRL2_I2S_PIN_I2S); + } else { + aw_info("i2s disabled!"); + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_SYSCTRL2, + AW869XX_BIT_SYSCTRL2_I2S_PIN_MASK, + AW869XX_BIT_SYSCTRL2_I2S_PIN_TRIG); + } + /* set BST_ADJ */ + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_BSTCFG5, + AW869XX_BIT_BSTCFG5_BST_ADJ_MASK, + AW869XX_BIT_BSTCFG5_BST_ADJ_LOW); +} + +static ssize_t aw869xx_cont_wait_num_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_wait_num = 0x%02X\n", + aw_haptic->info.cont_wait_num); + return len; +} + +static ssize_t aw869xx_cont_wait_num_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + rc = kstrtou8(buf, 0, &val); + if (rc < 0) + return rc; + aw_haptic->info.cont_wait_num = val; + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_CONTCFG4, &val, + AW_I2C_BYTE_ONE); + + return count; +} + +static ssize_t aw869xx_cont_drv_lvl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_lvl = 0x%02X, cont_drv2_lvl = 0x%02X\n", + aw_haptic->info.cont_drv1_lvl, + aw_haptic->info.cont_drv2_lvl); + return len; +} + +static ssize_t aw869xx_cont_drv_lvl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t databuf[2] = { 0, 0 }; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_haptic->info.cont_drv1_lvl = databuf[0]; + aw_haptic->info.cont_drv2_lvl = databuf[1]; + haptic_hv_i2c_write_bits(aw_haptic, AW869XX_REG_CONTCFG6, + AW869XX_BIT_CONTCFG6_DRV1_LVL_MASK, + aw_haptic->info.cont_drv1_lvl); + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_CONTCFG7, + &aw_haptic->info.cont_drv2_lvl, AW_I2C_BYTE_ONE); + } + return count; +} + +static ssize_t aw869xx_cont_drv_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, + "cont_drv1_time = 0x%02X, cont_drv2_time = 0x%02X\n", + aw_haptic->info.cont_drv1_time, + aw_haptic->info.cont_drv2_time); + return len; +} + +static ssize_t aw869xx_cont_drv_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t cont_time[2] = {0}; + uint32_t databuf[2] = { 0, 0 }; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + cont_time[0] = aw_haptic->info.cont_drv1_time = databuf[0]; + cont_time[1] = aw_haptic->info.cont_drv2_time = databuf[1]; + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_CONTCFG8, cont_time, + AW_I2C_BYTE_TWO); + } + return count; +} + +static ssize_t aw869xx_cont_brk_time_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "cont_brk_time = 0x%02X\n", + aw_haptic->info.cont_brk_time); + return len; +} + +static ssize_t aw869xx_cont_brk_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + rc = kstrtou8(buf, 0, &aw_haptic->info.cont_brk_time); + if (rc < 0) + return rc; + haptic_hv_i2c_writes(aw_haptic, AW869XX_REG_CONTCFG10, + &aw_haptic->info.cont_brk_time, AW_I2C_BYTE_ONE); + return count; +} + +static ssize_t aw869xx_trig_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + uint8_t i = 0; + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + for (i = 0; i < AW_TRIG_NUM; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "trig%d: trig_level=%d, trig_polar=%d, pos_enable=%d, pos_sequence=%d, neg_enable=%d, neg_sequence=%d trig_brk=%d, trig_bst=%d\n", + i + 1, + aw_haptic->trig[i].trig_level, + aw_haptic->trig[i].trig_polar, + aw_haptic->trig[i].pos_enable, + aw_haptic->trig[i].pos_sequence, + aw_haptic->trig[i].neg_enable, + aw_haptic->trig[i].neg_sequence, + aw_haptic->trig[i].trig_brk, + aw_haptic->trig[i].trig_bst); + } + return len; +} + +static ssize_t aw869xx_trig_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t databuf[9] = { 0 }; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (sscanf(buf, "%d %d %d %d %d %d %d %d %d", &databuf[0], &databuf[1], + &databuf[2], &databuf[3], &databuf[4], &databuf[5], + &databuf[6], &databuf[7], &databuf[8]) == 9) { + aw_info("%d, %d, %d, %d, %d, %d, %d, %d, %d", + databuf[0], databuf[1], databuf[2], databuf[3], + databuf[4], databuf[5], databuf[6], databuf[7], + databuf[8]); + if (databuf[0] < 1 || databuf[0] > 3) { + aw_info("input trig_num out of range!"); + return count; + } + if (databuf[0] == 1 && aw_haptic->info.is_enabled_one_wire) { + aw_info("trig1 pin used for one wire!"); + return count; + } + + if ((databuf[0] == 2 || databuf[0] == 3) && + aw_haptic->info.is_enabled_i2s && aw_haptic->i2s_config) { + aw_info("trig2 and trig3 pin used for i2s!"); + return count; + } + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + if (databuf[4] > aw_haptic->ram.ram_num || + databuf[6] > aw_haptic->ram.ram_num) { + aw_err("input seq value out of range!"); + return count; + } + databuf[0] -= 1; + + aw_haptic->trig[databuf[0]].trig_level = databuf[1]; + aw_haptic->trig[databuf[0]].trig_polar = databuf[2]; + aw_haptic->trig[databuf[0]].pos_enable = databuf[3]; + aw_haptic->trig[databuf[0]].pos_sequence = databuf[4]; + aw_haptic->trig[databuf[0]].neg_enable = databuf[5]; + aw_haptic->trig[databuf[0]].neg_sequence = databuf[6]; + aw_haptic->trig[databuf[0]].trig_brk = databuf[7]; + aw_haptic->trig[databuf[0]].trig_bst = databuf[8]; + mutex_lock(&aw_haptic->lock); + switch (databuf[0]) { + case 0: + aw869xx_trig1_param_config(aw_haptic); + break; + case 1: + aw869xx_trig2_param_config(aw_haptic); + break; + case 2: + aw869xx_trig3_param_config(aw_haptic); + break; + } + mutex_unlock(&aw_haptic->lock); + } + return count; +} + +static DEVICE_ATTR(cont_wait_num, S_IWUSR | S_IRUGO, aw869xx_cont_wait_num_show, + aw869xx_cont_wait_num_store); +static DEVICE_ATTR(cont_drv_lvl, S_IWUSR | S_IRUGO, aw869xx_cont_drv_lvl_show, + aw869xx_cont_drv_lvl_store); +static DEVICE_ATTR(cont_drv_time, S_IWUSR | S_IRUGO, aw869xx_cont_drv_time_show, + aw869xx_cont_drv_time_store); +static DEVICE_ATTR(cont_brk_time, S_IWUSR | S_IRUGO, aw869xx_cont_brk_time_show, + aw869xx_cont_brk_time_store); +static DEVICE_ATTR(trig, S_IWUSR | S_IRUGO, aw869xx_trig_show, + aw869xx_trig_store); + +static struct attribute *aw869xx_vibrator_attributes[] = { + &dev_attr_trig.attr, + &dev_attr_cont_wait_num.attr, + &dev_attr_cont_drv_lvl.attr, + &dev_attr_cont_drv_time.attr, + &dev_attr_cont_brk_time.attr, + NULL +}; + +static struct attribute_group aw869xx_vibrator_attribute_group = { + .attrs = aw869xx_vibrator_attributes +}; + +static int aw869xx_creat_node(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + ret = sysfs_create_group(&aw_haptic->vib_dev.dev->kobj, + &aw869xx_vibrator_attribute_group); + if (ret < 0) + aw_err("error creating sysfs attr files"); + return ret; +} + +struct aw_haptic_func aw869xx_func_list = { + .play_stop = aw869xx_stop, + .ram_init = aw869xx_ram_init, + .get_vbat = aw869xx_get_vbat, + .creat_node = aw869xx_creat_node, + .get_f0 = aw869xx_get_f0, + .ram_get_f0 = aw869xx_ram_get_f0, + .cont_config = aw869xx_cont_config, + .offset_cali = aw869xx_offset_cali, + .check_qualify = aw869xx_check_qualify, + .get_irq_state = aw869xx_get_irq_state, + .judge_rtp_going = aw869xx_judge_rtp_going, + .set_bst_peak_cur = aw869xx_set_bst_peak_cur, + .get_theory_time = aw869xx_get_theory_time, + .get_lra_resistance = aw869xx_get_lra_resistance, + .set_pwm = aw869xx_set_pwm, + .play_mode = aw869xx_play_mode, + .set_bst_vol = aw869xx_set_bst_vol, + .interrupt_setup = aw869xx_interrupt_setup, + .set_repeat_seq = aw869xx_set_repeat_seq, + .auto_bst_enable = aw869xx_auto_bst_enable, + .vbat_mode_config = aw869xx_vbat_mode_config, + .set_wav_seq = aw869xx_set_wav_seq, + .set_wav_loop = aw869xx_set_wav_loop, + .set_ram_addr = aw869xx_set_ram_addr, + .set_rtp_data = aw869xx_set_rtp_data, + .container_update = aw869xx_container_update, + .protect_config = aw869xx_protect_config, + .parse_dt = aw869xx_parse_dt, + .trig_init = aw869xx_trig_init, + .irq_clear = aw869xx_irq_clear, + .get_wav_loop = aw869xx_get_wav_loop, + .play_go = aw869xx_play_go, + .misc_para_init = aw869xx_misc_para_init, + .set_rtp_aei = aw869xx_set_rtp_aei, + .set_gain = aw869xx_set_gain, + .upload_lra = aw869xx_upload_lra, + .bst_mode_config = aw869xx_bst_mode_config, + .get_reg = aw869xx_get_reg, + .get_prctmode = aw869xx_get_prctmode, + .get_trim_lra = aw869xx_get_trim_lra, + .get_ram_data = aw869xx_get_ram_data, + .get_first_wave_addr = aw869xx_get_first_wave_addr, + .get_glb_state = aw869xx_get_glb_state, + .get_osc_status = aw869xx_osc_read_status, + .rtp_get_fifo_afs = aw869xx_rtp_get_fifo_afs, + .rtp_get_fifo_aes = aw869xx_rtp_get_fifo_aes, + .get_wav_seq = aw869xx_get_wav_seq, +#ifdef AW_SND_SOC_CODEC + .snd_soc_init = aw869xx_snd_soc_init, +#endif +}; diff --git a/drivers/misc/awinic/haptic_hv/haptic_hv.c b/drivers/misc/awinic/haptic_hv/haptic_hv.c new file mode 100644 index 000000000000..7d492a367624 --- /dev/null +++ b/drivers/misc/awinic/haptic_hv/haptic_hv.c @@ -0,0 +1,3835 @@ +/* + * File: haptic_hv.c + * + * Author: Ethan + * + * Copyright (c) 2021 AWINIC Technology CO., 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "haptic_hv.h" +#include "haptic_hv_reg.h" + +#define HAPTIC_HV_DRIVER_VERSION "v1.1.0" + +char *aw_ram_name = "haptic_ram.bin"; +char aw_rtp_name[][AW_RTP_NAME_MAX] = { + {"haptic_rtp_osc_24K_5s.bin"}, + {"haptic_rtp.bin"}, + {"haptic_rtp_lighthouse.bin"}, + {"haptic_rtp_silk.bin"}, +}; + +#ifdef AW_TIKTAP +static struct aw_haptic *g_aw_haptic; +#endif +#ifdef AW_DOUBLE +struct aw_haptic *left; +struct aw_haptic *right; +#endif + +/********************************************************* + * + * I2C Read/Write + * + *********************************************************/ +int haptic_hv_i2c_reads(struct aw_haptic *aw_haptic, uint8_t reg_addr, + uint8_t *buf, uint32_t len) +{ + int ret; + struct i2c_msg msg[] = { + [0] = { + .addr = aw_haptic->i2c->addr, + .flags = 0, + .len = sizeof(uint8_t), + .buf = ®_addr, + }, + [1] = { + .addr = aw_haptic->i2c->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + }, + }; + + ret = i2c_transfer(aw_haptic->i2c->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) { + aw_err("transfer failed."); + return ret; + } else if (ret != AW_I2C_READ_MSG_NUM) { + aw_err("transfer failed(size error)."); + return -ENXIO; + } + + return ret; +} + +int haptic_hv_i2c_writes(struct aw_haptic *aw_haptic, uint8_t reg_addr, + uint8_t *buf, uint32_t len) +{ + uint8_t *data = NULL; + int ret = -1; + + data = kmalloc(len + 1, GFP_KERNEL); + data[0] = reg_addr; + memcpy(&data[1], buf, len); + ret = i2c_master_send(aw_haptic->i2c, data, len + 1); + if (ret < 0) + aw_err("i2c master send 0x%02x err", reg_addr); + kfree(data); + return ret; +} + +int haptic_hv_i2c_write_bits(struct aw_haptic *aw_haptic, uint8_t reg_addr, + uint32_t mask, uint8_t reg_data) +{ + uint8_t reg_val = 0; + int ret = -1; + + ret = haptic_hv_i2c_reads(aw_haptic, reg_addr, ®_val, + AW_I2C_BYTE_ONE); + if (ret < 0) { + aw_err("i2c read error, ret=%d", ret); + return ret; + } + reg_val &= mask; + reg_val |= (reg_data & (~mask)); + ret = haptic_hv_i2c_writes(aw_haptic, reg_addr, ®_val, + AW_I2C_BYTE_ONE); + if (ret < 0) { + aw_err("i2c write error, ret=%d", ret); + return ret; + } + return 0; +} + +static int parse_dt_gpio(struct device *dev, struct aw_haptic *aw_haptic, + struct device_node *np) +{ + int val = 0; + + aw_haptic->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (aw_haptic->reset_gpio < 0) { + aw_err("no reset gpio provide"); + return -EPERM; + } + aw_info("reset gpio provide ok %d", aw_haptic->reset_gpio); + aw_haptic->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (aw_haptic->irq_gpio < 0) + aw_err("no irq gpio provided."); + else + aw_info("irq gpio provide ok irq = %d.", aw_haptic->irq_gpio); + val = of_property_read_u8(np, "mode", &aw_haptic->info.mode); + if (val != 0) + aw_info("mode not found"); +#ifdef AW_DOUBLE + if (of_device_is_compatible(np, "awinic,haptic_hv_l")) { + aw_info("compatible left vibrator."); + memcpy(aw_haptic->name, "left", sizeof("left")); + left = NULL; + left = aw_haptic; + } else if (of_device_is_compatible(np, "awinic,haptic_hv_r")) { + aw_info("compatible right vibrator."); + memcpy(aw_haptic->name, "right", sizeof("right")); + right = NULL; + right = aw_haptic; + } else { + aw_err("compatible failed."); + return -ERANGE; + } +#endif + + return 0; +} + +static void hw_reset(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + if (aw_haptic && gpio_is_valid(aw_haptic->reset_gpio)) { + gpio_set_value_cansleep(aw_haptic->reset_gpio, 0); + usleep_range(1000, 2000); + gpio_set_value_cansleep(aw_haptic->reset_gpio, 1); + usleep_range(8000, 8500); + } else { + aw_err("failed"); + } +} + +static void sw_reset(struct aw_haptic *aw_haptic) +{ + uint8_t reset = AW_BIT_RESET; + + aw_dbg("enter"); + haptic_hv_i2c_writes(aw_haptic, AW_REG_CHIPID, &reset, AW_I2C_BYTE_ONE); + usleep_range(3000, 3500); +} + +static int judge_value(uint8_t reg) +{ + int ret = 0; + + if (!reg) + return -ERANGE; + switch (reg) { + case AW86925_BIT_RSTCFG_PRE_VAL: + case AW86926_BIT_RSTCFG_PRE_VAL: + case AW86927_BIT_RSTCFG_PRE_VAL: + case AW86928_BIT_RSTCFG_PRE_VAL: + case AW86925_BIT_RSTCFG_VAL: + case AW86926_BIT_RSTCFG_VAL: + case AW86927_BIT_RSTCFG_VAL: + case AW86928_BIT_RSTCFG_VAL: + ret = -ERANGE; + break; + default: + break; + } + return ret; +} + +static int read_chipid(struct aw_haptic *aw_haptic, uint32_t *reg_val) +{ + uint8_t value[2] = {0}; + int ret = 0; + + /* try the old way of read chip id */ + ret = haptic_hv_i2c_reads(aw_haptic, AW_REG_CHIPID, &value[0], + AW_I2C_BYTE_ONE); + if (ret < 0) + return ret; + + ret = judge_value(value[0]); + if (!ret) { + *reg_val = value[0]; + return ret; + } + /* try the new way of read chip id */ + ret = haptic_hv_i2c_reads(aw_haptic, AW_REG_CHIPIDH, value, + AW_I2C_BYTE_TWO); + if (ret < 0) + return ret; + *reg_val = value[0] << 8 | value[1]; + return ret; +} + +static int ctrl_init(struct aw_haptic *aw_haptic) +{ + uint32_t reg = 0; + uint8_t cnt = 0; + + aw_info("enter"); + for (cnt = 0; cnt < AW_READ_CHIPID_RETRIES; cnt++) { + /* hardware reset */ + hw_reset(aw_haptic); + if (read_chipid(aw_haptic, ®) < 0) + aw_err("read chip id fail"); + switch (reg) { +#ifdef AW869X_DRIVER_ENABLE + case AW8695_CHIPID: + case AW8697_CHIPID: + aw_haptic->func = &aw869x_func_list; + return 0; +#endif +#ifdef AW869XX_DRIVER_ENABLE + case AW86905_CHIPID: + case AW86907_CHIPID: + case AW86915_CHIPID: + case AW86917_CHIPID: + aw_haptic->func = &aw869xx_func_list; + return 0; +#endif +#ifdef AW8671X_DRIVER_ENABLE + case AW86715_CHIPID: + case AW86716_CHIPID: + case AW86717_CHIPID: + case AW86718_CHIPID: + aw_haptic->func = &aw8671x_func_list; + return 0; +#endif +#ifdef AW8692X_DRIVER_ENABLE + case AW86925_CHIPID: + case AW86926_CHIPID: + case AW86927_CHIPID: + case AW86928_CHIPID: + aw_haptic->func = &aw8692x_func_list; + return 0; +#endif + default: + aw_info("unexpected chipid!"); + break; + } + usleep_range(AW_READ_CHIPID_RETRY_DELAY * 1000, + AW_READ_CHIPID_RETRY_DELAY * 1000 + 500); + } + return -EINVAL; +} + +static int parse_chipid(struct aw_haptic *aw_haptic) +{ + int ret = -1; + uint32_t reg = 0; + uint8_t cnt = 0; + + for (cnt = 0; cnt < AW_READ_CHIPID_RETRIES; cnt++) { + ret = read_chipid(aw_haptic, ®); + if (ret < 0) + aw_err("read chip id fail: %d", ret); + switch (reg) { +#ifdef AW869X_DRIVER_ENABLE + case AW8695_CHIPID: + aw_haptic->chipid = AW8695_CHIPID; + aw_haptic->bst_pc = AW_BST_PC_L1; + aw_haptic->i2s_config = false; + aw_info("detected aw8695."); + return ret; + case AW8697_CHIPID: + aw_haptic->chipid = AW8697_CHIPID; + aw_haptic->bst_pc = AW_BST_PC_L2; + aw_haptic->i2s_config = false; + aw_info("detected aw8697."); + return ret; +#endif +#ifdef AW869XX_DRIVER_ENABLE + case AW86905_CHIPID: + aw_haptic->chipid = AW86905_CHIPID; + aw_haptic->bst_pc = AW_BST_PC_L1; + aw_haptic->i2s_config = false; + aw_info("detected aw86905."); + return 0; + case AW86907_CHIPID: + aw_haptic->chipid = AW86907_CHIPID; + aw_haptic->bst_pc = AW_BST_PC_L2; + aw_haptic->i2s_config = false; + aw_info("detected aw86907."); + return 0; + case AW86915_CHIPID: + aw_haptic->chipid = AW86915_CHIPID; + aw_haptic->bst_pc = AW_BST_PC_L1; + aw_haptic->i2s_config = true; + aw_info("detected aw86915."); + return 0; + case AW86917_CHIPID: + aw_haptic->chipid = AW86917_CHIPID; + aw_haptic->bst_pc = AW_BST_PC_L2; + aw_haptic->i2s_config = true; + aw_info("detected aw86917."); + return 0; +#endif +#ifdef AW8671X_DRIVER_ENABLE + case AW86715_CHIPID: + aw_haptic->chipid = AW86715_CHIPID; + aw_haptic->i2s_config = false; + aw_info("detected aw86715."); + return 0; + case AW86716_CHIPID: + aw_haptic->chipid = AW86716_CHIPID; + aw_haptic->i2s_config = true; + aw_info("detected aw86716."); + return 0; + case AW86717_CHIPID: + aw_haptic->chipid = AW86717_CHIPID; + aw_haptic->i2s_config = true; + aw_info("detected aw86717."); + return 0; + case AW86718_CHIPID: + aw_haptic->chipid = AW86718_CHIPID; + aw_haptic->i2s_config = true; + aw_info("detected aw86718."); + return 0; +#endif +#ifdef AW8692X_DRIVER_ENABLE + case AW86925_CHIPID: + aw_haptic->chipid = AW86925_CHIPID; + aw_haptic->bst_pc = AW_BST_PC_L1; + aw_info("detected aw86925."); + return 0; + case AW86926_CHIPID: + aw_haptic->chipid = AW86926_CHIPID; + aw_haptic->bst_pc = AW_BST_PC_L1; + aw_info("detected aw86926."); + return 0; + case AW86927_CHIPID: + aw_haptic->chipid = AW86927_CHIPID; + aw_haptic->bst_pc = AW_BST_PC_L1; + aw_info("detected aw86927."); + return 0; + case AW86928_CHIPID: + aw_haptic->chipid = AW86928_CHIPID; + aw_haptic->bst_pc = AW_BST_PC_L1; + aw_info("detected aw86928."); + return 0; +#endif + default: + aw_info("unsupport device revision (0x%02X)", reg); + break; + } + usleep_range(AW_READ_CHIPID_RETRY_DELAY * 1000, + AW_READ_CHIPID_RETRY_DELAY * 1000 + 500); + } + return -EINVAL; +} + +static void ram_play(struct aw_haptic *aw_haptic, uint8_t mode) +{ +#ifdef AW_DOUBLE + if (mode == AW_RAM_MODE) { + aw_haptic->func->set_wav_loop(aw_haptic, 0x00, 0x00); + } + aw_haptic->func->play_mode(aw_haptic, mode); + if (aw_haptic->dual_flag) { + aw_haptic->dual_flag = false; + if (down_trylock(&left->sema) == 0) { + down_interruptible(&left->sema); + } else { + up(&left->sema); + up(&left->sema); + } + } +#else + aw_haptic->func->play_mode(aw_haptic, mode); +#endif + aw_haptic->func->play_go(aw_haptic, true); +} + +static int get_ram_num(struct aw_haptic *aw_haptic) +{ + uint8_t wave_addr[2] = {0}; + uint32_t first_wave_addr = 0; + + if (!aw_haptic->ram_init) { + aw_err("ram init faild, ram_num = 0!"); + return -EPERM; + } + mutex_lock(&aw_haptic->lock); + /* RAMINIT Enable */ + aw_haptic->func->ram_init(aw_haptic, true); + aw_haptic->func->play_stop(aw_haptic); + aw_haptic->func->set_ram_addr(aw_haptic); + aw_haptic->func->get_first_wave_addr(aw_haptic, wave_addr); + first_wave_addr = (wave_addr[0] << 8 | wave_addr[1]); + aw_haptic->ram.ram_num = + (first_wave_addr - aw_haptic->ram.base_addr - 1) / 4; + aw_info("first wave addr = 0x%04x", first_wave_addr); + aw_info("ram_num = %d", aw_haptic->ram.ram_num); + /* RAMINIT Disable */ + aw_haptic->func->ram_init(aw_haptic, false); + mutex_unlock(&aw_haptic->lock); + return 0; +} + +static void ram_vbat_comp(struct aw_haptic *aw_haptic, bool flag) +{ + int temp_gain = 0; + + if (flag) { + if (aw_haptic->ram_vbat_comp == AW_RAM_VBAT_COMP_ENABLE) { + aw_haptic->func->get_vbat(aw_haptic); + temp_gain = aw_haptic->gain * AW_VBAT_REFER / + aw_haptic->vbat; + if (temp_gain > + (128 * AW_VBAT_REFER / AW_VBAT_MIN)) { + temp_gain = 128 * AW_VBAT_REFER / AW_VBAT_MIN; + aw_dbg("gain limit=%d", temp_gain); + } + aw_haptic->func->set_gain(aw_haptic, temp_gain); + aw_info("ram vbat comp open"); + } else { + aw_haptic->func->set_gain(aw_haptic, aw_haptic->gain); + aw_info("ram vbat comp close"); + } + } else { + aw_haptic->func->set_gain(aw_haptic, aw_haptic->gain); + aw_info("ram vbat comp close"); + } +} + +static int judge_cali_range(struct aw_haptic *aw_haptic) +{ + uint32_t f0_cali_min = 0; + uint32_t f0_cali_max = 0; + + f0_cali_min = aw_haptic->info.f0_pre * + (100 - aw_haptic->info.f0_cali_percent) / 100; + f0_cali_max = aw_haptic->info.f0_pre * + (100 + aw_haptic->info.f0_cali_percent) / 100; + + aw_info("f0_pre = %d, f0_cali_min = %d, f0_cali_max = %d, f0 = %d", + aw_haptic->info.f0_pre, f0_cali_min, f0_cali_max, + aw_haptic->f0); + + if ((aw_haptic->f0 < f0_cali_min) || (aw_haptic->f0 > f0_cali_max)) { + aw_err("f0 calibration out of range = %d!", aw_haptic->f0); + return -ERANGE; + } + return 0; +} + +static void calculate_cali_data(struct aw_haptic *aw_haptic) +{ + char f0_cali_lra = 0; + int f0_cali_step = 0; + + /* calculate cali step */ + f0_cali_step = 100000 * ((int)aw_haptic->f0 - + (int)aw_haptic->info.f0_pre) / + ((int)aw_haptic->f0 * AW_OSC_CALI_ACCURACY); + aw_info("f0_cali_step = %d", f0_cali_step); + if (f0_cali_step >= 0) { /*f0_cali_step >= 0 */ + if (f0_cali_step % 10 >= 5) + f0_cali_step = 32 + (f0_cali_step / 10 + 1); + else + f0_cali_step = 32 + f0_cali_step / 10; + } else { /* f0_cali_step < 0 */ + if (f0_cali_step % 10 <= -5) + f0_cali_step = 32 + (f0_cali_step / 10 - 1); + else + f0_cali_step = 32 + f0_cali_step / 10; + } + if (f0_cali_step > 31) + f0_cali_lra = (char)f0_cali_step - 32; + else + f0_cali_lra = (char)f0_cali_step + 32; + /* update cali step */ + aw_haptic->f0_cali_data = (int)f0_cali_lra; + aw_info("f0_cali_data = 0x%02X", aw_haptic->f0_cali_data); +} + +static int f0_cali(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + aw_haptic->func->upload_lra(aw_haptic, AW_WRITE_ZERO); + if (aw_haptic->func->get_f0(aw_haptic)) { + aw_err("get f0 error"); + } else { + ret = judge_cali_range(aw_haptic); + if (ret < 0) + return -ERANGE; + calculate_cali_data(aw_haptic); + aw_haptic->func->upload_lra(aw_haptic, AW_F0_CALI_LRA); + } + /* restore standby work mode */ + aw_haptic->func->play_stop(aw_haptic); + return ret; +} + +static int ram_f0_cali(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + aw_haptic->func->upload_lra(aw_haptic, AW_WRITE_ZERO); + if (aw_haptic->func->ram_get_f0(aw_haptic)) { + aw_err("get f0 error"); + } else { + ret = judge_cali_range(aw_haptic); + if (ret < 0) + return -ERANGE; + calculate_cali_data(aw_haptic); + aw_haptic->func->upload_lra(aw_haptic, AW_F0_CALI_LRA); + } + /* restore standby work mode */ + aw_haptic->func->play_stop(aw_haptic); + return ret; +} + +static void pm_qos_enable(struct aw_haptic *aw_haptic, bool enable) +{ +#ifdef KERNEL_OVER_5_10 + if (enable) { + if (!cpu_latency_qos_request_active(&aw_haptic->aw_pm_qos_req_vb)) + cpu_latency_qos_add_request(&aw_haptic->aw_pm_qos_req_vb, CPU_LATENCY_QOC_VALUE); + } else { + if (cpu_latency_qos_request_active(&aw_haptic->aw_pm_qos_req_vb)) + cpu_latency_qos_remove_request(&aw_haptic->aw_pm_qos_req_vb); + } +#else + if (enable) { + if (!pm_qos_request_active(&aw_haptic->aw_pm_qos_req_vb)) + pm_qos_add_request(&aw_haptic->aw_pm_qos_req_vb, + PM_QOS_CPU_DMA_LATENCY, + AW_PM_QOS_VALUE_VB); + } else { + if (pm_qos_request_active(&aw_haptic->aw_pm_qos_req_vb)) + pm_qos_remove_request(&aw_haptic->aw_pm_qos_req_vb); + } +#endif +} + +static int rtp_osc_cali(struct aw_haptic *aw_haptic) +{ + uint32_t buf_len = 0; + int ret = -1; + const struct firmware *rtp_file; + + aw_haptic->rtp_cnt = 0; + aw_haptic->timeval_flags = 1; + + /* fw loaded */ + ret = request_firmware(&rtp_file, aw_rtp_name[0], aw_haptic->dev); + if (ret < 0) { + aw_err("failed to read %s", aw_rtp_name[0]); + return ret; + } + /*aw_haptic add stop,for irq interrupt during calibrate */ + aw_haptic->func->play_stop(aw_haptic); + aw_haptic->rtp_init = false; + mutex_lock(&aw_haptic->rtp_lock); + vfree(aw_haptic->aw_rtp); + aw_haptic->aw_rtp = vmalloc(rtp_file->size + sizeof(int)); + if (!aw_haptic->aw_rtp) { + release_firmware(rtp_file); + mutex_unlock(&aw_haptic->rtp_lock); + aw_err("error allocating memory"); + return -ENOMEM; + } + aw_haptic->aw_rtp->len = rtp_file->size; + aw_haptic->rtp_len = rtp_file->size; + aw_info("rtp file:[%s] size = %dbytes", aw_rtp_name[0], + aw_haptic->aw_rtp->len); + memcpy(aw_haptic->aw_rtp->data, rtp_file->data, rtp_file->size); + release_firmware(rtp_file); + mutex_unlock(&aw_haptic->rtp_lock); + /* gain */ + ram_vbat_comp(aw_haptic, false); + /* rtp mode config */ + aw_haptic->func->play_mode(aw_haptic, AW_RTP_MODE); + /* bst mode */ + aw_haptic->func->bst_mode_config(aw_haptic, AW_BST_BYPASS_MODE); + disable_irq(gpio_to_irq(aw_haptic->irq_gpio)); + /* haptic go */ + aw_haptic->func->play_go(aw_haptic, true); + mutex_lock(&aw_haptic->rtp_lock); + pm_qos_enable(aw_haptic, true); + while (1) { + if (!aw_haptic->func->rtp_get_fifo_afs(aw_haptic)) { +#ifdef AW_ENABLE_RTP_PRINT_LOG + aw_info("not almost_full, aw_haptic->rtp_cnt=%d", + aw_haptic->rtp_cnt); +#endif + if ((aw_haptic->aw_rtp->len - aw_haptic->rtp_cnt) < + (aw_haptic->ram.base_addr >> 2)) + buf_len = aw_haptic->aw_rtp->len - aw_haptic->rtp_cnt; + else + buf_len = (aw_haptic->ram.base_addr >> 2); + if (aw_haptic->rtp_cnt != aw_haptic->aw_rtp->len) { + if (aw_haptic->timeval_flags == 1) { + aw_haptic->kstart = ktime_get(); + aw_haptic->timeval_flags = 0; + } + aw_haptic->func->set_rtp_data(aw_haptic, + &aw_haptic->aw_rtp->data + [aw_haptic->rtp_cnt], buf_len); + aw_haptic->rtp_cnt += buf_len; + } + } + if (aw_haptic->func->get_osc_status(aw_haptic)) { + aw_haptic->kend = ktime_get(); + aw_info("osc trim playback done aw_haptic->rtp_cnt= %d", + aw_haptic->rtp_cnt); + break; + } + aw_haptic->kend = ktime_get(); + aw_haptic->microsecond = ktime_to_us(ktime_sub(aw_haptic->kend, + aw_haptic->kstart)); + if (aw_haptic->microsecond > AW_OSC_CALI_MAX_LENGTH) { + aw_info("osc trim time out! aw_haptic->rtp_cnt %d", + aw_haptic->rtp_cnt); + break; + } + } + pm_qos_enable(aw_haptic, false); + mutex_unlock(&aw_haptic->rtp_lock); + enable_irq(gpio_to_irq(aw_haptic->irq_gpio)); + aw_haptic->microsecond = ktime_to_us(ktime_sub(aw_haptic->kend, + aw_haptic->kstart)); + /*calibration osc */ + aw_info("microsecond: %llu", aw_haptic->microsecond); + return 0; +} + +static void rtp_trim_lra_cali(struct aw_haptic *aw_haptic) +{ +#ifdef AW_OSC_MULTI_CALI + uint8_t last_trim_code = 0; + int temp = 0; + int count = 5; +#endif + uint32_t lra_trim_code = 0; + /*0.1 percent below no need to calibrate */ + uint32_t osc_cali_threshold = 10; + uint32_t real_code = 0; + uint32_t theory_time = 0; + uint32_t real_time = 0; + + aw_haptic->func->upload_lra(aw_haptic, AW_WRITE_ZERO); +#ifdef AW_OSC_MULTI_CALI + while (count) { +#endif + rtp_osc_cali(aw_haptic); + real_time = aw_haptic->microsecond; + theory_time = aw_haptic->func->get_theory_time(aw_haptic); + if (theory_time == real_time) { + aw_info("theory_time == real_time: %d, no need to calibrate!", + real_time); + return; + } else if (theory_time < real_time) { + if ((real_time - theory_time) > + (theory_time / AW_OSC_TRIM_PARAM)) { + aw_info("(real_time - theory_time) > (theory_time/50), can't calibrate!"); + return; + } + + if ((real_time - theory_time) < + (osc_cali_threshold * theory_time / 10000)) { + aw_info("real_time: %d, theory_time: %d, no need to calibrate!", + real_time, theory_time); + return; + } + + real_code = 100000 * ((real_time - theory_time)) / + (theory_time * AW_OSC_CALI_ACCURACY); + real_code = ((real_code % 10 < 5) ? 0 : 1) + real_code / 10; + real_code = 32 + real_code; + } else if (theory_time > real_time) { + if ((theory_time - real_time) > + (theory_time / AW_OSC_TRIM_PARAM)) { + aw_info("(theory_time - real_time) > (theory_time / 50), can't calibrate!"); + return; + } + if ((theory_time - real_time) < + (osc_cali_threshold * theory_time / 10000)) { + aw_info("real_time: %d, theory_time: %d, no need to calibrate!", + real_time, theory_time); + return; + } + + real_code = (theory_time - real_time) / (theory_time / 100000) / AW_OSC_CALI_ACCURACY; + real_code = ((real_code % 10 < 5) ? 0 : 1) + real_code / 10; + real_code = 32 - real_code; + } + if (real_code > 31) + lra_trim_code = real_code - 32; + else + lra_trim_code = real_code + 32; +#ifdef AW_OSC_MULTI_CALI + last_trim_code = aw_haptic->func->get_trim_lra(aw_haptic); + if (last_trim_code) { + if (lra_trim_code >= 32) { + temp = last_trim_code - (64 - lra_trim_code); + if (temp < 32 && temp > 0 && last_trim_code >= 32) + temp = 32; + if (temp < 0) + temp = lra_trim_code + last_trim_code; + } else if (lra_trim_code > 0 && lra_trim_code < 32) { + temp = (last_trim_code + lra_trim_code) & 0x3f; + if (temp >= 32 && last_trim_code <= 31) + temp = 31; + if ((64 - last_trim_code) <= lra_trim_code) + temp = last_trim_code + lra_trim_code - 64; + } else { + temp = last_trim_code; + } + aw_haptic->osc_cali_data = temp; + } else { + aw_haptic->osc_cali_data = lra_trim_code; + } +#else + aw_haptic->osc_cali_data = lra_trim_code; +#endif + aw_haptic->func->upload_lra(aw_haptic, AW_OSC_CALI_LRA); + aw_info("real_time: %d, theory_time: %d", real_time, theory_time); + aw_info("real_code: %d, trim_lra: 0x%02X", real_code, lra_trim_code); +#ifdef AW_OSC_MULTI_CALI + count--; + } +#endif +} + +#ifdef AW_INPUT_FRAMEWORK +static void input_gain_work_routine(struct work_struct *work) +{ + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, + gain_work); + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->set_gain(aw_haptic, aw_haptic->gain); + mutex_unlock(&aw_haptic->lock); +} + +static void input_vib_work_routine(struct work_struct *work) +{ + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, + input_vib_work); + + mutex_lock(&aw_haptic->lock); + /* Enter standby mode */ + hrtimer_cancel(&aw_haptic->timer); + aw_haptic->func->play_stop(aw_haptic); + if (aw_haptic->state) { + aw_haptic->func->upload_lra(aw_haptic, AW_F0_CALI_LRA); + if (aw_haptic->activate_mode == AW_RAM_MODE) { + ram_vbat_comp(aw_haptic, false); + aw_haptic->func->set_wav_seq(aw_haptic, 0x00, + aw_haptic->index); + aw_haptic->func->set_wav_seq(aw_haptic, 0x01, 0x00); + aw_haptic->func->set_wav_loop(aw_haptic, 0x00, 0x00); + ram_play(aw_haptic, AW_RAM_MODE); + } else if (aw_haptic->activate_mode == AW_RAM_LOOP_MODE) { + ram_vbat_comp(aw_haptic, true); + aw_haptic->func->set_repeat_seq(aw_haptic, + aw_haptic->index); + ram_play(aw_haptic, AW_RAM_LOOP_MODE); + /* run ms timer */ + hrtimer_start(&aw_haptic->timer, + ktime_set(aw_haptic->duration / 1000, + (aw_haptic->duration % 1000) * + 1000000), HRTIMER_MODE_REL); + } else { + aw_err("activate_mode error"); + } + } + mutex_unlock(&aw_haptic->lock); +} + +static int input_upload_effect(struct input_dev *dev, struct ff_effect *effect, + struct ff_effect *old) +{ + struct aw_haptic *aw_haptic = input_get_drvdata(dev); + short wav_id = 0; + int wav_id_max = 0; + int ret = 0; + + mutex_lock(&aw_haptic->lock); + switch (effect->type) { + case FF_CONSTANT: + aw_haptic->activate_mode = AW_RAM_LOOP_MODE; + aw_haptic->duration = effect->replay.length; + aw_haptic->index = AW_EFFECT_ID_BOUNDS; + aw_info("waveform id = %d", aw_haptic->index); + break; + case FF_PERIODIC: + ret = copy_from_user(&wav_id, effect->u.periodic.custom_data, + sizeof(short)); + if (ret) { + aw_err("copy from user error %d!!", ret); + mutex_unlock(&aw_haptic->lock); + return -ERANGE; + } + aw_info("waveform id = %d", wav_id); + wav_id_max = sizeof(aw_rtp_name) / sizeof(*aw_rtp_name) + + aw_haptic->ram.ram_num - 1; + if (wav_id > 0 && wav_id < AW_EFFECT_ID_BOUNDS) { + aw_haptic->activate_mode = AW_RAM_MODE; + aw_haptic->index = wav_id; + } else if (wav_id > AW_EFFECT_ID_BOUNDS && wav_id <= wav_id_max) { + aw_haptic->activate_mode = AW_RTP_MODE; + aw_haptic->rtp_file_num = wav_id - AW_EFFECT_ID_BOUNDS; + } else { + aw_haptic->activate_mode = AW_STANDBY_MODE; + aw_err("waveform id is error"); + mutex_unlock(&aw_haptic->lock); + return -ERANGE; + } + break; + default: + aw_err("Unsupported effect type: %d", effect->type); + break; + } + mutex_unlock(&aw_haptic->lock); + + return 0; +} + +static int input_playback(struct input_dev *dev, int effect_id, int val) +{ + struct aw_haptic *aw_haptic = input_get_drvdata(dev); + + if (val > 0) + aw_haptic->state = 1; + if (val <= 0) + aw_haptic->state = 0; + switch (aw_haptic->activate_mode) { + case AW_RAM_MODE: + schedule_work(&aw_haptic->input_vib_work); + break; + case AW_RAM_LOOP_MODE: + schedule_work(&aw_haptic->input_vib_work); + break; + case AW_RTP_MODE: + schedule_work(&aw_haptic->rtp_work); + break; + default: + aw_err("Unsupported mode: %d", aw_haptic->activate_mode); + break; + } + + return 0; +} + +static int input_erase(struct input_dev *dev, int effect_id) +{ + struct aw_haptic *aw_haptic = input_get_drvdata(dev); + + aw_haptic->duration = 0; + return 0; +} + +static void input_set_gain(struct input_dev *dev, uint16_t gain) +{ + struct aw_haptic *aw_haptic = input_get_drvdata(dev); + + if (gain > 0x7fff) + gain = 0x7fff; + aw_haptic->gain = gain * 0x80 / 0x7fff; + schedule_work(&aw_haptic->gain_work); + aw_info("aw_haptic->gain = 0x%02x", aw_haptic->gain); +} + +static int input_framework_init(struct aw_haptic *aw_haptic) +{ + struct input_dev *input_dev; + int ret = 0; + + input_dev = devm_input_allocate_device(aw_haptic->dev); + if (input_dev == NULL) + return -ENOMEM; + input_dev->name = "aw-haptic-hv"; + input_set_drvdata(input_dev, aw_haptic); + aw_haptic->input_dev = input_dev; + input_set_capability(input_dev, EV_FF, FF_GAIN); + input_set_capability(input_dev, EV_FF, FF_CONSTANT); + input_set_capability(input_dev, EV_FF, FF_PERIODIC); + input_set_capability(input_dev, EV_FF, FF_CUSTOM); + ret = input_ff_create(input_dev, AW_EFFECT_NUMBER); + if (ret < 0) { + aw_err("create input FF device failed, rc=%d\n", ret); + return ret; + } + input_dev->ff->upload = input_upload_effect; + input_dev->ff->playback = input_playback; + input_dev->ff->erase = input_erase; + input_dev->ff->set_gain = input_set_gain; + INIT_WORK(&aw_haptic->gain_work, input_gain_work_routine); + INIT_WORK(&aw_haptic->input_vib_work, input_vib_work_routine); + ret = input_register_device(input_dev); + if (ret < 0) { + aw_err("register input device failed, rc=%d\n", ret); + input_ff_destroy(aw_haptic->input_dev); + return ret; + } + return ret; +} +#endif + +static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) +{ + struct aw_haptic *aw_haptic = container_of(timer, struct aw_haptic, + timer); + + aw_haptic->state = 0; + schedule_work(&aw_haptic->vibrator_work); + return HRTIMER_NORESTART; +} + +#ifdef AW_DURATION_DECIDE_WAVEFORM +static int ram_select_waveform(struct aw_haptic *aw_haptic) +{ + uint8_t wavseq = 0; + uint8_t wavloop = 0; + + if (aw_haptic->duration <= 0) { + aw_err("duration time %d error", aw_haptic->duration); + return -ERANGE; + } + aw_haptic->activate_mode = AW_RAM_MODE; + if ((aw_haptic->duration > 0) && + (aw_haptic->duration < aw_haptic->info.duration_time[0])) { + wavseq = AW_DURATION_WAVESEQ_1; + } else if ((aw_haptic->duration >= aw_haptic->info.duration_time[0]) && + (aw_haptic->duration < aw_haptic->info.duration_time[1])) { + wavseq = AW_DURATION_WAVESEQ_2; + } else if ((aw_haptic->duration >= aw_haptic->info.duration_time[1]) && + (aw_haptic->duration <= aw_haptic->info.duration_time[2])) { + wavseq = AW_DURATION_WAVESEQ_3; + } else if (aw_haptic->duration > aw_haptic->info.duration_time[2]) { + wavseq = AW_DURATION_WAVESEQ_4; + wavloop = 15; + aw_haptic->activate_mode = AW_RAM_LOOP_MODE; + } + aw_info("duration %d, select index %d", aw_haptic->duration, wavseq); + aw_haptic->func->set_wav_seq(aw_haptic, 0, wavseq); + aw_haptic->func->set_wav_loop(aw_haptic, 0, wavloop); + aw_haptic->func->set_wav_seq(aw_haptic, 1, 0); + aw_haptic->func->set_wav_loop(aw_haptic, 1, 0); + return 0; +} +#endif + +static void vibrator_work_routine(struct work_struct *work) +{ + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, + vibrator_work); + + mutex_lock(&aw_haptic->lock); + /* Enter standby mode */ + hrtimer_cancel(&aw_haptic->timer); + aw_haptic->func->play_stop(aw_haptic); + if (aw_haptic->state) { +#if defined(AW_DURATION_DECIDE_WAVEFORM) && !defined(HAPTIC_HV_HAL_COMPAT) + ram_select_waveform(aw_haptic); +#endif + aw_haptic->func->upload_lra(aw_haptic, AW_F0_CALI_LRA); + if (aw_haptic->activate_mode == AW_RAM_MODE) { + ram_vbat_comp(aw_haptic, false); + ram_play(aw_haptic, AW_RAM_MODE); + } else if (aw_haptic->activate_mode == AW_RAM_LOOP_MODE) { + ram_vbat_comp(aw_haptic, true); + ram_play(aw_haptic, AW_RAM_LOOP_MODE); + /* run ms timer */ + hrtimer_start(&aw_haptic->timer, + ktime_set(aw_haptic->duration / 1000, + (aw_haptic->duration % 1000) * + 1000000), HRTIMER_MODE_REL); + } else if (aw_haptic->activate_mode == AW_CONT_MODE) { + aw_haptic->func->cont_config(aw_haptic); + /* run ms timer */ + hrtimer_start(&aw_haptic->timer, + ktime_set(aw_haptic->duration / 1000, + (aw_haptic->duration % 1000) * + 1000000), HRTIMER_MODE_REL); + } else { + aw_err("activate_mode error"); + } + } + mutex_unlock(&aw_haptic->lock); +} + +static int rtp_play(struct aw_haptic *aw_haptic) +{ + uint8_t glb_state_val = 0; + uint32_t buf_len = 0; + int ret = 0; + struct aw_haptic_container *aw_rtp = aw_haptic->aw_rtp; + + while ((!aw_haptic->func->rtp_get_fifo_afs(aw_haptic)) + && (aw_haptic->play_mode == AW_RTP_MODE)) { +#ifdef AW_ENABLE_RTP_PRINT_LOG + aw_info("rtp cnt = %d", aw_haptic->rtp_cnt); +#endif + if (!aw_rtp) { + aw_info("aw_rtp is null, break!"); + ret = -ERANGE; + break; + } + if (aw_haptic->rtp_cnt < (aw_haptic->ram.base_addr)) { + if ((aw_rtp->len - aw_haptic->rtp_cnt) < + (aw_haptic->ram.base_addr)) { + buf_len = aw_rtp->len - aw_haptic->rtp_cnt; + } else { + buf_len = aw_haptic->ram.base_addr; + } + } else if ((aw_rtp->len - aw_haptic->rtp_cnt) < + (aw_haptic->ram.base_addr >> 2)) { + buf_len = aw_rtp->len - aw_haptic->rtp_cnt; + } else { + buf_len = aw_haptic->ram.base_addr >> 2; + } +#ifdef AW_ENABLE_RTP_PRINT_LOG + aw_info("buf_len = %d", buf_len); +#endif +#ifdef AW_DOUBLE + if (aw_haptic->rtp_cnt == 0 && aw_haptic->dual_flag == true) { + aw_haptic->dual_flag = false; + if (down_trylock(&left->sema) == 0) { + down_interruptible(&left->sema); + } else { + up(&left->sema); + up(&left->sema); + } + aw_info("dual rtp play start"); + } +#endif + aw_haptic->func->set_rtp_data(aw_haptic, + &aw_rtp->data[aw_haptic->rtp_cnt], + buf_len); + aw_haptic->rtp_cnt += buf_len; + glb_state_val = aw_haptic->func->get_glb_state(aw_haptic); + if ((aw_haptic->rtp_cnt == aw_rtp->len) + || ((glb_state_val & AW_GLBRD_STATE_MASK) == + AW_STATE_STANDBY)) { + if (aw_haptic->rtp_cnt != aw_rtp->len) + aw_err("rtp play suspend!"); + else + aw_info("rtp update complete!"); + aw_haptic->rtp_cnt = 0; + aw_haptic->rtp_init = false; + break; + } + } + return ret; +} + +static int wait_enter_rtp_mode(struct aw_haptic *aw_haptic) +{ + bool rtp_work_flag = false; + uint8_t ret = 0; + int cnt = 200; + + while (cnt) { + ret = aw_haptic->func->judge_rtp_going(aw_haptic); + if (ret) { + rtp_work_flag = true; + aw_info("RTP_GO!"); + break; + } + cnt--; + aw_info("wait for RTP_GO, glb_state=0x%02X", ret); + usleep_range(2000, 2500); + } + if (!rtp_work_flag) { + aw_haptic->func->play_stop(aw_haptic); + aw_err("failed to enter RTP_GO status!"); + return -ERANGE; + } + return 0; +} + +static void rtp_work_routine(struct work_struct *work) +{ + int ret = -1; + const struct firmware *rtp_file; + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, + rtp_work); + + if (!aw_haptic->ram.base_addr) { + aw_err("base addr is 0, not allow rtp play"); + return; + } + mutex_lock(&aw_haptic->lock); + hrtimer_cancel(&aw_haptic->timer); + aw_haptic->func->play_stop(aw_haptic); + aw_haptic->func->set_rtp_aei(aw_haptic, false); + aw_haptic->func->irq_clear(aw_haptic); + if (!aw_haptic->state) { + mutex_unlock(&aw_haptic->lock); + return; + } + mutex_unlock(&aw_haptic->lock); + mutex_lock(&aw_haptic->rtp_lock); + /* fw loaded */ + ret = request_firmware(&rtp_file, aw_rtp_name[aw_haptic->rtp_file_num], + aw_haptic->dev); + if (ret < 0) { + aw_err("failed to read %s", + aw_rtp_name[aw_haptic->rtp_file_num]); + mutex_unlock(&aw_haptic->rtp_lock); + return; + } + aw_haptic->rtp_init = false; + vfree(aw_haptic->aw_rtp); + aw_haptic->aw_rtp = vmalloc(rtp_file->size + sizeof(int)); + if (!aw_haptic->aw_rtp) { + release_firmware(rtp_file); + aw_err("error allocating memory"); + mutex_unlock(&aw_haptic->rtp_lock); + return; + } + aw_haptic->aw_rtp->len = rtp_file->size; + aw_info("rtp file:[%s] size = %dbytes", + aw_rtp_name[aw_haptic->rtp_file_num], aw_haptic->aw_rtp->len); + memcpy(aw_haptic->aw_rtp->data, rtp_file->data, rtp_file->size); + mutex_unlock(&aw_haptic->rtp_lock); + release_firmware(rtp_file); + mutex_lock(&aw_haptic->lock); + aw_haptic->rtp_init = true; + + aw_haptic->func->upload_lra(aw_haptic, AW_OSC_CALI_LRA); + /* gain */ + ram_vbat_comp(aw_haptic, false); + /* rtp mode config */ + aw_haptic->func->play_mode(aw_haptic, AW_RTP_MODE); + /* haptic go */ + aw_haptic->func->play_go(aw_haptic, true); + usleep_range(2000, 2500); + ret = wait_enter_rtp_mode(aw_haptic); + if (ret < 0) { + mutex_unlock(&aw_haptic->lock); + return; + } + mutex_unlock(&aw_haptic->lock); + + mutex_lock(&aw_haptic->rtp_lock); + pm_qos_enable(aw_haptic, true); + aw_haptic->rtp_cnt = 0; + rtp_play(aw_haptic); + if (aw_haptic->play_mode == AW_RTP_MODE) + aw_haptic->func->set_rtp_aei(aw_haptic, true); + pm_qos_enable(aw_haptic, false); + mutex_unlock(&aw_haptic->rtp_lock); +} + +static irqreturn_t irq_handle(int irq, void *data) +{ + int irq_state = 0; + struct aw_haptic *aw_haptic = data; + + do { + irq_state = aw_haptic->func->get_irq_state(aw_haptic); + if (irq_state == AW_IRQ_ALMOST_EMPTY) { + if (aw_haptic->rtp_init) { + mutex_lock(&aw_haptic->rtp_lock); + rtp_play(aw_haptic); + mutex_unlock(&aw_haptic->rtp_lock); + } else { + aw_info("rtp_init: %d", aw_haptic->rtp_init); + } + } + if (aw_haptic->play_mode != AW_RTP_MODE) + aw_haptic->func->set_rtp_aei(aw_haptic, false); + } while (irq_state); + return IRQ_HANDLED; +} + +static int irq_config(struct device *dev, struct aw_haptic *aw_haptic) +{ + int ret = -1; + int irq_flags = 0; + + if (gpio_is_valid(aw_haptic->irq_gpio) && + !(aw_haptic->flags & AW_FLAG_SKIP_INTERRUPTS)) { + /* register irq handler */ + aw_haptic->func->interrupt_setup(aw_haptic); + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(dev, + gpio_to_irq(aw_haptic->irq_gpio), + NULL, irq_handle, irq_flags, + "aw_haptic", aw_haptic); + if (ret != 0) { + aw_err("failed to request IRQ %d: %d", + gpio_to_irq(aw_haptic->irq_gpio), ret); + return ret; + } + } else { + dev_info(dev, "skipping IRQ registration"); + /* disable feature support if gpio was invalid */ + aw_haptic->flags |= AW_FLAG_SKIP_INTERRUPTS; + } + return 0; +} + +static void ram_load(const struct firmware *cont, void *context) +{ + uint16_t check_sum = 0; + int i = 0; + int ret = 0; + struct aw_haptic *aw_haptic = context; + struct aw_haptic_container *aw_fw; + +#ifdef AW_READ_BIN_FLEXBALLY + static uint8_t load_cont; + int ram_timer_val = 1000; + + load_cont++; +#endif + if (!cont) { + aw_err("failed to read %s", aw_ram_name); + release_firmware(cont); +#ifdef AW_READ_BIN_FLEXBALLY + if (load_cont <= 20) { + schedule_delayed_work(&aw_haptic->ram_work, + msecs_to_jiffies(ram_timer_val)); + aw_info("start hrtimer:load_cont%d", load_cont); + } +#endif + return; + } + aw_info("loaded %s - size: %zu", aw_ram_name, cont ? cont->size : 0); + /* check sum */ + for (i = 2; i < cont->size; i++) + check_sum += cont->data[i]; + if (check_sum != (uint16_t)((cont->data[0] << 8) | (cont->data[1]))) { + aw_err("check sum err: check_sum=0x%04x", check_sum); + release_firmware(cont); + return; + } + aw_info("check sum pass : 0x%04x", check_sum); + aw_haptic->ram.check_sum = check_sum; + + /* aw ram update */ + aw_fw = kzalloc(cont->size + sizeof(int), GFP_KERNEL); + if (!aw_fw) { + release_firmware(cont); + aw_err("Error allocating memory"); + return; + } + aw_fw->len = cont->size; + memcpy(aw_fw->data, cont->data, cont->size); + release_firmware(cont); + ret = aw_haptic->func->container_update(aw_haptic, aw_fw); + if (ret) { + aw_err("ram firmware update failed!"); + } else { + aw_haptic->ram_init = true; + aw_haptic->ram.len = aw_fw->len - aw_haptic->ram.ram_shift; + mutex_lock(&aw_haptic->lock); + aw_haptic->func->trig_init(aw_haptic); + mutex_unlock(&aw_haptic->lock); + aw_info("ram firmware update complete!"); + get_ram_num(aw_haptic); + } + kfree(aw_fw); + +#ifdef AW_BOOT_OSC_CALI + rtp_trim_lra_cali(aw_haptic); +#endif +} + +static int ram_update(struct aw_haptic *aw_haptic) +{ + aw_haptic->ram_init = false; + aw_haptic->rtp_init = false; + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw_ram_name, aw_haptic->dev, GFP_KERNEL, + aw_haptic, ram_load); +} + +static void ram_work_routine(struct work_struct *work) +{ + struct aw_haptic *aw_haptic = container_of(work, struct aw_haptic, + ram_work.work); + + ram_update(aw_haptic); +} + +static void ram_work_init(struct aw_haptic *aw_haptic) +{ + int ram_timer_val = AW_RAM_WORK_DELAY_INTERVAL; + + INIT_DELAYED_WORK(&aw_haptic->ram_work, ram_work_routine); + schedule_delayed_work(&aw_haptic->ram_work, + msecs_to_jiffies(ram_timer_val)); +} + +/***************************************************** + * + * haptic audio + * + *****************************************************/ +static int audio_ctrl_list_ins(struct aw_haptic *aw_haptic, + struct aw_haptic_ctr *haptic_ctr) +{ + struct aw_haptic_ctr *p_new = NULL; + struct aw_haptic_audio *haptic_audio = &aw_haptic->haptic_audio; + + p_new = (struct aw_haptic_ctr *)kzalloc( + sizeof(struct aw_haptic_ctr), GFP_KERNEL); + if (p_new == NULL) { + aw_err("kzalloc memory fail"); + return -ENOMEM; + } + /* update new list info */ + p_new->cnt = haptic_ctr->cnt; + p_new->cmd = haptic_ctr->cmd; + p_new->play = haptic_ctr->play; + p_new->wavseq = haptic_ctr->wavseq; + p_new->loop = haptic_ctr->loop; + p_new->gain = haptic_ctr->gain; + INIT_LIST_HEAD(&(p_new->list)); + list_add(&(p_new->list), &(haptic_audio->ctr_list)); + return 0; +} + +static void audio_ctrl_list_clr(struct aw_haptic_audio *haptic_audio) +{ + struct aw_haptic_ctr *p_ctr = NULL; + struct aw_haptic_ctr *p_ctr_bak = NULL; + + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + list_del(&p_ctr->list); + kfree(p_ctr); + } +} + +static void audio_init(struct aw_haptic *aw_haptic) +{ + aw_dbg("enter!"); + aw_haptic->func->set_wav_seq(aw_haptic, 0x01, 0x00); +} + +static void audio_off(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + mutex_lock(&aw_haptic->lock); + aw_haptic->func->set_gain(aw_haptic, 0x80); + aw_haptic->func->play_stop(aw_haptic); + audio_ctrl_list_clr(&aw_haptic->haptic_audio); + mutex_unlock(&aw_haptic->lock); +} + +static enum hrtimer_restart audio_timer_func(struct hrtimer *timer) +{ + struct aw_haptic *aw_haptic = + container_of(timer, struct aw_haptic, haptic_audio.timer); + + aw_dbg("enter"); + schedule_work(&aw_haptic->haptic_audio.work); + + hrtimer_start(&aw_haptic->haptic_audio.timer, + ktime_set(aw_haptic->haptic_audio.timer_val / 1000000, + (aw_haptic->haptic_audio.timer_val % 1000000) * + 1000), HRTIMER_MODE_REL); + return HRTIMER_NORESTART; +} + +static void audio_work_routine(struct work_struct *work) +{ + struct aw_haptic *aw_haptic = + container_of(work, struct aw_haptic, haptic_audio.work); + struct aw_haptic_audio *haptic_audio = NULL; + struct aw_haptic_ctr *ctr = &aw_haptic->haptic_audio.ctr; + struct aw_haptic_ctr *p_ctr = NULL; + struct aw_haptic_ctr *p_ctr_bak = NULL; + uint32_t ctr_list_flag = 0; + uint32_t ctr_list_input_cnt = 0; + uint32_t ctr_list_output_cnt = 0; + uint32_t ctr_list_diff_cnt = 0; + uint32_t ctr_list_del_cnt = 0; + int rtp_is_going_on = 0; + + aw_dbg("enter"); + haptic_audio = &(aw_haptic->haptic_audio); + mutex_lock(&aw_haptic->haptic_audio.lock); + memset(ctr, 0, sizeof(struct aw_haptic_ctr)); + ctr_list_flag = 0; + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + ctr_list_flag = 1; + break; + } + if (ctr_list_flag == 0) + aw_dbg("ctr list empty"); + if (ctr_list_flag == 1) { + list_for_each_entry_safe(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + ctr_list_input_cnt = p_ctr->cnt; + break; + } + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), + list) { + ctr_list_output_cnt = p_ctr->cnt; + break; + } + if (ctr_list_input_cnt > ctr_list_output_cnt) { + ctr_list_diff_cnt = + ctr_list_input_cnt - ctr_list_output_cnt; + } + if (ctr_list_input_cnt < ctr_list_output_cnt) { + ctr_list_diff_cnt = + 32 + ctr_list_input_cnt - ctr_list_output_cnt; + } + if (ctr_list_diff_cnt > 2) { + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio-> + ctr_list), list) { + if ((p_ctr->play == 0) + && (AW_CMD_ENABLE == + (AW_CMD_HAPTIC & p_ctr-> + cmd))) { + list_del(&p_ctr->list); + kfree(p_ctr); + ctr_list_del_cnt++; + } + if (ctr_list_del_cnt == ctr_list_diff_cnt) + break; + } + } + } + /* get the last data from list */ + list_for_each_entry_safe_reverse(p_ctr, p_ctr_bak, + &(haptic_audio->ctr_list), list) { + ctr->cnt = p_ctr->cnt; + ctr->cmd = p_ctr->cmd; + ctr->play = p_ctr->play; + ctr->wavseq = p_ctr->wavseq; + ctr->loop = p_ctr->loop; + ctr->gain = p_ctr->gain; + list_del(&p_ctr->list); + kfree(p_ctr); + break; + } + if (ctr->play) { + aw_info("cnt=%d, cmd=%d, play=%d, wavseq=%d, loop=%d, gain=%d", + ctr->cnt, ctr->cmd, ctr->play, + ctr->wavseq, ctr->loop, ctr->gain); + } + if (ctr->wavseq > aw_haptic->ram.ram_num) { + aw_err("wavseq out of range!"); + mutex_unlock(&aw_haptic->haptic_audio.lock); + return; + } + rtp_is_going_on = aw_haptic->func->judge_rtp_going(aw_haptic); + if (rtp_is_going_on) { + mutex_unlock(&aw_haptic->haptic_audio.lock); + return; + } + mutex_unlock(&aw_haptic->haptic_audio.lock); + +#ifdef AW_DOUBLE + if (ctr->play == AW_PLAY_ENABLE) { + mutex_lock(&aw_haptic->lock); + /* haptic config */ + if (ctr->cmd & AW_CMD_L_EN) { + left->func->play_stop(left); + left->func->play_mode(left, AW_RAM_MODE); + left->func->set_wav_seq(left, 0x00, ctr->wavseq); + left->func->set_wav_seq(left, 0x01, 0x00); + left->func->set_wav_loop(left, 0x00, ctr->loop); + left->func->set_gain(left, ctr->gain); + } + + if (ctr->cmd & AW_CMD_R_EN) { + right->func->play_stop(right); + right->func->play_mode(right, AW_RAM_MODE); + right->func->set_wav_seq(right, 0x00, ctr->wavseq); + right->func->set_wav_seq(right, 0x01, 0x00); + right->func->set_wav_loop(right, 0x00, ctr->loop); + right->func->set_gain(right, ctr->gain); + } + /* play go */ + if (ctr->cmd & AW_CMD_L_EN) + left->func->play_go(left, true); + if (ctr->cmd & AW_CMD_R_EN) + right->func->play_go(right, true); + mutex_unlock(&aw_haptic->lock); + } else if (ctr->play == AW_PLAY_STOP) { + mutex_lock(&aw_haptic->lock); + left->func->play_stop(left); + right->func->play_stop(right); + mutex_unlock(&aw_haptic->lock); + } else if (ctr->play == AW_PLAY_GAIN) { + mutex_lock(&aw_haptic->lock); + left->func->set_gain(left, ctr->gain); + right->func->set_gain(right, ctr->gain); + mutex_unlock(&aw_haptic->lock); + } +#else + if (ctr->cmd == AW_CMD_ENABLE) { + if (ctr->play == AW_PLAY_ENABLE) { + aw_info("haptic_audio_play_start"); + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + aw_haptic->func->play_mode(aw_haptic, AW_RAM_MODE); + aw_haptic->func->set_wav_seq(aw_haptic, 0x00, + ctr->wavseq); + aw_haptic->func->set_wav_seq(aw_haptic, 0x01, 0x00); + aw_haptic->func->set_wav_loop(aw_haptic, 0x00, + ctr->loop); + aw_haptic->func->set_gain(aw_haptic, ctr->gain); + aw_haptic->func->play_go(aw_haptic, true); + mutex_unlock(&aw_haptic->lock); + } else if (ctr->play == AW_PLAY_STOP) { + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + mutex_unlock(&aw_haptic->lock); + } else if (ctr->play == AW_PLAY_GAIN) { + mutex_lock(&aw_haptic->lock); + aw_haptic->func->set_gain(aw_haptic, ctr->gain); + mutex_unlock(&aw_haptic->lock); + } + } +#endif + +} + +/***************************************************** + * + * node + * + *****************************************************/ +#ifdef TIMED_OUTPUT +static int vibrator_get_time(struct timed_output_dev *dev) +{ + struct aw_haptic *aw_haptic = container_of(dev, struct aw_haptic, + vib_dev); + + if (hrtimer_active(&aw_haptic->timer)) { + ktime_t r = hrtimer_get_remaining(&aw_haptic->timer); + + return ktime_to_ms(r); + } + return 0; +} + +static void vibrator_enable(struct timed_output_dev *dev, int value) +{ + struct aw_haptic *aw_haptic = container_of(dev, struct aw_haptic, + vib_dev); + + aw_info("enter"); + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return; + } + if (value < 0) { + aw_err("unsupported param"); + return; + } + mutex_lock(&aw_haptic->lock); + aw_haptic->state = value; + aw_haptic->activate_mode = AW_RAM_MODE; + mutex_unlock(&aw_haptic->lock); + schedule_work(&aw_haptic->vibrator_work); +} +#else +static enum led_brightness brightness_get(struct led_classdev *cdev) +{ + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + return aw_haptic->amplitude; +} + +static void brightness_set(struct led_classdev *cdev, enum led_brightness level) +{ + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + aw_info("enter"); + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return; + } + mutex_lock(&aw_haptic->lock); + aw_haptic->amplitude = level; + aw_haptic->state = level; + aw_haptic->activate_mode = AW_RAM_MODE; + mutex_unlock(&aw_haptic->lock); + schedule_work(&aw_haptic->vibrator_work); +} +#endif + +static ssize_t state_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + return snprintf(buf, PAGE_SIZE, "state = %d\n", aw_haptic->state); +} + +static ssize_t state_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + return count; +} + +static ssize_t duration_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ktime_t time_rem; + s64 time_ms = 0; + + if (hrtimer_active(&aw_haptic->timer)) { + time_rem = hrtimer_get_remaining(&aw_haptic->timer); + time_ms = ktime_to_ms(time_rem); + } + return snprintf(buf, PAGE_SIZE, "duration = %lldms\n", time_ms); +} + +static ssize_t duration_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + /* setting 0 on duration is NOP for now */ + if (val <= 0) + return count; + aw_info("duration=%d", val); + aw_haptic->duration = val; + return count; +} + +static ssize_t activate_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + return snprintf(buf, PAGE_SIZE, "activate = %d\n", aw_haptic->state); +} + +#ifdef HAPTIC_HV_HAL_COMPAT +/* Moto-HAL-compatible. If "duration" has been set to less than or equal to + 100ms, perform a single short vibration using seq[0]. If "duration" has been + set to higher than 100ms, perform a long vibration for "duration" ms using + waveform index 2. */ +static ssize_t activate_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + if (val != 0 && val != 1) + return -EINVAL; + + aw_info("value=%d", val); + + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return -EIO; + } + + mutex_lock(&aw_haptic->lock); + aw_haptic->state = val; + aw_haptic->func->set_wav_seq(aw_haptic, 0, aw_haptic->seq[0]); + if (aw_haptic->duration <= 100 || aw_haptic->seq[0] > 2) { /* Short vibration */ +#ifdef AW_DURATION_DECIDE_WAVEFORM + if (aw_haptic->seq[0] <= 2) + ram_select_waveform(aw_haptic); +#endif + aw_haptic->activate_mode = AW_RAM_MODE; + aw_haptic->loop[0] = 0x00; + aw_haptic->func->set_wav_loop(aw_haptic, 0x00, 0x00); + } else { /* Long vibration */ + aw_haptic->activate_mode = AW_RAM_LOOP_MODE; + aw_haptic->index = 0x02; + aw_haptic->func->set_repeat_seq(aw_haptic, aw_haptic->index); + } + + aw_haptic->seq[0] = 1; /* Restore default waveform */ + mutex_unlock(&aw_haptic->lock); + schedule_work(&aw_haptic->vibrator_work); + + return count; +} +#else /* #ifdef HAPTIC_HV_HAL_COMPAT */ +static ssize_t activate_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + aw_info("value=%d", val); + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + mutex_lock(&aw_haptic->lock); + aw_haptic->state = val; + aw_haptic->activate_mode = aw_haptic->info.mode; + mutex_unlock(&aw_haptic->lock); + schedule_work(&aw_haptic->vibrator_work); + + return count; +} +#endif /* #ifdef HAPTIC_HV_HAL_COMPAT */ + +static ssize_t activate_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + return snprintf(buf, PAGE_SIZE, "activate_mode = %d\n", + aw_haptic->activate_mode); +} + +static ssize_t activate_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + mutex_lock(&aw_haptic->lock); + aw_haptic->activate_mode = val; + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t index_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->get_wav_seq(aw_haptic, 1); + aw_haptic->index = aw_haptic->seq[0]; + mutex_unlock(&aw_haptic->lock); + return snprintf(buf, PAGE_SIZE, "index = %d\n", aw_haptic->index); +} + +static ssize_t index_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + if (val > aw_haptic->ram.ram_num) { + aw_err("input value out of range!"); + return count; + } + aw_info("value=%d", val); + mutex_lock(&aw_haptic->lock); + aw_haptic->index = val; + aw_haptic->func->set_repeat_seq(aw_haptic, aw_haptic->index); + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t vmax_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + return snprintf(buf, PAGE_SIZE, "vmax = %dmV\n", aw_haptic->vmax); +} + +static ssize_t vmax_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + aw_info("value=%d", val); + mutex_lock(&aw_haptic->lock); + aw_haptic->vmax = val; + aw_haptic->func->set_bst_vol(aw_haptic, aw_haptic->vmax); + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t gain_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + return snprintf(buf, PAGE_SIZE, "gain = 0x%02X\n", aw_haptic->gain); +} + +static ssize_t gain_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + aw_info("value=0x%02x", val); + mutex_lock(&aw_haptic->lock); + aw_haptic->gain = val; + aw_haptic->func->set_gain(aw_haptic, aw_haptic->gain); + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t seq_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + uint8_t i = 0; + size_t count = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->get_wav_seq(aw_haptic, AW_SEQUENCER_SIZE); + mutex_unlock(&aw_haptic->lock); + for (i = 0; i < AW_SEQUENCER_SIZE; i++) + count += snprintf(buf + count, PAGE_SIZE - count, + "seq%d = %d\n", i + 1, aw_haptic->seq[i]); + return count; +} + +#ifdef HAPTIC_HV_HAL_COMPAT +/* Moto-HAL-compatible. Takes a 32-bit value, with the most significant byte + assigned to seq[0], the next most significant byte being assigned to seq[1], + next to seq[2], and the least significant byte being assigned to seq[3]. */ +static ssize_t seq_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, vib_dev); + unsigned int i, val = 0, seq_val; + int rc; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + aw_info("seq=0x%08X", val); + for (i = 0; i < 4; i++) { + seq_val = (val >> ((4-i-1) * 8)) & 0xFF; + if (seq_val > aw_haptic->ram.ram_num) { + aw_err("seq[%d]: Input value out of range! (0x%02X)", i, seq_val); + seq_val = 0; + } + aw_haptic->seq[i] = seq_val; + mutex_lock(&aw_haptic->lock); + aw_haptic->func->set_wav_seq(aw_haptic, i, aw_haptic->seq[i]); + mutex_unlock(&aw_haptic->lock); + } + + return count; +} +#else /* #ifdef HAPTIC_HV_HAL_COMPAT */ +static ssize_t seq_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + if (databuf[0] >= AW_SEQUENCER_SIZE || + databuf[1] > aw_haptic->ram.ram_num) { + aw_err("input value out of range!"); + return count; + } + aw_info("seq%d=0x%02X", databuf[0], databuf[1]); + mutex_lock(&aw_haptic->lock); + aw_haptic->seq[databuf[0]] = (uint8_t)databuf[1]; + aw_haptic->func->set_wav_seq(aw_haptic, (uint8_t)databuf[0], + aw_haptic->seq[databuf[0]]); + mutex_unlock(&aw_haptic->lock); + } + return count; +} +#endif /* #ifdef HAPTIC_HV_HAL_COMPAT */ + +static ssize_t loop_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + size_t count = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + mutex_lock(&aw_haptic->lock); + count = aw_haptic->func->get_wav_loop(aw_haptic, buf); + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t loop_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw_info("seq%d loop=0x%02X", databuf[0], databuf[1]); + mutex_lock(&aw_haptic->lock); + aw_haptic->loop[databuf[0]] = (uint8_t)databuf[1]; + aw_haptic->func->set_wav_loop(aw_haptic, (uint8_t)databuf[0], + aw_haptic->loop[databuf[0]]); + mutex_unlock(&aw_haptic->lock); + } + + return count; +} + +static ssize_t reg_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + len = aw_haptic->func->get_reg(aw_haptic, len, buf); + mutex_unlock(&aw_haptic->lock); + return len; +} + +static ssize_t reg_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t val = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t databuf[2] = { 0, 0 }; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + val = (uint8_t)databuf[1]; + mutex_lock(&aw_haptic->lock); + haptic_hv_i2c_writes(aw_haptic, (uint8_t)databuf[0], &val, + AW_I2C_BYTE_ONE); + mutex_unlock(&aw_haptic->lock); + } + return count; +} + +static ssize_t rtp_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "rtp_cnt = %d\n", + aw_haptic->rtp_cnt); + return len; +} + +static ssize_t rtp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + uint32_t rtp_num_max = sizeof(aw_rtp_name) / AW_RTP_NAME_MAX; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) { + aw_err("kstrtouint fail"); + return rc; + } + mutex_lock(&aw_haptic->lock); + if ((val > 0) && (val < rtp_num_max)) { + aw_haptic->state = 1; + aw_haptic->rtp_file_num = val; + aw_info("aw_rtp_name[%d]: %s", val, aw_rtp_name[val]); + } else if (val == 0) { + aw_haptic->state = 0; + } else { + aw_haptic->state = 0; + aw_err("input number error:%d", val); + } + mutex_unlock(&aw_haptic->lock); + schedule_work(&aw_haptic->rtp_work); + return count; +} + +static ssize_t ram_update_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + int i = 0; + uint8_t *ram_buf = NULL; + + aw_info("ram len = %d", aw_haptic->ram.len); + ram_buf = kzalloc(aw_haptic->ram.len, GFP_KERNEL); + if (!ram_buf) { + aw_err("Error allocating memory"); + return len; + } + mutex_lock(&aw_haptic->lock); + /* RAMINIT Enable */ + aw_haptic->func->ram_init(aw_haptic, true); + aw_haptic->func->play_stop(aw_haptic); + aw_haptic->func->set_ram_addr(aw_haptic); + aw_haptic->func->get_ram_data(aw_haptic, ram_buf); + for (i = 1; i <= aw_haptic->ram.len; i++) { + len += snprintf(buf + len, PAGE_SIZE, "0x%02x,", + *(ram_buf + i - 1)); + if (i % 16 == 0 || i == aw_haptic->ram.len) { + len = 0; + aw_info("%s", buf); + } + } + kfree(ram_buf); + /* RAMINIT Disable */ + aw_haptic->func->ram_init(aw_haptic, false); + len = snprintf(buf, PAGE_SIZE, "Please check log\n"); + mutex_unlock(&aw_haptic->lock); + return len; +} + +static ssize_t ram_update_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + if (val) + ram_update(aw_haptic); + return count; +} + +static ssize_t ram_num_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + get_ram_num(aw_haptic); + len += snprintf(buf + len, PAGE_SIZE - len, + "ram_num = %d\n", aw_haptic->ram.ram_num); + return len; +} + +static ssize_t f0_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->upload_lra(aw_haptic, AW_WRITE_ZERO); + aw_haptic->func->get_f0(aw_haptic); + mutex_unlock(&aw_haptic->lock); + len += snprintf(buf + len, PAGE_SIZE - len, "f0 = %d\n", aw_haptic->f0); + return len; +} + +static ssize_t ram_f0_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->upload_lra(aw_haptic, AW_WRITE_ZERO); + aw_haptic->func->ram_get_f0(aw_haptic); + mutex_unlock(&aw_haptic->lock); + len += snprintf(buf + len, PAGE_SIZE - len, "f0 = %d\n", aw_haptic->f0); + return len; +} + +static ssize_t cali_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->upload_lra(aw_haptic, AW_F0_CALI_LRA); + aw_haptic->func->get_f0(aw_haptic); + mutex_unlock(&aw_haptic->lock); + len += snprintf(buf + len, PAGE_SIZE - len, "f0 = %d\n", aw_haptic->f0); + return len; +} + +static ssize_t cali_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + if (val) { + mutex_lock(&aw_haptic->lock); + f0_cali(aw_haptic); + mutex_unlock(&aw_haptic->lock); + } + return count; +} + +static ssize_t ram_cali_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->upload_lra(aw_haptic, AW_F0_CALI_LRA); + aw_haptic->func->ram_get_f0(aw_haptic); + mutex_unlock(&aw_haptic->lock); + len += snprintf(buf + len, PAGE_SIZE - len, "f0 = %d\n", aw_haptic->f0); + return len; +} + +static ssize_t ram_cali_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + if (val) { + mutex_lock(&aw_haptic->lock); + ram_f0_cali(aw_haptic); + mutex_unlock(&aw_haptic->lock); + } + return count; +} + + +static ssize_t cont_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + if (val) { + aw_haptic->func->upload_lra(aw_haptic, AW_F0_CALI_LRA); + aw_haptic->func->cont_config(aw_haptic); + } + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t vbat_monitor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->get_vbat(aw_haptic); + len += snprintf(buf + len, PAGE_SIZE - len, "vbat_monitor = %d\n", + aw_haptic->vbat); + mutex_unlock(&aw_haptic->lock); + + return len; +} + +static ssize_t lra_resistance_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->get_lra_resistance(aw_haptic); + mutex_unlock(&aw_haptic->lock); + len += snprintf(buf + len, PAGE_SIZE - len, "lra_resistance = %d\n", + aw_haptic->lra); + return len; +} + +static ssize_t auto_boost_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "auto_boost = %d\n", + aw_haptic->auto_boost); + + return len; +} + +static ssize_t auto_boost_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + aw_haptic->func->auto_bst_enable(aw_haptic, val); + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t prct_mode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + uint8_t reg_val = 0; + + mutex_lock(&aw_haptic->lock); + reg_val = aw_haptic->func->get_prctmode(aw_haptic); + mutex_unlock(&aw_haptic->lock); + len += snprintf(buf + len, PAGE_SIZE - len, "prctmode = %d\n", reg_val); + return len; +} + +static ssize_t prct_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t databuf[2] = { 0, 0 }; + uint32_t addr = 0; + uint32_t val = 0; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + addr = databuf[0]; + val = databuf[1]; + mutex_lock(&aw_haptic->lock); + aw_haptic->func->protect_config(aw_haptic, addr, val); + mutex_unlock(&aw_haptic->lock); + } + return count; +} + +static ssize_t ram_vbat_comp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "ram_vbat_comp = %d\n", + aw_haptic->ram_vbat_comp); + + return len; +} + +static ssize_t ram_vbat_comp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + mutex_lock(&aw_haptic->lock); + if (val) + aw_haptic->ram_vbat_comp = AW_RAM_VBAT_COMP_ENABLE; + else + aw_haptic->ram_vbat_comp = AW_RAM_VBAT_COMP_DISABLE; + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t osc_cali_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "osc_cali_data = 0x%02X\n", + aw_haptic->osc_cali_data); + + return len; +} + +static ssize_t osc_cali_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + mutex_lock(&aw_haptic->lock); + if (val == 1) { + rtp_trim_lra_cali(aw_haptic); + } else if (val == 2) { + aw_haptic->func->upload_lra(aw_haptic, AW_OSC_CALI_LRA); + rtp_osc_cali(aw_haptic); + } + mutex_unlock(&aw_haptic->lock); + + return count; +} + +static ssize_t haptic_audio_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + ssize_t len = 0; + + len += snprintf(buf+len, PAGE_SIZE-len, "%d\n", + aw_haptic->haptic_audio.ctr.cnt); + return len; +} + +static ssize_t haptic_audio_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + uint32_t databuf[6] = { 0 }; + struct aw_haptic_ctr *hap_ctr = NULL; + + if (!aw_haptic->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + if (sscanf(buf, "%d %d %d %d %d %d", &databuf[0], &databuf[1], + &databuf[2], &databuf[3], &databuf[4], &databuf[5]) == 6) { + if (databuf[2]) { + aw_info("cnt=%d, cmd=%d, play=%d, wavseq=%d, loop=%d, gain=%d", + databuf[0], databuf[1], databuf[2], databuf[3], + databuf[4], databuf[5]); + } + + hap_ctr = (struct aw_haptic_ctr *)kzalloc( + sizeof(struct aw_haptic_ctr), GFP_KERNEL); + if (hap_ctr == NULL) { + aw_err("kzalloc memory fail"); + return count; + } + mutex_lock(&aw_haptic->haptic_audio.lock); + hap_ctr->cnt = (uint8_t)databuf[0]; + hap_ctr->cmd = (uint8_t)databuf[1]; + hap_ctr->play = (uint8_t)databuf[2]; + hap_ctr->wavseq = (uint8_t)databuf[3]; + hap_ctr->loop = (uint8_t)databuf[4]; + hap_ctr->gain = (uint8_t)databuf[5]; + audio_ctrl_list_ins(aw_haptic, hap_ctr); + if (hap_ctr->cmd == 0xff) { + aw_info("haptic_audio stop"); + if (hrtimer_active(&aw_haptic->haptic_audio.timer)) { + aw_info("cancel haptic_audio_timer"); + hrtimer_cancel(&aw_haptic->haptic_audio.timer); + aw_haptic->haptic_audio.ctr.cnt = 0; + audio_off(aw_haptic); + } + } else { + if (hrtimer_active(&aw_haptic->haptic_audio.timer)) { + /* */ + } else { + aw_info("start haptic_audio_timer"); + audio_init(aw_haptic); + hrtimer_start(&aw_haptic->haptic_audio.timer, + ktime_set(aw_haptic->haptic_audio. + delay_val / 1000000, + (aw_haptic->haptic_audio. + delay_val % 1000000) * + 1000), + HRTIMER_MODE_REL); + } + } + mutex_unlock(&aw_haptic->haptic_audio.lock); + kfree(hap_ctr); + } + return count; +} + +static ssize_t haptic_audio_time_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "haptic_audio.delay_val=%dus\n", + aw_haptic->haptic_audio.delay_val); + len += snprintf(buf + len, PAGE_SIZE - len, + "haptic_audio.timer_val=%dus\n", + aw_haptic->haptic_audio.timer_val); + return len; +} + +static ssize_t haptic_audio_time_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + uint32_t databuf[2] = { 0 }; + + if (sscanf(buf, "%d %d", &databuf[0], &databuf[1]) == 2) { + aw_haptic->haptic_audio.delay_val = databuf[0]; + aw_haptic->haptic_audio.timer_val = databuf[1]; + } + return count; +} + +static ssize_t gun_type_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + return snprintf(buf, PAGE_SIZE, "0x%02x\n", aw_haptic->gun_type); +} + +static ssize_t gun_type_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + aw_dbg("value=%d", val); + mutex_lock(&aw_haptic->lock); + aw_haptic->gun_type = val; + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t bullet_nr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + return snprintf(buf, PAGE_SIZE, "0x%02x\n", aw_haptic->bullet_nr); +} + +static ssize_t bullet_nr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + aw_dbg("value=%d", val); + mutex_lock(&aw_haptic->lock); + aw_haptic->bullet_nr = val; + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t awrw_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + int i = 0; + ssize_t len = 0; + + if (aw_haptic->i2c_info.flag != AW_SEQ_READ) { + aw_err("no read mode"); + return -ERANGE; + } + if (aw_haptic->i2c_info.reg_data == NULL) { + aw_err("awrw lack param"); + return -ERANGE; + } + for (i = 0; i < aw_haptic->i2c_info.reg_num; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%02x,", aw_haptic->i2c_info.reg_data[i]); + } + len += snprintf(buf + len - 1, PAGE_SIZE - len, "\n"); + return len; +} + +static ssize_t awrw_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + uint8_t value = 0; + char data_buf[5] = { 0 }; + uint32_t flag = 0; + uint32_t reg_num = 0; + uint32_t reg_addr = 0; + int i = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (sscanf(buf, "%x %x %x", &flag, ®_num, ®_addr) == 3) { + if (!reg_num) { + aw_err("param error"); + return -ERANGE; + } + aw_haptic->i2c_info.flag = flag; + aw_haptic->i2c_info.reg_num = reg_num; + if (aw_haptic->i2c_info.reg_data != NULL) + kfree(aw_haptic->i2c_info.reg_data); + aw_haptic->i2c_info.reg_data = kmalloc(reg_num, GFP_KERNEL); + if (flag == AW_SEQ_WRITE) { + if ((reg_num * 5) != (strlen(buf) - 3 * 5)) { + aw_err("param error"); + return -ERANGE; + } + for (i = 0; i < reg_num; i++) { + memcpy(data_buf, &buf[15 + i * 5], 4); + data_buf[4] = '\0'; + rc = kstrtou8(data_buf, 0, &value); + if (rc < 0) { + aw_err("param error"); + return -ERANGE; + } + aw_haptic->i2c_info.reg_data[i] = value; + } + mutex_lock(&aw_haptic->lock); + haptic_hv_i2c_writes(aw_haptic, (uint8_t)reg_addr, + aw_haptic->i2c_info.reg_data, reg_num); + mutex_unlock(&aw_haptic->lock); + } else if (flag == AW_SEQ_READ) { + mutex_lock(&aw_haptic->lock); + haptic_hv_i2c_reads(aw_haptic, reg_addr, + aw_haptic->i2c_info.reg_data, reg_num); + mutex_unlock(&aw_haptic->lock); + } + } else { + aw_err("param error"); + } + return count; +} + +static ssize_t f0_save_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "f0_cali_data = 0x%02X\n", + aw_haptic->f0_cali_data); + + return len; +} + +static ssize_t f0_save_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + aw_haptic->f0_cali_data = val; + return count; +} + +static ssize_t osc_save_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + len += snprintf(buf + len, PAGE_SIZE - len, "osc_cali_data = 0x%02X\n", + aw_haptic->osc_cali_data); + + return len; +} + +static ssize_t osc_save_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t val = 0; + int rc = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + aw_haptic->osc_cali_data = val; + return count; +} + +#ifdef AW_DOUBLE +static ssize_t dual_cont_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + + right->func->play_stop(right); + left->func->play_stop(left); + if (val) { + right->func->upload_lra(right, AW_F0_CALI_LRA); + left->func->upload_lra(left, AW_F0_CALI_LRA); + right->func->cont_config(right); + left->func->cont_config(left); + } + return count; +} + +static ssize_t dual_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "%d %d\n", + left->index, right->index); + return len; +} + +static ssize_t dual_index_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int index_l = 0; + int index_r = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (sscanf(buf, "%d %d", &index_l, &index_r) == 2) { + aw_info("index_l=%d index_r=%d", index_l, index_r); + if (index_l > aw_haptic->ram.ram_num || + index_r > aw_haptic->ram.ram_num) { + aw_err("input value out of range!"); + return count; + } + mutex_lock(&aw_haptic->lock); + left->index = index_l; + left->func->set_repeat_seq(left, left->index); + right->index = index_r; + right->func->set_repeat_seq(right, right->index); + mutex_unlock(&aw_haptic->lock); + } + return count; +} + +static ssize_t dual_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + return snprintf(buf, PAGE_SIZE, "dual_mode = %d\n", + aw_haptic->activate_mode); +} + +static ssize_t dual_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + mutex_lock(&aw_haptic->lock); + left->activate_mode = val; + right->activate_mode = val; + mutex_unlock(&aw_haptic->lock); + return count; +} + +static ssize_t dual_duration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + ktime_t time_rem; + s64 time_ms = 0; + + if (hrtimer_active(&aw_haptic->timer)) { + time_rem = hrtimer_get_remaining(&aw_haptic->timer); + time_ms = ktime_to_ms(time_rem); + } + return snprintf(buf, PAGE_SIZE, "duration = %lldms\n", time_ms); +} + +static ssize_t dual_duration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int duration_l = 0; + int duration_r = 0; + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + if (sscanf(buf, "%d %d", &duration_l, &duration_r) == 2) { + mutex_lock(&aw_haptic->lock); + left->duration = duration_l; + right->duration = duration_r; + mutex_unlock(&aw_haptic->lock); + } + return count; +} + +static ssize_t dual_activate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + + return snprintf(buf, PAGE_SIZE, "activate = %d\n", aw_haptic->state); +} + +static ssize_t dual_activate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + uint32_t val = 0; + int rc = 0; + + rc = kstrtouint(buf, 0, &val); + if (rc < 0) + return rc; + aw_info("value=%d", val); + if (!left->ram_init || !right->ram_init) { + aw_err("ram init failed, not allow to play!"); + return count; + } + mutex_lock(&aw_haptic->lock); + if (down_trylock(&left->sema) == 0) + up(&left->sema); + else + up(&left->sema); + left->state = val; + right->state = val; + left->dual_flag = true; + right->dual_flag = true; + mutex_unlock(&aw_haptic->lock); + schedule_work(&left->vibrator_work); + schedule_work(&right->vibrator_work); + return count; +} + + +static ssize_t dual_rtp_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "left_rtp_cnt = %d\n", + left->rtp_cnt); + len += snprintf(buf + len, PAGE_SIZE - len, "right_rtp_cnt = %d\n", + right->rtp_cnt); + return len; +} + +static ssize_t dual_rtp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + cdev_t *cdev = dev_get_drvdata(dev); + struct aw_haptic *aw_haptic = container_of(cdev, struct aw_haptic, + vib_dev); + int rtp_l = 0; + int rtp_r = 0; + uint32_t rtp_num_max = sizeof(aw_rtp_name) / AW_RTP_NAME_MAX; + + mutex_lock(&aw_haptic->lock); + if (down_trylock(&left->sema) == 0) + up(&left->sema); + else + up(&left->sema); + if (sscanf(buf, "%d %d", &rtp_l, &rtp_r) == 2) { + if (rtp_l > 0 && rtp_l < rtp_num_max) { + left->state = 1; + left->rtp_file_num = rtp_l; + } else if (rtp_l == 0) { + left->state = 0; + } else { + left->state = 0; + aw_err("input number error:%d", rtp_l); + } + if (rtp_r > 0 && rtp_r < rtp_num_max) { + right->state = 1; + right->rtp_file_num = rtp_r; + } else if (rtp_r == 0) { + right->state = 0; + } else { + right->state = 0; + aw_err("input number error:%d", rtp_r); + } + } + left->dual_flag = true; + right->dual_flag = true; + mutex_unlock(&aw_haptic->lock); + schedule_work(&left->rtp_work); + schedule_work(&right->rtp_work); + return count; +} +#endif + +static DEVICE_ATTR(f0, S_IWUSR | S_IRUGO, f0_show, NULL); +static DEVICE_ATTR(ram_f0, S_IWUSR | S_IRUGO, ram_f0_show, NULL); +static DEVICE_ATTR(seq, S_IWUSR | S_IRUGO, seq_show, seq_store); +static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, reg_show, reg_store); +static DEVICE_ATTR(vmax, S_IWUSR | S_IRUGO, vmax_show, vmax_store); +static DEVICE_ATTR(gain, S_IWUSR | S_IRUGO, gain_show, gain_store); +static DEVICE_ATTR(loop, S_IWUSR | S_IRUGO, loop_show, loop_store); +static DEVICE_ATTR(rtp, S_IWUSR | S_IRUGO, rtp_show, rtp_store); +static DEVICE_ATTR(cali, S_IWUSR | S_IRUGO, cali_show, cali_store); +static DEVICE_ATTR(ram_cali, S_IWUSR | S_IRUGO, ram_cali_show, ram_cali_store); +static DEVICE_ATTR(cont, S_IWUSR | S_IRUGO, NULL, cont_store); +static DEVICE_ATTR(awrw, S_IWUSR | S_IRUGO, awrw_show, awrw_store); +static DEVICE_ATTR(state, S_IWUSR | S_IRUGO, state_show, state_store); +static DEVICE_ATTR(index, S_IWUSR | S_IRUGO, index_show, index_store); +static DEVICE_ATTR(ram_num, S_IWUSR | S_IRUGO, ram_num_show, NULL); +static DEVICE_ATTR(duration, S_IWUSR | S_IRUGO, duration_show, duration_store); +static DEVICE_ATTR(activate, S_IWUSR | S_IRUGO, activate_show, activate_store); +static DEVICE_ATTR(osc_cali, S_IWUSR | S_IRUGO, osc_cali_show, osc_cali_store); +static DEVICE_ATTR(gun_type, S_IWUSR | S_IRUGO, gun_type_show, gun_type_store); +static DEVICE_ATTR(prctmode, S_IWUSR | S_IRUGO, prct_mode_show, + prct_mode_store); +static DEVICE_ATTR(bullet_nr, S_IWUSR | S_IRUGO, bullet_nr_show, + bullet_nr_store); +static DEVICE_ATTR(auto_boost, S_IWUSR | S_IRUGO, auto_boost_show, + auto_boost_store); +static DEVICE_ATTR(ram_update, S_IWUSR | S_IRUGO, ram_update_show, + ram_update_store); +static DEVICE_ATTR(haptic_audio, S_IWUSR | S_IRUGO, haptic_audio_show, + haptic_audio_store); +static DEVICE_ATTR(vbat_monitor, S_IWUSR | S_IRUGO, vbat_monitor_show, NULL); +static DEVICE_ATTR(activate_mode, S_IWUSR | S_IRUGO, activate_mode_show, + activate_mode_store); +static DEVICE_ATTR(ram_vbat_comp, S_IWUSR | S_IRUGO, ram_vbat_comp_show, + ram_vbat_comp_store); +static DEVICE_ATTR(lra_resistance, S_IWUSR | S_IRUGO, lra_resistance_show, + NULL); +static DEVICE_ATTR(haptic_audio_time, S_IWUSR | S_IRUGO, haptic_audio_time_show, + haptic_audio_time_store); +static DEVICE_ATTR(osc_save, S_IWUSR | S_IRUGO, osc_save_show, osc_save_store); +static DEVICE_ATTR(f0_save, S_IWUSR | S_IRUGO, f0_save_show, f0_save_store); + +#ifdef AW_DOUBLE +static DEVICE_ATTR(dual_cont, S_IWUSR | S_IRUGO, NULL, dual_cont_store); +static DEVICE_ATTR(dual_index, S_IWUSR | S_IRUGO, dual_index_show, dual_index_store); +static DEVICE_ATTR(dual_mode, S_IWUSR | S_IRUGO, dual_mode_show, dual_mode_store); +static DEVICE_ATTR(dual_duration, S_IWUSR | S_IRUGO, dual_duration_show, dual_duration_store); +static DEVICE_ATTR(dual_activate, S_IWUSR | S_IRUGO, dual_activate_show, dual_activate_store); +static DEVICE_ATTR(dual_rtp, S_IWUSR | S_IRUGO, dual_rtp_show, dual_rtp_store); +#endif + +static struct attribute *vibrator_attributes[] = { + &dev_attr_state.attr, + &dev_attr_duration.attr, + &dev_attr_activate.attr, + &dev_attr_activate_mode.attr, + &dev_attr_index.attr, + &dev_attr_vmax.attr, + &dev_attr_gain.attr, + &dev_attr_seq.attr, + &dev_attr_loop.attr, + &dev_attr_reg.attr, + &dev_attr_rtp.attr, + &dev_attr_ram_update.attr, + &dev_attr_ram_num.attr, + &dev_attr_f0.attr, + &dev_attr_ram_f0.attr, + &dev_attr_f0_save.attr, + &dev_attr_cali.attr, + &dev_attr_ram_cali.attr, + &dev_attr_cont.attr, + &dev_attr_vbat_monitor.attr, + &dev_attr_lra_resistance.attr, + &dev_attr_auto_boost.attr, + &dev_attr_prctmode.attr, + &dev_attr_ram_vbat_comp.attr, + &dev_attr_osc_cali.attr, + &dev_attr_osc_save.attr, + &dev_attr_haptic_audio.attr, + &dev_attr_haptic_audio_time.attr, + &dev_attr_gun_type.attr, + &dev_attr_bullet_nr.attr, + &dev_attr_awrw.attr, +#ifdef AW_DOUBLE + &dev_attr_dual_cont.attr, + &dev_attr_dual_index.attr, + &dev_attr_dual_mode.attr, + &dev_attr_dual_duration.attr, + &dev_attr_dual_activate.attr, + &dev_attr_dual_rtp.attr, +#endif + + NULL +}; + +static struct attribute_group vibrator_attribute_group = { + .attrs = vibrator_attributes +}; + +#ifdef AW_TIKTAP +static inline unsigned int tiktap_get_sys_msecs(void) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + struct timespec64 ts64; + + ktime_get_coarse_real_ts64(&ts64); +#else + struct timespec64 ts64 = current_kernel_time64(); +#endif + + return jiffies_to_msecs(timespec64_to_jiffies(&ts64)); +} + +static void tiktap_work_routine(struct work_struct *work) +{ + struct aw_haptic *aw_haptic = + container_of(work, struct aw_haptic, tiktap_work); + struct mmap_buf_format *tiktap_buf = aw_haptic->start_buf; + int count = 100; +#ifdef AW_DOUBLE + int sync_cnt = 40; +#endif + unsigned char reg_val = 0x10; + unsigned char glb_state_val = 0; + unsigned int write_start; + unsigned int buf_cnt = 0; + + mutex_lock(&aw_haptic->lock); + aw_haptic->tiktap_stop_flag = false; + aw_haptic->func->play_mode(aw_haptic, AW_RTP_MODE); + aw_haptic->func->upload_lra(aw_haptic, AW_F0_CALI_LRA); + while (true && count--) { + if (tiktap_buf->status == MMAP_BUF_DATA_VALID) { + aw_haptic->func->play_go(aw_haptic, true); + mdelay(1); + break; + } else if (aw_haptic->tiktap_stop_flag == true) { + mutex_unlock(&aw_haptic->lock); + return; + } + mdelay(1); + } + if (count <= 0) { + aw_err("wait 100 ms but start_buf->status != VALID! status = 0x%02x", + tiktap_buf->status); + aw_haptic->tiktap_stop_flag = true; + mutex_unlock(&aw_haptic->lock); + return; + } + aw_haptic->tiktap_ready = true; + mutex_unlock(&aw_haptic->lock); + +#ifdef AW_DOUBLE /* double tiktap work sync */ + while (sync_cnt--) { + if ((left->tiktap_ready == true) && + (right->tiktap_ready == true)) { + /* aw_info("double vib is ready! start write tiktap data."); */ + break; + } else if (aw_haptic->tiktap_stop_flag == true) { + aw_haptic->tiktap_ready = false; + return; + } + udelay(500); + } + if (sync_cnt <= 0) { + aw_err("wait 20ms but double vib not ready!"); + aw_haptic->tiktap_stop_flag = true; + aw_haptic->tiktap_ready = false; + return; + } +#endif + + mutex_lock(&aw_haptic->rtp_lock); + pm_qos_enable(aw_haptic, true); + write_start = tiktap_get_sys_msecs(); + while (true) { + if (tiktap_get_sys_msecs() > (write_start + 800)) { + aw_err("Failed! tiktap endless loop"); + break; + } + reg_val = aw_haptic->func->rtp_get_fifo_aes(aw_haptic); + glb_state_val = aw_haptic->func->get_glb_state(aw_haptic); + if ((glb_state_val & AW_GLBRD_STATE_MASK) != AW_STATE_RTP) { + aw_err("tiktap glb_state != RTP_GO!, glb_state = 0x%02x", + glb_state_val); + break; + } + if ((aw_haptic->tiktap_stop_flag == true) || + (tiktap_buf->status == MMAP_BUF_DATA_FINISHED) || + (tiktap_buf->status == MMAP_BUF_DATA_INVALID)) { + aw_err("tiktap exit! tiktap_buf->status = 0x%02x", + tiktap_buf->status); + break; + } else if (( +#ifdef AW_DOUBLE + (aw_haptic == left && (tiktap_buf->status == TIKTAP_L_VALID_R_FINISHED)) || + (aw_haptic == right && (tiktap_buf->status == TIKTAP_R_VALID_L_FINISHED)) || +#endif + tiktap_buf->status == MMAP_BUF_DATA_VALID) && (reg_val & 0x01)) { + aw_info("buf_cnt = %d, bit = %d, length = %d!", + buf_cnt, tiktap_buf->bit, tiktap_buf->length); + + aw_haptic->func->set_rtp_data(aw_haptic, tiktap_buf->data, tiktap_buf->length); +#ifdef AW_DOUBLE + if (aw_haptic == left) { + tiktap_buf->status &= TIkTAP_LEFT_STATUS_MASK; + tiktap_buf->status |= TIkTAP_LEFT_FINISHED_DONE; /* left done */ + } else if (aw_haptic == right) { + tiktap_buf->status &= TIKTAP_RIGHT_STATUS_MASK; + tiktap_buf->status |= TIKTAP_RIGHT_FINISHED_DONE; /* right done */ + } +#else + tiktap_buf->status = MMAP_BUF_DATA_FINISHED; +#endif + tiktap_buf = tiktap_buf->kernel_next; + write_start = tiktap_get_sys_msecs(); + buf_cnt++; + } else { + mdelay(1); + } + } + pm_qos_enable(aw_haptic, false); + aw_haptic->tiktap_stop_flag = true; + mutex_unlock(&aw_haptic->rtp_lock); +} + +static void tiktap_clean_buf(struct aw_haptic *aw_haptic, int status) +{ + struct mmap_buf_format *tiktap_buf = aw_haptic->start_buf; + int i = 0; + + for (i = 0; i < AW_TIKTAP_MMAP_BUF_SUM; i++) { + tiktap_buf->status = status; + tiktap_buf = tiktap_buf->kernel_next; + } +} + +static long tiktap_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct aw_haptic *aw_haptic = g_aw_haptic; + unsigned int tmp = 0; + + int ret = 0; + + switch (cmd) { + case TIKTAP_GET_HWINFO: + aw_info("cmd = TIKTAP_GET_HWINFO!"); + tmp = aw_haptic->chipid; + if (copy_to_user((void __user *)arg, &tmp, sizeof(unsigned int))) + ret = -EFAULT; + break; + case TIKTAP_GET_F0: + aw_info("cmd = TIKTAP_GET_F0!"); + tmp = aw_haptic->f0; + if (copy_to_user((void __user *)arg, &tmp, sizeof(unsigned int))) + ret = -EFAULT; + break; + case TIKTAP_STOP_MODE: + aw_info("cmd = TIKTAP_STOP_MODE!"); + tiktap_clean_buf(aw_haptic, MMAP_BUF_DATA_INVALID); +#ifdef AW_DOUBLE + left->tiktap_stop_flag = true; + right->tiktap_stop_flag = true; + left->tiktap_ready = false; + right->tiktap_ready = false; + mutex_lock(&left->lock); + left->func->play_stop(left); + mutex_unlock(&left->lock); + mutex_lock(&right->lock); + right->func->play_stop(right); + mutex_unlock(&right->lock); +#else + aw_haptic->tiktap_stop_flag = true; + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + mutex_unlock(&aw_haptic->lock); +#endif + break; + case TIKTAP_RTP_MODE: + /* aw_info("cmd = TIKTAP_RTP_MODE!"); */ + tiktap_clean_buf(aw_haptic, MMAP_BUF_DATA_INVALID); +#ifdef AW_DOUBLE + left->tiktap_ready = false; + right->tiktap_ready = false; + left->tiktap_stop_flag = true; + right->tiktap_stop_flag = true; + + mutex_lock(&left->lock); + left->func->play_stop(left); + mutex_unlock(&left->lock); + mutex_lock(&right->lock); + right->func->play_stop(right); + mutex_unlock(&right->lock); + + schedule_work(&right->tiktap_work); + schedule_work(&left->tiktap_work); +#else + aw_haptic->tiktap_stop_flag = true; + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + mutex_unlock(&aw_haptic->lock); + schedule_work(&aw_haptic->tiktap_work); +#endif + break; + default: + aw_info("unknown cmd = %d", cmd); + break; + } + return ret; +} + +#ifdef CONFIG_COMPAT +static long tiktap_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + ret = tiktap_unlocked_ioctl(file, cmd, arg); + return ret; +} +#endif + +static int tiktap_file_mmap(struct file *filp, struct vm_area_struct *vma) +{ + unsigned long phys; + struct aw_haptic *aw_haptic = g_aw_haptic; + int ret = 0; + +#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 7, 0) + vm_flags_t vm_flags = calc_vm_prot_bits(PROT_READ|PROT_WRITE, 0) | + calc_vm_flag_bits(MAP_SHARED); + + vm_flags |= current->mm->def_flags | VM_MAYREAD | VM_MAYWRITE | + VM_MAYEXEC | VM_SHARED | VM_MAYSHARE; + + if (vma && (pgprot_val(vma->vm_page_prot) != + pgprot_val(vm_get_page_prot(vm_flags)))) { + aw_err("vm_page_prot error!"); + return -EPERM; + } + + if (vma && ((vma->vm_end - vma->vm_start) != + (PAGE_SIZE << AW_TIKTAP_MMAP_PAGE_ORDER))) { + aw_err("mmap size check err!"); + return -EPERM; + } +#endif + phys = virt_to_phys(aw_haptic->start_buf); + + ret = remap_pfn_range(vma, vma->vm_start, (phys >> PAGE_SHIFT), + (vma->vm_end - vma->vm_start), vma->vm_page_prot); + if (ret) { + aw_err("mmap failed!"); + return ret; + } + + aw_info("success!"); + + return ret; +} + +#ifdef KERNEL_OVER_5_10 +static const struct proc_ops tiktap_proc_ops = { + .proc_mmap = tiktap_file_mmap, + .proc_ioctl = tiktap_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .proc_compat_ioctl = tiktap_compat_ioctl, +#endif +}; +#else +static const struct file_operations tiktap_proc_ops = { + .owner = THIS_MODULE, + .mmap = tiktap_file_mmap, + .unlocked_ioctl = tiktap_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = tiktap_compat_ioctl, +#endif +}; +#endif + +static int tiktap_init(struct aw_haptic *aw_haptic) +{ + /* contrl double vib init once */ + static int tiktap_init; + static struct mmap_buf_format *tiktap_start_buf; + static struct proc_dir_entry *tiktap_config_proc; + + if (tiktap_init == 0) { + /* Create proc file node */ + tiktap_config_proc = NULL; + tiktap_config_proc = proc_create(AW_TIKTAP_PROCNAME, 0666, + NULL, &tiktap_proc_ops); + if (tiktap_config_proc == NULL) { + aw_err("create proc entry %s failed", + AW_TIKTAP_PROCNAME); + return -EPERM; + } + aw_info("create proc entry %s success", AW_TIKTAP_PROCNAME); + + /* Construct shared memory */ + tiktap_start_buf = (struct mmap_buf_format *)__get_free_pages(GFP_KERNEL, AW_TIKTAP_MMAP_PAGE_ORDER); + if (tiktap_start_buf == NULL) { + aw_err("Error __get_free_pages failed"); + return -ENOMEM; + } + SetPageReserved(virt_to_page(tiktap_start_buf)); + { + struct mmap_buf_format *temp; + unsigned int i = 0; + + temp = tiktap_start_buf; + for (i = 1; i < AW_TIKTAP_MMAP_BUF_SUM; i++) { + temp->kernel_next = (tiktap_start_buf + i); + temp = temp->kernel_next; + } + temp->kernel_next = tiktap_start_buf; + + temp = tiktap_start_buf; + for (i = 0; i < AW_TIKTAP_MMAP_BUF_SUM; i++) { + temp->bit = i; + temp = temp->kernel_next; + } + } + + } + tiktap_init = 1; + + aw_haptic->aw_config_proc = tiktap_config_proc; + aw_haptic->start_buf = tiktap_start_buf; + /* init flag and work */ + aw_haptic->tiktap_stop_flag = true; + aw_haptic->tiktap_ready = false; + INIT_WORK(&aw_haptic->tiktap_work, tiktap_work_routine); + return 0; +} +#endif + +static int vibrator_init(struct aw_haptic *aw_haptic) +{ + int ret = 0; + + aw_info("enter"); + +#ifdef TIMED_OUTPUT + aw_info("TIMED_OUT FRAMEWORK!"); +#ifdef AW_DOUBLE + ret = memcmp(aw_haptic->name, "left", sizeof("left")); + if (!ret) + aw_haptic->vib_dev.name = "vibrator_l"; + ret = memcmp(aw_haptic->name, "right", sizeof("right")); + if (!ret) + aw_haptic->vib_dev.name = "vibrator_r"; +#else + aw_haptic->vib_dev.name = "vibrator"; +#endif + aw_haptic->vib_dev.get_time = vibrator_get_time; + aw_haptic->vib_dev.enable = vibrator_enable; + + ret = timed_output_dev_register(&(aw_haptic->vib_dev)); + if (ret < 0) { + aw_err("fail to create timed output dev"); + return ret; + } + ret = sysfs_create_group(&aw_haptic->vib_dev.dev->kobj, + &vibrator_attribute_group); + if (ret < 0) { + aw_err("error creating sysfs attr files"); + return ret; + } +#else + aw_info("loaded in leds_cdev framework!"); +#ifdef AW_DOUBLE + ret = memcmp(aw_haptic->name, "left", sizeof("left")); + if (!ret) + aw_haptic->vib_dev.name = "vibrator_l"; + ret = memcmp(aw_haptic->name, "right", sizeof("right")); + if (!ret) + aw_haptic->vib_dev.name = "vibrator_r"; +#else + aw_haptic->vib_dev.name = "vibrator"; +#endif + aw_haptic->vib_dev.brightness_get = brightness_get; + aw_haptic->vib_dev.brightness_set = brightness_set; + ret = devm_led_classdev_register(&aw_haptic->i2c->dev, + &aw_haptic->vib_dev); + if (ret < 0) { + aw_err("fail to create led dev"); + return ret; + } + ret = sysfs_create_group(&aw_haptic->vib_dev.dev->kobj, + &vibrator_attribute_group); + if (ret < 0) { + aw_err("error creating sysfs attr files"); + return ret; + } + + /* + * Android has a habit of trying to set the vibrator trigger to "transient", + * which destroys our own "activate", "duration", and "state" attributes. + * Remove the "trigger" attribute provided by leds_class so that no one can + * change the trigger of the vibrator LED device. + * + * Luckily, `sysfs_remove_file` only uses the `name` field, so we can use a + * compound literal instead of having to find the proper attribute struct + */ + sysfs_remove_file(&aw_haptic->vib_dev.dev->kobj, + &((struct attribute){.name = "trigger"})); + +#endif + hrtimer_init(&aw_haptic->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + aw_haptic->timer.function = vibrator_timer_func; + INIT_WORK(&aw_haptic->vibrator_work, vibrator_work_routine); + INIT_WORK(&aw_haptic->rtp_work, rtp_work_routine); + mutex_init(&aw_haptic->lock); + mutex_init(&aw_haptic->rtp_lock); + sema_init (&aw_haptic->sema, 1); + + return 0; +} + +static void haptic_init(struct aw_haptic *aw_haptic) +{ + aw_info("enter"); + /* haptic audio */ + aw_haptic->haptic_audio.delay_val = 1; + aw_haptic->haptic_audio.timer_val = 21318; + INIT_LIST_HEAD(&(aw_haptic->haptic_audio.ctr_list)); + hrtimer_init(&aw_haptic->haptic_audio.timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); + aw_haptic->haptic_audio.timer.function = audio_timer_func; + INIT_WORK(&aw_haptic->haptic_audio.work, audio_work_routine); + mutex_init(&aw_haptic->haptic_audio.lock); + INIT_LIST_HEAD(&(aw_haptic->haptic_audio.list)); + + /* haptic init */ + mutex_lock(&aw_haptic->lock); + aw_haptic->bullet_nr = 0; + aw_haptic->gun_type = 0xff; + aw_haptic->activate_mode = aw_haptic->info.mode; + aw_haptic->func->play_mode(aw_haptic, AW_STANDBY_MODE); + aw_haptic->func->set_pwm(aw_haptic, AW_PWM_24K); + /* misc value init */ + aw_haptic->func->misc_para_init(aw_haptic); + aw_haptic->func->set_bst_peak_cur(aw_haptic); + aw_haptic->func->set_bst_vol(aw_haptic, aw_haptic->vmax); + aw_haptic->func->protect_config(aw_haptic, 0x01, 0x00); + aw_haptic->func->auto_bst_enable(aw_haptic, + aw_haptic->info.is_enabled_auto_bst); + aw_haptic->func->offset_cali(aw_haptic); + aw_haptic->ram_vbat_comp = AW_RAM_VBAT_COMP_ENABLE; + /* f0 calibration */ + f0_cali(aw_haptic); + mutex_unlock(&aw_haptic->lock); +} + +static int aw_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + int ret = 0; + struct aw_haptic *aw_haptic; + struct device_node *np = i2c->dev.of_node; + + pr_info("<%s>%s: enter\n", AW_I2C_NAME, __func__); + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + pr_err("<%s>%s: check_functionality failed\n", AW_I2C_NAME, __func__); + return -EIO; + } + + aw_haptic = devm_kzalloc(&i2c->dev, sizeof(struct aw_haptic), + GFP_KERNEL); + if (aw_haptic == NULL) + return -ENOMEM; + + aw_haptic->dev = &i2c->dev; + aw_haptic->i2c = i2c; + + i2c_set_clientdata(i2c, aw_haptic); + dev_set_drvdata(&i2c->dev, aw_haptic); +#ifdef AW_INPUT_FRAMEWORK + ret = input_framework_init(aw_haptic); + if (ret < 0) + goto err_parse_dt; +#endif + /* aw_haptic rst & int */ + if (np) { + ret = parse_dt_gpio(&i2c->dev, aw_haptic, np); + if (ret) { + aw_err("failed to parse gpio"); + goto err_parse_dt; + } + } else { + aw_haptic->reset_gpio = -1; + aw_haptic->irq_gpio = -1; + } + + if (gpio_is_valid(aw_haptic->reset_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw_haptic->reset_gpio, + GPIOF_OUT_INIT_LOW, "aw_rst"); + if (ret) { + aw_err("rst request failed"); + goto err_reset_gpio_request; + } + } + +#ifdef AW_ENABLE_PIN_CONTROL + aw_haptic->pinctrl = devm_pinctrl_get(&i2c->dev); + if (IS_ERR(aw_haptic->pinctrl)) { + if (PTR_ERR(aw_haptic->pinctrl) == -EPROBE_DEFER) { + aw_err("pinctrl not ready"); + ret = -EPROBE_DEFER; + goto err_reset_gpio_request; + } + aw_err("Target does not use pinctrl"); + aw_haptic->pinctrl = NULL; + ret = -EINVAL; + goto err_reset_gpio_request; + } + aw_haptic->pinctrl_state = pinctrl_lookup_state(aw_haptic->pinctrl, + "irq_active"); + if (IS_ERR(aw_haptic->pinctrl_state)) { + aw_err("cannot find pinctrl state"); + ret = -EINVAL; + goto err_reset_gpio_request; + } else { + pinctrl_select_state(aw_haptic->pinctrl, + aw_haptic->pinctrl_state); + } +#endif + if (gpio_is_valid(aw_haptic->irq_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw_haptic->irq_gpio, + GPIOF_DIR_IN, "aw_int"); + if (ret) { + aw_err("int request failed"); + goto err_irq_gpio_request; + } + } + + /* aw func ptr init */ + ret = ctrl_init(aw_haptic); + if (ret < 0) { + aw_err("ctrl_init failed ret=%d", ret); + goto err_ctrl_init; + } + + ret = aw_haptic->func->check_qualify(aw_haptic); + if (ret < 0) { + aw_err("qualify check failed ret=%d", ret); + goto err_ctrl_init; + } + + /* aw_haptic chip id */ + ret = parse_chipid(aw_haptic); + if (ret < 0) { + aw_err("parse chipid failed ret=%d", ret); + goto err_id; + } + + sw_reset(aw_haptic); + aw_haptic->func->parse_dt(&i2c->dev, aw_haptic, np); + +#ifdef AW_SND_SOC_CODEC + aw_haptic->func->snd_soc_init(&i2c->dev); +#endif + + /* aw_haptic irq */ + ret = irq_config(&i2c->dev, aw_haptic); + if (ret != 0) { + aw_err("irq_config failed ret=%d", ret); + goto err_irq_config; + } + +#ifdef AW_TIKTAP + g_aw_haptic = aw_haptic; + ret = tiktap_init(aw_haptic); + if (ret) { + aw_err("tiktap_init failed ret = %d", ret); + goto err_irq_config; + } +#endif + + vibrator_init(aw_haptic); + haptic_init(aw_haptic); + aw_haptic->func->creat_node(aw_haptic); + ram_work_init(aw_haptic); + aw_info("probe completed successfully!"); + + return 0; + +err_id: +err_ctrl_init: +err_irq_config: + if (gpio_is_valid(aw_haptic->irq_gpio)) + devm_gpio_free(&i2c->dev, aw_haptic->irq_gpio); + +err_irq_gpio_request: + if (gpio_is_valid(aw_haptic->reset_gpio)) + devm_gpio_free(&i2c->dev, aw_haptic->reset_gpio); + +err_parse_dt: +err_reset_gpio_request: + devm_kfree(&i2c->dev, aw_haptic); + aw_haptic = NULL; + return ret; +} + +static int aw_remove(struct i2c_client *i2c) +{ + struct aw_haptic *aw_haptic = i2c_get_clientdata(i2c); + + aw_info("enter"); +#ifdef TIMED_OUTPUT + timed_output_dev_unregister(&aw_haptic->vib_dev); +#else + devm_led_classdev_unregister(&aw_haptic->i2c->dev, &aw_haptic->vib_dev); +#endif + cancel_delayed_work_sync(&aw_haptic->ram_work); + cancel_work_sync(&aw_haptic->haptic_audio.work); + hrtimer_cancel(&aw_haptic->haptic_audio.timer); + cancel_work_sync(&aw_haptic->rtp_work); + cancel_work_sync(&aw_haptic->vibrator_work); + hrtimer_cancel(&aw_haptic->timer); + mutex_destroy(&aw_haptic->lock); + mutex_destroy(&aw_haptic->rtp_lock); + mutex_destroy(&aw_haptic->haptic_audio.lock); + devm_free_irq(&i2c->dev, gpio_to_irq(aw_haptic->irq_gpio), aw_haptic); + if (gpio_is_valid(aw_haptic->irq_gpio)) + devm_gpio_free(&i2c->dev, aw_haptic->irq_gpio); +#ifdef AW_SND_SOC_CODEC + snd_soc_unregister_codec(&i2c->dev); +#endif +#ifdef AW_TIKTAP + proc_remove(aw_haptic->aw_config_proc); + aw_haptic->aw_config_proc = NULL; + free_pages((unsigned long)aw_haptic->start_buf, AW_TIKTAP_MMAP_PAGE_ORDER); + aw_haptic->start_buf = NULL; +#endif + if (gpio_is_valid(aw_haptic->reset_gpio)) + devm_gpio_free(&i2c->dev, aw_haptic->reset_gpio); + + return 0; +} + +static int aw_i2c_suspend(struct device *dev) +{ + int ret = 0; + struct aw_haptic *aw_haptic = dev_get_drvdata(dev); + + mutex_lock(&aw_haptic->lock); + aw_haptic->func->play_stop(aw_haptic); + mutex_unlock(&aw_haptic->lock); + + return ret; +} + +static int aw_i2c_resume(struct device *dev) +{ + int ret = 0; + return ret; +} + +static SIMPLE_DEV_PM_OPS(aw_pm_ops, aw_i2c_suspend, aw_i2c_resume); + +static const struct i2c_device_id aw_i2c_id[] = { + {AW_I2C_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, aw_i2c_id); + +static const struct of_device_id aw_dt_match[] = { +#ifdef AW_DOUBLE + {.compatible = "awinic,haptic_hv_r"}, + {.compatible = "awinic,haptic_hv_l"}, +#else + {.compatible = "awinic,haptic_hv"}, +#endif + {}, +}; + +static struct i2c_driver aw_i2c_driver = { + .driver = { + .name = AW_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw_dt_match), +#ifdef CONFIG_PM_SLEEP + .pm = &aw_pm_ops, +#endif + }, + .probe = aw_i2c_probe, + .remove = aw_remove, + .id_table = aw_i2c_id, +}; + +static int __init aw_i2c_init(void) +{ + int ret = 0; + + pr_info("<%s>%s: aw_haptic driver version %s\n", AW_I2C_NAME, __func__, + HAPTIC_HV_DRIVER_VERSION); + ret = i2c_add_driver(&aw_i2c_driver); + if (ret) { + pr_err("<%s>%s: fail to add aw_haptic device into i2c\n", + AW_I2C_NAME, __func__); + return ret; + } + return 0; +} +module_init(aw_i2c_init); + +static void __exit aw_i2c_exit(void) +{ + i2c_del_driver(&aw_i2c_driver); +} +module_exit(aw_i2c_exit); + +MODULE_DESCRIPTION("AWINIC Haptic Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/awinic/haptic_hv/haptic_hv.h b/drivers/misc/awinic/haptic_hv/haptic_hv.h new file mode 100644 index 000000000000..9e5115e0af1c --- /dev/null +++ b/drivers/misc/awinic/haptic_hv/haptic_hv.h @@ -0,0 +1,743 @@ +#ifndef _HAPTIC_HV_H_ +#define _HAPTIC_HV_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/********************************************************* + * + * Haptic_HV CHIPID + * + *********************************************************/ +#define AW_REG_CHIPID (0x00) /* AW869X/XX */ +#define AW_REG_CHIPIDH (0x57) /* AW8692X */ +#define AW_REG_CHIPIDL (0x58) /* AW8692X */ +#define AW8695_CHIPID (0x95) +#define AW8697_CHIPID (0x97) +#define AW86905_CHIPID (0x05) +#define AW86907_CHIPID (0x04) +#define AW86915_CHIPID (0x07) +#define AW86917_CHIPID (0x06) +#define AW86715_CHIPID (0x7150) +#define AW86716_CHIPID (0x7170) +#define AW86717_CHIPID (0x7171) +#define AW86718_CHIPID (0x7180) +#define AW86925_CHIPID (0x9250) +#define AW86926_CHIPID (0x9260) +#define AW86927_CHIPID (0x9270) +#define AW86928_CHIPID (0x9280) + +/********************************************************* + * + * Marco + * + *********************************************************/ +#define AW_I2C_NAME "haptic_hv" +#define AW_I2C_RETRIES (5) +#define AW_I2C_RETRY_DELAY (2) +#define AW_READ_CHIPID_RETRIES (5) +#define AW_READ_CHIPID_RETRY_DELAY (2) +#define AW_SEQUENCER_SIZE (8) +#define AW_I2C_READ_MSG_NUM (2) +#define AW_I2C_BYTE_ONE (1) +#define AW_I2C_BYTE_TWO (2) +#define AW_I2C_BYTE_THREE (3) +#define AW_I2C_BYTE_FOUR (4) +#define AW_I2C_BYTE_FIVE (5) +#define AW_I2C_BYTE_SIX (6) +#define AW_I2C_BYTE_SEVEN (7) +#define AW_I2C_BYTE_EIGHT (8) + +#define AW_SEQUENCER_LOOP_SIZE (4) +#define AW_RAM_GET_F0_SEQ (8) +#define AW_RTP_NAME_MAX (64) +#define AW_PM_QOS_VALUE_VB (400) +#define AW_DRV2_LVL_MAX (0x7F) +#define AW_VBAT_REFER (4200) +#define AW_VBAT_MIN (3000) +#define AW_VBAT_MAX (4500) +#define AW_RAM_WORK_DELAY_INTERVAL (8000) +#define AW_OSC_TRIM_PARAM (50) +#define AW_OSC_CALI_ACCURACY (24) +#define AW_OSC_CALI_MAX_LENGTH (5100000) +#define AW_TRIG_NUM (3) +#define AW_RAMDATA_RD_BUFFER_SIZE (1024) +#define AW_RAMDATA_WR_BUFFER_SIZE (2048) +#define AW_EFFECT_ID_BOUNDS (4) +#define AW_EFFECT_NUMBER (3) +#define AW_GLBRD_STATE_MASK (15<<0) +#define AW_STATE_STANDBY (0x00) +#define AW_STATE_RTP (0x08) +#define AW_BIT_RESET (0xAA) +#define CPU_LATENCY_QOC_VALUE (0) + +#ifndef AW_DURATION_WAVESEQ_1 +#define AW_DURATION_WAVESEQ_1 (3) +#endif + +#ifndef AW_DURATION_WAVESEQ_2 +#define AW_DURATION_WAVESEQ_2 (2) +#endif + +#ifndef AW_DURATION_WAVESEQ_3 +#define AW_DURATION_WAVESEQ_3 (1) +#endif + +#ifndef AW_DURATION_WAVESEQ_4 +#define AW_DURATION_WAVESEQ_4 (4) +#endif + +#define AW_CHECK_RAM_DATA +#define AW_READ_BIN_FLEXBALLY +#define AW_LRA_F0_DEFAULT +#define AW_INPUT_FRAMEWORK +/* #define AW_TIKTAP */ +/* #define AW_DOUBLE */ +/* #define AW_DURATION_DECIDE_WAVEFORM */ +/* #define AW_OSC_MULTI_CALI */ +/* #define AW_BOOT_OSC_CALI */ +/* #define AW_ENABLE_RTP_PRINT_LOG */ +/* #define AW_ENABLE_PIN_CONTROL */ +/* #define AW_SND_SOC_CODEC */ +/* #define AW869X_MUL_GET_F0 */ +#define AW869X_DRIVER_ENABLE +#define AW869XX_DRIVER_ENABLE +#define AW8671X_DRIVER_ENABLE +#define AW8692X_DRIVER_ENABLE + +/********************************************************* + * + * Conditional Marco + * + *********************************************************/ + +#ifdef AW_TIKTAP +#define AW_TIKTAP_PROCNAME "tiktap_buf" +#define AW_TIKTAP_MMAP_PAGE_ORDER (2) +#define AW_TIKTAP_MMAP_BUF_SUM (16) +#define AW_TIKTAP_MMAP_BUF_SIZE (1000) + +#pragma pack(4) +struct mmap_buf_format { + uint8_t status; + uint8_t bit; + int16_t length; + + struct mmap_buf_format *kernel_next; + struct mmap_buf_format *user_next; + uint8_t reg_addr; + int8_t data[AW_TIKTAP_MMAP_BUF_SIZE]; +}; /* 1024 byte */ +#pragma pack() + +#define TIKTAP_IOCTL_GROUP 0xFF +#define TIKTAP_GET_F0 _IO(TIKTAP_IOCTL_GROUP, 0x01) +#define TIKTAP_GET_HWINFO _IO(TIKTAP_IOCTL_GROUP, 0x02) +#define TIKTAP_SET_FREQ _IO(TIKTAP_IOCTL_GROUP, 0x03) +#define TIKTAP_SETTING_GAIN _IO(TIKTAP_IOCTL_GROUP, 0x04) +#define TIKTAP_SETTING_SPEED _IO(TIKTAP_IOCTL_GROUP, 0x05) +#define TIKTAP_SETTING_BSTVOL _IO(TIKTAP_IOCTL_GROUP, 0x06) +#define TIKTAP_ON_MODE _IO(TIKTAP_IOCTL_GROUP, 0x07) +#define TIKTAP_OFF_MODE _IO(TIKTAP_IOCTL_GROUP, 0x08) +#define TIKTAP_RTP_MODE _IO(TIKTAP_IOCTL_GROUP, 0x09) +#define TIKTAP_RTP_IRQ_MODE _IO(TIKTAP_IOCTL_GROUP, 0x0A) +#define TIKTAP_STOP_MODE _IO(TIKTAP_IOCTL_GROUP, 0x0B) +#define TIKTAP_STOP_RTP_MODE _IO(TIKTAP_IOCTL_GROUP, 0x0C) +#define TIKTAP_WRITE_REG _IO(TIKTAP_IOCTL_GROUP, 0x0D) +#define TIKTAP_READ_REG _IO(TIKTAP_IOCTL_GROUP, 0x0E) +#define TIKTAP_BST_SWITCH _IO(TIKTAP_IOCTL_GROUP, 0x0F) +#define TIKTAP_GET_SPEED _IO(TIKTAP_IOCTL_GROUP, 0x10) + +enum { + MMAP_BUF_DATA_VALID = 0x55, + MMAP_BUF_DATA_FINISHED = 0xAA, + MMAP_BUF_DATA_INVALID = 0xFF, +}; + +#define TIkTAP_LEFT_STATUS_MASK (0x0F) +#define TIkTAP_LEFT_FINISHED_DONE (MMAP_BUF_DATA_FINISHED&0xF0) + +#define TIKTAP_RIGHT_STATUS_MASK (0xF0) +#define TIKTAP_RIGHT_FINISHED_DONE (MMAP_BUF_DATA_FINISHED&0x0F) + +#define TIKTAP_L_VALID_R_FINISHED ((MMAP_BUF_DATA_VALID&TIKTAP_RIGHT_STATUS_MASK)|TIKTAP_RIGHT_FINISHED_DONE) +#define TIKTAP_R_VALID_L_FINISHED ((MMAP_BUF_DATA_VALID&TIkTAP_LEFT_STATUS_MASK)|TIkTAP_LEFT_FINISHED_DONE) +#endif + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 4, 1) +#define TIMED_OUTPUT +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 1) +#define KERNEL_OVER_4_19 +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) +#define KERNEL_OVER_5_10 +#endif + +#ifdef TIMED_OUTPUT +#include <../../../drivers/staging/android/timed_output.h> +typedef struct timed_output_dev cdev_t; +#else +typedef struct led_classdev cdev_t; +#endif + +#ifdef KERNEL_OVER_4_19 +typedef struct snd_soc_component aw_snd_soc_codec_t; +typedef struct snd_soc_component_driver aw_snd_soc_codec_driver_t; +#else +typedef struct snd_soc_codec aw_snd_soc_codec_t; +typedef struct snd_soc_codec_driver aw_snd_soc_codec_driver_t; +#endif + +struct aw_componet_codec_ops { + aw_snd_soc_codec_t *(*aw_snd_soc_kcontrol_codec)(struct snd_kcontrol * + kcontrol); + void *(*aw_snd_soc_codec_get_drvdata)(aw_snd_soc_codec_t *codec); + int (*aw_snd_soc_add_codec_controls)(aw_snd_soc_codec_t *codec, + const struct snd_kcontrol_new *controls, uint32_t num_controls); + void (*aw_snd_soc_unregister_codec)(struct device *dev); + int (*aw_snd_soc_register_codec)(struct device *dev, + const aw_snd_soc_codec_driver_t *codec_drv, + struct snd_soc_dai_driver *dai_drv, int num_dai); +}; + +/********************************************************* + * + * AW869X + * + *********************************************************/ +#define AW869X_MUL_GET_F0_NUM (3) +#define AW869X_MUL_GET_F0_RANGE (100) +#define AW869X_BST_VOL_MIN (6000) +#define AW869X_BST_VOL_MAX (10500) +#define AW869X_BST_VOL_FARMULA(bst_vol) ((((bst_vol) - 6000) / 142) << 1) +#define AW869X_F0_FARMULA(f0_reg, coeff) (1000000000 / ((f0_reg) * (coeff))) +#define AW869X_LRA_FORMULA(lra_code) (298 * (lra_code)) +#define AW869X_VBAT_FORMULA(vbat_code) (6100 * (vbat_code) / 256) +#define AW869X_RAM_ADDR_H(base_addr) ((base_addr) >> 8) +#define AW869X_RAM_ADDR_L(base_addr) ((base_addr) & 0x00FF) +#define AW869X_FIFO_AE_ADDR_H(base_addr) (((base_addr) >> 1) >> 8) +#define AW869X_FIFO_AE_ADDR_L(base_addr) (((base_addr) >> 1) & 0x00ff) +#define AW869X_FIFO_AF_ADDR_H(base_addr) (((base_addr) - ((base_addr) >> 2)) >> 8) +#define AW869X_FIFO_AF_ADDR_L(base_addr) (((base_addr) - ((base_addr) >> 2)) & 0x00ff) + +/********************************************************* + * + * AW869XX + * + *********************************************************/ +#define AW869XX_BST_VOL_MIN (6000) +#define AW869XX_BST_VOL_MAX (10971) +#define AW869XX_DRV2_LVL_FARMULA(vrms) (199 * (vrms) / 3050) +#define AW869XX_BST_VOL_FARMULA(bst_vol) (((bst_vol) - 6000) * 1000 / 78893) +#define AW869XX_F0_FARMULA(f0_reg) (384000 * 10 / (f0_reg)) +#define AW869XX_LRA_FORMULA(lra_code) (((lra_code) * 678 * 1000) / \ + (1024 * 10)) +#define AW869XX_VBAT_FORMULA(vbat_code) (6100 * (vbat_code) / 1024) +#define AW869XX_RAM_ADDR_H(base_addr) ((base_addr) >> 8) +#define AW869XX_RAM_ADDR_L(base_addr) ((base_addr) & 0x00FF) +#define AW869XX_FIFO_AE_ADDR_H(base_addr) ((((base_addr) >> 1) >> 4) & 0xF0) +#define AW869XX_FIFO_AE_ADDR_L(base_addr) (((base_addr) >> 1) & 0x00ff) +#define AW869XX_FIFO_AF_ADDR_H(base_addr) ((((base_addr) - ((base_addr) >> 2)) >> 8) & 0x0F) +#define AW869XX_FIFO_AF_ADDR_L(base_addr) (((base_addr) - ((base_addr) >> 2)) & 0x00ff) +#define AW869XX_RATES SNDRV_PCM_RATE_8000_48000 +#define AW869XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/********************************************************* + * + * AW8671X + * + *********************************************************/ +#define AW8671X_DRV2_LVL_FARMULA(vrms) (199 * (vrms) / 4000) +#define AW8671X_F0_FARMULA(f0_reg) (384000 * 10 / (f0_reg)) +#define AW8671X_VBAT_FORMULA(vbat) (6050 * (vbat) / 4095) +#define AW8671X_LRA_FORMULA(lra, d2s_gain) ((164197 * (lra)) / (1000 * (d2s_gain))) +#define AW8671X_FIFO_AE_ADDR_H(base_addr) ((((base_addr) >> 1) >> 4) & 0xF0) +#define AW8671X_FIFO_AE_ADDR_L(base_addr) (((base_addr) >> 1) & 0x00ff) +#define AW8671X_FIFO_AF_ADDR_H(base_addr) (((base_addr) - ((base_addr) >> 2)) >> 8 & 0x0F) +#define AW8671X_FIFO_AF_ADDR_L(base_addr) (((base_addr) - ((base_addr) >> 2)) & 0x00ff) +#define AW8671X_RATES SNDRV_PCM_RATE_8000_48000 +#define AW8671X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + +/********************************************************* + * + * AW8692X + * + *********************************************************/ +#define AW8692X_BST_VOL_MIN (6000) +#define AW8692X_BST_VOL_MAX (11437) +#define AW8692X_BOOT_DETECTION (5) +#define AW8692X_DRV2_LVL_FARMULA(vrms) (199 * (vrms) / 4000) +#define AW8692X_BST_VOL_FARMULA(bst_vol) (((bst_vol) - 3500) * 10 / 625) +#define AW8692X_F0_FARMULA(f0_reg) (384000 * 10 / (f0_reg)) +#define AW8692X_LRA_FORMULA(lra, d2s_gain) ((6075 * 100 * (lra)) / \ + (1024 * (d2s_gain))) +#define AW8692X_VBAT_FORMULA(vbat) (5 * 1215 * (vbat) / 1024) +#define AW8692X_RAMADDR_H(base_addr) ((base_addr) >> 8) +#define AW8692X_RAMADDR_L(base_addr) ((base_addr) & 0x00FF) +#define AW8692X_BASEADDR_H(base_addr) ((base_addr) >> 8) +#define AW8692X_BASEADDR_L(base_addr) ((base_addr) & 0x00FF) +#define AW8692X_FIFO_AE_ADDR_H(base_addr) ((((base_addr) >> 1) >> 4) & 0xF0) +#define AW8692X_FIFO_AE_ADDR_L(base_addr) (((base_addr) >> 1) & 0x00ff) +#define AW8692X_FIFO_AF_ADDR_H(base_addr) ((((base_addr) - ((base_addr) >> 2)) >> 8) & 0x0F) +#define AW8692X_FIFO_AF_ADDR_L(base_addr) (((base_addr) - ((base_addr) >> 2)) & 0x00ff) + +#define AW_DRV_WIDTH_FARMULA(f0_pre, brk_gain, track_margain) (240000 / \ + (f0_pre) - 8 - (brk_gain) - (track_margain)) + +/********************************************************* + * + * Log Format + * + *********************************************************/ +#ifdef AW_DOUBLE +#define aw_err(format, ...) \ + pr_err("[haptic_hv]<%s>%s: " format "\n", aw_haptic->name, __func__, ##__VA_ARGS__) + +#define aw_info(format, ...) \ + pr_info("[haptic_hv]<%s>%s: " format "\n", aw_haptic->name, __func__, ##__VA_ARGS__) + +#define aw_dbg(format, ...) \ + pr_debug("[haptic_hv]<%s>%s: " format "\n", aw_haptic->name, __func__, ##__VA_ARGS__) +#else + +#define aw_err(format, ...) \ + pr_err("[%s][%04d]%s: " format "\n", AW_I2C_NAME, __LINE__, __func__, ##__VA_ARGS__) + +#define aw_info(format, ...) \ + pr_info("[%s][%04d]%s: " format "\n", AW_I2C_NAME, __LINE__, __func__, ##__VA_ARGS__) + +#define aw_dbg(format, ...) \ + pr_debug("[%s][%04d]%s: " format "\n", AW_I2C_NAME, __LINE__, __func__, ##__VA_ARGS__) +#endif +/********************************************************* + * + * Enum Define + * + *********************************************************/ +enum aw_haptic_flags { + AW_FLAG_NONR = 0, + AW_FLAG_SKIP_INTERRUPTS = 1, +}; + +enum aw_haptic_work_mode { + AW_STANDBY_MODE = 0, + AW_RAM_MODE = 1, + AW_RTP_MODE = 2, + AW_TRIG_MODE = 3, + AW_CONT_MODE = 4, + AW_RAM_LOOP_MODE = 5, +}; + +enum aw_haptic_irq_status { + AW_IRQ_ALMOST_EMPTY = 1, + AW_IRQ_ALMOST_FULL = 2, + AW_IRQ_BST_SCP = 3, + AW_IRQ_BST_OVP = 4, + AW_IRQ_UVLO = 5, + AW_IRQ_OCD = 6, + AW_IRQ_OT = 7, + AW_IRQ_LOW_VBAT = 8, + AW_IRQ_OV = 9, + AW_IRQ_DONE = 10, +}; + +enum aw_haptic_bst_mode { + AW_BST_BYPASS_MODE = 0, + AW_BST_BOOST_MODE = 1, +}; + +enum aw_haptic_bst_pc { + AW_BST_PC_L1 = 0, + AW_BST_PC_L2 = 1, +}; + +enum aw_haptic_cont_vbat_comp_mode { + AW_CONT_VBAT_SW_COMP_MODE = 0, + AW_CONT_VBAT_HW_COMP_MODE = 1, +}; + +enum aw_haptic_ram_vbat_comp_mode { + AW_RAM_VBAT_COMP_DISABLE = 0, + AW_RAM_VBAT_COMP_ENABLE = 1, +}; + +enum aw_haptic_f0_flag { + AW_LRA_F0 = 0, + AW_CALI_F0 = 1, +}; + +enum aw_haptic_pwm_mode { + AW_PWM_48K = 0, + AW_PWM_24K = 1, + AW_PWM_12K = 2, +}; + +enum aw_haptic_play { + AW_PLAY_NULL = 0, + AW_PLAY_ENABLE = 1, + AW_PLAY_STOP = 2, + AW_PLAY_GAIN = 8, +}; + +enum aw_haptic_cmd { + AW_CMD_NULL = 0, + AW_CMD_ENABLE = 1, + AW_CMD_HAPTIC = 0x0f, + AW_CMD_TP = 0x10, + AW_CMD_R_EN = 0x40, + AW_CMD_L_EN = 0x80, + AW_CMD_SYS = 0xf0, + AW_CMD_STOP = 255, +}; + +enum aw_haptic_cali_lra { + AW_WRITE_ZERO = 0, + AW_F0_CALI_LRA = 1, + AW_OSC_CALI_LRA = 2, +}; + +enum aw_haptic_awrw_flag { + AW_SEQ_WRITE = 0, + AW_SEQ_READ = 1, +}; + +/********************************************************* + * + * Enum aw8671x + * + *********************************************************/ +enum aw8671x_haptic_chargepump { + AW8671X_CHARGEPUMP_CODE0 = 4950, + AW8671X_CHARGEPUMP_CODE1 = 5316, + AW8671X_CHARGEPUMP_CODE2 = 5661, + AW8671X_CHARGEPUMP_CODE3 = 5827, + AW8671X_CHARGEPUMP_CODE4 = 6336, +}; + +/********************************************************* + * + * Enum aw8692x + * + *********************************************************/ +enum aw8692x_haptic_rck_fre { + AW8692X_RCK_FRE_24K, + AW8692X_RCK_FRE_32K, + AW8692X_RCK_FRE_48K, + AW8692X_RCK_FRE_96K, +}; + +enum aw8692x_haptic_trig { + AW8692X_TRIG1, + AW8692X_TRIG2, + AW8692X_TRIG3, +}; + +/********************************************************* + * + * Struct Define + * + *********************************************************/ +struct trig { + /* AW869X */ + uint8_t enable; + uint8_t dual_edge; + uint8_t frist_seq; + uint8_t second_seq; + uint8_t default_level; + + /* AW869XX AW8671X AW8692X */ + uint8_t trig_brk; + uint8_t trig_bst; + uint8_t trig_level; + uint8_t trig_polar; + uint8_t pos_enable; + uint8_t neg_enable; + uint8_t pos_sequence; + uint8_t neg_sequence; +}; + +struct aw_haptic_ram { + uint32_t len; + uint32_t check_sum; + uint32_t base_addr; + uint8_t ram_num; + uint8_t version; + uint8_t ram_shift; + uint8_t baseaddr_shift; +}; + +struct aw_haptic_ctr { + uint8_t cnt; + uint8_t cmd; + uint8_t play; + uint8_t loop; + uint8_t gain; + uint8_t wavseq; + struct list_head list; +}; + +struct aw_i2c_info { + uint32_t flag; + uint32_t reg_num; + uint8_t *reg_data; +}; + +struct aw_haptic_audio { + int tz_num; + int tz_cnt_thr; + int tz_cnt_max; + int tz_high_num; + int delay_val; + int timer_val; + uint32_t uevent_report_flag; + uint32_t hap_cnt_outside_tz; + uint32_t hap_cnt_max_outside_tz; + struct mutex lock; + struct hrtimer timer; + struct work_struct work; + struct list_head list; + struct list_head ctr_list; + struct aw_haptic_ctr ctr; +}; + +struct aw_haptic_dts_info { + /* COMMON */ + uint8_t mode; + uint8_t f0_cali_percent; + uint8_t duration_time[3]; + uint32_t f0_pre; + uint32_t bst_vol_ram; + uint32_t bst_vol_rtp; + uint32_t bst_vol_default; + + /* AW869X */ + uint8_t tset; + uint8_t r_spare; + uint8_t cont_drv_lvl; + uint8_t cont_num_brk; + uint8_t cont_drv_lvl_ov; + uint8_t bstdbg[6]; + uint8_t bemf_config[4]; + uint8_t f0_trace_parameter[4]; + uint32_t f0_coeff; + uint32_t cont_td; + uint32_t cont_zc_thr; + + /* AW869XX */ + uint8_t bstcfg[5]; + uint8_t sine_array[4]; + + /* AW869XX AW8671X AW8692X */ + uint8_t d2s_gain; + uint8_t brk_bst_md; + uint8_t gain_bypass; + uint8_t cont_tset; + uint8_t cont_drv1_lvl; + uint8_t cont_drv2_lvl; + uint8_t cont_wait_num; + uint8_t cont_brk_time; + uint8_t cont_bemf_set; + uint8_t cont_brk_gain; + uint8_t cont_drv1_time; + uint8_t cont_drv2_time; + uint8_t cont_track_margin; + uint8_t cont_bst_brk_gain; + uint8_t trig_cfg[24]; + uint32_t cont_lra_vrms; + bool is_enabled_auto_bst; + bool is_enabled_i2s; + bool is_enabled_one_wire; +}; + +struct aw_haptic { + /* AW869X */ + uint32_t interval_us; + + /* AW869XX */ + bool i2s_config; + bool rtp_init; + bool ram_init; + bool dual_flag; + aw_snd_soc_codec_t *codec; + + /* COMMON */ + uint8_t flags; + uint8_t bst_pc; + uint8_t play_mode; + uint8_t auto_boost; + uint8_t max_pos_beme; + uint8_t max_neg_beme; + uint8_t activate_mode; + uint8_t ram_vbat_comp; + uint8_t seq[AW_SEQUENCER_SIZE]; + uint8_t loop[AW_SEQUENCER_SIZE]; + uint8_t name[15]; + + int vmax; + int gain; + int rate; + int width; + int state; + int index; + int chipid; + int sysclk; + int irq_gpio; + int duration; + int amplitude; + int reset_gpio; + + uint32_t f0; + uint32_t lra; + uint32_t vbat; + uint32_t pre_f0; + uint32_t rtp_cnt; + uint32_t rtp_len; + uint32_t gun_type; + uint32_t bullet_nr; + uint32_t theory_time; + uint32_t f0_cali_data; + uint32_t rtp_file_num; + uint32_t timeval_flags; + uint32_t osc_cali_data; + uint64_t microsecond; + + cdev_t vib_dev; + + ktime_t kend; + ktime_t kstart; + ktime_t kcurrent_time; + ktime_t kpre_enter_time; +#ifdef AW_TIKTAP + bool tiktap_ready; + bool vib_stop_flag; + bool tiktap_stop_flag; + struct work_struct tiktap_work; + struct proc_dir_entry *aw_config_proc; + struct mmap_buf_format *start_buf; +#endif + struct device *dev; + struct i2c_client *i2c; + struct input_dev *input_dev; + struct semaphore sema; + struct work_struct gain_work; + struct work_struct input_vib_work; + struct mutex lock; + struct hrtimer timer; + struct mutex rtp_lock; + struct work_struct rtp_work; + struct delayed_work ram_work; + struct work_struct vibrator_work; + struct aw_haptic_ram ram; + struct aw_haptic_dts_info info; + struct aw_haptic_audio haptic_audio; + struct pinctrl *pinctrl; + struct pinctrl_state *pinctrl_state; + struct aw_haptic_func *func; + struct aw_i2c_info i2c_info; + struct trig trig[AW_TRIG_NUM]; + struct aw_haptic_container *aw_rtp; + struct pm_qos_request aw_pm_qos_req_vb; +}; + +struct aw_haptic_container { + int len; + uint8_t data[]; +}; + +struct aw_haptic_func { + int (*get_f0)(struct aw_haptic *); + int (*ram_get_f0)(struct aw_haptic *); + int (*creat_node)(struct aw_haptic *); + int (*get_irq_state)(struct aw_haptic *); + int (*check_qualify)(struct aw_haptic *); + int (*judge_rtp_going)(struct aw_haptic *); + int (*container_update)(struct aw_haptic *, + struct aw_haptic_container *); + void (*play_stop)(struct aw_haptic *); + void (*get_vbat)(struct aw_haptic *); + void (*cont_config)(struct aw_haptic *); + void (*offset_cali)(struct aw_haptic *); + void (*play_go)(struct aw_haptic *, bool); + void (*ram_init)(struct aw_haptic *, bool); + void (*set_bst_peak_cur)(struct aw_haptic *); + void (*get_lra_resistance)(struct aw_haptic *); + void (*set_pwm)(struct aw_haptic *, uint8_t); + void (*set_gain)(struct aw_haptic *, uint8_t); + void (*play_mode)(struct aw_haptic *, uint8_t); + void (*set_bst_vol)(struct aw_haptic *, uint32_t); + void (*set_repeat_seq)(struct aw_haptic *, uint8_t); + void (*auto_bst_enable)(struct aw_haptic *, uint8_t); + void (*vbat_mode_config)(struct aw_haptic *, uint8_t); + void (*set_wav_seq)(struct aw_haptic *, uint8_t, uint8_t); + void (*set_wav_loop)(struct aw_haptic *, uint8_t, uint8_t); + void (*set_rtp_data)(struct aw_haptic *, uint8_t *, uint32_t); + void (*protect_config)(struct aw_haptic *, uint8_t, uint8_t); + void (*parse_dt)(struct device *, struct aw_haptic *, + struct device_node *); + void (*trig_init)(struct aw_haptic *); + void (*irq_clear)(struct aw_haptic *); + void (*set_ram_addr)(struct aw_haptic *); + void (*misc_para_init)(struct aw_haptic *); + void (*interrupt_setup)(struct aw_haptic *); + void (*set_rtp_aei)(struct aw_haptic *, bool); + void (*upload_lra)(struct aw_haptic *, uint32_t); + void (*get_wav_seq)(struct aw_haptic *, uint32_t); + void (*get_ram_data)(struct aw_haptic *, char *); + void (*bst_mode_config)(struct aw_haptic *, uint8_t); + void (*get_first_wave_addr)(struct aw_haptic *, uint8_t *); + size_t (*get_wav_loop)(struct aw_haptic *, char *); + ssize_t (*get_reg)(struct aw_haptic *, ssize_t, char *); + uint8_t (*get_prctmode)(struct aw_haptic *); + uint8_t (*get_trim_lra)(struct aw_haptic *); + uint8_t (*get_glb_state)(struct aw_haptic *); + uint8_t (*get_osc_status)(struct aw_haptic *); + uint8_t (*rtp_get_fifo_afs)(struct aw_haptic *); + uint8_t (*rtp_get_fifo_aes)(struct aw_haptic *); + uint64_t (*get_theory_time)(struct aw_haptic *); + +#ifdef AW_SND_SOC_CODEC + int (*snd_soc_init)(struct device *); +#endif +}; + +/********************************************************* + * + * Function Call + * + *********************************************************/ +#ifdef AW869X_DRIVER_ENABLE +extern struct aw_haptic_func aw869x_func_list; +#endif + +#ifdef AW869XX_DRIVER_ENABLE +extern struct aw_haptic_func aw869xx_func_list; +#endif + +#ifdef AW8671X_DRIVER_ENABLE +extern struct aw_haptic_func aw8671x_func_list; +#endif + +#ifdef AW8692X_DRIVER_ENABLE +extern struct aw_haptic_func aw8692x_func_list; +#endif + +extern int haptic_hv_i2c_reads(struct aw_haptic *, uint8_t, uint8_t *, uint32_t); +extern int haptic_hv_i2c_writes(struct aw_haptic *, uint8_t, uint8_t *, uint32_t); +extern int haptic_hv_i2c_write_bits(struct aw_haptic *, uint8_t, uint32_t, uint8_t); + +#endif diff --git a/drivers/misc/awinic/haptic_hv/haptic_hv_reg.h b/drivers/misc/awinic/haptic_hv/haptic_hv_reg.h new file mode 100644 index 000000000000..e439981498c1 --- /dev/null +++ b/drivers/misc/awinic/haptic_hv/haptic_hv_reg.h @@ -0,0 +1,2174 @@ +#ifndef _HAPTIC_HV_REG_H_ +#define _HAPTIC_HV_REG_H_ + +/********************************************************* + * + * AW869X Register List + * + *********************************************************/ +#define AW869X_REG_ID (0x00) +#define AW869X_REG_SYSST (0x01) +#define AW869X_REG_SYSINT (0x02) +#define AW869X_REG_SYSINTM (0x03) +#define AW869X_REG_SYSCTRL (0x04) +#define AW869X_REG_GO (0x05) +#define AW869X_REG_RTP_DATA (0x06) +#define AW869X_REG_WAVSEQ1 (0x07) +#define AW869X_REG_WAVSEQ2 (0x08) +#define AW869X_REG_WAVSEQ3 (0x09) +#define AW869X_REG_WAVSEQ4 (0x0a) +#define AW869X_REG_WAVSEQ5 (0x0b) +#define AW869X_REG_WAVSEQ6 (0x0c) +#define AW869X_REG_WAVSEQ7 (0x0d) +#define AW869X_REG_WAVSEQ8 (0x0e) +#define AW869X_REG_WAVLOOP1 (0x0f) +#define AW869X_REG_WAVLOOP2 (0x10) +#define AW869X_REG_WAVLOOP3 (0x11) +#define AW869X_REG_WAVLOOP4 (0x12) +#define AW869X_REG_MAINLOOP (0x13) +#define AW869X_REG_TRG1_WAV_P (0x14) +#define AW869X_REG_TRG2_WAV_P (0x15) +#define AW869X_REG_TRG3_WAV_P (0x16) +#define AW869X_REG_TRG1_WAV_N (0x17) +#define AW869X_REG_TRG2_WAV_N (0x18) +#define AW869X_REG_TRG3_WAV_N (0x19) +#define AW869X_REG_TRG_CFG1 (0x1b) +#define AW869X_REG_TRG_CFG2 (0x1c) +#define AW869X_REG_DBGCTRL (0x20) +#define AW869X_REG_BASE_ADDRH (0x21) +#define AW869X_REG_BASE_ADDRL (0x22) +#define AW869X_REG_FIFO_AEH (0x23) +#define AW869X_REG_FIFO_AEL (0x24) +#define AW869X_REG_FIFO_AFH (0x25) +#define AW869X_REG_FIFO_AFL (0x26) +#define AW869X_REG_DATCTRL (0x2b) +#define AW869X_REG_PWMPRC (0x2d) +#define AW869X_REG_PWMDBG (0x2e) +#define AW869X_REG_DBGSTAT (0x30) +#define AW869X_REG_BSTDBG1 (0x31) +#define AW869X_REG_BSTDBG2 (0x32) +#define AW869X_REG_BSTDBG3 (0x33) +#define AW869X_REG_BSTCFG (0x34) +#define AW869X_REG_ANADBG (0x35) +#define AW869X_REG_ANACTRL (0x36) +#define AW869X_REG_DATDBG (0x39) +#define AW869X_REG_BSTDBG4 (0x3a) +#define AW869X_REG_PRLVL (0x3e) +#define AW869X_REG_PRTIME (0x3f) +#define AW869X_REG_RAMADDRH (0x40) +#define AW869X_REG_RAMADDRL (0x41) +#define AW869X_REG_RAMDATA (0x42) +#define AW869X_REG_GLB_STATE (0x46) +#define AW869X_REG_BST_AUTO (0x47) +#define AW869X_REG_CONT_CTRL (0x48) +#define AW869X_REG_F_PRE_H (0x49) +#define AW869X_REG_F_PRE_L (0x4a) +#define AW869X_REG_TD_H (0x4b) +#define AW869X_REG_TD_L (0x4c) +#define AW869X_REG_TSET (0x4d) +#define AW869X_REG_TRIM_LRA (0x5b) +#define AW869X_REG_R_SPARE (0x5d) +#define AW869X_REG_D2SCFG (0x5e) +#define AW869X_REG_DETCTRL (0x5f) +#define AW869X_REG_RLDET (0x60) +#define AW869X_REG_OSDET (0x61) +#define AW869X_REG_VBATDET (0x62) +#define AW869X_REG_ADCTEST (0x66) +#define AW869X_REG_F_LRA_F0_H (0x68) +#define AW869X_REG_F_LRA_F0_L (0x69) +#define AW869X_REG_F_LRA_CONT_H (0x6a) +#define AW869X_REG_F_LRA_CONT_L (0x6b) +#define AW869X_REG_WAIT_VOL_MP (0x6d) +#define AW869X_REG_WAIT_VOL_MN (0x6f) +#define AW869X_REG_ZC_THRSH_H (0x72) +#define AW869X_REG_ZC_THRSH_L (0x73) +#define AW869X_REG_BEMF_VTHH_H (0x74) +#define AW869X_REG_BEMF_VTHH_L (0x75) +#define AW869X_REG_BEMF_VTHL_H (0x76) +#define AW869X_REG_BEMF_VTHL_L (0x77) +#define AW869X_REG_BEMF_NUM (0x78) +#define AW869X_REG_DRV_TIME (0x79) +#define AW869X_REG_TIME_NZC (0x7a) +#define AW869X_REG_DRV_LVL (0x7b) +#define AW869X_REG_DRV_LVL_OV (0x7c) +#define AW869X_REG_NUM_F0_1 (0x7d) +#define AW869X_REG_NUM_F0_2 (0x7e) +#define AW869X_REG_NUM_F0_3 (0x7f) + +/********************************************************* + * + * AW869XX Register List + * + *********************************************************/ +#define AW869XX_REG_ID (0x00) +#define AW869XX_REG_SYSST (0x01) +#define AW869XX_REG_SYSINT (0x02) +#define AW869XX_REG_SYSINTM (0x03) +#define AW869XX_REG_SYSST2 (0x04) +#define AW869XX_REG_SYSER (0x05) +#define AW869XX_REG_PLAYCFG1 (0x06) +#define AW869XX_REG_PLAYCFG2 (0x07) +#define AW869XX_REG_PLAYCFG3 (0x08) +#define AW869XX_REG_PLAYCFG4 (0x09) +#define AW869XX_REG_WAVCFG1 (0x0A) +#define AW869XX_REG_WAVCFG2 (0x0B) +#define AW869XX_REG_WAVCFG3 (0x0C) +#define AW869XX_REG_WAVCFG4 (0x0D) +#define AW869XX_REG_WAVCFG5 (0x0E) +#define AW869XX_REG_WAVCFG6 (0x0F) +#define AW869XX_REG_WAVCFG7 (0x10) +#define AW869XX_REG_WAVCFG8 (0x11) +#define AW869XX_REG_WAVCFG9 (0x12) +#define AW869XX_REG_WAVCFG10 (0x13) +#define AW869XX_REG_WAVCFG11 (0x14) +#define AW869XX_REG_WAVCFG12 (0x15) +#define AW869XX_REG_WAVCFG13 (0x16) +#define AW869XX_REG_CONTCFG1 (0x18) +#define AW869XX_REG_CONTCFG2 (0x19) +#define AW869XX_REG_CONTCFG3 (0x1A) +#define AW869XX_REG_CONTCFG4 (0x1B) +#define AW869XX_REG_CONTCFG5 (0x1C) +#define AW869XX_REG_CONTCFG6 (0x1D) +#define AW869XX_REG_CONTCFG7 (0x1E) +#define AW869XX_REG_CONTCFG8 (0x1F) +#define AW869XX_REG_CONTCFG9 (0x20) +#define AW869XX_REG_CONTCFG10 (0x21) +#define AW869XX_REG_CONTCFG11 (0x22) +#define AW869XX_REG_CONTCFG13 (0x24) +#define AW869XX_REG_CONTRD14 (0x25) +#define AW869XX_REG_CONTRD15 (0x26) +#define AW869XX_REG_CONTRD16 (0x27) +#define AW869XX_REG_CONTRD17 (0x28) +#define AW869XX_REG_RTPCFG1 (0x2D) +#define AW869XX_REG_RTPCFG2 (0x2E) +#define AW869XX_REG_RTPCFG3 (0x2F) +#define AW869XX_REG_RTPCFG4 (0x30) +#define AW869XX_REG_RTPCFG5 (0x31) +#define AW869XX_REG_RTPDATA (0x32) +#define AW869XX_REG_TRGCFG1 (0x33) +#define AW869XX_REG_TRGCFG2 (0x34) +#define AW869XX_REG_TRGCFG3 (0x35) +#define AW869XX_REG_TRGCFG4 (0x36) +#define AW869XX_REG_TRGCFG5 (0x37) +#define AW869XX_REG_TRGCFG6 (0x38) +#define AW869XX_REG_TRGCFG7 (0x39) +#define AW869XX_REG_TRGCFG8 (0x3A) +#define AW869XX_REG_GLBCFG2 (0x3C) +#define AW869XX_REG_GLBCFG4 (0x3E) +#define AW869XX_REG_GLBRD5 (0x3F) +#define AW869XX_REG_RAMADDRH (0x40) +#define AW869XX_REG_RAMADDRL (0x41) +#define AW869XX_REG_RAMDATA (0x42) +#define AW869XX_REG_SYSCTRL1 (0x43) +#define AW869XX_REG_SYSCTRL2 (0x44) +#define AW869XX_REG_SYSCTRL3 (0x45) +#define AW869XX_REG_SYSCTRL4 (0x46) +#define AW869XX_REG_SYSCTRL5 (0x47) +#define AW869XX_REG_SYSCTRL6 (0x48) +#define AW869XX_REG_SYSCTRL7 (0x49) +#define AW869XX_REG_I2SCFG1 (0x4A) +#define AW869XX_REG_I2SCFG2 (0x4B) +#define AW869XX_REG_PWMCFG1 (0x4C) +#define AW869XX_REG_PWMCFG3 (0x4E) +#define AW869XX_REG_PWMCFG4 (0x4F) +#define AW869XX_REG_DETCFG1 (0x51) +#define AW869XX_REG_DETCFG2 (0x52) +#define AW869XX_REG_DET_RL (0x53) +#define AW869XX_REG_DET_VBAT (0x55) +#define AW869XX_REG_DET_LO (0x57) +#define AW869XX_REG_TRIMCFG3 (0x5A) +#define AW869XX_REG_RDATA_A (0x64) +#define AW869XX_REG_IOCFG1 (0x6B) +#define AW869XX_REG_BSTCFG1 (0x6D) +#define AW869XX_REG_BSTCFG2 (0x6E) +#define AW869XX_REG_BSTCFG3 (0x6F) +#define AW869XX_REG_BSTCFG4 (0x70) +#define AW869XX_REG_BSTCFG5 (0x71) +#define AW869XX_REG_D2SCFG1 (0x76) + +/********************************************************* + * + * AW8671X Register List + * + *********************************************************/ +#define AW8671X_REG_RSTCFG (0x00) +#define AW8671X_REG_SYSST (0x01) +#define AW8671X_REG_SYSINT (0x02) +#define AW8671X_REG_SYSINTM (0x03) +#define AW8671X_REG_SYSST2 (0x04) +#define AW8671X_REG_SYSER (0x05) +#define AW8671X_REG_PLAYCFG1 (0x06) +#define AW8671X_REG_PLAYCFG2 (0x07) +#define AW8671X_REG_PLAYCFG3 (0x08) +#define AW8671X_REG_PLAYCFG4 (0x09) +#define AW8671X_REG_WAVCFG1 (0x0A) +#define AW8671X_REG_WAVCFG2 (0x0B) +#define AW8671X_REG_WAVCFG3 (0x0C) +#define AW8671X_REG_WAVCFG4 (0x0D) +#define AW8671X_REG_WAVCFG5 (0x0E) +#define AW8671X_REG_WAVCFG6 (0x0F) +#define AW8671X_REG_WAVCFG7 (0x10) +#define AW8671X_REG_WAVCFG8 (0x11) +#define AW8671X_REG_WAVCFG9 (0x12) +#define AW8671X_REG_WAVCFG10 (0x13) +#define AW8671X_REG_WAVCFG11 (0x14) +#define AW8671X_REG_WAVCFG12 (0x15) +#define AW8671X_REG_WAVCFG13 (0x16) +#define AW8671X_REG_CONTCFG1 (0x18) +#define AW8671X_REG_CONTCFG2 (0x19) +#define AW8671X_REG_CONTCFG3 (0x1A) +#define AW8671X_REG_CONTCFG4 (0x1B) +#define AW8671X_REG_CONTCFG5 (0x1C) +#define AW8671X_REG_CONTCFG6 (0x1D) +#define AW8671X_REG_CONTCFG7 (0x1E) +#define AW8671X_REG_CONTCFG8 (0x1F) +#define AW8671X_REG_CONTCFG9 (0x20) +#define AW8671X_REG_CONTCFG10 (0x21) +#define AW8671X_REG_CONTCFG11 (0x22) +#define AW8671X_REG_CONTCFG13 (0x24) +#define AW8671X_REG_CONTCFG14 (0x25) +#define AW8671X_REG_CONTCFG15 (0x26) +#define AW8671X_REG_CONTCFG16 (0x27) +#define AW8671X_REG_CONTCFG17 (0x28) +#define AW8671X_REG_RTPCFG1 (0x2D) +#define AW8671X_REG_RTPCFG2 (0x2E) +#define AW8671X_REG_RTPCFG3 (0x2F) +#define AW8671X_REG_RTPCFG4 (0x30) +#define AW8671X_REG_RTPCFG5 (0x31) +#define AW8671X_REG_RTPDATA (0X32) +#define AW8671X_REG_TRGCFG1 (0x33) +#define AW8671X_REG_TRGCFG2 (0x34) +#define AW8671X_REG_TRGCFG3 (0x35) +#define AW8671X_REG_TRGCFG4 (0x36) +#define AW8671X_REG_TRGCFG5 (0x37) +#define AW8671X_REG_TRGCFG6 (0x38) +#define AW8671X_REG_TRGCFG7 (0x39) +#define AW8671X_REG_TRGCFG8 (0x3A) +#define AW8671X_REG_GLBCFG2 (0x3C) +#define AW8671X_REG_GLBCFG4 (0x3E) +#define AW8671X_REG_GLBRD5 (0x3F) +#define AW8671X_REG_RAMADDRH (0x40) +#define AW8671X_REG_RAMADDRL (0x41) +#define AW8671X_REG_RAMDATA (0x42) +#define AW8671X_REG_SYSCTRL1 (0x43) +#define AW8671X_REG_SYSCTRL2 (0x44) +#define AW8671X_REG_SYSCTRL3 (0x45) +#define AW8671X_REG_SYSCTRL4 (0x46) +#define AW8671X_REG_I2SCFG1 (0x47) +#define AW8671X_REG_I2SCFG2 (0x48) +#define AW8671X_REG_I2SCFG3 (0x49) +#define AW8671X_REG_I2SCFG4 (0x4A) +#define AW8671X_REG_PWMCFG1 (0x4C) +#define AW8671X_REG_PWMCFG2 (0x4D) +#define AW8671X_REG_PWMCFG3 (0x4E) +#define AW8671X_REG_PWMCFG4 (0x4F) +#define AW8671X_REG_VBATCTRL (0x50) +#define AW8671X_REG_DETCFG1 (0x51) +#define AW8671X_REG_DETCFG3 (0x52) +#define AW8671X_REG_DETRD1 (0x54) +#define AW8671X_REG_DETRD2 (0x55) +#define AW8671X_REG_DETRD3 (0x56) +#define AW8671X_REG_IDH (0x57) +#define AW8671X_REG_IDL (0x58) +#define AW8671X_REG_TRIMCFG2 (0x5A) +#define AW8671X_REG_EFCFG5 (0x61) +#define AW8671X_REG_ANACFG14 (0x73) + +/********************************************************* + * + * AW8692X Register List + * + *********************************************************/ +#define AW8692X_REG_RSTCFG (0x00) +#define AW8692X_REG_SYSST (0x01) +#define AW8692X_REG_SYSINT (0x02) +#define AW8692X_REG_SYSINTM (0x03) +#define AW8692X_REG_SYSST2 (0x04) +#define AW8692X_REG_SYSER (0x05) +#define AW8692X_REG_PLAYCFG1 (0x06) +#define AW8692X_REG_PLAYCFG2 (0x07) +#define AW8692X_REG_PLAYCFG3 (0x08) +#define AW8692X_REG_PLAYCFG4 (0x09) +#define AW8692X_REG_WAVCFG1 (0x0A) +#define AW8692X_REG_WAVCFG2 (0x0B) +#define AW8692X_REG_WAVCFG3 (0x0C) +#define AW8692X_REG_WAVCFG4 (0x0D) +#define AW8692X_REG_WAVCFG5 (0x0E) +#define AW8692X_REG_WAVCFG6 (0x0F) +#define AW8692X_REG_WAVCFG7 (0x10) +#define AW8692X_REG_WAVCFG8 (0x11) +#define AW8692X_REG_WAVCFG9 (0x12) +#define AW8692X_REG_WAVCFG10 (0x13) +#define AW8692X_REG_WAVCFG11 (0x14) +#define AW8692X_REG_WAVCFG12 (0x15) +#define AW8692X_REG_WAVCFG13 (0x16) +#define AW8692X_REG_CONTCFG1 (0x18) +#define AW8692X_REG_CONTCFG2 (0x19) +#define AW8692X_REG_CONTCFG3 (0x1A) +#define AW8692X_REG_CONTCFG4 (0x1B) +#define AW8692X_REG_CONTCFG5 (0x1C) +#define AW8692X_REG_CONTCFG6 (0x1D) +#define AW8692X_REG_CONTCFG7 (0x1E) +#define AW8692X_REG_CONTCFG8 (0x1F) +#define AW8692X_REG_CONTCFG9 (0x20) +#define AW8692X_REG_CONTCFG10 (0x21) +#define AW8692X_REG_CONTCFG11 (0x22) +#define AW8692X_REG_CONTCFG12 (0x23) +#define AW8692X_REG_CONTCFG13 (0x24) +#define AW8692X_REG_CONTCFG14 (0x25) +#define AW8692X_REG_CONTCFG15 (0x26) +#define AW8692X_REG_CONTCFG16 (0x27) +#define AW8692X_REG_CONTCFG17 (0x28) +#define AW8692X_REG_CONTCFG18 (0x29) +#define AW8692X_REG_CONTCFG19 (0x2A) +#define AW8692X_REG_CONTCFG20 (0x2B) +#define AW8692X_REG_CONTCFG21 (0x2C) +#define AW8692X_REG_RTPCFG1 (0x2D) +#define AW8692X_REG_RTPCFG2 (0x2E) +#define AW8692X_REG_RTPCFG3 (0x2F) +#define AW8692X_REG_RTPCFG4 (0x30) +#define AW8692X_REG_RTPCFG5 (0x31) +#define AW8692X_REG_RTPDATA (0X32) +#define AW8692X_REG_TRGCFG1 (0x33) +#define AW8692X_REG_TRGCFG2 (0x34) +#define AW8692X_REG_TRGCFG3 (0x35) +#define AW8692X_REG_TRGCFG4 (0x36) +#define AW8692X_REG_TRGCFG5 (0x37) +#define AW8692X_REG_TRGCFG6 (0x38) +#define AW8692X_REG_TRGCFG7 (0x39) +#define AW8692X_REG_TRGCFG8 (0x3A) +#define AW8692X_REG_GLBCFG1 (0x3B) +#define AW8692X_REG_GLBCFG2 (0x3C) +#define AW8692X_REG_GLBCFG3 (0x3D) +#define AW8692X_REG_GLBCFG4 (0x3E) +#define AW8692X_REG_GLBRD5 (0x3F) +#define AW8692X_REG_RAMADDRH (0x40) +#define AW8692X_REG_RAMADDRL (0x41) +#define AW8692X_REG_RAMDATA (0x42) +#define AW8692X_REG_SYSCTRL1 (0x43) +#define AW8692X_REG_SYSCTRL2 (0x44) +#define AW8692X_REG_SYSCTRL3 (0x45) +#define AW8692X_REG_SYSCTRL4 (0x46) +#define AW8692X_REG_SYSCTRL5 (0x47) +#define AW8692X_REG_PWMCFG1 (0x48) +#define AW8692X_REG_PWMCFG2 (0x49) +#define AW8692X_REG_PWMCFG3 (0x4A) +#define AW8692X_REG_PWMCFG4 (0x4B) +#define AW8692X_REG_VBATCTRL (0x4C) +#define AW8692X_REG_DETCFG1 (0x4D) +#define AW8692X_REG_DETCFG2 (0x4E) +#define AW8692X_REG_DETRD1 (0x4F) +#define AW8692X_REG_DETRD2 (0x50) +#define AW8692X_REG_DETRD3 (0x51) +#define AW8692X_REG_TRIMCFG1 (0x52) +#define AW8692X_REG_TRIMCFG2 (0x53) +#define AW8692X_REG_TRIMCFG3 (0x54) +#define AW8692X_REG_TRIMCFG4 (0x55) +#define AW8692X_REG_IDH (0x57) +#define AW8692X_REG_IDL (0x58) +#define AW8692X_REG_AUTOSIN1 (0x59) +#define AW8692X_REG_AUTOSIN2 (0x5A) +#define AW8692X_REG_TMCFG (0x5B) +#define AW8692X_REG_EFCFG1 (0x5C) +#define AW8692X_REG_EFCFG6 (0x61) +#define AW8692X_REG_EFCFG7 (0x62) +#define AW8692X_REG_EFCFG8 (0x63) +#define AW8692X_REG_EFCFG9 (0x64) +#define AW8692X_REG_ANACFG2 (0x67) +#define AW8692X_REG_ANACFG11 (0x70) +#define AW8692X_REG_ANACFG12 (0x71) +#define AW8692X_REG_ANACFG13 (0x72) +#define AW8692X_REG_ANACFG15 (0x74) +#define AW8692X_REG_ANACFG16 (0x75) +#define AW8692X_REG_ANACFG20 (0x79) +#define AW8692X_REG_ANACFG22 (0x7C) + +/********************************************************* + * + * Register Access + * + *********************************************************/ +#define REG_NONE_ACCESS (0) +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) +#define AW_HAPTIC_REG_MAX (0xff) + +/********************************************************* + * + * AW869X Register Detail + * + *********************************************************/ +/* SYSST: reg0x01 */ +#define AW869X_BIT_SYSST_BSTERRS (1<<7) +#define AW869X_BIT_SYSST_OVS (1<<6) +#define AW869X_BIT_SYSST_UVLS (1<<5) +#define AW869X_BIT_SYSST_FF_AES (1<<4) +#define AW869X_BIT_SYSST_FF_AFS (1<<3) +#define AW869X_BIT_SYSST_OCDS (1<<2) +#define AW869X_BIT_SYSST_OTS (1<<1) +#define AW869X_BIT_SYSST_DONES (1<<0) + +/* SYSINT: reg0x02 */ +#define AW869X_BIT_SYSINT_BSTERRI (1<<7) +#define AW869X_BIT_SYSINT_OVI (1<<6) +#define AW869X_BIT_SYSINT_UVLI (1<<5) +#define AW869X_BIT_SYSINT_FF_AEI (1<<4) +#define AW869X_BIT_SYSINT_FF_AFI (1<<3) +#define AW869X_BIT_SYSINT_OCDI (1<<2) +#define AW869X_BIT_SYSINT_OTI (1<<1) +#define AW869X_BIT_SYSINT_DONEI (1<<0) + +/* SYSINTM: reg0x03 */ +#define AW869X_BIT_SYSINTM_BSTERR_MASK (~(1<<7)) +#define AW869X_BIT_SYSINTM_BSTERR_OFF (1<<7) +#define AW869X_BIT_SYSINTM_BSTERR_EN (0<<7) +#define AW869X_BIT_SYSINTM_OV_MASK (~(1<<6)) +#define AW869X_BIT_SYSINTM_OV_OFF (1<<6) +#define AW869X_BIT_SYSINTM_OV_EN (0<<6) +#define AW869X_BIT_SYSINTM_UVLO_MASK (~(1<<5)) +#define AW869X_BIT_SYSINTM_UVLO_OFF (1<<5) +#define AW869X_BIT_SYSINTM_UVLO_EN (0<<5) +#define AW869X_BIT_SYSINTM_FF_AE_MASK (~(1<<4)) +#define AW869X_BIT_SYSINTM_FF_AE_OFF (1<<4) +#define AW869X_BIT_SYSINTM_FF_AE_EN (0<<4) +#define AW869X_BIT_SYSINTM_FF_AF_MASK (~(1<<3)) +#define AW869X_BIT_SYSINTM_FF_AF_OFF (1<<3) +#define AW869X_BIT_SYSINTM_FF_AF_EN (0<<3) +#define AW869X_BIT_SYSINTM_OCD_MASK (~(1<<2)) +#define AW869X_BIT_SYSINTM_OCD_OFF (1<<2) +#define AW869X_BIT_SYSINTM_OCD_EN (0<<2) +#define AW869X_BIT_SYSINTM_OT_MASK (~(1<<1)) +#define AW869X_BIT_SYSINTM_OT_OFF (1<<1) +#define AW869X_BIT_SYSINTM_OT_EN (0<<1) +#define AW869X_BIT_SYSINTM_DONE_MASK (~(1<<0)) +#define AW869X_BIT_SYSINTM_DONE_OFF (1<<0) +#define AW869X_BIT_SYSINTM_DONE_EN (0<<0) + +/* SYSCTRL: reg0x04 */ +#define AW869X_BIT_SYSCTRL_WAVDAT_MODE_MASK (~(3<<6)) +#define AW869X_BIT_SYSCTRL_WAVDAT_MODE_4X (3<<6) +#define AW869X_BIT_SYSCTRL_WAVDAT_MODE_2X (0<<6) +#define AW869X_BIT_SYSCTRL_WAVDAT_MODE_1X (1<<6) +#define AW869X_BIT_SYSCTRL_RAMINIT_MASK (~(1<<5)) +#define AW869X_BIT_SYSCTRL_RAMINIT_EN (1<<5) +#define AW869X_BIT_SYSCTRL_RAMINIT_OFF (0<<5) +#define AW869X_BIT_SYSCTRL_PLAY_MODE_MASK (~(3<<2)) +#define AW869X_BIT_SYSCTRL_PLAY_MODE_CONT (2<<2) +#define AW869X_BIT_SYSCTRL_PLAY_MODE_RTP (1<<2) +#define AW869X_BIT_SYSCTRL_PLAY_MODE_RAM (0<<2) +#define AW869X_BIT_SYSCTRL_BST_MODE_MASK (~(1<<1)) +#define AW869X_BIT_SYSCTRL_BST_MODE_BOOST (1<<1) +#define AW869X_BIT_SYSCTRL_BST_MODE_BYPASS (0<<1) +#define AW869X_BIT_SYSCTRL_WORK_MODE_MASK (~(1<<0)) +#define AW869X_BIT_SYSCTRL_STANDBY (1<<0) +#define AW869X_BIT_SYSCTRL_ACTIVE (0<<0) + +/* GO: reg0x05 */ +#define AW869X_BIT_GO_MASK (~(1<<0)) +#define AW869X_BIT_GO_ENABLE (1<<0) +#define AW869X_BIT_GO_DISABLE (0<<0) + +/* WAVSEQ1: reg0x07 */ +#define AW869X_BIT_WAVSEQ1_WAIT (1<<7) +#define AW869X_BIT_WAVSEQ1_WAV_FRM_SEQ1_MASK (~(0x7F<<0)) + +/* WAVSEQ2: reg0x08 */ +#define AW869X_BIT_WAVSEQ2_WAIT (1<<7) +#define AW869X_BIT_WAVSEQ2_WAV_FRM_SEQ2_MASK (~(0x7F<<0)) + +/* WAVSEQ3: reg0x09 */ +#define AW869X_BIT_WAVSEQ3_WAIT (1<<7) +#define AW869X_BIT_WAVSEQ3_WAV_FRM_SEQ3_MASK (~(0x7F<<0)) + +/* WAVSEQ4: reg0x0a */ +#define AW869X_BIT_WAVSEQ4_WAIT (1<<7) +#define AW869X_BIT_WAVSEQ4_WAV_FRM_SEQ4_MASK (~(0x7F<<0)) + +/* WAVSEQ5: reg0x0b */ +#define AW869X_BIT_WAVSEQ5_WAIT (1<<7) +#define AW869X_BIT_WAVSEQ5_WAV_FRM_SEQ5_MASK (~(0x7F<<0)) + +/* WAVSEQ6: reg0x0c */ +#define AW869X_BIT_WAVSEQ6_WAIT (1<<7) +#define AW869X_BIT_WAVSEQ6_WAV_FRM_SEQ6_MASK (~(0x7F<<0)) + +/* WAVSEQ7: reg0x0d */ +#define AW869X_BIT_WAVSEQ7_WAIT (1<<7) +#define AW869X_BIT_WAVSEQ7_WAV_FRM_SEQ7_MASK (~(0x7F<<0)) + +/* WAVSEQ8: reg0x0e */ +#define AW869X_BIT_WAVSEQ8_WAIT (1<<7) +#define AW869X_BIT_WAVSEQ8_WAV_FRM_SEQ8_MASK (~(0x7F<<0)) + +/* WAVLOOP: */ +#define AW869X_BIT_WAVLOOP_SEQN_MASK (~(0x0F<<4)) +#define AW869X_BIT_WAVLOOP_SEQNP1_MASK (~(0x0F<<0)) +#define AW869X_BIT_WAVLOOP_INIFINITELY (0x0F<<0) + +/* WAVLOOP1: reg0x0f */ +#define AW869X_BIT_WAVLOOP1_SEQ1_MASK (~(0x0F<<4)) +#define AW869X_BIT_WAVLOOP1_SEQ2_MASK (~(0x0F<<0)) + +/* WAVLOOP2: reg0x10 */ +#define AW869X_BIT_WAVLOOP2_SEQ3_MASK (~(0x0F<<4)) +#define AW869X_BIT_WAVLOOP2_SEQ4_MASK (~(0x0F<<0)) + +/* WAVLOOP3: reg0x11 */ +#define AW869X_BIT_WAVLOOP3_SEQ5_MASK (~(0x0F<<4)) +#define AW869X_BIT_WAVLOOP3_SEQ6_MASK (~(0x0F<<0)) + +/* WAVLOOP4: reg0x12 */ +#define AW869X_BIT_WAVLOOP4_SEQ7_MASK (~(0x0F<<4)) +#define AW869X_BIT_WAVLOOP4_SEQ8_MASK (~(0x0F<<0)) + +/* TRGCFG1: reg0x1b */ +#define AW869X_BIT_TRGCFG1_TRG3_POLAR_MASK (~(1<<5)) +#define AW869X_BIT_TRGCFG1_TRG3_POLAR_NEG (1<<5) +#define AW869X_BIT_TRGCFG1_TRG3_POLAR_POS (0<<5) +#define AW869X_BIT_TRGCFG1_TRG3_EDGE_MASK (~(1<<4)) +#define AW869X_BIT_TRGCFG1_TRG3_EDGE_POS (1<<4) +#define AW869X_BIT_TRGCFG1_TRG3_EDGE_POS_NEG (0<<4) +#define AW869X_BIT_TRGCFG1_TRG2_POLAR_MASK (~(1<<3)) +#define AW869X_BIT_TRGCFG1_TRG2_POLAR_NEG (1<<3) +#define AW869X_BIT_TRGCFG1_TRG2_POLAR_POS (0<<3) +#define AW869X_BIT_TRGCFG1_TRG2_EDGE_MASK (~(1<<2)) +#define AW869X_BIT_TRGCFG1_TRG2_EDGE_POS (1<<2) +#define AW869X_BIT_TRGCFG1_TRG2_EDGE_POS_NEG (0<<2) +#define AW869X_BIT_TRGCFG1_TRG1_POLAR_MASK (~(1<<1)) +#define AW869X_BIT_TRGCFG1_TRG1_POLAR_NEG (1<<1) +#define AW869X_BIT_TRGCFG1_TRG1_POLAR_POS (0<<1) +#define AW869X_BIT_TRGCFG1_TRG1_EDGE_MASK (~(1<<0)) +#define AW869X_BIT_TRGCFG1_TRG1_EDGE_POS (1<<0) +#define AW869X_BIT_TRGCFG1_TRG1_EDGE_POS_NEG (0<<0) + +/* TRGCFG2: reg0x1c */ +#define AW869X_BIT_TRGCFG2_TRG3_ENABLE_MASK (~(1<<2)) +#define AW869X_BIT_TRGCFG2_TRG3_ENABLE (1<<2) +#define AW869X_BIT_TRGCFG2_TRG3_DISABLE (0<<2) +#define AW869X_BIT_TRGCFG2_TRG2_ENABLE_MASK (~(1<<1)) +#define AW869X_BIT_TRGCFG2_TRG2_ENABLE (1<<1) +#define AW869X_BIT_TRGCFG2_TRG2_DISABLE (0<<1) +#define AW869X_BIT_TRGCFG2_TRG1_ENABLE_MASK (~(1<<0)) +#define AW869X_BIT_TRGCFG2_TRG1_ENABLE (1<<0) +#define AW869X_BIT_TRGCFG2_TRG1_DISABLE (0<<0) + +/* DBGCTRL: reg0x20 */ +#define AW869X_BIT_DBGCTRL_INT_EDGE_MODE_MASK (~(1<<3)) +#define AW869X_BIT_DBGCTRL_INT_EDGE_MODE_POS (1<<3) +#define AW869X_BIT_DBGCTRL_INT_EDGE_MODE_BOTH (0<<3) +#define AW869X_BIT_DBGCTRL_INT_MODE_MASK (~(1<<2)) +#define AW869X_BIT_DBGCTRL_INT_MODE_EDGE (1<<2) +#define AW869X_BIT_DBGCTRL_INT_MODE_LEVEL (0<<2) + +/* DATCTRL: reg0x2b */ +#define AW869X_BIT_DATCTRL_FC_MASK (~(1<<6)) +#define AW869X_BIT_DATCTRL_FC_1000HZ (3<<6) +#define AW869X_BIT_DATCTRL_FC_800HZ (3<<6) +#define AW869X_BIT_DATCTRL_FC_600HZ (1<<6) +#define AW869X_BIT_DATCTRL_FC_400HZ (0<<6) +#define AW869X_BIT_DATCTRL_LPF_ENABLE_MASK (~(1<<5)) +#define AW869X_BIT_DATCTRL_LPF_ENABLE (1<<5) +#define AW869X_BIT_DATCTRL_LPF_DISABLE (0<<5) +#define AW869X_BIT_DATCTRL_WAKEMODE_ENABLE_MASK (~(1<<0)) +#define AW869X_BIT_DATCTRL_WAKEMODE_ENABLE (1<<0) +#define AW869X_BIT_DATCTRL_WAKEMODE_DISABLE (0<<0) + +/* PWMPRC: reg0x2d */ +#define AW869X_BIT_PWMPRC_PRC_MASK (~(1<<7)) +#define AW869X_BIT_PWMPRC_PRC_ENABLE (1<<7) +#define AW869X_BIT_PWMPRC_PRC_DISABLE (0<<7) +#define AW869X_BIT_PWMPRC_PRCTIME_MASK (~(0x7f<<0)) + +/* PWMDBG: reg0x2e */ +#define AW869X_BIT_PWMDBG_PWM_MODE_MASK (~(3<<5)) +#define AW869X_BIT_PWMDBG_PWM_12K (3<<5) +#define AW869X_BIT_PWMDBG_PWM_24K (2<<5) +#define AW869X_BIT_PWMDBG_PWM_48K (0<<5) + +/* DBGST: reg0x30 */ +#define AW869X_BIT_DBGSTAT_FF_EMPTY (1<<0) + +/* BSTCFG: reg0x34 */ +#define AW869X_BIT_BSTCFG_PEAKCUR_MASK (~(7<<0)) +#define AW869X_BIT_BSTCFG_PEAKCUR_4A (7<<0) +#define AW869X_BIT_BSTCFG_PEAKCUR_3P75A (6<<0) +#define AW869X_BIT_BSTCFG_PEAKCUR_3P5A (5<<0) +#define AW869X_BIT_BSTCFG_PEAKCUR_3A (4<<0) +#define AW869X_BIT_BSTCFG_PEAKCUR_2P5A (3<<0) +#define AW869X_BIT_BSTCFG_PEAKCUR_2P25A (2<<0) +#define AW869X_BIT_BSTCFG_PEAKCUR_2A (1<<0) +#define AW869X_BIT_BSTCFG_PEAKCUR_1P75A (0<<0) + +/* ANADBG: reg0x35 */ +#define AW869X_BIT_ANADBG_IOC_MASK (~(3<<2)) +#define AW869X_BIT_ANADBG_IOC_4P65A (3<<2) +#define AW869X_BIT_ANADBG_IOC_4P15A (2<<2) +#define AW869X_BIT_ANADBG_IOC_3P65A (1<<2) +#define AW869X_BIT_ANADBG_IOC_3P15A (0<<2) + +/* ANACTRL: reg0x36 */ +#define AW869X_BIT_ANACTRL_LRA_SRC_MASK (~(1<<5)) +#define AW869X_BIT_ANACTRL_LRA_SRC_REG (1<<5) +#define AW869X_BIT_ANACTRL_LRA_SRC_EFUSE (0<<5) +#define AW869X_BIT_ANACTRL_HD_PD_MASK (~(1<<3)) +#define AW869X_BIT_ANACTRL_HD_PD_EN (1<<3) +#define AW869X_BIT_ANACTRL_HD_HZ_EN (0<<3) + +/* BSTDBG4: reg0x3a */ +#define AW869X_BIT_BSTDBG4_BSTVOL_MASK (~(0x1F<<1)) + +/* PRLVL: reg0x3e */ +#define AW869X_BIT_PRLVL_PR_MASK (~(1<<7)) +#define AW869X_BIT_PRLVL_PR_ENABLE (1<<7) +#define AW869X_BIT_PRLVL_PR_DISABLE (0<<7) +#define AW869X_BIT_PRLVL_PRLVL_MASK (~(0x7f<<0)) + +/*PRTIME: reg0x3f */ +#define AW869X_BIT_PRTIME_PRTIME_MASK (~(0xff<<0)) + +/* GLB_STATE: reg 0x3F */ +/* GLB_STATE [3:0] */ +#define AW869X_BIT_GLB_STATE_STANDBY (0<<0) + +/* BST_AUTO: reg0x47 */ +#define AW869X_BIT_BST_AUTO_BST_AUTOSW_MASK (~(1<<2)) +#define AW869X_BIT_BST_AUTO_BST_AUTOMATIC_BOOST (1<<2) +#define AW869X_BIT_BST_AUTO_BST_MANUAL_BOOST (0<<2) +#define AW869X_BIT_BST_AUTO_BST_RTP_MASK (~(1<<1)) +#define AW869X_BIT_BST_AUTO_BST_RTP_ENABLE (1<<1) +#define AW869X_BIT_BST_AUTO_BST_RTP_DISABLE (0<<1) +#define AW869X_BIT_BST_AUTO_BST_RAM_MASK (~(1<<0)) +#define AW869X_BIT_BST_AUTO_BST_RAM_ENABLE (1<<0) +#define AW869X_BIT_BST_AUTO_BST_RAM_DISABLE (0<<0) + +/* CONT_CTRL: reg0x48 */ +#define AW869X_BIT_CONT_CTRL_ZC_DETEC_MASK (~(1<<7)) +#define AW869X_BIT_CONT_CTRL_ZC_DETEC_ENABLE (1<<7) +#define AW869X_BIT_CONT_CTRL_ZC_DETEC_DISABLE (0<<7) +#define AW869X_BIT_CONT_CTRL_WAIT_PERIOD_MASK (~(3<<5)) +#define AW869X_BIT_CONT_CTRL_WAIT_8PERIOD (3<<5) +#define AW869X_BIT_CONT_CTRL_WAIT_4PERIOD (2<<5) +#define AW869X_BIT_CONT_CTRL_WAIT_2PERIOD (1<<5) +#define AW869X_BIT_CONT_CTRL_WAIT_1PERIOD (0<<5) +#define AW869X_BIT_CONT_CTRL_MODE_MASK (~(1<<4)) +#define AW869X_BIT_CONT_CTRL_BY_DRV_TIME (1<<4) +#define AW869X_BIT_CONT_CTRL_BY_GO_SIGNAL (0<<4) +#define AW869X_BIT_CONT_CTRL_EN_CLOSE_MASK (~(1<<3)) +#define AW869X_BIT_CONT_CTRL_CLOSE_PLAYBACK (1<<3) +#define AW869X_BIT_CONT_CTRL_OPEN_PLAYBACK (0<<3) +#define AW869X_BIT_CONT_CTRL_F0_DETECT_MASK (~(1<<2)) +#define AW869X_BIT_CONT_CTRL_F0_DETECT_ENABLE (1<<2) +#define AW869X_BIT_CONT_CTRL_F0_DETECT_DISABLE (0<<2) +#define AW869X_BIT_CONT_CTRL_O2C_MASK (~(1<<1)) +#define AW869X_BIT_CONT_CTRL_O2C_ENABLE (1<<1) +#define AW869X_BIT_CONT_CTRL_O2C_DISABLE (0<<1) +#define AW869X_BIT_CONT_CTRL_AUTO_BRK_MASK (~(1<<0)) +#define AW869X_BIT_CONT_CTRL_AUTO_BRK_ENABLE (1<<0) +#define AW869X_BIT_CONT_CTRL_AUTO_BRK_DISABLE (0<<0) + +/* D2SCFG: reg0x5e */ +#define AW869X_BIT_D2SCFG_CLK_ADC_MASK (~(7<<5)) +#define AW869X_BIT_D2SCFG_CLK_ASC_0P09375MHZ (7<<5) +#define AW869X_BIT_D2SCFG_CLK_ASC_0P1875MHZ (6<<5) +#define AW869X_BIT_D2SCFG_CLK_ASC_0P375MHZ (5<<5) +#define AW869X_BIT_D2SCFG_CLK_ASC_0P75MHZ (4<<5) +#define AW869X_BIT_D2SCFG_CLK_ASC_1P5MHZ (3<<5) +#define AW869X_BIT_D2SCFG_CLK_ASC_3MHZ (2<<5) +#define AW869X_BIT_D2SCFG_CLK_ASC_6MHZ (1<<5) +#define AW869X_BIT_D2SCFG_CLK_ASC_12MHZ (0<<5) +#define AW869X_BIT_D2SCFG_D2S_GAIN_MASK (~(7<<0)) +#define AW869X_BIT_D2SCFG_D2S_GAIN_1 (0<<0) +#define AW869X_BIT_D2SCFG_D2S_GAIN_2 (1<<0) +#define AW869X_BIT_D2SCFG_D2S_GAIN_4 (2<<0) +#define AW869X_BIT_D2SCFG_D2S_GAIN_5 (3<<0) +#define AW869X_BIT_D2SCFG_D2S_GAIN_8 (4<<0) +#define AW869X_BIT_D2SCFG_D2S_GAIN_10 (5<<0) +#define AW869X_BIT_D2SCFG_D2S_GAIN_20 (6<<0) +#define AW869X_BIT_D2SCFG_D2S_GAIN_40 (7<<0) + +/* DETCTRL: reg0x5f */ +#define AW869X_BIT_DETCTRL_RL_OS_MASK (~(1<<6)) +#define AW869X_BIT_DETCTRL_RL_DETECT (1<<6) +#define AW869X_BIT_DETCTRL_OS_DETECT (0<<6) +#define AW869X_BIT_DETCTRL_PROTECT_MASK (~(1<<5)) +#define AW869X_BIT_DETCTRL_PROTECT_NO_ACTION (1<<5) +#define AW869X_BIT_DETCTRL_PROTECT_SHUTDOWN (0<<5) +#define AW869X_BIT_DETCTRL_ADO_SLOT_MODE_MASK (~(1<<4)) +#define AW869X_BIT_DETCTRL_ADO_SLOT_MODE_ENABLE (1<<4) +#define AW869X_BIT_DETCTRL_ADO_SLOT_MODE_DISABLE (0<<4) +#define AW869X_BIT_DETCTRL_VBAT_GO_MASK (~(1<<1)) +#define AW869X_BIT_DETCTRL_VABT_GO_ENABLE (1<<1) +#define AW869X_BIT_DETCTRL_VBAT_GO_DISBALE (0<<1) +#define AW869X_BIT_DETCTRL_DIAG_GO_MASK (~(1<<0)) +#define AW869X_BIT_DETCTRL_DIAG_GO_ENABLE (1<<0) +#define AW869X_BIT_DETCTRL_DIAG_GO_DISABLE (0<<0) + +/* ADCTEST: reg0x66 */ +#define AW869X_BIT_ADCTEST_VBAT_MODE_MASK (~(1<<6)) +#define AW869X_BIT_ADCTEST_VBAT_HW_COMP (1<<6) +#define AW869X_BIT_ADCTEST_VBAT_SW_COMP (0<<6) + +/* BEMF_NUM: reg0x78 */ +#define AW869X_BIT_BEMF_NUM_BRK_MASK (~(0x0F<<0)) + +/********************************************************* + * + * AW869XX Register Detail + * + *********************************************************/ +/* SYSST: reg 0x01 RO */ +#define AW869XX_BIT_SYSST_BST_SCPS (1<<7) +#define AW869XX_BIT_SYSST_BST_OVPS (1<<6) +#define AW869XX_BIT_SYSST_UVLS (1<<5) +#define AW869XX_BIT_SYSST_FF_AES (1<<4) +#define AW869XX_BIT_SYSST_FF_AFS (1<<3) +#define AW869XX_BIT_SYSST_OCDS (1<<2) +#define AW869XX_BIT_SYSST_OTS (1<<1) +#define AW869XX_BIT_SYSST_DONES (1<<0) + +/* SYSINT: reg 0x02 RC */ +#define AW869XX_BIT_SYSINT_BST_SCPI (1<<7) +#define AW869XX_BIT_SYSINT_BST_OVPI (1<<6) +#define AW869XX_BIT_SYSINT_UVLI (1<<5) +#define AW869XX_BIT_SYSINT_FF_AEI (1<<4) +#define AW869XX_BIT_SYSINT_FF_AFI (1<<3) +#define AW869XX_BIT_SYSINT_OCDI (1<<2) +#define AW869XX_BIT_SYSINT_OTI (1<<1) +#define AW869XX_BIT_SYSINT_DONEI (1<<0) + +/* SYSINTM: reg 0x03 RW */ +#define AW869XX_BIT_SYSINTM_BST_SCPM_MASK (~(1<<7)) +#define AW869XX_BIT_SYSINTM_BST_SCPM_OFF (1<<7) +#define AW869XX_BIT_SYSINTM_BST_SCPM_ON (0<<7) +#define AW869XX_BIT_SYSINTM_BST_OVPM_MASK (~(1<<6)) +#define AW869XX_BIT_SYSINTM_BST_OVPM_OFF (1<<6) +#define AW869XX_BIT_SYSINTM_BST_OVPM_ON (0<<6) +#define AW869XX_BIT_SYSINTM_UVLM_MASK (~(1<<5)) +#define AW869XX_BIT_SYSINTM_UVLM_OFF (1<<5) +#define AW869XX_BIT_SYSINTM_UVLM_ON (0<<5) +#define AW869XX_BIT_SYSINTM_FF_AEM_MASK (~(1<<4)) +#define AW869XX_BIT_SYSINTM_FF_AEM_OFF (1<<4) +#define AW869XX_BIT_SYSINTM_FF_AEM_ON (0<<4) +#define AW869XX_BIT_SYSINTM_FF_AFM_MASK (~(1<<3)) +#define AW869XX_BIT_SYSINTM_FF_AFM_OFF (1<<3) +#define AW869XX_BIT_SYSINTM_FF_AFM_ON (0<<3) +#define AW869XX_BIT_SYSINTM_OCDM_MASK (~(1<<2)) +#define AW869XX_BIT_SYSINTM_OCDM_OFF (1<<2) +#define AW869XX_BIT_SYSINTM_OCDM_ON (0<<2) +#define AW869XX_BIT_SYSINTM_OTM_MASK (~(1<<1)) +#define AW869XX_BIT_SYSINTM_OTM_OFF (1<<1) +#define AW869XX_BIT_SYSINTM_OTM_ON (0<<1) +#define AW869XX_BIT_SYSINTM_DONEM_MASK (~(1<<0)) +#define AW869XX_BIT_SYSINTM_DONEM_OFF (1<<0) +#define AW869XX_BIT_SYSINTM_DONEM_ON (0<<0) + +/* SYSST2: reg 0x04 RO */ +#define AW869XX_BIT_SYSST2_RAM_ADDR_ER (1<<7) +#define AW869XX_BIT_SYSST2_TRG_ADDR_ER (1<<6) +#define AW869XX_BIT_SYSST2_PLL_REF_OK (1<<5) +#define AW869XX_BIT_SYSST2_BST_OK (1<<4) +#define AW869XX_BIT_SYSST2_VBG_OK (1<<3) +#define AW869XX_BIT_SYSST2_LDO_OK (1<<2) +#define AW869XX_BIT_SYSST2_FF_FULL (1<<1) +#define AW869XX_BIT_SYSST2_FF_EMPTY (1<<0) + +/* SYSER: reg 0x05 RC */ +#define AW869XX_BIT_SYSER_I2S_ERR (1<<7) +#define AW869XX_BIT_SYSER_TRIG1_EVENT (1<<6) +#define AW869XX_BIT_SYSER_TRIG2_EVENT (1<<5) +#define AW869XX_BIT_SYSER_TRIG3_EVENT (1<<4) +#define AW869XX_BIT_SYSER_OV (1<<3) +#define AW869XX_BIT_SYSER_ADDR_ER (1<<2) +#define AW869XX_BIT_SYSER_FF_ER (1<<1) +#define AW869XX_BIT_SYSER_PLL_REF_ER (1<<0) + +/* PLAYCFG1: reg 0x06 RW */ +#define AW869XX_BIT_PLAYCFG1_BST_MODE_MASK (~(1<<6)) +#define AW869XX_BIT_PLAYCFG1_BST_MODE_BYPASS (0<<6) +#define AW869XX_BIT_PLAYCFG1_BST_MODE_BOOST (1<<6) +#define AW869XX_BIT_PLAYCFG1_BST_VOUT_RDA_MASK (~(63<<0)) + +/* PLAYCFG2: reg 0x07 RW */ +/* GAIN */ + +/* PLAYCFG3: reg 0x08 RW */ +#define AW869XX_BIT_PLAYCFG3_AUTO_BST_MASK (~(1<<6)) +#define AW869XX_BIT_PLAYCFG3_AUTO_BST_ENABLE (1<<6) +#define AW869XX_BIT_PLAYCFG3_AUTO_BST_DISABLE (0<<6) +#define AW869XX_BIT_PLAYCFG3_STOP_MODE_MASK (~(1<<5)) +#define AW869XX_BIT_PLAYCFG3_STOP_MODE_NOW (1<<5) +#define AW869XX_BIT_PLAYCFG3_STOP_MODE_LATER (0<<5) +#define AW869XX_BIT_PLAYCFG3_BRK_EN_MASK (~(1<<2)) +#define AW869XX_BIT_PLAYCFG3_BRK_ENABLE (1<<2) +#define AW869XX_BIT_PLAYCFG3_BRK_DISABLE (0<<2) +#define AW869XX_BIT_PLAYCFG3_PLAY_MODE_MASK (~(3<<0)) +#define AW869XX_BIT_PLAYCFG3_PLAY_MODE_STOP (3<<0) +#define AW869XX_BIT_PLAYCFG3_PLAY_MODE_CONT (2<<0) +#define AW869XX_BIT_PLAYCFG3_PLAY_MODE_RTP (1<<0) +#define AW869XX_BIT_PLAYCFG3_PLAY_MODE_RAM (0<<0) + +/* PLAYCFG4: reg 0x09 RW */ +#define AW869XX_BIT_PLAYCFG4_STOP_MASK (~(1<<1)) +#define AW869XX_BIT_PLAYCFG4_STOP_ON (1<<1) +#define AW869XX_BIT_PLAYCFG4_STOP_OFF (0<<1) +#define AW869XX_BIT_PLAYCFG4_GO_MASK (~(1<<0)) +#define AW869XX_BIT_PLAYCFG4_GO_ON (1<<0) +#define AW869XX_BIT_PLAYCFG4_GO_OFF (0<<0) + +/* WAVCFG1-8: reg 0x0A - reg 0x11 RW */ +#define AW869XX_BIT_WAVCFG_SEQWAIT_MASK (~(1<<7)) +#define AW869XX_BIT_WAVCFG_SEQWAIT_TIME (1<<7) +#define AW869XX_BIT_WAVCFG_SEQWAIT_NUMBER (0<<7) + +/* WAVCFG9-12: reg 0x12 - reg 0x15 RW */ +#define AW869XX_BIT_WAVLOOP_SEQ_ODD_MASK (~(0x0F<<4)) +#define AW869XX_BIT_WAVLOOP_SEQ_ODD_INIFINITELY (0x0F<<4) +#define AW869XX_BIT_WAVLOOP_SEQ_EVEN_MASK (~(0x0F<<0)) +#define AW869XX_BIT_WAVLOOP_SEQ_EVEN_INIFINITELY (0x0F<<0) +#define AW869XX_BIT_WAVLOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG9: reg 0x12 RW */ +#define AW869XX_BIT_WAVCFG9_SEQ1LOOP_MASK (~(0x0F<<4)) +#define AW869XX_BIT_WAVCFG9_SEQ1LOOP_INIFINITELY (0x0F<<4) +#define AW869XX_BIT_WAVCFG9_SEQ2LOOP_MASK (~(0x0F<<0)) +#define AW869XX_BIT_WAVCFG9_SEQ2LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG10: reg 0x13 RW */ +#define AW869XX_BIT_WAVCFG10_SEQ3LOOP_MASK (~(0x0F<<4)) +#define AW869XX_BIT_WAVCFG10_SEQ3LOOP_INIFINITELY (0x0F<<4) +#define AW869XX_BIT_WAVCFG10_SEQ4LOOP_MASK (~(0x0F<<0)) +#define AW869XX_BIT_WAVCFG10_SEQ4LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG11: reg 0x14 RW */ +#define AW869XX_BIT_WAVCFG11_SEQ5LOOP_MASK (~(0x0F<<4)) +#define AW869XX_BIT_WAVCFG11_SEQ5LOOP_INIFINITELY (0x0F<<4) +#define AW869XX_BIT_WAVCFG11_SEQ6LOOP_MASK (~(0x0F<<0)) +#define AW869XX_BIT_WAVCFG11_SEQ6LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG12: reg 0x15 RW */ +#define AW869XX_BIT_WAVCFG12_SEQ7LOOP_MASK (~(0x0F<<4)) +#define AW869XX_BIT_WAVCFG12_SEQ7LOOP_INIFINITELY (0x0F<<4) +#define AW869XX_BIT_WAVCFG12_SEQ8LOOP_MASK (~(0x0F<<0)) +#define AW869XX_BIT_WAVCFG12_SEQ8LOOP_INIFINITELY (0x0F<<0) + +/* CONTCFG1: reg 0x18 RW */ +#define AW869XX_BIT_CONTCFG1_EDGE_FRE_MASK (~(0x0F<<4)) +#define AW869XX_BIT_CONTCFG1_EN_F0_DET_MASK (~(1<<3)) +#define AW869XX_BIT_CONTCFG1_F0_DET_ENABLE (1<<3) +#define AW869XX_BIT_CONTCFG1_F0_DET_DISABLE (0<<3) +#define AW869XX_BIT_CONTCFG1_MBRK_MASK (~(1<<2)) +#define AW869XX_BIT_CONTCFG1_MBRK_ENABLE (1<<2) +#define AW869XX_BIT_CONTCFG1_MBRK_DISABLE (0<<2) +#define AW869XX_BIT_CONTCFG1_BRK_BST_MD_MASK (~(1<<1)) +#define AW869XX_BIT_CONTCFG1_BRK_BST_MD_ENABLE (1<<1) +#define AW869XX_BIT_CONTCFG1_BRK_BST_MD_DISABLE (0<<1) +#define AW869XX_BIT_CONTCFG1_SIN_MODE_MASK (~(1<<0)) +#define AW869XX_BIT_CONTCFG1_SIN_MODE_COS (1<<0) +#define AW869XX_BIT_CONTCFG1_SIN_MODE_SINE (0<<0) + +/* CONTCFG3: reg 0x1A RW */ +/* DRV_WIDTH */ + +/* CONTCFG4: reg 0x1B RW */ +/* WAIT_NUM */ + +/* CONTCFG5: reg 0x1C RW */ +#define AW869XX_BIT_CONTCFG5_BST_BRK_GAIN_MASK (~(0x0F<<4)) +#define AW869XX_BIT_CONTCFG5_BRK_GAIN_MASK (~(0x0F<<0)) + +/* CONTCFG6: reg 0x1D RW */ +#define AW869XX_BIT_CONTCFG6_TRACK_EN_MASK (~(1<<7)) +#define AW869XX_BIT_CONTCFG6_TRACK_ENABLE (1<<7) +#define AW869XX_BIT_CONTCFG6_TRACK_DISABLE (0<<7) +#define AW869XX_BIT_CONTCFG6_DRV1_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG7: reg 0x1E RW */ +#define AW869XX_BIT_CONTCFG7_DRV2_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG8: reg 0x1F RW */ +/* DRV1_TIME */ + +/* CONTCFG9: reg 0x20 RW */ +/* DRV2_TIME */ + +/* CONTCFG10: reg 0x21 RW */ +/* BRK_TIME */ + +/* CONTCFG11: reg 0x22 RW */ +/* TRACK_MARGIN */ + +/* CONTCFG13: reg 0x24 RW */ +#define AW869XX_BIT_CONTCFG13_TSET_MASK (~(0x0F<<4)) +#define AW869XX_BIT_CONTCFG13_BEME_SET_MASK (~(0x0F<<0)) + +/* CONTRD14: reg 0x25 RO */ +/* F_LRA_F0_H */ + +/* CONTRD15: reg 0x26 RO */ +/* F_LRA_F0_L */ + +/* CONTRD16: reg 0x27 RO */ +/* CONT_F0_H */ + +/* CONTRD17: reg 0x28 RO */ +/* CONT_F0_L */ + +/* RTPCFG1: reg 0x2D RW */ +/* BASE_ADDR_H */ + +/* RTPCFG2: reg 0x2E RW */ +/* BASE_ADDR_L */ + +/* RTPCFG3: reg 0x2F RW */ +#define AW869XX_BIT_RTPCFG3_FIFO_AEH_MASK (~(0x0F<<4)) +#define AW869XX_BIT_RTPCFG3_FIFO_AFH_MASK (~(0x0F<<0)) + +/* RTPCFG4: reg 0x30 RW */ +/* FIFO_AEL */ + +/* RTPCFG5: reg 0x31 RW */ +/* FIFO_AFL */ + +/* RTPDATA: reg 0x32 RW */ +/* FIFO_AFL */ + +#define AW869XX_BIT_TRG_ENABLE_MASK (~(1<<7)) +#define AW869XX_BIT_TRG_ENABLE (1<<7) +#define AW869XX_BIT_TRG_DISABLE (0<<7) +#define AW869XX_BIT_TRG_SEQ_MASK (~(0x7F<<0)) + +/* TRGCFG1: reg 0x33 RW */ +#define AW869XX_BIT_TRGCFG1_TRG1_POS_MASK (~(1<<7)) +#define AW869XX_BIT_TRGCFG1_TRG1_POS_ENABLE (1<<7) +#define AW869XX_BIT_TRGCFG1_TRG1_POS_DISABLE (0<<7) +#define AW869XX_BIT_TRGCFG1_TRG1SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG2: reg 0x34 RW */ +#define AW869XX_BIT_TRGCFG2_TRG2_POS_MASK (~(1<<7)) +#define AW869XX_BIT_TRGCFG2_TRG2_POS_ENABLE (1<<7) +#define AW869XX_BIT_TRGCFG2_TRG2_POS_DISABLE (0<<7) +#define AW869XX_BIT_TRGCFG2_TRG2SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG3: reg 0x35 RW */ +#define AW869XX_BIT_TRGCFG3_TRG3_POS_MASK (~(1<<7)) +#define AW869XX_BIT_TRGCFG3_TRG3_POS_ENABLE (1<<7) +#define AW869XX_BIT_TRGCFG3_TRG3_POS_DISABLE (0<<7) +#define AW869XX_BIT_TRGCFG3_TRG3SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG4: reg 0x36 RW */ +#define AW869XX_BIT_TRGCFG4_TRG1_NEG_MASK (~(1<<7)) +#define AW869XX_BIT_TRGCFG4_TRG1_NEG_ENABLE (1<<7) +#define AW869XX_BIT_TRGCFG4_TRG1_NEG_DISABLE (0<<7) +#define AW869XX_BIT_TRGCFG4_TRG1SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG5: reg 0x37 RW */ +#define AW869XX_BIT_TRGCFG5_TRG2_NEG_MASK (~(1<<7)) +#define AW869XX_BIT_TRGCFG5_TRG2_NEG_ENABLE (1<<7) +#define AW869XX_BIT_TRGCFG5_TRG2_NEG_DISABLE (0<<7) +#define AW869XX_BIT_TRGCFG5_TRG2SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG6: reg 0x38 RW */ +#define AW869XX_BIT_TRGCFG6_TRG3_NEG_MASK (~(1<<7)) +#define AW869XX_BIT_TRGCFG6_TRG3_NEG_ENABLE (1<<7) +#define AW869XX_BIT_TRGCFG6_TRG3_NEG_DISABLE (0<<7) +#define AW869XX_BIT_TRGCFG6_TRG3SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG7: reg 0x39 RW */ +#define AW869XX_BIT_TRGCFG7_TRG1_POLAR_MASK (~(1<<7)) +#define AW869XX_BIT_TRGCFG7_TRG1_POLAR_NEG (1<<7) +#define AW869XX_BIT_TRGCFG7_TRG1_POLAR_POS (0<<7) +#define AW869XX_BIT_TRGCFG7_TRG1_MODE_MASK (~(1<<6)) +#define AW869XX_BIT_TRGCFG7_TRG1_MODE_LEVEL (1<<6) +#define AW869XX_BIT_TRGCFG7_TRG1_MODE_EDGE (0<<6) +#define AW869XX_BIT_TRGCFG7_TRG1_AUTO_BRK_MASK (~(1<<5)) +#define AW869XX_BIT_TRGCFG7_TRG1_AUTO_BRK_ENABLE (1<<5) +#define AW869XX_BIT_TRGCFG7_TRG1_AUTO_BRK_DISABLE (0<<5) +#define AW869XX_BIT_TRGCFG7_TRG1_BST_MASK (~(1<<4)) +#define AW869XX_BIT_TRGCFG7_TRG1_BST_ENABLE (1<<4) +#define AW869XX_BIT_TRGCFG7_TRG1_BST_DISABLE (0<<4) +#define AW869XX_BIT_TRGCFG7_TRG2_POLAR_MASK (~(1<<3)) +#define AW869XX_BIT_TRGCFG7_TRG2_POLAR_NEG (1<<3) +#define AW869XX_BIT_TRGCFG7_TRG2_POLAR_POS (0<<3) +#define AW869XX_BIT_TRGCFG7_TRG2_MODE_MASK (~(1<<2)) +#define AW869XX_BIT_TRGCFG7_TRG2_MODE_LEVEL (1<<2) +#define AW869XX_BIT_TRGCFG7_TRG2_MODE_EDGE (0<<2) +#define AW869XX_BIT_TRGCFG7_TRG2_AUTO_BRK_MASK (~(1<<1)) +#define AW869XX_BIT_TRGCFG7_TRG2_AUTO_BRK_ENABLE (1<<1) +#define AW869XX_BIT_TRGCFG7_TRG2_AUTO_BRK_DISABLE (0<<1) +#define AW869XX_BIT_TRGCFG7_TRG2_BST_MASK (~(1<<0)) +#define AW869XX_BIT_TRGCFG7_TRG2_BST_ENABLE (1<<0) +#define AW869XX_BIT_TRGCFG7_TRG2_BST_DISABLE (0<<0) + +/* TRGCFG8: reg 0x3A RW */ +#define AW869XX_BIT_TRGCFG8_TRG3_POLAR_MASK (~(1<<7)) +#define AW869XX_BIT_TRGCFG8_TRG3_POLAR_NEG (1<<7) +#define AW869XX_BIT_TRGCFG8_TRG3_POLAR_POS (0<<7) +#define AW869XX_BIT_TRGCFG8_TRG3_MODE_MASK (~(1<<6)) +#define AW869XX_BIT_TRGCFG8_TRG3_MODE_LEVEL (1<<6) +#define AW869XX_BIT_TRGCFG8_TRG3_MODE_EDGE (0<<6) +#define AW869XX_BIT_TRGCFG8_TRG3_AUTO_BRK_MASK (~(1<<5)) +#define AW869XX_BIT_TRGCFG8_TRG3_AUTO_BRK_ENABLE (1<<5) +#define AW869XX_BIT_TRGCFG8_TRG3_AUTO_BRK_DISABLE (0<<5) +#define AW869XX_BIT_TRGCFG8_TRG3_BST_MASK (~(1<<4)) +#define AW869XX_BIT_TRGCFG8_TRG3_BST_ENABLE (1<<4) +#define AW869XX_BIT_TRGCFG8_TRG3_BST_DISABLE (0<<4) +#define AW869XX_BIT_TRGCFG8_TRG_ONEWIRE_MASK (~(1<<3)) +#define AW869XX_BIT_TRGCFG8_TRG_ONEWIRE_ENABLE (1<<3) +#define AW869XX_BIT_TRGCFG8_TRG_ONEWIRE_DISABLE (0<<3) +#define AW869XX_BIT_TRGCFG8_TRG1_STOP_MASK (~(1<<2)) +#define AW869XX_BIT_TRGCFG8_TRG1_STOP (1<<2) +#define AW869XX_BIT_TRGCFG8_TRG2_STOP_MASK (~(1<<1)) +#define AW869XX_BIT_TRGCFG8_TRG2_STOP (1<<1) +#define AW869XX_BIT_TRGCFG8_TRG3_STOP_MASK (~(1<<0)) +#define AW869XX_BIT_TRGCFG8_TRG3_STOP (1<<0) + +/* GLBCFG2: reg 0x3C RW */ +/* START_DLY */ +#define AW869XX_BIT_START_DLY_20US (0x01) +#define AW869XX_BIT_START_DLY_2P5MS (0x75) + +/* GLBCFG4: reg 0x3E RW */ +#define AW869XX_BIT_GLBCFG4_GO_PRIO_MASK (~(3<<6)) +#define AW869XX_BIT_GLBCFG4_TRG3_PRIO_MASK (~(3<<4)) +#define AW869XX_BIT_GLBCFG4_TRG2_PRIO_MASK (~(3<<2)) +#define AW869XX_BIT_GLBCFG4_TRG1_PRIO_MASK (~(3<<0)) + +/* GLBRD5: reg 0x3F R0 */ +/* GLB_STATE [3:0] */ +#define AW869XX_BIT_GLBRD5_STATE_MASK (~(0x0F<<0)) +#define AW869XX_BIT_GLBRD5_STATE_STANDBY (0<<0) +#define AW869XX_BIT_GLBRD5_STATE_WAKEUP (1<<0) +#define AW869XX_BIT_GLBRD5_STATE_STARTUP (2<<0) +#define AW869XX_BIT_GLBRD5_STATE_WAIT (3<<0) +#define AW869XX_BIT_GLBRD5_STATE_CONT_GO (6<<0) +#define AW869XX_BIT_GLBRD5_STATE_RAM_GO (7<<0) +#define AW869XX_BIT_GLBRD5_STATE_RTP_GO (8<<0) +#define AW869XX_BIT_GLBRD5_STATE_TRIG_GO (9<<0) +#define AW869XX_BIT_GLBRD5_STATE_I2S_GO (0x0A<<0) +#define AW869XX_BIT_GLBRD5_STATE_BRAKE (0x0B<<0) +#define AW869XX_BIT_GLBRD5_STATE_END (0x0C<<0) + +/* RAMADDRH: reg 0x40 RWS */ +#define AW869XX_BIT_RAMADDRH_MASK (~(63<<0)) + +/* RAMADDRL: reg 0x41 RWS */ +/* RAMADDRL */ + +/* RAMDATA: reg 0x42 RWS */ +/* RAMDATA */ + +/* SYSCTRL1: reg 0x43 RW */ +#define AW869XX_BIT_SYSCTRL1_VBAT_MODE_MASK (~(1<<7)) +#define AW869XX_BIT_SYSCTRL1_VBAT_MODE_HW (1<<7) +#define AW869XX_BIT_SYSCTRL1_VBAT_MODE_SW (0<<7) +#define AW869XX_BIT_SYSCTRL1_PERP_MASK (~(1<<6)) +#define AW869XX_BIT_SYSCTRL1_PERP_ON (1<<6) +#define AW869XX_BIT_SYSCTRL1_PERP_OFF (0<<6) +#define AW869XX_BIT_SYSCTRL1_CLK_SEL_MASK (~(3<<4)) +#define AW869XX_BIT_SYSCTRL1_CLK_SEL_OSC (1<<4) +#define AW869XX_BIT_SYSCTRL1_CLK_SEL_AUTO (0<<4) +#define AW869XX_BIT_SYSCTRL1_RAMINIT_MASK (~(1<<3)) +#define AW869XX_BIT_SYSCTRL1_RAMINIT_ON (1<<3) +#define AW869XX_BIT_SYSCTRL1_RAMINIT_OFF (0<<3) +#define AW869XX_BIT_SYSCTRL1_EN_FIR_MASK (~(1<<2)) +#define AW869XX_BIT_SYSCTRL1_FIR_ENABLE (0<<2) +#define AW869XX_BIT_SYSCTRL1_WAKE_MODE_MASK (~(1<<1)) +#define AW869XX_BIT_SYSCTRL1_WAKE_MODE_WAKEUP (1<<1) +#define AW869XX_BIT_SYSCTRL1_WAKE_MODE_BST (0<<1) +#define AW869XX_BIT_SYSCTRL1_RTP_CLK_MASK (~(1<<0)) +#define AW869XX_BIT_SYSCTRL1_RTP_PLL (1<<0) +#define AW869XX_BIT_SYSCTRL1_RTP_OSC (0<<0) + +/* SYSCTRL2: reg 0x44 RW */ +#define AW869XX_BIT_SYSCTRL2_WAKE_MASK (~(1<<7)) +#define AW869XX_BIT_SYSCTRL2_WAKE_ON (1<<7) +#define AW869XX_BIT_SYSCTRL2_WAKE_OFF (0<<7) +#define AW869XX_BIT_SYSCTRL2_STANDBY_MASK (~(1<<6)) +#define AW869XX_BIT_SYSCTRL2_STANDBY_ON (1<<6) +#define AW869XX_BIT_SYSCTRL2_STANDBY_OFF (0<<6) +#define AW869XX_BIT_SYSCTRL2_RTP_DLY_MASK (~(3<<4)) +#define AW869XX_BIT_SYSCTRL2_PLL_PIN_MASK (~(1<<3)) +#define AW869XX_BIT_SYSCTRL2_PLL_PIN_TEST (1<<3) +#define AW869XX_BIT_SYSCTRL2_I2S_PIN_MASK (~(1<<2)) +#define AW869XX_BIT_SYSCTRL2_I2S_PIN_I2S (1<<2) +#define AW869XX_BIT_SYSCTRL2_I2S_PIN_TRIG (0<<2) +#define AW869XX_BIT_SYSCTRL2_WAVDAT_MODE_MASK (~(3<<0)) +#define AW869XX_BIT_SYSCTRL2_RATE_12K (2<<0) +#define AW869XX_BIT_SYSCTRL2_RATE_24K (0<<0) +#define AW869XX_BIT_SYSCTRL2_RATE_48K (1<<0) + +/* SYSCTRL3: reg 0x45 RW */ +/* SIN_H */ + +/* SYSCTRL4: reg 0x46 RW */ +/* SIN_L */ + +/* SYSCTRL5: reg 0x47 RW */ +/* COS_H */ + +/* SYSCTRL6: reg 0x48 RW */ +/* COS_L */ + +/* SYSCTRL7: reg 0x49 RW */ +#define AW869XX_BIT_SYSCTRL7_GAIN_BYPASS_MASK (~(1<<6)) +#define AW869XX_BIT_SYSCTRL7_GAIN_CHANGEABLE (1<<6) +#define AW869XX_BIT_SYSCTRL7_GAIN_FIXED (0<<6) +#define AW869XX_BIT_SYSCTRL7_INT_EDGE_MODE_MASK (~(1<<5)) +#define AW869XX_BIT_SYSCTRL7_INT_EDGE_MODE_POS (0<<5) +#define AW869XX_BIT_SYSCTRL7_INT_EDGE_MODE_BOTH (1<<5) +#define AW869XX_BIT_SYSCTRL7_INT_MODE_MASK (~(1<<4)) +#define AW869XX_BIT_SYSCTRL7_INT_MODE_EDGE (1<<4) +#define AW869XX_BIT_SYSCTRL7_INT_MODE_LEVEL (0<<4) +#define AW869XX_BIT_SYSCTRL7_INTP_MASK (~(1<<3)) +#define AW869XX_BIT_SYSCTRL7_INTP_HIGH (1<<3) +#define AW869XX_BIT_SYSCTRL7_INTP_LOW (0<<3) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN_MASK (~(7<<0)) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN (7<<0) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN_1 (0<<0) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN_2 (1<<0) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN_4 (2<<0) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN_8 (3<<0) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN_10 (4<<0) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN_16 (5<<0) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN_20 (6<<0) +#define AW869XX_BIT_SYSCTRL7_D2S_GAIN_26 (7<<0) + +/* I2SCFG1: reg 0x4A RW */ +#define AW869XX_BIT_I2SCFG1_I2SMD_MASK (~(3<<6)) +#define AW869XX_BIT_I2SCFG1_I2SFS_MASK (~(3<<4)) +#define AW869XX_BIT_I2SCFG1_I2SFS_16BIT (0<<4) +#define AW869XX_BIT_I2SCFG1_I2SFS_20BIT (1<<4) +#define AW869XX_BIT_I2SCFG1_I2SFS_24BIT (2<<4) +#define AW869XX_BIT_I2SCFG1_I2SFS_32BIT (3<<4) +#define AW869XX_BIT_I2SCFG1_I2SBCK_MASK (~(3<<2)) +#define AW869XX_BIT_I2SCFG1_I2SBCK_32FS (0<<2) +#define AW869XX_BIT_I2SCFG1_I2SBCK_48FS (1<<2) +#define AW869XX_BIT_I2SCFG1_I2SBCK_64FS (2<<2) +#define AW869XX_BIT_I2SCFG1_RX_THRS_MASK (~(3<<0)) + +/* I2SCFG2: reg 0x4B RW */ +#define AW869XX_BIT_I2SCFG2_WSINV_MASK (~(1<<4)) +#define AW869XX_BIT_I2SCFG2_WSINV_SWITCH (1<<4) +#define AW869XX_BIT_I2SCFG2_WSINV_NO_SWITCH (0<<4) +#define AW869XX_BIT_I2SCFG2_BCKINV_MASK (~(1<<3)) +#define AW869XX_BIT_I2SCFG2_BCKINV_INVERT (1<<3) +#define AW869XX_BIT_I2SCFG2_BCKINV_NOTINVT (0<<3) +#define AW869XX_BIT_I2SCFG2_CHSEL_MASK (~(1<<2)) +#define AW869XX_BIT_I2SCFG2_CHSEL_LEFT (1<<2) +#define AW869XX_BIT_I2SCFG2_CHSEL_RIGHT (0<<2) +#define AW869XX_BIT_I2SCFG2_I2S_INT_MASK (~(1<<1)) +#define AW869XX_BIT_I2SCFG2_I2S_INT_ON (1<<1) +#define AW869XX_BIT_I2SCFG2_I2S_INT_OFF (0<<1) +#define AW869XX_BIT_I2SCFG2_I2S_EN_MASK (~(1<<0)) +#define AW869XX_BIT_I2SCFG2_I2S_ENABLE (1<<0) +#define AW869XX_BIT_I2SCFG2_I2S_DISABLE (0<<0) + +/* PWMCFG1: reg 0x4C RW */ +#define AW869XX_BIT_PWMCFG1_PRC_EN_MASK (~(1<<7)) +#define AW869XX_BIT_PWMCFG1_PRC_ENABLE (1<<7) +#define AW869XX_BIT_PWMCFG1_PRC_DISABLE (0<<7) +#define AW869XX_BIT_PWMCFG1_PRCTIME_MASK (~(0x7F<<0)) + +/* PWMCFG3: reg 0x4E RW */ +#define AW869XX_BIT_PWMCFG3_PR_EN_MASK (~(1<<7)) +#define AW869XX_BIT_PWMCFG3_PR_ENABLE (1<<7) +#define AW869XX_BIT_PWMCFG3_PR_DISABLE (0<<7) +#define AW869XX_BIT_PWMCFG3_PRLVL_MASK (~(0x7F<<0)) + +/* PWMCFG4: reg 0x4F RW */ +/* PRTIME */ + +/* DETCFG1: reg 0x51 RW */ +#define AW869XX_BIT_DETCFG1_FTS_GO_MASK (~(1<<7)) +#define AW869XX_BIT_DETCFG1_FTS_GO_ENABLE (1<<7) +#define AW869XX_BIT_DETCFG1_TEST_GO_MASK (~(1<<6)) +#define AW869XX_BIT_DETCFG1_TEST_GO_ENABLE (1<<6) +#define AW869XX_BIT_DETCFG1_ADO_SLOT_MODE_MASK (~(1<<5)) +#define AW869XX_BIT_DETCFG1_ADO_SLOT_ADC_32 (1<<5) +#define AW869XX_BIT_DETCFG1_ADO_SLOT_ADC_256 (0<<5) +#define AW869XX_BIT_DETCFG1_RL_OS_MASK (~(1<<4)) +#define AW869XX_BIT_DETCFG1_RL (1<<4) +#define AW869XX_BIT_DETCFG1_OS (0<<4) +#define AW869XX_BIT_DETCFG1_PRCT_MODE_MASK (~(1<<3)) +#define AW869XX_BIT_DETCFG1_PRCT_MODE_INVALID (1<<3) +#define AW869XX_BIT_DETCFG1_PRCT_MODE_VALID (0<<3) +#define AW869XX_BIT_DETCFG1_CLK_ADC_MASK (~(7<<0)) +#define AW869XX_BIT_DETCFG1_CLK_ADC_12M (0<<0) +#define AW869XX_BIT_DETCFG1_CLK_ADC_6M (1<<0) +#define AW869XX_BIT_DETCFG1_CLK_ADC_3M (2<<0) +#define AW869XX_BIT_DETCFG1_CLK_ADC_1M5 (3<<0) +#define AW869XX_BIT_DETCFG1_CLK_ADC_M75 (4<<0) +#define AW869XX_BIT_DETCFG1_CLK_ADC_M37 (5<<0) +#define AW869XX_BIT_DETCFG1_CLK_ADC_M18 (6<<0) +#define AW869XX_BIT_DETCFG1_CLK_ADC_M09 (7<<0) + +/* DETCFG2: reg 0x52 RW */ +#define AW869XX_BIT_DETCFG2_VBAT_GO_MASK (~(1<<1)) +#define AW869XX_BIT_DETCFG2_VABT_GO_ON (1<<1) +#define AW869XX_BIT_DETCFG2_DIAG_GO_MASK (~(1<<0)) +#define AW869XX_BIT_DETCFG2_DIAG_GO_ON (1<<0) + +/* DET_RL: reg 0x53 RW */ +/* RL */ + +/* DET_VBAT: reg 0x55 RW */ +/* VBAT */ + +/* DET_LO: reg 0x57 RW */ +#define AW869XX_BIT_DET_LO_TEST_MASK (~(3<<6)) +#define AW869XX_BIT_DET_LO_VBAT_MASK (~(3<<4)) +#define AW869XX_BIT_DET_LO_OS_MASK (~(3<<2)) +#define AW869XX_BIT_DET_LO_RL_MASK (~(3<<0)) + +/* TRIMCFG3: reg:0x5A RW */ +#define AW869XX_BIT_TRIMCFG3_OSC_TRIM_SRC_MASK (~(1<<7)) +#define AW869XX_BIT_TRIMCFG3_OSC_TRIM_SRC_REG (1<<7) +#define AW869XX_BIT_TRIMCFG3_OSC_TRIM_SRC_EFUSE (0<<7) +#define AW869XX_BIT_TRIMCFG3_LRA_TRIM_SRC_MASK (~(1<<6)) +#define AW869XX_BIT_TRIMCFG3_LRA_TRIM_SRC_REG (1<<6) +#define AW869XX_BIT_TRIMCFG3_LRA_TRIM_SRC_EFUSE (0<<6) +#define AW869XX_BIT_TRIMCFG3_TRIM_LRA_MASK (~(63<<0)) + +/* IOCFG1: reg:0x6B RW */ +#define AW869XX_BIT_IOCFG1_HSEN_MASK (~(1<<6)) +#define AW869XX_BIT_IOCFG1_HS_ENABLE (1<<6) +#define AW869XX_BIT_IOCFG1_HS_DISABLE (0<<6) +#define AW869XX_BIT_IOCFG1_IO_FAST_MASK (~(3<<4)) +#define AW869XX_BIT_IOCFG1_ALL_IO_FAST_ENABLE (3<<4) +#define AW869XX_BIT_IOCFG1_IIS_IO_FAST_ENABLE (2<<4) +#define AW869XX_BIT_IOCFG1_IIC_IO_FAST_ENABLE (1<<4) +#define AW869XX_BIT_IOCFG1_IO_FAST_DISABLE (0<<4) + +/* BSTCFG1: reg:0x6D RW */ +#define AW869XX_BIT_BSTCFG1_BST_PC_MASK (~(7<<1)) +#define AW869XX_BIT_BSTCFG1_PEAKCUR_2P75A (0<<1) +#define AW869XX_BIT_BSTCFG1_PEAKCUR_4A (5<<1) + +/* BSTCFG5: reg:0x71 RW */ +#define AW869XX_BIT_BSTCFG5_BST_ADJ_MASK (~(1<<7)) +#define AW869XX_BIT_BSTCFG5_BST_ADJ_HIGH (1<<7) +#define AW869XX_BIT_BSTCFG5_BST_ADJ_LOW (0<<7) + +/********************************************************* + * + * AW8671X Register Detail + * + *********************************************************/ +/* SYSST: reg 0x01 RO */ +#define AW8671X_BIT_SYSST_LOW_VBATS (1<<6) +#define AW8671X_BIT_SYSST_UVLS (1<<5) +#define AW8671X_BIT_SYSST_FF_AES (1<<4) +#define AW8671X_BIT_SYSST_FF_AFS (1<<3) +#define AW8671X_BIT_SYSST_OCDS (1<<2) +#define AW8671X_BIT_SYSST_OTS (1<<1) +#define AW8671X_BIT_SYSST_DONES (1<<0) + +/* SYSINT: reg 0x02 RC */ +#define AW8671X_BIT_SYSINT_LOW_VBATI (1<<6) +#define AW8671X_BIT_SYSINT_UVLI (1<<5) +#define AW8671X_BIT_SYSINT_FF_AEI (1<<4) +#define AW8671X_BIT_SYSINT_FF_AFI (1<<3) +#define AW8671X_BIT_SYSINT_OCDI (1<<2) +#define AW8671X_BIT_SYSINT_OTI (1<<1) +#define AW8671X_BIT_SYSINT_DONEI (1<<0) + +/* SYSINTM: reg 0x03 RW */ +#define AW8671X_BIT_SYSINTM_BST_OVPM_MASK (~(1<<6)) +#define AW8671X_BIT_SYSINTM_BST_OVPM_OFF (1<<6) +#define AW8671X_BIT_SYSINTM_BST_OVPM_ON (0<<6) +#define AW8671X_BIT_SYSINTM_UVLM_MASK (~(1<<5)) +#define AW8671X_BIT_SYSINTM_UVLM_OFF (1<<5) +#define AW8671X_BIT_SYSINTM_UVLM_ON (0<<5) +#define AW8671X_BIT_SYSINTM_FF_AEM_MASK (~(1<<4)) +#define AW8671X_BIT_SYSINTM_FF_AEM_OFF (1<<4) +#define AW8671X_BIT_SYSINTM_FF_AEM_ON (0<<4) +#define AW8671X_BIT_SYSINTM_FF_AFM_MASK (~(1<<3)) +#define AW8671X_BIT_SYSINTM_FF_AFM_OFF (1<<3) +#define AW8671X_BIT_SYSINTM_FF_AFM_ON (0<<3) +#define AW8671X_BIT_SYSINTM_OCDM_MASK (~(1<<2)) +#define AW8671X_BIT_SYSINTM_OCDM_OFF (1<<2) +#define AW8671X_BIT_SYSINTM_OCDM_ON (0<<2) +#define AW8671X_BIT_SYSINTM_OTM_MASK (~(1<<1)) +#define AW8671X_BIT_SYSINTM_OTM_OFF (1<<1) +#define AW8671X_BIT_SYSINTM_OTM_ON (0<<1) +#define AW8671X_BIT_SYSINTM_DONEM_MASK (~(1<<0)) +#define AW8671X_BIT_SYSINTM_DONEM_OFF (1<<0) +#define AW8671X_BIT_SYSINTM_DONEM_ON (0<<0) + +/* SYSST2: reg 0x04 RO */ +#define AW8671X_BIT_SYSST2_FF_FULL (1<<1) +#define AW8671X_BIT_SYSST2_FF_EMPTY (1<<0) + +/* PLAYCFG1: reg 0x06 RW */ +#define AW8671X_BIT_PLAYCFG1_CP_MODE_MASK (~(1<<4)) +#define AW8671X_BIT_PLAYCFG1_CP_MODE_BYPASS (0<<4) +#define AW8671X_BIT_PLAYCFG1_CP_MODE_CHARGEPUMP (1<<4) +#define AW8671X_BIT_PLAYCFG1_CP_CODE_MASK (~(0x0F<<0)) + +/* PLAYCFG2: reg 0x07 RW */ +/* GAIN */ + +/* PLAYCFG3: reg 0x08 RW */ +#define AW8671X_BIT_PLAYCFG3_AUTO_BST_MASK (~(1<<4)) +#define AW8671X_BIT_PLAYCFG3_AUTO_BST_ENABLE (1<<4) +#define AW8671X_BIT_PLAYCFG3_AUTO_BST_DISABLE (0<<4) +#define AW8671X_BIT_PLAYCFG3_STOP_MODE_MASK (~(1<<3)) +#define AW8671X_BIT_PLAYCFG3_STOP_MODE_NOW (1<<3) +#define AW8671X_BIT_PLAYCFG3_STOP_MODE_LATER (0<<3) +#define AW8671X_BIT_PLAYCFG3_BRK_EN_MASK (~(1<<2)) +#define AW8671X_BIT_PLAYCFG3_BRK_ENABLE (1<<2) +#define AW8671X_BIT_PLAYCFG3_BRK_DISABLE (0<<2) +#define AW8671X_BIT_PLAYCFG3_PLAY_MODE_MASK (~(3<<0)) +#define AW8671X_BIT_PLAYCFG3_PLAY_MODE_STOP (3<<0) +#define AW8671X_BIT_PLAYCFG3_PLAY_MODE_CONT (2<<0) +#define AW8671X_BIT_PLAYCFG3_PLAY_MODE_RTP (1<<0) +#define AW8671X_BIT_PLAYCFG3_PLAY_MODE_RAM (0<<0) + +/* PLAYCFG4: reg 0x09 RW */ +#define AW8671X_BIT_PLAYCFG4_STOP_MASK (~(1<<1)) +#define AW8671X_BIT_PLAYCFG4_STOP_ON (1<<1) +#define AW8671X_BIT_PLAYCFG4_STOP_OFF (0<<1) +#define AW8671X_BIT_PLAYCFG4_GO_MASK (~(1<<0)) +#define AW8671X_BIT_PLAYCFG4_GO_ON (1<<0) +#define AW8671X_BIT_PLAYCFG4_GO_OFF (0<<0) + +/* WAVCFG1-8: reg 0x0A - reg 0x11 RW */ +#define AW8671X_BIT_WAVCFG_SEQWAIT_MASK (~(1<<7)) +#define AW8671X_BIT_WAVCFG_SEQWAIT_TIME (1<<7) +#define AW8671X_BIT_WAVCFG_SEQWAIT_NUMBER (0<<7) +#define AW8671X_BIT_WAVCFG_WAVSEQ_NUMBER_MASK (~(0x7F<<0)) + +/* WAVCFG9-12: reg 0x12 - reg 0x15 RW */ +#define AW8671X_BIT_WAVLOOP_SEQ_ODD_MASK (~(0x0F<<4)) +#define AW8671X_BIT_WAVLOOP_SEQ_ODD_INIFINITELY (0x0F<<4) +#define AW8671X_BIT_WAVLOOP_SEQ_EVEN_MASK (~(0x0F<<0)) +#define AW8671X_BIT_WAVLOOP_SEQ_EVEN_INIFINITELY (0x0F<<0) +#define AW8671X_BIT_WAVLOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG9: reg 0x12 RW */ +#define AW8671X_BIT_WAVCFG9_SEQ1LOOP_MASK (~(0x0F<<4)) +#define AW8671X_BIT_WAVCFG9_SEQ1LOOP_INIFINITELY (0x0F<<4) +#define AW8671X_BIT_WAVCFG9_SEQ2LOOP_MASK (~(0x0F<<0)) +#define AW8671X_BIT_WAVCFG9_SEQ2LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG10: reg 0x13 RW */ +#define AW8671X_BIT_WAVCFG10_SEQ3LOOP_MASK (~(0x0F<<4)) +#define AW8671X_BIT_WAVCFG10_SEQ3LOOP_INIFINITELY (0x0F<<4) +#define AW8671X_BIT_WAVCFG10_SEQ4LOOP_MASK (~(0x0F<<0)) +#define AW8671X_BIT_WAVCFG10_SEQ4LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG11: reg 0x14 RW */ +#define AW8671X_BIT_WAVCFG11_SEQ5LOOP_MASK (~(0x0F<<4)) +#define AW8671X_BIT_WAVCFG11_SEQ5LOOP_INIFINITELY (0x0F<<4) +#define AW8671X_BIT_WAVCFG11_SEQ6LOOP_MASK (~(0x0F<<0)) +#define AW8671X_BIT_WAVCFG11_SEQ6LOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG12: reg 0x15 RW */ +#define AW8671X_BIT_WAVCFG12_SEQ7LOOP_MASK (~(0x0F<<4)) +#define AW8671X_BIT_WAVCFG12_SEQ7LOOP_INIFINITELY (0x0F<<4) +#define AW8671X_BIT_WAVCFG12_SEQ8LOOP_MASK (~(0x0F<<0)) +#define AW8671X_BIT_WAVCFG12_SEQ8LOOP_INIFINITELY (0x0F<<0) + +/***************** CONT *****************/ +/* CONTCFG1: reg 0x18 RW */ +#define AW8671X_BIT_CONTCFG1_BRK_BST_MD (~(1<<6)) +#define AW8671X_BIT_CONTCFG1_BRK_BST_MD_ENABLE (1<<6) +#define AW8671X_BIT_CONTCFG1_BRK_BST_MD_DISABLE (0<<6) +#define AW8671X_BIT_CONTCFG1_EN_F0_DET_MASK (~(1<<5)) +#define AW8671X_BIT_CONTCFG1_F0_DET_ENABLE (1<<5) +#define AW8671X_BIT_CONTCFG1_F0_DET_DISABLE (0<<5) +#define AW8671X_BIT_CONTCFG1_SIN_MODE_MASK (~(1<<1)) +#define AW8671X_BIT_CONTCFG1_SIN_MODE_COS (1<<1) +#define AW8671X_BIT_CONTCFG1_SIN_MODE_SINE (0<<1) +#define AW8671X_BIT_CONTCFG1_EDGE_FRE_MASK (~(0x0F<<0)) + +/* CONTCFG3: reg 0x1A RW */ +/* DRV_WIDTH */ + +/* CONTCFG4: reg 0x1B RW */ +/* WAIT_NUM */ + +/* CONTCFG5: reg 0x1C RW */ +#define AW8671X_BIT_CONTCFG5_BST_BRK_GAIN_MASK (~(0x0F<<4)) +#define AW8671X_BIT_CONTCFG5_BRK_GAIN_MASK (~(0x0F<<0)) + +/* CONTCFG6: reg 0x1D RW */ +#define AW8671X_BIT_CONTCFG6_TRACK_EN_MASK (~(1<<7)) +#define AW8671X_BIT_CONTCFG6_TRACK_ENABLE (1<<7) +#define AW8671X_BIT_CONTCFG6_TRACK_DISABLE (0<<7) +#define AW8671X_BIT_CONTCFG6_DRV1_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG7: reg 0x1E RW */ +#define AW8671X_BIT_CONTCFG7_DRV2_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG8: reg 0x1F RW */ +/* DRV1_TIME */ + +/* CONTCFG9: reg 0x20 RW */ +/* DRV2_TIME */ + +/* CONTCFG10: reg 0x21 RW */ +/* BRK_TIME */ + +/* CONTCFG11: reg 0x22 RW */ +/* TRACK_MARGIN */ + +/* CONTCFG13: reg 0x24 RW */ +#define AW8671X_BIT_CONTCFG13_TSET_MASK (~(0x0F<<4)) +#define AW8671X_BIT_CONTCFG13_BEME_SET_MASK (~(0x0F<<0)) + +/* CONTRD14: reg 0x25 RO */ +/* F_LRA_F0_H */ + +/* CONTRD15: reg 0x26 RO */ +/* F_LRA_F0_L */ + +/* CONTRD16: reg 0x27 RO */ +/* CONT_F0_H */ + +/* CONTRD17: reg 0x28 RO */ +/* CONT_F0_L */ + +/***************** RTP *****************/ +/* RTPCFG1: reg 0x2D RW */ +#define AW8671X_BIT_RTPCFG1_BASE_ADDR_H_MASK (~(0x0F<<0)) + +/* RTPCFG2: reg 0x2E RW */ +/* BASE_ADDR_L */ + +/* RTPCFG3: reg 0x2F RW */ +#define AW8671X_BIT_RTPCFG3_FIFO_AEH_MASK (~(0x0F<<4)) +#define AW8671X_BIT_RTPCFG3_FIFO_AFH_MASK (~(0x0F<<0)) +#define AW8671X_BIT_RTPCFG3_FIFO_AEH (0x0F<<4) +#define AW8671X_BIT_RTPCFG3_FIFO_AFH (0x0F<<0) + +/* RTPCFG4: reg 0x30 RW */ +/* FIFO_AEL */ + +/* RTPCFG5: reg 0x31 RW */ +/* FIFO_AFL */ + +/* RTPDATA: reg 0x32 RW */ +/* FIFO_AFL */ + +/***************** TRIGGER *****************/ +#define AW8671X_BIT_TRG_ENABLE_MASK (~(1<<7)) +#define AW8671X_BIT_TRG_ENABLE (1<<7) +#define AW8671X_BIT_TRG_DISABLE (0<<7) +#define AW8671X_BIT_TRG_SEQ_MASK (~(0x7F<<0)) + +/* TRGCFG1: reg 0x33 RW */ +#define AW8671X_BIT_TRGCFG1_TRG1_POS_MASK (~(1<<7)) +#define AW8671X_BIT_TRGCFG1_TRG1_POS_ENABLE (1<<7) +#define AW8671X_BIT_TRGCFG1_TRG1_POS_DISABLE (0<<7) +#define AW8671X_BIT_TRGCFG1_TRG1SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG2: reg 0x34 RW */ +#define AW8671X_BIT_TRGCFG2_TRG2_POS_MASK (~(1<<7)) +#define AW8671X_BIT_TRGCFG2_TRG2_POS_ENABLE (1<<7) +#define AW8671X_BIT_TRGCFG2_TRG2_POS_DISABLE (0<<7) +#define AW8671X_BIT_TRGCFG2_TRG2SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG3: reg 0x35 RW */ +#define AW8671X_BIT_TRGCFG3_TRG3_POS_MASK (~(1<<7)) +#define AW8671X_BIT_TRGCFG3_TRG3_POS_ENABLE (1<<7) +#define AW8671X_BIT_TRGCFG3_TRG3_POS_DISABLE (0<<7) +#define AW8671X_BIT_TRGCFG3_TRG3SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG4: reg 0x36 RW */ +#define AW8671X_BIT_TRGCFG4_TRG1_NEG_MASK (~(1<<7)) +#define AW8671X_BIT_TRGCFG4_TRG1_NEG_ENABLE (1<<7) +#define AW8671X_BIT_TRGCFG4_TRG1_NEG_DISABLE (0<<7) +#define AW8671X_BIT_TRGCFG4_TRG1SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG5: reg 0x37 RW */ +#define AW8671X_BIT_TRGCFG5_TRG2_NEG_MASK (~(1<<7)) +#define AW8671X_BIT_TRGCFG5_TRG2_NEG_ENABLE (1<<7) +#define AW8671X_BIT_TRGCFG5_TRG2_NEG_DISABLE (0<<7) +#define AW8671X_BIT_TRGCFG5_TRG2SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG6: reg 0x38 RW */ +#define AW8671X_BIT_TRGCFG6_TRG3_NEG_MASK (~(1<<7)) +#define AW8671X_BIT_TRGCFG6_TRG3_NEG_ENABLE (1<<7) +#define AW8671X_BIT_TRGCFG6_TRG3_NEG_DISABLE (0<<7) +#define AW8671X_BIT_TRGCFG6_TRG3SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG7: reg 0x39 RW */ +#define AW8671X_BIT_TRGCFG7_TRG1_POLAR_MASK (~(1<<7)) +#define AW8671X_BIT_TRGCFG7_TRG1_POLAR_NEG (1<<7) +#define AW8671X_BIT_TRGCFG7_TRG1_POLAR_POS (0<<7) +#define AW8671X_BIT_TRGCFG7_TRG1_MODE_MASK (~(1<<6)) +#define AW8671X_BIT_TRGCFG7_TRG1_MODE_LEVEL (1<<6) +#define AW8671X_BIT_TRGCFG7_TRG1_MODE_EDGE (0<<6) +#define AW8671X_BIT_TRGCFG7_TRG1_AUTO_BRK_MASK (~(1<<5)) +#define AW8671X_BIT_TRGCFG7_TRG1_AUTO_BRK_ENABLE (1<<5) +#define AW8671X_BIT_TRGCFG7_TRG1_AUTO_BRK_DISABLE (0<<5) +#define AW8671X_BIT_TRGCFG7_TRG1_BST_MASK (~(1<<4)) +#define AW8671X_BIT_TRGCFG7_TRG1_BST_ENABLE (1<<4) +#define AW8671X_BIT_TRGCFG7_TRG1_BST_DISABLE (0<<4) +#define AW8671X_BIT_TRGCFG7_TRG2_POLAR_MASK (~(1<<3)) +#define AW8671X_BIT_TRGCFG7_TRG2_POLAR_NEG (1<<3) +#define AW8671X_BIT_TRGCFG7_TRG2_POLAR_POS (0<<3) +#define AW8671X_BIT_TRGCFG7_TRG2_MODE_MASK (~(1<<2)) +#define AW8671X_BIT_TRGCFG7_TRG2_MODE_LEVEL (1<<2) +#define AW8671X_BIT_TRGCFG7_TRG2_MODE_EDGE (0<<2) +#define AW8671X_BIT_TRGCFG7_TRG2_AUTO_BRK_MASK (~(1<<1)) +#define AW8671X_BIT_TRGCFG7_TRG2_AUTO_BRK_ENABLE (1<<1) +#define AW8671X_BIT_TRGCFG7_TRG2_AUTO_BRK_DISABLE (0<<1) +#define AW8671X_BIT_TRGCFG7_TRG2_BST_MASK (~(1<<0)) +#define AW8671X_BIT_TRGCFG7_TRG2_BST_ENABLE (1<<0) +#define AW8671X_BIT_TRGCFG7_TRG2_BST_DISABLE (0<<0) + +/* TRGCFG8: reg 0x3A RW */ +#define AW8671X_BIT_TRGCFG8_TRG3_POLAR_MASK (~(1<<7)) +#define AW8671X_BIT_TRGCFG8_TRG3_POLAR_NEG (1<<7) +#define AW8671X_BIT_TRGCFG8_TRG3_POLAR_POS (0<<7) +#define AW8671X_BIT_TRGCFG8_TRG3_MODE_MASK (~(1<<6)) +#define AW8671X_BIT_TRGCFG8_TRG3_MODE_LEVEL (1<<6) +#define AW8671X_BIT_TRGCFG8_TRG3_MODE_EDGE (0<<6) +#define AW8671X_BIT_TRGCFG8_TRG3_AUTO_BRK_MASK (~(1<<5)) +#define AW8671X_BIT_TRGCFG8_TRG3_AUTO_BRK_ENABLE (1<<5) +#define AW8671X_BIT_TRGCFG8_TRG3_AUTO_BRK_DISABLE (0<<5) +#define AW8671X_BIT_TRGCFG8_TRG3_BST_MASK (~(1<<4)) +#define AW8671X_BIT_TRGCFG8_TRG3_BST_ENABLE (1<<4) +#define AW8671X_BIT_TRGCFG8_TRG3_BST_DISABLE (0<<4) +#define AW8671X_BIT_TRGCFG8_TRG_ONEWIRE_MASK (~(1<<3)) +#define AW8671X_BIT_TRGCFG8_TRG_ONEWIRE_ENABLE (1<<3) +#define AW8671X_BIT_TRGCFG8_TRG_ONEWIRE_DISABLE (0<<3) +#define AW8671X_BIT_TRGCFG8_TRG1_STOP_MASK (~(1<<2)) +#define AW8671X_BIT_TRGCFG8_TRG1_STOP (1<<2) +#define AW8671X_BIT_TRGCFG8_TRG2_STOP_MASK (~(1<<1)) +#define AW8671X_BIT_TRGCFG8_TRG2_STOP (1<<1) +#define AW8671X_BIT_TRGCFG8_TRG3_STOP_MASK (~(1<<0)) +#define AW8671X_BIT_TRGCFG8_TRG3_STOP (1<<0) + +/* GLBCFG2: reg 0x3C RW */ +/* START_DLY */ +#define AW8671X_BIT_START_DLY_20US (0x01) +#define AW8671X_BIT_START_DLY_2P5MS (0x75) + +/* GLBCFG4: reg 0x3E RW */ +#define AW8671X_BIT_GLBCFG4_GO_PRIO_MASK (~(3<<6)) +#define AW8671X_BIT_GLBCFG4_TRG3_PRIO_MASK (~(3<<4)) +#define AW8671X_BIT_GLBCFG4_TRG2_PRIO_MASK (~(3<<2)) +#define AW8671X_BIT_GLBCFG4_TRG1_PRIO_MASK (~(3<<0)) + +/* GLBRD5: reg 0x3F R0 */ +/* GLB_STATE [3:0] */ +#define AW8671X_BIT_GLBRD5_STATE_MASK (~(0x0F<<0)) +#define AW8671X_BIT_GLBRD5_STATE_STANDBY (0<<0) +#define AW8671X_BIT_GLBRD5_STATE_CONT_GO (6<<0) +#define AW8671X_BIT_GLBRD5_STATE_RAM_GO (7<<0) +#define AW8671X_BIT_GLBRD5_STATE_RTP_GO (8<<0) +#define AW8671X_BIT_GLBRD5_STATE_TRIG_GO (9<<0) +#define AW8671X_BIT_GLBRD5_STATE_I2S_GO (0x0A<<0) +#define AW8671X_BIT_GLBRD5_STATE_BRAKE (0x0B<<0) + +/* RAMADDRH: reg 0x40 RWS */ +#define AW8671X_BIT_RAMADDRH_MASK (~(0x0F<<0)) + +/* RAMADDRL: reg 0x41 RWS */ +/* RAMADDRL */ + +/* RAMDATA: reg 0x42 RWS */ +/* RAMDATA */ + +/***************** SYSCTRL *****************/ +/* SYSCTRL1: reg 0x43 RW */ +#define AW8671X_BIT_SYSCTRL1_RAMINIT_MASK (~(1<<3)) +#define AW8671X_BIT_SYSCTRL1_RAMINIT_ON (1<<3) +#define AW8671X_BIT_SYSCTRL1_RAMINIT_OFF (0<<3) +#define AW8671X_BIT_SYSCTRL1_EN_FIR_MASK (~(1<<2)) +#define AW8671X_BIT_SYSCTRL1_FIR_ENABLE (0<<2) + +/* SYSCTRL2: reg 0x44 RW */ +#define AW8671X_BIT_SYSCTRL2_WAVDAT_MODE (3<<0) +#define AW8671X_BIT_SYSCTRL2_STANDBY_MASK (~(1<<5)) +#define AW8671X_BIT_SYSCTRL2_STANDBY_ON (1<<5) +#define AW8671X_BIT_SYSCTRL2_STANDBY_OFF (0<<5) +#define AW8671X_BIT_SYSCTRL2_I2S_PIN_MASK (~(1<<2)) +#define AW8671X_BIT_SYSCTRL2_I2S_PIN_I2S (1<<2) +#define AW8671X_BIT_SYSCTRL2_I2S_PIN_TRIG (0<<2) +#define AW8671X_BIT_SYSCTRL2_WAVDAT_MODE_MASK (~(3<<0)) +#define AW8671X_BIT_SYSCTRL2_RATE_12K (2<<0) +#define AW8671X_BIT_SYSCTRL2_RATE_24K (0<<0) +#define AW8671X_BIT_SYSCTRL2_RATE_48K (1<<0) + +/* SYSCTRL3: reg 0x45 RW */ +#define AW8671X_BIT_SYSCTRL3_INT_EDGE_MODE_MASK (~(1<<5)) +#define AW8671X_BIT_SYSCTRL3_INT_EDGE_MODE_POS (0<<5) +#define AW8671X_BIT_SYSCTRL3_INT_EDGE_MODE_BOTH (1<<5) +#define AW8671X_BIT_SYSCTRL3_INT_MODE_MASK (~(1<<4)) +#define AW8671X_BIT_SYSCTRL3_INT_MODE_EDGE (1<<4) +#define AW8671X_BIT_SYSCTRL3_INT_MODE_LEVEL (0<<4) +#define AW8671X_BIT_SYSCTRL3_GAIN_BYPASS_MASK (~(1<<1)) +#define AW8671X_BIT_SYSCTRL3_GAIN_CHANGEABLE (1<<1) +#define AW8671X_BIT_SYSCTRL3_GAIN_FIXED (0<<1) + +/***************** I2S *****************/ +/* I2SCFG1: reg 0x47 RW */ +#define AW8671X_BIT_I2SCFG1_I2S_EN_MASK (~(1<<7)) +#define AW8671X_BIT_I2SCFG1_I2S_ENABLE (1<<7) +#define AW8671X_BIT_I2SCFG1_I2S_DISABLE (0<<7) + +#define AW8671X_BIT_I2SCFG2_I2S_INT_MASK (~(1<<5)) +#define AW8671X_BIT_I2SCFG2_I2S_INT_ON (1<<5) +#define AW8671X_BIT_I2SCFG2_I2S_INT_OFF (0<<5) + +#define AW8671X_BIT_I2SCFG1_I2SMD_MASK (~(3<<3)) +#define AW8671X_BIT_I2SCFG1_SLOT_NUM_MASK (~(7<<0)) + +/* I2SCFG3: reg 0x49 RW */ +#define AW8671X_BIT_I2SCFG3_I2SFS_MASK (~(3<<2)) +#define AW8671X_BIT_I2SCFG3_I2SFS_16BITS (0<<2) +#define AW8671X_BIT_I2SCFG3_I2SFS_20BITS (1<<2) +#define AW8671X_BIT_I2SCFG3_I2SFS_24BITS (2<<2) +#define AW8671X_BIT_I2SCFG3_I2SFS_32BITS (3<<2) +#define AW8671X_BIT_I2SCFG3_I2SBCK_MASK (~(3<<0)) +#define AW8671X_BIT_I2SCFG3_I2SBCK_32FS (0<<0) +#define AW8671X_BIT_I2SCFG3_I2SBCK_48FS (1<<0) +#define AW8671X_BIT_I2SCFG3_I2SBCK_64FS (2<<0) + +/* PWMCFG1: reg 0x4C RW */ +#define AW8671X_BIT_PWMCFG1_PRC_EN_MASK (~(1<<7)) +#define AW8671X_BIT_PWMCFG1_PRC_ENABLE (1<<7) +#define AW8671X_BIT_PWMCFG1_PRC_DISABLE (0<<7) +#define AW8671X_BIT_PWMCFG1_PRCTIME_MASK (~(0x7F<<0)) + +/* PWMCFG2: reg 0x4D RW */ +#define AW8671X_BIT_PWMCFG2_PRCT_MODE_MASK (~(1<<6)) +#define AW8671X_BIT_PWMCFG2_PRCT_MODE_VALID (0<<6) +#define AW8671X_BIT_PWMCFG2_PRCT_MODE_INVALID (1<<6) + +/* PWMCFG3: reg 0x4E RW */ +#define AW8671X_BIT_PWMCFG3_PR_EN_MASK (~(1<<7)) +#define AW8671X_BIT_PWMCFG3_PR_ENABLE (1<<7) +#define AW8671X_BIT_PWMCFG3_PR_DISABLE (0<<7) +#define AW8671X_BIT_PWMCFG3_PRLVL_MASK (~(0x7F<<0)) + +/* PWMCFG4: reg 0x4F RW */ +/* PRTIME */ + +/* VBATCTRL: reg 0x50 RW */ +#define AW8671X_BIT_VBATCTRL_VBAT_MODE_MASK (~(1<<6)) +#define AW8671X_BIT_VBATCTRL_VBAT_MODE_HW (1<<6) +#define AW8671X_BIT_VBATCTRL_VBAT_MODE_SW (0<<6) + +/* DETCFG1: reg 0x51 RW */ +#define AW8671X_BIT_DETCFG1_VBAT_REF_MASK (~(7<<4)) +#define AW8671X_BIT_DETCFG1_ADC_FS_MASK (~(3<<2)) +#define AW8671X_BIT_DETCFG1_ADC_FS (3<<2) +#define AW8671X_BIT_DETCFG1_ADC_FS_96KHZ (0<<2) +#define AW8671X_BIT_DETCFG1_ADC_FS_48KHZ (1<<2) +#define AW8671X_BIT_DETCFG1_ADC_FS_24KHZ (2<<2) +#define AW8671X_BIT_DETCFG1_ADC_FS_12KHZ (3<<2) + + +#define AW8671X_BIT_DETCFG1_DET_GO_MASK (~(3<<0)) +#define AW8671X_BIT_DETCFG1_DET_GO_NA (0<<0) +#define AW8671X_BIT_DETCFG1_DET_GO_DET_SEQ0 (1<<0) + +/* DETCFG3: reg 0x52 RW */ +#define AW8671X_BIT_DETCFG3_DET_SEQ0_MASK (~(0x0F<<3)) +#define AW8671X_BIT_DETCFG3_DET_SEQ0_VBAT (0<<3) +#define AW8671X_BIT_DETCFG3_DET_SEQ0_PVDD (1<<3) +#define AW8671X_BIT_DETCFG3_DET_SEQ0_ISEN (2<<3) +#define AW8671X_BIT_DETCFG3_DET_SEQ0_TEMP (3<<3) +#define AW8671X_BIT_DETCFG3_DET_SEQ0_RL (4<<3) +#define AW8671X_BIT_DETCFG3_DET_SEQ0_OS (5<<3) +#define AW8671X_BIT_DETCFG3_DET_SEQ0_VOUT (6<<3) + +#define AW8671X_BIT_DETCFG3_D2S_GAIN_MASK (~(7<<0)) +#define AW8671X_BIT_DETCFG3_D2S_GAIN (7<<0) +#define AW8671X_BIT_DETCFG3_D2S_GAIN_1 (0<<0) +#define AW8671X_BIT_DETCFG3_D2S_GAIN_2 (1<<0) +#define AW8671X_BIT_DETCFG3_D2S_GAIN_4 (2<<0) +#define AW8671X_BIT_DETCFG3_D2S_GAIN_8 (3<<0) +#define AW8671X_BIT_DETCFG3_D2S_GAIN_10 (4<<0) +#define AW8671X_BIT_DETCFG3_D2S_GAIN_16 (5<<0) +#define AW8671X_BIT_DETCFG3_D2S_GAIN_20 (6<<0) +#define AW8671X_BIT_DETCFG3_D2S_GAIN_40 (7<<0) + +/* DETRD1: reg 0x54 RW */ +#define AW8671X_BIT_DETRD1_ADC_DATA_H_MASK (~(0x0F<<4)) +#define AW8671X_BIT_DETRD1_AVG_DATA_H_MASK (~(0x0F<<0)) +#define AW8671X_BIT_DETRD1_AVG_DATA (0x0F<<0) + +/* DETRD2: reg 0x55 RW */ +/* AVG_DATA_L */ + +/* IDH: reg 0x57 RW */ +/* CHIPID_H */ + +/* IDL: reg 0x58 RW */ +/* CHIPID_L */ + +/* TRIMCFG2: reg:0x5A RW */ +#define AW8671X_BIT_TRIMCFG2_TRIM_OSC_H_MASK (~(1<<7)) +#define AW8671X_BIT_TRIMCFG2_OSC_TRIM_SRC_MASK (~(1<<6)) +#define AW8671X_BIT_TRIMCFG2_OSC_TRIM_SRC_REG (1<<6) +#define AW8671X_BIT_TRIMCFG2_OSC_TRIM_SRC_EFUSE (0<<6) +#define AW8671X_BIT_TRIMCFG2_TRIM_LRA_MASK (~(0x3F<<0)) + +/********************************************************* + * + * AW8692X Register Detail + * + *********************************************************/ +/* RSTCFG: reg 0x01 RO */ +#define AW86925_BIT_RSTCFG_PRE_VAL (0x00) +#define AW86926_BIT_RSTCFG_PRE_VAL (0x01) +#define AW86927_BIT_RSTCFG_PRE_VAL (0x02) +#define AW86928_BIT_RSTCFG_PRE_VAL (0x03) +#define AW86925_BIT_RSTCFG_VAL (0x80) +#define AW86926_BIT_RSTCFG_VAL (0x81) +#define AW86927_BIT_RSTCFG_VAL (0x82) +#define AW86928_BIT_RSTCFG_VAL (0x83) +/* SYSST: reg 0x01 RO */ +#define AW8692X_BIT_SYSST_BST_SCPS (1<<7) +#define AW8692X_BIT_SYSST_BST_OVPS (1<<6) +#define AW8692X_BIT_SYSST_UVLS (1<<5) +#define AW8692X_BIT_SYSST_FF_AES (1<<4) +#define AW8692X_BIT_SYSST_FF_AFS (1<<3) +#define AW8692X_BIT_SYSST_OCDS (1<<2) +#define AW8692X_BIT_SYSST_OTS (1<<1) +#define AW8692X_BIT_SYSST_DONES (1<<0) +/* SYSINT: reg 0x02 RC */ +#define AW8692X_BIT_SYSINT_BST_SCPI (1<<7) +#define AW8692X_BIT_SYSINT_BST_OVPI (1<<6) +#define AW8692X_BIT_SYSINT_UVLI (1<<5) +#define AW8692X_BIT_SYSINT_FF_AEI (1<<4) +#define AW8692X_BIT_SYSINT_FF_AFI (1<<3) +#define AW8692X_BIT_SYSINT_OCDI (1<<2) +#define AW8692X_BIT_SYSINT_OTI (1<<1) +#define AW8692X_BIT_SYSINT_DONEI (1<<0) + +/* SYSINTM: reg 0x03 RW */ +#define AW8692X_BIT_SYSINTM_BST_SCPM_MASK (~(1<<7)) +#define AW8692X_BIT_SYSINTM_BST_SCPM_OFF (1<<7) +#define AW8692X_BIT_SYSINTM_BST_SCPM_ON (0<<7) +#define AW8692X_BIT_SYSINTM_BST_OVPM_MASK (~(1<<6)) +#define AW8692X_BIT_SYSINTM_BST_OVPM_OFF (1<<6) +#define AW8692X_BIT_SYSINTM_BST_OVPM_ON (0<<6) +#define AW8692X_BIT_SYSINTM_UVLM_MASK (~(1<<5)) +#define AW8692X_BIT_SYSINTM_UVLM_OFF (1<<5) +#define AW8692X_BIT_SYSINTM_UVLM_ON (0<<5) +#define AW8692X_BIT_SYSINTM_FF_AEM_MASK (~(1<<4)) +#define AW8692X_BIT_SYSINTM_FF_AEM_OFF (1<<4) +#define AW8692X_BIT_SYSINTM_FF_AEM_ON (0<<4) +#define AW8692X_BIT_SYSINTM_FF_AFM_MASK (~(1<<3)) +#define AW8692X_BIT_SYSINTM_FF_AFM_OFF (1<<3) +#define AW8692X_BIT_SYSINTM_FF_AFM_ON (0<<3) +#define AW8692X_BIT_SYSINTM_OCDM_MASK (~(1<<2)) +#define AW8692X_BIT_SYSINTM_OCDM_OFF (1<<2) +#define AW8692X_BIT_SYSINTM_OCDM_ON (0<<2) +#define AW8692X_BIT_SYSINTM_OTM_MASK (~(1<<1)) +#define AW8692X_BIT_SYSINTM_OTM_OFF (1<<1) +#define AW8692X_BIT_SYSINTM_OTM_ON (0<<1) +#define AW8692X_BIT_SYSINTM_DONEM_MASK (~(1<<0)) +#define AW8692X_BIT_SYSINTM_DONEM_OFF (1<<0) +#define AW8692X_BIT_SYSINTM_DONEM_ON (0<<0) + +/* SYSST2: reg 0x04 RO */ +#define AW8692X_BIT_SYSST2_BST_OK (1<<4) +#define AW8692X_BIT_SYSST2_VBG_OK (1<<3) +#define AW8692X_BIT_SYSST2_LDO_OK (1<<2) +#define AW8692X_BIT_SYSST2_FF_FULL (1<<1) +#define AW8692X_BIT_SYSST2_FF_EMPTY (1<<0) + +/* PLAYCFG1: reg 0x06 RW */ +#define AW8692X_BIT_PLAYCFG1_BST_MODE_MASK (~(1<<7)) +#define AW8692X_BIT_PLAYCFG1_BST_MODE (1<<7) +#define AW8692X_BIT_PLAYCFG1_BST_MODE_BYPASS (0<<7) +#define AW8692X_BIT_PLAYCFG1_BST_VOUT_VREFSET_MASK (~(0x7F<<0)) + +/* PLAYCFG3: reg 0x08 RW */ +#define AW8692X_BIT_PLAYCFG3_ONEWIRE_COMP_MASK (~(1<<5)) +#define AW8692X_BIT_PLAYCFG3_1908_ONEWIRE_MODE (1<<5) +#define AW8692X_BIT_PLAYCFG3_2102_ONEWIRE_MODE (0<<5) +#define AW8692X_BIT_PLAYCFG3_AUTO_BST_MASK (~(1<<4)) +#define AW8692X_BIT_PLAYCFG3_AUTO_BST_ENABLE (1<<4) +#define AW8692X_BIT_PLAYCFG3_AUTO_BST_DISABLE (0<<4) +#define AW8692X_BIT_PLAYCFG3_STOP_MODE_MASK (~(1<<3)) +#define AW8692X_BIT_PLAYCFG3_STOP_MODE_NOW (1<<3) +#define AW8692X_BIT_PLAYCFG3_STOP_MODE_LATER (0<<3) +#define AW8692X_BIT_PLAYCFG3_BRK_EN_MASK (~(1<<2)) +#define AW8692X_BIT_PLAYCFG3_BRK_ENABLE (1<<2) +#define AW8692X_BIT_PLAYCFG3_BRK_DISABLE (0<<2) +#define AW8692X_BIT_PLAYCFG3_PLAY_MODE_MASK (~(3<<0)) +#define AW8692X_BIT_PLAYCFG3_PLAY_MODE_STOP (3<<0) +#define AW8692X_BIT_PLAYCFG3_PLAY_MODE_CONT (2<<0) +#define AW8692X_BIT_PLAYCFG3_PLAY_MODE_RTP (1<<0) +#define AW8692X_BIT_PLAYCFG3_PLAY_MODE_RAM (0<<0) + +/* PLAYCFG4: reg 0x09 RW */ +#define AW8692X_BIT_PLAYCFG4_STOP_MASK (~(1<<1)) +#define AW8692X_BIT_PLAYCFG4_STOP_ON (1<<1) +#define AW8692X_BIT_PLAYCFG4_STOP_OFF (0<<1) +#define AW8692X_BIT_PLAYCFG4_GO_MASK (~(1<<0)) +#define AW8692X_BIT_PLAYCFG4_GO_ON (1<<0) +#define AW8692X_BIT_PLAYCFG4_GO_OFF (0<<0) + +/* WAVCFG9-12: reg 0x12 - reg 0x15 RW */ +#define AW8692X_BIT_WAVLOOP_SEQ_ODD_MASK (~(0x0F<<4)) +#define AW8692X_BIT_WAVLOOP_SEQ_ODD_INIFINITELY (0x0F<<4) +#define AW8692X_BIT_WAVLOOP_SEQ_EVEN_MASK (~(0x0F<<0)) +#define AW8692X_BIT_WAVLOOP_SEQ_EVEN_INIFINITELY (0x0F<<0) +#define AW8692X_WAVLOOP_INIFINITELY (0x0F<<0) + +/* WAVCFG9: reg 0x12 RW */ +#define AW8692X_BIT_WAVCFG9_SEQ1LOOP_MASK (~(0x0F<<4)) +#define AW8692X_BIT_WAVCFG9_SEQ1LOOP_INIFINITELY (0x0F<<4) +#define AW8692X_BIT_WAVCFG9_SEQ2LOOP_MASK (~(0x0F<<0)) +#define AW8692X_BIT_WAVCFG9_SEQ2LOOP_INIFINITELY (0x0F<<0) + +/***************** CONT *****************/ +/* CONTCFG1: reg 0x18 RW */ +#define AW8692X_BIT_CONTCFG1_BRK_BST_MD_MASK (~(1<<6)) +#define AW8692X_BIT_CONTCFG1_BRK_BST_MD_ENABLE (1<<6) +#define AW8692X_BIT_CONTCFG1_BRK_BST_MD_DISABLE (0<<6) +#define AW8692X_BIT_CONTCFG1_EN_F0_DET_MASK (~(1<<5)) +#define AW8692X_BIT_CONTCFG1_F0_DET_ENABLE (1<<5) +#define AW8692X_BIT_CONTCFG1_F0_DET_DISABLE (0<<5) +#define AW8692X_BIT_CONTCFG1_SIN_MODE_MASK (~(1<<1)) +#define AW8692X_BIT_CONTCFG1_SIN_MODE_COS (1<<1) +#define AW8692X_BIT_CONTCFG1_SIN_MODE_SINE (0<<1) +#define AW8692X_BIT_CONTCFG1_EDGE_FRE_MASK (~(0x0F<<0)) + +/* CONTCFG5: reg 0x1C RW */ +#define AW8692X_BIT_CONTCFG5_BST_BRK_GAIN_MASK (~(0x0F<<4)) +#define AW8692X_BIT_CONTCFG5_BRK_GAIN_MASK (~(0x0F<<0)) + + +/* CONTCFG6: reg 0x1D RW */ +#define AW8692X_BIT_CONTCFG6_TRACK_EN_MASK (~(1<<7)) +#define AW8692X_BIT_CONTCFG6_TRACK_ENABLE (1<<7) +#define AW8692X_BIT_CONTCFG6_TRACK_DISABLE (0<<7) +#define AW8692X_BIT_CONTCFG6_DRV1_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG7: reg 0x1E RW */ +#define AW8692X_BIT_CONTCFG7_DRV2_LVL_MASK (~(0x7F<<0)) + +/* CONTCFG13: reg 0x24 RW */ +#define AW8692X_BIT_CONTCFG13_TSET_MASK (~(0x0F<<4)) +#define AW8692X_BIT_CONTCFG13_BEME_SET_MASK (~(0x0F<<0)) + +/***************** RTP *****************/ +/* RTPCFG1: reg 0x2D RW */ +#define AW8692X_BIT_RTPCFG1_TWORTP_EN_MASK (~(1<<6)) +#define AW8692X_BIT_RTPCFG1_TWORTP_ENABLE (1<<6) +#define AW8692X_BIT_RTPCFG1_TWORTP_DISABLE (0<<6) +#define AW8692X_BIT_RTPCFG1_BASE_ADDR_H_MASK (~(0x1F<<0)) + +/* RTPCFG3: reg 0x2F RW */ +#define AW8692X_BIT_RTPCFG3_FIFO_AEH_MASK (~(0x0F<<4)) +#define AW8692X_BIT_RTPCFG3_FIFO_AFH_MASK (~(0x0F<<0)) +#define AW8692X_BIT_RTPCFG3_FIFO_AEH (0x0F<<4) +#define AW8692X_BIT_RTPCFG3_FIFO_AFH (0x0F<<0) + +/***************** TRIGGER *****************/ +#define AW8692X_BIT_TRG_ENABLE_MASK (~(1<<7)) +#define AW8692X_BIT_TRG_ENABLE (1<<7) +#define AW8692X_BIT_TRG_DISABLE (0<<7) +#define AW8692X_BIT_TRG_SEQ_MASK (~(0x7F<<0)) +/* TRGCFG1: reg 0x33 RW */ +#define AW8692X_BIT_TRGCFG1_TRG1_POS_MASK (~(1<<7)) +#define AW8692X_BIT_TRGCFG1_TRG1_POS_ENABLE (1<<7) +#define AW8692X_BIT_TRGCFG1_TRG1_POS_DISABLE (0<<7) +#define AW8692X_BIT_TRGCFG1_TRG1SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG2: reg 0x34 RW */ +#define AW8692X_BIT_TRGCFG2_TRG2_POS_MASK (~(1<<7)) +#define AW8692X_BIT_TRGCFG2_TRG2_POS_ENABLE (1<<7) +#define AW8692X_BIT_TRGCFG2_TRG2_POS_DISABLE (0<<7) +#define AW8692X_BIT_TRGCFG2_TRG2SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG3: reg 0x35 RW */ +#define AW8692X_BIT_TRGCFG3_TRG3_POS_MASK (~(1<<7)) +#define AW8692X_BIT_TRGCFG3_TRG3_POS_ENABLE (1<<7) +#define AW8692X_BIT_TRGCFG3_TRG3_POS_DISABLE (0<<7) +#define AW8692X_BIT_TRGCFG3_TRG3SEQ_P_MASK (~(0x7F<<0)) + +/* TRGCFG4: reg 0x36 RW */ +#define AW8692X_BIT_TRGCFG4_TRG1_NEG_MASK (~(1<<7)) +#define AW8692X_BIT_TRGCFG4_TRG1_NEG_ENABLE (1<<7) +#define AW8692X_BIT_TRGCFG4_TRG1_NEG_DISABLE (0<<7) +#define AW8692X_BIT_TRGCFG4_TRG1SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG5: reg 0x37 RW */ +#define AW8692X_BIT_TRGCFG5_TRG2_NEG_MASK (~(1<<7)) +#define AW8692X_BIT_TRGCFG5_TRG2_NEG_ENABLE (1<<7) +#define AW8692X_BIT_TRGCFG5_TRG2_NEG_DISABLE (0<<7) +#define AW8692X_BIT_TRGCFG5_TRG2SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG6: reg 0x38 RW */ +#define AW8692X_BIT_TRGCFG6_TRG3_NEG_MASK (~(1<<7)) +#define AW8692X_BIT_TRGCFG6_TRG3_NEG_ENABLE (1<<7) +#define AW8692X_BIT_TRGCFG6_TRG3_NEG_DISABLE (0<<7) +#define AW8692X_BIT_TRGCFG6_TRG3SEQ_N_MASK (~(0x7F<<0)) + +/* TRGCFG7: reg 0x39 RW */ +#define AW8692X_BIT_TRGCFG7_TRG1_POLAR_MASK (~(1<<7)) +#define AW8692X_BIT_TRGCFG7_TRG1_POLAR_NEG (1<<7) +#define AW8692X_BIT_TRGCFG7_TRG1_POLAR_POS (0<<7) +#define AW8692X_BIT_TRGCFG7_TRG1_MODE_MASK (~(1<<6)) +#define AW8692X_BIT_TRGCFG7_TRG1_MODE_LEVEL (1<<6) +#define AW8692X_BIT_TRGCFG7_TRG1_MODE_EDGE (0<<6) +#define AW8692X_BIT_TRGCFG7_TRG1_AUTO_BRK_MASK (~(1<<5)) +#define AW8692X_BIT_TRGCFG7_TRG1_AUTO_BRK_ENABLE (1<<5) +#define AW8692X_BIT_TRGCFG7_TRG1_AUTO_BRK_DISABLE (0<<5) +#define AW8692X_BIT_TRGCFG7_TRG1_BST_MASK (~(1<<4)) +#define AW8692X_BIT_TRGCFG7_TRG1_BST_ENABLE (1<<4) +#define AW8692X_BIT_TRGCFG7_TRG1_BST_DISABLE (0<<4) +#define AW8692X_BIT_TRGCFG7_TRG2_POLAR_MASK (~(1<<3)) +#define AW8692X_BIT_TRGCFG7_TRG2_POLAR_NEG (1<<3) +#define AW8692X_BIT_TRGCFG7_TRG2_POLAR_POS (0<<3) +#define AW8692X_BIT_TRGCFG7_TRG2_MODE_MASK (~(1<<2)) +#define AW8692X_BIT_TRGCFG7_TRG2_MODE_LEVEL (1<<2) +#define AW8692X_BIT_TRGCFG7_TRG2_MODE_EDGE (0<<2) +#define AW8692X_BIT_TRGCFG7_TRG2_AUTO_BRK_MASK (~(1<<1)) +#define AW8692X_BIT_TRGCFG7_TRG2_AUTO_BRK_ENABLE (1<<1) +#define AW8692X_BIT_TRGCFG7_TRG2_AUTO_BRK_DISABLE (0<<1) +#define AW8692X_BIT_TRGCFG7_TRG2_BST_MASK (~(1<<0)) +#define AW8692X_BIT_TRGCFG7_TRG2_BST_ENABLE (1<<0) +#define AW8692X_BIT_TRGCFG7_TRG2_BST_DISABLE (0<<0) + +/* TRGCFG8: reg 0x3A RW */ +#define AW8692X_BIT_TRGCFG8_TRG3_POLAR_MASK (~(1<<7)) +#define AW8692X_BIT_TRGCFG8_TRG3_POLAR_NEG (1<<7) +#define AW8692X_BIT_TRGCFG8_TRG3_POLAR_POS (0<<7) +#define AW8692X_BIT_TRGCFG8_TRG3_MODE_MASK (~(1<<6)) +#define AW8692X_BIT_TRGCFG8_TRG3_MODE_LEVEL (1<<6) +#define AW8692X_BIT_TRGCFG8_TRG3_MODE_EDGE (0<<6) +#define AW8692X_BIT_TRGCFG8_TRG3_AUTO_BRK_MASK (~(1<<5)) +#define AW8692X_BIT_TRGCFG8_TRG3_AUTO_BRK_ENABLE (1<<5) +#define AW8692X_BIT_TRGCFG8_TRG3_AUTO_BRK_DISABLE (0<<5) +#define AW8692X_BIT_TRGCFG8_TRG3_BST_MASK (~(1<<4)) +#define AW8692X_BIT_TRGCFG8_TRG3_BST_ENABLE (1<<4) +#define AW8692X_BIT_TRGCFG8_TRG3_BST_DISABLE (0<<4) +#define AW8692X_BIT_TRGCFG8_TRG_ONEWIRE_MASK (~(1<<3)) +#define AW8692X_BIT_TRGCFG8_TRG_ONEWIRE_ENABLE (1<<3) +#define AW8692X_BIT_TRGCFG8_TRG_ONEWIRE_DISABLE (0<<3) +#define AW8692X_BIT_TRGCFG8_TRG1_STOP_MASK (~(1<<2)) +#define AW8692X_BIT_TRGCFG8_TRG1_STOP (1<<2) +#define AW8692X_BIT_TRGCFG8_TRG2_STOP_MASK (~(1<<1)) +#define AW8692X_BIT_TRGCFG8_TRG2_STOP (1<<1) +#define AW8692X_BIT_TRGCFG8_TRG3_STOP_MASK (~(1<<0)) +#define AW8692X_BIT_TRGCFG8_TRG3_STOP (1<<0) + +/* GLBCFG2: reg 0x3C RW */ +/* START_DLY */ +#define AW8692X_BIT_START_DLY_20US (0x01) +#define AW8692X_BIT_START_DLY_2P5MS (0x75) +/* GLBCFG4: reg 0x3E RW */ +#define AW8692X_BIT_GLBCFG4_GO_PRIO_MASK (~(3<<6)) +#define AW8692X_BIT_GLBCFG4_TRG3_PRIO_MASK (~(3<<4)) +#define AW8692X_BIT_GLBCFG4_TRG2_PRIO_MASK (~(3<<2)) +#define AW8692X_BIT_GLBCFG4_TRG1_PRIO_MASK (~(3<<0)) + +/* GLBRD5: reg 0x3F R0 */ +/* GLB_STATE [3:0] */ +#define AW8692X_BIT_GLBRD5_STATE_MASK (~(0x0F<<0)) +#define AW8692X_BIT_GLBRD5_STATE_STANDBY (0<<0) +#define AW8692X_BIT_GLBRD5_STATE_WAKEUP (1<<0) +#define AW8692X_BIT_GLBRD5_STATE_STARTUP (2<<0) +#define AW8692X_BIT_GLBRD5_STATE_WAIT (3<<0) +#define AW8692X_BIT_GLBRD5_STATE_CONT_GO (6<<0) +#define AW8692X_BIT_GLBRD5_STATE_RAM_GO (7<<0) +#define AW8692X_BIT_GLBRD5_STATE_RTP_GO (8<<0) +#define AW8692X_BIT_GLBRD5_STATE_TRIG_GO (9<<0) +#define AW8692X_BIT_GLBRD5_STATE_I2S_GO (0x0A<<0) +#define AW8692X_BIT_GLBRD5_STATE_BRAKE (0x0B<<0) +#define AW8692X_BIT_GLBRD5_STATE_END (0x0C<<0) + +/* RAMADDRH: reg 0x40 RWS */ +#define AW8692X_BIT_RAMADDRH_MASK (~(0x1F<<0)) + +/* SYSCTRL2: reg 0x44 RWS */ +#define AW8692X_BIT_SYSCTRL2_SLOT_CHSEL_MASK (~(1<<5)) +#define AW8692X_BIT_SYSCTRL2_RTP_SLOT_ONE (0<<5) +#define AW8692X_BIT_SYSCTRL2_RTP_SLOT_TWO (1<<5) +#define AW8692X_BIT_SYSCTRL2_RCK_FRE (3<<2) + +/* SYSCTRL3: reg 0x45 RW */ +#define AW8692X_BIT_SYSCTRL3_WCK_PIN_MASK (~(1<<7)) +#define AW8692X_BIT_SYSCTRL3_WCK_PIN_ON (1<<7) +#define AW8692X_BIT_SYSCTRL3_WCK_PIN_OFF (0<<7) +#define AW8692X_BIT_SYSCTRL3_WAKE_MASK (~(1<<6)) +#define AW8692X_BIT_SYSCTRL3_WAKE_ON (1<<6) +#define AW8692X_BIT_SYSCTRL3_WAKE_OFF (0<<6) +#define AW8692X_BIT_SYSCTRL3_STANDBY_MASK (~(1<<5)) +#define AW8692X_BIT_SYSCTRL3_STANDBY_ON (1<<5) +#define AW8692X_BIT_SYSCTRL3_STANDBY_OFF (0<<5) +#define AW8692X_BIT_SYSCTRL3_RTP_DLY_MASK (~(3<<3)) +#define AW8692X_BIT_SYSCTRL3_EN_RAMINIT_MASK (~(1<<2)) +#define AW8692X_BIT_SYSCTRL3_EN_RAMINIT_ON (1<<2) +#define AW8692X_BIT_SYSCTRL3_EN_RAMINIT_OFF (0<<2) +#define AW8692X_BIT_SYSCTRL3_EN_FIR_MASK (~(1<<1)) +#define AW8692X_BIT_SYSCTRL3_EN_FIR_ON (1<<1) +#define AW8692X_BIT_SYSCTRL3_EN_FIR_OFF (0<<1) +#define AW8692X_BIT_SYSCTRL3_WAKE_MODE_MASK (~(1<<0)) +#define AW8692X_BIT_SYSCTRL3_WAKE_MODE_ON (1<<0) +#define AW8692X_BIT_SYSCTRL3_WAKE_MODE_OFF (0<<0) + +/* SYSCTRL4: reg 0x46 RW */ +#define AW8692X_BIT_SYSCTRL4_EN_INTN_CLKOUT_MASK (~(1<<7)) +#define AW8692X_BIT_SYSCTRL4_EN_INTN_CLKOUT_ON (1<<7) +#define AW8692X_BIT_SYSCTRL4_EN_INTN_CLKOUT_OFF (0<<7) +#define AW8692X_BIT_SYSCTRL4_WAVDAT_MODE_MASK (~(3<<5)) +#define AW8692X_BIT_SYSCTRL4_WAVDAT_12K (2<<5) +#define AW8692X_BIT_SYSCTRL4_WAVDAT_48K (1<<5) +#define AW8692X_BIT_SYSCTRL4_WAVDAT_24K (0<<5) +#define AW8692X_BIT_SYSCTRL4_INT_EDGE_MODE_MASK (~(1<<4)) +#define AW8692X_BIT_SYSCTRL4_INT_EDGE_MODE_POS (0<<4) +#define AW8692X_BIT_SYSCTRL4_INT_EDGE_MODE_BOTH (1<<4) +#define AW8692X_BIT_SYSCTRL4_INT_MODE_MASK (~(1<<3)) +#define AW8692X_BIT_SYSCTRL4_INT_MODE_EDGE (1<<3) +#define AW8692X_BIT_SYSCTRL4_INT_MODE_LEVEL (0<<3) +#define AW8692X_BIT_SYSCTRL4_GAIN_BYPASS_MASK (~(1<<0)) + +/* SYSCTRL5: reg 0x47 RW */ +#define AW8692X_BIT_SYSCTRL5_EN_BRO_ADDR_MASK (~(1<<7)) +#define AW8692X_BIT_SYSCTRL5_EN_BRO_ADDR_ON (1<<7) +#define AW8692X_BIT_SYSCTRL5_EN_BRO_ADDR_OFF (0<<7) +#define AW8692X_BIT_SYSCTRL5_BROADCAST_ADDR_MASK (~(0x7F<<0)) + +/* PWMCFG1: reg 0x48 RW */ +#define AW8692X_BIT_PWMCFG1_PRC_EN_MASK (~(1<<7)) +#define AW8692X_BIT_PWMCFG1_PRC_ENABLE (1<<7) +#define AW8692X_BIT_PWMCFG1_PRC_DISABLE (0<<7) +#define AW8692X_BIT_PWMCFG1_PRCTIME_MASK (~(0x7F<<0)) +#define AW8692X_BIT_PWMCFG1_INIT_VAL (0xA0) + +/* PWMCFG2: reg 0x49 RW */ +#define AW8692X_BIT_PWMCFG2_PRCT_MODE_MASK (~(1<<6)) +#define AW8692X_BIT_PWMCFG2_PRCT_MODE_VALID (0<<6) +#define AW8692X_BIT_PWMCFG2_PRCT_MODE_INVALID (1<<6) +#define AW8692X_BIT_PWMCFG2_REF_SEL_MASK (~(1<<5)) +#define AW8692X_BIT_PWMCFG2_REF_SEL_TRIANGLE (1<<5) +#define AW8692X_BIT_PWMCFG2_REF_SEL_SAWTOOTH (0<<5) +#define AW8692X_BIT_PWMCFG2_PD_HWM_MASK (~(1<<4)) +#define AW8692X_BIT_PWMCFG2_PD_HWM_ON (1<<4) +#define AW8692X_BIT_PWMCFG2_PWMOE_MASK (~(1<<3)) +#define AW8692X_BIT_PWMCFG2_PWMOE_ON (1<<3) +#define AW8692X_BIT_PWMCFG2_PWMFRC_MASK (~(7<<0)) + +/* PWMCFG3: reg 0x4A RW */ +#define AW8692X_BIT_PWMCFG3_PR_EN_MASK (~(1<<7)) +#define AW8692X_BIT_PWMCFG3_PR_ENABLE (1<<7) +#define AW8692X_BIT_PWMCFG3_PR_DISABLE (0<<7) +#define AW8692X_BIT_PWMCFG3_PRLVL_MASK (~(0x7F<<0)) + +/* VBATCTRL: reg 0x4C RW */ +#define AW8692X_BIT_VBATCTRL_VBAT_PRO_MASK (~(1<<7)) +#define AW8692X_BIT_VBATCTRL_VBAT_PRO_ENABLE (1<<7) +#define AW8692X_BIT_VBATCTRL_VBAT_PRO_DISABLE (0<<7) + +#define AW8692X_BIT_VBATCTRL_VBAT_MODE_MASK (~(1<<6)) +#define AW8692X_BIT_VBATCTRL_VBAT_MODE_HW (1<<6) +#define AW8692X_BIT_VBATCTRL_VBAT_MODE_SW (0<<6) + +#define AW8692X_BIT_VBATCTRL_VBAT_MODE_CON_MASK (~(1<<5)) +#define AW8692X_BIT_VBATCTRL_VBAT_MODE_CON_DURING (1<<5) +#define AW8692X_BIT_VBATCTRL_VBAT_MODE_CON_BEFORE (0<<5) + +#define AW8692X_BIT_VBATCTRL_DELTA_VBAT_MASK (~(1<<4)) +#define AW8692X_BIT_VBATCTRL_DELTA_VBAT_0P2V (1<<4) +#define AW8692X_BIT_VBATCTRL_DELTA_VBAT_0P1V (0<<4) + +#define AW8692X_BIT_VBATCTRL_REL_VBAT_MASK (~(3<<2)) +#define AW8692X_BIT_VBATCTRL_ABS_VBAT_MASK (~(3<<0)) + +/* DETCFG1: reg 0x4D RW */ +#define AW8692X_BIT_DETCFG1_VBAT_REF_MASK (~(7<<4)) +#define AW8692X_BIT_DETCFG1_ADC_FS_MASK (~(3<<2)) +#define AW8692X_BIT_DETCFG1_ADC_FS (3<<2) +#define AW8692X_BIT_DETCFG1_ADC_FS_192KHZ (0<<2) +#define AW8692X_BIT_DETCFG1_ADC_FS_96KHZ (1<<2) +#define AW8692X_BIT_DETCFG1_ADC_FS_48KHZ (2<<2) +#define AW8692X_BIT_DETCFG1_ADC_FS_24KHZ (3<<2) + +#define AW8692X_BIT_DETCFG1_DET_GO_MASK (~(3<<0)) +#define AW8692X_BIT_DETCFG1_DET_GO_NA (0<<0) + +#define AW8692X_BIT_DETCFG1_DET_GO_MASK (~(3<<0)) +#define AW8692X_BIT_DETCFG1_DET_GO_NA (0<<0) +#define AW8692X_BIT_DETCFG1_DET_GO_DET_SEQ0 (1<<0) + +/* DETCFG2: reg 0x4E RW */ +#define AW8692X_BIT_DETCFG2_DET_SEQ0_MASK (~(0xF<<3)) +#define AW8692X_BIT_DETCFG2_DET_SEQ0_VBAT (0<<3) +#define AW8692X_BIT_DETCFG2_DET_SEQ0_PVDD (1<<3) +#define AW8692X_BIT_DETCFG2_DET_SEQ0_TRIG1 (2<<3) +#define AW8692X_BIT_DETCFG2_DET_SEQ0_RL (3<<3) +#define AW8692X_BIT_DETCFG2_DET_SEQ0_OS (4<<3) +#define AW8692X_BIT_DETCFG2_DET_SEQ0_VOUT (5<<3) +#define AW8692X_BIT_DETCFG2_DET_SEQ0_FTS (6<<3) +#define AW8692X_BIT_DETCFG2_D2S_GAIN_MASK (~(7<<0)) +#define AW8692X_BIT_DETCFG2_D2S_GAIN (7<<0) +#define AW8692X_BIT_DETCFG2_D2S_GAIN_1 (0<<0) +#define AW8692X_BIT_DETCFG2_D2S_GAIN_2 (1<<0) +#define AW8692X_BIT_DETCFG2_D2S_GAIN_4 (2<<0) +#define AW8692X_BIT_DETCFG2_D2S_GAIN_8 (3<<0) +#define AW8692X_BIT_DETCFG2_D2S_GAIN_10 (4<<0) +#define AW8692X_BIT_DETCFG2_D2S_GAIN_16 (5<<0) +#define AW8692X_BIT_DETCFG2_D2S_GAIN_20 (6<<0) +#define AW8692X_BIT_DETCFG2_D2S_GAIN_40 (7<<0) + +/* DETRD1: reg 0x4F RW */ +#define AW8692X_BIT_DETRD1_AVG_DATA (3<<0) + +/* TRIMCFG1: reg 0x52 RW */ +#define AW8692X_BIT_TRIMCFG1_RL_TRIM_SRC_MASK (~(0x01<<7)) +#define AW8692X_BIT_TRIMCFG1_RL_TRIM_SRC_EFUSE (0<<7) +#define AW8692X_BIT_TRIMCFG1_RL_TRIM_SRC_REG (1<<7) + +/* TMCFG: reg 0x5B RW */ +#define AW8692X_BIT_TMCFG_TM_LOCK (0x00) +#define AW8692X_BIT_TMCFG_TM_UNLOCK (0x7d) + +/* TMCFG: reg 0x5C RW */ +#define AW8692X_BIT_EFCFG1_INIT_VAL (0x09) + +/* EFCFG6: reg 0x61 RO */ +#define AW8692X_BIT_EFCFG6_LOCK_MASK (~(0x01<<7)) + +/* EFCFG7: reg 0x62 RO */ +#define AW8692X_BIT_EFCFG7_RESERVED_MASK (~(0x01<<7)) + +/* EFCFG8: reg 0x63 RO */ +#define AW8692X_BIT_EFCFG8_EF_TRM_BST_IPEAK_MASK (~(0xF0<<0)) + +/* EFCFG8: reg 0x64 RO */ +#define AW8692X_BIT_EFCFG9_EF_TRIM_OSC_MASK (~(0x0F<<0)) + +/* ANACFG2: reg 0x67 RW */ +#define AW8692X_BIT_ANACFG2_INIT_VAL (0x80) + +/* ANACFG11: reg 0x70 RW */ +#define AW8692X_BIT_ANACFG11_INIT_VAL (0x0f) + +/* ANACFG12: reg 0x71 RW */ +#define AW8692X_BIT_ANACFG12_BST_SKIP_MASK (~(0x01<<7)) +#define AW8692X_BIT_ANACFG12_BST_SKIP_OPEN (0<<7) +#define AW8692X_BIT_ANACFG12_BST_SKIP_SHUTDOWN (1<<7) + +/* ANACFG13: reg 0x72 RW */ +#define AW8692X_BIT_ANACFG13_BST_PC_MASK (~(0xF0<<0)) +#define AW8692X_BIT_ANACFG13_BST_PEAKCUR_3P45A (6<<4) +#define AW8692X_BIT_ANACFG13_BST_PEAKCUR_4A (9<<4) + +/* ANACFG15: reg 0x74 RW */ +#define AW8692X_BIT_ANACFG15_BST_PEAK_MODE_MASK (~(0x01<<7)) +#define AW8692X_BIT_ANACFG15_BST_PEAK_ADP (0<<7) +#define AW8692X_BIT_ANACFG15_BST_PEAK_BACK (1<<7) + +/* ANACFG16: reg 0x75 RW */ +#define AW8692X_BIT_ANACFG16_BST_SRC_MASK (~(1<<4)) +#define AW8692X_BIT_ANACFG16_BST_SRC_3NS (0<<4) + +/* ANACFG20: reg 0x79 RW */ +#define AW8692X_BIT_ANACFG20_TRIM_LRA_MASK (~(0x3F<<0)) +#define AW8692X_BIT_ANACFG20_TRIM_LRA (0x3F<<0) + +#endif diff --git a/drivers/misc/awinic/sarsensor/Android.mk b/drivers/misc/awinic/sarsensor/Android.mk new file mode 100644 index 000000000000..c9500f25e070 --- /dev/null +++ b/drivers/misc/awinic/sarsensor/Android.mk @@ -0,0 +1,17 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(AW96XX_USB_USE_ONLINE),true) + KERNEL_CFLAGS += CONFIG_AW96XX_POWER_SUPPLY_ONLINE=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := awinic_sar.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/sensors_class.ko +#LOCAL_REQUIRED_MODULES := sensors_class.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +KBUILD_OPTIONS += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/misc/awinic/sarsensor/Kbuild b/drivers/misc/awinic/sarsensor/Kbuild new file mode 100644 index 000000000000..47ffcf8ba15b --- /dev/null +++ b/drivers/misc/awinic/sarsensor/Kbuild @@ -0,0 +1,18 @@ +# add -Wall to try to catch everything we can. +#MTK have kernel-4.14 and kernel-4.19 and kernel-5.10 in the same dir,so we can't use below method +#KERNEL_DIR_PATH = $(shell find $(TOP)/kernel* -type d -name "kernel-*" | head -1) +KERNEL_VERSION_NAME = $(shell basename $(KERNEL_DIR_PATH)) +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include/linux/input/awinic +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/$(KERNEL_VERSION_NAME)/drivers/base + +ifneq ($(filter m y,$(CONFIG_AW96XX_POWER_SUPPLY_ONLINE)),) + EXTRA_CFLAGS += -DCONFIG_USE_POWER_SUPPLY_ONLINE +endif + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers + +obj-m := awinic_sar.o +awinic_sar-objs := ./comm/aw_sar_comm_interface.o aw_sar.o ./aw9610x/aw9610x.o ./aw9620x/aw9620x.o ./aw9620x/aw_update_fw.o ./aw963xx/aw963xx.o + diff --git a/drivers/misc/awinic/sarsensor/Makefile b/drivers/misc/awinic/sarsensor/Makefile new file mode 100644 index 000000000000..ebf65dd9e5b8 --- /dev/null +++ b/drivers/misc/awinic/sarsensor/Makefile @@ -0,0 +1,25 @@ +ifeq ($(strip $(MODULE_KERNEL_VERSION)), 5.15) +all: modules +modules: + $(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 +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers +else +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + +endif diff --git a/drivers/misc/awinic/sarsensor/aw9610x/aw9610x.c b/drivers/misc/awinic/sarsensor/aw9610x/aw9610x.c new file mode 100644 index 000000000000..2ac7804c9739 --- /dev/null +++ b/drivers/misc/awinic/sarsensor/aw9610x/aw9610x.c @@ -0,0 +1,875 @@ +#include "aw9610x.h" + +#define AW9610X_I2C_NAME "aw9610x_sar" +#define AW9610X_DRIVER_VERSION "v2.3.3" + +#define AW9610X_RETRIES (3) + +static struct aw_sar *g_aw_sar = NULL; + +//#define AW_INPUT_TRIGGER_TH1 +//#define AW_INPUT_TRIGGER_TH2 +//#define AW_INPUT_TRIGGER_TH3 + +static int32_t aw9610x_baseline_filter(struct aw_sar *p_sar) +{ + uint8_t i = 0; + uint32_t status0 = 0; + uint32_t status1 = 0; + struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data; + + aw_sar_i2c_read(p_sar->i2c, REG_STAT1, &status1); + aw_sar_i2c_read(p_sar->i2c, REG_STAT0, &status0); + + for (i = 0; i < AW9610X_CHANNEL_MAX; i++) { + if (((status1 >> i) & 0x01) == 1) { + if (aw9610x->satu_flag[i] == 0) { + aw_sar_i2c_read(p_sar->i2c, REG_BLFILT_CH0 + i * AW_CL1SPE_DEAL_OS, &aw9610x->satu_data[i]); + aw_sar_i2c_write(p_sar->i2c, REG_BLFILT_CH0 + i * AW_CL1SPE_DEAL_OS, ((aw9610x->satu_data[i] | 0x1fc) & 0x3fffffff)); + aw9610x->satu_flag[i] = 1; + } + } else if (((status1 >> i) & 0x01) == 0) { + if (aw9610x->satu_flag[i] == 1) { + if (((status0 >> (i + 24)) & 0x01) == 0) { + aw_sar_i2c_write(p_sar->i2c, REG_BLFILT_CH0 + i * AW_CL1SPE_DEAL_OS, aw9610x->satu_data[i]); + aw9610x->satu_flag[i] = 0; + } + } + } + } + + return AW_OK; +} + +static void aw9610x_saturat_release_handle(struct aw_sar *p_sar) +{ + uint32_t satu_irq = 0; + uint8_t i = 0; + uint32_t status0 = 0; + struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data; + + AWLOGD(p_sar->dev, "enter"); + + satu_irq = (aw9610x->irq_status >> 7) & 0x01; + if (satu_irq == 1) { + aw9610x_baseline_filter(p_sar); + } else { + aw_sar_i2c_read(p_sar->i2c, REG_STAT0, &status0); + for (i = 0; i < AW9610X_CHANNEL_MAX; i++) { + if (aw9610x->satu_flag[i] == 1) { + if (((status0 >> (i + 24)) & 0x01) == 0) { + aw_sar_i2c_write(p_sar->i2c, REG_BLFILT_CH0 + i * AW_CL1SPE_DEAL_OS, aw9610x->satu_data[i]); + aw9610x->satu_flag[i] = 0; + } + } + } + } + + AWLOGI(p_sar->dev, "satu_irq handle over!"); +} + +static void aw9610x_irq_handle(struct aw_sar *p_sar) +{ + uint8_t i = 0; + uint32_t curr_status = 0; + uint32_t curr_status_val = 0; + + AWLOGD(p_sar->dev, "enter"); + + aw_sar_i2c_read(p_sar->i2c, REG_STAT0, &curr_status_val); + AWLOGD(p_sar->dev, "channel = 0x%08x", curr_status_val); + + if (p_sar->channels_arr == NULL) { + AWLOGE(p_sar->dev, "input err!!!"); + return; + } + + for (i = 0; i < AW9610X_CHANNEL_MAX; i++) { + curr_status = + (((uint8_t)(curr_status_val >> (24 + i)) & 0x1)) +#ifdef AW_INPUT_TRIGGER_TH1 + | (((uint8_t)(curr_status_val >> (16 + i)) & 0x1) << 1) +#endif +#ifdef AW_INPUT_TRIGGER_TH2 + | (((uint8_t)(curr_status_val >> (8 + i)) & 0x1) << 2) +#endif +#ifdef AW_INPUT_TRIGGER_TH3 + | (((uint8_t)(curr_status_val >> (i)) & 0x1) << 3) +#endif + ; + //AWLOGI(p_sar->dev, "curr_state[%d] = 0x%x", i, curr_status); + + if (p_sar->channels_arr[i].used == AW_FALSE) { + //AWLOGD(p_sar->dev, "channels_arr[%d] no user", i); + continue; + } + if (p_sar->channels_arr[i].last_channel_info == curr_status) { + continue; + } + + switch (curr_status) { + case AW9610X_FAR: + input_report_abs(p_sar->channels_arr[i].input, ABS_DISTANCE, 0); + break; + case AW9610X_TRIGGER_TH0: + input_report_abs(p_sar->channels_arr[i].input, ABS_DISTANCE, 1); + break; +#ifdef AW_INPUT_TRIGGER_TH1 + case AW9610X_TRIGGER_TH1: + input_report_abs(p_sar->channels_arr[i].input, ABS_DISTANCE, 2); + break; +#endif +#ifdef AW_INPUT_TRIGGER_TH2 + case AW9610X_TRIGGER_TH2: + input_report_abs(p_sar->channels_arr[i].input, ABS_DISTANCE, 3); + break; +#endif +#ifdef AW_INPUT_TRIGGER_TH3 + case AW9610X_TRIGGER_TH3: + input_report_abs(p_sar->channels_arr[i].input, ABS_DISTANCE, 4); + break; +#endif + default: + AWLOGE(p_sar->dev, "error abs distance"); + return; + } + input_sync(p_sar->channels_arr[i].input); + + p_sar->channels_arr[i].last_channel_info = curr_status; + } +} + +static void aw9610x_version_aw9610x_private(struct aw_sar *p_sar) +{ + struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data; + + AWLOGD(p_sar->dev, "AW9610X enter"); + + if (aw9610x->satu_release == AW9610X_FUNC_ON) + aw9610x_saturat_release_handle(p_sar); +} + +static void aw9610x_irq_handle_func(uint32_t irq_status, void *data) +{ + struct aw_sar *p_sar = (struct aw_sar *)data; + struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data; + + AWLOGD(p_sar->dev, "enter"); + + AWLOGI(p_sar->dev, "IRQSRC = 0x%x", irq_status); + + switch (aw9610x->vers) { + case AW9610X: + aw9610x_version_aw9610x_private(p_sar); + break; + case AW9610XA: + break; + default: + break; + } + + aw9610x_irq_handle(p_sar); +} + +int32_t aw9610x_check_chipid(void *data) +{ + int32_t ret = -1; + uint32_t reg_val = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + + if (p_sar == NULL) { + return -AW_BIN_PARA_INVALID; + } + + ret = aw_sar_i2c_read(p_sar->i2c, REG_CHIPID, ®_val); + if (ret < 0) { + AWLOGE(p_sar->dev, "read CHIP ID failed: %d", ret); + } else { + reg_val = reg_val >> 16; + } + + if (reg_val == AW9610X_CHIP_ID) { + AWLOGI(p_sar->dev, "aw9610x detected, 0x%04x", reg_val); + return AW_OK; + } else { + AWLOGE(p_sar->dev, "unsupport dev, chipid is (0x%04x)", reg_val); + } + + return -AW_ERR; +} + +static const struct aw_sar_check_chipid_t g_aw9610x_check_chipid = { + .p_check_chipid_fn = aw9610x_check_chipid, +}; + +static ssize_t aw9610x_operation_mode_get(void *data, char *buf) +{ + ssize_t len = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data; + + if (p_sar->last_mode == AW9610X_ACTIVE_MODE) + len += snprintf(buf + len, PAGE_SIZE - len, "operation mode: Active\n"); + else if (p_sar->last_mode == AW9610X_SLEEP_MODE) + len += snprintf(buf + len, PAGE_SIZE - len, "operation mode: Sleep\n"); + else if ((p_sar->last_mode == AW9610X_DEEPSLEEP_MODE) && (aw9610x->vers == AW9610XA)) + len += snprintf(buf + len, PAGE_SIZE - len, "operation mode: DeepSleep\n"); + else + len += snprintf(buf + len, PAGE_SIZE - len, "operation mode: Unconfirmed\n"); + + return len; +} + +static void aw9610x_chip_info_get(void *data, char *buf, ssize_t *p_len) +{ + uint32_t reg_data = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data; + + *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "sar%d, aw9610x chip driver version %s\n", p_sar->dts_info.sar_num, AW9610X_DRIVER_VERSION); + *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "The driver supports UI\n"); + + aw_sar_i2c_read(p_sar->i2c, REG_CHIPID, ®_data); + *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "chipid is 0x%08x\n", reg_data); + + aw_sar_i2c_read(p_sar->i2c, REG_IRQEN, ®_data); + *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "REG_HOSTIRQEN is 0x%08x\n", reg_data); + + *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "chip_name:%s bin_prase_chip_name:%s\n", + aw9610x->chip_name, aw9610x->chip_type); +} + +static const struct aw_sar_get_chip_info_t g_aw9610x_get_chip_info = { + .p_get_chip_info_node_fn = aw9610x_chip_info_get, +}; + +static void aw9610x_reg_version_comp(struct aw_sar *p_sar, struct aw_bin *aw_bin) +{ + uint8_t i = 0; + uint32_t blfilt1_data = 0; + uint32_t blfilt1_tmp = 0; + struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data; + + if ((aw9610x->chip_name[7] == 'A') && + (aw_bin->header_info[0].chip_type[7] == '\0')) { + AWLOGI(p_sar->dev, "enter"); + for (i = 0; i < 6; i++) { + aw_sar_i2c_read(p_sar->i2c, REG_BLFILT_CH0 + (0x3c * i), &blfilt1_data); + AWLOGD(p_sar->dev, "addr 0x%04x val: 0x%08x", REG_BLFILT_CH0 + (0x3c * i), blfilt1_data); + blfilt1_tmp = (blfilt1_data >> 25) & 0x1; + if (blfilt1_tmp == 1) { + AWLOGD(p_sar->dev, "ablfilt1_tmp is 1"); + aw_sar_i2c_write_bits(p_sar->i2c, REG_BLRSTRNG_CH0 + (0x3c * i), ~(0x3f), 1 << i); + } + } + } +} + +static int32_t aw9610x_load_reg_bin(struct aw_bin *aw_bin, struct aw_sar *p_sar) +{ + int32_t ret = 0; + + struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data; + + snprintf(aw9610x->chip_type, sizeof(aw9610x->chip_type), "%s", aw_bin->header_info[0].chip_type); + AWLOGE(p_sar->dev, "chip name: %s", aw9610x->chip_type); + + ret = aw_sar_load_reg(aw_bin, p_sar->i2c); + aw9610x_reg_version_comp(p_sar, aw_bin); + + return ret; +} + +static ssize_t aw9610x_get_self_cap_offset(void *data, char *buf) +{ + ssize_t len = 0; + uint8_t i = 0; + uint32_t reg_val = 0; + uint32_t coff_data = 0; + uint32_t coff_data_int = 0; + uint32_t coff_data_dec = 0; + uint8_t temp_data[20] = { 0 }; + struct aw_sar *p_sar = (struct aw_sar *)data; + + for (i = 0; i < AW9610X_CHANNEL_MAX; i++) { + aw_sar_i2c_read(p_sar->i2c, + REG_AFECFG1_CH0 + i * AW_CL1SPE_CALI_OS, ®_val); + coff_data = (reg_val >> 24) * 900 + + ((reg_val >> 16) & 0xff) * 13; + coff_data_int = coff_data / 1000; + coff_data_dec = coff_data % 1000; + snprintf(temp_data, sizeof(temp_data), "%d.%d", coff_data_int, + coff_data_dec); + len += snprintf(buf+len, PAGE_SIZE-len, + "PARASITIC_DATA_CH%d = %s pf\n", i, temp_data); + } + + return len; +} + +static const struct aw_sar_offset_t g_aw9610x_offset = { + .p_get_offset_node_fn = aw9610x_get_self_cap_offset, +}; + +static uint32_t attr_buf[] = { + 8, 10, + 9, 100, + 10, 1000, +}; + +static void aw9610x_addrblock_load(struct aw_sar *p_sar, const char *buf) +{ + uint32_t addrbuf[4] = { 0 }; + uint8_t temp_buf[2] = { 0 }; + uint32_t i = 0; + struct aw9610x *aw9610x = p_sar->priv_data; + uint8_t addr_bytes = aw9610x->aw_i2c_package.addr_bytes; + uint8_t reg_num = aw9610x->aw_i2c_package.reg_num; + + for (i = 0; i < addr_bytes; i++) { + if (reg_num < attr_buf[1]) { + temp_buf[0] = buf[attr_buf[0] + i * 5]; + temp_buf[1] = buf[attr_buf[0] + i * 5 + 1]; + } else if (reg_num >= attr_buf[1] && reg_num < attr_buf[3]) { + temp_buf[0] = buf[attr_buf[2] + i * 5]; + temp_buf[1] = buf[attr_buf[2] + i * 5 + 1]; + } else if (reg_num >= attr_buf[3] && reg_num < attr_buf[5]) { + temp_buf[0] = buf[attr_buf[4] + i * 5]; + temp_buf[1] = buf[attr_buf[4] + i * 5 + 1]; + } + if (sscanf(temp_buf, "%02x", &addrbuf[i]) == 1) + aw9610x->aw_i2c_package.init_addr[i] = (uint8_t)addrbuf[i]; + } +} + +static int32_t aw9610x_awrw_write_seq(struct aw_sar *p_sar) +{ + struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data; + uint8_t w_buf[228]; + uint8_t addr_bytes = aw9610x->aw_i2c_package.addr_bytes; + uint8_t msg_cnt = 0; + uint8_t data_bytes = aw9610x->aw_i2c_package.data_bytes; + uint8_t reg_num = aw9610x->aw_i2c_package.reg_num; + uint8_t *p_reg_data = aw9610x->aw_i2c_package.p_reg_data; + uint32_t msg_idx = 0; + + for (msg_idx = 0; msg_idx < addr_bytes; msg_idx++) { + w_buf[msg_idx] = aw9610x->aw_i2c_package.init_addr[msg_idx]; + AWLOGI(p_sar->dev, "w_buf_addr[%d] = 0x%02x", msg_idx, w_buf[msg_idx]); + } + + msg_cnt = addr_bytes; + for (msg_idx = 0; msg_idx < data_bytes * reg_num; msg_idx++) { + w_buf[msg_cnt] = *p_reg_data++; + msg_cnt++; + } + + return aw_sar_i2c_write_seq(p_sar->i2c, w_buf, msg_cnt); +} + +static void aw9610x_datablock_load(struct aw_sar *p_sar, const char *buf) +{ + uint32_t i = 0; + uint8_t reg_data[220] = { 0 }; + uint32_t databuf[220] = { 0 }; + uint8_t temp_buf[2] = { 0 }; + struct aw9610x *aw9610x = p_sar->priv_data; + uint8_t addr_bytes = aw9610x->aw_i2c_package.addr_bytes; + uint8_t data_bytes = aw9610x->aw_i2c_package.data_bytes; + uint8_t reg_num = aw9610x->aw_i2c_package.reg_num; + + for (i = 0; i < data_bytes * reg_num; i++) { + if (reg_num < attr_buf[1]) { + temp_buf[0] = buf[attr_buf[0] + (addr_bytes + i) * 5]; + temp_buf[1] = + buf[attr_buf[0] + (addr_bytes + i) * 5 + 1]; + } else if (reg_num >= attr_buf[1] && reg_num < attr_buf[3]) { + temp_buf[0] = buf[attr_buf[2] + (addr_bytes + i) * 5]; + temp_buf[1] = + buf[attr_buf[2] + (addr_bytes + i) * 5 + 1]; + } else if (reg_num >= attr_buf[3] && reg_num < attr_buf[5]) { + temp_buf[0] = buf[attr_buf[4] + (addr_bytes + i) * 5]; + temp_buf[1] = + buf[attr_buf[4] + (addr_bytes + i) * 5 + 1]; + } + sscanf(temp_buf, "%02x", &databuf[i]); + reg_data[i] = (uint8_t)databuf[i]; + } + aw9610x->aw_i2c_package.p_reg_data = reg_data; + aw9610x_awrw_write_seq(p_sar); +} + +static int32_t aw9610x_awrw_read_seq(struct aw_sar *p_sar, uint8_t *reg_data) +{ + struct aw9610x *aw9610x= (struct aw9610x *)p_sar->priv_data; + int32_t ret = 0; + uint8_t w_buf[4]; + uint8_t buf[228]; + uint8_t data_bytes = aw9610x->aw_i2c_package.data_bytes; + uint8_t addr_bytes = aw9610x->aw_i2c_package.addr_bytes; + uint8_t reg_num = aw9610x->aw_i2c_package.reg_num; + uint32_t msg_idx = 0; + uint32_t msg_cnt = data_bytes * reg_num; + + for (msg_idx = 0; msg_idx < addr_bytes; msg_idx++) { + w_buf[msg_idx] = aw9610x->aw_i2c_package.init_addr[msg_idx]; + AWLOGD(p_sar->dev, "w_buf_addr[%d] = 0x%02x", + msg_idx, w_buf[msg_idx]); + } + + ret = aw_sar_i2c_read_seq(p_sar->i2c, w_buf, 2, (uint8_t *)buf, msg_cnt); + + for (msg_idx = 0; msg_idx < msg_cnt; msg_idx++) { + reg_data[msg_idx] = buf[msg_idx]; + AWLOGD(p_sar->dev, "buf = 0x%02x", buf[msg_idx]); + } + + return ret; +} + +static ssize_t aw9610x_awrw_get(void *data, char *buf) +{ + struct aw_sar *p_sar = (struct aw_sar *)data; + struct aw9610x *aw9610x= (struct aw9610x *)p_sar->priv_data; + uint8_t reg_data[228] = { 0 }; + uint8_t i = 0; + ssize_t len = 0; + uint8_t reg_num = aw9610x->aw_i2c_package.reg_num; + uint8_t data_bytes = aw9610x->aw_i2c_package.data_bytes; + + aw9610x_awrw_read_seq(p_sar, reg_data); + for (i = 0; i < reg_num * data_bytes; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%02x,", reg_data[i]); + } + + snprintf(buf + len - 1, PAGE_SIZE - len, "\n"); + + return len; +}; + +static ssize_t aw9610x_awrw_set(void *data, const char *buf, size_t count) +{ + uint32_t datatype[3] = { 0 }; + struct aw_sar *p_sar = (struct aw_sar *)data; + struct aw9610x *aw9610x= (struct aw9610x *)p_sar->priv_data; + + if (sscanf(buf, "%d %d %d", &datatype[0], &datatype[1], &datatype[2]) == 3) { + aw9610x->aw_i2c_package.addr_bytes = (uint8_t)datatype[0]; + aw9610x->aw_i2c_package.data_bytes = (uint8_t)datatype[1]; + aw9610x->aw_i2c_package.reg_num = (uint8_t)datatype[2]; + + aw9610x_addrblock_load(p_sar, buf); + if (count > 7 + 5 * aw9610x->aw_i2c_package.addr_bytes) + aw9610x_datablock_load(p_sar, buf); + } + + return count; +} + +static int32_t aw9610x_get_chip_version(void *data) +{ + uint32_t fw_ver = 0; + int32_t ret = 0; + uint32_t firmvers = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data; + + aw_sar_i2c_read(p_sar->i2c, REG_FWVER, &firmvers); + AWLOGD(p_sar->dev, "REG_FWVER = 0x%08x", firmvers); + + ret = aw_sar_i2c_read(p_sar->i2c, REG_FWVER2, &fw_ver); + if (ret < 0) { + AWLOGE(p_sar->dev, "read REG_FWVER2 err!"); + return -AW_ERR; + } + snprintf(aw9610x->chip_name, sizeof(aw9610x->chip_name), "AW9610X"); + p_sar->chip_type = AW_SAR_NONE_CHECK_CHIP; + + AWLOGD(p_sar->dev, "REG_FWVER2 : 0x%08x", fw_ver); + if (fw_ver == AW_CHIP_AW9610XA) { + aw9610x->vers = AW9610XA; + memcpy(aw9610x->chip_name + strlen(aw9610x->chip_name), "A", 2); + p_sar->chip_type = SAR_AW9610XA; + } else { + aw9610x->vers = AW9610X; + p_sar->chip_type = SAR_AW9610X; + aw9610x->chip_name[7] = '\0'; + } + AWLOGI(p_sar->dev, "the IC is = %s", aw9610x->chip_name); + + return AW_OK; +} + +#ifdef AW9610X_TVS_ABNORMAL_CAIL +static ssize_t aw9610x_set_aot(void *data) +{ + struct aw_sar *p_sar = (struct aw_sar *)data; + struct aw9610x *aw9610x = (struct aw9610x *)p_sar->priv_data; + uint32_t i = 0; + uint32_t irqen_reg_val = 0; + uint32_t reg_val_tmp = 0; + uint32_t max_delay_ms = AW9610X_AOT_OVER_DELAY_MAX_MS; + uint32_t scan_over_cnt = 0; + uint32_t ch_en = 0; + uint32_t scan_over_en = 0; + + //1. disable chip irq + aw_sar_i2c_read(p_sar->i2c, REG_IRQEN, &irqen_reg_val); + aw_sar_i2c_write(p_sar->i2c, REG_IRQEN, AW_REG_IRQEN_CLOSE); + + //2. aot cail + aw_sar_i2c_write_bits(p_sar->i2c, REG_SCANCTRL0, ~(AW9610X_AOT_MASK << AW9610X_AOT_BIT), + AW9610X_AOT_MASK << AW9610X_AOT_BIT); + // aot over + for (i = 0; i < max_delay_ms; i++) { + aw_sar_i2c_read(p_sar->i2c, REG_IRQSRC, ®_val_tmp); + if (((reg_val_tmp >> AW_REG_IRQSRC_AOT_OVER_BIT) & 0x01) == 1) { + AWLOGD(p_sar->dev, "aot over i = %d REG_IRQSRC:0X%X", i, reg_val_tmp); + break; + } + AWLOGD(p_sar->dev, "aot over i = %d REG_IRQSRC:0X%X", i, reg_val_tmp); + msleep(1); + } + + //3. scan 8 cnt over + aw_sar_i2c_read(p_sar->i2c, REG_SCANCTRL0, &ch_en); + aw_sar_i2c_read(p_sar->i2c, REG_CHINTEN, &scan_over_en); + if ((ch_en & AW9610X_AOT_MASK) != (scan_over_en & AW9610X_AOT_MASK)) { + aw_sar_i2c_write_bits(p_sar->i2c, REG_CHINTEN, ~(AW9610X_AOT_MASK), ch_en & (AW9610X_AOT_MASK)); + } + + for (scan_over_cnt = 0; scan_over_cnt < AW9610X_AOT_SCAN_OVER_CNT; scan_over_cnt++) { + for (i = 0; i < max_delay_ms; i++) { + aw_sar_i2c_read(p_sar->i2c, REG_IRQSRC, ®_val_tmp); + if (((reg_val_tmp >> REG_IRQSRC_SCAN_OVER_BIT) & 0x01) == 1) { + //AWLOGD(p_sar->dev, "scan %d cnt over REG_IRQSRC %d:0X%X", scan_over_cnt, i, reg_val_tmp); + break; + } + msleep(1); + } + } + if ((ch_en & AW9610X_AOT_MASK) != (scan_over_en & AW9610X_AOT_MASK)) { + aw_sar_i2c_write_bits(p_sar->i2c, REG_CHINTEN, ~(AW9610X_AOT_MASK), ch_en & (AW9610X_AOT_MASK)); + } + + if (aw9610x->vers == AW9610XA) { + //4. chip set sleep mode + aw_sar_i2c_write(p_sar->i2c, REG_CMD, AW9610X_SLEEP_MODE); + } else if (aw9610x->vers == AW9610X) { + aw_sar_i2c_write(p_sar->i2c, REG_CMD, AW9610X_DEEPSLEEP_MODE); //set aw9610x sleep mode + } + for (i = 0; i < max_delay_ms; i++) { + aw_sar_i2c_read(p_sar->i2c, REG_WST, ®_val_tmp); + if ((reg_val_tmp & 0xFF) == REG_REG_WST_SLEEP_MODE) { + AWLOGD(p_sar->dev, "sleep i = %d REG_WST:0X%X", i, reg_val_tmp); + break; + } + msleep(1); + } + + //5. write baseline data + for (i = 0; i < AW9610X_CHANNEL_MAX; i++) { + aw_sar_i2c_read(p_sar->i2c, REG_COMP_CH0 + i * AW9610X_REG_OFFSET_STEP, ®_val_tmp); + aw_sar_i2c_write(p_sar->i2c, REG_BASELINE_CH0 + i *AW9610X_REG_OFFSET_STEP, reg_val_tmp); + } + + //6. chip set active, irq recovery + aw_sar_i2c_write(p_sar->i2c, REG_CMD, AW9610X_ACTIVE_MODE); + aw_sar_i2c_write(p_sar->i2c, REG_IRQEN, irqen_reg_val); + + AWLOGD(p_sar->dev, "AW9610X_TVS_ABNORMAL_CAIL OVER!"); + + return AW_OK; +} +#else +static ssize_t aw9610x_set_aot(void *data) +{ + struct aw_sar *p_sar = (struct aw_sar *)data; + + aw_sar_i2c_write_bits(p_sar->i2c, REG_SCANCTRL0, ~(AW9610X_AOT_MASK << AW9610X_AOT_BIT), + (AW9610X_AOT_MASK) << AW9610X_AOT_BIT); + return AW_OK; +} +#endif + +static const struct aw_sar_aot_t g_aw9610x_aot = { + .p_set_aot_node_fn = aw9610x_set_aot, +}; + +/**********************mode operation start*******************************/ +static void aw9610x_enable_clock(void *i2c) +{ + aw_sar_i2c_write(i2c, REG_OSCEN, AW9610X_CPU_WORK_MASK); +} + +static uint32_t aw9610x_rc_irqscr(void *i2c) +{ + uint32_t val = 0; + aw_sar_i2c_read(i2c, REG_IRQSRC, &val); + return val; +} + +//Note: TVS exceptions need to be handled after active +static void aw9610x_set_active_cmd(void *i2c) +{ + aw_sar_i2c_write(i2c, REG_CMD, AW9610X_ACTIVE_MODE); + +#ifdef AW9610X_TVS_ABNORMAL_CAIL + AWLOGD(g_aw_sar->dev, "AW9610X_TVS_ABNORMAL_CAIL"); + if (g_aw_sar != NULL) { + aw9610x_set_aot(g_aw_sar); + } +#endif +} + +static void aw9610x_set_sleep_cmd(void *i2c) +{ + aw_sar_i2c_write(i2c, REG_CMD, AW9610X_SLEEP_MODE); +} + +static void aw9610x_set_deepsleep_cmd(void *i2c) +{ + aw_sar_i2c_write(i2c, REG_CMD, AW9610X_DEEPSLEEP_MODE); +} + +static const struct aw_sar_mode_set_t g_aw9610x_mode_set[] = { + { + .chip_id = SAR_AW9610XA | SAR_AW9610X, + .chip_mode = { + .curr_mode = AW9610X_ACTIVE_MODE, + .last_mode = AW9610X_DEEPSLEEP_MODE, + }, + .mode_switch_ops = { + .enable_clock = aw9610x_enable_clock, + .rc_irqscr = NULL, + .mode_update = aw9610x_set_active_cmd, + }, + }, + { + .chip_id = SAR_AW9610XA | SAR_AW9610X, + .chip_mode = { + .curr_mode = AW9610X_ACTIVE_MODE, + .last_mode = AW9610X_SLEEP_MODE, + }, + .mode_switch_ops = { + .enable_clock = NULL, + .rc_irqscr = NULL, + .mode_update = aw9610x_set_active_cmd, + }, + }, + { + .chip_id = SAR_AW9610XA | SAR_AW9610X, + .chip_mode = { + .curr_mode = AW9610X_ACTIVE_MODE, + .last_mode = AW9610X_ACTIVE_MODE, + }, + .mode_switch_ops = { + .enable_clock = NULL, + .rc_irqscr = NULL, + .mode_update = aw9610x_set_active_cmd, + }, + }, + { + .chip_id = SAR_AW9610XA | SAR_AW9610X, + .chip_mode = { + .curr_mode = AW9610X_SLEEP_MODE, + .last_mode = AW9610X_DEEPSLEEP_MODE, + }, + .mode_switch_ops = { + .enable_clock = aw9610x_enable_clock, + .rc_irqscr = NULL, + .mode_update = aw9610x_set_sleep_cmd, + }, + }, + { + .chip_id = SAR_AW9610XA | SAR_AW9610X, + .chip_mode = { + .curr_mode = AW9610X_SLEEP_MODE, + .last_mode = AW9610X_ACTIVE_MODE, + }, + .mode_switch_ops = { + .enable_clock = NULL, + .rc_irqscr = NULL, + .mode_update = aw9610x_set_sleep_cmd, + }, + }, + { + .chip_id = SAR_AW9610XA | SAR_AW9610X, + .chip_mode = { + .curr_mode = AW9610X_DEEPSLEEP_MODE, + .last_mode = AW9610X_SLEEP_MODE, + }, + .mode_switch_ops = { + .enable_clock = NULL, + .rc_irqscr = NULL, + .mode_update = aw9610x_set_deepsleep_cmd, + }, + }, + { + .chip_id = SAR_AW9610XA, + .chip_mode = { + .curr_mode = AW9610X_DEEPSLEEP_MODE, + .last_mode = AW9610X_ACTIVE_MODE, + }, + .mode_switch_ops = { + .enable_clock = NULL, + .rc_irqscr = aw9610x_rc_irqscr, + .mode_update = aw9610x_set_deepsleep_cmd, + }, + }, + { + .chip_id = SAR_AW9610X, + .chip_mode = { + .curr_mode = AW9610X_DEEPSLEEP_MODE, + .last_mode = AW9610X_ACTIVE_MODE, + }, + .mode_switch_ops = { + .enable_clock = NULL, + .rc_irqscr = NULL, + .mode_update = NULL, + }, + }, +}; +/**********************mode operation end*******************************/ + +static const struct aw_sar_irq_init_t g_aw9610x_irq_init = { + .flags = GPIOF_DIR_IN | GPIOF_INIT_HIGH, + .irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + .handler = NULL, + .thread_fn = NULL, + .rc_irq_fn = aw9610x_rc_irqscr, + .irq_spec_handler_fn = aw9610x_irq_handle_func, +}; + +static const struct aw_sar_soft_rst_t g_aw9610x_soft_rst = { + .reg_rst = REG_RESET, + .reg_rst_val = 0, + .delay_ms = 20, +}; + +static const struct aw_sar_init_over_irq_t g_aw9610x_init_over_irq = { + .wait_times = 20, + .daley_step = 1, + .reg_irqsrc = REG_IRQSRC, + .irq_offset_bit = 0, + .irq_mask = 0x1, + .irq_flag = 0x1, +}; + +static const struct aw_sar_load_bin_t g_aw9610x_load_reg_bin = { + .bin_name = "aw9610x", + .bin_opera_func = (aw_sar_bin_opera_t)aw9610x_load_reg_bin, + .p_update_fn = NULL, +}; + +static const struct aw_sar_para_load_t g_aw9610x_reg_arr_para = { + .reg_arr = aw9610x_reg_default, + .reg_arr_len = (sizeof(aw9610x_reg_default) / sizeof(aw9610x_reg_default[0])), +}; + +static const struct aw_sar_diff_t g_aw9610x_diff = { + .diff0_reg = REG_DIFF_CH0, + .diff_step = 4, + .rm_float = AW9610x_DATA_PROCESS_FACTOR, +}; + +static const struct aw_sar_mode_t g_aw9610x_mode = { + .mode_set_arr = &g_aw9610x_mode_set[0], + .mode_set_arr_len = sizeof(g_aw9610x_mode_set) / sizeof(g_aw9610x_mode_set[0]), + .p_set_mode_node_fn = NULL, + .p_get_mode_node_fn = aw9610x_operation_mode_get, +}; + +static const struct aw_sar_reg_list_t g_aw9610x_reg_list = { + .reg_none_access = REG_NONE_ACCESS, + .reg_rd_access = REG_RD_ACCESS, + .reg_wd_access = REG_WR_ACCESS, + .reg_perm = (struct aw_sar_reg_data *)&g_aw9610x_reg_access[0], + .reg_num = sizeof(g_aw9610x_reg_access) / sizeof(g_aw9610x_reg_access[0]), +}; + +static const struct aw_sar_pm_t g_aw9610x_pm_chip_mode = { + .suspend_set_mode = AW9610X_SLEEP_MODE, + .resume_set_mode = AW9610X_ACTIVE_MODE, + .shutdown_set_mode = AW9610X_SLEEP_MODE, +}; + +static const struct aw_sar_chip_mode_t g_aw9610x_chip_mode = { + .init_mode = AW9610X_ACTIVE_MODE, + .active = AW9610X_ACTIVE_MODE, + .pre_init_mode = AW9610X_SLEEP_MODE, +}; + +static const struct aw_sar_regulator_config_t g_regulator_config = { + .vcc_name = "vcc", + .min_uV = AW9610X_SAR_VCC_MIN_UV, + .max_uV = AW9610X_SAR_VCC_MAX_UV, +}; + +struct aw_sar_awrw_t g_aw9610x_awrw = { + .p_set_awrw_node_fn = aw9610x_awrw_set, + .p_get_awrw_node_fn = aw9610x_awrw_get, +}; + +static const struct aw_sar_platform_config g_aw9610x_platform_config = { + .p_regulator_config = &g_regulator_config, + .p_irq_init = &g_aw9610x_irq_init, + .p_pm_chip_mode = &g_aw9610x_pm_chip_mode, +}; + +static const struct aw_sar_chip_config g_aw9610x_chip_config = { + .ch_num_max = AW9610X_CHANNEL_MAX, + + .p_platform_config = &g_aw9610x_platform_config, + + .p_check_chipid = &g_aw9610x_check_chipid, + .p_soft_rst = &g_aw9610x_soft_rst, + .p_init_over_irq = &g_aw9610x_init_over_irq, + .p_fw_bin = NULL, + .p_reg_bin = &g_aw9610x_load_reg_bin, + .p_chip_mode = &g_aw9610x_chip_mode, + + //Node usage parameters + .p_reg_list = &g_aw9610x_reg_list, + .p_reg_arr = &g_aw9610x_reg_arr_para, + .p_aot = &g_aw9610x_aot, + .p_diff = &g_aw9610x_diff, + .p_offset = &g_aw9610x_offset, + .p_mode = &g_aw9610x_mode, + .p_prox_fw = NULL, + .p_get_chip_info = &g_aw9610x_get_chip_info, + .p_aw_sar_awrw = &g_aw9610x_awrw, + .p_boot_bin = NULL, + + .p_other_operation = aw9610x_get_chip_version, + .p_other_opera_free = NULL, +}; + +int32_t aw9610x_init(struct aw_sar *p_sar) +{ + if (p_sar == NULL) { + AWLOGE(p_sar->dev, "para is NULL, error!"); + return -AW_ERR; + } + + g_aw_sar = p_sar; + + p_sar->priv_data = devm_kzalloc(p_sar->dev, sizeof(struct aw9610x), GFP_KERNEL); + if (p_sar->priv_data == NULL) { + AWLOGE(p_sar->dev, "priv_data failed to malloc memory!"); + return -AW_ERR; + } + + //Chip private function operation + p_sar->p_sar_para = &g_aw9610x_chip_config; + + return AW_OK; +} + +void aw9610x_deinit(struct aw_sar *p_sar) +{ + if (p_sar->priv_data != NULL) { + devm_kfree(p_sar->dev, p_sar->priv_data); + } +} diff --git a/drivers/misc/awinic/sarsensor/aw9620x/aw9620x.c b/drivers/misc/awinic/sarsensor/aw9620x/aw9620x.c new file mode 100644 index 000000000000..ed3f3f621268 --- /dev/null +++ b/drivers/misc/awinic/sarsensor/aw9620x/aw9620x.c @@ -0,0 +1,655 @@ +#include "aw9620x.h" +#include "aw_update_fw_interface.h" + +#define AW9620X_DRIVER_VERSION "v0.0.8" + +static uint32_t aw9620x_rc_irqscr(void *i2c); + +static void aw9620x_irq_handle_func(uint32_t irq_status, void *data) +{ + int32_t ret = 0; + uint32_t curr_status_val = 0; + uint32_t curr_status = 0; + uint8_t i = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + + AWLOGD(p_sar->dev, "IRQSRC = 0x%x", irq_status); + + ret = aw_sar_i2c_read(p_sar->i2c, REG_STAT0, &curr_status_val); + if (ret < 0) { + AWLOGE(p_sar->dev, "i2c IO error"); + return; + } + AWLOGD(p_sar->dev, "STAT0 = 0x%x", curr_status_val); + + for (i = 0; i < AW9620X_CHANNEL_NUM_MAX; i++) { + curr_status = (((curr_status_val >> (OFFSET_BIT_8 + i)) & 0x1) << 1) | + (((curr_status_val >> (i)) & 0x1)); + if (p_sar->channels_arr[i].used == AW_FALSE) { + continue; + } + if (p_sar->channels_arr[i].last_channel_info == curr_status) { + continue; + } + + switch (curr_status) { + case AW9620X_TRIGGER_FAR: + input_report_abs(p_sar->channels_arr[i].input, ABS_DISTANCE, AW9620X_TRIGGER_FAR); + break; + case AW9620X_TRIGGER_TH0: + input_report_abs(p_sar->channels_arr[i].input, ABS_DISTANCE, AW9620X_TRIGGER_TH0); + break; + case AW9620X_TRIGGER_TH1: + input_report_abs(p_sar->channels_arr[i].input, ABS_DISTANCE, AW9620X_TRIGGER_TH1); + break; + default: + AWLOGD(p_sar->dev, "error abs distance"); + return; + } + input_sync(p_sar->channels_arr[i].input); + + p_sar->channels_arr[i].last_channel_info = curr_status; + } +} + +int32_t aw9620x_check_chipid(void *data) +{ + int32_t ret = -AW_ERR; + uint32_t reg_val = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + + if ((p_sar == NULL) && ( p_sar->priv_data == NULL)) { + return -AW_BIN_PARA_INVALID; + } + + AWLOGD(p_sar->dev, "enter"); + + ret = aw_sar_i2c_read(p_sar->i2c, REG_CHIP_ID, ®_val); + if (ret < 0) { + AWLOGE(p_sar->dev, "read CHIP ID failed: %d", ret); + return -AW_ERR; + } + + switch (reg_val) + { + case AW96203CSR_CHIP_ID: + memcpy(p_sar->chip_name, "aw96203", 8); + AWLOGD(p_sar->dev, "AW96203CSR CHIP ID : 0x%x", reg_val); + break; + case AW96205DNR_CHIP_ID: + memcpy(p_sar->chip_name, "aw96205", 8); + AWLOGD(p_sar->dev, "AW96205DNR CHIP ID : 0x%x", reg_val); + break; + case AW96208CSR_CHIP_ID: + memcpy(p_sar->chip_name, "aw96208", 8); + AWLOGD(p_sar->dev, "AW96208CSR CHIP ID : 0x%x", reg_val); + break; + default: + AWLOGD(p_sar->dev, "no chipid,need update root and frimware,CHIP ID : 0x%x", reg_val); + return -AW_ERR_CHIPID; + break; + } + + p_sar->chip_type = AW_SAR_NONE_CHECK_CHIP; + + return AW_OK; +} + +static int32_t aw9620x_soft_reset(void *data) +{ + int32_t ret = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + + AWLOGD(p_sar->dev, "enter"); + + ret = aw_sar_i2c_write(p_sar->i2c, REG_ACCESSEN, REG_OPEN_APB_ACCESS_EN); + if (ret < 0) { + AWLOGE(p_sar->dev, "read REG_APB_ACCESS_EN err: %d", ret); + return -AW_ERR; + } + + ret = aw_sar_i2c_write(p_sar->i2c, AW_REG_FLASH_WAKE_UP, AW_REG_FLASH_WAKE_UP_ON); + if (ret < 0) { + AWLOGE(p_sar->dev, "read REG_APB_ACCESS_EN err: %d", ret); + return -AW_ERR; + } + msleep(1); + + ret = aw_sar_i2c_write(p_sar->i2c, REG_MCFG, REG_SET_MCFG00); + if (ret != AW_OK) { + AWLOGD(p_sar->dev, "REG_MCFG err"); + return ret; + } + msleep(AW9620X_REG_MCFG_DELAY_MS); + + ret = aw_sar_i2c_write(p_sar->i2c, REG_RSTNALL, AW9620X_REG_RSTNALL_VAL); + if (ret < 0) { + AWLOGE(p_sar->dev, "read soft_reset err: %d", ret); + return -AW_ERR; + } + + msleep(AW9620X_POWER_ON_DELAY_MS); + + ret = aw_sar_i2c_write(p_sar->i2c, AW_REG_FLASH_WAKE_UP, AW_REG_FLASH_WAKE_UP_OFF); + if (ret < 0) { + AWLOGE(p_sar->dev, "read REG_APB_ACCESS_EN err: %d", ret); + return -AW_ERR; + } + + return AW_OK; +} + +static int32_t aw9620x_load_reg_bin(struct aw_bin *aw_bin, void *load_bin_para) +{ + int32_t ret = 0; + struct aw_sar *p_sar = (struct aw_sar *)load_bin_para; +/* + if (p_sar->chip_name == NULL) { + AWLOGE(p_sar->dev, "p_sar->chip_name is NULL, error!"); + return -AW_ERR; + } +*/ + AWLOGE(p_sar->dev, "reg chip name: %s, soc chip name: %s, len = %d", + p_sar->chip_name, aw_bin->header_info[0].chip_type, aw_bin->info.len); + + ret = strncmp(p_sar->chip_name, aw_bin->header_info[0].chip_type, sizeof(aw_bin->header_info[0].chip_type)); + if (ret != 0) { + AWLOGE(p_sar->dev, + "load_binname(%s) incompatible with chip type(%s)", + p_sar->chip_name, aw_bin->header_info[0].chip_type); + //return -AW_ERR; + } + ret = aw_sar_load_reg(aw_bin, p_sar->i2c); + + return ret; +} + +/*************************fw update start************************/ +static int32_t aw9620x_reg_update_firmware(struct aw_bin *aw_bin, void *load_bin_para) +{ + uint32_t start_index = aw_bin->header_info[0].valid_data_addr; + int32_t ret = 0; + struct aw_sar *p_sar = (struct aw_sar *)load_bin_para; + + ret = aw9620x_reg_update_fw(p_sar->i2c, + &(aw_bin->info.data[start_index]), aw_bin->header_info[0].valid_data_len); + if (ret != AW_OK) { + AWLOGE(p_sar->dev, "reg update fw error"); + } + + ret = aw9620x_soft_reset(p_sar); + aw9620x_rc_irqscr(p_sar->i2c); + + return ret; +} + +//BOOT update +static int32_t aw9620x_reg_update_bt(struct aw_bin *aw_bin, void *load_bin_para) +{ + uint32_t start_index = aw_bin->header_info[0].valid_data_addr; + struct aw_sar *p_sar = (struct aw_sar *)load_bin_para; + + return aw9620x_reg_update_boot(p_sar->i2c, + &(aw_bin->info.data[start_index]), aw_bin->header_info[0].valid_data_len); +} + +static int32_t aw9620x_prot_update_firmware(struct aw_bin *aw_bin,void *load_bin_para) +{ + uint32_t start_index = aw_bin->header_info[0].valid_data_addr; + uint32_t fw_bin_version = aw_bin->header_info[0].app_version; + struct aw_sar *p_sar = (struct aw_sar *)load_bin_para; + //If the verification of flash firmware area fails, the version number will not be compared, and it will be loaded directly + uint8_t direct_updata_flag = p_sar->fw_fail_flag; + + return aw9620x_prot_update_fw(p_sar->i2c, direct_updata_flag, + &(aw_bin->info.data[start_index]), aw_bin->header_info[0].valid_data_len, fw_bin_version); +} + +static int32_t aw9620x_prox_direct_upgrade_firmware(struct aw_bin *aw_bin, void *load_bin_para) +{ + uint32_t start_index = aw_bin->header_info[0].valid_data_addr; + uint32_t fw_bin_version = aw_bin->header_info[0].app_version; + struct aw_sar *p_sar = (struct aw_sar *)load_bin_para; + + return aw9620x_prot_update_fw(p_sar->i2c, AW_TRUE, + &(aw_bin->info.data[start_index]), aw_bin->header_info[0].valid_data_len, fw_bin_version); +} + +/*************************fw update end************************/ + +/**********************mode operation start*******************************/ +static void aw9620x_enable_clock(void *i2c) +{ + aw_sar_i2c_write(i2c, REG_HOSTCTRL, REG_HOSTCTRL_EN); +} + +static uint32_t aw9620x_rc_irqscr(void *i2c) +{ + uint32_t val = 0; + aw_sar_i2c_read(i2c, REG_IRQSRC, &val); + return val; +} + +static void aw9620x_set_active_cmd(void *i2c) +{ + aw_sar_i2c_write(i2c, REG_CMD, AW9620X_ACTIVE_MODE); +} + +static void aw9620x_set_sleep_cmd(void *i2c) +{ + aw_sar_i2c_write(i2c, REG_CMD, AW9620X_SLEEP_MODE); +} + +static void aw9620x_set_deepsleep_cmd(void *i2c) +{ + aw_sar_i2c_write(i2c, REG_CMD, AW9620X_DEEPSLEEP_MODE); +} + +static ssize_t aw9620x_mode_operation_get(void *data, char *buf) +{ + uint32_t reg_val = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + + aw_sar_i2c_read(p_sar->i2c, REG_WST, ®_val); + + return snprintf(buf, PAGE_SIZE, "mode : %d, REG_WST :0x%x \n", + p_sar->last_mode, reg_val); +} + +static void aw9620x_mul_cap_parasitic_data_get(struct aw_sar *p_sar, + char *buf, uint32_t ch, uint32_t *p_len, uint32_t reg_data) +{ + uint32_t j = 0; + uint32_t reg_data_cfg_val = 0; + uint32_t off_m = 0; + uint32_t fine_off_f_temp = 0; + uint32_t off_f = 0; + uint32_t off_c = 0; + int32_t s_off_c = 0; + int32_t parasitic_data_temp = 0; + int32_t parasitic_data_int = 0; + int32_t parasitic_data_float = 0; + + if (((reg_data >> AW9620X_AFECFG3_CVOFF2X) & 0x1) == 0) { + aw_sar_i2c_read(p_sar->i2c, REG_AFECFG1_CH0 + AW9620X_CFG_REG_STEP * ch, ®_data_cfg_val); + off_m = (reg_data_cfg_val >> 16) & 0xff; + off_f = reg_data_cfg_val & 0xff; + fine_off_f_temp = off_f * AW9620X_FINE_ADJUST_STEP0; + off_c = (reg_data_cfg_val >> 8) & 0xff; + for (j = 0; j < 8; j++) { + s_off_c += ((1 - 2 * ((off_m >> j) & 0x1)) * ((off_c >> j) & 0x1) * aw_sar_pow2(j)) * AW9620X_COARSE_ADJUST_STEP0; + } + + parasitic_data_temp = AW_SAR_ABS((int32_t)fine_off_f_temp + s_off_c); + parasitic_data_int = parasitic_data_temp / AW9620X_PARA_TIMES; + parasitic_data_float = parasitic_data_temp % AW9620X_PARA_TIMES; + *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "ch%d parasitic_data %d.%d pf\n", ch, parasitic_data_int, parasitic_data_float); + } else { + *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "ch%d Mutual tolerance mode does not allow 2x to be configured", ch); + } +} + +static void aw9620x_self_cap_parasitic_data_get(struct aw_sar *p_sar, + char *buf, uint32_t ch, uint32_t *p_len, uint32_t reg_data) +{ + uint32_t reg_data_cfg = 0; + uint32_t reg_data_cfg_val = 0; + uint32_t reg_data_cfg_tmp0 = 0; + uint32_t reg_data_cfg_tmp1 = 0; + uint32_t cfg_int = 0; + uint32_t cfg_float = 0; + + aw_sar_i2c_read(p_sar->i2c, REG_AFECFG1_CH0 + AW9620X_CFG_REG_STEP * ch, ®_data_cfg_val); + + if (((reg_data >> AW9620X_AFECFG3_CVOFF2X) & 0x1) == 0) { + reg_data_cfg_tmp0 = (reg_data_cfg_val & 0xff) * AW9620X_FINE_ADJUST_STEP0; + reg_data_cfg_tmp1 = ((reg_data_cfg_val >> 8) & 0xff) * AW9620X_COARSE_ADJUST_STEP0; + } else { + reg_data_cfg_tmp0 = (reg_data_cfg_val & 0xff) * AW9620X_FINE_ADJUST_STEP1; + reg_data_cfg_tmp1 = ((reg_data_cfg_val >> 8) & 0xff) * AW9620X_COARSE_ADJUST_STEP1; + } + reg_data_cfg = reg_data_cfg_tmp0 + reg_data_cfg_tmp1; + cfg_int = reg_data_cfg / AW9620X_PARA_TIMES; + cfg_float = reg_data_cfg % AW9620X_PARA_TIMES; + *p_len+= snprintf(buf + *p_len, PAGE_SIZE - *p_len, "ch%d parasitic_data %d.%d pf\n", ch, cfg_int, cfg_float); +} + +static ssize_t aw9620x_offset_get(void *data, char *buf) +{ + uint32_t len = 0; + uint32_t i = 0; + uint32_t reg_data = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + + for (i = 0; i < AW9620X_CHANNEL_NUM_MAX; i++) { + aw_sar_i2c_read(p_sar->i2c, REG_AFECFG3_CH0 + AW9620X_CFG_REG_STEP * i, ®_data); + if (((reg_data >> AW9620X_AFECFG3_CVMULTUALMOD) & 0x1) != 1) { + //self contained + aw9620x_self_cap_parasitic_data_get(p_sar, buf, i, &len, reg_data); + } else { + //mutual compatibility + aw9620x_mul_cap_parasitic_data_get(p_sar, buf, i, &len, reg_data); + } + } + + return len; +} + +static void aw9620x_sar_chip_info_get(void *data, char *buf, ssize_t *p_len) +{ + uint32_t reg_data = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + + *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "sar%d, aw9620x chip driver version:%s\n", + p_sar->dts_info.sar_num, AW9620X_DRIVER_VERSION); + *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "The driver supports UI\n"); + + aw_sar_i2c_read(p_sar->i2c, REG_CHIP_ID, ®_data); + *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "chipid is 0x%08x\n", reg_data); + + aw_sar_i2c_read(p_sar->i2c, REG_IRQEN, ®_data); + *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "REG_HOSTIRQEN is 0x%08x\n", reg_data); +} + +static void aw9620x_get_firmware_version(void *data, char *buf, ssize_t *p_len) +{ + uint32_t fw_ver = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + + aw_sar_i2c_read(p_sar->i2c, REG_FWVER, &fw_ver); + + *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "current firmware version is 0x%08x\n", fw_ver); +} + +static int32_t aw9320x_get_err_info(void *data) +{ + uint32_t err_code = 0; + uint32_t boot_mode = 0; + uint32_t jump_info = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + + aw_sar_i2c_write(p_sar->i2c, REG_ACCESSEN, REG_ACCESSEN_OPEN); + + //Get error check code,(0x09: Firmware verification failed.0x7:boot verification failed) + aw_sar_i2c_read(p_sar->i2c, AW9320X_SRAM_ERROR_CODE, &err_code); + + //The bit[31:24] must be 0, otherwise the boot cannot be jumped out actively + aw_sar_i2c_read(p_sar->i2c, REG_UPDATA_DIS, &jump_info); + + //bit8 0:Boot from ROM, 1: Boot from RAM + aw_sar_i2c_read(p_sar->i2c, REG_MCFG, &boot_mode); + + AWLOGE(p_sar->dev, "0x1c00:0x%x, 0x4744:0x%x, 0x4444:0x%x", err_code, jump_info, boot_mode); + + aw_sar_i2c_write(p_sar->i2c, REG_ACCESSEN, REG_ACCESSEN_CLOSE); + + return AW_OK; +} + + +static const struct aw_sar_mode_set_t g_aw9620x_mode_set[] = { + //0 + { + .chip_id = AW_SAR_NONE_CHECK_CHIP, + .chip_mode = { + .curr_mode = AW9620X_ACTIVE_MODE, + .last_mode = AW9620X_DEEPSLEEP_MODE, + }, + .mode_switch_ops = { + .enable_clock = aw9620x_enable_clock, + .rc_irqscr = NULL, + .mode_update = aw9620x_set_active_cmd, + }, + }, + //1 + { + .chip_id = AW_SAR_NONE_CHECK_CHIP, + .chip_mode = { + .curr_mode = AW9620X_ACTIVE_MODE, + .last_mode = AW9620X_SLEEP_MODE, + }, + .mode_switch_ops = { + .enable_clock = NULL, + .rc_irqscr = NULL, + .mode_update = aw9620x_set_active_cmd, + }, + }, + //1 + { + .chip_id = AW_SAR_NONE_CHECK_CHIP, + .chip_mode = { + .curr_mode = AW9620X_ACTIVE_MODE, + .last_mode = AW9620X_ACTIVE_MODE, + }, + .mode_switch_ops = { + .enable_clock = NULL, + .rc_irqscr = NULL, + .mode_update = aw9620x_set_active_cmd, + }, + }, + //2 + { + .chip_id = AW_SAR_NONE_CHECK_CHIP, + .chip_mode = { + .curr_mode = AW9620X_SLEEP_MODE, + .last_mode = AW9620X_DEEPSLEEP_MODE, + }, + .mode_switch_ops = { + .enable_clock = aw9620x_enable_clock, + .rc_irqscr = NULL, + .mode_update = aw9620x_set_sleep_cmd, + }, + }, + //3 + { + .chip_id = AW_SAR_NONE_CHECK_CHIP, + .chip_mode = { + .curr_mode = AW9620X_SLEEP_MODE, + .last_mode = AW9620X_ACTIVE_MODE, + }, + .mode_switch_ops = { + .enable_clock = NULL, + .rc_irqscr = NULL, + .mode_update = aw9620x_set_sleep_cmd, + }, + }, + //4 + { + .chip_id = AW_SAR_NONE_CHECK_CHIP, + .chip_mode = { + .curr_mode = AW9620X_DEEPSLEEP_MODE, + .last_mode = AW9620X_SLEEP_MODE, + }, + .mode_switch_ops = { + .enable_clock = NULL, + .rc_irqscr = NULL, + .mode_update = aw9620x_set_deepsleep_cmd, + }, + }, + //5 + { + .chip_id = AW_SAR_NONE_CHECK_CHIP, + .chip_mode = { + .curr_mode = AW9620X_DEEPSLEEP_MODE, + .last_mode = AW9620X_ACTIVE_MODE, + }, + .mode_switch_ops = { + .enable_clock = NULL, + .rc_irqscr = aw9620x_rc_irqscr, + .mode_update = aw9620x_set_deepsleep_cmd, + }, + }, +}; + +static const struct aw_sar_check_chipid_t g_aw9620x_check_chipid = { + .p_check_chipid_fn = aw9620x_check_chipid, +}; + +static const struct aw_sar_soft_rst_t g_aw9620x_soft_rst = { + .p_soft_reset_fn = aw9620x_soft_reset, +}; + +static const struct aw_sar_get_chip_info_t g_aw9620x_get_chip_info = { + .p_get_chip_info_node_fn = aw9620x_sar_chip_info_get, +}; + +#define AW9620X_MODE_SET_ARR_LEN (sizeof(g_aw9620x_mode_set) / sizeof(g_aw9620x_mode_set[0])) + +static const struct aw_sar_irq_init_t g_aw9620x_irq_init = { + .flags = GPIOF_DIR_IN | GPIOF_INIT_HIGH, + .irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + .handler = NULL, + .thread_fn = NULL, + .rc_irq_fn = aw9620x_rc_irqscr, + .irq_spec_handler_fn = aw9620x_irq_handle_func, +}; + +static const struct aw_sar_init_over_irq_t g_aw9620x_init_over_irq = { + .wait_times = 100, + .daley_step = 1, + .reg_irqsrc = REG_IRQSRC, + .irq_offset_bit = 0, + .irq_mask = 0x1, + .irq_flag = 0x1, + .p_check_init_over_irq_fn = NULL, + .p_get_err_type_fn = aw9320x_get_err_info, +}; + +static const struct aw_sar_load_bin_t g_aw9620x_load_reg_bin = { + .bin_name = "aw9620x_reg", + .bin_opera_func = aw9620x_load_reg_bin, + .bin_load_fail_opera = NULL, + .p_update_fn = NULL, +}; + +static const struct aw_sar_load_bin_t g_aw9620x_load_boot_bin = { + .bin_name = "aw9620x_boot", + .bin_opera_func = aw9620x_reg_update_bt, + .bin_load_fail_opera = NULL, +}; + +static const struct aw_sar_load_bin_t g_aw9620x_load_fw_bin = { + .bin_name = "aw9620x_fw", + .bin_opera_func = aw9620x_prot_update_firmware, + .bin_load_fail_opera = aw9620x_reg_update_firmware, +}; + +//Node prox upgrade firmware +static const struct aw_sar_load_bin_t g_aw9620x_node_prox_load_fw_bin = { + .bin_name = "aw9620x_fw", + .bin_opera_func = aw9620x_prox_direct_upgrade_firmware, + .p_get_prot_update_fw_node_fn = aw9620x_get_firmware_version, + .bin_load_fail_opera = NULL, +}; + +//Node reg upgrade firmware +static const struct aw_sar_load_bin_t g_aw9620x_node_reg_load_fw_bin = { + .bin_name = "aw9620x_fw", + .bin_opera_func = aw9620x_reg_update_firmware, + .bin_load_fail_opera = NULL, +}; + +static const struct aw_sar_para_load_t g_aw9620x_reg_arr_para = { + .reg_arr = aw9620x_reg_default, + .reg_arr_len = sizeof(aw9620x_reg_default) / sizeof(aw9620x_reg_default[0]), +}; + +static const struct aw_sar_aot_t g_aw9620x_aot = { + .aot_reg = REG_SCANCTRL0, + .aot_mask = ~(0xff << 8), + .aot_flag = 0xff << 8, +}; + +static const struct aw_sar_diff_t g_aw9620x_diff = { + .diff0_reg = REG_DIFF_CH0, + .diff_step = AW9620X_REG_STEP, + .rm_float = AW9620X_REMOVE_FLOAT_COEF, +}; + +static const struct aw_sar_offset_t g_aw9620x_offset = { + .p_get_offset_node_fn = aw9620x_offset_get, +}; + +static const struct aw_sar_mode_t g_aw9620x_mode = { + .mode_set_arr = &g_aw9620x_mode_set[0], + .mode_set_arr_len = AW9620X_MODE_SET_ARR_LEN, + .p_get_mode_node_fn = aw9620x_mode_operation_get, +}; + +static const struct aw_sar_reg_list_t g_aw9620x_reg_list = { + .reg_none_access = REG_NONE_ACCESS, + .reg_rd_access = REG_RD_ACCESS, + .reg_wd_access = REG_WR_ACCESS, + .reg_perm = (struct aw_sar_reg_data *)&g_aw9620x_reg_access[0], + .reg_num = sizeof(g_aw9620x_reg_access) / sizeof(g_aw9620x_reg_access[0]), +}; + +static const struct aw_sar_pm_t g_aw9620x_pm_chip_mode = { + .suspend_set_mode = AW9620X_SLEEP_MODE, + .resume_set_mode = AW9620X_ACTIVE_MODE, + .shutdown_set_mode = AW9620X_SLEEP_MODE, +}; + +static const struct aw_sar_chip_mode_t g_aw9620x_chip_mode = { + .init_mode = AW9620X_ACTIVE_MODE, + .active = AW9620X_ACTIVE_MODE, + .pre_init_mode = AW9620X_SLEEP_MODE, +}; + +static const struct aw_sar_regulator_config_t g_aw9620x_regulator_config = { + .vcc_name = "vcc", + .min_uV = AW9620X_SAR_VCC_MIN_UV, + .max_uV = AW9620X_SAR_VCC_MAX_UV, +}; + +static const struct aw_sar_platform_config g_aw9620x_platform_config = { + .p_regulator_config = &g_aw9620x_regulator_config, + .p_irq_init = &g_aw9620x_irq_init, + .p_pm_chip_mode = &g_aw9620x_pm_chip_mode, +}; + +static const struct aw_sar_chip_config g_aw9620x_chip_config = { + .ch_num_max = AW9620X_CHANNEL_NUM_MAX, + + .p_platform_config = &g_aw9620x_platform_config, + + .p_check_chipid = &g_aw9620x_check_chipid, + .p_soft_rst = &g_aw9620x_soft_rst, + .p_init_over_irq = &g_aw9620x_init_over_irq, + .p_fw_bin = &g_aw9620x_load_fw_bin, + .p_reg_bin = &g_aw9620x_load_reg_bin, + .p_chip_mode = &g_aw9620x_chip_mode, + + //Node usage parameters + .p_reg_list = &g_aw9620x_reg_list, + .p_reg_arr = &g_aw9620x_reg_arr_para, + .p_aot = &g_aw9620x_aot, + .p_diff = &g_aw9620x_diff, + .p_offset = &g_aw9620x_offset, + .p_mode = &g_aw9620x_mode, + .p_prox_fw = &g_aw9620x_node_prox_load_fw_bin, + .p_reg_fw = &g_aw9620x_node_reg_load_fw_bin, + .p_get_chip_info = &g_aw9620x_get_chip_info, + .p_aw_sar_awrw = NULL, + .p_boot_bin = &g_aw9620x_load_boot_bin, +}; + +int32_t aw9620x_init(struct aw_sar *p_sar) +{ + if (p_sar == NULL) { + AWLOGE(p_sar->dev, "para is NULL, error!"); + return -AW_ERR; + } + + //Chip private function operation + p_sar->p_sar_para = &g_aw9620x_chip_config; + + return AW_OK; +} + +void aw9620x_deinit(struct aw_sar *p_sar) +{ + if (p_sar->priv_data != NULL) { + devm_kfree(p_sar->dev, p_sar->priv_data); + } +} diff --git a/drivers/misc/awinic/sarsensor/aw9620x/aw_update_fw.c b/drivers/misc/awinic/sarsensor/aw9620x/aw_update_fw.c new file mode 100644 index 000000000000..a50ea2c4bd15 --- /dev/null +++ b/drivers/misc/awinic/sarsensor/aw9620x/aw_update_fw.c @@ -0,0 +1,1557 @@ +#include +#include +/*********************protocol update frimware start****************** *****/ +//#define AW_FW_INFO_LOG + +//If it is a bin file, the size side conversion is required +#define AW_SAR_BIN_TR + +static uint8_t g_prot_update_fw_flag = 0; + +static int32_t aw_check_isp_go_reg(AW_SAR_I2C_TYPE i2c) +{ + int32_t delay_cnt = 100; + uint32_t r_isp_go_reg = 0; + int32_t ret = 0; + + do { + ret = aw_sar_i2c_read(i2c, REG_ISPGO, &r_isp_go_reg); + if (ret != AW_FW_OK) { + AWLOGE_FW("read REG_ISPGO"); + return ret; + } + if (r_isp_go_reg == 0) { + break; + } + aw_sar_delay_ms(1); + } while (delay_cnt--); + + if (delay_cnt < 0) { + AWLOGE_FW("check_isp_go_reg err!"); + return -AW_FW_ERR; + } + + return AW_FW_OK; +} + +static int32_t aw_reg_read_val(AW_SAR_I2C_TYPE i2c, uint32_t *p_read_data, uint16_t start_addr) +{ + int32_t ret = 0; + + //3.config ISPADR reg + ret = aw_sar_i2c_write(i2c, REG_ISPADR, start_addr); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_ISPADR"); + return -AW_FW_ERR; + } + //4.config ISP_CMD reg + ret = aw_sar_i2c_write(i2c, REG_ISPCMD, REG_ISP_CMD_MAIN_ARR); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_ISPCMD"); + return -AW_FW_ERR; + } + //5.config ISP_GO reg + ret = aw_sar_i2c_write(i2c, REG_ISPGO, REG_SET_ISP_GO); + if (ret != AW_FW_OK) { + AWLOGE_FW("write ISP_CMD "); + return -AW_FW_ERR; + } + //6.check isp_go reg + ret = aw_check_isp_go_reg(i2c); + if (ret != AW_FW_OK) { + AWLOGE_FW("check_isp_go_reg"); + return -AW_FW_ERR; + } + //7 read data + ret = aw_sar_i2c_read(i2c, REG_ISPRDATA, p_read_data); + if (ret != AW_FW_OK) { + AWLOGE_FW("read REG_ISPRDATA p_read_data = 0x%08x", *p_read_data); + return -AW_FW_ERR; + } + + return AW_FW_OK; +} + +static uint32_t get_pack_checksum(uint8_t *data, uint16_t length, + uint8_t module, uint8_t command) +{ + uint32_t i = 0; + uint32_t check_sum = 0; + + check_sum = module + command + length; + for (i = 0; i < length; i += 4) { + check_sum += AW_SAR_GET_32_DATA(data[i + 0], data[i + 1], + data[i + 2], data[i + 3]); + } + + return (~check_sum + 1); +} + +static int32_t dri_to_soc_pack_send(AW_SAR_I2C_TYPE i2c, uint8_t module, uint8_t command, + uint16_t length, uint8_t *data) +{ + int8_t cnt = AW_RETRIES; +#ifdef AW_FW_INFO_LOG + uint32_t i = 0; +#endif + int32_t ret = -1; + uint32_t checksum = 0; + uint8_t *prot_pack_w = (uint8_t *)malloc(AW_PACK_FIXED_SIZE + length + SEND_ADDR_LEN); + if (prot_pack_w == NULL) { + AWLOGE_FW("malloc application failed!"); + return -1; + } + + prot_pack_w[0] = ((uint16_t)PROT_SEND_ADDR & GET_BITS_15_8) >> OFFSET_BIT_8; + prot_pack_w[1] = (uint16_t)PROT_SEND_ADDR & GET_BITS_7_0; + + //header + prot_pack_w[2] = ((uint16_t)AW_HEADER_VAL & GET_BITS_15_8) >> OFFSET_BIT_8; + prot_pack_w[3] = (uint16_t)AW_HEADER_VAL & GET_BITS_7_0; + + //size + prot_pack_w[4] = ((uint16_t)(AW_PACK_FIXED_SIZE + length) & GET_BITS_15_8) >> OFFSET_BIT_8; + prot_pack_w[5] = (uint16_t)(AW_PACK_FIXED_SIZE + length) & GET_BITS_7_0; + + //checksum + checksum = get_pack_checksum(data, length, module, command); + prot_pack_w[6] = ((uint32_t)checksum & GET_BITS_31_25) >> OFFSET_BIT_24; + prot_pack_w[7] = ((uint32_t)checksum & GET_BITS_24_16) >> OFFSET_BIT_16; + prot_pack_w[8] = ((uint32_t)checksum & GET_BITS_15_8) >> OFFSET_BIT_8; + prot_pack_w[9] = (uint32_t)checksum & GET_BITS_7_0; + + //module + prot_pack_w[10] = module; + + //command + prot_pack_w[11] = command; + + //length + prot_pack_w[12] = ((uint16_t)length & 0xff00) >> OFFSET_BIT_8; + prot_pack_w[13] = (uint16_t)length & 0x00ff; + + if (length != 0 && data != NULL) { + memcpy(prot_pack_w + AW_PACK_FIXED_SIZE + SEND_ADDR_LEN, data, length); + } + +#ifdef AW_FW_INFO_LOG + //Protocol sending content + AWLOGD_FW("Protocol sending content PROT_SEND_ADDR:0x%x", PROT_SEND_ADDR); + for (i = 2; i < AW_PACK_FIXED_SIZE + length + AW_ADDR_SIZE; i += 4) { + AWLOGD_FW("i = %d, 0x%02x 0x%02x 0x%02x 0x%02x", + i, prot_pack_w[i + 0], prot_pack_w[i + 1], + prot_pack_w[i + 2], prot_pack_w[i + 3]); + } +#endif + + do { + ret = aw_sar_i2c_write_seq(i2c, prot_pack_w, AW_PACK_FIXED_SIZE + AW_ADDR_SIZE + length); + if (ret < 0) { + AWLOGE_FW("aw_sar_i2c_write_seq err cnt = %d ret = %d", cnt, ret); + } else { + break; + } + } while(cnt--); + + free(prot_pack_w); + + if (cnt < 0) { + AWLOGE_FW("aw_sar_i2c_write_seq err! ret = %d", ret); + return -AW_FW_ERR; + } + + return AW_FW_OK; +} + +static int32_t soc_to_dri_pack_recv(AW_SAR_I2C_TYPE i2c, struct aw_soc_protocol *prot_pack, + uint32_t pack_len, uint8_t *addr) +{ + int8_t cnt = AW_RETRIES; + int32_t ret = -1; + + if (prot_pack == NULL || pack_len == 0) { + return -1; + } + + do { + ret = aw_sar_i2c_read_seq(i2c, addr, 2, (uint8_t *)prot_pack, pack_len); + if (ret < 0) { + AWLOGE_FW("aw_sar_i2c_read_seq cnt = %d ret = %d", cnt, ret); + } else { + break; + } + } while(cnt--); + + if (cnt < 0) { + AWLOGE_FW("aw_sar_i2c_read_seq! ret = %d", ret); + return -AW_FW_ERR; + } + + return AW_FW_OK; +} + +/** + * @brief flash init + * @param parse pack value + * @retval err code + */ +#define AW_SAR_FLIP_U16(value) ((((value) & (0x00FF)) << (8)) | (((value) & (0xFF00)) >> (8))) +#define AW_SAR_FLIP_U32(value) ((((value) & (0x000000FF)) << (24)) | ((((value) & (0x0000FF00))) << (8)) | (((value) & (0x00FF0000)) >> (8)) | (((value) & (0xFF000000)) >> (24))) +static int32_t soc_to_dri_pack_parse(AW_SAR_I2C_TYPE i2c, uint32_t length, + uint8_t module, uint8_t command) +{ + int32_t ret = -1; + uint32_t pack_len = AW_PACK_FIXED_SIZE + length; + uint8_t ack_addr[2] = { 0 }; + uint32_t cmd_status = 0; + + struct aw_soc_protocol *prot_pack_r = (struct aw_soc_protocol *)malloc(pack_len); + if (prot_pack_r == NULL) { + AWLOGE_FW("malloc application failed!"); + return -AW_FW_ERR; + } + + ack_addr[0] = (uint8_t)(AW_ACK_ADDR >> OFFSET_BIT_8); + ack_addr[1] = (uint8_t)AW_ACK_ADDR; + + ret = soc_to_dri_pack_recv(i2c, prot_pack_r, pack_len, ack_addr); + if (ret != AW_FW_OK) { + AWLOGE_FW("soc_to_dri_pack_recv failed!"); + goto err_pack_parse; + } + + prot_pack_r->header = AW_SAR_FLIP_U16(prot_pack_r->header); + prot_pack_r->size = AW_SAR_FLIP_U16(prot_pack_r->size); + prot_pack_r->length = AW_SAR_FLIP_U16(prot_pack_r->length); + prot_pack_r->checksum = AW_SAR_FLIP_U32(prot_pack_r->checksum); + + cmd_status = AW_SAR_GET_32_DATA(prot_pack_r->value[3], prot_pack_r->value[2], + prot_pack_r->value[1], prot_pack_r->value[0]); + + //parse soc to dri pack +/* AWLOGD_FW("header= 0x%x, size= 0x%x, length= 0x%x, checksum= 0x%x,", + prot_pack_r->header, prot_pack_r->size, + prot_pack_r->length, prot_pack_r->checksum); + AWLOGD_FW("module= 0x%x, command= 0x%x, length=0x%x, cmd_status = %d", + prot_pack_r->module, prot_pack_r->command, + prot_pack_r->length, cmd_status);*/ + + if ((module == prot_pack_r->module) && (command == prot_pack_r->command) && (cmd_status == 0)) { + } else { + AWLOGE_FW("soc to dri send pack parse failed!"); + return -AW_FW_ERR; + } + + free(prot_pack_r); + + return AW_FW_OK; + +err_pack_parse: + free(prot_pack_r); + return -AW_FW_ERR; +} + +static uint32_t aw_get_bin_checksum(const uint8_t *w_bin_offset, + uint32_t update_data_len, uint32_t check_len) +{ + uint32_t i = 0; + uint32_t check_sum = 0; + uint32_t tmp = 0; + uint32_t index = 0; + + AWLOGD_FW("update_data_len:0x%x, check_len:0x%x", update_data_len, check_len); + + for (i = 0; i < check_len; i += WORD_LEN) { + if (i < update_data_len) { +#ifdef AW_SAR_BIN_TR + tmp = AW_SAR_GET_32_DATA(w_bin_offset[index + 3], + w_bin_offset[index + 2], + w_bin_offset[index + 1], + w_bin_offset[index + 0]); +#else + tmp = AW_SAR_GET_32_DATA(w_bin_offset[index + 0], + w_bin_offset[index + 1], + w_bin_offset[index + 2], + w_bin_offset[index + 3]); +#endif + index += WORD_LEN; + } else { + tmp = AW_FLASH_DEFAULT_VAL; + } + check_sum += tmp; + } + check_sum = ~check_sum + 1; + + return check_sum; +} + +static int32_t aw_read_ack_irq(AW_SAR_I2C_TYPE i2c) +{ + uint32_t irq_stat = 0; + int32_t cnt = AW_WAIT_IRQ_CYCLES; + int32_t ret = 0; + + if (g_prot_update_fw_flag == SEND_UPDATE_FW_CMD) { + cnt = AW_PROT_STOP_WAIT_IRQ_CYCLES; + } + + do { + ret = aw_sar_i2c_read(i2c, REG_IRQSRC, &irq_stat); + if (ret != AW_FW_OK) { + AWLOGE_FW("read REG_IRQSRC fail"); + return -AW_FW_ERR; + } + if (((irq_stat >> 29) & 0x01) == 1) { + break; + } else { + //AWLOGE_FW("REG_IRQSRC val: 0x%x cnt: %d", irq_stat, cnt); + } + aw_sar_delay_ms(1); + } while (cnt--); + + if (cnt == -1) { + AWLOGE_FW("read irqsrc failed!, REG_IRQSRC val: 0x%x", irq_stat); + return -AW_FW_ERR; + } + + return AW_FW_OK; +} + +static int32_t aw_read_init_comp_irq(AW_SAR_I2C_TYPE i2c) +{ + uint32_t irq_stat = 0; + int32_t cnt = 10; + int32_t ret = 0; + + do { + ret = aw_sar_i2c_read(i2c, REG_IRQSRC, &irq_stat); + if (ret != AW_FW_OK) { + AWLOGD_FW("read REG_HOSTIRQSRC err"); + return -AW_FW_ERR; + } + + if (irq_stat == INIT_OVER_IRQ_OK) { + AWLOGD_FW("read init over irq ok success!,stop cmd success!"); + break; + } else { + AWLOGE_FW("stop cmd failed, REG_IRQSRC val: 0x%x cnt: %d", irq_stat, cnt); + } + + aw_sar_delay_ms(1); + } while (cnt--); + + if (cnt == -1) { + AWLOGD_FW("stop cmd failed!"); + return -AW_FW_ERR; + } + + return AW_FW_OK; +} + +static int32_t aw_send_once_cmd(AW_SAR_I2C_TYPE i2c, uint8_t module, uint8_t command, + uint8_t* send_value, uint16_t send_val_len) +{ + int32_t ret = -AW_FW_ERR; + uint8_t recv_len = 0; + uint32_t delay_ms_cnt = 0; + + //1.send cmd + ret = dri_to_soc_pack_send(i2c, module, command, + send_val_len, send_value); + if (ret != AW_FW_OK) { + AWLOGE_FW("UPDATE_START_CMD err"); + return -AW_FW_ERR; + } + + ret = aw_sar_i2c_write(i2c, AW_BT_PROT_CMD_PACK_ADDR, AW_SRAM_FIRST_DETECT); + if (ret != AW_FW_OK) { + AWLOGE_FW("REG_CMD_SEND_TIRG err"); + return -AW_FW_ERR; + } + + //2.send trig + ret = aw_sar_i2c_write(i2c, REG_CMD, REG_H2C_TRIG_PARSE_CMD); + if (ret != AW_FW_OK) { + AWLOGE_FW("REG_CMD_SEND_TIRG err"); + return -AW_FW_ERR; + } + + switch (g_prot_update_fw_flag) { + case SEND_START_CMD: + recv_len = SEND_START_CMD_RECV_LEN; + delay_ms_cnt = SEND_START_CMD_DELAY_MS; + break; + case SEND_ERASE_SECTOR_CMD: + recv_len = SEND_ERASE_CHIP_CMD_RECV_LEN; + delay_ms_cnt = SEND_ERASE_SECTOR_CMD_DELAY_MS; + break; + case SEND_UPDATE_FW_CMD: + recv_len = SEND_UPDATE_FW_CMD_RECV_LEN; + delay_ms_cnt = 0; + break; + case SEND_UPDATE_CHECK_CODE_CMD: + recv_len = SEND_UPDATE_CHECK_CODE_CMD_RECV_LEN; + delay_ms_cnt = SEND_UPDATE_CHECK_CODE_CMD_DELAY_MS; + break; + case SEND_RESTORE_CMD: + recv_len = SEND_RESTORE_CMD_RECV_LEN; + delay_ms_cnt = SEND_RESTORE_CMD_DELAY_MS; + break; + default: + recv_len = 0; + delay_ms_cnt = 0; + break; + } + + aw_sar_delay_ms(delay_ms_cnt); + + //3.Read interrupt information, wait 100ms + ret = aw_read_ack_irq(i2c); + if (ret != AW_FW_OK) { + AWLOGE_FW("read_ack_irq err"); + return -AW_FW_ERR; + } + + //4.read start ack and pare pack + ret = soc_to_dri_pack_parse(i2c, recv_len, module, command); + if (ret != AW_FW_OK) { + AWLOGE_FW("soc_to_dri_pack_parse err"); + return -AW_FW_ERR; + } + + return AW_FW_OK; +} + +static int32_t aw_send_stop_cmd(AW_SAR_I2C_TYPE i2c) +{ + int32_t ret = -AW_FW_ERR; + + AWLOGD_FW("enter"); + + aw_sar_i2c_write(i2c, AW_REG_MCFG, AW_CPU_HALT); + aw_sar_i2c_write(i2c, AW_REG_ACESS_EN, AW_ACC_PERI); + aw_sar_i2c_write_bits(i2c, REG_UPDATA_DIS, AW_REG_UPDATA_DIS_MASK, 0); + aw_sar_i2c_write(i2c, REG_ACCESSEN, REG_ACCESSEN_CLOSE); + aw_sar_i2c_write(i2c, AW_REG_MCFG, AW_CPU_RUN); + + aw_sar_delay_ms(SEND_STOP_CMD_DELAY_MS); + + //Read interrupt information, wait 10ms + ret = aw_read_init_comp_irq(i2c); + if (ret != AW_FW_OK) { + AWLOGE_FW("stop read_ack_irq err"); + return -AW_FW_ERR; + } + + return AW_FW_OK; +} + +static int32_t aw_reg_read_flash_val(AW_SAR_I2C_TYPE i2c, uint32_t *read_data, uint16_t start_addr) +{ + int32_t ret = 0; + + //1.config FMC reg + ret = aw_sar_i2c_write(i2c, REG_ACCESSEN, REG_OPEN_APB_ACCESS_EN); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_ACCESSEN failed!"); + return -AW_FW_ERR; + } + //2.config PMU_CFG reg + ret = aw_sar_i2c_write(i2c, REG_PMU_CFG, REG_ENSET_PMU_CFG); + if (ret != AW_FW_OK) { + AWLOGE_FW("config PMU_CFG failed"); + return -AW_FW_ERR; + } + + ret = aw_reg_read_val(i2c, read_data, start_addr); + if (ret != AW_FW_OK) { + AWLOGD_FW("read addr:0x%x failed", start_addr); + return -AW_FW_ERR; + } + + return AW_FW_OK; +} + + +#ifdef AW_SAR_BIN_TR +static void aw_swap(uint8_t *a, uint8_t *b) +{ + uint8_t tmp = 0; + + tmp = *a; + *a = *b; + *b = tmp; +} + +static void aw_bin_conver(uint8_t *data, uint32_t len) +{ + uint32_t i = 0; + + for (i = 0; i < len; i += WORD_LEN) { + aw_swap(&(data[i + 0]), &(data[i + 3])); + aw_swap(&(data[i + 1]), &(data[i + 2])); + } +} +#endif + +static int32_t aw_cycle_write_firmware(AW_SAR_I2C_TYPE i2c, + const uint8_t *fw_data, uint32_t firmware_len, + uint32_t flash_addr, uint32_t single_wr_len) +{ + uint8_t value_head_len = TRANSFER_SEQ_LEN + TRANSFER_DTS_ADDR_LEN; + uint32_t seq = 1; + int32_t ret = -AW_FW_ERR; + uint32_t i = 0; + uint32_t start_addr = 0; + uint32_t word_comp_len = 0; + uint32_t cycle_cnt = 0; + uint32_t cycle_cnt_last_len = 0; + uint8_t *firmware_info = NULL; + + AWLOGD_FW("enter single_wr_len = %d", single_wr_len); + + //If firmware len is not a multiple of 4, + //let it be equal to the multiple of 4 over there, + //and the less part is supplemented by 1 + if (firmware_len % WORD_LEN != 0) { + word_comp_len = WORD_LEN - firmware_len % WORD_LEN; + AWLOGE_FW("word_comp_len = %d", word_comp_len); + } + + firmware_len = firmware_len + word_comp_len; + cycle_cnt = firmware_len / single_wr_len; + cycle_cnt_last_len = firmware_len % single_wr_len; + + for (i = 0; i < cycle_cnt; i++) { + firmware_info = (uint8_t *)malloc(single_wr_len + value_head_len); + if (firmware_info == NULL) { + AWLOGE_FW("malloc application failed"); + return -AW_FW_ERR; + } + + //Insufficient word makes up for 0xff + memset(firmware_info, 1, single_wr_len + value_head_len); + + firmware_info[0] = (uint8_t)(seq >> OFFSET_BIT_24); + firmware_info[1] = (uint8_t)(seq >> OFFSET_BIT_16); + firmware_info[2] = (uint8_t)(seq >> OFFSET_BIT_8); + firmware_info[3] = (uint8_t)(seq); + + firmware_info[4] = (uint8_t)(flash_addr >> OFFSET_BIT_24); + firmware_info[5] = (uint8_t)(flash_addr >> OFFSET_BIT_16); + firmware_info[6] = (uint8_t)(flash_addr >> OFFSET_BIT_8); + firmware_info[7] = (uint8_t)(flash_addr); + + AWLOGD_FW("cnt = %d firmware addr written to flash 0x%02x 0x%02x 0x%02x 0x%02x", + i, firmware_info[4], firmware_info[5], + firmware_info[6], firmware_info[7]); + + memcpy(firmware_info + value_head_len, + &(fw_data[start_addr + single_wr_len * i]), single_wr_len); +#ifdef AW_SAR_BIN_TR + aw_bin_conver(firmware_info + value_head_len, single_wr_len); +#endif + ret = aw_send_once_cmd(i2c, UPDATE_MODULE, UPDATE_TRANSFER_CMD, + firmware_info, single_wr_len + value_head_len); + if (ret != AW_FW_OK) { + AWLOGE_FW("aw_send_once_cmd failed"); + goto err_hand; + } + + flash_addr += single_wr_len; + seq++; + + if (firmware_info != NULL) { + free(firmware_info); + firmware_info = NULL; + } + } + + if (cycle_cnt_last_len != 0) { + firmware_info = (uint8_t *)malloc(cycle_cnt_last_len + value_head_len); + if (firmware_info == NULL) { + AWLOGE_FW("malloc application failed"); + goto err_hand; + } + //Insufficient word makes up for 0xff + memset(firmware_info, 1, cycle_cnt_last_len + value_head_len); + + firmware_info[0] = (uint8_t)(seq >> OFFSET_BIT_24); + firmware_info[1] = (uint8_t)(seq >> OFFSET_BIT_16); + firmware_info[2] = (uint8_t)(seq >> OFFSET_BIT_8); + firmware_info[3] = (uint8_t)(seq); + firmware_info[4] = (uint8_t)(flash_addr >> OFFSET_BIT_24); + firmware_info[5] = (uint8_t)(flash_addr >> OFFSET_BIT_16); + firmware_info[6] = (uint8_t)(flash_addr >> OFFSET_BIT_8); + firmware_info[7] = (uint8_t)(flash_addr); + + AWLOGD_FW("last transfer, len = %d ,firmware addr written to flash 0x%02x 0x%02x 0x%02x 0x%02x", + cycle_cnt_last_len, firmware_info[4], firmware_info[5], + firmware_info[6], firmware_info[7]); + + memcpy(firmware_info + value_head_len, + &(fw_data[start_addr + cycle_cnt * single_wr_len]), + cycle_cnt_last_len); +#ifdef AW_SAR_BIN_TR + + aw_bin_conver(firmware_info + value_head_len, cycle_cnt_last_len); +#endif + + ret = aw_send_once_cmd(i2c, UPDATE_MODULE, UPDATE_TRANSFER_CMD, + firmware_info, cycle_cnt_last_len + value_head_len); + if (ret != AW_FW_OK) { + AWLOGD_FW("aw_send_once_cmd fail"); + goto err_hand; + } + if (firmware_info != NULL) { + free(firmware_info); + firmware_info = NULL; + } + } + + return AW_FW_OK; +err_hand: + if (firmware_info != NULL) { + free(firmware_info); + firmware_info = NULL; + } + return -AW_FW_ERR; +} + +static int32_t aw_write_firmware_checksum(AW_SAR_I2C_TYPE i2c, uint8_t *p_checksum, + uint32_t fw_check_en_addr, + uint32_t check_en_val, + uint32_t fw_check_code_addr) +{ + uint8_t i = 0; + uint8_t j = 0; + uint8_t transfer_value_w[AW_EN_TR_CHECK_VALUE_LEN] = { 0 }; + int32_t ret = 0; + uint32_t r_check_en = 0; + uint32_t r_checksum = 0; + uint32_t w_checksum = 0; + + for (i = 0; i < 4; i++) { + transfer_value_w[i] = (uint8_t)(0 >> ((3 - i) * 8)); + } + + for (i = 4; i < 8; i++) { + transfer_value_w[i] = (uint8_t)(fw_check_en_addr >> ((3 - j) * 8)); + j++; + } + + j = 0; + for (i = 8; i < 12; i++) { + transfer_value_w[i] = (uint8_t)(check_en_val >> ((3 - j) * 8)); + j++; + } + + j = 0; + for (i = 12; i < 16; i++) { + transfer_value_w[i] = p_checksum[j]; + j++; + } + + AWLOGD_FW("fw_check_en_addr:0x%8x, check_en_val:0x%8x", fw_check_en_addr, check_en_val); + + ret = aw_send_once_cmd(i2c, UPDATE_MODULE, UPDATE_TRANSFER_CMD, + transfer_value_w, AW_EN_TR_CHECK_VALUE_LEN); + if (ret != AW_FW_OK) { + AWLOGD_FW("aw_write_firmware_checksum err"); + return -AW_FW_ERR; + } + ret = aw_reg_read_flash_val(i2c, &r_check_en, (uint16_t)fw_check_en_addr); + if (ret != AW_FW_OK) { + AWLOGD_FW("aw_read check_en err"); + return -AW_FW_ERR; + } + ret = aw_reg_read_flash_val(i2c, &r_checksum, (uint16_t)fw_check_code_addr); + if (ret != AW_FW_OK) { + AWLOGD_FW("aw_read check_en err"); + return -AW_FW_ERR; + } + w_checksum = AW_SAR_GET_32_DATA(p_checksum[0], p_checksum[1], p_checksum[2], p_checksum[3]); + if ((r_check_en == check_en_val) && (r_checksum == w_checksum)) { + return AW_FW_OK; + } else { + AWLOGE_FW("r_check_en:0x%08x, check_en_val:0x%08x, r_checksum:0x%08x, w_checksum:0x%08x", + r_check_en, check_en_val, r_checksum, w_checksum); + return -AW_FW_ERR; + } + + return AW_FW_OK; +} + +static int32_t aw_get_fw_and_bt_info(AW_SAR_I2C_TYPE i2c, + const uint8_t *fw_data, uint32_t fw_len, + uint8_t *p_fw_check_sum, uint32_t check_len) +{ + uint32_t fw_checksum = 0; + int32_t ret = 0; + uint32_t bt_version = 0; + uint32_t bt_date = 0; + uint32_t bt_checksum = 0; + + ret = aw_sar_i2c_read(i2c, AW_BT_VER_INF_VERSION, &bt_version); + if (ret != AW_FW_OK) { + AWLOGE_FW("read AW_BT_VER_INF_VERSION fail!"); + return -AW_FW_ERR; + } + + ret = aw_sar_i2c_read(i2c, AW_BT_VER_INF_DATE, &bt_date); + if (ret != AW_FW_OK) { + AWLOGE_FW("read AW_BT_VER_INF_DATE fail"); + return -AW_FW_ERR; + } + + ret = aw_reg_read_flash_val(i2c, &bt_checksum, AW_BT_CHECK_SUM_ADDR); + if (ret != AW_FW_OK) { + AWLOGE_FW("read bt_checksum fail"); + return -AW_FW_ERR; + } + AWLOGD_FW("boot version:0x%08x, date:0x%08x checksum:0x%08x", bt_version, bt_date, bt_checksum); + + fw_checksum = aw_get_bin_checksum(fw_data, fw_len, check_len); + p_fw_check_sum[0] = (uint8_t)(fw_checksum >> 24); + p_fw_check_sum[1] = (uint8_t)(fw_checksum >> 16); + p_fw_check_sum[2] = (uint8_t)(fw_checksum >> 8); + p_fw_check_sum[3] = (uint8_t)(fw_checksum >> 0); + + AWLOGD_FW("firmware checksum is 0x%08x", fw_checksum); + + return AW_FW_OK; +} + +static int32_t aw_flash_erase_sector(AW_SAR_I2C_TYPE i2c, uint32_t erase_addr, uint32_t erase_sector_cnt, uint32_t sector_size) +{ + uint8_t i = 0; + uint8_t addr_buf[4] = { 0 }; + int32_t ret = 0; + + for (i = 0; i < erase_sector_cnt; i++) { + addr_buf[0] = (uint8_t)(erase_addr >> OFFSET_BIT_24); + addr_buf[1] = (uint8_t)(erase_addr >> OFFSET_BIT_16); + addr_buf[2] = (uint8_t)(erase_addr >> OFFSET_BIT_8); + addr_buf[3] = (uint8_t)(erase_addr); + g_prot_update_fw_flag = SEND_ERASE_SECTOR_CMD; + ret = aw_send_once_cmd(i2c, FLASH_MODULE, FLASH_ERASE_SECTOR_CMD, + addr_buf, sizeof(addr_buf)); + if (ret != AW_FW_OK) { + AWLOGE_FW("send erase sector once cmd i = %d", i); + break; + } + erase_addr += sector_size; + } + AWLOGD_FW("erase sector success!"); + + return ret; +} + +static int32_t aw_flash_erase_last_sector(AW_SAR_I2C_TYPE i2c, uint32_t erase_addr) +{ + uint8_t addr_buf[4] = { 0 }; + int32_t ret = 0; + + addr_buf[0] = (uint8_t)(erase_addr >> OFFSET_BIT_24); + addr_buf[1] = (uint8_t)(erase_addr >> OFFSET_BIT_16); + addr_buf[2] = (uint8_t)(erase_addr >> OFFSET_BIT_8); + addr_buf[3] = (uint8_t)(erase_addr); + g_prot_update_fw_flag = SEND_ERASE_SECTOR_CMD; + ret = aw_send_once_cmd(i2c, FLASH_MODULE, FLASH_ERASE_SECTOR_CMD, + addr_buf, sizeof(addr_buf)); + if (ret != AW_FW_OK) { + AWLOGE_FW("flash_erase_last_sector fail"); + return -AW_FW_ERR; + } + AWLOGD_FW("erase last sector addr = 0x%x", erase_addr); + + return AW_FW_OK; +} + +static int32_t aw_send_online_cmd(AW_SAR_I2C_TYPE i2c) +{ + int32_t ret = 0; + int32_t cnt = 200; + uint32_t irq_stat = 0; + + ret = aw_sar_i2c_write(i2c, AW_BT_HOST2CPU_TRIG, AW_BT_HOST2CPU_TRIG_ONLINE_UPGRADE_CMD); + if (ret != AW_FW_OK) { + AWLOGE_FW("write AW_BT_HOST2CPU_TRIG failed"); + return -AW_FW_ERR; + } + + aw_sar_delay_ms(1); + + do { + ret = aw_sar_i2c_read(i2c, REG_IRQSRC, &irq_stat); + if (ret != AW_FW_OK) { + AWLOGE_FW("read REG_IRQSRC fail"); + return -AW_FW_ERR; + } + if (((irq_stat >> 29) & 0x01) == 1) { + break; + } + aw_sar_delay_ms(1); + } while (cnt--); + + if (cnt == -1) { + AWLOGE_FW("read irqsrc failed!, REG_IRQSRC val: 0x%x", irq_stat); + return -AW_FW_ERR; + } + + return AW_FW_OK; +} + +static int32_t aw_send_all_update_cmd(AW_SAR_I2C_TYPE i2c, struct aw_update_fw *update_fw) +{ + int8_t update_flag = AW_TRUE; + int32_t ret = -AW_FW_ERR; + uint32_t data_tmp = 0; + uint8_t fw_check_sum[4] = { 0 }; + uint32_t reg_boot_loader_active_val = 0; + const uint8_t *fw_data = update_fw->fw_data_info->data_start_offset; + uint32_t fw_data_len = update_fw->fw_data_info->update_data_len; + const struct aw_flash_update_info *flash_update_info = update_fw->fw_info->flash_info; + uint8_t direct_update_flag = update_fw->update_para->direct_update_flag; + uint32_t fw_check_len = flash_update_info->check_len; + + do { + //1.Send online upgrade command + AWLOGD_FW("1.Send online upgrade command"); + ret = aw_send_online_cmd(i2c); + if (ret != AW_FW_OK) { + AWLOGE_FW("1.Send online upgrade command failed!"); + update_flag = AW_FALSE; + break; + } + + ret = aw_get_fw_and_bt_info(i2c, fw_data, fw_data_len, fw_check_sum, fw_check_len); + if (ret != AW_FW_OK) { + AWLOGE_FW("aw_get_fw_and_bt_info failed!"); + update_flag = AW_FALSE; + break; + } + + //2.send start cmd + AWLOGD_FW("2.send start cmd"); + g_prot_update_fw_flag = SEND_START_CMD; + ret = aw_send_once_cmd(i2c, UPDATE_MODULE, UPDATE_START_CMD, + P_AW_START_CMD_SEND_VALUE, AW_START_CMD_SEND_VALUE_LEN); + if (ret != AW_FW_OK) { + AWLOGE_FW("send start cmd failed!"); + update_flag = AW_FALSE; + break; + } + + //3.a en fw check erase_last_sector" + AWLOGD_FW("3.a en fw check erase_last_sector"); + ret = aw_flash_erase_last_sector(i2c, flash_update_info->erase_start_addr + + (flash_update_info->erase_sector_cnt - 1) * flash_update_info->sector_size); + if (ret != AW_FW_OK) { + AWLOGD_FW("send erase_last_sector failed!"); + update_flag = AW_FALSE; + break; + } + + //3.b en fw check + AWLOGD_FW("3.b en fw check"); + g_prot_update_fw_flag = SEND_UPDATE_CHECK_CODE_CMD; + ret = aw_write_firmware_checksum(i2c, fw_check_sum, flash_update_info->check_en_addr, + flash_update_info->check_en_val, + flash_update_info->check_code_addr); + if (ret != AW_FW_OK) { + AWLOGD_FW("aw_write_firmware_checksum err"); + update_flag = AW_FALSE; + break; + } + + //4.send Erase flash firmware area cmd, except for the last sector + AWLOGD_FW("4.send Erase Chip Cmd"); + ret = aw_flash_erase_sector(i2c, flash_update_info->erase_start_addr, + (flash_update_info->erase_sector_cnt - 1), + flash_update_info->sector_size); + if (ret != AW_FW_OK) { + AWLOGD_FW("send Erase flash firmware area cmd failed!"); + update_flag = AW_FALSE; + break; + } + //5.Cycle write firmware + AWLOGD_FW("5. Cycle write firmware"); + g_prot_update_fw_flag = SEND_UPDATE_FW_CMD; + ret = aw_cycle_write_firmware(i2c, fw_data, fw_data_len, flash_update_info->tr_start_addr, + update_fw->fw_info->single_wr_len); + if (ret != AW_FW_OK) { + AWLOGD_FW("Cycle write firmware"); + update_flag = AW_FALSE; + break; + } + //6.send stop cmd + AWLOGD_FW("6.send stop cmd"); + g_prot_update_fw_flag = SEND_STOP_CMD; + ret = aw_send_stop_cmd(i2c); + if (ret != AW_FW_OK) { + AWLOGD_FW("stop cmd failed, protocol update firmware failed"); + update_flag = AW_FALSE; + break; + } else { + AWLOGI_FW("protocol update firmware succeeded!"); + } + } while(0); + + if (update_flag == AW_FALSE) { + AWLOGD_FW("protocol update firmware failed, recovery reg"); + ret = aw_sar_i2c_write(i2c, REG_ACCESSEN, REG_ACCESSEN_OPEN); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_ACCESSEN failed!"); + return -AW_FW_ERR; + } + + ret = aw_sar_i2c_read(i2c, REG_BOOT_LOADER_ACTIVE, ®_boot_loader_active_val); + if (ret != AW_FW_OK) { + AWLOGE_FW("read REG_BOOT_LOADER_ACTIVE failed!"); + return -AW_FW_ERR; + } + if (reg_boot_loader_active_val != 0) { + ret = aw_sar_i2c_write(i2c, REG_BOOT_LOADER_ACTIVE, 0); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_BOOT_LOADER_ACTIVE failed!"); + return -AW_FW_ERR; + } + } + + ret = aw_sar_i2c_read(i2c, REG_UPDATA_DIS, &data_tmp); + if (ret != AW_FW_OK) { + AWLOGE_FW("read REG_UPDATA_DIS failed!"); + return -AW_FW_ERR; + } + if (((data_tmp >> 24) & 0xff) != 0) { + ret = aw_sar_i2c_write(i2c, REG_UPDATA_DIS, data_tmp & AW_REG_UPDATA_DIS_MASK); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_UPDATA_DIS failed!"); + return -AW_FW_ERR; + } + } + + ret = aw_sar_i2c_write(i2c, REG_ACCESSEN, REG_ACCESSEN_CLOSE); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_ACCESSEN failed!"); + return -AW_FW_ERR; + } + if (direct_update_flag == AW_TRUE) { + AWLOGI_FW("protocol update firmware failed!, exit load"); + return -AW_FW_ERR; + } + AWLOGI_FW("protocol update firmware failed, start reg mode upgrade"); + return -AW_PROT_UPDATE_ERR; + } + + return AW_FW_OK; +} + +static int32_t aw_fw_version_cmp(AW_SAR_I2C_TYPE i2c, int8_t *cmp_val, uint32_t flash_app_version, uint16_t fwver_reg) +{ + uint32_t firmware_version = 0; + int32_t ret = -AW_FW_ERR; + + ret = aw_sar_i2c_read(i2c, fwver_reg, &firmware_version); + if (ret < 0) { + AWLOGD_FW("read fwver_reg failed!"); + return -AW_FW_ERR; + } + AWLOGI_FW("chip_firmware version :0x%08x bin_irmware version :0x%08x!", + firmware_version, flash_app_version); + + if (flash_app_version != firmware_version) { + *cmp_val = BIN_VER_NOT_EQUALS_SOC_VER; + } else { + *cmp_val = BIN_VER_EQUALS_SOC_VER; + } + + return AW_FW_OK; +} + +static int32_t aw_prox_update_para_check(struct aw_update_fw *update_fw) +{ + if ((update_fw == NULL) || + (update_fw->fw_info == NULL) || + (update_fw->update_para == NULL) || + (update_fw->fw_data_info == NULL)) { + AWLOGE_FW("para1 is NULL"); + return -AW_FW_ERR; + } + + if (update_fw->fw_info->flash_info == NULL) { + AWLOGE_FW("para3 is NULL"); + return -AW_FW_ERR; + } + if (update_fw->update_para->aw_update_diff == NULL) { + AWLOGE_FW("para4 is NULL"); + return -AW_FW_ERR; + } + if (update_fw->fw_data_info->data_start_offset == NULL) { + AWLOGE_FW("para5 is NULL"); + return -AW_FW_ERR; + } + + return AW_FW_OK; +} + +int32_t aw_prot_update(AW_SAR_I2C_TYPE i2c, struct aw_update_fw *update_fw) +{ + int8_t cmp_val = 0; + int32_t ret = 0; + + ret = aw_prox_update_para_check(update_fw); + if (ret != AW_FW_OK) { + AWLOGE_FW("para check failed!"); + return -AW_FW_ERR; + } + + if (update_fw->update_para->direct_update_flag == AW_FALSE) { + aw_fw_version_cmp(i2c, &cmp_val, update_fw->fw_data_info->fw_version, + update_fw->update_para->aw_update_diff->fwver_reg_addr); + if (cmp_val == BIN_VER_EQUALS_SOC_VER) { + AWLOGI_FW("Same version number, no update!"); + return AW_FW_OK; + } else { + AWLOGD_FW("firmware Version number is different, update!"); + } + } + + return aw_send_all_update_cmd(i2c, update_fw); +} + +/********aw_reg_mode_update start********/ + +static int32_t aw_close_write_flash_protect(AW_SAR_I2C_TYPE i2c, uint32_t flash_addr_flag) +{ + int32_t ret = 0; + + //Open host read/write FMC protection + ret = aw_sar_i2c_write(i2c, REG_ACCESSEN, REG_OPEN_APB_ACCESS_EN); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_ACCESSEN failed"); + return -AW_FW_ERR; + } + + //Configure PMC_ CFG register + ret = aw_sar_i2c_write(i2c, REG_PMU_CFG, REG_SET_PMU_CFG); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_PMU_CFG failed!"); + return -AW_FW_ERR; + } + + //Turn on flash write protection + if (flash_addr_flag == BOOT_UPDATE) { + ret = aw_sar_i2c_write(i2c, REG_ARRAY2_EW_EN, REG_SET_BTROM_EW_EN); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_ARRAY2_EW_EN failed"); + return -AW_FW_ERR; + } + } + + return AW_FW_OK; +} + +static int32_t aw_reg_write_to_flash_once(AW_SAR_I2C_TYPE i2c, uint16_t addr, uint32_t w_data) +{ + int32_t ret = 0; + + //Write access address + ret = aw_sar_i2c_write(i2c, REG_ISPADR, addr); + if (ret != AW_FW_OK) { + AWLOGE_FW("write 0xff20 err"); + return -AW_FW_ERR; + } + + //Write data + ret = aw_sar_i2c_write(i2c, REG_ISPWDATA, w_data); + if (ret != AW_FW_OK) { + AWLOGE_FW("write 0xff20 err"); + return -AW_FW_ERR; + } + + //Configure ISP_CMD reg + ret = aw_sar_i2c_write(i2c, REG_ISPCMD, REG_ISP_CMD_CONFIG); + if (ret != AW_FW_OK) { + AWLOGE_FW("write 0xff20 err"); + return -AW_FW_ERR; + } + + //Configure ISP_GO reg + ret = aw_sar_i2c_write(i2c, REG_ISPGO, REG_SET_ISP_GO); + if (ret != AW_FW_OK) { + AWLOGE_FW("write 0xff20 err"); + return -AW_FW_ERR; + } + + ret = aw_check_isp_go_reg(i2c); + if (ret != AW_FW_OK) { + AWLOGE_FW("check_isp_go_reg err"); + return -AW_FW_ERR; + } + + return AW_FW_OK; +} + + +static int32_t aw_reg_write_val_to_flash(AW_SAR_I2C_TYPE i2c, uint8_t update_flag, uint16_t addr, uint32_t val) +{ + int32_t ret = 0; + + AWLOGD_FW("enter"); + + ret = aw_close_write_flash_protect(i2c, update_flag); + if (ret != AW_FW_OK) { + AWLOGE_FW("close_write_flash_protect failed!"); + return -AW_FW_ERR; + } + + ret = aw_reg_write_to_flash_once(i2c, addr, val); + if (ret != AW_FW_OK) { + AWLOGE_FW("write aw_reg_write_bin_once failed!"); + return -AW_FW_ERR; + } + + //Configure PMU_ CFG register + ret = aw_sar_i2c_write(i2c, REG_PMU_CFG, REG_ENSET_PMU_CFG); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_PMU_CFG failed!"); + return -AW_FW_ERR; + } + + return AW_FW_OK; +} + +static int32_t aw_rd_or_wi_cmp(AW_SAR_I2C_TYPE i2c, struct aw_update_common *update_info, + const uint8_t *w_bin_offset, uint32_t update_data_len) +{ + int32_t ret = 0; + uint32_t i = 0; + uint32_t r_data = 0; + uint32_t w_data = 0; + uint32_t read_cnt = update_info->update_data_len; + + AWLOGD_FW("enter"); + + //1.config FMC reg + ret = aw_sar_i2c_write(i2c, REG_ACCESSEN, REG_OPEN_APB_ACCESS_EN); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_ACCESSEN failed!"); + return -AW_FW_ERR; + } + //2.config PMU_CFG reg + ret = aw_sar_i2c_write(i2c, REG_PMU_CFG, REG_ENSET_PMU_CFG); + if (ret != AW_FW_OK) { + AWLOGE_FW("config PMU_CFG reg failed"); + return -AW_FW_ERR; + } + + AWLOGE_FW("read_cnt = %d", read_cnt); + for (i = 0; i < read_cnt; i += WORD_LEN) { + ret = aw_reg_read_val(i2c, &r_data, + update_info->flash_info->tr_start_addr + i); + if (ret != AW_FW_OK) { + AWLOGE_FW("reg_read_bin failed"); + return -AW_FW_ERR; + } +#ifdef AW_SAR_BIN_TR + w_data = AW_SAR_GET_32_DATA(w_bin_offset[i + 3], + w_bin_offset[i + 2], + w_bin_offset[i + 1], + w_bin_offset[i + 0]); + +#else + w_data = AW_SAR_GET_32_DATA(w_bin_offset[i + 0], + w_bin_offset[i + 1], + w_bin_offset[i + 2], + w_bin_offset[i + 3]); + +#endif +#ifdef AW_FW_INFO_LOG + AWLOGD_FW("i= %d, addr= 0x%08x, W_DATA= 0x%08x, R_DATA= 0x%08x", + i, update_info->flash_info->tr_start_addr + i, w_data, r_data); +#endif + if (w_data != r_data) { + AWLOGE_FW("read and write check failed! i=%d, w_data=0x%08x, r_data=0x%08x", + i, w_data, r_data); + return -AW_FW_ERR; + } + } + + return AW_FW_OK; +} + +static int32_t aw_reg_write_bin_to_flash(AW_SAR_I2C_TYPE i2c, struct aw_update_common *update_info) +{ + int32_t ret = 0; + uint32_t i = 0; + uint32_t w_data = 0; + uint32_t index = 0; + const uint8_t *p_data = update_info->w_bin_offset; + uint32_t len = update_info->update_data_len; + uint16_t flash_addr = update_info->flash_info->tr_start_addr; + + AWLOGD_FW("enter"); + + ret = aw_close_write_flash_protect(i2c, update_info->update_flag); + if (ret != AW_FW_OK) { + AWLOGE_FW("close_write_flash_protect failed!"); + return -AW_FW_ERR; + + } + for (i = 0; i < len; i += WORD_LEN, index += WORD_LEN) { +#ifdef AW_SAR_BIN_TR + w_data = AW_SAR_GET_32_DATA(p_data[index + 3], p_data[index + 2], + p_data[index + 1], p_data[index + 0]); +#else + w_data = AW_SAR_GET_32_DATA(p_data[index + 0], p_data[index + 1], + p_data[index + 2], p_data[index + 3]); +#endif + ret = aw_reg_write_to_flash_once(i2c, flash_addr + i, w_data); + if (ret != AW_FW_OK) { + AWLOGE_FW("write aw_reg_write_bin_once failed!"); + return -AW_FW_ERR; + } + } + + //Configure PMU_ CFG register + ret = aw_sar_i2c_write(i2c, REG_PMU_CFG, REG_ENSET_PMU_CFG); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_PMU_CFG failed!"); + return -AW_FW_ERR; + } + + return AW_FW_OK; +} + +static int32_t aw_erase_sector(AW_SAR_I2C_TYPE i2c, struct aw_update_common *update_info) +{ + int32_t ret = 0; + uint32_t i = 0; + uint32_t erase_len = update_info->flash_info->check_len; + uint32_t erase_cnt = update_info->flash_info->erase_sector_cnt; + uint32_t erase_addr = update_info->flash_info->erase_start_addr; + + AWLOGD_FW("erase_addr = 0x%08x, erase_len = %d, erase_cnt = %d", erase_addr, erase_len, erase_cnt); + erase_len = erase_len * 1; + + //1.close write protect + AWLOGD_FW("1.open host write FMC protect"); + ret = aw_sar_i2c_write(i2c, REG_ACCESSEN, REG_OPEN_APB_ACCESS_EN); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_ACCESSEN err"); + return ret; + } + + ret = aw_sar_i2c_write(i2c, REG_PMU_CFG, REG_SET_PMU_CFG); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_PMU_CFG err"); + return ret; + } + + if (update_info->update_flag == BOOT_UPDATE) { + ret = aw_sar_i2c_write(i2c, REG_ARRAY2_EW_EN, REG_SET_BTROM_EW_EN); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_ARRAY2_EW_EN failed!"); + return ret; + } + } + + for (i = 0; i < erase_cnt; i++) { + //2.Erase one sector at a time + ret = aw_sar_i2c_write(i2c, REG_ISPADR, erase_addr + i * update_info->flash_info->sector_size); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_ISPADR failed!"); + return ret; + } + + ret = aw_sar_i2c_write(i2c, REG_ISPCMD, REG_ACCESS_MAIN_ARR); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_ISPCMD failed!"); + return ret; + } + + ret = aw_sar_i2c_write(i2c, REG_T_RCV, REG_SET_T_RCV); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_T_RCV failed!"); + return ret; + } + + ret = aw_sar_i2c_write(i2c, REG_ISPGO, REG_SET_ISP_GO); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_ISPGO failed!"); + return ret; + } + + ret = aw_check_isp_go_reg(i2c); + if (ret != AW_FW_OK) { + AWLOGE_FW("check_isp_go_reg failed!"); + return ret; + } + } + ret = aw_sar_i2c_write(i2c, REG_T_RCV, REG_SET_T_RCV_EN); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_T_RCV failed!"); + return ret; + } + + ret = aw_sar_i2c_write(i2c, REG_PMU_CFG, REG_ENSET_PMU_CFG); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_PMU_CFG failed!"); + return ret; + } + + return AW_FW_OK; +} + +static int32_t aw_flash_update(AW_SAR_I2C_TYPE i2c, struct aw_update_common *update_info) +{ + int32_t ret = 0; + uint32_t check_sum = 0; + + AWLOGD_FW("enter read_len = %d", update_info->update_data_len); + + //1.open register access enable + AWLOGE_FW("1.opne register access enable"); + ret = aw_sar_i2c_write(i2c, REG_ACCESSEN, REG_OPEN_REG_ACCESS_EN); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_ACCESSEN failed!"); + return ret; + } + ret = aw_sar_i2c_write(i2c, REG_MCFG, REG_OPEN_MCFG_EN); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_MCFG failed!"); + return ret; + } + ret = aw_sar_i2c_write(i2c, REG_PMU_CFG, REG_SET_PMU_CFG); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_PMU_CFG failed!"); + return ret; + } + if (update_info->update_flag == FIRMWARE_UPDATE) { + ret = aw_sar_i2c_write(i2c, REG_ARRAY2_EW_EN, REG_SET_BTROM_EW_EN); + if (ret != AW_FW_OK) { + AWLOGE_FW("write REG_ARRAY2_EW_EN failed!"); + return ret; + } + } + + //2.Erase sector + AWLOGD_FW("2.Erase sector, flash_erase_start_addr:0x%08x, erase_len:0x%08x", + update_info->flash_info->erase_start_addr, update_info->flash_info->check_len); + ret = aw_erase_sector(i2c, update_info); + if (ret != AW_FW_OK) { + AWLOGE_FW("2. Erase sector failed!"); + return ret; + } + + //3.write flash + AWLOGD_FW("3.write flash"); + ret = aw_reg_write_bin_to_flash(i2c, update_info); + if (ret != AW_FW_OK) { + AWLOGE_FW("3. write flash failed!"); + return ret; + } + + //4.read data check + AWLOGD_FW("4.read data check"); + ret = aw_rd_or_wi_cmp(i2c, update_info, + update_info->w_bin_offset, + update_info->update_data_len); + if (ret != AW_FW_OK) { + AWLOGE_FW("4. read data check failed!"); + return -AW_FW_ERR; + } + + //5.write checksum + AWLOGD_FW("5.write checksum"); + //Turn on verification enable + ret = aw_reg_write_val_to_flash(i2c, update_info->update_flag, + update_info->flash_info->check_en_addr, + update_info->flash_info->check_en_val); + if (ret != AW_FW_OK) { + AWLOGE_FW("5.write check enable failed!"); + return -AW_FW_ERR; + } + + check_sum = aw_get_bin_checksum(update_info->w_bin_offset, + update_info->update_data_len, + update_info->flash_info->check_len); + AWLOGI_FW("check_sum = 0x%x", check_sum); + ret = aw_reg_write_val_to_flash(i2c, update_info->update_flag, + update_info->flash_info->check_code_addr, + check_sum); + if (ret != AW_FW_OK) { + AWLOGE_FW("5.write checksum failed!"); + return -AW_FW_ERR; + } + + //6.open flash protect + AWLOGD_FW("6.open flash protect"); + if (update_info->update_flag == FIRMWARE_UPDATE) { + ret = aw_sar_i2c_write(i2c, REG_ARRAY2_EW_EN, REG_ENSET_BTROM_EW_EN); + if (ret != AW_FW_OK) { + AWLOGE_FW("6.open flash protect failed"); + return ret; + } + } + + AWLOGI_FW("%s reg mode update succeeded!!!", + ((update_info->update_flag == BOOT_UPDATE)? "boot":"firmware")); + + return AW_FW_OK; +} + +int32_t aw_reg_update_para_check(struct aw_update_common *update_info) +{ + if ((update_info == NULL) || (update_info->flash_info == NULL)) { + return -AW_FW_ERR; + } + + if (update_info->w_bin_offset == NULL) { + return -AW_FW_ERR; + } + + return AW_FW_OK; +} + +int32_t aw_reg_update(AW_SAR_I2C_TYPE i2c, struct aw_update_common *update_info) +{ + int32_t ret = 0; + + ret = aw_reg_update_para_check(update_info); + if (ret != AW_FW_OK) { + return -AW_FW_ERR; + } + + return aw_flash_update(i2c, update_info); +} + +#define AW9620X_STAT_BASE_ADDR (0x1A00) +#define AW9620X_REG_FWVER ((0x000C) + AW9620X_STAT_BASE_ADDR) +#define AW9620X_REG_WST ((0x0010) + AW9620X_STAT_BASE_ADDR) + +#define AW_WORD_SZIE (4) + +#define AW9620X_BT_STAET_ADDR (0x10000000) +#define AW9620X_BT_END_ADDR (0x100007F4) +//Check the firmware area except the check code and check enable position +#define AW9620X_BT_CHECK_LEN (AW9620X_BT_END_ADDR - AW9620X_BT_STAET_ADDR + AW_WORD_SZIE) +#define AW9620X_BT_CHECK_EN_ADDR (0x100007F8) +#define AW9620X_BT_CHECK_CODE_ADDR (0x100007FC) +#define AW9620X_BT_CHECK_CODE_VAL (0x20222022) +#define AW9620X_BT_ERASE_START_ADDR (0x10000000) +#define AW9620X_BT_SECTOR_SIZE (128) +#define AW9620X_BT_ERASE_CNT ((AW9620X_BT_CHECK_CODE_ADDR - AW9620X_BT_ERASE_START_ADDR + AW_WORD_SZIE) / AW9620X_BT_SECTOR_SIZE) + +#define AW9620X_FW_START_ADDR (0x10002000) +#define AW9620X_FW_END_ADDR (0x10003FF4) +#define AW9620X_FW_CHECK_EN_ADDR (0x10003FF8) +#define AW9620X_FW_CHECK_CODE_ADDR (0x10003FFC) + +//Check the firmware area except the check code and check enable position +#define AW9620X_FW_CHECK_LEN (AW9620X_FW_END_ADDR - AW9620X_BT_CHECK_CODE_ADDR) + +#define AW9620X_FW_CHECK_CODE_VAL (0x20222022) +#define AW9620X_FW_ERASE_START_ADDR (0x10002000) +#define AW9620X_FW_SECTOR_SIZE (128) +//The number of sectors in the flash FW area is reduced by one +#define AW9620X_FW_ERASE_CNT ((AW9620X_FW_CHECK_CODE_ADDR - AW9620X_FW_ERASE_START_ADDR + AW_WORD_SZIE) / AW9620X_FW_SECTOR_SIZE) +#define AW9620X_CACHE_LEN (0x1000) + +static const struct aw_flash_update_info g_aw_prot_update_fw_info = { + .tr_start_addr = AW9620X_FW_START_ADDR, + .check_en_addr = AW9620X_FW_CHECK_EN_ADDR, + .check_code_addr = AW9620X_FW_CHECK_CODE_ADDR, + .check_en_val = AW9620X_FW_CHECK_CODE_VAL, + .check_len = AW9620X_FW_CHECK_LEN, + .erase_start_addr = AW9620X_FW_ERASE_START_ADDR, + .erase_sector_cnt = AW9620X_FW_ERASE_CNT, + .sector_size = AW9620X_FW_SECTOR_SIZE, +}; + +static const struct aw_update_chip_diff update_chip_diff = { + .fwver_reg_addr = AW9620X_REG_FWVER, + .chip_flag = CHIP_AW9620X, +}; + +int32_t aw9620x_prot_update_fw(AW_SAR_I2C_TYPE i2c, uint8_t direct_update_flag, + const uint8_t *data_start_offset, uint32_t update_data_len ,uint32_t fw_version) +{ + struct aw_update_para update_para = { + .direct_update_flag = direct_update_flag, //Upgrade without comparing version numbers + .aw_update_diff = &update_chip_diff, //Differences in chip updates + }; + //.cache_len = AW9620X_CACHE_LEN, + struct aw_fw_data_info fw_data_info = { + .data_start_offset = data_start_offset, + .update_data_len = update_data_len, + .fw_version = fw_version, + }; + + struct aw_fw_info fw_info = { + //.single_wr_len = AW9620X_CACHE_LEN / 4, + .single_wr_len = AW9620X_CACHE_LEN - 0x40, + .flash_info = &g_aw_prot_update_fw_info, + }; + + struct aw_update_fw update_fw = { + .fw_info = &fw_info, + .update_para = &update_para, + .fw_data_info = &fw_data_info, + }; + + return aw_prot_update(i2c, &update_fw); +} + +static const struct aw_flash_update_info g_aw_reg_fw_flash_info = { + .tr_start_addr = AW9620X_FW_START_ADDR, + .check_len = AW9620X_FW_CHECK_LEN, //37f8 + .check_en_addr = AW9620X_FW_CHECK_EN_ADDR, + .check_code_addr = AW9620X_FW_CHECK_CODE_ADDR, + .check_en_val = AW9620X_FW_CHECK_CODE_VAL, + + .erase_start_addr = AW9620X_FW_ERASE_START_ADDR, + .erase_sector_cnt = AW9620X_FW_ERASE_CNT, + .sector_size = AW9620X_FW_SECTOR_SIZE, + }; + +int32_t aw9620x_reg_update_fw(AW_SAR_I2C_TYPE i2c, + const uint8_t *data_start_offset, uint32_t update_data_len) +{ + struct aw_update_common update_info = { + .update_flag = FIRMWARE_UPDATE, + .w_bin_offset = data_start_offset, + .update_data_len = update_data_len, + .flash_info = &g_aw_reg_fw_flash_info, + }; + + return aw_reg_update(i2c, &update_info); +} + +static const struct aw_flash_update_info g_aw_reg_boot_flash_info = { + .tr_start_addr = AW9620X_BT_STAET_ADDR, + .check_len = AW9620X_BT_CHECK_LEN, //7f8 + .check_en_addr = AW9620X_BT_CHECK_EN_ADDR, + .check_code_addr = AW9620X_BT_CHECK_CODE_ADDR, + .check_en_val = AW9620X_BT_CHECK_CODE_VAL, + + .erase_start_addr = AW9620X_BT_STAET_ADDR, + .erase_sector_cnt = AW9620X_BT_ERASE_CNT, + .sector_size = AW9620X_BT_SECTOR_SIZE, +}; + +int32_t aw9620x_reg_update_boot(AW_SAR_I2C_TYPE i2c, + const uint8_t *data_start_offset, uint32_t update_data_len) +{ + struct aw_update_common update_info = { + .update_flag = BOOT_UPDATE, + .w_bin_offset = data_start_offset, + .update_data_len = update_data_len, + .flash_info = &g_aw_reg_boot_flash_info, + }; + + return aw_reg_update(i2c, &update_info); +} + diff --git a/drivers/misc/awinic/sarsensor/aw963xx/aw963xx.c b/drivers/misc/awinic/sarsensor/aw963xx/aw963xx.c new file mode 100644 index 000000000000..5bedd21128f9 --- /dev/null +++ b/drivers/misc/awinic/sarsensor/aw963xx/aw963xx.c @@ -0,0 +1,1458 @@ +#include "aw963xx.h" +#include "aw_sar.h" + +#define AW963XX_I2C_NAME "aw963xx_sar" +#define AW963XX_DRIVER_VERSION "v0.1.1.14" + +static void aw963xx_set_cs_as_irq(struct aw_sar *p_sar, int flag); +static void aw963xx_get_ref_ch_enable(struct aw_sar *p_sar); + +static int32_t aw963xx_read_init_over_irq(void *load_bin_para) +{ + uint32_t cnt = 1000; + uint32_t reg = 0; + int32_t ret = 0; + struct aw_sar *p_sar = (struct aw_sar *)load_bin_para; + + while (cnt--) { + ret = aw_sar_i2c_read(p_sar->i2c, REG_IRQSRC, ®); + if (ret != 0) { + AWLOGE(p_sar->dev, "i2c error %d", ret); + return ret; + } + if ((reg & 0x01) == 0x01) { + AWLOGI(p_sar->dev, "read init irq success!"); + aw_sar_i2c_read(p_sar->i2c, REG_FWVER, ®); + AWLOGI(p_sar->dev, "firmware version = 0x%08x", reg); + return AW_OK; + } + mdelay(1); + } + + aw_sar_i2c_read(p_sar->i2c, REG_FWVER, ®); + AWLOGI(p_sar->dev, "firmware version = 0x%08x", reg); + + return -AW_ERR; +} + +static void aw963xx_convert_little_endian_2_big_endian(struct aw_bin *aw_bin) +{ + int i = 0; + uint32_t start_index = aw_bin->header_info[0].valid_data_addr; + uint32_t fw_len = aw_bin->header_info[0].reg_num; + uint32_t uints = fw_len / AW963XX_SRAM_UPDATE_ONE_UINT_SIZE; + + for (i = 0; i < uints; i++) { + uint8_t tmp1 = aw_bin->info.data[start_index + i * AW963XX_SRAM_UPDATE_ONE_UINT_SIZE + 3]; + uint8_t tmp2 = aw_bin->info.data[start_index + i * AW963XX_SRAM_UPDATE_ONE_UINT_SIZE + 2]; + uint8_t tmp3 = aw_bin->info.data[start_index + i * AW963XX_SRAM_UPDATE_ONE_UINT_SIZE + 1]; + uint8_t tmp4 = aw_bin->info.data[start_index + i * AW963XX_SRAM_UPDATE_ONE_UINT_SIZE]; + aw_bin->info.data[start_index + i * AW963XX_SRAM_UPDATE_ONE_UINT_SIZE] = tmp1; + aw_bin->info.data[start_index + i * AW963XX_SRAM_UPDATE_ONE_UINT_SIZE + 1] = tmp2; + aw_bin->info.data[start_index + i * AW963XX_SRAM_UPDATE_ONE_UINT_SIZE + 2] = tmp3; + aw_bin->info.data[start_index + i * AW963XX_SRAM_UPDATE_ONE_UINT_SIZE + 3] = tmp4; + } +} + +/** + * @brief |----------------code ram-----------------| + * 0x2000 0x4fff + * |--- app wrote here ---|--fill with 0xff--| + * + * if the size of app is less than the size of code ram, the rest of the + * ram is filled with 0xff. + * @param load_bin_para + * @param offset the rear addr of app + * @return int32_t + */ +static int32_t aw963xx_sram_fill_not_wrote_area(void *load_bin_para, uint32_t offset) +{ + uint8_t buf[AW963XX_SRAM_UPDATE_ONE_PACK_SIZE + 2] = { 0 }; + uint8_t *r_buf = NULL; + int32_t ret = 0; + uint32_t i = 0; + uint32_t download_addr_with_ofst = 0; + uint32_t last_pack_len = (AW963XX_SRAM_END_ADDR - offset) % AW963XX_SRAM_UPDATE_ONE_PACK_SIZE; + uint32_t pack_cnt = last_pack_len == 0 ? + ((AW963XX_SRAM_END_ADDR - offset) / AW963XX_SRAM_UPDATE_ONE_PACK_SIZE) : + ((AW963XX_SRAM_END_ADDR - offset) / AW963XX_SRAM_UPDATE_ONE_PACK_SIZE) + 1; + struct aw_sar *p_sar = (struct aw_sar *)load_bin_para; + + r_buf = (uint8_t *)devm_kzalloc(p_sar->dev, AW963XX_SRAM_UPDATE_ONE_PACK_SIZE, GFP_KERNEL); + if (r_buf == NULL) { + AWLOGE(p_sar->dev, "devm_kzalloc error"); + return -AW_ERR; + } + + AWLOGI(p_sar->dev, "last_pack_len = %d", last_pack_len); + AWLOGI(p_sar->dev, "pack_cnt = %d", pack_cnt); + AWLOGI(p_sar->dev, "offset = 0x%x", offset); + + memset(buf, 0xff, sizeof(buf)); + + for (i = 0; i < pack_cnt; i++) { + memset(r_buf, 0, AW963XX_SRAM_UPDATE_ONE_PACK_SIZE); + download_addr_with_ofst = offset + i * AW963XX_SRAM_UPDATE_ONE_PACK_SIZE; + buf[0] = (uint8_t)(download_addr_with_ofst >> OFFSET_BIT_8); + buf[1] = (uint8_t)(download_addr_with_ofst); + if (i != (pack_cnt - 1)) { + ret = aw_sar_i2c_write_seq(p_sar->i2c, buf, AW963XX_SRAM_UPDATE_ONE_PACK_SIZE + 2); + if (ret != AW_OK) { + AWLOGI(p_sar->dev, "cnt%d, write_seq error!", i); + devm_kfree(p_sar->dev, r_buf); + return ret; + } + ret = aw_sar_i2c_read_seq(p_sar->i2c, buf, 2, r_buf, AW963XX_SRAM_UPDATE_ONE_PACK_SIZE); + if (ret != AW_OK) { + AWLOGI(p_sar->dev, "cnt%d, read_seq error!", i); + devm_kfree(p_sar->dev, r_buf); + return ret; + } + if (memcmp(&buf[2], r_buf, AW963XX_SRAM_UPDATE_ONE_PACK_SIZE) != 0) { + AWLOGE(p_sar->dev, "read is not equal to write "); + devm_kfree(p_sar->dev, r_buf); + return -AW_ERR; + } + } else { + ret = aw_sar_i2c_write_seq(p_sar->i2c, buf, last_pack_len + 2); + if (ret != AW_OK) { + AWLOGI(p_sar->dev, "cnt%d, write_seq error!", i); + devm_kfree(p_sar->dev, r_buf); + return ret; + } + ret = aw_sar_i2c_read_seq(p_sar->i2c, buf, 2, r_buf, last_pack_len); + if (ret != AW_OK) { + AWLOGI(p_sar->dev, "cnt%d, read_seq error!", i); + devm_kfree(p_sar->dev, r_buf); + return ret; + } + if (memcmp(&buf[2], r_buf, last_pack_len) != 0) { + AWLOGE(p_sar->dev, "read is not equal to write "); + devm_kfree(p_sar->dev, r_buf); + return -AW_ERR; + } + } + } + + devm_kfree(p_sar->dev, r_buf); + + return AW_OK; +} + +static int32_t aw963xx_sram_data_write(struct aw_bin *aw_bin, void *load_bin_para) +{ + uint8_t buf[AW963XX_SRAM_UPDATE_ONE_PACK_SIZE + 2] = { 0 }; + uint8_t *r_buf = NULL; + int32_t ret = 0; + uint32_t i = 0; + uint32_t pack_cnt = 0; + uint32_t start_index = aw_bin->header_info[0].valid_data_addr; + uint32_t fw_bin_version = aw_bin->header_info[0].app_version; + uint32_t download_addr = AW963XX_RAM_START_ADDR; + uint32_t fw_len = aw_bin->header_info[0].reg_num; + uint32_t last_pack_len = fw_len % AW963XX_SRAM_UPDATE_ONE_PACK_SIZE; + struct aw_sar *p_sar = (struct aw_sar *)load_bin_para; + uint32_t download_addr_with_ofst = 0; + + r_buf = (uint8_t *)devm_kzalloc(p_sar->dev, AW963XX_SRAM_UPDATE_ONE_PACK_SIZE, GFP_KERNEL); + if (r_buf == NULL) { + AWLOGE(p_sar->dev, "devm_kzalloc error"); + return -AW_ERR; + } + + pack_cnt = ((fw_len % AW963XX_SRAM_UPDATE_ONE_PACK_SIZE) == 0) ? + (fw_len / AW963XX_SRAM_UPDATE_ONE_PACK_SIZE) : + (fw_len / AW963XX_SRAM_UPDATE_ONE_PACK_SIZE) + 1; + + AWLOGI(p_sar->dev, "fw_bin_version = 0x%x", fw_bin_version); + AWLOGI(p_sar->dev, "download_addr = 0x%x", download_addr); + AWLOGI(p_sar->dev, "start_index = %d", start_index); + AWLOGI(p_sar->dev, "fw_len = %d", fw_len); + + for (i = 0; i < pack_cnt; i++) { + memset(r_buf, 0, AW963XX_SRAM_UPDATE_ONE_PACK_SIZE); + download_addr_with_ofst = download_addr + i * AW963XX_SRAM_UPDATE_ONE_PACK_SIZE; + buf[0] = (uint8_t)(download_addr_with_ofst >> OFFSET_BIT_8); + buf[1] = (uint8_t)(download_addr_with_ofst); + if (i != (pack_cnt - 1)) { + memcpy(&buf[2], &aw_bin->info.data[start_index + i * AW963XX_SRAM_UPDATE_ONE_PACK_SIZE], AW963XX_SRAM_UPDATE_ONE_PACK_SIZE); + ret = aw_sar_i2c_write_seq(p_sar->i2c, buf, AW963XX_SRAM_UPDATE_ONE_PACK_SIZE + 2); + if (ret != AW_OK) { + AWLOGI(p_sar->dev, "cnt%d, write_seq error!", i); + devm_kfree(p_sar->dev, r_buf); + return ret; + } + ret = aw_sar_i2c_read_seq(p_sar->i2c, buf, 2, r_buf, AW963XX_SRAM_UPDATE_ONE_PACK_SIZE); + if (ret != AW_OK) { + AWLOGI(p_sar->dev, "cnt%d, read_seq error!", i); + devm_kfree(p_sar->dev, r_buf); + return ret; + } + if (memcmp(&buf[2], r_buf, AW963XX_SRAM_UPDATE_ONE_PACK_SIZE) != 0) { + AWLOGE(p_sar->dev, "read is not equal to write "); + devm_kfree(p_sar->dev, r_buf); + return -AW_ERR; + } + } else { // last pack process + memcpy(&buf[2], &aw_bin->info.data[start_index + i * AW963XX_SRAM_UPDATE_ONE_PACK_SIZE], last_pack_len); + ret = aw_sar_i2c_write_seq(p_sar->i2c, buf, last_pack_len + 2); + if (ret != AW_OK) { + AWLOGI(p_sar->dev, "cnt%d, write_seq error!", i); + devm_kfree(p_sar->dev, r_buf); + return ret; + } + ret = aw_sar_i2c_read_seq(p_sar->i2c, buf, 2, r_buf, last_pack_len); + if (ret != AW_OK) { + AWLOGI(p_sar->dev, "cnt%d, read_seq error!", i); + devm_kfree(p_sar->dev, r_buf); + return ret; + } + if (memcmp(&buf[2], r_buf, last_pack_len) != 0) { + AWLOGE(p_sar->dev, "read is not equal to write "); + devm_kfree(p_sar->dev, r_buf); + return ret; + } + /* fill 0xff in the area that not worte. */ + ret = aw963xx_sram_fill_not_wrote_area(load_bin_para, download_addr_with_ofst + last_pack_len); + if (ret != AW_OK) { + AWLOGI(p_sar->dev, "cnt%d, sram_fill_not_wrote_area error!", i); + devm_kfree(p_sar->dev, r_buf); + return ret; + } + } + } + devm_kfree(p_sar->dev, r_buf); + + return AW_OK; +} + +static int32_t aw963xx_update_firmware(struct aw_bin *aw_bin, void *load_bin_para) +{ + struct aw_sar *p_sar = (struct aw_sar *)load_bin_para; + struct aw963xx *aw963xx = (struct aw963xx *)p_sar->priv_data; + struct i2c_client *i2c = p_sar->i2c; + int32_t ret = 0; + + pr_info("%s enter\n",__func__); + if (aw963xx->start_mode == AW963XX_ROM_MODE) { + AWLOGI(p_sar->dev, "no need to update fw."); + return AW_OK; + } + + //step1: close coderam shutdown mode + aw_sar_i2c_write(i2c, 0xfff4, 0x3c00d11f); + aw_sar_i2c_write(i2c, 0xc400, 0x21660000); + + // step 2: reset mcu only and set boot mode to 1. (0xf800 0x00010100) + aw_sar_i2c_write(i2c, REG_CPU_MODE_SET, AW963XX_RESET_CPU_SET_BOOT_SATRT); + + // step 3: enable data ram. (0xFFE4 0x3C000000) + aw_sar_i2c_write(i2c, REG_RAM_PASSWORD, AW963XX_NO_ENCRYPTION); + + // setp 4: convert LD to BD + aw963xx_convert_little_endian_2_big_endian(aw_bin); + + // step 5: write ram data. + ret = aw963xx_sram_data_write(aw_bin, load_bin_para); + if (ret == AW_OK) { + AWLOGI(p_sar->dev, "sram_data_write OK"); + } else { + AWLOGI(p_sar->dev, "sram_data_write error"); + } + mdelay(3); + + // step 6: exit reset mcu and boot cpu in ram. (0xf800 0x00000100) + aw_sar_i2c_write(i2c, REG_CPU_MODE_SET, AW963XX_EXIT_RESET_CPU_SET_BOOT_SATRT); + + // step 7: reset cpu (0xFF0C 0x0) + aw_sar_i2c_write(i2c, REG_CPU_RESET, AW963XX_RESET_SET); + + //step 8: Wait for chip initialization to complete + msleep(AW963XX_CHIP_INIT_MAX_TIME_MS); + + return aw963xx_read_init_over_irq(load_bin_para); +} + +static int32_t aw963xx_load_reg_bin(struct aw_bin *aw_bin, void *load_bin_para) +{ + int32_t ret = 0; + struct aw_sar *p_sar = (struct aw_sar *)load_bin_para; + struct aw963xx *aw963xx = (struct aw963xx *)p_sar->priv_data; +/* + if (p_sar->chip_name == NULL) { + AWLOGE(p_sar->dev, "p_sar->chip_name is NULL, error!"); + return -AW_ERR; + } +*/ + AWLOGD(p_sar->dev, "reg chip name: %s, soc chip name: %s, len = %d", + p_sar->chip_name, aw_bin->header_info[0].chip_type, aw_bin->info.len); + + ret = strncmp(p_sar->chip_name, aw_bin->header_info[0].chip_type, sizeof(aw_bin->header_info[0].chip_type)); + if (ret != 0) { + AWLOGE(p_sar->dev, + "load_binname(%s) incompatible with chip type(%s)", + p_sar->chip_name, aw_bin->header_info[0].chip_type); + //return -AW_ERR; + } + + ret = aw_sar_load_reg(aw_bin, p_sar->i2c); + + if (!strncmp(p_sar->chip_name, AW96308, sizeof(AW96308)) || + !strncmp(p_sar->chip_name, AW96310, sizeof(AW96310))) { + AWLOGD(p_sar->dev, "set cs%d as irq", aw963xx->irq_mux); + aw963xx_set_cs_as_irq(p_sar, aw963xx->irq_mux); + } + + return ret; +} + +static void aw963xx_irq_handle_func(uint32_t irq_status, void *data) +{ + int8_t i = 0; + int8_t j = 0; + uint32_t curr_status_val[4] = { 0 }; + + struct aw_sar *p_sar = (struct aw_sar *)data; + uint32_t ch_th[AW963XX_CHANNEL_NUM_MAX] = { 0 }; + AWLOGD(p_sar->dev, "IRQSRC = 0x%x", irq_status); + + for (i = 0; i < AW963XX_VALID_TH; i++) + aw_sar_i2c_read(p_sar->i2c, REG_STAT0 + i * (REG_STAT1 - REG_STAT0), &curr_status_val[i]); + + for (j = 0; j < AW963XX_CHANNEL_NUM_MAX; j++) { + if (p_sar->channels_arr[j].input == NULL) { + continue; + } + for (i = 0; i < AW963XX_VALID_TH; i++) { + ch_th[j] |= ((curr_status_val[i] >> j) & 0x01) << i; + AWLOGD(p_sar->dev, "ch= %d, th = %d ch_th = 0x%x", j, i, ch_th[j]); + } + AWLOGD(p_sar->dev, "ch = %d last_th=0x%x th = 0x%x", j, p_sar->channels_arr[j].last_channel_info, ch_th[j]); + + if (p_sar->channels_arr[j].last_channel_info != ch_th[j]) { + if ((ch_th[j] >> 3 & 0x01) == 1) { //th3 + input_report_abs(p_sar->channels_arr[j].input, ABS_DISTANCE, 4); + } else if ((ch_th[j] >> 2 & 0x01) == 1) { //th2 + input_report_abs(p_sar->channels_arr[j].input, ABS_DISTANCE, 3); + } else if ((ch_th[j] >> 1 & 0x01) == 1) { //th1 + input_report_abs(p_sar->channels_arr[j].input, ABS_DISTANCE, 2); + } else if ((ch_th[j] >> 0 & 0x01) == 1) { //th0 + input_report_abs(p_sar->channels_arr[j].input, ABS_DISTANCE, 1); + } else { //far + input_report_abs(p_sar->channels_arr[j].input, ABS_DISTANCE, 0); + } + input_sync(p_sar->channels_arr[j].input); + p_sar->channels_arr[j].last_channel_info = ch_th[j]; + } + } +} +static ssize_t aw963xx_operation_mode_get(void *data, char *buf) +{ + ssize_t len = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + + if (p_sar->last_mode == AW963XX_ACTIVE_MODE) + len += snprintf(buf + len, PAGE_SIZE - len, "operation mode: Active\n"); + else if (p_sar->last_mode == AW963XX_SLEEP_MODE) + len += snprintf(buf + len, PAGE_SIZE - len, "operation mode: Sleep\n"); + else if (p_sar->last_mode == AW963XX_DEEPSLEEP_MODE) + len += snprintf(buf + len, PAGE_SIZE - len, "operation mode: DeepSleep\n"); + else + len += snprintf(buf + len, PAGE_SIZE - len, "operation mode: Unconfirmed\n"); + + //Note: This code is designed to temporarily place platform interrupts during debugging + if (p_sar->irq_init.host_irq_stat == IRQ_DISABLE) { + enable_irq(p_sar->irq_init.to_irq); + p_sar->irq_init.host_irq_stat = IRQ_ENABLE; + } + + return len; +} + +static void aw963xx_sar_chip_info_get(void *data, char *buf, ssize_t *p_len) +{ + uint32_t reg_data = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + + *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "sar%d, aw963xx chip driver version:%s\n", + p_sar->dts_info.sar_num, AW963XX_DRIVER_VERSION); + *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "The driver supports UI\n"); + + aw_sar_i2c_read(p_sar->i2c, REG_CHIP_ID0, ®_data); + *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "chipid is 0x%08x\n", reg_data); + + aw_sar_i2c_read(p_sar->i2c, REG_IRQEN, ®_data); + *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "REG_HOSTIRQEN is 0x%08x\n", reg_data); +} + +static int32_t aw963xx_get_signed_cap(void *data, uint16_t reg_addr) +{ + uint32_t reg_data = 0; + int32_t off_f = 0; + uint32_t off_c = 0; + uint32_t off_m = 0; + uint32_t off_m_bit = 0; + uint32_t off_c_bit = 0; + int32_t s_ofst_c = 0; + uint32_t i = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + + aw_sar_i2c_read(p_sar->i2c, reg_addr, ®_data); + + off_f =((reg_data >> AW_BIT16) & ONE_WORD) * AW963XX_STEP_LEN_UNSIGNED_CAP_FINE_ADJ; + off_c = (reg_data >> AW_BIT8) & ONE_WORD; + off_m = reg_data & ONE_WORD; + + for (i = 0; i < 8; i++) { + off_m_bit = (off_m >> i) & 0x01; + off_c_bit = (off_c >> i) & 0x01; + s_ofst_c += ((1 - 2 * off_m_bit) * off_c_bit * aw_sar_pow2(i)) * AW963XX_STEP_LEN_UNSIGNED_CAP_ROUGH_ADJ; + } + + return (s_ofst_c + off_f); +} + +static uint32_t aw963xx_get_unsigned_cap(void *data, uint16_t reg_addr) +{ + uint32_t reg_data = 0; + uint32_t rough = 0; + uint32_t fine = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + + aw_sar_i2c_read(p_sar->i2c, reg_addr, ®_data); + + rough = ((reg_data >> AW_BIT8) & ONE_WORD) * AW963XX_STEP_LEN_UNSIGNED_CAP_ROUGH_ADJ; + fine =((reg_data >> AW_BIT16) & ONE_WORD) * AW963XX_STEP_LEN_UNSIGNED_CAP_FINE_ADJ; + + return (rough + fine); +} + +static void aw963xx_get_ref_ch_enable(struct aw_sar *p_sar) +{ + int32_t i = 0; + uint32_t refa_ch = 0; + uint32_t refb_ch = 0; + uint32_t reg_data = 0; + struct aw963xx *aw963xx = (struct aw963xx *)p_sar->priv_data; + + for (i = 0; i < AW963XX_CHANNEL_NUM_MAX; i++) { + aw_sar_i2c_read(p_sar->i2c, + REG_FILTCTRL0_CH0 + + i * (REG_FILTCTRL0_CH1 - REG_FILTCTRL0_CH0), + ®_data); + if ((reg_data >> AW963XX_FILTCTRL0_CHX_REFAEN) & 0x01) { + refa_ch = (reg_data >> AW963XX_FILTCTRL0_CHX_REFASEL) & 0x1f; + aw963xx->ref_ch_en[refa_ch] = AW963XX_REF_EN; + } + if ((reg_data >> AW963XX_FILTCTRL0_CHX_REFBEN) & 0x01) { + refb_ch = (reg_data >> AW963XX_FILTCTRL0_CHX_REFBSEL) & 0x1f; + aw963xx->ref_ch_en[refb_ch] = AW963XX_REF_EN; + } + AWLOGI(p_sar->dev, "ch%d = %d", i, aw963xx->ref_ch_en[i]); + } +} + +//Note: Because the kernel cannot handle floating-point types, it expands mul by 10 times +static uint8_t aw963xx_get_offset_multiple(struct aw_sar *p_sar, uint8_t ch) +{ + uint8_t mul = 1; + uint32_t reg_data = 0; + + aw_sar_i2c_read(p_sar->i2c, + REG_AFECFG2_CH0 + + ch * (REG_AFECFG2_CH1 - REG_AFECFG2_CH0), + ®_data); + if ((reg_data >> 27) & 0x1) { + if (((reg_data >> 29) & 0x3) == 0) { + mul = 16; + } else if (((reg_data >> 29) & 0x3) == 1) { + mul = 20; + } else if (((reg_data >> 29) & 0x3) == 2) { + mul = 26; + } else if (((reg_data >> 29) & 0x3) == 3) { + mul = 40; + } + return mul; + } + + aw_sar_i2c_read(p_sar->i2c, + REG_AFECFG3_CH0 + + ch * (REG_AFECFG3_CH1 - REG_AFECFG3_CH0), + ®_data); + if ((reg_data >> 11) & 0x1) { + mul = 20; + } else { + mul = 10; + } + + return mul; +} + +static ssize_t aw963xx_get_cap_offset(void *data, char *debug_buf, char *tcmd_buf) +{ + uint32_t reg_data = 0; + uint32_t mode = 0xff; + uint32_t i = 0; + uint32_t cap_ofst = 0; + int32_t signed_cap_ofst = 0; + uint32_t tmp = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + struct aw963xx *aw963xx = (struct aw963xx *)p_sar->priv_data; + uint8_t mul = 10; + uint32_t send_tcmd_offset = 0; + ssize_t tcmd_buf_len = 0; + ssize_t debug_buf_len = 0; + + aw963xx_get_ref_ch_enable(p_sar); + + for (i = 0; i < AW963XX_CHANNEL_NUM_MAX; i++) { + aw_sar_i2c_read(p_sar->i2c, + REG_AFESOFTCFG0_CH0 + + i * (REG_AFESOFTCFG0_CH1 - REG_AFESOFTCFG0_CH0), + ®_data); + mul = aw963xx_get_offset_multiple(p_sar, i); + mode = reg_data & 0x0ff; + switch (mode) { + case AW963XX_UNSIGNED_CAP: //self-capacitance mode unsigned cail + cap_ofst = aw963xx_get_unsigned_cap(p_sar, + REG_AFECFG1_CH0 + i * (REG_AFECFG1_CH1 - REG_AFECFG1_CH0)); + //Because it has been expanded by 10000 times before, + //the accuracy of removing mul's expansion loss can be ignored + cap_ofst = cap_ofst * mul / 10; + send_tcmd_offset = cap_ofst / AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE; + AWLOGI(p_sar->dev, "cap_ofst = %d", cap_ofst); + if (debug_buf != NULL) { + debug_buf_len += snprintf(debug_buf + debug_buf_len, PAGE_SIZE - debug_buf_len, + "unsigned cap ofst ch%d: %d.%dpf\r\n", + i, + cap_ofst / AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE, + cap_ofst % AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE); + } + break; + case AW963XX_SIGNED_CAP: //self-capacitance mode signed cail + signed_cap_ofst = aw963xx_get_signed_cap(p_sar, + REG_AFECFG1_CH0 + i * (REG_AFECFG1_CH1 - REG_AFECFG1_CH0)); + signed_cap_ofst = signed_cap_ofst * mul / 10; + send_tcmd_offset = signed_cap_ofst / AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE; + AWLOGI(p_sar->dev, "cap_ofst1 = 0x%x", signed_cap_ofst); + if (signed_cap_ofst < 0) { + tmp = -signed_cap_ofst; + AWLOGI(p_sar->dev, "cap_ofst2 = 0x%x", signed_cap_ofst); + if (debug_buf != NULL) { + debug_buf_len += snprintf(debug_buf + debug_buf_len, PAGE_SIZE - debug_buf_len, + "signed cap ofst ch%d: -%d.%dpf\r\n", + i, + tmp / AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE, + tmp % AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE); + } + } else { + AWLOGI(p_sar->dev, "cap_ofst2 = 0x%x", signed_cap_ofst); + if (debug_buf != NULL) { + debug_buf_len += snprintf(debug_buf + debug_buf_len, PAGE_SIZE - debug_buf_len, + "signed cap ofst ch%d: %d.%dpf\r\n", + i, + signed_cap_ofst / AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE, + signed_cap_ofst % AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE); + } + } + break; + case AW963XX_MUTUAL_CAP: //mutual-capacitance mode + if (aw963xx->ref_ch_en[i] == AW963XX_REF_EN) { + cap_ofst = aw963xx_get_unsigned_cap(p_sar, + REG_AFECFG1_M_CH0 + i * (REG_AFECFG1_M_CH1 - REG_AFECFG1_M_CH0)); + cap_ofst = cap_ofst * mul / 10; + send_tcmd_offset = cap_ofst / AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE; + AWLOGI(p_sar->dev, "ref cap_ofst = %d", cap_ofst); + if (debug_buf != NULL) { + debug_buf_len += snprintf(debug_buf + debug_buf_len, PAGE_SIZE - debug_buf_len, + "ref unsigned cap ofst ch%d: %d.%dpf\r\n", + i, + cap_ofst / AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE, + cap_ofst % AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE); + } + } else { + signed_cap_ofst = aw963xx_get_signed_cap(p_sar, + REG_AFECFG1_CH0 + i * (REG_AFECFG1_CH1 - REG_AFECFG1_CH0)); + signed_cap_ofst = signed_cap_ofst * mul / 10; + send_tcmd_offset = signed_cap_ofst / AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE; + AWLOGI(p_sar->dev, "cap_ofst1 = 0x%x", signed_cap_ofst); + if (signed_cap_ofst < 0) { + tmp = -signed_cap_ofst; + AWLOGI(p_sar->dev, "cap_ofst2 = 0x%x", signed_cap_ofst); + if (debug_buf != NULL) { + debug_buf_len += snprintf(debug_buf + debug_buf_len, PAGE_SIZE - debug_buf_len, + "mutual cap ofst ch%d: -%d.%dpf\r\n", + i, + tmp / AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE, + tmp % AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE); + } + } else { + AWLOGI(p_sar->dev, "cap_ofst2 = 0x%x", signed_cap_ofst); + if (debug_buf != NULL) { + debug_buf_len += snprintf(debug_buf + debug_buf_len, PAGE_SIZE - debug_buf_len, + "mutual cap ofst ch%d: %d.%dpf\r\n", + i, + signed_cap_ofst / AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE, + signed_cap_ofst % AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE); + } + } + } + break; + default: + AWLOGI(p_sar->dev, "aw963xx ofst error 0x%x", reg_data & 0x0f); + break; + } + if (tcmd_buf != NULL) { + tcmd_buf[i * 4 + 0] = (uint8_t)((send_tcmd_offset >> 0) & 0xff); + tcmd_buf[i * 4 + 1] = (uint8_t)((send_tcmd_offset >> 8) & 0xff); + tcmd_buf[i * 4 + 2] = (uint8_t)((send_tcmd_offset >> 16) & 0xff); + tcmd_buf[i * 4 + 3] = (uint8_t)((send_tcmd_offset >> 24) & 0xff); + tcmd_buf_len += 4; + } + } + + if(tcmd_buf != NULL) { + return tcmd_buf_len; + } else { + return debug_buf_len; + } +} + +static ssize_t aw963xx_get_cap_offset_send_to_tcmd(void *data, char *tcmd_buf) +{ + //Note: The format needs to be the same as that of tcmd + if (tcmd_buf != NULL) { + return aw963xx_get_cap_offset(data, NULL, tcmd_buf); + } + + return 0; +} + +static ssize_t aw963xx_get_cap_offset_send_to_debug(void *data, char *debug_buf) +{ + //Note: That debugging uses string output + if (debug_buf != NULL) { + return aw963xx_get_cap_offset(data, debug_buf, NULL); + } + + return 0; +} + +static void aw963xx_set_cs_as_irq(struct aw_sar *p_sar, int flag) +{ + if (flag == AW963XX_CS2_IRQ) { + aw_sar_i2c_write(p_sar->i2c, 0xfff4, 0x3c00d013); + aw_sar_i2c_write(p_sar->i2c, 0xc100, 0x00000020); + aw_sar_i2c_write(p_sar->i2c, 0xe018, 0x00000004); + } else if (flag == AW963XX_CS5_IRQ) { + aw_sar_i2c_write(p_sar->i2c, 0xfff4, 0x3c00d013); + aw_sar_i2c_write(p_sar->i2c, 0xc100, 0x00000800); + aw_sar_i2c_write(p_sar->i2c, 0xe018, 0x00000020); + } +} + +int32_t aw963xx_check_chipid(void *data) +{ + int32_t ret = -AW_ERR; + uint32_t reg_val = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + + if (p_sar == NULL) { + return -AW_BIN_PARA_INVALID; + } + + ret = aw_sar_i2c_read(p_sar->i2c, REG_CHIP_ID0, ®_val); + if (ret < 0) { + AWLOGE(p_sar->dev, "read CHIP ID failed: %d", ret); + return ret; + } + + switch (reg_val) { + case AW96303_CHIP_ID: + AWLOGI(p_sar->dev, "aw96303 detected, 0x%04x", reg_val); + memcpy(p_sar->chip_name, AW96303, 8); + ret = AW_OK; + break; + case AW96305_CHIP_ID: + AWLOGI(p_sar->dev, "aw96305 detected, 0x%04x", reg_val); + memcpy(p_sar->chip_name, AW96305, 8); + ret = AW_OK; + break; + case AW96308_CHIP_ID: + AWLOGI(p_sar->dev, "aw96308 detected, 0x%04x", reg_val); + memcpy(p_sar->chip_name, AW96308, 8); + ret = AW_OK; + break; + case AW96310_CHIP_ID: + AWLOGI(p_sar->dev, "aw96310 detected, 0x%04x", reg_val); + memcpy(p_sar->chip_name, AW96310, 8); + ret = AW_OK; + break; + default: + AWLOGI(p_sar->dev, "chip id error, 0x%04x", reg_val); + ret = -AW_ERR; + break; + } + + return ret; +} + +/**********************mode operation start*******************************/ +static void aw963xx_enable_clock(void *i2c) +{ + aw_sar_i2c_write_bits(i2c, REG_CHIPSTAT, ~AW963XX_CPU_OSC_CTRL_MASK, AW963XX_CPU_OSC_CTRL_MASK); +} + +static uint32_t aw963xx_rc_irqscr(void *i2c) +{ + uint32_t val = 0; + aw_sar_i2c_read(i2c, REG_IRQSRC, &val); + return val; +} + +static void aw963xx_set_active_cmd(void *i2c) +{ + aw_sar_i2c_write(i2c, REG_CMD, AW963XX_ACTIVE_MODE); +} + +static void aw963xx_set_sleep_cmd(void *i2c) +{ + aw_sar_i2c_write(i2c, REG_CMD, AW963XX_SLEEP_MODE); +} + +static void aw963xx_set_deepsleep_cmd(void *i2c) +{ + aw_sar_i2c_write(i2c, REG_CMD, AW963XX_DEEPSLEEP_MODE); +} + +static const struct aw_sar_mode_set_t g_aw963xx_mode_set[] = { + { + .chip_id = AW_SAR_NONE_CHECK_CHIP, + .chip_mode = { + .curr_mode = AW963XX_ACTIVE_MODE, + .last_mode = AW963XX_DEEPSLEEP_MODE, + }, + .mode_switch_ops = { + .enable_clock = aw963xx_enable_clock, + .rc_irqscr = NULL, + .mode_update = aw963xx_set_active_cmd, + }, + }, + { + .chip_id = AW_SAR_NONE_CHECK_CHIP, + .chip_mode = { + .curr_mode = AW963XX_ACTIVE_MODE, + .last_mode = AW963XX_SLEEP_MODE, + }, + .mode_switch_ops = { + .enable_clock = NULL, + .rc_irqscr = NULL, + .mode_update = aw963xx_set_active_cmd, + }, + }, + { + .chip_id = AW_SAR_NONE_CHECK_CHIP, + .chip_mode = { + .curr_mode = AW963XX_ACTIVE_MODE, + .last_mode = AW963XX_ACTIVE_MODE, + }, + .mode_switch_ops = { + .enable_clock = NULL, + .rc_irqscr = NULL, + .mode_update = aw963xx_set_active_cmd, + }, + }, + { + .chip_id = AW_SAR_NONE_CHECK_CHIP, + .chip_mode = { + .curr_mode = AW963XX_SLEEP_MODE, + .last_mode = AW963XX_DEEPSLEEP_MODE, + }, + .mode_switch_ops = { + .enable_clock = aw963xx_enable_clock, + .rc_irqscr = aw963xx_rc_irqscr, + .mode_update = aw963xx_set_sleep_cmd, + }, + }, + { + .chip_id = AW_SAR_NONE_CHECK_CHIP, + .chip_mode = { + .curr_mode = AW963XX_SLEEP_MODE, + .last_mode = AW963XX_ACTIVE_MODE, + }, + .mode_switch_ops = { + .enable_clock = NULL, + .rc_irqscr = aw963xx_rc_irqscr, + .mode_update = aw963xx_set_sleep_cmd, + }, + }, + { + .chip_id = AW_SAR_NONE_CHECK_CHIP, + .chip_mode = { + .curr_mode = AW963XX_DEEPSLEEP_MODE, + .last_mode = AW963XX_SLEEP_MODE, + }, + .mode_switch_ops = { + .enable_clock = NULL, + .rc_irqscr = aw963xx_rc_irqscr, + .mode_update = aw963xx_set_deepsleep_cmd, + }, + }, + { + .chip_id = AW_SAR_NONE_CHECK_CHIP, + .chip_mode = { + .curr_mode = AW963XX_DEEPSLEEP_MODE, + .last_mode = AW963XX_ACTIVE_MODE, + }, + .mode_switch_ops = { + .enable_clock = NULL, + .rc_irqscr = aw963xx_rc_irqscr, + .mode_update = aw963xx_set_deepsleep_cmd, + }, + }, +}; + +static void aw963xx_sar_get_firmware_info(void *data, char *buf, ssize_t *p_len) +{ + uint32_t reg_data = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + + aw_sar_i2c_read(p_sar->i2c, REG_FWVER, ®_data); + *p_len += snprintf(buf + *p_len, PAGE_SIZE - *p_len, "firmware is 0x%08x\n", reg_data); +} + +static int32_t aw963xx_parse_dts(void *data) +{ + int32_t val = 0; + struct aw_sar *p_sar = (struct aw_sar *)data; + struct aw963xx *aw963xx = (struct aw963xx *)p_sar->priv_data; + struct device_node *np = p_sar->i2c->dev.of_node; + + val = of_property_read_u32(np, "irq-mux", &aw963xx->irq_mux); + if (val != 0) { + AWLOGE(p_sar->dev, "irq-mux not detected"); + } else { + AWLOGI(p_sar->dev, "irq-mux = %d", aw963xx->irq_mux); + } + + val = of_property_read_u32(np, "start-mode", &aw963xx->start_mode); + if (val != 0) { + AWLOGE(p_sar->dev, "start-mode not detected"); + } else { + AWLOGI(p_sar->dev, "start-mode = %d", aw963xx->start_mode); + } + + return AW_OK; +} + +static const struct aw_sar_mode_t g_aw963xx_mode = { + .mode_set_arr = &g_aw963xx_mode_set[0], + .mode_set_arr_len = sizeof(g_aw963xx_mode_set) / sizeof(g_aw963xx_mode_set[0]), + .p_set_mode_node_fn = NULL, + .p_get_mode_node_fn = aw963xx_operation_mode_get, +}; + +static const struct aw_sar_diff_t g_aw963xx_diff = { + .diff0_reg = REG_DIFF_CH0, + .diff_step = REG_DIFF_CH1 - REG_DIFF_CH0, + .rm_float = AW963XX_DATA_PROCESS_FACTOR, + .p_get_diff_node_fn = NULL, +}; + +static const struct aw_sar_offset_t g_aw963xx_offset = { + .p_get_offset_node_fn = aw963xx_get_cap_offset_send_to_debug, +}; + +static const struct aw_sar_aot_t g_aw963xx_aot = { + .aot_reg = REG_SCANCTRL1, + .aot_mask = ~0xfff, + .aot_flag = 0xfff, +}; + +static const struct aw_sar_para_load_t g_aw963xx_reg_arr_para = { + .reg_arr = aw963xx_reg_default, + .reg_arr_len = (sizeof(aw963xx_reg_default) / sizeof(aw963xx_reg_default[0])), +}; + +static const struct aw_sar_regulator_config_t g_regulator_config = { + .vcc_name = "vcc", + .min_uV = AW9620X_SAR_VCC_MIN_UV, + .max_uV = AW9620X_SAR_VCC_MAX_UV, +}; + +static const struct aw_sar_reg_list_t g_aw963xx_reg_list = { + .reg_none_access = REG_NONE_ACCESS, + .reg_rd_access = REG_RD_ACCESS, + .reg_wd_access = REG_WR_ACCESS, + .reg_perm = (struct aw_sar_reg_data *)&g_aw963xx_reg_access[0], + .reg_num = sizeof(g_aw963xx_reg_access) / sizeof(g_aw963xx_reg_access[0]), +}; + +static const struct aw_sar_chip_mode_t g_aw963xx_chip_mode = { + //.init_mode = AW963XX_ACTIVE_MODE, + .init_mode = AW963XX_SLEEP_MODE, + .active = AW963XX_ACTIVE_MODE, + .pre_init_mode = AW963XX_SLEEP_MODE, +}; + +static const struct aw_sar_load_bin_t g_aw963xx_load_reg_bin = { + .bin_name = "aw963xx_reg", + .bin_opera_func = aw963xx_load_reg_bin, + .p_update_fn = NULL, +}; + +static const struct aw_sar_load_bin_t g_aw963xx_load_fw_bin = { + .bin_name = "aw963xx_fw", + .bin_opera_func = aw963xx_update_firmware, + .p_get_prot_update_fw_node_fn = aw963xx_sar_get_firmware_info, + .bin_load_fail_opera = NULL, +}; + +static const struct aw_sar_get_chip_info_t g_aw963xx_get_chip_info = { + .p_get_chip_info_node_fn = aw963xx_sar_chip_info_get, +}; + +static const struct aw_sar_check_chipid_t g_aw963xx_check_chipid = { + .p_check_chipid_fn = aw963xx_check_chipid, +}; + +static const struct aw_sar_irq_init_t g_aw963xx_irq_init = { + .flags = GPIOF_DIR_IN | GPIOF_INIT_HIGH, + .irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + .handler = NULL, + .thread_fn = NULL, + .rc_irq_fn = aw963xx_rc_irqscr, + .irq_spec_handler_fn = aw963xx_irq_handle_func, + + .p_irq_init_fn = NULL, + .p_irq_deinit_fn = NULL, +}; + +static const struct aw_sar_soft_rst_t g_aw963xx_soft_rst = { + .reg_rst = REG_SA_RSTNALL, + .reg_rst_val = AW963XX_SOFT_RST_EN, + .delay_ms = AW963XX_CHIP_INIT_MAX_TIME_MS, + .p_soft_reset_fn = NULL, +}; + +static const struct aw_sar_init_over_irq_t g_aw963xx_init_over_irq = { + .wait_times = 100, + .daley_step = 1, + .reg_irqsrc = REG_IRQSRC, + .irq_offset_bit = 0, + .irq_mask = 0x1, + .irq_flag = 0x1, + + .p_check_init_over_irq_fn = NULL, + .p_get_err_type_fn = NULL, +}; + +static ssize_t cali_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + struct aw963xx *aw963xx = container_of(class, struct aw963xx, capsense_class); + struct aw_sar *p_sar = NULL; + int32_t ret = 0; + + if (aw963xx == NULL) { + return 0; + } + p_sar = aw963xx->p_aw_sar; + if (p_sar == NULL) { + return 0; + } + + ret = aw_sar_i2c_write_bits(p_sar->i2c, REG_SCANCTRL1, ~0xfff, 0xfff); + + return snprintf(buf, 8, "%d\n", ret); +} + +static CLASS_ATTR_RO(cali); + +static ssize_t +reset_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) +{ + u32 temp = 0; + struct aw963xx *aw963xx = container_of(class, struct aw963xx, capsense_class); + struct aw_sar *p_sar = NULL; + + if (aw963xx == NULL) + return 0; + + p_sar = aw963xx->p_aw_sar; + if (p_sar == NULL) + return 0; + + aw_sar_i2c_read(p_sar->i2c, REG_WST, &temp); + if (!strncmp(buf, "reset", 5) || !strncmp(buf, "1", 1)) { + if (((temp >> 24) & 0x00000003) == 1) { + AWLOGD(p_sar->dev, "temp:0X%x", temp); + aw_sar_i2c_write_bits(p_sar->i2c, REG_SCANCTRL1, ~0xfff, 0xfff); + } + } + + return count; +} + +static CLASS_ATTR_WO(reset); + +static ssize_t mode_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + struct aw963xx *aw963xx = container_of(class, struct aw963xx, capsense_class); + struct aw_sar *p_sar = NULL; + int mode = 0; + + if (aw963xx == NULL) { + return 0; + } + p_sar = aw963xx->p_aw_sar; + if (p_sar == NULL) { + return 0; + } + + if (p_sar->last_mode == AW963XX_ACTIVE_MODE) + mode = 1; + else + mode = 0; + + AWLOGD(p_sar->dev, "mode:0X%x", p_sar->last_mode); + + return snprintf(buf, 8, "%d\n", mode); +} + + +static ssize_t mode_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + int8_t mode = 0; + int set_mode = 0; + struct aw963xx *aw963xx = container_of(class, struct aw963xx, capsense_class); + struct aw_sar *p_sar = NULL; + + if (aw963xx == NULL) { + return 0; + } + p_sar = aw963xx->p_aw_sar; + if (p_sar == NULL) { + return 0; + } + + mode = buf[0]; + + AWLOGD(p_sar->dev, "mode:0X%x", mode); + + if (mode == 0x01) + set_mode = AW963XX_ACTIVE_MODE; + else + set_mode = AW963XX_SLEEP_MODE; + + aw_sar_mode_set(p_sar, set_mode); + + return count; +} + +static CLASS_ATTR_RW(mode); + +static ssize_t reg_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u32 *p = (u32*)buf; + struct aw963xx *aw963xx = container_of(class, struct aw963xx, capsense_class); + struct aw_sar *p_sar = NULL; + + if (aw963xx == NULL) { + return 0; + } + p_sar = aw963xx->p_aw_sar; + if (p_sar == NULL) { + return 0; + } + + if(aw963xx->read_flag){ + aw963xx->read_flag = 0; + aw_sar_i2c_read(p_sar->i2c, aw963xx->read_reg, p); + AWLOGD(p_sar->dev, "read_reg = 0x%x, val = 0x%x", aw963xx->read_reg, *p); + return 4; + } + + return -1; +} + +static ssize_t reg_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + uint16_t regaddr = 0; + uint32_t val = 0; + int i = 0; + struct aw963xx *aw963xx = container_of(class, struct aw963xx, capsense_class); + struct aw_sar *p_sar = NULL; + + if (aw963xx == NULL) { + return 0; + } + p_sar = aw963xx->p_aw_sar; + if (p_sar == NULL) { + return 0; + } + + if( count != 7){ + AWLOGE(p_sar->dev, "params error[ count == %lu !=2]\n", count); + return -1; + } + for(i = 0 ; i < count ; i++) + AWLOGD(p_sar->dev, "buf[%d] = 0x%x\n", i, buf[i]); + + if(buf[6] == 0){ + regaddr = ((uint16_t)buf[0]<<8) | (uint16_t)buf[1]; + val= ((uint32_t)buf[2]<<24) | ((uint32_t)buf[3]<<16) | ((uint32_t)buf[4]<<8) | ((uint32_t)buf[5]); + AWLOGD(p_sar->dev, "regaddr:0x%x, val:0x%x", regaddr, val); + aw_sar_i2c_write(p_sar->i2c, regaddr, val); + } else if(buf[6] == 1) { + aw963xx->read_reg = ((uint16_t)buf[0]<<8) | (uint16_t)buf[1]; + AWLOGD(p_sar->dev, "aw963xx->read_reg:0x%x", aw963xx->read_reg); + aw963xx->read_flag = true; + AWLOGD(p_sar->dev, "-----------\n"); + } + return count; +} +static CLASS_ATTR_RW(reg); + +static ssize_t int_state_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + struct aw963xx *aw963xx = container_of(class, struct aw963xx, capsense_class); + struct aw_sar *p_sar = NULL; + + if (aw963xx == NULL) { + return 0; + } + p_sar = aw963xx->p_aw_sar; + if (p_sar == NULL) { + return 0; + } + AWLOGD(p_sar->dev, + "%d", p_sar->ret_val ); + + return snprintf(buf, 8, "%d\n", p_sar->intrrupt_init_state); +} +static CLASS_ATTR_RO(int_state); + +static ssize_t offset_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + struct aw963xx *aw963xx = container_of(class, struct aw963xx, capsense_class); + struct aw_sar *p_sar = NULL; + if (aw963xx == NULL) { + return 0; + } + p_sar = aw963xx->p_aw_sar; + if (p_sar == NULL) { + return 0; + } + + return aw963xx_get_cap_offset_send_to_tcmd(p_sar, buf); +} +static CLASS_ATTR_RO(offset); + +#ifdef USE_SENSORS_CLASS +static const char *g_aw963xx_ch_name[] = { + "Moto CapSense Ch0", "Moto CapSense Ch1", "Moto CapSense Ch2", + "Moto CapSense Ch3", "Moto CapSense Ch4", "Moto CapSensor Ch5", + "Moto CapSense Ch6", "Moto CapSense Ch7", "Moto CapSense Ch8" + "Moto CapSensor Ch9", "Moto CapSensor Ch10", "Moto CapSensor Ch11", + "Moto CapSensor Ch12", "Moto CapSensor Ch13", "Moto CapSensor Ch14" +}; +static int32_t g_aw963xx_counter = 0; +static struct aw963xx *g_aw963xx = NULL; + +static int capsensor_set_enable(struct sensors_classdev *sensors_cdev, unsigned int enable) +{ + uint8_t i = 0; + struct aw963xx *aw963xx = g_aw963xx; + struct aw_sar *p_sar = NULL; + uint8_t set_mode = 0; + static uint8_t sar_cali_flag = false; + + pr_info("%s enter\n",__func__); + + if (aw963xx == NULL) + return 0; + + p_sar = aw963xx->p_aw_sar; + if (p_sar == NULL) + return 0; + + AWLOGD(p_sar->dev, "enable %d", enable); + + for (i = 0; i < AW963XX_CHANNEL_NUM_MAX; i++) { + if ((p_sar->channels_arr[i].used == AW_FALSE) || + (p_sar->channels_arr[i].input == NULL)) + continue; + + if (strcmp(sensors_cdev->name, p_sar->channels_arr[i].name) == 0) { + if (enable == 0x01) { + pr_info("enable cap sensor[%d] : %s", i, sensors_cdev->name); + if (sar_cali_flag == false) { + aw_sar_i2c_write_bits(p_sar->i2c, REG_SCANCTRL1, ~0xfff, 0xfff); + set_mode = AW963XX_ACTIVE_MODE; + aw_sar_mode_set(p_sar, set_mode); + sar_cali_flag = true; + } + input_report_abs(p_sar->channels_arr[i].input, ABS_DISTANCE, 0); + input_sync(p_sar->channels_arr[i].input); + } else if (enable == 0) { + pr_info("disable cap sensor[%d] : %s", i, sensors_cdev->name); + if (sar_cali_flag == true) { + set_mode = AW963XX_SLEEP_MODE; + aw_sar_mode_set(p_sar, set_mode); + sar_cali_flag = false; + } + input_report_abs(p_sar->channels_arr[i].input, ABS_DISTANCE, -1); + input_sync(p_sar->channels_arr[i].input); + p_sar->channels_arr[i].last_channel_info = -1; + } else { + AWLOGD(p_sar->dev, "unknown enable symbol"); + } + break; + } + } + + AWLOGD(p_sar->dev, "enable over %d", enable); + + return 0; +} +#endif + +//moto_customization +static int32_t aw_sar_custom_flie_node_create(void *data) +{ + struct aw_sar *p_sar = NULL; + int32_t ret = 0; + struct aw963xx *aw963xx = NULL; +#ifdef USE_SENSORS_CLASS + int i = 0; +#endif + + if (data == NULL) { + return -1; + } + + p_sar = (struct aw_sar *)data; + aw963xx = (struct aw963xx *)p_sar->priv_data; + + AWLOGD(p_sar->dev, "aw_sar_custom_flie_node"); + + aw963xx->capsense_class.name = "capsense"; + aw963xx->capsense_class.owner = THIS_MODULE; + + ret = class_register(&aw963xx->capsense_class); + if (ret < 0) { + AWLOGE(p_sar->dev, "Create fsys class failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&aw963xx->capsense_class, &class_attr_cali); + if (ret < 0) { + AWLOGE(p_sar->dev, "Create cali file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&aw963xx->capsense_class, &class_attr_reset); + if (ret < 0) { + AWLOGE(p_sar->dev, "Create cali file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&aw963xx->capsense_class, &class_attr_int_state); + if (ret < 0) { + AWLOGE(p_sar->dev, "Create int_state file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&aw963xx->capsense_class, &class_attr_mode); + if (ret < 0) { + AWLOGE(p_sar->dev, "Create mode file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&aw963xx->capsense_class, &class_attr_reg); + if (ret < 0) { + AWLOGE(p_sar->dev, "Create reg file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&aw963xx->capsense_class, &class_attr_offset); + if (ret < 0) { + AWLOGE(p_sar->dev, "Create offset file failed (%d)\n", ret); + return ret; + } + +#ifdef USE_SENSORS_CLASS + for (i = 0; i < AW963XX_CHANNEL_NUM_MAX; i++){ + if ((p_sar->channels_arr[i].used == AW_FALSE) || + (p_sar->channels_arr[i].input == NULL)) { + continue; + } + + aw963xx->sensors_capsensor_chs[i].sensors_enable = capsensor_set_enable; + aw963xx->sensors_capsensor_chs[i].sensors_poll_delay = NULL; + aw963xx->sensors_capsensor_chs[i].name = g_aw963xx_ch_name[g_aw963xx_counter]; + aw963xx->sensors_capsensor_chs[i].vendor = "awinic"; + aw963xx->sensors_capsensor_chs[i].version = 1; + aw963xx->sensors_capsensor_chs[i].type = SENSOR_TYPE_MOTO_CAPSENSE; + aw963xx->sensors_capsensor_chs[i].max_range = "5"; + aw963xx->sensors_capsensor_chs[i].resolution = "5.0"; + aw963xx->sensors_capsensor_chs[i].sensor_power = "0.1"; + aw963xx->sensors_capsensor_chs[i].min_delay = 0; + aw963xx->sensors_capsensor_chs[i].fifo_reserved_event_count = 0; + aw963xx->sensors_capsensor_chs[i].fifo_max_event_count = 0; + aw963xx->sensors_capsensor_chs[i].delay_msec = 100; + aw963xx->sensors_capsensor_chs[i].enabled = 0; + + AWLOGD(p_sar->dev, "cap sensor_class channel_name:%s", g_aw963xx_ch_name[g_aw963xx_counter]); + AWLOGD(p_sar->dev, "ch:%d sensors_capsensor_chs:%p", i, &aw963xx->sensors_capsensor_chs[i]); + ret = sensors_classdev_register(&p_sar->channels_arr[i].input->dev, &aw963xx->sensors_capsensor_chs[i]); + if (ret < 0) + AWLOGE(p_sar->dev, "create ch0 cap sensor_class file failed (%d)\n", ret); + g_aw963xx_counter++; + g_aw963xx_counter = g_aw963xx_counter > (sizeof(g_aw963xx_ch_name) / sizeof(g_aw963xx_ch_name[0])) ? + (sizeof(g_aw963xx_ch_name) / sizeof(g_aw963xx_ch_name[0])) : g_aw963xx_counter; + } +#endif + + return ret; +} + +static void aw_sar_custom_flie_node_free(void *data) +{ + struct aw_sar *p_sar = NULL; + struct aw963xx *aw963xx = NULL; +#ifdef USE_SENSORS_CLASS + int i = 0; +#endif + + pr_info("%s enter\n",__func__); + + if (data == NULL) { + return; + } + + p_sar = (struct aw_sar *)data; + aw963xx = (struct aw963xx *)p_sar->priv_data; + +#ifdef USE_SENSORS_CLASS + for (i = 0; i < AW963XX_CHANNEL_NUM_MAX; i++){ + if ((p_sar->channels_arr[i].used == AW_FALSE) || + (p_sar->channels_arr[i].input == NULL)) { + continue; + } + g_aw963xx_counter--; + g_aw963xx_counter = g_aw963xx_counter < 0 ? 0 : g_aw963xx_counter; + sensors_classdev_unregister(&aw963xx->sensors_capsensor_chs[g_aw963xx_counter]); + } +#endif + + class_remove_file(&aw963xx->capsense_class, &class_attr_offset); + class_remove_file(&aw963xx->capsense_class, &class_attr_reg); + class_remove_file(&aw963xx->capsense_class, &class_attr_mode); + class_remove_file(&aw963xx->capsense_class, &class_attr_int_state); + class_remove_file(&aw963xx->capsense_class, &class_attr_cali); + class_remove_file(&aw963xx->capsense_class, &class_attr_reset); + + class_unregister(&aw963xx->capsense_class); +} + +static const struct aw_sar_pm_t g_aw963xx_pm_chip_mode = { + .suspend_set_mode = AW963XX_SLEEP_MODE, + .resume_set_mode = AW963XX_ACTIVE_MODE, + .shutdown_set_mode = AW963XX_SLEEP_MODE, +}; + +static const struct aw_sar_platform_config g_aw963xx_platform_config = { + .p_add_parse_dts_fn = &aw963xx_parse_dts, + .p_regulator_config = &g_regulator_config, + .p_irq_init = &g_aw963xx_irq_init, + .p_pm_chip_mode = &g_aw963xx_pm_chip_mode, +}; + +static const struct aw_sar_chip_config g_aw963xx_chip_config = { + .ch_num_max = AW963XX_CHANNEL_NUM_MAX, + .p_platform_config = &g_aw963xx_platform_config, + + .p_check_chipid = &g_aw963xx_check_chipid, + .p_soft_rst = &g_aw963xx_soft_rst, + .p_init_over_irq = &g_aw963xx_init_over_irq, + .p_fw_bin = &g_aw963xx_load_fw_bin, + .p_reg_bin = &g_aw963xx_load_reg_bin, + .p_chip_mode = &g_aw963xx_chip_mode, + + //Node usage parameters + .p_reg_list = &g_aw963xx_reg_list, + .p_reg_arr = &g_aw963xx_reg_arr_para, + .p_aot = &g_aw963xx_aot, + .p_diff = &g_aw963xx_diff, + .p_offset = &g_aw963xx_offset, + .p_mode = &g_aw963xx_mode, + .p_prox_fw = &g_aw963xx_load_fw_bin, + .p_get_chip_info = &g_aw963xx_get_chip_info, + .p_aw_sar_awrw = NULL, + .p_boot_bin = NULL, + + .p_other_operation = aw_sar_custom_flie_node_create, + .p_other_opera_free = aw_sar_custom_flie_node_free, +}; + +int32_t aw963xx_init(struct aw_sar *p_sar) +{ + struct aw963xx *aw963xx = NULL; + + pr_info("%s enter\n",__func__); + + if (p_sar == NULL) { + AWLOGE(p_sar->dev, "para is NULL, error!"); + return -AW_ERR; + } + + p_sar->priv_data = devm_kzalloc(p_sar->dev, sizeof(struct aw963xx), GFP_KERNEL); + if (p_sar->priv_data == NULL) { + AWLOGE(p_sar->dev, "priv_data failed to malloc memory!"); + return -AW_ERR; + } + + //Chip private function operation + p_sar->p_sar_para = &g_aw963xx_chip_config; + + aw963xx = (struct aw963xx *)p_sar->priv_data; + aw963xx->p_aw_sar = (void *)p_sar; + g_aw963xx = aw963xx; + + return AW_OK; +} + +void aw963xx_deinit(struct aw_sar *p_sar) +{ + struct aw963xx *aw963xx = NULL; + + pr_info("%s enter\n",__func__); + if ((p_sar == NULL) || (p_sar->priv_data == NULL)) { + return; + } + + aw963xx = (struct aw963xx *)p_sar->priv_data; + + del_timer_sync(&aw963xx->abnormal_irq_timer); + + if (p_sar->priv_data != NULL) { + devm_kfree(p_sar->dev, p_sar->priv_data); + } + + AWLOGI(p_sar->dev, "aw963xx_deinit ok!"); +} diff --git a/drivers/misc/awinic/sarsensor/aw_sar.c b/drivers/misc/awinic/sarsensor/aw_sar.c new file mode 100644 index 000000000000..8b08bfa591e9 --- /dev/null +++ b/drivers/misc/awinic/sarsensor/aw_sar.c @@ -0,0 +1,2207 @@ +#include "aw_sar_chip_interface.h" +#include "aw_sar.h" + +#define AW_SAR_I2C_NAME "awinic_sar" +#define AW_SAR_DRIVER_VERSION "v0.1.5.14" +#define USB_POWER_SUPPLY_NAME "usb" + + + +#define AW_I2C_RW_RETRY_TIME_MIN (2000) +#define AW_I2C_RW_RETRY_TIME_MAX (3000) +#define AW_RETRIES (5) + +#define AW_SAR_AWRW_OffSET (20) +#define AW_SAR_AWRW_DATA_WIDTH (5) +#define AW_DATA_OffSET_2 (2) +#define AW_DATA_OffSET_3 (3) +#define AW_SAR_CH_MAX (20) +#define AW_POWER_ON_SYSFS_DELAY_MS (5000) +#define AW_SAR_OFFSET_LEN (15) +#define AW_SAR_VCC_MIN_UV (1700000) +#define AW_SAR_VCC_MAX_UV (3600000) + +static struct mutex aw_sar_lock; + +static int32_t aw_sar_get_chip_info(struct aw_sar *p_sar); +static void aw_sar_sensor_free(struct aw_sar *p_sar); + +//Because disable/enable_irq api Therefore, IRQ is embedded +void aw_sar_disable_irq(struct aw_sar *p_sar) +{ + if (p_sar->irq_init.host_irq_stat == IRQ_ENABLE) { + disable_irq(p_sar->irq_init.to_irq); + p_sar->irq_init.host_irq_stat = IRQ_DISABLE; + } +} + +void aw_sar_enable_irq(struct aw_sar *p_sar) +{ + if (p_sar->irq_init.host_irq_stat == IRQ_DISABLE) { + enable_irq(p_sar->irq_init.to_irq); + p_sar->irq_init.host_irq_stat = IRQ_ENABLE; + } +} + +//Chip logic part start +//Load default array function +static int32_t aw_sar_para_loaded_func(struct i2c_client *i2c, + const struct aw_sar_para_load_t *para_load) +{ + int32_t i = 0; + int32_t ret = 0; + + for (i = 0; i < para_load->reg_arr_len; i = i + 2) { + ret = aw_sar_i2c_write(i2c, (uint16_t)para_load->reg_arr[i], + para_load->reg_arr[i + 1]); + if (ret != AW_OK) { + return -AW_REG_LOAD_ERR; + } + AWLOGD(&i2c->dev,"reg_addr = 0x%04x, reg_data = 0x%08x", + para_load->reg_arr[i], + para_load->reg_arr[i + 1]); + } + + AWLOGD(&i2c->dev, "para writen completely"); + + return AW_OK; +} + +//Mode setting function +static void aw_sar_mode_set_func(struct i2c_client *i2c, int to_irq, struct aw_sar_mode_set_t *mode_set_para, + const struct aw_sar_mode_set_t *mode_set, uint8_t len) +{ + uint8_t i = 0; + + AWLOGD(&i2c->dev, "enter"); + + for(i = 0; i < len; i++) { + //AWLOGD(&i2c->dev, "i = %d mode_set:%d chip_id:%d", i, mode_set[i].chip_id, mode_set_para->chip_id); + if ((mode_set[i].chip_mode.curr_mode == mode_set_para->chip_mode.curr_mode) && + (mode_set[i].chip_mode.last_mode == mode_set_para->chip_mode.last_mode) && + ((mode_set[i].chip_id == AW_SAR_NONE_CHECK_CHIP) || + ((mode_set[i].chip_id & mode_set_para->chip_id) != 0))) { + if (mode_set[i].mode_switch_ops.enable_clock != NULL) { + mode_set[i].mode_switch_ops.enable_clock(i2c); + } + if (mode_set[i].mode_switch_ops.rc_irqscr != NULL) { + mode_set[i].mode_switch_ops.rc_irqscr(i2c); + } + if (mode_set[i].mode_switch_ops.mode_update != NULL) { + AWLOGD(&i2c->dev, "set curr_mode:%d, ok", mode_set_para->chip_mode.curr_mode); + mode_set[i].mode_switch_ops.mode_update(i2c); + } + break; + } + } +} + +static int32_t aw_sar_check_init_over_irq_func(struct i2c_client *i2c, + const struct aw_sar_init_over_irq_t *p_check_irq) +{ + int32_t ret = -AW_ERR; + uint32_t irq_stat = 0; + int16_t cnt = p_check_irq->wait_times; + + do { + ret = aw_sar_i2c_read(i2c, p_check_irq->reg_irqsrc, &irq_stat); + if (ret < 0) { + return -AW_ERR; + } + if (((irq_stat >> p_check_irq->irq_offset_bit) & p_check_irq->irq_mask) == p_check_irq->irq_flag) { + AWLOGD(&i2c->dev, "check_init_over_irqt ok!"); + return AW_OK; + } else { + AWLOGE(&i2c->dev, "init over irq no ok! val:0x%x cnt: %d", irq_stat, cnt); + } + mdelay(1); + } while (cnt--); + + if (cnt < 0) { + AWLOGE(&i2c->dev, "init over irq error!"); + } + + return -AW_ERR_IRQ_INIT_OVER; +} + +static int32_t aw_sar_soft_reset_func(struct i2c_client *i2c, const struct aw_sar_soft_rst_t *p_soft_rst) +{ + int32_t ret = 0; + + AWLOGD(&i2c->dev, "enter"); + + ret = aw_sar_i2c_write(i2c, p_soft_rst->reg_rst, p_soft_rst->reg_rst_val); + if (ret < 0) { + AWLOGE(&i2c->dev, "soft_reset error: %d", ret); + return -AW_ERR; + } + + msleep(p_soft_rst->delay_ms); + + return AW_OK; +} +//Chip logic part end + +static int32_t aw_sar_parse_bin(const struct firmware *cont, struct aw_sar *p_sar) +{ + struct aw_bin *aw_bin = NULL; + int32_t ret = -AW_ERR; + + if (!cont) { + AWLOGE(p_sar->dev, "def_reg_bin request error!"); + return -AW_ERR; + } else { + AWLOGI(p_sar->dev, "def_reg_bin request successfully"); + } + + AWLOGD(p_sar->dev, "Bin file size: %d", (uint32_t)cont->size); + + aw_bin = kzalloc(cont->size + sizeof(struct aw_bin), GFP_KERNEL); + if (!aw_bin) { + kfree(aw_bin); + release_firmware(cont); + AWLOGE(p_sar->dev, "failed to allcating memory!"); + return -AW_ERR; + } + + aw_bin->info.len = cont->size; + memcpy(aw_bin->info.data, cont->data, cont->size); + + if (cont != NULL) { + release_firmware(cont); + } + + ret = aw_sar_parsing_bin_file(aw_bin); + if (ret < 0) { + AWLOGE(p_sar->dev, "parse bin fail! ret = %d", ret); + goto err; + } + + //Write bin file execution process + if (p_sar->load_bin.bin_opera_func != NULL) { + ret = p_sar->load_bin.bin_opera_func(aw_bin, p_sar); + if (ret != AW_OK) { + AWLOGE(p_sar->dev, "load_bin_to_chip error!"); + if (p_sar->load_bin.bin_load_fail_opera_func != NULL) { + ret = p_sar->load_bin.bin_load_fail_opera_func(aw_bin, p_sar); + if (ret != AW_OK) { + AWLOGE(p_sar->dev, "bin_load_fail_opera_func error!"); + goto err; + } else { + AWLOGD(p_sar->dev, "bin_load_fail_opera_func OK!"); + } + } else { + goto err; + } + } + } else { + AWLOGE(p_sar->dev, "bin_opera_func is null error!"); + } + + if (aw_bin != NULL) { + kfree(aw_bin); + } + + AWLOGE(p_sar->dev, "load_def_reg_bin ok!!!"); + + return AW_OK; +err: + if (aw_bin != NULL) { + kfree(aw_bin); + } + return -AW_ERR; +} + +int32_t aw_sar_load_bin_comm(struct aw_sar *p_sar) +{ + int32_t ret = -AW_ERR; + const struct firmware *fw = NULL; + + AWLOGD(p_sar->dev, "name :%s", p_sar->load_bin.bin_name); + + ret = request_firmware(&fw, p_sar->load_bin.bin_name, p_sar->dev); + AWLOGD(p_sar->dev, "ret = %d", ret); + if (ret != AW_OK) { + AWLOGE(p_sar->dev, "parse %s error!", p_sar->load_bin.bin_name); + return -AW_ERR; + } else { + AWLOGD(p_sar->dev, "parse %s ok!", p_sar->load_bin.bin_name); + } + + ret = aw_sar_parse_bin(fw, p_sar); + if (ret != AW_OK) { + AWLOGE(p_sar->dev, "reg_bin %s load error!", p_sar->load_bin.bin_name); + return -AW_ERR; + } else { + AWLOGD(p_sar->dev, "reg_bin %s load ok!",p_sar->load_bin.bin_name); + } + + return AW_OK; +} + +int32_t aw_sar_parse_dts_comm(struct device *dev, struct device_node *np, struct aw_sar_dts_info *p_dts_info) +{ + int32_t val = 0; + + val = of_property_read_u32(np, "sar-num", &p_dts_info->sar_num); + if (val != 0) { + AWLOGE(dev, "multiple sar failed!"); + return -AW_ERR; + } else { + AWLOGI(dev, "sar num = %d", p_dts_info->sar_num); + } + + p_dts_info->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (p_dts_info->irq_gpio < 0) { + p_dts_info->irq_gpio = -1; + AWLOGE(dev, "no irq gpio provided."); + return -AW_ERR; + } else { + AWLOGI(dev, "irq gpio provided ok."); + } + + val = of_property_read_u32(np, "channel_use_flag", &p_dts_info->channel_use_flag); + if (val != 0) { + AWLOGE(dev, "channel_use_flag failed!"); + return -AW_ERR; + } else { + AWLOGI(dev, "channel_use_flag = 0x%x", p_dts_info->channel_use_flag); + } + + //GPIO is set as internal pull-up input + p_dts_info->use_inter_pull_up = of_property_read_bool(np, "aw_sar,pin_set_inter_pull-up"); + AWLOGI(dev, "aw_sar,use_inter_pull_up = <%d>", p_dts_info->use_inter_pull_up); + + p_dts_info->use_pm = of_property_read_bool(np, "aw_sar,using_pm_ops"); + AWLOGI(dev, "aw_sar,pin_set_inter_pull-up = <%d>", p_dts_info->use_pm); + + p_dts_info->update_fw_flag = of_property_read_bool(np, "aw_sar,update_fw"); + AWLOGI(dev, "aw_sar,update_fw = <%d>", p_dts_info->update_fw_flag); + + p_dts_info->use_plug_cail_flag = of_property_read_bool(np, "aw_sar,use_plug_cail"); + AWLOGI(dev, "aw_sar,use_plug_cail_flag = <%d>", p_dts_info->use_plug_cail_flag); + + return AW_OK; +} + +static int32_t aw_sar_parse_dts(struct aw_sar *p_sar) +{ + int32_t ret = 0; + + AWLOGD(p_sar->dev, "enter"); + + ret = aw_sar_parse_dts_comm(p_sar->dev, p_sar->i2c->dev.of_node, &p_sar->dts_info); + + //Special requirements of SAR chip + if (p_sar->p_sar_para->p_platform_config->p_add_parse_dts_fn != NULL) { + ret |= p_sar->p_sar_para->p_platform_config->p_add_parse_dts_fn(p_sar); + } + + return ret; +} + +static irqreturn_t aw_sar_irq(int32_t irq, void *data) +{ + struct aw_sar *p_sar = (struct aw_sar *)data; + uint32_t irq_status = 0; + + //step1: read clear interrupt + if (p_sar->p_sar_para->p_platform_config->p_irq_init->rc_irq_fn != NULL) { + irq_status = p_sar->p_sar_para->p_platform_config->p_irq_init->rc_irq_fn(p_sar->i2c); + } + + //step2: Read the status register for status reporting + if (p_sar->p_sar_para->p_platform_config->p_irq_init->irq_spec_handler_fn != NULL) { + p_sar->p_sar_para->p_platform_config->p_irq_init->irq_spec_handler_fn(irq_status, p_sar); + } + + return IRQ_HANDLED; +} + +static int32_t aw_sar_irq_init_comm(struct aw_sar *p_sar, const struct aw_sar_irq_init_t *p_irq_init) +{ + int32_t ret = 0; + irq_handler_t thread_fn = p_irq_init->thread_fn; + + snprintf(p_sar->irq_init.label, sizeof(p_sar->irq_init.label), + "aw_sar%d_gpio", p_sar->dts_info.sar_num); + snprintf(p_sar->irq_init.dev_id, sizeof(p_sar->irq_init.dev_id), + "aw_sar%d_irq", p_sar->dts_info.sar_num); + + if (gpio_is_valid(p_sar->dts_info.irq_gpio)) { + p_sar->irq_init.to_irq = gpio_to_irq(p_sar->dts_info.irq_gpio); + ret = devm_gpio_request_one(p_sar->dev, + p_sar->dts_info.irq_gpio, + p_irq_init->flags, + p_sar->irq_init.label); + if (ret) { + AWLOGE(p_sar->dev, + "request irq gpio failed, ret = %d", ret); + ret = -AW_ERR; + } else { + if (thread_fn == NULL) { + thread_fn = aw_sar_irq; + } + ret = devm_request_threaded_irq(p_sar->dev, + p_sar->irq_init.to_irq, + p_irq_init->handler, + thread_fn, + p_irq_init->irq_flags, + p_sar->irq_init.dev_id, + p_sar); + if (ret != 0) { + AWLOGE(p_sar->dev, + "failed to request IRQ %d: %d", + p_sar->irq_init.to_irq, ret); + ret = -AW_ERR; + } else { + AWLOGI(p_sar->dev, + "IRQ request successfully!"); + ret = AW_OK; + p_sar->intrrupt_init_state = AW_TRUE; + AWLOGI(p_sar->dev, + "%d", p_sar->ret_val ); + } + } + } else { + AWLOGE(p_sar->dev, "irq gpio invalid!"); + return -AW_ERR; + } + + AWLOGI(p_sar->dev, "disable_irq"); + p_sar->irq_init.host_irq_stat = IRQ_DISABLE; + disable_irq(p_sar->irq_init.to_irq); + + AWLOGD(p_sar->dev, "irq init success!"); + + return AW_OK; +} + +static int32_t aw_sar_irq_init(struct aw_sar *p_sar) +{ + AWLOGD(p_sar->dev, "aw_sar_irq_init enter!"); + + if (p_sar->p_sar_para->p_platform_config->p_irq_init == NULL) { + AWLOGE(p_sar->dev, "AW_INVALID_PARA"); + return AW_INVALID_PARA; + } + + if (p_sar->p_sar_para->p_platform_config->p_irq_init->p_irq_init_fn != NULL) { + AWLOGE(p_sar->dev, "p_irq_init_fn"); + return p_sar->p_sar_para->p_platform_config->p_irq_init->p_irq_init_fn(p_sar); + } + + return aw_sar_irq_init_comm(p_sar, p_sar->p_sar_para->p_platform_config->p_irq_init); +} + +static void aw_sar_irq_free(struct aw_sar *p_sar) +{ + if ((p_sar->p_sar_para->p_platform_config != NULL) && + (p_sar->p_sar_para->p_platform_config->p_irq_init != NULL) && + (p_sar->p_sar_para->p_platform_config->p_irq_init->p_irq_deinit_fn != NULL)) { + p_sar->p_sar_para->p_platform_config->p_irq_init->p_irq_deinit_fn(p_sar); + AWLOGE(p_sar->dev, "AW_INVALID_PARA"); + return; + } + AWLOGE(p_sar->dev, "aw_sar_irq_free"); + +} + +static int32_t aw_sar_input_init_comm(struct aw_sar *p_sar) +{ + uint32_t i = 0; + int32_t ret = 0; + + p_sar->channels_arr = devm_kzalloc(p_sar->dev, + sizeof(struct aw_channels_info) * + p_sar->p_sar_para->ch_num_max, + GFP_KERNEL); + if (p_sar->channels_arr == NULL) { + AWLOGE(p_sar->dev, "devm_kzalloc err"); + return -AW_ERR; + } + + for (i = 0; i < p_sar->p_sar_para->ch_num_max; i++) { + //snprintf(p_sar->channels_arr[i].name, + // sizeof(p_sar->channels_arr->name), + // "aw_sar%d_ch%d", + // p_sar->dts_info.sar_num, i); + snprintf(p_sar->channels_arr[i].name, + sizeof(p_sar->channels_arr->name), + "Moto CapSense Ch%d", i); + + p_sar->channels_arr[i].last_channel_info = 0; + + if ((p_sar->dts_info.channel_use_flag >> i) & 0x01) { + p_sar->channels_arr[i].used = AW_TRUE; + p_sar->channels_arr[i].input = input_allocate_device(); + if (p_sar->channels_arr[i].input == NULL) { + return -AW_ERR; + } + p_sar->channels_arr[i].input->name = p_sar->channels_arr[i].name; + __set_bit(EV_ABS, p_sar->channels_arr[i].input->evbit); + input_set_abs_params(p_sar->channels_arr[i].input, + ABS_DISTANCE, -1, 100, 0, 0); + ret = input_register_device(p_sar->channels_arr[i].input); + if (ret) { + AWLOGE(p_sar->dev, "failed to register input device"); + return -AW_ERR; + } + input_report_abs(p_sar->channels_arr[i].input, ABS_DISTANCE, -1); + input_sync(p_sar->channels_arr[i].input); + } else { + p_sar->channels_arr[i].used = AW_FALSE; + p_sar->channels_arr[i].input = NULL; + } + } + + return AW_OK; +} + +static int32_t aw_sar_input_init(struct aw_sar *p_sar) +{ + if (p_sar->p_sar_para->p_platform_config->p_input_init_fn != NULL) { + return p_sar->p_sar_para->p_platform_config->p_input_init_fn(p_sar); + } + + return aw_sar_input_init_comm(p_sar); +} + +static void aw_sar_input_free_comm(struct aw_sar *p_sar) +{ + uint8_t i = 0; + + for (i = 0; i < p_sar->p_sar_para->ch_num_max; i++) { + if (p_sar->channels_arr[i].input != NULL) { + input_unregister_device(p_sar->channels_arr[i].input); + input_free_device(p_sar->channels_arr[i].input); + } + } + AWLOGE(p_sar->dev, "aw_sar_input_free ok"); +} + +static void aw_sar_input_free(struct aw_sar *p_sar) +{ + if ((p_sar->p_sar_para->p_platform_config != NULL) && + (p_sar->p_sar_para->p_platform_config->p_input_deinit_fn != NULL)) { + p_sar->p_sar_para->p_platform_config->p_input_deinit_fn(p_sar); + AWLOGE(p_sar->dev, "AW_INVALID_PARA"); + return; + } + AWLOGE(p_sar->dev, "aw_sar_input_free_comm"); + + aw_sar_input_free_comm(p_sar); +} + +static int32_t aw_sar_check_init_over_irq_comm(struct aw_sar *p_sar) +{ + int32_t ret = 0; + + AWLOGD(p_sar->dev, "enter"); + + if (p_sar->p_sar_para->p_init_over_irq == NULL) { + return AW_INVALID_PARA; + } + + ret = aw_sar_check_init_over_irq_func(p_sar->i2c, p_sar->p_sar_para->p_init_over_irq); + if (ret == -AW_ERR_IRQ_INIT_OVER) { + AWLOGD(p_sar->dev, "ret is AW_ERR_IRQ_INIT_OVER"); + if (p_sar->p_sar_para->p_init_over_irq->p_get_err_type_fn != NULL) { + //Consider the abnormality reasonable + if (p_sar->p_sar_para->p_init_over_irq->p_get_err_type_fn(p_sar) == AW_OK) { + AWLOGD(p_sar->dev, "get_err_type return ok"); + p_sar->fw_fail_flag = AW_TRUE; + return AW_OK; + } + } + } + + return ret; +} + +//If there is no special operation on the chip, execute the common process +int32_t aw_sar_check_init_over_irq(struct aw_sar *p_sar) +{ + if (p_sar->p_sar_para->p_init_over_irq == NULL) { + return AW_INVALID_PARA; + } + + if (p_sar->p_sar_para->p_init_over_irq->p_check_init_over_irq_fn != NULL) { + return p_sar->p_sar_para->p_init_over_irq->p_check_init_over_irq_fn(p_sar); + } + + return aw_sar_check_init_over_irq_comm(p_sar); +} + +static int32_t aw_sar_chip_other_operation(struct aw_sar *p_sar) +{ + if (p_sar->p_sar_para->p_other_operation != NULL) { + return p_sar->p_sar_para->p_other_operation(p_sar); + } + + return AW_OK; +} + +static void aw_sar_chip_other_operation_free(struct aw_sar *p_sar) +{ + if (p_sar->p_sar_para->p_other_opera_free != NULL) { + p_sar->p_sar_para->p_other_opera_free(p_sar); + } +} + +int32_t aw_sar_soft_reset(struct aw_sar *p_sar) +{ + AWLOGD(p_sar->dev, "enter"); + + //If a private interface is defined, the private interface is used + if ((p_sar->p_sar_para->p_soft_rst != NULL) && + (p_sar->p_sar_para->p_soft_rst->p_soft_reset_fn != NULL)) { + return p_sar->p_sar_para->p_soft_rst->p_soft_reset_fn(p_sar); + } + + return aw_sar_soft_reset_func(p_sar->i2c, p_sar->p_sar_para->p_soft_rst); +} + +static int32_t aw_sar_check_chipid(struct aw_sar *p_sar) +{ + if (p_sar->p_sar_para == NULL) { + return AW_INVALID_PARA; + } + + if (p_sar->p_sar_para->p_check_chipid != NULL) { + if (p_sar->p_sar_para->p_check_chipid->p_check_chipid_fn != NULL) { + return p_sar->p_sar_para->p_check_chipid->p_check_chipid_fn(p_sar); + } + } + + return -AW_ERR; +} + +int32_t aw_sar_load_def_reg_bin(struct aw_sar *p_sar) +{ + AWLOGD(p_sar->dev, "enter"); + + if ((p_sar->p_sar_para->p_reg_bin == NULL) || + (p_sar->p_sar_para->p_reg_bin->bin_name == NULL)) { + AWLOGE(p_sar->dev, "p_reg_bin is NULL or bin_name is NULL error"); + p_sar->ret_val = AW_BIN_PARA_INVALID; + return AW_INVALID_PARA; + } + + snprintf(p_sar->load_bin.bin_name, sizeof(p_sar->load_bin.bin_name), + "%s_%d.bin", p_sar->p_sar_para->p_reg_bin->bin_name, p_sar->dts_info.sar_num); + + p_sar->load_bin.bin_opera_func = p_sar->p_sar_para->p_reg_bin->bin_opera_func; + + return aw_sar_load_bin_comm(p_sar); +} + +static int32_t aw_sar_para_loaded(struct aw_sar *p_sar) +{ + if (p_sar->p_sar_para->p_reg_arr == NULL) { + return AW_INVALID_PARA; + } + + aw_sar_para_loaded_func(p_sar->i2c, p_sar->p_sar_para->p_reg_arr); + + return AW_OK; +} + +static int32_t aw_sar_reg_update_boot_work(struct aw_sar *p_sar) +{ + AWLOGD(p_sar->dev, "enter"); + + if ((p_sar->p_sar_para->p_boot_bin == NULL) || (p_sar->p_sar_para->p_boot_bin->bin_name == NULL)) { + return AW_INVALID_PARA; + } + + snprintf(p_sar->load_bin.bin_name, sizeof(p_sar->load_bin.bin_name), + "%s_%d.bin", p_sar->p_sar_para->p_boot_bin->bin_name, p_sar->dts_info.sar_num); + + p_sar->load_bin.bin_opera_func = p_sar->p_sar_para->p_boot_bin->bin_opera_func; + + return aw_sar_load_bin_comm(p_sar); +} + +static int32_t aw_sar_update_fw_para(struct aw_sar *p_sar, const struct aw_sar_load_bin_t *p_bin) +{ + if ((p_bin == NULL) || (p_bin->bin_name == NULL)) { + return AW_INVALID_PARA; + } + + p_sar->load_bin.bin_opera_func = p_bin->bin_opera_func; + p_sar->load_bin.bin_load_fail_opera_func = p_bin->bin_load_fail_opera; + + snprintf(p_sar->load_bin.bin_name, sizeof(p_sar->load_bin.bin_name), + "%s_%d.bin", p_bin->bin_name, p_sar->dts_info.sar_num); + + return AW_OK; +} + +int32_t aw_sar_update_fw(struct aw_sar *p_sar) +{ + AWLOGD(p_sar->dev, "enter"); + + if (aw_sar_update_fw_para(p_sar, p_sar->p_sar_para->p_fw_bin) != AW_OK) { + return -AW_ERR; + } + + return aw_sar_load_bin_comm(p_sar); +} + +static int32_t aw_sar_node_prox_update_fw(struct aw_sar *p_sar) +{ + AWLOGD(p_sar->dev, "enter"); + + if (aw_sar_update_fw_para(p_sar, p_sar->p_sar_para->p_prox_fw) != AW_OK) { + return -AW_ERR; + } + + return aw_sar_load_bin_comm(p_sar); +} + +static int32_t aw_sar_node_reg_update_fw(struct aw_sar *p_sar) +{ + AWLOGD(p_sar->dev, "enter"); + + if (aw_sar_update_fw_para(p_sar, p_sar->p_sar_para->p_reg_fw)) { + return -AW_ERR; + } + + return aw_sar_load_bin_comm(p_sar); +} + +static int32_t aw_sar_awrw_data_analysis(struct aw_sar *p_sar, + const char *buf, uint8_t len) +{ + uint32_t i = 0 ; + uint8_t data_temp[2] = { 0 }; + uint8_t index = 0; + uint32_t tranfar_data_temp = 0; + uint32_t theory_len = len * AW_SAR_AWRW_DATA_WIDTH + AW_SAR_AWRW_OffSET; + uint32_t actual_len = strlen(buf); + + AWLOGD(p_sar->dev, "enter"); + + if (theory_len != actual_len) { + AWLOGD(p_sar->dev, + "error theory_len = %d actual_len = %d", + theory_len, actual_len); + return -AW_ERR; + } + + for (i = 0; i < len * AW_SAR_AWRW_DATA_WIDTH; + i += AW_SAR_AWRW_DATA_WIDTH) { + data_temp[0] = buf[AW_SAR_AWRW_OffSET + i + AW_DATA_OffSET_2]; + data_temp[1] = buf[AW_SAR_AWRW_OffSET + i + AW_DATA_OffSET_3]; + + if (sscanf(data_temp, "%02x", &tranfar_data_temp) == 1) { + p_sar->awrw_info.p_i2c_tranfar_data[index] = + (uint8_t)tranfar_data_temp; + AWLOGD(p_sar->dev, "tranfar_data = 0x%2x", + p_sar->awrw_info.p_i2c_tranfar_data[index]); + } + index++; + } + + return 0; +} + +static int32_t aw_sar_awrw_write(struct aw_sar *p_sar, const char *buf) +{ + int32_t ret = 0; + + ret = aw_sar_awrw_data_analysis(p_sar, buf, + p_sar->awrw_info.i2c_tranfar_data_len); + if (ret == 0) { + aw_sar_i2c_write_seq(p_sar->i2c, p_sar->awrw_info.p_i2c_tranfar_data, + p_sar->awrw_info.i2c_tranfar_data_len); + } + + return ret; +} + +static int32_t aw_sar_awrw_read(struct aw_sar *p_sar, const char *buf) +{ + int32_t ret = 0; + uint8_t *p_buf = p_sar->awrw_info.p_i2c_tranfar_data + p_sar->awrw_info.addr_len; + uint32_t len = (uint16_t)(p_sar->awrw_info.data_len * p_sar->awrw_info.reg_num); + + ret = aw_sar_awrw_data_analysis(p_sar, buf, p_sar->awrw_info.addr_len); + if (ret == 0) { + ret = aw_sar_i2c_read_seq(p_sar->i2c, + p_sar->awrw_info.p_i2c_tranfar_data, + p_sar->awrw_info.addr_len, + p_sar->awrw_info.p_i2c_tranfar_data + p_sar->awrw_info.addr_len, + (uint16_t)(p_sar->awrw_info.data_len * p_sar->awrw_info.reg_num)); + if (ret != AW_OK) { + memset(p_buf, 0xff, len); + } + } + + return ret; +} + +static int32_t aw_sar_awrw_get_func(struct aw_sar *p_sar, char *buf) +{ + uint32_t len = 0; + uint i = 0; + + if (p_sar->awrw_info.p_i2c_tranfar_data == NULL) { + AWLOGE(p_sar->dev, "p_i2c_tranfar_data is NULL"); + return len; + } + + if (p_sar->awrw_info.rw_flag == AW_SAR_PACKAGE_RD) { + for (i = 0; i < p_sar->awrw_info.i2c_tranfar_data_len; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%02x,", + p_sar->awrw_info.p_i2c_tranfar_data[i]); + } + } else { + for (i = 0; i < (p_sar->awrw_info.data_len) * + (p_sar->awrw_info.reg_num); i++) { + len += snprintf(buf + len, PAGE_SIZE - len, + "0x%02x,", + p_sar->awrw_info.p_i2c_tranfar_data[p_sar->awrw_info.addr_len + i]); + } + } + snprintf(buf + len - 1, PAGE_SIZE - len, "\n"); + + devm_kfree(p_sar->dev, p_sar->awrw_info.p_i2c_tranfar_data); + p_sar->awrw_info.p_i2c_tranfar_data = NULL; + + return len; +} + +//Function: continuous read register interface +static ssize_t aw_sar_awrw_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw_sar *p_sar = dev_get_drvdata(dev); + ssize_t ret = 0; + + mutex_lock(&aw_sar_lock); + if ((p_sar->p_sar_para->p_aw_sar_awrw != NULL) && + (p_sar->p_sar_para->p_aw_sar_awrw->p_get_awrw_node_fn != NULL)) { + ret = (ssize_t)p_sar->p_sar_para->p_aw_sar_awrw->p_get_awrw_node_fn(p_sar, buf); + mutex_unlock(&aw_sar_lock); + return ret; + } + + ret = (ssize_t)aw_sar_awrw_get_func(p_sar, buf); + + mutex_unlock(&aw_sar_lock); + + return ret; +} + +static int32_t aw_sar_awrw_handle(struct aw_sar *p_sar, const char *buf) +{ + int32_t ret = 0; + + p_sar->awrw_info.i2c_tranfar_data_len = + p_sar->awrw_info.addr_len + + p_sar->awrw_info.data_len * + p_sar->awrw_info.reg_num; + if (p_sar->awrw_info.p_i2c_tranfar_data != NULL) { + devm_kfree(p_sar->dev, p_sar->awrw_info.p_i2c_tranfar_data); + p_sar->awrw_info.p_i2c_tranfar_data = NULL; + } + + p_sar->awrw_info.p_i2c_tranfar_data = + devm_kzalloc(p_sar->dev, + p_sar->awrw_info.i2c_tranfar_data_len, GFP_KERNEL); + if (p_sar->awrw_info.p_i2c_tranfar_data == NULL) { + AWLOGE(p_sar->dev, "devm_kzalloc error"); + return -AW_ERR; + } + + if (p_sar->awrw_info.rw_flag == AW_SAR_I2C_WR) { + ret = aw_sar_awrw_write(p_sar, buf); + if (ret != 0) + AWLOGE(p_sar->dev, "awrw_write error"); + if (p_sar->awrw_info.p_i2c_tranfar_data != NULL) { + devm_kfree(p_sar->dev, + p_sar->awrw_info.p_i2c_tranfar_data); + p_sar->awrw_info.p_i2c_tranfar_data = NULL; + } + } else if (p_sar->awrw_info.rw_flag == AW_SAR_I2C_RD) { + ret = aw_sar_awrw_read(p_sar, buf); + if (ret != 0) + AWLOGE(p_sar->dev, "awrw_read error"); + } else { + return -AW_ERR; + } + + return AW_OK; +} + +static int32_t aw_sar_awrw_set_func(struct aw_sar *p_sar, const char *buf) +{ + uint32_t rw_flag = 0; + uint32_t addr_bytes = 0; + uint32_t data_bytes = 0; + uint32_t package_nums = 0; + uint32_t reg_num = 0; + uint32_t i = 0; + uint32_t j = 0; + uint32_t addr_tmp = 0; + uint32_t buf_index0 = 0; + uint32_t buf_index1 = 0; + uint32_t r_buf_len = 0; + uint32_t tr_offset = 0; + uint8_t addr[4] = { 0 }; + uint32_t theory_len = 0; + uint32_t actual_len = 0; + + //step1: Parse frame header + if (sscanf(buf, "0x%02x 0x%02x 0x%02x ", &rw_flag, &addr_bytes, &data_bytes) != 3) { + AWLOGE(p_sar->dev, "sscanf0 parse error!"); + return -AW_ERR; + } + p_sar->awrw_info.rw_flag = (uint8_t)rw_flag; + p_sar->awrw_info.addr_len = (uint8_t)addr_bytes; + p_sar->awrw_info.data_len = (uint8_t)data_bytes; + + if (addr_bytes > 4) { + return -AW_ERR; + AWLOGE(p_sar->dev, "para error!"); + } + + if ((rw_flag == AW_SAR_I2C_WR) || (rw_flag == AW_SAR_I2C_RD)) { + if (sscanf(buf + AW_SAR_OFFSET_LEN, "0x%02x ", ®_num) != 1) { + AWLOGE(p_sar->dev, "sscanf1 parse error!"); + return -AW_ERR; + } + p_sar->awrw_info.reg_num = (uint8_t)reg_num; + aw_sar_awrw_handle(p_sar, buf); + } else if (rw_flag == AW_SAR_PACKAGE_RD){ + //step2: Get number of packages + if (sscanf(buf + AW_SAR_OFFSET_LEN, "0x%02x ", &package_nums) != 1) { + AWLOGE(p_sar->dev, "sscanf2 parse error!"); + return -AW_ERR; + } + theory_len = AW_SAR_OFFSET_LEN + AW_SAR_AWRW_DATA_WIDTH + + package_nums * (AW_SAR_AWRW_DATA_WIDTH + AW_SAR_AWRW_DATA_WIDTH * addr_bytes); + actual_len = strlen(buf); + // AWLOGD(p_sar->dev, "theory_len:%d, actual_len:%d", theory_len, actual_len); + if (theory_len != actual_len) { + + AWLOGE(p_sar->dev, "theory_len:%d, actual_len:%d error!", theory_len, actual_len); + return -AW_ERR; + } + + //step3: Get the size of read data and apply for space + // AWLOGE(p_sar->dev, "package_nums:%d", package_nums); + for (i = 0; i < package_nums; i++) { + buf_index0 = AW_SAR_OFFSET_LEN + AW_SAR_AWRW_DATA_WIDTH + + (AW_SAR_AWRW_DATA_WIDTH * addr_bytes + AW_SAR_AWRW_DATA_WIDTH) * i; + if (sscanf(buf + buf_index0, "0x%02x", ®_num) != 1) { + AWLOGE(p_sar->dev, "sscanf3 parse error!"); + return -AW_ERR; + } + //AWLOGE(p_sar->dev, "reg_num:%d", reg_num); + r_buf_len += reg_num * data_bytes; + } + + // AWLOGD(p_sar->dev, "r_buf_len:%d", r_buf_len); + p_sar->awrw_info.i2c_tranfar_data_len = r_buf_len; + + if (p_sar->awrw_info.p_i2c_tranfar_data != NULL) { + devm_kfree(p_sar->dev, p_sar->awrw_info.p_i2c_tranfar_data); + p_sar->awrw_info.p_i2c_tranfar_data = NULL; + } + p_sar->awrw_info.p_i2c_tranfar_data = devm_kzalloc(p_sar->dev, + r_buf_len, GFP_KERNEL); + if (p_sar->awrw_info.p_i2c_tranfar_data == NULL) { + AWLOGE(p_sar->dev, "devm_kzalloc error"); + return -AW_ERR; + } + + //step3: Resolve register address and read data in packets + for (i = 0; i < package_nums; i++) { + buf_index0 = AW_SAR_OFFSET_LEN + AW_SAR_AWRW_DATA_WIDTH + + (AW_SAR_AWRW_DATA_WIDTH * addr_bytes + AW_SAR_AWRW_DATA_WIDTH) * i; + if (sscanf(buf + buf_index0, "0x%02x", ®_num) != 1) { + AWLOGE(p_sar->dev, "sscanf4 parse error!"); + return -AW_ERR; + } + + for (j = 0; j < addr_bytes; j += 1) { + buf_index1 = buf_index0 + AW_SAR_AWRW_DATA_WIDTH + (j * AW_SAR_AWRW_DATA_WIDTH); + // AWLOGD(p_sar->dev, "buf_index1 = %d", buf_index1); + if (sscanf(buf + buf_index1, "0x%02x", &addr_tmp) == 1) { + addr[j] = (uint8_t)addr_tmp; + //AWLOGD(p_sar->dev, "tranfar_data = 0x%2x", addr[j]); + } else { + AWLOGE(p_sar->dev, "sscanf5 parse error!"); + return -AW_ERR; + } + } + // AWLOGD(p_sar->dev, "tr_offset = %d", tr_offset); + aw_sar_i2c_read_seq(p_sar->i2c, + addr, + addr_bytes, + p_sar->awrw_info.p_i2c_tranfar_data + tr_offset, + (uint16_t)(data_bytes * reg_num)); + tr_offset += data_bytes * reg_num; + } + } + + return AW_OK; +} + +//Function: continuous write register interface +static ssize_t aw_sar_awrw_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw_sar *p_sar = dev_get_drvdata(dev); + + mutex_lock(&aw_sar_lock); + + if ((p_sar->p_sar_para->p_aw_sar_awrw != NULL) && + (p_sar->p_sar_para->p_aw_sar_awrw->p_set_awrw_node_fn != NULL)) { + p_sar->p_sar_para->p_aw_sar_awrw->p_set_awrw_node_fn (p_sar, buf, count); + mutex_unlock(&aw_sar_lock); + return count; + } + + aw_sar_awrw_set_func(p_sar, buf); + + mutex_unlock(&aw_sar_lock); + + return count; +} +//Print all readable register values +static ssize_t aw_sar_reg_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw_sar *p_sar = dev_get_drvdata(dev); + ssize_t len = 0; + uint16_t i = 0; + uint32_t reg_data = 0; + int32_t ret = 0; + uint8_t reg_rd_access = 0; + + if (p_sar->p_sar_para->p_reg_list == NULL) { + return len; + } + + reg_rd_access = p_sar->p_sar_para->p_reg_list->reg_rd_access; + + for (i = 0; i < p_sar->p_sar_para->p_reg_list->reg_num; i++) { + if (p_sar->p_sar_para->p_reg_list->reg_perm[i].rw & reg_rd_access) { + ret = aw_sar_i2c_read(p_sar->i2c, p_sar->p_sar_para->p_reg_list->reg_perm[i].reg, ®_data); + if (ret < 0) { + len += snprintf(buf + len, PAGE_SIZE - len, + "i2c read error ret = %d\n", ret); + } + len+= snprintf(buf + len, PAGE_SIZE - len, + "%x,%x\n", + p_sar->p_sar_para->p_reg_list->reg_perm[i].reg, + reg_data); + } + } + AWLOGD(p_sar->dev, "len %d", (int)len); + + return len; +} + +//Write register interface with write permission +static ssize_t aw_sar_reg_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint16_t i = 0; + struct aw_sar *p_sar = dev_get_drvdata(dev); + uint32_t databuf[2] = { 0, 0 }; + uint8_t reg_wd_access = 0; + + if (p_sar->p_sar_para->p_reg_list == NULL) { + AWLOGE(p_sar->dev, "AW_INVALID_PARA"); + return count; + } + + reg_wd_access = p_sar->p_sar_para->p_reg_list->reg_wd_access; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) != 2) + return count; + + for (i = 0; i < p_sar->p_sar_para->p_reg_list->reg_num; i++) { + if ((uint16_t)databuf[0] == p_sar->p_sar_para->p_reg_list->reg_perm[i].reg) { + if (p_sar->p_sar_para->p_reg_list->reg_perm[i].rw & reg_wd_access) { + aw_sar_i2c_write(p_sar->i2c, + (uint16_t)databuf[0], (uint32_t)databuf[1]); + } + break; + } + } + + return count; +} + +//set chip Soft reset +static ssize_t aw_sar_soft_rst_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t flag = 0; + struct aw_sar *p_sar = dev_get_drvdata(dev); + + if (sscanf(buf, "%d", &flag) != 1) { + AWLOGE(p_sar->dev, "sscanf parse err"); + return count; + } + + if (flag == AW_TRUE) { + aw_sar_soft_reset(p_sar); + } + + return count; +} + +static int32_t aw_sar_aot(struct aw_sar *p_sar) +{ + if (p_sar->p_sar_para->p_aot == NULL) { + return AW_INVALID_PARA; + } + + if (p_sar->p_sar_para->p_aot->p_set_aot_node_fn != NULL) { + return p_sar->p_sar_para->p_aot->p_set_aot_node_fn(p_sar); + } + + return aw_sar_i2c_write_bits(p_sar->i2c, p_sar->p_sar_para->p_aot->aot_reg, + p_sar->p_sar_para->p_aot->aot_mask, p_sar->p_sar_para->p_aot->aot_flag); +} + +//Perform Auto-Offset-Tuning (AOT) +static ssize_t aw_sar_aot_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw_sar *p_sar = dev_get_drvdata(dev); + uint32_t cali_flag = 0; + + if (sscanf(buf, "%d", &cali_flag) != 1) + return count; + + if (cali_flag == AW_TRUE) { + aw_sar_aot(p_sar); + } else { + AWLOGE(p_sar->dev, "fail to set aot cali"); + } + + return count; +} + +//update Register configuration and set the chip active mode +int32_t aw_sar_update_reg_set_func(struct aw_sar *p_sar) +{ + AWLOGD(p_sar->dev, "enter"); + + aw_sar_load_def_reg_bin(p_sar); + aw_sar_mode_set(p_sar, p_sar->p_sar_para->p_chip_mode->active); + + return AW_OK; +} + +//Update register configuration +static ssize_t aw_sar_update_reg_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + uint32_t flag = 0; + struct aw_sar *p_sar = dev_get_drvdata(dev); + + if (sscanf(buf, "%d", &flag) != 1) { + AWLOGE(p_sar->dev, "sscanf parse error"); + return count; + } + + if (flag == AW_TRUE) { + mutex_lock(&aw_sar_lock); + aw_sar_soft_reset(p_sar); + aw_sar_update_reg_set_func(p_sar); + mutex_unlock(&aw_sar_lock); + } + + return count; +} + +//get chip diff val +static ssize_t aw_sar_get_diff(struct aw_sar *p_sar, char *buf) +{ + uint32_t i = 0; + int32_t ret = 0; + ssize_t len = 0; + uint32_t data = 0; + int32_t diff_val = 0; + const struct aw_sar_diff_t *diff = p_sar->p_sar_para->p_diff; + + if (p_sar->p_sar_para->p_diff == NULL) { + return AW_INVALID_PARA; + } + + //If a private interface is defined, the private interface is used + if (p_sar->p_sar_para->p_diff->p_get_diff_node_fn != NULL) { + return p_sar->p_sar_para->p_diff->p_get_diff_node_fn(p_sar, buf); + } + + for (i = 0; i < p_sar->p_sar_para->ch_num_max; i++) { + ret = aw_sar_i2c_read(p_sar->i2c, diff->diff0_reg + i * diff->diff_step, &data); + if (ret != AW_OK) { + AWLOGE(p_sar->dev, "read diff err: %d", ret); + return -AW_ERR; + } + diff_val = (int32_t)data / (int32_t)diff->rm_float; + len += snprintf(buf + len, PAGE_SIZE - len, "DIFF_CH%d = %d\n", i, diff_val); + } + + return len; +} + + +//Print diff values of all channels of the chip. +static ssize_t aw_sar_diff_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw_sar *p_sar = dev_get_drvdata(dev); + + return aw_sar_get_diff(p_sar, buf); +} + +void aw_sar_mode_set(struct aw_sar *p_sar, uint8_t curr_mode) +{ + struct aw_sar_mode_set_t mode_set_para; + + if (p_sar->p_sar_para->p_mode == NULL) { + return; + } + + //If a private interface is defined, the private interface is used + if (p_sar->p_sar_para->p_mode->p_set_mode_node_fn != NULL) { + p_sar->p_sar_para->p_mode->p_set_mode_node_fn(p_sar, curr_mode); + return; + } + + mode_set_para.chip_id = p_sar->chip_type; + mode_set_para.chip_mode.curr_mode = curr_mode; + mode_set_para.chip_mode.last_mode = p_sar->last_mode; + + aw_sar_mode_set_func(p_sar->i2c, p_sar->irq_init.to_irq, &mode_set_para, + p_sar->p_sar_para->p_mode->mode_set_arr, p_sar->p_sar_para->p_mode->mode_set_arr_len); + p_sar->last_mode = curr_mode; +} + +//Set the chip to enter different modes +static ssize_t aw_sar_mode_operation_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw_sar *p_sar = dev_get_drvdata(dev); + uint32_t mode = 0; + + if (sscanf(buf, "%d", &mode) != 1) { + AWLOGE(p_sar->dev, "sscanf parse err"); + return count; + } + aw_sar_mode_set(p_sar, mode); + + return count; +} + +//Get the current mode of the chip +static ssize_t aw_sar_mode_operation_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + struct aw_sar *p_sar = dev_get_drvdata(dev); + + if (p_sar->p_sar_para->p_mode == NULL) { + return len; + } + + if (p_sar->p_sar_para->p_mode->p_get_mode_node_fn != NULL) { + len = p_sar->p_sar_para->p_mode->p_get_mode_node_fn(p_sar, buf); + } + + return len; +} + +//Print information related information +static ssize_t aw_sar_chip_info_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw_sar *p_sar = dev_get_drvdata(dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, "driver version: %s\n", AW_SAR_DRIVER_VERSION); + len += snprintf(buf + len, PAGE_SIZE - len, "reg_load_state: %d\n", p_sar->ret_val); + + if ((p_sar->p_sar_para->p_get_chip_info != NULL) && + (p_sar->p_sar_para->p_get_chip_info->p_get_chip_info_node_fn != NULL)) { + p_sar->p_sar_para->p_get_chip_info->p_get_chip_info_node_fn(p_sar, buf, &len); + } + + return len; +} + +static ssize_t aw_sar_offset_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw_sar *p_sar = dev_get_drvdata(dev); + ssize_t len = 0; + + if ((p_sar->p_sar_para->p_offset != NULL) && + (p_sar->p_sar_para->p_offset->p_get_offset_node_fn != NULL)) { + len = (ssize_t)p_sar->p_sar_para->p_offset->p_get_offset_node_fn(p_sar, buf); + } + + return len; +} + +static DEVICE_ATTR(awrw, 0664, aw_sar_awrw_get, aw_sar_awrw_set); +static DEVICE_ATTR(reg, 0664, aw_sar_reg_get, aw_sar_reg_set); +static DEVICE_ATTR(soft_rst, 0664, NULL, aw_sar_soft_rst_set); +static DEVICE_ATTR(aot, 0664, NULL, aw_sar_aot_set); +static DEVICE_ATTR(update_reg, 0664, NULL, aw_sar_update_reg_set); +static DEVICE_ATTR(diff, 0664, aw_sar_diff_get, NULL); +static DEVICE_ATTR(mode_operation, 0664, aw_sar_mode_operation_get, aw_sar_mode_operation_set); +static DEVICE_ATTR(chip_info, 0664, aw_sar_chip_info_get, NULL); +static DEVICE_ATTR(offset, 0664, aw_sar_offset_get, NULL); + +static struct attribute *aw_sar_attributes[] = { + &dev_attr_awrw.attr, + &dev_attr_reg.attr, + &dev_attr_soft_rst.attr, + &dev_attr_aot.attr, + &dev_attr_update_reg.attr, + &dev_attr_diff.attr, + &dev_attr_mode_operation.attr, + &dev_attr_chip_info.attr, + &dev_attr_offset.attr, + NULL +}; + +const static struct attribute_group aw_sar_attribute_group = { + .attrs = aw_sar_attributes, +}; + +//firmware upgrade through write register mode, and the operation is supported by flash chip +static ssize_t aw_sar_prot_update_fw_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw_sar *p_sar = dev_get_drvdata(dev); + + + mutex_lock(&aw_sar_lock); + aw_sar_disable_irq(p_sar); + + AWLOGD(p_sar->dev, "enter"); + p_sar->prot_update_state = aw_sar_node_prox_update_fw(p_sar); + + aw_sar_enable_irq(p_sar); + mutex_unlock(&aw_sar_lock); + + return count; +} + +//firmware upgrade throughr Write register mode,and the operation is supported by flash chip +static ssize_t aw_sar_reg_update_fw_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw_sar *p_sar = dev_get_drvdata(dev); + + mutex_lock(&aw_sar_lock); + aw_sar_disable_irq(p_sar); + + AWLOGD(p_sar->dev, "enter"); + aw_sar_node_reg_update_fw(p_sar); + + aw_sar_enable_irq(p_sar); + mutex_unlock(&aw_sar_lock); + + return count; +} + +//boot upgrade throughr Write register mode,and the operation is supported by flash chip +static ssize_t aw_sar_reg_update_boot_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct aw_sar *p_sar = dev_get_drvdata(dev); + + if (p_sar->p_sar_para->p_boot_bin == NULL) { + return count; + } + + mutex_lock(&aw_sar_lock); + aw_sar_disable_irq(p_sar); + + AWLOGD(p_sar->dev, "enter"); + + aw_sar_reg_update_boot_work(p_sar); + + aw_sar_enable_irq(p_sar); + mutex_unlock(&aw_sar_lock); + + return count; +} + +//Print the protocol upgrade status. 0 is success, Not 0 is failure +//Print the current firmware version number +static ssize_t aw_sar_prot_update_fw_get(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw_sar *p_sar = dev_get_drvdata(dev); + ssize_t len = 0; + + len += snprintf(buf + len, PAGE_SIZE - len, + "protocol update state:%s!\n", + (p_sar->prot_update_state == 0) ? "ok" : "error"); + if ((p_sar->p_sar_para->p_prox_fw != NULL) && + (p_sar->p_sar_para->p_prox_fw->p_get_prot_update_fw_node_fn != NULL)) { + p_sar->p_sar_para->p_prox_fw->p_get_prot_update_fw_node_fn(p_sar, buf, &len); + } + + return len; +} + +static DEVICE_ATTR(prot_update_fw, 0664, aw_sar_prot_update_fw_get, aw_sar_prot_update_fw_set); +static DEVICE_ATTR(reg_update_fw, 0664, NULL, aw_sar_reg_update_fw_set); +static DEVICE_ATTR(reg_update_boot, 0664, NULL, aw_sar_reg_update_boot_set); + +static struct attribute *aw_sar_update_attributes[] = { + &dev_attr_prot_update_fw.attr, + &dev_attr_reg_update_fw.attr, + &dev_attr_reg_update_boot.attr, + NULL +}; + +const static struct attribute_group aw_sar_update_attribute_group = { + .attrs = aw_sar_update_attributes, +}; + +static void aw_sar_update_work(struct work_struct *work) +{ + int32_t ret = 0; + struct aw_sar *p_sar = container_of(work, struct aw_sar, update_work.work); + + mutex_lock(&aw_sar_lock); + + if (p_sar->dts_info.update_fw_flag == true) { + AWLOGI(p_sar->dev, "firmware upgrade start!"); + ret = aw_sar_update_fw(p_sar); + if (ret != AW_OK) { + AWLOGE(p_sar->dev, "protocol upgrade firmware error!"); + p_sar->ret_val = AW_PROT_UPDATE_ERR; + } else { + AWLOGI(p_sar->dev, "protocol upgrade firmware ok!"); + } + } + + //2.Parse the bin file and load the register configuration + ret = aw_sar_load_def_reg_bin(p_sar); + if (ret != AW_OK) { + p_sar->ret_val = AW_REG_LOAD_ERR; + AWLOGE(p_sar->dev, "reg_bin load err!"); + aw_sar_para_loaded(p_sar); + } + + //3.active chip + aw_sar_mode_set(p_sar, p_sar->p_sar_para->p_chip_mode->init_mode); + if (p_sar->irq_init.host_irq_stat == IRQ_DISABLE) { + enable_irq(p_sar->irq_init.to_irq); + p_sar->irq_init.host_irq_stat = IRQ_ENABLE; + } + + mutex_unlock(&aw_sar_lock); + + return; +} + +static void aw_sar_update(struct aw_sar *p_sar) +{ + AWLOGD(p_sar->dev, "enter"); + + if (p_sar->p_sar_para->p_reg_bin == NULL) { + return; + } + + if (p_sar->p_sar_para->p_reg_bin->p_update_fn != NULL) { + p_sar->p_sar_para->p_reg_bin->p_update_fn(p_sar); + } + + INIT_DELAYED_WORK(&p_sar->update_work, aw_sar_update_work); + schedule_delayed_work(&p_sar->update_work, + msecs_to_jiffies(AW_POWER_ON_SYSFS_DELAY_MS)); +} + +static int32_t aw_sar_create_node(struct aw_sar *p_sar) +{ + int32_t ret = 0; + + AWLOGD(p_sar->dev, "enter"); + + i2c_set_clientdata(p_sar->i2c, p_sar); + + ret = sysfs_create_group(&p_sar->i2c->dev.kobj, &aw_sar_attribute_group); + + if (p_sar->dts_info.update_fw_flag == true) { + ret |= sysfs_create_group(&p_sar->i2c->dev.kobj, &aw_sar_update_attribute_group); + } + + //Special requirements of SAR chip + if (p_sar->p_sar_para->p_platform_config->p_add_node_create_fn != NULL) { + ret |= p_sar->p_sar_para->p_platform_config->p_add_node_create_fn(p_sar); + } + + return ret; +} + +static void aw_sar_node_free(struct aw_sar *p_sar) +{ + sysfs_remove_group(&p_sar->i2c->dev.kobj, &aw_sar_attribute_group); + + if (p_sar->dts_info.update_fw_flag == true) { + sysfs_remove_group(&p_sar->i2c->dev.kobj, &aw_sar_update_attribute_group); + } + //Special requirements of SAR chip + if ((p_sar->p_sar_para->p_platform_config != NULL) && + (p_sar->p_sar_para->p_platform_config->p_add_node_free_fn != NULL)) { + p_sar->p_sar_para->p_platform_config->p_add_node_free_fn(p_sar); + } +} + +//The interrupt pin is set to internal pull-up start +void aw_sar_int_output(struct aw_sar *p_sar, int32_t level) +{ + pr_info("%s enter aw_sar int level:%d\n", __func__, level); + if (level == 0) { + if (p_sar->pinctrl.pinctrl) { + pinctrl_select_state(p_sar->pinctrl.pinctrl, + p_sar->pinctrl.int_out_low); + } else { + pr_info("%s Failed set int pin output low\n", __func__); + } + } else if (level == 1) { + if (p_sar->pinctrl.pinctrl) { + pinctrl_select_state(p_sar->pinctrl.pinctrl, + p_sar->pinctrl.int_out_high); + } else { + pr_info("%s Failed set int pin output high\n", __func__); + } + } +} + +static int32_t aw_sar_pinctrl_init(struct aw_sar *p_sar) +{ + struct aw_sar_pinctrl *pinctrl = &p_sar->pinctrl; + uint8_t pin_default_name[50] = { 0 }; + uint8_t pin_output_low_name[50] = { 0 }; + uint8_t pin_output_high_name[50] = { 0 }; + + AWLOGD(p_sar->dev, "enter"); + + pinctrl->pinctrl = devm_pinctrl_get(p_sar->dev); + if (IS_ERR_OR_NULL(pinctrl->pinctrl)) { + pr_info("%s:No pinctrl found\n", __func__); + pinctrl->pinctrl = NULL; + return -EINVAL; + } + + snprintf(pin_default_name, sizeof(pin_default_name), + "aw_default_sar%d", p_sar->dts_info.sar_num); + AWLOGD(p_sar->dev, "pin_default_name = %s", pin_default_name); + pinctrl->default_sta = pinctrl_lookup_state(pinctrl->pinctrl, + pin_default_name); + if (IS_ERR_OR_NULL(pinctrl->default_sta)) { + AWLOGE(p_sar->dev, "Failed get pinctrl state:default state"); + goto exit_pinctrl_init; + } + + snprintf(pin_output_high_name, sizeof(pin_output_high_name), + "aw_int_output_high_sar%d", p_sar->dts_info.sar_num); + AWLOGD(p_sar->dev, "pin_output_high_name = %s", pin_output_high_name); + pinctrl->int_out_high = pinctrl_lookup_state(pinctrl->pinctrl, + pin_output_high_name); + if (IS_ERR_OR_NULL(pinctrl->int_out_high)) { + AWLOGE(p_sar->dev, "Failed get pinctrl state:output_high"); + goto exit_pinctrl_init; + } + + snprintf(pin_output_low_name, sizeof(pin_output_low_name), + "aw_int_output_low_sar%d", p_sar->dts_info.sar_num); + AWLOGD(p_sar->dev, "pin_output_low_name = %s", pin_output_low_name); + pinctrl->int_out_low = pinctrl_lookup_state(pinctrl->pinctrl, + pin_output_low_name); + if (IS_ERR_OR_NULL(pinctrl->int_out_low)) { + AWLOGE(p_sar->dev, "Failed get pinctrl state:output_low"); + goto exit_pinctrl_init; + } + + AWLOGD(p_sar->dev, "Success init pinctrl"); + + return 0; + +exit_pinctrl_init: + devm_pinctrl_put(pinctrl->pinctrl); + pinctrl->pinctrl = NULL; + + return -EINVAL; +} + +static void aw_sar_pinctrl_deinit(struct aw_sar *p_sar) +{ + if (p_sar->pinctrl.pinctrl) { + devm_pinctrl_put(p_sar->pinctrl.pinctrl); + } +} +//The interrupt pin is set to internal pull-up end + +//AW_SAR_REGULATOR_POWER_ON start +static int32_t aw_sar_regulator_power_init(struct aw_sar *p_sar) +{ + int32_t rc = 0; + uint8_t vcc_name[20] = { 0 }; + + AWLOGD(p_sar->dev, "aw_sar power init enter"); + +// if (p_sar->p_sar_para->p_platform_config->p_regulator_config == NULL) +// return AW_INVALID_PARA; + + snprintf(vcc_name, sizeof(vcc_name), "vcc%d", + p_sar->dts_info.sar_num); + AWLOGD(p_sar->dev, "vcc_name = %s", vcc_name); + + p_sar->vcc = regulator_get(p_sar->dev, vcc_name); + if (IS_ERR(p_sar->vcc)) { + rc = PTR_ERR(p_sar->vcc); + AWLOGE(p_sar->dev, "regulator get failed vcc rc = %d", rc); + return AW_ERR; + } + + if (regulator_count_voltages(p_sar->vcc) > 0) { + rc = regulator_set_voltage(p_sar->vcc, + AW_SAR_VCC_MIN_UV, + AW_SAR_VCC_MAX_UV); + if (rc) { + AWLOGE(p_sar->dev, + "regulator set vol failed rc = %d", rc); + goto reg_vcc_put; + } + } + + return AW_OK; + +reg_vcc_put: + regulator_put(p_sar->vcc); + return AW_ERR; +} + +static void aw_sar_power_deinit(struct aw_sar *p_sar) +{ + if (p_sar->power_enable) { + //Turn off the power output. However, + //it may not be turned off immediately + //There are scenes where power sharing can exist + regulator_disable(p_sar->vcc); + regulator_put(p_sar->vcc); + } +} + +static void aw_sar_power_enable(struct aw_sar *p_sar, bool on) +{ + int32_t rc = 0; + + AWLOGD(p_sar->dev, "aw_sar power enable enter"); + + if (on) { + rc = regulator_enable(p_sar->vcc); + if (rc) { + AWLOGE(p_sar->dev, + "regulator_enable vol failed rc = %d", rc); + } else { + p_sar->power_enable = AW_TRUE; + msleep(20); + } + } else { + rc = regulator_disable(p_sar->vcc); + if (rc) { + AWLOGE(p_sar->dev, + "regulator_disable vol failed rc = %d", rc); + } else { + p_sar->power_enable = AW_FALSE; + } + } +} + +static int32_t regulator_is_get_voltage(struct aw_sar *p_sar) +{ + uint32_t cnt = 10; + int32_t voltage_val = 0; + + AWLOGD(p_sar->dev, "enter"); + + while(cnt--) { + voltage_val = regulator_get_voltage(p_sar->vcc); + AWLOGD(p_sar->dev, "aw_sar voltage is : %d uv", voltage_val); + if (voltage_val >= AW_SAR_VCC_MIN_UV){ + return AW_OK; + } + mdelay(1); + } + //Ensure that the chip initialization is completed + msleep(20); + + return -AW_VERS_ERR; +} +//AW_SAR_REGULATOR_POWER_ON end + +static void aw_sar_init_lock(struct aw_sar *p_sar) +{ + //Initialize lock, To protect the thread safety of updating bin file + mutex_init(&aw_sar_lock); + //Required for mode setting + p_sar->last_mode = p_sar->p_sar_para->p_chip_mode->pre_init_mode; + p_sar->fw_fail_flag = AW_FALSE; + p_sar->ret_val = AW_OK; +} + +// AW_SAR_USB_PLUG_CAIL start +static void aw_sar_ps_notify_callback_work(struct work_struct *work) +{ + struct aw_sar *p_sar = container_of(work, struct aw_sar, ps_notify_work); + AWLOGD(p_sar->dev, "aw_sar_ps_notify_callback_work enter"); + aw_sar_aot(p_sar); +} + +static int aw_sar_ps_get_state(struct aw_sar *p_sar, struct power_supply *psy, bool *present) +{ + union power_supply_propval pval = { 0 }; + int retval; + +#ifdef CONFIG_USE_POWER_SUPPLY_ONLINE + retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, &pval); +#else + retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &pval); +#endif + if (retval) { + AWLOGE(p_sar->dev, "%s psy get property failed", psy->desc->name); + return retval; + } + *present = (pval.intval) ? true : false; +#ifdef CONFIG_USE_POWER_SUPPLY_ONLINE + AWLOGI(p_sar->dev, "%s is %s", psy->desc->name, + (*present) ? "online" : "not online"); +#else + AWLOGI(p_sar->dev, "%s is %s", psy->desc->name, + (*present) ? "present" : "not present"); +#endif + return 0; +} + +static int aw_sar_ps_notify_callback(struct notifier_block *self, + unsigned long event, void *p) +{ + struct aw_sar *p_sar = container_of(self, struct aw_sar, ps_notif); + struct power_supply *psy = p; + bool present; + int retval; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0) + if (event == PSY_EVENT_PROP_CHANGED +#else + if ((event == PSY_EVENT_PROP_ADDED || event == PSY_EVENT_PROP_CHANGED) +#endif + && psy && psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, USB_POWER_SUPPLY_NAME, sizeof(USB_POWER_SUPPLY_NAME))) { + AWLOGE(p_sar->dev, "ps notification: event = %lu", event); + retval = aw_sar_ps_get_state(p_sar, psy, &present); + if (retval) { + AWLOGE(p_sar->dev, "psy get property failed"); + return retval; + } + if (event == PSY_EVENT_PROP_CHANGED) { + if (p_sar->ps_is_present == present) { + AWLOGE(p_sar->dev, "ps present state not change"); + return 0; + } + } + p_sar->ps_is_present = present; + schedule_work(&p_sar->ps_notify_work); + } + return 0; +} + +static int aw_sar_ps_notify_init(struct aw_sar *p_sar) +{ + struct power_supply *psy = NULL; + int ret = 0; + + INIT_WORK(&p_sar->ps_notify_work, aw_sar_ps_notify_callback_work); + p_sar->ps_notif.notifier_call = (notifier_fn_t)aw_sar_ps_notify_callback; + ret = power_supply_reg_notifier(&p_sar->ps_notif); + if (ret) { + AWLOGE(p_sar->dev,"Unable to register ps_notifier: %d", ret); + return -AW_ERR; + } + psy = power_supply_get_by_name(USB_POWER_SUPPLY_NAME); + if (psy) { + ret = aw_sar_ps_get_state(p_sar, psy, &p_sar->ps_is_present); + if (ret) { + AWLOGE(p_sar->dev, "psy get property failed rc=%d", + ret); + goto free_ps_notifier; + } + } + return AW_OK; +free_ps_notifier: + AWLOGE(p_sar->dev,"free_ps_notifier enter"); + power_supply_unreg_notifier(&p_sar->ps_notif); + return -AW_ERR; +} +// AW_SAR_USB_PLUG_CAIL end + + +static int32_t aw_sar_platform_rsc_init(struct aw_sar *p_sar) +{ + int32_t ret = 0; + + if (p_sar->p_sar_para->p_platform_config == NULL) { + return AW_INVALID_PARA; + } + + //step 1.parsing dts data + ret = aw_sar_parse_dts(p_sar); + if (ret != AW_OK) { + AWLOGE(p_sar->dev, "parse dts error!"); + return ret; + } + + //Initialization lock and some variables + aw_sar_init_lock(p_sar); + + //Configure whether to use USB plug-in calibration in DTS according to customer requirements + if (p_sar->dts_info.use_plug_cail_flag == true) { + ret = aw_sar_ps_notify_init(p_sar); + if (ret < 0) { + AWLOGE(p_sar->dev, "error creating power supply notify"); + goto free_usb_plug_cail; + } + } + + //The interrupt pin is set to internal pull-up and configured by DTS + if (p_sar->dts_info.use_inter_pull_up == true) { + ret = aw_sar_pinctrl_init(p_sar); + if (ret < 0) { + /* if define pinctrl must define the following state + * to let int-pin work normally: default, int_output_high, + * int_output_low, int_input + */ + AWLOGE(p_sar->dev, "Failed get wanted pinctrl state"); + goto err_pinctrl; + } + aw_sar_int_output(p_sar, 1); + } + + //step 2.Create debug file node + ret = aw_sar_create_node(p_sar); + if (ret != AW_OK) { + AWLOGE(p_sar->dev, "create node error!"); + goto free_sysfs_nodes; + } + + //step 3.Initialization interrupt + ret = aw_sar_irq_init(p_sar); + if (ret != AW_OK) { + AWLOGE(p_sar->dev, "interrupt initialization error!"); + goto free_irq; + } + + //step 4.Initialization input Subsystem + ret = aw_sar_input_init(p_sar); + if (ret != AW_OK) { + AWLOGE(p_sar->dev, "input_init error!"); + goto free_input; + } + + return AW_OK; + +free_input: + aw_sar_input_free(p_sar); +free_irq: + aw_sar_irq_free(p_sar); +free_sysfs_nodes: + aw_sar_node_free(p_sar); +err_pinctrl: +if (p_sar->dts_info.use_inter_pull_up == true) { + aw_sar_pinctrl_deinit(p_sar); +} +free_usb_plug_cail: +if (p_sar->dts_info.use_plug_cail_flag == true) { + power_supply_unreg_notifier(&p_sar->ps_notif); +} + + return -AW_ERR; +} + +/** + * @brief sar sensor initialization logic. + * + * @param p_sar data stored in type 'struct aw_sar *'. + * @return AW_OK 0 if init successed. others if unpack error. + */ +int32_t aw_sar_chip_init(struct aw_sar *p_sar) +{ + int32_t ret = 0; + + //step 1.check chipid,Verify whether the chip communication is successful + ret = aw_sar_check_chipid(p_sar); + if (ret != AW_OK) { + AWLOGE(p_sar->dev, "check_chipid error!"); + goto communication_fail; + } + + //step 2.Check chip initialization completed, + ret = aw_sar_soft_reset(p_sar); + if (ret != AW_OK) { + AWLOGE(p_sar->dev, "soft_reset error!"); + goto communication_fail; + } + + ret = aw_sar_check_init_over_irq(p_sar); + if (ret != AW_OK) { + AWLOGE(p_sar->dev, "check_init_over_irqt error!"); + goto communication_fail; + } + + //step 3.chip other operation + ret = aw_sar_chip_other_operation(p_sar); + if (ret != AW_OK) { + AWLOGE(p_sar->dev, "chip_other_operation error!"); + goto free_other_opera; + } + + //step 4.Parse the bin file, upgrade the firmware, and load the register prize + aw_sar_update(p_sar); + + AWLOGD(p_sar->dev, "init success!"); + + return AW_OK; +free_other_opera: + aw_sar_chip_other_operation_free(p_sar); +communication_fail: + + return -AW_ERR; +} + +static int32_t aw_sar_init(struct aw_sar *p_sar) +{ + int32_t ret = 0; + + //step 1: Platform resource initialization + ret = aw_sar_platform_rsc_init(p_sar); + if (ret != AW_OK) { + AWLOGE(p_sar->dev, "platform_rsc_init error!"); + return -AW_ERR; + } + + //step 2: Chip initialization + ret = aw_sar_chip_init(p_sar); + if (ret != AW_OK) { + aw_sar_input_free(p_sar); + aw_sar_irq_free(p_sar); + aw_sar_node_free(p_sar); + if (p_sar->dts_info.use_inter_pull_up == true) { + aw_sar_pinctrl_deinit(p_sar); + } + return -AW_ERR; + } + + return AW_OK; +} + +static int32_t aw_sar_regulator_power(struct aw_sar *p_sar) +{ + struct aw_sar_dts_info *p_dts_info = &p_sar->dts_info; + int32_t ret = 0; + + p_dts_info->use_regulator_flag = of_property_read_bool(p_sar->i2c->dev.of_node, "aw_sar,regulator-power-supply"); + AWLOGI(p_sar->dev, "regulator-power-supply = <%d>", p_dts_info->use_regulator_flag); + + //Configure the use of regulator power supply in DTS + if (p_sar->dts_info.use_regulator_flag == true) { + ret = aw_sar_regulator_power_init(p_sar); + if (ret != AW_OK) + AWLOGE(p_sar->dev, "power init failed"); + else + aw_sar_power_enable(p_sar, AW_TRUE); + ret = regulator_is_get_voltage(p_sar); + if (ret != AW_OK) { + AWLOGE(p_sar->dev, "get_voltage failed"); + aw_sar_power_deinit(p_sar); + } + } + + return ret; +} + +/** + * @brief Drive logic entry + * + */ +static int32_t aw_sar_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + int32_t ret = 0; + struct aw_sar *p_sar = NULL; + + pr_info("%s enter", __func__); + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + pr_err("check_functionality failed!\n"); + return -EIO; + } + + p_sar = devm_kzalloc(&i2c->dev, sizeof(struct aw_sar), GFP_KERNEL); + if (p_sar == NULL) { + pr_err("p_sar failed to malloc memory!"); + ret = -AW_ERR; + goto err_malloc; + } + + p_sar->dev = &i2c->dev; + p_sar->i2c = i2c; + i2c_set_clientdata(i2c, p_sar); + + //1.Judge whether to use regular power supply. If yes, supply power + ret = aw_sar_regulator_power(p_sar); + if (ret != AW_OK) { + AWLOGE(&i2c->dev, "regulator_power error!"); + goto err_get_voltage; + } + + //2.Get chip initialization resources + ret = aw_sar_get_chip_info(p_sar); + if (ret != AW_OK) { + AWLOGE(&i2c->dev, "chip_init error!"); + goto err_chip_init; + } + + //3.Chip initialization process + ret = aw_sar_init(p_sar); + if (ret != AW_OK) { + AWLOGE(&i2c->dev, "chip_init error!"); + goto err_chip_init; + } + + AWLOGD(&i2c->dev, "probe success!"); + + return 0; +err_chip_init: + aw_sar_sensor_free(p_sar); +err_get_voltage: +if (p_sar->dts_info.use_regulator_flag == true) { + aw_sar_power_deinit(p_sar); +} + +err_malloc: + return ret; +} + +static int32_t aw_sar_i2c_remove(struct i2c_client *i2c) +{ + struct aw_sar *p_sar = i2c_get_clientdata(i2c); + + aw_sar_chip_other_operation_free(p_sar); + + aw_sar_node_free(p_sar); + + aw_sar_irq_free(p_sar); + + aw_sar_input_free(p_sar); + + if (p_sar->dts_info.use_inter_pull_up == true) { + aw_sar_pinctrl_deinit(p_sar); + } + if (p_sar->dts_info.use_regulator_flag == true) { + aw_sar_power_deinit(p_sar); + } + + if (p_sar->dts_info.use_plug_cail_flag == true) { + power_supply_unreg_notifier(&p_sar->ps_notif); + } + + aw_sar_sensor_free(p_sar); + + AWLOGI(p_sar->dev, "aw_sar_i2c_remove ok!"); + + return 0; +} + +static int aw_sar_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct aw_sar *p_sar = i2c_get_clientdata(client); + + AWLOGI(p_sar->dev, "enter"); + + if (p_sar->dts_info.use_pm == true) { + if ((p_sar->p_sar_para->p_platform_config == NULL) || + (p_sar->p_sar_para->p_platform_config->p_pm_chip_mode == NULL)) { + return 0; + } + if (p_sar->p_sar_para->p_platform_config->p_pm_chip_mode->p_suspend_fn) { + p_sar->p_sar_para->p_platform_config->p_pm_chip_mode->p_suspend_fn(p_sar); + return 0; + } + aw_sar_mode_set(p_sar, p_sar->p_sar_para->p_platform_config->p_pm_chip_mode->suspend_set_mode); + } + + return 0; +} + +static int aw_sar_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct aw_sar *p_sar = i2c_get_clientdata(client); + + AWLOGI(p_sar->dev, "enter"); + + if (p_sar->dts_info.use_pm == true) { + if ((p_sar->p_sar_para->p_platform_config == NULL) || + (p_sar->p_sar_para->p_platform_config->p_pm_chip_mode == NULL)) { + return 0; + } + if (p_sar->p_sar_para->p_platform_config->p_pm_chip_mode->p_resume_fn) { + p_sar->p_sar_para->p_platform_config->p_pm_chip_mode->p_resume_fn(p_sar); + return 0; + } + aw_sar_mode_set(p_sar, p_sar->p_sar_para->p_platform_config->p_pm_chip_mode->resume_set_mode); + } + return 0; +} + +static void aw_sar_i2c_shutdown(struct i2c_client *i2c) +{ + struct aw_sar *p_sar = i2c_get_clientdata(i2c); + + AWLOGI(p_sar->dev, "enter"); + + if ((p_sar->p_sar_para->p_platform_config == NULL) || + (p_sar->p_sar_para->p_platform_config->p_pm_chip_mode == NULL)) { + return; + } + + if (p_sar->p_sar_para->p_platform_config->p_pm_chip_mode->p_shutdown_fn) { + p_sar->p_sar_para->p_platform_config->p_pm_chip_mode->p_shutdown_fn(p_sar); + return; + } + + aw_sar_mode_set(p_sar, p_sar->p_sar_para->p_platform_config->p_pm_chip_mode->shutdown_set_mode); +} + +static const struct dev_pm_ops aw_sar_pm_ops = { + .suspend = aw_sar_suspend, + .resume = aw_sar_resume, +}; + +static const struct of_device_id aw_sar_dt_match[] = { + { .compatible = "awinic,aw_sar", }, + {}, +}; + +static const struct i2c_device_id aw_sar_i2c_id[] = { + { AW_SAR_I2C_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, aw_sar_i2c_id); + +static struct i2c_driver aw_sar_i2c_driver = { + .driver = { + .name = AW_SAR_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw_sar_dt_match), + .pm = &aw_sar_pm_ops, + }, + .probe = aw_sar_i2c_probe, + .remove = aw_sar_i2c_remove, + .shutdown = aw_sar_i2c_shutdown, + .id_table = aw_sar_i2c_id, +}; + +static int32_t __init aw_sar_i2c_init(void) +{ + int32_t ret = 0; + + pr_info("awinic sar driver version %s\n", AW_SAR_DRIVER_VERSION); + + ret = i2c_add_driver(&aw_sar_i2c_driver); + if (ret) { + pr_err("fail to add aw_sar device into i2c\n"); + return ret; + } + + return 0; +} + +module_init(aw_sar_i2c_init); +static void __exit aw_sar_i2c_exit(void) +{ + i2c_del_driver(&aw_sar_i2c_driver); +} +module_exit(aw_sar_i2c_exit); + +/** + * @brief Distinguish different chips by chip name and obtain relevant chip information + * + * @param p_sar Structure to be filled + * @return AW_OK 0 if init successed. -AW_ERR -1 if error. + */ +static int32_t aw_sar_get_chip_info(struct aw_sar *p_sar) +{ + uint8_t i = 0; + int32_t ret = 0; + + AWLOGD(p_sar->dev, "enter"); + + for(i = 0; i < AW_SAR_DRIVER_MAX; i++) { + if (g_aw_sar_driver_list[i].p_who_am_i != NULL) { + ret = g_aw_sar_driver_list[i].p_who_am_i(p_sar); + if (ret == AW_OK) { + p_sar->curr_use_driver_type = g_aw_sar_driver_list[i].driver_type; + if (g_aw_sar_driver_list[i].p_chip_init == NULL) { + AWLOGE(p_sar->dev, "drvier:%d p_chip_init is null error!", i); + continue; + } + g_aw_sar_driver_list[i].p_chip_init(p_sar); + AWLOGI(p_sar->dev, "current use drvier is :%d", g_aw_sar_driver_list[i].driver_type); + return AW_OK; + } else { + AWLOGI(p_sar->dev, "drvier:%d Not supported", i); + } + } + } + + return -AW_ERR; +} + +/** + * @brief Distinguish different chips by chip name and obtain relevant chip information + * + * @param p_sar Associated release resource information,Follow priv_data member + * @return AW_OK 0 if init successed. -AW_ERR -1 if error. + */ +static void aw_sar_sensor_free(struct aw_sar *p_sar) +{ + if (g_aw_sar_driver_list[p_sar->curr_use_driver_type].p_chip_deinit != NULL) { + g_aw_sar_driver_list[p_sar->curr_use_driver_type].p_chip_deinit(p_sar); + } +} + +MODULE_DESCRIPTION("AWINIC SAR Driver"); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/awinic/sarsensor/comm/aw_sar_comm_interface.c b/drivers/misc/awinic/sarsensor/comm/aw_sar_comm_interface.c new file mode 100644 index 000000000000..dda27396be34 --- /dev/null +++ b/drivers/misc/awinic/sarsensor/comm/aw_sar_comm_interface.c @@ -0,0 +1,786 @@ +#include "aw_sar_comm_interface.h" + +#define AW_I2C_RW_RETRY_TIME_MIN (2000) +#define AW_I2C_RW_RETRY_TIME_MAX (3000) +#define AW_RETRIES (5) + +static int32_t awinic_i2c_write(struct i2c_client *i2c, uint8_t *tr_data, uint16_t len) +{ + struct i2c_msg msg; + + msg.addr = i2c->addr; + msg.flags = 0; + msg.len = len; + msg.buf = tr_data; + + return i2c_transfer(i2c->adapter, &msg, 1); +} + +static int32_t awinic_i2c_read(struct i2c_client *i2c, uint8_t *addr, + uint8_t addr_len, uint8_t *data, uint16_t data_len) +{ + struct i2c_msg msg[2]; + + msg[0].addr = i2c->addr; + msg[0].flags = 0; + msg[0].len = addr_len; + msg[0].buf = addr; + + msg[1].addr = i2c->addr; + msg[1].flags = 1; + msg[1].len = data_len; + msg[1].buf = data; + + return i2c_transfer(i2c->adapter, msg, 2); +} + +/** + * @brief Read register interface + * + * @param i2c: i2c client. + * @param reg_addr16: 16 bit register address. + * @param *reg_data32: 32 bit register data. + * @return AW_OK 0 if init successed. -AW_ERR -1 if error. + */ +int32_t aw_sar_i2c_read(struct i2c_client *i2c, uint16_t reg_addr16, uint32_t *reg_data32) +{ + int8_t cnt = 5; + int32_t ret = -1; + uint8_t r_buf[6] = { 0 }; + + if (i2c == NULL) { + AWLOGE(&i2c->dev, "i2c_dev is null error!"); + return -AW_ERR; + } + + r_buf[0] = (unsigned char)(reg_addr16 >> OFFSET_BIT_8); + r_buf[1] = (unsigned char)(reg_addr16); + + do { + ret = awinic_i2c_read(i2c, r_buf, 2, &r_buf[2], 4); + if (ret < 0) { + AWLOGE(&i2c->dev, "i2c read error reg: 0x%04x, ret= %d cnt= %d", reg_addr16, ret, cnt); + } else { + break; + } + usleep_range(2000, 3000); + } while (cnt--); + + if (cnt < 0) { + AWLOGE(&i2c->dev, "i2c read error!"); + return -AW_ERR; + } + + *reg_data32 = ((uint32_t)r_buf[5] << OFFSET_BIT_0) | ((uint32_t)r_buf[4] << OFFSET_BIT_8) | + ((uint32_t)r_buf[3] << OFFSET_BIT_16) | ((uint32_t)r_buf[2] << OFFSET_BIT_24); + + return AW_OK; +} + +/** + * @brief write register interface + * + * @param i2c: i2c client. + * @param reg_addr16: 16 bit register address. + * @param reg_data32: 32 bit register data. + * @return AW_OK 0 if init successed. -AW_ERR -1 if error. + */ +int32_t aw_sar_i2c_write(struct i2c_client *i2c, uint16_t reg_addr16, uint32_t reg_data32) +{ + int8_t cnt = 5; + int32_t ret = -1; + uint8_t w_buf[6] = { 0 }; + + if (i2c == NULL) { + AWLOGE(&i2c->dev, "i2c_dev is null error!"); + return -AW_ERR; + } + + /*reg_addr*/ + w_buf[0] = (uint8_t)(reg_addr16 >> OFFSET_BIT_8); + w_buf[1] = (uint8_t)(reg_addr16); + /*data*/ + w_buf[2] = (uint8_t)(reg_data32 >> OFFSET_BIT_24); + w_buf[3] = (uint8_t)(reg_data32 >> OFFSET_BIT_16); + w_buf[4] = (uint8_t)(reg_data32 >> OFFSET_BIT_8); + w_buf[5] = (uint8_t)(reg_data32); + + do { + ret = awinic_i2c_write(i2c, w_buf, ARRAY_SIZE(w_buf)); + if (ret < 0) { + AWLOGE(&i2c->dev, "i2c write error reg: 0x%04x data: 0x%08x, ret= %d cnt= %d", + reg_addr16, reg_data32, ret, cnt); + } else { + break; + } + } while (cnt--); + + if (cnt < 0) { + AWLOGE(&i2c->dev, "i2c write error!"); + return -AW_ERR; + } + + return AW_OK; +} + +/** + * @brief Write the corresponding bit of the register + * + * @param i2c:i2c client. + * @param reg_addr16: 16 bit register address. + * @param mask: Write the corresponding bit as 0 + * @param val: Write corresponding data to the register + * @return AW_OK 0 if init successed. -AW_ERR -1 if error. + */ +int32_t aw_sar_i2c_write_bits(struct i2c_client *i2c, uint16_t reg_addr16, + uint32_t mask, uint32_t val) +{ + uint32_t reg_val; + + aw_sar_i2c_read(i2c, reg_addr16, ®_val); + reg_val &= mask; + reg_val |= (val & (~mask)); + aw_sar_i2c_write(i2c, reg_addr16, reg_val); + + return AW_OK; +} + +/** + * @brief Continuously write data to the chip + * + * @param i2c:i2c client. + * @param *tr_data: Data written + * @param len: Length of data written + * @return AW_OK 0 if init successed. -AW_ERR -1 if error. + */ +int32_t aw_sar_i2c_write_seq(struct i2c_client *i2c, uint8_t *tr_data, uint16_t len) +{ + int8_t cnt = AW_RETRIES; + int32_t ret = -AW_ERR; + + do { + ret = awinic_i2c_write(i2c, tr_data, len); + if (ret < 0) + AWLOGE(&i2c->dev, "awinic i2c write seq error %d", ret); + else + break; + usleep_range(AW_I2C_RW_RETRY_TIME_MIN, AW_I2C_RW_RETRY_TIME_MAX); + } while (cnt--); + + if (cnt < 0) { + AWLOGE(&i2c->dev, "awinic i2c write error!"); + return -AW_ERR; + } + + return AW_OK; +} + +/** + * @brief Continuously Read data from chip + * + * @param i2c:i2c client. + * @param *addr: Read address + * @param addr_len: Length of read address (byte) + * @param *data: Data written + * @param data_len: Length of data written + * @return AW_OK 0 if init successed. -AW_ERR -1 if error. + */ +int32_t aw_sar_i2c_read_seq(struct i2c_client *i2c, uint8_t *addr, + uint8_t addr_len, uint8_t *data, uint16_t data_len) +{ + int8_t cnt = AW_RETRIES; + int32_t ret = -AW_ERR; + + do { + ret = awinic_i2c_read(i2c, addr, addr_len, data, data_len); + if (ret < 0) { + AWLOGE(&i2c->dev, "awinic sar i2c write error %d", ret); + } else { + break; + } + usleep_range(AW_I2C_RW_RETRY_TIME_MIN, AW_I2C_RW_RETRY_TIME_MAX); + } while (cnt--); + + if (cnt < 0) { + AWLOGE(&i2c->dev, "awinic sar i2c read error!"); + return -AW_ERR; + } + + return AW_OK; +} + +/*********************************Parse bin file code start**************************************************/ + +#define AWINIC_CODE_VERSION "V0.0.7-V1.0.4" /* "code version"-"excel version" */ + +enum bin_header_version_enum { + HEADER_VERSION_1_0_0 = 0x01000000, +}; + +enum data_type_enum { + DATA_TYPE_REGISTER = 0x00000000, + DATA_TYPE_DSP_REG = 0x00000010, + DATA_TYPE_DSP_CFG = 0x00000011, + DATA_TYPE_SOC_REG = 0x00000020, + DATA_TYPE_SOC_APP = 0x00000021, + DATA_TYPE_MULTI_BINS = 0x00002000, +}; + +enum data_version_enum { + DATA_VERSION_V1 = 0X00000001, /*default little edian */ + DATA_VERSION_MAX, +}; + +#define DEBUG_LOG_LEVEL +#ifdef DEBUG_LOG_LEVEL +#define DBG(fmt, arg...) do {\ +printk("AWINIC_BIN %s,line= %d,"fmt, __func__, __LINE__, ##arg);\ +} while (0) +#define DBG_ERR(fmt, arg...) do {\ +printk("AWINIC_BIN_ERR %s,line= %d,"fmt, __func__, __LINE__, ##arg);\ +} while (0) +#else +#define DBG(fmt, arg...) do {} while (0) +#define DBG_ERR(fmt, arg...) do {} while (0) +#endif + +#define printing_data_code + +typedef unsigned short int aw_uint16; +typedef unsigned long int aw_uint32; + +#define BigLittleSwap16(A) ((((aw_uint16)(A) & 0xff00) >> 8) | \ + (((aw_uint16)(A) & 0x00ff) << 8)) + +#define BigLittleSwap32(A) ((((aw_uint32)(A) & 0xff000000) >> 24) | \ + (((aw_uint32)(A) & 0x00ff0000) >> 8) | \ + (((aw_uint32)(A) & 0x0000ff00) << 8) | \ + (((aw_uint32)(A) & 0x000000ff) << 24)) + +static int aw_parse_bin_header_1_0_0(struct aw_bin *bin); + +/** +* +* Interface function +* +* return value: +* value = 0 :success; +* value = -1 :check bin header version +* value = -2 :check bin data type +* value = -3 :check sum or check bin data len error +* value = -4 :check data version +* value = -5 :check register num +* value = -6 :check dsp reg num +* value = -7 :check soc app num +* value = -8 :bin is NULL point +* +**/ + +/******************************************************** +* +* check sum data +* +********************************************************/ +static int aw_check_sum(struct aw_bin *bin, int bin_num) +{ + unsigned int i = 0; + unsigned int sum_data = 0; + unsigned int check_sum = 0; + char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr - + bin->header_info[bin_num].header_len)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + check_sum = AW_SAR_GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + + for (i = 4; + i < + bin->header_info[bin_num].bin_data_len + + bin->header_info[bin_num].header_len; i++) { + sum_data += *(p_check_sum + i); + } + DBG("aw_bin_parse bin_num=%d, check_sum = 0x%x, sum_data = 0x%x\n", + bin_num, check_sum, sum_data); + if (sum_data != check_sum) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse check sum or check bin data len error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, check_sum = 0x%x, sum_data = 0x%x\n", bin_num, check_sum, sum_data); + return -3; + } + p_check_sum = NULL; + + return 0; +} + +static int aw_check_data_version(struct aw_bin *bin, int bin_num) +{ + int i = 0; + DBG("enter\n"); + + for (i = DATA_VERSION_V1; i < DATA_VERSION_MAX; i++) { + if (bin->header_info[bin_num].bin_data_ver == i) { + return 0; + } + } + DBG_ERR("aw_bin_parse Unrecognized this bin data version\n"); + return -4; +} + +static int aw_check_register_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_register_num = 0; + unsigned int parse_register_num = 0; + char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + parse_register_num = AW_SAR_GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + check_register_num = (bin->header_info[bin_num].bin_data_len - 4) / + (bin->header_info[bin_num].reg_byte_len + + bin->header_info[bin_num].data_byte_len); + DBG + ("aw_bin_parse bin_num=%d, parse_register_num = 0x%x, check_register_num = 0x%x\n", + bin_num, parse_register_num, check_register_num); + if (parse_register_num != check_register_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse register num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_register_num = 0x%x, check_register_num = 0x%x\n", bin_num, parse_register_num, check_register_num); + return -5; + } + bin->header_info[bin_num].reg_num = parse_register_num; + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 4; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 4; + return 0; +} + +static int aw_check_dsp_reg_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_dsp_reg_num = 0; + unsigned int parse_dsp_reg_num = 0; + char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + parse_dsp_reg_num = AW_SAR_GET_32_DATA(*(p_check_sum + 7), + *(p_check_sum + 6), + *(p_check_sum + 5), + *(p_check_sum + 4)); + bin->header_info[bin_num].reg_data_byte_len = + AW_SAR_GET_32_DATA(*(p_check_sum + 11), *(p_check_sum + 10), + *(p_check_sum + 9), *(p_check_sum + 8)); + check_dsp_reg_num = + (bin->header_info[bin_num].bin_data_len - + 12) / bin->header_info[bin_num].reg_data_byte_len; + DBG + ("aw_bin_parse bin_num=%d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x\n", + bin_num, parse_dsp_reg_num, check_dsp_reg_num); + if (parse_dsp_reg_num != check_dsp_reg_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse dsp reg num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_dsp_reg_num = 0x%x, check_dsp_reg_num = 0x%x\n", bin_num, parse_dsp_reg_num, check_dsp_reg_num); + return -6; + } + bin->header_info[bin_num].download_addr = + AW_SAR_GET_32_DATA(*(p_check_sum + 3), *(p_check_sum + 2), + *(p_check_sum + 1), *(p_check_sum)); + bin->header_info[bin_num].reg_num = parse_dsp_reg_num; + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 12; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 12; + return 0; +} + +static int aw_check_soc_app_num_v1(struct aw_bin *bin, int bin_num) +{ + unsigned int check_soc_app_num = 0; + unsigned int parse_soc_app_num = 0; + char *p_check_sum = NULL; + + DBG("enter\n"); + + p_check_sum = + &(bin->info.data[(bin->header_info[bin_num].valid_data_addr)]); + DBG("aw_bin_parse p_check_sum = %p\n", p_check_sum); + bin->header_info[bin_num].app_version = AW_SAR_GET_32_DATA(*(p_check_sum + 3), + *(p_check_sum + 2), + *(p_check_sum + 1), + *(p_check_sum)); + parse_soc_app_num = AW_SAR_GET_32_DATA(*(p_check_sum + 11), + *(p_check_sum + 10), + *(p_check_sum + 9), *(p_check_sum + 8)); + check_soc_app_num = bin->header_info[bin_num].bin_data_len - 12; + DBG + ("aw_bin_parse bin_num=%d, parse_soc_app_num = 0x%x, check_soc_app_num = 0x%x\n", + bin_num, parse_soc_app_num, check_soc_app_num); + if (parse_soc_app_num != check_soc_app_num) { + p_check_sum = NULL; + DBG_ERR("aw_bin_parse soc app num is error\n"); + DBG_ERR("aw_bin_parse bin_num=%d, parse_soc_app_num = 0x%x, check_soc_app_num = 0x%x\n", bin_num, parse_soc_app_num, check_soc_app_num); + return -7; + } + bin->header_info[bin_num].reg_num = parse_soc_app_num; + bin->header_info[bin_num].download_addr = + AW_SAR_GET_32_DATA(*(p_check_sum + 7), *(p_check_sum + 6), + *(p_check_sum + 5), *(p_check_sum + 4)); + bin->header_info[bin_num].valid_data_len = + bin->header_info[bin_num].bin_data_len - 12; + p_check_sum = NULL; + bin->header_info[bin_num].valid_data_addr = + bin->header_info[bin_num].valid_data_addr + 12; + return 0; +} + +/************************ +*** +***bin header 1_0_0 +*** +************************/ +static void aw_get_single_bin_header_1_0_0(struct aw_bin *bin) +{ + int i; + DBG("enter %s\n", __func__); + bin->header_info[bin->all_bin_parse_num].header_len = 60; + bin->header_info[bin->all_bin_parse_num].check_sum = + AW_SAR_GET_32_DATA(*(bin->p_addr + 3), *(bin->p_addr + 2), + *(bin->p_addr + 1), *(bin->p_addr)); + bin->header_info[bin->all_bin_parse_num].header_ver = + AW_SAR_GET_32_DATA(*(bin->p_addr + 7), *(bin->p_addr + 6), + *(bin->p_addr + 5), *(bin->p_addr + 4)); + bin->header_info[bin->all_bin_parse_num].bin_data_type = + AW_SAR_GET_32_DATA(*(bin->p_addr + 11), *(bin->p_addr + 10), + *(bin->p_addr + 9), *(bin->p_addr + 8)); + bin->header_info[bin->all_bin_parse_num].bin_data_ver = + AW_SAR_GET_32_DATA(*(bin->p_addr + 15), *(bin->p_addr + 14), + *(bin->p_addr + 13), *(bin->p_addr + 12)); + bin->header_info[bin->all_bin_parse_num].bin_data_len = + AW_SAR_GET_32_DATA(*(bin->p_addr + 19), *(bin->p_addr + 18), + *(bin->p_addr + 17), *(bin->p_addr + 16)); + bin->header_info[bin->all_bin_parse_num].ui_ver = + AW_SAR_GET_32_DATA(*(bin->p_addr + 23), *(bin->p_addr + 22), + *(bin->p_addr + 21), *(bin->p_addr + 20)); + bin->header_info[bin->all_bin_parse_num].reg_byte_len = + AW_SAR_GET_32_DATA(*(bin->p_addr + 35), *(bin->p_addr + 34), + *(bin->p_addr + 33), *(bin->p_addr + 32)); + bin->header_info[bin->all_bin_parse_num].data_byte_len = + AW_SAR_GET_32_DATA(*(bin->p_addr + 39), *(bin->p_addr + 38), + *(bin->p_addr + 37), *(bin->p_addr + 36)); + bin->header_info[bin->all_bin_parse_num].device_addr = + AW_SAR_GET_32_DATA(*(bin->p_addr + 43), *(bin->p_addr + 42), + *(bin->p_addr + 41), *(bin->p_addr + 40)); + for (i = 0; i < 8; i++) { + bin->header_info[bin->all_bin_parse_num].chip_type[i] = + *(bin->p_addr + 24 + i); + } +// bin->header_info[bin->all_bin_parse_num].chip_type[i] = '\0'; +// DBG("%s : enter chip_type is %s\n", __func__, +// bin->header_info[bin->all_bin_parse_num].chip_type); + + bin->header_info[bin->all_bin_parse_num].reg_num = 0x00000000; + bin->header_info[bin->all_bin_parse_num].reg_data_byte_len = 0x00000000; + bin->header_info[bin->all_bin_parse_num].download_addr = 0x00000000; + bin->header_info[bin->all_bin_parse_num].app_version = 0x00000000; + bin->header_info[bin->all_bin_parse_num].valid_data_len = 0x00000000; + bin->all_bin_parse_num += 1; +} + +static int aw_parse_each_of_multi_bins_1_0_0(unsigned int bin_num, int bin_serial_num, + struct aw_bin *bin) +{ + int ret = 0; + unsigned int bin_start_addr = 0; + unsigned int valid_data_len = 0; + DBG("aw_bin_parse enter multi bin branch -- %s\n", __func__); + if (!bin_serial_num) { + bin_start_addr = AW_SAR_GET_32_DATA(*(bin->p_addr + 67), + *(bin->p_addr + 66), + *(bin->p_addr + 65), + *(bin->p_addr + 64)); + bin->p_addr += (60 + bin_start_addr); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + bin->header_info[bin->all_bin_parse_num - + 1].valid_data_addr + 4 + 8 * bin_num + 60; + } else { + valid_data_len = + bin->header_info[bin->all_bin_parse_num - 1].bin_data_len; + bin->p_addr += (60 + valid_data_len); + bin->header_info[bin->all_bin_parse_num].valid_data_addr = + bin->header_info[bin->all_bin_parse_num - + 1].valid_data_addr + + bin->header_info[bin->all_bin_parse_num - 1].bin_data_len + + 60; + } + + ret = aw_parse_bin_header_1_0_0(bin); + return ret; +} + +/* Get the number of bins in multi bins, and set a for loop, loop processing each bin data */ +static int aw_get_multi_bin_header_1_0_0(struct aw_bin *bin) +{ + int i = 0; + int ret = 0; + unsigned int bin_num = 0; + DBG("aw_bin_parse enter multi bin branch -- %s\n", __func__); + bin_num = AW_SAR_GET_32_DATA(*(bin->p_addr + 63), + *(bin->p_addr + 62), + *(bin->p_addr + 61), *(bin->p_addr + 60)); + if (bin->multi_bin_parse_num == 1) { + bin->header_info[bin->all_bin_parse_num].valid_data_addr = 60; + } + aw_get_single_bin_header_1_0_0(bin); + + for (i = 0; i < bin_num; i++) { + DBG("aw_bin_parse enter multi bin for is %d\n", i); + ret = aw_parse_each_of_multi_bins_1_0_0(bin_num, i, bin); + if (ret < 0) { + return ret; + } + } + return 0; +} + +/******************************************************** +* +* If the bin framework header version is 1.0.0, + determine the data type of bin, and then perform different processing + according to the data type + If it is a single bin data type, write the data directly into the structure array + If it is a multi-bin data type, first obtain the number of bins, + and then recursively call the bin frame header processing function + according to the bin number to process the frame header information of each bin separately +* +********************************************************/ +static int aw_parse_bin_header_1_0_0(struct aw_bin *bin) +{ + int ret = 0; + unsigned int bin_data_type; + DBG("enter %s\n", __func__); + bin_data_type = AW_SAR_GET_32_DATA(*(bin->p_addr + 11), + *(bin->p_addr + 10), + *(bin->p_addr + 9), *(bin->p_addr + 8)); + DBG("aw_bin_parse bin_data_type 0x%x\n", bin_data_type); + switch (bin_data_type) { + case DATA_TYPE_REGISTER: + case DATA_TYPE_DSP_REG: + case DATA_TYPE_SOC_APP: + /* Divided into two processing methods, + one is single bin processing, + and the other is single bin processing in multi bin */ + DBG("aw_bin_parse enter single bin branch\n"); + bin->single_bin_parse_num += 1; + DBG("%s bin->single_bin_parse_num is %d\n", __func__, + bin->single_bin_parse_num); + if (!bin->multi_bin_parse_num) { + bin->header_info[bin-> + all_bin_parse_num].valid_data_addr = + 60; + } + aw_get_single_bin_header_1_0_0(bin); + break; + case DATA_TYPE_MULTI_BINS: + /* Get the number of times to enter multi bins */ + DBG("aw_bin_parse enter multi bin branch\n"); + bin->multi_bin_parse_num += 1; + DBG("%s bin->multi_bin_parse_num is %d\n", __func__, + bin->multi_bin_parse_num); + ret = aw_get_multi_bin_header_1_0_0(bin); + if (ret < 0) { + return ret; + } + break; + default: + DBG_ERR("aw_bin_parse Unrecognized this bin data type\n"); + return -2; + } + return 0; +} + +/* get the bin's header version */ +static int aw_check_bin_header_version(struct aw_bin *bin) +{ + int ret = 0; + unsigned int header_version = 0; + + header_version = AW_SAR_GET_32_DATA(*(bin->p_addr + 7), + *(bin->p_addr + 6), + *(bin->p_addr + 5), *(bin->p_addr + 4)); + + DBG("aw_bin_parse header_version 0x%x\n", header_version); + + /* Write data to the corresponding structure array + according to different formats of the bin frame header version */ + switch (header_version) { + case HEADER_VERSION_1_0_0: + ret = aw_parse_bin_header_1_0_0(bin); + return ret; + default: + DBG_ERR("aw_bin_parse Unrecognized this bin header version \n"); + return -1; + } +} + +/** + * @brief Parse bin file + * + * @param bin: Store the contents of the parsed bin file + * @return 0 if init successed, other if error + */ +int aw_sar_parsing_bin_file(struct aw_bin *bin) +{ + int i = 0; + int ret = 0; + + DBG("aw_bin_parse code version:%s\n", AWINIC_CODE_VERSION); + if (!bin) { + DBG_ERR("aw_bin_parse bin is NULL\n"); + return -8; + } + bin->p_addr = bin->info.data; + bin->all_bin_parse_num = 0; + bin->multi_bin_parse_num = 0; + bin->single_bin_parse_num = 0; + + /* filling bins header info */ + ret = aw_check_bin_header_version(bin); + if (ret < 0) { + DBG_ERR("aw_bin_parse check bin header version error\n"); + return ret; + } + bin->p_addr = NULL; + + /* check bin header info */ + for (i = 0; i < bin->all_bin_parse_num; i++) { + /* check sum */ + ret = aw_check_sum(bin, i); + if (ret < 0) { + DBG_ERR("aw_bin_parse check sum data error\n"); + return ret; + } + /* check bin data version */ + ret = aw_check_data_version(bin, i); + if (ret < 0) { + DBG_ERR("aw_bin_parse check data version error\n"); + return ret; + } + /* check valid data */ + if (bin->header_info[i].bin_data_ver == DATA_VERSION_V1) { + /* check register num */ + if (bin->header_info[i].bin_data_type == + DATA_TYPE_REGISTER) { + ret = aw_check_register_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check register num error\n"); + return ret; + } + /* check dsp reg num */ + } else if (bin->header_info[i].bin_data_type == + DATA_TYPE_DSP_REG) { + ret = aw_check_dsp_reg_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check dsp reg num error\n"); + return ret; + } + /* check soc app num */ + } else if (bin->header_info[i].bin_data_type == + DATA_TYPE_SOC_APP) { + ret = aw_check_soc_app_num_v1(bin, i); + if (ret < 0) { + DBG_ERR + ("aw_bin_parse check soc app num error\n"); + return ret; + } + } else { + bin->header_info[i].valid_data_len = + bin->header_info[i].bin_data_len; + } + } + } + DBG("aw_bin_parse parsing success\n"); + + return 0; +} +/*********************************Parse bin file code end**************************************************/ + +/** + * @brief Calculate the second power + * + * @param cnt: ifrequency + * @return the second power + */ +uint32_t aw_sar_pow2(uint32_t cnt) +{ + uint32_t i = 0; + uint32_t sum = 1; + + if (cnt == 0) { + sum = 1; + } else { + for (i = 0; i < cnt; i++) { + sum *= 2; + } + } + + return sum; +} + +/** + * @brief Calculate the second power + * + * @param *aw_bin: Information after parsing bin file + * @param *i2c: i2c client. + * @return AW_OK 0 if init successed. -AW_ERR -1 if error + */ +int32_t aw_sar_load_reg(struct aw_bin *aw_bin, struct i2c_client *i2c) +{ + uint32_t i = 0; + int32_t ret = 0; + uint16_t reg_addr = 0; + uint32_t reg_data = 0; + uint32_t start_addr = aw_bin->header_info[0].valid_data_addr; + + for (i = 0; i < aw_bin->header_info[0].valid_data_len; + i += 6, start_addr += 6) { + reg_addr = (aw_bin->info.data[start_addr]) | + aw_bin->info.data[start_addr + 1] << OFFSET_BIT_8; + reg_data = aw_bin->info.data[start_addr + 2] | + (aw_bin->info.data[start_addr + 3] << OFFSET_BIT_8) | + (aw_bin->info.data[start_addr + 4] << OFFSET_BIT_16) | + (aw_bin->info.data[start_addr + 5] << OFFSET_BIT_24); + + ret = aw_sar_i2c_write(i2c, reg_addr, reg_data); + if (ret < 0) { + AWLOGE(&i2c->dev, "i2c write err"); + return -AW_ERR; + } + + AWLOGD(&i2c->dev, "reg_addr = 0x%04x, reg_data = 0x%08x", reg_addr, reg_data); + } + + return AW_OK; +} + +void aw_sar_delay_ms(uint32_t ms) +{ + mdelay(ms); +} + diff --git a/drivers/misc/cs40l2x-tables.c b/drivers/misc/cs40l2x-tables.c new file mode 100644 index 000000000000..210e3dcd41ce --- /dev/null +++ b/drivers/misc/cs40l2x-tables.c @@ -0,0 +1,958 @@ +/* + * cs40l2x-tables.c -- CS40L20/CS40L25/CS40L25A/CS40L25B Haptics Driver + * + * Copyright 2018 Cirrus Logic, Inc. + * + * Author: Jeff LaBundy + * + * 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. + * + */ + +#include "cs40l2x.h" + +const unsigned char cs40l2x_bst_k1_table[4][5] = { + {0x24, 0x32, 0x32, 0x4F, 0x57}, + {0x24, 0x32, 0x32, 0x4F, 0x57}, + {0x40, 0x32, 0x32, 0x4F, 0x57}, + {0x40, 0x32, 0x32, 0x4F, 0x57} +}; + +const unsigned char cs40l2x_bst_k2_table[4][5] = { + {0x24, 0x49, 0x66, 0xA3, 0xEA}, + {0x24, 0x49, 0x66, 0xA3, 0xEA}, + {0x48, 0x49, 0x66, 0xA3, 0xEA}, + {0x48, 0x49, 0x66, 0xA3, 0xEA} +}; + +const unsigned char cs40l2x_bst_slope_table[4] = {0x75, 0x6B, 0x3B, 0x28}; + +bool cs40l2x_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS40L2X_DEVID: + case CS40L2X_REVID: + case CS40L2X_FABID: + case CS40L2X_RELID: + case CS40L2X_OTPID: + case CS40L2X_CTRL_ASYNC1: + case CS40L2X_OTP_CTRL0: + case CS40L2X_OTP_CTRL3: + case CS40L2X_OTP_CTRL4: + case CS40L2X_OTP_CTRL5: + case CS40L2X_OTP_CTRL6: + case CS40L2X_OTP_CTRL7: + case CS40L2X_OTP_CTRL8: + case CS40L2X_PWR_CTRL1: + case CS40L2X_PWR_CTRL2: + case CS40L2X_PWR_CTRL3: + case CS40L2X_CTRL_OVRRIDE: + case CS40L2X_AMP_OUT_MUTE: + case CS40L2X_PROTECT_REL_ERR_IGN: + case CS40L2X_LRCK_PAD_CONTROL: + case CS40L2X_SDIN_PAD_CONTROL: + case CS40L2X_GPIO_PAD_CONTROL: + case CS40L2X_JTAG_CONTROL: + case CS40L2X_PWRMGT_CTL: + case CS40L2X_WAKESRC_CTL: + case CS40L2X_PWRMGT_STS: + case CS40L2X_PWRMGT_TEST: + case CS40L2X_PLL_CLK_CTRL: + case CS40L2X_DSP_CLK_CTRL: + case CS40L2X_GLOBAL_CLK_CTRL: + case CS40L2X_DATA_FS_SEL: + case CS40L2X_PLL_LOOP_PARAM: + case CS40L2X_PLL_MISC_CTRL: + case CS40L2X_MDSYNC_EN: + case CS40L2X_MDSYNC_TX_ID: + case CS40L2X_MDSYNC_PWR_CTRL: + case CS40L2X_MDSYNC_DATA_TX: + case CS40L2X_MDSYNC_TX_STATUS: + case CS40L2X_MDSYNC_DATA_RX: + case CS40L2X_MDSYNC_RX_STATUS: + case CS40L2X_MDSYNC_ERR_STATUS: + case CS40L2X_MDSYNC_SYNC_PTE2: + case CS40L2X_MDSYNC_SYNC_PTE3: + case CS40L2X_MDSYNC_SYNC_MSM_STATUS: + case CS40L2X_BSTCVRT_VCTRL1: + case CS40L2X_BSTCVRT_VCTRL2: + case CS40L2X_BSTCVRT_PEAK_CUR: + case CS40L2X_BSTCVRT_SFT_RAMP: + case CS40L2X_BSTCVRT_COEFF: + case CS40L2X_BSTCVRT_SLOPE_LBST: + case CS40L2X_BSTCVRT_SW_FREQ: + case CS40L2X_BSTCVRT_DCM_CTRL: + case CS40L2X_BSTCVRT_DCM_MODE_FORCE: + case CS40L2X_BSTCVRT_OVERVOLT_CTRL: + case CS40L2X_VI_VOL_POL: + case CS40L2X_DTEMP_WARN_THLD: + case CS40L2X_DTEMP_CFG: + case CS40L2X_DTEMP_EN: + case CS40L2X_VPVBST_FS_SEL: + case CS40L2X_SP_ENABLES: + case CS40L2X_SP_RATE_CTRL: + case CS40L2X_SP_FORMAT: + case CS40L2X_SP_HIZ_CTRL: + case CS40L2X_SP_FRAME_TX_SLOT: + case CS40L2X_SP_FRAME_RX_SLOT: + case CS40L2X_SP_TX_WL: + case CS40L2X_SP_RX_WL: + case CS40L2X_DAC_PCM1_SRC: + case CS40L2X_ASP_TX1_SRC: + case CS40L2X_ASP_TX2_SRC: + case CS40L2X_ASP_TX3_SRC: + case CS40L2X_ASP_TX4_SRC: + case CS40L2X_DSP1_RX1_SRC: + case CS40L2X_DSP1_RX2_SRC: + case CS40L2X_DSP1_RX3_SRC: + case CS40L2X_DSP1_RX4_SRC: + case CS40L2X_DSP1_RX5_SRC: + case CS40L2X_DSP1_RX6_SRC: + case CS40L2X_DSP1_RX7_SRC: + case CS40L2X_DSP1_RX8_SRC: + case CS40L2X_NGATE1_SRC: + case CS40L2X_NGATE2_SRC: + case CS40L2X_AMP_DIG_VOL_CTRL: + case CS40L2X_VPBR_CFG: + case CS40L2X_VBBR_CFG: + case CS40L2X_VPBR_STATUS: + case CS40L2X_VBBR_STATUS: + case CS40L2X_OVERTEMP_CFG: + case CS40L2X_AMP_ERR_VOL: + case CS40L2X_VOL_STATUS_TO_DSP: + case CS40L2X_CLASSH_CFG: + case CS40L2X_WKFET_CFG: + case CS40L2X_NG_CFG: + case CS40L2X_AMP_GAIN_CTRL: + case CS40L2X_DAC_MSM_CFG: + case CS40L2X_IRQ1_CFG: + case CS40L2X_IRQ1_STATUS: + case CS40L2X_IRQ1_STATUS1: + case CS40L2X_IRQ1_STATUS2: + case CS40L2X_IRQ1_STATUS3: + case CS40L2X_IRQ1_STATUS4: + case CS40L2X_IRQ1_RAW_STATUS1: + case CS40L2X_IRQ1_RAW_STATUS2: + case CS40L2X_IRQ1_RAW_STATUS3: + case CS40L2X_IRQ1_RAW_STATUS4: + case CS40L2X_IRQ1_MASK1: + case CS40L2X_IRQ1_MASK2: + case CS40L2X_IRQ1_MASK3: + case CS40L2X_IRQ1_MASK4: + case CS40L2X_IRQ1_FRC1: + case CS40L2X_IRQ1_FRC2: + case CS40L2X_IRQ1_FRC3: + case CS40L2X_IRQ1_FRC4: + case CS40L2X_IRQ1_EDGE1: + case CS40L2X_IRQ1_EDGE4: + case CS40L2X_IRQ1_POL1: + case CS40L2X_IRQ1_POL2: + case CS40L2X_IRQ1_POL3: + case CS40L2X_IRQ1_POL4: + case CS40L2X_IRQ1_DB3: + case CS40L2X_IRQ2_CFG: + case CS40L2X_IRQ2_STATUS: + case CS40L2X_IRQ2_STATUS1: + case CS40L2X_IRQ2_STATUS2: + case CS40L2X_IRQ2_STATUS3: + case CS40L2X_IRQ2_STATUS4: + case CS40L2X_IRQ2_RAW_STATUS1: + case CS40L2X_IRQ2_RAW_STATUS2: + case CS40L2X_IRQ2_RAW_STATUS3: + case CS40L2X_IRQ2_RAW_STATUS4: + case CS40L2X_IRQ2_MASK1: + case CS40L2X_IRQ2_MASK2: + case CS40L2X_IRQ2_MASK3: + case CS40L2X_IRQ2_MASK4: + case CS40L2X_IRQ2_FRC1: + case CS40L2X_IRQ2_FRC2: + case CS40L2X_IRQ2_FRC3: + case CS40L2X_IRQ2_FRC4: + case CS40L2X_IRQ2_EDGE1: + case CS40L2X_IRQ2_EDGE4: + case CS40L2X_IRQ2_POL1: + case CS40L2X_IRQ2_POL2: + case CS40L2X_IRQ2_POL3: + case CS40L2X_IRQ2_POL4: + case CS40L2X_IRQ2_DB3: + case CS40L2X_GPIO_STATUS1: + case CS40L2X_GPIO1_CTRL1: + case CS40L2X_GPIO2_CTRL1: + case CS40L2X_GPIO3_CTRL1: + case CS40L2X_GPIO4_CTRL1: + case CS40L2X_MIXER_NGATE_CFG: + case CS40L2X_MIXER_NGATE_CH1_CFG: + case CS40L2X_MIXER_NGATE_CH2_CFG: + case CS40L2X_CLOCK_DETECT_1: + case CS40L2X_TIMER1_CONTROL: + case CS40L2X_TIMER1_COUNT_PRESET: + case CS40L2X_TIMER1_STATUS: + case CS40L2X_TIMER1_COUNT_READBACK: + case CS40L2X_TIMER1_DSP_CLK_CFG: + case CS40L2X_TIMER1_DSP_CLK_STATUS: + case CS40L2X_TIMER2_CONTROL: + case CS40L2X_TIMER2_COUNT_PRESET: + case CS40L2X_TIMER2_STATUS: + case CS40L2X_TIMER2_COUNT_READBACK: + case CS40L2X_TIMER2_DSP_CLK_CFG: + case CS40L2X_TIMER2_DSP_CLK_STATUS: + case CS40L2X_DFT_JTAG_CONTROL: + case CS40L2X_DIE_STS1: + case CS40L2X_DIE_STS2: + case CS40L2X_TEMP_CAL1: + case CS40L2X_TEMP_CAL2: + case CS40L2X_DSP1_TIMESTAMP_COUNT: + case CS40L2X_DSP1_SYS_ID: + case CS40L2X_DSP1_SYS_VERSION: + case CS40L2X_DSP1_SYS_CORE_ID: + case CS40L2X_DSP1_SYS_AHB_ADDR: + case CS40L2X_DSP1_SYS_XSRAM_SIZE: + case CS40L2X_DSP1_SYS_YSRAM_SIZE: + case CS40L2X_DSP1_SYS_PSRAM_SIZE: + case CS40L2X_DSP1_SYS_PM_BOOT_SIZE: + case CS40L2X_DSP1_SYS_FEATURES: + case CS40L2X_DSP1_SYS_FIR_FILTERS: + case CS40L2X_DSP1_SYS_LMS_FILTERS: + case CS40L2X_DSP1_SYS_XM_BANK_SIZE: + case CS40L2X_DSP1_SYS_YM_BANK_SIZE: + case CS40L2X_DSP1_SYS_PM_BANK_SIZE: + case CS40L2X_DSP1_AHBM_WIN0_CTRL0: + case CS40L2X_DSP1_AHBM_WIN0_CTRL1: + case CS40L2X_DSP1_AHBM_WIN1_CTRL0: + case CS40L2X_DSP1_AHBM_WIN1_CTRL1: + case CS40L2X_DSP1_AHBM_WIN2_CTRL0: + case CS40L2X_DSP1_AHBM_WIN2_CTRL1: + case CS40L2X_DSP1_AHBM_WIN3_CTRL0: + case CS40L2X_DSP1_AHBM_WIN3_CTRL1: + case CS40L2X_DSP1_AHBM_WIN4_CTRL0: + case CS40L2X_DSP1_AHBM_WIN4_CTRL1: + case CS40L2X_DSP1_AHBM_WIN5_CTRL0: + case CS40L2X_DSP1_AHBM_WIN5_CTRL1: + case CS40L2X_DSP1_AHBM_WIN6_CTRL0: + case CS40L2X_DSP1_AHBM_WIN6_CTRL1: + case CS40L2X_DSP1_AHBM_WIN7_CTRL0: + case CS40L2X_DSP1_AHBM_WIN7_CTRL1: + case CS40L2X_DSP1_AHBM_WIN_DBG_CTRL0: + case CS40L2X_DSP1_AHBM_WIN_DBG_CTRL1: + case CS40L2X_DSP1_DEBUG: + case CS40L2X_DSP1_TIMER_CTRL: + case CS40L2X_DSP1_RX1_RATE: + case CS40L2X_DSP1_RX2_RATE: + case CS40L2X_DSP1_RX3_RATE: + case CS40L2X_DSP1_RX4_RATE: + case CS40L2X_DSP1_RX5_RATE: + case CS40L2X_DSP1_RX6_RATE: + case CS40L2X_DSP1_RX7_RATE: + case CS40L2X_DSP1_RX8_RATE: + case CS40L2X_DSP1_TX1_RATE: + case CS40L2X_DSP1_TX2_RATE: + case CS40L2X_DSP1_TX3_RATE: + case CS40L2X_DSP1_TX4_RATE: + case CS40L2X_DSP1_TX5_RATE: + case CS40L2X_DSP1_TX6_RATE: + case CS40L2X_DSP1_TX7_RATE: + case CS40L2X_DSP1_TX8_RATE: + case CS40L2X_DSP1_NMI_CTRL1: + case CS40L2X_DSP1_NMI_CTRL2: + case CS40L2X_DSP1_NMI_CTRL3: + case CS40L2X_DSP1_NMI_CTRL4: + case CS40L2X_DSP1_NMI_CTRL5: + case CS40L2X_DSP1_NMI_CTRL6: + case CS40L2X_DSP1_NMI_CTRL7: + case CS40L2X_DSP1_NMI_CTRL8: + case CS40L2X_DSP1_RESUME_CTRL: + case CS40L2X_DSP1_IRQ1_CTRL: + case CS40L2X_DSP1_IRQ2_CTRL: + case CS40L2X_DSP1_IRQ3_CTRL: + case CS40L2X_DSP1_IRQ4_CTRL: + case CS40L2X_DSP1_IRQ5_CTRL: + case CS40L2X_DSP1_IRQ6_CTRL: + case CS40L2X_DSP1_IRQ7_CTRL: + case CS40L2X_DSP1_IRQ8_CTRL: + case CS40L2X_DSP1_IRQ9_CTRL: + case CS40L2X_DSP1_IRQ10_CTRL: + case CS40L2X_DSP1_IRQ11_CTRL: + case CS40L2X_DSP1_IRQ12_CTRL: + case CS40L2X_DSP1_IRQ13_CTRL: + case CS40L2X_DSP1_IRQ14_CTRL: + case CS40L2X_DSP1_IRQ15_CTRL: + case CS40L2X_DSP1_IRQ16_CTRL: + case CS40L2X_DSP1_IRQ17_CTRL: + case CS40L2X_DSP1_IRQ18_CTRL: + case CS40L2X_DSP1_IRQ19_CTRL: + case CS40L2X_DSP1_IRQ20_CTRL: + case CS40L2X_DSP1_IRQ21_CTRL: + case CS40L2X_DSP1_IRQ22_CTRL: + case CS40L2X_DSP1_IRQ23_CTRL: + case CS40L2X_DSP1_SCRATCH1: + case CS40L2X_DSP1_SCRATCH2: + case CS40L2X_DSP1_SCRATCH3: + case CS40L2X_DSP1_SCRATCH4: + case CS40L2X_DSP1_CCM_CORE_CTRL: + case CS40L2X_DSP1_CCM_CLK_OVERRIDE: + case CS40L2X_DSP1_XM_MSTR_EN: + case CS40L2X_DSP1_XM_CORE_PRI: + case CS40L2X_DSP1_XM_AHB_PACK_PL_PRI: + case CS40L2X_DSP1_XM_AHB_UP_PL_PRI: + case CS40L2X_DSP1_XM_ACCEL_PL0_PRI: + case CS40L2X_DSP1_XM_NPL0_PRI: + case CS40L2X_DSP1_YM_MSTR_EN: + case CS40L2X_DSP1_YM_CORE_PRI: + case CS40L2X_DSP1_YM_AHB_PACK_PL_PRI: + case CS40L2X_DSP1_YM_AHB_UP_PL_PRI: + case CS40L2X_DSP1_YM_ACCEL_PL0_PRI: + case CS40L2X_DSP1_YM_NPL0_PRI: + case CS40L2X_DSP1_PM_MSTR_EN: + case CS40L2X_DSP1_PM_PATCH0_ADDR: + case CS40L2X_DSP1_PM_PATCH0_EN: + case CS40L2X_DSP1_PM_PATCH0_DATA_LO: + case CS40L2X_DSP1_PM_PATCH0_DATA_HI: + case CS40L2X_DSP1_PM_PATCH1_ADDR: + case CS40L2X_DSP1_PM_PATCH1_EN: + case CS40L2X_DSP1_PM_PATCH1_DATA_LO: + case CS40L2X_DSP1_PM_PATCH1_DATA_HI: + case CS40L2X_DSP1_PM_PATCH2_ADDR: + case CS40L2X_DSP1_PM_PATCH2_EN: + case CS40L2X_DSP1_PM_PATCH2_DATA_LO: + case CS40L2X_DSP1_PM_PATCH2_DATA_HI: + case CS40L2X_DSP1_PM_PATCH3_ADDR: + case CS40L2X_DSP1_PM_PATCH3_EN: + case CS40L2X_DSP1_PM_PATCH3_DATA_LO: + case CS40L2X_DSP1_PM_PATCH3_DATA_HI: + case CS40L2X_DSP1_PM_PATCH4_ADDR: + case CS40L2X_DSP1_PM_PATCH4_EN: + case CS40L2X_DSP1_PM_PATCH4_DATA_LO: + case CS40L2X_DSP1_PM_PATCH4_DATA_HI: + case CS40L2X_DSP1_PM_PATCH5_ADDR: + case CS40L2X_DSP1_PM_PATCH5_EN: + case CS40L2X_DSP1_PM_PATCH5_DATA_LO: + case CS40L2X_DSP1_PM_PATCH5_DATA_HI: + case CS40L2X_DSP1_PM_PATCH6_ADDR: + case CS40L2X_DSP1_PM_PATCH6_EN: + case CS40L2X_DSP1_PM_PATCH6_DATA_LO: + case CS40L2X_DSP1_PM_PATCH6_DATA_HI: + case CS40L2X_DSP1_PM_PATCH7_ADDR: + case CS40L2X_DSP1_PM_PATCH7_EN: + case CS40L2X_DSP1_PM_PATCH7_DATA_LO: + case CS40L2X_DSP1_PM_PATCH7_DATA_HI: + case CS40L2X_DSP1_MPU_XM_ACCESS0: + case CS40L2X_DSP1_MPU_YM_ACCESS0: + case CS40L2X_DSP1_MPU_WNDW_ACCESS0: + case CS40L2X_DSP1_MPU_XREG_ACCESS0: + case CS40L2X_DSP1_MPU_YREG_ACCESS0: + case CS40L2X_DSP1_MPU_XM_ACCESS1: + case CS40L2X_DSP1_MPU_YM_ACCESS1: + case CS40L2X_DSP1_MPU_WNDW_ACCESS1: + case CS40L2X_DSP1_MPU_XREG_ACCESS1: + case CS40L2X_DSP1_MPU_YREG_ACCESS1: + case CS40L2X_DSP1_MPU_XM_ACCESS2: + case CS40L2X_DSP1_MPU_YM_ACCESS2: + case CS40L2X_DSP1_MPU_WNDW_ACCESS2: + case CS40L2X_DSP1_MPU_XREG_ACCESS2: + case CS40L2X_DSP1_MPU_YREG_ACCESS2: + case CS40L2X_DSP1_MPU_XM_ACCESS3: + case CS40L2X_DSP1_MPU_YM_ACCESS3: + case CS40L2X_DSP1_MPU_WNDW_ACCESS3: + case CS40L2X_DSP1_MPU_XREG_ACCESS3: + case CS40L2X_DSP1_MPU_YREG_ACCESS3: + case CS40L2X_DSP1_MPU_XM_VIO_ADDR: + case CS40L2X_DSP1_MPU_XM_VIO_STATUS: + case CS40L2X_DSP1_MPU_YM_VIO_ADDR: + case CS40L2X_DSP1_MPU_YM_VIO_STATUS: + case CS40L2X_DSP1_MPU_PM_VIO_ADDR: + case CS40L2X_DSP1_MPU_PM_VIO_STATUS: + case CS40L2X_DSP1_MPU_LOCK_CONFIG: + case CS40L2X_DSP1_MPU_WDT_RST_CTRL: + case CS40L2X_DSP1_STRMARB_MSTR0_CFG0: + case CS40L2X_DSP1_STRMARB_MSTR0_CFG1: + case CS40L2X_DSP1_STRMARB_MSTR0_CFG2: + case CS40L2X_DSP1_STRMARB_MSTR1_CFG0: + case CS40L2X_DSP1_STRMARB_MSTR1_CFG1: + case CS40L2X_DSP1_STRMARB_MSTR1_CFG2: + case CS40L2X_DSP1_STRMARB_MSTR2_CFG0: + case CS40L2X_DSP1_STRMARB_MSTR2_CFG1: + case CS40L2X_DSP1_STRMARB_MSTR2_CFG2: + case CS40L2X_DSP1_STRMARB_MSTR3_CFG0: + case CS40L2X_DSP1_STRMARB_MSTR3_CFG1: + case CS40L2X_DSP1_STRMARB_MSTR3_CFG2: + case CS40L2X_DSP1_STRMARB_MSTR4_CFG0: + case CS40L2X_DSP1_STRMARB_MSTR4_CFG1: + case CS40L2X_DSP1_STRMARB_MSTR4_CFG2: + case CS40L2X_DSP1_STRMARB_MSTR5_CFG0: + case CS40L2X_DSP1_STRMARB_MSTR5_CFG1: + case CS40L2X_DSP1_STRMARB_MSTR5_CFG2: + case CS40L2X_DSP1_STRMARB_MSTR6_CFG0: + case CS40L2X_DSP1_STRMARB_MSTR6_CFG1: + case CS40L2X_DSP1_STRMARB_MSTR6_CFG2: + case CS40L2X_DSP1_STRMARB_MSTR7_CFG0: + case CS40L2X_DSP1_STRMARB_MSTR7_CFG1: + case CS40L2X_DSP1_STRMARB_MSTR7_CFG2: + case CS40L2X_DSP1_STRMARB_TX0_CFG0: + case CS40L2X_DSP1_STRMARB_TX0_CFG1: + case CS40L2X_DSP1_STRMARB_TX1_CFG0: + case CS40L2X_DSP1_STRMARB_TX1_CFG1: + case CS40L2X_DSP1_STRMARB_TX2_CFG0: + case CS40L2X_DSP1_STRMARB_TX2_CFG1: + case CS40L2X_DSP1_STRMARB_TX3_CFG0: + case CS40L2X_DSP1_STRMARB_TX3_CFG1: + case CS40L2X_DSP1_STRMARB_TX4_CFG0: + case CS40L2X_DSP1_STRMARB_TX4_CFG1: + case CS40L2X_DSP1_STRMARB_TX5_CFG0: + case CS40L2X_DSP1_STRMARB_TX5_CFG1: + case CS40L2X_DSP1_STRMARB_TX6_CFG0: + case CS40L2X_DSP1_STRMARB_TX6_CFG1: + case CS40L2X_DSP1_STRMARB_TX7_CFG0: + case CS40L2X_DSP1_STRMARB_TX7_CFG1: + case CS40L2X_DSP1_STRMARB_RX0_CFG0: + case CS40L2X_DSP1_STRMARB_RX0_CFG1: + case CS40L2X_DSP1_STRMARB_RX1_CFG0: + case CS40L2X_DSP1_STRMARB_RX1_CFG1: + case CS40L2X_DSP1_STRMARB_RX2_CFG0: + case CS40L2X_DSP1_STRMARB_RX2_CFG1: + case CS40L2X_DSP1_STRMARB_RX3_CFG0: + case CS40L2X_DSP1_STRMARB_RX3_CFG1: + case CS40L2X_DSP1_STRMARB_RX4_CFG0: + case CS40L2X_DSP1_STRMARB_RX4_CFG1: + case CS40L2X_DSP1_STRMARB_RX5_CFG0: + case CS40L2X_DSP1_STRMARB_RX5_CFG1: + case CS40L2X_DSP1_STRMARB_RX6_CFG0: + case CS40L2X_DSP1_STRMARB_RX6_CFG1: + case CS40L2X_DSP1_STRMARB_RX7_CFG0: + case CS40L2X_DSP1_STRMARB_RX7_CFG1: + case CS40L2X_DSP1_STRMARB_IRQ0_CFG0: + case CS40L2X_DSP1_STRMARB_IRQ0_CFG1: + case CS40L2X_DSP1_STRMARB_IRQ0_CFG2: + case CS40L2X_DSP1_STRMARB_IRQ1_CFG0: + case CS40L2X_DSP1_STRMARB_IRQ1_CFG1: + case CS40L2X_DSP1_STRMARB_IRQ1_CFG2: + case CS40L2X_DSP1_STRMARB_IRQ2_CFG0: + case CS40L2X_DSP1_STRMARB_IRQ2_CFG1: + case CS40L2X_DSP1_STRMARB_IRQ2_CFG2: + case CS40L2X_DSP1_STRMARB_IRQ3_CFG0: + case CS40L2X_DSP1_STRMARB_IRQ3_CFG1: + case CS40L2X_DSP1_STRMARB_IRQ3_CFG2: + case CS40L2X_DSP1_STRMARB_IRQ4_CFG0: + case CS40L2X_DSP1_STRMARB_IRQ4_CFG1: + case CS40L2X_DSP1_STRMARB_IRQ4_CFG2: + case CS40L2X_DSP1_STRMARB_IRQ5_CFG0: + case CS40L2X_DSP1_STRMARB_IRQ5_CFG1: + case CS40L2X_DSP1_STRMARB_IRQ5_CFG2: + case CS40L2X_DSP1_STRMARB_IRQ6_CFG0: + case CS40L2X_DSP1_STRMARB_IRQ6_CFG1: + case CS40L2X_DSP1_STRMARB_IRQ6_CFG2: + case CS40L2X_DSP1_STRMARB_IRQ7_CFG0: + case CS40L2X_DSP1_STRMARB_IRQ7_CFG1: + case CS40L2X_DSP1_STRMARB_IRQ7_CFG2: + case CS40L2X_DSP1_STRMARB_RESYNC_MSK: + case CS40L2X_DSP1_STRMARB_ERR_STATUS: + case CS40L2X_DSP1_INTPCTL_RES_STATIC: + case CS40L2X_DSP1_INTPCTL_RES_DYN: + case CS40L2X_DSP1_INTPCTL_NMI_CTRL: + case CS40L2X_DSP1_INTPCTL_IRQ_INV: + case CS40L2X_DSP1_INTPCTL_IRQ_MODE: + case CS40L2X_DSP1_INTPCTL_IRQ_EN: + case CS40L2X_DSP1_INTPCTL_IRQ_MSK: + case CS40L2X_DSP1_INTPCTL_IRQ_ERR: + case CS40L2X_DSP1_INTPCTL_IRQ_PEND: + case CS40L2X_DSP1_INTPCTL_TESTBITS: + case CS40L2X_DSP1_WDT_CONTROL: + case CS40L2X_DSP1_WDT_STATUS: + case CS40L2X_OTP_TRIM_1: + case CS40L2X_OTP_TRIM_2: + case CS40L2X_OTP_TRIM_3: + case CS40L2X_OTP_TRIM_4: + case CS40L2X_OTP_TRIM_5: + case CS40L2X_OTP_TRIM_6: + case CS40L2X_OTP_TRIM_7: + case CS40L2X_OTP_TRIM_8: + case CS40L2X_OTP_TRIM_9: + case CS40L2X_OTP_TRIM_10: + case CS40L2X_OTP_TRIM_11: + case CS40L2X_OTP_TRIM_12: + case CS40L2X_OTP_TRIM_13: + case CS40L2X_OTP_TRIM_14: + case CS40L2X_OTP_TRIM_15: + case CS40L2X_OTP_TRIM_16: + case CS40L2X_OTP_TRIM_17: + case CS40L2X_OTP_TRIM_18: + case CS40L2X_OTP_TRIM_19: + case CS40L2X_OTP_TRIM_20: + case CS40L2X_OTP_TRIM_21: + case CS40L2X_OTP_TRIM_22: + case CS40L2X_OTP_TRIM_23: + case CS40L2X_OTP_TRIM_24: + case CS40L2X_OTP_TRIM_25: + case CS40L2X_OTP_TRIM_26: + case CS40L2X_OTP_TRIM_27: + case CS40L2X_OTP_TRIM_28: + case CS40L2X_OTP_TRIM_29: + case CS40L2X_OTP_TRIM_30: + case CS40L2X_OTP_TRIM_31: + case CS40L2X_OTP_TRIM_32: + case CS40L2X_OTP_TRIM_33: + case CS40L2X_OTP_TRIM_34: + case CS40L2X_OTP_TRIM_35: + case CS40L2X_OTP_TRIM_36: + case CS40L2X_OTP_MEM0 ... CS40L2X_OTP_MEM31: + case CS40L2X_DSP1_XMEM_PACK_0 ... CS40L2X_DSP1_XMEM_PACK_3068: + case CS40L2X_DSP1_XMEM_UNPACK32_0 ... CS40L2X_DSP1_XMEM_UNPACK32_2046: + case CS40L2X_DSP1_XMEM_UNPACK24_0 ... CS40L2X_DSP1_XMEM_UNPACK24_4093: + case CS40L2X_DSP1_YMEM_PACK_0 ... CS40L2X_DSP1_YMEM_PACK_1532: + case CS40L2X_DSP1_YMEM_UNPACK32_0 ... CS40L2X_DSP1_YMEM_UNPACK32_1022: + case CS40L2X_DSP1_YMEM_UNPACK24_0 ... CS40L2X_DSP1_YMEM_UNPACK24_2045: + case CS40L2X_DSP1_PMEM_0 ... CS40L2X_DSP1_PMEM_5114: + return true; + default: + return false; + } +} + +bool cs40l2x_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS40L2X_OTP_MEM0 ... CS40L2X_OTP_MEM31: + return true; + default: + return false; + } +} + +static const struct cs40l2x_trim cs40l2x_trim_table_c[] = { + /* addr shift size */ + {0x00002030, 0, 4}, /*TRIM_OSC_FREQ_TRIM*/ + {0x00002030, 7, 1}, /*TRIM_OSC_TRIM_DONE*/ + {0x0000208C, 24, 6}, /*TST_DIGREG_VREF_TRIM*/ + {0x00002090, 14, 4}, /*TST_REF_TRIM*/ + {0x00002090, 10, 4}, /*TST_REF_TEMPCO_TRIM*/ + {0x0000300C, 11, 4}, /*PLL_LDOA_TST_VREF_TRIM*/ + {0x0000394C, 23, 2}, /*BST_ATEST_CM_VOFF*/ + {0x00003950, 0, 7}, /*BST_ATRIM_IADC_OFFSET*/ + {0x00003950, 8, 7}, /*BST_ATRIM_IADC_GAIN1*/ + {0x00003950, 16, 8}, /*BST_ATRIM_IPKCOMP_OFFSET1*/ + {0x00003950, 24, 8}, /*BST_ATRIM_IPKCOMP_GAIN1*/ + {0x00003954, 0, 7}, /*BST_ATRIM_IADC_OFFSET2*/ + {0x00003954, 8, 7}, /*BST_ATRIM_IADC_GAIN2*/ + {0x00003954, 16, 8}, /*BST_ATRIM_IPKCOMP_OFFSET2*/ + {0x00003954, 24, 8}, /*BST_ATRIM_IPKCOMP_GAIN2*/ + {0x00003958, 0, 7}, /*BST_ATRIM_IADC_OFFSET3*/ + {0x00003958, 8, 7}, /*BST_ATRIM_IADC_GAIN3*/ + {0x00003958, 16, 8}, /*BST_ATRIM_IPKCOMP_OFFSET3*/ + {0x00003958, 24, 8}, /*BST_ATRIM_IPKCOMP_GAIN3*/ + {0x0000395C, 0, 7}, /*BST_ATRIM_IADC_OFFSET4*/ + {0x0000395C, 8, 7}, /*BST_ATRIM_IADC_GAIN4*/ + {0x0000395C, 16, 8}, /*BST_ATRIM_IPKCOMP_OFFSET4*/ + {0x0000395C, 24, 8}, /*BST_ATRIM_IPKCOMP_GAIN4*/ + {0x0000416C, 0, 8}, /*VMON_GAIN_OTP_VAL*/ + {0x00004160, 0, 7}, /*VMON_OFFSET_OTP_VAL*/ + {0x0000416C, 8, 8}, /*IMON_GAIN_OTP_VAL*/ + {0x00004160, 16, 10}, /*IMON_OFFSET_OTP_VAL*/ + {0x0000416C, 16, 12}, /*VMON_CM_GAIN_OTP_VAL*/ + {0x0000416C, 28, 1}, /*VMON_CM_GAIN_SIGN_OTP_VAL*/ + {0x00004170, 0, 6}, /*IMON_CAL_TEMPCO_OTP_VAL*/ + {0x00004170, 6, 1}, /*IMON_CAL_TEMPCO_SIGN_OTP*/ + {0x00004170, 8, 6}, /*IMON_CAL_TEMPCO2_OTP_VAL*/ + {0x00004170, 14, 1}, /*IMON_CAL_TEMPCO2_DN_UPB_OTP_VAL*/ + {0x00004170, 16, 9}, /*IMON_CAL_TEMPCO_TBASE_OTP_VAL*/ + {0x00004360, 0, 5}, /*TEMP_GAIN_OTP_VAL*/ + {0x00004360, 6, 9}, /*TEMP_OFFSET_OTP_VAL*/ + {0x00004448, 0, 8}, /*VP_SARADC_OFFSET*/ + {0x00004448, 8, 8}, /*VP_GAIN_INDEX*/ + {0x00004448, 16, 8}, /*VBST_SARADC_OFFSET*/ + {0x00004448, 24, 8}, /*VBST_GAIN_INDEX*/ + {0x0000444C, 0, 3}, /*ANA_SELINVREF*/ + {0x00006E30, 0, 5}, /*GAIN_ERR_COEFF_0*/ + {0x00006E30, 8, 5}, /*GAIN_ERR_COEFF_1*/ + {0x00006E30, 16, 5}, /*GAIN_ERR_COEFF_2*/ + {0x00006E30, 24, 5}, /*GAIN_ERR_COEFF_3*/ + {0x00006E34, 0, 5}, /*GAIN_ERR_COEFF_4*/ + {0x00006E34, 8, 5}, /*GAIN_ERR_COEFF_5*/ + {0x00006E34, 16, 5}, /*GAIN_ERR_COEFF_6*/ + {0x00006E34, 24, 5}, /*GAIN_ERR_COEFF_7*/ + {0x00006E38, 0, 5}, /*GAIN_ERR_COEFF_8*/ + {0x00006E38, 8, 5}, /*GAIN_ERR_COEFF_9*/ + {0x00006E38, 16, 5}, /*GAIN_ERR_COEFF_10*/ + {0x00006E38, 24, 5}, /*GAIN_ERR_COEFF_11*/ + {0x00006E3C, 0, 5}, /*GAIN_ERR_COEFF_12*/ + {0x00006E3C, 8, 5}, /*GAIN_ERR_COEFF_13*/ + {0x00006E3C, 16, 5}, /*GAIN_ERR_COEFF_14*/ + {0x00006E3C, 24, 5}, /*GAIN_ERR_COEFF_15*/ + {0x00006E40, 0, 5}, /*GAIN_ERR_COEFF_16*/ + {0x00006E40, 8, 5}, /*GAIN_ERR_COEFF_17*/ + {0x00006E40, 16, 5}, /*GAIN_ERR_COEFF_18*/ + {0x00006E40, 24, 5}, /*GAIN_ERR_COEFF_19*/ + {0x00006E44, 0, 5}, /*GAIN_ERR_COEFF_20*/ + {0x00006E48, 0, 10}, /*VOFF_GAIN_0*/ + {0x00006E48, 10, 10}, /*VOFF_GAIN_1*/ + {0x00006E48, 20, 10}, /*VOFF_GAIN_2*/ + {0x00006E4C, 0, 10}, /*VOFF_GAIN_3*/ + {0x00006E4C, 10, 10}, /*VOFF_GAIN_4*/ + {0x00006E4C, 20, 10}, /*VOFF_GAIN_5*/ + {0x00006E50, 0, 10}, /*VOFF_GAIN_6*/ + {0x00006E50, 10, 10}, /*VOFF_GAIN_7*/ + {0x00006E50, 20, 10}, /*VOFF_GAIN_8*/ + {0x00006E54, 0, 10}, /*VOFF_GAIN_9*/ + {0x00006E54, 10, 10}, /*VOFF_GAIN_10*/ + {0x00006E54, 20, 10}, /*VOFF_GAIN_11*/ + {0x00006E58, 0, 10}, /*VOFF_GAIN_12*/ + {0x00006E58, 10, 10}, /*VOFF_GAIN_13*/ + {0x00006E58, 20, 10}, /*VOFF_GAIN_14*/ + {0x00006E5C, 0, 10}, /*VOFF_GAIN_15*/ + {0x00006E5C, 10, 10}, /*VOFF_GAIN_16*/ + {0x00006E5C, 20, 10}, /*VOFF_GAIN_17*/ + {0x00006E60, 0, 10}, /*VOFF_GAIN_18*/ + {0x00006E60, 10, 10}, /*VOFF_GAIN_19*/ + {0x00006E60, 20, 10}, /*VOFF_GAIN_20*/ + {0x00006E64, 0, 10}, /*VOFF_INT1*/ + {0x00007418, 7, 5}, /*DS_SPK_INT1_CAP_TRIM*/ + {0x0000741C, 0, 5}, /*DS_SPK_INT2_CAP_TRIM*/ + {0x0000741C, 11, 4}, /*DS_SPK_LPF_CAP_TRIM*/ + {0x0000741C, 19, 4}, /*DS_SPK_QUAN_CAP_TRIM*/ + {0x00007434, 17, 1}, /*FORCE_CAL*/ + {0x00007434, 18, 7}, /*CAL_OVERRIDE*/ + {0x00007068, 0, 9}, /*MODIX*/ + {0x0000410C, 7, 1}, /*VIMON_DLY_NOT_COMB*/ + {0x0000400C, 0, 7}, /*VIMON_DLY*/ + {0x00000000, 0, 1}, /*extra bit*/ + {0x00017040, 0, 8}, /*X_COORDINATE*/ + {0x00017040, 8, 8}, /*Y_COORDINATE*/ + {0x00017040, 16, 8}, /*WAFER_ID*/ + {0x00017040, 24, 8}, /*DVS*/ + {0x00017044, 0, 24}, /*LOT_NUMBER*/ +}; + +static const struct cs40l2x_trim cs40l2x_trim_table_d[] = { + /* addr shift size */ + {0x00002030, 0, 4}, /*TRIM_OSC_FREQ_TRIM*/ + {0x00002030, 7, 1}, /*TRIM_OSC_TRIM_DONE*/ + {0x0000208C, 24, 6}, /*TST_DIGREG_VREF_TRIM*/ + {0x00002090, 14, 4}, /*TST_REF_TRIM*/ + {0x00002090, 10, 4}, /*TST_REF_TEMPCO_TRIM*/ + {0x0000300C, 11, 4}, /*PLL_LDOA_TST_VREF_TRIM*/ + {0x0000394C, 23, 2}, /*BST_ATEST_CM_VOFF*/ + {0x00003950, 0, 7}, /*BST_ATRIM_IADC_OFFSET*/ + {0x00003950, 8, 7}, /*BST_ATRIM_IADC_GAIN1*/ + {0x00003950, 16, 8}, /*BST_ATRIM_IPKCOMP_OFFSET1*/ + {0x00003950, 24, 8}, /*BST_ATRIM_IPKCOMP_GAIN1*/ + {0x00003954, 0, 7}, /*BST_ATRIM_IADC_OFFSET2*/ + {0x00003954, 8, 7}, /*BST_ATRIM_IADC_GAIN2*/ + {0x00003954, 16, 8}, /*BST_ATRIM_IPKCOMP_OFFSET2*/ + {0x00003954, 24, 8}, /*BST_ATRIM_IPKCOMP_GAIN2*/ + {0x00003958, 0, 7}, /*BST_ATRIM_IADC_OFFSET3*/ + {0x00003958, 8, 7}, /*BST_ATRIM_IADC_GAIN3*/ + {0x00003958, 16, 8}, /*BST_ATRIM_IPKCOMP_OFFSET3*/ + {0x00003958, 24, 8}, /*BST_ATRIM_IPKCOMP_GAIN3*/ + {0x0000395C, 0, 7}, /*BST_ATRIM_IADC_OFFSET4*/ + {0x0000395C, 8, 7}, /*BST_ATRIM_IADC_GAIN4*/ + {0x0000395C, 16, 8}, /*BST_ATRIM_IPKCOMP_OFFSET4*/ + {0x0000395C, 24, 8}, /*BST_ATRIM_IPKCOMP_GAIN4*/ + {0x0000416C, 0, 8}, /*VMON_GAIN_OTP_VAL*/ + {0x00004160, 0, 7}, /*VMON_OFFSET_OTP_VAL*/ + {0x0000416C, 8, 8}, /*IMON_GAIN_OTP_VAL*/ + {0x00004160, 16, 10}, /*IMON_OFFSET_OTP_VAL*/ + {0x0000416C, 16, 12}, /*VMON_CM_GAIN_OTP_VAL*/ + {0x0000416C, 28, 1}, /*VMON_CM_GAIN_SIGN_OTP_VAL*/ + {0x00004170, 0, 6}, /*IMON_CAL_TEMPCO_OTP_VAL*/ + {0x00004170, 6, 1}, /*IMON_CAL_TEMPCO_SIGN_OTP*/ + {0x00004170, 8, 6}, /*IMON_CAL_TEMPCO2_OTP_VAL*/ + {0x00004170, 14, 1}, /*IMON_CAL_TEMPCO2_DN_UPB_OTP_VAL*/ + {0x00004170, 16, 9}, /*IMON_CAL_TEMPCO_TBASE_OTP_VAL*/ + {0x00004360, 0, 5}, /*TEMP_GAIN_OTP_VAL*/ + {0x00004360, 6, 9}, /*TEMP_OFFSET_OTP_VAL*/ + {0x00004448, 0, 8}, /*VP_SARADC_OFFSET*/ + {0x00004448, 8, 8}, /*VP_GAIN_INDEX*/ + {0x00004448, 16, 8}, /*VBST_SARADC_OFFSET*/ + {0x00004448, 24, 8}, /*VBST_GAIN_INDEX*/ + {0x0000444C, 0, 3}, /*ANA_SELINVREF*/ + {0x00006E30, 0, 5}, /*GAIN_ERR_COEFF_0*/ + {0x00006E30, 8, 5}, /*GAIN_ERR_COEFF_1*/ + {0x00006E30, 16, 5}, /*GAIN_ERR_COEFF_2*/ + {0x00006E30, 24, 5}, /*GAIN_ERR_COEFF_3*/ + {0x00006E34, 0, 5}, /*GAIN_ERR_COEFF_4*/ + {0x00006E34, 8, 5}, /*GAIN_ERR_COEFF_5*/ + {0x00006E34, 16, 5}, /*GAIN_ERR_COEFF_6*/ + {0x00006E34, 24, 5}, /*GAIN_ERR_COEFF_7*/ + {0x00006E38, 0, 5}, /*GAIN_ERR_COEFF_8*/ + {0x00006E38, 8, 5}, /*GAIN_ERR_COEFF_9*/ + {0x00006E38, 16, 5}, /*GAIN_ERR_COEFF_10*/ + {0x00006E38, 24, 5}, /*GAIN_ERR_COEFF_11*/ + {0x00006E3C, 0, 5}, /*GAIN_ERR_COEFF_12*/ + {0x00006E3C, 8, 5}, /*GAIN_ERR_COEFF_13*/ + {0x00006E3C, 16, 5}, /*GAIN_ERR_COEFF_14*/ + {0x00006E3C, 24, 5}, /*GAIN_ERR_COEFF_15*/ + {0x00006E40, 0, 5}, /*GAIN_ERR_COEFF_16*/ + {0x00006E40, 8, 5}, /*GAIN_ERR_COEFF_17*/ + {0x00006E40, 16, 5}, /*GAIN_ERR_COEFF_18*/ + {0x00006E40, 24, 5}, /*GAIN_ERR_COEFF_19*/ + {0x00006E44, 0, 5}, /*GAIN_ERR_COEFF_20*/ + {0x00006E48, 0, 10}, /*VOFF_GAIN_0*/ + {0x00006E48, 10, 10}, /*VOFF_GAIN_1*/ + {0x00006E48, 20, 10}, /*VOFF_GAIN_2*/ + {0x00006E4C, 0, 10}, /*VOFF_GAIN_3*/ + {0x00006E4C, 10, 10}, /*VOFF_GAIN_4*/ + {0x00006E4C, 20, 10}, /*VOFF_GAIN_5*/ + {0x00006E50, 0, 10}, /*VOFF_GAIN_6*/ + {0x00006E50, 10, 10}, /*VOFF_GAIN_7*/ + {0x00006E50, 20, 10}, /*VOFF_GAIN_8*/ + {0x00006E54, 0, 10}, /*VOFF_GAIN_9*/ + {0x00006E54, 10, 10}, /*VOFF_GAIN_10*/ + {0x00006E54, 20, 10}, /*VOFF_GAIN_11*/ + {0x00006E58, 0, 10}, /*VOFF_GAIN_12*/ + {0x00006E58, 10, 10}, /*VOFF_GAIN_13*/ + {0x00006E58, 20, 10}, /*VOFF_GAIN_14*/ + {0x00006E5C, 0, 10}, /*VOFF_GAIN_15*/ + {0x00006E5C, 10, 10}, /*VOFF_GAIN_16*/ + {0x00006E5C, 20, 10}, /*VOFF_GAIN_17*/ + {0x00006E60, 0, 10}, /*VOFF_GAIN_18*/ + {0x00006E60, 10, 10}, /*VOFF_GAIN_19*/ + {0x00006E60, 20, 10}, /*VOFF_GAIN_20*/ + {0x00006E64, 0, 10}, /*VOFF_INT1*/ + {0x00007418, 7, 5}, /*DS_SPK_INT1_CAP_TRIM*/ + {0x0000741C, 0, 5}, /*DS_SPK_INT2_CAP_TRIM*/ + {0x0000741C, 11, 4}, /*DS_SPK_LPF_CAP_TRIM*/ + {0x0000741C, 19, 4}, /*DS_SPK_QUAN_CAP_TRIM*/ + {0x00007434, 17, 1}, /*FORCE_CAL*/ + {0x00007434, 18, 7}, /*CAL_OVERRIDE*/ + {0x00007068, 0, 9}, /*MODIX*/ + {0x0000410C, 7, 1}, /*VIMON_DLY_NOT_COMB*/ + {0x0000400C, 0, 7}, /*VIMON_DLY*/ + {0x00004000, 11, 1}, /*VMON_POL*/ + {0x00017040, 0, 8}, /*X_COORDINATE*/ + {0x00017040, 8, 8}, /*Y_COORDINATE*/ + {0x00017040, 16, 8}, /*WAFER_ID*/ + {0x00017040, 24, 8}, /*DVS*/ + {0x00017044, 0, 24}, /*LOT_NUMBER*/ +}; + +static const struct cs40l2x_trim cs40l2x_trim_table_e[] = { + /* addr shift size */ + {0x00002030, 0, 4}, /*TRIM_OSC_FREQ_TRIM*/ + {0x00002030, 7, 1}, /*TRIM_OSC_TRIM_DONE*/ + {0x0000208C, 24, 6}, /*TST_DIGREG_VREF_TRIM*/ + {0x00002090, 14, 4}, /*TST_REF_TRIM*/ + {0x00002090, 10, 4}, /*TST_REF_TEMPCO_TRIM*/ + {0x0000300C, 11, 4}, /*PLL_LDOA_TST_VREF_TRIM*/ + {0x0000394C, 23, 2}, /*BST_ATEST_CM_VOFF*/ + {0x00003950, 0, 7}, /*BST_ATRIM_IADC_OFFSET*/ + {0x00003950, 8, 7}, /*BST_ATRIM_IADC_GAIN1*/ + {0x00003950, 16, 8}, /*BST_ATRIM_IPKCOMP_OFFSET1*/ + {0x00003950, 24, 8}, /*BST_ATRIM_IPKCOMP_GAIN1*/ + {0x00003954, 0, 7}, /*BST_ATRIM_IADC_OFFSET2*/ + {0x00003954, 8, 7}, /*BST_ATRIM_IADC_GAIN2*/ + {0x00003954, 16, 8}, /*BST_ATRIM_IPKCOMP_OFFSET2*/ + {0x00003954, 24, 8}, /*BST_ATRIM_IPKCOMP_GAIN2*/ + {0x00003958, 0, 7}, /*BST_ATRIM_IADC_OFFSET3*/ + {0x00003958, 8, 7}, /*BST_ATRIM_IADC_GAIN3*/ + {0x00003958, 16, 8}, /*BST_ATRIM_IPKCOMP_OFFSET3*/ + {0x00003958, 24, 8}, /*BST_ATRIM_IPKCOMP_GAIN3*/ + {0x0000395C, 0, 7}, /*BST_ATRIM_IADC_OFFSET4*/ + {0x0000395C, 8, 7}, /*BST_ATRIM_IADC_GAIN4*/ + {0x0000395C, 16, 8}, /*BST_ATRIM_IPKCOMP_OFFSET4*/ + {0x0000395C, 24, 8}, /*BST_ATRIM_IPKCOMP_GAIN4*/ + {0x0000416C, 0, 8}, /*VMON_GAIN_OTP_VAL*/ + {0x00004160, 0, 7}, /*VMON_OFFSET_OTP_VAL*/ + {0x0000416C, 8, 8}, /*IMON_GAIN_OTP_VAL*/ + {0x00004160, 16, 10}, /*IMON_OFFSET_OTP_VAL*/ + {0x0000416C, 16, 12}, /*VMON_CM_GAIN_OTP_VAL*/ + {0x0000416C, 28, 1}, /*VMON_CM_GAIN_SIGN_OTP_VAL*/ + {0x00004170, 0, 6}, /*IMON_CAL_TEMPCO_OTP_VAL*/ + {0x00004170, 6, 1}, /*IMON_CAL_TEMPCO_SIGN_OTP*/ + {0x00004170, 8, 6}, /*IMON_CAL_TEMPCO2_OTP_VAL*/ + {0x00004170, 14, 1}, /*IMON_CAL_TEMPCO2_DN_UPB_OTP_VAL*/ + {0x00004170, 16, 9}, /*IMON_CAL_TEMPCO_TBASE_OTP_VAL*/ + {0x00004360, 0, 5}, /*TEMP_GAIN_OTP_VAL*/ + {0x00004360, 6, 9}, /*TEMP_OFFSET_OTP_VAL*/ + {0x00004448, 0, 8}, /*VP_SARADC_OFFSET*/ + {0x00004448, 8, 8}, /*VP_GAIN_INDEX*/ + {0x00004448, 16, 8}, /*VBST_SARADC_OFFSET*/ + {0x00004448, 24, 8}, /*VBST_GAIN_INDEX*/ + {0x0000444C, 0, 3}, /*ANA_SELINVREF*/ + {0x00006E30, 0, 5}, /*GAIN_ERR_COEFF_0*/ + {0x00006E30, 8, 5}, /*GAIN_ERR_COEFF_1*/ + {0x00006E30, 16, 5}, /*GAIN_ERR_COEFF_2*/ + {0x00006E30, 24, 5}, /*GAIN_ERR_COEFF_3*/ + {0x00006E34, 0, 5}, /*GAIN_ERR_COEFF_4*/ + {0x00006E34, 8, 5}, /*GAIN_ERR_COEFF_5*/ + {0x00006E34, 16, 5}, /*GAIN_ERR_COEFF_6*/ + {0x00006E34, 24, 5}, /*GAIN_ERR_COEFF_7*/ + {0x00006E38, 0, 5}, /*GAIN_ERR_COEFF_8*/ + {0x00006E38, 8, 5}, /*GAIN_ERR_COEFF_9*/ + {0x00006E38, 16, 5}, /*GAIN_ERR_COEFF_10*/ + {0x00006E38, 24, 5}, /*GAIN_ERR_COEFF_11*/ + {0x00006E3C, 0, 5}, /*GAIN_ERR_COEFF_12*/ + {0x00006E3C, 8, 5}, /*GAIN_ERR_COEFF_13*/ + {0x00006E3C, 16, 5}, /*GAIN_ERR_COEFF_14*/ + {0x00006E3C, 24, 5}, /*GAIN_ERR_COEFF_15*/ + {0x00006E40, 0, 5}, /*GAIN_ERR_COEFF_16*/ + {0x00006E40, 8, 5}, /*GAIN_ERR_COEFF_17*/ + {0x00006E40, 16, 5}, /*GAIN_ERR_COEFF_18*/ + {0x00006E40, 24, 5}, /*GAIN_ERR_COEFF_19*/ + {0x00006E44, 0, 5}, /*GAIN_ERR_COEFF_20*/ + {0x00006E48, 0, 10}, /*VOFF_GAIN_0*/ + {0x00006E48, 10, 10}, /*VOFF_GAIN_1*/ + {0x00006E48, 20, 10}, /*VOFF_GAIN_2*/ + {0x00006E4C, 0, 10}, /*VOFF_GAIN_3*/ + {0x00006E4C, 10, 10}, /*VOFF_GAIN_4*/ + {0x00006E4C, 20, 10}, /*VOFF_GAIN_5*/ + {0x00006E50, 0, 10}, /*VOFF_GAIN_6*/ + {0x00006E50, 10, 10}, /*VOFF_GAIN_7*/ + {0x00006E50, 20, 10}, /*VOFF_GAIN_8*/ + {0x00006E54, 0, 10}, /*VOFF_GAIN_9*/ + {0x00006E54, 10, 10}, /*VOFF_GAIN_10*/ + {0x00006E54, 20, 10}, /*VOFF_GAIN_11*/ + {0x00006E58, 0, 10}, /*VOFF_GAIN_12*/ + {0x00006E58, 10, 10}, /*VOFF_GAIN_13*/ + {0x00006E58, 20, 10}, /*VOFF_GAIN_14*/ + {0x00006E5C, 0, 10}, /*VOFF_GAIN_15*/ + {0x00006E5C, 10, 10}, /*VOFF_GAIN_16*/ + {0x00006E5C, 20, 10}, /*VOFF_GAIN_17*/ + {0x00006E60, 0, 10}, /*VOFF_GAIN_18*/ + {0x00006E60, 10, 10}, /*VOFF_GAIN_19*/ + {0x00006E60, 20, 10}, /*VOFF_GAIN_20*/ + {0x00006E64, 0, 10}, /*VOFF_INT1*/ + {0x00007418, 7, 5}, /*DS_SPK_INT1_CAP_TRIM*/ + {0x0000741C, 0, 5}, /*DS_SPK_INT2_CAP_TRIM*/ + {0x0000741C, 11, 4}, /*DS_SPK_LPF_CAP_TRIM*/ + {0x0000741C, 19, 4}, /*DS_SPK_QUAN_CAP_TRIM*/ + {0x00007434, 17, 1}, /*FORCE_CAL*/ + {0x00007434, 18, 7}, /*CAL_OVERRIDE*/ + {0x00007068, 0, 9}, /*MODIX*/ + {0x0000410C, 7, 1}, /*VIMON_DLY_NOT_COMB*/ + {0x0000400C, 0, 7}, /*VIMON_DLY*/ + {0x00004000, 11, 1}, /*VMON_POL*/ + {0x00017040, 0, 8}, /*X_COORDINATE*/ + {0x00017040, 8, 8}, /*Y_COORDINATE*/ + {0x00017040, 16, 8}, /*WAFER_ID*/ + {0x00017040, 24, 8}, /*DVS*/ + {0x00017044, 0, 24}, /*LOT_NUMBER*/ + {0x00003010, 2, 6}, /*PLL_DCO_CAL_TRIM*/ +}; + +const struct cs40l2x_otp_desc cs40l2x_otp_map[CS40L2X_NUM_OTP_MAPS] = { + { + .id = 0xC, + .row_start = 2, + .col_start = 16, + .num_trims = ARRAY_SIZE(cs40l2x_trim_table_c), + .trim_table = cs40l2x_trim_table_c, + }, + { + .id = 0xD, + .row_start = 2, + .col_start = 16, + .num_trims = ARRAY_SIZE(cs40l2x_trim_table_d), + .trim_table = cs40l2x_trim_table_d, + }, + { + .id = 0xE, + .row_start = 2, + .col_start = 16, + .num_trims = ARRAY_SIZE(cs40l2x_trim_table_e), + .trim_table = cs40l2x_trim_table_e, + }, +}; + +const unsigned int cs40l2x_pbq_dig_scale[CS40L2X_PBQ_SCALE_MAX + 1] = { + 0, + 320, /* 1% */ + 272, + 244, + 224, + 208, + 195, + 185, + 176, + 167, + 160, /* 10% */ + 153, + 147, + 142, + 137, + 132, + 127, + 123, + 119, + 115, + 112, /* 20% */ + 108, + 105, + 102, + 99, + 96, + 94, + 91, + 88, + 86, + 84, /* 30% */ + 81, + 79, + 77, + 75, + 73, + 71, + 69, + 67, + 65, + 64, /* 40% */ + 62, + 60, + 59, + 57, + 55, + 54, + 52, + 51, + 50, + 48, /* 50% */ + 47, + 45, + 44, + 43, + 42, + 40, + 39, + 38, + 37, + 35, /* 60% */ + 34, + 33, + 32, + 31, + 30, + 29, + 28, + 27, + 26, + 25, /* 70% */ + 24, + 23, + 22, + 21, + 20, + 19, + 18, + 17, + 16, + 16, /* 80% */ + 15, + 14, + 13, + 12, + 11, + 10, + 10, + 9, + 8, + 7, /* 90% */ + 7, + 6, + 5, + 4, + 4, + 3, + 2, + 1, + 1, + 0, /* 100% */ +}; diff --git a/drivers/misc/cs40l2x.c b/drivers/misc/cs40l2x.c new file mode 100644 index 000000000000..cb655b65106f --- /dev/null +++ b/drivers/misc/cs40l2x.c @@ -0,0 +1,3207 @@ +/* + * cs40l2x.c -- CS40L20/CS40L25/CS40L25A/CS40L25B Haptics Driver + * + * Copyright 2018 Cirrus Logic, Inc. + * + * Author: Jeff LaBundy + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs40l2x.h" + +#ifdef CONFIG_ANDROID_TIMED_OUTPUT +#include "../staging/android/timed_output.h" +#else +#include +#endif /* CONFIG_ANDROID_TIMED_OUTPUT */ + +struct cs40l2x_private { + struct device *dev; + struct regmap *regmap; + struct regulator_bulk_data supplies[2]; + unsigned int num_supplies; + unsigned int devid; + unsigned int revid; + struct work_struct vibe_start_work; + struct work_struct vibe_pbq_work; + struct work_struct vibe_stop_work; + struct workqueue_struct *vibe_workqueue; + struct mutex lock; + unsigned int cp_trigger_index; + unsigned int cp_trailer_index; + unsigned int num_waves; + unsigned int vibegen_id; + unsigned int vibegen_rev; + unsigned int wt_limit_xm; + unsigned int wt_limit_ym; + bool vibe_init_success; + struct gpio_desc *reset_gpio; + struct cs40l2x_platform_data pdata; + struct list_head coeff_desc_head; + unsigned char diag_state; + unsigned int diag_dig_scale; + unsigned int f0_measured; + unsigned int redc_measured; + struct cs40l2x_pbq_pair pbq_pairs[CS40L2X_PBQ_DEPTH_MAX]; + struct hrtimer pbq_timer; + unsigned int pbq_depth; + unsigned int pbq_index; + unsigned int pbq_state; + unsigned int pbq_cp_dig_scale; + int pbq_repeat; + int pbq_remain; +#ifdef CONFIG_ANDROID_TIMED_OUTPUT + struct timed_output_dev timed_dev; + struct hrtimer vibe_timer; + int vibe_timeout; +#else + struct led_classdev led_dev; +#endif /* CONFIG_ANDROID_TIMED_OUTPUT */ +}; + +static const char * const cs40l2x_supplies[] = { + "VA", + "VP", +}; + +static const char * const cs40l2x_part_nums[] = { + "CS40L20", + "CS40L25", + "CS40L25A", + "CS40L25B", +}; + +static struct cs40l2x_private *cs40l2x_get_private(struct device *dev) +{ +#ifdef CONFIG_ANDROID_TIMED_OUTPUT + /* timed output device does not register under a parent device */ + return container_of(dev_get_drvdata(dev), + struct cs40l2x_private, timed_dev); +#else + return dev_get_drvdata(dev); +#endif /* CONFIG_ANDROID_TIMED_OUTPUT */ +} + +static ssize_t cs40l2x_cp_trigger_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", cs40l2x->cp_trigger_index); +} + +static ssize_t cs40l2x_cp_trigger_index_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int index; + + ret = kstrtou32(buf, 10, &index); + if (ret) + return -EINVAL; + + if ((index & CS40L2X_INDEX_MASK) >= cs40l2x->num_waves + && index != CS40L2X_INDEX_PBQ + && index != CS40L2X_INDEX_DIAG) + return -EINVAL; + + cs40l2x->cp_trigger_index = index; + + return count; +} + +static ssize_t cs40l2x_cp_trigger_queue_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + ssize_t len = 0; + int i; + + if (cs40l2x->pbq_depth == 0) + return -ENODATA; + + mutex_lock(&cs40l2x->lock); + + for (i = 0; i < cs40l2x->pbq_depth; i++) { + switch (cs40l2x->pbq_pairs[i].tag) { + case CS40L2X_PBQ_TAG_SILENCE: + len += snprintf(buf + len, PAGE_SIZE - len, "%d", + cs40l2x->pbq_pairs[i].mag); + break; + case CS40L2X_PBQ_TAG_START: + len += snprintf(buf + len, PAGE_SIZE - len, "!!"); + break; + case CS40L2X_PBQ_TAG_STOP: + len += snprintf(buf + len, PAGE_SIZE - len, "%d!!", + cs40l2x->pbq_pairs[i].repeat); + break; + default: + len += snprintf(buf + len, PAGE_SIZE - len, "%d.%d", + cs40l2x->pbq_pairs[i].tag, + cs40l2x->pbq_pairs[i].mag); + } + + if (i < (cs40l2x->pbq_depth - 1)) + len += snprintf(buf + len, PAGE_SIZE - len, ", "); + } + + switch (cs40l2x->pbq_repeat) { + case -1: + len += snprintf(buf + len, PAGE_SIZE - len, ", ~\n"); + break; + case 0: + len += snprintf(buf + len, PAGE_SIZE - len, "\n"); + break; + default: + len += snprintf(buf + len, PAGE_SIZE - len, ", %d!\n", + cs40l2x->pbq_repeat); + } + + mutex_unlock(&cs40l2x->lock); + + return len; +} + +static ssize_t cs40l2x_cp_trigger_queue_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + char *pbq_str_alloc, *pbq_str, *pbq_str_tok; + char *pbq_seg_alloc, *pbq_seg, *pbq_seg_tok; + size_t pbq_seg_len; + unsigned int pbq_depth = 0; + unsigned int val, num_empty; + int pbq_marker = -1; + int ret; + + pbq_str_alloc = kzalloc(count, GFP_KERNEL); + if (!pbq_str_alloc) + return -ENOMEM; + + pbq_seg_alloc = kzalloc(CS40L2X_PBQ_SEG_LEN_MAX + 1, GFP_KERNEL); + if (!pbq_seg_alloc) { + kfree(pbq_str_alloc); + return -ENOMEM; + } + + mutex_lock(&cs40l2x->lock); + + cs40l2x->pbq_depth = 0; + cs40l2x->pbq_repeat = 0; + + pbq_str = pbq_str_alloc; + strlcpy(pbq_str, buf, count); + + pbq_str_tok = strsep(&pbq_str, ","); + + while (pbq_str_tok) { + pbq_seg = pbq_seg_alloc; + pbq_seg_len = strlcpy(pbq_seg, strim(pbq_str_tok), + CS40L2X_PBQ_SEG_LEN_MAX + 1); + if (pbq_seg_len > CS40L2X_PBQ_SEG_LEN_MAX) { + ret = -E2BIG; + goto err_mutex; + } + + /* waveform specifier */ + if (strnchr(pbq_seg, CS40L2X_PBQ_SEG_LEN_MAX, '.')) { + /* index */ + pbq_seg_tok = strsep(&pbq_seg, "."); + + ret = kstrtou32(pbq_seg_tok, 10, &val); + if (ret) { + ret = -EINVAL; + goto err_mutex; + } + if (val == 0 || val >= cs40l2x->num_waves) { + ret = -EINVAL; + goto err_mutex; + } + cs40l2x->pbq_pairs[pbq_depth].tag = val; + + /* scale */ + pbq_seg_tok = strsep(&pbq_seg, "."); + + ret = kstrtou32(pbq_seg_tok, 10, &val); + if (ret) { + ret = -EINVAL; + goto err_mutex; + } + if (val == 0 || val > CS40L2X_PBQ_SCALE_MAX) { + ret = -EINVAL; + goto err_mutex; + } + cs40l2x->pbq_pairs[pbq_depth++].mag = val; + + /* repetition specifier */ + } else if (strnchr(pbq_seg, CS40L2X_PBQ_SEG_LEN_MAX, '!')) { + val = 0; + num_empty = 0; + + pbq_seg_tok = strsep(&pbq_seg, "!"); + + while (pbq_seg_tok) { + if (strnlen(pbq_seg_tok, + CS40L2X_PBQ_SEG_LEN_MAX)) { + ret = kstrtou32(pbq_seg_tok, 10, &val); + if (ret) { + ret = -EINVAL; + goto err_mutex; + } + if (val > CS40L2X_PBQ_REPEAT_MAX) { + ret = -EINVAL; + goto err_mutex; + } + } else { + num_empty++; + } + + pbq_seg_tok = strsep(&pbq_seg, "!"); + } + + /* number of empty tokens reveals specifier type */ + switch (num_empty) { + case 1: /* outer loop: "n!" or "!n" */ + if (cs40l2x->pbq_repeat) { + ret = -EINVAL; + goto err_mutex; + } + cs40l2x->pbq_repeat = val; + break; + + case 2: /* inner loop stop: "n!!" or "!!n" */ + if (pbq_marker < 0) { + ret = -EINVAL; + goto err_mutex; + } + + cs40l2x->pbq_pairs[pbq_depth].tag = + CS40L2X_PBQ_TAG_STOP; + cs40l2x->pbq_pairs[pbq_depth].mag = pbq_marker; + cs40l2x->pbq_pairs[pbq_depth++].repeat = val; + pbq_marker = -1; + break; + + case 3: /* inner loop start: "!!" */ + if (pbq_marker >= 0) { + ret = -EINVAL; + goto err_mutex; + } + + cs40l2x->pbq_pairs[pbq_depth].tag = + CS40L2X_PBQ_TAG_START; + pbq_marker = pbq_depth++; + break; + + default: + ret = -EINVAL; + goto err_mutex; + } + + /* loop specifier */ + } else if (strnchr(pbq_seg, CS40L2X_PBQ_SEG_LEN_MAX, '~')) { + if (cs40l2x->pbq_repeat) { + ret = -EINVAL; + goto err_mutex; + } + cs40l2x->pbq_repeat = -1; + + /* duration specifier */ + } else { + cs40l2x->pbq_pairs[pbq_depth].tag = + CS40L2X_PBQ_TAG_SILENCE; + + ret = kstrtou32(pbq_seg, 10, &val); + if (ret) { + ret = -EINVAL; + goto err_mutex; + } + if (val > CS40L2X_PBQ_DELAY_MAX) { + ret = -EINVAL; + goto err_mutex; + } + cs40l2x->pbq_pairs[pbq_depth++].mag = val; + } + + if (pbq_depth == CS40L2X_PBQ_DEPTH_MAX) { + ret = -E2BIG; + goto err_mutex; + } + + pbq_str_tok = strsep(&pbq_str, ","); + } + + cs40l2x->pbq_depth = pbq_depth; + ret = count; + +err_mutex: + mutex_unlock(&cs40l2x->lock); + + kfree(pbq_str_alloc); + kfree(pbq_seg_alloc); + + return ret; +} + +static unsigned int cs40l2x_dsp_reg(struct cs40l2x_private *cs40l2x, + const char *coeff_name, const unsigned char block_type) +{ + struct cs40l2x_coeff_desc *coeff_desc; + + list_for_each_entry(coeff_desc, &cs40l2x->coeff_desc_head, list) { + if (strcmp(coeff_desc->name, coeff_name)) + continue; + if (coeff_desc->block_type != block_type) + continue; + + return coeff_desc->reg; + } + + /* return an identifiable register that is known to be read-only */ + return CS40L2X_DEVID; +} + +static ssize_t cs40l2x_gpio1_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int val; + + mutex_lock(&cs40l2x->lock); + ret = regmap_read(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "GPIO_ENABLE", + CS40L2X_XM_UNPACKED_TYPE), &val); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to read GPIO1 configuration\n"); + return ret; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t cs40l2x_gpio1_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int val; + + ret = kstrtou32(buf, 10, &val); + if (ret) + return -EINVAL; + + mutex_lock(&cs40l2x->lock); + ret = regmap_write(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "GPIO_ENABLE", + CS40L2X_XM_UNPACKED_TYPE), + val ? CS40L2X_GPIO1_ENABLED : CS40L2X_GPIO1_DISABLED); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to write GPIO1 configuration\n"); + return ret; + } + + return count; +} + +static ssize_t cs40l2x_gpio1_rise_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int val; + + mutex_lock(&cs40l2x->lock); + ret = regmap_read(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "INDEXBUTTONPRESS", + CS40L2X_XM_UNPACKED_TYPE), &val); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to read index\n"); + return ret; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t cs40l2x_gpio1_rise_index_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int index; + + ret = kstrtou32(buf, 10, &index); + if (ret) + return -EINVAL; + + if (index > (cs40l2x->num_waves - 1)) + return -EINVAL; + + mutex_lock(&cs40l2x->lock); + ret = regmap_write(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "INDEXBUTTONPRESS", + CS40L2X_XM_UNPACKED_TYPE), index); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to write index\n"); + return ret; + } + + return count; +} + +static ssize_t cs40l2x_gpio1_fall_index_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int val; + + mutex_lock(&cs40l2x->lock); + ret = regmap_read(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "INDEXBUTTONRELEASE", + CS40L2X_XM_UNPACKED_TYPE), &val); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to read index\n"); + return ret; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t cs40l2x_gpio1_fall_index_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int index; + + ret = kstrtou32(buf, 10, &index); + if (ret) + return -EINVAL; + + if (index > (cs40l2x->num_waves - 1)) + return -EINVAL; + + mutex_lock(&cs40l2x->lock); + ret = regmap_write(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "INDEXBUTTONRELEASE", + CS40L2X_XM_UNPACKED_TYPE), index); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to write index\n"); + return ret; + } + + return count; +} + +static ssize_t cs40l2x_gpio1_fall_timeout_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int val; + + mutex_lock(&cs40l2x->lock); + ret = regmap_read(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "PRESS_RELEASE_TIMEOUT", + CS40L2X_XM_UNPACKED_TYPE), &val); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to read GPIO1 falling-edge timeout\n"); + return ret; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t cs40l2x_gpio1_fall_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int val; + + ret = kstrtou32(buf, 10, &val); + if (ret) + return -EINVAL; + + if (val > CS40L2X_PR_TIMEOUT_MAX) + return -EINVAL; + + mutex_lock(&cs40l2x->lock); + ret = regmap_write(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "PRESS_RELEASE_TIMEOUT", + CS40L2X_XM_UNPACKED_TYPE), val); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to write GPIO1 falling-edge timeout\n"); + return ret; + } + + return count; +} + +static ssize_t cs40l2x_standby_timeout_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int val; + + mutex_lock(&cs40l2x->lock); + ret = regmap_read(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "EVENT_TIMEOUT", + CS40L2X_XM_UNPACKED_TYPE), &val); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to read standby timeout\n"); + return ret; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t cs40l2x_standby_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int val; + + ret = kstrtou32(buf, 10, &val); + if (ret) + return -EINVAL; + + if (val > CS40L2X_EVENT_TIMEOUT_MAX) + return -EINVAL; + + mutex_lock(&cs40l2x->lock); + ret = regmap_write(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "EVENT_TIMEOUT", + CS40L2X_XM_UNPACKED_TYPE), val); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to write standby timeout\n"); + return ret; + } + + return count; +} + +static ssize_t cs40l2x_f0_measured_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + unsigned char diag_state; + unsigned int f0_measured; + + mutex_lock(&cs40l2x->lock); + + diag_state = cs40l2x->diag_state; + f0_measured = cs40l2x->f0_measured; + + mutex_unlock(&cs40l2x->lock); + + if (diag_state != CS40L2X_DIAG_STATE_DONE) + return -ENODATA; + + return snprintf(buf, PAGE_SIZE, "%d\n", f0_measured); +} + +static ssize_t cs40l2x_f0_stored_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int val; + + mutex_lock(&cs40l2x->lock); + ret = regmap_read(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "F0_STORED", + CS40L2X_XM_UNPACKED_TYPE), &val); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to read stored f0\n"); + return ret; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t cs40l2x_f0_stored_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int val; + + ret = kstrtou32(buf, 10, &val); + if (ret) + return -EINVAL; + + if (cs40l2x->pdata.f0_min > 0 && val < cs40l2x->pdata.f0_min) + return -EINVAL; + + if (cs40l2x->pdata.f0_max > 0 && val > cs40l2x->pdata.f0_max) + return -EINVAL; + + mutex_lock(&cs40l2x->lock); + ret = regmap_write(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "F0_STORED", + CS40L2X_XM_UNPACKED_TYPE), val); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to store f0\n"); + return ret; + } + + return count; +} + +static ssize_t cs40l2x_redc_measured_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + unsigned char diag_state; + unsigned int redc_measured; + + mutex_lock(&cs40l2x->lock); + + diag_state = cs40l2x->diag_state; + redc_measured = cs40l2x->redc_measured; + + mutex_unlock(&cs40l2x->lock); + + if (diag_state != CS40L2X_DIAG_STATE_DONE) + return -ENODATA; + + return snprintf(buf, PAGE_SIZE, "%d\n", redc_measured); +} + +static ssize_t cs40l2x_redc_stored_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int val; + + mutex_lock(&cs40l2x->lock); + ret = regmap_read(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "REDC_STORED", + CS40L2X_XM_UNPACKED_TYPE), &val); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to read stored ReDC\n"); + return ret; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t cs40l2x_redc_stored_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int val; + + ret = kstrtou32(buf, 10, &val); + if (ret) + return -EINVAL; + + if (cs40l2x->pdata.redc_min > 0 && val < cs40l2x->pdata.redc_min) + return -EINVAL; + + if (cs40l2x->pdata.redc_max > 0 && val > cs40l2x->pdata.redc_max) + return -EINVAL; + + mutex_lock(&cs40l2x->lock); + ret = regmap_write(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "REDC_STORED", + CS40L2X_XM_UNPACKED_TYPE), val); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to store ReDC\n"); + return ret; + } + + return count; +} + +static ssize_t cs40l2x_comp_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int val; + + mutex_lock(&cs40l2x->lock); + ret = regmap_read(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "COMPENSATION_ENABLE", + CS40L2X_XM_UNPACKED_TYPE), &val); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to read compensation state\n"); + return ret; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t cs40l2x_comp_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int val; + + ret = kstrtou32(buf, 10, &val); + if (ret) + return -EINVAL; + + mutex_lock(&cs40l2x->lock); + ret = regmap_write(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "COMPENSATION_ENABLE", + CS40L2X_XM_UNPACKED_TYPE), + val ? CS40L2X_COMP_ENABLED : CS40L2X_COMP_DISABLED); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to write compensation state\n"); + return ret; + } + + return count; +} + +static int cs40l2x_dig_scale_get(struct cs40l2x_private *cs40l2x, + unsigned int *dig_scale) +{ + int ret; + unsigned int val; + + if (!mutex_is_locked(&cs40l2x->lock)) + return -EACCES; + + ret = regmap_read(cs40l2x->regmap, CS40L2X_AMP_DIG_VOL_CTRL, &val); + if (ret) + return ret; + + *dig_scale = (CS40L2X_DIG_SCALE_ZERO - ((val & CS40L2X_AMP_VOL_PCM_MASK) + >> CS40L2X_AMP_VOL_PCM_SHIFT)) & CS40L2X_DIG_SCALE_MASK; + + return 0; +} + +static int cs40l2x_dig_scale_set(struct cs40l2x_private *cs40l2x, + unsigned int dig_scale) +{ + if (!mutex_is_locked(&cs40l2x->lock)) + return -EACCES; + + if (dig_scale == CS40L2X_DIG_SCALE_RESET) + return -EINVAL; + + return regmap_update_bits(cs40l2x->regmap, + CS40L2X_AMP_DIG_VOL_CTRL, + CS40L2X_AMP_VOL_PCM_MASK, + ((CS40L2X_DIG_SCALE_ZERO - dig_scale) + & CS40L2X_DIG_SCALE_MASK) + << CS40L2X_AMP_VOL_PCM_SHIFT); +} + +static ssize_t cs40l2x_dig_scale_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int dig_scale; + + mutex_lock(&cs40l2x->lock); + ret = cs40l2x_dig_scale_get(cs40l2x, &dig_scale); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to read digital scale\n"); + return ret; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", dig_scale); +} + +static ssize_t cs40l2x_dig_scale_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int dig_scale; + + ret = kstrtou32(buf, 10, &dig_scale); + if (ret) + return -EINVAL; + + if (dig_scale > CS40L2X_DIG_SCALE_MAX) + return -EINVAL; + + mutex_lock(&cs40l2x->lock); + ret = cs40l2x_dig_scale_set(cs40l2x, dig_scale); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to write digital scale\n"); + return ret; + } + + return count; +} + +static int cs40l2x_gpio1_dig_scale_get(struct cs40l2x_private *cs40l2x, + unsigned int *dig_scale) +{ + int ret; + unsigned int val; + + if (!mutex_is_locked(&cs40l2x->lock)) + return -EACCES; + + ret = regmap_read(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "GAIN_CONTROL", + CS40L2X_XM_UNPACKED_TYPE), &val); + if (ret) + return ret; + + *dig_scale = (val & CS40L2X_GAIN_CTRL_GPIO_MASK) + >> CS40L2X_GAIN_CTRL_GPIO_SHIFT; + + return 0; +} + +static int cs40l2x_gpio1_dig_scale_set(struct cs40l2x_private *cs40l2x, + unsigned int dig_scale) +{ + if (!mutex_is_locked(&cs40l2x->lock)) + return -EACCES; + + if (dig_scale == CS40L2X_DIG_SCALE_RESET) + return -EINVAL; + + return regmap_update_bits(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "GAIN_CONTROL", + CS40L2X_XM_UNPACKED_TYPE), + CS40L2X_GAIN_CTRL_GPIO_MASK, + dig_scale << CS40L2X_GAIN_CTRL_GPIO_SHIFT); +} + +static ssize_t cs40l2x_gpio1_dig_scale_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int dig_scale; + + mutex_lock(&cs40l2x->lock); + ret = cs40l2x_gpio1_dig_scale_get(cs40l2x, &dig_scale); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to read digital scale\n"); + return ret; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", dig_scale); +} + +static ssize_t cs40l2x_gpio1_dig_scale_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int dig_scale; + + ret = kstrtou32(buf, 10, &dig_scale); + if (ret) + return -EINVAL; + + if (dig_scale > CS40L2X_DIG_SCALE_MAX) + return -EINVAL; + + mutex_lock(&cs40l2x->lock); + ret = cs40l2x_gpio1_dig_scale_set(cs40l2x, dig_scale); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to write digital scale\n"); + return ret; + } + + return count; +} + +static int cs40l2x_cp_dig_scale_get(struct cs40l2x_private *cs40l2x, + unsigned int *dig_scale) +{ + int ret; + unsigned int val; + + if (!mutex_is_locked(&cs40l2x->lock)) + return -EACCES; + + ret = regmap_read(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "GAIN_CONTROL", + CS40L2X_XM_UNPACKED_TYPE), &val); + if (ret) + return ret; + + *dig_scale = (val & CS40L2X_GAIN_CTRL_TRIG_MASK) + >> CS40L2X_GAIN_CTRL_TRIG_SHIFT; + + return 0; +} + +static int cs40l2x_cp_dig_scale_set(struct cs40l2x_private *cs40l2x, + unsigned int dig_scale) +{ + if (!mutex_is_locked(&cs40l2x->lock)) + return -EACCES; + + if (dig_scale == CS40L2X_DIG_SCALE_RESET) + return -EINVAL; + + return regmap_update_bits(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "GAIN_CONTROL", + CS40L2X_XM_UNPACKED_TYPE), + CS40L2X_GAIN_CTRL_TRIG_MASK, + dig_scale << CS40L2X_GAIN_CTRL_TRIG_SHIFT); +} + +static ssize_t cs40l2x_cp_dig_scale_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int dig_scale; + + mutex_lock(&cs40l2x->lock); + ret = cs40l2x_cp_dig_scale_get(cs40l2x, &dig_scale); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to read digital scale\n"); + return ret; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", dig_scale); +} + +static ssize_t cs40l2x_cp_dig_scale_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int dig_scale; + + ret = kstrtou32(buf, 10, &dig_scale); + if (ret) + return -EINVAL; + + if (dig_scale > CS40L2X_DIG_SCALE_MAX) + return -EINVAL; + + mutex_lock(&cs40l2x->lock); + ret = cs40l2x_cp_dig_scale_set(cs40l2x, dig_scale); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to write digital scale\n"); + return ret; + } + + return count; +} + +static ssize_t cs40l2x_heartbeat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + int ret; + unsigned int val; + + mutex_lock(&cs40l2x->lock); + ret = regmap_read(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "HALO_HEARTBEAT", + CS40L2X_XM_UNPACKED_TYPE), &val); + mutex_unlock(&cs40l2x->lock); + + if (ret) { + pr_err("Failed to read heartbeat\n"); + return ret; + } + + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t cs40l2x_num_waves_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct cs40l2x_private *cs40l2x = cs40l2x_get_private(dev); + + return snprintf(buf, PAGE_SIZE, "%d\n", cs40l2x->num_waves); +} + +static DEVICE_ATTR(cp_trigger_index, 0660, cs40l2x_cp_trigger_index_show, + cs40l2x_cp_trigger_index_store); +static DEVICE_ATTR(cp_trigger_queue, 0660, cs40l2x_cp_trigger_queue_show, + cs40l2x_cp_trigger_queue_store); +static DEVICE_ATTR(gpio1_enable, 0660, cs40l2x_gpio1_enable_show, + cs40l2x_gpio1_enable_store); +static DEVICE_ATTR(gpio1_rise_index, 0660, cs40l2x_gpio1_rise_index_show, + cs40l2x_gpio1_rise_index_store); +static DEVICE_ATTR(gpio1_fall_index, 0660, cs40l2x_gpio1_fall_index_show, + cs40l2x_gpio1_fall_index_store); +static DEVICE_ATTR(gpio1_fall_timeout, 0660, cs40l2x_gpio1_fall_timeout_show, + cs40l2x_gpio1_fall_timeout_store); +static DEVICE_ATTR(standby_timeout, 0660, cs40l2x_standby_timeout_show, + cs40l2x_standby_timeout_store); +static DEVICE_ATTR(f0_measured, 0660, cs40l2x_f0_measured_show, NULL); +static DEVICE_ATTR(f0_stored, 0660, cs40l2x_f0_stored_show, + cs40l2x_f0_stored_store); +static DEVICE_ATTR(redc_measured, 0660, cs40l2x_redc_measured_show, NULL); +static DEVICE_ATTR(redc_stored, 0660, cs40l2x_redc_stored_show, + cs40l2x_redc_stored_store); +static DEVICE_ATTR(comp_enable, 0660, cs40l2x_comp_enable_show, + cs40l2x_comp_enable_store); +static DEVICE_ATTR(dig_scale, 0660, cs40l2x_dig_scale_show, + cs40l2x_dig_scale_store); +static DEVICE_ATTR(gpio1_dig_scale, 0660, cs40l2x_gpio1_dig_scale_show, + cs40l2x_gpio1_dig_scale_store); +static DEVICE_ATTR(cp_dig_scale, 0660, cs40l2x_cp_dig_scale_show, + cs40l2x_cp_dig_scale_store); +static DEVICE_ATTR(heartbeat, 0660, cs40l2x_heartbeat_show, NULL); +static DEVICE_ATTR(num_waves, 0660, cs40l2x_num_waves_show, NULL); + +static struct attribute *cs40l2x_dev_attrs[] = { + &dev_attr_cp_trigger_index.attr, + &dev_attr_cp_trigger_queue.attr, + &dev_attr_gpio1_enable.attr, + &dev_attr_gpio1_rise_index.attr, + &dev_attr_gpio1_fall_index.attr, + &dev_attr_gpio1_fall_timeout.attr, + &dev_attr_standby_timeout.attr, + &dev_attr_f0_measured.attr, + &dev_attr_f0_stored.attr, + &dev_attr_redc_measured.attr, + &dev_attr_redc_stored.attr, + &dev_attr_comp_enable.attr, + &dev_attr_dig_scale.attr, + &dev_attr_gpio1_dig_scale.attr, + &dev_attr_cp_dig_scale.attr, + &dev_attr_heartbeat.attr, + &dev_attr_num_waves.attr, + NULL, +}; + +static struct attribute_group cs40l2x_dev_attr_group = { + .attrs = cs40l2x_dev_attrs, +}; + +static int cs40l2x_stop_playback(struct cs40l2x_private *cs40l2x) +{ + int ret, i; + + if (!mutex_is_locked(&cs40l2x->lock)) + return -EACCES; + + for (i = 0; i < CS40L2X_ENDPLAYBACK_RETRIES; i++) { + ret = regmap_write(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "ENDPLAYBACK", + CS40L2X_XM_UNPACKED_TYPE), + CS40L2X_ENDPLAYBACK_REQ); + if (!ret) + return 0; + + usleep_range(10000, 10100); + } + + dev_err(cs40l2x->dev, "Parking device in reset\n"); + gpiod_set_value_cansleep(cs40l2x->reset_gpio, 0); + + return -EIO; +} + +static int cs40l2x_pbq_cancel(struct cs40l2x_private *cs40l2x) +{ + int ret; + + /* this function expects to be called from a locked worker function */ + if (!mutex_is_locked(&cs40l2x->lock)) + return -EACCES; + + hrtimer_cancel(&cs40l2x->pbq_timer); + + ret = cs40l2x_stop_playback(cs40l2x); + if (ret) + return ret; + + ret = cs40l2x_cp_dig_scale_set(cs40l2x, cs40l2x->pbq_cp_dig_scale); + if (ret) + return ret; + + cs40l2x->pbq_state = CS40L2X_PBQ_STATE_IDLE; + + return 0; +} + +static int cs40l2x_pbq_pair_launch(struct cs40l2x_private *cs40l2x) +{ + unsigned int tag, mag, cp_dig_scale; + int ret, i; + + /* this function expects to be called from a locked worker function */ + if (!mutex_is_locked(&cs40l2x->lock)) + return -EACCES; + + do { + /* restart queue as necessary */ + if (cs40l2x->pbq_index == cs40l2x->pbq_depth) { + cs40l2x->pbq_index = 0; + for (i = 0; i < cs40l2x->pbq_depth; i++) + cs40l2x->pbq_pairs[i].remain = + cs40l2x->pbq_pairs[i].repeat; + + switch (cs40l2x->pbq_remain) { + case -1: + /* loop until stopped */ + break; + case 0: + /* queue is finished */ + cs40l2x->cp_trailer_index = 0; + cs40l2x->pbq_state = CS40L2X_PBQ_STATE_IDLE; + + ret = cs40l2x_cp_dig_scale_set(cs40l2x, + cs40l2x->pbq_cp_dig_scale); + return ret; + default: + /* loop once more */ + cs40l2x->pbq_remain--; + } + } + + tag = cs40l2x->pbq_pairs[cs40l2x->pbq_index].tag; + mag = cs40l2x->pbq_pairs[cs40l2x->pbq_index].mag; + + switch (tag) { + case CS40L2X_PBQ_TAG_SILENCE: + hrtimer_start(&cs40l2x->pbq_timer, + ktime_set(mag / 1000, + (mag % 1000) * 1000000), + HRTIMER_MODE_REL); + cs40l2x->pbq_state = CS40L2X_PBQ_STATE_SILENT; + cs40l2x->pbq_index++; + break; + case CS40L2X_PBQ_TAG_START: + cs40l2x->pbq_index++; + break; + case CS40L2X_PBQ_TAG_STOP: + if (cs40l2x->pbq_pairs[cs40l2x->pbq_index].remain) { + cs40l2x->pbq_pairs[cs40l2x->pbq_index].remain--; + cs40l2x->pbq_index = mag; + } else { + cs40l2x->pbq_index++; + } + break; + default: + cp_dig_scale = cs40l2x->pbq_cp_dig_scale + + cs40l2x_pbq_dig_scale[mag]; + if (cp_dig_scale > CS40L2X_DIG_SCALE_MAX) + cp_dig_scale = CS40L2X_DIG_SCALE_MAX; + + ret = cs40l2x_cp_dig_scale_set(cs40l2x, cp_dig_scale); + if (ret) + return ret; + + ret = regmap_write(cs40l2x->regmap, + CS40L2X_MBOX_TRIGGERINDEX, tag); + if (ret) + return ret; + + hrtimer_start(&cs40l2x->pbq_timer, + ktime_set(0, CS40L2X_PBQ_POLL_NS), + HRTIMER_MODE_REL); + + cs40l2x->pbq_state = CS40L2X_PBQ_STATE_PLAYING; + cs40l2x->pbq_index++; + } + + } while (tag == CS40L2X_PBQ_TAG_START || tag == CS40L2X_PBQ_TAG_STOP); + + return 0; +} + +static void cs40l2x_vibe_pbq_worker(struct work_struct *work) +{ + struct cs40l2x_private *cs40l2x = + container_of(work, struct cs40l2x_private, vibe_pbq_work); + struct regmap *regmap = cs40l2x->regmap; + struct device *dev = cs40l2x->dev; + unsigned int val; + int ret; + + mutex_lock(&cs40l2x->lock); + + switch (cs40l2x->pbq_state) { + case CS40L2X_PBQ_STATE_IDLE: + /* queue may have been canceled */ + break; + + case CS40L2X_PBQ_STATE_PLAYING: + ret = regmap_read(regmap, cs40l2x_dsp_reg(cs40l2x, "STATUS", + CS40L2X_XM_UNPACKED_TYPE), &val); + if (ret) { + dev_err(dev, "Failed to capture playback status\n"); + goto err_mutex; + } + + if (val != CS40L2X_STATUS_IDLE) { + hrtimer_start(&cs40l2x->pbq_timer, + ktime_set(0, CS40L2X_PBQ_POLL_NS), + HRTIMER_MODE_REL); + goto err_mutex; + } + + ret = cs40l2x_pbq_pair_launch(cs40l2x); + if (ret) + dev_err(dev, "Failed to continue playback queue\n"); + break; + + case CS40L2X_PBQ_STATE_SILENT: + ret = cs40l2x_pbq_pair_launch(cs40l2x); + if (ret) + dev_err(dev, "Failed to continue playback queue\n"); + break; + + default: + dev_err(dev, "Unexpected playback queue state: %d\n", + cs40l2x->pbq_state); + } + +err_mutex: + mutex_unlock(&cs40l2x->lock); +} + +static enum hrtimer_restart cs40l2x_pbq_timer(struct hrtimer *timer) +{ + struct cs40l2x_private *cs40l2x = + container_of(timer, struct cs40l2x_private, pbq_timer); + + queue_work(cs40l2x->vibe_workqueue, &cs40l2x->vibe_pbq_work); + + return HRTIMER_NORESTART; +} + +static int cs40l2x_diag_capture(struct cs40l2x_private *cs40l2x) +{ + struct regmap *regmap = cs40l2x->regmap; + int ret; + + /* this function expects to be called from a locked worker function */ + if (!mutex_is_locked(&cs40l2x->lock)) + return -EACCES; + + if (cs40l2x->diag_state != CS40L2X_DIAG_STATE_RUN) + return -ENODATA; + + ret = regmap_read(regmap, cs40l2x_dsp_reg(cs40l2x, "F0", + CS40L2X_XM_UNPACKED_TYPE), &cs40l2x->f0_measured); + if (ret) + return ret; + + ret = regmap_read(regmap, cs40l2x_dsp_reg(cs40l2x, "REDC", + CS40L2X_XM_UNPACKED_TYPE), &cs40l2x->redc_measured); + if (ret) + return ret; + + cs40l2x->diag_state = CS40L2X_DIAG_STATE_DONE; + + return 0; +} + +static void cs40l2x_vibe_start_worker(struct work_struct *work) +{ + struct cs40l2x_private *cs40l2x = + container_of(work, struct cs40l2x_private, vibe_start_work); + struct regmap *regmap = cs40l2x->regmap; + struct device *dev = cs40l2x->dev; + int ret, i; + + mutex_lock(&cs40l2x->lock); + + /* handle interruption of special cases */ + switch (cs40l2x->cp_trailer_index) { + case CS40L2X_INDEX_PBQ: + ret = cs40l2x_pbq_cancel(cs40l2x); + if (ret) + dev_err(dev, "Failed to cancel playback queue\n"); + break; + + case CS40L2X_INDEX_DIAG: + cs40l2x->diag_state = CS40L2X_DIAG_STATE_INIT; + goto err_mutex; + } + + cs40l2x->cp_trailer_index = cs40l2x->cp_trigger_index; + + switch (cs40l2x->cp_trailer_index) { + case CS40L2X_INDEX_VIBE: + case CS40L2X_INDEX_CONT_MIN ... CS40L2X_INDEX_CONT_MAX: + case CS40L2X_INDEX_DIAG: + pm_stay_awake(dev); +#ifdef CONFIG_ANDROID_TIMED_OUTPUT + hrtimer_start(&cs40l2x->vibe_timer, + ktime_set(cs40l2x->vibe_timeout / 1000, + (cs40l2x->vibe_timeout % 1000) + * 1000000), + HRTIMER_MODE_REL); +#endif /* CONFIG_ANDROID_TIMED_OUTPUT */ + } + + switch (cs40l2x->cp_trailer_index) { + case CS40L2X_INDEX_VIBE: + case CS40L2X_INDEX_CONT_MIN ... CS40L2X_INDEX_CONT_MAX: + ret = regmap_write(regmap, CS40L2X_MBOX_TRIGGER_MS, + cs40l2x->cp_trailer_index & CS40L2X_INDEX_MASK); + if (ret) + dev_err(dev, "Failed to start playback\n"); + break; + + case CS40L2X_INDEX_CLICK_MIN ... CS40L2X_INDEX_CLICK_MAX: + ret = regmap_write(regmap, CS40L2X_MBOX_TRIGGERINDEX, + cs40l2x->cp_trailer_index); + if (ret) + dev_err(dev, "Failed to start playback\n"); + break; + + case CS40L2X_INDEX_PBQ: + cs40l2x->pbq_cp_dig_scale = CS40L2X_DIG_SCALE_RESET; + + ret = cs40l2x_cp_dig_scale_get(cs40l2x, + &cs40l2x->pbq_cp_dig_scale); + if (ret) { + dev_err(dev, "Failed to read digital scale\n"); + goto err_mutex; + } + + cs40l2x->pbq_index = 0; + cs40l2x->pbq_remain = cs40l2x->pbq_repeat; + + for (i = 0; i < cs40l2x->pbq_depth; i++) + cs40l2x->pbq_pairs[i].remain = + cs40l2x->pbq_pairs[i].repeat; + + ret = cs40l2x_pbq_pair_launch(cs40l2x); + if (ret) + dev_err(dev, "Failed to launch playback queue\n"); + break; + + case CS40L2X_INDEX_DIAG: + cs40l2x->diag_state = CS40L2X_DIAG_STATE_INIT; + cs40l2x->diag_dig_scale = CS40L2X_DIG_SCALE_RESET; + + ret = cs40l2x_dig_scale_get(cs40l2x, &cs40l2x->diag_dig_scale); + if (ret) { + dev_err(dev, "Failed to read digital scale\n"); + goto err_mutex; + } + + ret = cs40l2x_dig_scale_set(cs40l2x, 0); + if (ret) { + dev_err(dev, "Failed to reset digital scale\n"); + goto err_mutex; + } + + ret = regmap_write(regmap, + cs40l2x_dsp_reg(cs40l2x, "CLOSED_LOOP", + CS40L2X_XM_UNPACKED_TYPE), 0); + if (ret) { + dev_err(dev, "Failed to disable closed-loop mode\n"); + goto err_mutex; + } + + ret = regmap_write(regmap, CS40L2X_MBOX_STIMULUS_MODE, 1); + if (ret) { + dev_err(dev, "Failed to enable stimulus mode\n"); + goto err_mutex; + } + + msleep(CS40L2X_DIAG_STATE_DELAY_MS); + + ret = regmap_write(regmap, + cs40l2x_dsp_reg(cs40l2x, "CLOSED_LOOP", + CS40L2X_XM_UNPACKED_TYPE), 1); + if (ret) { + dev_err(dev, "Failed to enable closed-loop mode\n"); + goto err_mutex; + } + cs40l2x->diag_state = CS40L2X_DIAG_STATE_RUN; + + break; + + default: + dev_err(dev, "Invalid wavetable index\n"); + } + +err_mutex: + mutex_unlock(&cs40l2x->lock); +} + +static void cs40l2x_vibe_stop_worker(struct work_struct *work) +{ + struct cs40l2x_private *cs40l2x = + container_of(work, struct cs40l2x_private, vibe_stop_work); + struct regmap *regmap = cs40l2x->regmap; + struct device *dev = cs40l2x->dev; + int ret; + + mutex_lock(&cs40l2x->lock); + + switch (cs40l2x->cp_trailer_index) { + case CS40L2X_INDEX_VIBE: + case CS40L2X_INDEX_CONT_MIN ... CS40L2X_INDEX_CONT_MAX: + ret = cs40l2x_stop_playback(cs40l2x); + if (ret) + dev_err(dev, "Failed to stop playback\n"); + pm_relax(dev); + break; + + case CS40L2X_INDEX_CLICK_MIN ... CS40L2X_INDEX_CLICK_MAX: + ret = cs40l2x_stop_playback(cs40l2x); + if (ret) + dev_err(dev, "Failed to stop playback\n"); + break; + + case CS40L2X_INDEX_PBQ: + ret = cs40l2x_pbq_cancel(cs40l2x); + if (ret) + dev_err(dev, "Failed to cancel playback queue\n"); + break; + + case CS40L2X_INDEX_DIAG: + ret = cs40l2x_diag_capture(cs40l2x); + if (ret) + dev_err(dev, "Failed to capture f0 and ReDC\n"); + + ret = regmap_write(regmap, CS40L2X_MBOX_STIMULUS_MODE, 0); + if (ret) + dev_err(dev, "Failed to disable stimulus mode\n"); + + ret = cs40l2x_dig_scale_set(cs40l2x, cs40l2x->diag_dig_scale); + if (ret) + dev_err(dev, "Failed to restore digital scale\n"); + pm_relax(dev); + break; + + default: + dev_err(dev, "Invalid wavetable index\n"); + } + + cs40l2x->cp_trailer_index = 0; + + mutex_unlock(&cs40l2x->lock); +} + +#ifdef CONFIG_ANDROID_TIMED_OUTPUT +/* vibration callback for timed output device */ +static void cs40l2x_vibe_enable(struct timed_output_dev *sdev, int timeout) +{ + struct cs40l2x_private *cs40l2x = + container_of(sdev, struct cs40l2x_private, timed_dev); + + if (timeout > 0) { + cs40l2x->vibe_timeout = timeout; + queue_work(cs40l2x->vibe_workqueue, &cs40l2x->vibe_start_work); + } else { + hrtimer_cancel(&cs40l2x->vibe_timer); + queue_work(cs40l2x->vibe_workqueue, &cs40l2x->vibe_stop_work); + } +} + +static int cs40l2x_vibe_get_time(struct timed_output_dev *sdev) +{ + struct cs40l2x_private *cs40l2x = + container_of(sdev, struct cs40l2x_private, timed_dev); + int ret = 0; + + if (hrtimer_active(&cs40l2x->vibe_timer)) + ret = ktime_to_ms(hrtimer_get_remaining(&cs40l2x->vibe_timer)); + + return ret; +} + +static enum hrtimer_restart cs40l2x_vibe_timer(struct hrtimer *timer) +{ + struct cs40l2x_private *cs40l2x = + container_of(timer, struct cs40l2x_private, vibe_timer); + + queue_work(cs40l2x->vibe_workqueue, &cs40l2x->vibe_stop_work); + + return HRTIMER_NORESTART; +} + +static void cs40l2x_create_timed_output(struct cs40l2x_private *cs40l2x) +{ + int ret; + struct timed_output_dev *timed_dev = &cs40l2x->timed_dev; + struct hrtimer *vibe_timer = &cs40l2x->vibe_timer; + struct device *dev = cs40l2x->dev; + + timed_dev->name = CS40L2X_DEVICE_NAME; + timed_dev->enable = cs40l2x_vibe_enable; + timed_dev->get_time = cs40l2x_vibe_get_time; + + ret = timed_output_dev_register(timed_dev); + if (ret) { + dev_err(dev, "Failed to register timed output device: %d\n", + ret); + return; + } + + hrtimer_init(vibe_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + vibe_timer->function = cs40l2x_vibe_timer; + + ret = sysfs_create_group(&cs40l2x->timed_dev.dev->kobj, + &cs40l2x_dev_attr_group); + if (ret) { + dev_err(dev, "Failed to create sysfs group: %d\n", ret); + return; + } +} +#else +/* vibration callback for LED device */ +static void cs40l2x_vibe_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct cs40l2x_private *cs40l2x = + container_of(led_cdev, struct cs40l2x_private, led_dev); + + switch (brightness) { + case LED_OFF: + queue_work(cs40l2x->vibe_workqueue, &cs40l2x->vibe_stop_work); + break; + default: + queue_work(cs40l2x->vibe_workqueue, &cs40l2x->vibe_start_work); + } +} + +static void cs40l2x_create_led(struct cs40l2x_private *cs40l2x) +{ + int ret; + struct led_classdev *led_dev = &cs40l2x->led_dev; + struct device *dev = cs40l2x->dev; + + led_dev->name = CS40L2X_DEVICE_NAME; + led_dev->max_brightness = LED_FULL; + led_dev->brightness_set = cs40l2x_vibe_brightness_set; + led_dev->default_trigger = "transient"; + + ret = led_classdev_register(dev, led_dev); + if (ret) { + dev_err(dev, "Failed to register LED device: %d\n", ret); + return; + } + + ret = sysfs_create_group(&cs40l2x->dev->kobj, &cs40l2x_dev_attr_group); + if (ret) { + dev_err(dev, "Failed to create sysfs group: %d\n", ret); + return; + } +} +#endif /* CONFIG_ANDROID_TIMED_OUTPUT */ + +static void cs40l2x_vibe_init(struct cs40l2x_private *cs40l2x) +{ + struct hrtimer *pbq_timer = &cs40l2x->pbq_timer; + int ret; + +#ifdef CONFIG_ANDROID_TIMED_OUTPUT + cs40l2x_create_timed_output(cs40l2x); +#else + cs40l2x_create_led(cs40l2x); +#endif /* CONFIG_ANDROID_TIMED_OUTPUT */ + + cs40l2x->vibe_workqueue = + alloc_ordered_workqueue("vibe_workqueue", WQ_HIGHPRI); + if (!cs40l2x->vibe_workqueue) { + dev_err(cs40l2x->dev, "Failed to allocate workqueue\n"); + return; + } + + INIT_WORK(&cs40l2x->vibe_start_work, cs40l2x_vibe_start_worker); + INIT_WORK(&cs40l2x->vibe_pbq_work, cs40l2x_vibe_pbq_worker); + INIT_WORK(&cs40l2x->vibe_stop_work, cs40l2x_vibe_stop_worker); + + hrtimer_init(pbq_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + pbq_timer->function = cs40l2x_pbq_timer; + + ret = device_init_wakeup(cs40l2x->dev, true); + if (ret) { + dev_err(cs40l2x->dev, "Failed to initialize wakeup source\n"); + return; + } + + cs40l2x->vibe_init_success = true; +} + +static int cs40l2x_coeff_init(struct cs40l2x_private *cs40l2x) +{ + int ret, i; + struct regmap *regmap = cs40l2x->regmap; + struct device *dev = cs40l2x->dev; + struct cs40l2x_coeff_desc *coeff_desc; + unsigned int val, num_algos, algo_id, algo_rev; + unsigned int xm_base, xm_size, ym_base, ym_size; + unsigned int reg = CS40L2X_XM_FW_ID; + + ret = regmap_read(regmap, CS40L2X_XM_NUM_ALGOS, &num_algos); + if (ret) { + dev_err(dev, "Failed to read number of algorithms\n"); + return ret; + } + + if (num_algos > CS40L2X_NUM_ALGOS_MAX) { + dev_err(dev, "Invalid number of algorithms\n"); + return -EINVAL; + } + + /* add one extra iteration to account for system algorithm */ + for (i = 0; i < (num_algos + 1); i++) { + ret = regmap_read(regmap, + reg + CS40L2X_ALGO_ID_OFFSET, &algo_id); + if (ret) { + dev_err(dev, "Failed to read algo. %d ID\n", i); + return ret; + } + + ret = regmap_read(regmap, + reg + CS40L2X_ALGO_REV_OFFSET, &algo_rev); + if (ret) { + dev_err(dev, "Failed to read algo. %d revision\n", i); + return ret; + } + + /* discern firmware revision from system algorithm */ + if (i == 0) { + if (algo_rev < CS40L2X_FW_REV_MIN) { + dev_err(dev, + "Invalid firmware revision: %d.%d.%d\n", + (algo_rev & 0xFF0000) >> 16, + (algo_rev & 0xFF00) >> 8, + algo_rev & 0xFF); + return -EINVAL; + } + dev_info(dev, "Firmware revision %d.%d.%d\n", + (algo_rev & 0xFF0000) >> 16, + (algo_rev & 0xFF00) >> 8, + algo_rev & 0xFF); + } + + ret = regmap_read(regmap, + reg + CS40L2X_ALGO_XM_BASE_OFFSET, &xm_base); + if (ret) { + dev_err(dev, "Failed to read algo. %d XM_BASE\n", i); + return ret; + } + + ret = regmap_read(regmap, + reg + CS40L2X_ALGO_XM_SIZE_OFFSET, &xm_size); + if (ret) { + dev_err(dev, "Failed to read algo. %d XM_SIZE\n", i); + return ret; + } + + ret = regmap_read(regmap, + reg + CS40L2X_ALGO_YM_BASE_OFFSET, &ym_base); + if (ret) { + dev_err(dev, "Failed to read algo. %d YM_BASE\n", i); + return ret; + } + + ret = regmap_read(regmap, + reg + CS40L2X_ALGO_YM_SIZE_OFFSET, &ym_size); + if (ret) { + dev_err(dev, "Failed to read algo. %d YM_SIZE\n", i); + return ret; + } + + list_for_each_entry(coeff_desc, + &cs40l2x->coeff_desc_head, list) { + + if (coeff_desc->parent_id != algo_id) + continue; + + switch (coeff_desc->block_type) { + case CS40L2X_XM_UNPACKED_TYPE: + coeff_desc->reg = CS40L2X_DSP1_XMEM_UNPACK24_0 + + xm_base * 4 + + coeff_desc->block_offset * 4; + if (!strcmp(coeff_desc->name, "WAVETABLE")) { + cs40l2x->wt_limit_xm = (xm_size + - coeff_desc->block_offset) * 4; + cs40l2x->vibegen_id = algo_id; + cs40l2x->vibegen_rev = algo_rev; + } + break; + case CS40L2X_YM_UNPACKED_TYPE: + coeff_desc->reg = CS40L2X_DSP1_YMEM_UNPACK24_0 + + ym_base * 4 + + coeff_desc->block_offset * 4; + if (!strcmp(coeff_desc->name, "WAVETABLEYM")) + cs40l2x->wt_limit_ym = (ym_size + - coeff_desc->block_offset) * 4; + break; + } + + dev_dbg(dev, "Found control %s at 0x%08X\n", + coeff_desc->name, coeff_desc->reg); + } + + /* system algo. contains one extra register (num. algos.) */ + if (i) + reg += CS40L2X_ALGO_ENTRY_SIZE; + else + reg += (CS40L2X_ALGO_ENTRY_SIZE + 4); + } + + ret = regmap_read(regmap, reg, &val); + if (ret) { + dev_err(dev, "Failed to read list terminator\n"); + return ret; + } + + if (val != CS40L2X_ALGO_LIST_TERM) { + dev_err(dev, "Invalid list terminator: 0x%X\n", val); + return -EINVAL; + } + + dev_info(dev, "Maximum wavetable size: %d bytes (XM), %d bytes (YM)\n", + cs40l2x->wt_limit_xm / 4 * 3, + cs40l2x->wt_limit_ym / 4 * 3); + + return 0; +} + +static void cs40l2x_dsp_start(struct cs40l2x_private *cs40l2x) +{ + int ret; + struct regmap *regmap = cs40l2x->regmap; + struct device *dev = cs40l2x->dev; + unsigned int val; + int dsp_timeout = CS40L2X_DSP_TIMEOUT_COUNT; + + if (cs40l2x->pdata.gpio1_mode != CS40L2X_GPIO1_MODE_DEF_ON) { + ret = regmap_write(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "GPIO_ENABLE", + CS40L2X_XM_UNPACKED_TYPE), + CS40L2X_GPIO1_DISABLED); + if (ret) { + dev_err(dev, "Failed to pre-configure GPIO1\n"); + return; + } + } + + switch (cs40l2x->revid) { + case CS40L2X_REVID_A0: + ret = regmap_update_bits(regmap, CS40L2X_PWR_CTRL1, + CS40L2X_GLOBAL_EN_MASK, + 1 << CS40L2X_GLOBAL_EN_SHIFT); + if (ret) { + dev_err(dev, "Failed to enable device\n"); + return; + } + + ret = regmap_update_bits(regmap, CS40L2X_DSP1_CCM_CORE_CTRL, + CS40L2X_DSP1_RESET_MASK | + CS40L2X_DSP1_EN_MASK, + (1 << CS40L2X_DSP1_RESET_SHIFT) | + (1 << CS40L2X_DSP1_EN_SHIFT)); + if (ret) { + dev_err(dev, "Failed to start DSP\n"); + return; + } + break; + default: + ret = regmap_update_bits(regmap, CS40L2X_PWRMGT_CTL, + CS40L2X_MEM_RDY_MASK, + 1 << CS40L2X_MEM_RDY_SHIFT); + if (ret) { + dev_err(dev, "Failed to set memory ready flag\n"); + return; + } + + ret = regmap_update_bits(regmap, CS40L2X_DSP1_CCM_CORE_CTRL, + CS40L2X_DSP1_RESET_MASK, + 1 << CS40L2X_DSP1_RESET_SHIFT); + if (ret) { + dev_err(dev, "Failed to restart DSP\n"); + return; + } + } + + while (dsp_timeout > 0) { + usleep_range(10000, 10100); + + ret = regmap_read(regmap, cs40l2x_dsp_reg(cs40l2x, "HALO_STATE", + CS40L2X_XM_UNPACKED_TYPE), &val); + if (ret) { + dev_err(dev, "Failed to read DSP status\n"); + return; + } + + if (val == CS40L2X_HALO_STATE_RUNNING) + break; + + dsp_timeout--; + } + + if (dsp_timeout == 0) { + dev_err(dev, "Timed out with DSP status = %d\n", val); + return; + } + + ret = regmap_write(regmap, cs40l2x_dsp_reg(cs40l2x, "TIMEOUT_MS", + CS40L2X_XM_UNPACKED_TYPE), CS40L2X_TIMEOUT_MS_MAX); + if (ret) { + dev_err(dev, "Failed to extend playback timeout\n"); + return; + } + + ret = regmap_read(regmap, cs40l2x_dsp_reg(cs40l2x, "NUMBEROFWAVES", + CS40L2X_XM_UNPACKED_TYPE), &cs40l2x->num_waves); + if (ret) { + dev_err(dev, "Failed to count wavetable entries\n"); + return; + } + + if (cs40l2x->num_waves == 0) { + dev_err(dev, "Wavetable is empty\n"); + return; + } + + if (cs40l2x->pdata.f0_default) { + ret = regmap_write(regmap, + cs40l2x_dsp_reg(cs40l2x, "F0_STORED", + CS40L2X_XM_UNPACKED_TYPE), + cs40l2x->pdata.f0_default); + if (ret) { + dev_err(dev, "Failed to write default f0\n"); + return; + } + } + + if (cs40l2x->pdata.redc_default) { + ret = regmap_write(regmap, + cs40l2x_dsp_reg(cs40l2x, "REDC_STORED", + CS40L2X_XM_UNPACKED_TYPE), + cs40l2x->pdata.redc_default); + if (ret) { + dev_err(dev, "Failed to write default ReDC\n"); + return; + } + } + + if (cs40l2x->pdata.gpio1_rise_index > 0 + && cs40l2x->pdata.gpio1_rise_index + < cs40l2x->num_waves) { + ret = regmap_write(regmap, + cs40l2x_dsp_reg(cs40l2x, "INDEXBUTTONPRESS", + CS40L2X_XM_UNPACKED_TYPE), + cs40l2x->pdata.gpio1_rise_index); + if (ret) { + dev_err(dev, + "Failed to write default gpio1_rise_index\n"); + return; + } + } else if (cs40l2x->pdata.gpio1_rise_index >= cs40l2x->num_waves) { + dev_warn(dev, "Ignored default gpio1_rise_index\n"); + } + + if (cs40l2x->pdata.gpio1_fall_index > 0 + && cs40l2x->pdata.gpio1_fall_index + < cs40l2x->num_waves) { + ret = regmap_write(regmap, + cs40l2x_dsp_reg(cs40l2x, "INDEXBUTTONRELEASE", + CS40L2X_XM_UNPACKED_TYPE), + cs40l2x->pdata.gpio1_fall_index); + if (ret) { + dev_err(dev, + "Failed to write default gpio1_fall_index\n"); + return; + } + } else if (cs40l2x->pdata.gpio1_fall_index >= cs40l2x->num_waves) { + dev_warn(dev, "Ignored default gpio1_fall_index\n"); + } + + if (cs40l2x->pdata.gpio1_fall_timeout > 0 + && (cs40l2x->pdata.gpio1_fall_timeout + & CS40L2X_PDATA_MASK) + <= CS40L2X_PR_TIMEOUT_MAX) { + ret = regmap_write(regmap, + cs40l2x_dsp_reg(cs40l2x, + "PRESS_RELEASE_TIMEOUT", + CS40L2X_XM_UNPACKED_TYPE), + cs40l2x->pdata.gpio1_fall_timeout + & CS40L2X_PDATA_MASK); + if (ret) { + dev_err(dev, + "Failed to write default gpio1_fall_timeout\n"); + return; + } + } else if ((cs40l2x->pdata.gpio1_fall_timeout + & CS40L2X_PDATA_MASK) > CS40L2X_PR_TIMEOUT_MAX) { + dev_warn(dev, "Ignored default gpio1_fall_timeout\n"); + } + + dev_info(dev, "Normal-mode haptics successfully started\n"); + + cs40l2x_vibe_init(cs40l2x); +} + +static int cs40l2x_raw_write(struct cs40l2x_private *cs40l2x, unsigned int reg, + const void *val, size_t val_len, size_t limit) +{ + int ret, i; + + /* split "val" into smaller writes not to exceed "limit" in length */ + for (i = 0; i < val_len; i += limit) { + ret = regmap_raw_write(cs40l2x->regmap, (reg + i), (val + i), + (val_len - i) > limit ? limit : (val_len - i)); + if (ret) + break; + } + + return ret; +} + +static void cs40l2x_waveform_load(const struct firmware *fw, void *context) +{ + int ret; + struct cs40l2x_private *cs40l2x = (struct cs40l2x_private *)context; + struct device *dev = cs40l2x->dev; + unsigned int pos = CS40L2X_WT_FILE_HEADER_SIZE; + unsigned int block_type, block_length; + unsigned int algo_id, algo_rev; + + if (!fw) + goto skip_loading; + + if (memcmp(fw->data, "WMDR", 4)) { + dev_err(dev, "Failed to recognize waveform file\n"); + goto err_rls_fw; + } + + while (pos < fw->size) { + + /* block offset is not used here */ + pos += CS40L2X_WT_DBLK_OFFSET_SIZE; + + block_type = fw->data[pos] + + (fw->data[pos + 1] << 8); + pos += CS40L2X_WT_DBLK_TYPE_SIZE; + + algo_id = fw->data[pos] + + (fw->data[pos + 1] << 8) + + (fw->data[pos + 2] << 16) + + (fw->data[pos + 3] << 24); + pos += CS40L2X_WT_ALGO_ID_SIZE; + + algo_rev = fw->data[pos] + + (fw->data[pos + 1] << 8) + + (fw->data[pos + 2] << 16) + + (fw->data[pos + 3] << 24); + pos += CS40L2X_WT_ALGO_REV_SIZE; + + /* sample rate is not used here */ + pos += CS40L2X_WT_SAMPLE_RATE_SIZE; + + block_length = fw->data[pos] + + (fw->data[pos + 1] << 8) + + (fw->data[pos + 2] << 16) + + (fw->data[pos + 3] << 24); + pos += CS40L2X_WT_DBLK_LENGTH_SIZE; + + switch (block_type) { + case CS40L2X_XM_UNPACKED_TYPE: + if (algo_id != cs40l2x->vibegen_id) { + dev_err(dev, "Invalid algo. ID: 0x%06X\n", + algo_id); + goto err_rls_fw; + } + + if (((algo_rev >> 8) & CS40L2X_ALGO_REV_MASK) + != (cs40l2x->vibegen_rev + & CS40L2X_ALGO_REV_MASK)) { + dev_err(dev, "Invalid algo. rev.: %d.%d.%d\n", + (algo_rev & 0xFF000000) >> 24, + (algo_rev & 0xFF0000) >> 16, + (algo_rev & 0xFF00) >> 8); + goto err_rls_fw; + } + + if (block_length > cs40l2x->wt_limit_xm) { + dev_err(dev, + "Wavetable too large: %d bytes (XM)\n", + block_length / 4 * 3); + goto err_rls_fw; + } + + ret = cs40l2x_raw_write(cs40l2x, + cs40l2x_dsp_reg(cs40l2x, "WAVETABLE", + CS40L2X_XM_UNPACKED_TYPE), + &fw->data[pos], block_length, + CS40L2X_MAX_WLEN); + if (ret) { + dev_err(dev, + "Failed to write XM_UNPACKED memory\n"); + goto err_rls_fw; + } + break; + case CS40L2X_YM_UNPACKED_TYPE: + if (algo_id != cs40l2x->vibegen_id) { + dev_err(dev, "Invalid algo. ID: 0x%06X\n", + algo_id); + goto err_rls_fw; + } + + if (((algo_rev >> 8) & CS40L2X_ALGO_REV_MASK) + != (cs40l2x->vibegen_rev + & CS40L2X_ALGO_REV_MASK)) { + dev_err(dev, "Invalid algo. rev.: %d.%d.%d\n", + (algo_rev & 0xFF000000) >> 24, + (algo_rev & 0xFF0000) >> 16, + (algo_rev & 0xFF00) >> 8); + goto err_rls_fw; + } + + if (block_length > cs40l2x->wt_limit_ym) { + dev_err(dev, + "Wavetable too large: %d bytes (YM)\n", + block_length / 4 * 3); + goto err_rls_fw; + } + + ret = cs40l2x_raw_write(cs40l2x, + cs40l2x_dsp_reg(cs40l2x, "WAVETABLEYM", + CS40L2X_YM_UNPACKED_TYPE), + &fw->data[pos], block_length, + CS40L2X_MAX_WLEN); + if (ret) { + dev_err(dev, + "Failed to write YM_UNPACKED memory\n"); + goto err_rls_fw; + } + break; + } + + pos += block_length; + } + +skip_loading: + cs40l2x_dsp_start(cs40l2x); +err_rls_fw: + release_firmware(fw); +} + +static int cs40l2x_algo_parse(struct cs40l2x_private *cs40l2x, + const unsigned char *data) +{ + struct cs40l2x_coeff_desc *coeff_desc; + unsigned int pos = 0; + unsigned int algo_id, algo_desc_length, coeff_count; + unsigned int block_offset, block_type, block_length; + unsigned char algo_name_length; + int i; + + /* record algorithm ID */ + algo_id = *(data + pos) + + (*(data + pos + 1) << 8) + + (*(data + pos + 2) << 16) + + (*(data + pos + 3) << 24); + pos += CS40L2X_ALGO_ID_SIZE; + + /* skip past algorithm name */ + algo_name_length = *(data + pos); + pos += ((algo_name_length / 4) * 4) + 4; + + /* skip past algorithm description */ + algo_desc_length = *(data + pos) + + (*(data + pos + 1) << 8); + pos += ((algo_desc_length / 4) * 4) + 4; + + /* record coefficient count */ + coeff_count = *(data + pos) + + (*(data + pos + 1) << 8) + + (*(data + pos + 2) << 16) + + (*(data + pos + 3) << 24); + pos += CS40L2X_COEFF_COUNT_SIZE; + + for (i = 0; i < coeff_count; i++) { + block_offset = *(data + pos) + + (*(data + pos + 1) << 8); + pos += CS40L2X_COEFF_OFFSET_SIZE; + + block_type = *(data + pos) + + (*(data + pos + 1) << 8); + pos += CS40L2X_COEFF_TYPE_SIZE; + + block_length = *(data + pos) + + (*(data + pos + 1) << 8) + + (*(data + pos + 2) << 16) + + (*(data + pos + 3) << 24); + pos += CS40L2X_COEFF_LENGTH_SIZE; + + coeff_desc = devm_kzalloc(cs40l2x->dev, sizeof(*coeff_desc), + GFP_KERNEL); + if (!coeff_desc) + return -ENOMEM; + + coeff_desc->parent_id = algo_id; + coeff_desc->block_offset = block_offset; + coeff_desc->block_type = block_type; + + memcpy(coeff_desc->name, data + pos + 1, *(data + pos)); + coeff_desc->name[*(data + pos)] = '\0'; + + list_add(&coeff_desc->list, &cs40l2x->coeff_desc_head); + + pos += block_length; + } + + return 0; +} + +static void cs40l2x_firmware_load(const struct firmware *fw, void *context) +{ + int ret; + struct cs40l2x_private *cs40l2x = (struct cs40l2x_private *)context; + struct device *dev = cs40l2x->dev; + unsigned int pos = CS40L2X_FW_FILE_HEADER_SIZE; + unsigned int block_offset, block_length; + unsigned char block_type; + + if (!fw) { + dev_err(dev, "Failed to request firmware file\n"); + return; + } + + if (memcmp(fw->data, "WMFW", 4)) { + dev_err(dev, "Failed to recognize firmware file\n"); + goto err_rls_fw; + } + + while (pos < fw->size) { + + block_offset = fw->data[pos] + + (fw->data[pos + 1] << 8) + + (fw->data[pos + 2] << 16); + pos += CS40L2X_FW_DBLK_OFFSET_SIZE; + + block_type = fw->data[pos]; + pos += CS40L2X_FW_DBLK_TYPE_SIZE; + + block_length = fw->data[pos] + + (fw->data[pos + 1] << 8) + + (fw->data[pos + 2] << 16) + + (fw->data[pos + 3] << 24); + pos += CS40L2X_FW_DBLK_LENGTH_SIZE; + + switch (block_type) { + case CS40L2X_PM_PACKED_TYPE: + ret = cs40l2x_raw_write(cs40l2x, + CS40L2X_DSP1_PMEM_0 + + block_offset * 5, + &fw->data[pos], block_length, + CS40L2X_MAX_WLEN); + if (ret) { + dev_err(dev, + "Failed to write PM_PACKED memory\n"); + goto err_rls_fw; + } + break; + case CS40L2X_XM_PACKED_TYPE: + ret = cs40l2x_raw_write(cs40l2x, + CS40L2X_DSP1_XMEM_PACK_0 + + block_offset * 3, + &fw->data[pos], block_length, + CS40L2X_MAX_WLEN); + if (ret) { + dev_err(dev, + "Failed to write XM_PACKED memory\n"); + goto err_rls_fw; + } + break; + case CS40L2X_YM_PACKED_TYPE: + ret = cs40l2x_raw_write(cs40l2x, + CS40L2X_DSP1_YMEM_PACK_0 + + block_offset * 3, + &fw->data[pos], block_length, + CS40L2X_MAX_WLEN); + if (ret) { + dev_err(dev, + "Failed to write YM_PACKED memory\n"); + goto err_rls_fw; + } + break; + case CS40L2X_ALGO_INFO_TYPE: + ret = cs40l2x_algo_parse(cs40l2x, &fw->data[pos]); + if (ret) { + dev_err(dev, + "Failed to parse algorithm: %d\n", ret); + goto err_rls_fw; + } + break; + } + + pos += block_length; + } + + ret = cs40l2x_coeff_init(cs40l2x); + if (ret) + goto err_rls_fw; + + request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, CS40L2X_WT_NAME, + dev, GFP_KERNEL, cs40l2x, cs40l2x_waveform_load); +err_rls_fw: + release_firmware(fw); +} + +static int cs40l2x_boost_config(struct cs40l2x_private *cs40l2x, + int boost_ind, int boost_cap, int boost_ipk) +{ + int ret; + unsigned char bst_lbst_val, bst_cbst_range, bst_ipk_scaled; + struct regmap *regmap = cs40l2x->regmap; + struct device *dev = cs40l2x->dev; + + switch (boost_ind) { + case 1000: /* 1.0 uH */ + bst_lbst_val = 0; + break; + case 1200: /* 1.2 uH */ + bst_lbst_val = 1; + break; + case 1500: /* 1.5 uH */ + bst_lbst_val = 2; + break; + case 2200: /* 2.2 uH */ + bst_lbst_val = 3; + break; + default: + dev_err(dev, "Invalid boost inductor value: %d nH\n", + boost_ind); + return -EINVAL; + } + + switch (boost_cap) { + case 0 ... 19: + bst_cbst_range = 0; + break; + case 20 ... 50: + bst_cbst_range = 1; + break; + case 51 ... 100: + bst_cbst_range = 2; + break; + case 101 ... 200: + bst_cbst_range = 3; + break; + default: /* 201 uF and greater */ + bst_cbst_range = 4; + } + + ret = regmap_update_bits(regmap, CS40L2X_BSTCVRT_COEFF, + CS40L2X_BST_K1_MASK, + cs40l2x_bst_k1_table[bst_lbst_val][bst_cbst_range] + << CS40L2X_BST_K1_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost K1 coefficient\n"); + return ret; + } + + ret = regmap_update_bits(regmap, CS40L2X_BSTCVRT_COEFF, + CS40L2X_BST_K2_MASK, + cs40l2x_bst_k2_table[bst_lbst_val][bst_cbst_range] + << CS40L2X_BST_K2_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost K2 coefficient\n"); + return ret; + } + + ret = regmap_update_bits(regmap, CS40L2X_BSTCVRT_SLOPE_LBST, + CS40L2X_BST_SLOPE_MASK, + cs40l2x_bst_slope_table[bst_lbst_val] + << CS40L2X_BST_SLOPE_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost slope coefficient\n"); + return ret; + } + + ret = regmap_update_bits(regmap, CS40L2X_BSTCVRT_SLOPE_LBST, + CS40L2X_BST_LBST_VAL_MASK, + bst_lbst_val << CS40L2X_BST_LBST_VAL_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost inductor value\n"); + return ret; + } + + if ((boost_ipk < 1600) || (boost_ipk > 4500)) { + dev_err(dev, "Invalid boost inductor peak current: %d mA\n", + boost_ipk); + return -EINVAL; + } + bst_ipk_scaled = ((boost_ipk - 1600) / 50) + 0x10; + + ret = regmap_update_bits(regmap, CS40L2X_BSTCVRT_PEAK_CUR, + CS40L2X_BST_IPK_MASK, + bst_ipk_scaled << CS40L2X_BST_IPK_SHIFT); + if (ret) { + dev_err(dev, "Failed to write boost inductor peak current\n"); + return ret; + } + + return 0; +} + +static const struct reg_sequence cs40l2x_mpu_config[] = { + {CS40L2X_DSP1_MPU_LOCK_CONFIG, CS40L2X_MPU_UNLOCK_CODE1}, + {CS40L2X_DSP1_MPU_LOCK_CONFIG, CS40L2X_MPU_UNLOCK_CODE2}, + {CS40L2X_DSP1_MPU_XM_ACCESS0, 0xFFFFFFFF}, + {CS40L2X_DSP1_MPU_YM_ACCESS0, 0xFFFFFFFF}, + {CS40L2X_DSP1_MPU_WNDW_ACCESS0, 0xFFFFFFFF}, + {CS40L2X_DSP1_MPU_XREG_ACCESS0, 0xFFFFFFFF}, + {CS40L2X_DSP1_MPU_YREG_ACCESS0, 0xFFFFFFFF}, + {CS40L2X_DSP1_MPU_WNDW_ACCESS1, 0xFFFFFFFF}, + {CS40L2X_DSP1_MPU_XREG_ACCESS1, 0xFFFFFFFF}, + {CS40L2X_DSP1_MPU_YREG_ACCESS1, 0xFFFFFFFF}, + {CS40L2X_DSP1_MPU_WNDW_ACCESS2, 0xFFFFFFFF}, + {CS40L2X_DSP1_MPU_XREG_ACCESS2, 0xFFFFFFFF}, + {CS40L2X_DSP1_MPU_YREG_ACCESS2, 0xFFFFFFFF}, + {CS40L2X_DSP1_MPU_WNDW_ACCESS3, 0xFFFFFFFF}, + {CS40L2X_DSP1_MPU_XREG_ACCESS3, 0xFFFFFFFF}, + {CS40L2X_DSP1_MPU_YREG_ACCESS3, 0xFFFFFFFF}, + {CS40L2X_DSP1_MPU_LOCK_CONFIG, 0x00000000} +}; + +static int cs40l2x_dsp_load(struct cs40l2x_private *cs40l2x) +{ + int ret; + unsigned int revid = cs40l2x->revid; + struct regmap *regmap = cs40l2x->regmap; + struct device *dev = cs40l2x->dev; + + switch (revid) { + case CS40L2X_REVID_A0: + case CS40L2X_REVID_B0: + ret = regmap_multi_reg_write(regmap, cs40l2x_mpu_config, + ARRAY_SIZE(cs40l2x_mpu_config)); + if (ret) { + dev_err(dev, "Failed to configure MPU\n"); + return ret; + } + + request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + CS40L2X_FW_NAME_A0, dev, GFP_KERNEL, cs40l2x, + cs40l2x_firmware_load); + return 0; + case CS40L2X_REVID_B1: + request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + CS40L2X_FW_NAME_B1, dev, GFP_KERNEL, cs40l2x, + cs40l2x_firmware_load); + return 0; + default: + dev_err(dev, "No firmware defined for revision %02X\n", revid); + return -EINVAL; + } +} + +static int cs40l2x_init(struct cs40l2x_private *cs40l2x) +{ + int ret; + struct regmap *regmap = cs40l2x->regmap; + struct device *dev = cs40l2x->dev; + + /* REFCLK configuration is handled by revision B1 ROM */ + if (cs40l2x->pdata.refclk_gpio2 && + (cs40l2x->revid < CS40L2X_REVID_B1)) { + ret = regmap_update_bits(regmap, CS40L2X_GPIO_PAD_CONTROL, + CS40L2X_GP2_CTRL_MASK, + CS40L2X_GP2_CTRL_MCLK + << CS40L2X_GP2_CTRL_SHIFT); + if (ret) { + dev_err(dev, "Failed to select GPIO2 function\n"); + return ret; + } + + ret = regmap_update_bits(regmap, CS40L2X_PLL_CLK_CTRL, + CS40L2X_PLL_REFCLK_SEL_MASK, + CS40L2X_PLL_REFCLK_SEL_MCLK + << CS40L2X_PLL_REFCLK_SEL_SHIFT); + if (ret) { + dev_err(dev, "Failed to select clock source\n"); + return ret; + } + } + + ret = cs40l2x_boost_config(cs40l2x, cs40l2x->pdata.boost_ind, + cs40l2x->pdata.boost_cap, cs40l2x->pdata.boost_ipk); + if (ret) + return ret; + + ret = regmap_update_bits(regmap, CS40L2X_DAC_PCM1_SRC, + CS40L2X_DAC_PCM1_SRC_MASK, + CS40L2X_DAC_PCM1_SRC_DSP1TX1 + << CS40L2X_DAC_PCM1_SRC_SHIFT); + if (ret) { + dev_err(dev, "Failed to route DSP to amplifier\n"); + return ret; + } + + ret = regmap_update_bits(regmap, CS40L2X_DSP1_RX2_SRC, + CS40L2X_DSP1_RXn_SRC_MASK, + CS40L2X_DSP1_RXn_SRC_VMON + << CS40L2X_DSP1_RXn_SRC_SHIFT); + if (ret) { + dev_err(dev, "Failed to route voltage monitor to DSP\n"); + return ret; + } + + ret = regmap_update_bits(regmap, CS40L2X_DSP1_RX3_SRC, + CS40L2X_DSP1_RXn_SRC_MASK, + CS40L2X_DSP1_RXn_SRC_IMON + << CS40L2X_DSP1_RXn_SRC_SHIFT); + if (ret) { + dev_err(dev, "Failed to route current monitor to DSP\n"); + return ret; + } + + ret = regmap_update_bits(regmap, CS40L2X_DSP1_RX4_SRC, + CS40L2X_DSP1_RXn_SRC_MASK, + CS40L2X_DSP1_RXn_SRC_VPMON + << CS40L2X_DSP1_RXn_SRC_SHIFT); + if (ret) { + dev_err(dev, "Failed to route battery monitor to DSP\n"); + return ret; + } + + return 0; +} + +static int cs40l2x_otp_unpack(struct cs40l2x_private *cs40l2x) +{ + struct regmap *regmap = cs40l2x->regmap; + struct device *dev = cs40l2x->dev; + struct cs40l2x_trim trim; + unsigned char row_offset, col_offset; + unsigned int val, otp_map; + unsigned int *otp_mem; + int ret, i; + + otp_mem = kmalloc_array(CS40L2X_NUM_OTP_WORDS, + sizeof(*otp_mem), GFP_KERNEL); + if (!otp_mem) + return -ENOMEM; + + ret = regmap_read(cs40l2x->regmap, CS40L2X_OTPID, &val); + if (ret) { + dev_err(dev, "Failed to read OTP ID\n"); + goto err_otp_unpack; + } + + /* hard matching against known OTP IDs */ + for (i = 0; i < CS40L2X_NUM_OTP_MAPS; i++) { + if (cs40l2x_otp_map[i].id == val) { + otp_map = i; + break; + } + } + + /* reject unrecognized IDs, including untrimmed devices (OTP ID = 0) */ + if (i == CS40L2X_NUM_OTP_MAPS) { + dev_err(dev, "Unrecognized OTP ID: 0x%01X\n", val); + ret = -ENODEV; + goto err_otp_unpack; + } + + dev_dbg(dev, "Found OTP ID: 0x%01X\n", val); + + ret = regmap_bulk_read(regmap, CS40L2X_OTP_MEM0, otp_mem, + CS40L2X_NUM_OTP_WORDS); + if (ret) { + dev_err(dev, "Failed to read OTP contents\n"); + goto err_otp_unpack; + } + + ret = regmap_write(regmap, CS40L2X_TEST_KEY_CTL, + CS40L2X_TEST_KEY_UNLOCK_CODE1); + if (ret) { + dev_err(dev, "Failed to unlock test space (step 1 of 2)\n"); + goto err_otp_unpack; + } + + ret = regmap_write(regmap, CS40L2X_TEST_KEY_CTL, + CS40L2X_TEST_KEY_UNLOCK_CODE2); + if (ret) { + dev_err(dev, "Failed to unlock test space (step 2 of 2)\n"); + goto err_otp_unpack; + } + + row_offset = cs40l2x_otp_map[otp_map].row_start; + col_offset = cs40l2x_otp_map[otp_map].col_start; + + for (i = 0; i < cs40l2x_otp_map[otp_map].num_trims; i++) { + trim = cs40l2x_otp_map[otp_map].trim_table[i]; + + if (col_offset + trim.size - 1 > 31) { + /* trim straddles word boundary */ + val = (otp_mem[row_offset] & + GENMASK(31, col_offset)) >> col_offset; + val |= (otp_mem[row_offset + 1] & + GENMASK(col_offset + trim.size - 33, 0)) + << (32 - col_offset); + } else { + /* trim does not straddle word boundary */ + val = (otp_mem[row_offset] & + GENMASK(col_offset + trim.size - 1, + col_offset)) >> col_offset; + } + + /* advance column marker and wrap if necessary */ + col_offset += trim.size; + if (col_offset > 31) { + col_offset -= 32; + row_offset++; + } + + /* skip blank trims */ + if (trim.reg == 0) + continue; + + ret = regmap_update_bits(regmap, trim.reg, + GENMASK(trim.shift + trim.size - 1, trim.shift), + val << trim.shift); + if (ret) { + dev_err(dev, "Failed to write trim %d\n", i + 1); + goto err_otp_unpack; + } + + dev_dbg(dev, "Trim %d: wrote 0x%X to 0x%08X bits [%d:%d]\n", + i + 1, val, trim.reg, + trim.shift + trim.size - 1, trim.shift); + } + + ret = regmap_write(regmap, CS40L2X_TEST_KEY_CTL, + CS40L2X_TEST_KEY_RELOCK_CODE1); + if (ret) { + dev_err(dev, "Failed to lock test space (step 1 of 2)\n"); + goto err_otp_unpack; + } + + ret = regmap_write(regmap, CS40L2X_TEST_KEY_CTL, + CS40L2X_TEST_KEY_RELOCK_CODE2); + if (ret) { + dev_err(dev, "Failed to lock test space (step 2 of 2)\n"); + goto err_otp_unpack; + } + + ret = 0; + +err_otp_unpack: + kfree(otp_mem); + + return ret; +} + +static int cs40l2x_handle_of_data(struct i2c_client *i2c_client, + struct cs40l2x_platform_data *pdata) +{ + struct device_node *np = i2c_client->dev.of_node; + struct device *dev = &i2c_client->dev; + int ret; + unsigned int out_val; + + if (!np) + return 0; + + ret = of_property_read_u32(np, "cirrus,boost-ind-nanohenry", &out_val); + if (ret) { + dev_err(dev, "Boost inductor value not specified\n"); + return -EINVAL; + } + pdata->boost_ind = out_val; + + ret = of_property_read_u32(np, "cirrus,boost-cap-microfarad", &out_val); + if (ret) { + dev_err(dev, "Boost capacitance not specified\n"); + return -EINVAL; + } + pdata->boost_cap = out_val; + + ret = of_property_read_u32(np, "cirrus,boost-ipk-milliamp", &out_val); + if (ret) { + dev_err(dev, "Boost inductor peak current not specified\n"); + return -EINVAL; + } + pdata->boost_ipk = out_val; + + pdata->refclk_gpio2 = of_property_read_bool(np, "cirrus,refclk-gpio2"); + + ret = of_property_read_u32(np, "cirrus,f0-default", &out_val); + if (!ret) + pdata->f0_default = out_val; + + ret = of_property_read_u32(np, "cirrus,f0-min", &out_val); + if (!ret) + pdata->f0_min = out_val; + + ret = of_property_read_u32(np, "cirrus,f0-max", &out_val); + if (!ret) + pdata->f0_max = out_val; + + ret = of_property_read_u32(np, "cirrus,redc-default", &out_val); + if (!ret) + pdata->redc_default = out_val; + + ret = of_property_read_u32(np, "cirrus,redc-min", &out_val); + if (!ret) + pdata->redc_min = out_val; + + ret = of_property_read_u32(np, "cirrus,redc-max", &out_val); + if (!ret) + pdata->redc_max = out_val; + + ret = of_property_read_u32(np, "cirrus,gpio1-rise-index", &out_val); + if (!ret) + pdata->gpio1_rise_index = out_val; + + ret = of_property_read_u32(np, "cirrus,gpio1-fall-index", &out_val); + if (!ret) + pdata->gpio1_fall_index = out_val; + + ret = of_property_read_u32(np, "cirrus,gpio1-fall-timeout", &out_val); + if (!ret) + pdata->gpio1_fall_timeout = out_val | CS40L2X_PDATA_PRESENT; + + ret = of_property_read_u32(np, "cirrus,gpio1-mode", &out_val); + if (!ret) { + if (out_val > CS40L2X_GPIO1_MODE_MAX) + dev_warn(dev, "Ignored default gpio1_mode\n"); + else + pdata->gpio1_mode = out_val; + } + + return 0; +} + +static int cs40l2x_basic_mode_exit(struct cs40l2x_private *cs40l2x) +{ + struct regmap *regmap = cs40l2x->regmap; + struct device *dev = cs40l2x->dev; + unsigned int val; + int shutdown_timeout = CS40L2X_BASIC_TIMEOUT_COUNT; + int ret; + + ret = regmap_read(regmap, CS40L2X_BASIC_AMP_STATUS, &val); + if (ret) { + dev_err(dev, "Failed to read basic-mode status\n"); + return ret; + } + + if (val != CS40L2X_BASIC_BOOT_DONE) { + dev_err(dev, "Unexpected basic-mode status: %02X\n", val); + return -EIO; + } + + ret = regmap_write(regmap, CS40L2X_BASIC_SHUTDOWNREQUEST, 1); + if (ret) { + dev_err(dev, "Failed to write shutdown request\n"); + return ret; + } + + while (shutdown_timeout > 0) { + usleep_range(10000, 10100); + + ret = regmap_read(regmap, CS40L2X_BASIC_SHUTDOWNREQUEST, &val); + if (ret) { + dev_err(dev, "Failed to read shutdown request\n"); + return ret; + } + + if (!val) + break; + + shutdown_timeout--; + } + + if (shutdown_timeout == 0) { + dev_err(dev, "Timed out waiting for basic-mode shutdown\n"); + return -ETIME; + } + + ret = regmap_read(regmap, CS40L2X_BASIC_STATEMACHINE, &val); + if (ret) { + dev_err(dev, "Failed to read basic-mode state\n"); + return ret; + } + + if (val != CS40L2X_BASIC_SHUTDOWN) { + dev_err(dev, "Unexpected basic-mode state: %02X\n", val); + return -EBUSY; + } + + dev_info(dev, "Basic-mode haptics successfully stopped\n"); + + return 0; +} + +static const struct reg_sequence cs40l2x_rev_a0_errata[] = { + {CS40L2X_OTP_TRIM_30, 0x9091A1C8}, + {CS40L2X_PLL_LOOP_PARAM, 0x000C1837}, + {CS40L2X_PLL_MISC_CTRL, 0x03008E0E}, + {CS40L2X_BSTCVRT_DCM_CTRL, 0x00000051}, + {CS40L2X_CTRL_ASYNC1, 0x00000004}, + {CS40L2X_IRQ1_DB3, 0x00000000}, + {CS40L2X_IRQ2_DB3, 0x00000000}, +}; + +static const struct reg_sequence cs40l2x_rev_b0_errata[] = { + {CS40L2X_PLL_LOOP_PARAM, 0x000C1837}, + {CS40L2X_PLL_MISC_CTRL, 0x03008E0E}, + {CS40L2X_TEST_KEY_CTL, CS40L2X_TEST_KEY_UNLOCK_CODE1}, + {CS40L2X_TEST_KEY_CTL, CS40L2X_TEST_KEY_UNLOCK_CODE2}, + {CS40L2X_OTP_TRIM_12, 0x002F0065}, + {CS40L2X_OTP_TRIM_13, 0x00002B4F}, + {CS40L2X_TEST_KEY_CTL, CS40L2X_TEST_KEY_RELOCK_CODE1}, + {CS40L2X_TEST_KEY_CTL, CS40L2X_TEST_KEY_RELOCK_CODE2}, +}; + +static const struct reg_sequence cs40l2x_basic_mode_revert[] = { + {CS40L2X_PWR_CTRL1, 0x00000000}, + {CS40L2X_PWR_CTRL2, 0x00003321}, + {CS40L2X_LRCK_PAD_CONTROL, 0x00000007}, + {CS40L2X_SDIN_PAD_CONTROL, 0x00000007}, + {CS40L2X_GPIO_PAD_CONTROL, 0x00000000}, + {CS40L2X_AMP_DIG_VOL_CTRL, 0x00008000}, + {CS40L2X_IRQ2_MASK1, 0xFFFFFFFF}, + {CS40L2X_IRQ2_MASK2, 0xFFFFFFFF}, +}; + +static int cs40l2x_part_num_resolve(struct cs40l2x_private *cs40l2x) +{ + struct regmap *regmap = cs40l2x->regmap; + struct device *dev = cs40l2x->dev; + unsigned int val, devid, revid; + unsigned int part_num_index; + int otp_timeout = CS40L2X_OTP_TIMEOUT_COUNT; + int ret; + + while (otp_timeout > 0) { + usleep_range(10000, 10100); + + ret = regmap_read(regmap, CS40L2X_IRQ1_STATUS4, &val); + if (ret) { + dev_err(dev, "Failed to read OTP boot status\n"); + return ret; + } + + if (val & CS40L2X_OTP_BOOT_DONE) + break; + + otp_timeout--; + } + + if (otp_timeout == 0) { + dev_err(dev, "Timed out waiting for OTP boot\n"); + return -ETIME; + } + + ret = regmap_read(regmap, CS40L2X_IRQ1_STATUS3, &val); + if (ret) { + dev_err(dev, "Failed to read OTP error status\n"); + return ret; + } + + if (val & CS40L2X_OTP_BOOT_ERR) { + dev_err(dev, "Encountered fatal OTP error\n"); + return -EIO; + } + + ret = regmap_read(regmap, CS40L2X_DEVID, &devid); + if (ret) { + dev_err(dev, "Failed to read device ID\n"); + return ret; + } + + ret = regmap_read(regmap, CS40L2X_REVID, &revid); + if (ret) { + dev_err(dev, "Failed to read revision ID\n"); + return ret; + } + + switch (devid) { + case CS40L2X_DEVID_L20: + part_num_index = 0; + if (revid != CS40L2X_REVID_A0) + goto err_revid; + + ret = regmap_register_patch(regmap, cs40l2x_rev_a0_errata, + ARRAY_SIZE(cs40l2x_rev_a0_errata)); + if (ret) { + dev_err(dev, "Failed to apply revision %02X errata\n", + revid); + return ret; + } + + ret = cs40l2x_otp_unpack(cs40l2x); + if (ret) + return ret; + break; + case CS40L2X_DEVID_L25: + part_num_index = 1; + if (revid != CS40L2X_REVID_B0) + goto err_revid; + + ret = regmap_register_patch(regmap, cs40l2x_rev_b0_errata, + ARRAY_SIZE(cs40l2x_rev_b0_errata)); + if (ret) { + dev_err(dev, "Failed to apply revision %02X errata\n", + revid); + return ret; + } + break; + case CS40L2X_DEVID_L25A: + case CS40L2X_DEVID_L25B: + part_num_index = devid - CS40L2X_DEVID_L25A + 2; + if (revid < CS40L2X_REVID_B1) + goto err_revid; + + ret = cs40l2x_basic_mode_exit(cs40l2x); + if (ret) + return ret; + + ret = regmap_multi_reg_write(regmap, cs40l2x_basic_mode_revert, + ARRAY_SIZE(cs40l2x_basic_mode_revert)); + if (ret) { + dev_err(dev, "Failed to revert basic-mode fields\n"); + return ret; + } + + ret = regmap_register_patch(regmap, cs40l2x_rev_b0_errata, + ARRAY_SIZE(cs40l2x_rev_b0_errata)); + if (ret) { + dev_err(dev, "Failed to apply revision %02X errata\n", + revid); + return ret; + } + break; + default: + dev_err(dev, "Unrecognized device ID: 0x%06X\n", devid); + return -ENODEV; + } + + dev_info(dev, "Cirrus Logic %s revision %02X\n", + cs40l2x_part_nums[part_num_index], revid); + cs40l2x->devid = devid; + cs40l2x->revid = revid; + + return 0; +err_revid: + dev_err(dev, "Unexpected revision ID for %s: %02X\n", + cs40l2x_part_nums[part_num_index], revid); + return -ENODEV; +} + +static struct regmap_config cs40l2x_regmap = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .max_register = CS40L2X_LASTREG, + .precious_reg = cs40l2x_precious_reg, + .readable_reg = cs40l2x_readable_reg, + .cache_type = REGCACHE_NONE, +}; + +static int cs40l2x_i2c_probe(struct i2c_client *i2c_client, + const struct i2c_device_id *id) +{ + int ret, i; + struct cs40l2x_private *cs40l2x; + struct device *dev = &i2c_client->dev; + struct cs40l2x_platform_data *pdata = dev_get_platdata(dev); + + cs40l2x = devm_kzalloc(dev, sizeof(struct cs40l2x_private), GFP_KERNEL); + if (!cs40l2x) + return -ENOMEM; + + cs40l2x->dev = dev; + dev_set_drvdata(dev, cs40l2x); + i2c_set_clientdata(i2c_client, cs40l2x); + + mutex_init(&cs40l2x->lock); + + INIT_LIST_HEAD(&cs40l2x->coeff_desc_head); + + cs40l2x->regmap = devm_regmap_init_i2c(i2c_client, &cs40l2x_regmap); + if (IS_ERR(cs40l2x->regmap)) { + ret = PTR_ERR(cs40l2x->regmap); + dev_err(dev, "Failed to allocate register map: %d\n", ret); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(cs40l2x_supplies); i++) + cs40l2x->supplies[i].supply = cs40l2x_supplies[i]; + + cs40l2x->num_supplies = ARRAY_SIZE(cs40l2x_supplies); + + ret = devm_regulator_bulk_get(dev, cs40l2x->num_supplies, + cs40l2x->supplies); + if (ret) { + dev_err(dev, "Failed to request core supplies: %d\n", ret); + return ret; + } + + if (pdata) { + cs40l2x->pdata = *pdata; + } else { + pdata = devm_kzalloc(dev, sizeof(struct cs40l2x_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (i2c_client->dev.of_node) { + ret = cs40l2x_handle_of_data(i2c_client, pdata); + if (ret) + return ret; + + } + cs40l2x->pdata = *pdata; + } + + ret = regulator_bulk_enable(cs40l2x->num_supplies, cs40l2x->supplies); + if (ret) { + dev_err(dev, "Failed to enable core supplies: %d\n", ret); + return ret; + } + + cs40l2x->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(cs40l2x->reset_gpio)) + return PTR_ERR(cs40l2x->reset_gpio); + + /* satisfy reset pulse width specification (with margin) */ + usleep_range(2000, 2100); + + gpiod_set_value_cansleep(cs40l2x->reset_gpio, 1); + + /* satisfy control port delay specification (with margin) */ + usleep_range(1000, 1100); + + ret = cs40l2x_part_num_resolve(cs40l2x); + if (ret) + goto err; + + ret = cs40l2x_init(cs40l2x); + if (ret) + goto err; + + ret = cs40l2x_dsp_load(cs40l2x); + if (ret) + goto err; + + return 0; +err: + gpiod_set_value_cansleep(cs40l2x->reset_gpio, 0); + + regulator_bulk_disable(cs40l2x->num_supplies, cs40l2x->supplies); + + return ret; +} + +static int cs40l2x_i2c_remove(struct i2c_client *i2c_client) +{ + struct cs40l2x_private *cs40l2x = i2c_get_clientdata(i2c_client); + + if (cs40l2x->vibe_init_success) { +#ifdef CONFIG_ANDROID_TIMED_OUTPUT + hrtimer_cancel(&cs40l2x->vibe_timer); + + timed_output_dev_unregister(&cs40l2x->timed_dev); + + sysfs_remove_group(&cs40l2x->timed_dev.dev->kobj, + &cs40l2x_dev_attr_group); +#else + led_classdev_unregister(&cs40l2x->led_dev); + + sysfs_remove_group(&cs40l2x->dev->kobj, + &cs40l2x_dev_attr_group); +#endif /* CONFIG_ANDROID_TIMED_OUTPUT */ + + hrtimer_cancel(&cs40l2x->pbq_timer); + + cancel_work_sync(&cs40l2x->vibe_start_work); + cancel_work_sync(&cs40l2x->vibe_pbq_work); + cancel_work_sync(&cs40l2x->vibe_stop_work); + + destroy_workqueue(cs40l2x->vibe_workqueue); + + device_init_wakeup(cs40l2x->dev, false); + } + + gpiod_set_value_cansleep(cs40l2x->reset_gpio, 0); + + regulator_bulk_disable(cs40l2x->num_supplies, cs40l2x->supplies); + + mutex_destroy(&cs40l2x->lock); + + return 0; +} + +static int __maybe_unused cs40l2x_suspend(struct device *dev) +{ + struct cs40l2x_private *cs40l2x = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&cs40l2x->lock); + + if (cs40l2x->pdata.gpio1_mode == CS40L2X_GPIO1_MODE_AUTO) { + ret = regmap_write(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "GPIO_ENABLE", + CS40L2X_XM_UNPACKED_TYPE), + CS40L2X_GPIO1_ENABLED); + if (ret) + dev_err(dev, "Failed to enable GPIO1 upon suspend\n"); + } + + mutex_unlock(&cs40l2x->lock); + + return ret; +} + +static int __maybe_unused cs40l2x_resume(struct device *dev) +{ + struct cs40l2x_private *cs40l2x = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&cs40l2x->lock); + + if (cs40l2x->pdata.gpio1_mode == CS40L2X_GPIO1_MODE_AUTO) { + ret = regmap_write(cs40l2x->regmap, + cs40l2x_dsp_reg(cs40l2x, "GPIO_ENABLE", + CS40L2X_XM_UNPACKED_TYPE), + CS40L2X_GPIO1_DISABLED); + if (ret) + dev_err(dev, "Failed to disable GPIO1 upon resume\n"); + } + + mutex_unlock(&cs40l2x->lock); + + return ret; +} + +static SIMPLE_DEV_PM_OPS(cs40l2x_pm_ops, cs40l2x_suspend, cs40l2x_resume); + +static const struct of_device_id cs40l2x_of_match[] = { + { .compatible = "cirrus,cs40l20" }, + { .compatible = "cirrus,cs40l25" }, + { .compatible = "cirrus,cs40l25a" }, + { .compatible = "cirrus,cs40l25b" }, + { } +}; + +MODULE_DEVICE_TABLE(of, cs40l2x_of_match); + +static const struct i2c_device_id cs40l2x_id[] = { + { "cs40l20", 0 }, + { "cs40l25", 1 }, + { "cs40l25a", 2 }, + { "cs40l25b", 3 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, cs40l2x_id); + +static struct i2c_driver cs40l2x_i2c_driver = { + .driver = { + .name = "cs40l2x", + .of_match_table = cs40l2x_of_match, + .pm = &cs40l2x_pm_ops, + }, + .id_table = cs40l2x_id, + .probe = cs40l2x_i2c_probe, + .remove = cs40l2x_i2c_remove, +}; + +module_i2c_driver(cs40l2x_i2c_driver); + +MODULE_DESCRIPTION("CS40L20/CS40L25/CS40L25A/CS40L25B Haptics Driver"); +MODULE_AUTHOR("Jeff LaBundy, Cirrus Logic Inc, "); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/cs40l2x.h b/drivers/misc/cs40l2x.h new file mode 100644 index 000000000000..a854e6395365 --- /dev/null +++ b/drivers/misc/cs40l2x.h @@ -0,0 +1,805 @@ +/* + * cs40l2x.h -- CS40L20/CS40L25/CS40L25A/CS40L25B Haptics Driver + * + * Copyright 2018 Cirrus Logic, Inc. + * + * Author: Jeff LaBundy + * + * 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. + * + */ + +#ifndef __CS40L2X_H__ +#define __CS40L2X_H__ + +#include + +#define CS40L2X_FIRSTREG 0x00000000 +#define CS40L2X_LASTREG 0x03804FE8 +#define CS40L2X_DEVID 0x00000000 +#define CS40L2X_REVID 0x00000004 +#define CS40L2X_FABID 0x00000008 +#define CS40L2X_RELID 0x0000000C +#define CS40L2X_OTPID 0x00000010 +#define CS40L2X_SFT_RESET 0x00000020 +#define CS40L2X_TEST_KEY_CTL 0x00000040 +#define CS40L2X_USER_KEY_CTL 0x00000044 +#define CS40L2X_CTRL_ASYNC1 0x00000054 +#define CS40L2X_OTP_MEM0 0x00000400 +#define CS40L2X_OTP_MEM31 0x0000047C +#define CS40L2X_OTP_CTRL0 0x00000500 +#define CS40L2X_OTP_CTRL1 0x00000504 +#define CS40L2X_OTP_CTRL3 0x00000508 +#define CS40L2X_OTP_CTRL4 0x0000050C +#define CS40L2X_OTP_CTRL5 0x00000510 +#define CS40L2X_OTP_CTRL6 0x00000514 +#define CS40L2X_OTP_CTRL7 0x00000518 +#define CS40L2X_OTP_CTRL8 0x0000051C +#define CS40L2X_PWR_CTRL1 0x00002014 +#define CS40L2X_PWR_CTRL2 0x00002018 +#define CS40L2X_PWR_CTRL3 0x0000201C +#define CS40L2X_CTRL_OVRRIDE 0x00002020 +#define CS40L2X_AMP_OUT_MUTE 0x00002024 +#define CS40L2X_PROTECT_REL_ERR_IGN 0x00002034 +#define CS40L2X_LRCK_PAD_CONTROL 0x00002418 +#define CS40L2X_SDIN_PAD_CONTROL 0x00002420 +#define CS40L2X_GPIO_PAD_CONTROL 0x0000242C +#define CS40L2X_JTAG_CONTROL 0x00002438 +#define CS40L2X_PWRMGT_CTL 0x00002900 +#define CS40L2X_WAKESRC_CTL 0x00002904 +#define CS40L2X_PWRMGT_STS 0x00002908 +#define CS40L2X_PWRMGT_TEST 0x0000290C +#define CS40L2X_PLL_CLK_CTRL 0x00002C04 +#define CS40L2X_DSP_CLK_CTRL 0x00002C08 +#define CS40L2X_GLOBAL_CLK_CTRL 0x00002C0C +#define CS40L2X_DATA_FS_SEL 0x00002C10 +#define CS40L2X_PLL_LOOP_PARAM 0x00003008 +#define CS40L2X_PLL_MISC_CTRL 0x00003014 +#define CS40L2X_MDSYNC_EN 0x00003400 +#define CS40L2X_MDSYNC_TX_ID 0x00003408 +#define CS40L2X_MDSYNC_PWR_CTRL 0x0000340C +#define CS40L2X_MDSYNC_DATA_TX 0x00003410 +#define CS40L2X_MDSYNC_TX_STATUS 0x00003414 +#define CS40L2X_MDSYNC_DATA_RX 0x0000341C +#define CS40L2X_MDSYNC_RX_STATUS 0x00003420 +#define CS40L2X_MDSYNC_ERR_STATUS 0x00003424 +#define CS40L2X_MDSYNC_SYNC_PTE2 0x00003528 +#define CS40L2X_MDSYNC_SYNC_PTE3 0x0000352C +#define CS40L2X_MDSYNC_SYNC_MSM_STATUS 0x0000353C +#define CS40L2X_BSTCVRT_VCTRL1 0x00003800 +#define CS40L2X_BSTCVRT_VCTRL2 0x00003804 +#define CS40L2X_BSTCVRT_PEAK_CUR 0x00003808 +#define CS40L2X_BSTCVRT_SFT_RAMP 0x0000380C +#define CS40L2X_BSTCVRT_COEFF 0x00003810 +#define CS40L2X_BSTCVRT_SLOPE_LBST 0x00003814 +#define CS40L2X_BSTCVRT_SW_FREQ 0x00003818 +#define CS40L2X_BSTCVRT_DCM_CTRL 0x0000381C +#define CS40L2X_BSTCVRT_DCM_MODE_FORCE 0x00003820 +#define CS40L2X_BSTCVRT_OVERVOLT_CTRL 0x00003830 +#define CS40L2X_VI_VOL_POL 0x00004000 +#define CS40L2X_DTEMP_WARN_THLD 0x00004220 +#define CS40L2X_DTEMP_CFG 0x00004224 +#define CS40L2X_DTEMP_EN 0x00004308 +#define CS40L2X_VPVBST_FS_SEL 0x00004400 +#define CS40L2X_SP_ENABLES 0x00004800 +#define CS40L2X_SP_RATE_CTRL 0x00004804 +#define CS40L2X_SP_FORMAT 0x00004808 +#define CS40L2X_SP_HIZ_CTRL 0x0000480C +#define CS40L2X_SP_FRAME_TX_SLOT 0x00004810 +#define CS40L2X_SP_FRAME_RX_SLOT 0x00004820 +#define CS40L2X_SP_TX_WL 0x00004830 +#define CS40L2X_SP_RX_WL 0x00004840 +#define CS40L2X_DAC_PCM1_SRC 0x00004C00 +#define CS40L2X_ASP_TX1_SRC 0x00004C20 +#define CS40L2X_ASP_TX2_SRC 0x00004C24 +#define CS40L2X_ASP_TX3_SRC 0x00004C28 +#define CS40L2X_ASP_TX4_SRC 0x00004C2C +#define CS40L2X_DSP1_RX1_SRC 0x00004C40 +#define CS40L2X_DSP1_RX2_SRC 0x00004C44 +#define CS40L2X_DSP1_RX3_SRC 0x00004C48 +#define CS40L2X_DSP1_RX4_SRC 0x00004C4C +#define CS40L2X_DSP1_RX5_SRC 0x00004C50 +#define CS40L2X_DSP1_RX6_SRC 0x00004C54 +#define CS40L2X_DSP1_RX7_SRC 0x00004C58 +#define CS40L2X_DSP1_RX8_SRC 0x00004C5C +#define CS40L2X_NGATE1_SRC 0x00004C60 +#define CS40L2X_NGATE2_SRC 0x00004C64 +#define CS40L2X_AMP_DIG_VOL_CTRL 0x00006000 +#define CS40L2X_VPBR_CFG 0x00006404 +#define CS40L2X_VBBR_CFG 0x00006408 +#define CS40L2X_VPBR_STATUS 0x0000640C +#define CS40L2X_VBBR_STATUS 0x00006410 +#define CS40L2X_OVERTEMP_CFG 0x00006414 +#define CS40L2X_AMP_ERR_VOL 0x00006418 +#define CS40L2X_VOL_STATUS_TO_DSP 0x00006450 +#define CS40L2X_CLASSH_CFG 0x00006800 +#define CS40L2X_WKFET_CFG 0x00006804 +#define CS40L2X_NG_CFG 0x00006808 +#define CS40L2X_AMP_GAIN_CTRL 0x00006C04 +#define CS40L2X_DAC_MSM_CFG 0x00007400 +#define CS40L2X_IRQ1_CFG 0x00010000 +#define CS40L2X_IRQ1_STATUS 0x00010004 +#define CS40L2X_IRQ1_STATUS1 0x00010010 +#define CS40L2X_IRQ1_STATUS2 0x00010014 +#define CS40L2X_IRQ1_STATUS3 0x00010018 +#define CS40L2X_IRQ1_STATUS4 0x0001001C +#define CS40L2X_IRQ1_RAW_STATUS1 0x00010090 +#define CS40L2X_IRQ1_RAW_STATUS2 0x00010094 +#define CS40L2X_IRQ1_RAW_STATUS3 0x00010098 +#define CS40L2X_IRQ1_RAW_STATUS4 0x0001009C +#define CS40L2X_IRQ1_MASK1 0x00010110 +#define CS40L2X_IRQ1_MASK2 0x00010114 +#define CS40L2X_IRQ1_MASK3 0x00010118 +#define CS40L2X_IRQ1_MASK4 0x0001011C +#define CS40L2X_IRQ1_FRC1 0x00010190 +#define CS40L2X_IRQ1_FRC2 0x00010194 +#define CS40L2X_IRQ1_FRC3 0x00010198 +#define CS40L2X_IRQ1_FRC4 0x0001019C +#define CS40L2X_IRQ1_EDGE1 0x00010210 +#define CS40L2X_IRQ1_EDGE4 0x0001021C +#define CS40L2X_IRQ1_POL1 0x00010290 +#define CS40L2X_IRQ1_POL2 0x00010294 +#define CS40L2X_IRQ1_POL3 0x00010298 +#define CS40L2X_IRQ1_POL4 0x0001029C +#define CS40L2X_IRQ1_DB3 0x00010318 +#define CS40L2X_IRQ2_CFG 0x00010800 +#define CS40L2X_IRQ2_STATUS 0x00010804 +#define CS40L2X_IRQ2_STATUS1 0x00010810 +#define CS40L2X_IRQ2_STATUS2 0x00010814 +#define CS40L2X_IRQ2_STATUS3 0x00010818 +#define CS40L2X_IRQ2_STATUS4 0x0001081C +#define CS40L2X_IRQ2_RAW_STATUS1 0x00010890 +#define CS40L2X_IRQ2_RAW_STATUS2 0x00010894 +#define CS40L2X_IRQ2_RAW_STATUS3 0x00010898 +#define CS40L2X_IRQ2_RAW_STATUS4 0x0001089C +#define CS40L2X_IRQ2_MASK1 0x00010910 +#define CS40L2X_IRQ2_MASK2 0x00010914 +#define CS40L2X_IRQ2_MASK3 0x00010918 +#define CS40L2X_IRQ2_MASK4 0x0001091C +#define CS40L2X_IRQ2_FRC1 0x00010990 +#define CS40L2X_IRQ2_FRC2 0x00010994 +#define CS40L2X_IRQ2_FRC3 0x00010998 +#define CS40L2X_IRQ2_FRC4 0x0001099C +#define CS40L2X_IRQ2_EDGE1 0x00010A10 +#define CS40L2X_IRQ2_EDGE4 0x00010A1C +#define CS40L2X_IRQ2_POL1 0x00010A90 +#define CS40L2X_IRQ2_POL2 0x00010A94 +#define CS40L2X_IRQ2_POL3 0x00010A98 +#define CS40L2X_IRQ2_POL4 0x00010A9C +#define CS40L2X_IRQ2_DB3 0x00010B18 +#define CS40L2X_GPIO_STATUS1 0x00011000 +#define CS40L2X_GPIO1_CTRL1 0x00011008 +#define CS40L2X_GPIO2_CTRL1 0x0001100C +#define CS40L2X_GPIO3_CTRL1 0x00011010 +#define CS40L2X_GPIO4_CTRL1 0x00011014 +#define CS40L2X_MIXER_NGATE_CFG 0x00012000 +#define CS40L2X_MIXER_NGATE_CH1_CFG 0x00012004 +#define CS40L2X_MIXER_NGATE_CH2_CFG 0x00012008 +#define CS40L2X_DSP_MBOX_1 0x00013000 +#define CS40L2X_DSP_MBOX_2 0x00013004 +#define CS40L2X_DSP_MBOX_3 0x00013008 +#define CS40L2X_DSP_MBOX_4 0x0001300C +#define CS40L2X_DSP_MBOX_5 0x00013010 +#define CS40L2X_DSP_MBOX_6 0x00013014 +#define CS40L2X_DSP_MBOX_7 0x00013018 +#define CS40L2X_DSP_MBOX_8 0x0001301C +#define CS40L2X_DSP_VIRT1_MBOX_1 0x00013020 +#define CS40L2X_DSP_VIRT1_MBOX_2 0x00013024 +#define CS40L2X_DSP_VIRT1_MBOX_3 0x00013028 +#define CS40L2X_DSP_VIRT1_MBOX_4 0x0001302C +#define CS40L2X_DSP_VIRT1_MBOX_5 0x00013030 +#define CS40L2X_DSP_VIRT1_MBOX_6 0x00013034 +#define CS40L2X_DSP_VIRT1_MBOX_7 0x00013038 +#define CS40L2X_DSP_VIRT1_MBOX_8 0x0001303C +#define CS40L2X_DSP_VIRT2_MBOX_1 0x00013040 +#define CS40L2X_DSP_VIRT2_MBOX_2 0x00013044 +#define CS40L2X_DSP_VIRT2_MBOX_3 0x00013048 +#define CS40L2X_DSP_VIRT2_MBOX_4 0x0001304C +#define CS40L2X_DSP_VIRT2_MBOX_5 0x00013050 +#define CS40L2X_DSP_VIRT2_MBOX_6 0x00013054 +#define CS40L2X_DSP_VIRT2_MBOX_7 0x00013058 +#define CS40L2X_DSP_VIRT2_MBOX_8 0x0001305C +#define CS40L2X_CLOCK_DETECT_1 0x00014000 +#define CS40L2X_TIMER1_CONTROL 0x00015000 +#define CS40L2X_TIMER1_COUNT_PRESET 0x00015004 +#define CS40L2X_TIMER1_START_STOP 0x0001500C +#define CS40L2X_TIMER1_STATUS 0x00015010 +#define CS40L2X_TIMER1_COUNT_READBACK 0x00015014 +#define CS40L2X_TIMER1_DSP_CLK_CFG 0x00015018 +#define CS40L2X_TIMER1_DSP_CLK_STATUS 0x0001501C +#define CS40L2X_TIMER2_CONTROL 0x00015100 +#define CS40L2X_TIMER2_COUNT_PRESET 0x00015104 +#define CS40L2X_TIMER2_START_STOP 0x0001510C +#define CS40L2X_TIMER2_STATUS 0x00015110 +#define CS40L2X_TIMER2_COUNT_READBACK 0x00015114 +#define CS40L2X_TIMER2_DSP_CLK_CFG 0x00015118 +#define CS40L2X_TIMER2_DSP_CLK_STATUS 0x0001511C +#define CS40L2X_DFT_JTAG_CONTROL 0x00016000 +#define CS40L2X_DIE_STS1 0x00017040 +#define CS40L2X_DIE_STS2 0x00017044 +#define CS40L2X_TEMP_CAL1 0x00017048 +#define CS40L2X_TEMP_CAL2 0x0001704C +#define CS40L2X_DSP1_XMEM_PACK_0 0x02000000 +#define CS40L2X_DSP1_XMEM_PACK_3068 0x02002FF0 +#define CS40L2X_DSP1_XMEM_UNPACK32_0 0x02400000 +#define CS40L2X_DSP1_XMEM_UNPACK32_2046 0x02401FF8 +#define CS40L2X_DSP1_TIMESTAMP_COUNT 0x025C0800 +#define CS40L2X_DSP1_SYS_ID 0x025E0000 +#define CS40L2X_DSP1_SYS_VERSION 0x025E0004 +#define CS40L2X_DSP1_SYS_CORE_ID 0x025E0008 +#define CS40L2X_DSP1_SYS_AHB_ADDR 0x025E000C +#define CS40L2X_DSP1_SYS_XSRAM_SIZE 0x025E0010 +#define CS40L2X_DSP1_SYS_YSRAM_SIZE 0x025E0018 +#define CS40L2X_DSP1_SYS_PSRAM_SIZE 0x025E0020 +#define CS40L2X_DSP1_SYS_PM_BOOT_SIZE 0x025E0028 +#define CS40L2X_DSP1_SYS_FEATURES 0x025E002C +#define CS40L2X_DSP1_SYS_FIR_FILTERS 0x025E0030 +#define CS40L2X_DSP1_SYS_LMS_FILTERS 0x025E0034 +#define CS40L2X_DSP1_SYS_XM_BANK_SIZE 0x025E0038 +#define CS40L2X_DSP1_SYS_YM_BANK_SIZE 0x025E003C +#define CS40L2X_DSP1_SYS_PM_BANK_SIZE 0x025E0040 +#define CS40L2X_DSP1_AHBM_WIN0_CTRL0 0x025E2000 +#define CS40L2X_DSP1_AHBM_WIN0_CTRL1 0x025E2004 +#define CS40L2X_DSP1_AHBM_WIN1_CTRL0 0x025E2008 +#define CS40L2X_DSP1_AHBM_WIN1_CTRL1 0x025E200C +#define CS40L2X_DSP1_AHBM_WIN2_CTRL0 0x025E2010 +#define CS40L2X_DSP1_AHBM_WIN2_CTRL1 0x025E2014 +#define CS40L2X_DSP1_AHBM_WIN3_CTRL0 0x025E2018 +#define CS40L2X_DSP1_AHBM_WIN3_CTRL1 0x025E201C +#define CS40L2X_DSP1_AHBM_WIN4_CTRL0 0x025E2020 +#define CS40L2X_DSP1_AHBM_WIN4_CTRL1 0x025E2024 +#define CS40L2X_DSP1_AHBM_WIN5_CTRL0 0x025E2028 +#define CS40L2X_DSP1_AHBM_WIN5_CTRL1 0x025E202C +#define CS40L2X_DSP1_AHBM_WIN6_CTRL0 0x025E2030 +#define CS40L2X_DSP1_AHBM_WIN6_CTRL1 0x025E2034 +#define CS40L2X_DSP1_AHBM_WIN7_CTRL0 0x025E2038 +#define CS40L2X_DSP1_AHBM_WIN7_CTRL1 0x025E203C +#define CS40L2X_DSP1_AHBM_WIN_DBG_CTRL0 0x025E2040 +#define CS40L2X_DSP1_AHBM_WIN_DBG_CTRL1 0x025E2044 +#define CS40L2X_DSP1_XMEM_UNPACK24_0 0x02800000 +#define CS40L2X_DSP1_XMEM_UNPACK24_4093 0x02803FF4 +#define CS40L2X_DSP1_CTRL_BASE 0x02B80000 +#define CS40L2X_DSP1_CORE_SOFT_RESET 0x02B80010 +#define CS40L2X_DSP1_DEBUG 0x02B80040 +#define CS40L2X_DSP1_TIMER_CTRL 0x02B80048 +#define CS40L2X_DSP1_STREAM_ARB_CTRL 0x02B80050 +#define CS40L2X_DSP1_RX1_RATE 0x02B80080 +#define CS40L2X_DSP1_RX2_RATE 0x02B80088 +#define CS40L2X_DSP1_RX3_RATE 0x02B80090 +#define CS40L2X_DSP1_RX4_RATE 0x02B80098 +#define CS40L2X_DSP1_RX5_RATE 0x02B800A0 +#define CS40L2X_DSP1_RX6_RATE 0x02B800A8 +#define CS40L2X_DSP1_RX7_RATE 0x02B800B0 +#define CS40L2X_DSP1_RX8_RATE 0x02B800B8 +#define CS40L2X_DSP1_TX1_RATE 0x02B80280 +#define CS40L2X_DSP1_TX2_RATE 0x02B80288 +#define CS40L2X_DSP1_TX3_RATE 0x02B80290 +#define CS40L2X_DSP1_TX4_RATE 0x02B80298 +#define CS40L2X_DSP1_TX5_RATE 0x02B802A0 +#define CS40L2X_DSP1_TX6_RATE 0x02B802A8 +#define CS40L2X_DSP1_TX7_RATE 0x02B802B0 +#define CS40L2X_DSP1_TX8_RATE 0x02B802B8 +#define CS40L2X_DSP1_NMI_CTRL1 0x02B80480 +#define CS40L2X_DSP1_NMI_CTRL2 0x02B80488 +#define CS40L2X_DSP1_NMI_CTRL3 0x02B80490 +#define CS40L2X_DSP1_NMI_CTRL4 0x02B80498 +#define CS40L2X_DSP1_NMI_CTRL5 0x02B804A0 +#define CS40L2X_DSP1_NMI_CTRL6 0x02B804A8 +#define CS40L2X_DSP1_NMI_CTRL7 0x02B804B0 +#define CS40L2X_DSP1_NMI_CTRL8 0x02B804B8 +#define CS40L2X_DSP1_RESUME_CTRL 0x02B80500 +#define CS40L2X_DSP1_IRQ1_CTRL 0x02B80508 +#define CS40L2X_DSP1_IRQ2_CTRL 0x02B80510 +#define CS40L2X_DSP1_IRQ3_CTRL 0x02B80518 +#define CS40L2X_DSP1_IRQ4_CTRL 0x02B80520 +#define CS40L2X_DSP1_IRQ5_CTRL 0x02B80528 +#define CS40L2X_DSP1_IRQ6_CTRL 0x02B80530 +#define CS40L2X_DSP1_IRQ7_CTRL 0x02B80538 +#define CS40L2X_DSP1_IRQ8_CTRL 0x02B80540 +#define CS40L2X_DSP1_IRQ9_CTRL 0x02B80548 +#define CS40L2X_DSP1_IRQ10_CTRL 0x02B80550 +#define CS40L2X_DSP1_IRQ11_CTRL 0x02B80558 +#define CS40L2X_DSP1_IRQ12_CTRL 0x02B80560 +#define CS40L2X_DSP1_IRQ13_CTRL 0x02B80568 +#define CS40L2X_DSP1_IRQ14_CTRL 0x02B80570 +#define CS40L2X_DSP1_IRQ15_CTRL 0x02B80578 +#define CS40L2X_DSP1_IRQ16_CTRL 0x02B80580 +#define CS40L2X_DSP1_IRQ17_CTRL 0x02B80588 +#define CS40L2X_DSP1_IRQ18_CTRL 0x02B80590 +#define CS40L2X_DSP1_IRQ19_CTRL 0x02B80598 +#define CS40L2X_DSP1_IRQ20_CTRL 0x02B805A0 +#define CS40L2X_DSP1_IRQ21_CTRL 0x02B805A8 +#define CS40L2X_DSP1_IRQ22_CTRL 0x02B805B0 +#define CS40L2X_DSP1_IRQ23_CTRL 0x02B805B8 +#define CS40L2X_DSP1_SCRATCH1 0x02B805C0 +#define CS40L2X_DSP1_SCRATCH2 0x02B805C8 +#define CS40L2X_DSP1_SCRATCH3 0x02B805D0 +#define CS40L2X_DSP1_SCRATCH4 0x02B805D8 +#define CS40L2X_DSP1_CCM_CORE_CTRL 0x02BC1000 +#define CS40L2X_DSP1_CCM_CLK_OVERRIDE 0x02BC1008 +#define CS40L2X_DSP1_XM_MSTR_EN 0x02BC2000 +#define CS40L2X_DSP1_XM_CORE_PRI 0x02BC2008 +#define CS40L2X_DSP1_XM_AHB_PACK_PL_PRI 0x02BC2010 +#define CS40L2X_DSP1_XM_AHB_UP_PL_PRI 0x02BC2018 +#define CS40L2X_DSP1_XM_ACCEL_PL0_PRI 0x02BC2020 +#define CS40L2X_DSP1_XM_NPL0_PRI 0x02BC2078 +#define CS40L2X_DSP1_YM_MSTR_EN 0x02BC20C0 +#define CS40L2X_DSP1_YM_CORE_PRI 0x02BC20C8 +#define CS40L2X_DSP1_YM_AHB_PACK_PL_PRI 0x02BC20D0 +#define CS40L2X_DSP1_YM_AHB_UP_PL_PRI 0x02BC20D8 +#define CS40L2X_DSP1_YM_ACCEL_PL0_PRI 0x02BC20E0 +#define CS40L2X_DSP1_YM_NPL0_PRI 0x02BC2138 +#define CS40L2X_DSP1_PM_MSTR_EN 0x02BC2180 +#define CS40L2X_DSP1_PM_PATCH0_ADDR 0x02BC2188 +#define CS40L2X_DSP1_PM_PATCH0_EN 0x02BC218C +#define CS40L2X_DSP1_PM_PATCH0_DATA_LO 0x02BC2190 +#define CS40L2X_DSP1_PM_PATCH0_DATA_HI 0x02BC2194 +#define CS40L2X_DSP1_PM_PATCH1_ADDR 0x02BC2198 +#define CS40L2X_DSP1_PM_PATCH1_EN 0x02BC219C +#define CS40L2X_DSP1_PM_PATCH1_DATA_LO 0x02BC21A0 +#define CS40L2X_DSP1_PM_PATCH1_DATA_HI 0x02BC21A4 +#define CS40L2X_DSP1_PM_PATCH2_ADDR 0x02BC21A8 +#define CS40L2X_DSP1_PM_PATCH2_EN 0x02BC21AC +#define CS40L2X_DSP1_PM_PATCH2_DATA_LO 0x02BC21B0 +#define CS40L2X_DSP1_PM_PATCH2_DATA_HI 0x02BC21B4 +#define CS40L2X_DSP1_PM_PATCH3_ADDR 0x02BC21B8 +#define CS40L2X_DSP1_PM_PATCH3_EN 0x02BC21BC +#define CS40L2X_DSP1_PM_PATCH3_DATA_LO 0x02BC21C0 +#define CS40L2X_DSP1_PM_PATCH3_DATA_HI 0x02BC21C4 +#define CS40L2X_DSP1_PM_PATCH4_ADDR 0x02BC21C8 +#define CS40L2X_DSP1_PM_PATCH4_EN 0x02BC21CC +#define CS40L2X_DSP1_PM_PATCH4_DATA_LO 0x02BC21D0 +#define CS40L2X_DSP1_PM_PATCH4_DATA_HI 0x02BC21D4 +#define CS40L2X_DSP1_PM_PATCH5_ADDR 0x02BC21D8 +#define CS40L2X_DSP1_PM_PATCH5_EN 0x02BC21DC +#define CS40L2X_DSP1_PM_PATCH5_DATA_LO 0x02BC21E0 +#define CS40L2X_DSP1_PM_PATCH5_DATA_HI 0x02BC21E4 +#define CS40L2X_DSP1_PM_PATCH6_ADDR 0x02BC21E8 +#define CS40L2X_DSP1_PM_PATCH6_EN 0x02BC21EC +#define CS40L2X_DSP1_PM_PATCH6_DATA_LO 0x02BC21F0 +#define CS40L2X_DSP1_PM_PATCH6_DATA_HI 0x02BC21F4 +#define CS40L2X_DSP1_PM_PATCH7_ADDR 0x02BC21F8 +#define CS40L2X_DSP1_PM_PATCH7_EN 0x02BC21FC +#define CS40L2X_DSP1_PM_PATCH7_DATA_LO 0x02BC2200 +#define CS40L2X_DSP1_PM_PATCH7_DATA_HI 0x02BC2204 +#define CS40L2X_DSP1_MPU_XM_ACCESS0 0x02BC3000 +#define CS40L2X_DSP1_MPU_YM_ACCESS0 0x02BC3004 +#define CS40L2X_DSP1_MPU_WNDW_ACCESS0 0x02BC3008 +#define CS40L2X_DSP1_MPU_XREG_ACCESS0 0x02BC300C +#define CS40L2X_DSP1_MPU_YREG_ACCESS0 0x02BC3014 +#define CS40L2X_DSP1_MPU_XM_ACCESS1 0x02BC3018 +#define CS40L2X_DSP1_MPU_YM_ACCESS1 0x02BC301C +#define CS40L2X_DSP1_MPU_WNDW_ACCESS1 0x02BC3020 +#define CS40L2X_DSP1_MPU_XREG_ACCESS1 0x02BC3024 +#define CS40L2X_DSP1_MPU_YREG_ACCESS1 0x02BC302C +#define CS40L2X_DSP1_MPU_XM_ACCESS2 0x02BC3030 +#define CS40L2X_DSP1_MPU_YM_ACCESS2 0x02BC3034 +#define CS40L2X_DSP1_MPU_WNDW_ACCESS2 0x02BC3038 +#define CS40L2X_DSP1_MPU_XREG_ACCESS2 0x02BC303C +#define CS40L2X_DSP1_MPU_YREG_ACCESS2 0x02BC3044 +#define CS40L2X_DSP1_MPU_XM_ACCESS3 0x02BC3048 +#define CS40L2X_DSP1_MPU_YM_ACCESS3 0x02BC304C +#define CS40L2X_DSP1_MPU_WNDW_ACCESS3 0x02BC3050 +#define CS40L2X_DSP1_MPU_XREG_ACCESS3 0x02BC3054 +#define CS40L2X_DSP1_MPU_YREG_ACCESS3 0x02BC305C +#define CS40L2X_DSP1_MPU_XM_VIO_ADDR 0x02BC3100 +#define CS40L2X_DSP1_MPU_XM_VIO_STATUS 0x02BC3104 +#define CS40L2X_DSP1_MPU_YM_VIO_ADDR 0x02BC3108 +#define CS40L2X_DSP1_MPU_YM_VIO_STATUS 0x02BC310C +#define CS40L2X_DSP1_MPU_PM_VIO_ADDR 0x02BC3110 +#define CS40L2X_DSP1_MPU_PM_VIO_STATUS 0x02BC3114 +#define CS40L2X_DSP1_MPU_LOCK_CONFIG 0x02BC3140 +#define CS40L2X_DSP1_MPU_WDT_RST_CTRL 0x02BC3180 +#define CS40L2X_DSP1_STRMARB_MSTR0_CFG0 0x02BC5000 +#define CS40L2X_DSP1_STRMARB_MSTR0_CFG1 0x02BC5004 +#define CS40L2X_DSP1_STRMARB_MSTR0_CFG2 0x02BC5008 +#define CS40L2X_DSP1_STRMARB_MSTR1_CFG0 0x02BC5010 +#define CS40L2X_DSP1_STRMARB_MSTR1_CFG1 0x02BC5014 +#define CS40L2X_DSP1_STRMARB_MSTR1_CFG2 0x02BC5018 +#define CS40L2X_DSP1_STRMARB_MSTR2_CFG0 0x02BC5020 +#define CS40L2X_DSP1_STRMARB_MSTR2_CFG1 0x02BC5024 +#define CS40L2X_DSP1_STRMARB_MSTR2_CFG2 0x02BC5028 +#define CS40L2X_DSP1_STRMARB_MSTR3_CFG0 0x02BC5030 +#define CS40L2X_DSP1_STRMARB_MSTR3_CFG1 0x02BC5034 +#define CS40L2X_DSP1_STRMARB_MSTR3_CFG2 0x02BC5038 +#define CS40L2X_DSP1_STRMARB_MSTR4_CFG0 0x02BC5040 +#define CS40L2X_DSP1_STRMARB_MSTR4_CFG1 0x02BC5044 +#define CS40L2X_DSP1_STRMARB_MSTR4_CFG2 0x02BC5048 +#define CS40L2X_DSP1_STRMARB_MSTR5_CFG0 0x02BC5050 +#define CS40L2X_DSP1_STRMARB_MSTR5_CFG1 0x02BC5054 +#define CS40L2X_DSP1_STRMARB_MSTR5_CFG2 0x02BC5058 +#define CS40L2X_DSP1_STRMARB_MSTR6_CFG0 0x02BC5060 +#define CS40L2X_DSP1_STRMARB_MSTR6_CFG1 0x02BC5064 +#define CS40L2X_DSP1_STRMARB_MSTR6_CFG2 0x02BC5068 +#define CS40L2X_DSP1_STRMARB_MSTR7_CFG0 0x02BC5070 +#define CS40L2X_DSP1_STRMARB_MSTR7_CFG1 0x02BC5074 +#define CS40L2X_DSP1_STRMARB_MSTR7_CFG2 0x02BC5078 +#define CS40L2X_DSP1_STRMARB_TX0_CFG0 0x02BC5200 +#define CS40L2X_DSP1_STRMARB_TX0_CFG1 0x02BC5204 +#define CS40L2X_DSP1_STRMARB_TX1_CFG0 0x02BC5208 +#define CS40L2X_DSP1_STRMARB_TX1_CFG1 0x02BC520C +#define CS40L2X_DSP1_STRMARB_TX2_CFG0 0x02BC5210 +#define CS40L2X_DSP1_STRMARB_TX2_CFG1 0x02BC5214 +#define CS40L2X_DSP1_STRMARB_TX3_CFG0 0x02BC5218 +#define CS40L2X_DSP1_STRMARB_TX3_CFG1 0x02BC521C +#define CS40L2X_DSP1_STRMARB_TX4_CFG0 0x02BC5220 +#define CS40L2X_DSP1_STRMARB_TX4_CFG1 0x02BC5224 +#define CS40L2X_DSP1_STRMARB_TX5_CFG0 0x02BC5228 +#define CS40L2X_DSP1_STRMARB_TX5_CFG1 0x02BC522C +#define CS40L2X_DSP1_STRMARB_TX6_CFG0 0x02BC5230 +#define CS40L2X_DSP1_STRMARB_TX6_CFG1 0x02BC5234 +#define CS40L2X_DSP1_STRMARB_TX7_CFG0 0x02BC5238 +#define CS40L2X_DSP1_STRMARB_TX7_CFG1 0x02BC523C +#define CS40L2X_DSP1_STRMARB_RX0_CFG0 0x02BC5400 +#define CS40L2X_DSP1_STRMARB_RX0_CFG1 0x02BC5404 +#define CS40L2X_DSP1_STRMARB_RX1_CFG0 0x02BC5408 +#define CS40L2X_DSP1_STRMARB_RX1_CFG1 0x02BC540C +#define CS40L2X_DSP1_STRMARB_RX2_CFG0 0x02BC5410 +#define CS40L2X_DSP1_STRMARB_RX2_CFG1 0x02BC5414 +#define CS40L2X_DSP1_STRMARB_RX3_CFG0 0x02BC5418 +#define CS40L2X_DSP1_STRMARB_RX3_CFG1 0x02BC541C +#define CS40L2X_DSP1_STRMARB_RX4_CFG0 0x02BC5420 +#define CS40L2X_DSP1_STRMARB_RX4_CFG1 0x02BC5424 +#define CS40L2X_DSP1_STRMARB_RX5_CFG0 0x02BC5428 +#define CS40L2X_DSP1_STRMARB_RX5_CFG1 0x02BC542C +#define CS40L2X_DSP1_STRMARB_RX6_CFG0 0x02BC5430 +#define CS40L2X_DSP1_STRMARB_RX6_CFG1 0x02BC5434 +#define CS40L2X_DSP1_STRMARB_RX7_CFG0 0x02BC5438 +#define CS40L2X_DSP1_STRMARB_RX7_CFG1 0x02BC543C +#define CS40L2X_DSP1_STRMARB_IRQ0_CFG0 0x02BC5600 +#define CS40L2X_DSP1_STRMARB_IRQ0_CFG1 0x02BC5604 +#define CS40L2X_DSP1_STRMARB_IRQ0_CFG2 0x02BC5608 +#define CS40L2X_DSP1_STRMARB_IRQ1_CFG0 0x02BC5610 +#define CS40L2X_DSP1_STRMARB_IRQ1_CFG1 0x02BC5614 +#define CS40L2X_DSP1_STRMARB_IRQ1_CFG2 0x02BC5618 +#define CS40L2X_DSP1_STRMARB_IRQ2_CFG0 0x02BC5620 +#define CS40L2X_DSP1_STRMARB_IRQ2_CFG1 0x02BC5624 +#define CS40L2X_DSP1_STRMARB_IRQ2_CFG2 0x02BC5628 +#define CS40L2X_DSP1_STRMARB_IRQ3_CFG0 0x02BC5630 +#define CS40L2X_DSP1_STRMARB_IRQ3_CFG1 0x02BC5634 +#define CS40L2X_DSP1_STRMARB_IRQ3_CFG2 0x02BC5638 +#define CS40L2X_DSP1_STRMARB_IRQ4_CFG0 0x02BC5640 +#define CS40L2X_DSP1_STRMARB_IRQ4_CFG1 0x02BC5644 +#define CS40L2X_DSP1_STRMARB_IRQ4_CFG2 0x02BC5648 +#define CS40L2X_DSP1_STRMARB_IRQ5_CFG0 0x02BC5650 +#define CS40L2X_DSP1_STRMARB_IRQ5_CFG1 0x02BC5654 +#define CS40L2X_DSP1_STRMARB_IRQ5_CFG2 0x02BC5658 +#define CS40L2X_DSP1_STRMARB_IRQ6_CFG0 0x02BC5660 +#define CS40L2X_DSP1_STRMARB_IRQ6_CFG1 0x02BC5664 +#define CS40L2X_DSP1_STRMARB_IRQ6_CFG2 0x02BC5668 +#define CS40L2X_DSP1_STRMARB_IRQ7_CFG0 0x02BC5670 +#define CS40L2X_DSP1_STRMARB_IRQ7_CFG1 0x02BC5674 +#define CS40L2X_DSP1_STRMARB_IRQ7_CFG2 0x02BC5678 +#define CS40L2X_DSP1_STRMARB_RESYNC_MSK 0x02BC5A00 +#define CS40L2X_DSP1_STRMARB_ERR_STATUS 0x02BC5A08 +#define CS40L2X_DSP1_INTPCTL_RES_STATIC 0x02BC6000 +#define CS40L2X_DSP1_INTPCTL_RES_DYN 0x02BC6004 +#define CS40L2X_DSP1_INTPCTL_NMI_CTRL 0x02BC6008 +#define CS40L2X_DSP1_INTPCTL_IRQ_INV 0x02BC6010 +#define CS40L2X_DSP1_INTPCTL_IRQ_MODE 0x02BC6014 +#define CS40L2X_DSP1_INTPCTL_IRQ_EN 0x02BC6018 +#define CS40L2X_DSP1_INTPCTL_IRQ_MSK 0x02BC601C +#define CS40L2X_DSP1_INTPCTL_IRQ_FLUSH 0x02BC6020 +#define CS40L2X_DSP1_INTPCTL_IRQ_MSKCLR 0x02BC6024 +#define CS40L2X_DSP1_INTPCTL_IRQ_FRC 0x02BC6028 +#define CS40L2X_DSP1_INTPCTL_IRQ_MSKSET 0x02BC602C +#define CS40L2X_DSP1_INTPCTL_IRQ_ERR 0x02BC6030 +#define CS40L2X_DSP1_INTPCTL_IRQ_PEND 0x02BC6034 +#define CS40L2X_DSP1_INTPCTL_IRQ_GEN 0x02BC6038 +#define CS40L2X_DSP1_INTPCTL_TESTBITS 0x02BC6040 +#define CS40L2X_DSP1_WDT_CONTROL 0x02BC7000 +#define CS40L2X_DSP1_WDT_STATUS 0x02BC7008 +#define CS40L2X_DSP1_YMEM_PACK_0 0x02C00000 +#define CS40L2X_DSP1_YMEM_PACK_1532 0x02C017F0 +#define CS40L2X_DSP1_YMEM_UNPACK32_0 0x03000000 +#define CS40L2X_DSP1_YMEM_UNPACK32_1022 0x03000FF8 +#define CS40L2X_DSP1_YMEM_UNPACK24_0 0x03400000 +#define CS40L2X_DSP1_YMEM_UNPACK24_2045 0x03401FF4 +#define CS40L2X_DSP1_PMEM_0 0x03800000 +#define CS40L2X_DSP1_PMEM_5114 0x03804FE8 + +/* trim fields unpacked from OTP memory */ +#define CS40L2X_OTP_TRIM_1 0x0000208C +#define CS40L2X_OTP_TRIM_2 0x00002090 +#define CS40L2X_OTP_TRIM_3 0x00003010 +#define CS40L2X_OTP_TRIM_4 0x0000300C +#define CS40L2X_OTP_TRIM_5 0x0000394C +#define CS40L2X_OTP_TRIM_6 0x00003950 +#define CS40L2X_OTP_TRIM_7 0x00003954 +#define CS40L2X_OTP_TRIM_8 0x00003958 +#define CS40L2X_OTP_TRIM_9 0x0000395C +#define CS40L2X_OTP_TRIM_10 0x0000416C +#define CS40L2X_OTP_TRIM_11 0x00004160 +#define CS40L2X_OTP_TRIM_12 0x00004170 +#define CS40L2X_OTP_TRIM_13 0x00004360 +#define CS40L2X_OTP_TRIM_14 0x00004448 +#define CS40L2X_OTP_TRIM_15 0x0000444C +#define CS40L2X_OTP_TRIM_16 0x00006E30 +#define CS40L2X_OTP_TRIM_17 0x00006E34 +#define CS40L2X_OTP_TRIM_18 0x00006E38 +#define CS40L2X_OTP_TRIM_19 0x00006E3C +#define CS40L2X_OTP_TRIM_20 0x00006E40 +#define CS40L2X_OTP_TRIM_21 0x00006E44 +#define CS40L2X_OTP_TRIM_22 0x00006E48 +#define CS40L2X_OTP_TRIM_23 0x00006E4C +#define CS40L2X_OTP_TRIM_24 0x00006E50 +#define CS40L2X_OTP_TRIM_25 0x00006E54 +#define CS40L2X_OTP_TRIM_26 0x00006E58 +#define CS40L2X_OTP_TRIM_27 0x00006E5C +#define CS40L2X_OTP_TRIM_28 0x00006E60 +#define CS40L2X_OTP_TRIM_29 0x00006E64 +#define CS40L2X_OTP_TRIM_30 0x00007418 +#define CS40L2X_OTP_TRIM_31 0x0000741C +#define CS40L2X_OTP_TRIM_32 0x00007434 +#define CS40L2X_OTP_TRIM_33 0x00007068 +#define CS40L2X_OTP_TRIM_34 0x0000410C +#define CS40L2X_OTP_TRIM_35 0x0000400C +#define CS40L2X_OTP_TRIM_36 0x00002030 + +/* basic mode (ROM) fields */ +#define CS40L2X_BASIC_HALO_STATE 0x02800154 +#define CS40L2X_BASIC_HALO_HEARTBEAT 0x02800158 +#define CS40L2X_BASIC_STATEMACHINE 0X0280015C +#define CS40L2X_BASIC_F0_STORED 0X02800174 +#define CS40L2X_BASIC_GPIO_TRIGGER_EDGE 0x02800178 +#define CS40L2X_BASIC_BUZZ_FREQ 0x0280017C +#define CS40L2X_BASIC_BUZZ_LEVEL 0x02800180 +#define CS40L2X_BASIC_BUZZ_DURATION 0x02800184 +#define CS40L2X_BASIC_BUZZ_TRIGGER 0x02800188 +#define CS40L2X_BASIC_AMP_STATUS 0x0280018C +#define CS40L2X_BASIC_SHUTDOWNREQUEST 0x02800190 + +/* basic mode (ROM) states */ +#define CS40L2X_BASIC_IDLE 0 +#define CS40L2X_BASIC_PLAY_TONE 1 +#define CS40L2X_BASIC_CONTINUE_TONE 2 +#define CS40L2X_BASIC_SHUTTINGDOWN 3 +#define CS40L2X_BASIC_SHUTDOWN 4 + +/* basic mode (ROM) status flags */ +#define CS40L2X_BASIC_NO_STATUS 0x00 +#define CS40L2X_BASIC_BOOT_DONE 0x01 +#define CS40L2X_BASIC_OTP_ERROR 0x02 +#define CS40L2X_BASIC_AMP_ERROR 0x04 +#define CS40L2X_BASIC_TEMP_RISE_WARN 0x08 +#define CS40L2X_BASIC_TEMP_ERROR 0x10 +#define CS40L2X_BASIC_SA_ERROR 0x20 + +#define CS40L2X_GLOBAL_EN_MASK 0x00000001 +#define CS40L2X_GLOBAL_EN_SHIFT 0 + +#define CS40L2X_GP2_CTRL_MASK 0x07000000 +#define CS40L2X_GP2_CTRL_SHIFT 24 +#define CS40L2X_GP2_CTRL_MCLK 0x3 + +#define CS40L2X_MEM_RDY_MASK 0x00000002 +#define CS40L2X_MEM_RDY_SHIFT 1 + +#define CS40L2X_PLL_FORCE_EN_MASK 0x00010000 +#define CS40L2X_PLL_FORCE_EN_SHIFT 16 + +#define CS40L2X_PLL_REFCLK_SEL_MASK 0x00000007 +#define CS40L2X_PLL_REFCLK_SEL_SHIFT 0 +#define CS40L2X_PLL_REFCLK_SEL_MCLK 0x5 + +#define CS40L2X_DAC_PCM1_SRC_MASK 0x7F +#define CS40L2X_DAC_PCM1_SRC_SHIFT 0 +#define CS40L2X_DAC_PCM1_SRC_DSP1TX1 0x32 + +#define CS40L2X_DSP1_RXn_SRC_MASK 0x7F +#define CS40L2X_DSP1_RXn_SRC_SHIFT 0 +#define CS40L2X_DSP1_RXn_SRC_VMON 0x18 +#define CS40L2X_DSP1_RXn_SRC_IMON 0x19 +#define CS40L2X_DSP1_RXn_SRC_VPMON 0x28 + +#define CS40L2X_DSP1_RESET_MASK 0x200 +#define CS40L2X_DSP1_RESET_SHIFT 9 + +#define CS40L2X_DSP1_EN_MASK 0x1 +#define CS40L2X_DSP1_EN_SHIFT 0 + +#define CS40L2X_BST_IPK_MASK 0x0000007F +#define CS40L2X_BST_IPK_SHIFT 0 + +#define CS40L2X_BST_K1_MASK 0x000000FF +#define CS40L2X_BST_K1_SHIFT 0 +#define CS40L2X_BST_K2_MASK 0x0000FF00 +#define CS40L2X_BST_K2_SHIFT 8 + +#define CS40L2X_BST_SLOPE_MASK 0x0000FF00 +#define CS40L2X_BST_SLOPE_SHIFT 8 + +#define CS40L2X_BST_LBST_VAL_MASK 0x00000003 +#define CS40L2X_BST_LBST_VAL_SHIFT 0 + +#define CS40L2X_AMP_VOL_PCM_MASK 0x00003FF8 +#define CS40L2X_AMP_VOL_PCM_SHIFT 3 + +#define CS40L2X_GAIN_CTRL_GPIO_MASK 0x00FFC000 +#define CS40L2X_GAIN_CTRL_GPIO_SHIFT 14 + +#define CS40L2X_GAIN_CTRL_TRIG_MASK 0x00003FF0 +#define CS40L2X_GAIN_CTRL_TRIG_SHIFT 4 + +#define CS40L2X_OTP_BOOT_ERR 0x80000000 +#define CS40L2X_OTP_BOOT_DONE 0x00000002 + +#define CS40L2X_DEVID_L20 0x035A40 +#define CS40L2X_DEVID_L25 0x40A250 +#define CS40L2X_DEVID_L25A 0x40A25A +#define CS40L2X_DEVID_L25B 0x40A25B + +#define CS40L2X_REVID_A0 0xA0 +#define CS40L2X_REVID_B0 0xB0 +#define CS40L2X_REVID_B1 0xB1 + +#define CS40L2X_MPU_UNLOCK_CODE1 0x5555 +#define CS40L2X_MPU_UNLOCK_CODE2 0xAAAA + +#define CS40L2X_XM_UNPACKED_TYPE 0x05 +#define CS40L2X_YM_UNPACKED_TYPE 0x06 +#define CS40L2X_PM_PACKED_TYPE 0x10 +#define CS40L2X_XM_PACKED_TYPE 0x11 +#define CS40L2X_YM_PACKED_TYPE 0x12 +#define CS40L2X_ALGO_INFO_TYPE 0xF2 + +#define CS40L2X_FW_FILE_HEADER_SIZE 40 +#define CS40L2X_FW_DBLK_OFFSET_SIZE 3 +#define CS40L2X_FW_DBLK_TYPE_SIZE 1 +#define CS40L2X_FW_DBLK_LENGTH_SIZE 4 + +#define CS40L2X_WT_FILE_HEADER_SIZE 16 +#define CS40L2X_WT_DBLK_OFFSET_SIZE 2 +#define CS40L2X_WT_DBLK_TYPE_SIZE 2 +#define CS40L2X_WT_ALGO_ID_SIZE 4 +#define CS40L2X_WT_ALGO_REV_SIZE 4 +#define CS40L2X_WT_SAMPLE_RATE_SIZE 4 +#define CS40L2X_WT_DBLK_LENGTH_SIZE 4 + +#define CS40L2X_XM_FW_ID (CS40L2X_DSP1_XMEM_UNPACK24_0 + 12) +#define CS40L2X_XM_NUM_ALGOS (CS40L2X_DSP1_XMEM_UNPACK24_0 + 36) +#define CS40L2X_ALGO_ID_OFFSET 0 +#define CS40L2X_ALGO_REV_OFFSET 4 +#define CS40L2X_ALGO_XM_BASE_OFFSET 8 +#define CS40L2X_ALGO_XM_SIZE_OFFSET 12 +#define CS40L2X_ALGO_YM_BASE_OFFSET 16 +#define CS40L2X_ALGO_YM_SIZE_OFFSET 20 +#define CS40L2X_ALGO_ENTRY_SIZE 24 +#define CS40L2X_ALGO_LIST_TERM 0xBEDEAD +#define CS40L2X_ALGO_REV_MASK 0xFF0000 + +#define CS40L2X_NUM_ALGOS_MAX 16 +#define CS40L2X_FW_REV_MIN 0x040200 + +#define CS40L2X_ALGO_ID_SIZE 4 +#define CS40L2X_COEFF_COUNT_SIZE 4 +#define CS40L2X_COEFF_OFFSET_SIZE 2 +#define CS40L2X_COEFF_TYPE_SIZE 2 +#define CS40L2X_COEFF_LENGTH_SIZE 4 + +#define CS40L2X_TIMEOUT_MS_MAX 0x02AAAA /* ~174 sec */ +#define CS40L2X_PR_TIMEOUT_MAX 0x7FFFFF +#define CS40L2X_EVENT_TIMEOUT_MAX CS40L2X_PR_TIMEOUT_MAX + +#define CS40L2X_HALO_STATE_RUNNING 0x2 +#define CS40L2X_STATUS_IDLE 0xFFFFFF + +#define CS40L2X_MBOX_TRIGGERINDEX CS40L2X_DSP_VIRT1_MBOX_1 +#define CS40L2X_MBOX_TRIGGER_MS CS40L2X_DSP_VIRT1_MBOX_2 +#define CS40L2X_MBOX_STIMULUS_MODE CS40L2X_DSP_VIRT1_MBOX_3 + +#define CS40L2X_INDEX_MASK 0x7FFF +#define CS40L2X_INDEX_VIBE 0x0000 +#define CS40L2X_INDEX_CLICK_MIN 0x0001 +#define CS40L2X_INDEX_CLICK_MAX 0x7FFF +#define CS40L2X_INDEX_CONT_MIN 0x8000 +#define CS40L2X_INDEX_CONT_MAX 0xFFFD +#define CS40L2X_INDEX_PBQ 0xFFFE +#define CS40L2X_INDEX_DIAG 0xFFFF + +#define CS40L2X_PBQ_SEG_LEN_MAX 10 +#define CS40L2X_PBQ_DEPTH_MAX 256 +#define CS40L2X_PBQ_SCALE_MAX 100 +#define CS40L2X_PBQ_DELAY_MAX 10000 +#define CS40L2X_PBQ_REPEAT_MAX 16 +#define CS40L2X_PBQ_POLL_NS 5000000 +#define CS40L2X_PBQ_STATE_IDLE 0x00 +#define CS40L2X_PBQ_STATE_PLAYING 0x01 +#define CS40L2X_PBQ_STATE_SILENT 0x02 +#define CS40L2X_PBQ_TAG_SILENCE 0x0000 +#define CS40L2X_PBQ_TAG_START 0x8000 +#define CS40L2X_PBQ_TAG_STOP 0x8001 + +#define CS40L2X_DIAG_STATE_INIT 0x00 +#define CS40L2X_DIAG_STATE_RUN 0x01 +#define CS40L2X_DIAG_STATE_DONE 0x02 +#define CS40L2X_DIAG_STATE_DELAY_MS 500 + +#define CS40L2X_DSP_TIMEOUT_COUNT 10 /* 1 count = 10 ms */ +#define CS40L2X_OTP_TIMEOUT_COUNT 10 +#define CS40L2X_BASIC_TIMEOUT_COUNT 10 + +#define CS40L2X_NUM_OTP_MAPS 3 +#define CS40L2X_NUM_OTP_WORDS 32 + +#define CS40L2X_TEST_KEY_UNLOCK_CODE1 0x55 +#define CS40L2X_TEST_KEY_UNLOCK_CODE2 0xAA +#define CS40L2X_TEST_KEY_RELOCK_CODE1 0xCC +#define CS40L2X_TEST_KEY_RELOCK_CODE2 0x33 + +#define CS40L2X_DIG_SCALE_MAX 816 /* -102 dB */ +#define CS40L2X_DIG_SCALE_ZERO 0x800 +#define CS40L2X_DIG_SCALE_MASK 0x7FF +#define CS40L2X_DIG_SCALE_RESET 0x80000000 + +#define CS40L2X_MAX_WLEN 4096 + +#define CS40L2X_DEVICE_NAME "vibrator" + +#define CS40L2X_FW_NAME_A0 "cs40l20.wmfw" +#define CS40L2X_FW_NAME_B1 "cs40l25a.wmfw" + +#define CS40L2X_WT_NAME "cs40l20.bin" + +#define CS40L2X_PDATA_PRESENT 0x80000000 +#define CS40L2X_PDATA_MASK ~CS40L2X_PDATA_PRESENT + +#define CS40L2X_GPIO1_MODE_DEF_ON 0 +#define CS40L2X_GPIO1_MODE_DEF_OFF 1 +#define CS40L2X_GPIO1_MODE_AUTO 2 +#define CS40L2X_GPIO1_MODE_MAX CS40L2X_GPIO1_MODE_AUTO + +#define CS40L2X_GPIO1_ENABLED 1 +#define CS40L2X_GPIO1_DISABLED 0 + +#define CS40L2X_COMP_ENABLED 1 +#define CS40L2X_COMP_DISABLED 0 + +#define CS40L2X_ENDPLAYBACK_RETRIES 10 +#define CS40L2X_ENDPLAYBACK_REQ 1 +#define CS40L2X_ENDPLAYBACK_ACK 0 + +bool cs40l2x_readable_reg(struct device *dev, unsigned int reg); +bool cs40l2x_precious_reg(struct device *dev, unsigned int reg); + +struct cs40l2x_trim { + unsigned int reg; + unsigned char shift; + unsigned char size; +}; + +struct cs40l2x_otp_desc { + unsigned int id; + unsigned char row_start; + unsigned char col_start; + unsigned int num_trims; + const struct cs40l2x_trim *trim_table; +}; + +struct cs40l2x_coeff_desc { + unsigned int parent_id; + unsigned int block_offset; + unsigned int block_type; + unsigned char name[32]; + unsigned int reg; + struct list_head list; +}; + +struct cs40l2x_pbq_pair { + unsigned int tag; + unsigned int mag; + unsigned int repeat; + unsigned int remain; +}; + +extern const unsigned char cs40l2x_bst_k1_table[4][5]; +extern const unsigned char cs40l2x_bst_k2_table[4][5]; +extern const unsigned char cs40l2x_bst_slope_table[4]; + +extern const struct cs40l2x_otp_desc cs40l2x_otp_map[CS40L2X_NUM_OTP_MAPS]; + +extern const unsigned int cs40l2x_pbq_dig_scale[CS40L2X_PBQ_SCALE_MAX + 1]; + +#endif /*__CS40L2X_H__*/ diff --git a/drivers/misc/cypress/Android.mk b/drivers/misc/cypress/Android.mk new file mode 100644 index 000000000000..c527d21e6122 --- /dev/null +++ b/drivers/misc/cypress/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := cyttsp_sar.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk \ No newline at end of file diff --git a/drivers/misc/cypress/DataFetch.c b/drivers/misc/cypress/DataFetch.c new file mode 100644 index 000000000000..1c771852a5ac --- /dev/null +++ b/drivers/misc/cypress/DataFetch.c @@ -0,0 +1,251 @@ +/***************************************************************************** +* File Name: DataFetch.c +* Version 1.0 +* +* Description: +* This source file contains the functions to fetch data from the Hex image and +* load in to the arrays used for Programming. +* +* Owner: +* Tushar Rastogi, Application Engineer (tusr@cypress.com) +* +* Related Document: +* AN84858 - PSoC 4 Programming using an External Microcontroller (HSSP) +* +* Hardware Dependency: +* PSoC 5LP Development Kit - CY8CKIT-050 +* +* Code Tested With: +* PSoC Creator 2.2 +* ARM GCC 4.4.1 +* CY8CKIT-050 +* +* Note: +* The definitions of the functions used in this file will change based on the +* interface used to get Hex file data. +******************************************************************************* +* Copyright (2013), Cypress Semiconductor Corporation. +******************************************************************************* +* This software is owned by Cypress Semiconductor Corporation (Cypress) and is +* protected by and subject to worldwide patent protection (United States and +* foreign), United States copyright laws and international treaty provisions. +* Cypress hereby grants to licensee a personal, non-exclusive, non-transferable +* license to copy, use, modify, create derivative works of, and compile the +* Cypress Source Code and derivative works for the sole purpose of creating +* custom software in support of licensee product to be used only in conjunction +* with a Cypress integrated circuit as specified in the applicable agreement. +* Any reproduction, modification, translation, compilation, or representation of +* this software except as specified above is prohibited without the express +* written permission of Cypress. +* +* Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH +* REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +* Cypress reserves the right to make changes without further notice to the +* materials described herein. Cypress does not assume any liability arising out +* of the application or use of any product or circuit described herein. Cypress +* does not authorize its products for use as critical components in life-support +* systems where a malfunction or failure may reasonably be expected to result in +* significant injury to the user. The inclusion of Cypress' product in a life- +* support systems application implies that the manufacturer assumes all risk of +* such use and in doing so indemnifies Cypress against all charges. Use may be +* limited by and subject to the applicable Cypress software license agreement. + +******************************************************************************/ + +/****************************************************************************** +* Header file Inclusion +******************************************************************************/ +#include "cycapsense_hssp.h" +#include "DataFetch.h" + +/****************************************************************************** +* Global Constant declaration +******************************************************************************/ + +/****************************************************************************** +* Function Definitions +******************************************************************************/ + +/***************************** USER ATTENTION REQUIRED ************************ +**************** DEPENDS ON INTERFACE USED TO GET HEX FILE DATA *************** +All the function definitions below should be modified based on the interface +used to get Hex file data to be programmed. If a protocol like I2C, UART is +used to get the hex file data, change the function definitions accordingly */ + +/****************************************************************************** +* Function Name: HEX_ReadSiliconId +******************************************************************************* +* +* Summary: +* Loads the Device Silicon ID data in to an array +* +* Parameters: +* unsigned long *hexSiliconId - Address of variable which will store the +* Silicon ID fetched +* +* Return: +* None. +* +* Note: +* Modify definition based on method of getting programming data +* +******************************************************************************/ +void HEX_ReadSiliconId(struct hex_info *inf, unsigned long *hexSiliconId) +{ + unsigned char i; + + if (!inf || !inf->m_data) + return; + + for (i = 0; i < SILICON_ID_BYTE_LENGTH; i++) { + *hexSiliconId = *hexSiliconId | + ((unsigned long)(inf->m_data[2 + i]) << (8 * i)); + } +} + +/****************************************************************************** +* Function Name: HEX_ReadRowData +******************************************************************************* +* +* Summary: +* Loads the row data from hex file in to an array +* +* Parameters: +* unsigned short rowCount - Flash row count for which the data has to be +* fetched +* unsigned char * rowData - Base address of array which will store the +* Flash row data fetched +* +* Return: +* None. +* +* Note: +* Modify definition based on the method of getting programming data +* +* REMOVE THIS LOGIC WHILE PORTING THE HSSP CODE TO OTHER HOST PROGRAMMER +* +******************************************************************************/ +void HEX_ReadRowData(struct hex_info *inf, unsigned short rowCount, + unsigned char *rowData) +{ + unsigned short i; /* Maximum value of 'i' can be 256 */ + + if (!inf || !inf->data) + return; + + for (i = 0; i < FLASH_ROW_BYTE_SIZE_HEX_FILE; i++) { + rowData[i] = + inf->data[i + rowCount * FLASH_ROW_BYTE_SIZE_HEX_FILE]; + } +} + +/****************************************************************************** +* Function Name: HEX_ReadChipProtectionData +******************************************************************************* +* +* Summary: +* Loads the Chip Protection data in to an array +* +* Parameters: +* +* unsigned char * chipProtectionData - Address of variable which will store +* the Chip Protection data fetched +* +* Return: +* None. +* +* Note: +* Modify definition based on the method of getting programming data +* +******************************************************************************/ +void HEX_ReadChipProtectionData(struct hex_info *inf, + unsigned char *chipProtectionData) +{ + if (inf) + *chipProtectionData = inf->cl_protection; + else + *chipProtectionData = 1; +} + +/****************************************************************************** +* Function Name: HEX_ReadRowProtectionData +******************************************************************************* +* +* Summary: +* Loads the Flash Row Protection data in to an array +* +* Parameters: +* +* unsigned char * rowProtectionData - Base address of array which will store +* the Flash Row Protection data fetched +* unsigned char rowProtectionByteSize - Size of flash row protection settings +* in bytes +* +* Return: +* None. +* +* Note: +* Modify definition based on the method of getting programming data +* +******************************************************************************/ +void HEX_ReadRowProtectionData(struct hex_info *inf, + unsigned char rowProtectionByteSize, + unsigned char *rowProtectionData) +{ + if (!inf || !inf->s_data) + return; + + memcpy(rowProtectionData, inf->s_data, rowProtectionByteSize); +} + +/****************************************************************************** +* Function Name: HEX_ReadChecksumData +******************************************************************************* +* +* Summary: +* Loads the Checksum data in to an array +* +* Parameters: +* unsigned short * checksumData - Base address of the array which will store +* the Checksum data fetched +* +* Return: +* None. +* +* Note: +* Modify definition based on the method of getting programming data +* +******************************************************************************/ +void HEX_ReadChecksumData(struct hex_info *inf, unsigned short *checksumData) +{ + if (!inf) + return; + *checksumData = inf->cs; +} + +/****************************************************************************** +* Function Name: GetFlashRowCount +******************************************************************************* +* +* Summary: +* Returns the total number of Flash rows in the target PSoC 4 device +* +* Parameters: +* None +* +* Return: +* unsigned short +* Total number of Flash rows in target PSoC 4 +* +* Note: +* The Flash row count is from the HexImage.h file. Modify the definition based +* on the method of getting programming data +* +******************************************************************************/ +unsigned short GetFlashRowCount(void) +{ + return NUMBER_OF_FLASH_ROWS_HEX_FILE; +} + +/* [] END OF FILE */ diff --git a/drivers/misc/cypress/DataFetch.h b/drivers/misc/cypress/DataFetch.h new file mode 100644 index 000000000000..2f5c75860822 --- /dev/null +++ b/drivers/misc/cypress/DataFetch.h @@ -0,0 +1,71 @@ +/****************************************************************************** +* File Name: DataFetch.h +* Version 1.0 +* +* Description: +* This header file contains the definitions for the size of Programming data, +* and declarations for the functions used in getting the Programming data +* from Hex file image. The fuction definitions in corresponding +* DataFetch.c file will have to be modified if the method of getting +* programming data is changed. +* +* Note: +* +******************************************************************************* +* Copyright (2013), Cypress Semiconductor Corporation. +******************************************************************************* +* This software is owned by Cypress Semiconductor Corporation (Cypress) and is +* protected by and subject to worldwide patent protection (United States and +* foreign), United States copyright laws and international treaty provisions. +* Cypress hereby grants to licensee a personal, non-exclusive, non-transferable +* license to copy, use, modify, create derivative works of, and compile the +* Cypress Source Code and derivative works for the sole purpose of creating +* custom software in support of licensee product to be used only in conjunction +* with a Cypress integrated circuit as specified in the applicable agreement. +* Any reproduction, modification, translation, compilation, or representation of +* this software except as specified above is prohibited without the express +* written permission of Cypress. +* +* Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH +* REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +* Cypress reserves the right to make changes without further notice to the +* materials described herein. Cypress does not assume any liability arising out +* of the application or use of any product or circuit described herein. Cypress +* does not authorize its products for use as critical components in life-support +* systems where a malfunction or failure may reasonably be expected to result in +* significant injury to the user. The inclusion of Cypress' product in a life- +* support systems application implies that the manufacturer assumes all risk of +* such use and in doing so indemnifies Cypress against all charges. Use may be +* limited by and subject to the applicable Cypress software license agreement. +******************************************************************************/ +#ifndef __DATAFETCH_H +#define __DATAFETCH_H + +/****************************************************************************** +* Macro definitions +******************************************************************************/ + +/* The below definitions are not dependent on hex file and are always + constant */ +#define SILICON_ID_BYTE_LENGTH 4 +#define CHECKSUM_BYTE_LENGTH 2 +#define MAXIMUM_ROW_PROTECTION_BYTE_LENGTH 32 + +/****************************************************************************** +* Function Prototypes +******************************************************************************/ +void HEX_ReadSiliconId(struct hex_info *inf, unsigned long *hexSiliconId); +void HEX_ReadRowData(struct hex_info *inf, unsigned short rowCount, + unsigned char *rowData); +void HEX_ReadChipProtectionData(struct hex_info *inf, + unsigned char *chipProtectionData); +void HEX_ReadRowProtectionData(struct hex_info *inf, + unsigned char rowProtectionByteSize, + unsigned char *rowProtectionData); +void HEX_ReadChecksumData(struct hex_info *inf, unsigned short *checksumData); +unsigned short GetFlashRowCount(void); + +#endif /* __DATAFETCH_H */ + +/* [] END OF FILE */ diff --git a/drivers/misc/cypress/Kbuild b/drivers/misc/cypress/Kbuild new file mode 100644 index 000000000000..8404ac5f0de6 --- /dev/null +++ b/drivers/misc/cypress/Kbuild @@ -0,0 +1,13 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +EXTRA_CFLAGS += -DCY8C40xx_FAMILY +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../sensors/Module.symvers + +cyttsp_sar-y := ProgrammingSteps.o \ + SWD_PacketLayer.o \ + SWD_UpperPacketLayer.o \ + DataFetch.o \ + cyttsp_sar_mmi.o +obj-m += cyttsp_sar.o \ No newline at end of file diff --git a/drivers/misc/cypress/Makefile b/drivers/misc/cypress/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/misc/cypress/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/misc/cypress/ProgrammingSteps.c b/drivers/misc/cypress/ProgrammingSteps.c new file mode 100644 index 000000000000..1c9514ecd9ae --- /dev/null +++ b/drivers/misc/cypress/ProgrammingSteps.c @@ -0,0 +1,1406 @@ +/****************************************************************************** + * File Name: ProgrammingSteps.c + * Version 1.0 + * + * Description: + * This file provides the source code for the high level Programming functions + * used by the main code to program target PSoC 4 + * + * Owner: + * Tushar Rastogi, Application Engineer (tusr@cypress.com) + * + * Related Document: + * AN84858 - PSoC 4 Programming using an External Microcontroller (HSSP) + * + * Hardware Dependency: + * PSoC 5LP Development Kit - CY8CKIT-050 + * + * Code Tested With: + * PSoC Creator 2.2 + * ARM GCC 4.4.1 + * CY8CKIT-050 + * + ******************************************************************************* + * Copyright (2013), Cypress Semiconductor Corporation. + ******************************************************************************* + * This software is owned by Cypress Semiconductor Corporation (Cypress) and is + * protected by and subject to worldwide patent protection (United States and + * foreign), United States copyright laws and international treaty provisions. + * Cypress hereby grants to licensee a personal, non-exclusive, non-transferable + * license to copy, use, modify, create derivative works of, and compile the + * Cypress Source Code and derivative works for the sole purpose of creating + * custom software in support of licensee product to be used only in conjunction + * with a Cypress integrated circuit as specified in the applicable agreement. + * Any reproduction, modification, translation, compilation, or representation + * of this software except as specified above is prohibited without the express + * written permission of Cypress. + * + * Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH + * REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * Cypress reserves the right to make changes without further notice to the + * materials described herein. Cypress does not assume any liability arising out + * of the application or use of any product or circuit described herein. Cypress + * does not authorize its products for use as critical components in life-support + * systems where a malfunction or failure may reasonably be expected to result in + * significant injury to the user. The inclusion of Cypress' product in a life- + * support systems application implies that the manufacturer assumes all risk of + * such use and in doing so indemnifies Cypress against all charges. Use may be + * limited by and subject to the applicable Cypress software license agreement. + ******************************************************************************/ + +/****************************************************************************** + * Header file Inclusion + ******************************************************************************/ +#include "ProgrammingSteps.h" +#include "SWD_PhysicalLayer.h" +#include "SWD_UpperPacketLayer.h" +#include "SWD_PacketLayer.h" +#include "DataFetch.h" +#include "Timeout.h" + +/****************************************************************************** + * Global Variable definitions + ******************************************************************************/ +unsigned long checksum_Privileged; +unsigned long statusCode; + +unsigned char result; +unsigned char chipProtectionData_Chip; + +enum Transition_mode { OPEN_XXX, VIRGIN_OPEN, PROT_XXX, WRONG_TRANSITION } flow; + +/****************************************************************************** + * Function Definitions + ******************************************************************************/ + +/****************************************************************************** + * Function Name: PollSromStatus + ******************************************************************************* + * Summary: + * Polls the SROM_SYSREQ_BIT and SROM_PRIVILEGED_BIT in the CPUSS_SYSREQ + * register till it is reset or a timeout condition occurred, whichever is + * earlier. For a SROM polling timeout error, the timeout error status bit is + * set in swd_PacketAck variable and CPUSS_SYSARG register is read to get the + * error status code. If timeout does not happen, the CPUSS_SYSARG register is + * read to determine if the task executed successfully. + * + * Parameters: + * None. + * + * Return: + * SUCCESS - SROM executed the task successfully + * FAILURE - SROM task is not executed successfully and a timeout error occured. + * The failure code is stored in the statusCode global variable. + * + * Note: + * This function is called after non volatile memory operations like Read, + * Write of Flash, to check if SROM task has been executed which is indicated + * by SUCCESS. The status is read from the CPUSS_SYSARG register. + * + ******************************************************************************/ +unsigned char PollSromStatus(void) +{ + unsigned long time_elapsed = 0; + + do { + /* Read CPUSS_SYSREQ register and check if SROM_SYSREQ_BIT and + SROM_PRIVILEGED_BIT are reset to 0 */ + Read_IO(CPUSS_SYSREQ, &statusCode); + statusCode &= (SROM_SYSREQ_BIT | SROM_PRIVILEGED_BIT); + time_elapsed++; + } while ((statusCode != 0) && (time_elapsed <= SROM_POLLING_TIMEOUT)); + + /* If time exceeds the timeout value, set the SROM_TIMEOUT_ERROR bit in + swd_PacketAck */ + if (time_elapsed > SROM_POLLING_TIMEOUT) { + swd_PacketAck = swd_PacketAck | SROM_TIMEOUT_ERROR; + Read_IO(CPUSS_SYSARG, &statusCode); + return FAILURE; + } + + /* Read CPUSS_SYSARG register to check if the SROM command executed + successfully else set SROM_TIMEOUT_ERROR in swd_PacketAck */ + Read_IO(CPUSS_SYSARG, &statusCode); + + if ((statusCode & SROM_STATUS_SUCCESS_MASK) != SROM_STATUS_SUCCEEDED) { + swd_PacketAck = swd_PacketAck | SROM_TIMEOUT_ERROR; + return FAILURE; + } + + return SUCCESS; +} + +#if defined CY8C40xx_FAMILY +/****************************************************************************** + * Function Name: SetIMO48MHz + ******************************************************************************* + * Summary: + * Set IMO to 48 MHz + * + * Parameters: + * None. + * + * Return: + * None + * + * Note: + * This function is required to be called before any flash operation. + * This function sets the IMO to 48 MHz before flash write/erase operations + * and is part of the device acquire routine. + * + ******************************************************************************/ +void SetIMO48MHz(void) +{ + unsigned long parameter1 = 0; + + /* Load the Parameter1 with the SROM command to read silicon ID */ + parameter1 = (unsigned long)(((unsigned long)SROM_KEY1 << 0) + + (((unsigned long)SROM_KEY2 + + (unsigned long)SROM_CMD_SET_IMO_48MHZ) << + 8)); + + /* Write the command to CPUSS_SYSARG register */ + Write_IO(CPUSS_SYSARG, parameter1); + Write_IO(CPUSS_SYSREQ, SROM_SYSREQ_BIT | SROM_CMD_SET_IMO_48MHZ); +} +#endif +/****************************************************************************** + * Function Name: ReadSromStatus + ******************************************************************************* + * + * Summary: + * It reads the StatusCode global variable and returns LSB of this long variable + * to main.c. + * + * Parameters: + * None. + * + * Return: + * LSB of statusCode - LSB of statusCode global variable contains the error code + * + * Note: + * This function is called from main.c when SROM_TIMEOUT_ERROR bit is set in the + * swd_PacketAck. + * + ******************************************************************************/ + +unsigned char ReadSromStatus(void) +{ + return (unsigned char)statusCode; +} + +/****************************************************************************** + * Function Name: GetChipProtectionVal + ******************************************************************************* + * Summary: + * This sub-routine is used to read the Chip Protection Setting by using SROM + * System Calls. System call to read Silicon Id returns Chip protection settings + * in the CPUSS_SYSREQ register. The location of the data is bit [15:12] in the + * 32-bit register. + * + * Parameters: + * None. + * + * Return: + * chipProtectionData_Chip - 1 byte chip protection setting read from the chip + * + * Note: + * This function is called in the "Step 3. Erase All Flash" to read the chip + * protection settings to take decision whether to move the protection state + * to open and then erase the flash or directly erase the flash. + * + ******************************************************************************/ +unsigned char GetChipProtectionVal(struct hssp_data *d) +{ + unsigned long parameter1 = 0; + unsigned long chipProtData = 0; + + /* Load the Parameter1 with the SROM command to read silicon ID */ + parameter1 = (unsigned long)(((unsigned long)SROM_KEY1 << 0) + + (((unsigned long)SROM_KEY2 + + (unsigned long)SROM_CMD_GET_SILICON_ID) + << 8)); + + /* Write the command to CPUSS_SYSARG register */ + Write_IO(CPUSS_SYSARG, parameter1); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Request system call by writing to CPUSS_SYSREQ register */ + Write_IO(CPUSS_SYSREQ, SROM_SYSREQ_BIT | SROM_CMD_GET_SILICON_ID); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Read status of the operation */ + result = PollSromStatus(); + if (result != SUCCESS) + return FAILURE; + + /* Read CPUSS_SYSREQ register to get the current protection setting of + the chip */ + Read_IO(CPUSS_SYSREQ, &chipProtData); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + chipProtectionData_Chip = (unsigned char)(chipProtData >> 12); + + return SUCCESS; +} + +/****************************************************************************** + * Function Name: GetTransitionMode + ******************************************************************************* + * + * Summary: + * It reads the chipProtectionData_Chip global variable which contains the Chip + * protection setting stored in the Chip and chipProtectionData_Hex from the + * hex file which contains the Chip protection setting stored in the HEX file. + * The function then validates if the two settings correspond to a valid + * transition. + * + * Parameters: + * None. + * + * Return: + * SUCCESS - Returns SUCCESS if the transition is valid. + * FAILURE - Returns Failure if the transition is invalid. + * Stores the transition in the global enum flow. + * + * Note: + * This function is called in "Step 3. Erase All Flash" to take decision on + * basis of the global enum flow. + * + ******************************************************************************/ +unsigned char GetTransitionMode(struct hssp_data *d) +{ + unsigned char chipProtectionData_Hex; + + /* Get the chip protection setting in the HEX file */ + HEX_ReadChipProtectionData(&d->inf, &chipProtectionData_Hex); + + /* enum variable flow stores the transition (current protection + setting to setting in hex file) of the chip */ + flow = WRONG_TRANSITION; + + switch (chipProtectionData_Chip) { + /* virgin to open protection setting is the only + allowed transition */ + case CHIP_PROT_VIRGIN: + if (chipProtectionData_Hex == CHIP_PROT_OPEN) + flow = VIRGIN_OPEN; + else + flow = WRONG_TRANSITION; + break; + + /* All transitions from Open are allowed other than + transition to virgin mode */ + case CHIP_PROT_OPEN: + if (chipProtectionData_Hex == CHIP_PROT_VIRGIN) + flow = WRONG_TRANSITION; + else + flow = OPEN_XXX; + break; + + /* Protected to Protected and Protected to Open are + the allowed transitions */ + case CHIP_PROT_PROTECTED: + if ((chipProtectionData_Hex == CHIP_PROT_OPEN) || + (chipProtectionData_Hex == CHIP_PROT_PROTECTED)) + flow = PROT_XXX; + else + flow = WRONG_TRANSITION; + break; + + default: + flow = WRONG_TRANSITION; + break; + } + + /* Set TRANSITION_ERROR bit high in Swd_PacketAck to show wrong + transition error */ + if (flow == WRONG_TRANSITION) { + swd_PacketAck = swd_PacketAck | TRANSITION_ERROR; + return FAILURE; + } + + return SUCCESS; +} + +/****************************************************************************** + * Function Name: LoadLatch + ******************************************************************************* + * + * Summary: + * This function loads the page latch buffer with data to be programmed in to a + * row of flash. Data is loaded into the page latch buffer starting at the + * location specified by the SRAM_PARAMS_BASE input parameter. Data loaded into + * the page latch buffer will remain until a program operation is performed, + * which clears the page latch contents. + * + * Parameters: + * arrayID - Array Number of the flash + * rowData - Array containing 128 bytes of programming data + * + * Return: + * SUCCESS - Returns SUCCESS if Data is successfully latched + * FAILURE - Returns Failure if Data is not latched successfully + * + * Note: + * This function is called in "Step 5. Program Flash" and + * "Step 7. Program Protection Settings" to latch the programming data in SRAM. + * + ******************************************************************************/ +unsigned char LoadLatch(struct hssp_data *d, unsigned char arrayID, + unsigned char *rowData) +{ + unsigned long parameter1 = 0; + unsigned long parameter2 = 0; + unsigned char i = 0; + + /* Load parameter1 with the SROM command to load the page latch buffer + with programming data */ + parameter1 = ((unsigned long)SROM_KEY1 << 0) + + (((unsigned long)SROM_KEY2 + + (unsigned long)SROM_CMD_LOAD_LATCH) << 8) + + (0x00 << 16) + ((unsigned long)arrayID << 24); + + /* Number of Bytes to load minus 1 */ + parameter2 = (FLASH_ROW_BYTE_SIZE_HEX_FILE - 1); + + /* Write parameter1 in SRAM */ + Write_IO(SRAM_PARAMS_BASE + 0x00, parameter1); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Write parameter2 in SRAM */ + Write_IO(SRAM_PARAMS_BASE + 0x04, parameter2); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Put row data into SRAM buffer */ + for (i = 0; i < FLASH_ROW_BYTE_SIZE_HEX_FILE; i += 4) { + parameter1 = (rowData[i] << 0) + (rowData[i + 1] << 8) + + (rowData[i + 2] << 16) + (rowData[i + 3] << 24); + /* Write parameter1 in SRAM */ + Write_IO(SRAM_PARAMS_BASE + 0x08 + i, parameter1); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + } + + /* Call "Load Latch" SROM API */ + + /* Set location of parameters */ + Write_IO(CPUSS_SYSARG, SRAM_PARAMS_BASE); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Request SROM operation */ + Write_IO(CPUSS_SYSREQ, SROM_SYSREQ_BIT | SROM_CMD_LOAD_LATCH); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Read status of the operation */ + result = PollSromStatus(); + if (result != SUCCESS) + return FAILURE; + + return SUCCESS; +} + +/****************************************************************************** + * Function Name: ChecksumAPI + ******************************************************************************* + * + * Summary: + * This function reads either the whole flash memory or a row of flash. When + * performing a checksum on the whole flash, the user code and the supervisory + * flash regions are included. When performing a checksum only on one row of + * flash, the flash row number is passed as a parameter. For computing Checksum + * of entire flash, ChecksumRow input parameter is loaded as macro + * CHECKSUM_ENTIRE_FLASH (0x8000). + * + * Parameters: + * checksumRow: Row number of flash for which checksum has to be calculated. + * To compute checksum of entire flash, + * this variable is set to + * CHECKSUM_ENTIRE_FLASH (0x8000). + * + * checksum: This variable is loaded with the checksum after the execution of + * the function. + * + * Return: + * SUCCESS - Returns SUCCESS if Checksum System call is successfully executed. + * FAILURE - Returns Failure if Checksum system call fails to execute. + * + * Note: + * This function is called in "Step 4. Checksum Privileged Calculation" and + * "Step 9. Verify Checksum" to calculate the checksum of flash privileged rows + * and entire flash, respectively. + * + ******************************************************************************/ +unsigned char ChecksumAPI(struct hssp_data *d, unsigned short checksumRow, + unsigned long *checksum) +{ + unsigned long parameter1 = 0; + unsigned long checksum_chip = 0; + + /* Load parameter1 with the SROM command to compute checksum of whole + flash */ + parameter1 = ((unsigned long)SROM_KEY1 << 00) + + (((unsigned long)SROM_KEY2 + + (unsigned long)SROM_CMD_CHECKSUM) << 8) + + (((unsigned long)checksumRow & 0x000000FF) << 16) + + (((unsigned long)checksumRow & 0x0000FF00) << 16); + + /* Load CPUSS_SYSARG register with parameter1 command */ + Write_IO(CPUSS_SYSARG, parameter1); + + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Request SROM operation */ + Write_IO(CPUSS_SYSREQ, SROM_SYSREQ_BIT | SROM_CMD_CHECKSUM); + + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Read status of the operation */ + result = PollSromStatus(); + + if (result != SUCCESS) + return FAILURE; + + /* Read CPUSS_SYSARG register to get the checksum value */ + Read_IO(CPUSS_SYSARG, &checksum_chip); + + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* 28-bit checksum */ + *checksum = (checksum_chip & 0x0FFFFFFF); + + return SUCCESS; +} + +/****************************************************************************** + *Function Name: DeviceAcquire + ******************************************************************************* + * + * Summary: + * Acquire device with a alternate method which don't require >1.5Mhz SWD clock rate + * + * + * Parameters: + * None + * + * Return: + * SUCCESS - Returns SUCCESS if the device is successfully acquired. + * FAILURE - Returns Failure if the function fails in any of the intermediate + * step. + * + * Note: + * This function has very strict timing requirements. The device must be + * acquired as per the timing requirements given in PSoC 4 Device Programming + * Specification Document. + * + ******************************************************************************/ + +#define DEVICE_ACQUIRE_TIMEOUT_ALTERNATE_METHOD 400 + +unsigned char DeviceAcquire(void) +{ + unsigned long chip_DAP_Id = 0; + unsigned short total_packet_count = 0; + unsigned long status = 0; + unsigned long reset_address = 0; + unsigned long psr_reg_val = 0; + unsigned long params = 0; + int i = 1; + + SetXresCmosOutput(); + SetXresHigh(); + + SetSwdckCmosOutput(); + SetSwdckLow(); + + SetSwdioCmosOutput(); + SetSwdioLow(); + + SetXresLow(); + DelayHundredUs(); + SetXresHigh(); + + /* Execute SWD connect sequence. + 100 ms time out below is worst case time out value */ + do { + Swd_LineReset(); + Read_DAP(DPACC_DP_IDCODE_READ, &chip_DAP_Id); + total_packet_count++; + } while ((swd_PacketAck != SWD_OK_ACK) && + (total_packet_count < DEVICE_ACQUIRE_TIMEOUT_ALTERNATE_METHOD)); + + + /* Set PORT_ACQUIRE_TIMEOUT_ERROR bit in swd_PacketAck if time + exceeds 100 ms */ + if (total_packet_count == DEVICE_ACQUIRE_TIMEOUT_ALTERNATE_METHOD) { + swd_PacketAck = swd_PacketAck | PORT_ACQUIRE_TIMEOUT_ERROR; + return FAILURE; + } + + /* Set VERIFICATION_ERROR bit in swd_PacketAck if the DAP_ID read + from chip does not match with the ARM CM0_DAP_ID (MACRO defined in + ProgrammingSteps.h file - 0x0BB11477) */ + if ((chip_DAP_Id != CM0_DAP_ID) && (chip_DAP_Id != CM0PLUS_DAP_ID)) { + swd_PacketAck = swd_PacketAck | VERIFICATION_ERROR; + return FAILURE; + } + + /* Clear WDATAERR if any previous firmware upgrade operation was aborted in the mddle + and WDATAERR bit is set. This write is to the AP_ABORT register in Debug Port + (APnDP bit - 0, Address is 2'b00, Access -W for the AP ABORT register */ + Write_DAP(DPACC_DP_ABORT_WRITE, 0x00000008); + + /* Set the CSYSPWRUPEQ(System power up request), CDBGPRWUPREQ(debug power up request), + CDBGRSTREQ(Debug reset request) bits in the DP_CTRLSTAT regsiter) */ + Write_DAP(DPACC_DP_CTRLSTAT_WRITE, 0x54000000); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Set the AP register bank selection */ + Write_DAP(DPACC_DP_SELECT_WRITE, 0x00000000); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* set the register access as word 4-byte access + For device with CM0+ CPU, the HPROT[1] bit also needs to be set along with few other bits field */ + if (chip_DAP_Id == CM0PLUS_DAP_ID) { + Write_DAP(DPACC_AP_CSW_WRITE, 0x03000042); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + } + + /* Enable debug, and halt the CPU */ + Write_IO(0xE000EDF0, 0xA05F0003); + + /* Verify the debug enable, cpu halt bits are set */ + Read_IO(0xE000EDF0, &status); + if ((status & 0x00000003) != 0x00000003) + return FAILURE; + + /* Enable Breakpoint unit */ + Write_IO(0xE0002000, 0x00000003); + + /* Get address at reset vector */ + Read_IO(0x00000004, &reset_address); + + /* Map the address bits to the breakpoint compare register bit map, + Set the enable breakpoint bit, and the match bits */ + reset_address = (reset_address & 0x1FFFFFFC) | 0xC0000001; + + /* Update the breakpoint compare register */ + Write_IO(0xE0002008, reset_address); + + /* Issue software reset */ + Write_IO(0xE000ED0C, 0x05FA0004); + + /* Sufficient delay after reset for boot process, 5ms */ + while (i <= 50) { + DelayHundredUs(); + i++; + } + + /* Repeat a portion of the acquire sequence again */ + do { + Swd_LineReset(); + Read_DAP(DPACC_DP_IDCODE_READ, &chip_DAP_Id); + total_packet_count++; + } while ((swd_PacketAck != SWD_OK_ACK) && + (total_packet_count < DEVICE_ACQUIRE_TIMEOUT_ALTERNATE_METHOD)); + + + /* Set PORT_ACQUIRE_TIMEOUT_ERROR bit in swd_PacketAck if time + exceeds 100 ms */ + if (total_packet_count == DEVICE_ACQUIRE_TIMEOUT_ALTERNATE_METHOD) { + swd_PacketAck = swd_PacketAck | PORT_ACQUIRE_TIMEOUT_ERROR; + return FAILURE; + } + + /* Set VERIFICATION_ERROR bit in swd_PacketAck if the DAP_ID read + from chip does not match with the ARM CM0_DAP_ID (MACRO defined in + ProgrammingSteps.h file - 0x0BB11477) */ + if ((chip_DAP_Id != CM0_DAP_ID) && (chip_DAP_Id != CM0PLUS_DAP_ID)) { + swd_PacketAck = swd_PacketAck | VERIFICATION_ERROR; + return FAILURE; + } + + + /* Set the CSYSPWRUPEQ(System power up request), CDBGPRWUPREQ(debug power up request), + CDBGRSTREQ(Debug reset request) bits in the DP_CTRLSTAT regsiter) */ + Write_DAP(DPACC_DP_CTRLSTAT_WRITE, 0x54000000); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Set the AP register bank selection */ + Write_DAP(DPACC_DP_SELECT_WRITE, 0x00000000); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* set the register access as word 4-byte access + For device with CM0+ CPU, the HPROT[1] bit also needs to be set along with few other bits field */ + if (chip_DAP_Id == CM0PLUS_DAP_ID) { + Write_DAP(DPACC_AP_CSW_WRITE, 0x03000042); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + } + + /* Verify the debug enable, cpu halt bits are set */ + Read_IO(0xE000EDF0, &status); + if ((status & 0x00000003) != 0x00000003) + return FAILURE; + + /* Load infinite for loop code in SRAM address 0x20000300 */ + Write_IO(0x20000300, 0xE7FEE7FE); + + /* Load PC with address of infinite for loop SRAM address with thumb it(bit 0) set */ + Write_IO(0xE000EDF8, 0x20000301); + Write_IO(0xE000EDF4, 0x0001000F); + + /* Load SP with top of the SRAM address - set for mimimum SRAM size device(2KB size) */ + Write_IO(0xE000EDF8, 0x20000800); + Write_IO(0xE000EDF4, 0x00010011); + + /* Read xPSR regsiter, set the thumb bit, and store modified value to xPSR regsiter */ + Write_IO(0xE000EDF4, 0x00000010); + Read_IO(0xE000EDF8, &psr_reg_val); + psr_reg_val = psr_reg_val | 0x01000000; + Write_IO(0xE000EDF8, psr_reg_val); + Write_IO(0xE000EDF4, 0x00010010); + + /* Disable breakpoint unit */ + Write_IO(0xE0002000, 0x00000002); + + /* Unhalt CPU */ + Write_IO(0xE000EDF0, 0xA05F0001); + + /* The following SROM call to set IMO to 48MHZ must be skipped for PSOC 4100M, 4200M devices + Set "IMO=48MHZ" to enable Erase/Program/Write Flash operations. */ + params = ((unsigned long)SROM_KEY1 << 0) + (((unsigned long)SROM_KEY2 + (unsigned long)SROM_CMD_SET_IMO_48MHZ) << 8); + + /* Write Params in CPUSS_SYSARG */ + Write_IO(CPUSS_SYSARG, params); + + /* Request SROM call */ + Write_IO(CPUSS_SYSREQ, SROM_SYSREQ_BIT | SROM_CMD_SET_IMO_48MHZ); + + /* Read status of the operation */ + result = PollSromStatus(); + if (result != SUCCESS) + return FAILURE; + + + return SUCCESS; + +} + +/****************************************************************************** + * Function Name: VerifySiliconId + ******************************************************************************* + * + * Summary: + * This is Step 2 of the programming sequence. In this Step, Silicon Id of the + * PSoC 4 device is read and matched with the silicon id stored in the Hex File + * to verify that the correct device is being programmed. + * + * Parameters: + * None + * + * Return: + * SUCCESS - Returns SUCCESS if Silicon Id read from chip matches the Id in the + * HEX File. + * FAILURE - Returns Failure if any of the intermediate step returns a fail + * message. + * + * Note: + * + ******************************************************************************/ +unsigned char VerifySiliconId(struct hssp_data *d) +{ + unsigned char i; + unsigned long deviceSiliconID; + unsigned long hexSiliconId = 0; + unsigned long parameter1 = 0; + unsigned long siliconIdData1 = 0; + unsigned long siliconIdData2 = 0; + + /* Read and store Silicon ID from HEX file to hexSiliconId array */ + HEX_ReadSiliconId(&d->inf, &hexSiliconId); + + /* Load Parameter1 with the SROM command to read silicon ID from + PSoC 4 chip */ + parameter1 = (unsigned long)(((unsigned long)SROM_KEY1 << 0) + + (((unsigned long)SROM_KEY2 + + (unsigned long)SROM_CMD_GET_SILICON_ID) + << 8)); + + /* Load CPUSS_SYSARG register with parameter1 */ + Write_IO(CPUSS_SYSARG, parameter1); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Request SROM operation */ + Write_IO(CPUSS_SYSREQ, SROM_SYSREQ_BIT | SROM_CMD_GET_SILICON_ID); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Read status of the operation */ + result = PollSromStatus(); + if (result != SUCCESS) + return FAILURE; + + /* Read CPUSS_SYSARG and CPUSS_SYSREQ to read 4 bytes of silicon ID */ + Read_IO(CPUSS_SYSARG, &siliconIdData1); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + Read_IO(CPUSS_SYSREQ, &siliconIdData2); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* + SiliconIdData2 (0th byte) = 4th byte of Device Silicon ID (MSB) + SiliconIdData1 (3rd byte) = 3rd byte of Device Silicon ID + SiliconIdData1 (1st byte) = 2nd byte of Device Silicon ID + SiliconIdData1 (2nd byte) = 1st byte of Device Silicon ID (LSB) + */ + deviceSiliconID = (((siliconIdData2 << 24) & 0xFF000000) + + (siliconIdData1 & 0x00FF0000) + + ((siliconIdData1 << 8) & 0x0000FF00) + + ((siliconIdData1 >> 8) & 0x000000FF)); + + /* Match the Silicon ID read from HEX file and PSoC 4 chip */ + for (i = 0; i < SILICON_ID_BYTE_LENGTH; i++) { + if ((deviceSiliconID & 0xFF00FFFF) != + (hexSiliconId & 0xFF00FFFF)) { + /* Set the VERIFICATION_ERROR bit in swd_PacketAck */ + swd_PacketAck = swd_PacketAck | VERIFICATION_ERROR; + return FAILURE; + } + } + return SUCCESS; +} + +/****************************************************************************** + * Function Name: EraseAllFlash + ******************************************************************************* + * + * Summary: + * This is Step 3 of the programming sequence. In this Step, the whole user + * flash is erased. This function uses GetChipProtectionVal() and + * GetTransitionMode() API's to take the decision on the method to follow to + * erase the device. + * + * Parameters: + * None + * + * Return: + * SUCCESS - Returns SUCCESS if function successfully erases complete user + * flash region. + * FAILURE - Returns Failure if any of the intermediate step returns a fail + * message. + * + * Note: + * + ******************************************************************************/ +unsigned char EraseAllFlash(struct hssp_data *d) +{ + unsigned long parameter1 = 0; + + /* Get current chip protection setting */ + GetChipProtectionVal(d); + + /* Check if the Chip protection setting transition is valid */ + result = GetTransitionMode(d); + if (result != SUCCESS) + return FAILURE; + + /* If the transition is from open to any protection setting or from + virgin to open, call ERASE_ALL SROM command */ + if ((flow == OPEN_XXX) || (flow == VIRGIN_OPEN)) { + parameter1 = (unsigned long)(((unsigned long)SROM_KEY1 << 0) + + (((unsigned long)SROM_KEY2 + + (unsigned long) + SROM_CMD_ERASE_ALL) << 8)); + + /* Load ERASE_ALL SROM command in parameter1 to SRAM */ + Write_IO(SRAM_PARAMS_BASE + 0x00, parameter1); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Set location of parameters */ + Write_IO(CPUSS_SYSARG, SRAM_PARAMS_BASE); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Request SROM call */ + Write_IO(CPUSS_SYSREQ, SROM_SYSREQ_BIT | SROM_CMD_ERASE_ALL); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Read status of the operation */ + result = PollSromStatus(); + if (result != SUCCESS) + return FAILURE; + + } else if (flow == PROT_XXX) { + /* If the transition is from protected mode to open mode or + protected mode to protected mode only, + call ERASE_ALL SROM command */ + + /* Move chip to open state: 0x01 corresponds to open state, + 0x00 to macro 1 */ + parameter1 = ((unsigned long)SROM_KEY1 << 0) + + (((unsigned long)SROM_KEY2 + + (unsigned long)SROM_CMD_WRITE_PROTECTION) << 8) + + (0x01 << 16) + (0x00 << 24); + + /* Load the write protection command to SRAM */ + Write_IO(CPUSS_SYSARG, parameter1); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Request SROM call */ + Write_IO(CPUSS_SYSREQ, + SROM_SYSREQ_BIT | SROM_CMD_WRITE_PROTECTION); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Read status of the operation */ + result = PollSromStatus(); + if (result != SUCCESS) + return FAILURE; + + /* Re-acquire chip in OPEN mode */ + result = DeviceAcquire(); + if (result != SUCCESS) + return FAILURE; + } + + return SUCCESS; +} + +/****************************************************************************** + * Function Name: ChecksumPrivileged + ******************************************************************************* + * + * Summary: + * This is Step 4 of the programming sequence. In this Step, checksum of the + * privileged rows is calulated using system call to determine checksum. This + * step uses ChecksumAPI() API to store the checksum of privileged rows in a + * Checksum_Privileged global variable. This variable is used in step 9 to + * calculate the checksum of user rows. + * + * Parameters: + * None + * + * Return: + * SUCCESS - Returns SUCCESS if function successfully calculated the checksum + * of privileged rows. + * FAILURE - Returns Failure if any of the intermediate step returns a fail + * message. + * + * Note: + * + ******************************************************************************/ +unsigned char ChecksumPrivileged(struct hssp_data *d) +{ + result = ChecksumAPI(d, CHECKSUM_ENTIRE_FLASH, &checksum_Privileged); + if (result != SUCCESS) + return FAILURE; + return SUCCESS; +} + +/****************************************************************************** + * Function Name: ProgramFlash + ******************************************************************************* + * + * Summary: + * This is Step 5 of the programming sequence. In this Step, the whole user + * flash is re-programmed with the programming data in the HEX File. This + * function uses LoadLatch() API to latch the row data in SRAM page latch buffer + * which is then programmed to the specific row using system calls to program + * row. + * + * Parameters: + * None + * + * Return: + * SUCCESS - Returns SUCCESS if function successfully programs entire flash + * region. + * FAILURE - Returns Failure if any of the intermediate step returns a fail + * message. + * + * Note: + * + ******************************************************************************/ +unsigned char ProgramFlash(struct hssp_data *d) +{ + unsigned char arrayID = 0; + unsigned char rowData[FLASH_ROW_BYTE_SIZE_HEX_FILE]; + unsigned short numOfFlashRows = 0; + unsigned short rowCount = 0; + unsigned long parameter1 = 0; + + /* Get the total number of flash rows in the Target PSoC 4 device */ + numOfFlashRows = GetFlashRowCount(); + + /* Program all flash rows */ + for (rowCount = 0; rowCount < numOfFlashRows; rowCount++) { + + HEX_ReadRowData(&d->inf, rowCount, &rowData[0]); + arrayID = rowCount / ROWS_PER_ARRAY; + result = LoadLatch(d, arrayID, &rowData[0]); + if (result != SUCCESS) + return FAILURE; + + /* Load parameter1 with Program Row - SROM command */ + parameter1 = (unsigned long)(((unsigned long)SROM_KEY1 << 0) + + (((unsigned long)SROM_KEY2 + + (unsigned long) + SROM_CMD_PROGRAM_ROW) << 8) + + (((unsigned long)rowCount & + 0x000000FF) << 16) + + (((unsigned long)rowCount & + 0x0000FF00) << 16)); + + /* Write parameters in SRAM */ + Write_IO(SRAM_PARAMS_BASE + 0x00, parameter1); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Set location of parameters */ + Write_IO(CPUSS_SYSARG, SRAM_PARAMS_BASE); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Request SROM operation */ + Write_IO(CPUSS_SYSREQ, SROM_SYSREQ_BIT | SROM_CMD_PROGRAM_ROW); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Read status of the operation */ + result = PollSromStatus(); + if (result != SUCCESS) + return FAILURE; + + } + + return SUCCESS; +} + +/****************************************************************************** + * Function Name: VerifySwRevision + ******************************************************************************* + * + * Summary: + * This is a way to check Sw revision stored in flash at SW_REVISION_OFFSET. + * This is a preliminary step to avoid reflashing with the same SW revision. + * The checksum verification may not work because the checksun is changed is + * a configuration is stored in the flash. In this Step, flash region is + * directly read using Read_IO API defined in SWD_UpperPacketLayer.h and + * compared with the HEX File. + * + * Parameters: + * None + * + * Return: + * SUCCESS - Returns SUCCESS if function successfully verifies the software + * revision with the HEX File. + * FAILURE - Returns Failure if any of the intermediate step returns a fail + * message. + * + * Note: + * + ******************************************************************************/ +unsigned char VerifySwRevision(struct hssp_data *d) +{ + unsigned long flashData = 0; + unsigned short row; + unsigned short offset; + unsigned short HexSwRev; + unsigned short ChipSwRev; + unsigned char rowData[FLASH_ROW_BYTE_SIZE_HEX_FILE]; + + /* Get the Sw rev row in the Target PSoC 4 device */ + row = SW_REVISION_OFFSET / FLASH_ROW_BYTE_SIZE_HEX_FILE; + offset = SW_REVISION_OFFSET % FLASH_ROW_BYTE_SIZE_HEX_FILE; + + /* Extract row from the hex-file */ + HEX_ReadRowData(&d->inf, row, &rowData[0]); + + HexSwRev = (rowData[offset + 1]<<8) | rowData[offset]; + + /* Read flash via AHB-interface */ + Read_IO(SW_REVISION_OFFSET, &flashData); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + ChipSwRev = ((flashData >> (offset % 4)*8)) & 0x00FF; + ChipSwRev |= ((flashData >> (offset % 4 + 1) * 8) << 8) & 0xFF00; + d->sw_rev = HexSwRev; + + pr_info("cycapsense_hssp: File SW rev 0x%x, Flash SW rev 0x%x\n", + HexSwRev, ChipSwRev); + + if (HexSwRev != ChipSwRev) { + swd_PacketAck = swd_PacketAck|VERIFICATION_ERROR; + return FAILURE; + } + + return SUCCESS; +} + +/****************************************************************************** + * Function Name: VerifyFlash + ******************************************************************************* + * + * Summary: + * This is Step 6 of the programming sequence. This is an optional step as we + * verify the checksum explicitly. In this Step, flash region is directly read + * using Read_IO API defined in SWD_UpperPacketLayer.h and compared with the + * HEX File. + * + * Parameters: + * None + * + * Return: + * SUCCESS - Returns SUCCESS if function successfully verifies the entire flash + * with the HEX File. + * FAILURE - Returns Failure if any of the intermediate step returns a fail + * message. + * + * Note: + * + ******************************************************************************/ +unsigned char VerifyFlash(struct hssp_data *d) +{ + unsigned long flashData = 0; + unsigned short numOfFlashRows = 0; + unsigned short rowAddress = 0; + unsigned short rowCount; + unsigned char i; + unsigned char rowData[FLASH_ROW_BYTE_SIZE_HEX_FILE]; + unsigned char chipData[FLASH_ROW_BYTE_SIZE_HEX_FILE]; + + /* Get the total number of flash rows in the Target PSoC 4 device */ + numOfFlashRows = GetFlashRowCount(); + + /* Read and Verify Flash rows */ + for (rowCount = 0; rowCount < numOfFlashRows; rowCount++) { + /* Read row from hex file */ + /* linear address of row in flash */ + rowAddress = FLASH_ROW_BYTE_SIZE_HEX_FILE * rowCount; + + /* Extract 128-byte row from the hex-file from address: + "rowCount" into buffer - "rowData". */ + HEX_ReadRowData(&d->inf, rowCount, &rowData[0]); + + /* Read row from chip */ + for (i = 0; i < FLASH_ROW_BYTE_SIZE_HEX_FILE; i += 4) { + /* Read flash via AHB-interface */ + Read_IO(rowAddress + i, &flashData); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + chipData[i + 0] = (flashData >> 0) & 0xFF; + chipData[i + 1] = (flashData >> 8) & 0xFF; + chipData[i + 2] = (flashData >> 16) & 0xFF; + chipData[i + 3] = (flashData >> 24) & 0xFF; + } + + /* Compare the row data of HEX file with chip data */ + for (i = 0; i < FLASH_ROW_BYTE_SIZE_HEX_FILE; i++) { + if (chipData[i] != rowData[i]) { + swd_PacketAck = swd_PacketAck | + VERIFICATION_ERROR; + return FAILURE; + } + } + } + return SUCCESS; +} + +/****************************************************************************** + * Function Name: ProgramProtectionSettings + ******************************************************************************* + * + * Summary: + * This is Step 7 of the programming sequence. In this step, Chip protection + * settings and Row Protection settings are read from the HEX file and + * programmed to the specific loctions in the flash. + * + * Parameters: + * None + * + * Return: + * SUCCESS - Returns SUCCESS if function successfully writes the protection + * settings. + * FAILURE - Returns Failure if any of the intermediate step returns a fail + * message. + * + * Note: + * + ******************************************************************************/ +unsigned char ProgramProtectionSettings(struct hssp_data *d) +{ + unsigned char arrayID = 0; + unsigned char rowProtectionByteSize = 0; + unsigned char rowProtectionData[MAXIMUM_ROW_PROTECTION_BYTE_LENGTH]; + unsigned char chipProtectionData_Hex; + unsigned short numOfFlashRows = 0; + unsigned long parameter1 = 0; + + /* Get total number of flash rows to determine the size of + row protection data and arrayID */ + numOfFlashRows = GetFlashRowCount(); + + rowProtectionByteSize = numOfFlashRows / 8; + + arrayID = numOfFlashRows / ROWS_PER_ARRAY; + + HEX_ReadChipProtectionData(&d->inf, &chipProtectionData_Hex); + + HEX_ReadRowProtectionData(&d->inf, rowProtectionByteSize, + &rowProtectionData[0]); + + /* Load protection setting of current macro into volatile latch using + LoadLatch API */ + result = LoadLatch(d, arrayID, &rowProtectionData[0]); + if (result != SUCCESS) + return FAILURE; + + /* Program protection setting into supervisory row */ + parameter1 = (unsigned long)(((unsigned long)SROM_KEY1 << 0) + + (((unsigned long)SROM_KEY2 + + (unsigned long)SROM_CMD_WRITE_PROTECTION) + << 8) + + ((unsigned long)chipProtectionData_Hex << + 16)); + + /* Load parameter1 in CPUSS_SYSARG register */ + Write_IO(CPUSS_SYSARG, parameter1); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Request SROM call */ + Write_IO(CPUSS_SYSREQ, SROM_SYSREQ_BIT | SROM_CMD_WRITE_PROTECTION); + + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + /* Read status of the operation */ + result = PollSromStatus(); + if (result != SUCCESS) + return FAILURE; + + return SUCCESS; +} + +/****************************************************************************** + * Function Name: VerifyProtectionSettings + ******************************************************************************* + * + * Summary: + * This is Step 8 of the programming sequence. In this step, Chip protection + * settings and Row Protection settings are read from the HEX file and verified + * with the protection settings programmed in flash. + * + * Parameters: + * None + * + * Return: + * SUCCESS - Returns SUCCESS if function successfully verifies the protection + * settings. + * FAILURE - Returns Failure if any of the intermediate step returns a fail + * message. + * + * Note: + * + ******************************************************************************/ +unsigned char VerifyProtectionSettings(struct hssp_data *d) +{ + unsigned long protectionData = 0; + unsigned long flashProtectionAddress = 0; + unsigned short numOfFlashRows = 0; + unsigned char chipProtectionData_Hex = 0; + unsigned char rowProtectionByteSize = 0; + unsigned char i; + unsigned char rowProtectionData[MAXIMUM_ROW_PROTECTION_BYTE_LENGTH]; + unsigned char + rowProtectionFlashData[MAXIMUM_ROW_PROTECTION_BYTE_LENGTH]; + + numOfFlashRows = GetFlashRowCount(); + + rowProtectionByteSize = numOfFlashRows / 8; + + flashProtectionAddress = SFLASH_MACRO; + + /* Read Protection settings from hex-file */ + HEX_ReadRowProtectionData(&d->inf, rowProtectionByteSize, + &rowProtectionData[0]); + + /* Read Protection settings from silicon */ + for (i = 0; i < rowProtectionByteSize; i += 4) { + + Read_IO(flashProtectionAddress + i, &protectionData); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + rowProtectionFlashData[i + 0] = (protectionData >> 0) & 0xFF; + rowProtectionFlashData[i + 1] = (protectionData >> 8) & 0xFF; + rowProtectionFlashData[i + 2] = (protectionData >> 16) & 0xFF; + rowProtectionFlashData[i + 3] = (protectionData >> 24) & 0xFF; + } + + /* Compare hex and silicon's data */ + for (i = 0; i < rowProtectionByteSize; i++) { + + if (rowProtectionData[i] != rowProtectionFlashData[i]) { + /* Set the verification error bit for Flash + protection data mismatch and return failure */ + swd_PacketAck = swd_PacketAck | VERIFICATION_ERROR; + return FAILURE; + } + } + + /* Read Chip Level Protection from hex-file */ + HEX_ReadChipProtectionData(&d->inf, &chipProtectionData_Hex); + + /* Read Chip Level Protection from the silicon */ + Read_IO(SFLASH_CPUSS_PROTECTION, &protectionData); + if (swd_PacketAck != SWD_OK_ACK) + return FAILURE; + + chipProtectionData_Chip = (protectionData >> 24) & 0x0F; + if (chipProtectionData_Chip == CHIP_PROT_VIRGIN) + chipProtectionData_Chip = CHIP_PROT_OPEN; + else if (chipProtectionData_Chip == CHIP_PROT_OPEN) + chipProtectionData_Chip = CHIP_PROT_VIRGIN; + + /* Compare hex's and silicon's chip protection data */ + if (chipProtectionData_Chip != chipProtectionData_Hex) { + /* Set the verification error bit for Flash protection data + mismatch and return failure */ + swd_PacketAck = swd_PacketAck | VERIFICATION_ERROR; + return FAILURE; + } + + return SUCCESS; +} + +/****************************************************************************** + * Function Name: VerifyChecksum + ******************************************************************************* + * + * Summary: + * This is Step 9 of the programming sequence. In this step, Checksum of user + * data in flash is verified with the Checksum stored in the HEX File. This step + * uses the Checksum of privileged rows calculated in Step 4 get the checksum + * of user data in flash. + * + * Parameters: + * None + * + * Return: + * SUCCESS - Returns SUCCESS if function successfully verifies the checksum. + * FAILURE - Returns Failure if any of the intermediate step returns a fail + * message. + * + * Note: + * + ******************************************************************************/ +unsigned char VerifyChecksum(struct hssp_data *d) +{ + unsigned long checksum_All = 0; + unsigned short chip_Checksum = 0; + unsigned short checksumData = 0; + + /* Read the checksum of entire flash */ + result = ChecksumAPI(d, CHECKSUM_ENTIRE_FLASH, &checksum_All); + if (result != SUCCESS) + return FAILURE; + + /* Calculate checksum of user flash */ + chip_Checksum = (unsigned short)checksum_All - + (unsigned short)checksum_Privileged; + d->chip_cs = chip_Checksum; + + /* Read checksum from hex file */ + HEX_ReadChecksumData(&d->inf, &checksumData); + + /* Compare the checksum data of silicon and hex file */ + if (chip_Checksum != checksumData) { + swd_PacketAck = swd_PacketAck | VERIFICATION_ERROR; + return FAILURE; + } + + return SUCCESS; +} + +/****************************************************************************** + * Function Name: ReadHsspErrorStatus + ******************************************************************************* + * + * Summary: + * Returns the HSSP Error status in case of FAILURE return in any one of the + * programming steps. + * + * Parameters: + * None. + * + * Return: + * swd_PacketAck - Each bit of this 8-bit return value has a specific meaning. + * + * Note: + * Refer to the application note pdf for details on the Error status bit + * definitions + ******************************************************************************/ +unsigned char ReadHsspErrorStatus(void) +{ + return swd_PacketAck; +} + +/****************************************************************************** + * Function Name: ExitProgrammingMode + ******************************************************************************* + * + * Summary: + * Releases the target PSoC 4 device from Programming mode. + * + * Parameters: + * None. + * + * Return: + * None. + * + * Note: + * + ******************************************************************************/ +void ExitProgrammingMode(void) +{ + /* Drive the SWDIO, SWDCK outputs low */ + SetSwdckLow(); + SetSwdioLow(); + + /* Make SWDIO, SWDCK High-Z after completing Programming */ + SetSwdioHizInput(); + SetSwdckHizInput(); + + /* Generate active low rest pulse for 100 uS */ + SetXresCmosOutput(); + SetXresLow(); + DelayHundredUs(); + SetXresHigh(); +} + +/* [] END OF FILE */ diff --git a/drivers/misc/cypress/ProgrammingSteps.h b/drivers/misc/cypress/ProgrammingSteps.h new file mode 100644 index 000000000000..e31caa9e3fe7 --- /dev/null +++ b/drivers/misc/cypress/ProgrammingSteps.h @@ -0,0 +1,149 @@ +/****************************************************************************** +* File Name: ProgrammingSteps.h +* Version 1.0 +* +* Description: +* This header file contains high level definitions and function declarations +* associated with programming of the target PSoC 4 device +* +* Note: +* +******************************************************************************* +* Copyright (2013), Cypress Semiconductor Corporation. +******************************************************************************* +* This software is owned by Cypress Semiconductor Corporation (Cypress) and is +* protected by and subject to worldwide patent protection (United States and +* foreign), United States copyright laws and international treaty provisions. +* Cypress hereby grants to licensee a personal, non-exclusive, non-transferable +* license to copy, use, modify, create derivative works of, and compile the +* Cypress Source Code and derivative works for the sole purpose of creating +* custom software in support of licensee product to be used only in conjunction +* with a Cypress integrated circuit as specified in the applicable agreement. +* Any reproduction, modification, translation, compilation, or representation of +* this software except as specified above is prohibited without the express +* written permission of Cypress. +* +* Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH +* REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +* Cypress reserves the right to make changes without further notice to the +* materials described herein. Cypress does not assume any liability arising out +* of the application or use of any product or circuit described herein. Cypress +* does not authorize its products for use as critical components in life-support +* systems where a malfunction or failure may reasonably be expected to result in +* significant injury to the user. The inclusion of Cypress' product in a life- +* support systems application implies that the manufacturer assumes all risk of +* such use and in doing so indemnifies Cypress against all charges. Use may be +* limited by and subject to the applicable Cypress software license agreement. +******************************************************************************/ + +#ifndef __PROGRAMMINGSTEPS_H +#define __PROGRAMMINGSTEPS_H + +#include "cycapsense_hssp.h" +/****************************************************************************** +* Macro definitions +******************************************************************************/ + +/* Return value definitions for high level Programming functions */ +#ifndef FAILURE +#define FAILURE 0 +#endif + +#ifndef SUCCESS +#define SUCCESS (!FAILURE) +#endif + +/* Return value of PollSromStatus function */ +#define SROM_SUCCESS 0x01 + +/* Error codes returned by Swd_PacketAck whenever any top-level step returns a + failure status*/ + +/* This bit field is set if programmer fails to acquire the device in 1.5 ms */ +#define PORT_ACQUIRE_TIMEOUT_ERROR 0x10 + +/* This bit field is set if the SROM does not return the success status code + within the SROM Polling timeout duration*/ +#define SROM_TIMEOUT_ERROR 0x20 + +/* This bit field is set in case of JTAG ID mismatch or Flash data verification + mismatch or Checksum data mismatch */ +#define VERIFICATION_ERROR 0x40 + +/* This bit field is set if wrong transition of chip protection settings is + detected */ +#define TRANSITION_ERROR 0x80 + +/* Constants for Address Space of CPU */ +#if defined CY8C40xx_FAMILY +#define CPUSS_SYSREQ 0x40100004 +#define CPUSS_SYSARG 0x40100008 +#else +#define CPUSS_SYSREQ 0x40000004 +#define CPUSS_SYSARG 0x40000008 +#endif +#define TEST_MODE 0x40030014 +#define SRAM_PARAMS_BASE 0x20000100 +#define SFLASH_MACRO 0x0FFFF000 +#define SFLASH_CPUSS_PROTECTION 0x0FFFF07C + +/* SROM Constants */ +#define SROM_KEY1 0xB6 +#define SROM_KEY2 0xD3 +#define SROM_SYSREQ_BIT 0x80000000 +#define SROM_PRIVILEGED_BIT 0x10000000 +#define SROM_STATUS_SUCCEEDED 0xA0000000 +#define SROM_STATUS_FAILED 0xF0000000 + +/* SROM Commands */ +#define SROM_CMD_GET_SILICON_ID 0x00 +#define SROM_CMD_LOAD_LATCH 0x04 +#define SROM_CMD_PROGRAM_ROW 0x06 +#define SROM_CMD_ERASE_ALL 0x0A +#define SROM_CMD_CHECKSUM 0x0B +#define SROM_CMD_WRITE_PROTECTION 0x0D +#if defined CY8C40xx_FAMILY +#define SROM_CMD_SET_IMO_48MHZ 0x15 +#endif + +/* Chip Level Protection */ +#define CHIP_PROT_VIRGIN 0x00 +#define CHIP_PROT_OPEN 0x01 +#define CHIP_PROT_PROTECTED 0x02 +#define CHIP_PROT_KILL 0x04 + +/* Mask for status code returned by polling SROM */ +#define SROM_STATUS_SUCCESS_MASK 0xF0000000 + +/* Constant for computing checksum of entire flash */ +#define CHECKSUM_ENTIRE_FLASH 0x8000 + +/* Constant DAP ID of ARM Cortex M0 CPU */ +#define CM0_DAP_ID 0x0BC11477 +#define CM0PLUS_DAP_ID 0x0BC11477 + +/* Constant maximum number of rows in PSoC 4 */ +#define ROWS_PER_ARRAY 256 + +/****************************************************************************** +* Function Prototypes +******************************************************************************/ + +unsigned char DeviceAcquire(void); +unsigned char VerifySiliconId(struct hssp_data *d); +unsigned char EraseAllFlash(struct hssp_data *d); +unsigned char ChecksumPrivileged(struct hssp_data *d); +unsigned char ProgramFlash(struct hssp_data *d); +unsigned char VerifyFlash(struct hssp_data *d); +unsigned char ProgramProtectionSettings(struct hssp_data *d); +unsigned char VerifyProtectionSettings(struct hssp_data *d); +unsigned char VerifyChecksum(struct hssp_data *d); +unsigned char VerifySwRevision(struct hssp_data *d); +void ExitProgrammingMode(void); +unsigned char ReadHsspErrorStatus(void); +unsigned char ReadSromStatus(void); + +#endif /* __PROGRAMMINGSTEPS_H */ + +/* [] END OF FILE */ diff --git a/drivers/misc/cypress/SWD_PacketLayer.c b/drivers/misc/cypress/SWD_PacketLayer.c new file mode 100644 index 000000000000..07459ac3dd0f --- /dev/null +++ b/drivers/misc/cypress/SWD_PacketLayer.c @@ -0,0 +1,621 @@ +/****************************************************************************** +* File Name: SWD_PacketLayer.c +* Version 1.0 +* +* Description: +* This file provides the source code for the packet layer functions of the +* SWD protocol. This includes SWD Read packet, SWD Write packet. +* +* Owner: +* Tushar Rastogi, Application Engineer (tusr@cypress.com) +* +* Related Document: +* AN84858 - PSoC 4 Programming using an External Microcontroller (HSSP) +* +* Hardware Dependency: +* PSoC 5LP Development Kit - CY8CKIT-050 +* +* Code Tested With: +* PSoC Creator 2.2 +* ARM GCC 4.4.1 +* CY8CKIT-050 +* +* Note: +* The functions in SWD packet layer use the bit banging macros, functions +* in "SWD_PhysicalLayer.h" +* +******************************************************************************* +* Copyright (2013), Cypress Semiconductor Corporation. +******************************************************************************* +* This software is owned by Cypress Semiconductor Corporation (Cypress) and is +* protected by and subject to worldwide patent protection (United States and +* foreign), United States copyright laws and international treaty provisions. +* Cypress hereby grants to licensee a personal, non-exclusive, non-transferable +* license to copy, use, modify, create derivative works of, and compile the +* Cypress Source Code and derivative works for the sole purpose of creating +* custom software in support of licensee product to be used only in conjunction +* with a Cypress integrated circuit as specified in the applicable agreement. +* Any reproduction, modification, translation, compilation, or representation of +* this software except as specified above is prohibited without the express +* written permission of Cypress. +* +* Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH +* REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +* Cypress reserves the right to make changes without further notice to the +* materials described herein. Cypress does not assume any liability arising out +* of the application or use of any product or circuit described herein. Cypress +* does not authorize its products for use as critical components in life-support +* systems where a malfunction or failure may reasonably be expected to result in +* significant injury to the user. The inclusion of Cypress' product in a life- +* support systems application implies that the manufacturer assumes all risk of +* such use and in doing so indemnifies Cypress against all charges. Use may be +* limited by and subject to the applicable Cypress software license agreement. +******************************************************************************/ + +/****************************************************************************** +* Header file Inclusion +******************************************************************************/ +#include "SWD_PacketLayer.h" + +/* "SWD_PhysicalLayer.h" file contains the bit banging routines for + programming */ +#include "SWD_PhysicalLayer.h" + +/****************************************************************************** +* Global Variable definitions +******************************************************************************/ +/* Stores the 8-bit SWD Packet header */ +unsigned char swd_PacketHeader; + +/* Stores the 3-bit SWD Packet ACK data */ +unsigned char swd_PacketAck; + +/* 4-byte Read, Write packet data */ +unsigned char swd_PacketData[DATA_BYTES_PER_PACKET]; + +/****************************************************************************** +* Function Definitions +******************************************************************************/ + +/****************************************************************************** +* Function Name: Swd_SendByte +******************************************************************************* +* +* Summary: +* Sends a byte of data on the SWD lines (SWDIO, SWDCK) +* +* Parameters: +* txByte: Data byte to be sent by host on SWDIO line (Least significant bit +* sent first) +* +* Return: +* None. +* +* Note: +* This function is called for sending SWD header data in SWD Read/Write +* packets, and also to send data in a SWD Write packet. +* +******************************************************************************/ +static void Swd_SendByte(unsigned char txByte) +{ + unsigned char loop = 0; + + /* Loop for 8-bits of a byte */ + for (loop = 0; loop < 8; loop++) { + if (txByte & LSB_BIT_MASK) { /* Send a '1' */ + SetSwdckLow(); + SetSwdioHigh(); + SetSwdckHigh(); + } else { /* Send a '0' */ + + SetSwdckLow(); + SetSwdioLow(); + SetSwdckHigh(); + } + + txByte = txByte >> 1; /* Make the next bit to send as LS bit */ + } +} + +/****************************************************************************** +* Function Name: Swd_ReceiveByte +******************************************************************************* +* +* Summary: +* Receives a byte of data on the SWD lines (SWDIO, SWDCK) +* +* Parameters: +* None. +* +* Return: +* rxByte: Data byte received by host on SWDIO line (Least significant bit is +* first received bit) +* +* Note: +* This function is called for reading data bytes in a SWD Read Packet +* +******************************************************************************/ +static unsigned char Swd_ReceiveByte(void) +{ + unsigned char loop = 0; + unsigned char rxBit = 0; + unsigned char rxByte = 0; + + /* Loop for 8-bits of a byte */ + for (loop = 0; loop < 8; loop++) { + SWDCK_OUTPUT_LOW; + + /* Read the SWDIO input line */ + rxBit = ReadSwdio(); + + SWDCK_OUTPUT_HIGH; + + rxByte = rxByte >> 1; + + if (rxBit) { /* Received a '1' */ + rxByte = rxByte | MSB_BIT_MASK; + } else { /* Received a '0' */ + + rxByte = rxByte & (~MSB_BIT_MASK); + } + + } + + return rxByte; +} + +/****************************************************************************** +* Function Name: Swd_FirstTurnAroundPhase +******************************************************************************* +* +* Summary: +* Generates one SWDCK clock cycle for Turnaround phase of a SWD packet. +* SWDIO drive mode is changed to High-Z drive mode so that the host can read +* the data on the SWDIO. +* +* Parameters: +* None. +* +* Return: +* None. +* +* Note: +* This function is called during SWD Read/Write packets +* +******************************************************************************/ +static void Swd_FirstTurnAroundPhase(void) +{ + /* Change to High-Z drive mode for host to read the SWDIO line */ + SetSwdioHizInput(); + SetSwdckLow(); + SetSwdckHigh(); +} + +/****************************************************************************** +* Function Name: Swd_SecondTurnAroundPhase +******************************************************************************* +* +* Summary: +* Generates one SWDCK clock cycle for Turnaround phase of a SWD packet. +* SWDIO drive mode is changed to CMOS output drive mode so that the host can +* write data on SWDIO. +* +* Parameters: +* None. +* +* Return: +* None. +* +* Note: +* This function is called during SWD Read/Write packets +* +******************************************************************************/ +static void Swd_SecondTurnAroundPhase(void) +{ + /* Change to CMOS output drive mode for host to write to SWDIO */ + SetSwdioCmosOutput(); + SetSwdckLow(); + SetSwdckHigh(); +} + +/****************************************************************************** +* Function Name: Swd_GetAckSegment +******************************************************************************** +* +* Summary: +* Gets the 3-bit ACK response in a SWD packet +* +* Parameters: +* None. +* +* Return: +* ack: +* 3-bit ACK response is returned as a byte. Three possible return values are: +* 0x01 - SWD_OK_ACK +* 0x02 - SWD_WAIT_ACK +* 0x04 - SWD_FAULT_ACK +* Any other return value - Undefined ACK code. +* Treat it similar to SWD_FAULT_ACK and abort operation. +* +* Note: +* This function is called during SWD Read/Write packets +* +******************************************************************************/ +static unsigned char Swd_GetAckSegment(void) +{ + unsigned char ack = 0; + unsigned char receiveBit = 0; + unsigned char loop = 0; + + /* ACK bits are received lsb bit first */ + for (loop = 0; loop < NUMBER_OF_ACK_BITS; loop++) { + SetSwdckLow(); + + /* Store the ACK bit received */ + receiveBit = ReadSwdio(); + SetSwdckHigh(); + + /* Concatenate the ACK bit with ACK data byte */ + ack = ack | (receiveBit << loop); + } + + return ack; +} + +/****************************************************************************** +* Function Name: Swd_SendParity +******************************************************************************* +* +* Summary: +* Sends the 1-bit parity data on SWD lines +* +* Parameters: +* parity: +* 1-bit parity data received as a byte (either '1' or '0') +* +* Return: +* None. +* +* Note: +* This function is called during SWD Write packet +* +******************************************************************************/ +static void Swd_SendParity(unsigned char parity) +{ + /* Make the clock low, Send SWDIO data, Make Clock high */ + + SetSwdckLow(); + if (parity) + SetSwdioHigh(); + else + SetSwdioLow(); + + SetSwdckHigh(); +} + +/****************************************************************************** +* Function Name: Swd_ReceiveParity +******************************************************************************* +* +* Summary: +* Receives the 1-bit parity data on SWD lines +* +* Parameters: +* None. +* +* Return: +* parity: +* 1-bit parity data returned as a byte (either '1' or '0') +* +* Note: +* This function is called during SWD Read packet +* +******************************************************************************/ +static unsigned char Swd_ReceiveParity(void) +{ + unsigned char parity; + + /* Make the clock low, Read SWDIO data, Make Clock high */ + + SetSwdckLow(); + parity = ReadSwdio(); + SetSwdckHigh(); + + return parity; +} + +/****************************************************************************** +* Function Name: Swd_SendDummyClocks +******************************************************************************* +* +* Summary: +* Sends three SWDCK clock cycles with SWDIO line held low +* +* Parameters: +* None. +* +* Return: +* None. +* +* Note: +* This function is called during SWD Read/Write packets. +* +******************************************************************************/ +void Swd_SendDummyClocks(void) +{ + unsigned char loop; + + /* Send three SWDCK clock cycles with SWDIO line held low */ + SetSwdioLow(); + for (loop = 0; loop < NUMBER_OF_DUMMY_SWD_CLOCK_CYCLES; loop++) { + SetSwdckLow(); + SetSwdckHigh(); + } +} + +/****************************************************************************** +* Function Name: Swd_CountOneBits +******************************************************************************* +* +* Summary: +* Counts the number of 1's in a byte +* +* Parameters: +* dataByte: +* Byte for which the number of 1's need to be found +* +* Return: +* count: +* Number of 1's present in DataByte +* +* Note: +* This function is used for computing parity of data +* +******************************************************************************/ +static unsigned char Swd_CountOneBits(unsigned char dataByte) +{ + unsigned char count = 0; + unsigned char i; + + /* Loop for 8-bits of a byte */ + /* Increment count variable if LS bit is set */ + for (i = 0; i < 8; i++) { + if (dataByte & LSB_BIT_MASK) + count++; + + dataByte = dataByte >> 1; + } + + return count; +} + +/****************************************************************************** +* Function Name: Swd_ComputeDataParity +******************************************************************************* +* +* Summary: +* Computes Even parity of 4-byte data +* +* Parameters: +* swd_PacketData[DATA_BYTES_PER_PACKET] - Global variable that holds the data +* +* Return: +* Even parity bit +* 0x01 - Parity is '1' if 4-byte data has an odd number of 1's +* 0x00 - Parity is '0' if 4-byte data has an even number of 1's +* +* Note: +* This function is called during SWD Read/Write packets +* +******************************************************************************/ +static unsigned char Swd_ComputeDataParity(void) +{ + unsigned char count = 0; + unsigned char i; + + /* Count number of 1's in 4-byte data */ + for (i = 0; i < DATA_BYTES_PER_PACKET; i++) + count = count + Swd_CountOneBits(swd_PacketData[i]); + + /* Return even parity. If Lsb bit is 1, it implies the number of 1's + is odd, and hence the even parity bit is set. For even number + of 1's, parity bit is 0 */ + return count & LSB_BIT_MASK; +} + +/****************************************************************************** +* Function Name: Swd_ReadPacket +******************************************************************************* +* +* Summary: +* Sends a single SWD Read packet +* +* Parameters: +* swd_PacketHeader - Global variable that holds the 8-bit header data for packet +* +* Return: +* swd_PacketData[DATA_BYTES_PER_PACKET] - Global variable that holds the 4-byte +* data that has been read +* +* swd_PacketAck - Global variable that holds the status of the SWD transaction +* ACK response is stored as a byte. Three possible ACK return values are: +* 0x01 - SWD_OK_ACK +* 0x02 - SWD_WAIT_ACK +* 0x04 - SWD_FAULT_ACK +* 0x09 - SWD_OK_ACK, but Parity error in data received +* Any other ACK value - Undefined ACK code. +* Treat it similar to SWD_FAULT_ACK and abort +* operation. +* +* Note: +* 1.)Swd_RawReadPacket() is called during SPC Polling operations when the host +* must read the SPC status register continuously till a Programming +* operation is completed or data is ready to be read. +* +* 2.)This function is called duing continuous multi byte read operations from +* the SPC_DATA register +* +* 3.)This function is called during IDCODE instruction for reading the Device +* JTAG ID +* +* 4.)This function is not called during normal SWD read transactions. To read +* a register data, two SWD Read packets must be sent (call this function +* twice). This method of reading twice has been implemented in +* SWD_ReadPacket() function which will be called during normal read +* transactions. +* +******************************************************************************/ +void Swd_ReadPacket(void) +{ + unsigned char parity; + unsigned char loop = 0; + unsigned char i; + + do { + /* 8-bit Header data */ + Swd_SendByte(swd_PacketHeader); + + /* First Turnaround phase */ + Swd_FirstTurnAroundPhase(); + + /* Get the 3-bit ACK data */ + swd_PacketAck = Swd_GetAckSegment(); + + /* Read 4-byte data and store in Global + array swd_PacketData[] */ + for (i = 0; i < DATA_BYTES_PER_PACKET; i++) + swd_PacketData[i] = Swd_ReceiveByte(); + + /* Parity phase */ + parity = Swd_ReceiveParity(); + + /* Second Turnaround phase */ + Swd_SecondTurnAroundPhase(); + + /* Dummy clock phase since clock is not free running */ + Swd_SendDummyClocks(); + + /* Repeat for a WAIT ACK till timeout loop expires */ + loop++; + + } while ((swd_PacketAck == SWD_WAIT_ACK) + && (loop < NUMBER_OF_WAIT_ACK_LOOPS)); + + /* For a OK ACK, check the parity bit received with parity computed */ + if (swd_PacketAck == SWD_OK_ACK) { + if (parity != Swd_ComputeDataParity()) { + /* Set the Parity error bit in ACK code */ + swd_PacketAck = swd_PacketAck | SWD_PARITY_ERROR; + } + } + + /* Swd_PacketAck global variable holds the status of + the SWD transaction */ +} + +/****************************************************************************** +* Function Name: Swd_WritePacket +******************************************************************************* +* +* Summary: +* Sends a single SWD Write packet +* +* Parameters: +* swd_PacketHeader - Global variable that holds the 8-bit header data +* for packet +* swd_PacketData[DATA_BYTES_PER_PACKET] - Global variable that holds the data +* to be sent +* +* Return: +* swd_PacketAck - Global variable that holds the status of the SWD transaction +* ACK response is stored as a byte. Three possible ACK return values are: +* 0x01 - SWD_OK_ACK +* 0x02 - SWD_WAIT_ACK +* 0x04 - SWD_FAULT_ACK +* Any other ACK value - Undefined ACK code. +* Treat it similar to SWD_FAULT_ACK and abort operation. +* +* Note: +* This function is called for Address Write, Data Write operations +* +******************************************************************************/ +void Swd_WritePacket(void) +{ + unsigned char parity; + unsigned char loop = 0; + unsigned char i; + + /* Compute Even parity for 4-byte data */ + parity = Swd_ComputeDataParity(); + + do { + /* 8-bit Header data */ + Swd_SendByte(swd_PacketHeader); + + /* First Turnaround phase */ + Swd_FirstTurnAroundPhase(); + + /* Get the 3-bit ACK data */ + swd_PacketAck = Swd_GetAckSegment(); + + /* Second Turnaround phase */ + Swd_SecondTurnAroundPhase(); + + /* Send 4-byte data stored in Global array swd_PacketData[] */ + for (i = 0; i < DATA_BYTES_PER_PACKET; i++) + Swd_SendByte(swd_PacketData[i]); + + /* Parity phase */ + Swd_SendParity(parity); + + /* Dummy clock phase since clock is not free running */ + Swd_SendDummyClocks(); + + loop++; + + /* Repeat for a WAIT ACK till timeout loop expires */ + + } while ((swd_PacketAck == SWD_WAIT_ACK) + && (loop < NUMBER_OF_WAIT_ACK_LOOPS)); + + /* swd_PacketAck global variable holds the status of + the SWD transaction */ +} + +/****************************************************************************** +* Function Name: Swd_LineReset +******************************************************************************* +* +* Summary: +* Resets the SWD line by sending 51 SWDCK clock cycles with SWDIO line high +* +* Parameters: +* None +* +* Return: +* None +* +* Note: +* This function is called as part of SWD switching sequence +* +******************************************************************************/ +void Swd_LineReset(void) +{ + unsigned char i; + + /* To reset SWD line, clock more than 50 SWDCK clock cycles with SWDIO + high. */ + + SWDIO_OUTPUT_HIGH; + + for (i = 0; i < NUMBER_OF_SWD_RESET_CLOCK_CYCLES; i++) { + SWDCK_OUTPUT_LOW; + SWDCK_OUTPUT_HIGH; + } + SWDCK_OUTPUT_LOW; + SWDIO_OUTPUT_LOW; + SWDCK_OUTPUT_HIGH; +} + +/* []END OF FILE */ diff --git a/drivers/misc/cypress/SWD_PacketLayer.h b/drivers/misc/cypress/SWD_PacketLayer.h new file mode 100644 index 000000000000..7add4123bcf5 --- /dev/null +++ b/drivers/misc/cypress/SWD_PacketLayer.h @@ -0,0 +1,100 @@ +/****************************************************************************** +* File Name: SWD_PacketLayer.h +* Version 1.0 +* +* Description: +* This header file contains the constant definitions, function declarations +* associated with the packet layer of the SWD protocol. +* +* Note: +* +******************************************************************************* +* Copyright (2013), Cypress Semiconductor Corporation. +******************************************************************************* +* This software is owned by Cypress Semiconductor Corporation (Cypress) and is +* protected by and subject to worldwide patent protection (United States and +* foreign), United States copyright laws and international treaty provisions. +* Cypress hereby grants to licensee a personal, non-exclusive, non-transferable +* license to copy, use, modify, create derivative works of, and compile the +* Cypress Source Code and derivative works for the sole purpose of creating +* custom software in support of licensee product to be used only in conjunction +* with a Cypress integrated circuit as specified in the applicable agreement. +* Any reproduction, modification, translation, compilation, or representation of +* this software except as specified above is prohibited without the express +* written permission of Cypress. +* +* Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH +* REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +* Cypress reserves the right to make changes without further notice to the +* materials described herein. Cypress does not assume any liability arising out +* of the application or use of any product or circuit described herein. Cypress +* does not authorize its products for use as critical components in life-support +* systems where a malfunction or failure may reasonably be expected to result in +* significant injury to the user. The inclusion of Cypress' product in a life- +* support systems application implies that the manufacturer assumes all risk of +* such use and in doing so indemnifies Cypress against all charges. Use may be +* limited by and subject to the applicable Cypress software license agreement. +******************************************************************************/ + +#ifndef __SWD_PACKETLAYER_H +#define __SWD_PACKETLAYER_H + +/****************************************************************************** +* Constant definitions +******************************************************************************/ + +/* Data size of a SWD Packet in bytes */ +#define DATA_BYTES_PER_PACKET 4 + +/* Number of dummy SWDCK clocks at end of each SWD packet. + Required for bit banging programmers where clock is not free running */ +#define NUMBER_OF_DUMMY_SWD_CLOCK_CYCLES 3 + +/* Minimum number of SWDCK clock cycles to reset the SWD line state */ +#define NUMBER_OF_SWD_RESET_CLOCK_CYCLES 51 + +/* ACK response in a SWD packet is a 3-bit value */ +#define NUMBER_OF_ACK_BITS 3 + +/* SWD ACK response meanings. +* Parity error definition is not part of SWD ACK response from target PSoC 4. +* The actual ACK response is only 3-bit length. The parity error bit is +* defined as fourth bit by host application. It is generated by host if there +* is a parity error in the SWD Read packet data from target PSoC 4 */ +#define SWD_OK_ACK 0x01 +#define SWD_WAIT_ACK 0x02 +#define SWD_FAULT_ACK 0x04 +#define SWD_PARITY_ERROR 0x08 + +/* Maximum SWD packet loop timeout for SWD_WAIT_ACK response */ +#define NUMBER_OF_WAIT_ACK_LOOPS 5 + +/* Mask value to define Most significant bit (MSb) in a Byte */ +#define MSB_BIT_MASK 0x80 +/* Mask value to define Least significant bit (LSb) in a Byte */ +#define LSB_BIT_MASK 0x01 + +/****************************************************************************** +* Global Variable declaration +******************************************************************************/ +/* The below global variables are accessed by the upper layer files to create + SWD packet header, ACK, data */ + +extern unsigned char swd_PacketHeader; +extern unsigned char swd_PacketAck; +extern unsigned char swd_PacketData[DATA_BYTES_PER_PACKET]; + +/****************************************************************************** +* Function Prototypes +******************************************************************************/ +/* The below public fuctions are called by the upper layer files to send SWD + packets */ +void Swd_WritePacket(void); +void Swd_ReadPacket(void); +void Swd_LineReset(void); +void Swd_SendDummyClocks(void); + +#endif /* __SWD_PACKETLAYER_H */ + +/* [] END OF FILE */ diff --git a/drivers/misc/cypress/SWD_PhysicalLayer.h b/drivers/misc/cypress/SWD_PhysicalLayer.h new file mode 100644 index 000000000000..7d5f0b8cbe41 --- /dev/null +++ b/drivers/misc/cypress/SWD_PhysicalLayer.h @@ -0,0 +1,180 @@ +/****************************************************************************** + * File Name: SWD_PhysicalLayer.h + * Version 1.0 + * + * Description: + * This header file contains the macro definitions, function declarations for + * the physical layer of the SWD protocol. These include - + * a.) Setting a output pin high or low + * b.) Reading the logic level of an input pin + * c.) Configuring the drive mode of the programming pin + * + * The pin manipulation routines are defined both as macros and functions. + * The macros are used in "Step 1. DeviceAcquire()" as the function has strict + * timing requirements for execution. Using the macros instead of fuctions + * reduces execution overhead. + * + * The pin manipulation functions are used instead of macros in all other + * places to save code space. + * + * Note: + * The macros, functions defined here are specific to PSoC 5LP Host Programmer. + * Modify them as applicable for your Host Programmer. + * + ******************************************************************************* + * Copyright (2013), Cypress Semiconductor Corporation. + ******************************************************************************* + * This software is owned by Cypress Semiconductor Corporation (Cypress) and is + * protected by and subject to worldwide patent protection (United States and + * foreign), United States copyright laws and international treaty provisions. + * Cypress hereby grants to licensee a personal, non-exclusive, non-transferable + * license to copy, use, modify, create derivative works of, and compile the + * Cypress Source Code and derivative works for the sole purpose of creating + * custom software in support of licensee product to be used only in conjunction + * with a Cypress integrated circuit as specified in the applicable agreement. + * Any reproduction, modification, translation, compilation, or representation of + * this software except as specified above is prohibited without the express + * written permission of Cypress. + * + * Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH + * REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * Cypress reserves the right to make changes without further notice to the + * materials described herein. Cypress does not assume any liability arising out + * of the application or use of any product or circuit described herein. Cypress + * does not authorize its products for use as critical components in life-support + * systems where a malfunction or failure may reasonably be expected to result in + * significant injury to the user. The inclusion of Cypress' product in a life- + * support systems application implies that the manufacturer assumes all risk of + * such use and in doing so indemnifies Cypress against all charges. Use may be + * limited by and subject to the applicable Cypress software license agreement. + ******************************************************************************/ + +#ifndef __SWD_PHYSICALLAYER_H +#define __SWD_PHYSICALLAYER_H + +/* Host programmer registers, mask values are defined in "RegisterDefines.h" */ + +#include +#include +#include +#include "cycapsense_hssp.h" + +extern struct cycapsense_ctrl_data *ctrl_data; + +#define HSSP_CLK (ctrl_data->hssp_d.c_gpio) +#define HSSP_DAT (ctrl_data->hssp_d.d_gpio) +#define HSSP_RST (ctrl_data->hssp_d.rst_gpio) +#define HSSP_CLK_OFF (ctrl_data->hssp_d.c_offset) +#define HSSP_DAT_OFF (ctrl_data->hssp_d.d_offset) +#define HSSP_CLK_GROUP_OFF (ctrl_data->hssp_d.c_group_offset) +#define HSSP_DAT_GROUP_OFF (ctrl_data->hssp_d.d_group_offset) +#define MSM_TLMM_BASE (ctrl_data->hssp_d.gpio_base) + +#define GPIO_IN_OUT(n) (MSM_TLMM_BASE + (0x1000 * n)) + +static inline void hssp_set_clk_gpio(int gpio, int offset, int value) +{ + if (ctrl_data->hssp_d.raw_mode) { + writel_relaxed(value ? 2 : 0, (GPIO_IN_OUT(offset) + HSSP_CLK_GROUP_OFF)); + } else + gpio_set_value(gpio, value); +} +static inline void hssp_set_dat_gpio(int gpio, int offset, int value) +{ + if (ctrl_data->hssp_d.raw_mode) { + writel_relaxed(value ? 2 : 0, (GPIO_IN_OUT(offset) + HSSP_DAT_GROUP_OFF)); + } else + gpio_set_value(gpio, value); +} + + +static inline int hssp_get_gpio(int gpio, int offset) +{ + if (ctrl_data->hssp_d.raw_mode) + return !!(readl_relaxed_no_log((GPIO_IN_OUT(offset) + HSSP_DAT_GROUP_OFF)) & 0x1); + else + return gpio_get_value(gpio); +} + +/***************************** USER ATTENTION REQUIRED ************************ + ***************************** HOST PROCESSOR SPECIFIC ************************* + **************** Macros for Host Pin Drive mode configuration ***************** + * + * Uses the register definitions, mask values in "RegisterDefines.h" to + * configure the pin drive mode + * + * SWDIO pin on host side - CMOS output (host writes data to target PSoC 4), + * High Z digital input (host reads data from target + * PSoC 4 or when host is not programming target + * PSoC 4 (idle)) + * + * SWDCK pin on host side - CMOS output (when host is programming target PSoC 4), + * High Z digital input (when host is not programming + * target PSoC 4 (idle)) + * + * XRES pin on host side - CMOS output (when host is programming target PSoC 4) + * High Z digital input (when host is not programming + * target PSoC 4 (idle)) + * + * Modify these as applicable to your Host Programmer + ******************************************************************************/ + +#define SWDIO_DRIVEMODE_HIGHZIN SetSwdioHizInput() +#define SWDIO_DRIVEMODE_CMOSOUT SetSwdioCmosOutput() + +#define SWDCK_DRIVEMODE_HIGHZIN SetSwdckHizInput() +#define SWDCK_DRIVEMODE_CMOSOUT SetSwdckCmosOutput() + +#define XRES_DRIVEMODE_CMOSOUT SetXresCmosOutput() +#define XRES_DRIVEMODE_HIGHZIN SetXresHizInput() + +/***************************** USER ATTENTION REQUIRED ************************ + ***************************** HOST PROCESSOR SPECIFIC ************************* + **************** Macros for driving output pins on host side ****************** + * + * Uses the register definitions, mask values in "RegisterDefines.h" to drive + * output pins either to logic high (suffixed by 'HIGH') or + * logic low (suffixed by 'LOW') + * + * Modify these as applicable to your Host Programmer + ******************************************************************************/ + +#define SWDIO_OUTPUT_HIGH SetSwdioHigh() +#define SWDIO_OUTPUT_LOW SetSwdioLow() + +#define SWDCK_OUTPUT_HIGH SetSwdckHigh() +#define SWDCK_OUTPUT_LOW SetSwdckLow() + +#define XRES_OUTPUT_HIGH SetXresHigh() +#define XRES_OUTPUT_LOW SetXresLow() + +/****************************************************************************** + * Function Prototypes + ******************************************************************************/ +/* The below fuctions are for pin manipulation, and their definitions in + * SWD_PhysicalLayer.c are the same as the corresponding macro defitnions + * above. */ + +#define SetSwdckHigh() hssp_set_clk_gpio(HSSP_CLK, HSSP_CLK_OFF, 1) +#define SetSwdckLow() hssp_set_clk_gpio(HSSP_CLK, HSSP_CLK_OFF, 0) + +#define SetSwdckCmosOutput() gpio_direction_output(HSSP_CLK, 0) +#define SetSwdckHizInput() gpio_direction_input(HSSP_CLK) + +#define SetSwdioHigh() hssp_set_dat_gpio(HSSP_DAT, HSSP_DAT_OFF, 1) +#define SetSwdioLow() hssp_set_dat_gpio(HSSP_DAT, HSSP_DAT_OFF, 0) + +#define SetSwdioCmosOutput() gpio_direction_output(HSSP_DAT, 0) +#define ReadSwdio() hssp_get_gpio(HSSP_DAT, HSSP_DAT_OFF) + +#define SetSwdioHizInput() gpio_direction_input(HSSP_DAT) + +#define SetXresHigh() gpio_set_value(HSSP_RST, 1) +#define SetXresLow() gpio_set_value(HSSP_RST, 0) +#define SetXresCmosOutput() gpio_direction_output(HSSP_RST, 0) +#define SetXresHizInput() gpio_direction_input(HSSP_RST) + +#endif /* __SWD_PHYSICALLAYER_H */ + +/*[] END OF FILE */ diff --git a/drivers/misc/cypress/SWD_UpperPacketLayer.c b/drivers/misc/cypress/SWD_UpperPacketLayer.c new file mode 100644 index 000000000000..28dd4b9d0ca8 --- /dev/null +++ b/drivers/misc/cypress/SWD_UpperPacketLayer.c @@ -0,0 +1,182 @@ +/****************************************************************************** +* File Name: Swd_UpperPacketLayer.c +* Version 1.0 +* +* Description: +* This file provides the source code for function associated with the upper +* packet layer of the SWD protocol. +* +* Owner: +* Tushar Rastogi, Application Engineer (tusr@cypress.com) +* +* Related Document: +* AN84858 - PSoC 4 Programming using an External Microcontroller (HSSP) +* +* Hardware Dependency: +* PSoC 5LP Development Kit - CY8CKIT-050 +* +* Code Tested With: +* PSoC Creator 2.2 +* ARM GCC 4.4.1 +* CY8CKIT-050 +* +******************************************************************************* +* Copyright (2013), Cypress Semiconductor Corporation. +******************************************************************************* +* This software is owned by Cypress Semiconductor Corporation (Cypress) and is +* protected by and subject to worldwide patent protection (United States and +* foreign), United States copyright laws and international treaty provisions. +* Cypress hereby grants to licensee a personal, non-exclusive, non-transferable +* license to copy, use, modify, create derivative works of, and compile the +* Cypress Source Code and derivative works for the sole purpose of creating +* custom software in support of licensee product to be used only in conjunction +* with a Cypress integrated circuit as specified in the applicable agreement. +* Any reproduction, modification, translation, compilation, or representation of +* this software except as specified above is prohibited without the express +* written permission of Cypress. +* +* Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH +* REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +* Cypress reserves the right to make changes without further notice to the +* materials described herein. Cypress does not assume any liability arising out +* of the application or use of any product or circuit described herein. Cypress +* does not authorize its products for use as critical components in life-support +* systems where a malfunction or failure may reasonably be expected to result in +* significant injury to the user. The inclusion of Cypress' product in a life- +* support systems application implies that the manufacturer assumes all risk of +* such use and in doing so indemnifies Cypress against all charges. Use may be +* limited by and subject to the applicable Cypress software license agreement. + +******************************************************************************/ + +/****************************************************************************** +* Header file Inclusion +******************************************************************************/ +#include "SWD_PacketLayer.h" +#include "SWD_UpperPacketLayer.h" + +/****************************************************************************** +* Function Definitions +******************************************************************************/ + +/****************************************************************************** +* Function Name: Read_DAP +******************************************************************************* +* +* Summary: +* Reads the Swd_DAP_Register and stores the returned 32 bit value +* +* Parameters: +* swd_DAP_Register - MACROS for reading Debug Port and Access Port Register +* data_32 - 32-bit variable containing the returned value +* +* Return: +* None +* +******************************************************************************/ +void Read_DAP(unsigned char swd_DAP_Register, unsigned long *data_32) +{ + swd_PacketHeader = swd_DAP_Register; + + Swd_ReadPacket(); + if (swd_PacketAck != SWD_OK_ACK) + return; + + *data_32 = + (((unsigned long)swd_PacketData[3] << 24) | + ((unsigned long)swd_PacketData[2] << 16) | ((unsigned long) + swd_PacketData[1] << 8) + | ((unsigned long)swd_PacketData[0])); +} + +/****************************************************************************** +* Function Name: Write_DAP +******************************************************************************* +* +* Summary: +* Writes a 32-bit value to swd_DAP_Register +* +* Parameters: +* swd_DAP_Register - MACROS for writing to Debug Port and Access Port Register +* data_32 - 32-bit variable containing the value to be written +* +* Return: +* None +* +******************************************************************************/ +void Write_DAP(unsigned char swd_DAP_Register, unsigned long data_32) +{ + swd_PacketHeader = swd_DAP_Register; + + swd_PacketData[3] = (unsigned char)(data_32 >> 24); + swd_PacketData[2] = (unsigned char)(data_32 >> 16); + swd_PacketData[1] = (unsigned char)(data_32 >> 8); + swd_PacketData[0] = (unsigned char)(data_32); + + Swd_WritePacket(); +} + +/****************************************************************************** +* Function Name: Read_IO +******************************************************************************* +* +* Summary: +* Reads 32-bit date from specified address of CPU address space. +* Returns OK ACK if all SWD transactions succeeds (ACKed). +* +* Parameters: +* addr_32 - Address from which the data has to be read from CPU address space +* data_32 - 32-bit variable containing the read value +* +* Return: +* Swd_PacketAck - Acknowledge packet for the last SWD transaction +* +*******************************************************************************/ +unsigned char Read_IO(unsigned long addr_32, unsigned long *data_32) +{ + Write_DAP(DPACC_AP_TAR_WRITE, addr_32); + if (swd_PacketAck != SWD_OK_ACK) + return swd_PacketAck; + + Read_DAP(DPACC_AP_DRW_READ, data_32); + if (swd_PacketAck != SWD_OK_ACK) + return swd_PacketAck; + + Read_DAP(DPACC_AP_DRW_READ, data_32); + if (swd_PacketAck != SWD_OK_ACK) + return swd_PacketAck; + + return swd_PacketAck; +} + +/****************************************************************************** +* Function Name: Write_IO +******************************************************************************* +* +* Summary: +* Writes 32-bit data into specified address of CPU address space. +* Returns “true” if all SWD transactions succeeded (ACKed). +* +* Parameters: +* addr_32 - Address at which the data has to be written in CPU address space +* data_32 - 32-bit variable containing the value to be written +* +* Return: +* swd_PacketAck - Acknowledge packet for the last SWD transaction +* +******************************************************************************/ +unsigned char Write_IO(unsigned long addr_32, unsigned long data_32) +{ + Write_DAP(DPACC_AP_TAR_WRITE, addr_32); + if (swd_PacketAck != SWD_OK_ACK) + return swd_PacketAck; + + Write_DAP(DPACC_AP_DRW_WRITE, data_32); + if (swd_PacketAck != SWD_OK_ACK) + return swd_PacketAck; + + return swd_PacketAck; +} + +/* [] END OF FILE */ diff --git a/drivers/misc/cypress/SWD_UpperPacketLayer.h b/drivers/misc/cypress/SWD_UpperPacketLayer.h new file mode 100644 index 000000000000..2383c9e62cd9 --- /dev/null +++ b/drivers/misc/cypress/SWD_UpperPacketLayer.h @@ -0,0 +1,72 @@ +/****************************************************************************** +* File Name: SWD_UpperPacketLayer.h +* Version 1.0 +* +* Description: +* This header file contains the constant definitions, function declarations +* associated with the upper packet layer of the SWD protocol. +* +* Note: +* +******************************************************************************* +* Copyright (2013), Cypress Semiconductor Corporation. +******************************************************************************* +* This software is owned by Cypress Semiconductor Corporation (Cypress) and is +* protected by and subject to worldwide patent protection (United States and +* foreign), United States copyright laws and international treaty provisions. +* Cypress hereby grants to licensee a personal, non-exclusive, non-transferable +* license to copy, use, modify, create derivative works of, and compile the +* Cypress Source Code and derivative works for the sole purpose of creating +* custom software in support of licensee product to be used only in conjunction +* with a Cypress integrated circuit as specified in the applicable agreement. +* Any reproduction, modification, translation, compilation, or representation of +* this software except as specified above is prohibited without the express +* written permission of Cypress. +* +* Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH +* REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +* Cypress reserves the right to make changes without further notice to the +* materials described herein. Cypress does not assume any liability arising out +* of the application or use of any product or circuit described herein. Cypress +* does not authorize its products for use as critical components in life-support +* systems where a malfunction or failure may reasonably be expected to result in +* significant injury to the user. The inclusion of Cypress' product in a life- +* support systems application implies that the manufacturer assumes all risk of +* such use and in doing so indemnifies Cypress against all charges. Use may be +* limited by and subject to the applicable Cypress software license agreement. +******************************************************************************/ + +#ifndef __SWD_UPPERPACKETLAYER_H +#define __SWD_UPPERPACKETLAYER_H + +/****************************************************************************** +* Constant definitions +******************************************************************************/ + +/* Macro Definitions for Debug and Access Port registers read and write Commands + used in the functions defined in UpperPacketLayer.c */ +#define DPACC_DP_CTRLSTAT_READ 0x8D +#define DPACC_DP_IDCODE_READ 0xA5 +#define DPACC_AP_CSW_READ 0x87 +#define DPACC_AP_TAR_READ 0xAF +#define DPACC_AP_DRW_READ 0x9F + +#define DPACC_DP_ABORT_WRITE 0x81 +#define DPACC_DP_CTRLSTAT_WRITE 0xA9 +#define DPACC_DP_SELECT_WRITE 0xB1 +#define DPACC_AP_CSW_WRITE 0xA3 +#define DPACC_AP_TAR_WRITE 0x8B +#define DPACC_AP_DRW_WRITE 0xBB + +/****************************************************************************** +* Function Prototypes +******************************************************************************/ +void Read_DAP(unsigned char swd_DAP_Register, unsigned long *data_32); +void Write_DAP(unsigned char swd_DAP_Register, unsigned long data_32); +unsigned char Read_IO(unsigned long addr_32, unsigned long *data_32); +unsigned char Write_IO(unsigned long addr_32, unsigned long data_32); + +#endif /* __SWD_UPPERPACKETLAYER_H */ + +/* [] END OF FILE */ diff --git a/drivers/misc/cypress/Timeout.h b/drivers/misc/cypress/Timeout.h new file mode 100644 index 000000000000..dbdef4ecc432 --- /dev/null +++ b/drivers/misc/cypress/Timeout.h @@ -0,0 +1,81 @@ +/****************************************************************************** +* File Name: Timeout.h +* Version 1.0 +* +* Description: +* This header file contains the definitions for timeout parameters, function +* declaration for the delay routine. These definitions will vary based on host +* processor used to program target PSoC 5LP. Change the timeout values based on +* your Host processor. +* +* Note: +* Refer to the TimeoutCalc.h header file in the other project +* "C_Hssp_TimeoutCalc" for details on the procedure to calculate these +* parameters for your specific host programmer. +* +******************************************************************************* +* Copyright (2013), Cypress Semiconductor Corporation. +******************************************************************************* +* This software is owned by Cypress Semiconductor Corporation (Cypress) and is +* protected by and subject to worldwide patent protection (United States and +* foreign), United States copyright laws and international treaty provisions. +* Cypress hereby grants to licensee a personal, non-exclusive, non-transferable +* license to copy, use, modify, create derivative works of, and compile the +* Cypress Source Code and derivative works for the sole purpose of creating +* custom software in support of licensee product to be used only in conjunction +* with a Cypress integrated circuit as specified in the applicable agreement. +* Any reproduction, modification, translation, compilation, or representation of +* this software except as specified above is prohibited without the express +* written permission of Cypress. +* +* Disclaimer: CYPRESS MAKES NO WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, WITH +* REGARD TO THIS MATERIAL, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +* Cypress reserves the right to make changes without further notice to the +* materials described herein. Cypress does not assume any liability arising out +* of the application or use of any product or circuit described herein. Cypress +* does not authorize its products for use as critical components in life-support +* systems where a malfunction or failure may reasonably be expected to result in +* significant injury to the user. The inclusion of Cypress' product in a life- +* support systems application implies that the manufacturer assumes all risk of +* such use and in doing so indemnifies Cypress against all charges. Use may be +* limited by and subject to the applicable Cypress software license agreement. +******************************************************************************/ +#ifndef __TIMEOUT_H +#define __TIMEOUT_H + +/****************************************************************************** +* Macro definitions +******************************************************************************/ +#include + +/***************************** USER ATTENTION REQUIRED ************************ +***************************** HOST PROCESSOR SPECIFIC ************************* +* All below three timeout values namely: DEVICE_ACQUIRE_TIMEOUT, +* XRES_PULSE_100US, and SROM_POLLING_TIMEOUT are for a PSoC 5LP Host programmer +* running at 63 MHz. Modify these values as required for your host processor. +* Refer to the TimeoutCalc.h header file in the other project +* "C_Hssp_TimeoutCalc" for details on the procedure to calculate these +* parameters for your specific host programmer. +******************************************************************************/ + +#define XRES_PULSE_100US 600 +#if defined CY8C40xx_FAMILY +#define DEVICE_ACQUIRE_TIMEOUT 40 /* 40 retries, about 2 ms */ +#else +#define DEVICE_ACQUIRE_TIMEOUT 15 +#endif +#define SROM_POLLING_TIMEOUT 210416 + +/****************************************************************************** +* Function Prototypes +******************************************************************************/ + +static inline void DelayHundredUs(void) +{ + udelay(100); +} + +#endif /* __TIMEOUT_H */ + +/* [] END OF FILE */ diff --git a/drivers/misc/cypress/cycapsense_hssp.h b/drivers/misc/cypress/cycapsense_hssp.h new file mode 100644 index 000000000000..d20d8f3956c8 --- /dev/null +++ b/drivers/misc/cypress/cycapsense_hssp.h @@ -0,0 +1,80 @@ + +/* + * Header file for: + * Cypress CapSense(TM) firmware downloader. + * + * Copyright (c) 2015 Motorola Mobility LLC + * + * 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. + * + */ +#ifndef __CYCAPSENSE_HSSP_H__ +#define __CYCAPSENSE_HSSP_H__ + +#include +#include +#include + +struct hex_info { + char fw_name[NAME_MAX]; + u8 *data; + u8 *s_data; + u8 *m_data; + u8 cl_protection; + u16 cs; +}; +struct hssp_data { + struct hex_info inf; + const char *name; + unsigned int silicon_id; + int block_size; + int num_of_blocks; + int secure_bytes; + int c_gpio; + int d_gpio; + int rst_gpio; + int raw_mode; + int c_offset; + int d_offset; + u32 c_group_offset; + u32 d_group_offset; + void __iomem *gpio_base; + u16 chip_cs; + u16 sw_rev; +}; +struct cycapsense_ctrl_data { + struct device *dev; + struct hssp_data hssp_d; + struct work_struct work; + int cmd; +}; + +#define HSSP_CMD_NONE 0 +#define HSSP_CMD_RESET 1 +#define HSSP_CMD_ERASE 2 +#define HSSP_CMD_FW_UPDATE 3 + +int cycapsense_hssp_dnld(struct hssp_data *d); + +#define P4000_METADATA_SIZE 12 +#define SILICON_ID_BYTE_LENGTH 4 +#define FLASH_ROW_BYTE_SIZE_HEX_FILE 128 +#define NUMBER_OF_FLASH_ROWS_HEX_FILE 128 +#define SW_REVISION_OFFSET 0x3f7c + +#define FAILURE 1 +#define SUCCESS 0 + +#define RST_GPIO 1 +#define CLK_GPIO 4 +#define DAT_GPIO 7 + + +#endif /* __CYCAPSENSE_HSSP_H__ */ diff --git a/drivers/misc/cypress/cyttsp_sar.h b/drivers/misc/cypress/cyttsp_sar.h new file mode 100644 index 000000000000..2b932c76381d --- /dev/null +++ b/drivers/misc/cypress/cyttsp_sar.h @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2013 XiaoMi, Inc. + * + * 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. + * + */ + +#ifndef _CYTTSP_SAR_H_ +#define _CYTTSP_SAR_H_ + +#include + +#define CYTTSP_SAR_CHANNEL_ENABLE 0x06 +#define CYTTSP_SAR_FORCE_CALIBRATE 0x07 +#define CYTTSP_SAR_CHANNEL_MAX 0x08 +#define CYTTSP_SAR_STATE_ERROR 0x03 + +#define CYTTSP_CMD_VERIFY_CHKSUM 0x31 +#define CYTTSP_CMD_GET_FLASH_SIZE 0x32 +#define CYTTSP_CMD_ERASE_ROW 0x34 +#define CYTTSP_CMD_SEND_DATA 0x37 +#define CYTTSP_CMD_ENTER_BTLD 0x38 +#define CYTTSP_CMD_PROGRAM_ROW 0x39 +#define CYTTSP_CMD_VERIFY_ROW 0x3A +#define CYTTSP_CMD_EXIT_BTLD 0x3B + +#define CYTTSP_REG_TOUCHMODE 0x00 +#define CYTTSP_REG_HWVERSION 0x59 +#define CYTTSP_REG_INTERRUPT_PEDNING 0x50 +#define CYTTSP_REG_STATUS_LSB 0x51 +#define CYTTSP_REG_STATUS_MSB 0x52 + + +#define CYTTSP_REG_INVALID 0xFF + +#define CYTTSP_NORMAL_MODE 0x00 +#define CYTTSP_STANDBY_MODE 0x01 + +#define CYTTSP_STS_SUCCESS 0x00 + +#define CYTTSP_SAR_OP_MODE 0x00 +#define CYTTSP_SAR_FW_VER1 0x57 +#define CYTTSP_SAR_FW_VER2 0x58 + +#define CYTTSP_PACKET_START 0x01 +#define CYTTSP_PACKET_END 0x17 + +#define CYTTSP_MAX_PAYLOAD_LEN 0x15 + +#define CYTTSP_RESP_HEADER_LEN 0x4 +#define CYTTSP_RESP_TAIL_LEN 0x3 + +#define CYTTSP_ENTER_BTLD_RESP_LEN 8 +#define CYTTSP_GET_FLASHSZ_RESP_LEN 4 + + +#define CYTTSP_FW_VERSION_FILE_OFFSET 183 + + +/************************************** + * define platform data + * + **************************************/ +struct cyttsp_reg_data { + unsigned char reg; + unsigned char val; +}; + +static struct cyttsp_reg_data cyttsp_i2c_reg_setup[] = { + { + .reg = CYTTSP_NORMAL_MODE, + .val = 0x00, + }, + { + .reg = CYTTSP_SAR_CHANNEL_ENABLE, + .val = 0x0f, + }, + { + .reg = CYTTSP_SAR_FORCE_CALIBRATE, + .val = 0x3e, + }, +}; + +typedef struct cyttsp_sar_data *pcyttsp_data_t; +struct cyttsp_sar_data { + struct i2c_client *client; + struct cyttsp_sar_platform_data *pdata; + struct regulator *regulator_vdd; + struct regulator *regulator_avdd; + struct regulator *regulator_vddio; + struct input_dev *input_dev[4]; + bool dbgdump; + unsigned long sensorStatus; + bool enable; + u8 bootloader_addr; + u8 app_addr; +}; + + +struct cyttsp_sar_platform_data { + int irq_gpio; + u32 irq_gpio_flags; + unsigned long irqflags; + const char *input_name; + int i2c_reg_num; + struct cyttsp_reg_data *pi2c_reg; + int nsars; + int *key_code; + u8 sar_status_reg; + u8 bootloader_addr; + u8 standby_reg; + int default_config; + int config_array_size; + struct cyttsp_config_info *config_array; + bool cut_off_power; +}; + +#endif diff --git a/drivers/misc/cypress/cyttsp_sar_mmi.c b/drivers/misc/cypress/cyttsp_sar_mmi.c new file mode 100644 index 000000000000..d79d094399df --- /dev/null +++ b/drivers/misc/cypress/cyttsp_sar_mmi.c @@ -0,0 +1,1433 @@ +/* + * Copyright (C) 2013 Cypress, Inc. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cyttsp_sar.h" +#include +#include +#include +#include +#include +#include +#include "cycapsense_hssp.h" +#include "ProgrammingSteps.h" + +#define CYTTSP_DEBUG 1 +#define LOG_TAG "cyttsp " +#if CYTTSP_DEBUG +#define LOG_INFO(fmt, args...) pr_info(LOG_TAG fmt, ##args) +#else +#define LOG_INFO(fmt, args...) +#endif + +struct cycapsense_ctrl_data *ctrl_data; +static int programming_done; +static int fw_dl_status; +static BLOCKING_NOTIFIER_HEAD(hssp_notifier_list); + +pcyttsp_data_t pcyttsp_sar_ptr; +struct sensors_classdev sensors_capsensor_cdev[4]; + +static int cyttsp_i2c_read_block(struct device *dev, u8 addr, + u8 len, void *data) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret; + + ret = i2c_master_send(client, &addr, 1); + if (ret != 1) { + dev_err(dev, "Failed to read block!\n"); + return -EIO; + } + + ret = i2c_master_recv(client, data, len); + + return (ret < 0) ? ret : ret != len ? -EIO : 0; +} + +static u8 cyttsp_read_reg(struct cyttsp_sar_data *data, u8 reg) +{ + int ret; + u8 val; + + ret = cyttsp_i2c_read_block(&data->client->dev, + reg, 1, &val); + if (ret < 0) { + dev_err(&data->client->dev, "Failed to read reg!\n"); + return ret; + } + + return val; +} + +static int cyttsp_write_reg(struct cyttsp_sar_data *data, + u8 reg, u8 val) +{ + int ret; + u8 buffer[2]; + struct device *dev = &data->client->dev; + struct i2c_client *client = to_i2c_client(dev); + + buffer[0] = reg; + buffer[1] = val; + + ret = i2c_master_send(client, buffer, sizeof(buffer)); + if (ret != sizeof(buffer)) { + dev_err(&data->client->dev, "Failed to write reg!\n"); + return ret; + } + + return 0; +} + +static int cycapsense_validate_gpio(struct device *dev, + int gpio, char *name, int dir_out, int out_val) +{ + int error; + error = devm_gpio_request(dev, gpio, name); + if (error) { + dev_err(dev, "unable to request gpio [%d]\n", gpio); + goto f_end; + } + if (dir_out == 2) + goto f_end; + + if (dir_out == 1) + error = gpio_direction_output(gpio, out_val); + else + error = gpio_direction_input(gpio); + if (error) { + dev_err(dev, "unable to set direction for gpio [%d]\n", gpio); + goto f_end; + } +f_end: + return error; +} + +#ifdef CONFIG_OF +static void cyttsp_sar_dump_value(struct device *dev, + struct cyttsp_sar_platform_data *pdata) +{ + + dev_info(dev, "irq gpio = %d\n", pdata->irq_gpio); + dev_info(dev, "irqflags = %d\n", (int)pdata->irqflags); + dev_info(dev, "input name = %s\n", pdata->input_name); + dev_info(dev, "sar number = %d\n", pdata->nsars); + dev_info(dev, "sar_status_reg = 0x%x\n", pdata->sar_status_reg); + dev_info(dev, "standby_reg = 0x%x\n", pdata->standby_reg); + dev_info(dev, "default_config = %d\n", pdata->default_config); +} + +static int cyttsp_sar_parse_dt(struct device *dev, + struct cyttsp_sar_platform_data *pdata) +{ + int ret, error; + struct device_node *np = dev->of_node; + u32 temp_val; + unsigned int addr = 0, size = 0; + unsigned int gpios[12]; + + /* swd reset */ + dev_err(dev, "cycapsense_prog_probe reqeust reset gpio\n"); + ctrl_data->hssp_d.rst_gpio = of_get_gpio(np, 0); + error = cycapsense_validate_gpio(dev, + ctrl_data->hssp_d.rst_gpio, "capsense_reset_gpio", 1, 0); + if (error) + return error; + + gpio_export(ctrl_data->hssp_d.rst_gpio, false); + + dev_err(dev, "cycapsense_prog_probe clk gpio\n"); + ctrl_data->hssp_d.c_gpio = of_get_gpio(np, 1); + /*request only, direction == 2. Will be set by firmware loader*/ + error = cycapsense_validate_gpio(dev, + ctrl_data->hssp_d.c_gpio, "capsense_sclk_gpio", 2, 0); + if (error) + return error; + + dev_err(dev, "cycapsense_prog_probe data gpio\n"); + ctrl_data->hssp_d.d_gpio = of_get_gpio(np, 2); + /*request only, direction == 2. Will be set by firmware loader*/ + error = cycapsense_validate_gpio(dev, + ctrl_data->hssp_d.d_gpio, "capsense_sdata_gpio", 2, 0); + if (error) + return error; + + /*irq*/ + pdata->irq_gpio = of_get_gpio(np, 3); + + error = of_property_read_string(np, "label", &ctrl_data->hssp_d.name); + if (error) { + dev_err(dev, "Error reading node label\n"); + return error; + } + error = of_property_read_u32(np, "silicon_id", + &ctrl_data->hssp_d.silicon_id); + if (error) { + dev_err(dev, "Error reading node silicon id\n"); + return error; + } + error = of_property_read_u32(np, "block_size", + &ctrl_data->hssp_d.block_size); + if (error) { + dev_err(dev, "Error reading node block size\n"); + return error; + } + error = of_property_read_u32(np, "num_of_blocks", + &ctrl_data->hssp_d.num_of_blocks); + if (error) { + dev_err(dev, "Error reading node num_of_blocks\n"); + return error; + } + error = of_property_read_u32(np, "secure_bytes", + &ctrl_data->hssp_d.secure_bytes); + if (error) { + dev_err(dev, "Error reading node secure_bytes\n"); + return error; + } + of_property_read_u32(np, "use-raw-gpio-rw", + &ctrl_data->hssp_d.raw_mode); + if (ctrl_data->hssp_d.raw_mode) { + + of_property_read_u32(np, "gpio_base_addr", &addr); + of_property_read_u32(np, "gpio_base_size", &size); + memset(gpios, 0, sizeof(gpios)); + of_property_read_u32_array(np, "gpios", gpios, + ARRAY_SIZE(gpios)); + if (addr && size && gpios[CLK_GPIO] && gpios[DAT_GPIO]) { + ctrl_data->hssp_d.gpio_base = + devm_ioremap(dev, addr, size); + dev_info(dev, "gpio base addr = %p\n", ctrl_data->hssp_d.gpio_base); + ctrl_data->hssp_d.c_offset = gpios[CLK_GPIO]; + ctrl_data->hssp_d.d_offset = gpios[DAT_GPIO]; + dev_info(dev, "Raw GPIO (%d:%d) RW enabled\n", + ctrl_data->hssp_d.c_offset, + ctrl_data->hssp_d.d_offset); + error = of_property_read_u32(np, "gpio_clk_group", &ctrl_data->hssp_d.c_group_offset); + if (error) + ctrl_data->hssp_d.c_group_offset = 0x00; + error = of_property_read_u32(np, "gpio_dat_group", &ctrl_data->hssp_d.d_group_offset); + if (error) + ctrl_data->hssp_d.d_group_offset = 0x00; + dev_info(dev, "GPIO offset (0x%x, 0x%x) enabled\n", + ctrl_data->hssp_d.c_group_offset, + ctrl_data->hssp_d.d_group_offset); + + dev_info(dev, "clk gpio address is %08X\n", + addr + ctrl_data->hssp_d.c_group_offset + (0x1000 * gpios[CLK_GPIO])); + dev_info(dev, "dat gpio address is %08X\n", + addr + ctrl_data->hssp_d.d_group_offset + (0x1000 * gpios[DAT_GPIO])); + } else { + ctrl_data->hssp_d.raw_mode = 0; + dev_info(dev, "Raw GPIO RW enabled but regs are not defined.Switch to normal mode\n"); + } + } + + + ret = of_property_read_u32(np, "cyttsp,sar-status-reg", + &temp_val); + if (ret) { + dev_err(dev, "Unable to read fw sar-status-reg\n"); + return ret; + } else + pdata->sar_status_reg = (u8)temp_val; + + pdata->cut_off_power = 0; + + ret = of_property_read_u32(np, "cyttsp,standby-reg", + &temp_val); + if (ret) { + dev_err(dev, "Unable to read standby reg\n"); + return ret; + } else + pdata->standby_reg = (u8)temp_val; + + ret = of_property_read_u32(np, "cyttsp,bootloader-addr", + &temp_val); + if (ret) + dev_err(dev, "Unable to read bootloader address\n"); + else + pdata->bootloader_addr = (u8)temp_val; + + ret = of_property_read_u32(np, "cyttsp,key-num", &temp_val); + if (ret) { + dev_err(dev, "Unable to read key num\n"); + return ret; + } else + pdata->nsars = temp_val; + + ret = of_property_read_u32(np, "cyttsp,default-config", + &pdata->default_config); + if (ret) { + dev_err(dev, "Unable to get default config\n"); + pdata->default_config = -1; + } + + cyttsp_sar_dump_value(dev, pdata); + + return 0; +} +#else +static int cyttsp_sar_parse_dt(struct device *dev, + struct cyttsp_sar_platform_data *pdata) +{ + return -ENODEV; +} +#endif + +static irqreturn_t cyttsp_sar_interrupt(int irq, void *dev_id) +{ + struct cyttsp_sar_data *data = dev_id; + struct cyttsp_sar_platform_data *pdata = data->pdata; + u8 temp[3]; + u32 sar_state = 0; + u32 sensor_status = 0; + int ret ; + int i; + u32 channel_status_mask[CYTTSP_SAR_CHANNEL_MAX] = { + 0x00000003, + 0x0000000C, + 0x00000030, + 0x000000C0, + 0x00000300, + 0x00000C00, + 0x00003000, + 0x0000C000 }; + + dev_dbg(&data->client->dev, "cypress irq handler!\n"); + + if (data->enable) { + ret = cyttsp_i2c_read_block(&data->client->dev, CYTTSP_REG_INTERRUPT_PEDNING, 3, &temp[0]); + if (ret < 0) { + dev_err(&data->client->dev, "Failed to read interrupt pending regsiter!\n"); + return IRQ_NONE; + } + + sensor_status = ((unsigned long)temp[1] + (unsigned long)(temp[2] << 8)); + for (i = 0; i < pdata->nsars; i++) { + + /* Report event only if sensor status for a specific channel has changed + and it matches the channel position in interrupt pending register */ + if (((sensor_status ^ data->sensorStatus) & channel_status_mask[i])) { + /* Find the sar state for the channel */ + sar_state = (sensor_status & channel_status_mask[i]) >> (i * 2); + + if (sar_state < CYTTSP_SAR_STATE_ERROR) { + input_report_abs(data->input_dev[i], ABS_DISTANCE, sar_state); + input_sync(data->input_dev[i]); + } else { + dev_err(&data->client->dev, "sar sensor error detected, Need reset!\n"); + } + } + } + + } + + data->sensorStatus = sensor_status; + + return IRQ_HANDLED; +} + +/** + * brief Initialize I2C config from platform data + * param this Pointer to main parent struct + * + */ +static void hw_init(void) +{ + struct cyttsp_sar_data *data = pcyttsp_sar_ptr; + + struct cyttsp_sar_platform_data *pdata = data->pdata; + int i = 0; + int ret; + /* configure device */ + LOG_INFO("Going to Setup I2C Registers\n"); + while (i < pdata->i2c_reg_num) { + /* Write all registers/values contained in i2c_reg */ + LOG_INFO("Going to Write Reg: 0x%x Value: 0x%x\n", + pdata->pi2c_reg[i].reg, pdata->pi2c_reg[i].val); + ret = cyttsp_write_reg(data, pdata->pi2c_reg[i].reg, pdata->pi2c_reg[i].val); + if (ret < 0) + LOG_INFO("reg write failed\n"); + i++; + } + + LOG_INFO("i2c reg set done\n"); +} + +static void cycapsense_hssp_notify(int status) +{ + programming_done = status; + blocking_notifier_call_chain(&hssp_notifier_list, + programming_done, NULL); +} + +int cycapsense_hssp_dnld(struct hssp_data *d) +{ + int ret = SUCCESS; + cycapsense_hssp_notify(HSSP_START); + + if (d->inf.data == NULL || d->inf.s_data == NULL) { + pr_err("%s: Invalid input arguments\n", __func__); + ret = -EINVAL; + goto end; + } + + if (DeviceAcquire() == FAILURE) { + pr_err("%s: Device Acquire failed\n", __func__); + if (DeviceAcquire() == FAILURE) { + pr_err("%s: Device Acquire failed again\n", __func__); + ret = -EIO; + goto end; + } + } + + if (VerifySiliconId(d) == FAILURE) { + pr_err("%s: Verify Selicon ID failed\n", __func__); + ret = -EIO; + goto end; + } + + if (EraseAllFlash(d) == FAILURE) { + pr_err("%s: Flash Erase failed\n", __func__); + ret = -EIO; + goto end; + } + + if (ChecksumPrivileged(d) == FAILURE) { + pr_err("%s: Checksum Privileged failed\n", __func__); + ret = -EIO; + goto end; + } + + if (ProgramFlash(d) == FAILURE) { + pr_err("%s: Program Flash failed\n", __func__); + ret = -EIO; + goto end; + } + + if (VerifyFlash(d) == FAILURE) { + pr_err("%s: Verify Flash failed\n", __func__); + ret = -EIO; + goto end; + } + + if (ProgramProtectionSettings(d) == FAILURE) { + pr_err("%s: Program Protection failed\n", __func__); + ret = -EIO; + goto end; + } + + if (VerifyProtectionSettings(d) == FAILURE) { + pr_err("%s: Verify Protection failed\n", __func__); + ret = -EIO; + goto end; + } + + if (VerifyChecksum(d) == FAILURE) { + pr_err("%s: Verify Checksum failed\n", __func__); + ret = -EIO; + goto end; + } + +end: + ExitProgrammingMode(); + msleep(1000); + cycapsense_hssp_notify(HSSP_STOP); + return ret; +} + +const unsigned char ascii2bin[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F +}; +#define a_2_b(a) (ascii2bin[((a) & 0xff) - 0x30]) +#define a_a_2_byte(a1, a2) ((a_2_b(a1) << 4) | a_2_b(a2)) + +/* this simplifyied version of Intel HEX, Cypress PSoC specific*/ +int cycapsense_hssp_parse_hex(struct hssp_data *d, const u8 *src, int len) +{ + enum r_type { REC_DATA_T = 0, REC_EOF_T = 1, REC_EXT_L_ADDR_T = 4 }; + struct { int len; enum r_type type; int e_addr; } rec = { 0, 0, 0 }; + enum parser_state { REC_START, REC_LEN, REC_ADDR, REC_TYPE, REC_DATA, + REC_CS } state = REC_START; + int i = 0, j = 0, cnt; + u16 cs = 0, f_cs; + u8 *dst; + u8 rec_cs = 0, val; + + if (d == NULL) { + pr_err("%s: ID is not defined.\n", __func__); + return -EINVAL; + } + + cnt = d->block_size * d->num_of_blocks; + d->inf.data = kzalloc(cnt, GFP_KERNEL); + if (d->inf.data == NULL) { + pr_err("%s: Error kzalloc (%d).\n", __func__, cnt); + return -ENOMEM; + } + d->inf.s_data = kzalloc(d->secure_bytes, GFP_KERNEL); + if (d->inf.s_data == NULL) { + pr_err("%s: Error kzalloc (%d).\n", __func__, d->secure_bytes); + kfree(d->inf.data); + return -ENOMEM; + } + d->inf.m_data = kzalloc(P4000_METADATA_SIZE, GFP_KERNEL); + if (d->inf.m_data == NULL) { + pr_err("%s: Error kzalloc (%d).\n", __func__, + P4000_METADATA_SIZE); + kfree(d->inf.data); + kfree(d->inf.s_data); + return -ENOMEM; + } + dst = d->inf.data; /*data always first in PSoC HEX*/ + while (i < len) { + switch (state) { + case REC_START: + if (src[i] == '\r' || src[i] == '\n') + i++; + else if (src[i++] != ':') + goto parse_hex_end_err; + else + state = REC_LEN; + break; + case REC_LEN: + if (len - i < 2) + goto parse_hex_end_err; + rec.len = a_a_2_byte(src[i], src[i+1]); + i += 2; + rec_cs += rec.len; + state = REC_ADDR; + break; + case REC_ADDR: + if (len - i < 4) + goto parse_hex_end_err; + /* addr is not used by PSoC, only for check sum */ + val = a_a_2_byte(src[i], src[i+1]); + rec_cs += val; + val = a_a_2_byte(src[i+2], src[i+3]); + rec_cs += val; + i += 4; + state = REC_TYPE; + break; + case REC_TYPE: + if (len - i < 2) + goto parse_hex_end_err; + rec.type = a_a_2_byte(src[i], src[i+1]); + i += 2; + rec_cs += rec.type; + switch (rec.type) { + case REC_DATA_T: + state = REC_DATA; + break; + case REC_EXT_L_ADDR_T: + if (len - i < 4) + goto parse_hex_end_err; + val = a_a_2_byte(src[i], src[i+1]); + rec_cs += val; + rec.e_addr = val << 8; + val = a_a_2_byte(src[i+2], src[i+3]); + i += 4; + rec_cs += val; + rec.e_addr |= val; + if (rec.e_addr == 0x9030) { + /* Checksum */ + dst = (u8 *)&f_cs; + d->inf.cs = cs; + j = 0; + cnt = 2; + } else if (rec.e_addr == 0x9040) { + /* Flash protection */ + if (j > cnt) { + pr_err("%s: Too big for %s.\n", + __func__, d->name); + goto parse_hex_end_err; + } + dst = d->inf.s_data; + cnt = d->secure_bytes; + j = 0; + } else if (rec.e_addr == 0x9050) { + /* Metadata */ + j = 0; + dst = d->inf.m_data; + cnt = P4000_METADATA_SIZE; + } else if (rec.e_addr == 0x9060) { + /* Chip level protection */ + dst = (u8 *)&d->inf.cl_protection; + j = 0; + cnt = 1; + } else { + pr_err("%s: Unsupported ext addr 0x%x\n", + __func__, rec.e_addr); + goto parse_hex_end_err; + } + state = REC_CS; + break; + case REC_EOF_T: + if (be16_to_cpu(f_cs) != d->inf.cs) { + pr_err("%s: Data checksum failed 0x%x - 0x%x\n", + __func__, be16_to_cpu(f_cs), + d->inf.cs); + goto parse_hex_end_err; + } + state = REC_CS; + break; + default: /* Cypress supports only types above */ + pr_err("%s: Unsupported record type\n", + __func__); + goto parse_hex_end_err; + } + break; + case REC_DATA: + if (len - i < rec.len * 2) + goto parse_hex_end_err; + for (; rec.len; rec.len--, i += 2, dst++, j++) { + val = a_a_2_byte(src[i], src[i+1]); + if (j < cnt) { + *dst = val; + cs += val; + } + rec_cs += val; + } + state = REC_CS; + break; + case REC_CS: + if (len - i < 2) + goto parse_hex_end_err; + val = a_a_2_byte(src[i], src[i+1]); + i += 2; + rec_cs += val; + if (rec_cs != 0) { + pr_err("%s: Record check sum failed\n", + __func__); + goto parse_hex_end_err; + } + state = REC_START; + break; + } + } + return 0; +parse_hex_end_err: + pr_err("%s: HEX format error.\n", __func__); + kfree(d->inf.data); + d->inf.data = NULL; + kfree(d->inf.s_data); + d->inf.s_data = NULL; + kfree(d->inf.m_data); + d->inf.m_data = NULL; + return -EINVAL; +} + + +int __cycapsense_reset(struct cycapsense_ctrl_data *data) +{ + int status = programming_done; + + if (status == HSSP_STOP) + cycapsense_hssp_notify(HSSP_START); + + dev_info(data->dev, "Reset requested\n"); + device_lock(data->dev); + ExitProgrammingMode(); + msleep(1000); + + if (status == HSSP_STOP) + cycapsense_hssp_notify(HSSP_STOP); + + device_unlock(data->dev); + return 0; +} + +static int cycapsense_hssp_erase(struct hssp_data *d) +{ + int ret = SUCCESS; + cycapsense_hssp_notify(HSSP_START); + + device_lock(ctrl_data->dev); + if (DeviceAcquire() == FAILURE) { + pr_err("%s: Device Acquire failed\n", __func__); + if (DeviceAcquire() == FAILURE) { + pr_err("%s: Device Acquire failed again\n", __func__); + ret = -EIO; + goto end; + } + } + + if (EraseAllFlash(d) == FAILURE) { + pr_err("%s: Flash Erase failed\n", __func__); + ret = -EIO; + goto end; + } + + pr_info("%s: Flash Erase successful\n", __func__); +end: + d->chip_cs = 0; + device_unlock(ctrl_data->dev); + return ret; +} + + +static int cycapsense_hssp_verify_swrev(struct hssp_data *d) +{ + int ret; + + cycapsense_hssp_notify(HSSP_START); + + if (DeviceAcquire() == FAILURE) { + pr_err("%s: Device Acquire failed\n", __func__); + if (DeviceAcquire() == FAILURE) { + pr_err("%s: Device Acquire failed again\n", __func__); + ret = -EIO; + goto end; + } + } + + if (VerifySiliconId(d) == FAILURE) { + pr_err("%s: Verify Silicon ID failed\n", __func__); + ret = -EIO; + goto end; + } + + ret = VerifySwRevision(d); + +end: + ExitProgrammingMode(); + msleep(1000); + cycapsense_hssp_notify(HSSP_STOP); + return ret; +} + +int __cycapsense_fw_update(struct cycapsense_ctrl_data *data) +{ + const struct firmware *fw = NULL; + char *fw_name = NULL; + struct hex_info *inf; + int error = 0; + + inf = &data->hssp_d.inf; + + device_lock(data->dev); + + fw_name = kzalloc(NAME_MAX, GFP_KERNEL); + if (fw_name == NULL) { + dev_err(data->dev, "No memory for FW name\n"); + error = -ENOMEM; + goto fw_upd_end; + } + if (inf->fw_name[0] == 0) { + strlcpy(fw_name, data->hssp_d.name, NAME_MAX); + strlcat(fw_name, ".hex", NAME_MAX); + } else { + strlcpy(fw_name, inf->fw_name, NAME_MAX); + } + if (request_firmware(&fw, fw_name, data->dev) < 0 || !fw + || !fw->data || !fw->size) { + dev_err(data->dev, "Unable to get firmware %s\n", fw_name); + error = -ENOENT; + goto fw_upd_end; + } + error = cycapsense_hssp_parse_hex(&data->hssp_d, fw->data, + fw->size); + if (error) + goto fw_upd_end; + + if (cycapsense_hssp_verify_swrev(&data->hssp_d) != SUCCESS) { + + /* force update regardless check sum, if user requested */ + dev_info(data->dev, "Flashing firmware %s\n", fw_name); + fw_dl_status = 2; + error = cycapsense_hssp_dnld(&data->hssp_d); + if (!error) { + dev_info(data->dev, "%s flashed successful\n", + fw_name); + fw_dl_status = 0; + } else + fw_dl_status = 1; + } else { + data->hssp_d.chip_cs = data->hssp_d.inf.cs; + dev_info(data->dev, + "SW rev is matching. No firmware upgrade.\n"); + fw_dl_status = 0; + } + +fw_upd_end: + if (inf->data != NULL) { + kfree(inf->data); + inf->data = NULL; + } + if (inf->s_data != NULL) { + kfree(inf->s_data); + inf->s_data = NULL; + } + if (inf->m_data != NULL) { + kfree(inf->m_data); + inf->m_data = NULL; + } + if (fw != NULL) + release_firmware(fw); + if (fw_name != NULL) + kfree(fw_name); + + device_unlock(data->dev); + return error; +} + +static int capsensor_set_enable(struct sensors_classdev *sensors_cdev, unsigned int enable) +{ + struct cyttsp_sar_data *data = pcyttsp_sar_ptr; + struct input_dev *input = data->input_dev[0]; + + if (enable == 1) { + LOG_INFO("enable cap sensor\n"); + hw_init(); + input_report_abs(input, ABS_DISTANCE, 0); + input_sync(input); + data->enable = true; + } else if (enable == 0) { + LOG_INFO("disable cap sensor\n"); + data->enable = false; + } else { + LOG_INFO("unknown enable symbol\n"); + } + + return 0; +} + + +/** + *fn static void cyttsp_reg_setup_init(struct i2c_client *client) + *brief read reg val form dts + * reg_array_len for regs needed change num + * data_array_val's format + */ +static void cyttsp_reg_setup_init(struct i2c_client *client) +{ + u32 data_array_len = 0; + u32 *data_array; + int ret, i, j; + struct device_node *np = client->dev.of_node; + + ret = of_property_read_u32(np, "reg_array_len", &data_array_len); + if (ret < 0) { + LOG_INFO("data_array_len read error"); + return; + } + data_array = kmalloc(data_array_len * 2 * sizeof(u32), GFP_KERNEL); + ret = of_property_read_u32_array(np, "reg_array_val", + data_array, + data_array_len*2); + if (ret < 0) { + LOG_INFO("data_array_val read error"); + return; + } + for (i = 0; i < ARRAY_SIZE(cyttsp_i2c_reg_setup); i++) { + for (j = 0; j < data_array_len*2; j += 2) { + if (data_array[j] == cyttsp_i2c_reg_setup[i].reg) + cyttsp_i2c_reg_setup[i].val = data_array[j+1]; + } + } + kfree(data_array); +} + +static int cyttsp_initialize_regulator(struct cyttsp_sar_data *data) +{ + int ret = 0; + struct i2c_client *client = data->client; + + data->regulator_vdd = devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR(data->regulator_vdd)) { + dev_err(&client->dev, + "%s: regulator_get failed: %ld\n", + __func__, PTR_ERR(data->regulator_vdd)); + goto err_null_regulator; + } + + ret = regulator_enable(data->regulator_vdd); + if (ret < 0) { + dev_err(&client->dev, + "%s: regulator_enable failed: %d\n", + __func__, ret); + goto err_null_regulator; + } + + return 0; + +err_null_regulator: + data->regulator_vdd = NULL; + return ret; +} + +static void capsense_update_work(struct work_struct *w) +{ + struct cycapsense_ctrl_data *data = + container_of(w, struct cycapsense_ctrl_data, work); + struct i2c_client *client = to_i2c_client(data->dev); + + if(data->cmd == HSSP_CMD_NONE) + return; + + disable_irq(client->irq); + + switch (data->cmd) { + case HSSP_CMD_RESET: + __cycapsense_reset(data); + break; + case HSSP_CMD_ERASE: + cycapsense_hssp_erase(&data->hssp_d); + break; + case HSSP_CMD_FW_UPDATE: + __cycapsense_fw_update(data); + break; + } + data->cmd = HSSP_CMD_NONE; + + enable_irq(client->irq); +} + +int cycapsense_reset(void) +{ + if (ctrl_data == NULL || ctrl_data->dev == NULL) { + pr_err("%s: Ctrl data not initialized\n", __func__); + return -ENODEV; + } + + ctrl_data->cmd = HSSP_CMD_RESET; + schedule_work(&ctrl_data->work); + + return 0; +} + +static ssize_t cycapsense_fw_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 16, "0x%x\n", + ctrl_data && ctrl_data->hssp_d.chip_cs ? + ctrl_data->hssp_d.sw_rev : 0); +} + +static ssize_t cycapsense_fw_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + char *cp; + + if (!count) + return -EINVAL; + + cp = memchr(buf, '\n', count); + if (cp) + *(char *)cp = 0; + + if (!strcmp(buf, "reset")) + ctrl_data->cmd = HSSP_CMD_RESET; + else if (!strcmp(buf, "erase")) + ctrl_data->cmd = HSSP_CMD_ERASE; + else { + ctrl_data->cmd = HSSP_CMD_FW_UPDATE; + if (!strcmp(buf, "1")) + ctrl_data->hssp_d.inf.fw_name[0] = 0; + else + strlcpy(ctrl_data->hssp_d.inf.fw_name, buf, NAME_MAX); + } + + schedule_work(&ctrl_data->work); + + return count; +} + +static ssize_t cycapsense_reset_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 8, "%d\n", programming_done); +} + +static ssize_t cycapsense_reset_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + if (!count) + return -EINVAL; + + if (!strncmp(buf, "reset", 5) || !strncmp(buf, "1", 1)) + cycapsense_reset(); + + return count; +} + +static ssize_t cycapsense_enable_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + struct cyttsp_sar_data *data = pcyttsp_sar_ptr; + + return snprintf(buf, 8, "%d\n", (int)data->enable); + +} + +static ssize_t cycapsense_enable_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + struct cyttsp_sar_data *data = pcyttsp_sar_ptr; + struct cyttsp_sar_platform_data *pdata = data->pdata; + struct input_dev *input = NULL; + int i; + + if (!strncmp(buf, "1", 1)) { + LOG_INFO("enable cap sensor\n"); + hw_init(); + for (i = 0; i < pdata->nsars; i++) { + input = data->input_dev[i]; + input_report_abs(input, ABS_DISTANCE, 0); + input_sync(input); + } + data->enable = true; + } else if (!strncmp(buf, "0", 1)) { + LOG_INFO("disable cap sensor\n"); + for (i = 0; i < pdata->nsars; i++) { + input = data->input_dev[i]; + input_report_abs(input, ABS_DISTANCE, -1); + input_sync(input); + } + data->enable = false; + + } else { + LOG_INFO("unknown enable symbol\n"); + } + + return count; +} + +static ssize_t cycapsense_reg_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + struct cyttsp_sar_data *data = pcyttsp_sar_ptr; + u8 reg_value, reg = 0x00; + char *p = buf; + + while (reg <= 0x8a) { + reg_value = cyttsp_read_reg(data, reg); + p += snprintf(p, PAGE_SIZE, "ADDR(0x%02x)=0x%02x,\n", reg, reg_value); + reg++; + } + return (p-buf); +} + +static ssize_t cycapsense_reg_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + struct cyttsp_sar_data *data = pcyttsp_sar_ptr; + unsigned int val, reg; + int ret; + + if (sscanf(buf, "%x,%x", ®, &val) == 2) { + ret = cyttsp_write_reg(data, *((u8 *)®), *((u8 *)&val)); + } + + return count; +} + +static ssize_t cycapsense_debug_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + struct cyttsp_sar_data *data = pcyttsp_sar_ptr; + u8 val1, val2, reg = 0x00; + u16 value1, value2; + int i, flag = 0; + + if (!strncmp(buf, "0", 1)) { + flag = 1; + LOG_INFO("read channel 0 data\n"); + for (i = 0; i < 200; i++) { + if (flag == 0) + break; + reg = 0x5b; + val1 = cyttsp_read_reg(data, reg); + reg = 0x5c; + val2 = cyttsp_read_reg(data, reg); + value1 = val2 << 8 | val1; + reg = 0x7b; + val1 = cyttsp_read_reg(data, reg); + reg = 0x7c; + val2 = cyttsp_read_reg(data, reg); + value2 = val2 << 8 | val1; + LOG_INFO(" raw = 0x%x, signal = 0x%x\n", value1, value2); + msleep(100); + + } + } else if (!strncmp(buf, "1", 1)) { + flag = 1; + LOG_INFO("read channel 1 data\n"); + for (i = 0; i < 200; i++) { + if (flag == 0) + break; + reg = 0x5d; + val1 = cyttsp_read_reg(data, reg); + reg = 0x5e; + val2 = cyttsp_read_reg(data, reg); + value1 = val2 << 8 | val1; + reg = 0x7d; + val1 = cyttsp_read_reg(data, reg); + reg = 0x7e; + val2 = cyttsp_read_reg(data, reg); + value2 = val2 << 8 | val1; + LOG_INFO(" raw = 0x%x, signal = 0x%x\n", value1, value2); + msleep(100); + + } + } else if (!strncmp(buf, "2", 1)) { + flag = 1; + LOG_INFO("read channel 2 data\n"); + for (i = 0; i < 200; i++) { + if (flag == 0) + break; + reg = 0x5f; + val1 = cyttsp_read_reg(data, reg); + reg = 0x60; + val2 = cyttsp_read_reg(data, reg); + value1 = val2 << 8 | val1; + reg = 0x7f; + val1 = cyttsp_read_reg(data, reg); + reg = 0x80; + val2 = cyttsp_read_reg(data, reg); + value2 = val2 << 8 | val1; + LOG_INFO(" raw = 0x%x, signal = 0x%x\n", value1, value2); + msleep(100); + + } + } else if (!strncmp(buf, "3", 1)) { + flag = 1; + LOG_INFO("read channel 3 data\n"); + for (i = 0; i < 200; i++) { + if (flag == 0) + break; + reg = 0x61; + val1 = cyttsp_read_reg(data, reg); + reg = 0x62; + val2 = cyttsp_read_reg(data, reg); + value1 = val2 << 8 | val1; + reg = 0x81; + val1 = cyttsp_read_reg(data, reg); + reg = 0x82; + val2 = cyttsp_read_reg(data, reg); + value2 = val2 << 8 | val1; + LOG_INFO(" raw = 0x%x, signal = 0x%x\n", value1, value2); + msleep(100); + } + } else if (!strncmp(buf, "4", 1)) { + flag = 0; + } + + return count; +} + +static ssize_t cycapsense_fw_download_status_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 8, "%d", fw_dl_status); +} + +static CLASS_ATTR(fw_update, 0660, cycapsense_fw_show, cycapsense_fw_store); +static CLASS_ATTR(reset, 0660, cycapsense_reset_show, cycapsense_reset_store); +static CLASS_ATTR(enable, 0660, cycapsense_enable_show, cycapsense_enable_store); +static CLASS_ATTR(reg, 0660, cycapsense_reg_show, cycapsense_reg_store); +static CLASS_ATTR(debug, 0660, NULL, cycapsense_debug_store); +static CLASS_ATTR(fw_download_status, 0660, cycapsense_fw_download_status_show, NULL); + +static struct class capsense_class = { + .name = "capsense", + .owner = THIS_MODULE, +}; + +static int cyttsp_sar_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct cyttsp_sar_platform_data *pdata; + struct cyttsp_sar_data *data; + int error, i; + char *name; + char *buffer; + + dev_err(&client->dev, "cycapsense_probe enter\n"); + pdata = devm_kzalloc(&client->dev, + sizeof(struct cyttsp_sar_platform_data), GFP_KERNEL); + if (!pdata) { + dev_err(&client->dev, "Failed to allocate memroy for pdata\n"); + return -ENOMEM; + } + + ctrl_data = devm_kzalloc(&client->dev, + sizeof(struct cycapsense_ctrl_data), GFP_KERNEL); + if (ctrl_data == NULL) { + dev_err(&client->dev, "Failed to allocate memory for ctrl_data\n"); + return -ENOMEM; + } + + error = cyttsp_sar_parse_dt(&client->dev, pdata); + if (error) + return error; + client->irq = gpio_to_irq(pdata->irq_gpio); + + cyttsp_reg_setup_init(client); + pdata->pi2c_reg = cyttsp_i2c_reg_setup; + pdata->i2c_reg_num = ARRAY_SIZE(cyttsp_i2c_reg_setup); + client->dev.platform_data = pdata; + + data = kzalloc(sizeof(struct cyttsp_sar_data), GFP_KERNEL); + if (!data) { + dev_err(&client->dev, "Failed to allocate memory for data!\n"); + return -ENOMEM; + } + + data->client = client; + data->pdata = pdata; + pcyttsp_sar_ptr = data; + ctrl_data->dev = &client->dev; + + error = cyttsp_initialize_regulator(data); + if (error < 0) { + dev_err(&client->dev, "Failed to initialize regulator\n"); + goto err_free_data; + } + i2c_set_clientdata(data->client, data); + + for (i = 0; i < pdata->nsars; i++) { + /* Initialize input device */ + data->input_dev[i] = input_allocate_device(); + if (!data->input_dev[i]) { + dev_err(&client->dev, "Failed to allocate input device\n"); + goto err_irq_gpio_req; + } + name = kasprintf(GFP_KERNEL, "cyttsp_sar_ch%d", i); + + data->input_dev[i]->name = name; + + /* Set all the keycodes */ + __set_bit(EV_ABS, data->input_dev[i]->evbit); + input_set_abs_params(data->input_dev[i], ABS_DISTANCE, -1, 100, 0, 0); + + error = input_register_device(data->input_dev[i]); + if (error) { + dev_err(&client->dev, "Unable to register input %d device, error: %d\n", i, error); + goto err_free_input; + } + buffer = kasprintf(GFP_KERNEL, "Moto CapSense Ch%d", i); + sensors_capsensor_cdev[i].sensors_enable = capsensor_set_enable; + sensors_capsensor_cdev[i].sensors_poll_delay = NULL; + sensors_capsensor_cdev[i].name = buffer; + sensors_capsensor_cdev[i].vendor = "cypress"; + sensors_capsensor_cdev[i].version = 1; + sensors_capsensor_cdev[i].type = SENSOR_TYPE_MOTO_CAPSENSE; + sensors_capsensor_cdev[i].max_range = "5"; + sensors_capsensor_cdev[i].resolution = "5.0"; + sensors_capsensor_cdev[i].sensor_power = "3"; + sensors_capsensor_cdev[i].min_delay = 0; + sensors_capsensor_cdev[i].fifo_reserved_event_count = 0; + sensors_capsensor_cdev[i].fifo_max_event_count = 0; + sensors_capsensor_cdev[i].delay_msec = 100; + sensors_capsensor_cdev[i].enabled = 0; + + error = sensors_classdev_register(&data->input_dev[i]->dev, &sensors_capsensor_cdev[i]); + if (error < 0) + LOG_INFO("create %d cap sensor_class file failed (%d)\n", i, error); + + } + + error = request_threaded_irq(client->irq, NULL, cyttsp_sar_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, client->dev.driver->name, data); + + + dev_err(&client->dev, "registering irq %d\n", client->irq); + if (error) { + dev_err(&client->dev, "Error %d registering irq %d\n", error, client->irq); + goto err_unreg_input; + } + + error = class_register(&capsense_class); + if (error < 0) { + dev_err(&client->dev, + "Create fsys class failed (%d)\n", error); + return error; + } + + error = class_create_file(&capsense_class, &class_attr_fw_update); + if (error < 0) { + dev_err(&client->dev, + "Create fw_update file failed (%d)\n", error); + return error; + } + + error = class_create_file(&capsense_class, &class_attr_reset); + if (error < 0) { + dev_err(&client->dev, + "Create reset file failed (%d)\n", error); + return error; + } + + error = class_create_file(&capsense_class, &class_attr_reg); + if (error < 0) { + dev_err(&client->dev, + "Create reg file failed (%d)\n", error); + return error; + } + + error = class_create_file(&capsense_class, &class_attr_enable); + if (error < 0) { + dev_err(&client->dev, + "Create enable file failed (%d)\n", error); + return error; + } + + error = class_create_file(&capsense_class, &class_attr_debug); + if (error < 0) { + dev_err(&client->dev, + "Create debug file failed (%d)\n", error); + return error; + } + + error = class_create_file(&capsense_class, &class_attr_fw_download_status); + if (error < 0) { + dev_err(&client->dev, + "Create fw dl status file failed (%d)\n", error); + return error; + } + + fw_dl_status = 1; + ctrl_data->dev = &client->dev; + INIT_WORK(&ctrl_data->work, capsense_update_work); + gpio_direction_output(ctrl_data->hssp_d.rst_gpio, 0); + gpio_set_value(ctrl_data->hssp_d.rst_gpio, 1); + dev_dbg(&client->dev, "Cypress CapSense probe complete\n"); + + ctrl_data->cmd = HSSP_CMD_FW_UPDATE; + ctrl_data->hssp_d.inf.fw_name[0] = 0; + + schedule_work(&ctrl_data->work); + data->enable = false; + + return 0; + +err_unreg_input: + input_unregister_device(data->input_dev[0]); +err_free_input: + input_free_device(data->input_dev[i]); +err_irq_gpio_req: + if (gpio_is_valid(pdata->irq_gpio)) + gpio_free(pdata->irq_gpio); +err_free_data: + kfree(data); + return error; +} + +static int cyttsp_sar_remove(struct i2c_client *client) +{ + struct cyttsp_sar_data *data = i2c_get_clientdata(client); + const struct cyttsp_sar_platform_data *pdata = data->pdata; + int i; + + free_irq(client->irq, data); + if(ctrl_data) + cancel_work_sync(&ctrl_data->work); + + for (i = 0; i < pdata->nsars; i++) { + sensors_classdev_unregister(&sensors_capsensor_cdev[i]); + input_unregister_device(data->input_dev[i]); + } + if (gpio_is_valid(pdata->irq_gpio)) + gpio_free(pdata->irq_gpio); + if (data->regulator_vdd) + regulator_disable(data->regulator_vdd); + if (data->regulator_avdd) + regulator_disable(data->regulator_avdd); + if (data->regulator_vddio) + regulator_disable(data->regulator_vddio); + + class_unregister(&capsense_class); + + kfree(data); + data = NULL; + + return 0; +} + +static void cyttsp_sar_shutdown(struct i2c_client *client) +{ + disable_irq(client->irq); +} + +static const struct i2c_device_id cyt_id[] = { + {"cyttsp_streetfighter", 0}, + { }, +}; + +#ifdef CONFIG_OF +static struct of_device_id cyttsp_match_table[] = { + { .compatible = "cypress,sf3155",}, + { }, +}; +#else +#define cyttsp_match_table NULL +#endif + +static struct i2c_driver cyttsp_sar_driver = { + .driver = { + .name = "cyttsp_streetfighter", + .owner = THIS_MODULE, + .of_match_table = cyttsp_match_table, + }, + .probe = cyttsp_sar_probe, + .remove = cyttsp_sar_remove, + .shutdown = cyttsp_sar_shutdown, + .id_table = cyt_id, +}; + +static int __init cyttsp_sar_init(void) +{ + return i2c_add_driver(&cyttsp_sar_driver); +} + +static void __exit cyttsp_sar_exit(void) +{ + i2c_del_driver(&cyttsp_sar_driver); +} + +module_init(cyttsp_sar_init); +module_exit(cyttsp_sar_exit); + +/* Module information */ +MODULE_AUTHOR("XXX "); +MODULE_DESCRIPTION("Cypress Capsense SAR Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/misc/drv2624_mmi/Android.mk b/drivers/misc/drv2624_mmi/Android.mk new file mode 100644 index 000000000000..77bf55e7bdd0 --- /dev/null +++ b/drivers/misc/drv2624_mmi/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := drv2624_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/drv2624_mmi/Kbuild b/drivers/misc/drv2624_mmi/Kbuild new file mode 100644 index 000000000000..dd30c7fa930e --- /dev/null +++ b/drivers/misc/drv2624_mmi/Kbuild @@ -0,0 +1,7 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +ifeq ($(filter m y, $(CONFIG_INPUT_DRV2624_HAPTICS)),) +obj-m += drv2624_mmi.o +endif diff --git a/drivers/misc/drv2624_mmi/Makefile b/drivers/misc/drv2624_mmi/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/misc/drv2624_mmi/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/misc/drv2624_mmi/drv2624_mmi.c b/drivers/misc/drv2624_mmi/drv2624_mmi.c new file mode 100644 index 000000000000..50da89357a3e --- /dev/null +++ b/drivers/misc/drv2624_mmi/drv2624_mmi.c @@ -0,0 +1,1656 @@ +/* +** ============================================================================= +** Copyright (c) 2016 Texas Instruments 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 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: +** drv2624.c +** +** Description: +** DRV2624 chip driver +** +** ============================================================================= +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct drv2624_data *g_DRV2624data; + +static int +drv2624_reg_read(struct drv2624_data *ctrl, unsigned char reg) +{ + unsigned int val; + + int ret; + + ret = regmap_read(ctrl->mpRegmap, reg, &val); + + if (ret < 0) { + dev_err(ctrl->dev, + "%s reg=0x%x error %d\n", __func__, reg, ret); + return ret; + } else + return val; +} + +static int +drv2624_reg_write(struct drv2624_data *ctrl, + unsigned char reg, unsigned char val) +{ + int ret; + + ret = regmap_write(ctrl->mpRegmap, reg, val); + if (ret < 0) { + dev_err(ctrl->dev, + "%s reg=0x%x, value=0%x error %d\n", + __func__, reg, val, ret); + } + + return ret; +} + +static int +drv2624_bulk_read(struct drv2624_data *ctrl, + unsigned char reg, unsigned int count, u8 *buf) +{ + int ret; + + ret = regmap_bulk_read(ctrl->mpRegmap, reg, buf, count); + if (ret < 0) { + dev_err(ctrl->dev, + "%s reg=0%x, count=%d error %d\n", + __func__, reg, count, ret); + } + + return ret; +} + +static int +drv2624_bulk_write(struct drv2624_data *ctrl, + unsigned char reg, unsigned int count, const u8 *buf) +{ + int ret; + + ret = regmap_bulk_write(ctrl->mpRegmap, reg, buf, count); + if (ret < 0) { + dev_err(ctrl->dev, + "%s reg=0%x, count=%d error %d\n", + __func__, reg, count, ret); + } + + return ret; +} + +static int +drv2624_set_bits(struct drv2624_data *ctrl, + unsigned char reg, unsigned char mask, unsigned char val) +{ + int ret; + + ret = regmap_update_bits(ctrl->mpRegmap, reg, mask, val); + if (ret < 0) { + dev_err(ctrl->dev, + "%s reg=%x, mask=0x%x, value=0x%x error %d\n", + __func__, reg, mask, val, ret); + } + + return ret; +} + +static void +drv2624_change_voltage(struct drv2624_data *ctrl, enum work_mode mode) +{ + struct drv2624_platform_data *pDrv2624Platdata = + &ctrl->msPlatData; + struct actuator_data actuator = pDrv2624Platdata->msActuator; + + if (mode != actuator.meWorkMode) { + if (mode == REDUCED) { + /*write reduced strength voltages*/ + drv2624_reg_write(ctrl, + DRV2624_REG_RTP_INPUT, + actuator.mnRTPINPUTReduced); + pDrv2624Platdata->msActuator.meWorkMode = REDUCED; + dev_dbg(ctrl->dev, "%s, reduced voltage!\n", __func__); + } else { + /*back to default voltages from device tree*/ + drv2624_reg_write(ctrl, + DRV2624_REG_RTP_INPUT, + actuator.mnRTPINPUT); + drv2624_reg_write(ctrl, + DRV2624_REG_OVERDRIVE_CLAMP, + actuator.mnOverDriveClampVoltage); + pDrv2624Platdata->msActuator.meWorkMode = NORMAL; + dev_dbg(ctrl->dev, "%s, restored voltage.\n", __func__); + } + } +} + +static void +drv2624_hap_context(struct drv2624_data *ctrl, unsigned char val) +{ + int t_top; + enum work_mode mode; + + if (!gpio_is_valid(ctrl->msPlatData.mnGpioVCTRL)) + return; + + t_top = gpio_get_value(ctrl->msPlatData.mnGpioVCTRL); + if (t_top && atomic_read(&ctrl->reduce_pwr) && + (ctrl->mnCurrentVibrationTime > 100)) { + dev_dbg(ctrl->dev, "%s: haptic reduced\n", __func__); + mode = REDUCED; + } else + mode = NORMAL; + drv2624_change_voltage(ctrl, mode); +} + +static int +drv2624_set_go_bit(struct drv2624_data *ctrl, unsigned char val) +{ + int ret = 0; + int value = 0; + int retry = 10; /* to finish auto-brake*/ + + if (!ctrl->factory_mode && ctrl->reduced_pwr_capable && val == GO) + drv2624_hap_context(ctrl, val); + val &= 0x01; + ret = drv2624_reg_write(ctrl, DRV2624_REG_GO, val); + if (ret >= 0) { + if (val == 1) { + mdelay(1); + value = drv2624_reg_read(ctrl, DRV2624_REG_GO); + if (value < 0) { + ret = value; + } else if (value != 1) { + ret = -1; + dev_warn(ctrl->dev, + "%s, GO fail, stop action\n", __func__); + } + } else { + while (retry > 0) { + value = drv2624_reg_read(ctrl, DRV2624_REG_GO); + if (value < 0) { + ret = value; + break; + } + if (value == 0) + break; + mdelay(10); + retry--; + } + if (retry == 0) { + dev_err(ctrl->dev, + "%s, ERROR: clear GO fail\n", __func__); + } + } + } + + return ret; +} + +static void +drv2624_change_mode(struct drv2624_data *ctrl, unsigned char work_mode) +{ + drv2624_set_bits(ctrl, DRV2624_REG_MODE, DRV2624_MODE_MASK, work_mode); +} + +static void drv2624_stop(struct drv2624_data *ctrl) +{ + if (ctrl->mnVibratorPlaying == YES) { + hrtimer_cancel(&ctrl->timer); + drv2624_set_go_bit(ctrl, STOP); + ctrl->mnVibratorPlaying = NO; + wake_unlock(&ctrl->wklock); + } +} + +static ssize_t drv2624_store_state(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + + /* At present, nothing to do with setting state */ + return count; +} + +static ssize_t drv2624_show_duration(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct drv2624_data *ctrl = + container_of(cdev, struct drv2624_data, cdev); + ktime_t time_rem; + s64 time_us = 0; + + if (hrtimer_active(&ctrl->timer)) { + time_rem = hrtimer_get_remaining(&ctrl->timer); + time_us = ktime_to_us(time_rem); + } + + return snprintf(buf, PAGE_SIZE, "%lld\n", div_s64(time_us, 1000)); +} + +static ssize_t drv2624_store_duration(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct drv2624_data *ctrl = + container_of(cdev, struct drv2624_data, cdev); + u32 value; + int ret; + + ret = kstrtouint(buf, 0, &value); + if (ret < 0) + return ret; + dev_dbg(ctrl->dev, "%s, value=%d\n", __func__, value); + + mutex_lock(&ctrl->lock); + + ctrl->mnWorkMode = WORK_IDLE; + drv2624_stop(ctrl); + + if (value > 0) { + wake_lock(&ctrl->wklock); + + drv2624_change_mode(ctrl, MODE_RTP); + ctrl->mnVibratorPlaying = YES; + ctrl->mnCurrentVibrationTime = value; + drv2624_set_go_bit(ctrl, GO); + value = (value > MAX_TIMEOUT) ? MAX_TIMEOUT : value; + hrtimer_start(&ctrl->timer, + ns_to_ktime((u64) value * NSEC_PER_MSEC), + HRTIMER_MODE_REL); + } + mutex_unlock(&ctrl->lock); + + return count; +} + +static ssize_t drv2624_show_activate(struct device *dev, + struct device_attribute *attr, char *buf) +{ + /* For now nothing to show */ + return snprintf(buf, PAGE_SIZE, "%d\n", 0); +} + +static ssize_t drv2624_store_activate(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct drv2624_data *ctrl = + container_of(cdev, struct drv2624_data, cdev); + u32 value; + int ret; + + ret = kstrtouint(buf, 0, &value); + if (ret < 0) + return ret; + + if (value != 0 && value != 1) + return count; + + if (value == 0) + drv2624_stop(ctrl); + + return count; +} + + +static struct device_attribute drv2624_led_classdev_attrs[] = { + __ATTR(state, 0220, NULL, drv2624_store_state), + __ATTR(duration, 0664, drv2624_show_duration, + drv2624_store_duration), + __ATTR(activate, 0664, drv2624_show_activate, + drv2624_store_activate), +}; + +/* Dummy functions for brightness */ +static enum led_brightness +dev2624_haptics_brightness_get(struct led_classdev *cdev) +{ + return 0; +} + +static void +dev2624_haptics_brightness_set(struct led_classdev *cdev, + enum led_brightness level) +{ +} + +static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer) +{ + struct drv2624_data *ctrl = + container_of(timer, struct drv2624_data, timer); + + ctrl->mnWorkMode |= WORK_VIBRATOR; + schedule_work(&ctrl->vibrator_work); + + return HRTIMER_NORESTART; +} + +static void vibrator_work_routine(struct work_struct *work) +{ + struct drv2624_data *ctrl = + container_of(work, struct drv2624_data, vibrator_work); + unsigned char mode; + + mutex_lock(&ctrl->lock); + + if (ctrl->mnWorkMode & WORK_IRQ) { + unsigned char status = ctrl->mnIntStatus; + + if (status & OVERCURRENT_MASK) + dev_err(ctrl->dev, + "ERROR, Over Current detected!!\n"); + + if (status & OVERTEMPRATURE_MASK) + dev_err(ctrl->dev, + "ERROR, Over Temperature detected!!\n"); + + if (status & ULVO_MASK) + dev_err(ctrl->dev, + "ERROR, VDD drop observed!!\n"); + + if (status & PRG_ERR_MASK) + dev_err(ctrl->dev, "ERROR, PRG error!!\n"); + + if (status & PROCESS_DONE_MASK) { + mode = + drv2624_reg_read(ctrl, + DRV2624_REG_MODE) & DRV2624_MODE_MASK; + if (mode == MODE_CALIBRATION) { + if ((status & DIAG_MASK) != DIAG_SUCCESS) { + dev_err(ctrl->dev, + "Calibration fail\n"); + } else { + unsigned char calComp = + drv2624_reg_read(ctrl, + DRV2624_REG_CAL_COMP); + + unsigned char calBemf = + drv2624_reg_read(ctrl, + DRV2624_REG_CAL_BEMF); + + unsigned char calBemfGain = + drv2624_reg_read(ctrl, + DRV2624_REG_CAL_COMP) + & BEMFGAIN_MASK; + + dev_info(ctrl->dev, + "AutoCal : Comp=0x%x, Bemf=0x%x, Gain=0x%x\n", + calComp, calBemf, calBemfGain); + } + } else if (mode == MODE_DIAGNOSTIC) { + if ((status & DIAG_MASK) != DIAG_SUCCESS) { + dev_err(ctrl->dev, + "Diagnostic fail\n"); + } else { + unsigned char diagZ = + drv2624_reg_read(ctrl, + DRV2624_REG_DIAG_Z); + + unsigned char diagK = + drv2624_reg_read(ctrl, + DRV2624_REG_DIAG_K); + + dev_info(ctrl->dev, + "Diag : ZResult=0x%x, CurrentK=0x%x\n", + diagZ, diagK); + } + } else if (mode == MODE_WAVEFORM_SEQUENCER) { + dev_info(ctrl->dev, + "Waveform Sequencer Playback finished\n"); + } + + if (ctrl->mnVibratorPlaying == YES) { + ctrl->mnVibratorPlaying = NO; + wake_unlock(&ctrl->wklock); + } + } + + ctrl->mnWorkMode &= ~WORK_IRQ; + } + + if (ctrl->mnWorkMode & WORK_VIBRATOR) { + drv2624_stop(ctrl); + + ctrl->mnWorkMode &= ~WORK_VIBRATOR; + } + + mutex_unlock(&ctrl->lock); +} + +static int fw_chksum(const struct firmware *fw) +{ + int sum = 0; + + int i = 0; + + int size = fw->size; + + const unsigned char *pBuf = fw->data; + + for (i = 0; i < size; i++) { + if (!((i > 11) && (i < 16))) + sum += pBuf[i]; + } + + return sum; +} + +static void drv2624_firmware_load(const struct firmware *fw, void *context) +{ + struct drv2624_data *ctrl = context; + + int size = 0, fwsize = 0, i = 0; + + const unsigned char *pBuf = NULL; + + if (fw != NULL) { + pBuf = fw->data; + size = fw->size; + + memcpy(&(ctrl->msFwHeader), pBuf, + sizeof(struct drv2624_fw_header)); + if ((ctrl->msFwHeader.fw_magic != DRV2624_MAGIC) + || (ctrl->msFwHeader.fw_size != size) + || (ctrl->msFwHeader.fw_chksum != fw_chksum(fw))) { + dev_err(ctrl->dev, + "%s, ERROR!! firmware not right:Magic=0x%x,Size=%d,chksum=0x%x\n", + __func__, ctrl->msFwHeader.fw_magic, + ctrl->msFwHeader.fw_size, + ctrl->msFwHeader.fw_chksum); + } else { + dev_err(ctrl->dev, "%s, firmware good\n", + __func__); + + pBuf += sizeof(struct drv2624_fw_header); + + drv2624_reg_write(ctrl, + DRV2624_REG_RAM_ADDR_UPPER, 0); + drv2624_reg_write(ctrl, + DRV2624_REG_RAM_ADDR_LOWER, 0); + + fwsize = size - sizeof(struct drv2624_fw_header); + for (i = 0; i < fwsize; i++) { + drv2624_reg_write(ctrl, + DRV2624_REG_RAM_DATA, + pBuf[i]); + } + } + } else { + dev_err(ctrl->dev, + "%s, ERROR!! firmware not found\n", __func__); + } +} + +static void HapticsFirmwareLoad(const struct firmware *fw, void *context) +{ + struct drv2624_data *ctrl = context; + + mutex_lock(&ctrl->lock); + + drv2624_firmware_load(fw, context); + release_firmware(fw); + + mutex_unlock(&ctrl->lock); +} + +static int +drv2624_set_seq_loop(struct drv2624_data *ctrl, unsigned long arg) +{ + int ret = 0, i; + + struct drv2624_seq_loop seqLoop; + + unsigned char halfSize = DRV2624_SEQUENCER_SIZE / 2; + unsigned char loop[2] = { 0, 0 }; + + if (copy_from_user(&seqLoop, + (void __user *)arg, sizeof(struct drv2624_seq_loop))) + return -EFAULT; + + for (i = 0; i < DRV2624_SEQUENCER_SIZE; i++) { + if (i < halfSize) + loop[0] |= (seqLoop.mpLoop[i] << (i * 2)); + else + loop[1] |= (seqLoop.mpLoop[i] << ((i - halfSize) * 2)); + } + + ret = drv2624_bulk_write(ctrl, DRV2624_REG_SEQ_LOOP_1, 2, loop); + + return ret; +} + +static int +drv2624_set_main(struct drv2624_data *ctrl, unsigned long arg) +{ + int ret = 0; + + struct drv2624_wave_setting mainSetting; + + unsigned char control = 0; + + if (copy_from_user(&mainSetting, + (void __user *)arg, + sizeof(struct drv2624_wave_setting))) + return -EFAULT; + + control |= mainSetting.meScale; + control |= (mainSetting.meInterval << INTERVAL_SHIFT); + drv2624_set_bits(ctrl, + DRV2624_REG_CONTROL2, SCALE_MASK | INTERVAL_MASK, + control); + + drv2624_set_bits(ctrl, + DRV2624_REG_MAIN_LOOP, 0x07, mainSetting.meLoop); + + return ret; +} + +static int +drv2624_set_wave_seq(struct drv2624_data *ctrl, unsigned long arg) +{ + int ret = 0; + + struct drv2624_wave_seq waveSeq; + + if (copy_from_user(&waveSeq, + (void __user *)arg, sizeof(struct drv2624_wave_seq))) + return -EFAULT; + + ret = drv2624_bulk_write(ctrl, + DRV2624_REG_SEQUENCER_1, + DRV2624_SEQUENCER_SIZE, waveSeq.mpWaveIndex); + + return ret; +} + +static int +drv2624_get_diag_result(struct drv2624_data *ctrl, unsigned long arg) +{ + int ret = 0; + + struct drv2624_diag_result diagResult; + + unsigned char mode, go; + + memset(&diagResult, 0, sizeof(struct drv2624_diag_result)); + + mode = drv2624_reg_read(ctrl, DRV2624_REG_MODE) & DRV2624_MODE_MASK; + if (mode != MODE_DIAGNOSTIC) { + diagResult.mnFinished = -EFAULT; + return ret; + } + + go = drv2624_reg_read(ctrl, DRV2624_REG_GO) & 0x01; + if (go) { + diagResult.mnFinished = NO; + } else { + diagResult.mnFinished = YES; + diagResult.mnResult = + ((ctrl->mnIntStatus & DIAG_MASK) >> DIAG_SHIFT); + diagResult.mnDiagZ = + drv2624_reg_read(ctrl, DRV2624_REG_DIAG_Z); + diagResult.mnDiagK = + drv2624_reg_read(ctrl, DRV2624_REG_DIAG_K); + } + + if (copy_to_user + ((void __user *)arg, &diagResult, + sizeof(struct drv2624_diag_result))) + return -EFAULT; + + return ret; +} + +static int +drv2624_get_autocal_result(struct drv2624_data *ctrl, unsigned long arg) +{ + int ret = 0; + + struct drv2624_autocal_result autocalResult; + + unsigned char mode, go; + + memset(&autocalResult, 0, sizeof(struct drv2624_autocal_result)); + + mode = drv2624_reg_read(ctrl, DRV2624_REG_MODE) & DRV2624_MODE_MASK; + if (mode != MODE_CALIBRATION) { + autocalResult.mnFinished = -EFAULT; + return ret; + } + + go = drv2624_reg_read(ctrl, DRV2624_REG_GO) & 0x01; + if (go) { + autocalResult.mnFinished = NO; + } else { + autocalResult.mnFinished = YES; + autocalResult.mnResult = + ((ctrl->mnIntStatus & DIAG_MASK) >> DIAG_SHIFT); + autocalResult.mnCalComp = + drv2624_reg_read(ctrl, DRV2624_REG_CAL_COMP); + autocalResult.mnCalBemf = + drv2624_reg_read(ctrl, DRV2624_REG_CAL_BEMF); + autocalResult.mnCalGain = + drv2624_reg_read(ctrl, + DRV2624_REG_CAL_COMP) & BEMFGAIN_MASK; + } + + if (copy_to_user + ((void __user *)arg, &autocalResult, + sizeof(struct drv2624_autocal_result))) + return -EFAULT; + + return ret; +} + +static int drv2624_file_open(struct inode *inode, struct file *file) +{ + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + file->private_data = (void *)g_DRV2624data; + return 0; +} + +static int drv2624_file_release(struct inode *inode, struct file *file) +{ + file->private_data = (void *)NULL; + module_put(THIS_MODULE); + + return 0; +} + +static long +drv2624_file_unlocked_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct drv2624_data *ctrl = file->private_data; + + /* void __user *user_arg = (void __user *)arg; */ + int ret = 0; + + mutex_lock(&ctrl->lock); + + switch (cmd) { + case DRV2624_SET_SEQ_LOOP: + ret = drv2624_set_seq_loop(ctrl, arg); + break; + + case DRV2624_SET_MAIN: + ret = drv2624_set_main(ctrl, arg); + break; + + case DRV2624_SET_WAV_SEQ: + ret = drv2624_set_wave_seq(ctrl, arg); + break; + + case DRV2624_WAVSEQ_PLAY: + { + drv2624_stop(ctrl); + + wake_lock(&ctrl->wklock); + ctrl->mnVibratorPlaying = YES; + drv2624_change_mode(ctrl, + MODE_WAVEFORM_SEQUENCER); + drv2624_set_go_bit(ctrl, GO); + } + break; + + case DRV2624_STOP: + { + drv2624_stop(ctrl); + } + break; + + case DRV2624_RUN_DIAGNOSTIC: + { + drv2624_stop(ctrl); + + wake_lock(&ctrl->wklock); + ctrl->mnVibratorPlaying = YES; + drv2624_change_mode(ctrl, MODE_DIAGNOSTIC); + drv2624_set_go_bit(ctrl, GO); + } + break; + + case DRV2624_GET_DIAGRESULT: + ret = drv2624_get_diag_result(ctrl, arg); + break; + + case DRV2624_RUN_AUTOCAL: + { + drv2624_stop(ctrl); + + wake_lock(&ctrl->wklock); + ctrl->mnVibratorPlaying = YES; + drv2624_change_mode(ctrl, MODE_CALIBRATION); + drv2624_set_go_bit(ctrl, GO); + } + break; + + case DRV2624_GET_CALRESULT: + ret = drv2624_get_autocal_result(ctrl, arg); + break; + } + + mutex_unlock(&ctrl->lock); + + return ret; +} + +static ssize_t +drv2624_file_read(struct file *filp, char *buff, size_t length, loff_t *offset) +{ + struct drv2624_data *ctrl = + (struct drv2624_data *)filp->private_data; + int ret = 0; + + unsigned char value = 0; + + unsigned char *p_kBuf = NULL; + + mutex_lock(&ctrl->lock); + + switch (ctrl->mnFileCmd) { + case HAPTIC_CMDID_REG_READ: + if (length == 1) { + ret = + drv2624_reg_read(ctrl, + ctrl->mnCurrentReg); + if (0 > ret) { + dev_err(ctrl->dev, "dev read fail %d\n", + ret); + return ret; + } + value = ret; + + ret = copy_to_user(buff, &value, 1); + if (0 != ret) { + /* Failed to copy all the data, exit */ + dev_err(ctrl->dev, + "copy to user fail %d\n", ret); + return 0; + } + } else if (length > 1) { + p_kBuf = kzalloc(length, GFP_KERNEL); + if (p_kBuf != NULL) { + ret = drv2624_bulk_read(ctrl, + ctrl->mnCurrentReg, + length, p_kBuf); + if (0 > ret) { + dev_err(ctrl->dev, + "dev bulk read fail %d\n", ret); + } else { + ret = + copy_to_user(buff, p_kBuf, length); + if (0 != ret) { + dev_err(ctrl->dev, + "copy to user fail %d\n", + ret); + } + } + + kfree(p_kBuf); + } else { + dev_err(ctrl->dev, "read no mem\n"); + return -ENOMEM; + } + } + break; + + case HAPTIC_CMDID_READ_FIRMWARE: + { + int i; + + p_kBuf = kzalloc(length, GFP_KERNEL); + if (p_kBuf != NULL) { + drv2624_reg_write(ctrl, + DRV2624_REG_RAM_ADDR_UPPER, + ctrl->mnFwAddUpper); + drv2624_reg_write(ctrl, + DRV2624_REG_RAM_ADDR_LOWER, + ctrl->mnFwAddLower); + + for (i = 0; i < length; i++) { + p_kBuf[i] = + drv2624_reg_read(ctrl, + DRV2624_REG_RAM_DATA); + } + + ret = copy_to_user(buff, p_kBuf, length); + if (0 != ret) { + /* Failed to copy all the data, exit */ + dev_err(ctrl->dev, + "copy to user fail %d\n", ret); + } + + kfree(p_kBuf); + } else { + dev_err(ctrl->dev, "read no mem\n"); + return -ENOMEM; + } + } + break; + + default: + ctrl->mnFileCmd = 0; + break; + } + + mutex_unlock(&ctrl->lock); + + return length; +} + +static ssize_t +drv2624_file_write(struct file *filp, const char *buff, size_t len, + loff_t *off) +{ + struct drv2624_data *ctrl = + (struct drv2624_data *)filp->private_data; + + mutex_lock(&ctrl->lock); + + ctrl->mnFileCmd = buff[0]; + + switch (ctrl->mnFileCmd) { + case HAPTIC_CMDID_REG_READ: + { + if (len == 2) { + ctrl->mnCurrentReg = buff[1]; + } else { + dev_err(ctrl->dev, + " read cmd len %zu err\n", len); + } + break; + } + + case HAPTIC_CMDID_REG_WRITE: + { + if ((len - 1) == 2) { + drv2624_reg_write(ctrl, buff[1], + buff[2]); + } else if ((len - 1) > 2) { + unsigned char *data = + kzalloc(len - 2, GFP_KERNEL); + + if (data != NULL) { + if (copy_from_user + (data, &buff[2], len - 2) != 0) { + dev_err(ctrl->dev, + "%s, reg copy err\n", + __func__); + } else { + drv2624_bulk_write(ctrl, + buff[1], + len - 2, + data); + } + kfree(data); + } else { + dev_err(ctrl->dev, + "memory fail\n"); + } + } else { + dev_err(ctrl->dev, + "%s, reg_write len %zu error\n", + __func__, len); + } + break; + } + + case HAPTIC_CMDID_REG_SETBIT: + { + int i = 1; + + for (i = 1; i < len;) { + drv2624_set_bits(ctrl, buff[i], + buff[i + 1], buff[i + 2]); + i += 3; + } + break; + } + + case HAPTIC_CMDID_UPDATE_FIRMWARE: + { + struct firmware fw; + + unsigned char *fw_buffer = kzalloc(len - 1, GFP_KERNEL); + int result = -1; + + drv2624_stop(ctrl); + + if (fw_buffer != NULL) { + fw.size = len - 1; + + wake_lock(&ctrl->wklock); + result = + copy_from_user(fw_buffer, &buff[1], + fw.size); + if (result == 0) { + dev_info(ctrl->dev, + "%s, fwsize=%zu, f:%x, l:%x\n", + __func__, fw.size, buff[1], + buff[len - 1]); + fw.data = + (const unsigned char *)fw_buffer; + drv2624_firmware_load(&fw, (void *) + ctrl); + } + wake_unlock(&ctrl->wklock); + + kfree(fw_buffer); + } + break; + } + + case HAPTIC_CMDID_READ_FIRMWARE: + { + if (len == 3) { + ctrl->mnFwAddUpper = buff[2]; + ctrl->mnFwAddLower = buff[1]; + } else { + dev_err(ctrl->dev, + "%s, read fw len error\n", __func__); + } + break; + } + + default: + dev_err(ctrl->dev, "%s, unknown cmd\n", __func__); + break; + } + + mutex_unlock(&ctrl->lock); + + return len; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .read = drv2624_file_read, + .write = drv2624_file_write, + .unlocked_ioctl = drv2624_file_unlocked_ioctl, + .open = drv2624_file_open, + .release = drv2624_file_release, +}; + +static struct miscdevice drv2624_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = HAPTICS_DEVICE_NAME, + .fops = &fops, +}; + +static int Haptics_init(struct drv2624_data *ctrl) +{ + int ret = 0; + int i; + + ctrl->cdev.name = "vibrator"; + ctrl->cdev.brightness_get = dev2624_haptics_brightness_get; + ctrl->cdev.brightness_set = dev2624_haptics_brightness_set; + ctrl->cdev.max_brightness = 100; + + ret = devm_led_classdev_register(ctrl->dev, &(ctrl->cdev)); + if (ret < 0) { + dev_err(ctrl->dev, + "drv2624: fail to create timed output dev\n"); + return ret; + } + + for (i = 0; i < ARRAY_SIZE(drv2624_led_classdev_attrs); i++) { + ret = sysfs_create_file(&ctrl->cdev.dev->kobj, + &drv2624_led_classdev_attrs[i].attr); + if (ret < 0) { + dev_err(ctrl->dev, + "Error in creating sysfs file, nResult=%d\n", + ret); + goto sysfs_fail; + } + } + + ret = misc_register(&drv2624_misc); + if (ret) { + dev_err(ctrl->dev, "drv2624 misc fail: %d\n", ret); + return ret; + } + + hrtimer_init(&ctrl->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ctrl->timer.function = vibrator_timer_func; + INIT_WORK(&ctrl->vibrator_work, vibrator_work_routine); + + wake_lock_init(&ctrl->wklock, WAKE_LOCK_SUSPEND, "vibrator"); + mutex_init(&ctrl->lock); + + return 0; +sysfs_fail: + for (--i; i >= 0; i--) + sysfs_remove_file(&ctrl->cdev.dev->kobj, + &drv2624_led_classdev_attrs[i].attr); + + return ret; +} + +static void dev_init_platform_data(struct drv2624_data *ctrl) +{ + struct drv2624_platform_data *pDrv2624Platdata = + &ctrl->msPlatData; + struct actuator_data actuator = pDrv2624Platdata->msActuator; + + unsigned char value_temp = 0; + + unsigned char mask_temp = 0; + + drv2624_set_bits(ctrl, + DRV2624_REG_INT_ENABLE, INT_MASK_ALL, INT_ENABLE_ALL); + + drv2624_set_bits(ctrl, + DRV2624_REG_MODE, PINFUNC_MASK, + (PINFUNC_INT << PINFUNC_SHIFT)); + + if ((actuator.meActuatorType == ERM) + || (actuator.meActuatorType == LRA)) { + mask_temp |= ACTUATOR_MASK; + value_temp |= (actuator.meActuatorType << ACTUATOR_SHIFT); + } + + if ((pDrv2624Platdata->meLoop == CLOSE_LOOP) || + (pDrv2624Platdata->meLoop == OPEN_LOOP)) { + mask_temp |= LOOP_MASK; + value_temp |= (pDrv2624Platdata->meLoop << LOOP_SHIFT); + } + + if (value_temp != 0) { + drv2624_set_bits(ctrl, + DRV2624_REG_CONTROL1, + mask_temp | AUTOBRK_OK_MASK, + value_temp | AUTOBRK_OK_ENABLE); + } + + if (actuator.mnRatedVoltage != 0) { + drv2624_reg_write(ctrl, + DRV2624_REG_RATED_VOLTAGE, + actuator.mnRatedVoltage); + } else { + dev_err(ctrl->dev, "%s, ERROR Rated ZERO\n", __func__); + } + + if (actuator.mnOverDriveClampVoltage != 0) { + drv2624_reg_write(ctrl, + DRV2624_REG_OVERDRIVE_CLAMP, + actuator.mnOverDriveClampVoltage); + } else { + dev_err(ctrl->dev, + "%s, ERROR OverDriveVol ZERO\n", __func__); + } + + if (actuator.mnRTPINPUT != 0) { + drv2624_reg_write(ctrl, + DRV2624_REG_RTP_INPUT, + actuator.mnRTPINPUT); + } else { + dev_err(ctrl->dev, "%s,ERROR RTP INPUT\n", __func__); + } + + if (actuator.mnRateVoltageClamp != 0) { + drv2624_reg_write(ctrl, + DRV2624_REG_RATED_VOLTAGE_CLAMP, + actuator.mnRateVoltageClamp); + } + + actuator.meWorkMode = NORMAL; + /*update sample_time*/ + drv2624_reg_write(ctrl, DRV2624_REG_SAMPLE_TIME, actuator.mnSampleTime); + + if (actuator.meActuatorType == LRA) { + unsigned short openLoopPeriod = + (unsigned short)((unsigned int)1000000000 / + (24619 * actuator.mnLRAFreq)); + + drv2624_set_bits(ctrl, + DRV2624_REG_OL_PERIOD_H, 0x03, + (openLoopPeriod & 0x0300) >> 8); + drv2624_reg_write(ctrl, DRV2624_REG_OL_PERIOD_L, + (openLoopPeriod & 0x00ff)); + + if (!actuator.mnDriveTime) { + unsigned char DriveTime = + 5*(1000-actuator.mnLRAFreq)/actuator.mnLRAFreq; + + if (actuator.mnLRAFreq < 125) + DriveTime |= + (MINFREQ_SEL_45HZ << MINFREQ_SEL_SHIFT); + actuator.mnDriveTime = DriveTime; + } + + drv2624_set_bits(ctrl, + DRV2624_REG_DRIVE_TIME, + DRIVE_TIME_MASK | MINFREQ_SEL_MASK, + actuator.mnDriveTime); + dev_info(ctrl->dev, "%s, LRA = %d, DriveTime=0x%x\n", + __func__, actuator.mnLRAFreq, actuator.mnDriveTime); + } + + if (ctrl->msPlatData.auto_cal) { + drv2624_reg_write(ctrl, + DRV2624_REG_AUTO_CAL_TIME, + ctrl->msPlatData.auto_cal_time); + dev_info(ctrl->dev, + "%s,auto calibration time = %d\n", + __func__, ctrl->msPlatData.auto_cal_time); + } +} + +static irqreturn_t drv2624_irq_handler(int irq, void *dev_id) +{ + struct drv2624_data *ctrl = (struct drv2624_data *)dev_id; + + ctrl->mnIntStatus = + drv2624_reg_read(ctrl, DRV2624_REG_STATUS); + if (ctrl->mnIntStatus & INT_MASK) { + ctrl->mnWorkMode |= WORK_IRQ; + schedule_work(&ctrl->vibrator_work); + } + return IRQ_HANDLED; +} + +static int dev_auto_calibrate(struct drv2624_data *ctrl) +{ + wake_lock(&ctrl->wklock); + ctrl->mnVibratorPlaying = YES; + drv2624_change_mode(ctrl, MODE_CALIBRATION); + drv2624_set_go_bit(ctrl, GO); + + return 0; +} + +static struct regmap_config drv2624_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = DRV2624_REG_DIAG_K, + .cache_type = REGCACHE_NONE, +}; + +static inline bool drv2624_reduced_pwr_on(struct drv2624_platform_data *pdata) +{ + return (pdata->msActuator.mnRTPINPUTReduced != 0) && + gpio_is_valid(pdata->mnGpioVCTRL); +} + +#ifdef CONFIG_OF +static struct drv2624_platform_data *drv2624_of_init(struct i2c_client *client) +{ + struct drv2624_platform_data *pdata; + struct device_node *np = client->dev.of_node; + int rc; + + pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + pdata->mnGpioNRST = of_get_named_gpio(np, "ti,nrst-gpio", 0); + if (!gpio_is_valid(pdata->mnGpioNRST)) { + dev_warn(&client->dev, "%s: no RST gpio provided\n", __func__); + } + + pdata->mnGpioNPWR = of_get_named_gpio(np, "ti,npwr-gpio", 0); + if (!gpio_is_valid(pdata->mnGpioNPWR)) { + dev_warn(&client->dev, "%s: no NPWR gpio provided\n", __func__); + } + + pdata->mnGpioINT = of_get_named_gpio(np, "ti,irqz-gpio", 0); + if (!gpio_is_valid(pdata->mnGpioINT)) { + dev_warn(&client->dev, "%s: no IRQ gpio provided\n", __func__); + } + + pdata->mnGpioVCTRL = of_get_named_gpio(np, "ti,nvctrl-gpio", 0); + if (!gpio_is_valid(pdata->mnGpioVCTRL)) { + dev_warn(&client->dev, "%s: no VCTRL gpio provided\n", __func__); + } + + rc = of_property_read_u8(np, "ti,rated_voltage", + &pdata->msActuator.mnRatedVoltage); + if (rc) { + dev_err(&client->dev, "%s: rated voltage read failed\n", + __func__); + return NULL; + } + + rc = of_property_read_u8(np, "ti,overdrive_voltage", + &pdata->msActuator.mnOverDriveClampVoltage); + if (rc) { + dev_err(&client->dev, "%s: overdrive voltage read failed\n", + __func__); + return NULL; + } + + rc = of_property_read_u8(np, "ti,rate_voltage_clamp", + &pdata->msActuator.mnRateVoltageClamp); + if (rc) { + dev_warn(&client->dev, "%s: rate_voltage_clamp read failed\n", + __func__); + } + + rc = of_property_read_u8(np, "ti,rtp_input", + &pdata->msActuator.mnRTPINPUT); + if (rc && gpio_is_valid(pdata->mnGpioVCTRL)) + dev_warn(&client->dev, "%s:rtp input read failed\n", + __func__); + + rc = of_property_read_u8(np, "ti,rtp_input_reduced", + &pdata->msActuator.mnRTPINPUTReduced); + if (rc && gpio_is_valid(pdata->mnGpioVCTRL)) + dev_warn(&client->dev, "%s:rtp input reduced read failed\n", + __func__); + + rc = of_property_read_u8(np, "ti,drive_time", + &pdata->msActuator.mnDriveTime); + if (!rc) + dev_info(&client->dev, "%s: drive time %d\n", + __func__, pdata->msActuator.mnDriveTime); + + rc = of_property_read_u8(np, "ti,sample_time", + &pdata->msActuator.mnSampleTime); + if (rc) { + dev_err(&client->dev, "%s: sample time read failed\n", + __func__); + return NULL; + } + + pdata->msActuator.meActuatorType = of_property_read_bool(np, + "ti,lra_drive"); + + pdata->meLoop = of_property_read_bool(np, "ti,open_loop"); + + if (pdata->msActuator.meActuatorType) { + rc = of_property_read_u8(np, "ti,lra_freq", + &pdata->msActuator.mnLRAFreq); + if (rc) { + dev_err(&client->dev, "%s: lra frequency read failed\n", + __func__); + return NULL; + } + } + + pdata->auto_cal = of_property_read_bool(np, "ti,auto_cal"); + if (pdata->auto_cal) { + dev_info(&client->dev, "%s: auto calibration enabled\n", __func__); + rc = of_property_read_u8(np, "ti,auto_cal_time", + &pdata->auto_cal_time); + if (rc) { + dev_err(&client->dev, "%s: lra auto_cal_time read failed\n", + __func__); + pdata->auto_cal_time = 2; + } + } + + pdata->no_firmware = of_property_read_bool(np, "ti,no_firmware"); + if (pdata->no_firmware) + dev_info(&client->dev, "%s: no firmware loading\n", __func__); + return pdata; +} +#else +static inline struct drv2624_platform_data + *drv2624_of_init(struct i2c_client *client) +{ + return NULL; +} +#endif + +/* Attribute: reduce (RW) */ +static ssize_t reduce_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct drv2624_data *ctrl = dev_get_drvdata(dev); + atomic_set(&ctrl->reduce_pwr, (*buf == '0')?0:1); + dev_dbg(ctrl->dev, HAPTICS_DEVICE_NAME "%s: reduce set to %d", + __func__, atomic_read(&ctrl->reduce_pwr)); + return count; +} + +static ssize_t reduce_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct drv2624_data *ctrl = dev_get_drvdata(dev); + int state = atomic_read(&ctrl->reduce_pwr); + return scnprintf(buf, PAGE_SIZE, "%d", state); +} + +DEVICE_ATTR(reduce, (S_IRUGO | S_IWUSR | S_IWGRP), + reduce_show, reduce_store); + +#include +#include +#include + +#define VIBDEV_MAJOR 198 /* falls within gap in major numbers */ + +static DEFINE_IDA(minors); + +/* Attribute: path (RO) */ +static ssize_t path_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct drv2624_data *ctrl = dev_get_drvdata(dev); + ssize_t blen; + const char *path; + + if (!ctrl) { + pr_err("cannot get data pointer\n"); + return (ssize_t)0; + } + path = kobject_get_path(&ctrl->i2c_client->dev.kobj, GFP_KERNEL); + blen = scnprintf(buf, PAGE_SIZE, "%s", path ? path : "na"); + kfree(path); + return blen; +} + +/* Attribute: vendor (RO) */ +static ssize_t vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "ti," HAPTICS_DEVICE_NAME); +} + +DEVICE_ATTR_RO(path); +DEVICE_ATTR_RO(vendor); + +static struct attribute *vibrator_attrs[] = { + &dev_attr_path.attr, + &dev_attr_vendor.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(vibrator); + +static struct class vibrator_class = { + .name = "vibrator", + .owner = THIS_MODULE, + .dev_groups = vibrator_groups, +}; + +static int +drv2624_class_vibrator(struct drv2624_data *ctrl, bool create) +{ + int error = 0; + static struct device *vib_class_dev; + static int minor; + + if (create) { + error = class_register(&vibrator_class); + if (error) + return error; + + minor = ida_simple_get(&minors, + ctrl->i2c_client->addr, 0, GFP_KERNEL); + if (minor < 0) + minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL); + + dev_info(ctrl->dev, "assigned minor %d\n", minor); + vib_class_dev = device_create(&vibrator_class, NULL, + MKDEV(VIBDEV_MAJOR, minor), + ctrl, HAPTICS_DEVICE_NAME); + if (IS_ERR(vib_class_dev)) { + error = PTR_ERR(vib_class_dev); + vib_class_dev = NULL; + return error; + } + } else { + if (!vib_class_dev) + return -ENODEV; + device_destroy(&vibrator_class, MKDEV(VIBDEV_MAJOR, minor)); + vib_class_dev = NULL; + class_unregister(&vibrator_class); + } + + return 0; +} + +static int +drv2624_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct drv2624_data *ctrl; + + struct drv2624_platform_data *pdata; + int err = 0; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "%s:I2C check failed\n", __func__); + return -ENODEV; + } + + if (client->dev.of_node) + pdata = drv2624_of_init(client); + else + pdata = client->dev.platform_data; + + if (pdata == NULL) { + dev_err(&client->dev, "platform data is NULL, exiting\n"); + return -ENODEV; + } + + ctrl = + devm_kzalloc(&client->dev, sizeof(struct drv2624_data), GFP_KERNEL); + if (ctrl == NULL) { + dev_err(&client->dev, "%s:no memory\n", __func__); + return -ENOMEM; + } + + ctrl->reduced_pwr_capable = drv2624_reduced_pwr_on(pdata); + + ctrl->i2c_client = client; + ctrl->dev = &client->dev; + ctrl->mpRegmap = devm_regmap_init_i2c(client, &drv2624_i2c_regmap); + if (IS_ERR(ctrl->mpRegmap)) { + err = PTR_ERR(ctrl->mpRegmap); + dev_err(ctrl->dev, + "%s:Failed to allocate register map: %d\n", + __func__, err); + return err; + } + + memcpy(&ctrl->msPlatData, pdata, sizeof(struct drv2624_platform_data)); + i2c_set_clientdata(client, ctrl); + + if (gpio_is_valid(ctrl->msPlatData.mnGpioNPWR)) { + err = devm_gpio_request_one(ctrl->dev, ctrl->msPlatData.mnGpioNPWR, + GPIOF_OUT_INIT_LOW, "DRV2624_NPWR"); + if (err < 0) { + dev_err(ctrl->dev, "%s: GPIO %d request PWR error\n", + __func__, ctrl->msPlatData.mnGpioNPWR); + return err; + } + + gpio_direction_output(ctrl->msPlatData.mnGpioNPWR, 1); + udelay(100); + } + + if (gpio_is_valid(ctrl->msPlatData.mnGpioNRST)) { + err = devm_gpio_request_one(ctrl->dev, ctrl->msPlatData.mnGpioNRST, + GPIOF_OUT_INIT_LOW, "DRV2624_NRST"); + if (err < 0) { + dev_err(ctrl->dev, "%s: GPIO %d request NRST error\n", + __func__, ctrl->msPlatData.mnGpioNRST); + return err; + } + + gpio_direction_output(ctrl->msPlatData.mnGpioNRST, 0); + msleep(1); + gpio_direction_output(ctrl->msPlatData.mnGpioNRST, 1); + msleep(2); + } + + err = drv2624_reg_read(ctrl, DRV2624_REG_ID); + if (err < 0) { + dev_err(ctrl->dev, "%s, i2c bus fail (%d)\n", __func__, err); + goto exit_gpio_request_failed; + } + + ctrl->mnDeviceID = err & 0xF0; + dev_info(ctrl->dev, "%s, %s: id 0x%2x\n", + __func__, (err & 0xF0 ? "DRV2625":"DRV2624"), err); + + if (((ctrl->mnDeviceID) != 0x10) && ((ctrl->mnDeviceID) != 0x00)) { + dev_err(ctrl->dev, "%s:TI Chip ID check failed, id = 0x%x\n", + __func__, err); + err = -ENODEV; + goto exit_gpio_request_failed; + }; + + dev_init_platform_data(ctrl); + + if (gpio_is_valid(ctrl->msPlatData.mnGpioINT)) { + err = devm_gpio_request_one(ctrl->dev, ctrl->msPlatData.mnGpioINT, + GPIOF_DIR_IN, "DRV2624_INT"); + + if (err < 0) { + dev_err(ctrl->dev, + "%s: GPIO %d request INT error\n", + __func__, ctrl->msPlatData.mnGpioINT); + goto exit_gpio_request_failed; + } + + err = devm_request_threaded_irq(ctrl->dev, + client->irq, NULL, drv2624_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->name, ctrl); + if (err < 0) { + dev_err(ctrl->dev, "%s: request_irq failed\n", __func__); + goto exit_gpio_request_failed; + } + } + + g_DRV2624data = ctrl; + + Haptics_init(ctrl); + + if (!ctrl->msPlatData.no_firmware) { + err = request_firmware_nowait(THIS_MODULE, + FW_ACTION_HOTPLUG, "drv2624.bin", + &(client->dev), GFP_KERNEL, ctrl, + HapticsFirmwareLoad); + } + + if (ctrl->msPlatData.auto_cal) { + err = dev_auto_calibrate(ctrl); + if (err < 0) + dev_err(ctrl->dev, "%s: ERROR calibration\n", __func__); + } +/* + ctrl->factory_mode = (!strncmp("mot-factory", bi_bootmode(), BOOTMODE_MAX_LEN)) || + (!strncmp("factory", bi_bootmode(), BOOTMODE_MAX_LEN)); +*/ + ctrl->factory_mode = 0; + + if (ctrl->reduced_pwr_capable) { + /* init reduced force flag to "1" */ + /* modservice will flip flag if MOD is attached */ + atomic_set(&ctrl->reduce_pwr, 1); + err = sysfs_create_file(&client->dev.kobj, &dev_attr_reduce.attr); + if (err < 0) + dev_err(ctrl->dev, "%s: Failed to create reduce attributes\n", + __func__); + } + + drv2624_class_vibrator(ctrl, true); + + dev_info(ctrl->dev, "drv2624 probe succeeded\n"); + + return 0; + + exit_gpio_request_failed: + + dev_err(ctrl->dev, "%s failed, err=%d\n", __func__, err); + return err; +} + +static int drv2624_remove(struct i2c_client *client) +{ + struct drv2624_data *ctrl = i2c_get_clientdata(client); + int i = 0; + + if (gpio_is_valid(ctrl->msPlatData.mnGpioNRST)) + devm_gpio_free(ctrl->dev, ctrl->msPlatData.mnGpioNRST); + + if (gpio_is_valid(ctrl->msPlatData.mnGpioINT)) + devm_gpio_free(ctrl->dev, ctrl->msPlatData.mnGpioINT); + + devm_free_irq(ctrl->dev, client->irq, ctrl); + cancel_work_sync(&ctrl->vibrator_work); + drv2624_class_vibrator(ctrl, false); + wake_lock_destroy(&ctrl->wklock); + misc_deregister(&drv2624_misc); + for (i = 0; i < ARRAY_SIZE(drv2624_led_classdev_attrs); i++) + sysfs_remove_file(&ctrl->cdev.dev->kobj, + &drv2624_led_classdev_attrs[i].attr); + devm_led_classdev_unregister(ctrl->dev, &(ctrl->cdev)); + + return 0; +} + +static struct i2c_device_id drv2624_id_table[] = { + {HAPTICS_DEVICE_NAME, 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, drv2624_id_table); + +static struct i2c_driver drv2624_driver = { + .driver = { + .name = HAPTICS_DEVICE_NAME, + .owner = THIS_MODULE, + }, + .id_table = drv2624_id_table, + .probe = drv2624_probe, + .remove = drv2624_remove, +}; + +static int __init drv2624_init(void) +{ + return i2c_add_driver(&drv2624_driver); +} + +static void __exit drv2624_exit(void) +{ + i2c_del_driver(&drv2624_driver); +} + +module_init(drv2624_init); +module_exit(drv2624_exit); + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("Driver for " HAPTICS_DEVICE_NAME); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/drv8424/Android.mk b/drivers/misc/drv8424/Android.mk new file mode 100644 index 000000000000..65d10f178694 --- /dev/null +++ b/drivers/misc/drv8424/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := drv8424_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/drv8424/Kbuild b/drivers/misc/drv8424/Kbuild new file mode 100644 index 000000000000..a86fd171db15 --- /dev/null +++ b/drivers/misc/drv8424/Kbuild @@ -0,0 +1,10 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +#ifeq ($(CONFIG_LIBER_DTB), y) +#EXTRA_CFLAGS += -DCONFIG_USE_HW_CLK +#endif + +obj-m := drv8424_mmi.o +drv8424_mmi-objs := drv8424.o diff --git a/drivers/misc/drv8424/Makefile b/drivers/misc/drv8424/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/misc/drv8424/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/misc/drv8424/drv8424.c b/drivers/misc/drv8424/drv8424.c new file mode 100644 index 000000000000..908629ab73f9 --- /dev/null +++ b/drivers/misc/drv8424/drv8424.c @@ -0,0 +1,2755 @@ +#define pr_fmt(fmt) "drv8424: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_STEP_FREQ 2400 +#define MOTOR_CLASS_NAME "drv8424" +#define MOTOR_CONTROL "control" +#define MOTOR_HW_CLK 9600 //9.6KHz clock +#define MOTOR_HW_CLK_NAME "gcc_gp3" + +#define MOTOR_DEFAULT_EXPIRE 5000000 //5s timeout +#define MOTOR_DETECT_EXPIRE 3000000 //3s timeout +#define INT_20MS 20 + +#define MOTOR_MODE_SPEED 0 +#define MOTOR_MODE_STEP 1 + +#define DIR_EXTEND 1 +#define DIR_WITHDRAW 0 + +#define RESET_TIME 1000 + +#define UNINITIALIZED -10000 +#define OUT_OF_RANGE 10000 + +#define LOGD(fmt, args...) pr_err(fmt, ##args) +#define PARANOIC(fmt, args...) pr_debug(fmt, ##args) +#undef MOTOR_SLOT_DBG + +enum position_id { + POS_UNKNOWN = 0, + POS_COMPACT, + POS_EXPANDED, + POS_PEEK, + POS_MAX, +}; + +static const char *position_labels[] = { + "UNKNOWN", + "COMPACT", + "EXPANDED", + "PEEK", +}; + +#define ARGS_NUM_MIN 2 +typedef struct { + int index; + int value; +} sensor_scan_t; + +/* + * Sensor data defined in the range from -5000 to 5000 that corresponds + * to the range of angles from -50 degrees to 50 degrees. + * Each value represented by float with 2 digits precision and then + * multiplied by 100 to fit decimal range above. + * Everything is just for the reference purpose and will be + * adjusted as more details become available + */ +static sensor_scan_t scanner[POS_MAX] = { + {-1, -1}, + {1, 0}, /* compact position: magnet[1], value 0 degrees */ + {0, 0}, /* expanded position: magnet[0], value 0 degrees */ + {2, 0}, /* peek position: magnet[2], value 0 degrees */ +}; + +enum status_id { + STATUS_UNKNOWN, + STATUS_STOPPED_COMPACT, + STATUS_STOPPED_EXPANDED, + STATUS_STOPPED_PEEK, + STATUS_MOVING_OUT, + STATUS_MOVING_IN, + STATUS_QUERYING_POS, + STATUS_FAULTING, +}; + +static const char *status_labels[] = { + "UNKNOWN", + "STOPPED_COMPACT", + "STOPPED_EXPANDED", + "STOPPED_PEEK", + "EXPANDING", + "WITHDRAWING", + "QUERYING_POSITION", + "FAULTING", +}; + +enum gpios_index { + MOTOR_POWER_EN = 0, /* BOOST */ + MOTOR_FAULT1_INT, /* nFAULT1 INT */ + MOTOR_FAULT2_INT, /* nFAULT2 INT */ + MOTOR_STEP, /* STEP */ + MOTOR_DIR, /* DIR */ + MOTOR_MODE1, /* set to 0 in HW */ + MOTOR_MODE2, /* set to 0 in HW */ + MOTOR_EN, /* set to 1 in HW */ + MOTOR_SLEEP, /* nSLEEP */ + MOTOR_T0, /* set to 0 in HW */ + MOTOR_T1, /* set to 0 in HW */ + MOTOR_ACTIVE, /* signal GPIO */ + MOTOR_MAX_GPIO, + MOTOR_UNKNOWN = MOTOR_MAX_GPIO +}; + +static const char* const gpios_labels[] = { + "MOTOR_POWER_EN", + "MOTOR_FAULT1_INT", + "MOTOR_FAULT2_INT", + "MOTOR_STEP", + "MOTOR_DIR", + "MOTOR_MODE1", + "MOTOR_MODE2", + "MOTOR_EN", + "MOTOR_SLEEP", + "MOTOR_T0", + "MOTOR_T1", + "MOTOR_ACTIVE", + "MOTOR_UNKNOWN" +}; + +static const unsigned long hw_clocks[] = { + DEFAULT_STEP_FREQ, //0,0 software clock + 4800, //0,Z 2*2400 + 9600, //0,1 4*2400 + 19200, //Z,0 8*2400 + 9600, //Z,Z 4*2400 + 38400, //Z,1 16*2400 + 19200, //1,0 8*2400 + 76800, //1,Z 32*2400 + 38400, //1,1 16*2400 +}; + +enum motor_step_mode { + FULL_STEP = 0, //0,0 + STEP_2_RISING, //0,Z + STEP_4_RISING, //0,1 + STEP_8_RISING, //Z,0 + STEP_8_BOTH_EDEG, //Z,Z + STEP_16_RISING, //Z,1 + STEP_16_BOTH_EDGE, //1,0 + STEP_32_RISING, //1,Z + STEP_32_BOTH_EDEG, //1,1 +}; + +enum motor_torque { + TORQUE_FULL = 0, //0,0 + TORQUE_87, //0,Z + TORQUE_75, //0,1 + TORQUE_62, //Z,0 + TORQUE_50, //Z,Z + TORQUE_37, //Z,1 + TORQUE_25, //1,0 + TORQUE_12, //1,Z + TORQUE_DISABLE, //1,1 +}; + +//Keep in sequence with dtsi. +enum motor_pins_mode { + MODE0_LOW = 0, + MODE0_HIGH, + MODE0_DISABLE, + MODE1_LOW, + MODE1_HIGH, + MODE1_DISABLE, + T0_LOW, + T0_HIGH, + T0_DISABLE, + T1_LOW, + T1_HIGH, + T1_DISABLE, + INT_DEFAULT, + CLK_ACTIVE, + CLK_SLEEP, + INIT_STATE, + PINS_END +}; + +//Keep in same name with the pinctrl-names of dtsi +static const char* const pins_state[] = { + "m0_low", "m0_high", "m0_disable", + "m1_low", "m1_high", "m1_disable", + "t0_low", "t0_high", "t0_disable", + "t1_low", "t1_high", "t1_disable", + "drv8424_int_default", + "drv8424_clk_active", "drv8424_clk_sleep", + "init_state", + NULL +}; + +enum regime_idx { + SQ_FULL, + SQ_PROLONGED, + SQ_SHORT, + SQ_TINY, + SQ_SHOW_MAX, + SQ_SLOWMO, + SQ_MAX, +}; + +static const char *regime_names[] = { + "FULL", + "PROLONGED", + "SHORT", + "TINY", + "dummy", + "SLOWMO" +}; + +/* PPS Pulses + stage 1: 400 24 + stage 2: 1400 56 + stage 3: 3000 6252 + stage 4: 2000 12 +*/ +#define MAX_STAGE_LEGS 10 +typedef struct { + unsigned freq, ceiling; +} motor_stage; + +/* Default sequences for 2.3pitch */ +static motor_stage initial_data_2p3[SQ_MAX][MAX_STAGE_LEGS] = { + { /* FULL 39mm */ + {600,60}, {1000,60}, {1400,60}, {1800,4680}, + }, + { /* PROLONGED 44mm */ + {600,60}, {1000,60}, {1400,60}, {1800,5680}, + }, + { /* SHORT 5mm */ + {600,60}, {1000,770}, + }, + { /* TINY 1mm */ + {600,60}, {1000,68}, + }, + { /* dummy to continue beyond SQ_SHOW_MAX */ + {0, 0}, + }, + { /* SLOWMO */ + {600,4900}, + }, +}; + +/* Sequences for 2.8pitch */ +static motor_stage initial_data_2p8[SQ_MAX][MAX_STAGE_LEGS] = { + { /* FULL 39mm */ + {600,60}, {1000,60}, {1400,60}, {1800,3840}, + }, + { /* PROLONGED 44mm */ + {600,60}, {1000,60}, {1400,60}, {1800,4460}, + }, + { /* SHORT 5mm */ + {600,60}, {1000,600}, + }, + { /* TINY 1mm */ + {600,108}, + }, + { /* dummy to continue beyond SQ_SHOW_MAX */ + {0, 0}, + }, + { /* SLOWMO */ + {600,4100}, + }, +}; +/* 10,000,000 10,000,000 4,000,000 2,000,000 15,000,000 */ +//static unsigned timeoutMicroS[] = { 10000000, 13000000, 4000000, 2000000, 0, 15000000 }; + +enum cmds { + CMD_FAULT, + CMD_STATUS, + CMD_POSITION, + CMD_TIMEOUT, + CMD_RECOVERY, + CMD_TIMER_START, + CMD_TIMER_STOP, +}; + +static const char *cmd_labels[] = { + "CMD_FAULT", + "CMD_STATUS", + "CMD_POSITION", + "CMD_TIMEOUT", + "CMD_RECOVERY", + "CMD_TIMER_START", + "CMD_TIMER_STOP" +}; + +#define MAX_GPIOS MOTOR_UNKNOWN +typedef struct motor_control { + struct regulator *vdd; + int32_t ptable[MOTOR_MAX_GPIO]; + size_t tab_cells; + struct pinctrl* pins; + struct pinctrl_state *pin_state[PINS_END]; + const char* const * plabels; +} motor_control; + +#define CLOCK_NAME_LEN 16 +typedef struct motor_device { + char clock_name[CLOCK_NAME_LEN]; + struct device* dev; + struct device* sysfs_dev; + struct class* drv8424_class; + struct workqueue_struct* motor_wq; + struct clk * pwm_clk; + struct delayed_work motor_work; + struct task_struct *motor_task; + wait_queue_head_t sync_complete; + wait_queue_head_t status_wait; + wait_queue_head_t position_wait; + struct hrtimer stepping_timer; + struct hrtimer timeout_timer; + motor_control mc; + spinlock_t mlock; + struct mutex mx_lock; + atomic_t fault_irq; + bool faulting; + bool use_2p8_pitch; + unsigned step_freq; + unsigned long step_period; + unsigned long step_ceiling; + unsigned long cur_clk; + atomic_t step_count; + atomic_t stepping; + atomic_t status; + atomic_t position; + atomic_t destination; + unsigned slot; + unsigned mode, cur_mode; + unsigned torque; + unsigned time_out; + unsigned half; + unsigned stage, max_stages; + unsigned regime; + motor_stage sequencer[SQ_MAX][MAX_STAGE_LEGS]; + bool double_edge; + bool hw_clock; + bool power_default_off; + bool support_mode; + bool support_torque; + bool sensors_off; + bool irq_enabled; + bool do_not_arm_irq; + int level:1; + unsigned power_en:1; + unsigned nsleep:1; + unsigned nEN:1; + unsigned dir:1; + unsigned user_sync_complete:1; + unsigned status_update_ready:1; + unsigned position_update_ready:1; + struct kfifo cmd_pipe; + int volatile sensor_data[3]; + atomic_t samples; + bool detect_incomplete; + bool ready; +} motor_device; + +#define RESET_SENSOR_DATA(data) do { \ + md->sensor_data[0] = md->sensor_data[1] = \ + md->sensor_data[2] = data; \ + } while(0) + +#define GPIO_OUTPUT_DIR(g, p) do { \ + if (gpio_is_valid(g)) \ + gpio_direction_output(g, p); \ + } while(0) + +static int set_pinctrl_state(motor_device* md, unsigned state_index); +static void moto_drv8424_set_step_freq(motor_device* md, unsigned freq); +static void motor_set_sequencer(motor_device* md); + +#ifdef MOTOR_SLOT_DBG +#define RECORD_TIME(t) \ + do { \ + /* record earliest time only */ \ + if (!t) \ + t = ktime_get(); \ + } while(0) + +typedef struct { + ktime_t kTime; + void *tData; +} timed_data; + +static timed_data ArrayData[200]; +static timed_data ArrayOut[200]; +static unsigned max_slot = 200; +static unsigned slotD, slotP; +static DEFINE_MUTEX(logtime_mutex); +static ktime_t tZero, kTerm, kKill, kClean, kDetect, kSensorUp, kSensorDown; +static ktime_t kStepping[15000]; +static int steppingIdx; + +static void logtime_alloc(int esize) +{ + int i; + for (i = 0; i < max_slot; i++) { + ArrayData[i].tData = kzalloc(esize, GFP_KERNEL); + ArrayOut[i].tData = kzalloc(esize, GFP_KERNEL); + if (ArrayData[i].tData == NULL || + ArrayOut[i].tData == NULL) { + max_slot = i; + break; + } + } + pr_err("max slot set: %u\n", max_slot); +} + + +static void inline logtime_stepping_store(void) +{ + + kStepping[steppingIdx++] = ktime_get(); + if (steppingIdx >= ARRAY_SIZE(kStepping)) + steppingIdx--; +} + +static void logtime_store(void *ptr, int esize, int t) +{ + timed_data *dptr; + unsigned *cptr; + + if (!t) { + cptr = &slotD; + dptr = &ArrayData[*cptr]; + } else { + cptr = &slotP; + dptr = &ArrayOut[*cptr]; + } + + if ((*cptr + 1) < max_slot) { + dptr->kTime = ktime_get(); + memcpy(dptr->tData, ptr, esize); + *cptr = *cptr + 1; + } +} + +static void logtime_reset(void) +{ + int i; + size_t esize = sizeof(int)*3; + + slotD = slotP = 0U; + for (i = 0; i < max_slot; i++) { + ArrayData[i].kTime = ArrayOut[i].kTime = (ktime_t)0; + memset(ArrayData[i].tData, 0, esize); + memset(ArrayOut[i].tData, 0, esize); + } + tZero = kDetect = kTerm = kKill = kClean = kSensorUp = kSensorDown = (ktime_t)0; + + for (i = 0; i < ARRAY_SIZE(kStepping); i++) + kStepping[i] = (ktime_t)0; + steppingIdx = 0; +} + +#define DETECT 0 +#define TERMINATE 1 +#define KILL 2 +#define CLEAN 3 +#define SENS_UP 4 +#define SENS_DOWN 5 + +#define ZERO_CHK(t) (!t ? t : ktime_ms_delta(t, tZero)) + +#define LAST_STEPPING_TIMES 20 + +static void logtime_show(const char *f) +{ + int *iptr, *optr, i, o, endIdx; + ktime_t tdiff; + ktime_t td[10]; + + mutex_lock(&logtime_mutex); + endIdx = steppingIdx; + pr_err("Called from: %s; Available %u sample(s) zero time %llu\n", f, slotD, tZero); + if (!slotD) + return; + for (i = 0; i < slotD; i++) { + iptr = (int *)ArrayData[i].tData; + o = i; +ff: + optr = (int *)ArrayOut[o].tData; + if (*optr != 0) { + if (!(*iptr == *optr && + *(iptr+1) == *(optr+1) && + *(iptr+2) == *(optr+2))) { + if (++o > (slotD + 1)) + break; + goto ff; + } + } + tdiff = ktime_us_delta(ArrayOut[o].kTime, ArrayData[i].kTime); + pr_err("%d|%d) delta:%lldus <%d %d %d> [%d %d %d] {%llu-%llu}\n", i, o, tdiff, + *iptr, *(iptr+1), *(iptr+2), + *optr, *(optr+1), *(optr+2), + ArrayData[i].kTime, + ArrayOut[o].kTime); + } + td[SENS_UP] = ZERO_CHK(kSensorUp); + td[DETECT] = ZERO_CHK(kDetect); + td[SENS_DOWN] = ZERO_CHK(kSensorDown); + td[TERMINATE] = ZERO_CHK(kTerm); + td[KILL] = ZERO_CHK(kKill); + td[CLEAN] = ZERO_CHK(kClean); + pr_err("TZ=%llu SensUp=%llu(%lldms), Detect=%llu(%lldms), SensDown=%llu(%lldms), Term=%llu(%lldms), Kill=%llu(%lldms), Clean=%llu(%lldms)\n", + tZero, + kSensorUp, td[SENS_UP], + kDetect, td[DETECT], + kSensorDown, td[SENS_DOWN], + kTerm, td[TERMINATE], + kKill, td[KILL], + kClean, td[CLEAN]); + /* print the last 2o stepping times */ + pr_err("logged %d steppings; the last sample @%llu\n", endIdx, ArrayData[slotD-1].kTime); + if (endIdx > LAST_STEPPING_TIMES) { + for (i = endIdx - LAST_STEPPING_TIMES; i < endIdx; i++) { + /* print only if pulse happened after the last + * registered sensors data arrived */ + if (kStepping[i] > ArrayData[slotD-1].kTime) + pr_err("%llu\n", kStepping[i]); + } + } + logtime_reset(); + mutex_unlock(&logtime_mutex); +} + +#else +#define RECORD_TIME(...) +#define logtime_alloc(...) +#define logtime_store(...) +#define logtime_stepping_store(...) +#define logtime_show(...) +#define logtime_reset() +#endif + +#define POSITION_DETECT_INIT(_Pos, _Sta) do { \ + if (!md->sensors_off) { \ + atomic_set(&md->position, POS_UNKNOWN); \ + atomic_set(&md->status, STATUS_UNKNOWN); \ + atomic_set(&md->destination, POS_UNKNOWN); \ + RECORD_TIME(tZero); \ + /* run position detection */ \ + moto_drv8424_set_sensing(md, true); \ + dev_info(md->dev, "Start position detection\n"); \ + } else { \ + atomic_set(&md->position, _Pos); \ + moto_drv8424_cmd_push(md, CMD_POSITION, 0); \ + atomic_set(&md->status, _Sta); \ + moto_drv8424_cmd_push(md, CMD_STATUS, 0); \ + md->ready = true; \ + dev_info(md->dev, "Assume %s position\n", position_labels[_Pos]); \ + } \ + } while(0) + +#define POSITION_RANGE_CHK(n) \ + ((n >= POS_UNKNOWN) && (n < POS_MAX)) + +#define STOP_STEPPING_SIGNAL \ + do { \ + atomic_set(&md->stepping, 0); \ + atomic_set(&md->step_count, 0); \ + RECORD_TIME(kKill); \ + if (hrtimer_try_to_cancel(&md->stepping_timer) < 0) \ + hrtimer_cancel_wait_running(&md->stepping_timer); \ + } while(0) + +#define SIMPLE_MOTOR_STOP do { \ + char *msg = NULL; \ + msg = motor_stop(__func__, true, md, true); \ + if (msg) \ + LOGD("%s", msg); \ + } while(0) + + +static ktime_t adapt_time_helper(ktime_t usec) +{ + return ns_to_ktime(usec * 1000); +} +#if 0 +static inline bool is_hw_clk(motor_device* md) +{ + return (md->hw_clock && (md->cur_clk != hw_clocks[0])); +} +#endif +void sleep_helper(unsigned usec) +{ + if (usec < 500) { //<500us, udelay + udelay(usec); + } else if (usec >= 500 && usec < 20000) { //500us - 20ms, usleep_range + usleep_range(usec, usec + 10); + } else { //>= 20ms, msleep + msleep(usec); + } +} + +static int moto_drv8424_set_regulator_power(motor_device* md, bool en) +{ + motor_control * mc = &md->mc; + int err = 0; + + if (en) { + err = regulator_enable(mc->vdd); + if (err) { + dev_err(md->dev, "Failed to enable VDD ret=%d\n", err); + goto exit; + } + LOGD("regulator enabled\n"); + } else { + err = regulator_disable(mc->vdd); + if (err) { + dev_err(md->dev, "Failed to disable VDD ret=%d\n", err); + goto exit; + } + LOGD("regulator disabled\n"); + } + return 0; +exit: + return err; +} +#if 0 +static int set_motor_clk(motor_device* md, bool en) +{ + int ret = 0; + if (en) { + set_pinctrl_state(md, CLK_ACTIVE); + ret = clk_set_rate(md->pwm_clk, md->cur_clk); + if (ret < 0) { + dev_err(md->dev, "Failed to set clk rate to %ld\n", md->cur_clk); + ret = -ENODEV; + goto soft_clk; + } + ret = clk_prepare_enable(md->pwm_clk); + if (ret < 0) { + dev_err(md->dev, "Failed to clk_prepare_enable\n"); + ret = -ENODEV; + goto soft_clk; + } + } else { + clk_disable_unprepare(md->pwm_clk); + dev_info(md->dev, "disable clock"); + set_pinctrl_state(md, CLK_SLEEP); + } + return 0; + +soft_clk: + md->hw_clock = false; + return ret; +} +#endif +static int init_motor_clk(motor_device* md) +{ + int ret = 0; + + md->pwm_clk = devm_clk_get(md->dev, md->clock_name); + if (IS_ERR(md->pwm_clk)) { + dev_err(md->dev, "Get clk error, motor is not drived\n"); + ret = -ENODEV; + goto soft_clk; + } + return 0; +soft_clk: + md->hw_clock = false; + return ret; +} + +static int set_pinctrl_state(motor_device* md, unsigned state_index) +{ + motor_control* mc = &md->mc; + int ret = 0; + + if (state_index >= PINS_END) { + dev_err(md->dev, "Illegal pin index\n"); + ret = -EINVAL; + goto err; + } + dev_dbg(md->dev, "setting punctrl for state '%s'\n", pins_state[state_index]); + mc->pins = devm_pinctrl_get(md->dev); + if (IS_ERR_OR_NULL(mc->pins)) { + ret = PTR_ERR(mc->pins); + dev_err(md->dev, "Failed to get pinctrl %d\n", ret); + goto err; + } + mc->pin_state[state_index] = pinctrl_lookup_state(mc->pins, pins_state[state_index]); + if (IS_ERR_OR_NULL(mc->pin_state[state_index])) { + ret = PTR_ERR(mc->pin_state[state_index]); + dev_err(md->dev, "Cannot find pin_state '%s' %d\n", pins_state[state_index], ret); + goto err_pin_state; + } + ret = pinctrl_select_state(mc->pins, mc->pin_state[state_index]); + if (ret) { + dev_err(md->dev, "Cannot set pin_state '%s' %d\n", pins_state[state_index], ret); + } + +err_pin_state: + pinctrl_put(mc->pins); +err: + return ret; +} + +static void set_irq_state(motor_device *md, bool state) +{ + if (md->do_not_arm_irq) { + LOGD("setting irq %d ignored\n", state); + return; + } + if (state) { + if (md->irq_enabled == true) + return; + if (gpio_is_valid(md->mc.ptable[MOTOR_FAULT1_INT])) + enable_irq(gpio_to_irq(md->mc.ptable[MOTOR_FAULT1_INT])); + if (gpio_is_valid(md->mc.ptable[MOTOR_FAULT2_INT])) + enable_irq(gpio_to_irq(md->mc.ptable[MOTOR_FAULT2_INT])); + md->irq_enabled = true; + } else { + if (md->irq_enabled == false) + return; + if (gpio_is_valid(md->mc.ptable[MOTOR_FAULT1_INT])) + disable_irq(gpio_to_irq(md->mc.ptable[MOTOR_FAULT1_INT])); + if (gpio_is_valid(md->mc.ptable[MOTOR_FAULT2_INT])) + disable_irq(gpio_to_irq(md->mc.ptable[MOTOR_FAULT2_INT])); + md->irq_enabled = false; + + } +} + +static void moto_drv8424_set_motor_torque(motor_device* md) +{ + if (!md->support_torque) + return; + switch (md->torque) { + case TORQUE_FULL: + set_pinctrl_state(md, T0_LOW); + set_pinctrl_state(md, T1_LOW); + break; + case TORQUE_87: + set_pinctrl_state(md, T0_DISABLE); + set_pinctrl_state(md, T1_LOW); + break; + case TORQUE_75: + set_pinctrl_state(md, T0_HIGH); + set_pinctrl_state(md, T1_LOW); + break; + case TORQUE_62: + set_pinctrl_state(md, T0_LOW); + set_pinctrl_state(md, T1_DISABLE); + break; + case TORQUE_50: + set_pinctrl_state(md, T0_DISABLE); + set_pinctrl_state(md, T1_DISABLE); + break; + case TORQUE_37: + set_pinctrl_state(md, T0_HIGH); + set_pinctrl_state(md, T1_DISABLE); + break; + case TORQUE_25: + set_pinctrl_state(md, T0_LOW); + set_pinctrl_state(md, T1_HIGH); + break; + case TORQUE_12: + set_pinctrl_state(md, T0_DISABLE); + set_pinctrl_state(md, T1_HIGH); + break; + case TORQUE_DISABLE: + set_pinctrl_state(md, T0_HIGH); + set_pinctrl_state(md, T1_HIGH); + break; + default: + pr_err("Unsupported torque %d\n", md->torque); + return; + } + LOGD("pins T0 & T1 updated\n"); +} + +static void moto_drv8424_set_motor_mode(motor_device* md) +{ + if (!md->support_mode) { + md->cur_mode = md->mode; + return; + } + switch (md->mode) { + case FULL_STEP: + set_pinctrl_state(md, MODE0_LOW); + set_pinctrl_state(md, MODE1_LOW); + break; + case STEP_2_RISING: + set_pinctrl_state(md, MODE0_DISABLE); + set_pinctrl_state(md, MODE1_LOW); + break; + case STEP_4_RISING: + set_pinctrl_state(md, MODE0_HIGH); + set_pinctrl_state(md, MODE1_LOW); + break; + case STEP_8_RISING: + set_pinctrl_state(md, MODE0_LOW); + set_pinctrl_state(md, MODE1_DISABLE); + break; + case STEP_8_BOTH_EDEG: + set_pinctrl_state(md, MODE0_DISABLE); + set_pinctrl_state(md, MODE1_DISABLE); + break; + case STEP_16_RISING: + set_pinctrl_state(md, MODE0_HIGH); + set_pinctrl_state(md, MODE1_DISABLE); + break; + case STEP_16_BOTH_EDGE: + set_pinctrl_state(md, MODE0_LOW); + set_pinctrl_state(md, MODE1_HIGH); + break; + case STEP_32_RISING: + set_pinctrl_state(md, MODE0_DISABLE); + set_pinctrl_state(md, MODE1_HIGH); + break; + case STEP_32_BOTH_EDEG: + set_pinctrl_state(md, MODE0_HIGH); + set_pinctrl_state(md, MODE1_HIGH); + break; + default: + pr_err("Unsupported mode %d\n", md->mode); + return; + } + if (md->cur_mode != md->mode) { + if (md->hw_clock) { + if (md->mode == FULL_STEP) { + md->cur_clk = hw_clocks[md->mode]; + set_pinctrl_state(md, CLK_SLEEP); + dev_info(md->dev, "md->mode is FULL_STEP\n"); + } else { + md->cur_clk = hw_clocks[md->mode]; + dev_info(md->dev, "Switch hw clock\n"); + } + } + md->cur_mode = md->mode; + md->cur_mode = md->mode; + } + usleep_range(800, 900); + LOGD("pins MODE0 & MODE1 updated\n"); +} + +static void moto_drv8424_set_motor_dir(motor_device* md) +{ + motor_control* mc = &md->mc; + + GPIO_OUTPUT_DIR(mc->ptable[MOTOR_DIR], md->dir); + usleep_range(800, 900); + PARANOIC("pin DIR set %d\n", md->dir); +} + +/* Set operating modes, only control nSleep + * Set nSleep & nEn | H-Bridge | Vreg | Sequencer + * 1 0 | Op | Op | Op + * 1 1 | Dis | Op | Op + * 0 X | Dis | Dis | Dis + * Fault | Dis |Depends on Fault + */ +static void moto_drv8424_set_motor_opmode(motor_device* md) +{ + motor_control* mc = &md->mc; + + GPIO_OUTPUT_DIR(mc->ptable[MOTOR_SLEEP], md->nsleep); + //Twake 0.5ms Tsleep 0.7ms + usleep_range(800, 1000); + GPIO_OUTPUT_DIR(mc->ptable[MOTOR_EN], md->nEN); + PARANOIC("pin SLEEP set %d\n", md->nsleep); +} + +static int moto_drv8424_set_opmode(motor_device* md, unsigned opmode) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&md->mlock, flags); + if (md->nsleep == opmode) { + ret = -EINVAL; + goto exit; + } + md->nsleep = !!opmode; + md->nEN = !md->nsleep; + spin_unlock_irqrestore(&md->mlock, flags); + + PARANOIC("nsleep=%d, en=%d\n", md->nsleep, md->nEN); + return 0; +exit: + spin_unlock_irqrestore(&md->mlock, flags); + if (ret < 0) + dev_info(md->dev, "Unchanged the opmode status, ignore\n"); + return ret; +} + +static void inline moto_drv8424_cmd_push(motor_device* md, + int cmd, unsigned long delay) +{ + unsigned long flags; + + spin_lock_irqsave(&md->mlock, flags); + kfifo_put(&md->cmd_pipe, cmd); + spin_unlock_irqrestore(&md->mlock, flags); + queue_delayed_work(md->motor_wq, &md->motor_work, delay); +} + +static void moto_drv8424_set_sensing(motor_device* md, bool en) +{ + motor_control* mc = &md->mc; + + if (md->sensors_off) + return; + if (en) { + md->detect_incomplete = true; + /* signal sensor hub to start/stop sampling hall effect */ + GPIO_OUTPUT_DIR(mc->ptable[MOTOR_ACTIVE], 1); + RECORD_TIME(kSensorUp); + if (!md->power_en && !md->sensors_off) { + /* declare sensor querying stage when powered off */ + /* when motor powered status will indicate direction */ + atomic_set(&md->status, STATUS_QUERYING_POS); + moto_drv8424_cmd_push(md, CMD_STATUS, msecs_to_jiffies(INT_20MS)); + } + } else { + GPIO_OUTPUT_DIR(mc->ptable[MOTOR_ACTIVE], 0); + RECORD_TIME(kSensorDown); + } + PARANOIC("gpio ACTIVE set: %u\n", en ? 1 : 0); +} + +//Set VDD +static void moto_drv8424_set_power_en(motor_device* md) +{ + motor_control* mc = &md->mc; + GPIO_OUTPUT_DIR(mc->ptable[MOTOR_POWER_EN], md->power_en); + //Tpower 0.5ms + usleep_range(500, 1000); + PARANOIC("BOOST set %d\n", md->power_en); +} + +//Power sequence: PowerEn On->nSleep On->nSleep Off->PowerEn Off +static int moto_drv8424_set_power(motor_device* md, unsigned power) +{ + unsigned long flags; + int ret = 0; + + if (md->power_en == power) { + dev_dbg(md->dev, "Unchanged the power status, ignore\n"); + ret = -EINVAL; + goto exit; + } + PARANOIC("power_en=%u\n", !!power); + spin_lock_irqsave(&md->mlock, flags); + md->power_en = !!power; + spin_unlock_irqrestore(&md->mlock, flags); + ret = moto_drv8424_set_opmode(md, md->power_en); + if (ret < 0 ) { + goto exit; + } + if (!md->power_en) { + moto_drv8424_set_motor_opmode(md); + } + moto_drv8424_set_power_en(md); +exit: + if (md->power_en) { + logtime_reset(); + /* make sure zero time set before sensing status changed */ + RECORD_TIME(tZero); + } + /* Sensing can be On w/o motor powered */ + moto_drv8424_set_sensing(md, md->power_en ? true : false); + + return ret; +} + +static void moto_drv8424_set_regime(motor_device *md, unsigned regime) +{ + unsigned msn; + unsigned long flags; + + for (msn = 0; msn < MAX_STAGE_LEGS; msn++) { + if (md->sequencer[regime][msn].freq && md->sequencer[regime][msn].ceiling) + continue; + break; + } + spin_lock_irqsave(&md->mlock, flags); + md->regime = regime; + md->stage = 0; + md->max_stages = msn; /* msn value is 0-based */ + spin_unlock_irqrestore(&md->mlock, flags); + dev_info(md->dev, "Set active regime: %s, stages # %u\n", + regime_names[md->regime], msn+1); +} + +#if 0 +static int moto_drv8424_enable_clk(motor_device* md, bool en) +{ + return set_motor_clk(md, en); +} +#endif + +static void moto_drv8424_drive_stage_init(motor_device* md) +{ + atomic_set(&md->stepping, 1); + atomic_set(&md->step_count, 1); + md->double_edge = false; + md->half = md->step_period >> 1; + md->level = 1; + if (md->mode == STEP_8_BOTH_EDEG + || md->mode == STEP_16_BOTH_EDGE + || md->mode == STEP_32_BOTH_EDEG) { + md->double_edge = true; + } +} + +static bool moto_drv8424_next_stage(motor_device* md) +{ + unsigned freq; + motor_stage *ms; + + if (++md->stage > md->max_stages) { + md->max_stages = 0; + md->stage = 0; + return false; + } + + ms = &md->sequencer[md->regime][md->stage - 1]; + freq = ms->freq; + md->step_ceiling = ms->ceiling; + moto_drv8424_set_step_freq(md, freq); + moto_drv8424_drive_stage_init(md); + + return true; +} + +static int moto_drv8424_drive_sequencer(motor_device* md) +{ + if (md->faulting) { + dev_warn(md->dev, "Motor faulting: action cancelled!!!\n"); + return -EBUSY; + } + + md->ready = false; + moto_drv8424_next_stage(md); + moto_drv8424_set_motor_torque(md); + moto_drv8424_set_motor_dir(md); + moto_drv8424_set_motor_mode(md); + moto_drv8424_set_motor_opmode(md); + /* can judge about moving direction based on DIR or destination position */ + atomic_set(&md->status, md->dir ? STATUS_MOVING_OUT : STATUS_MOVING_IN); + moto_drv8424_cmd_push(md, CMD_STATUS, 0); + PARANOIC("sequencer init: stages %u ceiling %lu, freq %uHz period %luns, half %uns\n", + md->max_stages, md->step_ceiling, md->step_freq, md->step_period, md->half); + if (atomic_read(&md->stepping)) { + motor_control* mc = &md->mc; + + PARANOIC("Status updated: status %d\n", atomic_read(&md->status)); + GPIO_OUTPUT_DIR(mc->ptable[MOTOR_STEP], md->level); + atomic_inc(&md->step_count); + hrtimer_start(&md->stepping_timer, adapt_time_helper(md->half), HRTIMER_MODE_REL); + } + + return 0; +} + +/* this function is called with spinlock running!!! */ +static char *motor_stop(const char *f, bool lock, motor_device* md, bool clean) +{ + unsigned long flags; + char *retptr = NULL; + static char msg[128]; + + PARANOIC("step count %d\n", atomic_read(&md->step_count)); + if (clean) { + STOP_STEPPING_SIGNAL; + RECORD_TIME(kClean); + PARANOIC("step_count & stepping reset\n"); + } + + if (lock) + spin_lock_irqsave(&md->mlock, flags); + if (!md->user_sync_complete) { + md->user_sync_complete = true; + wake_up(&md->sync_complete); + snprintf(msg, 128, "called from: %s; waking up motor thread\n", f); + retptr = msg; + } + if (lock) + spin_unlock_irqrestore(&md->mlock, flags); + return retptr; +} + +static __ref int motor_kthread(void *arg) +{ + motor_device* md = (motor_device*)arg; + struct sched_param param = {.sched_priority = MAX_USER_RT_PRIO - 1}; + int value, ret = 0; + + sched_setscheduler(current, SCHED_FIFO, ¶m); + while (!kthread_should_stop()) { + md->user_sync_complete = false; + LOGD("wait for motor event\n"); + do { + ret = wait_event_interruptible(md->sync_complete, + md->user_sync_complete || kthread_should_stop()); + } while (ret != 0); + if (kthread_should_stop()) + break; + /* it's safe to call the following functions even if motor was not running */ + moto_drv8424_set_power(md, 0); + if (md->power_default_off) { + dev_info(md->dev, "vdd power off\n"); + moto_drv8424_set_regulator_power(md, false); + } + if (md->faulting) { + value = atomic_read(&md->fault_irq); + if (value != 0) { + moto_drv8424_cmd_push(md, CMD_FAULT, 0); + atomic_set(&md->status, STATUS_FAULTING); + moto_drv8424_cmd_push(md, CMD_STATUS, 0); + dev_warn(md->dev, "Motor #%d failure reported!!!\n", + irq_to_gpio(value) == md->mc.ptable[MOTOR_FAULT1_INT] ? 1 : 2); + } + goto show_stats; + } + /* + * In sensor driven position detection we need to handle + * motor slippage. When this happens, position will still + * be unknown even though stepper sequence completed. + */ + if (!md->sensors_off && md->detect_incomplete) { + if (md->regime == SQ_SLOWMO) { + /* make sure position is not unknown */ + atomic_set(&md->position, POS_COMPACT); + atomic_set(&md->status, STATUS_FAULTING); + moto_drv8424_cmd_push(md, CMD_STATUS, 0); + /* Unrecoverable motor fault */ + SIMPLE_MOTOR_STOP; + md->faulting = true; + /* make sure timeout is not stuck */ + md->time_out = 0; + dev_warn(md->dev, "Motor faulting state set!!!\n"); + goto show_stats; + } + /* Recovery from incomplete transition */ + moto_drv8424_cmd_push(md, CMD_RECOVERY, 0); + dev_warn(md->dev, "position recovery!!!\n"); + goto show_stats; + } + + /* Need to stop stepping to avoid overshoot, But only can + * do that when detection completed successfully. Otherwise + * there is noticeable delay between main transition and + * recovery attempt + */ + STOP_STEPPING_SIGNAL; + + /* if position detected based on sensor data, + * current position will be set to destination + * to follow the same logic flow + */ + atomic_set(&md->position, atomic_read(&md->destination)); + moto_drv8424_cmd_push(md, CMD_POSITION, 0); + + /* this will stop polling */ + atomic_set(&md->destination, 0); + + switch (atomic_read(&md->position)) { + case POS_COMPACT: + value = STATUS_STOPPED_COMPACT; + break; + case POS_EXPANDED: + value = STATUS_STOPPED_EXPANDED; + break; + case POS_PEEK: + value = STATUS_STOPPED_PEEK; + break; + default: value = STATUS_UNKNOWN; + /* this should never happen */ + dev_err(md->dev, "Undetermined position!!!"); + } + + atomic_set(&md->status, value); + moto_drv8424_cmd_push(md, CMD_STATUS, 0); + /* At this point it's safe to allow next position change */ + md->ready = true; + + PARANOIC("update: status %d, position %d\n", + atomic_read(&md->status), atomic_read(&md->position)); + + /* need to show statistics on completion */ + logtime_show(__func__); +show_stats: + set_irq_state(md, false); + atomic_set(&md->samples, 0); + } + + return 0; +} + +static int disable_motor(struct device* dev) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + STOP_STEPPING_SIGNAL; + return 0; +} + +static int motor_set_enable(struct device* dev, bool enable) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + + if (atomic_read(&md->stepping)) { + disable_motor(dev); + } + if (enable) { + /* reset sensor data before motor started moving */ + RESET_SENSOR_DATA(OUT_OF_RANGE); + moto_drv8424_drive_sequencer(md); + set_irq_state(md, true); + } + + return 0; +} + + +static void moto_drv8424_set_enable_with_power(motor_device *md, bool enable) +{ + if (md->power_default_off && enable) { + dev_info(md->dev, "vdd power on\n"); + moto_drv8424_set_regulator_power(md, true); + msleep(1); + } + if (!moto_drv8424_set_power(md, enable)) { + motor_set_enable(md->dev, enable); + } +} + +static void motor_set_motion_params(motor_device *md, int start, int end) +{ + int regime = SQ_FULL; + + if (start == POS_UNKNOWN || end == POS_UNKNOWN) { + dev_err(md->dev, "unknown src | dest!!!\n"); + } + + if (start == POS_EXPANDED) { + md->dir = DIR_WITHDRAW; + if (end == POS_COMPACT) + regime = SQ_FULL; + else // end == PEEK + regime = SQ_PROLONGED; + } else if (start == POS_COMPACT) { + if (end == POS_EXPANDED) { + md->dir = DIR_EXTEND; + regime = SQ_FULL; + } else { // end == PEEK + md->dir = DIR_WITHDRAW; + regime = SQ_SHORT; + } + } else { // start == PEEK + md->dir = DIR_EXTEND; + if (end == POS_EXPANDED) + regime = SQ_PROLONGED; + else // end == COMPACT + regime = SQ_SHORT; + } + + if (!md->sensors_off && md->detect_incomplete) { + regime = SQ_SLOWMO; + PARANOIC("engage SLOWMO in recovery\n"); + } + + moto_drv8424_set_regime(md, regime); +} + +static enum hrtimer_restart motor_stepping_timer_action(struct hrtimer *h) +{ + motor_device * md = container_of(h, motor_device, stepping_timer); + unsigned long flags; + bool showlog = false; + enum hrtimer_restart ret = HRTIMER_RESTART; + + spin_lock_irqsave(&md->mlock, flags); + logtime_stepping_store(); + if (!atomic_read(&md->stepping)) { + goto next_stage; + } + md->level = !md->level; + GPIO_OUTPUT_DIR(md->mc.ptable[MOTOR_STEP], md->level); + if (md->double_edge) { + atomic_inc(&md->step_count); + } else if (md->level) { + atomic_inc(&md->step_count); + } + + if (md->step_ceiling && atomic_read(&md->step_count) > md->step_ceiling) { + /* multi stage sequence */ + if (!moto_drv8424_next_stage(md)) { + char *msg = NULL; +next_stage: + /* wake up motor thread at the end of sequence */ + /* when sensors unavailable */ + if (md->sensors_off || md->detect_incomplete) { + msg = motor_stop(__func__, false, md, true); + showlog = true; + } + GPIO_OUTPUT_DIR(md->mc.ptable[MOTOR_STEP], 0); + spin_unlock_irqrestore(&md->mlock, flags); + RECORD_TIME(kTerm); + if (showlog) { + if (msg) + LOGD("%s", msg); + LOGD("sequence ended!!! last data: %d %d %d\n", + md->sensor_data[0], + md->sensor_data[1], + md->sensor_data[2]); + } + return HRTIMER_NORESTART; + } + } + hrtimer_forward_now(h, adapt_time_helper(md->half)); + spin_unlock_irqrestore(&md->mlock, flags); + + return ret; +} + +/* + * Timeout can happen either if destination position never reached or + * driver cannot detect current position. In either way timeout handler + * will try enforcing COMPACT state + */ +static enum hrtimer_restart motor_timeout_timer_action(struct hrtimer *h) +{ + motor_device * md = container_of(h, motor_device, timeout_timer); + int original = atomic_read(&md->position); + int destination = atomic_read(&md->destination); + enum hrtimer_restart ret = HRTIMER_NORESTART; + + if (md->sensors_off) { + dev_warn(md->dev, "time out in no sensors mode!\n"); + goto exit; + } + + if (POSITION_RANGE_CHK(original) && + POSITION_RANGE_CHK(destination)) { + LOGD("Timeout: position=%s, detection=%s\n", + position_labels[original], position_labels[destination]); + } else { + LOGD("Timeout: position=%d, detection=%d\n", original, destination); + } + + /* Timed out arriving to destination from known starting position. + * Solution would be to go back to starting position + */ + if (original != POS_UNKNOWN && + original != destination) { + /* repeat the same motion */ + SIMPLE_MOTOR_STOP; + LOGD("Timeout driving motor\n"); + } else { /* original & destination == POS_UNKNOWN */ + LOGD("Timeout position detection\n"); + } + moto_drv8424_cmd_push(md, CMD_TIMEOUT, msecs_to_jiffies(INT_20MS)); +exit: + return ret; +} + +#define PROXIMITY_HD 350 /* 3.5 degrees multiplied by 100 to get rid of float */ +#define PROXIMITY_SD 750 /* 7.5 degrees multiplied by 100 to get rid of float */ +static int sensProximity = PROXIMITY_HD; + +static inline bool IN_RANGE(int valMeas, int valSet) +{ + return (abs(valMeas) < sensProximity) ? true : false; +} + +static void motor_reset(motor_device *md) +{ + moto_drv8424_set_power(md, 1); + msleep(RESET_TIME); + moto_drv8424_set_power(md, 0); +} + +static int moto_drv8424_detect_position(motor_device *md) +{ + int i, start, end, ret = POS_UNKNOWN; + + if (atomic_read(&md->destination) > 0) { + /* check only destination position */ + start = atomic_read(&md->destination); + end = start + 1; + } else { + /* detection check all */ + start = POS_COMPACT; + end = POS_MAX; + } + + mutex_lock(&md->mx_lock); + logtime_store((void *)md->sensor_data, sizeof(md->sensor_data), 1); + for (i = start; i < end; i++) { + if (IN_RANGE(md->sensor_data[scanner[i].index], scanner[i].value)) { + RECORD_TIME(kDetect); + /* this is supposed to disable motor output immediately!!! */ + moto_drv8424_set_power(md, 0); + md->detect_incomplete = false; + STOP_STEPPING_SIGNAL; + PARANOIC("sensor within range: (%d) < %d < (%d)\n", + scanner[i].value - sensProximity, + md->sensor_data[scanner[i].index], + scanner[i].value + sensProximity); + if (sensProximity == PROXIMITY_SD) { + sensProximity = PROXIMITY_HD; + LOGD("set tighter proximity threshold\n"); + } + ret = i; + break; + } + } + mutex_unlock(&md->mx_lock); + + return ret; +} + +static void motor_fault_handler(motor_device *md) +{ + int fault_irq = atomic_read(&md->fault_irq); + + if (!fault_irq) { + dev_warn(md->dev, "Called w/o IRQ!!!\n"); + return; + } + + STOP_STEPPING_SIGNAL; + /* perform reset */ + motor_reset(md); + /* clear flags preventing the next run */ + md->faulting = false; + atomic_set(&md->fault_irq, 0); + md->detect_incomplete = false; + md->ready = true; + /* set transition parameters */ + motor_set_motion_params(md, atomic_read(&md->position), + atomic_read(&md->destination)); + moto_drv8424_set_enable_with_power(md, true); + + dev_warn(md->dev, "Device reset due to motor #%d fault\n", + irq_to_gpio(fault_irq) == md->mc.ptable[MOTOR_FAULT1_INT] ? 1 : 2); + PARANOIC("Pos/dest: %s/%s, status: %s\n", + position_labels[atomic_read(&md->position)], + position_labels[atomic_read(&md->destination)], + status_labels[atomic_read(&md->status)]); +} + +#define PEEK_ID 2 +#define COMPACT_ID 1 +#define EXPANDED_ID 0 + +#define PROX_THRES 4500 + +static void motor_cmd_work(struct work_struct *work) +{ + struct delayed_work *dw = container_of(work, struct delayed_work, work); + motor_device* md = container_of(dw, motor_device, motor_work); + int gotten, cpos, dest, cmd = 0; + unsigned long flags; + + for (;;) { + spin_lock_irqsave(&md->mlock, flags); + gotten = kfifo_get(&md->cmd_pipe, &cmd); + spin_unlock_irqrestore(&md->mlock, flags); + if (!gotten) + break; + switch (cmd) { + case CMD_FAULT: + motor_fault_handler(md); + break; + case CMD_STATUS: + sysfs_notify(&md->dev->kobj, NULL, "status"); + md->status_update_ready = true; + wake_up(&md->status_wait); + break; + case CMD_POSITION: + sysfs_notify(&md->dev->kobj, NULL, "position"); + md->position_update_ready = true; + wake_up(&md->position_wait); + break; + case CMD_TIMER_START: + hrtimer_start(&md->timeout_timer, + adapt_time_helper(md->time_out), HRTIMER_MODE_REL); + LOGD("timer armed\n"); + break; + case CMD_TIMER_STOP: + md->time_out = 0; + if (hrtimer_active(&md->timeout_timer)) { + hrtimer_try_to_cancel(&md->timeout_timer); + LOGD("timer stopped\n"); + } + break; + case CMD_RECOVERY: + /* pos & dest depends on recovery cause */ + dest = atomic_read(&md->destination); + md->power_en = 0; + motor_set_motion_params(md, + atomic_read(&md->position), dest); + if (dest == POS_COMPACT) { + /* Recovery invoked when motor failed to arrive to + * destination. If the destination is COMPACT, there + * is a chance motor will "overshoot" and go beyond + * COMPACT position w/o properly detecting it. + * In this case slider will go all the way up/down + * to its physical limit. So we need to allow + * detecting those. + * Thus setting destination to UNKNOWN to allow + * moto_drv8424_detect_position() scanning for all + * detectable positions + */ + dest = POS_UNKNOWN; + atomic_set(&md->destination, dest); + } + moto_drv8424_set_enable_with_power(md, true); + if (dest == POS_UNKNOWN) + LOGD("Enabled limits in recovery!!!\n"); + break; + case CMD_TIMEOUT: + /* We want to be able detecting ANY valid position, so */ + /* md->position and md->destination must stay unknown */ + /* Motion parameters will be set based on discovered */ + /* position or set to transition from EXPANEDE to */ + /* COMPACT if current position cannot be determined */ + md->power_en = 0; + cpos = POS_EXPANDED; + dest = POS_COMPACT; + if (md->sensor_data[PEEK_ID] < PROX_THRES || + (md->sensor_data[COMPACT_ID] < 0 && + md->sensor_data[COMPACT_ID] > -PROX_THRES)) { + /* near PEEK or between PEEK&COMPACT */ + /* move toward EXPANDED position */ + cpos = POS_PEEK; + } + motor_set_motion_params(md, cpos, dest); + moto_drv8424_set_enable_with_power(md, true); + break; + default: + dev_err(md->dev, "Unsupported command %d\n", cmd); + continue; + } + PARANOIC("processed %s\n", cmd_labels[cmd]); + } +} + +static irqreturn_t motor_fault_irq(int irq, void *pdata) +{ + motor_device * md = (motor_device*) pdata; + + atomic_set(&md->fault_irq, irq); + /* no need to wake up twice if device is faulting */ + if (!md->faulting) { + md->faulting = true; + md->user_sync_complete = true; + wake_up(&md->sync_complete); + } + + return IRQ_HANDLED; +} + +//module node interface +static void moto_drv8424_set_torque(motor_device* md, unsigned torque) +{ + unsigned long flags; + bool showlog = false; + + spin_lock_irqsave(&md->mlock, flags); + if(md->torque == torque) { + showlog = true; + goto exit; + } + md->torque = torque; +exit: + spin_unlock_irqrestore(&md->mlock, flags); + if (showlog) + dev_info(md->dev, "Unchanged the torque, ignore\n"); +} + +static int moto_drv8424_set_mode(motor_device* md, unsigned mode) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&md->mlock, flags); + if (md->mode == mode) { + ret = -EINVAL; + goto exit; + } + md->mode = mode; + spin_unlock_irqrestore(&md->mlock, flags); + return 0; +exit: + spin_unlock_irqrestore(&md->mlock, flags); + if (ret < 0) + dev_info(md->dev, "Unchanged the mode, ignore\n"); + return ret; +} + +static int moto_drv8424_set_dir(motor_device* md, unsigned dir) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&md->mlock, flags); + if (md->dir == dir) { + ret = -EINVAL; + goto exit; + } + md->dir = dir; + spin_unlock_irqrestore(&md->mlock, flags); + return 0; +exit: + spin_unlock_irqrestore(&md->mlock, flags); + if (ret < 0) + dev_dbg(md->dev, "Unchanged the dir, ignore\n"); + return ret; +} + +//Spec step max frequency 250KHz +#define STEP_MAX_FREQ 250000 +static void moto_drv8424_set_step_freq(motor_device* md, unsigned freq) +{ + if (md->step_freq == freq) { + dev_dbg(md->dev, "Unchanged the freq, ignore\n"); + return; + } else if(freq == 0) { + dev_err(md->dev, "Invalid frequency, ignore\n"); + return; + } + if(freq > STEP_MAX_FREQ) + freq = STEP_MAX_FREQ; + md->step_freq = freq; + md->step_period = 1000000 / md->step_freq; +} + +static int moto_drv8424_set_ceiling(motor_device* md, unsigned ceiling) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&md->mlock, flags); + if (md->step_ceiling == ceiling) { + ret = -EINVAL; + goto exit; + } + md->step_ceiling = ceiling; + spin_unlock_irqrestore(&md->mlock, flags); + return 0; +exit: + spin_unlock_irqrestore(&md->mlock, flags); + if (ret < 0) + dev_dbg(md->dev, "Unchanged the ceiling, ignore\n"); + return ret; +} + +static ssize_t motor_flags_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + ssize_t blen = 0; + + blen += snprintf(buf+blen, PAGE_SIZE-blen, "===> Motor params:\n"); + blen += snprintf(buf+blen, PAGE_SIZE-blen, "Position: %s\n", position_labels[atomic_read(&md->position)]); + blen += snprintf(buf+blen, PAGE_SIZE-blen, "Destination: %s\n", position_labels[atomic_read(&md->destination)]); + blen += snprintf(buf+blen, PAGE_SIZE-blen, "Status: %s\n", status_labels[atomic_read(&md->status)]); + blen += snprintf(buf+blen, PAGE_SIZE-blen, "Stepping/count/ceiling: %d/%d/%lu\n", + atomic_read(&md->stepping), atomic_read(&md->step_count), md->step_ceiling); + blen += snprintf(buf+blen, PAGE_SIZE-blen, "Stage/max: %d/%d\n", md->stage, md->max_stages); + blen += snprintf(buf+blen, PAGE_SIZE-blen, "Faulting/ready: %d/%d\n", md->faulting, md->ready); + blen += snprintf(buf+blen, PAGE_SIZE-blen, "Timeout: %u\n", md->time_out); + blen += snprintf(buf+blen, PAGE_SIZE-blen, "Pitch: %s\n", md->use_2p8_pitch ? "2.8" : "2.3"); + blen += snprintf(buf+blen, PAGE_SIZE-blen, "===> Sensor params:\n"); + blen += snprintf(buf+blen, PAGE_SIZE-blen, "Sensors: %s\n", md->sensors_off ? "Off" : "On"); + blen += snprintf(buf+blen, PAGE_SIZE-blen, "Samples: %d\n", atomic_read(&md->samples)); + + return blen; +} + +static ssize_t motor_enable_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + return snprintf(buf, 20, "%d\n", md->power_en); +} + +static ssize_t motor_enable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + unsigned enable = 0; + + if (md->faulting || md->ready == false) { + dev_err(dev, "%s: Device not ready or faulting\n", __func__); + return -EBUSY; + } + if (kstrtouint(buf, 10, &enable)) { + dev_err(dev, "Error value: %s\n", buf); + goto exit; + } + if (!!enable) { + if (atomic_read(&md->position) == POS_UNKNOWN) { + dev_warn(md->dev, "Cannot move from unknown position!!!\n"); + goto exit; + } + } + moto_drv8424_set_enable_with_power(md, !!enable); +exit: + return len; +} + +static ssize_t motor_dir_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + return snprintf(buf, 20, "%d\n", md->dir); +} + +static ssize_t motor_dir_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + unsigned value = 0; + + if (md->faulting || md->ready == false) { + dev_err(dev, "%s: Device not ready or faulting\n", __func__); + return -EBUSY; + } + if (kstrtouint(buf, 10, &value)) { + dev_err(dev, "Error value: %s\n", buf); + goto exit; + } + moto_drv8424_set_dir(md, value); +exit: + return len; +} + +static ssize_t motor_step_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + return snprintf(buf, 20, "%d\n", md->step_freq); +} + +static ssize_t motor_step_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + unsigned long flags; + unsigned value = 0; + + if (md->faulting || md->ready == false) { + dev_err(dev, "%s: Device not ready or faulting\n", __func__); + return -EBUSY; + } + if (kstrtouint(buf, 10, &value)) { + dev_err(dev, "Error value: %s\n", buf); + return -EINVAL; + } + spin_lock_irqsave(&md->mlock, flags); + moto_drv8424_set_step_freq(md, value); + spin_unlock_irqrestore(&md->mlock, flags); + dev_info(md->dev, "freq %uHz period %ldus\n", md->step_freq, md->step_period); + + return len; +} + +static ssize_t motor_prox_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, 20, "%d\n", sensProximity); +} + +static ssize_t motor_prox_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + unsigned value = 0; + + if (kstrtouint(buf, 10, &value)) { + dev_err(dev, "Error value: %s\n", buf); + return -EINVAL; + } + sensProximity = value; + dev_info(md->dev, "proximity value %d\n", sensProximity); + return len; +} + +static ssize_t motor_pitch_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + return snprintf(buf, 20, "%s\n", md->use_2p8_pitch ? "2.8" : "2.3"); +} + +static ssize_t motor_pitch_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + bool use_2p8_pitch = false; + + if (!strncmp(buf, "2.8", 3)) { + use_2p8_pitch = true; + } else if (!strncmp(buf, "2.8", 3)) { + use_2p8_pitch = false; + } else { + dev_err(dev, "Error value: %s\n", buf); + return -EINVAL; + } + if (use_2p8_pitch != md->use_2p8_pitch) { + md->use_2p8_pitch = use_2p8_pitch; + /* update sequencer */ + motor_set_sequencer(md); + dev_info(md->dev, "pitch value %s\n", md->use_2p8_pitch ? "2.8" : "2.3"); + } + + return len; +} + +static ssize_t motor_sensing_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + dev_info(md->dev, "Motor faulting state support\n"); + return snprintf(buf, 20, "%d\n", md->sensors_off ? 0 : 1); +} + +static ssize_t motor_sensing_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + unsigned value = 0; + + if (kstrtouint(buf, 10, &value)) { + dev_err(dev, "Error value: %s\n", buf); + return -EINVAL; + } + md->sensors_off = !!value ? false : true; + dev_info(md->dev, "sensing %d\n", md->sensors_off ? 0 : 1); + /* backdoor to recover from bad sensors calibration */ + if (md->sensors_off && + (md->faulting || md->ready == false)) { + md->faulting = false; + md->ready = true; + dev_info(md->dev, "Motor faulting state dropped!!!\n"); + } + + return len; +} + +static ssize_t motor_mode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + return snprintf(buf, 20, "%d\n", md->mode); +} + +static ssize_t motor_mode_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + unsigned value = 0; + + if (md->faulting) { + dev_err(dev, "Device faulting\n"); + return -EBUSY; + } + if (kstrtouint(buf, 10, &value)) { + dev_err(dev, "Error value: %s\n", buf); + goto exit; + } + moto_drv8424_set_mode(md, value); +exit: + return len; +} + +static ssize_t motor_torque_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + return snprintf(buf, 20, "%d\n", md->torque); +} + +static ssize_t motor_torque_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + unsigned value = 0; + + if (md->faulting) { + dev_err(dev, "Device faulting\n"); + return -EBUSY; + } + if (kstrtouint(buf, 10, &value)) { + dev_err(dev, "Error value: %s\n", buf); + goto exit; + } + moto_drv8424_set_torque(md, value); +exit: + return len; +} + +static ssize_t motor_ceiling_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + return snprintf(buf, 20, "%ld\n", md->step_ceiling); +} + +static ssize_t motor_ceiling_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + unsigned value = 0; + + if (md->faulting) { + dev_err(dev, "Device faulting\n"); + return -EBUSY; + } + if (kstrtouint(buf, 10, &value)) { + dev_err(dev, "Error value: %s\n", buf); + goto exit; + } + moto_drv8424_set_ceiling(md, value); +exit: + return len; +} + +static unsigned motor_get_max_pps(motor_stage *stage) +{ + unsigned mpps = 0; + int i; + + for (i = 0; i < MAX_STAGE_LEGS; i++, stage++) { + if (!(stage->freq | stage->ceiling)) + break; + if (stage->freq > mpps) + mpps = stage->freq; + } + + return mpps; +} + +static int motor_set_max_pps(motor_stage *stage, unsigned value) +{ + int i; + + for (i = 0; i < MAX_STAGE_LEGS; i++, stage++) { + if (!(stage->freq | stage->ceiling)) + break; + if (stage->freq > (stage + 1)->freq) { + stage->freq = value; + break; + } + } + + return 0; +} + +static ssize_t motor_maxpps_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + return snprintf(buf, 20, "%u\n", motor_get_max_pps(md->sequencer[SQ_FULL])); +} + +static ssize_t motor_maxpps_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + unsigned value = 0; + + if (kstrtouint(buf, 10, &value)) { + dev_err(dev, "Error value: %s\n", buf); + goto exit; + } + motor_set_max_pps(md->sequencer[SQ_FULL], value); + motor_set_max_pps(md->sequencer[SQ_PROLONGED], value); + dev_info(dev, "Set max PPS: %u\n", value); +exit: + return len; +} + +static ssize_t motor_time_out_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + return snprintf(buf, 20, "%d\n", md->time_out); +} + +static ssize_t motor_time_out_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + unsigned value = 0; + unsigned long flags; + + if (md->faulting) { + dev_err(dev, "Device faulting\n"); + return -EBUSY; + } + if (kstrtouint(buf, 10, &value)) { + dev_err(dev, "Error value: %s\n", buf); + goto exit; + } + spin_lock_irqsave(&md->mlock, flags); + if (value > MOTOR_DEFAULT_EXPIRE) { + value = MOTOR_DEFAULT_EXPIRE; + } + md->time_out = value; + spin_unlock_irqrestore(&md->mlock, flags); +exit: + return len; +} + +static ssize_t motor_reset_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + unsigned value = 0; + + if (kstrtouint(buf, 10, &value)) { + dev_err(dev, "Error value: %s\n", buf); + } else { + if (atomic_read(&md->stepping)) + disable_motor(md->dev); + + mutex_lock(&md->mx_lock); + motor_reset(md); + mutex_unlock(&md->mx_lock); + dev_info(dev, "Device reset\n"); + } + + return len; +} + +/* store new motor sequencer */ +/* Format: index freq0:ceiling0 [... freqN:ceilingN] */ +/* Example: 0 400:24 1400:56 3000:6252 2000:12 */ +static ssize_t motor_sequencer_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + char *buffer, *next, *pair; + unsigned nstages = 0; + unsigned msn, value, idx, freq, ceiling; + + buffer = kmalloc(len + 1, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + strlcpy(buffer, buf, len); + buffer[len] = '\0'; + + next = strnchr(buffer, len, ' '); + if (!next) { + dev_err(dev, "Invalid input data format: %s\n", buf); + len = -EINVAL; + goto exit; + } + *next++ = '\0'; + if (kstrtouint(buffer, 10, &value) || + (value < 0 || value >= SQ_SHOW_MAX)) { + dev_err(dev, "Invalid index: %s\n", buffer); + goto exit; + } + idx = value; + for (msn = 0, pair = next; msn < MAX_STAGE_LEGS; msn++) { + LOGD("old values [%u][%u]: freq=%u, ceiling=%u\n", + idx, msn, md->sequencer[idx][msn].freq, + md->sequencer[idx][msn].ceiling); + if (!pair) { + LOGD("zero-ing: [%u][%u]\n", idx, msn); + md->sequencer[idx][msn].freq = + md->sequencer[idx][msn].ceiling = 0; + continue; + } + LOGD("pair string: [%s]\n", pair); + next = strnchr(pair, len, ' '); + if (next) { + *next++ = '\0'; + len -= strlen(pair); + } + LOGD("remaining string: [%s]\n", next); + if (sscanf(pair, "%u:%u", &freq, &ceiling) == 2) { + nstages++; + md->sequencer[idx][msn].freq = freq; + md->sequencer[idx][msn].ceiling = ceiling; + dev_info(md->dev, "[%u][%u]: freq=%u, ceiling=%u\n", + idx, msn, freq, ceiling); + } + pair = next; + } + + if (nstages > 0) { + md->max_stages = nstages; + LOGD("# of stages: %u\n", md->max_stages); + } else { + dev_err(dev, "Error value: %s\n", buf); + len = -EINVAL; + } +exit: + kfree(buffer); + + return len; +} + +static ssize_t motor_sequencer_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + ssize_t blen = 0; + unsigned idx, msn; + + for (idx = 0; idx < SQ_SHOW_MAX; idx++) { + blen += snprintf(buf + blen, PAGE_SIZE - blen, "regime %s: ", + regime_names[idx]); + for (msn = 0; msn < MAX_STAGE_LEGS; msn++) { + if (md->sequencer[idx][msn].freq == 0 && + md->sequencer[idx][msn].ceiling == 0) + continue; + blen += snprintf(buf + blen, PAGE_SIZE - blen, "%u:%u ", + md->sequencer[idx][msn].freq, + md->sequencer[idx][msn].ceiling); + } + blen += snprintf(buf + blen, PAGE_SIZE - blen, "\n"); + } + + return blen; +} + +static ssize_t motor_regime_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + unsigned value; + + if (md->faulting || md->ready == false) { + dev_err(dev, "%s: Device not ready or faulting\n", __func__); + return -EBUSY; + } + if (kstrtouint(buf, 10, &value) || + (value < SQ_FULL || value >= SQ_SHOW_MAX)) { + dev_err(dev, "Error value: %s\n", buf); + return -EINVAL; + } + moto_drv8424_set_regime(md, value); + + return len; +} + +static ssize_t motor_regime_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + return snprintf(buf, 20, "%s\n", regime_names[md->regime]); +} + +static ssize_t motor_irq_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + unsigned value; + + if (kstrtouint(buf, 10, &value)) { + dev_err(dev, "Error value: %s\n", buf); + return -EINVAL; + } + if (!!value) { + md->do_not_arm_irq = false; + dev_info(dev, "Enabled fault IRQ\n"); + } else { + md->do_not_arm_irq = true; + dev_info(dev, "Disabled fault IRQ\n"); + } + + return len; +} + +static ssize_t motor_irq_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + return sprintf(buf, "%d\n", md->irq_enabled ? 1 : 0); +} + +static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR|S_IWGRP, motor_enable_show, motor_enable_store); +static DEVICE_ATTR(dir, S_IRUGO|S_IWUSR|S_IWGRP, motor_dir_show, motor_dir_store); +static DEVICE_ATTR(step, S_IRUGO|S_IWUSR|S_IWGRP, motor_step_show, motor_step_store); +static DEVICE_ATTR(ceiling, S_IRUGO|S_IWUSR|S_IWGRP, motor_ceiling_show, motor_ceiling_store); +static DEVICE_ATTR(maxpps, S_IRUGO|S_IWUSR|S_IWGRP, motor_maxpps_show, motor_maxpps_store); +static DEVICE_ATTR(mode, S_IRUGO|S_IWUSR|S_IWGRP, motor_mode_show, motor_mode_store); +static DEVICE_ATTR(sequencer, S_IRUGO|S_IWUSR|S_IWGRP, motor_sequencer_show, motor_sequencer_store); +static DEVICE_ATTR(torque, S_IRUGO|S_IWUSR|S_IWGRP, motor_torque_show, motor_torque_store); +static DEVICE_ATTR(time_out, S_IRUGO|S_IWUSR|S_IWGRP, motor_time_out_show, motor_time_out_store); +static DEVICE_ATTR(prox, S_IRUGO|S_IWUSR|S_IWGRP, motor_prox_show, motor_prox_store); +static DEVICE_ATTR(pitch, S_IRUGO|S_IWUSR|S_IWGRP, motor_pitch_show, motor_pitch_store); +static DEVICE_ATTR(sensing, S_IRUGO|S_IWUSR|S_IWGRP, motor_sensing_show, motor_sensing_store); +static DEVICE_ATTR(reset, S_IWUSR|S_IWGRP, NULL, motor_reset_store); +static DEVICE_ATTR(flags, S_IRUGO, motor_flags_show, NULL); +static DEVICE_ATTR(regime, S_IRUGO|S_IWUSR|S_IWGRP, motor_regime_show, motor_regime_store); +static DEVICE_ATTR(irq, S_IRUGO|S_IWUSR|S_IWGRP, motor_irq_show, motor_irq_store); + +#define ATTRS_STATIC 8 +#define ATTRS_MAX 14 + +static int last_idx = ATTRS_STATIC; + +static struct attribute *motor_attributes[ATTRS_MAX + 1] = { + &dev_attr_dir.attr, + &dev_attr_enable.attr, + &dev_attr_step.attr, + &dev_attr_ceiling.attr, + &dev_attr_maxpps.attr, + &dev_attr_reset.attr, + &dev_attr_prox.attr, + &dev_attr_pitch.attr, + &dev_attr_sensing.attr, + &dev_attr_flags.attr, + &dev_attr_time_out.attr, + &dev_attr_regime.attr, + &dev_attr_sequencer.attr, + &dev_attr_irq.attr, + NULL +}; + +#define ATTR_ADD(name) { \ + if (last_idx < ATTRS_MAX) { \ + dev_info(md->dev, "[%d] adding attribute '%s'\n", last_idx, #name); \ + motor_attributes[last_idx++] = &dev_attr_##name.attr; \ + } else { \ + dev_err(md->dev, "cannot add attribute '%s'\n", #name); \ + } \ +} + +static struct attribute_group motor_attribute_group = { + .attrs = motor_attributes +}; + +static const struct attribute_group * motor_attr_groups[] = { + &motor_attribute_group, + NULL +}; + +/* method to report/enforce position detection */ +static ssize_t motor_detect_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + POSITION_DETECT_INIT(POS_COMPACT, STATUS_STOPPED_COMPACT); + return len; +} + +static ssize_t motor_detect_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + return snprintf(buf, 20, "%s\n", position_labels[atomic_read(&md->position)]); +} + +/* HAL communicates desired position through this sysfs */ +static ssize_t motor_position_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + unsigned long flags; + unsigned value = 0; + + if (kstrtouint(buf, 10, &value) || + /* at this point it's well known position */ + (value <= POS_UNKNOWN || value > POS_PEEK)) { + dev_err(dev, "Error value: %s\n", buf); + return -EINVAL; + } + spin_lock_irqsave(&md->mlock, flags); + atomic_set(&md->destination, value); + spin_unlock_irqrestore(&md->mlock, flags); + dev_info(md->dev, "expected position %s\n", position_labels[value]); + + return len; +} + +static ssize_t motor_position_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + int ret; + + LOGD("enter\n"); + ret = wait_event_interruptible(md->position_wait, md->position_update_ready); + if (ret) { + dev_err(md->dev, "wait failed rc %d", ret); + return -EFAULT; + } + md->position_update_ready = false; + LOGD("Position update: %s\n", position_labels[atomic_read(&md->position)]); + + return snprintf(buf, 20, "%d", atomic_read(&md->position)); +} + +static ssize_t motor_status_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + int ret; + + LOGD("enter\n"); + ret = wait_event_interruptible(md->status_wait, md->status_update_ready); + if (ret) { + dev_err(md->dev, "wait failed rc %d", ret); + return -EFAULT; + } + md->status_update_ready = false; + LOGD("Status update: %s\n", status_labels[atomic_read(&md->status)]); + + return snprintf(buf, 20, "%d", atomic_read(&md->status)); +} +/* + * Input data expected in the following format: + * Decimal_1 Decimal_2 [Decimal_3] + * where Decimal_3 is optional + */ +static ssize_t motor_sensor_data_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +{ + motor_device* md = (motor_device*)dev_get_drvdata(dev); + motor_control* mc = &md->mc; + int argnum = 0; + int position = POS_UNKNOWN; + int volatile args[3] = {OUT_OF_RANGE}; + static bool inited; + + argnum = sscanf(buf, "%d %d %d", &args[0], &args[1], &args[2]); + PARANOIC("sensor_data (%d): %d %d %d\n", argnum, args[0], args[1], args[2]); + if (argnum > 0 && argnum >= ARGS_NUM_MIN) { + mutex_lock(&md->mx_lock); + if (!inited) { + /* First sample arrives when sensors HAL gets loaded + * This will be a good point to synchronize intial + * position detection with sensors data availability + */ + if (md->sensor_data[0] == UNINITIALIZED) { + inited = true; + if (!md->sensors_off) { + md->time_out = MOTOR_DETECT_EXPIRE; + moto_drv8424_cmd_push(md, CMD_TIMER_START, 0); + } + } + } + /* store necessary sensor info */ + memcpy((void *)md->sensor_data, (void *)args, sizeof(md->sensor_data)); + atomic_inc(&md->samples); + mutex_unlock(&md->mx_lock); + logtime_store((void *)args, sizeof(args), 0); + position = moto_drv8424_detect_position(md); + if (position != POS_UNKNOWN && md->ready == false) { + atomic_set(&md->destination, position); + motor_stop(__func__, true, md, true); + GPIO_OUTPUT_DIR(mc->ptable[MOTOR_ACTIVE], 0); + RECORD_TIME(kSensorDown); + moto_drv8424_cmd_push(md, CMD_TIMER_STOP, msecs_to_jiffies(INT_20MS)); + if (md->faulting) { + md->faulting = false; + dev_warn(md->dev, "Motor faulting state dropped!!!"); + } + dev_info(md->dev, "detected position: %s\n", + position_labels[position]); + LOGD("samples %d; last data: %d %d %d\n", + atomic_read(&md->samples), + md->sensor_data[0], + md->sensor_data[1], + md->sensor_data[2]); + } + if (md->ready) { + LOGD("extra data: %d %d %d\n", args[0], args[1], args[2]); + } + } + + return len; +} + +/* sysfs polled from HAL */ +static DEVICE_ATTR(position, S_IRUGO|S_IWUSR|S_IWGRP, motor_position_show, motor_position_store); +static DEVICE_ATTR(detect, S_IRUGO|S_IWUSR|S_IWGRP, motor_detect_show, motor_detect_store); +static DEVICE_ATTR(status, S_IRUGO, motor_status_show, NULL); +static DEVICE_ATTR(sensor_data, S_IWUSR|S_IWGRP, NULL, motor_sensor_data_store); + +static struct attribute *status_attributes[] = { + &dev_attr_status.attr, + &dev_attr_position.attr, + &dev_attr_detect.attr, + &dev_attr_sensor_data.attr, + NULL +}; + +static struct attribute_group status_attribute_group = { + .attrs = status_attributes +}; + +static int moto_drv8424_init_from_dt(motor_device* md) +{ + struct device* pdev = md->dev; + struct device_node *np = pdev->of_node; + motor_control* mc = &md->mc; + int i, rc = 0; + char gpio_name[32]; + const char *clock_name; + + mc->tab_cells = MOTOR_MAX_GPIO; + for (i = 0; i < mc->tab_cells; i++) { + snprintf(gpio_name, sizeof(gpio_name)-1, "%s-gpio", gpios_labels[i]); + md->mc.ptable[i] = of_get_named_gpio(np, gpio_name, 0); + if (gpio_is_valid(md->mc.ptable[i])) { + dev_info(pdev, "gpio %s = %d\n", gpios_labels[i], md->mc.ptable[i]); + } + } + if (!of_property_read_string(np, "clock-names", &clock_name)) + strlcpy(md->clock_name, clock_name, CLOCK_NAME_LEN); + else + strlcpy(md->clock_name, MOTOR_HW_CLK_NAME, CLOCK_NAME_LEN); + dev_dbg(pdev, "Clock name: %s\n", md->clock_name); + md->hw_clock = of_property_read_bool(np, "enable-hw-clock"); + dev_dbg(pdev, "Enable hw clock %d\n", md->hw_clock); + md->support_mode = of_property_read_bool(np, "support-mode"); + if (md->support_mode) { + ATTR_ADD(mode); + dev_info(pdev, "Support mode %d\n", md->support_mode); + } + md->support_torque = of_property_read_bool(np, "support-torque"); + if (md->support_torque) { + ATTR_ADD(torque); + dev_info(pdev, "Support torque %d\n", md->support_torque); + }; + md->sensors_off = of_property_read_bool(np, "no-sensors"); + if (md->sensors_off) + dev_info(pdev, "Ignore sensors data\n"); + md->power_default_off = of_property_read_bool(np, "power-default-off"); + dev_info(pdev, "power is default off: %d\n", md->power_default_off); + md->use_2p8_pitch = of_property_read_bool(np, "use-2p8-pitch"); + if (md->use_2p8_pitch) + dev_info(pdev, "use 2.8 pitch\n"); + + return rc; +} + +static int moto_drv8424_irq_setup(motor_device *md, unsigned gidx) +{ + static int id; + int ret, irq_gpio = md->mc.ptable[gidx]; + static char *irq_name[2]; + + if (!gpio_is_valid(irq_gpio)) + return ENOTCONN; + irq_name[id] = devm_kzalloc(md->dev, 32, GFP_KERNEL); + if (!irq_name[id]) + return -ENOMEM; + snprintf(irq_name[id], 31, "fault_motor%d", id+1); + ret = devm_request_threaded_irq(md->dev, + gpio_to_irq(irq_gpio), motor_fault_irq, NULL, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, irq_name[id], md); + if (ret) { + dev_err(md->dev, "Failed to register \'%s\': %d\n", + irq_name[id], ret); + } else { + dev_info(md->dev, "Registered IRQ %d as '%s'\n", + gpio_to_irq(irq_gpio), irq_name[id]); + /* disable immediately */ + disable_irq(gpio_to_irq(irq_gpio)); + } + id++; + + return ret; +} + +static void motor_set_sequencer(motor_device* md) +{ + motor_stage *stages; + int s, i; + + for (s = SQ_FULL; s < SQ_MAX; s++) { + PARANOIC("regime %s\n", regime_names[s]); + /* skip dummy */ + if (s == SQ_SHOW_MAX) + continue; + if (md->use_2p8_pitch) + stages = initial_data_2p8[s]; + else + stages = initial_data_2p3[s]; + for (i = 0; i < MAX_STAGE_LEGS; i++) { + if (!((stages+i)->freq | (stages+i)->ceiling)) + break; + md->sequencer[s][i].freq = (stages+i)->freq; + md->sequencer[s][i].ceiling = (stages+i)->ceiling; + } + PARANOIC("has %d pairs\n", i); +#if 0 + /* skip adding slow down stage if running in no */ + /* sensors mode and for TINY & SLOWMO regimes */ + if (md->sensors_off || s == SQ_TINY || s == SQ_SLOWMO) + continue; + if (++i < MAX_STAGE_LEGS) { + md->sequencer[s][i].freq = 800; + md->sequencer[s][i].ceiling = 60; + LOGD("added slow down stage to %s\n", regime_names[s]); + } +#endif + } +} + +static int moto_drv8424_probe(struct platform_device *pdev) +{ + struct device* dev = &pdev->dev; + motor_device* md; + int i; + int ret = 0; + + md = kzalloc(sizeof(motor_device), GFP_KERNEL); + if (!md) { + dev_err(dev, "probe: Out of memory\n"); + return -ENOMEM; + } + md->dev = dev; + md->mc.plabels = gpios_labels; + spin_lock_init(&md->mlock); + mutex_init(&md->mx_lock); + platform_set_drvdata(pdev, md); + moto_drv8424_set_step_freq(md, DEFAULT_STEP_FREQ); + /* assign default values for optional parameters */ + md->mode = FULL_STEP; + md->torque = TORQUE_FULL; + moto_drv8424_init_from_dt(md); + /* populate sequencer with initial values */ + motor_set_sequencer(md); + + md->mc.vdd = devm_regulator_get(md->dev, "vdd"); + if (IS_ERR(md->mc.vdd)) { + ret = PTR_ERR(md->mc.vdd); + dev_err(md->dev, "Failed to get VDD ret=%d\n", ret); + goto failed_mem; + } + if (!md->power_default_off) { + ret = moto_drv8424_set_regulator_power(md, true); + if (ret) { + regulator_put(md->mc.vdd); + dev_err(dev, "Failed enable regulator\n"); + goto failed_mem; + } + } + md->motor_wq = alloc_workqueue("motor_wq", WQ_HIGHPRI, 0); + if(!md->motor_wq) { + dev_err(dev, "Out of memory for work queue\n"); + goto failed_mem; + } + INIT_DELAYED_WORK(&md->motor_work, motor_cmd_work); + ret = kfifo_alloc(&md->cmd_pipe, sizeof(unsigned int)* 10, GFP_KERNEL); + if (ret) { + dev_err(dev, "Failed create fifo\n"); + goto failed_work; + } + md->status_update_ready = false; + init_waitqueue_head(&md->status_wait); + md->position_update_ready = false; + init_waitqueue_head(&md->position_wait); + md->user_sync_complete = false; + init_waitqueue_head(&md->sync_complete); + md->motor_task = kthread_run(motor_kthread, md, "motor_task"); + if (IS_ERR(md->motor_task)) { + ret = PTR_ERR(md->motor_task); + dev_err(dev, "Failed create motor kthread\n"); + goto failed_kfifo; + } + hrtimer_init(&md->stepping_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + md->stepping_timer.function = motor_stepping_timer_action; + hrtimer_init(&md->timeout_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + md->timeout_timer.function = motor_timeout_timer_action; + md->drv8424_class = class_create(THIS_MODULE, MOTOR_CLASS_NAME); + if(IS_ERR(md->drv8424_class)) { + dev_err(dev, "Failed to create class\n"); + goto failed_sys; + } + md->sysfs_dev = device_create_with_groups(md->drv8424_class, + dev, MKDEV(0, 0), md, motor_attr_groups, + "%s", MOTOR_CONTROL); + if (IS_ERR(md->sysfs_dev)) { + dev_err(dev, "Failed to create device\n"); + goto failed_sys_dev; + } + if (sysfs_create_group(&md->dev->kobj, &status_attribute_group)) { + dev_err(dev, "Failed to create status attribute"); + goto failed_device; + } + if (md->hw_clock) { + if (init_motor_clk(md) < 0) { + //Should get this gpio from DTB + dev_err(dev, "HW clock is not setup\n"); + } else { + md->cur_clk = hw_clocks[0]; + dev_info(dev, "HW clock is setup\n"); + } + } + /* set initial pinctrl */ + set_pinctrl_state(md, INIT_STATE); + //Init software clock pin. + set_pinctrl_state(md, CLK_SLEEP); + for (i = 0; i < MOTOR_UNKNOWN; i++) { + if (!gpio_is_valid(md->mc.ptable[i])) + continue; + ret = devm_gpio_request(dev, md->mc.ptable[i], gpios_labels[i]); + if(ret < 0) { + pr_err("Failed to request %s, errno %d\n", gpios_labels[i--], ret); + goto failed_gpio; + } + } + if (!set_pinctrl_state(md, INT_DEFAULT)) { + ret = moto_drv8424_irq_setup(md, MOTOR_FAULT1_INT); + if (ret < 0) + goto failed_gpio; + ret = moto_drv8424_irq_setup(md, MOTOR_FAULT2_INT); + if (ret < 0) + goto failed_gpio; + } else { + /* motor can work, but have no fault irq */ + dev_info(dev, "Device has no fault irq\n"); + } + /* get ready for initial position detection */ + RESET_SENSOR_DATA(UNINITIALIZED); + POSITION_DETECT_INIT(POS_COMPACT, STATUS_STOPPED_COMPACT); + dev_info(dev, "Success init device\n"); + if (!md->sensors_off) + dev_info(dev, "running position detection...\n"); + + logtime_alloc(sizeof(int)*3); + + return 0; + +failed_gpio: + while (i >= MOTOR_POWER_EN) { + if (gpio_is_valid(md->mc.ptable[i])) + devm_gpio_free(dev, md->mc.ptable[i]); + i--; + } + sysfs_remove_group(&md->dev->kobj, &status_attribute_group); +failed_device: + device_destroy(md->drv8424_class, MKDEV(0, 0)); +failed_sys_dev: + class_destroy(md->drv8424_class); +failed_sys: + kthread_stop(md->motor_task); +failed_work: + destroy_workqueue(md->motor_wq); +failed_kfifo: + kfifo_free(&md->cmd_pipe); +failed_mem: + kfree(md); + + return ret; +} + +static int moto_drv8424_remove(struct platform_device *pdev) +{ + motor_device* md = (motor_device*)platform_get_drvdata(pdev); + + set_irq_state(md, false); + moto_drv8424_set_power(md, 0); + moto_drv8424_set_regulator_power(md, false); + devm_regulator_put(md->mc.vdd); + device_destroy(md->drv8424_class, MKDEV(0, 0)); + class_destroy(md->drv8424_class); + sysfs_remove_group(&md->dev->kobj, &status_attribute_group); + disable_motor(md->dev); + kthread_stop(md->motor_task); + destroy_workqueue(md->motor_wq); + kfifo_free(&md->cmd_pipe); + kfree(md); + + return 0; +} + +void moto_drv8424_platform_shutdown(struct platform_device *pdev) +{ + motor_device* md = (motor_device*)platform_get_drvdata(pdev); + int curpos = atomic_read(&md->position); + + LOGD("shutdown handler\n"); + if (curpos != POS_COMPACT && curpos != POS_UNKNOWN) { + if (!md->sensors_off) { + /* unable to guarantee sensors will stay up */ + /* all the way until slider reached destination */ + /* thus not relying on sensors for this transition */ + md->sensors_off = true; + LOGD("turned off sensors\n"); + } + md->power_en = 0; + motor_set_motion_params(md, curpos, POS_COMPACT); + atomic_set(&md->destination, POS_COMPACT); + moto_drv8424_set_enable_with_power(md, true); + msleep(5000); + } +} + +static const struct of_device_id moto_drv8424_match_table[] = { + {.compatible = "moto,drv8424"}, + {}, +}; +MODULE_DEVICE_TABLE(of, moto_drv8424_match_table); + +static struct platform_driver moto_drv8424_driver = { + .probe = moto_drv8424_probe, + .remove = moto_drv8424_remove, + .shutdown = moto_drv8424_platform_shutdown, + .driver = { + .name = "moto,drv8424", + .owner = THIS_MODULE, + .of_match_table = moto_drv8424_match_table, + }, +}; + +static int __init moto_drv8424_motor_init(void) +{ + return platform_driver_register(&moto_drv8424_driver); +} + +module_init(moto_drv8424_motor_init); + +static void __exit moto_drv8424_motor_exit(void) +{ + platform_driver_unregister(&moto_drv8424_driver); +} +module_exit(moto_drv8424_motor_exit); + +MODULE_DESCRIPTION("Motorola TI DRV8424 Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/dts201a/Android.mk b/drivers/misc/dts201a/Android.mk new file mode 100644 index 000000000000..2353d0590a74 --- /dev/null +++ b/drivers/misc/dts201a/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := dts201a_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/misc/dts201a/Kbuild b/drivers/misc/dts201a/Kbuild new file mode 100644 index 000000000000..8a5c19ef97c1 --- /dev/null +++ b/drivers/misc/dts201a/Kbuild @@ -0,0 +1,40 @@ +HAVE_KERNEL_414 = $(shell test -d $(TOP)/kernel/msm-4.14 && echo 1) + +ifeq ($(HAVE_KERNEL_414),1) + KERNEL_SOURCE_PATH ?= kernel/msm-4.14 +else + KERNEL_SOURCE_PATH ?= kernel/msm-4.9 +endif + +# Each configuration option enables a list of files. +FEATURE_USE_CCI := false + +ifeq ($(FEATURE_USE_CCI), true) +HAVE_TECHPACK_CAMERA = $(shell test -d $(TOP)/$(KERNEL_SOURCE_PATH)/techpack/camera && echo 1) + +ifeq ($(HAVE_TECHPACK_CAMERA),1) + KERNEL_CAMERA_DRIVER_PATH ?= $(TOP)/$(KERNEL_SOURCE_PATH)/techpack/camera/drivers + EXTRA_CFLAGS += -DCONFIG_SPECTRA_CAMERA=1 + EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/../include + EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/../include/uapi +else + KERNEL_CAMERA_DRIVER_PATH ?= $(TOP)/$(KERNEL_SOURCE_PATH)/drivers/media/platform/msm/camera +endif + +EXTRA_CFLAGS += -DCAMERA_CCI +EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/cam_utils +EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/cam_cpas/include +EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/cam_sensor_module/cam_sensor_io +EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/cam_sensor_module/cam_sensor_utils +EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/cam_sensor_module/cam_cci +EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/cam_req_mgr +EXTRA_CFLAGS += -I$(KERNEL_CAMERA_DRIVER_PATH)/cam_smmu +endif + +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include +EXTRA_CFLAGS += -Wall +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../sensors/Module.symvers +dts201a_mmi-y := dts201a.o dts201a-cci.o +obj-m += dts201a_mmi.o + + diff --git a/drivers/misc/dts201a/Makefile b/drivers/misc/dts201a/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/misc/dts201a/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/misc/dts201a/dts201a-cci.c b/drivers/misc/dts201a/dts201a-cci.c new file mode 100644 index 000000000000..c5d9a70ccc1b --- /dev/null +++ b/drivers/misc/dts201a/dts201a-cci.c @@ -0,0 +1,471 @@ +/* + * dts201a_module-cci.c - Linux kernel modules for Partron + * sensor + * + * Copyright (C) 2020 Motorola mobility + * + * 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 CAMERA_CCI +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * power specific includes + */ +#include +#include +#include +#include +#include +/* + * API includes + */ +#include "dts201a-cci.h" + +#define SENSOR_NAME "dts201a" +#define DRV_NAME "dts201a_cci" + + +#define OTHER_DEVICE_TYPE 0x000200ff +struct cam_subdev *g_v4l2_dev_str = NULL; + + + +static int dts201a_request_pwren(struct cci_ctrl_t *cci_ctrl) +{ + int rc = 0; + + if (cci_ctrl->pwren_gpio == -1) { + dts201a_wanrmsg("pwren gpio disable"); + goto no_gpio; + } + + dts201a_dbgmsg("request pwren_gpio %d", cci_ctrl->pwren_gpio); + rc = gpio_request(cci_ctrl->pwren_gpio, "dts201a_pwren"); + if (rc) { + dts201a_errmsg("fail to acquire pwren %d", rc); + goto no_gpio; + } + + rc = gpio_direction_output(cci_ctrl->pwren_gpio, 0); + if (rc) { + dts201a_errmsg("fail to configure pwren as output %d", rc); + } + + return rc; + +no_gpio: + rc =-1; + return rc; +} + +static void dts201a_release_pwren(struct cci_ctrl_t *cci_ctrl) +{ + if (cci_ctrl->pwren_gpio > 0) { + dts201a_dbgmsg("release pwren_gpio %d", cci_ctrl->pwren_gpio); + gpio_free(cci_ctrl->pwren_gpio); + } + cci_ctrl->pwren_gpio = -1; +} + +static void dts201a_release_gpios_cci(struct cci_ctrl_t *t_ctrl) +{ + if (t_ctrl->power_supply) { + regulator_put(t_ctrl->power_supply); + t_ctrl->power_supply = NULL; + } + if (t_ctrl->cci_supply) { + regulator_put(t_ctrl->cci_supply); + t_ctrl->cci_supply = NULL; + } + dts201a_release_pwren(t_ctrl); +} + +static int dts201a_get_dt_info(struct device *dev, struct cci_ctrl_t *t_ctrl) +{ + int rc = 0; + struct device_node *of_node = NULL; + + if (!dev || !t_ctrl) + return -EINVAL; + + of_node = dev->of_node; + if (!of_node) { + dts201a_errmsg("of_node is NULL %d\n", __LINE__); + return -EINVAL; + } + + t_ctrl->pwren_gpio = -1; + rc = of_property_read_u32(of_node, "cell-index", &t_ctrl->pdev->id); + if (rc < 0) { + dts201a_errmsg("failed to read cell index %d\n", __LINE__); + return rc; + } + + rc = of_property_read_u32(of_node, "cci-master", &t_ctrl->cci_master); + if (rc < 0) { + dts201a_errmsg("failed to get the cci master %d\n", __LINE__); + return rc; + } + rc = of_property_read_u32(of_node, "cci-device", &t_ctrl->cci_num); + if (rc < 0) { + /* Set default master 0 */ + t_ctrl->cci_num = CCI_DEVICE_0; + rc = 0; + } + t_ctrl->io_master_info.cci_client->cci_device = t_ctrl->cci_num; + + t_ctrl->power_supply = regulator_get(dev, "laser"); + if (IS_ERR(t_ctrl->power_supply) || t_ctrl->power_supply == NULL) { + t_ctrl->power_supply = NULL; + + dts201a_wanrmsg("no regulator, laser power_supply"); + } + t_ctrl->cci_supply = regulator_get(dev, "cci"); + if (IS_ERR(t_ctrl->cci_supply)) { + t_ctrl->cci_supply = NULL; + /* try gpio */ + rc = of_property_read_u32_array(dev->of_node, "pwren-gpio", &t_ctrl->pwren_gpio, 1); + if (rc) { + t_ctrl->pwren_gpio = -1; + dts201a_wanrmsg("no regulator, nor power gpio => power ctrl disabled"); + } + if (gpio_is_valid(t_ctrl->pwren_gpio)) { + rc = devm_gpio_request(dev, gf_dev->pwr_gpio, "fir_pwr"); + if (rc) { + dts201a_wanrmsg("failed to request pwr gpio, rc = %d\n", rc); + } + gpio_direction_output(gf_dev->pwr_gpio, 1); + dts201a_wanrmsg("Unable to get cci power supply"); + } + if (t_ctrl->pwren_gpio != -1) + rc = dts201a_request_pwren(t_ctrl); + return rc; + + +} +static int msm_tof_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + int rc = 0; + return rc; +} + +static const struct v4l2_subdev_internal_ops msm_tof_internal_ops = { + .close = msm_tof_close, +}; + +static long msm_tof_subdev_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, void *arg) +{ + int32_t rc = 0; + return rc; +} + +static int32_t msm_tof_power(struct v4l2_subdev *sd, int on) +{ + dts201a_dbgmsg("TOF power called\n"); + return 0; +} + +static struct v4l2_subdev_core_ops msm_tof_subdev_core_ops = { + .ioctl = msm_tof_subdev_ioctl, + .s_power = msm_tof_power, +}; + +static struct v4l2_subdev_ops msm_tof_subdev_ops = { + .core = &msm_tof_subdev_core_ops, +}; + +static int32_t dts201a_platform_probe(struct platform_device *pdev) +{ + int32_t rc = 0; + struct dts201a_data *dts201a_data = NULL; + struct cci_ctrl_t *cci_ctrl = NULL; + struct cam_sensor_cci_client *cci_client = NULL; + + dts201a_errmsg("Enter\n"); + + if (!cam_cci_get_subdev(0)){ + dts201a_errmsg("Get cam_cci_get_subdev error\n"); + return -EPROBE_DEFER; + } + dts201a_info("Get cam_cci_get_subdev success\n"); + dts201a_data = kzalloc(sizeof(struct dts201a_data), GFP_KERNEL); + if (!dts201a_data) { + rc = -ENOMEM; + dts201a_errmsg("memory failed\n"); + return rc; + } + + if (dts201a_data) { + dts201a_data->client_object = kzalloc(sizeof(struct cci_ctrl_t), GFP_KERNEL); + if (!dts201a_data->client_object) { + rc = -ENOMEM; + dts201a_errmsg("alloc client_object failed\n"); + goto free_dts201a_data; + } + cci_ctrl = (struct cci_ctrl_t *)dts201a_data->client_object; + } + mutex_init(&dts201a_data->lock); + mutex_lock(&dts201a_data->lock); + cci_ctrl->pdev = pdev; + + cci_ctrl->dts201a_data = dts201a_data; + cci_ctrl->device_type = MSM_CAMERA_PLATFORM_DEVICE; + cci_ctrl->cam_pinctrl_status = 0; + cci_ctrl->io_master_info.master_type = CCI_MASTER; + cci_ctrl->io_master_info.cci_client = kzalloc( + sizeof(struct cam_sensor_cci_client), GFP_KERNEL); + if (!cci_ctrl->io_master_info.cci_client) { + dts201a_errmsg("alloc cci_client failed\n"); + goto free_tof_ctrl; + } + + rc = dts201a_get_dt_info(&pdev->dev, cci_ctrl); + if (rc < 0) { + dts201a_errmsg("%d, failed to get dt info rc %d\n", __LINE__, rc); + goto free_cci_client; + } + + + cci_client = cci_ctrl->io_master_info.cci_client; + cci_client->cci_i2c_master = cci_ctrl->cci_master; + //i2c slave address + cci_client->sid = 0x3A; + cci_client->retries = 3; + cci_client->id_map = 0; + //i2c speed 400k fast, 100k standard I2C_STANDARD_MODE + cci_client->i2c_freq_mode = I2C_FAST_MODE; + + cci_ctrl->v4l2_dev_str.internal_ops = &msm_tof_internal_ops; + cci_ctrl->v4l2_dev_str.ops = &msm_tof_subdev_ops; + strlcpy(cci_ctrl->device_name, SENSOR_NAME, + sizeof(cci_ctrl->device_name)); + cci_ctrl->v4l2_dev_str.name = cci_ctrl->device_name; + cci_ctrl->v4l2_dev_str.sd_flags = (V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS); + cci_ctrl->v4l2_dev_str.sd_flags = (V4L2_SUBDEV_FL_HAS_EVENTS); + cci_ctrl->v4l2_dev_str.ent_function = OTHER_DEVICE_TYPE; + cci_ctrl->v4l2_dev_str.token = cci_ctrl; + + rc = cam_register_subdev(&(cci_ctrl->v4l2_dev_str)); + if (rc) { + dts201a_errmsg("fail to create subdev"); + goto unregister_subdev; + } + dts201a_info("cam_register_subdev done \n"); + g_v4l2_dev_str = &cci_ctrl->v4l2_dev_str; + + /* setup device data */ + dev_set_drvdata(&pdev->dev, dts201a_data); + + dts201a_power_up_cci((void *)cci_ctrl); + msleep(10); + /* setup other stuff */ + rc = dts201a_setup(&pdev->dev, dts201a_data); + if (rc) { + dts201a_errmsg("fail to dts201a_palfrform_probe"); + goto release_gpios; + } + kref_init(&cci_ctrl->ref); + + dts201a_info("End = %d\n", rc); + mutex_unlock(&dts201a_data->lock); + return rc; + +release_gpios: + camera_io_release(&cci_ctrl->io_master_info); + dts201a_release_gpios_cci(cci_ctrl); +unregister_subdev: + cam_unregister_subdev(&(cci_ctrl->v4l2_dev_str)); +free_cci_client: + kfree(cci_ctrl->io_master_info.cci_client); +free_tof_ctrl: + kfree(cci_ctrl); +free_dts201a_data: + mutex_unlock(&dts201a_data->lock); + kfree(dts201a_data); + + return rc; +} + +static int32_t dts201a_platform_remove(struct platform_device *pdev) +{ + int ret = 0; + struct dts201a_data *dts201a_data = platform_get_drvdata(pdev); + struct cci_ctrl_t *cci_ctrl = (struct cci_ctrl_t *)dts201a_data->client_object; + dts201a_cleanup(dts201a_data); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static const struct of_device_id dts201a_cci_dt_match[] = { + {.compatible = "partron,dts201a",}, + {}, +}; + + +#ifdef CONFIG_PM +/** + * dts201a_pm_suspend - PM suspend function + * Called by kernel during system suspend phrase + */ +static int dts201a_pm_suspend(struct device *dev) +{ + return 0; +} +/** + * dts201a_pm_resume - PM resume function + * Called by kernel during system wakeup + */ +static int dts201a_pm_resume(struct device *dev) +{ + return 0; +} + +#endif + +#ifdef CONFIG_PM +static const struct dev_pm_ops dev_pm_ops = { + .suspend = dts201a_pm_suspend, + .resume = dts201a_pm_resume, +}; +#endif + +static struct platform_driver dts201a_platform_driver = { + .probe = dts201a_platform_probe, + .remove = dts201a_platform_remove, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = dts201a_cci_dt_match, +#ifdef CONFIG_PM + .pm = &dev_pm_ops, +#endif + + }, +}; + +int dts201a_power_up_cci(void *object) +{ + int rc = 0; + struct cci_ctrl_t *cci_ctrl = (struct cci_ctrl_t *)object; + + dts201a_dbgmsg("Enter"); + + if (!cci_ctrl) { + dts201a_errmsg("dts201a_power_up_cci failed %d\n", __LINE__); + return -EINVAL; + } + /* turn on power */ + if (cci_ctrl->power_supply) { + rc = cam_soc_util_regulator_enable(cci_ctrl->power_supply, "laser", 2800000, 2800000, 80000, 0); + if (rc) { + dts201a_errmsg("fail to turn on avdd regulator"); + return rc; + } + dts201a_dbgmsg("enable laser 28\n"); + } else + dts201a_wanrmsg("no power control"); + if (cci_ctrl->cci_supply) { + rc = cam_soc_util_regulator_enable(cci_ctrl->cci_supply,"cci", 1800000, 1800000, 0, 0); + if (rc) { + dts201a_errmsg("fail to turn on iovdd regulator"); + return rc; + } + dts201a_dbgmsg("enable laser 18\n"); + } else { + if (cci_ctrl->pwren_gpio != -1) { + gpio_set_value_cansleep(cci_ctrl->pwren_gpio, 1); + } + } + msleep(10); + rc = camera_io_init(&cci_ctrl->io_master_info); + if (rc < 0) + dts201a_errmsg("cci init failed: rc: %d", rc); + + dts201a_dbgmsg("End\n"); + + return rc; +} + +int dts201a_power_down_cci(void *cci_object) +{ + int rc = 0; + struct cci_ctrl_t *cci_ctrl = (struct cci_ctrl_t *)cci_object; + + if (!cci_ctrl) { + dts201a_errmsg("dts201a_power_down_cci failed %d\n", __LINE__); + return -EINVAL; + } + + dts201a_dbgmsg("Enter\n"); + /* turn off power */ + if (cci_ctrl->power_supply) { + rc = cam_soc_util_regulator_disable(cci_ctrl->power_supply, "laser", 2800000, 2800000, 80000, 0); + rc = cam_soc_util_regulator_disable(cci_ctrl->cci_supply,"cci", 1800000, 1800000, 0, 0); + if (rc) + dts201a_errmsg("reg disable failed. rc=%d\n", + rc); + } else if (cci_ctrl->pwren_gpio != -1) { + gpio_set_value_cansleep(cci_ctrl->pwren_gpio, 0); + } + camera_io_release(&cci_ctrl->io_master_info); + + dts201a_dbgmsg("power off"); + + return rc; +} + +void dts201a_clean_up_cci(void) +{ + int rc = 0; + rc = cam_unregister_subdev(g_v4l2_dev_str); +} + +int dts201a_init_cci(void) +{ + int ret = 0; + + dts201a_errmsg("Enter\n"); + + /* register as a platform device */ + ret = platform_driver_register(&dts201a_platform_driver); + if (ret) + dts201a_errmsg("%d, error ret:%d\n", __LINE__, ret); + + dts201a_dbgmsg("End\n"); + + return ret; +} + +void dts201a_exit_cci(void *object) +{ + struct cci_ctrl_t *cci_ctrl = (struct cci_ctrl_t *)object; + + dts201a_dbgmsg("Enter\n"); + platform_driver_unregister(&dts201a_platform_driver); + dts201a_dbgmsg("End\n"); +} +#endif /* end of CAMERA_CCI */ diff --git a/drivers/misc/dts201a/dts201a-cci.h b/drivers/misc/dts201a/dts201a-cci.h new file mode 100644 index 000000000000..e82ee76bc7e2 --- /dev/null +++ b/drivers/misc/dts201a/dts201a-cci.h @@ -0,0 +1,63 @@ +/* + * dts201a_module-cci.h - Linux kernel modules for Partron thermopile sensor + * + * Copyright (C) 2020 Motorola mobility + * + * 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. + */ +/* + * Defines + */ +#ifndef DTS201A_CCI_H +#define DTS201A_CCI_H +#include +#include "dts201a.h" + + +#ifdef CAMERA_CCI +struct cci_ctrl_t { + struct platform_device *pdev; + enum msm_camera_device_type_t device_type; + enum cci_device_num cci_num; + enum cci_i2c_master_t cci_master; + struct camera_io_master io_master_info; + struct cam_subdev v4l2_dev_str; + struct dts201a_data *dts201a_data; + struct msm_pinctrl_info pinctrl_info; + uint8_t cam_pinctrl_status; + char device_name[20]; + + /* reference counter */ + struct kref ref; + /*!< if null no regulator use for power ctrl */ + struct regulator *power_supply; + + struct regulator *cci_supply; + + /*!< power enable gpio number + * + * if -1 no gpio if vdd not avl pwr is not controllable + */ + int pwren_gpio; +}; + +int dts201a_init_cci(void); +void __exit dts201a_exit_cci(void *); +int dts201a_power_up_cci(void *); +int dts201a_power_down_cci(void *); +int dts201a_reset_release_cci(void *); +int dts201a_reset_hold_cci(void *); +void dts201a_clean_up_cci(void); +void *dts201a_get_cci(void *); +void dts201a_put_cci(void *); + +#endif /* CAMERA_CCI */ +#endif /* DTS201A_CCI_H */ diff --git a/drivers/misc/dts201a/dts201a.c b/drivers/misc/dts201a/dts201a.c new file mode 100644 index 000000000000..a01d26282e8a --- /dev/null +++ b/drivers/misc/dts201a/dts201a.c @@ -0,0 +1,1414 @@ +/* +* drivers/sensors/dts201a.c +* +* Partron DTS201A Thermopile Sensor module driver +* +* Copyright (C) 2013 Partron Co., Ltd. - Sensor Lab. +* partron (partron@partron.co.kr) +* +* Both authors are willing to be considered the contact and update points for +* the driver. +* +* 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, write to the Free Software +* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +* 02110-1301 USA +* +*/ +/****************************************************************************** + Revision 1.0.0 2013/Aug/08: + first release + Revision 1.0.1 2017/Nov/30: + update for sysfs raw data and coefficient + Revision 2.0.0 2020/Jul/03: + update for coefficient read and add the emissivity caculation equation +******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dts201a-cci.h" +//do not use kernel PM +#undef CONFIG_PM + +#ifdef CONFIG_DRM + #include +#endif +//#undef DTS201A_DEBUG +#define DTS201A_DEBUG +#define DTS201A_TEST + +#ifdef DTS201A_MAVG +static u8 o_avg_cnt = 0; +static u8 a_avg_cnt = 0; + +#endif + +#ifndef SENSOR_TYPE_MOTO_BODYTEMP +#define SENSOR_TYPE_MOTO_BODYTEMP (SENSOR_TYPE_DEVICE_PRIVATE_BASE + 42) +#endif + +static struct dts201a_data *g_dts201a_data = NULL; + +static u8 i2c_mavg = 0; +static int dts201a_power_on(struct dts201a_data *ther, bool on ); +static int dts201a_read_coefficient_reg( struct dts201a_data *ther ); +#define USE_SENSORS_CLASS +struct sensors_classdev sensors_thermopile_cdev = { + .vendor = "partron", + .version = 1, + .type = SENSOR_TYPE_MOTO_BODYTEMP, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#ifdef CAMERA_CCI +static int dts201a_cci_read(struct dts201a_data *dev, uint32_t addr, u8* data, u8 len) +{ + int rc = 0; + struct cci_ctrl_t *cci_ctrl = NULL; + + cci_ctrl = (struct cci_ctrl_t *)dev->client_object; + //use custom addr type to skip addr set during reading + rc = cam_camera_cci_i2c_read_seq(cci_ctrl->io_master_info.cci_client, addr, data, CAMERA_SENSOR_I2C_TYPE_CUSTOM, + CAMERA_SENSOR_I2C_TYPE_BYTE, len); + return rc != 0; +} +static int dts201a_cci_write(struct dts201a_data *dev, uint32_t addr, + uint8_t *data, uint16_t len,uint delay){ + int i = 0, rc = 0; + struct cam_sensor_i2c_reg_setting write_reg_setting; + struct cam_sensor_i2c_reg_array *reg_array = NULL; + struct cci_ctrl_t *cci_ctrl = NULL; + + cci_ctrl = (struct cci_ctrl_t *)dev->client_object; + + reg_array = kzalloc(sizeof(struct cam_sensor_i2c_reg_array) * len, GFP_KERNEL); + if (!reg_array) { + dts201a_errmsg("cci_write malloc failed\n"); + } + + for (i = 0; i < len; i++) { + reg_array[i].reg_addr = addr + i; + reg_array[i].reg_data = data[i]; + } + + write_reg_setting.addr_type = CAMERA_SENSOR_I2C_TYPE_BYTE; + write_reg_setting.data_type = CAMERA_SENSOR_I2C_TYPE_BYTE; + write_reg_setting.size = len; + write_reg_setting.reg_setting = reg_array; + write_reg_setting.delay = delay; + + rc = cam_cci_i2c_write_table(&cci_ctrl->io_master_info, &write_reg_setting); + + kfree(reg_array); + return rc; + +} +#else + +static int dts201a_i2c_read(struct dts201a_data *ther, + u8 *buf, u8 len) +{ + + struct i2c_msg msg[1]; + int err = 0; + int retry = 3; + + if ((ther->client == NULL) || (!ther->client->adapter)) + return -ENODEV; + mutex_lock( &ther->dts201a_i2c_mutex ); + + msg->addr = ther->client->addr; + //msg->addr = DTS201A_SADDR; + msg->flags = DTS_I2C_READ; + msg->len = len; + msg->buf = buf; + + while (retry--) { + err = i2c_transfer(ther->client->adapter, msg, 1); + if (err >= 0) { + mutex_unlock( &ther->dts201a_i2c_mutex ); + return err; + } + } + mutex_unlock( &ther->dts201a_i2c_mutex ); + pr_err("%s: i2c read failed at addr 0x%x: %d\n", __func__, msg->addr, err); + + return err; + +} + + +static int dts201a_i2c_write(struct dts201a_data *ther, + u8 cmd, u16 bm_config) +{ + + struct i2c_msg msg[1]; + int err = 0; + int retry = 3; + u8 wdata[3]={0, }; + + if ((ther->client == NULL) || (!ther->client->adapter)) + return -ENODEV; + mutex_lock( &ther->dts201a_i2c_mutex ); + + wdata[0] = cmd; + wdata[1] = (u8)(bm_config>>8); + wdata[2] = (u8)(bm_config); + + msg->addr = ther->client->addr; +// msg->addr =DTS201A_SADDR; + msg->flags = DTS_I2C_WRITE; + msg->len = 3; + msg->buf = wdata; + + while (retry--) { + err = i2c_transfer(ther->client->adapter, msg, 1); + if ( err >= 0 ) { + mutex_unlock( &ther->dts201a_i2c_mutex ); + return err; + } + } + mutex_unlock( &ther->dts201a_i2c_mutex ); + pr_err( "%s, i2c transfer error(%d) addr=%x\n", __func__, err, msg->addr ); + return err; +} +#endif +static int dts201a_hw_init(struct dts201a_data *ther) +{ + int err = 0; + u8 rbuf[5]={0, }; + u16 custom_id[2]={0, }; + +#ifdef DTS201A_DEBUG + dts201a_dbgmsg("\t %s %s\n", __func__, DTS_DEV_NAME); +#endif + +#ifdef CAMERA_CCI + //three bytes command addr+00 +00 + memset(rbuf,0,sizeof(rbuf)); + err= dts201a_cci_write(ther, DTS_CUST_ID0,rbuf,2,0); + if ( err < 0 ) goto error_firstread; + msleep( 20 ); + err = dts201a_cci_read( ther, rbuf, 5 ); + if ( err < 0 ) goto error_firstread; + custom_id[0] = ( ( rbuf[1] << 8 ) | rbuf[2] ); + + memset(rbuf,0,sizeof(rbuf)); + err= dts201a_cci_write(ther, DTS_CUST_ID1,rbuf,2,0); + if ( err < 0 ) goto error_firstread; + msleep(20); + err= dts201a_cci_read(ther, 0,rbuf,5); + if (err < 0) goto error_firstread; + custom_id[1] = ( (rbuf[1]<<8) | rbuf[2] ); + dts201a_dbgmsg("status = 0x%02x: custom_id: 0x%04x_%04x\n", (rbuf[0], custom_id[0],custom_id[1]); + if ( custom_id[1] != DTS201A_ID1 && custom_id[0] != DTS201A_ID0 ) { + err = -ENODEV; + goto error_unknown_device; + } +#else + err = dts201a_i2c_write( ther, DTS_CUST_ID0, 0x0 ); + if ( err < 0 ) goto error_firstread; + msleep( 20 ); + err = dts201a_i2c_read( ther, rbuf, 3 ); + if ( err < 0 ) goto error_firstread; + custom_id[0] = ( ( rbuf[1] << 8 ) | rbuf[2] ); + err = dts201a_i2c_write( ther, DTS_CUST_ID1, 0x0 ); + if ( err < 0 ) goto error_firstread; + msleep( 20 ); + err = dts201a_i2c_read( ther, rbuf, 3); + if ( err < 0 ) goto error_firstread; + custom_id[1] = ( ( rbuf[1] << 8 ) | rbuf[2] ); + dts201a_dbgmsg("status = 0x%02x: custom_id: 0x%04x_%04x\n", rbuf[0], custom_id[0],custom_id[1]); + /* register DTS201A_ID1 will be used for other purpose */ + if (custom_id[0] != DTS201A_ID0) + { + err = -ENODEV; + goto error_unknown_device; + } +#endif + + err = dts201a_read_coefficient_reg( ther ); + if ( err < 0 ) goto error_firstread; + ther->hw_initialized = 1; + pr_info("[dts201a] Hw init is done!\n"); + + return 0; + +error_firstread: + pr_err("Error reading CUST_ID_DTS201A : is device " + "available/working?\n"); + goto err_resume_state; +error_unknown_device: + pr_err("device unknown. Expected: 0x%04x_%04x," + " custom_id: 0x%04x_%04x\n", DTS201A_ID1, + DTS201A_ID0, custom_id[1], custom_id[0]); +err_resume_state: + ther->hw_initialized = 0; + pr_err("hw init error : status = 0x%02x: err = %d\n", rbuf[0], err); + + return err; +} + +static int dts201a_get_thermtemp_data(struct dts201a_data *ther) +{ + int err = 0, i = 0; + u8 rbuf[7] = {0,}; + u32 thermopile = 0; + u32 temperature = 0; + u8 status = 0; + +#ifdef CAMERA_CCI + memset(rbuf,0,sizeof(rbuf)); + err= dts201a_cci_write(ther, DTS_MEASURE8,rbuf,2,0); + if (err < 0) goto i2c_error; + msleep(DTS_CONVERSION_TIME); + memset(rbuf,0,sizeof(rbuf)); + err= dts201a_cci_read(ther, 0,rbuf,7); + if (err < 0) goto i2c_error; +#else + err = dts201a_i2c_write(ther, DTS_MEASURE8, 0x0); + if (err < 0) goto i2c_error; + msleep(DTS_CONVERSION_TIME); + err = dts201a_i2c_read(ther, rbuf, 7); + if (err < 0) goto i2c_error; +#endif + status = rbuf[0]; + + thermopile = (rbuf[1] << 16) | (rbuf[2] << 8) | rbuf[3]; + temperature = (rbuf[4] << 16) | (rbuf[5] << 8) | rbuf[6]; + //average in i2c if i2c_mavg> 0 + if(i2c_mavg > 0) { + for(i=0; iclient->dev, "thermopile = 0x%06x" +// "Temperature = 0x%06x, status = 0x%02x\n", +// thermopile, temperature, status); + dts201a_dbgmsg("status =0x%02x rbuf[1][2][3] = 0x%02x_%02x_%02x = 0x%06x\n", + rbuf[0], rbuf[1], rbuf[2], rbuf[3], thermopile); + dts201a_dbgmsg(" rbuf[4][5][6] = 0x%02x_%02x_%02x = 0x%06x\n", + rbuf[4], rbuf[5], rbuf[6], temperature); +#endif + + ther->out.therm = thermopile; + ther->out.temp = temperature; + ther->out.status = status; + + return err; + +i2c_error : + ther->out.therm = 0; + ther->out.temp = 0; + ther->out.status = 0; + + return err; +} + +int dts201a_enable(struct dts201a_data *ther) +{ + int err = 0; + dts201a_power_on(ther,true); + if (!atomic_cmpxchg(&ther->enabled, 0, 1)) { + hrtimer_start(&ther->timer, ther->poll_delay, HRTIMER_MODE_REL); + } +#ifdef DTS201A_DEBUG + pr_info("%s = %d\n", __func__, atomic_read(&ther->enabled)); +#endif + return err; +} + +int dts201a_disable(struct dts201a_data *ther) +{ + int err = 0; + + if (atomic_cmpxchg(&ther->enabled, 1, 0)) { + hrtimer_cancel(&ther->timer); + cancel_work_sync(&ther->work_thermopile); + pr_info("%s cancel work\n", __func__); + } +#ifdef DTS201A_DEBUG + pr_info("%s = %d\n", __func__, atomic_read(&ther->enabled)); +#endif + dts201a_power_on(ther,false); + return err; +} + +#ifdef USE_SENSORS_CLASS +static int dts201a_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + struct dts201a_data *ther = g_dts201a_data; + int mEnabled = atomic_read(&ther->enabled); + + if(!ther) { + pr_info("\t %s g_dts201a_data is NULL\n", __func__); + return 0; + } + if ((enable == 1) && (mEnabled == 0)) { + dts201a_errmsg("enable thermopile sensor\n"); + dts201a_enable(ther); + } else if ((enable == 0) && (mEnabled == 1)) { + dts201a_errmsg("disable thermopile sensor\n"); + dts201a_disable(ther); + } else { + dts201a_errmsg("unknown enable symbol\n"); + } + + return 0; +} +#if 0 +static int dts201a_set_poll_delay(struct sensors_classdev *sensors_cdev, + unsigned int delay_msec) +{ + struct dts201a_data *ther = g_dts201a_data; + unsigned long new_delay = delay_msec * NSEC_PER_MSEC; + + if(!ther) { + pr_info("\t %s g_dts201a_data is NULL\n", __func__); + return 0; + } +#ifdef DTS201A_DEBUG + pr_info("\t[dts201a] %s new_delay = %ld\n", __func__, new_delay); +#endif + + if(new_delay < DTS_DELAY_MINIMUM) + new_delay = DTS_DELAY_MINIMUM; + if (new_delay != ktime_to_ns(ther->poll_delay)) + ther->poll_delay = ns_to_ktime(new_delay); + mutex_lock(&ther->lock); + ther->poll_delay = ns_to_ktime(new_delay); + mutex_unlock(&ther->lock); + return 0; +} +#endif +#endif +static ssize_t dts201a_poll_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dts201a_data *ther = dev_get_drvdata(dev); + +#ifdef DTS201A_DEBUG + pr_info("\t[dts201a] %s poll_delay = %lld\n", __func__, ktime_to_ns(ther->poll_delay)); +#endif + return sprintf(buf, "%lldns\n", ktime_to_ns(ther->poll_delay)); +} + +static ssize_t dts201a_poll_delay_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct dts201a_data *ther = dev_get_drvdata(dev); + unsigned long new_delay = 0; + int err; + + err = kstrtoul(buf, 10, &new_delay); + if (err < 0) + return err; + +#ifdef DTS201A_DEBUG + pr_info("\t[dts201a] %s new_delay = %ld\n", __func__, new_delay); +#endif + + if(new_delay < DTS_DELAY_MINIMUM) + new_delay = DTS_DELAY_MINIMUM; + if (new_delay != ktime_to_ns(ther->poll_delay)) + ther->poll_delay = ns_to_ktime(new_delay); + mutex_lock(&ther->lock); + ther->poll_delay = ns_to_ktime(new_delay); + mutex_unlock(&ther->lock); + + return size; +} + +static ssize_t dts201a_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct dts201a_data *ther = dev_get_drvdata(dev); + +#ifdef DTS201A_DEBUG + pr_info("\t[dts201a] %s ther->enabled = %d\n", __func__, + atomic_read(&ther->enabled)); +#endif + + return sprintf(buf, "%d\n", atomic_read(&ther->enabled)); +} + +static ssize_t dts201a_data2read_show( struct device *dev, + struct device_attribute *attr, char *buf ) +{ + struct dts201a_data *ther = dev_get_drvdata( dev ); + bool current_vdd = ther->vdd_en; + if(!current_vdd) + dts201a_power_on(ther,true); + mutex_lock(&ther->lock); + dts201a_get_thermtemp_data(ther); + mutex_unlock(&ther->lock); + if(!current_vdd) + dts201a_power_on(ther,false); + return sprintf( buf, "%d %d %d\n", ther->out.therm, ther->out.temp ,ther->out.status ); +} + + +static ssize_t dts201a_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct dts201a_data *ther = dev_get_drvdata(dev); + bool new_value; + + if (sysfs_streq(buf, "1")) + new_value = true; + else if (sysfs_streq(buf, "0")) + new_value = false; + else { + pr_err("%s: invalid value %d\n", __func__, *buf); + return -EINVAL; + } + +#ifdef DTS201A_DEBUG + pr_info("\t[dts201a] %s new_value = 0x%d\n", __func__, new_value); +#endif + + if (new_value) + dts201a_enable(ther); + else + dts201a_disable(ther); + + return size; +} + + +static DEVICE_ATTR(poll_delay, (S_IWUSR|S_IRUSR | S_IWGRP|S_IRGRP), + dts201a_poll_delay_show, dts201a_poll_delay_store); +static DEVICE_ATTR(enable, (S_IWUSR|S_IRUSR | S_IWGRP|S_IRGRP), + dts201a_enable_show, dts201a_enable_store); + +static struct attribute *thermopile_sysfs_attrs[] = { + &dev_attr_poll_delay.attr, + &dev_attr_enable.attr, + NULL +}; + +static struct attribute_group thermopile_attribute_group = { + .attrs = thermopile_sysfs_attrs, +}; + +static ssize_t dts201a_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef DTS201A_DEBUG + pr_info("\t[dts201a] %s VENDOR = %s\n", __func__, DTS_VENDOR); +#endif + + return sprintf(buf, "%s\n", DTS_VENDOR); +} + +static ssize_t dts201a_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ +#ifdef DTS201A_DEBUG + pr_info("\t[dts201a] %s CHIP_ID = %s\n", __func__, DTS_CHIP_ID); +#endif + return sprintf(buf, "%s\n", DTS_CHIP_ID); +} + +#ifdef DTS201A_TEST +/*modify 11.30*/ +static ssize_t coefficient_reg_show( struct device *dev, + struct device_attribute *attr, char *buf ) +{ + + struct dts201a_data *ther = dev_get_drvdata( dev ); + int i=0; + printk( "[dts201a] %s\n", __func__ ); + + for( i = 0; i < 34; i++ ) + printk( "[dts201a] %s ndata[%d]=0x%02x\n", __func__, i, ther->ndata[i] ); + + for( i = 0; i < 34; i++ ) + printk( "[dts201a] %s hdata[%d]=0x%02x\n", __func__, i, ther->hdata[i] ); + + return sprintf( buf, "%02x%02x%02x%02x%02x%02x%02x%02x"\ + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"\ + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"\ + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"\ + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"\ + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"\ + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", + ther->ndata[0], ther->ndata[1], ther->ndata[2], ther->ndata[3], ther->ndata[4], ther->ndata[5], + ther->ndata[6], ther->ndata[7], ther->ndata[8], ther->ndata[9], ther->ndata[10], ther->ndata[11], + ther->ndata[12], ther->ndata[13], ther->ndata[14], ther->ndata[15], ther->ndata[16], ther->ndata[17], + ther->ndata[18], ther->ndata[19], ther->ndata[20], ther->ndata[21], ther->ndata[22], ther->ndata[23], + ther->ndata[24], ther->ndata[25], ther->ndata[26], ther->ndata[27], ther->ndata[28], ther->ndata[29], + ther->ndata[30], ther->ndata[31], ther->ndata[32], ther->ndata[33], + ther->hdata[0], ther->hdata[1], ther->hdata[2], ther->hdata[3], ther->hdata[4], ther->hdata[5], + ther->hdata[6], ther->hdata[7], ther->hdata[8], ther->hdata[9], ther->hdata[10], ther->hdata[11], + ther->hdata[12], ther->hdata[13], ther->hdata[14], ther->hdata[15], ther->hdata[16], ther->hdata[17], + ther->hdata[18], ther->hdata[19], ther->hdata[20], ther->hdata[21], ther->hdata[22], ther->hdata[23], + ther->hdata[24], ther->hdata[25], ther->hdata[26], ther->hdata[27], ther->hdata[28], ther->hdata[29], + ther->hdata[30], ther->hdata[31], ther->hdata[32], ther->hdata[33] ); + +} +/*modify 20.06.09*/ +static int dts201a_read_coefficient_reg( struct dts201a_data *ther ) +{ + int err = 0; + u8 rbuf[3]={0, }; + + int i; + dts201a_errmsg(" [BODYTEMP]\n"); + // 0x1a-0x27 General Mode + for(i=STX_N1; i<=ETX_N1; i++) + { +#ifdef CAMERA_CCI + memset(rbuf,0,sizeof(rbuf)); + err= dts201a_cci_write(ther, i,rbuf,2,0); + if (err < 0) { + dts201a_errmsg("i2c write error\n"); + return err; + } + msleep(5); + err= dts201a_cci_read(ther, 0,rbuf,3); +#else + err = dts201a_i2c_write(ther, i, 0x0); + if (err < 0) { + pr_err("%s: i2c write error\n", __func__); + return err; + } + /*modify 11.30 30 - 5*/ + msleep(5); + err = dts201a_i2c_read(ther, rbuf, 3); +#endif + /* add busy bit check */ + if ( (err < 0) || (rbuf[0]&0x20) ) { + dts201a_errmsg(" i2c read error=0x%02x, 0x%02x\n", err, rbuf[0]); + return err; + } + ther->ndata[( i - STX_N1 ) + ( i - STX_N1 )] = rbuf[1]; + ther->ndata[( ( i - STX_N1 ) + ( i - STX_N1 ) ) + 1] = rbuf[2]; + } + //0x13-0x15 + for(i=STX_M1; i<=ETX_M1; i++) + { +#ifdef CAMERA_CCI + memset(rbuf,0,sizeof(rbuf)); + err= dts201a_cci_write(ther, i,rbuf,2,0); + if (err < 0) { + dts201a_errmsg("i2c write error\n"); + return err; + } + msleep(5); + err= dts201a_cci_read(ther, 0,rbuf,3); +#else + err = dts201a_i2c_write(ther, i, 0x0); + if (err < 0) { + pr_err("%s: i2c write error\n", __func__); + return err; + } + /*modify 11.30*/ + msleep( 5 ); + err = dts201a_i2c_read(ther, rbuf, 3); +#endif + /* add busy bit check */ + if ( (err < 0) || (rbuf[0]&0x20) ) { + dts201a_errmsg(" i2c read error=0x%02x, 0x%02x\n", err, rbuf[0]); + return err; + } + ther->ndata[( i - STX_M1 ) + ( i - STX_M1 ) + ( 28 )] = rbuf[1]; + ther->ndata[( ( i - STX_M1 ) + ( i - STX_M1 ) ) + 1 + ( 28 )] = rbuf[2]; + } + + for(i=0; i<34; i++) + dts201a_errmsg("[BODYTEMP] ndata[%d]=0x%02x\n", i,ther->ndata[i]); + //0x28-0x38 + for(i=STX_H2; i<=ETX_M2; i++) { +#ifdef CAMERA_CCI + memset(rbuf,0,sizeof(rbuf)); + err= dts201a_cci_write(ther, i,rbuf,2,0); + if (err < 0) { + dts201a_errmsg(" i2c write error\n"); + return err; + } + /*modify 11.30*/ + msleep( 5 ); + err= dts201a_cci_read(ther, 0,rbuf,3); + /* add busy bit check */ + if ( ( err < 0 ) || ( rbuf[0] & 0x20 ) ) { + pr_err( "%s: i2c read error=0x%02x, 0x%02x\n", __func__, err, rbuf[0] ); + return err; + } + ther->hdata[( i - STX_H2 ) + ( i - STX_H2 )] = rbuf[1]; + ther->hdata[( ( i - STX_H2 ) + ( i - STX_H2 ) ) + 1] = rbuf[2]; +#else + err = dts201a_i2c_write( ther, i, 0x0 ); + if ( err < 0 ) { + pr_err( "%s: i2c write error\n", __func__ ); + return err; + } + + /*modify 11.30*/ + msleep( 5 ); + err = dts201a_i2c_read( ther, rbuf, 3 ); + /* add busy bit check */ + if ( ( err < 0 ) || ( rbuf[0] & 0x20 ) ) { + pr_err( "%s: i2c read error=0x%02x, 0x%02x\n", __func__, err, rbuf[0] ); + return err; + } + + ther->hdata[( i - STX_H2 ) + ( i - STX_H2 )] = rbuf[1]; + ther->hdata[( ( i - STX_H2 ) + ( i - STX_H2 ) ) + 1] = rbuf[2]; +#endif + } + for(i=0; i<34; i++) + dts201a_errmsg("[BODYTEMP] hdata[%d]=0x%02x\n", i,ther->hdata[i]); + return 0; +} +static ssize_t i2c_avg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", i2c_mavg); +} + +static ssize_t i2c_avg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int err = 0; + int count; + + err = kstrtouint(buf, 10, &count); + if (err < 0) + return err; + i2c_mavg = count; +#ifdef DTS201A_DEBUG + pr_info("\t[dts201a] %s i2c_mavg = %d\n", __func__, (int)i2c_mavg); +#endif + return size; +} +static int reg_addr = 0x17; +static ssize_t i2c_register_read_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int err = 0; + u8 rbuf[7]={0, }; + int reg; + struct dts201a_data *ther = dev_get_drvdata( dev ); + + reg = reg_addr; +#ifdef CAMERA_CCI + memset(rbuf,0,sizeof(rbuf)); + err = dts201a_cci_write(ther, reg,rbuf,2,0); + if ( err < 0 ) goto error_firstread; + msleep( 20 ); + err= dts201a_cci_read(ther, 0,rbuf,7); + if ( err < 0 ) goto error_firstread; +#else + err = dts201a_i2c_write( ther, reg, 0x0 ); + if ( err < 0 ) goto error_firstread; + msleep( 20 ); + err = dts201a_i2c_read( ther, rbuf, 7); + if ( err < 0 ) goto error_firstread; +#endif + dts201a_dbgmsg(" dts201a: reg0x%x status = 0x%02x: 0x%04x_%04x 0x%04x_%04x 0x%04x_%04x\n", + reg,rbuf[0], rbuf[1],rbuf[2],rbuf[3],rbuf[4], rbuf[5],rbuf[6]); + +error_firstread: + return sprintf(buf, " reg0x%x 0x%02x_0x%04x_%04x 0x%04x_%04x 0x%04x_%04x\n", + reg,rbuf[0], rbuf[1],rbuf[2], rbuf[3],rbuf[4], rbuf[5],rbuf[6]); + +} +static ssize_t i2c_register_read_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int cnt; + int reg; + + cnt = sscanf(buf, "%x",®); + if (cnt == 1) + reg_addr = reg; + return size; +} +static ssize_t i2c_register_write_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + int err = 0,cnt; + int reg,data; +#ifdef CAMERA_CCI + u8 rbuf[2]={0, }; +#endif + struct dts201a_data *ther = dev_get_drvdata( dev ); + + cnt = sscanf(buf, "%x,%x",®,&data); + if (cnt != 2) + goto out; +#ifdef CAMERA_CCI + memset(rbuf,0,sizeof(rbuf)); + rbuf[0] = (u8)data&0xFF; + rbuf[1] = (u8)((data >> 8)&0xFF); + err = dts201a_cci_write(ther, reg,rbuf,2,0); +#else + err = dts201a_i2c_write( ther, reg, data ); +#endif + if ( err < 0 ) { + dts201a_dbgmsg("error write to 0x57\n"); + } +#ifdef DTS201A_DEBUG + pr_info("\t[dts201a] %s reg %x = %x err= %d\n", __func__,reg,data, err); +#endif +out: + return size; +} +#endif + +static DEVICE_ATTR(vendor, 0440, dts201a_vendor_show, NULL); +static DEVICE_ATTR(name, 0440, dts201a_name_show, NULL); +#ifdef DTS201A_TEST +static DEVICE_ATTR(coefficient_reg, 0440, coefficient_reg_show, NULL); +static DEVICE_ATTR(dts201a_data2read, 0440, dts201a_data2read_show, NULL ); +static DEVICE_ATTR(i2c_avg, (S_IWUSR|S_IRUSR | S_IWGRP|S_IRGRP), i2c_avg_show, i2c_avg_store); +static DEVICE_ATTR(i2c_register_read, (S_IWUSR|S_IRUSR | S_IWGRP|S_IRGRP), i2c_register_read_show, i2c_register_read_store); +static DEVICE_ATTR(i2c_register_write, (S_IWUSR| S_IWGRP), NULL, i2c_register_write_store); +#endif + +#ifdef CONFIG_DRM +int dts201a_drm_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank; + int ret = 0; + struct dts201a_data *ther = g_dts201a_data; + + if(!ther) { + pr_info("%s g_dts201a_data is NULL\n", __func__); + return 0; + } + if (!evdata || (evdata->id != 0)) { + return 0; + } + + if (!(event == MSM_DRM_EARLY_EVENT_BLANK || event == MSM_DRM_EVENT_BLANK)) { + pr_info("%s event(%lu) do not need process\n",__func__, event); + return 0; + } + blank = evdata->data; + pr_info("%s DRM event:%lu,blank:%d", __func__,event, *blank); + switch (*blank) { + case MSM_DRM_BLANK_UNBLANK: + if (MSM_DRM_EARLY_EVENT_BLANK == event) { + pr_debug("%s resume: event = %lu, not care\n", __func__, event); + } else if (MSM_DRM_EVENT_BLANK == event) { + pr_debug("%s resume: event = %lu ,display wake up\n", __func__,event); + } + break; + case MSM_DRM_BLANK_POWERDOWN: + if (MSM_DRM_EARLY_EVENT_BLANK == event) { + if (atomic_read(&ther->enabled)) { + ret = dts201a_disable(ther); + if (ret < 0) + pr_info("%s: could not disable\n", __func__); + } + } else if (MSM_DRM_EVENT_BLANK == event) { + pr_debug("%s suspend: event = %lu, not care\n", __func__,event); + } + break; + default: + pr_info("%s DAM BLANK(%d) do not need process\n", __func__, *blank); + break; + } + + return 0; +} +#endif +static enum hrtimer_restart dts201a_timer_func(struct hrtimer *timer) +{ + struct dts201a_data *ther = g_dts201a_data; + if(!ther) { + pr_info("\t %s g_dts201a_data is NULL\n", __func__); + return 0; + } + queue_work(ther->thermopile_wq, &ther->work_thermopile); + hrtimer_forward_now(&ther->timer, ther->poll_delay); + + return HRTIMER_RESTART; +} + +static void dts201a_work_func(struct work_struct *work) +{ + int err = 0; + struct dts201a_data *ther = g_dts201a_data; + if(!ther) { + pr_info("\t %s g_dts201a_data is NULL\n", __func__); + return; + } + mutex_lock(&ther->lock); + err = dts201a_get_thermtemp_data(ther); + mutex_unlock(&ther->lock); + if (err < 0) + dev_err(ther->dev, "get_thermopile_data failed\n"); + + input_report_rel(ther->input_dev, REL_X, ther->out.therm); + input_report_rel(ther->input_dev, REL_Y, ther->out.temp); + input_report_rel(ther->input_dev, REL_Z, ther->out.status); + input_sync(ther->input_dev); +#ifdef DTS201A_DEBUG + dts201a_dbgmsg(" therm = 0x%06x, temp = 0x%06x\n", + ther->out.therm, ther->out.temp); +#endif + + return; +} +/** + * One time device setup + * + * call by bus (i2c/cci) level probe to finalize non bus related device setup + * + * @param data The device data + * @return 0 on success + */ + +int dts201a_setup(struct device *dev,struct dts201a_data *data) + +{ + struct dts201a_data *ther; + int err = 0; + ther = data; + + if (ther == NULL) { + err = -ENOMEM; + pr_err( " dts201a_setup failed to allocate memory for module data: %d\n", err); + goto err_hw_init_failed; + } + dts201a_power_on(ther,true); + ther->dev = dev; + err = dts201a_hw_init(ther); + dts201a_errmsg("dts201a_hw_init %d\n",err); + if (err < 0) { + dts201a_errmsg("dts201a_hw_init failed\n"); + goto err_hw_init_failed; + } + ther->thermopile_class = class_create(THIS_MODULE, "thermopile"); + err = IS_ERR_OR_NULL(ther->thermopile_class ); + if(err) { + dts201a_errmsg("could not create class\n"); + goto err_class_create_failed; + } + ther->dev = device_create(ther->thermopile_class,NULL,0,data,"dts201a"); + err= IS_ERR(ther->dev); + if(err) { + dts201a_errmsg(" could not create device\n"); + goto err_device_create; + } + dev_set_drvdata(ther->dev, ther); + dts201a_info("device_create done \n"); + hrtimer_init(&ther->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + ther->poll_delay = ns_to_ktime(DTS_DELAY_DEFAULT * NSEC_PER_MSEC); + ther->timer.function = dts201a_timer_func; + + ther->thermopile_wq = create_singlethread_workqueue("dts201a_thermopile_wq"); + if (!ther->thermopile_wq) { + err = -ENOMEM; + dts201a_errmsg("could not create workqueue\n"); + goto err_create_workqueue; + } + + INIT_WORK(&ther->work_thermopile, dts201a_work_func); + + ther->input_dev = input_allocate_device(); + if (!ther->input_dev) { + err = -ENOMEM; + dev_err(ther->dev, "input device allocate failed\n"); + goto err_input_allocate_device; + } + + ther->input_dev->name = "bodytemp_sensor"; + input_set_drvdata(ther->input_dev, ther); + input_set_capability(ther->input_dev, EV_REL, REL_X); + input_set_capability(ther->input_dev, EV_REL, REL_Y); + input_set_capability(ther->input_dev, EV_REL, REL_Z); + //input_set_drvdata(ther->input_dev,ther); + err = input_register_device(ther->input_dev); + if (err < 0) { + dev_err(ther->dev, + "unable to register input polled device %s\n", + ther->input_dev->name); + goto err_input_register_device; + } + dts201a_info(" input device register done"); + + err = sysfs_create_group(&ther->input_dev->dev.kobj, + &thermopile_attribute_group); + if (err) { + pr_err("%s: could not create sysfs group\n", __func__); + goto err_sysfs_create_group; + } + + sensors_thermopile_cdev.sensors_enable = dts201a_set_enable;//dts201a_enable; + //Fix at 200ms delay + //sensors_thermopile_cdev.sensors_poll_delay =dts201a_set_poll_delay; + sensors_thermopile_cdev.name = "thermopile_sensor"; + err = sensors_classdev_register(&ther->input_dev->dev, &sensors_thermopile_cdev); + err = 0; + if (err < 0) { + dts201a_errmsg(" sensors_classdev_register failed[%d]\n", err); + goto err_class_device_create; + } + err = device_create_file(ther->dev, &dev_attr_vendor); + if (err < 0) { + dts201a_errmsg(" device_create failed(vendor) failed\n"); + goto err_device_create_file2; + } + + err = device_create_file(ther->dev, &dev_attr_name); + if (err < 0) { + dts201a_errmsg(" device_create failed(name) failed\n"); + goto err_device_create_file3; + } + +#ifdef DTS201A_TEST + if (device_create_file(ther->dev, &dev_attr_coefficient_reg) < 0) + { + dts201a_errmsg(" could not create device file(%s)!\n", dev_attr_coefficient_reg.attr.name); + goto err_device_create_file4; + } + if (device_create_file(ther->dev, &dev_attr_dts201a_data2read) < 0) + { + dts201a_errmsg(" could not create device file(%s)!\n", dev_attr_dts201a_data2read.attr.name); + goto err_device_create_file5; + } + if (device_create_file(ther->dev, &dev_attr_i2c_avg) < 0) + { + dts201a_errmsg(" could not create device file(%s)!\n", dev_attr_i2c_avg.attr.name); + goto err_device_create_file6; + } + if (device_create_file(ther->dev, &dev_attr_i2c_register_read) < 0) + { + dts201a_errmsg(" could not create device file(%s)!\n", dev_attr_i2c_register_read.attr.name); + goto err_device_create_file7; + } + + if (device_create_file(ther->dev, &dev_attr_i2c_register_write) < 0) + { + dts201a_errmsg(" could not create device file(%s)!\n", dev_attr_i2c_register_write.attr.name); + goto err_device_create_file8; + } +#endif +#ifdef CONFIG_DRM + + ther->fb_notif.notifier_call = dts201a_drm_notifier_callback; + err = msm_drm_register_client(&ther->fb_notif); + if (err) { + dts201a_errmsg(" [DRM]Unable to register fb_notifier: %d\n", err); + } +#endif + g_dts201a_data = ther; + dts201a_power_on(ther,false); + dts201a_info(" done"); + return 0; + +#ifdef DTS201A_TEST +err_device_create_file8: + device_remove_file(ther->dev, &dev_attr_i2c_register_write); +err_device_create_file7: + device_remove_file(ther->dev, &dev_attr_i2c_register_read); +err_device_create_file6: + device_remove_file(ther->dev, &dev_attr_i2c_avg); +err_device_create_file5: + device_remove_file(ther->dev, &dev_attr_dts201a_data2read); +err_device_create_file4: + device_remove_file(ther->dev, &dev_attr_coefficient_reg); +#endif +err_device_create_file3: + device_remove_file(ther->dev, &dev_attr_name); +err_device_create_file2: + device_remove_file(ther->dev, &dev_attr_vendor); +err_class_device_create: + sensors_classdev_unregister(&sensors_thermopile_cdev); + sysfs_remove_group(&ther->input_dev->dev.kobj, + &thermopile_attribute_group); +err_sysfs_create_group: + input_unregister_device(ther->input_dev); +err_input_register_device: + input_free_device(ther->input_dev); +err_input_allocate_device: + destroy_workqueue(ther->thermopile_wq); +err_create_workqueue: + device_unregister(ther->dev); +err_device_create: + class_unregister(ther->thermopile_class); +err_class_create_failed: +err_hw_init_failed: +#ifndef CAMERA_CCI + dts201a_power_on(ther,false); + if( ther->i2c_power_supply) { + regulator_put(ther->i2c_power_supply); + ther->i2c_power_supply = NULL; + } + if (gpio_is_valid(ther->i2c_pwren_gpio )) { + gpio_set_value(ther->i2c_pwren_gpio, 0); + devm_gpio_free(ther->dev, ther->i2c_pwren_gpio); + } +#endif + pr_err("%s: Driver Init failed\n", DTS_DEV_NAME); + return err; +} +int dts201a_cleanup(struct dts201a_data *ther) +{ +#ifdef CAMERA_CCI + struct cci_ctrl_t *cci_ctrl = (struct cci_ctrl_t *)ther->client_object; +#endif + if(!ther) { + pr_info("\t %s dts201a_data is NULL\n", __func__); + return -1; + } +#ifdef DTS201A_DEBUG + pr_info("\t[dts201a] %s = %d\n", __func__, 0); +#endif +#ifdef CONFIG_DRM + if (msm_drm_unregister_client(&ther->fb_notif)) + pr_info("%s Error occurred while unregistering fb_notifier \n", __func__); +#endif +#ifdef DTS201A_TEST + device_remove_file(ther->dev, &dev_attr_i2c_register_write); + device_remove_file(ther->dev, &dev_attr_i2c_register_read); + device_remove_file(ther->dev, &dev_attr_i2c_avg); + device_remove_file(ther->dev, &dev_attr_dts201a_data2read); + device_remove_file(ther->dev, &dev_attr_coefficient_reg); +#endif + device_remove_file(ther->dev, &dev_attr_name); + device_remove_file(ther->dev, &dev_attr_vendor); + sensors_classdev_unregister(&sensors_thermopile_cdev); + sysfs_remove_group(&ther->input_dev->dev.kobj, + &thermopile_attribute_group); + input_unregister_device(ther->input_dev); + dts201a_disable(ther); + flush_workqueue(ther->thermopile_wq); + destroy_workqueue(ther->thermopile_wq); +#ifdef CAMERA_CCI + dts201a_power_down_cci(cci_ctrl); + if (cci_ctrl->cam_pinctrl_status) { + ret = pinctrl_select_state( + cci_ctrl->pinctrl_info.pinctrl, + cci_ctrl->pinctrl_info.gpio_state_suspend); + if (ret) + dts201a_errmsg("cannot set pin to suspend state"); + + devm_pinctrl_put(cci_ctrl->pinctrl_info.pinctrl); + } + /* release gpios */ + dts201a_release_gpios_cci(cci_ctrl); + /* main driver cleanup */ + dts201a_clean_up_cci(); + kfree(cci_ctrl->io_master_info.cci_client); + kfree(cci_ctrl); +#else + dts201a_power_on(ther,false); + if( ther->i2c_power_supply) { + regulator_put(ther->i2c_power_supply); + ther->i2c_power_supply = NULL; + } + if (gpio_is_valid(ther->i2c_pwren_gpio )) { + gpio_set_value_cansleep(ther->i2c_pwren_gpio, 0); + devm_gpio_free(ther->dev, ther->i2c_pwren_gpio); + } +#endif + input_free_device(ther->input_dev); + if (!IS_ERR(ther->dev)) { + device_unregister(ther->dev); + } + if(!IS_ERR_OR_NULL(ther->thermopile_class )) { + class_unregister(ther->thermopile_class ); + } + mutex_destroy(&ther->lock); + mutex_destroy(&ther->dts201a_i2c_mutex); + kfree(ther); + g_dts201a_data = NULL; +#ifdef DTS201A_DEBUG + pr_info("\t[dts201a] %s done\n", __func__); +#endif + return 0; +} +#ifndef CAMERA_CCI +static int dts201a_of_init(struct i2c_client *client,struct dts201a_data *ther) { + struct device_node *np = client->dev.of_node; + int rc; + + //get power supply from dts + ther->i2c_pwren_gpio = -1; + ther->vdd_en = false; + //always on + ther->i2c_vdd_voltage = -1; + ther->i2c_power_supply = regulator_get(&client->dev, "i2c_vdd"); + if (IS_ERR(ther->i2c_power_supply)) { + ther->i2c_power_supply = NULL; + /* try gpio */ + ther->i2c_pwren_gpio = of_get_named_gpio(np, "pwren-gpio",0) ; + if (ther->i2c_pwren_gpio < 0) { + ther->i2c_pwren_gpio = -1; + dts201a_wanrmsg("no regulator, nor power gpio => power ctrl disabled"); + } + dts201a_errmsg("Unable to get i2c power supply"); + } else { + rc = of_property_read_u32(np, "i2c_vdd-voltage", &ther->i2c_vdd_voltage); + if (rc) { + dts201a_errmsg("not found i2c_vdd-voltage"); + //always on + ther->i2c_vdd_voltage = -1; + } + } + + if (gpio_is_valid(ther->i2c_pwren_gpio )) { + rc = devm_gpio_request(ther->dev, ther->i2c_pwren_gpio, "fir_pwr"); + if (rc) { + dts201a_wanrmsg("failed to request pwr gpio, rc = %d\n", rc); + } + //default off + gpio_direction_output(ther->i2c_pwren_gpio, 0); + } + return 0; +} +static int dts201a_power_on(struct dts201a_data *ther, bool on ){ + int rc = 0; + if(ther->vdd_en == on) { + dts201a_errmsg("vdd regulator is same as current do nothing\n"); + return 0; + } + if (IS_ERR(ther->i2c_power_supply)) { + dts201a_errmsg("vdd regulator is invalid\n"); + if (gpio_is_valid(ther->i2c_pwren_gpio )) { + gpio_set_value(ther->i2c_pwren_gpio, on ? 1:0); + } + } else { + if((ther->i2c_vdd_voltage > 0) && + (regulator_count_voltages(ther->i2c_power_supply)>0)) { + rc = regulator_set_voltage(ther->i2c_power_supply,ther->i2c_vdd_voltage, + ther->i2c_vdd_voltage); + if (rc) + dts201a_info("set i2c_vdd-voltage to %d failed",ther->i2c_vdd_voltage); + } + if(on) { + rc = regulator_enable(ther->i2c_power_supply); + } else { + rc = regulator_disable(ther->i2c_power_supply); + } + } + ther->vdd_en = on; + mdelay(10); + dts201a_errmsg("vdd regulator is %s\n", + ther->vdd_en ? "on" : "off"); + return 0; +} +static int dts201a_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) { + struct dts201a_data *ther; + int err = 0; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "client not i2c capable\n"); + err = -ENODEV; + goto err_exit_check_functionality_failed; + } + + ther = kzalloc(sizeof(struct dts201a_data), GFP_KERNEL); + if (ther == NULL) { + err = -ENOMEM; + dev_err(&client->dev, + "failed to allocate memory for module data: %d\n", err); + goto err_exit_alloc_data_failed; + } + + ther->client = client; + i2c_set_clientdata(client, ther); + + mutex_init(&ther->lock); + mutex_init(&ther->dts201a_i2c_mutex); + mutex_lock(&ther->lock); + err = dts201a_of_init(client, ther); + if (err < 0) { + dts201a_errmsg("%d, failed to get dt info rc %d\n", __LINE__, err); + goto free_i2c_client; + } + + /* setup other stuff */ + err = dts201a_setup(&client->dev, ther); + if (err) { + dts201a_errmsg("fail to dts201a_setup"); + goto free_i2c_client; + } + + dts201a_info("End = %d\n", err); + mutex_unlock(&ther->lock); + return err; +free_i2c_client: + mutex_unlock(&ther->lock); + if(ther) { + kfree(ther); + ther = NULL; + g_dts201a_data = NULL; + } +err_exit_alloc_data_failed: +err_exit_check_functionality_failed: + pr_err("%s: dts201a_i2c_probe failed\n", DTS_DEV_NAME); + return err; +} +static int dts201a_i2c_remove(struct i2c_client *client) +{ + struct dts201a_data *ther = i2c_get_clientdata(client); + return dts201a_cleanup(ther); +} +#ifdef CONFIG_PM +static int dts201a_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct dts201a_data *ther = i2c_get_clientdata(client); + int ret = 0; + if(!ther) { + pr_info("\t %s dts201a_data is NULL\n", __func__); + return 0; + } +#ifdef DTS201A_DEBUG + pr_info("\t[PARTRON] %s = %d\n", __func__, 0); +#endif + dts201a_power_on(ther,true); + + if (!atomic_read(&ther->enabled)) { + ret = dts201a_enable(ther); + if (ret < 0) + pr_err("%s: could not enable\n", __func__); + } + + return ret; +} +static int dts201a_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct dts201a_data *ther = i2c_get_clientdata(client); + int ret = 0; + if(!ther) { + pr_info("\t %s dts201a_data is NULL\n", __func__); + return 0; + } +#ifdef DTS201A_DEBUG + pr_info("\t[dts201a] %s = %d\n", __func__, 0); +#endif + + if (atomic_read(&ther->enabled)) { + ret = dts201a_disable(ther); + if (ret < 0) + pr_err("%s: could not disable\n", __func__); + } + dts201a_power_on(ther,false); + return ret; +} +#endif /* CONFIG_PM */ + +static struct i2c_device_id dts201a_i2c_id[] = { + { DTS_DEV_NAME, 0}, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, dts201a_i2c_id); +#ifdef CONFIG_PM +static const struct dev_pm_ops dts201a_pm_ops = { + .suspend = dts201a_i2c_suspend, + .resume = dts201a_i2c_resume, +}; +#endif +static const struct of_device_id dts201a_i2c_dt_match[] = { + {.compatible = "partron,dts201a_i2c"}, + { }, +}; +static struct i2c_driver dts201a_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DTS_DEV_NAME, + .of_match_table = dts201a_i2c_dt_match, +#ifdef CONFIG_PM + .pm = &dts201a_pm_ops, +#endif + }, + .id_table = dts201a_i2c_id, + .probe = dts201a_i2c_probe, + .remove = dts201a_i2c_remove, + +}; +#endif +static int __init dts201a_init(void) { +#ifdef DTS201A_DEBUG + pr_info("\t[dts201a] %s = %s\n", __func__, DTS_DEV_NAME); +#endif +#ifdef CAMERA_CCI + return dts201a_init_cci(); +#else + return i2c_add_driver(&dts201a_driver); +#endif + +} + +static void __exit dts201a_exit(void) { +#ifdef dts201A_DEBUG + pr_info("\t[dts201a] %s = %s\n", __func__, DTS_DEV_NAME); +#endif +#ifdef CAMERA_CCI + dts201a_exit_cci((void*)g_dts201a_data); +#else + i2c_del_driver(&dts201a_driver); +#endif + return; +} + +module_init(dts201a_init); +module_exit(dts201a_exit); + +MODULE_DESCRIPTION("PARTRON dts201a thermopile sensor sysfs driver"); +MODULE_AUTHOR("PARTRON"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); diff --git a/drivers/misc/dts201a/dts201a.h b/drivers/misc/dts201a/dts201a.h new file mode 100644 index 000000000000..bd61f53c6dc5 --- /dev/null +++ b/drivers/misc/dts201a/dts201a.h @@ -0,0 +1,223 @@ +/* +* linux/sensor/dts201a.h +* +* Partron DTS201A Digital Thermopile Sensor module driver +* +* Copyright (C) 2013 Partron Co., Ltd. - Sensor Lab. +* partron (partron@partron.co.kr) +* +* Both authors are willing to be considered the contact and update points for +* the driver. +* +* 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, write to the Free Software +* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA +* 02110-1301 USA +* +*/ +/****************************************************************************** + Revision 1.0.0 2013/Nov/22: + first release + Revision 2.0. 2020/July/02: + update for coefficient read and add the emissivity caculation equation. + +******************************************************************************/ + +#ifndef __DTS201A_H__ +#define __DTS201A_H__ +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CAMERA_CCI +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cam_soc_util.h" +#endif +/** + * set to 0 1 activate or not debug from work (data interrupt/polling) + */ +#define WORK_DEBUG 0 +#if WORK_DEBUG +# define work_dbg(msg, ...)\ + printk("[D DTS201A] :" msg "\n", ##__VA_ARGS__) +#else +# define work_dbg(...) (void)0 +#endif +#define DEBUG +#ifdef DEBUG +#define dts201a_dbgmsg(str, ...) \ + pr_info("%s:" str, __func__,##__VA_ARGS__); +#else +# define dts201a_dbgmsg(...) (void)0 +#endif + + +#define dts201a_info(str, args...) \ + pr_info("%s: %d: " str "\n", __func__,__LINE__, ##args) + +#define dts201a_errmsg(str, args...) \ + pr_err("%s:%d: " str, __func__, __LINE__,##args) + +#define dts201a_wanrmsg(str, args...) \ + pr_warn("%s:%d: " str, __func__, __LINE__,##args) + +/* turn off poll log if not defined */ +#ifndef DTS201A_LOG_POLL_TIMING +# define DTS201A_LOG_POLL_TIMING 0 +#endif +/* turn off cci log timing if not defined */ +#ifndef DTS201A_LOG_CCI_TIMING +# define DTS201A_LOG_CCI_TIMING 0 +#endif +#define DTS_I2C_WRITE 0 +#define DTS_I2C_READ 1 +#define DTS201A_SADDR (0x3A) + +#define DTS201A_ID0 0x0000 +#define DTS201A_ID1 0x0000 + + + +//#define DTS201A_MAVG +//#define DTS201A_ONE_MODE + +#ifdef DTS201A_ONE_MODE +#define STX 0x1A +#define ETX 0x2E +#else /* DTS201A_TWO_MODE */ +#define STX_N1 0x1A +#define ETX_N1 0x27 +#define STX_M1 0x13 +#define ETX_M1 0x15 +#define STX_H2 0x28 +#define ETX_H2 0x35 +#define STX_M2 0x36 +#define ETX_M2 0x38 +#endif + +#define DTS_VENDOR "PARTRON" +#define DTS_CHIP_ID "DTS201A" +#define DTS_DEV_NAME "dts201a" + + +//#define DTS201A_ID0 0x3026 +//#define DTS201A_ID1 0x0010 +//#define DTS201A_ID0 0x3027 +//#define DTS201A_ID1 0x0110 +//#define DTS201A_ID0 0x3218 +//#define DTS201A_ID1 0x1110 +#define DTS_DELAY_DEFAULT 200 +#define DTS_DELAY_MINIMUM 30 +#define DTS_CONVERSION_TIME 30 + +#ifdef DTS201A_MAVG +#define DTS100A_MOVING_OAVG 5 +#define DTS100A_MOVING_AAVG 5 +#endif + +/* CONTROL REGISTERS */ +#define DTS_CUST_ID0 0x00 /* customer ID Low byte */ +#define DTS_CUST_ID1 0x01 /* customer ID High byte */ +#define DTS_SM 0xA0 /* Sensor Measurement */ +#define DTS_SM_USER 0xA1 /* Sensor Measurement User Config */ +#define DTS_SM_AZSM 0xA2 /* Auto-Zero corrected Sensor Measurement */ +#define DTS_SM_AZSM_USER 0xA3 /* Auto-Zero corrected Sensor Measurement User Config */ +#define DTS_TM 0xA4 /* Temperature Measurement */ +#define DTS_TM_USER 0xA5 /* Temperature Measurement User Config */ +#define DTS_TM_AZTM 0xA6 /* Auto-Zero corrected Temperature Measurement */ +#define DTS_TM_AZTM_USER 0xA7 /* Auto-Zero corrected Temperature Measurement User Config */ +#define DTS_START_NOM 0xA8 /* transition to normal mode */ +#define DTS_START_CM 0xA9 /* transition to command mode */ +#define DTS_MEASURE 0xAA /* measurement cycle and calculation and storage 1*/ +#define DTS_MEASURE1 0xAB /* measurement cycle and calculation and storage 1*/ +#define DTS_MEASURE2 0xAC /* measurement cycle and calculation and storage 2*/ +#define DTS_MEASURE4 0xAD /* measurement cycle and calculation and storage 4*/ +#define DTS_MEASURE8 0xAE /* measurement cycle and calculation and storage 8*/ +#define DTS_MEASURE16 0xAF /* measurement cycle and calculation and storage 16*/ + +//#define COEFFICIENT_FILE_PATH "/efs/FactoryApp/coefficient" +#define COEFFICIENT_FILE_PATH "/sdcard/Download/coefficient" + +#define VDD_VOLTAGE 1800000 +struct outputdata { +#ifdef DTS201A_MAVG + u32 therm_avg[DTS100A_MOVING_OAVG]; + u32 ambient_avg[DTS100A_MOVING_AAVG]; +#endif + u32 therm; // object temperature + u32 temp; // ambient temperature + u8 status; +}; + +#ifdef __KERNEL__ +struct dts201a_platform_data { + int irq; +}; +#endif /* __KERNEL__ */ +struct dts201a_data { + int id; /*!< multiple device id 0 based*/ + char name[64]; /*!< misc device name */ + int32_t hw_rev; /*!< hardware revision number*/ + + void *client_object; /*!< cci or i2c model i/f specific ptr */ + bool is_device_remove; /*!< true when device has been remove */ + + struct class* thermopile_class; + struct mutex lock; + struct mutex dts201a_i2c_mutex; + struct workqueue_struct *thermopile_wq; + struct work_struct work_thermopile; + struct input_dev *input_dev; + struct device *dev; + struct outputdata out; + struct hrtimer timer; + ktime_t poll_delay; + int hw_initialized; + atomic_t enabled; + u8 ndata[34]; + u8 hdata[34]; + + struct i2c_client *client; + struct regulator *i2c_power_supply; + /*!< power enable gpio number + * + * if -1 no gpio if vdd not avl pwr is not controllable + */ + int i2c_pwren_gpio; + int i2c_vdd_voltage; + bool vdd_en; +#if defined(CONFIG_DRM) + struct notifier_block fb_notif; +#endif +}; + + +int dts201a_setup(struct device *dev,struct dts201a_data *data); +int dts201a_cleanup(struct dts201a_data *ther); +int dts201a_enable(struct dts201a_data *ther); +int dts201a_disable(struct dts201a_data *ther); +#endif /* __DTS201A_H__ */ diff --git a/drivers/misc/hall/Android.mk b/drivers/misc/hall/Android.mk new file mode 100644 index 000000000000..8ef8a0f6959d --- /dev/null +++ b/drivers/misc/hall/Android.mk @@ -0,0 +1,21 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(HALL_PASSIVE_PEN),true) + KBUILD_OPTIONS += CONFIG_HALL_PASSIVE_PEN=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := hall_pen.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/sensors_class.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := hall_folio.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/hall/Kbuild b/drivers/misc/hall/Kbuild new file mode 100644 index 000000000000..84a1c7902335 --- /dev/null +++ b/drivers/misc/hall/Kbuild @@ -0,0 +1,19 @@ +KERNEL_DIR_PATH = $(shell find $(ANDROID_BUILD_TOP)/kernel/ -name "msm-*" -type d | head -1 | grep -i "kernel/msm-*") + +KERNEL_VERSION_NAME = $(shell basename $(KERNEL_DIR_PATH)) + +KERNEL_PLATFORM_PATH = $(shell find $(ANDROID_BUILD_TOP) -maxdepth 1 -name "kernel_platform" -type d) + +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +ifneq ($(filter m y,$(CONFIG_HALL_PASSIVE_PEN)),) + EXTRA_CFLAGS += -DCONFIG_HALL_PASSIVE_PEN +endif + + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers + +obj-m += hall_pen.o +obj-m += hall_folio.o diff --git a/drivers/misc/hall/Makefile b/drivers/misc/hall/Makefile new file mode 100644 index 000000000000..201497405686 --- /dev/null +++ b/drivers/misc/hall/Makefile @@ -0,0 +1,16 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS := $(M)/../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/misc/hall/hall_folio.c b/drivers/misc/hall/hall_folio.c new file mode 100644 index 000000000000..517daa623d7d --- /dev/null +++ b/drivers/misc/hall/hall_folio.c @@ -0,0 +1,294 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "hall_folio_detect" +#define LOG_DBG(fmt, args...) pr_info(DRIVER_NAME " [DBG]" "<%s:%d>"fmt, __func__, __LINE__, ##args) +#define LOG_INFO(fmt, args...) pr_info(DRIVER_NAME " [INFO]" "<%s:%d>"fmt, __func__, __LINE__, ##args) +#define LOG_ERR(fmt, args...) pr_err(DRIVER_NAME " [ERR]" "<%s:%d>"fmt, __func__, __LINE__, ##args) + +static int hall_sensor_probe(struct platform_device *pdev); +static int hall_sensor_remove(struct platform_device *pdev); + +struct hall_gpio { + char gpio_name[32]; + int gpio; + int irq; + int gpio_high_report_val; + int gpio_low_report_val; +}; + +static struct hall_sensor_str { + int status; + int enable; + int gpio_num; + int report_val; + struct regulator *hall_vdd; + struct hall_gpio *gpio_list; + spinlock_t mHallSensorLock; +}* hall_sensor_dev; + +#ifdef CONFIG_OF +static const struct of_device_id hall_pen_match[] = { + { .compatible = "hall_folio_detect", }, + {} +}; +#else +#define hall_pen_match NULL +#endif +MODULE_DEVICE_TABLE(of, hall_pen_match); + +static struct platform_driver hall_pen_driver = { + .probe = hall_sensor_probe, + .remove = hall_sensor_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = hall_pen_match, + }, +}; + +void check_and_send(void) +{ + int i; + int report_val = 0; + for (i = 0; i < hall_sensor_dev->gpio_num; i++) + { + LOG_INFO("hall report gpio%d = %d", i, gpio_get_value(hall_sensor_dev->gpio_list[i].gpio)); + if (gpio_get_value(hall_sensor_dev->gpio_list[i].gpio) > 0) + report_val |= hall_sensor_dev->gpio_list[i].gpio_high_report_val; + else if (gpio_get_value(hall_sensor_dev->gpio_list[i].gpio) == 0) + report_val |= hall_sensor_dev->gpio_list[i].gpio_low_report_val; + } + hall_sensor_dev->report_val = report_val; + LOG_DBG("hall report %d", report_val); +} + +void hall_enable(bool enable) +{ + int i; + if (enable && !hall_sensor_dev->enable) + { + LOG_INFO("hall_sensor enable"); + for (i = 0; i < hall_sensor_dev->gpio_num; i++) + { + enable_irq(hall_sensor_dev->gpio_list[i].irq); + enable_irq_wake(hall_sensor_dev->gpio_list[i].irq); + } + hall_sensor_dev->enable = 1; + check_and_send(); + } + else if (!enable && hall_sensor_dev->enable) + { + LOG_INFO("hall_sensor disable"); + for (i = 0; i < hall_sensor_dev->gpio_num; i++) + { + disable_irq(hall_sensor_dev->gpio_list[i].irq); + } + hall_sensor_dev->enable = 0; + } +} + +static ssize_t hall_enable_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + if (!strncmp(buf, "1", 1)) + { + hall_enable(true); + } + else if (!strncmp(buf, "0", 1)) + { + hall_enable(false); + } + return count; +} + +static ssize_t hall_rawdata_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", hall_sensor_dev->report_val); +} + +static struct class_attribute class_attr_enable = +__ATTR(enable, 0660, NULL, hall_enable_store); +static struct class_attribute class_attr_rawdata = +__ATTR(rawdata, 0660, hall_rawdata_show, NULL); + +static struct attribute *hall_class_attrs[] = { + &class_attr_enable.attr, + &class_attr_rawdata.attr, + NULL, +}; +ATTRIBUTE_GROUPS(hall_class); + +struct class hall_class = { + .name = "hall", + .owner = THIS_MODULE, + .class_groups = hall_class_groups, +}; + +static irqreturn_t hall_sensor_interrupt_handler(int irq, void *dev_id) +{ + unsigned long flags; + spin_lock_irqsave(&hall_sensor_dev->mHallSensorLock, flags); + LOG_DBG("hall_sensor_interrupt_handler = %d", irq); + check_and_send(); + spin_unlock_irqrestore(&hall_sensor_dev->mHallSensorLock, flags); + return IRQ_HANDLED; +} + +static int hall_sensor_probe(struct platform_device *pdev) +{ + int ret = 0; + enum of_gpio_flags flags; + struct device_node *np = pdev->dev.of_node; + int i; + + //Memory allocation + hall_sensor_dev = kzalloc(sizeof (struct hall_sensor_str), GFP_KERNEL); + if (!hall_sensor_dev) { + LOG_ERR("Memory allocation fails for hall sensor"); + ret = -ENOMEM; + goto fail_for_mem; + } + + spin_lock_init(&hall_sensor_dev->mHallSensorLock); + hall_sensor_dev->enable = 0; + //tcmd node + ret = of_property_read_string(np, "hall,factory-class-name", &hall_class.name); + ret = class_register(&hall_class); + + ret = of_property_read_u32(np,"hall,nirq-gpio-num", &hall_sensor_dev->gpio_num); + if (ret < 0) + { + LOG_ERR("GPIO for hall sensor does not exist"); + } + LOG_INFO("gpio num :%d", hall_sensor_dev->gpio_num); + hall_sensor_dev->gpio_list = kzalloc(sizeof (struct hall_gpio) * hall_sensor_dev->gpio_num, GFP_KERNEL); + if (!hall_sensor_dev->gpio_list) { + LOG_ERR("Memory allocation fails for hall sensor"); + ret = -ENOMEM; + goto fail_for_mem; + } + for (i = 0; i < hall_sensor_dev->gpio_num; i++) + { + char *gpio_name = hall_sensor_dev->gpio_list[i].gpio_name; + sprintf(gpio_name, "hall,nirq-gpio-high-val_%d", i); + ret = of_property_read_u32(np, gpio_name, &hall_sensor_dev->gpio_list[i].gpio_high_report_val); + sprintf(gpio_name, "hall,nirq-gpio-low-val_%d", i); + ret = of_property_read_u32(np, gpio_name, &hall_sensor_dev->gpio_list[i].gpio_low_report_val); + sprintf(gpio_name, "hall,nirq-gpio_%d", i); + hall_sensor_dev->gpio_list[i].gpio = of_get_named_gpio_flags(np, gpio_name, 0, &flags); + LOG_INFO("hall_sensor %s gpio = %d val = %d:%d", gpio_name, hall_sensor_dev->gpio_list[i].gpio, + hall_sensor_dev->gpio_list[i].gpio_high_report_val, + hall_sensor_dev->gpio_list[i].gpio_low_report_val); + if (gpio_is_valid(hall_sensor_dev->gpio_list[i].gpio)) + { + ret = gpio_request(hall_sensor_dev->gpio_list[i].gpio, gpio_name); + if (ret) { + LOG_ERR("Could not request %s : %d for hall sensor, ret: %d", + gpio_name, hall_sensor_dev->gpio_list[i].gpio, ret); + goto fail_for_irq; + } + gpio_direction_input(hall_sensor_dev->gpio_list[i].gpio); + + //set irq + hall_sensor_dev->gpio_list[i].irq = gpio_to_irq(hall_sensor_dev->gpio_list[i].gpio); + LOG_INFO("hall_sensor %s irq = %d, gpio = %d", gpio_name, hall_sensor_dev->gpio_list[i].irq, hall_sensor_dev->gpio_list[i].gpio); + + ret = request_threaded_irq(hall_sensor_dev->gpio_list[i].irq, NULL, hall_sensor_interrupt_handler, + IRQF_ONESHOT|IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, gpio_name, hall_sensor_dev); + if (ret) { + LOG_ERR("Could not register for hall sensor interrupt, irq = %d, ret: %d", + hall_sensor_dev->gpio_list[i].irq, ret); + hall_sensor_dev->gpio_list[i].irq = 0; + goto fail_for_irq; + } + disable_irq(hall_sensor_dev->gpio_list[i].irq); + } + } + + //enable vdd + hall_sensor_dev->hall_vdd = regulator_get(&pdev->dev, "hall_vdd"); + if (IS_ERR(hall_sensor_dev->hall_vdd)) + { + LOG_ERR("vdd error %ld", PTR_ERR(hall_sensor_dev->hall_vdd)); + goto fail_for_irq; + } + else + { + regulator_enable(hall_sensor_dev->hall_vdd); + LOG_INFO("hall_vdd regulator is %s", + regulator_is_enabled(hall_sensor_dev->hall_vdd) ? + "on" : "off"); + } + LOG_INFO("hall_sensor_probe Done"); + return 0; + + kfree(hall_sensor_dev); + hall_sensor_dev=NULL; + +fail_for_irq: + for (i = 0; i < hall_sensor_dev->gpio_num; i++) + { + if (hall_sensor_dev->gpio_list[i].irq) + free_irq(hall_sensor_dev->gpio_list[i].irq, hall_sensor_dev); + if (gpio_is_valid(hall_sensor_dev->gpio_list[i].gpio)) + gpio_free(hall_sensor_dev->gpio_list[i].gpio); + } + class_unregister(&hall_class); +fail_for_mem: + if (hall_sensor_dev->gpio_list) + kfree(hall_sensor_dev->gpio_list); + if (hall_sensor_dev) + kfree(hall_sensor_dev); + return ret; +} + +static int hall_sensor_remove(struct platform_device *pdev) +{ + int i; + regulator_disable(hall_sensor_dev->hall_vdd); + regulator_put(hall_sensor_dev->hall_vdd); + class_unregister(&hall_class); + for (i = 0; i < hall_sensor_dev->gpio_num; i++) + { + if (hall_sensor_dev->gpio_list[i].irq) + free_irq(hall_sensor_dev->gpio_list[i].irq, hall_sensor_dev); + if (gpio_is_valid(hall_sensor_dev->gpio_list[i].gpio)) + gpio_free(hall_sensor_dev->gpio_list[i].gpio); + } + if (hall_sensor_dev->gpio_list) + kfree(hall_sensor_dev->gpio_list); + if (hall_sensor_dev) + kfree(hall_sensor_dev); + + LOG_INFO("paltform rm"); + return 0; +} + +module_platform_driver(hall_pen_driver); + +MODULE_DESCRIPTION("Hall_sensor_folio Driver"); +MODULE_AUTHOR("cuijy1 "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/hall/hall_pen.c b/drivers/misc/hall/hall_pen.c new file mode 100644 index 000000000000..ba1e05bcbacc --- /dev/null +++ b/drivers/misc/hall/hall_pen.c @@ -0,0 +1,549 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HALL_PASSIVE_PEN +#include +#endif + +#define DRIVER_NAME "hall_pen_detect" +#define LOG_DBG(fmt, args...) pr_debug(DRIVER_NAME " [DBG]" "<%s:%d>"fmt, __func__, __LINE__, ##args) +#define LOG_INFO(fmt, args...) pr_info(DRIVER_NAME " [INFO]" "<%s:%d>"fmt, __func__, __LINE__, ##args) +#define LOG_ERR(fmt, args...) pr_err(DRIVER_NAME " [ERR]" "<%s:%d>"fmt, __func__, __LINE__, ##args) + +static int hall_sensor_probe(struct platform_device *pdev); +static int hall_sensor_remove(struct platform_device *pdev); + +struct hall_gpio { + char gpio_name[32]; + int gpio; + int irq; + int gpio_high_report_val; + int gpio_low_report_val; +}; + +static struct hall_sensor_str { + int status; + int enable; + int gpio_num; + int report_val; + int pen_detect; + struct regulator *hall_vdd; + struct hall_gpio *gpio_list; + spinlock_t mHallSensorLock; + struct input_dev *hall_dev; + struct sensors_classdev sensors_pen_cdev; +#ifdef CONFIG_HALL_PASSIVE_PEN + struct delayed_work hall_sensor_work; + struct delayed_work hall_sensor_dowork; + struct input_handler pen_handler; + struct input_handle pen_handle; +#endif +}* hall_sensor_dev; + +#ifdef CONFIG_OF +static const struct of_device_id hall_pen_match[] = { + { .compatible = "hall_pen_detect", }, + {} +}; +#else +#define hall_pen_match NULL +#endif +MODULE_DEVICE_TABLE(of, hall_pen_match); + +static struct platform_driver hall_pen_driver = { + .probe = hall_sensor_probe, + .remove = hall_sensor_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = hall_pen_match, + }, +}; + +#ifdef CONFIG_HALL_PASSIVE_PEN +static struct workqueue_struct *hall_sensor_wq; +static struct workqueue_struct *hall_sensor_do_wq; +static BLOCKING_NOTIFIER_HEAD(pen_detection_notifier_list); + +/** + * pen_detection_register_client - register a client notifier + * @nb: notifier block to callback on events + * + * This function registers a notifier callback function + * to pen_detection_notifier_list, which would be called when + * the passive pen is inserted or pulled out. + */ +int pen_detection_register_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&pen_detection_notifier_list, + nb); +} +EXPORT_SYMBOL(pen_detection_register_client); + +/** + * pen_detection_unregister_client - unregister a client notifier + * @nb: notifier block to callback on events + * + * This function unregisters the callback function from + * pen_detection_notifier_list. + */ +int pen_detection_unregister_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&pen_detection_notifier_list, + nb); +} +EXPORT_SYMBOL(pen_detection_unregister_client); + +/** + * pen_detection_notifier_call_chain - notify clients of pen_detection_events + * @val: event PEN_DETECTION_INSERT or PEN_DETECTION_PULL + * @v: notifier data, inculde display pen detection event. + */ +static int pen_detection_notifier_call_chain(unsigned long val, void *v) +{ + return blocking_notifier_call_chain(&pen_detection_notifier_list, val, + v); +} + +static void pen_do_work_function(struct work_struct *dat) +{ + LOG_INFO("[%s] hall_sensor_interrupt = %d\n", DRIVER_NAME,hall_sensor_dev->pen_detect); + cancel_delayed_work(&hall_sensor_dev->hall_sensor_work); + queue_delayed_work(hall_sensor_wq, &hall_sensor_dev->hall_sensor_work, 0); +} + +static void pen_report_function(struct work_struct *dat) +{ + int i; + unsigned long flags; + int report_val = 0; + for (i = 0; i < hall_sensor_dev->gpio_num; i++) + { + spin_lock_irqsave(&hall_sensor_dev->mHallSensorLock, flags); + if (gpio_get_value(hall_sensor_dev->gpio_list[i].gpio) > 0) + report_val |= hall_sensor_dev->gpio_list[i].gpio_high_report_val; + else if (gpio_get_value(hall_sensor_dev->gpio_list[i].gpio) == 0) + report_val |= hall_sensor_dev->gpio_list[i].gpio_low_report_val; + spin_unlock_irqrestore(&hall_sensor_dev->mHallSensorLock, flags); + } + if(hall_sensor_dev->report_val != report_val){ + LOG_INFO("hall report %d", report_val); + hall_sensor_dev->report_val = report_val; + if(hall_sensor_dev->report_val) + hall_sensor_dev->status = 0; + else + hall_sensor_dev->status = 1; + input_report_switch(hall_sensor_dev->hall_dev, SW_PEN_INSERTED, !hall_sensor_dev->status); + input_sync(hall_sensor_dev->hall_dev); + if(hall_sensor_dev->report_val) + pen_detection_notifier_call_chain(PEN_DETECTION_INSERT, NULL); + else + pen_detection_notifier_call_chain(PEN_DETECTION_PULL, NULL); + } +} +#endif + +void check_and_send(void) +{ + int i; + int report_val = 0; + for (i = 0; i < hall_sensor_dev->gpio_num; i++) + { + LOG_DBG("hall report gpio%d = %d", i, gpio_get_value(hall_sensor_dev->gpio_list[i].gpio)); + if (gpio_get_value(hall_sensor_dev->gpio_list[i].gpio) > 0) + report_val |= hall_sensor_dev->gpio_list[i].gpio_high_report_val; + else if (gpio_get_value(hall_sensor_dev->gpio_list[i].gpio) == 0) + report_val |= hall_sensor_dev->gpio_list[i].gpio_low_report_val; + } + if(hall_sensor_dev->pen_detect && + hall_sensor_dev->report_val != report_val){ + LOG_INFO("hall report %d", report_val); + hall_sensor_dev->report_val = report_val; + input_report_abs(hall_sensor_dev->hall_dev, ABS_DISTANCE, report_val); + input_sync(hall_sensor_dev->hall_dev); + } +} + +void hall_enable(bool enable) +{ + int i; + if (enable && !hall_sensor_dev->enable) + { + LOG_INFO("hall_sensor enable"); + for (i = 0; i < hall_sensor_dev->gpio_num; i++) + { + enable_irq(hall_sensor_dev->gpio_list[i].irq); + enable_irq_wake(hall_sensor_dev->gpio_list[i].irq); + } + hall_sensor_dev->enable = 1; + check_and_send(); + } + else if (!enable && hall_sensor_dev->enable) + { + LOG_INFO("hall_sensor disable"); + for (i = 0; i < hall_sensor_dev->gpio_num; i++) + { + disable_irq(hall_sensor_dev->gpio_list[i].irq); + disable_irq_wake(hall_sensor_dev->gpio_list[i].irq); + } + hall_sensor_dev->enable = 0; + } +} + +static int hallpen_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + hall_sensor_dev->report_val = -1; +#ifndef CONFIG_HALL_PASSIVE_PEN + hall_sensor_dev->pen_detect = enable; + hall_enable(enable); + if (enable == 0) + { + input_report_abs(hall_sensor_dev->hall_dev, ABS_DISTANCE, -1); + input_sync(hall_sensor_dev->hall_dev); + } +#endif + return 0; +} +static ssize_t hall_enable_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + if (!strncmp(buf, "1", 1)) + { + hall_enable(true); + } + else if (!strncmp(buf, "0", 1)) + { + hall_enable(false); + } + return count; +} + +static ssize_t hall_rawdata_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", hall_sensor_dev->report_val); +} + +static struct class_attribute class_attr_enable = +__ATTR(enable, 0660, NULL, hall_enable_store); +static struct class_attribute class_attr_rawdata = +__ATTR(rawdata, 0660, hall_rawdata_show, NULL); + +static struct attribute *hall_class_attrs[] = { + &class_attr_enable.attr, + &class_attr_rawdata.attr, + NULL, +}; +ATTRIBUTE_GROUPS(hall_class); + +struct class hall_class = { + .name = "hall", + .owner = THIS_MODULE, + .class_groups = hall_class_groups, +}; + +#ifdef CONFIG_HALL_PASSIVE_PEN +static struct input_device_id mID[] = { + { .driver_info = 1 }, //scan all device to match hall sensor + { }, +}; +int pen_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id){ + LOG_INFO("[%s] hall_sensor connect to handler\n", DRIVER_NAME); + return 0; +} + +void pen_event(struct input_handle *handle, unsigned int type, unsigned int code, int value){ + if(type==EV_SW && code==SW_PEN_INSERTED ){ + if(value != 2 && !!test_bit(code, hall_sensor_dev->hall_dev->sw) != !hall_sensor_dev->status){ + __change_bit(code, hall_sensor_dev->hall_dev->sw); + LOG_INFO("[%s] reset dev->sw=%d \n", DRIVER_NAME,!hall_sensor_dev->status); + } + } +} + +bool pen_match(struct input_handler *handler, struct input_dev *dev){ + if( (dev->name && handler->name) && + !strcmp(dev->name,"pen_input") && + !strcmp(handler->name,"pen_input_handler")) + return true; + + return false; +} + +static irqreturn_t hall_sensor_interrupt_handler(int irq, void *dev_id) +{ + LOG_DBG("hall_sensor_interrupt_handler = %d", irq); + queue_delayed_work(hall_sensor_do_wq, &hall_sensor_dev->hall_sensor_dowork, msecs_to_jiffies(0)); + return IRQ_HANDLED; +} +#else +static irqreturn_t hall_sensor_interrupt_handler(int irq, void *dev_id) +{ + unsigned long flags; + spin_lock_irqsave(&hall_sensor_dev->mHallSensorLock, flags); + LOG_DBG("hall_sensor_interrupt_handler = %d", irq); + check_and_send(); + spin_unlock_irqrestore(&hall_sensor_dev->mHallSensorLock, flags); + return IRQ_HANDLED; +} +#endif + +static int hall_sensor_probe(struct platform_device *pdev) +{ + int ret = 0; + enum of_gpio_flags flags; + struct device_node *np = pdev->dev.of_node; + int i; + const char *name_temp; + //struct totalHallInformation *pHallInformationData = NULL; + + //Memory allocation + hall_sensor_dev = kzalloc(sizeof (struct hall_sensor_str), GFP_KERNEL); + if (!hall_sensor_dev) { + LOG_ERR("Memory allocation fails for hall sensor"); + ret = -ENOMEM; + goto fail_for_mem; + } + + spin_lock_init(&hall_sensor_dev->mHallSensorLock); +#ifdef CONFIG_HALL_PASSIVE_PEN + hall_sensor_dev->enable = 1; + hall_sensor_dev->pen_detect = 1; +#else + hall_sensor_dev->enable = 0; + hall_sensor_dev->pen_detect = 0; +#endif + //tcmd node + ret = of_property_read_string(np, "hall,factory-class-name", &hall_class.name); + ret = class_register(&hall_class); + ret = of_property_read_string(np, "hall,input-dev-name", &name_temp); + LOG_INFO("hall_input num :%s", name_temp); + + ret = of_property_read_u32(np,"hall,nirq-gpio-num", &hall_sensor_dev->gpio_num); + if (ret < 0) + { + LOG_ERR("GPIO for hall sensor does not exist"); + } + LOG_INFO("gpio num :%d", hall_sensor_dev->gpio_num); + + hall_sensor_dev->hall_dev = input_allocate_device(); + if (!hall_sensor_dev->hall_dev){ + LOG_ERR(" hall_indev allocation fails\n" ); + return -ENOMEM; + } + hall_sensor_dev->hall_dev->name = name_temp; + hall_sensor_dev->hall_dev->dev.parent= NULL; + LOG_INFO("hall_ num :%s", hall_sensor_dev->hall_dev->name); +#ifdef CONFIG_HALL_PASSIVE_PEN + /* Set all the keycodes */ + hall_sensor_dev->hall_dev->phys= "/dev/input/pen_detect"; + input_set_capability(hall_sensor_dev->hall_dev, EV_SW, SW_PEN_INSERTED); +#else + __set_bit(EV_ABS, hall_sensor_dev->hall_dev->evbit); + input_set_abs_params(hall_sensor_dev->hall_dev, ABS_DISTANCE, -1, 100, 0, 0); +#endif + + ret = input_register_device(hall_sensor_dev->hall_dev); + if (ret) { + LOG_ERR("[%s] input registration fails\n", DRIVER_NAME); + ret = -1; + goto exit_input_free; + } +#ifdef CONFIG_HALL_PASSIVE_PEN + hall_sensor_dev->pen_handler.match=pen_match; + hall_sensor_dev->pen_handler.connect=pen_connect; + hall_sensor_dev->pen_handler.event=pen_event; + hall_sensor_dev->pen_handler.name="pen_detect_handler"; + hall_sensor_dev->pen_handler.id_table = mID; + ret = input_register_handler(& hall_sensor_dev->pen_handler); + if(ret){ + LOG_ERR("[%s] handler registration fails\n", DRIVER_NAME); + ret = -1; + goto exit_unregister_input_dev; + } + hall_sensor_dev->pen_handle.name="pen_detect_handle"; + hall_sensor_dev->pen_handle.open=1; //receive any event from hall sensor + hall_sensor_dev->pen_handle.dev=hall_sensor_dev->hall_dev; + hall_sensor_dev->pen_handle.handler=&hall_sensor_dev->pen_handler; + ret = input_register_handle(& hall_sensor_dev->pen_handle); + if(ret){ + LOG_ERR("[%s] handle registration fails\n", DRIVER_NAME); + ret = -1; + goto exit_unregister_handler; + } +#else + input_report_abs(hall_sensor_dev->hall_dev, ABS_DISTANCE, -1); + input_sync(hall_sensor_dev->hall_dev); +#endif + + hall_sensor_dev->sensors_pen_cdev.sensors_enable = hallpen_enable; + hall_sensor_dev->sensors_pen_cdev.sensors_poll_delay = NULL; + hall_sensor_dev->sensors_pen_cdev.name = hall_sensor_dev->hall_dev->name; + hall_sensor_dev->sensors_pen_cdev.vendor = "motorola"; + hall_sensor_dev->sensors_pen_cdev.version = 1; + hall_sensor_dev->sensors_pen_cdev.type = SENSOR_TYPE_MOTO_HALL; + hall_sensor_dev->sensors_pen_cdev.max_range = "5"; + hall_sensor_dev->sensors_pen_cdev.resolution = "5.0"; + hall_sensor_dev->sensors_pen_cdev.sensor_power = "0.005"; + hall_sensor_dev->sensors_pen_cdev.min_delay = 0; + hall_sensor_dev->sensors_pen_cdev.fifo_reserved_event_count = 0; + hall_sensor_dev->sensors_pen_cdev.fifo_max_event_count = 0; + hall_sensor_dev->sensors_pen_cdev.delay_msec = 100; + hall_sensor_dev->sensors_pen_cdev.enabled = 0; + + ret = sensors_classdev_register(&hall_sensor_dev->hall_dev->dev, &hall_sensor_dev->sensors_pen_cdev); + if (ret < 0) + LOG_ERR("create cap sensor_class file failed (%d)\n", ret); + + hall_sensor_dev->gpio_list = kzalloc(sizeof (struct hall_gpio) * hall_sensor_dev->gpio_num, GFP_KERNEL); + if (!hall_sensor_dev->gpio_list) { + LOG_ERR("Memory allocation fails for hall sensor"); + ret = -ENOMEM; + goto fail_for_mem; + } + +#ifdef CONFIG_HALL_PASSIVE_PEN + hall_sensor_wq = create_singlethread_workqueue("hall_sensor_wq"); + hall_sensor_do_wq = create_singlethread_workqueue("hall_sensor_do_wq"); + INIT_DELAYED_WORK(&hall_sensor_dev->hall_sensor_work, pen_report_function); + INIT_DELAYED_WORK(&hall_sensor_dev->hall_sensor_dowork, pen_do_work_function); + queue_delayed_work(hall_sensor_do_wq, &hall_sensor_dev->hall_sensor_dowork, 0); +#endif + + for (i = 0; i < hall_sensor_dev->gpio_num; i++) + { + char *gpio_name = hall_sensor_dev->gpio_list[i].gpio_name; + sprintf(gpio_name, "hall,nirq-gpio-high-val_%d", i); + ret = of_property_read_u32(np, gpio_name, &hall_sensor_dev->gpio_list[i].gpio_high_report_val); + sprintf(gpio_name, "hall,nirq-gpio-low-val_%d", i); + ret = of_property_read_u32(np, gpio_name, &hall_sensor_dev->gpio_list[i].gpio_low_report_val); + sprintf(gpio_name, "hall,nirq-gpio_%d", i); + hall_sensor_dev->gpio_list[i].gpio = of_get_named_gpio_flags(np, gpio_name, 0, &flags); + LOG_INFO("hall_sensor %s gpio = %d val = %d:%d", gpio_name, hall_sensor_dev->gpio_list[i].gpio, + hall_sensor_dev->gpio_list[i].gpio_high_report_val, + hall_sensor_dev->gpio_list[i].gpio_low_report_val); + if (gpio_is_valid(hall_sensor_dev->gpio_list[i].gpio)) + { + ret = gpio_request(hall_sensor_dev->gpio_list[i].gpio, gpio_name); + if (ret) { + LOG_ERR("Could not request %s : %d for hall sensor, ret: %d", + gpio_name, hall_sensor_dev->gpio_list[i].gpio, ret); + goto fail_for_irq; + } + gpio_direction_input(hall_sensor_dev->gpio_list[i].gpio); + + //set irq + hall_sensor_dev->gpio_list[i].irq = gpio_to_irq(hall_sensor_dev->gpio_list[i].gpio); + LOG_INFO("hall_sensor %s irq = %d, gpio = %d", gpio_name, hall_sensor_dev->gpio_list[i].irq, hall_sensor_dev->gpio_list[i].gpio); + + ret = request_threaded_irq(hall_sensor_dev->gpio_list[i].irq, NULL, hall_sensor_interrupt_handler, + IRQF_ONESHOT|IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING, gpio_name, hall_sensor_dev); + if (ret) { + LOG_ERR("Could not register for hall sensor interrupt, irq = %d, ret: %d", + hall_sensor_dev->gpio_list[i].irq, ret); + hall_sensor_dev->gpio_list[i].irq = 0; + goto fail_for_irq; + } +#ifndef CONFIG_HALL_PASSIVE_PEN + disable_irq(hall_sensor_dev->gpio_list[i].irq); +#endif + } + } + + //enable vdd + hall_sensor_dev->hall_vdd = regulator_get(&pdev->dev, "hall_vdd"); + if (IS_ERR(hall_sensor_dev->hall_vdd)) + { + LOG_ERR("vdd error %ld", PTR_ERR(hall_sensor_dev->hall_vdd)); + goto fail_for_irq; + } + else + { + regulator_enable(hall_sensor_dev->hall_vdd); + LOG_INFO("hall_vdd regulator is %s", + regulator_is_enabled(hall_sensor_dev->hall_vdd) ? + "on" : "off"); + } + LOG_INFO("hall_sensor_probe Done"); + return 0; + + kfree(hall_sensor_dev); + hall_sensor_dev=NULL; + +fail_for_irq: + for (i = 0; i < hall_sensor_dev->gpio_num; i++) + { + if (hall_sensor_dev->gpio_list[i].irq) + free_irq(hall_sensor_dev->gpio_list[i].irq, hall_sensor_dev); + if (gpio_is_valid(hall_sensor_dev->gpio_list[i].gpio)) + gpio_free(hall_sensor_dev->gpio_list[i].gpio); + } + class_unregister(&hall_class); +fail_for_mem: + if (hall_sensor_dev->gpio_list) + kfree(hall_sensor_dev->gpio_list); + if (hall_sensor_dev) + kfree(hall_sensor_dev); +#ifdef CONFIG_HALL_PASSIVE_PEN +exit_unregister_handler: + input_unregister_handler(& hall_sensor_dev->pen_handler); +exit_unregister_input_dev: + input_unregister_device(hall_sensor_dev->hall_dev); +#endif +exit_input_free: + input_free_device(hall_sensor_dev->hall_dev); + hall_sensor_dev->hall_dev = NULL; + return ret; +} + +static int hall_sensor_remove(struct platform_device *pdev) +{ + int i; + regulator_disable(hall_sensor_dev->hall_vdd); + regulator_put(hall_sensor_dev->hall_vdd); + class_unregister(&hall_class); + for (i = 0; i < hall_sensor_dev->gpio_num; i++) + { + if (hall_sensor_dev->gpio_list[i].irq) + free_irq(hall_sensor_dev->gpio_list[i].irq, hall_sensor_dev); + if (gpio_is_valid(hall_sensor_dev->gpio_list[i].gpio)) + gpio_free(hall_sensor_dev->gpio_list[i].gpio); + } + if (hall_sensor_dev->gpio_list) + kfree(hall_sensor_dev->gpio_list); + if (hall_sensor_dev) + kfree(hall_sensor_dev); + + sensors_classdev_unregister(&hall_sensor_dev->sensors_pen_cdev); + input_unregister_device(hall_sensor_dev->hall_dev); + LOG_INFO("paltform rm"); + return 0; +} + +module_platform_driver(hall_pen_driver); + +MODULE_DESCRIPTION("Hall_sensor_pen Driver"); +MODULE_AUTHOR("cuijy1 "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/ldo_vibrator_mmi/Android.mk b/drivers/misc/ldo_vibrator_mmi/Android.mk new file mode 100644 index 000000000000..5f159d20b029 --- /dev/null +++ b/drivers/misc/ldo_vibrator_mmi/Android.mk @@ -0,0 +1,15 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(VIBRATOR_NOISE_CAMERA), true) + KBUILD_OPTIONS += CONFIG_VIBRATOR_NOISE_CAMERA=y +endif + +KBUILD_OPTIONS += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) + +include $(CLEAR_VARS) +LOCAL_MODULE := ldo_vibrator_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/misc/ldo_vibrator_mmi/Kbuild b/drivers/misc/ldo_vibrator_mmi/Kbuild new file mode 100644 index 000000000000..ef98f416cd93 --- /dev/null +++ b/drivers/misc/ldo_vibrator_mmi/Kbuild @@ -0,0 +1,12 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +ifneq ($(filter m y,$(CONFIG_VIBRATOR_NOISE_CAMERA)),) + EXTRA_CFLAGS += -DCONFIG_VIBRATOR_NOISE_CAMERA +endif + +ldo_vibrator_mmi-y += moto_ldo_vibrator.o + +obj-m += ldo_vibrator_mmi.o + diff --git a/drivers/misc/ldo_vibrator_mmi/Makefile b/drivers/misc/ldo_vibrator_mmi/Makefile new file mode 100644 index 000000000000..7e1a4d16963f --- /dev/null +++ b/drivers/misc/ldo_vibrator_mmi/Makefile @@ -0,0 +1,28 @@ +ifeq ($(strip $(MODULE_KERNEL_VERSION)), 5.15) + +all: modules + +modules: + $(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 +else +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean +endif diff --git a/drivers/misc/ldo_vibrator_mmi/moto_ldo_vibrator.c b/drivers/misc/ldo_vibrator_mmi/moto_ldo_vibrator.c new file mode 100644 index 000000000000..62ce6a95e510 --- /dev/null +++ b/drivers/misc/ldo_vibrator_mmi/moto_ldo_vibrator.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + * Define vibration periods: default(5sec), min(50ms), max(15sec)). + */ +#define MOTO_VIB_MIN_PLAY_MS 50 +#define MOTO_VIB_PLAY_MS 5000 +#define MOTO_VIB_MAX_PLAY_MS 15000 + +#ifdef CONFIG_VIBRATOR_NOISE_CAMERA +extern int mot_actuator_on_vibrate_start(void); +extern int mot_actuator_on_vibrate_stop(void); +#endif + +struct vib_ldo_chip { + struct led_classdev cdev; + struct mutex lock; + struct hrtimer stop_timer; + struct work_struct vib_work; + + int state; + int en_gpio; + u64 vib_play_ms; + bool vib_enabled; +}; + + +static inline int moto_vib_ldo_enable(struct vib_ldo_chip *chip, bool enable) +{ + int ret; + +#ifdef CONFIG_VIBRATOR_NOISE_CAMERA + static int mot_actuator_started = 0; +#endif + + if (chip->vib_enabled == enable) + return 0; + + pr_debug("moto_vib_ldo_enable enable = %d\n", enable); + + if (enable) { + ret = gpio_direction_output(chip->en_gpio, 1); + if (ret < 0) + pr_err(" %s : ldo en gpio enable failed \n", __func__); + chip->vib_enabled = true; + } + else { + ret = gpio_direction_output(chip->en_gpio, 0); + if (ret < 0) + pr_err(" %s : ldo en gpio disable failed \n", __func__); + chip->vib_enabled = false; + } + +#ifdef CONFIG_VIBRATOR_NOISE_CAMERA + if ((chip->vib_play_ms > 70) && (enable)) { + mot_actuator_on_vibrate_start(); + mot_actuator_started = 1; + } + if(!(enable) && (mot_actuator_started == 1 )) { + mot_actuator_on_vibrate_stop(); + mot_actuator_started = 0; + } + pr_debug("moto_vib_ldo_enable camera done\n"); +#endif + + return ret; +} + +static void moto_vib_work(struct work_struct *work) +{ + struct vib_ldo_chip *chip = container_of(work, struct vib_ldo_chip, + vib_work); + int ret = 0; + + if (chip->state) { + if (!chip->vib_enabled) + ret = moto_vib_ldo_enable(chip, true); + + if (ret == 0) + hrtimer_start(&chip->stop_timer, + ms_to_ktime(chip->vib_play_ms), + HRTIMER_MODE_REL); + } else { + moto_vib_ldo_enable(chip, false); + } +} + +static enum hrtimer_restart vib_stop_timer(struct hrtimer *timer) +{ + struct vib_ldo_chip *chip = container_of(timer, struct vib_ldo_chip, + stop_timer); + + chip->state = 0; + schedule_work(&chip->vib_work); + return HRTIMER_NORESTART; +} + +static ssize_t moto_vib_show_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip, + cdev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", chip->vib_enabled); +} + +static ssize_t moto_vib_store_state(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + /* At present, nothing to do with setting state */ + return count; +} + +static ssize_t moto_vib_show_duration(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip, + cdev); + ktime_t time_rem; + s64 time_ms = 0; + + if (hrtimer_active(&chip->stop_timer)) { + time_rem = hrtimer_get_remaining(&chip->stop_timer); + time_ms = ktime_to_ms(time_rem); + } + + return scnprintf(buf, PAGE_SIZE, "%lld\n", time_ms); +} + +static ssize_t moto_vib_store_duration(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip, + cdev); + u32 val; + int ret; + + ret = kstrtouint(buf, 0, &val); + if (ret < 0) + return ret; + + /* setting 0 on duration is NOP for now */ + if (val <= 0) + return count; + + if (val < MOTO_VIB_MIN_PLAY_MS) + val = MOTO_VIB_MIN_PLAY_MS; + + if (val > MOTO_VIB_MAX_PLAY_MS) + val = MOTO_VIB_MAX_PLAY_MS; + + mutex_lock(&chip->lock); + chip->vib_play_ms = val; + mutex_unlock(&chip->lock); + + return count; +} + +static ssize_t moto_vib_show_activate(struct device *dev, + struct device_attribute *attr, char *buf) +{ + /* For now nothing to show */ + return scnprintf(buf, PAGE_SIZE, "%d\n", 0); +} + +static ssize_t moto_vib_store_activate(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip, + cdev); + u32 val; + int ret; + + ret = kstrtouint(buf, 0, &val); + if (ret < 0) + return ret; + + if (val != 0 && val != 1) + return count; + + mutex_lock(&chip->lock); + hrtimer_cancel(&chip->stop_timer); + chip->state = val; + pr_debug("state = %d, time = %llums\n", chip->state, chip->vib_play_ms); + mutex_unlock(&chip->lock); + schedule_work(&chip->vib_work); + + return count; +} + +static struct device_attribute moto_vib_attrs[] = { + __ATTR(state, 0664, moto_vib_show_state, moto_vib_store_state), + __ATTR(duration, 0664, moto_vib_show_duration, moto_vib_store_duration), + __ATTR(activate, 0664, moto_vib_show_activate, moto_vib_store_activate), +}; + +static int moto_vib_hw_init(struct device *dev, struct vib_ldo_chip *chip) +{ + int ret = 0; + + chip->en_gpio = of_get_named_gpio(dev->of_node, "moto,vib-ldo-gpio", 0); + + if (!gpio_is_valid(chip->en_gpio)) { + ret = -ENODEV; + pr_err("moto,vib-ldo-gpio is invalid, ret=%d\n",ret); + return ret; + } + else { + ret = gpio_request(chip->en_gpio, "vib-ldo-gpio"); + if (ret < 0) { + pr_err(" %s : gpio_requset vib-ldo-gpio pin failed \n", __func__); + ret = -ENODEV; + return ret; + } + ret = gpio_direction_output(chip->en_gpio, 0); + if (ret < 0) { + pr_err(" %s : gpio set defalut status failed \n", __func__); + ret = -ENODEV; + goto err_en_gpio; + } + } + return ret; + +err_en_gpio: + if (gpio_is_valid(chip->en_gpio) ) + gpio_free(chip->en_gpio); + + return ret; +} + +/* Dummy functions for brightness */ +static enum led_brightness moto_vib_brightness_get(struct led_classdev *cdev) +{ + return 0; +} + +static void moto_vib_brightness_set(struct led_classdev *cdev, + enum led_brightness level) +{ +} + +static int moto_vibrator_ldo_suspend(struct device *dev) +{ + struct vib_ldo_chip *chip = dev_get_drvdata(dev); + + mutex_lock(&chip->lock); + + hrtimer_cancel(&chip->stop_timer); + cancel_work_sync(&chip->vib_work); + moto_vib_ldo_enable(chip, false); + mutex_unlock(&chip->lock); + + return 0; +} +static SIMPLE_DEV_PM_OPS(moto_vibrator_ldo_pm_ops, moto_vibrator_ldo_suspend, + NULL); + +static int moto_vibrator_ldo_probe(struct platform_device *pdev) +{ + //struct device_node *of_node = pdev->dev.of_node; + struct vib_ldo_chip *chip; + int i, ret = 0; + pr_info("Vibrator LDO successfully registered: ret = %d\n",ret); + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + + ret = moto_vib_hw_init(&pdev->dev, chip); + if (ret < 0) { + pr_err("couldn't init hw resource, ret=%d\n", ret); + return ret; + } + + chip->vib_play_ms = MOTO_VIB_PLAY_MS; + mutex_init(&chip->lock); + INIT_WORK(&chip->vib_work, moto_vib_work); + + hrtimer_init(&chip->stop_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + chip->stop_timer.function = vib_stop_timer; + dev_set_drvdata(&pdev->dev, chip); + + chip->cdev.name = "vibrator"; + chip->cdev.brightness_get = moto_vib_brightness_get; + chip->cdev.brightness_set = moto_vib_brightness_set; + chip->cdev.max_brightness = 100; + ret = devm_led_classdev_register(&pdev->dev, &chip->cdev); + if (ret < 0) { + pr_err("Error in registering led class device, ret=%d\n", ret); + goto fail; + } + + for (i = 0; i < ARRAY_SIZE(moto_vib_attrs); i++) { + ret = sysfs_create_file(&chip->cdev.dev->kobj, + &moto_vib_attrs[i].attr); + if (ret < 0) { + dev_err(&pdev->dev, "Error in creating sysfs file, ret=%d\n", + ret); + goto sysfs_fail; + } + } +#ifdef CONFIG_LEDS_TRIGGER_TRANSIENT + /* + * Android has a habit of trying to set the vibrator trigger to "transient", + * which destroys our own "activate", "duration", and "state" attributes. + * Remove the "trigger" attribute provided by leds_class so that no one can + * change the trigger of the vibrator LED device. + * + * Luckily, `sysfs_remove_file` only uses the `name` field, so we can use a + * compound literal instead of having to find the proper attribute struct + */ + sysfs_remove_file(&chip->cdev.dev->kobj, + &((struct attribute){.name = "trigger"})); +#endif + + pr_info("Vibrator LDO successfully registered: gpio = %d\n",chip->en_gpio); + return 0; + +sysfs_fail: + for (--i; i >= 0; i--) + sysfs_remove_file(&chip->cdev.dev->kobj, + &moto_vib_attrs[i].attr); +fail: + mutex_destroy(&chip->lock); + dev_set_drvdata(&pdev->dev, NULL); + if (gpio_is_valid(chip->en_gpio) ) + gpio_free(chip->en_gpio); + return ret; +} + +static int moto_vibrator_ldo_remove(struct platform_device *pdev) +{ + struct vib_ldo_chip *chip = dev_get_drvdata(&pdev->dev); + + hrtimer_cancel(&chip->stop_timer); + cancel_work_sync(&chip->vib_work); + mutex_destroy(&chip->lock); + dev_set_drvdata(&pdev->dev, NULL); + if (gpio_is_valid(chip->en_gpio) ) + gpio_free(chip->en_gpio); + return 0; +} + +static const struct of_device_id vibrator_ldo_match_table[] = { + { .compatible = "moto,vibrator-ldo" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, vibrator_ldo_match_table); + +static struct platform_driver moto_vibrator_ldo_driver = { + .driver = { + .name = "moto-vibrator-ldo", + .of_match_table = vibrator_ldo_match_table, + .pm = &moto_vibrator_ldo_pm_ops, + }, + .probe = moto_vibrator_ldo_probe, + .remove = moto_vibrator_ldo_remove, +}; +module_platform_driver(moto_vibrator_ldo_driver); + +MODULE_DESCRIPTION("MOTO LDO-Vibrator driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mmi_sigprint/Android.mk b/drivers/misc/mmi_sigprint/Android.mk new file mode 100644 index 000000000000..b296d6d1fa3f --- /dev/null +++ b/drivers/misc/mmi_sigprint/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_sigprint.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/mmi_sigprint/Kbuild b/drivers/misc/mmi_sigprint/Kbuild new file mode 100644 index 000000000000..527d0071a46e --- /dev/null +++ b/drivers/misc/mmi_sigprint/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m = mmi_sigprint.o diff --git a/drivers/misc/mmi_sigprint/Makefile b/drivers/misc/mmi_sigprint/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/misc/mmi_sigprint/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/misc/mmi_sigprint/mmi_sigprint.c b/drivers/misc/mmi_sigprint/mmi_sigprint.c new file mode 100644 index 000000000000..50afaa2bc427 --- /dev/null +++ b/drivers/misc/mmi_sigprint/mmi_sigprint.c @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2020 Motorola Mobility Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#define CREATE_TRACE_POINTS +#include + +static struct tracepoint * tracepoint_signal_generate = NULL; +#define STORE_SIGINFO(_errno, _code, info) \ + do { \ + if (info == SEND_SIG_NOINFO || \ + info == SEND_SIG_FORCED) { \ + _errno = 0; \ + _code = SI_USER; \ + } else if (info == SEND_SIG_PRIV) { \ + _errno = 0; \ + _code = SI_KERNEL; \ + } else { \ + _errno = info->si_errno; \ + _code = info->si_code; \ + } \ + } while (0) + +#ifndef TASK_STATE_TO_CHAR_STR +#define TASK_STATE_TO_CHAR_STR "RSDTtXZxKWPNn" +#endif + +static const char stat_nam[] = TASK_STATE_TO_CHAR_STR; + +typedef unsigned long(*kallsyms_lookup_name_func_t)(const char *name); +static kallsyms_lookup_name_func_t kallsyms_lookup_name_func; + +/* + * This portion of the code can be used as template if user want to add specific + * signals to debug msg. + */ + +/* +static const char * const signal_deliver_results[] = { + "delivered", + "ignored", + "already_pending", + "overflow_fail", + "lost_info", +}; + +static void probe_signal_generate(void *ignore, int sig, struct siginfo *info, + struct task_struct *task, int group, int result) +{ + unsigned state = task->state ? __ffs(task->state) + 1 : 0; + int errno, code; + + // only log delivered signals + STORE_SIGINFO(errno, code, info); + pr_info("[signal][%d:%s]generate sig %d to [%d:%s:%c] errno=%d code=%d grp=%d res=%s\n", + current->pid, current->comm, sig, + task->pid, task->comm, + state < sizeof(stat_nam) - 1 ? stat_nam[state] : '?', + errno, code, group, signal_deliver_results[result]); +} + +static void probe_signal_deliver(void *ignore, int sig, struct siginfo *info, + struct k_sigaction *ka) +{ + int errno, code; + + STORE_SIGINFO(errno, code, info); + pr_info("[signal]sig %d delivered to [%d:%s] errno=%d code=%d sa_handler=%lx sa_flags=%lx\n", + sig, current->pid, current->comm, errno, code, + (unsigned long)ka->sa.sa_handler, ka->sa.sa_flags); +} +*/ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) +static void probe_death_signal(void *ignore, int sig, struct siginfo *info, + struct task_struct *task, int _group, int result) +#else +static void probe_death_signal(void *ignore, int sig, struct kernel_siginfo *info, + struct task_struct *task, int _group, int result) +#endif +{ + struct signal_struct *signal = task->signal; + unsigned int state; + int group; + + /* + * all action will cause process coredump or terminate + * kernel log reduction: only print delivered signals + */ + if (sig_fatal(task, sig) && result == TRACE_SIGNAL_DELIVERED) { + signal = task->signal; + group = _group || + (signal->flags & (SIGNAL_GROUP_EXIT | SIGNAL_GROUP_COREDUMP)); + + /* + * kernel log reduction + * skip SIGRTMIN because it's used as timer signal + * skip if the target thread is already dead + */ + if (sig == SIGRTMIN || + (task->state & (TASK_DEAD | EXIT_DEAD | EXIT_ZOMBIE))) + return; + /* + * Global init gets no signals it doesn't want. + * Container-init gets no signals it doesn't want from same + * container. + * + * Note that if global/container-init sees a sig_kernel_only() + * signal here, the signal must have been generated internally + * or must have come from an ancestor namespace. In either + * case, the signal cannot be dropped. + */ + if (unlikely(signal->flags & SIGNAL_UNKILLABLE) && + !sig_kernel_only(sig)) + return; + + /* + * kernel log reduction + * only print process instead of all threads + */ + if (group && (task != task->group_leader)) + return; + + state = task->state ? __ffs(task->state) + 1 : 0; + pr_info("[signal][%d:%s]send death sig %d to[%d:%s:%c]\n", + current->pid, current->comm, + sig, task->pid, task->comm, + state < sizeof(stat_nam) - 1 ? stat_nam[state] : '?'); + } else if ((sig_kernel_stop(sig) && result == TRACE_SIGNAL_DELIVERED) || + sig == SIGCONT) { + + /* + * kernel log reduction + * only print process instead of all threads + */ + if (_group && (task != task->group_leader)) + return; + + state = task->state ? __ffs(task->state) + 1 : 0; + pr_info("[signal][%d:%s]send %s sig %d to[%d:%s:%c]\n", + current->pid, current->comm, + (sig == SIGCONT) ? "continue" : "stop", + sig, task->pid, task->comm, + state < sizeof(stat_nam) - 1 ? stat_nam[state] : '?'); + } +} + +static int __init init_signal_log(void) +{ + int ret = -1; + struct kprobe kp = { + .symbol_name = "kallsyms_lookup_name", + }; + + ret = register_kprobe(&kp); + if (ret < 0) { + pr_err("Failed to register kprobe, %d\n", ret); + return ret; + } + unregister_kprobe(&kp); + kallsyms_lookup_name_func = (kallsyms_lookup_name_func_t)kp.addr; + if (!kallsyms_lookup_name_func) { + pr_err("Failed to get kallsyms_lookup_name address\n"); + return -ENXIO; + } + //register_trace_signal_generate(probe_death_signal, NULL); + tracepoint_signal_generate = (struct tracepoint *)kallsyms_lookup_name_func("__tracepoint_signal_generate"); + if (!tracepoint_signal_generate) { + pr_err("Failed to get __tracepoint_signal_generate address\n"); + return -ENXIO; + } + tracepoint_probe_register(tracepoint_signal_generate, probe_death_signal, NULL); + + // Code example if extra debug msg needed to add. + /* + register_trace_signal_deliver(probe_signal_deliver, NULL); + register_trace_signal_generate(probe_signal_generate, NULL); + */ + return 0; +} + +static void exit_signal_log(void) +{ + if (tracepoint_signal_generate) + tracepoint_probe_unregister(tracepoint_signal_generate, probe_death_signal, NULL); +} + +module_init(init_signal_log); +module_exit(exit_signal_log); + +MODULE_DESCRIPTION("Motorola death signal debug"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mmi_sys_temp/Android.mk b/drivers/misc/mmi_sys_temp/Android.mk new file mode 100644 index 000000000000..20db57e05efb --- /dev/null +++ b/drivers/misc/mmi_sys_temp/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_sys_temp.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/mmi_sys_temp/Kbuild b/drivers/misc/mmi_sys_temp/Kbuild new file mode 100644 index 000000000000..d676833a3d63 --- /dev/null +++ b/drivers/misc/mmi_sys_temp/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += mmi_sys_temp.o diff --git a/drivers/misc/mmi_sys_temp/Makefile b/drivers/misc/mmi_sys_temp/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/misc/mmi_sys_temp/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/misc/mmi_sys_temp/mmi_sys_temp.c b/drivers/misc/mmi_sys_temp/mmi_sys_temp.c new file mode 100644 index 000000000000..0e5ce97d481b --- /dev/null +++ b/drivers/misc/mmi_sys_temp/mmi_sys_temp.c @@ -0,0 +1,482 @@ +/* + * Copyright (C) 2015, 2018 Motorola Mobility LLC + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TEMP_NODE_SENSOR_NAMES "mmi,temperature-names" +#define SENSOR_LISTENER_NAMES "mmi,sensor-listener-names" +#define NEED_UEVENT_SENSOR_NAMES "mmi,need-uevent-sensors" +#define UEVENT_TEMP_THRESHOLD "mmi,uevent-temp-threshold" +#define DEFAULT_UEVENT_TEMP_THRESHOLD 40000 + +#define DEFAULT_TEMPERATURE 0 + +struct mmi_sys_temp_sensor { + struct thermal_zone_device *tz_dev; + const char *name; + int temp; + bool need_uevent; + int pre_temp; +}; + +struct mmi_sys_temp_dev { + struct platform_device *pdev; + int num_sensors; + int num_sensors_listener; + int uevent_temp_threshold; + struct mmi_sys_temp_sensor *sensor; + struct mmi_sys_temp_sensor *sensor_listener; + struct notifier_block psy_nb; + struct work_struct psy_changed_work; +}; + +static struct mmi_sys_temp_dev *sys_temp_dev; + +static int uevent_generate(struct mmi_sys_temp_dev *data, int index) +{ + struct kobj_uevent_env *env; + int ret; + + if ((!data->sensor[index].need_uevent) || + (data->sensor[index].temp < data->uevent_temp_threshold)) + return 0; + + //compare unit C + if ((data->sensor[index].temp / 1000) == (data->sensor[index].pre_temp / 1000)) + return 0; + + env = devm_kzalloc(&data->pdev->dev, sizeof(*env), GFP_KERNEL); + if (!env) { + dev_err(&data->pdev->dev, "%s: alloc uevent error\n", __func__); + return -1; + } + + data->sensor[index].pre_temp = data->sensor[index].temp; + + add_uevent_var(env, "NAME=%s", data->sensor[index].name); + add_uevent_var(env, "TEMP=%d", data->sensor[index].temp); + add_uevent_var(env, "TRIP=%d", 0); + + ret = kobject_uevent_env(&data->sensor[index].tz_dev->device.kobj, KOBJ_CHANGE, env->envp); + + devm_kfree(&data->pdev->dev, env); + + dev_info(&data->pdev->dev, "trigger uevent index %i, temp %d pre_temp %d", + index, data->sensor[index].temp, data->sensor[index].pre_temp); + + return ret; +} + +static int uevent_parse_dt(struct mmi_sys_temp_dev *data, struct device_node *node, int num_sensors) +{ + const char* name; + int num_need_uevent_sensors, i; + int ret = 0; + + num_need_uevent_sensors = of_property_count_strings(node, NEED_UEVENT_SENSOR_NAMES); + if (num_need_uevent_sensors <= 0) { + dev_info(&data->pdev->dev, "No sensor need uevent\n"); + return ret; + } + + if (of_property_read_u32(node, UEVENT_TEMP_THRESHOLD, &data->uevent_temp_threshold)) + data->uevent_temp_threshold = DEFAULT_UEVENT_TEMP_THRESHOLD; + + for (i=0; ipdev->dev, "Unable to read of_prop string of uevent sensors\n"); + break; + } + + dev_info(&data->pdev->dev, "%s need uevent, thresh %d\n", name, data->uevent_temp_threshold); + for(n=0; nsensor[n].name, name)) { + dev_info(&data->pdev->dev, "%s uevent flag match!\n", name); + data->sensor[n].need_uevent = true; + break; + } + } + } + + return ret; +} + +static int mmi_sys_temp_ioctl_open(struct inode *node, struct file *file) +{ + return 0; +} + +static int mmi_sys_temp_ioctl_release(struct inode *node, struct file *file) +{ + return 0; +} + +static long mmi_sys_temp_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int i; + int ret = 0; + struct mmi_sys_temp_ioctl request; + + if (!sys_temp_dev) + return -EINVAL; + + if ((_IOC_TYPE(cmd) != MMI_SYS_TEMP_MAGIC_NUM) || + (_IOC_NR(cmd) >= MMI_SYS_TEMP_MAX_NUM)) { + return -ENOTTY; + } + + switch (cmd) { + case MMI_SYS_TEMP_SET_TEMP: + ret = copy_from_user(&request, (void __user *)arg, + sizeof(struct mmi_sys_temp_ioctl)); + if (ret) { + dev_err(&sys_temp_dev->pdev->dev, + "failed to copy_from_user\n"); + return -EACCES; + } + + dev_dbg(&sys_temp_dev->pdev->dev, "name=%s, temperature=%d\n", + request.name, request.temperature); + + for (i = 0; i < sys_temp_dev->num_sensors; i++) { + if (!strncasecmp(sys_temp_dev->sensor[i].name, + request.name, + MMI_SYS_TEMP_NAME_LENGTH)) { + sys_temp_dev->sensor[i].temp = + request.temperature; + + /*generate sensor uevent*/ + uevent_generate(sys_temp_dev, i); + break; + } + } + if (i >= sys_temp_dev->num_sensors) { + dev_dbg(&sys_temp_dev->pdev->dev, + "name %s not supported\n", + request.name); + ret = -EBADR; + } + break; + default: + break; + } + return ret; +} + +#ifdef CONFIG_COMPAT +static long mmi_sys_temp_compat_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + arg = (unsigned long)compat_ptr(arg); + return mmi_sys_temp_ioctl(file, cmd, arg); +} +#endif /* CONFIG_COMPAT */ + +static const struct file_operations mmi_sys_temp_fops = { + .owner = THIS_MODULE, + .open = mmi_sys_temp_ioctl_open, + .unlocked_ioctl = mmi_sys_temp_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = mmi_sys_temp_compat_ioctl, +#endif /* CONFIG_COMPAT */ + .release = mmi_sys_temp_ioctl_release, +}; + +static struct miscdevice mmi_sys_temp_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "mmi_sys_temp", + .fops = &mmi_sys_temp_fops, +}; + +static int mmi_sys_temp_get(struct thermal_zone_device *thermal, + int *temp) +{ + struct mmi_sys_temp_sensor *sensor = thermal->devdata; + + if (!sensor) + return -EINVAL; + + *temp = sensor->temp; + return 0; +} + +static struct thermal_zone_device_ops mmi_sys_temp_ops = { + .get_temp = mmi_sys_temp_get, +}; + +static void psy_changed_work_func(struct work_struct *work) +{ + int num_sensors_listener, i, desc = 0; + char buf[1024]; + + if (!sys_temp_dev) + return; + + num_sensors_listener = sys_temp_dev->num_sensors_listener; + + for (i = 0; i < num_sensors_listener; i++) { + if(sys_temp_dev->sensor_listener[i].tz_dev) { + + thermal_zone_get_temp(sys_temp_dev->sensor_listener[i].tz_dev, + &sys_temp_dev->sensor_listener[i].temp); + + /*generate sensor uevent*/ + uevent_generate(sys_temp_dev, i); + } else { + dev_err(&sys_temp_dev->pdev->dev, + "Invalid thermal zone\n"); + return; + } + } + + for (i = 0; i < num_sensors_listener; i++) { + desc += + sprintf(buf + desc, "%s=%s%d.%d, ", + sys_temp_dev->sensor_listener[i].name, + sys_temp_dev->sensor_listener[i].temp < 0 ? "-" : "", + abs(sys_temp_dev->sensor_listener[i].temp / 1000), + abs(sys_temp_dev->sensor_listener[i].temp % 1000)); + } + + dev_info(&sys_temp_dev->pdev->dev, + "%s\n", buf); + return; +} + +static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) +{ + if (!sys_temp_dev) + return -EINVAL; + + if (evt == PSY_EVENT_PROP_CHANGED) + schedule_work(&sys_temp_dev->psy_changed_work); + + return NOTIFY_OK; +} + +static int mmi_sys_temp_probe(struct platform_device *pdev) +{ + int ret = 0; + int i; + struct device_node *node; + int num_sensors; + int num_registered = 0; + int num_sensors_listener; + + if (!pdev) + return -ENODEV; + + node = pdev->dev.of_node; + if (!node) { + dev_err(&pdev->dev, "bad of_node\n"); + return -ENODEV; + } + + num_sensors = of_property_count_strings(node, TEMP_NODE_SENSOR_NAMES); + if (num_sensors <= 0) { + dev_err(&pdev->dev, + "bad number of sensors: %d\n", num_sensors); + return -EINVAL; + } + + num_sensors_listener = of_property_count_strings(node, SENSOR_LISTENER_NAMES); + if (num_sensors_listener <= 0) { + dev_err(&pdev->dev, + "bad number of sensors-listener: %d\n", num_sensors_listener); + } + sys_temp_dev = devm_kzalloc(&pdev->dev, sizeof(struct mmi_sys_temp_dev), + GFP_KERNEL); + if (!sys_temp_dev) { + dev_err(&pdev->dev, + "Unable to alloc memory for sys_temp_dev\n"); + return -ENOMEM; + } + + sys_temp_dev->pdev = pdev; + sys_temp_dev->num_sensors = num_sensors; + + sys_temp_dev->sensor = + (struct mmi_sys_temp_sensor *)devm_kzalloc(&pdev->dev, + (num_sensors * + sizeof(struct mmi_sys_temp_sensor)), + GFP_KERNEL); + if (!sys_temp_dev->sensor) { + dev_err(&pdev->dev, + "Unable to alloc memory for sensor\n"); + return -ENOMEM; + } + + for (i = 0; i < num_sensors; i++) { + ret = of_property_read_string_index(node, + TEMP_NODE_SENSOR_NAMES, i, + &sys_temp_dev->sensor[i].name); + if (ret) { + dev_err(&pdev->dev, "Unable to read of_prop string\n"); + goto err_thermal_unreg; + } + + sys_temp_dev->sensor[i].temp = DEFAULT_TEMPERATURE; + sys_temp_dev->sensor[i].tz_dev = + thermal_zone_device_register(sys_temp_dev->sensor[i].name, + 0, 0, + &sys_temp_dev->sensor[i], + &mmi_sys_temp_ops, + NULL, 0, 0); + if (IS_ERR(sys_temp_dev->sensor[i].tz_dev)) { + dev_err(&pdev->dev, + "thermal_zone_device_register() failed.\n"); + ret = -ENODEV; + goto err_thermal_unreg; + } + num_registered = i + 1; + } + + /*uevent parameter process*/ + uevent_parse_dt(sys_temp_dev, node, num_sensors); + + platform_set_drvdata(pdev, sys_temp_dev); + + ret = misc_register(&mmi_sys_temp_misc); + if (ret) { + dev_err(&pdev->dev, "Error registering device %d\n", ret); + goto err_thermal_unreg; + } + + if (num_sensors_listener <= 0) { + dev_info(&sys_temp_dev->pdev->dev, + "No configure sensors listener !\n"); + goto err_sensors_listener; + } + + sys_temp_dev->num_sensors_listener = num_sensors_listener; + sys_temp_dev->sensor_listener = + (struct mmi_sys_temp_sensor *)devm_kzalloc(&pdev->dev, + (num_sensors_listener * + sizeof(struct mmi_sys_temp_sensor)), + GFP_KERNEL); + if (!sys_temp_dev->sensor_listener) { + dev_err(&pdev->dev, + "Unable to alloc memory for sensor_listener\n"); + goto err_sensors_listener; + } + + for (i = 0; i < num_sensors_listener; i++) { + ret = of_property_read_string_index(node, + SENSOR_LISTENER_NAMES, i, + &sys_temp_dev->sensor_listener[i].name); + if (ret) { + dev_err(&pdev->dev, "Unable to read of_prop string\n"); + goto err_sensors_listener; + } + + sys_temp_dev->sensor_listener[i].temp = DEFAULT_TEMPERATURE; + if (sys_temp_dev->sensor_listener[i].name) { + sys_temp_dev->sensor_listener[i].tz_dev = + thermal_zone_get_zone_by_name(sys_temp_dev->sensor_listener[i].name); + if (IS_ERR(sys_temp_dev->sensor_listener[i].tz_dev)) { + dev_err(&pdev->dev, + "thermal_zone_get_zone_by_name() failed." + "name %s, i %d\n", sys_temp_dev->sensor_listener[i].name, i); + goto err_sensors_listener; + } + } else { + dev_err(&pdev->dev, + "Invalid sensor listener name\n"); + goto err_sensors_listener; + } + } + + INIT_WORK(&sys_temp_dev->psy_changed_work, psy_changed_work_func); + sys_temp_dev->psy_nb.notifier_call = psy_changed; + power_supply_reg_notifier(&sys_temp_dev->psy_nb); +err_sensors_listener: + return 0; + +err_thermal_unreg: + for (i = 0; i < num_registered; i++) + thermal_zone_device_unregister(sys_temp_dev->sensor[i].tz_dev); + + devm_kfree(&pdev->dev, sys_temp_dev); + return ret; +} + +static int mmi_sys_temp_remove(struct platform_device *pdev) +{ + int i; + struct mmi_sys_temp_dev *dev = platform_get_drvdata(pdev); + + for (i = 0; i < dev->num_sensors; i++) + thermal_zone_device_unregister(dev->sensor[i].tz_dev); + + misc_deregister(&mmi_sys_temp_misc); + platform_set_drvdata(pdev, NULL); + devm_kfree(&pdev->dev, sys_temp_dev); + return 0; +} + +static const struct of_device_id mmi_sys_temp_match_table[] = { + {.compatible = "mmi,sys-temp"}, + {}, +}; +MODULE_DEVICE_TABLE(of, mmi_sys_temp_match_table); + +static struct platform_driver mmi_sys_temp_driver = { + .probe = mmi_sys_temp_probe, + .remove = mmi_sys_temp_remove, + .driver = { + .name = "mmi_sys_temp", + .owner = THIS_MODULE, + .of_match_table = mmi_sys_temp_match_table, + }, +}; + +static int __init mmi_sys_temp_init(void) +{ + return platform_driver_register(&mmi_sys_temp_driver); +} + +static void __exit mmi_sys_temp_exit(void) +{ + platform_driver_unregister(&mmi_sys_temp_driver); +} + +module_init(mmi_sys_temp_init); +module_exit(mmi_sys_temp_exit); + +MODULE_ALIAS("platform:mmi_sys_temp"); +MODULE_AUTHOR("Motorola Mobility LLC"); +MODULE_DESCRIPTION("Motorola Mobility System Temperatures"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/pen/Android.mk b/drivers/misc/pen/Android.mk new file mode 100644 index 000000000000..ee98274fb619 --- /dev/null +++ b/drivers/misc/pen/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := bu520xx_pen.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/pen/Kbuild b/drivers/misc/pen/Kbuild new file mode 100644 index 000000000000..07673f24771c --- /dev/null +++ b/drivers/misc/pen/Kbuild @@ -0,0 +1,15 @@ +KERNEL_DIR_PATH = $(shell find $(ANDROID_BUILD_TOP)/kernel/ -name "msm-*" -type d | head -1 | grep -i "kernel/msm-*") + +KERNEL_VERSION_NAME = $(shell basename $(KERNEL_DIR_PATH)) + +KERNEL_PLATFORM_PATH = $(shell find $(ANDROID_BUILD_TOP) -maxdepth 1 -name "kernel_platform" -type d) + +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers + +bu520xx-y := bu520xx_pen.o + +obj-m += bu520xx_pen.o diff --git a/drivers/misc/pen/Makefile b/drivers/misc/pen/Makefile new file mode 100644 index 000000000000..201497405686 --- /dev/null +++ b/drivers/misc/pen/Makefile @@ -0,0 +1,16 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS := $(M)/../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/misc/pen/bu520xx_pen.c b/drivers/misc/pen/bu520xx_pen.c new file mode 100644 index 000000000000..d49815dd3e1e --- /dev/null +++ b/drivers/misc/pen/bu520xx_pen.c @@ -0,0 +1,529 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_HAS_WAKELOCK +#include +#else +#include +#include +#endif + +#define BU520XX_DBG_ENABLE +#ifdef BU520XX_DBG_ENABLE //BU52055 for sofiap +#define pen_dbg(fmt, args...) pr_info("[DBG] %s(L%04d) : " fmt "\n", __func__, __LINE__, ##args) +#else +#define pen_dbg(fmt, args...) +#endif // BU520XX_DBG_ENABLE +#define pen_dbg_func_in() pen_dbg("[BU520XX-DBG-F.IN] %s", __func__) +#define pen_dbg_func_out() pen_dbg("[BU520XX-DBG-F.OUT] %s", __func__) +#define pen_dbg_line() pen_dbg("[LINE] %d(%s)", __LINE__, __func__) + +#define pen_err(fmt, args...) printk("[err] %s(L%04d) : " fmt "\n", __func__, __LINE__, ##args) +#define pen_info(fmt, args...) printk(fmt, ##args) + +#define DRIVER_NAME "pen_detect" +static struct workqueue_struct *hall_sensor_wq; +static struct workqueue_struct *hall_sensor_do_wq; +static struct kobject *hall_sensor_kobj; + +static int bu520xx_pen_probe(struct platform_device *pdev); +static int bu520xx_pen_remove(struct platform_device *pdev); + +static struct input_device_id mID[] = { + { .driver_info = 1 }, //scan all device to match hall sensor + { }, +}; + +static struct hall_sensor_str { + int irq; + int status; + int gpio; + int enable; + spinlock_t mHallSensorLock; + #ifdef CONFIG_HAS_WAKELOCK + struct wake_lock wake_lock; + #else + struct wakeup_source *wake_lock; + #endif + struct input_dev *pen_indev; + struct input_handler pen_handler; + struct input_handle pen_handle; + struct delayed_work hall_sensor_work; + struct delayed_work hall_sensor_dowork; +}* hall_sensor_dev; + +static BLOCKING_NOTIFIER_HEAD(pen_detection_notifier_list); + +/** + * pen_detection_register_client - register a client notifier + * @nb: notifier block to callback on events + * + * This function registers a notifier callback function + * to pen_detection_notifier_list, which would be called when + * the passive pen is inserted or pulled out. + */ +int pen_detection_register_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&pen_detection_notifier_list, + nb); +} +EXPORT_SYMBOL(pen_detection_register_client); + +/** + * pen_detection_unregister_client - unregister a client notifier + * @nb: notifier block to callback on events + * + * This function unregisters the callback function from + * pen_detection_notifier_list. + */ +int pen_detection_unregister_client(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&pen_detection_notifier_list, + nb); +} +EXPORT_SYMBOL(pen_detection_unregister_client); + +/** + * pen_detection_notifier_call_chain - notify clients of pen_detection_events + * @val: event PEN_DETECTION_INSERT or PEN_DETECTION_PULL + * @v: notifier data, inculde display pen detection event. + */ +static int pen_detection_notifier_call_chain(unsigned long val, void *v) +{ + return blocking_notifier_call_chain(&pen_detection_notifier_list, val, + v); +} + +//static void hall_sensor_shutdown(struct platform_device *pdev); +int pen_connect(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id){ + pen_dbg_func_in(); + pen_info("[%s] hall_sensor connect to handler\n", DRIVER_NAME); + return 0; +} + +void pen_event(struct input_handle *handle, unsigned int type, unsigned int code, int value){ + pen_dbg_func_in(); + if(type==EV_SW && code==SW_PEN_INSERTED ){ + if(value != 2 && !!test_bit(code, hall_sensor_dev->pen_indev->sw) != !hall_sensor_dev->status){ + __change_bit(code, hall_sensor_dev->pen_indev->sw); + pen_info("[%s] reset dev->sw=%d \n", DRIVER_NAME,!hall_sensor_dev->status); + } + } +} + +bool pen_match(struct input_handler *handler, struct input_dev *dev){ + pen_dbg_func_in(); + if( (dev->name && handler->name) && + !strcmp(dev->name,"pen_input") && + !strcmp(handler->name,"pen_input_handler")) + return true; + + return false; +} + +static ssize_t show_action_status(struct device *dev,struct device_attribute *attr, char *buf) +{ + pen_dbg_func_in(); + if(!hall_sensor_dev) + return sprintf(buf, "Hall sensor does not exist!\n"); + return sprintf(buf, "%d\n",hall_sensor_dev->status); +} +static ssize_t store_action_status(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int request; + unsigned long flags; + + pen_dbg_func_in(); + if(!hall_sensor_dev){ + pen_err("Hall sensor does not exist!\n"); + return 0; + //return sprintf(buf, "Hall sensor does not exist!\n"); + } + + sscanf(buf, "%du", &request); + spin_lock_irqsave(&hall_sensor_dev->mHallSensorLock, flags); + if (!request) + hall_sensor_dev->status = 0; + else + hall_sensor_dev->status = 1; + spin_unlock_irqrestore(&hall_sensor_dev->mHallSensorLock, flags); + pen_info("[%s] SW_PEN_INSERTED rewite value = %d\n", DRIVER_NAME,!hall_sensor_dev->status); + return count; +} + +static ssize_t show_hall_sensor_enable(struct device *dev, struct device_attribute *attr, char *buf) +{ + pen_dbg_func_in(); + if(!hall_sensor_dev) + return sprintf(buf, "Hall sensor does not exist!\n"); + return sprintf(buf, "%d\n",hall_sensor_dev->enable); +} + +static ssize_t store_hall_sensor_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int request; + + pen_dbg_func_in(); + if(!hall_sensor_dev){ + pen_err("Hall sensor does not exist!\n"); + return 0; + //return sprintf(buf, "Hall sensor does not exist!\n"); + } + sscanf(buf, "%du", &request); + if(request==hall_sensor_dev->enable){ + return count; + } + else { + unsigned long flags; + spin_lock_irqsave(&hall_sensor_dev->mHallSensorLock, flags); + if (hall_sensor_dev->enable==0){ + enable_irq(hall_sensor_dev->irq); + hall_sensor_dev->enable=1; + } + else if (hall_sensor_dev->enable==1){ + disable_irq(hall_sensor_dev->irq); + hall_sensor_dev->enable=0; + } + spin_unlock_irqrestore(&hall_sensor_dev->mHallSensorLock, flags); + } + return count; +} + + +static SENSOR_DEVICE_ATTR_2(action_status, S_IRUGO|S_IWUSR, show_action_status, store_action_status, 0, 0); +static SENSOR_DEVICE_ATTR_2(activity, S_IRUGO|S_IWUSR,show_hall_sensor_enable, store_hall_sensor_enable, 0, 0); + +static struct attribute *hall_sensor_attrs[] = { + &sensor_dev_attr_action_status.dev_attr.attr, + &sensor_dev_attr_activity.dev_attr.attr, + NULL +}; + +static struct attribute_group hall_sensor_group = { + .name = "hall_sensor", + .attrs = hall_sensor_attrs +}; + +static int pen_input_device_create(void) +{ + int err = 0; + + pen_dbg_func_in(); + hall_sensor_dev->pen_indev = input_allocate_device(); + if(!hall_sensor_dev->pen_indev){ + pen_err("[%s] pen_indev allocation fails\n", DRIVER_NAME); + err = -ENOMEM; + goto exit; + } + + hall_sensor_dev->pen_indev->name = "pen_detect"; + hall_sensor_dev->pen_indev->phys= "/dev/input/pen_detect"; + hall_sensor_dev->pen_indev->dev.parent= NULL; + input_set_capability(hall_sensor_dev->pen_indev, EV_SW, SW_PEN_INSERTED); + + err = input_register_device(hall_sensor_dev->pen_indev); + if (err) { + pen_err("[%s] input registration fails\n", DRIVER_NAME); + err = -1; + goto exit_input_free; + } + hall_sensor_dev->pen_handler.match=pen_match; + hall_sensor_dev->pen_handler.connect=pen_connect; + hall_sensor_dev->pen_handler.event=pen_event; + hall_sensor_dev->pen_handler.name="pen_detect_handler"; + hall_sensor_dev->pen_handler.id_table = mID; + err=input_register_handler(& hall_sensor_dev->pen_handler); + if(err){ + pen_err("[%s] handler registration fails\n", DRIVER_NAME); + err = -1; + goto exit_unregister_input_dev; + } + hall_sensor_dev->pen_handle.name="pen_detect_handle"; + hall_sensor_dev->pen_handle.open=1; //receive any event from hall sensor + hall_sensor_dev->pen_handle.dev=hall_sensor_dev->pen_indev; + hall_sensor_dev->pen_handle.handler=&hall_sensor_dev->pen_handler; + err=input_register_handle(& hall_sensor_dev->pen_handle); + if(err){ + pen_err("[%s] handle registration fails\n", DRIVER_NAME); + err = -1; + goto exit_unregister_handler; + } + return 0; + +exit_unregister_handler: + input_unregister_handler(& hall_sensor_dev->pen_handler); +exit_unregister_input_dev: + input_unregister_device(hall_sensor_dev->pen_indev); +exit_input_free: + input_free_device(hall_sensor_dev->pen_indev); + hall_sensor_dev->pen_indev = NULL; +exit: + return err; +} + + + +static void pen_do_work_function(struct work_struct *dat) +{ + pen_dbg_func_in(); + pen_info("[%s] hall_sensor_interrupt = %d\n", DRIVER_NAME,hall_sensor_dev->irq); + cancel_delayed_work(&hall_sensor_dev->hall_sensor_work); + queue_delayed_work(hall_sensor_wq, &hall_sensor_dev->hall_sensor_work, 0); +} + + +static void pen_report_function(struct work_struct *dat) +{ + unsigned long flags; + int counter, status = 0, initial_status; + + pen_dbg_func_in(); + if (!hall_sensor_dev) { + pen_info("[%s] hall_sensor_dev isn't init.\n", DRIVER_NAME); + return; + } + initial_status = hall_sensor_dev->status; + for (counter = 0;counter < 3;counter++){ + msleep(50); + spin_lock_irqsave(&hall_sensor_dev->mHallSensorLock, flags); + if (gpio_get_value(hall_sensor_dev->gpio) > 0){ + hall_sensor_dev->status = 1; + status++; + }else{ + hall_sensor_dev->status = 0; + } + pen_info("[%s] SW_PEN_INSERTED check[%d] value = %d\n", DRIVER_NAME, counter, !hall_sensor_dev->status); + spin_unlock_irqrestore(&hall_sensor_dev->mHallSensorLock, flags); + } + + if((status > 0) && (status < 3)){ + pen_info("[%s] SW_PEN_INSERTED do not report to framework.\n", DRIVER_NAME); + hall_sensor_dev->status = initial_status; + return; + } + + if (!hall_sensor_dev->pen_indev) { + pen_info("[%s] pen_indev isn't ready,don't report.\n", DRIVER_NAME); + return; + } + input_report_switch(hall_sensor_dev->pen_indev, SW_PEN_INSERTED, !hall_sensor_dev->status); + input_sync(hall_sensor_dev->pen_indev); +#ifdef CONFIG_HAS_WAKELOCK + wake_unlock(&hall_sensor_dev->wake_lock); +#else +#endif + +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_timeout(&hall_sensor_dev->wake_lock, msecs_to_jiffies(3000)); +#else + PM_WAKEUP_EVENT(hall_sensor_dev->wake_lock,msecs_to_jiffies(3000)); +#endif + + if(!hall_sensor_dev->status) + pen_detection_notifier_call_chain(PEN_DETECTION_INSERT, NULL); + else + pen_detection_notifier_call_chain(PEN_DETECTION_PULL, NULL); + pen_info("[%s] SW_pen report value = %d\n", DRIVER_NAME,!hall_sensor_dev->status); +} + +static irqreturn_t hall_sensor_interrupt_handler(int irq, void *dev_id) +{ + pen_dbg_func_in(); + pen_info("[%s] hall_sensor_interrupt_handler = %d\n", DRIVER_NAME,hall_sensor_dev->irq); + queue_delayed_work(hall_sensor_do_wq, &hall_sensor_dev->hall_sensor_dowork, msecs_to_jiffies(0)); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock(&hall_sensor_dev->wake_lock); +#else +#endif + return IRQ_HANDLED; +} + +static int set_irq_hall_sensor(void) +{ + int rc = 0 ; + + pen_dbg_func_in(); + hall_sensor_dev->irq = gpio_to_irq(hall_sensor_dev->gpio); + pen_info("[%s] hall_sensor irq = %d\n", DRIVER_NAME,hall_sensor_dev->irq); + rc = request_irq(hall_sensor_dev->irq,hall_sensor_interrupt_handler, + IRQF_SHARED|IRQF_TRIGGER_RISING|IRQF_TRIGGER_FALLING,"hall_sensor_irq",hall_sensor_dev); + if (rc<0) { + pen_info("[%s] Could not register for hall sensor interrupt, irq = %d, rc = %d\n", DRIVER_NAME,hall_sensor_dev->irq,rc); + rc = -EIO; + goto err_gpio_request_irq_fail ; + } + + enable_irq_wake(hall_sensor_dev->irq); + + return 0; + +err_gpio_request_irq_fail: + return rc; +} + +#ifdef CONFIG_OF +static const struct of_device_id bu520xx_pen_match[] = { + { .compatible = "rohm,bu520xx_pen_detect", }, + {} +}; +#else +#define bu520xx_pen_match NULL +#endif +MODULE_DEVICE_TABLE(of, bu520xx_pen_match); + +static struct platform_driver bu520xx_pen_driver = { + .probe = bu520xx_pen_probe, + .remove = bu520xx_pen_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = bu520xx_pen_match, + }, +}; + +static int bu520xx_pen_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + enum of_gpio_flags flags; + int ret; + + pen_dbg_func_in(); + + //set file node + hall_sensor_kobj = kobject_create_and_add("hall_sensor_kobject", kernel_kobj); + if (!hall_sensor_kobj){ + pen_err("[%s] hall_sensor_kobject fails for hall sensor\n", DRIVER_NAME); + platform_device_unregister(pdev); + platform_driver_unregister(&bu520xx_pen_driver); + return -ENOMEM; + } + + ret = sysfs_create_group(hall_sensor_kobj, &hall_sensor_group); + if (ret){ + goto fail_for_hall_sensor; + } + + //Memory allocation + hall_sensor_dev = kzalloc(sizeof (struct hall_sensor_str), GFP_KERNEL); + if (!hall_sensor_dev) { + pen_err("[%s] Memory allocation fails for hall sensor\n", DRIVER_NAME); + ret = -ENOMEM; + goto fail_for_hall_sensor; + } + + spin_lock_init(&hall_sensor_dev->mHallSensorLock); + hall_sensor_dev->enable = 1; + + //set gpio + hall_sensor_dev->gpio = of_get_named_gpio_flags(np, + "rohm,nirq-gpio", 0, &flags); + if (hall_sensor_dev->gpio < 0) + { + pen_err("[%s] GPIO for hall sensor does not exist.\n", DRIVER_NAME); + ret= -1; + goto fail_for_set_gpio_hall_sensor; + } + + gpio_request(hall_sensor_dev->gpio,"pen_detect_gpio"); + gpio_direction_input(hall_sensor_dev->gpio); + + //create input_dev + hall_sensor_dev->pen_indev = NULL; + ret = pen_input_device_create(); + if (ret < 0) + goto fail_for_create_input_dev; + + //init workqueue & start detect signal + hall_sensor_wq = create_singlethread_workqueue("hall_sensor_wq"); + hall_sensor_do_wq = create_singlethread_workqueue("hall_sensor_do_wq"); + INIT_DELAYED_WORK(&hall_sensor_dev->hall_sensor_work, pen_report_function); + INIT_DELAYED_WORK(&hall_sensor_dev->hall_sensor_dowork, pen_do_work_function); + + + queue_delayed_work(hall_sensor_do_wq, &hall_sensor_dev->hall_sensor_dowork, 0); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&hall_sensor_dev->wake_lock, WAKE_LOCK_SUSPEND, "pen_suspend_blocker"); +#else + PM_WAKEUP_REGISTER(&pdev->dev, hall_sensor_dev->wake_lock, "pen_suspend_blocker"); + if(!hall_sensor_dev->wake_lock){ + dev_err(&pdev->dev,"%s: Failed to allocate wakeup source\n",__func__); + ret = -ENOMEM; + goto fail_wakeup_init; + } +#endif + //set irq + ret = set_irq_hall_sensor(); + if (ret < 0) + goto fail_for_irq_hall_sensor; + + pen_info("hall_sensor_init Done.\r\n"); + return 0; + +fail_for_irq_hall_sensor: + destroy_workqueue(hall_sensor_do_wq); + destroy_workqueue(hall_sensor_wq); + input_free_device(hall_sensor_dev->pen_indev); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&hall_sensor_dev->wake_lock); +#else + PM_WAKEUP_UNREGISTER(hall_sensor_dev->wake_lock); +#endif +fail_wakeup_init: +fail_for_create_input_dev: + hall_sensor_dev->pen_indev=NULL; + gpio_free(hall_sensor_dev->gpio); +fail_for_set_gpio_hall_sensor: + kfree(hall_sensor_dev); + hall_sensor_dev=NULL; + +fail_for_hall_sensor: + kobject_put(hall_sensor_kobj); + return ret; +} + +static int bu520xx_pen_remove(struct platform_device *pdev) +{ + disable_irq(hall_sensor_dev->irq); + gpio_free(hall_sensor_dev->gpio); + destroy_workqueue(hall_sensor_do_wq); + destroy_workqueue(hall_sensor_wq); + input_free_device(hall_sensor_dev->pen_indev); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&hall_sensor_dev->wake_lock); +#else + PM_WAKEUP_UNREGISTER(hall_sensor_dev->wake_lock); +#endif + hall_sensor_dev->pen_indev=NULL; + kfree(hall_sensor_dev); + hall_sensor_dev=NULL; + kobject_put(hall_sensor_kobj); + platform_driver_unregister(&bu520xx_pen_driver); + platform_device_unregister(pdev); + return 1; +} + + +module_platform_driver(bu520xx_pen_driver); + + +MODULE_DESCRIPTION("Rohm_Hall_sensor-bu520xx_pen Driver"); +MODULE_AUTHOR("huangjq9 "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/qrng_hwrng/Android.mk b/drivers/misc/qrng_hwrng/Android.mk new file mode 100755 index 000000000000..3b487c289174 --- /dev/null +++ b/drivers/misc/qrng_hwrng/Android.mk @@ -0,0 +1,13 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := qrng_hwrng.ko +LOCAL_MODULE_TAGS := optional +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/qrng_hwrng/Kbuild b/drivers/misc/qrng_hwrng/Kbuild new file mode 100755 index 000000000000..2e3d28b31b6a --- /dev/null +++ b/drivers/misc/qrng_hwrng/Kbuild @@ -0,0 +1,8 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +EXTRA_CFLAGS += -DCONFIG_HW_RANDOM_IDQ250C2_TIMER +EXTRA_CFLAGS += -DCONFIG_HW_RANDOM_IDQ250C2_MISC_DEVICE + +obj-m += qrng_hwrng.o diff --git a/drivers/misc/qrng_hwrng/qrng_hwrng.c b/drivers/misc/qrng_hwrng/qrng_hwrng.c new file mode 100755 index 000000000000..082e049a086d --- /dev/null +++ b/drivers/misc/qrng_hwrng/qrng_hwrng.c @@ -0,0 +1,1104 @@ +// --------------------------------------------------------------------------- +// vim: ts=4 sw=4: +// --------------------------------------------------------------------------- +// IDQuantique Co., Ltd. +// +// Copyright (C) 2019 IDQuantique Corporation. +// All rights reserved. +// +// Project : QRNG +// Filename : qrng_hwrng.c +// +// --------------------------------------------------------------------------- +// Feature : hwrng driver for QRNG-IDQ250C2 +// Function Description : +// Note: +// --------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_HW_RANDOM_IDQ250C2_MISC_DEVICE +#define QRNG_IOCTL_DIAG _IO('Q',4) +#endif + +#define BURST_PKT_CNT 1 +#define PKT_SIZE 18 + +static unsigned char *kbuf = NULL; + +int qrng_get_data(char *data_buf, int data_size); +static int data_avail = 0; + +#define CHUNK_SIZE BURST_PKT_CNT * (PKT_SIZE -2) + + +#define QRNG_DEBUG + +#ifdef QRNG_DEBUG +#define PINFO(fmt, args...) pr_info("[QRNG_I] %s(): " fmt, __FUNCTION__, ##args); +#define PDBG(fmt, args...) pr_info("[QRNG_D] %s(): " fmt, __FUNCTION__, ##args); +#define PERR(fmt, args...) pr_err ("[QRNG_E] %s(): " fmt, __FUNCTION__, ##args); +#define PLINE pr_info("[QRNG_D] %s(): %d line\n", __FUNCTION__, __LINE__); +#else +#define PINFO(fmt, args...) pr_info("[QRNG_I] %s(): " fmt, __FUNCTION__, ##args); +#define PDBG(fmt, args...) do {} while(0); +#define PERR(fmt, args...) pr_info("[QRNG_E] %s(): " fmt, __FUNCTION__, ##args); +#define PLINE do {} while(0); +#endif + +#define QRNG_PRO_WINSIZE 512 +#define QRNG_PACKETSIZE 16 +#define PKT_SIZE 18 // 9 * 2 byte +#define QRNG_DEV_READY_FLAG 0x01 +#define QRNG_PRO_CUTOFF 15 +#define QRNG_BIT_REP_H8 4 + +static uint8_t gnQrngHchk = 0x00; +static uint32_t gnQrngHchkCnt = 0; + +typedef enum _QRNG_ERR { + QRNG_OK = 0, + QRNG_NOT_EXIST = 1, + QRNG_HEADER_ERR = 2, + QRNG_INF_ERR = 3, + QRNG_VST_FAIL = 4, + QRNG_RETRY_TIMEOUT = 5, + QRNG_PARAM_ERROR = 8, + QRNG_CHIP_READ_ERROR = 19, + QRNG_BYTE_REP_ERROR = 21, + QRNG_PROPORTION_ERROR = 22, + QRNG_ERROR = 0xFFFF +} QRNG_ERR; + +// --------------------------------------------------------------------------- +//END of IDQ250C2_LIB +// --------------------------------------------------------------------------- + + +typedef struct _QRNG_DEV { + struct device *dev; + int dev_num; + char dev_name[7]; + struct i2c_client *client; + struct mutex trans_mtx; +#ifdef CONFIG_QRNG_HQM + QRNG_HQM hqm; +#endif + int GPIO_PWDN; + int GPIO_LDO_EN; + int QRNG_LDO_EN_STATUS; + int QRNG_READY_STATUS; + int QRNG_PWDN_STATUS; + int QRNG_SUSPEND; +} QRNG_DEV; + +#define DEVICE_NAME "qrng-i2c" + +static QRNG_DEV *gQrng = NULL; + +// --------------------------------------------------------------------------- +// hardware adapter function for qrng +// --------------------------------------------------------------------------- +static int __maybe_unused qrng_i2c_write(uint8_t nAddr, uint8_t nData) +{ + uint8_t pBuff[2]; + + PDBG("0x%02X:0x%02X", nAddr, nData); + if (gQrng == NULL) return -EIO; + + pBuff[0] = nAddr; + pBuff[1] = nData; + + if (i2c_master_send(gQrng->client,pBuff,2) != 2) + { + PERR("i2c_master_send error\n"); + return -EIO; + } + + return 0; +} + +static int qrng_i2c_read(uint8_t nAddr, uint8_t *pData) +{ + if (gQrng == NULL) return -EIO; + + if (i2c_master_send(gQrng->client,&nAddr,1) != 1) + { + PERR("i2c_master_send error : 0x%02X\n", nAddr); + return -EIO; + } + if (i2c_master_recv(gQrng->client,pData,1) != 1) + { + PERR("i2c_master_recv error : 0x%02X\n", nAddr); + return -EIO; + } + + PDBG("0x%02X:0x%02X", nAddr, *pData); + return 0; +} + +static int qrng_i2c_read_burst(uint8_t nAddr, int nLen, uint8_t *pData) +{ + struct i2c_adapter *adap = gQrng->client->adapter; + struct i2c_msg msgs[2]; + + PDBG("0x%02X - length(%d)", nAddr, nLen); + + if (gQrng == NULL) return -EIO; + + msgs[0].addr = gQrng->client->addr; + msgs[0].flags = gQrng->client->flags; + msgs[0].len = 1; + msgs[0].buf = &nAddr; + + msgs[1].addr = gQrng->client->addr; + msgs[1].flags = gQrng->client->flags | I2C_M_RD; + msgs[1].len = nLen; + msgs[1].buf = pData; + + if (i2c_transfer(adap, msgs, 2) != 2) + { + PERR("i2c_transfer error\n"); + return -EIO; + } + + return 0; +} + +static int qrng_get_vi(void) +{ + // vi status : 1 = ok / 0 = fail + return 1; +} + +static void qrng_delay_ms(int ms) +{ + if (ms >= 10) msleep(ms); + else while (ms--) udelay(1000); +} + +static void* qrng_memcpy(void *dest,const void *src,unsigned int len) +{ + return memcpy(dest, src, len); +} + + +static int qrng_pwdn(int pol) +{ + int ret; + PDBG("%d", pol); + if (pol) // set pwdn high + { + ret = gpio_direction_output(gQrng->GPIO_PWDN, 1); + if (!ret) { + gQrng->QRNG_PWDN_STATUS = 1; + PINFO("QRNG_PWDN_STATUS = 1"); + } + } + else + { // set pwdn low + ret = gpio_direction_output(gQrng->GPIO_PWDN, 0); + if (!ret) { + gQrng->QRNG_PWDN_STATUS = 0; + PINFO("QRNG_PWDN_STATUS = 0"); + } + } + return ret; +} + +// --------------------------------------------------------------------------- +// Health Test variable +// --------------------------------------------------------------------------- + +static uint8_t g_bQrngWindowData[QRNG_PRO_WINSIZE] = {0,}; // Propotion Window Buffer +static uint8_t g_bHist[256] = {0,}; // Propotion Hist Buffer +static int g_iWindowDataCnt = 0; // Propotion Window Count + +static int g_iQrngByteReinit = 1; // Repetition Init Count (Byte) +static uint8_t g_bLastData[16] = {0,}; // Repetition Byte Last Data Buffer + +static int g_QrngError_cnt = 0; // Health Error Count (5 times Repeats => Return err) + +// --------------------------------------------------------------------------- +// qrng function +// --------------------------------------------------------------------------- + +#define RETRY 10 + +static int qrng_rd_pkt(uint8_t pPkt[PKT_SIZE]) +{ + int ret, retry; + + for (retry=RETRY; retry>0; retry--) + { + // read packet + ret = qrng_i2c_read_burst(0x12, PKT_SIZE, pPkt); + if (ret != 0) { + PDBG("QRNG_INF_ERR"); + return QRNG_INF_ERR; + } + + // check header + if (pPkt[0] != 0xA2) return QRNG_HEADER_ERR; + + // check ready (not duplicated packet) + else if (pPkt[1] != 0x08) + { + break; + } + } + + if (retry <= 0) { + WARN("[QRNG] %s : QRNG_RETRY_TIMEOUT", __func__); + return QRNG_RETRY_TIMEOUT; + } + return QRNG_OK; +} + +// Name : qrng_propotion +// Function : Proportion Test 512 Windows (Cutoff : 13) +static int qrng_propotion(const uint8_t *data, int m_nEditProValue) +{ + + int i; + int iMax = 0; + uint8_t bWindowdata[QRNG_PACKETSIZE]; + + qrng_memcpy(&bWindowdata[0], data, QRNG_PACKETSIZE); + qrng_memcpy(&g_bQrngWindowData[g_iWindowDataCnt], &bWindowdata[0],QRNG_PACKETSIZE); + + for(i = 0; i < QRNG_PACKETSIZE; i++) + { + g_bHist[bWindowdata[i]] += 1; + + if (g_bHist[bWindowdata[i]] >= iMax) + { + iMax = g_bHist[bWindowdata[i]]; + } + else + { + ; + } + } + + if(iMax >= m_nEditProValue) + { + return 1; + } + else + { + return 0; + } +} + +//------------------------------------------------------------ +// Name : qrng_byteRepetition +// Function : BYTE Repetition Check (default : 4 Bytes) +//------------------------------------------------------------ +static int qrng_byteRepetition(const uint8_t *data, int m_nEditRepH8) +{ + int iCntByte = 0; + int i; + int init = g_iQrngByteReinit; + uint8_t bTemp = 0; + uint8_t bWindowByte[QRNG_PACKETSIZE]; // MAX 15+ 16 + uint8_t bLastData[QRNG_PACKETSIZE]; + + qrng_memcpy(bLastData, g_bLastData, m_nEditRepH8); + + if (g_iQrngByteReinit == 1) + { + ; + } + else + { + bTemp = bLastData[0]; + for(i=1;i < m_nEditRepH8; i++) + { + + if(bTemp == bLastData[i]) + { + iCntByte += 1; + } + else + { + iCntByte = 0; + } + + bTemp = bLastData[i]; + + } + } + + qrng_memcpy(&bWindowByte, data, QRNG_PACKETSIZE); + + for (i = 0; i < QRNG_PACKETSIZE ; i++) + { + + if(init == 1) + { + bTemp = bWindowByte[i]; + init = 0; + } + else + { + if (bTemp == bWindowByte[i]) + { + iCntByte += 1; + } + else + { + iCntByte = 0; + } + if (iCntByte == (m_nEditRepH8-1)) + { + return 1; + } + + bTemp = bWindowByte[i]; + } + } + + return 0; +} + +//------------------------------------------------------------ +// Name : qrng_healthTest +// Function : include Proportion , Repetition Bytes, BIT +//------------------------------------------------------------ + +static int qrng_healthTest(const uint8_t *data) +{ + static int q_iProOverFlame = 0; + static int q_iReByteOverFlame = 0; + + int i; + uint8_t ucPacketData[QRNG_PACKETSIZE]; + + qrng_memcpy(ucPacketData, data, QRNG_PACKETSIZE); + + q_iProOverFlame = qrng_propotion(ucPacketData, QRNG_PRO_CUTOFF); + + q_iReByteOverFlame = qrng_byteRepetition(ucPacketData, QRNG_BIT_REP_H8); + + if((q_iReByteOverFlame == 0) && (q_iProOverFlame == 0)) + { + g_iQrngByteReinit = 0; + + if (g_iWindowDataCnt < (QRNG_PRO_WINSIZE - QRNG_PACKETSIZE)) + { + g_iWindowDataCnt += QRNG_PACKETSIZE; + } + else + { + for(i = 0; i < QRNG_PACKETSIZE; i++) + { + g_bHist[g_bQrngWindowData[i]]--; + } + + qrng_memcpy(&g_bQrngWindowData[0],&g_bQrngWindowData[QRNG_PACKETSIZE], QRNG_PRO_WINSIZE - QRNG_PACKETSIZE); + + } + qrng_memcpy(&g_bLastData[0], &ucPacketData[QRNG_PACKETSIZE - QRNG_BIT_REP_H8], QRNG_BIT_REP_H8); + + } + else + { + for(i = 0; i QRNG_READY_STATUS & QRNG_DEV_READY_FLAG)) { + PERR("QRNG DEV is not ready."); + return QRNG_NOT_EXIST; + } + + if(qrng_pwdn(1) < 0) { + PERR("Failed to set qrng_pwdn : 1"); + return QRNG_INF_ERR; + } + qrng_delay_ms(3); + + // initialize health check info + qrng_init_hchk(); + + if(qrng_i2c_read(0x10, buf) < 0) { + PERR("Failed to read status registry"); + return QRNG_INF_ERR; + } + + if (buf[0] != 0x0) { + PERR("ERR I2C Read [0x10] : 0x%02X", buf[0]); + return QRNG_HEADER_ERR; + } + + return QRNG_OK; +} + +static int qrng_rd_samp(uint8_t *pData, int nLen, uint8_t *pHchk) +{ + uint8_t pPkt[PKT_SIZE]; + int ret, i, j, pkt_cnt, idx = 0; + int health_count = 0, header_count = 0; + + if (gQrng->QRNG_SUSPEND == 1) + { + return -EFAULT; + } + + if (gQrng->QRNG_PWDN_STATUS != 1) + { + ret = qrng_init(); + if (ret) { + PERR("qrng_init error: %d\n", ret); + return -EFAULT; + } + } + + pkt_cnt = (((nLen+1) >> 1) + 7) >> 3; + + *pHchk = 0; // health check + + for (i=pkt_cnt; i>0; i--) + { +samp_retry: + // check vi fail + if (qrng_get_vi() == 0) + return QRNG_VST_FAIL; + + // read packet + ret = qrng_rd_pkt(pPkt); + if (ret != QRNG_OK) + { + if ((ret == QRNG_HEADER_ERR) && (header_count < 5)) { + header_count++; + goto samp_retry; + }else{ + return ret; + } + } + + if (pPkt[1]) // check Health data + { + PERR("Health data wrong : 0x%02X", pPkt[1]); + gnQrngHchk |= pPkt[1]; + gnQrngHchkCnt++; + if (health_count < 5) { + health_count++; + goto samp_retry; + }else{ + *pHchk |= pPkt[1]; +#ifdef QRNG_DEBUG + panic("Health data wrong"); +#endif + return QRNG_PARAM_ERROR; + } + } + ret = qrng_healthTest(&pPkt[2]); + + if(ret != 0) + { + PINFO("SP800-90B approved tests failure : %d", ret); + g_QrngError_cnt++; + if(g_QrngError_cnt == 5) + { +#ifdef QRNG_DEBUG + panic("SP800-90B approved tests failure"); +#endif + return ret; + } + i++; + } + else + { + g_QrngError_cnt = 0; + for (j=2; jQRNG_PWDN_STATUS != 0) + { + gQrng->QRNG_PWDN_STATUS = 0; + if(qrng_pwdn(0) < 0) + { + PERR("Failed to set qrng_pwdn : 0"); + } + } + qrng_delay_ms(3); +} + +static void kerneltimer_register(struct timer_list *pTimer, unsigned long timeover) +{ + timer_setup(pTimer,kerneltimer_timeover, 0); + mod_timer(pTimer, jiffies + msecs_to_jiffies(timeover)); +} + +static void kerneltimer_exit(struct timer_list *pTimer) +{ + if(pTimer != NULL) + { + del_timer(pTimer); + } +} +#endif +// --------------------------------------------------------------------------- +// misc driver +// --------------------------------------------------------------------------- + +int qrng_get_data(char *data_buf, int data_size) +{ + int ret = 0; + char hchk = 0; + int len = 0; + unsigned int read_data = 0; + + if(data_size <= 0) { + PERR("wrong data size : %d\n", data_size); + return -EINVAL; + } + mutex_lock(&gQrng->trans_mtx); + +#ifdef CONFIG_HW_RANDOM_IDQ250C2_TIMER + kerneltimer_exit(&timer); +#endif + + while(data_size){ + if(!data_avail){ + ret = qrng_rd_samp(kbuf, CHUNK_SIZE, &hchk); + if(ret){ + if(hchk){ + PERR("qrng_rd_samp health erro:hchk%02Xh(ret:%d)\n", hchk, ret); + }else{ + PERR("qrng_rd_samp error: %d\n", ret); + } + break; + } + + data_avail = CHUNK_SIZE; + } + + len = data_avail; + if(len > data_size) + len = data_size; + + data_avail -= len; + + qrng_memcpy(data_buf+read_data, kbuf+data_avail, len); + + data_size -= len; + read_data += len; + + } + +#ifdef CONFIG_HW_RANDOM_IDQ250C2_TIMER + kerneltimer_register(&timer, TIME_STEP); +#endif + mutex_unlock(&gQrng->trans_mtx); + + return ret; +} + +EXPORT_SYMBOL(qrng_get_data); + +#ifdef CONFIG_HW_RANDOM_IDQ250C2_MISC_DEVICE +static int qrng_op_open(struct inode *inode, struct file *filp) +{ + PLINE; + /* enforce read-only access to this chrdev */ + if ((filp->f_mode & FMODE_READ) == 0) + return -EINVAL; + if (filp->f_mode & FMODE_WRITE) + return -EINVAL; + return 0; +} + +static int qrng_op_release(struct inode *inode, struct file *filp) +{ + return 0; +} + +#define CHRDEV_READ_SIZE 64 +static ssize_t qrng_op_read(struct file *filp, char *buf, + size_t count, loff_t *f_pos) +{ + int ret = 0; + size_t read = 0; + size_t total = count; + char *pbuf = buf; + + unsigned char buffer[CHRDEV_READ_SIZE]; + + if(filp->f_flags & O_NONBLOCK){ + PDBG("NONBLOCK STATUS is not supported\n"); + return 0; + } + + if(buf == NULL || count < 0){ + PDBG("Invalid arguments\n"); + return 0; + } + + while(total > 0) { + read = total > CHRDEV_READ_SIZE ? CHRDEV_READ_SIZE : total; + ret = qrng_get_data(buffer, read); + + if(ret){ + PERR("Fail to get_data on chrdev\n"); + memset(buffer, 0x00, CHRDEV_READ_SIZE); + return -EINVAL; + } + + if(copy_to_user(pbuf, buffer, read)){ + PERR("copy to user() failed\n"); + memset(buffer, 0x00, CHRDEV_READ_SIZE); + return -EFAULT; + } + + total -= read; + pbuf += read; + } + + memset(buffer, 0x00, CHRDEV_READ_SIZE); + return (count - total); +} + +static ssize_t qrng_op_write(struct file *filp, const char __user *buf, + size_t count, loff_t *f_pos) +{ + return 0; +} + +static int qrng_diag(void); +static unsigned int get_elapse_ms(int bRestart); + +static long qrng_op_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + PLINE; + switch (cmd) + { + case QRNG_IOCTL_DIAG: + return qrng_diag(); + } + return -EINVAL; +} + +#define DIAG_READ_SIZE (1024) +#define DIAG_READ_CNT (12) +#define DIAG_BUFF_LEN ((DIAG_READ_CNT)*(DIAG_READ_SIZE)) +#define DIAG_TIMEOUT_MS (2000) +#define DIAG_DIFF_MAX (20*2*DIAG_BUFF_LEN/100) // 20 % + +static int qrng_diag(void) +{ + int ret, i, j, hist[4], max, min, diff; + char *kbuf, *p; + unsigned int elps_ms; + + PLINE; + + // memory allocation for kernel area + kbuf = kvzalloc(DIAG_BUFF_LEN, GFP_KERNEL); + if (kbuf == NULL) + { + PERR("kzalloc failed\n"); + return -ENOMEM; + } + + // read rng data + get_elapse_ms(1); + for (i=0, p=kbuf; i DIAG_TIMEOUT_MS) + { + PERR("diagnostic timeout: %d ms\n", elps_ms); + kvfree(kbuf); + return -ETIMEDOUT; + } + } + + // check uniformity + hist[0] = hist[1] = hist[2] = hist[3] = 0; + for (i=0; i=0; j-=2) + hist[(kbuf[i]>>j)&3]++; + PINFO("Histogram data : %d %d %d %d\n", hist[0], hist[1], hist[2], hist[3]); + + max = 0; + min = DIAG_BUFF_LEN; + for (i=0; i<4; i++) + { + if (hist[i] > max) max = hist[i]; + if (hist[i] < min) min = hist[i]; + } + diff = max - min; + PINFO("Diff : %d (%d - %d)\n", diff, max, min); + if (diff > DIAG_DIFF_MAX) + { + PERR("diagnostic uniformity fail: %d (%d-%d)\n", diff, max, min); + kvfree(kbuf); + return -EINVAL; + } + + elps_ms = get_elapse_ms(0); + PINFO("Diagnosis elapsed time: %d ms\n", elps_ms); + + kvfree(kbuf); + + PINFO("QRNG : GOOD"); + return 0; +} + +static unsigned int get_elapse_ms(int bRestart) +{ + static unsigned int start_ms = 0; + unsigned int end_ms, elapse_ms; + struct timespec start, end; + + // get end time + getnstimeofday(&end); + end_ms = (end.tv_sec*1000) + (end.tv_nsec/1000000); + + // calculate elase time + elapse_ms = end_ms - start_ms; + + // get start time for next check + if (bRestart) + { + getnstimeofday(&start); + start_ms = (start.tv_sec*1000) + (start.tv_nsec/1000000); + } + + return elapse_ms; +} +#endif //CONFIG_HW_RANDOM_IDQ250C2_MISC_DEVICE + +static struct file_operations qrng_fops = { + .owner = THIS_MODULE, +#ifdef CONFIG_HW_RANDOM_IDQ250C2_MISC_DEVICE + .read = qrng_op_read, + .write = qrng_op_write, + .unlocked_ioctl = qrng_op_ioctl, + .open = qrng_op_open, + .release = qrng_op_release, +#endif +}; + +static struct miscdevice qrng_misc = { +#ifdef CONFIG_HW_RANDOM_IDQ250C2_MISC_DEVICE + .name = "qrandom", + .nodename = "qrng-char", +#else + .name = "qrng_misc", +#endif + .minor = MISC_DYNAMIC_MINOR, + .fops = &qrng_fops, +}; + +// --------------------------------------------------------------------------- +// i2c protocol driver +// --------------------------------------------------------------------------- + +#define PWDN_GPIO_NAME "pwdn-gpio" + +static int qrng_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = -1; + struct device *dev = &client->dev; + QRNG_DEV *qrng; + + PLINE; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + PERR("Error : i2c_check_functionality"); + return -ENOTSUPP; + } + + if ((qrng = kzalloc(sizeof(*qrng), GFP_KERNEL)) == NULL) { + PERR("Error : kzalloc for QRND_DEV"); + return -ENOMEM; + } + + qrng->dev_num = 0; + scnprintf(qrng->dev_name, sizeof(qrng->dev_name), "qrng-%d", qrng->dev_num); + PINFO("qrng->dev_name : %s", qrng->dev_name); + + qrng->dev = get_device(dev); + dev_set_drvdata(dev, qrng); + + qrng->client = client; + qrng->QRNG_READY_STATUS = 0x00; // Make sure + + mutex_init(&qrng->trans_mtx); + + + qrng->GPIO_PWDN = of_get_named_gpio(dev->of_node, PWDN_GPIO_NAME, 0); + if(qrng->GPIO_PWDN < 0 ){ + PERR("Failed to get %s, error: %d\n", PWDN_GPIO_NAME, qrng->GPIO_PWDN); + return -ENODEV; + }else{ + PINFO("Success to get %s :%d\n", PWDN_GPIO_NAME, qrng->GPIO_PWDN); + } + + gQrng = qrng; + + // qrng initialization + + ret = gpio_request(gQrng->GPIO_PWDN, "QRNG_GPIO_PWDN"); + if (ret < 0) { + PERR("Unable to request GPIO_PWDN : %d\n", ret); + goto err; + } + + gQrng->QRNG_READY_STATUS = QRNG_DEV_READY_FLAG; + + // set pwdn and ldo enable + if(qrng_pwdn(0) < 0) { + PERR("Failed to set qrng_pwdn : 0"); + return QRNG_INF_ERR; + } + + gQrng->QRNG_PWDN_STATUS = 0; + + kbuf = kzalloc(CHUNK_SIZE, GFP_KERNEL); + if (kbuf == NULL){ + PERR("kzalloc failed\n"); + ret = -ENOMEM; + goto err; + } + + // misc device + qrng_misc.parent = qrng->dev; + ret = misc_register (&qrng_misc); + if (ret){ + PERR("misc_register error: %d\n", ret); + goto err; + } + PINFO("misc_register: ok\n"); + + memset(&qrng_rng, 0x00, sizeof (struct hwrng)); + qrng_rng.name = "qrng"; + qrng_rng.read = qrng_rng_read; +// qrng_rng.quality = CONFIG_HW_RANDOM_IDQ250C2_QUALITY; + + // hwrng device + ret = hwrng_register(&qrng_rng); + if (ret){ + PERR("hwrng_register: %d\n", ret); + goto err_hwrng; + } + + PINFO("hwrng_register: ok\n"); + + if(gQrng == NULL) { + PERR("gQrng is null\n"); + goto err_hwrng; + } + + return 0; + +err_hwrng: + misc_deregister(&qrng_misc); +err: + PERR("Probe failed"); + gpio_free(qrng->GPIO_PWDN); + gQrng = NULL; + kzfree(qrng); + return ret; +} + +static int qrng_i2c_remove(struct i2c_client *client) +{ + struct device *dev = &(client->dev); + QRNG_DEV *qrng = dev_get_drvdata(dev); + + PLINE; + +#ifdef CONFIG_HW_RANDOM_IDQ250C2_TIMER + kerneltimer_exit(&timer); +#endif + + hwrng_unregister(&qrng_rng); + misc_deregister(&qrng_misc); + + gpio_free(qrng->GPIO_PWDN); + + gQrng = NULL; + if(qrng) + kzfree(qrng); + + if(kbuf) + kzfree(kbuf); + + return 0; +} + + +static const struct i2c_device_id qrng_i2c_id[] = { + {DEVICE_NAME, 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, qrng_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id qrng_of_table[] = { + {.compatible = "qrng,qrng-i2c"}, + {}, +}; +MODULE_DEVICE_TABLE(of, qrng_of_table); +#endif + +static struct i2c_driver qrng_i2c_driver = { + .driver = { + .name = DEVICE_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(qrng_of_table), + }, + .probe = qrng_i2c_probe, + .remove = qrng_i2c_remove, + .id_table = qrng_i2c_id, +}; + +// --------------------------------------------------------------------------- +// module init / exit +// --------------------------------------------------------------------------- + + + +static int __init qrng_mod_init(void) +{ + int ret = 0; + + PLINE; + + ret = i2c_add_driver(&qrng_i2c_driver); + + if (ret){ + PERR("i2c_add_driver error: %d\n", ret); + return ret; + } else { + PINFO("i2c_add_driver ok\n"); + } + return ret; +} + +static void __exit qrng_mod_exit(void) +{ + PLINE; + i2c_del_driver(&qrng_i2c_driver); + + +} + +module_init(qrng_mod_init); +module_exit(qrng_mod_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hankoo Frank Lee "); +MODULE_DESCRIPTION("QRNG-IDQ250C2 Random Nubmer Generator (RNG) driver"); + +// --------------------------------------------------------------------------- +// end of this +// --------------------------------------------------------------------------- diff --git a/drivers/misc/regulator_vibrator_mmi/Android.mk b/drivers/misc/regulator_vibrator_mmi/Android.mk new file mode 100644 index 000000000000..b1234cdb2a5e --- /dev/null +++ b/drivers/misc/regulator_vibrator_mmi/Android.mk @@ -0,0 +1,13 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(VIBRATOR_NOISE_CAMERA), true) + KBUILD_OPTIONS += CONFIG_VIBRATOR_NOISE_CAMERA=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := regulator_vibrator_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/misc/regulator_vibrator_mmi/Kbuild b/drivers/misc/regulator_vibrator_mmi/Kbuild new file mode 100644 index 000000000000..11f504ca1cdb --- /dev/null +++ b/drivers/misc/regulator_vibrator_mmi/Kbuild @@ -0,0 +1,12 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +ifneq ($(filter m y,$(CONFIG_VIBRATOR_NOISE_CAMERA)),) + EXTRA_CFLAGS += -DCONFIG_VIBRATOR_NOISE_CAMERA +endif + +regulator_vibrator_mmi-y += moto_regulator_vibrator.o + +obj-m += regulator_vibrator_mmi.o + diff --git a/drivers/misc/regulator_vibrator_mmi/Makefile b/drivers/misc/regulator_vibrator_mmi/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/misc/regulator_vibrator_mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/misc/regulator_vibrator_mmi/moto_regulator_vibrator.c b/drivers/misc/regulator_vibrator_mmi/moto_regulator_vibrator.c new file mode 100644 index 000000000000..fd0c47b95549 --- /dev/null +++ b/drivers/misc/regulator_vibrator_mmi/moto_regulator_vibrator.c @@ -0,0 +1,636 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * Define vibration periods: default(5sec), min(50ms), max(15sec) and + * overdrive(30ms). + */ +#define EXT_VIB_MIN_PLAY_MS 50 +#define EXT_VIB_PLAY_MS 5000 +#define EXT_VIB_MAX_PLAY_MS 15000 + +#ifdef CONFIG_VIBRATOR_NOISE_CAMERA +extern int mot_actuator_on_vibrate_start(void); +extern int mot_actuator_on_vibrate_stop(void); +#endif + +struct vib_ldo_chip { + struct led_classdev cdev; + struct mutex lock; + struct hrtimer stop_timer; + struct work_struct vib_work; + + int state; + u64 vib_play_ms; + + int vcc_2v6_Pin; + int vcc_3v0_Pin; + + int pwr_by_gpio; + struct regulator *vcc; + u32 regulator_voltage_max; + u32 regulator_voltage_min; + + /*for long/short vibrator*/ + bool dis_short_long; + int dis_long_ms; + + u32 long_voltage_supply; + u32 short_voltage_supply; + u32 long_play_threshold; + bool pwr_by_regulator; +}; + +static void ext_vib_work(struct work_struct *work) +{ + struct vib_ldo_chip *chip = container_of(work, struct vib_ldo_chip, + vib_work); + int ret = 0; + + static bool regulator_en_flag = false; + if (chip->state) { + if (chip->pwr_by_gpio) { + if (chip->dis_short_long) { + pr_warn("vib in dis short and long, play ms=%lld, dis_longms=%d\n",chip->vib_play_ms, chip->dis_long_ms); + if (chip->vib_play_ms > chip->dis_long_ms) { + ret = gpio_direction_output(chip->vcc_2v6_Pin, chip->state); +#ifdef CONFIG_VIBRATOR_NOISE_CAMERA + mot_actuator_on_vibrate_start(); +#endif + } + else { + ret = gpio_direction_output(chip->vcc_3v0_Pin, chip->state); + ret = gpio_direction_output(chip->vcc_2v6_Pin, chip->state); + } + } + else { + ret = gpio_direction_output(chip->vcc_3v0_Pin, chip->state); + ret = gpio_direction_output(chip->vcc_2v6_Pin, chip->state); + } + } + else if (!regulator_en_flag) { + ret = regulator_enable(chip->vcc); + regulator_en_flag = true; + } + if (ret == 0) + hrtimer_start(&chip->stop_timer, + ms_to_ktime(chip->vib_play_ms), + HRTIMER_MODE_REL); + } + else { + if (chip->pwr_by_gpio) { + gpio_direction_output(chip->vcc_2v6_Pin, chip->state); + gpio_direction_output(chip->vcc_3v0_Pin, chip->state); +#ifdef CONFIG_VIBRATOR_NOISE_CAMERA + if (chip->dis_short_long) { + if (chip->vib_play_ms > chip->dis_long_ms) { + mot_actuator_on_vibrate_stop(); + } + } +#endif + } + else if (regulator_en_flag) { + regulator_en_flag = false; + ret = regulator_disable(chip->vcc); + } + } + if (chip->pwr_by_gpio) + { + pr_info("vib:chip->state = %d,vib_play_ms=%lld\n",chip->state, + chip->vib_play_ms); + } + else + { + pr_info("vib:chip->state = %d,vib_play_ms=%lld vib_vol=%d\n",chip->state, + chip->vib_play_ms, regulator_get_voltage(chip->vcc)); + } +} + +static enum hrtimer_restart vib_stop_timer(struct hrtimer *timer) +{ + struct vib_ldo_chip *chip = container_of(timer, struct vib_ldo_chip, + stop_timer); + + chip->state = 0; + schedule_work(&chip->vib_work); + return HRTIMER_NORESTART; +} + +static ssize_t ext_vib_show_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip, + cdev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", chip->state); +} + +static ssize_t ext_vib_store_state(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + /* At present, nothing to do with setting state */ + return count; +} + +static ssize_t ext_vib_show_duration(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip, + cdev); + ktime_t time_rem; + s64 time_ms = 0; + + if (hrtimer_active(&chip->stop_timer)) { + time_rem = hrtimer_get_remaining(&chip->stop_timer); + time_ms = ktime_to_ms(time_rem); + } + + return scnprintf(buf, PAGE_SIZE, "%lld\n", time_ms); +} + +static ssize_t ext_vib_store_duration(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip, + cdev); + u32 val; + int ret; + static bool long_vib_flag = true; + + ret = kstrtouint(buf, 0, &val); + if (ret < 0) + return ret; + + /* setting 0 on duration is NOP for now */ + if (val <= 0) + return count; + + if (val < EXT_VIB_MIN_PLAY_MS) + val = EXT_VIB_MIN_PLAY_MS; + + if (val > EXT_VIB_MAX_PLAY_MS) + val = EXT_VIB_MAX_PLAY_MS; + + if (chip->pwr_by_regulator) { + if((val >= chip->long_play_threshold) && long_vib_flag){ + chip->regulator_voltage_max = chip->long_voltage_supply; + chip->regulator_voltage_min = chip->long_voltage_supply; + ret = regulator_set_voltage(chip->vcc, chip->regulator_voltage_min, chip->regulator_voltage_max); + if (ret) + pr_info("set vcc regulator voltage failed\n"); + long_vib_flag = false; + } + if((val < chip->long_play_threshold) && (!long_vib_flag)){ + chip->regulator_voltage_max = chip->short_voltage_supply; + chip->regulator_voltage_min = chip->short_voltage_supply; + ret = regulator_set_voltage(chip->vcc, chip->regulator_voltage_min, chip->regulator_voltage_max); + if (ret) + pr_info("set vcc regulator voltage failed\n"); + long_vib_flag = true; + } + } + + mutex_lock(&chip->lock); + chip->vib_play_ms = val; + mutex_unlock(&chip->lock); + + return count; +} + +static ssize_t ext_vib_show_activate(struct device *dev, + struct device_attribute *attr, char *buf) +{ + /* For now nothing to show */ + return scnprintf(buf, PAGE_SIZE, "%d\n", 0); +} + +static ssize_t ext_vib_store_activate(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip, + cdev); + u32 val; + int ret; + + ret = kstrtouint(buf, 0, &val); + if (ret < 0) + return ret; + + if (val != 0 && val != 1) + return count; + + mutex_lock(&chip->lock); + hrtimer_cancel(&chip->stop_timer); + chip->state = val; + pr_info("vib: state = %d, time = %llu ms\n", chip->state, chip->vib_play_ms); + mutex_unlock(&chip->lock); + + schedule_work(&chip->vib_work); + + return count; +} + +static int vcc_2v6 = 0; +static int vcc_3v0 = 0; +static ssize_t ext_vib_show_en(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + return scnprintf(buf, PAGE_SIZE, "vcc_2v6=%d\n", vcc_2v6); +} + +static ssize_t ext_vib_store_en(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip, + cdev); + u32 val; + int ret; + + ret = kstrtouint(buf, 0, &val); + if (ret < 0) + return ret; + + mutex_lock(&chip->lock); + vcc_2v6 = val; + if(val) + ret = gpio_direction_output(chip->vcc_2v6_Pin, val); + else + ret = gpio_direction_output(chip->vcc_2v6_Pin, val); + + mutex_unlock(&chip->lock); + return count; +} + +static ssize_t ext_vib_show_v3en(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + return scnprintf(buf, PAGE_SIZE, "vcc_3v0=%d\n", vcc_3v0); +} + +static ssize_t ext_vib_store_v3en(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip, + cdev); + u32 val; + int ret; + + ret = kstrtouint(buf, 0, &val); + if (ret < 0) + return ret; + + mutex_lock(&chip->lock); + vcc_3v0 = val; + if(val){ + ret = gpio_direction_output(chip->vcc_3v0_Pin, val); + ret = gpio_direction_output(chip->vcc_2v6_Pin, val); + } + else{ + ret = gpio_direction_output(chip->vcc_3v0_Pin, val); + ret = gpio_direction_output(chip->vcc_2v6_Pin, val); + } + + mutex_unlock(&chip->lock); + return count; +} + +static ssize_t ext_vib_show_vcc21(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip, + cdev); + + return scnprintf(buf, PAGE_SIZE, "regulator_voltage = %d\n", chip->regulator_voltage_max); +} + +static ssize_t ext_vib_store_vcc21(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct led_classdev *cdev = dev_get_drvdata(dev); + struct vib_ldo_chip *chip = container_of(cdev, struct vib_ldo_chip, + cdev); + u32 val; + int ret; + + ret = kstrtouint(buf, 0, &val); + if (ret < 0) + return ret; + + mutex_lock(&chip->lock); + chip->regulator_voltage_max = val; + chip->regulator_voltage_min = val; + ret = regulator_set_voltage(chip->vcc, chip->regulator_voltage_min, + chip->regulator_voltage_max); + mutex_unlock(&chip->lock); + return count; +} + +static struct device_attribute ext_vib_attrs[] = { + __ATTR(state, 0664, ext_vib_show_state, ext_vib_store_state), + __ATTR(duration, 0664, ext_vib_show_duration, ext_vib_store_duration), + __ATTR(activate, 0664, ext_vib_show_activate, ext_vib_store_activate), + __ATTR(en, 0664, ext_vib_show_en, ext_vib_store_en), + __ATTR(v3en, 0664, ext_vib_show_v3en, ext_vib_store_v3en), + __ATTR(vcc21, 0664, ext_vib_show_vcc21, ext_vib_store_vcc21), +}; + +static int ext_vib_parse_dt(struct device *dev, struct vib_ldo_chip *chip) +{ + int ret; + u32 voltage_supply[2]; + u32 current_supply; + u32 long_play_threshold; + u32 voltage_supply_regulator; + + chip->pwr_by_gpio = of_property_read_bool(dev->of_node, "vib-gpio-vcc-enable"); + if (chip->pwr_by_gpio) + { + chip->vcc_2v6_Pin = of_get_named_gpio(dev->of_node, "vib,gpio_2v6_en", 0); + if (!gpio_is_valid(chip->vcc_2v6_Pin)) + { + pr_err("vib: vcc_2v6_Pin gpio is invalid \n"); + return -ENODEV; + } + ret = gpio_request(chip->vcc_2v6_Pin, "vcc_2v6_Pin"); + if (ret < 0) + { + pr_err("vib: vcc_2v6_Pin Request gpio fail ret = %d]\n", ret); + return ret; + } + ret = gpio_direction_output(chip->vcc_2v6_Pin, 0); + if(ret < 0){ + pr_err("vib:vcc_2v6_Pin gpio direction set fail ret = %d\n",ret); + return ret; + } + pr_info("vib: vcc_2v6_Pin gpio num is %d \n", chip->vcc_2v6_Pin); + + chip->vcc_3v0_Pin = of_get_named_gpio(dev->of_node, "vib,gpio_3v0_en", 0); + if (!gpio_is_valid(chip->vcc_3v0_Pin)) + { + pr_err("vib: vcc_3v0_Pin gpio is invalid \n"); + return -ENODEV; + } + ret = gpio_request(chip->vcc_3v0_Pin, "vcc_3v0_Pin"); + if (ret < 0) + { + pr_err("vib: vcc_3v0_Pin Request gpio fail ret = %d]\n", ret); + return ret; + } + ret = gpio_direction_output(chip->vcc_3v0_Pin, 0); + if(ret < 0){ + pr_err("vib: vcc_3v0_Pin gpio direction set fail ret = %d\n",ret); + return ret; + } + pr_info("vib: vcc_3v0_Pin gpio num is %d \n", chip->vcc_3v0_Pin); + + chip->dis_short_long = of_property_read_bool(dev->of_node, + "qcom,vib-dis-short-long"); + if (chip->dis_short_long) { + pr_warn("read dis_short_long true"); + ret = of_property_read_u32(dev->of_node, "qcom,vib-dis-short-long-val", + &chip->dis_long_ms); + if (ret < 0) { + pr_err("qcom,vib-dis-short-long-val property read failed, ret=%d\n",ret); + return ret; + } + pr_warn("read dis_long_ms=%d\n", chip->dis_long_ms); + } + } + else + { + chip->pwr_by_regulator = of_property_read_bool(dev->of_node, + "vib,pwr-by-regulator-enable"); + if (chip->pwr_by_regulator) { + + ret = of_property_read_u32_array(dev->of_node, "vib,long-vcc-voltage", &voltage_supply_regulator, 1); + if (ret < 0) + { + pr_info(" fail to get long vcc regulator voltage \n"); + return -ENODEV; + } + chip->long_voltage_supply = voltage_supply_regulator; + ret = of_property_read_u32_array(dev->of_node, "vib,short-vcc-voltage", &voltage_supply_regulator, 1); + if (ret < 0) + { + pr_info(" fail to get short vcc regulator voltage \n"); + return -ENODEV; + } + chip->short_voltage_supply = voltage_supply_regulator; + chip->regulator_voltage_max = chip->short_voltage_supply; + chip->regulator_voltage_min = chip->short_voltage_supply; + + ret = of_property_read_u32_array(dev->of_node, "vib,long-play-threshold", &long_play_threshold, 1); + if (ret < 0) + { + pr_info(" fail to get long_play_threshold \n"); + return -ENODEV; + } + chip->long_play_threshold = long_play_threshold; + pr_info(" vcc regulator voltage get long vib = %d, short vib = %d long_play_threshold = %d\n", + chip->long_voltage_supply, chip->short_voltage_supply, chip->long_play_threshold); + } else { + ret = of_property_read_u32_array(dev->of_node, "vib,vcc-voltage", voltage_supply, 2); + if (ret < 0) + { + pr_info("%s: fail to get vcc regulator voltage \n", __func__); + return -ENODEV; + } + + pr_info("vib: vcc regulator voltage get Max = %d, Min = %d \n",voltage_supply[1], voltage_supply[0]); + chip->regulator_voltage_max = voltage_supply[1]; + chip->regulator_voltage_min = voltage_supply[0]; + } + + ret = of_property_read_u32_array(dev->of_node, "vib,vcc-current", ¤t_supply, 1); + if (ret < 0) { + pr_info("%s: fail to get vcc regulator current\n", __func__);\ + /* 300mA as default */ + current_supply = 300000; + } + else + pr_info("vib: vcc regulator current = %d\n", current_supply); + + chip->vcc = devm_regulator_get(dev, "vib_vcc"); + + if (IS_ERR(chip->vcc)) + { + ret = PTR_ERR(chip->vcc); + pr_info("%s: get vcc regulator failed %d \n", __func__, ret); + return ret; + } + + if (regulator_count_voltages(chip->vcc) > 0) + { + ret = regulator_set_voltage(chip->vcc, chip->regulator_voltage_min, chip->regulator_voltage_max); + if (ret) + pr_info("%s: set vcc regulator voltage failed %d \n", __func__, ret); + + ret = regulator_set_load(chip->vcc, current_supply); + if (ret) + pr_info("%s: set vcc regulator current failed %d \n", __func__, ret); + } + } + + return ret; +} + +/* Dummy functions for brightness */ +static enum led_brightness ext_vib_brightness_get(struct led_classdev *cdev) +{ + return 0; +} + +static void ext_vib_brightness_set(struct led_classdev *cdev, + enum led_brightness level) +{ +} + +static int ext_vibrator_ldo_suspend(struct device *dev) +{ + struct vib_ldo_chip *chip = dev_get_drvdata(dev); + + mutex_lock(&chip->lock); + hrtimer_cancel(&chip->stop_timer); + cancel_work_sync(&chip->vib_work); + if (chip->pwr_by_gpio) + { + gpio_direction_output(chip->vcc_2v6_Pin, 0); + gpio_direction_output(chip->vcc_3v0_Pin, 0); + } + else + regulator_disable(chip->vcc); + mutex_unlock(&chip->lock); + + return 0; +} +static SIMPLE_DEV_PM_OPS(ext_vibrator_ldo_pm_ops, ext_vibrator_ldo_suspend, + NULL); + +static int ext_vibrator_ldo_probe(struct platform_device *pdev) +{ + struct vib_ldo_chip *chip; + int i, ret; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + ret = ext_vib_parse_dt(&pdev->dev, chip); + if (ret < 0) { + pr_err("vib: couldn't parse device tree, ret=%d\n", ret); + return ret; + } + + chip->vib_play_ms = EXT_VIB_PLAY_MS; + mutex_init(&chip->lock); + INIT_WORK(&chip->vib_work, ext_vib_work); + + hrtimer_init(&chip->stop_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + chip->stop_timer.function = vib_stop_timer; + + dev_set_drvdata(&pdev->dev, chip); + + chip->cdev.name = "vibrator"; + chip->cdev.brightness_get = ext_vib_brightness_get; + chip->cdev.brightness_set = ext_vib_brightness_set; + chip->cdev.max_brightness = 100; + ret = devm_led_classdev_register(&pdev->dev, &chip->cdev); + if (ret < 0) { + pr_err("vib: Error in registering led class device, ret=%d\n", ret); + goto fail; + } + + for (i = 0; i < ARRAY_SIZE(ext_vib_attrs); i++) { + ret = sysfs_create_file(&chip->cdev.dev->kobj, + &ext_vib_attrs[i].attr); + if (ret < 0) { + dev_err(&pdev->dev, "Error in creating sysfs file, ret=%d\n", + ret); + goto sysfs_fail; + } + } + + pr_info("vib: Vibrator LDO successfully \n"); + + return 0; + +sysfs_fail: + for (--i; i >= 0; i--) + sysfs_remove_file(&chip->cdev.dev->kobj, + &ext_vib_attrs[i].attr); +fail: + mutex_destroy(&chip->lock); + dev_set_drvdata(&pdev->dev, NULL); + return ret; +} + +static int ext_vibrator_ldo_remove(struct platform_device *pdev) +{ + struct vib_ldo_chip *chip = dev_get_drvdata(&pdev->dev); + int i = ARRAY_SIZE(ext_vib_attrs); + + hrtimer_cancel(&chip->stop_timer); + cancel_work_sync(&chip->vib_work); + + if (chip->pwr_by_gpio){ + gpio_free(chip->vcc_2v6_Pin); + gpio_free(chip->vcc_3v0_Pin); + } + else + devm_regulator_put(chip->vcc); + + for (--i; i >= 0; i--) + sysfs_remove_file(&chip->cdev.dev->kobj, + &ext_vib_attrs[i].attr); + + mutex_destroy(&chip->lock); + dev_set_drvdata(&pdev->dev, NULL); + + return 0; +} + +static const struct of_device_id vibrator_ldo_match_table[] = { + { .compatible = "ext-vibrator-ldo" }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, vibrator_ldo_match_table); + +static struct platform_driver ext_vibrator_ldo_driver = { + .driver = { + .name = "ext-vibrator-ldo", + .of_match_table = vibrator_ldo_match_table, + .pm = &ext_vibrator_ldo_pm_ops, + }, + .probe = ext_vibrator_ldo_probe, + .remove = ext_vibrator_ldo_remove, +}; +module_platform_driver(ext_vibrator_ldo_driver); + +MODULE_DESCRIPTION("EXT Vibrator-LDO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/sx9310/Android.mk b/drivers/misc/sx9310/Android.mk new file mode 100644 index 000000000000..7ed44578a5f1 --- /dev/null +++ b/drivers/misc/sx9310/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := sx931x_triple.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/sx9310/Kbuild b/drivers/misc/sx9310/Kbuild new file mode 100644 index 000000000000..7c4b9351c0b6 --- /dev/null +++ b/drivers/misc/sx9310/Kbuild @@ -0,0 +1,19 @@ +KERNEL_DIR_PATH = $(shell find $(TOP)/kernel/ -name "msm-*" | head -1 | grep -i "kernel/msm-*") + +KERNEL_VERSION_NAME = $(shell basename $(KERNEL_DIR_PATH)) + +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +ifneq (,$(KERNEL_DIR_PATH)) + EXTRA_CFLAGS += -I$(TOP)/kernel/$(KERNEL_VERSION_NAME)/drivers/base +else + EXTRA_CFLAGS += -I$(TOP)/kernel/drivers/base +endif + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../sensors/Module.symvers + +sx931x_triple-y := sx9310_triple.o + +obj-m += sx931x_triple.o diff --git a/drivers/misc/sx9310/Makefile b/drivers/misc/sx9310/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/misc/sx9310/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/misc/sx9310/sx9310_triple.c b/drivers/misc/sx9310/sx9310_triple.c new file mode 100644 index 000000000000..b473a861808d --- /dev/null +++ b/drivers/misc/sx9310/sx9310_triple.c @@ -0,0 +1,1754 @@ +/* + * file sx9310_sar.c + * brief SX9310 Driver for two channel SAP using + * + * Driver for the SX9310 + * Copyright (c) 2015-2016 Semtech Corp + * + * 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. + */ +#define DEBUG +#define DRIVER_NAME "sx9310" +#define USE_SENSORS_CLASS +#define USE_KERNEL_SUSPEND + +#define MAX_WRITE_ARRAY_SIZE 32 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* main struct, interrupt,init,pointers */ +#include +#include "base.h" + +#define IDLE 0 +#define ACTIVE 1 +#define S_PROX 1 +#define S_BODY 2 + +#define SX9310_DEBUG 1 +#define LOG_TAG "SX9310 " + +#if SX9310_DEBUG +#define LOG_INFO(fmt, args...) pr_info(LOG_TAG fmt, ##args) +#else +#define LOG_INFO(fmt, args...) +#endif + +#define LOG_DBG(fmt, args...) pr_debug(LOG_TAG fmt, ##args) +#define LOG_ERR(fmt, args...) pr_err(LOG_TAG fmt, ##args) + +static int sx9310_debug_enable; +module_param_named( + debug_enable, sx9310_debug_enable, int, S_IRUSR | S_IWUSR +); + +static int last_val; +static int mEnabled; +static int programming_done; +psx93XX_t sx9310_sar_ptr; + +/** + * struct sx9310 + * Specialized struct containing input event data, platform data, and + * last cap state read if needed. + */ +typedef struct sx9310 { + pbuttonInformation_t pbuttonInformation; + psx9310_platform_data_t hw; /* specific platform data settings */ +} sx9310_t, *psx9310_t; + +static void ForcetoTouched(psx93XX_t this) +{ + psx9310_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + struct _buttonInfo *pCurrentButton = NULL; + + pDevice = this->pDevice; + if (this && pDevice) { + LOG_INFO("ForcetoTouched()\n"); + + pCurrentButton = pDevice->pbuttonInformation->buttons; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + pCurrentButton->state = ACTIVE; + last_val = 1; + if (mEnabled) { + input_report_abs(input_top, ABS_DISTANCE, 1); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, 1); + input_sync(input_bottom); + } + LOG_INFO("Leaving ForcetoTouched()\n"); + } +} + +/** + * fn static int write_register(psx93XX_t this, u8 address, u8 value) + * brief Sends a write register to the device + * param this Pointer to main parent struct + * param address 8-bit register address + * param value 8-bit register value to write to address + * return Value from i2c_master_send + */ +static int write_register(psx93XX_t this, u8 address, u8 value) +{ + struct i2c_client *i2c = 0; + char buffer[2]; + int returnValue = 0; + + buffer[0] = address; + buffer[1] = value; + returnValue = -ENOMEM; + if (this && this->bus) { + i2c = this->bus; + + returnValue = i2c_master_send(i2c, buffer, 2); + if (sx9310_debug_enable) + LOG_DBG("write_register Addr: \ + 0x%x Val: 0x%x Return: %d\n", + address, value, returnValue); + } + if (returnValue < 0) { + ForcetoTouched(this); + LOG_INFO("Write_register-ForcetoTouched()\n"); + } + return returnValue; +} + +/** + * fn static int read_register(psx93XX_t this, u8 address, u8 *value) + * brief Reads a register's value from the device + * param this Pointer to main parent struct + * param address 8-Bit address to read from + * param value Pointer to 8-bit value to save register value to + * return Value from i2c_smbus_read_byte_data if < 0. else 0 + */ +static int read_register(psx93XX_t this, u8 address, u8 *value) +{ + struct i2c_client *i2c = 0; + s32 returnValue = 0; + + if (this && value && this->bus) { + i2c = this->bus; + returnValue = i2c_smbus_read_byte_data(i2c, address); + if (sx9310_debug_enable) + LOG_DBG("read_register Addr: 0x%x Return: 0x%x\n", + address, returnValue); + if (returnValue >= 0) { + *value = returnValue; + return 0; + } else { + return returnValue; + } + } + ForcetoTouched(this); + LOG_INFO("read_register-ForcetoTouched()\n"); + return -ENOMEM; +} + +/** + * detect if sx9310 exist or not + * return 1 if chip exist else return 0 + */ +static int sx9310_detect(struct i2c_client *client) +{ + s32 returnValue = 0, i; + u8 address = SX9310_WHOAMI; + u8 value_9310 = 0x01; + u8 value_9311 = 0x02; + + if (client) { + for (i = 0; i < 3; i++) { + returnValue = i2c_smbus_read_byte_data(client, address); + LOG_INFO("sx9310 read_register for %d time Addr:\ + 0x%x Return: 0x%x\n", + i, address, returnValue); + if (value_9310 == returnValue || + value_9311 == returnValue) { + LOG_INFO("sx9310 detect success !\n"); + return 1; + } + } + } + LOG_ERR("sx9310 detect failed!!!\n"); + return 0; +} + +/** + * brief Perform a manual offset calibration + * param this Pointer to main parent struct + * return Value return value from the write register + */ +static int manual_offset_calibration(psx93XX_t this) +{ + s32 returnValue = 0; + + returnValue = write_register(this, SX9310_IRQSTAT_REG, 0xFF); + return returnValue; +} + +/** + * brief sysfs show function for manual calibration which currently just + * returns register value. + */ +static ssize_t manual_offset_calibration_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 reg_value = 0; + psx93XX_t this = dev_get_drvdata(dev); + + LOG_INFO("Reading IRQSTAT_REG\n"); + read_register(this, SX9310_IRQSTAT_REG, ®_value); + return scnprintf(buf, PAGE_SIZE, "%d\n", reg_value); +} + +/* brief sysfs store function for manual calibration */ +static ssize_t manual_offset_calibration_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + psx93XX_t this = dev_get_drvdata(dev); + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + if (val) { + LOG_INFO("Performing manual_offset_calibration()\n"); + manual_offset_calibration(this); + } + return count; +} + +static DEVICE_ATTR(calibrate, 0644, manual_offset_calibration_show, + manual_offset_calibration_store); +static struct attribute *sx9310_attributes[] = { + &dev_attr_calibrate.attr, + NULL, +}; +static struct attribute_group sx9310_attr_group = { + .attrs = sx9310_attributes, +}; + +/** + * fn static int read_regStat(psx93XX_t this) + * brief Shortcut to read what caused interrupt. + * details This is to keep the drivers a unified + * function that will read whatever register(s) + * provide information on why the interrupt was caused. + * param this Pointer to main parent struct + * return If successful, Value of bit(s) that cause interrupt, else 0 + */ +static int read_regStat(psx93XX_t this) +{ + u8 data = 0; + + if (this) { + if (read_register(this, SX9310_IRQSTAT_REG, &data) == 0) + return (data & 0x00FF); + } + return 0; +} + +static void read_rawData(psx93XX_t this) +{ + u8 msb=0, lsb=0; + u8 csx; + s32 useful; + s32 average; + s32 diff; + u16 offset; + if(this){ + for(csx =0; csx 32767) + useful -= 65536; + if (average > 32767) + average -= 65536; + if (diff > 4095) + diff -= 8192; + if (sx9310_debug_enable) + LOG_DBG("sx9310 [CS: %d] Useful = %d Average = %d, DIFF = %d Offset = %d \n",csx,useful,average,diff,offset); + } + } +} + +/** + * brief Initialize I2C config from platform data + * param this Pointer to main parent struct + * + */ +static void hw_init(psx93XX_t this) +{ + psx9310_t pDevice = 0; + psx9310_platform_data_t pdata = 0; + int i = 0; + /* configure device */ + LOG_INFO("Going to Setup I2C Registers\n"); + pDevice = this->pDevice; + pdata = pDevice->hw; + if (this && pDevice && pdata) { + while (i < pdata->i2c_reg_num) { + /* Write all registers/values contained in i2c_reg */ + if (sx9310_debug_enable) + LOG_DBG("Going to Write Reg: \ + 0x%x Value: 0x%x\n", + pdata->pi2c_reg[i].reg, + pdata->pi2c_reg[i].val); + write_register(this, pdata->pi2c_reg[i].reg, + pdata->pi2c_reg[i].val); + i++; + } + + write_register(this, SX9310_CPS_CTRL0_REG, + this->board->cust_prox_ctrl0); + write_register(this, SX9310_CPSRD, + this->board->cust_raw_data_channel); + } else { + LOG_ERR("ERROR! platform data 0x%p\n", pDevice->hw); + /* Force to touched if error */ + ForcetoTouched(this); + LOG_INFO("Hardware_init-ForcetoTouched()\n"); + } +} + +/** + * fn static int initialize(psx93XX_t this) + * brief Performs all initialization needed to configure the device + * param this Pointer to main parent struct + * return Last used command's return value (negative if error) + */ +static int initialize(psx93XX_t this) +{ + int ret; + + if (this) { + /* prepare reset by disabling any irq handling */ + this->irq_disabled = 1; + disable_irq(this->irq); + /* perform a reset */ + ret = write_register(this, SX9310_SOFTRESET_REG, + SX9310_SOFTRESET); + if (ret < 0) + goto error_exit; + /* wait until the reset has finished by monitoring NIRQ */ + LOG_INFO("Software Reset. Waiting device back to continue.\n"); + /* just sleep for awhile instead of using a loop + * with reading irq status */ + msleep(300); + /* + * while(this->get_nirq_low && this->get_nirq_low()) + * { read_regStat(this); } + */ + LOG_INFO("Device back from the reset, continuing. NIRQ = %d\n", + this->get_nirq_low(this->board->irq_gpio)); + hw_init(this); + msleep(100); /* make sure everything is running */ + ret = manual_offset_calibration(this); + if (ret < 0) + goto error_exit; + /* re-enable interrupt handling */ + enable_irq(this->irq); + this->irq_disabled = 0; + + /* make sure no interrupts are pending + * since enabling irq will only + * work on next falling edge */ + read_regStat(this); + LOG_INFO("Exiting initialize(). NIRQ = %d\n", + this->get_nirq_low(this->board->irq_gpio)); + programming_done = ACTIVE; + return 0; + } + return -ENOMEM; + +error_exit: + programming_done = IDLE; + return ret; +} + +/** + * brief Handle what to do when a touch occurs + * param this Pointer to main parent struct + */ +static void touchProcess(psx93XX_t this) +{ + int counter = 0; + u8 i = 0; + int numberOfButtons = 0; + psx9310_t pDevice = NULL; + struct _buttonInfo *buttons = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + struct _buttonInfo *pCurrentButton = NULL; + struct sx9310_platform_data *board; + + pDevice = this->pDevice; + board = this->board; + if (this && pDevice) { + if (sx9310_debug_enable) + LOG_DBG("Inside touchProcess()\n"); + read_register(this, SX9310_STAT0_REG, &i); + + buttons = pDevice->pbuttonInformation->buttons; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + numberOfButtons = pDevice->pbuttonInformation->buttonSize; + + if (unlikely((buttons == NULL) || (input_top == NULL) || + (input_bottom == NULL))) { + LOG_ERR("ERROR!! buttons or input NULL!!!\n"); + return; + } + + for (counter = 0; counter < numberOfButtons; counter++) { + pCurrentButton = &buttons[counter]; + if (pCurrentButton == NULL) { + LOG_ERR("ERR!current button index: %d NULL!\n", + counter); + return; /* ERRORR!!!! */ + } + switch (pCurrentButton->state) { + case IDLE: /* Button is being in far state! */ + if ((i & pCurrentButton->mask) == + pCurrentButton->mask) { + LOG_INFO("CS %d State=BODY.\n", + counter); + if (board->cap_channel_top == counter) { + input_report_abs(input_top, + ABS_DISTANCE, 2); + input_sync(input_top); + } else if (board->cap_channel_bottom == + counter) { + input_report_abs(input_bottom, + ABS_DISTANCE, 2); + input_sync(input_bottom); + } + pCurrentButton->state = S_BODY; + last_val = 2; + } else if ((i & pCurrentButton->mask) == + (pCurrentButton->mask & 0x0f)) { + LOG_INFO("CS %d State=PROX.\n", + counter); + if (board->cap_channel_top == counter) { + input_report_abs(input_top, + ABS_DISTANCE, 1); + input_sync(input_top); + } else if (board->cap_channel_bottom == + counter) { + input_report_abs(input_bottom, + ABS_DISTANCE, 1); + input_sync(input_bottom); + } + pCurrentButton->state = S_PROX; + last_val = 0; + } else { + if (sx9310_debug_enable) + LOG_DBG("CS %d still in" + "IDLE State.\n", + counter); + } + break; + case S_PROX: /* Button is being in proximity! */ + if ((i & pCurrentButton->mask) == + pCurrentButton->mask) { + LOG_INFO("CS %d State=BODY.\n", + counter); + if (board->cap_channel_top == counter) { + input_report_abs(input_top, + ABS_DISTANCE, 2); + input_sync(input_top); + } else if (board->cap_channel_bottom == + counter) { + input_report_abs(input_bottom, + ABS_DISTANCE, 2); + input_sync(input_bottom); + } + pCurrentButton->state = S_BODY; + last_val = 2; + } else if ((i & pCurrentButton->mask) == + (pCurrentButton->mask & 0x0f)) { + if (sx9310_debug_enable) + LOG_INFO("CS %d still in PROX State.\n", + counter); + } else{ + if (sx9310_debug_enable) + LOG_DBG("CS %d State=IDLE.\n", + counter); + if (board->cap_channel_top == counter) { + input_report_abs(input_top, + ABS_DISTANCE, 0); + input_sync(input_top); + } else if (board->cap_channel_bottom == + counter) { + input_report_abs(input_bottom, + ABS_DISTANCE, 0); + input_sync(input_bottom); + } + pCurrentButton->state = IDLE; + last_val = 0; + } + break; + case S_BODY: /* Button is being in 0mm! */ + if ((i & pCurrentButton->mask) == + pCurrentButton->mask) { + if (sx9310_debug_enable) + LOG_INFO("CS %d still in BODY State.\n", + counter); + } else if ((i & pCurrentButton->mask) == + (pCurrentButton->mask & 0x0f)) { + LOG_INFO("CS %d State=PROX.\n", + counter); + if (board->cap_channel_top == counter) { + input_report_abs(input_top, + ABS_DISTANCE, 1); + input_sync(input_top); + } else if (board->cap_channel_bottom + == counter) { + input_report_abs(input_bottom, + ABS_DISTANCE, 1); + input_sync(input_bottom); + } + pCurrentButton->state = S_PROX; + last_val = 1; + } else{ + if (sx9310_debug_enable) + LOG_DBG("CS %d State=IDLE.\n", + counter); + if (board->cap_channel_top == counter) { + input_report_abs(input_top, + ABS_DISTANCE, 0); + input_sync(input_top); + } else if (board->cap_channel_bottom + == counter) { + input_report_abs(input_bottom, + ABS_DISTANCE, 0); + input_sync(input_bottom); + } + pCurrentButton->state = IDLE; + last_val = 0; + } + break; + /* Shouldn't be here, device only + * allowed ACTIVE or IDLE */ + default: + break; + }; + } + if (sx9310_debug_enable) + LOG_DBG("Leaving touchProcess()\n"); + } +} + +static int sx9310_get_nirq_state(unsigned irq_gpio) +{ + if (irq_gpio) + return !gpio_get_value(irq_gpio); + + LOG_ERR("sx9310 irq_gpio is not set."); + return -EINVAL; +} + +static struct _totalButtonInformation smtcButtonInformation = { + .buttons = psmtcButtons, + .buttonSize = ARRAY_SIZE(psmtcButtons), +}; + +/** + *fn static void sx9310_reg_setup_init(struct i2c_client *client) + *brief read reg val form dts + * reg_array_len for regs needed change num + * data_array_val's format + */ +static void sx9310_reg_setup_init(struct i2c_client *client) +{ + u32 data_array_len = 0; + u32 *data_array; + int ret, i, j; + struct device_node *np = client->dev.of_node; + + ret = of_property_read_u32(np, "reg_array_len", &data_array_len); + if (ret < 0) { + LOG_ERR("data_array_len read error"); + return; + } + data_array = kmalloc(data_array_len * 2 * sizeof(u32), GFP_KERNEL); + ret = of_property_read_u32_array(np, "reg_array_val", + data_array, + data_array_len*2); + if (ret < 0) { + LOG_ERR("data_array_val read error"); + return; + } + for (i = 0; i < ARRAY_SIZE(sx9310_i2c_reg_setup); i++) { + for (j = 0; j < data_array_len*2; j += 2) { + if (data_array[j] == sx9310_i2c_reg_setup[i].reg) { + sx9310_i2c_reg_setup[i].val = data_array[j+1]; + LOG_INFO("read dtsi 0x%02x:0x%02x set reg\n", + data_array[j], data_array[j+1]); + } + } + } + kfree(data_array); + /*for test*/ + /*for (i = 0; i < ARRAY_SIZE(sx9310_i2c_reg_setup); i++) { + dev_err(&client->dev, "%x:%x", + sx9310_i2c_reg_setup[i].reg, + sx9310_i2c_reg_setup[i].val); + }*/ +} + +static void sx9310_platform_data_of_init(struct i2c_client *client, + psx9310_platform_data_t pplatData) +{ + struct device_node *np = client->dev.of_node; + u32 scan_period, sensor_en, raw_data_channel; + u32 cap_channel_top, cap_channel_bottom; + int ret; + + client->irq = of_get_gpio(np, 0); + pplatData->irq_gpio = client->irq; + + ret = of_property_read_u32(np, "cap,use_channel", &sensor_en); + if (ret) + sensor_en = DUMMY_USE_CHANNEL; + + ret = of_property_read_u32(np, "cap,use_channel_top", &cap_channel_top); + if (ret) + sensor_en = DUMMY_USE_CHANNEL; + + ret = of_property_read_u32(np, "cap,use_channel_bottom", + &cap_channel_bottom); + if (ret) + sensor_en = DUMMY_USE_CHANNEL; + + ret = of_property_read_u32(np, "cap,scan_period", &scan_period); + if (ret) + scan_period = DUMMY_SCAN_PERIOD; + + ret = of_property_read_u32(np, "cap,raw_data_channel", + &raw_data_channel); + if (ret) + raw_data_channel = DUMMY_RAW_DATA_CHANNEL; + + pplatData->cust_prox_ctrl0 = (scan_period << 4) | sensor_en; + pplatData->cust_raw_data_channel = raw_data_channel; + pplatData->cap_channel_top = (int)cap_channel_top; + pplatData->cap_channel_bottom = (int)cap_channel_bottom; + + pplatData->get_is_nirq_low = sx9310_get_nirq_state; + pplatData->init_platform_hw = NULL; + /* pointer to an exit function. Here in case needed in the future */ + /* + *.exit_platform_hw = sx9310_exit_ts, + */ + pplatData->exit_platform_hw = NULL; + sx9310_reg_setup_init(client); + pplatData->pi2c_reg = sx9310_i2c_reg_setup; + pplatData->i2c_reg_num = ARRAY_SIZE(sx9310_i2c_reg_setup); + + pplatData->pbuttonInformation = &smtcButtonInformation; +} + +static ssize_t capsense_reset_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 8, "%d\n", programming_done); +} + +static ssize_t capsense_reset_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + psx93XX_t this = sx9310_sar_ptr; + psx9310_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + + pDevice = this->pDevice; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + + if (!count || (this == NULL)) + return -EINVAL; + + if (!strncmp(buf, "reset", 5) || !strncmp(buf, "1", 1)) + write_register(this, SX9310_IRQSTAT_REG, 0xFF); + + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + + return count; +} + +static CLASS_ATTR(reset, 0660, capsense_reset_show, capsense_reset_store); + +static ssize_t capsense_enable_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 8, "%d\n", mEnabled); +} + +static ssize_t capsense_enable_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + psx93XX_t this = sx9310_sar_ptr; + psx9310_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + + pDevice = this->pDevice; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + + if (!count || (this == NULL)) + return -EINVAL; + + if ((!strncmp(buf, "1", 1)) && (mEnabled == 0)) { + LOG_INFO("enable cap sensor\n"); + initialize(this); + + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + mEnabled = 1; + } else if ((!strncmp(buf, "0", 1)) && (mEnabled == 1)) { + LOG_INFO("disable cap sensor\n"); + + write_register(this, SX9310_CPS_CTRL0_REG, 0x10); + write_register(this, SX9310_IRQ_ENABLE_REG, 0x00); + + input_report_abs(input_top, ABS_DISTANCE, -1); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, -1); + input_sync(input_bottom); + mEnabled = 0; + } else { + LOG_ERR("unknown enable symbol\n"); + } + + return count; +} + +#ifdef USE_SENSORS_CLASS +static int capsensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + psx93XX_t this = sx9310_sar_ptr; + psx9310_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + + pDevice = this->pDevice; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + + if ((enable == 1) && (mEnabled == 0)) { + LOG_INFO("enable cap sensor\n"); + initialize(this); + + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + mEnabled = 1; + } else if ((enable == 0) && (mEnabled == 1)) { + LOG_INFO("disable cap sensor\n"); + + write_register(this, SX9310_CPS_CTRL0_REG, 0x10); + write_register(this, SX9310_IRQ_ENABLE_REG, 0x00); + + input_report_abs(input_top, ABS_DISTANCE, -1); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, -1); + input_sync(input_bottom); + mEnabled = 0; + } else { + LOG_ERR("unknown enable symbol\n"); + } + + return 0; +} +#endif + +static CLASS_ATTR(enable, 0660, capsense_enable_show, capsense_enable_store); + +static ssize_t reg_dump_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u8 reg_value = 0; + u16 offset_value = 0; + u16 diff_value = 0; + psx93XX_t this = sx9310_sar_ptr; + char *p = buf; + + if (this->read_flag) { + this->read_flag = 0; + read_register(this, this->read_reg, ®_value); + p += snprintf(p, PAGE_SIZE, "%02x\n", reg_value); + return (p-buf); + } + + read_register(this, SX9310_IRQ_ENABLE_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "ENABLE(0x%02x)=0x%02x\n", + SX9310_IRQ_ENABLE_REG, reg_value); + + read_register(this, SX9310_IRQFUNC_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "IRQFUNC(0x%02x)=0x%02x\n", + SX9310_IRQFUNC_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL0_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL0(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL0_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL1_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL1(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL1_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL2_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL2(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL2_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL3_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL3(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL3_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL4_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL4(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL4_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL5_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL5(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL5_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL6_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL6(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL6_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL7_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL7(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL7_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL8_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL8(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL8_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL9_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL9(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL9_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL10_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL10(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL10_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL11_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL11(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL11_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL12_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL12(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL12_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL13_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL13(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL13_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL14_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL14(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL14_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL15_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL15(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL15_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL16_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL16(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL16_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL17_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL17(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL17_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL18_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL18(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL18_REG, reg_value); + + read_register(this, SX9310_CPS_CTRL19_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "CTRL19(0x%02x)=0x%02x\n", + SX9310_CPS_CTRL19_REG, reg_value); + + read_register(this, SX9310_SAR_CTRL0_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "SCTRL0(0x%02x)=0x%02x\n", + SX9310_SAR_CTRL0_REG, reg_value); + + read_register(this, SX9310_SAR_CTRL1_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "SCTRL1(0x%02x)=0x%02x\n", + SX9310_SAR_CTRL1_REG, reg_value); + + read_register(this, SX9310_SAR_CTRL2_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "SCTRL2(0x%02x)=0x%02x\n", + SX9310_SAR_CTRL2_REG, reg_value); + + read_register(this, SX9310_CPSRD, ®_value); + p += snprintf(p, PAGE_SIZE, "CPSD(0x%02x)=0x%02x\n", + SX9310_CPSRD, reg_value); + + read_register(this, SX9310_USEMSB, ®_value); + p += snprintf(p, PAGE_SIZE, "USEMSB(0x%02x)=0x%02x\n", + SX9310_USEMSB, reg_value); + + read_register(this, SX9310_USELSB, ®_value); + p += snprintf(p, PAGE_SIZE, "USELSB(0x%02x)=0x%02x\n", + SX9310_USELSB, reg_value); + + read_register(this, SX9310_AVGMSB, ®_value); + p += snprintf(p, PAGE_SIZE, "AVGMSB(0x%02x)=0x%02x\n", + SX9310_AVGMSB, reg_value); + + read_register(this, SX9310_AVGLSB, ®_value); + p += snprintf(p, PAGE_SIZE, "AVGLSB(0x%02x)=0x%02x\n", + SX9310_AVGLSB, reg_value); + + read_register(this, SX9310_DIFFMSB, ®_value); + p += snprintf(p, PAGE_SIZE, "DIFFMSB(0x%02x)=0x%02x\n", + SX9310_DIFFMSB, reg_value); + diff_value = reg_value; + diff_value <<= 8; + + read_register(this, SX9310_DIFFLSB, ®_value); + p += snprintf(p, PAGE_SIZE, "DIFFLSB(0x%02x)=0x%02x\n", + SX9310_DIFFLSB, reg_value); + diff_value += reg_value; + + read_register(this, SX9310_OFFSETMSB, ®_value); + p += snprintf(p, PAGE_SIZE, "OFFSETMSB(0x%02x)=0x%02x\n", + SX9310_OFFSETMSB, reg_value); + offset_value = reg_value; + offset_value <<= 8; + + read_register(this, SX9310_OFFSETLSB, ®_value); + p += snprintf(p, PAGE_SIZE, "OFFSETLSB(0x%02x)=0x%02x\n", + SX9310_OFFSETLSB, reg_value); + offset_value += reg_value; + + read_register(this, SX9310_SARMSB, ®_value); + p += snprintf(p, PAGE_SIZE, "SARMSB(0x%02x)=0x%02x\n", + SX9310_SARMSB, reg_value); + + read_register(this, SX9310_SARLSB, ®_value); + p += snprintf(p, PAGE_SIZE, "SARLSB(0x%02x)=0x%02x\n", + SX9310_SARLSB, reg_value); + + reg_value = gpio_get_value(this->board->irq_gpio); + p += snprintf(p, PAGE_SIZE, "NIRQ=%d\n", reg_value); + + p += snprintf(p, PAGE_SIZE, "diff_value=0x%04x\n", + diff_value); + p += snprintf(p, PAGE_SIZE, "offset_value=0x%04x\n", + offset_value); + + return (p-buf); +} + +static ssize_t reg_dump_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + psx93XX_t this = sx9310_sar_ptr; + unsigned int val, reg, opt; + + if (strcmp("select_ch0\n", buf) == 0) { + write_register(this, SX9310_CPSRD, 0x00); + } else if (strcmp("select_ch1\n", buf) == 0) { + write_register(this, SX9310_CPSRD, 0x01); + } else if (strcmp("select_ch2\n", buf) == 0) { + write_register(this, SX9310_CPSRD, 0x02); + } else if (strcmp("calibrate\n", buf) == 0) { + write_register(this, SX9310_IRQSTAT_REG, 0xff); + } else if (sscanf(buf, "%x,%x,%x", ®, &val, &opt) == 3) { + LOG_DBG("%s, read reg = 0x%02x\n", __func__, *(u8 *)®); + this->read_reg = *((u8 *)®); + this->read_flag = 1; + } else if (sscanf(buf, "%x,%x", ®, &val) == 2) { + LOG_DBG("%s,reg = 0x%02x, val = 0x%02x\n", + __func__, *(u8 *)®, *(u8 *)&val); + write_register(this, *((u8 *)®), *((u8 *)&val)); + } + + return count; +} + +static CLASS_ATTR(reg, 0660, reg_dump_show, reg_dump_store); + +static void ps_notify_callback_work(struct work_struct *work) +{ + psx93XX_t this = container_of(work, sx93XX_t, ps_notify_work); + psx9310_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + int ret = 0; + + pDevice = this->pDevice; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + + /* lenovo-sw wunan3 2018-08-01 porting IKSWO-111359: input: update sx9310 calibrate method begin */ + mutex_lock(&this->mutex); + if (mEnabled) { + LOG_INFO("Usb insert,going to force calibrate\n"); + ret = write_register(this, SX9310_CPS_CTRL0_REG, 0x20); + if (ret < 0) + LOG_ERR(" Usb insert,disable cap sensor failed\n"); + msleep(60); + ret = write_register(this, SX9310_CPS_CTRL0_REG, 0x2f); + if (ret < 0) + LOG_ERR(" Usb insert,enabel cap sensor failed\n"); + } + mutex_unlock(&this->mutex); + /* lenovo-sw wunan3 2018-08-01 porting IKSWO-111359: input: update sx9310 calibrate method end */ + + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); +} + +static int ps_get_state(struct power_supply *psy, bool *present) +{ + union power_supply_propval pval = { 0 }; + int retval; + + retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, + &pval); + if (retval) { + LOG_ERR("%s psy get property failed\n", psy->desc->name); + return retval; + } + *present = (pval.intval) ? true : false; + LOG_INFO("%s is %s\n", psy->desc->name, + (*present) ? "present" : "not present"); + return 0; +} + +static int ps_notify_callback(struct notifier_block *self, + unsigned long event, void *p) +{ + psx93XX_t this = container_of(self, sx93XX_t, ps_notif); + struct power_supply *psy = p; + bool present; + int retval; + + if ((event == PSY_EVENT_PROP_ADDED || event == PSY_EVENT_PROP_CHANGED) + && psy && psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, "usb", sizeof("usb"))) { + LOG_INFO("ps notification: event = %lu\n", event); + retval = ps_get_state(psy, &present); + if (retval) { + LOG_ERR("psy get property failed\n"); + return retval; + } + + if (event == PSY_EVENT_PROP_CHANGED) { + if (this->ps_is_present == present) { + LOG_INFO("ps present state not change\n"); + return 0; + } + } + this->ps_is_present = present; + schedule_work(&this->ps_notify_work); + } + + return 0; +} + +static struct class capsense_class = { + .name = "capsense", + .owner = THIS_MODULE, +}; + +/** + * fn static int sx9310_probe(struct i2c_client *client, const struct i2c_device_id *id) + * brief Probe function + * param client pointer to i2c_client + * param id pointer to i2c_device_id + * return Whether probe was successful + */ +static int sx9310_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + psx93XX_t this = 0; + psx9310_t pDevice = 0; + psx9310_platform_data_t pplatData = 0; + int ret = 0; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + struct power_supply *psy = NULL; + + LOG_INFO("sx9310_probe()\n"); + + pplatData = kzalloc(sizeof(struct sx9310_platform_data), GFP_KERNEL); + sx9310_platform_data_of_init(client, pplatData); + client->dev.platform_data = pplatData; + + if (!pplatData) { + LOG_ERR("platform data is required!\n"); + return -EINVAL; + } + + pplatData->cap_vdd = regulator_get(&client->dev, "cap_vdd"); + if (IS_ERR(pplatData->cap_vdd)) { + if (PTR_ERR(pplatData->cap_vdd) == -EPROBE_DEFER) { + ret = PTR_ERR(pplatData->cap_vdd); + LOG_ERR("%s: Failed to get regulator\n", __func__); + goto err_vdd_defer; + } + } else { + int error = regulator_enable(pplatData->cap_vdd); + if (error) { + LOG_ERR("%s: Error %d enable regulator\n", + __func__, error); + goto err_vdd_enable; + } + pplatData->cap_vdd_en = true; + LOG_INFO("cap_vdd regulator is %s\n", + regulator_is_enabled(pplatData->cap_vdd) ? + "on" : "off"); + } + + pplatData->cap_svdd = regulator_get(&client->dev, "cap_svdd"); + if (!IS_ERR(pplatData->cap_svdd)) { + ret = regulator_enable(pplatData->cap_svdd); + if (ret) { + LOG_ERR("Failed to enable cap_svdd\n"); + goto err_svdd_enable; + } + pplatData->cap_svdd_en = true; + LOG_INFO("cap_svdd regulator is %s\n", + regulator_is_enabled(pplatData->cap_svdd) ? + "on" : "off"); + } else { + ret = PTR_ERR(pplatData->cap_svdd); + if (ret == -EPROBE_DEFER) + goto err_svdd_defer; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -EIO; + + /* detect if sx9310 exist or not */ + if (sx9310_detect(client) == 0) + return -ENODEV; + + /* create memory for main struct */ + this = kzalloc(sizeof(sx93XX_t), GFP_KERNEL); + LOG_INFO("\t Initialized Main Memory: 0x%p\n", this); + + if (this) { + /* In case we need to reinitialize data + * (e.q. if suspend reset device) */ + this->init = initialize; + /* shortcut to read status of interrupt */ + this->refreshStatus = read_regStat; + /* pointer to function from platform data to get pendown + * (1->NIRQ=0, 0->NIRQ=1) */ + this->get_nirq_low = pplatData->get_is_nirq_low; + /* save irq in case we need to reference it */ + this->irq = gpio_to_irq(client->irq); + /* do we need to create an irq timer after interrupt ? */ + this->useIrqTimer = 0; + this->board = pplatData; + /* Setup function to call on corresponding reg irq source bit */ + if (MAX_NUM_STATUS_BITS >= 8) { + this->statusFunc[0] = 0; /* TXEN_STAT */ + this->statusFunc[1] = 0; /* UNUSED */ + this->statusFunc[2] = 0; /* UNUSED */ + this->statusFunc[3] = read_rawData; /* CONV_STAT */ + this->statusFunc[4] = 0; /* COMP_STAT */ + this->statusFunc[5] = touchProcess; /* RELEASE_STAT */ + this->statusFunc[6] = touchProcess; /* TOUCH_STAT */ + this->statusFunc[7] = 0; /* RESET_STAT */ + } + + /* setup i2c communication */ + this->bus = client; + i2c_set_clientdata(client, this); + + /* record device struct */ + this->pdev = &client->dev; + + /* create memory for device specific struct */ + this->pDevice = pDevice = kzalloc(sizeof(sx9310_t), GFP_KERNEL); + LOG_INFO("\t Initialized Device Specific Memory: 0x%p\n", + pDevice); + sx9310_sar_ptr = this; + if (pDevice) { + /* for accessing items in user data (e.g. calibrate) */ + ret = sysfs_create_group(&client->dev.kobj, + &sx9310_attr_group); + + + /* Check if we hava a platform + * initialization function to call*/ + if (pplatData->init_platform_hw) + pplatData->init_platform_hw(); + + /* Add Pointer to main platform data struct */ + pDevice->hw = pplatData; + + /* Initialize the button information + * initialized with keycodes */ + pDevice->pbuttonInformation = + pplatData->pbuttonInformation; + + /* Create the input device */ + input_top = input_allocate_device(); + if (!input_top) + return -ENOMEM; + + /* Set all the keycodes */ + __set_bit(EV_ABS, input_top->evbit); + input_set_abs_params(input_top, ABS_DISTANCE, + -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_top = + input_top; + input_top->name = "SX9310 Cap Touch top"; + if (input_register_device(input_top)) { + LOG_ERR("add top cap touch unsuccess\n"); + return -ENOMEM; + } + /* Create the input device */ + input_bottom = input_allocate_device(); + if (!input_bottom) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_bottom->evbit); + input_set_abs_params(input_bottom, ABS_DISTANCE, + -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_bottom = + input_bottom; + /* save the input pointer and finish initialization */ + input_bottom->name = "SX9310 Cap Touch bottom"; + if (input_register_device(input_bottom)) { + LOG_ERR("add bottom cap touch unsuccess\n"); + return -ENOMEM; + } + } + + ret = class_register(&capsense_class); + if (ret < 0) { + LOG_ERR("Create fsys class failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_reset); + if (ret < 0) { + LOG_ERR("Create reset file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_enable); + if (ret < 0) { + LOG_ERR("Create enable file failed (%d)\n", ret); + return ret; + } + + ret = class_create_file(&capsense_class, &class_attr_reg); + if (ret < 0) { + LOG_ERR("Create reg file failed (%d)\n", ret); + return ret; + } + + /*restore sys/class/capsense label*/ + kobject_uevent(&capsense_class.p->subsys.kobj, KOBJ_CHANGE); +#ifdef USE_SENSORS_CLASS + sensors_capsensor_top_cdev.sensors_enable = + capsensor_set_enable; + sensors_capsensor_top_cdev.sensors_poll_delay = NULL; + ret = sensors_classdev_register(&input_top->dev, + &sensors_capsensor_top_cdev); + if (ret < 0) + LOG_ERR("create top cap sensor_class file \ + failed (%d)\n", ret); + sensors_capsensor_bottom_cdev.sensors_enable = + capsensor_set_enable; + sensors_capsensor_bottom_cdev.sensors_poll_delay = NULL; + ret = sensors_classdev_register(&input_bottom->dev, + &sensors_capsensor_bottom_cdev); + if (ret < 0) + LOG_ERR("create bottom cap sensor_class file \ + failed (%d)\n", ret); +#endif + + sx93XX_sar_init(this); + + write_register(this, SX9310_CPS_CTRL0_REG, 0x10); + write_register(this, SX9310_IRQ_ENABLE_REG, 0x00); + mEnabled = 0; + + INIT_WORK(&this->ps_notify_work, ps_notify_callback_work); + this->ps_notif.notifier_call = ps_notify_callback; + ret = power_supply_reg_notifier(&this->ps_notif); + if (ret) { + LOG_ERR( + "Unable to register ps_notifier: %d\n", ret); + goto err_ps_notifier; + } + + psy = power_supply_get_by_name("usb"); + if (psy) { + ret = ps_get_state(psy, &this->ps_is_present); + if (ret) { + LOG_ERR( + "psy get property failed rc=%d\n", + ret); + goto free_ps_notifier; + } + } + + return 0; + } + return -ENOMEM; + +free_ps_notifier: + power_supply_unreg_notifier(&this->ps_notif); + +err_ps_notifier: + LOG_ERR("%s input free device.\n", __func__); + input_free_device(input_top); + input_free_device(input_bottom); + + regulator_disable(pplatData->cap_svdd); + +err_svdd_enable: + regulator_put(pplatData->cap_svdd); + +err_svdd_defer: + regulator_disable(pplatData->cap_vdd); + +err_vdd_enable: + regulator_put(pplatData->cap_vdd); + +err_vdd_defer: + kfree(pplatData); + + return ret; +} + +/** + * fn static int sx9310_remove(struct i2c_client *client) + * brief Called when device is to be removed + * param client Pointer to i2c_client struct + * return Value from sx93XX_sar_remove() + */ +static int sx9310_remove(struct i2c_client *client) +{ + psx9310_platform_data_t pplatData = 0; + psx9310_t pDevice = 0; + psx93XX_t this = i2c_get_clientdata(client); + + pDevice = this->pDevice; + pplatData = client->dev.platform_data; + if (this && pDevice) { + power_supply_unreg_notifier(&this->ps_notif); + cancel_work_sync(&this->ps_notify_work); + +#ifdef USE_SENSORS_CLASS + sensors_classdev_unregister(&sensors_capsensor_top_cdev); + sensors_classdev_unregister(&sensors_capsensor_bottom_cdev); +#endif + input_unregister_device(pDevice->pbuttonInformation->input_top); + input_unregister_device( + pDevice->pbuttonInformation->input_bottom); + + if (gpio_is_valid(pplatData ->irq_gpio)) + gpio_free(pplatData ->irq_gpio); + if (this->board->cap_svdd_en) { + regulator_disable(this->board->cap_svdd); + regulator_put(this->board->cap_svdd); + } + + if (this->board->cap_vdd_en) { + regulator_disable(this->board->cap_vdd); + regulator_put(this->board->cap_vdd); + } + + sysfs_remove_group(&client->dev.kobj, &sx9310_attr_group); + class_unregister(&capsense_class); + if (pplatData && pplatData->exit_platform_hw) + pplatData->exit_platform_hw(); + kfree(this->pDevice); + } + return sx93XX_sar_remove(this); +} +#if defined(USE_KERNEL_SUSPEND) +/*====================================================*/ +/***** Kernel Suspend *****/ +static int sx9310_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + psx93XX_t this = i2c_get_clientdata(client); + + sx93XX_suspend(this); + return 0; +} +/***** Kernel Resume *****/ +static int sx9310_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + psx93XX_t this = i2c_get_clientdata(client); + + sx93XX_resume(this); + return 0; +} +/*====================================================*/ +#endif + +#ifdef CONFIG_OF +static const struct of_device_id synaptics_rmi4_match_tbl[] = { + { .compatible = "semtech," DRIVER_NAME }, + { }, +}; +MODULE_DEVICE_TABLE(of, synaptics_rmi4_match_tbl); +#endif + +#if defined(USE_KERNEL_SUSPEND) +static const struct dev_pm_ops sx9310_pm_ops = { + .suspend = sx9310_suspend, + .resume = sx9310_resume, +}; +#endif + +static struct i2c_device_id sx9310_idtable[] = { + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sx9310_idtable); + +static struct i2c_driver sx9310_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, +#if defined(USE_KERNEL_SUSPEND) + .pm = &sx9310_pm_ops, +#endif + }, + .id_table = sx9310_idtable, + .probe = sx9310_probe, + .remove = sx9310_remove, +}; +static int __init sx9310_init(void) +{ + return i2c_add_driver(&sx9310_driver); +} +static void __exit sx9310_exit(void) +{ + i2c_del_driver(&sx9310_driver); +} + +module_init(sx9310_init); +module_exit(sx9310_exit); + +MODULE_AUTHOR("Semtech Corp. (http://www.semtech.com/)"); +MODULE_DESCRIPTION("SX9310 Capacitive Touch Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1"); + +#ifdef USE_THREADED_IRQ +static void sx93XX_process_interrupt(psx93XX_t this, u8 nirqlow) +{ + int status = 0; + int counter = 0; + + if (!this) { + LOG_ERR("sx93XX_worker_func, NULL sx93XX_t\n"); + return; + } + /* since we are not in an interrupt don't need to disable irq. */ + status = this->refreshStatus(this); + counter = -1; + if (sx9310_debug_enable) + LOG_DBG("Worker - Refresh Status %d\n", status); + + while ((++counter) < MAX_NUM_STATUS_BITS) { /* counter start from MSB */ + if (((status >> counter) & 0x01) + && (this->statusFunc[counter])) { + if (sx9310_debug_enable) + LOG_DBG("Function %d Pointer Found. Calling\n" + , counter); + this->statusFunc[counter](this); + } + } + if (unlikely(this->useIrqTimer && nirqlow)) { + /* In case we need to send a timer for example on a touchscreen + * checking penup, perform this here + */ + cancel_delayed_work(&this->dworker); + schedule_delayed_work(&this->dworker, + msecs_to_jiffies(this->irqTimeout)); + LOG_INFO("Schedule Irq timer"); + } +} + + +static void sx93XX_worker_func(struct work_struct *work) +{ + psx93XX_t this = 0; + + if (work) { + this = container_of(work, sx93XX_t, dworker.work); + if (!this) { + LOG_ERR("sx93XX_worker_func, NULL sx93XX_t\n"); + return; + } + if ((!this->get_nirq_low) + || (!this->get_nirq_low(this->board->irq_gpio))) { + /* only run if nirq is high */ + sx93XX_process_interrupt(this, 0); + } + } else { + LOG_ERR("sx93XX_worker_func, NULL work_struct\n"); + } +} +static irqreturn_t sx93XX_interrupt_thread(int irq, void *data) +{ + psx93XX_t this = 0; + + this = data; + + mutex_lock(&this->mutex); + if (sx9310_debug_enable) + LOG_DBG("sx93XX_irq\n"); + if ((!this->get_nirq_low) || this->get_nirq_low(this->board->irq_gpio)) + sx93XX_process_interrupt(this, 1); + else { + if (sx9310_debug_enable) + LOG_DBG("sx93XX_irq - nirq read high\n"); + } + mutex_unlock(&this->mutex); + return IRQ_HANDLED; +} +#else +static void sx93XX_schedule_work(psx93XX_t this, unsigned long delay) +{ + unsigned long flags; + + if (this) { + LOG_INFO("sx93XX_schedule_work()\n"); + spin_lock_irqsave(&this->lock, flags); + /* Stop any pending penup queues */ + cancel_delayed_work(&this->dworker); + /* + * after waiting for a delay, this put the job + * in the kernel-global workqueue. + * so no need to create new thread in work queue. + */ + schedule_delayed_work(&this->dworker, delay); + spin_unlock_irqrestore(&this->lock, flags); + } else + LOG_ERR("sx93XX_schedule_work, NULL psx93XX_t\n"); +} + +static irqreturn_t sx93XX_irq(int irq, void *pvoid) +{ + psx93XX_t this = 0; + + if (pvoid) { + this = (psx93XX_t)pvoid; + if (sx9310_debug_enable) + LOG_DBG("sx93XX_irq\n"); + if ((!this->get_nirq_low) + || this->get_nirq_low(this->board->irq_gpio)) { + if (sx9310_debug_enable) + LOG_DBG("sx93XX_irq - Schedule Work\n"); + sx93XX_schedule_work(this, 0); + } else { + if (sx9310_debug_enable) + LOG_DBG("sx93XX_irq - nirq read high\n"); + } + } else + LOG_ERR("sx93XX_irq, NULL pvoid\n"); + return IRQ_HANDLED; +} + +static void sx93XX_worker_func(struct work_struct *work) +{ + psx93XX_t this = 0; + int status = 0; + int counter = 0; + u8 nirqLow = 0; + + if (work) { + this = container_of(work, sx93XX_t, dworker.work); + + if (!this) { + LOG_ERR("sx93XX_worker_func, NULL sx93XX_t\n"); + return; + } + if (unlikely(this->useIrqTimer)) { + if ((!this->get_nirq_low) + || this->get_nirq_low(this->board->irq_gpio)) + nirqLow = 1; + } + /* since we are not in an interrupt + * don't need to disable irq. */ + status = this->refreshStatus(this); + counter = -1; + if (sx9310_debug_enable) + LOG_DBG("Worker - Refresh Status %d\n", status); + /* counter start from MSB */ + while ((++counter) < MAX_NUM_STATUS_BITS) { + if (((status >> counter) & 0x01) + && (this->statusFunc[counter])) { + LOG_INFO("Function %d Pointer Found. Calling\n" + , counter); + this->statusFunc[counter](this); + } + } + if (unlikely(this->useIrqTimer && nirqLow)) { + /* Early models and if RATE=0 for newer models + * require a penup timer; + * Queue up the function again for checking on penup */ + sx93XX_schedule_work(this, + msecs_to_jiffies(this->irqTimeout)); + } + } else { + LOG_ERR("sx93XX_worker_func, NULL work_struct\n"); + } +} +#endif + +void sx93XX_suspend(psx93XX_t this) +{ + if (this) { + write_register(this,0x41,0x00); + if (sx9310_debug_enable) + LOG_INFO("sx9310 suspend: disable irq!\n"); + disable_irq(this->irq); + /* if upper layer don't disable capsensor, */ + /* we should let it enter sleep in suspend. */ + if (mEnabled) + write_register(this, SX9310_CPS_CTRL0_REG, 0x20); + } +} +void sx93XX_resume(psx93XX_t this) +{ + if (this) { + if (sx9310_debug_enable) + LOG_INFO("sx9310 resume: enable irq!\n"); +#ifdef USE_THREADED_IRQ + mutex_lock(&this->mutex); + /* Just in case need to reset any uncaught interrupts */ + sx93XX_process_interrupt(this, 0); + mutex_unlock(&this->mutex); +#else + sx93XX_schedule_work(this, 0); +#endif + /* we should let capsensor enter active in resume*/ + if (mEnabled) + write_register(this, SX9310_CPS_CTRL0_REG, 0x2f); + enable_irq(this->irq); + write_register(this,0x41,0x01); + } +} + +int sx93XX_sar_init(psx93XX_t this) +{ + int err = 0; + + if (this && this->pDevice) { +#ifdef USE_THREADED_IRQ + + /* initialize worker function */ + INIT_DELAYED_WORK(&this->dworker, sx93XX_worker_func); + + + /* initialize mutex */ + mutex_init(&this->mutex); + /* initailize interrupt reporting */ + this->irq_disabled = 0; + err = request_threaded_irq(this->irq, NULL, + sx93XX_interrupt_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + this->pdev->driver->name, + this); +#else + /* initialize spin lock */ + spin_lock_init(&this->lock); + + /* initialize worker function */ + INIT_DELAYED_WORK(&this->dworker, sx93XX_worker_func); + + /* initailize interrupt reporting */ + this->irq_disabled = 0; + err = request_irq(this->irq, sx93XX_irq, IRQF_TRIGGER_FALLING, + this->pdev->driver->name, this); +#endif + if (err) { + LOG_ERR("irq %d busy?\n", this->irq); + return err; + } +#ifdef USE_THREADED_IRQ + LOG_INFO("registered with threaded irq (%d)\n", this->irq); +#else + LOG_INFO("registered with irq (%d)\n", this->irq); +#endif + /* call init function pointer: + * this should initialize all registers */ + if (this->init) + return this->init(this); + LOG_ERR("No init function!!!!\n"); + } + return -ENOMEM; +} + +int sx93XX_sar_remove(psx93XX_t this) +{ + if (this) { + /* Cancel the Worker Func */ + cancel_delayed_work_sync(&this->dworker); + free_irq(this->irq, this); + kfree(this); + return 0; + } + return -ENOMEM; +} diff --git a/drivers/misc/sx9325/Android.mk b/drivers/misc/sx9325/Android.mk new file mode 100644 index 000000000000..4c148cdcd554 --- /dev/null +++ b/drivers/misc/sx9325/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := sx932x_sar.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/sx9325/Kbuild b/drivers/misc/sx9325/Kbuild new file mode 100644 index 000000000000..0d6870611333 --- /dev/null +++ b/drivers/misc/sx9325/Kbuild @@ -0,0 +1,19 @@ +KERNEL_DIR_PATH = $(shell find $(TOP)/kernel/ -name "msm-*" | head -1 | grep -i "kernel/msm-*") + +KERNEL_VERSION_NAME = $(shell basename $(KERNEL_DIR_PATH)) + +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +ifneq (,$(KERNEL_DIR_PATH)) + EXTRA_CFLAGS += -I$(TOP)/kernel/$(KERNEL_VERSION_NAME)/drivers/base +else + EXTRA_CFLAGS += -I$(TOP)/kernel/drivers/base +endif + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../sensors/Module.symvers + +sx932x_sar-y := sx9325_sar.o + +obj-m += sx932x_sar.o diff --git a/drivers/misc/sx9325/Makefile b/drivers/misc/sx9325/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/misc/sx9325/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/misc/sx9325/sx9325_sar.c b/drivers/misc/sx9325/sx9325_sar.c new file mode 100644 index 000000000000..cd51d2ba21c9 --- /dev/null +++ b/drivers/misc/sx9325/sx9325_sar.c @@ -0,0 +1,1690 @@ +/* + * file sx9325_sar.c + * brief SX9325 Driver for two channel SAP using + * + * Driver for the SX9325 + * Copyright (c) 2015-2016 Semtech Corp + * + * 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. + */ +#define DEBUG +#define DRIVER_NAME "sx9325" +#define USE_SENSORS_CLASS +#define USE_KERNEL_SUSPEND + +#define MAX_WRITE_ARRAY_SIZE 32 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* main struct, interrupt,init,pointers */ +#include +#include "base.h" + +#define IDLE 0 +#define ACTIVE 1 +#define S_PROX 1 +#define S_BODY 2 + +#define SX9325_DEBUG 1 +#define LOG_TAG "SX9325 " + +#if SX9325_DEBUG +#define LOG_INFO(fmt, args...) pr_info(LOG_TAG "[INFO]" "<%s><%d>"fmt, __func__, __LINE__, ##args) +#else +#define LOG_INFO(fmt, args...) +#endif + +#define LOG_DBG(fmt, args...) pr_info(LOG_TAG "[DBG]" "<%s><%d>"fmt, __func__, __LINE__, ##args) +#define LOG_ERR(fmt, args...) pr_err(LOG_TAG "[ERR]" "<%s><%d>"fmt, __func__, __LINE__, ##args) +/* lenovo-sw wunan3 IKSWP-12537 New log structure for SAR sensor begin */ +static int sx9325_debug_enable; +module_param_named( + debug_enable, sx9325_debug_enable, int, S_IRUSR | S_IWUSR +); +/* lenovo-sw wunan3 IKSWP-12537 New log structure for SAR sensor end */ + +static int last_val; +static int mEnabled; +static int programming_done; +psx93XX_t sx9325_sar_ptr; + +/** + * struct sx9325 + * Specialized struct containing input event data, platform data, and + * last cap state read if needed. + */ +typedef struct sx9325 { + pbuttonInformation_t pbuttonInformation; + psx9325_platform_data_t hw; /* specific platform data settings */ +} sx9325_t, *psx9325_t; + +static void ForcetoTouched(psx93XX_t this) +{ + psx9325_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + struct _buttonInfo *pCurrentButton = NULL; + + pDevice = this->pDevice; + if (this && pDevice) { + LOG_INFO("ForcetoTouched()\n"); + + pCurrentButton = pDevice->pbuttonInformation->buttons; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + pCurrentButton->state = ACTIVE; + last_val = 1; + if (mEnabled) { + input_report_abs(input_top, ABS_DISTANCE, 1); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, 1); + input_sync(input_bottom); + } + LOG_INFO("Leaving ForcetoTouched()\n"); + } +} + +/** + * fn static int write_register(psx93XX_t this, u8 address, u8 value) + * brief Sends a write register to the device + * param this Pointer to main parent struct + * param address 8-bit register address + * param value 8-bit register value to write to address + * return Value from i2c_master_send + */ +static int write_register(psx93XX_t this, u8 address, u8 value) +{ + struct i2c_client *i2c = 0; + char buffer[2]; + int returnValue = 0; + + buffer[0] = address; + buffer[1] = value; + returnValue = -ENOMEM; + if (this && this->bus) { + i2c = this->bus; + + returnValue = i2c_master_send(i2c, buffer, 2); + if (sx9325_debug_enable) + LOG_DBG("write_register Addr: \ + 0x%x Val: 0x%x Return: %d\n", + address, value, returnValue); + } + if (returnValue < 0) { + ForcetoTouched(this); + LOG_INFO("Write_register-ForcetoTouched()\n"); + } + return returnValue; +} + +/** + * fn static int read_register(psx93XX_t this, u8 address, u8 *value) + * brief Reads a register's value from the device + * param this Pointer to main parent struct + * param address 8-Bit address to read from + * param value Pointer to 8-bit value to save register value to + * return Value from i2c_smbus_read_byte_data if < 0. else 0 + */ +static int read_register(psx93XX_t this, u8 address, u8 *value) +{ + struct i2c_client *i2c = 0; + s32 returnValue = 0; + + if (this && value && this->bus) { + i2c = this->bus; + returnValue = i2c_smbus_read_byte_data(i2c, address); + if (sx9325_debug_enable) + LOG_DBG("read_register Addr: 0x%x Return: 0x%x\n", + address, returnValue); + if (returnValue >= 0) { + *value = returnValue; + return 0; + } else { + return returnValue; + } + } + ForcetoTouched(this); + LOG_INFO("read_register-ForcetoTouched()\n"); + return -ENOMEM; +} + +/** + * detect if sx9325 exist or not + * return 1 if chip exist else return 0 + */ +static int sx9325_detect(struct i2c_client *client) +{ + s32 returnValue = 0, i; + u8 address = SX932x_WHOAMI_REG; + u8 value_9325 = SX932x_WHOAMI_VALUE; + + if (client) { + for (i = 0; i < 3; i++) { + returnValue = i2c_smbus_read_byte_data(client, address); + LOG_INFO("sx9325 read_register for %d time Addr:\ + 0x%x Return: 0x%x\n", + i, address, returnValue); + if (value_9325 == returnValue){ + LOG_INFO("sx9325 detect success !\n"); + return 1; + } + } + } + LOG_ERR("sx9325 detect failed !!!\n"); + return 0; +} + +/** + * brief Perform a manual offset calibration + * param this Pointer to main parent struct + * return Value return value from the write register + */ +static int manual_offset_calibration(psx93XX_t this) +{ + s32 returnValue = 0; + + returnValue = write_register(this,SX932x_STAT2_REG,0x0F); + return returnValue; +} + +/** + * brief sysfs show function for manual calibration which currently just + * returns register value. + */ +static ssize_t manual_offset_calibration_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u8 reg_value = 0; + psx93XX_t this = sx9325_sar_ptr; + + LOG_INFO("Reading IRQSTAT_REG\n"); + read_register(this, SX932x_IRQSTAT_REG, ®_value); + return scnprintf(buf, PAGE_SIZE, "%d\n", reg_value); +} + +/* brief sysfs store function for manual calibration */ +static ssize_t manual_offset_calibration_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + psx93XX_t this = sx9325_sar_ptr; + unsigned long val; + + if (kstrtoul(buf, 0, &val)) + return -EINVAL; + if (val) { + LOG_INFO("Performing manual_offset_calibration()\n"); + manual_offset_calibration(this); + } + return count; +} +/** + * fn static int read_regStat(psx93XX_t this) + * brief Shortcut to read what caused interrupt. + * details This is to keep the drivers a unified + * function that will read whatever register(s) + * provide information on why the interrupt was caused. + * param this Pointer to main parent struct + * return If successful, Value of bit(s) that cause interrupt, else 0 + */ +static int read_regStat(psx93XX_t this) +{ + u8 data = 0; + + if (this) { + if (read_register(this, SX932x_IRQSTAT_REG, &data) == 0) + return (data & 0x00FF); + } + return 0; +} + +static void read_rawData(psx93XX_t this) +{ + /* lenovo-sw wunan3 IKSWP-12537 New log structure for SAR sensor begin */ + u8 msb=0, lsb=0; + u8 csx; + s32 useful; + s32 average; + s32 diff; + u16 offset; + if(this){ + for(csx =0; csx 32767) + useful -= 65536; + if (average > 32767) + average -= 65536; + if (diff > 32767) + diff -= 65536; + if (sx9325_debug_enable) + LOG_DBG("sx9325 [CS: %d] Useful = %d Average = %d, DIFF = %d Offset = %d \n",csx,useful,average,diff,offset); + } + } + /* lenovo-sw wunan3 IKSWP-12537 New log structure for SAR sensor end */ +} + +/** + * brief Initialize I2C config from platform data + * param this Pointer to main parent struct + * + */ +static void hw_init(psx93XX_t this) +{ + psx9325_t pDevice = 0; + psx9325_platform_data_t pdata = 0; + int i = 0; + /* configure device */ + LOG_INFO("Going to Setup I2C Registers\n"); + pDevice = this->pDevice; + pdata = pDevice->hw; + if (this && pDevice && pdata) { + while (i < pdata->i2c_reg_num) { + /* Write all registers/values contained in i2c_reg */ + if (sx9325_debug_enable) + LOG_DBG("Going to Write Reg: \ + 0x%x Value: 0x%x\n", + pdata->pi2c_reg[i].reg, + pdata->pi2c_reg[i].val); + write_register(this, pdata->pi2c_reg[i].reg, + pdata->pi2c_reg[i].val); + i++; + } + + write_register(this, SX932x_CTRL1_REG, + this->board->cust_prox_ctrl0); + write_register(this, SX932x_CPSRD, + this->board->cust_raw_data_channel); + } else { + LOG_ERR("ERROR! platform data 0x%p\n", pDevice->hw); + /* Force to touched if error */ + ForcetoTouched(this); + LOG_INFO("Hardware_init-ForcetoTouched()\n"); + } +} + +/** + * fn static int initialize(psx93XX_t this) + * brief Performs all initialization needed to configure the device + * param this Pointer to main parent struct + * return Last used command's return value (negative if error) + */ +static int initialize(psx93XX_t this) +{ + int ret; + + if (this) { + /* prepare reset by disabling any irq handling */ + this->irq_disabled = 1; + disable_irq(this->irq); + /* perform a reset */ + ret = write_register(this, SX932x_SOFTRESET_REG, + SX932x_SOFTRESET); + if (ret < 0) + goto error_exit; + /* wait until the reset has finished by monitoring NIRQ */ + LOG_INFO("Software Reset. Waiting device back to continue.\n"); + /* just sleep for awhile instead of using a loop + * with reading irq status */ + msleep(300); + /* + * while(this->get_nirq_low && this->get_nirq_low()) + * { read_regStat(this); } + */ + LOG_INFO("Device back from the reset, continuing. NIRQ = %d\n", + this->get_nirq_low(this->board->irq_gpio)); + hw_init(this); + msleep(100); /* make sure everything is running */ + ret = manual_offset_calibration(this); + if (ret < 0) + goto error_exit; + /* re-enable interrupt handling */ + enable_irq(this->irq); + this->irq_disabled = 0; + + /* make sure no interrupts are pending + * since enabling irq will only + * work on next falling edge */ + read_regStat(this); + LOG_INFO("Exiting initialize(). NIRQ = %d\n", + this->get_nirq_low(this->board->irq_gpio)); + programming_done = ACTIVE; + return 0; + } + return -ENOMEM; + +error_exit: + programming_done = IDLE; + return ret; +} + +/** + * brief Handle what to do when a touch occurs + * param this Pointer to main parent struct + */ +static void touchProcess(psx93XX_t this) +{ + int counter = 0; + u8 i = 0; + u8 Lsb,Msb; + int numberOfButtons = 0; + psx9325_t pDevice = NULL; + struct _buttonInfo *buttons = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + struct _buttonInfo *pCurrentButton = NULL; + struct sx9325_platform_data *board; + + pDevice = this->pDevice; + board = this->board; + if (this && pDevice) { + if (sx9325_debug_enable) + LOG_DBG("Inside touchProcess()\n"); + + read_register(this, SX932x_STAT0_REG, &Lsb); //PROX + read_register(this, SX932x_STAT1_REG, &Msb); //Body + Lsb = (u8)(Lsb&0x0F); + Msb = (u8)(Msb&0x0F); + i = (u8)((Msb<<4)|Lsb); + + buttons = pDevice->pbuttonInformation->buttons; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + numberOfButtons = pDevice->pbuttonInformation->buttonSize; + + if (unlikely((buttons == NULL) || (input_top == NULL) || + (input_bottom == NULL))) { + LOG_ERR("ERROR!! buttons or input NULL!!!\n"); + return; + } + + for (counter = 0; counter < numberOfButtons; counter++) { + pCurrentButton = &buttons[counter]; + if (pCurrentButton == NULL) { + LOG_ERR("ERR!current button index: %d NULL!\n", + counter); + return; /* ERRORR!!!! */ + } + switch (pCurrentButton->state) { + case IDLE: /* Button is being in far state! */ + if ((i & pCurrentButton->mask) == + pCurrentButton->mask) { + LOG_INFO("CS %d State=BODY.\n", + counter); + if (board->cap_channel_top == counter) { + input_report_abs(input_top, + ABS_DISTANCE, 2); + input_sync(input_top); + } else if (board->cap_channel_bottom == + counter) { + input_report_abs(input_bottom, + ABS_DISTANCE, 2); + input_sync(input_bottom); + } + pCurrentButton->state = S_BODY; + last_val = 2; + } else if ((i & pCurrentButton->mask) == + (pCurrentButton->mask & 0x0f)) { + LOG_INFO("CS %d State=PROX.\n", + counter); + if (board->cap_channel_top == counter) { + input_report_abs(input_top, + ABS_DISTANCE, 1); + input_sync(input_top); + } else if (board->cap_channel_bottom == + counter) { + input_report_abs(input_bottom, + ABS_DISTANCE, 1); + input_sync(input_bottom); + } + pCurrentButton->state = S_PROX; + last_val = 0; + } else { + if (sx9325_debug_enable) + LOG_DBG("CS %d still in" + "IDLE State.\n", + counter); + } + break; + case S_PROX: /* Button is being in proximity! */ + if ((i & pCurrentButton->mask) == + pCurrentButton->mask) { + LOG_INFO("CS %d State=BODY.\n", + counter); + if (board->cap_channel_top == counter) { + input_report_abs(input_top, + ABS_DISTANCE, 2); + input_sync(input_top); + } else if (board->cap_channel_bottom == + counter) { + input_report_abs(input_bottom, + ABS_DISTANCE, 2); + input_sync(input_bottom); + } + pCurrentButton->state = S_BODY; + last_val = 2; + } else if ((i & pCurrentButton->mask) == + (pCurrentButton->mask & 0x0f)) { + if (sx9325_debug_enable) + LOG_INFO("CS %d still in PROX State.\n", + counter); + } else{ + if (sx9325_debug_enable) + LOG_DBG("CS %d State=IDLE.\n", + counter); + if (board->cap_channel_top == counter) { + input_report_abs(input_top, + ABS_DISTANCE, 0); + input_sync(input_top); + } else if (board->cap_channel_bottom == + counter) { + input_report_abs(input_bottom, + ABS_DISTANCE, 0); + input_sync(input_bottom); + } + pCurrentButton->state = IDLE; + last_val = 0; + } + break; + case S_BODY: /* Button is being in 0mm! */ + if ((i & pCurrentButton->mask) == + pCurrentButton->mask) { + if (sx9325_debug_enable) + LOG_INFO("CS %d still in BODY State.\n", + counter); + } else if ((i & pCurrentButton->mask) == + (pCurrentButton->mask & 0x0f)) { + LOG_INFO("CS %d State=PROX.\n", + counter); + if (board->cap_channel_top == counter) { + input_report_abs(input_top, + ABS_DISTANCE, 1); + input_sync(input_top); + } else if (board->cap_channel_bottom + == counter) { + input_report_abs(input_bottom, + ABS_DISTANCE, 1); + input_sync(input_bottom); + } + pCurrentButton->state = S_PROX; + last_val = 1; + } else{ + if (sx9325_debug_enable) + LOG_DBG("CS %d State=IDLE.\n", + counter); + if (board->cap_channel_top == counter) { + input_report_abs(input_top, + ABS_DISTANCE, 0); + input_sync(input_top); + } else if (board->cap_channel_bottom + == counter) { + input_report_abs(input_bottom, + ABS_DISTANCE, 0); + input_sync(input_bottom); + } + pCurrentButton->state = IDLE; + last_val = 0; + } + break; + /* Shouldn't be here, device only + * allowed ACTIVE or IDLE */ + default: + break; + }; + } + if (sx9325_debug_enable) + LOG_DBG("Leaving touchProcess()\n"); + } +} + +static int sx9325_get_nirq_state(unsigned irq_gpio) +{ + if (irq_gpio) + return !gpio_get_value(irq_gpio); + + LOG_ERR("sx9325 irq_gpio is not set."); + return -EINVAL; +} + +static struct _totalButtonInformation smtcButtonInformation = { + .buttons = psmtcButtons, + .buttonSize = ARRAY_SIZE(psmtcButtons), +}; + +/** + *fn static void sx9325_reg_setup_init(struct i2c_client *client) + *brief read reg val form dts + * reg_array_len for regs needed change num + * data_array_val's format + */ +static void sx9325_reg_setup_init(struct i2c_client *client) +{ + u32 data_array_len = 0; + u32 *data_array; + int ret, i, j; + struct device_node *np = client->dev.of_node; + + ret = of_property_read_u32(np, "reg_array_len", &data_array_len); + if (ret < 0) { + LOG_ERR("data_array_len read error"); + return; + } + data_array = kmalloc(data_array_len * 2 * sizeof(u32), GFP_KERNEL); + ret = of_property_read_u32_array(np, "reg_array_val", + data_array, + data_array_len*2); + if (ret < 0) { + LOG_ERR("data_array_val read error"); + return; + } + for (i = 0; i < ARRAY_SIZE(sx9325_i2c_reg_setup); i++) { + for (j = 0; j < data_array_len*2; j += 2) { + if (data_array[j] == sx9325_i2c_reg_setup[i].reg) { + sx9325_i2c_reg_setup[i].val = data_array[j+1]; + LOG_INFO("read dtsi 0x%02x:0x%02x set reg\n", + data_array[j], data_array[j+1]); + } + } + } + kfree(data_array); + /*for test*/ + /*for (i = 0; i < ARRAY_SIZE(sx9325_i2c_reg_setup); i++) { + dev_err(&client->dev, "%x:%x", + sx9325_i2c_reg_setup[i].reg, + sx9325_i2c_reg_setup[i].val); + }*/ +} + +static void sx9325_platform_data_of_init(struct i2c_client *client, + psx9325_platform_data_t pplatData) +{ + struct device_node *np = client->dev.of_node; + u32 scan_period, sensor_en, raw_data_channel; + u32 cap_channel_top, cap_channel_bottom; + int ret; + + client->irq = of_get_gpio(np, 0); + pplatData->irq_gpio = client->irq; + + ret = of_property_read_u32(np, "cap,use_channel", &sensor_en); + if (ret) + sensor_en = DUMMY_USE_CHANNEL; + + ret = of_property_read_u32(np, "cap,use_channel_top", &cap_channel_top); + if (ret) + sensor_en = DUMMY_USE_CHANNEL; + + ret = of_property_read_u32(np, "cap,use_channel_bottom", + &cap_channel_bottom); + if (ret) + sensor_en = DUMMY_USE_CHANNEL; + + ret = of_property_read_u32(np, "cap,scan_period", &scan_period); + if (ret) + scan_period = DUMMY_SCAN_PERIOD; + + ret = of_property_read_u32(np, "cap,raw_data_channel", + &raw_data_channel); + if (ret) + raw_data_channel = DUMMY_RAW_DATA_CHANNEL; + + pplatData->cust_prox_ctrl0 = (scan_period << 4) | sensor_en; + pplatData->cust_raw_data_channel = raw_data_channel; + pplatData->cap_channel_top = (int)cap_channel_top; + pplatData->cap_channel_bottom = (int)cap_channel_bottom; + + pplatData->get_is_nirq_low = sx9325_get_nirq_state; + pplatData->init_platform_hw = NULL; + /* pointer to an exit function. Here in case needed in the future */ + /* + *.exit_platform_hw = sx9325_exit_ts, + */ + pplatData->exit_platform_hw = NULL; + sx9325_reg_setup_init(client); + pplatData->pi2c_reg = sx9325_i2c_reg_setup; + pplatData->i2c_reg_num = ARRAY_SIZE(sx9325_i2c_reg_setup); + + pplatData->pbuttonInformation = &smtcButtonInformation; +} +static ssize_t capsense_reset_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 8, "%d\n", programming_done); +} + +static ssize_t capsense_reset_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + psx93XX_t this = sx9325_sar_ptr; + psx9325_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + + pDevice = this->pDevice; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + + if (!count || (this == NULL)) + return -EINVAL; + + if (!strncmp(buf, "reset", 5) || !strncmp(buf, "1", 1)) + write_register(this, SX932x_STAT2_REG, 0x0F); + + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + + return count; +} + +static ssize_t capsense_enable_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return snprintf(buf, 8, "%d\n", mEnabled); +} + +static ssize_t capsense_enable_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + psx93XX_t this = sx9325_sar_ptr; + psx9325_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + + pDevice = this->pDevice; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + + if (!count || (this == NULL)) + return -EINVAL; + + if ((!strncmp(buf, "1", 1)) && (mEnabled == 0)) { + LOG_INFO("enable cap sensor\n"); + initialize(this); + + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + mEnabled = 1; + } else if ((!strncmp(buf, "0", 1)) && (mEnabled == 1)) { + LOG_INFO("disable cap sensor\n"); + + write_register(this, SX932x_CTRL1_REG, 0x10); + write_register(this, SX932x_IRQ_ENABLE_REG, 0x00); + + input_report_abs(input_top, ABS_DISTANCE, -1); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, -1); + input_sync(input_bottom); + mEnabled = 0; + } else { + LOG_ERR("unknown enable symbol\n"); + } + + return count; +} + +#ifdef USE_SENSORS_CLASS +static int capsensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + psx93XX_t this = sx9325_sar_ptr; + psx9325_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + + pDevice = this->pDevice; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + + if ((enable == 1) && (mEnabled == 0)) { + LOG_INFO("enable cap sensor\n"); + initialize(this); + + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); + mEnabled = 1; + } else if ((enable == 0) && (mEnabled == 1)) { + LOG_INFO("disable cap sensor\n"); + + write_register(this, SX932x_CTRL1_REG, 0x00); + write_register(this, SX932x_IRQ_ENABLE_REG, 0x00); + + input_report_abs(input_top, ABS_DISTANCE, -1); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, -1); + input_sync(input_bottom); + mEnabled = 0; + } else { + LOG_ERR("unknown enable symbol\n"); + } + + return 0; +} +#endif + + +static ssize_t reg_dump_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u16 i = 0; + u8 reg_value = 0; + u16 register_number = 0; + //u16 offset_value = 0; + //u16 diff_value = 0; + psx93XX_t this = sx9325_sar_ptr; + char *p = buf; + + if (this->read_flag) { + this->read_flag = 0; + read_register(this, this->read_reg, ®_value); + p += snprintf(p, PAGE_SIZE, "%02x\n", reg_value); + return (p-buf); + } + + read_register(this, SX932x_IRQSTAT_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "IRQSTAT(0x%02x)=0x%02x\n", + SX932x_IRQSTAT_REG, reg_value); + + read_register(this, SX932x_STAT0_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "STAT0(0x%02x)=0x%02x\n", + SX932x_STAT0_REG, reg_value); + + read_register(this, SX932x_STAT1_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "STAT1(0x%02x)=0x%02x\n", + SX932x_STAT1_REG, reg_value); + + read_register(this, SX932x_STAT2_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "STAT2(0x%02x)=0x%02x\n", + SX932x_STAT2_REG, reg_value); + + read_register(this, SX932x_STAT3_REG, ®_value); + p += snprintf(p, PAGE_SIZE, "STAT3(0x%02x)=0x%02x\n", + SX932x_STAT3_REG, reg_value); + + register_number = sizeof(sx9325_i2c_reg_setup)/sizeof(smtc_reg_data_t); + + for (i = 0; i < register_number; i++) + { + read_register(this, sx9325_i2c_reg_setup[i].reg, ®_value); + p += snprintf(p, PAGE_SIZE, "ENABLE(0x%02x)=0x%02x\n", + sx9325_i2c_reg_setup[i].reg, reg_value); + } + + read_register(this, SX932x_CPSRD, ®_value); + p += snprintf(p, PAGE_SIZE, "CPSRD(0x%02x)=0x%02x\n", + SX932x_CPSRD, reg_value); + + read_register(this, SX932x_USEMSB, ®_value); + p += snprintf(p, PAGE_SIZE, "USEMSB(0x%02x)=0x%02x\n", + SX932x_USEMSB, reg_value); + + read_register(this, SX932x_USELSB, ®_value); + p += snprintf(p, PAGE_SIZE, "USELSB(0x%02x)=0x%02x\n", + SX932x_USELSB, reg_value); + + read_register(this, SX932x_AVGMSB, ®_value); + p += snprintf(p, PAGE_SIZE, "AVGMSB(0x%02x)=0x%02x\n", + SX932x_AVGMSB, reg_value); + + read_register(this, SX932x_AVGLSB, ®_value); + p += snprintf(p, PAGE_SIZE, "AVGLSB(0x%02x)=0x%02x\n", + SX932x_AVGLSB, reg_value); + + read_register(this, SX932x_DIFFMSB, ®_value); + p += snprintf(p, PAGE_SIZE, "DIFFMSB(0x%02x)=0x%02x\n", + SX932x_DIFFMSB, reg_value); + + read_register(this, SX932x_DIFFLSB, ®_value); + p += snprintf(p, PAGE_SIZE, "DIFFLSB(0x%02x)=0x%02x\n", + SX932x_DIFFLSB, reg_value); + + read_register(this, SX932x_OFFSETMSB, ®_value); + p += snprintf(p, PAGE_SIZE, "OFFSETMSB(0x%02x)=0x%02x\n", + SX932x_OFFSETMSB, reg_value); + + read_register(this, SX932x_OFFSETLSB, ®_value); + p += snprintf(p, PAGE_SIZE, "OFFSETLSB(0x%02x)=0x%02x\n", + SX932x_OFFSETLSB, reg_value); + + reg_value = gpio_get_value(this->board->irq_gpio); + p += snprintf(p, PAGE_SIZE, "NIRQ=%d\n", reg_value); + + return (p-buf); +} + +static ssize_t reg_dump_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + psx93XX_t this = sx9325_sar_ptr; + unsigned int val, reg, opt; + + if (strcmp("select_ch0\n", buf) == 0) { + write_register(this, SX932x_CPSRD, 0x00); + } else if (strcmp("select_ch1\n", buf) == 0) { + write_register(this, SX932x_CPSRD, 0x01); + } else if (strcmp("select_ch2\n", buf) == 0) { + write_register(this, SX932x_CPSRD, 0x02); + } else if (strcmp("calibrate\n", buf) == 0) { + write_register(this, SX932x_STAT2_REG, 0x0f); + } else if (sscanf(buf, "%x,%x,%x", ®, &val, &opt) == 3) { + LOG_DBG("%s, read reg = 0x%02x\n", __func__, *(u8 *)®); + this->read_reg = *((u8 *)®); + this->read_flag = 1; + } else if (sscanf(buf, "%x,%x", ®, &val) == 2) { + LOG_DBG("%s,reg = 0x%02x, val = 0x%02x\n", + __func__, *(u8 *)®, *(u8 *)&val); + write_register(this, *((u8 *)®), *((u8 *)&val)); + } + + return count; +} + +static ssize_t sx932x_trigger_rawdata_log_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + psx93XX_t this = sx9325_sar_ptr; + if (strncmp(buf, "1", 1) == 0) + read_rawData(this); + return count; +} + +static void ps_notify_callback_work(struct work_struct *work) +{ + psx93XX_t this = container_of(work, sx93XX_t, ps_notify_work); + psx9325_t pDevice = NULL; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + int ret = 0; + + pDevice = this->pDevice; + input_top = pDevice->pbuttonInformation->input_top; + input_bottom = pDevice->pbuttonInformation->input_bottom; + + mutex_lock(&this->mutex); + if (mEnabled) { + LOG_INFO("Usb insert,going to force calibrate\n"); + ret = write_register(this, SX932x_CTRL1_REG, 0x20); + if (ret < 0) + LOG_ERR(" Usb insert,calibrate cap sensor failed\n"); + msleep(1000); + ret = write_register(this, SX932x_CTRL1_REG, + this->board->cust_prox_ctrl0); + if (ret < 0) + LOG_ERR(" Usb insert,enabel cap sensor failed\n"); + } + mutex_unlock(&this->mutex); + input_report_abs(input_top, ABS_DISTANCE, 0); + input_sync(input_top); + input_report_abs(input_bottom, ABS_DISTANCE, 0); + input_sync(input_bottom); +} + +static int ps_get_state(struct power_supply *psy, bool *present) +{ + union power_supply_propval pval = { 0 }; + int retval; + + retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, + &pval); + if (retval) { + LOG_ERR("%s psy get property failed\n", psy->desc->name); + return retval; + } + *present = (pval.intval) ? true : false; + LOG_INFO("%s is %s\n", psy->desc->name, + (*present) ? "present" : "not present"); + return 0; +} + +static int ps_notify_callback(struct notifier_block *self, + unsigned long event, void *p) +{ + psx93XX_t this = container_of(self, sx93XX_t, ps_notif); + struct power_supply *psy = p; + bool present; + int retval; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0) + if (event == PSY_EVENT_PROP_CHANGED +#else + if ((event == PSY_EVENT_PROP_ADDED || event == PSY_EVENT_PROP_CHANGED) +#endif + && psy && psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, "usb", sizeof("usb"))) { + LOG_INFO("ps notification: event = %lu\n", event); + retval = ps_get_state(psy, &present); + if (retval) { + LOG_ERR("psy get property failed\n"); + return retval; + } + + if (event == PSY_EVENT_PROP_CHANGED) { + if (this->ps_is_present == present) { + LOG_INFO("ps present state not change\n"); + return 0; + } + } + this->ps_is_present = present; + schedule_work(&this->ps_notify_work); + } + + return 0; +} +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) +static struct class_attribute class_attr_reset = + __ATTR(reset, 0660, capsense_reset_show, capsense_reset_store); + +static struct class_attribute class_attr_enable = + __ATTR(enable, 0660, capsense_enable_show, capsense_enable_store); + +static struct class_attribute class_attr_reg = + __ATTR(reg, 0660, reg_dump_show, reg_dump_store); + +static struct class_attribute class_attr_calibrate = + __ATTR(calibrate, 0660, manual_offset_calibration_show, + manual_offset_calibration_store); + +static struct class_attribute class_attr_trigger_rawdata_log = + __ATTR(trigger_rawdata_log, 0660, NULL, sx932x_trigger_rawdata_log_store); + +static struct attribute *capsense_class_attrs[] = { + &class_attr_reset.attr, + &class_attr_enable.attr, + &class_attr_reg.attr, + &class_attr_calibrate.attr, + &class_attr_trigger_rawdata_log.attr, + NULL, +}; +ATTRIBUTE_GROUPS(capsense_class); +#else +static struct class_attribute capsense_class_attributes[] = { + __ATTR(reset, 0660, capsense_reset_show, capsense_reset_store), + __ATTR(enable, 0660, capsense_enable_show, capsense_enable_store), + __ATTR(reg, 0660, reg_dump_show, reg_dump_store), + __ATTR(calibrate, 0660, manual_offset_calibration_show,manual_offset_calibration_store), + __ATTR(trigger_rawdata_log, 0660, NULL, sx932x_trigger_rawdata_log_store), + __ATTR_NULL, +}; +#endif +static struct class capsense_class = { + .name = "capsense", + .owner = THIS_MODULE, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + .class_groups = capsense_class_groups, +#else + .class_attrs = capsense_class_attributes, +#endif +}; + +/** + * fn static int sx9325_probe(struct i2c_client *client, const struct i2c_device_id *id) + * brief Probe function + * param client pointer to i2c_client + * param id pointer to i2c_device_id + * return Whether probe was successful + */ +static int sx9325_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + psx93XX_t this = 0; + psx9325_t pDevice = 0; + psx9325_platform_data_t pplatData = 0; + int ret; + struct input_dev *input_top = NULL; + struct input_dev *input_bottom = NULL; + struct power_supply *psy = NULL; + + LOG_INFO("sx9325_probe()\n"); + + /* detect if sx9325 exist or not */ + if (sx9325_detect(client) == 0) + return -ENODEV; + + pplatData = kzalloc(sizeof(struct sx9325_platform_data), GFP_KERNEL); + sx9325_platform_data_of_init(client, pplatData); + client->dev.platform_data = pplatData; + + if (!pplatData) { + LOG_ERR("platform data is required!\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -EIO; + + /* create memory for main struct */ + this = kzalloc(sizeof(sx93XX_t), GFP_KERNEL); + LOG_INFO("\t Initialized Main Memory: 0x%p\n", this); + + if (this) { + /* In case we need to reinitialize data + * (e.q. if suspend reset device) */ + this->init = initialize; + /* shortcut to read status of interrupt */ + this->refreshStatus = read_regStat; + /* pointer to function from platform data to get pendown + * (1->NIRQ=0, 0->NIRQ=1) */ + this->get_nirq_low = pplatData->get_is_nirq_low; + /* save irq in case we need to reference it */ + this->irq = gpio_to_irq(client->irq); + /* do we need to create an irq timer after interrupt ? */ + this->useIrqTimer = 0; + this->board = pplatData; + /* Setup function to call on corresponding reg irq source bit */ + if (MAX_NUM_STATUS_BITS >= 8) { + this->statusFunc[0] = 0; /* TXEN_STAT */ + this->statusFunc[1] = 0; /* UNUSED */ + this->statusFunc[2] = touchProcess; /* BODY/TABLE */ + this->statusFunc[3] = read_rawData; /* CONV_STAT */ + this->statusFunc[4] = 0; /* COMP_STAT */ + this->statusFunc[5] = touchProcess; /* RELEASE_STAT */ + this->statusFunc[6] = touchProcess; /* TOUCH_STAT */ + this->statusFunc[7] = 0; /* RESET_STAT */ + } + + /* setup i2c communication */ + this->bus = client; + i2c_set_clientdata(client, this); + + /* record device struct */ + this->pdev = &client->dev; + + /* create memory for device specific struct */ + this->pDevice = pDevice = kzalloc(sizeof(sx9325_t), GFP_KERNEL); + LOG_INFO("\t Initialized Device Specific Memory: 0x%p\n", + pDevice); + sx9325_sar_ptr = this; + if (pDevice) { + /* Check if we hava a platform + * initialization function to call*/ + if (pplatData->init_platform_hw) + pplatData->init_platform_hw(); + + /* Add Pointer to main platform data struct */ + pDevice->hw = pplatData; + + /* Initialize the button information + * initialized with keycodes */ + pDevice->pbuttonInformation = + pplatData->pbuttonInformation; + + /* Create the input device */ + input_top = input_allocate_device(); + if (!input_top) + return -ENOMEM; + + /* Set all the keycodes */ + __set_bit(EV_ABS, input_top->evbit); + input_set_abs_params(input_top, ABS_DISTANCE, + -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_top = + input_top; + input_top->name = "SX9325 Cap Touch top"; + if (input_register_device(input_top)) { + LOG_ERR("add top cap touch unsuccess\n"); + return -ENOMEM; + } + /* Create the input device */ + input_bottom = input_allocate_device(); + if (!input_bottom) + return -ENOMEM; + /* Set all the keycodes */ + __set_bit(EV_ABS, input_bottom->evbit); + input_set_abs_params(input_bottom, ABS_DISTANCE, + -1, 100, 0, 0); + /* save the input pointer and finish initialization */ + pDevice->pbuttonInformation->input_bottom = + input_bottom; + /* save the input pointer and finish initialization */ + input_bottom->name = "SX9325 Cap Touch bottom"; + if (input_register_device(input_bottom)) { + LOG_ERR("add bottom cap touch unsuccess\n"); + return -ENOMEM; + } + } + + ret = class_register(&capsense_class); + if (ret < 0) { + LOG_ERR("Create fsys class failed (%d)\n", ret); + return ret; + } + + /*restore sys/class/capsense label*/ + kobject_uevent(&capsense_class.p->subsys.kobj, KOBJ_CHANGE); +#ifdef USE_SENSORS_CLASS + sensors_capsensor_top_cdev.sensors_enable = + capsensor_set_enable; + sensors_capsensor_top_cdev.sensors_poll_delay = NULL; + ret = sensors_classdev_register(&input_top->dev, + &sensors_capsensor_top_cdev); + if (ret < 0) + LOG_ERR("create top cap sensor_class file \ + failed (%d)\n", ret); + sensors_capsensor_bottom_cdev.sensors_enable = + capsensor_set_enable; + sensors_capsensor_bottom_cdev.sensors_poll_delay = NULL; + ret = sensors_classdev_register(&input_bottom->dev, + &sensors_capsensor_bottom_cdev); + if (ret < 0) + LOG_ERR("create bottom cap sensor_class file \ + failed (%d)\n", ret); +#endif +#if 1 + pplatData->cap_vdd = regulator_get(&client->dev, "cap_vdd"); + if (IS_ERR(pplatData->cap_vdd)) { + if (PTR_ERR(pplatData->cap_vdd) == -EPROBE_DEFER) { + ret = PTR_ERR(pplatData->cap_vdd); + goto err_vdd_defer; + } + LOG_ERR("%s: Failed to get regulator\n", + __func__); + } else { + int error = regulator_enable(pplatData->cap_vdd); + + if (error) { + regulator_put(pplatData->cap_vdd); + LOG_ERR("%s: Error %d enable regulator\n", + __func__, error); + return error; + } + pplatData->cap_vdd_en = true; + LOG_INFO("cap_vdd regulator is %s\n", + regulator_is_enabled(pplatData->cap_vdd) ? + "on" : "off"); + } +#endif +#if 0 + pplatData->cap_svdd = regulator_get(&client->dev, "cap_svdd"); + if (!IS_ERR(pplatData->cap_svdd)) { + ret = regulator_enable(pplatData->cap_svdd); + if (ret) { + regulator_put(pplatData->cap_svdd); + LOG_ERR("Failed to enable cap_svdd\n"); + goto err_svdd_error; + } + pplatData->cap_svdd_en = true; + LOG_INFO("cap_svdd regulator is %s\n", + regulator_is_enabled(pplatData->cap_svdd) ? + "on" : "off"); + } else { + ret = PTR_ERR(pplatData->cap_vdd); + if (ret == -EPROBE_DEFER) + goto err_svdd_error; + } +#endif + sx93XX_sar_init(this); + + write_register(this, SX932x_CTRL1_REG, 0x10); + write_register(this, SX932x_IRQ_ENABLE_REG, 0x00); + mEnabled = 0; + + INIT_WORK(&this->ps_notify_work, ps_notify_callback_work); + this->ps_notif.notifier_call = ps_notify_callback; + ret = power_supply_reg_notifier(&this->ps_notif); + if (ret) { + LOG_ERR( + "Unable to register ps_notifier: %d\n", ret); + goto free_ps_notifier; + } + + psy = power_supply_get_by_name("usb"); + if (psy) { + ret = ps_get_state(psy, &this->ps_is_present); + if (ret) { + LOG_ERR( + "psy get property failed rc=%d\n", + ret); + goto free_fb_notifier; + } + } + + return 0; + } + return -ENOMEM; + +free_fb_notifier: + power_supply_unreg_notifier(&this->ps_notif); + +free_ps_notifier: + LOG_ERR("%s free ps notifier:.\n", __func__); + regulator_disable(pplatData->cap_svdd); + regulator_put(pplatData->cap_svdd); +#if 0 +err_svdd_error: + LOG_ERR("%s svdd defer.\n", __func__); + regulator_disable(pplatData->cap_vdd); + regulator_put(pplatData->cap_vdd); +#endif +#if 1 +err_vdd_defer: + LOG_ERR("%s input free device.\n", __func__); + input_free_device(input_top); + input_free_device(input_bottom); +#endif + + return ret; +} + +/** + * fn static int sx9325_remove(struct i2c_client *client) + * brief Called when device is to be removed + * param client Pointer to i2c_client struct + * return Value from sx93XX_sar_remove() + */ +static int sx9325_remove(struct i2c_client *client) +{ + psx9325_platform_data_t pplatData = 0; + psx9325_t pDevice = 0; + psx93XX_t this = i2c_get_clientdata(client); + + pDevice = this->pDevice; + pplatData = client->dev.platform_data; + if (this && pDevice) { + power_supply_unreg_notifier(&this->ps_notif); + cancel_work_sync(&this->ps_notify_work); + +#ifdef USE_SENSORS_CLASS + sensors_classdev_unregister(&sensors_capsensor_top_cdev); + sensors_classdev_unregister(&sensors_capsensor_bottom_cdev); +#endif + input_unregister_device(pDevice->pbuttonInformation->input_top); + input_unregister_device( + pDevice->pbuttonInformation->input_bottom); + + if (gpio_is_valid(pplatData ->irq_gpio)) + gpio_free(pplatData ->irq_gpio); +#if 0 + if (this->board->cap_svdd_en) { + regulator_disable(this->board->cap_svdd); + regulator_put(this->board->cap_svdd); + } +#endif +#if 1 + if (this->board->cap_vdd_en) { + regulator_disable(this->board->cap_vdd); + regulator_put(this->board->cap_vdd); + } +#endif + class_unregister(&capsense_class); + if (pplatData && pplatData->exit_platform_hw) + pplatData->exit_platform_hw(); + kfree(this->pDevice); + } + return sx93XX_sar_remove(this); +} + +#if defined(USE_KERNEL_SUSPEND) +/*====================================================*/ +/***** Kernel Suspend *****/ +static int sx9325_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + psx93XX_t this = i2c_get_clientdata(client); + + sx93XX_suspend(this); + return 0; +} +/***** Kernel Resume *****/ +static int sx9325_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + psx93XX_t this = i2c_get_clientdata(client); + + sx93XX_resume(this); + return 0; +} +/*====================================================*/ +#endif + +#ifdef CONFIG_OF +static const struct of_device_id sx9325_match_table[] = { + { .compatible = "semtech," DRIVER_NAME }, + { }, +}; +//MODULE_DEVICE_TABLE(of, synaptics_rmi4_match_tbl); +#endif + +#if defined(USE_KERNEL_SUSPEND) +static const struct dev_pm_ops sx9325_pm_ops = { + .suspend = sx9325_suspend, + .resume = sx9325_resume, +}; +#endif + +static struct i2c_device_id sx9325_idtable[] = { + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sx9325_idtable); + +static struct i2c_driver sx9325_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = sx9325_match_table, +#if defined(USE_KERNEL_SUSPEND) + .pm = &sx9325_pm_ops, +#endif + }, + .id_table = sx9325_idtable, + .probe = sx9325_probe, + .remove = sx9325_remove, +}; +static int __init sx9325_init(void) +{ + return i2c_add_driver(&sx9325_driver); +} +static void __exit sx9325_exit(void) +{ + i2c_del_driver(&sx9325_driver); +} + +module_init(sx9325_init); +module_exit(sx9325_exit); + +MODULE_AUTHOR("Semtech Corp. (http://www.semtech.com/)"); +MODULE_DESCRIPTION("SX9325 Capacitive Touch Controller Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("0.1"); + +#ifdef USE_THREADED_IRQ +static void sx93XX_process_interrupt(psx93XX_t this, u8 nirqlow) +{ + int status = 0; + int counter = 0; + + if (!this) { + LOG_ERR("sx93XX_worker_func, NULL sx93XX_t\n"); + return; + } + /* since we are not in an interrupt don't need to disable irq. */ + status = this->refreshStatus(this); + counter = -1; + if (sx9325_debug_enable) + LOG_DBG("Worker - Refresh Status %d\n", status); + + while ((++counter) < MAX_NUM_STATUS_BITS) { /* counter start from MSB */ + if (((status >> counter) & 0x01) + && (this->statusFunc[counter])) { + if (sx9325_debug_enable) + LOG_DBG("Function %d Pointer Found. Calling\n" + ,counter); + this->statusFunc[counter](this); + } + } + if (unlikely(this->useIrqTimer && nirqlow)) { + /* In case we need to send a timer for example on a touchscreen + * checking penup, perform this here + */ + cancel_delayed_work(&this->dworker); + schedule_delayed_work(&this->dworker, + msecs_to_jiffies(this->irqTimeout)); + LOG_INFO("Schedule Irq timer"); + } +} + + +static void sx93XX_worker_func(struct work_struct *work) +{ + psx93XX_t this = 0; + + if (work) { + this = container_of(work, sx93XX_t, dworker.work); + if (!this) { + LOG_ERR("sx93XX_worker_func, NULL sx93XX_t\n"); + return; + } + if ((!this->get_nirq_low) + || (!this->get_nirq_low(this->board->irq_gpio))) { + /* only run if nirq is high */ + sx93XX_process_interrupt(this, 0); + } + } else { + LOG_ERR("sx93XX_worker_func, NULL work_struct\n"); + } +} +static irqreturn_t sx93XX_interrupt_thread(int irq, void *data) +{ + psx93XX_t this = 0; + + this = data; + + mutex_lock(&this->mutex); + if (sx9325_debug_enable) + LOG_DBG("sx93XX_irq\n"); + if ((!this->get_nirq_low) || this->get_nirq_low(this->board->irq_gpio)) + sx93XX_process_interrupt(this, 1); + else { + if (sx9325_debug_enable) + LOG_DBG("sx93XX_irq - nirq read high\n"); + } + mutex_unlock(&this->mutex); + return IRQ_HANDLED; +} +#else +static void sx93XX_schedule_work(psx93XX_t this, unsigned long delay) +{ + unsigned long flags; + + if (this) { + LOG_INFO("sx93XX_schedule_work()\n"); + spin_lock_irqsave(&this->lock, flags); + /* Stop any pending penup queues */ + cancel_delayed_work(&this->dworker); + /* + * after waiting for a delay, this put the job + * in the kernel-global workqueue. + * so no need to create new thread in work queue. + */ + schedule_delayed_work(&this->dworker, delay); + spin_unlock_irqrestore(&this->lock, flags); + } else + LOG_ERR("sx93XX_schedule_work, NULL psx93XX_t\n"); +} + +static irqreturn_t sx93XX_irq(int irq, void *pvoid) +{ + psx93XX_t this = 0; + + if (pvoid) { + this = (psx93XX_t)pvoid; + if (sx9325_debug_enable) + LOG_DBG("sx93XX_irq\n"); + if ((!this->get_nirq_low) + || this->get_nirq_low(this->board->irq_gpio)) { + if (sx9325_debug_enable) + LOG_DBG("sx93XX_irq - Schedule Work\n"); + sx93XX_schedule_work(this, 0); + } else { + if (sx9325_debug_enable) + LOG_DBG("sx93XX_irq - nirq read high\n"); + } + } else + LOG_ERR("sx93XX_irq, NULL pvoid\n"); + return IRQ_HANDLED; +} + +static void sx93XX_worker_func(struct work_struct *work) +{ + psx93XX_t this = 0; + int status = 0; + int counter = 0; + u8 nirqLow = 0; + + if (work) { + this = container_of(work, sx93XX_t, dworker.work); + + if (!this) { + LOG_ERR("sx93XX_worker_func, NULL sx93XX_t\n"); + return; + } + if (unlikely(this->useIrqTimer)) { + if ((!this->get_nirq_low) + || this->get_nirq_low(this->board->irq_gpio)) + nirqLow = 1; + } + /* since we are not in an interrupt + * don't need to disable irq. */ + status = this->refreshStatus(this); + counter = -1; + if (sx9325_debug_enable) + LOG_DBG("Worker - Refresh Status %d\n", status); + /* counter start from MSB */ + while ((++counter) < MAX_NUM_STATUS_BITS) { + if (((status >> counter) & 0x01) + && (this->statusFunc[counter])) { + LOG_INFO("Function %d Pointer Found. Calling\n" + ,counter); + this->statusFunc[counter](this); + } + } + if (unlikely(this->useIrqTimer && nirqLow)) { + /* Early models and if RATE=0 for newer models + * require a penup timer; + * Queue up the function again for checking on penup */ + sx93XX_schedule_work(this, + msecs_to_jiffies(this->irqTimeout)); + } + } else { + LOG_ERR("sx93XX_worker_func, NULL work_struct\n"); + } +} +#endif + +void sx93XX_suspend(psx93XX_t this) +{ + if (this) { + if (mEnabled) + write_register(this, SX932x_CTRL1_REG, + this->board->cust_prox_ctrl0&0x20); + if (sx9325_debug_enable) + LOG_INFO("sx9325 suspend: disable irq!\n"); + disable_irq(this->irq); + } +} +void sx93XX_resume(psx93XX_t this) +{ + if (this) { + if (sx9325_debug_enable) + LOG_INFO("sx9325 resume: enable irq!\n"); +#ifdef USE_THREADED_IRQ + mutex_lock(&this->mutex); + /* Just in case need to reset any uncaught interrupts */ + sx93XX_process_interrupt(this, 0); + mutex_unlock(&this->mutex); +#else + sx93XX_schedule_work(this, 0); +#endif + enable_irq(this->irq); + if (mEnabled) + write_register(this, SX932x_CTRL1_REG, + this->board->cust_prox_ctrl0); + } +} + +int sx93XX_sar_init(psx93XX_t this) +{ + int err = 0; + + if (this && this->pDevice) { +#ifdef USE_THREADED_IRQ + + /* initialize worker function */ + INIT_DELAYED_WORK(&this->dworker, sx93XX_worker_func); + + + /* initialize mutex */ + mutex_init(&this->mutex); + /* initailize interrupt reporting */ + this->irq_disabled = 0; + err = request_threaded_irq(this->irq, NULL, + sx93XX_interrupt_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + this->pdev->driver->name, + this); +#else + /* initialize spin lock */ + spin_lock_init(&this->lock); + + /* initialize worker function */ + INIT_DELAYED_WORK(&this->dworker, sx93XX_worker_func); + + /* initailize interrupt reporting */ + this->irq_disabled = 0; + err = request_irq(this->irq, sx93XX_irq, IRQF_TRIGGER_FALLING, + this->pdev->driver->name, this); +#endif + if (err) { + LOG_ERR("irq %d busy?\n", this->irq); + return err; + } +#ifdef USE_THREADED_IRQ + LOG_INFO("registered with threaded irq (%d)\n", this->irq); +#else + LOG_INFO("registered with irq (%d)\n", this->irq); +#endif + /* call init function pointer: + * this should initialize all registers */ + if (this->init) + return this->init(this); + LOG_ERR("No init function!!!!\n"); + } + return -ENOMEM; +} + +int sx93XX_sar_remove(psx93XX_t this) +{ + if (this) { + /* Cancel the Worker Func */ + cancel_delayed_work_sync(&this->dworker); + free_irq(this->irq, this); + kfree(this); + return 0; + } + return -ENOMEM; +} diff --git a/drivers/misc/sx933x/Android.mk b/drivers/misc/sx933x/Android.mk new file mode 100644 index 000000000000..21a6a2b0e44c --- /dev/null +++ b/drivers/misc/sx933x/Android.mk @@ -0,0 +1,19 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := sx933x_sar.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/sensors_class.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +KBUILD_OPTIONS += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) +include $(DLKM_DIR)/AndroidKernelModule.mk + +ifeq ($(SX933X_USB_CAL),true) + KERNEL_CFLAGS += CONFIG_SX933X_USB_CAL=y +endif + +ifeq ($(SX933X_FLIP_CAL),true) + KERNEL_CFLAGS += CONFIG_SX933X_FLIP_CAL=y +endif diff --git a/drivers/misc/sx933x/Kbuild b/drivers/misc/sx933x/Kbuild new file mode 100644 index 000000000000..40ec04af6ccb --- /dev/null +++ b/drivers/misc/sx933x/Kbuild @@ -0,0 +1,73 @@ +KERNEL_DIR_PATH = $(shell find $(ANDROID_BUILD_TOP)/kernel/ -name "msm-*" -type d | head -1 | grep -i "kernel/msm-*") + +KERNEL_VERSION_NAME = $(shell basename $(KERNEL_DIR_PATH)) + +KERNEL_PLATFORM_PATH = $(shell find $(ANDROID_BUILD_TOP) -maxdepth 1 -name "kernel_platform" -type d) + +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +ifneq ($(filter m y,$(CONFIG_SX933X_USB_CAL)),) + EXTRA_CFLAGS += -DCONFIG_CAPSENSE_USB_CAL +endif + +ifneq ($(filter m y,$(CONFIG_SX933X_FLIP_CAL)),) + EXTRA_CFLAGS += -DCONFIG_CAPSENSE_FLIP_CAL +endif + +ifneq (,$(filter river%,$(TARGET_PRODUCT))) +EXTRA_CFLAGS += -DCONFIG_CAPSENSE_USB_CAL +endif + +ifneq (,$(filter doha%,$(TARGET_PRODUCT))) +EXTRA_CFLAGS += -DCONFIG_CAPSENSE_USB_CAL +endif + +ifneq (,$(filter sofia%,$(TARGET_PRODUCT))) +EXTRA_CFLAGS += -DCONFIG_CAPSENSE_USB_CAL +endif + +ifneq (,$(filter odin%,$(TARGET_PRODUCT))) +EXTRA_CFLAGS += -DCONFIG_CAPSENSE_USB_CAL \ + -DCONFIG_CAPSENSE_ATTACH_CAL +endif + +ifneq (,$(filter olson%,$(TARGET_PRODUCT))) +EXTRA_CFLAGS += -DCONFIG_CAPSENSE_USB_CAL \ + -DCONFIG_CAPSENSE_FLIP_CAL +endif + +ifneq (,$(filter rav%,$(TARGET_PRODUCT))) +EXTRA_CFLAGS += -DCONFIG_CAPSENSE_USB_CAL +endif + +ifneq (,$(filter burton%,$(TARGET_PRODUCT))) +EXTRA_CFLAGS += -DCONFIG_CAPSENSE_USB_CAL +endif + +ifneq (,$(filter smith% nairo%,$(TARGET_PRODUCT))) +EXTRA_CFLAGS += -DCONFIG_CAPSENSE_USB_CAL +endif + +ifneq (,$(filter racer%,$(TARGET_PRODUCT))) +EXTRA_CFLAGS += -DCONFIG_CAPSENSE_USB_CAL +EXTRA_CFLAGS += -DCONFIG_CAPSENSE_HEADSET_STATE +endif + +ifneq (,$(filter kiev%,$(TARGET_PRODUCT))) +EXTRA_CFLAGS += -DCONFIG_CAPSENSE_USB_CAL +endif + +ifneq (,$(KERNEL_DIR_PATH)) + EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/kernel/$(KERNEL_VERSION_NAME)/drivers/base +else ifneq (,$(KERNEL_PLATFORM_PATH)) + EXTRA_CFLAGS += -I$(KERNEL_PLATFORM_PATH)/common/drivers/base +else + EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/kernel/drivers/base +endif +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../sensors/Module.symvers + +sx932x_sar-y := sx933x_sar.o + +obj-m += sx933x_sar.o diff --git a/drivers/misc/sx933x/Makefile b/drivers/misc/sx933x/Makefile new file mode 100644 index 000000000000..13af5267daf0 --- /dev/null +++ b/drivers/misc/sx933x/Makefile @@ -0,0 +1,33 @@ +ifeq ($(strip $(MODULE_KERNEL_VERSION)), 5.15) + +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers + +else + +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + +endif diff --git a/drivers/misc/sx933x/sx933x_sar.c b/drivers/misc/sx933x/sx933x_sar.c new file mode 100644 index 000000000000..36909055b192 --- /dev/null +++ b/drivers/misc/sx933x/sx933x_sar.c @@ -0,0 +1,1963 @@ +/*! \file sx933x.c + * \brief SX933x Driver + * + * Driver for the SX933x + * Copyright (c) 2018 Semtech Corp + * + * 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. + */ +#define DRIVER_NAME "sx933x" + +#define MAX_WRITE_ARRAY_SIZE 32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../../include/linux/input/sx933x.h" /* main struct, interrupt,init,pointers */ +#include "base.h" + +#define LOG_TAG "[sar SX933x]: " + +#define LOG_INFO(fmt, args...) pr_info(LOG_TAG "[INFO]" "<%s><%d>"fmt, __func__, __LINE__, ##args) +#define LOG_DBG(fmt, args...) pr_debug(LOG_TAG "[DBG]" "<%s><%d>"fmt, __func__, __LINE__, ##args) +#define LOG_ERR(fmt, args...) pr_err(LOG_TAG "[ERR]" "<%s><%d>"fmt, __func__, __LINE__, ##args) + +#define USE_DTS_REG + +#define SX933x_I2C_M_WR 0 /* for i2c Write */ +#define SX933x_I2C_M_RD 1 /* for i2c Read */ + +#define IDLE 0 +#define PROXACTIVE 1 +#define BODYACTIVE 2 + +#define MAIN_SENSOR 1 //CS1 + +/* Failer Index */ +#define SX933x_ID_ERROR 1 +#define SX933x_NIRQ_ERROR 2 +#define SX933x_CONN_ERROR 3 +#define SX933x_I2C_ERROR 4 + +#define SX933X_I2C_WATCHDOG_TIME 10000 +#define SX933X_I2C_WATCHDOG_TIME_ERR 2000 + +/*! \struct sx933x + * Specialized struct containing input event data, platform data, and + * last cap state read if needed. + */ +typedef struct sx933x +{ + pbuttonInformation_t pbuttonInformation; + psx933x_platform_data_t hw; /* specific platform data settings */ +} sx933x_t, *psx933x_t; + +static int irq_gpio_num; +static psx93XX_t global_sx933x; + +static void sx93XX_schedule_work(psx93XX_t this, unsigned long delay); +static int sx933x_get_nirq_state(void) +{ + return !gpio_get_value(irq_gpio_num); +} + +static void sx933x_reinitialize(psx93XX_t this); + +/*! \fn static int sx933x_i2c_write_16bit(psx93XX_t this, u8 address, u8 value) + * \brief Sends a write register to the device + * \param this Pointer to main parent struct + * \param address 8-bit register address + * \param value 8-bit register value to write to address + * \return Value from i2c_master_send + */ + +static int sx933x_i2c_write_16bit(psx93XX_t this, u16 reg_addr, u32 buf) +{ + int ret = -ENOMEM; + struct i2c_client *i2c = 0; + struct i2c_msg msg; + unsigned char w_buf[6]; + + if (this && this->bus) + { + i2c = this->bus; + w_buf[0] = (u8)(reg_addr>>8); + w_buf[1] = (u8)(reg_addr); + w_buf[2] = (u8)(buf>>24); + w_buf[3] = (u8)(buf>>16); + w_buf[4] = (u8)(buf>>8); + w_buf[5] = (u8)(buf); + + msg.addr = i2c->addr; + msg.flags = SX933x_I2C_M_WR; + msg.len = 6; //2bytes regaddr + 4bytes data + msg.buf = (u8 *)w_buf; + + ret = i2c_transfer(i2c->adapter, &msg, 1); + if (ret < 0) + LOG_ERR(" i2c write reg 0x%x error %d\n", reg_addr, ret); + } + return ret; +} + +/*! \fn static int sx933x_i2c_read_16bit(psx93XX_t this, u8 address, u8 *value) + * \brief Reads a register's value from the device + * \param this Pointer to main parent struct + * \param address 8-Bit address to read from + * \param value Pointer to 8-bit value to save register value to + * \return Value from i2c_smbus_read_byte_data if < 0. else 0 + */ +static int sx933x_i2c_read_16bit(psx93XX_t this, u16 reg_addr, u32 *data32) +{ + int ret = -ENOMEM; + struct i2c_client *i2c = 0; + struct i2c_msg msg[2]; + u8 w_buf[2]; + u8 buf[4]; + + if (this && this->bus) + { + i2c = this->bus; + + w_buf[0] = (u8)(reg_addr>>8); + w_buf[1] = (u8)(reg_addr); + + msg[0].addr = i2c->addr; + msg[0].flags = SX933x_I2C_M_WR; + msg[0].len = 2; + msg[0].buf = (u8 *)w_buf; + + msg[1].addr = i2c->addr;; + msg[1].flags = SX933x_I2C_M_RD; + msg[1].len = 4; + msg[1].buf = (u8 *)buf; + + ret = i2c_transfer(i2c->adapter, msg, 2); + if (ret < 0) + LOG_ERR("i2c read reg 0x%x error %d\n", reg_addr, ret); + + data32[0] = ((u32)buf[0]<<24) | ((u32)buf[1]<<16) | ((u32)buf[2]<<8) | ((u32)buf[3]); + } + return ret; +} + + + + +//static int sx933x_set_mode(psx93XX_t this, unsigned char mode); + +/*! \fn static int read_regStat(psx93XX_t this) + * \brief Shortcut to read what caused interrupt. + * \details This is to keep the drivers a unified + * function that will read whatever register(s) + * provide information on why the interrupt was caused. + * \param this Pointer to main parent struct + * \return If successful, Value of bit(s) that cause interrupt, else 0 + */ +static int read_regStat(psx93XX_t this) +{ + u32 data = 0; + if (this) + { + if (sx933x_i2c_read_16bit(this,SX933X_HOSTIRQSRC_REG,&data) > 0) //bug + return (data & 0x00FF); + } + return 0; +} + +static int sx933x_Hardware_Check(psx93XX_t this) +{ + int ret; + u32 idCode; + u8 loop = 0; + this->failStatusCode = 0; + + //Check th IRQ Status + while(this->get_nirq_low && this->get_nirq_low()) + { + read_regStat(this); + msleep(100); + if(++loop >10) + { + this->failStatusCode = SX933x_NIRQ_ERROR; + break; + } + } + + //Check I2C Connection + ret = sx933x_i2c_read_16bit(this, SX933X_INFO_REG, &idCode); + if(ret < 0) + { + this->failStatusCode = SX933x_I2C_ERROR; + } + + if(idCode!= SX933X_WHOAMI_VALUE) + { + this->failStatusCode = SX933x_ID_ERROR; + } + + if(idCode == SX9338_DFN_WHOAMI_VALUE) + { + this->failStatusCode = 0; + } + + LOG_INFO("sx933x idcode = 0x%x, failcode = 0x%x\n", idCode, this->failStatusCode); + return (int)this->failStatusCode; +} + +static int sx933x_global_variable_init(psx93XX_t this) +{ + this->irq_disabled = 0; + this->failStatusCode = 0; + this->reg_in_dts = true; + return 0; +} + +/*! \brief Perform a manual offset calibration + * \param this Pointer to main parent struct + * \return Value return value from the write register + */ +static int manual_offset_calibration(psx93XX_t this) +{ + int ret = 0; + ret = sx933x_i2c_write_16bit(this, SX933X_CMD_REG, I2C_REGCMD_COMPEN); + return ret; + +} + +static void read_dbg_raw(psx93XX_t this) +{ + int ph, state; + u32 uData, ph_sel; + s32 ant_use, ant_raw; + s32 avg, diff; + u16 off; + s32 adc_min, adc_max, use_flt_dlt_var; + s32 ref_a_use=0, ref_b_use=0; + int ref_ph_a, ref_ph_b; + + psx933x_t pDevice = NULL; + psx933x_platform_data_t pdata = NULL; + + pDevice = this->pDevice; + pdata = pDevice->hw; + ref_ph_a = pdata->ref_phase_a; + ref_ph_b = pdata->ref_phase_b; + LOG_DBG("[SX933x] ref_ph_a= %d ref_ph_b= %d\n", ref_ph_a, ref_ph_b); + + sx933x_i2c_read_16bit(this, SX933X_STAT0_REG, &uData); + LOG_DBG("SX933X_STAT0_REG= 0x%X\n", uData); + + if(ref_ph_a != 0xFF) + { + sx933x_i2c_read_16bit(this, SX933X_USEPH0_REG + ref_ph_a*4, &uData); + ref_a_use = (s32)uData >> 10; + } + if(ref_ph_b != 0xFF) + { + sx933x_i2c_read_16bit(this, SX933X_USEPH0_REG + ref_ph_b*4, &uData); + ref_b_use = (s32)uData >> 10; + } + + sx933x_i2c_read_16bit(this, SX933X_REG_DBG_PHASE_SEL, &ph_sel); + + sx933x_i2c_read_16bit(this, SX933X_REG_PROX_ADC_MIN, &uData); + adc_min = (s32)uData>>10; + sx933x_i2c_read_16bit(this, SX933X_REG_PROX_ADC_MAX, &uData); + adc_max = (s32)uData>>10; + sx933x_i2c_read_16bit(this, SX933X_REG_PROX_RAW, &uData); + ant_raw = (s32)uData>>10; + sx933x_i2c_read_16bit(this, SX933X_REG_DLT_VAR, &uData); + use_flt_dlt_var = (s32)uData>>3; + + if (((ph_sel >> 3) & 0x7) == 0) + { + sx933x_i2c_read_16bit(this, SX933X_USEPH0_REG, &uData); + ant_use = (s32)uData>>10; + ph = 0; + } + else if (((ph_sel >> 3) & 0x7) == 1) + { + sx933x_i2c_read_16bit(this, SX933X_USEPH1_REG, &uData); + ant_use = (s32)uData>>10; + ph = 1; + } + else if (((ph_sel >> 3) & 0x7) == 2) + { + sx933x_i2c_read_16bit(this, SX933X_USEPH2_REG, &uData); + ant_use = (s32)uData>>10; + ph = 2; + } + else if (((ph_sel >> 3) & 0x7) == 3) + { + sx933x_i2c_read_16bit(this, SX933X_USEPH3_REG, &uData); + ant_use = (s32)uData>>10; + ph = 3; + } + else if (((ph_sel >> 3) & 0x7) == 4) + { + sx933x_i2c_read_16bit(this, SX933X_USEPH4_REG, &uData); + ant_use = (s32)uData>>10; + ph = 4; + } + else + { + LOG_DBG("read_dbg_raw(): invalid reg_val= 0x%X\n", ph_sel); + ph = -1; + } + + if(ph != -1) + { + sx933x_i2c_read_16bit(this, SX933X_AVGPH0_REG + ph*4, &uData); + avg = (s32)uData>>10; + sx933x_i2c_read_16bit(this, SX933X_DIFFPH0_REG + ph*4, &uData); + diff = (s32)uData>>10; + sx933x_i2c_read_16bit(this, SX933X_OFFSETPH0_REG + ph*4*2, &uData); + off = (u16)(uData & 0x7FFF); + state = psmtcButtons[ph].state; + + if(ref_ph_a != 0xFF && ref_ph_b != 0xFF) + { + LOG_DBG( + "SMTC_DBG PH= %d USE= %d RAW= %d PH%d_USE= %d PH%d_USE= %d STATE= %d AVG= %d DIFF= %d OFF= %d ADC_MIN= %d ADC_MAX= %d DLT= %d SMTC_END\n", + ph, ant_use, ant_raw, ref_ph_a, ref_a_use, ref_ph_b, ref_b_use, state, avg, diff, off, adc_min, adc_max, use_flt_dlt_var); + } + else if(ref_ph_a != 0xFF) + { + LOG_DBG( + "SMTC_DBG PH= %d USE= %d RAW= %d PH%d_USE= %d STATE= %d AVG= %d DIFF= %d OFF= %d ADC_MIN= %d ADC_MAX= %d DLT= %d SMTC_END\n", + ph, ant_use, ant_raw, ref_ph_a, ref_a_use, state, avg, diff, off, adc_min, adc_max, use_flt_dlt_var); + } + else if(ref_ph_b != 0xFF) + { + LOG_DBG( + "SMTC_DBG PH= %d USE= %d RAW= %d PH%d_USE= %d STATE= %d AVG= %d DIFF= %d OFF= %d ADC_MIN= %d ADC_MAX= %d DLT= %d SMTC_END\n", + ph, ant_use, ant_raw, ref_ph_b, ref_b_use, state, avg, diff, off, adc_min, adc_max, use_flt_dlt_var); + } + else + { + LOG_DBG( + "SMTC_DBG PH= %d USE= %d RAW= %d STATE= %d AVG= %d DIFF= %d OFF= %d ADC_MIN= %d ADC_MAX= %d DLT= %d SMTC_END\n", + ph, ant_use, ant_raw, state, avg, diff, off, adc_min, adc_max, use_flt_dlt_var); + } + } +} + +static void read_rawData(psx93XX_t this) +{ + u8 csx, index; + s32 useful, average, diff; + s32 ref_a_use=0, ref_b_use=0; + u32 uData; + u16 offset; + int state; + psx933x_t pDevice = NULL; + psx933x_platform_data_t pdata = NULL; + int ref_ph_a, ref_ph_b; + + if(this) + { + pDevice = this->pDevice; + pdata = pDevice->hw; + ref_ph_a = pdata->ref_phase_a; + ref_ph_b = pdata->ref_phase_b; + LOG_DBG("[SX933x] ref_ph_a= %d ref_ph_b= %d\n", ref_ph_a, ref_ph_b); + + sx933x_i2c_read_16bit(this, SX933X_STAT0_REG, &uData); + LOG_DBG("SX933X_STAT0_REG= 0x%X\n", uData); + + if(ref_ph_a != 0xFF) + { + sx933x_i2c_read_16bit(this, SX933X_USEPH0_REG + ref_ph_a*4, &uData); + ref_a_use = (s32)uData >> 10; + } + if(ref_ph_b != 0xFF) + { + sx933x_i2c_read_16bit(this, SX933X_USEPH0_REG + ref_ph_b*4, &uData); + ref_b_use = (s32)uData >> 10; + } + + for(csx =0; csx<5; csx++) + { + index = csx*4; + sx933x_i2c_read_16bit(this, SX933X_USEPH0_REG + index, &uData); + useful = (s32)uData>>10; + sx933x_i2c_read_16bit(this, SX933X_AVGPH0_REG + index, &uData); + average = (s32)uData>>10; + sx933x_i2c_read_16bit(this, SX933X_DIFFPH0_REG + index, &uData); + diff = (s32)uData>>10; + sx933x_i2c_read_16bit(this, SX933X_OFFSETPH0_REG + index*2, &uData); + offset = (u16)(uData & 0x7FFF); + + state = psmtcButtons[csx].state; + + if(ref_ph_a != 0xFF && ref_ph_b != 0xFF) + { + LOG_DBG( + "SMTC_DAT PH= %d DIFF= %d USE= %d PH%d_USE= %d PH%d_USE= %d STATE= %d OFF= %d AVG= %d SMTC_END\n", + csx, diff, useful, ref_ph_a, ref_a_use, ref_ph_b, ref_b_use, state, offset, average); + } + else if(ref_ph_a != 0xFF) + { + LOG_DBG( + "SMTC_DAT PH= %d DIFF= %d USE= %d PH%d_USE= %d STATE= %d OFF= %d AVG= %d SMTC_END\n", + csx, diff, useful, ref_ph_a, ref_a_use, state, offset, average); + } + else if(ref_ph_b != 0xFF) + { + LOG_DBG( + "SMTC_DAT PH= %d DIFF= %d USE= %d PH%d_USE= %d STATE= %d OFF= %d AVG= %d SMTC_END\n", + csx, diff, useful, ref_ph_b, ref_b_use, state, offset, average); + } + else + { + LOG_DBG( + "SMTC_DAT PH= %d DIFF= %d USE= %d STATE= %d OFF= %d AVG= %d SMTC_END\n", + csx, diff, useful, state, offset, average); + } + } + + read_dbg_raw(this); + } +} + +static ssize_t capsense_reset_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + u32 temp = 0; + sx933x_i2c_read_16bit(global_sx933x, SX933X_GNRLCTRL2_REG, &temp); + if (!count) + return -EINVAL; + + if (!strncmp(buf, "reset", 5) || !strncmp(buf, "1", 1)) { + if (temp & 0x0000001F) { + LOG_DBG("Going to refresh baseline\n"); + manual_offset_calibration(global_sx933x); + } + } + + return count; +} + +#ifdef CONFIG_CAPSENSE_HEADSET_STATE +static ssize_t capsense_headset_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + u32 reg_temp = 0; + int i; + psx933x_t pDevice = global_sx933x->pDevice; + psx933x_platform_data_t pdata = pDevice->hw; + + sx933x_i2c_read_16bit(global_sx933x, SX933X_GNRLCTRL2_REG, ®_temp); + if (!count || pdata == NULL) + return -EINVAL; + + if (!strncmp(buf, "1", 1)) { + LOG_INFO("headset in update reg num:%d\n", pdata->headset_operate_reg_num); + for (i = 0; i < pdata->headset_operate_reg_num; i++) + { + sx933x_i2c_write_16bit(global_sx933x, pdata->headset_operate_reg[i].reg, pdata->headset_operate_reg[i].val); + LOG_INFO("set Reg 0x%x Value: 0x%x\n", + pdata->headset_operate_reg[i].reg,pdata->headset_operate_reg[i].val); + } + //cal sensor + if (reg_temp & 0x0000001F) { + LOG_DBG("Going to refresh baseline\n"); + manual_offset_calibration(global_sx933x); + } + } + + if (!strncmp(buf, "0", 1)) { + LOG_INFO("headset out back reg num:%d\n", pdata->headset_operate_reg_num); + for (i = 0; i < pdata->headset_operate_reg_num; i++) + { + sx933x_i2c_write_16bit(global_sx933x, pdata->headset_operate_reg_bck[i].reg, pdata->headset_operate_reg_bck[i].val); + LOG_INFO("set Reg 0x%x Value: 0x%x\n", + pdata->headset_operate_reg_bck[i].reg,pdata->headset_operate_reg_bck[i].val); + } + //cal sensor + if (reg_temp & 0x0000001F) { + LOG_DBG("Going to refresh baseline\n"); + manual_offset_calibration(global_sx933x); + } + } + return count; +} +#endif + +static ssize_t capsense_raw_data_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + char *p = buf; + int csx; + s32 useful, average, diff; + u32 uData; + u16 offset; + + psx93XX_t this = global_sx933x; + if(this) { + for(csx =0; csx<5; csx++) { + sx933x_i2c_read_16bit(this, SX933X_USEPH0_REG + csx*4, &uData); + useful = (s32)uData>>10; + sx933x_i2c_read_16bit(this, SX933X_AVGPH0_REG + csx*4, &uData); + average = (s32)uData>>10; + sx933x_i2c_read_16bit(this, SX933X_DIFFPH0_REG + csx*4, &uData); + diff = (s32)uData>>10; + sx933x_i2c_read_16bit(this, SX933X_OFFSETPH0_REG + csx*8, &uData); + offset = (u16)(uData & 0x7FFF); + p += snprintf(p, PAGE_SIZE, "[PH: %d] Useful = %d, Average = %d, DIFF = %d Offset = %d \n", + csx,useful,average,diff,offset); + } + } + return (p-buf); +} + +static ssize_t sx933x_register_write_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + u32 reg_address = 0, val = 0; + psx93XX_t this = global_sx933x; + + if (sscanf(buf, "%x,%x", ®_address, &val) != 2) + { + LOG_ERR("The number of data are wrong\n"); + return -EINVAL; + } + + sx933x_i2c_write_16bit(this, reg_address, val); + + LOG_DBG("Register(0x%x) data(0x%x)\n", reg_address, val); + return count; +} + +static ssize_t sx933x_register_read_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + u32 val=0; + int regist = 0; + int nirq_state = 0; + psx93XX_t this = global_sx933x; + + if (sscanf(buf, "%x", ®ist) != 1) + { + LOG_ERR(" The number of data are wrong\n"); + return -EINVAL; + } + + sx933x_i2c_read_16bit(this, regist, &val); + nirq_state = sx933x_get_nirq_state(); + + LOG_DBG("Register(0x%2x) data(0x%4x) nirq_state(%d)\n", regist, val, nirq_state); + return count; +} + +static ssize_t manual_offset_calibration_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u32 reg_value = 0; + psx93XX_t this = global_sx933x; + + LOG_DBG("Reading IRQSTAT_REG\n"); + sx933x_i2c_read_16bit(this,SX933X_HOSTIRQSRC_REG,®_value); + return sprintf(buf, "%d\n", reg_value); +} + + +static ssize_t manual_offset_calibration_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + unsigned long val; + psx93XX_t this = global_sx933x; + + if (kstrtoul(buf, 10, &val)) //(strict_strtoul(buf, 10, &val)) { + { + LOG_ERR("Invalid Argument\n"); + return -EINVAL; + } + + if (val) + manual_offset_calibration(this); + + return count; +} + +static ssize_t sx933x_int_state_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + psx93XX_t this = global_sx933x; + LOG_DBG("Reading INT line state\n"); + return sprintf(buf, "%d\n", this->int_state); +} + +static ssize_t sx933x_reinitialize_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + psx93XX_t this = global_sx933x; + + sx933x_reinitialize(this); + + return count; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) +static struct class_attribute class_attr_reset = + __ATTR(reset, 0660, NULL, capsense_reset_store); +#ifdef CONFIG_CAPSENSE_HEADSET_STATE +static struct class_attribute class_attr_headset = + __ATTR(headset, 0660, NULL, capsense_headset_store); +#endif +static struct class_attribute class_attr_raw_data = + __ATTR(raw_data, 0660, capsense_raw_data_show, NULL); +static struct class_attribute class_attr_register_write = + __ATTR(register_write, 0660, NULL,sx933x_register_write_store); +static struct class_attribute class_attr_register_read = + __ATTR(register_read,0660, NULL,sx933x_register_read_store); +static struct class_attribute class_attr_manual_calibrate = + __ATTR(manual_calibrate, 0660, manual_offset_calibration_show, + manual_offset_calibration_store); +static struct class_attribute class_attr_int_state = + __ATTR(int_state, 0440, sx933x_int_state_show, NULL); +static struct class_attribute class_attr_reinitialize = + __ATTR(reinitialize, 0660, NULL, sx933x_reinitialize_store); + + +static struct attribute *capsense_class_attrs[] = { + &class_attr_reset.attr, +#ifdef CONFIG_CAPSENSE_HEADSET_STATE + &class_attr_headset.attr, +#endif + &class_attr_raw_data.attr, + &class_attr_register_write.attr, + &class_attr_register_read.attr, + &class_attr_manual_calibrate.attr, + &class_attr_int_state.attr, + &class_attr_reinitialize.attr, + NULL, +}; +ATTRIBUTE_GROUPS(capsense_class); +#else +static struct class_attribute capsense_class_attributes[] = { + __ATTR(reset, 0660, NULL, capsense_reset_store), + __ATTR(raw_data, 0660, capsense_raw_data_show, NULL), + __ATTR(register_write, 0660, NULL,sx933x_register_write_store), + __ATTR(register_read,0660, NULL,sx933x_register_read_store), + __ATTR(manual_calibrate, 0660, manual_offset_calibration_show,manual_offset_calibration_store), + __ATTR(int_state, 0440, sx933x_int_state_show, NULL), + __ATTR(reinitialize, 0660, NULL, sx933x_reinitialize_store), + __ATTR_NULL, +}; +#endif + +struct class capsense_class = { + .name = "capsense", + .owner = THIS_MODULE, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + .class_groups = capsense_class_groups, +#else + .class_attrs = capsense_class_attributes, +#endif +}; + +/**************************************/ + +/*! \brief Initialize I2C config from platform data + * \param this Pointer to main parent struct + */ +static void sx933x_reg_init(psx93XX_t this) +{ + psx933x_t pDevice = 0; + psx933x_platform_data_t pdata = 0; + int i = 0; + uint32_t tmpvalue; + /* configure device */ + if (this && (pDevice = this->pDevice) && (pdata = pDevice->hw)) + { + /*******************************************************************************/ + // try to initialize from device tree! + /*******************************************************************************/ + while ( i < ARRAY_SIZE(sx933x_i2c_reg_setup)) + { + /* Write all registers/values contained in i2c_reg */ + LOG_DBG("Going to Write Reg: 0x%x Value: 0x%x\n", + sx933x_i2c_reg_setup[i].reg,sx933x_i2c_reg_setup[i].val); + tmpvalue = sx933x_i2c_reg_setup[i].val; + if (sx933x_i2c_reg_setup[i].reg == SX933X_GNRLCTRL2_REG) + { + if((sx933x_i2c_reg_setup[i].val & 0x3F) == 0) + { + tmpvalue = (sx933x_i2c_reg_setup[i].val|0x3F); + } + } + sx933x_i2c_write_16bit(this, sx933x_i2c_reg_setup[i].reg, tmpvalue); + i++; + } +#ifdef USE_DTS_REG + if (this->reg_in_dts == true) + { + i = 0; + while ( i < pdata->i2c_reg_num) + { + /* Write all registers/values contained in i2c_reg */ + LOG_DBG("Going to Write Reg from dts: 0x%x Value: 0x%x\n", + pdata->pi2c_reg[i].reg,pdata->pi2c_reg[i].val); + sx933x_i2c_write_16bit(this, pdata->pi2c_reg[i].reg,pdata->pi2c_reg[i].val); + i++; + } + } +#ifdef CONFIG_CAPSENSE_HEADSET_STATE + i = 0; + LOG_INFO("set headset reg num:%d", pdata->headset_operate_reg_num); + while ( i < pdata->headset_operate_reg_num) + { + sx933x_i2c_read_16bit(this, pdata->headset_operate_reg_bck[i].reg,&(pdata->headset_operate_reg_bck[i].val)); + LOG_ERR("Read Headset init Reg : 0x%x Value: 0x%x\n", + pdata->headset_operate_reg_bck[i].reg,pdata->headset_operate_reg_bck[i].val); + i++; + } +#endif +#endif + /*******************************************************************************/ + sx933x_i2c_write_16bit(this, SX933X_CMD_REG,SX933X_PHASE_CONTROL); //enable phase control + } + else { + LOG_ERR("ERROR! platform data 0x%p\n",pDevice->hw); + } + +} + + +/*! \fn static int initialize(psx93XX_t this) + * \brief Performs all initialization needed to configure the device + * \param this Pointer to main parent struct + * \return Last used command's return value (negative if error) + */ +static int initialize(psx93XX_t this) +{ + int retry; + if (this) + { + LOG_INFO("SX933x income initialize\n"); + /* prepare reset by disabling any irq handling */ + this->irq_disabled = 1; + disable_irq(this->irq); + /* perform a reset */ + for ( retry = 10; retry > 0; retry-- ) { + if (sx933x_i2c_write_16bit(this, SX933X_RESET_REG, I2C_SOFTRESET_VALUE) >= 0) + break; + LOG_INFO("SX933x write SX933X_RESET_REG retry:%d\n", 11 - retry); + msleep(10); + } + /* wait until the reset has finished by monitoring NIRQ */ + LOG_INFO("Sent Software Reset. Waiting until device is back from reset to continue.\n"); + /* just sleep for awhile instead of using a loop with reading irq status */ + msleep(100); + sx933x_global_variable_init(this); + sx933x_reg_init(this); + + /* re-enable interrupt handling */ + enable_irq(this->irq); + + /* make sure no interrupts are pending since enabling irq will only + * work on next falling edge */ + read_regStat(this); + return 0; + } + return -ENOMEM; +} + +/*! + * \brief Handle what to do when a touch occurs + * \param this Pointer to main parent struct + */ +static void touchProcess(psx93XX_t this) +{ + int counter = 0; + u32 i = 0; + u32 touchFlag = 0; + int numberOfButtons = 0; + psx933x_t pDevice = NULL; + struct _buttonInfo *buttons = NULL; + struct input_dev *input = NULL; + + struct _buttonInfo *pCurrentButton = NULL; + + if (this && (pDevice = this->pDevice)) + { + sx933x_i2c_read_16bit(this, SX933X_STAT0_REG, &i); + LOG_DBG("touchProcess STAT0_REG:0x%08x\n", i); + + buttons = pDevice->pbuttonInformation->buttons; + numberOfButtons = pDevice->pbuttonInformation->buttonSize; + + if (unlikely( buttons==NULL )) + { + LOG_ERR(":ERROR!! buttons NULL!!!\n"); + return; + } + + for (counter = 0; counter < numberOfButtons; counter++) + { + if (buttons[counter].enabled == false) { + LOG_DBG("touchProcess %s disabled, ignor this\n", buttons[counter].name); + continue; + } + if (buttons[counter].used== false) { + LOG_DBG("touchProcess %s unused, ignor this\n", buttons[counter].name); + continue; + } + pCurrentButton = &buttons[counter]; + if (pCurrentButton==NULL) + { + LOG_ERR("ERROR!! current button at index: %d NULL!!!\n", counter); + return; // ERRORR!!!! + } + input = pCurrentButton->input_dev; + if (input==NULL) + { + LOG_ERR("ERROR!! current button input at index: %d NULL!!!\n", counter); + return; // ERRORR!!!! + } + + touchFlag = i & (pCurrentButton->ProxMask | pCurrentButton->BodyMask); + if (touchFlag == (pCurrentButton->ProxMask | pCurrentButton->BodyMask)) { + if (pCurrentButton->state == BODYACTIVE) + LOG_DBG(" %s already BODYACTIVE\n", pCurrentButton->name); + else { + input_report_abs(input, ABS_DISTANCE, 2); + input_sync(input); + pCurrentButton->state = BODYACTIVE; + LOG_DBG(" %s report 5mm BODYACTIVE\n", pCurrentButton->name); + } + } else if (touchFlag == pCurrentButton->ProxMask) { + if (pCurrentButton->state == PROXACTIVE) + LOG_DBG(" %s already PROXACTIVE\n", pCurrentButton->name); + else { + input_report_abs(input, ABS_DISTANCE, 1); + input_sync(input); + pCurrentButton->state = PROXACTIVE; + LOG_DBG(" %s report 15mm PROXACTIVE\n", pCurrentButton->name); + } + }else if (touchFlag == 0) { + if (pCurrentButton->state == IDLE) + LOG_DBG("%s already released.\n", pCurrentButton->name); + else { + input_report_abs(input, ABS_DISTANCE, 0); + input_sync(input); + pCurrentButton->state = IDLE; + LOG_DBG("%s report released.\n", pCurrentButton->name); + } + } + } + LOG_DBG("Leaving touchProcess()\n"); + } +} + +#ifdef CONFIG_CAPSENSE_FLIP_CAL +static int read_dt_regs(struct device *dev, const char *dt_field, int *num_regs, struct smtc_reg_data **regs) +{ + int byte_len; + int rc; + struct device_node *node = dev->of_node; + + if (of_find_property(node, dt_field, &byte_len)) { + *regs = (struct smtc_reg_data *) + devm_kzalloc(dev, byte_len, GFP_KERNEL); + + if (*regs == NULL) + return -ENOMEM; + + *num_regs = byte_len / sizeof(struct smtc_reg_data); + + rc = of_property_read_u32_array(node, + dt_field, + (u32 *)*regs, + byte_len / sizeof(u32)); + + if (rc < 0) { + LOG_ERR("Couldn't read %s regs rc = %d\n", dt_field, rc); + return -ENODEV; + } + + if (*num_regs) + return 0; + } + return 1; +} + +static bool parse_flip_dt_params(struct sx933x_platform_data *pdata, struct device *dev) +{ + struct device_node *node = dev->of_node; + + if (of_property_read_u32(node,"flip-gpio-when-open", + &pdata->phone_flip_open_val)) + return false; + + if (read_dt_regs(dev, "flip-open-regs", + &pdata->num_flip_open_regs, &pdata->flip_open_regs)) + return false; + + if (read_dt_regs(dev, "flip-closed-regs", + &pdata->num_flip_closed_regs, &pdata->flip_closed_regs)) + return false; + + LOG_INFO("Parsed reg update on open/close OK\n"); + + return true; +} +#endif + +static int sx933x_parse_dt(struct sx933x_platform_data *pdata, struct device *dev) +{ + struct device_node *dNode = dev->of_node; + enum of_gpio_flags flags; + int rc; + + if (dNode == NULL) + return -ENODEV; + + rc = of_property_read_u32(dNode,"Semtech,power-supply-type",&pdata->power_supply_type); + if(rc < 0){ + pdata->power_supply_type = SX933X_POWER_SUPPLY_TYPE_PMIC_LDO; + LOG_INFO("pmic ldo is the default if not set power-supply-type in dt\n"); + } + + switch(pdata->power_supply_type){ + case SX933X_POWER_SUPPLY_TYPE_PMIC_LDO: + /* using regulator_get() to fetch power_supply in sx933x_probe()*/ + break; + case SX933X_POWER_SUPPLY_TYPE_ALWAYS_ON: + /* power supply always on: no need fetch others control in drivers */ + break; + case SX933X_POWER_SUPPLY_TYPE_EXTERNAL_LDO: + /* parse the gpio number for external LDO enable pin*/ + pdata->eldo_gpio = of_get_named_gpio_flags(dNode, + "Semtech,eldo-gpio",0,&flags); + LOG_INFO("used eLDO_gpio 0x%x \n", pdata->eldo_gpio); + break; + default: + LOG_INFO("Error power_supply_type: 0x%x \n", pdata->power_supply_type); + break; + } + + pdata->irq_gpio= of_get_named_gpio_flags(dNode, + "Semtech,nirq-gpio", 0, &flags); + irq_gpio_num = pdata->irq_gpio; + if (pdata->irq_gpio < 0){ + LOG_ERR("get irq_gpio error\n"); + return -ENODEV; + } + + pdata->button_used_flag = 0; + of_property_read_u32(dNode,"Semtech,button-flag",&pdata->button_used_flag); + LOG_INFO("used button 0x%x \n", pdata->button_used_flag); + + pdata->ref_phase_a = -1; + pdata->ref_phase_b = -1; + if ( of_property_read_u32(dNode,"Semtech,ref-phases-a",&pdata->ref_phase_a) ) + { + LOG_ERR("[SX933x]: %s - get ref-phases error\n", __func__); + } + if ( of_property_read_u32(dNode,"Semtech,ref-phases-b",&pdata->ref_phase_b) ) + { + LOG_ERR("[SX933x]: %s - get ref-phases-b error\n", __func__); + } + LOG_INFO("[SX933x]: %s ref_phase_a= %d ref_phase_b= %d\n", + __func__, pdata->ref_phase_a, pdata->ref_phase_b); + +#ifdef USE_DTS_REG + // load in registers from device tree + of_property_read_u32(dNode,"Semtech,reg-num",&pdata->i2c_reg_num); + // layout is register, value, register, value.... + // if an extra item is after just ignore it. reading the array in will cause it to fail anyway + LOG_INFO("size of elements %d \n", pdata->i2c_reg_num); + if (pdata->i2c_reg_num > 0) + { + // initialize platform reg data array + pdata->pi2c_reg = devm_kzalloc(dev,sizeof(struct smtc_reg_data)*pdata->i2c_reg_num, GFP_KERNEL); + if (unlikely(pdata->pi2c_reg == NULL)) + { + LOG_ERR("size of elements %d alloc error\n", pdata->i2c_reg_num); + return -ENOMEM; + } + + // initialize the array + if (of_property_read_u32_array(dNode,"Semtech,reg-init",(u32*)&(pdata->pi2c_reg[0]),sizeof(struct smtc_reg_data)*pdata->i2c_reg_num/sizeof(u32))) + return -ENOMEM; + } +#ifdef CONFIG_CAPSENSE_HEADSET_STATE + of_property_read_u32(dNode,"Semtech,headset-reg-num",&pdata->headset_operate_reg_num); + LOG_INFO("size of headset operate reg elements %d \n", pdata->headset_operate_reg_num); + if (pdata->headset_operate_reg_num > 0) + { + pdata->headset_operate_reg = devm_kzalloc(dev,sizeof(struct smtc_reg_data)*pdata->headset_operate_reg_num, GFP_KERNEL); + if (unlikely(pdata->headset_operate_reg == NULL)) + { + LOG_ERR("size of elements %d alloc error\n", pdata->headset_operate_reg_num); + return -ENOMEM; + } + pdata->headset_operate_reg_bck = devm_kzalloc(dev,sizeof(struct smtc_reg_data)*pdata->headset_operate_reg_num, GFP_KERNEL); + if (unlikely(pdata->headset_operate_reg_bck == NULL)) + { + LOG_ERR("size of elements %d alloc error\n", pdata->headset_operate_reg_num); + return -ENOMEM; + } + + if (of_property_read_u32_array(dNode,"Semtech,headset-reg",(u32*)&(pdata->headset_operate_reg[0]), + sizeof(struct smtc_reg_data)*pdata->headset_operate_reg_num/sizeof(u32))) + return -ENOMEM; + if (of_property_read_u32_array(dNode,"Semtech,headset-reg",(u32*)&(pdata->headset_operate_reg_bck[0]), + sizeof(struct smtc_reg_data)*pdata->headset_operate_reg_num/sizeof(u32))) + return -ENOMEM; + } +#endif +#endif +#ifdef CONFIG_CAPSENSE_FLIP_CAL + pdata->phone_flip_update_regs = parse_flip_dt_params(pdata, dev); +#endif + pdata->reinit_on_i2c_failure = of_property_read_bool(dNode, "reinit-on-i2c-failure"); + + LOG_INFO("-[%d] parse_dt complete\n", pdata->irq_gpio); + return 0; +} + +/* get the NIRQ state (1->NIRQ-low, 0->NIRQ-high) */ +static int sx933x_init_platform_hw(struct i2c_client *client) +{ + psx93XX_t this = i2c_get_clientdata(client); + struct sx933x *pDevice = NULL; + struct sx933x_platform_data *pdata = NULL; + + int rc = 0; + + LOG_INFO("init_platform_hw start!"); + + if (this && (pDevice = this->pDevice) && (pdata = pDevice->hw)) + { + if (gpio_is_valid(pdata->irq_gpio)) + { + rc = gpio_request(pdata->irq_gpio, "sx933x_irq_gpio"); + if (rc < 0) + { + LOG_ERR("SX933x Request gpio. Fail![%d]\n", rc); + return rc; + } + rc = gpio_direction_input(pdata->irq_gpio); + if (rc < 0) + { + LOG_ERR("SX933x Set gpio direction. Fail![%d]\n", rc); + return rc; + } + this->irq = client->irq = gpio_to_irq(pdata->irq_gpio); + } + else + { + LOG_ERR("SX933x Invalid irq gpio num.(init)\n"); + } + } + else + { + LOG_ERR("Do not init platform HW"); + } + return rc; +} + +static void sx933x_exit_platform_hw(struct i2c_client *client) +{ + psx93XX_t this = i2c_get_clientdata(client); + struct sx933x *pDevice = NULL; + struct sx933x_platform_data *pdata = NULL; + + if (this && (pDevice = this->pDevice) && (pdata = pDevice->hw)) + { + if (gpio_is_valid(pdata->irq_gpio)) + { + gpio_free(pdata->irq_gpio); + } + else + { + LOG_ERR("Invalid irq gpio num.(exit)\n"); + } + } + return; +} + +static int capsensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + bool disableFlag = true; + int i = 0; + u32 temp = 0x0; + + for (i = 0; i < ARRAY_SIZE(psmtcButtons); i++) { + if (strcmp(sensors_cdev->name, psmtcButtons[i].name) == 0) { + if (enable == 1) { + LOG_INFO("enable cap sensor : %s\n", sensors_cdev->name); + sx933x_i2c_read_16bit(global_sx933x, SX933X_GNRLCTRL2_REG, &temp); + temp = temp | 0x0000001F; + LOG_DBG("set reg 0x%x val 0x%x\n", SX933X_GNRLCTRL2_REG, temp); + sx933x_i2c_write_16bit(global_sx933x, SX933X_GNRLCTRL2_REG, temp); + psmtcButtons[i].enabled = true; + input_report_abs(psmtcButtons[i].input_dev, ABS_DISTANCE, 0); + input_sync(psmtcButtons[i].input_dev); + + manual_offset_calibration(global_sx933x); + } else if (enable == 0) { + LOG_INFO("disable cap sensor : %s\n", sensors_cdev->name); + psmtcButtons[i].enabled = false; + input_report_abs(psmtcButtons[i].input_dev, ABS_DISTANCE, -1); + input_sync(psmtcButtons[i].input_dev); + } else { + LOG_ERR("unknown enable symbol\n"); + } + } + } + + //if all chs disabled, then disable all + for (i = 0; i < ARRAY_SIZE(psmtcButtons); i++) { + if (psmtcButtons[i].enabled) { + disableFlag = false; + break; + } + } + if (disableFlag) { + LOG_INFO("disable all chs\n"); + sx933x_i2c_read_16bit(global_sx933x, SX933X_GNRLCTRL2_REG, &temp); + LOG_DBG("read reg 0x%x val 0x%x\n", SX933X_GNRLCTRL2_REG, temp); + temp = temp & 0xFFFFFFE0; + LOG_DBG("set reg 0x%x val 0x%x\n", SX933X_GNRLCTRL2_REG, temp); + sx933x_i2c_write_16bit(global_sx933x, SX933X_GNRLCTRL2_REG, temp); + } + return 0; +} + +static int capsensor_set_poll_delay(struct sensors_classdev *sensors_cdev, unsigned int val) +{ + LOG_DBG("Dummy poll_delay called with %d\n", val); + return 0; +} + +static int capsensor_flush(struct sensors_classdev *sensors_cdev) +{ + LOG_DBG("Dummy flush called\n"); + return 0; +} + +#ifdef CONFIG_CAPSENSE_USB_CAL +static void ps_notify_callback_work(struct work_struct *work) +{ + u32 temp = 0; + sx933x_i2c_read_16bit(global_sx933x, SX933X_GNRLCTRL2_REG, &temp); + if (temp & 0x0000001F) { + LOG_DBG("USB state change, Going to force calibrate\n"); + manual_offset_calibration(global_sx933x); + } +} + +static int ps_get_state(struct power_supply *psy, bool *present) +{ + union power_supply_propval pval = {0}; + int retval; + + retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, + &pval); + if (retval) { + LOG_DBG("%s psy get property failed\n", psy->desc->name); + return retval; + } + *present = (pval.intval) ? true : false; + LOG_DBG("%s is %s\n", psy->desc->name, + (*present) ? "present" : "not present"); + return 0; +} + +static int ps_notify_callback(struct notifier_block *self, + unsigned long event, void *p) +{ + struct sx933x_platform_data *data = + container_of(self, struct sx933x_platform_data, ps_notif); + struct power_supply *psy = p; + bool present; + int retval; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0) + if (event == PSY_EVENT_PROP_CHANGED +#else + if ((event == PSY_EVENT_PROP_ADDED || event == PSY_EVENT_PROP_CHANGED) +#endif + && psy && psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, "usb", sizeof("usb")) && data) { + LOG_DBG("ps notification: event = %lu\n", event); + retval = ps_get_state(psy, &present); + if (retval) { + return retval; + } + + if (event == PSY_EVENT_PROP_CHANGED) { + if (data->ps_is_present == present) { + LOG_DBG("ps present state not change\n"); + return 0; + } + } + data->ps_is_present = present; + schedule_work(&data->ps_notify_work); + } + +#ifdef CONFIG_CAPSENSE_ATTACH_CAL + if (event == PSY_EVENT_PROP_CHANGED + && psy && psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, "phone", sizeof("phone")) && data) { + LOG_DBG("phone ps notification: event = %lu\n", event); + + retval = ps_get_state(psy, &present); + if (retval) + return retval; + + if (data->phone_is_present != present) { + data->phone_is_present = present; + schedule_work(&data->ps_notify_work); + } + } +#endif + + return 0; +} + +#ifdef CONFIG_CAPSENSE_FLIP_CAL +static void write_flip_regs(int num_regs, struct smtc_reg_data *regs) +{ + int i; + u32 temp = 0; + + /* Disable if we are on */ + sx933x_i2c_read_16bit(global_sx933x, SX933X_GNRLCTRL2_REG, &temp); + sx933x_i2c_write_16bit(global_sx933x, SX933X_GNRLCTRL2_REG, temp & 0xFFFFFFE0); + + for(i=0; i < num_regs; i++) + { + /* Write all registers/values contained in i2c_reg */ + LOG_DBG("Going to Write Reg from dts: 0x%x Value: 0x%x\n", + regs[i].reg, regs[i].val); + sx933x_i2c_write_16bit(global_sx933x, + regs[i].reg, regs[i].val); + } + + /* If one of the sensors is on, re-enable it */ + for (i=0; i < ARRAY_SIZE(psmtcButtons); i++) { + if (psmtcButtons[i].enabled) { + sx933x_i2c_write_16bit(global_sx933x, SX933X_GNRLCTRL2_REG, temp | 0x0000001F); + break; + } + } +} + +static void update_flip_regs(struct sx933x_platform_data *data, unsigned long state) +{ + if (data->phone_flip_update_regs) { + if (state == data->phone_flip_open_val) { + /* Flip open */ + LOG_DBG("Writing %d regs on open\n", + data->num_flip_open_regs); + write_flip_regs(data->num_flip_open_regs, data->flip_open_regs); + } else { + /* Flip closed */ + LOG_DBG("Writing %d regs on close\n", + data->num_flip_closed_regs); + write_flip_regs(data->num_flip_closed_regs, data->flip_closed_regs); + } + } +} + +static int flip_notify_callback(struct notifier_block *self, + unsigned long state, void *p) +{ + struct sx933x_platform_data *data = + container_of(self, struct sx933x_platform_data, flip_notif); + struct extcon_dev *edev = p; + + if(data->ext_flip_det == edev) { + if(data->phone_flip_state != state) { + update_flip_regs(data, state); + data->phone_flip_state = state; + schedule_work(&data->ps_notify_work); + } + } + + return 0; +} +#endif +#endif + +static void sx933x_i2c_watchdog_work(struct work_struct *work); + +/*! \fn static int sx933x_probe(struct i2c_client *client, const struct i2c_device_id *id) + * \brief Probe function + * \param client pointer to i2c_client + * \param id pointer to i2c_device_id + * \return Whether probe was successful + */ +static int sx933x_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int i = 0; + int err = 0; + + psx93XX_t this = 0; + psx933x_t pDevice = 0; + psx933x_platform_data_t pplatData = 0; +#ifdef CONFIG_CAPSENSE_USB_CAL + struct power_supply *psy = NULL; +#endif + struct totalButtonInformation *pButtonInformationData = NULL; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + + LOG_INFO("sx933x_probe()\n"); + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) + { + LOG_ERR("Check i2c functionality.Fail!\n"); + err = -EIO; + return err; + } + + this = devm_kzalloc(&client->dev,sizeof(sx93XX_t), GFP_KERNEL); /* create memory for main struct */ + LOG_DBG("Initialized Main Memory: 0x%p\n",this); + + pButtonInformationData = devm_kzalloc(&client->dev , sizeof(struct totalButtonInformation), GFP_KERNEL); + if (!pButtonInformationData) + { + LOG_ERR("Failed to allocate memory(totalButtonInformation)\n"); + err = -ENOMEM; + return err; + } + + pButtonInformationData->buttonSize = ARRAY_SIZE(psmtcButtons); + pButtonInformationData->buttons = psmtcButtons; + pplatData = devm_kzalloc(&client->dev,sizeof(struct sx933x_platform_data), GFP_KERNEL); + if (!pplatData) + { + LOG_ERR("platform data is required!\n"); + return -EINVAL; + } + pplatData->get_is_nirq_low = sx933x_get_nirq_state; + pplatData->pbuttonInformation = pButtonInformationData; + + client->dev.platform_data = pplatData; + err = sx933x_parse_dt(pplatData, &client->dev); + if (err) + { + LOG_ERR("could not setup pin\n"); + return ENODEV; + } + + pplatData->init_platform_hw = sx933x_init_platform_hw; + LOG_INFO("SX933x init_platform_hw done!\n"); + + if (this) + { + LOG_INFO("SX933x initialize start!!"); + /* In case we need to reinitialize data + * (e.q. if suspend reset device) */ + this->init = initialize; + /* shortcut to read status of interrupt */ + this->refreshStatus = read_regStat; + /* pointer to function from platform data to get pendown + * (1->NIRQ=0, 0->NIRQ=1) */ + this->get_nirq_low = pplatData->get_is_nirq_low; + /* save irq in case we need to reference it */ + this->irq = client->irq; + /* do we need to create an irq timer after interrupt ? */ + this->useIrqTimer = 0; + + /* Setup function to call on corresponding reg irq source bit */ + if (MAX_NUM_STATUS_BITS>= 8) + { + this->statusFunc[0] = 0; /* TXEN_STAT */ + this->statusFunc[1] = 0; /* UNUSED */ + this->statusFunc[2] = touchProcess; /* body&table */ + this->statusFunc[3] = read_rawData; /* CONV_STAT */ + this->statusFunc[4] = touchProcess; /* COMP_STAT */ + this->statusFunc[5] = touchProcess; /* RELEASE_STAT */ + this->statusFunc[6] = touchProcess; /* TOUCH_STAT */ + this->statusFunc[7] = 0; /* RESET_STAT */ + } + + /* setup i2c communication */ + this->bus = client; + i2c_set_clientdata(client, this); + + /* record device struct */ + this->pdev = &client->dev; + + /* create memory for device specific struct */ + this->pDevice = pDevice = devm_kzalloc(&client->dev,sizeof(sx933x_t), GFP_KERNEL); + LOG_DBG("initialized Device Specific Memory: 0x%p\n",pDevice); + + if (pDevice) + { + /* for accessing items in user data (e.g. calibrate) */ + err = class_register(&capsense_class); + if (err < 0) { + LOG_ERR("Create fsys class failed (%d)\n", err); + return err; + } + + /*restore sys/class/capsense label*/ + kobject_uevent(&capsense_class.p->subsys.kobj, KOBJ_CHANGE); + + /* Add Pointer to main platform data struct */ + pDevice->hw = pplatData; + + /* Check if we hava a platform initialization function to call*/ + if (pplatData->init_platform_hw) + pplatData->init_platform_hw(client); + + /* Initialize the button information initialized with keycodes */ + pDevice->pbuttonInformation = pplatData->pbuttonInformation; + + for (i = 0; i < pButtonInformationData->buttonSize; i++) + { + if (pplatData->button_used_flag>>i & 0x01) { + pButtonInformationData->buttons[i].used = true; + pButtonInformationData->buttons[i].state = IDLE; + pButtonInformationData->buttons[i].input_dev = input_allocate_device(); + if (!pButtonInformationData->buttons[i].input_dev) + return -ENOMEM; + pButtonInformationData->buttons[i].input_dev->name = pButtonInformationData->buttons[i].name; + /* Set all the keycodes */ + __set_bit(EV_ABS, pButtonInformationData->buttons[i].input_dev->evbit); + input_set_abs_params(pButtonInformationData->buttons[i].input_dev, ABS_DISTANCE, -1, 100, 0, 0); + + err = input_register_device(pButtonInformationData->buttons[i].input_dev); + /* report a unused val, then first val will report after enable */ + input_report_abs(pButtonInformationData->buttons[i].input_dev, ABS_DISTANCE, -1); + input_sync(pButtonInformationData->buttons[i].input_dev); + + pButtonInformationData->buttons[i].sensors_capsensor_cdev.sensors_enable = capsensor_set_enable; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.sensors_poll_delay = capsensor_set_poll_delay; /* Dummy function */ + pButtonInformationData->buttons[i].sensors_capsensor_cdev.sensors_flush = capsensor_flush; /* Dummy function */ + pButtonInformationData->buttons[i].sensors_capsensor_cdev.name = pButtonInformationData->buttons[i].name; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.vendor = "semtech"; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.version = 1; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.type = SENSOR_TYPE_MOTO_CAPSENSE; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.max_range = "5"; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.resolution = "5.0"; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.sensor_power = "3"; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.min_delay = 0; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.fifo_reserved_event_count = 0; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.fifo_max_event_count = 0; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.delay_msec = 100; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.enabled = 0; + pButtonInformationData->buttons[i].enabled = false; + + err = sensors_classdev_register(&pButtonInformationData->buttons[i].input_dev->dev, &pButtonInformationData->buttons[i].sensors_capsensor_cdev); + if (err < 0) + LOG_ERR("create %d cap sensor_class file failed (%d)\n", i, err); + } + } + } + + switch(pplatData->power_supply_type){ + case SX933X_POWER_SUPPLY_TYPE_PMIC_LDO: + pplatData->cap_vdd = regulator_get(&client->dev, "cap_vdd"); + if (IS_ERR(pplatData->cap_vdd)) { + if (PTR_ERR(pplatData->cap_vdd) == -EPROBE_DEFER) { + err = PTR_ERR(pplatData->cap_vdd); + return err; + } + LOG_INFO("Failed to get regulator\n"); + } else { + LOG_INFO("with cap_vdd\n"); + err = regulator_enable(pplatData->cap_vdd); + if (err) { + regulator_put(pplatData->cap_vdd); + LOG_ERR("Error %d enable regulator\n", + err); + return err; + } + pplatData->cap_vdd_en = true; + LOG_INFO("cap_vdd regulator is %s\n", + regulator_is_enabled(pplatData->cap_vdd) ? + "on" : "off"); + } + break; + case SX933X_POWER_SUPPLY_TYPE_ALWAYS_ON: + LOG_INFO("using always on power supply\n"); + break; + case SX933X_POWER_SUPPLY_TYPE_EXTERNAL_LDO: + LOG_INFO("enable external LDO, en_gpio:%d\n", + pplatData->eldo_gpio); + err = gpio_request(pplatData->eldo_gpio, "sx933x_eldo_gpio"); + if (err < 0){ + LOG_ERR("SX933x Request eLDO gpio. Fail![%d]\n", err); + return err; + } + err = gpio_direction_output(pplatData->eldo_gpio,1); + if(err < 0){ + LOG_ERR("can not enable external LDO,%d", err); + return err; + } + pplatData->eldo_vdd_en = true; + msleep(20); + break; + } + +#ifdef CONFIG_CAPSENSE_USB_CAL + /*notify usb state*/ + INIT_WORK(&pplatData->ps_notify_work, ps_notify_callback_work); + pplatData->ps_notif.notifier_call = ps_notify_callback; + err = power_supply_reg_notifier(&pplatData->ps_notif); + if (err) + LOG_ERR("Unable to register ps_notifier: %d\n", err); + + psy = power_supply_get_by_name("usb"); + if (psy) { + err = ps_get_state(psy, &pplatData->ps_is_present); + if (err) { + LOG_ERR("psy get property failed rc=%d\n", err); + power_supply_unreg_notifier(&pplatData->ps_notif); + } + } +#ifdef CONFIG_CAPSENSE_FLIP_CAL + if (of_property_read_bool(client->dev.of_node, "extcon")) { + pplatData->flip_notif.notifier_call = flip_notify_callback; + pplatData->ext_flip_det = + extcon_get_edev_by_phandle(&client->dev, 0); + if (IS_ERR(pplatData->ext_flip_det)) { + pplatData->ext_flip_det = NULL; + LOG_ERR("failed to get extcon flip dev\n"); + } else { + if(extcon_register_notifier(pplatData->ext_flip_det, + EXTCON_MECHANICAL, &pplatData->flip_notif)) + LOG_ERR("failed to register extcon flip dev notifier\n"); + else + pplatData->phone_flip_state = + extcon_get_state(pplatData->ext_flip_det, + EXTCON_MECHANICAL); + } + } else + LOG_ERR("extcon not in dev tree!\n"); +#endif +#endif + + sx93XX_IRQ_init(this); + /* call init function pointer (this should initialize all registers */ + if (this->init) { + this->init(this); + } + else { + LOG_ERR("No init function!!!!\n"); + return -ENOMEM; + } + } else { + return -1; + } + + pplatData->exit_platform_hw = sx933x_exit_platform_hw; + + if (sx933x_Hardware_Check(this) != 0) { + LOG_ERR("sx933x_Hardware_CheckFail!\n"); + //return -1; + } + + global_sx933x = this; + + if(pplatData->reinit_on_i2c_failure) { + INIT_DELAYED_WORK(&this->i2c_watchdog_work, sx933x_i2c_watchdog_work); + schedule_delayed_work(&this->i2c_watchdog_work, + msecs_to_jiffies(SX933X_I2C_WATCHDOG_TIME)); + } + +#ifdef CONFIG_CAPSENSE_FLIP_CAL + update_flip_regs(pplatData, pplatData->phone_flip_state); +#endif + LOG_INFO("sx933x_probe() Done\n"); + return 0; +} + +/*! \fn static int sx933x_remove(struct i2c_client *client) + * \brief Called when device is to be removed + * \param client Pointer to i2c_client struct + * \return Value 0 + */ +static int sx933x_remove(struct i2c_client *client) +{ + psx933x_platform_data_t pplatData =0; + psx933x_t pDevice = 0; + struct _buttonInfo *pCurrentbutton; + int i = 0; + psx93XX_t this = i2c_get_clientdata(client); + LOG_DBG("sx933x_remove"); + if (this && (pDevice = this->pDevice)) + { + pplatData = client->dev.platform_data; + free_irq(this->irq, this); + cancel_delayed_work_sync(&(this->dworker)); + + class_unregister(&capsense_class); +#ifdef CONFIG_CAPSENSE_USB_CAL + cancel_work_sync(&pplatData->ps_notify_work); + power_supply_unreg_notifier(&pplatData->ps_notif); +#endif + if (pplatData && pplatData->exit_platform_hw) + pplatData->exit_platform_hw(client); + + if (pplatData->cap_vdd_en) { + regulator_disable(pplatData->cap_vdd); + regulator_put(pplatData->cap_vdd); + } + + if(pplatData->eldo_vdd_en){ + gpio_direction_output(pplatData->eldo_gpio,0); + } + for (i = 0; i pbuttonInformation->buttonSize; i++) { + pCurrentbutton = &(pDevice->pbuttonInformation->buttons[i]); + if (pCurrentbutton->used == true) { + sensors_classdev_unregister(&(pCurrentbutton->sensors_capsensor_cdev)); + input_unregister_device(pCurrentbutton->input_dev); + } + } + } + return 0; +} + + +#if 1//def CONFIG_PM +/*====================================================*/ +/***** Kernel Suspend *****/ +static int sx933x_suspend(struct device *dev) +{ + psx93XX_t this = dev_get_drvdata(dev); + psx933x_t pDevice = 0; + psx933x_platform_data_t pdata = 0; + + if (this) { + /* If we happen to reinitialize during suspend we might fail so wait for it to end */ + if ((pDevice = this->pDevice) && (pdata = pDevice->hw)) { + if (pdata->reinit_on_i2c_failure) + cancel_delayed_work_sync(&this->i2c_watchdog_work); + } + + sx933x_i2c_write_16bit(this,SX933X_CMD_REG,0xD);//make sx933x in Sleep mode + LOG_DBG(LOG_TAG "sx933x suspend:disable irq!\n"); + disable_irq(this->irq); + this->suspended = 1; + } + return 0; +} +/***** Kernel Resume *****/ +static int sx933x_resume(struct device *dev) +{ + psx93XX_t this = dev_get_drvdata(dev); + psx933x_t pDevice = 0; + psx933x_platform_data_t pdata = 0; + + if (this) { + LOG_DBG(LOG_TAG "sx933x resume:enable irq!\n"); + sx93XX_schedule_work(this,0); + enable_irq(this->irq); + sx933x_i2c_write_16bit(this,SX933X_CMD_REG,0xC);//Exit from Sleep mode + this->suspended = 0; + + /* Restart the watchdog in 2 seconds */ + if ((pDevice = this->pDevice) && (pdata = pDevice->hw)){ + if (pdata->reinit_on_i2c_failure) + schedule_delayed_work(&this->i2c_watchdog_work, + msecs_to_jiffies(SX933X_I2C_WATCHDOG_TIME_ERR)); + } + } + return 0; +} +/*====================================================*/ +#else +#define sx933x_suspend NULL +#define sx933x_resume NULL +#endif /* CONFIG_PM */ + +static struct i2c_device_id sx933x_idtable[] = +{ + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sx933x_idtable); +#ifdef CONFIG_OF +static struct of_device_id sx933x_match_table[] = +{ + { .compatible = "Semtech,sx933x",}, + { }, +}; +#else +#define sx933x_match_table NULL +#endif +static const struct dev_pm_ops sx933x_pm_ops = +{ + .suspend = sx933x_suspend, + .resume = sx933x_resume, +}; +static struct i2c_driver sx933x_driver = +{ + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = sx933x_match_table, + .pm = &sx933x_pm_ops, + }, + .id_table = sx933x_idtable, + .probe = sx933x_probe, + .remove = sx933x_remove, +}; +static int __init sx933x_I2C_init(void) +{ + return i2c_add_driver(&sx933x_driver); +} +static void __exit sx933x_I2C_exit(void) +{ + i2c_del_driver(&sx933x_driver); +} + +module_init(sx933x_I2C_init); +module_exit(sx933x_I2C_exit); + +MODULE_AUTHOR("Semtech Corp. (http://www.semtech.com/)"); +MODULE_DESCRIPTION("SX933x Capacitive Proximity Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1"); + +static void sx93XX_schedule_work(psx93XX_t this, unsigned long delay) +{ + unsigned long flags; + if (this) + { + LOG_DBG("sx93XX_schedule_work()\n"); + spin_lock_irqsave(&this->lock,flags); + /* Stop any pending penup queues */ + cancel_delayed_work(&this->dworker); + //after waiting for a delay, this put the job in the kernel-global workqueue. so no need to create new thread in work queue. + schedule_delayed_work(&this->dworker,delay); + spin_unlock_irqrestore(&this->lock,flags); + } + else + LOG_ERR("sx93XX_schedule_work, NULL psx93XX_t\n"); +} + +static irqreturn_t sx93XX_irq(int irq, void *pvoid) +{ + psx93XX_t this = 0; + if (pvoid) + { + this = (psx93XX_t)pvoid; + if ((!this->get_nirq_low) || this->get_nirq_low()) + { + LOG_DBG("sx93XX_irq - call sx93XX_schedule_work\n"); + sx93XX_schedule_work(this,0); + this->int_state = 1; + } + else + { + LOG_DBG("sx93XX_irq - nirq read high\n"); + } + } + else + { + LOG_ERR("sx93XX_irq, NULL pvoid\n"); + } + return IRQ_HANDLED; +} + +static void sx93XX_worker_func(struct work_struct *work) +{ + psx93XX_t this = 0; + int status = 0; + int counter = 0; + u8 nirqLow = 0; + if (work) + { + this = container_of(work,sx93XX_t,dworker.work); + + if (!this) + { + LOG_ERR("sx93XX_worker_func, NULL sx93XX_t\n"); + return; + } + if (unlikely(this->useIrqTimer)) + { + if ((!this->get_nirq_low) || this->get_nirq_low()) + { + nirqLow = 1; + } + } + /* since we are not in an interrupt don't need to disable irq. */ + status = this->refreshStatus(this); + counter = -1; + LOG_DBG("Worker_func - Refresh Status %d, use_timer_in_irq:%d\n", status, this->useIrqTimer); + + while((++counter) < MAX_NUM_STATUS_BITS) /* counter start from MSB */ + { + if (((status>>counter) & 0x01) && (this->statusFunc[counter])) + { + LOG_DBG("SX933x Function Pointer Found. Calling\n"); + this->statusFunc[counter](this); + } + } + if (unlikely(this->useIrqTimer && nirqLow)) + { + /* Early models and if RATE=0 for newer models require a penup timer */ + /* Queue up the function again for checking on penup */ + sx93XX_schedule_work(this,msecs_to_jiffies(this->irqTimeout)); + } + } + else + { + LOG_ERR("sx93XX_worker_func, NULL work_struct\n"); + } +} + +int sx93XX_IRQ_init(psx93XX_t this) +{ + int err = 0; + if (this && this->pDevice) + { + this->int_state = 0; + /* initialize spin lock */ + spin_lock_init(&this->lock); + /* initialize worker function */ + INIT_DELAYED_WORK(&this->dworker, sx93XX_worker_func); + /* initailize interrupt reporting */ + this->irq_disabled = 0; + err = request_irq(this->irq, sx93XX_irq, IRQF_TRIGGER_FALLING, + this->pdev->driver->name, this); + if (err) + { + LOG_ERR("irq %d busy?\n", this->irq); + return err; + } + LOG_INFO("registered with irq (%d)\n", this->irq); + } + return -ENOMEM; +} + +/* Read i2c every 10 seconds, if there is an error, schedule again in 2 seconds + * and if it fails a few more times we can assume there is a device error and reset + */ +static void sx933x_i2c_watchdog_work(struct work_struct *work) +{ + static int err_cnt = 0; + psx93XX_t this = container_of(work, sx93XX_t, i2c_watchdog_work.work); + int ret; + u32 temp; + int delay = SX933X_I2C_WATCHDOG_TIME; + + LOG_DBG("sx933x_i2c_watchdog_work"); + + if(!this->suspended) { + ret = sx933x_i2c_read_16bit(this, SX933X_INFO_REG, &temp); + if (ret < 0) { + err_cnt++; + LOG_ERR("sx933x_i2c_watchdog_work err_cnt: %d", err_cnt); + delay = SX933X_I2C_WATCHDOG_TIME_ERR; + } else + err_cnt = 0; + + if (err_cnt >= 3) { + err_cnt = 0; + sx933x_reinitialize(this); + delay = SX933X_I2C_WATCHDOG_TIME; + } + } else + LOG_DBG("sx933x_i2c_watchdog_work before resume."); + + schedule_delayed_work(&this->i2c_watchdog_work, + msecs_to_jiffies(delay)); +} + +static void sx933x_reinitialize(psx93XX_t this) +{ + psx933x_t pDevice = 0; + psx933x_platform_data_t pdata = 0; + u32 temp; + int err; + int i=0; + int retry; + + if (this && (pDevice = this->pDevice) && (pdata = pDevice->hw)) { + if (!pdata->reinit_on_i2c_failure) + return; + + if (!atomic_add_unless(&this->init_busy, 1, 1)) + return; + + disable_irq(this->irq); + + regulator_disable(pdata->cap_vdd); + msleep(100); + err = regulator_enable(pdata->cap_vdd); + if (err) + LOG_ERR("Error %d enable regulator\n", err); + msleep(100); + + /* perform a reset */ + for ( retry = 10; retry > 0; retry-- ) { + if (sx933x_i2c_write_16bit(this, SX933X_RESET_REG, I2C_SOFTRESET_VALUE) >= 0) + break; + LOG_INFO("SX933x write SX933X_RESET_REG retry:%d\n", 11 - retry); + msleep(10); + } + /* wait until the reset has finished by monitoring NIRQ */ + LOG_INFO("Sent Software Reset. Waiting until device is back from reset to continue.\n"); + /* just sleep for awhile instead of using a loop with reading irq status */ + msleep(100); + + sx933x_reg_init(this); + +#ifdef CONFIG_CAPSENSE_FLIP_CAL + update_flip_regs(pdata, pdata->phone_flip_state); +#endif + + /* re-enable interrupt handling */ + enable_irq(this->irq); + + /* make sure no interrupts are pending since enabling irq will only + * work on next falling edge */ + read_regStat(this); + + /* If one of the sensors is on, re-enable it */ + sx933x_i2c_read_16bit(this, SX933X_GNRLCTRL2_REG, &temp); + for (i=0; i < ARRAY_SIZE(psmtcButtons); i++) { + if (psmtcButtons[i].enabled) { + sx933x_i2c_write_16bit(this, SX933X_GNRLCTRL2_REG, temp | 0x0000001F); + break; + } + } + + manual_offset_calibration(this); + atomic_set(&this->init_busy, 0); + LOG_ERR("reinitialized sx933x, count %d\n", this->reset_count++); + } +} diff --git a/drivers/misc/sx937x/Android.mk b/drivers/misc/sx937x/Android.mk new file mode 100644 index 000000000000..0cae60f8a4f1 --- /dev/null +++ b/drivers/misc/sx937x/Android.mk @@ -0,0 +1,22 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(SX937X_USB_CAL),true) + KBUILD_OPTIONS += CONFIG_SX937X_USB_CAL=y +endif + +ifeq ($(SX937X_USB_USE_ONLINE),true) + KBUILD_OPTIONS += CONFIG_SX937X_POWER_SUPPLY_ONLINE=y +endif + +ifeq ($(SX937X_FLIP_CAL),true) + KBUILD_OPTIONS += CONFIG_SX937X_FLIP_CAL=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := sx937x_sar.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/sensors_class.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/sx937x/Kbuild b/drivers/misc/sx937x/Kbuild new file mode 100644 index 000000000000..70d7682d7029 --- /dev/null +++ b/drivers/misc/sx937x/Kbuild @@ -0,0 +1,40 @@ +KERNEL_DIR_PATH = $(shell find $(ANDROID_BUILD_TOP)/kernel/ -name "msm-*" -type d | head -1 | grep -i "kernel/msm-*") + +KERNEL_VERSION_NAME = $(shell basename $(KERNEL_DIR_PATH)) + +KERNEL_PLATFORM_PATH = $(shell find $(ANDROID_BUILD_TOP) -maxdepth 1 -name "kernel_platform" -type d) + +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +ifneq ($(filter m y,$(CONFIG_SX937X_USB_CAL)),) + EXTRA_CFLAGS += -DCONFIG_CAPSENSE_USB_CAL +endif + +ifneq ($(filter m y,$(CONFIG_SX937X_POWER_SUPPLY_ONLINE)),) + EXTRA_CFLAGS += -DCONFIG_USE_POWER_SUPPLY_ONLINE +endif + +ifneq ($(filter m y,$(CONFIG_SX937X_FLIP_CAL)),) + EXTRA_CFLAGS += -DCONFIG_CAPSENSE_FLIP_CAL +endif + +ifneq (,$(filter river%,$(TARGET_PRODUCT))) +EXTRA_CFLAGS += -DCONFIG_CAPSENSE_USB_CAL +endif + +ifneq (,$(KERNEL_DIR_PATH)) + EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/kernel/$(KERNEL_VERSION_NAME)/drivers/base +else ifneq (,$(KERNEL_PLATFORM_PATH)) + EXTRA_CFLAGS += -I$(KERNEL_PLATFORM_PATH)/common/drivers/base +else + EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/kernel/drivers/base +endif + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers + +#sx937x_sar-y := sx937x_sar.o + +obj-m += sx937x_sar.o + diff --git a/drivers/misc/sx937x/Makefile b/drivers/misc/sx937x/Makefile new file mode 100644 index 000000000000..201497405686 --- /dev/null +++ b/drivers/misc/sx937x/Makefile @@ -0,0 +1,16 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS := $(M)/../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/misc/sx937x/sx937x_sar.c b/drivers/misc/sx937x/sx937x_sar.c new file mode 100644 index 000000000000..23ec940fab8a --- /dev/null +++ b/drivers/misc/sx937x/sx937x_sar.c @@ -0,0 +1,2078 @@ +/*! \file sx937x.c + * \brief SX937x Driver + * + * Driver for the SX937x + * Copyright (c) 2018 Semtech Corp + * + * 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. + */ +#define DRIVER_NAME "sx937x" + +#define MAX_WRITE_ARRAY_SIZE 32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* main struct, interrupt,init,pointers */ +#include "base.h" + +#define LOG_TAG "[sar SX937x]: " + +#define LOG_INFO(fmt, args...) pr_info(LOG_TAG "[INFO]" "<%s><%d>"fmt, __func__, __LINE__, ##args) +#define LOG_DBG(fmt, args...) pr_debug(LOG_TAG "[DBG]" "<%s><%d>"fmt, __func__, __LINE__, ##args) +#define LOG_ERR(fmt, args...) pr_err(LOG_TAG "[ERR]" "<%s><%d>"fmt, __func__, __LINE__, ##args) + +#define USE_DTS_REG + +#define SX937x_I2C_M_WR 0 /* for i2c Write */ +#define SX937x_I2C_M_RD 1 /* for i2c Read */ + +#define IDLE 0 +#define PROXACTIVE 1 +#define BODYACTIVE 2 + +#define MAIN_SENSOR 1 //CS1 + +/* Failer Index */ +#define SX937x_ID_ERROR 1 +#define SX937x_NIRQ_ERROR 2 +#define SX937x_CONN_ERROR 3 +#define SX937x_I2C_ERROR 4 + +#define SX937X_I2C_WATCHDOG_TIME 10000 +#define SX937X_I2C_WATCHDOG_TIME_ERR 2000 + +#define NUM_RETRY_ON_I2C_ERR 5 +#define SLEEP_BETWEEN_RETRY 10 +/*! \struct sx937x + * Specialized struct containing input event data, platform data, and + * last cap state read if needed. + */ +typedef struct sx937x +{ + pbuttonInformation_t pbuttonInformation; + psx937x_platform_data_t hw; /* specific platform data settings */ +} sx937x_t, *psx937x_t; + +static int irq_gpio_num; +static psx93XX_t global_sx937x; + +static void sx93XX_schedule_work(psx93XX_t this, unsigned long delay); +static int sx937x_get_nirq_state(void) +{ + return !gpio_get_value(irq_gpio_num); +} + +static void sx937x_reinitialize(psx93XX_t this); + +/*! \fn static int sx937x_i2c_write_16bit(psx93XX_t this, u8 address, u8 value) + * \brief Sends a write register to the device + * \param this Pointer to main parent struct + * \param address 8-bit register address + * \param value 8-bit register value to write to address + * \return Value from i2c_master_send + */ + +static int sx937x_i2c_write_16bit(psx93XX_t this, u16 reg_addr, u32 buf) +{ + int ret = -ENOMEM; + struct i2c_client *i2c = 0; + struct i2c_msg msg; + unsigned char w_buf[6]; + int num_retried = 0; + + if (this && this->bus) + { + i2c = this->bus; + w_buf[0] = (u8)(reg_addr>>8); + w_buf[1] = (u8)(reg_addr); + w_buf[2] = (u8)(buf>>24); + w_buf[3] = (u8)(buf>>16); + w_buf[4] = (u8)(buf>>8); + w_buf[5] = (u8)(buf); + + msg.addr = i2c->addr; + msg.flags = SX937x_I2C_M_WR; + msg.len = 6; //2bytes regaddr + 4bytes data + msg.buf = (u8 *)w_buf; + while(true) + { + ret = i2c_transfer(i2c->adapter, &msg, 1); + if (ret > 0) + break; + if (num_retried++ < NUM_RETRY_ON_I2C_ERR) + { + LOG_ERR("i2c write reg 0x%x error %d. Going to retry", reg_addr, ret); + if (SLEEP_BETWEEN_RETRY != 0) + msleep(SLEEP_BETWEEN_RETRY); + } + else { + LOG_ERR("i2c write reg 0x%x error %d after retried %d times", + reg_addr, ret, NUM_RETRY_ON_I2C_ERR); + break; + } + } + } + return ret; +} + +/*! \fn static int sx937x_i2c_read_16bit(psx93XX_t this, u8 address, u8 *value) + * \brief Reads a register's value from the device + * \param this Pointer to main parent struct + * \param address 8-Bit address to read from + * \param value Pointer to 8-bit value to save register value to + * \return Value from i2c_smbus_read_byte_data if < 0. else 0 + */ +static int sx937x_i2c_read_16bit(psx93XX_t this, u16 reg_addr, u32 *data32) +{ + int ret = -ENOMEM; + struct i2c_client *i2c = 0; + struct i2c_msg msg[2]; + int num_retried = 0; + u8 w_buf[2]; + u8 buf[4]; + + if (this && this->bus) + { + i2c = this->bus; + + w_buf[0] = (u8)(reg_addr>>8); + w_buf[1] = (u8)(reg_addr); + + msg[0].addr = i2c->addr; + msg[0].flags = SX937x_I2C_M_WR; + msg[0].len = 2; + msg[0].buf = (u8 *)w_buf; + + msg[1].addr = i2c->addr;; + msg[1].flags = SX937x_I2C_M_RD; + msg[1].len = 4; + msg[1].buf = (u8 *)buf; + + while (true) + { + + ret = i2c_transfer(i2c->adapter, msg, 2); + if (ret > 0){ + data32[0] = ((u32)buf[0]<<24) | ((u32)buf[1]<<16) | ((u32)buf[2]<<8) | ((u32)buf[3]); + break; + } + if (num_retried++ < NUM_RETRY_ON_I2C_ERR) + { + LOG_ERR("i2c read reg 0x%x error %d. Going to retry", reg_addr, ret); + if (SLEEP_BETWEEN_RETRY != 0) + msleep(SLEEP_BETWEEN_RETRY); + } + else { + LOG_ERR("i2c read reg 0x%x error %d after retried %d times", reg_addr, ret, NUM_RETRY_ON_I2C_ERR); + break; + } + } + + } + return ret; +} + +//static int sx937x_set_mode(psx93XX_t this, unsigned char mode); + +/*! \fn static int read_regStat(psx93XX_t this) + * \brief Shortcut to read what caused interrupt. + * \details This is to keep the drivers a unified + * function that will read whatever register(s) + * provide information on why the interrupt was caused. + * \param this Pointer to main parent struct + * \return If successful, Value of bit(s) that cause interrupt, else 0 + */ +static int read_regStat(psx93XX_t this) +{ + u32 data = 0; + if (this) + { + if (sx937x_i2c_read_16bit(this,SX937X_IRQ_SOURCE,&data) > 0) //bug + return (data & 0x00FF); + } + return 0; +} + +static int sx937x_Hardware_Check(psx93XX_t this) +{ + int ret; + u32 idCode; + u8 loop = 0; + this->failStatusCode = 0; + + //Check th IRQ Status + while(this->get_nirq_low && this->get_nirq_low()) + { + read_regStat(this); + msleep(100); + if(++loop >10) + { + this->failStatusCode = SX937x_NIRQ_ERROR; + break; + } + } + + //Check I2C Connection + ret = sx937x_i2c_read_16bit(this, SX937X_DEVICE_INFO, &idCode); + if (ret < 0) + { + this->failStatusCode = SX937x_I2C_ERROR; + LOG_ERR("Failed to read device info:failcode = 0x%x\n",this->failStatusCode); + + return ret; + } + + if (idCode >> 12 != SX937X_WHOAMI_VALUE >> 4) + { + this->failStatusCode = SX937x_ID_ERROR; + } + + LOG_INFO("sx937x idcode = 0x%x, failcode = 0x%x\n", idCode, this->failStatusCode); + return (int)this->failStatusCode; +} + +static int sx937x_global_variable_init(psx93XX_t this) +{ + this->irq_disabled = 0; + this->failStatusCode = 0; + this->reg_in_dts = true; + return 0; +} + +/*! \brief Perform a manual offset calibration + * \param this Pointer to main parent struct + * \return Value return value from the write register + */ +static int manual_offset_calibration(psx93XX_t this) +{ + int ret = 0; + ret = sx937x_i2c_write_16bit(this, SX937X_COMMAND, 0xE); + return ret; + +} + +static void read_dbg_raw(psx93XX_t this) +{ + int ph, state; + u32 uData, ph_sel; + s32 ant_use, ant_raw; + s32 avg, diff; + u16 off; + s32 adc_min, adc_max, use_flt_dlt_var; + s32 ref_a_use=0, ref_b_use=0, ref_c_use=0; + int ref_ph_a, ref_ph_b, ref_ph_c; + + psx937x_t pDevice = NULL; + psx937x_platform_data_t pdata = NULL; + + pDevice = this->pDevice; + pdata = pDevice->hw; + ref_ph_a = pdata->ref_phase_a; + ref_ph_b = pdata->ref_phase_b; + ref_ph_c = pdata->ref_phase_c; + + sx937x_i2c_read_16bit(this, SX937X_DEVICE_STATUS_A, &uData); + LOG_DBG("SX937X_STAT0_REG= 0x%X\n", uData); + + if(ref_ph_a != -1) + { + sx937x_i2c_read_16bit(this, SX937X_USEFUL_PH0 + ref_ph_a*4, &uData); + ref_a_use = (s32)uData >> 10; + } + if(ref_ph_b != -1) + { + sx937x_i2c_read_16bit(this, SX937X_USEFUL_PH0 + ref_ph_b*4, &uData); + ref_b_use = (s32)uData >> 10; + } + if(ref_ph_c != -1) + { + sx937x_i2c_read_16bit(this, SX937X_USEFUL_PH0 + ref_ph_c*4, &uData); + ref_c_use = (s32)uData >> 10; + } + + sx937x_i2c_read_16bit(this, SX937X_DEBUG_SETUP, &ph_sel); + + sx937x_i2c_read_16bit(this, SX937X_DEBUG_READBACK_0, &uData); + adc_min = (s32)uData>>10; + sx937x_i2c_read_16bit(this, SX937X_DEBUG_READBACK_1, &uData); + adc_max = (s32)uData>>10; + sx937x_i2c_read_16bit(this, SX937X_DEBUG_READBACK_2, &uData); + ant_raw = (s32)uData>>10; + sx937x_i2c_read_16bit(this, SX937X_DEBUG_READBACK_3, &uData); + use_flt_dlt_var = (s32)uData>>4; + + ph = (ph_sel >> 3) & 0x7; + + sx937x_i2c_read_16bit(this, SX937X_USEFUL_PH0 + ph*4, &uData); + ant_use = (s32)uData>>10; + + sx937x_i2c_read_16bit(this, SX937X_AVERAGE_PH0 + ph*4, &uData); + avg = (s32)uData>>10; + sx937x_i2c_read_16bit(this, SX937X_DIFF_PH0 + ph*4, &uData); + diff = (s32)uData>>10; + sx937x_i2c_read_16bit(this, SX937X_OFFSET_PH0 + ph*4*3, &uData); + off = (u16)(uData & 0x3FFF); + state = psmtcButtons[ph].state; + + LOG_DBG("SMTC_DBG PH= %d USE= %d RAW= %d PH%d_USE= %d PH%d_USE= %d PH%d_USE= %d STATE= %d AVG= %d DIFF= %d OFF= %d ADC_MIN= %d ADC_MAX= %d DLT= %d SMTC_END\n", + ph, ant_use, ant_raw, ref_ph_a, ref_a_use, ref_ph_b, ref_b_use, ref_ph_c, ref_c_use, state, avg, diff, off, adc_min, adc_max, use_flt_dlt_var); +} + +static void read_rawData(psx93XX_t this) +{ + u8 csx, index; + s32 useful, average, diff; + s32 ref_a_use=0, ref_b_use=0, ref_c_use=0; + u32 uData; + u32 use_hex, avg_hex, dif_hex, dlt_hex, dbg_ph; + u16 offset; + int state; + psx937x_t pDevice = NULL; + psx937x_platform_data_t pdata = NULL; + int ref_ph_a, ref_ph_b, ref_ph_c; + + if(this) + { + pDevice = this->pDevice; + pdata = pDevice->hw; + ref_ph_a = pdata->ref_phase_a; + ref_ph_b = pdata->ref_phase_b; + ref_ph_c = pdata->ref_phase_c; + LOG_DBG("[SX937x] ref_ph_a= %d ref_ph_b= %d ref_ph_c= %d\n", ref_ph_a, ref_ph_b, ref_ph_c); + + sx937x_i2c_read_16bit(this, SX937X_DEVICE_STATUS_A, &uData); + LOG_DBG("SX937X_DEVICE_STATUS_A= 0x%X\n", uData); + sx937x_i2c_read_16bit(this, SX937X_DEBUG_SETUP, &dbg_ph); + dbg_ph = (dbg_ph >> 3) & 0x7; + sx937x_i2c_read_16bit(this, SX937X_DEBUG_READBACK_3, &dlt_hex); + + if(ref_ph_a != -1) + { + sx937x_i2c_read_16bit(this, SX937X_USEFUL_PH0 + ref_ph_a*4, &uData); + ref_a_use = (s32)uData >> 10; + } + if(ref_ph_b != -1) + { + sx937x_i2c_read_16bit(this, SX937X_USEFUL_PH0 + ref_ph_b*4, &uData); + ref_b_use = (s32)uData >> 10; + } + if(ref_ph_c != -1) + { + sx937x_i2c_read_16bit(this, SX937X_USEFUL_PH0 + ref_ph_c*4, &uData); + ref_c_use = (s32)uData >> 10; + } + + for(csx =0; csx<8; csx++) + { + index = csx*4; + sx937x_i2c_read_16bit(this, SX937X_USEFUL_PH0 + index, &use_hex); + useful = (s32)use_hex>>10; + sx937x_i2c_read_16bit(this, SX937X_AVERAGE_PH0 + index, &avg_hex); + average = (s32)avg_hex>>10; + sx937x_i2c_read_16bit(this, SX937X_DIFF_PH0 + index, &dif_hex); + diff = (s32)dif_hex>>10; + sx937x_i2c_read_16bit(this, SX937X_OFFSET_PH0 + index*3, &uData); + offset = (u16)(uData & 0x3FFF); + + state = psmtcButtons[csx].state; + + LOG_DBG("SMTC_DAT PH= %d DIFF= %d USE= %d PH%d_USE= %d PH%d_USE= %d PH%d_USE= %d STATE= %d OFF= %d AVG= %d SMTC_END\n", + csx, diff, useful, ref_ph_a, ref_a_use, ref_ph_b, ref_b_use, ref_ph_c, ref_c_use, state, offset, average); + + LOG_DBG("SMTC_HEX PH= %d USE= 0x%X AVG= 0x%X DIF= 0x%X PH%d_DLT= 0x%X SMTC_END\n", + csx, use_hex, avg_hex, dif_hex, dbg_ph, dlt_hex); + } + + read_dbg_raw(this); + } +} + +static ssize_t capsense_reset_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + u32 temp = 0; + sx937x_i2c_read_16bit(global_sx937x, SX937X_GENERAL_SETUP, &temp); + if (!count) + return -EINVAL; + + if (!strncmp(buf, "reset", 5) || !strncmp(buf, "1", 1)) { + if (temp & 0x000000FF) { + LOG_DBG("Going to refresh baseline\n"); + manual_offset_calibration(global_sx937x); + } + } + + return count; +} + +#ifdef CONFIG_CAPSENSE_HEADSET_STATE +static ssize_t capsense_headset_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + u32 reg_temp = 0; + int i; + psx937x_t pDevice = global_sx937x->pDevice; + psx937x_platform_data_t pdata = pDevice->hw; + + sx937x_i2c_read_16bit(global_sx937x, SX937X_GENERAL_SETUP, ®_temp); + if (!count || pdata == NULL) + return -EINVAL; + + if (!strncmp(buf, "1", 1)) { + LOG_INFO("headset in update reg num:%d\n", pdata->headset_operate_reg_num); + for (i = 0; i < pdata->headset_operate_reg_num; i++) + { + sx937x_i2c_write_16bit(global_sx937x, pdata->headset_operate_reg[i].reg, pdata->headset_operate_reg[i].val); + LOG_INFO("set Reg 0x%x Value: 0x%x\n", + pdata->headset_operate_reg[i].reg,pdata->headset_operate_reg[i].val); + } + //cal sensor + if (reg_temp & 0x000000FF) { + LOG_DBG("Going to refresh baseline\n"); + manual_offset_calibration(global_sx937x); + } + } + + if (!strncmp(buf, "0", 1)) { + LOG_INFO("headset out back reg num:%d\n", pdata->headset_operate_reg_num); + for (i = 0; i < pdata->headset_operate_reg_num; i++) + { + sx937x_i2c_write_16bit(global_sx937x, pdata->headset_operate_reg_bck[i].reg, pdata->headset_operate_reg_bck[i].val); + LOG_INFO("set Reg 0x%x Value: 0x%x\n", + pdata->headset_operate_reg_bck[i].reg,pdata->headset_operate_reg_bck[i].val); + } + //cal sensor + if (reg_temp & 0x000000FF) { + LOG_DBG("Going to refresh baseline\n"); + manual_offset_calibration(global_sx937x); + } + } + return count; +} +#endif + +static ssize_t capsense_raw_data_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + char *p = buf; + int csx; + s32 useful, average, diff; + u32 uData; + u16 offset; + + psx93XX_t this = global_sx937x; + if(this) { + for(csx =0; csx<8; csx++) { + sx937x_i2c_read_16bit(this, SX937X_USEFUL_PH0 + csx*4, &uData); + useful = (s32)uData>>10; + sx937x_i2c_read_16bit(this, SX937X_AVERAGE_PH0 + csx*4, &uData); + average = (s32)uData>>10; + sx937x_i2c_read_16bit(this, SX937X_DIFF_PH0 + csx*4, &uData); + diff = (s32)uData>>10; + sx937x_i2c_read_16bit(this, SX937X_OFFSET_PH0 + csx*12, &uData); + offset = (u16)(uData & 0x3FFF); + p += snprintf(p, PAGE_SIZE, "PH= %d Useful = %d Average = %d DIFF = %d Offset = %d \n", + csx,useful,average,diff,offset); + } + } + return (p-buf); +} + +static ssize_t sx937x_register_write_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + u32 reg_address = 0, val = 0; + psx93XX_t this = global_sx937x; + + if (sscanf(buf, "%x,%x", ®_address, &val) != 2) + { + LOG_ERR("The number of data are wrong\n"); + return -EINVAL; + } + + sx937x_i2c_write_16bit(this, reg_address, val); + + LOG_DBG("Register(0x%x) data(0x%x)\n", reg_address, val); + return count; +} + +static int sx937x_temp_regist = 0; +static u32 sx937x_temp_val = 0; +static ssize_t sx937x_register_read_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + int nirq_state = 0; + psx93XX_t this = global_sx937x; + + if (sscanf(buf, "%x", &sx937x_temp_regist) != 1) + { + LOG_ERR(" The number of data are wrong\n"); + return -EINVAL; + } + + sx937x_i2c_read_16bit(this, sx937x_temp_regist, &sx937x_temp_val); + nirq_state = sx937x_get_nirq_state(); + + LOG_DBG("Register(0x%2x) data(0x%4x) nirq_state(%d)\n", sx937x_temp_regist, sx937x_temp_val, nirq_state); + return count; +} + +static ssize_t sx937x_register_read_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + return sprintf(buf, "Register(0x%2x) data(0x%4x)\n", sx937x_temp_regist, sx937x_temp_val); +} + +static ssize_t reg_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u32 *p = (u32*)buf; +#ifdef CONFIG_CAPSENSE_POWER_CONTROL_SUPPORT + psx937x_t pDevice = global_sx937x->pDevice; + psx937x_platform_data_t pdata = pDevice->hw; + + if (!pdata->cap_vdd_en) + if ( power_on_chip(global_sx937x) != 0) + return -1; +#endif + + psx93XX_t this = global_sx937x; + if(this->read_flag){ + this->read_flag = 0; + if (sx937x_i2c_read_16bit(this, this->read_reg, p) < 0) + return -1; + LOG_DBG("%s : read_reg = 0x%x, val = 0x%x\n",__func__,this->read_reg,*p); + return 4; + } + return -1; +} + +/* + reg attr is for TCMD on MTK only, + buf[6]-read_flag: + 0-real write, + 1-just transfer the reg value want to be readed +*/ +static ssize_t reg_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + psx93XX_t this = global_sx937x; + u16 regaddr = 0; + u32 val = 0; + int i = 0; + +#ifdef CONFIG_CAPSENSE_POWER_CONTROL_SUPPORT + psx937x_t pDevice = global_sx937x->pDevice; + psx937x_platform_data_t pdata = pDevice->hw; + + if (!pdata->cap_vdd_en) + if ( power_on_chip(global_sx937x) != 0) + return -1; +#endif + + if( count != 7){ + LOG_ERR("%s :params error[ count == %lu !=2]\n",__func__,count); + return -1; + } + for(i = 0 ; i < count ; i++) + LOG_DBG("%s : buf[%d] = 0x%x\n",__func__,i,buf[i]); + if(buf[6] == 0){ + regaddr = ((u16)buf[0]<<8) | (u16)buf[1]; + val= ((u32)buf[2]<<24) | ((u32)buf[3]<<16) | ((u32)buf[4]<<8) | ((u32)buf[5]); + if (sx937x_i2c_write_16bit(this, regaddr, val) < 0) + return -1; + } else if(buf[6] == 1) { + this->read_reg = ((u16)buf[0]<<8) | (u16)buf[1]; + this->read_flag = 1; + LOG_DBG("-----------\n"); + } + return count; +} + +static ssize_t manual_offset_calibration_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + u32 reg_value = 0; + psx93XX_t this = global_sx937x; + + LOG_DBG("Reading IRQSTAT_REG\n"); + sx937x_i2c_read_16bit(this,SX937X_IRQ_SOURCE,®_value); + return sprintf(buf, "%d\n", reg_value); +} + + +static ssize_t manual_offset_calibration_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + unsigned long val; + psx93XX_t this = global_sx937x; + + if (kstrtoul(buf, 10, &val)) //(strict_strtoul(buf, 10, &val)) { + { + LOG_ERR("Invalid Argument\n"); + return -EINVAL; + } + + if (val) + manual_offset_calibration(this); + + return count; +} + +static ssize_t sx937x_int_state_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + psx93XX_t this = global_sx937x; + LOG_DBG("Reading INT line state\n"); + return sprintf(buf, "%d\n", this->int_state); +} + +static ssize_t sx937x_reinitialize_store(struct class *class, + struct class_attribute *attr, + const char *buf, size_t count) +{ + psx93XX_t this = global_sx937x; + + sx937x_reinitialize(this); + + return count; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) +static struct class_attribute class_attr_reset = + __ATTR(reset, 0660, NULL, capsense_reset_store); +#ifdef CONFIG_CAPSENSE_HEADSET_STATE +static struct class_attribute class_attr_headset = + __ATTR(headset, 0660, NULL, capsense_headset_store); +#endif +static struct class_attribute class_attr_raw_data = + __ATTR(raw_data, 0660, capsense_raw_data_show, NULL); +static struct class_attribute class_attr_register_write = + __ATTR(register_write, 0660, NULL,sx937x_register_write_store); +static struct class_attribute class_attr_register_read = + __ATTR(register_read,0660, sx937x_register_read_show,sx937x_register_read_store); +static struct class_attribute class_attr_manual_calibrate = + __ATTR(manual_calibrate, 0660, manual_offset_calibration_show, + manual_offset_calibration_store); +static struct class_attribute class_attr_int_state = + __ATTR(int_state, 0440, sx937x_int_state_show, NULL); +static struct class_attribute class_attr_reinitialize = + __ATTR(reinitialize, 0660, NULL, sx937x_reinitialize_store); +static struct class_attribute class_attr_reg = + __ATTR(reg, 0660, reg_show, reg_store); + +static struct attribute *capsense_class_attrs[] = { + &class_attr_reset.attr, +#ifdef CONFIG_CAPSENSE_HEADSET_STATE + &class_attr_headset.attr, +#endif + &class_attr_raw_data.attr, + &class_attr_register_write.attr, + &class_attr_register_read.attr, + &class_attr_manual_calibrate.attr, + &class_attr_int_state.attr, + &class_attr_reinitialize.attr, + &class_attr_reg.attr, + NULL, +}; +ATTRIBUTE_GROUPS(capsense_class); +#else +static struct class_attribute capsense_class_attributes[] = { + __ATTR(reset, 0660, NULL, capsense_reset_store), + __ATTR(raw_data, 0660, capsense_raw_data_show, NULL), + __ATTR(register_write, 0660, NULL,sx937x_register_write_store), + __ATTR(register_read,0660, sx937x_register_read_show,sx937x_register_read_store), + __ATTR(manual_calibrate, 0660, manual_offset_calibration_show,manual_offset_calibration_store), + __ATTR(int_state, 0440, sx937x_int_state_show, NULL), + __ATTR(reinitialize, 0660, NULL, sx937x_reinitialize_store), + __ATTR(reg, 0660, reg_show, reg_store), + __ATTR_NULL, +}; +#endif + +struct class capsense_class = { + .name = "capsense", + .owner = THIS_MODULE, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + .class_groups = capsense_class_groups, +#else + .class_attrs = capsense_class_attributes, +#endif +}; + +/**************************************/ + +/*! \brief Initialize I2C config from platform data + * \param this Pointer to main parent struct + */ +static void sx937x_reg_init(psx93XX_t this) +{ + psx937x_t pDevice = 0; + psx937x_platform_data_t pdata = 0; + int i = 0; + //uint32_t tmpvalue; + + /* configure device */ + if (this && (pDevice = this->pDevice) && (pdata = pDevice->hw)) + { + /*******************************************************************************/ + // try to initialize from device tree! + /*******************************************************************************/ +#if 0 + while ( i < ARRAY_SIZE(sx937x_i2c_reg_setup)) + { + /* Write all registers/values contained in i2c_reg */ + LOG_DBG("Going to Write Reg: 0x%x Value: 0x%x\n", + sx937x_i2c_reg_setup[i].reg,sx937x_i2c_reg_setup[i].val); + tmpvalue = sx937x_i2c_reg_setup[i].val; + if (sx937x_i2c_reg_setup[i].reg == SX937X_GENERAL_SETUP) + { + if((sx937x_i2c_reg_setup[i].val & 0xFF) == 0) + { + tmpvalue = (sx937x_i2c_reg_setup[i].val|0xFF); + } + } + sx937x_i2c_write_16bit(this, sx937x_i2c_reg_setup[i].reg, tmpvalue); + i++; + } +#endif +#ifdef USE_DTS_REG + if (this->reg_in_dts == true) + { + i = 0; + while ( i < pdata->i2c_reg_num) + { + /* Write all registers/values contained in i2c_reg */ + LOG_DBG("Going to Write Reg from dts: 0x%x Value: 0x%x\n", + pdata->pi2c_reg[i].reg,pdata->pi2c_reg[i].val); + sx937x_i2c_write_16bit(this, pdata->pi2c_reg[i].reg,pdata->pi2c_reg[i].val); + i++; + } + } +#ifdef CONFIG_CAPSENSE_HEADSET_STATE + i = 0; + LOG_INFO("set headset reg num:%d", pdata->headset_operate_reg_num); + while ( i < pdata->headset_operate_reg_num) + { + sx937x_i2c_read_16bit(this, pdata->headset_operate_reg_bck[i].reg,&(pdata->headset_operate_reg_bck[i].val)); + LOG_ERR("Read Headset init Reg : 0x%x Value: 0x%x\n", + pdata->headset_operate_reg_bck[i].reg,pdata->headset_operate_reg_bck[i].val); + i++; + } +#endif +#endif + /*******************************************************************************/ + sx937x_i2c_write_16bit(this, SX937X_COMMAND, 0xF); //enable phase control + } + else { + LOG_ERR("ERROR! platform data 0x%p\n",pDevice->hw); + } + +} + + +/*! \fn static int initialize(psx93XX_t this) + * \brief Performs all initialization needed to configure the device + * \param this Pointer to main parent struct + * \return Last used command's return value (negative if error) + */ +static int initialize(psx93XX_t this) +{ + int retry; + if (this) + { + LOG_INFO("SX937x income initialize\n"); + /* prepare reset by disabling any irq handling */ + this->irq_disabled = 1; + disable_irq(this->irq); + /* perform a reset */ + for ( retry = 10; retry > 0; retry-- ) { + if (sx937x_i2c_write_16bit(this, SX937X_DEVICE_RESET, 0xDE) >= 0) + break; + LOG_INFO("SX937x write SX937X_RESET_REG retry:%d\n", 11 - retry); + msleep(10); + } + /* wait until the reset has finished by monitoring NIRQ */ + LOG_INFO("Sent Software Reset. Waiting until device is back from reset to continue.\n"); + /* just sleep for awhile instead of using a loop with reading irq status */ + msleep(100); + sx937x_global_variable_init(this); + sx937x_reg_init(this); + + /* re-enable interrupt handling */ + enable_irq(this->irq); + + /* make sure no interrupts are pending since enabling irq will only + * work on next falling edge */ + read_regStat(this); + return 0; + } + return -ENOMEM; +} + +/*! + * \brief Handle what to do when a touch occurs + * \param this Pointer to main parent struct + */ +static void touchProcess(psx93XX_t this) +{ + int counter = 0; + u32 i = 0; + u32 touchFlag = 0; + int numberOfButtons = 0; + psx937x_t pDevice = NULL; + struct _buttonInfo *buttons = NULL; + struct input_dev *input = NULL; + + struct _buttonInfo *pCurrentButton = NULL; + + if (this && (pDevice = this->pDevice)) + { + sx937x_i2c_read_16bit(this, SX937X_DEVICE_STATUS_A, &i); + LOG_DBG("touchProcess STAT0_REG:0x%08x\n", i); + + buttons = pDevice->pbuttonInformation->buttons; + numberOfButtons = pDevice->pbuttonInformation->buttonSize; + + if (unlikely( buttons==NULL )) + { + LOG_ERR(":ERROR!! buttons NULL!!!\n"); + return; + } + + for (counter = 0; counter < numberOfButtons; counter++) + { + if (buttons[counter].enabled == false) { + LOG_DBG("touchProcess %s disabled, ignor this\n", buttons[counter].name); + continue; + } + if (buttons[counter].used== false) { + LOG_DBG("touchProcess %s unused, ignor this\n", buttons[counter].name); + continue; + } + pCurrentButton = &buttons[counter]; + if (pCurrentButton==NULL) + { + LOG_ERR("ERROR!! current button at index: %d NULL!!!\n", counter); + return; // ERRORR!!!! + } + input = pCurrentButton->input_dev; + if (input==NULL) + { + LOG_ERR("ERROR!! current button input at index: %d NULL!!!\n", counter); + return; // ERRORR!!!! + } + + touchFlag = i & (pCurrentButton->ProxMask | pCurrentButton->BodyMask); + if (touchFlag == (pCurrentButton->ProxMask | pCurrentButton->BodyMask)) { + if (pCurrentButton->state == BODYACTIVE) + LOG_DBG(" %s already BODYACTIVE\n", pCurrentButton->name); + else { + input_report_abs(input, ABS_DISTANCE, 2); + input_sync(input); + pCurrentButton->state = BODYACTIVE; + LOG_DBG(" %s report 5mm BODYACTIVE\n", pCurrentButton->name); + } + } else if (touchFlag == pCurrentButton->ProxMask) { + if (pCurrentButton->state == PROXACTIVE) + LOG_DBG(" %s already PROXACTIVE\n", pCurrentButton->name); + else { + input_report_abs(input, ABS_DISTANCE, 1); + input_sync(input); + pCurrentButton->state = PROXACTIVE; + LOG_DBG(" %s report 15mm PROXACTIVE\n", pCurrentButton->name); + } + }else if (touchFlag == 0) { + if (pCurrentButton->state == IDLE) + LOG_DBG("%s already released.\n", pCurrentButton->name); + else { + input_report_abs(input, ABS_DISTANCE, 0); + input_sync(input); + pCurrentButton->state = IDLE; + LOG_DBG("%s report released.\n", pCurrentButton->name); + } + } + } + LOG_DBG("Leaving touchProcess()\n"); + } +} + +#define DSI_DISPALY_MAX_LEN 64 +const char *get_dsi_display_name(void) +{ + const char *bootargs = NULL; + char *end = NULL; + char *idx = NULL; + struct device_node *np; + static char display[DSI_DISPALY_MAX_LEN] = {'\0'}; + + if (display[0] != '\0') + return display; + + np = of_find_node_by_path("/chosen"); + if (np == NULL) + return NULL; + + if (of_property_read_string(np, "bootargs", &bootargs) != 0) + goto putnode; + + idx = strstr(bootargs, "msm_drm.dsi_display0="); + if (idx) { + end = strpbrk(idx, " "); + idx = strpbrk(idx, "="); + if (idx && end > idx) + strlcpy(display, idx + 1, end - idx); + } + + return display; + +putnode: + of_node_put(np); + return NULL; +} + +#ifdef CONFIG_CAPSENSE_FLIP_CAL +static int read_dt_regs(struct device *dev, const char *dt_field, int *num_regs, struct smtc_reg_data **regs) +{ + int byte_len; + int rc; + struct device_node *node = dev->of_node; + + if (of_find_property(node, dt_field, &byte_len)) { + *regs = (struct smtc_reg_data *) + devm_kzalloc(dev, byte_len, GFP_KERNEL); + + if (*regs == NULL) + return -ENOMEM; + + *num_regs = byte_len / sizeof(struct smtc_reg_data); + + rc = of_property_read_u32_array(node, + dt_field, + (u32 *)*regs, + byte_len / sizeof(u32)); + + if (rc < 0) { + LOG_ERR("Couldn't read %s regs rc = %d\n", dt_field, rc); + return -ENODEV; + } + + if (*num_regs) + return 0; + } + return 1; +} + +static bool parse_flip_dt_params(struct sx937x_platform_data *pdata, struct device *dev) +{ + struct device_node *node = dev->of_node; + + if (of_property_read_u32(node,"flip-gpio-when-open", + &pdata->phone_flip_open_val)) + return false; + + if (read_dt_regs(dev, "flip-open-regs", + &pdata->num_flip_open_regs, &pdata->flip_open_regs)) + return false; + + if (read_dt_regs(dev, "flip-closed-regs", + &pdata->num_flip_closed_regs, &pdata->flip_closed_regs)) + return false; + + LOG_INFO("Parsed reg update on open/close OK\n"); + + return true; +} +#endif + +static int sx937x_parse_dt(struct sx937x_platform_data *pdata, struct device *dev) +{ + struct device_node *dNode = dev->of_node; + enum of_gpio_flags flags; + int i, rc, support_panel_num; + const char *panel_name, *current_dsi; + const char *reg_group_name = "Semtech,reg-init"; + + if (dNode == NULL) + return -ENODEV; + + rc = of_property_read_u32(dNode,"Semtech,power-supply-type",&pdata->power_supply_type); + if(rc < 0){ + pdata->power_supply_type = SX937X_POWER_SUPPLY_TYPE_PMIC_LDO; + LOG_INFO("pmic ldo is the default if not set power-supply-type in dt\n"); + } + + switch(pdata->power_supply_type){ + case SX937X_POWER_SUPPLY_TYPE_PMIC_LDO: + /* using regulator_get() to fetch power_supply in sx937x_probe()*/ + break; + case SX937X_POWER_SUPPLY_TYPE_ALWAYS_ON: + /* power supply always on: no need fetch others control in drivers */ + break; + case SX937X_POWER_SUPPLY_TYPE_EXTERNAL_LDO: + /* parse the gpio number for external LDO enable pin*/ + pdata->eldo_gpio = of_get_named_gpio_flags(dNode, + "Semtech,eldo-gpio",0,&flags); + LOG_INFO("used eLDO_gpio 0x%x \n", pdata->eldo_gpio); + break; + default: + LOG_INFO("Error power_supply_type: 0x%x \n", pdata->power_supply_type); + break; + } + + pdata->irq_gpio= of_get_named_gpio_flags(dNode, + "Semtech,nirq-gpio", 0, &flags); + irq_gpio_num = pdata->irq_gpio; + if (pdata->irq_gpio < 0){ + LOG_ERR("get irq_gpio error\n"); + return -ENODEV; + } + + pdata->button_used_flag = 0; + of_property_read_u32(dNode,"Semtech,button-flag",&pdata->button_used_flag); + LOG_INFO("used button 0x%x \n", pdata->button_used_flag); + + pdata->ref_phase_a = -1; + pdata->ref_phase_b = -1; + pdata->ref_phase_c = -1; + if ( of_property_read_u32(dNode,"Semtech,ref-phases-a",&pdata->ref_phase_a) ) + { + LOG_ERR("[SX937x]: %s - get ref-phases error\n", __func__); + return -ENODEV; + } + if ( of_property_read_u32(dNode,"Semtech,ref-phases-b",&pdata->ref_phase_b) ) + { + LOG_ERR("[SX937x]: %s - get ref-phases-b error\n", __func__); + return -ENODEV; + } + if ( of_property_read_u32(dNode,"Semtech,ref-phases-c",&pdata->ref_phase_c) ) + { + LOG_ERR("[SX937x]: %s - get ref-phases-c error\n", __func__); + return -ENODEV; + } + if (pdata->ref_phase_a == 0xff) pdata->ref_phase_a = -1; + if (pdata->ref_phase_b == 0xff) pdata->ref_phase_b = -1; + if (pdata->ref_phase_c == 0xff) pdata->ref_phase_c = -1; + + LOG_INFO("[SX937x]: %s ref_phase_a= %d ref_phase_b= %d ref_phase_c= %d\n", + __func__, pdata->ref_phase_a, pdata->ref_phase_b, pdata->ref_phase_c); + +#ifdef USE_DTS_REG + // load in registers from device tree + of_property_read_u32(dNode,"Semtech,reg-num",&pdata->i2c_reg_num); + // layout is register, value, register, value.... + // if an extra item is after just ignore it. reading the array in will cause it to fail anyway + LOG_INFO("size of elements %d \n", pdata->i2c_reg_num); + if (pdata->i2c_reg_num > 0) + { + // initialize platform reg data array + pdata->pi2c_reg = devm_kzalloc(dev,sizeof(struct smtc_reg_data)*pdata->i2c_reg_num, GFP_KERNEL); + if (unlikely(pdata->pi2c_reg == NULL)) + { + LOG_ERR("size of elements %d alloc error\n", pdata->i2c_reg_num); + return -ENOMEM; + } + + current_dsi = get_dsi_display_name(); + /* + if three's no "support-panel-num" in dts or + there's no matched panel_name(maybe it is a bare board), + the default reg_group_name will be "Semtech,reg-init" + */ + if(!of_property_read_u32(dNode,"support-panel-num",&support_panel_num)) + { + LOG_INFO("support_panel_num is %d \n", support_panel_num); + for (i = 0; i < support_panel_num; i++) { + if(of_property_read_string_index(dNode, "support-panel-names", i, &panel_name)) + { + LOG_ERR("[SX937x]: %s - get support-panel-names error\n", __func__); + return -ENODEV; + } + + if(strstr(current_dsi, panel_name)) + { + if(of_property_read_string_index(dNode, "reg-groups-names", i, ®_group_name)) + { + LOG_ERR("[SX937x]: %s - get reg-groups-names error\n", __func__); + return -ENODEV; + } + LOG_INFO("dsi display matched, index %d, support dsi: %s, reg group: %s\n", i, panel_name, reg_group_name); + break; + } + } + } else { + LOG_INFO("multi panels isn't supported\n"); + } + + // initialize the array + if (of_property_read_u32_array(dNode,reg_group_name,(u32*)&(pdata->pi2c_reg[0]),sizeof(struct smtc_reg_data)*pdata->i2c_reg_num/sizeof(u32))) + return -ENOMEM; + } +#ifdef CONFIG_CAPSENSE_HEADSET_STATE + of_property_read_u32(dNode,"Semtech,headset-reg-num",&pdata->headset_operate_reg_num); + LOG_INFO("size of headset operate reg elements %d \n", pdata->headset_operate_reg_num); + if (pdata->headset_operate_reg_num > 0) + { + pdata->headset_operate_reg = devm_kzalloc(dev,sizeof(struct smtc_reg_data)*pdata->headset_operate_reg_num, GFP_KERNEL); + if (unlikely(pdata->headset_operate_reg == NULL)) + { + LOG_ERR("size of elements %d alloc error\n", pdata->headset_operate_reg_num); + return -ENOMEM; + } + pdata->headset_operate_reg_bck = devm_kzalloc(dev,sizeof(struct smtc_reg_data)*pdata->headset_operate_reg_num, GFP_KERNEL); + if (unlikely(pdata->headset_operate_reg_bck == NULL)) + { + LOG_ERR("size of elements %d alloc error\n", pdata->headset_operate_reg_num); + return -ENOMEM; + } + + if (of_property_read_u32_array(dNode,"Semtech,headset-reg",(u32*)&(pdata->headset_operate_reg[0]), + sizeof(struct smtc_reg_data)*pdata->headset_operate_reg_num/sizeof(u32))) + return -ENOMEM; + if (of_property_read_u32_array(dNode,"Semtech,headset-reg",(u32*)&(pdata->headset_operate_reg_bck[0]), + sizeof(struct smtc_reg_data)*pdata->headset_operate_reg_num/sizeof(u32))) + return -ENOMEM; + } +#endif +#endif +#ifdef CONFIG_CAPSENSE_FLIP_CAL + pdata->phone_flip_update_regs = parse_flip_dt_params(pdata, dev); +#endif + pdata->reinit_on_i2c_failure = of_property_read_bool(dNode, "reinit-on-i2c-failure"); + + LOG_INFO("-[%d] parse_dt complete\n", pdata->irq_gpio); + return 0; +} + +/* get the NIRQ state (1->NIRQ-low, 0->NIRQ-high) */ +static int sx937x_init_platform_hw(struct i2c_client *client) +{ + psx93XX_t this = i2c_get_clientdata(client); + struct sx937x *pDevice = NULL; + struct sx937x_platform_data *pdata = NULL; + + int rc = 0; + + LOG_INFO("init_platform_hw start!"); + + if (this && (pDevice = this->pDevice) && (pdata = pDevice->hw)) + { + if (gpio_is_valid(pdata->irq_gpio)) + { + rc = gpio_request(pdata->irq_gpio, "sx937x_irq_gpio"); + if (rc < 0) + { + LOG_ERR("SX937x Request gpio. Fail![%d]\n", rc); + return rc; + } + rc = gpio_direction_input(pdata->irq_gpio); + if (rc < 0) + { + LOG_ERR("SX937x Set gpio direction. Fail![%d]\n", rc); + return rc; + } + this->irq = client->irq = gpio_to_irq(pdata->irq_gpio); + } + else + { + LOG_ERR("SX937x Invalid irq gpio num.(init)\n"); + } + } + else + { + LOG_ERR("Do not init platform HW"); + } + return rc; +} + +static void sx937x_exit_platform_hw(struct i2c_client *client) +{ + psx93XX_t this = i2c_get_clientdata(client); + struct sx937x *pDevice = NULL; + struct sx937x_platform_data *pdata = NULL; + + if (this && (pDevice = this->pDevice) && (pdata = pDevice->hw)) + { + if (gpio_is_valid(pdata->irq_gpio)) + { + gpio_free(pdata->irq_gpio); + } + else + { + LOG_ERR("Invalid irq gpio num.(exit)\n"); + } + } + return; +} + +static int capsensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + bool disableFlag = true; + int i = 0; + u32 temp = 0x0; + + for (i = 0; i < ARRAY_SIZE(psmtcButtons); i++) { + if (strcmp(sensors_cdev->name, psmtcButtons[i].name) == 0) { + if (enable == 1) { + LOG_INFO("enable cap sensor : %s\n", sensors_cdev->name); + sx937x_i2c_read_16bit(global_sx937x, SX937X_GENERAL_SETUP, &temp); + temp = temp | 0x0000007F; + LOG_DBG("set reg 0x%x val 0x%x\n", SX937X_GENERAL_SETUP, temp); + sx937x_i2c_write_16bit(global_sx937x, SX937X_GENERAL_SETUP, temp); + psmtcButtons[i].enabled = true; + input_report_abs(psmtcButtons[i].input_dev, ABS_DISTANCE, 0); + input_sync(psmtcButtons[i].input_dev); + + manual_offset_calibration(global_sx937x); + } else if (enable == 0) { + LOG_INFO("disable cap sensor : %s\n", sensors_cdev->name); + psmtcButtons[i].enabled = false; + input_report_abs(psmtcButtons[i].input_dev, ABS_DISTANCE, -1); + input_sync(psmtcButtons[i].input_dev); + } else { + LOG_ERR("unknown enable symbol\n"); + } + } + } + + //if all chs disabled, then disable all + for (i = 0; i < ARRAY_SIZE(psmtcButtons); i++) { + if (psmtcButtons[i].enabled) { + disableFlag = false; + break; + } + } + if (disableFlag) { + LOG_INFO("disable all chs\n"); + sx937x_i2c_read_16bit(global_sx937x, SX937X_GENERAL_SETUP, &temp); + LOG_DBG("read reg 0x%x val 0x%x\n", SX937X_GENERAL_SETUP, temp); + temp = temp & 0xFFFFFF00; + LOG_DBG("set reg 0x%x val 0x%x\n", SX937X_GENERAL_SETUP, temp); + sx937x_i2c_write_16bit(global_sx937x, SX937X_GENERAL_SETUP, temp); + } + return 0; +} + +#ifdef CONFIG_CAPSENSE_USB_CAL +static void ps_notify_callback_work(struct work_struct *work) +{ + u32 temp = 0; + sx937x_i2c_read_16bit(global_sx937x, SX937X_GENERAL_SETUP, &temp); + if (temp & 0x000000FF) { + LOG_DBG("USB state change, Going to force calibrate\n"); + manual_offset_calibration(global_sx937x); + } +} + +static int ps_get_state(struct power_supply *psy, bool *present) +{ + union power_supply_propval pval = {0}; + int retval; + +#ifdef CONFIG_USE_POWER_SUPPLY_ONLINE + retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, + &pval); +#else + retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, + &pval); +#endif + + if (retval) { + LOG_DBG("%s psy get property failed\n", psy->desc->name); + return retval; + } + *present = (pval.intval) ? true : false; + +#ifdef CONFIG_USE_POWER_SUPPLY_ONLINE + LOG_DBG("%s is %s\n", psy->desc->name, + (*present) ? "online" : "not online"); +#else + LOG_DBG("%s is %s\n", psy->desc->name, + (*present) ? "present" : "not present"); +#endif + + return 0; +} + +static int ps_notify_callback(struct notifier_block *self, + unsigned long event, void *p) +{ + struct sx937x_platform_data *data = + container_of(self, struct sx937x_platform_data, ps_notif); + struct power_supply *psy = p; + bool present; + int retval; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,9,0) + if (event == PSY_EVENT_PROP_CHANGED +#else + if ((event == PSY_EVENT_PROP_ADDED || event == PSY_EVENT_PROP_CHANGED) +#endif + && psy && psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, "usb", sizeof("usb")) && data) { + LOG_DBG("ps notification: event = %lu\n", event); + retval = ps_get_state(psy, &present); + if (retval) { + return retval; + } + + if (event == PSY_EVENT_PROP_CHANGED) { + if (data->ps_is_present == present) { + LOG_DBG("ps present state not change\n"); + return 0; + } + } + data->ps_is_present = present; + schedule_work(&data->ps_notify_work); + } + +#ifdef CONFIG_CAPSENSE_ATTACH_CAL + if (event == PSY_EVENT_PROP_CHANGED + && psy && psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, "phone", sizeof("phone")) && data) { + LOG_DBG("phone ps notification: event = %lu\n", event); + + retval = ps_get_state(psy, &present); + if (retval) + return retval; + + if (data->phone_is_present != present) { + data->phone_is_present = present; + schedule_work(&data->ps_notify_work); + } + } +#endif + + return 0; +} + +#ifdef CONFIG_CAPSENSE_FLIP_CAL +static void write_flip_regs(int num_regs, struct smtc_reg_data *regs) +{ + int i; + + for(i=0; i < num_regs; i++) + { + /* Write all registers/values contained in i2c_reg */ + LOG_DBG("Going to Write Reg from dts: 0x%x Value: 0x%x\n", + regs[i].reg, regs[i].val); + sx937x_i2c_write_16bit(global_sx937x, + regs[i].reg, regs[i].val); + } +} + +static void update_flip_regs(struct sx937x_platform_data *data, unsigned long state) +{ + if (data->phone_flip_update_regs) { + if (state == data->phone_flip_open_val) { + /* Flip open */ + LOG_DBG("Writing %d regs on open\n", + data->num_flip_open_regs); + write_flip_regs(data->num_flip_open_regs, data->flip_open_regs); + } else { + /* Flip closed */ + LOG_DBG("Writing %d regs on close\n", + data->num_flip_closed_regs); + write_flip_regs(data->num_flip_closed_regs, data->flip_closed_regs); + } + } +} + +static int flip_notify_callback(struct notifier_block *self, + unsigned long state, void *p) +{ + struct sx937x_platform_data *data = + container_of(self, struct sx937x_platform_data, flip_notif); + struct extcon_dev *edev = p; + + if(data->ext_flip_det == edev) { + if(data->phone_flip_state != state) { + update_flip_regs(data, state); + data->phone_flip_state = state; + schedule_work(&data->ps_notify_work); + } + } + + return 0; +} +#endif +#endif + +static void sx937x_i2c_watchdog_work(struct work_struct *work); + +/*! \fn static int sx937x_probe(struct i2c_client *client, const struct i2c_device_id *id) + * \brief Probe function + * \param client pointer to i2c_client + * \param id pointer to i2c_device_id + * \return Whether probe was successful + */ +static int sx937x_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int i = 0; + int err = 0; + + psx93XX_t this = 0; + psx937x_t pDevice = 0; + psx937x_platform_data_t pplatData = 0; +#ifdef CONFIG_CAPSENSE_USB_CAL + struct power_supply *psy = NULL; +#endif + struct totalButtonInformation *pButtonInformationData = NULL; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + + LOG_INFO("sx937x_probe()\n"); + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) + { + LOG_ERR("Check i2c functionality.Fail!\n"); + err = -EIO; + return err; + } + + this = devm_kzalloc(&client->dev,sizeof(sx93XX_t), GFP_KERNEL); /* create memory for main struct */ + LOG_DBG("Initialized Main Memory: 0x%p\n",this); + if (!this){ + LOG_ERR("Failed to create this \n"); + return -ENOMEM; + } + + /* setup i2c communication */ + this->bus = client; + i2c_set_clientdata(client, this); + + /* record device struct */ + this->pdev = &client->dev; + + pButtonInformationData = devm_kzalloc(&client->dev , sizeof(struct totalButtonInformation), GFP_KERNEL); + if (!pButtonInformationData) + { + LOG_ERR("Failed to allocate memory(totalButtonInformation)\n"); + err = -ENOMEM; + return err; + } + + pButtonInformationData->buttonSize = ARRAY_SIZE(psmtcButtons); + pButtonInformationData->buttons = psmtcButtons; + pplatData = devm_kzalloc(&client->dev,sizeof(struct sx937x_platform_data), GFP_KERNEL); + if (!pplatData) + { + LOG_ERR("platform data is required!\n"); + return -EINVAL; + } + pplatData->get_is_nirq_low = sx937x_get_nirq_state; + pplatData->pbuttonInformation = pButtonInformationData; + + client->dev.platform_data = pplatData; + err = sx937x_parse_dt(pplatData, &client->dev); + if (err) + { + LOG_ERR("could not setup pin\n"); + return ENODEV; + } + + pplatData->init_platform_hw = sx937x_init_platform_hw; + LOG_INFO("SX937x init_platform_hw done!\n"); + + switch(pplatData->power_supply_type) { + case SX937X_POWER_SUPPLY_TYPE_PMIC_LDO: + pplatData->cap_vdd = regulator_get(&client->dev, "cap_vdd"); + if (IS_ERR(pplatData->cap_vdd)) { + if (PTR_ERR(pplatData->cap_vdd) == -EPROBE_DEFER) { + err = PTR_ERR(pplatData->cap_vdd); + return err; + } + LOG_INFO("Failed to get regulator\n"); + } else { + LOG_INFO("with cap_vdd\n"); + err = regulator_enable(pplatData->cap_vdd); + if (err) { + regulator_put(pplatData->cap_vdd); + LOG_ERR("Error %d enable regulator\n", + err); + return err; + } + pplatData->cap_vdd_en = true; + LOG_INFO("cap_vdd regulator is %s\n", + regulator_is_enabled(pplatData->cap_vdd) ? + "on" : "off"); + msleep(10); + } + break; + case SX937X_POWER_SUPPLY_TYPE_ALWAYS_ON: + LOG_INFO("using always on power supply\n"); + break; + case SX937X_POWER_SUPPLY_TYPE_EXTERNAL_LDO: + LOG_INFO("enable external LDO, en_gpio:%d\n", + pplatData->eldo_gpio); + err = gpio_request(pplatData->eldo_gpio, "sx937x_eldo_gpio"); + if (err < 0){ + LOG_ERR("SX937x Request eLDO gpio. Fail![%d]\n", err); + return err; + } + err = gpio_direction_output(pplatData->eldo_gpio,1); + if(err < 0){ + LOG_ERR("can not enable external LDO,%d", err); + return err; + } + pplatData->eldo_vdd_en = true; + msleep(20); + break; + } + + if (sx937x_Hardware_Check(this) != 0) { + LOG_ERR("sx937x_Hardware_Check Fail!\n"); + goto FREE_PMIC; + } + + if (this) + { + LOG_INFO("SX937x initialize start!!"); + /* In case we need to reinitialize data + * (e.q. if suspend reset device) */ + this->init = initialize; + /* shortcut to read status of interrupt */ + this->refreshStatus = read_regStat; + /* pointer to function from platform data to get pendown + * (1->NIRQ=0, 0->NIRQ=1) */ + this->get_nirq_low = pplatData->get_is_nirq_low; + /* save irq in case we need to reference it */ + this->irq = client->irq; + /* do we need to create an irq timer after interrupt ? */ + this->useIrqTimer = 0; + + /* Setup function to call on corresponding reg irq source bit */ + if (MAX_NUM_STATUS_BITS>= 8) + { + this->statusFunc[0] = 0; /* TXEN_STAT */ + this->statusFunc[1] = 0; /* UNUSED */ + this->statusFunc[2] = touchProcess; /* body&table */ + this->statusFunc[3] = read_rawData; /* CONV_STAT */ + this->statusFunc[4] = touchProcess; /* COMP_STAT */ + this->statusFunc[5] = touchProcess; /* RELEASE_STAT */ + this->statusFunc[6] = touchProcess; /* TOUCH_STAT */ + this->statusFunc[7] = 0; /* RESET_STAT */ + } + + /* create memory for device specific struct */ + this->pDevice = pDevice = devm_kzalloc(&client->dev,sizeof(sx937x_t), GFP_KERNEL); + LOG_DBG("initialized Device Specific Memory: 0x%p\n",pDevice); + + if (pDevice) + { + /* for accessing items in user data (e.g. calibrate) */ + err = class_register(&capsense_class); + if (err < 0) { + LOG_ERR("Create fsys class failed (%d)\n", err); + return err; + } + + /*restore sys/class/capsense label*/ + kobject_uevent(&capsense_class.p->subsys.kobj, KOBJ_CHANGE); + + /* Add Pointer to main platform data struct */ + pDevice->hw = pplatData; + + /* Check if we hava a platform initialization function to call*/ + if (pplatData->init_platform_hw) + pplatData->init_platform_hw(client); + + /* Initialize the button information initialized with keycodes */ + pDevice->pbuttonInformation = pplatData->pbuttonInformation; + + for (i = 0; i < pButtonInformationData->buttonSize; i++) + { + if (pplatData->button_used_flag>>i & 0x01) { + pButtonInformationData->buttons[i].used = true; + pButtonInformationData->buttons[i].state = IDLE; + pButtonInformationData->buttons[i].input_dev = input_allocate_device(); + if (!pButtonInformationData->buttons[i].input_dev) + return -ENOMEM; + pButtonInformationData->buttons[i].input_dev->name = pButtonInformationData->buttons[i].name; + /* Set all the keycodes */ + __set_bit(EV_ABS, pButtonInformationData->buttons[i].input_dev->evbit); + input_set_abs_params(pButtonInformationData->buttons[i].input_dev, ABS_DISTANCE, -1, 100, 0, 0); + + err = input_register_device(pButtonInformationData->buttons[i].input_dev); + /* report a unused val, then first val will report after enable */ + input_report_abs(pButtonInformationData->buttons[i].input_dev, ABS_DISTANCE, -1); + input_sync(pButtonInformationData->buttons[i].input_dev); + + pButtonInformationData->buttons[i].sensors_capsensor_cdev.sensors_enable = capsensor_set_enable; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.sensors_poll_delay = NULL; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.name = pButtonInformationData->buttons[i].name; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.vendor = "semtech"; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.version = 1; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.type = SENSOR_TYPE_MOTO_CAPSENSE; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.max_range = "5"; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.resolution = "5.0"; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.sensor_power = "3"; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.min_delay = 0; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.fifo_reserved_event_count = 0; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.fifo_max_event_count = 0; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.delay_msec = 100; + pButtonInformationData->buttons[i].sensors_capsensor_cdev.enabled = 0; + pButtonInformationData->buttons[i].enabled = false; + + err = sensors_classdev_register(&pButtonInformationData->buttons[i].input_dev->dev, &pButtonInformationData->buttons[i].sensors_capsensor_cdev); + if (err < 0) + LOG_ERR("create %d cap sensor_class file failed (%d)\n", i, err); + } + } + } + +#ifdef CONFIG_CAPSENSE_USB_CAL + /*notify usb state*/ + INIT_WORK(&pplatData->ps_notify_work, ps_notify_callback_work); + pplatData->ps_notif.notifier_call = ps_notify_callback; + err = power_supply_reg_notifier(&pplatData->ps_notif); + if (err) + LOG_ERR("Unable to register ps_notifier: %d\n", err); + + psy = power_supply_get_by_name("usb"); + if (psy) { + err = ps_get_state(psy, &pplatData->ps_is_present); + if (err) { + LOG_ERR("psy get property failed rc=%d\n", err); + power_supply_unreg_notifier(&pplatData->ps_notif); + } + } +#ifdef CONFIG_CAPSENSE_FLIP_CAL + if (of_property_read_bool(client->dev.of_node, "extcon")) { + pplatData->flip_notif.notifier_call = flip_notify_callback; + pplatData->ext_flip_det = + extcon_get_edev_by_phandle(&client->dev, 0); + if (IS_ERR(pplatData->ext_flip_det)) { + pplatData->ext_flip_det = NULL; + LOG_ERR("failed to get extcon flip dev\n"); + } else { + if(extcon_register_notifier(pplatData->ext_flip_det, + EXTCON_MECHANICAL, &pplatData->flip_notif)) + LOG_ERR("failed to register extcon flip dev notifier\n"); + else { + pplatData->phone_flip_state = + extcon_get_state(pplatData->ext_flip_det, + EXTCON_MECHANICAL); + update_flip_regs(pplatData, pplatData->phone_flip_state); + } + } + } else + LOG_ERR("extcon not in dev tree!\n"); +#endif +#endif + + sx93XX_IRQ_init(this); + /* call init function pointer (this should initialize all registers */ + if (this->init) { + this->init(this); + } + else { + LOG_ERR("No init function!!!!\n"); + return -ENOMEM; + } + } else { + return -1; + } + + pplatData->exit_platform_hw = sx937x_exit_platform_hw; + + global_sx937x = this; + + if(pplatData->reinit_on_i2c_failure) { + INIT_DELAYED_WORK(&this->i2c_watchdog_work, sx937x_i2c_watchdog_work); + schedule_delayed_work(&this->i2c_watchdog_work, + msecs_to_jiffies(SX937X_I2C_WATCHDOG_TIME)); + } + + LOG_INFO("sx937x_probe() Done\n"); + return 0; + +FREE_PMIC: + LOG_ERR("sx937x FREE_PMIC\n"); + if (pplatData->cap_vdd_en) { + regulator_disable(pplatData->cap_vdd); + regulator_put(pplatData->cap_vdd); + } + + if(pplatData->eldo_vdd_en){ + gpio_direction_output(pplatData->eldo_gpio,0); + } + return -ENXIO; +} + +/*! \fn static int sx937x_remove(struct i2c_client *client) + * \brief Called when device is to be removed + * \param client Pointer to i2c_client struct + * \return Value 0 + */ +static int sx937x_remove(struct i2c_client *client) +{ + psx937x_platform_data_t pplatData =0; + psx937x_t pDevice = 0; + struct _buttonInfo *pCurrentbutton; + int i = 0; + psx93XX_t this = i2c_get_clientdata(client); + LOG_DBG("sx937x_remove"); + if (this && (pDevice = this->pDevice)) + { + pplatData = client->dev.platform_data; + free_irq(this->irq, this); + cancel_delayed_work_sync(&(this->dworker)); + + class_unregister(&capsense_class); +#ifdef CONFIG_CAPSENSE_USB_CAL + cancel_work_sync(&pplatData->ps_notify_work); + power_supply_unreg_notifier(&pplatData->ps_notif); +#endif + if (pplatData && pplatData->exit_platform_hw) + pplatData->exit_platform_hw(client); + + if (pplatData->cap_vdd_en) { + regulator_disable(pplatData->cap_vdd); + regulator_put(pplatData->cap_vdd); + } + + if(pplatData->eldo_vdd_en){ + gpio_direction_output(pplatData->eldo_gpio,0); + } + for (i = 0; i pbuttonInformation->buttonSize; i++) { + pCurrentbutton = &(pDevice->pbuttonInformation->buttons[i]); + if (pCurrentbutton->used == true) { + sensors_classdev_unregister(&(pCurrentbutton->sensors_capsensor_cdev)); + input_unregister_device(pCurrentbutton->input_dev); + } + } + } + return 0; +} + + +#if 1//def CONFIG_PM +/*====================================================*/ +/***** Kernel Suspend *****/ +static int sx937x_suspend(struct device *dev) +{ + psx93XX_t this = dev_get_drvdata(dev); + psx937x_t pDevice = 0; + psx937x_platform_data_t pdata = 0; + + if (this) { + /* If we happen to reinitialize during suspend we might fail so wait for it to end */ + if ((pDevice = this->pDevice) && (pdata = pDevice->hw)) { + if (pdata->reinit_on_i2c_failure) + cancel_delayed_work_sync(&this->i2c_watchdog_work); + } + + sx937x_i2c_write_16bit(this,SX937X_COMMAND,0xD);//make sx937x in Sleep mode + LOG_DBG(LOG_TAG "sx937x suspend:disable irq!\n"); + disable_irq(this->irq); + this->suspended = 1; + } + return 0; +} +/***** Kernel Resume *****/ +static int sx937x_resume(struct device *dev) +{ + psx93XX_t this = dev_get_drvdata(dev); + psx937x_t pDevice = 0; + psx937x_platform_data_t pdata = 0; + + if (this) { + LOG_DBG(LOG_TAG "sx937x resume:enable irq!\n"); + sx93XX_schedule_work(this,0); + enable_irq(this->irq); + sx937x_i2c_write_16bit(this,SX937X_COMMAND,0xC);//Exit from Sleep mode + this->suspended = 0; + + /* Restart the watchdog in 2 seconds */ + if ((pDevice = this->pDevice) && (pdata = pDevice->hw)){ + if (pdata->reinit_on_i2c_failure) + schedule_delayed_work(&this->i2c_watchdog_work, + msecs_to_jiffies(SX937X_I2C_WATCHDOG_TIME_ERR)); + } + } + return 0; +} +/*====================================================*/ +#else +#define sx937x_suspend NULL +#define sx937x_resume NULL +#endif /* CONFIG_PM */ + +static struct i2c_device_id sx937x_idtable[] = +{ + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sx937x_idtable); +#ifdef CONFIG_OF +static struct of_device_id sx937x_match_table[] = +{ + { .compatible = "Semtech,sx937x",}, + { }, +}; +#else +#define sx937x_match_table NULL +#endif +static const struct dev_pm_ops sx937x_pm_ops = +{ + .suspend = sx937x_suspend, + .resume = sx937x_resume, +}; +static struct i2c_driver sx937x_driver = +{ + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = sx937x_match_table, + .pm = &sx937x_pm_ops, + }, + .id_table = sx937x_idtable, + .probe = sx937x_probe, + .remove = sx937x_remove, +}; +static int __init sx937x_I2C_init(void) +{ + return i2c_add_driver(&sx937x_driver); +} +static void __exit sx937x_I2C_exit(void) +{ + i2c_del_driver(&sx937x_driver); +} + +module_init(sx937x_I2C_init); +module_exit(sx937x_I2C_exit); + +MODULE_AUTHOR("Semtech Corp. (http://www.semtech.com/)"); +MODULE_DESCRIPTION("SX937x Capacitive Proximity Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1"); + +static void sx93XX_schedule_work(psx93XX_t this, unsigned long delay) +{ + unsigned long flags; + if (this) + { + LOG_DBG("sx93XX_schedule_work()\n"); + spin_lock_irqsave(&this->lock,flags); + /* Stop any pending penup queues */ + cancel_delayed_work(&this->dworker); + //after waiting for a delay, this put the job in the kernel-global workqueue. so no need to create new thread in work queue. + schedule_delayed_work(&this->dworker,delay); + spin_unlock_irqrestore(&this->lock,flags); + } + else + LOG_ERR("sx93XX_schedule_work, NULL psx93XX_t\n"); +} + +static irqreturn_t sx93XX_irq(int irq, void *pvoid) +{ + psx93XX_t this = 0; + if (pvoid) + { + this = (psx93XX_t)pvoid; + if ((!this->get_nirq_low) || this->get_nirq_low()) + { + LOG_DBG("sx93XX_irq - call sx93XX_schedule_work\n"); + sx93XX_schedule_work(this,0); + this->int_state = 1; + } + else + { + LOG_DBG("sx93XX_irq - nirq read high\n"); + } + } + else + { + LOG_ERR("sx93XX_irq, NULL pvoid\n"); + } + return IRQ_HANDLED; +} + +static void sx93XX_worker_func(struct work_struct *work) +{ + psx93XX_t this = 0; + int status = 0; + int counter = 0; + u8 nirqLow = 0; + if (work) + { + this = container_of(work,sx93XX_t,dworker.work); + + if (!this) + { + LOG_ERR("sx93XX_worker_func, NULL sx93XX_t\n"); + return; + } + if (unlikely(this->useIrqTimer)) + { + if ((!this->get_nirq_low) || this->get_nirq_low()) + { + nirqLow = 1; + } + } + /* since we are not in an interrupt don't need to disable irq. */ + status = this->refreshStatus(this); + counter = -1; + LOG_DBG("Worker_func - Refresh Status %d, use_timer_in_irq:%d\n", status, this->useIrqTimer); + + while((++counter) < MAX_NUM_STATUS_BITS) /* counter start from MSB */ + { + if (((status>>counter) & 0x01) && (this->statusFunc[counter])) + { + LOG_DBG("SX937x Function Pointer Found. Calling\n"); + this->statusFunc[counter](this); + } + } + if (unlikely(this->useIrqTimer && nirqLow)) + { + /* Early models and if RATE=0 for newer models require a penup timer */ + /* Queue up the function again for checking on penup */ + sx93XX_schedule_work(this,msecs_to_jiffies(this->irqTimeout)); + } + } + else + { + LOG_ERR("sx93XX_worker_func, NULL work_struct\n"); + } +} + +int sx93XX_IRQ_init(psx93XX_t this) +{ + int err = 0; + if (this && this->pDevice) + { + this->int_state = 0; + /* initialize spin lock */ + spin_lock_init(&this->lock); + /* initialize worker function */ + INIT_DELAYED_WORK(&this->dworker, sx93XX_worker_func); + /* initailize interrupt reporting */ + this->irq_disabled = 0; + err = request_irq(this->irq, sx93XX_irq, IRQF_TRIGGER_FALLING, + this->pdev->driver->name, this); + if (err) + { + LOG_ERR("irq %d busy?\n", this->irq); + return err; + } + LOG_INFO("registered with irq (%d)\n", this->irq); + } + return -ENOMEM; +} + +/* Read i2c every 10 seconds, if there is an error, schedule again in 2 seconds + * and if it fails a few more times we can assume there is a device error and reset + */ +static void sx937x_i2c_watchdog_work(struct work_struct *work) +{ + static int err_cnt = 0; + psx93XX_t this = container_of(work, sx93XX_t, i2c_watchdog_work.work); + int ret; + u32 temp; + int delay = SX937X_I2C_WATCHDOG_TIME; + + LOG_DBG("sx937x_i2c_watchdog_work"); + + if(!this->suspended) { + ret = sx937x_i2c_read_16bit(this, SX937X_DEVICE_INFO, &temp); + if (ret < 0) { + err_cnt++; + LOG_ERR("sx937x_i2c_watchdog_work err_cnt: %d", err_cnt); + delay = SX937X_I2C_WATCHDOG_TIME_ERR; + } else + err_cnt = 0; + + if (err_cnt >= 3) { + err_cnt = 0; + sx937x_reinitialize(this); + delay = SX937X_I2C_WATCHDOG_TIME; + } + } else + LOG_DBG("sx937x_i2c_watchdog_work before resume."); + + schedule_delayed_work(&this->i2c_watchdog_work, + msecs_to_jiffies(delay)); +} + +static void sx937x_reinitialize(psx93XX_t this) +{ + psx937x_t pDevice = 0; + psx937x_platform_data_t pdata = 0; + u32 temp; + int i=0; + int retry; + if (this && (pDevice = this->pDevice) && (pdata = pDevice->hw)) { + if (!pdata->reinit_on_i2c_failure) + return; + if (!atomic_add_unless(&this->init_busy, 1, 1)) + return; + disable_irq(this->irq); + /* perform a reset */ + for ( retry = 10; retry > 0; retry-- ) { + if (sx937x_i2c_write_16bit(this, SX937X_DEVICE_RESET, 0xDE) >= 0) + break; + LOG_INFO("SX937x write SX937X_DEVICE_RESET retry:%d\n", 11 - retry); + msleep(10); + } + /* wait until the reset has finished by monitoring NIRQ */ + LOG_INFO("Sent Software Reset. Waiting until device is back from reset to continue.\n"); + /* just sleep for awhile instead of using a loop with reading irq status */ + msleep(100); + sx937x_reg_init(this); +#ifdef CONFIG_CAPSENSE_FLIP_CAL + update_flip_regs(pdata, pdata->phone_flip_state); +#endif + /* re-enable interrupt handling */ + enable_irq(this->irq); + + /* make sure no interrupts are pending since enabling irq will only + * work on next falling edge */ + read_regStat(this); + + /* If one of the sensors is on, re-enable it */ + sx937x_i2c_read_16bit(this, SX937X_GENERAL_SETUP, &temp); + for (i=0; i < ARRAY_SIZE(psmtcButtons); i++) { + if (psmtcButtons[i].enabled) { + sx937x_i2c_write_16bit(this, SX937X_GENERAL_SETUP, temp | 0x0000007F); + break; + } + } + + manual_offset_calibration(this); + atomic_set(&this->init_busy, 0); + LOG_ERR("reinitialized sx937x, count %d\n", this->reset_count++); + } +} diff --git a/drivers/misc/sx937x_multi/Android.mk b/drivers/misc/sx937x_multi/Android.mk new file mode 100644 index 000000000000..7d10690b78c5 --- /dev/null +++ b/drivers/misc/sx937x_multi/Android.mk @@ -0,0 +1,11 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + + +include $(CLEAR_VARS) +LOCAL_MODULE := sx937x_multi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/sensors_class.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/sx937x_multi/Kbuild b/drivers/misc/sx937x_multi/Kbuild new file mode 100644 index 000000000000..ff6fdecb3b32 --- /dev/null +++ b/drivers/misc/sx937x_multi/Kbuild @@ -0,0 +1,16 @@ +KERNEL_DIR_PATH = $(shell find $(ANDROID_BUILD_TOP)/kernel/ -name "msm-*" -type d | head -1 | grep -i "kernel/msm-*") + +KERNEL_VERSION_NAME = $(shell basename $(KERNEL_DIR_PATH)) + +KERNEL_PLATFORM_PATH = $(shell find $(ANDROID_BUILD_TOP) -maxdepth 1 -name "kernel_platform" -type d) + +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers + + +obj-m += sx937x_multi.o + diff --git a/drivers/misc/sx937x_multi/Makefile b/drivers/misc/sx937x_multi/Makefile new file mode 100644 index 000000000000..201497405686 --- /dev/null +++ b/drivers/misc/sx937x_multi/Makefile @@ -0,0 +1,16 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS := $(M)/../../sensors/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/misc/sx937x_multi/sx937x_multi.c b/drivers/misc/sx937x_multi/sx937x_multi.c new file mode 100644 index 000000000000..dd02034382d5 --- /dev/null +++ b/drivers/misc/sx937x_multi/sx937x_multi.c @@ -0,0 +1,1757 @@ +/*! \file sx937x.c + * \brief SX937x Driver + * + * Driver for the SX937x + * Copyright (c) 2018 Semtech Corp + * + * 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. + */ +#define DRIVER_NAME "sx937x_multi" + +#define MAX_WRITE_ARRAY_SIZE 32 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* main struct, interrupt,init,pointers */ +//#include "base.h" + +#define LOG_TAG "[sar SX937x]: " + +#define LOG_INFO(fmt, args...) pr_info(LOG_TAG "[INFO]" "<%s><%d>"fmt, __func__, __LINE__, ##args) +#define LOG_DBG(fmt, args...) pr_debug(LOG_TAG "[DBG]" "<%s><%d>"fmt, __func__, __LINE__, ##args) +#define LOG_ERR(fmt, args...) pr_err(LOG_TAG "[ERR]" "<%s><%d>"fmt, __func__, __LINE__, ##args) + +#define SX937x_I2C_M_WR 0 /* for i2c Write */ +#define SX937x_I2C_M_RD 1 /* for i2c Read */ + +#define IDLE 0 +#define PROXACTIVE 1 +#define BODYACTIVE 2 + +/* Failer Index */ +#define SX937x_ID_ERROR 0x1 +#define SX937x_NIRQ_ERROR 0x2 +#define SX937x_CONN_ERROR 0x4 +#define SX937x_I2C_ERROR 0x8 + +#define SX937X_I2C_WATCHDOG_TIME 10000 +#define SX937X_I2C_WATCHDOG_TIME_ERR 2000 + +#define MAX_CHANNEL_NUMBER 8 +static struct class *capsense_class; + +static void sx937x_reinitialize(psx93XX_t this); +static void sx937x_i2c_watchdog_work(struct work_struct *work); + +//irq use +static int sx937x_get_nirq_low(psx93XX_t this) +{ + return !gpio_get_value(this->hw->irq_gpio); +} + +static void sx93XX_schedule_work(psx93XX_t this, unsigned long delay) +{ + unsigned long flags; + if (this) + { + LOG_DBG("sx93XX_schedule_work()\n"); + spin_lock_irqsave(&this->lock,flags); + /* Stop any pending penup queues */ + cancel_delayed_work(&this->dworker); + //after waiting for a delay, this put the job in the kernel-global workqueue. so no need to create new thread in work queue. + schedule_delayed_work(&this->dworker,delay); + spin_unlock_irqrestore(&this->lock,flags); + } + else + LOG_ERR("sx93XX_schedule_work, NULL psx93XX_t\n"); +} + +static irqreturn_t sx93XX_irq(int irq, void *pvoid) +{ + psx93XX_t this = 0; + if (pvoid) + { + this = (psx93XX_t)pvoid; + if ((!this->get_nirq_low) || this->get_nirq_low(this)) + { + LOG_DBG("sx93XX_irq - call sx93XX_schedule_work\n"); + sx93XX_schedule_work(this,0); + this->int_state = 1; + } + else + { + LOG_DBG("sx93XX_irq - nirq read high\n"); + } + } + else + { + LOG_ERR("sx93XX_irq, NULL pvoid\n"); + } + return IRQ_HANDLED; +} + +static void sx93XX_worker_func(struct work_struct *work) +{ + psx93XX_t this = 0; + int status = 0; + int counter = 0; + u8 nirqLow = 0; + if (work) + { + this = container_of(work,sx93XX_t,dworker.work); + + if (!this) + { + LOG_ERR("sx93XX_worker_func, NULL sx93XX_t\n"); + return; + } + if (unlikely(this->useIrqTimer)) + { + if ((!this->get_nirq_low) || this->get_nirq_low(this)) + { + nirqLow = 1; + } + } + /* since we are not in an interrupt don't need to disable irq. */ + status = this->refreshStatus(this); + counter = -1; + if(status != 0){ + LOG_INFO("Worker_func - Refresh Status %d, use_timer_in_irq:%d\n", status, this->useIrqTimer); + } + while((++counter) < MAX_NUM_STATUS_BITS) /* counter start from MSB */ + { + if (((status>>counter) & 0x01) && (this->statusFunc[counter])) + { + LOG_INFO("SX937x Function Pointer Found. Calling counter==%d\n",counter); + this->statusFunc[counter](this); + } + } + if (unlikely(this->useIrqTimer && nirqLow)) + { + /* Early models and if RATE=0 for newer models require a penup timer */ + /* Queue up the function again for checking on penup */ + sx93XX_schedule_work(this,msecs_to_jiffies(this->irqTimeout)); + } + } + else + { + LOG_ERR("sx93XX_worker_func, NULL work_struct\n"); + } +} + +int sx93XX_IRQ_init(psx93XX_t this) +{ + int err = 0; + if (this) + { + this->int_state = 0; + /* initialize spin lock */ + spin_lock_init(&this->lock); + /* initialize worker function */ + INIT_DELAYED_WORK(&this->dworker, sx93XX_worker_func); + /* initailize interrupt reporting */ + this->irq_disabled = 0; + err = request_irq(this->irq, sx93XX_irq, IRQF_TRIGGER_FALLING, + this->hw->dbg_name, this); + if (err) + { + LOG_ERR("irq %d busy?\n", this->irq); + return err; + } + LOG_INFO("registered with irq (%d)\n", this->irq); + } + return -ENOMEM; +} + +//I2C use +/*! \fn static int sx937x_i2c_write_16bit(psx93XX_t this, u8 address, u8 value) + * \brief Sends a write register to the device + * \param this Pointer to main parent struct + * \param address 8-bit register address + * \param value 8-bit register value to write to address + * \return Value from i2c_master_send + */ +static int sx937x_i2c_write_16bit(void *bus, u16 reg_addr, u32 buf) +{ + int ret = -ENOMEM; + struct i2c_client *i2c = 0; + struct i2c_msg msg; + unsigned char w_buf[6]; + + if (bus) + { + i2c = bus; + w_buf[0] = (u8)(reg_addr>>8); + w_buf[1] = (u8)(reg_addr); + w_buf[2] = (u8)(buf>>24); + w_buf[3] = (u8)(buf>>16); + w_buf[4] = (u8)(buf>>8); + w_buf[5] = (u8)(buf); + + msg.addr = i2c->addr; + msg.flags = SX937x_I2C_M_WR; + msg.len = 6; //2bytes regaddr + 4bytes data + msg.buf = (u8 *)w_buf; + + ret = i2c_transfer(i2c->adapter, &msg, 1); + if (ret < 0) + LOG_ERR(" i2c write reg 0x%x error %d\n", reg_addr, ret); + + } + return ret; +} + +/*! \fn static int sx937x_i2c_read_16bit(psx93XX_t this, u8 address, u8 *value) + * \brief Reads a register's value from the device + * \param this Pointer to main parent struct + * \param address 8-Bit address to read from + * \param value Pointer to 8-bit value to save register value to + * \return Value from i2c_smbus_read_byte_data if < 0. else 0 + */ +static int sx937x_i2c_read_16bit(void *bus, u16 reg_addr, u32 *data32) +{ + int ret = -ENOMEM; + struct i2c_client *i2c = 0; + struct i2c_msg msg[2]; + u8 w_buf[2]; + u8 buf[4]; + + if (bus) + { + i2c = bus; + + w_buf[0] = (u8)(reg_addr>>8); + w_buf[1] = (u8)(reg_addr); + + msg[0].addr = i2c->addr; + msg[0].flags = SX937x_I2C_M_WR; + msg[0].len = 2; + msg[0].buf = (u8 *)w_buf; + + msg[1].addr = i2c->addr;; + msg[1].flags = SX937x_I2C_M_RD; + msg[1].len = 4; + msg[1].buf = (u8 *)buf; + + ret = i2c_transfer(i2c->adapter, msg, 2); + if (ret < 0) + LOG_ERR("i2c read reg 0x%x error %d\n", reg_addr, ret); + + data32[0] = ((u32)buf[0]<<24) | ((u32)buf[1]<<16) | ((u32)buf[2]<<8) | ((u32)buf[3]); + + } + return ret; +} + + +/*! \fn static int read_regStat(psx93XX_t this) + * \brief Shortcut to read what caused interrupt. + * \details This is to keep the drivers a unified + * function that will read whatever register(s) + * provide information on why the interrupt was caused. + * \param this Pointer to main parent struct + * \return If successful, Value of bit(s) that cause interrupt, else 0 + */ +static int read_regStat(psx93XX_t this) +{ + u32 data = 0; + if (this) + { + if (sx937x_i2c_read_16bit(this->bus,SX937X_IRQ_SOURCE,&data) > 0) //bug + return (data & 0x00FF); + } + return 0; +} + +static int sx937x_Hardware_Check(psx93XX_t this) +{ + int ret; + u32 idCode; + u8 loop = 0; + this->failStatusCode = 0; + + //Check th IRQ Status + while(this->get_nirq_low && this->get_nirq_low(this)) + { + read_regStat(this); + msleep(100); + if(++loop >10) + { + this->failStatusCode = SX937x_NIRQ_ERROR; + break; + } + } + + //Check I2C Connection + ret = sx937x_i2c_read_16bit(this->bus, SX937X_DEVICE_INFO, &idCode); + if(ret < 0 || idCode == 0) + { + this->failStatusCode |= SX937x_I2C_ERROR; + } + + if(idCode!= SX937X_WHOAMI_VALUE) + { + this->failStatusCode |= SX937x_ID_ERROR; + } + + LOG_ERR("sx937x idcode = 0x%x, failcode = 0x%x\n", idCode, this->failStatusCode); + return this->failStatusCode; +} + +static int sx937x_global_variable_init(psx93XX_t this) +{ + this->irq_disabled = 0; + this->failStatusCode = 0; + this->reg_in_dts = true; + return 0; +} + +/*! \brief Perform a manual offset calibration + * \param this Pointer to main parent struct + * \return Value return value from the write register + */ +static int manual_offset_calibration(psx937x_platform_data_t data) +{ + int ret = 0; + ret = sx937x_i2c_write_16bit(data->bus, SX937X_COMMAND, 0xE); + return ret; + +} + +//class node use +static void read_dbg_raw(psx93XX_t this) +{ + int ph; + u32 uData, ph_sel; + s32 ant_use, ant_raw; + s32 avg, diff; + u16 off; + s32 adc_min, adc_max, use_flt_dlt_var; + s32 ref_a_use=0, ref_b_use=0, ref_c_use=0; + int ref_ph_a, ref_ph_b, ref_ph_c; + + ref_ph_a = this->hw->ref_phase_a; + ref_ph_b = this->hw->ref_phase_b; + ref_ph_c = this->hw->ref_phase_c; + + sx937x_i2c_read_16bit(this->bus, SX937X_DEVICE_STATUS_A, &uData); + LOG_INFO("SX937X_STAT0_REG= 0x%X\n", uData); + + if(ref_ph_a != -1) + { + sx937x_i2c_read_16bit(this->bus, SX937X_USEFUL_PH0 + ref_ph_a*4, &uData); + ref_a_use = (s32)uData >> 10; + } + if(ref_ph_b != -1) + { + sx937x_i2c_read_16bit(this->bus, SX937X_USEFUL_PH0 + ref_ph_b*4, &uData); + ref_b_use = (s32)uData >> 10; + } + if(ref_ph_c != -1) + { + sx937x_i2c_read_16bit(this->bus, SX937X_USEFUL_PH0 + ref_ph_c*4, &uData); + ref_c_use = (s32)uData >> 10; + } + + sx937x_i2c_read_16bit(this->bus, SX937X_DEBUG_SETUP, &ph_sel); + + sx937x_i2c_read_16bit(this->bus, SX937X_DEBUG_READBACK_0, &uData); + adc_min = (s32)uData>>10; + sx937x_i2c_read_16bit(this->bus, SX937X_DEBUG_READBACK_1, &uData); + adc_max = (s32)uData>>10; + sx937x_i2c_read_16bit(this->bus, SX937X_DEBUG_READBACK_2, &uData); + ant_raw = (s32)uData>>10; + sx937x_i2c_read_16bit(this->bus, SX937X_DEBUG_READBACK_3, &uData); + use_flt_dlt_var = (s32)uData>>4; + + ph = (ph_sel >> 3) & 0x7; + + sx937x_i2c_read_16bit(this->bus, SX937X_USEFUL_PH0 + ph*4, &uData); + ant_use = (s32)uData>>10; + + sx937x_i2c_read_16bit(this->bus, SX937X_AVERAGE_PH0 + ph*4, &uData); + avg = (s32)uData>>10; + sx937x_i2c_read_16bit(this->bus, SX937X_DIFF_PH0 + ph*4, &uData); + diff = (s32)uData>>10; + sx937x_i2c_read_16bit(this->bus, SX937X_OFFSET_PH0 + ph*4*3, &uData); + off = (u16)(uData & 0x3FFF); + //state = psmtcButtons[ph].state; + + LOG_INFO("SMTC_DBG PH= %d USE= %d RAW= %d PH%d_USE= %d PH%d_USE= %d PH%d_USE= %d AVG= %d DIFF= %d OFF= %d ADC_MIN= %d ADC_MAX= %d DLT= %d SMTC_END\n", + ph, ant_use, ant_raw, ref_ph_a, ref_a_use, ref_ph_b, ref_b_use, ref_ph_c, ref_c_use, avg,diff,off, adc_min,adc_max, use_flt_dlt_var); +} + +static void read_rawData(psx93XX_t this) +{ + u8 csx, index; + s32 useful, average, diff; + s32 ref_a_use=0, ref_b_use=0, ref_c_use=0; + u32 uData; + u32 use_hex, avg_hex, dif_hex, dlt_hex, dbg_ph; + u16 offset; + int ref_ph_a, ref_ph_b, ref_ph_c; + + if(this) + { + ref_ph_a = this->hw->ref_phase_a; + ref_ph_b = this->hw->ref_phase_b; + ref_ph_c = this->hw->ref_phase_c; + LOG_INFO("[SX937x] ref_ph_a= %d ref_ph_b= %d ref_ph_c= %d\n", ref_ph_a, ref_ph_b, ref_ph_c); + + sx937x_i2c_read_16bit(this->bus, SX937X_DEVICE_STATUS_A, &uData); + sx937x_i2c_read_16bit(this->bus, SX937X_DEBUG_SETUP, &dbg_ph); + dbg_ph = (dbg_ph >> 3) & 0x7; + sx937x_i2c_read_16bit(this->bus, SX937X_DEBUG_READBACK_3, &dlt_hex); + + if(ref_ph_a != -1) + { + sx937x_i2c_read_16bit(this->bus, SX937X_USEFUL_PH0 + ref_ph_a*4, &uData); + ref_a_use = (s32)uData >> 10; + } + if(ref_ph_b != -1) + { + sx937x_i2c_read_16bit(this->bus, SX937X_USEFUL_PH0 + ref_ph_b*4, &uData); + ref_b_use = (s32)uData >> 10; + } + if(ref_ph_c != -1) + { + sx937x_i2c_read_16bit(this->bus, SX937X_USEFUL_PH0 + ref_ph_c*4, &uData); + ref_c_use = (s32)uData >> 10; + } + + for(csx =0; csx<8; csx++) + { + index = csx*4; + sx937x_i2c_read_16bit(this->bus, SX937X_USEFUL_PH0 + index, &use_hex); + useful = (s32)use_hex>>10; + sx937x_i2c_read_16bit(this->bus, SX937X_AVERAGE_PH0 + index, &avg_hex); + average = (s32)avg_hex>>10; + sx937x_i2c_read_16bit(this->bus, SX937X_DIFF_PH0 + index, &dif_hex); + diff = (s32)dif_hex>>10; + sx937x_i2c_read_16bit(this->bus, SX937X_OFFSET_PH0 + index*3, &uData); + offset = (u16)(uData & 0x3FFF); + + //state = this->hw->[csx].state; + + LOG_INFO("SMTC_DAT PH= %d DIFF= %d USE= %d PH%d_USE= %d PH%d_USE= %d PH%d_USE= %d OFF= %d AVG= %d SMTC_END\n", + csx, diff, useful, ref_ph_a, ref_a_use, ref_ph_b, ref_b_use, ref_ph_c, ref_c_use, offset, average); + + LOG_INFO("SMTC_HEX PH= %d USE= 0x%X AVG= 0x%X DIF= 0x%X PH%d_DLT= 0x%X SMTC_END\n", + csx, use_hex, avg_hex, dif_hex, dbg_ph, dlt_hex); + } + + read_dbg_raw(this); + } +} + +static ssize_t capsense_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + psx93XX_t this = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%s\n", this->hw->dbg_name); +} + +static ssize_t capsense_reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) + +{ + u32 temp = 0; + int i = 0; + psx93XX_t this = dev_get_drvdata(dev); + sx937x_i2c_read_16bit(this->bus, SX937X_GENERAL_SETUP, &temp); + if (!count) + return -EINVAL; + + if (!strncmp(buf, "cal", 3) ) { + LOG_INFO("%s sx937x capsense_reset_store msg: cal\n", this->hw->dbg_name); + if (temp & 0x000000FF) { + if (this->hw->reinit_on_cali) { + LOG_DBG("Going to reinit chipset %s\n",buf); + sx937x_reinitialize(this); + } else { + LOG_DBG("Going to refresh baseline %s\n",buf); + manual_offset_calibration(this->hw); + } + } + } + + if (!strncmp(buf, "flip_near", 9)) { + LOG_INFO("%s sx937x capsense_reset_store msg: flip_near\n", this->hw->dbg_name); + sx937x_i2c_read_16bit(this->bus, SX937X_GENERAL_SETUP, &temp); + temp = temp & 0x000000FF; + for (i = 0; i < this->hw->flip_reg_num; i++) + { + if (this->hw->flip_near_reg[i].reg == SX937X_GENERAL_SETUP) + { + this->hw->flip_near_reg[i].val = this->hw->flip_near_reg[i].val & 0xFFFFFF00; + this->hw->flip_near_reg[i].val = this->hw->flip_near_reg[i].val | temp; + } + sx937x_i2c_write_16bit(this->bus, this->hw->flip_near_reg[i].reg,this->hw->flip_near_reg[i].val); + LOG_INFO("sx937 flip near download %s params set Reg 0x%x Value: 0x%x\n", + this->hw->dbg_name,this->hw->flip_near_reg[i].reg,this->hw->flip_near_reg[i].val); + } + } + + if (!strncmp(buf, "flip_far", 8)) { + LOG_INFO("%s sx937x capsense_reset_store msg: flip_far\n", this->hw->dbg_name); + sx937x_i2c_read_16bit(this->bus, SX937X_GENERAL_SETUP, &temp); + temp = temp & 0x000000FF; + if (this->hw->flip_far_reg_num > 0) + { + for (i = 0; i < this->hw->flip_far_reg_num; i++) + { + if (this->hw->flip_far_reg[i].reg == SX937X_GENERAL_SETUP) + { + this->hw->flip_far_reg[i].val = this->hw->flip_far_reg[i].val & 0xFFFFFF00; + this->hw->flip_far_reg[i].val = this->hw->flip_far_reg[i].val | temp; + } + sx937x_i2c_write_16bit(this->bus, this->hw->flip_far_reg[i].reg,this->hw->flip_far_reg[i].val); + LOG_INFO("sx937 flip near download %s params set Reg 0x%x Value: 0x%x\n", + this->hw->dbg_name,this->hw->flip_far_reg[i].reg,this->hw->flip_far_reg[i].val); + } + } + else + { + for (i = 0; i < this->hw->flip_reg_num; i++) + { + if (this->hw->flip_far_reg[i].reg == SX937X_GENERAL_SETUP) + { + this->hw->flip_far_reg[i].val = this->hw->flip_far_reg[i].val & 0xFFFFFF00; + this->hw->flip_far_reg[i].val = this->hw->flip_far_reg[i].val | temp; + } + sx937x_i2c_write_16bit(this->bus, this->hw->flip_far_reg[i].reg,this->hw->flip_far_reg[i].val); + LOG_INFO("sx937 flip near download %s params set Reg 0x%x Value: 0x%x\n", + this->hw->dbg_name,this->hw->flip_far_reg[i].reg,this->hw->flip_far_reg[i].val); + } + } + } + + return count; +} + +static ssize_t capsense_raw_data_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + char *p = buf; + int csx; + s32 useful, average, diff; + u32 uData; + u16 offset; + + psx93XX_t this = dev_get_drvdata(dev); + if(this) { + for(csx =0; csx<8; csx++) { + sx937x_i2c_read_16bit(this->bus, SX937X_USEFUL_PH0 + csx*4, &uData); + useful = (s32)uData>>10; + sx937x_i2c_read_16bit(this->bus, SX937X_AVERAGE_PH0 + csx*4, &uData); + average = (s32)uData>>10; + sx937x_i2c_read_16bit(this->bus, SX937X_DIFF_PH0 + csx*4, &uData); + diff = (s32)uData>>10; + sx937x_i2c_read_16bit(this->bus, SX937X_OFFSET_PH0 + csx*12, &uData); + offset = (u16)(uData & 0x3FFF); + p += snprintf(p, PAGE_SIZE, "PH= %d Useful = %d Average = %d DIFF = %d Offset = %d \n", + csx,useful,average,diff,offset); + } + } + return (p-buf); +} + +static ssize_t sx937x_register_write_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u32 reg_address = 0, val = 0; + psx93XX_t this = dev_get_drvdata(dev); + + if (sscanf(buf, "%x,%x", ®_address, &val) != 2) + { + LOG_ERR("The number of data are wrong\n"); + return -EINVAL; + } + + LOG_INFO("%s sx937x_fac_cal_store\n", this->hw->dbg_name); + sx937x_i2c_write_16bit(this->bus, reg_address, val); + + LOG_INFO("%s Register(0x%x) data(0x%x)\n", this->hw->dbg_name, reg_address, val); + return count; +} + + + +static ssize_t sx937x_register_read_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int sx937x_temp_regist = 0; + u32 sx937x_temp_val = 0; + psx93XX_t this = dev_get_drvdata(dev); + + if (sscanf(buf, "%x", &sx937x_temp_regist) != 1) + { + LOG_ERR(" The number of data are wrong\n"); + return -EINVAL; + } + + LOG_INFO("%s sx937x_fac_cal_store\n", this->hw->dbg_name); + sx937x_i2c_read_16bit(this->bus, sx937x_temp_regist, &sx937x_temp_val); + + LOG_INFO("%s Register(0x%2x) data(0x%4x)\n", this->hw->dbg_name, sx937x_temp_regist, sx937x_temp_val); + return count; +} + +static ssize_t sx937x_irq_state_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + psx93XX_t this = dev_get_drvdata(dev); + LOG_INFO("%s Reading INT line state %d\n", this->hw->dbg_name, this->int_state); + return sprintf(buf, "%d\n", this->int_state); +} + +static ssize_t sx937x_fac_detect_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + psx93XX_t this = dev_get_drvdata(dev); + u32 reg_value = 0; + char temp_id[7] ="0"; + char sx93x_id[4] ="0"; + sx937x_i2c_read_16bit(this->bus,SX937X_DEVICE_INFO,®_value); + LOG_INFO("Reading device id reg_value==%x\n",reg_value); + sprintf(temp_id, "%x\n",reg_value); + strncpy(sx93x_id,temp_id,sizeof(sx93x_id)-1); + if(strcmp(sx93x_id,"937")==0){ + + LOG_INFO("Detect ic sx937x\n"); + return sprintf(buf, "%d\n",1); + + }else{ + + LOG_INFO("Not found ic sx937x\n"); + return sprintf(buf, "%d\n",0); + } +} + +static ssize_t sx937x_fac_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) + +{ + psx93XX_t this = dev_get_drvdata(dev); + u32 temp = 0; + int ret = 0; + if ( !strncmp(buf, "1", 1)) { + LOG_INFO("enable cap sensor\n"); + sx937x_i2c_read_16bit(this->bus, SX937X_GENERAL_SETUP, &temp); + temp = temp | 0x0000007F; + LOG_INFO("set reg 0x%x val 0x%x\n", SX937X_GENERAL_SETUP, temp); + sx937x_i2c_write_16bit(this->bus, SX937X_GENERAL_SETUP, temp); + if(ret <0){ + LOG_ERR("enable write enable sx937x error ret =%d\n",ret); + return -EINVAL; + } + } + if ( !strncmp(buf, "0", 1)) { + LOG_INFO("disnable cap sensor\n"); + sx937x_i2c_read_16bit(this->bus, SX937X_GENERAL_SETUP, &temp); + temp = temp | 0xFFFFFF00; + LOG_INFO("set reg 0x%x val 0x%x\n", SX937X_GENERAL_SETUP, temp); + sx937x_i2c_write_16bit(this->bus, SX937X_GENERAL_SETUP, temp); + if(ret <0){ + LOG_ERR("enable write enable sx937x error ret =%d\n",ret); + return -EINVAL; + } + } + return count; +} + +static ssize_t sx937x_fac_cal_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) + +{ + psx93XX_t this = dev_get_drvdata(dev); + LOG_INFO("%s sx937x_fac_cal_store\n", this->hw->dbg_name); + if ( !strncmp(buf, "1", 1)) { + manual_offset_calibration(this->hw); + LOG_INFO("set reg 0x%x val 0xe\n", SX937X_COMMAND); + } + + return count; +} +static ssize_t sx937x_fac_comp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + psx93XX_t this = dev_get_drvdata(dev); + int i ,j= 0; + int read_ret; + u8 reg_data[MAX_CHANNEL_NUMBER*4+2] = {0}; + u16 reg_addr; + u32 temp_val; + reg_addr = SX937X_OFFSET_PH0; + + LOG_INFO("%s sx937x_fac_comp_show\n", this->hw->dbg_name); + + for (i = 0; i < MAX_CHANNEL_NUMBER; i++) { + read_ret = sx937x_i2c_read_16bit(this->bus,reg_addr+0xc*i,&temp_val); + if(read_ret<0){ + LOG_INFO("failed to read reg data 0x%x", reg_addr); + } + LOG_INFO("fac_cam i==%d reg_addr ==0x%x temp_val===%x\n",i,reg_addr+0xc*i,temp_val); + reg_data[2 * i] = (u8)((temp_val & 0x3FFF) >> 8); + reg_data[1 + 2 * i] = (u8)(temp_val & 0x3FFF); + LOG_INFO("tc_cmn_drv_cap_sense_read_cal_data reg[%d]==%x,reg[%d]==%x\n", + (2*i), reg_data[2 * i], + (1+2*i), reg_data[1 + 2 * i]); + } + for(j=0;jhw->dbg_name); + for ( i = 0; i < MAX_CHANNEL_NUMBER; i++) { + read_ret=sx937x_i2c_read_16bit(this->bus,reg_addr+0x4*i,&temp_val); + if(read_ret<0){ + LOG_INFO("failed to read reg data 0x%x", reg_addr); + } + LOG_INFO("sx937x i==%d,reg_addr:0x%x",i, reg_addr + 0x4 * i); + diff_val = ((int)temp_val)>>10; + data[4 * i] = (u8)(diff_val >> 24); + data[1 + 4 * i] = (u8)(diff_val >> 16); + data[2 + 4 * i] = (u8)(diff_val >> 8); + data[3 + 4 * i] = (u8)(diff_val); + LOG_INFO("sx937x diff_val==%x,data[%d]==%x,data[%d]==%x,data[%d]==%x,data[%d]==%x", + diff_val, + (4*i), data[4 * i], + (1+4*i), data[1+4 * i], + (2+4*i), data[2+4 * i], + (3+4*i), data[3+4 * i]); + } + for(i=0;ihw)) + { + /*******************************************************************************/ + // try to initialize from device tree! + /*******************************************************************************/ + while ( i < ARRAY_SIZE(sx937x_i2c_reg_setup)) + { + /* Write all registers/values contained in i2c_reg */ + LOG_DBG("Going to Write Reg: 0x%x Value: 0x%x\n", + sx937x_i2c_reg_setup[i].reg,sx937x_i2c_reg_setup[i].val); + tmpvalue = sx937x_i2c_reg_setup[i].val; + if (sx937x_i2c_reg_setup[i].reg == SX937X_GENERAL_SETUP) + { + if((sx937x_i2c_reg_setup[i].val & 0xFF) == 0) + { + tmpvalue = (sx937x_i2c_reg_setup[i].val|0xFF); + } + } + sx937x_i2c_write_16bit(this->bus, sx937x_i2c_reg_setup[i].reg, tmpvalue); + i++; + } + + if (this->reg_in_dts == true) + { + i = 0; + while ( i < pdata->i2c_reg_num) + { + /* Write all registers/values contained in i2c_reg */ + LOG_DBG("Going to Write Reg from dts: 0x%x Value: 0x%x\n", + pdata->pi2c_reg[i].reg,pdata->pi2c_reg[i].val); + sx937x_i2c_write_16bit(this->bus, pdata->pi2c_reg[i].reg,pdata->pi2c_reg[i].val); + i++; + } + } + + /*******************************************************************************/ + sx937x_i2c_write_16bit(this->bus, SX937X_COMMAND, 0xF); //enable phase control + } + else { + LOG_ERR("ERROR! platform data 0x%p\n",this->hw); + } +} + + +/*! \fn static int initialize(psx93XX_t this) + * \brief Performs all initialization needed to configure the device + * \param this Pointer to main parent struct + * \return Last used command's return value (negative if error) + */ +static int initialize(psx93XX_t this) +{ + int retry; + bool reset_ok = false; + if (this) + { + LOG_INFO("SX937x initialize\n"); + /* prepare reset by disabling any irq handling */ + this->irq_disabled = 1; + disable_irq(this->irq); + /* perform a reset */ + for ( retry = 10; retry > 0; retry-- ) { + if (sx937x_i2c_write_16bit(this->bus, SX937X_DEVICE_RESET, 0xDE) >= 0){ + reset_ok = true; + LOG_INFO("write ok"); + break; + } + LOG_INFO("SX937x write SX937X_RESET_REG retry:%d\n", 11 - retry); + msleep(10); + } + if (reset_ok == false) + return -ENOMEM; + /* wait until the reset has finished by monitoring NIRQ */ + LOG_INFO("Sent Software Reset. Waiting until device is back from reset to continue.\n"); + /* just sleep for awhile instead of using a loop with reading irq status */ + msleep(100); + + if(sx937x_global_variable_init(this)==0) + { + LOG_INFO("sx937x_global_variable_init success.\n"); + } + sx937x_reg_init(this); + + /* re-enable interrupt handling */ + enable_irq(this->irq); + + /* make sure no interrupts are pending since enabling irq will only + * work on next falling edge */ + read_regStat(this); + return 0; + } + return -ENOMEM; +} + +/*! + * \brief Handle what to do when a touch occurs + * \param this Pointer to main parent struct + */ +static void touchProcess(psx93XX_t this) +{ + int counter = 0; + u32 i = 0; + u32 touchFlag = 0; + int numberOfButtons = 0; + struct _buttonInfo *buttons = NULL; + struct input_dev *input = NULL; + + struct _buttonInfo *pCurrentButton = NULL; + + if (this) + { + sx937x_i2c_read_16bit(this->bus, SX937X_DEVICE_STATUS_A, &i); + LOG_INFO("touchProcess STAT0_REG:0x%x val 0x%08x\n", SX937X_DEVICE_STATUS_A, i); + + buttons = this->hw->buttons; + numberOfButtons = this->hw->buttonSize; + + if (unlikely( buttons==NULL )) + { + LOG_ERR(":ERROR!! buttons NULL!!!\n"); + return; + } + + for (counter = 0; counter < numberOfButtons; counter++) + { + if (buttons[counter].enabled == false) { + LOG_DBG("touchProcess %s disabled, ignor this\n", buttons[counter].name); + continue; + } + if (buttons[counter].used== false) { + LOG_DBG("touchProcess %s unused, ignor this\n", buttons[counter].name); + continue; + } + pCurrentButton = &buttons[counter]; + if (pCurrentButton==NULL) + { + LOG_ERR("ERROR!! current button at index: %d NULL!!!\n", counter); + return; // ERRORR!!!! + } + input = pCurrentButton->input_dev; + if (input==NULL) + { + LOG_ERR("ERROR!! current button input at index: %d NULL!!!\n", counter); + return; // ERRORR!!!! + } + + touchFlag = i & (pCurrentButton->ProxMask | pCurrentButton->BodyMask); + if (touchFlag == (pCurrentButton->ProxMask | pCurrentButton->BodyMask)) { + if (pCurrentButton->state == BODYACTIVE) + LOG_DBG(" %s already BODYACTIVE\n", pCurrentButton->name); + else { + input_report_abs(input, ABS_DISTANCE, 2); + input_sync(input); + pCurrentButton->state = BODYACTIVE; + LOG_DBG(" %s report 5mm BODYACTIVE\n", pCurrentButton->name); + } + } else if (touchFlag == pCurrentButton->ProxMask) { + if (pCurrentButton->state == PROXACTIVE) + LOG_DBG(" %s already PROXACTIVE\n", pCurrentButton->name); + else { + input_report_abs(input, ABS_DISTANCE, 1); + input_sync(input); + pCurrentButton->state = PROXACTIVE; + LOG_DBG(" %s report 15mm PROXACTIVE\n", pCurrentButton->name); + } + }else if (touchFlag == 0) { + if (pCurrentButton->state == IDLE) + LOG_DBG("%s already released.\n", pCurrentButton->name); + else { + input_report_abs(input, ABS_DISTANCE, 0); + input_sync(input); + pCurrentButton->state = IDLE; + LOG_DBG("%s report released.\n", pCurrentButton->name); + } + } + } + } +} + +static int sx937x_parse_dts(struct sx937x_platform_data *pdata, struct device *dev) +{ + struct device_node *dNode = dev->of_node; + enum of_gpio_flags flags; + int i,j, rc; + const char *reg_group_name = "Semtech,reg-init"; + int name_index,name_count; + + if (dNode == NULL) + return -ENODEV; + + rc = of_property_read_u32(dNode,"Semtech,power-supply-type",&pdata->power_supply_type); + if(rc < 0){ + pdata->power_supply_type = SX937X_POWER_SUPPLY_TYPE_PMIC_LDO; + LOG_INFO("pmic ldo is the default if not set power-supply-type in dt\n"); + } + + switch(pdata->power_supply_type){ + case SX937X_POWER_SUPPLY_TYPE_PMIC_LDO: + /* using regulator_get() to fetch power_supply in sx937x_probe()*/ + break; + case SX937X_POWER_SUPPLY_TYPE_ALWAYS_ON: + /* power supply always on: no need fetch others control in drivers */ + break; + case SX937X_POWER_SUPPLY_TYPE_EXTERNAL_LDO: + /* parse the gpio number for external LDO enable pin*/ + pdata->eldo_gpio = of_get_named_gpio_flags(dNode, + "Semtech,eldo-gpio",0,&flags); + LOG_INFO("used eLDO_gpio 0x%x \n", pdata->eldo_gpio); + break; + default: + LOG_INFO("Error power_supply_type: 0x%x \n", pdata->power_supply_type); + break; + } + + pdata->irq_gpio= of_get_named_gpio_flags(dNode,"Semtech,nirq-gpio", 0, &flags); + if (pdata->irq_gpio < 0){ + LOG_ERR("get irq_gpio error\n"); + return -ENODEV; + } + + of_property_read_string(dNode, "Semtech,debug-name", &pdata->dbg_name); + LOG_INFO("used dbg_name %s\n", pdata->dbg_name); + + pdata->button_used_flag = 0; + of_property_read_u32(dNode,"Semtech,button-flag",&pdata->button_used_flag); + LOG_INFO("used button 0x%x \n", pdata->button_used_flag); + + name_count = of_property_count_strings(dNode, "Semtech,button-names"); + LOG_DBG("name_count %d pdata->buttonSize==%d\n", name_count, pdata->buttonSize); + for (i = 0, name_index = 0; i < pdata->buttonSize & name_index < name_count; i++) + { + if (pdata->button_used_flag>>i & 0x01) { + pdata->buttons[i].used = true; + of_property_read_string_index(dNode,"Semtech,button-names", + name_index, &pdata->buttons[i].name); + name_index++; + } + } + pdata->ref_phase_a = -1; + pdata->ref_phase_b = -1; + pdata->ref_phase_c = -1; + if ( of_property_read_u32(dNode,"Semtech,ref-phases-a",&pdata->ref_phase_a) ) + { + LOG_ERR("[SX937x]: %s - get ref-phases error\n", __func__); + return -ENODEV; + } + if ( of_property_read_u32(dNode,"Semtech,ref-phases-b",&pdata->ref_phase_b) ) + { + LOG_ERR("[SX937x]: %s - get ref-phases-b error\n", __func__); + return -ENODEV; + } + if ( of_property_read_u32(dNode,"Semtech,ref-phases-c",&pdata->ref_phase_c) ) + { + LOG_ERR("[SX937x]: %s - get ref-phases-c error\n", __func__); + return -ENODEV; + } + if (pdata->ref_phase_a == 0xff) pdata->ref_phase_a = -1; + if (pdata->ref_phase_b == 0xff) pdata->ref_phase_b = -1; + if (pdata->ref_phase_c == 0xff) pdata->ref_phase_c = -1; + + LOG_INFO("ref_phase_a= %d ref_phase_b= %d ref_phase_c= %d\n", + pdata->ref_phase_a, pdata->ref_phase_b, pdata->ref_phase_c); + + // load in registers from device tree + of_property_read_u32(dNode,"Semtech,reg-num",&pdata->i2c_reg_num); + // layout is register, value, register, value.... + // if an extra item is after just ignore it. reading the array in will cause it to fail anyway + LOG_INFO("size of default elements %d \n", pdata->i2c_reg_num); + if (pdata->i2c_reg_num > 0) + { + // initialize platform reg data array + pdata->pi2c_reg = devm_kzalloc(dev,sizeof(struct smtc_reg_data)*pdata->i2c_reg_num, GFP_KERNEL); + if (unlikely(pdata->pi2c_reg == NULL)) + { + LOG_ERR("size of elements %d alloc error\n", pdata->i2c_reg_num); + return -ENOMEM; + } + + // initialize the array + if (of_property_read_u32_array(dNode,reg_group_name,(u32*)&(pdata->pi2c_reg[0]),sizeof(struct smtc_reg_data)*pdata->i2c_reg_num/sizeof(u32))) + return -ENOMEM; + } + + //load param when flip near + of_property_read_u32(dNode,"Semtech,flip_operation_num",&pdata->flip_reg_num); + LOG_INFO("size of flip state elements %d \n", pdata->flip_reg_num); + if(pdata->flip_reg_num >0) + { + pdata->flip_near_reg = devm_kzalloc(dev,sizeof(struct smtc_reg_data)*pdata->flip_reg_num, GFP_KERNEL); + if (unlikely(pdata->flip_near_reg == NULL)) + { + LOG_ERR("size of elements %d alloc error\n", pdata->flip_reg_num); + return -ENOMEM; + } + // initialize the array + if (of_property_read_u32_array(dNode,"Semtech,flip_near_init",(u32*)&(pdata->flip_near_reg[0]),sizeof(struct smtc_reg_data)*pdata->flip_reg_num/sizeof(u32))) + return -ENOMEM; + } + + //load param when flip far + of_property_read_u32(dNode,"Semtech,flip_operation_far_num",&pdata->flip_far_reg_num); + LOG_INFO("size of flip far state elements %d \n", pdata->flip_far_reg_num); + if(pdata->flip_far_reg_num >0) + { + pdata->flip_far_reg = devm_kzalloc(dev,sizeof(struct smtc_reg_data)*pdata->flip_far_reg_num, GFP_KERNEL); + if (unlikely(pdata->flip_far_reg == NULL)) + { + LOG_ERR("size of elements %d alloc error\n", pdata->flip_reg_num); + return -ENOMEM; + } + if (of_property_read_u32_array(dNode,"Semtech,flip_far_init",(u32*)&(pdata->flip_far_reg[0]),sizeof(struct smtc_reg_data)*pdata->flip_far_reg_num/sizeof(u32))) + return -ENOMEM; + } + else if(pdata->flip_reg_num >0) // use same lenght with flip_near_reg + { + pdata->flip_far_reg = devm_kzalloc(dev,sizeof(struct smtc_reg_data)*pdata->flip_reg_num, GFP_KERNEL); + if (unlikely(pdata->flip_far_reg == NULL)) + { + LOG_ERR("size of elements %d alloc error\n", pdata->flip_reg_num); + return -ENOMEM; + } + for(i = 0;iflip_reg_num;i++) + { + for(j =0;ji2c_reg_num;j++) + { + if(pdata->flip_near_reg[i].reg == pdata->pi2c_reg[j].reg) + { + pdata->flip_far_reg[i].reg =pdata->pi2c_reg[j].reg; + pdata->flip_far_reg[i].val =pdata->pi2c_reg[j].val; + } + } + + LOG_INFO("flip_far_reg params set Reg 0x%x Value: 0x%x\n",pdata->flip_far_reg[i].reg,pdata->flip_far_reg[i].val); + } + } + + pdata->reinit_on_cali = of_property_read_bool(dNode, "reinit-on-cali"); + pdata->reinit_on_i2c_failure = of_property_read_bool(dNode, "reinit-on-i2c-failure"); + LOG_INFO("reinit_on_cali %d,reinit_on_i2c_failure %d \n", pdata->reinit_on_cali,pdata->reinit_on_i2c_failure); + + LOG_DBG("-[%d] parse_dt complete\n", pdata->irq_gpio); + return 0; +} + +/* get the NIRQ state (1->NIRQ-low, 0->NIRQ-high) */ +static int sx937x_init_platform_hw(struct i2c_client *client) +{ + psx93XX_t this = i2c_get_clientdata(client); + struct sx937x_platform_data *pdata = NULL; + + int rc = 0; + + LOG_INFO("init_platform_hw start!"); + + if (this && (pdata = this->hw)) + { + if (gpio_is_valid(pdata->irq_gpio)) + { + rc = gpio_request(pdata->irq_gpio, "sx937x_irq_gpio"); + if (rc < 0) + { + LOG_ERR("SX937x Request gpio. Fail![%d]\n", rc); + return rc; + } + rc = gpio_direction_input(pdata->irq_gpio); + if (rc < 0) + { + LOG_ERR("SX937x Set gpio direction. Fail![%d]\n", rc); + return rc; + } + this->irq = client->irq = gpio_to_irq(pdata->irq_gpio); + } + else + { + LOG_ERR("SX937x Invalid irq gpio num.(init)\n"); + } + } + else + { + LOG_ERR("Do not init platform HW"); + } + return rc; +} + +static void sx937x_exit_platform_hw(struct i2c_client *client) +{ + psx93XX_t this = i2c_get_clientdata(client); + struct sx937x_platform_data *pdata = NULL; + + if (this && (pdata = this->hw)) + { + if (gpio_is_valid(pdata->irq_gpio)) + { + gpio_free(pdata->irq_gpio); + } + else + { + LOG_ERR("Invalid irq gpio num.(exit)\n"); + } + } + return; +} +static int capsensor_set_enable(struct sensors_classdev *sensors_cdev, + unsigned int enable) +{ + psx93XX_t this = (psx93XX_t)(sensors_cdev->dev->platform_data); + bool disableFlag = true; + int i = 0; + u32 temp = 0x0; + struct _buttonInfo *buttons = this->hw->buttons; + int buttonSize = this->hw->buttonSize; + + for (i = 0; i < buttonSize; i++) { + if (strcmp(sensors_cdev->name, buttons[i].name) == 0) { + if (enable == 1) { + LOG_INFO("enable cap sensor : %s\n", sensors_cdev->name); + sx937x_i2c_read_16bit(this->bus, SX937X_GENERAL_SETUP, &temp); + temp = temp | 0x0000007F; + LOG_DBG("set reg 0x%x val 0x%x\n", SX937X_GENERAL_SETUP, temp); + sx937x_i2c_write_16bit(this->bus, SX937X_GENERAL_SETUP, temp); + buttons[i].enabled = true; + input_report_abs(buttons[i].input_dev, ABS_DISTANCE, 0); + input_sync(buttons[i].input_dev); + + manual_offset_calibration(this->hw); + } else if (enable == 0) { + LOG_INFO("disable cap sensor : %s\n", sensors_cdev->name); + buttons[i].enabled = false; + input_report_abs(buttons[i].input_dev, ABS_DISTANCE, -1); + input_sync(buttons[i].input_dev); + } else { + LOG_ERR("unknown enable symbol\n"); + } + } + } + + //if all chs disabled, then disable all + for (i = 0; i < buttonSize; i++) { + if (buttons[i].enabled) { + disableFlag = false; + break; + } + } + if (disableFlag) { + LOG_DBG("disable all chs\n"); + sx937x_i2c_read_16bit(this->bus, SX937X_GENERAL_SETUP, &temp); + LOG_DBG("read reg 0x%x val 0x%x\n", SX937X_GENERAL_SETUP, temp); + temp = temp & 0xFFFFFF00; + LOG_DBG("set reg 0x%x val 0x%x\n", SX937X_GENERAL_SETUP, temp); + sx937x_i2c_write_16bit(this->bus, SX937X_GENERAL_SETUP, temp); + } + return 0; +} + +/*! \fn static int sx937x_probe(struct i2c_client *client, const struct i2c_device_id *id) + * \brief Probe function + * \param client pointer to i2c_client + * \param id pointer to i2c_device_id + * \return Whether probe was successful + */ +static int sx937x_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int i = 0; + int err = 0; + + psx93XX_t this = NULL; + psx937x_platform_data_t pplatData = NULL; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + + LOG_INFO("sx937x_probe enter\n"); + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) + { + LOG_ERR("Check i2c functionality.Fail!\n"); + return -EIO; + } + + this = devm_kzalloc(&client->dev,sizeof(sx93XX_t), GFP_KERNEL); /* create memory for main struct */ + if (!this) + { + LOG_ERR("Failed to allocate memory(sx93XX_t)\n"); + return -ENOMEM; + } + + pplatData = devm_kzalloc(&client->dev,sizeof(sx937x_platform_data_t), GFP_KERNEL); + if (!pplatData) + { + LOG_ERR("platform data is required!\n"); + return -EINVAL; + } + + pplatData->buttonSize = ARRAY_SIZE(psmtcButtons); + memcpy(pplatData->buttons, psmtcButtons, sizeof(psmtcButtons)); + + client->dev.platform_data = pplatData; + err = sx937x_parse_dts(pplatData, &client->dev); + if (err) + { + LOG_ERR("could not setup pin\n"); + return ENODEV; + } + + pplatData->init_platform_hw = sx937x_init_platform_hw; + + if (this) + { + LOG_INFO("SX937x initialize start!!"); + /* In case we need to reinitialize data + * (e.q. if suspend reset device) */ + this->hw = pplatData; + + this->init = initialize; + /* shortcut to read status of interrupt */ + this->refreshStatus = read_regStat; + /* pointer to function from platform data to get pendown + * (1->NIRQ=0, 0->NIRQ=1) */ + this->get_nirq_low = sx937x_get_nirq_low; + /* save irq in case we need to reference it */ + //this->irq = client->irq; + /* do we need to create an irq timer after interrupt ? */ + this->useIrqTimer = 0; + + /* Setup function to call on corresponding reg irq source bit */ + if (MAX_NUM_STATUS_BITS>= 8) + { + this->statusFunc[0] = 0; /* TXEN_STAT */ + this->statusFunc[1] = 0; /* UNUSED */ + this->statusFunc[2] = touchProcess; /* body&table */ + this->statusFunc[3] = read_rawData; /* CONV_STAT */ + this->statusFunc[4] = touchProcess; /* COMP_STAT */ + this->statusFunc[5] = touchProcess; /* RELEASE_STAT */ + this->statusFunc[6] = touchProcess; /* TOUCH_STAT */ + this->statusFunc[7] = 0; /* RESET_STAT */ + } + + /* setup i2c communication */ + this->bus = client; + pplatData->bus = client; + i2c_set_clientdata(client, this); + + /* record device struct */ + this->pdev = &client->dev; + + this->dbg_dev = device_create(capsense_class, NULL, MKDEV(0,0), this, "%s", pplatData->dbg_name); + if (IS_ERR(this->dbg_dev)) + { + LOG_ERR("capsense_class dbg dev create fail\n"); + return PTR_ERR(this->dbg_dev);; + } + + for (i = 0; i < ARRAY_SIZE(capsense_class_attrs); ++i) { + err = device_create_file(this->dbg_dev, capsense_class_attrs[i]); + if (err) + LOG_ERR("i = %d,device_create_file register fail\n",i); + } + + /* Check if we hava a platform initialization function to call*/ + if (pplatData->init_platform_hw) + pplatData->init_platform_hw(client); + + for (i = 0; i < pplatData->buttonSize; i++) + { + if (pplatData->button_used_flag>>i & 0x01) { + pplatData->buttons[i].used = true; + pplatData->buttons[i].state = IDLE; + pplatData->buttons[i].input_dev = input_allocate_device(); + if (!pplatData->buttons[i].input_dev) + return -ENOMEM; + pplatData->buttons[i].input_dev->name = pplatData->buttons[i].name; + /* Set all the keycodes */ + __set_bit(EV_ABS, pplatData->buttons[i].input_dev->evbit); + input_set_abs_params(pplatData->buttons[i].input_dev, ABS_DISTANCE, -1, 100, 0, 0); + + err = input_register_device(pplatData->buttons[i].input_dev); + /* report a unused val, then first val will report after enable */ + input_report_abs(pplatData->buttons[i].input_dev, ABS_DISTANCE, -1); + input_sync(pplatData->buttons[i].input_dev); + + pplatData->buttons[i].sensors_capsensor_cdev.sensors_enable = capsensor_set_enable; + pplatData->buttons[i].sensors_capsensor_cdev.sensors_poll_delay = NULL; + pplatData->buttons[i].sensors_capsensor_cdev.name = pplatData->buttons[i].name; + pplatData->buttons[i].sensors_capsensor_cdev.vendor = "semtech"; + pplatData->buttons[i].sensors_capsensor_cdev.version = 1; + pplatData->buttons[i].sensors_capsensor_cdev.type = SENSOR_TYPE_MOTO_CAPSENSE; + pplatData->buttons[i].sensors_capsensor_cdev.max_range = "5"; + pplatData->buttons[i].sensors_capsensor_cdev.resolution = "5.0"; + pplatData->buttons[i].sensors_capsensor_cdev.sensor_power = "3"; + pplatData->buttons[i].sensors_capsensor_cdev.min_delay = 0; + pplatData->buttons[i].sensors_capsensor_cdev.fifo_reserved_event_count = 0; + pplatData->buttons[i].sensors_capsensor_cdev.fifo_max_event_count = 0; + pplatData->buttons[i].sensors_capsensor_cdev.delay_msec = 100; + pplatData->buttons[i].sensors_capsensor_cdev.enabled = 0; + pplatData->buttons[i].enabled = false; + err = sensors_classdev_register(&pplatData->buttons[i].input_dev->dev, &pplatData->buttons[i].sensors_capsensor_cdev); + if (err < 0) + LOG_ERR("create %d cap sensor_class file failed (%d)\n", i, err); + pplatData->buttons[i].sensors_capsensor_cdev.dev->platform_data = this; + + } + } + + switch(pplatData->power_supply_type){ + case SX937X_POWER_SUPPLY_TYPE_PMIC_LDO: + pplatData->cap_vdd = regulator_get(&client->dev, "cap_vdd"); + if (IS_ERR(pplatData->cap_vdd)) { + if (PTR_ERR(pplatData->cap_vdd) == -EPROBE_DEFER) { + err = PTR_ERR(pplatData->cap_vdd); + return err; + } + LOG_INFO("Failed to get regulator\n"); + } else { + LOG_INFO("cap_vdd init regulator is %s\n", + regulator_is_enabled(pplatData->cap_vdd) ? + "on" : "off"); + err = regulator_enable(pplatData->cap_vdd); + if (err) { + regulator_put(pplatData->cap_vdd); + LOG_ERR("Error %d enable regulator\n", + err); + return err; + } + pplatData->cap_vdd_en = true; + LOG_INFO("cap_vdd regulator is %s\n", + regulator_is_enabled(pplatData->cap_vdd) ? + "on" : "off"); + } + break; + case SX937X_POWER_SUPPLY_TYPE_ALWAYS_ON: + LOG_INFO("using always on power supply\n"); + break; + case SX937X_POWER_SUPPLY_TYPE_EXTERNAL_LDO: + LOG_INFO("enable external LDO, en_gpio:%d\n", + pplatData->eldo_gpio); + err = gpio_request(pplatData->eldo_gpio, "sx937x_eldo_gpio"); + if (err < 0){ + LOG_ERR("SX937x Request eLDO gpio. Fail![%d]\n", err); + return err; + } + err = gpio_direction_output(pplatData->eldo_gpio,1); + if(err < 0){ + LOG_ERR("can not enable external LDO,%d", err); + return err; + } + pplatData->eldo_vdd_en = true; + msleep(20); + break; + } + sx93XX_IRQ_init(this); + } else { + return -1; + } + + pplatData->exit_platform_hw = sx937x_exit_platform_hw; + + /* call init function pointer (this should initialize all registers */ + if (this->init) { + if (this->init(this) != 0) + goto Hardware_CheckFail; + } + + if (sx937x_Hardware_Check(this) != 0) { + LOG_ERR("sx937x_Hardware_CheckFail!\n"); + if (this->failStatusCode & SX937x_I2C_ERROR) { + LOG_ERR("I2C error, delete sensor"); + goto Hardware_CheckFail; + } + } + + if (pplatData->reinit_on_i2c_failure) { + INIT_DELAYED_WORK(&this->i2c_watchdog_work, sx937x_i2c_watchdog_work); + schedule_delayed_work(&this->i2c_watchdog_work, + msecs_to_jiffies(SX937X_I2C_WATCHDOG_TIME)); + } + + LOG_INFO("sx937x_probe() Done\n"); + return 0; + +Hardware_CheckFail: + if (this->dbg_dev) { + device_unregister(this->dbg_dev); + this->dbg_dev = NULL; + } + + for (i = 0; i < pplatData->buttonSize; i++) { + if (pplatData->buttons[i].used == true) { + sensors_classdev_unregister(&(pplatData->buttons[i].sensors_capsensor_cdev)); + input_unregister_device(pplatData->buttons[i].input_dev); + pplatData->buttons[i].used = false; + } + } + if (pplatData->cap_vdd_en) { + regulator_disable(pplatData->cap_vdd); + regulator_put(pplatData->cap_vdd); + pplatData->cap_vdd_en = false; + } + if(pplatData->eldo_vdd_en){ + gpio_direction_output(pplatData->eldo_gpio,0); + pplatData->eldo_vdd_en = false; + } + + return -ENODEV; +} +/*! \fn static int sx937x_remove(struct i2c_client *client) + * \brief Called when device is to be removed + * \param client Pointer to i2c_client struct + * \return Value 0 + */ +#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE +static int sx937x_remove(struct i2c_client *client) +#else +static void sx937x_remove(struct i2c_client *client) +#endif +{ + psx937x_platform_data_t pplatData =0; + struct _buttonInfo *pCurrentbutton; + int i = 0; + psx93XX_t this = i2c_get_clientdata(client); + LOG_DBG("sx937x_remove"); + if (this) + { + pplatData = client->dev.platform_data; + free_irq(this->irq, this); + cancel_delayed_work_sync(&(this->dworker)); + + if (pplatData && pplatData->exit_platform_hw) + pplatData->exit_platform_hw(client); + + if (pplatData->cap_vdd_en) { + regulator_disable(pplatData->cap_vdd); + regulator_put(pplatData->cap_vdd); + } + + if (this->dbg_dev) + device_unregister(this->dbg_dev); + + if(pplatData->eldo_vdd_en){ + gpio_direction_output(pplatData->eldo_gpio,0); + } + for (i = 0; i < pplatData->buttonSize; i++) { + pCurrentbutton = &(pplatData->buttons[i]); + if (pCurrentbutton->used == true) { + sensors_classdev_unregister(&(pCurrentbutton->sensors_capsensor_cdev)); + input_unregister_device(pCurrentbutton->input_dev); + } + } + } +#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE + return 0; +#endif + +} +/*====================================================*/ +/***** Kernel Suspend *****/ +static int sx937x_suspend(struct device *dev) +{ + psx93XX_t this = dev_get_drvdata(dev); + psx937x_platform_data_t pdata = 0; + + if (this) { + /* If we happen to reinitialize during suspend we might fail so wait for it to end */ + if ((pdata = this->hw)) { + if (pdata->reinit_on_i2c_failure) + cancel_delayed_work_sync(&this->i2c_watchdog_work); + } + + sx937x_i2c_write_16bit(this->bus,SX937X_COMMAND,0xD);//make sx937x in Sleep mode + LOG_DBG(LOG_TAG "sx937x suspend:disable irq!\n"); + disable_irq(this->irq); + this->suspended = 1; + } + return 0; +} +/***** Kernel Resume *****/ +static int sx937x_resume(struct device *dev) +{ + psx93XX_t this = dev_get_drvdata(dev); + psx937x_platform_data_t pdata = 0; + + if (this) { + sx93XX_schedule_work(this,0); + enable_irq(this->irq); + sx937x_i2c_write_16bit(this->bus,SX937X_COMMAND,0xC);//Exit from Sleep mode + this->suspended = 0; + + /* Restart the watchdog in 2 seconds */ + if ((pdata = this->hw)) { + if (pdata->reinit_on_i2c_failure) + schedule_delayed_work(&this->i2c_watchdog_work, + msecs_to_jiffies(SX937X_I2C_WATCHDOG_TIME_ERR)); + } + + } + return 0; +} +/*====================================================*/ + + +static struct i2c_device_id sx937x_idtable[] = +{ + { DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sx937x_idtable); + +static struct of_device_id sx937x_match_table[] = +{ + { .compatible = "Semtech,sx937x",}, + { }, +}; + +static const struct dev_pm_ops sx937x_pm_ops = +{ + .suspend = sx937x_suspend, + .resume = sx937x_resume, +}; + +static struct i2c_driver sx937x_driver = +{ + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = sx937x_match_table, + .pm = &sx937x_pm_ops, + }, + .id_table = sx937x_idtable, + .probe = sx937x_probe, + .remove = sx937x_remove, +}; +static int __init sx937x_I2C_init(void) +{ + capsense_class = class_create(THIS_MODULE, "capsense"); + if (IS_ERR(capsense_class)) + { + printk("sensor init fail!\n"); + return PTR_ERR(capsense_class); + } + return i2c_add_driver(&sx937x_driver); +} +static void __exit sx937x_I2C_exit(void) +{ + i2c_del_driver(&sx937x_driver); + class_destroy(capsense_class); +} + +module_init(sx937x_I2C_init); +module_exit(sx937x_I2C_exit); + +MODULE_AUTHOR("Semtech Corp. (http://www.semtech.com/)"); +MODULE_DESCRIPTION("SX937x Capacitive Proximity Controller Driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1"); + +/* Read i2c every 10 seconds, if there is an error, schedule again in 2 seconds + * and if it fails a few more times we can assume there is a device error and reset + */ +static void sx937x_i2c_watchdog_work(struct work_struct *work) +{ + static int err_cnt = 0; + psx93XX_t this = container_of(work, sx93XX_t, i2c_watchdog_work.work); + int ret; + u32 temp; + int delay = SX937X_I2C_WATCHDOG_TIME; + + LOG_DBG("sx937x_i2c_watchdog_work"); + + if(!this->suspended) { + ret = sx937x_i2c_read_16bit(this->bus, SX937X_DEVICE_INFO, &temp); + if (ret < 0) { + err_cnt++; + LOG_ERR("sx937x_i2c_watchdog_work err_cnt: %d", err_cnt); + delay = SX937X_I2C_WATCHDOG_TIME_ERR; + } else + err_cnt = 0; + + if (err_cnt >= 3) { + err_cnt = 0; + sx937x_reinitialize(this); + delay = SX937X_I2C_WATCHDOG_TIME; + } + } else + LOG_DBG("sx937x_i2c_watchdog_work before resume."); + + schedule_delayed_work(&this->i2c_watchdog_work, + msecs_to_jiffies(delay)); +} + +static void sx937x_reinitialize(psx93XX_t this) +{ + psx937x_platform_data_t pdata = 0; + struct _buttonInfo *pCurrentbutton; + u32 temp; + int i=0; + int retry; + + if (this && (pdata = this->hw)) { + if (!pdata->reinit_on_i2c_failure && !pdata->reinit_on_cali) + return; + if (!atomic_add_unless(&this->init_busy, 1, 1)) + return; + disable_irq(this->irq); + /* perform a reset */ + for ( retry = 10; retry > 0; retry-- ) { + if (sx937x_i2c_write_16bit(this->bus, SX937X_DEVICE_RESET, 0xDE) >= 0) + break; + LOG_INFO("SX937x write SX937X_DEVICE_RESET retry:%d\n", 11 - retry); + msleep(10); + } + /* wait until the reset has finished by monitoring NIRQ */ + LOG_INFO("Sent Software Reset. Waiting until device is back from reset to continue.\n"); + /* just sleep for awhile instead of using a loop with reading irq status */ + msleep(100); + sx937x_reg_init(this); + /* re-enable interrupt handling */ + enable_irq(this->irq); + + /* make sure no interrupts are pending since enabling irq will only + * work on next falling edge */ + read_regStat(this); + + /* If one of the sensors is on, re-enable it */ + sx937x_i2c_read_16bit(this->bus, SX937X_GENERAL_SETUP, &temp); + for (i=0; i < pdata->buttonSize; i++) { + pCurrentbutton = &(pdata->buttons[i]); + if (pCurrentbutton->enabled) { + sx937x_i2c_write_16bit(this->bus, SX937X_GENERAL_SETUP, temp | 0x0000007F); + break; + } + } + + manual_offset_calibration(this->hw); + atomic_set(&this->init_busy, 0); + LOG_ERR("reinitialized sx937x, count %d\n", this->reset_count++); + } +} diff --git a/drivers/misc/tps61280/Android.mk b/drivers/misc/tps61280/Android.mk new file mode 100644 index 000000000000..3876dca68d8a --- /dev/null +++ b/drivers/misc/tps61280/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := tps61280.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/tps61280/Kbuild b/drivers/misc/tps61280/Kbuild new file mode 100644 index 000000000000..f2883bf7de82 --- /dev/null +++ b/drivers/misc/tps61280/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +obj-m += tps61280.o diff --git a/drivers/misc/tps61280/Makefile b/drivers/misc/tps61280/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/misc/tps61280/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/misc/tps61280/tps61280.c b/drivers/misc/tps61280/tps61280.c new file mode 100644 index 000000000000..e2bf5663e874 --- /dev/null +++ b/drivers/misc/tps61280/tps61280.c @@ -0,0 +1,290 @@ +/* + * Driver for TI,TP61280 DC/DC Boost Converter + * + * Copyright (c) 2016 lenovo Corporation. 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 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#define TPS61280_REVISION 0x00 +#define TPS61280_CONFIG 0x01 +#define TPS61280_VOUTFLOORSET 0x02 +#define TPS61280_VOUTROOFSET 0x03 +#define TPS61280_ILIMSET 0x04 +#define TPS61280_STATUS 0x05 +#define TPS61280_E2PROMCTRL 0xFF + +#define TPS61280_ILIM_BASE 0x08 +#define TPS61280_ILIM_OFFSET 1500 +#define TPS61280_ILIM_STEP 500 +#define TPS61280_ENABLE_ILIM 0x0 +#define TPS61280_DISABLE_ILIM 0x20 +#define TPS61280_VOUT_OFFSET 2850 +#define TPS61280_VOUT_STEP 50 +#define TPS61280_HOTDIE_TEMP 120 +#define TPS61280_NORMAL_OPTEMP 40 + +#define TPS61280_CONFIG_MODE_MASK 0x03 +#define TPS61280_CONFIG_SSFM_MASK 0x04 +#define TPS61280_CONFIG_GPIOCFG_MASK 0x08 +#define TPS61280_CONFIG_MODE_VSEL 0x03 +#define TPS61280_CONFIG_MODE_FORCE_PWM 0x02 +#define TPS61280_CONFIG_MODE_AUTO_PWM 0x01 +#define TPS61280_CONFIG_MODE_DEVICE 0x00 + +#define TPS61280_ILIM_MASK 0x0F +#define TPS61280_SOFT_START_MASK 0x10 +#define TPS61280_ILIM_OFF_MASK 0x20 +#define TPS61280_ILIM_ALL_MASK (TPS61280_ILIM_MASK |\ + TPS61280_SOFT_START_MASK |\ + TPS61280_ILIM_OFF_MASK) +#define TPS61280_VOUT_MASK 0x1F + +#define TPS61280_CONFIG_ENABLE_MASK 0x60 +#define TPS61280_CONFIG_BYPASS_HW_CONTRL 0x00 +#define TPS61280_CONFIG_BYPASS_AUTO_TRANS 0x20 +#define TPS61280_CONFIG_BYPASS_PASS_TROUGH 0x40 +#define TPS61280_CONFIG_BYPASS_SHUTDOWN 0x60 + +/* in config register bit 7 is a reset. bit 4 is reserved */ +#define TPS61280_CONFIG_MASK (TPS61280_CONFIG_MODE_MASK |\ + TPS61280_CONFIG_SSFM_MASK |\ + TPS61280_CONFIG_GPIOCFG_MASK |\ + TPS61280_CONFIG_ENABLE_MASK) + +#define TPS61280_PGOOD_MASK BIT(0) +#define TPS61280_FAULT_MASK BIT(1) +#define TPS61280_ILIMBST_MASK BIT(2) +#define TPS61280_ILIMPT_MASK BIT(3) +#define TPS61280_OPMODE_MASK BIT(4) +#define TPS61280_DCDCMODE_MASK BIT(5) +#define TPS61280_HOTDIE_MASK BIT(6) +#define TPS61280_THERMALSD_MASK BIT(7) + +#define TPS61280_MAX_VOUT_REG 2 +#define TPS61280_VOUT_MASK 0x1F + +#define TPS61280_VOUT_VMIN 2850000 +#define TPS61280_VOUT_VMAX 4400000 +#define TPS61280_VOUT_VSTEP 50000 + +/* four configuration registers of tps61280 */ +struct tps61280_platform_data { + int config; + int vout_floor; + int vout_roof; + int ilim; +}; + +struct tps61280_chip { + struct device *dev; + struct i2c_client *client; + struct regmap *rmap; + struct tps61280_platform_data pdata; + +}; + +static const struct regmap_config tps61280_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 5, +}; + +static int tps61280_config(struct tps61280_chip *tps61280) +{ + int ret; + struct device *dev = tps61280->dev; + struct tps61280_platform_data pdata = tps61280->pdata; + + ret = regmap_update_bits(tps61280->rmap, TPS61280_CONFIG, + TPS61280_CONFIG_ENABLE_MASK, TPS61280_CONFIG_BYPASS_SHUTDOWN); + if (ret < 0) { + dev_err(dev, "CONFIG update failed %d\n", ret); + return ret; + } + + ret = regmap_update_bits(tps61280->rmap, TPS61280_VOUTFLOORSET, + TPS61280_VOUT_MASK, pdata.vout_floor); + if (ret < 0) { + dev_err(dev, "VOUTFLOORSET update failed %d\n", ret); + return ret; + } + + ret = regmap_update_bits(tps61280->rmap, TPS61280_VOUTROOFSET, + TPS61280_VOUT_MASK, pdata.vout_roof); + if (ret < 0) { + dev_err(dev, "VOUTROOFET update failed %d\n", ret); + return ret; + } + + ret = regmap_update_bits(tps61280->rmap, TPS61280_ILIMSET, + TPS61280_ILIM_ALL_MASK, pdata.ilim); + if (ret < 0) { + dev_err(dev, "ILIM update failed %d\n", ret); + return ret; + } + + ret = regmap_update_bits(tps61280->rmap, TPS61280_CONFIG, + TPS61280_CONFIG_MASK, pdata.config); + if (ret < 0) { + dev_err(dev, "CONFIG update failed %d\n", ret); + return ret; + } + + return 0; +} + +static int tps61280_parse_dt_data(struct i2c_client *client, + struct tps61280_platform_data *pdata) +{ + struct device_node *np = client->dev.of_node; + int rc; + + rc = of_property_read_u32(np, "ti,config", &pdata->config); + if (rc) { + dev_err(&client->dev, "%s: config read error\n", __func__); + return -EIO; + } + + rc = of_property_read_u32(np, "ti,vout-floor", &pdata->vout_floor); + if (rc) { + dev_err(&client->dev, "%s: vout_floor read error\n", __func__); + return -EIO; + } + + rc = of_property_read_u32(np, "ti,vout-roof", &pdata->vout_roof); + if (rc) { + dev_err(&client->dev, "%s: vout_roof read error\n", __func__); + return -EIO; + } + + rc = of_property_read_u32(np, "ti,ilim", &pdata->ilim); + if (rc) { + dev_err(&client->dev, "%s: ilim read error\n", __func__); + return -EIO; + } + + return 0; +} + + +static int tps61280_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tps61280_chip *tps61280; + int ret; + unsigned readout; + + if (!client->dev.of_node) { + dev_err(&client->dev, "Only Supporting DT\n"); + return -ENODEV; + } + + tps61280 = devm_kzalloc(&client->dev, sizeof(*tps61280), GFP_KERNEL); + if (!tps61280) + return -ENOMEM; + + ret = tps61280_parse_dt_data(client, &tps61280->pdata); + if (ret < 0) { + dev_err(&client->dev, "Platform data pasring failed: %d\n", + ret); + return ret; + } + + tps61280->rmap = devm_regmap_init_i2c(client, &tps61280_regmap_config); + if (IS_ERR(tps61280->rmap)) { + ret = PTR_ERR(tps61280->rmap); + dev_err(&client->dev, "regmap init failed with err%d\n", ret); + return ret; + } + + tps61280->client = client; + tps61280->dev = &client->dev; + i2c_set_clientdata(client, tps61280); + + ret = regmap_read(tps61280->rmap, TPS61280_STATUS, &readout); + if (ret < 0) + dev_err(&client->dev, "STATUS read failed %d\n", ret); + else + dev_info(&client->dev, "power on status %#x\n", readout); + + ret = regmap_read(tps61280->rmap, TPS61280_REVISION, &readout); + if (ret < 0) + dev_err(&client->dev, "Revision read failed %d\n", ret); + else + dev_info(&client->dev, "Revision %#x\n", readout); + + ret = tps61280_config(tps61280); + if (ret < 0) { + dev_err(&client->dev, "Mode init failed: %d\n", ret); + return ret; + } + + dev_info(&client->dev, "DC-Boost probe successful\n"); + + ret = regmap_read(tps61280->rmap, TPS61280_STATUS, &readout); + if (ret < 0) + dev_err(&client->dev, "STATUS read failed %d\n", ret); + else + dev_info(&client->dev, "current status %#x\n", readout); + + return 0; +} + + +static const struct of_device_id tps61280_dt_match[] = { + { .compatible = "ti,tps61280" }, + { }, +}; +MODULE_DEVICE_TABLE(of, tps61280_dt_match); + +static const struct i2c_device_id tps61280_i2c_id[] = { + { "tps61280", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tps61280_i2c_id); + +static struct i2c_driver tps61280_driver = { + .driver = { + .name = "tps61280", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tps61280_dt_match), + }, + .probe = tps61280_probe, + .id_table = tps61280_i2c_id, +}; + +static int __init tps61280_init(void) +{ + return i2c_add_driver(&tps61280_driver); +} +fs_initcall_sync(tps61280_init); + +static void __exit tps61280_exit(void) +{ + i2c_del_driver(&tps61280_driver); +} +module_exit(tps61280_exit); + +MODULE_DESCRIPTION("simple tps61280 driver"); +MODULE_ALIAS("i2c:tps61280"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/tps61280a/Android.mk b/drivers/misc/tps61280a/Android.mk new file mode 100644 index 000000000000..aa98a0d8a3e8 --- /dev/null +++ b/drivers/misc/tps61280a/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := tps61280a.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/tps61280a/Kbuild b/drivers/misc/tps61280a/Kbuild new file mode 100644 index 000000000000..fc12070e8374 --- /dev/null +++ b/drivers/misc/tps61280a/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +obj-m += tps61280a.o diff --git a/drivers/misc/tps61280a/Makefile b/drivers/misc/tps61280a/Makefile new file mode 100644 index 000000000000..4416b208b762 --- /dev/null +++ b/drivers/misc/tps61280a/Makefile @@ -0,0 +1,25 @@ +#KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +#all: +# $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +#modules_install: +# $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +#clean: +# $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + +#all: modules + +modules: + $(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 diff --git a/drivers/misc/tps61280a/tps61280a.c b/drivers/misc/tps61280a/tps61280a.c new file mode 100644 index 000000000000..6b0318c6aa90 --- /dev/null +++ b/drivers/misc/tps61280a/tps61280a.c @@ -0,0 +1,461 @@ +/* Copyright (c) 2015-2016, The Linux Foundation. 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 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PMIC_REG_CONFIG 0x01 +#define PMIC_REG_VOUTFLOORSET 0x02 +#define PMIC_REG_VOUTROOFSET 0x03 +#define PMIC_REG_ILIMSET 0x04 +#define PMIC_REG_STATUS 0x05 +#define PMIC_REG_E2PROMCTRL 0xFF + +#define VOUT_SPPORT_MIN 0 +#define VOUT_SPPORT_MAX 0x1F +#define VOUT_SPPORT_MID 0x0F +#define DEFAULT_VOUTL_VALUE 0x06 +#define DEFAULT_VOUTH_VALUE 0x0A + +#define PINCTRL_STATE_ACTIVE "tps61280a_active" +#define PINCTRL_STATE_SUSPEND "tps61280a_suspend" +#define RETRY_COUNT 3 +struct tps61280a_platform_data { + int gpio_en; + int gpio_vsel; + int gpio_rst; + struct pinctrl *tps_pinctrl; + struct pinctrl_state *pinctrl_state_active; + struct pinctrl_state *pinctrl_state_suspend; + struct i2c_client *i2c_client; + uint8_t xbuf[4]; +}; + +static struct tps61280a_platform_data *gpdata; + +static int tps61280a_i2c_read(struct i2c_client *client, uint8_t command, uint8_t *data, uint8_t length, uint8_t toRetry) +{ + int retry; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &command, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = length, + .buf = data, + } + }; + + for (retry = 0; retry < toRetry; retry++) { + if (i2c_transfer(client->adapter, msg, 2) == 2) + break; + msleep(10); + } + + if (retry == toRetry) { + pr_err("%s: i2c_read_block retry over %d\n", __func__, toRetry); + return -EIO; + } + + return 0; +} + +static int tps61280a_i2c_write(struct i2c_client *client, uint8_t *data, uint8_t length, uint8_t toRetry) +{ + int retry; + + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = length, + .buf = data, + } + }; + + for (retry = 0; retry < toRetry; retry++) { + if (i2c_transfer(client->adapter, msg, 1) == 1) + break; + msleep(10); + } + + if (retry == toRetry) { + pr_err("%s: i2c_write_block retry over %d\n", __func__, toRetry); + return -EIO; + } + + return 0; +} + +static unsigned char tps61280a_read_register(uint8_t adress) +{ + int ret = 0; + unsigned char buffer; + struct i2c_client *i2c =gpdata->i2c_client; + + ret = tps61280a_i2c_read(i2c, adress, &buffer, 1, RETRY_COUNT); + if (ret) { + pr_err("%s failed\n", __func__); + } + return buffer; +} + +static int tps61280a_write_register(uint8_t adress, uint8_t value) +{ + int ret = 0; + unsigned char buffer[2]; + struct i2c_client *i2c =gpdata->i2c_client; + + buffer[0] = adress; + buffer[1] = value; + ret = tps61280a_i2c_write(i2c, buffer , 2, RETRY_COUNT); + if (ret) { + pr_err("%s failed\n", __func__); + } + return ret; +} + +static int tps61280a_vout_setting(int vfloor, int vroof) +{ + int ret = 0; + int fvout = (vfloor - 2850) / 50, rvout = (vroof - 2850) / 50; + + pr_info("%s vfloor = %d fvout = 0x%x\n", __func__, vfloor , fvout); + pr_info("%s vroot = %d rvout = 0x%x\n", __func__, vroof, rvout); + + fvout = max(fvout, VOUT_SPPORT_MIN); + fvout = min(fvout, VOUT_SPPORT_MAX); + + rvout = max(rvout, VOUT_SPPORT_MIN); + rvout = min(rvout, VOUT_SPPORT_MAX); + + ret = tps61280a_write_register(PMIC_REG_VOUTFLOORSET, fvout); + if (ret) { + pr_err("%s FLOORSET failed\n", __func__); + } + ret = 0 ; + ret = tps61280a_write_register(PMIC_REG_VOUTROOFSET, rvout); + if (ret) { + pr_err("%s ROOFSET failed\n", __func__); + } + + return ret; +} + +static void tps61280a_dump(void) +{ + int ret = 0 , i; + for (i=1;i<=5;i++) + { + ret = tps61280a_read_register(i); + pr_err("%s reg = 0x%x data = 0x%x\n", __func__,i ,ret); + } + ret = tps61280a_read_register(PMIC_REG_E2PROMCTRL); + pr_info("%s reg = 0xff data = 0x%x\n", __func__,ret); + +} + + +static int config_tps61280a_enable_setting(void) +{ + int ret = 0; + + ret = tps61280a_write_register(PMIC_REG_CONFIG, 0x09); + if (ret) { + pr_err("%s failed\n", __func__); + } + ret = 0; + ret = tps61280a_write_register(PMIC_REG_ILIMSET, 0x0f); + if (ret) { + pr_err("%s failed\n", __func__); + } + + return ret; +} + +/*static int tps61280a_regulator_configure(struct i2c_client *client, struct tps61280a_platform_data *pdata, bool regulator_on) +{ + int ret = 0; + + if (regulator_on) { + pdata->vcc_i2c = regulator_get(&client->dev, "vcc_i2c"); + if (IS_ERR(pdata->vcc_i2c)) { + ret = PTR_ERR(pdata->vcc_i2c); + pr_err("regulator get failed rc=%d\n", ret); + } + if (regulator_count_voltages(pdata->vcc_i2c) > 0) { + ret = regulator_set_voltage(pdata->vcc_i2c, 1800000, 1800000); + if (ret) { + pr_err("regulator set_vtg failed rc=%d\n", ret); + } + } + + ret = regulator_enable(pdata->vcc_i2c); + } else { + if (regulator_count_voltages(pdata->vcc_i2c) > 0) + regulator_set_voltage(pdata->vcc_i2c, 0, 1800000); + + regulator_put(pdata->vcc_i2c); + } + + return ret; +} +*/ +static int tps61280a_gpio_power_config(struct tps61280a_platform_data *pdata) +{ + int error = 0; + + if (!pdata) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + if (gpio_is_valid(pdata->gpio_en)) { + error = gpio_request(pdata->gpio_en, "tps61280a_en_gpio"); + if (error) { + pr_err("unable to request gpio = %d\n", pdata->gpio_en); + return error; + } + + error = gpio_direction_output(pdata->gpio_en, 1); + if (error) { + pr_err("unable to set direction for gpio = %d\n", pdata->gpio_en); + return error; + } + } + + if (gpio_is_valid(pdata->gpio_vsel)) { + error = gpio_request(pdata->gpio_vsel, "tps61280a_vsel_gpio"); + if (error) { + pr_err("unable to request gpio = %d\n", pdata->gpio_vsel); + return error; + } + + error = gpio_direction_output(pdata->gpio_vsel, 1); + if (error) { + pr_err("unable to set direction for gpio = %d\n", pdata->gpio_vsel); + return error; + } + } + + /* Add for RT4803 EN/nBYP timing */ + udelay(60); + + if (gpio_is_valid(pdata->gpio_rst)) { + error = gpio_request(pdata->gpio_rst, "tps61280a_rst_gpio"); + if (error) { + pr_err("unable to request gpio = %d\n", pdata->gpio_rst); + return error; + } + + error = gpio_direction_output(pdata->gpio_rst, 1); + if (error) { + pr_err("unable to set direction for gpio = %d\n", pdata->gpio_rst); + return error; + } + } + tps61280a_dump(); + //msleep(5); + config_tps61280a_enable_setting(); + //msleep(5); + //tps61280a_dump(); + + return error; +} + +static int tps61280a_parse_dt(struct device *dev, struct tps61280a_platform_data *pdata) +{ + if (!dev || !dev->of_node || !pdata) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + pdata->gpio_en= of_get_named_gpio(dev->of_node, "tps61280a,en-gpio", 0); + if (!gpio_is_valid(pdata->gpio_en)) { + pr_err("DT:en-gpio value is not valid\n"); + } + + pdata->gpio_vsel = of_get_named_gpio(dev->of_node, "tps61280a,vsel-gpio", 0); + if (!gpio_is_valid(pdata->gpio_vsel)) { + pr_err("DT:vsel-gpio value is not valid\n"); + } + + pdata->gpio_rst = of_get_named_gpio(dev->of_node, "tps61280a,rst-gpio", 0); + if (!gpio_is_valid(pdata->gpio_rst)) { + pr_err("DT:rst-gpio value is not valid\n"); + } + + pr_info("DT: gpio_en=%d, gpio_vsel=%d, gpio_rst =%d\n", + pdata->gpio_en, pdata->gpio_vsel, pdata->gpio_rst); + return 0; +} + +static int tps61280a_pinctrl_init(struct i2c_client *client, struct tps61280a_platform_data *pdata) +{ + if (!pdata) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + pdata->tps_pinctrl = devm_pinctrl_get(&(client->dev)); + if (!pdata->tps_pinctrl) { + dev_err(&(client->dev), "Target does not use pinctrl\n"); + return -EINVAL; + } + + pdata->pinctrl_state_active = pinctrl_lookup_state(pdata->tps_pinctrl, PINCTRL_STATE_ACTIVE); + if (!pdata->pinctrl_state_active) { + dev_err(&(client->dev), "Can not lookup %s pinstate\n", PINCTRL_STATE_ACTIVE); + return -EINVAL; + } + + pdata->pinctrl_state_suspend = pinctrl_lookup_state(pdata->tps_pinctrl, PINCTRL_STATE_SUSPEND); + if (!pdata->pinctrl_state_suspend) { + dev_err(&(client->dev), "Can not lookup %s pinstate\n", PINCTRL_STATE_SUSPEND); + return -EINVAL; + } + + pinctrl_select_state(pdata->tps_pinctrl, pdata->pinctrl_state_active); + return 0; +} + +static int tps61280a_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + int vout_floor = 3500, vout_roof = 4200; + struct tps61280a_platform_data *pdata = NULL; + + if (!client) { + pr_err("%s: invalid input\n", __func__); + return -EINVAL; + } + + pdata = devm_kzalloc(&(client->dev), sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + ret = -ENOMEM; + pr_err("%s: allocate tps61280a_platform_data failed\n", __func__); + goto err_alloc_platform_data; + } + + ret = tps61280a_parse_dt(&(client->dev), pdata); + if (ret) { + ret = -EINVAL; + pr_err("%s: failed to parse DT\n", __func__); + goto err_dt_parse; + } + + pdata->i2c_client = client; + gpdata = pdata; + ret = tps61280a_pinctrl_init(client, pdata); + if (ret) + { + pr_err("%s: tps61280a_pinctrl_init failed\n", __func__); + goto err_pinctrl_init; + } + + //tps61280a_regulator_configure(client, pdata, true); + + ret = tps61280a_gpio_power_config(pdata); + if (ret) + { + pr_err("%s: tps61280a_gpio_power_config failed\n", __func__); + goto err_gpio_power; + } + + ret = tps61280a_vout_setting(vout_floor, vout_roof); + if (ret) + { + pr_err("%s: failed\n", __func__); + goto err_config_init; + } + + tps61280a_dump(); + pr_info("%s end\n", __func__); + return 0; + +err_config_init: +err_gpio_power: +err_pinctrl_init: + gpdata = NULL; +err_dt_parse: + devm_kfree(&(client->dev), pdata); +err_alloc_platform_data: + return ret; +} + +static int tps61280a_remove(struct i2c_client *client) +{ + struct tps61280a_platform_data *tps = i2c_get_clientdata(client); + + gpdata = NULL; + devm_kfree(&(client->dev), tps); + + return 0; +} + +static struct of_device_id tps61280a_match_table[] = { + {.compatible = "ti,tps61280a" }, + {}, +}; + +static struct i2c_device_id tps61280a_id[] = { + { "ti,tps61280a", 0}, + {} +}; + +static struct i2c_driver tps61280a_driver = { + .driver = { + .name = "ti,tps61280a", + .owner = THIS_MODULE, + .of_match_table = tps61280a_match_table, + }, + .probe = tps61280a_probe, + .remove = tps61280a_remove, + .id_table = tps61280a_id, +}; + +static int __init tps61280a_init(void) +{ + return i2c_add_driver(&tps61280a_driver); +} + +static void __exit tps61280a_exit(void) +{ + i2c_del_driver(&tps61280a_driver); +} + +subsys_initcall(tps61280a_init); +module_exit(tps61280a_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("tps61280a driver"); + diff --git a/drivers/misc/utag/Android.mk b/drivers/misc/utag/Android.mk new file mode 100644 index 000000000000..e11fa6989b60 --- /dev/null +++ b/drivers/misc/utag/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := utags.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/misc/utag/Kbuild b/drivers/misc/utag/Kbuild new file mode 100644 index 000000000000..89c6baff5215 --- /dev/null +++ b/drivers/misc/utag/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +obj-m = utags.o diff --git a/drivers/misc/utag/Makefile b/drivers/misc/utag/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/misc/utag/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/misc/utag/utags.c b/drivers/misc/utag/utags.c new file mode 100644 index 000000000000..12d93fe6446b --- /dev/null +++ b/drivers/misc/utag/utags.c @@ -0,0 +1,2272 @@ +/* Copyright (c) 2012, Motorola Mobility LLC. 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 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. + * + */ +#define pr_fmt(fmt) "utags (%s): " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_UTAG_SIZE 1024 +#define MAX_UTAG_NAME 32 +#define UTAG_DEPTH 5 +#define UTAG_HEAD "__UTAG_HEAD__" +#define UTAG_TAIL "__UTAG_TAIL__" +#define ROUNDUP(a, b) (((a) + ((b)-1)) & ~((b)-1)) +#define TO_SECT_SIZE(n) (((n) + 511) & ~511) +#define DRVNAME "utags" +#define DEFAULT_ROOT "config" +#define HW_ROOT "hw" + +struct ctrl; + +enum utag_flag { + UTAG_FLAG_PROTECTED = 1 << 0, +}; + +#define UTAG_STATUS_LOADED '0' +#define UTAG_STATUS_RELOAD '1' +#define UTAG_STATUS_NOT_READY '2' +#define UTAG_STATUS_FAILED '3' + +struct utag { + char name[MAX_UTAG_NAME]; /* UTAG name and type combined */ + char name_only[MAX_UTAG_NAME]; /* UTAG name with type removed */ + uint32_t size; + uint32_t flags; + uint32_t util; + void *payload; + struct utag *next; + struct utag *prev; +}; + +struct frozen_utag { + char name[MAX_UTAG_NAME]; + uint32_t size; + uint32_t flags; + uint32_t util; + uint8_t payload[]; +}; + +#define UTAG_MIN_TAG_SIZE (sizeof(struct frozen_utag)) + +enum utag_output { + OUT_ASCII = 0, + OUT_RAW, + OUT_TYPE +}; + +static char *files[] = { + "ascii", + "raw", + "type" +}; + +struct proc_node { + struct list_head entry; + char name[MAX_UTAG_NAME]; /* UTAG name string */ + char type[MAX_UTAG_NAME]; /* UTAG type string */ + char file_name[MAX_UTAG_NAME]; + struct proc_dir_entry *file; + struct proc_dir_entry *dir; + uint32_t mode; + struct ctrl *ctrl; +}; + +struct dir_node { + struct list_head entry; + char name[MAX_UTAG_NAME]; + char path[MAX_UTAG_NAME]; + struct proc_dir_entry *dir; + struct proc_dir_entry *parent; + struct ctrl *ctrl; +}; + +struct blkdev { + const char *name; + struct file *filep; + size_t size; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) || defined(CONFIG_MMI_UTAG_RW_BIO) + struct block_device *bdev; +#endif +}; + +struct ctrl { + struct blkdev main; + struct blkdev backup; + struct platform_device *pdev; + struct proc_dir_entry *root; + char reload; + size_t rsize; + struct list_head dir_list; + struct list_head node_list; + const char *dir_name; + uint32_t lock; + uint32_t hwtag; + struct mutex access_lock; + struct utag *attrib; + struct utag *features; + struct completion load_comp; + struct completion store_comp; + struct workqueue_struct *load_queue; + struct workqueue_struct *store_queue; + struct work_struct load_work; + struct work_struct store_work; + struct utag *head; + int store_work_result; +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) || defined(CONFIG_MMI_UTAG_RW_BIO) +#include +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) +#define PDE_DATA(x) pde_data(x) +#endif + +static struct page *addr_to_page(void *addr) +{ + if (is_vmalloc_addr(addr)) + return vmalloc_to_page(addr); + else + return virt_to_page(addr); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) +#define NR_BIO_MAX_PAGES BIO_MAX_VECS +#else +#define NR_BIO_MAX_PAGES BIO_MAX_PAGES +#endif + +static int utags_submit_bio(struct block_device *bdev, void *buf, int pages, int opf) +{ + int i, ret; + struct bio *bio; + int num, left_pages = pages; + + pr_debug("%s: pages %d left_pages %d begin\n", __func__, pages, left_pages); + while (left_pages > 0) { + num = (left_pages >= NR_BIO_MAX_PAGES) ? NR_BIO_MAX_PAGES : left_pages; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + bio = bio_alloc(bdev, num, 0, GFP_KERNEL); +#else + bio = bio_alloc(GFP_KERNEL, num); +#endif + if (!bio) + return -ENOMEM; + + bio->bi_iter.bi_sector = (pages - left_pages) * (PAGE_SIZE >> 9); + bio->bi_opf = opf; +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) + bio_set_dev(bio, bdev); +#endif + + for (i = 0; i < num; i++) { + if (!bio_add_page(bio, addr_to_page(buf + (pages - left_pages + i) * PAGE_SIZE), PAGE_SIZE, 0)) { + bio_put(bio); + return -EIO; + } + } + + ret = submit_bio_wait(bio); + if (ret) + pr_err("Submit bio err %d,%d,%d", num, opf, ret); + + bio_put(bio); + left_pages -= num; + pr_debug("%s: pages %d left_pages %d\n", __func__, pages, left_pages); + } + return ret; +} + +static ssize_t rw_bdev(struct block_device *bdev, void *buf, size_t count, int opf) +{ + int ret; + + ret = utags_submit_bio(bdev, buf, 1 << get_order(count), opf); + return ret < 0 ? ret : count; +} + +static ssize_t kernel_read_stub(struct blkdev* cb, void *buf, size_t count) +{ + return rw_bdev(cb->bdev, buf, count, REQ_OP_READ); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) +static inline ssize_t kernel_read_stub(struct blkdev* cb, void *addr, size_t count) +{ + loff_t pos = 0; + return kernel_read(cb->filep, addr, count, &pos); +} +#else +static inline ssize_t kernel_read_stub(struct blkdev* cb, void *addr, size_t count) +{ + return kernel_read(cb->filep, 0, addr, count); +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) || defined(CONFIG_MMI_UTAG_RW_BIO) +static ssize_t kernel_write_stub(struct blkdev* cb, void *buf, size_t count) +{ + return rw_bdev(cb->bdev, buf, count, REQ_OP_WRITE | REQ_SYNC); +} +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0) +static inline ssize_t kernel_write_stub(struct blkdev* cb, void *addr, size_t count) +{ + loff_t pos = 0; + return kernel_write(cb->filep, addr, count, &pos); +} +#else +static inline ssize_t kernel_write_stub(struct blkdev* cb, void *addr, size_t count) +{ + loff_t pos = 0; + return vfs_write(cb->filep, addr, count, &pos); +} +#endif + +static int build_utags_directory(struct ctrl *ctrl); +static void clear_utags_directory(struct ctrl *ctrl); + +static int store_utags(struct ctrl *ctrl, struct utag *tags); + +static ssize_t write_utag(struct file *file, const char __user *buffer, + size_t count, loff_t *pos); +static ssize_t new_utag(struct file *file, const char __user *buffer, + size_t count, loff_t *pos); +static ssize_t delete_utag(struct file *file, const char __user *buffer, + size_t count, loff_t *pos); +static int add_utag_tail(struct utag *head, char *utag_name, char *utag_type); + +static int lock_open(struct inode *inode, struct file *file); +static int partition_open(struct inode *inode, struct file *file); + +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +static const struct proc_ops utag_fops = { + .proc_open = partition_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = write_utag, +}; + +static const struct proc_ops new_fops = { + .proc_read = NULL, + .proc_write = new_utag, +}; + +static const struct proc_ops lock_fops = { + .proc_open = lock_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; + +static const struct proc_ops delete_fops = { + .proc_read = NULL, + .proc_write = delete_utag, +}; +#else +static const struct file_operations utag_fops = { + .owner = THIS_MODULE, + .open = partition_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = write_utag, +}; + +static const struct file_operations new_fops = { + .read = NULL, + .write = new_utag, +}; + +static const struct file_operations lock_fops = { + .open = lock_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const struct file_operations delete_fops = { + .read = NULL, + .write = delete_utag, +}; +#endif + +/* + * check utag name + * for special symbols + * + */ + +static int no_show_tag(char *name) +{ + if (('/' == name[0]) && (0 == name[1])) { + pr_err("skipping bl root tag"); + return 1; + } + + if (('.' == name[0]) && (0 == name[1])) { + pr_err("skipping . tag"); + return 1; + } + + return 0; +} + +/* + * Read tags head + */ + +static int read_head(struct blkdev *cb, struct utag *htag) +{ + int bytes; + struct frozen_utag *buf; + + if (!htag) { + pr_err("[%s] null pointer", cb->name); + return -EINVAL; + } + + buf = vmalloc(UTAG_MIN_TAG_SIZE); + if (!buf) + return -ENOMEM; + + bytes = kernel_read_stub(cb, (void *) buf, UTAG_MIN_TAG_SIZE); + if ((int) UTAG_MIN_TAG_SIZE > bytes) { + pr_err("ERR file (%s) read failed ret %d\n", cb->name, bytes); + vfree(buf); + return -EIO; + } + + strlcpy(htag->name, buf->name, MAX_UTAG_NAME - 1); + if (strncmp(htag->name, UTAG_HEAD, MAX_UTAG_NAME)) { + pr_err("[%s] invalid or empty utags partition\n", cb->name); + vfree(buf); + return -EIO; + } + htag->flags = ntohl(buf->flags); + htag->util = ntohl(buf->util); + htag->size = ntohl(buf->size); + pr_debug("utag file (%s) flags %#x util %#x size %#x\n", + cb->name, htag->flags, htag->util, htag->size); + vfree(buf); + return 0; +} + +/* + * Initialize empty tags + * + */ +static int init_empty(struct ctrl *ctrl) +{ + struct utag *htag; + + pr_err("erasing [%s]\n", ctrl->dir_name); + htag = kzalloc(sizeof(struct utag), GFP_KERNEL); + if (!htag) + return -ENOMEM; + + strlcpy(htag->name, UTAG_HEAD, MAX_UTAG_NAME); + add_utag_tail(htag, UTAG_TAIL, NULL); + ctrl->head = htag; + queue_work(ctrl->store_queue, &ctrl->store_work); + wait_for_completion(&ctrl->store_comp); + kfree(htag); + return 0; +} + +static char *get_dir_name(struct ctrl *ctrl, struct proc_dir_entry *dir) +{ + struct dir_node *c, *dir_node = NULL; + + list_for_each_entry(c, &ctrl->dir_list, entry) { + if (c->dir == dir) { + dir_node = c; + break; + } + } + return dir_node ? dir_node->name : "na"; +} + +/* + * Check util field of head utag for actual data size + * + */ +static size_t data_size(struct blkdev *cb) +{ + size_t bytes; + struct utag htag; + struct ctrl *ctrl = container_of(cb, struct ctrl, main); + + memset(&htag, 0, sizeof(struct utag)); + if (read_head(cb, &htag)) + return 0; + + ctrl->lock = htag.flags & UTAG_FLAG_PROTECTED ? 1 : 0; + bytes = htag.util; + pr_debug("file (%s) saved %zu block %zu flags %#x ctrl %zu\n", + cb->name, bytes, cb->size, htag.flags, ctrl->rsize); + + /* On the first read always load entire partition */ + if (ctrl->hwtag) { + pr_debug("[%s] hwtag sizes\n", ctrl->dir_name); + if (!ctrl->rsize || !bytes || bytes > cb->size) + bytes = cb->size; + } else + bytes = cb->size; + + ctrl->rsize = bytes; + + pr_debug("[%s] reading %zu bytes\n", ctrl->dir_name, bytes); + return bytes; +} + +/* + * Open and store file handle for a utag partition + * + * Not thread safe, call from a safe context only + */ +static int open_utags(struct blkdev *cb) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) || defined(CONFIG_MMI_UTAG_RW_BIO) + struct block_device *bdev = NULL; + + bdev = blkdev_get_by_path(cb->name, FMODE_READ | FMODE_WRITE, cb); + + if (IS_ERR(bdev)) { + pr_err("(%s) failed get block device\n", cb->name); + return -EIO; + } + + cb->bdev = bdev; + cb->size = i_size_read(bdev->bd_inode); + cb->filep = NULL; + pr_debug("%s: read inode size %lu\n", __func__, cb->size); +#else + struct inode *inode = NULL; + + if (!cb->name) + return -EIO; + + if (cb->filep) + return 0; + + cb->filep = filp_open(cb->name, O_RDWR|O_SYNC, 0600); + if (IS_ERR_OR_NULL(cb->filep)) { + int rc = PTR_ERR(cb->filep); + if (rc == -EROFS) { + pr_info("[%s] readonly, open as O_RDONLY\n", cb->name); + cb->filep = filp_open(cb->name, O_RDONLY|O_SYNC, 0600); + if (IS_ERR_OR_NULL(cb->filep)) { + rc = PTR_ERR(cb->filep); + pr_err("opening (%s) errno=%d\n", cb->name, rc); + cb->filep = NULL; + return rc; + } + } else { + pr_err("opening (%s) errno=%d\n", cb->name, rc); + cb->filep = NULL; + return rc; + } + } + + if (cb->filep->f_path.dentry) + inode = cb->filep->f_path.dentry->d_inode; + if (!inode || !S_ISBLK(inode->i_mode)) { + pr_err("(%s) not a block device\n", cb->name); + filp_close(cb->filep, NULL); + cb->filep = NULL; + return -EIO; + } + + cb->size = i_size_read(inode->i_bdev->bd_inode); +#endif + pr_debug("[%s] (pid %i) open (%s) success\n", + current->comm, current->pid, cb->name); + return 0; +} + + +/* + * Free the memory associated with a list of tags. + * + */ + +static inline void free_tags(struct utag *tags) +{ + struct utag *next; + + while (tags) { + next = tags->next; + kfree(tags->payload); + kfree(tags); + tags = next; + } +} + +static void walk_tags(struct utag *tags) +{ + struct utag *next; + + while (tags) { + next = tags->next; + pr_debug("utag [%s], payload size %u\n", + tags->name, tags->size); + tags = next; + } +} + +static inline void walk_proc_nodes(struct ctrl *ctrl) +{ + struct proc_node *p; + + list_for_each_entry(p, &ctrl->node_list, entry) { + pr_debug("proc-node [%s:%s:%s]\n", + p->name, p->type, p->file_name); + } +} + +static inline void walk_dir_nodes(struct ctrl *ctrl) +{ + struct dir_node *d; + + list_for_each_entry(d, &ctrl->dir_list, entry) { + pr_debug("dir-node [%s] path [%s]\n", d->name, d->path); + } +} + +/* + * compare names, break check at any null in names + * returns true if match, false otherwise + */ + +static inline bool names_match(const char *s1, const char *s2) +{ + register size_t count = MAX_UTAG_NAME; + register int r, c1, c2; + + pr_debug("cmp (%s) <=> (%s)\n)", s1, s2); + while (count--) { + c1 = *s1++; + c2 = *s2++; + r = c1 - c2; + if (r || !c1 || !c2) + return (r) ? false : true; + } + return true; +} + +/* + * + * Check for name to have single ':' char + * not in the first or last position + * + * returns true if name is OK, false otherwise + */ + +static inline bool validate_name(const char *s1, int count) +{ + register int c1 = *s1, sep = 0; + + if (c1 == ':') + return false; + while (count--) { + if (c1 == ':') + sep++; + if (sep > 1) + return false; + c1 = *s1++; + } + if (c1 == ':') + return false; + return true; +} + +/* + * validate path to have :type as a last element + */ + +static int check_path(char *fullpath, int count) +{ + char *ptr, *bptr; + + ptr = strnchr(fullpath, count, ':'); + bptr = strnchr(fullpath, count, '/'); + if (bptr && ptr && bptr > ptr) { + pr_err("Invalid path %s\n", fullpath); + return -EINVAL; + } + + return 0; +} + + +/* + * Extract hierarchical names from the fullpath + * Return: number of pointers in name array + */ + +static int full_split(char *fullpath, char **name, char **type) +{ + int i; + char *ptr; + + ptr = strnchr(fullpath, MAX_UTAG_NAME, ':'); + if (ptr) { + *ptr++ = 0; + *type = ptr; + pr_debug("type=%s\n", *type); + } + + for (i = 0, ptr = fullpath; ptr; i++) { + if (*ptr == '/') + *ptr++ = 0; + name[i] = ptr; + ptr = strnchr(ptr, MAX_UTAG_NAME, '/'); + pr_debug("name[%d]=%s\n", i, name[i]); + } + + return i; +} + +static int add_utag_tail(struct utag *head, char *utag_name, char *utag_type) +{ + char utag[MAX_UTAG_NAME]; + struct utag *new, *tail; + + scnprintf(utag, MAX_UTAG_NAME, "%s%s%s", utag_name, + utag_type ? ":" : "", utag_type ? utag_type : ""); + + /* find list tail and check for duplicate utag */ + for (tail = head; tail->next; tail = tail->next) { + if (!strncmp(tail->name_only, utag, MAX_UTAG_NAME)) { + pr_debug("utag [%s] already exists\n", utag); + return -EEXIST; + } + } + pr_debug("tail utag [%s]\n", tail->name); + + new = kzalloc(sizeof(struct utag), GFP_KERNEL); + if (!new) + return -ENOMEM; + + strlcpy(new->name, utag, MAX_UTAG_NAME); + new->size = new->flags = new->util = 0; + + if (!tail->prev) { /* tail is in fact the head */ + tail->next = new; + new->prev = tail; + } else { + /* insert new utag before tail */ + new->prev = tail->prev; + new->next = tail; + tail->prev->next = new; + tail->prev = new; + } + walk_tags(head); + + return 0; +} + +/* + * Find first instance of utag by specified name + */ + +static struct utag *find_first_utag(const struct utag *head, const char *name) +{ + struct utag *cur; + + if (!head) + return NULL; + + cur = head->next; /* skip HEAD */ + while (cur) { + /* skip TAIL */ + if (cur->next == NULL) + break; + if (names_match(name, cur->name)) + return cur; + cur = cur->next; + } + return NULL; +} + +/* + * Create, initialize add to the list procfs utag file node + */ + +static int proc_utag_file(char *utag_name, char *utag_type, + enum utag_output mode, struct dir_node *dnode, +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE + const struct proc_ops *fops) +#else + const struct file_operations *fops) +#endif +{ + struct proc_node *node; + struct ctrl *ctrl = dnode->ctrl; + + if (sizeof(files) < mode) + return -EINVAL; + + node = kzalloc(sizeof(struct proc_node), GFP_KERNEL); + if (node) { + list_add_tail(&node->entry, &ctrl->node_list); + strlcpy(node->file_name, files[mode], MAX_UTAG_NAME); + strlcpy(node->name, utag_name, MAX_UTAG_NAME); + if (utag_type) + strlcpy(node->type, utag_type, MAX_UTAG_NAME); + else + node->type[0] = 0; + node->mode = mode; + node->dir = dnode->dir; + node->ctrl = ctrl; + node->file = proc_create_data(node->file_name, 0660, + dnode->dir, fops, node); + + pr_debug("created file [%s/%s]\n", + get_dir_name(ctrl, dnode->dir), node->file_name); + } + return 0; +} + +static struct dir_node *find_dir_node(struct ctrl *ctrl, char *path) +{ + struct dir_node *c, *dir_node = NULL; + + list_for_each_entry(c, &ctrl->dir_list, entry) { + if (!strncmp(c->path, path, MAX_UTAG_NAME)) { + dir_node = c; + break; + } + } + return dir_node; +} + +static int proc_utag_util(struct ctrl *ctrl) +{ + struct proc_dir_entry *dir; + + dir = proc_mkdir_data("all", 0771, ctrl->root, NULL); + if (!dir) { + pr_err("failed to create util\n"); + return -EIO; + } + + if (!proc_create_data("new", 0220, dir, &new_fops, ctrl)) { + pr_err("Failed to create utag new entry\n"); + return -EIO; + } + + if (!proc_create_data("lock", 0660, dir, &lock_fops, ctrl)) { + pr_err("Failed to create lock entry\n"); + return -EIO; + } + + if (!proc_create_data(".delete", 0220, dir, &delete_fops, ctrl)) { + pr_err("Failed to create delete entry\n"); + return -EIO; + } + + return 0; +} + +static struct proc_dir_entry *proc_utag_dir(struct ctrl *ctrl, + char *tname, char *path, char *ttype, bool populate, + struct proc_dir_entry *parent) +{ + struct dir_node *dnode; + struct proc_dir_entry *dir = NULL; + + if (!parent) + parent = ctrl->root; + + dnode = find_dir_node(ctrl, path); + if (dnode) { + if (populate) + goto populate_utag_dir; + + pr_debug("procfs dir %s exists; skip\n", tname); + return dnode->dir; + } + + dir = proc_mkdir_data(tname, 0771, parent, NULL); + if (!dir) { + pr_err("failed to create dir %s\n", tname); + return ERR_PTR(-ENOMEM); + } + + dnode = kzalloc(sizeof(struct dir_node), GFP_KERNEL); + if (!dnode) { + kfree(dir); + pr_err("failed to create node structure\n"); + return ERR_PTR(-ENOMEM); + } + + dnode->parent = parent; + dnode->ctrl = ctrl; + dnode->dir = dir; + list_add_tail(&dnode->entry, &ctrl->dir_list); + strlcpy(dnode->name, tname, MAX_UTAG_NAME); + strlcpy(dnode->path, path, MAX_UTAG_NAME); + + if (!populate) + return dir; + +populate_utag_dir: + + proc_utag_file(tname, ttype, OUT_ASCII, dnode, &utag_fops); + proc_utag_file(tname, ttype, OUT_RAW, dnode, &utag_fops); + proc_utag_file(tname, ttype, OUT_TYPE, dnode, &utag_fops); + + return dir; +} + +/* + * Convert a block of tags, presumably loaded from seconday storage, into a + * format that can be manipulated. + */ +static struct utag *thaw_tags(size_t block_size, void *buf) +{ + struct utag *head = NULL, *cur = NULL; + uint8_t *ptr = buf; + + while (1) { + struct frozen_utag *frozen; + uint8_t *next_ptr; + char *sep; + + frozen = (struct frozen_utag *)ptr; + + if (!head) { + /* This is allocation of the head */ + cur = kzalloc(sizeof(struct utag), GFP_KERNEL); + if (!cur) + return NULL; + } + + strlcpy(cur->name, frozen->name, MAX_UTAG_NAME - 1); + strlcpy(cur->name_only, frozen->name, MAX_UTAG_NAME-1); + sep = strnchr(cur->name_only, MAX_UTAG_NAME, ':'); + if (sep) + *sep = 0; + cur->flags = ntohl(frozen->flags); + cur->util = ntohl(frozen->util); + cur->size = ntohl(frozen->size); + + if (!head) { + head = cur; + + if (strcmp(head->name, UTAG_HEAD)) { + pr_err("invalid or empty utags partition\n"); + goto err_free; + } + } + + /* moved here to print statistics for tail as well */ + next_ptr = ptr + UTAG_MIN_TAG_SIZE + ROUNDUP(cur->size, 4); + pr_debug("utag [%s] size %zu\n", cur->name, next_ptr - ptr); + + /* check if this is the end */ + if (!strcmp(cur->name, UTAG_TAIL)) { + /* footer payload size should be zero */ + if (0 != cur->size) { + pr_err("invalid utags tail\n"); + goto err_free; + } + + /* all done */ + break; + } + + /* + * Ensure there is enough space in the buffer for both the + * payload and the tag header for the next tag. + */ + if ((next_ptr - (uint8_t *) buf) + UTAG_MIN_TAG_SIZE > + block_size) { + pr_err("invalid tags size\n"); + goto err_free; + } + + if (cur->size != 0) { + cur->payload = kzalloc(cur->size + 1, GFP_KERNEL); + if (!cur->payload) + goto err_free; + memcpy(cur->payload, frozen->payload, cur->size); + } + + /* advance to beginning of next tag */ + ptr = next_ptr; + + /* get ready for the next tag */ + cur->next = kzalloc(sizeof(struct utag), GFP_KERNEL); + /* FIXME if kzalloc fails, kernel will panic in the next line */ + cur->next->prev = cur; + cur = cur->next; + if (!cur) + goto err_free; + } /* while (1) */ + + walk_tags(head); + goto out; + + err_free: + free_tags(head); + head = NULL; + out: + return head; +} + +static void *freeze_tags(size_t block_size, struct utag *tags, + size_t *tags_size) +{ + size_t written, frozen_size = 0; + char *buf = NULL, *ptr; + struct utag *cur = tags; + size_t zeros; + struct frozen_utag frozen = { {0} }; + + /* Make sure the tags start with the HEAD marker. */ + if (!tags || strncmp(tags->name, UTAG_HEAD, MAX_UTAG_NAME)) { + pr_err("invalid utags head\n"); + return NULL; + } + + /* + * Walk the list once to determine the amount of space to allocate + * for the frozen tags. + */ + while (cur) { + pr_debug("utag [%s], payload size %u\n", cur->name, cur->size); + frozen_size += ROUNDUP(cur->size, 4) + UTAG_MIN_TAG_SIZE; + pr_debug("calculated size %zu\n", frozen_size); + if (!strncmp(cur->name, UTAG_TAIL, MAX_UTAG_NAME)) + break; + cur = cur->next; + } + + /* round up frozen_size to eMMC sector size */ + frozen_size = TO_SECT_SIZE(frozen_size); + pr_debug("frozen size aligned to sector size %zu\n", frozen_size); + + /* do some more sanity checking */ + if (!cur || cur->next) { + pr_err("utags corrupted\n"); + return NULL; + } + + if (block_size < frozen_size) { + pr_err("utag size %zu too big\n", frozen_size); + return NULL; + } + + ptr = buf = vmalloc(frozen_size); + if (!buf) + return NULL; + + cur = tags; + /* root utag stores size of entire image in util word */ + cur->util = frozen_size; + while (1) { + written = 0; + memcpy(frozen.name, cur->name, MAX_UTAG_NAME); + frozen.flags = htonl(cur->flags); + frozen.size = htonl(cur->size); + frozen.util = htonl(cur->util); + + memcpy(ptr, &frozen, UTAG_MIN_TAG_SIZE); + ptr += UTAG_MIN_TAG_SIZE; + written += UTAG_MIN_TAG_SIZE; + + if (cur->size) { + memcpy(ptr, cur->payload, cur->size); + ptr += cur->size; + written += cur->size; + } + + /* pad with zeros if needed */ + zeros = ROUNDUP(cur->size, 4) - cur->size; + if (zeros) { + memset(ptr, 0, zeros); + ptr += zeros; + written += zeros; + } + + pr_debug("written %zu bytes for utag [%s]\n", + written, cur->name); + + if (!strncmp(cur->name, UTAG_TAIL, MAX_UTAG_NAME)) + break; + + cur = cur->next; + } + + memset(ptr, 0, buf + frozen_size - ptr); + if ((buf + frozen_size - ptr)) + pr_debug("padded %zu bytes\n", buf + frozen_size - ptr); + if (tags_size) + *tags_size = frozen_size; + + return buf; +} + +/* + * Try to load utags into memory from a partition on secondary storage. + * + * Not thread safe, call from a safe context only + */ +static struct utag *load_utags(struct blkdev *cb) +{ + size_t bytes; + int ret_bytes; + void *data; + struct utag *head = NULL; + struct ctrl *ctrl = container_of(cb, struct ctrl, main); + + if (open_utags(cb)) + return NULL; + + bytes = data_size(cb); + + /* + * make sure the block is at least big enough to hold header + * and footer, create empty partition for hwtags only + */ + if (UTAG_MIN_TAG_SIZE * 2 > bytes) { + pr_err("[%s] invalid tags size %zu\n", ctrl->dir_name, bytes); + if (!ctrl->hwtag) + return NULL; + if (init_empty(ctrl)) + return NULL; + bytes = UTAG_MIN_TAG_SIZE * 2; + } + + data = vmalloc(bytes); + if (!data) + return NULL; + + ret_bytes = kernel_read_stub(cb, data, bytes); + if (bytes != ret_bytes) { + pr_err("(%s) read failed ret %d\n", cb->name, ret_bytes); + goto free_data; + } + + head = thaw_tags(bytes, data); + if (!head && ctrl->hwtag) + init_empty(ctrl); + + /* Save pointer to the root attributes UTAG if present */ + if (ctrl->hwtag) { + ctrl->attrib = find_first_utag(head, ".attributes"); + pr_debug(" .attributes %s\n", ctrl->attrib ? + "found" : "not found"); + ctrl->features = find_first_utag(head, ".features"); + pr_debug(" .features %s\n", ctrl->features ? + "found" : "not found"); + } + + free_data: + vfree(data); + return head; +} + +/* + * Wrapper to call load_utags as a work function + * + */ +void load_work_func(struct work_struct *work) +{ + struct ctrl *ctrl = container_of(work, struct ctrl, load_work); + + ctrl->head = load_utags(&ctrl->main); + complete(&ctrl->load_comp); +} + +static int full_utag_name(struct proc_node *pnode, char *tag) +{ + int i, subdir, blen; + char *subdir_names[UTAG_DEPTH]; + struct ctrl *ctrl = pnode->ctrl; + struct proc_dir_entry *parent; + struct dir_node *c, *dir_node; + + *tag = 0; + for (subdir = 0, parent = pnode->dir; parent;) { + dir_node = NULL; + list_for_each_entry(c, &ctrl->dir_list, entry) { + if (c->dir == parent) { + dir_node = c; + break; + } + } + + if (!dir_node) + break; + + pr_debug("dir [%s] has %sparent\n", dir_node->name, + dir_node->parent == ctrl->root ? "no " : ""); + + subdir_names[subdir++] = dir_node->name; + parent = dir_node->parent; + } + + pr_debug("utag consists of %d subdirs\n", subdir); + + /* apply subdirs in opposite order */ + for (blen = 0, i = subdir - 1; i >= 0; i--) + blen += scnprintf(tag + blen, MAX_UTAG_NAME - blen, + "%s%s", subdir_names[i], i ? "/" : ""); + + if (pnode->type[0] != 0) + /* top off with type strings */ + blen += scnprintf(tag + blen, MAX_UTAG_NAME - blen, + ":%s", pnode->type); + + pr_debug("full name [%s](%d) has %d subdirs\n", tag, blen, subdir); + + return blen; +} + +static int check_utag_range(char *tag, struct utag *head, char *data, + size_t count) +{ + char rtag[MAX_UTAG_NAME]; + struct utag *range; + char *buf, *ptr, *tok; + size_t check; + bool found = false; + size_t len, data_len; + + /* copy utag name and append .range */ + strlcpy(rtag, tag, MAX_UTAG_NAME); + tok = strnchr(rtag, MAX_UTAG_NAME, ':'); + if (tok) + *tok = 0; + if (strlcat(rtag, "/.range", MAX_UTAG_NAME) > MAX_UTAG_NAME) { + pr_err("full name [%s] .range error\n", rtag); + return -EIO; + } + + pr_debug("utag range check [%s]\n", rtag); + + /* walk the thawed list to find the range */ + range = find_first_utag(head, rtag); + if (!range) { + pr_debug("full name [%s] no .range\n", rtag); + return 0; + } + + if (!range->size) { + pr_debug("full name [%s] .range not set\n", rtag); + return 0; + } + + /* check sizes and termination of the data */ + check = strnlen(range->payload, range->size); + if (check == range->size) { + pr_err("range payload for [%s] is not null terminated\n", rtag); + return -EIO; + } + + if (count > (check+1)) { + pr_err("data for [%s] is not allowed %zu > %zu\n", tag, count, check+1); + return -EIO; + } + + data_len = strnlen(data, count); + if (count == data_len) { + pr_err("data for [%s] not a string\n", tag); + return -EIO; + } + + /* make local copy of .range payload to tokenize */ + buf = ptr = kzalloc(range->size + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy(buf, range->payload, range->size); + + while (ptr && !found) { + tok = strsep(&ptr, ","); + len = strnlen(tok, MAX_UTAG_SIZE); + pr_debug("range value [%s] for [%s]\n", tok, rtag); + /* we can skip right away if length does not match */ + if (data_len != len) + continue; + if (!strncmp(tok, data, len)) + found = true; + } + kfree(buf); + + /* if ptr is still NULL we did not find a match */ + if (!found) { + pr_err("data [%s] out of range @[%s]\n", data, rtag); + return -EINVAL; + } + pr_debug("data [%s] in range\n", data); + + return 0; +} + +static int replace_first_utag(struct utag *head, char *name, + void *payload, size_t size) +{ + struct utag *utag; + void *oldpayload; + + /* search for the first occurrence of specified type of tag */ + utag = find_first_utag(head, name); + if (!utag) + return 0; + + oldpayload = utag->payload; + if (utag->flags & UTAG_FLAG_PROTECTED) { + pr_err("protected utag %s\n", name); + return -EIO; + } + + utag->payload = kzalloc(size + 1, GFP_KERNEL); + if (!utag->payload) { + utag->payload = oldpayload; + return -EIO; + } + + memcpy(utag->payload, payload, size); + utag->size = size; + kfree(oldpayload); + return 0; +} + +static int store_utags(struct ctrl *ctrl, struct utag *tags) +{ + size_t written; + size_t tags_size; + char *datap = NULL; + int rc = 0; + struct blkdev *cb = &ctrl->main; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) + mm_segment_t fs; + fs = get_fs(); + set_fs(KERNEL_DS); +#endif + + pr_debug("[%s] utags partition blk_sz=%zu\n", ctrl->dir_name, cb->size); + + datap = freeze_tags(cb->size, tags, &tags_size); + if (!datap) { + rc = -EIO; + goto out; + } + + if (open_utags(cb)) { + rc = -EIO; + goto err_free; + } + + written = kernel_write_stub(cb, datap, tags_size); + if (written < tags_size) { + pr_err("failed to write file (%s), rc=%zu\n", + cb->name, written); + rc = -EIO; + } + + /* Only try to use backup partition if it is configured */ + if (ctrl->backup.name) { + cb = &ctrl->backup; + if (open_utags(cb)) { + rc = -EIO; + goto err_free; + } + + written = kernel_write_stub(cb, datap, tags_size); + if (written < tags_size) { + pr_err("failed to write file (%s), rc=%zu\n", + cb->name, written); + rc = -EIO; + } + } + +err_free: + vfree(datap); +out: +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) + set_fs(fs); +#endif + return rc; +} + +/* + * Wrappers to call store_utags as a work function + * + */ +void store_work_func(struct work_struct *work) +{ + int rc; + struct ctrl *ctrl = container_of(work, struct ctrl, store_work); + + rc = store_utags(ctrl, ctrl->head); + if (rc) + pr_err("error storing utags partition\n"); + ctrl->store_work_result = rc; + complete(&ctrl->store_comp); +} + +static int read_utag(struct seq_file *file, void *v) +{ + int i, error; + char utag_name[MAX_UTAG_NAME]; + uint8_t *ptr; + struct utag *tags = NULL; + struct utag *tag = NULL; + struct proc_node *proc = (struct proc_node *)file->private; + struct ctrl *ctrl = proc->ctrl; + int rc = 0; + + mutex_lock(&ctrl->access_lock); + queue_work(ctrl->load_queue, &ctrl->load_work); + wait_for_completion(&ctrl->load_comp); + tags = ctrl->head; + if (NULL == tags) { + pr_err("load utags error\n"); + mutex_unlock(&ctrl->access_lock); + return -EFAULT; + } + + /* traverse back all parent directories up to root */ + error = full_utag_name(proc, utag_name); + if (!error) { + seq_puts(file, "cannot find utag associated with this file\n"); + rc = -EINVAL; + goto free_tags_exit; + } + + tag = find_first_utag(tags, utag_name); + if (NULL == tag) { + seq_printf(file, "utag [%s] not found\n", utag_name); + rc = -EINVAL; + goto free_tags_exit; + } + + switch (proc->mode) { + case OUT_ASCII: + seq_printf(file, "%s", (char *)tag->payload); + break; + case OUT_RAW: + ptr = (uint8_t *) tag->payload; + for (i = 0; i < tag->size; i++) + seq_printf(file, "%02X", *(ptr + i)); + break; + case OUT_TYPE: + if (*(char *)proc->type != 0) + seq_printf(file, "%s", (char *)proc->type); + break; + } + seq_puts(file, "\n"); + +free_tags_exit: + free_tags(tags); + mutex_unlock(&ctrl->access_lock); + return rc; +} + +static ssize_t write_utag(struct file *file, const char __user *buffer, + size_t count, loff_t *pos) +{ + int i, error; + char *payload, utag[MAX_UTAG_NAME]; + struct utag *tags = NULL; + struct inode *inode = file_inode(file); + struct proc_node *proc = PDE_DATA(inode); + struct ctrl *ctrl = proc->ctrl; + size_t lco, length = count; + + pr_debug("%zu bytes write attempt to [%s](%d)\n", + count, proc->name, proc->mode); + if (OUT_TYPE == proc->mode) { + return -EINVAL; + } + + if (MAX_UTAG_SIZE < count) { + pr_err("error utag too big %zu\n", count); + return -EINVAL; + } + /* allocate an extra byte in case we need to add null-byte */ + payload = kzalloc(count + 1, GFP_KERNEL); + if (!payload) + return -ENOMEM; + + if (copy_from_user(payload, buffer, count)) { + pr_err("user copy error\n"); + count = -EIO; + goto free_temp_exit; + } + + lco = count - 1; /* last character offset */ + if (payload[lco] == '\n') { + payload[lco] = 0; + /* update offset only if input has anything else, but '\n' */ + if (lco) + lco--; + length--; + pr_debug("replace trailing newline with null-byte\n"); + } + /* make sure ASCII input is properly terminated */ + if (proc->mode == OUT_ASCII) { + for (i = 0; i <= lco; i++) + if (payload[i] == 0) + break; + if (i > lco) { + payload[++lco] = 0; + length++; + pr_debug("add trailing null-byte\n"); + } + } + + mutex_lock(&ctrl->access_lock); + queue_work(ctrl->load_queue, &ctrl->load_work); + wait_for_completion(&ctrl->load_comp); + tags = ctrl->head; + if (NULL == tags) { + pr_err("[%s] load error\n", ctrl->dir_name); + count = -EIO; + goto free_temp_exit; + } + + if (ctrl->lock) { + pr_err("[%s] [%s] is locked\n", proc->name, ctrl->dir_name); + count = -EACCES; + goto free_tags_exit; + } + + /* traverse back all parent directories up to root */ + error = full_utag_name(proc, utag); + if (!error) { + pr_err("cannot find utag associated with this file\n"); + count = -EIO; + goto free_tags_exit; + } + + /* check if this utag has .range child only for hwtags */ + if (ctrl->hwtag && length) { + error = check_utag_range(utag, tags, payload, length); + if (error) { + count = -EINVAL; + goto free_tags_exit; + } + } + + error = replace_first_utag(tags, utag, payload, length); + if (error) { + pr_err("error storing [%s] new payload\n", utag); + count = -EIO; + goto free_tags_exit; + } + + queue_work(ctrl->store_queue, &ctrl->store_work); + wait_for_completion(&ctrl->store_comp); + if (ctrl->store_work_result) + count = ctrl->store_work_result; +free_tags_exit: + free_tags(tags); +free_temp_exit: + kfree(payload); + mutex_unlock(&ctrl->access_lock); + return count; +} + +static int rebuild_utags_directory(struct ctrl *ctrl) +{ + clear_utags_directory(ctrl); + return build_utags_directory(ctrl); +} + +/* + * Process delete file request. Check for existing utag, + * delete it, save utags, update proc fs +*/ + +static ssize_t delete_utag(struct file *file, const char __user *buffer, + size_t count, loff_t *pos) +{ + char *pattern, expendable[MAX_UTAG_NAME]; + struct utag *tags, *cur, *next; + struct inode *inode = file_inode(file); + struct ctrl *ctrl = PDE_DATA(inode); + + if ((MAX_UTAG_NAME < count) || (0 == count)) { + pr_err("invalid utag name %zu\n", count); + return -EIO; + } + + if (copy_from_user(expendable, buffer, count)) { + pr_err("user copy error\n"); + return -EFAULT; + } + + /* payload has input string plus \n. Replace \n with \0 */ + expendable[count-1] = 0; + if (!validate_name(expendable, count-1)) { + pr_err("invalid format %s\n", expendable); + return -EINVAL; + } + + if (check_path(expendable, count-1)) + return -EINVAL; + + mutex_lock(&ctrl->access_lock); + queue_work(ctrl->load_queue, &ctrl->load_work); + wait_for_completion(&ctrl->load_comp); + tags = ctrl->head; + if (NULL == tags) { + pr_err("[%s] load error\n", ctrl->dir_name); + mutex_unlock(&ctrl->access_lock); + return -EINVAL; + } + + if (ctrl->lock) { + pr_err("[%s] [%s] is locked\n", expendable, ctrl->dir_name); + count = -EACCES; + goto just_leave; + } + + cur = find_first_utag(tags, expendable); + if (!cur) { + pr_err("cannot find utag %s\n", expendable); + count = -EINVAL; + goto just_leave; + } + + /* update pointers */ + cur->prev->next = cur->next; + cur->next->prev = cur->prev; + kfree(cur); + pr_debug("deleted utag [%s]\n", expendable); + + /* remove all utags beneath */ + for (cur = tags->next; cur->next;) { + pattern = strnstr(cur->name, expendable, MAX_UTAG_NAME); + /* any subutags will start with the suffix followed by a '/' */ + if ((pattern == cur->name) && (cur->name[count-1] == '/')) { + pr_debug("deleting utag [%s]\n", cur->name); + next = cur->next; + cur->prev->next = cur->next; + cur->next->prev = cur->prev; + kfree(cur); + cur = next; + continue; + } + cur = cur->next; + } + + /* Store changed partition */ + queue_work(ctrl->store_queue, &ctrl->store_work); + wait_for_completion(&ctrl->store_comp); + if (ctrl->store_work_result) + count = ctrl->store_work_result; + rebuild_utags_directory(ctrl); +just_leave: + free_tags(tags); + mutex_unlock(&ctrl->access_lock); + return count; +} + +static int lock_show(struct seq_file *file, void *v) +{ + struct ctrl *ctrl = (struct ctrl *)file->private; + + if (!ctrl) { + pr_err("no control data set\n"); + return -EIO; + } + + seq_printf(file, "%u\n", ctrl->lock); + return 0; +} + +/* Check utag name againts valid range saved in vld utag payload */ + +static int check_hwtag(struct ctrl *ctrl, char *attr, struct utag *vld) +{ + +char *buf, *ptr, *tok; +bool found = false; +size_t len, alen; + + if (!vld || !attr) { + pr_debug("no validation tag or data\n"); + return 0; + } + + if (!vld->size) { + pr_err("allow [%s] because [%s] is empty\n", attr, vld->name); + return 0; + } + + alen = strnlen(attr, MAX_UTAG_NAME); + pr_debug("name check for [%s] length [%zu]\n", attr, alen); + /* make local copy of validation payload to tokenize */ + buf = ptr = kzalloc(vld->size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy(ptr, vld->payload, vld->size); + + while (ptr && !found) { + tok = strsep(&ptr, ","); + /* *ptr is updated to show past the token */ + len = strnlen(tok, MAX_UTAG_NAME); + if (alen > len) + continue; + pr_debug("attr value [%s] len [%zu]\n", tok, len); + if (!strncmp(tok, attr, len)) + found = true; + } + kfree(buf); + + /* if ptr is still NULL we did not find a match */ + if (!found) { + pr_err("[%s] is not allowed\n", attr); + return -EINVAL; + } + + return 0; +} + +/* + * Process new file request. Check for existing utag, + * add empty new utag, save utags and add file interface +*/ + +static ssize_t new_utag(struct file *file, const char __user *buffer, + size_t count, loff_t *pos) +{ + struct inode *inode = file_inode(file); + struct ctrl *ctrl = PDE_DATA(inode); + struct utag *tags, *cur; + struct proc_dir_entry *parent = NULL; + char tree[UTAG_DEPTH][MAX_UTAG_NAME]; + char expendable[MAX_UTAG_NAME], *names[UTAG_DEPTH], *type = NULL; + int error, i, num_names; + size_t ret = count; + + if ((MAX_UTAG_NAME < count) || (0 == count)) { + pr_err("invalid utag name %zu\n", count); + return -EIO; + } + + if (copy_from_user(expendable, buffer, count)) { + pr_err("user copy error\n"); + return -EFAULT; + } + /* payload has input string plus \n. Replace \n with \0 */ + expendable[count-1] = 0; + if (!validate_name(expendable, count-1)) { + pr_err("invalid format %s\n", expendable); + return -EFAULT; + } + + if (check_path(expendable, count-1)) + return -EFAULT; + + if (no_show_tag(expendable)) { + pr_err("[%s] reserved name %s\n", ctrl->dir_name, expendable); + return -EFAULT; + } + + pr_debug("adding [%s] utag\n", expendable); + + mutex_lock(&ctrl->access_lock); + queue_work(ctrl->load_queue, &ctrl->load_work); + wait_for_completion(&ctrl->load_comp); + tags = ctrl->head; + if (NULL == tags) { + pr_err("[%s] load error\n", ctrl->dir_name); + mutex_unlock(&ctrl->access_lock); + return -EFAULT; + } + + if (ctrl->lock) { + pr_err("[%s] [%s] is locked\n", expendable, ctrl->dir_name); + ret = -EACCES; + goto just_leave; + } + + /* Ignore request if utag name already in use */ + cur = find_first_utag(tags, expendable); + if (NULL != cur) { + pr_err("cannot create [%s]; already in use\n", expendable); + ret = -EINVAL; + goto just_leave; + } + + + num_names = full_split(expendable, names, &type); + if (num_names == 0) { + pr_err("failed to split path\n"); + ret = -EINVAL; + goto just_leave; + } + + /* check if we are trying to add an attribute file */ + /* check every level of the path for validity */ + for (i = 0; i < num_names; i++) { + /* build name for each sublevel and add */ + /* slash to all sublevels except first */ + + scnprintf(tree[i], MAX_UTAG_NAME, "%s%s%s", i ? tree[i-1] : "", + i ? "/" : "", names[i]); + pr_debug("prepared subdir [%s] as [%s]\n", names[i], tree[i]); + + /* only check features for files that do not have a .*/ + if (ctrl->hwtag && '.' != names[i][0]) + if (check_hwtag(ctrl, tree[i], ctrl->features)) { + ret = -EINVAL; + goto just_leave; + } + } + + /* only check attributes for last file starting with a . */ + /* do not check for attributes in the root */ + if (ctrl->hwtag && num_names != 1 && '.' == names[num_names-1][0]) + if (check_hwtag(ctrl, names[num_names-1], ctrl->attrib)) { + ret = -EINVAL; + goto just_leave; + } + + for (i = 0; i < num_names; i++) { + error = add_utag_tail(tags, tree[i], type); + if (error == -EEXIST) { + struct dir_node *dnode; + /* need to update parent to ensure proper hierarchy */ + dnode = find_dir_node(ctrl, tree[i]); + parent = dnode->dir; + continue; + } + + /* create utag dir for every part of utag name */ + parent = proc_utag_dir(ctrl, names[i], tree[i], type, + true, parent); + if (IS_ERR(parent)) + break; + } + + walk_dir_nodes(ctrl); + walk_proc_nodes(ctrl); + + /* Store changed partition */ + queue_work(ctrl->store_queue, &ctrl->store_work); + wait_for_completion(&ctrl->store_comp); + if (ctrl->store_work_result) + ret = ctrl->store_work_result; +just_leave: + free_tags(tags); + mutex_unlock(&ctrl->access_lock); + return ret; +} + +static int reload_show(struct seq_file *file, void *v) +{ + struct ctrl *ctrl = (struct ctrl *)file->private; + + if (!ctrl) + pr_err("no control data set\n"); + else + seq_printf(file, "%c\n", ctrl->reload); + + pr_debug("[%s] %c\n", ctrl->dir_name, ctrl->reload); + return 0; +} + +static ssize_t reload_write(struct file *file, const char __user *buffer, + size_t count, loff_t *pos) +{ + struct inode *inode = file_inode(file); + struct ctrl *ctrl = PDE_DATA(inode); + + + /* only single character input plus new line */ + if (2 < count) { + pr_err("invalid command length\n"); + return -EIO; + } + + mutex_lock(&ctrl->access_lock); + + if (UTAG_STATUS_LOADED == ctrl->reload) { + pr_info("[%s] (pid %i) [%s] already loaded\n", + current->comm, current->pid, ctrl->dir_name); + mutex_unlock(&ctrl->access_lock); + return count; + } + + if (copy_from_user(&ctrl->reload, buffer, 1)) { + pr_err("user copy error\n"); + mutex_unlock(&ctrl->access_lock); + return -EFAULT; + } + + pr_info("[%s] (pid %i) [%s] %c\n", + current->comm, current->pid, ctrl->dir_name, ctrl->reload); + + if (UTAG_STATUS_RELOAD == ctrl->reload) { + if (rebuild_utags_directory(ctrl)) + ctrl->reload = UTAG_STATUS_FAILED; + } + + mutex_unlock(&ctrl->access_lock); + return count; +} + +static int lock_open(struct inode *inode, struct file *file) +{ + return single_open(file, lock_show, PDE_DATA(inode)); +} + +static int reload_open(struct inode *inode, struct file *file) +{ + return single_open(file, reload_show, PDE_DATA(inode)); +} + +static int partition_open(struct inode *inode, struct file *file) +{ + return single_open(file, read_utag, PDE_DATA(inode)); +} + +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +static const struct proc_ops reload_fops = { + .proc_open = reload_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = reload_write, +}; +#else +static const struct file_operations reload_fops = { + .owner = THIS_MODULE, + .open = reload_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = reload_write, +}; +#endif + +static int build_utags_directory(struct ctrl *ctrl) +{ + struct proc_dir_entry *parent; + struct utag *tags, *cur; + int rc = 0; + + /* try to load utags from primary partition */ + queue_work(ctrl->load_queue, &ctrl->load_work); + wait_for_completion(&ctrl->load_comp); + tags = ctrl->head; + if (NULL == tags) { + pr_err("[%s] load error\n", ctrl->dir_name); + return -EIO; + } + /* skip utags head */ + cur = tags->next; + while (1) { + int i, num_names; + char expendable[MAX_UTAG_NAME]; + char *type, *names[UTAG_DEPTH]; + char path[MAX_UTAG_NAME]; + + memset(path, 0, MAX_UTAG_NAME); + + /* hook to skip special tags if we have any*/ + if (no_show_tag(cur->name)) { + cur = cur->next; + continue; + } + + /* skip utags tail */ + if (cur->next == NULL) + break; + + memcpy(expendable, cur->name, MAX_UTAG_NAME); + parent = NULL, type = NULL; + num_names = full_split(expendable, names, &type); + + for (i = 0; i < num_names; i++) { + scnprintf(path + strlen(path), + MAX_UTAG_NAME - strlen(path), + "%s%s", i ? "/" : "", names[i]); + pr_debug("creating subdir %s as %s\n", names[i], path); + parent = proc_utag_dir(ctrl, names[i], path, type, + /* populate only utag dir */ + (i == (num_names - 1)) ? true : false, + parent); + if (IS_ERR(parent)) { + rc = -EINVAL; + goto stop_building_utags; + } + } + + cur = cur->next; + } + +stop_building_utags: + + walk_dir_nodes(ctrl); + walk_proc_nodes(ctrl); + + free_tags(tags); + if (!rc) + ctrl->reload = UTAG_STATUS_LOADED; + return rc; +} + +#ifdef CONFIG_OF +static char *bootargs_str; + +static int utag_get_bootarg(char *key, char **value, char *prop, char *spl_flag) +{ + const char *bootargs_ptr = NULL; + char *idx = NULL; + char *kvpair = NULL; + int err = 1; + struct device_node *n = of_find_node_by_path("/chosen"); + size_t bootargs_ptr_len = 0; + + if (n == NULL) + goto err; + + if (of_property_read_string(n, prop, &bootargs_ptr) != 0) + goto err_putnode; + + bootargs_ptr_len = strlen(bootargs_ptr); + if (!bootargs_str) { + /* Following operations need a non-const version of bootargs */ + bootargs_str = kzalloc(bootargs_ptr_len + 1, GFP_KERNEL); + if (!bootargs_str) + goto err_putnode; + } + strlcpy(bootargs_str, bootargs_ptr, bootargs_ptr_len + 1); + + idx = strnstr(bootargs_str, key, strlen(bootargs_str)); + if (idx) { + kvpair = strsep(&idx, " "); + if (kvpair) + if (strsep(&kvpair, "=")) { + *value = strsep(&kvpair, spl_flag); + if (*value) + err = 0; + } + } + +err_putnode: + of_node_put(n); +err: + return err; +} + +#ifdef CONFIG_BOOT_CONFIG +static char bootdevice_name[256]; +static int utags_get_bootdevice_from_bootconfig(char **bootconfig_val) +{ + static char bootdevice_init = 0; + int rc = 0; + + if (bootdevice_init) { + *bootconfig_val = bootdevice_name; + return 0; + } + + rc = utag_get_bootarg("androidboot.bootdevice=", bootconfig_val, "mmi,bootconfig", "\n"); + if (!rc && *bootconfig_val) { + strncpy(bootdevice_name, *bootconfig_val, strlen(*bootconfig_val)); + bootdevice_init = 1; + } + return rc; +} +#endif + +#define PLATFORM_PATH "/dev/block/platform/soc/" + +static void utags_bootdevice_expand(const char **name_ptr, const char *name) +{ + int rc = -1; + size_t max_len = strlen(name); + char *bootdevice = NULL; + char *replace, *suffix, *expanded; + + if (name == NULL) + return; +#ifndef CONFIG_BOOT_CONFIG + rc = utag_get_bootarg("androidboot.bootdevice=", &bootdevice, "bootargs", " "); + if (rc || !bootdevice) + goto need_no_expansion; +#else + rc = utags_get_bootdevice_from_bootconfig(&bootdevice); + + if (rc || !bootdevice) { + rc = utag_get_bootarg("androidboot.bootdevice=", &bootdevice, "bootargs", " "); + if (rc || !bootdevice) + goto need_no_expansion; + } +#endif + + replace = strnstr(name, "bootdevice", max_len); + suffix = strnstr(name, "by-name", max_len); + if (!replace || !suffix) + goto need_no_expansion; + + max_len = strlen(PLATFORM_PATH) + strlen(bootdevice) + strlen(suffix); + max_len += 2; /* account on '/' and null termination */ + + expanded = kzalloc(max_len, GFP_KERNEL); + if (!expanded) { + pr_err("cannot expand bootdevice\n"); + goto need_no_expansion; + } + + snprintf(expanded, max_len, + PLATFORM_PATH "%s/%s", bootdevice, suffix); + pr_info("path expanded to: %s(%zu)\n", expanded, max_len); + *name_ptr = (const char *)expanded; + + return; + +need_no_expansion: + *name_ptr = kstrdup(name, GFP_KERNEL); +} + +static int utags_dt_init(struct platform_device *pdev) +{ + int rc; + const char *path_ptr; + struct device_node *node = pdev->dev.of_node; + struct ctrl *ctrl; + + ctrl = dev_get_drvdata(&pdev->dev); + rc = of_property_read_string(node, "mmi,main-utags", &path_ptr); + if (rc) { + pr_err("storage path not provided\n"); + return -EIO; + } + utags_bootdevice_expand(&ctrl->main.name, path_ptr); + + rc = of_property_read_string(node, "mmi,backup-utags", &path_ptr); + if (rc) + pr_info("backup storage path not provided\n"); + utags_bootdevice_expand(&ctrl->backup.name, path_ptr); + + ctrl->dir_name = DEFAULT_ROOT; + rc = of_property_read_string(node, "mmi,dir-name", &ctrl->dir_name); + if (!rc) + pr_debug("utag dir override %s\n", ctrl->dir_name); + + return 0; +} +#else +static int utags_dt_init(struct platform_device *pdev) { return -EINVAL; } +#endif + +static void clear_utags_directory(struct ctrl *ctrl) +{ + struct proc_node *node, *s = NULL; + struct dir_node *dir_node, *c = NULL; + + list_for_each_entry_safe(dir_node, c, &ctrl->dir_list, entry) { + if (dir_node->parent != ctrl->root) + continue; + /* remove whole subtree of first level subdir */ + remove_proc_subtree(dir_node->name, ctrl->root); + + pr_debug("removing subtree [%s]\n", dir_node->name); + + list_del(&dir_node->entry); + kfree(dir_node); + } + + /* all subtrees removed; just free memory */ + list_for_each_entry_safe(dir_node, c, &ctrl->dir_list, entry) { + list_del(&dir_node->entry); + kfree(dir_node); + } + + /* nothing left in procfs except reload file; free memory */ + list_for_each_entry_safe(node, s, &ctrl->node_list, entry) { + list_del(&node->entry); + kfree(node); + } +} + + +#define UTAGS_QNAME_SIZE 16 +static int utags_probe(struct platform_device *pdev) +{ + int rc; + struct ctrl *ctrl; + char buf[UTAGS_QNAME_SIZE]; + + ctrl = devm_kzalloc(&pdev->dev, sizeof(struct ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + INIT_LIST_HEAD(&ctrl->dir_list); + INIT_LIST_HEAD(&ctrl->node_list); + ctrl->pdev = pdev; + ctrl->reload = UTAG_STATUS_NOT_READY; + mutex_init(&ctrl->access_lock); + + init_completion(&ctrl->load_comp); + init_completion(&ctrl->store_comp); + INIT_WORK(&ctrl->load_work, load_work_func); + INIT_WORK(&ctrl->store_work, store_work_func); + + dev_set_drvdata(&pdev->dev, ctrl); + + rc = utags_dt_init(pdev); + if (rc) + return -EIO; + + scnprintf(buf, UTAGS_QNAME_SIZE, "%s_load", ctrl->dir_name); + ctrl->load_queue = create_workqueue(buf); + if (!ctrl->load_queue) { + pr_err("Failed to create workqueue %s\n", buf); + return -ENOMEM; + } + + scnprintf(buf, UTAGS_QNAME_SIZE, "%s_store", ctrl->dir_name); + ctrl->store_queue = create_workqueue(buf); + if (!ctrl->store_queue) { + destroy_workqueue(ctrl->load_queue); + pr_err("Failed to create workqueue %s\n", buf); + return -ENOMEM; + } + + ctrl->root = proc_mkdir_data(ctrl->dir_name, 0771, NULL, NULL); + if (!ctrl->root) { + destroy_workqueue(ctrl->load_queue); + destroy_workqueue(ctrl->store_queue); + pr_err("Failed to create dir entry\n"); + return -EIO; + } + + if (!strncmp(ctrl->dir_name, HW_ROOT, sizeof(HW_ROOT))) + ctrl->hwtag = 1; + + if (!proc_create_data("reload", 0660, ctrl->root, &reload_fops, ctrl)) { + pr_err("Failed to create reload entry\n"); + destroy_workqueue(ctrl->load_queue); + destroy_workqueue(ctrl->store_queue); + remove_proc_subtree(ctrl->dir_name, NULL); + return -EIO; + } + + if (proc_utag_util(ctrl)) { + pr_err("Failed to create util dir\n"); + destroy_workqueue(ctrl->load_queue); + destroy_workqueue(ctrl->store_queue); + remove_proc_subtree(ctrl->dir_name, NULL); + return -EFAULT; + } + + pr_info("Done [%s]\n", ctrl->dir_name); + return 0; +} + +static int utags_remove(struct platform_device *pdev) +{ + struct ctrl *ctrl = dev_get_drvdata(&pdev->dev); + + clear_utags_directory(ctrl); + remove_proc_subtree(ctrl->dir_name, NULL); + destroy_workqueue(ctrl->load_queue); + destroy_workqueue(ctrl->store_queue); + if (ctrl->main.filep) + filp_close(ctrl->main.filep, NULL); + if (ctrl->backup.filep) + filp_close(ctrl->backup.filep, NULL); + + if (ctrl->main.name) + kfree(ctrl->main.name); + if (ctrl->backup.name) + kfree(ctrl->backup.name); + + if (bootargs_str) { + kfree(bootargs_str); + bootargs_str = NULL; + } + + devm_kfree(&pdev->dev, ctrl); + dev_set_drvdata(&pdev->dev, NULL); + return 0; +} + +#ifdef CONFIG_OF +static struct of_device_id utags_match_table[] = { + { .compatible = "mmi,utags", + }, + {} +}; +#endif + +static struct platform_driver utags_driver = { + .probe = utags_probe, + .remove = utags_remove, + .driver = { + .name = DRVNAME, + .bus = &platform_bus_type, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(utags_match_table), + }, +}; + +static int __init utags_init(void) +{ + return platform_driver_register(&utags_driver); +} + +static void __exit utags_exit(void) +{ + platform_driver_unregister(&utags_driver); +} + +late_initcall(utags_init); +module_exit(utags_exit); + +MODULE_LICENSE("GPL"); +#if KERNEL_VERSION(5, 4, 0) <= LINUX_VERSION_CODE +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif +MODULE_AUTHOR("Motorola Mobility LLC"); +MODULE_DESCRIPTION("Configuration module"); diff --git a/drivers/mmi_annotate/Android.mk b/drivers/mmi_annotate/Android.mk new file mode 100644 index 000000000000..2337b1b2d73d --- /dev/null +++ b/drivers/mmi_annotate/Android.mk @@ -0,0 +1,12 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_annotate.ko +LOCAL_MODULE_TAGS := optional +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/mmi_annotate/Kbuild b/drivers/mmi_annotate/Kbuild new file mode 100644 index 000000000000..f554a4e253b2 --- /dev/null +++ b/drivers/mmi_annotate/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += mmi_annotate.o \ No newline at end of file diff --git a/drivers/mmi_annotate/Makefile b/drivers/mmi_annotate/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/mmi_annotate/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/mmi_annotate/mmi_annotate.c b/drivers/mmi_annotate/mmi_annotate.c new file mode 100644 index 000000000000..f7a496facf25 --- /dev/null +++ b/drivers/mmi_annotate/mmi_annotate.c @@ -0,0 +1,367 @@ +/* + * Copyright (C) 2018 Motorola Mobility LLC + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if KERNEL_VERSION(5, 10, 0) > LINUX_VERSION_CODE +#include +#endif +#include +#include +#include +#include +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +#if IS_ENABLED(CONFIG_QCOM_MINIDUMP) +#include +#endif +#endif + +#define MAX_USER_STR 1024 +#define DEFAULT_MEM_SIZE 4096 +#define PERSIST_MAGIC_NUM 0xABCD1234 + +struct platform_data { + phys_addr_t mem_address; + size_t mem_size; +}; + +struct mem_data_t { + unsigned int size; + size_t cur_off; + unsigned char *contents; +}; + +struct persist_data_t { + unsigned int magic_num; + size_t size; + size_t cur_off; + unsigned char contents[]; +}; + +static DEFINE_MUTEX(persist_lock); +static DEFINE_MUTEX(mem_lock); + +static struct proc_dir_entry *procfs_file; +static struct persist_data_t *persist_data; +static struct mem_data_t mem_data; +static int persist_unsupported = 0; + +static int mmi_annotate_seq_show(struct seq_file *f, void *ptr) +{ + mutex_lock(&mem_lock); + if (mem_data.contents && mem_data.cur_off > 0) { + seq_printf(f, "%s", mem_data.contents); + } else { + seq_printf(f, "No annotated data.\n"); + } + mutex_unlock(&mem_lock); + + return 0; +} + +static int mmi_annotate_open(struct inode *inode, struct file *file) +{ + return single_open(file, mmi_annotate_seq_show, inode->i_private); +} + +static ssize_t mmi_annotate_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset) +{ + char buffer[MAX_USER_STR]; + const size_t maxlen = sizeof(buffer) - 1; + + memset(buffer, 0, sizeof(buffer)); + if (copy_from_user(buffer, buf, count > maxlen ? maxlen : count)) + return -EFAULT; + + if (!persist_unsupported) + mmi_annotate_persist("%s", buffer); + else + mmi_annotate("%s", buffer); + + return count; +} + +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +static const struct proc_ops mmi_annotate_operations = { + .proc_open = mmi_annotate_open, + .proc_read = seq_read, + .proc_write = mmi_annotate_write, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +#else +static const struct file_operations mmi_annotate_operations = { + .open = mmi_annotate_open, + .read = seq_read, + .write = mmi_annotate_write, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0) +static void* mmi_annotate_ram_vmap(phys_addr_t start, size_t size) +{ + struct page **pages; + phys_addr_t page_start; + unsigned int page_count; + unsigned int i; + void *vaddr; + + page_start = start - offset_in_page(start); + page_count = DIV_ROUND_UP(size + offset_in_page(start), PAGE_SIZE); + + pages = kmalloc_array(page_count, sizeof(struct page *), GFP_KERNEL); + if (!pages) { + pr_err("%s: Failed to allocate array for %u pages\n", + __func__, page_count); + return NULL; + } + + for (i = 0; i < page_count; i++) { + phys_addr_t addr = page_start + i * PAGE_SIZE; + pages[i] = pfn_to_page(addr >> PAGE_SHIFT); + } + vaddr = vmap(pages, page_count, VM_MAP, PAGE_KERNEL); + kfree(pages); + + if (!vaddr) { + pr_err("%s: Failed to vmap the %d pages\n", + __func__, page_count); + return NULL; + } + + /* + * Since vmap() uses page granularity, we must add the offset + * into the page here, to get the byte granularity address + * into the mapping to represent the actual "start" location. + */ + return vaddr + offset_in_page(start); +} +#endif + +static int mmi_annotate_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct platform_data *pdata; + struct resource res; + struct device_node *node; +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +#if IS_ENABLED(CONFIG_QCOM_MINIDUMP) + struct md_region md_entry; +#endif +#endif + int err = 0; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + err = -ENOMEM; + goto err; + } + + /* Get reserved memory region from Device-tree */ + node = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!node) { + dev_err(dev, "No %s specified\n", "memory-region"); + goto err; + } + + err = of_address_to_resource(node, 0, &res); + of_node_put(node); + if (err) { + dev_err(dev, "No memory address assigned to the region\n"); + goto err; + } + + pdata->mem_size = resource_size(&res); + pdata->mem_address = res.start; + + dev_info(dev, "size %zx", pdata->mem_size); + dev_info(dev, "addr %lx", (unsigned long)pdata->mem_address); + + if (pdata->mem_size < sizeof(struct persist_data_t)) { + dev_err(dev, "Mem size too small %zx/%zd\n", + pdata->mem_size, sizeof(struct persist_data_t)); + err = -ENOMEM; + goto err; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0) + persist_data = mmi_annotate_ram_vmap(pdata->mem_address, pdata->mem_size); +#else + persist_data = dma_remap(dev, NULL, pdata->mem_address, + pdata->mem_size, 0); +#endif + if (!persist_data) { + dev_err(dev, "Cannot remap buffer %pa size %zx\n", + &pdata->mem_address, pdata->mem_size); + err = -ENOMEM; + goto err; + } + + /* Allocate local memory region for non-persistent data */ + if(of_property_read_u32(dev->of_node, "mem-size", &mem_data.size)) { + mem_data.size = DEFAULT_MEM_SIZE; + dev_info(dev, "mem-size defaulting to %d", DEFAULT_MEM_SIZE); + } + else + dev_info(dev, "mem-size %x", mem_data.size); + + persist_unsupported = of_property_read_bool(dev->of_node, "persist_unsupported"); + if (persist_unsupported ) + dev_info(dev, "persist unsupported : 1"); + + mem_data.contents = kzalloc(mem_data.size, GFP_KERNEL); + if(!mem_data.contents) { + dev_err(dev, "Cannot allocate buffer of size %x\n", + mem_data.size); + err = -ENOMEM; + goto err; + } + + /* If persist data is valid, copy it to the local mem area and reset it */ + persist_data->size = pdata->mem_size - sizeof(struct persist_data_t); + if(persist_data->magic_num == PERSIST_MAGIC_NUM && + persist_data->cur_off <= persist_data->size) { + memcpy(mem_data.contents, + persist_data->contents, + persist_data->cur_off); + mem_data.cur_off += persist_data->cur_off; + } + else + persist_data->magic_num = PERSIST_MAGIC_NUM; + persist_data->cur_off = 0; + + /* Create the procfs file at /proc/driver/mmi_annotate */ + procfs_file = proc_create("driver/mmi_annotate", + 0444, NULL, &mmi_annotate_operations); + +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +#if IS_ENABLED(CONFIG_QCOM_MINIDUMP) + /*Register annotate to minidump */ + strlcpy(md_entry.name, "ANNOTATE", sizeof(md_entry.name)); + if (!persist_unsupported) { + md_entry.virt_addr = (uintptr_t)phys_to_virt(pdata->mem_address); + md_entry.phys_addr = pdata->mem_address; + md_entry.size = pdata->mem_size; + } else { + md_entry.virt_addr = (uintptr_t)mem_data.contents; + md_entry.phys_addr = virt_to_phys((void*)mem_data.contents); + md_entry.size = mem_data.size; + } + if (msm_minidump_add_region(&md_entry) < 0) + pr_err("Failed to add annotate in Minidump\n"); +#endif +#endif +err: + return err; +} + +int mmi_annotate(const char *fmt, ...) +{ + va_list args; + int len = 0; + char line_buf[512]; + + va_start(args, fmt); + len += vsnprintf(line_buf, sizeof(line_buf), fmt, args); + va_end(args); + + mutex_lock(&mem_lock); + if(mem_data.contents && mem_data.cur_off + len < mem_data.size) { + memcpy(mem_data.contents + mem_data.cur_off, line_buf, len); + mem_data.cur_off += len; + } + mutex_unlock(&mem_lock); + + return 0; +} +EXPORT_SYMBOL(mmi_annotate); + +int mmi_annotate_persist(const char *fmt, ...) +{ + va_list args; + int len = 0; + char line_buf[512]; + + va_start(args, fmt); + len += vsnprintf(line_buf, sizeof(line_buf), fmt, args); + va_end(args); + + if (!persist_unsupported) { + mutex_lock(&persist_lock); + if(persist_data && + persist_data->cur_off + len < persist_data->size) { + memcpy(persist_data->contents + persist_data->cur_off, + line_buf, len); + persist_data->cur_off += len; + } + mutex_unlock(&persist_lock); + } else { + mmi_annotate("%s", line_buf); + } + + return 0; +} +EXPORT_SYMBOL(mmi_annotate_persist); + +static int mmi_annotate_remove(struct platform_device *pdev) +{ + if (procfs_file) + remove_proc_entry("driver/mmi_annotate", NULL); + if(mem_data.contents) + kfree(mem_data.contents); + return 0; +} + +static const struct of_device_id mmi_annotate_match[] = { + { .compatible = "mmi,annotate" }, + {} +}; + +static struct platform_driver mmi_annotate_driver = { + .probe = mmi_annotate_probe, + .remove = mmi_annotate_remove, + .driver = { + .name = "mmi_annotate", + .of_match_table = mmi_annotate_match, + }, +}; + +static int mmi_annotate_init(void) +{ + return platform_driver_register(&mmi_annotate_driver); +} + +static void mmi_annotate_exit(void) +{ + platform_driver_unregister(&mmi_annotate_driver); +} + +module_init(mmi_annotate_init); +module_exit(mmi_annotate_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("MMI annotate"); diff --git a/drivers/mmi_info/Android.mk b/drivers/mmi_info/Android.mk new file mode 100644 index 000000000000..6146c8aacbae --- /dev/null +++ b/drivers/mmi_info/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_info.ko +LOCAL_MODULE_TAGS := optional +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_annotate.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/mmi_info/Kbuild b/drivers/mmi_info/Kbuild new file mode 100644 index 000000000000..4a3f1a0f3fac --- /dev/null +++ b/drivers/mmi_info/Kbuild @@ -0,0 +1,12 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +mmi_info-objs += mmi_storage_info.o +mmi_info-objs += mmi_ram_info.o +mmi_info-objs += mmi_unit_info.o +mmi_info-objs += mmi_boot_info.o +mmi_info-objs += mmi_info_main.o +obj-m += mmi_info.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_annotate/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/mmi_info/Makefile b/drivers/mmi_info/Makefile new file mode 100644 index 000000000000..ea7fc97f510a --- /dev/null +++ b/drivers/mmi_info/Makefile @@ -0,0 +1,16 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS := $(M)/../mmi_annotate/Module.symvers diff --git a/drivers/mmi_info/mmi_boot_info.c b/drivers/mmi_info/mmi_boot_info.c new file mode 100644 index 000000000000..36fec70fa114 --- /dev/null +++ b/drivers/mmi_info/mmi_boot_info.c @@ -0,0 +1,346 @@ +/* + * Copyright (C) 2009 Motorola, Inc. + * Copyright (C) 2012 Motorola Mobility. All rights reserved. + * Copyright (C) 2018 Motorola Mobility LLC + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_info.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) +#define PDE_DATA(x) pde_data(x) +#endif +/* + * EMIT_BOOTINFO and EMIT_BOOTINFO_STR are used to emit the bootinfo + * information for data provided via DEVICE TREE. + * + * The format for all bootinfo lines is "name : val" and these macros + * enforce that format. + * + * strname is the name printed in the name/val pair. + * name is the name of the function to call + * + * EMIT_BOOTINFO and EMIT_BOOTINFO_STR depend on buf and len to already + * be defined. + */ +#define EMIT_BOOTINFO(strname, fmt, name) \ + do { \ + seq_printf(m, strname ": " fmt "\n", \ + bi_##name()); \ + } while (0) + +#define EMIT_BOOTINFO_STR(strname, strval) \ + do { \ + if (strlen(strval) == 0) { \ + seq_printf(m, "%s: UNKNOWN\n", \ + strname); \ + } else { \ + seq_printf(m, "%s: %s\n", \ + strname, strval); \ + } \ + } while (0) + +/*-------------------------------------------------------------------------*/ + +#define BOOTREASON_MAX_LEN 64 +#define BOOTMODE_MAX_LEN 64 +static char bootreason[BOOTREASON_MAX_LEN]; +static char bootmode[BOOTMODE_MAX_LEN]; + + +static void mmi_bootarg_setup(void) +{ + char *s; + + if (mmi_get_bootarg("androidboot.bootreason=", &s) == 0) + strlcpy(bootreason, s, BOOTREASON_MAX_LEN); + + if (mmi_get_bootarg("androidboot.mode=", &s) == 0) + strlcpy(bootmode, s, BOOTMODE_MAX_LEN); +} + +/* + * powerup_reason contains the powerup reason provided by the ATAGs when + * the machine boots. + * + * Exported symbols: + * bi_powerup_reason() -- returns the powerup reason + */ +u32 bi_powerup_reason(void) +{ + return mmi_chosen_data.powerup_reason; +} +EXPORT_SYMBOL(bi_powerup_reason); + +#define EMIT_POWERUPREASON() \ + EMIT_BOOTINFO("POWERUPREASON", "0x%08x", powerup_reason) + +/* + * mbm_version contains the MBM version. + * mbm_loader_version contains the MBM loader version. + * mbm_version and mbm_loader_version default to 0 if they are + * not set. + * + * Exported symbols: + * bi_mbm_version() -- returns the MBM version + */ +u32 bi_mbm_version(void) +{ + return mmi_chosen_data.mbmversion; +} +EXPORT_SYMBOL(bi_mbm_version); + +#define EMIT_MBM_VERSION() \ + EMIT_BOOTINFO("MBM_VERSION", "0x%08x", mbm_version) + +/* + * boot_seq contains the boot sequence number. + * boot_seq default to 0 if it is not set. + * + * Exported symbols: + * bi_boot_seq() -- returns the boot seq + */ +u32 bi_boot_seq(void) +{ + return mmi_chosen_data.boot_seq; +} +EXPORT_SYMBOL(bi_boot_seq); + +#define EMIT_BOOT_SEQ() \ + EMIT_BOOTINFO("BOOT_SEQ", "%d", boot_seq) + +/* + * BL build signature a succession of lines of text each denoting + * build/versioning information for each bootloader component, + * as passed along from bootloader via ATAG_BL_BUILD_SIG(s) + */ + +#define MAX_BL_BUILD_SIG 20 +#define MAX_BLD_SIG_ITEM 20 +#define MAX_BLD_SIG_VALUE 80 + +struct bl_build_sig { + char item[MAX_BLD_SIG_ITEM]; + char value[MAX_BLD_SIG_VALUE]; +}; + +static unsigned int bl_build_sig_count; +static struct bl_build_sig bl_build_sigs[MAX_BL_BUILD_SIG]; + +static void convert_to_upper(char *str) +{ + while (*str) { + *str = toupper(*str); + str++; + } +} + +void bi_add_bl_build_sig(char *bld_sig) +{ + int pos; + char *value; + char *ptr; + + if (!bld_sig || (bl_build_sig_count >= MAX_BL_BUILD_SIG)) + return; + + value = (char *)memchr((void *)bld_sig, '=', MAX_BLD_SIG_ITEM); + if (!value) + return; + + pos = value - bld_sig; + + ptr = bl_build_sigs[bl_build_sig_count].item; + strlcpy(ptr, bld_sig, pos+1); + convert_to_upper(ptr); + + ptr = bl_build_sigs[bl_build_sig_count].value; + strlcpy(ptr, value+1, MAX_BLD_SIG_VALUE); + + bl_build_sig_count++; +} + +static void of_blsig(void) +{ + struct property *p; + struct device_node *n; + + /* Only do this one time once we find the sigs */ + if (bl_build_sig_count) + return; + + n = of_find_node_by_path("/chosen/mmi,bl_sigs"); + if (n == NULL) + return; + + for_each_property_of_node(n, p) + if (strcmp(p->name, "name")) + bi_add_bl_build_sig(p->value); + + of_node_put(n); +} + +#define EMIT_BL_BUILD_SIG() \ + do { \ + int i; \ + for (i = 0; i < bl_build_sig_count; i++) { \ + EMIT_BOOTINFO_STR(bl_build_sigs[i].item, \ + bl_build_sigs[i].value); \ + } \ + } while (0) + +/* System revision s global symbol exported by setup.c + * use wrapper to maintain coherent format with the other + * boot info elements + */ + +u32 bi_hwrev(void) +{ + return mmi_chosen_data.system_rev; +} +EXPORT_SYMBOL(bi_hwrev); + +#define EMIT_HWREV() \ + EMIT_BOOTINFO("HW_REV", "0x%04x", hwrev) + +/* Serial high and low are symbols exported by setup.c + * use wrapper to maintain coherent format with the other + * boot info elements + */ + +static u64 bi_serial(void) +{ + return ((u64)mmi_chosen_data.system_serial_high << 32) + | (u64)mmi_chosen_data.system_serial_low; +} + +#define EMIT_SERIAL() \ + EMIT_BOOTINFO("SERIAL", "0x%llx", serial) + +const char *bi_chipid(void) +{ + return mmi_chosen_data.chipid; +} +EXPORT_SYMBOL(bi_chipid); + +#define EMIT_CHIPID() \ + EMIT_BOOTINFO("CHIPID", "%s", chipid) + +const char *bi_bootreason(void) +{ + return bootreason; +} +EXPORT_SYMBOL(bi_bootreason); + + +const char *bi_bootmode(void) +{ + return bootmode; +} +EXPORT_SYMBOL(bi_bootmode); + +/* get_bootinfo fills in the /proc/bootinfo information. + * We currently only have the powerup reason, mbm_version, serial + * and hwrevision. + */ + +static int get_bootinfo(struct seq_file *m, void *v) +{ + EMIT_SERIAL(); + EMIT_HWREV(); + EMIT_POWERUPREASON(); + EMIT_MBM_VERSION(); + EMIT_BL_BUILD_SIG(); + EMIT_BOOT_SEQ(); + EMIT_CHIPID(); + EMIT_BOOTINFO("Last boot reason", "%s", bootreason); + + return 0; +} + +static struct proc_dir_entry *proc_bootinfo; + +static int bootinfo_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, get_bootinfo, PDE_DATA(inode)); +} + +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +static const struct proc_ops bootinfo_proc_fops = { + .proc_open = bootinfo_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +#else +static const struct file_operations bootinfo_proc_fops = { + .open = bootinfo_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +int mmi_boot_info_init(void) +{ + int i; + + of_blsig(); + mmi_bootarg_setup(); + + /* /proc/bootinfo */ + proc_bootinfo = proc_create("bootinfo", + 0444, NULL, &bootinfo_proc_fops); + + /* Add to dont panic dump */ + for (i = 0; i < bl_build_sig_count; i++) { + bl_build_sigs[i].item[MAX_BLD_SIG_ITEM - 1] = 0; + bl_build_sigs[i].value[MAX_BLD_SIG_VALUE - 1] = 0; + mmi_annotate_persist(bl_build_sigs[i].item); + mmi_annotate_persist("="); + mmi_annotate_persist(bl_build_sigs[i].value); + mmi_annotate_persist("\n"); + } + mmi_annotate_persist("MBM_VERSION: 0x%08x\n", bi_mbm_version()); + mmi_annotate_persist("SERIAL: 0x%llx\n", bi_serial()); + mmi_annotate_persist("HW_REV: 0x%04x\n", bi_hwrev()); + mmi_annotate_persist("BOOT_SEQ: %d\n", bi_boot_seq()); + mmi_annotate_persist("CHIPID: %s\n", bi_chipid()); + mmi_annotate("POWERUPREASON: 0x%08x\n", bi_powerup_reason()); + mmi_annotate("Last boot reason: %s\n\n", bi_bootreason()); + + pr_info("BOOT_SEQ: %d\n", bi_boot_seq()); + return 0; +} + +void mmi_boot_info_exit(void) +{ + if (proc_bootinfo) + remove_proc_entry("bootinfo", NULL); +} diff --git a/drivers/mmi_info/mmi_info.h b/drivers/mmi_info/mmi_info.h new file mode 100644 index 000000000000..5706d823a25b --- /dev/null +++ b/drivers/mmi_info/mmi_info.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2013 Motorola Mobility LLC + * Copyright (C) 2018 Motorola Mobility LLC + * + * 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. + */ +#ifndef __ARCH_ARM_MACH_MSM_MMI_HW_INFO_H +#define __ARCH_ARM_MACH_MSM_MMI_HW_INFO_H + +int mmi_storage_info_init(void); +void mmi_storage_info_exit(void); +int mmi_ram_info_init(void); +void mmi_ram_info_exit(void); +int mmi_unit_info_init(void); +void mmi_unit_info_exit(void); +int mmi_boot_info_init(void); +void mmi_boot_info_exit(void); +int mmi_get_bootarg(char *key, char **value); + +struct mmi_storage_info { + char type[16]; /* UFS or eMMC */ + char size[16]; /* size in GB */ + char card_manufacturer[32]; + char product_name[32]; /* model ID */ + char firmware_version[32]; +}; + +/* set of data provided to the modem over SMEM */ +#define SERIALNO_MAX_LEN 64 +#define ANDROIDBOOT_DEVICE_MAX_LEN 32 +#define MMI_UNIT_INFO_VER 3 +#define BARCODE_MAX_LEN 65 +#define MACHINE_MAX_LEN 33 +#define CARRIER_MAX_LEN 65 +#define BASEBAND_MAX_LEN 97 +#define MSMHW_MAX_LEN 32 +#define DEVICE_MAX_LEN 33 +#define RADIO_MAX_LEN 33 +#define CHIPID_MAX_LEN 32 +struct mmi_unit_info { + uint32_t version; + uint32_t system_rev; + uint32_t system_serial_low; + uint32_t system_serial_high; + char machine[MACHINE_MAX_LEN]; + char barcode[BARCODE_MAX_LEN]; + char carrier[CARRIER_MAX_LEN]; + char baseband[BASEBAND_MAX_LEN]; + char device[DEVICE_MAX_LEN]; + uint32_t radio; + uint32_t powerup_reason; + char radio_str[RADIO_MAX_LEN]; +}; + +/* select properties from /chosen node in of */ +struct mmi_chosen_info { + unsigned int system_rev; + unsigned int system_serial_low; + unsigned int system_serial_high; + unsigned int powerup_reason; + unsigned int product_id; + unsigned int mbmversion; + unsigned int boot_seq; + char baseband[BASEBAND_MAX_LEN]; + char msm_hw[MSMHW_MAX_LEN]; + char chipid[CHIPID_MAX_LEN]; +}; + +extern struct mmi_chosen_info mmi_chosen_data; + +#define SYSFS_ATTRIBUTE(_name, _mode) \ +static struct kobj_attribute _name##_attr = \ + __ATTR(_name, _mode, _name##_show, NULL); \ + +#define SYSFS_SIMPLE_SHOW(_name, _param, _type, _size) \ +static ssize_t _name##_show(struct kobject *kobj, \ + struct kobj_attribute *attr, char *buf) \ +{ \ + return snprintf(buf, _size, _type"\n", _param); \ +} \ + +#endif diff --git a/drivers/mmi_info/mmi_info_main.c b/drivers/mmi_info/mmi_info_main.c new file mode 100644 index 000000000000..1264263b771c --- /dev/null +++ b/drivers/mmi_info/mmi_info_main.c @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2018 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include "mmi_info.h" + +struct mmi_chosen_info mmi_chosen_data; +static char *bootargs_str; + +/** + * mmi_device_is_available - check if a device is available for use + * + * @device: Node to check for availability, with locks already held + * + * Returns true if the status property + * -- is absent, + * -- refers to another property using string list as following + * mmi,status = , , [,] [, ...]; + * and at least one in the list of values matches + * value of //. + * Partial matches accepted when "contains" provided. + * false otherwise + */ +bool mmi_device_is_available(struct device_node *np) +{ + bool found; + int len, operation; + const char *path, *mmi_dts; + const char *mmi_dts_val, *val; + struct property *prop; + struct device_node *mmi_np; + + if (np == NULL) + return true; + + prop = of_find_property(np, "mmi,status", &len); + if (prop == NULL || len < 0) { + return true; + } + path = prop->value; + + mmi_dts = of_prop_next_string(prop, path); + if (mmi_dts == NULL) + return false; + + mmi_np = of_find_node_by_path(path); + if (mmi_np == NULL) + return false; + + mmi_dts_val = of_get_property(mmi_np, mmi_dts, &len); + of_node_put(mmi_np); + if (mmi_dts_val == NULL || len <= 0) + return false; + + val = mmi_dts; + operation = 0; + found = false; + while ((val = of_prop_next_string(prop, val))) { + if (val == NULL) + return false; + + if (!operation) { + operation++; + if (!strcmp(val, "contains")) { + operation++; + continue; + } + } + if (operation == 1) { + if (!strcmp(val, mmi_dts_val)) + found = true; + } else if (strstr(mmi_dts_val, val)) { + found = true; + } + if (found) + return true; + } + return false; +} +EXPORT_SYMBOL(mmi_device_is_available); + +bool mmi_check_dynamic_device_node(char *dev_name) +{ + struct property *prop; + struct device_node *node, *dst_node = NULL; + int len; + char *val = NULL; + bool result = true; + + node = of_find_node_by_path("/chosen"); + if (node == NULL) + goto out; + + prop = of_find_property(node, "mmi,dynamic_devices", &len); + of_node_put(node); + if (prop == NULL || len < 0) { + pr_err("%s: cannot find mmi,dynamic_devices property\n", __func__); + goto out; + } + + while ((val = (char *)of_prop_next_string(prop, val))) { + if (strstr(val, dev_name)) { + pr_info("%s: find matched dev name string %s\n", __func__, val); + dst_node = of_find_node_by_path(val); + break; + } + } + + if (dst_node == NULL) { + pr_err("%s: cannot find any node with dev_name %s\n", __func__, dev_name); + goto out; + } + + result = mmi_device_is_available(dst_node); + of_node_put(dst_node); +out: + return result; +} +EXPORT_SYMBOL(mmi_check_dynamic_device_node); + +static int mmi_get_bootarg_dt(char *key, char **value, char *prop, char *spl_flag) +{ + const char *bootargs_tmp = NULL; + char *idx = NULL; + char *kvpair = NULL; + int err = 1; + struct device_node *n = of_find_node_by_path("/chosen"); + size_t bootargs_tmp_len = 0; + + if (n == NULL) + goto err; + + if (of_property_read_string(n, prop, &bootargs_tmp) != 0) + goto putnode; + + bootargs_tmp_len = strlen(bootargs_tmp); + if (!bootargs_str) { + /* The following operations need a non-const + * version of bootargs + */ + bootargs_str = kzalloc(bootargs_tmp_len + 1, GFP_KERNEL); + if (!bootargs_str) + goto putnode; + } + strlcpy(bootargs_str, bootargs_tmp, bootargs_tmp_len + 1); + + idx = strnstr(bootargs_str, key, strlen(bootargs_str)); + if (idx) { + kvpair = strsep(&idx, " "); + if (kvpair) + if (strsep(&kvpair, "=")) { + *value = strsep(&kvpair, spl_flag); + if (*value) + err = 0; + } + } + +putnode: + of_node_put(n); +err: + return err; +} + +int mmi_get_bootarg(char *key, char **value) +{ +#ifdef CONFIG_BOOT_CONFIG + return mmi_get_bootarg_dt(key, value, "mmi,bootconfig", "\n"); +#else + return mmi_get_bootarg_dt(key, value, "bootargs", " "); +#endif +} + +static void mmi_of_populate_setup(void) +{ + const char *temp; + struct device_node *n = of_find_node_by_path("/chosen"); + + if (n == NULL) + return; + + of_property_read_u32(n, "mmi,powerup_reason", + &mmi_chosen_data.powerup_reason); + of_property_read_u32(n, "mmi,mbmversion", &mmi_chosen_data.mbmversion); + of_property_read_u32(n, "mmi,boot_seq", &mmi_chosen_data.boot_seq); + of_property_read_u32(n, "mmi,prod_id", &mmi_chosen_data.product_id); + of_property_read_u32(n, "linux,hwrev", &mmi_chosen_data.system_rev); + of_property_read_u32(n, "linux,seriallow", + &mmi_chosen_data.system_serial_low); + of_property_read_u32(n, "linux,serialhigh", + &mmi_chosen_data.system_serial_high); + if (of_property_read_string(n, "mmi,baseband", &temp) == 0) + strlcpy(mmi_chosen_data.baseband, temp, BASEBAND_MAX_LEN); + if (of_property_read_string(n, "mmi,msm_hw", &temp) == 0) + strlcpy(mmi_chosen_data.msm_hw, temp, MSMHW_MAX_LEN); + if (of_property_read_string(n, "mmi,chipid", &temp) == 0) + strlcpy(mmi_chosen_data.chipid, temp, CHIPID_MAX_LEN); + + of_node_put(n); +} + +static int __init mmi_hw_info_init(void) +{ + mmi_of_populate_setup(); + mmi_storage_info_init(); + mmi_ram_info_init(); + mmi_unit_info_init(); + mmi_boot_info_init(); + return 0; +} + +static void mmi_hw_info_exit(void) +{ + mmi_storage_info_exit(); + mmi_ram_info_exit(); + mmi_unit_info_exit(); + mmi_boot_info_exit(); + kfree(bootargs_str); +} + +module_init(mmi_hw_info_init); +module_exit(mmi_hw_info_exit); +MODULE_DESCRIPTION("Motorola Mobility LLC. HW Info"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmi_info/mmi_ram_info.c b/drivers/mmi_info/mmi_ram_info.c new file mode 100644 index 000000000000..265c125f99c5 --- /dev/null +++ b/drivers/mmi_info/mmi_ram_info.c @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2012 Motorola Mobility LLC + * Copyright (C) 2018 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include "mmi_info.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) +#include +/* Match to + * vendor/qcom/nonhlos/boot_images/QcomPkg/SDMPkg/Include/smem_type.h + */ +#define SMEM_SDRAM_INFO 135 +#else +#include +#define SMEM_SDRAM_INFO SMEM_ID_VENDOR1 +#endif + +static struct { + unsigned int mr5; + unsigned int mr6; + unsigned int mr7; + unsigned int mr8; + unsigned int ramsize; +} *smem_ddr_info; + +struct smem_ddr_info_v2 { + unsigned int mr5; + unsigned int mr6; + unsigned int mr7; + unsigned int mr8; + unsigned int ramsize; + unsigned int type; +}; + +static char sysfsram_type_name[20] = "unknown"; +static char sysfsram_vendor_name[20] = "unknown"; +static uint32_t sysfsram_ramsize; + +static ssize_t mr_register_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + uint32_t val = 0; + const char *name = attr->attr.name; + + if (smem_ddr_info != NULL && + strnlen(name, 4) == 3 && name[0] == 'm' && name[1] == 'r') { + switch (name[2]) { + case '5': + val = smem_ddr_info->mr5; + break; + case '6': + val = smem_ddr_info->mr6; + break; + case '7': + val = smem_ddr_info->mr7; + break; + case '8': + val = smem_ddr_info->mr8; + break; + } + } + + return snprintf(buf, 8, "0x%02x\n", val); +} + +static ssize_t info_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, 60, "%s:%s:%uMB\n", + sysfsram_vendor_name, + sysfsram_type_name, + sysfsram_ramsize); +} + +SYSFS_SIMPLE_SHOW(size, sysfsram_ramsize, "%u", 12) +SYSFS_SIMPLE_SHOW(type, sysfsram_type_name, "%s", 20) + +SYSFS_ATTRIBUTE(size, 0444) +SYSFS_ATTRIBUTE(type, 0444) +SYSFS_ATTRIBUTE(info, 0444) + +static struct kobj_attribute mr5_register_attr = + __ATTR(mr5, 0444, mr_register_show, NULL); + +static struct kobj_attribute mr6_register_attr = + __ATTR(mr6, 0444, mr_register_show, NULL); + +static struct kobj_attribute mr7_register_attr = + __ATTR(mr7, 0444, mr_register_show, NULL); + +static struct kobj_attribute mr8_register_attr = + __ATTR(mr8, 0444, mr_register_show, NULL); + +static struct attribute *ram_info_properties_attrs[] = { + &mr5_register_attr.attr, + &mr6_register_attr.attr, + &mr7_register_attr.attr, + &mr8_register_attr.attr, + &size_attr.attr, + &type_attr.attr, + &info_attr.attr, + NULL +}; + +static struct attribute_group ram_info_properties_attr_group = { + .attrs = ram_info_properties_attrs, +}; + +static struct kobject *ram_info_properties_kobj; + +static void parse_ddr_type(void) +{ + uint32_t tid; + static const char *types[] = { + "S4 SDRAM", + "S2 SDRAM", + "N NVM", + "Reserved" + }; + + /* identify type */ + tid = smem_ddr_info->mr8 & 0x03; + if (tid < ARRAY_SIZE(types)) { + snprintf(sysfsram_type_name, sizeof(sysfsram_type_name), + "%s", types[tid]); + } +} + +static void parse_ddr_type_v2(struct smem_ddr_info_v2 *ddr_info) +{ + uint32_t tid; + static const char *types[] = { + "No DDR", + "LPDDR1", + "LPDDR2", + "PCDDR2", + "PCDDR3", + "LPDDR3", + "LPDDR4", + "LPDDR4X", + "LPDDR5", + "LPDDR5X" + }; + + /* identify type */ + tid = ddr_info->type; + if (tid < ARRAY_SIZE(types)) { + snprintf(sysfsram_type_name, sizeof(sysfsram_type_name), + "%s", types[tid]); + } +} + +int mmi_ram_info_init(void) +{ + int status = 0; + uint8_t is_v2 = 0; + uint32_t vid; + const char *vname = "unknown"; + static const char *vendors[] = { + "unknown", + "Samsung", + "Qimonda", + "Elpida", + "Etron", + "Nanya", + "Hynix", + "Mosel", + "Winbond", + "ESMT", + "unknown", + "Spansion", + "SST", + "ZMOS", + "Intel" + }; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + ssize_t ddr_info_size = 0; + smem_ddr_info = qcom_smem_get(QCOM_SMEM_HOST_ANY, + SMEM_SDRAM_INFO, + &ddr_info_size); + + if(smem_ddr_info) { + if(ddr_info_size < sizeof(*smem_ddr_info)) { + pr_err("%s: Invalid SMEM size, cannot get RAM info\n", __func__); + goto err; + } + + /* v2 has an extra field */ + if(ddr_info_size == sizeof(struct smem_ddr_info_v2)) + is_v2 = 1; + } +#else + smem_ddr_info = smem_alloc(SMEM_SDRAM_INFO, sizeof(*smem_ddr_info), 0, + SMEM_ANY_HOST_FLAG); +#endif + if (smem_ddr_info == NULL) { + pr_err("%s: failed to access RAM info in SMEM\n", __func__); + goto err; + } + + /* identify vendor */ + vid = smem_ddr_info->mr5 & 0xFF; + if (vid < ARRAY_SIZE(vendors)) + vname = vendors[vid]; + else if (vid == 0x13) + vname = "CXMT"; + else if (vid == 0xFE) + vname = "Numonyx"; + else if (vid == 0xFF) + vname = "Micron"; + + snprintf(sysfsram_vendor_name, sizeof(sysfsram_vendor_name), + "%s", vname); + + if(is_v2) + parse_ddr_type_v2((struct smem_ddr_info_v2 *)smem_ddr_info); + else + parse_ddr_type(); + + /* extract size */ + sysfsram_ramsize = smem_ddr_info->ramsize; + + mmi_annotate_persist( + "RAM: %s, %s, %u MB, MR5:0x%02X, MR6:0x%02X, " + "MR7:0x%02X, MR8:0x%02X\n", + vname, sysfsram_type_name, smem_ddr_info->ramsize, + smem_ddr_info->mr5, smem_ddr_info->mr6, + smem_ddr_info->mr7, smem_ddr_info->mr8); + + /* create sysfs object */ + ram_info_properties_kobj = kobject_create_and_add("ram", NULL); + + if (ram_info_properties_kobj) + status = sysfs_create_group(ram_info_properties_kobj, + &ram_info_properties_attr_group); + + if (!ram_info_properties_kobj || status) { + pr_err("%s: failed to create /sys/ram\n", __func__); + goto err; + } + + return 0; +err: + return 1; +} + +void mmi_ram_info_exit(void) +{ + sysfs_remove_group(ram_info_properties_kobj, + &ram_info_properties_attr_group); + kobject_del(ram_info_properties_kobj); +} diff --git a/drivers/mmi_info/mmi_storage_info.c b/drivers/mmi_info/mmi_storage_info.c new file mode 100644 index 000000000000..8081c1279e61 --- /dev/null +++ b/drivers/mmi_info/mmi_storage_info.c @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2016 Motorola Mobility LLC + * Copyright (C) 2018 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include +#include +#include "mmi_info.h" + +struct mmi_storage_info *info; + +SYSFS_SIMPLE_SHOW(fw, info->firmware_version, "%s", 32) +SYSFS_SIMPLE_SHOW(model, info->product_name, "%s", 32) +SYSFS_SIMPLE_SHOW(vendor, info->card_manufacturer, "%s", 32) +SYSFS_SIMPLE_SHOW(size, info->size, "%s", 16) +SYSFS_SIMPLE_SHOW(type, info->type, "%s", 16) + +SYSFS_ATTRIBUTE(fw, 0444) +SYSFS_ATTRIBUTE(model, 0444) +SYSFS_ATTRIBUTE(vendor, 0444) +SYSFS_ATTRIBUTE(size, 0444) +SYSFS_ATTRIBUTE(type, 0444) + +static struct attribute *st_info_properties_attrs[] = { + &fw_attr.attr, + &model_attr.attr, + &vendor_attr.attr, + &size_attr.attr, + &type_attr.attr, + NULL +}; + +static struct attribute_group st_info_properties_attr_group = { + .attrs = st_info_properties_attrs, +}; + +static struct kobject *st_info_properties_kobj; + +int mmi_storage_info_init(void) +{ + int ret = 0; + int status = 0; + struct property *p; + struct device_node *n; + + n = of_find_node_by_path("/chosen/mmi,storage"); + if (n == NULL) { + ret = 1; + goto err; + } + + info = kzalloc(sizeof(struct mmi_storage_info), GFP_KERNEL); + if (!info) { + pr_err("%s: failed to allocate space for mmi_storage_info\n", + __func__); + ret = 1; + goto err; + } + + for_each_property_of_node(n, p) { + if (!strcmp(p->name, "type") && p->value) + strlcpy(info->type, (char *)p->value, + sizeof(info->type)); + if (!strcmp(p->name, "size") && p->value) + strlcpy(info->size, (char *)p->value, + sizeof(info->size)); + if (!strcmp(p->name, "manufacturer") && p->value) + strlcpy(info->card_manufacturer, (char *)p->value, + sizeof(info->card_manufacturer)); + if (!strcmp(p->name, "product") && p->value) + strlcpy(info->product_name, (char *)p->value, + sizeof(info->product_name)); + if (!strcmp(p->name, "firmware") && p->value) + strlcpy(info->firmware_version, (char *)p->value, + sizeof(info->firmware_version)); + } + + of_node_put(n); + + pr_info("mmi_storage_info :%s: %s %s %s FV=%s\n", + info->type, + info->size, + info->card_manufacturer, + info->product_name, + info->firmware_version); + + mmi_annotate_persist( + "%s: %s %s %s FV=%s\n", + info->type, + info->size, + info->card_manufacturer, + info->product_name, + info->firmware_version); + + /* Export to sysfs*/ + st_info_properties_kobj = kobject_create_and_add("storage", NULL); + if (st_info_properties_kobj) + status = sysfs_create_group(st_info_properties_kobj, + &st_info_properties_attr_group); + + if (!st_info_properties_kobj || status) { + pr_err("%s: failed to create /sys/storage\n", __func__); + ret = 1; + goto err; + } + +err: + return ret; +} + +void mmi_storage_info_exit(void) +{ + kfree(info); + info = NULL; + + sysfs_remove_group(st_info_properties_kobj, + &st_info_properties_attr_group); + kobject_del(st_info_properties_kobj); +} diff --git a/drivers/mmi_info/mmi_unit_info.c b/drivers/mmi_info/mmi_unit_info.c new file mode 100644 index 000000000000..fd75fb750664 --- /dev/null +++ b/drivers/mmi_info/mmi_unit_info.c @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2013 Motorola Mobility LLC + * Copyright (C) 2018 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "mmi_info.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) +#include +/* Match to + * vendor/qcom/nonhlos/boot_images/QcomPkg/SDMPkg/Include/smem_type.h + */ +#define SMEM_ID_VENDOR0 134 +#else +#include +#endif + +#define SMEM_KERNEL_RESERVE SMEM_ID_VENDOR0 + +static struct proc_dir_entry *unitinfo_procfs_file; +static struct mmi_unit_info *mui; +static char serialno[SERIALNO_MAX_LEN]; +static char carrier[CARRIER_MAX_LEN]; +static char baseband[BASEBAND_MAX_LEN]; +static char androidboot_device[ANDROIDBOOT_DEVICE_MAX_LEN]; +static unsigned int androidboot_radio; +static char androidboot_radio_str[RADIO_MAX_LEN]; + +static void mmi_bootarg_setup(void) +{ + char *s; + + if (mmi_get_bootarg("androidboot.radio=", &s) == 0) { + if (kstrtouint(s, 16, &androidboot_radio) < 0) + androidboot_radio = 0; + strlcpy(androidboot_radio_str, s, RADIO_MAX_LEN); + } + + if (mmi_get_bootarg("androidboot.device=", &s) == 0) + strlcpy(androidboot_device, s, ANDROIDBOOT_DEVICE_MAX_LEN); + + if (mmi_get_bootarg("androidboot.baseband=", &s) == 0) + strlcpy(baseband, s, BASEBAND_MAX_LEN); + + if (mmi_get_bootarg("androidboot.carrier=", &s) == 0) + strlcpy(carrier, s, CARRIER_MAX_LEN); + + if (mmi_get_bootarg("androidboot.serialno=", &s) == 0) + strlcpy(serialno, s, SERIALNO_MAX_LEN); +} + +static int unitinfo_seq_show(struct seq_file *f, void *ptr) +{ + /*seq_printf(f, "Hardware\t: %s\n", arch_read_hardware_id());*/ + seq_printf(f, "Revision\t: %04x\n", mmi_chosen_data.system_rev); + seq_printf(f, "Serial\t\t: %08x%08x\n", + mmi_chosen_data.system_serial_high, + mmi_chosen_data.system_serial_low); + + seq_printf(f, "Device\t\t: %s\n", androidboot_device); + /* Zero is not a valid "Radio" value. */ + /* Lack of "Radio" entry in cpuinfo means: */ + /* look for radio in "Revision" */ + if (strnlen(androidboot_radio_str, RADIO_MAX_LEN)) + seq_printf(f, "Radio\t\t: %s\n", androidboot_radio_str); + + seq_printf(f, "MSM Hardware\t: %s\n", mmi_chosen_data.msm_hw); + + return 0; +} + +static int mmi_unit_smem_setup(void) +{ + int ret = 0; + struct mmi_unit_info *mui_copy; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + ssize_t mui_size; +#endif + + #define SMEM_KERNEL_RESERVE_SIZE 1024 + mui_copy = kzalloc(SMEM_KERNEL_RESERVE_SIZE, GFP_KERNEL); + if (!mui_copy) { + pr_err("%s: failed to allocate space for mmi_unit_info\n", + __func__); + ret = 1; + goto err; + } + + mui_copy->version = MMI_UNIT_INFO_VER; + mui_copy->system_rev = mmi_chosen_data.system_rev; + mui_copy->system_serial_low = mmi_chosen_data.system_serial_low; + mui_copy->system_serial_high = mmi_chosen_data.system_serial_high; + strlcpy(mui_copy->machine, "", MACHINE_MAX_LEN); + strlcpy(mui_copy->barcode, serialno, BARCODE_MAX_LEN); + strlcpy(mui_copy->baseband, mmi_chosen_data.baseband, BASEBAND_MAX_LEN); + strlcpy(mui_copy->carrier, carrier, CARRIER_MAX_LEN); + strlcpy(mui_copy->device, androidboot_device, DEVICE_MAX_LEN); + mui_copy->radio = androidboot_radio; + strlcpy(mui_copy->radio_str, androidboot_radio_str, RADIO_MAX_LEN); + mui_copy->powerup_reason = mmi_chosen_data.powerup_reason; + + pr_info("mmi_unit_info (SMEM) for modem: version = 0x%02x," + " device = '%s', radio = 0x%x, radio_str = '%s'," + " system_rev = 0x%04x, system_serial = 0x%08x%08x," + " machine = '%s', barcode = '%s', baseband = '%s'," + " carrier = '%s', pu_reason = 0x%08x\n", + mui_copy->version, + mui_copy->device, + mui_copy->radio, + mui_copy->radio_str, + mui_copy->system_rev, + mui_copy->system_serial_high, mui_copy->system_serial_low, + mui_copy->machine, mui_copy->barcode, + mui_copy->baseband, mui_copy->carrier, + mui_copy->powerup_reason); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,14,0) + if(!qcom_smem_alloc(QCOM_SMEM_HOST_ANY, + SMEM_KERNEL_RESERVE, + SMEM_KERNEL_RESERVE_SIZE)) + mui = qcom_smem_get(QCOM_SMEM_HOST_ANY, + SMEM_KERNEL_RESERVE, + &mui_size); + + if (!mui || mui_size != SMEM_KERNEL_RESERVE_SIZE) { +#else + mui = (struct mmi_unit_info *) smem_alloc(SMEM_KERNEL_RESERVE, + SMEM_KERNEL_RESERVE_SIZE, 0, SMEM_ANY_HOST_FLAG); + + if (!mui) { +#endif + pr_err("%s: failed to allocate mmi_unit_info in SMEM\n", + __func__); + ret = 1; + goto err_free; + } else if (PTR_ERR(mui_copy) == -EPROBE_DEFER) { + pr_err("%s: SMEM not yet initialized\n", __func__); + ret = 1; + goto err_free; + } + + memcpy(mui, mui_copy, SMEM_KERNEL_RESERVE_SIZE); + +err_free: + kfree(mui_copy); +err: + return ret; +} + +static int unitinfo_open(struct inode *inode, struct file *file) +{ + return single_open(file, unitinfo_seq_show, inode->i_private); +} + +#if KERNEL_VERSION(5, 10, 0) <= LINUX_VERSION_CODE +static const struct proc_ops unitinfo_operations = { + .proc_open = unitinfo_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, +}; +#else +static const struct file_operations unitinfo_operations = { + .open = unitinfo_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif + +int mmi_unit_info_init(void) +{ + mmi_bootarg_setup(); + mmi_unit_smem_setup(); + + /* /proc/unitinfo */ + unitinfo_procfs_file = proc_create("unitinfo", + 0444, NULL, &unitinfo_operations); + + return 0; +} + +void mmi_unit_info_exit(void) +{ + if (unitinfo_procfs_file) + remove_proc_entry("unitinfo", NULL); +} diff --git a/drivers/mmi_muc_uart/Android.mk b/drivers/mmi_muc_uart/Android.mk new file mode 100644 index 000000000000..110870c0ee16 --- /dev/null +++ b/drivers/mmi_muc_uart/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_muc_uart.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/mmi_muc_uart/Kbuild b/drivers/mmi_muc_uart/Kbuild new file mode 100644 index 000000000000..5d31b228b615 --- /dev/null +++ b/drivers/mmi_muc_uart/Kbuild @@ -0,0 +1,8 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +mmi_muc_uart-objs += mmi_uart.o +mmi_muc_uart-objs += mmi_char.o +mmi_muc_uart-objs += muc_uart.o +obj-m += mmi_muc_uart.o \ No newline at end of file diff --git a/drivers/mmi_muc_uart/mmi_char.c b/drivers/mmi_muc_uart/mmi_char.c new file mode 100644 index 000000000000..71e544f4a50d --- /dev/null +++ b/drivers/mmi_muc_uart/mmi_char.c @@ -0,0 +1,145 @@ +/* Copyright (c) 2018, Motorola Mobility LLC. 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 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. + * + */ +#include +#include +#include +#include +#include +#include +#include "mmi_char.h" +#include "muc_uart.h" + +#define NUM_MINORS 1 + +static struct platform_device *parent_pdev; +static struct cdev mmi_char_cdev; +static struct class *mmi_char_class; +static dev_t mmi_char_dev_num; +wait_queue_head_t mmi_char_read_wq; +static int mmi_char_major; +static int mmi_char_max_write_size; +static atomic_t mmi_char_open_excl; + +static int mmi_char_open(struct inode *inode, struct file *file) +{ + if (atomic_xchg(&mmi_char_open_excl, 1)) + return -EBUSY; + + return 0; +} + +static int mmi_char_release(struct inode *inode, struct file *file) +{ + WARN_ON(!atomic_xchg(&mmi_char_open_excl, 0)); + + return 0; +} + +static ssize_t mmi_char_write(struct file *file, + const char __user *buf, + size_t count, + loff_t *ppos) +{ + if(count > mmi_char_max_write_size) + return -ENOSPC; + + return muc_uart_pb_write(parent_pdev, buf, count); +} + +static ssize_t mmi_char_read(struct file *file, + char __user *buf, + size_t count, + loff_t *ppos) +{ + int ret; + + if (!(file->f_flags & O_NONBLOCK)) { + ret = wait_event_interruptible(mmi_char_read_wq, + muc_uart_pb_get_q_size(parent_pdev)); + if(ret < 0) + goto exit; + } + + /* Count gets updated to bytes read, ret will + * be 0 if ok, and negative on an error + */ + ret = muc_uart_pb_get_q(parent_pdev, buf, &count); + if(ret >= 0) + ret = count; + +exit: + return ret; +} + +static unsigned int mmi_char_poll(struct file *file, + struct poll_table_struct *pll_table) +{ + int ret = 0; + + poll_wait(file, &mmi_char_read_wq, pll_table); + if(muc_uart_pb_get_q_size(parent_pdev)) + ret |= POLLIN; + + return ret; +} + +static const struct file_operations mmi_char_fops = { + .write = mmi_char_write, + .read = mmi_char_read, + .open = mmi_char_open, + .llseek = noop_llseek, + .release = mmi_char_release, + .poll = mmi_char_poll, +}; + +int mmi_char_init(int max_write_size, struct platform_device *pdev) +{ + int ret; + mmi_char_max_write_size = max_write_size; + parent_pdev = pdev; + + ret = alloc_chrdev_region(&mmi_char_dev_num, 0, NUM_MINORS, "muc"); + if (ret < 0) + goto err_dev; + + mmi_char_major = MAJOR(mmi_char_dev_num); + mmi_char_dev_num = MKDEV(mmi_char_major, 0); + mmi_char_class = class_create(THIS_MODULE, "mmi_char_class"); + + cdev_init(&mmi_char_cdev, &mmi_char_fops); + ret = cdev_add(&mmi_char_cdev, mmi_char_dev_num, 1); + if(ret) + goto err_cdev; + + if(!device_create(mmi_char_class, NULL, + mmi_char_dev_num, NULL, "muc%d", 0)) + goto err_cdev; + + atomic_set(&mmi_char_open_excl, 0); + + init_waitqueue_head(&mmi_char_read_wq); + muc_uart_pb_register_wq(pdev, &mmi_char_read_wq); + + return 0; +err_cdev: + unregister_chrdev_region(mmi_char_dev_num, NUM_MINORS); +err_dev: + return 1; +} + +void mmi_char_exit(void) +{ + device_destroy(mmi_char_class, mmi_char_dev_num); + unregister_chrdev_region(mmi_char_dev_num, NUM_MINORS); + class_destroy(mmi_char_class); +} diff --git a/drivers/mmi_muc_uart/mmi_char.h b/drivers/mmi_muc_uart/mmi_char.h new file mode 100644 index 000000000000..f47bd8124ca7 --- /dev/null +++ b/drivers/mmi_muc_uart/mmi_char.h @@ -0,0 +1,19 @@ +/* Copyright (c) 2018, Motorola Mobility LLC. 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 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. + * + */ +#ifndef _DRIVERS_CHAR_MMI_H_ +#define _DRIVERS_CHAR_MMI_H_ + +int mmi_char_init(int max_write_size, struct platform_device *pdev); +void mmi_char_exit(void); + +#endif /* _DRIVERS_CHAR_MMI_H_ */ \ No newline at end of file diff --git a/drivers/mmi_muc_uart/mmi_tty.c b/drivers/mmi_muc_uart/mmi_tty.c new file mode 100644 index 000000000000..0dc6d6891916 --- /dev/null +++ b/drivers/mmi_muc_uart/mmi_tty.c @@ -0,0 +1,236 @@ +/* Copyright (c) 2018, Motorola Mobility LLC. 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 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_tty.h" + +/* TODO only 1 device for non-debug version. */ +#define MMI_TTY_MINOR_COUNT 2 +#define MMI_TTY_DRIVER_NAME "tty_muc" +#define MMI_TTY_DEVICE_NAME "ttymuc" + +static DEFINE_SPINLOCK(mmi_tty_lock); + +static struct tty_driver *mmi_tty_driver; +static dev_t mmi_tty_dev_no; + +struct mmi_tty_data { + struct platform_device *pdev; + struct tty_port port; + struct tty_struct *tty; + int open_count; +}; + +static struct mmi_tty_data mmi_tty[MMI_TTY_MINOR_COUNT]; + +static int (*mmi_tty_rx_cb)(struct platform_device *pdev, + int idx, + uint8_t *payload, + size_t len); +static int mmi_max_write_size; + +static int mmi_tty_open(struct tty_struct *tty, struct file *f) +{ + int n = tty->index; + struct mmi_tty_data *tty_data; + unsigned long flags; + + if (n < 0 || n >= MMI_TTY_MINOR_COUNT) + return -ENODEV; + + tty_data = mmi_tty + n; + + if (tty_data->open_count >= 1) + return -EBUSY; + + spin_lock_irqsave(&mmi_tty_lock, flags); + + tty_data->tty = tty; + tty->driver_data = tty_data; + tty_data->open_count++; + + spin_unlock_irqrestore(&mmi_tty_lock, flags); + + return 0; +} + +static void mmi_tty_close(struct tty_struct *tty, struct file *f) +{ + struct mmi_tty_data *tty_data = tty->driver_data; + unsigned long flags; + + if (tty_data == NULL) + return; + + spin_lock_irqsave(&mmi_tty_lock, flags); + tty_data->open_count--; + + if (tty_data->open_count == 0) { + tty_data->tty = NULL; + tty->driver_data = NULL; + } + + spin_unlock_irqrestore(&mmi_tty_lock, flags); +} + +static int mmi_tty_write(struct tty_struct *tty, + const unsigned char *buf, int len) +{ + struct mmi_tty_data *tty_data = tty->driver_data; + int ret; + + /* If multiple users are trying to open/close/write to + * the same device the kernel panics sometimes. + */ + if (!tty_data) + return -ENODEV; + + pr_debug("muc_uart tty: Writing %d bytes.\n", len); + ret = (*mmi_tty_rx_cb)(tty_data->pdev, + tty->index, + (uint8_t *)buf, + (size_t)len); + pr_debug("muc_uart tty: Done with ret %d.\n", ret); + return ret; +} + +static int mmi_tty_write_room(struct tty_struct *tty) +{ + return mmi_max_write_size; +} + +static int mmi_tty_chars_in_buffer(struct tty_struct *tty) +{ + /* Data is always written when available, this should be changed if + * write to userspace is changed to delayed work + */ + return 0; +} + +static const struct tty_operations mmi_tty_ops = { + .open = mmi_tty_open, + .close = mmi_tty_close, + .write = mmi_tty_write, + .write_room = mmi_tty_write_room, + .chars_in_buffer = mmi_tty_chars_in_buffer, +}; + +/* Write a message to userspace */ +int mmi_tty_push_to_us(int idx, uint8_t *buf, size_t sz) +{ + struct mmi_tty_data *tty_data; + unsigned char *tty_buf; + int tty_allocated; + unsigned long flags; + + if (idx < 0 || idx >= MMI_TTY_MINOR_COUNT) + return -ENODEV; + + tty_data = &(mmi_tty[idx]); + + spin_lock_irqsave(&mmi_tty_lock, flags); + + if (tty_data->tty == NULL) { + spin_unlock_irqrestore(&mmi_tty_lock, flags); + return -EIO; + } + + tty_allocated = tty_prepare_flip_string(&tty_data->port, + &tty_buf, sz); + + if (tty_allocated < sz) { + spin_unlock_irqrestore(&mmi_tty_lock, flags); + return -ENOMEM; + } + + memcpy(tty_buf, buf, sz); + tty_flip_buffer_push(&tty_data->port); + pr_debug("muc_uart tty: Pushed %zd bytes.\n", sz); + spin_unlock_irqrestore(&mmi_tty_lock, flags); + + return 0; +} + +int mmi_tty_init(int (*tty_rx_cb)(struct platform_device *pdev, + int idx, uint8_t *payload, size_t len), + int max_write_size, + struct platform_device *pdev) +{ + int result; + int i; + + mmi_tty_rx_cb = tty_rx_cb; + mmi_max_write_size = max_write_size; + + mmi_tty_driver = alloc_tty_driver(MMI_TTY_MINOR_COUNT); + if (mmi_tty_driver == NULL) + return -ENOMEM; + + mmi_tty_driver->owner = THIS_MODULE; + mmi_tty_driver->driver_name = MMI_TTY_DRIVER_NAME; + mmi_tty_driver->name = MMI_TTY_DEVICE_NAME; + mmi_tty_driver->minor_start = 0; + mmi_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM; + mmi_tty_driver->subtype = SYSTEM_TYPE_TTY; + mmi_tty_driver->init_termios = tty_std_termios; + mmi_tty_driver->init_termios.c_cflag = B115200 | CS8 | CREAD; + mmi_tty_driver->init_termios.c_iflag = IGNBRK; + mmi_tty_driver->init_termios.c_oflag = 0; + mmi_tty_driver->init_termios.c_lflag = 0; + mmi_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + + tty_set_operations(mmi_tty_driver, &mmi_tty_ops); + + result = tty_register_driver(mmi_tty_driver); + if (result) { + pr_err("mmi_tty Failed to register mmi_tty driver with major num :%d, res: %d.\n", + MAJOR(mmi_tty_dev_no), + result); + put_tty_driver(mmi_tty_driver); + mmi_tty_driver = NULL; + return result; + } + + for (i = 0; i < MMI_TTY_MINOR_COUNT; i++) { + tty_port_init(&mmi_tty[i].port); + tty_port_register_device(&mmi_tty[i].port, mmi_tty_driver, + i, NULL); + mmi_tty[i].pdev = pdev; + } + + pr_info("mmi_tty TTY Registered OK\n"); + + return 0; +} + +void mmi_tty_exit(void) +{ + int i; + + if (mmi_tty_driver) { + for (i = MMI_TTY_MINOR_COUNT - 1; i >= 0; i--) + tty_unregister_device(mmi_tty_driver, i); + tty_unregister_driver(mmi_tty_driver); + put_tty_driver(mmi_tty_driver); + } +} diff --git a/drivers/mmi_muc_uart/mmi_tty.h b/drivers/mmi_muc_uart/mmi_tty.h new file mode 100644 index 000000000000..69d209c21b96 --- /dev/null +++ b/drivers/mmi_muc_uart/mmi_tty.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2018, Motorola Mobility LLC. 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 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. + * + */ + +#ifndef _DRIVERS_TTY_MMI_H_ +#define _DRIVERS_TTY_MMI_H_ + +#define MMI_TTY_DEFAULT_IDX 0 +#define MMI_TTY_DEBUG_IDX 1 + +int mmi_tty_init(int (*tty_rx_cb)(struct platform_device *pdev, + int idx, uint8_t *payload, size_t len), + int max_write_size, + struct platform_device *pdev); +void mmi_tty_exit(void); +int mmi_tty_push_to_us(int idx, uint8_t *buf, size_t sz); + +#endif /* _DRIVERS_TTY_MMI_H_ */ diff --git a/drivers/mmi_muc_uart/mmi_uart.c b/drivers/mmi_muc_uart/mmi_uart.c new file mode 100644 index 000000000000..08e2c00a8800 --- /dev/null +++ b/drivers/mmi_muc_uart/mmi_uart.c @@ -0,0 +1,608 @@ +/* Copyright (c) 2018, Motorola Mobility LLC. 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 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_uart.h" + +#define DRIVERNAME "mmi_uart" +#define N_MMI_UART 26 + +static DEFINE_SPINLOCK(tx_lock); + +struct mmi_uart_err_stats { + uint32_t tx_failure; + uint32_t rx_crc; + uint32_t rx_timeout; + uint32_t rx_abort; + uint32_t rx_len; + uint32_t rx_mag; +}; + +struct mmi_uart_data { + struct platform_device *pdev; + struct tty_struct *tty; + char rx_data[UART_MAX_MSG_SIZE]; + size_t rx_len; + unsigned long last_rx; + struct mmi_uart_err_stats stats; + const char *tty_name; + speed_t default_baud; + bool tx_busy; + bool tx_ready; +}; + +#define MMI_UART_SEGMENT_TIMEOUT 500 /* msec */ + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +/* Found in tty_io.c */ +extern struct mutex tty_mutex; + +static size_t (*mmi_uart_rx_cb)(struct platform_device *pdev, + uint8_t *payload, + size_t len); +static int (*mmi_uart_register_cb)(struct platform_device *pdev, + void *uart_data); +static void *(*mmi_uart_get_data)(struct device *dev); + +static ssize_t uart_stats_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mmi_uart_data *mud = + (struct mmi_uart_data *)mmi_uart_get_data(dev); + + return scnprintf(buf, PAGE_SIZE, + "tx err:%d, rx crc:%d, rx timeout:%d, rx abort:%d, " + "rx len:%d, rx mag:%d\n", + mud->stats.tx_failure, mud->stats.rx_crc, + mud->stats.rx_timeout, mud->stats.rx_abort, + mud->stats.rx_len, mud->stats.rx_mag); +} + +/* /sys/devices/soc:mod_uart/uart_stats */ +static DEVICE_ATTR_RO(uart_stats); + +static struct attribute *uart_attrs[] = { + &dev_attr_uart_stats.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(uart); + +void mmi_uart_report_rx_len_err(void *uart_data) +{ + struct mmi_uart_data *mud = (struct mmi_uart_data *)uart_data; + + mud->stats.rx_len++; +} + +void mmi_uart_report_rx_crc_err(void *uart_data) +{ + struct mmi_uart_data *mud = (struct mmi_uart_data *)uart_data; + + mud->stats.rx_crc++; +} + +void mmi_uart_report_rx_mag_err(void *uart_data) +{ + struct mmi_uart_data *mud = (struct mmi_uart_data *)uart_data; + + mud->stats.rx_mag++; +} + +void mmi_uart_report_tx_err(void *uart_data) +{ + struct mmi_uart_data *mud = (struct mmi_uart_data *)uart_data; + + mud->stats.tx_failure++; +} + +int mmi_uart_set_tx_busy(void *uart_data) +{ + struct mmi_uart_data *mud = (struct mmi_uart_data *)uart_data; + unsigned long flags; + int ret; + + spin_lock_irqsave(&tx_lock, flags); + if (mud->tx_busy) + ret = 1; + else { + mud->tx_busy = 1; + ret = 0; + } + spin_unlock_irqrestore(&tx_lock, flags); + return ret; +} + +void mmi_uart_clear_tx_busy(void *uart_data) +{ + struct mmi_uart_data *mud = (struct mmi_uart_data *)uart_data; + unsigned long flags; + + spin_lock_irqsave(&tx_lock, flags); + mud->tx_busy = 0; + spin_unlock_irqrestore(&tx_lock, flags); +} + +static int mmi_uart_get_ready(void *uart_data) +{ + struct mmi_uart_data *mud = (struct mmi_uart_data *)uart_data; + unsigned long flags; + int res; + + spin_lock_irqsave(&tx_lock, flags); + res = mud->tx_ready; + spin_unlock_irqrestore(&tx_lock, flags); + + return res; +} + +static void mmi_uart_set_ready(void *uart_data, bool ready) +{ + struct mmi_uart_data *mud = (struct mmi_uart_data *)uart_data; + unsigned long flags; + + spin_lock_irqsave(&tx_lock, flags); + mud->tx_ready = ready; + spin_unlock_irqrestore(&tx_lock, flags); +} + +/* Only call with tx busy lock */ +int mmi_uart_send(void *uart_data, uint8_t *buf, size_t len) +{ + struct mmi_uart_data *mud = (struct mmi_uart_data *)uart_data; + struct device *dev = &mud->pdev->dev; + int ret; + + print_hex_dump_debug("muc_uart tx: ", DUMP_PREFIX_OFFSET, + 16, 1, buf, len, true); + + if (!mmi_uart_get_ready(uart_data) || !mud->tty) { + dev_err(dev, "%s: no tty\n", __func__); + return -ENODEV; + } + + ret = mud->tty->ops->write(mud->tty, buf, len); + if (ret != len) { + dev_err(dev, "%s: Failed to send message\n", __func__); + goto send_err; + } + + return 0; + +send_err: + mud->stats.tx_failure++; + return -EIO; +} + +int mmi_uart_get_baud(void *uart_data) +{ + struct mmi_uart_data *mud = (struct mmi_uart_data *)uart_data; + speed_t speed; + + if (!mud || !mud->tty) { + pr_err("%s: no tty\n", __func__); + return -ENODEV; + } + + down_read(&mud->tty->termios_rwsem); + speed = mud->tty->termios.c_ispeed; + up_read(&mud->tty->termios_rwsem); + + return (int)speed; +} + +static int config_tty(struct mmi_uart_data *mud, speed_t speed, + struct tty_struct *tty) +{ + struct ktermios kt; + + if (!speed) + speed = mud->default_baud; + dev_info(&mud->pdev->dev, "%s: speed=%d\n", __func__, speed); + + /* Use one stop bit (CSTOPB not set) and no parity (PARENB not set) */ + kt.c_cflag = 0; + kt.c_cflag |= CLOCAL; /* Ignore modem control lines */ + kt.c_cflag |= CREAD; /* Enable receiver */ + kt.c_cflag |= CS8; /* Character size */ + kt.c_cflag |= CRTSCTS; /* Enable RTS/CTS (hardware) flow control */ + kt.c_iflag = 0; + kt.c_iflag |= IGNBRK; /* Ignore BREAK condition on input */ + kt.c_iflag |= IGNPAR; /* Ignore framing errors and parity errors. */ + kt.c_oflag = 0; + kt.c_lflag = 0; + + /* Save off the previous c_line so we don't overwrite it */ + kt.c_line = tty->termios.c_line; + + tty_termios_encode_baud_rate(&kt, speed, speed); + + return tty_set_termios(tty, &kt); +} + +int mmi_uart_set_baud(void *uart_data, uint32_t baud) +{ + struct mmi_uart_data *mud = (struct mmi_uart_data *)uart_data; + int ret = -ENODEV; + + if (mud && mud->tty) + ret = config_tty(mud, (speed_t)baud, mud->tty); + + return ret; +} + +/** + * Find the tty driver for a given tty name. + * + * @name: name string to match + * @line: pointer to resulting tty line nr + * + * Originally copied from tty_find_polling_driver, with polling check removed. + */ +static struct tty_driver *find_tty_driver(char *name, int *line) +{ + struct tty_driver *p, *res = NULL; + unsigned long tty_line = 0; + int len; + char *str, *stp; + + for (str = name; *str; str++) + if ((*str >= '0' && *str <= '9') || *str == ',') + break; + if (!*str) + return NULL; + + len = str - name; + if (kstrtoul(str, 10, &tty_line)) + return NULL; + + mutex_lock(&tty_mutex); + /* Search through the tty devices to look for a match */ + list_for_each_entry(p, &tty_drivers, tty_drivers) { + if (strncmp(name, p->name, len) != 0) + continue; + stp = str; + if (*stp == ',') + stp++; + if (*stp == '\0') + stp = NULL; + + if (tty_line >= 0 && tty_line < p->num) { + res = tty_driver_kref_get(p); + *line = tty_line; + break; + } + } + mutex_unlock(&tty_mutex); + + return res; +} + +int mmi_uart_do_pm(void *uart_data, bool on) +{ + struct mmi_uart_data *mud = (struct mmi_uart_data *)uart_data; + + if (!mud || !mud->tty) { + pr_err("%s: no tty\n", __func__); + return -ENODEV; + } + + return mud->tty->ops->ioctl(mud->tty, on ? TIOCPMGET : TIOCPMPUT, 0); +} + +int mmi_uart_open(void *uart_data) +{ + struct mmi_uart_data *mud = (struct mmi_uart_data *)uart_data; + struct tty_driver *driver; + int tty_line = 0; + int ret; + struct tty_struct *tty_tmp; + + if (mud->tty) { + dev_warn(&mud->pdev->dev, "%s: already open\n", __func__); + return -EEXIST; + } + + dev_dbg(&mud->pdev->dev, "%s: opening uart\n", __func__); + + /* Find the driver for the specified tty */ + driver = find_tty_driver((char *)mud->tty_name, &tty_line); + if (!driver || !driver->ttys) { + dev_err(&mud->pdev->dev, "%s: Did not find tty driver\n", + __func__); + ret = -ENODEV; + goto open_fail; + } + + /* Use existing tty if present */ + tty_tmp = driver->ttys[tty_line]; + if (!tty_tmp) + tty_tmp = tty_init_dev(driver, tty_line); + + tty_driver_kref_put(driver); + + if (IS_ERR(tty_tmp)) { + ret = PTR_ERR(tty_tmp); + goto open_fail; + } + + ret = tty_tmp->ops->open(tty_tmp, NULL); + if (ret) { + dev_err(&mud->pdev->dev, "%s: Failed to open tty\n", __func__); + goto release_tty; + } + + ret = config_tty(mud, 0 /* default speed */, tty_tmp); + if (ret) { + dev_err(&mud->pdev->dev, "%s: Failed to config tty\n", + __func__); + goto close_tty_locked; + } + + /* + * Before the line discipline can be set, the tty must be unlocked. + * If this is not done, the kernel will deadlock. + */ + tty_unlock(tty_tmp); + + /* Reset sysfs stat entries */ + memset((void *)&mud->stats, 0, sizeof(struct mmi_uart_err_stats)); + + /* Set device to ready before we set up the ldisc, we don't want + * to not be ready if we start processing rx messages. + */ + mud->tty = tty_tmp; + mmi_uart_set_ready(uart_data, true); + + /* + * Set the line discipline to the local ldisc. This will allow this + * driver to know when new data is received without having to poll. + */ + ret = tty_set_ldisc(tty_tmp, N_MMI_UART); + if (ret) { + dev_err(&mud->pdev->dev, "%s: Failed to set ldisc\n", __func__); + mmi_uart_set_ready(uart_data, false); + goto close_tty_unlocked; + } + tty_tmp->disc_data = mud; + + dev_info(&mud->pdev->dev, "%s: uart init done\n", __func__); + + return 0; + +close_tty_unlocked: + /* TTY must be locked to close the connection */ + tty_lock(tty_tmp); + +close_tty_locked: + tty_tmp->ops->close(tty_tmp, NULL); + tty_unlock(tty_tmp); + +release_tty: + /* Release the TTY, which requires the mutex to be held */ + mutex_lock(&tty_mutex); + tty_release_struct(tty_tmp, tty_tmp->index); + mutex_unlock(&tty_mutex); + +open_fail: + + return ret; +} + +int mmi_uart_close(void *uart_data) +{ + struct mmi_uart_data *mud = (struct mmi_uart_data *)uart_data; + int ret = 0; + + dev_dbg(&mud->pdev->dev, "%s: closing uart\n", __func__); + + if (mmi_uart_set_tx_busy(uart_data)) + return -EBUSY; + + if (!mmi_uart_get_ready(uart_data) || !mud->tty) { + dev_warn(&mud->pdev->dev, "%s: already closed\n", __func__); + return -ENODEV; + } + + dev_dbg(&mud->pdev->dev, "%s: really closing\n", __func__); + + if (tty_set_ldisc(mud->tty, N_TTY)) + dev_err(&mud->pdev->dev, "%s: Failed to set ldisc\n", __func__); + + /* TTY must be locked to close the connection */ + tty_lock(mud->tty); + mud->tty->ops->close(mud->tty, NULL); + tty_unlock(mud->tty); + + /* Release the TTY, which requires the mutex to be held */ + mutex_lock(&tty_mutex); + tty_release_struct(mud->tty, mud->tty->index); + mutex_unlock(&tty_mutex); + mud->tty = NULL; + + mmi_uart_set_ready(uart_data, false); + mmi_uart_clear_tx_busy(uart_data); + + return ret; +} + +static int mmi_uart_consume_segment(struct mmi_uart_data *mud) +{ + size_t segment_size; + + segment_size = (*mmi_uart_rx_cb)(mud->pdev, mud->rx_data, mud->rx_len); + if (segment_size) { + mud->rx_len -= segment_size; + if (mud->rx_len) { + memmove(mud->rx_data, mud->rx_data + segment_size, + mud->rx_len); + /* more data to consume */ + return 1; + } + } + + return 0; +} + +static int n_mmi_uart_receive_buf2(struct tty_struct *tty, + const unsigned char *cp, + char *fp, int count) +{ + struct mmi_uart_data *mud = tty->disc_data; + struct device *dev = &mud->pdev->dev; + int to_be_consumed = count; + + print_hex_dump_debug("muc_uart rx raw: ", DUMP_PREFIX_OFFSET, + 16, 1, cp, count, true); + + /* + * Try to clean up garbage/incomplete chars received + * for some reason. + */ + if (mud->rx_len && + (jiffies_to_msecs(jiffies - mud->last_rx) > + MMI_UART_SEGMENT_TIMEOUT)) { + mud->stats.rx_timeout++; + print_hex_dump_debug("muc_uart rx raw (timeout): ", + DUMP_PREFIX_OFFSET, + 16, 1, mud->rx_data, mud->rx_len, true); + dev_err(dev, "%s: RX Buffer cleaned up\n", __func__); + mud->rx_len = 0; + } + mud->last_rx = jiffies; + + while (to_be_consumed > 0) { + int copy_size; + + if (mud->rx_len == UART_MAX_MSG_SIZE) { + /* + * buffer is already left full, not consumed. + * Something is wrong. Need to reset the buffer + * to proceed + */ + mud->stats.rx_abort++; + print_hex_dump_debug("muc_uart rx raw (overflow): ", + DUMP_PREFIX_OFFSET, 16, 1, + mud->rx_data, mud->rx_len, true); + dev_err(dev, "%s: RX buffer overflow\n", __func__); + mud->rx_len = 0; + } + + copy_size = MIN(to_be_consumed, + UART_MAX_MSG_SIZE - mud->rx_len); + + memcpy(&mud->rx_data[mud->rx_len], cp, copy_size); + mud->rx_len += copy_size; + + do {} while (mmi_uart_consume_segment(mud)); + + to_be_consumed -= copy_size; + } + + return count; +} + +static struct tty_ldisc_ops mmi_uart_ldisc = { + .owner = THIS_MODULE, + .magic = TTY_LDISC_MAGIC, + .name = DRIVERNAME, + .receive_buf2 = n_mmi_uart_receive_buf2, +}; + +int mmi_uart_init(size_t (*uart_rx_cb)(struct platform_device *pdev, + uint8_t *payload, size_t len), + int (*uart_register_cb)(struct platform_device *pdev, + void *uart_data), + void *(*uart_get_data)(struct device *dev), + struct platform_device *pdev) +{ + struct mmi_uart_data *mud; + speed_t speed; + int ret; + struct device_node *np = pdev->dev.of_node; + + mmi_uart_rx_cb = uart_rx_cb; + mmi_uart_register_cb = uart_register_cb; + mmi_uart_get_data = uart_get_data; + + ret = tty_register_ldisc(N_MMI_UART, &mmi_uart_ldisc); + if (ret) { + pr_err("%s: ldisc registration failed: %d\n", __func__, ret); + return ret; + } + + mud = devm_kzalloc(&pdev->dev, sizeof(*mud), GFP_KERNEL); + if (!mud) + return -ENOMEM; + + ret = of_property_read_u32(np, "mmi,tty_speed", &speed); + if (ret) { + dev_err(&pdev->dev, "%s: TTY speed not populated\n", __func__); + return ret; + } + mud->default_baud = speed; + mud->pdev = pdev; + + /* Retrieve the name of the tty from the device tree */ + ret = of_property_read_string(np, "mmi,tty", &mud->tty_name); + if (ret) { + dev_err(&pdev->dev, "%s: TTY name not populated\n", __func__); + return ret; + } + dev_info(&pdev->dev, "%s: Using %s\n", __func__, mud->tty_name); + + ret = sysfs_create_groups(&pdev->dev.kobj, uart_groups); + if (ret) { + dev_err(&pdev->dev, "Failed to create sysfs attributes\n"); + return ret; + } + + if ((*mmi_uart_register_cb)(pdev, mud)) { + ret = -EPROBE_DEFER; + goto remove_sysfs; + } + + return 0; + +remove_sysfs: + sysfs_remove_groups(&pdev->dev.kobj, uart_groups); + + return ret; +} + +int mmi_uart_exit(void *uart_data) +{ + int ret; + struct mmi_uart_data *mud = (struct mmi_uart_data *)uart_data; + + sysfs_remove_groups(&mud->pdev->dev.kobj, uart_groups); + mmi_uart_do_pm(mud, false); + mmi_uart_close(mud); + + ret = tty_unregister_ldisc(N_MMI_UART); + if (ret < 0) + pr_err("%s: ldisc unregistration failed: %d\n", __func__, ret); + + return 0; +} diff --git a/drivers/mmi_muc_uart/mmi_uart.h b/drivers/mmi_muc_uart/mmi_uart.h new file mode 100644 index 000000000000..21f87eb70c4a --- /dev/null +++ b/drivers/mmi_muc_uart/mmi_uart.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2018, Motorola Mobility LLC. 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 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. + * + */ +#ifndef _DRIVERS_MMI_UART_H_ +#define _DRIVERS_MMI_UART_H_ + +#define UART_MAX_MSG_SIZE (2048) + +int mmi_uart_open(void *uart_data); +int mmi_uart_close(void *uart_data); +int mmi_uart_send(void *uart_data, uint8_t *buf, size_t len); + +/* Lock the UART while setting the baud or pm. */ +int mmi_uart_set_tx_busy(void *uart_data); +void mmi_uart_clear_tx_busy(void *uart_data); +int mmi_uart_do_pm(void *uart_data, bool on); +int mmi_uart_get_baud(void *uart_data); +int mmi_uart_set_baud(void *uart_data, uint32_t baud); + +/* sysfs err reporting */ +void mmi_uart_report_rx_len_err(void *uart_data); +void mmi_uart_report_rx_crc_err(void *uart_data); +void mmi_uart_report_rx_mag_err(void *uart_data); +void mmi_uart_report_tx_err(void *uart_data); + +/* Driver Initializations */ +int mmi_uart_init(size_t (*uart_rx_cb)(struct platform_device *pdev, + uint8_t *payload, size_t len), + int (*uart_register_cb)(struct platform_device *pdev, + void *uart_data), + void *(*uart_get_data)(struct device *dev), + struct platform_device *pdev); +int mmi_uart_exit(void *uart_data); + +#endif /* _DRIVERS_MMI_UART_H_ */ diff --git a/drivers/mmi_muc_uart/muc_protocol.h b/drivers/mmi_muc_uart/muc_protocol.h new file mode 100644 index 000000000000..a10988706cab --- /dev/null +++ b/drivers/mmi_muc_uart/muc_protocol.h @@ -0,0 +1,105 @@ +/* Copyright (c) 2018, Motorola Mobility LLC. 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 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. + * + */ +#ifndef MODD_MUC_PROTOCOL_H +#define MODD_MUC_PROTOCOL_H + +struct mmi_uart_hdr_t { + uint16_t magic; /* 0xA1A1 */ + uint16_t cmd; + uint16_t payload_length; + uint8_t payload[]; +} __packed; +/* Followed by CRC16 (Poly8005) */ + +struct mmi_uart_fw_t { + uint16_t port_id; + uint16_t sn; + uint8_t payload[]; +} __packed; + +struct mmi_uart_pb_hdr_t { + uint16_t port_id; + uint16_t cmd; +} __packed; + +#define MSG_MAGIC 0xA1A1 +#define MSG_META_DATA_SIZE (sizeof(struct mmi_uart_hdr_t) + sizeof(uint16_t)) + +#define MSG_ACK_MASK 0x8000 +#define MSG_NACK_MASK 0x4000 + +/* Msg ID's */ +#define UART_SLEEP_REJ 0x0000 +#define UART_SLEEP_REQ 0x0001 +#define PACKETBUS_PROT_MSG 0x0002 +#define POWER_STATUS 0x0003 +#define POWER_CONTROL 0x0004 +#define USB_CONTROL 0x0005 +#define BOOT_MODE 0x0007 +#define MUC_FW_VERSION 0x0008 +#define MUC_SET_GPIO 0x0009 +#define MUC_DEBUG_MSG 0x0010 + +/* MUC gpios */ +#define MUC_GPIO_1 35 +#define MOD_ATTACH_GPIO 36 +#define FORCE_USB_BOOT_GPIO 136 + +/* Packetbus update command */ +#define PACKETBUS_PORT_MUC_FW 0x0007 +#define BOLT_MSG_GET_MUC_FW 0x0000 + +/* TODO put in dev tree? */ +#define MUC_FIRMWARE_NAME "muc_firmware.zip" +#define MUC_FW_PAYLOAD_SIZE 1022 + +enum { NORMAL = 0, BP_TOOLS, FACTORY, QCOM, RECOVERY }; +struct boot_mode_t { + uint8_t boot_mode; /* androidboot.mode */ + uint32_t hwid; /* androidboot.hwrev */ + uint8_t ap_guid[16]; /* ro.mot.build.guid */ + uint8_t ap_fw_ver_str[256]; /* ro.build.fingerprint */ + uint32_t muc_fw_vers; /* ro.mot.build.version.mod.nuttx */ +} __packed; + +enum { VBUS_IN = 0, VBUS_IN_SPLIT, VBUS_OUT, DC_IN }; +struct power_control_t { + uint8_t flow; + uint32_t voltage_uv; + int32_t current_ua; +} __packed; + +enum { USB_SECONDARY_DISABLE = 0, USB_SECONDARY_ENABLE }; +struct usb_control_t { + uint8_t cmd; +} __packed; + +struct power_status_t { + bool reverse_boost; /* PMI855 Output Enabled. */ + bool dc_in; /* DC IN Enabled. */ + bool charging; /* Battery Charging. */ + bool charger; /* MOD USBC Attached. */ + bool vbus; /* VBUS Present */ + int16_t battery_temp; /* Degrees Celsius */ + uint16_t battery_capacity; /* % Capacity */ + uint16_t battery_max_capacity; /* mAhr */ + uint32_t battery_voltage; /* uV */ + int32_t battery_current; /* uA */ + uint32_t battery_max_voltage; /* uV */ + uint32_t mod_output_voltage; /* uV */ + uint32_t mod_input_voltage; /* uV */ + uint32_t mod_output_current; /* uA */ + uint32_t mod_input_current; /* uA */ +} __packed; + +#endif /* MODD_MUC_PROTOCOL_H */ diff --git a/drivers/mmi_muc_uart/muc_uart.c b/drivers/mmi_muc_uart/muc_uart.c new file mode 100644 index 000000000000..f2f24bf7a5ff --- /dev/null +++ b/drivers/mmi_muc_uart/muc_uart.c @@ -0,0 +1,1833 @@ +/* Copyright (c) 2018, Motorola Mobility LLC. 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 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "muc_uart.h" +#include "mmi_uart.h" +#include "mmi_char.h" +#include "muc_protocol.h" + +#ifdef CONFIG_IPC_LOGGING +void *muc_ipc_log; +#define MUC_IPC_LOG_PAGES 50 +#define MUC_DBG(msg, ...) \ + do { \ + if (muc_ipc_log) { \ + ipc_log_string(muc_ipc_log, \ + "[%s] " msg, __func__, ##__VA_ARGS__); \ + } \ + } while (0) +#define MUC_ERR(msg, ...) \ + do { \ + MUC_DBG(msg, ##__VA_ARGS__); \ + pr_err("muc_uart: [%s] "msg, __func__, ##__VA_ARGS__); \ + } while (0) +#define MUC_LOG(msg, ...) \ + do { \ + MUC_DBG(msg, ##__VA_ARGS__); \ + pr_info("muc_uart: [%s] "msg, __func__, ##__VA_ARGS__); \ + } while (0) +#else +#define MUC_DBG(msg, ...) \ + pr_info("muc_uart: [%s] "msg, __func__, ##__VA_ARGS__) +#define MUC_ERR(msg, ...) \ + pr_err("muc_uart: [%s] "msg, __func__, ##__VA_ARGS__) +#define MUC_LOG(msg, ...) \ + pr_info("muc_uart: [%s] "msg, __func__, ##__VA_ARGS__) +#endif + +#define DRIVERNAME "muc_uart" + +/* Go into low power mode on a sleep request by the muc. Side effects are that + * we might return -EAGAIN on a transmit, and will be slower to wake up + * when the muc sends the next message + */ +#define MUC_UART_DISABLE_ON_SLEEP 1 + +/* Initiate a sleep request after a timeout if the muc hasn't requested + * it already + */ +#define MUC_UART_SEND_SLEEP_ON_IDLE 1 +#define MUC_UART_IDLE_TIME 1000 /* ms */ + +/* When a sleep request on occurs either to or from the muc, sending + * is blocked. If the sleep request never finishes for some reason + * we will be stuck forever. Use this value for a timer to clear + * the request eventually to try to maybe recover or panic. + */ +#define MUC_UART_SLEEP_REQ_TIMEOUT 500 /* ms */ + +/* How many times to try to send a message before giving up */ +#define MUC_UART_RETRY_COUNT 100 + +/* Max number of chars we will copy from the fw vers sent by the muc */ +#define MUC_FW_VERS_MAX_SIZE 32 + +/* Should match EXT_USB_CHECK_TIME in dwc3-msm.c */ +#define EXT_USB_CHECK_TIME 3000 + +struct write_data { + struct list_head list; + uint16_t cmd; + uint8_t *payload; + size_t payload_length; + uint8_t retries; +}; + +struct read_data { + struct list_head list; + size_t payload_length; + uint8_t *payload; +}; + +struct mod_muc_data_t { + struct platform_device *pdev; + void *uart_data; + int wake_out_gpio; + int wake_in_gpio; + int wake_irq; + int muc_1_gpio; + int muc_2_gpio; + int mod_attached_gpio; + int mod_attached_irq; + int uart_pm_state; + atomic_t suspend_ok; + atomic_t waiting_for_ack; + struct timer_list idle_timer; + struct timer_list ack_timer; + struct timer_list sleep_req_timer; + atomic_t mod_attached; + atomic_t mod_online; + struct workqueue_struct *write_wq; + struct write_data *write_data_q; + int write_q_size; + struct read_data *read_data_q; + int read_q_size; + wait_queue_head_t *read_wq; + atomic_t write_credits; + struct delayed_work wake_work; + struct delayed_work sleep_work; + struct delayed_work idle_work; + struct delayed_work attach_work; + struct delayed_work write_work; + struct delayed_work connect_work; + struct work_struct ps_notify_work; + struct power_supply *phone_psy; + struct power_supply *batt_psy; + struct power_supply *bms_psy; + struct power_supply *usb_psy; + struct power_supply *mmi_psy; + struct power_supply *dc_psy; + atomic_t sleep_req_pending; + struct notifier_block ps_nb; + char muc_fw_vers[MUC_FW_VERS_MAX_SIZE]; +}; + +/* How long until we move on without an ack */ +#define MUC_UART_ACK_TIME 2000 +#define WRITE_CREDIT_INIT 100 +static DEFINE_SPINLOCK(write_q_lock); +static DEFINE_SPINLOCK(read_q_lock); + +/* Share tx between acks and send queue */ +DEFINE_MUTEX(tx_lock); + +static char *param_guid = ""; +module_param(param_guid, charp, S_IRUSR); +MODULE_PARM_DESC(param_guid, "ro.mot.build.guid"); + +static char *param_vers = ""; +module_param(param_vers, charp, S_IRUSR); +MODULE_PARM_DESC(param_vers, "ro.build.fingerprint"); + +static char *param_mucfw = ""; +module_param(param_mucfw, charp, S_IRUSR); +MODULE_PARM_DESC(param_mucfw, "ro.mot.build.version.mod.nuttx"); + +static int muc_uart_config_gpio(struct device *dev, + int gpio, char *name, int dir_out, int out_val); + +#if MUC_UART_SEND_SLEEP_ON_IDLE +static inline void reset_idle_timer(struct mod_muc_data_t *mm_data) +{ + cancel_delayed_work(&mm_data->idle_work); + if (mm_data) + mod_timer(&mm_data->idle_timer, + jiffies + msecs_to_jiffies(MUC_UART_IDLE_TIME)); +} +#else +static inline void reset_idle_timer(struct mod_muc_data_t *mm_data) {} +#endif + +static inline void set_wait_for_ack(struct mod_muc_data_t *mm_data, + int cmd) +{ + if (mm_data) + if (!(cmd & MSG_ACK_MASK || + cmd & MSG_NACK_MASK || + cmd == UART_SLEEP_REQ || + cmd == UART_SLEEP_REJ)) { + atomic_set(&mm_data->waiting_for_ack, 1); + mod_timer(&mm_data->ack_timer, jiffies + + msecs_to_jiffies(MUC_UART_ACK_TIME)); + } +} + +static inline void clear_wait_for_ack(struct mod_muc_data_t *mm_data) +{ + if (mm_data) { + del_timer(&mm_data->ack_timer); + atomic_set(&mm_data->waiting_for_ack, 0); + atomic_add_unless(&mm_data->write_credits, 1, 100); + MUC_DBG("got an ack, num credits: %d\n", + atomic_read(&mm_data->write_credits)); + cancel_delayed_work(&mm_data->write_work); + queue_delayed_work(mm_data->write_wq, + &mm_data->write_work, + msecs_to_jiffies(0)); + } +} + +/* 1 if the mod is attached to the phone, 0 otherwise */ +static ssize_t mod_attached_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int gpio_state = 0; + struct platform_device *pdev = to_platform_device(dev); + struct mod_muc_data_t *mm_data = platform_get_drvdata(pdev); + + if (mm_data) + gpio_state = atomic_read(&mm_data->mod_attached); + return snprintf(buf, 4, "%u\n", gpio_state); +} + +static ssize_t muc_fw_vers_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mod_muc_data_t *mm_data = platform_get_drvdata(pdev); + + if (mm_data && mm_data->muc_fw_vers[0] != '\0') + return snprintf(buf, strlen(mm_data->muc_fw_vers) + 1, + "%s", mm_data->muc_fw_vers); + + return 0; +} + +/* /sys/devices/soc:mod_uart/mod_attached */ +DEVICE_ATTR_RO(mod_attached); +DEVICE_ATTR_RO(muc_fw_vers); + +static struct attribute *muc_uart_attrs[] = { + &dev_attr_mod_attached.attr, + &dev_attr_muc_fw_vers.attr, + NULL +}; + +ATTRIBUTE_GROUPS(muc_uart); + +static void muc_uart_attach_work(struct work_struct *w) +{ + struct mod_muc_data_t *mm_data = + container_of(w, struct mod_muc_data_t, attach_work.work); + struct kobj_uevent_env *env; + int gpio_state; + + if (mm_data) { + gpio_state = gpio_get_value(mm_data->mod_attached_gpio); + + if (atomic_read(&mm_data->mod_attached) == gpio_state) + return; + + atomic_set(&mm_data->mod_attached, gpio_state); + + env = kzalloc(sizeof(*env), GFP_KERNEL); + if (!env) + return; + + if (gpio_state) + add_uevent_var(env, "MOD_EVENT=ATTACHED"); + else + add_uevent_var(env, "MOD_EVENT=DETACHED"); + + kobject_uevent_env(&mm_data->pdev->dev.kobj, + KOBJ_CHANGE, + env->envp); + kfree(env); + + /* also make sure to notify the drivers of the attach/detach */ + power_supply_changed(mm_data->phone_psy); + } +} + +static irqreturn_t muc_uart_attach_irq_handler(int irq, void *data) +{ + struct mod_muc_data_t *mm_data = (struct mod_muc_data_t *)data; + + cancel_delayed_work(&mm_data->attach_work); + schedule_delayed_work(&mm_data->attach_work, msecs_to_jiffies(0)); + return IRQ_HANDLED; +} + +static void muc_uart_connect_work(struct work_struct *w) +{ + struct mod_muc_data_t *mm_data = + container_of(w, struct mod_muc_data_t, connect_work.work); + struct kobj_uevent_env *env; + + if (mm_data) { + env = kzalloc(sizeof(*env), GFP_KERNEL); + if (!env) + return; + + if (atomic_read(&mm_data->mod_online)) + add_uevent_var(env, "MOD_EVENT=ONLINE"); + else + add_uevent_var(env, "MOD_EVENT=OFFLINE"); + + kobject_uevent_env(&mm_data->pdev->dev.kobj, + KOBJ_CHANGE, + env->envp); + kfree(env); + } +} + +static inline void set_sleep_req_pending(struct mod_muc_data_t *mm_data) +{ + atomic_set(&mm_data->sleep_req_pending, 1); + mod_timer(&mm_data->sleep_req_timer, jiffies + + msecs_to_jiffies(MUC_UART_SLEEP_REQ_TIMEOUT)); +} + +static inline void clear_sleep_req_pending(struct mod_muc_data_t *mm_data) +{ + del_timer(&mm_data->sleep_req_timer); + atomic_set(&mm_data->sleep_req_pending, 0); +} + +static int muc_uart_wake_muc(struct mod_muc_data_t *mm_data) +{ + gpio_direction_output(mm_data->wake_out_gpio, 0); + + /* Only toggle the line if the muc isn't awake */ + if (gpio_get_value(mm_data->wake_in_gpio)) { + gpio_set_value(mm_data->wake_out_gpio, 1); + mdelay(1); + gpio_set_value(mm_data->wake_out_gpio, 0); + mdelay(1); + return 1; + } + + /* Return 0 if the muc was already awake */ + return 0; +} + +static int muc_uart_send(struct mod_muc_data_t *mm_data, + uint16_t cmd, + uint8_t *payload, + size_t payload_length) +{ + size_t pkt_size; + uint16_t calc_crc; + uint8_t *pkt; + struct mmi_uart_hdr_t *hdr; + int ret; + + if (!mm_data) + return -ENODEV; + + if (payload_length > UART_MAX_MSG_SIZE - MSG_META_DATA_SIZE) { + mmi_uart_report_tx_err(mm_data->uart_data); + return -E2BIG; + } + + /* If we are waiting on a sleep req */ + if (atomic_read(&mm_data->sleep_req_pending)) { + mmi_uart_report_tx_err(mm_data->uart_data); + return -EALREADY; + } + + /* If sending a sleep req, block further sends until + * we get a rej or go to sleep + */ + if (cmd == UART_SLEEP_REQ) + set_sleep_req_pending(mm_data); + + /* If someone else is sending */ + if (mmi_uart_set_tx_busy(mm_data->uart_data)) { + mmi_uart_report_tx_err(mm_data->uart_data); + return -EBUSY; + } + + /* If we are in low power mode that needs to change + * else just try to wake the muc + */ + if (!mm_data->uart_pm_state) { + cancel_delayed_work(&mm_data->wake_work); + schedule_delayed_work(&mm_data->wake_work, msecs_to_jiffies(0)); + mmi_uart_clear_tx_busy(mm_data->uart_data); + mmi_uart_report_tx_err(mm_data->uart_data); + return -EAGAIN; + } else if (muc_uart_wake_muc(mm_data)) { + mmi_uart_clear_tx_busy(mm_data->uart_data); + mmi_uart_report_tx_err(mm_data->uart_data); + return -EAGAIN; + } + + reset_idle_timer(mm_data); + + pkt_size = sizeof(struct mmi_uart_hdr_t) + + payload_length + sizeof(calc_crc); + pkt = kmalloc(pkt_size, GFP_ATOMIC); + if (!pkt) { + mmi_uart_clear_tx_busy(mm_data->uart_data); + mmi_uart_report_tx_err(mm_data->uart_data); + return -ENOMEM; + } + + /* Populate the packet */ + hdr = (struct mmi_uart_hdr_t *)pkt; + hdr->payload_length = cpu_to_le16(payload_length); + hdr->magic = cpu_to_le16(MSG_MAGIC); + hdr->cmd = cpu_to_le16(cmd); + memcpy(pkt + sizeof(*hdr), payload, payload_length); + calc_crc = crc16(0, pkt, pkt_size - sizeof(calc_crc)); + *(uint16_t *)(pkt + pkt_size - sizeof(calc_crc)) = + cpu_to_le16(calc_crc); + + ret = mmi_uart_send(mm_data->uart_data, pkt, pkt_size); + kfree(pkt); + + mmi_uart_clear_tx_busy(mm_data->uart_data); + + if (ret == 0) { + set_wait_for_ack(mm_data, cmd); + return payload_length; + } else { + /* Just in case we failed to send and we were + * going to sleep + */ + clear_sleep_req_pending(mm_data); + } + + return ret; +} + +/* Any message that needs an ack goes on the queue */ +static int __muc_uart_queue_send(struct mod_muc_data_t *mm_data, + uint16_t cmd, + uint8_t *payload, + size_t payload_len, + bool payload_persistant) +{ + struct write_data *write_data; + unsigned long flags; + + if (payload_len > UART_MAX_MSG_SIZE - MSG_META_DATA_SIZE) + return -E2BIG; + + /* TODO this should probably panic */ + if (atomic_dec_if_positive(&mm_data->write_credits) < 0) + MUC_ERR("out of credits! " + "queue size is: %d\n", mm_data->write_q_size); + + write_data = kmalloc(sizeof(*write_data), GFP_ATOMIC); + if (WARN_ON(!write_data)) { + atomic_inc(&mm_data->write_credits); + return -ENOMEM; + } + + if(!payload_persistant) { + write_data->payload = kmalloc(payload_len, GFP_ATOMIC); + /* TODO this should probably panic */ + if (WARN_ON(!write_data->payload)) { + atomic_inc(&mm_data->write_credits); + kfree(write_data); + return -ENOMEM; + } + memcpy(write_data->payload, payload, payload_len); + } else + write_data->payload = payload; + + INIT_LIST_HEAD(&write_data->list); + write_data->payload_length = payload_len; + write_data->cmd = cmd; + write_data->retries = MUC_UART_RETRY_COUNT; + + spin_lock_irqsave(&write_q_lock, flags); + if (!mm_data->write_data_q) + mm_data->write_data_q = write_data; + else + list_add_tail(&write_data->list, + &mm_data->write_data_q->list); + mm_data->write_q_size++; + spin_unlock_irqrestore(&write_q_lock, flags); + + if (!atomic_read(&mm_data->waiting_for_ack)) { + cancel_delayed_work(&mm_data->write_work); + queue_delayed_work(mm_data->write_wq, + &mm_data->write_work, + msecs_to_jiffies(0)); + } + + return payload_len; +} + +static int muc_uart_queue_send(struct mod_muc_data_t *mm_data, + uint16_t cmd, + uint8_t *payload, + size_t payload_len) +{ + return __muc_uart_queue_send(mm_data, cmd, payload, + payload_len, false); +} + +static void muc_uart_send_work(struct work_struct *w) +{ + struct mod_muc_data_t *mm_data = + container_of(w, struct mod_muc_data_t, write_work.work); + struct write_data *write_data = NULL; + unsigned long flags; + int ret; + + if (atomic_read(&mm_data->waiting_for_ack)) { + MUC_DBG("waiting for an ack..."); + return; + } + + MUC_DBG("num credits remaining: %d\n", + atomic_read(&mm_data->write_credits)); + + spin_lock_irqsave(&write_q_lock, flags); + if (mm_data->write_data_q) { + write_data = mm_data->write_data_q; + if (list_is_last(&write_data->list, + &mm_data->write_data_q->list)) + mm_data->write_data_q = NULL; + else { + mm_data->write_data_q = + list_next_entry(write_data, list); + list_del(&write_data->list); + } + mm_data->write_q_size--; + } + spin_unlock_irqrestore(&write_q_lock, flags); + + if (!write_data) { + return; + } + + /* Wake the muc up here if it isn't already awake + * Send will wake it too, but hopefully this will + * reduce the chances it returns an error. + * + * Also wake up the uart if it is in low power mode. + */ + mmi_uart_set_tx_busy(mm_data->uart_data); + if (!mm_data->uart_pm_state) { + mmi_uart_do_pm(mm_data->uart_data, true); + mm_data->uart_pm_state = 1; + } + mmi_uart_clear_tx_busy(mm_data->uart_data); + muc_uart_wake_muc(mm_data); + + mutex_lock(&tx_lock); + ret = muc_uart_send(mm_data, + write_data->cmd, + write_data->payload, + write_data->payload_length); + mutex_unlock(&tx_lock); + + if (ret < 0) { + MUC_DBG("failed to send: %d\n", ret); + if (write_data->retries) { + write_data->retries--; + if (!write_data->retries) { + MUC_ERR("failed to send message %d\n", ret); + atomic_add_unless(&mm_data->write_credits, 1, 100); + goto destroy_msg; + } + } + + /* Put back on top of queue to try later*/ + INIT_LIST_HEAD(&write_data->list); + spin_lock_irqsave(&write_q_lock, flags); + if (mm_data->write_data_q) + list_add_tail(&write_data->list, + &mm_data->write_data_q->list); + mm_data->write_data_q = write_data; + mm_data->write_q_size++; + spin_unlock_irqrestore(&write_q_lock, flags); + + cancel_delayed_work(&mm_data->write_work); + queue_delayed_work(mm_data->write_wq, + &mm_data->write_work, + msecs_to_jiffies(10)); + } else + goto destroy_msg; + + return; +destroy_msg: + kfree(write_data->payload); + kfree(write_data); +} + +static int muc_uart_send_bootmode(struct mod_muc_data_t *mm_data) +{ + struct boot_mode_t bootmode; + uint32_t mucfw_major; + uint32_t mucfw_minor; + + memset(&bootmode, 0x00, sizeof(struct boot_mode_t)); + + bootmode.hwid = bi_hwrev(); + + strncpy(bootmode.ap_guid, + param_guid, + sizeof(bootmode.ap_guid)); + strncpy(bootmode.ap_fw_ver_str, + param_vers, + sizeof(bootmode.ap_fw_ver_str)); + + /* Format mucfw vers from "MAJOR.MINOR_DESCRIPTION" + * to uint32_t in format MAJOR<<16|MINOR + */ + if (sscanf(param_mucfw, "%d.%d%*s", &mucfw_major, &mucfw_minor) != 2) { + mucfw_major = 0; + mucfw_minor = 0; + } + + bootmode.muc_fw_vers = (mucfw_major << 16) | mucfw_minor; + + if (strncmp(bi_bootmode(), "mot-factory", strlen("mot-factory")) == 0) { + bootmode.boot_mode = FACTORY; + } else if (strncmp(bi_bootmode(), "qcom", strlen("qcom")) == 0) { + bootmode.boot_mode = QCOM; + } else if (strncmp(bi_bootmode(), "bp-tools", strlen("bp-tools")) == 0) { + bootmode.boot_mode = BP_TOOLS; + } else if (strncmp(bi_bootmode(), "recovery", strlen("recovery")) == 0) { + bootmode.boot_mode = RECOVERY; + } else { + /* TODO defaults to normal. Should we have unknown/other? */ + bootmode.boot_mode = NORMAL; + } + + MUC_DBG("sending AP bootmode %u\n", + bootmode.boot_mode); + MUC_DBG("sending AP hwid 0x%x\n", + bootmode.hwid); + MUC_DBG("(%zd) sending AP guid %s\n", + sizeof(bootmode.ap_guid), bootmode.ap_guid); + MUC_DBG("(%zd) sending AP vers %s\n", + sizeof(bootmode.ap_fw_ver_str), bootmode.ap_fw_ver_str); + MUC_DBG("sending MUC fw vers 0x%08x\n", + bootmode.muc_fw_vers); + + return muc_uart_queue_send(mm_data, + BOOT_MODE, + (uint8_t *)&bootmode, + sizeof(struct boot_mode_t)); +} + +#define VBUS_PRESENT_UV 2000000 +static int muc_uart_send_power_status(struct mod_muc_data_t *mm_data) +{ + struct power_status_t pstatus; + union power_supply_propval pval = {0}; + + if(!mm_data->batt_psy || + !mm_data->bms_psy || + !mm_data->usb_psy || + !mm_data->dc_psy) + return -1; + + memset(&pstatus, 0x00, sizeof(struct power_status_t)); + + if (!power_supply_get_property(mm_data->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &pval)) + pstatus.charger = pval.intval ? 1 : 0; + + if (pstatus.charger && + !power_supply_get_property(mm_data->usb_psy, + POWER_SUPPLY_PROP_TYPEC_MODE, &pval) && + (pval.intval == POWER_SUPPLY_TYPEC_NONE)) + pstatus.charger = 0; + + if (!power_supply_get_property(mm_data->batt_psy, + POWER_SUPPLY_PROP_TEMP, &pval)) + /* Div by 10 to convert from deciDegC to DegC */ + pstatus.battery_temp = (int16_t)(pval.intval / 10); + + if (!power_supply_get_property(mm_data->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &pval)) + pstatus.battery_capacity = + pval.intval > 0 ? (uint16_t)pval.intval : 0; + + if (!power_supply_get_property(mm_data->bms_psy, + POWER_SUPPLY_PROP_CHARGE_FULL, &pval)) + /* Div by 1000 to convert from uah to mah */ + pstatus.battery_max_capacity = + pval.intval > 0 ? (uint16_t)(pval.intval / 1000) : 0; + + if (!power_supply_get_property(mm_data->batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval)) + pstatus.battery_voltage = + pval.intval > 0 ? (uint32_t)pval.intval : 0; + + if (!power_supply_get_property(mm_data->batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &pval)) + pstatus.battery_current = (int32_t)pval.intval; + + if (!power_supply_get_property(mm_data->batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval)) + pstatus.battery_max_voltage = + pval.intval > 0 ? (uint32_t)pval.intval : 0; + + if (!power_supply_get_property(mm_data->batt_psy, + POWER_SUPPLY_PROP_STATUS, &pval)) + pstatus.charging = + (pval.intval == POWER_SUPPLY_STATUS_CHARGING); + + if (!power_supply_get_property(mm_data->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_MAX, &pval)) + pstatus.mod_input_voltage = + pval.intval > 0 ? (uint32_t)pval.intval : 0; + if (!power_supply_get_property(mm_data->usb_psy, + POWER_SUPPLY_PROP_HW_CURRENT_MAX, &pval)) + pstatus.mod_input_current = + pval.intval > 0 ? (uint32_t)pval.intval : 0; + + if (!power_supply_get_property(mm_data->dc_psy, + POWER_SUPPLY_PROP_PRESENT, &pval)) + pstatus.dc_in = pval.intval ? 1 : 0; + + if (!power_supply_get_property(mm_data->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval)) + pstatus.vbus = (pval.intval >= VBUS_PRESENT_UV) ? 1 : 0; + + /* TODO */ + /* pstatus.reverse_boost; + * pstatus.mod_output_current; + * pstatus.mod_input_current; + */ + + pr_debug("muc_uart reverse_boost %d\n", + pstatus.reverse_boost); + pr_debug("muc_uart charging %d\n", + pstatus.charging); + pr_debug("muc_uart charger %d\n", + pstatus.charger); + pr_debug("muc_uart battery_temp %hd degC\n", + pstatus.battery_temp); + pr_debug("muc_uart battery_voltage %u uV\n", + pstatus.battery_voltage); + pr_debug("muc_uart battery_current %d uA\n", + pstatus.battery_current); + pr_debug("muc_uart battery_max_voltage %u uV\n", + pstatus.battery_max_voltage); + pr_debug("muc_uart battery_capacity %hu percent\n", + pstatus.battery_capacity); + pr_debug("muc_uart battery_max_capacity %hu mAhr\n", + pstatus.battery_max_capacity); + pr_debug("muc_uart mod_output_voltage %u uV\n", + pstatus.mod_output_voltage); + pr_debug("muc_uart mod_input_voltage %u uV\n", + pstatus.mod_input_voltage); + pr_debug("muc_uart mod_output_current %u uA\n", + pstatus.mod_output_current); + pr_debug("muc_uart mod_input_current %u uA\n", + pstatus.mod_input_current); + + return muc_uart_queue_send(mm_data, + POWER_STATUS, + (uint8_t *)&pstatus, + sizeof(struct power_status_t)); +} + +static const char *pctrl_names[] = { + "VBUS_IN", + "VBUS_IN_SPLIT", + "VBUS_OUT", + "DC_IN", +}; + +static void muc_uart_set_power_control(struct mod_muc_data_t *mm_data, + struct power_control_t *pwrctl) +{ + union power_supply_propval pval = {0}; + + if (!mm_data || !pwrctl || !mm_data->mmi_psy) { + MUC_ERR("No pwrctrl\n"); + return; + } + + MUC_DBG("flow %s, voltage %u, current %u\n", + pctrl_names[pwrctl->flow], + pwrctl->voltage_uv, + pwrctl->current_ua); + + if (pwrctl->flow == VBUS_OUT) { + pval.intval = 1; + power_supply_set_property(mm_data->mmi_psy, + POWER_SUPPLY_PROP_USB_OTG, + &pval); + } else { + pval.intval = 0; + power_supply_set_property(mm_data->mmi_psy, + POWER_SUPPLY_PROP_USB_OTG, + &pval); + } + + if ((pwrctl->flow == VBUS_IN) || (pwrctl->flow == VBUS_IN_SPLIT)) { + pval.intval = pwrctl->current_ua; + power_supply_set_property(mm_data->mmi_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, + &pval); + } +} + +static void muc_uart_ps_notifier_work(struct work_struct *w) +{ + struct mod_muc_data_t *mm_data = + container_of(w, struct mod_muc_data_t, ps_notify_work); + + muc_uart_send_power_status(mm_data); +} + +static int muc_uart_ps_notifier_cb(struct notifier_block *nb, + unsigned long event, + void *v) +{ + struct mod_muc_data_t *mm_data = container_of(nb, + struct mod_muc_data_t, ps_nb); + struct power_supply *psy = v; + + if (!psy || + event != PSY_EVENT_PROP_CHANGED || + (strcmp(psy->desc->name, "usb") != 0 && + strcmp(psy->desc->name, "bms") != 0 && + strcmp(psy->desc->name, "battery") != 0)) + return NOTIFY_OK; + + schedule_work(&mm_data->ps_notify_work); + + return NOTIFY_OK; +} + +int muc_uart_pb_register_wq(struct platform_device *pdev, + wait_queue_head_t *read_wq) +{ + struct mod_muc_data_t *mm_data = + platform_get_drvdata(pdev); + + /* Only allow the first one who registered */ + if(mm_data->read_wq) + return 1; + + mm_data->read_wq = read_wq; + return 0; +} + +int muc_uart_pb_get_q(struct platform_device *pdev, + char __user *buf, + size_t *count) +{ + struct mod_muc_data_t *mm_data = + platform_get_drvdata(pdev); + + unsigned long flags; + struct read_data *pb_msg = NULL; + + spin_lock_irqsave(&read_q_lock, flags); + if (mm_data->read_data_q) { + pb_msg = mm_data->read_data_q; + if(list_is_last(&pb_msg->list, + &mm_data->read_data_q->list)) + mm_data->read_data_q = NULL; + else { + mm_data->read_data_q = + list_next_entry(pb_msg, list); + list_del(&pb_msg->list); + } + mm_data->read_q_size--; + } + spin_unlock_irqrestore(&read_q_lock, flags); + + if(!pb_msg) + return -ENOMSG; + + if(pb_msg->payload_length > *count) + return -ENOSPC; + + if(copy_to_user(buf, pb_msg->payload, pb_msg->payload_length)) + return -EFAULT; + + *count = pb_msg->payload_length; + + kfree(pb_msg->payload); + kfree(pb_msg); + + return 0; +} + +int muc_uart_pb_get_q_size(struct platform_device *pdev) +{ + struct mod_muc_data_t *mm_data = + platform_get_drvdata(pdev); + + return mm_data->read_q_size; +} + +static int muc_uart_pb_put_q(struct mod_muc_data_t *mm_data, + size_t payload_length, + uint8_t *payload) +{ + unsigned long flags; + struct read_data *pb_msg; + + if(!payload_length) + return 1; + + /* TODO should this list have a size limit? + * otherwise a crash of the consumer could + * potentially crash the kernel ... + */ + + /* Set up the message */ + pb_msg = kmalloc(sizeof(struct read_data), GFP_KERNEL); + if (!pb_msg) + return -ENOMEM; + + pb_msg->payload = kmalloc(payload_length, GFP_KERNEL); + if (!pb_msg->payload) { + kfree(pb_msg); + return -ENOMEM; + } + + INIT_LIST_HEAD(&pb_msg->list); + pb_msg->payload_length = payload_length; + memcpy(pb_msg->payload, payload, payload_length); + + spin_lock_irqsave(&read_q_lock, flags); + if (!mm_data->read_data_q) + mm_data->read_data_q = pb_msg; + else + list_add_tail(&pb_msg->list, + &mm_data->read_data_q->list); + mm_data->read_q_size++; + spin_unlock_irqrestore(&read_q_lock, flags); + + /* Wake up any process waiting on a message */ + if(mm_data->read_wq) + wake_up(mm_data->read_wq); + + return 0; +} + +static int muc_uart_send_firmware(struct mod_muc_data_t *mm_data) +{ + struct mmi_uart_fw_t *fw_pkt; + const struct firmware *fw = NULL; + int fw_data_remaining; + int fw_data_offset = 0; + uint16_t fw_pkt_sn = 1; + uint16_t fw_pkt_len; + uint16_t fw_payload_len; + + MUC_LOG("Updating muc firmware\n"); + + if (request_firmware(&fw, MUC_FIRMWARE_NAME, &mm_data->pdev->dev)) { + MUC_ERR("Unable to locate firmware %s!\n", MUC_FIRMWARE_NAME); + goto err; + } + + MUC_LOG("Firmware size is %zd bytes\n", fw->size); + MUC_LOG("Queuing %zd messages for upload\n", + (fw->size % MUC_FW_PAYLOAD_SIZE) > 0 ? + (fw->size / MUC_FW_PAYLOAD_SIZE) + 2 : + (fw->size / MUC_FW_PAYLOAD_SIZE) + 1); + + fw_pkt = kmalloc(sizeof(struct mmi_uart_fw_t) + + MUC_FW_PAYLOAD_SIZE, GFP_KERNEL); + if (!fw_pkt) { + MUC_ERR("Unable allocate firmware packet\n"); + goto err_mem; + } + + fw_pkt->port_id = PACKETBUS_PORT_MUC_FW; + fw_data_remaining = fw->size; + + while (fw_data_remaining > 0) { + if (fw_data_remaining >= MUC_FW_PAYLOAD_SIZE) + fw_payload_len = MUC_FW_PAYLOAD_SIZE; + else + fw_payload_len = fw_data_remaining; + + memcpy(fw_pkt->payload, + fw->data + fw_data_offset, + fw_payload_len); + + fw_pkt_len = sizeof(struct mmi_uart_fw_t) + fw_payload_len; + fw_pkt->sn = fw_pkt_sn++; + + if (muc_uart_queue_send(mm_data, + PACKETBUS_PROT_MSG, + (uint8_t *)fw_pkt, + fw_pkt_len) != fw_pkt_len) + goto err_pkt; + + fw_data_offset += fw_payload_len; + fw_data_remaining -= fw_payload_len; + } + + /* Last packet empty payload to indicate EOF */ + fw_pkt_len = sizeof(struct mmi_uart_fw_t); + fw_pkt->sn = fw_pkt_sn; + + if (muc_uart_queue_send(mm_data, + PACKETBUS_PROT_MSG, + (uint8_t *)fw_pkt, + fw_pkt_len) != fw_pkt_len) + goto err_pkt; + + kfree(fw_pkt); + release_firmware(fw); + + return 0; +err_pkt: + kfree(fw_pkt); +err_mem: + release_firmware(fw); +err: + return 1; +} + +static int muc_uart_check_update(struct mod_muc_data_t *mm_data, + size_t payload_len, + uint8_t *payload) +{ + struct mmi_uart_pb_hdr_t *pb_hdr; + + /* Check if this message was for us instead of userspace */ + if (payload_len < sizeof(struct mmi_uart_pb_hdr_t)) + return 1; + + pb_hdr = (struct mmi_uart_pb_hdr_t *)payload; + + if (pb_hdr->port_id != PACKETBUS_PORT_MUC_FW) + return 1; + + if (pb_hdr->cmd != BOLT_MSG_GET_MUC_FW) + return 1; + + if (muc_uart_send_firmware(mm_data)) + MUC_ERR("Failed to send firmware to muc\n"); + + return 0; +} + +int muc_uart_pb_write(struct platform_device *pdev, + const char __user *buf, + size_t count) +{ + uint8_t *payload; + struct mod_muc_data_t *mm_data = + platform_get_drvdata(pdev); + + /* Payload is freed after send work is done */ + payload = kmalloc(count, GFP_KERNEL); + if (!payload) + return -ENOMEM; + + if (copy_from_user(payload, buf, count)) + return -EFAULT; + + return __muc_uart_queue_send(mm_data, PACKETBUS_PROT_MSG, + payload,count, true); +} + +static int muc_uart_set_gpio(struct device *dev, int value) +{ + /* payload: 1 byte, output value */ + pr_info("muc_uart_set_gpio out value:%d\n", value); + + muc_uart_config_gpio(dev, MOD_ATTACH_GPIO, "mod_attach", 1, value); + gpio_set_value(MOD_ATTACH_GPIO, value); + + gpio_set_value(MUC_GPIO_1, value); + + muc_uart_config_gpio(dev, FORCE_USB_BOOT_GPIO, "force_usb_boot", 1, value); + gpio_set_value(FORCE_USB_BOOT_GPIO, value); + + return 0; +} + +static int muc_uart_handle_usb_ctrl(struct mod_muc_data_t *mm_data, + struct usb_control_t *usb_ctrl_pkt) +{ + if (!mm_data) + return 1; + + if (usb_ctrl_pkt->cmd != USB_SECONDARY_DISABLE && + usb_ctrl_pkt->cmd != USB_SECONDARY_ENABLE) + return 1; + + atomic_set(&mm_data->mod_online, usb_ctrl_pkt->cmd); + power_supply_changed(mm_data->phone_psy); + + return 0; +} + +static void muc_uart_handle_message(struct mod_muc_data_t *mm_data, + struct mmi_uart_hdr_t *hdr, + uint8_t *payload, + size_t payload_len) +{ + unsigned long flags; + + MUC_DBG("rxed msg of type 0x%x.\n", hdr->cmd); + switch (hdr->cmd) { + case UART_SLEEP_REQ: { + spin_lock_irqsave(&write_q_lock, flags); + if (mm_data->write_data_q) + muc_uart_send(mm_data, + UART_SLEEP_REJ, + NULL, 0); + else { + set_sleep_req_pending(mm_data); + schedule_delayed_work(&mm_data->sleep_work, msecs_to_jiffies(0)); + } + spin_unlock_irqrestore(&write_q_lock, flags); + break; + } + case UART_SLEEP_REJ: { + clear_sleep_req_pending(mm_data); + reset_idle_timer(mm_data); + break; + } + case PACKETBUS_PROT_MSG: { + if (!muc_uart_check_update(mm_data, + payload_len, + payload) || + !muc_uart_pb_put_q(mm_data, + payload_len, + payload)) { + muc_uart_send(mm_data, + PACKETBUS_PROT_MSG|MSG_ACK_MASK, + NULL, 0); + } else { + MUC_ERR("Couldn't add packet to pb q!\n"); + muc_uart_send(mm_data, + PACKETBUS_PROT_MSG|MSG_NACK_MASK, + NULL, 0); + } + break; + } + case PACKETBUS_PROT_MSG | MSG_ACK_MASK: { + clear_wait_for_ack(mm_data); + break; + } + case PACKETBUS_PROT_MSG | MSG_NACK_MASK: { + clear_wait_for_ack(mm_data); + break; + } + case POWER_STATUS: { + if (muc_uart_send_power_status(mm_data) >= 0) + muc_uart_send(mm_data, POWER_STATUS|MSG_ACK_MASK, NULL, 0); + else + muc_uart_send(mm_data, POWER_STATUS|MSG_NACK_MASK, NULL, 0); + break; + } + case POWER_CONTROL: { + /* TODO nack if size is wrong ? */ + if (payload_len == sizeof(struct power_control_t)) + muc_uart_set_power_control(mm_data, + (struct power_control_t *)payload); + muc_uart_send(mm_data, hdr->cmd|MSG_ACK_MASK, NULL, 0); + break; + } + case BOOT_MODE: { + if (muc_uart_send_bootmode(mm_data) >= 0) + muc_uart_send(mm_data, BOOT_MODE|MSG_ACK_MASK, NULL, 0); + else + muc_uart_send(mm_data, BOOT_MODE|MSG_NACK_MASK, NULL, 0); + break; + } + case MUC_FW_VERSION: { + if (payload_len) { + muc_uart_send(mm_data, MUC_FW_VERSION|MSG_ACK_MASK, NULL, 0); + MUC_LOG("muc_fw_ver: %.*s\n", + (int)payload_len, + (char *)payload); + memset(mm_data->muc_fw_vers, 0, MUC_FW_VERS_MAX_SIZE); + /* Last value of muc_fw_vers should be null char */ + memcpy(mm_data->muc_fw_vers, payload, + payload_len < (MUC_FW_VERS_MAX_SIZE - 1) ? + payload_len : (MUC_FW_VERS_MAX_SIZE - 1)); + } else + muc_uart_send(mm_data, MUC_FW_VERSION|MSG_NACK_MASK, NULL, 0); + break; + } + case MUC_SET_GPIO: { + if (payload_len != 1) { + pr_info("muc_set_gpio invalid payload_len: %d\n", (int)payload_len); + muc_uart_send(mm_data, MUC_SET_GPIO|MSG_NACK_MASK, NULL, 0); + break; + } + + if (muc_uart_set_gpio(&mm_data->pdev->dev, (int)payload[0]) == 0) { + muc_uart_send(mm_data, MUC_SET_GPIO|MSG_ACK_MASK, NULL, 0); + } else { + muc_uart_send(mm_data, MUC_SET_GPIO|MSG_NACK_MASK, NULL, 0); + } + break; + } + case USB_CONTROL: { + /* TODO nack if size is wrong ? */ + if (payload_len == sizeof(struct usb_control_t)) + muc_uart_handle_usb_ctrl(mm_data, + (struct usb_control_t *)payload); + muc_uart_send(mm_data, hdr->cmd|MSG_ACK_MASK, NULL, 0); + break; + } + case MUC_DEBUG_MSG: { + if (payload_len) { + /* Ensure null terminated */ + payload[payload_len - 1] = '\0'; + MUC_LOG("muc_dbg_msg: %s\n", (char *)payload); + } + muc_uart_send(mm_data, hdr->cmd|MSG_ACK_MASK, NULL, 0); + break; + } + default: { + if (hdr->cmd & MSG_ACK_MASK) + clear_wait_for_ack(mm_data); + else if (hdr->cmd & MSG_NACK_MASK) + clear_wait_for_ack(mm_data); + else { + MUC_ERR("Unhandled type %d\n", hdr->cmd); + muc_uart_send(mm_data, hdr->cmd|MSG_NACK_MASK, NULL, 0); + } + } + } +} + +static size_t muc_uart_rx_cb(struct platform_device *pdev, + uint8_t *data, + size_t len) +{ + int i; + size_t content_size; + size_t segment_size; + struct mmi_uart_hdr_t *hdr; + uint16_t calc_crc; + uint16_t rcvd_crc; + struct mod_muc_data_t *mm_data = + platform_get_drvdata(pdev); + + reset_idle_timer(mm_data); + + /* Need at least a header to start */ + if (len < sizeof(*hdr)) + return 0; + + hdr = (struct mmi_uart_hdr_t *)data; + + /* Verify the magic number */ + if (le16_to_cpu(hdr->magic) != MSG_MAGIC) { + MUC_ERR("invalid magic %x\n", + le16_to_cpu(hdr->magic)); + mmi_uart_report_rx_mag_err(mm_data->uart_data); + + /* Look through the rest of the data we have, + * and try to find a magic number, then throw out + * the bytes in front of it by returning the count + * before it. This function will be re-entered + * and the magic number should now be in front. + */ + for(i = 1; i < (len - 1); i++) { + if (le16_to_cpu(*(uint16_t *)(data + i)) == MSG_MAGIC) { + MUC_LOG("Found valid data at offset %d\n", i); + return i; + } + } + + return i; + } + + segment_size = le16_to_cpu(hdr->payload_length) + + sizeof(*hdr) + sizeof(calc_crc); + + /* Validate the payload size */ + if (le16_to_cpu(hdr->payload_length) > + UART_MAX_MSG_SIZE - MSG_META_DATA_SIZE) { + MUC_ERR("invalid len %zd\n", segment_size); + mmi_uart_report_rx_len_err(mm_data->uart_data); + return sizeof(*hdr); + } + + /* Need the whole message to continue */ + if (len < segment_size) + return 0; + + content_size = segment_size - sizeof(calc_crc); + rcvd_crc = *(uint16_t *)(data + content_size); + calc_crc = crc16(0, data, content_size); + + /* Verify the CRC */ + if (le16_to_cpu(rcvd_crc) != calc_crc) { + mmi_uart_report_rx_crc_err(mm_data->uart_data); + print_hex_dump_debug("muc_uart rx (CRC error): ", + DUMP_PREFIX_OFFSET, + 16, 1, data, content_size, true); + MUC_ERR("CRC mismatch, received: 0x%x, " + "calculated: 0x%x\n", le16_to_cpu(rcvd_crc), calc_crc); + } else { + MUC_DBG("cmd=%x, magic=%x, " + "payload_length=%x, len=%zd\n", + hdr->cmd, hdr->magic, hdr->payload_length, + content_size); + + print_hex_dump_debug("muc_uart rx: ", DUMP_PREFIX_OFFSET, 16, 1, + data, content_size, true); + + /* TODO it still might be possible for a message to go + * out of the queue before we handle the response to a + * request. + */ + mutex_lock(&tx_lock); + muc_uart_handle_message(mm_data, + hdr, + data + sizeof(*hdr), + content_size - sizeof(*hdr)); + mutex_unlock(&tx_lock); + } + + return segment_size; +} + +/* TODO call from power management suspend. */ +static void muc_uart_sleep_work(struct work_struct *w) +{ + struct mod_muc_data_t *mm_data = + container_of(w, struct mod_muc_data_t, sleep_work.work); + + atomic_set(&mm_data->suspend_ok, 1); + del_timer(&mm_data->idle_timer); + cancel_delayed_work_sync(&mm_data->idle_work); + + if (!mm_data || !mm_data->uart_data || + mmi_uart_set_tx_busy(mm_data->uart_data)) + schedule_delayed_work(&mm_data->sleep_work, msecs_to_jiffies(500)); + else { + MUC_DBG("off.\n"); +#if MUC_UART_DISABLE_ON_SLEEP + if (mm_data->uart_pm_state) { + mmi_uart_do_pm(mm_data->uart_data, false); + mm_data->uart_pm_state = 0; + } +#endif + /* Set to high Z / float */ + gpio_direction_input(mm_data->wake_out_gpio); + clear_sleep_req_pending(mm_data); + mmi_uart_clear_tx_busy(mm_data->uart_data); + } +} + +/* TODO call from power management wake. */ +static void muc_uart_wake_work(struct work_struct *w) +{ + static bool booted = false; + struct mod_muc_data_t *mm_data = + container_of(w, struct mod_muc_data_t, wake_work.work); + + cancel_delayed_work_sync(&mm_data->sleep_work); + atomic_set(&mm_data->suspend_ok, 0); + + reset_idle_timer(mm_data); + + if (!mm_data || !mm_data->uart_data || + mmi_uart_set_tx_busy(mm_data->uart_data)) + schedule_delayed_work(&mm_data->wake_work, msecs_to_jiffies(500)); + else { + MUC_DBG("on.\n"); + + if (!mm_data->uart_pm_state) { + mmi_uart_do_pm(mm_data->uart_data, true); + mm_data->uart_pm_state = 1; + } + + muc_uart_wake_muc(mm_data); + mmi_uart_clear_tx_busy(mm_data->uart_data); + + /* Send boot mode on initial wake */ + if (!booted) { + if (muc_uart_send_bootmode(mm_data) >= 0); + booted = true; + /* Else if there was something in the queue when + * we went to sleep start sending it + */ + } else { + cancel_delayed_work(&mm_data->write_work); + queue_delayed_work(mm_data->write_wq, + &mm_data->write_work, + msecs_to_jiffies(0)); + } + } +} + +static irqreturn_t muc_uart_wake_irq_handler(int irq, void *data) +{ + struct mod_muc_data_t *mm_data = (struct mod_muc_data_t *)data; + + /* 0->1 irq is to sleep, 1->0 irq is to wake */ + if (gpio_get_value(mm_data->wake_in_gpio)) { + if (atomic_read(&mm_data->sleep_req_pending)) { + cancel_delayed_work(&mm_data->sleep_work); + schedule_delayed_work(&mm_data->sleep_work, + msecs_to_jiffies(0)); + } + } else { + cancel_delayed_work(&mm_data->wake_work); + schedule_delayed_work(&mm_data->wake_work, msecs_to_jiffies(0)); + } + + return IRQ_HANDLED; +} + +static void muc_uart_idle_work(struct work_struct *w) +{ + unsigned long flags; + struct mod_muc_data_t *mm_data = + container_of(w, struct mod_muc_data_t, idle_work.work); + + if (!atomic_read(&mm_data->sleep_req_pending)) { + mutex_lock(&tx_lock); + spin_lock_irqsave(&write_q_lock, flags); + if(!mm_data->write_data_q && + muc_uart_send(mm_data, UART_SLEEP_REQ, NULL, 0) < 0) + schedule_delayed_work(&mm_data->idle_work, + msecs_to_jiffies(1000)); + spin_unlock_irqrestore(&write_q_lock, flags); + mutex_unlock(&tx_lock); + } + + reset_idle_timer(mm_data); +} + +static void muc_uart_idle_cb(unsigned long timer_data) +{ + struct mod_muc_data_t *mm_data = + (struct mod_muc_data_t *)timer_data; + + schedule_delayed_work(&mm_data->idle_work, msecs_to_jiffies(0)); +} + +static void muc_uart_ack_cb(unsigned long timer_data) +{ + struct mod_muc_data_t *mm_data = + (struct mod_muc_data_t *)timer_data; + + /* TODO what should I do if I don't get an ack in time? */ + if (mm_data) { + atomic_set(&mm_data->waiting_for_ack, 0); + cancel_delayed_work(&mm_data->write_work); + queue_delayed_work(mm_data->write_wq, + &mm_data->write_work, + msecs_to_jiffies(0)); + } +} + +static void muc_uart_sleep_req_cb(unsigned long timer_data) +{ + struct mod_muc_data_t *mm_data = + (struct mod_muc_data_t *)timer_data; + + /* TODO this should probably panic */ + if (mm_data) { + if(atomic_read(&mm_data->sleep_req_pending)) + atomic_set(&mm_data->sleep_req_pending, 0); + } +} + +static int muc_uart_register_cb(struct platform_device *pdev, + void *uart_data) +{ + struct mod_muc_data_t *mm_data = platform_get_drvdata(pdev); + + if (!mm_data) + return 1; + + mm_data->uart_data = uart_data; + mmi_uart_open(uart_data); + schedule_delayed_work(&mm_data->wake_work, msecs_to_jiffies(0)); + + MUC_LOG("register ok\n"); + + return 0; +} + +static void *muc_uart_get_uart_data(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mod_muc_data_t *mm_data = platform_get_drvdata(pdev); + return mm_data->uart_data; +} + +static int muc_uart_config_gpio(struct device *dev, + int gpio, char *name, int dir_out, int out_val) +{ + int error; + + error = devm_gpio_request(dev, gpio, name); + if (error) { + dev_err(dev, "unable to request gpio [%d]\n", gpio); + goto f_end; + } + if (dir_out == 2) + goto f_end; + if (dir_out == 1) + error = gpio_direction_output(gpio, out_val); + else + error = gpio_direction_input(gpio); + if (error) { + dev_err(dev, "unable to set direction for gpio [%d]\n", gpio); + goto f_end; + } +f_end: + return error; +} + +static enum power_supply_property muc_uart_ps_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, +}; + +static int muc_uart_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mod_muc_data_t *mm_data = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = atomic_read(&mm_data->mod_attached); + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = atomic_read(&mm_data->mod_online); + break; + default: + return -EINVAL; + } + return 0; +} + +static int muc_uart_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct mod_muc_data_t *mm_data = power_supply_get_drvdata(psy); + struct usb_control_t usbctrl_pkt; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: { + schedule_delayed_work(&mm_data->connect_work, + msecs_to_jiffies(EXT_USB_CHECK_TIME)); + + usbctrl_pkt.cmd = val->intval; + muc_uart_queue_send(mm_data, + USB_CONTROL, + (uint8_t *)&usbctrl_pkt, + sizeof(struct usb_control_t)); + break; + } + default: + return -EINVAL; + } + return 0; +} + +static const struct power_supply_desc phone_psy_desc = { + .name = "phone", + .type = POWER_SUPPLY_TYPE_MAIN, + .get_property = muc_uart_get_property, + .set_property = muc_uart_set_property, + .properties = muc_uart_ps_props, + .num_properties = ARRAY_SIZE(muc_uart_ps_props), +}; + +static int muc_uart_probe(struct platform_device *pdev) +{ + struct device_node *np = (&pdev->dev)->of_node; + struct mod_muc_data_t *mm_data; + struct power_supply_config psy_cfg = {}; + +#ifdef CONFIG_IPC_LOGGING + muc_ipc_log = ipc_log_context_create(MUC_IPC_LOG_PAGES, "muc_uart", 0); + if (!muc_ipc_log) + MUC_ERR("Failed to create IPC logging context\n"); +#endif + + MUC_LOG("probe start\n"); + + mm_data = devm_kzalloc(&pdev->dev, sizeof(*mm_data), GFP_KERNEL); + if (!mm_data) + return -ENOMEM; + mm_data->pdev = pdev; + platform_set_drvdata(pdev, mm_data); + psy_cfg.drv_data = mm_data; + + INIT_DELAYED_WORK(&mm_data->wake_work, muc_uart_wake_work); + INIT_DELAYED_WORK(&mm_data->sleep_work, muc_uart_sleep_work); + INIT_DELAYED_WORK(&mm_data->idle_work, muc_uart_idle_work); + INIT_DELAYED_WORK(&mm_data->attach_work, muc_uart_attach_work); + INIT_DELAYED_WORK(&mm_data->write_work, muc_uart_send_work); + INIT_DELAYED_WORK(&mm_data->connect_work, muc_uart_connect_work); + INIT_WORK(&mm_data->ps_notify_work, muc_uart_ps_notifier_work); + + /* Create workqueue for the write work */ + mm_data->write_wq = alloc_workqueue("muc_uart_write", WQ_UNBOUND, 1); + if (!mm_data->write_wq) { + dev_err(&pdev->dev, "Could not create workqueue\n"); + goto err; + } + + atomic_set(&mm_data->write_credits, WRITE_CREDIT_INIT); + + setup_timer(&mm_data->idle_timer, + muc_uart_idle_cb, + (unsigned long)mm_data); + setup_timer(&mm_data->ack_timer, + muc_uart_ack_cb, + (unsigned long)mm_data); + setup_timer(&mm_data->sleep_req_timer, + muc_uart_sleep_req_cb, + (unsigned long)mm_data); + + if (sysfs_create_groups(&pdev->dev.kobj, muc_uart_groups)) { + dev_err(&pdev->dev, "Failed to create sysfs attributes\n"); + goto err_dest_wq; + } + + if (mmi_uart_init(muc_uart_rx_cb, + muc_uart_register_cb, + muc_uart_get_uart_data, + pdev)) + goto err_dest_groups; + + mm_data->wake_out_gpio = of_get_gpio(np, 0); + mm_data->wake_in_gpio = of_get_gpio(np, 1); + mm_data->mod_attached_gpio = of_get_gpio(np, 2); + mm_data->muc_1_gpio = of_get_gpio(np, 3); + mm_data->muc_2_gpio = of_get_gpio(np, 4); + + if (mm_data->wake_out_gpio < 0 || + mm_data->wake_in_gpio < 0 || + mm_data->mod_attached_gpio < 0 || + mm_data->muc_1_gpio < 0 || + mm_data->muc_2_gpio < 0) + goto err_exit_uart; + + mm_data->wake_irq = + gpio_to_irq(mm_data->wake_in_gpio); + mm_data->mod_attached_irq = + gpio_to_irq(mm_data->mod_attached_gpio); + + /* Let wake irq take us out of suspend */ + if (irq_set_irq_wake(mm_data->wake_irq, 1)) + MUC_ERR("Failed to set wake irq as wake source"); + /* Let attach irq take us out of suspend */ + if (irq_set_irq_wake(mm_data->mod_attached_irq, 1)) + MUC_ERR("Failed to set attach irq as wake source"); + + if (devm_request_irq(&pdev->dev, + mm_data->wake_irq, + muc_uart_wake_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + pdev->dev.driver->name, + mm_data)) + goto err_exit_uart; + + if (devm_request_irq(&pdev->dev, + mm_data->mod_attached_irq, + muc_uart_attach_irq_handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + pdev->dev.driver->name, + mm_data)) + goto err_exit_uart; + + if (muc_uart_config_gpio(&pdev->dev, + mm_data->wake_out_gpio, "muc_wake", 1, 1)) + goto err_exit_uart; + + if (muc_uart_config_gpio(&pdev->dev, + mm_data->muc_1_gpio, "muc_1", 1, 0)) + goto err_free_wake_out; + + if (muc_uart_config_gpio(&pdev->dev, + mm_data->muc_2_gpio, "muc_2", 1, 1)) + goto err_free_muc1; + + if (mmi_char_init(UART_MAX_MSG_SIZE - MSG_META_DATA_SIZE, pdev)) + goto err_free_muc2; + + mm_data->phone_psy = devm_power_supply_register(&pdev->dev, + &phone_psy_desc, + &psy_cfg); + if (IS_ERR(mm_data->phone_psy)) { + MUC_ERR("failed: phone power supply register\n"); + goto err_exit_char; + } + + mm_data->batt_psy = power_supply_get_by_name("battery"); + mm_data->bms_psy = power_supply_get_by_name("bms"); + mm_data->usb_psy = power_supply_get_by_name("usb"); + mm_data->mmi_psy = power_supply_get_by_name("mmi_battery"); + mm_data->dc_psy = power_supply_get_by_name("dc"); + + mm_data->ps_nb.notifier_call = muc_uart_ps_notifier_cb; + if (power_supply_reg_notifier(&mm_data->ps_nb)) { + MUC_ERR("couldn't register ps notifier\n"); + goto err_put_ps; + } + + kobject_uevent(&pdev->dev.kobj, KOBJ_ADD); + schedule_delayed_work(&mm_data->attach_work, msecs_to_jiffies(0)); + + MUC_LOG("probe done\n"); + + return 0; + +err_put_ps: + power_supply_put(mm_data->batt_psy); + power_supply_put(mm_data->bms_psy); + power_supply_put(mm_data->usb_psy); + power_supply_put(mm_data->mmi_psy); + power_supply_put(mm_data->dc_psy); +err_exit_char: + mmi_char_exit(); +err_free_muc2: + gpio_free(mm_data->muc_2_gpio); +err_free_muc1: + gpio_free(mm_data->muc_1_gpio); +err_free_wake_out: + gpio_free(mm_data->wake_out_gpio); +err_exit_uart: + mmi_uart_exit(mm_data->uart_data); +err_dest_groups: + sysfs_remove_groups(&pdev->dev.kobj, muc_uart_groups); +err_dest_wq: + del_timer(&mm_data->idle_timer); + del_timer(&mm_data->ack_timer); + del_timer(&mm_data->sleep_req_timer); + destroy_workqueue(mm_data->write_wq); +err: + return 1; +} + +static int muc_uart_remove(struct platform_device *pdev) +{ + struct mod_muc_data_t *mm_data = platform_get_drvdata(pdev); + + irq_set_irq_wake(mm_data->wake_irq, 0); + irq_set_irq_wake(mm_data->mod_attached_irq, 0); + + if (gpio_is_valid(mm_data->wake_in_gpio)) + gpio_free(mm_data->wake_in_gpio); + + if (gpio_is_valid(mm_data->wake_out_gpio)) + gpio_free(mm_data->wake_out_gpio); + + if (gpio_is_valid(mm_data->mod_attached_gpio)) + gpio_free(mm_data->mod_attached_gpio); + + power_supply_put(mm_data->batt_psy); + power_supply_put(mm_data->bms_psy); + power_supply_put(mm_data->usb_psy); + power_supply_put(mm_data->mmi_psy); + power_supply_put(mm_data->dc_psy); + del_timer(&mm_data->idle_timer); + del_timer(&mm_data->ack_timer); + del_timer(&mm_data->sleep_req_timer); + cancel_delayed_work_sync(&mm_data->wake_work); + cancel_delayed_work_sync(&mm_data->sleep_work); + cancel_delayed_work_sync(&mm_data->idle_work); + cancel_delayed_work_sync(&mm_data->attach_work); + cancel_delayed_work_sync(&mm_data->write_work); + cancel_work_sync(&mm_data->ps_notify_work); + destroy_workqueue(mm_data->write_wq); + mmi_uart_exit(mm_data->uart_data); + mmi_char_exit(); + sysfs_remove_groups(&pdev->dev.kobj, muc_uart_groups); + kobject_uevent(&pdev->dev.kobj, KOBJ_REMOVE); + platform_set_drvdata(pdev, NULL); + return 0; +} + +/* TODO define suspend functions */ +static const struct dev_pm_ops muc_uart_dev_pm_ops = { + .runtime_suspend = NULL, + .runtime_resume = NULL, + .runtime_idle = NULL, + .suspend_noirq = NULL, + .resume_noirq = NULL +}; + +static const struct of_device_id muc_uart_match_table[] = { + { .compatible = "mmi,muc-uart"}, + {} +}; + +static struct platform_driver muc_uart_driver = { + .probe = muc_uart_probe, + .remove = muc_uart_remove, + .driver = { + .name = DRIVERNAME, + .pm = &muc_uart_dev_pm_ops, + .of_match_table = muc_uart_match_table, + }, +}; + +int muc_uart_init(void) +{ + MUC_LOG("guid: %s\n", param_guid); + MUC_LOG("vers: %s\n", param_vers); + MUC_LOG("mucfw: %s\n", param_mucfw); + return platform_driver_register(&muc_uart_driver); +} + +void muc_uart_exit(void) +{ + platform_driver_unregister(&muc_uart_driver); +} + +module_init(muc_uart_init); +module_exit(muc_uart_exit); + +MODULE_DESCRIPTION("MMI MuC UART Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mmi_muc_uart/muc_uart.h b/drivers/mmi_muc_uart/muc_uart.h new file mode 100644 index 000000000000..b6d0c36f9f45 --- /dev/null +++ b/drivers/mmi_muc_uart/muc_uart.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2018, Motorola Mobility LLC. 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 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. + * + */ +#ifndef _DRIVERS_MUC_UART_H_ +#define _DRIVERS_MUC_UART_H_ + +int muc_uart_pb_get_q_size(struct platform_device *pdev); +int muc_uart_pb_get_q(struct platform_device *pdev, + char __user *buf, + size_t *count); +int muc_uart_pb_register_wq(struct platform_device *pdev, + wait_queue_head_t *read_wq); +int muc_uart_pb_write(struct platform_device *pdev, + const char __user *buf, + size_t count); + +#endif /* _DRIVERS_MUC_UART_H_ */ diff --git a/drivers/mmi_relay/Android.mk b/drivers/mmi_relay/Android.mk new file mode 100644 index 000000000000..1d574e1facb3 --- /dev/null +++ b/drivers/mmi_relay/Android.mk @@ -0,0 +1,12 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_relay.ko +LOCAL_MODULE_TAGS := optional +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/mmi_relay/Kbuild b/drivers/mmi_relay/Kbuild new file mode 100644 index 000000000000..12e00ae16d00 --- /dev/null +++ b/drivers/mmi_relay/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += mmi_relay.o diff --git a/drivers/mmi_relay/Makefile b/drivers/mmi_relay/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/mmi_relay/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/mmi_relay/mmi_relay.c b/drivers/mmi_relay/mmi_relay.c new file mode 100644 index 000000000000..14a91fe620b7 --- /dev/null +++ b/drivers/mmi_relay/mmi_relay.c @@ -0,0 +1,234 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct _relay_node { + struct list_head list; + union { + struct atomic_notifier_head atomic_head; + struct blocking_notifier_head blocking_head; + }; + int id; + notifier_t type; + notifier_d dev; +}relay_node; + +struct mmi_relay_dev { + struct platform_device *pdev; + struct mutex lock; + relay_node nodes[NUM_TYPES][NUM_DEVS]; + unsigned notifier_nums; +}; +static struct mmi_relay_dev* relay_dev; + +/* + * Informs the registered notifiers about an event + * @type: notification type: atomic blocking + * @dev: notification sender + * @val: value passed to notifier function + */ + +int relay_notifier_fire(notifier_t type, notifier_d dev, unsigned long val, void *v) +{ + int ret = 0; + + if(!relay_dev) { + pr_err("Device is not initialized\n"); + return -EINVAL; + } + + if(type >= NUM_TYPES || dev >= NUM_DEVS) { + pr_err("Relay error type or dev\n"); + return -EPERM; + } + switch(type) { + case ATOMIC: + { + struct atomic_notifier_head* h = &relay_dev->nodes[type][dev].atomic_head; + ret = atomic_notifier_call_chain(h, val, v); + break; + } + case BLOCKING: + { + struct blocking_notifier_head* h = &relay_dev->nodes[type][dev].blocking_head; + ret = blocking_notifier_call_chain(h, val, v); + break; + } + default: + BUG_ON(1); + } + return ret; +} +EXPORT_SYMBOL_GPL(relay_notifier_fire); + +/** + * Register an atomic or blocking notify to receive notifications from dev. + * @dev: notification sender + */ +int relay_register_action(notifier_t type, notifier_d dev, struct notifier_block * nb) +{ + int ret = 0; + + if(!relay_dev) { + pr_err("Device is not initialized\n"); + return -EINVAL; + } + + if(type >= NUM_TYPES || dev >= NUM_DEVS) { + pr_err("Relay error type or dev\n"); + return -EPERM; + } + + mutex_lock(&relay_dev->lock); + switch(type) { + case ATOMIC: + { + struct atomic_notifier_head* h = &relay_dev->nodes[type][dev].atomic_head; + ret = atomic_notifier_chain_register(h, nb); + if (!ret) + relay_notifier_fire(ATOMIC, MMI_RELAY, RELAY_NOTIFY_REGISTER, NULL); + break; + } + case BLOCKING: + { + struct blocking_notifier_head* h = &relay_dev->nodes[type][dev].blocking_head; + ret = blocking_notifier_chain_register(h, nb); + if (!ret) + relay_notifier_fire(BLOCKING, MMI_RELAY, RELAY_NOTIFY_REGISTER, NULL); + + break; + } + default: + BUG_ON(1); + } + mutex_unlock(&relay_dev->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(relay_register_action); + +int relay_unregister_action(notifier_t type, notifier_d dev, struct notifier_block *nb) +{ + int ret = 0; + + if(!relay_dev) { + pr_err("Device is not initialized\n"); + return -EINVAL; + } + + if(type >= NUM_TYPES || dev >= NUM_DEVS) { + pr_err("Relay error type or dev\n"); + return -EPERM; + } + + mutex_lock(&relay_dev->lock); + switch(type) { + case ATOMIC: + { + struct atomic_notifier_head* h = &relay_dev->nodes[type][dev].atomic_head; + ret = atomic_notifier_chain_unregister(h, nb); + if (!ret) + relay_notifier_fire(ATOMIC, MMI_RELAY, RELAY_NOTIFY_UNREGISTER, NULL); + break; + } + case BLOCKING: + { + struct blocking_notifier_head* h = &relay_dev->nodes[type][dev].blocking_head; + ret = blocking_notifier_chain_unregister(h, nb); + if (!ret) + relay_notifier_fire(BLOCKING, MMI_RELAY, RELAY_NOTIFY_UNREGISTER, NULL); + break; + } + default: + BUG_ON(1); + } + mutex_unlock(&relay_dev->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(relay_unregister_action); + +static int mmi_relay_probe(struct platform_device *pdev) +{ + notifier_t i; + notifier_d j; + + relay_dev = kzalloc(sizeof(struct mmi_relay_dev), GFP_KERNEL); + if (!relay_dev) { + pr_err("Failed to allocate memory for relay_dev\n"); + return -ENOMEM; + } + + relay_dev->pdev = pdev; + relay_dev->notifier_nums = NUM_DEVS * NUM_TYPES; + + mutex_init(&relay_dev->lock); + + for(i = (notifier_t)0; i < NUM_TYPES; i++) { + for(j = (notifier_d)0; j < NUM_DEVS; j++) { + relay_dev->nodes[i][j].type = i; + relay_dev->nodes[i][j].dev= j; + relay_dev->nodes[i][j].id = i << 16 | j; + INIT_LIST_HEAD(&relay_dev->nodes[i][j].list); + + if(i == ATOMIC) { + ATOMIC_INIT_NOTIFIER_HEAD(&relay_dev->nodes[i][j].atomic_head); + } else { + BLOCKING_INIT_NOTIFIER_HEAD(&relay_dev->nodes[i][j].blocking_head); + } + } + } + return 0; +} + +static int mmi_relay_remove(struct platform_device *pdev) +{ + kfree(relay_dev); + return 0; +} + +static const struct of_device_id mmi_relay_mt[] = { + {.compatible = "mmi,sys-relay"}, + {}, +}; +MODULE_DEVICE_TABLE(of, mmi_relay_mt); + +static struct platform_driver mmi_relay_driver = { + .probe = mmi_relay_probe, + .remove = mmi_relay_remove, + .driver = { + .name = "mmi_relay", + .owner = THIS_MODULE, + .of_match_table = mmi_relay_mt, + }, +}; + +static int __init mmi_relay_init(void) +{ + return platform_driver_register(&mmi_relay_driver); +} + +static void __exit mmi_relay_exit(void) +{ + platform_driver_unregister(&mmi_relay_driver); +} + +module_init(mmi_relay_init); +module_exit(mmi_relay_exit); + +MODULE_ALIAS("platform:mmi_relay"); +MODULE_AUTHOR("Motorola Mobility LLC"); +MODULE_DESCRIPTION("Motorola Mobility Module Conmmunication Relay"); +MODULE_LICENSE("GPL"); diff --git a/drivers/moto_f_diag/Android.mk b/drivers/moto_f_diag/Android.mk new file mode 100644 index 000000000000..afbcbc81be6c --- /dev/null +++ b/drivers/moto_f_diag/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := moto_f_diag.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/moto_f_diag/Kbuild b/drivers/moto_f_diag/Kbuild new file mode 100644 index 000000000000..38c7c2fbb401 --- /dev/null +++ b/drivers/moto_f_diag/Kbuild @@ -0,0 +1,7 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +moto_f_diag-objs += tty_diag.o +moto_f_diag-objs += f_diag.o +obj-m += moto_f_diag.o \ No newline at end of file diff --git a/drivers/moto_f_diag/Makefile b/drivers/moto_f_diag/Makefile new file mode 100644 index 000000000000..34b09dd815a7 --- /dev/null +++ b/drivers/moto_f_diag/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean \ No newline at end of file diff --git a/drivers/moto_f_diag/f_diag.c b/drivers/moto_f_diag/f_diag.c new file mode 100644 index 000000000000..3957ae19b830 --- /dev/null +++ b/drivers/moto_f_diag/f_diag.c @@ -0,0 +1,73 @@ +/* f_diag.c + * Copyright (C) 2007 Google, Inc. + * Copyright (c) 2008-2017, The Linux Foundation. All rights reserved. + * Author: Brian Swetland + * Copyright (c) 2018, Motorola Mobility LLC. + * 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. + * + */ +#include +#include + +#include "tty_diag.h" + +struct usb_diag_ch *usb_diag_open(const char *name, void *priv, + void (*notify)(void *, unsigned int, struct diag_request *)) +{ + return tty_diag_channel_open(name, priv, notify); +} +EXPORT_SYMBOL(usb_diag_open); + +void usb_diag_close(struct usb_diag_ch *ch) +{ + return tty_diag_channel_close(ch); +} +EXPORT_SYMBOL(usb_diag_close); + +int usb_diag_alloc_req(struct usb_diag_ch *ch, int n_write, int n_read) +{ + return 0; +} +EXPORT_SYMBOL(usb_diag_alloc_req); +#define DWC3_MAX_REQUEST_SIZE (16 * 1024 * 1024) + +int usb_diag_request_size(struct usb_diag_ch *ch) +{ + return DWC3_MAX_REQUEST_SIZE; +} +EXPORT_SYMBOL(usb_diag_request_size); + +int usb_diag_read(struct usb_diag_ch *ch, struct diag_request *d_req) +{ + return tty_diag_channel_read(ch, d_req); +} +EXPORT_SYMBOL(usb_diag_read); + +int usb_diag_write(struct usb_diag_ch *ch, struct diag_request *d_req) +{ + return tty_diag_channel_write(ch, d_req); +} +EXPORT_SYMBOL(usb_diag_write); + +static int __init diag_init(void) +{ + return diag_tty_init(); +} + +static void __exit diag_exit(void) +{ + diag_tty_exit(); +} + +module_init(diag_init); +module_exit(diag_exit); + +MODULE_DESCRIPTION("TTY diag function driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/moto_f_diag/tty_diag.c b/drivers/moto_f_diag/tty_diag.c new file mode 100644 index 000000000000..3bd5eada73e6 --- /dev/null +++ b/drivers/moto_f_diag/tty_diag.c @@ -0,0 +1,374 @@ +/* Copyright (c) 2018, Motorola Mobility LLC. 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 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tty_diag.h" + +#define DIAG_MAJOR 185 +#define DIAG_TTY_MINOR_COUNT 3 + +static DEFINE_SPINLOCK(diag_tty_lock); + +static struct tty_driver *diag_tty_driver; +static struct usb_diag_ch legacy_ch; +static struct diag_request *d_req_ptr; + +struct diag_tty_data { + struct tty_port port; + struct tty_struct *tty; + int open_count; +}; + +static struct diag_tty_data diag_tty[DIAG_TTY_MINOR_COUNT]; +static int diag_packet_incomplete; + +static int ttydiag_dbg_cmd_code, ttydiag_dbg_subsys_id, dbg_tty_minor; + +/* 1 - would add channel dispatch checking logic + * + * by default, this is off (0) + * + * + * try `echo "1">/sys/class/diag/diag/dbg_ftm' to turn it on. + */ +static int dbg_ftm_flag; + +static int diag_tty_open(struct tty_struct *tty, struct file *f) +{ + int n = tty->index; + struct diag_tty_data *tty_data; + unsigned long flags; + + tty_data = diag_tty + n; + + if (n < 0 || n >= DIAG_TTY_MINOR_COUNT) + return -ENODEV; + + if (tty_data->open_count >= 1) + return -EBUSY; + + spin_lock_irqsave(&diag_tty_lock, flags); + + tty_data->tty = tty; + tty->driver_data = tty_data; + tty_data->open_count++; + + spin_unlock_irqrestore(&diag_tty_lock, flags); + + legacy_ch.notify(legacy_ch.priv, USB_DIAG_CONNECT, NULL); + + return 0; +} + +static void diag_tty_close(struct tty_struct *tty, struct file *f) +{ + struct diag_tty_data *tty_data = tty->driver_data; + int disconnect_channel = 1; + int i; + unsigned long flags; + + if (tty_data == NULL) + return; + + spin_lock_irqsave(&diag_tty_lock, flags); + tty_data->open_count--; + + for (i = 0; i < DIAG_TTY_MINOR_COUNT; i++) { + if (diag_tty[i].open_count) { + disconnect_channel = 0; + break; + } + } + + if (disconnect_channel && legacy_ch.notify && legacy_ch.priv) + legacy_ch.priv_usb = NULL; + + if (tty_data->open_count == 0) + tty->driver_data = NULL; + + spin_unlock_irqrestore(&diag_tty_lock, flags); +} + +static int diag_tty_write(struct tty_struct *tty, + const unsigned char *buf, int len) +{ + struct diag_tty_data *tty_data = tty->driver_data; + struct diag_request *d_req_temp = d_req_ptr; + unsigned long flags; + + spin_lock_irqsave(&diag_tty_lock, flags); + + /* Make sure diag char driver is ready and no outstanding request */ + if ((d_req_ptr == NULL) || legacy_ch.priv_usb) { + spin_unlock_irqrestore(&diag_tty_lock, flags); + return -EAGAIN; + } + + /* Diag packet must fit in buff and be written all at once */ + if (len > d_req_ptr->length) { + spin_unlock_irqrestore(&diag_tty_lock, flags); + return -EMSGSIZE; + } + + /* Check whether fresh packet */ + if (!diag_packet_incomplete) { + memcpy(d_req_ptr->buf, buf, len); + d_req_ptr->actual = len; + } else { + if (d_req_ptr->actual + len > d_req_ptr->length) { + d_req_ptr->actual = 0; + diag_packet_incomplete = 0; + spin_unlock_irqrestore(&diag_tty_lock, flags); + return -EMSGSIZE; + } else { + memcpy(d_req_ptr->buf + d_req_ptr->actual, buf, len); + d_req_ptr->actual += len; + } + } + + /* Check if packet is now complete */ + if (d_req_ptr->buf[d_req_ptr->actual - 1] != 0x7E) { + diag_packet_incomplete = 1; + spin_unlock_irqrestore(&diag_tty_lock, flags); + return len; + } else + diag_packet_incomplete = 0; + + if (dbg_ftm_flag == 1) { + + if (tty_data != NULL) { + if (!strncmp(tty_data->tty->name, "ttydiag0", + strlen("ttydiag0"))) { + dbg_tty_minor = 0; + ttydiag_dbg_cmd_code = + (int)(*(char *)d_req_ptr->buf); + ttydiag_dbg_subsys_id = + (int)(*(char *)(d_req_ptr->buf+1)); + } + if (!strncmp(tty_data->tty->name, "ttydiag1", + strlen("ttydiag1"))) { + dbg_tty_minor = 1; + ttydiag_dbg_cmd_code = + (int)(*(char *)d_req_ptr->buf); + ttydiag_dbg_subsys_id = + (int)(*(char *)(d_req_ptr->buf+1)); + } + } + } + + /* Set active tty for responding */ + legacy_ch.priv_usb = tty_data; + + spin_unlock_irqrestore(&diag_tty_lock, flags); + legacy_ch.notify(legacy_ch.priv, USB_DIAG_READ_DONE, d_req_temp); + + return len; +} + +static int diag_tty_write_room(struct tty_struct *tty) +{ + if ((d_req_ptr == NULL) || legacy_ch.priv_usb) + return 0; + else + return d_req_ptr->length; +} + +static int diag_tty_chars_in_buffer(struct tty_struct *tty) +{ + /* Data is always written when available, this should be changed if + write to userspace is changed to delayed work */ + return 0; +} + +static const struct tty_operations diag_tty_ops = { + .open = diag_tty_open, + .close = diag_tty_close, + .write = diag_tty_write, + .write_room = diag_tty_write_room, + .chars_in_buffer = diag_tty_chars_in_buffer, +}; + +/* Diag char driver ready */ +struct usb_diag_ch *tty_diag_channel_open(const char *name, void *priv, + void (*notify)(void *, unsigned, struct diag_request *)) +{ + int i; + unsigned long flags; + + if (legacy_ch.priv != NULL) + return ERR_PTR(-EBUSY); + + spin_lock_init(&diag_tty_lock); + spin_lock_irqsave(&diag_tty_lock, flags); + legacy_ch.priv = priv; + legacy_ch.notify = notify; + spin_unlock_irqrestore(&diag_tty_lock, flags); + + for (i = 0; i < DIAG_TTY_MINOR_COUNT; i++) { + tty_port_init(&diag_tty[i].port); + tty_port_register_device(&diag_tty[i].port, diag_tty_driver, + i, NULL); + } + + return &legacy_ch; +} + +/* Diag char driver no longer ready */ +void tty_diag_channel_close(struct usb_diag_ch *diag_ch) +{ + struct diag_tty_data *priv_usb = diag_ch->priv_usb; + unsigned long flags; + int i; + + if (diag_ch->priv_usb) { + tty_insert_flip_char(&priv_usb->port, 0x00, TTY_BREAK); + tty_flip_buffer_push(&priv_usb->port); + } + spin_lock_irqsave(&diag_tty_lock, flags); + diag_ch->priv = NULL; + diag_ch->notify = NULL; + + for (i = DIAG_TTY_MINOR_COUNT - 1; i >= 0; i--) + tty_unregister_device(diag_tty_driver, i); + + spin_unlock_irqrestore(&diag_tty_lock, flags); +} + +/* Diag char driver prepares tty driver to receive a write from userspace */ +int tty_diag_channel_read(struct usb_diag_ch *diag_ch, + struct diag_request *d_req) +{ + d_req_ptr = d_req; + + return 0; +} + +/* Diag char driver has diag packet ready for userspace */ +int tty_diag_channel_write(struct usb_diag_ch *diag_ch, + struct diag_request *d_req) +{ + struct diag_tty_data *tty_data = diag_ch->priv_usb; + unsigned char *tty_buf; + int tty_allocated; + unsigned long flags; + int cmd_code, subsys_id; + + /* If diag packet is not 1:1 response (perhaps logging packet?), + try primary channel */ + if (tty_data == NULL) + tty_data = &(diag_tty[0]); + + spin_lock_irqsave(&diag_tty_lock, flags); + + if (dbg_ftm_flag == 1) { + cmd_code = (int)(*(char *)d_req->buf); + subsys_id = (int)(*(char *)(d_req->buf+1)); + + if (cmd_code == ttydiag_dbg_cmd_code && + subsys_id == ttydiag_dbg_subsys_id && + dbg_tty_minor != -1) { + /* respond to last tty */ + ttydiag_dbg_cmd_code = 0; + ttydiag_dbg_subsys_id = 0; + tty_data = &(diag_tty[dbg_tty_minor]); + dbg_tty_minor = -1; + } else { + tty_data = &(diag_tty[2]); + } + } + + if (tty_data->tty == NULL) { + spin_unlock_irqrestore(&diag_tty_lock, flags); + return -EIO; + } + + tty_allocated = tty_prepare_flip_string(&tty_data->port, + &tty_buf, d_req->length); + + if (tty_allocated < d_req->length) { + spin_unlock_irqrestore(&diag_tty_lock, flags); + return -ENOMEM; + } + + /* Unset active tty for next request diag tool */ + diag_ch->priv_usb = NULL; + + memcpy(tty_buf, d_req->buf, d_req->length); + tty_flip_buffer_push(&tty_data->port); + + d_req->actual = d_req->length; + spin_unlock_irqrestore(&diag_tty_lock, flags); + + diag_ch->notify(diag_ch->priv, USB_DIAG_WRITE_DONE_SYNC, d_req); + + return 0; +} + +int diag_tty_init(void) +{ + int result; + + legacy_ch.notify = NULL; + legacy_ch.priv = NULL; + legacy_ch.priv_usb = NULL; + diag_packet_incomplete = 0; + dbg_tty_minor = -1; + + diag_tty_driver = alloc_tty_driver(DIAG_TTY_MINOR_COUNT); + if (diag_tty_driver == NULL) + return -ENOMEM; + + diag_tty_driver->owner = THIS_MODULE; + diag_tty_driver->driver_name = "tty_diag"; + diag_tty_driver->name = "ttydiag"; + diag_tty_driver->major = DIAG_MAJOR; + diag_tty_driver->minor_start = 0; + diag_tty_driver->type = TTY_DRIVER_TYPE_SYSTEM; + diag_tty_driver->subtype = SYSTEM_TYPE_TTY; + diag_tty_driver->init_termios = tty_std_termios; + diag_tty_driver->init_termios.c_cflag = B115200 | CS8 | CREAD; + diag_tty_driver->init_termios.c_iflag = IGNBRK; + diag_tty_driver->init_termios.c_oflag = 0; + diag_tty_driver->init_termios.c_lflag = 0; + diag_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; + + tty_set_operations(diag_tty_driver, &diag_tty_ops); + + result = tty_register_driver(diag_tty_driver); + if (result) { + printk(KERN_ERR "Failed to register diag_tty driver."); + put_tty_driver(diag_tty_driver); + diag_tty_driver = NULL; + return result; + } + + return 0; +} + +void diag_tty_exit(void) +{ + if (diag_tty_driver) { + tty_unregister_driver(diag_tty_driver); + put_tty_driver(diag_tty_driver); + } +} diff --git a/drivers/moto_f_diag/tty_diag.h b/drivers/moto_f_diag/tty_diag.h new file mode 100644 index 000000000000..d3c888ef2e00 --- /dev/null +++ b/drivers/moto_f_diag/tty_diag.h @@ -0,0 +1,29 @@ +/* Copyright (c) 2018, Motorola Mobility LLC. 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 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. + * + */ + +#ifndef _DRIVERS_TTY_DIAG_H_ +#define _DRIVERS_TTY_DIAG_H_ + +#include + +int diag_tty_init(void); +void diag_tty_exit(void); +struct usb_diag_ch *tty_diag_channel_open(const char *name, void *priv, + void (*notify)(void *, unsigned, struct diag_request *)); +void tty_diag_channel_close(struct usb_diag_ch *diag_ch); +int tty_diag_channel_read(struct usb_diag_ch *diag_ch, + struct diag_request *d_req); +int tty_diag_channel_write(struct usb_diag_ch *diag_ch, + struct diag_request *d_req); + +#endif /* _DRIVERS_TTY_DIAG_H_ */ diff --git a/drivers/moto_f_mass_storage/Android.mk b/drivers/moto_f_mass_storage/Android.mk new file mode 100644 index 000000000000..76a249537bbe --- /dev/null +++ b/drivers/moto_f_mass_storage/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := moto_f_mass_storage.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/moto_f_mass_storage/Kbuild b/drivers/moto_f_mass_storage/Kbuild new file mode 100644 index 000000000000..f4d67d11106d --- /dev/null +++ b/drivers/moto_f_mass_storage/Kbuild @@ -0,0 +1,6 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +moto_f_mass_storage-objs += f_mass_storage.o storage_common.o +obj-m += moto_f_mass_storage.o diff --git a/drivers/moto_f_mass_storage/Makefile b/drivers/moto_f_mass_storage/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/moto_f_mass_storage/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/moto_f_mass_storage/f_mass_storage.c b/drivers/moto_f_mass_storage/f_mass_storage.c new file mode 100644 index 000000000000..f82fa92eb1cf --- /dev/null +++ b/drivers/moto_f_mass_storage/f_mass_storage.c @@ -0,0 +1,2854 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * f_mass_storage.c -- Mass Storage USB Composite Function + * + * Copyright (C) 2003-2008 Alan Stern + * Copyright (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. 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. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * 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. + */ + +/* + * The Mass Storage Function acts as a USB Mass Storage device, + * appearing to the host as a disk drive or as a CD-ROM drive. In + * addition to providing an example of a genuinely useful composite + * function for a USB device, it also illustrates a technique of + * double-buffering for increased throughput. + * + * For more information about MSF and in particular its module + * parameters and sysfs interface read the + * file. + */ + +/* + * MSF is configured by specifying a fsg_config structure. It has the + * following fields: + * + * nluns Number of LUNs function have (anywhere from 1 + * to FSG_MAX_LUNS). + * luns An array of LUN configuration values. This + * should be filled for each LUN that + * function will include (ie. for "nluns" + * LUNs). Each element of the array has + * the following fields: + * ->filename The path to the backing file for the LUN. + * Required if LUN is not marked as + * removable. + * ->ro Flag specifying access to the LUN shall be + * read-only. This is implied if CD-ROM + * emulation is enabled as well as when + * it was impossible to open "filename" + * in R/W mode. + * ->removable Flag specifying that LUN shall be indicated as + * being removable. + * ->cdrom Flag specifying that LUN shall be reported as + * being a CD-ROM. + * ->nofua Flag specifying that FUA flag in SCSI WRITE(10,12) + * commands for this LUN shall be ignored. + * + * vendor_name + * product_name + * release Information used as a reply to INQUIRY + * request. To use default set to NULL, + * NULL, 0xffff respectively. The first + * field should be 8 and the second 16 + * characters or less. + * + * can_stall Set to permit function to halt bulk endpoints. + * Disabled on some USB devices known not + * to work correctly. You should set it + * to true. + * + * If "removable" is not set for a LUN then a backing file must be + * specified. If it is set, then NULL filename means the LUN's medium + * is not loaded (an empty string as "filename" in the fsg_config + * structure causes error). The CD-ROM emulation includes a single + * data track and no audio tracks; hence there need be only one + * backing file per LUN. + * + * This function is heavily based on "File-backed Storage Gadget" by + * Alan Stern which in turn is heavily based on "Gadget Zero" by David + * Brownell. The driver's SCSI command interface was based on the + * "Information technology - Small Computer System Interface - 2" + * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93, + * available at . + * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which + * was based on the "Universal Serial Bus Mass Storage Class UFI + * Command Specification" document, Revision 1.0, December 14, 1998, + * available at + * . + */ + +/* + * Driver Design + * + * The MSF is fairly straightforward. There is a main kernel + * thread that handles most of the work. Interrupt routines field + * callbacks from the controller driver: bulk- and interrupt-request + * completion notifications, endpoint-0 events, and disconnect events. + * Completion events are passed to the main thread by wakeup calls. Many + * ep0 requests are handled at interrupt time, but SetInterface, + * SetConfiguration, and device reset requests are forwarded to the + * thread in the form of "exceptions" using SIGUSR1 signals (since they + * should interrupt any ongoing file I/O operations). + * + * The thread's main routine implements the standard command/data/status + * parts of a SCSI interaction. It and its subroutines are full of tests + * for pending signals/exceptions -- all this polling is necessary since + * the kernel has no setjmp/longjmp equivalents. (Maybe this is an + * indication that the driver really wants to be running in userspace.) + * An important point is that so long as the thread is alive it keeps an + * open reference to the backing file. This will prevent unmounting + * the backing file's underlying filesystem and could cause problems + * during system shutdown, for example. To prevent such problems, the + * thread catches INT, TERM, and KILL signals and converts them into + * an EXIT exception. + * + * In normal operation the main thread is started during the gadget's + * fsg_bind() callback and stopped during fsg_unbind(). But it can + * also exit when it receives a signal, and there's no point leaving + * the gadget running when the thread is dead. As of this moment, MSF + * provides no way to deregister the gadget when thread dies -- maybe + * a callback functions is needed. + * + * To provide maximum throughput, the driver uses a circular pipeline of + * buffer heads (struct fsg_buffhd). In principle the pipeline can be + * arbitrarily long; in practice the benefits don't justify having more + * than 2 stages (i.e., double buffering). But it helps to think of the + * pipeline as being a long one. Each buffer head contains a bulk-in and + * a bulk-out request pointer (since the buffer can be used for both + * output and input -- directions always are given from the host's + * point of view) as well as a pointer to the buffer and various state + * variables. + * + * Use of the pipeline follows a simple protocol. There is a variable + * (fsg->next_buffhd_to_fill) that points to the next buffer head to use. + * At any time that buffer head may still be in use from an earlier + * request, so each buffer head has a state variable indicating whether + * it is EMPTY, FULL, or BUSY. Typical use involves waiting for the + * buffer head to be EMPTY, filling the buffer either by file I/O or by + * USB I/O (during which the buffer head is BUSY), and marking the buffer + * head FULL when the I/O is complete. Then the buffer will be emptied + * (again possibly by USB I/O, during which it is marked BUSY) and + * finally marked EMPTY again (possibly by a completion routine). + * + * A module parameter tells the driver to avoid stalling the bulk + * endpoints wherever the transport specification allows. This is + * necessary for some UDCs like the SuperH, which cannot reliably clear a + * halt on a bulk endpoint. However, under certain circumstances the + * Bulk-only specification requires a stall. In such cases the driver + * will halt the endpoint and set a flag indicating that it should clear + * the halt in software during the next device reset. Hopefully this + * will permit everything to work correctly. Furthermore, although the + * specification allows the bulk-out endpoint to halt when the host sends + * too much data, implementing this would cause an unavoidable race. + * The driver will always use the "no-stall" approach for OUT transfers. + * + * One subtle point concerns sending status-stage responses for ep0 + * requests. Some of these requests, such as device reset, can involve + * interrupting an ongoing file I/O operation, which might take an + * arbitrarily long time. During that delay the host might give up on + * the original ep0 request and issue a new one. When that happens the + * driver should not notify the host about completion of the original + * request, as the host will no longer be waiting for it. So the driver + * assigns to each ep0 request a unique tag, and it keeps track of the + * tag value of the request associated with a long-running exception + * (device-reset, interface-change, or configuration-change). When the + * exception handler is finished, the status-stage response is submitted + * only if the current ep0 request tag is equal to the exception request + * tag. Thus only the most recently received ep0 request will get a + * status-stage response. + * + * Warning: This driver source file is too long. It ought to be split up + * into a header file plus about 3 separate .c files, to handle the details + * of the Gadget, USB Mass Storage, and SCSI protocols. + */ + + +/* #define VERBOSE_DEBUG */ +/* #define DUMP_MSGS */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "storage_common.h" + + +/*------------------------------------------------------------------------*/ + +#define FSG_DRIVER_DESC "Motorola Mass Storage Function" +#define FSG_DRIVER_VERSION "2021/08/30" + +static const char fsg_string_interface[] = "Mass Storage"; + +enum fsg_restart_type { + FSG_RESTART_NONE, + FSG_REBOOT, + FSG_REBOOT_BL, +}; + +/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ +static struct usb_string fsg_strings[] = { + {FSG_STRING_INTERFACE, fsg_string_interface}, + {} +}; + +static struct usb_gadget_strings fsg_stringtab = { + .language = 0x0409, /* en-us */ + .strings = fsg_strings, +}; + +static struct usb_gadget_strings *fsg_strings_array[] = { + &fsg_stringtab, + NULL, +}; + +struct fsg_lun_opts { + struct config_group group; + struct fsg_lun *lun; + int lun_id; +}; + +struct fsg_opts { + struct fsg_common *common; + struct usb_function_instance func_inst; + struct fsg_lun_opts lun0; + struct config_group *default_groups[2]; + bool no_configfs; /* for legacy gadgets */ + + /* + * Read/write access to configfs attributes is handled by configfs. + * + * This is to protect the data from concurrent access by read/write + * and create symlink/remove symlink. + */ + struct mutex lock; + int refcnt; +}; + +struct fsg_lun_config { + const char *filename; + char ro; + char removable; + char cdrom; + char nofua; + char inquiry_string[INQUIRY_STRING_LEN]; +}; + +static inline struct fsg_opts * +fsg_opts_from_func_inst(const struct usb_function_instance *fi) +{ + return container_of(fi, struct fsg_opts, func_inst); +} + +/*-------------------------------------------------------------------------*/ + +struct fsg_dev; +struct fsg_common; + +/* Data shared by all the FSG instances. */ +struct fsg_common { + struct usb_gadget *gadget; + struct usb_composite_dev *cdev; + struct fsg_dev *fsg; + wait_queue_head_t io_wait; + wait_queue_head_t fsg_wait; + + /* filesem protects: backing files in use */ + struct rw_semaphore filesem; + + /* lock protects: state and thread_task */ + spinlock_t lock; + + struct usb_ep *ep0; /* Copy of gadget->ep0 */ + struct usb_request *ep0req; /* Copy of cdev->req */ + unsigned int ep0_req_tag; + + struct fsg_buffhd *next_buffhd_to_fill; + struct fsg_buffhd *next_buffhd_to_drain; + struct fsg_buffhd *buffhds; + unsigned int fsg_num_buffers; + + int cmnd_size; + u8 cmnd[MAX_COMMAND_SIZE]; + + unsigned int lun; + struct fsg_lun *luns[FSG_MAX_LUNS]; + struct fsg_lun *curlun; + + unsigned int bulk_out_maxpacket; + enum fsg_state state; /* For exception handling */ + unsigned int exception_req_tag; + void *exception_arg; + + enum data_direction data_dir; + u32 data_size; + u32 data_size_from_cmnd; + u32 tag; + u32 residue; + u32 usb_amount_left; + + unsigned int can_stall:1; + unsigned int free_storage_on_release:1; + unsigned int phase_error:1; + unsigned int short_packet_received:1; + unsigned int bad_lun_okay:1; + unsigned int running:1; + unsigned int sysfs:1; + + struct completion thread_notifier; + struct task_struct *thread_task; + + /* Gadget's private data. */ + void *private_data; + + char inquiry_string[INQUIRY_STRING_LEN]; + + enum fsg_restart_type restart_type; + struct delayed_work restart_work; +}; + +struct fsg_dev { + struct usb_function function; + struct usb_gadget *gadget; /* Copy of cdev->gadget */ + struct fsg_common *common; + + u16 interface_number; + + unsigned int bulk_in_enabled:1; + unsigned int bulk_out_enabled:1; + + unsigned long atomic_bitflags; +#define IGNORE_BULK_OUT 0 + + struct usb_ep *bulk_in; + struct usb_ep *bulk_out; +}; + +static inline int __fsg_is_set(struct fsg_common *common, + const char *func, unsigned line) +{ + if (common->fsg) + return 1; + ERROR(common, "common->fsg is NULL in %s at %u\n", func, line); + WARN_ON(1); + return 0; +} + +#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__)) + +static inline struct fsg_dev *fsg_from_func(struct usb_function *f) +{ + return container_of(f, struct fsg_dev, function); +} + +typedef void (*fsg_routine_t)(struct fsg_dev *); + +static int exception_in_progress(struct fsg_common *common) +{ + return common->state > FSG_STATE_NORMAL; +} + +/* Make bulk-out requests be divisible by the maxpacket size */ +static void set_bulk_out_req_length(struct fsg_common *common, + struct fsg_buffhd *bh, unsigned int length) +{ + unsigned int rem; + + bh->bulk_out_intended_length = length; + rem = length % common->bulk_out_maxpacket; + if (rem > 0) + length += common->bulk_out_maxpacket - rem; + bh->outreq->length = length; +} + + +/*-------------------------------------------------------------------------*/ + +static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) +{ + const char *name; + + if (ep == fsg->bulk_in) + name = "bulk-in"; + else if (ep == fsg->bulk_out) + name = "bulk-out"; + else + name = ep->name; + DBG(fsg, "%s set halt\n", name); + return usb_ep_set_halt(ep); +} + + +/*-------------------------------------------------------------------------*/ + +/* These routines may be called in process context or in_irq */ + +static void __raise_exception(struct fsg_common *common, enum fsg_state new_state, + void *arg) +{ + unsigned long flags; + + /* + * Do nothing if a higher-priority exception is already in progress. + * If a lower-or-equal priority exception is in progress, preempt it + * and notify the main thread by sending it a signal. + */ + spin_lock_irqsave(&common->lock, flags); + if (common->state <= new_state) { + common->exception_req_tag = common->ep0_req_tag; + common->state = new_state; + common->exception_arg = arg; + if (common->thread_task) + send_sig_info(SIGUSR1, SEND_SIG_PRIV, + common->thread_task); + } + spin_unlock_irqrestore(&common->lock, flags); +} + +static void raise_exception(struct fsg_common *common, enum fsg_state new_state) +{ + __raise_exception(common, new_state, NULL); +} + +/*-------------------------------------------------------------------------*/ + +static int ep0_queue(struct fsg_common *common) +{ + int rc; + + rc = usb_ep_queue(common->ep0, common->ep0req, GFP_ATOMIC); + common->ep0->driver_data = common; + if (rc != 0 && rc != -ESHUTDOWN) { + /* We can't do much more than wait for a reset */ + WARNING(common, "error in submission: %s --> %d\n", + common->ep0->name, rc); + } + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +/* Completion handlers. These always run in_irq. */ + +static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_common *common = ep->driver_data; + struct fsg_buffhd *bh = req->context; + + if (req->status || req->actual != req->length) + DBG(common, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, req->length); + if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); + + /* Synchronize with the smp_load_acquire() in sleep_thread() */ + smp_store_release(&bh->state, BUF_STATE_EMPTY); + wake_up(&common->io_wait); +} + +static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct fsg_common *common = ep->driver_data; + struct fsg_buffhd *bh = req->context; + + dump_msg(common, "bulk-out", req->buf, req->actual); + if (req->status || req->actual != bh->bulk_out_intended_length) + DBG(common, "%s --> %d, %u/%u\n", __func__, + req->status, req->actual, bh->bulk_out_intended_length); + if (req->status == -ECONNRESET) /* Request was cancelled */ + usb_ep_fifo_flush(ep); + + /* Synchronize with the smp_load_acquire() in sleep_thread() */ + smp_store_release(&bh->state, BUF_STATE_FULL); + wake_up(&common->io_wait); +} + +static int _fsg_common_get_max_lun(struct fsg_common *common) +{ + int i = ARRAY_SIZE(common->luns) - 1; + + while (i >= 0 && !common->luns[i]) + --i; + + return i; +} + +static int fsg_setup(struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct fsg_dev *fsg = fsg_from_func(f); + struct usb_request *req = fsg->common->ep0req; + u16 w_index = le16_to_cpu(ctrl->wIndex); + u16 w_value = le16_to_cpu(ctrl->wValue); + u16 w_length = le16_to_cpu(ctrl->wLength); + + if (!fsg_is_set(fsg->common)) + return -EOPNOTSUPP; + + ++fsg->common->ep0_req_tag; /* Record arrival of a new request */ + req->context = NULL; + req->length = 0; + dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl)); + + switch (ctrl->bRequest) { + + case US_BULK_RESET_REQUEST: + if (ctrl->bRequestType != + (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (w_index != fsg->interface_number || w_value != 0 || + w_length != 0) + return -EDOM; + + /* + * Raise an exception to stop the current operation + * and reinitialize our state. + */ + DBG(fsg, "bulk reset request\n"); + raise_exception(fsg->common, FSG_STATE_PROTOCOL_RESET); + return USB_GADGET_DELAYED_STATUS; + + case US_BULK_GET_MAX_LUN: + if (ctrl->bRequestType != + (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE)) + break; + if (w_index != fsg->interface_number || w_value != 0 || + w_length != 1) + return -EDOM; + VDBG(fsg, "get max LUN\n"); + *(u8 *)req->buf = _fsg_common_get_max_lun(fsg->common); + + /* Respond with data/status */ + req->length = min((u16)1, w_length); + return ep0_queue(fsg->common); + } + + VDBG(fsg, + "unknown class-specific control req %02x.%02x v%04x i%04x l%u\n", + ctrl->bRequestType, ctrl->bRequest, + le16_to_cpu(ctrl->wValue), w_index, w_length); + return -EOPNOTSUPP; +} + + +/*-------------------------------------------------------------------------*/ + +/* All the following routines run in process context */ + +/* Use this for bulk or interrupt transfers, not ep0 */ +static int start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, + struct usb_request *req) +{ + int rc; + + if (ep == fsg->bulk_in) + dump_msg(fsg, "bulk-in", req->buf, req->length); + + rc = usb_ep_queue(ep, req, GFP_KERNEL); + if (rc) { + + /* We can't do much more than wait for a reset */ + req->status = rc; + + /* + * Note: currently the net2280 driver fails zero-length + * submissions if DMA is enabled. + */ + if (rc != -ESHUTDOWN && + !(rc == -EOPNOTSUPP && req->length == 0)) + WARNING(fsg, "error in submission: %s --> %d\n", + ep->name, rc); + } + return rc; +} + +static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh) +{ + if (!fsg_is_set(common)) + return false; + bh->state = BUF_STATE_SENDING; + if (start_transfer(common->fsg, common->fsg->bulk_in, bh->inreq)) + bh->state = BUF_STATE_EMPTY; + return true; +} + +static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh) +{ + if (!fsg_is_set(common)) + return false; + bh->state = BUF_STATE_RECEIVING; + if (start_transfer(common->fsg, common->fsg->bulk_out, bh->outreq)) + bh->state = BUF_STATE_FULL; + return true; +} + +static int sleep_thread(struct fsg_common *common, bool can_freeze, + struct fsg_buffhd *bh) +{ + int rc; + + /* Wait until a signal arrives or bh is no longer busy */ + if (can_freeze) + /* + * synchronize with the smp_store_release(&bh->state) in + * bulk_in_complete() or bulk_out_complete() + */ + rc = wait_event_freezable(common->io_wait, + bh && smp_load_acquire(&bh->state) >= + BUF_STATE_EMPTY); + else + rc = wait_event_interruptible(common->io_wait, + bh && smp_load_acquire(&bh->state) >= + BUF_STATE_EMPTY); + return rc ? -EINTR : 0; +} + + +/*-------------------------------------------------------------------------*/ + +static int do_synchronize_cache(struct fsg_common *common) +{ + struct fsg_lun *curlun = common->curlun; + int rc; + + /* We ignore the requested LBA and write out all file's + * dirty data buffers. */ + rc = fsg_lun_fsync_sub(curlun); + if (rc) + curlun->sense_data = SS_WRITE_ERROR; + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + u8 *buf = (u8 *) bh->buf; + + if (!curlun) { /* Unsupported LUNs are okay */ + common->bad_lun_okay = 1; + memset(buf, 0, 36); + buf[0] = TYPE_NO_LUN; /* Unsupported, no device-type */ + buf[4] = 31; /* Additional length */ + return 36; + } + + buf[0] = curlun->cdrom ? TYPE_ROM : TYPE_DISK; + buf[1] = curlun->removable ? 0x80 : 0; + buf[2] = 2; /* ANSI SCSI level 2 */ + buf[3] = 2; /* SCSI-2 INQUIRY data format */ + buf[4] = 31; /* Additional length */ + buf[5] = 0; /* No special options */ + buf[6] = 0; + buf[7] = 0; + if (curlun->inquiry_string[0]) + memcpy(buf + 8, curlun->inquiry_string, + sizeof(curlun->inquiry_string)); + else + memcpy(buf + 8, common->inquiry_string, + sizeof(common->inquiry_string)); + return 36; +} + +static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + u8 *buf = (u8 *) bh->buf; + u32 sd, sdinfo; + int valid; + + /* + * From the SCSI-2 spec., section 7.9 (Unit attention condition): + * + * If a REQUEST SENSE command is received from an initiator + * with a pending unit attention condition (before the target + * generates the contingent allegiance condition), then the + * target shall either: + * a) report any pending sense data and preserve the unit + * attention condition on the logical unit, or, + * b) report the unit attention condition, may discard any + * pending sense data, and clear the unit attention + * condition on the logical unit for that initiator. + * + * FSG normally uses option a); enable this code to use option b). + */ +#if 0 + if (curlun && curlun->unit_attention_data != SS_NO_SENSE) { + curlun->sense_data = curlun->unit_attention_data; + curlun->unit_attention_data = SS_NO_SENSE; + } +#endif + + if (!curlun) { /* Unsupported LUNs are okay */ + common->bad_lun_okay = 1; + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + sdinfo = 0; + valid = 0; + } else { + sd = curlun->sense_data; + sdinfo = curlun->sense_data_info; + valid = curlun->info_valid << 7; + curlun->sense_data = SS_NO_SENSE; + curlun->sense_data_info = 0; + curlun->info_valid = 0; + } + + memset(buf, 0, 18); + buf[0] = valid | 0x70; /* Valid, current error */ + buf[2] = SK(sd); + put_unaligned_be32(sdinfo, &buf[3]); /* Sense information */ + buf[7] = 18 - 8; /* Additional sense length */ + buf[12] = ASC(sd); + buf[13] = ASCQ(sd); + return 18; +} + +static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + int mscmnd = common->cmnd[0]; + u8 *buf = (u8 *) bh->buf; + u8 *buf0 = buf; + int pc, page_code; + int changeable_values, all_pages; + int valid_page = 0; + int len, limit; + + if ((common->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + pc = common->cmnd[2] >> 6; + page_code = common->cmnd[2] & 0x3f; + if (pc == 3) { + curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; + return -EINVAL; + } + changeable_values = (pc == 1); + all_pages = (page_code == 0x3f); + + /* + * Write the mode parameter header. Fixed values are: default + * medium type, no cache control (DPOFUA), and no block descriptors. + * The only variable value is the WriteProtect bit. We will fill in + * the mode data length later. + */ + memset(buf, 0, 8); + if (mscmnd == MODE_SENSE) { + buf[2] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */ + buf += 4; + limit = 255; + } else { /* MODE_SENSE_10 */ + buf[3] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */ + buf += 8; + limit = 65535; /* Should really be FSG_BUFLEN */ + } + + /* No block descriptors */ + + /* + * The mode pages, in numerical order. The only page we support + * is the Caching page. + */ + if (page_code == 0x08 || all_pages) { + valid_page = 1; + buf[0] = 0x08; /* Page code */ + buf[1] = 10; /* Page length */ + memset(buf+2, 0, 10); /* None of the fields are changeable */ + + if (!changeable_values) { + buf[2] = 0x04; /* Write cache enable, */ + /* Read cache not disabled */ + /* No cache retention priorities */ + put_unaligned_be16(0xffff, &buf[4]); + /* Don't disable prefetch */ + /* Minimum prefetch = 0 */ + put_unaligned_be16(0xffff, &buf[8]); + /* Maximum prefetch */ + put_unaligned_be16(0xffff, &buf[10]); + /* Maximum prefetch ceiling */ + } + buf += 12; + } + + /* + * Check that a valid page was requested and the mode data length + * isn't too long. + */ + len = buf - buf0; + if (!valid_page || len > limit) { + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + /* Store the mode data length */ + if (mscmnd == MODE_SENSE) + buf0[0] = len - 1; + else + put_unaligned_be16(len - 2, buf0); + return len; +} + +static int do_start_stop(struct fsg_common *common) +{ + struct fsg_lun *curlun = common->curlun; + int loej, start; + + if (!curlun) { + return -EINVAL; + } else if (!curlun->removable) { + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } else if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */ + (common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + loej = common->cmnd[4] & 0x02; + start = common->cmnd[4] & 0x01; + + /* + * Our emulation doesn't support mounting; the medium is + * available for use as soon as it is loaded. + */ + if (start) { + if (!fsg_lun_is_open(curlun)) { + curlun->sense_data = SS_MEDIUM_NOT_PRESENT; + return -EINVAL; + } + return 0; + } + + /* Are we allowed to unload the media? */ + if (curlun->prevent_medium_removal) { + LDBG(curlun, "unload attempt prevented\n"); + curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED; + return -EINVAL; + } + + if (!loej) + return 0; + + up_read(&common->filesem); + down_write(&common->filesem); + fsg_lun_close(curlun); + up_write(&common->filesem); + down_read(&common->filesem); + + return 0; +} + +static int do_prevent_allow(struct fsg_common *common) +{ + struct fsg_lun *curlun = common->curlun; + int prevent; + + if (!common->curlun) { + return -EINVAL; + } else if (!common->curlun->removable) { + common->curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; + } + + prevent = common->cmnd[4] & 0x01; + if ((common->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */ + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + + if (curlun->prevent_medium_removal && !prevent) + fsg_lun_fsync_sub(curlun); + curlun->prevent_medium_removal = prevent; + return 0; +} + +static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh) +{ + struct fsg_lun *curlun = common->curlun; + + /* We don't support MODE SELECT */ + if (curlun) + curlun->sense_data = SS_INVALID_COMMAND; + return -EINVAL; +} + + +/*-------------------------------------------------------------------------*/ + +static int halt_bulk_in_endpoint(struct fsg_dev *fsg) +{ + int rc; + + rc = fsg_set_halt(fsg, fsg->bulk_in); + if (rc == -EAGAIN) + VDBG(fsg, "delayed bulk-in endpoint halt\n"); + while (rc != 0) { + if (rc != -EAGAIN) { + WARNING(fsg, "usb_ep_set_halt -> %d\n", rc); + rc = 0; + break; + } + + /* Wait for a short time and then try again */ + if (msleep_interruptible(100) != 0) + return -EINTR; + rc = usb_ep_set_halt(fsg->bulk_in); + } + return rc; +} + +static int wedge_bulk_in_endpoint(struct fsg_dev *fsg) +{ + int rc; + + DBG(fsg, "bulk-in set wedge\n"); + if (fsg->bulk_in->ops->set_wedge) + rc = fsg->bulk_in->ops->set_wedge(fsg->bulk_in); + else + rc = fsg->bulk_in->ops->set_halt(fsg->bulk_in, 1); + if (rc == -EAGAIN) + VDBG(fsg, "delayed bulk-in endpoint wedge\n"); + while (rc != 0) { + if (rc != -EAGAIN) { + WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc); + rc = 0; + break; + } + + /* Wait for a short time and then try again */ + if (msleep_interruptible(100) != 0) + return -EINTR; + if (fsg->bulk_in->ops->set_wedge) + rc = fsg->bulk_in->ops->set_wedge(fsg->bulk_in); + else + rc = fsg->bulk_in->ops->set_halt(fsg->bulk_in, 1); + } + return rc; +} + +static int throw_away_data(struct fsg_common *common) +{ + struct fsg_buffhd *bh, *bh2; + u32 amount; + int rc; + + for (bh = common->next_buffhd_to_drain; + bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0; + bh = common->next_buffhd_to_drain) { + + /* Try to submit another request if we need one */ + bh2 = common->next_buffhd_to_fill; + if (bh2->state == BUF_STATE_EMPTY && + common->usb_amount_left > 0) { + amount = min(common->usb_amount_left, FSG_BUFLEN); + + /* + * Except at the end of the transfer, amount will be + * equal to the buffer size, which is divisible by + * the bulk-out maxpacket size. + */ + set_bulk_out_req_length(common, bh2, amount); + if (!start_out_transfer(common, bh2)) + /* Dunno what to do if common->fsg is NULL */ + return -EIO; + common->next_buffhd_to_fill = bh2->next; + common->usb_amount_left -= amount; + continue; + } + + /* Wait for the data to be received */ + rc = sleep_thread(common, false, bh); + if (rc) + return rc; + + /* Throw away the data in a filled buffer */ + bh->state = BUF_STATE_EMPTY; + common->next_buffhd_to_drain = bh->next; + + /* A short packet or an error ends everything */ + if (bh->outreq->actual < bh->bulk_out_intended_length || + bh->outreq->status != 0) { + raise_exception(common, FSG_STATE_ABORT_BULK_OUT); + return -EINTR; + } + } + return 0; +} + +static int finish_reply(struct fsg_common *common) +{ + struct fsg_buffhd *bh = common->next_buffhd_to_fill; + int rc = 0; + + switch (common->data_dir) { + case DATA_DIR_NONE: + break; /* Nothing to send */ + + /* + * If we don't know whether the host wants to read or write, + * this must be CB or CBI with an unknown command. We mustn't + * try to send or receive any data. So stall both bulk pipes + * if we can and wait for a reset. + */ + case DATA_DIR_UNKNOWN: + if (!common->can_stall) { + /* Nothing */ + } else if (fsg_is_set(common)) { + fsg_set_halt(common->fsg, common->fsg->bulk_out); + rc = halt_bulk_in_endpoint(common->fsg); + } else { + /* Don't know what to do if common->fsg is NULL */ + rc = -EIO; + } + break; + + /* All but the last buffer of data must have already been sent */ + case DATA_DIR_TO_HOST: + if (common->data_size == 0) { + /* Nothing to send */ + + /* Don't know what to do if common->fsg is NULL */ + } else if (!fsg_is_set(common)) { + rc = -EIO; + + /* If there's no residue, simply send the last buffer */ + } else if (common->residue == 0) { + bh->inreq->zero = 0; + if (!start_in_transfer(common, bh)) + return -EIO; + common->next_buffhd_to_fill = bh->next; + + /* + * For Bulk-only, mark the end of the data with a short + * packet. If we are allowed to stall, halt the bulk-in + * endpoint. (Note: This violates the Bulk-Only Transport + * specification, which requires us to pad the data if we + * don't halt the endpoint. Presumably nobody will mind.) + */ + } else { + bh->inreq->zero = 1; + if (!start_in_transfer(common, bh)) + rc = -EIO; + common->next_buffhd_to_fill = bh->next; + if (common->can_stall) + rc = halt_bulk_in_endpoint(common->fsg); + } + break; + + /* + * We have processed all we want from the data the host has sent. + * There may still be outstanding bulk-out requests. + */ + case DATA_DIR_FROM_HOST: + if (common->residue == 0) { + /* Nothing to receive */ + + /* Did the host stop sending unexpectedly early? */ + } else if (common->short_packet_received) { + raise_exception(common, FSG_STATE_ABORT_BULK_OUT); + rc = -EINTR; + + /* + * We haven't processed all the incoming data. Even though + * we may be allowed to stall, doing so would cause a race. + * The controller may already have ACK'ed all the remaining + * bulk-out packets, in which case the host wouldn't see a + * STALL. Not realizing the endpoint was halted, it wouldn't + * clear the halt -- leading to problems later on. + */ +#if 0 + } else if (common->can_stall) { + if (fsg_is_set(common)) + fsg_set_halt(common->fsg, + common->fsg->bulk_out); + raise_exception(common, FSG_STATE_ABORT_BULK_OUT); + rc = -EINTR; +#endif + + /* + * We can't stall. Read in the excess data and throw it + * all away. + */ + } else { + rc = throw_away_data(common); + } + break; + } + return rc; +} + +static void send_status(struct fsg_common *common) +{ + struct fsg_lun *curlun = common->curlun; + struct fsg_buffhd *bh; + struct bulk_cs_wrap *csw; + int rc; + u8 status = US_BULK_STAT_OK; + u32 sd, sdinfo = 0; + + /* Wait for the next buffer to become available */ + bh = common->next_buffhd_to_fill; + rc = sleep_thread(common, false, bh); + if (rc) + return; + + if (curlun) { + sd = curlun->sense_data; + sdinfo = curlun->sense_data_info; + } else if (common->bad_lun_okay) + sd = SS_NO_SENSE; + else + sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; + + if (common->phase_error) { + DBG(common, "sending phase-error status\n"); + status = US_BULK_STAT_PHASE; + sd = SS_INVALID_COMMAND; + } else if (sd != SS_NO_SENSE) { + DBG(common, "sending command-failure status\n"); + status = US_BULK_STAT_FAIL; + VDBG(common, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" + " info x%x\n", + SK(sd), ASC(sd), ASCQ(sd), sdinfo); + } + + /* Store and send the Bulk-only CSW */ + csw = (void *)bh->buf; + + csw->Signature = cpu_to_le32(US_BULK_CS_SIGN); + csw->Tag = common->tag; + csw->Residue = cpu_to_le32(common->residue); + csw->Status = status; + + bh->inreq->length = US_BULK_CS_WRAP_LEN; + bh->inreq->zero = 0; + if (!start_in_transfer(common, bh)) + /* Don't know what to do if common->fsg is NULL */ + return; + + common->next_buffhd_to_fill = bh->next; + return; +} + + +/*-------------------------------------------------------------------------*/ + +/* + * Check whether the command is properly formed and whether its data size + * and direction agree with the values we already have. + */ +static int check_command(struct fsg_common *common, int cmnd_size, + enum data_direction data_dir, unsigned int mask, + int needs_medium, const char *name) +{ + int i; + unsigned int lun = common->cmnd[1] >> 5; + static const char dirletter[4] = {'u', 'o', 'i', 'n'}; + char hdlen[20]; + struct fsg_lun *curlun; + + hdlen[0] = 0; + if (common->data_dir != DATA_DIR_UNKNOWN) + sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir], + common->data_size); + VDBG(common, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", + name, cmnd_size, dirletter[(int) data_dir], + common->data_size_from_cmnd, common->cmnd_size, hdlen); + + /* + * We can't reply at all until we know the correct data direction + * and size. + */ + if (common->data_size_from_cmnd == 0) + data_dir = DATA_DIR_NONE; + if (common->data_size < common->data_size_from_cmnd) { + /* + * Host data size < Device data size is a phase error. + * Carry out the command, but only transfer as much as + * we are allowed. + */ + common->data_size_from_cmnd = common->data_size; + common->phase_error = 1; + } + common->residue = common->data_size; + common->usb_amount_left = common->data_size; + + /* Conflicting data directions is a phase error */ + if (common->data_dir != data_dir && common->data_size_from_cmnd > 0) { + common->phase_error = 1; + return -EINVAL; + } + + /* Verify the length of the command itself */ + if (cmnd_size != common->cmnd_size) { + + /* + * Special case workaround: There are plenty of buggy SCSI + * implementations. Many have issues with cbw->Length + * field passing a wrong command size. For those cases we + * always try to work around the problem by using the length + * sent by the host side provided it is at least as large + * as the correct command length. + * Examples of such cases would be MS-Windows, which issues + * REQUEST SENSE with cbw->Length == 12 where it should + * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and + * REQUEST SENSE with cbw->Length == 10 where it should + * be 6 as well. + */ + if (cmnd_size <= common->cmnd_size) { + DBG(common, "%s is buggy! Expected length %d " + "but we got %d\n", name, + cmnd_size, common->cmnd_size); + cmnd_size = common->cmnd_size; + } else { + common->phase_error = 1; + return -EINVAL; + } + } + + /* Check that the LUN values are consistent */ + if (common->lun != lun) + DBG(common, "using LUN %u from CBW, not LUN %u from CDB\n", + common->lun, lun); + + /* Check the LUN */ + curlun = common->curlun; + if (curlun) { + if (common->cmnd[0] != REQUEST_SENSE) { + curlun->sense_data = SS_NO_SENSE; + curlun->sense_data_info = 0; + curlun->info_valid = 0; + } + } else { + common->bad_lun_okay = 0; + + /* + * INQUIRY and REQUEST SENSE commands are explicitly allowed + * to use unsupported LUNs; all others may not. + */ + if (common->cmnd[0] != INQUIRY && + common->cmnd[0] != REQUEST_SENSE) { + DBG(common, "unsupported LUN %u\n", common->lun); + return -EINVAL; + } + } + + /* + * If a unit attention condition exists, only INQUIRY and + * REQUEST SENSE commands are allowed; anything else must fail. + */ + if (curlun && curlun->unit_attention_data != SS_NO_SENSE && + common->cmnd[0] != INQUIRY && + common->cmnd[0] != REQUEST_SENSE) { + curlun->sense_data = curlun->unit_attention_data; + curlun->unit_attention_data = SS_NO_SENSE; + return -EINVAL; + } + + /* Check that only command bytes listed in the mask are non-zero */ + common->cmnd[1] &= 0x1f; /* Mask away the LUN */ + for (i = 1; i < cmnd_size; ++i) { + if (common->cmnd[i] && !(mask & (1 << i))) { + if (curlun) + curlun->sense_data = SS_INVALID_FIELD_IN_CDB; + return -EINVAL; + } + } + + /* If the medium isn't mounted and the command needs to access + * it, return an error. */ + if (curlun && !fsg_lun_is_open(curlun) && needs_medium) { + curlun->sense_data = SS_MEDIUM_NOT_PRESENT; + return -EINVAL; + } + + return 0; +} + +#define SC_REBOOT 0xd7 +#define SC_REBOOT_2 0xd8 +static int do_scsi_command(struct fsg_common *common) +{ + struct fsg_buffhd *bh; + int rc; + int reply = -EINVAL; + static char unknown[16]; + + dump_cdb(common); + + /* Wait for the next buffer to become available for data or status */ + bh = common->next_buffhd_to_fill; + common->next_buffhd_to_drain = bh; + rc = sleep_thread(common, false, bh); + if (rc) + return rc; + + common->phase_error = 0; + common->short_packet_received = 0; + + down_read(&common->filesem); /* We're using the backing file */ + switch (common->cmnd[0]) { + + case INQUIRY: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "INQUIRY"); + if (reply == 0) + reply = do_inquiry(common, bh); + break; + + case MODE_SELECT: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_FROM_HOST, + (1<<1) | (1<<4), 0, + "MODE SELECT(6)"); + if (reply == 0) + reply = do_mode_select(common, bh); + break; + + case MODE_SELECT_10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_FROM_HOST, + (1<<1) | (3<<7), 0, + "MODE SELECT(10)"); + if (reply == 0) + reply = do_mode_select(common, bh); + break; + + case MODE_SENSE: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (1<<4), 0, + "MODE SENSE(6)"); + if (reply == 0) + reply = do_mode_sense(common, bh); + break; + + case MODE_SENSE_10: + common->data_size_from_cmnd = + get_unaligned_be16(&common->cmnd[7]); + reply = check_command(common, 10, DATA_DIR_TO_HOST, + (1<<1) | (1<<2) | (3<<7), 0, + "MODE SENSE(10)"); + if (reply == 0) + reply = do_mode_sense(common, bh); + break; + + case ALLOW_MEDIUM_REMOVAL: + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + (1<<4), 0, + "PREVENT-ALLOW MEDIUM REMOVAL"); + if (reply == 0) + reply = do_prevent_allow(common); + break; + + case REQUEST_SENSE: + common->data_size_from_cmnd = common->cmnd[4]; + reply = check_command(common, 6, DATA_DIR_TO_HOST, + (1<<4), 0, + "REQUEST SENSE"); + if (reply == 0) + reply = do_request_sense(common, bh); + break; + + case START_STOP: + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + (1<<1) | (1<<4), 0, + "START-STOP UNIT"); + if (reply == 0) + reply = do_start_stop(common); + break; + + case SYNCHRONIZE_CACHE: + common->data_size_from_cmnd = 0; + reply = check_command(common, 10, DATA_DIR_NONE, + (0xf<<2) | (3<<7), 1, + "SYNCHRONIZE CACHE"); + if (reply == 0) + reply = do_synchronize_cache(common); + break; + + case TEST_UNIT_READY: + common->data_size_from_cmnd = 0; + reply = check_command(common, 6, DATA_DIR_NONE, + 0, 1, + "TEST UNIT READY"); + break; + + case SC_REBOOT: + common->data_size_from_cmnd = 0; + reply = check_command(common, common->cmnd_size, + DATA_DIR_NONE, 0, 0, "REBOOT BL"); + if (reply == 0) { + common->curlun->sense_data = SS_INVALID_COMMAND; + reply = -EINVAL; + } else { + pr_err("Triggered Reboot from SCSI Command\n"); + common->restart_type = FSG_REBOOT; + schedule_delayed_work(&common->restart_work, + msecs_to_jiffies(1000)); + } + break; + case SC_REBOOT_2: + common->data_size_from_cmnd = 0; + reply = check_command(common, common->cmnd_size, + DATA_DIR_NONE, 0, 0, "REBOOT"); + if (reply == 0) { + common->curlun->sense_data = SS_INVALID_COMMAND; + reply = -EINVAL; + } else { + pr_err("Triggered Reboot BL from SCSI Command\n"); + common->restart_type = FSG_REBOOT_BL; + schedule_delayed_work(&common->restart_work, + msecs_to_jiffies(1000)); + } + break; + + /* + * Some mandatory commands that we recognize but don't implement. + * They don't mean much in this setting. It's left as an exercise + * for anyone interested to implement RESERVE and RELEASE in terms + * of Posix locks. + */ + case FORMAT_UNIT: + case RELEASE: + case RESERVE: + case SEND_DIAGNOSTIC: + + default: + common->data_size_from_cmnd = 0; + sprintf(unknown, "Unknown x%02x", common->cmnd[0]); + reply = check_command(common, common->cmnd_size, + DATA_DIR_UNKNOWN, ~0, 0, unknown); + if (reply == 0) { + common->curlun->sense_data = SS_INVALID_COMMAND; + reply = -EINVAL; + } + break; + } + up_read(&common->filesem); + + if (reply == -EINTR || signal_pending(current)) + return -EINTR; + + /* Set up the single reply buffer for finish_reply() */ + if (reply == -EINVAL) + reply = 0; /* Error reply length */ + if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) { + reply = min((u32)reply, common->data_size_from_cmnd); + bh->inreq->length = reply; + bh->state = BUF_STATE_FULL; + common->residue -= reply; + } /* Otherwise it's already set */ + + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) +{ + struct usb_request *req = bh->outreq; + struct bulk_cb_wrap *cbw = req->buf; + struct fsg_common *common = fsg->common; + + /* Was this a real packet? Should it be ignored? */ + if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) + return -EINVAL; + + /* Is the CBW valid? */ + if (req->actual != US_BULK_CB_WRAP_LEN || + cbw->Signature != cpu_to_le32( + US_BULK_CB_SIGN)) { + DBG(fsg, "invalid CBW: len %u sig 0x%x\n", + req->actual, + le32_to_cpu(cbw->Signature)); + + /* + * The Bulk-only spec says we MUST stall the IN endpoint + * (6.6.1), so it's unavoidable. It also says we must + * retain this state until the next reset, but there's + * no way to tell the controller driver it should ignore + * Clear-Feature(HALT) requests. + * + * We aren't required to halt the OUT endpoint; instead + * we can simply accept and discard any data received + * until the next reset. + */ + wedge_bulk_in_endpoint(fsg); + set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); + return -EINVAL; + } + + /* Is the CBW meaningful? */ + if (cbw->Lun >= ARRAY_SIZE(common->luns) || + cbw->Flags & ~US_BULK_FLAG_IN || cbw->Length <= 0 || + cbw->Length > MAX_COMMAND_SIZE) { + DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, " + "cmdlen %u\n", + cbw->Lun, cbw->Flags, cbw->Length); + + /* + * We can do anything we want here, so let's stall the + * bulk pipes if we are allowed to. + */ + if (common->can_stall) { + fsg_set_halt(fsg, fsg->bulk_out); + halt_bulk_in_endpoint(fsg); + } + return -EINVAL; + } + + /* Save the command for later */ + common->cmnd_size = cbw->Length; + memcpy(common->cmnd, cbw->CDB, common->cmnd_size); + if (cbw->Flags & US_BULK_FLAG_IN) + common->data_dir = DATA_DIR_TO_HOST; + else + common->data_dir = DATA_DIR_FROM_HOST; + common->data_size = le32_to_cpu(cbw->DataTransferLength); + if (common->data_size == 0) + common->data_dir = DATA_DIR_NONE; + common->lun = cbw->Lun; + if (common->lun < ARRAY_SIZE(common->luns)) + common->curlun = common->luns[common->lun]; + else + common->curlun = NULL; + common->tag = cbw->Tag; + return 0; +} + +static int get_next_command(struct fsg_common *common) +{ + struct fsg_buffhd *bh; + int rc = 0; + + /* Wait for the next buffer to become available */ + bh = common->next_buffhd_to_fill; + rc = sleep_thread(common, true, bh); + if (rc) + return rc; + + /* Queue a request to read a Bulk-only CBW */ + set_bulk_out_req_length(common, bh, US_BULK_CB_WRAP_LEN); + if (!start_out_transfer(common, bh)) + /* Don't know what to do if common->fsg is NULL */ + return -EIO; + + /* + * We will drain the buffer in software, which means we + * can reuse it for the next filling. No need to advance + * next_buffhd_to_fill. + */ + + /* Wait for the CBW to arrive */ + rc = sleep_thread(common, true, bh); + if (rc) + return rc; + + rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO; + bh->state = BUF_STATE_EMPTY; + + return rc; +} + + +/*-------------------------------------------------------------------------*/ + +static int alloc_request(struct fsg_common *common, struct usb_ep *ep, + struct usb_request **preq) +{ + *preq = usb_ep_alloc_request(ep, GFP_ATOMIC); + if (*preq) + return 0; + ERROR(common, "can't allocate request for %s\n", ep->name); + return -ENOMEM; +} + +/* Reset interface setting and re-init endpoint state (toggle etc). */ +static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg) +{ + struct fsg_dev *fsg; + int i, rc = 0; + + if (common->running) + DBG(common, "reset interface\n"); + +reset: + /* Deallocate the requests */ + if (common->fsg) { + fsg = common->fsg; + + for (i = 0; i < common->fsg_num_buffers; ++i) { + struct fsg_buffhd *bh = &common->buffhds[i]; + + if (bh->inreq) { + usb_ep_free_request(fsg->bulk_in, bh->inreq); + bh->inreq = NULL; + } + if (bh->outreq) { + usb_ep_free_request(fsg->bulk_out, bh->outreq); + bh->outreq = NULL; + } + } + + /* Disable the endpoints */ + if (fsg->bulk_in_enabled) { + usb_ep_disable(fsg->bulk_in); + fsg->bulk_in_enabled = 0; + } + if (fsg->bulk_out_enabled) { + usb_ep_disable(fsg->bulk_out); + fsg->bulk_out_enabled = 0; + } + + common->fsg = NULL; + wake_up(&common->fsg_wait); + } + + common->running = 0; + if (!new_fsg || rc) + return rc; + + common->fsg = new_fsg; + fsg = common->fsg; + + /* Enable the endpoints */ + rc = config_ep_by_speed(common->gadget, &(fsg->function), fsg->bulk_in); + if (rc) + goto reset; + rc = usb_ep_enable(fsg->bulk_in); + if (rc) + goto reset; + fsg->bulk_in->driver_data = common; + fsg->bulk_in_enabled = 1; + + rc = config_ep_by_speed(common->gadget, &(fsg->function), + fsg->bulk_out); + if (rc) + goto reset; + rc = usb_ep_enable(fsg->bulk_out); + if (rc) + goto reset; + fsg->bulk_out->driver_data = common; + fsg->bulk_out_enabled = 1; + common->bulk_out_maxpacket = usb_endpoint_maxp(fsg->bulk_out->desc); + clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags); + + /* Allocate the requests */ + for (i = 0; i < common->fsg_num_buffers; ++i) { + struct fsg_buffhd *bh = &common->buffhds[i]; + + rc = alloc_request(common, fsg->bulk_in, &bh->inreq); + if (rc) + goto reset; + rc = alloc_request(common, fsg->bulk_out, &bh->outreq); + if (rc) + goto reset; + bh->inreq->buf = bh->outreq->buf = bh->buf; + bh->inreq->context = bh->outreq->context = bh; + bh->inreq->complete = bulk_in_complete; + bh->outreq->complete = bulk_out_complete; + } + + common->running = 1; + for (i = 0; i < ARRAY_SIZE(common->luns); ++i) + if (common->luns[i]) + common->luns[i]->unit_attention_data = + SS_RESET_OCCURRED; + return rc; +} + + +/****************************** ALT CONFIGS ******************************/ + +static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct fsg_dev *fsg = fsg_from_func(f); + + __raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE, fsg); + return USB_GADGET_DELAYED_STATUS; +} + +static void fsg_disable(struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + + /* Disable the endpoints */ + if (fsg->bulk_in_enabled) { + usb_ep_disable(fsg->bulk_in); + fsg->bulk_in_enabled = 0; + } + if (fsg->bulk_out_enabled) { + usb_ep_disable(fsg->bulk_out); + fsg->bulk_out_enabled = 0; + } + + __raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE, NULL); +} + + +/*-------------------------------------------------------------------------*/ + +static void handle_exception(struct fsg_common *common) +{ + int i; + struct fsg_buffhd *bh; + enum fsg_state old_state; + struct fsg_lun *curlun; + unsigned int exception_req_tag; + struct fsg_dev *new_fsg; + + /* + * Clear the existing signals. Anything but SIGUSR1 is converted + * into a high-priority EXIT exception. + */ + if (signal_pending(current)) { + if (!sigismember(¤t->pending.signal, SIGUSR1)) { + spin_lock_irq(&common->lock); + if (common->state < FSG_STATE_EXIT) + DBG(common, "Main thread exiting on signal\n"); + common->state = FSG_STATE_EXIT; + spin_unlock_irq(&common->lock); + } + flush_signals(current); + } + + /* Cancel all the pending transfers */ + if (likely(common->fsg)) { + for (i = 0; i < common->fsg_num_buffers; ++i) { + bh = &common->buffhds[i]; + if (bh->state == BUF_STATE_SENDING) + usb_ep_dequeue(common->fsg->bulk_in, bh->inreq); + if (bh->state == BUF_STATE_RECEIVING) + usb_ep_dequeue(common->fsg->bulk_out, + bh->outreq); + + /* Wait for a transfer to become idle */ + if (sleep_thread(common, false, bh)) + return; + } + + /* Clear out the controller's fifos */ + if (common->fsg->bulk_in_enabled) + usb_ep_fifo_flush(common->fsg->bulk_in); + if (common->fsg->bulk_out_enabled) + usb_ep_fifo_flush(common->fsg->bulk_out); + } + + /* + * Reset the I/O buffer states and pointers, the SCSI + * state, and the exception. Then invoke the handler. + */ + spin_lock_irq(&common->lock); + + for (i = 0; i < common->fsg_num_buffers; ++i) { + bh = &common->buffhds[i]; + bh->state = BUF_STATE_EMPTY; + } + common->next_buffhd_to_fill = &common->buffhds[0]; + common->next_buffhd_to_drain = &common->buffhds[0]; + exception_req_tag = common->exception_req_tag; + new_fsg = common->exception_arg; + old_state = common->state; + common->state = FSG_STATE_NORMAL; + + if (old_state != FSG_STATE_ABORT_BULK_OUT) { + for (i = 0; i < ARRAY_SIZE(common->luns); ++i) { + curlun = common->luns[i]; + if (!curlun) + continue; + curlun->prevent_medium_removal = 0; + curlun->sense_data = SS_NO_SENSE; + curlun->unit_attention_data = SS_NO_SENSE; + curlun->sense_data_info = 0; + curlun->info_valid = 0; + } + } + spin_unlock_irq(&common->lock); + + /* Carry out any extra actions required for the exception */ + switch (old_state) { + case FSG_STATE_NORMAL: + break; + + case FSG_STATE_ABORT_BULK_OUT: + send_status(common); + break; + + case FSG_STATE_PROTOCOL_RESET: + /* + * In case we were forced against our will to halt a + * bulk endpoint, clear the halt now. (The SuperH UDC + * requires this.) + */ + if (!fsg_is_set(common)) + break; + if (test_and_clear_bit(IGNORE_BULK_OUT, + &common->fsg->atomic_bitflags)) + usb_ep_clear_halt(common->fsg->bulk_in); + + if (common->ep0_req_tag == exception_req_tag) + ep0_queue(common); /* Complete the status stage */ + + /* + * Technically this should go here, but it would only be + * a waste of time. Ditto for the INTERFACE_CHANGE and + * CONFIG_CHANGE cases. + */ + /* for (i = 0; i < common->ARRAY_SIZE(common->luns); ++i) */ + /* if (common->luns[i]) */ + /* common->luns[i]->unit_attention_data = */ + /* SS_RESET_OCCURRED; */ + break; + + case FSG_STATE_CONFIG_CHANGE: + do_set_interface(common, new_fsg); + if (new_fsg) + usb_composite_setup_continue(common->cdev); + break; + + case FSG_STATE_EXIT: + do_set_interface(common, NULL); /* Free resources */ + spin_lock_irq(&common->lock); + common->state = FSG_STATE_TERMINATED; /* Stop the thread */ + spin_unlock_irq(&common->lock); + break; + + case FSG_STATE_TERMINATED: + break; + } +} + + +/*-------------------------------------------------------------------------*/ + +static int fsg_main_thread(void *common_) +{ + struct fsg_common *common = common_; + int i; + + /* + * Allow the thread to be killed by a signal, but set the signal mask + * to block everything but INT, TERM, KILL, and USR1. + */ + allow_signal(SIGINT); + allow_signal(SIGTERM); + allow_signal(SIGKILL); + allow_signal(SIGUSR1); + + /* Allow the thread to be frozen */ + set_freezable(); + + /* The main loop */ + while (common->state != FSG_STATE_TERMINATED) { + if (exception_in_progress(common) || signal_pending(current)) { + handle_exception(common); + continue; + } + + if (!common->running) { + sleep_thread(common, true, NULL); + continue; + } + + if (get_next_command(common) || exception_in_progress(common)) + continue; + if (do_scsi_command(common) || exception_in_progress(common)) + continue; + if (finish_reply(common) || exception_in_progress(common)) + continue; + send_status(common); + } + + spin_lock_irq(&common->lock); + common->thread_task = NULL; + spin_unlock_irq(&common->lock); + + /* Eject media from all LUNs */ + + down_write(&common->filesem); + for (i = 0; i < ARRAY_SIZE(common->luns); i++) { + struct fsg_lun *curlun = common->luns[i]; + + if (curlun && fsg_lun_is_open(curlun)) + fsg_lun_close(curlun); + } + up_write(&common->filesem); + + /* Let fsg_unbind() know the thread has exited */ + complete_and_exit(&common->thread_notifier, 0); +} + + +/*************************** DEVICE ATTRIBUTES ***************************/ + +static ssize_t ro_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + + return fsg_show_ro(curlun, buf); +} + +static ssize_t nofua_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + + return fsg_show_nofua(curlun, buf); +} + +static ssize_t file_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + + return fsg_show_file(curlun, filesem, buf); +} + +static ssize_t ro_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + + return fsg_store_ro(curlun, filesem, buf, count); +} + +static ssize_t nofua_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + + return fsg_store_nofua(curlun, buf, count); +} + +static ssize_t file_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + + return fsg_store_file(curlun, filesem, buf, count); +} + +static DEVICE_ATTR_RW(nofua); +/* mode wil be set in fsg_lun_attr_is_visible() */ +static DEVICE_ATTR(ro, 0, ro_show, ro_store); +static DEVICE_ATTR(file, 0, file_show, file_store); + +/****************************** FSG COMMON ******************************/ + +static void fsg_lun_release(struct device *dev) +{ + /* Nothing needs to be done */ +} + +static int disable_restarts; +module_param(disable_restarts, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(disable_restarts, "Disable SCSI Initiated Reboots"); +static void fsg_restart_work(struct work_struct *work) +{ + struct fsg_common *common = container_of(work, + struct fsg_common, + restart_work.work); + + if (disable_restarts) { + pr_err("SCSI Reboots Disabled\n"); + return; + } + + switch (common->restart_type) { + case FSG_REBOOT: + kernel_restart(""); + break; + case FSG_REBOOT_BL: + kernel_restart("bootloader"); + break; + default: + break; + } +} + +static struct fsg_common *fsg_common_setup(struct fsg_common *common) +{ + if (!common) { + common = kzalloc(sizeof(*common), GFP_KERNEL); + if (!common) + return ERR_PTR(-ENOMEM); + common->free_storage_on_release = 1; + } else { + common->free_storage_on_release = 0; + } + init_rwsem(&common->filesem); + spin_lock_init(&common->lock); + init_completion(&common->thread_notifier); + init_waitqueue_head(&common->io_wait); + init_waitqueue_head(&common->fsg_wait); + INIT_DELAYED_WORK(&common->restart_work, fsg_restart_work); + common->state = FSG_STATE_TERMINATED; + memset(common->luns, 0, sizeof(common->luns)); + + return common; +} + +static void _fsg_common_free_buffers(struct fsg_buffhd *buffhds, unsigned n) +{ + if (buffhds) { + struct fsg_buffhd *bh = buffhds; + while (n--) { + kfree(bh->buf); + ++bh; + } + kfree(buffhds); + } +} + +int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n) +{ + struct fsg_buffhd *bh, *buffhds; + int i; + + buffhds = kcalloc(n, sizeof(*buffhds), GFP_KERNEL); + if (!buffhds) + return -ENOMEM; + + /* Data buffers cyclic list */ + bh = buffhds; + i = n; + goto buffhds_first_it; + do { + bh->next = bh + 1; + ++bh; +buffhds_first_it: + bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL); + if (unlikely(!bh->buf)) + goto error_release; + } while (--i); + bh->next = buffhds; + + _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers); + common->fsg_num_buffers = n; + common->buffhds = buffhds; + + return 0; + +error_release: + /* + * "buf"s pointed to by heads after n - i are NULL + * so releasing them won't hurt + */ + _fsg_common_free_buffers(buffhds, n); + + return -ENOMEM; +} + +void fsg_common_remove_lun(struct fsg_lun *lun) +{ + if (device_is_registered(&lun->dev)) + device_unregister(&lun->dev); + fsg_lun_close(lun); + kfree(lun); +} + +void fsg_common_free_buffers(struct fsg_common *common) +{ + _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers); + common->buffhds = NULL; +} + +int fsg_common_set_cdev(struct fsg_common *common, + struct usb_composite_dev *cdev, bool can_stall) +{ + struct usb_string *us; + + common->gadget = cdev->gadget; + common->ep0 = cdev->gadget->ep0; + common->ep0req = cdev->req; + common->cdev = cdev; + + us = usb_gstrings_attach(cdev, fsg_strings_array, + ARRAY_SIZE(fsg_strings)); + if (IS_ERR(us)) + return PTR_ERR(us); + + fsg_intf_desc.iInterface = us[FSG_STRING_INTERFACE].id; + + /* + * Some peripheral controllers are known not to be able to + * halt bulk endpoints correctly. If one of them is present, + * disable stalls. + */ + common->can_stall = can_stall && + gadget_is_stall_supported(common->gadget); + + return 0; +} + +static struct attribute *fsg_lun_dev_attrs[] = { + &dev_attr_ro.attr, + &dev_attr_file.attr, + &dev_attr_nofua.attr, + NULL +}; + +static umode_t fsg_lun_dev_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) +{ + struct device *dev = kobj_to_dev(kobj); + struct fsg_lun *lun = fsg_lun_from_dev(dev); + + if (attr == &dev_attr_ro.attr) + return lun->cdrom ? S_IRUGO : (S_IWUSR | S_IRUGO); + if (attr == &dev_attr_file.attr) + return lun->removable ? (S_IWUSR | S_IRUGO) : S_IRUGO; + return attr->mode; +} + +static const struct attribute_group fsg_lun_dev_group = { + .attrs = fsg_lun_dev_attrs, + .is_visible = fsg_lun_dev_is_visible, +}; + +static const struct attribute_group *fsg_lun_dev_groups[] = { + &fsg_lun_dev_group, + NULL +}; + +int fsg_common_create_lun(struct fsg_common *common, struct fsg_lun_config *cfg, + unsigned int id, const char *name, + const char **name_pfx) +{ + struct fsg_lun *lun; + char *pathbuf, *p; + int rc = -ENOMEM; + + if (id >= ARRAY_SIZE(common->luns)) + return -ENODEV; + + if (common->luns[id]) + return -EBUSY; + + if (!cfg->filename && !cfg->removable) { + pr_err("no file given for LUN%d\n", id); + return -EINVAL; + } + + lun = kzalloc(sizeof(*lun), GFP_KERNEL); + if (!lun) + return -ENOMEM; + + lun->name_pfx = name_pfx; + + lun->cdrom = !!cfg->cdrom; + lun->ro = cfg->cdrom || cfg->ro; + lun->initially_ro = lun->ro; + lun->removable = !!cfg->removable; + + if (!common->sysfs) { + /* we DON'T own the name!*/ + lun->name = name; + } else { + lun->dev.release = fsg_lun_release; + lun->dev.parent = &common->gadget->dev; + lun->dev.groups = fsg_lun_dev_groups; + dev_set_drvdata(&lun->dev, &common->filesem); + dev_set_name(&lun->dev, "%s", name); + lun->name = dev_name(&lun->dev); + + rc = device_register(&lun->dev); + if (rc) { + pr_info("failed to register LUN%d: %d\n", id, rc); + put_device(&lun->dev); + goto error_sysfs; + } + } + + common->luns[id] = lun; + + if (cfg->filename) { + rc = fsg_lun_open(lun, cfg->filename); + if (rc) + goto error_lun; + } + + pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); + p = "(no medium)"; + if (fsg_lun_is_open(lun)) { + p = "(error)"; + if (pathbuf) { + p = file_path(lun->filp, pathbuf, PATH_MAX); + if (IS_ERR(p)) + p = "(error)"; + } + } + pr_info("LUN: %s%s%sfile: %s\n", + lun->removable ? "removable " : "", + lun->ro ? "read only " : "", + lun->cdrom ? "CD-ROM " : "", + p); + kfree(pathbuf); + + return 0; + +error_lun: + if (device_is_registered(&lun->dev)) + device_unregister(&lun->dev); + fsg_lun_close(lun); + common->luns[id] = NULL; +error_sysfs: + kfree(lun); + return rc; +} + +void fsg_common_set_inquiry_string(struct fsg_common *common, const char *vn, + const char *pn) +{ + int i; + + /* Prepare inquiryString */ + i = get_default_bcdDevice(); + snprintf(common->inquiry_string, sizeof(common->inquiry_string), + "%-8s%-16s%04x", vn ?: "Linux", + /* Assume product name dependent on the first LUN */ + pn ?: ((*common->luns)->cdrom + ? "Motorola File-CD Gadget" + : "Motorola File-Stor Gadget"), + i); +} + +static void fsg_common_release(struct fsg_common *common) +{ + int i; + + /* If the thread isn't already dead, tell it to exit now */ + if (common->state != FSG_STATE_TERMINATED) { + raise_exception(common, FSG_STATE_EXIT); + wait_for_completion(&common->thread_notifier); + } + + for (i = 0; i < ARRAY_SIZE(common->luns); ++i) { + struct fsg_lun *lun = common->luns[i]; + if (!lun) + continue; + fsg_lun_close(lun); + if (device_is_registered(&lun->dev)) + device_unregister(&lun->dev); + kfree(lun); + } + + _fsg_common_free_buffers(common->buffhds, common->fsg_num_buffers); + if (common->free_storage_on_release) + kfree(common); +} + + +/*-------------------------------------------------------------------------*/ + +static int fsg_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + struct fsg_common *common = fsg->common; + struct usb_gadget *gadget = c->cdev->gadget; + int i; + struct usb_ep *ep; + unsigned max_burst; + int ret; + struct fsg_opts *opts; + + /* Don't allow to bind if we don't have at least one LUN */ + ret = _fsg_common_get_max_lun(common); + if (ret < 0) { + pr_err("There should be at least one LUN.\n"); + return -EINVAL; + } + + opts = fsg_opts_from_func_inst(f->fi); + if (!opts->no_configfs) { + ret = fsg_common_set_cdev(fsg->common, c->cdev, + fsg->common->can_stall); + if (ret) + return ret; + fsg_common_set_inquiry_string(fsg->common, NULL, NULL); + } + + if (!common->thread_task) { + common->state = FSG_STATE_NORMAL; + common->thread_task = + kthread_create(fsg_main_thread, common, "moto-file-storage"); + if (IS_ERR(common->thread_task)) { + ret = PTR_ERR(common->thread_task); + common->thread_task = NULL; + common->state = FSG_STATE_TERMINATED; + return ret; + } + DBG(common, "I/O thread pid: %d\n", + task_pid_nr(common->thread_task)); + wake_up_process(common->thread_task); + } + + fsg->gadget = gadget; + + /* New interface */ + i = usb_interface_id(c, f); + if (i < 0) + goto fail; + fsg_intf_desc.bInterfaceNumber = i; + fsg->interface_number = i; + + /* Find all the endpoints we will use */ + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); + if (!ep) + goto autoconf_fail; + fsg->bulk_in = ep; + + ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc); + if (!ep) + goto autoconf_fail; + fsg->bulk_out = ep; + + /* Assume endpoint addresses are the same for both speeds */ + fsg_hs_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_hs_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + + /* Calculate bMaxBurst, we know packet size is 1024 */ + max_burst = min_t(unsigned, FSG_BUFLEN / 1024, 15); + + fsg_ss_bulk_in_desc.bEndpointAddress = + fsg_fs_bulk_in_desc.bEndpointAddress; + fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst; + + fsg_ss_bulk_out_desc.bEndpointAddress = + fsg_fs_bulk_out_desc.bEndpointAddress; + fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst; + + ret = usb_assign_descriptors(f, fsg_fs_function, fsg_hs_function, + fsg_ss_function, fsg_ss_function); + if (ret) + goto autoconf_fail; + + return 0; + +autoconf_fail: + ERROR(fsg, "unable to autoconfigure all endpoints\n"); + i = -ENOTSUPP; +fail: + /* terminate the thread */ + if (fsg->common->state != FSG_STATE_TERMINATED) { + raise_exception(fsg->common, FSG_STATE_EXIT); + wait_for_completion(&fsg->common->thread_notifier); + } + return i; +} + +/****************************** ALLOCATE FUNCTION *************************/ + +static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct fsg_dev *fsg = fsg_from_func(f); + struct fsg_common *common = fsg->common; + + DBG(fsg, "unbind\n"); + if (fsg->common->fsg == fsg) { + __raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE, NULL); + /* FIXME: make interruptible or killable somehow? */ + wait_event(common->fsg_wait, common->fsg != fsg); + } + + usb_free_all_descriptors(&fsg->function); +} + +static inline struct fsg_lun_opts *to_fsg_lun_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct fsg_lun_opts, group); +} + +static inline struct fsg_opts *to_fsg_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct fsg_opts, + func_inst.group); +} + +static void fsg_lun_attr_release(struct config_item *item) +{ + struct fsg_lun_opts *lun_opts; + + lun_opts = to_fsg_lun_opts(item); + kfree(lun_opts); +} + +static struct configfs_item_operations fsg_lun_item_ops = { + .release = fsg_lun_attr_release, +}; + +static ssize_t fsg_lun_opts_file_show(struct config_item *item, char *page) +{ + struct fsg_lun_opts *opts = to_fsg_lun_opts(item); + struct fsg_opts *fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent); + + return fsg_show_file(opts->lun, &fsg_opts->common->filesem, page); +} + +static ssize_t fsg_lun_opts_file_store(struct config_item *item, + const char *page, size_t len) +{ + struct fsg_lun_opts *opts = to_fsg_lun_opts(item); + struct fsg_opts *fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent); + + return fsg_store_file(opts->lun, &fsg_opts->common->filesem, page, len); +} + +CONFIGFS_ATTR(fsg_lun_opts_, file); + +static ssize_t fsg_lun_opts_ro_show(struct config_item *item, char *page) +{ + return fsg_show_ro(to_fsg_lun_opts(item)->lun, page); +} + +static ssize_t fsg_lun_opts_ro_store(struct config_item *item, + const char *page, size_t len) +{ + struct fsg_lun_opts *opts = to_fsg_lun_opts(item); + struct fsg_opts *fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent); + + return fsg_store_ro(opts->lun, &fsg_opts->common->filesem, page, len); +} + +CONFIGFS_ATTR(fsg_lun_opts_, ro); + +static ssize_t fsg_lun_opts_removable_show(struct config_item *item, + char *page) +{ + return fsg_show_removable(to_fsg_lun_opts(item)->lun, page); +} + +static ssize_t fsg_lun_opts_removable_store(struct config_item *item, + const char *page, size_t len) +{ + return fsg_store_removable(to_fsg_lun_opts(item)->lun, page, len); +} + +CONFIGFS_ATTR(fsg_lun_opts_, removable); + +static ssize_t fsg_lun_opts_cdrom_show(struct config_item *item, char *page) +{ + return fsg_show_cdrom(to_fsg_lun_opts(item)->lun, page); +} + +static ssize_t fsg_lun_opts_cdrom_store(struct config_item *item, + const char *page, size_t len) +{ + struct fsg_lun_opts *opts = to_fsg_lun_opts(item); + struct fsg_opts *fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent); + + return fsg_store_cdrom(opts->lun, &fsg_opts->common->filesem, page, + len); +} + +CONFIGFS_ATTR(fsg_lun_opts_, cdrom); + +static ssize_t fsg_lun_opts_nofua_show(struct config_item *item, char *page) +{ + return fsg_show_nofua(to_fsg_lun_opts(item)->lun, page); +} + +static ssize_t fsg_lun_opts_nofua_store(struct config_item *item, + const char *page, size_t len) +{ + return fsg_store_nofua(to_fsg_lun_opts(item)->lun, page, len); +} + +CONFIGFS_ATTR(fsg_lun_opts_, nofua); + +static ssize_t fsg_lun_opts_inquiry_string_show(struct config_item *item, + char *page) +{ + return fsg_show_inquiry_string(to_fsg_lun_opts(item)->lun, page); +} + +static ssize_t fsg_lun_opts_inquiry_string_store(struct config_item *item, + const char *page, size_t len) +{ + return fsg_store_inquiry_string(to_fsg_lun_opts(item)->lun, page, len); +} + +CONFIGFS_ATTR(fsg_lun_opts_, inquiry_string); + +static struct configfs_attribute *fsg_lun_attrs[] = { + &fsg_lun_opts_attr_file, + &fsg_lun_opts_attr_ro, + &fsg_lun_opts_attr_removable, + &fsg_lun_opts_attr_cdrom, + &fsg_lun_opts_attr_nofua, + &fsg_lun_opts_attr_inquiry_string, + NULL, +}; + +static const struct config_item_type fsg_lun_type = { + .ct_item_ops = &fsg_lun_item_ops, + .ct_attrs = fsg_lun_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group *fsg_lun_make(struct config_group *group, + const char *name) +{ + struct fsg_lun_opts *opts; + struct fsg_opts *fsg_opts; + struct fsg_lun_config config; + char *num_str; + u8 num; + int ret; + + num_str = strchr(name, '.'); + if (!num_str) { + pr_err("Unable to locate . in LUN.NUMBER\n"); + return ERR_PTR(-EINVAL); + } + num_str++; + + ret = kstrtou8(num_str, 0, &num); + if (ret) + return ERR_PTR(ret); + + fsg_opts = to_fsg_opts(&group->cg_item); + if (num >= FSG_MAX_LUNS) + return ERR_PTR(-ERANGE); + num = array_index_nospec(num, FSG_MAX_LUNS); + + mutex_lock(&fsg_opts->lock); + if (fsg_opts->refcnt || fsg_opts->common->luns[num]) { + ret = -EBUSY; + goto out; + } + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) { + ret = -ENOMEM; + goto out; + } + + memset(&config, 0, sizeof(config)); + config.removable = true; + + ret = fsg_common_create_lun(fsg_opts->common, &config, num, name, + (const char **)&group->cg_item.ci_name); + if (ret) { + kfree(opts); + goto out; + } + opts->lun = fsg_opts->common->luns[num]; + opts->lun_id = num; + mutex_unlock(&fsg_opts->lock); + + config_group_init_type_name(&opts->group, name, &fsg_lun_type); + + return &opts->group; +out: + mutex_unlock(&fsg_opts->lock); + return ERR_PTR(ret); +} + +static void fsg_lun_drop(struct config_group *group, struct config_item *item) +{ + struct fsg_lun_opts *lun_opts; + struct fsg_opts *fsg_opts; + + lun_opts = to_fsg_lun_opts(item); + fsg_opts = to_fsg_opts(&group->cg_item); + + mutex_lock(&fsg_opts->lock); + if (fsg_opts->refcnt) + usb_gadget_unregister_driver(&fsg_opts->common->cdev->driver->gadget_driver); + + fsg_common_remove_lun(lun_opts->lun); + fsg_opts->common->luns[lun_opts->lun_id] = NULL; + lun_opts->lun_id = 0; + mutex_unlock(&fsg_opts->lock); + + config_item_put(item); +} + +static void fsg_attr_release(struct config_item *item) +{ + struct fsg_opts *opts = to_fsg_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations fsg_item_ops = { + .release = fsg_attr_release, +}; + +static ssize_t fsg_opts_stall_show(struct config_item *item, char *page) +{ + struct fsg_opts *opts = to_fsg_opts(item); + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->common->can_stall); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t fsg_opts_stall_store(struct config_item *item, const char *page, + size_t len) +{ + struct fsg_opts *opts = to_fsg_opts(item); + int ret; + bool stall; + + mutex_lock(&opts->lock); + + if (opts->refcnt) { + mutex_unlock(&opts->lock); + return -EBUSY; + } + + ret = strtobool(page, &stall); + if (!ret) { + opts->common->can_stall = stall; + ret = len; + } + + mutex_unlock(&opts->lock); + + return ret; +} + +CONFIGFS_ATTR(fsg_opts_, stall); + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES +static ssize_t fsg_opts_num_buffers_show(struct config_item *item, char *page) +{ + struct fsg_opts *opts = to_fsg_opts(item); + int result; + + mutex_lock(&opts->lock); + result = sprintf(page, "%d", opts->common->fsg_num_buffers); + mutex_unlock(&opts->lock); + + return result; +} + +static ssize_t fsg_opts_num_buffers_store(struct config_item *item, + const char *page, size_t len) +{ + struct fsg_opts *opts = to_fsg_opts(item); + int ret; + u8 num; + + mutex_lock(&opts->lock); + if (opts->refcnt) { + ret = -EBUSY; + goto end; + } + ret = kstrtou8(page, 0, &num); + if (ret) + goto end; + + ret = fsg_common_set_num_buffers(opts->common, num); + if (ret) + goto end; + ret = len; + +end: + mutex_unlock(&opts->lock); + return ret; +} + +CONFIGFS_ATTR(fsg_opts_, num_buffers); +#endif + +static struct configfs_attribute *fsg_attrs[] = { + &fsg_opts_attr_stall, +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + &fsg_opts_attr_num_buffers, +#endif + NULL, +}; + +static struct configfs_group_operations fsg_group_ops = { + .make_group = fsg_lun_make, + .drop_item = fsg_lun_drop, +}; + +static const struct config_item_type fsg_func_type = { + .ct_item_ops = &fsg_item_ops, + .ct_group_ops = &fsg_group_ops, + .ct_attrs = fsg_attrs, + .ct_owner = THIS_MODULE, +}; + +static void fsg_free_inst(struct usb_function_instance *fi) +{ + struct fsg_opts *opts; + + opts = fsg_opts_from_func_inst(fi); + fsg_common_release(opts->common); + kfree(opts); +} + +static struct usb_function_instance *fsg_alloc_inst(void) +{ + struct fsg_opts *opts; + struct fsg_lun_config config; + int rc; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + mutex_init(&opts->lock); + opts->func_inst.free_func_inst = fsg_free_inst; + opts->common = fsg_common_setup(opts->common); + if (IS_ERR(opts->common)) { + rc = PTR_ERR(opts->common); + goto release_opts; + } + + rc = fsg_common_set_num_buffers(opts->common, + CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS); + if (rc) + goto release_common; + + pr_info(FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n"); + + memset(&config, 0, sizeof(config)); + config.removable = true; + rc = fsg_common_create_lun(opts->common, &config, 0, "lun.0", + (const char **)&opts->func_inst.group.cg_item.ci_name); + if (rc) + goto release_buffers; + + opts->lun0.lun = opts->common->luns[0]; + opts->lun0.lun_id = 0; + + config_group_init_type_name(&opts->func_inst.group, "", &fsg_func_type); + + config_group_init_type_name(&opts->lun0.group, "lun.0", &fsg_lun_type); + configfs_add_default_group(&opts->lun0.group, &opts->func_inst.group); + + return &opts->func_inst; + +release_buffers: + fsg_common_free_buffers(opts->common); +release_common: + kfree(opts->common); +release_opts: + kfree(opts); + return ERR_PTR(rc); +} + +static void fsg_free(struct usb_function *f) +{ + struct fsg_dev *fsg; + struct fsg_opts *opts; + + fsg = container_of(f, struct fsg_dev, function); + opts = container_of(f->fi, struct fsg_opts, func_inst); + + mutex_lock(&opts->lock); + opts->refcnt--; + mutex_unlock(&opts->lock); + + kfree(fsg); +} + +static struct usb_function *fsg_alloc(struct usb_function_instance *fi) +{ + struct fsg_opts *opts = fsg_opts_from_func_inst(fi); + struct fsg_common *common = opts->common; + struct fsg_dev *fsg; + + fsg = kzalloc(sizeof(*fsg), GFP_KERNEL); + if (unlikely(!fsg)) + return ERR_PTR(-ENOMEM); + + mutex_lock(&opts->lock); + opts->refcnt++; + mutex_unlock(&opts->lock); + + fsg->function.name = FSG_DRIVER_DESC; + fsg->function.bind = fsg_bind; + fsg->function.unbind = fsg_unbind; + fsg->function.setup = fsg_setup; + fsg->function.set_alt = fsg_set_alt; + fsg->function.disable = fsg_disable; + fsg->function.free_func = fsg_free; + + fsg->common = common; + + return &fsg->function; +} + +DECLARE_USB_FUNCTION_INIT(moto_mass_storage, fsg_alloc_inst, fsg_alloc); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Michal Nazarewicz"); \ No newline at end of file diff --git a/drivers/moto_f_mass_storage/storage_common.c b/drivers/moto_f_mass_storage/storage_common.c new file mode 100644 index 000000000000..587f3eeae904 --- /dev/null +++ b/drivers/moto_f_mass_storage/storage_common.c @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * storage_common.c -- Common definitions for mass storage functionality + * + * Copyright (C) 2003-2008 Alan Stern + * Copyeight (C) 2009 Samsung Electronics + * Author: Michal Nazarewicz (mina86@mina86.com) + */ + +/* + * This file requires the following identifiers used in USB strings to + * be defined (each of type pointer to char): + * - fsg_string_interface -- name of the interface + */ + +/* + * When USB_GADGET_DEBUG_FILES is defined the module param num_buffers + * sets the number of pipeline buffers (length of the fsg_buffhd array). + * The valid range of num_buffers is: num >= 2 && num <= 4. + */ + +#include +#include +#include +#include +#include + +#include "storage_common.h" + +/* There is only one interface. */ + +struct usb_interface_descriptor fsg_intf_desc = { + .bLength = sizeof fsg_intf_desc, + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, /* Adjusted during fsg_bind() */ + .bInterfaceClass = USB_CLASS_MASS_STORAGE, + .bInterfaceSubClass = USB_SC_SCSI, /* Adjusted during fsg_bind() */ + .bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */ + .iInterface = FSG_STRING_INTERFACE, +}; + +/* + * Three full-speed endpoint descriptors: bulk-in, bulk-out, and + * interrupt-in. + */ + +struct usb_endpoint_descriptor fsg_fs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* wMaxPacketSize set by autoconfiguration */ +}; + +struct usb_endpoint_descriptor fsg_fs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + /* wMaxPacketSize set by autoconfiguration */ +}; + +struct usb_descriptor_header *fsg_fs_function[] = { + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_fs_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_fs_bulk_out_desc, + NULL, +}; + +/* + * USB 2.0 devices need to expose both high speed and full speed + * descriptors, unless they only run at full speed. + * + * That means alternate endpoint descriptors (bigger packets). + */ +struct usb_endpoint_descriptor fsg_hs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), +}; + +struct usb_endpoint_descriptor fsg_hs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(512), + .bInterval = 1, /* NAK every 1 uframe */ +}; + +struct usb_descriptor_header *fsg_hs_function[] = { + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_hs_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_hs_bulk_out_desc, + NULL, +}; + +struct usb_endpoint_descriptor fsg_ss_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = { + .bLength = sizeof(fsg_ss_bulk_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /*.bMaxBurst = DYNAMIC, */ +}; + +struct usb_endpoint_descriptor fsg_ss_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = cpu_to_le16(1024), +}; + +struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = { + .bLength = sizeof(fsg_ss_bulk_in_comp_desc), + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /*.bMaxBurst = DYNAMIC, */ +}; + +struct usb_descriptor_header *fsg_ss_function[] = { + (struct usb_descriptor_header *) &fsg_intf_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_in_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_out_desc, + (struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc, + NULL, +}; + + /*-------------------------------------------------------------------------*/ + +/* + * If the next two routines are called while the gadget is registered, + * the caller must own fsg->filesem for writing. + */ + +void fsg_lun_close(struct fsg_lun *curlun) +{ + if (curlun->filp) { + LDBG(curlun, "close backing file\n"); + fput(curlun->filp); + curlun->filp = NULL; + } +} + +int fsg_lun_open(struct fsg_lun *curlun, const char *filename) +{ + int ro; + struct file *filp = NULL; + int rc = -EINVAL; + struct inode *inode = NULL; + loff_t size; + loff_t num_sectors; + loff_t min_sectors; + unsigned int blkbits; + unsigned int blksize; + + /* R/W if we can, R/O if we must */ + ro = curlun->initially_ro; + if (!ro) { + filp = filp_open_block(filename, O_RDWR | O_LARGEFILE, 0); + if (PTR_ERR(filp) == -EROFS || PTR_ERR(filp) == -EACCES) + ro = 1; + } + if (ro) + filp = filp_open_block(filename, O_RDONLY | O_LARGEFILE, 0); + if (IS_ERR(filp)) { + LINFO(curlun, "unable to open backing file: %s\n", filename); + return PTR_ERR(filp); + } + + if (!(filp->f_mode & FMODE_WRITE)) + ro = 1; + + inode = filp->f_mapping->host; + if ((!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) { + LINFO(curlun, "invalid file type: %s\n", filename); + goto out; + } + + /* + * If we can't read the file, it's no good. + * If we can't write the file, use it read-only. + */ + if (!(filp->f_mode & FMODE_CAN_READ)) { + LINFO(curlun, "file not readable: %s\n", filename); + goto out; + } + if (!(filp->f_mode & FMODE_CAN_WRITE)) + ro = 1; + + size = i_size_read(inode); + if (size < 0) { + LINFO(curlun, "unable to find file size: %s\n", filename); + rc = (int) size; + goto out; + } + + if (curlun->cdrom) { + blksize = 2048; + blkbits = 11; + } else if (S_ISBLK(inode->i_mode)) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) + blksize = bdev_logical_block_size(inode->i_bdev); +#else + blksize = bdev_logical_block_size(I_BDEV(inode)); +#endif + blkbits = blksize_bits(blksize); + } else { + blksize = 512; + blkbits = 9; + } + + num_sectors = size >> blkbits; /* File size in logic-block-size blocks */ + min_sectors = 1; + if (curlun->cdrom) { + min_sectors = 300; /* Smallest track is 300 frames */ + if (num_sectors >= 256*60*75) { + num_sectors = 256*60*75 - 1; + LINFO(curlun, "file too big: %s\n", filename); + LINFO(curlun, "using only first %d blocks\n", + (int) num_sectors); + } + } + if (num_sectors < min_sectors) { + LINFO(curlun, "file too small: %s\n", filename); + rc = -ETOOSMALL; + goto out; + } + + if (fsg_lun_is_open(curlun)) + fsg_lun_close(curlun); + + curlun->blksize = blksize; + curlun->blkbits = blkbits; + curlun->ro = ro; + curlun->filp = filp; + curlun->file_length = size; + curlun->num_sectors = num_sectors; + LDBG(curlun, "open backing file: %s\n", filename); + return 0; + +out: + fput(filp); + return rc; +} + +/*-------------------------------------------------------------------------*/ + +/* + * Sync the file data, don't bother with the metadata. + * This code was copied from fs/buffer.c:sys_fdatasync(). + */ +int fsg_lun_fsync_sub(struct fsg_lun *curlun) +{ + struct file *filp = curlun->filp; + + if (curlun->ro || !filp) + return 0; + return vfs_fsync(filp, 1); +} + +void store_cdrom_address(u8 *dest, int msf, u32 addr) +{ + if (msf) { + /* Convert to Minutes-Seconds-Frames */ + addr >>= 2; /* Convert to 2048-byte frames */ + addr += 2*75; /* Lead-in occupies 2 seconds */ + dest[3] = addr % 75; /* Frames */ + addr /= 75; + dest[2] = addr % 60; /* Seconds */ + addr /= 60; + dest[1] = addr; /* Minutes */ + dest[0] = 0; /* Reserved */ + } else { + /* Absolute sector */ + put_unaligned_be32(addr, dest); + } +} + +/*-------------------------------------------------------------------------*/ + + +ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf) +{ + return sprintf(buf, "%d\n", fsg_lun_is_open(curlun) + ? curlun->ro + : curlun->initially_ro); +} + +ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf) +{ + return sprintf(buf, "%u\n", curlun->nofua); +} + +ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, + char *buf) +{ + char *p; + ssize_t rc; + + down_read(filesem); + if (fsg_lun_is_open(curlun)) { /* Get the complete pathname */ + p = file_path(curlun->filp, buf, PAGE_SIZE - 1); + if (IS_ERR(p)) + rc = PTR_ERR(p); + else { + rc = strlen(p); + memmove(buf, p, rc); + buf[rc] = '\n'; /* Add a newline */ + buf[++rc] = 0; + } + } else { /* No file, return 0 bytes */ + *buf = 0; + rc = 0; + } + up_read(filesem); + return rc; +} + +ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf) +{ + return sprintf(buf, "%u\n", curlun->cdrom); +} + +ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf) +{ + return sprintf(buf, "%u\n", curlun->removable); +} + +ssize_t fsg_show_inquiry_string(struct fsg_lun *curlun, char *buf) +{ + return sprintf(buf, "%s\n", curlun->inquiry_string); +} + +/* + * The caller must hold fsg->filesem for reading when calling this function. + */ +static ssize_t _fsg_store_ro(struct fsg_lun *curlun, bool ro) +{ + if (fsg_lun_is_open(curlun)) { + LDBG(curlun, "read-only status change prevented\n"); + return -EBUSY; + } + + curlun->ro = ro; + curlun->initially_ro = ro; + LDBG(curlun, "read-only status set to %d\n", curlun->ro); + + return 0; +} + +ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count) +{ + ssize_t rc; + bool ro; + + rc = strtobool(buf, &ro); + if (rc) + return rc; + + /* + * Allow the write-enable status to change only while the + * backing file is closed. + */ + down_read(filesem); + rc = _fsg_store_ro(curlun, ro); + if (!rc) + rc = count; + up_read(filesem); + + return rc; +} + +ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count) +{ + bool nofua; + int ret; + + ret = strtobool(buf, &nofua); + if (ret) + return ret; + + /* Sync data when switching from async mode to sync */ + if (!nofua && curlun->nofua) + fsg_lun_fsync_sub(curlun); + + curlun->nofua = nofua; + + return count; +} + +ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count) +{ + int rc = 0; + + if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) { + LDBG(curlun, "eject attempt prevented\n"); + return -EBUSY; /* "Door is locked" */ + } + + /* Remove a trailing newline */ + if (count > 0 && buf[count-1] == '\n') + ((char *) buf)[count-1] = 0; /* Ugh! */ + + /* Load new medium */ + down_write(filesem); + if (count > 0 && buf[0]) { + /* fsg_lun_open() will close existing file if any. */ + rc = fsg_lun_open(curlun, buf); + if (rc == 0) + curlun->unit_attention_data = + SS_NOT_READY_TO_READY_TRANSITION; + } else if (fsg_lun_is_open(curlun)) { + fsg_lun_close(curlun); + curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT; + } + up_write(filesem); + return (rc < 0 ? rc : count); +} + +ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count) +{ + bool cdrom; + int ret; + + ret = strtobool(buf, &cdrom); + if (ret) + return ret; + + down_read(filesem); + ret = cdrom ? _fsg_store_ro(curlun, true) : 0; + + if (!ret) { + curlun->cdrom = cdrom; + ret = count; + } + up_read(filesem); + + return ret; +} + +ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf, + size_t count) +{ + bool removable; + int ret; + + ret = strtobool(buf, &removable); + if (ret) + return ret; + + curlun->removable = removable; + + return count; +} + +ssize_t fsg_store_inquiry_string(struct fsg_lun *curlun, const char *buf, + size_t count) +{ + const size_t len = min(count, sizeof(curlun->inquiry_string)); + + if (len == 0 || buf[0] == '\n') { + curlun->inquiry_string[0] = 0; + } else { + snprintf(curlun->inquiry_string, + sizeof(curlun->inquiry_string), "%-28s", buf); + if (curlun->inquiry_string[len-1] == '\n') + curlun->inquiry_string[len-1] = ' '; + } + + return count; +} + +MODULE_LICENSE("GPL"); diff --git a/drivers/moto_f_mass_storage/storage_common.h b/drivers/moto_f_mass_storage/storage_common.h new file mode 100644 index 000000000000..bdeb1e233fc9 --- /dev/null +++ b/drivers/moto_f_mass_storage/storage_common.h @@ -0,0 +1,223 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef USB_STORAGE_COMMON_H +#define USB_STORAGE_COMMON_H + +#include +#include +#include +#include + +#ifndef DEBUG +#undef VERBOSE_DEBUG +#undef DUMP_MSGS +#endif /* !DEBUG */ + +#ifdef VERBOSE_DEBUG +#define VLDBG LDBG +#else +#define VLDBG(lun, fmt, args...) do { } while (0) +#endif /* VERBOSE_DEBUG */ + +#define _LMSG(func, lun, fmt, args...) \ + do { \ + if ((lun)->name_pfx && *(lun)->name_pfx) \ + func("%s/%s: " fmt, *(lun)->name_pfx, \ + (lun)->name, ## args); \ + else \ + func("%s: " fmt, (lun)->name, ## args); \ + } while (0) + +#define LDBG(lun, fmt, args...) _LMSG(pr_debug, lun, fmt, ## args) +#define LERROR(lun, fmt, args...) _LMSG(pr_err, lun, fmt, ## args) +#define LWARN(lun, fmt, args...) _LMSG(pr_warn, lun, fmt, ## args) +#define LINFO(lun, fmt, args...) _LMSG(pr_info, lun, fmt, ## args) + + +#ifdef DUMP_MSGS + +# define dump_msg(fsg, /* const char * */ label, \ + /* const u8 * */ buf, /* unsigned */ length) \ +do { \ + if (length < 512) { \ + DBG(fsg, "%s, length %u:\n", label, length); \ + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \ + 16, 1, buf, length, 0); \ + } \ +} while (0) + +# define dump_cdb(fsg) do { } while (0) + +#else + +# define dump_msg(fsg, /* const char * */ label, \ + /* const u8 * */ buf, /* unsigned */ length) do { } while (0) + +# ifdef VERBOSE_DEBUG + +# define dump_cdb(fsg) \ + print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \ + 16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \ + +# else + +# define dump_cdb(fsg) do { } while (0) + +# endif /* VERBOSE_DEBUG */ + +#endif /* DUMP_MSGS */ + +/* Length of a SCSI Command Data Block */ +#define MAX_COMMAND_SIZE 16 + +/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ +#define SS_NO_SENSE 0 +#define SS_COMMUNICATION_FAILURE 0x040800 +#define SS_INVALID_COMMAND 0x052000 +#define SS_INVALID_FIELD_IN_CDB 0x052400 +#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100 +#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500 +#define SS_MEDIUM_NOT_PRESENT 0x023a00 +#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302 +#define SS_NOT_READY_TO_READY_TRANSITION 0x062800 +#define SS_RESET_OCCURRED 0x062900 +#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900 +#define SS_UNRECOVERED_READ_ERROR 0x031100 +#define SS_WRITE_ERROR 0x030c02 +#define SS_WRITE_PROTECTED 0x072700 + +#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */ +#define ASC(x) ((u8) ((x) >> 8)) +#define ASCQ(x) ((u8) (x)) + +/* + * Vendor (8 chars), product (16 chars), release (4 hexadecimal digits) and NUL + * byte + */ +#define INQUIRY_STRING_LEN ((size_t) (8 + 16 + 4 + 1)) + +struct fsg_lun { + struct file *filp; + loff_t file_length; + loff_t num_sectors; + + unsigned int initially_ro:1; + unsigned int ro:1; + unsigned int removable:1; + unsigned int cdrom:1; + unsigned int prevent_medium_removal:1; + unsigned int registered:1; + unsigned int info_valid:1; + unsigned int nofua:1; + + u32 sense_data; + u32 sense_data_info; + u32 unit_attention_data; + + unsigned int blkbits; /* Bits of logical block size + of bound block device */ + unsigned int blksize; /* logical block size of bound block device */ + struct device dev; + const char *name; /* "lun.name" */ + const char **name_pfx; /* "function.name" */ + char inquiry_string[INQUIRY_STRING_LEN]; +}; + +static inline bool fsg_lun_is_open(struct fsg_lun *curlun) +{ + return curlun->filp != NULL; +} + +/* Default size of buffer length. */ +#define FSG_BUFLEN ((u32)16384) + +/* Maximal number of LUNs supported in mass storage function */ +#define FSG_MAX_LUNS 16 + +enum fsg_buffer_state { + BUF_STATE_SENDING = -2, + BUF_STATE_RECEIVING, + BUF_STATE_EMPTY = 0, + BUF_STATE_FULL +}; + +struct fsg_buffhd { + void *buf; + enum fsg_buffer_state state; + struct fsg_buffhd *next; + + /* + * The NetChip 2280 is faster, and handles some protocol faults + * better, if we don't submit any short bulk-out read requests. + * So we will record the intended request length here. + */ + unsigned int bulk_out_intended_length; + + struct usb_request *inreq; + struct usb_request *outreq; +}; + +enum fsg_state { + FSG_STATE_NORMAL, + FSG_STATE_ABORT_BULK_OUT, + FSG_STATE_PROTOCOL_RESET, + FSG_STATE_CONFIG_CHANGE, + FSG_STATE_EXIT, + FSG_STATE_TERMINATED +}; + +enum data_direction { + DATA_DIR_UNKNOWN = 0, + DATA_DIR_FROM_HOST, + DATA_DIR_TO_HOST, + DATA_DIR_NONE +}; + +static inline struct fsg_lun *fsg_lun_from_dev(struct device *dev) +{ + return container_of(dev, struct fsg_lun, dev); +} + +enum { + FSG_STRING_INTERFACE +}; + +extern struct usb_interface_descriptor fsg_intf_desc; + +extern struct usb_endpoint_descriptor fsg_fs_bulk_in_desc; +extern struct usb_endpoint_descriptor fsg_fs_bulk_out_desc; +extern struct usb_descriptor_header *fsg_fs_function[]; + +extern struct usb_endpoint_descriptor fsg_hs_bulk_in_desc; +extern struct usb_endpoint_descriptor fsg_hs_bulk_out_desc; +extern struct usb_descriptor_header *fsg_hs_function[]; + +extern struct usb_endpoint_descriptor fsg_ss_bulk_in_desc; +extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc; +extern struct usb_endpoint_descriptor fsg_ss_bulk_out_desc; +extern struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc; +extern struct usb_descriptor_header *fsg_ss_function[]; + +void fsg_lun_close(struct fsg_lun *curlun); +int fsg_lun_open(struct fsg_lun *curlun, const char *filename); +int fsg_lun_fsync_sub(struct fsg_lun *curlun); +void store_cdrom_address(u8 *dest, int msf, u32 addr); +ssize_t fsg_show_ro(struct fsg_lun *curlun, char *buf); +ssize_t fsg_show_nofua(struct fsg_lun *curlun, char *buf); +ssize_t fsg_show_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, + char *buf); +ssize_t fsg_show_inquiry_string(struct fsg_lun *curlun, char *buf); +ssize_t fsg_show_cdrom(struct fsg_lun *curlun, char *buf); +ssize_t fsg_show_removable(struct fsg_lun *curlun, char *buf); +ssize_t fsg_store_ro(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count); +ssize_t fsg_store_nofua(struct fsg_lun *curlun, const char *buf, size_t count); +ssize_t fsg_store_file(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count); +ssize_t fsg_store_cdrom(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count); +ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf, + size_t count); +ssize_t fsg_store_inquiry_string(struct fsg_lun *curlun, const char *buf, + size_t count); + +#endif /* USB_STORAGE_COMMON_H */ diff --git a/drivers/moto_f_usbnet/Android.mk b/drivers/moto_f_usbnet/Android.mk new file mode 100644 index 000000000000..8055486826cd --- /dev/null +++ b/drivers/moto_f_usbnet/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := moto_f_usbnet.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/moto_f_usbnet/Kbuild b/drivers/moto_f_usbnet/Kbuild new file mode 100644 index 000000000000..331c29b3cfa2 --- /dev/null +++ b/drivers/moto_f_usbnet/Kbuild @@ -0,0 +1,6 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +moto_f_usbnet-objs += f_usbnet.o +obj-m += moto_f_usbnet.o diff --git a/drivers/moto_f_usbnet/Makefile b/drivers/moto_f_usbnet/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/moto_f_usbnet/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/moto_f_usbnet/f_usbnet.c b/drivers/moto_f_usbnet/f_usbnet.c new file mode 100644 index 000000000000..2131da211a85 --- /dev/null +++ b/drivers/moto_f_usbnet/f_usbnet.c @@ -0,0 +1,1248 @@ +/* + * Gadget Driver for Motorola USBNet + * + * Copyright (C) 2009 Motorola, 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 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Macro Defines + */ + +#define EP0_BUFSIZE 256 + + +/* Vendor Request to config IP */ +#define USBNET_SET_IP_ADDRESS 0x05 +#define USBNET_SET_SUBNET_MASK 0x06 +#define USBNET_SET_HOST_IP 0x07 + +/* Linux Network Interface */ +#define USB_MTU 15360 +#define CURRENT_MTU 15000 +#define MAX_BULK_TX_REQ_NUM 80 +#define MAX_BULK_RX_REQ_NUM 80 +#define MAX_INTR_RX_REQ_NUM 80 +#define INTERFACE_STRING_INDEX 0 + +#define WORK_RX_MEMORY 0 + +struct usbnet_context { + spinlock_t lock; /* For RX/TX list */ + struct net_device *dev; + + struct usb_gadget *gadget; + + struct usb_ep *bulk_in; + struct usb_ep *bulk_out; + struct usb_ep *intr_out; + u16 config; /* current USB config w_value */ + + struct list_head rx_reqs; + struct list_head tx_reqs; + + struct net_device_stats stats; + struct work_struct usbnet_config_wq; + u32 ip_addr; + u32 subnet_mask; + u32 router_ip; + u32 iff_flag; + struct socket *socket; + struct work_struct work; + unsigned long todo; +}; + + +struct usbnet_device { + struct usb_function function; + struct usb_composite_dev *cdev; + struct usbnet_context *net_ctxt; +}; + + +/* for configfs support */ +struct usbnet_opts { + struct usb_function_instance func_inst; + char *name; + struct usbnet_device *dev; +}; + +static inline struct usbnet_opts *to_usbnet_opts(struct config_item *item) +{ + return container_of(to_config_group(item), struct usbnet_opts, + func_inst.group); +} +/* static strings, in UTF-8 */ +static struct usb_string usbnet_string_defs[] = { + [INTERFACE_STRING_INDEX].s = "Usbnet", + { /* ZEROES END LIST */ }, +}; + +static struct usb_gadget_strings usbnet_string_table = { + .language = 0x0409, /* en-us */ + .strings = usbnet_string_defs, +}; + +static struct usb_gadget_strings *usbnet_strings[] = { + &usbnet_string_table, + NULL, +}; + +#define MAX_INST_NAME_LEN 40 + +/* There is only one interface. */ + +static struct usb_interface_descriptor usbnet_intf_desc = { + .bLength = sizeof(usbnet_intf_desc), + .bDescriptorType = USB_DT_INTERFACE, + .bNumEndpoints = 3, + .bInterfaceClass = 0x02, + .bInterfaceSubClass = 0x0a, + .bInterfaceProtocol = 0x01, +}; + + +static struct usb_endpoint_descriptor usbnet_fs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor usbnet_fs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor intr_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = __constant_cpu_to_le16(64), + .bInterval = 1, +}; + +static struct usb_descriptor_header *fs_function[] = { + (struct usb_descriptor_header *) &usbnet_intf_desc, + (struct usb_descriptor_header *) &usbnet_fs_bulk_in_desc, + (struct usb_descriptor_header *) &usbnet_fs_bulk_out_desc, + (struct usb_descriptor_header *) &intr_out_desc, + NULL, +}; + +static struct usb_endpoint_descriptor usbnet_hs_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), + .bInterval = 0, +}; + +static struct usb_endpoint_descriptor usbnet_hs_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(512), + .bInterval = 0, +}; + +static struct usb_descriptor_header *hs_function[] = { + (struct usb_descriptor_header *) &usbnet_intf_desc, + (struct usb_descriptor_header *) &usbnet_hs_bulk_in_desc, + (struct usb_descriptor_header *) &usbnet_hs_bulk_out_desc, + (struct usb_descriptor_header *) &intr_out_desc, + NULL, +}; + +static struct usb_endpoint_descriptor usbnet_ss_bulk_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(1024), + .bInterval = 0, +}; + +static struct usb_ss_ep_comp_descriptor usbnet_ss_bulk_in_comp_desc = { + .bLength = sizeof usbnet_ss_bulk_in_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + +static struct usb_endpoint_descriptor usbnet_ss_bulk_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = __constant_cpu_to_le16(1024), + .bInterval = 0, +}; + +static struct usb_ss_ep_comp_descriptor usbnet_ss_bulk_out_comp_desc = { + .bLength = sizeof usbnet_ss_bulk_out_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ +}; + +static struct usb_ss_ep_comp_descriptor usbnet_ss_intr_comp_desc = { + .bLength = sizeof usbnet_ss_intr_comp_desc, + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, + + /* the following 2 values can be tweaked if necessary */ + /* .bMaxBurst = 0, */ + /* .bmAttributes = 0, */ + .wBytesPerInterval = cpu_to_le16(64), +}; + +static struct usb_descriptor_header *ss_function[] = { + (struct usb_descriptor_header *) &usbnet_intf_desc, + (struct usb_descriptor_header *) &usbnet_ss_bulk_in_desc, + (struct usb_descriptor_header *) &usbnet_ss_bulk_in_comp_desc, + (struct usb_descriptor_header *) &usbnet_ss_bulk_out_desc, + (struct usb_descriptor_header *) &usbnet_ss_bulk_out_comp_desc, + (struct usb_descriptor_header *) &intr_out_desc, + (struct usb_descriptor_header *) &usbnet_ss_intr_comp_desc, + NULL, +}; + + +#define DO_NOT_STOP_QUEUE 0 +#define STOP_QUEUE 1 + +#define USBNETDBG(context, fmt, args...) {\ + if (context && (context->gadget)) \ + dev_info(&(context->gadget->dev) , fmt , ## args); } + +static const char *usb_description = "Motorola BLAN Interface"; + +static int ether_queue_out(struct usb_request *req , + struct usbnet_context *context); +struct usb_request *usb_get_recv_request(struct usbnet_context *context); +static void defer_kevent(struct usbnet_context *context, int flag); +static void eth_work(struct work_struct *work); +static void rx_fill(struct usbnet_context *context) +{ + struct usb_request *req; + + /* fill unused rxq slots with some skb */ + while ((req = usb_get_recv_request(context))) { + if (ether_queue_out(req,context) < 0) { + defer_kevent(context, WORK_RX_MEMORY); + USBNETDBG(context,"rx_fill failed, defer_kevent\n"); + break; + } + } +} + +static void eth_work(struct work_struct *work) +{ + struct usbnet_context *context; + + context= container_of(work, struct usbnet_context, work); + if (test_and_clear_bit(WORK_RX_MEMORY, &context->todo)) { + if (netif_running(context->dev)) + rx_fill(context); + } + if (context->todo) + USBNETDBG(context,"work done, flags = 0x%lx\n", context->todo); +} + +static void defer_kevent(struct usbnet_context *context, int flag) +{ + if (test_and_set_bit(flag, &context->todo)) + return; + if (!schedule_work(&context->work)) + USBNETDBG(context, "kevent %d may have been dropped\n", flag); + USBNETDBG(context, "kevent %d schedule done\n", flag); +} +static ssize_t usbnet_desc_show(struct device *dev, + struct device_attribute *attr, char *buff) +{ + ssize_t status = 0; + status = snprintf(buff, 30, "%s\n", usb_description); + return status; +} + +static DEVICE_ATTR(description, S_IRUGO, usbnet_desc_show, NULL); + +static ssize_t usbnet_ip_addr_show(struct device *dev, + struct device_attribute *attr, char *buff) +{ + ssize_t status = 0; + struct net_device *ndev = to_net_dev(dev); + struct usbnet_context *context = netdev_priv(ndev); + + status = snprintf(buff, 30, "0x%08x\n", context->ip_addr); + return status; +}; +static DEVICE_ATTR(ip_addr, S_IRUGO, usbnet_ip_addr_show, NULL); + +static ssize_t usbnet_subnet_mask_show(struct device *dev, + struct device_attribute *attr, char *buff) +{ + ssize_t status = 0; + struct net_device *ndev = to_net_dev(dev); + struct usbnet_context *context = netdev_priv(ndev); + + status = snprintf(buff, 30, "0x%08x\n", context->subnet_mask); + return status; +}; +static DEVICE_ATTR(subnet_mask, S_IRUGO, usbnet_subnet_mask_show, NULL); + +static ssize_t usbnet_iff_flag_show(struct device *dev, + struct device_attribute *attr, char *buff) +{ + ssize_t status = 0; + struct net_device *ndev = to_net_dev(dev); + struct usbnet_context *context = netdev_priv(ndev); + + status = snprintf(buff, 30, "0x%08x\n", context->iff_flag); + return status; +}; +static DEVICE_ATTR(iff_flag, S_IRUGO, usbnet_iff_flag_show, NULL); + +static void usbnet_if_config_notify(struct usbnet_context *context) +{ + struct kobj_uevent_env *env; + struct net_device *net_dev = context->dev; + + env = kzalloc(sizeof(*env), GFP_KERNEL); + if (!env) { + USBNETDBG(context, "%s: alloc uevent error\n", __func__); + return; + } + + add_uevent_var(env, "USBNET_IF_CFG_IP_ADDR=0x%08x", + context->ip_addr); + add_uevent_var(env, "USBNET_IP_CFG_ROUTER_ADDR=0x%08x", + context->router_ip); + add_uevent_var(env, "USBNET_IP_CFG_SUBNET_MASK=0x%08x", + context->subnet_mask); + kobject_uevent_env(&net_dev->dev.kobj, KOBJ_CHANGE, env->envp); + kfree(env); +} + +static inline struct usbnet_device *usbnet_func_to_dev(struct usb_function *f) +{ + return container_of(f, struct usbnet_device, function); +} + +static int ether_queue_out(struct usb_request *req , + struct usbnet_context *context) +{ + unsigned long flags; + struct sk_buff *skb; + int ret; + unsigned int size; + + size = ALIGN(USB_MTU + NET_IP_ALIGN, context->bulk_out->maxpacket); + skb = alloc_skb(size, GFP_ATOMIC); + if (!skb) { + USBNETDBG(context, "%s: failed to alloc skb\n", __func__); + ret = -ENOMEM; + goto fail; + } + + skb_reserve(skb, NET_IP_ALIGN); + + req->buf = skb->data; + req->length = size; + req->context = skb; + + ret = usb_ep_queue(context->bulk_out, req, GFP_KERNEL); + if (ret == 0) + return 0; + if (ret == -ENOMEM){ + USBNETDBG(context, "%s: failed to enqueue nomem\n", __func__); +fail: + defer_kevent(context, WORK_RX_MEMORY); + } + if (ret) { + USBNETDBG(context, "ether_queue_out --> %d\n", ret); + if (skb) + dev_kfree_skb_any(skb); + } + spin_lock_irqsave(&context->lock, flags); + list_add_tail(&req->list, &context->rx_reqs); + spin_unlock_irqrestore(&context->lock, flags); + + return ret; +} + +struct usb_request *usb_get_recv_request(struct usbnet_context *context) +{ + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&context->lock, flags); + if (list_empty(&context->rx_reqs)) { + req = NULL; + } else { + req = list_first_entry(&context->rx_reqs, + struct usb_request, list); + list_del(&req->list); + } + spin_unlock_irqrestore(&context->lock, flags); + + return req; +} + +struct usb_request *usb_get_xmit_request(int stop_flag, struct net_device *dev) +{ + struct usbnet_context *context = netdev_priv(dev); + unsigned long flags; + struct usb_request *req; + + spin_lock_irqsave(&context->lock, flags); + if (list_empty(&context->tx_reqs)) { + req = NULL; + } else { + req = list_first_entry(&context->tx_reqs, + struct usb_request, list); + list_del(&req->list); + if (stop_flag == STOP_QUEUE && + list_empty(&context->tx_reqs)) + netif_stop_queue(dev); + } + spin_unlock_irqrestore(&context->lock, flags); + return req; +} + +static netdev_tx_t usb_ether_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct usbnet_context *context = netdev_priv(dev); + struct usb_request *req; + unsigned long flags; + unsigned len; + int rc; + + /*usbnet might be disabled before ip link down + * free the skb in this case + */ + if (NULL == context->bulk_in) { + dev_kfree_skb_any(skb); + USBNETDBG(context, + "%s:bulk_in end point is disabled\n", __func__); + return NETDEV_TX_OK; + } + + if (!context->bulk_in->enabled) { + dev_kfree_skb_any(skb); + USBNETDBG(context, + "%s: bulk_in didn't enabled\n", __func__); + return NETDEV_TX_OK; + } + + req = usb_get_xmit_request(STOP_QUEUE, dev); + + if (!req) { + USBNETDBG(context, "%s: could not obtain tx request\n", + __func__); + return NETDEV_TX_BUSY; + } + + /* Add 4 bytes CRC */ + skb->len += 4; + + /* ensure that we end with a short packet */ + len = skb->len; + if (!(len & 63) || !(len & 511)) + len++; + + req->context = skb; + req->buf = skb->data; + req->length = len; + + rc = usb_ep_queue(context->bulk_in, req, GFP_KERNEL); + if (rc != 0) { + spin_lock_irqsave(&context->lock, flags); + list_add_tail(&req->list, &context->tx_reqs); + spin_unlock_irqrestore(&context->lock, flags); + + dev_kfree_skb_any(skb); + context->stats.tx_dropped++; + + USBNETDBG(context, + "%s: could not queue tx request\n", __func__); + } + + return NETDEV_TX_OK; +} + +static int usb_ether_open(struct net_device *dev) +{ + struct usbnet_context *context = netdev_priv(dev); + USBNETDBG(context, "%s\n", __func__); + defer_kevent(context, WORK_RX_MEMORY); + USBNETDBG(context, "restart rx work\n"); + return 0; +} + +static int usb_ether_stop(struct net_device *dev) +{ + struct usbnet_context *context = netdev_priv(dev); + USBNETDBG(context, "%s\n", __func__); + return 0; +} + +static struct net_device_stats *usb_ether_get_stats(struct net_device *dev) +{ + struct usbnet_context *context = netdev_priv(dev); + return &context->stats; +} + +static int usbnet_add_files(struct usbnet_context *context); +static void usbnet_remove_files(struct usbnet_context *context); +static void usbnet_if_config(struct work_struct *work) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) + struct ifreq ifr; + mm_segment_t saved_fs; + struct sockaddr_in *sin; + unsigned err; +#endif + struct usbnet_context *context = container_of(work, + struct usbnet_context, usbnet_config_wq); + struct socket *sock = context->socket; + + if (!sock) { + USBNETDBG(context, "%s: Error, socket is empty\n", __func__); + return; + } + sock_net_set(sock->sk, dev_net(context->dev)); + + pr_info("%s : Configuring with config = %d, ip_addr = 0x%08x", + __func__, context->config, context->ip_addr); + pr_info("subnet = 0x%08x, router_ip = 0x%08x, flags = 0x%08x\n", + context->subnet_mask, context->router_ip, context->iff_flag); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) + memset(&ifr, 0, sizeof(ifr)); + sin = (void *) &(ifr.ifr_ifru.ifru_addr); + strlcpy(ifr.ifr_ifrn.ifrn_name, context->dev->name, + sizeof(ifr.ifr_ifrn.ifrn_name)); + sin->sin_family = AF_INET; + + sin->sin_addr.s_addr = context->ip_addr; + saved_fs = get_fs(); + set_fs(KERNEL_DS); + + err = sock->ops->ioctl(sock, SIOCSIFADDR, (unsigned long)&ifr); + if (err) + USBNETDBG(context, "%s: Error in SIOCSIFADDR\n", __func__); + + sin->sin_addr.s_addr = context->subnet_mask; + err = sock->ops->ioctl(sock, SIOCSIFNETMASK, (unsigned long)&ifr); + if (err) + USBNETDBG(context, "%s: Error in SIOCSIFNETMASK\n", __func__); + + sin->sin_addr.s_addr = context->ip_addr | ~(context->subnet_mask); + err = sock->ops->ioctl(sock, SIOCSIFBRDADDR, (unsigned long)&ifr); + if (err) + USBNETDBG(context, "%s: Error in SIOCSIFBRDADDR\n", __func__); + + memset(&ifr, 0, sizeof(ifr)); + strlcpy(ifr.ifr_ifrn.ifrn_name, context->dev->name, + sizeof(ifr.ifr_ifrn.ifrn_name)); + ifr.ifr_flags = context->dev->flags & ~IFF_UP; + ifr.ifr_flags |= context->iff_flag; + err = sock->ops->ioctl(sock, SIOCSIFFLAGS, (unsigned long)&ifr); + if (err) + USBNETDBG(context, "%s: Error in SIOCSIFFLAGS\n", __func__); + set_fs(saved_fs); +#endif + if (context->iff_flag) + usbnet_add_files(context); + else + usbnet_remove_files(context); + usbnet_if_config_notify(context); +} + +static const struct net_device_ops usbnet_eth_netdev_ops = { + .ndo_open = usb_ether_open, + .ndo_stop = usb_ether_stop, + .ndo_start_xmit = usb_ether_xmit, + .ndo_get_stats = usb_ether_get_stats, +}; + +static void usb_ether_setup(struct net_device *dev) +{ + struct usbnet_context *context = netdev_priv(dev); + INIT_LIST_HEAD(&context->rx_reqs); + INIT_LIST_HEAD(&context->tx_reqs); + + spin_lock_init(&context->lock); + context->dev = dev; + + dev->netdev_ops = &usbnet_eth_netdev_ops; + dev->watchdog_timeo = 20; + + ether_setup(dev); + + eth_random_addr((u8 *)dev->dev_addr); +} + +/*-------------------------------------------------------------------------*/ +static void usbnet_cleanup(struct usbnet_device *dev) +{ + struct usbnet_context *context = dev->net_ctxt; + if (context) { + device_remove_file(&(context->dev->dev), &dev_attr_description); + unregister_netdev(context->dev); + flush_work(&context->work); + free_netdev(context->dev); + dev->net_ctxt = NULL; + } +} + +static void usbnet_unbind(struct usb_configuration *c, struct usb_function *f) +{ + struct usbnet_device *dev = usbnet_func_to_dev(f); + struct usb_composite_dev *cdev = c->cdev; + struct usbnet_context *context = dev->net_ctxt; + struct usb_request *req; + + dev->cdev = cdev; + + if (context->bulk_in) + usb_ep_disable(context->bulk_in); + + if (context->bulk_out) { + usb_ep_disable(context->bulk_out); + + /* Free BULK OUT Requests */ + while ((req = usb_get_recv_request(context))) + usb_ep_free_request(context->bulk_out, req); + } + + if (context->bulk_in) { + /* Free BULK IN Requests */ + while ((req = usb_get_xmit_request(DO_NOT_STOP_QUEUE, + context->dev))) { + usb_ep_free_request(context->bulk_in, req); + } + } + + context->gadget = NULL; + context->bulk_in = NULL; + context->bulk_out = NULL; + context->config = 0; +} + +static void ether_out_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct sk_buff *skb = req->context; + struct usbnet_context *context = ep->driver_data; + int status; + + if (req->status == 0) { + skb_put(skb, req->actual); + skb->protocol = eth_type_trans(skb, context->dev); + context->stats.rx_packets++; + context->stats.rx_bytes += req->actual; + if( ETH_HLEN > skb->len + || skb->len > CURRENT_MTU) { + USBNETDBG(context, "rx length not right %d\n", skb->len); + context->stats.rx_errors++; + dev_kfree_skb_any(skb); + } else { + status = netif_rx(skb); + if (status < 0) { + USBNETDBG(context, "netif_rx failed %d\n", status); + context->stats.rx_errors++; + dev_kfree_skb_any(skb); + } + } + + } else { + dev_kfree_skb_any(skb); + context->stats.rx_errors++; + } + + /* don't bother requeuing if we just went offline */ + if ((req->status == -ENODEV) || (req->status == -ESHUTDOWN)) { + unsigned long flags; + spin_lock_irqsave(&context->lock, flags); + list_add_tail(&req->list, &context->rx_reqs); + spin_unlock_irqrestore(&context->lock, flags); + }else { + if (ether_queue_out(req, context)) + USBNETDBG(context, "ether_out: cannot requeue\n"); + } +} + +static void ether_in_complete(struct usb_ep *ep, struct usb_request *req) +{ + unsigned long flags; + struct sk_buff *skb = req->context; + struct usbnet_context *context = ep->driver_data; + + if (req->status == 0) { + context->stats.tx_packets++; + context->stats.tx_bytes += req->actual; + } else { + context->stats.tx_errors++; + } + + dev_kfree_skb_any(skb); + + spin_lock_irqsave(&context->lock, flags); + if (list_empty(&context->tx_reqs)) + netif_start_queue(context->dev); + + list_add_tail(&req->list, &context->tx_reqs); + spin_unlock_irqrestore(&context->lock, flags); +} + +static int usbnet_bind(struct usb_configuration *c, + struct usb_function *f) +{ + struct usb_composite_dev *cdev = c->cdev; + struct usbnet_device *dev = usbnet_func_to_dev(f); + struct usbnet_context *context = dev->net_ctxt; + int n, rc, id; + struct usb_ep *ep; + struct usb_request *req; + unsigned long flags; + dev->cdev = cdev; + id = usb_interface_id(c, f); + if (id < 0) + return id; + + usbnet_intf_desc.bInterfaceNumber = id; + + if (usbnet_string_defs[INTERFACE_STRING_INDEX].id == 0) { + rc = usb_string_id(c->cdev); + if (rc < 0) { + USBNETDBG(context, "string id returned %d\n", rc); + return rc; + } + usbnet_string_defs[INTERFACE_STRING_INDEX].id = rc; + usbnet_intf_desc.iInterface = rc; + } + + context->gadget = cdev->gadget; + + /* Find all the endpoints we will use */ + ep = usb_ep_autoconfig(cdev->gadget, &usbnet_fs_bulk_in_desc); + if (!ep) { + USBNETDBG(context, "%s usbnet_hs_bulk_in_desc error\n", + __func__); + goto autoconf_fail; + } + ep->driver_data = context; + context->bulk_in = ep; + + ep = usb_ep_autoconfig(cdev->gadget, &usbnet_fs_bulk_out_desc); + if (!ep) { + USBNETDBG(context, "%s usbnet_hs_bulk_out_desc error\n", + __func__); + goto autoconf_fail; + } + ep->driver_data = context; + context->bulk_out = ep; + + + ep = usb_ep_autoconfig(cdev->gadget, &intr_out_desc); + if (!ep) { + USBNETDBG(context, "%s intr_out_desc error\n", + __func__); + goto autoconf_fail; + } + ep->driver_data = context; + context->intr_out = ep; + + if (gadget_is_dualspeed(cdev->gadget)) { + + /* Assume endpoint addresses are the same for both speeds */ + usbnet_hs_bulk_in_desc.bEndpointAddress = + usbnet_fs_bulk_in_desc.bEndpointAddress; + usbnet_hs_bulk_out_desc.bEndpointAddress = + usbnet_fs_bulk_out_desc.bEndpointAddress; + } + + /* support super speed hardware */ + if (gadget_is_superspeed(c->cdev->gadget)) { + usbnet_ss_bulk_in_desc.bEndpointAddress = + usbnet_fs_bulk_in_desc.bEndpointAddress; + usbnet_ss_bulk_out_desc.bEndpointAddress = + usbnet_fs_bulk_out_desc.bEndpointAddress; + } + + + rc = -ENOMEM; + + for (n = 0; n < MAX_BULK_RX_REQ_NUM; n++) { + req = usb_ep_alloc_request(context->bulk_out, + GFP_KERNEL); + if (!req) { + USBNETDBG(context, "%s: alloc request bulk_out fail\n", + __func__); + break; + } + req->complete = ether_out_complete; + spin_lock_irqsave(&context->lock, flags); + list_add_tail(&req->list, &context->rx_reqs); + spin_unlock_irqrestore(&context->lock, flags); + } + for (n = 0; n < MAX_BULK_TX_REQ_NUM; n++) { + req = usb_ep_alloc_request(context->bulk_in, + GFP_KERNEL); + if (!req) { + USBNETDBG(context, "%s: alloc request bulk_in fail\n", + __func__); + break; + } + req->complete = ether_in_complete; + spin_lock_irqsave(&context->lock, flags); + list_add_tail(&req->list, &context->tx_reqs); + spin_unlock_irqrestore(&context->lock, flags); + } + + return 0; + +autoconf_fail: + rc = -ENOTSUPP; + usbnet_unbind(c, f); + return rc; +} + +static void do_set_config(struct usb_function *f, u16 new_config) +{ + struct usbnet_device *dev = usbnet_func_to_dev(f); + struct usbnet_context *context = dev->net_ctxt; + struct usb_composite_dev *cdev = f->config->cdev; + int result = 0; + + USBNETDBG(context, "do_set_config ep %s\n", + context->bulk_in->name); + + if (context->config == new_config) /* Config did not change */ + return; + + context->config = new_config; + + if (new_config == 1) { /* Enable End points */ + USBNETDBG(context, + "config_ep_by_speed ep %s\n", + context->bulk_in->name); + + result = config_ep_by_speed(cdev->gadget, f, context->bulk_in); + if (result) { + context->bulk_in->desc = NULL; + USBNETDBG(context, + "config_ep_by_speed failed: ep %s, result %d\n", + context->bulk_in->name, result); + return; + } + + result = usb_ep_enable(context->bulk_in); + + if (result != 0) { + USBNETDBG(context, + "%s: failed to enable BULK_IN EP ret=%d\n", + __func__, result); + } + + context->bulk_in->driver_data = context; + + result = config_ep_by_speed(cdev->gadget, f, context->bulk_out); + if (result) { + context->bulk_out->desc = NULL; + USBNETDBG(context, + "config_ep_by_speed failed: ep %s, result %d\n", + context->bulk_out->name, result); + usb_ep_disable(context->bulk_in); + return; + } + + result = usb_ep_enable(context->bulk_out); + + if (result != 0) { + USBNETDBG(context, + "%s: failed to enable BULK_OUT EP ret = %d\n", + __func__, result); + } + + context->bulk_out->driver_data = context; + + context->intr_out->desc = &intr_out_desc; + result = usb_ep_enable(context->intr_out); + + if (result != 0) { + USBNETDBG(context, + "%s: failed to enable INTR_OUT EP ret = %d\n", + __func__, result); + } + + context->intr_out->driver_data = context; + rx_fill(context); + } else {/* Disable Endpoints */ + if (context->bulk_in) + usb_ep_disable(context->bulk_in); + if (context->bulk_out) + usb_ep_disable(context->bulk_out); + if (context->intr_out) + usb_ep_disable(context->intr_out); + context->ip_addr = 0; + context->subnet_mask = 0; + context->router_ip = 0; + context->iff_flag = 0; + schedule_work(&context->usbnet_config_wq); + } +} + + +static int usbnet_set_alt(struct usb_function *f, + unsigned intf, unsigned alt) +{ + struct usbnet_device *dev = usbnet_func_to_dev(f); + struct usbnet_context *context = dev->net_ctxt; + USBNETDBG(context, "usbnet_set_alt intf: %d alt: %d\n", intf, alt); + do_set_config(f, 1); + return 0; +} + +static int usbnet_ctrlrequest( + /*struct usb_composite_dev *cdev,*/struct usb_function *f, + const struct usb_ctrlrequest *ctrl) +{ + struct usbnet_device *dev = usbnet_func_to_dev(f); + struct usbnet_context *context = dev->net_ctxt; + int rc = -EOPNOTSUPP; + int wIndex = le16_to_cpu(ctrl->wIndex); + int wValue = le16_to_cpu(ctrl->wValue); + int wLength = le16_to_cpu(ctrl->wLength); + struct usb_request *req = dev->cdev->req; + + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) { + switch (ctrl->bRequest) { + case USBNET_SET_IP_ADDRESS: + context->ip_addr = (wValue << 16) | wIndex; + rc = 0; + break; + case USBNET_SET_SUBNET_MASK: + context->subnet_mask = (wValue << 16) | wIndex; + rc = 0; + break; + case USBNET_SET_HOST_IP: + context->router_ip = (wValue << 16) | wIndex; + rc = 0; + break; + default: + break; + } + + if (context->ip_addr && context->subnet_mask + && context->router_ip) { + context->iff_flag = IFF_UP; + /* schedule a work queue to do this because we + need to be able to sleep */ + schedule_work(&context->usbnet_config_wq); + } + } + + /* respond with data transfer or status phase? */ + if (rc >= 0) { + req->zero = rc < wLength; + req->length = rc; + rc = usb_ep_queue(dev->cdev->gadget->ep0, req, GFP_ATOMIC); + if (rc < 0) + USBNETDBG(context, "usbnet setup response error\n"); + } + + return rc; +} + +static void usbnet_disable(struct usb_function *f) +{ + struct usbnet_device *dev = usbnet_func_to_dev(f); + struct usbnet_context *context = dev->net_ctxt; + USBNETDBG(context, "%s\n", __func__); +#ifdef CONFIG_MOT_USB_DEBUG_LOG + dump_stack(); +#endif + do_set_config(f, 0); +} + +static void usbnet_suspend(struct usb_function *f) +{ + struct usbnet_device *dev = usbnet_func_to_dev(f); + struct usbnet_context *context = dev->net_ctxt; + USBNETDBG(context, "%s\n", __func__); +} + +static void usbnet_resume(struct usb_function *f) +{ + struct usbnet_device *dev = usbnet_func_to_dev(f); + struct usbnet_context *context = dev->net_ctxt; + USBNETDBG(context, "%s\n", __func__); +} + +static void usbnet_free_func(struct usb_function *f) +{ +} +static void usbnet_opts_release(struct config_item *item) +{ + struct usbnet_opts *opts = to_usbnet_opts(item); + + usb_put_function_instance(&opts->func_inst); +} + +static struct configfs_item_operations usbnet_item_ops = { + .release = usbnet_opts_release, +}; + +static struct config_item_type usbnet_func_type = { + .ct_item_ops = &usbnet_item_ops, + .ct_owner = THIS_MODULE, +}; + +static int usbnet_set_inst_name(struct usb_function_instance *fi, + const char *name) +{ + struct usbnet_opts *opts = container_of(fi, struct usbnet_opts, func_inst); + char *ptr; + int name_len; + + name_len = strlen(name) + 1; + if (name_len > MAX_INST_NAME_LEN) + return -ENAMETOOLONG; + + ptr = kstrndup(name, name_len, GFP_KERNEL); + if (!ptr) + return -ENOMEM; + + opts->name = ptr; + + return 0; +} + +static int usbnet_add_files(struct usbnet_context *context) +{ + int ret; + struct net_device *net_dev = context->dev; + + ret = device_create_file(&net_dev->dev, &dev_attr_ip_addr); + if (ret < 0) { + pr_err("%s: ip_addr creation error=%d\n", __func__, ret); + return ret; + } + ret = device_create_file(&net_dev->dev, &dev_attr_subnet_mask); + if (ret < 0) { + pr_err("%s: subnet_mask creation error=%d\n", __func__, ret); + device_remove_file(&net_dev->dev, &dev_attr_ip_addr); + return ret; + } + ret = device_create_file(&net_dev->dev, &dev_attr_iff_flag); + if (ret < 0) { + pr_err("%s: iff_flag creation error=%d\n", __func__, ret); + device_remove_file(&net_dev->dev, &dev_attr_ip_addr); + device_remove_file(&net_dev->dev, &dev_attr_subnet_mask); + return ret; + } + + return 0; +} + +static void usbnet_remove_files(struct usbnet_context *context) +{ + struct net_device *net_dev = context->dev; + + device_remove_file(&net_dev->dev, &dev_attr_ip_addr); + device_remove_file(&net_dev->dev, &dev_attr_subnet_mask); + device_remove_file(&net_dev->dev, &dev_attr_iff_flag); +} + +static void usbnet_free_inst(struct usb_function_instance *f) +{ + struct usbnet_opts *opts; + struct usbnet_device *dev; + struct usbnet_context *context; + struct socket *sk; + + opts = container_of(f, struct usbnet_opts, func_inst); + dev = opts->dev; + if (!IS_ERR(dev)) { + context = (struct usbnet_context *)dev->net_ctxt; + if (context) { + sk = context->socket; + if (sk) { + sk->ops->shutdown(sk, SHUT_RDWR); + sk->sk->sk_user_data = NULL; + sock_release(sk); + context->socket = NULL; + } + } + usbnet_cleanup(dev); + } + kfree(opts->name); + kfree(opts); +} + +static struct usb_function_instance *usbnet_alloc_inst(void) +{ + int ret; + struct usbnet_opts *opts; + struct usbnet_context *context; + struct usbnet_device *dev; + struct net_device *net_dev; + + opts = kzalloc(sizeof(*opts), GFP_KERNEL); + if (!opts) + return ERR_PTR(-ENOMEM); + + opts->func_inst.set_inst_name = usbnet_set_inst_name; + opts->func_inst.free_func_inst = usbnet_free_inst; + config_group_init_type_name(&opts->func_inst.group, "", + &usbnet_func_type); + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if(!dev) { + pr_err("%s: alloc_usbnetdev error\n", __func__); + kfree(opts); + return ERR_PTR(-ENOMEM); + } + opts->dev = dev; + net_dev = alloc_netdev(sizeof(struct usbnet_context), + "usb%d", NET_NAME_UNKNOWN, usb_ether_setup); + if (!net_dev) { + pr_err("%s: alloc_netdev error\n", __func__); + kfree(opts); + kfree(dev); + return ERR_PTR(-ENOMEM); + } + + net_dev->mtu = CURRENT_MTU; + ret = register_netdev(net_dev); + if (ret) { + pr_err("%s: register_netdev error\n", __func__); + free_netdev(net_dev); + kfree(opts); + kfree(dev); + return ERR_PTR(-EINVAL); + } + + ret = device_create_file(&net_dev->dev, &dev_attr_description); + if (ret < 0) { + pr_err("%s: sys file creation error\n", __func__); + unregister_netdev(net_dev); + free_netdev(net_dev); + kfree(opts); + kfree(dev); + return ERR_PTR(-ENOMEM); + } + + context = netdev_priv(net_dev); + ret = sock_create_kern(&init_net, AF_INET, SOCK_DGRAM, + 0, &context->socket); + if (ret < 0) { + pr_err("%s: socket creation error, ret=%d\n", __func__, ret); + device_remove_file(&net_dev->dev, &dev_attr_description); + unregister_netdev(net_dev); + free_netdev(net_dev); + kfree(opts); + kfree(dev); + return ERR_PTR(ret); + } + context->socket->sk->sk_user_data = context; + + INIT_WORK(&context->usbnet_config_wq, usbnet_if_config); + INIT_WORK(&context->work, eth_work); + context->config = 0; + dev->net_ctxt = context; + spin_lock_init(&context->lock); + return &opts->func_inst; +} + +static struct usb_function *usbnet_alloc(struct usb_function_instance *fi) +{ + struct usbnet_opts *opts; + struct usbnet_device *dev; + + opts = container_of(fi, struct usbnet_opts, func_inst); + dev = opts->dev; + if (IS_ERR(dev)) + return NULL; + dev->function.name = opts->name; + dev->function.fs_descriptors = fs_function; + dev->function.hs_descriptors = hs_function; + dev->function.ss_descriptors = ss_function; + dev->function.ssp_descriptors = ss_function; + + dev->function.bind = usbnet_bind; + dev->function.unbind = usbnet_unbind; + dev->function.set_alt = usbnet_set_alt; + dev->function.disable = usbnet_disable; + dev->function.suspend = usbnet_suspend; + dev->function.resume = usbnet_resume; + dev->function.strings = usbnet_strings; + dev->function.setup = usbnet_ctrlrequest; + dev->function.free_func = usbnet_free_func; + fi->f = &dev->function; + return &dev->function; +} + +DECLARE_USB_FUNCTION(usbnet, usbnet_alloc_inst, usbnet_alloc); +static int __init usbnet_init(void) +{ + int ret; + ret = usb_function_register(&usbnetusb_func); + if (ret) { + pr_err("%s: failed to register usbnet %d\n", __func__, ret); + return ret; + } + return ret; +} + +static void __exit usbnet_exit(void) +{ + usb_function_unregister(&usbnetusb_func); +} + +module_init(usbnet_init); +module_exit(usbnet_exit); + +MODULE_DESCRIPTION("Motorola USB LAN driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/moto_mm/Android.mk b/drivers/moto_mm/Android.mk new file mode 100644 index 000000000000..d142c23bd9c9 --- /dev/null +++ b/drivers/moto_mm/Android.mk @@ -0,0 +1,12 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + + +include $(CLEAR_VARS) +LOCAL_MODULE := moto_mm.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki + +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/moto_mm/Kbuild b/drivers/moto_mm/Kbuild new file mode 100644 index 000000000000..7aeed6cf3254 --- /dev/null +++ b/drivers/moto_mm/Kbuild @@ -0,0 +1,6 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += moto_mm.o + diff --git a/drivers/moto_mm/Kconfig b/drivers/moto_mm/Kconfig new file mode 100644 index 000000000000..f66554cd5c45 --- /dev/null +++ b/drivers/moto_mm/Kconfig @@ -0,0 +1 @@ +# SPDX-License-Identifier: GPL-2.0 diff --git a/drivers/moto_mm/Makefile b/drivers/moto_mm/Makefile new file mode 100755 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/moto_mm/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/moto_mm/moto_mm.c b/drivers/moto_mm/moto_mm.c new file mode 100755 index 000000000000..7a3b4065f28c --- /dev/null +++ b/drivers/moto_mm/moto_mm.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2023 Motorola Mobility LLC + * + * 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. + */ + +#define pr_fmt(fmt) "moto_mm: " fmt + +#include +#include +#include +#include + + +static void tune_inactive_ratio_hook(void *data, unsigned long *inactive_ratio, int file) +{ + if (file) + *inactive_ratio = min(2UL, *inactive_ratio); + else + *inactive_ratio = 1; + + return; +} + + +#define REGISTER_HOOK(name) do {\ + rc = register_trace_android_vh_##name(name##_hook, NULL);\ + if (rc) {\ + pr_err("register hook %s failed", #name);\ + goto err_out_##name;\ + }\ +} while (0) + +#define UNREGISTER_HOOK(name) do {\ + unregister_trace_android_vh_##name(name##_hook, NULL);\ +} while (0) + +#define ERROR_OUT(name) err_out_##name + +static int register_all_hooks(void) +{ + int rc; + + /* tune_inactive_ratio_hook */ + REGISTER_HOOK(tune_inactive_ratio); + return 0; + + UNREGISTER_HOOK(tune_inactive_ratio); +ERROR_OUT(tune_inactive_ratio): + return rc; +} + +static void unregister_all_hook(void) +{ + UNREGISTER_HOOK(tune_inactive_ratio); +} + +static int __init moto_mm_init(void) +{ + int ret = 0; + + ret = register_all_hooks(); + if (ret != 0) { + return ret; + } + + pr_info("moto_mm_init succeed!\n"); + return 0; +} + +static void __exit moto_mm_exit(void) +{ + unregister_all_hook(); + + pr_info("moto_mm_exit succeed!\n"); + return; +} + +module_init(moto_mm_init); +module_exit(moto_mm_exit); +MODULE_DESCRIPTION("Motorola mm optimizations driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/moto_mmap_fault/Android.mk b/drivers/moto_mmap_fault/Android.mk new file mode 100644 index 000000000000..9047a745bf23 --- /dev/null +++ b/drivers/moto_mmap_fault/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := moto_mmap_fault.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + +ifeq ($(TUNE_MMAP_READAROUND), true) + KERNEL_CFLAGS += KCFLAGS=-DTUNE_MMAP_READAROUND + KBUILD_OPTIONS += KCFLAGS=-DTUNE_MMAP_READAROUND +endif + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/moto_mmap_fault/Kbuild b/drivers/moto_mmap_fault/Kbuild new file mode 100644 index 000000000000..5282f8338622 --- /dev/null +++ b/drivers/moto_mmap_fault/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += moto_mmap_fault.o diff --git a/drivers/moto_mmap_fault/Makefile b/drivers/moto_mmap_fault/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/moto_mmap_fault/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/moto_mmap_fault/moto_mmap_fault.c b/drivers/moto_mmap_fault/moto_mmap_fault.c new file mode 100644 index 000000000000..0dc50bcd09bc --- /dev/null +++ b/drivers/moto_mmap_fault/moto_mmap_fault.c @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2023 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include + +static int max_ra_pages = -1; +module_param(max_ra_pages, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(max_ra_pages, "Max read ahead pages"); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(5, 15, 104) || (LINUX_VERSION_CODE > KERNEL_VERSION(5, 10, 177) && LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)) +#ifndef TUNE_MMAP_READAROUND +#define TUNE_MMAP_READAROUND +#endif +#endif + +#if defined(TUNE_MMAP_READAROUND) +static void __nocfi tune_mmap_readaround(void *p, unsigned int ra_pages, pgoff_t pgoff, + pgoff_t *start, unsigned int *size, unsigned int *async_size) +{ + *start = max_t(long, 0, pgoff - max_ra_pages / 2); + *size = max_ra_pages; + *async_size = max_ra_pages / 4; + return; +} +#else +static void __nocfi filemap_fault_get_page(void *p, struct vm_fault *vmf, struct page **page_out, bool *retry) +{ + struct file *file = vmf->vma->vm_file; + pgoff_t offset = vmf->pgoff; + struct page *page = NULL; + struct file_ra_state *ra = NULL; + struct address_space *mapping = NULL; + unsigned int mmap_miss; + unsigned int old_ra_pages = 0; + + if (!file) + return; + + ra = &file->f_ra; + if(!ra) + return; + + mapping = file->f_mapping; + page = find_get_page(mapping, offset); + + if (likely(page) && !(vmf->flags & FAULT_FLAG_TRIED)) { + put_page(page); + } else if (!page) { + mmap_miss = READ_ONCE(ra->mmap_miss); + if ((vmf->vma->vm_flags & VM_RAND_READ) || + (!ra->ra_pages) || + (vmf->vma->vm_flags & VM_SEQ_READ) || + mmap_miss > 100) { + return; + } else { + old_ra_pages = ra->ra_pages; + if (ra->ra_pages > max_ra_pages) { + ra->ra_pages = max_ra_pages; // reduce the read ahead limit to 8 pages + vmf->android_oem_data1[0] = old_ra_pages; + vmf->android_oem_data1[1] = max_ra_pages; + } + return; + } + } else { + put_page(page); + } + + return; +} + +static void __nocfi filemap_fault_cache_page(void *p, struct vm_fault *vmf, struct page *page) +{ + struct file *file = vmf->vma->vm_file; + struct file_ra_state *ra = NULL; + + if (!file) + return; + + ra = &file->f_ra; + if(!ra) + return; + + if ((ra->ra_pages == max_ra_pages) && (vmf->android_oem_data1[0] != 0) + && (vmf->android_oem_data1[1] == max_ra_pages)) { + ra->ra_pages = (unsigned int)vmf->android_oem_data1[0]; //restore the old ra_pages + vmf->android_oem_data1[0] = 0; + vmf->android_oem_data1[1] = 0; + } +} +#endif + +static int __nocfi __init moto_mmap_fault_init(void) +{ + int ret = 0; + int ramsize_GB = (totalram_pages() >> (30 - PAGE_SHIFT)) + 1; + + if (max_ra_pages == -1) { + /* Set 8 pages for < 8G RAM and set 16 pages for >= 8G RAM */ + if (ramsize_GB < 8) + max_ra_pages = 8; + else + max_ra_pages = 16; + } + +#if defined(TUNE_MMAP_READAROUND) + pr_info("Using the new mmap fault driver, totalram size=%dGB", ramsize_GB); + ret = register_trace_android_vh_tune_mmap_readaround(tune_mmap_readaround, NULL); +#else + pr_info("Using the legacy mmap fault driver, totalram size=%dGB", ramsize_GB); + ret = register_trace_android_vh_filemap_fault_get_page(filemap_fault_get_page, NULL) ?: + register_trace_android_vh_filemap_fault_cache_page(filemap_fault_cache_page, NULL); +#endif + if (ret != 0) + return -ENXIO; + else + return 0; +} +static void __nocfi __exit moto_mmap_fault_exit(void) +{ +#if defined(TUNE_MMAP_READAROUND) + unregister_trace_android_vh_tune_mmap_readaround(tune_mmap_readaround, NULL); +#else + unregister_trace_android_vh_filemap_fault_get_page(filemap_fault_get_page, NULL); + unregister_trace_android_vh_filemap_fault_cache_page(filemap_fault_cache_page, NULL); +#endif +} + +module_init(moto_mmap_fault_init); +module_exit(moto_mmap_fault_exit); +MODULE_DESCRIPTION("Motorola vendor mmap fault driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/moto_swap/Android.mk b/drivers/moto_swap/Android.mk new file mode 100644 index 000000000000..c7b7e99166c5 --- /dev/null +++ b/drivers/moto_swap/Android.mk @@ -0,0 +1,21 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +KERNEL_CFLAGS += CONFIG_HYBRIDSWAP_ZRAM=y +KERNEL_CFLAGS += CONFIG_HYBRIDSWAP=y +KERNEL_CFLAGS += CONFIG_HYBRIDSWAP_SWAPD=y +KERNEL_CFLAGS += CONFIG_HYBRIDSWAP_CORE=y + +KBUILD_OPTIONS += CONFIG_HYBRIDSWAP_ZRAM=y +KBUILD_OPTIONS += CONFIG_HYBRIDSWAP=y +KBUILD_OPTIONS += CONFIG_HYBRIDSWAP_SWAPD=y +KBUILD_OPTIONS += CONFIG_HYBRIDSWAP_CORE=y + +LOCAL_MODULE := moto_swap.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki + +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/moto_swap/Kbuild b/drivers/moto_swap/Kbuild new file mode 100644 index 000000000000..290c038a8089 --- /dev/null +++ b/drivers/moto_swap/Kbuild @@ -0,0 +1,50 @@ +HAVE_KERNEL_5_4 = $(shell test -d $(ANDROID_BUILD_TOP)/kernel/msm-5.4 && echo 1) +HAVE_KERNEL_5_15 = $(shell test -d $(ANDROID_BUILD_TOP)/kernel_platform/gki/kernel/5.15 && echo 1) + +ifeq ($(HAVE_KERNEL_5_4),1) +ZRAM_SRC = zram-5.4 +EXTRA_CFLAGS += -DCONFIG_ZRAM_5_4 +else ifeq ($(HAVE_KERNEL_5_15),1) +ZRAM_SRC = zram-5.15 +EXTRA_CFLAGS += -DCONFIG_ZRAM_5_15 +else +ZRAM_SRC = zram-5.10 +EXTRA_CFLAGS += -DCONFIG_ZRAM_5_10 +endif + +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +ifneq ($(filter m y,$(CONFIG_HYBRIDSWAP_ZRAM)),) +EXTRA_CFLAGS += -DCONFIG_HYBRIDSWAP_ZRAM +endif + +ifneq ($(filter m y,$(CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK)),) +EXTRA_CFLAGS += -DCONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +endif + +ifneq ($(filter m y,$(CONFIG_HYBRIDSWAP)),) +EXTRA_CFLAGS += -DCONFIG_HYBRIDSWAP +endif + +ifneq ($(filter m y,$(CONFIG_HYBRIDSWAP_SWAPD)),) +EXTRA_CFLAGS += -DCONFIG_HYBRIDSWAP_SWAPD +endif + +ifneq ($(filter m y,$(CONFIG_HYBRIDSWAP_CORE)),) +EXTRA_CFLAGS += -DCONFIG_HYBRIDSWAP_CORE +endif + +moto_swap-objs += $(ZRAM_SRC)/zram_drv.o +moto_swap-objs += hybridswap/hybridswap_main.o +moto_swap-objs += hybridswap/hybridswap_eswap.o + +ifneq ($(filter m y,$(CONFIG_HYBRIDSWAP_ASYNC_COMPRESS)),) +moto_swap-objs += hybridswap/hybridswap_akcompress.o +endif + +moto_swap-objs += hybridswap/hybridswap_swapd.o +moto_swap-objs += $(ZRAM_SRC)/zcomp.o + +obj-m += moto_swap.o diff --git a/drivers/moto_swap/Kconfig b/drivers/moto_swap/Kconfig new file mode 100644 index 000000000000..7b58f3946dd8 --- /dev/null +++ b/drivers/moto_swap/Kconfig @@ -0,0 +1,78 @@ +# SPDX-License-Identifier: GPL-2.0 +config HYBRIDSWAP_ZRAM + tristate "Compressed RAM block device support" + depends on BLOCK && SYSFS && ZSMALLOC && CRYPTO && !ZRAM + select CRYPTO_LZO + help + Creates virtual block devices called /dev/zramX (X = 0, 1, ...). + Pages written to these disks are compressed and stored in memory + itself. These disks allow very fast I/O and compression provides + good amounts of memory savings. + + It has several use cases, for example: /tmp storage, use as swap + disks and maybe many more. + + See Documentation/admin-guide/blockdev/zram.rst for more information. + +config HYBRIDSWAP_ZRAM_WRITEBACK + bool "Write back incompressible or idle page to backing device" + depends on HYBRIDSWAP_ZRAM + help + With incompressible page, there is no memory saving to keep it + in memory. Instead, write it out to backing device. + For this feature, admin should set up backing device via + /sys/block/zramX/backing_dev. + + With /sys/block/zramX/{idle,writeback}, application could ask + idle page's writeback to the backing device to save in memory. + + See Documentation/admin-guide/blockdev/zram.rst for more information. + +config HYBRIDSWAP_ZRAM_MEMORY_TRACKING + bool "Track zRam block status" + depends on HYBRIDSWAP_ZRAM && DEBUG_FS + help + With this feature, admin can track the state of allocated blocks + of zRAM. Admin could see the information via + /sys/kernel/debug/zram/zramX/block_state. + + See Documentation/admin-guide/blockdev/zram.rst for more information. + +config HYBRIDSWAP + bool "Enable Hybridswap" + depends on MEMCG && HYBRIDSWAP_ZRAM && !HYBRIDSWAP_ZRAM_WRITEBACK + default y + help + Hybridswap is a intelligent memory management solution. + +config HYBRIDSWAP_SWAPD + bool "Enable hybridswap swapd thread to reclaim anon pages in background" + default y + depends on HYBRIDSWAP + help + swapd is a kernel thread that reclaim anonymous pages in the + background. When the use of swap pages reaches the watermark + and the refault of anonymous pages is high, the content of + zram will exchanged to eswap by a certain percentage. + +# Selected when system need hybridswap container +config HYBRIDSWAP_CORE + bool "Hybridswap container device support" + depends on HYBRIDSWAP_ZRAM && HYBRIDSWAP + default y + help + Say Y here if you want to use the hybridswap + as the backend device in ZRAM. + If unsure, say N here. + This module can't be compiled as a module, + the module is as one part of the ZRAM driver. + +config HYBRIDSWAP_ASYNC_COMPRESS + bool "hypbridswap support asynchronous compress anon pages" + depends on HYBRIDSWAP_ZRAM && HYBRIDSWAP + default n + help + Say Y here if you want to create asynchronous thread + for compress anon pages. + If unsure, say N here. + This feature will reduce the kswapd cpu load. diff --git a/drivers/moto_swap/Makefile b/drivers/moto_swap/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/moto_swap/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/moto_swap/hybridswap/hybridswap.h b/drivers/moto_swap/hybridswap/hybridswap.h new file mode 100644 index 000000000000..2fa4e13d7ad5 --- /dev/null +++ b/drivers/moto_swap/hybridswap/hybridswap.h @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020-2022 Oplus. All rights reserved. + */ + +#ifndef HYBRIDSWAP_H +#define HYBRIDSWAP_H +extern int __init hybridswap_pre_init(void); +extern ssize_t hybridswap_vmstat_show(struct device *dev, + struct device_attribute *attr, char *buf); +extern ssize_t hybridswap_loglevel_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len); +extern ssize_t hybridswap_loglevel_show(struct device *dev, + struct device_attribute *attr, char *buf); +extern ssize_t hybridswap_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len); +extern ssize_t hybridswap_enable_show(struct device *dev, + struct device_attribute *attr, char *buf); +#ifdef CONFIG_HYBRIDSWAP_CORE +extern void hybridswap_record(struct zram *zram, u32 index, struct mem_cgroup *memcg); +extern void hybridswap_untrack(struct zram *zram, u32 index); +extern int hybridswap_page_fault(struct zram *zram, u32 index); +extern bool hybridswap_delete(struct zram *zram, u32 index); + +extern ssize_t hybridswap_report_show(struct device *dev, + struct device_attribute *attr, char *buf); +extern ssize_t hybridswap_stat_snap_show(struct device *dev, + struct device_attribute *attr, char *buf); +extern ssize_t hybridswap_meminfo_show(struct device *dev, + struct device_attribute *attr, char *buf); +extern ssize_t hybridswap_core_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len); +extern ssize_t hybridswap_core_enable_show(struct device *dev, + struct device_attribute *attr, char *buf); +extern ssize_t hybridswap_loop_device_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len); +extern ssize_t hybridswap_loop_device_show(struct device *dev, + struct device_attribute *attr, char *buf); +extern ssize_t hybridswap_dev_life_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len); +extern ssize_t hybridswap_dev_life_show(struct device *dev, + struct device_attribute *attr, char *buf); +extern ssize_t hybridswap_quota_day_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len); +extern ssize_t hybridswap_quota_day_show(struct device *dev, + struct device_attribute *attr, char *buf); +extern ssize_t hybridswap_zram_increase_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len); +extern ssize_t hybridswap_zram_increase_show(struct device *dev, + struct device_attribute *attr, char *buf); +#endif + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS +/* 63---48,47--32,31-0 : cgroup id, thread_index, index*/ +#define ZRAM_INDEX_SHIFT 32 +#define CACHE_INDEX_SHIFT 32 +#define CACHE_INDEX_MASK ((1llu << CACHE_INDEX_SHIFT) - 1) +#define ZRAM_INDEX_MASK ((1llu << ZRAM_INDEX_SHIFT) - 1) + +#define cache_index_val(index) (((unsigned long)index & CACHE_INDEX_MASK) << ZRAM_INDEX_SHIFT) +#define zram_index_val(id) ((unsigned long)id & ZRAM_INDEX_MASK) +#define mk_page_val(cache_index, index) (cache_index_val(cache_index) | zram_index_val(index)) + +#define fetch_cache_id(page) ((page->private >> 32) & CACHE_INDEX_MASK) +#define fetch_zram_index(page) (page->private & ZRAM_INDEX_MASK) + +#define zram_set_page(zram, index, page) (zram->table[index].page = page) +#define zram_fetch_page(zram, index) (zram->table[index].page) + +extern void del_page_from_cache(struct page *page); +extern int add_anon_page2cache(struct zram * zram, u32 index, + struct page *page); +extern ssize_t hybridswap_akcompress_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len); +extern ssize_t hybridswap_akcompress_show(struct device *dev, + struct device_attribute *attr, char *buf); +extern void put_free_page(struct page *page); +extern void put_anon_pages(struct page *page); +extern int akcompress_cache_page_fault(struct zram *zram, + struct page *page, u32 index); +extern void destroy_akcompressd_task(struct zram *zram); +#endif + +#ifdef CONFIG_HYBRIDSWAP_SWAPD +extern ssize_t hybridswap_swapd_pause_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len); +extern ssize_t hybridswap_swapd_pause_show(struct device *dev, + struct device_attribute *attr, char *buf); +#endif +static inline bool current_is_mswapd(void) +{ +#ifdef CONFIG_HYBRIDSWAP_SWAPD + return (strncmp(current->comm, "mswapd:", sizeof("mswapd:") - 1) == 0); +#else + return false; +#endif +} +#endif /* HYBRIDSWAP_H */ diff --git a/drivers/moto_swap/hybridswap/hybridswap_akcompress.c b/drivers/moto_swap/hybridswap/hybridswap_akcompress.c new file mode 100644 index 000000000000..4d18890149cf --- /dev/null +++ b/drivers/moto_swap/hybridswap/hybridswap_akcompress.c @@ -0,0 +1,587 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020-2022 Oplus. All rights reserved. + */ + +#define pr_fmt(fmt) "moto_swap: " fmt + +#include +#include +#include +#include +#include + +#ifdef CONFIG_ZRAM_5_4 +#include "../zram-5.4/zram_drv.h" +#include "../zram-5.4/zram_drv_internal.h" +#define MEMCG_OEM_DATA(memcg) ((memcg)->android_oem_data1) +#elif defined CONFIG_ZRAM_5_15 +#include "../zram-5.15/zram_drv.h" +#include "../zram-5.15/zram_drv_internal.h" +#define BIO_MAX_PAGES BIO_MAX_VECS +#define MEMCG_OEM_DATA(memcg) ((memcg)->android_oem_data1[0]) +#else +#include "../zram-5.10/zram_drv.h" +#include "../zram-5.10/zram_drv_internal.h" +#define MEMCG_OEM_DATA(memcg) ((memcg)->android_oem_data1) +#endif +#include "hybridswap_internal.h" +#include "hybridswap.h" + +struct compress_info_s { + struct list_head free_page_head; + spinlock_t free_lock; + unsigned int free_cnt; + + unsigned int max_cnt; +} compress_info; + +#define MAX_AKCOMPRESSD_THREADS 4 +#define DEFAULT_CACHE_SIZE_MB 64 +#define DEFAULT_COMPRESS_BATCH_MB 1 +#define DEFAULT_CACHE_COUNT ((DEFAULT_CACHE_SIZE_MB << 20) >> PAGE_SHIFT) +#define WAKEUP_AKCOMPRESSD_WATERMARK ((DEFAULT_COMPRESS_BATCH_MB << 20) >> PAGE_SHIFT) + +static wait_queue_head_t akcompressd_wait; +static struct task_struct *akc_task[MAX_AKCOMPRESSD_THREADS]; +static atomic64_t akc_cnt[MAX_AKCOMPRESSD_THREADS]; +static int akcompressd_threads = 0; +static atomic64_t cached_cnt; +static struct zram *zram_info; +static DEFINE_MUTEX(akcompress_init_lock); + +struct idr cached_idr = IDR_INIT(cached_idr); +DEFINE_SPINLOCK(cached_idr_lock); + +static void wake_all_akcompressd(void); + +void clear_page_memcg(struct cgroup_cache_page *cache) +{ + struct list_head *pos; + struct page *page; + + spin_lock(&cache->lock); + if (list_empty(&cache->head)) + goto out; + + list_for_each(pos, &cache->head) { + page = list_entry(pos, struct page, lru); + if (!page->mem_cgroup) + BUG(); + page->mem_cgroup = NULL; + } + +out: + cache->dead = 1; + spin_unlock(&cache->lock); +} + +static inline struct page * fetch_free_page(void) +{ + struct page *page = NULL; + + spin_lock(&compress_info.free_lock); + if (compress_info.free_cnt > 0) { + if (list_empty(&compress_info.free_page_head)) + BUG(); + page = lru_to_page(&compress_info.free_page_head); + list_del(&page->lru); + compress_info.free_cnt--; + } + spin_unlock(&compress_info.free_lock); + + return page; +} + +void put_free_page(struct page *page) +{ + set_page_private(page, 0); + spin_lock(&compress_info.free_lock); + list_add_tail(&page->lru, &compress_info.free_page_head); + compress_info.free_cnt++; + spin_unlock(&compress_info.free_lock); +} + +static inline struct cgroup_cache_page *find_and_fetch_memcg_cache(int cache_id) +{ + struct cgroup_cache_page *cache; + + spin_lock(&cached_idr_lock); + cache = (struct cgroup_cache_page *)idr_find(&cached_idr, cache_id); + if (unlikely(!cache)) { + spin_unlock(&cached_idr_lock); + pr_err("cache_id %d cache not find.\n", cache_id); + + return NULL; + } + fetch_memcg_cache(container_of(cache, memcg_hybs_t, cache)); + spin_unlock(&cached_idr_lock); + + return cache; +} + +void del_page_from_cache(struct page *page) +{ + int cache_id; + struct cgroup_cache_page *cache; + + if (!page) + return; + + cache_id = fetch_cache_id(page); + if (unlikely(cache_id < 0 || cache_id > MEM_CGROUP_ID_MAX)) { + hybp(HYB_ERR, "page %p cache_id %d index %u is invalid.\n", + page, cache_id, fetch_zram_index(page)); + return; + } + + cache = find_and_fetch_memcg_cache(cache_id); + if (!cache) + return; + + spin_lock(&cache->lock); + list_del(&page->lru); + cache->cnt--; + spin_unlock(&cache->lock); + put_memcg_cache(container_of(cache, memcg_hybs_t, cache)); + atomic64_dec(&cached_cnt); +} + +void del_page_from_cache_with_cache(struct page *page, + struct cgroup_cache_page *cache) +{ + spin_lock(&cache->lock); + list_del(&page->lru); + cache->cnt--; + spin_unlock(&cache->lock); + atomic64_dec(&cached_cnt); +} + +void put_anon_pages(struct page *page) +{ + memcg_hybs_t *hybs = MEMCGRP_ITEM_DATA(page->mem_cgroup); + + spin_lock(&hybs->cache.lock); + list_add(&page->lru, &hybs->cache.head); + hybs->cache.cnt++; + spin_unlock(&hybs->cache.lock); +} + +static inline bool can_stop_working(struct cgroup_cache_page *cache, int index) +{ + spin_lock(&cache->lock); + if (unlikely(!list_empty(&cache->head))) { + spin_unlock(&cache->lock); + return false; + } + spin_unlock(&cache->lock); + return 1; +} + +static int check_cache_state(struct cgroup_cache_page *cache) +{ + if (cache->cnt == 0 || cache->compressing == 1) + return 0; + + spin_lock(&cache->lock); + if (cache->cnt == 0 || cache->compressing) { + spin_unlock(&cache->lock); + return 0; + } + cache->compressing = 1; + spin_unlock(&cache->lock); + fetch_memcg_cache(container_of(cache, memcg_hybs_t, cache)); + return 1; +} + +struct cgroup_cache_page *fetch_one_cache(void) +{ + struct cgroup_cache_page *cache = NULL; + int id; + + spin_lock(&cached_idr_lock); + idr_for_each_entry(&cached_idr, cache, id) { + if (check_cache_state(cache)) + break; + } + spin_unlock(&cached_idr_lock); + + return cache; +} + +void mark_compressing_stop(struct cgroup_cache_page *cache) +{ + spin_lock(&cache->lock); + if (cache->dead) + hybp(HYB_WARN, "stop compressing, may be cgroup is delelted\n"); + cache->compressing = 0; + spin_unlock(&cache->lock); + put_memcg_cache(container_of(cache, memcg_hybs_t, cache)); +} + +static inline struct page *fetch_anon_page(struct zram *zram, + struct cgroup_cache_page *cache) +{ + struct page *page, *prev_page; + int index; + + if (compress_info.free_cnt == 0) + return NULL; + + prev_page = NULL; +try_again: + page = NULL; + + spin_lock(&cache->lock); + if (!list_empty(&cache->head)) { + page = lru_to_page(&cache->head); + index = fetch_zram_index(page); + } + spin_unlock(&cache->lock); + + if (page) { + if (prev_page && (page == prev_page)) { + hybp(HYB_ERR, "zram %p index %d page %p\n", + zram, index, page); + BUG(); + } + + zram_slot_lock(zram, index); + if (!zram_test_flag(zram, index, ZRAM_CACHED)) { + zram_slot_unlock(zram, index); + prev_page = page; + goto try_again; + } + + prev_page = NULL; + zram_clear_flag(zram, index, ZRAM_CACHED); + del_page_from_cache_with_cache(page, cache); + zram_set_flag(zram, index, ZRAM_CACHED_COMPRESS); + zram_slot_unlock(zram, index); + } + + return page; +} + +int add_anon_page2cache(struct zram * zram, u32 index, struct page *page) +{ + struct page *dst_page; + void *src, *dst; + struct mem_cgroup *memcg; + struct cgroup_cache_page *cache; + memcg_hybs_t *hybs; + + if (akcompressd_threads == 0) + return 0; + + memcg = page->mem_cgroup; + if (!memcg || !MEMCGRP_ITEM_DATA(memcg)) + return 0; + + hybs = MEMCGRP_ITEM_DATA(memcg); + cache = &hybs->cache; + if (find_and_fetch_memcg_cache(cache->id) != cache) + return 0; + + spin_lock(&cache->lock); + if (cache->dead == 1) { + spin_unlock(&cache->lock); + return 0; + } + spin_unlock(&cache->lock); + + dst_page = fetch_free_page(); + if (!dst_page) + return 0; + + src = kmap_atomic(page); + dst = kmap_atomic(dst_page); + memcpy(dst, src, PAGE_SIZE); + kunmap_atomic(src); + kunmap_atomic(dst); + + dst_page->mem_cgroup = memcg; + set_page_private(dst_page, mk_page_val(cache->id, index)); + update_zram_index(zram, index, (unsigned long)dst_page); + atomic64_inc(&cached_cnt); + wake_all_akcompressd(); + hybp(HYB_DEBUG, "add_anon_page2cache index %u page %p passed\n", + index, dst_page); + return 1; +} + +static inline void akcompressd_try_to_sleep(wait_queue_head_t *waitq) +{ + DEFINE_WAIT(wait); + + prepare_to_wait(waitq, &wait, TASK_INTERRUPTIBLE); + freezable_schedule(); + finish_wait(waitq, &wait); +} + +static int akcompressd_func(void *data) +{ + struct page *page; + int ret, thread_index; + struct list_head compress_fail_list; + struct cgroup_cache_page *cache = NULL; + + thread_index = (int)data; + if (thread_index < 0 || thread_index >= MAX_AKCOMPRESSD_THREADS) { + hybp(HYB_ERR, "akcompress task index %d is invalid.\n", thread_index); + return -EINVAL; + } + + set_freezable(); + while (!kthread_should_stop()) { + akcompressd_try_to_sleep(&akcompressd_wait); + count_swapd_event(AKCOMPRESSD_WAKEUP); + + cache = fetch_one_cache(); + if (!cache) + continue; + +finish_last_jobs: + INIT_LIST_HEAD(&compress_fail_list); + page = fetch_anon_page(zram_info, cache); + while (page) { + ret = async_compress_page(zram_info, page); + put_memcg_cache(container_of(cache, memcg_hybs_t, cache)); + + if (ret) + list_add(&page->lru, &compress_fail_list); + else { + atomic64_inc(&akc_cnt[thread_index]); + page->mem_cgroup = NULL; + put_free_page(page); + } + page = fetch_anon_page(zram_info, cache); + } + + if (!list_empty(&compress_fail_list)) + hybp(HYB_ERR, "have some compress failed pages.\n"); + + if (kthread_should_stop()) { + if (!can_stop_working(cache, thread_index)) + goto finish_last_jobs; + } + mark_compressing_stop(cache); + } + + return 0; +} + +static int update_akcompressd_threads(int thread_count, struct zram *zram) +{ + int drop, increase; + int last_index, start_index, hid; + static DEFINE_MUTEX(update_lock); + + if (thread_count < 0 || thread_count > MAX_AKCOMPRESSD_THREADS) { + hybp(HYB_ERR, "thread_count %d is invalid\n", thread_count); + return -EINVAL; + } + + mutex_lock(&update_lock); + if (!zram_info || zram_info != zram) + zram_info = zram; + + if (thread_count == akcompressd_threads) { + mutex_unlock(&update_lock); + return thread_count; + } + + last_index = akcompressd_threads - 1; + if (thread_count < akcompressd_threads) { + drop = akcompressd_threads - thread_count; + for (hid = last_index; hid > (last_index - drop); hid--) { + if (akc_task[hid]) { + kthread_stop(akc_task[hid]); + akc_task[hid] = NULL; + } + } + } else { + increase = thread_count - akcompressd_threads; + start_index = last_index + 1; + for (hid = start_index; hid < (start_index + increase); hid++) { + if (unlikely(akc_task[hid])) + BUG(); + akc_task[hid]= kthread_run(akcompressd_func, + (void*)(unsigned long)hid, "akcompressd:%d", hid); + if (IS_ERR(akc_task[hid])) { + pr_err("Failed to start akcompressd%d\n", hid); + akc_task[hid] = NULL; + break; + } + } + } + + hybp(HYB_INFO, "akcompressd_threads count changed, old:%d new:%d\n", + akcompressd_threads, thread_count); + akcompressd_threads = thread_count; + mutex_unlock(&update_lock); + + return thread_count; +} + +static void wake_all_akcompressd(void) +{ + if (atomic64_read(&cached_cnt) < WAKEUP_AKCOMPRESSD_WATERMARK) + return; + + if (!waitqueue_active(&akcompressd_wait)) + return; + + wake_up_interruptible(&akcompressd_wait); +} + +int create_akcompressd_task(struct zram *zram) +{ + return update_akcompressd_threads(1, zram) != 1; +} + +void destroy_akcompressd_task(struct zram *zram) +{ + (void)update_akcompressd_threads(0, zram); +} + +ssize_t hybridswap_akcompress_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int ret; + unsigned long val; + struct zram *zram = dev_to_zram(dev); + + ret = kstrtoul(buf, 0, &val); + if (unlikely(ret)) { + hybp(HYB_ERR, "val is error!\n"); + return -EINVAL; + } + + ret = update_akcompressd_threads(val, zram); + if (ret < 0) { + hybp(HYB_ERR, "create task failed, val %d\n", val); + return ret; + } + + return len; +} + +ssize_t hybridswap_akcompress_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int len = 0, id, i; + struct cgroup_cache_page *cache = NULL; + unsigned long cnt = atomic64_read(&cached_cnt); + memcg_hybs_t *hybs; + + len += sprintf(buf + len, "akcompressd_threads: %d\n", akcompressd_threads); + len += sprintf(buf + len, "cached page cnt: %lu\n", cnt); + len += sprintf(buf + len, "free page cnt: %u\n", compress_info.free_cnt); + + for (i = 0; i < MAX_AKCOMPRESSD_THREADS; i++) + len += sprintf(buf + len, "%-d %-d\n", i, atomic64_read(&akc_cnt[i])); + + if (cnt == 0) + return len; + + spin_lock(&cached_idr_lock); + idr_for_each_entry(&cached_idr, cache, id) { + hybs = container_of(cache, memcg_hybs_t, cache); + if (cache->cnt == 0) + continue; + + len += scnprintf(buf + len, PAGE_SIZE - len, "%s %d\n", + hybs->name, cache->cnt); + + if (len >= PAGE_SIZE) + break; + } + spin_unlock(&cached_idr_lock); + + return len; +} + +void __init akcompressd_pre_init(void) +{ + int i; + struct page *page; + + mutex_lock(&akcompress_init_lock); + INIT_LIST_HEAD(&compress_info.free_page_head); + spin_lock_init(&compress_info.free_lock); + compress_info.free_cnt = 0; + + init_waitqueue_head(&akcompressd_wait); + + atomic64_set(&cached_cnt, 0); + for (i = 0; i < MAX_AKCOMPRESSD_THREADS; i++) + atomic64_set(&akc_cnt[i], 0); + + for (i = 0; i < DEFAULT_CACHE_COUNT; i ++) { + page = alloc_page(GFP_KERNEL); + + if (page) { + list_add_tail(&page->lru, &compress_info.free_page_head); + } else + break; + } + compress_info.free_cnt = i; + mutex_unlock(&akcompress_init_lock); +} + +void __exit akcompressd_pre_deinit(void) +{ + int i; + struct page *page, *tmp; + + mutex_lock(&akcompress_init_lock); + if (list_empty(&compress_info.free_page_head)) + goto out; + list_for_each_entry_safe(page, tmp, &compress_info.free_page_head , lru) { + list_del(&page->lru); + free_page(page); + } + +out: + compress_info.free_cnt = 0; + mutex_unlock(&akcompress_init_lock); +} + +int akcompress_cache_page_fault(struct zram *zram, + struct page *page, u32 index) +{ + void *src, *dst; + + if (zram_test_flag(zram, index, ZRAM_CACHED)) { + struct page *src_page = (struct page *)zram_fetch_page(zram, index); + + src = kmap_atomic(src_page); + dst = kmap_atomic(page); + memcpy(dst, src, PAGE_SIZE); + kunmap_atomic(src); + kunmap_atomic(dst); + zram_slot_unlock(zram, index); + + hybp(HYB_DEBUG, "read_anon_page_from_cache index %u page %p passed, ZRAM_CACHED\n", + index, src_page); + return 1; + } + + if (zram_test_flag(zram, index, ZRAM_CACHED_COMPRESS)) { + struct page *src_page = (struct page *)zram_fetch_page(zram, index); + + src = kmap_atomic(src_page); + dst = kmap_atomic(page); + memcpy(dst, src, PAGE_SIZE); + kunmap_atomic(src); + kunmap_atomic(dst); + zram_slot_unlock(zram, index); + + hybp(HYB_DEBUG, "read_anon_page_from_cache index %u page %p passed, ZRAM_CACHED_COMPRESS\n", + index, src_page); + return 1; + } + + return 0; +} diff --git a/drivers/moto_swap/hybridswap/hybridswap_eswap.c b/drivers/moto_swap/hybridswap/hybridswap_eswap.c new file mode 100644 index 000000000000..9bad6d608ef8 --- /dev/null +++ b/drivers/moto_swap/hybridswap/hybridswap_eswap.c @@ -0,0 +1,5507 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020-2022 Oplus. All rights reserved. + */ + +#define pr_fmt(fmt) "moto_swap: " fmt + +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_FG_TASK_UID +#include +#endif +#include + +#ifdef CONFIG_ZRAM_5_4 +#include "../zram-5.4/zram_drv.h" +#include "../zram-5.4/zram_drv_internal.h" +#define MEMCG_OEM_DATA(memcg) ((memcg)->android_oem_data1) +#elif defined CONFIG_ZRAM_5_15 +#include "../zram-5.15/zram_drv.h" +#include "../zram-5.15/zram_drv_internal.h" +#define BIO_MAX_PAGES BIO_MAX_VECS +#define MEMCG_OEM_DATA(memcg) ((memcg)->android_oem_data1[0]) +#else +#include "../zram-5.10/zram_drv.h" +#include "../zram-5.10/zram_drv_internal.h" +#define MEMCG_OEM_DATA(memcg) ((memcg)->android_oem_data1) +#endif +#include "hybridswap_internal.h" +#include "hybridswap.h" + +#define PRE_EOL_INFO_OVER_VAL 2 +#define LIFE_TIME_EST_OVER_VAL 8 +#define DEFAULT_STORED_WM_RATIO 90 +#define DEVICE_NAME_LEN 64 +#define MIN_RECLAIM_ZRAM_SZ (1024 * 1024) +#define esentry_extid(e) ((e) >> ESWAP_SHIFT) +#define esentry_pgid(e) (((e) & ((1 << ESWAP_SHIFT) - 1)) >> PAGE_SHIFT) +#define esentry_pgoff(e) ((e) & (PAGE_SIZE - 1)) +#define DUMP_BUF_LEN 512 +#define HYBRIDSWAP_KEY_INDEX 0 +#define HYBRIDSWAP_KEY_SIZE 64 +#define HYBRIDSWAP_KEY_INDEX_SHIFT 3 +#define HYBRIDSWAP_MAX_INFILGHT_NUM 256 +#define HYBRIDSWAP_SECTOR_SHIFT 9 +#define HYBRIDSWAP_PAGE_SIZE_SECTOR (PAGE_SIZE >> HYBRIDSWAP_SECTOR_SHIFT) +#define HYBRIDSWAP_READ_TIME 10 +#define HYBRIDSWAP_WRITE_TIME 100 +#define HYB_FAULT_OUT_TIME 10 +#define CLASS_NAME_LEN 32 +#define MBYTE_SHIFT 20 +#define ENTRY_PTR_SHIFT 23 +#define ENTRY_MCG_SHIFT_HALF 8 +#define ENTRY_LOCK_BIT ENTRY_MCG_SHIFT_HALF +#define ENTRY_DATA_BIT (ENTRY_PTR_SHIFT + ENTRY_MCG_SHIFT_HALF + \ + ENTRY_MCG_SHIFT_HALF + 1) + +struct zs_eswap_para { + struct hybridswap_page_pool *pool; + size_t alloc_size; + bool fast; + bool nofail; +}; + +struct hybridswap_cfg { + atomic_t enable; + atomic_t out_to_eswap_enable; + struct hybstatus *stat; + struct workqueue_struct *reclaim_wq; + struct zram *zram; + + atomic_t dev_life; + unsigned long quota_day; +}; + +struct async_req { + struct mem_cgroup *mcg; + unsigned long size; + unsigned long out_size; + unsigned long reclaimined_sz; + struct work_struct work; + int nice; + bool preload; +}; + +struct io_priv { + struct zram *zram; + enum hybridswap_class class; + struct hybridswap_page_pool page_pool; +}; + +struct io_work_arg { + void *iohandle; + struct hybridswap_entry *ioentry; + struct hybridswap_buffer io_buf; + struct io_priv data; + struct hybridswap_key_point_record record; +}; + +struct hyb_sgm_time { + ktime_t submit_bio; + ktime_t end_io; +}; + +struct hyb_sgm { + struct work_struct stopio_work; + struct hyb_sgm_time time; + struct hybridswap_io_req *req; + struct hybridswap_entry *io_entries_fifo[BIO_MAX_PAGES]; + struct list_head io_entries; + sector_t segment_sector; + int eswap_cnt; + u32 bio_result; + int page_cnt; +}; + +struct hyb_info { + unsigned long size; + int total_objects; + int nr_es; + int memcg_num; + + unsigned long *bitmask; + atomic_t last_alloc_bit; + + struct hyb_entries_table *eswap_table; + struct hyb_entries_head *eswap; + + struct hyb_entries_table *objects; + struct hyb_entries_head *maps; + struct hyb_entries_head *lru; + + atomic_t stored_exts; + atomic_t *eswap_stored_pages; + + unsigned int memcgid_cnt[MEM_CGROUP_ID_MAX + 1]; +}; + +struct hyb_entries_head { + unsigned int mcg_left : 8; + unsigned int lock : 1; + unsigned int prev : 23; + unsigned int mcg_right : 8; + unsigned int data : 1; + unsigned int next : 23; +}; + +struct hyb_entries_table { + struct hyb_entries_head *(*fetch_node)(int, void *); + void *private; +}; + +#define index_node(index, tab) ((tab)->fetch_node((index), (tab)->private)) +#define next_index(index, tab) (index_node((index), (tab))->next) +#define prev_index(index, tab) (index_node((index), (tab))->prev) +#define is_last_index(index, hindex, tab) (next_index(index, tab) == (hindex)) +#define is_first_index(index, hindex, tab) (prev_index(index, tab) == (hindex)) +#define hyb_entries_for_each_entry(index, hindex, tab) \ + for ((index) = next_index((hindex), (tab)); \ + (index) != (hindex); (index) = next_index((index), (tab))) +#define hyb_entries_for_each_entry_safe(index, tmp, hindex, tab) \ + for ((index) = next_index((hindex), (tab)), (tmp) = next_index((index), (tab)); \ + (index) != (hindex); (index) = (tmp), (tmp) = next_index((index), (tab))) +#define hyb_entries_for_each_entry_reverse(index, hindex, tab) \ + for ((index) = prev_index((hindex), (tab)); \ + (index) != (hindex); (index) = prev_index((index), (tab))) +#define hyb_entries_for_each_entry_reverse_safe(index, tmp, hindex, tab) \ + for ((index) = prev_index((hindex), (tab)), (tmp) = prev_index((index), (tab)); \ + (index) != (hindex); (index) = (tmp), (tmp) = prev_index((index), (tab))) + +static unsigned long warn_level[HYB_CLASS_BUTT] = { + 0, 500, 500, 0 +}; + +const char *key_point_name[HYB_KYE_POINT_BUTT] = { + "START", + "INIT", + "IOENTRY_ALLOC", + "FIND_ESWAP", + "IO_ESWAP", + "SEGMENT_ALLOC", + "BIO_ALLOC", + "SUBMIT_BIO", + "END_IO", + "SCHED_WORK", + "END_WORK", + "CALL_BACK", + "WAKE_UP", + "ZRAM_LOCK", + "DONE" +}; + +static const char class_name[HYB_CLASS_BUTT][CLASS_NAME_LEN] = { + "out_to_eswap", + "page_fault", + "batches", + "readahead" +}; + +static const char *fg_bg[2] = {"BG", "FG"}; + +bool hyb_io_work_begin_flag; +struct hybridswap_cfg global_settings; + +static u8 hybridswap_io_key[HYBRIDSWAP_KEY_SIZE]; +static struct workqueue_struct *hybridswap_proc_read_workqueue; +static struct workqueue_struct *hybridswap_proc_write_workqueue; +static char loop_device[DEVICE_NAME_LEN]; + +struct mem_cgroup *find_memcg_by_id(unsigned short memcgid); +int obj_index(struct hyb_info *infos, int index); +int eswap_index(struct hyb_info *infos, int index); +int memcgindex(struct hyb_info *infos, int index); +void free_hyb_info(struct hyb_info *infos); +struct hyb_info *alloc_hyb_info(unsigned long ori_size, unsigned long comp_size); +void hybridswap_check_infos_eswap(struct hyb_info *infos); +void hybridswap_free_eswap(struct hyb_info *infos, int eswapid); +int hybridswap_alloc_eswap(struct hyb_info *infos, struct mem_cgroup *mcg); +int fetch_eswap(struct hyb_info *infos, int eswapid); +void put_eswap(struct hyb_info *infos, int eswapid); +int fetch_memcg_eswap(struct hyb_info *infos, struct mem_cgroup *mcg); +int fetch_memcg_zram_entry(struct hyb_info *infos, struct mem_cgroup *mcg); +int fetch_eswap_zram_entry(struct hyb_info *infos, int eswapid); +struct hyb_entries_table *alloc_table(struct hyb_entries_head *(*fetch_node)(int, void *), + void *private, gfp_t gfp); +void hyb_lock_with_idx(int index, struct hyb_entries_table *table); +void hyb_unlock_with_idx(int index, struct hyb_entries_table *table); +void hyb_entries_init(int index, struct hyb_entries_table *table); +void hyb_entries_add_nolock(int index, int hindex, struct hyb_entries_table *table); +void hyb_entries_add_tail_nolock(int index, int hindex, struct hyb_entries_table *table); +void hyb_entries_del_nolock(int index, int hindex, struct hyb_entries_table *table); +void hyb_entries_add(int index, int hindex, struct hyb_entries_table *table); +void hyb_entries_add_tail(int index, int hindex, struct hyb_entries_table *table); +void hyb_entries_del(int index, int hindex, struct hyb_entries_table *table); +unsigned short hyb_entries_fetch_memcgid(int index, struct hyb_entries_table *table); +void hyb_entries_set_memcgid(int index, struct hyb_entries_table *table, int memcgid); +bool hyb_entries_set_priv(int index, struct hyb_entries_table *table); +bool hyb_entries_clear_priv(int index, struct hyb_entries_table *table); +bool hyb_entries_test_priv(int index, struct hyb_entries_table *table); +bool hyb_entries_empty(int hindex, struct hyb_entries_table *table); +void zram_set_mcg(struct zram *zram, u32 index, int memcgid); +struct mem_cgroup *zram_fetch_mcg(struct zram *zram, u32 index); +int zram_fetch_mcg_last_index(struct hyb_info *infos, + struct mem_cgroup *mcg, + int *index, int max_cnt); +int swap_maps_fetch_eswap_index(struct hyb_info *infos, + int eswapid, int *index); +void swap_sorted_list_add(struct zram *zram, u32 index, struct mem_cgroup *memcg); +void swap_sorted_list_add_tail(struct zram *zram, u32 index, struct mem_cgroup *mcg); +void swap_sorted_list_del(struct zram *zram, u32 index); +void swap_maps_insert(struct zram *zram, u32 index); +void swap_maps_destroy(struct zram *zram, u32 index); + +static void hybridswapiowrkshow(struct seq_file *m, struct hybstatus *stat) +{ + int i; + + for (i = 0; i < HYB_CLASS_BUTT; ++i) { + seq_printf(m, "hybridswap_%s_total_lat: %lld\n", + class_name[i], + atomic64_read(&stat->lat[i].total_lat)); + seq_printf(m, "hybridswap_%s_max_lat: %lld\n", + class_name[i], + atomic64_read(&stat->lat[i].max_lat)); + seq_printf(m, "hybridswap_%s_timeout_cnt: %lld\n", + class_name[i], + atomic64_read(&stat->lat[i].timeout_cnt)); + } + + for (i = 0; i < 2; i++) { + seq_printf(m, "page_fault_timeout_100ms_cnt(%s): %lld\n", + fg_bg[i], + atomic64_read(&stat->fault_stat[i].timeout_100ms_cnt)); + seq_printf(m, "page_fault_timeout_500ms_cnt(%s): %lld\n", + fg_bg[i], + atomic64_read(&stat->fault_stat[i].timeout_500ms_cnt)); + } +} + +static void hybstatuss_show(struct seq_file *m, + struct hybstatus *stat) +{ + seq_printf(m, "hybridswap_out_times: %lld\n", + atomic64_read(&stat->reclaimin_cnt)); + seq_printf(m, "hybridswap_out_comp_size: %lld MB\n", + atomic64_read(&stat->reclaimin_bytes) >> MBYTE_SHIFT); + if (PAGE_SHIFT < MBYTE_SHIFT) + seq_printf(m, "hybridswap_out_ori_size: %lld MB\n", + atomic64_read(&stat->reclaimin_pages) >> + (MBYTE_SHIFT - PAGE_SHIFT)); + seq_printf(m, "hybridswap_in_times: %lld\n", + atomic64_read(&stat->batchout_cnt)); + seq_printf(m, "hybridswap_in_comp_size: %lld MB\n", + atomic64_read(&stat->batchout_bytes) >> MBYTE_SHIFT); + if (PAGE_SHIFT < MBYTE_SHIFT) + seq_printf(m, "hybridswap_in_ori_size: %lld MB\n", + atomic64_read(&stat->batchout_pages) >> + (MBYTE_SHIFT - PAGE_SHIFT)); + seq_printf(m, "hybridswap_all_fault: %lld\n", + atomic64_read(&stat->fault_cnt)); + seq_printf(m, "hybridswap_fault: %lld\n", + atomic64_read(&stat->hybridswap_fault_cnt)); +} + +static void hyb_info_info_show(struct seq_file *m, + struct hybstatus *stat) +{ + seq_printf(m, "hybridswap_reout_ori_size: %lld MB\n", + atomic64_read(&stat->reout_pages) >> + (MBYTE_SHIFT - PAGE_SHIFT)); + seq_printf(m, "hybridswap_reout_comp_size: %lld MB\n", + atomic64_read(&stat->reout_bytes) >> MBYTE_SHIFT); + seq_printf(m, "hybridswap_store_comp_size: %lld MB\n", + atomic64_read(&stat->stored_size) >> MBYTE_SHIFT); + seq_printf(m, "hybridswap_store_ori_size: %lld MB\n", + atomic64_read(&stat->stored_pages) >> + (MBYTE_SHIFT - PAGE_SHIFT)); + seq_printf(m, "hybridswap_notify_free_size: %lld MB\n", + atomic64_read(&stat->notify_free) >> + (MBYTE_SHIFT - ESWAP_SHIFT)); + seq_printf(m, "hybridswap_store_memcg_cnt: %lld\n", + atomic64_read(&stat->mcg_cnt)); + seq_printf(m, "hybridswap_store_eswap_cnt: %lld\n", + atomic64_read(&stat->eswap_cnt)); + seq_printf(m, "hybridswap_store_frag_info_cnt: %lld\n", + atomic64_read(&stat->frag_cnt)); +} + +static void hybridswap_fail_show(struct seq_file *m, + struct hybstatus *stat) +{ + int i; + + for (i = 0; i < HYB_CLASS_BUTT; ++i) { + seq_printf(m, "hybridswap_%s_io_fail_cnt: %lld\n", + class_name[i], + atomic64_read(&stat->io_fail_cnt[i])); + seq_printf(m, "hybridswap_%s_alloc_fail_cnt: %lld\n", + class_name[i], + atomic64_read(&stat->alloc_fail_cnt[i])); + } +} + +int hybridswap_psi_show(struct seq_file *m, void *v) +{ + struct hybstatus *stat = NULL; + + if (!hybridswap_core_enabled()) + return -EINVAL; + + stat = hybridswap_fetch_stat_obj(); + if (unlikely(!stat)) { + hybp(HYB_ERR, "can't fetch stat obj!\n"); + return -EINVAL; + } + + hybstatuss_show(m, stat); + hyb_info_info_show(m, stat); + hybridswapiowrkshow(m, stat); + hybridswap_fail_show(m, stat); + + return 0; +} + +unsigned long hybridswap_fetch_zram_used_pages(void) +{ + struct hybstatus *stat = NULL; + + if (!hybridswap_core_enabled()) + return 0; + + stat = hybridswap_fetch_stat_obj(); + if (unlikely(!stat)) { + hybp(HYB_ERR, "can't get stat obj!\n"); + + return 0; + } + + return atomic64_read(&stat->zram_stored_pages); +} + +unsigned long long hybridswap_fetch_zram_pagefault(void) +{ + struct hybstatus *stat = NULL; + + if (!hybridswap_core_enabled()) + return 0; + + stat = hybridswap_fetch_stat_obj(); + if (unlikely(!stat)) { + hybp(HYB_ERR, "can't fetch stat obj!\n"); + + return 0; + } + + return atomic64_read(&stat->fault_cnt); +} + +bool hybridswap_reclaim_work_running(void) +{ + struct hybstatus *stat = NULL; + + if (!hybridswap_core_enabled()) + return false; + + stat = hybridswap_fetch_stat_obj(); + if (unlikely(!stat)) { + hybp(HYB_ERR, "can't fetch stat obj!\n"); + + return 0; + } + + return atomic64_read(&stat->reclaimin_infight) ? true : false; +} + +unsigned long long hybridswap_read_mcg_stats(struct mem_cgroup *mcg, + enum hybridswap_mcg_member mcg_member) +{ + struct mem_cgroup_hybridswap *mcg_hybs; + + unsigned long long val = 0; + int extcnt; + + if (!hybridswap_core_enabled()) + return 0; + + mcg_hybs = MEMCGRP_ITEM_DATA(mcg); + if (!mcg_hybs) { + hybp(HYB_DEBUG, "NULL mcg_hybs\n"); + return 0; + } + + switch (mcg_member) { + case MCG_ZRAM_STORED_SZ: + val = atomic64_read(&mcg_hybs->zram_stored_size); + break; + case MCG_ZRAM_STORED_PG_SZ: + val = atomic64_read(&mcg_hybs->zram_page_size); + break; + case MCG_DISK_STORED_SZ: + val = atomic64_read(&mcg_hybs->hybridswap_stored_size); + break; + case MCG_DISK_STORED_PG_SZ: + val = atomic64_read(&mcg_hybs->hybridswap_stored_pages); + break; + case MCG_ANON_FAULT_CNT: + val = atomic64_read(&mcg_hybs->hybridswap_allfaultcnt); + break; + case MCG_DISK_FAULT_CNT: + val = atomic64_read(&mcg_hybs->hybridswap_faultcnt); + break; + case MCG_ESWAPOUT_CNT: + val = atomic64_read(&mcg_hybs->hybridswap_outcnt); + break; + case MCG_ESWAPOUT_SZ: + val = atomic64_read(&mcg_hybs->hybridswap_outextcnt) << ESWAP_SHIFT; + break; + case MCG_ESWAPIN_CNT: + val = atomic64_read(&mcg_hybs->hybridswap_incnt); + break; + case MCG_ESWAPIN_SZ: + val = atomic64_read(&mcg_hybs->hybridswap_inextcnt) << ESWAP_SHIFT; + break; + case MCG_DISK_SPACE: + extcnt = atomic_read(&mcg_hybs->hybridswap_extcnt); + if (extcnt < 0) + extcnt = 0; + val = ((unsigned long long) extcnt) << ESWAP_SHIFT; + break; + case MCG_DISK_SPACE_PEAK: + extcnt = atomic_read(&mcg_hybs->hybridswap_peakextcnt); + if (extcnt < 0) + extcnt = 0; + val = ((unsigned long long) extcnt) << ESWAP_SHIFT; + break; + default: + break; + } + + return val; +} + +void hybridswap_fail_record(enum hybridswap_fail_point point, + u32 index, int eswapid, unsigned char *task_comm) +{ + struct hybstatus *stat = NULL; + unsigned long flags; + unsigned int copylen = strlen(task_comm) + 1; + + stat = hybridswap_fetch_stat_obj(); + if (unlikely(!stat)) { + hybp(HYB_ERR, "can't fetch stat obj!\n"); + return; + } + + if (copylen > TASK_COMM_LEN) { + hybp(HYB_ERR, "task_comm len %d is err\n", copylen); + return; + } + + spin_lock_irqsave(&stat->record.lock, flags); + if (stat->record.num < MAX_FAIL_RECORD_NUM) { + stat->record.record[stat->record.num].point = point; + stat->record.record[stat->record.num].index = index; + stat->record.record[stat->record.num].eswapid = eswapid; + stat->record.record[stat->record.num].time = ktime_get(); + memcpy(stat->record.record[stat->record.num].task_comm, + task_comm, copylen); + stat->record.num++; + } + spin_unlock_irqrestore(&stat->record.lock, flags); +} + +static void hybridswap_fail_record_fetch( + struct hybridswap_fail_record_info *record_info) +{ + struct hybstatus *stat = NULL; + unsigned long flags; + + if (!hybridswap_core_enabled()) + return; + + stat = hybridswap_fetch_stat_obj(); + if (unlikely(!stat)) { + hybp(HYB_ERR, "can't fetch stat obj!\n"); + return; + } + + spin_lock_irqsave(&stat->record.lock, flags); + memcpy(record_info, &stat->record, + sizeof(struct hybridswap_fail_record_info)); + stat->record.num = 0; + spin_unlock_irqrestore(&stat->record.lock, flags); +} + +static ssize_t hybridswap_fail_record_show(char *buf) +{ + int i; + ssize_t size = 0; + struct hybridswap_fail_record_info record_info = { 0 }; + + hybridswap_fail_record_fetch(&record_info); + + size += scnprintf(buf + size, PAGE_SIZE, + "hybridswap_fail_record_num: %d\n", record_info.num); + for (i = 0; i < record_info.num; ++i) + size += scnprintf(buf + size, PAGE_SIZE - size, + "point[%u]time[%lld]taskname[%s]index[%u]eswapid[%d]\n", + record_info.record[i].point, + ktime_us_delta(ktime_get(), + record_info.record[i].time), + record_info.record[i].task_comm, + record_info.record[i].index, + record_info.record[i].eswapid); + + return size; +} + +ssize_t hybridswap_report_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return hybridswap_fail_record_show(buf); +} + +static inline meminfo_show(struct hybstatus *stat, char *buf, ssize_t len) +{ + unsigned long eswap_total_pages = 0, eswap_compressed_pages = 0; + unsigned long eswap_used_pages = 0; + unsigned long zram_total_pags, zram_used_pages, zram_compressed; + ssize_t size = 0; + + if (!stat || !buf || !len) + return 0; + + (void)hybridswap_stored_info(&eswap_total_pages, &eswap_compressed_pages); + eswap_used_pages = atomic64_read(&stat->stored_pages); +#ifdef CONFIG_HYBRIDSWAP_SWAPD + zram_total_pags = fetch_nr_zram_total(); +#else + zram_total_pags = 0; +#endif + zram_compressed = atomic64_read(&stat->zram_stored_size); + zram_used_pages = atomic64_read(&stat->zram_stored_pages); + + size += scnprintf(buf + size, len - size, "%-32s %12lu KB\n", + "EST:", eswap_total_pages << (PAGE_SHIFT - 10)); + size += scnprintf(buf + size, len - size, "%-32s %12lu KB\n", + "ESU_C:", eswap_compressed_pages << (PAGE_SHIFT - 10)); + size += scnprintf(buf + size, len - size, "%-32s %12lu KB\n", + "ESU_O:", eswap_used_pages << (PAGE_SHIFT - 10)); + size += scnprintf(buf + size, len - size, "%-32s %12lu KB\n", + "ZST:", zram_total_pags << (PAGE_SHIFT - 10)); + size += scnprintf(buf + size, len - size, "%-32s %12lu KB\n", + "ZSU_C:", zram_compressed >> 10); + size += scnprintf(buf + size, len - size, "%-32s %12lu KB\n", + "ZSU_O:", zram_used_pages << (PAGE_SHIFT - 10)); + + return size; +} + +ssize_t hybridswap_stat_snap_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t size = 0; + struct hybstatus *stat = NULL; + + if (!hybridswap_core_enabled()) + return 0; + + stat = hybridswap_fetch_stat_obj(); + if (unlikely(!stat)) { + hybp(HYB_INFO, "can't fetch stat obj!\n"); + return 0; + } + + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu\n", + "reclaimin_cnt:", atomic64_read(&stat->reclaimin_cnt)); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu KB\n", + "reclaimin_bytes:", atomic64_read(&stat->reclaimin_bytes) / SZ_1K); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu KB\n", + "reclaimin_real_load:", atomic64_read(&stat->reclaimin_real_load) / SZ_1K); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu KB\n", + "reclaimin_bytes_daily:", atomic64_read(&stat->reclaimin_bytes_daily) / SZ_1K); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu KB\n", + "reclaimin_pages:", atomic64_read(&stat->reclaimin_pages) * PAGE_SIZE / SZ_1K); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu\n", + "reclaimin_infight:", atomic64_read(&stat->reclaimin_infight)); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu\n", + "batchout_cnt:", atomic64_read(&stat->batchout_cnt)); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu KB\n", + "batchout_bytes:", atomic64_read(&stat->batchout_bytes) / SZ_1K); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu KB\n", + "batchout_real_load:", atomic64_read(&stat->batchout_real_load) / SZ_1K); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu KB\n", + "batchout_pages:", atomic64_read(&stat->batchout_pages) * PAGE_SIZE / SZ_1K); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu\n", + "batchout_inflight:", atomic64_read(&stat->batchout_inflight)); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu\n", + "fault_cnt:", atomic64_read(&stat->fault_cnt)); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu\n", + "hybridswap_fault_cnt:", atomic64_read(&stat->hybridswap_fault_cnt)); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu KB\n", + "reout_pages:", atomic64_read(&stat->reout_pages) * PAGE_SIZE / SZ_1K); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu KB\n", + "reout_bytes:", atomic64_read(&stat->reout_bytes) / SZ_1K); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu KB\n", + "zram_stored_pages:", atomic64_read(&stat->zram_stored_pages) * PAGE_SIZE / SZ_1K); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu KB\n", + "zram_stored_size:", atomic64_read(&stat->zram_stored_size) / SZ_1K); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu KB\n", + "stored_pages:", atomic64_read(&stat->stored_pages) * PAGE_SIZE / SZ_1K); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu KB\n", + "stored_size:", atomic64_read(&stat->stored_size) / SZ_1K); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu KB\n", + "reclain-batchout:", (atomic64_read(&stat->reclaimin_real_load) - + atomic64_read(&stat->batchout_real_load)) / SZ_1K); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12lld KB\n", + "reclain-batchout-stored:", + (atomic64_read(&stat->reclaimin_real_load) - + atomic64_read(&stat->batchout_real_load) - + atomic64_read(&stat->stored_size)) / SZ_1K); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12lld KB\n", + "dropped_eswap_size:", atomic64_read(&stat->dropped_eswap_size) / SZ_1K); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu\n", + "notify_free:", atomic64_read(&stat->notify_free)); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu\n", + "frag_cnt:", atomic64_read(&stat->frag_cnt)); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu\n", + "mcg_cnt:", atomic64_read(&stat->mcg_cnt)); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu\n", + "ext_cnt:", atomic64_read(&stat->eswap_cnt)); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu\n", + "miss_free:", atomic64_read(&stat->miss_free)); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu\n", + "memcgid_clear:", atomic64_read(&stat->memcgid_clear)); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu\n", + "skip_track_cnt:", atomic64_read(&stat->skip_track_cnt)); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu\n", + "null_memcg_skip_track_cnt:", + atomic64_read(&stat->null_memcg_skip_track_cnt)); + size += scnprintf(buf + size, PAGE_SIZE - size, "%-32s %12llu\n", + "used_swap_pages:", atomic64_read(&stat->used_swap_pages) * PAGE_SIZE / SZ_1K); + size += meminfo_show(stat, buf + size, PAGE_SIZE - size); + + return size; +} + +ssize_t hybridswap_meminfo_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hybstatus *stat = NULL; + + if (!hybridswap_core_enabled()) + return 0; + + stat = hybridswap_fetch_stat_obj(); + if (unlikely(!stat)) { + hybp(HYB_INFO, "can't fetch stat obj!\n"); + return 0; + } + + return meminfo_show(stat, buf, PAGE_SIZE); +} + +static void hybridswap_iostatus_bytes(struct hybridswap_io_req *req) +{ + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + + if (!stat || !req->page_cnt) + return; + + if (req->io_para.class == HYB_RECLAIM_IN) { + atomic64_add(req->page_cnt * PAGE_SIZE, &stat->reclaimin_bytes); + atomic64_add(req->page_cnt * PAGE_SIZE, &stat->reclaimin_bytes_daily); + atomic64_add(atomic64_read(&req->real_load), &stat->reclaimin_real_load); + atomic64_inc(&stat->reclaimin_cnt); + } else { + atomic64_add(req->page_cnt * PAGE_SIZE, &stat->batchout_bytes); + atomic64_inc(&stat->batchout_cnt); + } +} + +static void hybridswap_key_init(void) +{ + get_random_bytes(hybridswap_io_key, HYBRIDSWAP_KEY_SIZE); +} + +static void hybridswap_io_req_release(struct kref *ref) +{ + struct hybridswap_io_req *req = + container_of(ref, struct hybridswap_io_req, refcount); + + if (req->io_para.complete_notify && req->io_para.private) + req->io_para.complete_notify(req->io_para.private); + + kfree(req); +} + +static void hyb_sgm_free(struct hybridswap_io_req *req, + struct hyb_sgm *segment) +{ + int i; + + for (i = 0; i < segment->eswap_cnt; ++i) { + INIT_LIST_HEAD(&segment->io_entries_fifo[i]->list); + req->io_para.done_callback(segment->io_entries_fifo[i], -EIO, req); + } + kfree(segment); +} + +static void hybridswap_limit_doing(struct hybridswap_io_req *req) +{ + int ret; + + if (!req->limit_doing_flag) + return; + + if (atomic_read(&req->eswap_doing) >= HYBRIDSWAP_MAX_INFILGHT_NUM) { + do { + hybp(HYB_DEBUG, "wait doing start\n"); + ret = wait_event_timeout(req->io_wait, + atomic_read(&req->eswap_doing) < + HYBRIDSWAP_MAX_INFILGHT_NUM, + msecs_to_jiffies(100)); + } while (!ret); + } +} + +static void hybridswap_wait_io_finish(struct hybridswap_io_req *req) +{ + int ret; + unsigned int wait_time; + + if (!req->wait_io_finish_flag || !req->page_cnt) + return; + + if (req->io_para.class == HYB_FAULT_OUT) { + hybp(HYB_DEBUG, "fault out wait finish start\n"); + wait_for_completion_io_timeout(&req->io_end_flag, + MAX_SCHEDULE_TIMEOUT); + + return; + } + + wait_time = (req->io_para.class == HYB_RECLAIM_IN) ? + HYBRIDSWAP_WRITE_TIME : HYBRIDSWAP_READ_TIME; + + do { + hybp(HYB_DEBUG, "wait finish start\n"); + ret = wait_event_timeout(req->io_wait, + (!atomic_read(&req->eswap_doing)), + msecs_to_jiffies(wait_time)); + } while (!ret); +} + +static void hybridswap_doing_inc(struct hyb_sgm *segment) +{ + mutex_lock(&segment->req->refmutex); + kref_get(&segment->req->refcount); + mutex_unlock(&segment->req->refmutex); + atomic_add(segment->page_cnt, &segment->req->eswap_doing); +} + +static void hybridswap_doing_dec(struct hybridswap_io_req *req, + int num) +{ + if ((atomic_sub_return(num, &req->eswap_doing) < + HYBRIDSWAP_MAX_INFILGHT_NUM) && req->limit_doing_flag && + wq_has_sleeper(&req->io_wait)) + wake_up(&req->io_wait); +} + +static void hybridswap_io_end_wake_up(struct hybridswap_io_req *req) +{ + if (req->io_para.class == HYB_FAULT_OUT) { + complete(&req->io_end_flag); + return; + } + + if (wq_has_sleeper(&req->io_wait)) + wake_up(&req->io_wait); +} + +static void hybridswap_ioentry_proc(struct hyb_sgm *segment) +{ + int i; + struct hybridswap_io_req *req = segment->req; + struct hybridswap_key_point_record *record = req->io_para.record; + int page_num; + ktime_t callback_start; + unsigned long long callback_start_ravg_sum; + + for (i = 0; i < segment->eswap_cnt; ++i) { + INIT_LIST_HEAD(&segment->io_entries_fifo[i]->list); + page_num = segment->io_entries_fifo[i]->pages_sz; + hybp(HYB_DEBUG, "eswap_id[%d] %d page_num %d\n", + i, segment->io_entries_fifo[i]->eswapid, page_num); + callback_start = ktime_get(); + callback_start_ravg_sum = hybridswap_fetch_ravg_sum(); + if (req->io_para.done_callback) + req->io_para.done_callback(segment->io_entries_fifo[i], + 0, req); + hybperf_async_perf(record, HYB_CALL_BACK, + callback_start, callback_start_ravg_sum); + hybridswap_doing_dec(req, page_num); + } +} + +static void hybridswap_errio_record(enum hybridswap_fail_point point, + struct hybridswap_io_req *req, int eswapid) +{ + if (req->io_para.class == HYB_FAULT_OUT) + hybridswap_fail_record(point, 0, eswapid, + req->io_para.record->task_comm); +} + +static void hybridswap_iostatus_fail(enum hybridswap_class class) +{ + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + + if (!stat || (class >= HYB_CLASS_BUTT)) + return; + + atomic64_inc(&stat->io_fail_cnt[class]); +} + +static void hybridswap_errio_proc(struct hybridswap_io_req *req, + struct hyb_sgm *segment) +{ + hybp(HYB_ERR, "segment sector 0x%llx, eswap_cnt %d\n", + segment->segment_sector, segment->eswap_cnt); + hybp(HYB_ERR, "class %u, bio_result %u\n", + req->io_para.class, segment->bio_result); + hybridswap_iostatus_fail(req->io_para.class); + hybridswap_errio_record(HYB_FAULT_OUT_IO_FAIL, req, + segment->io_entries_fifo[0]->eswapid); + hybridswap_doing_dec(req, segment->page_cnt); + hybridswap_io_end_wake_up(req); + hyb_sgm_free(req, segment); + kref_put_mutex(&req->refcount, hybridswap_io_req_release, + &req->refmutex); +} + +static void hybridswap_io_end_work(struct work_struct *work) +{ + struct hyb_sgm *segment = + container_of(work, struct hyb_sgm, stopio_work); + struct hybridswap_io_req *req = segment->req; + struct hybridswap_key_point_record *record = req->io_para.record; + int old_nice = task_nice(current); + ktime_t work_start; + unsigned long long work_start_ravg_sum; + + if (unlikely(segment->bio_result)) { + hybridswap_errio_proc(req, segment); + return; + } + + hybp(HYB_DEBUG, "segment sector 0x%llx, eswap_cnt %d passed\n", + segment->segment_sector, segment->eswap_cnt); + hybp(HYB_DEBUG, "class %u, bio_result %u passed\n", + req->io_para.class, segment->bio_result); + + set_user_nice(current, req->nice); + + hybperf_async_perf(record, HYB_SCHED_WORK, + segment->time.end_io, 0); + work_start = ktime_get(); + work_start_ravg_sum = hybridswap_fetch_ravg_sum(); + + hybridswap_ioentry_proc(segment); + + hybperf_async_perf(record, HYB_END_WORK, work_start, + work_start_ravg_sum); + + hybridswap_io_end_wake_up(req); + + kref_put_mutex(&req->refcount, hybridswap_io_req_release, + &req->refmutex); + kfree(segment); + + set_user_nice(current, old_nice); +} + +static void hybridswap_end_io(struct bio *bio) +{ + struct hyb_sgm *segment = bio->bi_private; + struct hybridswap_io_req *req = NULL; + struct workqueue_struct *workqueue = NULL; + struct hybridswap_key_point_record *record = NULL; + + if (unlikely(!segment || !(segment->req))) { + hybp(HYB_ERR, "segment or req null\n"); + bio_put(bio); + + return; + } + + req = segment->req; + record = req->io_para.record; + + hybperf_async_perf(record, HYB_END_IO, + segment->time.submit_bio, 0); + + workqueue = (req->io_para.class == HYB_RECLAIM_IN) ? + hybridswap_proc_write_workqueue : hybridswap_proc_read_workqueue; + segment->time.end_io = ktime_get(); + segment->bio_result = bio->bi_status; + + queue_work(workqueue, &segment->stopio_work); + bio_put(bio); +} + +static bool hybridswap_eswap_merge_back( + struct hyb_sgm *segment, + struct hybridswap_entry *ioentry) +{ + struct hybridswap_entry *tail_ioentry = + list_last_entry(&segment->io_entries, + struct hybridswap_entry, list); + + return ((tail_ioentry->addr + + tail_ioentry->pages_sz * HYBRIDSWAP_PAGE_SIZE_SECTOR) == + ioentry->addr); +} + +static bool hybridswap_eswap_merge_front( + struct hyb_sgm *segment, + struct hybridswap_entry *ioentry) +{ + struct hybridswap_entry *head_ioentry = + list_first_entry(&segment->io_entries, + struct hybridswap_entry, list); + + return (head_ioentry->addr == + (ioentry->addr + + ioentry->pages_sz * HYBRIDSWAP_PAGE_SIZE_SECTOR)); +} + +static bool hybridswap_eswap_merge(struct hybridswap_io_req *req, + struct hybridswap_entry *ioentry) +{ + struct hyb_sgm *segment = req->segment; + + if (segment == NULL) + return false; + + if ((segment->page_cnt + ioentry->pages_sz) > BIO_MAX_PAGES) + return false; + + if (hybridswap_eswap_merge_front(segment, ioentry)) { + list_add(&ioentry->list, &segment->io_entries); + segment->io_entries_fifo[segment->eswap_cnt++] = ioentry; + segment->segment_sector = ioentry->addr; + segment->page_cnt += ioentry->pages_sz; + return true; + } + + if (hybridswap_eswap_merge_back(segment, ioentry)) { + list_add_tail(&ioentry->list, &segment->io_entries); + segment->io_entries_fifo[segment->eswap_cnt++] = ioentry; + segment->page_cnt += ioentry->pages_sz; + return true; + } + + return false; +} + +static struct bio *hybridswap_bio_alloc(enum hybridswap_class class) +{ + gfp_t gfp = (class != HYB_RECLAIM_IN) ? GFP_ATOMIC : GFP_NOIO; + struct bio *bio = bio_alloc(gfp, BIO_MAX_PAGES); + + if (!bio && (class == HYB_FAULT_OUT)) + bio = bio_alloc(GFP_NOIO, BIO_MAX_PAGES); + + return bio; +} + +static int hybridswap_bio_add_page(struct bio *bio, + struct hyb_sgm *segment) +{ + int i; + int k = 0; + struct hybridswap_entry *ioentry = NULL; + struct hybridswap_entry *tmp = NULL; + + list_for_each_entry_safe(ioentry, tmp, &segment->io_entries, list) { + for (i = 0; i < ioentry->pages_sz; i++) { + ioentry->dest_pages[i]->index = + bio->bi_iter.bi_sector + k; + if (unlikely(!bio_add_page(bio, + ioentry->dest_pages[i], PAGE_SIZE, 0))) { + return -EIO; + } + k += HYBRIDSWAP_PAGE_SIZE_SECTOR; + } + } + + return 0; +} + +static void hybridswap_set_bio_opf(struct bio *bio, + struct hyb_sgm *segment) +{ + if (segment->req->io_para.class == HYB_RECLAIM_IN) { + bio->bi_opf |= REQ_BACKGROUND; + return; + } + + bio->bi_opf |= REQ_SYNC; +} + +int hybridswap_submit_bio(struct hyb_sgm *segment) +{ + unsigned int op = + (segment->req->io_para.class == HYB_RECLAIM_IN) ? + REQ_OP_WRITE : REQ_OP_READ; + struct hybridswap_entry *head_ioentry = + list_first_entry(&segment->io_entries, + struct hybridswap_entry, list); + struct hybridswap_key_point_record *record = + segment->req->io_para.record; + struct bio *bio = NULL; + + hybperfiowrkstart(record, HYB_BIO_ALLOC); + bio = hybridswap_bio_alloc(segment->req->io_para.class); + hybperfiowrkend(record, HYB_BIO_ALLOC); + if (unlikely(!bio)) { + hybp(HYB_ERR, "bio is null.\n"); + hybridswap_errio_record(HYB_FAULT_OUT_BIO_ALLOC_FAIL, + segment->req, segment->io_entries_fifo[0]->eswapid); + + return -ENOMEM; + } + + bio->bi_iter.bi_sector = segment->segment_sector; + bio_set_dev(bio, segment->req->io_para.bdev); + bio->bi_private = segment; + bio_set_op_attrs(bio, op, 0); + bio->bi_end_io = hybridswap_end_io; + hybridswap_set_bio_opf(bio, segment); + + if (unlikely(hybridswap_bio_add_page(bio, segment))) { + bio_put(bio); + hybp(HYB_ERR, "bio_add_page fail\n"); + hybridswap_errio_record(HYB_FAULT_OUT_BIO_ADD_FAIL, + segment->req, segment->io_entries_fifo[0]->eswapid); + + return -EIO; + } + + hybridswap_doing_inc(segment); + hybp(HYB_DEBUG, "submit bio sector %llu eswapid %d\n", + segment->segment_sector, head_ioentry->eswapid); + hybp(HYB_DEBUG, "eswap_cnt %d class %u\n", + segment->eswap_cnt, segment->req->io_para.class); + + segment->req->page_cnt += segment->page_cnt; + segment->req->segment_cnt++; + segment->time.submit_bio = ktime_get(); + + hybperfiowrkstart(record, HYB_SUBMIT_BIO); + submit_bio(bio); + hybperfiowrkend(record, HYB_SUBMIT_BIO); + + return 0; +} + +static int hybridswap_new_segment_init(struct hybridswap_io_req *req, + struct hybridswap_entry *ioentry) +{ + gfp_t gfp = (req->io_para.class != HYB_RECLAIM_IN) ? + GFP_ATOMIC : GFP_NOIO; + struct hyb_sgm *segment = NULL; + struct hybridswap_key_point_record *record = req->io_para.record; + + hybperfiowrkstart(record, HYB_SEGMENT_ALLOC); + segment = kzalloc(sizeof(struct hyb_sgm), gfp); + if (!segment && (req->io_para.class == HYB_FAULT_OUT)) + segment = kzalloc(sizeof(struct hyb_sgm), GFP_NOIO); + hybperfiowrkend(record, HYB_SEGMENT_ALLOC); + if (unlikely(!segment)) { + hybridswap_errio_record(HYB_FAULT_OUT_SEGMENT_ALLOC_FAIL, + req, ioentry->eswapid); + + return -ENOMEM; + } + + segment->req = req; + INIT_LIST_HEAD(&segment->io_entries); + list_add_tail(&ioentry->list, &segment->io_entries); + segment->io_entries_fifo[segment->eswap_cnt++] = ioentry; + segment->page_cnt = ioentry->pages_sz; + INIT_WORK(&segment->stopio_work, hybridswap_io_end_work); + segment->segment_sector = ioentry->addr; + req->segment = segment; + + return 0; +} + +static int hybridswap_io_submit(struct hybridswap_io_req *req, + bool merge_flag) +{ + int ret; + struct hyb_sgm *segment = req->segment; + + if (!segment || ((merge_flag) && (segment->page_cnt < BIO_MAX_PAGES))) + return 0; + + hybridswap_limit_doing(req); + + ret = hybridswap_submit_bio(segment); + if (unlikely(ret)) { + hybp(HYB_WARN, "submit bio failed, ret %d\n", ret); + hyb_sgm_free(req, segment); + } + req->segment = NULL; + + return ret; +} + +static bool hybridswap_check_io_para_err(struct hybridswap_io *io_para) +{ + if (unlikely(!io_para)) { + hybp(HYB_ERR, "io_para null\n"); + + return true; + } + + if (unlikely(!io_para->bdev || + (io_para->class >= HYB_CLASS_BUTT))) { + hybp(HYB_ERR, "io_para err, class %u\n", + io_para->class); + + return true; + } + + if (unlikely(!io_para->done_callback)) { + hybp(HYB_ERR, "done_callback err\n"); + + return true; + } + + return false; +} + +static bool hybridswap_check_entry_err( + struct hybridswap_entry *ioentry) +{ + int i; + + if (unlikely(!ioentry)) { + hybp(HYB_ERR, "ioentry null\n"); + + return true; + } + + if (unlikely((!ioentry->dest_pages) || + (ioentry->eswapid < 0) || + (ioentry->pages_sz > BIO_MAX_PAGES) || + (ioentry->pages_sz <= 0))) { + hybp(HYB_ERR, "eswapid %d, page_sz %d\n", ioentry->eswapid, + ioentry->pages_sz); + + return true; + } + + for (i = 0; i < ioentry->pages_sz; ++i) { + if (!ioentry->dest_pages[i]) { + hybp(HYB_ERR, "dest_pages[%d] is null\n", i); + return true; + } + } + + return false; +} + +static int hybridswap_io_eswapent(void *iohandle, + struct hybridswap_entry *ioentry) +{ + int ret; + struct hybridswap_io_req *req = (struct hybridswap_io_req *)iohandle; + + if (unlikely(hybridswap_check_entry_err(ioentry))) { + hybridswap_errio_record(HYB_FAULT_OUT_IO_ENTRY_PARA_FAIL, + req, ioentry ? ioentry->eswapid : -EINVAL); + req->io_para.done_callback(ioentry, -EIO, req); + + return -EFAULT; + } + + hybp(HYB_DEBUG, "eswap id %d, pages_sz %d, addr %llx\n", + ioentry->eswapid, ioentry->pages_sz, + ioentry->addr); + + if (hybridswap_eswap_merge(req, ioentry)) + return hybridswap_io_submit(req, true); + + ret = hybridswap_io_submit(req, false); + if (unlikely(ret)) { + hybp(HYB_ERR, "submit fail %d\n", ret); + req->io_para.done_callback(ioentry, -EIO, req); + + return ret; + } + + ret = hybridswap_new_segment_init(req, ioentry); + if (unlikely(ret)) { + hybp(HYB_ERR, "hybridswap_new_segment_init fail %d\n", ret); + req->io_para.done_callback(ioentry, -EIO, req); + + return ret; + } + + return 0; +} + +int hyb_io_work_begin(void) +{ + if (hyb_io_work_begin_flag) + return 0; + + hybridswap_proc_read_workqueue = alloc_workqueue("proc_hybridswap_read", + WQ_HIGHPRI | WQ_UNBOUND, 0); + if (unlikely(!hybridswap_proc_read_workqueue)) + return -EFAULT; + + hybridswap_proc_write_workqueue = alloc_workqueue("proc_hybridswap_write", + WQ_CPU_INTENSIVE, 0); + if (unlikely(!hybridswap_proc_write_workqueue)) { + destroy_workqueue(hybridswap_proc_read_workqueue); + + return -EFAULT; + } + + hybridswap_key_init(); + + hyb_io_work_begin_flag = true; + + return 0; +} + +void *hybridswap_plug_start(struct hybridswap_io *io_para) +{ + gfp_t gfp; + struct hybridswap_io_req *req = NULL; + + if (unlikely(hybridswap_check_io_para_err(io_para))) + return NULL; + + gfp = (io_para->class != HYB_RECLAIM_IN) ? + GFP_ATOMIC : GFP_NOIO; + req = kzalloc(sizeof(struct hybridswap_io_req), gfp); + if (!req && (io_para->class == HYB_FAULT_OUT)) + req = kzalloc(sizeof(struct hybridswap_io_req), GFP_NOIO); + + if (unlikely(!req)) { + hybp(HYB_ERR, "io_req null\n"); + + return NULL; + } + + kref_init(&req->refcount); + mutex_init(&req->refmutex); + atomic_set(&req->eswap_doing, 0); + init_waitqueue_head(&req->io_wait); + req->io_para.bdev = io_para->bdev; + req->io_para.class = io_para->class; + req->io_para.done_callback = io_para->done_callback; + req->io_para.complete_notify = io_para->complete_notify; + req->io_para.private = io_para->private; + req->io_para.record = io_para->record; + req->limit_doing_flag = + (io_para->class == HYB_RECLAIM_IN) || + (io_para->class == HYB_PRE_OUT); + req->wait_io_finish_flag = + (io_para->class == HYB_RECLAIM_IN) || + (io_para->class == HYB_FAULT_OUT); + req->nice = task_nice(current); + init_completion(&req->io_end_flag); + + return (void *)req; +} + +int hybridswap_read_eswap(void *iohandle, + struct hybridswap_entry *ioentry) +{ + return hybridswap_io_eswapent(iohandle, ioentry); +} + +int hybridswap_write_eswap(void *iohandle, + struct hybridswap_entry *ioentry) +{ + return hybridswap_io_eswapent(iohandle, ioentry); +} + +int hybridswap_plug_finish(void *iohandle) +{ + int ret; + struct hybridswap_io_req *req = (struct hybridswap_io_req *)iohandle; + + hybperfiowrkstart(req->io_para.record, HYB_IO_ESWAP); + ret = hybridswap_io_submit(req, false); + if (unlikely(ret)) + hybp(HYB_ERR, "submit fail %d\n", ret); + + hybperfiowrkend(req->io_para.record, HYB_IO_ESWAP); + hybridswap_wait_io_finish(req); + hybperfiowrkpoint(req->io_para.record, HYB_WAKE_UP); + + hybridswap_iostatus_bytes(req); + hybperf_io_stat(req->io_para.record, req->page_cnt, + req->segment_cnt); + + kref_put_mutex(&req->refcount, hybridswap_io_req_release, + &req->refmutex); + + hybp(HYB_DEBUG, "io schedule finish succ\n"); + + return ret; +} + +static void hybridswap_dump_point_lat( + struct hybridswap_key_point_record *record, ktime_t start) +{ + int i; + + for (i = 0; i < HYB_KYE_POINT_BUTT; ++i) { + if (!record->key_point[i].record_cnt) + continue; + + hybp(HYB_ERR, + "%s diff %lld cnt %u end %u lat %lld ravg_sum %llu\n", + key_point_name[i], + ktime_us_delta(record->key_point[i].first_time, start), + record->key_point[i].record_cnt, + record->key_point[i].end_cnt, + record->key_point[i].proc_total_time, + record->key_point[i].proc_ravg_sum); + } +} + +static void hybridswap_dump_no_record_point( + struct hybridswap_key_point_record *record, char *log, + unsigned int *count) +{ + int i; + unsigned int point = 0; + + for (i = 0; i < HYB_KYE_POINT_BUTT; ++i) + if (record->key_point[i].record_cnt) + point = i; + + point++; + if (point < HYB_KYE_POINT_BUTT) + *count += snprintf(log + *count, + (size_t)(DUMP_BUF_LEN - *count), + " no_record_point %s", key_point_name[point]); + else + *count += snprintf(log + *count, + (size_t)(DUMP_BUF_LEN - *count), " all_point_record"); +} + +static long long hybridswap_calc_speed(s64 page_cnt, s64 time) +{ + s64 size; + + if (!page_cnt) + return 0; + + size = page_cnt * PAGE_SIZE * BITS_PER_BYTE; + if (time) + return size * USEC_PER_SEC / time; + else + return S64_MAX; +} + +static void hybridswap_dump_lat( + struct hybridswap_key_point_record *record, ktime_t curr_time, + bool perf_end_flag) +{ + char log[DUMP_BUF_LEN] = { 0 }; + unsigned int count = 0; + ktime_t start; + s64 total_time; + + start = record->key_point[HYB_START].first_time; + total_time = ktime_us_delta(curr_time, start); + count += snprintf(log + count, + (size_t)(DUMP_BUF_LEN - count), + "totaltime(us) %lld class %u task %s nice %d", + total_time, record->class, record->task_comm, record->nice); + + if (perf_end_flag) + count += snprintf(log + count, (size_t)(DUMP_BUF_LEN - count), + " page %d segment %d speed(bps) %lld level %lu", + record->page_cnt, record->segment_cnt, + hybridswap_calc_speed(record->page_cnt, total_time), + record->warn_level); + else + count += snprintf(log + count, (size_t)(DUMP_BUF_LEN - count), + " state %c", task_state_to_char(record->task)); + + hybridswap_dump_no_record_point(record, log, &count); + + hybp(HYB_ERR, "perf end flag %u %s\n", perf_end_flag, log); + hybridswap_dump_point_lat(record, start); + dump_stack(); +} + +static unsigned long hybperf_warn_level( + enum hybridswap_class class) +{ + if (unlikely(class >= HYB_CLASS_BUTT)) + return 0; + + return warn_level[class]; +} + +void hybperf_warning(struct timer_list *t) +{ + struct hybridswap_key_point_record *record = + from_timer(record, t, lat_monitor); + static unsigned long last_dumpiowrkjiffies = 0; + + if (!record->warn_level) + return; + + if (jiffies_to_msecs(jiffies - last_dumpiowrkjiffies) <= 60000) + return; + + hybridswap_dump_lat(record, ktime_get(), false); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + if (likely(record->task)) + sched_show_task(record->task); +#endif + last_dumpiowrkjiffies = jiffies; + record->warn_level <<= 2; + record->timeout_flag = true; + mod_timer(&record->lat_monitor, + jiffies + msecs_to_jiffies(record->warn_level)); +} + +static void hybperf_init_monitor( + struct hybridswap_key_point_record *record, + enum hybridswap_class class) +{ + record->warn_level = hybperf_warn_level(class); + + if (!record->warn_level) + return; + + record->task = current; + get_task_struct(record->task); + timer_setup(&record->lat_monitor, hybperf_warning, 0); + mod_timer(&record->lat_monitor, + jiffies + msecs_to_jiffies(record->warn_level)); +} + +static void hybperf_stop_monitor( + struct hybridswap_key_point_record *record) +{ + if (!record->warn_level) + return; + + del_timer_sync(&record->lat_monitor); + put_task_struct(record->task); +} + +static void hybperf_init(struct hybridswap_key_point_record *record, + enum hybridswap_class class) +{ + int i; + + for (i = 0; i < HYB_KYE_POINT_BUTT; ++i) + spin_lock_init(&record->key_point[i].time_lock); + + record->nice = task_nice(current); + record->class = class; + get_task_comm(record->task_comm, current); + hybperf_init_monitor(record, class); +} + +void hybperf_start_proc( + struct hybridswap_key_point_record *record, + enum hybridswap_key_point type, ktime_t curr_time, + unsigned long long current_ravg_sum) +{ + struct hybridswap_key_point_info *key_point = + &record->key_point[type]; + + if (!key_point->record_cnt) + key_point->first_time = curr_time; + + key_point->record_cnt++; + key_point->last_time = curr_time; + key_point->last_ravg_sum = current_ravg_sum; +} + +void hybperf_end_proc( + struct hybridswap_key_point_record *record, + enum hybridswap_key_point type, ktime_t curr_time, + unsigned long long current_ravg_sum) +{ + struct hybridswap_key_point_info *key_point = + &record->key_point[type]; + s64 diff_time = ktime_us_delta(curr_time, key_point->last_time); + + key_point->proc_total_time += diff_time; + if (diff_time > key_point->proc_max_time) + key_point->proc_max_time = diff_time; + + key_point->proc_ravg_sum += current_ravg_sum - + key_point->last_ravg_sum; + key_point->end_cnt++; + key_point->last_time = curr_time; + key_point->last_ravg_sum = current_ravg_sum; +} + +void hybperf_async_perf( + struct hybridswap_key_point_record *record, + enum hybridswap_key_point type, ktime_t start, + unsigned long long start_ravg_sum) +{ + unsigned long long current_ravg_sum = ((type == HYB_CALL_BACK) || + (type == HYB_END_WORK)) ? hybridswap_fetch_ravg_sum() : 0; + unsigned long flags; + + spin_lock_irqsave(&record->key_point[type].time_lock, flags); + hybperf_start_proc(record, type, start, start_ravg_sum); + hybperf_end_proc(record, type, ktime_get(), + current_ravg_sum); + spin_unlock_irqrestore(&record->key_point[type].time_lock, flags); +} + +void hybperfiowrkpoint( + struct hybridswap_key_point_record *record, + enum hybridswap_key_point type) +{ + hybperf_start_proc(record, type, ktime_get(), + hybridswap_fetch_ravg_sum()); + record->key_point[type].end_cnt++; +} + +void hybperf_start( + struct hybridswap_key_point_record *record, + ktime_t stsrt, unsigned long long start_ravg_sum, + enum hybridswap_class class) +{ + hybperf_init(record, class); + hybperf_start_proc(record, HYB_START, stsrt, + start_ravg_sum); + record->key_point[HYB_START].end_cnt++; +} + +void hybperfiowrkstat( + struct hybridswap_key_point_record *record) +{ + int task_is_fg = 0; + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + s64 curr_lat; + s64 timeout_value[HYB_CLASS_BUTT] = { + 2000000, 100000, 500000, 2000000 + }; + + if (!stat || (record->class >= HYB_CLASS_BUTT)) + return; + + curr_lat = ktime_us_delta(record->key_point[HYB_DONE].first_time, + record->key_point[HYB_START].first_time); + atomic64_add(curr_lat, &stat->lat[record->class].total_lat); + if (curr_lat > atomic64_read(&stat->lat[record->class].max_lat)) + atomic64_set(&stat->lat[record->class].max_lat, curr_lat); + if (curr_lat > timeout_value[record->class]) + atomic64_inc(&stat->lat[record->class].timeout_cnt); + if (record->class == HYB_FAULT_OUT) { + if (curr_lat <= timeout_value[HYB_FAULT_OUT]) + return; +#ifdef CONFIG_FG_TASK_UID + task_is_fg = current_is_fg() ? 1 : 0; +#endif + if (curr_lat > 500000) + atomic64_inc(&stat->fault_stat[task_is_fg].timeout_500ms_cnt); + else if (curr_lat > 100000) + atomic64_inc(&stat->fault_stat[task_is_fg].timeout_100ms_cnt); + hybp(HYB_INFO, "task %s:%d fault out timeout us %llu fg %d\n", + current->comm, current->pid, curr_lat, task_is_fg); + } +} + +void hybperf_end(struct hybridswap_key_point_record *record) +{ + int loglevel; + + hybperf_stop_monitor(record); + hybperfiowrkpoint(record, HYB_DONE); + hybperfiowrkstat(record); + + loglevel = record->timeout_flag ? HYB_ERR : HYB_DEBUG; + if (loglevel > hybridswap_loglevel()) + return; + + hybridswap_dump_lat(record, + record->key_point[HYB_DONE].first_time, true); +} + +void hybperfiowrkstart( + struct hybridswap_key_point_record *record, + enum hybridswap_key_point type) +{ + hybperf_start_proc(record, type, ktime_get(), + hybridswap_fetch_ravg_sum()); +} + +void hybperfiowrkend( + struct hybridswap_key_point_record *record, + enum hybridswap_key_point type) +{ + hybperf_end_proc(record, type, ktime_get(), + hybridswap_fetch_ravg_sum()); +} + +void hybperf_io_stat( + struct hybridswap_key_point_record *record, int page_cnt, + int segment_cnt) +{ + record->page_cnt = page_cnt; + record->segment_cnt = segment_cnt; +} + +static struct io_eswapent *alloc_io_eswapent(struct hybridswap_page_pool *pool, + bool fast, bool nofail) +{ + int i; + struct io_eswapent *io_eswap = hybridswap_malloc(sizeof(struct io_eswapent), + fast, nofail); + + if (!io_eswap) { + hybp(HYB_ERR, "alloc io_eswap failed\n"); + return NULL; + } + + io_eswap->eswapid = -EINVAL; + io_eswap->pool = pool; + for (i = 0; i < (int)ESWAP_PG_CNT; i++) { + io_eswap->pages[i] = hybridswap_alloc_page(pool, GFP_ATOMIC, + fast, nofail); + if (!io_eswap->pages[i]) { + hybp(HYB_ERR, "alloc page[%d] failed\n", i); + goto page_free; + } + } + return io_eswap; +page_free: + for (i = 0; i < (int)ESWAP_PG_CNT; i++) + if (io_eswap->pages[i]) + hybridswap_page_recycle(io_eswap->pages[i], pool); + hybridswap_free(io_eswap); + + return NULL; +} + +static void discard_io_eswapent(struct io_eswapent *io_eswap, unsigned int op) +{ + struct zram *zram = NULL; + int i; + + if (!io_eswap) { + hybp(HYB_ERR, "NULL io_eswap\n"); + return; + } + if (!io_eswap->mcg) + zram = io_eswap->zram; + else + zram = MEMCGRP_ITEM(io_eswap->mcg, zram); + if (!zram) { + hybp(HYB_ERR, "NULL zram\n"); + goto out; + } + for (i = 0; i < (int)ESWAP_PG_CNT; i++) + if (io_eswap->pages[i]) + hybridswap_page_recycle(io_eswap->pages[i], io_eswap->pool); + if (io_eswap->eswapid < 0) + goto out; + hybp(HYB_DEBUG, "eswap = %d, op = %d\n", io_eswap->eswapid, op); + if (op == REQ_OP_READ) { + put_eswap(zram->infos, io_eswap->eswapid); + goto out; + } + for (i = 0; i < io_eswap->cnt; i++) { + u32 index = io_eswap->index[i]; + + zram_slot_lock(zram, index); + if (io_eswap->mcg) + swap_sorted_list_add_tail(zram, index, io_eswap->mcg); + zram_clear_flag(zram, index, ZRAM_UNDER_WB); + zram_slot_unlock(zram, index); + } + hybridswap_free_eswap(zram->infos, io_eswap->eswapid); +out: + hybridswap_free(io_eswap); +} + +static void copy_to_pages(u8 *src, struct page *pages[], + unsigned long eswpentry, int size) +{ + u8 *dst = NULL; + int pg_id = esentry_pgid(eswpentry); + int offset = esentry_pgoff(eswpentry); + + if (!src) { + hybp(HYB_ERR, "NULL src\n"); + return; + } + if (!pages) { + hybp(HYB_ERR, "NULL pages\n"); + return; + } + if (size < 0 || size > (int)PAGE_SIZE) { + hybp(HYB_ERR, "size = %d invalid\n", size); + return; + } + dst = page_to_virt(pages[pg_id]); + if (offset + size <= (int)PAGE_SIZE) { + memcpy(dst + offset, src, size); + return; + } + if (pg_id == ESWAP_PG_CNT - 1) { + hybp(HYB_ERR, "eswap overflow, addr = %lx, size = %d\n", + eswpentry, size); + return; + } + memcpy(dst + offset, src, PAGE_SIZE - offset); + dst = page_to_virt(pages[pg_id + 1]); + memcpy(dst, src + PAGE_SIZE - offset, offset + size - PAGE_SIZE); +} + +static void copy_from_pages(u8 *dst, struct page *pages[], + unsigned long eswpentry, int size) +{ + u8 *src = NULL; + int pg_id = esentry_pgid(eswpentry); + int offset = esentry_pgoff(eswpentry); + + if (!dst) { + hybp(HYB_ERR, "NULL dst\n"); + return; + } + if (!pages) { + hybp(HYB_ERR, "NULL pages\n"); + return; + } + if (size < 0 || size > (int)PAGE_SIZE) { + hybp(HYB_ERR, "size = %d invalid\n", size); + return; + } + + src = page_to_virt(pages[pg_id]); + if (offset + size <= (int)PAGE_SIZE) { + memcpy(dst, src + offset, size); + return; + } + if (pg_id == ESWAP_PG_CNT - 1) { + hybp(HYB_ERR, "eswap overflow, addr = %lx, size = %d\n", + eswpentry, size); + return; + } + memcpy(dst, src + offset, PAGE_SIZE - offset); + src = page_to_virt(pages[pg_id + 1]); + memcpy(dst + PAGE_SIZE - offset, src, offset + size - PAGE_SIZE); +} + +static bool zram_test_skip(struct zram *zram, u32 index, struct mem_cgroup *mcg) +{ + if (zram_test_flag(zram, index, ZRAM_WB)) + return true; + if (zram_test_flag(zram, index, ZRAM_UNDER_WB)) + return true; + if (zram_test_flag(zram, index, ZRAM_BATCHING_OUT)) + return true; + if (zram_test_flag(zram, index, ZRAM_SAME)) + return true; + if (mcg != zram_fetch_mcg(zram, index)) + return true; + if (!zram_get_obj_size(zram, index)) + return true; + + return false; +} + +static bool zram_test_overwrite(struct zram *zram, u32 index, int eswapid) +{ + if (!zram_test_flag(zram, index, ZRAM_WB)) + return true; + if (eswapid != esentry_extid(zram_get_handle(zram, index))) + return true; + + return false; +} + +static void update_size_info(struct zram *zram, u32 index) +{ + struct hybstatus *stat; + int size = zram_get_obj_size(zram, index); + struct mem_cgroup *mcg; + memcg_hybs_t *hybs; + int eswapid; + + if (!zram_test_flag(zram, index, ZRAM_IN_BD)) + return; + + eswapid = esentry_extid(zram_get_handle(zram, index)); + hybp(HYB_DEBUG, "eswapid %d index %d\n", eswapid, index); + + if (eswapid >= 0 && eswapid < zram->infos->nr_es) + atomic_dec(&zram->infos->eswap_stored_pages[eswapid]); + else { + hybp(HYB_ERR, "eswap = %d invalid\n", eswapid); + eswapid = -1; + } + + stat = hybridswap_fetch_stat_obj(); + if (stat) { + atomic64_add(size, &stat->dropped_eswap_size); + atomic64_sub(size, &stat->stored_size); + atomic64_dec(&stat->stored_pages); + } else + hybp(HYB_ERR, "NULL stat\n"); + + mcg = zram_fetch_mcg(zram, index); + if (mcg) { + hybs = MEMCGRP_ITEM_DATA(mcg); + + if (hybs) { + atomic64_sub(size, &hybs->hybridswap_stored_size); + atomic64_dec(&hybs->hybridswap_stored_pages); + } else + hybp(HYB_ERR, "NULL hybs\n"); + } else + hybp(HYB_ERR, "NULL mcg\n"); + zram_clear_flag(zram, index, ZRAM_IN_BD); +} + +static void move_to_hybridswap(struct zram *zram, u32 index, + unsigned long eswpentry, struct mem_cgroup *mcg) +{ + int size; + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + + if (!stat) { + hybp(HYB_ERR, "NULL stat\n"); + return; + } + if (!zram) { + hybp(HYB_ERR, "NULL zram\n"); + return; + } + if (index >= (u32)zram->infos->total_objects) { + hybp(HYB_ERR, "index = %d invalid\n", index); + return; + } + if (!mcg) { + hybp(HYB_ERR, "NULL mcg\n"); + return; + } + + size = zram_get_obj_size(zram, index); + + zram_clear_flag(zram, index, ZRAM_UNDER_WB); + + zs_free(zram->mem_pool, zram_get_handle(zram, index)); + atomic64_sub(size, &zram->stats.compr_data_size); + atomic64_dec(&zram->stats.pages_stored); + + zram_set_mcg(zram, index, mcg->id.id); + zram_set_flag(zram, index, ZRAM_IN_BD); + zram_set_flag(zram, index, ZRAM_WB); + zram_set_obj_size(zram, index, size); + if (size == PAGE_SIZE) + zram_set_flag(zram, index, ZRAM_HUGE); + zram_set_handle(zram, index, eswpentry); + swap_maps_insert(zram, index); + + atomic64_add(size, &stat->stored_size); + atomic64_add(size, &MEMCGRP_ITEM(mcg, hybridswap_stored_size)); + atomic64_inc(&stat->stored_pages); + atomic_inc(&zram->infos->eswap_stored_pages[esentry_extid(eswpentry)]); + atomic64_inc(&MEMCGRP_ITEM(mcg, hybridswap_stored_pages)); +} + +static void __move_to_zram(struct zram *zram, u32 index, unsigned long handle, + struct io_eswapent *io_eswap) +{ + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + struct mem_cgroup *mcg = io_eswap->mcg; + int size = zram_get_obj_size(zram, index); + + if (!stat) { + hybp(HYB_ERR, "NULL stat\n"); + return; + } + + zram_slot_lock(zram, index); + if (zram_test_overwrite(zram, index, io_eswap->eswapid)) { + zram_slot_unlock(zram, index); + zs_free(zram->mem_pool, handle); + return; + } + swap_maps_destroy(zram, index); + zram_set_handle(zram, index, handle); + zram_clear_flag(zram, index, ZRAM_WB); + // Moto: add to head to avoid be swapped out soon. + if (mcg) + swap_sorted_list_add(zram, index, mcg); + zram_set_flag(zram, index, ZRAM_FROM_HYBRIDSWAP); + atomic64_add(size, &zram->stats.compr_data_size); + atomic64_inc(&zram->stats.pages_stored); + zram_clear_flag(zram, index, ZRAM_IN_BD); + zram_slot_unlock(zram, index); + + atomic64_inc(&stat->batchout_pages); + atomic64_sub(size, &stat->stored_size); + atomic64_dec(&stat->stored_pages); + atomic64_add(size, &stat->batchout_real_load); + atomic_dec(&zram->infos->eswap_stored_pages[io_eswap->eswapid]); + if (mcg) { + atomic64_sub(size, &MEMCGRP_ITEM(mcg, hybridswap_stored_size)); + atomic64_dec(&MEMCGRP_ITEM(mcg, hybridswap_stored_pages)); + } +} + +static int move_to_zram(struct zram *zram, u32 index, struct io_eswapent *io_eswap) +{ + unsigned long handle, eswpentry; +// struct mem_cgroup *mcg = NULL; + int size, i; + u8 *dst = NULL; + + if (!zram) { + hybp(HYB_ERR, "NULL zram\n"); + return -EINVAL; + } + if (index >= (u32)zram->infos->total_objects) { + hybp(HYB_ERR, "index = %d invalid\n", index); + return -EINVAL; + } + if (!io_eswap) { + hybp(HYB_ERR, "NULL io_eswap\n"); + return -EINVAL; + } + +// mcg = io_eswap->mcg; + zram_slot_lock(zram, index); + eswpentry = zram_get_handle(zram, index); + if (zram_test_overwrite(zram, index, io_eswap->eswapid)) { + zram_slot_unlock(zram, index); + return 0; + } + size = zram_get_obj_size(zram, index); + zram_slot_unlock(zram, index); + + for (i = esentry_pgid(eswpentry) - 1; i >= 0 && io_eswap->pages[i]; i--) { + hybridswap_page_recycle(io_eswap->pages[i], io_eswap->pool); + io_eswap->pages[i] = NULL; + } + handle = hybridswap_zsmalloc(zram->mem_pool, size, io_eswap->pool); + if (!handle) { + hybp(HYB_ERR, "alloc handle failed, size = %d\n", size); + return -ENOMEM; + } + dst = zs_map_object(zram->mem_pool, handle, ZS_MM_WO); + copy_from_pages(dst, io_eswap->pages, eswpentry, size); + zs_unmap_object(zram->mem_pool, handle); + __move_to_zram(zram, index, handle, io_eswap); + + return 0; +} + +static int eswap_unlock(struct io_eswapent *io_eswap) +{ + int eswapid; + struct mem_cgroup *mcg = NULL; + struct zram *zram = NULL; + int k; + unsigned long eswpentry; + int real_load = 0, size; + + if (!io_eswap) { + hybp(HYB_ERR, "NULL io_eswap\n"); + goto out; + } + + mcg = io_eswap->mcg; + if (!mcg) { + hybp(HYB_ERR, "NULL mcg\n"); + goto out; + } + zram = MEMCGRP_ITEM(mcg, zram); + if (!zram) { + hybp(HYB_ERR, "NULL zram\n"); + goto out; + } + eswapid = io_eswap->eswapid; + if (eswapid < 0) + goto out; + + eswapid = io_eswap->eswapid; + if (MEMCGRP_ITEM(mcg, in_swapin)) + goto out; + hybp(HYB_DEBUG, "add eswapid = %d, cnt = %d.\n", + eswapid, io_eswap->cnt); + eswpentry = ((unsigned long)eswapid) << ESWAP_SHIFT; + for (k = 0; k < io_eswap->cnt; k++) + zram_slot_lock(zram, io_eswap->index[k]); + for (k = 0; k < io_eswap->cnt; k++) { + move_to_hybridswap(zram, io_eswap->index[k], eswpentry, mcg); + size = zram_get_obj_size(zram, io_eswap->index[k]); + eswpentry += size; + real_load += size; + } + put_eswap(zram->infos, eswapid); + io_eswap->eswapid = -EINVAL; + for (k = 0; k < io_eswap->cnt; k++) + zram_slot_unlock(zram, io_eswap->index[k]); + hybp(HYB_DEBUG, "add eswap OK.\n"); +out: + discard_io_eswapent(io_eswap, REQ_OP_WRITE); + if (mcg) + css_put(&mcg->css); + + return real_load; +} + +static void eswap_add(struct io_eswapent *io_eswap, + enum hybridswap_class class) +{ + struct mem_cgroup *mcg = NULL; + struct zram *zram = NULL; + int eswapid; + int k; + + if (!io_eswap) { + hybp(HYB_ERR, "NULL io_eswap\n"); + return; + } + + mcg = io_eswap->mcg; + if (!mcg) + zram = io_eswap->zram; + else + zram = MEMCGRP_ITEM(mcg, zram); + if (!zram) { + hybp(HYB_ERR, "NULL zram\n"); + goto out; + } + + eswapid = io_eswap->eswapid; + if (eswapid < 0) + goto out; + + io_eswap->cnt = swap_maps_fetch_eswap_index(zram->infos, + eswapid, + io_eswap->index); + hybp(HYB_DEBUG, "eswapid = %d, cnt = %d.\n", eswapid, io_eswap->cnt); + for (k = 0; k < io_eswap->cnt; k++) { + int ret = move_to_zram(zram, io_eswap->index[k], io_eswap); + + if (ret < 0) + goto out; + } + hybp(HYB_DEBUG, "eswap add OK, free eswapid = %d.\n", eswapid); + hybridswap_free_eswap(zram->infos, io_eswap->eswapid); + io_eswap->eswapid = -EINVAL; + if (mcg) { + atomic64_inc(&MEMCGRP_ITEM(mcg, hybridswap_inextcnt)); + atomic_dec(&MEMCGRP_ITEM(mcg, hybridswap_extcnt)); + } +out: + discard_io_eswapent(io_eswap, REQ_OP_READ); + if (mcg) + css_put(&mcg->css); +} + +static void eswap_clear(struct zram *zram, int eswapid) +{ + int *index = NULL; + int cnt; + int k; + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + + if (!stat) { + hybp(HYB_ERR, "NULL stat\n"); + return; + } + + index = kzalloc(sizeof(int) * ESWAP_MAX_OBJ_CNT, GFP_NOIO); + if (!index) + index = kzalloc(sizeof(int) * ESWAP_MAX_OBJ_CNT, + GFP_NOIO | __GFP_NOFAIL); + + cnt = swap_maps_fetch_eswap_index(zram->infos, eswapid, index); + + for (k = 0; k < cnt; k++) { + zram_slot_lock(zram, index[k]); + if (zram_test_overwrite(zram, index[k], eswapid)) { + zram_slot_unlock(zram, index[k]); + continue; + } + zram_set_mcg(zram, index[k], 0); + zram_set_flag(zram, index[k], ZRAM_MCGID_CLEAR); + atomic64_inc(&stat->memcgid_clear); + zram_slot_unlock(zram, index[k]); + } + + kfree(index); +} + +static int shrink_entry(struct zram *zram, u32 index, struct io_eswapent *io_eswap, + unsigned long eswap_off) +{ + unsigned long handle; + int size; + u8 *src = NULL; + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + + if (!stat) { + hybp(HYB_ERR, "NULL stat\n"); + return -EINVAL; + } + if (!zram) { + hybp(HYB_ERR, "NULL zram\n"); + return -EINVAL; + } + if (index >= (u32)zram->infos->total_objects) { + hybp(HYB_ERR, "index = %d invalid\n", index); + return -EINVAL; + } + + zram_slot_lock(zram, index); + handle = zram_get_handle(zram, index); + if (!handle || zram_test_skip(zram, index, io_eswap->mcg)) { + zram_slot_unlock(zram, index); + return 0; + } + size = zram_get_obj_size(zram, index); + if (eswap_off + size > ESWAP_SIZE) { + zram_slot_unlock(zram, index); + return -ENOSPC; + } + + src = zs_map_object(zram->mem_pool, handle, ZS_MM_RO); + copy_to_pages(src, io_eswap->pages, eswap_off, size); + zs_unmap_object(zram->mem_pool, handle); + io_eswap->index[io_eswap->cnt++] = index; + + swap_sorted_list_del(zram, index); + zram_set_flag(zram, index, ZRAM_UNDER_WB); + if (zram_test_flag(zram, index, ZRAM_FROM_HYBRIDSWAP)) { + atomic64_inc(&stat->reout_pages); + atomic64_add(size, &stat->reout_bytes); + } + zram_slot_unlock(zram, index); + atomic64_inc(&stat->reclaimin_pages); + + return size; +} + +static int shrink_entry_list(struct io_eswapent *io_eswap) +{ + struct mem_cgroup *mcg = NULL; + struct zram *zram = NULL; + unsigned long stored_size; + int *swap_index = NULL; + int swap_cnt, k; + int swap_size = 0; + + if (!io_eswap) { + hybp(HYB_ERR, "NULL io_eswap\n"); + return -EINVAL; + } + + mcg = io_eswap->mcg; + zram = MEMCGRP_ITEM(mcg, zram); + hybp(HYB_DEBUG, "mcg = %d\n", mcg->id.id); + stored_size = atomic64_read(&MEMCGRP_ITEM(mcg, zram_stored_size)); + hybp(HYB_DEBUG, "zram_stored_size = %ld\n", stored_size); + if (stored_size < ESWAP_SIZE) { + hybp(HYB_INFO, "%lu is smaller than ESWAP_SIZE\n", stored_size); + return -ENOENT; + } + + swap_index = kzalloc(sizeof(int) * ESWAP_MAX_OBJ_CNT, GFP_NOIO); + if (!swap_index) + return -ENOMEM; + io_eswap->eswapid = hybridswap_alloc_eswap(zram->infos, mcg); + if (io_eswap->eswapid < 0) { + kfree(swap_index); + return io_eswap->eswapid; + } + swap_cnt = zram_fetch_mcg_last_index(zram->infos, mcg, swap_index, + ESWAP_MAX_OBJ_CNT); + io_eswap->cnt = 0; + for (k = 0; k < swap_cnt && swap_size < (int)ESWAP_SIZE; k++) { + int size = shrink_entry(zram, swap_index[k], io_eswap, swap_size); + + if (size < 0) + break; + swap_size += size; + } + kfree(swap_index); + hybp(HYB_DEBUG, "fill eswap = %d, cnt = %d, overhead = %ld.\n", + io_eswap->eswapid, io_eswap->cnt, ESWAP_SIZE - swap_size); + if (swap_size == 0) { + hybp(HYB_ERR, "swap_size = 0, free eswapid = %d.\n", + io_eswap->eswapid); + hybridswap_free_eswap(zram->infos, io_eswap->eswapid); + io_eswap->eswapid = -EINVAL; + return -ENOENT; + } + + return swap_size; +} + +void hybridswap_manager_deinit(struct zram *zram) +{ + if (!zram) { + hybp(HYB_ERR, "NULL zram\n"); + return; + } + + free_hyb_info(zram->infos); + zram->infos = NULL; +} + +int hybridswap_manager_init(struct zram *zram) +{ + int ret; + + if (!zram) { + hybp(HYB_ERR, "NULL zram\n"); + ret = -EINVAL; + goto out; + } + + zram->infos = alloc_hyb_info(zram->disksize, + zram->nr_pages << PAGE_SHIFT); + if (!zram->infos) { + ret = -ENOMEM; + goto out; + } + return 0; +out: + hybridswap_manager_deinit(zram); + + return ret; +} + +void hybridswap_manager_memcg_init(struct zram *zram, + struct mem_cgroup *memcg) +{ + memcg_hybs_t *hybs; + + if (!memcg || !zram || !zram->infos) { + hybp(HYB_ERR, "invalid zram or mcg_hyb\n"); + return; + } + + hyb_entries_init(memcgindex(zram->infos, memcg->id.id), zram->infos->objects); + hyb_entries_init(memcgindex(zram->infos, memcg->id.id), zram->infos->eswap_table); + + hybs = MEMCGRP_ITEM_DATA(memcg); + hybs->in_swapin = false; + atomic64_set(&hybs->zram_stored_size, 0); + atomic64_set(&hybs->zram_page_size, 0); + atomic64_set(&hybs->hybridswap_stored_pages, 0); + atomic64_set(&hybs->hybridswap_stored_size, 0); + atomic64_set(&hybs->hybridswap_allfaultcnt, 0); + atomic64_set(&hybs->hybridswap_outcnt, 0); + atomic64_set(&hybs->hybridswap_incnt, 0); + atomic64_set(&hybs->hybridswap_faultcnt, 0); + atomic64_set(&hybs->hybridswap_outextcnt, 0); + atomic64_set(&hybs->hybridswap_inextcnt, 0); + atomic_set(&hybs->hybridswap_extcnt, 0); + atomic_set(&hybs->hybridswap_peakextcnt, 0); + mutex_init(&hybs->swap_lock); + + smp_wmb(); + hybs->zram = zram; + hybp(HYB_DEBUG, "new memcg in zram, id = %d.\n", memcg->id.id); +} + +void hybridswap_manager_memcg_deinit(struct mem_cgroup *mcg) +{ + struct zram *zram = NULL; + struct hyb_info *infos = NULL; + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + int last_index = -1; + memcg_hybs_t *hybs; + + if (!stat) { + hybp(HYB_ERR, "NULL stat\n"); + return; + } + + hybs = MEMCGRP_ITEM_DATA(mcg); + if (!hybs->zram) + return; + + zram = hybs->zram; + if (!zram->infos) { + hybp(HYB_WARN, "mcg %p name %s id %d zram %p infos is NULL\n", + mcg, hybs->name, mcg->id.id, zram); + return; + } + + hybp(HYB_DEBUG, "deinit mcg %d %s\n", mcg->id.id, hybs->name); + if (mcg->id.id == 0) + return; + + infos = zram->infos; + while (1) { + int index = fetch_memcg_zram_entry(infos, mcg); + + if (index == -ENOENT) + break; + + if (index < 0) { + hybp(HYB_ERR, "invalid index\n"); + return; + } + + if (last_index == index) { + hybp(HYB_ERR, "dup index %d\n", index); + dump_stack(); + } + + zram_slot_lock(zram, index); + if (index == last_index || mcg == zram_fetch_mcg(zram, index)) { + hyb_entries_del(obj_index(zram->infos, index), + memcgindex(zram->infos, mcg->id.id), + zram->infos->objects); + zram_set_mcg(zram, index, 0); + zram_set_flag(zram, index, ZRAM_MCGID_CLEAR); + atomic64_inc(&stat->memcgid_clear); + } + zram_slot_unlock(zram, index); + last_index = index; + } + + hybp(HYB_DEBUG, "deinit mcg %d %s, entry done\n", mcg->id.id, hybs->name); + while (1) { + int eswapid = fetch_memcg_eswap(infos, mcg); + + if (eswapid == -ENOENT) + break; + + eswap_clear(zram, eswapid); + hyb_entries_set_memcgid(eswap_index(infos, eswapid), infos->eswap_table, 0); + put_eswap(infos, eswapid); + } + hybp(HYB_DEBUG, "deinit mcg %d %s, eswap done\n", mcg->id.id, hybs->name); + hybs->zram = NULL; +} +void hybridswap_swap_sorted_list_add(struct zram *zram, + u32 index, struct mem_cgroup *memcg) +{ + if (!zram) { + hybp(HYB_ERR, "NULL zram\n"); + return; + } + if (index >= (u32)zram->infos->total_objects) { + hybp(HYB_ERR, "index = %d invalid\n", index); + return; + } + + swap_sorted_list_add(zram, index, memcg); +} + +void hybridswap_swap_sorted_list_del(struct zram *zram, u32 index) +{ + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + + if (!stat) { + hybp(HYB_ERR, "NULL stat\n"); + return; + } + if (!zram) { + hybp(HYB_ERR, "NULL zram\n"); + return; + } + if (index >= (u32)zram->infos->total_objects) { + hybp(HYB_ERR, "index = %d invalid\n", index); + return; + } + + zram_clear_flag(zram, index, ZRAM_FROM_HYBRIDSWAP); + if (zram_test_flag(zram, index, ZRAM_MCGID_CLEAR)) { + zram_clear_flag(zram, index, ZRAM_MCGID_CLEAR); + atomic64_dec(&stat->memcgid_clear); + } + + if (zram_test_flag(zram, index, ZRAM_WB)) { + update_size_info(zram, index); + swap_maps_destroy(zram, index); + zram_clear_flag(zram, index, ZRAM_WB); + zram_set_mcg(zram, index, 0); + zram_set_handle(zram, index, 0); + } else { + swap_sorted_list_del(zram, index); + } +} + +unsigned long hybridswap_eswap_create(struct mem_cgroup *mcg, + int *eswapid, + struct hybridswap_buffer *buf, + void **private) +{ + struct io_eswapent *io_eswap = NULL; + int reclaim_size; + + if (!mcg) { + hybp(HYB_ERR, "NULL mcg\n"); + return 0; + } + if (!eswapid) { + hybp(HYB_ERR, "NULL eswapid\n"); + return 0; + } + (*eswapid) = -EINVAL; + if (!buf) { + hybp(HYB_ERR, "NULL buf\n"); + return 0; + } + if (!private) { + hybp(HYB_ERR, "NULL private\n"); + return 0; + } + + io_eswap = alloc_io_eswapent(buf->pool, false, true); + if (!io_eswap) + return 0; + io_eswap->mcg = mcg; + reclaim_size = shrink_entry_list(io_eswap); + if (reclaim_size < 0) { + discard_io_eswapent(io_eswap, REQ_OP_WRITE); + (*eswapid) = reclaim_size; + return 0; + } + io_eswap->real_load = reclaim_size; + css_get(&mcg->css); + (*eswapid) = io_eswap->eswapid; + buf->dest_pages = io_eswap->pages; + (*private) = io_eswap; + hybp(HYB_DEBUG, "mcg = %d, eswapid = %d\n", mcg->id.id, io_eswap->eswapid); + + return reclaim_size; +} + +void hybridswap_eswap_register(void *private, struct hybridswap_io_req *req) +{ + struct io_eswapent *io_eswap = private; + + if (!io_eswap) { + hybp(HYB_ERR, "NULL io_eswap\n"); + return; + } + hybp(HYB_DEBUG, "eswapid = %d\n", io_eswap->eswapid); + atomic64_add(eswap_unlock(io_eswap), &req->real_load); +} + +void hybridswap_eswap_objs_del(struct zram *zram, u32 index) +{ + int eswapid; + struct mem_cgroup *mcg = NULL; + unsigned long eswpentry; + int size; + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + + if (!stat) { + hybp(HYB_ERR, "NULL stat\n"); + return; + } + if (!zram || !zram->infos) { + hybp(HYB_ERR, "NULL zram\n"); + return; + } + if (index >= (u32)zram->infos->total_objects) { + hybp(HYB_ERR, "index = %d invalid\n", index); + return; + } + if (!zram_test_flag(zram, index, ZRAM_WB)) { + hybp(HYB_ERR, "not WB object\n"); + return; + } + + eswpentry = zram_get_handle(zram, index); + size = zram_get_obj_size(zram, index); + atomic64_sub(size, &stat->stored_size); + atomic64_dec(&stat->stored_pages); + atomic64_add(size, &stat->dropped_eswap_size); + mcg = zram_fetch_mcg(zram, index); + if (mcg) { + atomic64_sub(size, &MEMCGRP_ITEM(mcg, hybridswap_stored_size)); + atomic64_dec(&MEMCGRP_ITEM(mcg, hybridswap_stored_pages)); + } + + zram_clear_flag(zram, index, ZRAM_IN_BD); + if (!atomic_dec_and_test( + &zram->infos->eswap_stored_pages[esentry_extid(eswpentry)])) + return; + eswapid = fetch_eswap(zram->infos, esentry_extid(eswpentry)); + if (eswapid < 0) + return; + + atomic64_inc(&stat->notify_free); + if (mcg) + atomic64_inc(&MEMCGRP_ITEM(mcg, hybridswap_eswap_notify_free)); + hybp(HYB_DEBUG, "free eswapid = %d\n", eswapid); + hybridswap_free_eswap(zram->infos, eswapid); +} + +int hybridswap_find_eswap_by_index(unsigned long eswpentry, + struct hybridswap_buffer *buf, + void **private) +{ + int eswapid; + struct io_eswapent *io_eswap = NULL; + struct zram *zram = NULL; + + if (!buf) { + hybp(HYB_ERR, "NULL buf\n"); + return -EINVAL; + } + if (!private) { + hybp(HYB_ERR, "NULL private\n"); + return -EINVAL; + } + + zram = buf->zram; + eswapid = fetch_eswap(zram->infos, esentry_extid(eswpentry)); + if (eswapid < 0) + return eswapid; + io_eswap = alloc_io_eswapent(buf->pool, true, true); + if (!io_eswap) { + hybp(HYB_ERR, "io_eswap alloc failed\n"); + put_eswap(zram->infos, eswapid); + return -ENOMEM; + } + + io_eswap->eswapid = eswapid; + io_eswap->zram = zram; + io_eswap->mcg = find_memcg_by_id( + hyb_entries_fetch_memcgid(eswap_index(zram->infos, eswapid), + zram->infos->eswap_table)); + if (io_eswap->mcg) + css_get(&io_eswap->mcg->css); + buf->dest_pages = io_eswap->pages; + (*private) = io_eswap; + hybp(HYB_DEBUG, "fetch entry = %lx eswap = %d\n", eswpentry, eswapid); + + return eswapid; +} + +int hybridswap_find_eswap_by_memcg(struct mem_cgroup *mcg, + struct hybridswap_buffer *buf, + void **private) +{ + int eswapid; + struct io_eswapent *io_eswap = NULL; + + if (!mcg) { + hybp(HYB_ERR, "NULL mcg\n"); + return -EINVAL; + } + if (!buf) { + hybp(HYB_ERR, "NULL buf\n"); + return -EINVAL; + } + if (!private) { + hybp(HYB_ERR, "NULL private\n"); + return -EINVAL; + } + + eswapid = fetch_memcg_eswap(MEMCGRP_ITEM(mcg, zram)->infos, mcg); + if (eswapid < 0) + return eswapid; + io_eswap = alloc_io_eswapent(buf->pool, true, false); + if (!io_eswap) { + hybp(HYB_ERR, "io_eswap alloc failed\n"); + put_eswap(MEMCGRP_ITEM(mcg, zram)->infos, eswapid); + return -ENOMEM; + } + io_eswap->eswapid = eswapid; + io_eswap->mcg = mcg; + css_get(&mcg->css); + buf->dest_pages = io_eswap->pages; + (*private) = io_eswap; + hybp(HYB_DEBUG, "fetch mcg = %d, eswap = %d\n", mcg->id.id, eswapid); + + return eswapid; +} + +void hybridswap_eswap_destroy(void *private, enum hybridswap_class class) +{ + struct io_eswapent *io_eswap = private; + + if (!io_eswap) { + hybp(HYB_ERR, "NULL io_eswap\n"); + return; + } + + hybp(HYB_DEBUG, "eswapid = %d\n", io_eswap->eswapid); + eswap_add(io_eswap, class); +} + +void hybridswap_eswap_exception(enum hybridswap_class class, + void *private) +{ + struct io_eswapent *io_eswap = private; + struct mem_cgroup *mcg = NULL; + unsigned int op = (class == HYB_RECLAIM_IN) ? + REQ_OP_WRITE : REQ_OP_READ; + + if (!io_eswap) { + hybp(HYB_ERR, "NULL io_eswap\n"); + return; + } + + hybp(HYB_DEBUG, "eswapid = %d, op = %d\n", io_eswap->eswapid, op); + mcg = io_eswap->mcg; + discard_io_eswapent(io_eswap, op); + if (mcg) + css_put(&mcg->css); +} + +struct mem_cgroup *hybridswap_zram_fetch_mcg(struct zram *zram, u32 index) +{ + return zram_fetch_mcg(zram, index); +} + +void zram_set_mcg(struct zram *zram, u32 index, int memcgid) +{ + hyb_entries_set_memcgid(obj_index(zram->infos, index), + zram->infos->objects, memcgid); +} + +struct mem_cgroup *zram_fetch_mcg(struct zram *zram, u32 index) +{ + unsigned short memcgid; + + memcgid = hyb_entries_fetch_memcgid(obj_index(zram->infos, index), + zram->infos->objects); + + return find_memcg_by_id(memcgid); +} + +int zram_fetch_mcg_last_index(struct hyb_info *infos, + struct mem_cgroup *mcg, + int *index, int max_cnt) +{ + int cnt = 0; + u32 i, tmp; + + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return 0; + } + if (!infos->objects) { + hybp(HYB_ERR, "NULL table\n"); + return 0; + } + if (!mcg) { + hybp(HYB_ERR, "NULL mcg\n"); + return 0; + } + if (!index) { + hybp(HYB_ERR, "NULL index\n"); + return 0; + } + + hyb_lock_with_idx(memcgindex(infos, mcg->id.id), infos->objects); + hyb_entries_for_each_entry_reverse_safe(i, tmp, + memcgindex(infos, mcg->id.id), infos->objects) { + if (i >= (u32)infos->total_objects) { + hybp(HYB_ERR, "index = %d invalid\n", i); + continue; + } + index[cnt++] = i; + if (cnt >= max_cnt) + break; + } + hyb_unlock_with_idx(memcgindex(infos, mcg->id.id), infos->objects); + + return cnt; +} + +int swap_maps_fetch_eswap_index(struct hyb_info *infos, + int eswapid, int *index) +{ + int cnt = 0; + u32 i; + + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return 0; + } + if (!infos->objects) { + hybp(HYB_ERR, "NULL table\n"); + return 0; + } + if (!index) { + hybp(HYB_ERR, "NULL index\n"); + return 0; + } + if (eswapid < 0 || eswapid >= infos->nr_es) { + hybp(HYB_ERR, "eswap = %d invalid\n", eswapid); + return 0; + } + + hyb_lock_with_idx(eswap_index(infos, eswapid), infos->objects); + hyb_entries_for_each_entry(i, eswap_index(infos, eswapid), infos->objects) { + if (cnt >= (int)ESWAP_MAX_OBJ_CNT) { + WARN_ON_ONCE(1); + break; + } + index[cnt++] = i; + } + hyb_unlock_with_idx(eswap_index(infos, eswapid), infos->objects); + + return cnt; +} + +void swap_sorted_list_add(struct zram *zram, u32 index, struct mem_cgroup *memcg) +{ + unsigned long size; + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + + if (!stat) { + hybp(HYB_ERR, "NULL stat\n"); + return; + } + if (!zram) { + hybp(HYB_ERR, "NULL zram\n"); + return; + } + if (index >= (u32)zram->infos->total_objects) { + hybp(HYB_ERR, "index = %d invalid\n", index); + return; + } + if (zram_test_flag(zram, index, ZRAM_WB)) { + hybp(HYB_ERR, "WB object, index = %d\n", index); + return; + } +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + if (zram_test_flag(zram, index, ZRAM_CACHED)) { + hybp(HYB_ERR, "CACHED object, index = %d\n", index); + return; + } + if (zram_test_flag(zram, index, ZRAM_CACHED_COMPRESS)) { + hybp(HYB_ERR, "CACHED_COMPRESS object, index = %d\n", index); + return; + } +#endif + if (zram_test_flag(zram, index, ZRAM_SAME)) + return; + + zram_set_mcg(zram, index, memcg->id.id); + hyb_entries_add(obj_index(zram->infos, index), + memcgindex(zram->infos, memcg->id.id), + zram->infos->objects); + + size = zram_get_obj_size(zram, index); + + atomic64_add(size, &MEMCGRP_ITEM(memcg, zram_stored_size)); + atomic64_inc(&MEMCGRP_ITEM(memcg, zram_page_size)); + atomic64_add(size, &stat->zram_stored_size); + atomic64_inc(&stat->zram_stored_pages); +} + +void swap_sorted_list_add_tail(struct zram *zram, u32 index, struct mem_cgroup *mcg) +{ + unsigned long size; + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + + if (!stat) { + hybp(HYB_ERR, "NULL stat\n"); + return; + } + if (!zram) { + hybp(HYB_ERR, "NULL zram\n"); + return; + } + if (!mcg || !MEMCGRP_ITEM(mcg, zram) || !MEMCGRP_ITEM(mcg, zram)->infos) { + hybp(HYB_ERR, "invalid mcg\n"); + return; + } + if (index >= (u32)zram->infos->total_objects) { + hybp(HYB_ERR, "index = %d invalid\n", index); + return; + } + if (zram_test_flag(zram, index, ZRAM_WB)) { + hybp(HYB_ERR, "WB object, index = %d\n", index); + return; + } +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + if (zram_test_flag(zram, index, ZRAM_CACHED)) { + hybp(HYB_ERR, "CACHED object, index = %d\n", index); + return; + } + if (zram_test_flag(zram, index, ZRAM_CACHED_COMPRESS)) { + hybp(HYB_ERR, "CACHED_COMPRESS object, index = %d\n", index); + return; + } +#endif + if (zram_test_flag(zram, index, ZRAM_SAME)) + return; + + zram_set_mcg(zram, index, mcg->id.id); + hyb_entries_add_tail(obj_index(zram->infos, index), + memcgindex(zram->infos, mcg->id.id), + zram->infos->objects); + + size = zram_get_obj_size(zram, index); + + atomic64_add(size, &MEMCGRP_ITEM(mcg, zram_stored_size)); + atomic64_inc(&MEMCGRP_ITEM(mcg, zram_page_size)); + atomic64_add(size, &stat->zram_stored_size); + atomic64_inc(&stat->zram_stored_pages); +} + +void swap_sorted_list_del(struct zram *zram, u32 index) +{ + struct mem_cgroup *mcg = NULL; + unsigned long size; + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + + if (!stat) { + hybp(HYB_ERR, "NULL stat\n"); + return; + } + if (!zram || !zram->infos) { + hybp(HYB_ERR, "NULL zram\n"); + return; + } + if (index >= (u32)zram->infos->total_objects) { + hybp(HYB_ERR, "index = %d invalid\n", index); + return; + } + if (zram_test_flag(zram, index, ZRAM_WB)) { + hybp(HYB_ERR, "WB object, index = %d\n", index); + return; + } + + mcg = zram_fetch_mcg(zram, index); + if (!mcg || !MEMCGRP_ITEM(mcg, zram) || !MEMCGRP_ITEM(mcg, zram)->infos) + return; + if (zram_test_flag(zram, index, ZRAM_SAME)) + return; + + size = zram_get_obj_size(zram, index); + hyb_entries_del(obj_index(zram->infos, index), + memcgindex(zram->infos, mcg->id.id), + zram->infos->objects); + zram_set_mcg(zram, index, 0); + + atomic64_sub(size, &MEMCGRP_ITEM(mcg, zram_stored_size)); + atomic64_dec(&MEMCGRP_ITEM(mcg, zram_page_size)); + atomic64_sub(size, &stat->zram_stored_size); + atomic64_dec(&stat->zram_stored_pages); +} + +void swap_maps_insert(struct zram *zram, u32 index) +{ + unsigned long eswpentry; + u32 eswapid; + + if (!zram) { + hybp(HYB_ERR, "NULL zram\n"); + return; + } + if (index >= (u32)zram->infos->total_objects) { + hybp(HYB_ERR, "index = %d invalid\n", index); + return; + } + + eswpentry = zram_get_handle(zram, index); + eswapid = esentry_extid(eswpentry); + hyb_entries_add_tail(obj_index(zram->infos, index), + eswap_index(zram->infos, eswapid), + zram->infos->objects); +} + +void swap_maps_destroy(struct zram *zram, u32 index) +{ + unsigned long eswpentry; + u32 eswapid; + + if (!zram) { + hybp(HYB_ERR, "NULL zram\n"); + return; + } + if (index >= (u32)zram->infos->total_objects) { + hybp(HYB_ERR, "index = %d invalid\n", index); + return; + } + + eswpentry = zram_get_handle(zram, index); + eswapid = esentry_extid(eswpentry); + hyb_entries_del(obj_index(zram->infos, index), + eswap_index(zram->infos, eswapid), + zram->infos->objects); +} + +static struct hyb_entries_head *fetch_node_default(int index, void *private) +{ + struct hyb_entries_head *table = private; + + return &table[index]; +} + +struct hyb_entries_table *alloc_table(struct hyb_entries_head *(*fetch_node)(int, void *), + void *private, gfp_t gfp) +{ + struct hyb_entries_table *table = + kmalloc(sizeof(struct hyb_entries_table), gfp); + + if (!table) + return NULL; + table->fetch_node = fetch_node ? fetch_node : fetch_node_default; + table->private = private; + + return table; +} + +void hyb_lock_with_idx(int index, struct hyb_entries_table *table) +{ + struct hyb_entries_head *node = index_node(index, table); + + if (!node) { + hybp(HYB_ERR, "index = %d, table = %pK\n", index, table); + return; + } + bit_spin_lock(ENTRY_LOCK_BIT, (unsigned long *)node); +} + +void hyb_unlock_with_idx(int index, struct hyb_entries_table *table) +{ + struct hyb_entries_head *node = index_node(index, table); + + if (!node) { + hybp(HYB_ERR, "index = %d, table = %pK\n", index, table); + return; + } + bit_spin_unlock(ENTRY_LOCK_BIT, (unsigned long *)node); +} + +bool hyb_entries_empty(int hindex, struct hyb_entries_table *table) +{ + bool ret = false; + + hyb_lock_with_idx(hindex, table); + ret = (prev_index(hindex, table) == hindex) && (next_index(hindex, table) == hindex); + hyb_unlock_with_idx(hindex, table); + + return ret; +} + +void hyb_entries_init(int index, struct hyb_entries_table *table) +{ + struct hyb_entries_head *node = index_node(index, table); + + if (!node) { + hybp(HYB_ERR, "index = %d, table = %pS func %pS\n", + index, table, table->fetch_node); + return; + } + memset(node, 0, sizeof(struct hyb_entries_head)); + node->prev = index; + node->next = index; +} + +void hyb_entries_add_nolock(int index, int hindex, struct hyb_entries_table *table) +{ + struct hyb_entries_head *node = NULL; + struct hyb_entries_head *head = NULL; + struct hyb_entries_head *next = NULL; + int nindex; + + node = index_node(index, table); + if (!node) { + hybp(HYB_ERR, + "NULL node, index = %d, hindex = %d, table = %pK\n", + index, hindex, table); + return; + } + head = index_node(hindex, table); + if (!head) { + hybp(HYB_ERR, + "NULL head, index = %d, hindex = %d, table = %pK\n", + index, hindex, table); + return; + } + next = index_node(head->next, table); + if (!next) { + hybp(HYB_ERR, + "NULL next, index = %d, hindex = %d, table = %pK\n", + index, hindex, table); + return; + } + + nindex = head->next; + if (index != hindex) + hyb_lock_with_idx(index, table); + node->prev = hindex; + node->next = nindex; + if (index != hindex) + hyb_unlock_with_idx(index, table); + head->next = index; + if (nindex != hindex) + hyb_lock_with_idx(nindex, table); + next->prev = index; + if (nindex != hindex) + hyb_unlock_with_idx(nindex, table); +} + +void hyb_entries_add_tail_nolock(int index, int hindex, struct hyb_entries_table *table) +{ + struct hyb_entries_head *node = NULL; + struct hyb_entries_head *head = NULL; + struct hyb_entries_head *tail = NULL; + int tindex; + + node = index_node(index, table); + if (!node) { + hybp(HYB_ERR, + "NULL node, index = %d, hindex = %d, table = %pK\n", + index, hindex, table); + return; + } + head = index_node(hindex, table); + if (!head) { + hybp(HYB_ERR, + "NULL head, index = %d, hindex = %d, table = %pK\n", + index, hindex, table); + return; + } + tail = index_node(head->prev, table); + if (!tail) { + hybp(HYB_ERR, + "NULL tail, index = %d, hindex = %d, table = %pK\n", + index, hindex, table); + return; + } + + tindex = head->prev; + if (index != hindex) + hyb_lock_with_idx(index, table); + node->prev = tindex; + node->next = hindex; + if (index != hindex) + hyb_unlock_with_idx(index, table); + head->prev = index; + if (tindex != hindex) + hyb_lock_with_idx(tindex, table); + tail->next = index; + if (tindex != hindex) + hyb_unlock_with_idx(tindex, table); +} + +void hyb_entries_del_nolock(int index, int hindex, struct hyb_entries_table *table) +{ + struct hyb_entries_head *node = NULL; + struct hyb_entries_head *prev = NULL; + struct hyb_entries_head *next = NULL; + int pindex, nindex; + + node = index_node(index, table); + if (!node) { + hybp(HYB_ERR, + "NULL node, index = %d, hindex = %d, table = %pK\n", + index, hindex, table); + return; + } + prev = index_node(node->prev, table); + if (!prev) { + hybp(HYB_ERR, + "NULL prev, index = %d, hindex = %d, table = %pK\n", + index, hindex, table); + return; + } + next = index_node(node->next, table); + if (!next) { + hybp(HYB_ERR, + "NULL next, index = %d, hindex = %d, table = %pK\n", + index, hindex, table); + return; + } + + if (index != hindex) + hyb_lock_with_idx(index, table); + pindex = node->prev; + nindex = node->next; + node->prev = index; + node->next = index; + if (index != hindex) + hyb_unlock_with_idx(index, table); + if (pindex != hindex) + hyb_lock_with_idx(pindex, table); + prev->next = nindex; + if (pindex != hindex) + hyb_unlock_with_idx(pindex, table); + if (nindex != hindex) + hyb_lock_with_idx(nindex, table); + next->prev = pindex; + if (nindex != hindex) + hyb_unlock_with_idx(nindex, table); +} + +void hyb_entries_add(int index, int hindex, struct hyb_entries_table *table) +{ + hyb_lock_with_idx(hindex, table); + hyb_entries_add_nolock(index, hindex, table); + hyb_unlock_with_idx(hindex, table); +} + +void hyb_entries_add_tail(int index, int hindex, struct hyb_entries_table *table) +{ + hyb_lock_with_idx(hindex, table); + hyb_entries_add_tail_nolock(index, hindex, table); + hyb_unlock_with_idx(hindex, table); +} + +void hyb_entries_del(int index, int hindex, struct hyb_entries_table *table) +{ + hyb_lock_with_idx(hindex, table); + hyb_entries_del_nolock(index, hindex, table); + hyb_unlock_with_idx(hindex, table); +} + +unsigned short hyb_entries_fetch_memcgid(int index, struct hyb_entries_table *table) +{ + struct hyb_entries_head *node = index_node(index, table); + int memcgid; + + if (!node) { + hybp(HYB_ERR, "index = %d, table = %pK\n", index, table); + return 0; + } + + hyb_lock_with_idx(index, table); + memcgid = (node->mcg_left << ENTRY_MCG_SHIFT_HALF) | node->mcg_right; + hyb_unlock_with_idx(index, table); + + return memcgid; +} + +void hyb_entries_set_memcgid(int index, struct hyb_entries_table *table, int memcgid) +{ + struct hyb_entries_head *node = index_node(index, table); + + if (!node) { + hybp(HYB_ERR, "index = %d, table = %pK, mcg = %d\n", + index, table, memcgid); + return; + } + + hyb_lock_with_idx(index, table); + node->mcg_left = (u32)memcgid >> ENTRY_MCG_SHIFT_HALF; + node->mcg_right = (u32)memcgid & ((1 << ENTRY_MCG_SHIFT_HALF) - 1); + hyb_unlock_with_idx(index, table); +} + +bool hyb_entries_set_priv(int index, struct hyb_entries_table *table) +{ + struct hyb_entries_head *node = index_node(index, table); + bool ret = false; + + if (!node) { + hybp(HYB_ERR, "index = %d, table = %pK\n", index, table); + return false; + } + hyb_lock_with_idx(index, table); + ret = !test_and_set_bit(ENTRY_DATA_BIT, (unsigned long *)node); + hyb_unlock_with_idx(index, table); + + return ret; +} + +bool hyb_entries_test_priv(int index, struct hyb_entries_table *table) +{ + struct hyb_entries_head *node = index_node(index, table); + bool ret = false; + + if (!node) { + hybp(HYB_ERR, "index = %d, table = %pK\n", index, table); + return false; + } + hyb_lock_with_idx(index, table); + ret = test_bit(ENTRY_DATA_BIT, (unsigned long *)node); + hyb_unlock_with_idx(index, table); + + return ret; +} + +bool hyb_entries_clear_priv(int index, struct hyb_entries_table *table) +{ + struct hyb_entries_head *node = index_node(index, table); + bool ret = false; + + if (!node) { + hybp(HYB_ERR, "index = %d, table = %pK\n", index, table); + return false; + } + + hyb_lock_with_idx(index, table); + ret = test_and_clear_bit(ENTRY_DATA_BIT, (unsigned long *)node); + hyb_unlock_with_idx(index, table); + + return ret; +} + +struct mem_cgroup *find_memcg_by_id(unsigned short memcgid) +{ + struct mem_cgroup *mcg = NULL; + + rcu_read_lock(); + mcg = mem_cgroup_from_id(memcgid); + rcu_read_unlock(); + + return mcg; +} + +static bool frag_info_dec(bool prev_flag, bool next_flag, + struct hybstatus *stat) +{ + if (prev_flag && next_flag) { + atomic64_inc(&stat->frag_cnt); + return false; + } + + if (prev_flag || next_flag) + return false; + + return true; +} + +static bool frag_info_inc(bool prev_flag, bool next_flag, + struct hybstatus *stat) +{ + if (prev_flag && next_flag) { + atomic64_dec(&stat->frag_cnt); + return false; + } + + if (prev_flag || next_flag) + return false; + + return true; +} + +static bool pre_is_conted(struct hyb_info *infos, int eswapid, int memcgid) +{ + int prev; + + if (is_first_index(eswap_index(infos, eswapid), memcgindex(infos, memcgid), + infos->eswap_table)) + return false; + prev = prev_index(eswap_index(infos, eswapid), infos->eswap_table); + + return (prev >= 0) && (eswap_index(infos, eswapid) == prev + 1); +} + +static bool ne_is_conted(struct hyb_info *infos, int eswapid, int memcgid) +{ + int next; + + if (is_last_index(eswap_index(infos, eswapid), memcgindex(infos, memcgid), + infos->eswap_table)) + return false; + next = next_index(eswap_index(infos, eswapid), infos->eswap_table); + + return (next >= 0) && (eswap_index(infos, eswapid) + 1 == next); +} + +static void eswap_frag_info_sub(struct hyb_info *infos, int eswapid) +{ + bool prev_flag = false; + bool next_flag = false; + int memcgid; + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + + if (!stat) { + hybp(HYB_ERR, "NULL stat\n"); + return; + } + + if (!infos->eswap_table) { + hybp(HYB_ERR, "NULL table\n"); + return; + } + if (eswapid < 0 || eswapid >= infos->nr_es) { + hybp(HYB_ERR, "eswap = %d invalid\n", eswapid); + return; + } + + memcgid = hyb_entries_fetch_memcgid(eswap_index(infos, eswapid), infos->eswap_table); + if (memcgid <= 0 || memcgid >= infos->memcg_num) { + hybp(HYB_ERR, "memcgid = %d invalid\n", memcgid); + return; + } + + atomic64_dec(&stat->eswap_cnt); + infos->memcgid_cnt[memcgid]--; + if (infos->memcgid_cnt[memcgid] == 0) { + atomic64_dec(&stat->mcg_cnt); + atomic64_dec(&stat->frag_cnt); + return; + } + + prev_flag = pre_is_conted(infos, eswapid, memcgid); + next_flag = ne_is_conted(infos, eswapid, memcgid); + + if (frag_info_dec(prev_flag, next_flag, stat)) + atomic64_dec(&stat->frag_cnt); +} + +static void eswap_frag_info_add(struct hyb_info *infos, int eswapid) +{ + bool prev_flag = false; + bool next_flag = false; + int memcgid; + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + + if (!stat) { + hybp(HYB_ERR, "NULL stat\n"); + return; + } + + if (!infos->eswap_table) { + hybp(HYB_ERR, "NULL table\n"); + return; + } + if (eswapid < 0 || eswapid >= infos->nr_es) { + hybp(HYB_ERR, "eswap = %d invalid\n", eswapid); + return; + } + + memcgid = hyb_entries_fetch_memcgid(eswap_index(infos, eswapid), infos->eswap_table); + if (memcgid <= 0 || memcgid >= infos->memcg_num) { + hybp(HYB_ERR, "memcgid = %d invalid\n", memcgid); + return; + } + + atomic64_inc(&stat->eswap_cnt); + if (infos->memcgid_cnt[memcgid] == 0) { + infos->memcgid_cnt[memcgid]++; + atomic64_inc(&stat->frag_cnt); + atomic64_inc(&stat->mcg_cnt); + return; + } + infos->memcgid_cnt[memcgid]++; + + prev_flag = pre_is_conted(infos, eswapid, memcgid); + next_flag = ne_is_conted(infos, eswapid, memcgid); + + if (frag_info_inc(prev_flag, next_flag, stat)) + atomic64_inc(&stat->frag_cnt); +} + +static int eswap_bit2id(struct hyb_info *infos, int bit) +{ + if (bit < 0 || bit >= infos->nr_es) { + hybp(HYB_ERR, "bit = %d invalid\n", bit); + return -EINVAL; + } + + return infos->nr_es - bit - 1; +} + +static int eswap_id2bit(struct hyb_info *infos, int id) +{ + if (id < 0 || id >= infos->nr_es) { + hybp(HYB_ERR, "id = %d invalid\n", id); + return -EINVAL; + } + + return infos->nr_es - id - 1; +} + +int obj_index(struct hyb_info *infos, int index) +{ + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return -EINVAL; + } + if (index < 0 || index >= infos->total_objects) { + hybp(HYB_ERR, "index = %d invalid\n", index); + return -EINVAL; + } + + return index; +} + +int eswap_index(struct hyb_info *infos, int index) +{ + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return -EINVAL; + } + if (index < 0 || index >= infos->nr_es) { + hybp(HYB_ERR, "index = %d invalid\n", index); + return -EINVAL; + } + + return index + infos->total_objects; +} + +int memcgindex(struct hyb_info *infos, int index) +{ + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return -EINVAL; + } + if (index <= 0 || index >= infos->memcg_num) { + hybp(HYB_ERR, "index = %d invalid, memcg_num %d\n", index, + infos->memcg_num); + return -EINVAL; + } + + return index + infos->total_objects + infos->nr_es; +} + +static struct hyb_entries_head *fetch_objects_node(int index, void *private) +{ + struct hyb_info *infos = private; + + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return NULL; + } + if (index < 0) { + hybp(HYB_ERR, "index = %d invalid\n", index); + return NULL; + } + if (index < infos->total_objects) + return &infos->lru[index]; + index -= infos->total_objects; + if (index < infos->nr_es) + return &infos->maps[index]; + index -= infos->nr_es; + if (index > 0 && index < infos->memcg_num) { + struct mem_cgroup *mcg = find_memcg_by_id(index); + + if (!mcg) + goto err_out; + return (struct hyb_entries_head *)(&MEMCGRP_ITEM(mcg, swap_sorted_list)); + } +err_out: + hybp(HYB_ERR, "index = %d invalid, mcg is NULL\n", index); + + return NULL; +} + +static void free_obj_list_table(struct hyb_info *infos) +{ + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return; + } + + if (infos->lru) { + vfree(infos->lru); + infos->lru = NULL; + } + if (infos->maps) { + vfree(infos->maps); + infos->maps = NULL; + } + + kfree(infos->objects); + infos->objects = NULL; +} + +static int init_obj_list_table(struct hyb_info *infos) +{ + int i; + + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return -EINVAL; + } + + infos->lru = vzalloc(sizeof(struct hyb_entries_head) * infos->total_objects); + if (!infos->lru) { + hybp(HYB_ERR, "infos->lru alloc failed\n"); + goto err_out; + } + infos->maps = vzalloc(sizeof(struct hyb_entries_head) * infos->nr_es); + if (!infos->maps) { + hybp(HYB_ERR, "infos->maps alloc failed\n"); + goto err_out; + } + infos->objects = alloc_table(fetch_objects_node, infos, GFP_KERNEL); + if (!infos->objects) { + hybp(HYB_ERR, "infos->objects alloc failed\n"); + goto err_out; + } + for (i = 0; i < infos->total_objects; i++) + hyb_entries_init(obj_index(infos, i), infos->objects); + for (i = 0; i < infos->nr_es; i++) + hyb_entries_init(eswap_index(infos, i), infos->objects); + + hybp(HYB_INFO, "hybridswap obj list table init OK.\n"); + return 0; +err_out: + free_obj_list_table(infos); + hybp(HYB_ERR, "hybridswap obj list table init failed.\n"); + + return -ENOMEM; +} + +static struct hyb_entries_head *fetch_eswap_table_node(int index, void *private) +{ + struct hyb_info *infos = private; + + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return NULL; + } + + if (index < infos->total_objects) + goto err_out; + index -= infos->total_objects; + if (index < infos->nr_es) + return &infos->eswap[index]; + index -= infos->nr_es; + if (index > 0 && index < infos->memcg_num) { + struct mem_cgroup *mcg = find_memcg_by_id(index); + + if (!mcg) + return NULL; + return (struct hyb_entries_head *)(&MEMCGRP_ITEM(mcg, eswap_lru)); + } +err_out: + hybp(HYB_ERR, "index = %d invalid\n", index); + + return NULL; +} + +static void free_eswap_list_table(struct hyb_info *infos) +{ + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return; + } + + if (infos->eswap) + vfree(infos->eswap); + + kfree(infos->eswap_table); +} + +static int init_eswap_list_table(struct hyb_info *infos) +{ + int i; + + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return -EINVAL; + } + infos->eswap = vzalloc(sizeof(struct hyb_entries_head) * infos->nr_es); + if (!infos->eswap) + goto err_out; + infos->eswap_table = alloc_table(fetch_eswap_table_node, infos, GFP_KERNEL); + if (!infos->eswap_table) + goto err_out; + for (i = 0; i < infos->nr_es; i++) + hyb_entries_init(eswap_index(infos, i), infos->eswap_table); + hybp(HYB_INFO, "hybridswap eswap list table init OK.\n"); + return 0; +err_out: + free_eswap_list_table(infos); + hybp(HYB_ERR, "hybridswap eswap list table init failed.\n"); + + return -ENOMEM; +} + +void free_hyb_info(struct hyb_info *infos) +{ + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return; + } + + vfree(infos->bitmask); + vfree(infos->eswap_stored_pages); + free_obj_list_table(infos); + free_eswap_list_table(infos); + vfree(infos); +} + +struct hyb_info *alloc_hyb_info(unsigned long ori_size, + unsigned long comp_size) +{ + struct hyb_info *infos = vzalloc(sizeof(struct hyb_info)); + + if (!infos) { + hybp(HYB_ERR, "infos alloc failed\n"); + goto err_out; + } + if (comp_size & (ESWAP_SIZE - 1)) { + hybp(HYB_ERR, "disksize = %ld align invalid (32K align needed)\n", + comp_size); + goto err_out; + } + infos->size = comp_size; + infos->nr_es = comp_size >> ESWAP_SHIFT; + infos->memcg_num = MEM_CGROUP_ID_MAX; + infos->total_objects = ori_size >> PAGE_SHIFT; + infos->bitmask = vzalloc(BITS_TO_LONGS(infos->nr_es) * sizeof(long)); + if (!infos->bitmask) { + hybp(HYB_ERR, "infos->bitmask alloc failed, %lu\n", + BITS_TO_LONGS(infos->nr_es) * sizeof(long)); + goto err_out; + } + infos->eswap_stored_pages = vzalloc(sizeof(atomic_t) * infos->nr_es); + if (!infos->eswap_stored_pages) { + hybp(HYB_ERR, "infos->eswap_stored_pages alloc failed\n"); + goto err_out; + } + if (init_obj_list_table(infos)) { + hybp(HYB_ERR, "init obj list table failed\n"); + goto err_out; + } + if (init_eswap_list_table(infos)) { + hybp(HYB_ERR, "init eswap list table failed\n"); + goto err_out; + } + hybp(HYB_INFO, "infos %p size %lu nr_es %lu memcg_num %lu total_objects %lu\n", + infos, (unsigned long)infos->size, (unsigned long)infos->nr_es, (unsigned long)infos->memcg_num, + (unsigned long)infos->total_objects); + hybp(HYB_INFO, "hyb_info init OK.\n"); + return infos; +err_out: + free_hyb_info(infos); + hybp(HYB_ERR, "hyb_info init failed.\n"); + + return NULL; +} + +void hybridswap_check_infos_eswap(struct hyb_info *infos) +{ + int i; + + if (!infos) + return; + + for (i = 0; i < infos->nr_es; i++) { + int cnt = atomic_read(&infos->eswap_stored_pages[i]); + int eswapid = eswap_index(infos, i); + bool data = hyb_entries_test_priv(eswapid, infos->eswap_table); + int memcgid = hyb_entries_fetch_memcgid(eswapid, infos->eswap_table); + + if (cnt < 0 || (cnt > 0 && memcgid == 0)) + hybp(HYB_ERR, "%8d %8d %8d %8d %4d\n", i, cnt, eswapid, + memcgid, data); + } +} + +void hybridswap_free_eswap(struct hyb_info *infos, int eswapid) +{ + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return; + } + if (eswapid < 0 || eswapid >= infos->nr_es) { + hybp(HYB_ERR, "INVALID eswap %d\n", eswapid); + return; + } + hybp(HYB_DEBUG, "free eswap id = %d.\n", eswapid); + + hyb_entries_set_memcgid(eswap_index(infos, eswapid), infos->eswap_table, 0); + if (!test_and_clear_bit(eswap_id2bit(infos, eswapid), infos->bitmask)) { + hybp(HYB_ERR, "bit not set, eswap = %d\n", eswapid); + WARN_ON_ONCE(1); + } + atomic_dec(&infos->stored_exts); +} + +static int alloc_bitmask(unsigned long *bitmask, int max, int last_bit) +{ + int bit; + + if (!bitmask) { + hybp(HYB_ERR, "NULL bitmask.\n"); + return -EINVAL; + } +retry: + bit = find_next_zero_bit(bitmask, max, last_bit); + if (bit == max) { + if (last_bit == 0) { + hybp(HYB_ERR, "alloc bitmask failed.\n"); + return -ENOSPC; + } + last_bit = 0; + goto retry; + } + if (test_and_set_bit(bit, bitmask)) + goto retry; + + return bit; +} + +int hybridswap_alloc_eswap(struct hyb_info *infos, struct mem_cgroup *mcg) +{ + int last_bit; + int bit; + int eswapid; + int memcgid; + + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return -EINVAL; + } + if (!mcg) { + hybp(HYB_ERR, "NULL mcg\n"); + return -EINVAL; + } + + last_bit = atomic_read(&infos->last_alloc_bit); + hybp(HYB_DEBUG, "last_bit = %d.\n", last_bit); + bit = alloc_bitmask(infos->bitmask, infos->nr_es, last_bit); + if (bit < 0) { + hybp(HYB_ERR, "alloc bitmask failed.\n"); + return bit; + } + eswapid = eswap_bit2id(infos, bit); + memcgid = hyb_entries_fetch_memcgid(eswap_index(infos, eswapid), infos->eswap_table); + if (memcgid) { + hybp(HYB_ERR, "already has mcg %d, eswap %d\n", + memcgid, eswapid); + goto err_out; + } + hyb_entries_set_memcgid(eswap_index(infos, eswapid), infos->eswap_table, mcg->id.id); + + atomic_set(&infos->last_alloc_bit, bit); + atomic_inc(&infos->stored_exts); + hybp(HYB_DEBUG, "eswap %d init OK.\n", eswapid); + hybp(HYB_DEBUG, "memcgid = %d, eswap id = %d\n", mcg->id.id, eswapid); + + return eswapid; +err_out: + clear_bit(bit, infos->bitmask); + WARN_ON_ONCE(1); + return -EBUSY; +} + +int fetch_eswap(struct hyb_info *infos, int eswapid) +{ + int memcgid; + + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return -EINVAL; + } + if (eswapid < 0 || eswapid >= infos->nr_es) { + hybp(HYB_ERR, "eswap = %d invalid\n", eswapid); + return -EINVAL; + } + + if (!hyb_entries_clear_priv(eswap_index(infos, eswapid), infos->eswap_table)) + return -EBUSY; + memcgid = hyb_entries_fetch_memcgid(eswap_index(infos, eswapid), infos->eswap_table); + if (memcgid) { + eswap_frag_info_sub(infos, eswapid); + hyb_entries_del(eswap_index(infos, eswapid), memcgindex(infos, memcgid), + infos->eswap_table); + } + hybp(HYB_DEBUG, "eswap id = %d\n", eswapid); + + return eswapid; +} + +void put_eswap(struct hyb_info *infos, int eswapid) +{ + int memcgid; + + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return; + } + if (eswapid < 0 || eswapid >= infos->nr_es) { + hybp(HYB_ERR, "eswap = %d invalid\n", eswapid); + return; + } + + memcgid = hyb_entries_fetch_memcgid(eswap_index(infos, eswapid), infos->eswap_table); + if (memcgid) { + hyb_lock_with_idx(memcgindex(infos, memcgid), infos->eswap_table); + hyb_entries_add_nolock(eswap_index(infos, eswapid), memcgindex(infos, memcgid), + infos->eswap_table); + eswap_frag_info_add(infos, eswapid); + hyb_unlock_with_idx(memcgindex(infos, memcgid), infos->eswap_table); + } + if (!hyb_entries_set_priv(eswap_index(infos, eswapid), infos->eswap_table)) { + hybp(HYB_ERR, "private not set, eswap = %d\n", eswapid); + WARN_ON_ONCE(1); + return; + } + hybp(HYB_DEBUG, "put eswap %d.\n", eswapid); +} + +int fetch_memcg_eswap(struct hyb_info *infos, struct mem_cgroup *mcg) +{ + int memcgid; + int eswapid = -ENOENT; + int index; + + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return -EINVAL; + } + if (!infos->eswap_table) { + hybp(HYB_ERR, "NULL table\n"); + return -EINVAL; + } + if (!mcg) { + hybp(HYB_ERR, "NULL mcg\n"); + return -EINVAL; + } + + memcgid = mcg->id.id; + hyb_lock_with_idx(memcgindex(infos, memcgid), infos->eswap_table); + hyb_entries_for_each_entry(index, memcgindex(infos, memcgid), infos->eswap_table) + if (hyb_entries_clear_priv(index, infos->eswap_table)) { + eswapid = index - infos->total_objects; + break; + } + if (eswapid >= 0 && eswapid < infos->nr_es) { + eswap_frag_info_sub(infos, eswapid); + hyb_entries_del_nolock(index, memcgindex(infos, memcgid), infos->eswap_table); + hybp(HYB_DEBUG, "eswap id = %d\n", eswapid); + } + hyb_unlock_with_idx(memcgindex(infos, memcgid), infos->eswap_table); + + return eswapid; +} + +int fetch_memcg_zram_entry(struct hyb_info *infos, struct mem_cgroup *mcg) +{ + int memcgid, idx; + int index = -ENOENT; + + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return -EINVAL; + } + if (!infos->objects) { + hybp(HYB_ERR, "NULL table\n"); + return -EINVAL; + } + if (!mcg) { + hybp(HYB_ERR, "NULL mcg\n"); + return -EINVAL; + } + + memcgid = mcg->id.id; + hyb_lock_with_idx(memcgindex(infos, memcgid), infos->objects); + hyb_entries_for_each_entry(idx, memcgindex(infos, memcgid), infos->objects) { + index = idx; + break; + } + hyb_unlock_with_idx(memcgindex(infos, memcgid), infos->objects); + + return index; +} + +int fetch_eswap_zram_entry(struct hyb_info *infos, int eswapid) +{ + int index = -ENOENT; + int idx; + + if (!infos) { + hybp(HYB_ERR, "NULL infos\n"); + return -EINVAL; + } + if (!infos->objects) { + hybp(HYB_ERR, "NULL table\n"); + return -EINVAL; + } + if (eswapid < 0 || eswapid >= infos->nr_es) { + hybp(HYB_ERR, "eswap = %d invalid\n", eswapid); + return -EINVAL; + } + + hyb_lock_with_idx(eswap_index(infos, eswapid), infos->objects); + hyb_entries_for_each_entry(idx, eswap_index(infos, eswapid), infos->objects) { + index = idx; + break; + } + hyb_unlock_with_idx(eswap_index(infos, eswapid), infos->objects); + + return index; +} + +void *hybridswap_malloc(size_t size, bool fast, bool nofail) +{ + void *mem = NULL; + + if (likely(fast)) { + mem = kzalloc(size, GFP_ATOMIC); + if (likely(mem || !nofail)) + return mem; + } + + mem = kzalloc(size, GFP_NOIO); + + return mem; +} + +void hybridswap_free(const void *mem) +{ + kfree(mem); +} + +struct page *hybridswap_alloc_page_common(void *data, gfp_t gfp) +{ + struct page *page = NULL; + struct zs_eswap_para *eswap_para = (struct zs_eswap_para *)data; + + if (eswap_para->pool) { + spin_lock(&eswap_para->pool->page_pool_lock); + if (!list_empty(&eswap_para->pool->page_pool_list)) { + page = list_first_entry( + &eswap_para->pool->page_pool_list, + struct page, lru); + list_del(&page->lru); + } + spin_unlock(&eswap_para->pool->page_pool_lock); + } + + if (!page) { + if (eswap_para->fast) { + page = alloc_page(GFP_ATOMIC); + if (likely(page)) + goto out; + } + if (eswap_para->nofail) + page = alloc_page(GFP_NOIO); + else + page = alloc_page(gfp); + } +out: + return page; +} + +unsigned long hybridswap_zsmalloc(struct zs_pool *zs_pool, + size_t size, struct hybridswap_page_pool *pool) +{ + gfp_t gfp = __GFP_DIRECT_RECLAIM | __GFP_KSWAPD_RECLAIM | + __GFP_NOWARN | __GFP_HIGHMEM | __GFP_MOVABLE; + return zs_malloc(zs_pool, size, gfp); +} + +unsigned long zram_zsmalloc(struct zs_pool *zs_pool, size_t size, gfp_t gfp) +{ + return zs_malloc(zs_pool, size, gfp); +} + +struct page *hybridswap_alloc_page(struct hybridswap_page_pool *pool, + gfp_t gfp, bool fast, bool nofail) +{ + struct zs_eswap_para eswap_para; + + eswap_para.pool = pool; + eswap_para.fast = fast; + eswap_para.nofail = nofail; + + return hybridswap_alloc_page_common((void *)&eswap_para, gfp); +} + +void hybridswap_page_recycle(struct page *page, struct hybridswap_page_pool *pool) +{ + if (pool) { + spin_lock(&pool->page_pool_lock); + list_add(&page->lru, &pool->page_pool_list); + spin_unlock(&pool->page_pool_lock); + } else { + __free_page(page); + } +} + +bool hybridswap_out_to_eswap_enable(void) +{ + return !!atomic_read(&global_settings.out_to_eswap_enable); +} + +void hybridswap_set_out_to_eswap_disable(void) +{ + atomic_set(&global_settings.out_to_eswap_enable, false); +} + +void hybridswap_set_out_to_eswap_enable(bool en) +{ + atomic_set(&global_settings.out_to_eswap_enable, en ? 1 : 0); +} + +bool hybridswap_core_enabled(void) +{ + return !!atomic_read(&global_settings.enable); +} + +void hybridswap_set_enable(bool en) +{ + hybridswap_set_out_to_eswap_enable(en); + + if (!hybridswap_core_enabled()) + atomic_set(&global_settings.enable, en ? 1 : 0); +} + +struct hybstatus *hybridswap_fetch_stat_obj(void) +{ + return global_settings.stat; +} + +bool hybridswap_dev_life(void) +{ + return !!atomic_read(&global_settings.dev_life); +} + +void hybridswap_set_dev_life(bool en) +{ + atomic_set(&global_settings.dev_life, en ? 1 : 0); +} + +unsigned long hybridswap_quota_day(void) +{ + return global_settings.quota_day; +} + +void hybridswap_set_quota_day(unsigned long val) +{ + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + global_settings.quota_day = val; + + // reset reclaimin_bytes_daily when set quota, so we can only + // set quota once per day. + if (stat) { + atomic64_set(&stat->reclaimin_bytes_daily, 0); + } +} + +bool hybridswap_reach_life_protect(void) +{ + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + unsigned long quota = hybridswap_quota_day(); + + if (hybridswap_dev_life()) + quota /= 10; + return atomic64_read(&stat->reclaimin_bytes_daily) > quota; +} + +void hybridswap_close_bdev(struct block_device *bdev, struct file *backing_dev) +{ + if (bdev) + blkdev_put(bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL); + + if (backing_dev) + filp_close(backing_dev, NULL); +} + +struct file *hybridswap_open_bdev(const char *file_name) +{ + struct file *backing_dev = NULL; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) + backing_dev = filp_open(file_name, O_RDWR|O_LARGEFILE, 0); +#else + backing_dev = filp_open_block(file_name, O_RDWR|O_LARGEFILE, 0); +#endif + if (unlikely(IS_ERR(backing_dev))) { + hybp(HYB_ERR, "open the %s failed! eno = %ld\n", + file_name, PTR_ERR(backing_dev)); + backing_dev = NULL; + return NULL; + } + + if (unlikely(!S_ISBLK(backing_dev->f_mapping->host->i_mode))) { + hybp(HYB_ERR, "%s isn't a blk device\n", file_name); + hybridswap_close_bdev(NULL, backing_dev); + return NULL; + } + + return backing_dev; +} + +int hybridswap_bind(struct zram *zram, const char *file_name) +{ + struct file *backing_dev = NULL; + struct inode *inode = NULL; + unsigned long nr_pages; + struct block_device *bdev = NULL; + int err; + + backing_dev = hybridswap_open_bdev(file_name); + if (unlikely(!backing_dev)) + return -EINVAL; + + inode = backing_dev->f_mapping->host; + bdev = blkdev_get_by_dev(inode->i_rdev, + FMODE_READ | FMODE_WRITE | FMODE_EXCL, zram); + if (IS_ERR(bdev)) { + hybp(HYB_ERR, "%s blkdev_fetch failed!\n", file_name); + err = PTR_ERR(bdev); + bdev = NULL; + goto out; + } + + nr_pages = (unsigned long)i_size_read(inode) >> PAGE_SHIFT; + err = set_blocksize(bdev, PAGE_SIZE); + if (unlikely(err)) { + hybp(HYB_ERR, + "%s set blocksize failed! eno = %d\n", file_name, err); + goto out; + } + + zram->bdev = bdev; + zram->backing_dev = backing_dev; + zram->nr_pages = nr_pages; + return 0; + +out: + hybridswap_close_bdev(bdev, backing_dev); + + return err; +} + +static inline unsigned long fetch_original_used_swap(void) +{ + struct sysinfo val; + + si_swapinfo(&val); + + return val.totalswap - val.freeswap; +} + +void hybstatus_init(struct hybstatus *stat) +{ + int i; + + atomic64_set(&stat->reclaimin_cnt, 0); + atomic64_set(&stat->reclaimin_bytes, 0); + atomic64_set(&stat->reclaimin_real_load, 0); + atomic64_set(&stat->dropped_eswap_size, 0); + atomic64_set(&stat->reclaimin_bytes_daily, 0); + atomic64_set(&stat->reclaimin_pages, 0); + atomic64_set(&stat->reclaimin_infight, 0); + atomic64_set(&stat->batchout_cnt, 0); + atomic64_set(&stat->batchout_bytes, 0); + atomic64_set(&stat->batchout_real_load, 0); + atomic64_set(&stat->batchout_pages, 0); + atomic64_set(&stat->batchout_inflight, 0); + atomic64_set(&stat->fault_cnt, 0); + atomic64_set(&stat->hybridswap_fault_cnt, 0); + atomic64_set(&stat->reout_pages, 0); + atomic64_set(&stat->reout_bytes, 0); + atomic64_set(&stat->zram_stored_pages, 0); + atomic64_set(&stat->zram_stored_size, 0); + atomic64_set(&stat->stored_pages, 0); + atomic64_set(&stat->stored_size, 0); + atomic64_set(&stat->notify_free, 0); + atomic64_set(&stat->frag_cnt, 0); + atomic64_set(&stat->mcg_cnt, 0); + atomic64_set(&stat->eswap_cnt, 0); + atomic64_set(&stat->miss_free, 0); + atomic64_set(&stat->memcgid_clear, 0); + atomic64_set(&stat->skip_track_cnt, 0); + atomic64_set(&stat->null_memcg_skip_track_cnt, 0); + atomic64_set(&stat->used_swap_pages, fetch_original_used_swap()); + atomic64_set(&stat->stored_wm_scale, DEFAULT_STORED_WM_RATIO); + + for (i = 0; i < HYB_CLASS_BUTT; ++i) { + atomic64_set(&stat->io_fail_cnt[i], 0); + atomic64_set(&stat->alloc_fail_cnt[i], 0); + atomic64_set(&stat->lat[i].total_lat, 0); + atomic64_set(&stat->lat[i].max_lat, 0); + } + + stat->record.num = 0; + spin_lock_init(&stat->record.lock); +} + +static bool hybridswap_global_setting_init(struct zram *zram) +{ + if (unlikely(global_settings.stat)) + return false; + + global_settings.zram = zram; + hybridswap_set_enable(false); + global_settings.stat = hybridswap_malloc( + sizeof(struct hybstatus), false, true); + if (unlikely(!global_settings.stat)) { + hybp(HYB_ERR, "global stat allocation failed!\n"); + return false; + } + + hybstatus_init(global_settings.stat); + global_settings.reclaim_wq = alloc_workqueue("hybridswap_reclaim", + WQ_CPU_INTENSIVE, 0); + if (unlikely(!global_settings.reclaim_wq)) { + hybp(HYB_ERR, "reclaim workqueue allocation failed!\n"); + hybridswap_free(global_settings.stat); + global_settings.stat = NULL; + + return false; + } + + global_settings.quota_day = HYBRIDSWAP_QUOTA_DAY; + + hybp(HYB_DEBUG, "global settings init success\n"); + return true; +} + +void hybridswap_global_setting_deinit(void) +{ + destroy_workqueue(global_settings.reclaim_wq); + hybridswap_free(global_settings.stat); + global_settings.stat = NULL; + global_settings.zram = NULL; + global_settings.reclaim_wq = NULL; +} + +struct workqueue_struct *hybridswap_fetch_reclaim_workqueue(void) +{ + return global_settings.reclaim_wq; +} + +static int hybridswap_core_init(struct zram *zram) +{ + int ret; + + if (loop_device[0] == '\0') { + hybp(HYB_ERR, "please setting loop_device first\n"); + return -EINVAL; + } + + if (!hybridswap_global_setting_init(zram)) + return -EINVAL; + + ret = hybridswap_bind(zram, loop_device); + if (unlikely(ret)) { + hybp(HYB_ERR, "bind storage device failed! %d\n", ret); + hybridswap_global_setting_deinit(); + } + + return 0; +} + +int hybridswap_set_enable_init(bool en) +{ + int ret; + + if (hybridswap_core_enabled() || !en) + return 0; + + if (!global_settings.stat) { + hybp(HYB_ERR, "global_settings.stat is null!\n"); + + return -EINVAL; + } + + ret = hybridswap_manager_init(global_settings.zram); + if (unlikely(ret)) { + hybp(HYB_ERR, "init manager failed! %d\n", ret); + + return -EINVAL; + } + + ret = hyb_io_work_begin(); + if (unlikely(ret)) { + hybp(HYB_ERR, "init schedule failed! %d\n", ret); + hybridswap_manager_deinit(global_settings.zram); + + return -EINVAL; + } + + return 0; +} + +ssize_t hybridswap_core_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (unlikely(ret)) { + hybp(HYB_ERR, "val is error!\n"); + + return -EINVAL; + } + + if (hybridswap_set_enable_init(!!val)) + return -EINVAL; + + hybridswap_set_enable(!!val); + + return len; +} + +ssize_t hybridswap_core_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int len = snprintf(buf, PAGE_SIZE, "hybridswap %s out_to_eswap %s\n", + hybridswap_core_enabled() ? "enable" : "disable", + hybridswap_out_to_eswap_enable() ? "enable" : "disable"); + + return len; +} + +ssize_t hybridswap_loop_device_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram; + int ret = 0; + + if (len > (DEVICE_NAME_LEN - 1)) { + hybp(HYB_ERR, "buf %s len %d is too long\n", buf, (int)len); + return -EINVAL; + } + + memcpy(loop_device, buf, len); + loop_device[len] = '\0'; + (void) strstrip(loop_device); + + zram = dev_to_zram(dev); + down_write(&zram->init_lock); + if (zram->disksize == 0) { + hybp(HYB_ERR, "disksize is 0\n"); + goto out; + } + + ret = hybridswap_core_init(zram); + if (ret) + hybp(HYB_ERR, "hybridswap_core_init init failed\n"); + +out: + up_write(&zram->init_lock); + return len; +} + +ssize_t hybridswap_loop_device_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int len = 0; + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + if (!zram->backing_dev) { + memcpy(buf, "none\n", 5); + up_read(&zram->init_lock); + return 5; + } + up_read(&zram->init_lock); + + len = sprintf(buf, "%s\n", loop_device); + + return len; +} + +ssize_t hybridswap_dev_life_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (unlikely(ret)) { + hybp(HYB_ERR, "val is error!\n"); + + return -EINVAL; + } + + hybridswap_set_dev_life(!!val); + + return len; +} + +ssize_t hybridswap_dev_life_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int len = 0; + + len = sprintf(buf, "%s\n", + hybridswap_dev_life() ? "enable" : "disable"); + + return len; +} + +ssize_t hybridswap_quota_day_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int ret; + unsigned long val; + + ret = kstrtoul(buf, 0, &val); + if (unlikely(ret)) { + hybp(HYB_ERR, "val is error!\n"); + + return -EINVAL; + } + + hybridswap_set_quota_day(val); + + return len; +} + +ssize_t hybridswap_quota_day_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int len = 0; + + len = sprintf(buf, "%lu\n", hybridswap_quota_day()); + + return len; +} + +ssize_t hybridswap_zram_increase_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + char *type_buf = NULL; + unsigned long val; + struct zram *zram = dev_to_zram(dev); + + type_buf = strstrip((char *)buf); + if (kstrtoul(type_buf, 0, &val)) + return -EINVAL; + + zram->increase_nr_pages = (val << 8); + return len; +} + +ssize_t hybridswap_zram_increase_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t size = 0; + struct zram *zram = dev_to_zram(dev); + + size += scnprintf(buf + size, PAGE_SIZE - size, + "%lu\n", zram->increase_nr_pages >> 8); + + return size; +} + +int mem_cgroup_stored_wm_scale_write( + struct cgroup_subsys_state *css, struct cftype *cft, s64 val) +{ + if (val > MAX_RATIO || val < MIN_RATIO) + return -EINVAL; + + if (!global_settings.stat) + return -EINVAL; + + atomic64_set(&global_settings.stat->stored_wm_scale, val); + + return 0; +} + +s64 mem_cgroup_stored_wm_scale_read( + struct cgroup_subsys_state *css, struct cftype *cft) +{ + if (!global_settings.stat) + return -EINVAL; + + return atomic64_read(&global_settings.stat->stored_wm_scale); +} + +int hybridswap_stored_info(unsigned long *total, unsigned long *used) +{ + if (!total || !used) + return -EINVAL; + + if (!global_settings.stat || !global_settings.zram) { + *total = 0; + *used = 0; + return 0; + } + + *used = atomic64_read(&global_settings.stat->eswap_cnt) * ESWAP_PG_CNT; + *total = global_settings.zram->nr_pages; + + return 0; +} + +bool hybridswap_stored_wm_ok(void) +{ + unsigned long scale, stored_pages, total_pages, wm_scale; + int ret; + + if (!hybridswap_core_enabled()) + return false; + + ret = hybridswap_stored_info(&total_pages, &stored_pages); + if (ret) + return false; + + scale = (stored_pages * 100) / (total_pages + 1); + wm_scale = atomic64_read(&global_settings.stat->stored_wm_scale); + + return scale <= wm_scale; +} + +int hybridswap_core_enable(void) +{ + int ret; + + ret = hybridswap_set_enable_init(true); + if (ret) { + hybp(HYB_ERR, "set true failed, ret=%d\n", ret); + return ret; + } + + hybridswap_set_enable(true); + return 0; +} + +void hybridswap_core_disable(void) +{ + (void)hybridswap_set_enable_init(false); + hybridswap_set_enable(false); +} + +static void hybridswap_memcg_iter( + int (*iter)(struct mem_cgroup *, void *), void *data) +{ + struct mem_cgroup *mcg = fetch_next_memcg(NULL); + int ret; + + while (mcg) { + ret = iter(mcg, data); + hybp(HYB_DEBUG, "%pS mcg %d %s %s, ret %d\n", + iter, mcg->id.id, + MEMCGRP_ITEM(mcg, name), + ret ? "failed" : "pass", + ret); + if (ret) { + fetch_next_memcg_break(mcg); + return; + } + mcg = fetch_next_memcg(mcg); + } +} + +void hybridswap_record(struct zram *zram, u32 index, + struct mem_cgroup *memcg) +{ + memcg_hybs_t *hybs; + struct hybstatus *stat; + + if (!hybridswap_core_enabled()) + return; + + if (!memcg || !memcg->id.id) { + stat = hybridswap_fetch_stat_obj(); + if (stat) + atomic64_inc(&stat->null_memcg_skip_track_cnt); + return; + } + + hybs = MEMCGRP_ITEM_DATA(memcg); + if (!hybs) { + hybs = hybridswap_cache_alloc(memcg, false); + if (!hybs) { + stat = hybridswap_fetch_stat_obj(); + if (stat) + atomic64_inc(&stat->skip_track_cnt); + return; + } + } + + if (unlikely(!hybs->zram)) { + spin_lock(&hybs->zram_init_lock); + if (!hybs->zram) + hybridswap_manager_memcg_init(zram, memcg); + spin_unlock(&hybs->zram_init_lock); + } + + hybridswap_swap_sorted_list_add(zram, index, memcg); + +#ifdef CONFIG_HYBRIDSWAP_SWAPD + zram_slot_unlock(zram, index); + if (!zram_watermark_ok()) + wake_all_swapd(); + zram_slot_lock(zram, index); +#endif +} + +void hybridswap_untrack(struct zram *zram, u32 index) +{ + if (!hybridswap_core_enabled()) + return; + + while (zram_test_flag(zram, index, ZRAM_UNDER_WB) || + zram_test_flag(zram, index, ZRAM_BATCHING_OUT)) { + zram_slot_unlock(zram, index); + udelay(50); + zram_slot_lock(zram, index); + } + + hybridswap_swap_sorted_list_del(zram, index); +} + +static unsigned long memcg_reclaim_size(struct mem_cgroup *memcg) +{ + memcg_hybs_t *hybs = MEMCGRP_ITEM_DATA(memcg); + unsigned long zram_size, cur_size, new_size; + + if (!hybs) + return 0; + + zram_size = atomic64_read(&hybs->zram_stored_size); + if (hybs->force_swapout) { + hybs->can_eswaped = zram_size; + return zram_size; + } + + cur_size = atomic64_read(&hybs->hybridswap_stored_size); + new_size = (zram_size + cur_size) * + atomic_read(&hybs->zram2ufs_scale) / 100; + + hybs->can_eswaped = (new_size > cur_size) ? (new_size - cur_size) : 0; + return hybs->can_eswaped; +} + +static int hybridswap_permcg_sz(struct mem_cgroup *memcg, void *data) +{ + unsigned long *out_size = (unsigned long *)data; + + *out_size += memcg_reclaim_size(memcg); + return 0; +} + +static void hybridswap_flush_cb(enum hybridswap_class class, + void *pri, struct hybridswap_io_req *req) +{ + switch (class) { + case HYB_FAULT_OUT: + case HYB_PRE_OUT: + case HYB_BATCH_OUT: + hybridswap_eswap_destroy(pri, class); + break; + case HYB_RECLAIM_IN: + hybridswap_eswap_register(pri, req); + break; + default: + break; + } +} + +static void hybridswap_flush_done(struct hybridswap_entry *ioentry, + int err, struct hybridswap_io_req *req) +{ + struct io_priv *data; + + if (unlikely(!ioentry)) + return; + + data = (struct io_priv *)(ioentry->private); + if (likely(!err)) { + hybridswap_flush_cb(data->class, + ioentry->manager_private, req); +#ifdef CONFIG_HYBRIDSWAP_SWAPD + if (!zram_watermark_ok()) + wake_all_swapd(); +#endif + } else { + hybridswap_eswap_exception(data->class, + ioentry->manager_private); + } + hybridswap_free(ioentry); +} + +static void hybridswap_free_pagepool(struct io_work_arg *iowork) +{ + struct page *free_page = NULL; + + spin_lock(&iowork->data.page_pool.page_pool_lock); + while (!list_empty(&iowork->data.page_pool.page_pool_list)) { + free_page = list_first_entry( + &iowork->data.page_pool.page_pool_list, + struct page, lru); + list_del_init(&free_page->lru); + __free_page(free_page); + } + spin_unlock(&iowork->data.page_pool.page_pool_lock); +} + +static void hybridswap_plug_complete(void *data) +{ + struct io_work_arg *iowork = (struct io_work_arg *)data; + + hybridswap_free_pagepool(iowork); + + hybperf_end(&iowork->record); + + hybridswap_free(iowork); +} + +static void *hybridswap_init_plug(struct zram *zram, + enum hybridswap_class class, + struct io_work_arg *iowork) +{ + struct hybridswap_io io_para; + + io_para.bdev = zram->bdev; + io_para.class = class; + io_para.private = (void *)iowork; + io_para.record = &iowork->record; + INIT_LIST_HEAD(&iowork->data.page_pool.page_pool_list); + spin_lock_init(&iowork->data.page_pool.page_pool_lock); + io_para.done_callback = hybridswap_flush_done; + switch (io_para.class) { + case HYB_RECLAIM_IN: + io_para.complete_notify = hybridswap_plug_complete; + iowork->io_buf.pool = NULL; + break; + case HYB_BATCH_OUT: + case HYB_PRE_OUT: + io_para.complete_notify = hybridswap_plug_complete; + iowork->io_buf.pool = &iowork->data.page_pool; + break; + case HYB_FAULT_OUT: + io_para.complete_notify = NULL; + iowork->io_buf.pool = NULL; + break; + default: + break; + } + iowork->io_buf.zram = zram; + iowork->data.zram = zram; + iowork->data.class = io_para.class; + return hybridswap_plug_start(&io_para); +} + +static void hybridswap_fill_entry(struct hybridswap_entry *ioentry, + struct hybridswap_buffer *io_buf, + void *private) +{ + ioentry->addr = ioentry->eswapid * ESWAP_SECTOR_SIZE; + ioentry->dest_pages = io_buf->dest_pages; + ioentry->pages_sz = ESWAP_PG_CNT; + ioentry->private = private; +} + +static int hybridswap_reclaim_check(struct mem_cgroup *memcg, + unsigned long *require_size) +{ + memcg_hybs_t *hybs = MEMCGRP_ITEM_DATA(memcg); + + if (unlikely(!hybs) || unlikely(!hybs->zram)) + return -EAGAIN; + if (unlikely(hybs->in_swapin)) + return -EAGAIN; + if (!hybs->force_swapout && *require_size < MIN_RECLAIM_ZRAM_SZ) + return -EAGAIN; + + return 0; +} + +static int hybridswap_update_reclaim_sz(unsigned long *require_size, + unsigned long *mcg_reclaimed_sz, + unsigned long reclaim_size) +{ + *mcg_reclaimed_sz += reclaim_size; + + if (*require_size > reclaim_size) + *require_size -= reclaim_size; + else + *require_size = 0; + + return 0; +} + +static void hybstatus_alloc_fail(enum hybridswap_class class, + int err) +{ + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + + if (!stat || (err != -ENOMEM) || (class >= HYB_CLASS_BUTT)) + return; + + atomic64_inc(&stat->alloc_fail_cnt[class]); +} + +static int hybridswap_reclaim_eswap(struct mem_cgroup *memcg, + struct io_work_arg *iowork, + unsigned long *require_size, + unsigned long *mcg_reclaimed_sz, + int *errio) +{ + int ret; + unsigned long reclaim_size; + + hybperfiowrkstart(&iowork->record, HYB_IOENTRY_ALLOC); + iowork->ioentry = hybridswap_malloc( + sizeof(struct hybridswap_entry), false, true); + hybperfiowrkend(&iowork->record, HYB_IOENTRY_ALLOC); + if (unlikely(!iowork->ioentry)) { + hybp(HYB_ERR, "alloc io entry failed!\n"); + *require_size = 0; + *errio = -ENOMEM; + hybstatus_alloc_fail(HYB_RECLAIM_IN, -ENOMEM); + + return *errio; + } + + hybperfiowrkstart(&iowork->record, HYB_FIND_ESWAP); + reclaim_size = hybridswap_eswap_create( + memcg, &iowork->ioentry->eswapid, + &iowork->io_buf, &iowork->ioentry->manager_private); + hybperfiowrkend(&iowork->record, HYB_FIND_ESWAP); + if (unlikely(!reclaim_size)) { + if (iowork->ioentry->eswapid != -ENOENT) + *require_size = 0; + hybridswap_free(iowork->ioentry); + return -EAGAIN; + } + + hybridswap_fill_entry(iowork->ioentry, &iowork->io_buf, + (void *)(&iowork->data)); + + hybperfiowrkstart(&iowork->record, HYB_IO_ESWAP); + ret = hybridswap_write_eswap(iowork->iohandle, iowork->ioentry); + hybperfiowrkend(&iowork->record, HYB_IO_ESWAP); + if (unlikely(ret)) { + hybp(HYB_ERR, "hybridswap write failed! %d\n", ret); + *require_size = 0; + *errio = ret; + hybstatus_alloc_fail(HYB_RECLAIM_IN, ret); + + return *errio; + } + + ret = hybridswap_update_reclaim_sz(require_size, mcg_reclaimed_sz, + reclaim_size); + if (MEMCGRP_ITEM(memcg, force_swapout)) + return 0; + return ret; +} + +static int hybridswap_permcg_reclaim(struct mem_cgroup *memcg, + unsigned long require_size, unsigned long *mcg_reclaimed_sz) +{ + int ret, extcnt; + int errio = 0; + unsigned long require_size_before = 0; + struct io_work_arg *iowork = NULL; + ktime_t start = ktime_get(); + unsigned long long start_ravg_sum = hybridswap_fetch_ravg_sum(); + memcg_hybs_t *hybs = MEMCGRP_ITEM_DATA(memcg); + + ret = hybridswap_reclaim_check(memcg, &require_size); + if (ret) + return ret == -EAGAIN ? 0 : ret; + + iowork = hybridswap_malloc(sizeof(struct io_work_arg), false, true); + if (unlikely(!iowork)) { + hybp(HYB_ERR, "alloc iowork failed!\n"); + hybstatus_alloc_fail(HYB_RECLAIM_IN, -ENOMEM); + + return -ENOMEM; + } + + hybperf_start(&iowork->record, start, start_ravg_sum, + HYB_RECLAIM_IN); + hybperfiowrkstart(&iowork->record, HYB_INIT); + iowork->iohandle = hybridswap_init_plug(hybs->zram, + HYB_RECLAIM_IN, iowork); + hybperfiowrkend(&iowork->record, HYB_INIT); + if (unlikely(!iowork->iohandle)) { + hybp(HYB_ERR, "plug start failed!\n"); + hybperf_end(&iowork->record); + hybridswap_free(iowork); + hybstatus_alloc_fail(HYB_RECLAIM_IN, -ENOMEM); + ret = -EIO; + goto out; + } + + require_size_before = require_size; + while (require_size) { + if (hybridswap_reclaim_eswap(memcg, iowork, + &require_size, mcg_reclaimed_sz, &errio)) + break; + + atomic64_inc(&hybs->hybridswap_outextcnt); + extcnt = atomic_inc_return(&hybs->hybridswap_extcnt); + if (extcnt > atomic_read(&hybs->hybridswap_peakextcnt)) + atomic_set(&hybs->hybridswap_peakextcnt, extcnt); + } + + ret = hybridswap_plug_finish(iowork->iohandle); + if (unlikely(ret)) { + hybp(HYB_ERR, "hybridswap write flush failed! %d\n", ret); + hybstatus_alloc_fail(HYB_RECLAIM_IN, ret); + require_size = 0; + } else { + ret = errio; + } + atomic64_inc(&hybs->hybridswap_outcnt); + +out: + hybp(HYB_DEBUG, "memcg %s %lu %lu out_to_eswap %lu KB eswap %lu zram %lu %d\n", + hybs->name, (unsigned long)require_size_before, (unsigned long)require_size, + (unsigned long)((require_size_before - require_size) >> 10), + (unsigned long)atomic64_read(&hybs->hybridswap_stored_size), + (unsigned long)atomic64_read(&hybs->zram_stored_size), ret); + return ret; +} + +static void hybridswap_reclaimin_inc(void) +{ + struct hybstatus *stat; + + stat = hybridswap_fetch_stat_obj(); + if (unlikely(!stat)) + return; + atomic64_inc(&stat->reclaimin_infight); +} + +static void hybridswap_reclaimin_dec(void) +{ + struct hybstatus *stat; + + stat = hybridswap_fetch_stat_obj(); + if (unlikely(!stat)) + return; + atomic64_dec(&stat->reclaimin_infight); +} + +static int hybridswap_permcg_reclaimin(struct mem_cgroup *memcg, + void *data) +{ + struct async_req *rq = (struct async_req *)data; + unsigned long mcg_reclaimed_size = 0, require_size; + int ret; + memcg_hybs_t *hybs; + + hybs = MEMCGRP_ITEM_DATA(memcg); + if (!hybs) + return 0; + + require_size = hybs->can_eswaped * rq->size / rq->out_size; + if (require_size < MIN_RECLAIM_ZRAM_SZ) + return 0; + + if (!mutex_trylock(&hybs->swap_lock)) + return 0; + + ret = hybridswap_permcg_reclaim(memcg, require_size, + &mcg_reclaimed_size); + rq->reclaimined_sz += mcg_reclaimed_size; + mutex_unlock(&hybs->swap_lock); + + hybp(HYB_DEBUG, "memcg %s mcg_reclaimed_size %lu rq->reclaimined_sz %lu rq->size %lu rq->out_size %lu ret %d\n", + hybs->name, mcg_reclaimed_size, rq->reclaimined_sz, + rq->size, rq->out_size, ret); + + if (!ret && rq->reclaimined_sz >= rq->size) + return -EINVAL; + + return ret; +} + +static void hybridswap_reclaim_work(struct work_struct *work) +{ + struct async_req *rq = container_of(work, struct async_req, work); + int old_nice = task_nice(current); + + set_user_nice(current, rq->nice); + hybridswap_reclaimin_inc(); + hybridswap_memcg_iter(hybridswap_permcg_reclaimin, rq); + hybridswap_reclaimin_dec(); + set_user_nice(current, old_nice); + hybp(HYB_INFO, "SWAPOUT want %lu MB real %lu Mb\n", rq->size >> 20, + rq->reclaimined_sz >> 20); + hybridswap_free(rq); +} + +unsigned long hybridswap_out_to_eswap(unsigned long size) +{ + struct async_req *rq = NULL; + unsigned long out_size = 0; + + if (!hybridswap_core_enabled() || !hybridswap_out_to_eswap_enable() + || hybridswap_reach_life_protect() || !size) + return 0; + + hybridswap_memcg_iter(hybridswap_permcg_sz, &out_size); + if (!out_size) + return 0; + + rq = hybridswap_malloc(sizeof(struct async_req), false, true); + if (unlikely(!rq)) { + hybp(HYB_ERR, "alloc async req fail!\n"); + hybstatus_alloc_fail(HYB_RECLAIM_IN, -ENOMEM); + return 0; + } + + if (out_size < size) + size = out_size; + rq->size = size; + rq->out_size = out_size; + rq->reclaimined_sz = 0; + rq->nice = task_nice(current); + INIT_WORK(&rq->work, hybridswap_reclaim_work); + queue_work(hybridswap_fetch_reclaim_workqueue(), &rq->work); + + return out_size > size ? size : out_size; +} + +static int hybridswap_batches_eswap(struct io_work_arg *iowork, + struct mem_cgroup *mcg, + bool preload, + int *errio) +{ + int ret; + + hybperfiowrkstart(&iowork->record, HYB_IOENTRY_ALLOC); + iowork->ioentry = hybridswap_malloc( + sizeof(struct hybridswap_entry), !preload, preload); + hybperfiowrkend(&iowork->record, HYB_IOENTRY_ALLOC); + if (unlikely(!iowork->ioentry)) { + hybp(HYB_ERR, "alloc io entry failed!\n"); + *errio = -ENOMEM; + hybstatus_alloc_fail(HYB_BATCH_OUT, -ENOMEM); + + return *errio; + } + + hybperfiowrkstart(&iowork->record, HYB_FIND_ESWAP); + iowork->ioentry->eswapid = hybridswap_find_eswap_by_memcg( + mcg, &iowork->io_buf, + &iowork->ioentry->manager_private); + hybperfiowrkend(&iowork->record, HYB_FIND_ESWAP); + if (iowork->ioentry->eswapid < 0) { + hybstatus_alloc_fail(HYB_BATCH_OUT, + iowork->ioentry->eswapid); + hybridswap_free(iowork->ioentry); + return -EAGAIN; + } + + hybridswap_fill_entry(iowork->ioentry, &iowork->io_buf, + (void *)(&iowork->data)); + + hybperfiowrkstart(&iowork->record, HYB_IO_ESWAP); + ret = hybridswap_read_eswap(iowork->iohandle, iowork->ioentry); + hybperfiowrkend(&iowork->record, HYB_IO_ESWAP); + if (unlikely(ret)) { + hybp(HYB_ERR, "hybridswap read failed! %d\n", ret); + hybstatus_alloc_fail(HYB_BATCH_OUT, ret); + *errio = ret; + + return *errio; + } + + return 0; +} + +static int hybridswap_do_batches_init(struct io_work_arg **out_sched, + struct mem_cgroup *mcg, bool preload) +{ + struct io_work_arg *iowork = NULL; + ktime_t start = ktime_get(); + unsigned long long start_ravg_sum = hybridswap_fetch_ravg_sum(); + + iowork = hybridswap_malloc(sizeof(struct io_work_arg), + !preload, preload); + if (unlikely(!iowork)) { + hybp(HYB_ERR, "alloc iowork failed!\n"); + hybstatus_alloc_fail(HYB_BATCH_OUT, -ENOMEM); + + return -ENOMEM; + } + + hybperf_start(&iowork->record, start, start_ravg_sum, + preload ? HYB_PRE_OUT : HYB_BATCH_OUT); + + hybperfiowrkstart(&iowork->record, HYB_INIT); + iowork->iohandle = hybridswap_init_plug(MEMCGRP_ITEM(mcg, zram), + preload ? HYB_PRE_OUT : HYB_BATCH_OUT, + iowork); + hybperfiowrkend(&iowork->record, HYB_INIT); + if (unlikely(!iowork->iohandle)) { + hybp(HYB_ERR, "plug start failed!\n"); + hybperf_end(&iowork->record); + hybridswap_free(iowork); + hybstatus_alloc_fail(HYB_BATCH_OUT, -ENOMEM); + + return -EIO; + } + + *out_sched = iowork; + + return 0; +} + +static int hybridswap_do_batches(struct mem_cgroup *mcg, + unsigned long size, bool preload) +{ + int ret = 0; + int errio = 0; + struct io_work_arg *iowork = NULL; + + if (unlikely(!mcg || !MEMCGRP_ITEM(mcg, zram))) { + hybp(HYB_WARN, "no zram in mcg!\n"); + ret = -EINVAL; + goto out; + } + + ret = hybridswap_do_batches_init(&iowork, mcg, preload); + if (unlikely(ret)) + goto out; + + MEMCGRP_ITEM(mcg, in_swapin) = true; + while (size) { + if (hybridswap_batches_eswap(iowork, mcg, preload, &errio)) + break; + size -= ESWAP_SIZE; + } + + ret = hybridswap_plug_finish(iowork->iohandle); + if (unlikely(ret)) { + hybp(HYB_ERR, "hybridswap read flush failed! %d\n", ret); + hybstatus_alloc_fail(HYB_BATCH_OUT, ret); + } else { + ret = errio; + } + + if (atomic64_read(&MEMCGRP_ITEM(mcg, hybridswap_stored_size)) && + hybridswap_loglevel() >= HYB_INFO) + hybridswap_check_infos_eswap((MEMCGRP_ITEM(mcg, zram)->infos)); + + atomic64_inc(&MEMCGRP_ITEM(mcg, hybridswap_incnt)); + MEMCGRP_ITEM(mcg, in_swapin) = false; +out: + return ret; +} + +static void hybridswap_batchout_inc(void) +{ + struct hybstatus *stat; + + stat = hybridswap_fetch_stat_obj(); + if (unlikely(!stat)) + return; + atomic64_inc(&stat->batchout_inflight); +} + +static void hybridswap_batchout_dec(void) +{ + struct hybstatus *stat; + + stat = hybridswap_fetch_stat_obj(); + if (unlikely(!stat)) + return; + atomic64_dec(&stat->batchout_inflight); +} + +int hybridswap_batches(struct mem_cgroup *mcg, + unsigned long size, bool preload) +{ + int ret; + + if (!hybridswap_core_enabled()) + return 0; + + hybridswap_batchout_inc(); + ret = hybridswap_do_batches(mcg, size, preload); + hybridswap_batchout_dec(); + + return ret; +} + +static void hybridswap_fault_stat(struct zram *zram, u32 index) +{ + struct mem_cgroup *mcg = NULL; + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + + if (unlikely(!stat)) + return; + + atomic64_inc(&stat->fault_cnt); + + mcg = hybridswap_zram_fetch_mcg(zram, index); + if (mcg) + atomic64_inc(&MEMCGRP_ITEM(mcg, hybridswap_allfaultcnt)); +} + +static void hybridswap_fault2_stat(struct zram *zram, u32 index) +{ + struct mem_cgroup *mcg = NULL; + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + + if (unlikely(!stat)) + return; + + atomic64_inc(&stat->hybridswap_fault_cnt); + + mcg = hybridswap_zram_fetch_mcg(zram, index); + if (mcg) + atomic64_inc(&MEMCGRP_ITEM(mcg, hybridswap_faultcnt)); +} + +static bool hybridswap_page_fault_check(struct zram *zram, + u32 index, unsigned long *zentry) +{ + if (!hybridswap_core_enabled()) + return false; + + hybridswap_fault_stat(zram, index); + + if (!zram_test_flag(zram, index, ZRAM_WB)) + return false; + + zram_set_flag(zram, index, ZRAM_BATCHING_OUT); + *zentry = zram_get_handle(zram, index); + zram_slot_unlock(zram, index); + return true; +} + +static int hybridswap_page_fault_fetch_eswap(struct zram *zram, + struct io_work_arg *iowork, + unsigned long zentry, + u32 index) +{ + int wait_cycle = 0; + + iowork->io_buf.zram = zram; + iowork->data.zram = zram; + iowork->io_buf.pool = NULL; + hybperfiowrkstart(&iowork->record, HYB_FIND_ESWAP); + iowork->ioentry->eswapid = hybridswap_find_eswap_by_index(zentry, + &iowork->io_buf, &iowork->ioentry->manager_private); + hybperfiowrkend(&iowork->record, HYB_FIND_ESWAP); + if (unlikely(iowork->ioentry->eswapid == -EBUSY)) { + while (1) { + zram_slot_lock(zram, index); + if (!zram_test_flag(zram, index, ZRAM_WB)) { + zram_slot_unlock(zram, index); + hybridswap_free(iowork->ioentry); +#ifdef CONFIG_HYBRIDSWAP_SWAPD + if (wait_cycle >= 1000) + atomic_long_dec(&page_fault_pause); +#endif + return -EAGAIN; + } + zram_slot_unlock(zram, index); + + hybperfiowrkstart(&iowork->record, + HYB_FIND_ESWAP); + iowork->ioentry->eswapid = + hybridswap_find_eswap_by_index(zentry, + &iowork->io_buf, + &iowork->ioentry->manager_private); + hybperfiowrkend(&iowork->record, + HYB_FIND_ESWAP); + if (likely(iowork->ioentry->eswapid != -EBUSY)) + break; + + // Moto: waited enough, exit with EIO. + if (wait_cycle > 100000) { + hybp(HYB_ERR, "waited 100000 cycles in page fault, exit with EIO!\n"); + return -EIO; + } + + if (wait_cycle < 100) + udelay(50); + else + usleep_range(50, 100); + wait_cycle++; +#ifdef CONFIG_HYBRIDSWAP_SWAPD + if (wait_cycle == 1000) { + atomic_long_inc(&page_fault_pause); + atomic_long_inc(&page_fault_pause_cnt); + } +#endif + } + } +#ifdef CONFIG_HYBRIDSWAP_SWAPD + if (wait_cycle >= 1000) + atomic_long_dec(&page_fault_pause); +#endif + if (iowork->ioentry->eswapid < 0) { + hybstatus_alloc_fail(HYB_FAULT_OUT, + iowork->ioentry->eswapid); + + return iowork->ioentry->eswapid; + } + hybridswap_fault2_stat(zram, index); + hybridswap_fill_entry(iowork->ioentry, &iowork->io_buf, + (void *)(&iowork->data)); + return 0; +} + +static int hybridswap_page_fault_exit_check(struct zram *zram, + u32 index, int ret) +{ + zram_slot_lock(zram, index); + if (likely(!ret)) { + if (unlikely(zram_test_flag(zram, index, ZRAM_WB))) { + hybp(HYB_ERR, "still in WB status!\n"); + ret = -EIO; + } + } + zram_clear_flag(zram, index, ZRAM_BATCHING_OUT); + + return ret; +} + +static int hybridswap_page_fault_eswap(struct zram *zram, u32 index, + struct io_work_arg *iowork, unsigned long zentry) +{ + int ret; + + hybperfiowrkstart(&iowork->record, HYB_IOENTRY_ALLOC); + iowork->ioentry = hybridswap_malloc(sizeof(struct hybridswap_entry), + true, true); + hybperfiowrkend(&iowork->record, HYB_IOENTRY_ALLOC); + if (unlikely(!iowork->ioentry)) { + hybp(HYB_ERR, "alloc io entry failed!\n"); + hybstatus_alloc_fail(HYB_FAULT_OUT, -ENOMEM); + hybridswap_fail_record(HYB_FAULT_OUT_ENTRY_ALLOC_FAIL, + index, 0, iowork->record.task_comm); + return -ENOMEM; + } + + ret = hybridswap_page_fault_fetch_eswap(zram, iowork, zentry, index); + if (ret) + return ret; + + hybperfiowrkstart(&iowork->record, HYB_IO_ESWAP); + ret = hybridswap_read_eswap(iowork->iohandle, iowork->ioentry); + hybperfiowrkend(&iowork->record, HYB_IO_ESWAP); + if (unlikely(ret)) { + hybp(HYB_ERR, "hybridswap read failed! %d\n", ret); + hybstatus_alloc_fail(HYB_FAULT_OUT, ret); + } + + return ret; +} + +int hybridswap_page_fault(struct zram *zram, u32 index) +{ + int ret = 0; + int errio; + struct io_work_arg iowork; + unsigned long zentry; + ktime_t start = ktime_get(); + unsigned long long start_ravg_sum = hybridswap_fetch_ravg_sum(); + + if (!hybridswap_page_fault_check(zram, index, &zentry)) + return ret; + + memset(&iowork.record, 0, sizeof(struct hybridswap_key_point_record)); + hybperf_start(&iowork.record, start, start_ravg_sum, + HYB_FAULT_OUT); + + hybperfiowrkstart(&iowork.record, HYB_INIT); + iowork.iohandle = hybridswap_init_plug(zram, + HYB_FAULT_OUT, &iowork); + hybperfiowrkend(&iowork.record, HYB_INIT); + if (unlikely(!iowork.iohandle)) { + hybp(HYB_ERR, "plug start failed!\n"); + hybstatus_alloc_fail(HYB_FAULT_OUT, -ENOMEM); + ret = -EIO; + hybridswap_fail_record(HYB_FAULT_OUT_INIT_FAIL, + index, 0, iowork.record.task_comm); + + goto out; + } + + errio = hybridswap_page_fault_eswap(zram, index, &iowork, zentry); + ret = hybridswap_plug_finish(iowork.iohandle); + if (unlikely(ret)) { + hybp(HYB_ERR, "hybridswap flush failed! %d\n", ret); + hybstatus_alloc_fail(HYB_FAULT_OUT, ret); + } else { + ret = (errio != -EAGAIN) ? errio : 0; + } +out: + hybperfiowrkstart(&iowork.record, HYB_ZRAM_LOCK); + ret = hybridswap_page_fault_exit_check(zram, index, ret); + hybperfiowrkend(&iowork.record, HYB_ZRAM_LOCK); + hybperf_end(&iowork.record); + + return ret; +} + +bool hybridswap_delete(struct zram *zram, u32 index) +{ + if (!hybridswap_core_enabled()) + return true; + + if (zram_test_flag(zram, index, ZRAM_UNDER_WB) + || zram_test_flag(zram, index, ZRAM_BATCHING_OUT)) { + struct hybstatus *stat = hybridswap_fetch_stat_obj(); + + if (stat) + atomic64_inc(&stat->miss_free); + return false; + } + + if (!zram_test_flag(zram, index, ZRAM_WB)) + return true; + + hybridswap_eswap_objs_del(zram, index); + + return true; +} + +void hybridswap_mem_cgroup_deinit(struct mem_cgroup *memcg) +{ + if (!hybridswap_core_enabled()) + return; + + hybridswap_manager_memcg_deinit(memcg); +} + +void hybridswap_force_reclaim(struct mem_cgroup *mcg) +{ + unsigned long mcg_reclaimed_size = 0, require_size; + memcg_hybs_t *hybs; + + if (!hybridswap_core_enabled() || !hybridswap_out_to_eswap_enable() + || hybridswap_reach_life_protect()) + return; + + if (!mcg) + return; + + hybs = MEMCGRP_ITEM_DATA(mcg); + if (!hybs || !hybs->zram) + return; + + mutex_lock(&hybs->swap_lock); + require_size = atomic64_read(&hybs->zram_stored_size); + hybs->force_swapout = true; + hybridswap_permcg_reclaim(mcg, require_size, &mcg_reclaimed_size); + hybs->force_swapout = false; + mutex_unlock(&hybs->swap_lock); +} + +void mem_cgroup_id_remove_hook(void *data, struct mem_cgroup *memcg) +{ + if (!MEMCG_OEM_DATA(memcg)) + return; + + hybridswap_mem_cgroup_deinit(memcg); + hybp(HYB_DEBUG, "hybridswap remove mcg id = %d\n", memcg->id.id); +} diff --git a/drivers/moto_swap/hybridswap/hybridswap_internal.h b/drivers/moto_swap/hybridswap/hybridswap_internal.h new file mode 100644 index 000000000000..14cd84e1d6d1 --- /dev/null +++ b/drivers/moto_swap/hybridswap/hybridswap_internal.h @@ -0,0 +1,565 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020-2022 Oplus. All rights reserved. + */ + +#ifndef HYBRIDSWAP_INTERNAL_H +#define HYBRIDSWAP_INTERNAL_H + +#include +#include +#include +#include +#include + +#define ESWAP_SHIFT 15 +#define ESWAP_SIZE (1UL << ESWAP_SHIFT) +#define ESWAP_PG_CNT (ESWAP_SIZE >> PAGE_SHIFT) +#define ESWAP_SECTOR_SIZE (ESWAP_PG_CNT << 3) +#define ESWAP_MAX_OBJ_CNT (30 * ESWAP_PG_CNT) +#define ESWAP_MASK (~(ESWAP_SIZE - 1)) +#define ESWAP_ALIGN_UP(size) ((size + ESWAP_SIZE - 1) & ESWAP_MASK) + +#define MAX_FAIL_RECORD_NUM 4 +#define MAX_APP_GRADE 600 + +#define HYBRIDSWAP_QUOTA_DAY 0x280000000 /* 10G bytes */ +#define HYBRIDSWAP_CHECK_GAP 86400 /* 24 hour */ +#define MEM_CGROUP_NAME_MAX_LEN 32 + +#define MAX_RATIO 100 +#define MIN_RATIO 0 + +enum { + HYB_ERR = 0, + HYB_WARN, + HYB_INFO, + HYB_DEBUG, + HYB_MAX +}; + +void hybridswap_loglevel_set(int level); +int hybridswap_loglevel(void); + +#define DUMP_STACK_ON_ERR 0 +#define pt(l, f, ...) pr_err("[%s][%s]:"f, #l, __func__, ##__VA_ARGS__) +static inline void pr_none(void) {} +#define hybp(l, f, ...) do {\ + (l <= hybridswap_loglevel()) ? pt(l, f, ##__VA_ARGS__) : pr_none();\ + if (DUMP_STACK_ON_ERR && l == HYB_ERR) dump_stack();\ +} while (0) + +enum hybridswap_class { + HYB_RECLAIM_IN = 0, + HYB_FAULT_OUT, + HYB_BATCH_OUT, + HYB_PRE_OUT, + HYB_CLASS_BUTT +}; + +enum hybridswap_key_point { + HYB_START = 0, + HYB_INIT, + HYB_IOENTRY_ALLOC, + HYB_FIND_ESWAP, + HYB_IO_ESWAP, + HYB_SEGMENT_ALLOC, + HYB_BIO_ALLOC, + HYB_SUBMIT_BIO, + HYB_END_IO, + HYB_SCHED_WORK, + HYB_END_WORK, + HYB_CALL_BACK, + HYB_WAKE_UP, + HYB_ZRAM_LOCK, + HYB_DONE, + HYB_KYE_POINT_BUTT +}; + +enum hybridswap_mcg_member { + MCG_ZRAM_STORED_SZ = 0, + MCG_ZRAM_STORED_PG_SZ, + MCG_DISK_STORED_SZ, + MCG_DISK_STORED_PG_SZ, + MCG_ANON_FAULT_CNT, + MCG_DISK_FAULT_CNT, + MCG_ESWAPOUT_CNT, + MCG_ESWAPOUT_SZ, + MCG_ESWAPIN_CNT, + MCG_ESWAPIN_SZ, + MCG_DISK_SPACE, + MCG_DISK_SPACE_PEAK, +}; + +enum hybridswap_fail_point { + HYB_FAULT_OUT_INIT_FAIL = 0, + HYB_FAULT_OUT_ENTRY_ALLOC_FAIL, + HYB_FAULT_OUT_IO_ENTRY_PARA_FAIL, + HYB_FAULT_OUT_SEGMENT_ALLOC_FAIL, + HYB_FAULT_OUT_BIO_ALLOC_FAIL, + HYB_FAULT_OUT_BIO_ADD_FAIL, + HYB_FAULT_OUT_IO_FAIL, + HYBRIDSWAP_FAIL_POINT_BUTT +}; + +struct hybridswap_fail_record { + unsigned char task_comm[TASK_COMM_LEN]; + enum hybridswap_fail_point point; + ktime_t time; + u32 index; + int eswapid; +}; + +struct hybridswap_fail_record_info { + int num; + spinlock_t lock; + struct hybridswap_fail_record record[MAX_FAIL_RECORD_NUM]; +}; + +struct hybridswap_key_point_info { + unsigned int record_cnt; + unsigned int end_cnt; + ktime_t first_time; + ktime_t last_time; + s64 proc_total_time; + s64 proc_max_time; + unsigned long long last_ravg_sum; + unsigned long long proc_ravg_sum; + spinlock_t time_lock; +}; + +struct hybridswap_key_point_record { + struct timer_list lat_monitor; + unsigned long warn_level; + int page_cnt; + int segment_cnt; + int nice; + bool timeout_flag; + unsigned char task_comm[TASK_COMM_LEN]; + struct task_struct *task; + enum hybridswap_class class; + struct hybridswap_key_point_info key_point[HYB_KYE_POINT_BUTT]; +}; + +struct hybridswapiowrkstat { + atomic64_t total_lat; + atomic64_t max_lat; + atomic64_t timeout_cnt; +}; + +struct hybridswap_fault_timeout_cnt{ + atomic64_t timeout_100ms_cnt; + atomic64_t timeout_500ms_cnt; +}; + +struct hybstatus { + atomic64_t reclaimin_cnt; + atomic64_t reclaimin_bytes; + atomic64_t reclaimin_real_load; + atomic64_t reclaimin_bytes_daily; + atomic64_t reclaimin_pages; + atomic64_t reclaimin_infight; + atomic64_t batchout_cnt; + atomic64_t batchout_bytes; + atomic64_t batchout_real_load; + atomic64_t batchout_pages; + atomic64_t batchout_inflight; + atomic64_t fault_cnt; + atomic64_t hybridswap_fault_cnt; + atomic64_t reout_pages; + atomic64_t reout_bytes; + atomic64_t zram_stored_pages; + atomic64_t zram_stored_size; + atomic64_t stored_pages; + atomic64_t stored_size; + atomic64_t notify_free; + atomic64_t frag_cnt; + atomic64_t mcg_cnt; + atomic64_t eswap_cnt; + atomic64_t miss_free; + atomic64_t memcgid_clear; + atomic64_t skip_track_cnt; + atomic64_t used_swap_pages; + atomic64_t null_memcg_skip_track_cnt; + atomic64_t stored_wm_scale; + atomic64_t dropped_eswap_size; + atomic64_t io_fail_cnt[HYB_CLASS_BUTT]; + atomic64_t alloc_fail_cnt[HYB_CLASS_BUTT]; + struct hybridswapiowrkstat lat[HYB_CLASS_BUTT]; + struct hybridswap_fault_timeout_cnt fault_stat[2]; /* 0:bg 1:fg */ + struct hybridswap_fail_record_info record; +}; + +struct hybridswap_page_pool { + struct list_head page_pool_list; + spinlock_t page_pool_lock; +}; + +struct io_eswapent { + int eswapid; + struct zram *zram; + struct mem_cgroup *mcg; + struct page *pages[ESWAP_PG_CNT]; + u32 index[ESWAP_MAX_OBJ_CNT]; + int cnt; + int real_load; + + struct hybridswap_page_pool *pool; +}; + +struct hybridswap_buffer { + struct zram *zram; + struct hybridswap_page_pool *pool; + struct page **dest_pages; +}; + +struct hybridswap_entry { + int eswapid; + sector_t addr; + struct page **dest_pages; + int pages_sz; + struct list_head list; + void *private; + void *manager_private; +}; + +struct hybridswap_io_req; +struct hybridswap_io { + struct block_device *bdev; + enum hybridswap_class class; + void (*done_callback)(struct hybridswap_entry *, int, struct hybridswap_io_req *); + void (*complete_notify)(void *); + void *private; + struct hybridswap_key_point_record *record; +}; + +struct hybridswap_io_req { + struct hybridswap_io io_para; + struct kref refcount; + struct mutex refmutex; + struct wait_queue_head io_wait; + atomic_t eswap_doing; + struct completion io_end_flag; + struct hyb_sgm *segment; + bool limit_doing_flag; + bool wait_io_finish_flag; + int page_cnt; + int segment_cnt; + int nice; + atomic64_t real_load; +}; + +/* Change hybridswap_event_item, you should change swapd_text togather*/ +enum hybridswap_event_item { +#ifdef CONFIG_HYBRIDSWAP_SWAPD + SWAPD_WAKEUP, + SWAPD_REFAULT, + SWAPD_MEMCG_RATIO_SKIP, + SWAPD_MEMCG_REFAULT_SKIP, + SWAPD_SHRINK_ANON, + SWAPD_SWAPOUT, + SWAPD_SKIP_SWAPOUT, + SWAPD_EMPTY_ROUND, + SWAPD_OVER_MIN_BUFFER_SKIP_TIMES, + SWAPD_EMPTY_ROUND_SKIP_TIMES, + SWAPD_SNAPSHOT_TIMES, + SWAPD_SKIP_SHRINK_OF_WINDOW, + SWAPD_MANUAL_PAUSE, +#ifdef CONFIG_OPLUS_JANK + SWAPD_CPU_BUSY_SKIP_TIMES, + SWAPD_CPU_BUSY_BREAK_TIMES, +#endif +#endif +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + AKCOMPRESSD_WAKEUP, +#endif + NR_EVENT_ITEMS +}; + +struct swapd_event_state { + unsigned long event[NR_EVENT_ITEMS]; +}; + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS +struct cgroup_cache_page { + spinlock_t lock; + struct list_head head; + unsigned int cnt; + int id; + char compressing; + char dead; +}; +#endif + +typedef struct mem_cgroup_hybridswap { +#ifdef CONFIG_HYBRIDSWAP + atomic64_t ufs2zram_scale; + atomic_t zram2ufs_scale; + atomic64_t app_grade; + atomic64_t app_uid; + struct list_head grade_node; + char name[MEM_CGROUP_NAME_MAX_LEN]; + struct zram *zram; + struct mem_cgroup *memcg; + refcount_t usage; +#endif +#ifdef CONFIG_HYBRIDSWAP_SWAPD + atomic_t mem2zram_scale; + atomic_t pagefault_level; + unsigned long long reclaimed_pagefault; + long long can_reclaimed; +#endif +#ifdef CONFIG_HYBRIDSWAP_CORE + unsigned long swap_sorted_list; + unsigned long eswap_lru; + struct list_head link_list; + spinlock_t zram_init_lock; + long long can_eswaped; + + atomic64_t zram_stored_size; + atomic64_t zram_page_size; + unsigned long zram_watermark; + + atomic_t hybridswap_extcnt; + atomic_t hybridswap_peakextcnt; + + atomic64_t hybridswap_stored_pages; + atomic64_t hybridswap_stored_size; + atomic64_t hybridswap_eswap_notify_free; + + atomic64_t hybridswap_outcnt; + atomic64_t hybridswap_incnt; + atomic64_t hybridswap_allfaultcnt; + atomic64_t hybridswap_faultcnt; + + atomic64_t hybridswap_outextcnt; + atomic64_t hybridswap_inextcnt; + + struct mutex swap_lock; + bool in_swapin; + bool force_swapout; +#endif +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + struct cgroup_cache_page cache; +#endif +}memcg_hybs_t; + +#ifdef CONFIG_ZRAM_5_15 +#define MEMCGRP_ITEM_DATA(memcg) ((memcg_hybs_t *)(memcg)->android_oem_data1[0]) +#else +#define MEMCGRP_ITEM_DATA(memcg) ((memcg_hybs_t *)(memcg)->android_oem_data1) +#endif + +#define MEMCGRP_ITEM(memcg, item) (MEMCGRP_ITEM_DATA(memcg)->item) + +extern void __put_memcg_cache(memcg_hybs_t *hybs); + +static inline memcg_hybs_t *fetch_memcg_cache(memcg_hybs_t *hybs) +{ + refcount_inc(&hybs->usage); + return hybs; +} + +static inline void put_memcg_cache(memcg_hybs_t *hybs) +{ + if (refcount_dec_and_test(&hybs->usage)) + __put_memcg_cache(hybs); +} + +DECLARE_PER_CPU(struct swapd_event_state, swapd_event_states); +extern struct mutex reclaim_para_lock; + +static inline void __count_swapd_event(enum hybridswap_event_item item) +{ + raw_cpu_inc(swapd_event_states.event[item]); +} + +static inline void count_swapd_event(enum hybridswap_event_item item) +{ + this_cpu_inc(swapd_event_states.event[item]); +} + +static inline void __count_swapd_events(enum hybridswap_event_item item, long delta) +{ + raw_cpu_add(swapd_event_states.event[item], delta); +} + +static inline void count_swapd_events(enum hybridswap_event_item item, long delta) +{ + this_cpu_add(swapd_event_states.event[item], delta); +} + +void *hybridswap_malloc(size_t size, bool fast, bool nofail); +void hybridswap_free(const void *mem); +unsigned long hybridswap_zsmalloc(struct zs_pool *zs_pool, + size_t size, struct hybridswap_page_pool *pool); +struct page *hybridswap_alloc_page( + struct hybridswap_page_pool *pool, gfp_t gfp, + bool fast, bool nofail); +void hybridswap_page_recycle(struct page *page, + struct hybridswap_page_pool *pool); +struct hybstatus *hybridswap_fetch_stat_obj(void); +int hybridswap_manager_init(struct zram *zram); +void hybridswap_manager_memcg_init(struct zram *zram, + struct mem_cgroup *memcg); +void hybridswap_manager_memcg_deinit(struct mem_cgroup *mcg); +void hybridswap_swap_sorted_list_add(struct zram *zram, u32 index, + struct mem_cgroup *memcg); +void hybridswap_swap_sorted_list_del(struct zram *zram, u32 index); +unsigned long hybridswap_eswap_create(struct mem_cgroup *memcg, + int *eswapid, + struct hybridswap_buffer *dest_buf, + void **private); +void hybridswap_eswap_register(void *private, struct hybridswap_io_req *req); +void hybridswap_eswap_objs_del(struct zram *zram, u32 index); +int hybridswap_find_eswap_by_index( + unsigned long eswpentry, struct hybridswap_buffer *buf, void **private); +int hybridswap_find_eswap_by_memcg( + struct mem_cgroup *mcg, + struct hybridswap_buffer *dest_buf, void **private); +void hybridswap_eswap_destroy(void *private, enum hybridswap_class class); +void hybridswap_eswap_exception(enum hybridswap_class class, + void *private); +void hybridswap_manager_deinit(struct zram *zram); +struct mem_cgroup *hybridswap_zram_fetch_mcg(struct zram *zram, u32 index); +int hyb_io_work_begin(void); +void *hybridswap_plug_start(struct hybridswap_io *io_para); +int hybridswap_read_eswap(void *iohandle, + struct hybridswap_entry *ioentry); +int hybridswap_write_eswap(void *iohandle, + struct hybridswap_entry *ioentry); + +int hybridswap_plug_finish(void *iohandle); + +void hybperf_start( + struct hybridswap_key_point_record *record, + ktime_t stsrt, unsigned long long start_ravg_sum, + enum hybridswap_class class); + +void hybperf_end(struct hybridswap_key_point_record *record); + +void hybperfiowrkstart( + struct hybridswap_key_point_record *record, + enum hybridswap_key_point type); + +void hybperfiowrkend( + struct hybridswap_key_point_record *record, + enum hybridswap_key_point type); + +void hybperfiowrkpoint( + struct hybridswap_key_point_record *record, + enum hybridswap_key_point type); + +void hybperf_async_perf( + struct hybridswap_key_point_record *record, + enum hybridswap_key_point type, ktime_t start, + unsigned long long start_ravg_sum); + +void hybperf_io_stat( + struct hybridswap_key_point_record *record, int page_cnt, + int segment_cnt); + +static inline unsigned long long hybridswap_fetch_ravg_sum(void) +{ + return 0; +} + +void hybridswap_fail_record(enum hybridswap_fail_point point, + u32 index, int eswapid, unsigned char *task_comm); +bool hybridswap_reach_life_protect(void); +struct workqueue_struct *hybridswap_fetch_reclaim_workqueue(void); +extern struct mem_cgroup *fetch_next_memcg(struct mem_cgroup *prev); +extern void fetch_next_memcg_break(struct mem_cgroup *prev); +extern memcg_hybs_t *hybridswap_cache_alloc(struct mem_cgroup *memcg, bool atomic); +extern void memcg_app_grade_resort(void); +extern unsigned long memcg_anon_pages(struct mem_cgroup *memcg); + +#ifdef CONFIG_HYBRIDSWAP_CORE +extern bool hybridswap_core_enabled(void); +extern bool hybridswap_out_to_eswap_enable(void); +extern void hybridswap_mem_cgroup_deinit(struct mem_cgroup *memcg); +extern unsigned long hybridswap_out_to_eswap(unsigned long size); +extern int hybridswap_batches(struct mem_cgroup *mcg, + unsigned long size, bool preload); +extern unsigned long zram_zsmalloc(struct zs_pool *zs_pool, + size_t size, gfp_t gfp); +extern struct task_struct *fetch_task_from_proc(struct inode *inode); +extern unsigned long hybridswap_fetch_zram_used_pages(void); +extern unsigned long long hybridswap_fetch_zram_pagefault(void); +extern bool hybridswap_reclaim_work_running(void); +extern void hybridswap_force_reclaim(struct mem_cgroup *mcg); +extern bool hybridswap_stored_wm_ok(void); +extern void mem_cgroup_id_remove_hook(void *data, struct mem_cgroup *memcg); +extern int mem_cgroup_stored_wm_scale_write( + struct cgroup_subsys_state *css, struct cftype *cft, s64 val); +extern s64 mem_cgroup_stored_wm_scale_read( + struct cgroup_subsys_state *css, struct cftype *cft); +extern bool hybridswap_delete(struct zram *zram, u32 index); +extern int hybridswap_stored_info(unsigned long *total, unsigned long *used); + +extern unsigned long long hybridswap_read_mcg_stats( + struct mem_cgroup *mcg, enum hybridswap_mcg_member mcg_member); +extern int hybridswap_core_enable(void); +extern void hybridswap_core_disable(void); +extern int hybridswap_psi_show(struct seq_file *m, void *v); +#else +static inline unsigned long long hybridswap_read_mcg_stats( + struct mem_cgroup *mcg, enum hybridswap_mcg_member mcg_member) +{ + return 0; +} + +unsigned long long hybridswap_fetch_zram_pagefault(void) +{ + return 0; +} + +static inline unsigned long long hybridswap_fetch_zram_pagefault(void) +{ + return 0; +} + +static inline bool hybridswap_reclaim_work_running(void) +{ + return false; +} + +static inline bool hybridswap_core_enabled(void) { return false; } +static inline bool hybridswap_out_to_eswap_enable(void) { return false; } +#endif + +#ifdef CONFIG_HYBRIDSWAP_SWAPD +extern atomic_long_t page_fault_pause; +extern atomic_long_t page_fault_pause_cnt; +extern struct cftype mem_cgroup_swapd_legacy_files[]; +extern bool zram_watermark_ok(void); +extern void wake_all_swapd(void); +extern void alloc_pages_slowpath_hook(void *data, gfp_t gfp_mask, + unsigned int order, unsigned long delta); +extern void rmqueue_hook(void *data, struct zone *preferred_zone, + struct zone *zone, unsigned int order, gfp_t gfp_flags, + unsigned int alloc_flags, int migratetype); +extern void __init swapd_pre_init(void); +extern void swapd_pre_deinit(void); +extern void update_swapd_mcg_setup(struct mem_cgroup *memcg); +extern bool free_zram_is_ok(void); +extern bool free_swap_is_low(void); +extern unsigned long fetch_nr_zram_total(void); +extern int swapd_init(struct zram *zram); +extern void swapd_exit(void); +extern bool hybridswap_swapd_enabled(void); +#else +static inline bool hybridswap_swapd_enabled(void) { return false; } +#endif + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS +extern spinlock_t cached_idr_lock; +extern struct idr cached_idr; + +extern void __init akcompressd_pre_init(void); +extern void __exit akcompressd_pre_deinit(void); +extern int create_akcompressd_task(struct zram *zram); +extern void clear_page_memcg(struct cgroup_cache_page *cache); +#endif + +#endif /* end of HYBRIDSWAP_INTERNAL_H */ diff --git a/drivers/moto_swap/hybridswap/hybridswap_main.c b/drivers/moto_swap/hybridswap/hybridswap_main.c new file mode 100644 index 000000000000..23d23dca4c07 --- /dev/null +++ b/drivers/moto_swap/hybridswap/hybridswap_main.c @@ -0,0 +1,1092 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020-2022 Oplus. All rights reserved. + */ + +#define pr_fmt(fmt) "moto_swap: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_ZRAM_5_4 +#include "../zram-5.4/zram_drv.h" +#include "../zram-5.4/zram_drv_internal.h" +#define MEMCG_OEM_DATA(memcg) ((memcg)->android_oem_data1) +#elif defined CONFIG_ZRAM_5_15 +#include "../zram-5.15/zram_drv.h" +#include "../zram-5.15/zram_drv_internal.h" +#define BIO_MAX_PAGES BIO_MAX_VECS +#define MEMCG_OEM_DATA(memcg) ((memcg)->android_oem_data1[0]) +static unsigned long memcg_page_state_local(struct mem_cgroup *memcg, int idx) +{ + long x = 0; + int cpu; + + for_each_possible_cpu(cpu) + x += per_cpu(memcg->vmstats_percpu->state[idx], cpu); +#ifdef CONFIG_SMP + if (x < 0) + x = 0; +#endif + return x; +} +#else +#include "../zram-5.10/zram_drv.h" +#include "../zram-5.10/zram_drv_internal.h" +#define MEMCG_OEM_DATA(memcg) ((memcg)->android_oem_data1) +#endif +#include "hybridswap_internal.h" +#include "hybridswap.h" + + + +static const char *swapd_text[NR_EVENT_ITEMS] = { +#ifdef CONFIG_HYBRIDSWAP_SWAPD + "swapd_wakeup", + "swapd_hit_pagefaults", + "swapd_memcg_scale_skip", + "swapd_memcg_pagefault_skip", + "swapd_shrink_anon", + "swapd_swapout", + "swapd_skip_swapout", + "swapd_nothing_ignore", + "swapd_over_min_buffer_skip_times", + "swapd_nothing_ignore_skip_times", + "swapd_snapshot_times", + "swapd_skip_shrink_of_window", + "swapd_manual_pause", +#ifdef CONFIG_OPLUS_JANK + "swapd_cpu_busy_skip_times", + "swapd_cpu_busy_break_times", +#endif +#endif +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + "akcompressd_running", +#endif +}; + +enum scan_balance { + SCAN_EQUAL, + SCAN_FRACT, + SCAN_ANON, + SCAN_FILE, +}; + +static int log_level = HYB_MAX; +static struct kmem_cache *hybridswap_cache; +static struct list_head grade_head; +static DEFINE_SPINLOCK(grade_list_lock); +static DEFINE_MUTEX(hybridswap_enable_lock); +static bool hybridswap_enabled = false; + +DEFINE_MUTEX(reclaim_para_lock); +DEFINE_PER_CPU(struct swapd_event_state, swapd_event_states); + +extern unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg, + unsigned long nr_pages, + gfp_t gfp_mask, + bool may_swap); + +void hybridswap_loglevel_set(int level) +{ + log_level = level; +} + +int hybridswap_loglevel(void) +{ + return log_level; +} + +void __put_memcg_cache(memcg_hybs_t *hybs) +{ +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + if (hybs->cache.id > 0) { + spin_lock(&cached_idr_lock); + idr_replace(&cached_idr, NULL, hybs->cache.id); + idr_remove(&cached_idr, hybs->cache.id); + spin_unlock(&cached_idr_lock); + } + + spin_lock(&hybs->cache.lock); + if (hybs->cache.dead != 1) + BUG(); + spin_unlock(&hybs->cache.lock); +#endif + kmem_cache_free(hybridswap_cache, (void *)hybs); +} + +static inline void sum_hybridswap_vm_events(unsigned long *ret) +{ + int cpu; + int i; + + memset(ret, 0, NR_EVENT_ITEMS * sizeof(unsigned long)); + + for_each_online_cpu(cpu) { + struct swapd_event_state *this = + &per_cpu(swapd_event_states, cpu); + + for (i = 0; i < NR_EVENT_ITEMS; i++) + ret[i] += this->event[i]; + } +} + +static inline void all_hybridswap_vm_events(unsigned long *ret) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) + get_online_cpus(); +#else + cpus_read_lock(); +#endif + sum_hybridswap_vm_events(ret); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) + put_online_cpus(); +#else + cpus_read_unlock(); +#endif +} + +ssize_t hybridswap_vmstat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long *vm_buf = NULL; + int len = 0; + int i = 0; + + vm_buf = kzalloc(sizeof(struct swapd_event_state), GFP_KERNEL); + if (!vm_buf) + return -ENOMEM; + all_hybridswap_vm_events(vm_buf); + +#ifdef CONFIG_HYBRIDSWAP_SWAPD + len += snprintf(buf + len, PAGE_SIZE - len, "%-32s %12lu\n", + "page_fault_pause", atomic_long_read(&page_fault_pause)); + len += snprintf(buf + len, PAGE_SIZE - len, "%-32s %12lu\n", + "page_fault_pause_cnt", atomic_long_read(&page_fault_pause_cnt)); +#endif + + for (;i < NR_EVENT_ITEMS; i++) { + len += snprintf(buf + len, PAGE_SIZE - len, "%-32s %12lu\n", + swapd_text[i], vm_buf[i]); + if (len == PAGE_SIZE) + break; + } + kfree(vm_buf); + + return len; +} + +ssize_t hybridswap_loglevel_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + char *type_buf = NULL; + unsigned long val; + + type_buf = strstrip((char *)buf); + if (kstrtoul(type_buf, 0, &val)) + return -EINVAL; + + if (val >= HYB_MAX) { + hybp(HYB_ERR, "val %lu is not valid\n", val); + return -EINVAL; + } + hybridswap_loglevel_set((int)val); + + return len; +} + +ssize_t hybridswap_loglevel_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t size = 0; + + size += scnprintf(buf + size, PAGE_SIZE - size, + "Hybridswap log level: %d\n", hybridswap_loglevel()); + + return size; +} + +/* Make sure the memcg is not NULL in caller */ +memcg_hybs_t *hybridswap_cache_alloc(struct mem_cgroup *memcg, bool atomic) +{ + memcg_hybs_t *hybs; + u64 ret; + gfp_t flags = GFP_KERNEL; + + if (MEMCG_OEM_DATA(memcg)) + BUG(); + + if (atomic) + flags &= ~__GFP_DIRECT_RECLAIM; + + hybs = (memcg_hybs_t *)kmem_cache_zalloc(hybridswap_cache, flags); + if (!hybs) + return NULL; + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + spin_lock_init(&hybs->cache.lock); + INIT_LIST_HEAD(&hybs->cache.head); + hybs->cache.cnt = 0; + hybs->cache.compressing = 0; + hybs->cache.dead = 0; + spin_lock(&cached_idr_lock); + hybs->cache.id = idr_alloc(&cached_idr, NULL, 1, MEM_CGROUP_ID_MAX, + GFP_KERNEL); + if (hybs->cache.id < 0) { + spin_unlock(&cached_idr_lock); + kmem_cache_free(hybridswap_cache, (void*)hybs); + return NULL; + } + idr_replace(&cached_idr, &hybs->cache, hybs->cache.id); + spin_unlock(&cached_idr_lock); +#endif + INIT_LIST_HEAD(&hybs->grade_node); +#ifdef CONFIG_HYBRIDSWAP_CORE + spin_lock_init(&hybs->zram_init_lock); +#endif + atomic64_set(&hybs->app_grade, 300); + atomic64_set(&hybs->ufs2zram_scale, 100); +#ifdef CONFIG_HYBRIDSWAP_SWAPD + atomic_set(&hybs->mem2zram_scale, 80); + atomic_set(&hybs->zram2ufs_scale, 50); + atomic_set(&hybs->pagefault_level, 50); +#endif + hybs->memcg = memcg; + refcount_set(&hybs->usage, 1); + + ret = atomic64_cmpxchg((atomic64_t *)&MEMCG_OEM_DATA(memcg), 0, (u64)hybs); + if (ret != 0) { +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + hybs->cache.dead = 1; +#endif + put_memcg_cache(hybs); + return (memcg_hybs_t *)ret; + } + + return hybs; +} + +#ifdef CONFIG_HYBRIDSWAP_SWAPD +static void tune_scan_type_hook(void *data, char *scan_balance) +{ + /*hybrid swapd,scan anon only*/ + if (current_is_mswapd()) { + *scan_balance = SCAN_ANON; + return; + } + +#ifdef CONFIG_HYBRIDSWAP_CORE + if (unlikely(!hybridswap_core_enabled())) + return; + + /*real zram full, scan file only*/ + if (!free_zram_is_ok()) { + *scan_balance = SCAN_FILE; + return; + } +#endif +} +#endif + +static void tune_swappiness_hook(void *data, int *swappiness) +{ + if (current_is_kswapd()) { + *swappiness = *swappiness; +#ifdef CONFIG_HYBRIDSWAP_SWAPD + } else if (current_is_mswapd()) { + *swappiness = 200; + if (free_swap_is_low()) { + *swappiness = 0; + } +#endif + } else { + *swappiness = 60; + } + + return; +} + +static void mem_cgroup_alloc_hook(void *data, struct mem_cgroup *memcg) +{ + if (MEMCG_OEM_DATA(memcg)) + BUG(); + + hybridswap_cache_alloc(memcg, true); +} + +static void mem_cgroup_free_hook(void *data, struct mem_cgroup *memcg) +{ + memcg_hybs_t *hybs; + + if (!MEMCG_OEM_DATA(memcg)) + return; + + hybs = (memcg_hybs_t *)MEMCG_OEM_DATA(memcg); + MEMCG_OEM_DATA(memcg) = 0; +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + clear_page_memcg(&hybs->cache); +#endif + put_memcg_cache(hybs); +} + +void memcg_app_grade_update(struct mem_cgroup *tarfetch) +{ + struct list_head *pos = NULL; + unsigned long flags; + +#ifdef CONFIG_HYBRIDSWAP_SWAPD + update_swapd_mcg_setup(tarfetch); +#endif + spin_lock_irqsave(&grade_list_lock, flags); + list_for_each(pos, &grade_head) { + memcg_hybs_t *hybs = list_entry(pos, memcg_hybs_t, grade_node); + if (atomic64_read(&hybs->app_grade) < + atomic64_read(&MEMCGRP_ITEM(tarfetch, app_grade))) + break; + } + list_move_tail(&MEMCGRP_ITEM(tarfetch, grade_node), pos); + spin_unlock_irqrestore(&grade_list_lock, flags); +} + +static void mem_cgroup_css_online_hook(void *data, + struct cgroup_subsys_state *css, struct mem_cgroup *memcg) +{ + if (MEMCG_OEM_DATA(memcg)) + memcg_app_grade_update(memcg); + + css_get(css); +} + +static void mem_cgroup_css_offline_hook(void *data, + struct cgroup_subsys_state *css, struct mem_cgroup *memcg) +{ + unsigned long flags; + + if (MEMCG_OEM_DATA(memcg)) { + spin_lock_irqsave(&grade_list_lock, flags); + list_del_init(&MEMCGRP_ITEM(memcg, grade_node)); + spin_unlock_irqrestore(&grade_list_lock, flags); + } + + css_put(css); +} + +#define REGISTER_HOOK(name) do {\ + rc = register_trace_android_vh_##name(name##_hook, NULL);\ + if (rc) {\ + hybp(HYB_ERR, "%s:%d register hook %s failed", __FILE__, __LINE__, #name);\ + goto err_out_##name;\ + }\ +} while (0) + +#define UNREGISTER_HOOK(name) do {\ + unregister_trace_android_vh_##name(name##_hook, NULL);\ +} while (0) + +#define ERROR_OUT(name) err_out_##name + +static int register_all_hooks(void) +{ + int rc; + + /* mem_cgroup_alloc_hook */ + REGISTER_HOOK(mem_cgroup_alloc); + /* mem_cgroup_free_hook */ + REGISTER_HOOK(mem_cgroup_free); + /* mem_cgroup_css_online_hook */ + REGISTER_HOOK(mem_cgroup_css_online); + /* mem_cgroup_css_offline_hook */ + REGISTER_HOOK(mem_cgroup_css_offline); +#ifdef CONFIG_HYBRIDSWAP_SWAPD + /* rmqueue_hook */ + REGISTER_HOOK(rmqueue); + /* tune_scan_type_hook */ + REGISTER_HOOK(tune_scan_type); +#endif + /* tune_swappiness_hook */ + REGISTER_HOOK(tune_swappiness); +#ifdef CONFIG_HYBRIDSWAP_CORE + /* mem_cgroup_id_remove_hook */ + REGISTER_HOOK(mem_cgroup_id_remove); +#endif + return 0; + +#ifdef CONFIG_HYBRIDSWAP_CORE + UNREGISTER_HOOK(mem_cgroup_id_remove); +ERROR_OUT(mem_cgroup_id_remove): +#endif + UNREGISTER_HOOK(tune_swappiness); +ERROR_OUT(tune_swappiness): +#ifdef CONFIG_HYBRIDSWAP_SWAPD + UNREGISTER_HOOK(tune_scan_type); +ERROR_OUT(tune_scan_type): + UNREGISTER_HOOK(rmqueue); +ERROR_OUT(rmqueue): +#endif + UNREGISTER_HOOK(mem_cgroup_css_offline); +ERROR_OUT(mem_cgroup_css_offline): + UNREGISTER_HOOK(mem_cgroup_css_online); +ERROR_OUT(mem_cgroup_css_online): + UNREGISTER_HOOK(mem_cgroup_free); +ERROR_OUT(mem_cgroup_free): + UNREGISTER_HOOK(mem_cgroup_alloc); +ERROR_OUT(mem_cgroup_alloc): + return rc; +} + +static void unregister_all_hook(void) +{ + UNREGISTER_HOOK(mem_cgroup_alloc); + UNREGISTER_HOOK(mem_cgroup_free); + UNREGISTER_HOOK(mem_cgroup_css_offline); + UNREGISTER_HOOK(mem_cgroup_css_online); +#ifdef CONFIG_HYBRIDSWAP_CORE + UNREGISTER_HOOK(mem_cgroup_id_remove); +#endif +#ifdef CONFIG_HYBRIDSWAP_SWAPD + UNREGISTER_HOOK(rmqueue); + UNREGISTER_HOOK(tune_scan_type); +#endif + UNREGISTER_HOOK(tune_swappiness); +} + +unsigned long memcg_anon_pages(struct mem_cgroup *memcg) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) + struct lruvec *lruvec = NULL; + struct mem_cgroup_per_node *mz = NULL; +#endif + if (!memcg) + return 0; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) + mz = mem_cgroup_nodeinfo(memcg, 0); + if (!mz) { + fetch_next_memcg_break(memcg); + return 0; + } + + lruvec = &mz->lruvec; + if (!lruvec) { + fetch_next_memcg_break(memcg); + return 0; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0) + return (mem_cgroup_fetch_lru_size(lruvec, LRU_ACTIVE_ANON) + + mem_cgroup_fetch_lru_size(lruvec, LRU_INACTIVE_ANON)); +#else + return (lruvec_page_state(lruvec, NR_ACTIVE_ANON) + + lruvec_page_state(lruvec, NR_INACTIVE_ANON)); +#endif + +#else + return (memcg_page_state_local(memcg, NR_ACTIVE_ANON) + + memcg_page_state_local(memcg, NR_INACTIVE_ANON)); +#endif +} + +static unsigned long memcg_inactive_anon_pages(struct mem_cgroup *memcg) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) + struct lruvec *lruvec = NULL; + struct mem_cgroup_per_node *mz = NULL; +#endif + + if (!memcg) + return 0; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) + mz = mem_cgroup_nodeinfo(memcg, 0); + if (!mz) { + fetch_next_memcg_break(memcg); + return 0; + } + + lruvec = &mz->lruvec; + if (!lruvec) { + fetch_next_memcg_break(memcg); + return 0; + } + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0) + return mem_cgroup_fetch_lru_size(lruvec, LRU_INACTIVE_ANON); +#else + return lruvec_page_state(lruvec, NR_INACTIVE_ANON); +#endif +#else + return memcg_page_state_local(memcg, NR_INACTIVE_ANON); +#endif +} + +static ssize_t mem_cgroup_force_shrink_anon(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) +{ + struct mem_cgroup *memcg; + unsigned long nr_need_reclaim, reclaim_total, nr_reclaimed; + int ret; + + buf = strstrip(buf); + ret = kstrtoul(buf, 0, &reclaim_total); + if (unlikely(ret)) { + hybp(HYB_ERR, "reclaim_total %s value is error!\n", buf); + return -EINVAL; + } + + memcg = mem_cgroup_from_css(of_css(of)); + + if (reclaim_total) + nr_need_reclaim = memcg_anon_pages(memcg); + else + nr_need_reclaim = memcg_inactive_anon_pages(memcg); + + hybp(HYB_INFO, "FORCE SHRINK +\n"); + nr_reclaimed = try_to_free_mem_cgroup_pages(memcg, nr_need_reclaim, + GFP_KERNEL, true); + hybp(HYB_INFO, "FORCE SHRINK - to_reclaim %lu reclaimed %lu\n", nr_need_reclaim, nr_reclaimed); + return nbytes; +} + +static int memcg_total_info_per_app_show(struct seq_file *m, void *v) +{ + struct mem_cgroup *memcg = NULL; + unsigned long anon_size; + unsigned long zram_compress_size; + unsigned long eswap_compress_size; + unsigned long zram_page_size; + unsigned long eswap_page_size; + + seq_printf(m, "%-8s %-8s %-8s %-8s %-8s %s \n", + "anon", "zram_c", "zram_p", "eswap_c", "eswap_p", + "memcg_n"); + while ((memcg = fetch_next_memcg(memcg))) { + if (!MEMCGRP_ITEM_DATA(memcg)) + continue; + + anon_size = memcg_anon_pages(memcg); + zram_compress_size = hybridswap_read_mcg_stats(memcg, + MCG_ZRAM_STORED_SZ); + eswap_compress_size = hybridswap_read_mcg_stats(memcg, + MCG_DISK_STORED_SZ); + zram_page_size = hybridswap_read_mcg_stats(memcg, + MCG_ZRAM_STORED_PG_SZ); + eswap_page_size = hybridswap_read_mcg_stats(memcg, + MCG_DISK_STORED_PG_SZ); + + anon_size *= PAGE_SIZE / SZ_1K; + zram_compress_size /= SZ_1K; + eswap_compress_size /= SZ_1K; + zram_page_size *= PAGE_SIZE / SZ_1K; + eswap_page_size *= PAGE_SIZE / SZ_1K; + + seq_printf(m, "%-8lu %-8lu %-8lu %-8lu %-8lu %s \n", + anon_size, zram_compress_size, zram_page_size, + eswap_compress_size, eswap_page_size, + MEMCGRP_ITEM(memcg, name)); + } + + return 0; +} + +static int memcg_swap_stat_show(struct seq_file *m, void *v) +{ + struct mem_cgroup *memcg = NULL; + unsigned long eswap_out_cnt; + unsigned long eswap_out_size; + unsigned long eswap_in_size; + unsigned long eswap_in_cnt; + unsigned long page_fault_cnt; + unsigned long cur_eswap_size; + unsigned long max_eswap_size; + unsigned long zram_compress_size, zram_page_size; + unsigned long eswap_compress_size, eswap_page_size; + + memcg = mem_cgroup_from_css(seq_css(m)); + + zram_compress_size = hybridswap_read_mcg_stats(memcg, MCG_ZRAM_STORED_SZ); + zram_page_size = hybridswap_read_mcg_stats(memcg, MCG_ZRAM_STORED_PG_SZ); + eswap_compress_size = hybridswap_read_mcg_stats(memcg, MCG_DISK_STORED_SZ); + eswap_page_size = hybridswap_read_mcg_stats(memcg, MCG_DISK_STORED_PG_SZ); + + eswap_out_cnt = hybridswap_read_mcg_stats(memcg, MCG_ESWAPOUT_CNT); + eswap_out_size = hybridswap_read_mcg_stats(memcg, MCG_ESWAPOUT_SZ); + eswap_in_size = hybridswap_read_mcg_stats(memcg, MCG_ESWAPIN_SZ); + eswap_in_cnt = hybridswap_read_mcg_stats(memcg, MCG_ESWAPIN_CNT); + page_fault_cnt = hybridswap_read_mcg_stats(memcg, MCG_DISK_FAULT_CNT); + cur_eswap_size = hybridswap_read_mcg_stats(memcg, MCG_DISK_SPACE); + max_eswap_size = hybridswap_read_mcg_stats(memcg, MCG_DISK_SPACE_PEAK); + + seq_printf(m, "%-32s %12lu KB\n", "zramCompressedSize:", + zram_compress_size / SZ_1K); + seq_printf(m, "%-32s %12lu KB\n", "zramOrignalSize:", + zram_page_size << (PAGE_SHIFT - 10)); + seq_printf(m, "%-32s %12lu KB\n", "eswapCompressedSize:", + eswap_compress_size / SZ_1K); + seq_printf(m, "%-32s %12lu KB\n", "eswapOrignalSize:", + eswap_page_size << (PAGE_SHIFT - 10)); + seq_printf(m, "%-32s %12lu \n", "eswapOutTotal:", eswap_out_cnt); + seq_printf(m, "%-32s %12lu KB\n", "eswapOutSize:", eswap_out_size / SZ_1K); + seq_printf(m, "%-32s %12lu\n", "eswapInTotal:", eswap_in_cnt); + seq_printf(m, "%-32s %12lu KB\n", "eswapInSize:", eswap_in_size / SZ_1K); + seq_printf(m, "%-32s %12lu\n", "pageInTotal:", page_fault_cnt); + seq_printf(m, "%-32s %12lu KB\n", "eswapSizeCur:", cur_eswap_size / SZ_1K); + seq_printf(m, "%-32s %12lu KB\n", "eswapSizeMax:", max_eswap_size / SZ_1K); + + return 0; +} + +static ssize_t mem_cgroup_name_write(struct kernfs_open_file *of, char *buf, + size_t nbytes, loff_t off) +{ + struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of)); + memcg_hybs_t *hybp = MEMCGRP_ITEM_DATA(memcg); + int len, w_len; + + if (!hybp) + return -EINVAL; + + buf = strstrip(buf); + len = strlen(buf) + 1; + if (len > MEM_CGROUP_NAME_MAX_LEN) + len = MEM_CGROUP_NAME_MAX_LEN; + + w_len = snprintf(hybp->name, len, "%s", buf); + if (w_len > len) + hybp->name[len - 1] = '\0'; + + return nbytes; +} + +static int mem_cgroup_name_show(struct seq_file *m, void *v) +{ + struct mem_cgroup *memcg = mem_cgroup_from_css(seq_css(m)); + + if (!MEMCGRP_ITEM_DATA(memcg)) + return -EPERM; + + seq_printf(m, "%s\n", MEMCGRP_ITEM(memcg, name)); + + return 0; +} + +static int mem_cgroup_app_grade_write(struct cgroup_subsys_state *css, + struct cftype *cft, s64 val) +{ + struct mem_cgroup *memcg; + memcg_hybs_t *hybs; + + if (val > MAX_APP_GRADE || val < 0) + return -EINVAL; + + memcg = mem_cgroup_from_css(css); + hybs = MEMCGRP_ITEM_DATA(memcg); + if (!hybs) { + hybs = hybridswap_cache_alloc(memcg, false); + if (!hybs) + return -EINVAL; + } + + if (atomic64_read(&MEMCGRP_ITEM(memcg, app_grade)) != val) + atomic64_set(&MEMCGRP_ITEM(memcg, app_grade), val); + memcg_app_grade_update(memcg); + + return 0; +} + +static s64 mem_cgroup_app_grade_read(struct cgroup_subsys_state *css, + struct cftype *cft) +{ + struct mem_cgroup *memcg = mem_cgroup_from_css(css); + + if (!MEMCGRP_ITEM_DATA(memcg)) + return -EPERM; + + return atomic64_read(&MEMCGRP_ITEM(memcg, app_grade)); +} + +int mem_cgroup_app_uid_write(struct cgroup_subsys_state *css, + struct cftype *cft, s64 val) +{ + struct mem_cgroup *memcg; + memcg_hybs_t *hybs; + + if (val < 0) + return -EINVAL; + + memcg = mem_cgroup_from_css(css); + hybs = MEMCGRP_ITEM_DATA(memcg); + if (!hybs) { + return -EINVAL; + } + + if (atomic64_read(&MEMCGRP_ITEM(memcg, app_uid)) != val) + atomic64_set(&MEMCGRP_ITEM(memcg, app_uid), val); + + return 0; +} + +static s64 mem_cgroup_app_uid_read(struct cgroup_subsys_state *css, struct cftype *cft) +{ + struct mem_cgroup *memcg = mem_cgroup_from_css(css); + + if (!MEMCGRP_ITEM_DATA(memcg)) + return -EPERM; + + return atomic64_read(&MEMCGRP_ITEM(memcg, app_uid)); +} + +static int mem_cgroup_ufs2zram_scale_write(struct cgroup_subsys_state *css, + struct cftype *cft, s64 val) +{ + struct mem_cgroup *memcg = mem_cgroup_from_css(css); + + if (!MEMCGRP_ITEM_DATA(memcg)) + return -EPERM; + + if (val > MAX_RATIO || val < MIN_RATIO) + return -EINVAL; + + atomic64_set(&MEMCGRP_ITEM(memcg, ufs2zram_scale), val); + + return 0; +} + +static s64 mem_cgroup_ufs2zram_scale_read(struct cgroup_subsys_state *css, + struct cftype *cft) +{ + struct mem_cgroup *memcg = mem_cgroup_from_css(css); + + if (!MEMCGRP_ITEM_DATA(memcg)) + return -EPERM; + + return atomic64_read(&MEMCGRP_ITEM(memcg, ufs2zram_scale)); +} + +static int mem_cgroup_force_eswapin_write(struct cgroup_subsys_state *css, + struct cftype *cft, s64 val) +{ + struct mem_cgroup *memcg = mem_cgroup_from_css(css); + memcg_hybs_t *hybs; + unsigned long size = 0; + const unsigned int scale = 100; + + hybs = MEMCGRP_ITEM_DATA(memcg); + if (!hybs) + return -EPERM; + +#ifdef CONFIG_HYBRIDSWAP_CORE + size = atomic64_read(&hybs->hybridswap_stored_size); +#endif + size = atomic64_read(&hybs->ufs2zram_scale) * size / scale; + size = ESWAP_ALIGN_UP(size); + +#ifdef CONFIG_HYBRIDSWAP_CORE + hybridswap_batches(memcg, size, val ? true : false); +#endif + + return 0; +} + +static int mem_cgroup_force_eswapout_write(struct cgroup_subsys_state *css, + struct cftype *cft, s64 val) +{ +#ifdef CONFIG_HYBRIDSWAP_CORE + hybridswap_force_reclaim(mem_cgroup_from_css(css)); +#endif + return 0; +} + +struct mem_cgroup *fetch_next_memcg(struct mem_cgroup *prev) +{ + memcg_hybs_t *hybs = NULL; + struct mem_cgroup *memcg = NULL; + struct list_head *pos = NULL; + unsigned long flags; + bool prev_got = true; + + spin_lock_irqsave(&grade_list_lock, flags); +find_again: + if (unlikely(!prev)) + pos = &grade_head; + else + pos = &MEMCGRP_ITEM(prev, grade_node); + + if (list_empty(pos)) /* deleted node */ + goto unlock; + + if (pos->next == &grade_head) + goto unlock; + + hybs = list_entry(pos->next, struct mem_cgroup_hybridswap, grade_node); + memcg = hybs->memcg; + if (unlikely(!memcg)) + goto unlock; + + if (!css_tryget(&memcg->css)) { + if (prev && prev_got) + css_put(&prev->css); + prev = memcg; + prev_got = false; + goto find_again; + } + +unlock: + spin_unlock_irqrestore(&grade_list_lock, flags); + if (prev && prev_got) + css_put(&prev->css); + + return memcg; +} + +void fetch_next_memcg_break(struct mem_cgroup *memcg) +{ + if (memcg) + css_put(&memcg->css); +} + +static struct cftype mem_cgroup_hybridswap_legacy_files[] = { + { + .name = "force_shrink_anon", + .write = mem_cgroup_force_shrink_anon, + }, + { + .name = "total_info_per_app", + .flags = CFTYPE_ONLY_ON_ROOT, + .seq_show = memcg_total_info_per_app_show, + }, + { + .name = "swap_stat", + .seq_show = memcg_swap_stat_show, + }, + { + .name = "name", + .write = mem_cgroup_name_write, + .seq_show = mem_cgroup_name_show, + }, + { + .name = "app_score", + .write_s64 = mem_cgroup_app_grade_write, + .read_s64 = mem_cgroup_app_grade_read, + }, + { + .name = "app_uid", + .write_s64 = mem_cgroup_app_uid_write, + .read_s64 = mem_cgroup_app_uid_read, + }, + { + .name = "ub_ufs2zram_ratio", + .write_s64 = mem_cgroup_ufs2zram_scale_write, + .read_s64 = mem_cgroup_ufs2zram_scale_read, + }, + { + .name = "force_eswapin", + .write_s64 = mem_cgroup_force_eswapin_write, + }, + { + .name = "force_eswapout", + .write_s64 = mem_cgroup_force_eswapout_write, + }, +#ifdef CONFIG_HYBRIDSWAP_CORE + { + .name = "psi", + .flags = CFTYPE_ONLY_ON_ROOT, + .seq_show = hybridswap_psi_show, + }, + { + .name = "stored_wm_ratio", + .flags = CFTYPE_ONLY_ON_ROOT, + .write_s64 = mem_cgroup_stored_wm_scale_write, + .read_s64 = mem_cgroup_stored_wm_scale_read, + }, +#endif + { }, /* terminate */ +}; + +static int hybridswap_enable(struct zram *zram) +{ + int ret = 0; + + if (hybridswap_enabled) { + hybp(HYB_WARN, "hybridswap_enabled is true\n"); + return ret; + } + +#ifdef CONFIG_HYBRIDSWAP_SWAPD + ret = swapd_init(zram); + if (ret) + return ret; +#endif + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + ret = create_akcompressd_task(zram); + if (ret) + goto create_akcompressd_task_fail; +#endif + +#ifdef CONFIG_HYBRIDSWAP_CORE + ret = hybridswap_core_enable(); + if (ret) + goto hybridswap_core_enable_fail; +#endif + hybridswap_enabled = true; + + return 0; + +#ifdef CONFIG_HYBRIDSWAP_CORE +hybridswap_core_enable_fail: +#endif +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + destroy_akcompressd_task(zram); +create_akcompressd_task_fail: +#endif +#ifdef CONFIG_HYBRIDSWAP_SWAPD + swapd_exit(); +#endif + return ret; +} + +static void hybridswap_disable(struct zram * zram) +{ + if (!hybridswap_enabled) { + hybp(HYB_WARN, "hybridswap_enabled is false\n"); + return; + } + +#ifdef CONFIG_HYBRIDSWAP_CORE + hybridswap_core_disable(); +#endif + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + destroy_akcompressd_task(zram); +#endif + +#ifdef CONFIG_HYBRIDSWAP_SWAPD + swapd_exit(); +#endif + hybridswap_enabled = false; +} + +ssize_t hybridswap_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int len = snprintf(buf, PAGE_SIZE, "hybridswap %s out_to_eswap %s swapd %s\n", + hybridswap_core_enabled() ? "enable" : "disable", + hybridswap_out_to_eswap_enable() ? "enable" : "disable", + hybridswap_swapd_enabled() ? "enable" : "disable"); + + return len; +} + +ssize_t hybridswap_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int ret; + unsigned long val; + char *kbuf; + struct zram *zram; + + kbuf = strstrip((char *)buf); + ret = kstrtoul(kbuf, 0, &val); + if (unlikely(ret)) { + hybp(HYB_ERR, "val %s is invalid!\n", kbuf); + + return -EINVAL; + } + + mutex_lock(&hybridswap_enable_lock); + zram = dev_to_zram(dev); + if (val == 0) + hybridswap_disable(zram); + else + ret = hybridswap_enable(zram); + mutex_unlock(&hybridswap_enable_lock); + + if (ret == 0) + ret = len; + return ret; +} + +int __init hybridswap_pre_init(void) +{ + int ret; + + INIT_LIST_HEAD(&grade_head); + log_level = HYB_INFO; + + hybridswap_cache = kmem_cache_create("mem_cgroup_hybridswap", + sizeof(struct mem_cgroup_hybridswap), + 0, SLAB_PANIC, NULL); + if (!hybridswap_cache) { + hybp(HYB_ERR, "create hybridswap_cache failed\n"); + ret = -ENOMEM; + return ret; + } + + ret = cgroup_add_legacy_cftypes(&memory_cgrp_subsys, + mem_cgroup_hybridswap_legacy_files); + if (ret) { + hybp(HYB_INFO, "add mem_cgroup_hybridswap_legacy_files failed\n"); + goto error_out; + } + +#ifdef CONFIG_HYBRIDSWAP_SWAPD + ret = cgroup_add_legacy_cftypes(&memory_cgrp_subsys, + mem_cgroup_swapd_legacy_files); + if (ret) { + hybp(HYB_INFO, "add mem_cgroup_swapd_legacy_files failed!\n"); + goto error_out; + } +#endif + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + akcompressd_pre_init(); +#endif + +#ifdef CONFIG_HYBRIDSWAP_SWAPD + swapd_pre_init(); +#endif + ret = register_all_hooks(); + if (ret) + goto fail_out; + + hybp(HYB_INFO, "hybridswap inited success!\n"); + return 0; + +fail_out: +#ifdef CONFIG_HYBRIDSWAP_SWAPD + swapd_pre_deinit(); +#endif +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + akcompressd_pre_deinit(); +#endif +error_out: + if (hybridswap_cache) { + kmem_cache_destroy(hybridswap_cache); + hybridswap_cache = NULL; + } + return ret; +} + +void __exit hybridswap_exit(void) +{ + unregister_all_hook(); + +#ifdef CONFIG_HYBRIDSWAP_SWAPD + swapd_pre_deinit(); +#endif +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + akcompressd_pre_deinit(); +#endif + + if (hybridswap_cache) { + kmem_cache_destroy(hybridswap_cache); + hybridswap_cache = NULL; + } +} diff --git a/drivers/moto_swap/hybridswap/hybridswap_swapd.c b/drivers/moto_swap/hybridswap/hybridswap_swapd.c new file mode 100644 index 000000000000..b172c90b15a2 --- /dev/null +++ b/drivers/moto_swap/hybridswap/hybridswap_swapd.c @@ -0,0 +1,1983 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2020-2022 Oplus. All rights reserved. + */ + +#define pr_fmt(fmt) "moto_swap: " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if IS_ENABLED(CONFIG_DRM_MSM) || IS_ENABLED(CONFIG_DRM_OPLUS_NOTIFY) +#include +#endif +#include + +#ifdef CONFIG_ZRAM_5_4 +#include "../zram-5.4/zram_drv.h" +#include "../zram-5.4/zram_drv_internal.h" +#define MEMCG_OEM_DATA(memcg) ((memcg)->android_oem_data1) +#elif defined CONFIG_ZRAM_5_15 +#include "../zram-5.15/zram_drv.h" +#include "../zram-5.15/zram_drv_internal.h" +#define BIO_MAX_PAGES BIO_MAX_VECS +#define MEMCG_OEM_DATA(memcg) ((memcg)->android_oem_data1[0]) +#else +#include "../zram-5.10/zram_drv.h" +#include "../zram-5.10/zram_drv_internal.h" +#define MEMCG_OEM_DATA(memcg) ((memcg)->android_oem_data1) +#endif +#include "hybridswap_internal.h" + +#define MOTO_SWAP_VERSION 2 + +struct swapd_param { + unsigned int min_grade; + unsigned int max_grade; + unsigned int mem2zram_scale; + unsigned int zram2ufs_scale; + unsigned int pagefault_level; +}; + +struct hybridswapd_task { + wait_queue_head_t swapd_wait; + atomic_t swapd_wait_flag; + struct task_struct *swapd; + struct cpumask swapd_bind_cpumask; +}; +#define PGDAT_ITEM_DATA(pgdat) ((struct hybridswapd_task*)(pgdat)->android_oem_data1) +#define PGDAT_ITEM(pgdat, item) (PGDAT_ITEM_DATA(pgdat)->item) + +#define INFOS_PAGEFAULT_THRESHOLD 3600 +#define PAGEFAULT_SNAPSHOT_MIN_GAP 150 +#define NOTHING_IGNORE_VALUE 20 +#define MAX_SKIP_GAP 1000 +#define EMPTY_ROUND_CHECK_THRESHOLD 10 +#define ZRAM_WM_RATIO 75 +#define COMPRESS_RATIO 33 +#define SWAPD_MAX_LEVEL_NUM 4 +#define SWAPD_DEFAULT_BIND_CPUS "0-3" +#define MAX_RECLAIMIN_SZ (100llu << 20) +#define page_to_kb(nr) (nr << (PAGE_SHIFT - 10)) +#define SWAPD_SHRINK_WINDOW (HZ * 10) +#define SWAPD_SHRINK_SIZE_PER_WINDOW 1024 +#define PAGES_TO_MB(pages) ((pages) >> 8) +#define PAGES_PER_1MB (1 << 8) + +unsigned long long total_pagefault_percent; +unsigned long long swapd_skip_interval; +bool last_round_is_empty; +unsigned long last_swapd_time; +atomic64_t zram_wm_scale = ATOMIC_LONG_INIT(ZRAM_WM_RATIO); +atomic64_t compress_scale = ATOMIC_LONG_INIT(COMPRESS_RATIO); +atomic_t usable_mem = ATOMIC_INIT(0); +atomic_t min_mem_watermark = ATOMIC_INIT(0); +atomic_t high_mem_watermark = ATOMIC_INIT(0); +atomic_t max_reclaim_size = ATOMIC_INIT(100); +atomic64_t free_swap_level = ATOMIC64_INIT(0); +atomic64_t zram_crit_thres = ATOMIC_LONG_INIT(0); +atomic64_t cpuload_level = ATOMIC_LONG_INIT(0); +atomic64_t infos_pagefault_level = ATOMIC_LONG_INIT(INFOS_PAGEFAULT_THRESHOLD); +atomic64_t pagefault_refresh_min = ATOMIC_LONG_INIT(PAGEFAULT_SNAPSHOT_MIN_GAP); +atomic64_t nothing_ignore_skip_interval = ATOMIC_LONG_INIT(NOTHING_IGNORE_VALUE); +atomic64_t max_skip_interval = ATOMIC_LONG_INIT(MAX_SKIP_GAP); +atomic64_t nothing_ignore_check_level = ATOMIC_LONG_INIT(EMPTY_ROUND_CHECK_THRESHOLD); +static unsigned long reclaim_exceed_sleep_ms = 50; +static unsigned long all_totalreserve_pages; + +static wait_queue_head_t refresh_daemonwait; +static atomic_t refresh_daemonwait_flag; +static atomic_t refresh_daemoninit_flag = ATOMIC_LONG_INIT(0); +static struct task_struct *refresh_daemontask; +static DEFINE_MUTEX(lowmem_event_lock); +static pid_t swapid = -1; +static unsigned long long infos_last_anon_pagefault; +static unsigned long last_refresh_t; +static struct swapd_param zswap_param[SWAPD_MAX_LEVEL_NUM]; +static enum cpuhp_state swapd_online; +static struct zram *swapd_zram = NULL; +static u64 max_reclaimin_size = MAX_RECLAIMIN_SZ; +atomic_long_t page_fault_pause = ATOMIC_LONG_INIT(0); +atomic_long_t page_fault_pause_cnt = ATOMIC_LONG_INIT(0); +#if IS_ENABLED(CONFIG_DRM_MSM) || IS_ENABLED(CONFIG_DRM_OPLUS_NOTIFY) +static struct notifier_block fb_notif; +static atomic_t display_off = ATOMIC_LONG_INIT(0); +#endif +static unsigned long swapd_shrink_window = SWAPD_SHRINK_WINDOW; +static unsigned long swapd_shrink_limit_per_window = SWAPD_SHRINK_SIZE_PER_WINDOW; +static unsigned long swapd_last_window_start; +static unsigned long swapd_last_window_shrink; +static atomic_t swapd_pause = ATOMIC_INIT(0); +static atomic_t swapd_enabled = ATOMIC_INIT(0); +static unsigned long swapd_nap_jiffies = 1; + +extern unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *memcg, + unsigned long nr_pages, + gfp_t gfp_mask, + bool may_swap); +#ifdef CONFIG_OPLUS_JANK +extern u32 fetch_cpu_load(u32 win_cnt, struct cpumask *mask); +#endif + +inline u64 fetch_zram_wm_scale_value(void) +{ + return atomic64_read(&zram_wm_scale); +} + +inline u64 fetch_compress_scale_value(void) +{ + return atomic64_read(&compress_scale); +} + +inline unsigned int fetch_usable_mem_value(void) +{ + return atomic_read(&usable_mem); +} + +inline unsigned int fetch_min_mem_watermark_value(void) +{ + return atomic_read(&min_mem_watermark); +} + +inline unsigned int fetch_high_mem_watermark_value(void) +{ + return atomic_read(&high_mem_watermark); +} + +inline u64 fetch_swapd_max_reclaim_size(void) +{ + return atomic_read(&max_reclaim_size); +} + +inline u64 fetch_free_swap_level_value(void) +{ + return atomic64_read(&free_swap_level); +} + +inline unsigned long long fetch_infos_pagefault_level_value(void) +{ + return atomic64_read(&infos_pagefault_level); +} + +inline unsigned long fetch_pagefault_refresh_min_value(void) +{ + return atomic64_read(&pagefault_refresh_min); +} + +inline unsigned long long fetch_nothing_ignore_skip_interval_value(void) +{ + return atomic64_read(¬hing_ignore_skip_interval); +} + +inline unsigned long long fetch_max_skip_interval_value(void) +{ + return atomic64_read(&max_skip_interval); +} + +inline unsigned long long fetch_nothing_ignore_check_level_value(void) +{ + return atomic64_read(¬hing_ignore_check_level); +} + +inline u64 fetch_zram_critical_level_value(void) +{ + return atomic64_read(&zram_crit_thres); +} + +inline u64 fetch_cpuload_level_value(void) +{ + return atomic64_read(&cpuload_level); +} + +static ssize_t usable_mem_params_write(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) +{ + unsigned int usable_mem_value; + unsigned int min_mem_watermark_value; + unsigned int high_mem_watermark_value; + u64 free_swap_level_value; + + buf = strstrip(buf); + + if (sscanf(buf, "%u %u %u %llu", + &usable_mem_value, + &min_mem_watermark_value, + &high_mem_watermark_value, + &free_swap_level_value) != 4) + return -EINVAL; + + atomic_set(&usable_mem, usable_mem_value); + atomic_set(&min_mem_watermark, min_mem_watermark_value); + atomic_set(&high_mem_watermark, high_mem_watermark_value); + atomic64_set(&free_swap_level, + (free_swap_level_value * (SZ_1M / PAGE_SIZE))); + + if (atomic_read(&min_mem_watermark) == 0) + atomic_set(&refresh_daemoninit_flag, 0); + else + atomic_set(&refresh_daemoninit_flag, 1); + + wake_all_swapd(); + + return nbytes; +} + +static int usable_mem_params_show(struct seq_file *m, void *v) +{ + seq_printf(m, "avail_buffers: %u\n", + atomic_read(&usable_mem)); + seq_printf(m, "min_avail_buffers: %u\n", + atomic_read(&min_mem_watermark)); + seq_printf(m, "high_avail_buffers: %u\n", + atomic_read(&high_mem_watermark)); + seq_printf(m, "free_swap_threshold: %llu\n", + (atomic64_read(&free_swap_level) * PAGE_SIZE / SZ_1M)); + + return 0; +} + +static ssize_t swapd_max_reclaim_size_write(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) +{ + const unsigned int base = 10; + u32 max_reclaim_size_value; + int ret; + + buf = strstrip(buf); + ret = kstrtouint(buf, base, &max_reclaim_size_value); + if (ret) + return -EINVAL; + + atomic_set(&max_reclaim_size, max_reclaim_size_value); + + return nbytes; +} + +static int swapd_max_reclaim_size_show(struct seq_file *m, void *v) +{ + seq_printf(m, "swapd_max_reclaim_size: %u\n", + atomic_read(&max_reclaim_size)); + + return 0; +} + +static int infos_pagefault_level_write(struct cgroup_subsys_state *css, + struct cftype *cft, s64 val) +{ + if (val < 0) + return -EINVAL; + + atomic64_set(&infos_pagefault_level, val); + + return 0; +} + +static s64 infos_pagefault_level_read(struct cgroup_subsys_state *css, + struct cftype *cft) +{ + return atomic64_read(&infos_pagefault_level); +} + +static int nothing_ignore_skip_interval_write(struct cgroup_subsys_state *css, + struct cftype *cft, s64 val) +{ + if (val < 0) + return -EINVAL; + + atomic64_set(¬hing_ignore_skip_interval, val); + + return 0; +} + +static s64 nothing_ignore_skip_interval_read(struct cgroup_subsys_state *css, + struct cftype *cft) +{ + return atomic64_read(¬hing_ignore_skip_interval); +} + +static int max_skip_interval_write(struct cgroup_subsys_state *css, + struct cftype *cft, s64 val) +{ + if (val < 0) + return -EINVAL; + + atomic64_set(&max_skip_interval, val); + + return 0; +} + +static s64 max_skip_interval_read(struct cgroup_subsys_state *css, + struct cftype *cft) +{ + return atomic64_read(&max_skip_interval); +} + +static int nothing_ignore_check_level_write(struct cgroup_subsys_state *css, + struct cftype *cft, s64 val) +{ + if (val < 0) + return -EINVAL; + + atomic64_set(¬hing_ignore_check_level, val); + + return 0; +} + +static s64 nothing_ignore_check_level_read(struct cgroup_subsys_state *css, + struct cftype *cft) +{ + return atomic64_read(¬hing_ignore_check_level); +} + +static int pagefault_refresh_min_write( + struct cgroup_subsys_state *css, struct cftype *cft, s64 val) +{ + if (val < 0) + return -EINVAL; + + atomic64_set(&pagefault_refresh_min, val); + + return 0; +} + +static s64 pagefault_refresh_min_read( + struct cgroup_subsys_state *css, struct cftype *cft) +{ + return atomic64_read(&pagefault_refresh_min); +} + +static int zram_critical_thres_write(struct cgroup_subsys_state *css, + struct cftype *cft, s64 val) +{ + if (val < 0) + return -EINVAL; + + atomic64_set(&zram_crit_thres, val << (20 - PAGE_SHIFT)); + + return 0; +} + +static s64 zram_critical_thres_read(struct cgroup_subsys_state *css, + struct cftype *cft) +{ + return atomic64_read(&zram_crit_thres) >> (20 - PAGE_SHIFT); +} + +static s64 cpuload_level_read(struct cgroup_subsys_state *css, + struct cftype *cft) + +{ + return atomic64_read(&cpuload_level); +} + +static int cpuload_level_write(struct cgroup_subsys_state *css, + struct cftype *cft, s64 val) +{ + if (val < 0) + return -EINVAL; + + atomic64_set(&cpuload_level, val); + + return 0; +} + +static s64 swapid_read(struct cgroup_subsys_state *css, struct cftype *cft) +{ + return swapid; +} + +static void swapd_mcgs_setup_parse(int level_num) +{ + struct mem_cgroup *memcg = NULL; + memcg_hybs_t *hybs = NULL; + int i, k; + + while ((memcg = fetch_next_memcg(memcg))) { + hybs = MEMCGRP_ITEM_DATA(memcg); + + if (!hybs) continue; + + for (i = level_num - 1, k = 0; i >= 0; i--) { + if (atomic64_read(&hybs->app_grade) >= zswap_param[i].min_grade && + atomic64_read(&hybs->app_grade) <= zswap_param[i].max_grade) { + k = i; + break; + } + else if (atomic64_read(&hybs->app_grade) <= zswap_param[i].max_grade) { + k = i; + } + } + atomic_set(&hybs->mem2zram_scale, zswap_param[k].mem2zram_scale); + atomic_set(&hybs->zram2ufs_scale, zswap_param[k].zram2ufs_scale); + atomic_set(&hybs->pagefault_level, zswap_param[k].pagefault_level); + } +} + +static void update_swapd_memcg_hybs(memcg_hybs_t *hybs) +{ + int i; + + for (i = 0; i < SWAPD_MAX_LEVEL_NUM; ++i) { + if (!zswap_param[i].min_grade && !zswap_param[i].max_grade) + return; + + if (atomic64_read(&hybs->app_grade) >= zswap_param[i].min_grade && + atomic64_read(&hybs->app_grade) <= zswap_param[i].max_grade) + break; + } + + if (i == SWAPD_MAX_LEVEL_NUM) + return; + + atomic_set(&hybs->mem2zram_scale, zswap_param[i].mem2zram_scale); + atomic_set(&hybs->zram2ufs_scale, zswap_param[i].zram2ufs_scale); + atomic_set(&hybs->pagefault_level, zswap_param[i].pagefault_level); +} + +void update_swapd_mcg_setup(struct mem_cgroup *memcg) +{ + memcg_hybs_t *hybs = MEMCGRP_ITEM_DATA(memcg); + + if (!hybs) + return; + + update_swapd_memcg_hybs(hybs); +} + +static int update_swapd_mcgs_setup(char *buf) +{ + const char delim[] = " "; + char *token = NULL; + int level_num; + int i; + + buf = strstrip(buf); + token = strsep(&buf, delim); + + if (!token) + return -EINVAL; + + if (kstrtoint(token, 0, &level_num)) + return -EINVAL; + + if (level_num > SWAPD_MAX_LEVEL_NUM || level_num < 0) + return -EINVAL; + + mutex_lock(&reclaim_para_lock); + for (i = 0; i < level_num; ++i) { + token = strsep(&buf, delim); + if (!token) + goto out; + + if (kstrtoint(token, 0, &zswap_param[i].min_grade) || + zswap_param[i].min_grade > MAX_APP_GRADE) + goto out; + + token = strsep(&buf, delim); + if (!token) + goto out; + + if (kstrtoint(token, 0, &zswap_param[i].max_grade) || + zswap_param[i].max_grade > MAX_APP_GRADE) + goto out; + + token = strsep(&buf, delim); + if (!token) + goto out; + + if (kstrtoint(token, 0, &zswap_param[i].mem2zram_scale) || + zswap_param[i].mem2zram_scale > MAX_RATIO) + goto out; + + token = strsep(&buf, delim); + if (!token) + goto out; + + if (kstrtoint(token, 0, &zswap_param[i].zram2ufs_scale) || + zswap_param[i].zram2ufs_scale > MAX_RATIO) + goto out; + + token = strsep(&buf, delim); + if (!token) + goto out; + + if (kstrtoint(token, 0, &zswap_param[i].pagefault_level)) + goto out; + } + + swapd_mcgs_setup_parse(level_num); + mutex_unlock(&reclaim_para_lock); + return 0; + +out: + mutex_unlock(&reclaim_para_lock); + return -EINVAL; +} + +static ssize_t swapd_mcgs_setup_write(struct kernfs_open_file *of, char *buf, + size_t nbytes, loff_t off) +{ + int ret = update_swapd_mcgs_setup(buf); + + if (ret) + return ret; + + return nbytes; +} + +static int swapd_mcgs_setup_show(struct seq_file *m, void *v) +{ + int i; + + for (i = 0; i < SWAPD_MAX_LEVEL_NUM; ++i) { + seq_printf(m, "level %d min score: %u\n", + i, zswap_param[i].min_grade); + seq_printf(m, "level %d max score: %u\n", + i, zswap_param[i].max_grade); + seq_printf(m, "level %d ub_mem2zram_ratio: %u\n", + i, zswap_param[i].mem2zram_scale); + seq_printf(m, "level %d ub_zram2ufs_ratio: %u\n", + i, zswap_param[i].zram2ufs_scale); + seq_printf(m, "memcg %d refault_threshold: %u\n", + i, zswap_param[i].pagefault_level); + } + + return 0; +} + +static ssize_t swapd_nap_jiffies_write(struct kernfs_open_file *of, char *buf, + size_t nbytes, loff_t off) +{ + unsigned long nap; + + buf = strstrip(buf); + if (!buf) + return -EINVAL; + + if (kstrtoul(buf, 0, &nap)) + return -EINVAL; + + swapd_nap_jiffies = nap; + return nbytes; +} + +static int swapd_nap_jiffies_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%lu\n", swapd_nap_jiffies); + + return 0; +} + +static ssize_t swapd_single_mcg_setup_write(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) +{ + struct mem_cgroup *memcg = mem_cgroup_from_css(of_css(of)); + unsigned int mem2zram_scale; + unsigned int zram2ufs_scale; + unsigned int pagefault_level; + memcg_hybs_t *hybs = MEMCGRP_ITEM_DATA(memcg); + + if (!hybs) + return -EINVAL; + + buf = strstrip(buf); + + if (sscanf(buf, "%u %u %u", &mem2zram_scale, &zram2ufs_scale, + &pagefault_level) != 3) + return -EINVAL; + + if (mem2zram_scale > MAX_RATIO || zram2ufs_scale > MAX_RATIO) + return -EINVAL; + + atomic_set(&MEMCGRP_ITEM(memcg, mem2zram_scale), mem2zram_scale); + atomic_set(&MEMCGRP_ITEM(memcg, zram2ufs_scale), zram2ufs_scale); + atomic_set(&MEMCGRP_ITEM(memcg, pagefault_level), pagefault_level); + + return nbytes; +} + +static int swapd_single_mcg_setup_show(struct seq_file *m, void *v) +{ + struct mem_cgroup *memcg = mem_cgroup_from_css(seq_css(m)); + memcg_hybs_t *hybs = MEMCGRP_ITEM_DATA(memcg); + + if (!hybs) + return -EINVAL; + + seq_printf(m, "memcg score: %llu\n", + atomic64_read(&hybs->app_grade)); + seq_printf(m, "memcg ub_mem2zram_ratio: %u\n", + atomic_read(&hybs->mem2zram_scale)); + seq_printf(m, "memcg ub_zram2ufs_ratio: %u\n", + atomic_read(&hybs->zram2ufs_scale)); + seq_printf(m, "memcg refault_threshold: %u\n", + atomic_read(&hybs->pagefault_level)); + + return 0; +} + +static int mem_cgroup_zram_wm_scale_write(struct cgroup_subsys_state *css, + struct cftype *cft, s64 val) +{ + if (val > MAX_RATIO || val < MIN_RATIO) + return -EINVAL; + + atomic64_set(&zram_wm_scale, val); + + return 0; +} + +static s64 mem_cgroup_zram_wm_scale_read(struct cgroup_subsys_state *css, + struct cftype *cft) +{ + return atomic64_read(&zram_wm_scale); +} + +static int mem_cgroup_compress_scale_write(struct cgroup_subsys_state *css, + struct cftype *cft, s64 val) +{ + if (val > MAX_RATIO || val < MIN_RATIO) + return -EINVAL; + + atomic64_set(&compress_scale, val); + + return 0; +} + +static s64 mem_cgroup_compress_scale_read(struct cgroup_subsys_state *css, + struct cftype *cft) +{ + return atomic64_read(&compress_scale); +} + +static int memcg_active_app_info_list_show(struct seq_file *m, void *v) +{ + struct mem_cgroup *memcg = NULL; + unsigned long anon_size; + unsigned long zram_size; + unsigned long eswap_size; + + while ((memcg = fetch_next_memcg(memcg))) { + u64 grade; + + if (!MEMCGRP_ITEM_DATA(memcg)) + continue; + + grade = atomic64_read(&MEMCGRP_ITEM(memcg, app_grade)); + anon_size = memcg_anon_pages(memcg); + eswap_size = hybridswap_read_mcg_stats(memcg, + MCG_DISK_STORED_PG_SZ); + zram_size = hybridswap_read_mcg_stats(memcg, + MCG_ZRAM_STORED_PG_SZ); + + if (anon_size + zram_size + eswap_size == 0) + continue; + + if (!strlen(MEMCGRP_ITEM(memcg, name))) + continue; + + anon_size *= PAGE_SIZE / SZ_1K; + zram_size *= PAGE_SIZE / SZ_1K; + eswap_size *= PAGE_SIZE / SZ_1K; + + seq_printf(m, "%s %llu %lu %lu %lu %llu\n", + MEMCGRP_ITEM(memcg, name), grade, + anon_size, zram_size, eswap_size, + MEMCGRP_ITEM(memcg, reclaimed_pagefault)); + } + return 0; +} + +static unsigned long fetch_totalreserve_pages(void) +{ + int nid; + unsigned long val = 0; + + for_each_node_state(nid, N_MEMORY) { + pg_data_t *pgdat = NODE_DATA(nid); + + if (pgdat) + val += pgdat->totalreserve_pages; + } + + return val; +} + +struct pglist_data *first_online_pgdat(void) +{ + return NODE_DATA(first_online_node); +} + +struct pglist_data *next_online_pgdat(struct pglist_data *pgdat) +{ + int nid = next_online_node(pgdat->node_id); + + if (nid == MAX_NUMNODES) + return NULL; + return NODE_DATA(nid); +} + +struct zone *next_zone(struct zone *zone) +{ + pg_data_t *pgdat = zone->zone_pgdat; + + if (zone < pgdat->node_zones + MAX_NR_ZONES - 1) + zone++; + else { + pgdat = next_online_pgdat(pgdat); + if (pgdat) + zone = pgdat->node_zones; + else + zone = NULL; + } + return zone; +} + +unsigned int system_cur_usable_mem(void) +{ + unsigned long reclaimable; + long buffers; + unsigned long pagecache; + unsigned long wmark_low = 0; + struct zone *zone; + + buffers = global_zone_page_state(NR_FREE_PAGES) - all_totalreserve_pages; + + for_each_zone(zone) + wmark_low += low_wmark_pages(zone); + pagecache = global_node_page_state(NR_ACTIVE_FILE) + + global_node_page_state(NR_INACTIVE_FILE); + pagecache -= min(pagecache / 2, wmark_low); + buffers += pagecache; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) + reclaimable = global_node_page_state(NR_SLAB_RECLAIMABLE) + + global_node_page_state(NR_KERNEL_MISC_RECLAIMABLE); +#else + reclaimable = global_node_page_state(NR_SLAB_RECLAIMABLE_B) + + global_node_page_state(NR_KERNEL_MISC_RECLAIMABLE); +#endif + buffers += reclaimable - min(reclaimable / 2, wmark_low); + + if (buffers < 0) + buffers = 0; + + return buffers >> 8; /* pages to MB */ +} + +static bool min_buffer_is_suitable(void) +{ + u32 curr_buffers = system_cur_usable_mem(); + + if (curr_buffers >= fetch_min_mem_watermark_value()) + return true; + + return false; +} + +bool high_buffer_is_suitable(void) +{ + u32 curr_buffers = system_cur_usable_mem(); + + if (curr_buffers >= fetch_high_mem_watermark_value()) + return true; + + return false; +} + +static void refresh_pagefaults(void) +{ + struct mem_cgroup *memcg = NULL; + + while ((memcg = fetch_next_memcg(memcg))) { + MEMCGRP_ITEM(memcg, reclaimed_pagefault) = + hybridswap_read_mcg_stats(memcg, MCG_ANON_FAULT_CNT); + } + + infos_last_anon_pagefault = hybridswap_fetch_zram_pagefault(); + last_refresh_t = jiffies; +} + +bool fetch_memcg_pagefault_status(struct mem_cgroup *memcg, + pg_data_t *pgdat) +{ + const unsigned int percent_constant = 100; + unsigned long long cur_anon_pagefault; + unsigned long anon_total; + unsigned int scale, thresh; + memcg_hybs_t *hybs; + + if (!memcg || !MEMCGRP_ITEM_DATA(memcg)) + return false; + + hybs = MEMCGRP_ITEM_DATA(memcg); + thresh = atomic_read(&hybs->pagefault_level); + if (thresh == 0) + return false; + + cur_anon_pagefault = hybridswap_read_mcg_stats(memcg, MCG_ANON_FAULT_CNT); + if (cur_anon_pagefault == hybs->reclaimed_pagefault) + return false; + + anon_total = memcg_anon_pages(memcg) + + hybridswap_read_mcg_stats(memcg, MCG_DISK_STORED_PG_SZ) + + hybridswap_read_mcg_stats(memcg, MCG_ZRAM_STORED_PG_SZ); + scale = (cur_anon_pagefault - hybs->reclaimed_pagefault) * + percent_constant / (anon_total + 1); + hybp(HYB_INFO, "CHECK MEMCG PAGEFAULT %s: anon pagefault %u%%(%u)\n", hybs->name, + scale, thresh); + + if (scale >= thresh) + return true; + + return false; +} + +static bool hybridswap_scale_ok(void) +{ + struct hybstatus *stat = NULL; + + stat = hybridswap_fetch_stat_obj(); + if (unlikely(!stat)) { + hybp(HYB_ERR, "can't fetch stat obj!\n"); + + return false; + } + + return (atomic64_read(&stat->zram_stored_pages) > + atomic64_read(&stat->stored_pages)); +} + +static bool fetch_infos_pagefault_status(void) +{ + const unsigned int percent_constant = 1000; + unsigned long long cur_anon_pagefault; + unsigned long long cur_time; + unsigned long long scale = 0; + + cur_anon_pagefault = hybridswap_fetch_zram_pagefault(); + cur_time = jiffies; + + if (cur_anon_pagefault == infos_last_anon_pagefault + || cur_time == last_refresh_t) + goto false_out; + + scale = (cur_anon_pagefault - infos_last_anon_pagefault) * + percent_constant / (jiffies_to_msecs(cur_time - + last_refresh_t) + 1); + + total_pagefault_percent = scale; + + if (scale > fetch_infos_pagefault_level_value()) + return true; + + hybp(HYB_INFO, "current %llu t %llu last %llu t %lu scale %llu pagefault_scale %llu\n", + cur_anon_pagefault, cur_time, + infos_last_anon_pagefault, last_refresh_t, + scale, (unsigned long long)infos_pagefault_level.counter); +false_out: + return false; +} + +static int reclaim_exceed_sleep_ms_write( + struct cgroup_subsys_state *css, struct cftype *cft, s64 val) +{ + if (val < 0) + return -EINVAL; + + reclaim_exceed_sleep_ms = val; + + return 0; +} + +static s64 reclaim_exceed_sleep_ms_read( + struct cgroup_subsys_state *css, struct cftype *cft) +{ + return reclaim_exceed_sleep_ms; +} + +static int max_reclaimin_size_mb_write( + struct cgroup_subsys_state *css, struct cftype *cft, u64 val) +{ + max_reclaimin_size = (val << 20); + + return 0; +} + +static u64 max_reclaimin_size_mb_read( + struct cgroup_subsys_state *css, struct cftype *cft) +{ + return max_reclaimin_size >> 20; +} + +static ssize_t swapd_shrink_parameter_write(struct kernfs_open_file *of, + char *buf, size_t nbytes, loff_t off) +{ + unsigned long window, limit; + + buf = strstrip(buf); + if (sscanf(buf, "%lu %lu", &window, &limit) != 2) + return -EINVAL; + + swapd_shrink_window = msecs_to_jiffies(window); + swapd_shrink_limit_per_window = limit; + + return nbytes; +} + +static int swapd_shrink_parameter_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%-32s %lu(jiffies) %u(msec)\n", "swapd_shrink_window", + swapd_shrink_window, jiffies_to_msecs(swapd_shrink_window)); + seq_printf(m, "%-32s %lu MB\n", "swapd_shrink_limit_per_window", + swapd_shrink_limit_per_window); + seq_printf(m, "%-32s %u msec\n", "swapd_last_window", + jiffies_to_msecs(jiffies - swapd_last_window_start)); + seq_printf(m, "%-32s %lu MB\n", "swapd_last_window_shrink", + swapd_last_window_shrink); + + return 0; +} + +static int swapd_update_cpumask(struct task_struct *tsk, char *buf, + struct pglist_data *pgdat) +{ + int retval; + struct cpumask temp_mask; + const struct cpumask *cpumask = cpumask_of_node(pgdat->node_id); + struct hybridswapd_task* hyb_task = PGDAT_ITEM_DATA(pgdat); + + if (unlikely(!hyb_task)) { + hybp(HYB_ERR, "set task %s cpumask %s node %d failed, " + "hyb_task is NULL\n", tsk->comm, buf, pgdat->node_id); + return -EINVAL; + } + + cpumask_clear(&temp_mask); + retval = cpulist_parse(buf, &temp_mask); + if (retval < 0 || cpumask_empty(&temp_mask)) { + hybp(HYB_ERR, "%s are invalid, use default\n", buf); + goto use_default; + } + + if (!cpumask_subset(&temp_mask, cpu_present_mask)) { + hybp(HYB_ERR, "%s is not subset of cpu_present_mask, use default\n", + buf); + goto use_default; + } + + if (!cpumask_subset(&temp_mask, cpumask)) { + hybp(HYB_ERR, "%s is not subset of cpumask, use default\n", buf); + goto use_default; + } + + set_cpus_allowed_ptr(tsk, &temp_mask); + cpumask_copy(&hyb_task->swapd_bind_cpumask, &temp_mask); + return 0; + +use_default: + if (cpumask_empty(&hyb_task->swapd_bind_cpumask)) + set_cpus_allowed_ptr(tsk, cpumask); + return -EINVAL; +} + +static ssize_t swapd_bind_write(struct kernfs_open_file *of, char *buf, + size_t nbytes, loff_t off) +{ + int ret = 0, nid; + struct pglist_data *pgdat; + + buf = strstrip(buf); + for_each_node_state(nid, N_MEMORY) { + pgdat = NODE_DATA(nid); + if (!PGDAT_ITEM_DATA(pgdat)) + continue; + + if (PGDAT_ITEM(pgdat, swapd)) { + ret = swapd_update_cpumask(PGDAT_ITEM(pgdat, swapd), + buf, pgdat); + if (ret) + break; + } + } + + if (ret) + return ret; + + return nbytes; +} + +static int swapd_bind_read(struct seq_file *m, void *v) +{ + int nid; + struct pglist_data *pgdat; + struct hybridswapd_task* hyb_task; + + seq_printf(m, "%4s %s\n", "Node", "mask"); + for_each_node_state(nid, N_MEMORY) { + pgdat = NODE_DATA(nid); + hyb_task = PGDAT_ITEM_DATA(pgdat); + if (!hyb_task) + continue; + + if (!hyb_task->swapd) + continue; + seq_printf(m, "%4d %*pbl\n", nid, + cpumask_pr_args(&hyb_task->swapd_bind_cpumask)); + } + + return 0; +} + +static s64 mem_cgroup_version_read(struct cgroup_subsys_state *css, + struct cftype *cft) +{ + return MOTO_SWAP_VERSION; +} + +struct cftype mem_cgroup_swapd_legacy_files[] = { + { + .name = "moto_swap_version", + .flags = CFTYPE_ONLY_ON_ROOT, + .read_s64 = mem_cgroup_version_read, + }, + { + .name = "active_app_info_list", + .flags = CFTYPE_ONLY_ON_ROOT, + .seq_show = memcg_active_app_info_list_show, + }, + { + .name = "zram_wm_ratio", + .flags = CFTYPE_ONLY_ON_ROOT, + .write_s64 = mem_cgroup_zram_wm_scale_write, + .read_s64 = mem_cgroup_zram_wm_scale_read, + }, + { + .name = "compress_ratio", + .flags = CFTYPE_ONLY_ON_ROOT, + .write_s64 = mem_cgroup_compress_scale_write, + .read_s64 = mem_cgroup_compress_scale_read, + }, + { + .name = "swapd_pid", + .flags = CFTYPE_ONLY_ON_ROOT, + .read_s64 = swapid_read, + }, + { + .name = "avail_buffers", + .flags = CFTYPE_ONLY_ON_ROOT, + .write = usable_mem_params_write, + .seq_show = usable_mem_params_show, + }, + { + .name = "swapd_max_reclaim_size", + .flags = CFTYPE_ONLY_ON_ROOT, + .write = swapd_max_reclaim_size_write, + .seq_show = swapd_max_reclaim_size_show, + }, + { + .name = "area_anon_refault_threshold", + .flags = CFTYPE_ONLY_ON_ROOT, + .write_s64 = infos_pagefault_level_write, + .read_s64 = infos_pagefault_level_read, + }, + { + .name = "empty_round_skip_interval", + .flags = CFTYPE_ONLY_ON_ROOT, + .write_s64 = nothing_ignore_skip_interval_write, + .read_s64 = nothing_ignore_skip_interval_read, + }, + { + .name = "max_skip_interval", + .flags = CFTYPE_ONLY_ON_ROOT, + .write_s64 = max_skip_interval_write, + .read_s64 = max_skip_interval_read, + }, + { + .name = "empty_round_check_threshold", + .flags = CFTYPE_ONLY_ON_ROOT, + .write_s64 = nothing_ignore_check_level_write, + .read_s64 = nothing_ignore_check_level_read, + }, + { + .name = "anon_refault_snapshot_min_interval", + .flags = CFTYPE_ONLY_ON_ROOT, + .write_s64 = pagefault_refresh_min_write, + .read_s64 = pagefault_refresh_min_read, + }, + { + .name = "swapd_mcgs_setup", + .flags = CFTYPE_ONLY_ON_ROOT, + .write = swapd_mcgs_setup_write, + .seq_show = swapd_mcgs_setup_show, + }, + { + .name = "swapd_single_mcg_setup", + .write = swapd_single_mcg_setup_write, + .seq_show = swapd_single_mcg_setup_show, + }, + { + .name = "zram_critical_threshold", + .flags = CFTYPE_ONLY_ON_ROOT, + .write_s64 = zram_critical_thres_write, + .read_s64 = zram_critical_thres_read, + }, + { + .name = "cpuload_threshold", + .flags = CFTYPE_ONLY_ON_ROOT, + .write_s64 = cpuload_level_write, + .read_s64 = cpuload_level_read, + }, + { + .name = "reclaim_exceed_sleep_ms", + .flags = CFTYPE_ONLY_ON_ROOT, + .write_s64 = reclaim_exceed_sleep_ms_write, + .read_s64 = reclaim_exceed_sleep_ms_read, + }, + { + .name = "swapd_bind", + .flags = CFTYPE_ONLY_ON_ROOT, + .write = swapd_bind_write, + .seq_show = swapd_bind_read, + }, + { + .name = "max_reclaimin_size_mb", + .flags = CFTYPE_ONLY_ON_ROOT, + .write_u64 = max_reclaimin_size_mb_write, + .read_u64 = max_reclaimin_size_mb_read, + }, + { + .name = "swapd_shrink_parameter", + .flags = CFTYPE_ONLY_ON_ROOT, + .write = swapd_shrink_parameter_write, + .seq_show = swapd_shrink_parameter_show, + }, + { + .name = "swapd_nap_jiffies", + .flags = CFTYPE_ONLY_ON_ROOT, + .write = swapd_nap_jiffies_write, + .seq_show = swapd_nap_jiffies_show, + }, + { }, /* terminate */ +}; + +void wakeup_refresh_daemon(void) +{ + unsigned long curr_refresh = + jiffies_to_msecs(jiffies - last_refresh_t); + + if (curr_refresh >= + fetch_pagefault_refresh_min_value()) { + atomic_set(&refresh_daemonwait_flag, 1); + wake_up_interruptible(&refresh_daemonwait); + } +} + +static int refresh_daemon(void *p) +{ + int ret; + + while (!kthread_should_stop()) { + ret = wait_event_interruptible(refresh_daemonwait, + atomic_read(&refresh_daemonwait_flag)); + if (ret) + continue; + + if (unlikely(kthread_should_stop())) + break; + + atomic_set(&refresh_daemonwait_flag, 0); + + refresh_pagefaults(); + count_swapd_event(SWAPD_SNAPSHOT_TIMES); + } + + return 0; +} + +static int refresh_daemonrun(void) +{ + atomic_set(&refresh_daemonwait_flag, 0); + init_waitqueue_head(&refresh_daemonwait); + refresh_daemontask = kthread_run(refresh_daemon, NULL, "snapshotd"); + + if (IS_ERR(refresh_daemontask)) { + hybp(HYB_ERR, "Failed to start refresh_daemon\n"); + return PTR_ERR(refresh_daemontask); + } + + return 0; +} + +static void refresh_daemonexit(void) +{ + if (refresh_daemontask) { + atomic_set(&refresh_daemonwait_flag, 1); + kthread_stop(refresh_daemontask); + } + refresh_daemontask = NULL; +} + +unsigned long fetch_nr_zram_total(void) +{ + unsigned long nr_zram = 1; + + if (!swapd_zram) + return nr_zram; + + nr_zram = swapd_zram->disksize >> PAGE_SHIFT; +#if (defined CONFIG_ZRAM_WRITEBACK) || (defined CONFIG_HYBRIDSWAP_CORE) + nr_zram -= swapd_zram->increase_nr_pages; +#endif + return nr_zram ?: 1; +} + +u64 get_hybridswap_meminfo(const char *type) +{ + if (!type || !swapd_zram) + return 0; + + if (!strcmp(type, "same_pages")) + return (u64)atomic64_read(&swapd_zram->stats.same_pages); + if (!strcmp(type, "compr_data_size")) + return (u64)atomic64_read(&swapd_zram->stats.compr_data_size); + if (!strcmp(type, "pages_stored")) + return (u64)atomic64_read(&swapd_zram->stats.pages_stored); + return 0; +} +EXPORT_SYMBOL(get_hybridswap_meminfo); + +bool zram_watermark_ok(void) +{ + long long diff_buffers; + long long wm = 0; + long long cur_scale = 0; + unsigned long zram_used = hybridswap_fetch_zram_used_pages(); + const unsigned int percent_constant = 100; + + diff_buffers = fetch_high_mem_watermark_value() - + system_cur_usable_mem(); + diff_buffers *= SZ_1M / PAGE_SIZE; + diff_buffers *= fetch_compress_scale_value() / 10; + diff_buffers = diff_buffers * percent_constant / fetch_nr_zram_total(); + + cur_scale = zram_used * percent_constant / fetch_nr_zram_total(); + wm = min(fetch_zram_wm_scale_value(), fetch_zram_wm_scale_value()- diff_buffers); + + return cur_scale > wm; +} + +static inline bool zram_is_low(void) +{ + unsigned long nr_used = hybridswap_fetch_zram_used_pages(); + unsigned long nr_total = fetch_nr_zram_total(); + + return ((nr_total - nr_used) < fetch_free_swap_level_value()); +} + +bool free_zram_is_ok(void) +{ + unsigned long nr_used = hybridswap_fetch_zram_used_pages(); + unsigned long nr_total = fetch_nr_zram_total(); + unsigned long reserve = nr_total >> 6; + + return (nr_used < (nr_total - reserve)); +} + +bool free_swap_is_low(void) +{ + struct sysinfo info; + si_swapinfo(&info); + return (info.freeswap < fetch_free_swap_level_value()); +} + +static bool zram_need_swapout(void) +{ + bool zram_wm_ok = zram_watermark_ok(); + bool avail_buffer_wm_ok = !high_buffer_is_suitable(); + bool ufs_wm_ok = true; + +#ifdef CONFIG_HYBRIDSWAP_CORE + ufs_wm_ok = hybridswap_stored_wm_ok(); +#endif + + if (zram_wm_ok && avail_buffer_wm_ok && ufs_wm_ok) + return true; + + hybp(HYB_INFO, "zram_wm_ok %d avail_buffer_wm_ok %d ufs_wm_ok %d\n", + zram_wm_ok, avail_buffer_wm_ok, ufs_wm_ok); + + return false; +} + +bool zram_watermark_exceed(void) +{ + u64 nr_zram_used; + u64 nr_wm = fetch_zram_critical_level_value(); + + if (!nr_wm) + return false; + + nr_zram_used = hybridswap_fetch_zram_used_pages(); + + if (nr_zram_used > nr_wm) + return true; + + return false; +} + +#ifdef CONFIG_OPLUS_JANK +static bool is_cpu_busy(void) +{ + unsigned int cpuload = 0; + int i; + struct cpumask mask; + + cpumask_clear(&mask); + + for (i = 0; i < 6; i++) + cpumask_set_cpu(i, &mask); + + cpuload = fetch_cpu_load(1, &mask); + if (cpuload > fetch_cpuload_level_value()) { + hybp(HYB_INFO, "cpuload %d\n", cpuload); + return true; + } + + return false; +} +#endif + +static void wakeup_swapd(pg_data_t *pgdat) +{ + unsigned long curr_interval; + struct hybridswapd_task* hyb_task = PGDAT_ITEM_DATA(pgdat); + + if (!hyb_task || !hyb_task->swapd) + return; + + if (atomic_read(&swapd_pause)) { + count_swapd_event(SWAPD_MANUAL_PAUSE); + return; + } + +#if IS_ENABLED(CONFIG_DRM_MSM) || IS_ENABLED(CONFIG_DRM_OPLUS_NOTIFY) + if (atomic_read(&display_off)) + return; +#endif + + if (!waitqueue_active(&hyb_task->swapd_wait)) + return; + + if (atomic_read(&refresh_daemoninit_flag) == 1) + wakeup_refresh_daemon(); + + if (min_buffer_is_suitable()) { + count_swapd_event(SWAPD_OVER_MIN_BUFFER_SKIP_TIMES); + return; + } + + curr_interval = jiffies_to_msecs(jiffies - last_swapd_time); + if (curr_interval < swapd_skip_interval) { + count_swapd_event(SWAPD_EMPTY_ROUND_SKIP_TIMES); + return; + } + + atomic_set(&hyb_task->swapd_wait_flag, 1); + wake_up_interruptible(&hyb_task->swapd_wait); +} + +void wake_all_swapd(void) +{ + pg_data_t *pgdat = NULL; + int nid; + + for_each_online_node(nid) { + pgdat = NODE_DATA(nid); + wakeup_swapd(pgdat); + } +} + +static inline u64 __calc_nr_to_reclaim(void) +{ + u32 curr_buffers; + u64 high_buffers; + u64 max_reclaim_size_value; + u64 reclaim_size = 0; + + high_buffers = fetch_high_mem_watermark_value(); + curr_buffers = system_cur_usable_mem(); + max_reclaim_size_value = fetch_swapd_max_reclaim_size(); + if (curr_buffers < high_buffers) + reclaim_size = high_buffers - curr_buffers; + + reclaim_size = min(reclaim_size, max_reclaim_size_value); + + return reclaim_size * SZ_1M / PAGE_SIZE; +} + +static inline u64 calc_shrink_scale(pg_data_t *pgdat) +{ + struct mem_cgroup *memcg = NULL; + const u32 percent_constant = 100; + u64 total_can_reclaimed = 0; + + while ((memcg = fetch_next_memcg(memcg))) { + s64 nr_anon, nr_zram, nr_eswap, total, can_reclaimed, thresh; + memcg_hybs_t *hybs; + + hybs = MEMCGRP_ITEM_DATA(memcg); + thresh = atomic_read(&hybs->mem2zram_scale); + if (!thresh || fetch_memcg_pagefault_status(memcg, pgdat)) { + hybs->can_reclaimed = 0; + continue; + } + + nr_anon = memcg_anon_pages(memcg); + if (nr_anon == 0) { + hybs->can_reclaimed = 0; + continue; + } + + nr_zram = hybridswap_read_mcg_stats(memcg, MCG_ZRAM_STORED_PG_SZ); + nr_eswap = hybridswap_read_mcg_stats(memcg, MCG_DISK_STORED_PG_SZ); + total = nr_anon + nr_zram + nr_eswap; + + can_reclaimed = total * thresh / percent_constant; + if (can_reclaimed <= (nr_zram + nr_eswap)) + hybs->can_reclaimed = 0; + else + hybs->can_reclaimed = can_reclaimed - (nr_zram + nr_eswap); + + if (hybs->can_reclaimed < PAGES_PER_1MB) { // Skip small reclaim + hybs->can_reclaimed = 0; + continue; + } + + hybp(HYB_DEBUG, "CHECK MEMCG %s: can_reclaim %luKB nr_anon %luKB zram %luKB eswap %luKB total %luKB reclaimed %lu%%(%lu)\n", + hybs->name, (unsigned long)page_to_kb(hybs->can_reclaimed), + (unsigned long)page_to_kb(nr_anon), (unsigned long)page_to_kb(nr_zram), + (unsigned long)page_to_kb(nr_eswap), (unsigned long)page_to_kb(total), + (unsigned long)((nr_zram + nr_eswap) * 100 / (total + 1)), + (unsigned long)thresh); + total_can_reclaimed += hybs->can_reclaimed; + } + + return total_can_reclaimed; +} + +static unsigned long swapd_shrink_anon(pg_data_t *pgdat, + unsigned long nr_to_reclaim) +{ + struct mem_cgroup *memcg = NULL; + unsigned long nr_reclaimed = 0; + unsigned long reclaim_memcg_cnt = 0; + u64 total_can_reclaimed = calc_shrink_scale(pgdat); + unsigned long start_js = jiffies; + bool exit = false; + unsigned long RECLAIM_PAGES_PER_CYCLE = PAGES_PER_1MB * 100; + unsigned long reclaim_pages_this_cycle = 0; + unsigned long reclaim_cycles = 0; + + if (unlikely(total_can_reclaimed == 0)) + goto out; + + if (total_can_reclaimed < nr_to_reclaim) + nr_to_reclaim = total_can_reclaimed; + + reclaim_cycles = (nr_to_reclaim / RECLAIM_PAGES_PER_CYCLE) + (nr_to_reclaim % RECLAIM_PAGES_PER_CYCLE > 0 ? 1 : 0); + + hybp(HYB_INFO, "SWAPD_SHRINK ANON + available %uMB(%lu) to_reclaim %luKB can_reclaimed %luKB reclaim_cycles %lu", + (unsigned int)system_cur_usable_mem(), (unsigned long)fetch_high_mem_watermark_value(), + page_to_kb(nr_to_reclaim), (unsigned long)page_to_kb(total_can_reclaimed), reclaim_cycles); + + while (reclaim_cycles) { + reclaim_pages_this_cycle = min(nr_to_reclaim - nr_reclaimed, RECLAIM_PAGES_PER_CYCLE); + if (reclaim_pages_this_cycle <= 0) break; + + while ((memcg = fetch_next_memcg(memcg))) { + unsigned long memcg_nr_reclaimed, memcg_to_reclaim; + memcg_hybs_t *hybs; + + if (high_buffer_is_suitable() || atomic_read(&swapd_pause)) { + fetch_next_memcg_break(memcg); + exit = true; + break; + } + + hybs = MEMCGRP_ITEM_DATA(memcg); + if (!hybs->can_reclaimed) + continue; + + memcg_to_reclaim = reclaim_pages_this_cycle * hybs->can_reclaimed / total_can_reclaimed; + if (memcg_to_reclaim > 0) { + memcg_nr_reclaimed = try_to_free_mem_cgroup_pages(memcg, + memcg_to_reclaim, GFP_KERNEL, true); + reclaim_memcg_cnt++; + hybs->can_reclaimed -= memcg_nr_reclaimed; + hybp(HYB_DEBUG, "SHRINK MEMCG %s: to_reclaim %lu reclaimed %lu\n", hybs->name, + memcg_to_reclaim, memcg_nr_reclaimed); + nr_reclaimed += memcg_nr_reclaimed; + } + if (nr_reclaimed >= nr_to_reclaim) { + fetch_next_memcg_break(memcg); + exit = true; + break; + } + + if (hybs->can_reclaimed < 0 || memcg_nr_reclaimed == 0) // no pages can be reclaimed, skip it. + hybs->can_reclaimed = 0; + + if (swapd_nap_jiffies && time_after_eq(jiffies, start_js + swapd_nap_jiffies)) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout((jiffies - start_js) * 2); + start_js = jiffies; + } + } + if (exit) + break; + + if (zram_is_low()) + break; + reclaim_cycles--; + } + +out: + hybp(HYB_INFO, "SWAPD_SHRINK ANON - available %uMB(%lu) reclaimed %luKB from memcg %lu\n", + (unsigned int)system_cur_usable_mem(), (unsigned long)fetch_high_mem_watermark_value(), + page_to_kb(nr_reclaimed), reclaim_memcg_cnt); + return nr_reclaimed; +} + +static void swapd_shrink_node(pg_data_t *pgdat) +{ + const unsigned int increase_rate = 2; + unsigned long nr_reclaimed = 0; + unsigned long nr_to_reclaim; + +#if IS_ENABLED(CONFIG_DRM_MSM) || IS_ENABLED(CONFIG_DRM_OPLUS_NOTIFY) + if (atomic_read(&display_off)) + return; +#endif + + if (zram_is_low()) + return; + + if (high_buffer_is_suitable()) + return; + +#ifdef CONFIG_OPLUS_JANK + if (is_cpu_busy()) { + count_swapd_event(SWAPD_CPU_BUSY_BREAK_TIMES); + return; + } +#endif + + if ((jiffies - swapd_last_window_start) < swapd_shrink_window) { + if (swapd_last_window_shrink >= swapd_shrink_limit_per_window) { + count_swapd_event(SWAPD_SKIP_SHRINK_OF_WINDOW); + hybp(HYB_INFO, "swapd_last_window_shrink %lu, skip shrink\n", + swapd_last_window_shrink); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(reclaim_exceed_sleep_ms)); + return; + } + } else { + swapd_last_window_start = jiffies; + swapd_last_window_shrink = 0lu; + } + + nr_to_reclaim = __calc_nr_to_reclaim(); + if (!nr_to_reclaim) + return; + + count_swapd_event(SWAPD_SHRINK_ANON); + nr_reclaimed = swapd_shrink_anon(pgdat, nr_to_reclaim); + swapd_last_window_shrink += PAGES_TO_MB(nr_reclaimed); + + if (nr_reclaimed < fetch_nothing_ignore_check_level_value()) { + count_swapd_event(SWAPD_EMPTY_ROUND); + if (last_round_is_empty) + swapd_skip_interval = min(swapd_skip_interval * + increase_rate, + fetch_max_skip_interval_value()); + else + swapd_skip_interval = + fetch_nothing_ignore_skip_interval_value(); + last_round_is_empty = true; + hybp(HYB_INFO, "SWAPD_SHRINK_EMPTY_ROUND, reclaimed %lu KB, swapd_skip_interval %llu\n", + (unsigned long)nr_reclaimed * 4, swapd_skip_interval); + } else { + swapd_skip_interval = 0; + last_round_is_empty = false; + } +} + +static int swapd(void *p) +{ + pg_data_t *pgdat = (pg_data_t *)p; + struct task_struct *tsk = current; + struct hybridswapd_task* hyb_task = PGDAT_ITEM_DATA(pgdat); + static unsigned long last_reclaimin_jiffies = 0; + long page_fault_pause_value; + int display_un_blank = 1; + + swapid = tsk->pid; + + cpumask_clear(&hyb_task->swapd_bind_cpumask); + (void)swapd_update_cpumask(tsk, SWAPD_DEFAULT_BIND_CPUS, pgdat); + set_freezable(); + + swapd_last_window_start = jiffies - swapd_shrink_window; + while (!kthread_should_stop()) { + bool pagefault = false; + u64 available, wmhigh; + u64 to_swapout, swapped_size = 0; + + wait_event_freezable(hyb_task->swapd_wait, + atomic_read(&hyb_task->swapd_wait_flag)); + atomic_set(&hyb_task->swapd_wait_flag, 0); + if (unlikely(kthread_should_stop())) + break; + count_swapd_event(SWAPD_WAKEUP); + hybp(HYB_INFO, "SWAPD_WAKEUP"); + + if (fetch_infos_pagefault_status() && hybridswap_scale_ok()) { + pagefault = true; + count_swapd_event(SWAPD_REFAULT); + goto do_eswap; + } + + swapd_shrink_node(pgdat); + last_swapd_time = jiffies; +do_eswap: + page_fault_pause_value = atomic_long_read(&page_fault_pause); +#if IS_ENABLED(CONFIG_DRM_MSM) || IS_ENABLED(CONFIG_DRM_OPLUS_NOTIFY) + display_un_blank = !atomic_read(&display_off); +#endif + if (!hybridswap_reclaim_work_running() && display_un_blank && + (zram_need_swapout() || pagefault) && !page_fault_pause_value && + jiffies_to_msecs(jiffies - last_reclaimin_jiffies) >= 50) { + wmhigh = fetch_high_mem_watermark_value(); + available = system_cur_usable_mem(); + + if (available < wmhigh && !atomic_read(&swapd_pause)) { + to_swapout = (wmhigh - available) * SZ_1M; + to_swapout = min_t(u64, to_swapout, max_reclaimin_size); +#ifdef CONFIG_HYBRIDSWAP_CORE + hybp(HYB_INFO, "SWAPD_ESWAPOUT + available %uMB(%lu) to_swapout %luMB", + (unsigned int)available, (unsigned long)wmhigh, (unsigned long)(to_swapout / SZ_1M)); + swapped_size = hybridswap_out_to_eswap(to_swapout); + count_swapd_event(SWAPD_SWAPOUT); + last_reclaimin_jiffies = jiffies; + hybp(HYB_INFO, "SWAPD_ESWAPOUT - swapped out %luMB\n", (unsigned long)(swapped_size / SZ_1M)); +#endif + } else { + count_swapd_event(SWAPD_SKIP_SWAPOUT); + } + } + hybp(HYB_INFO, "SWAPD_SLEEP"); + } + + return 0; +} + +int swapd_run(int nid) +{ + pg_data_t *pgdat = NODE_DATA(nid); + struct sched_param param = { + .sched_priority = DEFAULT_PRIO, + }; + struct hybridswapd_task* hyb_task = PGDAT_ITEM_DATA(pgdat); + int ret; + + if (!hyb_task || hyb_task->swapd) + return 0; + + atomic_set(&hyb_task->swapd_wait_flag, 0); + hyb_task->swapd = kthread_create(swapd, pgdat, "mswapd:%d", nid); + if (IS_ERR(hyb_task->swapd)) { + hybp(HYB_ERR, "Failed to start swapd on node %d\n", nid); + ret = PTR_ERR(hyb_task->swapd); + hyb_task->swapd = NULL; + return ret; + } + + sched_setscheduler_nocheck(hyb_task->swapd, SCHED_NORMAL, ¶m); + set_user_nice(hyb_task->swapd, PRIO_TO_NICE(param.sched_priority)); + wake_up_process(hyb_task->swapd); + + return 0; +} + +void swapd_stop(int nid) +{ + struct pglist_data *pgdata = NODE_DATA(nid); + struct task_struct *swapd; + struct hybridswapd_task* hyb_task; + + if (unlikely(!PGDAT_ITEM_DATA(pgdata))) { + hybp(HYB_ERR, "nid %d pgdata %p PGDAT_ITEM_DATA is NULL\n", + nid, pgdata); + return; + } + + hyb_task = PGDAT_ITEM_DATA(pgdata); + swapd = hyb_task->swapd; + if (swapd) { + atomic_set(&hyb_task->swapd_wait_flag, 1); + kthread_stop(swapd); + hyb_task->swapd = NULL; + } + + swapid = -1; +} + +static int mem_hotplug_swapd_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct memory_notify *arg = (struct memory_notify*)data; + int nid = arg->status_change_nid; + + if (action == MEM_ONLINE) + swapd_run(nid); + else if (action == MEM_OFFLINE) + swapd_stop(nid); + + return NOTIFY_OK; +} + +static struct notifier_block swapd_notifier_nb = { + .notifier_call = mem_hotplug_swapd_notifier, +}; + +static int swapd_cpu_online(unsigned int cpu) +{ + int nid; + + for_each_node_state(nid, N_MEMORY) { + pg_data_t *pgdat = NODE_DATA(nid); + struct hybridswapd_task* hyb_task; + struct cpumask *mask; + + hyb_task = PGDAT_ITEM_DATA(pgdat); + mask = &hyb_task->swapd_bind_cpumask; + + if (cpumask_any_and(cpu_online_mask, mask) < nr_cpu_ids) + set_cpus_allowed_ptr(PGDAT_ITEM(pgdat, swapd), mask); + } + return 0; +} + +void alloc_pages_slowpath_hook(void *data, gfp_t gfp_flags, + unsigned int order, unsigned long delta) +{ + if (gfp_flags & __GFP_KSWAPD_RECLAIM) + wake_all_swapd(); +} + +void rmqueue_hook(void *data, struct zone *preferred_zone, + struct zone *zone, unsigned int order, gfp_t gfp_flags, + unsigned int alloc_flags, int migratetype) +{ + if (gfp_flags & __GFP_KSWAPD_RECLAIM) + wake_all_swapd(); +} + +static int create_swapd_thread(struct zram *zram) +{ + int nid; + int ret; + struct pglist_data *pgdat; + struct hybridswapd_task *tsk_info; + + for_each_node(nid) { + pgdat = NODE_DATA(nid); + if (!PGDAT_ITEM_DATA(pgdat)) { + tsk_info = kzalloc(sizeof(struct hybridswapd_task), + GFP_KERNEL); + if (!tsk_info) { + hybp(HYB_ERR, "kmalloc tsk_info failed node %d\n", nid); + goto error_out; + } + + pgdat->android_oem_data1 = (u64)tsk_info; + } + + init_waitqueue_head(&PGDAT_ITEM(pgdat, swapd_wait)); + } + + for_each_node_state(nid, N_MEMORY) { + if (swapd_run(nid)) + goto error_out; + } + + ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, + "mm/swapd:online", swapd_cpu_online, NULL); + if (ret < 0) { + hybp(HYB_ERR, "swapd: failed to register hotplug callbacks.\n"); + goto error_out; + } + swapd_online = ret; + + return 0; + +error_out: + for_each_node(nid) { + pgdat = NODE_DATA(nid); + + if (!PGDAT_ITEM_DATA(pgdat)) + continue; + + if (PGDAT_ITEM(pgdat, swapd)) { + kthread_stop(PGDAT_ITEM(pgdat, swapd)); + PGDAT_ITEM(pgdat, swapd) = NULL; + } + + kfree((void*)PGDAT_ITEM_DATA(pgdat)); + pgdat->android_oem_data1 = 0; + } + + return -ENOMEM; +} + +static void destroy_swapd_thread(void) +{ + int nid; + struct pglist_data *pgdat; + + cpuhp_remove_state_nocalls(swapd_online); + for_each_node(nid) { + pgdat = NODE_DATA(nid); + if (!PGDAT_ITEM_DATA(pgdat)) + continue; + + swapd_stop(nid); + kfree((void*)PGDAT_ITEM_DATA(pgdat)); + pgdat->android_oem_data1 = 0; + } +} + +ssize_t hybridswap_swapd_pause_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + char *type_buf = NULL; + bool val; + + type_buf = strstrip((char *)buf); + if (kstrtobool(type_buf, &val)) + return -EINVAL; + atomic_set(&swapd_pause, val); + + return len; +} + +ssize_t hybridswap_swapd_pause_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t size = 0; + + size += scnprintf(buf + size, PAGE_SIZE - size, + "%d\n", atomic_read(&swapd_pause)); + + return size; +} + +#if IS_ENABLED(CONFIG_DRM_MSM) || IS_ENABLED(CONFIG_DRM_OPLUS_NOTIFY) +static int bright_fb_notifier_callback(struct notifier_block *self, + unsigned long event, void *data) +{ + struct msm_drm_notifier *evdata = data; + int *blank; + + if (evdata && evdata->data) { + blank = evdata->data; + + if (*blank == MSM_DRM_BLANK_POWERDOWN) + atomic_set(&display_off, 1); + else if (*blank == MSM_DRM_BLANK_UNBLANK) + atomic_set(&display_off, 0); + } + + return NOTIFY_OK; +} +#endif + +void __init swapd_pre_init(void) +{ + all_totalreserve_pages = fetch_totalreserve_pages(); +} + +void swapd_pre_deinit(void) +{ + all_totalreserve_pages = 0; +} + +int swapd_init(struct zram *zram) +{ + int ret; + + ret = register_memory_notifier(&swapd_notifier_nb); + if (ret) { + hybp(HYB_ERR, "register_memory_notifier failed, ret = %d\n", ret); + return ret; + } + +#if IS_ENABLED(CONFIG_DRM_MSM) || IS_ENABLED(CONFIG_DRM_OPLUS_NOTIFY) + fb_notif.notifier_call = bright_fb_notifier_callback; + ret = msm_drm_register_client(&fb_notif); + if (ret) { + hybp(HYB_ERR, "msm_drm_register_client failed, ret=%d\n", ret); + goto msm_drm_register_fail; + } +#endif + + ret = refresh_daemonrun(); + if (ret) { + hybp(HYB_ERR, "refresh_daemonrun failed, ret=%d\n", ret); + goto refresh_daemonfail; + } + + ret = create_swapd_thread(zram); + if (ret) { + hybp(HYB_ERR, "create_swapd_thread failed, ret=%d\n", ret); + goto create_swapd_fail; + } + + swapd_zram = zram; + atomic_set(&swapd_enabled, 1); + return 0; + +create_swapd_fail: + refresh_daemonexit(); +refresh_daemonfail: +#if IS_ENABLED(CONFIG_DRM_MSM) || IS_ENABLED(CONFIG_DRM_OPLUS_NOTIFY) + msm_drm_unregister_client(&fb_notif); +msm_drm_register_fail: +#endif + unregister_memory_notifier(&swapd_notifier_nb); + return ret; +} + +void swapd_exit(void) +{ + destroy_swapd_thread(); + refresh_daemonexit(); +#if IS_ENABLED(CONFIG_DRM_MSM) || IS_ENABLED(CONFIG_DRM_OPLUS_NOTIFY) + msm_drm_unregister_client(&fb_notif); +#endif + unregister_memory_notifier(&swapd_notifier_nb); + atomic_set(&swapd_enabled, 0); +} + +bool hybridswap_swapd_enabled(void) +{ + return !!atomic_read(&swapd_enabled); +} diff --git a/drivers/moto_swap/zram-5.10/zcomp.c b/drivers/moto_swap/zram-5.10/zcomp.c new file mode 100644 index 000000000000..33e3b76c4fa9 --- /dev/null +++ b/drivers/moto_swap/zram-5.10/zcomp.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2014 Sergey Senozhatsky. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zcomp.h" + +static const char * const backends[] = { + "lzo", + "lzo-rle", +#if IS_ENABLED(CONFIG_CRYPTO_LZ4) + "lz4", +#endif +#if IS_ENABLED(CONFIG_CRYPTO_LZ4HC) + "lz4hc", +#endif +#if IS_ENABLED(CONFIG_CRYPTO_842) + "842", +#endif +#if IS_ENABLED(CONFIG_CRYPTO_ZSTD) + "zstd", +#endif +}; + +static void zcomp_strm_free(struct zcomp_strm *zstrm) +{ + if (!IS_ERR_OR_NULL(zstrm->tfm)) + crypto_free_comp(zstrm->tfm); + free_pages((unsigned long)zstrm->buffer, 1); + zstrm->tfm = NULL; + zstrm->buffer = NULL; +} + +/* + * Initialize zcomp_strm structure with ->tfm initialized by backend, and + * ->buffer. Return a negative value on error. + */ +static int zcomp_strm_init(struct zcomp_strm *zstrm, struct zcomp *comp) +{ + zstrm->tfm = crypto_alloc_comp(comp->name, 0, 0); + /* + * allocate 2 pages. 1 for compressed data, plus 1 extra for the + * case when compressed size is larger than the original one + */ + zstrm->buffer = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); + if (IS_ERR_OR_NULL(zstrm->tfm) || !zstrm->buffer) { + zcomp_strm_free(zstrm); + return -ENOMEM; + } + return 0; +} + +bool zcomp_available_algorithm(const char *comp) +{ + int i; + + i = sysfs_match_string(backends, comp); + if (i >= 0) + return true; + + /* + * Crypto does not ignore a trailing new line symbol, + * so make sure you don't supply a string containing + * one. + * This also means that we permit zcomp initialisation + * with any compressing algorithm known to crypto api. + */ + return crypto_has_comp(comp, 0, 0) == 1; +} + +/* show available compressors */ +ssize_t zcomp_available_show(const char *comp, char *buf) +{ + bool known_algorithm = false; + ssize_t sz = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(backends); i++) { + if (!strcmp(comp, backends[i])) { + known_algorithm = true; + sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, + "[%s] ", backends[i]); + } else { + sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, + "%s ", backends[i]); + } + } + + /* + * Out-of-tree module known to crypto api or a missing + * entry in `backends'. + */ + if (!known_algorithm && crypto_has_comp(comp, 0, 0) == 1) + sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, + "[%s] ", comp); + + sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); + return sz; +} + +struct zcomp_strm *zcomp_stream_get(struct zcomp *comp) +{ + local_lock(&comp->stream->lock); + return this_cpu_ptr(comp->stream); +} + +void zcomp_stream_put(struct zcomp *comp) +{ + local_unlock(&comp->stream->lock); +} + +int zcomp_compress(struct zcomp_strm *zstrm, + const void *src, unsigned int *dst_len) +{ + /* + * Our dst memory (zstrm->buffer) is always `2 * PAGE_SIZE' sized + * because sometimes we can endup having a bigger compressed data + * due to various reasons: for example compression algorithms tend + * to add some padding to the compressed buffer. Speaking of padding, + * comp algorithm `842' pads the compressed length to multiple of 8 + * and returns -ENOSP when the dst memory is not big enough, which + * is not something that ZRAM wants to see. We can handle the + * `compressed_size > PAGE_SIZE' case easily in ZRAM, but when we + * receive -ERRNO from the compressing backend we can't help it + * anymore. To make `842' happy we need to tell the exact size of + * the dst buffer, zram_drv will take care of the fact that + * compressed buffer is too big. + */ + *dst_len = PAGE_SIZE * 2; + + return crypto_comp_compress(zstrm->tfm, + src, PAGE_SIZE, + zstrm->buffer, dst_len); +} + +int zcomp_decompress(struct zcomp_strm *zstrm, + const void *src, unsigned int src_len, void *dst) +{ + unsigned int dst_len = PAGE_SIZE; + + return crypto_comp_decompress(zstrm->tfm, + src, src_len, + dst, &dst_len); +} + +int zcomp_cpu_up_prepare(unsigned int cpu, struct hlist_node *node) +{ + struct zcomp *comp = hlist_entry(node, struct zcomp, node); + struct zcomp_strm *zstrm; + int ret; + + zstrm = per_cpu_ptr(comp->stream, cpu); + local_lock_init(&zstrm->lock); + + ret = zcomp_strm_init(zstrm, comp); + if (ret) + pr_err("Can't allocate a compression stream\n"); + return ret; +} + +int zcomp_cpu_dead(unsigned int cpu, struct hlist_node *node) +{ + struct zcomp *comp = hlist_entry(node, struct zcomp, node); + struct zcomp_strm *zstrm; + + zstrm = per_cpu_ptr(comp->stream, cpu); + zcomp_strm_free(zstrm); + return 0; +} + +static int zcomp_init(struct zcomp *comp) +{ + int ret; + + comp->stream = alloc_percpu(struct zcomp_strm); + if (!comp->stream) + return -ENOMEM; + + ret = cpuhp_state_add_instance(CPUHP_ZCOMP_PREPARE, &comp->node); + if (ret < 0) + goto cleanup; + return 0; + +cleanup: + free_percpu(comp->stream); + return ret; +} + +void zcomp_destroy(struct zcomp *comp) +{ + cpuhp_state_remove_instance(CPUHP_ZCOMP_PREPARE, &comp->node); + free_percpu(comp->stream); + kfree(comp); +} + +/* + * search available compressors for requested algorithm. + * allocate new zcomp and initialize it. return compressing + * backend pointer or ERR_PTR if things went bad. ERR_PTR(-EINVAL) + * if requested algorithm is not supported, ERR_PTR(-ENOMEM) in + * case of allocation error, or any other error potentially + * returned by zcomp_init(). + */ +struct zcomp *zcomp_create(const char *compress) +{ + struct zcomp *comp; + int error; + + if (!zcomp_available_algorithm(compress)) + return ERR_PTR(-EINVAL); + + comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL); + if (!comp) + return ERR_PTR(-ENOMEM); + + comp->name = compress; + error = zcomp_init(comp); + if (error) { + kfree(comp); + return ERR_PTR(error); + } + return comp; +} diff --git a/drivers/moto_swap/zram-5.10/zcomp.h b/drivers/moto_swap/zram-5.10/zcomp.h new file mode 100644 index 000000000000..40f6420f4b2e --- /dev/null +++ b/drivers/moto_swap/zram-5.10/zcomp.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2014 Sergey Senozhatsky. + */ + +#ifndef _ZCOMP_H_ +#define _ZCOMP_H_ +#include + +struct zcomp_strm { + /* The members ->buffer and ->tfm are protected by ->lock. */ + local_lock_t lock; + /* compression/decompression buffer */ + void *buffer; + struct crypto_comp *tfm; +}; + +/* dynamic per-device compression frontend */ +struct zcomp { + struct zcomp_strm __percpu *stream; + const char *name; + struct hlist_node node; +}; + +int zcomp_cpu_up_prepare(unsigned int cpu, struct hlist_node *node); +int zcomp_cpu_dead(unsigned int cpu, struct hlist_node *node); +ssize_t zcomp_available_show(const char *comp, char *buf); +bool zcomp_available_algorithm(const char *comp); + +struct zcomp *zcomp_create(const char *comp); +void zcomp_destroy(struct zcomp *comp); + +struct zcomp_strm *zcomp_stream_get(struct zcomp *comp); +void zcomp_stream_put(struct zcomp *comp); + +int zcomp_compress(struct zcomp_strm *zstrm, + const void *src, unsigned int *dst_len); + +int zcomp_decompress(struct zcomp_strm *zstrm, + const void *src, unsigned int src_len, void *dst); + +bool zcomp_set_max_streams(struct zcomp *comp, int num_strm); +#endif /* _ZCOMP_H_ */ diff --git a/drivers/moto_swap/zram-5.10/zram_drv.c b/drivers/moto_swap/zram-5.10/zram_drv.c new file mode 100644 index 000000000000..e25cb6134120 --- /dev/null +++ b/drivers/moto_swap/zram-5.10/zram_drv.c @@ -0,0 +1,2359 @@ +/* + * Compressed RAM block device + * + * Copyright (C) 2008, 2009, 2010 Nitin Gupta + * 2012, 2013 Minchan Kim + * + * This code is released using a dual license strategy: BSD/GPL + * You can choose the licence that better fits your requirements. + * + * Released under the terms of 3-clause BSD License + * Released under the terms of GNU General Public License Version 2.0 + * + */ + +#define KMSG_COMPONENT "zram" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zram_drv.h" +#include "zram_drv_internal.h" +#ifdef CONFIG_HYBRIDSWAP +#include "../hybridswap/hybridswap.h" +#endif + +static DEFINE_IDR(zram_index_idr); +/* idr index must be protected */ +static DEFINE_MUTEX(zram_index_mutex); + +static int zram_major; +static const char *default_compressor = "lzo-rle"; + +/* Module params (documentation at end) */ +static unsigned int num_devices = 1; +/* + * Pages that compress to sizes equals or greater than this are stored + * uncompressed in memory. + */ +static size_t huge_class_size; + +static const struct block_device_operations zram_devops; +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +static const struct block_device_operations zram_wb_devops; +#endif + +static void zram_free_page(struct zram *zram, size_t index); +static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, + u32 index, int offset, struct bio *bio); + + +static int zram_slot_trylock(struct zram *zram, u32 index) +{ + return bit_spin_trylock(ZRAM_LOCK, &zram->table[index].flags); +} + +static unsigned long zram_get_element(struct zram *zram, u32 index) +{ + return zram->table[index].element; +} + +static inline bool zram_allocated(struct zram *zram, u32 index) +{ + return zram_get_obj_size(zram, index) || + zram_test_flag(zram, index, ZRAM_SAME) || + zram_test_flag(zram, index, ZRAM_WB); +} + +#if PAGE_SIZE != 4096 +static inline bool is_partial_io(struct bio_vec *bvec) +{ + return bvec->bv_len != PAGE_SIZE; +} +#else +static inline bool is_partial_io(struct bio_vec *bvec) +{ + return false; +} +#endif + +/* + * Check if request is within bounds and aligned on zram logical blocks. + */ +static inline bool valid_io_request(struct zram *zram, + sector_t start, unsigned int size) +{ + u64 end, bound; + + /* unaligned request */ + if (unlikely(start & (ZRAM_SECTOR_PER_LOGICAL_BLOCK - 1))) + return false; + if (unlikely(size & (ZRAM_LOGICAL_BLOCK_SIZE - 1))) + return false; + + end = start + (size >> SECTOR_SHIFT); + bound = zram->disksize >> SECTOR_SHIFT; + /* out of range range */ + if (unlikely(start >= bound || end > bound || start > end)) + return false; + + /* I/O request is valid */ + return true; +} + +static void update_position(u32 *index, int *offset, struct bio_vec *bvec) +{ + *index += (*offset + bvec->bv_len) / PAGE_SIZE; + *offset = (*offset + bvec->bv_len) % PAGE_SIZE; +} + +static inline void update_used_max(struct zram *zram, + const unsigned long pages) +{ + unsigned long old_max, cur_max; + + old_max = atomic_long_read(&zram->stats.max_used_pages); + + do { + cur_max = old_max; + if (pages > cur_max) + old_max = atomic_long_cmpxchg( + &zram->stats.max_used_pages, cur_max, pages); + } while (old_max != cur_max); +} + +static inline void zram_fill_page(void *ptr, unsigned long len, + unsigned long value) +{ + WARN_ON_ONCE(!IS_ALIGNED(len, sizeof(unsigned long))); + memset_l(ptr, value, len / sizeof(unsigned long)); +} + +static bool page_same_filled(void *ptr, unsigned long *element) +{ + unsigned long *page; + unsigned long val; + unsigned int pos, last_pos = PAGE_SIZE / sizeof(*page) - 1; + + page = (unsigned long *)ptr; + val = page[0]; + + if (val != page[last_pos]) + return false; + + for (pos = 1; pos < last_pos; pos++) { + if (val != page[pos]) + return false; + } + + *element = val; + + return true; +} + +static ssize_t initstate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 val; + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + val = init_done(zram); + up_read(&zram->init_lock); + + return scnprintf(buf, PAGE_SIZE, "%u\n", val); +} + +static ssize_t disksize_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return scnprintf(buf, PAGE_SIZE, "%llu\n", zram->disksize); +} + +static ssize_t mem_limit_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + u64 limit; + char *tmp; + struct zram *zram = dev_to_zram(dev); + + limit = memparse(buf, &tmp); + if (buf == tmp) /* no chars parsed, invalid input */ + return -EINVAL; + + down_write(&zram->init_lock); + zram->limit_pages = PAGE_ALIGN(limit) >> PAGE_SHIFT; + up_write(&zram->init_lock); + + return len; +} + +static ssize_t mem_used_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int err; + unsigned long val; + struct zram *zram = dev_to_zram(dev); + + err = kstrtoul(buf, 10, &val); + if (err || val != 0) + return -EINVAL; + + down_read(&zram->init_lock); + if (init_done(zram)) { + atomic_long_set(&zram->stats.max_used_pages, + zs_get_total_pages(zram->mem_pool)); + } + up_read(&zram->init_lock); + + return len; +} + +static ssize_t idle_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + unsigned long nr_pages = zram->disksize >> PAGE_SHIFT; + int index; + + if (!sysfs_streq(buf, "all")) + return -EINVAL; + + down_read(&zram->init_lock); + if (!init_done(zram)) { + up_read(&zram->init_lock); + return -EINVAL; + } + + for (index = 0; index < nr_pages; index++) { + /* + * Do not mark ZRAM_UNDER_WB slot as ZRAM_IDLE to close race. + * See the comment in writeback_store. + */ + zram_slot_lock(zram, index); + if (zram_allocated(zram, index) && + !zram_test_flag(zram, index, ZRAM_UNDER_WB)) + zram_set_flag(zram, index, ZRAM_IDLE); + zram_slot_unlock(zram, index); + } + + up_read(&zram->init_lock); + + return len; +} + +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +static ssize_t writeback_limit_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + u64 val; + ssize_t ret = -EINVAL; + + if (kstrtoull(buf, 10, &val)) + return ret; + + down_read(&zram->init_lock); + spin_lock(&zram->wb_limit_lock); + zram->wb_limit_enable = val; + spin_unlock(&zram->wb_limit_lock); + up_read(&zram->init_lock); + ret = len; + + return ret; +} + +static ssize_t writeback_limit_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + bool val; + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + spin_lock(&zram->wb_limit_lock); + val = zram->wb_limit_enable; + spin_unlock(&zram->wb_limit_lock); + up_read(&zram->init_lock); + + return scnprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t writeback_limit_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + u64 val; + ssize_t ret = -EINVAL; + + if (kstrtoull(buf, 10, &val)) + return ret; + + down_read(&zram->init_lock); + spin_lock(&zram->wb_limit_lock); + zram->bd_wb_limit = val; + spin_unlock(&zram->wb_limit_lock); + up_read(&zram->init_lock); + ret = len; + + return ret; +} + +static ssize_t writeback_limit_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u64 val; + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + spin_lock(&zram->wb_limit_lock); + val = zram->bd_wb_limit; + spin_unlock(&zram->wb_limit_lock); + up_read(&zram->init_lock); + + return scnprintf(buf, PAGE_SIZE, "%llu\n", val); +} + +static void reset_bdev(struct zram *zram) +{ + struct block_device *bdev; + + if (!zram->backing_dev) + return; + + bdev = zram->bdev; + if (zram->old_block_size) + set_blocksize(bdev, zram->old_block_size); + blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL); + /* hope filp_close flush all of IO */ + filp_close(zram->backing_dev, NULL); + zram->backing_dev = NULL; + zram->old_block_size = 0; + zram->bdev = NULL; + zram->disk->fops = &zram_devops; + kvfree(zram->bitmap); + zram->bitmap = NULL; +} + +static ssize_t backing_dev_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct file *file; + struct zram *zram = dev_to_zram(dev); + char *p; + ssize_t ret; + + down_read(&zram->init_lock); + file = zram->backing_dev; + if (!file) { + memcpy(buf, "none\n", 5); + up_read(&zram->init_lock); + return 5; + } + + p = file_path(file, buf, PAGE_SIZE - 1); + if (IS_ERR(p)) { + ret = PTR_ERR(p); + goto out; + } + + ret = strlen(p); + memmove(buf, p, ret); + buf[ret++] = '\n'; +out: + up_read(&zram->init_lock); + return ret; +} + +static ssize_t backing_dev_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + char *file_name; + size_t sz; + struct file *backing_dev = NULL; + struct inode *inode; + struct address_space *mapping; + unsigned int bitmap_sz, old_block_size = 0; + unsigned long nr_pages, *bitmap = NULL; + struct block_device *bdev = NULL; + int err; + struct zram *zram = dev_to_zram(dev); + + file_name = kmalloc(PATH_MAX, GFP_KERNEL); + if (!file_name) + return -ENOMEM; + + down_write(&zram->init_lock); + if (init_done(zram)) { + pr_info("Can't setup backing device for initialized device\n"); + err = -EBUSY; + goto out; + } + + strlcpy(file_name, buf, PATH_MAX); + /* ignore trailing newline */ + sz = strlen(file_name); + if (sz > 0 && file_name[sz - 1] == '\n') + file_name[sz - 1] = 0x00; + + backing_dev = filp_open_block(file_name, O_RDWR|O_LARGEFILE, 0); + if (IS_ERR(backing_dev)) { + err = PTR_ERR(backing_dev); + backing_dev = NULL; + goto out; + } + + mapping = backing_dev->f_mapping; + inode = mapping->host; + + /* Support only block device in this moment */ + if (!S_ISBLK(inode->i_mode)) { + err = -ENOTBLK; + goto out; + } + + bdev = blkdev_get_by_dev(inode->i_rdev, + FMODE_READ | FMODE_WRITE | FMODE_EXCL, zram); + if (IS_ERR(bdev)) { + err = PTR_ERR(bdev); + bdev = NULL; + goto out; + } + + nr_pages = i_size_read(inode) >> PAGE_SHIFT; + bitmap_sz = BITS_TO_LONGS(nr_pages) * sizeof(long); + bitmap = kvzalloc(bitmap_sz, GFP_KERNEL); + if (!bitmap) { + err = -ENOMEM; + goto out; + } + + old_block_size = block_size(bdev); + err = set_blocksize(bdev, PAGE_SIZE); + if (err) + goto out; + + reset_bdev(zram); + + zram->old_block_size = old_block_size; + zram->bdev = bdev; + zram->backing_dev = backing_dev; + zram->bitmap = bitmap; + zram->nr_pages = nr_pages; + /* + * With writeback feature, zram does asynchronous IO so it's no longer + * synchronous device so let's remove synchronous io flag. Othewise, + * upper layer(e.g., swap) could wait IO completion rather than + * (submit and return), which will cause system sluggish. + * Furthermore, when the IO function returns(e.g., swap_readpage), + * upper layer expects IO was done so it could deallocate the page + * freely but in fact, IO is going on so finally could cause + * use-after-free when the IO is really done. + */ + zram->disk->fops = &zram_wb_devops; + up_write(&zram->init_lock); + + pr_info("setup backing device %s\n", file_name); + kfree(file_name); + + return len; +out: + if (bitmap) + kvfree(bitmap); + + if (bdev) + blkdev_put(bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL); + + if (backing_dev) + filp_close(backing_dev, NULL); + + up_write(&zram->init_lock); + + kfree(file_name); + + return err; +} + +static unsigned long alloc_block_bdev(struct zram *zram) +{ + unsigned long blk_index = 1; +retry: + /* skip 0 bit to confuse zram.handle = 0 */ + blk_index = find_next_zero_bit(zram->bitmap, zram->nr_pages, blk_index); + if (blk_index == zram->nr_pages) + return 0; + + if (test_and_set_bit(blk_index, zram->bitmap)) + goto retry; + + atomic64_inc(&zram->stats.bd_count); + return blk_index; +} + +static void free_block_bdev(struct zram *zram, unsigned long blk_index) +{ + int was_set; + + was_set = test_and_clear_bit(blk_index, zram->bitmap); + WARN_ON_ONCE(!was_set); + atomic64_dec(&zram->stats.bd_count); +} + +static void zram_page_end_io(struct bio *bio) +{ + struct page *page = bio_first_page_all(bio); + + page_endio(page, op_is_write(bio_op(bio)), + blk_status_to_errno(bio->bi_status)); + bio_put(bio); +} + +/* + * Returns 1 if the submission is successful. + */ +static int read_from_bdev_async(struct zram *zram, struct bio_vec *bvec, + unsigned long entry, struct bio *parent) +{ + struct bio *bio; + + bio = bio_alloc(GFP_ATOMIC, 1); + if (!bio) + return -ENOMEM; + + bio->bi_iter.bi_sector = entry * (PAGE_SIZE >> 9); + bio_set_dev(bio, zram->bdev); + if (!bio_add_page(bio, bvec->bv_page, bvec->bv_len, bvec->bv_offset)) { + bio_put(bio); + return -EIO; + } + + if (!parent) { + bio->bi_opf = REQ_OP_READ; + bio->bi_end_io = zram_page_end_io; + } else { + bio->bi_opf = parent->bi_opf; + bio_chain(bio, parent); + } + + submit_bio(bio); + return 1; +} + +#define PAGE_WB_SIG "page_index=" + +#define PAGE_WRITEBACK 0 +#define HUGE_WRITEBACK 1 +#define IDLE_WRITEBACK 2 + + +static ssize_t writeback_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + unsigned long nr_pages = zram->disksize >> PAGE_SHIFT; + unsigned long index = 0; + struct bio bio; + struct bio_vec bio_vec; + struct page *page; + ssize_t ret = len; + int mode, err; + unsigned long blk_index = 0; + + if (sysfs_streq(buf, "idle")) + mode = IDLE_WRITEBACK; + else if (sysfs_streq(buf, "huge")) + mode = HUGE_WRITEBACK; + else { + if (strncmp(buf, PAGE_WB_SIG, sizeof(PAGE_WB_SIG) - 1)) + return -EINVAL; + + if (kstrtol(buf + sizeof(PAGE_WB_SIG) - 1, 10, &index) || + index >= nr_pages) + return -EINVAL; + + nr_pages = 1; + mode = PAGE_WRITEBACK; + } + + down_read(&zram->init_lock); + if (!init_done(zram)) { + ret = -EINVAL; + goto release_init_lock; + } + + if (!zram->backing_dev) { + ret = -ENODEV; + goto release_init_lock; + } + + page = alloc_page(GFP_KERNEL); + if (!page) { + ret = -ENOMEM; + goto release_init_lock; + } + + for (; nr_pages != 0; index++, nr_pages--) { + struct bio_vec bvec; + + bvec.bv_page = page; + bvec.bv_len = PAGE_SIZE; + bvec.bv_offset = 0; + + spin_lock(&zram->wb_limit_lock); + if (zram->wb_limit_enable && !zram->bd_wb_limit) { + spin_unlock(&zram->wb_limit_lock); + ret = -EIO; + break; + } + spin_unlock(&zram->wb_limit_lock); + + if (!blk_index) { + blk_index = alloc_block_bdev(zram); + if (!blk_index) { + ret = -ENOSPC; + break; + } + } + + zram_slot_lock(zram, index); + if (!zram_allocated(zram, index)) + goto next; + + if (zram_test_flag(zram, index, ZRAM_WB) || + zram_test_flag(zram, index, ZRAM_SAME) || + zram_test_flag(zram, index, ZRAM_UNDER_WB)) + goto next; + + if (mode == IDLE_WRITEBACK && + !zram_test_flag(zram, index, ZRAM_IDLE)) + goto next; + if (mode == HUGE_WRITEBACK && + !zram_test_flag(zram, index, ZRAM_HUGE)) + goto next; + /* + * Clearing ZRAM_UNDER_WB is duty of caller. + * IOW, zram_free_page never clear it. + */ + zram_set_flag(zram, index, ZRAM_UNDER_WB); + /* Need for hugepage writeback racing */ + zram_set_flag(zram, index, ZRAM_IDLE); + zram_slot_unlock(zram, index); + if (zram_bvec_read(zram, &bvec, index, 0, NULL)) { + zram_slot_lock(zram, index); + zram_clear_flag(zram, index, ZRAM_UNDER_WB); + zram_clear_flag(zram, index, ZRAM_IDLE); + zram_slot_unlock(zram, index); + continue; + } + + bio_init(&bio, &bio_vec, 1); + bio_set_dev(&bio, zram->bdev); + bio.bi_iter.bi_sector = blk_index * (PAGE_SIZE >> 9); + bio.bi_opf = REQ_OP_WRITE | REQ_SYNC; + + bio_add_page(&bio, bvec.bv_page, bvec.bv_len, + bvec.bv_offset); + /* + * XXX: A single page IO would be inefficient for write + * but it would be not bad as starter. + */ + err = submit_bio_wait(&bio); + if (err) { + zram_slot_lock(zram, index); + zram_clear_flag(zram, index, ZRAM_UNDER_WB); + zram_clear_flag(zram, index, ZRAM_IDLE); + zram_slot_unlock(zram, index); + /* + * Return last IO error unless every IO were + * not suceeded. + */ + ret = err; + continue; + } + + atomic64_inc(&zram->stats.bd_writes); + /* + * We released zram_slot_lock so need to check if the slot was + * changed. If there is freeing for the slot, we can catch it + * easily by zram_allocated. + * A subtle case is the slot is freed/reallocated/marked as + * ZRAM_IDLE again. To close the race, idle_store doesn't + * mark ZRAM_IDLE once it found the slot was ZRAM_UNDER_WB. + * Thus, we could close the race by checking ZRAM_IDLE bit. + */ + zram_slot_lock(zram, index); + if (!zram_allocated(zram, index) || + !zram_test_flag(zram, index, ZRAM_IDLE)) { + zram_clear_flag(zram, index, ZRAM_UNDER_WB); + zram_clear_flag(zram, index, ZRAM_IDLE); + goto next; + } + + zram_free_page(zram, index); + zram_clear_flag(zram, index, ZRAM_UNDER_WB); + zram_set_flag(zram, index, ZRAM_WB); + zram_set_element(zram, index, blk_index); + blk_index = 0; + atomic64_inc(&zram->stats.pages_stored); + spin_lock(&zram->wb_limit_lock); + if (zram->wb_limit_enable && zram->bd_wb_limit > 0) + zram->bd_wb_limit -= 1UL << (PAGE_SHIFT - 12); + spin_unlock(&zram->wb_limit_lock); +next: + zram_slot_unlock(zram, index); + } + + if (blk_index) + free_block_bdev(zram, blk_index); + __free_page(page); +release_init_lock: + up_read(&zram->init_lock); + + return ret; +} + +struct zram_work { + struct work_struct work; + struct zram *zram; + unsigned long entry; + struct bio *bio; + struct bio_vec bvec; +}; + +#if PAGE_SIZE != 4096 +static void zram_sync_read(struct work_struct *work) +{ + struct zram_work *zw = container_of(work, struct zram_work, work); + struct zram *zram = zw->zram; + unsigned long entry = zw->entry; + struct bio *bio = zw->bio; + + read_from_bdev_async(zram, &zw->bvec, entry, bio); +} + +/* + * Block layer want one ->submit_bio to be active at a time, so if we use + * chained IO with parent IO in same context, it's a deadlock. To avoid that, + * use a worker thread context. + */ +static int read_from_bdev_sync(struct zram *zram, struct bio_vec *bvec, + unsigned long entry, struct bio *bio) +{ + struct zram_work work; + + work.bvec = *bvec; + work.zram = zram; + work.entry = entry; + work.bio = bio; + + INIT_WORK_ONSTACK(&work.work, zram_sync_read); + queue_work(system_unbound_wq, &work.work); + flush_work(&work.work); + destroy_work_on_stack(&work.work); + + return 1; +} +#else +static int read_from_bdev_sync(struct zram *zram, struct bio_vec *bvec, + unsigned long entry, struct bio *bio) +{ + WARN_ON(1); + return -EIO; +} +#endif + +static int read_from_bdev(struct zram *zram, struct bio_vec *bvec, + unsigned long entry, struct bio *parent, bool sync) +{ + atomic64_inc(&zram->stats.bd_reads); + if (sync) + return read_from_bdev_sync(zram, bvec, entry, parent); + else + return read_from_bdev_async(zram, bvec, entry, parent); +} +#else +#ifdef CONFIG_HYBRIDSWAP_CORE +static void reset_bdev(struct zram *zram) +{ + struct block_device *bdev; + + if (!zram->backing_dev) + return; + + bdev = zram->bdev; + if (zram->old_block_size) + set_blocksize(bdev, zram->old_block_size); + blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL); + /* hope filp_close flush all of IO */ + filp_close(zram->backing_dev, NULL); + zram->backing_dev = NULL; + zram->old_block_size = 0; + zram->bdev = NULL; +} +#else +static inline void reset_bdev(struct zram *zram) {}; +#endif +static int read_from_bdev(struct zram *zram, struct bio_vec *bvec, + unsigned long entry, struct bio *parent, bool sync) +{ + return -EIO; +} + +static void free_block_bdev(struct zram *zram, unsigned long blk_index) {}; +#endif + +#ifdef CONFIG_HYBRIDSWAP_ZRAM_MEMORY_TRACKING + +static struct dentry *zram_debugfs_root; + +static void zram_debugfs_create(void) +{ + zram_debugfs_root = debugfs_create_dir("zram", NULL); +} + +static void zram_debugfs_destroy(void) +{ + debugfs_remove_recursive(zram_debugfs_root); +} + +static void zram_accessed(struct zram *zram, u32 index) +{ + zram_clear_flag(zram, index, ZRAM_IDLE); + zram->table[index].ac_time = ktime_get_boottime(); +} + +static ssize_t read_block_state(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + char *kbuf; + ssize_t index, written = 0; + struct zram *zram = file->private_data; + unsigned long nr_pages = zram->disksize >> PAGE_SHIFT; + struct timespec64 ts; + + kbuf = kvmalloc(count, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + down_read(&zram->init_lock); + if (!init_done(zram)) { + up_read(&zram->init_lock); + kvfree(kbuf); + return -EINVAL; + } + + for (index = *ppos; index < nr_pages; index++) { + int copied; + + zram_slot_lock(zram, index); + if (!zram_allocated(zram, index)) + goto next; + + ts = ktime_to_timespec64(zram->table[index].ac_time); + copied = snprintf(kbuf + written, count, + "%12zd %12lld.%06lu %c%c%c%c\n", + index, (s64)ts.tv_sec, + ts.tv_nsec / NSEC_PER_USEC, + zram_test_flag(zram, index, ZRAM_SAME) ? 's' : '.', + zram_test_flag(zram, index, ZRAM_WB) ? 'w' : '.', + zram_test_flag(zram, index, ZRAM_HUGE) ? 'h' : '.', + zram_test_flag(zram, index, ZRAM_IDLE) ? 'i' : '.'); + + if (count < copied) { + zram_slot_unlock(zram, index); + break; + } + written += copied; + count -= copied; +next: + zram_slot_unlock(zram, index); + *ppos += 1; + } + + up_read(&zram->init_lock); + if (copy_to_user(buf, kbuf, written)) + written = -EFAULT; + kvfree(kbuf); + + return written; +} + +static const struct file_operations proc_zram_block_state_op = { + .open = simple_open, + .read = read_block_state, + .llseek = default_llseek, +}; + +static void zram_debugfs_register(struct zram *zram) +{ + if (!zram_debugfs_root) + return; + + zram->debugfs_dir = debugfs_create_dir(zram->disk->disk_name, + zram_debugfs_root); + debugfs_create_file("block_state", 0400, zram->debugfs_dir, + zram, &proc_zram_block_state_op); +} + +static void zram_debugfs_unregister(struct zram *zram) +{ + debugfs_remove_recursive(zram->debugfs_dir); +} +#else +static void zram_debugfs_create(void) {}; +static void zram_debugfs_destroy(void) {}; +static void zram_accessed(struct zram *zram, u32 index) +{ + zram_clear_flag(zram, index, ZRAM_IDLE); +}; +static void zram_debugfs_register(struct zram *zram) {}; +static void zram_debugfs_unregister(struct zram *zram) {}; +#endif + +/* + * We switched to per-cpu streams and this attr is not needed anymore. + * However, we will keep it around for some time, because: + * a) we may revert per-cpu streams in the future + * b) it's visible to user space and we need to follow our 2 years + * retirement rule; but we already have a number of 'soon to be + * altered' attrs, so max_comp_streams need to wait for the next + * layoff cycle. + */ +static ssize_t max_comp_streams_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", num_online_cpus()); +} + +static ssize_t max_comp_streams_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + return len; +} + +static ssize_t comp_algorithm_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t sz; + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + sz = zcomp_available_show(zram->compressor, buf); + up_read(&zram->init_lock); + + return sz; +} + +static ssize_t comp_algorithm_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + char compressor[ARRAY_SIZE(zram->compressor)]; + size_t sz; + + strlcpy(compressor, buf, sizeof(compressor)); + /* ignore trailing newline */ + sz = strlen(compressor); + if (sz > 0 && compressor[sz - 1] == '\n') + compressor[sz - 1] = 0x00; + + if (!zcomp_available_algorithm(compressor)) + return -EINVAL; + + down_write(&zram->init_lock); + if (init_done(zram)) { + up_write(&zram->init_lock); + pr_info("Can't change algorithm for initialized device\n"); + return -EBUSY; + } + + strcpy(zram->compressor, compressor); + up_write(&zram->init_lock); + return len; +} + +static ssize_t compact_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + if (!init_done(zram)) { + up_read(&zram->init_lock); + return -EINVAL; + } + + zs_compact(zram->mem_pool); + up_read(&zram->init_lock); + + return len; +} + +static ssize_t io_stat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + ssize_t ret; + + down_read(&zram->init_lock); + ret = scnprintf(buf, PAGE_SIZE, + "%8llu %8llu %8llu %8llu\n", + (u64)atomic64_read(&zram->stats.failed_reads), + (u64)atomic64_read(&zram->stats.failed_writes), + (u64)atomic64_read(&zram->stats.invalid_io), + (u64)atomic64_read(&zram->stats.notify_free)); + up_read(&zram->init_lock); + + return ret; +} + +static ssize_t mm_stat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + struct zs_pool_stats pool_stats; + u64 orig_size, mem_used = 0; + long max_used; + ssize_t ret; + + memset(&pool_stats, 0x00, sizeof(struct zs_pool_stats)); + + down_read(&zram->init_lock); + if (init_done(zram)) { + mem_used = zs_get_total_pages(zram->mem_pool); + zs_pool_stats(zram->mem_pool, &pool_stats); + } + + orig_size = atomic64_read(&zram->stats.pages_stored); + max_used = atomic_long_read(&zram->stats.max_used_pages); + + ret = scnprintf(buf, PAGE_SIZE, + "%8llu %8llu %8llu %8lu %8ld %8llu %8lu %8llu\n", + orig_size << PAGE_SHIFT, + (u64)atomic64_read(&zram->stats.compr_data_size), + mem_used << PAGE_SHIFT, + zram->limit_pages << PAGE_SHIFT, + max_used << PAGE_SHIFT, + (u64)atomic64_read(&zram->stats.same_pages), + atomic_long_read(&pool_stats.pages_compacted), + (u64)atomic64_read(&zram->stats.huge_pages)); + up_read(&zram->init_lock); + + return ret; +} + +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +#define FOUR_K(x) ((x) * (1 << (PAGE_SHIFT - 12))) +static ssize_t bd_stat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + ssize_t ret; + + down_read(&zram->init_lock); + ret = scnprintf(buf, PAGE_SIZE, + "%8llu %8llu %8llu\n", + FOUR_K((u64)atomic64_read(&zram->stats.bd_count)), + FOUR_K((u64)atomic64_read(&zram->stats.bd_reads)), + FOUR_K((u64)atomic64_read(&zram->stats.bd_writes))); + up_read(&zram->init_lock); + + return ret; +} +#endif + +static ssize_t debug_stat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int version = 1; + struct zram *zram = dev_to_zram(dev); + ssize_t ret; + + down_read(&zram->init_lock); + ret = scnprintf(buf, PAGE_SIZE, + "version: %d\n%8llu %8llu\n", + version, + (u64)atomic64_read(&zram->stats.writestall), + (u64)atomic64_read(&zram->stats.miss_free)); + up_read(&zram->init_lock); + + return ret; +} + +static DEVICE_ATTR_RO(io_stat); +static DEVICE_ATTR_RO(mm_stat); +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +static DEVICE_ATTR_RO(bd_stat); +#endif +static DEVICE_ATTR_RO(debug_stat); + +static void zram_meta_free(struct zram *zram, u64 disksize) +{ + size_t num_pages = disksize >> PAGE_SHIFT; + size_t index; + + /* Free all pages that are still in this zram device */ + for (index = 0; index < num_pages; index++) + zram_free_page(zram, index); + + zs_destroy_pool(zram->mem_pool); + vfree(zram->table); +} + +static bool zram_meta_alloc(struct zram *zram, u64 disksize) +{ + size_t num_pages; + + num_pages = disksize >> PAGE_SHIFT; + zram->table = vzalloc(array_size(num_pages, sizeof(*zram->table))); + if (!zram->table) + return false; + + zram->mem_pool = zs_create_pool(zram->disk->disk_name); + if (!zram->mem_pool) { + vfree(zram->table); + return false; + } + + if (!huge_class_size) + huge_class_size = zs_huge_class_size(zram->mem_pool); + return true; +} + +/* + * To protect concurrent access to the same index entry, + * caller should hold this table index entry's bit_spinlock to + * indicate this index entry is accessing. + */ +static void zram_free_page(struct zram *zram, size_t index) +{ + unsigned long handle; + +#ifdef CONFIG_HYBRIDSWAP_ZRAM_MEMORY_TRACKING + zram->table[index].ac_time = 0; +#endif + if (zram_test_flag(zram, index, ZRAM_IDLE)) + zram_clear_flag(zram, index, ZRAM_IDLE); + + if (zram_test_flag(zram, index, ZRAM_HUGE)) { + zram_clear_flag(zram, index, ZRAM_HUGE); + atomic64_dec(&zram->stats.huge_pages); + } + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + if (zram_test_flag(zram, index, ZRAM_CACHED)) { + struct page *page = (struct page *)zram_get_page(zram, index); + + del_page_from_cache(page); + page->mem_cgroup = NULL; + put_free_page(page); + zram_clear_flag(zram, index, ZRAM_CACHED); + goto out; + } + + if (zram_test_flag(zram, index, ZRAM_CACHED_COMPRESS)) { + zram_clear_flag(zram, index, ZRAM_CACHED_COMPRESS); + goto out; + } +#endif + +#ifdef CONFIG_HYBRIDSWAP_CORE + hybridswap_untrack(zram, index); +#endif + + if (zram_test_flag(zram, index, ZRAM_WB)) { + zram_clear_flag(zram, index, ZRAM_WB); + free_block_bdev(zram, zram_get_element(zram, index)); + atomic64_dec(&zram->stats.pages_stored); + goto out; + } + + /* + * No memory is allocated for same element filled pages. + * Simply clear same page flag. + */ + if (zram_test_flag(zram, index, ZRAM_SAME)) { + zram_clear_flag(zram, index, ZRAM_SAME); + atomic64_dec(&zram->stats.same_pages); + atomic64_dec(&zram->stats.pages_stored); + goto out; + } + + handle = zram_get_handle(zram, index); + if (!handle) + return; + + zs_free(zram->mem_pool, handle); + + atomic64_sub(zram_get_obj_size(zram, index), + &zram->stats.compr_data_size); + atomic64_dec(&zram->stats.pages_stored); + +out: + zram_set_handle(zram, index, 0); + zram_set_obj_size(zram, index, 0); + WARN_ON_ONCE(zram->table[index].flags & + ~(1UL << ZRAM_LOCK | 1UL << ZRAM_UNDER_WB)); +} + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS +void update_zram_index(struct zram *zram, u32 index, unsigned long page) +{ + zram_slot_lock(zram, index); + put_anon_pages((struct page*)page); + + zram_free_page(zram, index); + zram_set_flag(zram, index, ZRAM_CACHED); + zram_set_page(zram, index, page); + zram_set_obj_size(zram, index, PAGE_SIZE); + zram_slot_unlock(zram, index); +} + +int async_compress_page(struct zram *zram, struct page* page) +{ + int ret = 0; + unsigned long alloced_pages; + unsigned long handle = 0; + unsigned int comp_len = 0; + void *src, *dst; + struct zcomp_strm *zstrm; + int index = get_zram_index(page); + +compress_again: + zram_slot_lock(zram, index); + if (!zram_test_flag(zram, index, ZRAM_CACHED_COMPRESS)) { + zram_slot_unlock(zram, index); + return 0; + } + zram_slot_unlock(zram, index); + + zstrm = zcomp_stream_get(zram->comp); + src = kmap_atomic(page); + ret = zcomp_compress(zstrm, src, &comp_len); + kunmap_atomic(src); + + if (unlikely(ret)) { + zcomp_stream_put(zram->comp); + pr_err("Compression failed! err=%d\n", ret); + zs_free(zram->mem_pool, handle); + return ret; + } + + if (comp_len >= huge_class_size) + comp_len = PAGE_SIZE; + + if (!handle) + handle = zs_malloc(zram->mem_pool, comp_len, + __GFP_KSWAPD_RECLAIM | + __GFP_NOWARN | + __GFP_HIGHMEM | + __GFP_MOVABLE | + __GFP_CMA); + if (!handle) { + zcomp_stream_put(zram->comp); + atomic64_inc(&zram->stats.writestall); + handle = zs_malloc(zram->mem_pool, comp_len, + GFP_NOIO | __GFP_HIGHMEM | + __GFP_MOVABLE | __GFP_CMA); + if (handle) + goto compress_again; + return -ENOMEM; + } + + alloced_pages = zs_get_total_pages(zram->mem_pool); + update_used_max(zram, alloced_pages); + + if (zram->limit_pages && alloced_pages > zram->limit_pages) { + zcomp_stream_put(zram->comp); + zs_free(zram->mem_pool, handle); + return -ENOMEM; + } + + dst = zs_map_object(zram->mem_pool, handle, ZS_MM_WO); + + src = zstrm->buffer; + if (comp_len == PAGE_SIZE) + src = kmap_atomic(page); + memcpy(dst, src, comp_len); + if (comp_len == PAGE_SIZE) + kunmap_atomic(src); + + zcomp_stream_put(zram->comp); + zs_unmap_object(zram->mem_pool, handle); + atomic64_add(comp_len, &zram->stats.compr_data_size); + + /* + * Free memory associated with this sector + * before overwriting unused sectors. + */ + zram_slot_lock(zram, index); + if (!zram_test_flag(zram, index, ZRAM_CACHED_COMPRESS)) { + atomic64_sub(comp_len, &zram->stats.compr_data_size); + zs_free(zram->mem_pool, handle); + zram_slot_unlock(zram, index); + return 0; + } + zram_free_page(zram, index); + + if (comp_len == PAGE_SIZE) { + zram_set_flag(zram, index, ZRAM_HUGE); + atomic64_inc(&zram->stats.huge_pages); + } + + zram_set_handle(zram, index, handle); + zram_set_obj_size(zram, index, comp_len); +#ifdef CONFIG_HYBRIDSWAP_CORE + hybridswap_record(zram, index, page->mem_cgroup); +#endif + zram_slot_unlock(zram, index); + + /* Update stats */ + atomic64_inc(&zram->stats.pages_stored); + + return ret; +} +#endif + +static int __zram_bvec_read(struct zram *zram, struct page *page, u32 index, + struct bio *bio, bool partial_io) +{ + struct zcomp_strm *zstrm; + unsigned long handle; + unsigned int size; + void *src, *dst; + int ret; + + zram_slot_lock(zram, index); + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + if (akcompress_cache_page_fault(zram, page, index)) + return 0; +#endif + +#ifdef CONFIG_HYBRIDSWAP_CORE + if (likely(!bio)) { + ret = hybridswap_page_fault(zram, index); + if (unlikely(ret)) { + pr_err("search in hybridswap failed! err=%d, page=%u\n", + ret, index); + zram_slot_unlock(zram, index); + return ret; + } + } +#endif + + if (zram_test_flag(zram, index, ZRAM_WB)) { + struct bio_vec bvec; + + zram_slot_unlock(zram, index); + + bvec.bv_page = page; + bvec.bv_len = PAGE_SIZE; + bvec.bv_offset = 0; + return read_from_bdev(zram, &bvec, + zram_get_element(zram, index), + bio, partial_io); + } + + handle = zram_get_handle(zram, index); + if (!handle || zram_test_flag(zram, index, ZRAM_SAME)) { + unsigned long value; + void *mem; + + value = handle ? zram_get_element(zram, index) : 0; + mem = kmap_atomic(page); + zram_fill_page(mem, PAGE_SIZE, value); + kunmap_atomic(mem); + zram_slot_unlock(zram, index); + return 0; + } + + size = zram_get_obj_size(zram, index); + + if (size != PAGE_SIZE) + zstrm = zcomp_stream_get(zram->comp); + + src = zs_map_object(zram->mem_pool, handle, ZS_MM_RO); + if (size == PAGE_SIZE) { + dst = kmap_atomic(page); + memcpy(dst, src, PAGE_SIZE); + kunmap_atomic(dst); + ret = 0; + } else { + dst = kmap_atomic(page); + ret = zcomp_decompress(zstrm, src, size, dst); + kunmap_atomic(dst); + zcomp_stream_put(zram->comp); + } + zs_unmap_object(zram->mem_pool, handle); + zram_slot_unlock(zram, index); + + /* Should NEVER happen. Return bio error if it does. */ + if (WARN_ON(ret)) + pr_err("Decompression failed! err=%d, page=%u\n", ret, index); + + return ret; +} + +static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, + u32 index, int offset, struct bio *bio) +{ + int ret; + struct page *page; + + page = bvec->bv_page; + if (is_partial_io(bvec)) { + /* Use a temporary buffer to decompress the page */ + page = alloc_page(GFP_NOIO|__GFP_HIGHMEM); + if (!page) + return -ENOMEM; + } + + ret = __zram_bvec_read(zram, page, index, bio, is_partial_io(bvec)); + if (unlikely(ret)) + goto out; + + if (is_partial_io(bvec)) { + void *dst = kmap_atomic(bvec->bv_page); + void *src = kmap_atomic(page); + + memcpy(dst + bvec->bv_offset, src + offset, bvec->bv_len); + kunmap_atomic(src); + kunmap_atomic(dst); + } +out: + if (is_partial_io(bvec)) + __free_page(page); + + return ret; +} + +static int __zram_bvec_write(struct zram *zram, struct bio_vec *bvec, + u32 index, struct bio *bio) +{ + int ret = 0; + unsigned long alloced_pages; + unsigned long handle = 0; + unsigned int comp_len = 0; + void *src, *dst, *mem; + struct zcomp_strm *zstrm; + struct page *page = bvec->bv_page; + unsigned long element = 0; + enum zram_pageflags flags = 0; + + mem = kmap_atomic(page); + if (page_same_filled(mem, &element)) { + kunmap_atomic(mem); + /* Free memory associated with this sector now. */ + flags = ZRAM_SAME; + atomic64_inc(&zram->stats.same_pages); + goto out; + } + kunmap_atomic(mem); + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + if ((current_is_kswapd() || current_is_mswapd(current)) && + add_anon_page2cache(zram, index, page)) { + return 0; + } +#endif + +compress_again: + zstrm = zcomp_stream_get(zram->comp); + src = kmap_atomic(page); + ret = zcomp_compress(zstrm, src, &comp_len); + kunmap_atomic(src); + + if (unlikely(ret)) { + zcomp_stream_put(zram->comp); + pr_err("Compression failed! err=%d\n", ret); + zs_free(zram->mem_pool, handle); + return ret; + } + + if (comp_len >= huge_class_size) + comp_len = PAGE_SIZE; + /* + * handle allocation has 2 paths: + * a) fast path is executed with preemption disabled (for + * per-cpu streams) and has __GFP_DIRECT_RECLAIM bit clear, + * since we can't sleep; + * b) slow path enables preemption and attempts to allocate + * the page with __GFP_DIRECT_RECLAIM bit set. we have to + * put per-cpu compression stream and, thus, to re-do + * the compression once handle is allocated. + * + * if we have a 'non-null' handle here then we are coming + * from the slow path and handle has already been allocated. + */ + if (!handle) + handle = zs_malloc(zram->mem_pool, comp_len, + __GFP_KSWAPD_RECLAIM | + __GFP_NOWARN | + __GFP_HIGHMEM | + __GFP_MOVABLE | + __GFP_CMA); + if (!handle) { + zcomp_stream_put(zram->comp); + atomic64_inc(&zram->stats.writestall); + handle = zs_malloc(zram->mem_pool, comp_len, + GFP_NOIO | __GFP_HIGHMEM | + __GFP_MOVABLE | __GFP_CMA); + if (handle) + goto compress_again; + return -ENOMEM; + } + + alloced_pages = zs_get_total_pages(zram->mem_pool); + update_used_max(zram, alloced_pages); + + if (zram->limit_pages && alloced_pages > zram->limit_pages) { + zcomp_stream_put(zram->comp); + zs_free(zram->mem_pool, handle); + return -ENOMEM; + } + + dst = zs_map_object(zram->mem_pool, handle, ZS_MM_WO); + + src = zstrm->buffer; + if (comp_len == PAGE_SIZE) + src = kmap_atomic(page); + memcpy(dst, src, comp_len); + if (comp_len == PAGE_SIZE) + kunmap_atomic(src); + + zcomp_stream_put(zram->comp); + zs_unmap_object(zram->mem_pool, handle); + atomic64_add(comp_len, &zram->stats.compr_data_size); +out: + /* + * Free memory associated with this sector + * before overwriting unused sectors. + */ + zram_slot_lock(zram, index); + zram_free_page(zram, index); + + if (comp_len == PAGE_SIZE) { + zram_set_flag(zram, index, ZRAM_HUGE); + atomic64_inc(&zram->stats.huge_pages); + } + + if (flags) { + zram_set_flag(zram, index, flags); + zram_set_element(zram, index, element); + } else { + zram_set_handle(zram, index, handle); + zram_set_obj_size(zram, index, comp_len); + } + +#ifdef CONFIG_HYBRIDSWAP_CORE + hybridswap_record(zram, index, page->mem_cgroup); +#endif + zram_slot_unlock(zram, index); + + /* Update stats */ + atomic64_inc(&zram->stats.pages_stored); + return ret; +} + +static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, + u32 index, int offset, struct bio *bio) +{ + int ret; + struct page *page = NULL; + void *src; + struct bio_vec vec; + + vec = *bvec; + if (is_partial_io(bvec)) { + void *dst; + /* + * This is a partial IO. We need to read the full page + * before to write the changes. + */ + page = alloc_page(GFP_NOIO|__GFP_HIGHMEM); + if (!page) + return -ENOMEM; + + ret = __zram_bvec_read(zram, page, index, bio, true); + if (ret) + goto out; + + src = kmap_atomic(bvec->bv_page); + dst = kmap_atomic(page); + memcpy(dst + offset, src + bvec->bv_offset, bvec->bv_len); + kunmap_atomic(dst); + kunmap_atomic(src); + + vec.bv_page = page; + vec.bv_len = PAGE_SIZE; + vec.bv_offset = 0; + } + + ret = __zram_bvec_write(zram, &vec, index, bio); +out: + if (is_partial_io(bvec)) + __free_page(page); + return ret; +} + +/* + * zram_bio_discard - handler on discard request + * @index: physical block index in PAGE_SIZE units + * @offset: byte offset within physical block + */ +static void zram_bio_discard(struct zram *zram, u32 index, + int offset, struct bio *bio) +{ + size_t n = bio->bi_iter.bi_size; + + /* + * zram manages data in physical block size units. Because logical block + * size isn't identical with physical block size on some arch, we + * could get a discard request pointing to a specific offset within a + * certain physical block. Although we can handle this request by + * reading that physiclal block and decompressing and partially zeroing + * and re-compressing and then re-storing it, this isn't reasonable + * because our intent with a discard request is to save memory. So + * skipping this logical block is appropriate here. + */ + if (offset) { + if (n <= (PAGE_SIZE - offset)) + return; + + n -= (PAGE_SIZE - offset); + index++; + } + + while (n >= PAGE_SIZE) { + zram_slot_lock(zram, index); + zram_free_page(zram, index); + zram_slot_unlock(zram, index); + atomic64_inc(&zram->stats.notify_free); + index++; + n -= PAGE_SIZE; + } +} + +/* + * Returns errno if it has some problem. Otherwise return 0 or 1. + * Returns 0 if IO request was done synchronously + * Returns 1 if IO request was successfully submitted. + */ +static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, + int offset, unsigned int op, struct bio *bio) +{ + int ret; + + if (!op_is_write(op)) { + atomic64_inc(&zram->stats.num_reads); + ret = zram_bvec_read(zram, bvec, index, offset, bio); + flush_dcache_page(bvec->bv_page); + } else { + atomic64_inc(&zram->stats.num_writes); + ret = zram_bvec_write(zram, bvec, index, offset, bio); + } + + zram_slot_lock(zram, index); + zram_accessed(zram, index); + zram_slot_unlock(zram, index); + + if (unlikely(ret < 0)) { + if (!op_is_write(op)) + atomic64_inc(&zram->stats.failed_reads); + else + atomic64_inc(&zram->stats.failed_writes); + } + + return ret; +} + +static void __zram_make_request(struct zram *zram, struct bio *bio) +{ + int offset; + u32 index; + struct bio_vec bvec; + struct bvec_iter iter; + unsigned long start_time; + + index = bio->bi_iter.bi_sector >> SECTORS_PER_PAGE_SHIFT; + offset = (bio->bi_iter.bi_sector & + (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT; + + switch (bio_op(bio)) { + case REQ_OP_DISCARD: + case REQ_OP_WRITE_ZEROES: + zram_bio_discard(zram, index, offset, bio); + bio_endio(bio); + return; + default: + break; + } + + start_time = bio_start_io_acct(bio); + bio_for_each_segment(bvec, bio, iter) { + struct bio_vec bv = bvec; + unsigned int unwritten = bvec.bv_len; + + do { + bv.bv_len = min_t(unsigned int, PAGE_SIZE - offset, + unwritten); + if (zram_bvec_rw(zram, &bv, index, offset, + bio_op(bio), bio) < 0) { + bio->bi_status = BLK_STS_IOERR; + break; + } + + bv.bv_offset += bv.bv_len; + unwritten -= bv.bv_len; + + update_position(&index, &offset, &bv); + } while (unwritten); + } + bio_end_io_acct(bio, start_time); + bio_endio(bio); +} + +/* + * Handler function for all zram I/O requests. + */ +static blk_qc_t zram_submit_bio(struct bio *bio) +{ + struct zram *zram = bio->bi_disk->private_data; + + if (!valid_io_request(zram, bio->bi_iter.bi_sector, + bio->bi_iter.bi_size)) { + atomic64_inc(&zram->stats.invalid_io); + goto error; + } + + __zram_make_request(zram, bio); + return BLK_QC_T_NONE; + +error: + bio_io_error(bio); + return BLK_QC_T_NONE; +} + +static void zram_slot_free_notify(struct block_device *bdev, + unsigned long index) +{ + struct zram *zram; + + zram = bdev->bd_disk->private_data; + + atomic64_inc(&zram->stats.notify_free); + if (!zram_slot_trylock(zram, index)) { + atomic64_inc(&zram->stats.miss_free); + return; + } + +#ifdef CONFIG_HYBRIDSWAP_CORE + if (!hybridswap_delete(zram, index)) { + zram_slot_unlock(zram, index); + atomic64_inc(&zram->stats.miss_free); + return; + } +#endif + zram_free_page(zram, index); + zram_slot_unlock(zram, index); +} + +static int zram_rw_page(struct block_device *bdev, sector_t sector, + struct page *page, unsigned int op) +{ + int offset, ret; + u32 index; + struct zram *zram; + struct bio_vec bv; + unsigned long start_time; + + if (PageTransHuge(page)) + return -ENOTSUPP; + zram = bdev->bd_disk->private_data; + + if (!valid_io_request(zram, sector, PAGE_SIZE)) { + atomic64_inc(&zram->stats.invalid_io); + ret = -EINVAL; + goto out; + } + + index = sector >> SECTORS_PER_PAGE_SHIFT; + offset = (sector & (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT; + + bv.bv_page = page; + bv.bv_len = PAGE_SIZE; + bv.bv_offset = 0; + + start_time = disk_start_io_acct(bdev->bd_disk, SECTORS_PER_PAGE, op); + ret = zram_bvec_rw(zram, &bv, index, offset, op, NULL); + disk_end_io_acct(bdev->bd_disk, op, start_time); +out: + /* + * If I/O fails, just return error(ie, non-zero) without + * calling page_endio. + * It causes resubmit the I/O with bio request by upper functions + * of rw_page(e.g., swap_readpage, __swap_writepage) and + * bio->bi_end_io does things to handle the error + * (e.g., SetPageError, set_page_dirty and extra works). + */ + if (unlikely(ret < 0)) + return ret; + + switch (ret) { + case 0: + page_endio(page, op_is_write(op), 0); + break; + case 1: + ret = 0; + break; + default: + WARN_ON(1); + } + return ret; +} + +static void zram_reset_device(struct zram *zram) +{ + struct zcomp *comp; + u64 disksize; + + down_write(&zram->init_lock); + + zram->limit_pages = 0; + + if (!init_done(zram)) { + up_write(&zram->init_lock); + return; + } + + comp = zram->comp; + disksize = zram->disksize; + zram->disksize = 0; + + set_capacity(zram->disk, 0); + part_stat_set_all(&zram->disk->part0, 0); + + up_write(&zram->init_lock); + /* I/O operation under all of CPU are done so let's free */ + zram_meta_free(zram, disksize); + memset(&zram->stats, 0, sizeof(zram->stats)); + zcomp_destroy(comp); + reset_bdev(zram); +} + +static ssize_t disksize_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + u64 disksize; + struct zcomp *comp; + struct zram *zram = dev_to_zram(dev); + int err; + + disksize = memparse(buf, NULL); + if (!disksize) + return -EINVAL; + + down_write(&zram->init_lock); + if (init_done(zram)) { + pr_info("Cannot change disksize for initialized device\n"); + err = -EBUSY; + goto out_unlock; + } + + disksize = PAGE_ALIGN(disksize); + if (!zram_meta_alloc(zram, disksize)) { + err = -ENOMEM; + goto out_unlock; + } + + comp = zcomp_create(zram->compressor); + if (IS_ERR(comp)) { + pr_err("Cannot initialise %s compressing backend\n", + zram->compressor); + err = PTR_ERR(comp); + goto out_free_meta; + } + + zram->comp = comp; + zram->disksize = disksize; + set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); + + revalidate_disk_size(zram->disk, true); + up_write(&zram->init_lock); + + return len; + +out_free_meta: + zram_meta_free(zram, disksize); +out_unlock: + up_write(&zram->init_lock); + return err; +} + +static ssize_t reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int ret; + unsigned short do_reset; + struct zram *zram; + struct block_device *bdev; + + ret = kstrtou16(buf, 10, &do_reset); + if (ret) + return ret; + + if (!do_reset) + return -EINVAL; + + zram = dev_to_zram(dev); + bdev = bdget_disk(zram->disk, 0); + if (!bdev) + return -ENOMEM; + + mutex_lock(&bdev->bd_mutex); + /* Do not reset an active device or claimed device */ + if (bdev->bd_openers || zram->claim) { + mutex_unlock(&bdev->bd_mutex); + bdput(bdev); + return -EBUSY; + } + + /* From now on, anyone can't open /dev/zram[0-9] */ + zram->claim = true; + mutex_unlock(&bdev->bd_mutex); + + /* Make sure all the pending I/O are finished */ + fsync_bdev(bdev); + zram_reset_device(zram); + revalidate_disk_size(zram->disk, true); + bdput(bdev); + + mutex_lock(&bdev->bd_mutex); + zram->claim = false; + mutex_unlock(&bdev->bd_mutex); + + return len; +} + +static int zram_open(struct block_device *bdev, fmode_t mode) +{ + int ret = 0; + struct zram *zram; + + WARN_ON(!mutex_is_locked(&bdev->bd_mutex)); + + zram = bdev->bd_disk->private_data; + /* zram was claimed to reset so open request fails */ + if (zram->claim) + ret = -EBUSY; + + return ret; +} + +static const struct block_device_operations zram_devops = { + .open = zram_open, + .submit_bio = zram_submit_bio, + .swap_slot_free_notify = zram_slot_free_notify, + .rw_page = zram_rw_page, + .owner = THIS_MODULE +}; + +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +static const struct block_device_operations zram_wb_devops = { + .open = zram_open, + .submit_bio = zram_submit_bio, + .swap_slot_free_notify = zram_slot_free_notify, + .owner = THIS_MODULE +}; +#endif + +static DEVICE_ATTR_WO(compact); +static DEVICE_ATTR_RW(disksize); +static DEVICE_ATTR_RO(initstate); +static DEVICE_ATTR_WO(reset); +static DEVICE_ATTR_WO(mem_limit); +static DEVICE_ATTR_WO(mem_used_max); +static DEVICE_ATTR_WO(idle); +static DEVICE_ATTR_RW(max_comp_streams); +static DEVICE_ATTR_RW(comp_algorithm); +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +static DEVICE_ATTR_RW(backing_dev); +static DEVICE_ATTR_WO(writeback); +static DEVICE_ATTR_RW(writeback_limit); +static DEVICE_ATTR_RW(writeback_limit_enable); +#endif +#ifdef CONFIG_HYBRIDSWAP +static DEVICE_ATTR_RO(hybridswap_vmstat); +static DEVICE_ATTR_RW(hybridswap_loglevel); +static DEVICE_ATTR_RW(hybridswap_enable); +#endif +#ifdef CONFIG_HYBRIDSWAP_SWAPD +static DEVICE_ATTR_RW(hybridswap_swapd_pause); +#endif +#ifdef CONFIG_HYBRIDSWAP_CORE +static DEVICE_ATTR_RW(hybridswap_core_enable); +static DEVICE_ATTR_RW(hybridswap_loop_device); +static DEVICE_ATTR_RW(hybridswap_dev_life); +static DEVICE_ATTR_RW(hybridswap_quota_day); +static DEVICE_ATTR_RO(hybridswap_report); +static DEVICE_ATTR_RO(hybridswap_stat_snap); +static DEVICE_ATTR_RO(hybridswap_meminfo); +static DEVICE_ATTR_RW(hybridswap_zram_increase); +#endif +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS +static DEVICE_ATTR_RW(hybridswap_akcompress); +#endif + +static struct attribute *zram_disk_attrs[] = { + &dev_attr_disksize.attr, + &dev_attr_initstate.attr, + &dev_attr_reset.attr, + &dev_attr_compact.attr, + &dev_attr_mem_limit.attr, + &dev_attr_mem_used_max.attr, + &dev_attr_idle.attr, + &dev_attr_max_comp_streams.attr, + &dev_attr_comp_algorithm.attr, +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK + &dev_attr_backing_dev.attr, + &dev_attr_writeback.attr, + &dev_attr_writeback_limit.attr, + &dev_attr_writeback_limit_enable.attr, +#endif + &dev_attr_io_stat.attr, + &dev_attr_mm_stat.attr, +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK + &dev_attr_bd_stat.attr, +#endif + &dev_attr_debug_stat.attr, +#ifdef CONFIG_HYBRIDSWAP + &dev_attr_hybridswap_vmstat.attr, + &dev_attr_hybridswap_loglevel.attr, + &dev_attr_hybridswap_enable.attr, +#endif +#ifdef CONFIG_HYBRIDSWAP_SWAPD + &dev_attr_hybridswap_swapd_pause.attr, +#endif +#ifdef CONFIG_HYBRIDSWAP_CORE + &dev_attr_hybridswap_core_enable.attr, + &dev_attr_hybridswap_report.attr, + &dev_attr_hybridswap_meminfo.attr, + &dev_attr_hybridswap_stat_snap.attr, + &dev_attr_hybridswap_loop_device.attr, + &dev_attr_hybridswap_dev_life.attr, + &dev_attr_hybridswap_quota_day.attr, + &dev_attr_hybridswap_zram_increase.attr, +#endif +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + &dev_attr_hybridswap_akcompress.attr, +#endif + NULL, +}; + +static const struct attribute_group zram_disk_attr_group = { + .attrs = zram_disk_attrs, +}; + +static const struct attribute_group *zram_disk_attr_groups[] = { + &zram_disk_attr_group, + NULL, +}; + +/* + * Allocate and initialize new zram device. the function returns + * '>= 0' device_id upon success, and negative value otherwise. + */ +static int zram_add(void) +{ + struct zram *zram; + struct request_queue *queue; + int ret, device_id; + + zram = kzalloc(sizeof(struct zram), GFP_KERNEL); + if (!zram) + return -ENOMEM; + + ret = idr_alloc(&zram_index_idr, zram, 0, 0, GFP_KERNEL); + if (ret < 0) + goto out_free_dev; + device_id = ret; + + init_rwsem(&zram->init_lock); +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK + spin_lock_init(&zram->wb_limit_lock); +#endif + queue = blk_alloc_queue(NUMA_NO_NODE); + if (!queue) { + pr_err("Error allocating disk queue for device %d\n", + device_id); + ret = -ENOMEM; + goto out_free_idr; + } + + /* gendisk structure */ + zram->disk = alloc_disk(1); + if (!zram->disk) { + pr_err("Error allocating disk structure for device %d\n", + device_id); + ret = -ENOMEM; + goto out_free_queue; + } + + zram->disk->major = zram_major; + zram->disk->first_minor = device_id; + zram->disk->fops = &zram_devops; + zram->disk->queue = queue; + zram->disk->private_data = zram; + snprintf(zram->disk->disk_name, 16, "zram%d", device_id); + + /* Actual capacity set using syfs (/sys/block/zram/disksize */ + set_capacity(zram->disk, 0); + /* zram devices sort of resembles non-rotational disks */ + blk_queue_flag_set(QUEUE_FLAG_NONROT, zram->disk->queue); + blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, zram->disk->queue); + + /* + * To ensure that we always get PAGE_SIZE aligned + * and n*PAGE_SIZED sized I/O requests. + */ + blk_queue_physical_block_size(zram->disk->queue, PAGE_SIZE); + blk_queue_logical_block_size(zram->disk->queue, + ZRAM_LOGICAL_BLOCK_SIZE); + blk_queue_io_min(zram->disk->queue, PAGE_SIZE); + blk_queue_io_opt(zram->disk->queue, PAGE_SIZE); + zram->disk->queue->limits.discard_granularity = PAGE_SIZE; + blk_queue_max_discard_sectors(zram->disk->queue, UINT_MAX); + blk_queue_flag_set(QUEUE_FLAG_DISCARD, zram->disk->queue); + + /* + * zram_bio_discard() will clear all logical blocks if logical block + * size is identical with physical block size(PAGE_SIZE). But if it is + * different, we will skip discarding some parts of logical blocks in + * the part of the request range which isn't aligned to physical block + * size. So we can't ensure that all discarded logical blocks are + * zeroed. + */ + if (ZRAM_LOGICAL_BLOCK_SIZE == PAGE_SIZE) + blk_queue_max_write_zeroes_sectors(zram->disk->queue, UINT_MAX); + + blk_queue_flag_set(QUEUE_FLAG_STABLE_WRITES, zram->disk->queue); + device_add_disk(NULL, zram->disk, zram_disk_attr_groups); + + strlcpy(zram->compressor, default_compressor, sizeof(zram->compressor)); + + zram_debugfs_register(zram); + pr_info("Added device: %s\n", zram->disk->disk_name); + return device_id; + +out_free_queue: + blk_cleanup_queue(queue); +out_free_idr: + idr_remove(&zram_index_idr, device_id); +out_free_dev: + kfree(zram); + return ret; +} + +static int zram_remove(struct zram *zram) +{ + struct block_device *bdev; + + bdev = bdget_disk(zram->disk, 0); + if (!bdev) + return -ENOMEM; + + mutex_lock(&bdev->bd_mutex); + if (bdev->bd_openers || zram->claim) { + mutex_unlock(&bdev->bd_mutex); + bdput(bdev); + return -EBUSY; + } + + zram->claim = true; + mutex_unlock(&bdev->bd_mutex); + + zram_debugfs_unregister(zram); + + /* Make sure all the pending I/O are finished */ + fsync_bdev(bdev); + zram_reset_device(zram); + bdput(bdev); + + pr_info("Removed device: %s\n", zram->disk->disk_name); + + del_gendisk(zram->disk); + blk_cleanup_queue(zram->disk->queue); + put_disk(zram->disk); + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + destroy_akcompressd_task(zram); +#endif + kfree(zram); + return 0; +} + +/* zram-control sysfs attributes */ + +/* + * NOTE: hot_add attribute is not the usual read-only sysfs attribute. In a + * sense that reading from this file does alter the state of your system -- it + * creates a new un-initialized zram device and returns back this device's + * device_id (or an error code if it fails to create a new device). + */ +static ssize_t hot_add_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + int ret; + + mutex_lock(&zram_index_mutex); + ret = zram_add(); + mutex_unlock(&zram_index_mutex); + + if (ret < 0) + return ret; + return scnprintf(buf, PAGE_SIZE, "%d\n", ret); +} +static struct class_attribute class_attr_hot_add = + __ATTR(hot_add, 0400, hot_add_show, NULL); + +static ssize_t hot_remove_store(struct class *class, + struct class_attribute *attr, + const char *buf, + size_t count) +{ + struct zram *zram; + int ret, dev_id; + + /* dev_id is gendisk->first_minor, which is `int' */ + ret = kstrtoint(buf, 10, &dev_id); + if (ret) + return ret; + if (dev_id < 0) + return -EINVAL; + + mutex_lock(&zram_index_mutex); + + zram = idr_find(&zram_index_idr, dev_id); + if (zram) { + ret = zram_remove(zram); + if (!ret) + idr_remove(&zram_index_idr, dev_id); + } else { + ret = -ENODEV; + } + + mutex_unlock(&zram_index_mutex); + return ret ? ret : count; +} +static CLASS_ATTR_WO(hot_remove); + +static struct attribute *zram_control_class_attrs[] = { + &class_attr_hot_add.attr, + &class_attr_hot_remove.attr, + NULL, +}; +ATTRIBUTE_GROUPS(zram_control_class); + +static struct class zram_control_class = { + .name = "zram-control", + .owner = THIS_MODULE, + .class_groups = zram_control_class_groups, +}; + +static int zram_remove_cb(int id, void *ptr, void *data) +{ + zram_remove(ptr); + return 0; +} + +static void destroy_devices(void) +{ + class_unregister(&zram_control_class); + idr_for_each(&zram_index_idr, &zram_remove_cb, NULL); + zram_debugfs_destroy(); + idr_destroy(&zram_index_idr); + unregister_blkdev(zram_major, "zram"); + cpuhp_remove_multi_state(CPUHP_ZCOMP_PREPARE); +} + +static int __init zram_init(void) +{ + int ret; + + ret = cpuhp_setup_state_multi(CPUHP_ZCOMP_PREPARE, "block/zram:prepare", + zcomp_cpu_up_prepare, zcomp_cpu_dead); + if (ret < 0) + return ret; + + ret = class_register(&zram_control_class); + if (ret) { + pr_err("Unable to register zram-control class\n"); + cpuhp_remove_multi_state(CPUHP_ZCOMP_PREPARE); + return ret; + } + + zram_debugfs_create(); + zram_major = register_blkdev(0, "zram"); + if (zram_major <= 0) { + pr_err("Unable to get major number\n"); + class_unregister(&zram_control_class); + cpuhp_remove_multi_state(CPUHP_ZCOMP_PREPARE); + return -EBUSY; + } + + while (num_devices != 0) { + mutex_lock(&zram_index_mutex); + ret = zram_add(); + mutex_unlock(&zram_index_mutex); + if (ret < 0) + goto out_error; + num_devices--; + } + +#ifdef CONFIG_HYBRIDSWAP + ret = hybridswap_pre_init(); + if (ret) + goto out_error; +#endif + return 0; + +out_error: + destroy_devices(); + return ret; +} + +static void __exit zram_exit(void) +{ + destroy_devices(); +} + +module_init(zram_init); +module_exit(zram_exit); + +module_param(num_devices, uint, 0); +MODULE_PARM_DESC(num_devices, "Number of pre-created zram devices"); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Nitin Gupta "); +MODULE_DESCRIPTION("Compressed RAM Block Device"); diff --git a/drivers/moto_swap/zram-5.10/zram_drv.h b/drivers/moto_swap/zram-5.10/zram_drv.h new file mode 100644 index 000000000000..7e8e5ab8c148 --- /dev/null +++ b/drivers/moto_swap/zram-5.10/zram_drv.h @@ -0,0 +1,150 @@ +/* + * Compressed RAM block device + * + * Copyright (C) 2008, 2009, 2010 Nitin Gupta + * 2012, 2013 Minchan Kim + * + * This code is released using a dual license strategy: BSD/GPL + * You can choose the licence that better fits your requirements. + * + * Released under the terms of 3-clause BSD License + * Released under the terms of GNU General Public License Version 2.0 + * + */ + +#ifndef _ZRAM_DRV_H_ +#define _ZRAM_DRV_H_ + +#include +#include +#include + +#include "zcomp.h" + +#define SECTORS_PER_PAGE_SHIFT (PAGE_SHIFT - SECTOR_SHIFT) +#define SECTORS_PER_PAGE (1 << SECTORS_PER_PAGE_SHIFT) +#define ZRAM_LOGICAL_BLOCK_SHIFT 12 +#define ZRAM_LOGICAL_BLOCK_SIZE (1 << ZRAM_LOGICAL_BLOCK_SHIFT) +#define ZRAM_SECTOR_PER_LOGICAL_BLOCK \ + (1 << (ZRAM_LOGICAL_BLOCK_SHIFT - SECTOR_SHIFT)) + + +/* + * The lower ZRAM_FLAG_SHIFT bits of table.flags is for + * object size (excluding header), the higher bits is for + * zram_pageflags. + * + * zram is mainly used for memory efficiency so we want to keep memory + * footprint small so we can squeeze size and flags into a field. + * The lower ZRAM_FLAG_SHIFT bits is for object size (excluding header), + * the higher bits is for zram_pageflags. + */ +#define ZRAM_FLAG_SHIFT 24 + +/* Flags for zram pages (table[page_no].flags) */ +enum zram_pageflags { + /* zram slot is locked */ + ZRAM_LOCK = ZRAM_FLAG_SHIFT, + ZRAM_SAME, /* Page consists the same element */ + ZRAM_WB, /* page is stored on backing_device */ + ZRAM_UNDER_WB, /* page is under writeback */ + ZRAM_HUGE, /* Incompressible page */ + ZRAM_IDLE, /* not accessed page since last idle marking */ +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + ZRAM_CACHED, /* page is cached in async compress cache buffer */ + ZRAM_CACHED_COMPRESS, /* page is under async compress */ +#endif +#ifdef CONFIG_HYBRIDSWAP_CORE + ZRAM_BATCHING_OUT, + ZRAM_FROM_HYBRIDSWAP, + ZRAM_MCGID_CLEAR, + ZRAM_IN_BD, /* zram stored in back device */ +#endif + __NR_ZRAM_PAGEFLAGS, +}; + +/*-- Data structures */ + +/* Allocated for each disk page */ +struct zram_table_entry { + union { + unsigned long handle; + unsigned long element; +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + unsigned long page; +#endif + }; + unsigned long flags; +#ifdef CONFIG_HYBRIDSWAP_ZRAM_MEMORY_TRACKING + ktime_t ac_time; +#endif +}; + +struct zram_stats { + atomic64_t compr_data_size; /* compressed size of pages stored */ + atomic64_t num_reads; /* failed + successful */ + atomic64_t num_writes; /* --do-- */ + atomic64_t failed_reads; /* can happen when memory is too low */ + atomic64_t failed_writes; /* can happen when memory is too low */ + atomic64_t invalid_io; /* non-page-aligned I/O requests */ + atomic64_t notify_free; /* no. of swap slot free notifications */ + atomic64_t same_pages; /* no. of same element filled pages */ + atomic64_t huge_pages; /* no. of huge pages */ + atomic64_t pages_stored; /* no. of pages currently stored */ + atomic_long_t max_used_pages; /* no. of maximum pages stored */ + atomic64_t writestall; /* no. of write slow paths */ + atomic64_t miss_free; /* no. of missed free */ +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK + atomic64_t bd_count; /* no. of pages in backing device */ + atomic64_t bd_reads; /* no. of reads from backing device */ + atomic64_t bd_writes; /* no. of writes from backing device */ +#endif +}; + +struct zram { + struct zram_table_entry *table; + struct zs_pool *mem_pool; + struct zcomp *comp; + struct gendisk *disk; + /* Prevent concurrent execution of device init */ + struct rw_semaphore init_lock; + /* + * the number of pages zram can consume for storing compressed data + */ + unsigned long limit_pages; + + struct zram_stats stats; + /* + * This is the limit on amount of *uncompressed* worth of data + * we can store in a disk. + */ + u64 disksize; /* bytes */ + char compressor[CRYPTO_MAX_ALG_NAME]; + /* + * zram is claimed so open request will be failed + */ + bool claim; /* Protected by bdev->bd_mutex */ + struct file *backing_dev; +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK + spinlock_t wb_limit_lock; + bool wb_limit_enable; + u64 bd_wb_limit; + struct block_device *bdev; + unsigned int old_block_size; + unsigned long *bitmap; + unsigned long nr_pages; +#endif +#ifdef CONFIG_HYBRIDSWAP_ZRAM_MEMORY_TRACKING + struct dentry *debugfs_dir; +#endif +#if (defined CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK) || (defined CONFIG_HYBRIDSWAP_CORE) + struct block_device *bdev; + unsigned int old_block_size; + unsigned long nr_pages; + unsigned long increase_nr_pages; +#endif +#ifdef CONFIG_HYBRIDSWAP_CORE + struct hyb_info *infos; +#endif +}; +#endif diff --git a/drivers/moto_swap/zram-5.10/zram_drv_internal.h b/drivers/moto_swap/zram-5.10/zram_drv_internal.h new file mode 100644 index 000000000000..3c102cf38773 --- /dev/null +++ b/drivers/moto_swap/zram-5.10/zram_drv_internal.h @@ -0,0 +1,39 @@ +#ifndef _ZRAM_DRV_INTERNAL_H_ +#define _ZRAM_DRV_INTERNAL_H_ +#ifdef BIT +#undef BIT +#define BIT(nr) (1lu << (nr)) +#endif + +#define zram_slot_lock(zram, index) (bit_spin_lock(ZRAM_LOCK, &zram->table[index].flags)) + +#define zram_slot_unlock(zram, index) (bit_spin_unlock(ZRAM_LOCK, &zram->table[index].flags)) + +#define init_done(zram) (zram->disksize) + +#define dev_to_zram(dev) ((struct zram *)dev_to_disk(dev)->private_data) + +#define zram_get_handle(zram, index) (zram->table[index].handle) + +#define zram_set_handle(zram, index, handle_val) (zram->table[index].handle = handle_val) + +#define zram_test_flag(zram, index, flag) (zram->table[index].flags & BIT(flag)) + +#define zram_set_flag(zram, index, flag) (zram->table[index].flags |= BIT(flag)) + +#define zram_clear_flag(zram, index, flag) (zram->table[index].flags &= ~BIT(flag)) + +#define zram_set_element(zram, index, element) (zram->table[index].element = element) + +#define zram_get_obj_size(zram, index) (zram->table[index].flags & (BIT(ZRAM_FLAG_SHIFT) - 1)) + +#define zram_set_obj_size(zram, index, size) do {\ + unsigned long flags = zram->table[index].flags >> ZRAM_FLAG_SHIFT; \ + zram->table[index].flags = (flags << ZRAM_FLAG_SHIFT) | size; \ +} while(0) + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS +extern int async_compress_page(struct zram *zram, struct page* page); +extern void update_zram_index(struct zram *zram, u32 index, unsigned long page); +#endif +#endif diff --git a/drivers/moto_swap/zram-5.15/zcomp.c b/drivers/moto_swap/zram-5.15/zcomp.c new file mode 100644 index 000000000000..0916de952e09 --- /dev/null +++ b/drivers/moto_swap/zram-5.15/zcomp.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2014 Sergey Senozhatsky. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zcomp.h" + +static const char * const backends[] = { +#if IS_ENABLED(CONFIG_CRYPTO_LZO) + "lzo", + "lzo-rle", +#endif +#if IS_ENABLED(CONFIG_CRYPTO_LZ4) + "lz4", +#endif +#if IS_ENABLED(CONFIG_CRYPTO_LZ4HC) + "lz4hc", +#endif +#if IS_ENABLED(CONFIG_CRYPTO_842) + "842", +#endif +#if IS_ENABLED(CONFIG_CRYPTO_ZSTD) + "zstd", +#endif +}; + +static void zcomp_strm_free(struct zcomp_strm *zstrm) +{ + if (!IS_ERR_OR_NULL(zstrm->tfm)) + crypto_free_comp(zstrm->tfm); + free_pages((unsigned long)zstrm->buffer, 1); + zstrm->tfm = NULL; + zstrm->buffer = NULL; +} + +/* + * Initialize zcomp_strm structure with ->tfm initialized by backend, and + * ->buffer. Return a negative value on error. + */ +static int zcomp_strm_init(struct zcomp_strm *zstrm, struct zcomp *comp) +{ + zstrm->tfm = crypto_alloc_comp(comp->name, 0, 0); + /* + * allocate 2 pages. 1 for compressed data, plus 1 extra for the + * case when compressed size is larger than the original one + */ + zstrm->buffer = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); + if (IS_ERR_OR_NULL(zstrm->tfm) || !zstrm->buffer) { + zcomp_strm_free(zstrm); + return -ENOMEM; + } + return 0; +} + +bool zcomp_available_algorithm(const char *comp) +{ + /* + * Crypto does not ignore a trailing new line symbol, + * so make sure you don't supply a string containing + * one. + * This also means that we permit zcomp initialisation + * with any compressing algorithm known to crypto api. + */ + return crypto_has_comp(comp, 0, 0) == 1; +} + +/* show available compressors */ +ssize_t zcomp_available_show(const char *comp, char *buf) +{ + bool known_algorithm = false; + ssize_t sz = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(backends); i++) { + if (!strcmp(comp, backends[i])) { + known_algorithm = true; + sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, + "[%s] ", backends[i]); + } else { + sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, + "%s ", backends[i]); + } + } + + /* + * Out-of-tree module known to crypto api or a missing + * entry in `backends'. + */ + if (!known_algorithm && crypto_has_comp(comp, 0, 0) == 1) + sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, + "[%s] ", comp); + + sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); + return sz; +} + +struct zcomp_strm *zcomp_stream_get(struct zcomp *comp) +{ + local_lock(&comp->stream->lock); + return this_cpu_ptr(comp->stream); +} + +void zcomp_stream_put(struct zcomp *comp) +{ + local_unlock(&comp->stream->lock); +} + +int zcomp_compress(struct zcomp_strm *zstrm, + const void *src, unsigned int *dst_len) +{ + /* + * Our dst memory (zstrm->buffer) is always `2 * PAGE_SIZE' sized + * because sometimes we can endup having a bigger compressed data + * due to various reasons: for example compression algorithms tend + * to add some padding to the compressed buffer. Speaking of padding, + * comp algorithm `842' pads the compressed length to multiple of 8 + * and returns -ENOSP when the dst memory is not big enough, which + * is not something that ZRAM wants to see. We can handle the + * `compressed_size > PAGE_SIZE' case easily in ZRAM, but when we + * receive -ERRNO from the compressing backend we can't help it + * anymore. To make `842' happy we need to tell the exact size of + * the dst buffer, zram_drv will take care of the fact that + * compressed buffer is too big. + */ + *dst_len = PAGE_SIZE * 2; + + return crypto_comp_compress(zstrm->tfm, + src, PAGE_SIZE, + zstrm->buffer, dst_len); +} + +int zcomp_decompress(struct zcomp_strm *zstrm, + const void *src, unsigned int src_len, void *dst) +{ + unsigned int dst_len = PAGE_SIZE; + + return crypto_comp_decompress(zstrm->tfm, + src, src_len, + dst, &dst_len); +} + +int zcomp_cpu_up_prepare(unsigned int cpu, struct hlist_node *node) +{ + struct zcomp *comp = hlist_entry(node, struct zcomp, node); + struct zcomp_strm *zstrm; + int ret; + + zstrm = per_cpu_ptr(comp->stream, cpu); + local_lock_init(&zstrm->lock); + + ret = zcomp_strm_init(zstrm, comp); + if (ret) + pr_err("Can't allocate a compression stream\n"); + return ret; +} + +int zcomp_cpu_dead(unsigned int cpu, struct hlist_node *node) +{ + struct zcomp *comp = hlist_entry(node, struct zcomp, node); + struct zcomp_strm *zstrm; + + zstrm = per_cpu_ptr(comp->stream, cpu); + zcomp_strm_free(zstrm); + return 0; +} + +static int zcomp_init(struct zcomp *comp) +{ + int ret; + + comp->stream = alloc_percpu(struct zcomp_strm); + if (!comp->stream) + return -ENOMEM; + + ret = cpuhp_state_add_instance(CPUHP_ZCOMP_PREPARE, &comp->node); + if (ret < 0) + goto cleanup; + return 0; + +cleanup: + free_percpu(comp->stream); + return ret; +} + +void zcomp_destroy(struct zcomp *comp) +{ + cpuhp_state_remove_instance(CPUHP_ZCOMP_PREPARE, &comp->node); + free_percpu(comp->stream); + kfree(comp); +} + +/* + * search available compressors for requested algorithm. + * allocate new zcomp and initialize it. return compressing + * backend pointer or ERR_PTR if things went bad. ERR_PTR(-EINVAL) + * if requested algorithm is not supported, ERR_PTR(-ENOMEM) in + * case of allocation error, or any other error potentially + * returned by zcomp_init(). + */ +struct zcomp *zcomp_create(const char *compress) +{ + struct zcomp *comp; + int error; + + /* + * Crypto API will execute /sbin/modprobe if the compression module + * is not loaded yet. We must do it here, otherwise we are about to + * call /sbin/modprobe under CPU hot-plug lock. + */ + if (!zcomp_available_algorithm(compress)) + return ERR_PTR(-EINVAL); + + comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL); + if (!comp) + return ERR_PTR(-ENOMEM); + + comp->name = compress; + error = zcomp_init(comp); + if (error) { + kfree(comp); + return ERR_PTR(error); + } + return comp; +} diff --git a/drivers/moto_swap/zram-5.15/zcomp.h b/drivers/moto_swap/zram-5.15/zcomp.h new file mode 100644 index 000000000000..40f6420f4b2e --- /dev/null +++ b/drivers/moto_swap/zram-5.15/zcomp.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2014 Sergey Senozhatsky. + */ + +#ifndef _ZCOMP_H_ +#define _ZCOMP_H_ +#include + +struct zcomp_strm { + /* The members ->buffer and ->tfm are protected by ->lock. */ + local_lock_t lock; + /* compression/decompression buffer */ + void *buffer; + struct crypto_comp *tfm; +}; + +/* dynamic per-device compression frontend */ +struct zcomp { + struct zcomp_strm __percpu *stream; + const char *name; + struct hlist_node node; +}; + +int zcomp_cpu_up_prepare(unsigned int cpu, struct hlist_node *node); +int zcomp_cpu_dead(unsigned int cpu, struct hlist_node *node); +ssize_t zcomp_available_show(const char *comp, char *buf); +bool zcomp_available_algorithm(const char *comp); + +struct zcomp *zcomp_create(const char *comp); +void zcomp_destroy(struct zcomp *comp); + +struct zcomp_strm *zcomp_stream_get(struct zcomp *comp); +void zcomp_stream_put(struct zcomp *comp); + +int zcomp_compress(struct zcomp_strm *zstrm, + const void *src, unsigned int *dst_len); + +int zcomp_decompress(struct zcomp_strm *zstrm, + const void *src, unsigned int src_len, void *dst); + +bool zcomp_set_max_streams(struct zcomp *comp, int num_strm); +#endif /* _ZCOMP_H_ */ diff --git a/drivers/moto_swap/zram-5.15/zram_drv.c b/drivers/moto_swap/zram-5.15/zram_drv.c new file mode 100644 index 000000000000..ee4dc91f8559 --- /dev/null +++ b/drivers/moto_swap/zram-5.15/zram_drv.c @@ -0,0 +1,2169 @@ +/* + * Compressed RAM block device + * + * Copyright (C) 2008, 2009, 2010 Nitin Gupta + * 2012, 2013 Minchan Kim + * + * This code is released using a dual license strategy: BSD/GPL + * You can choose the licence that better fits your requirements. + * + * Released under the terms of 3-clause BSD License + * Released under the terms of GNU General Public License Version 2.0 + * + */ + +#define KMSG_COMPONENT "zram" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zram_drv.h" +#include "zram_drv_internal.h" +#ifdef CONFIG_HYBRIDSWAP +#include "../hybridswap/hybridswap.h" +#endif + +static DEFINE_IDR(zram_index_idr); +/* idr index must be protected */ +static DEFINE_MUTEX(zram_index_mutex); + +static int zram_major; +static const char *default_compressor = "lzo-rle"; + +/* Module params (documentation at end) */ +static unsigned int num_devices = 1; +/* + * Pages that compress to sizes equals or greater than this are stored + * uncompressed in memory. + */ +static size_t huge_class_size; + +static const struct block_device_operations zram_devops; +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +static const struct block_device_operations zram_wb_devops; +#endif + +static void zram_free_page(struct zram *zram, size_t index); +static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, + u32 index, int offset, struct bio *bio); + + +static int zram_slot_trylock(struct zram *zram, u32 index) +{ + return bit_spin_trylock(ZRAM_LOCK, &zram->table[index].flags); +} + +static unsigned long zram_get_element(struct zram *zram, u32 index) +{ + return zram->table[index].element; +} + +static inline bool zram_allocated(struct zram *zram, u32 index) +{ + return zram_get_obj_size(zram, index) || + zram_test_flag(zram, index, ZRAM_SAME) || + zram_test_flag(zram, index, ZRAM_WB); +} + +#if PAGE_SIZE != 4096 +static inline bool is_partial_io(struct bio_vec *bvec) +{ + return bvec->bv_len != PAGE_SIZE; +} +#else +static inline bool is_partial_io(struct bio_vec *bvec) +{ + return false; +} +#endif + +/* + * Check if request is within bounds and aligned on zram logical blocks. + */ +static inline bool valid_io_request(struct zram *zram, + sector_t start, unsigned int size) +{ + u64 end, bound; + + /* unaligned request */ + if (unlikely(start & (ZRAM_SECTOR_PER_LOGICAL_BLOCK - 1))) + return false; + if (unlikely(size & (ZRAM_LOGICAL_BLOCK_SIZE - 1))) + return false; + + end = start + (size >> SECTOR_SHIFT); + bound = zram->disksize >> SECTOR_SHIFT; + /* out of range range */ + if (unlikely(start >= bound || end > bound || start > end)) + return false; + + /* I/O request is valid */ + return true; +} + +static void update_position(u32 *index, int *offset, struct bio_vec *bvec) +{ + *index += (*offset + bvec->bv_len) / PAGE_SIZE; + *offset = (*offset + bvec->bv_len) % PAGE_SIZE; +} + +static inline void update_used_max(struct zram *zram, + const unsigned long pages) +{ + unsigned long old_max, cur_max; + + old_max = atomic_long_read(&zram->stats.max_used_pages); + + do { + cur_max = old_max; + if (pages > cur_max) + old_max = atomic_long_cmpxchg( + &zram->stats.max_used_pages, cur_max, pages); + } while (old_max != cur_max); +} + +static inline void zram_fill_page(void *ptr, unsigned long len, + unsigned long value) +{ + WARN_ON_ONCE(!IS_ALIGNED(len, sizeof(unsigned long))); + memset_l(ptr, value, len / sizeof(unsigned long)); +} + +static bool page_same_filled(void *ptr, unsigned long *element) +{ + unsigned long *page; + unsigned long val; + unsigned int pos, last_pos = PAGE_SIZE / sizeof(*page) - 1; + + page = (unsigned long *)ptr; + val = page[0]; + + if (val != page[last_pos]) + return false; + + for (pos = 1; pos < last_pos; pos++) { + if (val != page[pos]) + return false; + } + + *element = val; + + return true; +} + +static ssize_t initstate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 val; + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + val = init_done(zram); + up_read(&zram->init_lock); + + return scnprintf(buf, PAGE_SIZE, "%u\n", val); +} + +static ssize_t disksize_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return scnprintf(buf, PAGE_SIZE, "%llu\n", zram->disksize); +} + +static ssize_t mem_limit_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + u64 limit; + char *tmp; + struct zram *zram = dev_to_zram(dev); + + limit = memparse(buf, &tmp); + if (buf == tmp) /* no chars parsed, invalid input */ + return -EINVAL; + + down_write(&zram->init_lock); + zram->limit_pages = PAGE_ALIGN(limit) >> PAGE_SHIFT; + up_write(&zram->init_lock); + + return len; +} + +static ssize_t mem_used_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int err; + unsigned long val; + struct zram *zram = dev_to_zram(dev); + + err = kstrtoul(buf, 10, &val); + if (err || val != 0) + return -EINVAL; + + down_read(&zram->init_lock); + if (init_done(zram)) { + atomic_long_set(&zram->stats.max_used_pages, + zs_get_total_pages(zram->mem_pool)); + } + up_read(&zram->init_lock); + + return len; +} + +static ssize_t idle_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + unsigned long nr_pages = zram->disksize >> PAGE_SHIFT; + int index; + + if (!sysfs_streq(buf, "all")) + return -EINVAL; + + down_read(&zram->init_lock); + if (!init_done(zram)) { + up_read(&zram->init_lock); + return -EINVAL; + } + + for (index = 0; index < nr_pages; index++) { + /* + * Do not mark ZRAM_UNDER_WB slot as ZRAM_IDLE to close race. + * See the comment in writeback_store. + */ + zram_slot_lock(zram, index); + if (zram_allocated(zram, index) && + !zram_test_flag(zram, index, ZRAM_UNDER_WB)) + zram_set_flag(zram, index, ZRAM_IDLE); + zram_slot_unlock(zram, index); + } + + up_read(&zram->init_lock); + + return len; +} + +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +static ssize_t writeback_limit_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + u64 val; + ssize_t ret = -EINVAL; + + if (kstrtoull(buf, 10, &val)) + return ret; + + down_read(&zram->init_lock); + spin_lock(&zram->wb_limit_lock); + zram->wb_limit_enable = val; + spin_unlock(&zram->wb_limit_lock); + up_read(&zram->init_lock); + ret = len; + + return ret; +} + +static ssize_t writeback_limit_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + bool val; + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + spin_lock(&zram->wb_limit_lock); + val = zram->wb_limit_enable; + spin_unlock(&zram->wb_limit_lock); + up_read(&zram->init_lock); + + return scnprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t writeback_limit_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + u64 val; + ssize_t ret = -EINVAL; + + if (kstrtoull(buf, 10, &val)) + return ret; + + down_read(&zram->init_lock); + spin_lock(&zram->wb_limit_lock); + zram->bd_wb_limit = val; + spin_unlock(&zram->wb_limit_lock); + up_read(&zram->init_lock); + ret = len; + + return ret; +} + +static ssize_t writeback_limit_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u64 val; + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + spin_lock(&zram->wb_limit_lock); + val = zram->bd_wb_limit; + spin_unlock(&zram->wb_limit_lock); + up_read(&zram->init_lock); + + return scnprintf(buf, PAGE_SIZE, "%llu\n", val); +} + +static void reset_bdev(struct zram *zram) +{ + struct block_device *bdev; + + if (!zram->backing_dev) + return; + + bdev = zram->bdev; + blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL); + /* hope filp_close flush all of IO */ + filp_close(zram->backing_dev, NULL); + zram->backing_dev = NULL; + zram->bdev = NULL; + zram->disk->fops = &zram_devops; + kvfree(zram->bitmap); + zram->bitmap = NULL; +} + +static ssize_t backing_dev_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct file *file; + struct zram *zram = dev_to_zram(dev); + char *p; + ssize_t ret; + + down_read(&zram->init_lock); + file = zram->backing_dev; + if (!file) { + memcpy(buf, "none\n", 5); + up_read(&zram->init_lock); + return 5; + } + + p = file_path(file, buf, PAGE_SIZE - 1); + if (IS_ERR(p)) { + ret = PTR_ERR(p); + goto out; + } + + ret = strlen(p); + memmove(buf, p, ret); + buf[ret++] = '\n'; +out: + up_read(&zram->init_lock); + return ret; +} + +static ssize_t backing_dev_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + char *file_name; + size_t sz; + struct file *backing_dev = NULL; + struct inode *inode; + struct address_space *mapping; + unsigned int bitmap_sz; + unsigned long nr_pages, *bitmap = NULL; + struct block_device *bdev = NULL; + int err; + struct zram *zram = dev_to_zram(dev); + + file_name = kmalloc(PATH_MAX, GFP_KERNEL); + if (!file_name) + return -ENOMEM; + + down_write(&zram->init_lock); + if (init_done(zram)) { + pr_info("Can't setup backing device for initialized device\n"); + err = -EBUSY; + goto out; + } + + strlcpy(file_name, buf, PATH_MAX); + /* ignore trailing newline */ + sz = strlen(file_name); + if (sz > 0 && file_name[sz - 1] == '\n') + file_name[sz - 1] = 0x00; + + backing_dev = filp_open_block(file_name, O_RDWR|O_LARGEFILE, 0); + if (IS_ERR(backing_dev)) { + err = PTR_ERR(backing_dev); + backing_dev = NULL; + goto out; + } + + mapping = backing_dev->f_mapping; + inode = mapping->host; + + /* Support only block device in this moment */ + if (!S_ISBLK(inode->i_mode)) { + err = -ENOTBLK; + goto out; + } + + bdev = blkdev_get_by_dev(inode->i_rdev, + FMODE_READ | FMODE_WRITE | FMODE_EXCL, zram); + if (IS_ERR(bdev)) { + err = PTR_ERR(bdev); + bdev = NULL; + goto out; + } + + nr_pages = i_size_read(inode) >> PAGE_SHIFT; + bitmap_sz = BITS_TO_LONGS(nr_pages) * sizeof(long); + bitmap = kvzalloc(bitmap_sz, GFP_KERNEL); + if (!bitmap) { + err = -ENOMEM; + goto out; + } + + reset_bdev(zram); + + zram->bdev = bdev; + zram->backing_dev = backing_dev; + zram->bitmap = bitmap; + zram->nr_pages = nr_pages; + /* + * With writeback feature, zram does asynchronous IO so it's no longer + * synchronous device so let's remove synchronous io flag. Othewise, + * upper layer(e.g., swap) could wait IO completion rather than + * (submit and return), which will cause system sluggish. + * Furthermore, when the IO function returns(e.g., swap_readpage), + * upper layer expects IO was done so it could deallocate the page + * freely but in fact, IO is going on so finally could cause + * use-after-free when the IO is really done. + */ + zram->disk->fops = &zram_wb_devops; + up_write(&zram->init_lock); + + pr_info("setup backing device %s\n", file_name); + kfree(file_name); + + return len; +out: + kvfree(bitmap); + + if (bdev) + blkdev_put(bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL); + + if (backing_dev) + filp_close(backing_dev, NULL); + + up_write(&zram->init_lock); + + kfree(file_name); + + return err; +} + +static unsigned long alloc_block_bdev(struct zram *zram) +{ + unsigned long blk_idx = 1; +retry: + /* skip 0 bit to confuse zram.handle = 0 */ + blk_idx = find_next_zero_bit(zram->bitmap, zram->nr_pages, blk_idx); + if (blk_idx == zram->nr_pages) + return 0; + + if (test_and_set_bit(blk_idx, zram->bitmap)) + goto retry; + + atomic64_inc(&zram->stats.bd_count); + return blk_idx; +} + +static void free_block_bdev(struct zram *zram, unsigned long blk_idx) +{ + int was_set; + + was_set = test_and_clear_bit(blk_idx, zram->bitmap); + WARN_ON_ONCE(!was_set); + atomic64_dec(&zram->stats.bd_count); +} + +static void zram_page_end_io(struct bio *bio) +{ + struct page *page = bio_first_page_all(bio); + + page_endio(page, op_is_write(bio_op(bio)), + blk_status_to_errno(bio->bi_status)); + bio_put(bio); +} + +/* + * Returns 1 if the submission is successful. + */ +static int read_from_bdev_async(struct zram *zram, struct bio_vec *bvec, + unsigned long entry, struct bio *parent) +{ + struct bio *bio; + + bio = bio_alloc(GFP_ATOMIC, 1); + if (!bio) + return -ENOMEM; + + bio->bi_iter.bi_sector = entry * (PAGE_SIZE >> 9); + bio_set_dev(bio, zram->bdev); + if (!bio_add_page(bio, bvec->bv_page, bvec->bv_len, bvec->bv_offset)) { + bio_put(bio); + return -EIO; + } + + if (!parent) { + bio->bi_opf = REQ_OP_READ; + bio->bi_end_io = zram_page_end_io; + } else { + bio->bi_opf = parent->bi_opf; + bio_chain(bio, parent); + } + + submit_bio(bio); + return 1; +} + +#define PAGE_WB_SIG "page_index=" + +#define PAGE_WRITEBACK 0 +#define HUGE_WRITEBACK 1 +#define IDLE_WRITEBACK 2 + + +static ssize_t writeback_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + unsigned long nr_pages = zram->disksize >> PAGE_SHIFT; + unsigned long index = 0; + struct bio bio; + struct bio_vec bio_vec; + struct page *page; + ssize_t ret = len; + int mode, err; + unsigned long blk_idx = 0; + + if (sysfs_streq(buf, "idle")) + mode = IDLE_WRITEBACK; + else if (sysfs_streq(buf, "huge")) + mode = HUGE_WRITEBACK; + else { + if (strncmp(buf, PAGE_WB_SIG, sizeof(PAGE_WB_SIG) - 1)) + return -EINVAL; + + if (kstrtol(buf + sizeof(PAGE_WB_SIG) - 1, 10, &index) || + index >= nr_pages) + return -EINVAL; + + nr_pages = 1; + mode = PAGE_WRITEBACK; + } + + down_read(&zram->init_lock); + if (!init_done(zram)) { + ret = -EINVAL; + goto release_init_lock; + } + + if (!zram->backing_dev) { + ret = -ENODEV; + goto release_init_lock; + } + + page = alloc_page(GFP_KERNEL); + if (!page) { + ret = -ENOMEM; + goto release_init_lock; + } + + for (; nr_pages != 0; index++, nr_pages--) { + struct bio_vec bvec; + + bvec.bv_page = page; + bvec.bv_len = PAGE_SIZE; + bvec.bv_offset = 0; + + spin_lock(&zram->wb_limit_lock); + if (zram->wb_limit_enable && !zram->bd_wb_limit) { + spin_unlock(&zram->wb_limit_lock); + ret = -EIO; + break; + } + spin_unlock(&zram->wb_limit_lock); + + if (!blk_idx) { + blk_idx = alloc_block_bdev(zram); + if (!blk_idx) { + ret = -ENOSPC; + break; + } + } + + zram_slot_lock(zram, index); + if (!zram_allocated(zram, index)) + goto next; + + if (zram_test_flag(zram, index, ZRAM_WB) || + zram_test_flag(zram, index, ZRAM_SAME) || + zram_test_flag(zram, index, ZRAM_UNDER_WB)) + goto next; + + if (mode == IDLE_WRITEBACK && + !zram_test_flag(zram, index, ZRAM_IDLE)) + goto next; + if (mode == HUGE_WRITEBACK && + !zram_test_flag(zram, index, ZRAM_HUGE)) + goto next; + /* + * Clearing ZRAM_UNDER_WB is duty of caller. + * IOW, zram_free_page never clear it. + */ + zram_set_flag(zram, index, ZRAM_UNDER_WB); + /* Need for hugepage writeback racing */ + zram_set_flag(zram, index, ZRAM_IDLE); + zram_slot_unlock(zram, index); + if (zram_bvec_read(zram, &bvec, index, 0, NULL)) { + zram_slot_lock(zram, index); + zram_clear_flag(zram, index, ZRAM_UNDER_WB); + zram_clear_flag(zram, index, ZRAM_IDLE); + zram_slot_unlock(zram, index); + continue; + } + + bio_init(&bio, &bio_vec, 1); + bio_set_dev(&bio, zram->bdev); + bio.bi_iter.bi_sector = blk_idx * (PAGE_SIZE >> 9); + bio.bi_opf = REQ_OP_WRITE | REQ_SYNC; + + bio_add_page(&bio, bvec.bv_page, bvec.bv_len, + bvec.bv_offset); + /* + * XXX: A single page IO would be inefficient for write + * but it would be not bad as starter. + */ + err = submit_bio_wait(&bio); + if (err) { + zram_slot_lock(zram, index); + zram_clear_flag(zram, index, ZRAM_UNDER_WB); + zram_clear_flag(zram, index, ZRAM_IDLE); + zram_slot_unlock(zram, index); + /* + * Return last IO error unless every IO were + * not suceeded. + */ + ret = err; + continue; + } + + atomic64_inc(&zram->stats.bd_writes); + /* + * We released zram_slot_lock so need to check if the slot was + * changed. If there is freeing for the slot, we can catch it + * easily by zram_allocated. + * A subtle case is the slot is freed/reallocated/marked as + * ZRAM_IDLE again. To close the race, idle_store doesn't + * mark ZRAM_IDLE once it found the slot was ZRAM_UNDER_WB. + * Thus, we could close the race by checking ZRAM_IDLE bit. + */ + zram_slot_lock(zram, index); + if (!zram_allocated(zram, index) || + !zram_test_flag(zram, index, ZRAM_IDLE)) { + zram_clear_flag(zram, index, ZRAM_UNDER_WB); + zram_clear_flag(zram, index, ZRAM_IDLE); + goto next; + } + + zram_free_page(zram, index); + zram_clear_flag(zram, index, ZRAM_UNDER_WB); + zram_set_flag(zram, index, ZRAM_WB); + zram_set_element(zram, index, blk_idx); + blk_idx = 0; + atomic64_inc(&zram->stats.pages_stored); + spin_lock(&zram->wb_limit_lock); + if (zram->wb_limit_enable && zram->bd_wb_limit > 0) + zram->bd_wb_limit -= 1UL << (PAGE_SHIFT - 12); + spin_unlock(&zram->wb_limit_lock); +next: + zram_slot_unlock(zram, index); + } + + if (blk_idx) + free_block_bdev(zram, blk_idx); + __free_page(page); +release_init_lock: + up_read(&zram->init_lock); + + return ret; +} + +struct zram_work { + struct work_struct work; + struct zram *zram; + unsigned long entry; + struct bio *bio; + struct bio_vec bvec; +}; + +#if PAGE_SIZE != 4096 +static void zram_sync_read(struct work_struct *work) +{ + struct zram_work *zw = container_of(work, struct zram_work, work); + struct zram *zram = zw->zram; + unsigned long entry = zw->entry; + struct bio *bio = zw->bio; + + read_from_bdev_async(zram, &zw->bvec, entry, bio); +} + +/* + * Block layer want one ->submit_bio to be active at a time, so if we use + * chained IO with parent IO in same context, it's a deadlock. To avoid that, + * use a worker thread context. + */ +static int read_from_bdev_sync(struct zram *zram, struct bio_vec *bvec, + unsigned long entry, struct bio *bio) +{ + struct zram_work work; + + work.bvec = *bvec; + work.zram = zram; + work.entry = entry; + work.bio = bio; + + INIT_WORK_ONSTACK(&work.work, zram_sync_read); + queue_work(system_unbound_wq, &work.work); + flush_work(&work.work); + destroy_work_on_stack(&work.work); + + return 1; +} +#else +static int read_from_bdev_sync(struct zram *zram, struct bio_vec *bvec, + unsigned long entry, struct bio *bio) +{ + WARN_ON(1); + return -EIO; +} +#endif + +static int read_from_bdev(struct zram *zram, struct bio_vec *bvec, + unsigned long entry, struct bio *parent, bool sync) +{ + atomic64_inc(&zram->stats.bd_reads); + if (sync) + return read_from_bdev_sync(zram, bvec, entry, parent); + else + return read_from_bdev_async(zram, bvec, entry, parent); +} +#else +#ifdef CONFIG_HYBRIDSWAP_CORE +static void reset_bdev(struct zram *zram) +{ + struct block_device *bdev; + + if (!zram->backing_dev) + return; + + bdev = zram->bdev; + blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL); + /* hope filp_close flush all of IO */ + filp_close(zram->backing_dev, NULL); + zram->backing_dev = NULL; + zram->bdev = NULL; +} +#else +static inline void reset_bdev(struct zram *zram) {}; +#endif +static int read_from_bdev(struct zram *zram, struct bio_vec *bvec, + unsigned long entry, struct bio *parent, bool sync) +{ + return -EIO; +} + +static void free_block_bdev(struct zram *zram, unsigned long blk_idx) {}; +#endif + +#ifdef CONFIG_HYBRIDSWAP_ZRAM_MEMORY_TRACKING + +static struct dentry *zram_debugfs_root; + +static void zram_debugfs_create(void) +{ + zram_debugfs_root = debugfs_create_dir("zram", NULL); +} + +static void zram_debugfs_destroy(void) +{ + debugfs_remove_recursive(zram_debugfs_root); +} + +static void zram_accessed(struct zram *zram, u32 index) +{ + zram_clear_flag(zram, index, ZRAM_IDLE); + zram->table[index].ac_time = ktime_get_boottime(); +} + +static ssize_t read_block_state(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + char *kbuf; + ssize_t index, written = 0; + struct zram *zram = file->private_data; + unsigned long nr_pages = zram->disksize >> PAGE_SHIFT; + struct timespec64 ts; + + kbuf = kvmalloc(count, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + down_read(&zram->init_lock); + if (!init_done(zram)) { + up_read(&zram->init_lock); + kvfree(kbuf); + return -EINVAL; + } + + for (index = *ppos; index < nr_pages; index++) { + int copied; + + zram_slot_lock(zram, index); + if (!zram_allocated(zram, index)) + goto next; + + ts = ktime_to_timespec64(zram->table[index].ac_time); + copied = snprintf(kbuf + written, count, + "%12zd %12lld.%06lu %c%c%c%c\n", + index, (s64)ts.tv_sec, + ts.tv_nsec / NSEC_PER_USEC, + zram_test_flag(zram, index, ZRAM_SAME) ? 's' : '.', + zram_test_flag(zram, index, ZRAM_WB) ? 'w' : '.', + zram_test_flag(zram, index, ZRAM_HUGE) ? 'h' : '.', + zram_test_flag(zram, index, ZRAM_IDLE) ? 'i' : '.'); + + if (count < copied) { + zram_slot_unlock(zram, index); + break; + } + written += copied; + count -= copied; +next: + zram_slot_unlock(zram, index); + *ppos += 1; + } + + up_read(&zram->init_lock); + if (copy_to_user(buf, kbuf, written)) + written = -EFAULT; + kvfree(kbuf); + + return written; +} + +static const struct file_operations proc_zram_block_state_op = { + .open = simple_open, + .read = read_block_state, + .llseek = default_llseek, +}; + +static void zram_debugfs_register(struct zram *zram) +{ + if (!zram_debugfs_root) + return; + + zram->debugfs_dir = debugfs_create_dir(zram->disk->disk_name, + zram_debugfs_root); + debugfs_create_file("block_state", 0400, zram->debugfs_dir, + zram, &proc_zram_block_state_op); +} + +static void zram_debugfs_unregister(struct zram *zram) +{ + debugfs_remove_recursive(zram->debugfs_dir); +} +#else +static void zram_debugfs_create(void) {}; +static void zram_debugfs_destroy(void) {}; +static void zram_accessed(struct zram *zram, u32 index) +{ + zram_clear_flag(zram, index, ZRAM_IDLE); +}; +static void zram_debugfs_register(struct zram *zram) {}; +static void zram_debugfs_unregister(struct zram *zram) {}; +#endif + +/* + * We switched to per-cpu streams and this attr is not needed anymore. + * However, we will keep it around for some time, because: + * a) we may revert per-cpu streams in the future + * b) it's visible to user space and we need to follow our 2 years + * retirement rule; but we already have a number of 'soon to be + * altered' attrs, so max_comp_streams need to wait for the next + * layoff cycle. + */ +static ssize_t max_comp_streams_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", num_online_cpus()); +} + +static ssize_t max_comp_streams_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + return len; +} + +static ssize_t comp_algorithm_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t sz; + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + sz = zcomp_available_show(zram->compressor, buf); + up_read(&zram->init_lock); + + return sz; +} + +static ssize_t comp_algorithm_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + char compressor[ARRAY_SIZE(zram->compressor)]; + size_t sz; + + strlcpy(compressor, buf, sizeof(compressor)); + /* ignore trailing newline */ + sz = strlen(compressor); + if (sz > 0 && compressor[sz - 1] == '\n') + compressor[sz - 1] = 0x00; + + if (!zcomp_available_algorithm(compressor)) + return -EINVAL; + + down_write(&zram->init_lock); + if (init_done(zram)) { + up_write(&zram->init_lock); + pr_info("Can't change algorithm for initialized device\n"); + return -EBUSY; + } + + strcpy(zram->compressor, compressor); + up_write(&zram->init_lock); + return len; +} + +static ssize_t compact_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + if (!init_done(zram)) { + up_read(&zram->init_lock); + return -EINVAL; + } + + zs_compact(zram->mem_pool); + up_read(&zram->init_lock); + + return len; +} + +static ssize_t io_stat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + ssize_t ret; + + down_read(&zram->init_lock); + ret = scnprintf(buf, PAGE_SIZE, + "%8llu %8llu %8llu %8llu\n", + (u64)atomic64_read(&zram->stats.failed_reads), + (u64)atomic64_read(&zram->stats.failed_writes), + (u64)atomic64_read(&zram->stats.invalid_io), + (u64)atomic64_read(&zram->stats.notify_free)); + up_read(&zram->init_lock); + + return ret; +} + +static ssize_t mm_stat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + struct zs_pool_stats pool_stats; + u64 orig_size, mem_used = 0; + long max_used; + ssize_t ret; + + memset(&pool_stats, 0x00, sizeof(struct zs_pool_stats)); + + down_read(&zram->init_lock); + if (init_done(zram)) { + mem_used = zs_get_total_pages(zram->mem_pool); + zs_pool_stats(zram->mem_pool, &pool_stats); + } + + orig_size = atomic64_read(&zram->stats.pages_stored); + max_used = atomic_long_read(&zram->stats.max_used_pages); + + ret = scnprintf(buf, PAGE_SIZE, + "%8llu %8llu %8llu %8lu %8ld %8llu %8lu %8llu %8llu\n", + orig_size << PAGE_SHIFT, + (u64)atomic64_read(&zram->stats.compr_data_size), + mem_used << PAGE_SHIFT, + zram->limit_pages << PAGE_SHIFT, + max_used << PAGE_SHIFT, + (u64)atomic64_read(&zram->stats.same_pages), + atomic_long_read(&pool_stats.pages_compacted), + (u64)atomic64_read(&zram->stats.huge_pages), + (u64)atomic64_read(&zram->stats.huge_pages_since)); + up_read(&zram->init_lock); + + return ret; +} + +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +#define FOUR_K(x) ((x) * (1 << (PAGE_SHIFT - 12))) +static ssize_t bd_stat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + ssize_t ret; + + down_read(&zram->init_lock); + ret = scnprintf(buf, PAGE_SIZE, + "%8llu %8llu %8llu\n", + FOUR_K((u64)atomic64_read(&zram->stats.bd_count)), + FOUR_K((u64)atomic64_read(&zram->stats.bd_reads)), + FOUR_K((u64)atomic64_read(&zram->stats.bd_writes))); + up_read(&zram->init_lock); + + return ret; +} +#endif + +static ssize_t debug_stat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int version = 1; + struct zram *zram = dev_to_zram(dev); + ssize_t ret; + + down_read(&zram->init_lock); + ret = scnprintf(buf, PAGE_SIZE, + "version: %d\n%8llu %8llu\n", + version, + (u64)atomic64_read(&zram->stats.writestall), + (u64)atomic64_read(&zram->stats.miss_free)); + up_read(&zram->init_lock); + + return ret; +} + +static DEVICE_ATTR_RO(io_stat); +static DEVICE_ATTR_RO(mm_stat); +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +static DEVICE_ATTR_RO(bd_stat); +#endif +static DEVICE_ATTR_RO(debug_stat); + +static void zram_meta_free(struct zram *zram, u64 disksize) +{ + size_t num_pages = disksize >> PAGE_SHIFT; + size_t index; + + /* Free all pages that are still in this zram device */ + for (index = 0; index < num_pages; index++) + zram_free_page(zram, index); + + zs_destroy_pool(zram->mem_pool); + vfree(zram->table); +} + +static bool zram_meta_alloc(struct zram *zram, u64 disksize) +{ + size_t num_pages; + + num_pages = disksize >> PAGE_SHIFT; + zram->table = vzalloc(array_size(num_pages, sizeof(*zram->table))); + if (!zram->table) + return false; + + zram->mem_pool = zs_create_pool(zram->disk->disk_name); + if (!zram->mem_pool) { + vfree(zram->table); + return false; + } + + if (!huge_class_size) + huge_class_size = zs_huge_class_size(zram->mem_pool); + return true; +} + +/* + * To protect concurrent access to the same index entry, + * caller should hold this table index entry's bit_spinlock to + * indicate this index entry is accessing. + */ +static void zram_free_page(struct zram *zram, size_t index) +{ + unsigned long handle; + +#ifdef CONFIG_HYBRIDSWAP_ZRAM_MEMORY_TRACKING + zram->table[index].ac_time = 0; +#endif + if (zram_test_flag(zram, index, ZRAM_IDLE)) + zram_clear_flag(zram, index, ZRAM_IDLE); + + if (zram_test_flag(zram, index, ZRAM_HUGE)) { + zram_clear_flag(zram, index, ZRAM_HUGE); + atomic64_dec(&zram->stats.huge_pages); + } + +#ifdef CONFIG_HYBRIDSWAP_CORE + hybridswap_untrack(zram, index); +#endif + + if (zram_test_flag(zram, index, ZRAM_WB)) { + zram_clear_flag(zram, index, ZRAM_WB); + free_block_bdev(zram, zram_get_element(zram, index)); + atomic64_dec(&zram->stats.pages_stored); + goto out; + } + + /* + * No memory is allocated for same element filled pages. + * Simply clear same page flag. + */ + if (zram_test_flag(zram, index, ZRAM_SAME)) { + zram_clear_flag(zram, index, ZRAM_SAME); + atomic64_dec(&zram->stats.same_pages); + atomic64_dec(&zram->stats.pages_stored); + goto out; + } + + handle = zram_get_handle(zram, index); + if (!handle) + return; + + zs_free(zram->mem_pool, handle); + + atomic64_sub(zram_get_obj_size(zram, index), + &zram->stats.compr_data_size); + atomic64_dec(&zram->stats.pages_stored); + +out: + zram_set_handle(zram, index, 0); + zram_set_obj_size(zram, index, 0); + WARN_ON_ONCE(zram->table[index].flags & + ~(1UL << ZRAM_LOCK | 1UL << ZRAM_UNDER_WB)); +} + +static int __zram_bvec_read(struct zram *zram, struct page *page, u32 index, + struct bio *bio, bool partial_io) +{ + struct zcomp_strm *zstrm; + unsigned long handle; + unsigned int size; + void *src, *dst; + int ret; + + zram_slot_lock(zram, index); + +#ifdef CONFIG_HYBRIDSWAP_CORE + if (likely(!bio)) { + ret = hybridswap_page_fault(zram, index); + if (unlikely(ret)) { + pr_err("search in hybridswap failed! err=%d, page=%u\n", + ret, index); + zram_slot_unlock(zram, index); + return ret; + } + } +#endif + + if (zram_test_flag(zram, index, ZRAM_WB)) { + struct bio_vec bvec; + + zram_slot_unlock(zram, index); + + bvec.bv_page = page; + bvec.bv_len = PAGE_SIZE; + bvec.bv_offset = 0; + return read_from_bdev(zram, &bvec, + zram_get_element(zram, index), + bio, partial_io); + } + + handle = zram_get_handle(zram, index); + if (!handle || zram_test_flag(zram, index, ZRAM_SAME)) { + unsigned long value; + void *mem; + + value = handle ? zram_get_element(zram, index) : 0; + mem = kmap_atomic(page); + zram_fill_page(mem, PAGE_SIZE, value); + kunmap_atomic(mem); + zram_slot_unlock(zram, index); + return 0; + } + + size = zram_get_obj_size(zram, index); + + if (size != PAGE_SIZE) + zstrm = zcomp_stream_get(zram->comp); + + src = zs_map_object(zram->mem_pool, handle, ZS_MM_RO); + if (size == PAGE_SIZE) { + dst = kmap_atomic(page); + memcpy(dst, src, PAGE_SIZE); + kunmap_atomic(dst); + ret = 0; + } else { + dst = kmap_atomic(page); + ret = zcomp_decompress(zstrm, src, size, dst); + kunmap_atomic(dst); + zcomp_stream_put(zram->comp); + } + zs_unmap_object(zram->mem_pool, handle); + zram_slot_unlock(zram, index); + + /* Should NEVER happen. Return bio error if it does. */ + if (WARN_ON(ret)) + pr_err("Decompression failed! err=%d, page=%u\n", ret, index); + + return ret; +} + +static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, + u32 index, int offset, struct bio *bio) +{ + int ret; + struct page *page; + + page = bvec->bv_page; + if (is_partial_io(bvec)) { + /* Use a temporary buffer to decompress the page */ + page = alloc_page(GFP_NOIO|__GFP_HIGHMEM); + if (!page) + return -ENOMEM; + } + + ret = __zram_bvec_read(zram, page, index, bio, is_partial_io(bvec)); + if (unlikely(ret)) + goto out; + + if (is_partial_io(bvec)) { + void *dst = kmap_atomic(bvec->bv_page); + void *src = kmap_atomic(page); + + memcpy(dst + bvec->bv_offset, src + offset, bvec->bv_len); + kunmap_atomic(src); + kunmap_atomic(dst); + } +out: + if (is_partial_io(bvec)) + __free_page(page); + + return ret; +} + +static int __zram_bvec_write(struct zram *zram, struct bio_vec *bvec, + u32 index, struct bio *bio) +{ + int ret = 0; + unsigned long alloced_pages; + unsigned long handle = 0; + unsigned int comp_len = 0; + void *src, *dst, *mem; + struct zcomp_strm *zstrm; + struct page *page = bvec->bv_page; + unsigned long element = 0; + enum zram_pageflags flags = 0; + + mem = kmap_atomic(page); + if (page_same_filled(mem, &element)) { + kunmap_atomic(mem); + /* Free memory associated with this sector now. */ + flags = ZRAM_SAME; + atomic64_inc(&zram->stats.same_pages); + goto out; + } + kunmap_atomic(mem); + +compress_again: + zstrm = zcomp_stream_get(zram->comp); + src = kmap_atomic(page); + ret = zcomp_compress(zstrm, src, &comp_len); + kunmap_atomic(src); + + if (unlikely(ret)) { + zcomp_stream_put(zram->comp); + pr_err("Compression failed! err=%d\n", ret); + zs_free(zram->mem_pool, handle); + return ret; + } + + if (comp_len >= huge_class_size) + comp_len = PAGE_SIZE; + /* + * handle allocation has 2 paths: + * a) fast path is executed with preemption disabled (for + * per-cpu streams) and has __GFP_DIRECT_RECLAIM bit clear, + * since we can't sleep; + * b) slow path enables preemption and attempts to allocate + * the page with __GFP_DIRECT_RECLAIM bit set. we have to + * put per-cpu compression stream and, thus, to re-do + * the compression once handle is allocated. + * + * if we have a 'non-null' handle here then we are coming + * from the slow path and handle has already been allocated. + */ + if (!handle) + handle = zs_malloc(zram->mem_pool, comp_len, + __GFP_KSWAPD_RECLAIM | + __GFP_NOWARN | + __GFP_HIGHMEM | + __GFP_MOVABLE | + __GFP_CMA); + if (!handle) { + zcomp_stream_put(zram->comp); + atomic64_inc(&zram->stats.writestall); + handle = zs_malloc(zram->mem_pool, comp_len, + GFP_NOIO | __GFP_HIGHMEM | + __GFP_MOVABLE | __GFP_CMA); + if (handle) + goto compress_again; + return -ENOMEM; + } + + alloced_pages = zs_get_total_pages(zram->mem_pool); + update_used_max(zram, alloced_pages); + + if (zram->limit_pages && alloced_pages > zram->limit_pages) { + zcomp_stream_put(zram->comp); + zs_free(zram->mem_pool, handle); + return -ENOMEM; + } + + dst = zs_map_object(zram->mem_pool, handle, ZS_MM_WO); + + src = zstrm->buffer; + if (comp_len == PAGE_SIZE) + src = kmap_atomic(page); + memcpy(dst, src, comp_len); + if (comp_len == PAGE_SIZE) + kunmap_atomic(src); + + zcomp_stream_put(zram->comp); + zs_unmap_object(zram->mem_pool, handle); + atomic64_add(comp_len, &zram->stats.compr_data_size); +out: + /* + * Free memory associated with this sector + * before overwriting unused sectors. + */ + zram_slot_lock(zram, index); + zram_free_page(zram, index); + + if (comp_len == PAGE_SIZE) { + zram_set_flag(zram, index, ZRAM_HUGE); + atomic64_inc(&zram->stats.huge_pages); + atomic64_inc(&zram->stats.huge_pages_since); + } + + if (flags) { + zram_set_flag(zram, index, flags); + zram_set_element(zram, index, element); + } else { + zram_set_handle(zram, index, handle); + zram_set_obj_size(zram, index, comp_len); + } + +#ifdef CONFIG_HYBRIDSWAP_CORE + hybridswap_record(zram, index, page_memcg(page)); +#endif + zram_slot_unlock(zram, index); + + /* Update stats */ + atomic64_inc(&zram->stats.pages_stored); + return ret; +} + +static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, + u32 index, int offset, struct bio *bio) +{ + int ret; + struct page *page = NULL; + void *src; + struct bio_vec vec; + + vec = *bvec; + if (is_partial_io(bvec)) { + void *dst; + /* + * This is a partial IO. We need to read the full page + * before to write the changes. + */ + page = alloc_page(GFP_NOIO|__GFP_HIGHMEM); + if (!page) + return -ENOMEM; + + ret = __zram_bvec_read(zram, page, index, bio, true); + if (ret) + goto out; + + src = kmap_atomic(bvec->bv_page); + dst = kmap_atomic(page); + memcpy(dst + offset, src + bvec->bv_offset, bvec->bv_len); + kunmap_atomic(dst); + kunmap_atomic(src); + + vec.bv_page = page; + vec.bv_len = PAGE_SIZE; + vec.bv_offset = 0; + } + + ret = __zram_bvec_write(zram, &vec, index, bio); +out: + if (is_partial_io(bvec)) + __free_page(page); + return ret; +} + +/* + * zram_bio_discard - handler on discard request + * @index: physical block index in PAGE_SIZE units + * @offset: byte offset within physical block + */ +static void zram_bio_discard(struct zram *zram, u32 index, + int offset, struct bio *bio) +{ + size_t n = bio->bi_iter.bi_size; + + /* + * zram manages data in physical block size units. Because logical block + * size isn't identical with physical block size on some arch, we + * could get a discard request pointing to a specific offset within a + * certain physical block. Although we can handle this request by + * reading that physiclal block and decompressing and partially zeroing + * and re-compressing and then re-storing it, this isn't reasonable + * because our intent with a discard request is to save memory. So + * skipping this logical block is appropriate here. + */ + if (offset) { + if (n <= (PAGE_SIZE - offset)) + return; + + n -= (PAGE_SIZE - offset); + index++; + } + + while (n >= PAGE_SIZE) { + zram_slot_lock(zram, index); + zram_free_page(zram, index); + zram_slot_unlock(zram, index); + atomic64_inc(&zram->stats.notify_free); + index++; + n -= PAGE_SIZE; + } +} + +/* + * Returns errno if it has some problem. Otherwise return 0 or 1. + * Returns 0 if IO request was done synchronously + * Returns 1 if IO request was successfully submitted. + */ +static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, + int offset, unsigned int op, struct bio *bio) +{ + int ret; + + if (!op_is_write(op)) { + atomic64_inc(&zram->stats.num_reads); + ret = zram_bvec_read(zram, bvec, index, offset, bio); + flush_dcache_page(bvec->bv_page); + } else { + atomic64_inc(&zram->stats.num_writes); + ret = zram_bvec_write(zram, bvec, index, offset, bio); + } + + zram_slot_lock(zram, index); + zram_accessed(zram, index); + zram_slot_unlock(zram, index); + + if (unlikely(ret < 0)) { + if (!op_is_write(op)) + atomic64_inc(&zram->stats.failed_reads); + else + atomic64_inc(&zram->stats.failed_writes); + } + + return ret; +} + +static void __zram_make_request(struct zram *zram, struct bio *bio) +{ + int offset; + u32 index; + struct bio_vec bvec; + struct bvec_iter iter; + unsigned long start_time; + + index = bio->bi_iter.bi_sector >> SECTORS_PER_PAGE_SHIFT; + offset = (bio->bi_iter.bi_sector & + (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT; + + switch (bio_op(bio)) { + case REQ_OP_DISCARD: + case REQ_OP_WRITE_ZEROES: + zram_bio_discard(zram, index, offset, bio); + bio_endio(bio); + return; + default: + break; + } + + start_time = bio_start_io_acct(bio); + bio_for_each_segment(bvec, bio, iter) { + struct bio_vec bv = bvec; + unsigned int unwritten = bvec.bv_len; + + do { + bv.bv_len = min_t(unsigned int, PAGE_SIZE - offset, + unwritten); + if (zram_bvec_rw(zram, &bv, index, offset, + bio_op(bio), bio) < 0) { + bio->bi_status = BLK_STS_IOERR; + break; + } + + bv.bv_offset += bv.bv_len; + unwritten -= bv.bv_len; + + update_position(&index, &offset, &bv); + } while (unwritten); + } + bio_end_io_acct(bio, start_time); + bio_endio(bio); +} + +/* + * Handler function for all zram I/O requests. + */ +static blk_qc_t zram_submit_bio(struct bio *bio) +{ + struct zram *zram = bio->bi_bdev->bd_disk->private_data; + + if (!valid_io_request(zram, bio->bi_iter.bi_sector, + bio->bi_iter.bi_size)) { + atomic64_inc(&zram->stats.invalid_io); + goto error; + } + + __zram_make_request(zram, bio); + return BLK_QC_T_NONE; + +error: + bio_io_error(bio); + return BLK_QC_T_NONE; +} + +static void zram_slot_free_notify(struct block_device *bdev, + unsigned long index) +{ + struct zram *zram; + + zram = bdev->bd_disk->private_data; + + atomic64_inc(&zram->stats.notify_free); + if (!zram_slot_trylock(zram, index)) { + atomic64_inc(&zram->stats.miss_free); + return; + } + +#ifdef CONFIG_HYBRIDSWAP_CORE + if (!hybridswap_delete(zram, index)) { + zram_slot_unlock(zram, index); + atomic64_inc(&zram->stats.miss_free); + return; + } +#endif + zram_free_page(zram, index); + zram_slot_unlock(zram, index); +} + +static int zram_rw_page(struct block_device *bdev, sector_t sector, + struct page *page, unsigned int op) +{ + int offset, ret; + u32 index; + struct zram *zram; + struct bio_vec bv; + unsigned long start_time; + + if (PageTransHuge(page)) + return -ENOTSUPP; + zram = bdev->bd_disk->private_data; + + if (!valid_io_request(zram, sector, PAGE_SIZE)) { + atomic64_inc(&zram->stats.invalid_io); + ret = -EINVAL; + goto out; + } + + index = sector >> SECTORS_PER_PAGE_SHIFT; + offset = (sector & (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT; + + bv.bv_page = page; + bv.bv_len = PAGE_SIZE; + bv.bv_offset = 0; + + start_time = disk_start_io_acct(bdev->bd_disk, SECTORS_PER_PAGE, op); + ret = zram_bvec_rw(zram, &bv, index, offset, op, NULL); + disk_end_io_acct(bdev->bd_disk, op, start_time); +out: + /* + * If I/O fails, just return error(ie, non-zero) without + * calling page_endio. + * It causes resubmit the I/O with bio request by upper functions + * of rw_page(e.g., swap_readpage, __swap_writepage) and + * bio->bi_end_io does things to handle the error + * (e.g., SetPageError, set_page_dirty and extra works). + */ + if (unlikely(ret < 0)) + return ret; + + switch (ret) { + case 0: + page_endio(page, op_is_write(op), 0); + break; + case 1: + ret = 0; + break; + default: + WARN_ON(1); + } + return ret; +} + +static void zram_reset_device(struct zram *zram) +{ + struct zcomp *comp; + u64 disksize; + + down_write(&zram->init_lock); + + zram->limit_pages = 0; + + if (!init_done(zram)) { + up_write(&zram->init_lock); + return; + } + + comp = zram->comp; + disksize = zram->disksize; + zram->disksize = 0; + + set_capacity_and_notify(zram->disk, 0); + part_stat_set_all(zram->disk->part0, 0); + + up_write(&zram->init_lock); + /* I/O operation under all of CPU are done so let's free */ + zram_meta_free(zram, disksize); + memset(&zram->stats, 0, sizeof(zram->stats)); + zcomp_destroy(comp); + reset_bdev(zram); +} + +static ssize_t disksize_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + u64 disksize; + struct zcomp *comp; + struct zram *zram = dev_to_zram(dev); + int err; + + disksize = memparse(buf, NULL); + if (!disksize) + return -EINVAL; + + down_write(&zram->init_lock); + if (init_done(zram)) { + pr_info("Cannot change disksize for initialized device\n"); + err = -EBUSY; + goto out_unlock; + } + + disksize = PAGE_ALIGN(disksize); + if (!zram_meta_alloc(zram, disksize)) { + err = -ENOMEM; + goto out_unlock; + } + + comp = zcomp_create(zram->compressor); + if (IS_ERR(comp)) { + pr_err("Cannot initialise %s compressing backend\n", + zram->compressor); + err = PTR_ERR(comp); + goto out_free_meta; + } + + zram->comp = comp; + zram->disksize = disksize; + set_capacity_and_notify(zram->disk, zram->disksize >> SECTOR_SHIFT); + up_write(&zram->init_lock); + + return len; + +out_free_meta: + zram_meta_free(zram, disksize); +out_unlock: + up_write(&zram->init_lock); + return err; +} + +static ssize_t reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int ret; + unsigned short do_reset; + struct zram *zram; + struct block_device *bdev; + + ret = kstrtou16(buf, 10, &do_reset); + if (ret) + return ret; + + if (!do_reset) + return -EINVAL; + + zram = dev_to_zram(dev); + bdev = zram->disk->part0; + + mutex_lock(&bdev->bd_disk->open_mutex); + /* Do not reset an active device or claimed device */ + if (bdev->bd_openers || zram->claim) { + mutex_unlock(&bdev->bd_disk->open_mutex); + return -EBUSY; + } + + /* From now on, anyone can't open /dev/zram[0-9] */ + zram->claim = true; + mutex_unlock(&bdev->bd_disk->open_mutex); + + /* Make sure all the pending I/O are finished */ + fsync_bdev(bdev); + zram_reset_device(zram); + + mutex_lock(&bdev->bd_disk->open_mutex); + zram->claim = false; + mutex_unlock(&bdev->bd_disk->open_mutex); + + return len; +} + +static int zram_open(struct block_device *bdev, fmode_t mode) +{ + int ret = 0; + struct zram *zram; + + WARN_ON(!mutex_is_locked(&bdev->bd_disk->open_mutex)); + + zram = bdev->bd_disk->private_data; + /* zram was claimed to reset so open request fails */ + if (zram->claim) + ret = -EBUSY; + + return ret; +} + +static const struct block_device_operations zram_devops = { + .open = zram_open, + .submit_bio = zram_submit_bio, + .swap_slot_free_notify = zram_slot_free_notify, + .rw_page = zram_rw_page, + .owner = THIS_MODULE +}; + +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +static const struct block_device_operations zram_wb_devops = { + .open = zram_open, + .submit_bio = zram_submit_bio, + .swap_slot_free_notify = zram_slot_free_notify, + .owner = THIS_MODULE +}; +#endif + +static DEVICE_ATTR_WO(compact); +static DEVICE_ATTR_RW(disksize); +static DEVICE_ATTR_RO(initstate); +static DEVICE_ATTR_WO(reset); +static DEVICE_ATTR_WO(mem_limit); +static DEVICE_ATTR_WO(mem_used_max); +static DEVICE_ATTR_WO(idle); +static DEVICE_ATTR_RW(max_comp_streams); +static DEVICE_ATTR_RW(comp_algorithm); +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +static DEVICE_ATTR_RW(backing_dev); +static DEVICE_ATTR_WO(writeback); +static DEVICE_ATTR_RW(writeback_limit); +static DEVICE_ATTR_RW(writeback_limit_enable); +#endif +#ifdef CONFIG_HYBRIDSWAP +static DEVICE_ATTR_RO(hybridswap_vmstat); +static DEVICE_ATTR_RW(hybridswap_loglevel); +static DEVICE_ATTR_RW(hybridswap_enable); +#endif +#ifdef CONFIG_HYBRIDSWAP_SWAPD +static DEVICE_ATTR_RW(hybridswap_swapd_pause); +#endif +#ifdef CONFIG_HYBRIDSWAP_CORE +static DEVICE_ATTR_RW(hybridswap_core_enable); +static DEVICE_ATTR_RW(hybridswap_loop_device); +static DEVICE_ATTR_RW(hybridswap_dev_life); +static DEVICE_ATTR_RW(hybridswap_quota_day); +static DEVICE_ATTR_RO(hybridswap_report); +static DEVICE_ATTR_RO(hybridswap_stat_snap); +static DEVICE_ATTR_RO(hybridswap_meminfo); +static DEVICE_ATTR_RW(hybridswap_zram_increase); +#endif + +static struct attribute *zram_disk_attrs[] = { + &dev_attr_disksize.attr, + &dev_attr_initstate.attr, + &dev_attr_reset.attr, + &dev_attr_compact.attr, + &dev_attr_mem_limit.attr, + &dev_attr_mem_used_max.attr, + &dev_attr_idle.attr, + &dev_attr_max_comp_streams.attr, + &dev_attr_comp_algorithm.attr, +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK + &dev_attr_backing_dev.attr, + &dev_attr_writeback.attr, + &dev_attr_writeback_limit.attr, + &dev_attr_writeback_limit_enable.attr, +#endif + &dev_attr_io_stat.attr, + &dev_attr_mm_stat.attr, +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK + &dev_attr_bd_stat.attr, +#endif + &dev_attr_debug_stat.attr, +#ifdef CONFIG_HYBRIDSWAP + &dev_attr_hybridswap_vmstat.attr, + &dev_attr_hybridswap_loglevel.attr, + &dev_attr_hybridswap_enable.attr, +#endif +#ifdef CONFIG_HYBRIDSWAP_SWAPD + &dev_attr_hybridswap_swapd_pause.attr, +#endif +#ifdef CONFIG_HYBRIDSWAP_CORE + &dev_attr_hybridswap_core_enable.attr, + &dev_attr_hybridswap_report.attr, + &dev_attr_hybridswap_meminfo.attr, + &dev_attr_hybridswap_stat_snap.attr, + &dev_attr_hybridswap_loop_device.attr, + &dev_attr_hybridswap_dev_life.attr, + &dev_attr_hybridswap_quota_day.attr, + &dev_attr_hybridswap_zram_increase.attr, +#endif + NULL, +}; + +static const struct attribute_group zram_disk_attr_group = { + .attrs = zram_disk_attrs, +}; + +static const struct attribute_group *zram_disk_attr_groups[] = { + &zram_disk_attr_group, + NULL, +}; + +/* + * Allocate and initialize new zram device. the function returns + * '>= 0' device_id upon success, and negative value otherwise. + */ +static int zram_add(void) +{ + struct zram *zram; + int ret, device_id; + + zram = kzalloc(sizeof(struct zram), GFP_KERNEL); + if (!zram) + return -ENOMEM; + + ret = idr_alloc(&zram_index_idr, zram, 0, 0, GFP_KERNEL); + if (ret < 0) + goto out_free_dev; + device_id = ret; + + init_rwsem(&zram->init_lock); +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK + spin_lock_init(&zram->wb_limit_lock); +#endif + + /* gendisk structure */ + zram->disk = blk_alloc_disk(NUMA_NO_NODE); + if (!zram->disk) { + pr_err("Error allocating disk structure for device %d\n", + device_id); + ret = -ENOMEM; + goto out_free_idr; + } + + zram->disk->major = zram_major; + zram->disk->first_minor = device_id; + zram->disk->minors = 1; + zram->disk->fops = &zram_devops; + zram->disk->private_data = zram; + snprintf(zram->disk->disk_name, 16, "zram%d", device_id); + + /* Actual capacity set using syfs (/sys/block/zram/disksize */ + set_capacity(zram->disk, 0); + /* zram devices sort of resembles non-rotational disks */ + blk_queue_flag_set(QUEUE_FLAG_NONROT, zram->disk->queue); + blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, zram->disk->queue); + + /* + * To ensure that we always get PAGE_SIZE aligned + * and n*PAGE_SIZED sized I/O requests. + */ + blk_queue_physical_block_size(zram->disk->queue, PAGE_SIZE); + blk_queue_logical_block_size(zram->disk->queue, + ZRAM_LOGICAL_BLOCK_SIZE); + blk_queue_io_min(zram->disk->queue, PAGE_SIZE); + blk_queue_io_opt(zram->disk->queue, PAGE_SIZE); + zram->disk->queue->limits.discard_granularity = PAGE_SIZE; + blk_queue_max_discard_sectors(zram->disk->queue, UINT_MAX); + blk_queue_flag_set(QUEUE_FLAG_DISCARD, zram->disk->queue); + + /* + * zram_bio_discard() will clear all logical blocks if logical block + * size is identical with physical block size(PAGE_SIZE). But if it is + * different, we will skip discarding some parts of logical blocks in + * the part of the request range which isn't aligned to physical block + * size. So we can't ensure that all discarded logical blocks are + * zeroed. + */ + if (ZRAM_LOGICAL_BLOCK_SIZE == PAGE_SIZE) + blk_queue_max_write_zeroes_sectors(zram->disk->queue, UINT_MAX); + + blk_queue_flag_set(QUEUE_FLAG_STABLE_WRITES, zram->disk->queue); + device_add_disk(NULL, zram->disk, zram_disk_attr_groups); + + strlcpy(zram->compressor, default_compressor, sizeof(zram->compressor)); + + zram_debugfs_register(zram); + pr_info("Added device: %s\n", zram->disk->disk_name); + return device_id; + +out_free_idr: + idr_remove(&zram_index_idr, device_id); +out_free_dev: + kfree(zram); + return ret; +} + +static int zram_remove(struct zram *zram) +{ + struct block_device *bdev = zram->disk->part0; + + mutex_lock(&bdev->bd_disk->open_mutex); + if (bdev->bd_openers || zram->claim) { + mutex_unlock(&bdev->bd_disk->open_mutex); + return -EBUSY; + } + + zram->claim = true; + mutex_unlock(&bdev->bd_disk->open_mutex); + + zram_debugfs_unregister(zram); + + /* Make sure all the pending I/O are finished */ + fsync_bdev(bdev); + zram_reset_device(zram); + + pr_info("Removed device: %s\n", zram->disk->disk_name); + + del_gendisk(zram->disk); + blk_cleanup_disk(zram->disk); + kfree(zram); + return 0; +} + +/* zram-control sysfs attributes */ + +/* + * NOTE: hot_add attribute is not the usual read-only sysfs attribute. In a + * sense that reading from this file does alter the state of your system -- it + * creates a new un-initialized zram device and returns back this device's + * device_id (or an error code if it fails to create a new device). + */ +static ssize_t hot_add_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + int ret; + + mutex_lock(&zram_index_mutex); + ret = zram_add(); + mutex_unlock(&zram_index_mutex); + + if (ret < 0) + return ret; + return scnprintf(buf, PAGE_SIZE, "%d\n", ret); +} +static struct class_attribute class_attr_hot_add = + __ATTR(hot_add, 0400, hot_add_show, NULL); + +static ssize_t hot_remove_store(struct class *class, + struct class_attribute *attr, + const char *buf, + size_t count) +{ + struct zram *zram; + int ret, dev_id; + + /* dev_id is gendisk->first_minor, which is `int' */ + ret = kstrtoint(buf, 10, &dev_id); + if (ret) + return ret; + if (dev_id < 0) + return -EINVAL; + + mutex_lock(&zram_index_mutex); + + zram = idr_find(&zram_index_idr, dev_id); + if (zram) { + ret = zram_remove(zram); + if (!ret) + idr_remove(&zram_index_idr, dev_id); + } else { + ret = -ENODEV; + } + + mutex_unlock(&zram_index_mutex); + return ret ? ret : count; +} +static CLASS_ATTR_WO(hot_remove); + +static struct attribute *zram_control_class_attrs[] = { + &class_attr_hot_add.attr, + &class_attr_hot_remove.attr, + NULL, +}; +ATTRIBUTE_GROUPS(zram_control_class); + +static struct class zram_control_class = { + .name = "zram-control", + .owner = THIS_MODULE, + .class_groups = zram_control_class_groups, +}; + +static int zram_remove_cb(int id, void *ptr, void *data) +{ + zram_remove(ptr); + return 0; +} + +static void destroy_devices(void) +{ + class_unregister(&zram_control_class); + idr_for_each(&zram_index_idr, &zram_remove_cb, NULL); + zram_debugfs_destroy(); + idr_destroy(&zram_index_idr); + unregister_blkdev(zram_major, "zram"); + cpuhp_remove_multi_state(CPUHP_ZCOMP_PREPARE); +} + +static int __init zram_init(void) +{ + int ret; + + ret = cpuhp_setup_state_multi(CPUHP_ZCOMP_PREPARE, "block/zram:prepare", + zcomp_cpu_up_prepare, zcomp_cpu_dead); + if (ret < 0) + return ret; + + ret = class_register(&zram_control_class); + if (ret) { + pr_err("Unable to register zram-control class\n"); + cpuhp_remove_multi_state(CPUHP_ZCOMP_PREPARE); + return ret; + } + + zram_debugfs_create(); + zram_major = register_blkdev(0, "zram"); + if (zram_major <= 0) { + pr_err("Unable to get major number\n"); + class_unregister(&zram_control_class); + cpuhp_remove_multi_state(CPUHP_ZCOMP_PREPARE); + return -EBUSY; + } + + while (num_devices != 0) { + mutex_lock(&zram_index_mutex); + ret = zram_add(); + mutex_unlock(&zram_index_mutex); + if (ret < 0) + goto out_error; + num_devices--; + } + +#ifdef CONFIG_HYBRIDSWAP + ret = hybridswap_pre_init(); + if (ret) + goto out_error; +#endif + return 0; + +out_error: + destroy_devices(); + return ret; +} + +static void __exit zram_exit(void) +{ + destroy_devices(); +} + +module_init(zram_init); +module_exit(zram_exit); + +module_param(num_devices, uint, 0); +MODULE_PARM_DESC(num_devices, "Number of pre-created zram devices"); + +MODULE_IMPORT_NS(MINIDUMP); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Nitin Gupta "); +MODULE_DESCRIPTION("Compressed RAM Block Device"); diff --git a/drivers/moto_swap/zram-5.15/zram_drv.h b/drivers/moto_swap/zram-5.15/zram_drv.h new file mode 100644 index 000000000000..84a3a48e2eb9 --- /dev/null +++ b/drivers/moto_swap/zram-5.15/zram_drv.h @@ -0,0 +1,144 @@ +/* + * Compressed RAM block device + * + * Copyright (C) 2008, 2009, 2010 Nitin Gupta + * 2012, 2013 Minchan Kim + * + * This code is released using a dual license strategy: BSD/GPL + * You can choose the licence that better fits your requirements. + * + * Released under the terms of 3-clause BSD License + * Released under the terms of GNU General Public License Version 2.0 + * + */ + +#ifndef _ZRAM_DRV_H_ +#define _ZRAM_DRV_H_ + +#include +#include +#include + +#include "zcomp.h" + +#define SECTORS_PER_PAGE_SHIFT (PAGE_SHIFT - SECTOR_SHIFT) +#define SECTORS_PER_PAGE (1 << SECTORS_PER_PAGE_SHIFT) +#define ZRAM_LOGICAL_BLOCK_SHIFT 12 +#define ZRAM_LOGICAL_BLOCK_SIZE (1 << ZRAM_LOGICAL_BLOCK_SHIFT) +#define ZRAM_SECTOR_PER_LOGICAL_BLOCK \ + (1 << (ZRAM_LOGICAL_BLOCK_SHIFT - SECTOR_SHIFT)) + + +/* + * The lower ZRAM_FLAG_SHIFT bits of table.flags is for + * object size (excluding header), the higher bits is for + * zram_pageflags. + * + * zram is mainly used for memory efficiency so we want to keep memory + * footprint small so we can squeeze size and flags into a field. + * The lower ZRAM_FLAG_SHIFT bits is for object size (excluding header), + * the higher bits is for zram_pageflags. + */ +#define ZRAM_FLAG_SHIFT 24 + +/* Flags for zram pages (table[page_no].flags) */ +enum zram_pageflags { + /* zram slot is locked */ + ZRAM_LOCK = ZRAM_FLAG_SHIFT, + ZRAM_SAME, /* Page consists the same element */ + ZRAM_WB, /* page is stored on backing_device */ + ZRAM_UNDER_WB, /* page is under writeback */ + ZRAM_HUGE, /* Incompressible page */ + ZRAM_IDLE, /* not accessed page since last idle marking */ + +#ifdef CONFIG_HYBRIDSWAP_CORE + ZRAM_BATCHING_OUT, + ZRAM_FROM_HYBRIDSWAP, + ZRAM_MCGID_CLEAR, + ZRAM_IN_BD, /* zram stored in back device */ +#endif + __NR_ZRAM_PAGEFLAGS, +}; + +/*-- Data structures */ + +/* Allocated for each disk page */ +struct zram_table_entry { + union { + unsigned long handle; + unsigned long element; + }; + unsigned long flags; +#ifdef CONFIG_HYBRIDSWAP_ZRAM_MEMORY_TRACKING + ktime_t ac_time; +#endif +}; + +struct zram_stats { + atomic64_t compr_data_size; /* compressed size of pages stored */ + atomic64_t num_reads; /* failed + successful */ + atomic64_t num_writes; /* --do-- */ + atomic64_t failed_reads; /* can happen when memory is too low */ + atomic64_t failed_writes; /* can happen when memory is too low */ + atomic64_t invalid_io; /* non-page-aligned I/O requests */ + atomic64_t notify_free; /* no. of swap slot free notifications */ + atomic64_t same_pages; /* no. of same element filled pages */ + atomic64_t huge_pages; /* no. of huge pages */ + atomic64_t huge_pages_since; /* no. of huge pages since zram set up */ + atomic64_t pages_stored; /* no. of pages currently stored */ + atomic_long_t max_used_pages; /* no. of maximum pages stored */ + atomic64_t writestall; /* no. of write slow paths */ + atomic64_t miss_free; /* no. of missed free */ +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK + atomic64_t bd_count; /* no. of pages in backing device */ + atomic64_t bd_reads; /* no. of reads from backing device */ + atomic64_t bd_writes; /* no. of writes from backing device */ +#endif +}; + +struct zram { + struct zram_table_entry *table; + struct zs_pool *mem_pool; + struct zcomp *comp; + struct gendisk *disk; + /* Prevent concurrent execution of device init */ + struct rw_semaphore init_lock; + /* + * the number of pages zram can consume for storing compressed data + */ + unsigned long limit_pages; + + struct zram_stats stats; + /* + * This is the limit on amount of *uncompressed* worth of data + * we can store in a disk. + */ + u64 disksize; /* bytes */ + char compressor[CRYPTO_MAX_ALG_NAME]; + /* + * zram is claimed so open request will be failed + */ + bool claim; /* Protected by disk->open_mutex */ + struct file *backing_dev; +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK + spinlock_t wb_limit_lock; + bool wb_limit_enable; + u64 bd_wb_limit; + struct block_device *bdev; + unsigned long *bitmap; + unsigned long nr_pages; +#endif +#ifdef CONFIG_HYBRIDSWAP_ZRAM_MEMORY_TRACKING + struct dentry *debugfs_dir; +#endif +#if (defined CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK) || (defined CONFIG_HYBRIDSWAP_CORE) + struct block_device *bdev; + unsigned int old_block_size; + unsigned long nr_pages; + unsigned long increase_nr_pages; +#endif +#ifdef CONFIG_HYBRIDSWAP_CORE + struct hyb_info *infos; +#endif +}; +#endif diff --git a/drivers/moto_swap/zram-5.15/zram_drv_internal.h b/drivers/moto_swap/zram-5.15/zram_drv_internal.h new file mode 100644 index 000000000000..a0746122f404 --- /dev/null +++ b/drivers/moto_swap/zram-5.15/zram_drv_internal.h @@ -0,0 +1,35 @@ +#ifndef _ZRAM_DRV_INTERNAL_H_ +#define _ZRAM_DRV_INTERNAL_H_ +#ifdef BIT +#undef BIT +#define BIT(nr) (1lu << (nr)) +#endif + +#define zram_slot_lock(zram, index) (bit_spin_lock(ZRAM_LOCK, &zram->table[index].flags)) + +#define zram_slot_unlock(zram, index) (bit_spin_unlock(ZRAM_LOCK, &zram->table[index].flags)) + +#define init_done(zram) (zram->disksize) + +#define dev_to_zram(dev) ((struct zram *)dev_to_disk(dev)->private_data) + +#define zram_get_handle(zram, index) (zram->table[index].handle) + +#define zram_set_handle(zram, index, handle_val) (zram->table[index].handle = handle_val) + +#define zram_test_flag(zram, index, flag) (zram->table[index].flags & BIT(flag)) + +#define zram_set_flag(zram, index, flag) (zram->table[index].flags |= BIT(flag)) + +#define zram_clear_flag(zram, index, flag) (zram->table[index].flags &= ~BIT(flag)) + +#define zram_set_element(zram, index, element) (zram->table[index].element = element) + +#define zram_get_obj_size(zram, index) (zram->table[index].flags & (BIT(ZRAM_FLAG_SHIFT) - 1)) + +#define zram_set_obj_size(zram, index, size) do {\ + unsigned long flags = zram->table[index].flags >> ZRAM_FLAG_SHIFT; \ + zram->table[index].flags = (flags << ZRAM_FLAG_SHIFT) | size; \ +} while(0) + +#endif diff --git a/drivers/moto_swap/zram-5.4/zcomp.c b/drivers/moto_swap/zram-5.4/zcomp.c new file mode 100644 index 000000000000..1a8564a79d8d --- /dev/null +++ b/drivers/moto_swap/zram-5.4/zcomp.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2014 Sergey Senozhatsky. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zcomp.h" + +static const char * const backends[] = { + "lzo", + "lzo-rle", +#if IS_ENABLED(CONFIG_CRYPTO_LZ4) + "lz4", +#endif +#if IS_ENABLED(CONFIG_CRYPTO_LZ4HC) + "lz4hc", +#endif +#if IS_ENABLED(CONFIG_CRYPTO_842) + "842", +#endif +#if IS_ENABLED(CONFIG_CRYPTO_ZSTD) + "zstd", +#endif + NULL +}; + +static void zcomp_strm_free(struct zcomp_strm *zstrm) +{ + if (!IS_ERR_OR_NULL(zstrm->tfm)) + crypto_free_comp(zstrm->tfm); + free_pages((unsigned long)zstrm->buffer, 1); + kfree(zstrm); +} + +/* + * allocate new zcomp_strm structure with ->tfm initialized by + * backend, return NULL on error + */ +static struct zcomp_strm *zcomp_strm_alloc(struct zcomp *comp) +{ + struct zcomp_strm *zstrm = kmalloc(sizeof(*zstrm), GFP_KERNEL); + if (!zstrm) + return NULL; + + zstrm->tfm = crypto_alloc_comp(comp->name, 0, 0); + /* + * allocate 2 pages. 1 for compressed data, plus 1 extra for the + * case when compressed size is larger than the original one + */ + zstrm->buffer = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); + if (IS_ERR_OR_NULL(zstrm->tfm) || !zstrm->buffer) { + zcomp_strm_free(zstrm); + zstrm = NULL; + } + return zstrm; +} + +bool zcomp_available_algorithm(const char *comp) +{ + int i; + + i = __sysfs_match_string(backends, -1, comp); + if (i >= 0) + return true; + + /* + * Crypto does not ignore a trailing new line symbol, + * so make sure you don't supply a string containing + * one. + * This also means that we permit zcomp initialisation + * with any compressing algorithm known to crypto api. + */ + return crypto_has_comp(comp, 0, 0) == 1; +} + +/* show available compressors */ +ssize_t zcomp_available_show(const char *comp, char *buf) +{ + bool known_algorithm = false; + ssize_t sz = 0; + int i = 0; + + for (; backends[i]; i++) { + if (!strcmp(comp, backends[i])) { + known_algorithm = true; + sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, + "[%s] ", backends[i]); + } else { + sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, + "%s ", backends[i]); + } + } + + /* + * Out-of-tree module known to crypto api or a missing + * entry in `backends'. + */ + if (!known_algorithm && crypto_has_comp(comp, 0, 0) == 1) + sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2, + "[%s] ", comp); + + sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n"); + return sz; +} + +struct zcomp_strm *zcomp_stream_get(struct zcomp *comp) +{ + return *get_cpu_ptr(comp->stream); +} + +void zcomp_stream_put(struct zcomp *comp) +{ + put_cpu_ptr(comp->stream); +} + +int zcomp_compress(struct zcomp_strm *zstrm, + const void *src, unsigned int *dst_len) +{ + /* + * Our dst memory (zstrm->buffer) is always `2 * PAGE_SIZE' sized + * because sometimes we can endup having a bigger compressed data + * due to various reasons: for example compression algorithms tend + * to add some padding to the compressed buffer. Speaking of padding, + * comp algorithm `842' pads the compressed length to multiple of 8 + * and returns -ENOSP when the dst memory is not big enough, which + * is not something that ZRAM wants to see. We can handle the + * `compressed_size > PAGE_SIZE' case easily in ZRAM, but when we + * receive -ERRNO from the compressing backend we can't help it + * anymore. To make `842' happy we need to tell the exact size of + * the dst buffer, zram_drv will take care of the fact that + * compressed buffer is too big. + */ + *dst_len = PAGE_SIZE * 2; + + return crypto_comp_compress(zstrm->tfm, + src, PAGE_SIZE, + zstrm->buffer, dst_len); +} + +int zcomp_decompress(struct zcomp_strm *zstrm, + const void *src, unsigned int src_len, void *dst) +{ + unsigned int dst_len = PAGE_SIZE; + + return crypto_comp_decompress(zstrm->tfm, + src, src_len, + dst, &dst_len); +} + +int zcomp_cpu_up_prepare(unsigned int cpu, struct hlist_node *node) +{ + struct zcomp *comp = hlist_entry(node, struct zcomp, node); + struct zcomp_strm *zstrm; + + if (WARN_ON(*per_cpu_ptr(comp->stream, cpu))) + return 0; + + zstrm = zcomp_strm_alloc(comp); + if (IS_ERR_OR_NULL(zstrm)) { + pr_err("Can't allocate a compression stream\n"); + return -ENOMEM; + } + *per_cpu_ptr(comp->stream, cpu) = zstrm; + return 0; +} + +int zcomp_cpu_dead(unsigned int cpu, struct hlist_node *node) +{ + struct zcomp *comp = hlist_entry(node, struct zcomp, node); + struct zcomp_strm *zstrm; + + zstrm = *per_cpu_ptr(comp->stream, cpu); + if (!IS_ERR_OR_NULL(zstrm)) + zcomp_strm_free(zstrm); + *per_cpu_ptr(comp->stream, cpu) = NULL; + return 0; +} + +static int zcomp_init(struct zcomp *comp) +{ + int ret; + + comp->stream = alloc_percpu(struct zcomp_strm *); + if (!comp->stream) + return -ENOMEM; + + ret = cpuhp_state_add_instance(CPUHP_ZCOMP_PREPARE, &comp->node); + if (ret < 0) + goto cleanup; + return 0; + +cleanup: + free_percpu(comp->stream); + return ret; +} + +void zcomp_destroy(struct zcomp *comp) +{ + cpuhp_state_remove_instance(CPUHP_ZCOMP_PREPARE, &comp->node); + free_percpu(comp->stream); + kfree(comp); +} + +/* + * search available compressors for requested algorithm. + * allocate new zcomp and initialize it. return compressing + * backend pointer or ERR_PTR if things went bad. ERR_PTR(-EINVAL) + * if requested algorithm is not supported, ERR_PTR(-ENOMEM) in + * case of allocation error, or any other error potentially + * returned by zcomp_init(). + */ +struct zcomp *zcomp_create(const char *compress) +{ + struct zcomp *comp; + int error; + + if (!zcomp_available_algorithm(compress)) + return ERR_PTR(-EINVAL); + + comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL); + if (!comp) + return ERR_PTR(-ENOMEM); + + comp->name = compress; + error = zcomp_init(comp); + if (error) { + kfree(comp); + return ERR_PTR(error); + } + return comp; +} diff --git a/drivers/moto_swap/zram-5.4/zcomp.h b/drivers/moto_swap/zram-5.4/zcomp.h new file mode 100644 index 000000000000..1806475b919d --- /dev/null +++ b/drivers/moto_swap/zram-5.4/zcomp.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2014 Sergey Senozhatsky. + */ + +#ifndef _ZCOMP_H_ +#define _ZCOMP_H_ + +struct zcomp_strm { + /* compression/decompression buffer */ + void *buffer; + struct crypto_comp *tfm; +}; + +/* dynamic per-device compression frontend */ +struct zcomp { + struct zcomp_strm * __percpu *stream; + const char *name; + struct hlist_node node; +}; + +int zcomp_cpu_up_prepare(unsigned int cpu, struct hlist_node *node); +int zcomp_cpu_dead(unsigned int cpu, struct hlist_node *node); +ssize_t zcomp_available_show(const char *comp, char *buf); +bool zcomp_available_algorithm(const char *comp); + +struct zcomp *zcomp_create(const char *comp); +void zcomp_destroy(struct zcomp *comp); + +struct zcomp_strm *zcomp_stream_get(struct zcomp *comp); +void zcomp_stream_put(struct zcomp *comp); + +int zcomp_compress(struct zcomp_strm *zstrm, + const void *src, unsigned int *dst_len); + +int zcomp_decompress(struct zcomp_strm *zstrm, + const void *src, unsigned int src_len, void *dst); + +bool zcomp_set_max_streams(struct zcomp *comp, int num_strm); +#endif /* _ZCOMP_H_ */ diff --git a/drivers/moto_swap/zram-5.4/zram_drv.c b/drivers/moto_swap/zram-5.4/zram_drv.c new file mode 100644 index 000000000000..aaccc68e91ce --- /dev/null +++ b/drivers/moto_swap/zram-5.4/zram_drv.c @@ -0,0 +1,2353 @@ +/* + * Compressed RAM block device + * + * Copyright (C) 2008, 2009, 2010 Nitin Gupta + * 2012, 2013 Minchan Kim + * + * This code is released using a dual license strategy: BSD/GPL + * You can choose the licence that better fits your requirements. + * + * Released under the terms of 3-clause BSD License + * Released under the terms of GNU General Public License Version 2.0 + * + */ + +#define KMSG_COMPONENT "zram" +#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zram_drv.h" +#include "zram_drv_internal.h" +#ifdef CONFIG_HYBRIDSWAP +#include "../hybridswap/hybridswap.h" +#endif + +static DEFINE_IDR(zram_index_idr); +/* idr index must be protected */ +static DEFINE_MUTEX(zram_index_mutex); + +static int zram_major; +static const char *default_compressor = "lzo-rle"; + +/* Module params (documentation at end) */ +static unsigned int num_devices = 1; +/* + * Pages that compress to sizes equals or greater than this are stored + * uncompressed in memory. + */ +static size_t huge_class_size; + +static const struct block_device_operations zram_devops; +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +static const struct block_device_operations zram_wb_devops; +#endif + +static void zram_free_page(struct zram *zram, size_t index); +static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, + u32 index, int offset, struct bio *bio); + + +static int zram_slot_trylock(struct zram *zram, u32 index) +{ + return bit_spin_trylock(ZRAM_LOCK, &zram->table[index].flags); +} + +static unsigned long zram_get_element(struct zram *zram, u32 index) +{ + return zram->table[index].element; +} + +static inline bool zram_allocated(struct zram *zram, u32 index) +{ + return zram_get_obj_size(zram, index) || + zram_test_flag(zram, index, ZRAM_SAME) || + zram_test_flag(zram, index, ZRAM_WB); +} + +#if PAGE_SIZE != 4096 +static inline bool is_partial_io(struct bio_vec *bvec) +{ + return bvec->bv_len != PAGE_SIZE; +} +#else +static inline bool is_partial_io(struct bio_vec *bvec) +{ + return false; +} +#endif + +/* + * Check if request is within bounds and aligned on zram logical blocks. + */ +static inline bool valid_io_request(struct zram *zram, + sector_t start, unsigned int size) +{ + u64 end, bound; + + /* unaligned request */ + if (unlikely(start & (ZRAM_SECTOR_PER_LOGICAL_BLOCK - 1))) + return false; + if (unlikely(size & (ZRAM_LOGICAL_BLOCK_SIZE - 1))) + return false; + + end = start + (size >> SECTOR_SHIFT); + bound = zram->disksize >> SECTOR_SHIFT; + /* out of range range */ + if (unlikely(start >= bound || end > bound || start > end)) + return false; + + /* I/O request is valid */ + return true; +} + +static void update_position(u32 *index, int *offset, struct bio_vec *bvec) +{ + *index += (*offset + bvec->bv_len) / PAGE_SIZE; + *offset = (*offset + bvec->bv_len) % PAGE_SIZE; +} + +static inline void update_used_max(struct zram *zram, + const unsigned long pages) +{ + unsigned long old_max, cur_max; + + old_max = atomic_long_read(&zram->stats.max_used_pages); + + do { + cur_max = old_max; + if (pages > cur_max) + old_max = atomic_long_cmpxchg( + &zram->stats.max_used_pages, cur_max, pages); + } while (old_max != cur_max); +} + +static inline void zram_fill_page(void *ptr, unsigned long len, + unsigned long value) +{ + WARN_ON_ONCE(!IS_ALIGNED(len, sizeof(unsigned long))); + memset_l(ptr, value, len / sizeof(unsigned long)); +} + +static bool page_same_filled(void *ptr, unsigned long *element) +{ + unsigned int pos; + unsigned long *page; + unsigned long val; + + page = (unsigned long *)ptr; + val = page[0]; + + for (pos = 1; pos < PAGE_SIZE / sizeof(*page); pos++) { + if (val != page[pos]) + return false; + } + + *element = val; + + return true; +} + +static ssize_t initstate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u32 val; + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + val = init_done(zram); + up_read(&zram->init_lock); + + return scnprintf(buf, PAGE_SIZE, "%u\n", val); +} + +static ssize_t disksize_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + + return scnprintf(buf, PAGE_SIZE, "%llu\n", zram->disksize); +} + +static ssize_t mem_limit_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + u64 limit; + char *tmp; + struct zram *zram = dev_to_zram(dev); + + limit = memparse(buf, &tmp); + if (buf == tmp) /* no chars parsed, invalid input */ + return -EINVAL; + + down_write(&zram->init_lock); + zram->limit_pages = PAGE_ALIGN(limit) >> PAGE_SHIFT; + up_write(&zram->init_lock); + + return len; +} + +static ssize_t mem_used_max_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int err; + unsigned long val; + struct zram *zram = dev_to_zram(dev); + + err = kstrtoul(buf, 10, &val); + if (err || val != 0) + return -EINVAL; + + down_read(&zram->init_lock); + if (init_done(zram)) { + atomic_long_set(&zram->stats.max_used_pages, + zs_get_total_pages(zram->mem_pool)); + } + up_read(&zram->init_lock); + + return len; +} + +static ssize_t idle_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + unsigned long nr_pages = zram->disksize >> PAGE_SHIFT; + int index; + + if (!sysfs_streq(buf, "all")) + return -EINVAL; + + down_read(&zram->init_lock); + if (!init_done(zram)) { + up_read(&zram->init_lock); + return -EINVAL; + } + + for (index = 0; index < nr_pages; index++) { + /* + * Do not mark ZRAM_UNDER_WB slot as ZRAM_IDLE to close race. + * See the comment in writeback_store. + */ + zram_slot_lock(zram, index); + if (zram_allocated(zram, index) && + !zram_test_flag(zram, index, ZRAM_UNDER_WB)) + zram_set_flag(zram, index, ZRAM_IDLE); + zram_slot_unlock(zram, index); + } + + up_read(&zram->init_lock); + + return len; +} + +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +static ssize_t writeback_limit_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + u64 val; + ssize_t ret = -EINVAL; + + if (kstrtoull(buf, 10, &val)) + return ret; + + down_read(&zram->init_lock); + spin_lock(&zram->wb_limit_lock); + zram->wb_limit_enable = val; + spin_unlock(&zram->wb_limit_lock); + up_read(&zram->init_lock); + ret = len; + + return ret; +} + +static ssize_t writeback_limit_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + bool val; + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + spin_lock(&zram->wb_limit_lock); + val = zram->wb_limit_enable; + spin_unlock(&zram->wb_limit_lock); + up_read(&zram->init_lock); + + return scnprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t writeback_limit_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + u64 val; + ssize_t ret = -EINVAL; + + if (kstrtoull(buf, 10, &val)) + return ret; + + down_read(&zram->init_lock); + spin_lock(&zram->wb_limit_lock); + zram->bd_wb_limit = val; + spin_unlock(&zram->wb_limit_lock); + up_read(&zram->init_lock); + ret = len; + + return ret; +} + +static ssize_t writeback_limit_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u64 val; + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + spin_lock(&zram->wb_limit_lock); + val = zram->bd_wb_limit; + spin_unlock(&zram->wb_limit_lock); + up_read(&zram->init_lock); + + return scnprintf(buf, PAGE_SIZE, "%llu\n", val); +} + +static void reset_bdev(struct zram *zram) +{ + struct block_device *bdev; + + if (!zram->backing_dev) + return; + + bdev = zram->bdev; + if (zram->old_block_size) + set_blocksize(bdev, zram->old_block_size); + blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL); + /* hope filp_close flush all of IO */ + filp_close(zram->backing_dev, NULL); + zram->backing_dev = NULL; + zram->old_block_size = 0; + zram->bdev = NULL; + zram->disk->queue->backing_dev_info->capabilities |= + BDI_CAP_SYNCHRONOUS_IO; + kvfree(zram->bitmap); + zram->bitmap = NULL; +} + +static ssize_t backing_dev_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct file *file; + struct zram *zram = dev_to_zram(dev); + char *p; + ssize_t ret; + + down_read(&zram->init_lock); + file = zram->backing_dev; + if (!file) { + memcpy(buf, "none\n", 5); + up_read(&zram->init_lock); + return 5; + } + + p = file_path(file, buf, PAGE_SIZE - 1); + if (IS_ERR(p)) { + ret = PTR_ERR(p); + goto out; + } + + ret = strlen(p); + memmove(buf, p, ret); + buf[ret++] = '\n'; +out: + up_read(&zram->init_lock); + return ret; +} + +static ssize_t backing_dev_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + char *file_name; + size_t sz; + struct file *backing_dev = NULL; + struct inode *inode; + struct address_space *mapping; + unsigned int bitmap_sz, old_block_size = 0; + unsigned long nr_pages, *bitmap = NULL; + struct block_device *bdev = NULL; + int err; + struct zram *zram = dev_to_zram(dev); + + file_name = kmalloc(PATH_MAX, GFP_KERNEL); + if (!file_name) + return -ENOMEM; + + down_write(&zram->init_lock); + if (init_done(zram)) { + pr_info("Can't setup backing device for initialized device\n"); + err = -EBUSY; + goto out; + } + + strlcpy(file_name, buf, PATH_MAX); + /* ignore trailing newline */ + sz = strlen(file_name); + if (sz > 0 && file_name[sz - 1] == '\n') + file_name[sz - 1] = 0x00; + + backing_dev = filp_open(file_name, O_RDWR|O_LARGEFILE, 0); + if (IS_ERR(backing_dev)) { + err = PTR_ERR(backing_dev); + backing_dev = NULL; + goto out; + } + + mapping = backing_dev->f_mapping; + inode = mapping->host; + + /* Support only block device in this moment */ + if (!S_ISBLK(inode->i_mode)) { + err = -ENOTBLK; + goto out; + } + + bdev = bdgrab(I_BDEV(inode)); + err = blkdev_get(bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL, zram); + if (err < 0) { + bdev = NULL; + goto out; + } + + nr_pages = i_size_read(inode) >> PAGE_SHIFT; + bitmap_sz = BITS_TO_LONGS(nr_pages) * sizeof(long); + bitmap = kvzalloc(bitmap_sz, GFP_KERNEL); + if (!bitmap) { + err = -ENOMEM; + goto out; + } + + old_block_size = block_size(bdev); + err = set_blocksize(bdev, PAGE_SIZE); + if (err) + goto out; + + reset_bdev(zram); + + zram->old_block_size = old_block_size; + zram->bdev = bdev; + zram->backing_dev = backing_dev; + zram->bitmap = bitmap; + zram->nr_pages = nr_pages; + /* + * With writeback feature, zram does asynchronous IO so it's no longer + * synchronous device so let's remove synchronous io flag. Othewise, + * upper layer(e.g., swap) could wait IO completion rather than + * (submit and return), which will cause system sluggish. + * Furthermore, when the IO function returns(e.g., swap_readpage), + * upper layer expects IO was done so it could deallocate the page + * freely but in fact, IO is going on so finally could cause + * use-after-free when the IO is really done. + */ + zram->disk->queue->backing_dev_info->capabilities &= + ~BDI_CAP_SYNCHRONOUS_IO; + up_write(&zram->init_lock); + + pr_info("setup backing device %s\n", file_name); + kfree(file_name); + + return len; +out: + if (bitmap) + kvfree(bitmap); + + if (bdev) + blkdev_put(bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL); + + if (backing_dev) + filp_close(backing_dev, NULL); + + up_write(&zram->init_lock); + + kfree(file_name); + + return err; +} + +static unsigned long alloc_block_bdev(struct zram *zram) +{ + unsigned long blk_idx = 1; +retry: + /* skip 0 bit to confuse zram.handle = 0 */ + blk_idx = find_next_zero_bit(zram->bitmap, zram->nr_pages, blk_idx); + if (blk_idx == zram->nr_pages) + return 0; + + if (test_and_set_bit(blk_idx, zram->bitmap)) + goto retry; + + atomic64_inc(&zram->stats.bd_count); + return blk_idx; +} + +static void free_block_bdev(struct zram *zram, unsigned long blk_idx) +{ + int was_set; + + was_set = test_and_clear_bit(blk_idx, zram->bitmap); + WARN_ON_ONCE(!was_set); + atomic64_dec(&zram->stats.bd_count); +} + +static void zram_page_end_io(struct bio *bio) +{ + struct page *page = bio_first_page_all(bio); + + page_endio(page, op_is_write(bio_op(bio)), + blk_status_to_errno(bio->bi_status)); + bio_put(bio); +} + +/* + * Returns 1 if the submission is successful. + */ +static int read_from_bdev_async(struct zram *zram, struct bio_vec *bvec, + unsigned long entry, struct bio *parent) +{ + struct bio *bio; + + bio = bio_alloc(GFP_ATOMIC, 1); + if (!bio) + return -ENOMEM; + + bio->bi_iter.bi_sector = entry * (PAGE_SIZE >> 9); + bio_set_dev(bio, zram->bdev); + if (!bio_add_page(bio, bvec->bv_page, bvec->bv_len, bvec->bv_offset)) { + bio_put(bio); + return -EIO; + } + + if (!parent) { + bio->bi_opf = REQ_OP_READ; + bio->bi_end_io = zram_page_end_io; + } else { + bio->bi_opf = parent->bi_opf; + bio_chain(bio, parent); + } + + submit_bio(bio); + return 1; +} + +#define HUGE_WRITEBACK 1 +#define IDLE_WRITEBACK 2 + +static ssize_t writeback_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + unsigned long nr_pages = zram->disksize >> PAGE_SHIFT; + unsigned long index; + struct bio bio; + struct bio_vec bio_vec; + struct page *page; + ssize_t ret = len; + int mode, err; + unsigned long blk_idx = 0; + + if (sysfs_streq(buf, "idle")) + mode = IDLE_WRITEBACK; + else if (sysfs_streq(buf, "huge")) + mode = HUGE_WRITEBACK; + else + return -EINVAL; + + down_read(&zram->init_lock); + if (!init_done(zram)) { + ret = -EINVAL; + goto release_init_lock; + } + + if (!zram->backing_dev) { + ret = -ENODEV; + goto release_init_lock; + } + + page = alloc_page(GFP_KERNEL); + if (!page) { + ret = -ENOMEM; + goto release_init_lock; + } + + for (index = 0; index < nr_pages; index++) { + struct bio_vec bvec; + + bvec.bv_page = page; + bvec.bv_len = PAGE_SIZE; + bvec.bv_offset = 0; + + spin_lock(&zram->wb_limit_lock); + if (zram->wb_limit_enable && !zram->bd_wb_limit) { + spin_unlock(&zram->wb_limit_lock); + ret = -EIO; + break; + } + spin_unlock(&zram->wb_limit_lock); + + if (!blk_idx) { + blk_idx = alloc_block_bdev(zram); + if (!blk_idx) { + ret = -ENOSPC; + break; + } + } + + zram_slot_lock(zram, index); + if (!zram_allocated(zram, index)) + goto next; + + if (zram_test_flag(zram, index, ZRAM_WB) || + zram_test_flag(zram, index, ZRAM_SAME) || + zram_test_flag(zram, index, ZRAM_UNDER_WB)) + goto next; + + if (mode == IDLE_WRITEBACK && + !zram_test_flag(zram, index, ZRAM_IDLE)) + goto next; + if (mode == HUGE_WRITEBACK && + !zram_test_flag(zram, index, ZRAM_HUGE)) + goto next; + /* + * Clearing ZRAM_UNDER_WB is duty of caller. + * IOW, zram_free_page never clear it. + */ + zram_set_flag(zram, index, ZRAM_UNDER_WB); + /* Need for hugepage writeback racing */ + zram_set_flag(zram, index, ZRAM_IDLE); + zram_slot_unlock(zram, index); + if (zram_bvec_read(zram, &bvec, index, 0, NULL)) { + zram_slot_lock(zram, index); + zram_clear_flag(zram, index, ZRAM_UNDER_WB); + zram_clear_flag(zram, index, ZRAM_IDLE); + zram_slot_unlock(zram, index); + continue; + } + + bio_init(&bio, &bio_vec, 1); + bio_set_dev(&bio, zram->bdev); + bio.bi_iter.bi_sector = blk_idx * (PAGE_SIZE >> 9); + bio.bi_opf = REQ_OP_WRITE | REQ_SYNC; + + bio_add_page(&bio, bvec.bv_page, bvec.bv_len, + bvec.bv_offset); + /* + * XXX: A single page IO would be inefficient for write + * but it would be not bad as starter. + */ + err = submit_bio_wait(&bio); + if (err) { + zram_slot_lock(zram, index); + zram_clear_flag(zram, index, ZRAM_UNDER_WB); + zram_clear_flag(zram, index, ZRAM_IDLE); + zram_slot_unlock(zram, index); + /* + * Return last IO error unless every IO were + * not suceeded. + */ + ret = err; + continue; + } + + atomic64_inc(&zram->stats.bd_writes); + /* + * We released zram_slot_lock so need to check if the slot was + * changed. If there is freeing for the slot, we can catch it + * easily by zram_allocated. + * A subtle case is the slot is freed/reallocated/marked as + * ZRAM_IDLE again. To close the race, idle_store doesn't + * mark ZRAM_IDLE once it found the slot was ZRAM_UNDER_WB. + * Thus, we could close the race by checking ZRAM_IDLE bit. + */ + zram_slot_lock(zram, index); + if (!zram_allocated(zram, index) || + !zram_test_flag(zram, index, ZRAM_IDLE)) { + zram_clear_flag(zram, index, ZRAM_UNDER_WB); + zram_clear_flag(zram, index, ZRAM_IDLE); + goto next; + } + + zram_free_page(zram, index); + zram_clear_flag(zram, index, ZRAM_UNDER_WB); + zram_set_flag(zram, index, ZRAM_WB); + zram_set_element(zram, index, blk_idx); + blk_idx = 0; + atomic64_inc(&zram->stats.pages_stored); + spin_lock(&zram->wb_limit_lock); + if (zram->wb_limit_enable && zram->bd_wb_limit > 0) + zram->bd_wb_limit -= 1UL << (PAGE_SHIFT - 12); + spin_unlock(&zram->wb_limit_lock); +next: + zram_slot_unlock(zram, index); + } + + if (blk_idx) + free_block_bdev(zram, blk_idx); + __free_page(page); +release_init_lock: + up_read(&zram->init_lock); + + return ret; +} + +struct zram_work { + struct work_struct work; + struct zram *zram; + unsigned long entry; + struct bio *bio; + struct bio_vec bvec; +}; + +#if PAGE_SIZE != 4096 +static void zram_sync_read(struct work_struct *work) +{ + struct zram_work *zw = container_of(work, struct zram_work, work); + struct zram *zram = zw->zram; + unsigned long entry = zw->entry; + struct bio *bio = zw->bio; + + read_from_bdev_async(zram, &zw->bvec, entry, bio); +} + +/* + * Block layer want one ->make_request_fn to be active at a time + * so if we use chained IO with parent IO in same context, + * it's a deadlock. To avoid, it, it uses worker thread context. + */ +static int read_from_bdev_sync(struct zram *zram, struct bio_vec *bvec, + unsigned long entry, struct bio *bio) +{ + struct zram_work work; + + work.bvec = *bvec; + work.zram = zram; + work.entry = entry; + work.bio = bio; + + INIT_WORK_ONSTACK(&work.work, zram_sync_read); + queue_work(system_unbound_wq, &work.work); + flush_work(&work.work); + destroy_work_on_stack(&work.work); + + return 1; +} +#else +static int read_from_bdev_sync(struct zram *zram, struct bio_vec *bvec, + unsigned long entry, struct bio *bio) +{ + WARN_ON(1); + return -EIO; +} +#endif + +static int read_from_bdev(struct zram *zram, struct bio_vec *bvec, + unsigned long entry, struct bio *parent, bool sync) +{ + atomic64_inc(&zram->stats.bd_reads); + if (sync) + return read_from_bdev_sync(zram, bvec, entry, parent); + else + return read_from_bdev_async(zram, bvec, entry, parent); +} +#else +#ifdef CONFIG_HYBRIDSWAP_CORE +static void reset_bdev(struct zram *zram) +{ + struct block_device *bdev; + + if (!zram->backing_dev) + return; + + bdev = zram->bdev; + if (zram->old_block_size) + set_blocksize(bdev, zram->old_block_size); + blkdev_put(bdev, FMODE_READ|FMODE_WRITE|FMODE_EXCL); + /* hope filp_close flush all of IO */ + filp_close(zram->backing_dev, NULL); + zram->backing_dev = NULL; + zram->old_block_size = 0; + zram->bdev = NULL; +} +#else +static inline void reset_bdev(struct zram *zram) {}; +#endif +static int read_from_bdev(struct zram *zram, struct bio_vec *bvec, + unsigned long entry, struct bio *parent, bool sync) +{ + return -EIO; +} + +static void free_block_bdev(struct zram *zram, unsigned long blk_idx) {}; +#endif + +#ifdef CONFIG_HYBRIDSWAP_ZRAM_MEMORY_TRACKING + +static struct dentry *zram_debugfs_root; + +static void zram_debugfs_create(void) +{ + zram_debugfs_root = debugfs_create_dir("zram", NULL); +} + +static void zram_debugfs_destroy(void) +{ + debugfs_remove_recursive(zram_debugfs_root); +} + +static void zram_accessed(struct zram *zram, u32 index) +{ + zram_clear_flag(zram, index, ZRAM_IDLE); + zram->table[index].ac_time = ktime_get_boottime(); +} + +static ssize_t read_block_state(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + char *kbuf; + ssize_t index, written = 0; + struct zram *zram = file->private_data; + unsigned long nr_pages = zram->disksize >> PAGE_SHIFT; + struct timespec64 ts; + + kbuf = kvmalloc(count, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + down_read(&zram->init_lock); + if (!init_done(zram)) { + up_read(&zram->init_lock); + kvfree(kbuf); + return -EINVAL; + } + + for (index = *ppos; index < nr_pages; index++) { + int copied; + + zram_slot_lock(zram, index); + if (!zram_allocated(zram, index)) + goto next; + + ts = ktime_to_timespec64(zram->table[index].ac_time); + copied = snprintf(kbuf + written, count, + "%12zd %12lld.%06lu %c%c%c%c\n", + index, (s64)ts.tv_sec, + ts.tv_nsec / NSEC_PER_USEC, + zram_test_flag(zram, index, ZRAM_SAME) ? 's' : '.', + zram_test_flag(zram, index, ZRAM_WB) ? 'w' : '.', + zram_test_flag(zram, index, ZRAM_HUGE) ? 'h' : '.', + zram_test_flag(zram, index, ZRAM_IDLE) ? 'i' : '.'); + + if (count <= copied) { + zram_slot_unlock(zram, index); + break; + } + written += copied; + count -= copied; +next: + zram_slot_unlock(zram, index); + *ppos += 1; + } + + up_read(&zram->init_lock); + if (copy_to_user(buf, kbuf, written)) + written = -EFAULT; + kvfree(kbuf); + + return written; +} + +static const struct file_operations proc_zram_block_state_op = { + .open = simple_open, + .read = read_block_state, + .llseek = default_llseek, +}; + +static void zram_debugfs_register(struct zram *zram) +{ + if (!zram_debugfs_root) + return; + + zram->debugfs_dir = debugfs_create_dir(zram->disk->disk_name, + zram_debugfs_root); + debugfs_create_file("block_state", 0400, zram->debugfs_dir, + zram, &proc_zram_block_state_op); +} + +static void zram_debugfs_unregister(struct zram *zram) +{ + debugfs_remove_recursive(zram->debugfs_dir); +} +#else +static void zram_debugfs_create(void) {}; +static void zram_debugfs_destroy(void) {}; +static void zram_accessed(struct zram *zram, u32 index) +{ + zram_clear_flag(zram, index, ZRAM_IDLE); +}; +static void zram_debugfs_register(struct zram *zram) {}; +static void zram_debugfs_unregister(struct zram *zram) {}; +#endif + +/* + * We switched to per-cpu streams and this attr is not needed anymore. + * However, we will keep it around for some time, because: + * a) we may revert per-cpu streams in the future + * b) it's visible to user space and we need to follow our 2 years + * retirement rule; but we already have a number of 'soon to be + * altered' attrs, so max_comp_streams need to wait for the next + * layoff cycle. + */ +static ssize_t max_comp_streams_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, PAGE_SIZE, "%d\n", num_online_cpus()); +} + +static ssize_t max_comp_streams_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + return len; +} + +static ssize_t comp_algorithm_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + size_t sz; + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + sz = zcomp_available_show(zram->compressor, buf); + up_read(&zram->init_lock); + + return sz; +} + +static ssize_t comp_algorithm_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + char compressor[ARRAY_SIZE(zram->compressor)]; + size_t sz; + + strlcpy(compressor, buf, sizeof(compressor)); + /* ignore trailing newline */ + sz = strlen(compressor); + if (sz > 0 && compressor[sz - 1] == '\n') + compressor[sz - 1] = 0x00; + + if (!zcomp_available_algorithm(compressor)) + return -EINVAL; + + down_write(&zram->init_lock); + if (init_done(zram)) { + up_write(&zram->init_lock); + pr_info("Can't change algorithm for initialized device\n"); + return -EBUSY; + } + + strcpy(zram->compressor, compressor); + up_write(&zram->init_lock); + return len; +} + +static ssize_t compact_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + + down_read(&zram->init_lock); + if (!init_done(zram)) { + up_read(&zram->init_lock); + return -EINVAL; + } + + zs_compact(zram->mem_pool); + up_read(&zram->init_lock); + + return len; +} + +static ssize_t io_stat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + ssize_t ret; + + down_read(&zram->init_lock); + ret = scnprintf(buf, PAGE_SIZE, + "%8llu %8llu %8llu %8llu\n", + (u64)atomic64_read(&zram->stats.failed_reads), + (u64)atomic64_read(&zram->stats.failed_writes), + (u64)atomic64_read(&zram->stats.invalid_io), + (u64)atomic64_read(&zram->stats.notify_free)); + up_read(&zram->init_lock); + + return ret; +} + +static ssize_t mm_stat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + struct zs_pool_stats pool_stats; + u64 orig_size, mem_used = 0; + long max_used; + ssize_t ret; + + memset(&pool_stats, 0x00, sizeof(struct zs_pool_stats)); + + down_read(&zram->init_lock); + if (init_done(zram)) { + mem_used = zs_get_total_pages(zram->mem_pool); + zs_pool_stats(zram->mem_pool, &pool_stats); + } + + orig_size = atomic64_read(&zram->stats.pages_stored); + max_used = atomic_long_read(&zram->stats.max_used_pages); + + ret = scnprintf(buf, PAGE_SIZE, + "%8llu %8llu %8llu %8lu %8ld %8llu %8lu %8llu\n", + orig_size << PAGE_SHIFT, + (u64)atomic64_read(&zram->stats.compr_data_size), + mem_used << PAGE_SHIFT, + zram->limit_pages << PAGE_SHIFT, + max_used << PAGE_SHIFT, + (u64)atomic64_read(&zram->stats.same_pages), + atomic_long_read(&pool_stats.pages_compacted), + (u64)atomic64_read(&zram->stats.huge_pages)); + up_read(&zram->init_lock); + + return ret; +} + +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +#define FOUR_K(x) ((x) * (1 << (PAGE_SHIFT - 12))) +static ssize_t bd_stat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + ssize_t ret; + + down_read(&zram->init_lock); + ret = scnprintf(buf, PAGE_SIZE, + "%8llu %8llu %8llu\n", + FOUR_K((u64)atomic64_read(&zram->stats.bd_count)), + FOUR_K((u64)atomic64_read(&zram->stats.bd_reads)), + FOUR_K((u64)atomic64_read(&zram->stats.bd_writes))); + up_read(&zram->init_lock); + + return ret; +} +#endif + +static ssize_t debug_stat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int version = 1; + struct zram *zram = dev_to_zram(dev); + ssize_t ret; + + down_read(&zram->init_lock); + ret = scnprintf(buf, PAGE_SIZE, + "version: %d\n%8llu %8llu\n", + version, + (u64)atomic64_read(&zram->stats.writestall), + (u64)atomic64_read(&zram->stats.miss_free)); + up_read(&zram->init_lock); + + return ret; +} + +static DEVICE_ATTR_RO(io_stat); +static DEVICE_ATTR_RO(mm_stat); +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +static DEVICE_ATTR_RO(bd_stat); +#endif +static DEVICE_ATTR_RO(debug_stat); + +static void zram_meta_free(struct zram *zram, u64 disksize) +{ + size_t num_pages = disksize >> PAGE_SHIFT; + size_t index; + + /* Free all pages that are still in this zram device */ + for (index = 0; index < num_pages; index++) + zram_free_page(zram, index); + + zs_destroy_pool(zram->mem_pool); + vfree(zram->table); +} + +static bool zram_meta_alloc(struct zram *zram, u64 disksize) +{ + size_t num_pages; + + num_pages = disksize >> PAGE_SHIFT; + zram->table = vzalloc(array_size(num_pages, sizeof(*zram->table))); + if (!zram->table) + return false; + + zram->mem_pool = zs_create_pool(zram->disk->disk_name); + if (!zram->mem_pool) { + vfree(zram->table); + return false; + } + + if (!huge_class_size) + huge_class_size = zs_huge_class_size(zram->mem_pool); + return true; +} + +/* + * To protect concurrent access to the same index entry, + * caller should hold this table index entry's bit_spinlock to + * indicate this index entry is accessing. + */ +static void zram_free_page(struct zram *zram, size_t index) +{ + unsigned long handle; + +#ifdef CONFIG_HYBRIDSWAP_ZRAM_MEMORY_TRACKING + zram->table[index].ac_time = 0; +#endif + if (zram_test_flag(zram, index, ZRAM_IDLE)) + zram_clear_flag(zram, index, ZRAM_IDLE); + + if (zram_test_flag(zram, index, ZRAM_HUGE)) { + zram_clear_flag(zram, index, ZRAM_HUGE); + atomic64_dec(&zram->stats.huge_pages); + } + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + if (zram_test_flag(zram, index, ZRAM_CACHED)) { + struct page *page = (struct page *)zram_get_page(zram, index); + + del_page_from_cache(page); + page->mem_cgroup = NULL; + put_free_page(page); + zram_clear_flag(zram, index, ZRAM_CACHED); + goto out; + } + + if (zram_test_flag(zram, index, ZRAM_CACHED_COMPRESS)) { + zram_clear_flag(zram, index, ZRAM_CACHED_COMPRESS); + goto out; + } +#endif + +#ifdef CONFIG_HYBRIDSWAP_CORE + hybridswap_untrack(zram, index); +#endif + + if (zram_test_flag(zram, index, ZRAM_WB)) { + zram_clear_flag(zram, index, ZRAM_WB); + free_block_bdev(zram, zram_get_element(zram, index)); + atomic64_dec(&zram->stats.pages_stored); + goto out; + } + + /* + * No memory is allocated for same element filled pages. + * Simply clear same page flag. + */ + if (zram_test_flag(zram, index, ZRAM_SAME)) { + zram_clear_flag(zram, index, ZRAM_SAME); + atomic64_dec(&zram->stats.same_pages); + atomic64_dec(&zram->stats.pages_stored); + goto out; + } + + handle = zram_get_handle(zram, index); + if (!handle) + return; + + zs_free(zram->mem_pool, handle); + + atomic64_sub(zram_get_obj_size(zram, index), + &zram->stats.compr_data_size); + atomic64_dec(&zram->stats.pages_stored); + +out: + zram_set_handle(zram, index, 0); + zram_set_obj_size(zram, index, 0); + WARN_ON_ONCE(zram->table[index].flags & + ~(1UL << ZRAM_LOCK | 1UL << ZRAM_UNDER_WB)); +} + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS +void update_zram_index(struct zram *zram, u32 index, unsigned long page) +{ + zram_slot_lock(zram, index); + put_anon_pages((struct page*)page); + + zram_free_page(zram, index); + zram_set_flag(zram, index, ZRAM_CACHED); + zram_set_page(zram, index, page); + zram_set_obj_size(zram, index, PAGE_SIZE); + zram_slot_unlock(zram, index); +} + +int async_compress_page(struct zram *zram, struct page* page) +{ + int ret = 0; + unsigned long alloced_pages; + unsigned long handle = 0; + unsigned int comp_len = 0; + void *src, *dst; + struct zcomp_strm *zstrm; + int index = get_zram_index(page); + +compress_again: + zram_slot_lock(zram, index); + if (!zram_test_flag(zram, index, ZRAM_CACHED_COMPRESS)) { + zram_slot_unlock(zram, index); + return 0; + } + zram_slot_unlock(zram, index); + + zstrm = zcomp_stream_get(zram->comp); + src = kmap_atomic(page); + ret = zcomp_compress(zstrm, src, &comp_len); + kunmap_atomic(src); + + if (unlikely(ret)) { + zcomp_stream_put(zram->comp); + pr_err("Compression failed! err=%d\n", ret); + zs_free(zram->mem_pool, handle); + return ret; + } + + if (comp_len >= huge_class_size) + comp_len = PAGE_SIZE; + + if (!handle) + handle = zs_malloc(zram->mem_pool, comp_len, + __GFP_KSWAPD_RECLAIM | + __GFP_NOWARN | + __GFP_HIGHMEM | + __GFP_MOVABLE | + __GFP_CMA); + if (!handle) { + zcomp_stream_put(zram->comp); + atomic64_inc(&zram->stats.writestall); + handle = zs_malloc(zram->mem_pool, comp_len, + GFP_NOIO | __GFP_HIGHMEM | + __GFP_MOVABLE | __GFP_CMA); + if (handle) + goto compress_again; + return -ENOMEM; + } + + alloced_pages = zs_get_total_pages(zram->mem_pool); + update_used_max(zram, alloced_pages); + + if (zram->limit_pages && alloced_pages > zram->limit_pages) { + zcomp_stream_put(zram->comp); + zs_free(zram->mem_pool, handle); + return -ENOMEM; + } + + dst = zs_map_object(zram->mem_pool, handle, ZS_MM_WO); + + src = zstrm->buffer; + if (comp_len == PAGE_SIZE) + src = kmap_atomic(page); + memcpy(dst, src, comp_len); + if (comp_len == PAGE_SIZE) + kunmap_atomic(src); + + zcomp_stream_put(zram->comp); + zs_unmap_object(zram->mem_pool, handle); + atomic64_add(comp_len, &zram->stats.compr_data_size); + + /* + * Free memory associated with this sector + * before overwriting unused sectors. + */ + zram_slot_lock(zram, index); + if (!zram_test_flag(zram, index, ZRAM_CACHED_COMPRESS)) { + atomic64_sub(comp_len, &zram->stats.compr_data_size); + zs_free(zram->mem_pool, handle); + zram_slot_unlock(zram, index); + return 0; + } + zram_free_page(zram, index); + + if (comp_len == PAGE_SIZE) { + zram_set_flag(zram, index, ZRAM_HUGE); + atomic64_inc(&zram->stats.huge_pages); + } + + zram_set_handle(zram, index, handle); + zram_set_obj_size(zram, index, comp_len); +#ifdef CONFIG_HYBRIDSWAP_CORE + hybridswap_record(zram, index, page->mem_cgroup); +#endif + zram_slot_unlock(zram, index); + + /* Update stats */ + atomic64_inc(&zram->stats.pages_stored); + + return ret; +} +#endif + +static int __zram_bvec_read(struct zram *zram, struct page *page, u32 index, + struct bio *bio, bool partial_io) +{ + struct zcomp_strm *zstrm; + unsigned long handle; + unsigned int size; + void *src, *dst; + int ret; + + zram_slot_lock(zram, index); + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + if (akcompress_cache_page_fault(zram, page, index)) + return 0; +#endif + +#ifdef CONFIG_HYBRIDSWAP_CORE + if (likely(!bio)) { + ret = hybridswap_page_fault(zram, index); + if (unlikely(ret)) { + pr_err("search in hybridswap failed! err=%d, page=%u\n", + ret, index); + zram_slot_unlock(zram, index); + return ret; + } + } +#endif + + if (zram_test_flag(zram, index, ZRAM_WB)) { + struct bio_vec bvec; + + zram_slot_unlock(zram, index); + + bvec.bv_page = page; + bvec.bv_len = PAGE_SIZE; + bvec.bv_offset = 0; + return read_from_bdev(zram, &bvec, + zram_get_element(zram, index), + bio, partial_io); + } + + handle = zram_get_handle(zram, index); + if (!handle || zram_test_flag(zram, index, ZRAM_SAME)) { + unsigned long value; + void *mem; + + value = handle ? zram_get_element(zram, index) : 0; + mem = kmap_atomic(page); + zram_fill_page(mem, PAGE_SIZE, value); + kunmap_atomic(mem); + zram_slot_unlock(zram, index); + return 0; + } + + size = zram_get_obj_size(zram, index); + + if (size != PAGE_SIZE) + zstrm = zcomp_stream_get(zram->comp); + + src = zs_map_object(zram->mem_pool, handle, ZS_MM_RO); + if (size == PAGE_SIZE) { + dst = kmap_atomic(page); + memcpy(dst, src, PAGE_SIZE); + kunmap_atomic(dst); + ret = 0; + } else { + dst = kmap_atomic(page); + ret = zcomp_decompress(zstrm, src, size, dst); + kunmap_atomic(dst); + zcomp_stream_put(zram->comp); + } + zs_unmap_object(zram->mem_pool, handle); + zram_slot_unlock(zram, index); + + /* Should NEVER happen. Return bio error if it does. */ + if (WARN_ON(ret)) + pr_err("Decompression failed! err=%d, page=%u\n", ret, index); + + return ret; +} + +static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec, + u32 index, int offset, struct bio *bio) +{ + int ret; + struct page *page; + + page = bvec->bv_page; + if (is_partial_io(bvec)) { + /* Use a temporary buffer to decompress the page */ + page = alloc_page(GFP_NOIO|__GFP_HIGHMEM); + if (!page) + return -ENOMEM; + } + + ret = __zram_bvec_read(zram, page, index, bio, is_partial_io(bvec)); + if (unlikely(ret)) + goto out; + + if (is_partial_io(bvec)) { + void *dst = kmap_atomic(bvec->bv_page); + void *src = kmap_atomic(page); + + memcpy(dst + bvec->bv_offset, src + offset, bvec->bv_len); + kunmap_atomic(src); + kunmap_atomic(dst); + } +out: + if (is_partial_io(bvec)) + __free_page(page); + + return ret; +} + +static int __zram_bvec_write(struct zram *zram, struct bio_vec *bvec, + u32 index, struct bio *bio) +{ + int ret = 0; + unsigned long alloced_pages; + unsigned long handle = 0; + unsigned int comp_len = 0; + void *src, *dst, *mem; + struct zcomp_strm *zstrm; + struct page *page = bvec->bv_page; + unsigned long element = 0; + enum zram_pageflags flags = 0; + + mem = kmap_atomic(page); + if (page_same_filled(mem, &element)) { + kunmap_atomic(mem); + /* Free memory associated with this sector now. */ + flags = ZRAM_SAME; + atomic64_inc(&zram->stats.same_pages); + goto out; + } + kunmap_atomic(mem); + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + if ((current_is_kswapd() || current_is_mswapd(current)) && + add_anon_page2cache(zram, index, page)) { + return 0; + } +#endif + +compress_again: + zstrm = zcomp_stream_get(zram->comp); + src = kmap_atomic(page); + ret = zcomp_compress(zstrm, src, &comp_len); + kunmap_atomic(src); + + if (unlikely(ret)) { + zcomp_stream_put(zram->comp); + pr_err("Compression failed! err=%d\n", ret); + zs_free(zram->mem_pool, handle); + return ret; + } + + if (comp_len >= huge_class_size) + comp_len = PAGE_SIZE; + /* + * handle allocation has 2 paths: + * a) fast path is executed with preemption disabled (for + * per-cpu streams) and has __GFP_DIRECT_RECLAIM bit clear, + * since we can't sleep; + * b) slow path enables preemption and attempts to allocate + * the page with __GFP_DIRECT_RECLAIM bit set. we have to + * put per-cpu compression stream and, thus, to re-do + * the compression once handle is allocated. + * + * if we have a 'non-null' handle here then we are coming + * from the slow path and handle has already been allocated. + */ + if (!handle) + handle = zs_malloc(zram->mem_pool, comp_len, + __GFP_KSWAPD_RECLAIM | + __GFP_NOWARN | + __GFP_HIGHMEM | + __GFP_MOVABLE | + __GFP_CMA | + __GFP_OFFLINABLE); // NOTE: __GFP_OFFLINABLE only for QCOM 5.4 kernel + if (!handle) { + zcomp_stream_put(zram->comp); + atomic64_inc(&zram->stats.writestall); + handle = zs_malloc(zram->mem_pool, comp_len, + GFP_NOIO | __GFP_HIGHMEM | + __GFP_MOVABLE | __GFP_CMA | + __GFP_OFFLINABLE); // NOTE: __GFP_OFFLINABLE only for QCOM 5.4 kernel + if (handle) + goto compress_again; + return -ENOMEM; + } + + alloced_pages = zs_get_total_pages(zram->mem_pool); + update_used_max(zram, alloced_pages); + + if (zram->limit_pages && alloced_pages > zram->limit_pages) { + zcomp_stream_put(zram->comp); + zs_free(zram->mem_pool, handle); + return -ENOMEM; + } + + dst = zs_map_object(zram->mem_pool, handle, ZS_MM_WO); + + src = zstrm->buffer; + if (comp_len == PAGE_SIZE) + src = kmap_atomic(page); + memcpy(dst, src, comp_len); + if (comp_len == PAGE_SIZE) + kunmap_atomic(src); + + zcomp_stream_put(zram->comp); + zs_unmap_object(zram->mem_pool, handle); + atomic64_add(comp_len, &zram->stats.compr_data_size); +out: + /* + * Free memory associated with this sector + * before overwriting unused sectors. + */ + zram_slot_lock(zram, index); + zram_free_page(zram, index); + + if (comp_len == PAGE_SIZE) { + zram_set_flag(zram, index, ZRAM_HUGE); + atomic64_inc(&zram->stats.huge_pages); + } + + if (flags) { + zram_set_flag(zram, index, flags); + zram_set_element(zram, index, element); + } else { + zram_set_handle(zram, index, handle); + zram_set_obj_size(zram, index, comp_len); + } + +#ifdef CONFIG_HYBRIDSWAP_CORE + hybridswap_record(zram, index, page->mem_cgroup); +#endif + zram_slot_unlock(zram, index); + + /* Update stats */ + atomic64_inc(&zram->stats.pages_stored); + return ret; +} + +static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, + u32 index, int offset, struct bio *bio) +{ + int ret; + struct page *page = NULL; + void *src; + struct bio_vec vec; + + vec = *bvec; + if (is_partial_io(bvec)) { + void *dst; + /* + * This is a partial IO. We need to read the full page + * before to write the changes. + */ + page = alloc_page(GFP_NOIO|__GFP_HIGHMEM); + if (!page) + return -ENOMEM; + + ret = __zram_bvec_read(zram, page, index, bio, true); + if (ret) + goto out; + + src = kmap_atomic(bvec->bv_page); + dst = kmap_atomic(page); + memcpy(dst + offset, src + bvec->bv_offset, bvec->bv_len); + kunmap_atomic(dst); + kunmap_atomic(src); + + vec.bv_page = page; + vec.bv_len = PAGE_SIZE; + vec.bv_offset = 0; + } + + ret = __zram_bvec_write(zram, &vec, index, bio); +out: + if (is_partial_io(bvec)) + __free_page(page); + return ret; +} + +/* + * zram_bio_discard - handler on discard request + * @index: physical block index in PAGE_SIZE units + * @offset: byte offset within physical block + */ +static void zram_bio_discard(struct zram *zram, u32 index, + int offset, struct bio *bio) +{ + size_t n = bio->bi_iter.bi_size; + + /* + * zram manages data in physical block size units. Because logical block + * size isn't identical with physical block size on some arch, we + * could get a discard request pointing to a specific offset within a + * certain physical block. Although we can handle this request by + * reading that physiclal block and decompressing and partially zeroing + * and re-compressing and then re-storing it, this isn't reasonable + * because our intent with a discard request is to save memory. So + * skipping this logical block is appropriate here. + */ + if (offset) { + if (n <= (PAGE_SIZE - offset)) + return; + + n -= (PAGE_SIZE - offset); + index++; + } + + while (n >= PAGE_SIZE) { + zram_slot_lock(zram, index); + zram_free_page(zram, index); + zram_slot_unlock(zram, index); + atomic64_inc(&zram->stats.notify_free); + index++; + n -= PAGE_SIZE; + } +} + +/* + * Returns errno if it has some problem. Otherwise return 0 or 1. + * Returns 0 if IO request was done synchronously + * Returns 1 if IO request was successfully submitted. + */ +static int zram_bvec_rw(struct zram *zram, struct bio_vec *bvec, u32 index, + int offset, unsigned int op, struct bio *bio) +{ + unsigned long start_time = jiffies; + struct request_queue *q = zram->disk->queue; + int ret; + + generic_start_io_acct(q, op, bvec->bv_len >> SECTOR_SHIFT, + &zram->disk->part0); + + if (!op_is_write(op)) { + atomic64_inc(&zram->stats.num_reads); + ret = zram_bvec_read(zram, bvec, index, offset, bio); + flush_dcache_page(bvec->bv_page); + } else { + atomic64_inc(&zram->stats.num_writes); + ret = zram_bvec_write(zram, bvec, index, offset, bio); + } + + generic_end_io_acct(q, op, &zram->disk->part0, start_time); + + zram_slot_lock(zram, index); + zram_accessed(zram, index); + zram_slot_unlock(zram, index); + + if (unlikely(ret < 0)) { + if (!op_is_write(op)) + atomic64_inc(&zram->stats.failed_reads); + else + atomic64_inc(&zram->stats.failed_writes); + } + + return ret; +} + +static void __zram_make_request(struct zram *zram, struct bio *bio) +{ + int offset; + u32 index; + struct bio_vec bvec; + struct bvec_iter iter; + + index = bio->bi_iter.bi_sector >> SECTORS_PER_PAGE_SHIFT; + offset = (bio->bi_iter.bi_sector & + (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT; + + switch (bio_op(bio)) { + case REQ_OP_DISCARD: + case REQ_OP_WRITE_ZEROES: + zram_bio_discard(zram, index, offset, bio); + bio_endio(bio); + return; + default: + break; + } + + bio_for_each_segment(bvec, bio, iter) { + struct bio_vec bv = bvec; + unsigned int unwritten = bvec.bv_len; + + do { + bv.bv_len = min_t(unsigned int, PAGE_SIZE - offset, + unwritten); + if (zram_bvec_rw(zram, &bv, index, offset, + bio_op(bio), bio) < 0) + goto out; + + bv.bv_offset += bv.bv_len; + unwritten -= bv.bv_len; + + update_position(&index, &offset, &bv); + } while (unwritten); + } + + bio_endio(bio); + return; + +out: + bio_io_error(bio); +} + +/* + * Handler function for all zram I/O requests. + */ +static blk_qc_t zram_make_request(struct request_queue *queue, struct bio *bio) +{ + struct zram *zram = queue->queuedata; + + if (!valid_io_request(zram, bio->bi_iter.bi_sector, + bio->bi_iter.bi_size)) { + atomic64_inc(&zram->stats.invalid_io); + goto error; + } + + __zram_make_request(zram, bio); + return BLK_QC_T_NONE; + +error: + bio_io_error(bio); + return BLK_QC_T_NONE; +} + +static void zram_slot_free_notify(struct block_device *bdev, + unsigned long index) +{ + struct zram *zram; + + zram = bdev->bd_disk->private_data; + + atomic64_inc(&zram->stats.notify_free); + if (!zram_slot_trylock(zram, index)) { + atomic64_inc(&zram->stats.miss_free); + return; + } + +#ifdef CONFIG_HYBRIDSWAP_CORE + if (!hybridswap_delete(zram, index)) { + zram_slot_unlock(zram, index); + atomic64_inc(&zram->stats.miss_free); + return; + } +#endif + zram_free_page(zram, index); + zram_slot_unlock(zram, index); +} + +static int zram_rw_page(struct block_device *bdev, sector_t sector, + struct page *page, unsigned int op) +{ + int offset, ret; + u32 index; + struct zram *zram; + struct bio_vec bv; + + if (PageTransHuge(page)) + return -ENOTSUPP; + zram = bdev->bd_disk->private_data; + + if (!valid_io_request(zram, sector, PAGE_SIZE)) { + atomic64_inc(&zram->stats.invalid_io); + ret = -EINVAL; + goto out; + } + + index = sector >> SECTORS_PER_PAGE_SHIFT; + offset = (sector & (SECTORS_PER_PAGE - 1)) << SECTOR_SHIFT; + + bv.bv_page = page; + bv.bv_len = PAGE_SIZE; + bv.bv_offset = 0; + + ret = zram_bvec_rw(zram, &bv, index, offset, op, NULL); +out: + /* + * If I/O fails, just return error(ie, non-zero) without + * calling page_endio. + * It causes resubmit the I/O with bio request by upper functions + * of rw_page(e.g., swap_readpage, __swap_writepage) and + * bio->bi_end_io does things to handle the error + * (e.g., SetPageError, set_page_dirty and extra works). + */ + if (unlikely(ret < 0)) + return ret; + + switch (ret) { + case 0: + page_endio(page, op_is_write(op), 0); + break; + case 1: + ret = 0; + break; + default: + WARN_ON(1); + } + return ret; +} + +static void zram_reset_device(struct zram *zram) +{ + struct zcomp *comp; + u64 disksize; + + down_write(&zram->init_lock); + + zram->limit_pages = 0; + + if (!init_done(zram)) { + up_write(&zram->init_lock); + return; + } + + comp = zram->comp; + disksize = zram->disksize; + zram->disksize = 0; + + set_capacity(zram->disk, 0); + part_stat_set_all(&zram->disk->part0, 0); + + up_write(&zram->init_lock); + /* I/O operation under all of CPU are done so let's free */ + zram_meta_free(zram, disksize); + memset(&zram->stats, 0, sizeof(zram->stats)); + zcomp_destroy(comp); + reset_bdev(zram); +} + +static ssize_t disksize_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + u64 disksize; + struct zcomp *comp; + struct zram *zram = dev_to_zram(dev); + int err; + + disksize = memparse(buf, NULL); + if (!disksize) + return -EINVAL; + + down_write(&zram->init_lock); + if (init_done(zram)) { + pr_info("Cannot change disksize for initialized device\n"); + err = -EBUSY; + goto out_unlock; + } + + disksize = PAGE_ALIGN(disksize); + if (!zram_meta_alloc(zram, disksize)) { + err = -ENOMEM; + goto out_unlock; + } + + comp = zcomp_create(zram->compressor); + if (IS_ERR(comp)) { + pr_err("Cannot initialise %s compressing backend\n", + zram->compressor); + err = PTR_ERR(comp); + goto out_free_meta; + } + + zram->comp = comp; + zram->disksize = disksize; + set_capacity(zram->disk, zram->disksize >> SECTOR_SHIFT); + + revalidate_disk(zram->disk); + up_write(&zram->init_lock); + + return len; + +out_free_meta: + zram_meta_free(zram, disksize); +out_unlock: + up_write(&zram->init_lock); + return err; +} + +static ssize_t reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + int ret; + unsigned short do_reset; + struct zram *zram; + struct block_device *bdev; + + ret = kstrtou16(buf, 10, &do_reset); + if (ret) + return ret; + + if (!do_reset) + return -EINVAL; + + zram = dev_to_zram(dev); + bdev = bdget_disk(zram->disk, 0); + if (!bdev) + return -ENOMEM; + + mutex_lock(&bdev->bd_mutex); + /* Do not reset an active device or claimed device */ + if (bdev->bd_openers || zram->claim) { + mutex_unlock(&bdev->bd_mutex); + bdput(bdev); + return -EBUSY; + } + + /* From now on, anyone can't open /dev/zram[0-9] */ + zram->claim = true; + mutex_unlock(&bdev->bd_mutex); + + /* Make sure all the pending I/O are finished */ + fsync_bdev(bdev); + zram_reset_device(zram); + revalidate_disk(zram->disk); + bdput(bdev); + + mutex_lock(&bdev->bd_mutex); + zram->claim = false; + mutex_unlock(&bdev->bd_mutex); + + return len; +} + +static int zram_open(struct block_device *bdev, fmode_t mode) +{ + int ret = 0; + struct zram *zram; + + WARN_ON(!mutex_is_locked(&bdev->bd_mutex)); + + zram = bdev->bd_disk->private_data; + /* zram was claimed to reset so open request fails */ + if (zram->claim) + ret = -EBUSY; + + return ret; +} + +static const struct block_device_operations zram_devops = { + .open = zram_open, + .swap_slot_free_notify = zram_slot_free_notify, + .rw_page = zram_rw_page, + .owner = THIS_MODULE +}; + +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +static const struct block_device_operations zram_wb_devops = { + .open = zram_open, + .submit_bio = zram_submit_bio, + .swap_slot_free_notify = zram_slot_free_notify, + .owner = THIS_MODULE +}; +#endif + +static DEVICE_ATTR_WO(compact); +static DEVICE_ATTR_RW(disksize); +static DEVICE_ATTR_RO(initstate); +static DEVICE_ATTR_WO(reset); +static DEVICE_ATTR_WO(mem_limit); +static DEVICE_ATTR_WO(mem_used_max); +static DEVICE_ATTR_WO(idle); +static DEVICE_ATTR_RW(max_comp_streams); +static DEVICE_ATTR_RW(comp_algorithm); +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK +static DEVICE_ATTR_RW(backing_dev); +static DEVICE_ATTR_WO(writeback); +static DEVICE_ATTR_RW(writeback_limit); +static DEVICE_ATTR_RW(writeback_limit_enable); +#endif +#ifdef CONFIG_HYBRIDSWAP +static DEVICE_ATTR_RO(hybridswap_vmstat); +static DEVICE_ATTR_RW(hybridswap_loglevel); +static DEVICE_ATTR_RW(hybridswap_enable); +#endif +#ifdef CONFIG_HYBRIDSWAP_SWAPD +static DEVICE_ATTR_RW(hybridswap_swapd_pause); +#endif +#ifdef CONFIG_HYBRIDSWAP_CORE +static DEVICE_ATTR_RW(hybridswap_core_enable); +static DEVICE_ATTR_RW(hybridswap_loop_device); +static DEVICE_ATTR_RW(hybridswap_dev_life); +static DEVICE_ATTR_RW(hybridswap_quota_day); +static DEVICE_ATTR_RO(hybridswap_report); +static DEVICE_ATTR_RO(hybridswap_stat_snap); +static DEVICE_ATTR_RO(hybridswap_meminfo); +static DEVICE_ATTR_RW(hybridswap_zram_increase); +#endif +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS +static DEVICE_ATTR_RW(hybridswap_akcompress); +#endif + +static struct attribute *zram_disk_attrs[] = { + &dev_attr_disksize.attr, + &dev_attr_initstate.attr, + &dev_attr_reset.attr, + &dev_attr_compact.attr, + &dev_attr_mem_limit.attr, + &dev_attr_mem_used_max.attr, + &dev_attr_idle.attr, + &dev_attr_max_comp_streams.attr, + &dev_attr_comp_algorithm.attr, +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK + &dev_attr_backing_dev.attr, + &dev_attr_writeback.attr, + &dev_attr_writeback_limit.attr, + &dev_attr_writeback_limit_enable.attr, +#endif + &dev_attr_io_stat.attr, + &dev_attr_mm_stat.attr, +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK + &dev_attr_bd_stat.attr, +#endif + &dev_attr_debug_stat.attr, +#ifdef CONFIG_HYBRIDSWAP + &dev_attr_hybridswap_vmstat.attr, + &dev_attr_hybridswap_loglevel.attr, + &dev_attr_hybridswap_enable.attr, +#endif +#ifdef CONFIG_HYBRIDSWAP_SWAPD + &dev_attr_hybridswap_swapd_pause.attr, +#endif +#ifdef CONFIG_HYBRIDSWAP_CORE + &dev_attr_hybridswap_core_enable.attr, + &dev_attr_hybridswap_report.attr, + &dev_attr_hybridswap_meminfo.attr, + &dev_attr_hybridswap_stat_snap.attr, + &dev_attr_hybridswap_loop_device.attr, + &dev_attr_hybridswap_dev_life.attr, + &dev_attr_hybridswap_quota_day.attr, + &dev_attr_hybridswap_zram_increase.attr, +#endif +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + &dev_attr_hybridswap_akcompress.attr, +#endif + NULL, +}; + +static const struct attribute_group zram_disk_attr_group = { + .attrs = zram_disk_attrs, +}; + +static const struct attribute_group *zram_disk_attr_groups[] = { + &zram_disk_attr_group, + NULL, +}; + +/* + * Allocate and initialize new zram device. the function returns + * '>= 0' device_id upon success, and negative value otherwise. + */ +static int zram_add(void) +{ + struct zram *zram; + struct request_queue *queue; + int ret, device_id; + + zram = kzalloc(sizeof(struct zram), GFP_KERNEL); + if (!zram) + return -ENOMEM; + + ret = idr_alloc(&zram_index_idr, zram, 0, 0, GFP_KERNEL); + if (ret < 0) + goto out_free_dev; + device_id = ret; + + init_rwsem(&zram->init_lock); +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK + spin_lock_init(&zram->wb_limit_lock); +#endif + queue = blk_alloc_queue(GFP_KERNEL); + if (!queue) { + pr_err("Error allocating disk queue for device %d\n", + device_id); + ret = -ENOMEM; + goto out_free_idr; + } + + blk_queue_make_request(queue, zram_make_request); + + /* gendisk structure */ + zram->disk = alloc_disk(1); + if (!zram->disk) { + pr_err("Error allocating disk structure for device %d\n", + device_id); + ret = -ENOMEM; + goto out_free_queue; + } + + zram->disk->major = zram_major; + zram->disk->first_minor = device_id; + zram->disk->fops = &zram_devops; + zram->disk->queue = queue; + zram->disk->queue->queuedata = zram; + zram->disk->private_data = zram; + snprintf(zram->disk->disk_name, 16, "zram%d", device_id); + + /* Actual capacity set using syfs (/sys/block/zram/disksize */ + set_capacity(zram->disk, 0); + /* zram devices sort of resembles non-rotational disks */ + blk_queue_flag_set(QUEUE_FLAG_NONROT, zram->disk->queue); + blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, zram->disk->queue); + + /* + * To ensure that we always get PAGE_SIZE aligned + * and n*PAGE_SIZED sized I/O requests. + */ + blk_queue_physical_block_size(zram->disk->queue, PAGE_SIZE); + blk_queue_logical_block_size(zram->disk->queue, + ZRAM_LOGICAL_BLOCK_SIZE); + blk_queue_io_min(zram->disk->queue, PAGE_SIZE); + blk_queue_io_opt(zram->disk->queue, PAGE_SIZE); + zram->disk->queue->limits.discard_granularity = PAGE_SIZE; + blk_queue_max_discard_sectors(zram->disk->queue, UINT_MAX); + blk_queue_flag_set(QUEUE_FLAG_DISCARD, zram->disk->queue); + + /* + * zram_bio_discard() will clear all logical blocks if logical block + * size is identical with physical block size(PAGE_SIZE). But if it is + * different, we will skip discarding some parts of logical blocks in + * the part of the request range which isn't aligned to physical block + * size. So we can't ensure that all discarded logical blocks are + * zeroed. + */ + if (ZRAM_LOGICAL_BLOCK_SIZE == PAGE_SIZE) + blk_queue_max_write_zeroes_sectors(zram->disk->queue, UINT_MAX); + + zram->disk->queue->backing_dev_info->capabilities |= + (BDI_CAP_STABLE_WRITES | BDI_CAP_SYNCHRONOUS_IO); + device_add_disk(NULL, zram->disk, zram_disk_attr_groups); + + strlcpy(zram->compressor, default_compressor, sizeof(zram->compressor)); + + zram_debugfs_register(zram); + pr_info("Added device: %s\n", zram->disk->disk_name); + return device_id; + +out_free_queue: + blk_cleanup_queue(queue); +out_free_idr: + idr_remove(&zram_index_idr, device_id); +out_free_dev: + kfree(zram); + return ret; +} + +static int zram_remove(struct zram *zram) +{ + struct block_device *bdev; + + bdev = bdget_disk(zram->disk, 0); + if (!bdev) + return -ENOMEM; + + mutex_lock(&bdev->bd_mutex); + if (bdev->bd_openers || zram->claim) { + mutex_unlock(&bdev->bd_mutex); + bdput(bdev); + return -EBUSY; + } + + zram->claim = true; + mutex_unlock(&bdev->bd_mutex); + + zram_debugfs_unregister(zram); + + /* Make sure all the pending I/O are finished */ + fsync_bdev(bdev); + zram_reset_device(zram); + bdput(bdev); + + pr_info("Removed device: %s\n", zram->disk->disk_name); + + del_gendisk(zram->disk); + blk_cleanup_queue(zram->disk->queue); + put_disk(zram->disk); + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + destroy_akcompressd_task(zram); +#endif + kfree(zram); + return 0; +} + +/* zram-control sysfs attributes */ + +/* + * NOTE: hot_add attribute is not the usual read-only sysfs attribute. In a + * sense that reading from this file does alter the state of your system -- it + * creates a new un-initialized zram device and returns back this device's + * device_id (or an error code if it fails to create a new device). + */ +static ssize_t hot_add_show(struct class *class, + struct class_attribute *attr, + char *buf) +{ + int ret; + + mutex_lock(&zram_index_mutex); + ret = zram_add(); + mutex_unlock(&zram_index_mutex); + + if (ret < 0) + return ret; + return scnprintf(buf, PAGE_SIZE, "%d\n", ret); +} +static struct class_attribute class_attr_hot_add = + __ATTR(hot_add, 0400, hot_add_show, NULL); + +static ssize_t hot_remove_store(struct class *class, + struct class_attribute *attr, + const char *buf, + size_t count) +{ + struct zram *zram; + int ret, dev_id; + + /* dev_id is gendisk->first_minor, which is `int' */ + ret = kstrtoint(buf, 10, &dev_id); + if (ret) + return ret; + if (dev_id < 0) + return -EINVAL; + + mutex_lock(&zram_index_mutex); + + zram = idr_find(&zram_index_idr, dev_id); + if (zram) { + ret = zram_remove(zram); + if (!ret) + idr_remove(&zram_index_idr, dev_id); + } else { + ret = -ENODEV; + } + + mutex_unlock(&zram_index_mutex); + return ret ? ret : count; +} +static CLASS_ATTR_WO(hot_remove); + +static struct attribute *zram_control_class_attrs[] = { + &class_attr_hot_add.attr, + &class_attr_hot_remove.attr, + NULL, +}; +ATTRIBUTE_GROUPS(zram_control_class); + +static struct class zram_control_class = { + .name = "zram-control", + .owner = THIS_MODULE, + .class_groups = zram_control_class_groups, +}; + +static int zram_remove_cb(int id, void *ptr, void *data) +{ + zram_remove(ptr); + return 0; +} + +static void destroy_devices(void) +{ + class_unregister(&zram_control_class); + idr_for_each(&zram_index_idr, &zram_remove_cb, NULL); + zram_debugfs_destroy(); + idr_destroy(&zram_index_idr); + unregister_blkdev(zram_major, "zram"); + cpuhp_remove_multi_state(CPUHP_ZCOMP_PREPARE); +} + +static int __init zram_init(void) +{ + int ret; + + ret = cpuhp_setup_state_multi(CPUHP_ZCOMP_PREPARE, "block/zram:prepare", + zcomp_cpu_up_prepare, zcomp_cpu_dead); + if (ret < 0) + return ret; + + ret = class_register(&zram_control_class); + if (ret) { + pr_err("Unable to register zram-control class\n"); + cpuhp_remove_multi_state(CPUHP_ZCOMP_PREPARE); + return ret; + } + + zram_debugfs_create(); + zram_major = register_blkdev(0, "zram"); + if (zram_major <= 0) { + pr_err("Unable to get major number\n"); + class_unregister(&zram_control_class); + cpuhp_remove_multi_state(CPUHP_ZCOMP_PREPARE); + return -EBUSY; + } + + while (num_devices != 0) { + mutex_lock(&zram_index_mutex); + ret = zram_add(); + mutex_unlock(&zram_index_mutex); + if (ret < 0) + goto out_error; + num_devices--; + } + +#ifdef CONFIG_HYBRIDSWAP + ret = hybridswap_pre_init(); + if (ret) + goto out_error; +#endif + return 0; + +out_error: + destroy_devices(); + return ret; +} + +static void __exit zram_exit(void) +{ + destroy_devices(); +} + +module_init(zram_init); +module_exit(zram_exit); + +module_param(num_devices, uint, 0); +MODULE_PARM_DESC(num_devices, "Number of pre-created zram devices"); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Nitin Gupta "); +MODULE_DESCRIPTION("Compressed RAM Block Device"); diff --git a/drivers/moto_swap/zram-5.4/zram_drv.h b/drivers/moto_swap/zram-5.4/zram_drv.h new file mode 100644 index 000000000000..7e8e5ab8c148 --- /dev/null +++ b/drivers/moto_swap/zram-5.4/zram_drv.h @@ -0,0 +1,150 @@ +/* + * Compressed RAM block device + * + * Copyright (C) 2008, 2009, 2010 Nitin Gupta + * 2012, 2013 Minchan Kim + * + * This code is released using a dual license strategy: BSD/GPL + * You can choose the licence that better fits your requirements. + * + * Released under the terms of 3-clause BSD License + * Released under the terms of GNU General Public License Version 2.0 + * + */ + +#ifndef _ZRAM_DRV_H_ +#define _ZRAM_DRV_H_ + +#include +#include +#include + +#include "zcomp.h" + +#define SECTORS_PER_PAGE_SHIFT (PAGE_SHIFT - SECTOR_SHIFT) +#define SECTORS_PER_PAGE (1 << SECTORS_PER_PAGE_SHIFT) +#define ZRAM_LOGICAL_BLOCK_SHIFT 12 +#define ZRAM_LOGICAL_BLOCK_SIZE (1 << ZRAM_LOGICAL_BLOCK_SHIFT) +#define ZRAM_SECTOR_PER_LOGICAL_BLOCK \ + (1 << (ZRAM_LOGICAL_BLOCK_SHIFT - SECTOR_SHIFT)) + + +/* + * The lower ZRAM_FLAG_SHIFT bits of table.flags is for + * object size (excluding header), the higher bits is for + * zram_pageflags. + * + * zram is mainly used for memory efficiency so we want to keep memory + * footprint small so we can squeeze size and flags into a field. + * The lower ZRAM_FLAG_SHIFT bits is for object size (excluding header), + * the higher bits is for zram_pageflags. + */ +#define ZRAM_FLAG_SHIFT 24 + +/* Flags for zram pages (table[page_no].flags) */ +enum zram_pageflags { + /* zram slot is locked */ + ZRAM_LOCK = ZRAM_FLAG_SHIFT, + ZRAM_SAME, /* Page consists the same element */ + ZRAM_WB, /* page is stored on backing_device */ + ZRAM_UNDER_WB, /* page is under writeback */ + ZRAM_HUGE, /* Incompressible page */ + ZRAM_IDLE, /* not accessed page since last idle marking */ +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + ZRAM_CACHED, /* page is cached in async compress cache buffer */ + ZRAM_CACHED_COMPRESS, /* page is under async compress */ +#endif +#ifdef CONFIG_HYBRIDSWAP_CORE + ZRAM_BATCHING_OUT, + ZRAM_FROM_HYBRIDSWAP, + ZRAM_MCGID_CLEAR, + ZRAM_IN_BD, /* zram stored in back device */ +#endif + __NR_ZRAM_PAGEFLAGS, +}; + +/*-- Data structures */ + +/* Allocated for each disk page */ +struct zram_table_entry { + union { + unsigned long handle; + unsigned long element; +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS + unsigned long page; +#endif + }; + unsigned long flags; +#ifdef CONFIG_HYBRIDSWAP_ZRAM_MEMORY_TRACKING + ktime_t ac_time; +#endif +}; + +struct zram_stats { + atomic64_t compr_data_size; /* compressed size of pages stored */ + atomic64_t num_reads; /* failed + successful */ + atomic64_t num_writes; /* --do-- */ + atomic64_t failed_reads; /* can happen when memory is too low */ + atomic64_t failed_writes; /* can happen when memory is too low */ + atomic64_t invalid_io; /* non-page-aligned I/O requests */ + atomic64_t notify_free; /* no. of swap slot free notifications */ + atomic64_t same_pages; /* no. of same element filled pages */ + atomic64_t huge_pages; /* no. of huge pages */ + atomic64_t pages_stored; /* no. of pages currently stored */ + atomic_long_t max_used_pages; /* no. of maximum pages stored */ + atomic64_t writestall; /* no. of write slow paths */ + atomic64_t miss_free; /* no. of missed free */ +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK + atomic64_t bd_count; /* no. of pages in backing device */ + atomic64_t bd_reads; /* no. of reads from backing device */ + atomic64_t bd_writes; /* no. of writes from backing device */ +#endif +}; + +struct zram { + struct zram_table_entry *table; + struct zs_pool *mem_pool; + struct zcomp *comp; + struct gendisk *disk; + /* Prevent concurrent execution of device init */ + struct rw_semaphore init_lock; + /* + * the number of pages zram can consume for storing compressed data + */ + unsigned long limit_pages; + + struct zram_stats stats; + /* + * This is the limit on amount of *uncompressed* worth of data + * we can store in a disk. + */ + u64 disksize; /* bytes */ + char compressor[CRYPTO_MAX_ALG_NAME]; + /* + * zram is claimed so open request will be failed + */ + bool claim; /* Protected by bdev->bd_mutex */ + struct file *backing_dev; +#ifdef CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK + spinlock_t wb_limit_lock; + bool wb_limit_enable; + u64 bd_wb_limit; + struct block_device *bdev; + unsigned int old_block_size; + unsigned long *bitmap; + unsigned long nr_pages; +#endif +#ifdef CONFIG_HYBRIDSWAP_ZRAM_MEMORY_TRACKING + struct dentry *debugfs_dir; +#endif +#if (defined CONFIG_HYBRIDSWAP_ZRAM_WRITEBACK) || (defined CONFIG_HYBRIDSWAP_CORE) + struct block_device *bdev; + unsigned int old_block_size; + unsigned long nr_pages; + unsigned long increase_nr_pages; +#endif +#ifdef CONFIG_HYBRIDSWAP_CORE + struct hyb_info *infos; +#endif +}; +#endif diff --git a/drivers/moto_swap/zram-5.4/zram_drv_internal.h b/drivers/moto_swap/zram-5.4/zram_drv_internal.h new file mode 100644 index 000000000000..3c102cf38773 --- /dev/null +++ b/drivers/moto_swap/zram-5.4/zram_drv_internal.h @@ -0,0 +1,39 @@ +#ifndef _ZRAM_DRV_INTERNAL_H_ +#define _ZRAM_DRV_INTERNAL_H_ +#ifdef BIT +#undef BIT +#define BIT(nr) (1lu << (nr)) +#endif + +#define zram_slot_lock(zram, index) (bit_spin_lock(ZRAM_LOCK, &zram->table[index].flags)) + +#define zram_slot_unlock(zram, index) (bit_spin_unlock(ZRAM_LOCK, &zram->table[index].flags)) + +#define init_done(zram) (zram->disksize) + +#define dev_to_zram(dev) ((struct zram *)dev_to_disk(dev)->private_data) + +#define zram_get_handle(zram, index) (zram->table[index].handle) + +#define zram_set_handle(zram, index, handle_val) (zram->table[index].handle = handle_val) + +#define zram_test_flag(zram, index, flag) (zram->table[index].flags & BIT(flag)) + +#define zram_set_flag(zram, index, flag) (zram->table[index].flags |= BIT(flag)) + +#define zram_clear_flag(zram, index, flag) (zram->table[index].flags &= ~BIT(flag)) + +#define zram_set_element(zram, index, element) (zram->table[index].element = element) + +#define zram_get_obj_size(zram, index) (zram->table[index].flags & (BIT(ZRAM_FLAG_SHIFT) - 1)) + +#define zram_set_obj_size(zram, index, size) do {\ + unsigned long flags = zram->table[index].flags >> ZRAM_FLAG_SHIFT; \ + zram->table[index].flags = (flags << ZRAM_FLAG_SHIFT) | size; \ +} while(0) + +#ifdef CONFIG_HYBRIDSWAP_ASYNC_COMPRESS +extern int async_compress_page(struct zram *zram, struct page* page); +extern void update_zram_index(struct zram *zram, u32 index, unsigned long page); +#endif +#endif diff --git a/drivers/nfc/mmi_nfc_clk/Android.mk b/drivers/nfc/mmi_nfc_clk/Android.mk new file mode 100644 index 000000000000..6eb9505a36eb --- /dev/null +++ b/drivers/nfc/mmi_nfc_clk/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_nfc_clk.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/nfc/mmi_nfc_clk/Kbuild b/drivers/nfc/mmi_nfc_clk/Kbuild new file mode 100644 index 000000000000..0437959eb2e4 --- /dev/null +++ b/drivers/nfc/mmi_nfc_clk/Kbuild @@ -0,0 +1,4 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall + +obj-m += mmi_nfc_clk.o diff --git a/drivers/nfc/mmi_nfc_clk/Makefile b/drivers/nfc/mmi_nfc_clk/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/nfc/mmi_nfc_clk/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/nfc/mmi_nfc_clk/mmi_nfc_clk.c b/drivers/nfc/mmi_nfc_clk/mmi_nfc_clk.c new file mode 100644 index 000000000000..7c83be3745eb --- /dev/null +++ b/drivers/nfc/mmi_nfc_clk/mmi_nfc_clk.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_VERSION "0.0.1" + +struct mmi_nfc_clk_drvdata { + struct device *dev; + unsigned int clkreq_gpio; + unsigned int clkreq_irq; +}; + +static irqreturn_t mmi_nfc_clk_gpio_isr(int irq, void *dev_id) +{ + // 0x00005346 LN_BB_CLK3_EN_CTL is set to 0x01 + // Software control is off + // PC_POLARITY - pin control polarity - logic high enables clock + // FOLLOW_PC_EN - pin control enable is on + // + // PM660 GPIO 04 will trigger this ISR. As soon as MSM and PM660 is + // awake BB_CLK3 is enabled. No additional action is needed here. + return IRQ_HANDLED; +} + +static int read_setup_clkreq_irq(struct platform_device *pdev, + struct mmi_nfc_clk_drvdata *drvdata) +{ + int rc = 0; + int gpio; + int irq; + const char *desc = "nfc_clkreq"; + + gpio = of_get_named_gpio(pdev->dev.of_node, "mmi,clkreq_gpio", 0); + if (gpio < 0) { + rc = gpio; + dev_err(&pdev->dev, "probe: clkreq gpio not found, error=%d\n", rc); + goto end; + } + drvdata->clkreq_gpio = gpio; + + rc = devm_gpio_request_one(&pdev->dev, drvdata->clkreq_gpio, + GPIOF_IN, desc); + + if (rc < 0) { + dev_err(&pdev->dev, "probe: gpio (%d) request failed, error %d\n", + drvdata->clkreq_gpio, rc); + goto end; + } + + irq = gpio_to_irq(drvdata->clkreq_gpio); + if (irq < 0) { + rc = irq; + dev_err(&pdev->dev, "probe: get irq failed for gpio %d, error %d\n", + drvdata->clkreq_gpio, rc); + goto end; + } + dev_dbg(&pdev->dev, "probe: gpio %d, irq %d\n", drvdata->clkreq_gpio, irq); + + drvdata->clkreq_irq = irq; + rc = devm_request_irq(&pdev->dev, drvdata->clkreq_irq, + mmi_nfc_clk_gpio_isr, IRQF_TRIGGER_RISING, + desc, drvdata); + + if (rc < 0) { + dev_err(&pdev->dev, "probe: unable to claim irq %d; error %d\n", + drvdata->clkreq_irq, rc); + goto end; + } + + enable_irq_wake(drvdata->clkreq_irq); + +end: + return rc; +} + + +static int mmi_nfc_clk_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mmi_nfc_clk_drvdata *drvdata; + int rc = 0; + + dev_dbg(&pdev->dev, "%s begin\n", __func__); + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->dev = &pdev->dev; + platform_set_drvdata(pdev, drvdata); + + rc = read_setup_clkreq_irq(pdev, drvdata); + if (rc < 0) { + dev_err(&pdev->dev, "probe: failed rc=%d\n", rc); + return rc; + } + + device_init_wakeup(&pdev->dev, 1); + dev_info(&pdev->dev, "probe: success !\n"); + return rc; +} + +static int mmi_nfc_clk_remove(struct platform_device *pdev) +{ + device_init_wakeup(&pdev->dev, 0); + return 0; +} + + +static const struct of_device_id mmi_nfc_clk_match[] = { + { .compatible = "mmi,nfc-clk" }, + {} +}; + +static struct platform_driver mmi_nfc_clk_plat_driver = { + .probe = mmi_nfc_clk_probe, + .remove = mmi_nfc_clk_remove, + .driver = { + .name = "mmi_nfc_clk", + .owner = THIS_MODULE, + .of_match_table = mmi_nfc_clk_match, + }, +}; + +module_platform_driver(mmi_nfc_clk_plat_driver); + + +MODULE_AUTHOR("Motorola Mobiity"); +MODULE_DESCRIPTION("NFC Clock Enable/Disable driver"); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/nfc/sec_nfc/Android.mk b/drivers/nfc/sec_nfc/Android.mk new file mode 100644 index 000000000000..b65f863687cf --- /dev/null +++ b/drivers/nfc/sec_nfc/Android.mk @@ -0,0 +1,19 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + + +ifeq ($(SEC_NFC_PRODUCT_N3), true) + KERNEL_CFLAGS += CONFIG_SEC_NFC_PRODUCT_N3=y +else + KERNEL_CFLAGS += CONFIG_SEC_NFC_PRODUCT_N5=y +endif +KERNEL_CFLAGS += CONFIG_SEC_NFC_PRODUCT_N5=y +KERNEL_CFLAGS += CONFIG_SEC_NFC_IF_I2C=y + +include $(CLEAR_VARS) +LOCAL_MODULE := sec_nfc.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_info.ko +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/nfc/sec_nfc/Kbuild b/drivers/nfc/sec_nfc/Kbuild new file mode 100644 index 000000000000..6493f043054b --- /dev/null +++ b/drivers/nfc/sec_nfc/Kbuild @@ -0,0 +1,14 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +ifneq ($(filter m y,$(CONFIG_SEC_NFC_PRODUCT_N5)),) + EXTRA_CFLAGS += -DCONFIG_SEC_NFC_PRODUCT_N5 +endif +ifneq ($(filter m y,$(CONFIG_SEC_NFC_IF_I2C)),) + EXTRA_CFLAGS += -DCONFIG_SEC_NFC_IF_I2C +endif + +obj-m += sec_nfc.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../mmi_info/Module.symvers diff --git a/drivers/nfc/sec_nfc/Makefile b/drivers/nfc/sec_nfc/Makefile new file mode 100644 index 000000000000..4916d32b6d02 --- /dev/null +++ b/drivers/nfc/sec_nfc/Makefile @@ -0,0 +1,17 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS := $(M)/../../mmi_info/Module.symvers + diff --git a/drivers/nfc/sec_nfc/sec_nfc.c b/drivers/nfc/sec_nfc/sec_nfc.c new file mode 100644 index 000000000000..704fc813f1a2 --- /dev/null +++ b/drivers/nfc/sec_nfc/sec_nfc.c @@ -0,0 +1,1156 @@ +/* + * SAMSUNG NFC Controller + * + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Author: Woonki Lee + * Heejae Kim + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program + * + */ +#ifdef CONFIG_SEC_NFC_IF_I2C_GPIO +#define CONFIG_SEC_NFC_IF_I2C +#endif + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_SEC_NFC_GPIO_CLK +#include +#endif +#ifdef CONFIG_SEC_NFC_WAKE_LOCK +#include +#endif +#include +#include + +#ifndef CONFIG_SEC_NFC_IF_I2C +struct sec_nfc_i2c_info {}; +#define sec_nfc_read NULL +#define sec_nfc_write NULL +#define sec_nfc_poll NULL +#define sec_nfc_i2c_irq_clear(x) + +#define SEC_NFC_GET_INFO(dev) platform_get_drvdata(to_platform_device(dev)) + +#else /* CONFIG_SEC_NFC_IF_I2C */ +#include +#include +#include +#include +#include "sec_nfc.h" + +#define SEC_NFC_GET_INFO(dev) i2c_get_clientdata(to_i2c_client(dev)) +enum sec_nfc_irq { + SEC_NFC_SKIP = -1, + SEC_NFC_NONE, + SEC_NFC_INT, + SEC_NFC_READ_TIMES, +}; + +struct sec_nfc_i2c_info { + struct i2c_client *i2c_dev; + struct mutex read_mutex; + enum sec_nfc_irq read_irq; + wait_queue_head_t read_wait; + size_t buflen; + u8 *buf; +}; +#endif + +struct sec_nfc_info { + struct miscdevice miscdev; + struct mutex mutex; + enum sec_nfc_mode mode; + struct device *dev; + struct sec_nfc_platform_data *pdata; + struct sec_nfc_i2c_info i2c_info; +#ifdef CONFIG_SEC_NFC_WAKE_LOCK + struct wake_lock nfc_wake_lock; +#endif +#ifdef CONFIG_SEC_NFC_GPIO_CLK + bool clk_ctl; + bool clk_state; +#endif +#ifdef CONFIG_SEC_NFC_DEDICATED_CLK + void __iomem *clkctrl; +#endif +}; + +#ifdef CONFIG_SEC_ESE_COLDRESET + struct mutex coldreset_mutex; + struct mutex sleep_wake_mutex; + bool sleep_wakeup_state[2]; + u8 disable_combo_reset_cmd[4] = {0x2F, 0x30, 0x01, 0x00}; + bool is_shutdown = false; + enum sec_nfc_mode cur_mode; +#endif + +#ifdef CONFIG_SEC_NFC_IF_I2C +static irqreturn_t sec_nfc_irq_thread_fn(int irq, void *dev_id) +{ + struct sec_nfc_info *info = dev_id; + struct sec_nfc_platform_data *pdata = info->pdata; + + dev_dbg(info->dev, "[NFC] Read Interrupt is occurred!\n"); + + if (gpio_get_value(pdata->irq) == 0) { + dev_err(info->dev, "[NFC] Warning,irq-gpio state is low!\n"); + return IRQ_HANDLED; + } + + mutex_lock(&info->i2c_info.read_mutex); + /* Skip interrupt during power switching + * It is released after first write */ + if (info->i2c_info.read_irq == SEC_NFC_SKIP) { + dev_dbg(info->dev, "%s: Now power swiching. Skip this IRQ\n", __func__); + mutex_unlock(&info->i2c_info.read_mutex); + return IRQ_HANDLED; + } + + info->i2c_info.read_irq += SEC_NFC_READ_TIMES; + mutex_unlock(&info->i2c_info.read_mutex); + + wake_up_interruptible(&info->i2c_info.read_wait); +#ifdef CONFIG_SEC_NFC_WAKE_LOCK + wake_lock_timeout(&info->nfc_wake_lock, 2 * HZ); +#endif + + return IRQ_HANDLED; +} + +static ssize_t sec_nfc_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct sec_nfc_info *info = container_of(file->private_data, + struct sec_nfc_info, miscdev); + enum sec_nfc_irq irq; + int ret = 0; + + dev_dbg(info->dev, "%s: info: %p, count: %zu\n", __func__, + info, count); + + mutex_lock(&info->mutex); + if (info->mode == SEC_NFC_MODE_OFF) { + dev_err(info->dev, "sec_nfc is not enabled\n"); + ret = -ENODEV; + goto out; + } + + mutex_lock(&info->i2c_info.read_mutex); + if (count == 0) { + if (info->i2c_info.read_irq >= SEC_NFC_INT) + info->i2c_info.read_irq--; + mutex_unlock(&info->i2c_info.read_mutex); + goto out; + } + + irq = info->i2c_info.read_irq; + mutex_unlock(&info->i2c_info.read_mutex); + if (irq == SEC_NFC_NONE) { + if (file->f_flags & O_NONBLOCK) { + dev_err(info->dev, "it is nonblock\n"); + ret = -EAGAIN; + goto out; + } + } + + /* i2c recv */ + if (count > info->i2c_info.buflen) + count = info->i2c_info.buflen; + + if (count > SEC_NFC_MSG_MAX_SIZE) { + dev_err(info->dev, "user required wrong size :%d\n", (u32)count); + ret = -EINVAL; + goto out; + } + + mutex_lock(&info->i2c_info.read_mutex); + memset(info->i2c_info.buf, 0, count); + ret = i2c_master_recv(info->i2c_info.i2c_dev, info->i2c_info.buf, (u32)count); + dev_dbg(info->dev, "recv size : %d\n", ret); + + if (ret == -EREMOTEIO) { + ret = -ERESTART; + goto read_error; + } + else if (ret != count) { + dev_err(info->dev, "read failed: return: %d count: %d\n", + ret, (u32)count); + /* ret = -EREMOTEIO; */ + goto read_error; + } + + if (info->i2c_info.read_irq >= SEC_NFC_INT) + info->i2c_info.read_irq--; + + if (info->i2c_info.read_irq == SEC_NFC_READ_TIMES) + wake_up_interruptible(&info->i2c_info.read_wait); + + mutex_unlock(&info->i2c_info.read_mutex); + + if (copy_to_user(buf, info->i2c_info.buf, ret)) { + dev_err(info->dev, "copy failed to user\n"); + ret = -EFAULT; + } + + goto out; + +read_error: + info->i2c_info.read_irq = SEC_NFC_NONE; + mutex_unlock(&info->i2c_info.read_mutex); +out: + mutex_unlock(&info->mutex); + + return ret; +} + +static ssize_t sec_nfc_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct sec_nfc_info *info = container_of(file->private_data, + struct sec_nfc_info, miscdev); + int ret = 0; + + dev_dbg(info->dev, "%s: info: %p, count %d\n", __func__, + info, (u32)count); + + mutex_lock(&info->mutex); + + if (info->mode == SEC_NFC_MODE_OFF) { + dev_err(info->dev, "sec_nfc is not enabled\n"); + ret = -ENODEV; + goto out; + } + + if (count > info->i2c_info.buflen) + count = info->i2c_info.buflen; + + if (count > SEC_NFC_MSG_MAX_SIZE) { + dev_err(info->dev, "user required wrong size :%d\n", (u32)count); + ret = -EINVAL; + goto out; + } + + if (copy_from_user(info->i2c_info.buf, buf, count)) { + dev_err(info->dev, "copy failed from user\n"); + ret = -EFAULT; + goto out; + } + + /* Skip interrupt during power switching + * It is released after first write + */ + mutex_lock(&info->i2c_info.read_mutex); + ret = i2c_master_send(info->i2c_info.i2c_dev, info->i2c_info.buf, count); + if (info->i2c_info.read_irq == SEC_NFC_SKIP) + info->i2c_info.read_irq = SEC_NFC_NONE; + mutex_unlock(&info->i2c_info.read_mutex); + + if (ret == -EREMOTEIO) { + dev_err(info->dev, "send failed: return: %d count: %d\n", + ret, (u32)count); + ret = -ERESTART; + goto out; + } + + if (ret != count) { + dev_err(info->dev, "send failed: return: %d count: %d\n", + ret, (u32)count); + ret = -EREMOTEIO; + } + +out: + mutex_unlock(&info->mutex); + + return ret; +} + +static unsigned int sec_nfc_poll(struct file *file, poll_table *wait) +{ + struct sec_nfc_info *info = container_of(file->private_data, + struct sec_nfc_info, miscdev); + enum sec_nfc_irq irq; + + int ret = 0; + + dev_dbg(info->dev, "%s: info: %p\n", __func__, info); + + mutex_lock(&info->mutex); + + if (info->mode == SEC_NFC_MODE_OFF) { + dev_err(info->dev, "sec_nfc is not enabled\n"); + ret = -ENODEV; + goto out; + } + + poll_wait(file, &info->i2c_info.read_wait, wait); + + mutex_lock(&info->i2c_info.read_mutex); + irq = info->i2c_info.read_irq; + if (irq == SEC_NFC_READ_TIMES) + ret = (POLLIN | POLLRDNORM); + mutex_unlock(&info->i2c_info.read_mutex); + +out: + mutex_unlock(&info->mutex); + + return ret; +} + +void sec_nfc_i2c_irq_clear(struct sec_nfc_info *info) +{ + /* clear interrupt. Interrupt will be occurred at power off */ + mutex_lock(&info->i2c_info.read_mutex); + info->i2c_info.read_irq = SEC_NFC_NONE; + mutex_unlock(&info->i2c_info.read_mutex); +} + +int sec_nfc_i2c_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct sec_nfc_info *info = dev_get_drvdata(dev); + struct sec_nfc_platform_data *pdata = info->pdata; + int ret; + + pr_info("%s: start: %p\n", __func__, info); + + info->i2c_info.buflen = SEC_NFC_MAX_BUFFER_SIZE; + info->i2c_info.buf = kzalloc(SEC_NFC_MAX_BUFFER_SIZE, GFP_KERNEL); + if (!info->i2c_info.buf) { + dev_err(dev, "failed to allocate memory for sec_nfc_info->buf\n"); + return -ENOMEM; + } + info->i2c_info.i2c_dev = client; + info->i2c_info.read_irq = SEC_NFC_NONE; + mutex_init(&info->i2c_info.read_mutex); + init_waitqueue_head(&info->i2c_info.read_wait); + i2c_set_clientdata(client, info); + + info->dev = dev; + + ret = gpio_request(pdata->irq, "nfc_int"); + if (ret) { + pr_err("GPIO request is failed to register IRQ\n"); + goto err_irq_req; + } + client->irq = gpio_to_irq(pdata->irq); + pr_info("%s: push interrupt no = %d\n", __func__, client->irq); + + ret = request_threaded_irq(client->irq, NULL, sec_nfc_irq_thread_fn, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, SEC_NFC_DRIVER_NAME, info); + if (ret < 0) { + pr_err("failed to register IRQ handler\n"); + kfree(info->i2c_info.buf); + return ret; + } + + pr_info("%s success\n", __func__); + return 0; + +err_irq_req: + return ret; +} + +void sec_nfc_i2c_remove(struct device *dev) +{ + struct sec_nfc_info *info = dev_get_drvdata(dev); + struct i2c_client *client = info->i2c_info.i2c_dev; + struct sec_nfc_platform_data *pdata = info->pdata; + free_irq(client->irq, info); + gpio_free(pdata->irq); +} +#endif /* CONFIG_SEC_NFC_IF_I2C */ + +#ifdef CONFIG_SEC_NFC_GPIO_CLK +static irqreturn_t sec_nfc_clk_irq_thread(int irq, void *dev_id) +{ + struct sec_nfc_info *info = dev_id; + struct sec_nfc_platform_data *pdata = info->pdata; + bool value; + + dev_dbg(info->dev, "[NFC]Clock Interrupt is occurred!\n"); + value = gpio_get_value(pdata->clk_req) > 0 ? true : false; + + if (value == info->clk_state) + return IRQ_HANDLED; + + if (value) + clk_prepare_enable(pdata->clk); + else + clk_disable_unprepare(pdata->clk); + + info->clk_state = value; + + return IRQ_HANDLED; +} + +void sec_nfc_clk_ctl_enable(struct sec_nfc_info *info) +{ + struct sec_nfc_platform_data *pdata = info->pdata; + unsigned int irq; + int ret; + + irq = gpio_to_irq(pdata->clk_req); + + if (info->clk_ctl) + return; + + info->clk_state = false; + ret = request_threaded_irq(irq, NULL, sec_nfc_clk_irq_thread, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + SEC_NFC_DRIVER_NAME, info); + if (ret < 0) + dev_err(info->dev, "failed to register CLK REQ IRQ handler\n"); + + info->clk_ctl = true; +} + +void sec_nfc_clk_ctl_disable(struct sec_nfc_info *info) +{ + struct sec_nfc_platform_data *pdata = info->pdata; + unsigned int irq; + + if (pdata->clk_req) + irq = gpio_to_irq(pdata->clk_req); + else + return; + + if (!info->clk_ctl) + return; + + free_irq(irq, info); + if (info->clk_state) + clk_disable_unprepare(pdata->clk); + info->clk_state = false; + info->clk_ctl = false; +} +#else +#define sec_nfc_clk_ctl_enable(x) +#define sec_nfc_clk_ctl_disable(x) +#endif /* CONFIG_SEC_NFC_GPIO_CLK */ + +static void sec_nfc_set_mode(struct sec_nfc_info *info, + enum sec_nfc_mode mode) +{ + struct sec_nfc_platform_data *pdata = info->pdata; +#ifdef CONFIG_SEC_ESE_COLDRESET + int alreadFirmHigh = 0; + int ret ; + enum sec_nfc_mode oldmode = info->mode; +#endif +#ifdef CONFIG_SEC_NFC_DEDICATED_CLK + unsigned int val = readl(info->clkctrl); +#endif + + /* intfo lock is aleady gotten before calling this function */ + if (info->mode == mode) { + pr_err("Power mode is already %d", mode); + return; + } + info->mode = mode; + +#ifdef CONFIG_SEC_NFC_IF_I2C + /* Skip interrupt during power switching + * It is released after first write */ + mutex_lock(&info->i2c_info.read_mutex); +#ifdef CONFIG_SEC_ESE_COLDRESET + cur_mode = mode; + memset(sleep_wakeup_state, false, sizeof(sleep_wakeup_state)); + if (oldmode == SEC_NFC_MODE_OFF) { + if (gpio_get_value(pdata->firm) == 1) { + alreadFirmHigh = 1; + pr_err("Firm is already high; do not anything"); + } + else { /*Firm pin is low*/ + gpio_set_value(pdata->firm, SEC_NFC_FW_ON); + msleep(SEC_NFC_VEN_WAIT_TIME); + } + + if (gpio_get_value(pdata->ven) == SEC_NFC_PW_ON) { + ret = i2c_master_send(info->i2c_info.i2c_dev, disable_combo_reset_cmd, + sizeof(disable_combo_reset_cmd)/sizeof(u8)); + pr_err("disable combo_reset_command ret: %d", ret); + } + else { + pr_err("skip disable combo_reset_command"); + } + + if (alreadFirmHigh == 1) { + pr_err("Firm is already high; do not anything2"); + } + else { /*Firm pin is low*/ + mdelay(3); /*wait for FW*/ + gpio_set_value(pdata->firm, SEC_NFC_FW_OFF); + } +} +#endif + info->i2c_info.read_irq = SEC_NFC_SKIP; + mutex_unlock(&info->i2c_info.read_mutex); +#endif + +#ifdef CONFIG_SEC_ESE_COLDRESET + mdelay(1); + pr_err("FIRMWARE_GUARD_TIME(+1ms) in PW_OFF(total:4ms)\n"); +#endif + + gpio_set_value(pdata->ven, SEC_NFC_PW_OFF); + if (pdata->firm) + gpio_set_value(pdata->firm, SEC_NFC_FW_OFF); + + if (mode == SEC_NFC_MODE_BOOTLOADER) + if (pdata->firm) + gpio_set_value(pdata->firm, SEC_NFC_FW_ON); + + if (mode != SEC_NFC_MODE_OFF) { + msleep(SEC_NFC_VEN_WAIT_TIME); + sec_nfc_clk_ctl_enable(info); + gpio_set_value(pdata->ven, SEC_NFC_PW_ON); +#ifdef CONFIG_SEC_NFC_DEDICATED_CLK + val |= 0xC0000000; + writel(val, info->clkctrl); +#endif +#ifdef CONFIG_SEC_NFC_IF_I2C + enable_irq_wake(info->i2c_info.i2c_dev->irq); +#endif + msleep(SEC_NFC_VEN_WAIT_TIME/2); + } + else { +#ifdef CONFIG_SEC_ESE_COLDRESET + int PW_OFF_DURATION = 20; + struct timespec t0, t1; + getnstimeofday(&t0); + mdelay(PW_OFF_DURATION); + + if (is_shutdown == false) + gpio_set_value(pdata->ven, SEC_NFC_PW_ON); + + getnstimeofday(&t1); + pr_err("DeepStby: PW_OFF duration (%d)ms, real PW_OFF duration is (%ld-%ld)ms\n", + PW_OFF_DURATION ,t0.tv_nsec*1000, t1.tv_nsec*1000); + pr_err("DeepStby: enter DeepStby(PW_ON)\n"); +#endif + sec_nfc_clk_ctl_disable(info); +#ifdef CONFIG_SEC_NFC_DEDICATED_CLK + val &= ~0xC0000000; + writel(val, info->clkctrl); +#endif +#ifdef CONFIG_SEC_NFC_IF_I2C + disable_irq_wake(info->i2c_info.i2c_dev->irq); +#endif + } + +#ifdef CONFIG_SEC_NFC_WAKE_LOCK + if (wake_lock_active(&info->nfc_wake_lock)) + wake_unlock(&info->nfc_wake_lock); +#endif + + pr_info("%s NFC mode is : %d\n", __func__, mode); +} + +#ifdef CONFIG_SEC_ESE_COLDRESET +struct cold_reset_gpio { + int firm_gpio; + int coldreset_gpio; +}; + +struct cold_reset_gpio cold_reset_gpio_data; + +void init_coldreset_mutex(void) +{ + mutex_init(&coldreset_mutex); +} + +void init_sleep_wake_mutex(void) +{ + mutex_init(&sleep_wake_mutex); +} + +void check_and_sleep_nfc(unsigned int gpio, int value) { + if (sleep_wakeup_state[IDX_SLEEP_WAKEUP_NFC] == true || + sleep_wakeup_state[IDX_SLEEP_WAKEUP_ESE] == true) { + pr_info("%s keep wake up state\n", __func__); + return; + } + gpio_set_value(gpio, value); +} + +int trig_cold_reset_id(int id) +{ + int wakeup_delay = 20; + int duration = 18; + struct timespec t0, t1, t2; + int isFirmHigh = 0; + + if (id == ESE_ID) + mutex_lock(&coldreset_mutex); + + getnstimeofday(&t0); + if (gpio_get_value(cold_reset_gpio_data.firm_gpio) == 1) { + isFirmHigh = 1; + } + else { + gpio_set_value(cold_reset_gpio_data.firm_gpio, SEC_NFC_FW_ON); + mdelay(wakeup_delay); + } + + getnstimeofday(&t1); + gpio_set_value(cold_reset_gpio_data.coldreset_gpio, SEC_NFC_COLDRESET_ON); + mdelay(duration); + gpio_set_value(cold_reset_gpio_data.coldreset_gpio, SEC_NFC_COLDRESET_OFF); + getnstimeofday(&t2); + + if (isFirmHigh == 1) + pr_err("COLDRESET: FW_PIN already high, do not FW_OFF\n"); + else + gpio_set_value(cold_reset_gpio_data.firm_gpio, SEC_NFC_FW_OFF); + + pr_err("COLDRESET: FW_ON time (%ld-%ld)\n", t0.tv_nsec*1000, t1.tv_nsec*1000); + pr_err("COLDRESET: GPIO3 ON time (%ld-%ld)\n", t1.tv_nsec*1000, t2.tv_nsec*1000); + + if (id == ESE_ID) + mutex_unlock(&coldreset_mutex); + + pr_err("COLDRESET: exit"); + return 0; +} + +extern int trig_cold_reset(void) // Called from eSE driver +{ + pr_info("%s trig_cold_reset\n", __func__); + return trig_cold_reset_id(ESE_ID); +} +EXPORT_SYMBOL(trig_cold_reset); + +extern int trig_nfc_wakeup(void) +{ + pr_info("%s trig_nfc_wakeup\n", __func__); + + if (cur_mode == SEC_NFC_MODE_OFF || cur_mode == SEC_NFC_MODE_BOOTLOADER) { + pr_err("nfc mode not support to wake up"); + return -EPERM;; + } + mutex_lock(&sleep_wake_mutex); + gpio_set_value(cold_reset_gpio_data.firm_gpio, SEC_NFC_WAKE_UP); + sleep_wakeup_state[IDX_SLEEP_WAKEUP_ESE] = true; + mutex_unlock(&sleep_wake_mutex); + return 0; +} +EXPORT_SYMBOL(trig_nfc_wakeup); + +extern int trig_nfc_sleep(void) +{ + pr_info("%s trig_nfc_sleep\n", __func__); + if (cur_mode == SEC_NFC_MODE_OFF || cur_mode == SEC_NFC_MODE_BOOTLOADER) { + pr_err("nfc mode not support to sleep"); + return -EPERM;; + } + mutex_lock(&sleep_wake_mutex); + sleep_wakeup_state[IDX_SLEEP_WAKEUP_ESE] = false; + check_and_sleep_nfc(cold_reset_gpio_data.firm_gpio, SEC_NFC_WAKE_SLEEP); + mutex_unlock(&sleep_wake_mutex); + return 0; +} +EXPORT_SYMBOL(trig_nfc_sleep); +#endif + +static long sec_nfc_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct sec_nfc_info *info = container_of(file->private_data, + struct sec_nfc_info, miscdev); + struct sec_nfc_platform_data *pdata = info->pdata; + unsigned int new = (unsigned int)arg; + int ret = 0; + + dev_dbg(info->dev, "%s: info: %p, cmd: 0x%x\n", + __func__, info, cmd); + + mutex_lock(&info->mutex); +#ifdef CONFIG_SEC_ESE_COLDRESET + mutex_lock(&coldreset_mutex); +#endif + + switch (cmd) { + case SEC_NFC_SET_MODE: + if (info->mode == new) + break; + + if (new >= SEC_NFC_MODE_COUNT) { + dev_err(info->dev, "wrong mode (%d)\n", new); + ret = -EFAULT; + break; + } + sec_nfc_set_mode(info, new); + break; + +#if defined(CONFIG_SEC_NFC_PRODUCT_N3) + case SEC_NFC_SLEEP: + case SEC_NFC_WAKEUP: + break; + +#elif defined(CONFIG_SEC_NFC_PRODUCT_N5) || defined(CONFIG_SEC_NFC_PRODUCT_N7) + case SEC_NFC_SLEEP: + if (info->mode != SEC_NFC_MODE_BOOTLOADER) { +#ifdef CONFIG_SEC_NFC_WAKE_LOCK + if (wake_lock_active(&info->nfc_wake_lock)) + wake_unlock(&info->nfc_wake_lock); +#endif +#ifdef CONFIG_SEC_ESE_COLDRESET + mutex_lock(&sleep_wake_mutex); + sleep_wakeup_state[IDX_SLEEP_WAKEUP_NFC] = false; + check_and_sleep_nfc(pdata->wake, SEC_NFC_WAKE_SLEEP); + mutex_unlock(&sleep_wake_mutex); +#else + gpio_set_value(pdata->wake, SEC_NFC_WAKE_SLEEP); +#endif + } + break; + + case SEC_NFC_WAKEUP: + if (info->mode != SEC_NFC_MODE_BOOTLOADER) { + gpio_set_value(pdata->wake, SEC_NFC_WAKE_UP); +#ifdef CONFIG_SEC_ESE_COLDRESET + sleep_wakeup_state[IDX_SLEEP_WAKEUP_NFC] = true; +#endif +#ifdef CONFIG_SEC_NFC_WAKE_LOCK + if (!wake_lock_active(&info->nfc_wake_lock)) + wake_lock(&info->nfc_wake_lock); +#endif + } + break; +#endif +#ifdef CONFIG_SEC_ESE_COLDRESET + case SEC_NFC_COLD_RESET: + trig_cold_reset_id(DEVICEHOST_ID); + break; + case SEC_NFC_SHUTDOWN: + is_shutdown = true; + break; +#endif + default: + dev_err(info->dev, "Unknown ioctl 0x%x\n", cmd); + ret = -ENOIOCTLCMD; + break; + } + +#ifdef CONFIG_SEC_ESE_COLDRESET + mutex_unlock(&coldreset_mutex); +#endif + mutex_unlock(&info->mutex); + + return ret; +} + +static int sec_nfc_open(struct inode *inode, struct file *file) +{ + struct sec_nfc_info *info = container_of(file->private_data, + struct sec_nfc_info, miscdev); + int ret = 0; + + pr_info("%s info : %p\n", __func__, info); + + mutex_lock(&info->mutex); + if (info->mode != SEC_NFC_MODE_OFF) { + dev_err(info->dev, "sec_nfc is busy\n"); + ret = -EBUSY; + goto out; + } + + sec_nfc_set_mode(info, SEC_NFC_MODE_OFF); + +out: + mutex_unlock(&info->mutex); + return ret; +} + +static int sec_nfc_close(struct inode *inode, struct file *file) +{ + struct sec_nfc_info *info = container_of(file->private_data, + struct sec_nfc_info, miscdev); + + pr_info("%s: info : %p\n", __func__, info); + + mutex_lock(&info->mutex); + sec_nfc_set_mode(info, SEC_NFC_MODE_OFF); + mutex_unlock(&info->mutex); + + return 0; +} + +static const struct file_operations sec_nfc_fops = { + .owner = THIS_MODULE, + .read = sec_nfc_read, + .write = sec_nfc_write, + .poll = sec_nfc_poll, + .open = sec_nfc_open, + .release = sec_nfc_close, + .unlocked_ioctl = sec_nfc_ioctl, +}; + +#ifdef CONFIG_PM +static int sec_nfc_suspend(struct device *dev) +{ + struct sec_nfc_info *info = SEC_NFC_GET_INFO(dev); + int ret = 0; + + mutex_lock(&info->mutex); + + if (info->mode == SEC_NFC_MODE_BOOTLOADER) + ret = -EPERM; + + mutex_unlock(&info->mutex); + + return ret; +} + +static int sec_nfc_resume(struct device *dev) +{ + return 0; +} + +static SIMPLE_DEV_PM_OPS(sec_nfc_pm_ops, sec_nfc_suspend, sec_nfc_resume); +#endif + +#ifdef CONFIG_OF +/*device tree parsing*/ +static int sec_nfc_parse_dt(struct device *dev, + struct sec_nfc_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + + if (!np) { + pr_err("nfc of_node NULL\n"); + return -EINVAL; + } + pdata->ven = of_get_named_gpio(np, "sec-nfc,ven-gpio", 0); + pdata->firm = of_get_named_gpio(np, "sec-nfc,firm-gpio", 0); + pdata->wake = pdata->firm; +#ifdef CONFIG_SEC_NFC_IF_I2C + pdata->irq = of_get_named_gpio(np, "sec-nfc,irq-gpio", 0); +#endif + +#ifdef CONFIG_SEC_ESE_COLDRESET + pdata->coldreset = of_get_named_gpio(np, "sec-nfc,coldreset-gpio",0); + cold_reset_gpio_data.firm_gpio = pdata->firm; + cold_reset_gpio_data.coldreset_gpio = pdata->coldreset; +#endif + +#ifdef CONFIG_SEC_NFC_LDO_EN + pdata->pvdd_en = of_get_named_gpio(np, "sec-nfc,ldo_en",0); +#endif +#ifdef CONFIG_SEC_NFC_PMIC_LDO + if (of_property_read_string(np, "sec-nfc,pmic-ldo", &pdata->pvdd_regulator_name)) + pr_err("%s: Failed to get %s regulator.\n", __func__, pdata->pvdd_regulator_name); +#endif + +#ifdef CONFIG_SEC_NFC_DEDICATED_CLK + if(of_property_read_u32(np, "clkctrl-reg", (u32 *)&pdata->clkctrl_addr)) + return -EINVAL; +#endif +#ifdef CONFIG_SEC_NFC_GPIO_CLK + pdata->clk_req = of_get_named_gpio(np, "sec-nfc,clk_req-gpio", 0); + pdata->clk = clk_get(dev, "OSC_NFC"); +#endif + + pr_info("%s: irq : %d, ven : %d, firm : %d\n", + __func__, pdata->irq, pdata->ven, pdata->firm); + return 0; +} +#else /* CONFIG_OF */ +static int sec_nfc_parse_dt(struct device *dev, + struct sec_nfc_platform_data *pdata) +{ + return -ENODEV; +} +#endif + +static int __sec_nfc_probe(struct device *dev) +{ + struct sec_nfc_info *info; + struct sec_nfc_platform_data *pdata = NULL; + int ret = 0; + + pr_info("%s : start\n", __func__); + + if (dev->of_node && !mmi_device_is_available(dev->of_node)) { + dev_err(dev,"%s : mmi: device not supported\n", __func__); + return -ENODEV; + } + else { + dev_err(dev,"%s : supported device found\n", __func__); + } + + if (dev->of_node) { + pdata = devm_kzalloc(dev, sizeof(struct sec_nfc_platform_data), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + ret = sec_nfc_parse_dt(dev, pdata); + if (ret) + return ret; + } + else { + pdata = dev->platform_data; + } + + if (!pdata) { + dev_err(dev, "No platform data\n"); + ret = -ENOMEM; + goto err_pdata; + } + + info = kzalloc(sizeof(struct sec_nfc_info), GFP_KERNEL); + if (!info) { + ret = -ENOMEM; + goto err_info_alloc; + } + info->dev = dev; + info->pdata = pdata; + info->mode = SEC_NFC_MODE_OFF; + + mutex_init(&info->mutex); + dev_set_drvdata(dev, info); + + info->miscdev.minor = MISC_DYNAMIC_MINOR; + info->miscdev.name = SEC_NFC_DRIVER_NAME; + info->miscdev.fops = &sec_nfc_fops; + info->miscdev.parent = dev; + ret = misc_register(&info->miscdev); + if (ret < 0) { + dev_err(dev, "failed to register Device\n"); + goto err_dev_reg; + } + +#ifdef CONFIG_SEC_NFC_DEDICATED_CLK + if (pdata->clkctrl_addr != 0) { + info->clkctrl = ioremap_nocache(pdata->clkctrl_addr, 0x80); + if (!info->clkctrl) { + dev_err(dev, "cannot remap nfc clock register\n"); + ret = -ENXIO; + goto err_iomap; + } + } +#endif +#ifdef CONFIG_SEC_NFC_LDO_EN + ret = gpio_request(pdata->pvdd_en, "ldo_en"); + if (ret < 0) { + pr_err("failed to request about pvdd_en pin\n"); + goto err_dev_reg; + } + if (!is_charging_mode) + gpio_direction_output(pdata->pvdd_en, NFC_I2C_LDO_ON); + pr_info("pvdd en: %d\n", gpio_get_value(pdata->pvdd_en)); +#endif +#ifdef CONFIG_SEC_NFC_PMIC_LDO + if (!is_charging_mode) { + pdata->pvdd_regulator = regulator_get(NULL, pdata->pvdd_regulator_name); + if (IS_ERR(pdata->pvdd_regulator)) + pr_err("%s: Failed to get %s pmic regulator.\n", __func__, pdata->pvdd_regulator); + ret = regulator_enable(pdata->pvdd_regulator); + if (ret < 0) + pr_err("%s: Failed to enable pmic regulator: %d\n", __func__, ret); + } +#endif + + ret = gpio_request(pdata->ven, "nfc_ven"); + if (ret) { + dev_err(dev, "failed to get gpio ven\n"); + goto err_gpio_ven; + } + if (!is_charging_mode) + gpio_direction_output(pdata->ven, SEC_NFC_PW_OFF); + + if (pdata->firm) { + ret = gpio_request(pdata->firm, "nfc_firm"); + if (ret) { + dev_err(dev, "failed to get gpio firm\n"); + goto err_gpio_firm; + } + gpio_direction_output(pdata->firm, SEC_NFC_FW_OFF); + } +#ifdef CONFIG_SEC_ESE_COLDRESET + init_coldreset_mutex(); + init_sleep_wake_mutex(); + memset(sleep_wakeup_state, false, sizeof(sleep_wakeup_state)); + ret = gpio_request(pdata->coldreset, "nfc_coldreset"); + if (ret) { + dev_err(dev, "failed to get gpio coldreset(NFC-GPIO3)\n"); + goto err_gpio_coldreset; + } + gpio_direction_output(pdata->coldreset, SEC_NFC_COLDRESET_OFF); +#endif +#ifdef CONFIG_SEC_NFC_WAKE_LOCK + wake_lock_init(&info->nfc_wake_lock, WAKE_LOCK_SUSPEND, "nfc_wake_lock"); +#endif + + dev_info(dev, "%s: success info: %p, pdata %p\n", __func__, info, pdata); + + return 0; + +#ifdef CONFIG_SEC_ESE_COLDRESET +err_gpio_coldreset: + gpio_free(pdata->coldreset); +#endif +err_gpio_firm: + gpio_free(pdata->ven); +err_gpio_ven: +#ifdef CONFIG_SEC_NFC_DEDICATED_CLK + iounmap(info->clkctrl); +err_iomap: +#endif +err_dev_reg: + mutex_destroy(&info->mutex); + kfree(info); +err_info_alloc: + devm_kfree(dev, pdata); +err_pdata: + return ret; +} + +static int __sec_nfc_remove(struct device *dev) +{ + struct sec_nfc_info *info = dev_get_drvdata(dev); + struct i2c_client *client = info->i2c_info.i2c_dev; + struct sec_nfc_platform_data *pdata = info->pdata; + + dev_dbg(info->dev, "%s\n", __func__); + +#ifdef CONFIG_SEC_NFC_GPIO_CLK + if (pdata->clk) + clk_unprepare(pdata->clk); +#endif + + misc_deregister(&info->miscdev); + sec_nfc_set_mode(info, SEC_NFC_MODE_OFF); + free_irq(client->irq, info); + gpio_free(pdata->irq); + gpio_set_value(pdata->firm, 0); + gpio_free(pdata->ven); + if (pdata->firm) + gpio_free(pdata->firm); + +#ifdef CONFIG_SEC_NFC_WAKE_LOCK + wake_lock_destroy(&info->nfc_wake_lock); +#endif + + kfree(info); + + return 0; +} + +#ifdef CONFIG_SEC_NFC_IF_I2C +static struct i2c_device_id sec_nfc_id_table[] = { + { SEC_NFC_DRIVER_NAME, 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, sec_nfc_id_table); +typedef struct i2c_driver sec_nfc_driver_type; +#define SEC_NFC_INIT(driver) i2c_add_driver(driver) +#define SEC_NFC_EXIT(driver) i2c_del_driver(driver) + +static int sec_nfc_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + + ret = __sec_nfc_probe(&client->dev); + if (ret) + return ret; + + if (sec_nfc_i2c_probe(client)) + __sec_nfc_remove(&client->dev); + + return ret; +} + +static int sec_nfc_remove(struct i2c_client *client) +{ + sec_nfc_i2c_remove(&client->dev); + return __sec_nfc_remove(&client->dev); +} +#else /* CONFIG_SEC_NFC_IF_I2C */ +MODULE_DEVICE_TABLE(platform, sec_nfc_id_table); +typedef struct platform_driver sec_nfc_driver_type; +#define SEC_NFC_INIT(driver) platform_driver_register(driver); +#define SEC_NFC_EXIT(driver) platform_driver_unregister(driver); + +static int sec_nfc_probe(struct platform_device *pdev) +{ + return __sec_nfc_probe(&pdev->dev); +} + +static int sec_nfc_remove(struct platform_device *pdev) +{ + return __sec_nfc_remove(&pdev->dev); +} + +static struct platform_device_id sec_nfc_id_table[] = { + { SEC_NFC_DRIVER_NAME, 0 }, + { } +}; +#endif /* CONFIG_SEC_NFC_IF_I2C */ + +#ifdef CONFIG_OF +static const struct of_device_id nfc_match_table[] = { + { .compatible = SEC_NFC_DRIVER_NAME,}, + {}, +}; +#else /* CONFIG_OF */ +#define nfc_match_table NULL +#endif + +static struct i2c_driver sec_nfc_driver = { + .probe = sec_nfc_probe, + .id_table = sec_nfc_id_table, + .remove = sec_nfc_remove, + .driver = { + .name = SEC_NFC_DRIVER_NAME, +#ifdef CONFIG_PM + .pm = &sec_nfc_pm_ops, +#endif + .of_match_table = nfc_match_table, + }, +}; + +static int __init sec_nfc_init(void) +{ + pr_info("%s \n", __func__); + return SEC_NFC_INIT(&sec_nfc_driver); +} + +static void __exit sec_nfc_exit(void) +{ + SEC_NFC_EXIT(&sec_nfc_driver); +} + +module_init(sec_nfc_init); +module_exit(sec_nfc_exit); + +MODULE_DESCRIPTION("Samsung sec_nfc driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/nfc/sec_nfc/sec_nfc.h b/drivers/nfc/sec_nfc/sec_nfc.h new file mode 100644 index 000000000000..4c6137517bbd --- /dev/null +++ b/drivers/nfc/sec_nfc/sec_nfc.h @@ -0,0 +1,142 @@ +/* + * SAMSUNG NFC Controller + * + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Author: Woonki Lee + * Author: Heejae Kim + * + * 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. + * + * Last update: 2016-01-14 + */ + +#include +#ifdef CONFIG_SEC_NFC_GPIO_CLK +#include +#endif +#ifdef CONFIG_SEC_NFC_PMIC_LDO +#include +#include +#endif + +#ifdef CONFIG_SEC_NFC_I2C /* support old driver configuration */ + +#ifndef CONFIG_SEC_NFC +#define CONFIG_SEC_NFC +#endif + +#if !defined(CONFIG_SEC_NFC_IF_I2C) && !defined(CONFIG_SEC_NFC_IF_UART) +#define CONFIG_SEC_NFC_IF_I2C +#endif + +#if !defined(CONFIG_SEC_NFC_PRODUCT_N3) && !defined(CONFIG_SEC_NFC_PRODUCT_N5) +#define CONFIG_SEC_NFC_PRODUCT_N5 +#endif + +#endif /* CONFIG_SEC_NFC_I2C */ + +#define SEC_NFC_DRIVER_NAME "sec-nfc" + +#define SEC_NFC_MAX_BUFFER_SIZE 512 + +/* ioctl */ +#define SEC_NFC_MAGIC 'S' +#define SEC_NFC_GET_MODE _IOW(SEC_NFC_MAGIC, 0, unsigned int) +#define SEC_NFC_SET_MODE _IOW(SEC_NFC_MAGIC, 1, unsigned int) +#define SEC_NFC_SLEEP _IOW(SEC_NFC_MAGIC, 2, unsigned int) +#define SEC_NFC_WAKEUP _IOW(SEC_NFC_MAGIC, 3, unsigned int) +#ifdef CONFIG_SEC_ESE_COLDRESET +#define SEC_NFC_COLD_RESET _IOW(SEC_NFC_MAGIC, 5, unsigned int) +#define SEC_NFC_SHUTDOWN _IOW(SEC_NFC_MAGIC, 6, unsigned int) +#endif + +/* size */ +#define SEC_NFC_MSG_MAX_SIZE (256 + 4) + +/* wait for device stable */ +#ifdef CONFIG_SEC_NFC_MARGINTIME +#define SEC_NFC_VEN_WAIT_TIME (150) +#else +#define SEC_NFC_VEN_WAIT_TIME (20) +#endif + +/* gpio pin configuration */ +struct sec_nfc_platform_data { + int irq; + int ven; + int firm; + int wake; + int pvdd_en; + unsigned int tvdd; + unsigned int avdd; +#ifdef CONFIG_SEC_ESE_COLDRESET + unsigned int coldreset; +#endif +#ifdef CONFIG_SEC_NFC_GPIO_CLK + unsigned int clk_req; + struct clk *clk; +#endif + + void (*cfg_gpio)(void); + u32 ven_gpio_flags; + u32 firm_gpio_flags; + u32 irq_gpio_flags; + const char *nfc_pvdd; +#ifdef CONFIG_SEC_NFC_PMIC_LDO + const char *pvdd_regulator_name; + struct regulator *pvdd_regulator; +#endif +#ifdef CONFIG_SEC_NFC_DEDICATED_CLK + phys_addr_t clkctrl_addr; +#endif +}; + +enum sec_nfc_mode { + SEC_NFC_MODE_OFF = 0, + SEC_NFC_MODE_FIRMWARE, + SEC_NFC_MODE_BOOTLOADER, + SEC_NFC_MODE_COUNT, +}; + +#ifdef CONFIG_SEC_NFC_PRODUCT_N3 +enum sec_nfc_power { + SEC_NFC_PW_OFF = 0, + SEC_NFC_PW_ON, +}; +#else /* CONFIG_SEC_NFC_PRODUCT_N3 */ +enum sec_nfc_power { + SEC_NFC_PW_ON = 0, + SEC_NFC_PW_OFF, +}; +#endif + +enum sec_nfc_firmpin { + SEC_NFC_FW_OFF = 0, + SEC_NFC_FW_ON, +}; + +enum sec_nfc_wake { + SEC_NFC_WAKE_SLEEP = 0, + SEC_NFC_WAKE_UP, +}; + +#ifdef CONFIG_SEC_ESE_COLDRESET +enum sec_nfc_coldreset{ + SEC_NFC_COLDRESET_OFF = 0, + SEC_NFC_COLDRESET_ON, +}; + +#define FIRMWARE_GUARD_TIME (4) +#define DEVICEHOST_ID (0x00) +#define ESE_ID (0x02) +#define IDX_SLEEP_WAKEUP_NFC 0 +#define IDX_SLEEP_WAKEUP_ESE 1 +#endif + +bool is_charging_mode; + +#define NFC_I2C_LDO_ON 1 +#define NFC_I2C_LDO_OFF 0 diff --git a/drivers/nfc/sn1xx/Android.mk b/drivers/nfc/sn1xx/Android.mk new file mode 100644 index 000000000000..d7675ec04b13 --- /dev/null +++ b/drivers/nfc/sn1xx/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := nfc_i2c.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_info.ko +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/nfc/sn1xx/Kbuild b/drivers/nfc/sn1xx/Kbuild new file mode 100644 index 000000000000..5426c69d2b16 --- /dev/null +++ b/drivers/nfc/sn1xx/Kbuild @@ -0,0 +1,8 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += nfc_i2c.o +nfc_i2c-objs:= nfc_common.o nfc_i2c_drv.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../mmi_info/Module.symvers diff --git a/drivers/nfc/sn1xx/Makefile b/drivers/nfc/sn1xx/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/nfc/sn1xx/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/nfc/sn1xx/nfc_common.c b/drivers/nfc/sn1xx/nfc_common.c new file mode 100644 index 000000000000..7d4798d8750a --- /dev/null +++ b/drivers/nfc/sn1xx/nfc_common.c @@ -0,0 +1,1000 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include + + +int nfc_parse_dt(struct device *dev, struct platform_gpio *nfc_gpio, + struct platform_ldo *ldo, uint8_t interface) +{ + struct device_node *np = dev->of_node; + int ret; + + if (!np) { + pr_err("nfc of_node NULL\n"); + return -EINVAL; + } + + if (interface == PLATFORM_IF_I2C) { + nfc_gpio->irq = of_get_named_gpio(np, DTS_IRQ_GPIO_STR, 0); + if ((!gpio_is_valid(nfc_gpio->irq))) { + pr_err("nfc irq gpio invalid %d\n", nfc_gpio->irq); + return -EINVAL; + } + pr_info("%s: irq %d\n", __func__, nfc_gpio->irq); + } + + nfc_gpio->ven = of_get_named_gpio(np, DTS_VEN_GPIO_STR, 0); + if ((!gpio_is_valid(nfc_gpio->ven))) { + pr_err("nfc ven gpio invalid %d\n", nfc_gpio->ven); + return -EINVAL; + } + + nfc_gpio->dwl_req = of_get_named_gpio(np, DTS_FWDN_GPIO_STR, 0); + if ((!gpio_is_valid(nfc_gpio->dwl_req))) { + pr_err("nfc dwl_req gpio invalid %d\n", nfc_gpio->dwl_req); + return -EINVAL; + } + + nfc_gpio->clkreq = of_get_named_gpio(np, DTS_CLKREQ_GPIO_STR, 0); + if (!gpio_is_valid(nfc_gpio->clkreq)) { + dev_err(dev, "clkreq gpio invalid %d\n", nfc_gpio->dwl_req); + return -EINVAL; + } + + pr_info("%s: ven %d, dwl req %d, clkreq %d\n", __func__, + nfc_gpio->ven, nfc_gpio->dwl_req, nfc_gpio->clkreq); + + // optional property + ret = of_property_read_u32_array(np, NFC_LDO_VOL_DT_NAME, + (u32 *) ldo->vdd_levels, + ARRAY_SIZE(ldo->vdd_levels)); + if (ret) { + dev_err(dev, "error reading NFC VDDIO min and max value\n"); + // set default as per datasheet + ldo->vdd_levels[0] = NFC_VDDIO_MIN; + ldo->vdd_levels[1] = NFC_VDDIO_MAX; + } + + // optional property + ret = of_property_read_u32(np, NFC_LDO_CUR_DT_NAME, &ldo->max_current); + if (ret) { + dev_err(dev, "error reading NFC current value\n"); + // set default as per datasheet + ldo->max_current = NFC_CURRENT_MAX; + } + + return 0; +} + +/** + * nfc_ldo_vote() + * @nfc_dev: NFC device containing regulator handle + * + * LDO voting based on voltage and current entries in DT + * + * Return: 0 on success and -ve on failure + */ +int nfc_ldo_vote(struct nfc_dev *nfc_dev) +{ + int ret; + + ret = regulator_set_voltage(nfc_dev->reg, + nfc_dev->ldo.vdd_levels[0], + nfc_dev->ldo.vdd_levels[1]); + if (ret < 0) { + pr_err("%s: set voltage failed\n", __func__); + return ret; + } + + /* pass expected current from NFC in uA */ + ret = regulator_set_load(nfc_dev->reg, nfc_dev->ldo.max_current); + if (ret < 0) { + pr_err("%s: set load failed\n", __func__); + return ret; + } + + ret = regulator_enable(nfc_dev->reg); + if (ret < 0) + pr_err("%s: regulator_enable failed\n", __func__); + else + nfc_dev->is_vreg_enabled = true; + return ret; +} + +/** + * nfc_ldo_config() + * @dev: device instance to read DT entry + * @nfc_dev: NFC device containing regulator handle + * + * Configure LDO if entry is present in DT file otherwise + * return with success as it's optional + * + * Return: 0 on success and -ve on failure + */ +int nfc_ldo_config(struct device *dev, struct nfc_dev *nfc_dev) +{ + int ret; + + if (of_get_property(dev->of_node, NFC_LDO_SUPPLY_NAME, NULL)) { + // Get the regulator handle + nfc_dev->reg = regulator_get(dev, NFC_LDO_SUPPLY_DT_NAME); + if (IS_ERR(nfc_dev->reg)) { + ret = PTR_ERR(nfc_dev->reg); + nfc_dev->reg = NULL; + pr_err("%s: regulator_get failed, ret = %d\n", + __func__, ret); + return ret; + } + } else { + nfc_dev->reg = NULL; + pr_err("%s: regulator entry not present\n", __func__); + // return success as it's optional to configure LDO + return 0; + } + + // LDO config supported by platform DT + ret = nfc_ldo_vote(nfc_dev); + if (ret < 0) { + pr_err("%s: LDO voting failed, ret = %d\n", __func__, ret); + regulator_put(nfc_dev->reg); + } + return ret; +} + +/** + * nfc_ldo_unvote() + * @nfc_dev: NFC device containing regulator handle + * + * set voltage and load to zero and disable regulator + * + * Return: 0 on success and -ve on failure + */ +int nfc_ldo_unvote(struct nfc_dev *nfc_dev) +{ + int ret; + + if (!nfc_dev->is_vreg_enabled) { + pr_err("%s: regulator already disabled\n", __func__); + return -EINVAL; + } + + ret = regulator_disable(nfc_dev->reg); + if (ret < 0) { + pr_err("%s: regulator_disable failed\n", __func__); + return ret; + } + nfc_dev->is_vreg_enabled = false; + + ret = regulator_set_voltage(nfc_dev->reg, 0, NFC_VDDIO_MAX); + if (ret < 0) { + pr_err("%s: set voltage failed\n", __func__); + return ret; + } + + ret = regulator_set_load(nfc_dev->reg, 0); + if (ret < 0) + pr_err("%s: set load failed\n", __func__); + return ret; +} + +void gpio_set_ven(struct nfc_dev *nfc_dev, int value) +{ + if (gpio_get_value(nfc_dev->gpio.ven) != value) { + gpio_set_value(nfc_dev->gpio.ven, value); + // hardware dependent delay + usleep_range(10000, 10100); + } +} + +int configure_gpio(unsigned int gpio, int flag) +{ + int ret; + + pr_debug("%s: nfc gpio [%d] flag [%01x]\n", __func__, gpio, flag); + + if (gpio_is_valid(gpio)) { + ret = gpio_request(gpio, "nfc_gpio"); + if (ret) { + pr_err("%s: unable to request nfc gpio [%d]\n", + __func__, gpio); + return ret; + } + // set direction and value for output pin + if (flag & GPIO_OUTPUT) + ret = gpio_direction_output(gpio, (GPIO_HIGH & flag)); + else + ret = gpio_direction_input(gpio); + + if (ret) { + pr_err + ("%s: unable to set direction for nfc gpio [%d]\n", + __func__, gpio); + gpio_free(gpio); + return ret; + } + // Consider value as control for input IRQ pin + if (flag & GPIO_IRQ) { + ret = gpio_to_irq(gpio); + if (ret < 0) { + pr_err("%s: unable to set irq for nfc gpio [%d]\n", + __func__, gpio); + gpio_free(gpio); + return ret; + } + pr_debug + ("%s: gpio_to_irq successful [%d]\n", + __func__, gpio); + return ret; + } + } else { + pr_err("%s: invalid gpio\n", __func__); + ret = -EINVAL; + } + return ret; +} + +void nfc_misc_remove(struct nfc_dev *nfc_dev, int count) +{ + pr_debug("%s: entry\n", __func__); + + kfree(nfc_dev->kbuf); + device_destroy(nfc_dev->nfc_class, nfc_dev->devno); + cdev_del(&nfc_dev->c_dev); + class_destroy(nfc_dev->nfc_class); + unregister_chrdev_region(nfc_dev->devno, count); +} + +int nfc_misc_probe(struct nfc_dev *nfc_dev, + const struct file_operations *nfc_fops, int count, + char *devname, char *classname) +{ + int ret = 0; + + ret = alloc_chrdev_region(&nfc_dev->devno, 0, count, devname); + if (ret < 0) { + pr_err("%s: failed to alloc chrdev region ret %d\n", + __func__, ret); + return ret; + } + nfc_dev->nfc_class = class_create(THIS_MODULE, classname); + if (IS_ERR(nfc_dev->nfc_class)) { + ret = PTR_ERR(nfc_dev->nfc_class); + pr_err("%s: failed to register device class ret %d\n", + __func__, ret); + unregister_chrdev_region(nfc_dev->devno, count); + return ret; + } + cdev_init(&nfc_dev->c_dev, nfc_fops); + ret = cdev_add(&nfc_dev->c_dev, nfc_dev->devno, count); + if (ret < 0) { + pr_err("%s: failed to add cdev ret %d\n", __func__, ret); + class_destroy(nfc_dev->nfc_class); + unregister_chrdev_region(nfc_dev->devno, count); + return ret; + } + nfc_dev->nfc_device = device_create(nfc_dev->nfc_class, NULL, + nfc_dev->devno, nfc_dev, "nq-nci"); + if (IS_ERR(nfc_dev->nfc_device)) { + ret = PTR_ERR(nfc_dev->nfc_device); + pr_err("%s: failed to create the device ret %d\n", + __func__, ret); + cdev_del(&nfc_dev->c_dev); + class_destroy(nfc_dev->nfc_class); + unregister_chrdev_region(nfc_dev->devno, count); + return ret; + } + + nfc_dev->kbuflen = MAX_BUFFER_SIZE; + nfc_dev->kbuf = kzalloc(MAX_BUFFER_SIZE, GFP_KERNEL | GFP_DMA); + if (!nfc_dev->kbuf) + return -ENOMEM; + + nfc_dev->cold_reset.rsp_pending = false; + nfc_dev->cold_reset.is_nfc_enabled = false; + init_waitqueue_head(&nfc_dev->cold_reset.read_wq); + + return 0; +} + + +static int send_cold_reset_cmd(struct nfc_dev *nfc_dev) +{ + int ret = 0; + char *cold_reset_cmd = NULL; + + cold_reset_cmd = kzalloc(COLD_RESET_CMD_LEN, GFP_DMA | GFP_KERNEL); + if (!cold_reset_cmd) + return -ENOMEM; + + if (gpio_get_value(nfc_dev->gpio.dwl_req)) { + pr_err("FW download in-progress\n"); + ret = -EBUSY; + goto error; + } + if (!gpio_get_value(nfc_dev->gpio.ven)) { + pr_err("VEN LOW - NFCC powered off\n"); + ret = -ENODEV; + goto error; + } + + cold_reset_cmd[0] = COLD_RESET_CMD_GID; + cold_reset_cmd[1] = COLD_RESET_OID; + cold_reset_cmd[2] = COLD_RESET_CMD_PAYLOAD_LEN; + + ret = nfc_dev->nfc_write(nfc_dev, cold_reset_cmd, + COLD_RESET_CMD_LEN, MAX_RETRY_COUNT); + if (ret <= 0) + pr_err("%s: write failed after max retry, ret %d\n", + __func__, ret); + +error: + kfree(cold_reset_cmd); + return ret; +} + +void read_cold_reset_rsp(struct nfc_dev *nfc_dev, char *header) +{ + int ret = -1; + char *cold_reset_rsp = NULL; + struct cold_reset *cold_reset = &nfc_dev->cold_reset; + + cold_reset_rsp = kzalloc(COLD_RESET_RSP_LEN, GFP_DMA | GFP_KERNEL); + if (!cold_reset_rsp) + return; + + /* + * read header also if NFC is disabled + * for enable case, will be taken care by nfc read thread + */ + if ((!cold_reset->is_nfc_enabled) && + (nfc_dev->interface == PLATFORM_IF_I2C)) { + + ret = nfc_dev->nfc_read(nfc_dev, cold_reset_rsp, + NCI_HDR_LEN); + if (ret <= 0) { + pr_err("%s: failure to read cold reset rsp header\n", + __func__); + goto error; + } + } else { + + /* For I3C driver, header is read by the worker thread */ + memcpy(cold_reset_rsp, header, NCI_HDR_LEN); + } + + if ((cold_reset_rsp[0] != COLD_RESET_RSP_GID) + || (cold_reset_rsp[1] != COLD_RESET_OID)) { + pr_err("%s: - invalid response GID or OID for cold_reset\n", + __func__); + ret = -EINVAL; + goto error; + } + if ((NCI_HDR_LEN + cold_reset_rsp[2]) > COLD_RESET_RSP_LEN) { + pr_err("%s: - invalid response for cold_reset\n", __func__); + ret = -EINVAL; + goto error; + } + + if (nfc_dev->interface == PLATFORM_IF_I2C) + ret = nfc_dev->nfc_read(nfc_dev, + &cold_reset_rsp[NCI_PAYLOAD_IDX], + cold_reset_rsp[2]); + else + ret = nfc_dev->i3c_dev.nfc_read_direct(nfc_dev, + &cold_reset_rsp[NCI_PAYLOAD_IDX], + cold_reset_rsp[2]); + + if (ret <= 0) { + pr_err("%s: failure to read cold reset rsp payload\n", + __func__); + goto error; + } + cold_reset->status = cold_reset_rsp[NCI_PAYLOAD_IDX]; + +error: + kfree(cold_reset_rsp); +} + +/* + * Power management of the eSE + * eSE and NFCC both are powered using VEN gpio, + * VEN HIGH - eSE and NFCC both are powered on + * VEN LOW - eSE and NFCC both are power down + */ +int nfc_ese_pwr(struct nfc_dev *nfc_dev, unsigned long arg) +{ + int ret = 0; + + if (arg == ESE_POWER_ON) { + /* + * Let's store the NFC VEN pin state + * will check stored value in case of eSE power off request, + * to find out if NFC MW also sent request to set VEN HIGH + * VEN state will remain HIGH if NFC is enabled otherwise + * it will be set as LOW + */ + nfc_dev->nfc_ven_enabled = gpio_get_value(nfc_dev->gpio.ven); + if (!nfc_dev->nfc_ven_enabled) { + pr_debug("eSE HAL service setting ven HIGH\n"); + gpio_set_ven(nfc_dev, 1); + } else { + pr_debug("ven already HIGH\n"); + } + nfc_dev->is_ese_session_active = true; + } else if (arg == ESE_POWER_OFF) { + if (!nfc_dev->nfc_ven_enabled) { + pr_debug("NFC not enabled, disabling ven\n"); + gpio_set_ven(nfc_dev, 0); + } else { + pr_debug("keep ven high as NFC is enabled\n"); + } + nfc_dev->is_ese_session_active = false; + } else if (arg == ESE_COLD_RESET) { + + // set default value for status as failure + nfc_dev->cold_reset.status = -EIO; + + ret = send_cold_reset_cmd(nfc_dev); + if (ret <= 0) { + pr_err("failed to send cold reset command\n"); + return nfc_dev->cold_reset.status; + } + + nfc_dev->cold_reset.rsp_pending = true; + + // check if NFC is enabled + if (nfc_dev->cold_reset.is_nfc_enabled) { + + /* + * nfc_read thread will initiate cold reset response + * and it will signal for data available + */ + wait_event_interruptible(nfc_dev->cold_reset.read_wq, + !nfc_dev->cold_reset.rsp_pending); + + } else { + + // Read data as NFC thread is not active + + nfc_dev->nfc_enable_intr(nfc_dev); + + if (nfc_dev->interface == PLATFORM_IF_I2C) { + ret = wait_event_interruptible_timeout( + nfc_dev->read_wq, + !nfc_dev->i2c_dev.irq_enabled, + msecs_to_jiffies(MAX_IRQ_WAIT_TIME)); + if (ret <= 0) { + nfc_dev->nfc_disable_intr(nfc_dev); + nfc_dev->cold_reset.rsp_pending = false; + return nfc_dev->cold_reset.status; + } + read_cold_reset_rsp(nfc_dev, NULL); + nfc_dev->cold_reset.rsp_pending = false; + } else { + wait_event_interruptible( + nfc_dev->cold_reset.read_wq, + !nfc_dev->cold_reset.rsp_pending); + nfc_dev->nfc_disable_intr(nfc_dev); + } + } + + ret = nfc_dev->cold_reset.status; + + } else if (arg == ESE_POWER_STATE) { + // eSE power state + ret = gpio_get_value(nfc_dev->gpio.ven); + } else { + pr_err("%s bad arg %lu\n", __func__, arg); + ret = -ENOIOCTLCMD; + } + return ret; +} + +/* + * nfc_ioctl_power_states() - power control + * @nfc_dev: nfc device data structure + * @arg: mode that we want to move to + * + * Device power control. Depending on the arg value, device moves to + * different states, refer nfcc_ioctl_request in nfc_common.h for args + * + * Return: -ENOIOCTLCMD if arg is not supported, 0 in any other case + */ +static int nfc_ioctl_power_states(struct nfc_dev *nfc_dev, unsigned long arg) +{ + int ret = 0; + + if (arg == NFC_POWER_OFF) { + /* + * We are attempting a hardware reset so let us disable + * interrupts to avoid spurious notifications to upper + * layers. + */ + nfc_dev->nfc_disable_intr(nfc_dev); + pr_debug("gpio firm disable\n"); + if (gpio_is_valid(nfc_dev->gpio.dwl_req)) { + gpio_set_value(nfc_dev->gpio.dwl_req, 0); + usleep_range(10000, 10100); + } + + pr_debug("Set ven to low\n"); + gpio_set_ven(nfc_dev, 0); + + nfc_dev->nfc_ven_enabled = false; + + } else if (arg == NFC_POWER_ON) { + nfc_dev->nfc_enable_intr(nfc_dev); + pr_debug("gpio_set_value enable: %s:\n", __func__); + if (gpio_is_valid(nfc_dev->gpio.dwl_req)) { + gpio_set_value(nfc_dev->gpio.dwl_req, 0); + usleep_range(10000, 10100); + } + gpio_set_ven(nfc_dev, 1); + nfc_dev->nfc_ven_enabled = true; + + if (nfc_dev->interface == PLATFORM_IF_I3C) + nfc_dev->i3c_dev.read_hdr = NCI_HDR_LEN; + + } else if (arg == NFC_FW_DWL_VEN_TOGGLE) { + /* + * We are switching to download Mode, toggle the enable pin + * in order to set the NFCC in the new mode + */ + + gpio_set_ven(nfc_dev, 1); + + if (gpio_is_valid(nfc_dev->gpio.dwl_req)) { + gpio_set_value(nfc_dev->gpio.dwl_req, 1); + usleep_range(10000, 10100); + } + if (nfc_dev->interface == PLATFORM_IF_I2C) { + gpio_set_ven(nfc_dev, 0); + gpio_set_ven(nfc_dev, 1); + } + + } else if (arg == NFC_FW_DWL_HIGH) { + /* + * Setting firmware download gpio to HIGH + * before FW download start + */ + pr_debug("set fw gpio high\n"); + if (gpio_is_valid(nfc_dev->gpio.dwl_req)) { + gpio_set_value(nfc_dev->gpio.dwl_req, 1); + usleep_range(10000, 10100); + } else + pr_debug("gpio.dwl_req is invalid\n"); + + } else if (arg == NFC_VEN_FORCED_HARD_RESET + && nfc_dev->interface == PLATFORM_IF_I2C) { + /* + * TODO: Enable Ven reset for I3C, after hot join integration + */ + + gpio_set_value(nfc_dev->gpio.ven, 0); + usleep_range(10000, 10100); + gpio_set_value(nfc_dev->gpio.ven, 1); + usleep_range(10000, 10100); + pr_info("%s VEN forced reset done\n", __func__); + + } else if (arg == NFC_FW_DWL_LOW) { + /* + * Setting firmware download gpio to LOW + * FW download finished + */ + pr_debug("set fw gpio LOW\n"); + gpio_set_value(nfc_dev->gpio.dwl_req, 0); + usleep_range(10000, 10100); + + if (nfc_dev->interface == PLATFORM_IF_I3C) + nfc_dev->i3c_dev.read_hdr = NCI_HDR_LEN; + + } else if (arg == NFC_FW_HDR_LEN) { + if (nfc_dev->interface == PLATFORM_IF_I3C) + nfc_dev->i3c_dev.read_hdr = FW_HDR_LEN; + } else if (arg == NFC_ENABLE) { + /* + * Setting flag true when NFC is enabled + */ + nfc_dev->cold_reset.is_nfc_enabled = true; + } else if (arg == NFC_DISABLE) { + /* + * Setting flag true when NFC is disabled + */ + nfc_dev->cold_reset.is_nfc_enabled = false; + } else { + pr_err("%s bad arg %lu\n", __func__, arg); + ret = -ENOIOCTLCMD; + } + return ret; +} + +/* + * Inside nfc_ioctl_nfcc_info + * + * @brief nfc_ioctl_nfcc_info + * + * Check the NFC Chipset and firmware version details + */ +unsigned int nfc_ioctl_nfcc_info(struct file *filp, unsigned long arg) +{ + unsigned int r = 0; + struct nfc_dev *nfc_dev = filp->private_data; + + r = nfc_dev->nqx_info.i; + pr_debug("nfc : %s r = %d\n", __func__, r); + + return r; +} + + +/** @brief IOCTL function to be used to set or get data from upper layer. + * + * @param pfile fil node for opened device. + * @cmd IOCTL type from upper layer. + * @arg IOCTL arg from upper layer. + * + * @return 0 on success, error code for failures. + */ + +#ifdef CONFIG_COMPAT +long nfc_compat_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct nfc_dev *nfc_dev = pfile->private_data; + + if (!nfc_dev) + return -ENODEV; + + arg = (compat_u64)arg; + pr_debug("%s cmd = %x arg = %zx\n", __func__, cmd, arg); + + switch (cmd) { + case NFC_SET_PWR: + ret = nfc_ioctl_power_states(nfc_dev, arg); + break; + case ESE_SET_PWR: + ret = nfc_ese_pwr(nfc_dev, arg); + break; + case ESE_GET_PWR: + ret = nfc_ese_pwr(nfc_dev, ESE_POWER_STATE); + break; + case NFCC_GET_INFO: + ret = nfc_ioctl_nfcc_info(pfile, arg); + break; + case NFC_GET_PLATFORM_TYPE: + ret = nfc_dev->interface; + break; + default: + pr_err("%s bad cmd %lu\n", __func__, arg); + ret = -ENOIOCTLCMD; + } + return ret; +} +#endif + +long nfc_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct nfc_dev *nfc_dev = pfile->private_data; + + if (!nfc_dev) + return -ENODEV; + + pr_debug("%s cmd = %x arg = %zx\n", __func__, cmd, arg); + + switch (cmd) { + case NFC_SET_PWR: + ret = nfc_ioctl_power_states(nfc_dev, arg); + break; + case ESE_SET_PWR: + ret = nfc_ese_pwr(nfc_dev, arg); + break; + case ESE_GET_PWR: + ret = nfc_ese_pwr(nfc_dev, ESE_POWER_STATE); + break; + case NFCC_GET_INFO: + ret = nfc_ioctl_nfcc_info(pfile, arg); + break; + case NFC_GET_PLATFORM_TYPE: + ret = nfc_dev->interface; + break; + default: + pr_err("%s bad cmd %lu\n", __func__, arg); + ret = -ENOIOCTLCMD; + } + return ret; +} + +int nfc_dev_open(struct inode *inode, struct file *filp) +{ + struct nfc_dev *nfc_dev = container_of(inode->i_cdev, + struct nfc_dev, c_dev); + + if (!nfc_dev) + return -ENODEV; + + pr_debug("%s: %d, %d\n", __func__, imajor(inode), iminor(inode)); + + mutex_lock(&nfc_dev->dev_ref_mutex); + + filp->private_data = nfc_dev; + + if (nfc_dev->dev_ref_count == 0) { + if (gpio_is_valid(nfc_dev->gpio.dwl_req)) { + gpio_set_value(nfc_dev->gpio.dwl_req, 0); + usleep_range(10000, 10100); + } + nfc_dev->nfc_enable_intr(nfc_dev); + } + nfc_dev->dev_ref_count = nfc_dev->dev_ref_count + 1; + + mutex_unlock(&nfc_dev->dev_ref_mutex); + + return 0; +} + +int nfc_dev_close(struct inode *inode, struct file *filp) +{ + struct nfc_dev *nfc_dev = container_of(inode->i_cdev, + struct nfc_dev, c_dev); + + if (!nfc_dev) + return -ENODEV; + + pr_debug("%s: %d, %d\n", __func__, imajor(inode), iminor(inode)); + + mutex_lock(&nfc_dev->dev_ref_mutex); + + if (nfc_dev->dev_ref_count == 1) { + + nfc_dev->nfc_disable_intr(nfc_dev); + + if (gpio_is_valid(nfc_dev->gpio.dwl_req)) { + gpio_set_value(nfc_dev->gpio.dwl_req, 0); + usleep_range(10000, 10100); + } + } + + if (nfc_dev->dev_ref_count > 0) + nfc_dev->dev_ref_count = nfc_dev->dev_ref_count - 1; + + filp->private_data = NULL; + + mutex_unlock(&nfc_dev->dev_ref_mutex); + + return 0; +} + +int is_data_available_for_read(struct nfc_dev *nfc_dev) +{ + int ret; + + nfc_dev->nfc_enable_intr(nfc_dev); + + ret = wait_event_interruptible_timeout(nfc_dev->read_wq, + !nfc_dev->i2c_dev.irq_enabled, + msecs_to_jiffies(MAX_IRQ_WAIT_TIME)); + return ret; +} + +/* Check for availability of NFC controller hardware */ +int nfcc_hw_check(struct nfc_dev *nfc_dev) +{ + int ret = 0; + unsigned char reset_ntf_len = 0; + char *nci_reset_cmd = NULL; + char *nci_reset_rsp = NULL; + char *nci_reset_ntf = NULL; + char *nci_get_version_cmd = NULL; + char *nci_get_version_rsp = NULL; + + nci_reset_cmd = kzalloc(NCI_RESET_CMD_LEN + 1, GFP_DMA | GFP_KERNEL); + if (!nci_reset_cmd) + return -ENOMEM; + + nci_reset_rsp = kzalloc(NCI_RESET_RSP_LEN + 1, GFP_DMA | GFP_KERNEL); + if (!nci_reset_rsp) { + ret = -ENOMEM; + goto done; + } + + nci_reset_ntf = kzalloc(NCI_RESET_NTF_LEN + 1, GFP_DMA | GFP_KERNEL); + if (!nci_reset_ntf) { + ret = -ENOMEM; + goto done; + } + + nci_get_version_cmd = kzalloc(NCI_GET_VERSION_CMD_LEN + 1, + GFP_DMA | GFP_KERNEL); + if (!nci_get_version_cmd) { + ret = -ENOMEM; + goto done; + } + + nci_get_version_rsp = kzalloc(NCI_GET_VERSION_RSP_LEN + 1, + GFP_DMA | GFP_KERNEL); + if (!nci_get_version_rsp) { + ret = -ENOMEM; + goto done; + } + + if (nfc_dev->interface == PLATFORM_IF_I3C) + nfc_dev->nfc_enable_intr(nfc_dev); + else { + /* making sure that the NFCC starts in a clean state. */ + gpio_set_ven(nfc_dev, 1);/* HPD : Enable*/ + gpio_set_ven(nfc_dev, 0);/* ULPM: Disable */ + gpio_set_ven(nfc_dev, 1);/* HPD : Enable*/ + } + + nci_reset_cmd[0] = 0x20; + nci_reset_cmd[1] = 0x00; + nci_reset_cmd[2] = 0x01; + nci_reset_cmd[3] = 0x00; + + /* send NCI CORE RESET CMD with Keep Config parameters */ + ret = nfc_dev->nfc_write(nfc_dev, nci_reset_cmd, NCI_RESET_CMD_LEN, + MAX_RETRY_COUNT); + if (ret <= 0) { + pr_err("%s: - nfc core reset error\n", __func__); + + if (gpio_is_valid(nfc_dev->gpio.dwl_req)) { + gpio_set_value(nfc_dev->gpio.dwl_req, 1); + usleep_range(10000, 10100); + } + + if (nfc_dev->interface == PLATFORM_IF_I2C) { + gpio_set_ven(nfc_dev, 0); + gpio_set_ven(nfc_dev, 1); + } + + nci_get_version_cmd[0] = 0x00; + nci_get_version_cmd[1] = 0x04; + nci_get_version_cmd[2] = 0xF1; + nci_get_version_cmd[3] = 0x00; + nci_get_version_cmd[4] = 0x00; + nci_get_version_cmd[5] = 0x00; + nci_get_version_cmd[6] = 0x6E; + nci_get_version_cmd[7] = 0xEF; + + ret = nfc_dev->nfc_write(nfc_dev, nci_get_version_cmd, + NCI_GET_VERSION_CMD_LEN, MAX_RETRY_COUNT); + if (ret <= 0) { + pr_err("%s: - nfc get version cmd error ret %d\n", + __func__, ret); + goto err_nfcc_hw_check; + } + + if (nfc_dev->interface == PLATFORM_IF_I2C) { + ret = is_data_available_for_read(nfc_dev); + if (ret <= 0) { + nfc_dev->nfc_disable_intr(nfc_dev); + pr_err("%s: - error waiting for get version rsp ret %d\n", + __func__, ret); + goto err_nfcc_hw_check; + } + } + + ret = nfc_dev->nfc_read(nfc_dev, nci_get_version_rsp, + NCI_GET_VERSION_RSP_LEN); + if (ret <= 0) { + pr_err("%s: - nfc get version rsp error ret %d\n", + __func__, ret); + goto err_nfcc_hw_check; + } else { + nfc_dev->nqx_info.info.chip_type = + nci_get_version_rsp[3]; + nfc_dev->nqx_info.info.rom_version = + nci_get_version_rsp[4]; + nfc_dev->nqx_info.info.fw_minor = + nci_get_version_rsp[6]; + nfc_dev->nqx_info.info.fw_major = + nci_get_version_rsp[7]; + } + + gpio_set_value(nfc_dev->gpio.dwl_req, 0); + + goto err_nfcc_reset_failed; + } + + if (nfc_dev->interface == PLATFORM_IF_I2C) { + ret = is_data_available_for_read(nfc_dev); + if (ret <= 0) { + nfc_dev->nfc_disable_intr(nfc_dev); + pr_err("%s: - error waiting for core reset rsp ret %d\n", + __func__, ret); + + goto err_nfcc_hw_check; + } + } + + /* Read Response of RESET command */ + ret = nfc_dev->nfc_read(nfc_dev, nci_reset_rsp, NCI_RESET_RSP_LEN); + if (ret <= 0) { + pr_err("%s: - nfc rst rsp read err %d\n", __func__, + ret); + goto err_nfcc_hw_check; + } + + if (nfc_dev->interface == PLATFORM_IF_I2C) { + ret = is_data_available_for_read(nfc_dev); + if (ret <= 0) { + pr_err("%s: - error waiting for core reset ntf ret %d\n", + __func__, ret); + nfc_dev->nfc_disable_intr(nfc_dev); + goto err_nfcc_hw_check; + } + } + + /* Read Notification of RESET command */ + ret = nfc_dev->nfc_read(nfc_dev, nci_reset_ntf, NCI_RESET_NTF_LEN); + if (ret <= 0) { + pr_err("%s: nfc nfc read error %d\n", __func__, ret); + goto err_nfcc_hw_check; + } + + reset_ntf_len = NCI_HDR_LEN + nci_reset_ntf[NCI_PAYLOAD_LEN_IDX] - 1; + if (reset_ntf_len > NCI_HDR_LEN) { + nfc_dev->nqx_info.info.chip_type = + nci_reset_ntf[reset_ntf_len - NFC_CHIP_TYPE_OFF]; + nfc_dev->nqx_info.info.rom_version = + nci_reset_ntf[reset_ntf_len - NFC_ROM_VERSION_OFF]; + nfc_dev->nqx_info.info.fw_major = + nci_reset_ntf[reset_ntf_len - NFC_FW_MAJOR_OFF]; + nfc_dev->nqx_info.info.fw_minor = + nci_reset_ntf[reset_ntf_len]; + } + pr_debug("%s: - NFC reset rsp : NfcNciRx %x %x %x\n", + __func__, nci_reset_rsp[0], + nci_reset_rsp[1], nci_reset_rsp[2]); + +err_nfcc_reset_failed: + pr_info("NFC chip_type = %x\n", + nfc_dev->nqx_info.info.chip_type); + pr_info("NFC fw version = %x.%x.%x\n", + nfc_dev->nqx_info.info.rom_version, + nfc_dev->nqx_info.info.fw_major, + nfc_dev->nqx_info.info.fw_minor); + + switch (nfc_dev->nqx_info.info.chip_type) { + case NFCC_SN100_A: + case NFCC_SN100_B: + pr_debug("%s: ## NFCC == SN100x ##\n", __func__); + break; + default: + pr_err("%s: - NFCC HW not Supported\n", __func__); + break; + } + + ret = 0; + nfc_dev->nfc_ven_enabled = true; + + goto disable_i3c_intr; + +err_nfcc_hw_check: + if (nfc_dev->interface == PLATFORM_IF_I2C) + gpio_set_ven(nfc_dev, 0); + + gpio_set_value(nfc_dev->gpio.dwl_req, 0); + + ret = -ENXIO; + pr_debug("%s: - NFCC HW not available\n", __func__); + +disable_i3c_intr: + if (nfc_dev->interface == PLATFORM_IF_I3C) + nfc_dev->nfc_disable_intr(nfc_dev); +done: + kfree(nci_reset_rsp); + kfree(nci_reset_ntf); + kfree(nci_get_version_cmd); + kfree(nci_get_version_rsp); + kfree(nci_reset_cmd); + + return ret; +} diff --git a/drivers/nfc/sn1xx/nfc_i2c_drv.c b/drivers/nfc/sn1xx/nfc_i2c_drv.c new file mode 100644 index 000000000000..473832fa27b8 --- /dev/null +++ b/drivers/nfc/sn1xx/nfc_i2c_drv.c @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. + */ + +#include + + +/** + * i2c_disable_irq() + * + * Check if interrupt is disabled or not + * and disable interrupt + * + * Return: int + */ +int i2c_disable_irq(struct nfc_dev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->i2c_dev.irq_enabled_lock, flags); + if (dev->i2c_dev.irq_enabled) { + disable_irq_nosync(dev->i2c_dev.client->irq); + dev->i2c_dev.irq_enabled = false; + } + spin_unlock_irqrestore(&dev->i2c_dev.irq_enabled_lock, flags); + + return 0; +} + +/** + * i2c_enable_irq() + * + * Check if interrupt is enabled or not + * and enable interrupt + * + * Return: int + */ +int i2c_enable_irq(struct nfc_dev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->i2c_dev.irq_enabled_lock, flags); + if (!dev->i2c_dev.irq_enabled) { + dev->i2c_dev.irq_enabled = true; + enable_irq(dev->i2c_dev.client->irq); + } + spin_unlock_irqrestore(&dev->i2c_dev.irq_enabled_lock, flags); + + return 0; +} + +static irqreturn_t i2c_irq_handler(int irq, void *dev_id) +{ + struct nfc_dev *nfc_dev = dev_id; + struct i2c_dev *i2c_dev = &nfc_dev->i2c_dev; + + if (device_may_wakeup(&i2c_dev->client->dev)) + pm_wakeup_event(&i2c_dev->client->dev, WAKEUP_SRC_TIMEOUT); + + i2c_disable_irq(nfc_dev); + wake_up(&nfc_dev->read_wq); + + return IRQ_HANDLED; +} + +int i2c_read(struct nfc_dev *dev, char *buf, size_t count) +{ + int ret; + + pr_debug("%s : reading %zu bytes.\n", __func__, count); + /* Read data */ + ret = i2c_master_recv(dev->i2c_dev.client, buf, count); + if (ret <= 0) { + pr_err("%s: i2c_master_recv returned %d\n", __func__, ret); + goto i2c_read_err; + } + if (ret > count) { + pr_err("%s: received too many bytes from i2c (%d)\n", + __func__, ret); + ret = -EIO; + } + /* delay for the slow nfc devices between susequent read operation */ + usleep_range(1000, 1100); +i2c_read_err: + return ret; +} + +int i2c_write(struct nfc_dev *dev, const char *buf, size_t count, + int max_retry_cnt) +{ + int ret = -EINVAL; + int retry_cnt; + + pr_debug("%s : writing %zu bytes.\n", __func__, count); + + for (retry_cnt = 1; retry_cnt <= max_retry_cnt; retry_cnt++) { + ret = i2c_master_send(dev->i2c_dev.client, buf, count); + if (ret <= 0) { + pr_warn("%s: write failed, Maybe in Standby Mode - Retry(%d)\n", + __func__, retry_cnt); + usleep_range(1000, 1100); + } else if (ret == count) + break; + } + return ret; +} + +ssize_t nfc_i2c_dev_read(struct file *filp, char __user *buf, + size_t count, loff_t *offset) +{ + int ret; + char *tmp = NULL; + struct nfc_dev *nfc_dev = filp->private_data; + struct i2c_dev *i2c_dev = &nfc_dev->i2c_dev; + + if (!nfc_dev) + return -ENODEV; + + if (count > nfc_dev->kbuflen) + count = nfc_dev->kbuflen; + + pr_debug("%s : reading %zu bytes.\n", __func__, count); + + mutex_lock(&nfc_dev->read_mutex); + + if (!gpio_get_value(nfc_dev->gpio.irq)) { + if (filp->f_flags & O_NONBLOCK) { + pr_err(":f_falg has O_NONBLOCK. EAGAIN\n"); + ret = -EAGAIN; + goto err; + } + while (1) { + ret = 0; + if (!i2c_dev->irq_enabled) { + i2c_dev->irq_enabled = true; + enable_irq(i2c_dev->client->irq); + } + if (!gpio_get_value(nfc_dev->gpio.irq)) { + ret = wait_event_interruptible(nfc_dev->read_wq, + !i2c_dev->irq_enabled); + + if (ret) { + pr_err("error wakeup of read wq\n"); + goto err; + } + } + i2c_disable_irq(nfc_dev); + + if (gpio_get_value(nfc_dev->gpio.irq)) + break; + + if (!gpio_get_value(nfc_dev->gpio.ven)) { + pr_info("%s: ven low in read !\n", __func__); + ret = -ENODEV; + goto err; + } + + pr_warn("%s: spurious interrupt detected\n", __func__); + } + } + + tmp = nfc_dev->kbuf; + if (!tmp) { + pr_err("%s: device doesn't exist anymore\n", __func__); + ret = -ENODEV; + goto err; + } + memset(tmp, 0x00, count); + + /* Read data */ + ret = i2c_read(nfc_dev, tmp, count); + if (ret <= 0) { + pr_err("%s: i2c_master_recv returned %d\n", __func__, ret); + goto err; + } + + /* check if it's response of cold reset command + * NFC HAL process shouldn't receive this data as + * command was sent by SPI driver + */ + if (nfc_dev->cold_reset.rsp_pending + && (tmp[0] == COLD_RESET_RSP_GID) + && (tmp[1] == COLD_RESET_OID)) { + read_cold_reset_rsp(nfc_dev, tmp); + nfc_dev->cold_reset.rsp_pending = false; + wake_up_interruptible(&nfc_dev->cold_reset.read_wq); + mutex_unlock(&nfc_dev->read_mutex); + /* + * NFC process doesn't know about cold reset command + * being sent as it was initiated by eSE process + * we shouldn't return any data to NFC process + */ + return 0; + } + if (copy_to_user(buf, tmp, ret)) { + pr_warn("%s : failed to copy to user space\n", __func__); + ret = -EFAULT; + } + +err: + mutex_unlock(&nfc_dev->read_mutex); + return ret; +} + +ssize_t nfc_i2c_dev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offset) +{ + int ret; + char *tmp = NULL; + struct nfc_dev *nfc_dev = filp->private_data; + + if (!nfc_dev) { + ret = -ENODEV; + goto out; + } + + if (count > nfc_dev->kbuflen) { + pr_err("%s: out of memory\n", __func__); + ret = -ENOMEM; + goto out; + } + + tmp = memdup_user(buf, count); + if (IS_ERR(tmp)) { + pr_err("%s: memdup_user failed\n", __func__); + ret = PTR_ERR(tmp); + goto out; + } + + ret = i2c_write(nfc_dev, tmp, count, NO_RETRY); + if (ret != count) { + pr_err("%s: failed to write %d\n", __func__, ret); + ret = -EIO; + goto out_free; + } + +out_free: + kfree(tmp); +out: + return ret; +} + +static const struct file_operations nfc_i2c_dev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = nfc_i2c_dev_read, + .write = nfc_i2c_dev_write, + .open = nfc_dev_open, + .release = nfc_dev_close, + .unlocked_ioctl = nfc_dev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = nfc_compat_dev_ioctl, +#endif +}; + +int nfc_i2c_dev_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + struct nfc_dev *nfc_dev = NULL; + struct i2c_dev *i2c_dev = NULL; + struct platform_gpio nfc_gpio; + struct platform_ldo nfc_ldo; + + pr_debug("%s: enter\n", __func__); + + if (client->dev.of_node && !mmi_device_is_available(client->dev.of_node)) { + pr_err("%s : mmi: device not supported\n", __func__); + return -ENODEV; + } else { + pr_err("%s : supported device found\n", __func__); + } + + //retrieve details of gpios from dt + + ret = nfc_parse_dt(&client->dev, &nfc_gpio, &nfc_ldo, PLATFORM_IF_I2C); + if (ret) { + pr_err("%s : failed to parse dt\n", __func__); + goto err; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("%s : need I2C_FUNC_I2C\n", __func__); + ret = -ENODEV; + goto err; + } + nfc_dev = kzalloc(sizeof(struct nfc_dev), GFP_KERNEL); + if (nfc_dev == NULL) { + ret = -ENOMEM; + goto err; + } + nfc_dev->interface = PLATFORM_IF_I2C; + nfc_dev->i2c_dev.client = client; + i2c_dev = &nfc_dev->i2c_dev; + nfc_dev->nfc_read = i2c_read; + nfc_dev->nfc_write = i2c_write; + nfc_dev->nfc_enable_intr = i2c_enable_irq; + nfc_dev->nfc_disable_intr = i2c_disable_irq; + + ret = configure_gpio(nfc_gpio.ven, GPIO_OUTPUT); + if (ret) { + pr_err("%s: unable to request nfc reset gpio [%d]\n", + __func__, nfc_gpio.ven); + goto err_free_nfc_dev; + } + ret = configure_gpio(nfc_gpio.irq, GPIO_IRQ); + if (ret <= 0) { + pr_err("%s: unable to request nfc irq gpio [%d]\n", + __func__, nfc_gpio.irq); + goto err_free_ven; + } + client->irq = ret; + ret = configure_gpio(nfc_gpio.dwl_req, GPIO_OUTPUT); + if (ret) { + pr_err("%s: unable to request nfc firm downl gpio [%d]\n", + __func__, nfc_gpio.dwl_req); + goto err_free_irq; + } + + ret = configure_gpio(nfc_gpio.clkreq, GPIO_INPUT); + if (ret) { + pr_err("%s: unable to request nfc clkreq gpio [%d]\n", + __func__, nfc_gpio.clkreq); + goto err_free_dwl_req; + } + + nfc_dev->gpio.ven = nfc_gpio.ven; + nfc_dev->gpio.irq = nfc_gpio.irq; + nfc_dev->gpio.dwl_req = nfc_gpio.dwl_req; + nfc_dev->gpio.clkreq = nfc_gpio.clkreq; + + /* init mutex and queues */ + init_waitqueue_head(&nfc_dev->read_wq); + mutex_init(&nfc_dev->read_mutex); + mutex_init(&nfc_dev->dev_ref_mutex); + spin_lock_init(&i2c_dev->irq_enabled_lock); + + ret = nfc_misc_probe(nfc_dev, &nfc_i2c_dev_fops, DEV_COUNT, + NFC_CHAR_DEV_NAME, CLASS_NAME); + if (ret) { + pr_err("%s: nfc_misc_probe failed\n", __func__); + goto err_mutex_destroy; + } + /* interrupt initializations */ + pr_info("%s : requesting IRQ %d\n", __func__, client->irq); + i2c_dev->irq_enabled = true; + ret = request_irq(client->irq, i2c_irq_handler, + IRQF_TRIGGER_HIGH, client->name, nfc_dev); + if (ret) { + pr_err("%s: request_irq failed\n", __func__); + goto err_nfc_misc_remove; + } + i2c_disable_irq(nfc_dev); + i2c_set_clientdata(client, nfc_dev); + + ret = nfc_ldo_config(&client->dev, nfc_dev); + if (ret) { + pr_err("LDO config failed\n"); + goto err_ldo_config_failed; + } + + ret = nfcc_hw_check(nfc_dev); + if (ret) { + pr_err("nfc hw check failed ret %d\n", ret); + } + + device_init_wakeup(&client->dev, true); + enable_irq_wake(client->irq); + i2c_dev->irq_wake_up = false; + nfc_dev->is_ese_session_active = false; + + pr_info("%s success\n", __func__); + return 0; + +err_ldo_config_failed: + if (nfc_dev->reg) { + nfc_ldo_unvote(nfc_dev); + regulator_put(nfc_dev->reg); + } + free_irq(client->irq, nfc_dev); +err_nfc_misc_remove: + nfc_misc_remove(nfc_dev, DEV_COUNT); +err_mutex_destroy: + mutex_destroy(&nfc_dev->dev_ref_mutex); + mutex_destroy(&nfc_dev->read_mutex); + gpio_free(nfc_dev->gpio.clkreq); +err_free_dwl_req: + gpio_free(nfc_dev->gpio.dwl_req); +err_free_irq: + gpio_free(nfc_dev->gpio.irq); +err_free_ven: + gpio_free(nfc_dev->gpio.ven); +err_free_nfc_dev: + kfree(nfc_dev); +err: + pr_err("%s: failed\n", __func__); + return ret; +} + +int nfc_i2c_dev_remove(struct i2c_client *client) +{ + int ret = 0; + struct nfc_dev *nfc_dev = NULL; + + pr_info("%s: remove device\n", __func__); + nfc_dev = i2c_get_clientdata(client); + if (!nfc_dev) { + pr_err("%s: device doesn't exist anymore\n", __func__); + ret = -ENODEV; + return ret; + } + + gpio_set_value(nfc_dev->gpio.ven, 0); + // HW dependent delay before LDO goes into LPM mode + usleep_range(10000, 10100); + if (nfc_dev->reg) { + nfc_ldo_unvote(nfc_dev); + regulator_put(nfc_dev->reg); + } + + device_init_wakeup(&client->dev, false); + free_irq(client->irq, nfc_dev); + nfc_misc_remove(nfc_dev, DEV_COUNT); + mutex_destroy(&nfc_dev->dev_ref_mutex); + mutex_destroy(&nfc_dev->read_mutex); + + if (gpio_is_valid(nfc_dev->gpio.clkreq)) + gpio_free(nfc_dev->gpio.clkreq); + + if (gpio_is_valid(nfc_dev->gpio.dwl_req)) + gpio_free(nfc_dev->gpio.dwl_req); + + if (gpio_is_valid(nfc_dev->gpio.irq)) + gpio_free(nfc_dev->gpio.irq); + + if (gpio_is_valid(nfc_dev->gpio.ven)) + gpio_free(nfc_dev->gpio.ven); + + kfree(nfc_dev); + return ret; +} + +int nfc_i2c_dev_suspend(struct device *device) +{ + struct i2c_client *client = to_i2c_client(device); + struct nfc_dev *nfc_dev = i2c_get_clientdata(client); + struct i2c_dev *i2c_dev = &nfc_dev->i2c_dev; + + if (device_may_wakeup(&client->dev) && i2c_dev->irq_enabled) { + // if (!enable_irq_wake(client->irq)) + // i2c_dev->irq_wake_up = true; + i2c_dev->irq_wake_up = true; + } + return 0; +} + +int nfc_i2c_dev_resume(struct device *device) +{ + struct i2c_client *client = to_i2c_client(device); + struct nfc_dev *nfc_dev = i2c_get_clientdata(client); + struct i2c_dev *i2c_dev = &nfc_dev->i2c_dev; + + if (device_may_wakeup(&client->dev) && i2c_dev->irq_wake_up) { + // if (!disable_irq_wake(client->irq)) + // i2c_dev->irq_wake_up = false; + i2c_dev->irq_wake_up = false; + } + return 0; +} + +static const struct i2c_device_id nfc_i2c_dev_id[] = { + {NFC_I2C_DEV_ID, 0}, + {} +}; + +static const struct of_device_id nfc_i2c_dev_match_table[] = { + {.compatible = NFC_I2C_DRV_STR,}, + {} +}; + +static const struct dev_pm_ops nfc_i2c_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(nfc_i2c_dev_suspend, nfc_i2c_dev_resume) +}; + +static struct i2c_driver nfc_i2c_dev_driver = { + .id_table = nfc_i2c_dev_id, + .probe = nfc_i2c_dev_probe, + .remove = nfc_i2c_dev_remove, + .driver = { + .name = NFC_I2C_DRV_STR, + .pm = &nfc_i2c_dev_pm_ops, + .of_match_table = nfc_i2c_dev_match_table, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; + +MODULE_DEVICE_TABLE(of, nfc_i2c_dev_match_table); + +static int __init nfc_i2c_dev_init(void) +{ + int ret = 0; + + ret = i2c_add_driver(&nfc_i2c_dev_driver); + if (ret != 0) + pr_err("NFC I2C add driver error ret %d\n", ret); + return ret; +} + +module_init(nfc_i2c_dev_init); + +static void __exit nfc_i2c_dev_exit(void) +{ + pr_debug("Unloading NFC I2C driver\n"); + i2c_del_driver(&nfc_i2c_dev_driver); +} + +module_exit(nfc_i2c_dev_exit); + +MODULE_DESCRIPTION("QTI NFC I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/nfc/sn2xx/Android.mk b/drivers/nfc/sn2xx/Android.mk new file mode 100644 index 000000000000..b0c96950b347 --- /dev/null +++ b/drivers/nfc/sn2xx/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := nfc_nci.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_info.ko +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/nfc/sn2xx/Kbuild b/drivers/nfc/sn2xx/Kbuild new file mode 100644 index 000000000000..645e1e8da222 --- /dev/null +++ b/drivers/nfc/sn2xx/Kbuild @@ -0,0 +1,8 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += nfc_nci.o +nfc_nci-objs:= nfc_common.o nfc_i2c_drv.o ese_cold_reset.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../mmi_info/Module.symvers diff --git a/drivers/nfc/sn2xx/Makefile b/drivers/nfc/sn2xx/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/nfc/sn2xx/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/nfc/sn2xx/ese_cold_reset.c b/drivers/nfc/sn2xx/ese_cold_reset.c new file mode 100644 index 000000000000..87158a0e00eb --- /dev/null +++ b/drivers/nfc/sn2xx/ese_cold_reset.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include "nfc_common.h" + +/** + * send_ese_cmd() - Send eSE command to NFC controller. + * @nfc_dev: NFC device handle. + * + * Return: 0 on pass and negative value on failure. + */ +static int send_ese_cmd(struct nfc_dev *nfc_dev) +{ + int ret; + + if (nfc_dev->nfc_state == NFC_STATE_FW_DWL) { + dev_err(nfc_dev->nfc_device, + "cannot send ese cmd as FW download is in-progress\n"); + return -EBUSY; + } + if (!gpio_get_value(nfc_dev->configs.gpio.ven)) { + dev_err(nfc_dev->nfc_device, + "cannot send ese cmd as NFCC powered off\n"); + return -ENODEV; + } + if (nfc_dev->cold_reset.cmd_buf == NULL) + return -EFAULT; + ret = nfc_dev->nfc_write(nfc_dev, nfc_dev->cold_reset.cmd_buf, + nfc_dev->cold_reset.cmd_len, + MAX_RETRY_COUNT); + if (ret <= 0) + dev_err(nfc_dev->nfc_device, + "%s: write failed after max retry, ret %d\n", + __func__, ret); + + return ret; +} + +/** + * read_cold_reset_rsp() - Read response of the cold reset command. + * @nfc_dev: NFC device handle. + * @header: Pointer to NCI header if it is already read. + * + * Return: 0 on pass and negative value on failure. + */ +int read_cold_reset_rsp(struct nfc_dev *nfc_dev, char *header) +{ + int ret = -EPERM; + struct cold_reset *cold_rst = &nfc_dev->cold_reset; + char *rsp_buf = NULL; + + if (cold_rst->rsp_len < COLD_RESET_RSP_LEN) { + dev_err(nfc_dev->nfc_device, + "%s: received cold reset rsp buffer length is invalid\n", + __func__); + return -EINVAL; + } + + rsp_buf = kzalloc(cold_rst->rsp_len, GFP_DMA | GFP_KERNEL); + if (!rsp_buf) + return -ENOMEM; + + /* + * read header if NFC is disabled + * for enable case, header is read by nfc read thread(for i2c) + */ + if ((!cold_rst->is_nfc_enabled) && + (nfc_dev->interface == PLATFORM_IF_I2C)) { + ret = i2c_master_recv(nfc_dev->i2c_dev.client, rsp_buf, NCI_HDR_LEN); + if (ret <= 0) { + dev_err(nfc_dev->nfc_device, + "%s: failure to read cold reset rsp header\n", + __func__); + ret = -EIO; + goto error; + } + /* + * return failure, if packet is not a response packet or + * if response's OID doesn't match with the CMD's OID + */ + if (!(rsp_buf[0] & NCI_RSP_PKT_TYPE) || + (!cold_rst->cmd_buf) || + (rsp_buf[1] != cold_rst->cmd_buf[1])) { + + dev_err(nfc_dev->nfc_device, + "%s: - invalid cold reset response 0x%x 0x%x\n", + __func__, rsp_buf[0], rsp_buf[1]); + ret = -EINVAL; + goto error; + } + } else if (header) { + memcpy(rsp_buf, header, NCI_HDR_LEN); + } else { + dev_err(nfc_dev->nfc_device, + "%s: - invalid or NULL header\n", __func__); + ret = -EINVAL; + goto error; + } + + if ((NCI_HDR_LEN + rsp_buf[NCI_PAYLOAD_LEN_IDX]) > + cold_rst->rsp_len) { + dev_err(nfc_dev->nfc_device, + "%s: - no space for cold_reset resp\n", __func__); + ret = -ENOMEM; + goto error; + } + + if (nfc_dev->interface == PLATFORM_IF_I2C) { + ret = nfc_dev->nfc_read(nfc_dev, + &rsp_buf[NCI_PAYLOAD_IDX], + rsp_buf[NCI_PAYLOAD_LEN_IDX], + NCI_CMD_RSP_TIMEOUT); + + if (ret <= 0) { + dev_err(nfc_dev->nfc_device, + "%s: failure to read cold reset rsp payload\n", + __func__); + ret = -EIO; + goto error; + } + ret = cold_rst->status = rsp_buf[NCI_PAYLOAD_IDX]; + + pr_debug("nfc ese rsp hdr 0x%x 0x%x 0x%x, payload byte0 0x%x\n", + rsp_buf[0], rsp_buf[1], rsp_buf[2], rsp_buf[3]); + } + +error: + kfree(rsp_buf); + + return ret; +} + + +/** + * ese_cold_reset_ioctl() - This function handles the eSE cold reset ioctls. + * @nfc_dev: NFC device handle. + * @arg: ioctl argument. + * + * Return: 0 on pass and negative value on failure. + */ + +int ese_cold_reset_ioctl(struct nfc_dev *nfc_dev, unsigned long arg) +{ + int ret; + struct ese_ioctl_arg ioctl_arg; + struct ese_cold_reset_arg *cold_reset_arg = NULL; + + if (!arg) { + dev_err(nfc_dev->nfc_device, "arg is invalid\n"); + return -EINVAL; + } + + ret = copy_from_user((void *)&ioctl_arg, (const void *)arg, + sizeof(ioctl_arg)); + if (ret) { + dev_err(nfc_dev->nfc_device, + "ese ioctl arg copy from user failed\n"); + return -EFAULT; + } + + cold_reset_arg = kzalloc(sizeof(struct ese_cold_reset_arg), GFP_KERNEL); + if (!cold_reset_arg) + return -ENOMEM; + + mutex_lock(&nfc_dev->write_mutex); + ret = copy_struct_from_user(cold_reset_arg, + sizeof(struct ese_cold_reset_arg), + u64_to_user_ptr(ioctl_arg.buf), + sizeof(struct ese_cold_reset_arg)); + if (ret) { + dev_err(nfc_dev->nfc_device, + "ese ioctl arg buffer copy from user failed\n"); + + ret = -EFAULT; + goto err; + } + + switch (cold_reset_arg->sub_cmd) { + + case ESE_COLD_RESET_DO: + + /* + * cold reset allowed during protection enable, only if the + * source is same as the one which enabled protection. + */ + if (nfc_dev->cold_reset.is_crp_en && + (cold_reset_arg->src != + nfc_dev->cold_reset.last_src_ese_prot)) { + dev_err(nfc_dev->nfc_device, + "cold reset from %d denied, protection is on\n", + cold_reset_arg->src); + ret = -EACCES; + goto err; + } + + nfc_dev->cold_reset.cmd_buf = kzalloc(COLD_RESET_CMD_LEN, + GFP_DMA | GFP_KERNEL); + if (!nfc_dev->cold_reset.cmd_buf) { + ret = -ENOMEM; + goto err; + } + + nfc_dev->cold_reset.cmd_buf[0] = PROP_NCI_CMD_GID; + nfc_dev->cold_reset.cmd_buf[1] = COLD_RESET_OID; + nfc_dev->cold_reset.cmd_buf[2] = COLD_RESET_CMD_PL_LEN; + nfc_dev->cold_reset.cmd_len = NCI_HDR_LEN + + COLD_RESET_CMD_PL_LEN; + nfc_dev->cold_reset.rsp_len = COLD_RESET_RSP_LEN; + break; + + case ESE_COLD_RESET_PROTECT_EN: + + if (nfc_dev->cold_reset.is_crp_en) { + if (cold_reset_arg->src != + nfc_dev->cold_reset.last_src_ese_prot) { + dev_err(nfc_dev->nfc_device, + "ese protection enable denied\n"); + ret = -EACCES; + goto err; + } + pr_warn("ese protection already enabled\n"); + + ret = 0; + /* free buffers and exit with pass */ + goto err; + } + + case ESE_COLD_RESET_PROTECT_DIS: + + if (nfc_dev->cold_reset.is_crp_en && + cold_reset_arg->src != + nfc_dev->cold_reset.last_src_ese_prot) { + pr_err("ese cold reset protection disable denied\n"); + ret = -EACCES; + goto err; + } + nfc_dev->cold_reset.cmd_buf = kzalloc(COLD_RESET_PROT_CMD_LEN, + GFP_DMA | GFP_KERNEL); + if (!nfc_dev->cold_reset.cmd_buf) { + ret = -ENOMEM; + goto err; + } + + nfc_dev->cold_reset.cmd_buf[0] = PROP_NCI_CMD_GID; + nfc_dev->cold_reset.cmd_buf[1] = COLD_RESET_PROT_OID; + nfc_dev->cold_reset.cmd_buf[2] = COLD_RESET_PROT_CMD_PL_LEN; + nfc_dev->cold_reset.cmd_len = NCI_HDR_LEN + + COLD_RESET_PROT_CMD_PL_LEN; + nfc_dev->cold_reset.rsp_len = COLD_RESET_PROT_RSP_LEN; + if (cold_reset_arg->sub_cmd == ESE_COLD_RESET_PROTECT_EN) + nfc_dev->cold_reset.cmd_buf[3] = 0x1; + else + nfc_dev->cold_reset.cmd_buf[3] = 0x0; + + break; + + default: + pr_err("%s invalid ese ioctl sub cmd %d\n", __func__, + cold_reset_arg->sub_cmd); + ret = -ENOIOCTLCMD; + goto err; + } + + pr_debug("nfc ese cmd hdr 0x%x 0x%x 0x%x\n", + nfc_dev->cold_reset.cmd_buf[0], + nfc_dev->cold_reset.cmd_buf[1], + nfc_dev->cold_reset.cmd_buf[2]); + + ret = send_ese_cmd(nfc_dev); + if (ret <= 0) { + pr_err("failed to send ese command\n"); + goto err; + } + + nfc_dev->cold_reset.rsp_pending = true; + + /* check if NFC is enabled */ + if (nfc_dev->cold_reset.is_nfc_enabled) { + /* + * nfc_read thread will initiate cold reset response + * and it will signal for data available + */ + wait_event_interruptible(nfc_dev->cold_reset.read_wq, + !nfc_dev->cold_reset.rsp_pending); + } else { + + /* + * Read data as NFC read thread is not active + */ + + if (nfc_dev->interface == PLATFORM_IF_I2C) { + ret = is_nfc_data_available_for_read(nfc_dev); + if (ret <= 0) { + nfc_dev->nfc_disable_intr(nfc_dev); + nfc_dev->cold_reset.rsp_pending = false; + goto err; + } + + ret = read_cold_reset_rsp(nfc_dev, NULL); + nfc_dev->cold_reset.rsp_pending = false; + if (ret < 0) { + pr_err("%s rsp read err\n", __func__); + goto err; + } + } else { + /* + * Enable intr as it is disabled when NFC is in disable + * state + */ + nfc_dev->nfc_enable_intr(nfc_dev); + + wait_event_interruptible( + nfc_dev->cold_reset.read_wq, + !nfc_dev->cold_reset.rsp_pending); + } + + nfc_dev->nfc_disable_intr(nfc_dev); + } + + if (cold_reset_arg->sub_cmd == ESE_COLD_RESET_PROTECT_EN) { + nfc_dev->cold_reset.is_crp_en = true; + nfc_dev->cold_reset.last_src_ese_prot = cold_reset_arg->src; + } else if (cold_reset_arg->sub_cmd == ESE_COLD_RESET_PROTECT_DIS) { + nfc_dev->cold_reset.is_crp_en = false; + nfc_dev->cold_reset.last_src_ese_prot = + ESE_COLD_RESET_ORIGIN_NONE; + } else + pr_debug("ese cmd is %d\n", cold_reset_arg->sub_cmd); + + ret = nfc_dev->cold_reset.status; + +err: + kfree(nfc_dev->cold_reset.cmd_buf); + nfc_dev->cold_reset.cmd_buf = NULL; + kfree(cold_reset_arg); + cold_reset_arg = NULL; + + mutex_unlock(&nfc_dev->write_mutex); + + return ret; +} diff --git a/drivers/nfc/sn2xx/ese_cold_reset.h b/drivers/nfc/sn2xx/ese_cold_reset.h new file mode 100644 index 000000000000..a55120a82fa5 --- /dev/null +++ b/drivers/nfc/sn2xx/ese_cold_reset.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + */ + +#ifndef __ESE_COLD_RESET_H +#define __ESE_COLD_RESET_H + +#include + +#define MAX_BUFF_SIZE 264 + +/* ESE_COLD_RESET MACROS */ +#define COLD_RESET_CMD_LEN 3 +#define COLD_RESET_RSP_LEN 4 +#define COLD_RESET_PROT_CMD_LEN 4 +#define COLD_RESET_PROT_RSP_LEN 4 +#define PROP_NCI_CMD_GID 0x2F +#define COLD_RESET_CMD_PL_LEN 0x00 +#define COLD_RESET_PROT_CMD_PL_LEN 0x01 +#define PROP_NCI_RSP_GID 0x4F +#define COLD_RESET_OID 0x1E +#define COLD_RESET_PROT_OID 0x1F + +#define ESE_COLD_RESET _IOWR(NFCC_MAGIC, 0x08, struct ese_ioctl_arg) + +enum ese_ioctl_arg_type { + ESE_ARG_TYPE_COLD_RESET = 0, +}; + +/* ESE_COLD_RESET ioctl origin, max 4 are supported */ +enum ese_cold_reset_origin { + ESE_COLD_RESET_ORIGIN_ESE = 0, + ESE_COLD_RESET_ORIGIN_NFC, + ESE_COLD_RESET_ORIGIN_OTHER = 0x20, + ESE_COLD_RESET_ORIGIN_NONE = 0xFF, +}; + +/* ESE_COLD_RESET ioctl sub commands, max 8 are supported */ +enum ese_cold_reset_sub_cmd { + ESE_COLD_RESET_DO = 0, + ESE_COLD_RESET_PROTECT_EN, + ESE_COLD_RESET_PROTECT_DIS, +}; + +/* Data passed in buf of ese cold reset ioctl */ +struct ese_cold_reset_arg { + __u8 src; + __u8 sub_cmd; + __u16 rfu; +}; + +/* Argument buffer passed to ese ioctl */ +struct ese_ioctl_arg { + __u64 buf; + __u32 buf_size; + __u8 type; +}; + +/* Features specific Parameters */ +struct cold_reset { + wait_queue_head_t read_wq; + char *cmd_buf; + uint16_t cmd_len; + uint16_t rsp_len; + /* Source of last ese protection command */ + uint8_t last_src_ese_prot; + uint8_t status; + /* Is cold reset protection enabled */ + bool is_crp_en; + bool rsp_pending; + /* Is NFC enabled from UI */ + bool is_nfc_enabled; +}; + +struct nfc_dev; +int ese_cold_reset_ioctl(struct nfc_dev *nfc_dev, unsigned long arg); +int read_cold_reset_rsp(struct nfc_dev *nfc_dev, char *header); + +#endif diff --git a/drivers/nfc/sn2xx/nfc_common.c b/drivers/nfc/sn2xx/nfc_common.c new file mode 100644 index 000000000000..5f62402d38e3 --- /dev/null +++ b/drivers/nfc/sn2xx/nfc_common.c @@ -0,0 +1,1017 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2021 NXP + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ******************************************************************************/ + +#include +#include +#include +#include "nfc_common.h" + +int nfc_parse_dt(struct device *dev, struct platform_configs *nfc_configs, + uint8_t interface) +{ + int ret; + struct device_node *np = dev->of_node; + struct platform_gpio *nfc_gpio = &nfc_configs->gpio; + struct platform_ldo *ldo = &nfc_configs->ldo; + + if (!np) { + pr_err("nfc of_node NULL\n"); + return -EINVAL; + } + + nfc_gpio->irq = -EINVAL; + nfc_gpio->dwl_req = -EINVAL; + nfc_gpio->ven = -EINVAL; + nfc_gpio->clkreq = -EINVAL; + + /* required for i2c based chips only */ + if (interface == PLATFORM_IF_I2C) { + nfc_gpio->irq = of_get_named_gpio(np, DTS_IRQ_GPIO_STR, 0); + if ((!gpio_is_valid(nfc_gpio->irq))) { + pr_err("nfc irq gpio invalid %d\n", nfc_gpio->irq); + return -EINVAL; + } + pr_info("%s: irq %d\n", __func__, nfc_gpio->irq); + } + nfc_gpio->ven = of_get_named_gpio(np, DTS_VEN_GPIO_STR, 0); + if ((!gpio_is_valid(nfc_gpio->ven))) { + pr_err("nfc ven gpio invalid %d\n", nfc_gpio->ven); + return -EINVAL; + } + + nfc_gpio->dwl_req = of_get_named_gpio(np, DTS_FWDN_GPIO_STR, 0); + + /* not returning failure for dwl gpio as it is optional for sn220 */ + if ((!gpio_is_valid(nfc_gpio->dwl_req))) + pr_warn("nfc dwl_req gpio invalid %d\n", nfc_gpio->dwl_req); + + nfc_gpio->clkreq = of_get_named_gpio(np, DTS_CLKREQ_GPIO_STR, 0); + if (!gpio_is_valid(nfc_gpio->clkreq)) { + dev_err(dev, "clkreq gpio invalid %d\n", nfc_gpio->dwl_req); + return -EINVAL; + } + + pr_info("%s: ven %d, dwl req %d, clkreq %d\n", __func__, + nfc_gpio->ven, nfc_gpio->dwl_req, nfc_gpio->clkreq); + + // optional property + ret = of_property_read_u32_array(np, NFC_LDO_VOL_DT_NAME, + (u32 *) ldo->vdd_levels, + ARRAY_SIZE(ldo->vdd_levels)); + if (ret) { + dev_err(dev, "error reading NFC VDDIO min and max value\n"); + // set default as per datasheet + ldo->vdd_levels[0] = NFC_VDDIO_MIN; + ldo->vdd_levels[1] = NFC_VDDIO_MAX; + } + + // optional property + ret = of_property_read_u32(np, NFC_LDO_CUR_DT_NAME, &ldo->max_current); + if (ret) { + dev_err(dev, "error reading NFC current value\n"); + // set default as per datasheet + ldo->max_current = NFC_CURRENT_MAX; + } + + return 0; +} + +/** + * nfc_ldo_vote() + * @nfc_dev: NFC device containing regulator handle + * + * LDO voting based on voltage and current entries in DT + * + * Return: 0 on success and -ve on failure + */ +int nfc_ldo_vote(struct nfc_dev *nfc_dev) +{ + int ret; + + ret = regulator_set_voltage(nfc_dev->reg, + nfc_dev->configs.ldo.vdd_levels[0], + nfc_dev->configs.ldo.vdd_levels[1]); + if (ret < 0) { + pr_err("%s: set voltage failed\n", __func__); + return ret; + } + + /* pass expected current from NFC in uA */ + ret = regulator_set_load(nfc_dev->reg, nfc_dev->configs.ldo.max_current); + if (ret < 0) { + pr_err("%s: set load failed\n", __func__); + return ret; + } + + ret = regulator_enable(nfc_dev->reg); + if (ret < 0) + pr_err("%s: regulator_enable failed\n", __func__); + else + nfc_dev->is_vreg_enabled = true; + return ret; +} + +/** + * nfc_ldo_config() + * @dev: device instance to read DT entry + * @nfc_dev: NFC device containing regulator handle + * + * Configure LDO if entry is present in DT file otherwise + * return with success as it's optional + * + * Return: 0 on success and -ve on failure + */ +int nfc_ldo_config(struct device *dev, struct nfc_dev *nfc_dev) +{ + int ret; + + if (of_get_property(dev->of_node, NFC_LDO_SUPPLY_NAME, NULL)) { + // Get the regulator handle + nfc_dev->reg = regulator_get(dev, NFC_LDO_SUPPLY_DT_NAME); + if (IS_ERR(nfc_dev->reg)) { + ret = PTR_ERR(nfc_dev->reg); + nfc_dev->reg = NULL; + pr_err("%s: regulator_get failed, ret = %d\n", + __func__, ret); + return ret; + } + } else { + nfc_dev->reg = NULL; + pr_err("%s: regulator entry not present\n", __func__); + // return success as it's optional to configure LDO + return 0; + } + + // LDO config supported by platform DT + ret = nfc_ldo_vote(nfc_dev); + if (ret < 0) { + pr_err("%s: LDO voting failed, ret = %d\n", __func__, ret); + regulator_put(nfc_dev->reg); + } + return ret; +} + +/** + * nfc_ldo_unvote() + * @nfc_dev: NFC device containing regulator handle + * + * set voltage and load to zero and disable regulator + * + * Return: 0 on success and -ve on failure + */ +int nfc_ldo_unvote(struct nfc_dev *nfc_dev) +{ + int ret; + + if (!nfc_dev->is_vreg_enabled) { + pr_err("%s: regulator already disabled\n", __func__); + return -EINVAL; + } + + ret = regulator_disable(nfc_dev->reg); + if (ret < 0) { + pr_err("%s: regulator_disable failed\n", __func__); + return ret; + } + nfc_dev->is_vreg_enabled = false; + + ret = regulator_set_voltage(nfc_dev->reg, 0, NFC_VDDIO_MAX); + if (ret < 0) { + pr_err("%s: set voltage failed\n", __func__); + return ret; + } + + ret = regulator_set_load(nfc_dev->reg, 0); + if (ret < 0) + pr_err("%s: set load failed\n", __func__); + return ret; +} + +void set_valid_gpio(int gpio, int value) +{ + if (gpio_is_valid(gpio)) { + pr_debug("%s gpio %d value %d\n", __func__, gpio, value); + gpio_set_value(gpio, value); + /* hardware dependent delay */ + usleep_range(NFC_GPIO_SET_WAIT_TIME_USEC, + NFC_GPIO_SET_WAIT_TIME_USEC + 100); + } +} + +int get_valid_gpio(int gpio) +{ + int value = -EINVAL; + + if (gpio_is_valid(gpio)) { + value = gpio_get_value(gpio); + pr_debug("%s gpio %d value %d\n", __func__, gpio, value); + } + return value; +} + +void gpio_set_ven(struct nfc_dev *nfc_dev, int value) +{ + struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio; + + if (gpio_get_value(nfc_gpio->ven) != value) { + pr_debug("%s: value %d\n", __func__, value); + + gpio_set_value(nfc_gpio->ven, value); + /* hardware dependent delay */ + usleep_range(NFC_GPIO_SET_WAIT_TIME_USEC, + NFC_GPIO_SET_WAIT_TIME_USEC + 100); + } +} + +int configure_gpio(unsigned int gpio, int flag) +{ + int ret; + + pr_debug("%s: nfc gpio [%d] flag [%01x]\n", __func__, gpio, flag); + + if (gpio_is_valid(gpio)) { + ret = gpio_request(gpio, "nfc_gpio"); + if (ret) { + pr_err("%s: unable to request nfc gpio [%d]\n", + __func__, gpio); + return ret; + } + /* set direction and value for output pin */ + if (flag & GPIO_OUTPUT) { + ret = gpio_direction_output(gpio, (GPIO_HIGH & flag)); + pr_debug("nfc o/p gpio %d level %d\n", gpio, gpio_get_value(gpio)); + } else { + ret = gpio_direction_input(gpio); + pr_debug("nfc i/p gpio %d\n", gpio); + } + + if (ret) { + pr_err + ("%s: unable to set direction for nfc gpio [%d]\n", + __func__, gpio); + gpio_free(gpio); + return ret; + } + // Consider value as control for input IRQ pin + if (flag & GPIO_IRQ) { + ret = gpio_to_irq(gpio); + if (ret < 0) { + pr_err("%s: unable to set irq for nfc gpio [%d]\n", + __func__, gpio); + gpio_free(gpio); + return ret; + } + pr_debug + ("%s: gpio_to_irq successful [%d]\n", + __func__, gpio); + return ret; + } + } else { + pr_err("%s: invalid gpio\n", __func__); + ret = -EINVAL; + } + return ret; +} + +void nfc_misc_unregister(struct nfc_dev *nfc_dev, int count) +{ + pr_debug("%s: entry\n", __func__); + + kfree(nfc_dev->kbuf); + device_destroy(nfc_dev->nfc_class, nfc_dev->devno); + cdev_del(&nfc_dev->c_dev); + class_destroy(nfc_dev->nfc_class); + unregister_chrdev_region(nfc_dev->devno, count); + if (nfc_dev->ipcl) + ipc_log_context_destroy(nfc_dev->ipcl); +} + +int nfc_misc_register(struct nfc_dev *nfc_dev, + const struct file_operations *nfc_fops, int count, + char *devname, char *classname) +{ + int ret = 0; + + ret = alloc_chrdev_region(&nfc_dev->devno, 0, count, devname); + if (ret < 0) { + pr_err("%s: failed to alloc chrdev region ret %d\n", + __func__, ret); + return ret; + } + nfc_dev->nfc_class = class_create(THIS_MODULE, classname); + if (IS_ERR(nfc_dev->nfc_class)) { + ret = PTR_ERR(nfc_dev->nfc_class); + pr_err("%s: failed to register device class ret %d\n", + __func__, ret); + unregister_chrdev_region(nfc_dev->devno, count); + return ret; + } + cdev_init(&nfc_dev->c_dev, nfc_fops); + ret = cdev_add(&nfc_dev->c_dev, nfc_dev->devno, count); + if (ret < 0) { + pr_err("%s: failed to add cdev ret %d\n", __func__, ret); + class_destroy(nfc_dev->nfc_class); + unregister_chrdev_region(nfc_dev->devno, count); + return ret; + } + nfc_dev->nfc_device = device_create(nfc_dev->nfc_class, NULL, + nfc_dev->devno, nfc_dev, "%s", devname); + if (IS_ERR(nfc_dev->nfc_device)) { + ret = PTR_ERR(nfc_dev->nfc_device); + pr_err("%s: failed to create the device ret %d\n", + __func__, ret); + cdev_del(&nfc_dev->c_dev); + class_destroy(nfc_dev->nfc_class); + unregister_chrdev_region(nfc_dev->devno, count); + return ret; + } + + nfc_dev->ipcl = ipc_log_context_create(NUM_OF_IPC_LOG_PAGES, + dev_name(nfc_dev->nfc_device), 0); + + nfc_dev->kbuflen = MAX_BUFFER_SIZE; + nfc_dev->kbuf = kzalloc(MAX_BUFFER_SIZE, GFP_KERNEL | GFP_DMA); + if (!nfc_dev->kbuf) { + nfc_misc_unregister(nfc_dev, count); + return -ENOMEM; + } + + nfc_dev->cold_reset.rsp_pending = false; + nfc_dev->cold_reset.is_nfc_enabled = false; + nfc_dev->cold_reset.is_crp_en = false; + nfc_dev->cold_reset.last_src_ese_prot = ESE_COLD_RESET_ORIGIN_NONE; + + init_waitqueue_head(&nfc_dev->cold_reset.read_wq); + + return 0; +} + +/* + * Power management of the eSE + * eSE and NFCC both are powered using VEN gpio, + * VEN HIGH - eSE and NFCC both are powered on + * VEN LOW - eSE and NFCC both are power down + */ +int nfc_ese_pwr(struct nfc_dev *nfc_dev, unsigned long arg) +{ + int ret = 0; + + if (arg == ESE_POWER_ON) { + /* + * Let's store the NFC VEN pin state + * will check stored value in case of eSE power off request, + * to find out if NFC MW also sent request to set VEN HIGH + * VEN state will remain HIGH if NFC is enabled otherwise + * it will be set as LOW + */ + nfc_dev->nfc_ven_enabled = gpio_get_value(nfc_dev->configs.gpio.ven); + if (!nfc_dev->nfc_ven_enabled) { + pr_debug("eSE HAL service setting ven HIGH\n"); + gpio_set_ven(nfc_dev, 1); + } else { + pr_debug("ven already HIGH\n"); + } + nfc_dev->is_ese_session_active = true; + } else if (arg == ESE_POWER_OFF) { + if (!nfc_dev->nfc_ven_enabled) { + pr_debug("NFC not enabled, disabling ven\n"); + gpio_set_ven(nfc_dev, 0); + } else { + pr_debug("keep ven high as NFC is enabled\n"); + } + nfc_dev->is_ese_session_active = false; + } else if (arg == ESE_POWER_STATE) { + /* get VEN gpio state for eSE, as eSE also enabled through same GPIO */ + ret = gpio_get_value(nfc_dev->configs.gpio.ven); + } else { + pr_err("%s bad arg %lu\n", __func__, arg); + ret = -ENOIOCTLCMD; + } + return ret; +} + +/* + * nfc_ioctl_power_states() - power control + * @nfc_dev: nfc device data structure + * @arg: mode that we want to move to + * + * Device power control. Depending on the arg value, device moves to + * different states, refer nfcc_ioctl_request in nfc_common.h for args + * + * Return: -ENOIOCTLCMD if arg is not supported, 0 in any other case + */ +static int nfc_ioctl_power_states(struct nfc_dev *nfc_dev, unsigned long arg) +{ + int ret = 0; + struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio; + + if (arg == NFC_POWER_OFF) { + /* + * We are attempting a hardware reset so let us disable + * interrupts to avoid spurious notifications to upper + * layers. + */ + nfc_dev->nfc_disable_intr(nfc_dev); + set_valid_gpio(nfc_gpio->dwl_req, 0); + gpio_set_ven(nfc_dev, 0); + nfc_dev->nfc_ven_enabled = false; + + } else if (arg == NFC_POWER_ON) { + nfc_dev->nfc_enable_intr(nfc_dev); + set_valid_gpio(nfc_gpio->dwl_req, 0); + + gpio_set_ven(nfc_dev, 1); + nfc_dev->nfc_ven_enabled = true; + + } else if (arg == NFC_FW_DWL_VEN_TOGGLE) { + /* + * We are switching to download Mode, toggle the enable pin + * in order to set the NFCC in the new mode + */ + nfc_dev->nfc_disable_intr(nfc_dev); + set_valid_gpio(nfc_gpio->dwl_req, 1); + nfc_dev->nfc_state = NFC_STATE_FW_DWL; + gpio_set_ven(nfc_dev, 0); + gpio_set_ven(nfc_dev, 1); + nfc_dev->nfc_enable_intr(nfc_dev); + } else if (arg == NFC_FW_DWL_HIGH) { + /* + * Setting firmware download gpio to HIGH + * before FW download start + */ + pr_debug("set fw gpio high\n"); + set_valid_gpio(nfc_gpio->dwl_req, 1); + nfc_dev->nfc_state = NFC_STATE_FW_DWL; + + } else if (arg == NFC_VEN_FORCED_HARD_RESET) { + nfc_dev->nfc_disable_intr(nfc_dev); + gpio_set_ven(nfc_dev, 0); + gpio_set_ven(nfc_dev, 1); + nfc_dev->nfc_enable_intr(nfc_dev); + pr_info("%s VEN forced reset done\n", __func__); + + } else if (arg == NFC_FW_DWL_LOW) { + /* + * Setting firmware download gpio to LOW + * FW download finished + */ + pr_debug("set fw gpio LOW\n"); + set_valid_gpio(nfc_gpio->dwl_req, 0); + nfc_dev->nfc_state = NFC_STATE_NCI; + + } else if (arg == NFC_ENABLE) { + /* + * Setting flag true when NFC is enabled + */ + nfc_dev->cold_reset.is_nfc_enabled = true; + } else if (arg == NFC_DISABLE) { + /* + * Setting flag true when NFC is disabled + */ + nfc_dev->cold_reset.is_nfc_enabled = false; + } else { + pr_err("%s bad arg %lu\n", __func__, arg); + ret = -ENOIOCTLCMD; + } + return ret; +} + +/* + * Inside nfc_ioctl_nfcc_info + * + * @brief nfc_ioctl_nfcc_info + * + * Check the NFC Chipset and firmware version details + */ +unsigned int nfc_ioctl_nfcc_info(struct file *filp, unsigned long arg) +{ + unsigned int r = 0; + struct nfc_dev *nfc_dev = filp->private_data; + + r = nfc_dev->nqx_info.i; + pr_debug("nfc : %s r = 0x%x\n", __func__, r); + + return r; +} + +/** @brief IOCTL function to be used to set or get data from upper layer. + * + * @param pfile fil node for opened device. + * @cmd IOCTL type from upper layer. + * @arg IOCTL arg from upper layer. + * + * @return 0 on success, error code for failures. + */ +#ifdef CONFIG_COMPAT +long nfc_compat_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct nfc_dev *nfc_dev = pfile->private_data; + + if (!nfc_dev) + return -ENODEV; + + arg = (compat_u64)arg; + pr_debug("%s cmd = %x arg = %zx\n", __func__, cmd, arg); + + switch (cmd) { + case NFC_SET_PWR: + ret = nfc_ioctl_power_states(nfc_dev, arg); + break; + case ESE_SET_PWR: + ret = nfc_ese_pwr(nfc_dev, arg); + break; + case ESE_GET_PWR: + ret = nfc_ese_pwr(nfc_dev, ESE_POWER_STATE); + break; + case NFCC_GET_INFO: + ret = nfc_ioctl_nfcc_info(pfile, arg); + break; + case NFC_GET_PLATFORM_TYPE: + ret = nfc_dev->interface; + break; + case ESE_COLD_RESET: + pr_debug("nfc ese cold reset ioctl\n"); + ret = ese_cold_reset_ioctl(nfc_dev, arg); + break; + case NFC_GET_IRQ_STATE: + ret = gpio_get_value(nfc_dev->configs.gpio.irq); + break; + default: + pr_err("%s Unsupported ioctl cmd 0x%x, arg %lu\n", + __func__, cmd, arg); + ret = -ENOIOCTLCMD; + } + return ret; +} +#endif + +long nfc_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct nfc_dev *nfc_dev = pfile->private_data; + + if (!nfc_dev) + return -ENODEV; + + pr_debug("%s cmd = %x arg = %zx\n", __func__, cmd, arg); + + switch (cmd) { + case NFC_SET_PWR: + ret = nfc_ioctl_power_states(nfc_dev, arg); + break; + case ESE_SET_PWR: + ret = nfc_ese_pwr(nfc_dev, arg); + break; + case ESE_GET_PWR: + ret = nfc_ese_pwr(nfc_dev, ESE_POWER_STATE); + break; + case NFCC_GET_INFO: + ret = nfc_ioctl_nfcc_info(pfile, arg); + break; + case NFC_GET_PLATFORM_TYPE: + ret = nfc_dev->interface; + break; + case ESE_COLD_RESET: + pr_debug("nfc ese cold reset ioctl\n"); + ret = ese_cold_reset_ioctl(nfc_dev, arg); + break; + case NFC_GET_IRQ_STATE: + ret = gpio_get_value(nfc_dev->configs.gpio.irq); + break; + default: + pr_err("%s Unsupported ioctl cmd 0x%x, arg %lu\n", + __func__, cmd, arg); + ret = -ENOIOCTLCMD; + } + return ret; +} +int nfc_dev_open(struct inode *inode, struct file *filp) +{ + struct nfc_dev *nfc_dev = container_of(inode->i_cdev, + struct nfc_dev, c_dev); + + if (!nfc_dev) + return -ENODEV; + + pr_debug("%s: %d, %d\n", __func__, imajor(inode), iminor(inode)); + + /* Set flag to block freezer fake signal if not set already. + * Without this Signal being set, Driver is trying to do a read + * which is causing the delay in moving to Hibernate Mode. + */ + if (!(current->flags & PF_NOFREEZE)) { + current->flags |= PF_NOFREEZE; + pr_debug("%s: current->flags 0x%x.\n", __func__, current->flags); + } + + mutex_lock(&nfc_dev->dev_ref_mutex); + + filp->private_data = nfc_dev; + + if (nfc_dev->dev_ref_count == 0) { + set_valid_gpio(nfc_dev->configs.gpio.dwl_req, 0); + nfc_dev->nfc_enable_intr(nfc_dev); + } + nfc_dev->dev_ref_count = nfc_dev->dev_ref_count + 1; + + mutex_unlock(&nfc_dev->dev_ref_mutex); + + return 0; +} + +int nfc_dev_close(struct inode *inode, struct file *filp) +{ + struct nfc_dev *nfc_dev = container_of(inode->i_cdev, + struct nfc_dev, c_dev); + + if (!nfc_dev) + return -ENODEV; + + pr_debug("%s: %d, %d\n", __func__, imajor(inode), iminor(inode)); + + /* unset the flag to restore to previous state */ + if (current->flags & PF_NOFREEZE) { + current->flags &= ~PF_NOFREEZE; + pr_debug("%s: current->flags 0x%x.\n", __func__, current->flags); + } + + mutex_lock(&nfc_dev->dev_ref_mutex); + + if (nfc_dev->dev_ref_count == 1) { + nfc_dev->nfc_disable_intr(nfc_dev); + set_valid_gpio(nfc_dev->configs.gpio.dwl_req, 0); + } + + if (nfc_dev->dev_ref_count > 0) + nfc_dev->dev_ref_count = nfc_dev->dev_ref_count - 1; + + filp->private_data = NULL; + + mutex_unlock(&nfc_dev->dev_ref_mutex); + + return 0; +} + +int is_nfc_data_available_for_read(struct nfc_dev *nfc_dev) +{ + int ret; + + nfc_dev->nfc_enable_intr(nfc_dev); + + ret = wait_event_interruptible_timeout(nfc_dev->read_wq, + !nfc_dev->i2c_dev.irq_enabled, + msecs_to_jiffies(MAX_IRQ_WAIT_TIME)); + return ret; +} + +/** + * get_nfcc_chip_type_dl() - get chip type in fw download command; + * @nfc_dev: nfc device data structure + * + * Perform get version command and determine chip + * type from response. + * + * @Return: enum chip_types value + * + */ +static enum chip_types get_nfcc_chip_type_dl(struct nfc_dev *nfc_dev) +{ + int ret = 0; + uint8_t *cmd = nfc_dev->write_kbuf; + uint8_t *rsp = nfc_dev->read_kbuf; + enum chip_types chip_type = CHIP_UNKNOWN; + + *cmd++ = DL_CMD; + *cmd++ = DL_GET_VERSION_CMD_PAYLOAD_LEN; + *cmd++ = DL_GET_VERSION_CMD_ID; + *cmd++ = DL_PAYLOAD_BYTE_ZERO; + *cmd++ = DL_PAYLOAD_BYTE_ZERO; + *cmd++ = DL_PAYLOAD_BYTE_ZERO; + *cmd++ = DL_GET_VERSION_CMD_CRC_1; + *cmd++ = DL_GET_VERSION_CMD_CRC_2; + + pr_debug("%s:Sending GET_VERSION cmd of size = %d\n", __func__, DL_GET_VERSION_CMD_LEN); + ret = nfc_dev->nfc_write(nfc_dev, nfc_dev->write_kbuf, DL_GET_VERSION_CMD_LEN, + MAX_RETRY_COUNT); + if (ret <= 0) { + pr_err("%s: - nfc get version cmd error ret %d\n", __func__, ret); + goto err; + } + memset(rsp, 0x00, DL_GET_VERSION_RSP_LEN_2); + pr_debug("%s:Reading response of GET_VERSION cmd\n", __func__); + ret = nfc_dev->nfc_read(nfc_dev, rsp, DL_GET_VERSION_RSP_LEN_2, NCI_CMD_RSP_TIMEOUT); + if (ret <= 0) { + pr_err("%s: - nfc get version rsp error ret %d\n", __func__, ret); + goto err; + } + if (rsp[0] == FW_MSG_CMD_RSP && ret >= DL_GET_VERSION_RSP_LEN_2) { + + nfc_dev->fw_major_version = rsp[FW_MAJOR_VER_OFFSET]; + + if (rsp[FW_ROM_CODE_VER_OFFSET] == SN1XX_ROM_VER && + rsp[FW_MAJOR_VER_OFFSET] == SN1xx_MAJOR_VER) + chip_type = CHIP_SN1XX; + else if (rsp[FW_ROM_CODE_VER_OFFSET] == SN220_ROM_VER && + rsp[FW_MAJOR_VER_OFFSET] == SN220_MAJOR_VER) + chip_type = CHIP_SN220; + else if (rsp[FW_ROM_CODE_VER_OFFSET] == NXP557_ROM_VER && + rsp[FW_MAJOR_VER_OFFSET] == NXP557_MAJOR_VER) + chip_type = CHIP_NXP557; + + pr_debug("%s:NFC Chip Type 0x%02x Rom Version 0x%02x FW Minor 0x%02x Major 0x%02x\n", + __func__, rsp[GET_VERSION_RSP_CHIP_TYPE_OFFSET], + rsp[FW_ROM_CODE_VER_OFFSET], + rsp[GET_VERSION_RSP_MINOR_VERSION_OFFSET], + rsp[FW_MAJOR_VER_OFFSET]); + + nfc_dev->nqx_info.info.chip_type = rsp[GET_VERSION_RSP_CHIP_TYPE_OFFSET]; + nfc_dev->nqx_info.info.rom_version = rsp[FW_ROM_CODE_VER_OFFSET]; + nfc_dev->nqx_info.info.fw_minor = rsp[GET_VERSION_RSP_MINOR_VERSION_OFFSET]; + nfc_dev->nqx_info.info.fw_major = rsp[FW_MAJOR_VER_OFFSET]; + } +err: + return chip_type; +} + +/** + * get_nfcc_session_state_dl() - gets the session state + * @nfc_dev: nfc device data structure + * + * Performs get session command and determine + * the nfcc state based on session status. + * + * @Return nfcc state based on session status. + * NFC_STATE_FW_TEARED if sessionis not closed + * NFC_STATE_FW_DWL if session closed + * NFC_STATE_UNKNOWN in error cases. + */ +enum nfc_state_flags get_nfcc_session_state_dl(struct nfc_dev *nfc_dev) +{ + int ret = 0; + uint8_t *cmd = nfc_dev->write_kbuf; + uint8_t *rsp = nfc_dev->read_kbuf; + enum nfc_state_flags nfc_state = NFC_STATE_UNKNOWN; + + *cmd++ = DL_CMD; + *cmd++ = DL_GET_SESSION_STATE_CMD_PAYLOAD_LEN; + *cmd++ = DL_GET_SESSION_CMD_ID; + *cmd++ = DL_PAYLOAD_BYTE_ZERO; + *cmd++ = DL_PAYLOAD_BYTE_ZERO; + *cmd++ = DL_PAYLOAD_BYTE_ZERO; + *cmd++ = DL_GET_SESSION_CMD_CRC_1; + *cmd++ = DL_GET_SESSION_CMD_CRC_2; + + pr_debug("%s:Sending GET_SESSION_STATE cmd of size = %d\n", __func__, + DL_GET_SESSION_STATE_CMD_LEN); + ret = nfc_dev->nfc_write(nfc_dev, nfc_dev->write_kbuf, DL_GET_SESSION_STATE_CMD_LEN, + MAX_RETRY_COUNT); + if (ret <= 0) { + pr_err("%s: - nfc get session cmd error ret %d\n", __func__, ret); + goto err; + } + memset(rsp, 0x00, DL_GET_SESSION_STATE_RSP_LEN); + pr_debug("%s:Reading response of GET_SESSION_STATE cmd\n", __func__); + ret = nfc_dev->nfc_read(nfc_dev, rsp, DL_GET_SESSION_STATE_RSP_LEN, NCI_CMD_RSP_TIMEOUT); + if (ret <= 0) { + pr_err("%s: - nfc get session rsp error ret %d\n", __func__, ret); + goto err; + } + if (rsp[0] != FW_MSG_CMD_RSP) { + pr_err("%s: - nfc invalid get session state rsp\n", __func__); + goto err; + } + pr_debug("Response bytes are %02x%02x%02x%02x%02x%02x%02x%02x\n", + rsp[0], rsp[1], rsp[2], rsp[3], rsp[4], rsp[5], rsp[6], rsp[7]); + /*verify fw in non-teared state */ + if (rsp[GET_SESSION_STS_OFF] != NFCC_SESSION_STS_CLOSED) { + pr_err("%s NFCC booted in FW teared state\n", __func__); + nfc_state = NFC_STATE_FW_TEARED; + } else { + pr_info("%s NFCC booted in FW DN mode\n", __func__); + nfc_state = NFC_STATE_FW_DWL; + } +err: + return nfc_state; +} + +/** + * get_nfcc_chip_type() - get nfcc chip type in nci mode. + * @nfc_dev: nfc device data structure. + * + * Function to perform nci core reset and extract + * chip type from the response. + * + * @Return: enum chip_types value + * + */ +static enum chip_types get_nfcc_chip_type(struct nfc_dev *nfc_dev) +{ + int ret = 0; + uint8_t major_version = 0; + uint8_t rom_version = 0; + uint8_t *cmd = nfc_dev->write_kbuf; + uint8_t *rsp = nfc_dev->read_kbuf; + enum chip_types chip_type = CHIP_UNKNOWN; + + *cmd++ = NCI_MSG_CMD; + *cmd++ = NCI_CORE_RESET_CMD_OID; + *cmd++ = NCI_CORE_RESET_CMD_PAYLOAD_LEN; + *cmd++ = NCI_CORE_RESET_KEEP_CONFIG; + + pr_debug("%s:Sending NCI Core Reset cmd of size = %d\n", __func__, NCI_RESET_CMD_LEN); + ret = nfc_dev->nfc_write(nfc_dev, nfc_dev->write_kbuf, NCI_RESET_CMD_LEN, NO_RETRY); + if (ret <= 0) { + pr_err("%s: - nfc nci core reset cmd error ret %d\n", __func__, ret); + goto err; + } + + /* to flush out debug NTF this delay is required */ + usleep_range(NCI_RESET_RESP_READ_DELAY, NCI_RESET_RESP_READ_DELAY + 100); + nfc_dev->nfc_enable_intr(nfc_dev); + + memset(rsp, 0x00, NCI_RESET_RSP_LEN); + pr_debug("%s:Reading NCI Core Reset rsp\n", __func__); + ret = nfc_dev->nfc_read(nfc_dev, rsp, NCI_RESET_RSP_LEN, NCI_CMD_RSP_TIMEOUT); + if (ret <= 0) { + pr_err("%s: - nfc nci core reset rsp error ret %d\n", __func__, ret); + goto err_disable_intr; + } + + pr_debug(" %s: nci core reset response 0x%02x%02x%02x%02x\n", + __func__, rsp[0], rsp[1], rsp[2], rsp[3]); + if (rsp[0] != NCI_MSG_RSP) { + /* reset response failed response*/ + pr_err("%s invalid nci core reset response\n", __func__); + goto err_disable_intr; + } + + memset(rsp, 0x00, NCI_RESET_NTF_LEN); + /* read nci rest response ntf */ + ret = nfc_dev->nfc_read(nfc_dev, rsp, NCI_RESET_NTF_LEN, NCI_CMD_RSP_TIMEOUT); + if (ret <= 0) { + pr_err("%s - nfc nci rest rsp ntf error status %d\n", __func__, ret); + goto err_disable_intr; + } + + if (rsp[0] == NCI_MSG_NTF) { + /* read version info from NCI Reset Notification */ + rom_version = rsp[NCI_HDR_LEN + rsp[NCI_PAYLOAD_LEN_IDX] - 3]; + major_version = rsp[NCI_HDR_LEN + rsp[NCI_PAYLOAD_LEN_IDX] - 2]; + /* determine chip type based on version info */ + if (rom_version == SN1XX_ROM_VER && major_version == SN1xx_MAJOR_VER) + chip_type = CHIP_SN1XX; + else if (rom_version == SN220_ROM_VER && major_version == SN220_MAJOR_VER) + chip_type = CHIP_SN220; + pr_debug(" %s:NCI Core Reset ntf 0x%02x%02x%02x%02x\n", + __func__, rsp[0], rsp[1], rsp[2], rsp[3]); + + nfc_dev->nqx_info.info.chip_type = rsp[NCI_HDR_LEN + rsp[NCI_PAYLOAD_LEN_IDX] - + NFC_CHIP_TYPE_OFF]; + nfc_dev->nqx_info.info.rom_version = rom_version; + nfc_dev->nqx_info.info.fw_major = major_version; + nfc_dev->nqx_info.info.fw_minor = rsp[NCI_HDR_LEN + rsp[NCI_PAYLOAD_LEN_IDX] - + NFC_FW_MINOR_OFF]; + } +err_disable_intr: + nfc_dev->nfc_disable_intr(nfc_dev); +err: + return chip_type; +} + +/** + * validate_download_gpio() - validate download gpio. + * @nfc_dev: nfc_dev device data structure. + * @chip_type: chip type of the platform. + * + * Validates dwnld gpio should configured for supported and + * should not be configured for unsupported platform. + * + * @Return: true if gpio validation successful ortherwise + * false if validation fails. + */ +static bool validate_download_gpio(struct nfc_dev *nfc_dev, enum chip_types chip_type) +{ + bool status = false; + struct platform_gpio *nfc_gpio; + + if (nfc_dev == NULL) { + pr_err("%s nfc devices structure is null\n", __func__); + return status; + } + nfc_gpio = &nfc_dev->configs.gpio; + if (chip_type == CHIP_SN1XX) { + /* gpio should be configured for SN1xx */ + status = gpio_is_valid(nfc_gpio->dwl_req); + } else if (chip_type == CHIP_SN220) { + /* gpio should not be configured for SN220 */ + set_valid_gpio(nfc_gpio->dwl_req, 0); + /* do not need free_gpio wile gpio is invalid */ + if (gpio_is_valid(nfc_gpio->dwl_req)) { + gpio_free(nfc_gpio->dwl_req); + } + nfc_gpio->dwl_req = -EINVAL; + status = true; + } else if (chip_type == CHIP_NXP557) { + status = gpio_is_valid(nfc_gpio->dwl_req); + } + return status; +} + +/* Check for availability of NFC controller hardware */ +int nfcc_hw_check(struct nfc_dev *nfc_dev) +{ + int ret = 0; + enum nfc_state_flags nfc_state = NFC_STATE_UNKNOWN; + enum chip_types chip_type = CHIP_UNKNOWN; + struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio; + + /*get fw version in nci mode*/ + gpio_set_ven(nfc_dev, 1); + gpio_set_ven(nfc_dev, 0); + gpio_set_ven(nfc_dev, 1); + chip_type = get_nfcc_chip_type(nfc_dev); + + /*get fw version in fw dwl mode*/ + if (chip_type == CHIP_UNKNOWN) { + nfc_dev->nfc_enable_intr(nfc_dev); + /*Chip is unknown, initially assume with fw dwl pin enabled*/ + set_valid_gpio(nfc_gpio->dwl_req, 1); + gpio_set_ven(nfc_dev, 0); + gpio_set_ven(nfc_dev, 1); + chip_type = get_nfcc_chip_type_dl(nfc_dev); + /*get the state of nfcc normal/teared in fw dwl mode*/ + } else { + nfc_state = NFC_STATE_NCI; + } + + /*validate gpio config required as per the chip*/ + if (!validate_download_gpio(nfc_dev, chip_type)) { + pr_info("%s gpio validation fail\n", __func__); + ret = -ENXIO; + goto err; + } + + /*check whether the NFCC is in FW DN or Teared state*/ + if (nfc_state != NFC_STATE_NCI) + nfc_state = get_nfcc_session_state_dl(nfc_dev); + + /*nfcc state specific operations */ + switch (nfc_state) { + case NFC_STATE_FW_TEARED: + pr_warn("%s: - NFCC FW Teared State\n", __func__); + case NFC_STATE_FW_DWL: + case NFC_STATE_NCI: + break; + case NFC_STATE_UNKNOWN: + default: + ret = -ENXIO; + pr_err("%s: - NFCC HW not available\n", __func__); + goto err; + } + nfc_dev->nfc_state = nfc_state; +err: + nfc_dev->nfc_disable_intr(nfc_dev); + set_valid_gpio(nfc_gpio->dwl_req, 0); + gpio_set_ven(nfc_dev, 0); + gpio_set_ven(nfc_dev, 1); + nfc_dev->nfc_ven_enabled = true; + return ret; +} + +int validate_nfc_state_nci(struct nfc_dev *nfc_dev) +{ + struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio; + + if (!gpio_get_value(nfc_gpio->ven)) { + pr_err("VEN LOW - NFCC powered off\n"); + return -ENODEV; + } + if (get_valid_gpio(nfc_gpio->dwl_req) == 1) { + pr_err("FW download in-progress\n"); + return -EBUSY; + } + if (nfc_dev->nfc_state != NFC_STATE_NCI) { + pr_err("FW download state\n"); + return -EBUSY; + } + return 0; +} diff --git a/drivers/nfc/sn2xx/nfc_common.h b/drivers/nfc/sn2xx/nfc_common.h new file mode 100644 index 000000000000..b35ecf2f66a1 --- /dev/null +++ b/drivers/nfc/sn2xx/nfc_common.h @@ -0,0 +1,352 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2021 NXP + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ******************************************************************************/ + +#ifndef _NFC_COMMON_H_ +#define _NFC_COMMON_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "nfc_i2c_drv.h" +#include "ese_cold_reset.h" + +// Max device count for this driver +#define DEV_COUNT 1 + +// NFC device class +#define CLASS_NAME "qti-nfc" + +// NFC character device name, this will be in /dev/ +#define NFC_CHAR_DEV_NAME "nq-nci" + +// NCI packet details +#define NCI_MSG_CMD 0x20 +#define NCI_MSG_RSP 0x40 +#define NCI_MSG_NTF 0x60 +#define DL_CMD 0x00 +#define DL_PAYLOAD_BYTE_ZERO 0x00 +#define NCI_HDR_LEN 3 +#define NCI_PAYLOAD_IDX 3 +#define NCI_PAYLOAD_LEN_IDX 2 + +/*Time to wait for first NCI rest response*/ +#define NCI_RESET_RESP_READ_DELAY (10000) // 10ms +#define NCI_RESET_RESP_TIMEOUT (500) // 500ms + +// FW DNLD packet details +#define FW_MSG_CMD_RSP 0x00 +#define FW_HDR_LEN 2 +#define FW_PAYLOAD_LEN_IDX 1 +#define FW_CRC_LEN 2 + +#define NCI_RSP_PKT_TYPE (0x40) +#define FW_MIN_PAYLOAD_LEN 4 +#define MIN_NFC_DL_FRAME_SIZE 3 + +#define NCI_RESET_CMD_LEN (4) +#define NCI_RESET_RSP_LEN (4) +#define NCI_CORE_RESET_CMD_OID (0x0) +#define NCI_CORE_RESET_CMD_PAYLOAD_LEN (0x1) +#define NCI_CORE_RESET_KEEP_CONFIG (0x0) +#define NCI_RESET_NTF_LEN (13) +#define SN1XX_ROM_VER 0x01 +#define SN1xx_MAJOR_VER 0x10 +#define SN220_ROM_VER 0x01 +#define SN220_MAJOR_VER 0x01 +#define NXP557_ROM_VER 0x12 +#define NXP557_MAJOR_VER 0x00 +#define FW_ROM_CODE_VER_OFFSET 4 +#define FW_MAJOR_VER_OFFSET 7 +#define GET_VERSION_RSP_CHIP_TYPE_OFFSET 3 +#define GET_VERSION_RSP_MINOR_VERSION_OFFSET 6 +#define DL_GET_VERSION_CMD_LEN (8) +#define DL_GET_VERSION_RSP_LEN_1 (12) /* SN110 */ +#define DL_GET_VERSION_RSP_LEN_2 (20) /* SN220 */ +#define DL_GET_VERSION_CMD_PAYLOAD_LEN (4) +#define DL_GET_VERSION_CMD_ID (0xF1) +#define DL_GET_VERSION_CMD_CRC_1 (0x6E) +#define DL_GET_VERSION_CMD_CRC_2 (0xEF) + +#define DL_RESET_CMD_LEN (8) +#define DL_GET_SESSION_STATE_CMD_LEN (8) +#define DL_GET_SESSION_STATE_RSP_LEN (8) +#define DL_GET_SESSION_STATE_CMD_PAYLOAD_LEN (4) +#define DL_GET_SESSION_CMD_ID (0xF2) +#define DL_GET_SESSION_CMD_CRC_1 (0xF5) +#define DL_GET_SESSION_CMD_CRC_2 (0x33) +#define GET_SESSION_STS_OFF (3) +#define NFCC_SESSION_STS_CLOSED (0x0) + +/* Below offsets should be subtracted from NCI header length + payload length */ + +#define NFC_CHIP_TYPE_OFF (4) +#define NFC_FW_MINOR_OFF (1) + +#define GET_VERSION_CMD_LEN 8 +#define GET_SESSION_STATE_CMD_LEN 8 +#define MAX_NCI_PAYLOAD_LEN (255) +#define MAX_BUFFER_SIZE (NCI_HDR_LEN + MAX_NCI_PAYLOAD_LEN) +/* + * From MW 11.04 buffer size increased to support + * frame size of 554 in FW download mode + * Frame len(2) + Frame Header(6) + DATA(512) + HASH(32) + CRC(2) + RFU(4) + */ +#define MAX_DL_PAYLOAD_LEN (550) +#define MAX_DL_BUFFER_SIZE (FW_HDR_LEN + FW_CRC_LEN + MAX_DL_PAYLOAD_LEN) +// Maximum retry count for standby writes +#define MAX_RETRY_COUNT (3) + +// Retry count for normal write +#define NO_RETRY (1) +#define MAX_IRQ_WAIT_TIME (90) +#define WAKEUP_SRC_TIMEOUT (2000) + +/*command response timeout*/ +#define NCI_CMD_RSP_TIMEOUT (2000) //2s +/*Time to wait for NFCC to be ready again after any change in the GPIO*/ +#define NFC_GPIO_SET_WAIT_TIME_USEC (10000) +/*Time to wait after soft reset via any NCI/DL cmd*/ +#define NFC_SOFT_RESET_WAIT_TIME_USEC (5000) +/*Time to wait before retrying i2c writes*/ +#define WRITE_RETRY_WAIT_TIME_USEC (1000) +/*Time to wait before retrying read for some specific usecases*/ +#define READ_RETRY_WAIT_TIME_USEC (3500) +#define NFC_MAGIC 0xE9 + +// Ioctls +// The type should be aligned with MW HAL definitions + +#define NFC_SET_PWR _IOW(NFC_MAGIC, 0x01, unsigned int) +#define ESE_SET_PWR _IOW(NFC_MAGIC, 0x02, unsigned int) +#define ESE_GET_PWR _IOR(NFC_MAGIC, 0x03, unsigned int) +#define NFC_GET_PLATFORM_TYPE _IO(NFC_MAGIC, 0x04) + +/* NFC HAL can call this ioctl to get the current IRQ state */ +#define NFC_GET_IRQ_STATE _IO(NFC_MAGIC, 0x06) + +#define DTS_IRQ_GPIO_STR "qcom,sn-irq" +#define DTS_VEN_GPIO_STR "qcom,sn-ven" +#define DTS_FWDN_GPIO_STR "qcom,sn-firm" +#define DTS_CLKREQ_GPIO_STR "qcom,sn-clkreq" +#define DTS_CLKSRC_GPIO_STR "qcom,clk-src" +#define NFC_LDO_SUPPLY_DT_NAME "qcom,sn-vdd-1p8" +#define NFC_LDO_SUPPLY_NAME "qcom,sn-vdd-1p8-supply" +#define NFC_LDO_VOL_DT_NAME "qcom,sn-vdd-1p8-voltage" +#define NFC_LDO_CUR_DT_NAME "qcom,sn-vdd-1p8-current" + +//as per SN1x0 datasheet +#define NFC_VDDIO_MIN 1650000 //in uV +#define NFC_VDDIO_MAX 1950000 //in uV +#define NFC_CURRENT_MAX 157000 //in uA + + +#define NUM_OF_IPC_LOG_PAGES (2) +#define PKT_MAX_LEN (4) // no of max bytes to print for cmd/resp + +#define GET_IPCLOG_MAX_PKT_LEN(c) ((c > PKT_MAX_LEN) ? PKT_MAX_LEN : c) + +#define NFCLOG_IPC(nfc_dev, log_to_dmesg, x...) \ +do { \ + ipc_log_string(nfc_dev->ipcl, x); \ + if (log_to_dmesg) { \ + if (nfc_dev->nfc_device) \ + dev_err((nfc_dev->nfc_device), x); \ + else \ + pr_err(x); \ + } \ +} while (0) + +enum ese_ioctl_request { + /* eSE POWER ON */ + ESE_POWER_ON = 0, + /* eSE POWER OFF */ + ESE_POWER_OFF, + /* eSE POWER STATE */ + ESE_POWER_STATE +}; + +enum nfcc_ioctl_request { + /* NFC disable request with VEN LOW */ + NFC_POWER_OFF = 0, + /* NFC enable request with VEN Toggle */ + NFC_POWER_ON, + /* firmware download request with VEN Toggle */ + NFC_FW_DWL_VEN_TOGGLE, + /* ISO reset request */ + NFC_ISO_RESET, + /* request for firmware download gpio HIGH */ + NFC_FW_DWL_HIGH, + /* VEN hard reset request */ + NFC_VEN_FORCED_HARD_RESET, + /* request for firmware download gpio LOW */ + NFC_FW_DWL_LOW, + /* NFC enable without VEN gpio modification */ + NFC_ENABLE, + /* NFC disable without VEN gpio modification */ + NFC_DISABLE, +}; + +/*nfc platform interface type*/ +enum interface_flags { + /*I2C physical IF for NFCC */ + PLATFORM_IF_I2C = 0, +}; + +/*nfc state flags*/ +enum nfc_state_flags { + /*nfc in unknown state */ + NFC_STATE_UNKNOWN = 0, + /*nfc in download mode */ + NFC_STATE_FW_DWL = 0x1, + /*nfc booted in NCI mode */ + NFC_STATE_NCI = 0x2, + /*nfc booted in Fw teared mode */ + NFC_STATE_FW_TEARED = 0x4, +}; +/* + * Power state for IBI handing, mainly needed to defer the IBI handling + * for the IBI received in suspend state to do it later in resume call + */ +enum pm_state_flags { + PM_STATE_NORMAL = 0, + PM_STATE_SUSPEND, + PM_STATE_IBI_BEFORE_RESUME, +}; + +/* Enum for GPIO values*/ +enum gpio_values { + GPIO_INPUT = 0x0, + GPIO_OUTPUT = 0x1, + GPIO_HIGH = 0x2, + GPIO_OUTPUT_HIGH = 0x3, + GPIO_IRQ = 0x4, +}; + +// NFC GPIO variables +struct platform_gpio { + unsigned int irq; + unsigned int ven; + unsigned int clkreq; + unsigned int dwl_req; +}; + +// NFC LDO entries from DT +struct platform_ldo { + int vdd_levels[2]; + int max_current; +}; + +// NFC Struct to get all the required configs from DTS +struct platform_configs { + struct platform_gpio gpio; + struct platform_ldo ldo; +}; + +enum chip_types { + CHIP_SN1XX = 0x01, + CHIP_SN220 = 0x02, + CHIP_NXP557 = 0x03, + CHIP_UNKNOWN = 0xFF, +}; + +/* Device specific structure */ +struct nfc_dev { + wait_queue_head_t read_wq; + struct mutex read_mutex; + struct mutex write_mutex; + uint8_t *read_kbuf; + uint8_t *write_kbuf; + struct mutex dev_ref_mutex; + unsigned int dev_ref_count; + struct class *nfc_class; + struct device *nfc_device; + struct cdev c_dev; + dev_t devno; + /* Interface flag */ + uint8_t interface; + /* nfc state flags */ + uint8_t nfc_state; + /* NFC VEN pin state */ + bool nfc_ven_enabled; + /* current firmware major version */ + uint8_t fw_major_version; + bool is_vreg_enabled; + bool is_ese_session_active; + struct i2c_dev i2c_dev; + struct platform_configs configs; + struct cold_reset cold_reset; + struct regulator *reg; + + /* read buffer*/ + size_t kbuflen; + u8 *kbuf; + + union nqx_uinfo nqx_info; + + void *ipcl; + + int (*nfc_read)(struct nfc_dev *dev, char *buf, size_t count, int timeout); + int (*nfc_write)(struct nfc_dev *dev, const char *buf, const size_t count, + int max_retry_cnt); + int (*nfc_enable_intr)(struct nfc_dev *dev); + int (*nfc_disable_intr)(struct nfc_dev *dev); +}; + +int nfc_dev_open(struct inode *inode, struct file *filp); +int nfc_dev_close(struct inode *inode, struct file *filp); +long nfc_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg); +#ifdef CONFIG_COMPAT +long nfc_compat_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg); +#endif +int nfc_parse_dt(struct device *dev, struct platform_configs *nfc_configs, + uint8_t interface); +int nfc_misc_register(struct nfc_dev *nfc_dev, + const struct file_operations *nfc_fops, int count, char *devname, + char *classname); +void nfc_misc_unregister(struct nfc_dev *nfc_dev, int count); +int configure_gpio(unsigned int gpio, int flag); +void gpio_set_ven(struct nfc_dev *nfc_dev, int value); +int nfcc_hw_check(struct nfc_dev *nfc_dev); +int nfc_ldo_config(struct device *dev, struct nfc_dev *nfc_dev); +int nfc_ldo_vote(struct nfc_dev *nfc_dev); +int nfc_ldo_unvote(struct nfc_dev *nfc_dev); +int is_nfc_data_available_for_read(struct nfc_dev *nfc_dev); +int validate_nfc_state_nci(struct nfc_dev *nfc_dev); +void set_nfcc_state_from_rsp(struct nfc_dev *dev, const char *buf, + const int count); +void enable_dwnld_mode(struct nfc_dev *nfc_dev, bool value); +#endif //_NFC_COMMON_H_ diff --git a/drivers/nfc/sn2xx/nfc_i2c_drv.c b/drivers/nfc/sn2xx/nfc_i2c_drv.c new file mode 100644 index 000000000000..8c29310a058c --- /dev/null +++ b/drivers/nfc/sn2xx/nfc_i2c_drv.c @@ -0,0 +1,582 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2021 NXP + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ******************************************************************************/ +/* + * Copyright (C) 2010 Trusted Logic S.A. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "nfc_common.h" + +/** + * i2c_disable_irq() + * + * Check if interrupt is disabled or not + * and disable interrupt + * + * Return: int + */ +int i2c_disable_irq(struct nfc_dev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->i2c_dev.irq_enabled_lock, flags); + if (dev->i2c_dev.irq_enabled) { + disable_irq_nosync(dev->i2c_dev.client->irq); + dev->i2c_dev.irq_enabled = false; + } + spin_unlock_irqrestore(&dev->i2c_dev.irq_enabled_lock, flags); + + return 0; +} + +/** + * i2c_enable_irq() + * + * Check if interrupt is enabled or not + * and enable interrupt + * + * Return: int + */ +int i2c_enable_irq(struct nfc_dev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->i2c_dev.irq_enabled_lock, flags); + if (!dev->i2c_dev.irq_enabled) { + dev->i2c_dev.irq_enabled = true; + enable_irq(dev->i2c_dev.client->irq); + } + spin_unlock_irqrestore(&dev->i2c_dev.irq_enabled_lock, flags); + + return 0; +} + +static irqreturn_t i2c_irq_handler(int irq, void *dev_id) +{ + struct nfc_dev *nfc_dev = dev_id; + struct i2c_dev *i2c_dev = &nfc_dev->i2c_dev; + + if (device_may_wakeup(&i2c_dev->client->dev)) + pm_wakeup_event(&i2c_dev->client->dev, WAKEUP_SRC_TIMEOUT); + + i2c_disable_irq(nfc_dev); + wake_up(&nfc_dev->read_wq); + + return IRQ_HANDLED; +} + +int i2c_read(struct nfc_dev *nfc_dev, char *buf, size_t count, int timeout) +{ + int ret; + struct i2c_dev *i2c_dev = &nfc_dev->i2c_dev; + struct platform_gpio *nfc_gpio = &nfc_dev->configs.gpio; + uint16_t i = 0; + uint16_t disp_len = GET_IPCLOG_MAX_PKT_LEN(count); + + pr_debug("%s : reading %zu bytes.\n", __func__, count); + + if (timeout > NCI_CMD_RSP_TIMEOUT) + timeout = NCI_CMD_RSP_TIMEOUT; + + if (count > MAX_BUFFER_SIZE) + count = MAX_BUFFER_SIZE; + + if (!gpio_get_value(nfc_gpio->irq)) { + while (1) { + ret = 0; + if (!i2c_dev->irq_enabled) { + i2c_dev->irq_enabled = true; + enable_irq(i2c_dev->client->irq); + } + if (!gpio_get_value(nfc_gpio->irq)) { + if (timeout) { + ret = wait_event_interruptible_timeout(nfc_dev->read_wq, + !i2c_dev->irq_enabled, msecs_to_jiffies(timeout)); + + if (ret <= 0) { + pr_err("%s timeout/error in read\n", __func__); + goto err; + } + } else { + ret = wait_event_interruptible(nfc_dev->read_wq, + !i2c_dev->irq_enabled); + if (ret) { + pr_err("%s error wakeup of read wq\n", __func__); + ret = -EINTR; + goto err; + } + } + } + i2c_disable_irq(nfc_dev); + + if (gpio_get_value(nfc_gpio->irq)) + break; + if (!gpio_get_value(nfc_gpio->ven)) { + pr_info("%s: releasing read\n", __func__); + ret = -EIO; + goto err; + } + pr_warn("%s: spurious interrupt detected\n", __func__); + } + } + + memset(buf, 0x00, count); + /* Read data */ + ret = i2c_master_recv(nfc_dev->i2c_dev.client, buf, count); + NFCLOG_IPC(nfc_dev, false, "%s of %zu bytes, ret %d", __func__, count, + ret); + if (ret <= 0) { + pr_err("%s: returned %d\n", __func__, ret); + goto err; + } + + for (i = 0; i < disp_len; i++) + NFCLOG_IPC(nfc_dev, false, " %02x", buf[i]); + + /* check if it's response of cold reset command + * NFC HAL process shouldn't receive this data as + * command was esepowermanager + */ + if (nfc_dev->cold_reset.rsp_pending && nfc_dev->cold_reset.cmd_buf + && (buf[0] == PROP_NCI_RSP_GID) + && (buf[1] == nfc_dev->cold_reset.cmd_buf[1])) { + read_cold_reset_rsp(nfc_dev, buf); + nfc_dev->cold_reset.rsp_pending = false; + wake_up_interruptible(&nfc_dev->cold_reset.read_wq); + /* + * NFC process doesn't know about cold reset command + * being sent as it was initiated by eSE process + * we shouldn't return any data to NFC process + */ + return 0; + } + +err: + return ret; +} + +int i2c_write(struct nfc_dev *nfc_dev, const char *buf, size_t count, + int max_retry_cnt) +{ + int ret = -EINVAL; + int retry_cnt; + uint16_t i = 0; + uint16_t disp_len = GET_IPCLOG_MAX_PKT_LEN(count); + + if (count > MAX_DL_BUFFER_SIZE) + count = MAX_DL_BUFFER_SIZE; + + pr_debug("%s : writing %zu bytes.\n", __func__, count); + + NFCLOG_IPC(nfc_dev, false, "%s sending %zu B", __func__, count); + + for (i = 0; i < disp_len; i++) + NFCLOG_IPC(nfc_dev, false, " %02x", buf[i]); + + for (retry_cnt = 1; retry_cnt <= max_retry_cnt; retry_cnt++) { + + ret = i2c_master_send(nfc_dev->i2c_dev.client, buf, count); + NFCLOG_IPC(nfc_dev, false, "%s ret %d", __func__, ret); + if (ret <= 0) { + pr_warn("%s: write failed ret %d, Maybe in Standby Mode - Retry(%d)\n", + __func__, ret, retry_cnt); + usleep_range(WRITE_RETRY_WAIT_TIME_USEC, + WRITE_RETRY_WAIT_TIME_USEC + 100); + } else if (ret != count) { + pr_err("%s: failed to write %d\n", __func__, ret); + ret = -EIO; + } else if (ret == count) + break; + } + return ret; +} + +ssize_t nfc_i2c_dev_read(struct file *filp, char __user *buf, + size_t count, loff_t *offset) +{ + int ret = 0; + struct nfc_dev *nfc_dev = (struct nfc_dev *)filp->private_data; + + mutex_lock(&nfc_dev->read_mutex); + if (filp->f_flags & O_NONBLOCK) { + ret = i2c_master_recv(nfc_dev->i2c_dev.client, nfc_dev->read_kbuf, count); + pr_debug("%s: NONBLOCK read ret = %d\n", __func__, ret); + } else { + ret = i2c_read(nfc_dev, nfc_dev->read_kbuf, count, 0); + } + if (ret > 0) { + if (copy_to_user(buf, nfc_dev->read_kbuf, ret)) { + pr_warn("%s : failed to copy to user space\n", __func__); + ret = -EFAULT; + } + } + mutex_unlock(&nfc_dev->read_mutex); + return ret; +} + +ssize_t nfc_i2c_dev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offset) +{ + int ret; + struct nfc_dev *nfc_dev = (struct nfc_dev *)filp->private_data; + + if (count > MAX_DL_BUFFER_SIZE) + count = MAX_DL_BUFFER_SIZE; + + if (!nfc_dev) + return -ENODEV; + + mutex_lock(&nfc_dev->write_mutex); + if (copy_from_user(nfc_dev->write_kbuf, buf, count)) { + pr_err("%s : failed to copy from user space\n", __func__); + mutex_unlock(&nfc_dev->write_mutex); + return -EFAULT; + } + ret = i2c_write(nfc_dev, nfc_dev->write_kbuf, count, NO_RETRY); + mutex_unlock(&nfc_dev->write_mutex); + return ret; +} + +static const struct file_operations nfc_i2c_dev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = nfc_i2c_dev_read, + .write = nfc_i2c_dev_write, + .open = nfc_dev_open, + .release = nfc_dev_close, + .unlocked_ioctl = nfc_dev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = nfc_compat_dev_ioctl, +#endif +}; + +int nfc_i2c_dev_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret = 0; + struct nfc_dev *nfc_dev = NULL; + struct i2c_dev *i2c_dev = NULL; + struct platform_configs nfc_configs; + struct platform_gpio *nfc_gpio = &nfc_configs.gpio; + + pr_debug("%s: enter\n", __func__); + + //retrieve details of gpios from dt + + ret = nfc_parse_dt(&client->dev, &nfc_configs, PLATFORM_IF_I2C); + if (ret) { + pr_err("%s : failed to parse dt\n", __func__); + goto err; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("%s : need I2C_FUNC_I2C\n", __func__); + ret = -ENODEV; + goto err; + } + nfc_dev = kzalloc(sizeof(struct nfc_dev), GFP_KERNEL); + if (nfc_dev == NULL) { + ret = -ENOMEM; + goto err; + } + nfc_dev->read_kbuf = kzalloc(MAX_BUFFER_SIZE, GFP_DMA | GFP_KERNEL); + if (!nfc_dev->read_kbuf) { + ret = -ENOMEM; + goto err_free_nfc_dev; + } + nfc_dev->write_kbuf = kzalloc(MAX_DL_BUFFER_SIZE, GFP_DMA | GFP_KERNEL); + if (!nfc_dev->write_kbuf) { + ret = -ENOMEM; + goto err_free_read_kbuf; + } + nfc_dev->interface = PLATFORM_IF_I2C; + nfc_dev->nfc_state = NFC_STATE_NCI; + nfc_dev->i2c_dev.client = client; + i2c_dev = &nfc_dev->i2c_dev; + nfc_dev->nfc_read = i2c_read; + nfc_dev->nfc_write = i2c_write; + nfc_dev->nfc_enable_intr = i2c_enable_irq; + nfc_dev->nfc_disable_intr = i2c_disable_irq; + nfc_dev->fw_major_version = 0; + ret = configure_gpio(nfc_gpio->ven, GPIO_OUTPUT); + if (ret) { + pr_err("%s: unable to request nfc reset gpio [%d]\n", + __func__, nfc_gpio->ven); + goto err_free_write_kbuf; + } + ret = configure_gpio(nfc_gpio->irq, GPIO_IRQ); + if (ret <= 0) { + pr_err("%s: unable to request nfc irq gpio [%d]\n", + __func__, nfc_gpio->irq); + goto err_free_ven; + } + client->irq = ret; + ret = configure_gpio(nfc_gpio->dwl_req, GPIO_OUTPUT); + if (ret) { + pr_err("%s: unable to request nfc firm downl gpio [%d]\n", + __func__, nfc_gpio->dwl_req); + //not returning failure here as dwl gpio is a optional gpio for sn220 + } + + ret = configure_gpio(nfc_gpio->clkreq, GPIO_INPUT); + if (ret) { + pr_err("%s: unable to request nfc clkreq gpio [%d]\n", + __func__, nfc_gpio->clkreq); + goto err_free_dwl_req; + } + + /*copy the retrieved gpio details from DT */ + memcpy(&nfc_dev->configs, &nfc_configs, sizeof(struct platform_configs)); + + /* init mutex and queues */ + init_waitqueue_head(&nfc_dev->read_wq); + mutex_init(&nfc_dev->read_mutex); + mutex_init(&nfc_dev->write_mutex); + mutex_init(&nfc_dev->dev_ref_mutex); + spin_lock_init(&i2c_dev->irq_enabled_lock); + ret = nfc_misc_register(nfc_dev, &nfc_i2c_dev_fops, DEV_COUNT, + NFC_CHAR_DEV_NAME, CLASS_NAME); + if (ret) { + pr_err("%s: nfc_misc_register failed\n", __func__); + goto err_mutex_destroy; + } + /* interrupt initializations */ + pr_info("%s : requesting IRQ %d\n", __func__, client->irq); + i2c_dev->irq_enabled = true; + ret = request_irq(client->irq, i2c_irq_handler, + IRQF_TRIGGER_HIGH, client->name, nfc_dev); + if (ret) { + pr_err("%s: request_irq failed\n", __func__); + goto err_nfc_misc_unregister; + } + i2c_disable_irq(nfc_dev); + i2c_set_clientdata(client, nfc_dev); + + ret = nfc_ldo_config(&client->dev, nfc_dev); + if (ret) { + pr_err("LDO config failed\n"); + goto err_ldo_config_failed; + } + + ret = nfcc_hw_check(nfc_dev); + if (ret || nfc_dev->nfc_state == NFC_STATE_UNKNOWN) { + pr_err("nfc hw check failed ret %d\n", ret); + } + + device_init_wakeup(&client->dev, true); + enable_irq_wake(client->irq); + i2c_dev->irq_wake_up = false; + nfc_dev->is_ese_session_active = false; + + pr_info("%s success\n", __func__); + return 0; + +err_ldo_config_failed: + if (nfc_dev->reg) { + nfc_ldo_unvote(nfc_dev); + regulator_put(nfc_dev->reg); + } + free_irq(client->irq, nfc_dev); +err_nfc_misc_unregister: + nfc_misc_unregister(nfc_dev, DEV_COUNT); +err_mutex_destroy: + mutex_destroy(&nfc_dev->dev_ref_mutex); + mutex_destroy(&nfc_dev->read_mutex); + mutex_destroy(&nfc_dev->write_mutex); + gpio_free(nfc_gpio->clkreq); +err_free_dwl_req: + if (gpio_is_valid(nfc_gpio->dwl_req)) + gpio_free(nfc_gpio->dwl_req); + gpio_free(nfc_gpio->irq); +err_free_ven: + gpio_free(nfc_gpio->ven); +err_free_write_kbuf: + kfree(nfc_dev->write_kbuf); +err_free_read_kbuf: + kfree(nfc_dev->read_kbuf); +err_free_nfc_dev: + kfree(nfc_dev); +err: + pr_err("%s: failed\n", __func__); + return ret; +} + +int nfc_i2c_dev_remove(struct i2c_client *client) +{ + int ret = 0; + struct nfc_dev *nfc_dev = NULL; + + pr_info("%s: remove device\n", __func__); + nfc_dev = i2c_get_clientdata(client); + if (!nfc_dev) { + pr_err("%s: device doesn't exist anymore\n", __func__); + ret = -ENODEV; + return ret; + } + + if (nfc_dev->dev_ref_count > 0) { + pr_err("%s: device already in use\n", __func__); + return -EBUSY; + } + + gpio_set_value(nfc_dev->configs.gpio.ven, 0); + // HW dependent delay before LDO goes into LPM mode + usleep_range(10000, 10100); + if (nfc_dev->reg) { + nfc_ldo_unvote(nfc_dev); + regulator_put(nfc_dev->reg); + } + + device_init_wakeup(&client->dev, false); + free_irq(client->irq, nfc_dev); + nfc_misc_unregister(nfc_dev, DEV_COUNT); + mutex_destroy(&nfc_dev->dev_ref_mutex); + mutex_destroy(&nfc_dev->read_mutex); + mutex_destroy(&nfc_dev->write_mutex); + + if (gpio_is_valid(nfc_dev->configs.gpio.clkreq)) + gpio_free(nfc_dev->configs.gpio.clkreq); + + if (gpio_is_valid(nfc_dev->configs.gpio.dwl_req)) + gpio_free(nfc_dev->configs.gpio.dwl_req); + + if (gpio_is_valid(nfc_dev->configs.gpio.irq)) + gpio_free(nfc_dev->configs.gpio.irq); + + if (gpio_is_valid(nfc_dev->configs.gpio.ven)) + gpio_free(nfc_dev->configs.gpio.ven); + + kfree(nfc_dev->read_kbuf); + kfree(nfc_dev->write_kbuf); + kfree(nfc_dev); + return ret; +} + +int nfc_i2c_dev_suspend(struct device *device) +{ + struct i2c_client *client = to_i2c_client(device); + struct nfc_dev *nfc_dev = i2c_get_clientdata(client); + struct i2c_dev *i2c_dev = NULL; + + if (!nfc_dev) { + pr_err("%s: device doesn't exist anymore\n", __func__); + return -ENODEV; + } + + i2c_dev = &nfc_dev->i2c_dev; + + NFCLOG_IPC(nfc_dev, false, "%s: irq_enabled = %d", __func__, + i2c_dev->irq_enabled); + + if (device_may_wakeup(&client->dev) && i2c_dev->irq_enabled) { + //if (!enable_irq_wake(client->irq)) + // i2c_dev->irq_wake_up = true; + i2c_dev->irq_wake_up = true; + } + return 0; +} + +int nfc_i2c_dev_resume(struct device *device) +{ + struct i2c_client *client = to_i2c_client(device); + struct nfc_dev *nfc_dev = i2c_get_clientdata(client); + struct i2c_dev *i2c_dev = NULL; + + if (!nfc_dev) { + pr_err("%s: device doesn't exist anymore\n", __func__); + return -ENODEV; + } + + i2c_dev = &nfc_dev->i2c_dev; + + NFCLOG_IPC(nfc_dev, false, "%s: irq_wake_up = %d", __func__, + i2c_dev->irq_wake_up); + + if (device_may_wakeup(&client->dev) && i2c_dev->irq_wake_up) { + //if (!disable_irq_wake(client->irq)) + // i2c_dev->irq_wake_up = false; + i2c_dev->irq_wake_up = false; + } + return 0; +} + +static const struct i2c_device_id nfc_i2c_dev_id[] = { + {NFC_I2C_DEV_ID, 0}, + {} +}; + +static const struct of_device_id nfc_i2c_dev_match_table[] = { + {.compatible = NFC_I2C_DRV_STR,}, + {} +}; + +static const struct dev_pm_ops nfc_i2c_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(nfc_i2c_dev_suspend, nfc_i2c_dev_resume) +}; + +static struct i2c_driver nfc_i2c_dev_driver = { + .id_table = nfc_i2c_dev_id, + .probe = nfc_i2c_dev_probe, + .remove = nfc_i2c_dev_remove, + .driver = { + .name = NFC_I2C_DRV_STR, + .pm = &nfc_i2c_dev_pm_ops, + .of_match_table = nfc_i2c_dev_match_table, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; + +MODULE_DEVICE_TABLE(of, nfc_i2c_dev_match_table); + +static int __init nfc_i2c_dev_init(void) +{ + int ret = 0; + + ret = i2c_add_driver(&nfc_i2c_dev_driver); + if (ret != 0) + pr_err("NFC I2C add driver error ret %d\n", ret); + return ret; +} + +module_init(nfc_i2c_dev_init); + +static void __exit nfc_i2c_dev_exit(void) +{ + pr_debug("Unloading NFC I2C driver\n"); + i2c_del_driver(&nfc_i2c_dev_driver); +} + +module_exit(nfc_i2c_dev_exit); + +MODULE_DESCRIPTION("QTI NFC I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/nfc/sn2xx/nfc_i2c_drv.h b/drivers/nfc/sn2xx/nfc_i2c_drv.h new file mode 100644 index 000000000000..3e9321fbf6a0 --- /dev/null +++ b/drivers/nfc/sn2xx/nfc_i2c_drv.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2021 NXP + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ******************************************************************************/ + +#ifndef _NFC_I2C_DRV_H_ +#define _NFC_I2C_DRV_H_ +#include + +#define NFC_I2C_DRV_STR "qcom,sn-nci" /*kept same as dts */ +#define NFC_I2C_DEV_ID "sn-i2c" + +struct nfc_dev; + +//Interface specific parameters +struct i2c_dev { + struct i2c_client *client; + /*IRQ parameters */ + bool irq_enabled; + spinlock_t irq_enabled_lock; + /* NFC_IRQ wake-up state */ + bool irq_wake_up; +}; + +long nfc_i2c_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg); +int nfc_i2c_dev_probe(struct i2c_client *client, + const struct i2c_device_id *id); +int nfc_i2c_dev_remove(struct i2c_client *client); +int nfc_i2c_dev_suspend(struct device *device); +int nfc_i2c_dev_resume(struct device *device); + +int i2c_enable_irq(struct nfc_dev *dev); +int i2c_disable_irq(struct nfc_dev *dev); +int i2c_write(struct nfc_dev *dev, const char *buf, size_t count, + int max_retry_cnt); +int i2c_read(struct nfc_dev *dev, char *buf, size_t count, int timeout); + +#endif //_NFC_I2C_DRV_H_ diff --git a/drivers/nfc/st21nfc/Android.mk b/drivers/nfc/st21nfc/Android.mk new file mode 100644 index 000000000000..7baa9bff33d5 --- /dev/null +++ b/drivers/nfc/st21nfc/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := st21nfc.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_info.ko +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/nfc/st21nfc/Kbuild b/drivers/nfc/st21nfc/Kbuild new file mode 100644 index 000000000000..76794fa10ece --- /dev/null +++ b/drivers/nfc/st21nfc/Kbuild @@ -0,0 +1,7 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += st21nfc.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../mmi_info/Module.symvers diff --git a/drivers/nfc/st21nfc/Makefile b/drivers/nfc/st21nfc/Makefile new file mode 100644 index 000000000000..4916d32b6d02 --- /dev/null +++ b/drivers/nfc/st21nfc/Makefile @@ -0,0 +1,17 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS := $(M)/../../mmi_info/Module.symvers + diff --git a/drivers/nfc/st21nfc/st21nfc.c b/drivers/nfc/st21nfc/st21nfc.c new file mode 100644 index 000000000000..2210d252956e --- /dev/null +++ b/drivers/nfc/st21nfc/st21nfc.c @@ -0,0 +1,1090 @@ +/* + * Copyright (C) 2016 ST Microelectronics S.A. + * Copyright (C) 2010 Stollmann E+V GmbH + * Copyright (C) 2010 Trusted Logic S.A. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions 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 . + */ +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define MAX_BUFFER_SIZE 260 +#define WAKEUP_SRC_TIMEOUT (500) + +#define DRIVER_VERSION "2.0.4" + +#ifdef ST_NFC_HW_DETECT +#define HW_54J_PROP_NAME "ese_st" +#define HW_21J_PROP_NAME "st" +#define HW_UNKNOWN_PROP_NAME "unknown" +#endif + +/* prototypes */ +static irqreturn_t st21nfc_dev_irq_handler(int irq, void *dev_id); +/* + * The platform data member 'polarity_mode' defines + * how the wakeup pin is configured and handled. + * it can take the following values : + * IRQF_TRIGGER_RISING + * IRQF_TRIGGER_HIGH + */ + +struct st21nfc_platform { + struct mutex read_mutex; + struct i2c_client *client; + unsigned int irq_gpio; + unsigned int reset_gpio; + unsigned int ena_gpio; + unsigned int clkreq_gpio; + unsigned int polarity_mode; +}; + +static bool irqIsAttached; + +static bool device_open; /* Is device open? */ + +#ifdef ST_NFC_HW_DETECT +static int chipsettype = -1; +#endif + +struct st21nfc_dev { + wait_queue_head_t read_wq; + struct miscdevice st21nfc_device; + bool irq_enabled; + bool irq_wake_up; + struct st21nfc_platform platform_data; + spinlock_t irq_enabled_lock; + /* CLK control */ + bool clk_run; + struct clk *s_clk; + u8 *read_buf; + u8 *write_buf; +}; + +/* + * Routine to enable clock. + * this routine can be extended to select from multiple + * sources based on clk_src_name. + */ +static int st_clock_select(struct st21nfc_dev *st21nfc_dev) +{ + int r = 0; + + st21nfc_dev->s_clk = clk_get(&st21nfc_dev->platform_data.client->dev, "nfc_ref_clk"); + + /* if NULL we assume external crystal and dont fail */ + if ((st21nfc_dev->s_clk == NULL) || IS_ERR(st21nfc_dev->s_clk)) + return 0; + + if (st21nfc_dev->clk_run == false) + r = clk_prepare_enable(st21nfc_dev->s_clk); + + if (r) + goto err_clk; + + st21nfc_dev->clk_run = true; + + return r; + +err_clk: + r = -1; + return r; +} + +/* + * Routine to disable clocks + */ +static int st_clock_deselect(struct st21nfc_dev *st21nfc_dev) +{ + int r = -1; + + /* if NULL we assume external crystal and dont fail */ + if ((st21nfc_dev->s_clk == NULL) || IS_ERR(st21nfc_dev->s_clk)) + return 0; + + if ((st21nfc_dev->s_clk != NULL) && !IS_ERR(st21nfc_dev->s_clk)) { + if (st21nfc_dev->clk_run == true) { + clk_disable_unprepare(st21nfc_dev->s_clk); + st21nfc_dev->clk_run = false; + } + return 0; + } + return r; +} + +static void st21nfc_disable_irq(struct st21nfc_dev *st21nfc_dev); + +static int st21nfc_loc_set_polaritymode(struct st21nfc_dev *st21nfc_dev, + int mode) +{ + struct i2c_client *client = st21nfc_dev->platform_data.client; + unsigned int irq_type; + int ret; + + st21nfc_dev->platform_data.polarity_mode = mode; + /* setup irq_flags */ + switch (mode) { + case IRQF_TRIGGER_RISING: + irq_type = IRQ_TYPE_EDGE_RISING; + break; + case IRQF_TRIGGER_HIGH: + irq_type = IRQ_TYPE_LEVEL_HIGH; + break; + default: + irq_type = IRQ_TYPE_EDGE_RISING; + break; + } + if (irqIsAttached) { + free_irq(client->irq, st21nfc_dev); + irqIsAttached = false; + } + ret = irq_set_irq_type(client->irq, irq_type); + if (ret) { + pr_err("set_irq_type failed\n"); + return -ENODEV; + } + /* request irq. the irq is set whenever the chip has data available + * for reading. it is cleared when all data has been read. + */ + pr_debug("requesting IRQ %d\n", client->irq); + st21nfc_dev->irq_enabled = true; + + ret = request_irq(client->irq, st21nfc_dev_irq_handler, + st21nfc_dev->platform_data.polarity_mode, client->name, st21nfc_dev); + if (!ret) { + irqIsAttached = true; + st21nfc_disable_irq(st21nfc_dev); + } + + return ret; +} + +static void st21nfc_disable_irq(struct st21nfc_dev *st21nfc_dev) +{ + unsigned long flags; + + spin_lock_irqsave(&st21nfc_dev->irq_enabled_lock, flags); + if (st21nfc_dev->irq_enabled) { + pr_debug("IRQ %d\n", + st21nfc_dev->platform_data.client->irq); + disable_irq_nosync(st21nfc_dev->platform_data.client->irq); + st21nfc_dev->irq_enabled = false; + } + spin_unlock_irqrestore(&st21nfc_dev->irq_enabled_lock, flags); +} + +static void st21nfc_enable_irq(struct st21nfc_dev *st21nfc_dev) +{ + unsigned long flags; + + spin_lock_irqsave(&st21nfc_dev->irq_enabled_lock, flags); + if (!st21nfc_dev->irq_enabled) { + st21nfc_dev->irq_enabled = true; + enable_irq(st21nfc_dev->platform_data.client->irq); + + } + spin_unlock_irqrestore(&st21nfc_dev->irq_enabled_lock, flags); +} + + +static irqreturn_t st21nfc_dev_irq_handler(int irq, void *dev_id) +{ + struct st21nfc_dev *st21nfc_dev = dev_id; + + pr_debug("enter\n"); + if (device_may_wakeup(&st21nfc_dev->platform_data.client->dev)) + pm_wakeup_event(&st21nfc_dev->platform_data.client->dev, + WAKEUP_SRC_TIMEOUT); + st21nfc_disable_irq(st21nfc_dev); + + /* Wake up waiting readers */ + wake_up(&st21nfc_dev->read_wq); + + return IRQ_HANDLED; +} + + +static ssize_t st21nfc_dev_read(struct file *filp, char __user *buf, + size_t count, loff_t *offset) +{ + struct st21nfc_dev *st21nfc_dev = container_of(filp->private_data, + struct st21nfc_dev, + st21nfc_device); + char *tmp = NULL; + int ret; + + if (count > MAX_BUFFER_SIZE) + count = MAX_BUFFER_SIZE; + + tmp = st21nfc_dev->read_buf; + memset(tmp, 0x00, count); + + pr_debug("reading %zu bytes.\n", count); + + ret = gpio_get_value(st21nfc_dev->platform_data.irq_gpio); + if (ret <= 0) { + /* The CLF has nothing to send */ + memset(tmp, 0x7E, count); + ret = count; + } else { + mutex_lock(&st21nfc_dev->platform_data.read_mutex); + + /* Read data */ + ret = i2c_master_recv(st21nfc_dev->platform_data.client, tmp, count); + mutex_unlock(&st21nfc_dev->platform_data.read_mutex); + + if (ret < 0) { + pr_err("i2c_master_recv returned %d\n", ret); + return ret; + } + if (ret > count) { + pr_err("received too many bytes from i2c (%d)\n", ret); + return -EIO; + } + } + + if (copy_to_user(buf, tmp, ret)) { + pr_warn("failed to copy to user space\n"); + return -EFAULT; + } + return ret; +} + +#define MAX_RETRY_COUNT 3 + +static ssize_t st21nfc_dev_write(struct file *filp, const char __user *buf, + size_t count, loff_t *offset) +{ + struct st21nfc_dev *st21nfc_dev; + char *tmp = NULL; + int ret = count; + int retry_cnt; + + st21nfc_dev = container_of(filp->private_data, + struct st21nfc_dev, st21nfc_device); + pr_debug("st21nfc_dev ptr %p\n", st21nfc_dev); + + if (count > MAX_BUFFER_SIZE) + count = MAX_BUFFER_SIZE; + + tmp = st21nfc_dev->write_buf; + memset(tmp, 0x00, count); + + if (copy_from_user(tmp, buf, count)) { + pr_err("failed to copy from user space\n"); + return -EFAULT; + } + + pr_debug("writing %zu bytes.\n", count); + /* Write data */ + for (retry_cnt = 1; retry_cnt <= MAX_RETRY_COUNT; retry_cnt++) { + ret = i2c_master_send(st21nfc_dev->platform_data.client, tmp, count); + if (ret < 0) { + //pr_err("%s : i2c_master_send returned %d (retry (%d)\n", __func__, ret, retry_cnt); + usleep_range(1000, 1100); + } else if (ret == count) + break; + } + if (ret != count) { + pr_err("i2c_master_send returned %d instead of %zu\n", ret, count); + ret = -EIO; + } + return ret; +} + +static int st21nfc_dev_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + struct st21nfc_dev *st21nfc_dev = NULL; + + if (device_open) { + ret = -EBUSY; + pr_err("device already opened ret= %d\n", ret); + } else { + device_open = true; + st21nfc_dev = container_of(filp->private_data, + struct st21nfc_dev, + st21nfc_device); + + pr_debug("device_open = %d", device_open); + pr_debug("%d,%d ", imajor(inode), iminor(inode)); + pr_debug("st21nfc_dev ptr %p\n", st21nfc_dev); + + } + return ret; +} + + +static int st21nfc_release(struct inode *inode, struct file *file) +{ + device_open = false; + pr_debug("device_open = %d\n", device_open); + + return 0; +} + +static void (*st21nfc_st54spi_cb)(int, void *); +static void *st21nfc_st54spi_data; + +void st21nfc_register_st54spi_cb(void (*cb)(int, void *), void *data) +{ + pr_info("%s\n", __func__); + st21nfc_st54spi_cb = cb; + st21nfc_st54spi_data = data; +} +void st21nfc_unregister_st54spi_cb(void) +{ + pr_info("%s\n", __func__); + st21nfc_st54spi_cb = NULL; + st21nfc_st54spi_data = NULL; +} + +static long st21nfc_dev_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct st21nfc_dev *st21nfc_dev = container_of(filp->private_data, + struct st21nfc_dev, + st21nfc_device); + + int ret = 0; + u32 tmp; + + pr_debug("cmd=%d", cmd); + + /* Check type and command number */ + if (_IOC_TYPE(cmd) != ST21NFC_MAGIC) + return -ENOTTY; + + /* Check access direction once here; don't repeat below. + * IOC_DIR is from the user perspective, while access_ok is + * from the kernel perspective; so they look reversed. + */ + if (_IOC_DIR(cmd) & _IOC_READ) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0) + ret = !access_ok((void __user *)arg, _IOC_SIZE(cmd)); +#else + ret = !access_ok(VERIFY_WRITE, + (void __user *)arg, _IOC_SIZE(cmd)); +#endif + if (ret == 0 && _IOC_DIR(cmd) & _IOC_WRITE) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0) + ret = !access_ok((void __user *)arg, _IOC_SIZE(cmd)); +#else + ret = !access_ok(VERIFY_READ, + (void __user *)arg, _IOC_SIZE(cmd)); +#endif + if (ret) + return -EFAULT; + + switch (cmd) { + + case ST21NFC_SET_POLARITY_RISING: + case ST21NFC_LEGACY_SET_POLARITY_RISING: + pr_info(" ### ST21NFC_SET_POLARITY_RISING ###\n"); + st21nfc_loc_set_polaritymode(st21nfc_dev, IRQF_TRIGGER_RISING); + break; + + case ST21NFC_SET_POLARITY_HIGH: + case ST21NFC_LEGACY_SET_POLARITY_HIGH: + pr_info(" ### ST21NFC_SET_POLARITY_HIGH ###\n"); + st21nfc_loc_set_polaritymode(st21nfc_dev, IRQF_TRIGGER_HIGH); + break; + + case ST21NFC_PULSE_RESET: + case ST21NFC_LEGACY_PULSE_RESET: + /* Double pulse is done to exit Quick boot mode.*/ + pr_info("Double Pulse Request\n"); + if (st21nfc_dev->platform_data.reset_gpio != 0) { + if (st21nfc_st54spi_cb != 0) + (*st21nfc_st54spi_cb)(ST54SPI_CB_RESET_START, + st21nfc_st54spi_data); + /* pulse low for 20 millisecs */ + gpio_set_value(st21nfc_dev->platform_data.reset_gpio, + 0); + msleep(20); + gpio_set_value(st21nfc_dev->platform_data.reset_gpio, + 1); + msleep(10); + /* pulse low for 20 millisecs */ + gpio_set_value(st21nfc_dev->platform_data.reset_gpio, + 0); + msleep(20); + gpio_set_value(st21nfc_dev->platform_data.reset_gpio, + 1); + msleep(20); + pr_info("done Double Pulse Request\n"); + if (st21nfc_st54spi_cb != 0) + (*st21nfc_st54spi_cb)(ST54SPI_CB_RESET_END, + st21nfc_st54spi_data); + } + break; + + case ST21NFC_GET_WAKEUP: + case ST21NFC_LEGACY_GET_WAKEUP: + /* deliver state of Wake_up_pin as return value of ioctl */ + ret = gpio_get_value(st21nfc_dev->platform_data.irq_gpio); + /* + * Warning: depending on gpio_get_value implementation, + * it can returns a value different than 1 in case of high level + */ + if (ret > 0) + ret = 1; + else + ret = 0; + pr_debug("irq get gpio result %d\n", ret); + break; + case ST21NFC_GET_POLARITY: + case ST21NFC_LEGACY_GET_POLARITY: + ret = st21nfc_dev->platform_data.polarity_mode; + pr_debug("get polarity %d\n", ret); + break; + case ST21NFC_RECOVERY: + case ST21NFC_LEGACY_RECOVERY: + /* For ST21NFCD usage only */ + pr_info("Recovery Request\n"); + if (st21nfc_dev->platform_data.reset_gpio != 0) { + if (irqIsAttached) { + struct i2c_client *client = st21nfc_dev->platform_data.client; + free_irq(client->irq, st21nfc_dev); + irqIsAttached = false; + pr_debug("IRQ detached\n"); + } + /* pulse low for 20 millisecs */ + gpio_set_value(st21nfc_dev->platform_data.reset_gpio, 0); + msleep(10); + /* during the reset, force IRQ OUT as */ + /* DH output instead of input in normal usage */ + ret = gpio_direction_output( + st21nfc_dev->platform_data.irq_gpio, 1); + if (ret) { + pr_err("gpio_direction_output failed\n"); + ret = -ENODEV; + break; + } + gpio_set_value(st21nfc_dev->platform_data.irq_gpio, 1); + msleep(10); + gpio_set_value(st21nfc_dev->platform_data.reset_gpio, + 1); + pr_info("done Pulse Request\n"); + } + msleep(20); + gpio_set_value(st21nfc_dev->platform_data.irq_gpio, 0); + msleep(20); + gpio_set_value(st21nfc_dev->platform_data.irq_gpio, 1); + msleep(20); + gpio_set_value(st21nfc_dev->platform_data.irq_gpio, 0); + msleep(20); + pr_info("Recovery procedure finished\n"); + ret = gpio_direction_input(st21nfc_dev->platform_data.irq_gpio); + if (ret) { + pr_err("gpio_direction_input failed\n"); + ret = -ENODEV; + } + break; + case ST21NFC_USE_ESE: + ret = __get_user(tmp, (u32 __user *)arg); + if (ret == 0) { + if (st21nfc_st54spi_cb != 0) + (*st21nfc_st54spi_cb)(tmp ? ST54SPI_CB_ESE_USED + : ST54SPI_CB_ESE_NOT_USED, + st21nfc_st54spi_data); + } + pr_debug("use ESE %d : %d\n", ret, tmp); + break; + default: + pr_err("bad ioctl %u\n", cmd); + ret = -EINVAL; + break; + } + return ret; +} + +static unsigned int st21nfc_poll(struct file *file, poll_table *wait) +{ + struct st21nfc_dev *st21nfc_dev = container_of(file->private_data, + struct st21nfc_dev, + st21nfc_device); + unsigned int mask = 0; + int pinlev = 0; + + /* wait for Wake_up_pin == high */ + poll_wait(file, &st21nfc_dev->read_wq, wait); + + pinlev = gpio_get_value(st21nfc_dev->platform_data.irq_gpio); + + if (pinlev > 0) { + pr_debug("return ready\n"); + mask = POLLIN | POLLRDNORM; /* signal data avail */ + st21nfc_disable_irq(st21nfc_dev); + } else { + /* Wake_up_pin is low. Activate ISR */ + if (!st21nfc_dev->irq_enabled) { + pr_debug("enable irq\n"); + st21nfc_enable_irq(st21nfc_dev); + } else { + pr_debug("irq already enabled\n"); + } + } + return mask; +} + +static const struct file_operations st21nfc_dev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = st21nfc_dev_read, + .write = st21nfc_dev_write, + .open = st21nfc_dev_open, + .poll = st21nfc_poll, + .release = st21nfc_release, + + .unlocked_ioctl = st21nfc_dev_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = st21nfc_dev_ioctl +#endif +}; + +static ssize_t st21nfc_show_i2c_addr(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (client != NULL) + return snprintf(buf, 6, + "0x%.2x\n", client->addr); + return 0; +} /* st21nfc_show_i2c_addr() */ + +static ssize_t st21nfc_change_i2c_addr(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct st21nfc_dev *data = dev_get_drvdata(dev); + long new_addr = 0; + + if (data != NULL && data->platform_data.client != NULL) { + if (!kstrtol(buf, 10, &new_addr)) { + mutex_lock(&data->platform_data.read_mutex); + data->platform_data.client->addr = new_addr; + mutex_unlock(&data->platform_data.read_mutex); + return count; + } + return -EINVAL; + } + return 0; +} + +static ssize_t st21nfc_version(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, strlen(DRIVER_VERSION)+2, "%s\n", DRIVER_VERSION); +} + +#ifdef ST_NFC_HW_DETECT +static ssize_t st21nfc_hwversion(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if(chipsettype == 1) + return snprintf(buf, strlen(HW_54J_PROP_NAME)+2, "%s\n", HW_54J_PROP_NAME); + + if(chipsettype == 0) + return snprintf(buf, strlen(HW_21J_PROP_NAME)+2, "%s\n", HW_21J_PROP_NAME); + + return snprintf(buf, strlen(HW_UNKNOWN_PROP_NAME)+2, "%s\n", HW_UNKNOWN_PROP_NAME); +} +#endif + +static DEVICE_ATTR(i2c_addr, S_IRUGO | S_IWUSR, st21nfc_show_i2c_addr, + st21nfc_change_i2c_addr); + +static DEVICE_ATTR(version, S_IRUGO, st21nfc_version, NULL); + +#ifdef ST_NFC_HW_DETECT +static DEVICE_ATTR(hwversion, S_IRUGO, st21nfc_hwversion, NULL); +#endif + +static struct attribute *st21nfc_attrs[] = { + &dev_attr_i2c_addr.attr, + &dev_attr_version.attr, +#ifdef ST_NFC_HW_DETECT + &dev_attr_hwversion.attr, +#endif + NULL, +}; + +static struct attribute_group st21nfc_attr_grp = { + .attrs = st21nfc_attrs, +}; + +#ifdef CONFIG_OF +static int nfc_parse_dt(struct device *dev, struct st21nfc_platform_data *pdata) +{ + int r = 0; + struct device_node *np = dev->of_node; + + np = of_find_compatible_node(NULL, NULL, "st,st21nfc"); + if (IS_ERR_OR_NULL(np)) { + pr_err("[dsc] cannot find compatible node \"%s\"", "st,st21nfc"); + return -ENODEV; + } + + pdata->reset_gpio = of_get_named_gpio(np, "st,reset_gpio", 0); + if ((!gpio_is_valid(pdata->reset_gpio))) { + pr_err("[dsc]fail to get reset_gpio\n"); + return -EINVAL; + } + pdata->irq_gpio = of_get_named_gpio(np, "st,irq_gpio", 0); + if ((!gpio_is_valid(pdata->irq_gpio))) { + pr_err("[dsc]fail to get irq_gpio\n"); + return -EINVAL; + } + + pdata->clkreq_gpio = of_get_named_gpio(np, "st,clkreq_gpio", 0); + if ((!gpio_is_valid(pdata->clkreq_gpio))) + pr_err("[dsc][OPTIONAL] fail to get clkreq_gpio\n"); + + pdata->polarity_mode = IRQF_TRIGGER_RISING; + pr_info("[dsc]get reset_gpio[%d], \ + irq_gpio[%d], polarity_mode[%d]\n",pdata->reset_gpio, + pdata->irq_gpio, pdata->polarity_mode); + return r; +} +#else +static int nfc_parse_dt(struct device *dev, struct st21nfc_platform_data *pdata) +{ + return 0; +} +#endif + + +static int st21nfc_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct st21nfc_platform_data *platform_data; + struct st21nfc_dev *st21nfc_dev; + +#ifdef ST_NFC_HW_DETECT + int core_reset_ntf_start_point = 0; + //int print_point = 0; + char *tmp = NULL; +#endif + + pr_info("st21nfc_probe\n"); + + if (client->dev.of_node && !mmi_device_is_available(client->dev.of_node)) { + pr_err("%s : mmi: device not supported\n", __func__); + return -ENODEV; + } else { + pr_err("%s : supported device found\n", __func__); + } + + if (client->dev.of_node) { + platform_data = devm_kzalloc(&client->dev, + sizeof(struct st21nfc_platform_data), GFP_KERNEL); + if (!platform_data) { + dev_err(&client->dev, + "nfc-nci prob: Failed to allocate memory\n"); + return -ENOMEM; + } + pr_info("Parse st21nfc DTS\n"); + ret = nfc_parse_dt(&client->dev, platform_data); + if (ret) + return ret; + } else { + platform_data = client->dev.platform_data; + pr_info("No st21nfc DTS\n"); + } + if (!platform_data) + return -EINVAL; + dev_dbg(&client->dev, + "nfc-nci probe: inside nfc-nci flags = %x\n", client->flags); + + if (platform_data == NULL) { + dev_err(&client->dev, "nfc-nci probe: failed\n"); + return -ENODEV; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + pr_err("need I2C_FUNC_I2C\n"); + return -ENODEV; + } + + client->adapter->timeout = msecs_to_jiffies(3 * 10); /* 30ms */ + client->adapter->retries = 0; + + st21nfc_dev = kzalloc(sizeof(*st21nfc_dev), GFP_KERNEL); + if (st21nfc_dev == NULL) { + dev_err(&client->dev, + "failed to allocate memory for module data\n"); + ret = -ENOMEM; + goto err_exit; + } + + pr_debug("dev_cb_addr %p\n", st21nfc_dev); + + /* store for later use */ + st21nfc_dev->platform_data.irq_gpio = platform_data->irq_gpio; + st21nfc_dev->platform_data.clkreq_gpio = platform_data->clkreq_gpio; + st21nfc_dev->platform_data.ena_gpio = platform_data->ena_gpio; + st21nfc_dev->platform_data.reset_gpio = platform_data->reset_gpio; + st21nfc_dev->platform_data.polarity_mode = platform_data->polarity_mode; + st21nfc_dev->platform_data.client = client; + + st21nfc_dev->read_buf = kmalloc(MAX_BUFFER_SIZE, GFP_ATOMIC); + if (!st21nfc_dev->read_buf ) { + dev_err(&client->dev, + "failed to allocate memory for st21nfc_dev->read_buf\n"); + ret = -ENOMEM; + goto err_free_dev; + } + + st21nfc_dev->write_buf = kmalloc(MAX_BUFFER_SIZE, GFP_ATOMIC); + if (!st21nfc_dev->write_buf) { + dev_err(&client->dev, + "failed to allocate memory for st21nfc_dev->write_buf\n"); + ret = -ENOMEM; + goto err_free_read_buffer; + } + + ret = gpio_request(platform_data->irq_gpio, "irq_gpio"); + if (ret) { + pr_err("irq gpio_request failed\n"); + ret = -ENODEV; + goto err_free_write_buffer; + } + + ret = gpio_direction_input(platform_data->irq_gpio); + if (ret) { + pr_err("irq gpio_direction_input failed\n"); + ret = -ENODEV; + goto err_free_write_buffer; + } + + if (gpio_is_valid(platform_data->clkreq_gpio)) { + ret = gpio_request(platform_data->clkreq_gpio, "clkreq_gpio"); + if (ret) { + pr_err("clkreq gpio_request failed\n"); + ret = -ENODEV; + goto err_free_write_buffer; + } else { + ret = gpio_direction_input(platform_data->clkreq_gpio); + if (ret) { + pr_err("clkreq gpio_direction_input failed\n"); + ret = -ENODEV; + goto err_clkreq_gpio; + } + } + } else { + pr_warn("clkreq gpio not provided. Ext xtal expected\n"); + } + + /* initialize irqIsAttached variable */ + irqIsAttached = false; + + /* initialize device_open variable */ + device_open = 0; + + /* handle optional RESET */ + if (platform_data->reset_gpio != 0) { + ret = gpio_request(platform_data->reset_gpio, "reset_gpio"); + if (ret) { + pr_err("reset gpio_request failed\n"); + ret = -ENODEV; + goto err_clkreq_gpio; + } + ret = gpio_direction_output(platform_data->reset_gpio, 1); + if (ret) { + pr_err("reset gpio_direction_output failed\n"); + ret = -ENODEV; + goto err_reset_gpio; + } + /* low active */ + gpio_set_value(st21nfc_dev->platform_data.reset_gpio, 1); + } + + client->irq = gpio_to_irq(platform_data->irq_gpio); + + /* init mutex and queues */ + init_waitqueue_head(&st21nfc_dev->read_wq); + mutex_init(&st21nfc_dev->platform_data.read_mutex); + spin_lock_init(&st21nfc_dev->irq_enabled_lock); + pr_debug("debug irq_gpio = %d, client-irq = %d\n", platform_data->irq_gpio, client->irq); + st21nfc_dev->st21nfc_device.minor = MISC_DYNAMIC_MINOR; + st21nfc_dev->st21nfc_device.name = "st21nfc"; + st21nfc_dev->st21nfc_device.fops = &st21nfc_dev_fops; + st21nfc_dev->st21nfc_device.parent = &client->dev; + + i2c_set_clientdata(client, st21nfc_dev); + ret = misc_register(&st21nfc_dev->st21nfc_device); + if (ret) { + pr_err("misc_register failed\n"); + goto err_misc_register; + } + + if (sysfs_create_group(&client->dev.kobj, &st21nfc_attr_grp)) { + pr_err("sysfs_create_group failed\n"); + goto err_request_irq_failed; + } + st21nfc_disable_irq(st21nfc_dev); + + device_init_wakeup(&client->dev, true); + device_set_wakeup_capable(&client->dev, true); + st21nfc_dev->irq_wake_up = false; + + ret = st_clock_select(st21nfc_dev); + if (ret < 0) { + pr_err("st_clock_select failed\n"); + goto err_request_irq_failed; + } + pr_info("done successfully\n"); + +#ifdef ST_NFC_HW_DETECT + pr_info("Start hw checking, double pulse request"); + if (st21nfc_dev->platform_data.reset_gpio != 0) { + if (st21nfc_st54spi_cb != 0) + (*st21nfc_st54spi_cb)(ST54SPI_CB_RESET_START, + st21nfc_st54spi_data); + /* pulse low for 20 millisecs */ + gpio_set_value(st21nfc_dev->platform_data.reset_gpio, + 0); + msleep(20); + gpio_set_value(st21nfc_dev->platform_data.reset_gpio, + 1); + msleep(10); + /* pulse low for 20 millisecs */ + gpio_set_value(st21nfc_dev->platform_data.reset_gpio, + 0); + msleep(20); + gpio_set_value(st21nfc_dev->platform_data.reset_gpio, + 1); + msleep(20); + pr_info("done Double Pulse Request\n"); + if (st21nfc_st54spi_cb != 0) + (*st21nfc_st54spi_cb)(ST54SPI_CB_RESET_END, + st21nfc_st54spi_data); + } + + pr_info("Try read CORE_RESET_NTF from chipset"); + tmp = st21nfc_dev->read_buf; + memset(tmp, 0x00, MAX_BUFFER_SIZE); + + ret = gpio_get_value(st21nfc_dev->platform_data.irq_gpio); + if (ret <= 0) { + /* The CLF has nothing to send */ + memset(tmp, 0x7E, MAX_BUFFER_SIZE); + ret = MAX_BUFFER_SIZE; + } else { + mutex_lock(&st21nfc_dev->platform_data.read_mutex); + + /* Read data */ + ret = i2c_master_recv(st21nfc_dev->platform_data.client, tmp, MAX_BUFFER_SIZE); + mutex_unlock(&st21nfc_dev->platform_data.read_mutex); + + if (ret < 0) { + pr_err("HW info detect:i2c_master_recv returned %d\n", ret); + chipsettype = -1; + } + if (ret > MAX_BUFFER_SIZE) { + pr_err("received too many bytes from i2c (%d)\n", ret); + } + if (ret > 0) + { + pr_info("read %d byte from ST NFC device", ret); + /* There will be some 0x7e byte at the beginning of the read buf. ignore them */ + + for (core_reset_ntf_start_point = 0;core_reset_ntf_start_point < ret;core_reset_ntf_start_point++) { + if(tmp[core_reset_ntf_start_point] != 0x7e) + break; + } + + pr_info("core_reset_ntf start at %d" , core_reset_ntf_start_point); + //for (print_point = core_reset_ntf_start_point;print_point < ret -core_reset_ntf_start_point; print_point++) + //pr_info("%02x ", tmp[print_point]); + + if (tmp[core_reset_ntf_start_point + 1] == 0x0 && tmp[core_reset_ntf_start_point + 3] == 0x1) { + pr_info("HW info detect: route mode NTF received"); + if (tmp[core_reset_ntf_start_point + 8] == 0x05) + chipsettype = 1; + else if (tmp[core_reset_ntf_start_point + 8] == 0x04) + chipsettype = 0; + else + chipsettype = -1; + + } else if (tmp[core_reset_ntf_start_point + 2] == 0x39 && tmp[core_reset_ntf_start_point + 3] == 0xA1) { + pr_info("HW info detect: loader mode NTF received"); + if (tmp[core_reset_ntf_start_point + 16] == 0x01) + chipsettype = 1; + else if (tmp[core_reset_ntf_start_point + 16] == 0x02) + chipsettype = 0; + else + chipsettype = -1; + } else { + pr_info("HW info detect: No core_reset_ntf received"); + chipsettype = -1; + } + } + } +#endif + return 0; + +err_request_irq_failed: + misc_deregister(&st21nfc_dev->st21nfc_device); +err_misc_register: + mutex_destroy(&st21nfc_dev->platform_data.read_mutex); +err_reset_gpio: + if (gpio_is_valid(platform_data->reset_gpio)) + gpio_free(platform_data->reset_gpio); +err_clkreq_gpio: + if (gpio_is_valid(platform_data->clkreq_gpio)) + gpio_free(platform_data->clkreq_gpio); +err_free_write_buffer: + kfree(st21nfc_dev->write_buf); +err_free_read_buffer: + kfree(st21nfc_dev->read_buf); +err_free_dev: + kfree(st21nfc_dev); +err_exit: + gpio_free(platform_data->irq_gpio); + if (platform_data->ena_gpio != 0) + gpio_free(platform_data->ena_gpio); + return ret; +} + +#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE +static int st21nfc_remove(struct i2c_client *client) +#else +static void st21nfc_remove(struct i2c_client *client) +#endif +{ + struct st21nfc_dev *st21nfc_dev; + + st21nfc_dev = i2c_get_clientdata(client); + st_clock_deselect(st21nfc_dev); + free_irq(client->irq, st21nfc_dev); + misc_deregister(&st21nfc_dev->st21nfc_device); + mutex_destroy(&st21nfc_dev->platform_data.read_mutex); + if (gpio_is_valid(st21nfc_dev->platform_data.reset_gpio)) + gpio_free(st21nfc_dev->platform_data.reset_gpio); + if (gpio_is_valid(st21nfc_dev->platform_data.clkreq_gpio)) + gpio_free(st21nfc_dev->platform_data.clkreq_gpio); + gpio_free(st21nfc_dev->platform_data.irq_gpio); + if (st21nfc_dev->platform_data.ena_gpio != 0) + gpio_free(st21nfc_dev->platform_data.ena_gpio); + kfree(st21nfc_dev->write_buf); + kfree(st21nfc_dev->read_buf); + kfree(st21nfc_dev); +#if KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE + return 0; +#endif +} + +static int st21nfc_suspend(struct device *device) +{ + struct i2c_client *client = to_i2c_client(device); + struct st21nfc_dev *st21nfc_dev = i2c_get_clientdata(client); + + if (device_may_wakeup(&client->dev) && st21nfc_dev->irq_enabled) { + if (!enable_irq_wake(client->irq)) + st21nfc_dev->irq_wake_up = true; + } + + return 0; +} + +static int st21nfc_resume(struct device *device) +{ + struct i2c_client *client = to_i2c_client(device); + struct st21nfc_dev *st21nfc_dev = i2c_get_clientdata(client); + + if (device_may_wakeup(&client->dev) && st21nfc_dev->irq_wake_up) { + if (!disable_irq_wake(client->irq)) + st21nfc_dev->irq_wake_up = false; + } + + return 0; +} + + +static const struct i2c_device_id st21nfc_id[] = { + {"st21nfc", 0}, + {} +}; + +static const struct of_device_id st21nfc_of_match[] = { + { .compatible = "st,st21nfc", }, + {} +}; + +static const struct dev_pm_ops st21nfc_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(st21nfc_suspend, st21nfc_resume) +}; + +static struct i2c_driver st21nfc_driver = { + .id_table = st21nfc_id, + .driver = { + .name = "st21nfc", + .owner = THIS_MODULE, + .of_match_table = st21nfc_of_match, + .pm = &st21nfc_pm_ops, + }, + .probe = st21nfc_probe, + .remove = st21nfc_remove, +}; + +/* + * module load/unload record keeping + */ + +static int __init st21nfc_dev_init(void) +{ + pr_info("Loading st21nfc driver\n"); + return i2c_add_driver(&st21nfc_driver); +} + +module_init(st21nfc_dev_init); + +static void __exit st21nfc_dev_exit(void) +{ + pr_debug("Unloading st21nfc driver\n"); + i2c_del_driver(&st21nfc_driver); +} + +module_exit(st21nfc_dev_exit); + +MODULE_AUTHOR("STMicroelectronics"); +MODULE_DESCRIPTION("NFC ST21NFC driver"); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/Android.mk b/drivers/power/Android.mk new file mode 100644 index 000000000000..9c82a6b1f912 --- /dev/null +++ b/drivers/power/Android.mk @@ -0,0 +1,4 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/drivers/power/bm_adsp_ulog/Android.mk b/drivers/power/bm_adsp_ulog/Android.mk new file mode 100755 index 000000000000..05d6ad994824 --- /dev/null +++ b/drivers/power/bm_adsp_ulog/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := bm_adsp_ulog.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/bm_adsp_ulog/Kbuild b/drivers/power/bm_adsp_ulog/Kbuild new file mode 100755 index 000000000000..3272e839f8f6 --- /dev/null +++ b/drivers/power/bm_adsp_ulog/Kbuild @@ -0,0 +1,6 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += bm_adsp_ulog.o + diff --git a/drivers/power/bm_adsp_ulog/Makefile b/drivers/power/bm_adsp_ulog/Makefile new file mode 100755 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/power/bm_adsp_ulog/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/power/bm_adsp_ulog/README b/drivers/power/bm_adsp_ulog/README new file mode 100644 index 000000000000..e44395f1d9eb --- /dev/null +++ b/drivers/power/bm_adsp_ulog/README @@ -0,0 +1,39 @@ +Motorola Mobility. Battery Manager ULOG Specific Bindings + +Why we need adsp battery manager log support in AP side? +1. Only diag_mdlog in bug2go can help to collect ADSP battery manager log. +2. ADSP battery manager mdlog is not enabled by default in Bug2Go configuration + and we have to enable it manually. +3. Diag mdlog has to be pulled out from phone and use QXDM tool to parse the log + even if the ADSP battery manger log is enabled in diag log configuration of bug2go. +4. Factory and user version will never use Bug2Go, so diag_mdlog may not be available. + +What the new DLKM bm_adsp_ulog.ko benefits us? +1. Provide debugfs interface to collect battery manager ADSP log in AP side. +2. Provide debugfs interfaces to configure log level and category for log mask. +3. Provide exported APIs for other Moto battery charger DLKM to get battery charger + ADSP log and output to Kernel log or IPC logging. + +Module Name: bm_adsp_ulog.ko +Log Tag: BM_ULOG +IPC Log: /d/ipc_logging/bm_ulog/log +Support Platform: SM8350/SM7325/SM8450 + +devicetree properties description: +- categories + Usage: optional + Value Type: + Definition: 64 bits ulog mask only to capture the bm ulog type in the mask. + +- level + Usage: optional + Value Type: + Definition: BM ulog level to limit the bm ulog + +Example: +bm_adsp_ulog: bm_adsp_ulog { + compatible = "qcom,bm-adsp-ulog"; + categories = <0xFFBFFFFF>; + level = <3>; +}; + diff --git a/drivers/power/bm_adsp_ulog/bm_adsp_ulog.c b/drivers/power/bm_adsp_ulog/bm_adsp_ulog.c new file mode 100644 index 000000000000..5de30f6ee238 --- /dev/null +++ b/drivers/power/bm_adsp_ulog/bm_adsp_ulog.c @@ -0,0 +1,670 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#define pr_fmt(fmt) "BM_ULOG: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* owner/type/opcodes for battery charger */ +#define MSG_OWNER_BC 32778 +#define MSG_TYPE_REQ_RESP 1 + +#define BM_ULOG_GET 0x18 +#define BM_ULOG_PROP_SET 0x19 +#define BM_INIT_ULOG_GET 0x23 + +/* Generic definitions */ +#define BM_ULOG_WAIT_TIME_MS 5000 +#define MAX_ULOG_READ_BUFFER_SIZE 8192 +#define BM_ULOG_PAGES (50) + +#define bm_info(bmdev, fmt, ...) \ + do { \ + pr_info(fmt, ##__VA_ARGS__); \ + ipc_log_string(bmdev->ipc_log, fmt, ##__VA_ARGS__); \ + } while (0) + +#define bm_dbg(bmdev, fmt, ...) \ + do { \ + if (bmdev->debug_enabled && *bmdev->debug_enabled) \ + pr_info(fmt, ##__VA_ARGS__); \ + else \ + pr_debug(fmt, ##__VA_ARGS__); \ + ipc_log_string(bmdev->ipc_log, fmt, ##__VA_ARGS__); \ + } while (0) + +static bool debug_enabled; +module_param(debug_enabled, bool, 0600); +MODULE_PARM_DESC(debug_enabled, "Enable ulog to Kernel debug log"); + +struct bm_ulog_prop_req { + struct pmic_glink_hdr hdr; + u64 categories; + u32 level; +}; + +struct bm_ulog_req { + struct pmic_glink_hdr hdr; + u32 max_logsize; +}; + +struct bm_ulog_resp { + struct pmic_glink_hdr hdr; + char read_buffer[MAX_ULOG_READ_BUFFER_SIZE]; +}; + +struct bm_ulog_dev { + struct device *dev; + struct pmic_glink_client *client; + struct mutex lock; + struct completion ack; + u32 level; + u64 categories; + struct dentry *debugfs_dir; + bool *debug_enabled; + void *ipc_log; + struct task_struct *bm_ulog_task; + char ulog_buffer[MAX_ULOG_READ_BUFFER_SIZE]; +}; + +static struct bm_ulog_dev *g_bmdev = NULL; + +static int bm_ulog_write(struct bm_ulog_dev *bmdev, void *data, size_t len) +{ + int rc; + + mutex_lock(&bmdev->lock); + reinit_completion(&bmdev->ack); + rc = pmic_glink_write(bmdev->client, data, len); + if (!rc) { + rc = wait_for_completion_timeout(&bmdev->ack, + msecs_to_jiffies(BM_ULOG_WAIT_TIME_MS)); + if (!rc) { + pr_err("Error, timed out sending message\n"); + mutex_unlock(&bmdev->lock); + return -ETIMEDOUT; + } + rc = 0; + } + mutex_unlock(&bmdev->lock); + + return rc; +} + +static int bm_ulog_callback(void *priv, void *data, size_t len) +{ + struct pmic_glink_hdr *hdr = data; + struct bm_ulog_dev *bmdev = priv; + + pr_debug("owner: %u type: %u opcode: %#x len: %zu\n", hdr->owner, + hdr->type, hdr->opcode, len); + + switch (hdr->opcode) { + case BM_ULOG_PROP_SET: + complete(&bmdev->ack); + break; + case BM_ULOG_GET: + case BM_INIT_ULOG_GET: + if (len != sizeof(struct bm_ulog_resp)) { + pr_err("Incorrect len %zu for bm ulog resp\n", len); + break; + } + memcpy(bmdev->ulog_buffer, + ((struct bm_ulog_resp *)data)->read_buffer, + MAX_ULOG_READ_BUFFER_SIZE); + complete(&bmdev->ack); + break; + default: + pr_err("Unknown opcode %u\n", hdr->opcode); + break; + } + + return 0; +} + +static int bm_ulog_set_mask(struct bm_ulog_dev *bmdev, + enum bm_ulog_category_bitmap categories, + enum bm_ulog_level_type level) +{ + int rc; + struct bm_ulog_prop_req prop_req = { { 0 } }; + + if (level > BM_LOG_LEVEL_ALL_LOGS) + level = BM_LOG_LEVEL_ALL_LOGS; + + prop_req.level = level; + prop_req.categories = categories; + prop_req.hdr.owner = MSG_OWNER_BC; + prop_req.hdr.type = MSG_TYPE_REQ_RESP; + prop_req.hdr.opcode = BM_ULOG_PROP_SET; + + rc = bm_ulog_write(bmdev, &prop_req, sizeof(prop_req)); + if (rc) { + pr_err("BM ulog set mask failed\n"); + return rc; + } + + bmdev->level = level; + bmdev->categories = categories; + pr_debug("BM ulog categories: 0x%llx, level: %d\n", + (u64)categories, level); + + return 0; +} + +static int bm_ulog_request_log(struct bm_ulog_dev *bmdev, u32 size) +{ + int rc; + u32 max_logsize; + struct bm_ulog_req ulog_req = { { 0 } }; + + if (size > 0 && size < MAX_ULOG_READ_BUFFER_SIZE) + max_logsize = size; + else + max_logsize = MAX_ULOG_READ_BUFFER_SIZE; + + ulog_req.hdr.owner = MSG_OWNER_BC; + ulog_req.hdr.type = MSG_TYPE_REQ_RESP; + ulog_req.hdr.opcode = BM_ULOG_GET; + ulog_req.max_logsize = max_logsize; + + rc = bm_ulog_write(bmdev, &ulog_req, sizeof(ulog_req)); + + return rc; +} + +static int bm_ulog_request_init_log(struct bm_ulog_dev *bmdev, u32 size) +{ + int rc; + u32 max_logsize; + struct bm_ulog_req ulog_req = { { 0 } }; + + if (size > 0 && size < MAX_ULOG_READ_BUFFER_SIZE) + max_logsize = size; + else + max_logsize = MAX_ULOG_READ_BUFFER_SIZE; + + ulog_req.hdr.owner = MSG_OWNER_BC; + ulog_req.hdr.type = MSG_TYPE_REQ_RESP; + ulog_req.hdr.opcode = BM_INIT_ULOG_GET; + ulog_req.max_logsize = max_logsize; + + rc = bm_ulog_write(bmdev, &ulog_req, sizeof(ulog_req)); + + return rc; +} + +int bm_ulog_get_log(char *buf, u32 size) +{ + int rc; + struct bm_ulog_dev *bmdev = g_bmdev; + + if (!bmdev) { + pr_err("BM ulog has not initialized yet\n"); + return -ENODEV; + } + + if (!bmdev->debug_enabled) { + pr_err("BM ulog debug_enabled invalid\n"); + return -ENODEV; + } + + if (*bmdev->debug_enabled == false) { + pr_debug("BM ulog has not enabled yet\n"); + return -ENOTSUPP; + } + + if (!buf) { + pr_err("BM ulog invalid buf=%p\n", buf); + return -EINVAL; + } + + if (!size || size > MAX_ULOG_READ_BUFFER_SIZE) { + pr_err("BM ulog invalid size=%d\n", size); + return -EINVAL; + } + + rc = bm_ulog_request_log(bmdev, size); + if (rc) { + pr_err("BM ulog failed to request log, rc=%d\n", rc); + return rc; + } + + memcpy(buf, bmdev->ulog_buffer, size); + + return 0; +} +EXPORT_SYMBOL(bm_ulog_get_log); + +int bm_ulog_get_mask_log(enum bm_ulog_category_bitmap categories, + enum bm_ulog_level_type level, + char *buf, u32 size) +{ + int rc; + struct bm_ulog_dev *bmdev = g_bmdev; + + if (!bmdev) { + pr_err("BM ulog has not initialized yet\n"); + return -ENODEV; + } + + if (!bmdev->debug_enabled) { + pr_err("BM ulog debug_enabled invalid\n"); + return -ENODEV; + } + + if (*bmdev->debug_enabled == false) { + pr_debug("BM ulog has not enabled yet\n"); + return -ENOTSUPP; + } + + if (!buf) { + pr_err("BM ulog invalid buf=%p\n", buf); + return -EINVAL; + } + + if (!size || size > MAX_ULOG_READ_BUFFER_SIZE) { + pr_err("BM ulog invalid size=%d\n", size); + return -EINVAL; + } + + rc = bm_ulog_set_mask(bmdev, categories, level); + if (rc) { + pr_err("BM ulog failed to set mask, rc=%d\n", rc); + return rc; + } + + rc = bm_ulog_request_log(bmdev, size); + if (rc) { + pr_err("BM ulog failed to request log, rc=%d\n", rc); + return rc; + } + + memcpy(buf, bmdev->ulog_buffer, size); + + return 0; +} +EXPORT_SYMBOL(bm_ulog_get_mask_log); + +static int bm_ulog_print_buffer(struct bm_ulog_dev *bmdev, u32 size) +{ + int i; + int header = 0; + + for (i = 0; i < size; i++) { + if (bmdev->ulog_buffer[i] == '\x0a') { + bmdev->ulog_buffer[i] = '\0'; + bm_dbg(bmdev, "%s\n", &bmdev->ulog_buffer[header]); + header = i + 1; + } else if (bmdev->ulog_buffer[i] == '\0') { + if (header < i) { + bm_dbg(bmdev, "%s\n", &bmdev->ulog_buffer[header]); + } + break; + } + } + if (i > 0) + pr_info("recv len=%d\n", i); + return i; +} + +static int bm_ulog_print_init_log(u32 size) +{ + int rc; + struct bm_ulog_dev *bmdev = g_bmdev; + + if (!bmdev) { + pr_err("BM ulog has not initialized yet\n"); + return -ENODEV; + } + + if (!bmdev->debug_enabled) { + pr_err("BM ulog debug_enabled invalid\n"); + return -ENODEV; + } + + if (!size || size > MAX_ULOG_READ_BUFFER_SIZE) { + pr_err("BM ulog invalid size=%d\n", size); + return -EINVAL; + } + + rc = bm_ulog_request_init_log(bmdev, size); + if (rc) { + pr_err("BM ulog failed to request log, rc=%d\n", rc); + return rc; + } + + bm_ulog_print_buffer(bmdev, size); + + return 0; +} + +int bm_ulog_print_log(u32 size) +{ + int rc; + struct bm_ulog_dev *bmdev = g_bmdev; + + if (!bmdev) { + pr_err("BM ulog has not initialized yet\n"); + return -ENODEV; + } + + if (!bmdev->debug_enabled) { + pr_err("BM ulog debug_enabled invalid\n"); + return -ENODEV; + } + + if (*bmdev->debug_enabled == false) { + pr_debug("BM ulog has not enabled yet\n"); + return -ENOTSUPP; + } + + if (!size || size > MAX_ULOG_READ_BUFFER_SIZE) { + pr_err("BM ulog invalid size=%d\n", size); + return -EINVAL; + } + + rc = bm_ulog_request_log(bmdev, size); + if (rc) { + pr_err("BM ulog failed to request log, rc=%d\n", rc); + return rc; + } + + bm_ulog_print_buffer(bmdev, size); + + return 0; +} +EXPORT_SYMBOL(bm_ulog_print_log); + +int bm_ulog_print_mask_log(enum bm_ulog_category_bitmap categories, + enum bm_ulog_level_type level, u32 size) +{ + int rc; + struct bm_ulog_dev *bmdev = g_bmdev; + + if (!bmdev) { + pr_err("BM ulog has not initialized yet\n"); + return -ENODEV; + } + + if (!bmdev->debug_enabled) { + pr_err("BM ulog debug_enabled invalid\n"); + return -ENODEV; + } + + if (*bmdev->debug_enabled == false) { + pr_debug("BM ulog has not enabled yet\n"); + return -ENOTSUPP; + } + + if (!size || size > MAX_ULOG_READ_BUFFER_SIZE) { + pr_err("BM ulog invalid size=%d\n", size); + return -EINVAL; + } + + rc = bm_ulog_set_mask(bmdev, categories, level); + if (rc) { + pr_err("BM ulog failed to set mask, rc=%d\n", rc); + return rc; + } + + rc = bm_ulog_request_log(bmdev, size); + if (rc) { + pr_err("BM ulog failed to request log, rc=%d\n", rc); + return rc; + } + + bm_ulog_print_buffer(bmdev, size); + + return 0; +} +EXPORT_SYMBOL(bm_ulog_print_mask_log); + +#ifdef CONFIG_DEBUG_FS +static int bm_ulog_dump_show(struct seq_file *s, void *unused) +{ + int rc; + struct bm_ulog_dev *bmdev = s->private; + + rc = bm_ulog_set_mask(bmdev, bmdev->categories, bmdev->level); + if (rc) { + pr_err("BM ulog failed to set mask, rc=%d\n", rc); + return rc; + } + + rc = bm_ulog_request_log(bmdev, MAX_ULOG_READ_BUFFER_SIZE); + if (rc) { + pr_err("BM ulog failed to request log, rc=%d\n", rc); + return rc; + } + seq_puts(s, bmdev->ulog_buffer); + + return 0; +} + +static int bm_ulog_open(struct inode *inode, struct file *file) +{ + return single_open(file, bm_ulog_dump_show, inode->i_private); +} + +static const struct file_operations bm_ulog_fops = { + .open = bm_ulog_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void bm_ulog_add_debugfs(struct bm_ulog_dev *bmdev) +{ + int rc; + struct dentry *dir, *file; + + dir = debugfs_create_dir("bm_ulog", NULL); + if (IS_ERR(dir)) { + rc = PTR_ERR(dir); + pr_err("Failed to create bm ulog debugfs directory, rc=%d\n", + rc); + return; + } + + file = debugfs_create_file("dump", 0444, dir, bmdev, &bm_ulog_fops); + if (IS_ERR(file)) { + rc = PTR_ERR(file); + pr_err("Failed to create ulog dump debugfs file, rc=%d\n", + rc); + debugfs_remove_recursive(dir); + return; + } + + debugfs_create_x64("categories", 0664, dir, &bmdev->categories); + debugfs_create_x32("level", 0664, dir, &bmdev->level); + + bmdev->debugfs_dir = dir; +} +#else +static void bm_ulog_add_debugfs(struct bm_ulog_dev *bmdev) { } +#endif + +static int bm_ulog_kthread(void *param) +{ + struct bm_ulog_dev * bmdev = param; + int read_count = 0; + int sleep_ms = 0; + + bm_info(bmdev, "bm ulog kthread start\n"); + do { + bm_ulog_request_log(bmdev, MAX_ULOG_READ_BUFFER_SIZE); + read_count = bm_ulog_print_buffer(bmdev, MAX_ULOG_READ_BUFFER_SIZE); + + if (read_count > 1024) { + sleep_ms = 1; + } else if (read_count > 128) { + sleep_ms = 50; + } else if (read_count == 0) { + sleep_ms = sleep_ms + 50; + } + if (sleep_ms > 200) + sleep_ms = 200; + + msleep(sleep_ms); + } while(!kthread_should_stop()); + + bm_info(bmdev, "bm ulog kthread exit\n"); + return 0; +} + +bool bm_ulog_is_bm_ulog_enabled(struct bm_ulog_dev *bmdev) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool rt = false; + const char *bootargs = NULL; + char *bm_ulog_enabled = NULL; + + if (bmdev && bmdev->debug_enabled && *bmdev->debug_enabled) { + bm_info(bmdev, "bmdev->bm_ulog_enabled is true\n"); + return true; + } + + if (!np) { + bm_info(bmdev, "np is null\n"); + return false; + } + + if (!of_property_read_string(np, "bootargs", &bootargs)) { + bm_ulog_enabled = strstr(bootargs, "bm_ulog_enabled=1 "); + bm_info(bmdev, "of_property_read_string bm_ulog_enabled=%s\n", bm_ulog_enabled); + if (bm_ulog_enabled) { + rt = true; + } + } + + of_node_put(np); + + bm_info(bmdev,"bm ulog rt = %d\n", rt); + return rt; +} + +static int bm_ulog_probe(struct platform_device *pdev) +{ + int rc; + struct bm_ulog_dev *bmdev; + struct pmic_glink_client_data client_data = { }; + struct device_node *node = pdev->dev.of_node; + bool init_log_enabled, init_debug_enabled; + + bmdev = devm_kzalloc(&pdev->dev, sizeof(*bmdev), GFP_KERNEL); + if (!bmdev) + return -ENOMEM; + + rc = of_property_read_u64(node, "categories", &bmdev->categories); + if (rc) + bmdev->categories = BM_ALL; + rc = of_property_read_u32(node, "level", &bmdev->level); + if (rc) + bmdev->level = BM_LOG_LEVEL_INFO; + + init_log_enabled = of_property_read_bool(node, "init-log-enabled"); + + bmdev->dev = &pdev->dev; + client_data.id = MSG_OWNER_BC; + client_data.name = "battery_manager_adsp_ulog"; + client_data.msg_cb = bm_ulog_callback; + client_data.priv = bmdev; + + bmdev->client = pmic_glink_register_client(bmdev->dev, &client_data); + if (IS_ERR(bmdev->client)) { + rc = PTR_ERR(bmdev->client); + if (rc != -EPROBE_DEFER) + dev_err(bmdev->dev, "Error in registering with pmic_glink %d\n", + rc); + return rc; + } + + mutex_init(&bmdev->lock); + init_completion(&bmdev->ack); + platform_set_drvdata(pdev, bmdev); + bmdev->debug_enabled = &debug_enabled; + g_bmdev = bmdev; + bmdev->ipc_log = ipc_log_context_create(BM_ULOG_PAGES, "bm_ulog", 0); + if (!bmdev->ipc_log) + dev_err(bmdev->dev, "Failed to create ipc log\n"); + + if (init_log_enabled) { + init_debug_enabled = debug_enabled; + debug_enabled = init_log_enabled; + bm_ulog_print_log(MAX_ULOG_READ_BUFFER_SIZE); + bm_ulog_print_init_log(MAX_ULOG_READ_BUFFER_SIZE); + bm_ulog_print_mask_log(bmdev->categories, bmdev->level, + MAX_ULOG_READ_BUFFER_SIZE); + debug_enabled = init_debug_enabled; + } + + debug_enabled = bm_ulog_is_bm_ulog_enabled(bmdev); + bm_info(bmdev, "bm_ulog_check_debug_enabled debug_enabled=%d\n", debug_enabled); + if (bmdev->debug_enabled && *bmdev->debug_enabled) { + bmdev->bm_ulog_task = kthread_create(bm_ulog_kthread, bmdev, "bm_ulog_kthread"); + if (IS_ERR(bmdev->bm_ulog_task)) { + bm_info(bmdev, "Failed to create bm_ulog_task ret = %ld\n", PTR_ERR(bmdev->bm_ulog_task)); + }else { + wake_up_process(bmdev->bm_ulog_task); + bm_info(bmdev, "Successed to create bm_ulog_task\n"); + } + } + + bm_ulog_add_debugfs(bmdev); + + bm_info(bmdev, "BM adsp ulog driver initialized successfully\n"); + + return 0; +} + +static int bm_ulog_remove(struct platform_device *pdev) +{ + struct bm_ulog_dev *bmdev = platform_get_drvdata(pdev); + int rc; + + ipc_log_context_destroy(bmdev->ipc_log); + debugfs_remove_recursive(bmdev->debugfs_dir); + rc = pmic_glink_unregister_client(bmdev->client); + if (rc < 0) { + pr_err("Error unregistering from pmic_glink, rc=%d\n", rc); + return rc; + } + g_bmdev = NULL; + + return 0; +} + +static const struct of_device_id bm_ulog_match_table[] = { + { .compatible = "qcom,bm-adsp-ulog" }, + {}, +}; + +static struct platform_driver bm_ulog_driver = { + .driver = { + .name = "bm_adsp_ulog", + .of_match_table = bm_ulog_match_table, + }, + .probe = bm_ulog_probe, + .remove = bm_ulog_remove, +}; +module_platform_driver(bm_ulog_driver); + +MODULE_DESCRIPTION("QTI Glink battery manager adsp ulog driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/bq25890_mmi/Android.mk b/drivers/power/bq25890_mmi/Android.mk new file mode 100755 index 000000000000..646034a70299 --- /dev/null +++ b/drivers/power/bq25890_mmi/Android.mk @@ -0,0 +1,11 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := bq25890_charger_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/drivers/power/bq25890_mmi/Kbuild b/drivers/power/bq25890_mmi/Kbuild new file mode 100755 index 000000000000..84dd5faffa8b --- /dev/null +++ b/drivers/power/bq25890_mmi/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += bq25890_charger_mmi.o diff --git a/drivers/power/bq25890_mmi/bq25890_charger_mmi.c b/drivers/power/bq25890_mmi/bq25890_charger_mmi.c new file mode 100755 index 000000000000..0b913715790f --- /dev/null +++ b/drivers/power/bq25890_mmi/bq25890_charger_mmi.c @@ -0,0 +1,1093 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * TI BQ25890 charger driver + * + * Copyright (C) 2015 Intel Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define BQ25890_MANUFACTURER "Texas Instruments" +#define BQ25890_IRQ_PIN "bq25890_irq" + +#define BQ25890_ID 3 +#define BQ25895_ID 7 +#define BQ25896_ID 0 + +enum bq25890_fields { + F_EN_HIZ, F_EN_ILIM, F_IILIM, /* Reg00 */ + F_BHOT, F_BCOLD, F_VINDPM_OFS, /* Reg01 */ + F_CONV_START, F_CONV_RATE, F_BOOSTF, F_ICO_EN, + F_HVDCP_EN, F_MAXC_EN, F_FORCE_DPM, F_AUTO_DPDM_EN, /* Reg02 */ + F_BAT_LOAD_EN, F_WD_RST, F_OTG_CFG, F_CHG_CFG, F_SYSVMIN, /* Reg03 */ + F_PUMPX_EN, F_ICHG, /* Reg04 */ + F_IPRECHG, F_ITERM, /* Reg05 */ + F_VREG, F_BATLOWV, F_VRECHG, /* Reg06 */ + F_TERM_EN, F_STAT_DIS, F_WD, F_TMR_EN, F_CHG_TMR, + F_JEITA_ISET, /* Reg07 */ + F_BATCMP, F_VCLAMP, F_TREG, /* Reg08 */ + F_FORCE_ICO, F_TMR2X_EN, F_BATFET_DIS, F_JEITA_VSET, + F_BATFET_DLY, F_BATFET_RST_EN, F_PUMPX_UP, F_PUMPX_DN, /* Reg09 */ + F_BOOSTV, F_BOOSTI, /* Reg0A */ + F_VBUS_STAT, F_CHG_STAT, F_PG_STAT, F_SDP_STAT, F_VSYS_STAT, /* Reg0B */ + F_WD_FAULT, F_BOOST_FAULT, F_CHG_FAULT, F_BAT_FAULT, + F_NTC_FAULT, /* Reg0C */ + F_FORCE_VINDPM, F_VINDPM, /* Reg0D */ + F_THERM_STAT, F_BATV, /* Reg0E */ + F_SYSV, /* Reg0F */ + F_TSPCT, /* Reg10 */ + F_VBUS_GD, F_VBUSV, /* Reg11 */ + F_ICHGR, /* Reg12 */ + F_VDPM_STAT, F_IDPM_STAT, F_IDPM_LIM, /* Reg13 */ + F_REG_RST, F_ICO_OPTIMIZED, F_PN, F_TS_PROFILE, F_DEV_REV, /* Reg14 */ + + F_MAX_FIELDS +}; + +/* initial field values, converted to register values */ +struct bq25890_init_data { + u8 ichg; /* charge current */ + u8 vreg; /* regulation voltage */ + u8 iterm; /* termination current */ + u8 iprechg; /* precharge current */ + u8 sysvmin; /* minimum system voltage limit */ + u8 boostv; /* boost regulation voltage */ + u8 boosti; /* boost current limit */ + u8 boostf; /* boost frequency */ + u8 ilim_en; /* enable ILIM pin */ + u8 treg; /* thermal regulation threshold */ +}; + +struct bq25890_state { + u8 online; + u8 chrg_status; + u8 chrg_fault; + u8 vsys_status; + u8 boost_fault; + u8 bat_fault; +}; + +struct bq25890_device { + struct i2c_client *client; + struct device *dev; + struct power_supply *charger; + + struct usb_phy *usb_phy; + struct notifier_block usb_nb; + struct work_struct usb_work; + unsigned long usb_event; + + struct regmap *rmap; + struct regmap_field *rmap_fields[F_MAX_FIELDS]; + + int chip_id; + struct bq25890_init_data init_data; + struct bq25890_state state; + + struct mutex lock; /* protect state data */ +}; + +static const struct regmap_range bq25890_readonly_reg_ranges[] = { + regmap_reg_range(0x0b, 0x0c), + regmap_reg_range(0x0e, 0x13), +}; + +static const struct regmap_access_table bq25890_writeable_regs = { + .no_ranges = bq25890_readonly_reg_ranges, + .n_no_ranges = ARRAY_SIZE(bq25890_readonly_reg_ranges), +}; + +static const struct regmap_range bq25890_volatile_reg_ranges[] = { + regmap_reg_range(0x00, 0x00), + regmap_reg_range(0x09, 0x09), + regmap_reg_range(0x0b, 0x0c), + regmap_reg_range(0x0e, 0x14), +}; + +static const struct regmap_access_table bq25890_volatile_regs = { + .yes_ranges = bq25890_volatile_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(bq25890_volatile_reg_ranges), +}; + +static const struct regmap_config bq25890_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = 0x14, + .cache_type = REGCACHE_RBTREE, + + .wr_table = &bq25890_writeable_regs, + .volatile_table = &bq25890_volatile_regs, +}; + +static const struct reg_field bq25890_reg_fields[] = { + /* REG00 */ + [F_EN_HIZ] = REG_FIELD(0x00, 7, 7), + [F_EN_ILIM] = REG_FIELD(0x00, 6, 6), + [F_IILIM] = REG_FIELD(0x00, 0, 5), + /* REG01 */ + [F_BHOT] = REG_FIELD(0x01, 6, 7), + [F_BCOLD] = REG_FIELD(0x01, 5, 5), + [F_VINDPM_OFS] = REG_FIELD(0x01, 0, 4), + /* REG02 */ + [F_CONV_START] = REG_FIELD(0x02, 7, 7), + [F_CONV_RATE] = REG_FIELD(0x02, 6, 6), + [F_BOOSTF] = REG_FIELD(0x02, 5, 5), + [F_ICO_EN] = REG_FIELD(0x02, 4, 4), + [F_HVDCP_EN] = REG_FIELD(0x02, 3, 3), // reserved on BQ25896 + [F_MAXC_EN] = REG_FIELD(0x02, 2, 2), // reserved on BQ25896 + [F_FORCE_DPM] = REG_FIELD(0x02, 1, 1), + [F_AUTO_DPDM_EN] = REG_FIELD(0x02, 0, 0), + /* REG03 */ + [F_BAT_LOAD_EN] = REG_FIELD(0x03, 7, 7), + [F_WD_RST] = REG_FIELD(0x03, 6, 6), + [F_OTG_CFG] = REG_FIELD(0x03, 5, 5), + [F_CHG_CFG] = REG_FIELD(0x03, 4, 4), + [F_SYSVMIN] = REG_FIELD(0x03, 1, 3), + /* MIN_VBAT_SEL on BQ25896 */ + /* REG04 */ + [F_PUMPX_EN] = REG_FIELD(0x04, 7, 7), + [F_ICHG] = REG_FIELD(0x04, 0, 6), + /* REG05 */ + [F_IPRECHG] = REG_FIELD(0x05, 4, 7), + [F_ITERM] = REG_FIELD(0x05, 0, 3), + /* REG06 */ + [F_VREG] = REG_FIELD(0x06, 2, 7), + [F_BATLOWV] = REG_FIELD(0x06, 1, 1), + [F_VRECHG] = REG_FIELD(0x06, 0, 0), + /* REG07 */ + [F_TERM_EN] = REG_FIELD(0x07, 7, 7), + [F_STAT_DIS] = REG_FIELD(0x07, 6, 6), + [F_WD] = REG_FIELD(0x07, 4, 5), + [F_TMR_EN] = REG_FIELD(0x07, 3, 3), + [F_CHG_TMR] = REG_FIELD(0x07, 1, 2), + [F_JEITA_ISET] = REG_FIELD(0x07, 0, 0), // reserved on BQ25895 + /* REG08 */ + [F_BATCMP] = REG_FIELD(0x08, 5, 7), + [F_VCLAMP] = REG_FIELD(0x08, 2, 4), + [F_TREG] = REG_FIELD(0x08, 0, 1), + /* REG09 */ + [F_FORCE_ICO] = REG_FIELD(0x09, 7, 7), + [F_TMR2X_EN] = REG_FIELD(0x09, 6, 6), + [F_BATFET_DIS] = REG_FIELD(0x09, 5, 5), + [F_JEITA_VSET] = REG_FIELD(0x09, 4, 4), // reserved on BQ25895 + [F_BATFET_DLY] = REG_FIELD(0x09, 3, 3), + [F_BATFET_RST_EN] = REG_FIELD(0x09, 2, 2), + [F_PUMPX_UP] = REG_FIELD(0x09, 1, 1), + [F_PUMPX_DN] = REG_FIELD(0x09, 0, 0), + /* REG0A */ + [F_BOOSTV] = REG_FIELD(0x0A, 4, 7), + /* PFM_OTG_DIS 3 on BQ25896 */ + [F_BOOSTI] = REG_FIELD(0x0A, 0, 2), // reserved on BQ25895 + /* REG0B */ + [F_VBUS_STAT] = REG_FIELD(0x0B, 5, 7), + [F_CHG_STAT] = REG_FIELD(0x0B, 3, 4), + [F_PG_STAT] = REG_FIELD(0x0B, 2, 2), + [F_SDP_STAT] = REG_FIELD(0x0B, 1, 1), // reserved on BQ25896 + [F_VSYS_STAT] = REG_FIELD(0x0B, 0, 0), + /* REG0C */ + [F_WD_FAULT] = REG_FIELD(0x0C, 7, 7), + [F_BOOST_FAULT] = REG_FIELD(0x0C, 6, 6), + [F_CHG_FAULT] = REG_FIELD(0x0C, 4, 5), + [F_BAT_FAULT] = REG_FIELD(0x0C, 3, 3), + [F_NTC_FAULT] = REG_FIELD(0x0C, 0, 2), + /* REG0D */ + [F_FORCE_VINDPM] = REG_FIELD(0x0D, 7, 7), + [F_VINDPM] = REG_FIELD(0x0D, 0, 6), + /* REG0E */ + [F_THERM_STAT] = REG_FIELD(0x0E, 7, 7), + [F_BATV] = REG_FIELD(0x0E, 0, 6), + /* REG0F */ + [F_SYSV] = REG_FIELD(0x0F, 0, 6), + /* REG10 */ + [F_TSPCT] = REG_FIELD(0x10, 0, 6), + /* REG11 */ + [F_VBUS_GD] = REG_FIELD(0x11, 7, 7), + [F_VBUSV] = REG_FIELD(0x11, 0, 6), + /* REG12 */ + [F_ICHGR] = REG_FIELD(0x12, 0, 6), + /* REG13 */ + [F_VDPM_STAT] = REG_FIELD(0x13, 7, 7), + [F_IDPM_STAT] = REG_FIELD(0x13, 6, 6), + [F_IDPM_LIM] = REG_FIELD(0x13, 0, 5), + /* REG14 */ + [F_REG_RST] = REG_FIELD(0x14, 7, 7), + [F_ICO_OPTIMIZED] = REG_FIELD(0x14, 6, 6), + [F_PN] = REG_FIELD(0x14, 3, 5), + [F_TS_PROFILE] = REG_FIELD(0x14, 2, 2), + [F_DEV_REV] = REG_FIELD(0x14, 0, 1) +}; + +/* + * Most of the val -> idx conversions can be computed, given the minimum, + * maximum and the step between values. For the rest of conversions, we use + * lookup tables. + */ +enum bq25890_table_ids { + /* range tables */ + TBL_ICHG, + TBL_ITERM, + TBL_VREG, + TBL_BOOSTV, + TBL_SYSVMIN, + + /* lookup tables */ + TBL_TREG, + TBL_BOOSTI, +}; + +/* Thermal Regulation Threshold lookup table, in degrees Celsius */ +static const u32 bq25890_treg_tbl[] = { 60, 80, 100, 120 }; + +#define BQ25890_TREG_TBL_SIZE ARRAY_SIZE(bq25890_treg_tbl) + +/* Boost mode current limit lookup table, in uA */ +static const u32 bq25890_boosti_tbl[] = { + 500000, 700000, 1100000, 1300000, 1600000, 1800000, 2100000, 2400000 +}; + +#define BQ25890_BOOSTI_TBL_SIZE ARRAY_SIZE(bq25890_boosti_tbl) + +struct bq25890_range { + u32 min; + u32 max; + u32 step; +}; + +struct bq25890_lookup { + const u32 *tbl; + u32 size; +}; + +static const union { + struct bq25890_range rt; + struct bq25890_lookup lt; +} bq25890_tables[] = { + /* range tables */ + [TBL_ICHG] = { .rt = {0, 5056000, 64000} }, /* uA */ + [TBL_ITERM] = { .rt = {64000, 1024000, 64000} }, /* uA */ + [TBL_VREG] = { .rt = {3840000, 4608000, 16000} }, /* uV */ + [TBL_BOOSTV] = { .rt = {4550000, 5510000, 64000} }, /* uV */ + [TBL_SYSVMIN] = { .rt = {3000000, 3700000, 100000} }, /* uV */ + + /* lookup tables */ + [TBL_TREG] = { .lt = {bq25890_treg_tbl, BQ25890_TREG_TBL_SIZE} }, + [TBL_BOOSTI] = { .lt = {bq25890_boosti_tbl, BQ25890_BOOSTI_TBL_SIZE} } +}; + +static int bq25890_field_read(struct bq25890_device *bq, + enum bq25890_fields field_id) +{ + int ret; + int val; + + ret = regmap_field_read(bq->rmap_fields[field_id], &val); + if (ret < 0) + return ret; + + return val; +} + +static int bq25890_field_write(struct bq25890_device *bq, + enum bq25890_fields field_id, u8 val) +{ + return regmap_field_write(bq->rmap_fields[field_id], val); +} + +static u8 bq25890_find_idx(u32 value, enum bq25890_table_ids id) +{ + u8 idx; + + if (id >= TBL_TREG) { + const u32 *tbl = bq25890_tables[id].lt.tbl; + u32 tbl_size = bq25890_tables[id].lt.size; + + for (idx = 1; idx < tbl_size && tbl[idx] <= value; idx++) + ; + } else { + const struct bq25890_range *rtbl = &bq25890_tables[id].rt; + u8 rtbl_size; + + rtbl_size = (rtbl->max - rtbl->min) / rtbl->step + 1; + + for (idx = 1; + idx < rtbl_size && (idx * rtbl->step + rtbl->min <= value); + idx++) + ; + } + + return idx - 1; +} + +static u32 bq25890_find_val(u8 idx, enum bq25890_table_ids id) +{ + const struct bq25890_range *rtbl; + + /* lookup table? */ + if (id >= TBL_TREG) + return bq25890_tables[id].lt.tbl[idx]; + + /* range table */ + rtbl = &bq25890_tables[id].rt; + + return (rtbl->min + idx * rtbl->step); +} + +enum bq25890_status { + STATUS_NOT_CHARGING, + STATUS_PRE_CHARGING, + STATUS_FAST_CHARGING, + STATUS_TERMINATION_DONE, +}; + +enum bq25890_chrg_fault { + CHRG_FAULT_NORMAL, + CHRG_FAULT_INPUT, + CHRG_FAULT_THERMAL_SHUTDOWN, + CHRG_FAULT_TIMER_EXPIRED, +}; + +static int bq25890_power_supply_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret; + struct bq25890_device *bq = power_supply_get_drvdata(psy); + struct bq25890_state state; + + mutex_lock(&bq->lock); + state = bq->state; + mutex_unlock(&bq->lock); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (!state.online) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else if (state.chrg_status == STATUS_NOT_CHARGING) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + else if (state.chrg_status == STATUS_PRE_CHARGING || + state.chrg_status == STATUS_FAST_CHARGING) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (state.chrg_status == STATUS_TERMINATION_DONE) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + + break; + + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = BQ25890_MANUFACTURER; + break; + + case POWER_SUPPLY_PROP_MODEL_NAME: + if (bq->chip_id == BQ25890_ID) + val->strval = "BQ25890"; + else if (bq->chip_id == BQ25895_ID) + val->strval = "BQ25895"; + else if (bq->chip_id == BQ25896_ID) + val->strval = "BQ25896"; + else + val->strval = "UNKNOWN"; + + break; + + case POWER_SUPPLY_PROP_ONLINE: + if (state.online && bq25890_field_read(bq, F_CHG_CFG)) + val->intval = 1; + else + val->intval = 0; + break; + + case POWER_SUPPLY_PROP_HEALTH: + if (!state.chrg_fault && !state.bat_fault && !state.boost_fault) + val->intval = POWER_SUPPLY_HEALTH_GOOD; + else if (state.bat_fault) + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + else if (state.chrg_fault == CHRG_FAULT_TIMER_EXPIRED) + val->intval = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; + else if (state.chrg_fault == CHRG_FAULT_THERMAL_SHUTDOWN) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + else + val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = bq25890_field_read(bq, F_ICHGR); /* read measured value */ + if (ret < 0) + return ret; + + /* converted_val = ADC_val * 50mA (table 10.3.19) */ + val->intval = ret * 50000; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + ret = bq25890_field_read(bq, F_ICHG); /* read measured value */ + if (ret < 0) + return ret; + val->intval = bq25890_find_val(ret, TBL_ICHG); + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + if (!state.online) { + val->intval = 0; + break; + } + + ret = bq25890_field_read(bq, F_BATV); /* read measured value */ + if (ret < 0) + return ret; + + /* converted_val = 2.304V + ADC_val * 20mV (table 10.3.15) */ + val->intval = 2304000 + ret * 20000; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + ret = bq25890_field_read(bq, F_VREG); /* read measured value */ + if (ret < 0) + return ret; + val->intval = bq25890_find_val(ret, TBL_VREG); + break; + + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + val->intval = bq25890_find_val(bq->init_data.iterm, TBL_ITERM); + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = bq25890_field_read(bq, F_SYSV); /* read measured value */ + if (ret < 0) + return ret; + + /* converted_val = 2.304V + ADC_val * 20mV (table 10.3.15) */ + val->intval = 2304000 + ret * 20000; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int bq25890_power_supply_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct bq25890_device *bq = power_supply_get_drvdata(psy); + u8 conv_data; + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + conv_data = bq25890_find_idx(val->intval, TBL_VREG); + ret = bq25890_field_write(bq, F_VREG, conv_data); + if (ret < 0) { + dev_dbg(bq->dev, "Failed to write F_VREG %d\n", ret); + } else { + dev_dbg(bq->dev, "Set F_VREG to %d (%d)\n", + bq25890_find_val(conv_data, TBL_VREG), conv_data); + } + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + conv_data = bq25890_find_idx(val->intval, TBL_ICHG); + ret = bq25890_field_write(bq, F_ICHG, conv_data); + if (ret < 0) { + dev_dbg(bq->dev, "Failed to write F_ICHG %d\n", ret); + } else { + dev_dbg(bq->dev, "Set F_ICHG to %d (%d)\n", + bq25890_find_val(conv_data, TBL_ICHG), conv_data); + } + break; + + case POWER_SUPPLY_PROP_ONLINE: + ret = bq25890_field_write(bq, F_CHG_CFG, !!val->intval); + if (ret < 0) + dev_dbg(bq->dev, "Failed to write F_CHG_CFG %d\n", ret); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int bq25890_power_supply_prop_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + case POWER_SUPPLY_PROP_ONLINE: + ret = 1; + break; + default: + ret = 0; + break; + } + return ret; +} + +static int bq25890_get_chip_state(struct bq25890_device *bq, + struct bq25890_state *state) +{ + int i, ret; + + struct { + enum bq25890_fields id; + u8 *data; + } state_fields[] = { + {F_CHG_STAT, &state->chrg_status}, + {F_PG_STAT, &state->online}, + {F_VSYS_STAT, &state->vsys_status}, + {F_BOOST_FAULT, &state->boost_fault}, + {F_BAT_FAULT, &state->bat_fault}, + {F_CHG_FAULT, &state->chrg_fault} + }; + + for (i = 0; i < ARRAY_SIZE(state_fields); i++) { + ret = bq25890_field_read(bq, state_fields[i].id); + if (ret < 0) + return ret; + + *state_fields[i].data = ret; + } + + dev_dbg(bq->dev, "S:CHG/PG/VSYS=%d/%d/%d, F:CHG/BOOST/BAT=%d/%d/%d\n", + state->chrg_status, state->online, state->vsys_status, + state->chrg_fault, state->boost_fault, state->bat_fault); + + return 0; +} + +static bool bq25890_state_changed(struct bq25890_device *bq, + struct bq25890_state *new_state) +{ + struct bq25890_state old_state; + + mutex_lock(&bq->lock); + old_state = bq->state; + mutex_unlock(&bq->lock); + + return (old_state.chrg_status != new_state->chrg_status || + old_state.chrg_fault != new_state->chrg_fault || + old_state.online != new_state->online || + old_state.bat_fault != new_state->bat_fault || + old_state.boost_fault != new_state->boost_fault || + old_state.vsys_status != new_state->vsys_status); +} + +static void bq25890_handle_state_change(struct bq25890_device *bq, + struct bq25890_state *new_state) +{ + int ret; + struct bq25890_state old_state; + + mutex_lock(&bq->lock); + old_state = bq->state; + mutex_unlock(&bq->lock); + + if (!new_state->online) { /* power removed */ + /* disable ADC */ + ret = bq25890_field_write(bq, F_CONV_START, 0); + if (ret < 0) + goto error; + } else if (!old_state.online) { /* power inserted */ + /* enable ADC, to have control of charge current/voltage */ + ret = bq25890_field_write(bq, F_CONV_START, 1); + if (ret < 0) + goto error; + } + + return; + +error: + dev_err(bq->dev, "Error communicating with the chip.\n"); +} + +static irqreturn_t bq25890_irq_handler_thread(int irq, void *private) +{ + struct bq25890_device *bq = private; + int ret; + struct bq25890_state state; + + ret = bq25890_get_chip_state(bq, &state); + if (ret < 0) + goto handled; + + if (!bq25890_state_changed(bq, &state)) + goto handled; + + bq25890_handle_state_change(bq, &state); + + mutex_lock(&bq->lock); + bq->state = state; + mutex_unlock(&bq->lock); + + power_supply_changed(bq->charger); + +handled: + return IRQ_HANDLED; +} + +static int bq25890_chip_reset(struct bq25890_device *bq) +{ + int ret; + int rst_check_counter = 10; + + ret = bq25890_field_write(bq, F_REG_RST, 1); + if (ret < 0) + return ret; + + do { + ret = bq25890_field_read(bq, F_REG_RST); + if (ret < 0) + return ret; + + usleep_range(5, 10); + } while (ret == 1 && --rst_check_counter); + + if (!rst_check_counter) + return -ETIMEDOUT; + + return 0; +} + +static int bq25890_hw_init(struct bq25890_device *bq) +{ + int ret; + int i; + struct bq25890_state state; + + const struct { + enum bq25890_fields id; + u32 value; + } init_data[] = { + {F_ICHG, bq->init_data.ichg}, + {F_VREG, bq->init_data.vreg}, + {F_ITERM, bq->init_data.iterm}, + {F_IPRECHG, bq->init_data.iprechg}, + {F_SYSVMIN, bq->init_data.sysvmin}, + {F_BOOSTV, bq->init_data.boostv}, + {F_BOOSTI, bq->init_data.boosti}, + {F_BOOSTF, bq->init_data.boostf}, + {F_EN_ILIM, bq->init_data.ilim_en}, + {F_TREG, bq->init_data.treg} + }; + + ret = bq25890_chip_reset(bq); + if (ret < 0) { + dev_dbg(bq->dev, "Reset failed %d\n", ret); + return ret; + } + + /* disable watchdog */ + ret = bq25890_field_write(bq, F_WD, 0); + if (ret < 0) { + dev_dbg(bq->dev, "Disabling watchdog failed %d\n", ret); + return ret; + } + + /* initialize currents/voltages and other parameters */ + for (i = 0; i < ARRAY_SIZE(init_data); i++) { + ret = bq25890_field_write(bq, init_data[i].id, + init_data[i].value); + if (ret < 0) { + dev_dbg(bq->dev, "Writing init data failed %d\n", ret); + return ret; + } + } + + /* Configure ADC for continuous conversions. This does not enable it. */ + ret = bq25890_field_write(bq, F_CONV_RATE, 1); + if (ret < 0) { + dev_dbg(bq->dev, "Config ADC failed %d\n", ret); + return ret; + } + + ret = bq25890_get_chip_state(bq, &state); + if (ret < 0) { + dev_dbg(bq->dev, "Get state failed %d\n", ret); + return ret; + } + + mutex_lock(&bq->lock); + bq->state = state; + mutex_unlock(&bq->lock); + + return 0; +} + +static enum power_supply_property bq25890_power_supply_props[] = { + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; + +static char *bq25890_charger_supplied_to[] = { + "main-battery", +}; + +static const struct power_supply_desc bq25890_power_supply_desc = { + .name = "bq25890-charger", + .type = POWER_SUPPLY_TYPE_UNKNOWN, + .properties = bq25890_power_supply_props, + .num_properties = ARRAY_SIZE(bq25890_power_supply_props), + .get_property = bq25890_power_supply_get_property, + .set_property = bq25890_power_supply_set_property, + .property_is_writeable = bq25890_power_supply_prop_is_writeable, +}; + +static int bq25890_power_supply_init(struct bq25890_device *bq) +{ + struct power_supply_config psy_cfg = { .drv_data = bq, }; + + psy_cfg.supplied_to = bq25890_charger_supplied_to; + psy_cfg.num_supplicants = ARRAY_SIZE(bq25890_charger_supplied_to); + + bq->charger = power_supply_register(bq->dev, &bq25890_power_supply_desc, + &psy_cfg); + + return PTR_ERR_OR_ZERO(bq->charger); +} + +static void bq25890_usb_work(struct work_struct *data) +{ + int ret; + struct bq25890_device *bq = + container_of(data, struct bq25890_device, usb_work); + + switch (bq->usb_event) { + case USB_EVENT_ID: + /* Enable boost mode */ + ret = bq25890_field_write(bq, F_OTG_CFG, 1); + if (ret < 0) + goto error; + break; + + case USB_EVENT_NONE: + /* Disable boost mode */ + ret = bq25890_field_write(bq, F_OTG_CFG, 0); + if (ret < 0) + goto error; + + power_supply_changed(bq->charger); + break; + } + + return; + +error: + dev_err(bq->dev, "Error switching to boost/charger mode.\n"); +} + +static int bq25890_usb_notifier(struct notifier_block *nb, unsigned long val, + void *priv) +{ + struct bq25890_device *bq = + container_of(nb, struct bq25890_device, usb_nb); + + bq->usb_event = val; + queue_work(system_power_efficient_wq, &bq->usb_work); + + return NOTIFY_OK; +} + +static int bq25890_irq_probe(struct bq25890_device *bq) +{ + struct gpio_desc *irq; + + irq = devm_gpiod_get(bq->dev, BQ25890_IRQ_PIN, GPIOD_IN); + if (IS_ERR(irq)) { + dev_err(bq->dev, "Could not probe irq pin.\n"); + return PTR_ERR(irq); + } + + return gpiod_to_irq(irq); +} + +static int bq25890_fw_read_u32_props(struct bq25890_device *bq) +{ + int ret; + u32 property; + int i; + struct bq25890_init_data *init = &bq->init_data; + struct { + char *name; + bool optional; + enum bq25890_table_ids tbl_id; + u8 *conv_data; /* holds converted value from given property */ + } props[] = { + /* required properties */ + {"ti,charge-current", false, TBL_ICHG, &init->ichg}, + {"ti,battery-regulation-voltage", false, TBL_VREG, &init->vreg}, + {"ti,termination-current", false, TBL_ITERM, &init->iterm}, + {"ti,precharge-current", false, TBL_ITERM, &init->iprechg}, + {"ti,minimum-sys-voltage", false, TBL_SYSVMIN, &init->sysvmin}, + {"ti,boost-voltage", false, TBL_BOOSTV, &init->boostv}, + {"ti,boost-max-current", false, TBL_BOOSTI, &init->boosti}, + + /* optional properties */ + {"ti,thermal-regulation-threshold", true, TBL_TREG, &init->treg} + }; + + /* initialize data for optional properties */ + init->treg = 3; /* 120 degrees Celsius */ + + for (i = 0; i < ARRAY_SIZE(props); i++) { + ret = device_property_read_u32(bq->dev, props[i].name, + &property); + if (ret < 0) { + if (props[i].optional) + continue; + + dev_err(bq->dev, "Unable to read property %d %s\n", ret, + props[i].name); + + return ret; + } + + *props[i].conv_data = bq25890_find_idx(property, + props[i].tbl_id); + } + + return 0; +} + +static int bq25890_fw_probe(struct bq25890_device *bq) +{ + int ret; + struct bq25890_init_data *init = &bq->init_data; + + ret = bq25890_fw_read_u32_props(bq); + if (ret < 0) + return ret; + + init->ilim_en = device_property_read_bool(bq->dev, "ti,use-ilim-pin"); + init->boostf = device_property_read_bool(bq->dev, "ti,boost-low-freq"); + + return 0; +} + +static int bq25890_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct device *dev = &client->dev; + struct bq25890_device *bq; + int ret; + int i; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(dev, "No support for SMBUS_BYTE_DATA\n"); + return -ENODEV; + } + + bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL); + if (!bq) + return -ENOMEM; + + bq->client = client; + bq->dev = dev; + + mutex_init(&bq->lock); + + bq->rmap = devm_regmap_init_i2c(client, &bq25890_regmap_config); + if (IS_ERR(bq->rmap)) { + dev_err(dev, "failed to allocate register map\n"); + return PTR_ERR(bq->rmap); + } + + for (i = 0; i < ARRAY_SIZE(bq25890_reg_fields); i++) { + const struct reg_field *reg_fields = bq25890_reg_fields; + + bq->rmap_fields[i] = devm_regmap_field_alloc(dev, bq->rmap, + reg_fields[i]); + if (IS_ERR(bq->rmap_fields[i])) { + dev_err(dev, "cannot allocate regmap field\n"); + return PTR_ERR(bq->rmap_fields[i]); + } + } + + i2c_set_clientdata(client, bq); + + bq->chip_id = bq25890_field_read(bq, F_PN); + if (bq->chip_id < 0) { + dev_err(dev, "Cannot read chip ID.\n"); + return bq->chip_id; + } + + if ((bq->chip_id != BQ25890_ID) && (bq->chip_id != BQ25895_ID) + && (bq->chip_id != BQ25896_ID)) { + dev_err(dev, "Chip with ID=%d, not supported!\n", bq->chip_id); + return -ENODEV; + } + + if (!dev->platform_data) { + ret = bq25890_fw_probe(bq); + if (ret < 0) { + dev_err(dev, "Cannot read device properties.\n"); + return ret; + } + } else { + return -ENODEV; + } + + ret = bq25890_hw_init(bq); + if (ret < 0) { + dev_err(dev, "Cannot initialize the chip.\n"); + return ret; + } + + if (client->irq <= 0) + client->irq = bq25890_irq_probe(bq); + + if (client->irq < 0) { + dev_err(dev, "No irq resource found.\n"); + return client->irq; + } + + /* OTG reporting */ + bq->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + if (!IS_ERR_OR_NULL(bq->usb_phy)) { + INIT_WORK(&bq->usb_work, bq25890_usb_work); + bq->usb_nb.notifier_call = bq25890_usb_notifier; + usb_register_notifier(bq->usb_phy, &bq->usb_nb); + } + + ret = devm_request_threaded_irq(dev, client->irq, NULL, + bq25890_irq_handler_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + BQ25890_IRQ_PIN, bq); + if (ret) + goto irq_fail; + + ret = bq25890_power_supply_init(bq); + if (ret < 0) { + dev_err(dev, "Failed to register power supply\n"); + goto irq_fail; + } + + return 0; + +irq_fail: + if (!IS_ERR_OR_NULL(bq->usb_phy)) + usb_unregister_notifier(bq->usb_phy, &bq->usb_nb); + + return ret; +} + +static int bq25890_remove(struct i2c_client *client) +{ + struct bq25890_device *bq = i2c_get_clientdata(client); + + power_supply_unregister(bq->charger); + + if (!IS_ERR_OR_NULL(bq->usb_phy)) + usb_unregister_notifier(bq->usb_phy, &bq->usb_nb); + + /* reset all registers to default values */ + bq25890_chip_reset(bq); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int bq25890_suspend(struct device *dev) +{ + struct bq25890_device *bq = dev_get_drvdata(dev); + + /* + * If charger is removed, while in suspend, make sure ADC is diabled + * since it consumes slightly more power. + */ + return bq25890_field_write(bq, F_CONV_START, 0); +} + +static int bq25890_resume(struct device *dev) +{ + int ret; + struct bq25890_state state; + struct bq25890_device *bq = dev_get_drvdata(dev); + + ret = bq25890_get_chip_state(bq, &state); + if (ret < 0) + return ret; + + mutex_lock(&bq->lock); + bq->state = state; + mutex_unlock(&bq->lock); + + /* Re-enable ADC only if charger is plugged in. */ + if (state.online) { + ret = bq25890_field_write(bq, F_CONV_START, 1); + if (ret < 0) + return ret; + } + + /* signal userspace, maybe state changed while suspended */ + power_supply_changed(bq->charger); + + return 0; +} +#endif + +static const struct dev_pm_ops bq25890_pm = { + SET_SYSTEM_SLEEP_PM_OPS(bq25890_suspend, bq25890_resume) +}; + +static const struct i2c_device_id bq25890_i2c_ids[] = { + { "bq25890", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, bq25890_i2c_ids); + +static const struct of_device_id bq25890_of_match[] = { + { .compatible = "ti,bq25890-mmi", }, + { }, +}; +MODULE_DEVICE_TABLE(of, bq25890_of_match); + +static const struct acpi_device_id bq25890_acpi_match[] = { + {"BQ258900", 0}, + {}, +}; +MODULE_DEVICE_TABLE(acpi, bq25890_acpi_match); + +static struct i2c_driver bq25890_driver = { + .driver = { + .name = "bq25890-charger", + .of_match_table = of_match_ptr(bq25890_of_match), + .acpi_match_table = ACPI_PTR(bq25890_acpi_match), + .pm = &bq25890_pm, + }, + .probe = bq25890_probe, + .remove = bq25890_remove, + .id_table = bq25890_i2c_ids, +}; +module_i2c_driver(bq25890_driver); + +MODULE_AUTHOR("Laurentiu Palcu "); +MODULE_DESCRIPTION("bq25890 charger driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/bq2589x_chg_mmi/Android.mk b/drivers/power/bq2589x_chg_mmi/Android.mk new file mode 100644 index 000000000000..73f1c7da2d11 --- /dev/null +++ b/drivers/power/bq2589x_chg_mmi/Android.mk @@ -0,0 +1,27 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := bq2589x_charger.ko + +ifeq ($(MMI_QC3P_TURBO_CHARGER),true) + KERNEL_CFLAGS += CONFIG_MMI_QC3P_TURBO_CHARGER=y +endif + +ifeq ($(MMI_QC3P_WT6670_DETECTED),true) + KERNEL_CFLAGS += CONFIG_MMI_QC3P_WT6670_DETECTED=y +endif + +ifeq ($(MMI_EXT_CHG_LED),true) + KERNEL_CFLAGS += CONFIG_MMI_EXT_CHG_LED=y + KBUILD_OPTIONS += CONFIG_MMI_EXT_CHG_LED=y +endif + +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_discrete_charger_class.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +KBUILD_OPTIONS += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/bq2589x_chg_mmi/Kbuild b/drivers/power/bq2589x_chg_mmi/Kbuild new file mode 100644 index 000000000000..d40823873eee --- /dev/null +++ b/drivers/power/bq2589x_chg_mmi/Kbuild @@ -0,0 +1,20 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/bq2589x_chg_mmi + +obj-m += bq2589x_charger.o + +ifneq ($(filter m y,$(CONFIG_MMI_QC3P_TURBO_CHARGER)),) + EXTRA_CFLAGS += -DCONFIG_MMI_QC3P_TURBO_CHARGER +endif + +ifneq ($(filter m y,$(CONFIG_MMI_QC3P_WT6670_DETECTED)),) + EXTRA_CFLAGS += -DCONFIG_MMI_QC3P_WT6670_DETECTED +endif + +ifneq ($(filter m y,$(CONFIG_MMI_EXT_CHG_LED)),) + EXTRA_CFLAGS += -DCONFIG_MMI_EXT_CHG_LED +endif + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_discrete_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/bq2589x_chg_mmi/Makefile b/drivers/power/bq2589x_chg_mmi/Makefile new file mode 100644 index 000000000000..6fd4304bb47c --- /dev/null +++ b/drivers/power/bq2589x_chg_mmi/Makefile @@ -0,0 +1,32 @@ +ifeq ($(strip $(MODULE_KERNEL_VERSION)), 5.15) + +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_discrete_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers + +else + +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean +endif diff --git a/drivers/power/bq2589x_chg_mmi/bq2589x_charger.c b/drivers/power/bq2589x_chg_mmi/bq2589x_charger.c new file mode 100644 index 000000000000..f57bc679382b --- /dev/null +++ b/drivers/power/bq2589x_chg_mmi/bq2589x_charger.c @@ -0,0 +1,3571 @@ +/* + * BQ2589x battery charging driver + * + * Copyright (C) 2013 Texas Instruments + * + * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bq2589x_reg.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +#include +#endif + +#ifdef __indicator_led_en__ +static struct bq2589x *bq_g; +#define TRILED_NUM_MAX 3 + +struct pwm_setting { + u64 pre_period_ns; + u64 period_ns; + u64 duty_ns; +}; + +struct led_setting { + u64 on_ms; + u64 off_ms; + enum led_brightness brightness; + bool blink; + bool breath; +}; + +struct qpnp_led_dev { + struct led_classdev cdev; + struct pwm_device *pwm_dev; + struct pwm_setting pwm_setting; + struct led_setting led_setting; + struct indicator_led_chip *chip; + struct mutex lock; + const char *label; + const char *default_trigger; + u8 id; + bool blinking; + bool breathing; +}; + +struct indicator_led_chip { + struct device *dev; + struct qpnp_led_dev *leds; + struct nvmem_device *pbs_nvmem; + struct mutex bus_lock; + int num_leds; + u16 reg_base; + u8 subtype; + u8 bitmap; + struct pinctrl *pinctrl; + struct pinctrl_state *pin_sta_default; +}; +#endif + +#define BQ2589x_MANUFACTURER "Texas Instruments" +#define BQ25890_IRQ_PIN "bq2589x_irq" +#define BQ2589X_STATUS_PLUGIN 0x0001 +#define BQ2589X_STATUS_PG 0x0002 +#define BQ2589X_STATUS_CHARGE_ENABLE 0x0004 +#define BQ2589X_STATUS_FAULT 0x0008 +#define BQ2589X_STATUS_EXIST 0x0100 + +static struct bq2589x *g_bq; +static DEFINE_MUTEX(bq2589x_i2c_lock); +#ifdef CONFIG_MMI_QC3P_TURBO_CHARGER +bool qc3p_update_policy(struct bq2589x *chip); +#endif +#define MMI_HVDCP2_VOLTAGE_STANDARD 8000000 +#define MMI_HVDCP3_VOLTAGE_STANDARD 7500000 +#define MMI_HVDCP_DETECT_ICL_LIMIT 500 +#define BQ2589X_ENABLE_ICO_THRESHOLD 250 +struct bq2589x_iio { + struct iio_channel *usbin_v_chan; +}; + +enum bq2589x_status { + STATUS_NOT_CHARGING, + STATUS_PRE_CHARGING, + STATUS_FAST_CHARGING, + STATUS_TERMINATION_DONE, +}; + + +static enum power_supply_usb_type bq2589x_usb_type[] = { + POWER_SUPPLY_USB_TYPE_UNKNOWN, + POWER_SUPPLY_USB_TYPE_SDP, + POWER_SUPPLY_USB_TYPE_DCP, + POWER_SUPPLY_USB_TYPE_CDP, +}; + +enum bq2589x_vbus_type { + BQ2589X_VBUS_NONE, + BQ2589X_VBUS_USB_SDP, + BQ2589X_VBUS_USB_CDP, /*CDP for bq25890, Adapter for bq25892*/ + BQ2589X_VBUS_USB_DCP, + BQ2589X_VBUS_MAXC, + BQ2589X_VBUS_UNKNOWN, + BQ2589X_VBUS_NONSTAND, + BQ2589X_VBUS_OTG, + BQ2589X_VBUS_TYPE_NUM, +}; + +enum bq2589x_part_no { + BQ25890 = 0x03, + BQ25892 = 0x00, + BQ25895 = 0x07, + SC89890H = 0x04, +}; + +struct bq2589x_config { + bool enable_auto_dpdm; + //bool enable_12v; + bool enable_term; + bool enable_ico; + bool use_absolute_vindpm; + bool disable_ilim; + + int charge_voltage; + int charge_current; + int term_current; + int vindpm; +}; + +struct bq2589x_state { + bool vsys_stat; + bool therm_stat; + bool online; + bool chrg_en; + bool hiz_en; + bool term_en; + bool vbus_gd; + + u8 chrg_stat; + u8 vbus_status; + u8 chrg_type; + u8 health; + u8 chrg_fault; + u8 ntc_fault; +}; + +struct bq2589x { + enum bq2589x_part_no part_no; + + unsigned int status; + + bool dpdm_enabled; + bool typec_apsd_rerun_done; + bool enabled; + + int real_charger_type; + int revision; + int vbus_type; + int vbus_volt; + int vbat_volt; + int rsoc; + int chg_en_gpio; + int wls_en_gpio; + bool ignore_request_dpdm; + + const char *chg_dev_name; + + struct charger_device *chg_dev; + struct device *dev; + struct i2c_client *client; + struct regulator *dpdm_reg; + struct mutex regulator_lock; + struct bq2589x_state state; + struct mutex lock; + + struct bq2589x_config cfg; + struct work_struct irq_work; + struct delayed_work monitor_work; + struct delayed_work ico_work; + struct delayed_work pe_volt_tune_work; + struct delayed_work check_pe_tuneup_work; + + struct power_supply_desc usb; + struct power_supply_desc wall; + struct power_supply *batt_psy; + /*struct power_supply *usb_psy;*/ + struct power_supply *wall_psy; + struct power_supply_config usb_cfg; + struct power_supply_config wall_cfg; + + /* enable dynamic adjust battery voltage */ + struct power_supply *battery; + bool enable_dynamic_adjust_batvol; + int final_cc; + int final_cv; + int cv_tune; + + /* enable dynamic adjust vindpm */ + bool enable_dynamic_adjust_vindpm; + int vindpm_flag; + + /*mmi qc3*/ + bool mmi_qc3_support; + struct semaphore sem_dpdm; + struct task_struct *mmi_qc3_authen_task; + wait_queue_head_t mmi_qc3_wait_que; + bool mmi_qc3_trig_flag; + bool mmi_is_qc3_authen; + u32 input_current_cache; + int pulse_cnt; + + /*mmi qc3p*/ + bool mmi_qc3p_rerun_done; + bool mmi_qc3p_wa; + int mmi_qc3p_power; + + /*mmi PD*/ + int pd_active; + + struct bq2589x_iio iio; +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED + struct iio_channel **ext_iio_chans; +#endif + bool factory_mode; + bool sc8989_adc_stop; +}; + +struct pe_ctrl { + bool enable; + bool tune_up_volt; + bool tune_down_volt; + bool tune_done; + bool tune_fail; + int tune_count; + int target_volt; + int high_volt_level;/* vbus volt > this threshold means tune up successfully */ + int low_volt_level; /* vbus volt < this threshold means tune down successfully */ + int vbat_min_volt; /* to tune up voltage only when vbat > this threshold */ +}; +static struct pe_ctrl pe; + +static int bq2589x_read_byte(struct bq2589x *bq, u8 *data, u8 reg) +{ + int ret; + + mutex_lock(&bq2589x_i2c_lock); + ret = i2c_smbus_read_byte_data(bq->client, reg); + if (ret < 0) { + dev_err(bq->dev, "failed to read 0x%.2x\n", reg); + mutex_unlock(&bq2589x_i2c_lock); + return ret; + } + + *data = (u8)ret; + mutex_unlock(&bq2589x_i2c_lock); + + return 0; +} + +static int bq2589x_write_byte(struct bq2589x *bq, u8 reg, u8 data) +{ + int ret; + mutex_lock(&bq2589x_i2c_lock); + ret = i2c_smbus_write_byte_data(bq->client, reg, data); + mutex_unlock(&bq2589x_i2c_lock); + return ret; +} + +static int bq2589x_update_bits(struct bq2589x *bq, u8 reg, u8 mask, u8 data) +{ + int ret; + u8 tmp; + + ret = bq2589x_read_byte(bq, &tmp, reg); + + if (ret) + return ret; + + tmp &= ~mask; + tmp |= data & mask; + + return bq2589x_write_byte(bq, reg, tmp); +} + +/*static enum bq2589x_vbus_type bq2589x_get_vbus_type(struct bq2589x *bq) +{ + u8 val = 0; + int ret; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_0B); + if (ret < 0) + return 0; + val &= BQ2589X_VBUS_STAT_MASK; + val >>= BQ2589X_VBUS_STAT_SHIFT; + + return val; +}*/ + +static int bq2589x_enable_otg(struct bq2589x *bq) +{ + u8 val = BQ2589X_OTG_ENABLE << BQ2589X_OTG_CONFIG_SHIFT; + + return bq2589x_update_bits(bq, BQ2589X_REG_03, BQ2589X_OTG_CONFIG_MASK, val); + +} + +static int bq2589x_disable_otg(struct bq2589x *bq) +{ + u8 val = BQ2589X_OTG_DISABLE << BQ2589X_OTG_CONFIG_SHIFT; + + return bq2589x_update_bits(bq, BQ2589X_REG_03, BQ2589X_OTG_CONFIG_MASK, val); + +} + +/*static int bq2589x_set_otg_volt(struct bq2589x *bq, int volt) +{ + u8 val = 0; + + if (volt < BQ2589X_BOOSTV_BASE) + volt = BQ2589X_BOOSTV_BASE; + if (volt > BQ2589X_BOOSTV_BASE + (BQ2589X_BOOSTV_MASK >> BQ2589X_BOOSTV_SHIFT) * BQ2589X_BOOSTV_LSB) + volt = BQ2589X_BOOSTV_BASE + (BQ2589X_BOOSTV_MASK >> BQ2589X_BOOSTV_SHIFT) * BQ2589X_BOOSTV_LSB; + + val = ((volt - BQ2589X_BOOSTV_BASE) / BQ2589X_BOOSTV_LSB) << BQ2589X_BOOSTV_SHIFT; + + return bq2589x_update_bits(bq, BQ2589X_REG_0A, BQ2589X_BOOSTV_MASK, val); + +}*/ + +static int bq2589x_set_otg_current(struct bq2589x *bq, int curr) +{ + u8 temp; + + if (bq->part_no == SC89890H) { + if (curr < 600) + temp = SC89890H_BOOST_LIM_500MA; + else if (curr < 900) + temp = SC89890H_BOOST_LIM_750MA; + else if (curr < 1300) + temp = SC89890H_BOOST_LIM_1200MA; + else if (curr < 1500) + temp = SC89890H_BOOST_LIM_1400MA; + else if (curr < 1700) + temp = SC89890H_BOOST_LIM_1650MA; + else if (curr < 1900) + temp = SC89890H_BOOST_LIM_1875MA; + else if (curr < 2200) + temp = SC89890H_BOOST_LIM_2150MA; + else if (curr < 2500) + temp = SC89890H_BOOST_LIM_2450MA; + else + temp = SC89890H_BOOST_LIM_1400MA; + } else { + if (curr >= 2400) + temp = BQ2589X_BOOST_LIM_2400MA; + else if (curr >= 2100) + temp = BQ2589X_BOOST_LIM_2100MA; + else if (curr >= 1800) + temp = BQ2589X_BOOST_LIM_1800MA; + else if (curr >= 1600) + temp = BQ2589X_BOOST_LIM_1600MA; + else if (curr >= 1300) + temp = BQ2589X_BOOST_LIM_1300MA; + else if (curr >= 1100) + temp = BQ2589X_BOOST_LIM_1100MA; + else if (curr >= 700) + temp = BQ2589X_BOOST_LIM_700MA; + else + temp = BQ2589X_BOOST_LIM_500MA; + } + + + return bq2589x_update_bits(bq, BQ2589X_REG_0A, BQ2589X_BOOST_LIM_MASK, temp << BQ2589X_BOOST_LIM_SHIFT); +} + +static int bq2589x_enable_charger(struct bq2589x *bq) +{ + int ret; + u8 val = BQ2589X_CHG_ENABLE << BQ2589X_CHG_CONFIG_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_03, BQ2589X_CHG_CONFIG_MASK, val); + if (ret == 0) + bq->status |= BQ2589X_STATUS_CHARGE_ENABLE; + return ret; +} + +static int bq2589x_disable_charger(struct bq2589x *bq) +{ + int ret; + u8 val = BQ2589X_CHG_DISABLE << BQ2589X_CHG_CONFIG_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_03, BQ2589X_CHG_CONFIG_MASK, val); + if (ret == 0) + bq->status &= ~BQ2589X_STATUS_CHARGE_ENABLE; + return ret; +} + +/* interfaces that can be called by other module */ +int bq2589x_adc_start(struct bq2589x *bq, bool oneshot) +{ + u8 val; + int ret; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_02); + if (ret < 0) { + dev_err(bq->dev, "%s failed to read register 0x02:%d\n", __func__, ret); + return ret; + } + + if (((val & BQ2589X_CONV_RATE_MASK) >> BQ2589X_CONV_RATE_SHIFT) == BQ2589X_ADC_CONTINUE_ENABLE) + return 0; /*is doing continuous scan*/ + if (oneshot) + ret = bq2589x_update_bits(bq, BQ2589X_REG_02, BQ2589X_CONV_START_MASK, BQ2589X_CONV_START << BQ2589X_CONV_START_SHIFT); + else + ret = bq2589x_update_bits(bq, BQ2589X_REG_02, BQ2589X_CONV_RATE_MASK, BQ2589X_ADC_CONTINUE_ENABLE << BQ2589X_CONV_RATE_SHIFT); + return ret; +} + +int bq2589x_adc_stop(struct bq2589x *bq) +{ + return bq2589x_update_bits(bq, BQ2589X_REG_02, BQ2589X_CONV_RATE_MASK, BQ2589X_ADC_CONTINUE_DISABLE << BQ2589X_CONV_RATE_SHIFT); +} + + +int bq2589x_adc_read_battery_volt(struct bq2589x *bq) +{ + uint8_t val; + int volt; + int ret; + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_0E); + if (ret < 0) { + dev_err(bq->dev, "read battery voltage failed :%d\n", ret); + return ret; + } else{ + volt = BQ2589X_BATV_BASE + ((val & BQ2589X_BATV_MASK) >> BQ2589X_BATV_SHIFT) * BQ2589X_BATV_LSB ; + return volt; + } +} + + +int bq2589x_adc_read_sys_volt(struct bq2589x *bq) +{ + uint8_t val; + int volt; + int ret; + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_0F); + if (ret < 0) { + dev_err(bq->dev, "read system voltage failed :%d\n", ret); + return ret; + } else{ + volt = BQ2589X_SYSV_BASE + ((val & BQ2589X_SYSV_MASK) >> BQ2589X_SYSV_SHIFT) * BQ2589X_SYSV_LSB ; + return volt; + } +} + +int bq2589x_adc_read_vbus_volt(struct bq2589x *bq) +{ + uint8_t val; + int volt; + int ret; + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_11); + if (ret < 0) { + dev_err(bq->dev, "read vbus voltage failed :%d\n", ret); + return ret; + } else{ + volt = BQ2589X_VBUSV_BASE + ((val & BQ2589X_VBUSV_MASK) >> BQ2589X_VBUSV_SHIFT) * BQ2589X_VBUSV_LSB ; + return volt; + } +} + +int bq2589x_adc_read_temperature(struct bq2589x *bq) +{ + uint8_t val; + int temp; + int ret; + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_10); + if (ret < 0) { + dev_err(bq->dev, "read temperature failed :%d\n", ret); + return ret; + } else{ + temp = BQ2589X_TSPCT_BASE + ((val & BQ2589X_TSPCT_MASK) >> BQ2589X_TSPCT_SHIFT) * BQ2589X_TSPCT_LSB ; + return temp; + } +} + +int bq2589x_adc_read_charge_current(struct bq2589x *bq) +{ + uint8_t val; + int curr; + int ret; + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_12); + if (ret < 0) { + dev_err(bq->dev, "read charge current failed :%d\n", ret); + return ret; + } else{ + curr = (int)(BQ2589X_ICHGR_BASE + ((val & BQ2589X_ICHGR_MASK) >> BQ2589X_ICHGR_SHIFT) * BQ2589X_ICHGR_LSB) ; + return curr; + } +} + +static int mmi_read_usbin_voltage_chan(struct bq2589x *bq, int *val) +{ + int rc; + + if (!bq->iio.usbin_v_chan) + return -ENODATA; + + rc = iio_read_channel_processed(bq->iio.usbin_v_chan, val); + if (rc < 0) { + dev_err(bq->dev, "Couldn't read USBIN channel rc=%d\n", rc); + return rc; + } + + return 0; +} + +static int mmi_get_usb_voltage_now(struct bq2589x *bq, int *val) +{ + int rc; + int raw_date; + + rc = mmi_read_usbin_voltage_chan(bq, &raw_date); + if (rc < 0) { + dev_err(bq->dev, "Couldn't read USBIN over vadc rc=%d\n", rc); + return rc; + } + + *val = raw_date * 3; + + return 0; +} + +int bq2589x_set_chargecurrent(struct bq2589x *bq, int curr) +{ + u8 ichg; + + if (bq->part_no == SC89890H) { + ichg = (curr - SC89890H_ICHG_BASE)/SC89890H_ICHG_LSB; + /* enable dynamic adjust battery voltage */ + if(bq->enable_dynamic_adjust_batvol) + bq->final_cc = curr * 1000; + } else { + ichg = (curr - BQ2589X_ICHG_BASE)/BQ2589X_ICHG_LSB; + } + return bq2589x_update_bits(bq, BQ2589X_REG_04, BQ2589X_ICHG_MASK, ichg << BQ2589X_ICHG_SHIFT); + +} + +int bq2589x_set_term_current(struct bq2589x *bq, int curr) +{ + u8 iterm; + + if (bq->part_no == SC89890H) { + iterm = (curr - SC89890H_ITERM_BASE) / SC89890H_ITERM_LSB; + } else { + iterm = (curr - BQ2589X_ITERM_BASE) / BQ2589X_ITERM_LSB; + } + + return bq2589x_update_bits(bq, BQ2589X_REG_05, BQ2589X_ITERM_MASK, iterm << BQ2589X_ITERM_SHIFT); +} + + +int bq2589x_set_prechg_current(struct bq2589x *bq, int curr) +{ + u8 iprechg; + + if (bq->part_no == SC89890H) { + iprechg = (curr - SC89890H_IPRECHG_BASE) / SC89890H_IPRECHG_LSB; + } else { + iprechg = (curr - BQ2589X_IPRECHG_BASE) / BQ2589X_IPRECHG_LSB; + } + + return bq2589x_update_bits(bq, BQ2589X_REG_05, BQ2589X_IPRECHG_MASK, iprechg << BQ2589X_IPRECHG_SHIFT); +} + +int bq2589x_set_chargevoltage(struct bq2589x *bq, int volt) +{ + u8 val; + + val = (volt - BQ2589X_VREG_BASE)/BQ2589X_VREG_LSB; + return bq2589x_update_bits(bq, BQ2589X_REG_06, BQ2589X_VREG_MASK, val << BQ2589X_VREG_SHIFT); +} + + +int bq2589x_set_input_volt_limit(struct bq2589x *bq, int volt) +{ + u8 val; + val = (volt - BQ2589X_VINDPM_BASE) / BQ2589X_VINDPM_LSB; + return bq2589x_update_bits(bq, BQ2589X_REG_0D, BQ2589X_VINDPM_MASK, val << BQ2589X_VINDPM_SHIFT); +} + +int bq2589x_set_input_current_limit(struct bq2589x *bq, int curr) +{ + u8 val; + + if (curr < BQ2589X_IINLIM_BASE) + val = 0; + /*for HW accuracy issue, so icl >= 3A we need to set 0x3F*/ + else if (curr >= BQ2589X_IINLIM_MAX){ + if (bq->part_no == SC89890H) + val = 0x3A; + else + val = 0x3F; + } + else + val = (curr - BQ2589X_IINLIM_BASE) / BQ2589X_IINLIM_LSB; + + return bq2589x_update_bits(bq, BQ2589X_REG_00, BQ2589X_IINLIM_MASK, val << BQ2589X_IINLIM_SHIFT); +} + +int bq2589x_get_charging_status(struct bq2589x *bq) +{ + u8 val = 0; + int ret; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_0B); + if (ret < 0) { + dev_err(bq->dev, "%s Failed to read register 0x0b:%d\n", __func__, ret); + return ret; + } + val &= BQ2589X_CHRG_STAT_MASK; + val >>= BQ2589X_CHRG_STAT_SHIFT; + return val; +} + +void bq2589x_set_otg(struct bq2589x *bq, int enable) +{ + int ret; + + if (enable) { + /*disable wls output*/ + if (gpio_is_valid(bq->wls_en_gpio)) { + gpio_direction_output(bq->wls_en_gpio, 1); + } + + ret = bq2589x_enable_otg(bq); + if (ret < 0) { + dev_err(bq->dev, "%s:Failed to enable otg-%d\n", __func__, ret); + return; + } + } else{ + ret = bq2589x_disable_otg(bq); + if (ret < 0) + dev_err(bq->dev, "%s:Failed to disable otg-%d\n", __func__, ret); + + /*resume wls output*/ + if (gpio_is_valid(bq->wls_en_gpio)) { + gpio_direction_output(bq->wls_en_gpio, 0); + } + } +} + +int bq2589x_set_watchdog_timer(struct bq2589x *bq, u8 timeout) +{ + return bq2589x_update_bits(bq, BQ2589X_REG_07, BQ2589X_WDT_MASK, (u8)((timeout - BQ2589X_WDT_BASE) / BQ2589X_WDT_LSB) << BQ2589X_WDT_SHIFT); +} + +int bq2589x_disable_watchdog_timer(struct bq2589x *bq) +{ + u8 val = BQ2589X_WDT_DISABLE << BQ2589X_WDT_SHIFT; + + return bq2589x_update_bits(bq, BQ2589X_REG_07, BQ2589X_WDT_MASK, val); +} + +int bq2589x_reset_watchdog_timer(struct bq2589x *bq) +{ + u8 val = BQ2589X_WDT_RESET << BQ2589X_WDT_RESET_SHIFT; + + return bq2589x_update_bits(bq, BQ2589X_REG_03, BQ2589X_WDT_RESET_MASK, val); +} + +int bq2589x_force_dpdm(struct bq2589x *bq) +{ + int ret; + u8 val = 0; + + if (bq->part_no == SC89890H) { + val = 0x2 << BQ2589X_DP_VSEL_SHIFT; + bq2589x_update_bits(bq, BQ2589X_REG_01, BQ2589X_DP_VSEL_MASK, val); + val = 0x1 << BQ2589X_DM_VSEL_SHIFT; + bq2589x_update_bits(bq, BQ2589X_REG_01, BQ2589X_DM_VSEL_MASK, val); + + msleep(30); + + val = 0x1 << BQ2589X_DP_VSEL_SHIFT; + bq2589x_update_bits(bq, BQ2589X_REG_01, BQ2589X_DP_VSEL_MASK, val); + val = 0x1 << BQ2589X_DM_VSEL_SHIFT; + bq2589x_update_bits(bq, BQ2589X_REG_01, BQ2589X_DM_VSEL_MASK, val); + + msleep(30); + } + + val = BQ2589X_FORCE_DPDM << BQ2589X_FORCE_DPDM_SHIFT; + ret = bq2589x_update_bits(bq, BQ2589X_REG_02, BQ2589X_FORCE_DPDM_MASK, val); + if (ret) + return ret; + + return 0; +} + +int bq2589x_reset_chip(struct bq2589x *bq) +{ + int ret; + u8 val = BQ2589X_RESET << BQ2589X_RESET_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_14, BQ2589X_RESET_MASK, val); + return ret; +} + +int bq2589x_enter_ship_mode(struct bq2589x *bq) +{ + int ret; + u8 val = BQ2589X_BATFET_OFF << BQ2589X_BATFET_DIS_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_09, BQ2589X_BATFET_DIS_MASK, val); + return ret; + +} + +int bq2589x_enter_hiz_mode(struct bq2589x *bq) +{ + u8 val = BQ2589X_HIZ_ENABLE << BQ2589X_ENHIZ_SHIFT; + + return bq2589x_update_bits(bq, BQ2589X_REG_00, BQ2589X_ENHIZ_MASK, val); + +} + +int bq2589x_exit_hiz_mode(struct bq2589x *bq) +{ + + u8 val = BQ2589X_HIZ_DISABLE << BQ2589X_ENHIZ_SHIFT; + + return bq2589x_update_bits(bq, BQ2589X_REG_00, BQ2589X_ENHIZ_MASK, val); + +} + +int bq2589x_get_hiz_mode(struct bq2589x *bq, u8 *state) +{ + u8 val; + int ret; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_00); + if (ret) + return ret; + *state = (val & BQ2589X_ENHIZ_MASK) >> BQ2589X_ENHIZ_SHIFT; + + return 0; +} + +int bq2589x_is_enabled_charging(struct charger_device *chg_dev, bool *en) +{ + u8 val; + int ret; + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_03); + if (ret) + return ret; + *en = ((val & BQ2589X_CHG_CONFIG_MASK) >> BQ2589X_CHG_CONFIG_SHIFT)? true:false; + + return 0; +} + +int bq2589x_pumpx_enable(struct bq2589x *bq, int enable) +{ + u8 val; + int ret; + int retry_cnt; + + if (enable) + val = BQ2589X_PUMPX_ENABLE << BQ2589X_EN_PUMPX_SHIFT; + else + val = BQ2589X_PUMPX_DISABLE << BQ2589X_EN_PUMPX_SHIFT; + + for (retry_cnt = 3; retry_cnt > 0; retry_cnt--) { + ret = bq2589x_update_bits(bq, BQ2589X_REG_04, BQ2589X_EN_PUMPX_MASK, val); + if(!ret) + break; + msleep(10); + dev_info(bq->dev,"%s:enable pumpx:%d retry_cnt=%d\n", __func__, enable,retry_cnt); + } + + return ret; +} + +int bq2589x_pumpx_increase_volt(struct bq2589x *bq) +{ + u8 val; + int ret; + + val = BQ2589X_PUMPX_UP << BQ2589X_PUMPX_UP_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_09, BQ2589X_PUMPX_UP_MASK, val); + + return ret; + +} + +int bq2589x_pumpx_increase_volt_done(struct bq2589x *bq) +{ + u8 val; + int ret; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_09); + if (ret) + return ret; + + if (val & BQ2589X_PUMPX_UP_MASK) + return 1; /* not finished*/ + else + return 0; /* pumpx up finished*/ + +} + +int bq2589x_pumpx_decrease_volt(struct bq2589x *bq) +{ + u8 val; + int ret; + + val = BQ2589X_PUMPX_DOWN << BQ2589X_PUMPX_DOWN_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_09, BQ2589X_PUMPX_DOWN_MASK, val); + + return ret; + +} + +int bq2589x_pumpx_decrease_volt_done(struct bq2589x *bq) +{ + u8 val; + int ret; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_09); + if (ret) + return ret; + + if (val & BQ2589X_PUMPX_DOWN_MASK) + return 1; /* not finished*/ + else + return 0; /* pumpx down finished*/ + +} + +static int bq2589x_force_ico(struct bq2589x *bq) +{ + u8 val; + int ret; + + val = BQ2589X_FORCE_ICO << BQ2589X_FORCE_ICO_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_09, BQ2589X_FORCE_ICO_MASK, val); + + return ret; +} + +static int bq2589x_check_force_ico_done(struct bq2589x *bq) +{ + u8 val; + int ret; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_14); + if (ret) + return ret; + + if (val & BQ2589X_ICO_OPTIMIZED_MASK) + return 1; /*finished*/ + else + return 0; /* in progress*/ +} + +static int bq2589x_enable_term(struct bq2589x* bq, bool enable) +{ + u8 val; + int ret; + + if (enable) + val = BQ2589X_TERM_ENABLE << BQ2589X_EN_TERM_SHIFT; + else + val = BQ2589X_TERM_DISABLE << BQ2589X_EN_TERM_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_07, BQ2589X_EN_TERM_MASK, val); + + return ret; +} + +static int bq2589x_enable_auto_dpdm(struct bq2589x* bq, bool enable) +{ + u8 val; + int ret; + + if (enable) + val = BQ2589X_AUTO_DPDM_ENABLE << BQ2589X_AUTO_DPDM_EN_SHIFT; + else + val = BQ2589X_AUTO_DPDM_DISABLE << BQ2589X_AUTO_DPDM_EN_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_02, BQ2589X_AUTO_DPDM_EN_MASK, val); + + return ret; + +} + +static int bq2589x_use_absolute_vindpm(struct bq2589x* bq, bool enable) +{ + u8 val; + int ret; + + if (enable) + val = BQ2589X_FORCE_VINDPM_ENABLE << BQ2589X_FORCE_VINDPM_SHIFT; + else + val = BQ2589X_FORCE_VINDPM_DISABLE << BQ2589X_FORCE_VINDPM_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_0D, BQ2589X_FORCE_VINDPM_MASK, val); + + return ret; + +} + +static int bq2589x_enable_ico(struct bq2589x* bq, bool enable) +{ + u8 val; + int ret; + + if (enable) + val = BQ2589X_ICO_ENABLE << BQ2589X_ICOEN_SHIFT; + else + val = BQ2589X_ICO_DISABLE << BQ2589X_ICOEN_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_02, BQ2589X_ICOEN_MASK, val); + + return ret; + +} + + +static int bq2589x_read_idpm_limit(struct bq2589x *bq) +{ + uint8_t val; + int curr; + int ret; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_13); + if (ret < 0) { + dev_err(bq->dev, "read vbus voltage failed :%d\n", ret); + return ret; + } else{ + curr = BQ2589X_IDPM_LIM_BASE + ((val & BQ2589X_IDPM_LIM_MASK) >> BQ2589X_IDPM_LIM_SHIFT) * BQ2589X_IDPM_LIM_LSB ; + return curr; + } +} + +/*static bool bq2589x_is_charge_done(struct bq2589x *bq) +{ + int ret; + u8 val; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_0B); + if (ret < 0) { + dev_err(bq->dev, "%s:read REG0B failed :%d\n", __func__, ret); + return false; + } + val &= BQ2589X_CHRG_STAT_MASK; + val >>= BQ2589X_CHRG_STAT_SHIFT; + + return (val == BQ2589X_CHRG_STAT_CHGDONE); +}*/ + +int bq2589x_set_maxcharge_en(struct bq2589x *bq, bool enable) +{ + u8 val; + + if (enable) + val = BQ2589X_MAXC_ENABLE << BQ2589X_MAXCEN_SHIFT; + else + val = BQ2589X_MAXC_DISABLE << BQ2589X_MAXCEN_SHIFT; + + return bq2589x_update_bits(bq, BQ2589X_REG_02, BQ2589X_MAXCEN_MASK, val); +} + +int bq2589x_set_hvdcp_en(struct bq2589x *bq, bool enable) +{ + u8 val; + + if (enable) + val = BQ2589X_HVDCP_ENABLE << BQ2589X_HVDCPEN_SHIFT; + else + val = BQ2589X_HVDCP_DISABLE << BQ2589X_HVDCPEN_SHIFT; + + return bq2589x_update_bits(bq, BQ2589X_REG_02, BQ2589X_HVDCPEN_MASK, val); +} + +static int bq2589x_get_vindpm_volt(struct bq2589x *bq) +{ + uint8_t val; + int volt; + int ret; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_0D); + if (ret < 0) { + dev_err(bq->dev, "read vindpm failed :%d\n", ret); + return ret; + } else{ + volt = BQ2589X_VINDPM_BASE + ((val & BQ2589X_VINDPM_MASK) >> BQ2589X_VINDPM_SHIFT) * BQ2589X_VINDPM_LSB ; + return volt; + } +} + +static int bq2589x_sync_state(struct bq2589x *bq, struct bq2589x_state *state) +{ + u8 chrg_stat, volt_stat, fault; + u8 chrg_param_0,chrg_param_1,chrg_param_2; + int ret; + + ret = bq2589x_read_byte(bq, &chrg_stat, BQ2589X_REG_0B); + if (ret < 0){ + ret = bq2589x_read_byte(bq, &chrg_stat, BQ2589X_REG_0B); + if (ret < 0){ + dev_err(bq->dev, "read bq2589x REG 0B failed :%d\n", ret); + return ret; + } + } + + ret = bq2589x_read_byte(bq, &volt_stat, BQ2589X_REG_0E); + if (ret < 0){ + dev_err(bq->dev, "read bq2589x REG 0E failed :%d\n", ret); + return ret; + } + state->chrg_type = (chrg_stat & BQ2589X_VBUS_STAT_MASK) >> BQ2589X_VBUS_STAT_SHIFT; + state->chrg_stat = (chrg_stat & BQ2589X_CHRG_STAT_MASK) >> BQ2589X_CHRG_STAT_SHIFT; + state->online = (chrg_stat & BQ2589X_PG_STAT_MASK) >> BQ2589X_PG_STAT_SHIFT; + state->vsys_stat = (chrg_stat & BQ2589X_VSYS_STAT_MASK) >> BQ2589X_VSYS_STAT_SHIFT; + state->therm_stat = (volt_stat & BQ2589X_THERM_STAT_MASK) >> BQ2589X_THERM_STAT_SHIFT; + + dev_info(bq->dev, "%s chrg_stat =%d,chrg_type =%d online = %d\n",__func__,state->chrg_stat,state->chrg_type,state->online); + + ret = bq2589x_read_byte(bq, &fault, BQ2589X_REG_0C); + if (ret < 0){ + dev_err(bq->dev, "read bq2589x REG 0C failed :%d\n", ret); + return ret; + } + state->chrg_fault = fault; + state->ntc_fault = (fault & BQ2589X_FAULT_NTC_MASK) >> BQ2589X_FAULT_NTC_SHIFT; + state->health = state->ntc_fault; + + ret = bq2589x_read_byte(bq, &chrg_param_0, BQ2589X_REG_00); + if (ret < 0){ + dev_err(bq->dev, "read bq2589x REG 00 failed :%d\n", ret); + return ret; + } + state->hiz_en = (chrg_param_0 & BQ2589X_ENHIZ_MASK) >> BQ2589X_ENHIZ_SHIFT; + + ret = bq2589x_read_byte(bq, &chrg_param_1, BQ2589X_REG_07); + if (ret < 0){ + dev_err(bq->dev, "read bq2589x REG 07 failed :%d\n", ret); + return ret; + } + state->term_en = (chrg_param_1 & BQ2589X_EN_TERM_MASK) >> BQ2589X_EN_TERM_SHIFT; + + ret = bq2589x_read_byte(bq, &chrg_param_2, BQ2589X_REG_11); + if (ret < 0){ + dev_err(bq->dev, "read bq2589x REG 11 failed :%d\n", ret); + return ret; + } + state->vbus_gd = (chrg_param_2 & BQ2589X_VBUS_GD_MASK) >> BQ2589X_VBUS_GD_SHIFT; + + return 0; +} + +static int bq2589x_init_device(struct bq2589x *bq) +{ + int ret; + + /*common initialization*/ + if (bq->part_no == SC89890H) { + bq2589x_update_bits(bq, BQ2589X_REG_00, BQ2589X_ENILIM_MASK, + BQ2589X_ENILIM_DISABLE << BQ2589X_ENILIM_SHIFT); + } + + bq2589x_disable_watchdog_timer(bq); + + /*disable maxcharge en to allow qc2.0 detection*/ + bq2589x_set_maxcharge_en(bq, false); + + bq2589x_set_hvdcp_en(bq, false); + + bq2589x_enable_auto_dpdm(bq, bq->cfg.enable_auto_dpdm); + bq2589x_enable_term(bq, bq->cfg.enable_term); + bq2589x_enable_ico(bq, bq->cfg.enable_ico); + /*force use absolute vindpm if auto_dpdm not enabled*/ + if (!bq->cfg.enable_auto_dpdm) + bq->cfg.use_absolute_vindpm = true; + bq2589x_use_absolute_vindpm(bq, bq->cfg.use_absolute_vindpm); + + ret = bq2589x_set_term_current(bq, bq->cfg.term_current); + if (ret < 0) { + dev_err(bq->dev, "%s:Failed to set termination current:%d\n", __func__, ret); + return ret; + } + + ret = bq2589x_set_chargevoltage(bq, bq->cfg.charge_voltage); + if (ret < 0) { + dev_err(bq->dev, "%s:Failed to set charge voltage:%d\n", __func__, ret); + return ret; + } + + ret = bq2589x_set_chargecurrent(bq, bq->cfg.charge_current); + if (ret < 0) { + dev_err(bq->dev, "%s:Failed to set charge current:%d\n", __func__, ret); + return ret; + } + + ret = bq2589x_enable_charger(bq); + if (ret < 0) { + dev_err(bq->dev, "%s:Failed to enable charger:%d\n", __func__, ret); + return ret; + } + + if(bq->sc8989_adc_stop) + bq2589x_adc_stop(bq); + else + bq2589x_adc_start(bq, false); + + ret = bq2589x_pumpx_enable(bq, 0); + if (ret) { + dev_err(bq->dev, "%s:Failed to enable pumpx:%d\n", __func__, ret); + return ret; + } + + /*common initialization*/ + if (bq->cfg.disable_ilim) { + ret = bq2589x_update_bits(bq, BQ2589X_REG_00, BQ2589X_ENILIM_MASK, + BQ2589X_ENILIM_DISABLE << BQ2589X_ENILIM_SHIFT); + if (ret < 0) { + dev_err(bq->dev, "%s:Failed to operate EN_LIM for ENLIM_DISABLE:%d\n", __func__, ret); + } else { + dev_err(bq->dev, "%s:Success to operate EN_LIM for ENLIM_DISABLE:%d\n", __func__, ret); + } + } + + bq2589x_set_watchdog_timer(bq, 160); + + return ret; +} + +#ifdef CONFIG_MMI_QC3P_TURBO_CHARGER +static int bq2589x_set_recharge_volt(struct bq2589x *bq, int recharge_volt) +{ + int val; + dev_err(bq->dev, "%s:%d", __func__, recharge_volt); + val = (recharge_volt - BQ2589X_VRECHRG_OFFSET_MV) / BQ2589X_VRECHRG_STEP_MV; + + return bq2589x_update_bits(bq, BQ2589X_REG_06, + BQ2589X_VRECHG_MASK, val); +} +#endif + +/* +static int bq2589x_charge_status(struct bq2589x *bq) +{ + u8 val = 0; + + bq2589x_read_byte(bq, &val, BQ2589X_REG_0B); + val &= BQ2589X_CHRG_STAT_MASK; + val >>= BQ2589X_CHRG_STAT_SHIFT; + switch (val) { + case BQ2589X_CHRG_STAT_FASTCHG: + return POWER_SUPPLY_CHARGE_TYPE_FAST; + case BQ2589X_CHRG_STAT_PRECHG: + return POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + case BQ2589X_CHRG_STAT_CHGDONE: + case BQ2589X_CHRG_STAT_IDLE: + return POWER_SUPPLY_CHARGE_TYPE_NONE; + default: + return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + } +}*/ + +static int bq2589x_power_supply_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct bq2589x *bq = power_supply_get_drvdata(psy); + struct bq2589x_state state = bq->state; + u8 chrg_status = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = BQ2589x_MANUFACTURER; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + if (bq->part_no== BQ25890) + val->strval = "BQ25890"; + else if (bq->part_no== BQ25892) + val->strval = "BQ25892"; + else if (bq->part_no== BQ25895) + val->strval = "BQ25895"; + else + val->strval = "UNKNOWN"; + break; + case POWER_SUPPLY_PROP_STATUS: + chrg_status = bq2589x_get_charging_status(bq); + if (!state.online) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else if (chrg_status == STATUS_NOT_CHARGING || chrg_status == STATUS_TERMINATION_DONE) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + else if (chrg_status == STATUS_PRE_CHARGING || chrg_status == STATUS_FAST_CHARGING) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = state.online; + break; + case POWER_SUPPLY_PROP_HEALTH: + if (state.chrg_fault & 0xF8) + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + else + val->intval = POWER_SUPPLY_HEALTH_GOOD; + + switch (state.health) { + case 6: /* 110 - TS Hot */ + val->intval = POWER_SUPPLY_HEALTH_HOT; + break; + case 2: /* 010 - TS Warm */ + val->intval = POWER_SUPPLY_HEALTH_WARM; + break; + case 3: /* 011 - TS Cool */ + val->intval = POWER_SUPPLY_HEALTH_COOL; + break; + case 5: /* 101 - TS Cold */ + val->intval = POWER_SUPPLY_HEALTH_COLD; + break; + } + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + mmi_get_usb_voltage_now(bq, &val->intval); + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = bq2589x_adc_read_charge_current(bq); + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + val->intval = bq2589x_read_idpm_limit(bq); + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + val->intval = bq2589x_get_vindpm_volt(bq); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + chrg_status = bq2589x_get_charging_status(bq); + switch (chrg_status) { + case STATUS_PRE_CHARGING: + val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case STATUS_FAST_CHARGING: + val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; + break; + case STATUS_TERMINATION_DONE: + val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case STATUS_NOT_CHARGING: + val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + default: + val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + } + break; + case POWER_SUPPLY_PROP_USB_TYPE: + switch (state.chrg_type) { + case 0: /* 000 - No Input */ + val->intval = POWER_SUPPLY_USB_TYPE_UNKNOWN; + break; + case 1: /* 001 - USB Host SDP */ + val->intval = POWER_SUPPLY_USB_TYPE_SDP; + break; + case 2: /* 010 - USB CDP */ + val->intval = POWER_SUPPLY_USB_TYPE_CDP; + break; + case 3: /* 011 - USB DCP */ + val->intval = POWER_SUPPLY_USB_TYPE_DCP; + break; + case 4: /* 100 - Adjustable High Voltage DCP */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + val->intval = QTI_POWER_SUPPLY_TYPE_USB_HVDCP; +#else + val->intval = POWER_SUPPLY_TYPE_USB_HVDCP; +#endif + break; + case 5: /* 101 - Unkown Adapter */ + val->intval = POWER_SUPPLY_USB_TYPE_UNKNOWN; + break; + case 6: /* 110 - Non-Standard Adapter */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + val->intval = QTI_POWER_SUPPLY_TYPE_USB_FLOAT; +#else + val->intval = POWER_SUPPLY_TYPE_USB_FLOAT; +#endif + break; + default: + val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + } + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = state.vbus_gd; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int bq2589x_set_icl(struct charger_device *chg_dev, u32 uA); + +static int bq2589x_power_supply_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct bq2589x *bq = power_supply_get_drvdata(psy); + int ret = -EINVAL; + + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = bq2589x_set_icl(bq->chg_dev, val->intval); + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = bq2589x_set_input_volt_limit(bq, val->intval); + break; + default: + return -EINVAL; + } + + return ret; +} +static int bq2589x_power_supply_prop_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + return true; + default: + return false; + } +} + +static enum power_supply_property bq2589x_power_supply_props[] = { + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_PRESENT +}; + +static char *bq2589x_charger_supplied_to[] = { + "battery", +}; + +static const struct power_supply_desc bq2589x_power_supply_desc = { + .name = "charger", + .type = POWER_SUPPLY_TYPE_UNKNOWN, + .usb_types = bq2589x_usb_type, + .num_usb_types = ARRAY_SIZE(bq2589x_usb_type), + .properties = bq2589x_power_supply_props, + .num_properties = ARRAY_SIZE(bq2589x_power_supply_props), + .get_property = bq2589x_power_supply_get_property, + .set_property = bq2589x_power_supply_set_property, + .property_is_writeable = bq2589x_power_supply_prop_is_writeable, +}; + +static int bq2589x_psy_register(struct bq2589x *bq) +{ + struct power_supply_config psy_cfg = { .drv_data = bq, }; + + psy_cfg.supplied_to = bq2589x_charger_supplied_to; + psy_cfg.num_supplicants = ARRAY_SIZE(bq2589x_charger_supplied_to); + + bq->wall_psy = devm_power_supply_register(bq->dev, &bq2589x_power_supply_desc, &psy_cfg); + if (IS_ERR(bq->wall_psy)) + return -EINVAL; + + return 0; +} + +static void bq2589x_psy_unregister(struct bq2589x *bq) +{ + power_supply_unregister(bq->wall_psy); +} + +static ssize_t bq2589x_show_registers(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 addr; + u8 val; + u8 tmpbuf[300]; + int len; + int idx = 0; + int ret ; + + idx = snprintf(buf, PAGE_SIZE, "%s:\n", "Charger 1"); + for (addr = 0x0; addr <= 0x14; addr++) { + ret = bq2589x_read_byte(g_bq, &val, addr); + if (ret == 0) { + len = snprintf(tmpbuf, PAGE_SIZE - idx,"Reg[0x%.2x] = 0x%.2x\n", addr, val); + memcpy(&buf[idx], tmpbuf, len); + idx += len; + } + } + + return idx; +} + +static DEVICE_ATTR(registers, S_IRUGO, bq2589x_show_registers, NULL); + +static struct attribute *bq2589x_attributes[] = { + &dev_attr_registers.attr, + NULL, +}; + +static const struct attribute_group bq2589x_attr_group = { + .attrs = bq2589x_attributes, +}; + +static int mmi_get_iio_channel(struct bq2589x *bq, const char *propname, + struct iio_channel **chan) +{ + int rc = 0; + + rc = of_property_match_string(bq->dev->of_node, + "io-channel-names", propname); + if (rc < 0) + return 0; + + *chan = iio_channel_get(bq->dev, propname); + if (IS_ERR(*chan)) { + rc = PTR_ERR(*chan); + if (rc != -EPROBE_DEFER) + dev_err(bq->dev, "%s channel unavailable, %d\n", + propname, rc); + *chan = NULL; + } + + return rc; +} + +static int mmi_parse_dt_adc_channels(struct bq2589x *bq) +{ + int rc = 0; + + rc = mmi_get_iio_channel(bq, "gpio3_div3", + &bq->iio.usbin_v_chan); + if (rc < 0) + return rc; + + return 0; +} + +static int bq2589x_parse_dt(struct device *dev, struct bq2589x *bq) +{ + int ret; + struct device_node *np = dev->of_node; + + if (of_property_read_string(np, "charger_name", &bq->chg_dev_name) < 0) { + bq->chg_dev_name = "master_chg"; + dev_err(bq->dev,"%s: no charger name\n", __func__); + } + + bq->chg_en_gpio = of_get_named_gpio(np, "bq2589x_en-gpio", 0); + if (gpio_is_valid(bq->chg_en_gpio)) + { + ret = devm_gpio_request(bq->dev, bq->chg_en_gpio, "bq25890 chg en pin"); + if (ret) { + dev_err(bq->dev, "%s: %d gpio request failed\n", __func__, bq->chg_en_gpio); + return ret; + } + gpio_direction_output(bq->chg_en_gpio,0);//default enable charge + } + + /*wls outout en control*/ + bq->wls_en_gpio = of_get_named_gpio(bq->dev->of_node, "mmi,wls-en-gpio", 0); + if (gpio_is_valid(bq->wls_en_gpio)) { + ret = gpio_request(bq->wls_en_gpio, "mmi wls en pin"); + if (ret) { + dev_err(bq->dev, "%s: %d gpio(wls en) request failed\n", __func__, bq->wls_en_gpio); + return ret; + } + + gpio_direction_output(bq->wls_en_gpio, 0);//default enable wls charge + } + + ret = of_property_read_u32(np, "ti,bq2589x,vbus-volt-high-level", &pe.high_volt_level); + if (ret) + return ret; + + ret = of_property_read_u32(np, "ti,bq2589x,vbus-volt-low-level", &pe.low_volt_level); + if (ret) + return ret; + + ret = of_property_read_u32(np, "ti,bq2589x,vbat-min-volt-to-tuneup", &pe.vbat_min_volt); + if (ret) + return ret; + + bq->cfg.enable_auto_dpdm = of_property_read_bool(np, "ti,bq2589x,enable-auto-dpdm"); + bq->cfg.enable_term = of_property_read_bool(np, "ti,bq2589x,enable-termination"); + bq->cfg.enable_ico = of_property_read_bool(np, "ti,bq2589x,enable-ico"); + bq->cfg.disable_ilim = of_property_read_bool(np, "ti,bq2589x,disable-ilim"); + bq->cfg.use_absolute_vindpm = of_property_read_bool(np, "ti,bq2589x,use-absolute-vindpm"); + + /* enable dynamic adjust battery voltage */ + bq->enable_dynamic_adjust_batvol = of_property_read_bool(np, "enable_dynamic_adjust_batvol"); + dev_err(bq->dev, "%s: enable_dynamic_adjust_batvol = %d \n", __func__, bq->enable_dynamic_adjust_batvol); + + /* enable dynamic adjust vindpm */ + bq->enable_dynamic_adjust_vindpm = of_property_read_bool(np, "enable_dynamic_adjust_vindpm"); + dev_err(bq->dev, "%s: enable_dynamic_adjust_vindpm = %d \n", __func__, bq->enable_dynamic_adjust_vindpm); + if(bq->enable_dynamic_adjust_vindpm){ + bq->vindpm_flag = false; + } + + ret = of_property_read_u32(np, "ti,bq2589x,charge-voltage",&bq->cfg.charge_voltage); + if (ret) + return ret; + + ret = of_property_read_u32(np, "ti,bq2589x,charge-current",&bq->cfg.charge_current); + if (ret) + return ret; + + ret = of_property_read_u32(np, "ti,bq2589x,term-current",&bq->cfg.term_current); + if (ret) + return ret; + + ret = of_property_read_u32(np, "ti,bq2589x,vindpm",&bq->cfg.vindpm); + if (ret) + bq->cfg.vindpm = 4600; + + bq->mmi_qc3_support = of_property_read_bool(np, "mmi,qc3-support"); + + bq->sc8989_adc_stop = of_property_read_bool(np, "sc,sc8989_adc_stop"); + + ret = mmi_parse_dt_adc_channels(bq); + if (ret) { + dev_err(dev, "Failed to get adc channels%d\n", ret); + return ret; + } + + return 0; +} + +static int bq2589x_detect_device(struct bq2589x *bq) +{ + int ret; + u8 data; + + ret = bq2589x_read_byte(bq, &data, BQ2589X_REG_14); + if (ret == 0) { + bq->part_no = (data & BQ2589X_PN_MASK) >> BQ2589X_PN_SHIFT; + bq->revision = (data & BQ2589X_DEV_REV_MASK) >> BQ2589X_DEV_REV_SHIFT; + } + + return ret; +} + +static int bq2589x_read_batt_rsoc(struct bq2589x *bq) +{ + union power_supply_propval ret = {0,}; + + if (!bq->batt_psy) + bq->batt_psy = power_supply_get_by_name("bms"); + + if (bq->batt_psy) { + power_supply_get_property(bq->batt_psy, POWER_SUPPLY_PROP_CAPACITY, &ret); + return ret.intval; + } else { + return 50; + } +} + +int bq2589x_get_usb_present(struct bq2589x *bq) +{ + uint8_t val; + int ret; + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_11); + if (ret < 0) { + dev_err(bq->dev, "read vbus BQ2589X_REG_11 failed :%d\n", ret); + return ret; + } + + bq->state.vbus_gd = (val & BQ2589X_VBUS_GD_MASK) >> BQ2589X_VBUS_GD_SHIFT; + return ret; +} + +static bq2589x_reuqest_dpdm(struct bq2589x *bq, bool enable) +{ + int ret = 0; + + if(bq->ignore_request_dpdm) { + dev_err(bq->dev, "%s ignore_request_dpdm\n", __func__); + return ret; + } + mutex_lock(&bq->regulator_lock); + /* fetch the DPDM regulator */ + if (!bq->dpdm_reg && of_get_property(bq->dev->of_node, "dpdm-supply", NULL)) { + bq->dpdm_reg = devm_regulator_get(bq->dev, "dpdm"); + if (IS_ERR(bq->dpdm_reg)) { + ret = PTR_ERR(bq->dpdm_reg); + dev_err(bq->dev, "Couldn't get dpdm regulator ret=%d\n", ret); + bq->dpdm_reg = NULL; + mutex_unlock(&bq->regulator_lock); + return ret; + } + } + + + if (enable) { + if (bq->dpdm_reg && !bq->dpdm_enabled) { + dev_err(bq->dev, "enabling DPDM regulator\n"); + ret = regulator_enable(bq->dpdm_reg); + if (ret < 0) + dev_err(bq->dev, "Couldn't enable dpdm regulator ret=%d\n", ret); + else + bq->dpdm_enabled = true; + } + } else { + if (bq->dpdm_reg && bq->dpdm_enabled) { + dev_err(bq->dev, "disabling DPDM regulator\n"); + ret = regulator_disable(bq->dpdm_reg); + if (ret < 0) + dev_err(bq->dev, "Couldn't disable dpdm regulator ret=%d\n", ret); + else + bq->dpdm_enabled = false; + } + } + mutex_unlock(&bq->regulator_lock); + + return ret; +} + +void bq2589x_set_ilim_enable(struct bq2589x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2589X_ENILIM_ENABLE << BQ2589X_ENILIM_SHIFT; + else + val = BQ2589X_ENILIM_DISABLE << BQ2589X_ENILIM_SHIFT; + + dev_info(bq->dev, "%s:set ILIM %s\n", __func__, enable ? "enabled":"disabled"); + + ret = bq2589x_update_bits(bq, BQ2589X_REG_00, BQ2589X_ENILIM_MASK, val); + if (ret < 0) + dev_err(bq->dev, "fail to set ILIM pin ret=%d\n", ret); +} + +static bool bq2589x_is_rerun_apsd_complete(struct bq2589x *bq) +{ + int ret; + u8 val = 0; + bool result = false; + + ret = bq2589x_read_byte(bq, &val, BQ2589X_REG_02); + if (ret) + return false; + + result = !(val & BQ2589X_FORCE_DPDM_MASK); + pr_info("%s:rerun apsd %s", __func__, result ? "complete" : "not complete"); + return result; +} + +static int bq2589x_rerun_apsd_if_required(struct bq2589x *bq) +{ + int ret = 0; + ret = bq2589x_get_usb_present(bq); + if (ret) + return ret; + + if (!bq->state.vbus_gd) + return 0; + + ret = bq2589x_reuqest_dpdm(bq, true); + if (ret < 0) + dev_err(bq->dev,"%s:cannot enable DPDM ret=%d\n",__func__, ret); + + bq2589x_force_dpdm(bq); + + bq->pulse_cnt = 0; + bq->typec_apsd_rerun_done = true; + + dev_info(bq->dev,"rerun apsd done\n"); + return 0; +} + +static void bq2589x_adjust_absolute_vindpm(struct bq2589x *bq) +{ + u16 vbus_volt; + u16 vindpm_volt; + int ret; + + ret = bq2589x_disable_charger(bq); + if (ret < 0) { + dev_err(bq->dev,"%s:failed to disable charger\n",__func__); + /*return;*/ + } + /* wait for new adc data */ + msleep(1000); + vbus_volt = bq2589x_adc_read_vbus_volt(bq); + ret = bq2589x_enable_charger(bq); + if (ret < 0) { + dev_err(bq->dev, "%s:failed to enable charger\n",__func__); + return; + } + + if (vbus_volt < 6000) + vindpm_volt = vbus_volt - 600; + else + vindpm_volt = vbus_volt - 1200; + ret = bq2589x_set_input_volt_limit(bq, vindpm_volt); + if (ret < 0) + dev_err(bq->dev, "%s:Set absolute vindpm threshold %d Failed:%d\n", __func__, vindpm_volt, ret); + else + dev_info(bq->dev, "%s:Set absolute vindpm threshold %d successfully\n", __func__, vindpm_volt); + +} + +#ifndef CONFIG_MMI_QC3P_WT6670_DETECTED +static int mmi_reset_dpdm(struct bq2589x *bq) +{ + int ret; + int dp_val, dm_val; + + /* dp HIZ and dm HIZ */ + + dm_val = 0x0<dev, "vbus voltage now = %d\n", vbus_voltage); + + if (vbus_voltage > MMI_HVDCP2_VOLTAGE_STANDARD) { + dev_info(bq->dev, "QC20 charger detected\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + *charger_type = QTI_POWER_SUPPLY_TYPE_USB_HVDCP; +#else + *charger_type = POWER_SUPPLY_TYPE_USB_HVDCP; +#endif + } else { + dev_info(bq->dev, "charger type is not HVDCP\n"); + } + + ret = mmi_adjust_qc20_hvdcp_5v(bq); + if (ret) { + dev_err(bq->dev, "Cann't adjust qc20 hvdcp 5V\n"); + } + + msleep(300);//need tunning + + mmi_get_usb_voltage_now(bq, &vbus_voltage); + dev_info(bq->dev, "vbus voltage now = %d after qc20 detected\n", vbus_voltage); + + return ret; +} +#endif + +// Must enter 3.0 mode to call ,otherwise cannot step correctly. +static int mmi_qc30_step_up_vbus(struct bq2589x *bq) +{ + int ret; + int dp_val; + + /* dm 3.3v to dm 0.6v step up 200mV when IC is QC3.0 mode*/ + dp_val = 0x6<dev, "%s qc30 step up vbus error\n", __func__); + } + + mdelay(100);//need tunning + + mmi_get_usb_voltage_now(bq, &vbus_voltage); + dev_info(bq->dev, "%s vbus voltage now = %d in detected qc30\n", __func__,vbus_voltage); + + if (vbus_voltage > MMI_HVDCP3_VOLTAGE_STANDARD) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + *charger_type = QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3; +#else + *charger_type = POWER_SUPPLY_TYPE_USB_HVDCP_3; +#endif + dev_info(bq->dev, "%s QC3.0 charger detected\n", __func__); + } + + for (i = 0; i < 16; i++) { + ret = mmi_qc30_step_down_vbus(bq); + if (ret) + dev_err(bq->dev, "%s qc30 step down vbus error\n", __func__); + } + +#ifndef CONFIG_MMI_QC3P_TURBO_CHARGER + mmi_get_usb_voltage_now(bq, &vbus_voltage); + dev_info(bq->dev, "%s vbus voltage now = %d after detected qc30\n", __func__,vbus_voltage); +#endif + + return ret; +} + +#ifdef CONFIG_MMI_QC3P_TURBO_CHARGER +static int bq2589x_detected_qc3p_hvdcp(struct bq2589x *bq, int *charger_type) +{ + int ret = 0; + int dp_val, dm_val; + int i=0, vbus_voltage; + + mdelay(100);//need tunning + + mmi_get_usb_voltage_now(bq, &vbus_voltage); + + if (vbus_voltage < QC3P_AUTHEN_LOW_THR_MV + || vbus_voltage > QC3P_AUTHEN_HIGH_THR_MV) { + bq->mmi_qc3p_power = MMI_POWER_SUPPLY_QC3P_NONE; + /*do qc3p rerun*/ + dev_err(bq->dev, "qc3p voltage is invalid, rerun qc3p detect\n"); + if (!bq->mmi_qc3p_rerun_done && !bq->pd_active) { + bq->mmi_qc3p_rerun_done = true; + bq->mmi_qc3p_wa = true; + dp_val = 0x0<dev, "%s qc3p step up vbus error\n", __func__); + + ret = mmi_qc30_step_down_vbus(bq); + if (ret) + dev_err(bq->dev, "%s qc3p step down vbus error\n", __func__); + + } + + mdelay(30);//need tuning + + mmi_get_usb_voltage_now(bq, &vbus_voltage); + + for (i = 0; i < 2; i++) { + ret = mmi_qc30_step_up_vbus(bq); + if (ret) + dev_err(bq->dev, "%s qc3p step up vbus error\n", __func__); + } + + for (i = 0; i < 2; i++) { + ret = mmi_qc30_step_down_vbus(bq); + if (ret) + dev_err(bq->dev, "%s qc3p step down vbus error\n", __func__); + } + + msleep(30); + + dev_info(bq->dev, "%s vbus voltage now = %d after detected qc3p\n", __func__,vbus_voltage); + + if (vbus_voltage > QC3P_AUTHEN_NONE_THR_MV) + bq->mmi_qc3p_power = MMI_POWER_SUPPLY_QC3P_NONE; + else if (vbus_voltage > QC3P_AUTHEN_45W_THR_MV) + bq->mmi_qc3p_power = MMI_POWER_SUPPLY_QC3P_45W; + else if (vbus_voltage > QC3P_AUTHEN_27W_THR_MV) + bq->mmi_qc3p_power = MMI_POWER_SUPPLY_QC3P_27W; + else if (vbus_voltage > QC3P_AUTHEN_18W_THR_MV) + bq->mmi_qc3p_power = MMI_POWER_SUPPLY_QC3P_18W; + else + bq->mmi_qc3p_power = MMI_POWER_SUPPLY_QC3P_NONE; + + if (bq->mmi_qc3p_power != MMI_POWER_SUPPLY_QC3P_NONE) { + *charger_type = POWER_SUPPLY_TYPE_USB_HVDCP_3P5; + dev_info(bq->dev, "%s detected qc3p, qc3p power = %d\n", + __func__,bq->mmi_qc3p_power); + } else { + dev_err(bq->dev, "qc3p power is invalid, rerun qc3p detect\n"); + //do rerun qc3p + if (!bq->mmi_qc3p_rerun_done && !bq->pd_active) { + bq->mmi_qc3p_rerun_done = true; + bq->mmi_qc3p_wa = true; + dp_val = 0x0<ext_iio_chans[chan])) + return false; + + if (!chip->ext_iio_chans[chan]) { + chip->ext_iio_chans[chan] = iio_channel_get(chip->dev, + mmi_qc3p_ext_iio_chan_name[chan]); + if (IS_ERR(chip->ext_iio_chans[chan])) { + rc = PTR_ERR(chip->ext_iio_chans[chan]); + if (rc == -EPROBE_DEFER) + chip->ext_iio_chans[chan] = NULL; + + dev_info(chip->dev, "Failed to get IIO channel %s, rc=%d\n", + mmi_qc3p_ext_iio_chan_name[chan], rc); + return false; + } + } + + return true; +} + +int mmi_charger_read_iio_chan(struct bq2589x *chip, + enum mmi_qc3p_ext_iio_channels chan, int *val) +{ + int rc; + + if (is_chan_valid(chip, chan)) { + rc = iio_read_channel_processed( + chip->ext_iio_chans[chan], val); + return (rc < 0) ? rc : 0; + } + + return -EINVAL; +} + +int mmi_charger_write_iio_chan(struct bq2589x *chip, + enum mmi_qc3p_ext_iio_channels chan, int val) +{ + if (is_chan_valid(chip, chan)) + return iio_write_channel_raw(chip->ext_iio_chans[chan], + val); + + return -EINVAL; +} + +static int mmi_init_iio_psy(struct bq2589x *chip, + struct device *dev) +{ + chip->ext_iio_chans = devm_kcalloc(chip->dev, + ARRAY_SIZE(mmi_qc3p_ext_iio_chan_name), + sizeof(*chip->ext_iio_chans), + GFP_KERNEL); + if (!chip->ext_iio_chans) + return -ENOMEM; + + return 0; +} + +int qc3p_start_detection(struct bq2589x *chip) +{ + int ret = 0; + ret = mmi_charger_write_iio_chan(chip, SMB5_QC3P_START_DETECT, true); + if(ret ) + dev_err(chip->dev, "Cann't write SMB5_QC3P_START_DETECT IIO\n"); + + dev_info(chip->dev, "write SMB5_QC3P_START_DETECT IIO success\n"); + return 0; +} + +bool qc3p_detection_done(struct bq2589x *chip) +{ + int ret = 0; + int val = 0; + int delay_count =0; + + do { + ret = mmi_charger_read_iio_chan(chip, SMB5_QC3P_DETECTION_READY, &val); + if(ret ) + dev_err(chip->dev, "Cann't read SMB5_QC3P_DETECTION_READY IIO\n"); + + dev_info(chip->dev, "read SMB5_QC3P_DETECTION_READY IIO :%d\n",val); + + msleep(100); + delay_count ++; + }while(val == false && delay_count <= 35); + + dev_info(chip->dev, "read SMB5_QC3P_DETECTION_READY IIO :%d\n",val); + return val; +} + +int qc3p_read_charger_type(struct bq2589x *chip) +{ + int ret = 0; + int val = 0; + + ret = mmi_charger_read_iio_chan(chip, SMB5_USB_REAL_TYPE, &val); + if(ret ) + dev_err(chip->dev, "Cann't read SMB5_USB_REAL_TYPE IIO\n"); + + dev_info(chip->dev, "read SMB5_USB_REAL_TYPE IIO :%d\n",val); + return val; +} + +bool qc3p_update_policy(struct bq2589x *chip ) +{ + int ret = 0; + int val = 0; + + ret = mmi_charger_write_iio_chan(chip, SMB5_QC3P_START_POLICY, val); + if(ret ) + dev_err(chip->dev, "Cann't write SMB5_QC3P_START_POLICY IIO\n"); + + dev_info(chip->dev, "write SMB5_QC3P_START_POLICY IIO :%d\n",val); + return val; +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +int bq2589x_enable_termination(struct charger_device *chg_dev, bool enable) +#else +static int bq2589x_enable_termination(struct charger_device *chg_dev, bool enable) +#endif +{ + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + u8 val; + int ret; + if (enable) + val = BQ2589X_TERM_ENABLE << BQ2589X_EN_TERM_SHIFT; + else + val = BQ2589X_TERM_DISABLE << BQ2589X_EN_TERM_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_07, BQ2589X_EN_TERM_MASK, val); + dev_info(bq->dev, "%s, %s enable term %s\n", __func__, + enable ? "enable" : "disable", + ret == 0 ? "success" : "failed"); + return ret; +} +EXPORT_SYMBOL_GPL(bq2589x_enable_termination); + +static int mmi_hvdcp_detect_kthread(void *param) +{ + struct bq2589x * bq = param; + int ret; + int charger_type = POWER_SUPPLY_TYPE_UNKNOWN; + struct sched_param sch_param = {.sched_priority = MAX_RT_PRIO-1}; + + sched_setscheduler(current, SCHED_FIFO, &sch_param); + + do { + + wait_event_interruptible(bq->mmi_qc3_wait_que, bq->mmi_qc3_trig_flag || kthread_should_stop()); + if (kthread_should_stop()) + break; + + down(&bq->sem_dpdm); + dev_info(bq->dev, "mmi_hvdcp_detect_kthread begin\n"); + bq->mmi_qc3_trig_flag = false; + bq->mmi_is_qc3_authen = true; + bq2589x_set_input_current_limit(bq, MMI_HVDCP_DETECT_ICL_LIMIT); + +#ifndef CONFIG_MMI_QC3P_WT6670_DETECTED + //do qc2.0 detected + ret = mmi_detected_qc20_hvdcp(bq, &charger_type); + if (ret) { + dev_err(bq->dev, "Cann't detected qc20 hvdcp\n"); + goto out; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + if (charger_type != QTI_POWER_SUPPLY_TYPE_USB_HVDCP || bq->pd_active) +#else + if (charger_type != POWER_SUPPLY_TYPE_USB_HVDCP || bq->pd_active) +#endif + goto out; + + //do qc3.0 detected + ret = mmi_detected_qc30_hvdcp(bq, &charger_type); + if (ret) { + dev_err(bq->dev, "Cann't detected qc30 hvdcp\n"); + } + +#ifdef CONFIG_MMI_QC3P_TURBO_CHARGER + //do qc3p detected + if (charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3 && !bq->pd_active) { + ret = bq2589x_detected_qc3p_hvdcp(bq, &charger_type); + if (ret) { + dev_err(bq->dev, "Cann't detected qc3p hvdcp\n"); + goto out; + } + } +#endif +#else + bq2589x_enable_termination(bq->chg_dev, true); + qc3p_start_detection(bq); + qc3p_detection_done(bq); + ret = qc3p_read_charger_type(bq); + + switch(ret) { + case WT_CHG_TYPE_QC2: + charger_type = POWER_SUPPLY_TYPE_USB_HVDCP; + dev_info(bq->dev, "HDVCP 2.0 detected\n"); + break; + case WT_CHG_TYPE_QC3: + case WT_CHG_TYPE_QC3P_18W: + charger_type = POWER_SUPPLY_TYPE_USB_HVDCP_3; + dev_info(bq->dev, "HDVCP 3.0 or qc3p 18W detected\n"); + break; + case WT_CHG_TYPE_QC3P_27W: + dev_err(bq->dev, "QC3P 27W have been detected !\n"); + charger_type = POWER_SUPPLY_TYPE_USB_HVDCP_3P5; + qc3p_update_policy(bq); + break; + default: + dev_info(bq->dev, "No HDVCP detected\n"); + goto out; + } +#endif + bq2589x_get_usb_present(bq); + if (!bq->state.vbus_gd || bq->pd_active) + goto out; + + bq->real_charger_type = charger_type; + //notify charging policy to update charger type + bq->chg_dev->noti.hvdcp_done = true; + charger_dev_notify(bq->chg_dev); + + out: + bq2589x_set_input_current_limit(bq, bq->input_current_cache); + bq->mmi_is_qc3_authen = false; + charger_type = POWER_SUPPLY_TYPE_UNKNOWN; + up(&bq->sem_dpdm); + dev_info(bq->dev, "mmi_hvdcp_detect_kthread end\n"); + }while(!kthread_should_stop()); + + dev_dbg(bq->dev, "qc3 kthread stop\n"); + return 0; +} + +static void mmi_start_hvdcp_detect(struct bq2589x *bq) +{ + + if (bq->mmi_qc3_support + && !bq->pd_active + && bq->real_charger_type == POWER_SUPPLY_TYPE_USB_DCP) { + //down(&bq->sem_dpdm); + dev_info(bq->dev, "start hvdcp detect\n"); + bq->mmi_qc3_trig_flag = true; + wake_up_interruptible(&bq->mmi_qc3_wait_que); + //up(&bq->sem_dpdm); + } + +} + +static void bq2589x_adapter_in_func(struct bq2589x *bq) +{ + int ret = 0; + + if ((bq->vbus_type == BQ2589X_VBUS_USB_SDP || + bq->vbus_type == BQ2589X_VBUS_NONSTAND || + bq->vbus_type == BQ2589X_VBUS_UNKNOWN) + && (!bq->typec_apsd_rerun_done)) { + down(&bq->sem_dpdm); + dev_err(bq->dev, "rerun apsd for 0x%x\n", bq->vbus_type); + bq2589x_rerun_apsd_if_required(bq); + up(&bq->sem_dpdm); + return; + } + +#ifdef CONFIG_MMI_QC3P_TURBO_CHARGER + if (true == bq->mmi_qc3p_wa) { + if (bq->vbus_type != BQ2589X_VBUS_USB_DCP) { + pr_err("BQ2589x charger type fix to DCP to workaroud BC1.2 rerun fail \n"); + bq->vbus_type = BQ2589X_VBUS_USB_DCP; + } + } +#endif + + switch (bq->vbus_type) { + case BQ2589X_VBUS_MAXC: + dev_info(bq->dev, "%s:HVDCP adapter plugged in\n", __func__); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + bq->real_charger_type = QTI_POWER_SUPPLY_TYPE_USB_HVDCP; +#else + bq->real_charger_type = POWER_SUPPLY_TYPE_USB_HVDCP; +#endif + bq->chg_dev->noti.hvdcp_done = true; + schedule_delayed_work(&bq->ico_work, 0); + break; + case BQ2589X_VBUS_USB_CDP: + dev_info(bq->dev, "%s:CDP plugged in\n", __func__); + bq->real_charger_type = POWER_SUPPLY_TYPE_USB_CDP; + break; + case BQ2589X_VBUS_USB_DCP: + dev_info(bq->dev, "%s:DCP adapter plugged in\n", __func__); + bq->real_charger_type = POWER_SUPPLY_TYPE_USB_DCP; + mmi_start_hvdcp_detect(bq); + schedule_delayed_work(&bq->check_pe_tuneup_work, 0); + break; + case BQ2589X_VBUS_USB_SDP: + dev_info(bq->dev, "%s:SDP plugged in\n", __func__); + bq->real_charger_type = POWER_SUPPLY_TYPE_USB; + break; + case BQ2589X_VBUS_UNKNOWN: + dev_err(bq->dev, "%s:Unkown adpater plugged in\n", __func__); + case BQ2589X_VBUS_NONSTAND: + dev_info(bq->dev, "%s:NON STAND plugged in\n", __func__); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + bq->real_charger_type = QTI_POWER_SUPPLY_TYPE_USB_FLOAT; +#else + bq->real_charger_type = POWER_SUPPLY_TYPE_USB_FLOAT; +#endif + break; + default: + dev_info(bq->dev, "%s:Other adapter %d plugged in\n", __func__, bq->vbus_type); + bq->real_charger_type = POWER_SUPPLY_TYPE_UNKNOWN; + schedule_delayed_work(&bq->ico_work, 0); + break; + } + + if (bq->cfg.use_absolute_vindpm) { + ret = bq2589x_use_absolute_vindpm(bq, true); + if (ret < 0) + dev_err(bq->dev,"%s:force vindpm failed:%d\n",__func__,ret); + + ret = bq2589x_set_input_volt_limit(bq, bq->cfg.vindpm); + if (ret < 0) + dev_err(bq->dev,"%s:reset vindpm threshold to %d failed:%d\n",__func__,bq->cfg.vindpm,ret); + else + dev_info(bq->dev,"%s:reset vindpm threshold to %d successfully\n",__func__,bq->cfg.vindpm); + } + + if(bq->sc8989_adc_stop){ + bq2589x_adc_start(bq, false); + } + + schedule_delayed_work(&bq->monitor_work, 0); + + bq->chg_dev->noti.apsd_done = true; + bq->typec_apsd_rerun_done = false; + charger_dev_notify(bq->chg_dev); + +} + +static void bq2589x_adapter_out_func(struct bq2589x *bq) +{ + int ret; + + ret = bq2589x_set_input_volt_limit(bq, bq->cfg.vindpm); + if (ret < 0) + dev_err(bq->dev,"%s:reset vindpm threshold to %d failed:%d\n",__func__,bq->cfg.vindpm,ret); + else + dev_info(bq->dev,"%s:reset vindpm threshold to %d successfully\n",__func__,bq->cfg.vindpm); + + cancel_delayed_work_sync(&bq->monitor_work); + +#ifndef CONFIG_MMI_QC3P_WT6670_DETECTED + if (bq->mmi_qc3_support) { + down(&bq->sem_dpdm); + mmi_reset_dpdm(bq); + up(&bq->sem_dpdm); + } +#endif + +#ifdef CONFIG_MMI_QC3P_TURBO_CHARGER + bq2589x_enable_termination(bq->chg_dev, true); +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED + qc3p_update_policy(bq); +#endif +#endif + bq->pulse_cnt = 0; + bq->mmi_qc3p_wa = false; + bq->mmi_qc3p_rerun_done = false; + bq->typec_apsd_rerun_done = false; + bq->chg_dev->noti.apsd_done = false; + bq->chg_dev->noti.hvdcp_done = false; + bq->real_charger_type = POWER_SUPPLY_TYPE_UNKNOWN; + bq->mmi_qc3p_power = MMI_POWER_SUPPLY_QC3P_NONE; + bq2589x_reuqest_dpdm(bq, false); + if(bq->sc8989_adc_stop){ + bq2589x_adc_stop(bq); + } + charger_dev_notify(bq->chg_dev); +} + +static void bq2589x_ico_workfunc(struct work_struct *work) +{ + struct bq2589x *bq = container_of(work, struct bq2589x, ico_work.work); + int ret; + int idpm; + u8 status; + static bool ico_issued; + + if (!bq->cfg.enable_ico) + return; + + if (!ico_issued) { + ret = bq2589x_force_ico(bq); + if (ret < 0) { + schedule_delayed_work(&bq->ico_work, HZ); /* retry 1 second later*/ + dev_info(bq->dev, "%s:ICO command issued failed:%d\n", __func__, ret); + } else { + ico_issued = true; + schedule_delayed_work(&bq->ico_work, 3 * HZ); + dev_info(bq->dev, "%s:ICO command issued successfully\n", __func__); + } + } else { + ico_issued = false; + ret = bq2589x_check_force_ico_done(bq); + if (ret) {/*ico done*/ + ret = bq2589x_read_byte(bq, &status, BQ2589X_REG_13); + if (ret == 0) { + idpm = ((status & BQ2589X_IDPM_LIM_MASK) >> BQ2589X_IDPM_LIM_SHIFT) * BQ2589X_IDPM_LIM_LSB + BQ2589X_IDPM_LIM_BASE; + dev_info(bq->dev, "%s:ICO done, result is:%d mA\n", __func__, idpm); + } + } + } +} + +static void bq2589x_check_pe_tuneup_workfunc(struct work_struct *work) +{ + struct bq2589x *bq = container_of(work, struct bq2589x, check_pe_tuneup_work.work); + + if (!pe.enable) { + schedule_delayed_work(&bq->ico_work, 0); + return; + } + + bq->vbat_volt = bq2589x_adc_read_battery_volt(bq); + bq->rsoc = bq2589x_read_batt_rsoc(bq); + + if (bq->vbat_volt > pe.vbat_min_volt && bq->rsoc < 95) { + dev_info(bq->dev, "%s:trying to tune up vbus voltage\n", __func__); + pe.target_volt = pe.high_volt_level; + pe.tune_up_volt = true; + pe.tune_down_volt = false; + pe.tune_done = false; + pe.tune_count = 0; + pe.tune_fail = false; + schedule_delayed_work(&bq->pe_volt_tune_work, 0); + } else if (bq->rsoc >= 95) { + schedule_delayed_work(&bq->ico_work, 0); + } else { + /* wait battery voltage up enough to check again */ + schedule_delayed_work(&bq->check_pe_tuneup_work, 2*HZ); + } +} + +static void bq2589x_tune_volt_workfunc(struct work_struct *work) +{ + struct bq2589x *bq = container_of(work, struct bq2589x, pe_volt_tune_work.work); + int ret = 0; + static bool pumpx_cmd_issued; + + bq->vbus_volt = bq2589x_adc_read_vbus_volt(bq); + + dev_info(bq->dev, "%s:vbus voltage:%d, Tune Target Volt:%d\n", __func__, bq->vbus_volt, pe.target_volt); + + if ((pe.tune_up_volt && bq->vbus_volt > pe.target_volt) || + (pe.tune_down_volt && bq->vbus_volt < pe.target_volt)) { + dev_info(bq->dev, "%s:voltage tune successfully\n", __func__); + pe.tune_done = true; + bq2589x_adjust_absolute_vindpm(bq); + if (pe.tune_up_volt) + schedule_delayed_work(&bq->ico_work, 0); + return; + } + + if (pe.tune_count > 10) { + dev_info(bq->dev, "%s:voltage tune failed,reach max retry count\n", __func__); + pe.tune_fail = true; + bq2589x_adjust_absolute_vindpm(bq); + + if (pe.tune_up_volt) + schedule_delayed_work(&bq->ico_work, 0); + return; + } + + if (!pumpx_cmd_issued) { + if (pe.tune_up_volt) + ret = bq2589x_pumpx_increase_volt(bq); + else if (pe.tune_down_volt) + ret = bq2589x_pumpx_decrease_volt(bq); + if (ret) { + schedule_delayed_work(&bq->pe_volt_tune_work, HZ); + } else { + dev_info(bq->dev, "%s:pumpx command issued.\n", __func__); + pumpx_cmd_issued = true; + pe.tune_count++; + schedule_delayed_work(&bq->pe_volt_tune_work, 3*HZ); + } + } else { + if (pe.tune_up_volt) + ret = bq2589x_pumpx_increase_volt_done(bq); + else if (pe.tune_down_volt) + ret = bq2589x_pumpx_decrease_volt_done(bq); + if (ret == 0) { + dev_info(bq->dev, "%s:pumpx command finishedd!\n", __func__); + bq2589x_adjust_absolute_vindpm(bq); + pumpx_cmd_issued = 0; + } + schedule_delayed_work(&bq->pe_volt_tune_work, HZ); + } +} + +/* enable dynamic adjust battery voltage */ +static void bq2589x_adjust_constant_voltage(struct bq2589x *bq, int vbat) +{ + int ret; + int cv_adjust; + u8 status = 0; + + dev_err(bq->dev, "bq2589x_adjust_constant_voltage\n"); + + if (bq->final_cv > vbat && (bq->final_cv - vbat) < 16000) + return; + + ret = bq2589x_read_byte(bq, &status, BQ2589X_REG_13); + dev_err(bq->dev, "ret = %d,status=%02x\n", ret,status); + if ((ret == 0) && ((status & BQ2589X_VDPM_STAT_MASK) || + (status & BQ2589X_IDPM_STAT_MASK)) + && (bq->final_cv > vbat)) + return; + + if (bq->final_cv > vbat) { + bq->cv_tune++; + } else if (bq->cv_tune > 0) { + bq->cv_tune--; + } + bq->cv_tune = min(bq->cv_tune, 6); //limit to max 6 - 96mV + dev_err(bq->dev, "cv_tune = %duV\n", bq->cv_tune*16000); + + cv_adjust = bq->cv_tune * BQ2589X_VREG_LSB * 1000; + cv_adjust += bq->final_cv; + cv_adjust /= 1000; + ret |= bq2589x_set_chargevoltage(bq, cv_adjust); + if (!ret) + return; + dev_err(bq->dev, "failed to tune cv, reset the tuning\n"); + bq->cv_tune = 0; + bq2589x_set_chargevoltage(bq, bq->final_cv/1000); + return; +} + +static void bq2589x_monitor_workfunc(struct work_struct *work) +{ + struct bq2589x *bq = container_of(work, struct bq2589x, monitor_work.work); + u8 status = 0; + int ret; + int chg_current; + u8 addr; + u8 val; + + /* enable dynamic adjust battery voltage */ + union power_supply_propval val_battery = {0}; + u8 chrg_status = 0; + int vbat_uv, ibat_ua; + + bq2589x_reset_watchdog_timer(bq); + + bq->rsoc = bq2589x_read_batt_rsoc(bq); + + bq->vbus_volt = bq2589x_adc_read_vbus_volt(bq); + bq->vbat_volt = bq2589x_adc_read_battery_volt(bq); + chg_current = bq2589x_adc_read_charge_current(bq); + + dev_info(bq->dev, "%s:vbus volt:%d,vbat volt:%d,charge current:%d\n", __func__,bq->vbus_volt,bq->vbat_volt,chg_current); + + ret = bq2589x_read_byte(bq, &status, BQ2589X_REG_13); + if (ret == 0 && (status & BQ2589X_VDPM_STAT_MASK)) + dev_info(bq->dev, "%s:VINDPM occurred\n", __func__); + if (ret == 0 && (status & BQ2589X_IDPM_STAT_MASK)) + dev_info(bq->dev, "%s:IINDPM occurred\n", __func__); + + /* enable dynamic adjust battery voltage */ + if(bq->enable_dynamic_adjust_batvol){ + if (!bq->battery) + bq->battery = power_supply_get_by_name ("battery"); + if (bq->battery) { + power_supply_get_property(bq->battery, POWER_SUPPLY_PROP_CURRENT_NOW, &val_battery); + chrg_status = bq2589x_get_charging_status(bq); + if(val_battery.intval < 10000 && chrg_status != STATUS_TERMINATION_DONE) { + pr_err("VBUS present but no charging current \n"); + } + + ibat_ua = val_battery.intval; + power_supply_get_property(bq->battery, POWER_SUPPLY_PROP_VOLTAGE_NOW, &val_battery); + vbat_uv = val_battery.intval; + dev_err(bq->dev, "vbat=%duV, cv=%duV\n", vbat_uv, bq->final_cv); + dev_err(bq->dev, "ibat=%duA, cc=%duA,tune=%d\n", ibat_ua, bq->final_cc,bq->cv_tune); + if ((ibat_ua > 10000 && ibat_ua < (bq->final_cc - 100000)) || vbat_uv > bq->final_cv) { + bq2589x_adjust_constant_voltage(bq, vbat_uv); + } + } + } + + /* enable dynamic adjust vindpm */ + if(bq->enable_dynamic_adjust_vindpm){ + if (!bq->battery) + bq->battery = power_supply_get_by_name ("battery"); + if (bq->battery) { + if(bq->rsoc > 80 && bq->vindpm_flag == false){ + ret = bq2589x_set_input_volt_limit(bq, 4700); + bq->vindpm_flag = true; + } + if(bq->rsoc <= 80 && bq->vindpm_flag){ + ret = bq2589x_set_input_volt_limit(bq, bq->cfg.vindpm); + bq->vindpm_flag = false; + } + } + } + + for (addr = 0x0; addr <= 0x14; addr++) { + ret = bq2589x_read_byte(bq, &val, addr); + if (ret == 0) { + dev_info(bq->dev, "Reg[0x%.2x] = 0x%.2x\n", addr, val); + } + } + + if (bq->vbus_type == BQ2589X_VBUS_USB_DCP && bq->vbus_volt > pe.high_volt_level && + bq->rsoc > 95 && !pe.tune_down_volt && pe.enable) { + pe.tune_down_volt = true; + pe.tune_up_volt = false; + pe.target_volt = pe.low_volt_level; + pe.tune_done = false; + pe.tune_count = 0; + pe.tune_fail = false; + schedule_delayed_work(&bq->pe_volt_tune_work, 0); + } + + /* read temperature,or any other check if need to decrease charge current*/ + schedule_delayed_work(&bq->monitor_work, 10 * HZ); +} + + + +static void bq2589x_charger_irq_workfunc(struct work_struct *work) +{ + struct bq2589x *bq = container_of(work, struct bq2589x, irq_work); + struct bq2589x_state state; + u8 status = 0; + u8 fault = 0; + u8 charge_status = 0; + int ret; + bool reapsd_complete = false; + + if (!bq->dpdm_enabled) { + bq2589x_reuqest_dpdm(bq, true); + } + + ret = bq2589x_sync_state(bq, &state); + if (ret < 0) + return; + mutex_lock(&bq->lock); + bq->state = state; + mutex_unlock(&bq->lock); + + /* Read STATUS and FAULT registers */ + ret = bq2589x_read_byte(bq, &status, BQ2589X_REG_0B); + if (ret) + return; + + ret = bq2589x_read_byte(bq, &fault, BQ2589X_REG_0C); + if (ret) + return; + + bq->vbus_type = (status & BQ2589X_VBUS_STAT_MASK) >> BQ2589X_VBUS_STAT_SHIFT; + + if(state.vbus_gd && state.online + && (bq->typec_apsd_rerun_done == true) + && (bq->vbus_type != BQ2589X_VBUS_NONE)) { + reapsd_complete = bq2589x_is_rerun_apsd_complete(bq); + } + + if (((!state.vbus_gd) || (bq->vbus_type == BQ2589X_VBUS_OTG)) && (bq->status & BQ2589X_STATUS_PLUGIN)) { + dev_info(bq->dev, "%s:adapter removed\n", __func__); + bq->status &= ~BQ2589X_STATUS_PLUGIN; + bq2589x_adapter_out_func(bq); + /* enable dynamic adjust vindpm */ + if(bq->enable_dynamic_adjust_vindpm){ + ret = bq2589x_set_input_volt_limit(bq, bq->cfg.vindpm); + bq->vindpm_flag = false; + } + /* enable dynamic adjust battery voltage */ + if(bq->enable_dynamic_adjust_batvol){ + bq->cv_tune = 0; + bq2589x_set_chargevoltage(bq, bq->final_cv/1000); + } + } else if ((bq->vbus_type != BQ2589X_VBUS_NONE) && (bq->vbus_type != BQ2589X_VBUS_OTG) + && (!(bq->status & BQ2589X_STATUS_PLUGIN) || (reapsd_complete == true)) + && state.online) { + dev_info(bq->dev, "%s:adapter plugged in\n", __func__); + bq->status |= BQ2589X_STATUS_PLUGIN; + bq2589x_adapter_in_func(bq); + } + + + if ((status & BQ2589X_PG_STAT_MASK) && !(bq->status & BQ2589X_STATUS_PG)) + bq->status |= BQ2589X_STATUS_PG; + else if (!(status & BQ2589X_PG_STAT_MASK) && (bq->status & BQ2589X_STATUS_PG)) + bq->status &= ~BQ2589X_STATUS_PG; + + if (fault && !(bq->status & BQ2589X_STATUS_FAULT)) + bq->status |= BQ2589X_STATUS_FAULT; + else if (!fault && (bq->status & BQ2589X_STATUS_FAULT)) + bq->status &= ~BQ2589X_STATUS_FAULT; + + charge_status = (status & BQ2589X_CHRG_STAT_MASK) >> BQ2589X_CHRG_STAT_SHIFT; + if (charge_status == BQ2589X_CHRG_STAT_IDLE) + dev_info(bq->dev, "%s:not charging\n", __func__); + else if (charge_status == BQ2589X_CHRG_STAT_PRECHG) + dev_info(bq->dev, "%s:precharging\n", __func__); + else if (charge_status == BQ2589X_CHRG_STAT_FASTCHG) + dev_info(bq->dev, "%s:fast charging\n", __func__); + else if (charge_status == BQ2589X_CHRG_STAT_CHGDONE) + dev_info(bq->dev, "%s:charge done!\n", __func__); + + if (fault) + dev_info(bq->dev, "%s:charge fault:%02x\n", __func__,fault); +} + +static int bq2589x_irq_probe(struct bq2589x *bq) +{ + struct gpio_desc *irq; + + irq = devm_gpiod_get(bq->dev, BQ25890_IRQ_PIN, GPIOD_IN); + if (IS_ERR(irq)) { + dev_err(bq->dev, "Could not probe irq pin.\n"); + return PTR_ERR(irq); + } + + return gpiod_to_irq(irq); +} + +static irqreturn_t bq2589x_charger_interrupt(int irq, void *data) +{ + struct bq2589x *bq = data; + + dev_info(bq->dev, "%s", __func__); + schedule_work(&bq->irq_work); + return IRQ_HANDLED; +} + +static int mmi_config_pd_active(struct charger_device *chg_dev, int val) +{ + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + + bq->pd_active = val; + + return 0; +} + +static int mmi_get_qc3p_power(struct charger_device *chg_dev, int *qc3p_power) +{ + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + + *qc3p_power = bq->mmi_qc3p_power; + + return 0; +} + +static int mmi_get_pulse_cnt(struct charger_device *chg_dev, int *count) +{ + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + + *count = bq->pulse_cnt; + + return 0; +} + +static int mmi_set_dp_dm(struct charger_device *chg_dev, int val) +{ + int rc = 0; + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + + down(&bq->sem_dpdm); + + switch(val) { + case MMI_POWER_SUPPLY_DP_DM_DP_PULSE: + bq->pulse_cnt++; + rc = mmi_qc30_step_up_vbus(bq); + if (rc) { + dev_err(bq->dev, "Couldn't increase pulse count rc=%d\n", + rc); + bq->pulse_cnt--; + } + dev_dbg(bq->dev,"DP_DM_DP_PULSE rc=%d cnt=%d\n", + rc, bq->pulse_cnt); + break; + case MMI_POWER_SUPPLY_DP_DM_DM_PULSE: + rc = mmi_qc30_step_down_vbus(bq); + if (!rc && bq->pulse_cnt) + bq->pulse_cnt--; + dev_dbg(bq->dev, "DP_DM_DM_PULSE rc=%d cnt=%d\n", + rc, bq->pulse_cnt); + break; + case MMI_POWER_SUPPLY_IGNORE_REQUEST_DPDM: + bq->ignore_request_dpdm = true; + dev_err(bq->dev, "MMI_POWER_SUPPLY_IGNORE_REQUEST_DPDM\n"); + break; + case MMI_POWER_SUPPLY_DONOT_IGNORE_REQUEST_DPDM: + bq->ignore_request_dpdm = false; + dev_err(bq->dev, "MMI_POWER_SUPPLY_DONOT_IGNORE_REQUEST_DPDM\n"); + break; + default: + break; + } + + up(&bq->sem_dpdm); + + return rc; +} + +static int bq2589x_get_real_charger_type(struct charger_device *chg_dev, int *chg_type) +{ + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + + *chg_type = bq->real_charger_type; + + return 0; +} + +static bool bq2589x_is_factory_mode(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool factory_mode = false; + const char *bootargs = NULL; + char *bootmode = NULL; + char *end = NULL; + + if (!np) + return factory_mode; + + if (!of_property_read_string(np, "bootargs", &bootargs)) { + bootmode = strstr(bootargs, "androidboot.mode="); + if (bootmode) { + end = strpbrk(bootmode, " "); + bootmode = strpbrk(bootmode, "="); + } + if (bootmode && + end > bootmode && + strnstr(bootmode, "mot-factory", end - bootmode)) { + factory_mode = true; + } + } + of_node_put(np); + + return factory_mode; +} + +static int bq2589x_set_icl(struct charger_device *chg_dev, u32 uA) +{ + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + u32 mA; + + mA = uA / 1000; + dev_info(bq->dev,"%s set icl curr = %d mA\n", __func__, mA); + + bq->input_current_cache = mA; + + if (bq->mmi_is_qc3_authen) { + dev_info(bq->dev, "hvdcp detecting now\n"); + return 0; + } + if (bq->part_no == SC89890H && bq->factory_mode) { + if (mA < BQ2589X_ENABLE_ICO_THRESHOLD) { + bq2589x_enable_ico(bq, false); + } else { + bq2589x_enable_ico(bq, true); + } + } + return bq2589x_set_input_current_limit(bq, mA); +} + +static int bq2589x_get_icl(struct charger_device *chg_dev, u32 *uA) +{ + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + + *uA = bq2589x_read_idpm_limit(bq)*1000; + + dev_info(bq->dev,"%s get icl curr = %d uA\n", __func__, *uA); + + return 0; +} + +static int bq2589x_enable_hw_jeita(struct charger_device *chg_dev, bool en) +{ + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + dev_warn(bq->dev,"%s cannot set jeita state to %s\n", __func__, en ? "enabled":"disabled"); + + return en; +} + +static int bq2589x_set_otg_enable(struct charger_device *chg_dev, bool enable) +{ + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + u8 val; + int ret = 0; + + if (enable) { + /*disable wls output*/ + if (gpio_is_valid(bq->wls_en_gpio)) { + gpio_direction_output(bq->wls_en_gpio, 1); + } + + if (bq->part_no == SC89890H) { + ret = bq2589x_disable_charger(bq); + } + val = BQ2589X_OTG_ENABLE << BQ2589X_OTG_CONFIG_SHIFT; + ret = bq2589x_update_bits(bq, BQ2589X_REG_03, BQ2589X_OTG_CONFIG_MASK, val); + } + else { + val = BQ2589X_OTG_DISABLE << BQ2589X_OTG_CONFIG_SHIFT; + if (bq->part_no == SC89890H) { + ret = bq2589x_enable_charger(bq); + } + ret = bq2589x_update_bits(bq, BQ2589X_REG_03, BQ2589X_OTG_CONFIG_MASK, val); + + /*resume wls output*/ + if (gpio_is_valid(bq->wls_en_gpio)) { + gpio_direction_output(bq->wls_en_gpio, 0); + } + } + dev_info(bq->dev, "%s: %s otg\n", __func__, enable ? "enable" : "disable"); + + return ret; +} + +static int bq2589x_set_boost_current_limit(struct charger_device *chg_dev, u32 uA) +{ + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + int ret = 0; + uA /= 1000; + ret = bq2589x_set_otg_current(bq, uA); + + dev_info(bq->dev,"%s set boost current limit = %d mA, %s\n", __func__, uA, ret ? "failed" : "success"); + + return ret; +} + +static int bq2589x_enable_hz(struct charger_device *chg_dev, bool enable) +{ + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + int ret; + + if (enable) + ret = bq2589x_enter_hiz_mode(bq); + else + ret = bq2589x_exit_hiz_mode(bq); + + dev_info(bq->dev,"%s, %s input hz %s\n", __func__, enable ? "enable" : "disable", ret == 0 ? "success":"failed"); + + return ret; +} + +static int bq2589x_enable_charging(struct charger_device *chg_dev, bool enable) +{ + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + u8 val; + int ret; + + if (enable) + val = BQ2589X_CHG_ENABLE << BQ2589X_CHG_CONFIG_SHIFT; + else + val = BQ2589X_CHG_DISABLE << BQ2589X_CHG_CONFIG_SHIFT; + + ret = bq2589x_update_bits(bq, BQ2589X_REG_03, BQ2589X_CHG_CONFIG_MASK, val); + + if (ret == 0) { + if (enable) + bq->status |= BQ2589X_STATUS_CHARGE_ENABLE; + else + bq->status &= ~BQ2589X_STATUS_CHARGE_ENABLE; + } + + dev_info(bq->dev,"%s, %s charging %s\n", __func__, enable ? "enable" : "disable", ret == 0 ? "success":"failed"); + + return ret; +} + +static int bq2589x_set_charging_current(struct charger_device *chg_dev, u32 uA) +{ + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + int ret = 0; + uA /= 1000; + ret = bq2589x_set_chargecurrent(bq, uA); + + dev_info(bq->dev,"%s set charging curr = %d mA, %s\n", __func__, uA, ret ? "failed" : "success"); + + return ret; +} + +static int bq2589x_set_charging_voltage(struct charger_device *chg_dev, u32 uV) +{ + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + int ret = 0; + + /* enable dynamic adjust battery voltage */ + if(bq->enable_dynamic_adjust_batvol) + bq->final_cv = uV; + + uV /= 1000; + ret = bq2589x_set_chargevoltage(bq, uV); + + dev_info(bq->dev,"%s set charging volt = %d mV, %s\n", __func__, uV, ret ? "failed" : "success"); + + return ret; +} + +static int bq2589x_is_charging_halted(struct charger_device *chg_dev, bool *en) +{ + int ret = 0; + int chrg_stat = 0; + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + chrg_stat = bq2589x_get_charging_status(bq); + + switch(chrg_stat) { + case STATUS_NOT_CHARGING: + case STATUS_TERMINATION_DONE: + *en = true; + break; + case STATUS_PRE_CHARGING: + case STATUS_FAST_CHARGING: + *en = false; + break; + default: + break; + } + + return ret; +} + +static int mmi_rerun_aicl(struct charger_device *chg_dev) +{ + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + int ret = 0; + + ret = bq2589x_force_ico(bq); + + dev_info(bq->dev,"%s force ico %s\n", __func__, ret ? "failed" : "success"); + + return ret; +} + +static int bq2589x_dump_registers(struct charger_device *chg_dev, struct seq_file *m) +{ + struct bq2589x *bq = dev_get_drvdata(&chg_dev->dev); + u8 addr; + u8 val; + int ret; + + for(addr = 0x0; addr<=0x14; addr++) { + ret = bq2589x_read_byte(bq, &val, addr); + if (ret) + continue; + seq_printf(m, "%s Reg[0x%.2x] = 0x%.2x\n", __func__, addr, val); + + } + + return ret; +} + +static const struct charger_properties bq2589x_chg_props = { + .alias_name = "bq25890", +}; + +static struct charger_ops bq2589x_chg_ops = { + .get_pulse_cnt = mmi_get_pulse_cnt, + .set_dp_dm = mmi_set_dp_dm, + .get_real_charger_type = bq2589x_get_real_charger_type, + .set_input_current = bq2589x_set_icl, + .get_input_current = bq2589x_get_icl, + .enable_hw_jeita = bq2589x_enable_hw_jeita, + .enable_otg = bq2589x_set_otg_enable, + .set_boost_current_limit = bq2589x_set_boost_current_limit, + .enable_charging = bq2589x_enable_charging, + .enable_hz = bq2589x_enable_hz, + .set_charging_current = bq2589x_set_charging_current, + .set_constant_voltage = bq2589x_set_charging_voltage, + .is_charge_halted = bq2589x_is_charging_halted, + + .dump_registers = bq2589x_dump_registers, + .is_enabled_charging = bq2589x_is_enabled_charging, + .enable_termination = bq2589x_enable_termination, + .get_qc3p_power = mmi_get_qc3p_power, + .config_pd_active = mmi_config_pd_active, + .rerun_aicl = mmi_rerun_aicl, +}; + +#ifdef __indicator_led_en__ +static int indicator_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct qpnp_led_dev *led = + container_of(led_cdev, struct qpnp_led_dev, cdev); + int rc = 0; + + mutex_lock(&led->lock); + if (brightness > LED_FULL) + brightness = LED_FULL; + + if (brightness == led->led_setting.brightness && + !led->blinking && !led->breathing) { + mutex_unlock(&led->lock); + return 0; + } + + led->led_setting.brightness = brightness; + if (!!brightness) + bq2589x_update_bits(bq_g, BQ2589X_REG_07, BQ2589X_EN_LED_MASK, BQ2589X_LED_ENABLE<led_setting.blink = false; + led->led_setting.breath = false; + + mutex_unlock(&led->lock); + + return rc; +} + +static enum led_brightness indicator_led_get_brightness( + struct led_classdev *led_cdev) +{ + return led_cdev->brightness; +} + +static int indicator_led_set_blink(struct led_classdev *led_cdev, + unsigned long *on_ms, unsigned long *off_ms) +{ + int rc = 0; + return rc; +} + +static int indicator_led_register(struct indicator_led_chip *chip) +{ + struct qpnp_led_dev *led; + int rc, i, j; + + + for (i = 0; i < chip->num_leds; i++) { + led = &chip->leds[i]; + mutex_init(&led->lock); + led->cdev.name = led->label; + led->cdev.max_brightness = LED_FULL; + led->cdev.brightness_set_blocking = indicator_led_set_brightness; + led->cdev.brightness_get = indicator_led_get_brightness; + led->cdev.blink_set = indicator_led_set_blink; + led->cdev.default_trigger = led->default_trigger; + led->cdev.brightness = LED_OFF; + + rc = devm_led_classdev_register(chip->dev, &led->cdev); + if (rc < 0) { + dev_err(chip->dev, "%s led class device registering failed, rc=%d\n", + led->label, rc); + goto err_out; + } + } + + return 0; + +err_out: + for (j = 0; j <= i; j++) { + mutex_destroy(&chip->leds[j].lock); + } + return rc; +} + +static int indicator_led_parse_dt(struct indicator_led_chip *chip) +{ + struct device_node *node = chip->dev->of_node, *child_node; + struct qpnp_led_dev *led; + int rc = 0, id, i = 0; + + chip->num_leds = of_get_available_child_count(node); + if (chip->num_leds == 0) { + dev_err(chip->dev, "No led child node defined\n"); + return -ENODEV; + } + + if (chip->num_leds > TRILED_NUM_MAX) { + dev_err(chip->dev, "can't support %d leds(max %d)\n", + chip->num_leds, TRILED_NUM_MAX); + return -EINVAL; + } + + chip->leds = devm_kcalloc(chip->dev, chip->num_leds, + sizeof(struct qpnp_led_dev), GFP_KERNEL); + if (!chip->leds) + return -ENOMEM; + + for_each_available_child_of_node(node, child_node) { + rc = of_property_read_u32(child_node, "led-sources", &id); + if (rc) { + dev_err(chip->dev, "Get led-sources failed, rc=%d\n", + rc); + return rc; + } + + if (id >= TRILED_NUM_MAX) { + dev_err(chip->dev, "only support 0~%d current source\n", + TRILED_NUM_MAX - 1); + return -EINVAL; + } + + led = &chip->leds[i++]; + led->chip = chip; + led->id = id; + led->label = + of_get_property(child_node, "label", NULL) ? : + child_node->name; + led->default_trigger = of_get_property(child_node, + "linux,default-trigger", NULL); + } + + return rc; +} + +static int indicator_led_probe(struct i2c_client *client) +{ + struct indicator_led_chip *chip; + int rc = 0; + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &client->dev; + + rc = indicator_led_parse_dt(chip); + if (rc < 0) { + if (rc != -EPROBE_DEFER) + dev_err(chip->dev, "Devicetree properties parsing failed, rc=%d\n", + rc); + return rc; + } + rc = indicator_led_register(chip); + if (rc < 0) { + dev_err(chip->dev, "Registering LED class devices failed, rc=%d\n", + rc); + goto destroy; + } + + dev_err(chip->dev, "%s has been finished\n", __func__); + return 0; +destroy: + return rc; +} +#endif + +static int bq2589x_charger_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct bq2589x *bq; + int ret; + + bq = devm_kzalloc(dev, sizeof(struct bq2589x), GFP_KERNEL); + if (!bq) { + dev_err(dev, "%s: out of memory\n", __func__); + return -ENOMEM; + } +#ifdef __indicator_led_en__ + bq_g = bq; +#endif + bq->dev = dev; + bq->client = client; + i2c_set_clientdata(client, bq); + + bq->ignore_request_dpdm = false; + + ret = bq2589x_detect_device(bq); + if (!ret && bq->part_no == BQ25890) { + bq->status |= BQ2589X_STATUS_EXIST; + dev_info(dev, "%s: charger device bq25890 detected, revision:%d\n", __func__, bq->revision); + } else if (!ret && bq->part_no == SC89890H) { + bq->status |= BQ2589X_STATUS_EXIST; + dev_info(dev, "%s: charger device sc89890h detected, revision:%d\n", __func__, bq->revision); + } else { + dev_info(dev, "%s: no bq25890 charger device found:%d\n", __func__, ret); + return -ENODEV; + } + + bq->batt_psy = power_supply_get_by_name("bms"); + + g_bq = bq; + + mutex_init(&bq->lock); + mutex_init(&bq->regulator_lock); + sema_init(&bq->sem_dpdm, 1); + + if (client->dev.of_node) + bq2589x_parse_dt(&client->dev, bq); + + bq->chg_dev = charger_device_register(bq->chg_dev_name, + &client->dev, bq, + &bq2589x_chg_ops, + &bq2589x_chg_props); + if (IS_ERR_OR_NULL(bq->chg_dev)) { + ret = PTR_ERR(bq->chg_dev); + return ret; + } + + ret = bq2589x_init_device(bq); + if (ret) { + dev_err(dev, "device init failure: %d\n", ret); + goto err_0; + } + + if (client->irq <= 0) + client->irq = bq2589x_irq_probe(bq); + + if (client->irq < 0) { + dev_err(dev, "No irq resource found.\n"); + return client->irq; + } + + ret = bq2589x_psy_register(bq); + if (ret) + goto err_0; + + if (bq->part_no != SC89890H) { + bq2589x_set_ilim_enable(bq, true); + } + + INIT_WORK(&bq->irq_work, bq2589x_charger_irq_workfunc); + INIT_DELAYED_WORK(&bq->monitor_work, bq2589x_monitor_workfunc); + INIT_DELAYED_WORK(&bq->ico_work, bq2589x_ico_workfunc); + INIT_DELAYED_WORK(&bq->pe_volt_tune_work, bq2589x_tune_volt_workfunc); + INIT_DELAYED_WORK(&bq->check_pe_tuneup_work, bq2589x_check_pe_tuneup_workfunc); + + if (bq->mmi_qc3_support) { + bq->mmi_qc3_authen_task = kthread_create(mmi_hvdcp_detect_kthread, bq, "mmi_qc3_authen"); + if (IS_ERR(bq->mmi_qc3_authen_task)) { + ret = PTR_ERR(bq->mmi_qc3_authen_task); + dev_err(dev, "Failed to create mmi_qc3_authen_task ret = %d\n", ret); + goto err_irq; + } + init_waitqueue_head(&bq->mmi_qc3_wait_que); + wake_up_process(bq->mmi_qc3_authen_task); + } + + ret = sysfs_create_group(&bq->dev->kobj, &bq2589x_attr_group); + if (ret) { + dev_err(dev, "failed to register sysfs. err: %d\n", ret); + goto err_irq; + } + + if (client->irq) { + ret = devm_request_threaded_irq(dev, client->irq, NULL, + bq2589x_charger_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + BQ25890_IRQ_PIN, bq); + if (ret) { + dev_err(dev, "%s:Request IRQ %d failed: %d\n", __func__, client->irq, ret); + goto err_irq; + } else { + dev_info(dev, "%s:irq = %d\n", __func__, client->irq); + enable_irq_wake(client->irq); + } + } + +#ifdef CONFIG_MMI_QC3P_TURBO_CHARGER + ret = bq2589x_set_recharge_volt(bq, 100);//100mv + if (ret) + goto err_0; +#endif +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED + ret = mmi_init_iio_psy(bq, dev); + if (ret) { + dev_err(dev, + "mmi iio psy init failed\n"); + } +#endif + + pe.enable = false; + + bq2589x_rerun_apsd_if_required(bq); + + if (bq2589x_is_factory_mode()) + bq->factory_mode = true; + +#ifdef __indicator_led_en__ + bq2589x_update_bits(bq, BQ2589X_REG_07, BQ2589X_EN_LED_MASK, BQ2589X_LED_DISABLE<mmi_qc3_support && bq->mmi_qc3_authen_task) + kthread_stop(bq->mmi_qc3_authen_task); + + cancel_work_sync(&bq->irq_work); + cancel_delayed_work_sync(&bq->ico_work); + cancel_delayed_work_sync(&bq->check_pe_tuneup_work); + if (pe.enable) { + cancel_delayed_work_sync(&bq->pe_volt_tune_work); + cancel_delayed_work_sync(&bq->monitor_work); + } + +err_0: + g_bq = NULL; + return ret; +} + +static void bq2589x_charger_shutdown(struct i2c_client *client) +{ + struct bq2589x *bq = i2c_get_clientdata(client); + + dev_info(bq->dev, "%s: shutdown\n", __func__); + + /* enable dynamic adjust battery voltage */ + if(bq->enable_dynamic_adjust_batvol){ + bq2589x_set_chargevoltage(bq, bq->final_cv/1000); + } + + /*set CONV_RATE 0 to exits continuous conversion mode*/ + bq2589x_adc_stop(bq); + + bq2589x_psy_unregister(bq); + + if (bq->mmi_qc3_support && bq->mmi_qc3_authen_task) + kthread_stop(bq->mmi_qc3_authen_task); + + sysfs_remove_group(&bq->dev->kobj, &bq2589x_attr_group); + cancel_work_sync(&bq->irq_work); + cancel_delayed_work_sync(&bq->ico_work); + cancel_delayed_work_sync(&bq->check_pe_tuneup_work); + if (pe.enable) { + cancel_delayed_work_sync(&bq->pe_volt_tune_work); + cancel_delayed_work_sync(&bq->monitor_work); + } + + devm_free_irq(&client->dev, client->irq, bq); + + /*release charging enable gpio to avoid power leakage.*/ + if(gpio_is_valid(bq->chg_en_gpio)){ + devm_gpio_free(&client->dev, bq->chg_en_gpio); + } + g_bq = NULL; +} + +static struct of_device_id bq2589x_charger_match_table[] = { + {.compatible = "ti,bq2589x-1",}, + {}, +}; + + +static const struct i2c_device_id bq2589x_charger_id[] = { + { "bq2589x-1", BQ25890 }, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, bq2589x_charger_id); + +static struct i2c_driver bq2589x_charger_driver = { + .driver = { + .name = "bq2589x-1", + .of_match_table = bq2589x_charger_match_table, + }, + .id_table = bq2589x_charger_id, + + .probe = bq2589x_charger_probe, + .shutdown = bq2589x_charger_shutdown, +}; + +module_i2c_driver(bq2589x_charger_driver); + +MODULE_DESCRIPTION("TI BQ2589x Charger Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); diff --git a/drivers/power/bq2589x_chg_mmi/bq2589x_reg.h b/drivers/power/bq2589x_chg_mmi/bq2589x_reg.h new file mode 100644 index 000000000000..97716c8a0d3a --- /dev/null +++ b/drivers/power/bq2589x_chg_mmi/bq2589x_reg.h @@ -0,0 +1,437 @@ + +#ifndef __BQ2589X_HEADER__ +#define __BQ2589X_HEADER__ +#ifdef CONFIG_MMI_EXT_CHG_LED +#define __indicator_led_en__ +#endif + +#ifdef __indicator_led_en__ +#include +#include +#endif + +/* Register 00h */ +#define BQ2589X_REG_00 0x00 +#define BQ2589X_ENHIZ_MASK 0x80 +#define BQ2589X_ENHIZ_SHIFT 7 +#define BQ2589X_HIZ_ENABLE 1 +#define BQ2589X_HIZ_DISABLE 0 +#define BQ2589X_ENILIM_MASK 0x40 +#define BQ2589X_ENILIM_SHIFT 6 +#define BQ2589X_ENILIM_ENABLE 1 +#define BQ2589X_ENILIM_DISABLE 0 + +#define BQ2589X_IINLIM_MASK 0x3F +#define BQ2589X_IINLIM_SHIFT 0 +#define BQ2589X_IINLIM_BASE 100 +#define BQ2589X_IINLIM_LSB 50 +/*for HW accuracy issue, so icl >= 3A we need to set 0x3F*/ +#define BQ2589X_IINLIM_MAX 3000 + +/* Register 01h */ +#define BQ2589X_REG_01 0x01 +#define BQ2589X_DP_VSEL_MASK 0xE0 +#define BQ2589X_DP_VSEL_SHIFT 5 +#define BQ2589X_DM_VSEL_MASK 0x1C +#define BQ2589X_DM_VSEL_SHIFT 2 + +/* Register 0x02 */ +#define BQ2589X_REG_02 0x02 +#define BQ2589X_CONV_START_MASK 0x80 +#define BQ2589X_CONV_START_SHIFT 7 +#define BQ2589X_CONV_START 0 +#define BQ2589X_CONV_RATE_MASK 0x40 +#define BQ2589X_CONV_RATE_SHIFT 6 +#define BQ2589X_ADC_CONTINUE_ENABLE 1 +#define BQ2589X_ADC_CONTINUE_DISABLE 0 + +#define BQ2589X_BOOST_FREQ_MASK 0x20 +#define BQ2589X_BOOST_FREQ_SHIFT 5 +#define BQ2589X_BOOST_FREQ_1500K 0 +#define BQ2589X_BOOST_FREQ_500K 0 + +#define BQ2589X_ICOEN_MASK 0x10 +#define BQ2589X_ICOEN_SHIFT 4 +#define BQ2589X_ICO_ENABLE 1 +#define BQ2589X_ICO_DISABLE 0 +#define BQ2589X_HVDCPEN_MASK 0x08 +#define BQ2589X_HVDCPEN_SHIFT 3 +#define BQ2589X_HVDCP_ENABLE 1 +#define BQ2589X_HVDCP_DISABLE 0 +#define BQ2589X_MAXCEN_MASK 0x04 +#define BQ2589X_MAXCEN_SHIFT 2 +#define BQ2589X_MAXC_ENABLE 1 +#define BQ2589X_MAXC_DISABLE 0 + +#define BQ2589X_FORCE_DPDM_MASK 0x02 +#define BQ2589X_FORCE_DPDM_SHIFT 1 +#define BQ2589X_FORCE_DPDM 1 +#define BQ2589X_AUTO_DPDM_EN_MASK 0x01 +#define BQ2589X_AUTO_DPDM_EN_SHIFT 0 +#define BQ2589X_AUTO_DPDM_ENABLE 1 +#define BQ2589X_AUTO_DPDM_DISABLE 0 + + +/* Register 0x03 */ +#define BQ2589X_REG_03 0x03 +#define BQ2589X_BAT_LOADEN_MASK 0x80 +#define BQ2589X_BAT_LOAEN_SHIFT 7 +#define BQ2589X_WDT_RESET_MASK 0x40 +#define BQ2589X_WDT_RESET_SHIFT 6 +#define BQ2589X_WDT_RESET 1 + +#define BQ2589X_OTG_CONFIG_MASK 0x20 +#define BQ2589X_OTG_CONFIG_SHIFT 5 +#define BQ2589X_OTG_ENABLE 1 +#define BQ2589X_OTG_DISABLE 0 + +#define BQ2589X_CHG_CONFIG_MASK 0x10 +#define BQ2589X_CHG_CONFIG_SHIFT 4 +#define BQ2589X_CHG_ENABLE 1 +#define BQ2589X_CHG_DISABLE 0 + + +#define BQ2589X_SYS_MINV_MASK 0x0E +#define BQ2589X_SYS_MINV_SHIFT 1 + +#define BQ2589X_SYS_MINV_BASE 3000 +#define BQ2589X_SYS_MINV_LSB 100 + + +/* Register 0x04*/ +#define BQ2589X_REG_04 0x04 +#define BQ2589X_EN_PUMPX_MASK 0x80 +#define BQ2589X_EN_PUMPX_SHIFT 7 +#define BQ2589X_PUMPX_ENABLE 1 +#define BQ2589X_PUMPX_DISABLE 0 +#define BQ2589X_ICHG_MASK 0x7F +#define BQ2589X_ICHG_SHIFT 0 +#define BQ2589X_ICHG_BASE 0 +#define BQ2589X_ICHG_LSB 64 +#define SC89890H_ICHG_BASE 0 +#define SC89890H_ICHG_LSB 60 + +/* Register 0x05*/ +#define BQ2589X_REG_05 0x05 +#define BQ2589X_IPRECHG_MASK 0xF0 +#define BQ2589X_IPRECHG_SHIFT 4 +#define BQ2589X_ITERM_MASK 0x0F +#define BQ2589X_ITERM_SHIFT 0 +#define BQ2589X_IPRECHG_BASE 64 +#define BQ2589X_IPRECHG_LSB 64 +#define BQ2589X_ITERM_BASE 64 +#define BQ2589X_ITERM_LSB 64 +#define SC89890H_IPRECHG_BASE 60 +#define SC89890H_IPRECHG_LSB 60 +#define SC89890H_ITERM_BASE 30 +#define SC89890H_ITERM_LSB 60 + +/* Register 0x06*/ +#define BQ2589X_REG_06 0x06 +#define BQ2589X_VREG_MASK 0xFC +#define BQ2589X_VREG_SHIFT 2 +#define BQ2589X_BATLOWV_MASK 0x02 +#define BQ2589X_BATLOWV_SHIFT 1 +#define BQ2589X_BATLOWV_2800MV 0 +#define BQ2589X_BATLOWV_3000MV 1 +#define BQ2589X_VRECHG_MASK 0x01 +#define BQ2589X_VRECHG_SHIFT 0 +#define BQ2589X_VRECHG_100MV 0 +#define BQ2589X_VRECHG_200MV 1 +#define BQ2589X_VREG_BASE 3840 +#define BQ2589X_VREG_LSB 16 + +/* recharge voltage */ +#ifdef CONFIG_MMI_QC3P_TURBO_CHARGER +#define BQ2589X_VRECHRG_STEP_MV 100 +#define BQ2589X_VRECHRG_OFFSET_MV 100 +#endif + +/* Register 0x07*/ +#define BQ2589X_REG_07 0x07 +#define BQ2589X_EN_TERM_MASK 0x80 +#define BQ2589X_EN_TERM_SHIFT 7 +#define BQ2589X_TERM_ENABLE 1 +#define BQ2589X_TERM_DISABLE 0 + +#define BQ2589X_EN_LED_MASK 0x40 +#define BQ2589X_EN_LED_SHIFT 6 +#define BQ2589X_LED_ENABLE 0 +#define BQ2589X_LED_DISABLE 1 + +#define BQ2589X_WDT_MASK 0x30 +#define BQ2589X_WDT_SHIFT 4 +#define BQ2589X_WDT_DISABLE 0 +#define BQ2589X_WDT_40S 1 +#define BQ2589X_WDT_80S 2 +#define BQ2589X_WDT_160S 3 +#define BQ2589X_WDT_BASE 0 +#define BQ2589X_WDT_LSB 40 + +#define BQ2589X_EN_TIMER_MASK 0x08 +#define BQ2589X_EN_TIMER_SHIFT 3 + +#define BQ2589X_CHG_TIMER_ENABLE 1 +#define BQ2589X_CHG_TIMER_DISABLE 0 + +#define BQ2589X_CHG_TIMER_MASK 0x06 +#define BQ2589X_CHG_TIMER_SHIFT 1 +#define BQ2589X_CHG_TIMER_5HOURS 0 +#define BQ2589X_CHG_TIMER_8HOURS 1 +#define BQ2589X_CHG_TIMER_12HOURS 2 +#define BQ2589X_CHG_TIMER_20HOURS 3 + +#define BQ2589X_JEITA_ISET_MASK 0x01 +#define BQ2589X_JEITA_ISET_SHIFT 0 +#define BQ2589X_JEITA_ISET_50PCT 0 +#define BQ2589X_JEITA_ISET_20PCT 1 + + +/* Register 0x08*/ +#define BQ2589X_REG_08 0x08 +#define BQ2589X_BAT_COMP_MASK 0xE0 +#define BQ2589X_BAT_COMP_SHIFT 5 +#define BQ2589X_VCLAMP_MASK 0x1C +#define BQ2589X_VCLAMP_SHIFT 2 +#define BQ2589X_TREG_MASK 0x03 +#define BQ2589X_TREG_SHIFT 0 +#define BQ2589X_TREG_60C 0 +#define BQ2589X_TREG_80C 1 +#define BQ2589X_TREG_100C 2 +#define BQ2589X_TREG_120C 3 + +#define BQ2589X_BAT_COMP_BASE 0 +#define BQ2589X_BAT_COMP_LSB 20 +#define BQ2589X_VCLAMP_BASE 0 +#define BQ2589X_VCLAMP_LSB 32 + + +/* Register 0x09*/ +#define BQ2589X_REG_09 0x09 +#define BQ2589X_FORCE_ICO_MASK 0x80 +#define BQ2589X_FORCE_ICO_SHIFT 7 +#define BQ2589X_FORCE_ICO 1 +#define BQ2589X_TMR2X_EN_MASK 0x40 +#define BQ2589X_TMR2X_EN_SHIFT 6 +#define BQ2589X_BATFET_DIS_MASK 0x20 +#define BQ2589X_BATFET_DIS_SHIFT 5 +#define BQ2589X_BATFET_OFF 1 + +#define BQ2589X_JEITA_VSET_MASK 0x10 +#define BQ2589X_JEITA_VSET_SHIFT 4 +#define BQ2589X_JEITA_VSET_N150MV 0 +#define BQ2589X_JEITA_VSET_VREG 1 +#define BQ2589X_BATFET_RST_EN_MASK 0x04 +#define BQ2589X_BATFET_RST_EN_SHIFT 2 +#define BQ2589X_PUMPX_UP_MASK 0x02 +#define BQ2589X_PUMPX_UP_SHIFT 1 +#define BQ2589X_PUMPX_UP 1 +#define BQ2589X_PUMPX_DOWN_MASK 0x01 +#define BQ2589X_PUMPX_DOWN_SHIFT 0 +#define BQ2589X_PUMPX_DOWN 1 + + +/* Register 0x0A*/ +#define BQ2589X_REG_0A 0x0A +#define BQ2589X_BOOSTV_MASK 0xF0 +#define BQ2589X_BOOSTV_SHIFT 4 +#define BQ2589X_BOOSTV_BASE 4550 +#define BQ2589X_BOOSTV_LSB 64 +#define SC89890H_BOOSTV_BASE 3900 +#define SC89890H_BOOSTV_LSB 100 + + +#define BQ2589X_BOOST_LIM_MASK 0x07 +#define BQ2589X_BOOST_LIM_SHIFT 0 +#define BQ2589X_BOOST_LIM_500MA 0x00 +#define BQ2589X_BOOST_LIM_700MA 0x01 +#define BQ2589X_BOOST_LIM_1100MA 0x02 +#define BQ2589X_BOOST_LIM_1300MA 0x03 +#define BQ2589X_BOOST_LIM_1600MA 0x04 +#define BQ2589X_BOOST_LIM_1800MA 0x05 +#define BQ2589X_BOOST_LIM_2100MA 0x06 +#define BQ2589X_BOOST_LIM_2400MA 0x07 +#define SC89890H_BOOST_LIM_500MA 0x00 +#define SC89890H_BOOST_LIM_750MA 0x01 +#define SC89890H_BOOST_LIM_1200MA 0x02 +#define SC89890H_BOOST_LIM_1400MA 0x03 +#define SC89890H_BOOST_LIM_1650MA 0x04 +#define SC89890H_BOOST_LIM_1875MA 0x05 +#define SC89890H_BOOST_LIM_2150MA 0x06 +#define SC89890H_BOOST_LIM_2450MA 0x07 + + +/* Register 0x0B*/ +#define BQ2589X_REG_0B 0x0B +#define BQ2589X_VBUS_STAT_MASK 0xE0 +#define BQ2589X_VBUS_STAT_SHIFT 5 +#define BQ2589X_CHRG_STAT_MASK 0x18 +#define BQ2589X_CHRG_STAT_SHIFT 3 +#define BQ2589X_CHRG_STAT_IDLE 0 +#define BQ2589X_CHRG_STAT_PRECHG 1 +#define BQ2589X_CHRG_STAT_FASTCHG 2 +#define BQ2589X_CHRG_STAT_CHGDONE 3 + +#define BQ2589X_PG_STAT_MASK 0x04 +#define BQ2589X_PG_STAT_SHIFT 2 +#define BQ2589X_SDP_STAT_MASK 0x02 +#define BQ2589X_SDP_STAT_SHIFT 1 +#define BQ2589X_VSYS_STAT_MASK 0x01 +#define BQ2589X_VSYS_STAT_SHIFT 0 + + +/* Register 0x0C*/ +#define BQ2589X_REG_0C 0x0c +#define BQ2589X_FAULT_WDT_MASK 0x80 +#define BQ2589X_FAULT_WDT_SHIFT 7 +#define BQ2589X_FAULT_BOOST_MASK 0x40 +#define BQ2589X_FAULT_BOOST_SHIFT 6 +#define BQ2589X_FAULT_CHRG_MASK 0x30 +#define BQ2589X_FAULT_CHRG_SHIFT 4 +#define BQ2589X_FAULT_CHRG_NORMAL 0 +#define BQ2589X_FAULT_CHRG_INPUT 1 +#define BQ2589X_FAULT_CHRG_THERMAL 2 +#define BQ2589X_FAULT_CHRG_TIMER 3 + +#define BQ2589X_FAULT_BAT_MASK 0x08 +#define BQ2589X_FAULT_BAT_SHIFT 3 +#define BQ2589X_FAULT_NTC_MASK 0x07 +#define BQ2589X_FAULT_NTC_SHIFT 0 +#define BQ2589X_FAULT_NTC_TSCOLD 1 +#define BQ2589X_FAULT_NTC_TSHOT 2 + +#define BQ2589X_FAULT_NTC_WARM 2 +#define BQ2589X_FAULT_NTC_COOL 3 +#define BQ2589X_FAULT_NTC_COLD 5 +#define BQ2589X_FAULT_NTC_HOT 6 + + +/* Register 0x0D*/ +#define BQ2589X_REG_0D 0x0D +#define BQ2589X_FORCE_VINDPM_MASK 0x80 +#define BQ2589X_FORCE_VINDPM_SHIFT 7 +#define BQ2589X_FORCE_VINDPM_ENABLE 1 +#define BQ2589X_FORCE_VINDPM_DISABLE 0 +#define BQ2589X_VINDPM_MASK 0x7F +#define BQ2589X_VINDPM_SHIFT 0 + +#define BQ2589X_VINDPM_BASE 2600 +#define BQ2589X_VINDPM_LSB 100 + +#define SC89890H_VINDPMOS_MASK 0x03 +#define SC89890H_VINDPMOS_SHIFT 0 +#define SC89890H_VINDPMOS_400MV 0 +#define SC89890H_VINDPMOS_600MV 1 + +/* Register 0x0E*/ +#define BQ2589X_REG_0E 0x0E +#define BQ2589X_THERM_STAT_MASK 0x80 +#define BQ2589X_THERM_STAT_SHIFT 7 +#define BQ2589X_BATV_MASK 0x7F +#define BQ2589X_BATV_SHIFT 0 +#define BQ2589X_BATV_BASE 2304 +#define BQ2589X_BATV_LSB 20 + + +/* Register 0x0F*/ +#define BQ2589X_REG_0F 0x0F +#define BQ2589X_SYSV_MASK 0x7F +#define BQ2589X_SYSV_SHIFT 0 +#define BQ2589X_SYSV_BASE 2304 +#define BQ2589X_SYSV_LSB 20 + + +/* Register 0x10*/ +#define BQ2589X_REG_10 0x10 +#define BQ2589X_TSPCT_MASK 0x7F +#define BQ2589X_TSPCT_SHIFT 0 +#define BQ2589X_TSPCT_BASE 21 +#define BQ2589X_TSPCT_LSB 465//should be 0.465,kernel does not support float + +/* Register 0x11*/ +#define BQ2589X_REG_11 0x11 +#define BQ2589X_VBUS_GD_MASK 0x80 +#define BQ2589X_VBUS_GD_SHIFT 7 +#define BQ2589X_VBUSV_MASK 0x7F +#define BQ2589X_VBUSV_SHIFT 0 +#define BQ2589X_VBUSV_BASE 2600 +#define BQ2589X_VBUSV_LSB 100 + + +/* Register 0x12*/ +#define BQ2589X_REG_12 0x12 +#define BQ2589X_ICHGR_MASK 0x7F +#define BQ2589X_ICHGR_SHIFT 0 +#define BQ2589X_ICHGR_BASE 0 +#define BQ2589X_ICHGR_LSB 50 + + +/* Register 0x13*/ +#define BQ2589X_REG_13 0x13 +#define BQ2589X_VDPM_STAT_MASK 0x80 +#define BQ2589X_VDPM_STAT_SHIFT 7 +#define BQ2589X_IDPM_STAT_MASK 0x40 +#define BQ2589X_IDPM_STAT_SHIFT 6 +#define BQ2589X_IDPM_LIM_MASK 0x3F +#define BQ2589X_IDPM_LIM_SHIFT 0 +#define BQ2589X_IDPM_LIM_BASE 100 +#define BQ2589X_IDPM_LIM_LSB 50 + + +/* Register 0x14*/ +#define BQ2589X_REG_14 0x14 +#define BQ2589X_RESET_MASK 0x80 +#define BQ2589X_RESET_SHIFT 7 +#define BQ2589X_RESET 1 +#define BQ2589X_ICO_OPTIMIZED_MASK 0x40 +#define BQ2589X_ICO_OPTIMIZED_SHIFT 6 +#define BQ2589X_PN_MASK 0x38 +#define BQ2589X_PN_SHIFT 3 +#define BQ2589X_TS_PROFILE_MASK 0x04 +#define BQ2589X_TS_PROFILE_SHIFT 2 +#define BQ2589X_DEV_REV_MASK 0x03 +#define BQ2589X_DEV_REV_SHIFT 0 + + +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED +enum WT_charger_type{ + WT_CHG_TYPE_BEGIN = 0, + WT_CHG_TYPE_FC, + WT_CHG_TYPE_SDP, + WT_CHG_TYPE_CDP, + WT_CHG_TYPE_DCP, + WT_CHG_TYPE_QC2, + WT_CHG_TYPE_QC3, + WT_CHG_TYPE_OCP, + WT_CHG_TYPE_QC3P_18W,//0x8 + WT_CHG_TYPE_QC3P_27W, + WT6670_CHG_TYPE_UNKNOWN, +}; + +enum mmi_qc3p_ext_iio_channels { + /*qc3p*/ + SMB5_USB_REAL_TYPE, + SMB5_QC3P_POWER, + SMB5_QC3P_START_DETECT, + SMB5_QC3P_DETECTION_READY, + SMB5_QC3P_START_POLICY, + SMB5_BC12_START_DETECT, + SMB5_BC12_DETECTION_READY, + SMB5_READ_USBIN_VOLTAGE, + SMB5_READ_BC12_CHG_TYPE, +}; + +static const char * const mmi_qc3p_ext_iio_chan_name[] = { + /*qc3p*/ + [SMB5_USB_REAL_TYPE] = "wt6670_usb_real_type", + [SMB5_QC3P_POWER] = "wt6670_usb_qc3p_power", + [SMB5_QC3P_START_DETECT] = "wt6670_start_detection", + [SMB5_QC3P_DETECTION_READY] "wt6670_detection_ready", + [SMB5_QC3P_START_POLICY] "qc3p_start_policy", + [SMB5_BC12_START_DETECT] = "wt6670_start_bc12_detection", + [SMB5_BC12_DETECTION_READY] = "wt6670_detection_bc12_ready", + [SMB5_READ_USBIN_VOLTAGE] = "read_usbin_voltage", + [SMB5_READ_BC12_CHG_TYPE] = "wt6670_bc12_chg_type", +}; +#endif +#endif diff --git a/drivers/power/bq2597x_mmi/Android.mk b/drivers/power/bq2597x_mmi/Android.mk new file mode 100644 index 000000000000..87a40bfabcb0 --- /dev/null +++ b/drivers/power/bq2597x_mmi/Android.mk @@ -0,0 +1,19 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := bq2597x_mmi.ko + +ifeq ($(BQ2597X_DUAL_ENABLE),true) + KERNEL_CFLAGS += CONFIG_BQ2597X_DUAL=y +endif + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/bq2597x_mmi/Kbuild b/drivers/power/bq2597x_mmi/Kbuild new file mode 100644 index 000000000000..8fbfa78a8359 --- /dev/null +++ b/drivers/power/bq2597x_mmi/Kbuild @@ -0,0 +1,12 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/power/bq2597x_mmi + +obj-m += bq2597x_mmi.o + +ifneq ($(filter m y,$(CONFIG_BQ2597X_DUAL)),) + bq2597x_mmi-objs += bq25980_charger.o +else + bq2597x_mmi-objs += bq2597x_single_mmi.o +endif diff --git a/drivers/power/bq2597x_mmi/Makefile b/drivers/power/bq2597x_mmi/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/power/bq2597x_mmi/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/power/bq2597x_mmi/bq25970_reg.h b/drivers/power/bq2597x_mmi/bq25970_reg.h new file mode 100644 index 000000000000..768cda5f0821 --- /dev/null +++ b/drivers/power/bq2597x_mmi/bq25970_reg.h @@ -0,0 +1,790 @@ + +#ifndef __BQ2597X_HEADER__ +#define __BQ2597X_HEADER__ + +/* Register 00h */ +#define BQ2597X_REG_00 0x00 +#define BQ2597X_BAT_OVP_DIS_MASK 0x80 +#define BQ2597X_BAT_OVP_DIS_SHIFT 7 +#define BQ2597X_BAT_OVP_ENABLE 0 +#define BQ2597X_BAT_OVP_DISABLE 1 + +#define BQ2597X_BAT_OVP_MASK 0x3F +#define BQ2597X_BAT_OVP_SHIFT 0 +#define BQ2597X_BAT_OVP_BASE 3475 +#define BQ2597X_BAT_OVP_LSB 25 + +/* Register 01h */ +#define BQ2597X_REG_01 0x01 +#define BQ2597X_BAT_OVP_ALM_DIS_MASK 0x80 +#define BQ2597X_BAT_OVP_ALM_DIS_SHIFT 7 +#define BQ2597X_BAT_OVP_ALM_ENABLE 0 +#define BQ2597X_BAT_OVP_ALM_DISABLE 1 + +#define BQ2597X_BAT_OVP_ALM_MASK 0x3F +#define BQ2597X_BAT_OVP_ALM_SHIFT 0 +#define BQ2597X_BAT_OVP_ALM_BASE 3500 +#define BQ2597X_BAT_OVP_ALM_LSB 25 + +/* Register 02h */ +#define BQ2597X_REG_02 0x02 +#define BQ2597X_BAT_OCP_DIS_MASK 0x80 +#define BQ2597X_BAT_OCP_DIS_SHIFT 7 +#define BQ2597X_BAT_OCP_ENABLE 0 +#define BQ2597X_BAT_OCP_DISABLE 1 + +#define BQ2597X_BAT_OCP_MASK 0x7F +#define BQ2597X_BAT_OCP_SHIFT 0 +#define BQ2597X_BAT_OCP_BASE 2000 +#define BQ2597X_BAT_OCP_LSB 100 + +/* Register 03h */ +#define BQ2597X_REG_03 0x03 +#define BQ2597X_BAT_OCP_ALM_DIS_MASK 0x80 +#define BQ2597X_BAT_OCP_ALM_DIS_SHIFT 7 +#define BQ2597X_BAT_OCP_ALM_ENABLE 0 +#define BQ2597X_BAT_OCP_ALM_DISABLE 1 + +#define BQ2597X_BAT_OCP_ALM_MASK 0x7F +#define BQ2597X_BAT_OCP_ALM_SHIFT 0 +#define BQ2597X_BAT_OCP_ALM_BASE 2000 +#define BQ2597X_BAT_OCP_ALM_LSB 100 + +/* Register 04h */ +#define BQ2597X_REG_04 0x04 +#define BQ2597X_BAT_UCP_ALM_DIS_MASK 0x80 +#define BQ2597X_BAT_UCP_ALM_DIS_SHIFT 7 +#define BQ2597X_BAT_UCP_ALM_ENABLE 0 +#define BQ2597X_BAT_UCP_ALM_DISABLE 1 + +#define BQ2597X_BAT_UCP_ALM_MASK 0x7F +#define BQ2597X_BAT_UCP_ALM_SHIFT 0 +#define BQ2597X_BAT_UCP_ALM_BASE 0 +#define BQ2597X_BAT_UCP_ALM_LSB 50 + +/* Register 05h */ +#define BQ2597X_REG_05 0x05 +#define BQ2597X_AC_OVP_STAT_MASK 0x80 +#define BQ2597X_AC_OVP_STAT_SHIFT 7 + +#define BQ2597X_AC_OVP_FLAG_MASK 0x40 +#define BQ2597X_AC_OVP_FLAG_SHIFT 6 + +#define BQ2597X_AC_OVP_MASK_MASK 0x20 +#define BQ2597X_AC_OVP_MASK_SHIFT 5 + +#define BQ2597X_VDROP_THRESHOLD_SET_MASK 0x10 +#define BQ2597X_VDROP_THRESHOLD_SET_SHIFT 4 +#define BQ2597X_VDROP_THRESHOLD_300MV 0 +#define BQ2597X_VDROP_THRESHOLD_400MV 1 + +#define BQ2597X_VDROP_DEGLITCH_SET_MASK 0x08 +#define BQ2597X_VDROP_DEGLITCH_SET_SHIFT 3 +#define BQ2597X_VDROP_DEGLITCH_8US 0 +#define BQ2597X_VDROP_DEGLITCH_5MS 1 + +#define BQ2597X_AC_OVP_MASK 0x07 +#define BQ2597X_AC_OVP_SHIFT 0 +#define BQ2597X_AC_OVP_BASE 11 +#define BQ2597X_AC_OVP_LSB 1 +#define BQ2597X_AC_OVP_6P5V 65 + +/* Register 06h */ +#define BQ2597X_REG_06 0x06 +#define BQ2597X_VBUS_PD_EN_MASK 0x80 +#define BQ2597X_VBUS_PD_EN_SHIFT 7 +#define BQ2597X_VBUS_PD_ENABLE 1 +#define BQ2597X_VBUS_PD_DISABLE 0 + +#define BQ2597X_BUS_OVP_MASK 0x7F +#define BQ2597X_BUS_OVP_SHIFT 0 +#define BQ2597X_BUS_OVP_BASE 5950 +#define BQ2597X_BUS_OVP_LSB 50 + +/* Register 07h */ +#define BQ2597X_REG_07 0x07 +#define BQ2597X_BUS_OVP_ALM_DIS_MASK 0x80 +#define BQ2597X_BUS_OVP_ALM_DIS_SHIFT 7 +#define BQ2597X_BUS_OVP_ALM_ENABLE 0 +#define BQ2597X_BUS_OVP_ALM_DISABLE 1 + +#define BQ2597X_BUS_OVP_ALM_MASK 0x7F +#define BQ2597X_BUS_OVP_ALM_SHIFT 0 +#define BQ2597X_BUS_OVP_ALM_BASE 6000 +#define BQ2597X_BUS_OVP_ALM_LSB 50 + +/* Register 08h */ +#define BQ2597X_REG_08 0x08 +#define BQ2597X_BUS_OCP_DIS_MASK 0x80 +#define BQ2597X_BUS_OCP_DIS_SHIFT 7 +#define BQ2597X_BUS_OCP_ENABLE 0 +#define BQ2597X_BUS_OCP_DISABLE 1 + +#define BQ2597X_IBUS_UCP_RISE_FLAG_MASK 0x40 +#define BQ2597X_IBUS_UCP_RISE_FLAG_SHIFT 6 + +#define BQ2597X_IBUS_UCP_RISE_MASK_MASK 0x20 +#define BQ2597X_IBUS_UCP_RISE_MASK_SHIFT 5 +#define BQ2597X_IBUS_UCP_RISE_MASK_ENABLE 1 +#define BQ2597X_IBUS_UCP_RISE_MASK_DISABLE 0 + +#define BQ2597X_IBUS_UCP_FALL_FLAG_MASK 0x10 +#define BQ2597X_IBUS_UCP_FALL_FLAG_SHIFT 4 + +#define BQ2597X_BUS_OCP_MASK 0x0F +#define BQ2597X_BUS_OCP_SHIFT 0 +#define BQ2597X_BUS_OCP_BASE 1000 +#define BQ2597X_BUS_OCP_LSB 250 + +/* Register 09h */ +#define BQ2597X_REG_09 0x09 +#define BQ2597X_BUS_OCP_ALM_DIS_MASK 0x80 +#define BQ2597X_BUS_OCP_ALM_DIS_SHIFT 7 +#define BQ2597X_BUS_OCP_ALM_ENABLE 0 +#define BQ2597X_BUS_OCP_ALM_DISABLE 1 + +#define BQ2597X_BUS_OCP_ALM_MASK 0x7F +#define BQ2597X_BUS_OCP_ALM_SHIFT 0 +#define BQ2597X_BUS_OCP_ALM_BASE 0 +#define BQ2597X_BUS_OCP_ALM_LSB 50 + +/* Register 0Ah */ +#define BQ2597X_REG_0A 0x0A +#define BQ2597X_TSHUT_FLAG_MASK 0x80 +#define BQ2597X_TSHUT_FLAG_SHIFT 7 + +#define BQ2597X_TSHUT_STAT_MASK 0x40 +#define BQ2597X_TSHUT_STAT_SHIFT 6 + +#define BQ2597X_VBUS_ERRORLO_STAT_MASK 0x20 +#define BQ2597X_VBUS_ERRORLO_STAT_SHIFT 5 + +#define BQ2597X_VBUS_ERRORHI_STAT_MASK 0x10 +#define BQ2597X_VBUS_ERRORHI_STAT_SHIFT 4 + +#define BQ2597X_SS_TIMEOUT_FLAG_MASK 0x08 +#define BQ2597X_SS_TIMEOUT_FLAG_SHIFT 3 + +#define BQ2597X_CONV_SWITCHING_STAT_MASK 0x04 +#define BQ2597X_CONV_SWITCHING_STAT_SHIFT 2 + +#define BQ2597X_CONV_OCP_FLAG_MASK 0x02 +#define BQ2597X_CONV_OCP_FLAG_SHIFT 1 + +#define BQ2597X_PIN_DIAG_FALL_FLAG_MASK 0x01 +#define BQ2597X_PIN_DIAG_FALL_FLAG_SHIFT 0 + +/* Register 0Bh */ +#define BQ2597X_REG_0B 0x0B +#define BQ2597X_REG_RST_MASK 0x80 +#define BQ2597X_REG_RST_SHIFT 7 +#define BQ2597X_REG_RST_ENABLE 1 +#define BQ2597X_REG_RST_DISABLE 0 + +#define BQ2597X_FSW_SET_MASK 0x70 +#define BQ2597X_FSW_SET_SHIFT 4 +#define BQ2597X_FSW_SET_187P5KHZ 0 +#define BQ2597X_FSW_SET_250KHZ 1 +#define BQ2597X_FSW_SET_300KHZ 2 +#define BQ2597X_FSW_SET_375KHZ 3 +#define BQ2597X_FSW_SET_500KHZ 4 +#define BQ2597X_FSW_SET_750KHZ 5 + +#define BQ2597X_WD_TIMEOUT_FLAG_MASK 0x08 +#define BQ2597X_WD_TIMEOUT_SHIFT 3 + +#define BQ2597X_WATCHDOG_DIS_MASK 0x04 +#define BQ2597X_WATCHDOG_DIS_SHIFT 2 +#define BQ2597X_WATCHDOG_ENABLE 0 +#define BQ2597X_WATCHDOG_DISABLE 1 + +#define BQ2597X_WATCHDOG_MASK 0x03 +#define BQ2597X_WATCHDOG_SHIFT 0 +#define BQ2597X_WATCHDOG_0P5S 0 +#define BQ2597X_WATCHDOG_1S 1 +#define BQ2597X_WATCHDOG_5S 2 +#define BQ2597X_WATCHDOG_30S 3 + +/* Register 0Ch */ +#define BQ2597X_REG_0C 0x0C +#define BQ2597X_CHG_EN_MASK 0x80 +#define BQ2597X_CHG_EN_SHIFT 7 +#define BQ2597X_CHG_ENABLE 1 +#define BQ2597X_CHG_DISABLE 0 + +#define BQ2597X_MS_MASK 0x60 +#define BQ2597X_MS_SHIFT 5 +#define BQ2597X_MS_STANDALONE 0 +#define BQ2597X_MS_SLAVE 1 +#define BQ2597X_MS_MASTER 2 + +#define BQ2597X_FREQ_SHIFT_MASK 0x18 +#define BQ2597X_FREQ_SHIFT_SHIFT 3 +#define BQ2597X_FREQ_SHIFT_NORMINAL 0 +#define BQ2597X_FREQ_SHIFT_POSITIVE10 1 +#define BQ2597X_FREQ_SHIFT_NEGATIVE10 2 +#define BQ2597X_FREQ_SHIFT_SPREAD_SPECTRUM 3 + +#define BQ2597X_TSBUS_DIS_MASK 0x04 +#define BQ2597X_TSBUS_DIS_SHIFT 2 +#define BQ2597X_TSBUS_ENABLE 0 +#define BQ2597X_TSBUS_DISABLE 1 + +#define BQ2597X_TSBAT_DIS_MASK 0x02 +#define BQ2597X_TSBAT_DIS_SHIFT 1 +#define BQ2597X_TSBAT_ENABLE 0 +#define BQ2597X_TSBAT_DISABLE 1 + +#define BQ2597X_TDIE_DIS_MASK 0x01 +#define BQ2597X_TDIE_DIS_SHIFT 0 +#define BQ2597X_TDIE_ENABLE 0 +#define BQ2597X_TDIE_DISABLE 1 + +/* Register 0Dh */ +#define BQ2597X_REG_0D 0x0D +#define BQ2597X_BAT_OVP_ALM_STAT_MASK 0x80 +#define BQ2597X_BAT_OVP_ALM_STAT_SHIFT 7 + +#define BQ2597X_BAT_OCP_ALM_STAT_MASK 0x40 +#define BQ2597X_BAT_OCP_ALM_STAT_SHIFT 6 + +#define BQ2597X_BUS_OVP_ALM_STAT_MASK 0x20 +#define BQ2597X_BUS_OVP_ALM_STAT_SHIFT 5 + +#define BQ2597X_BUS_OCP_ALM_STAT_MASK 0x10 +#define BQ2597X_BUS_OCP_ALM_STAT_SHIFT 4 + +#define BQ2597X_BAT_UCP_ALM_STAT_MASK 0x08 +#define BQ2597X_BAT_UCP_ALM_STAT_SHIFT 3 + +#define BQ2597X_ADAPTER_INSERT_STAT_MASK 0x04 +#define BQ2597X_ADAPTER_INSERT_STAT_SHIFT 2 + +#define BQ2597X_VBAT_INSERT_STAT_MASK 0x02 +#define BQ2597X_VBAT_INSERT_STAT_SHIFT 1 + +#define BQ2597X_ADC_DONE_STAT_MASK 0x01 +#define BQ2597X_ADC_DONE_STAT_SHIFT 0 +#define BQ2597X_ADC_DONE_STAT_COMPLETE 1 +#define BQ2597X_ADC_DONE_STAT_NOTCOMPLETE 0 + +/* Register 0Eh */ +#define BQ2597X_REG_0E 0x0E +#define BQ2597X_BAT_OVP_ALM_FLAG_MASK 0x80 +#define BQ2597X_BAT_OVP_ALM_FLAG_SHIFT 7 + +#define BQ2597X_BAT_OCP_ALM_FLAG_MASK 0x40 +#define BQ2597X_BAT_OCP_ALM_FLAG_SHIFT 6 + +#define BQ2597X_BUS_OVP_ALM_FLAG_MASK 0x20 +#define BQ2597X_BUS_OVP_ALM_FLAG_SHIFT 5 + +#define BQ2597X_BUS_OCP_ALM_FLAG_MASK 0x10 +#define BQ2597X_BUS_OCP_ALM_FLAG_SHIFT 4 + +#define BQ2597X_BAT_UCP_ALM_FLAG_MASK 0x08 +#define BQ2597X_BAT_UCP_ALM_FLAG_SHIFT 3 + +#define BQ2597X_ADAPTER_INSERT_FLAG_MASK 0x04 +#define BQ2597X_ADAPTER_INSERT_FLAG_SHIFT 2 + +#define BQ2597X_VBAT_INSERT_FLAG_MASK 0x02 +#define BQ2597X_VBAT_INSERT_FLAG_SHIFT 1 + +#define BQ2597X_ADC_DONE_FLAG_MASK 0x01 +#define BQ2597X_ADC_DONE_FLAG_SHIFT 0 +#define BQ2597X_ADC_DONE_FLAG_COMPLETE 1 +#define BQ2597X_ADC_DONE_FLAG_NOTCOMPLETE 0 + +/* Register 0Fh */ +#define BQ2597X_REG_0F 0x0F +#define BQ2597X_BAT_OVP_ALM_MASK_MASK 0x80 +#define BQ2597X_BAT_OVP_ALM_MASK_SHIFT 7 +#define BQ2597X_BAT_OVP_ALM_MASK_ENABLE 1 +#define BQ2597X_BAT_OVP_ALM_MASK_DISABLE 0 + +#define BQ2597X_BAT_OCP_ALM_MASK_MASK 0x40 +#define BQ2597X_BAT_OCP_ALM_MASK_SHIFT 6 +#define BQ2597X_BAT_OCP_ALM_MASK_ENABLE 1 +#define BQ2597X_BAT_OCP_ALM_MASK_DISABLE 0 + +#define BQ2597X_BUS_OVP_ALM_MASK_MASK 0x20 +#define BQ2597X_BUS_OVP_ALM_MASK_SHIFT 5 +#define BQ2597X_BUS_OVP_ALM_MASK_ENABLE 1 +#define BQ2597X_BUS_OVP_ALM_MASK_DISABLE 0 + +#define BQ2597X_BUS_OCP_ALM_MASK_MASK 0x10 +#define BQ2597X_BUS_OCP_ALM_MASK_SHIFT 4 +#define BQ2597X_BUS_OCP_ALM_MASK_ENABLE 1 +#define BQ2597X_BUS_OCP_ALM_MASK_DISABLE 0 + +#define BQ2597X_BAT_UCP_ALM_MASK_MASK 0x08 +#define BQ2597X_BAT_UCP_ALM_MASK_SHIFT 3 +#define BQ2597X_BAT_UCP_ALM_MASK_ENABLE 1 +#define BQ2597X_BAT_UCP_ALM_MASK_DISABLE 0 + +#define BQ2597X_ADAPTER_INSERT_MASK_MASK 0x04 +#define BQ2597X_ADAPTER_INSERT_MASK_SHIFT 2 +#define BQ2597X_ADAPTER_INSERT_MASK_ENABLE 1 +#define BQ2597X_ADAPTER_INSERT_MASK_DISABLE 0 + +#define BQ2597X_VBAT_INSERT_MASK_MASK 0x02 +#define BQ2597X_VBAT_INSERT_MASK_SHIFT 1 +#define BQ2597X_VBAT_INSERT_MASK_ENABLE 1 +#define BQ2597X_VBAT_INSERT_MASK_DISABLE 0 + +#define BQ2597X_ADC_DONE_MASK_MASK 0x01 +#define BQ2597X_ADC_DONE_MASK_SHIFT 0 +#define BQ2597X_ADC_DONE_MASK_ENABLE 1 +#define BQ2597X_ADC_DONE_MASK_DISABLE 0 + +/* Register 10h */ +#define BQ2597X_REG_10 0x10 +#define BQ2597X_BAT_OVP_FLT_STAT_MASK 0x80 +#define BQ2597X_BAT_OVP_FLT_STAT_SHIFT 7 + +#define BQ2597X_BAT_OCP_FLT_STAT_MASK 0x40 +#define BQ2597X_BAT_OCP_FLT_STAT_SHIFT 6 + +#define BQ2597X_BUS_OVP_FLT_STAT_MASK 0x20 +#define BQ2597X_BUS_OVP_FLT_STAT_SHIFT 5 + +#define BQ2597X_BUS_OCP_FLT_STAT_MASK 0x10 +#define BQ2597X_BUS_OCP_FLT_STAT_SHIFT 4 + +#define BQ2597X_TSBUS_TSBAT_ALM_STAT_MASK 0x08 +#define BQ2597X_TSBUS_TSBAT_ALM_STAT_SHIFT 3 + +#define BQ2597X_TSBAT_FLT_STAT_MASK 0x04 +#define BQ2597X_TSBAT_FLT_STAT_SHIFT 2 + +#define BQ2597X_TSBUS_FLT_STAT_MASK 0x02 +#define BQ2597X_TSBUS_FLT_STAT_SHIFT 1 + +#define BQ2597X_TDIE_ALM_STAT_MASK 0x01 +#define BQ2597X_TDIE_ALM_STAT_SHIFT 0 + +/* Register 11h */ +#define BQ2597X_REG_11 0x11 +#define BQ2597X_BAT_OVP_FLT_FLAG_MASK 0x80 +#define BQ2597X_BAT_OVP_FLT_FLAG_SHIFT 7 + +#define BQ2597X_BAT_OCP_FLT_FLAG_MASK 0x40 +#define BQ2597X_BAT_OCP_FLT_FLAG_SHIFT 6 + +#define BQ2597X_BUS_OVP_FLT_FLAG_MASK 0x20 +#define BQ2597X_BUS_OVP_FLT_FLAG_SHIFT 5 + +#define BQ2597X_BUS_OCP_FLT_FLAG_MASK 0x10 +#define BQ2597X_BUS_OCP_FLT_FLAG_SHIFT 4 + +#define BQ2597X_TSBUS_TSBAT_ALM_FLAG_MASK 0x08 +#define BQ2597X_TSBUS_TSBAT_ALM_FLAG_SHIFT 3 + +#define BQ2597X_TSBAT_FLT_FLAG_MASK 0x04 +#define BQ2597X_TSBAT_FLT_FLAG_SHIFT 2 + +#define BQ2597X_TSBUS_FLT_FLAG_MASK 0x02 +#define BQ2597X_TSBUS_FLT_FLAG_SHIFT 1 + +#define BQ2597X_TDIE_ALM_FLAG_MASK 0x01 +#define BQ2597X_TDIE_ALM_FLAG_SHIFT 0 + +/* Register 12h */ +#define BQ2597X_REG_12 0x12 +#define BQ2597X_BAT_OVP_FLT_MASK_MASK 0x80 +#define BQ2597X_BAT_OVP_FLT_MASK_SHIFT 7 +#define BQ2597X_BAT_OVP_FLT_MASK_ENABLE 1 +#define BQ2597X_BAT_OVP_FLT_MASK_DISABLE 0 + +#define BQ2597X_BAT_OCP_FLT_MASK_MASK 0x40 +#define BQ2597X_BAT_OCP_FLT_MASK_SHIFT 6 +#define BQ2597X_BAT_OCP_FLT_MASK_ENABLE 1 +#define BQ2597X_BAT_OCP_FLT_MASK_DISABLE 0 + +#define BQ2597X_BUS_OVP_FLT_MASK_MASK 0x20 +#define BQ2597X_BUS_OVP_FLT_MASK_SHIFT 5 +#define BQ2597X_BUS_OVP_FLT_MASK_ENABLE 1 +#define BQ2597X_BUS_OVP_FLT_MASK_DISABLE 0 + +#define BQ2597X_BUS_OCP_FLT_MASK_MASK 0x10 +#define BQ2597X_BUS_OCP_FLT_MASK_SHIFT 4 +#define BQ2597X_BUS_OCP_FLT_MASK_ENABLE 1 +#define BQ2597X_BUS_OCP_FLT_MASK_DISABLE 0 + +#define BQ2597X_TSBUS_TSBAT_ALM_MASK_MASK 0x08 +#define BQ2597X_TSBUS_TSBAT_ALM_MASK_SHIFT 3 +#define BQ2597X_TSBUS_TSBAT_ALM_MASK_ENABLE 1 +#define BQ2597X_TSBUS_TSBAT_ALM_MASK_DISABLE 0 + +#define BQ2597X_TSBAT_FLT_MASK_MASK 0x04 +#define BQ2597X_TSBAT_FLT_MASK_SHIFT 2 +#define BQ2597X_TSBAT_FLT_MASK_ENABLE 1 +#define BQ2597X_TSBAT_FLT_MASK_DISABLE 0 + +#define BQ2597X_TSBUS_FLT_MASK_MASK 0x02 +#define BQ2597X_TSBUS_FLT_MASK_SHIFT 1 +#define BQ2597X_TSBUS_FLT_MASK_ENABLE 1 +#define BQ2597X_TSBUS_FLT_MASK_DISABLE 0 + +#define BQ2597X_TDIE_ALM_MASK_MASK 0x01 +#define BQ2597X_TDIE_ALM_MASK_SHIFT 0 +#define BQ2597X_TDIE_ALM_MASK_ENABLE 1 +#define BQ2597X_TDIE_ALM_MASK_DISABLE 0 + +/* Register 13h */ +#define BQ2597X_REG_13 0x13 +#define BQ2597X_DEV_ID_MASK 0x0F +#define BQ2597X_DEV_ID_SHIFT 0 + +/* Register 14h */ +#define BQ2597X_REG_14 0x14 +#define BQ2597X_ADC_EN_MASK 0x80 +#define BQ2597X_ADC_EN_SHIFT 7 +#define BQ2597X_ADC_ENABLE 1 +#define BQ2597X_ADC_DISABLE 0 + +#define BQ2597X_ADC_RATE_MASK 0x40 +#define BQ2597X_ADC_RATE_SHIFT 6 +#define BQ2597X_ADC_RATE_CONTINOUS 0 +#define BQ2597X_ADC_RATE_ONESHOT 1 + +#define BQ2597X_ADC_AVG_MASK 0x20 +#define BQ2597X_ADC_AVG_SHIFT 5 +#define BQ2597X_ADC_AVG_DISABLE 0 +#define BQ2597X_ADC_AVG_ENABLE 1 + +#define BQ2597X_ADC_AVG_INIT_MASK 0x10 +#define BQ2597X_ADC_AVG_INIT_SHIFT 4 +#define BQ2597X_ADC_AVG_INIT_EXIST_REG_VAL 0 +#define BQ2597X_ADC_AVG_INIT_NEW_CONVERSION 1 + +#define BQ2597X_ADC_SAMPLE_MASK 0x0C +#define BQ2597X_ADC_SAMPLE_SHIFT 2 +#define BQ2597X_ADC_SAMPLE_15BITS 0 +#define BQ2597X_ADC_SAMPLE_14BITS 1 +#define BQ2597X_ADC_SAMPLE_13BITS 2 +#define BQ2597X_ADC_SAMPLE_12BITS 3 + +#define BQ2597X_IBUS_ADC_DIS_MASK 0x01 +#define BQ2597X_IBUS_ADC_DIS_SHIFT 0 +#define BQ2597X_IBUS_ADC_ENABLE 0 +#define BQ2597X_IBUS_ADC_DISABLE 1 + +/* Register 15h */ +#define BQ2597X_REG_15 0x15 +#define BQ2597X_VBUS_ADC_DIS_MASK 0x80 +#define BQ2597X_VBUS_ADC_DIS_SHIFT 7 +#define BQ2597X_VBUS_ADC_ENABLE 0 +#define BQ2597X_VBUS_ADC_DISABLE 1 + +#define BQ2597X_VAC_ADC_DIS_MASK 0x40 +#define BQ2597X_VAC_ADC_DIS_SHIFT 6 +#define BQ2597X_VAC_ADC_ENABLE 0 +#define BQ2597X_VAC_ADC_DISABLE 1 + +#define BQ2597X_VOUT_ADC_DIS_MASK 0x20 +#define BQ2597X_VOUT_ADC_DIS_SHIFT 5 +#define BQ2597X_VOUT_ADC_ENABLE 0 +#define BQ2597X_VOUT_ADC_DISABLE 1 + +#define BQ2597X_VBAT_ADC_DIS_MASK 0x10 +#define BQ2597X_VBAT_ADC_DIS_SHIFT 4 +#define BQ2597X_VBAT_ADC_ENABLE 0 +#define BQ2597X_VBAT_ADC_DISABLE 1 + +#define BQ2597X_IBAT_ADC_DIS_MASK 0x08 +#define BQ2597X_IBAT_ADC_DIS_SHIFT 3 +#define BQ2597X_IBAT_ADC_ENABLE 0 +#define BQ2597X_IBAT_ADC_DISABLE 1 + +#define BQ2597X_TSBUS_ADC_DIS_MASK 0x04 +#define BQ2597X_TSBUS_ADC_DIS_SHIFT 2 +#define BQ2597X_TSBUS_ADC_ENABLE 0 +#define BQ2597X_TSBUS_ADC_DISABLE 1 + +#define BQ2597X_TSBAT_ADC_DIS_MASK 0x02 +#define BQ2597X_TSBAT_ADC_DIS_SHIFT 1 +#define BQ2597X_TSBAT_ADC_ENABLE 0 +#define BQ2597X_TSBAT_ADC_DISABLE 1 + +#define BQ2597X_TDIE_ADC_DIS_MASK 0x01 +#define BQ2597X_TDIE_ADC_DIS_SHIFT 0 +#define BQ2597X_TDIE_ADC_ENABLE 0 +#define BQ2597X_TDIE_ADC_DISABLE 1 + +/* Register 16h */ +#define BQ2597X_REG_16 0x16 +#define BQ2597X_IBUS_POL_MASK 0x80 +#define BQ2597X_IBUS_POL_SHIFT 7 +#define BQ2597X_IBUS_POL_POSITIVE 0 +#define BQ2597X_IBUS_POL_NAGETIVE 1 + +#define BQ2597X_IBUS_ADC1_MASK 0x7F +#define BQ2597X_IBUS_ADC1_SHIFT 0 +#define BQ2597X_IBUS_ADC1_BASE 0 +#define BQ2597X_IBUS_ADC1_LSB 256 + +/* Register 17h */ +#define BQ2597X_REG_17 0x17 +#define BQ2597X_IBUS_ADC0_MASK 0xFF +#define BQ2597X_IBUS_ADC0_SHIFT 0 +#define BQ2597X_IBUS_ADC0_BASE 0 +#define BQ2597X_IBUS_ADC0_LSB 1 + +/* Register 18h */ +#define BQ2597X_REG_18 0x18 +#define BQ2597X_VBUS_POL_MASK 0x80 +#define BQ2597X_VBUS_POL_SHIFT 7 +#define BQ2597X_VBUS_POL_POSITIVE 0 +#define BQ2597X_VBUS_POL_NEGATIVE 1 + +#define BQ2597X_VBUS_ADC1_MASK 0x7F +#define BQ2597X_VBUS_ADC1_SHIFT 0 +#define BQ2597X_VBUS_ADC1_BASE 0 +#define BQ2597X_VBUS_ADC1_LSB 256 + +/* Register 19h */ +#define BQ2597X_REG_19 0x19 +#define BQ2597X_VBUS_ADC0_MASK 0xFF +#define BQ2597X_VBUS_ADC0_SHIFT 0 +#define BQ2597X_VBUS_ADC0_BASE 0 +#define BQ2597X_VBUS_ADC0_LSB 1 + +/* Register 1Ah */ +#define BQ2597X_REG_1A 0x1A +#define BQ2597X_VAC_POL_MASK 0x80 +#define BQ2597X_VAC_POL_SHIFT 7 +#define BQ2597X_VAC_POL_POSITIVE 0 +#define BQ2597X_VAC_POL_NEGATIVE 1 + +#define BQ2597X_VAC_ADC1_MASK 0x7F +#define BQ2597X_VAC_ADC1_SHIFT 0 +#define BQ2597X_VAC_ADC1_BASE 0 +#define BQ2597X_VAC_ADC1_LSB 256 + +/* Register 1Bh */ +#define BQ2597X_REG_1B 0x1B +#define BQ2597X_VAC_ADC0_MASK 0xFF +#define BQ2597X_VAC_ADC0_SHIFT 0 +#define BQ2597X_VAC_ADC0_BASE 0 +#define BQ2597X_VAC_ADC0_LSB 1 + +/* Register 1Ch */ +#define BQ2597X_REG_1C 0x1C +#define BQ2597X_VOUT_POL_MASK 0x80 +#define BQ2597X_VOUT_POL_SHIFT 7 +#define BQ2597X_VOUT_POL_POSITIVE 0 +#define BQ2597X_VOUT_POL_NEGATIVE 1 + +#define BQ2597X_VOUT_ADC1_MASK 0x7F +#define BQ2597X_VOUT_ADC1_SHIFT 0 +#define BQ2597X_VOUT_ADC1_BASE 0 +#define BQ2597X_VOUT_ADC1_LSB 256 + +/* Register 1Dh */ +#define BQ2597X_REG_1D 0x1D +#define BQ2597X_VOUT_ADC0_MASK 0xFF +#define BQ2597X_VOUT_ADC0_SHIFT 0 +#define BQ2597X_VOUT_ADC0_BASE 0 +#define BQ2597X_VOUT_ADC0_LSB 1 + +/* Register 1Eh */ +#define BQ2597X_REG_1E 0x1E +#define BQ2597X_VBAT_POL_MASK 0x80 +#define BQ2597X_VBAT_POL_SHIFT 7 +#define BQ2597X_VBAT_POL_POSITIVE 0 +#define BQ2597X_VBAT_POL_NEGATIVE 1 + +#define BQ2597X_VBAT_ADC1_MASK 0x7F +#define BQ2597X_VBAT_ADC1_SHIFT 0 +#define BQ2597X_VBAT_ADC1_BASE 0 +#define BQ2597X_VBAT_ADC1_LSB 256 + +/* Register 1Fh */ +#define BQ2597X_REG_1F 0x1F +#define BQ2597X_VBAT_ADC0_MASK 0xFF +#define BQ2597X_VBAT_ADC0_SHIFT 0 +#define BQ2597X_VBAT_ADC0_BASE 0 +#define BQ2597X_VBAT_ADC0_LSB 1 + +/* Register 20h */ +#define BQ2597X_REG_20 0x20 +#define BQ2597X_IBAT_POL_MASK 0x80 +#define BQ2597X_IBAT_POL_SHIFT 7 +#define BQ2597X_IBAT_POL_POSITIVE 0 +#define BQ2597X_IBAT_POL_NEGATIVE 1 + +#define BQ2597X_IBAT_ADC1_MASK 0x7F +#define BQ2597X_IBAT_ADC1_SHIFT 0 +#define BQ2597X_IBAT_ADC1_BASE 0 +#define BQ2597X_IBAT_ADC1_LSB 256 + +/* Register 21h */ +#define BQ2597X_REG_21 0x21 +#define BQ2597X_IBAT_ADC0_MASK 0xFF +#define BQ2597X_IBAT_ADC0_SHIFT 0 +#define BQ2597X_IBAT_ADC0_BASE 0 +#define BQ2597X_IBAT_ADC0_LSB 1 + +/* Register 22h */ +#define BQ2597X_REG_22 0x22 +#define BQ2597X_TSBUS_POL_MASK 0x80 +#define BQ2597X_TSBUS_POL_SHIFT 7 +#define BQ2597X_TSBUS_POL_POSITIVE 0 +#define BQ2597X_TSBUS_POL_NEGATIVE 1 + +#define BQ2597X_TSBUS_ADC1_MASK 0x7F +#define BQ2597X_TSBUS_ADC1_SHIFT 0 +#define BQ2597X_TSBUS_ADC1_BASE 0 +#define BQ2597X_TSBUS_ADC1_LSB 25 + +/* Register 23h */ +#define BQ2597X_REG_23 0x23 +#define BQ2597X_TSBUS_ADC0_MASK 0xFF +#define BQ2597X_TSBUS_ADC0_SHIFT 0 +#define BQ2597X_TSBUS_ADC0_BASE 0 +#define BQ2597X_TSBUS_ADC0_LSB 0.09766 + +/* Register 24h */ +#define BQ2597X_REG_24 0x24 +#define BQ2597X_TSBAT_POL_MASK 0x80 +#define BQ2597X_TSBAT_POL_SHIFT 7 +#define BQ2597X_TSBAT_POL_POSITIVE 0 +#define BQ2597X_TSBAT_POL_NEGATIVE 1 + +#define BQ2597X_TSBAT_ADC1_MASK 0x7F +#define BQ2597X_TSBAT_ADC1_SHIFT 0 +#define BQ2597X_TSBAT_ADC1_BASE 0 +#define BQ2597X_TSBAT_ADC1_LSB 25 + +/* Register 25h */ +#define BQ2597X_REG_25 0x25 +#define BQ2597X_TSBAT_ADC0_MASK 0xFF +#define BQ2597X_TSBAT_ADC0_SHIFT 0 +#define BQ2597X_TSBAT_ADC0_BASE 0 +#define BQ2597X_TSBAT_ADC0_LSB 0.09766 + +/* Register 26h */ +#define BQ2597X_REG_26 0x26 +#define BQ2597X_TDIE_POL_MASK 0x80 +#define BQ2597X_TDIE_POL_SHIFT 7 +#define BQ2597X_TDIE_POL_POSITIVE 0 +#define BQ2597X_TDIE_POL_NEGATIVE 1 + +#define BQ2597X_TDIE_ADC1_MASK 0x7F +#define BQ2597X_TDIE_ADC1_SHIFT 0 +#define BQ2597X_TDIE_ADC1_BASE 0 +#define BQ2597X_TDIE_ADC1_LSB 128 + +/* Register 27h */ +#define BQ2597X_REG_27 0x27 +#define BQ2597X_TDIE_ADC0_MASK 0xFF +#define BQ2597X_TDIE_ADC0_SHIFT 0 +#define BQ2597X_TDIE_ADC0_BASE 0 +#define BQ2597X_TDIE_ADC0_LSB 0.5 + +/* Register 28h */ +#define BQ2597X_REG_28 0x28 +#define BQ2597X_TSBUS_FLT1_MASK 0xFF +#define BQ2597X_TSBUS_FLT1_SHIFT 0 +#define BQ2597X_TSBUS_FLT1_BASE 0 +#define BQ2597X_TSBUS_FLT1_LSB 0.19531 + +/* Register 29h */ +#define BQ2597X_REG_29 0x29 +#define BQ2597X_TSBAT_FLT0_MASK 0xFF +#define BQ2597X_TSBAT_FLT0_SHIFT 0 +#define BQ2597X_TSBAT_FLT0_BASE 0 +#define BQ2597X_TSBAT_FLT0_LSB 0.19531 + +/* Register 2Ah */ +#define BQ2597X_REG_2A 0x2A +#define BQ2597X_TDIE_ALM_MASK 0xFF +#define BQ2597X_TDIE_ALM_SHIFT 0 +#define BQ2597X_TDIE_ALM_BASE 30 + +#define BQ2597X_TDIE_ALM_LSB 2 /*careful multiply is used for calc*/ + +/* Register 2Bh */ +#define BQ2597X_REG_2B 0x2B +#define BQ2597X_SS_TIMEOUT_SET_MASK 0xE0 +#define BQ2597X_SS_TIMEOUT_SET_SHIFT 5 +#define BQ2597X_SS_TIMEOUT_DISABLE 0 +#define BQ2597X_SS_TIMEOUT_SET_12P5MS 1 +#define BQ2597X_SS_TIMEOUT_SET_25MS 2 +#define BQ2597X_SS_TIMEOUT_SET_50MS 3 +#define BQ2597X_SS_TIMEOUT_SET_100MS 4 +#define BQ2597X_SS_TIMEOUT_SET_400MS 5 +#define BQ2597X_SS_TIMEOUT_SET_1500MS 6 +#define BQ2597X_SS_TIMEOUT_SET_100000MS 7 + +#define BQ2597X_EN_REGULATION_MASK 0x10 +#define BQ2597X_EN_REGULATION_SHIFT 4 +#define BQ2597X_EN_REGULATION_ENABLE 1 +#define BQ2597X_EN_REGULATION_DISABLE 0 + +#define BQ2597X_VOUT_OVP_DIS_MASK 0x08 +#define BQ2597X_VOUT_OVP_DIS_SHIFT 3 +#define BQ2597X_VOUT_OVP_ENABLE 1 +#define BQ2597X_VOUT_OVP_DISABLE 0 + +#define BQ2597X_IBUS_UCP_RISE_TH_MASK 0x04 +#define BQ2597X_IBUS_UCP_RISE_TH_SHIFT 2 +#define BQ2597X_IBUS_UCP_RISE_300MA 0 +#define BQ2597X_IBUS_UCP_RISE_500MA 1 + +#define BQ2597X_SET_IBAT_SNS_RES_MASK 0x02 +#define BQ2597X_SET_IBAT_SNS_RES_SHIFT 1 +#define BQ2597X_SET_IBAT_SNS_RES_2MHM 0 +#define BQ2597X_SET_IBAT_SNS_RES_5MHM 1 + + +#define BQ2597X_VAC_PD_EN_MASK 0x01 +#define BQ2597X_VAC_PD_EN_SHIFT 0 +#define BQ2597X_VAC_PD_ENABLE 1 +#define BQ2597X_VAC_PD_DISABLE 0 + +/* Register 2Ch */ +#define BQ2597X_REG_2C 0x2C +#define BQ2597X_IBAT_REG_MASK 0xC0 +#define BQ2597X_IBAT_REG_SHIFT 6 +#define BQ2597X_IBAT_REG_200MA 0 +#define BQ2597X_IBAT_REG_300MA 1 +#define BQ2597X_IBAT_REG_400MA 2 +#define BQ2597X_IBAT_REG_500MA 3 +#define BQ2597X_VBAT_REG_MASK 0x30 +#define BQ2597X_VBAT_REG_SHIFT 4 +#define BQ2597X_VBAT_REG_50MV 0 +#define BQ2597X_VBAT_REG_100MV 1 +#define BQ2597X_VBAT_REG_150MV 2 +#define BQ2597X_VBAT_REG_200MV 3 + +#define BQ2597X_VBAT_REG_ACTIVE_STAT_MASK 0x08 +#define BQ2597X_IBAT_REG_ACTIVE_STAT_MASK 0x04 +#define BQ2597X_VDROP_OVP_ACTIVE_STAT_MASK 0x02 +#define BQ2597X_VOUT_OVP_ACTIVE_STAT_MASK 0x01 + + +#define BQ2597X_REG_2D 0x2D +#define BQ2597X_VBAT_REG_ACTIVE_FLAG_MASK 0x80 +#define BQ2597X_IBAT_REG_ACTIVE_FLAG_MASK 0x40 +#define BQ2597X_VDROP_OVP_FLAG_MASK 0x20 +#define BQ2597X_VOUT_OVP_FLAG_MASK 0x10 +#define BQ2597X_VBAT_REG_ACTIVE_MASK_MASK 0x08 +#define BQ2597X_IBAT_REG_ACTIVE_MASK_MASK 0x04 +#define BQ2597X_VDROP_OVP_MASK_MASK 0x02 +#define BQ2597X_VOUT_OVP_MASK_MASK 0x01 + + +#define BQ2597X_REG_2E 0x2E +#define BQ2597X_VBUS_ERR_LOW_DG_MASK 0x10 +#define BQ2597X_VBUS_ERR_LOW_DG_SHIFT 4 +#define BQ2597X_VBUS_ERR_LOW_DG_10US 0 +#define BQ2597X_VBUS_ERR_LOW_DG_10MS 1 +#define BQ2597X_IBUS_LOW_DG_MASK 0x08 +#define BQ2597X_IBUS_LOW_DG_SHIFT 3 +#define BQ2597X_IBUS_LOW_DG_10US 0 +#define BQ2597X_IBUS_LOW_DG_5MS 1 + +#endif diff --git a/drivers/power/bq2597x_mmi/bq2597x_mmi.c b/drivers/power/bq2597x_mmi/bq2597x_mmi.c new file mode 100644 index 000000000000..02068ae1f4d2 --- /dev/null +++ b/drivers/power/bq2597x_mmi/bq2597x_mmi.c @@ -0,0 +1,2289 @@ +/* + * BQ2570x battery charging driver + * + * Copyright (C) 2017 Texas Instruments * + * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define pr_fmt(fmt) "[bq2597x] %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bq25970_reg.h" +#include + +#ifdef pr_debug +#undef pr_debug +#define pr_debug pr_err + +#endif + +enum { + ADC_IBUS, + ADC_VBUS, + ADC_VAC, + ADC_VOUT, + ADC_VBAT, + ADC_IBAT, + ADC_TBUS, + ADC_TBAT, + ADC_TDIE, + ADC_MAX_NUM, +}; + +enum { + BQ25970 = 0x00, +}; + +#define BAT_OVP_ALARM BIT(7) +#define BAT_OCP_ALARM BIT(6) +#define BUS_OVP_ALARM BIT(5) +#define BUS_OCP_ALARM BIT(4) +#define BAT_UCP_ALARM BIT(3) +#define VBUS_INSERT BIT(2) +#define VBAT_INSERT BIT(1) +#define ADC_DONE BIT(0) + +#define BAT_OVP_FAULT BIT(7) +#define BAT_OCP_FAULT BIT(6) +#define BUS_OVP_FAULT BIT(5) +#define BUS_OCP_FAULT BIT(4) +#define TBUS_TBAT_ALARM BIT(3) +#define TS_BAT_FAULT BIT(2) +#define TS_BUS_FAULT BIT(1) +#define TS_DIE_FAULT BIT(0) + +/*below used for comm with other module*/ +#define BAT_OVP_FAULT_SHIFT 8 +#define BAT_OCP_FAULT_SHIFT 9 +#define BUS_OVP_FAULT_SHIFT 10 +#define BUS_OCP_FAULT_SHIFT 11 +#define BAT_THERM_FAULT_SHIFT 12 +#define BUS_THERM_FAULT_SHIFT 13 +#define DIE_THERM_FAULT_SHIFT 14 + +#define BAT_OVP_FAULT_MASK (1 << BAT_OVP_FAULT_SHIFT) +#define BAT_OCP_FAULT_MASK (1 << BAT_OCP_FAULT_SHIFT) +#define BUS_OVP_FAULT_MASK (1 << BUS_OVP_FAULT_SHIFT) +#define BUS_OCP_FAULT_MASK (1 << BUS_OCP_FAULT_SHIFT) +#define BAT_THERM_FAULT_MASK (1 << BAT_THERM_FAULT_SHIFT) +#define BUS_THERM_FAULT_MASK (1 << BUS_THERM_FAULT_SHIFT) +#define DIE_THERM_FAULT_MASK (1 << DIE_THERM_FAULT_SHIFT) + +#define BAT_OVP_ALARM_SHIFT 0 +#define BAT_OCP_ALARM_SHIFT 1 +#define BUS_OVP_ALARM_SHIFT 2 +#define BUS_OCP_ALARM_SHIFT 3 +#define BAT_THERM_ALARM_SHIFT 4 +#define BUS_THERM_ALARM_SHIFT 5 +#define DIE_THERM_ALARM_SHIFT 6 +#define BAT_UCP_ALARM_SHIFT 7 + +#define BAT_OVP_ALARM_MASK (1 << BAT_OVP_ALARM_SHIFT) +#define BAT_OCP_ALARM_MASK (1 << BAT_OCP_ALARM_SHIFT) +#define BUS_OVP_ALARM_MASK (1 << BUS_OVP_ALARM_SHIFT) +#define BUS_OCP_ALARM_MASK (1 << BUS_OCP_ALARM_SHIFT) +#define BAT_THERM_ALARM_MASK (1 << BAT_THERM_ALARM_SHIFT) +#define BUS_THERM_ALARM_MASK (1 << BUS_THERM_ALARM_SHIFT) +#define DIE_THERM_ALARM_MASK (1 << DIE_THERM_ALARM_SHIFT) +#define BAT_UCP_ALARM_MASK (1 << BAT_UCP_ALARM_SHIFT) + +#define VBAT_REG_STATUS_SHIFT 0 +#define IBAT_REG_STATUS_SHIFT 1 + +#define VBAT_REG_STATUS_MASK (1 << VBAT_REG_STATUS_SHIFT) +#define IBAT_REG_STATUS_MASK (1 << VBAT_REG_STATUS_SHIFT) +/*end*/ + +struct bq2597x_cfg { + bool bat_ovp_disable; + bool bat_ocp_disable; + bool bat_ovp_alm_disable; + bool bat_ocp_alm_disable; + + int bat_ovp_th; + int bat_ovp_alm_th; + int bat_ocp_th; + int bat_ocp_alm_th; + + bool bus_ovp_alm_disable; + bool bus_ocp_disable; + bool bus_ocp_alm_disable; + + int bus_ovp_th; + int bus_ovp_alm_th; + int bus_ocp_th; + int bus_ocp_alm_th; + + bool bat_ucp_alm_disable; + + int bat_ucp_alm_th; + int ac_ovp_th; + + bool bat_therm_disable; + bool bus_therm_disable; + bool die_therm_disable; + + int bat_therm_th; /*in %*/ + int bus_therm_th; /*in %*/ + int die_therm_th; /*in degC*/ + + int sense_r_mohm; +}; + +struct bq2597x { + struct device *dev; + struct i2c_client *client; + + int part_no; + int revision; + + struct mutex data_lock; + struct mutex i2c_rw_lock; + struct mutex charging_disable_lock; + struct mutex irq_complete; + struct regmap *regmap; + + bool irq_waiting; + bool irq_disabled; + bool resume_completed; + + bool batt_present; + bool vbus_present; + + bool usb_present; + bool charge_enabled; /* Register bit status */ + + /* ADC reading */ + int vbat_volt; + int vbus_volt; + int vout_volt; + int vac_volt; + + int ibat_curr; + int ibus_curr; + + int bat_temp; + int bus_temp; + int die_temp; + + /* alarm/fault status */ + bool bat_ovp_fault; + bool bat_ocp_fault; + bool bus_ovp_fault; + bool bus_ocp_fault; + + bool bat_ovp_alarm; + bool bat_ocp_alarm; + bool bus_ovp_alarm; + bool bus_ocp_alarm; + + bool bat_ucp_alarm; + + bool bat_therm_alarm; + bool bus_therm_alarm; + bool die_therm_alarm; + + bool bat_therm_fault; + bool bus_therm_fault; + bool die_therm_fault; + + bool therm_shutdown_flag; + bool therm_shutdown_stat; + + bool vbat_reg; + bool ibat_reg; + + int prev_alarm; + int prev_fault; + + int chg_ma; + int chg_mv; + + int charge_state; + + struct bq2597x_cfg *cfg; + + int skip_writes; + int skip_reads; + + struct bq2597x_platform_data *platform_data; + + struct delayed_work monitor_work; + + struct dentry *debug_root; + + struct power_supply *fc2_psy; + + struct regulator *vdd_i2c_vreg; + struct pinctrl *irq_pinctrl; + struct gpio irq_gpio; +}; + +/************************************************************************/ + +struct bq2597x *g_chip; + +static int __bq2597x_read_byte(struct bq2597x *bq, u8 reg, u8 *data) +{ + s32 ret; + + ret = i2c_smbus_read_byte_data(bq->client, reg); + if (ret < 0) { + pr_err("i2c read fail: can't read from reg 0x%02X\n", reg); + return ret; + } + + *data = (u8) ret; + + return 0; +} + +static int __bq2597x_write_byte(struct bq2597x *bq, int reg, u8 val) +{ + s32 ret; + + ret = i2c_smbus_write_byte_data(bq->client, reg, val); + if (ret < 0) { + pr_err("i2c write fail: can't write 0x%02X to reg 0x%02X: %d\n", + val, reg, ret); + return ret; + } + return 0; +} + +static int __bq2597x_read_word(struct bq2597x *bq, u8 reg, u16 *data) +{ + s32 ret; + + ret = i2c_smbus_read_word_data(bq->client, reg); + if (ret < 0) { + pr_err("i2c read fail: can't read from reg 0x%02X\n", reg); + return ret; + } + + *data = (u16) ret; + + return 0; +} + +static int bq2597x_read_byte(struct bq2597x *bq, u8 reg, u8 *data) +{ + int ret; + + if (bq->skip_reads) { + *data = 0; + return 0; + } + + mutex_lock(&bq->i2c_rw_lock); + ret = __bq2597x_read_byte(bq, reg, data); + mutex_unlock(&bq->i2c_rw_lock); + + return ret; +} + +static int bq2597x_write_byte(struct bq2597x *bq, u8 reg, u8 data) +{ + int ret; + + if (bq->skip_writes) + return 0; + + mutex_lock(&bq->i2c_rw_lock); + ret = __bq2597x_write_byte(bq, reg, data); + mutex_unlock(&bq->i2c_rw_lock); + + return ret; +} + +static int bq2597x_read_word(struct bq2597x *bq, u8 reg, u16 *data) +{ + int ret; + + if (bq->skip_reads) { + *data = 0; + return 0; + } + + mutex_lock(&bq->i2c_rw_lock); + ret = __bq2597x_read_word(bq, reg, data); + mutex_unlock(&bq->i2c_rw_lock); + + return ret; +} + +static int bq2597x_update_bits(struct bq2597x *bq, u8 reg, + u8 mask, u8 data) +{ + int ret; + u8 tmp; + + if (bq->skip_reads || bq->skip_writes) + return 0; + + mutex_lock(&bq->i2c_rw_lock); + ret = __bq2597x_read_byte(bq, reg, &tmp); + if (ret) { + pr_err("Failed: reg=%02X, ret=%d\n", reg, ret); + goto out; + } + + tmp &= ~mask; + tmp |= data & mask; + + ret = __bq2597x_write_byte(bq, reg, tmp); + if (ret) + pr_err("Failed: reg=%02X, ret=%d\n", reg, ret); + +out: + mutex_unlock(&bq->i2c_rw_lock); + return ret; +} + +/*********************************************************************/ + +static int bq2597x_enable_charge(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_CHG_ENABLE; + else + val = BQ2597X_CHG_DISABLE; + + val <<= BQ2597X_CHG_EN_SHIFT; + + pr_err("bq2597x enable charge %d\n", enable); + + ret = bq2597x_update_bits(bq, BQ2597X_REG_0C, + BQ2597X_CHG_EN_MASK, val); + + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_charge); + +static int bq2597x_check_charge_enabled(struct bq2597x *bq, bool *enabled) +{ + int ret; + u8 val; + + ret = bq2597x_read_byte(bq, BQ2597X_REG_0C, &val); + if (!ret) + *enabled = !!(val & BQ2597X_CHG_EN_MASK); + return ret; +} + +static int bq2597x_enable_wdt(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_WATCHDOG_ENABLE; + else + val = BQ2597X_WATCHDOG_DISABLE; + + val <<= BQ2597X_WATCHDOG_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_0B, + BQ2597X_WATCHDOG_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_wdt); + +static int bq2597x_set_wdt(struct bq2597x *bq, int ms) +{ + int ret; + u8 val; + + if (ms == 500) + val = BQ2597X_WATCHDOG_0P5S; + else if (ms == 1000) + val = BQ2597X_WATCHDOG_1S; + else if (ms == 5000) + val = BQ2597X_WATCHDOG_5S; + else if (ms == 30000) + val = BQ2597X_WATCHDOG_30S; + else + val = BQ2597X_WATCHDOG_30S; + + val <<= BQ2597X_WATCHDOG_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_0B, + BQ2597X_WATCHDOG_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_wdt); + +static int bq2597x_enable_batovp(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_BAT_OVP_ENABLE; + else + val = BQ2597X_BAT_OVP_DISABLE; + + val <<= BQ2597X_BAT_OVP_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_00, + BQ2597X_BAT_OVP_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_batovp); + +static int bq2597x_set_batovp_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + pr_err("bq2597x bq2597x_set_batovp_th %d\n", threshold); + if (threshold < BQ2597X_BAT_OVP_BASE) + threshold = BQ2597X_BAT_OVP_BASE; + + val = (threshold - BQ2597X_BAT_OVP_BASE) / BQ2597X_BAT_OVP_LSB; + + val <<= BQ2597X_BAT_OVP_SHIFT; + pr_err("bq2597x bq2597x_set_batovp_th 0X%x\n", val); + ret = bq2597x_update_bits(bq, BQ2597X_REG_00, + BQ2597X_BAT_OVP_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_batovp_th); + +static int bq2597x_enable_batovp_alarm(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_BAT_OVP_ALM_ENABLE; + else + val = BQ2597X_BAT_OVP_ALM_DISABLE; + + val <<= BQ2597X_BAT_OVP_ALM_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_01, + BQ2597X_BAT_OVP_ALM_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_batovp_alarm); + +static int bq2597x_set_batovp_alarm_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold < BQ2597X_BAT_OVP_ALM_BASE) + threshold = BQ2597X_BAT_OVP_ALM_BASE; + + val = (threshold - BQ2597X_BAT_OVP_ALM_BASE) / BQ2597X_BAT_OVP_ALM_LSB; + + val <<= BQ2597X_BAT_OVP_ALM_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_01, + BQ2597X_BAT_OVP_ALM_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_batovp_alarm_th); + +static int bq2597x_enable_batocp(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_BAT_OCP_ENABLE; + else + val = BQ2597X_BAT_OCP_DISABLE; + + val <<= BQ2597X_BAT_OCP_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_02, + BQ2597X_BAT_OCP_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_batocp); + +static int bq2597x_set_batocp_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold < BQ2597X_BAT_OCP_BASE) + threshold = BQ2597X_BAT_OCP_BASE; + + val = (threshold - BQ2597X_BAT_OCP_BASE) / BQ2597X_BAT_OCP_LSB; + + val <<= BQ2597X_BAT_OCP_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_02, + BQ2597X_BAT_OCP_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_batocp_th); + +static int bq2597x_enable_batocp_alarm(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_BAT_OCP_ALM_ENABLE; + else + val = BQ2597X_BAT_OCP_ALM_DISABLE; + + val <<= BQ2597X_BAT_OCP_ALM_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_03, + BQ2597X_BAT_OCP_ALM_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_batocp_alarm); + +static int bq2597x_set_batocp_alarm_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold < BQ2597X_BAT_OCP_ALM_BASE) + threshold = BQ2597X_BAT_OCP_ALM_BASE; + + val = (threshold - BQ2597X_BAT_OCP_ALM_BASE) / BQ2597X_BAT_OCP_ALM_LSB; + + val <<= BQ2597X_BAT_OCP_ALM_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_03, + BQ2597X_BAT_OCP_ALM_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_batocp_alarm_th); + +static int bq2597x_set_busovp_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold < BQ2597X_BUS_OVP_BASE) + threshold = BQ2597X_BUS_OVP_BASE; + + val = (threshold - BQ2597X_BUS_OVP_BASE) / BQ2597X_BUS_OVP_LSB; + + val <<= BQ2597X_BUS_OVP_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_06, + BQ2597X_BUS_OVP_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_busovp_th); + +static int bq2597x_enable_busovp_alarm(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_BUS_OVP_ALM_ENABLE; + else + val = BQ2597X_BUS_OVP_ALM_DISABLE; + + val <<= BQ2597X_BUS_OVP_ALM_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_07, + BQ2597X_BUS_OVP_ALM_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_busovp_alarm); + +static int bq2597x_set_busovp_alarm_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold < BQ2597X_BUS_OVP_ALM_BASE) + threshold = BQ2597X_BUS_OVP_ALM_BASE; + + val = (threshold - BQ2597X_BUS_OVP_ALM_BASE) / BQ2597X_BUS_OVP_ALM_LSB; + + val <<= BQ2597X_BUS_OVP_ALM_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_07, + BQ2597X_BUS_OVP_ALM_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_busovp_alarm_th); + +static int bq2597x_enable_busocp(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_BUS_OCP_ENABLE; + else + val = BQ2597X_BUS_OCP_DISABLE; + + val <<= BQ2597X_BUS_OCP_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_08, + BQ2597X_BUS_OCP_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_busocp); + +static int bq2597x_set_busocp_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold < BQ2597X_BUS_OCP_BASE) + threshold = BQ2597X_BUS_OCP_BASE; + + val = (threshold - BQ2597X_BUS_OCP_BASE) / BQ2597X_BUS_OCP_LSB; + + val <<= BQ2597X_BUS_OCP_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_08, + BQ2597X_BUS_OCP_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_busocp_th); + +static int bq2597x_enable_busocp_alarm(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_BUS_OCP_ALM_ENABLE; + else + val = BQ2597X_BUS_OCP_ALM_DISABLE; + + val <<= BQ2597X_BUS_OCP_ALM_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_09, + BQ2597X_BUS_OCP_ALM_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_busocp_alarm); + +static int bq2597x_set_busocp_alarm_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold < BQ2597X_BUS_OCP_ALM_BASE) + threshold = BQ2597X_BUS_OCP_ALM_BASE; + + val = (threshold - BQ2597X_BUS_OCP_ALM_BASE) / BQ2597X_BUS_OCP_ALM_LSB; + + val <<= BQ2597X_BUS_OCP_ALM_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_09, + BQ2597X_BUS_OCP_ALM_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_busocp_alarm_th); + +static int bq2597x_enable_batucp_alarm(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_BAT_UCP_ALM_ENABLE; + else + val = BQ2597X_BAT_UCP_ALM_DISABLE; + + val <<= BQ2597X_BAT_UCP_ALM_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_04, + BQ2597X_BAT_UCP_ALM_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_batucp_alarm); + +static int bq2597x_set_batucp_alarm_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold < BQ2597X_BAT_UCP_ALM_BASE) + threshold = BQ2597X_BAT_UCP_ALM_BASE; + + val = (threshold - BQ2597X_BAT_UCP_ALM_BASE) / BQ2597X_BAT_UCP_ALM_LSB; + + val <<= BQ2597X_BAT_UCP_ALM_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_04, + BQ2597X_BAT_UCP_ALM_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_batucp_alarm_th); + +static int bq2597x_set_acovp_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold < BQ2597X_AC_OVP_BASE) + threshold = BQ2597X_AC_OVP_BASE; + + if (threshold == BQ2597X_AC_OVP_6P5V) + val = 0x07; + else + val = (threshold - BQ2597X_AC_OVP_BASE) / BQ2597X_AC_OVP_LSB; + + val <<= BQ2597X_AC_OVP_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_05, + BQ2597X_AC_OVP_MASK, val); + + return ret; + +} +EXPORT_SYMBOL_GPL(bq2597x_set_acovp_th); + +static int bq2597x_set_vdrop_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold == 300) + val = BQ2597X_VDROP_THRESHOLD_300MV; + else + val = BQ2597X_VDROP_THRESHOLD_400MV; + + val <<= BQ2597X_VDROP_THRESHOLD_SET_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_05, + BQ2597X_VDROP_THRESHOLD_SET_MASK, + val); + + return ret; +} + +static int bq2597x_set_vdrop_deglitch(struct bq2597x *bq, int us) +{ + int ret; + u8 val; + + if (us == 8) + val = BQ2597X_VDROP_DEGLITCH_8US; + else + val = BQ2597X_VDROP_DEGLITCH_5MS; + + val <<= BQ2597X_VDROP_DEGLITCH_SET_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_05, + BQ2597X_VDROP_DEGLITCH_SET_MASK, + val); + return ret; +} + +static int bq2597x_enable_bat_therm(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_TSBAT_ENABLE; + else + val = BQ2597X_TSBAT_DISABLE; + + val <<= BQ2597X_TSBAT_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_0C, + BQ2597X_TSBAT_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_bat_therm); + +/* + * the input threshold is the raw value that would write to register directly. + */ +static int bq2597x_set_bat_therm_th(struct bq2597x *bq, u8 threshold) +{ + int ret; + + ret = bq2597x_write_byte(bq, BQ2597X_REG_29, threshold); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_bat_therm_th); + +static int bq2597x_enable_bus_therm(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_TSBUS_ENABLE; + else + val = BQ2597X_TSBUS_DISABLE; + + val <<= BQ2597X_TSBUS_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_0C, + BQ2597X_TSBUS_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_bus_therm); + +/* + * the input threshold is the raw value that would write to register directly. + */ +static int bq2597x_set_bus_therm_th(struct bq2597x *bq, u8 threshold) +{ + int ret; + + ret = bq2597x_write_byte(bq, BQ2597X_REG_28, threshold); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_bus_therm_th); + +static int bq2597x_enable_die_therm(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_TDIE_ENABLE; + else + val = BQ2597X_TDIE_DISABLE; + + val <<= BQ2597X_TDIE_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_0C, + BQ2597X_TDIE_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_die_therm); + +/* + * please be noted that the unit here is degC + */ +static int bq2597x_set_die_therm_th(struct bq2597x *bq, u8 threshold) +{ + int ret; + u8 val; + + /*BE careful, LSB is here is 1/LSB, so we use multiply here*/ + val = (threshold - BQ2597X_TDIE_ALM_BASE) * BQ2597X_TDIE_ALM_LSB; + val <<= BQ2597X_TDIE_ALM_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_2A, + BQ2597X_TDIE_ALM_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_die_therm_th); + +static int bq2597x_enable_adc(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_ADC_ENABLE; + else + val = BQ2597X_ADC_DISABLE; + + val <<= BQ2597X_ADC_EN_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_14, + BQ2597X_ADC_EN_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_adc); + +static int bq2597x_set_adc_average(struct bq2597x *bq, bool avg) +{ + int ret; + u8 val; + + if (avg) + val = BQ2597X_ADC_AVG_ENABLE; + else + val = BQ2597X_ADC_AVG_DISABLE; + + val <<= BQ2597X_ADC_AVG_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_14, + BQ2597X_ADC_AVG_MASK, val); + return 0; +} +EXPORT_SYMBOL_GPL(bq2597x_set_adc_average); + +static int bq2597x_set_adc_scanrate(struct bq2597x *bq, bool oneshot) +{ + int ret; + u8 val; + + if (oneshot) + val = BQ2597X_ADC_RATE_ONESHOT; + else + val = BQ2597X_ADC_RATE_CONTINOUS; + + val <<= BQ2597X_ADC_RATE_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_14, + BQ2597X_ADC_EN_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_adc_scanrate); + +static int bq2597x_set_adc_bits(struct bq2597x *bq, int bits) +{ + int ret; + u8 val; + + if (bits > 15) + bits = 15; + if (bits < 12) + bits = 12; + val = 15 - bits; + + val <<= BQ2597X_ADC_SAMPLE_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_14, + BQ2597X_ADC_SAMPLE_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_adc_bits); + +#define ADC_REG_BASE 0x16 +static int bq2597x_get_adc_data(struct bq2597x *bq, int channel, int *result) +{ + int ret; + u16 val; + s16 t; + + if (channel > ADC_MAX_NUM) + return -EINVAL; + + ret = bq2597x_read_word(bq, ADC_REG_BASE + (channel << 1), &val); + if (ret < 0) { + pr_err(" bq2597x i2c read failed!\n"); + return ret; + } + t = val & 0xFF; + t <<= 8; + t |= (val >> 8) & 0xFF; + *result = t; + + return 0; +} +EXPORT_SYMBOL_GPL(bq2597x_get_adc_data); + +static int bq2597x_set_adc_scan(struct bq2597x *bq, int channel, bool enable) +{ + int ret; + u8 reg; + u8 mask; + u8 shift; + u8 val; + + if (channel > ADC_MAX_NUM) + return -EINVAL; + + if (channel == ADC_IBUS) { + reg = BQ2597X_REG_14; + shift = BQ2597X_IBUS_ADC_DIS_SHIFT; + mask = BQ2597X_IBUS_ADC_DIS_MASK; + } else { + reg = BQ2597X_REG_15; + shift = 8 - channel; + mask = 1 << shift; + } + + if (enable) + val = 0 << shift; + else + val = 1 << shift; + + ret = bq2597x_update_bits(bq, reg, mask, val); + + return ret; +} + +static int bq2597x_set_alarm_int_mask(struct bq2597x *bq, u8 mask) +{ + int ret; + u8 val; + + ret = bq2597x_read_byte(bq, BQ2597X_REG_0F, &val); + if (ret) + return ret; + + val |= mask; + + ret = bq2597x_write_byte(bq, BQ2597X_REG_0F, val); + + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_alarm_int_mask); + +static int bq2597x_clear_alarm_int_mask(struct bq2597x *bq, u8 mask) +{ + int ret; + u8 val; + + ret = bq2597x_read_byte(bq, BQ2597X_REG_0F, &val); + if (ret) + return ret; + + val &= ~mask; + + ret = bq2597x_write_byte(bq, BQ2597X_REG_0F, val); + + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_clear_alarm_int_mask); + +static int bq2597x_set_fault_int_mask(struct bq2597x *bq, u8 mask) +{ + int ret; + u8 val; + + ret = bq2597x_read_byte(bq, BQ2597X_REG_12, &val); + if (ret) + return ret; + + val |= mask; + + ret = bq2597x_write_byte(bq, BQ2597X_REG_12, val); + + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_fault_int_mask); + +static int bq2597x_clear_fault_int_mask(struct bq2597x *bq, u8 mask) +{ + int ret; + u8 val; + + ret = bq2597x_read_byte(bq, BQ2597X_REG_12, &val); + if (ret) + return ret; + + val &= ~mask; + + ret = bq2597x_write_byte(bq, BQ2597X_REG_12, val); + + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_clear_fault_int_mask); + +static int bq2597x_set_sense_resistor(struct bq2597x *bq, int r_mohm) +{ + int ret; + u8 val; + + if (r_mohm == 2) + val = BQ2597X_SET_IBAT_SNS_RES_2MHM; + else if (r_mohm == 5) + val = BQ2597X_SET_IBAT_SNS_RES_5MHM; + else + return -EINVAL; + + val <<= BQ2597X_SET_IBAT_SNS_RES_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_2B, + BQ2597X_SET_IBAT_SNS_RES_MASK, + val); + return ret; +} + +static int bq2597x_set_ucp_threshold(struct bq2597x *bq, int curr_ma) +{ + int ret; + u8 val; + + if (curr_ma == 300) + val = BQ2597X_IBUS_UCP_RISE_300MA; + else if (curr_ma == 500) + val = BQ2597X_IBUS_UCP_RISE_500MA; + else + val = BQ2597X_IBUS_UCP_RISE_300MA; + + val <<= BQ2597X_IBUS_UCP_RISE_TH_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_2B, + BQ2597X_IBUS_UCP_RISE_TH_MASK, + val); + return ret; +} + +static int bq2597x_enable_regulation(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_EN_REGULATION_ENABLE; + else + val = BQ2597X_EN_REGULATION_DISABLE; + + val <<= BQ2597X_EN_REGULATION_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_2B, + BQ2597X_EN_REGULATION_MASK, + val); + + return ret; + +} + +static int bq2597x_set_ss_timeout(struct bq2597x *bq, int timeout) +{ + int ret; + u8 val; + + switch (timeout) { + case 0: + val = BQ2597X_SS_TIMEOUT_DISABLE; + break; + case 12: + val = BQ2597X_SS_TIMEOUT_SET_12P5MS; + break; + case 25: + val = BQ2597X_SS_TIMEOUT_SET_25MS; + break; + case 50: + val = BQ2597X_SS_TIMEOUT_SET_50MS; + break; + case 100: + val = BQ2597X_SS_TIMEOUT_SET_100MS; + break; + case 400: + val = BQ2597X_SS_TIMEOUT_SET_400MS; + break; + case 1500: + val = BQ2597X_SS_TIMEOUT_SET_1500MS; + break; + case 100000: + val = BQ2597X_SS_TIMEOUT_SET_100000MS; + break; + default: + val = BQ2597X_SS_TIMEOUT_DISABLE; + break; + } + + val <<= BQ2597X_SS_TIMEOUT_SET_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_2B, + BQ2597X_SS_TIMEOUT_SET_MASK, + val); + + return ret; +} + +static int bq2597x_set_ibat_reg_th(struct bq2597x *bq, int th_ma) +{ + int ret; + u8 val; + + if (th_ma == 200) + val = BQ2597X_IBAT_REG_200MA; + else if (th_ma == 300) + val = BQ2597X_IBAT_REG_300MA; + else if (th_ma == 400) + val = BQ2597X_IBAT_REG_400MA; + else if (th_ma == 500) + val = BQ2597X_IBAT_REG_500MA; + else + val = BQ2597X_IBAT_REG_500MA; + + val <<= BQ2597X_IBAT_REG_SHIFT; + ret = bq2597x_update_bits(bq, BQ2597X_REG_2C, + BQ2597X_IBAT_REG_MASK, + val); + + return ret; +} + +static int bq2597x_set_vbat_reg_th(struct bq2597x *bq, int th_mv) +{ + int ret; + u8 val; + + if (th_mv == 50) + val = BQ2597X_VBAT_REG_50MV; + else if (th_mv == 100) + val = BQ2597X_VBAT_REG_100MV; + else if (th_mv == 150) + val = BQ2597X_VBAT_REG_150MV; + else + val = BQ2597X_VBAT_REG_200MV; + + val <<= BQ2597X_VBAT_REG_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_2C, + BQ2597X_VBAT_REG_MASK, + val); + + return ret; +} + +static void bq2597x_dump_reg(struct bq2597x *bq) +{ + int ret; + u8 val; + u8 addr; + + for (addr = 0x00; addr <= 0x2B; addr++) { + ret = bq2597x_read_byte(bq, addr, &val); + if (!ret) + pr_err("Reg[%02X] = 0x%02X\n", addr, val); + } +} + +void bq2597x_dump_reg_g(void) +{ + int ret; + u8 val; + u8 addr; + + for (addr = 0x00; addr <= 0x2B; addr++) { + ret = bq2597x_read_byte(g_chip, addr, &val); + if (!ret) + pr_err("Reg[%02X] = 0x%02X\n", addr, val); + } +} + +EXPORT_SYMBOL(bq2597x_dump_reg_g); + +#if 0 +static int bq2597x_check_reg_status(struct bq2597x *bq) +{ + int ret; + u8 val; + + ret = bq2597x_read_byte(bq, BQ2597X_REG_2C, &val); + if (!ret) { + bq->vbat_reg = !!(val & BQ2597X_VBAT_REG_ACTIVE_STAT_MASK); + bq->ibat_reg = !!(val & BQ2597X_IBAT_REG_ACTIVE_STAT_MASK); + } + + return ret; +} +#endif + +static int bq2597x_detect_device(struct bq2597x *bq) +{ + int ret; + u8 data; + + ret = bq2597x_read_byte(bq, BQ2597X_REG_13, &data); + if (ret == 0) { + bq->part_no = (data & BQ2597X_DEV_ID_MASK); + bq->part_no >>= BQ2597X_DEV_ID_SHIFT; + } + + return ret; +} + +static int bq2597x_parse_dt(struct bq2597x *bq, struct device *dev) +{ + int ret; + struct device_node *np = dev->of_node; + + bq->cfg = devm_kzalloc(dev, sizeof(struct bq2597x_cfg), + GFP_KERNEL); + + if (!bq->cfg) { + pr_err("Out of memory\n"); + return -ENOMEM; + } + + if (of_find_property(np, "vdd-i2c-supply", NULL)) { + bq->vdd_i2c_vreg = devm_regulator_get(bq->dev, "vdd-i2c"); + if (IS_ERR(bq->vdd_i2c_vreg)) { + pr_err("bq2597x get regulator failed\n"); + return PTR_ERR(bq->vdd_i2c_vreg); + } + } + + bq->cfg->bat_ovp_disable = of_property_read_bool(np, + "ti,bq2597x,bat-ovp-disable"); + bq->cfg->bat_ocp_disable = of_property_read_bool(np, + "ti,bq2597x,bat-ocp-disable"); + bq->cfg->bat_ovp_alm_disable = of_property_read_bool(np, + "ti,bq2597x,bat-ovp-alarm-disable"); + bq->cfg->bat_ocp_alm_disable = of_property_read_bool(np, + "ti,bq2597x,bat-ocp-alarm-disable"); + bq->cfg->bus_ocp_disable = of_property_read_bool(np, + "ti,bq2597x,bus-ocp-disable"); + bq->cfg->bus_ovp_alm_disable = of_property_read_bool(np, + "ti,bq2597x,bus-ovp-alarm-disable"); + bq->cfg->bus_ocp_alm_disable = of_property_read_bool(np, + "ti,bq2597x,bus-ocp-alarm-disable"); + bq->cfg->bat_ucp_alm_disable = of_property_read_bool(np, + "ti,bq2597x,bat-ucp-alarm-disable"); + bq->cfg->bat_therm_disable = of_property_read_bool(np, + "ti,bq2597x,bat-therm-disable"); + bq->cfg->bus_therm_disable = of_property_read_bool(np, + "ti,bq2597x,bus-therm-disable"); + bq->cfg->die_therm_disable = of_property_read_bool(np, + "ti,bq2597x,die-therm-disable"); + + ret = of_property_read_u32(np, "ti,bq2597x,bat-ovp-threshold", + &bq->cfg->bat_ovp_th); + if (ret) { + pr_err("failed to read bat-ovp-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bat-ovp-alarm-threshold", + &bq->cfg->bat_ovp_alm_th); + if (ret) { + pr_err("failed to read bat-ovp-alarm-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bat-ocp-threshold", + &bq->cfg->bat_ocp_th); + if (ret) { + pr_err("failed to read bat-ocp-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bat-ocp-alarm-threshold", + &bq->cfg->bat_ocp_alm_th); + if (ret) { + pr_err("failed to read bat-ocp-alarm-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bus-ovp-threshold", + &bq->cfg->bus_ovp_th); + if (ret) { + pr_err("failed to read bus-ovp-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bus-ovp-alarm-threshold", + &bq->cfg->bus_ovp_alm_th); + if (ret) { + pr_err("failed to read bus-ovp-alarm-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bus-ocp-threshold", + &bq->cfg->bus_ocp_th); + if (ret) { + pr_err("failed to read bus-ocp-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bus-ocp-alarm-threshold", + &bq->cfg->bus_ocp_alm_th); + if (ret) { + pr_err("failed to read bus-ocp-alarm-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bat-ucp-alarm-threshold", + &bq->cfg->bat_ucp_alm_th); + if (ret) { + pr_err("failed to read bat-ucp-alarm-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bat-therm-threshold", + &bq->cfg->bat_therm_th); + if (ret) { + pr_err("failed to read bat-therm-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bus-therm-threshold", + &bq->cfg->bus_therm_th); + if (ret) { + pr_err("failed to read bus-therm-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,die-therm-threshold", + &bq->cfg->die_therm_th); + if (ret) { + pr_err("failed to read die-therm-threshold\n"); + return ret; + } + + ret = of_property_read_u32(np, "ti,bq2597x,ac-ovp-threshold", + &bq->cfg->ac_ovp_th); + if (ret) { + pr_err("failed to read ac-ovp-threshold\n"); + return ret; + } + + bq->irq_gpio.gpio = of_get_gpio_flags(np, 0, + (enum of_gpio_flags *)&bq->irq_gpio.flags); + of_property_read_string_index(np, "gpio-names", 0, + &bq->irq_gpio.label); + if (!gpio_is_valid(bq->irq_gpio.gpio)) { + + pr_err("Invalid gpio irq int=%d\n", bq->irq_gpio.gpio); + return -EINVAL; + } + +#if 0 + ret = of_property_read_u32(np, "ti,bq2597x,sense-resistor-mohm", + &bq->cfg->sense_r_mohm); + if (ret) { + pr_err("failed to read sense-resistor-mohm\n"); + return ret; + } +#endif + + return 0; +} + +static int bq2597x_init_protection(struct bq2597x *bq) +{ + int ret; + + ret = bq2597x_enable_batovp(bq, !bq->cfg->bat_ovp_disable); + pr_info("%s bat ovp %s\n", + bq->cfg->bat_ovp_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_batocp(bq, !bq->cfg->bat_ocp_disable); + pr_info("%s bat ocp %s\n", + bq->cfg->bat_ocp_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_batovp_alarm(bq, !bq->cfg->bat_ovp_alm_disable); + pr_info("%s bat ovp alarm %s\n", + bq->cfg->bat_ovp_alm_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_batocp_alarm(bq, !bq->cfg->bat_ocp_alm_disable); + pr_info("%s bat ocp alarm %s\n", + bq->cfg->bat_ocp_alm_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_batucp_alarm(bq, !bq->cfg->bat_ucp_alm_disable); + pr_info("%s bat ocp alarm %s\n", + bq->cfg->bat_ucp_alm_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_busovp_alarm(bq, !bq->cfg->bus_ovp_alm_disable); + pr_info("%s bus ovp alarm %s\n", + bq->cfg->bus_ovp_alm_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_busocp(bq, !bq->cfg->bus_ocp_disable); + pr_info("%s bus ocp %s\n", + bq->cfg->bus_ocp_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_busocp_alarm(bq, !bq->cfg->bus_ocp_alm_disable); + pr_info("%s bus ocp alarm %s\n", + bq->cfg->bus_ocp_alm_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_bat_therm(bq, !bq->cfg->bat_therm_disable); + pr_info("%s bat therm %s\n", + bq->cfg->bat_therm_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_bus_therm(bq, !bq->cfg->bus_therm_disable); + pr_info("%s bus therm %s\n", + bq->cfg->bus_therm_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_die_therm(bq, !bq->cfg->die_therm_disable); + pr_info("%s die therm %s\n", + bq->cfg->die_therm_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_set_batovp_th(bq, bq->cfg->bat_ovp_th); + pr_info("set bat ovp th %d %s\n", bq->cfg->bat_ovp_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_batovp_alarm_th(bq, bq->cfg->bat_ovp_alm_th); + pr_info("set bat ovp alarm threshold %d %s\n", bq->cfg->bat_ovp_alm_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_batocp_th(bq, bq->cfg->bat_ocp_th); + pr_info("set bat ocp threshold %d %s\n", bq->cfg->bat_ocp_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_batocp_alarm_th(bq, bq->cfg->bat_ocp_alm_th); + pr_info("set bat ocp alarm threshold %d %s\n", bq->cfg->bat_ocp_alm_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_busovp_th(bq, bq->cfg->bus_ovp_th); + pr_info("set bus ovp threshold %d %s\n", bq->cfg->bus_ovp_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_busovp_alarm_th(bq, bq->cfg->bus_ovp_alm_th); + pr_info("set bus ovp alarm threshold %d %s\n", bq->cfg->bus_ovp_alm_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_busocp_th(bq, bq->cfg->bus_ocp_th); + pr_info("set bus ocp threshold %d %s\n", bq->cfg->bus_ocp_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_busocp_alarm_th(bq, bq->cfg->bus_ocp_alm_th); + pr_info("set bus ocp alarm th %d %s\n", bq->cfg->bus_ocp_alm_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_batucp_alarm_th(bq, bq->cfg->bat_ucp_alm_th); + pr_info("set bat ucp threshold %d %s\n", bq->cfg->bat_ucp_alm_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_bat_therm_th(bq, bq->cfg->bat_therm_th); + pr_info("set die therm threshold %d %s\n", bq->cfg->bat_therm_th, + !ret ? "successfully" : "failed"); + ret = bq2597x_set_bus_therm_th(bq, bq->cfg->bus_therm_th); + pr_info("set bus therm threshold %d %s\n", bq->cfg->bus_therm_th, + !ret ? "successfully" : "failed"); + ret = bq2597x_set_die_therm_th(bq, bq->cfg->die_therm_th); + pr_info("set die therm threshold %d %s\n", bq->cfg->die_therm_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_acovp_th(bq, bq->cfg->ac_ovp_th); + pr_info("set ac ovp threshold %d %s\n", bq->cfg->ac_ovp_th, + !ret ? "successfully" : "failed"); + + return 0; +} + +static int bq2597x_init_adc(struct bq2597x *bq) +{ + + bq2597x_set_adc_scanrate(bq, false); + bq2597x_set_adc_bits(bq, 13); + bq2597x_set_adc_average(bq, true); + bq2597x_set_adc_scan(bq, ADC_IBUS, true); + bq2597x_set_adc_scan(bq, ADC_VBUS, true); + bq2597x_set_adc_scan(bq, ADC_VOUT, false); + bq2597x_set_adc_scan(bq, ADC_VBAT, true); + bq2597x_set_adc_scan(bq, ADC_IBAT, false); + bq2597x_set_adc_scan(bq, ADC_TBUS, true); + bq2597x_set_adc_scan(bq, ADC_TBAT, true); + bq2597x_set_adc_scan(bq, ADC_TDIE, true); + bq2597x_set_adc_scan(bq, ADC_VAC, true); + + bq2597x_enable_adc(bq, true); + return 0; +} + +static int bq2597x_init_int_src(struct bq2597x *bq) +{ + int ret; + /*TODO:be careful ts bus and ts bat alarm bit mask is in + * fault mask register, so you need call + * bq2597x_set_fault_int_mask for tsbus and tsbat alarm*/ + ret = bq2597x_set_alarm_int_mask(bq, ADC_DONE + /* | BAT_UCP_ALARM*/ + | BAT_OVP_ALARM); + if (ret) { + pr_err("failed to set alarm mask:%d\n", ret); + return ret; + } +#if 0 + ret = bq2597x_set_fault_int_mask(bq, TS_BUS_FAULT); + if (ret) { + pr_err("failed to set fault mask:%d\n", ret); + return ret; + } +#endif + return ret; +} + +static int bq2597x_init_regulation(struct bq2597x *bq) +{ + bq2597x_set_ibat_reg_th(bq, 300); + bq2597x_set_vbat_reg_th(bq, 100); + + bq2597x_set_vdrop_deglitch(bq, 5000); + bq2597x_set_vdrop_th(bq, 400); + + bq2597x_enable_regulation(bq, true); + + return 0; +} + +static int bq2597x_init_device(struct bq2597x *bq) +{ + bq2597x_enable_wdt(bq, false); + + bq2597x_set_ss_timeout(bq, 1500); + bq2597x_set_sense_resistor(bq, bq->cfg->sense_r_mohm); + bq2597x_set_ucp_threshold(bq, 300); + + bq2597x_init_protection(bq); + bq2597x_init_adc(bq); + bq2597x_init_int_src(bq); + + bq2597x_init_regulation(bq); + + return 0; +} + +static int bq2597x_set_present(struct bq2597x *bq, bool present) +{ + bq->usb_present = present; + + if (present) + bq2597x_init_device(bq); + return 0; +} + +static ssize_t bq2597x_show_registers(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bq2597x *bq = dev_get_drvdata(dev); + u8 addr; + u8 val; + u8 tmpbuf[300]; + int len; + int idx = 0; + int ret; + + idx = snprintf(buf, PAGE_SIZE, "%s:\n", "bq25970"); + for (addr = 0x0; addr <= 0x2B; addr++) { + ret = bq2597x_read_byte(bq, addr, &val); + if (ret == 0) { + len = snprintf(tmpbuf, PAGE_SIZE - idx, + "Reg[%.2X] = 0x%.2x\n", addr, val); + memcpy(&buf[idx], tmpbuf, len); + idx += len; + } + } + + return idx; +} + +static ssize_t bq2597x_store_register(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct bq2597x *bq = dev_get_drvdata(dev); + int ret; + unsigned int reg; + unsigned int val; + + ret = sscanf(buf, "%x %x", ®, &val); + if (ret == 2 && reg <= 0x2B) { + bq2597x_write_byte(bq, (unsigned char)reg, (unsigned char)val); + } + + return count; +} + +static DEVICE_ATTR(registers, 0660, bq2597x_show_registers, bq2597x_store_register); + +static struct attribute *bq2597x_attributes[] = { + &dev_attr_registers.attr, + NULL, +}; + +static const struct attribute_group bq2597x_attr_group = { + .attrs = bq2597x_attributes, +}; + +static enum power_supply_property bq2597x_charger_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CHARGING_ENABLED, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_NOW_ERROR, +}; + +static void bq2597x_check_alarm_status(struct bq2597x *bq); +static void bq2597x_check_fault_status(struct bq2597x *bq); + +static int bq2597x_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct bq2597x *bq = power_supply_get_drvdata(psy); + int ret; + int result; + + switch (psp) { + case POWER_SUPPLY_PROP_CHARGING_ENABLED: + bq2597x_check_charge_enabled(bq, &bq->charge_enabled); + val->intval = bq->charge_enabled; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = bq->vbus_present; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = bq2597x_get_adc_data(bq, ADC_VBAT, &result); + if (!ret) + bq->vbat_volt = result; + + val->intval = bq->vbat_volt; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = bq2597x_get_adc_data(bq, ADC_IBAT, &result); + if (!ret) + bq->ibat_curr = result; + + val->intval = bq->ibat_curr; + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED: + ret = bq2597x_get_adc_data(bq, ADC_VBUS, &result); + if (!ret) + bq->vbus_volt = result; + + val->intval = bq->vbus_volt; + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_NOW: + ret = bq2597x_get_adc_data(bq, ADC_IBUS, &result); + if (!ret) + bq->ibus_curr = result; + + val->intval = bq->ibus_curr; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW_ERROR: + bq2597x_check_alarm_status(bq); + bq2597x_check_fault_status(bq); + val->intval = ((bq->bat_ovp_alarm << BAT_OVP_ALARM_SHIFT) + | (bq->bat_ocp_alarm << BAT_OCP_ALARM_SHIFT) + | (bq->bat_ucp_alarm << BAT_UCP_ALARM_SHIFT) + | (bq->bus_ovp_alarm << BUS_OVP_ALARM_SHIFT) + | (bq->bus_ocp_alarm << BUS_OCP_ALARM_SHIFT) + | (bq->bat_therm_alarm << BAT_THERM_ALARM_SHIFT) + | (bq->bus_therm_alarm << BUS_THERM_ALARM_SHIFT) + | (bq->die_therm_alarm << DIE_THERM_ALARM_SHIFT) + | (bq->bat_ovp_fault << BAT_OVP_FAULT_SHIFT) + | (bq->bat_ocp_fault << BAT_OCP_FAULT_SHIFT) + | (bq->bus_ovp_fault << BUS_OVP_FAULT_SHIFT) + | (bq->bus_ocp_fault << BUS_OCP_FAULT_SHIFT) + | (bq->bat_therm_fault << BAT_THERM_FAULT_SHIFT) + | (bq->bus_therm_fault << BUS_THERM_FAULT_SHIFT) + | (bq->die_therm_fault << DIE_THERM_FAULT_SHIFT)); + break; + default: + return -EINVAL; + + } + return 0; +} + +static int bq2597x_charger_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct bq2597x *bq = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_CHARGING_ENABLED: + + bq2597x_enable_charge(bq, val->intval); + bq2597x_check_charge_enabled(bq, &bq->charge_enabled); + pr_info("POWER_SUPPLY_PROP_CHARGING_ENABLED: %s\n", + val->intval ? "enable" : "disable"); + + break; + case POWER_SUPPLY_PROP_PRESENT: + bq2597x_set_present(bq, !!val->intval); + pr_info("set present :%d\n", val->intval); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int bq2597x_charger_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + int ret; + + switch (prop) { + case POWER_SUPPLY_PROP_CHARGING_ENABLED: + ret = 1; + break; + default: + ret = 0; + break; + } + return ret; +} + +static const struct power_supply_desc bq2597x_psy_desc = { + .name = "bq2597x", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = bq2597x_charger_props, + .num_properties = ARRAY_SIZE(bq2597x_charger_props), + .get_property = bq2597x_charger_get_property, + .set_property = bq2597x_charger_set_property, + .property_is_writeable = bq2597x_charger_is_writeable, +}; + +static int bq2597x_psy_register(struct bq2597x *bq) +{ + struct power_supply_config bq2597x_cfg = {}; + + bq2597x_cfg.drv_data = bq; + bq2597x_cfg.of_node = bq->dev->of_node; + bq->fc2_psy = power_supply_register(bq->dev, &bq2597x_psy_desc, &bq2597x_cfg); + if (IS_ERR(bq->fc2_psy)) { + pr_err("Couldn't register bq2597x power supply\n"); + return PTR_ERR(bq->fc2_psy); + } + + pr_info("power supply register bq2597x successfully\n"); + return 0; +} + +EXPORT_SYMBOL_GPL(bq2597x_dump_reg); + +static void bq2597x_check_alarm_status(struct bq2597x *bq) +{ + int ret; + u8 flag = 0; + u8 stat = 0; + bool changed = false; + + mutex_lock(&bq->data_lock); + ret = bq2597x_read_byte(bq, BQ2597X_REG_08, &flag); + if (!ret && (flag & BQ2597X_IBUS_UCP_FALL_FLAG_MASK)) + pr_debug("UCP_FLAG =0x%02X\n", !!(flag & BQ2597X_IBUS_UCP_FALL_FLAG_MASK)); + + + ret = bq2597x_read_byte(bq, BQ2597X_REG_2D, &flag); + if (!ret && (flag & BQ2597X_VDROP_OVP_FLAG_MASK)) + pr_debug("VDROP_OVP_FLAG =0x%02X\n", !!(flag & BQ2597X_VDROP_OVP_FLAG_MASK)); + /*read to clear alarm flag*/ + ret = bq2597x_read_byte(bq, BQ2597X_REG_0E, &flag); + if (!ret && flag) + pr_debug("INT_FLAG =0x%02X\n", flag); + + ret = bq2597x_read_byte(bq, BQ2597X_REG_0D, &stat); + if (!ret && stat != bq->prev_alarm) { + pr_debug("INT_STAT = 0X%02x\n", stat); + changed = true; + bq->prev_alarm = stat; + bq->bat_ovp_alarm = !!(stat & BAT_OVP_ALARM); + bq->bat_ocp_alarm = !!(stat & BAT_OCP_ALARM); + bq->bus_ovp_alarm = !!(stat & BUS_OVP_ALARM); + bq->bus_ocp_alarm = !!(stat & BUS_OCP_ALARM); + bq->batt_present = !!(stat & VBAT_INSERT); + bq->vbus_present = !!(stat & VBUS_INSERT); + bq->bat_ucp_alarm = !!(stat & BAT_UCP_ALARM); + } + + ret = bq2597x_read_byte(bq, BQ2597X_REG_08, &stat); + if (!ret && (stat & 0x50)) + pr_err("Reg[05]BUS_UCPOVP = 0x%02X\n", stat); + + ret = bq2597x_read_byte(bq, BQ2597X_REG_0A, &stat); + if (!ret && (stat & 0x02)) + pr_err("Reg[0A]CONV_OCP = 0x%02X\n", stat); + + mutex_unlock(&bq->data_lock); + + if (changed) + power_supply_changed(bq->fc2_psy); + +} + +static void bq2597x_check_fault_status(struct bq2597x *bq) +{ + int ret; + u8 flag = 0; + u8 stat = 0; + bool changed = false; + + mutex_lock(&bq->data_lock); + + ret = bq2597x_read_byte(bq, BQ2597X_REG_10, &stat); + if (!ret && stat) + pr_err("FAULT_STAT = 0x%02X\n", stat); + + ret = bq2597x_read_byte(bq, BQ2597X_REG_11, &flag); + if (!ret && flag) + pr_err("FAULT_FLAG = 0x%02X\n", flag); + + if (!ret && flag != bq->prev_fault) { + changed = true; + bq->prev_fault = flag; + bq->bat_ovp_fault = !!(flag & BAT_OVP_FAULT); + bq->bat_ocp_fault = !!(flag & BAT_OCP_FAULT); + bq->bus_ovp_fault = !!(flag & BUS_OVP_FAULT); + bq->bus_ocp_fault = !!(flag & BUS_OCP_FAULT); + bq->bat_therm_fault = !!(flag & TS_BAT_FAULT); + bq->bus_therm_fault = !!(flag & TS_BUS_FAULT); + + bq->bat_therm_alarm = !!(flag & TBUS_TBAT_ALARM); + bq->bus_therm_alarm = !!(flag & TBUS_TBAT_ALARM); + } + + mutex_unlock(&bq->data_lock); + + if (changed) + power_supply_changed(bq->fc2_psy); + +} + +static irqreturn_t bq2597x_charger_interrupt(int irq, void *dev_id) +{ + struct bq2597x *bq = dev_id; + + pr_debug("interrupt enter -\n"); + mutex_lock(&bq->irq_complete); + bq->irq_waiting = true; + if (!bq->resume_completed) { + dev_dbg(bq->dev, "IRQ triggered before device-resume\n"); + if (!bq->irq_disabled) { + disable_irq_nosync(irq); + bq->irq_disabled = true; + } + mutex_unlock(&bq->irq_complete); + return IRQ_HANDLED; + } + bq->irq_waiting = false; + + /* TODO */ + bq2597x_check_alarm_status(bq); + bq2597x_check_fault_status(bq); + + bq2597x_dump_reg(bq); + mutex_unlock(&bq->irq_complete); + + pr_err("stat=0x%02X, fault flag =0x%02X\n", bq->prev_alarm, bq->prev_fault); + + return IRQ_HANDLED; +} + +static void determine_initial_status(struct bq2597x *bq) +{ + bq2597x_charger_interrupt(bq->client->irq, bq); +} + +static int show_registers(struct seq_file *m, void *data) +{ + struct bq2597x *bq = m->private; + u8 addr; + int ret; + u8 val; + + for (addr = 0x0; addr <= 0x2B; addr++) { + ret = bq2597x_read_byte(bq, addr, &val); + if (!ret) + seq_printf(m, "Reg-[%02X] = 0x%02X\n", addr, val); + } + return 0; +} + +static int reg_debugfs_open(struct inode *inode, struct file *file) +{ + struct bq2597x *bq = inode->i_private; + + return single_open(file, show_registers, bq); +} + +static const struct file_operations reg_debugfs_ops = { + .owner = THIS_MODULE, + .open = reg_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void create_debugfs_entry(struct bq2597x *bq) +{ + bq->debug_root = debugfs_create_dir("bq2597x", NULL); + if (!bq->debug_root) + pr_err("Failed to create debug dir\n"); + + if (bq->debug_root) { + debugfs_create_file("registers", + S_IFREG | S_IRUGO, + bq->debug_root, bq, ®_debugfs_ops); + + debugfs_create_x32("skip_reads", + S_IFREG | S_IWUSR | S_IRUGO, + bq->debug_root, + &(bq->skip_reads)); + debugfs_create_x32("skip_writes", + S_IFREG | S_IWUSR | S_IRUGO, + bq->debug_root, + &(bq->skip_writes)); + } +} + +static const struct regmap_config bq2597x_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0xFFFF, +}; + +static int bq2597x_charger_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bq2597x *bq; + int ret; + + bq = devm_kzalloc(&client->dev, sizeof(struct bq2597x), GFP_KERNEL); + if (!bq) { + pr_err("Out of memory\n"); + return -ENOMEM; + } + + bq->dev = &client->dev; + + bq->client = client; + i2c_set_clientdata(client, bq); + + mutex_init(&bq->i2c_rw_lock); + mutex_init(&bq->data_lock); + mutex_init(&bq->charging_disable_lock); + mutex_init(&bq->irq_complete); + + bq->resume_completed = true; + bq->irq_waiting = false; + + bq->regmap = regmap_init_i2c(client, &bq2597x_regmap_config); + if (IS_ERR(bq->regmap)) { + pr_err("Couldn't initialize register regmap rc = %ld\n", + PTR_ERR(bq->regmap)); + ret = PTR_ERR(bq->regmap); + goto free_mem; + } + + if (client->dev.of_node) { + ret = bq2597x_parse_dt(bq, &client->dev); + if (ret) + goto free_regulator; + } + + if (regulator_count_voltages(bq->vdd_i2c_vreg) > 0) { + ret = regulator_set_voltage(bq->vdd_i2c_vreg, + 1800000, 1800000); + if (ret) { + pr_err("Failed to set vreg voltage, rc=%d\n", + ret); + goto free_regulator; + } + } + ret = regulator_enable(bq->vdd_i2c_vreg); + if (ret) { + pr_err("Failed to enable vdd vreg, rc=%d\n", ret); + goto disable_regator;; + } + + ret = bq2597x_detect_device(bq); + if (ret) { + pr_err("No bq2597x device found!\n"); + goto disable_regator; + } + + ret = bq2597x_init_device(bq); + if (ret) { + pr_err("Failed to init device\n"); + goto disable_regator; + } + + ret = bq2597x_psy_register(bq); + if (ret) { + pr_err("Failed to register psy\n"); + goto free_regulator; + } + + bq->irq_pinctrl = + pinctrl_get_select(bq->dev, "bq_int_default"); + if (!bq->irq_pinctrl) { + pr_err("Couldn't set pinctrl bq_int_default\n"); + goto free_psy; + } + + ret = gpio_request_one(bq->irq_gpio.gpio, + bq->irq_gpio.flags, + bq->irq_gpio.label); + if (ret) { + pr_err("failed to request irq GPIO, ret %d, gpio %d, lable %s\n", + ret, bq->irq_gpio.gpio, bq->irq_gpio.label); + goto free_psy; + } + + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, bq2597x_charger_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "bq2597x charger irq", bq); + if (ret < 0) { + pr_err("request irq for irq=%d failed, ret =%d\n", + client->irq, ret); + goto free_gpio; + } + enable_irq_wake(client->irq); + } + + device_init_wakeup(bq->dev, 1); + create_debugfs_entry(bq); + ret = sysfs_create_group(&bq->dev->kobj, &bq2597x_attr_group); + if (ret) { + pr_err("failed to register sysfs. err: %d\n", ret); + goto free_irq; + } + + determine_initial_status(bq); + + pr_info("bq2597x probe successfully-, Part Num:%d\n!", + bq->part_no); + g_chip = bq; + return 0; + +free_irq: + devm_free_irq(&client->dev,client->irq,bq); +free_gpio: + gpio_free(bq->irq_gpio.gpio); +free_psy: + power_supply_unregister(bq->fc2_psy); +disable_regator: + regulator_disable(bq->vdd_i2c_vreg); +free_regulator: + devm_regulator_put(bq->vdd_i2c_vreg); +free_mem: + devm_kfree(bq->dev, bq); + return ret; +} + +static inline bool is_device_suspended(struct bq2597x *bq) +{ + return !bq->resume_completed; +} + +static int bq2597x_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq2597x *bq = i2c_get_clientdata(client); + + mutex_lock(&bq->irq_complete); + bq->resume_completed = false; + mutex_unlock(&bq->irq_complete); + pr_err("Suspend successfully!"); + + return 0; +} + +static int bq2597x_suspend_noirq(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq2597x *bq = i2c_get_clientdata(client); + + if (bq->irq_waiting) { + pr_err_ratelimited("Aborting suspend, an interrupt was detected while suspending\n"); + return -EBUSY; + } + return 0; +} + +static int bq2597x_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq2597x *bq = i2c_get_clientdata(client); + + pr_err("Resume begin"); + mutex_lock(&bq->irq_complete); + bq->resume_completed = true; + if (bq->irq_waiting) { + bq->irq_disabled = false; + enable_irq(client->irq); + mutex_unlock(&bq->irq_complete); + bq2597x_charger_interrupt(client->irq, bq); + } else { + mutex_unlock(&bq->irq_complete); + } + + power_supply_changed(bq->fc2_psy); + pr_err("Resume successfully!"); + + return 0; +} +static int bq2597x_charger_remove(struct i2c_client *client) +{ + struct bq2597x *bq = i2c_get_clientdata(client); + + bq2597x_enable_adc(bq, false); + + devm_regulator_put(bq->vdd_i2c_vreg); + devm_free_irq(&client->dev,client->irq,bq); + gpio_free(bq->irq_gpio.gpio); + power_supply_unregister(bq->fc2_psy); + + mutex_destroy(&bq->charging_disable_lock); + mutex_destroy(&bq->data_lock); + mutex_destroy(&bq->i2c_rw_lock); + mutex_destroy(&bq->irq_complete); + + debugfs_remove_recursive(bq->debug_root); + + sysfs_remove_group(&bq->dev->kobj, &bq2597x_attr_group); + + return 0; +} + +static void bq2597x_charger_shutdown(struct i2c_client *client) +{ + struct bq2597x *bq = i2c_get_clientdata(client); + bq2597x_enable_adc(bq, false); + pr_info("Shutdown Successfully\n"); +} + +static struct of_device_id bq2597x_charger_match_table[] = { + {.compatible = "ti,bq25970-charger",}, + {}, +}; +MODULE_DEVICE_TABLE(of, bq2597x_charger_match_table); + +static const struct i2c_device_id bq2597x_charger_id[] = { + { "bq25970-charger", BQ25970 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, bq2597x_charger_id); + +static const struct dev_pm_ops bq2597x_pm_ops = { + .resume = bq2597x_resume, + .suspend_noirq = bq2597x_suspend_noirq, + .suspend = bq2597x_suspend, +}; +static struct i2c_driver bq2597x_charger_driver = { + .driver = { + .name = "bq2597x-charger", + .owner = THIS_MODULE, + .of_match_table = bq2597x_charger_match_table, + .pm = &bq2597x_pm_ops, + }, + .id_table = bq2597x_charger_id, + + .probe = bq2597x_charger_probe, + .remove = bq2597x_charger_remove, + .shutdown = bq2597x_charger_shutdown, +}; + +module_i2c_driver(bq2597x_charger_driver); + +MODULE_DESCRIPTION("TI BQ2597x Charger Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Texas Instruments"); + diff --git a/drivers/power/bq2597x_mmi/bq25980_charger.c b/drivers/power/bq2597x_mmi/bq25980_charger.c new file mode 100644 index 000000000000..6b10454168f2 --- /dev/null +++ b/drivers/power/bq2597x_mmi/bq25980_charger.c @@ -0,0 +1,1907 @@ +// SPDX-License-Identifier: GPL-2.0 +// BQ25980 Battery Charger Driver +// Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bq25980_charger.h" + +//undef because of pinctrl +//#define CONFIG_INTERRUPT_AS_GPIO + +struct bq25980_state { + bool dischg; + bool ovp; + bool ocp; + bool wdt; + bool tflt; + bool online; + bool ce; + bool hiz; + bool bypass; + + u32 vbat_adc; + u32 vsys_adc; + u32 ibat_adc; + u32 fault_status; +}; + +enum bq_work_mode { + BQ_STANDALONE, + BQ_SLAVE, + BQ_MASTER, +}; + +#define BQ_MODE_COUNT 3 + +enum bq_device_id { + BQ25980 = 0, + BQ25960 = 8, + BQ25975, +}; + +enum bq_compatible_id { + BQ25960_STANDALONE, + BQ25960_SLAVE, + BQ25960_MASTER, + + BQ25980_STANDALONE, + BQ25980_SLAVE, + BQ25980_MASTER, + + BQ25975_STANDALONE, +}; + +struct bq25980_chip_info { + + int model_id; + + const struct regmap_config *regmap_config; + + const struct reg_default *reg_init_values; + + int busocp_sc_def; + int busocp_byp_def; + int busocp_sc_max; + int busocp_byp_max; + int busocp_sc_min; + int busocp_byp_min; + int busocp_step; + int busocp_offset; + + int busovp_sc_def; + int busovp_byp_def; + int busovp_sc_step; + + int busovp_sc_offset; + int busovp_byp_step; + int busovp_byp_offset; + int busovp_sc_min; + int busovp_sc_max; + int busovp_byp_min; + int busovp_byp_max; + + int batovp_def; + int batovp_max; + int batovp_min; + int batovp_step; + int batovp_offset; + + int batocp_def; + int batocp_max; + + int vac_sc_ovp; + int vac_byp_ovp; + + int adc_curr_step; + int adc_vbat_volt_step; + int adc_vbus_volt_step; + int adc_vbus_volt_offset; +}; + +struct bq25980_init_data { + u32 ichg; + u32 bypass_ilim; + u32 sc_ilim; + u32 vreg; + u32 iterm; + u32 iprechg; + u32 bypass_vlim; + u32 sc_vlim; + u32 ichg_max; + u32 vreg_max; +}; + +struct bq25980_device { + struct i2c_client *client; + struct device *dev; + struct power_supply *charger; + struct power_supply *battery; + struct mutex lock; + struct regmap *regmap; + + char model_name[I2C_NAME_SIZE]; + + struct bq25980_init_data init_data; + const struct bq25980_chip_info *chip_info; + struct bq25980_state state; + int watchdog_timer; + int mode; + int device_id; + struct power_supply_desc psy_desc; + struct pinctrl *irq_pinctrl; + bool usb_present; + int irq_counts; + bool irq_waiting; + bool irq_disabled; + bool resume_completed; + struct mutex irq_complete; +}; + +static struct reg_default bq25980_reg_init_val[] = { + {BQ25980_BATOVP, 0x69},//0x69:9100mV + {BQ25980_BATOVP_ALM, 0x61},//0x61:8940mV + {BQ25980_BATOCP, 0xEE},//0xEE:disable for dual //0x64:11000mA for standalone + {BQ25980_BATOCP_ALM, 0x7F},//0x7F:12700mA + {BQ25980_CHRGR_CFG_1, 0xA8}, + {BQ25980_CHRGR_CTRL_1, 0x4A}, + {BQ25980_BUSOVP, 0x3C},//0X3c:20000mV + {BQ25980_BUSOVP_ALM, 0x32},//0X32:19000mV + {BQ25980_BUSOCP, 0x0C},//0X0c:4200mA + {BQ25980_REG_09, 0x8C}, + {BQ25980_TEMP_CONTROL, 0x2C}, + {BQ25980_TDIE_ALM, 0x78},//0x78:85C + {BQ25980_TSBUS_FLT, 0x15}, + {BQ25980_TSBAT_FLG, 0x15}, + {BQ25980_VAC_CONTROL, 0xD8},//0xD8:22V + {BQ25980_CHRGR_CTRL_2, 0x00}, + {BQ25980_CHRGR_CTRL_3, 0x74},//0x74:watchdog disable 5s,275kHz + {BQ25980_CHRGR_CTRL_4, 0x01}, + {BQ25980_CHRGR_CTRL_5, 0x00}, + + {BQ25980_MASK1, 0x00}, + {BQ25980_MASK2, 0x00}, + {BQ25980_MASK3, 0x00}, + {BQ25980_MASK4, 0x00}, + {BQ25980_MASK5, 0x80}, + + {BQ25980_ADC_CONTROL1, 0x04},//sample 14 bit + {BQ25980_ADC_CONTROL2, 0xE6}, +}; + +static struct reg_default bq25960_reg_init_val[] = { + {BQ25980_BATOVP, 0x6c},//0x69:4550mV 0x6c:4580mv + {BQ25980_BATOVP_ALM, 0x64},//0x61:4470mV 0x64:4500mv + {BQ25980_BATOCP, 0xEE},//0xEE:disable for dual //0x46:7000mA for standalone + {BQ25980_BATOCP_ALM, 0x7F},//0x7F:12700mA + {BQ25980_CHRGR_CFG_1, 0xA8}, + {BQ25980_CHRGR_CTRL_1, 0x49}, + {BQ25980_BUSOVP, 0x64},//0X50:11000mV 0x64:12000mv + {BQ25980_BUSOVP_ALM, 0x50},//0X46:10500mV 0X50:11000mV + {BQ25980_BUSOCP, 0x0C},//0X0c:4000mA + {BQ25980_REG_09, 0x8C}, + {BQ25980_TEMP_CONTROL, 0x2C}, + {BQ25980_TDIE_ALM, 0x78},//0x78:85C + {BQ25980_TSBUS_FLT, 0x15}, + {BQ25980_TSBAT_FLG, 0x15}, + {BQ25980_VAC_CONTROL, 0x48},//0x48:12V*2 + {BQ25980_CHRGR_CTRL_2, 0x00}, + {BQ25980_CHRGR_CTRL_3, 0x94},//0x94:watchdog disable 5s,500kHz + {BQ25980_CHRGR_CTRL_4, 0x81},//5m oum battery sense resister + {BQ25980_CHRGR_CTRL_5, 0x60}, + + {BQ25980_MASK1, 0x00}, + {BQ25980_MASK2, 0x00}, + {BQ25980_MASK3, 0x00}, + {BQ25980_MASK4, 0x00}, + {BQ25980_MASK5, 0x80}, + + {BQ25980_ADC_CONTROL1, 0x84},//sample 14 bit + {BQ25980_ADC_CONTROL2, 0xE6}, + +}; + +static struct reg_default bq25980_reg_defs[] = { + {BQ25980_BATOVP, 0x5A}, + {BQ25980_BATOVP_ALM, 0x46}, + {BQ25980_BATOCP, 0x51}, + {BQ25980_BATOCP_ALM, 0x50}, + {BQ25980_CHRGR_CFG_1, 0x28}, + {BQ25980_CHRGR_CTRL_1, 0x0}, + {BQ25980_BUSOVP, 0x26}, + {BQ25980_BUSOVP_ALM, 0x22}, + {BQ25980_BUSOCP, 0xD}, + {BQ25980_REG_09, 0xC}, + {BQ25980_TEMP_CONTROL, 0x30}, + {BQ25980_TDIE_ALM, 0xC8}, + {BQ25980_TSBUS_FLT, 0x15}, + {BQ25980_TSBAT_FLG, 0x15}, + {BQ25980_VAC_CONTROL, 0x0}, + {BQ25980_CHRGR_CTRL_2, 0x0}, + {BQ25980_CHRGR_CTRL_3, 0x20}, + {BQ25980_CHRGR_CTRL_4, 0x1D}, + {BQ25980_CHRGR_CTRL_5, 0x18}, + {BQ25980_STAT1, 0x0}, + {BQ25980_STAT2, 0x0}, + {BQ25980_STAT3, 0x0}, + {BQ25980_STAT4, 0x0}, + {BQ25980_STAT5, 0x0}, + {BQ25980_FLAG1, 0x0}, + {BQ25980_FLAG2, 0x0}, + {BQ25980_FLAG3, 0x0}, + {BQ25980_FLAG4, 0x0}, + {BQ25980_FLAG5, 0x0}, + {BQ25980_MASK1, 0x0}, + {BQ25980_MASK2, 0x0}, + {BQ25980_MASK3, 0x0}, + {BQ25980_MASK4, 0x0}, + {BQ25980_MASK5, 0x0}, + {BQ25980_DEVICE_INFO, 0x8}, + {BQ25980_ADC_CONTROL1, 0x0}, + {BQ25980_ADC_CONTROL2, 0x0}, + {BQ25980_IBUS_ADC_LSB, 0x0}, + {BQ25980_IBUS_ADC_MSB, 0x0}, + {BQ25980_VBUS_ADC_LSB, 0x0}, + {BQ25980_VBUS_ADC_MSB, 0x0}, + {BQ25980_VAC1_ADC_LSB, 0x0}, + {BQ25980_VAC2_ADC_LSB, 0x0}, + {BQ25980_VOUT_ADC_LSB, 0x0}, + {BQ25980_VBAT_ADC_LSB, 0x0}, + {BQ25980_IBAT_ADC_MSB, 0x0}, + {BQ25980_IBAT_ADC_LSB, 0x0}, + {BQ25980_TSBUS_ADC_LSB, 0x0}, + {BQ25980_TSBAT_ADC_LSB, 0x0}, + {BQ25980_TDIE_ADC_LSB, 0x0}, + {BQ25980_DEGLITCH_TIME, 0x0}, + {BQ25980_CHRGR_CTRL_6, 0x0}, +}; + +static struct reg_default bq25975_reg_defs[] = { + {BQ25980_BATOVP, 0x5A}, + {BQ25980_BATOVP_ALM, 0x46}, + {BQ25980_BATOCP, 0x51}, + {BQ25980_BATOCP_ALM, 0x50}, + {BQ25980_CHRGR_CFG_1, 0x28}, + {BQ25980_CHRGR_CTRL_1, 0x0}, + {BQ25980_BUSOVP, 0x26}, + {BQ25980_BUSOVP_ALM, 0x22}, + {BQ25980_BUSOCP, 0xD}, + {BQ25980_REG_09, 0xC}, + {BQ25980_TEMP_CONTROL, 0x30}, + {BQ25980_TDIE_ALM, 0xC8}, + {BQ25980_TSBUS_FLT, 0x15}, + {BQ25980_TSBAT_FLG, 0x15}, + {BQ25980_VAC_CONTROL, 0x0}, + {BQ25980_CHRGR_CTRL_2, 0x0}, + {BQ25980_CHRGR_CTRL_3, 0x20}, + {BQ25980_CHRGR_CTRL_4, 0x1D}, + {BQ25980_CHRGR_CTRL_5, 0x18}, + {BQ25980_STAT1, 0x0}, + {BQ25980_STAT2, 0x0}, + {BQ25980_STAT3, 0x0}, + {BQ25980_STAT4, 0x0}, + {BQ25980_STAT5, 0x0}, + {BQ25980_FLAG1, 0x0}, + {BQ25980_FLAG2, 0x0}, + {BQ25980_FLAG3, 0x0}, + {BQ25980_FLAG4, 0x0}, + {BQ25980_FLAG5, 0x0}, + {BQ25980_MASK1, 0x0}, + {BQ25980_MASK2, 0x0}, + {BQ25980_MASK3, 0x0}, + {BQ25980_MASK4, 0x0}, + {BQ25980_MASK5, 0x0}, + {BQ25980_DEVICE_INFO, 0x8}, + {BQ25980_ADC_CONTROL1, 0x0}, + {BQ25980_ADC_CONTROL2, 0x0}, + {BQ25980_IBUS_ADC_LSB, 0x0}, + {BQ25980_IBUS_ADC_MSB, 0x0}, + {BQ25980_VBUS_ADC_LSB, 0x0}, + {BQ25980_VBUS_ADC_MSB, 0x0}, + {BQ25980_VAC1_ADC_LSB, 0x0}, + {BQ25980_VAC2_ADC_LSB, 0x0}, + {BQ25980_VOUT_ADC_LSB, 0x0}, + {BQ25980_VBAT_ADC_LSB, 0x0}, + {BQ25980_IBAT_ADC_MSB, 0x0}, + {BQ25980_IBAT_ADC_LSB, 0x0}, + {BQ25980_TSBUS_ADC_LSB, 0x0}, + {BQ25980_TSBAT_ADC_LSB, 0x0}, + {BQ25980_TDIE_ADC_LSB, 0x0}, + {BQ25980_DEGLITCH_TIME, 0x0}, + {BQ25980_CHRGR_CTRL_6, 0x0}, +}; + +static struct reg_default bq25960_reg_defs[] = { + {BQ25980_BATOVP, 0x5A}, + {BQ25980_BATOVP_ALM, 0x46}, + {BQ25980_BATOCP, 0x51}, + {BQ25980_BATOCP_ALM, 0x50}, + {BQ25980_CHRGR_CFG_1, 0x28}, + {BQ25980_CHRGR_CTRL_1, 0x0}, + {BQ25980_BUSOVP, 0x26}, + {BQ25980_BUSOVP_ALM, 0x22}, + {BQ25980_BUSOCP, 0xD}, + {BQ25980_REG_09, 0xC}, + {BQ25980_TEMP_CONTROL, 0x30}, + {BQ25980_TDIE_ALM, 0xC8}, + {BQ25980_TSBUS_FLT, 0x15}, + {BQ25980_TSBAT_FLG, 0x15}, + {BQ25980_VAC_CONTROL, 0x0}, + {BQ25980_CHRGR_CTRL_2, 0x0}, + {BQ25980_CHRGR_CTRL_3, 0x20}, + {BQ25980_CHRGR_CTRL_4, 0x1D}, + {BQ25980_CHRGR_CTRL_5, 0x18}, + {BQ25980_STAT1, 0x0}, + {BQ25980_STAT2, 0x0}, + {BQ25980_STAT3, 0x0}, + {BQ25980_STAT4, 0x0}, + {BQ25980_STAT5, 0x0}, + {BQ25980_FLAG1, 0x0}, + {BQ25980_FLAG2, 0x0}, + {BQ25980_FLAG3, 0x0}, + {BQ25980_FLAG4, 0x0}, + {BQ25980_FLAG5, 0x0}, + {BQ25980_MASK1, 0x0}, + {BQ25980_MASK2, 0x0}, + {BQ25980_MASK3, 0x0}, + {BQ25980_MASK4, 0x0}, + {BQ25980_MASK5, 0x0}, + {BQ25980_DEVICE_INFO, 0x8}, + {BQ25980_ADC_CONTROL1, 0x0}, + {BQ25980_ADC_CONTROL2, 0x0}, + {BQ25980_IBUS_ADC_LSB, 0x0}, + {BQ25980_IBUS_ADC_MSB, 0x0}, + {BQ25980_VBUS_ADC_LSB, 0x0}, + {BQ25980_VBUS_ADC_MSB, 0x0}, + {BQ25980_VAC1_ADC_LSB, 0x0}, + {BQ25980_VAC2_ADC_LSB, 0x0}, + {BQ25980_VOUT_ADC_LSB, 0x0}, + {BQ25980_VBAT_ADC_LSB, 0x0}, + {BQ25980_IBAT_ADC_MSB, 0x0}, + {BQ25980_IBAT_ADC_LSB, 0x0}, + {BQ25980_TSBUS_ADC_LSB, 0x0}, + {BQ25980_TSBAT_ADC_LSB, 0x0}, + {BQ25980_TDIE_ADC_LSB, 0x0}, + {BQ25980_DEGLITCH_TIME, 0x0}, + {BQ25980_CHRGR_CTRL_6, 0x0}, +}; + +static int bq25980_reg_init(struct bq25980_device *bq); +//static int bq25980_watchdog_time[BQ25980_NUM_WD_VAL] = {5000, 10000, 50000, +// 300000}; + +static void dump_reg(struct bq25980_device *bq, int start, int end) +{ + int ret; + unsigned int val; + int addr; + + for (addr = start; addr <= end; addr++) { + ret = regmap_read(bq->regmap, addr, &val); + if (!ret) + dev_err(bq->dev, "Reg[%02X] = 0x%02X\n", addr, val); + } +} + +static void dump_all_reg(struct bq25980_device *bq) +{ + int ret; + unsigned int val; + int addr; + + for (addr = 0x00; addr <= 0x37; addr++) { + ret = regmap_read(bq->regmap, addr, &val); + if (!ret) + dev_err(bq->dev, "Reg[%02X] = 0x%02X\n", addr, val); + } +} +/* +static int bq25980_get_adc_enable(struct bq25980_device *bq) +{ + unsigned int reg_code; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_ADC_CONTROL1, ®_code); + if (ret) + return ret; + + return !!(reg_code & BQ25980_ADC_EN); +} +*/ +static int bq25980_set_adc_enable(struct bq25980_device *bq, bool enable) +{ + int ret; + + dev_notice(bq->dev, "%s %d", __FUNCTION__, enable); + + if (enable) + ret = regmap_update_bits(bq->regmap, BQ25980_ADC_CONTROL1, + BQ25980_ADC_EN, BQ25980_ADC_EN); + else + ret = regmap_update_bits(bq->regmap, BQ25980_ADC_CONTROL1, + BQ25980_ADC_EN, 0); + + return ret; +} + +/* +static int bq25980_get_input_curr_lim(struct bq25980_device *bq) +{ + unsigned int busocp_reg_code; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_BUSOCP, &busocp_reg_code); + if (ret) + return ret; + + return (busocp_reg_code * bq->chip_info->busocp_step) + bq->chip_info->busocp_offset; +} + +static int bq25980_set_hiz(struct bq25980_device *bq, int setting) +{ + return regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_EN_HIZ, setting); +}*/ +/* +static int bq25980_set_input_curr_lim(struct bq25980_device *bq, int busocp) +{ + unsigned int busocp_reg_code; + + if (!busocp) + return bq25980_set_hiz(bq, BQ25980_ENABLE_HIZ); + + bq25980_set_hiz(bq, BQ25980_DISABLE_HIZ); + + if (bq->state.bypass) { + busocp = max(busocp, bq->chip_info->busocp_byp_min); + busocp = min(busocp, bq->chip_info->busocp_byp_max); + } else { + busocp = max(busocp, bq->chip_info->busocp_sc_min); + busocp = min(busocp, bq->chip_info->busocp_sc_max); + } + + busocp_reg_code = (busocp - bq->chip_info->busocp_offset) + / bq->chip_info->busocp_step; + + return regmap_write(bq->regmap, BQ25980_BUSOCP, busocp_reg_code); +} + +static int bq25980_get_input_volt_lim(struct bq25980_device *bq) +{ + unsigned int busovp_reg_code; + unsigned int busovp_offset; + unsigned int busovp_step; + int ret; + + if (bq->state.bypass) { + busovp_step = bq->chip_info->busovp_byp_step; + busovp_offset = bq->chip_info->busovp_byp_offset; + } else { + busovp_step = bq->chip_info->busovp_sc_step; + busovp_offset = bq->chip_info->busovp_sc_offset; + } + + ret = regmap_read(bq->regmap, BQ25980_BUSOVP, &busovp_reg_code); + if (ret) + return ret; + + return (busovp_reg_code * busovp_step) + busovp_offset; +} + +static int bq25980_set_input_volt_lim(struct bq25980_device *bq, int busovp) +{ + unsigned int busovp_reg_code; + unsigned int busovp_step; + unsigned int busovp_offset; + int ret; + + if (bq->state.bypass) { + busovp_step = bq->chip_info->busovp_byp_step; + busovp_offset = bq->chip_info->busovp_byp_offset; + if (busovp > bq->chip_info->busovp_byp_max) + busovp = bq->chip_info->busovp_byp_max; + else if (busovp < bq->chip_info->busovp_byp_min) + busovp = bq->chip_info->busovp_byp_min; + } else { + busovp_step = bq->chip_info->busovp_sc_step; + busovp_offset = bq->chip_info->busovp_sc_offset; + if (busovp > bq->chip_info->busovp_sc_max) + busovp = bq->chip_info->busovp_sc_max; + else if (busovp < bq->chip_info->busovp_sc_min) + busovp = bq->chip_info->busovp_sc_min; + } + + busovp_reg_code = (busovp - busovp_offset) / busovp_step; + + ret = regmap_write(bq->regmap, BQ25980_BUSOVP, busovp_reg_code); + if (ret) + return ret; + + return regmap_write(bq->regmap, BQ25980_BUSOVP_ALM, busovp_reg_code); +} +*/ +static int bq25980_get_const_charge_curr(struct bq25980_device *bq) +{ + unsigned int batocp_reg_code; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_BATOCP, &batocp_reg_code); + if (ret) + return ret; + + return (batocp_reg_code & BQ25980_BATOCP_MASK) * + BQ25980_BATOCP_STEP_uA; +} +/* +static int bq25980_set_const_charge_curr(struct bq25980_device *bq, int batocp) +{ + unsigned int batocp_reg_code; + int ret; + + batocp = max(batocp, BQ25980_BATOCP_MIN_uA); + batocp = min(batocp, bq->chip_info->batocp_max); + + batocp_reg_code = batocp / BQ25980_BATOCP_STEP_uA; + + ret = regmap_update_bits(bq->regmap, BQ25980_BATOCP, + BQ25980_BATOCP_MASK, batocp_reg_code); + if (ret) + return ret; + + return regmap_update_bits(bq->regmap, BQ25980_BATOCP_ALM, + BQ25980_BATOCP_MASK, batocp_reg_code); +} +*/ +static int bq25980_get_const_charge_volt(struct bq25980_device *bq) +{ + unsigned int batovp_reg_code; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_BATOVP, &batovp_reg_code); + if (ret) + return ret; + + return ((batovp_reg_code * bq->chip_info->batovp_step) + + bq->chip_info->batovp_offset); +} +/* +static int bq25980_set_const_charge_volt(struct bq25980_device *bq, int batovp) +{ + unsigned int batovp_reg_code; + int ret; + + if (batovp < bq->chip_info->batovp_min) + batovp = bq->chip_info->batovp_min; + + if (batovp > bq->chip_info->batovp_max) + batovp = bq->chip_info->batovp_max; + + batovp_reg_code = (batovp - bq->chip_info->batovp_offset) / + bq->chip_info->batovp_step; + + ret = regmap_write(bq->regmap, BQ25980_BATOVP, batovp_reg_code); + if (ret) + return ret; + + return regmap_write(bq->regmap, BQ25980_BATOVP_ALM, batovp_reg_code); +}*/ +/* +static int bq25980_set_bypass(struct bq25980_device *bq, bool en_bypass) +{ + int ret; + + if (en_bypass) + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_EN_BYPASS, BQ25980_EN_BYPASS); + else + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_EN_BYPASS, en_bypass); + if (ret) + return ret; + + bq->state.bypass = en_bypass; + + return 0; +} +*/ +static int bq25980_set_chg_en(struct bq25980_device *bq, bool en_chg) +{ + int ret; + + if (en_chg) + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_CHG_EN, BQ25980_CHG_EN); + else + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_CHG_EN, en_chg); + if (ret) + return ret; + + bq->state.ce = en_chg; + + return 0; +} + +static int bq25980_get_adc_ibus(struct bq25980_device *bq) +{ + int ibus_adc_lsb, ibus_adc_msb; + u16 ibus_adc; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_IBUS_ADC_MSB, &ibus_adc_msb); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_IBUS_ADC_LSB, &ibus_adc_lsb); + if (ret) + return ret; + + ibus_adc = (ibus_adc_msb << 8) | ibus_adc_lsb; + + if (ibus_adc_msb & BQ25980_ADC_POLARITY_BIT) + return ((ibus_adc ^ 0xffff) + 1) * bq->chip_info->adc_curr_step; + + return ibus_adc * bq->chip_info->adc_curr_step; +} + +static int bq25980_get_adc_vbus(struct bq25980_device *bq) +{ + int vbus_adc_lsb, vbus_adc_msb; + u16 vbus_adc; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_VBUS_ADC_MSB, &vbus_adc_msb); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_VBUS_ADC_LSB, &vbus_adc_lsb); + if (ret) + return ret; + + vbus_adc = (vbus_adc_msb << 8) | vbus_adc_lsb; + + return bq->chip_info->adc_vbus_volt_offset + vbus_adc * bq->chip_info->adc_vbus_volt_step /10; +} + +static int bq25980_get_ibat_adc(struct bq25980_device *bq) +{ + int ret; + int ibat_adc_lsb, ibat_adc_msb; + int ibat_adc; + + ret = regmap_read(bq->regmap, BQ25980_IBAT_ADC_MSB, &ibat_adc_msb); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_IBAT_ADC_LSB, &ibat_adc_lsb); + if (ret) + return ret; + + ibat_adc = (ibat_adc_msb << 8) | ibat_adc_lsb; + + if (ibat_adc_msb & BQ25980_ADC_POLARITY_BIT) + return ((ibat_adc ^ 0xffff) + 1) * BQ25960_ADC_CURR_STEP_uA; + + return ibat_adc * BQ25960_ADC_CURR_STEP_uA; +} + +static int bq25980_get_adc_vbat(struct bq25980_device *bq) +{ + int vsys_adc_lsb, vsys_adc_msb; + u16 vsys_adc; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_VBAT_ADC_MSB, &vsys_adc_msb); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_VBAT_ADC_LSB, &vsys_adc_lsb); + if (ret) + return ret; + + vsys_adc = (vsys_adc_msb << 8) | vsys_adc_lsb; + + return vsys_adc * bq->chip_info->adc_vbat_volt_step / 10; +} + +static int bq25980_get_state(struct bq25980_device *bq, + struct bq25980_state *state) +{ + unsigned int chg_ctrl_2; + unsigned int stat1; + unsigned int stat2; + unsigned int stat3; + unsigned int stat4; + unsigned int ibat_adc_msb; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT1, &stat1); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT2, &stat2); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT3, &stat3); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT4, &stat4); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_CHRGR_CTRL_2, &chg_ctrl_2); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_IBAT_ADC_MSB, &ibat_adc_msb); + if (ret) + return ret; + + state->dischg = ibat_adc_msb & BQ25980_ADC_POLARITY_BIT; + state->ovp = (stat1 & BQ25980_STAT1_OVP_MASK) | + (stat3 & BQ25980_STAT3_OVP_MASK); + state->ocp = (stat1 & BQ25980_STAT1_OCP_MASK) | + (stat2 & BQ25980_STAT2_OCP_MASK); + state->tflt = stat4 & BQ25980_STAT4_TFLT_MASK; + state->wdt = stat4 & BQ25980_WD_STAT; + state->online = stat3 & BQ25980_PRESENT_MASK; + state->ce = chg_ctrl_2 & BQ25980_CHG_EN; + state->hiz = chg_ctrl_2 & BQ25980_EN_HIZ; + state->bypass = chg_ctrl_2 & BQ25980_EN_BYPASS; + + return 0; +} +#if 0 +static int bq25980_set_battery_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct bq25980_device *bq = power_supply_get_drvdata(psy); + int ret = 0; + + if (ret) + return ret; + + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = bq25980_set_const_charge_curr(bq, val->intval); + if (ret) + return ret; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = bq25980_set_const_charge_volt(bq, val->intval); + if (ret) + return ret; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int bq25980_get_battery_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct bq25980_device *bq = power_supply_get_drvdata(psy); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + val->intval = bq->init_data.ichg_max; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + val->intval = bq->init_data.vreg_max; + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = bq25980_get_ibat_adc(bq); + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = bq25980_get_adc_vbat(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; + + default: + return -EINVAL; + } + + return 0; +} +#endif +static int bq25980_set_present(struct bq25980_device *bq, bool present) +{ + bq->usb_present = present; + + if (present) + bq25980_reg_init(bq); + return 0; +} + +static int bq25980_set_charger_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct bq25980_device *bq = power_supply_get_drvdata(psy); + int ret = -EINVAL; + + switch (prop) { +/* + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = bq25980_set_input_curr_lim(bq, val->intval); + if (ret) + return ret; + break; + + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = bq25980_set_input_volt_lim(bq, val->intval); + if (ret) + return ret; + break; +*/ + case POWER_SUPPLY_PROP_CHARGING_ENABLED: + dev_err(bq->dev,"%s,POWER_SUPPLY_PROP_CHARGING_ENABLED prop:%d,value:%d\n",__func__,prop,val->intval); + ret = bq25980_set_chg_en(bq, val->intval); + if (ret) + return ret; + break; +/* + case POWER_SUPPLY_PROP_TI_ADC: + ret = bq25980_set_adc_enable(bq, val->intval); + if (ret) + return ret; + break; + + case POWER_SUPPLY_PROP_TI_BYPASS: + ret = bq25980_set_bypass(bq, val->intval); + if (ret) + return ret; + if (val->intval) + ret = bq25980_set_input_curr_lim(bq, bq->chip_info->busocp_byp_def); + else + ret = bq25980_set_input_curr_lim(bq, bq->chip_info->busocp_sc_def); + if (ret) + return ret; + break; +*/ + case POWER_SUPPLY_PROP_PRESENT: + bq25980_set_present(bq, !!val->intval); +// sc_info("set present :%d\n", val->intval); + dev_err(bq->dev,"%s,POWER_SUPPLY_PROP_PRESENT prop:%d,value:%d\n",__func__,prop,val->intval); + break; + case POWER_SUPPLY_PROP_UPDATE_NOW: + dev_err(bq->dev,"%s,POWER_SUPPLY_PROP_UPDATE_NOW prop:%d,value:%d\n",__func__,prop,val->intval); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int bq25980_get_charger_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct bq25980_device *bq = power_supply_get_drvdata(psy); + struct bq25980_state state; + int ret = 0; + unsigned int chg_ctrl_2 = 0; + unsigned int stat1 = 0; + unsigned int stat2 = 0; + unsigned int stat3 = 0; + unsigned int stat4 = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_CHIP_VERSION: + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = BQ25980_MANUFACTURER; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = bq->model_name; + break; +/* case POWER_SUPPLY_PROP_ONLINE: + ret = regmap_read(bq->regmap, BQ25980_STAT3, &stat3); + if (ret) + return ret; + + state.online = stat3 & BQ25980_PRESENT_MASK; + val->intval = state.online; + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = bq25980_get_input_volt_lim(bq); + if (ret < 0) + return ret; + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = bq25980_get_input_curr_lim(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; +*/ + case POWER_SUPPLY_PROP_CP_STATUS1: + val->intval = 0; + /* + ((sc->bat_ovp_fault << SC8549_VBAT_OVP_FLAG_SHIFT) + | (sc->bat_ocp_fault << SC8549_IBAT_OCP_FLAG_SHIFT) + | (sc->bus_ovp_fault << SC8549_VBUS_OVP_FLAG_SHIFT) + | (sc->bus_ocp_fault << SC8549_IBUS_OCP_FLAG_SHIFT)); + */ + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + + ret = regmap_read(bq->regmap, BQ25980_STAT1, &stat1); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT2, &stat2); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT3, &stat3); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT4, &stat4); + if (ret) + return ret; + + state.ovp = (stat1 & BQ25980_STAT1_OVP_MASK) | + (stat3 & BQ25980_STAT3_OVP_MASK); + state.ocp = (stat1 & BQ25980_STAT1_OCP_MASK) | + (stat2 & BQ25980_STAT2_OCP_MASK); + state.tflt = stat4 & BQ25980_STAT4_TFLT_MASK; + + if (state.tflt) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + else if (state.ovp) + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + else if (state.ocp) + val->intval = POWER_SUPPLY_HEALTH_OVERCURRENT; + + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = bq25980_get_ibat_adc(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = bq25980_get_adc_vbat(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = bq25980_get_const_charge_curr(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = bq25980_get_const_charge_volt(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED: + ret = bq25980_get_adc_vbus(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_NOW: + ret = bq25980_get_adc_ibus(bq); + if (ret < 0) + return ret; + + val->intval = ret; + //dump_all_reg(bq); + break; + case POWER_SUPPLY_PROP_CHARGING_ENABLED: + ret = regmap_read(bq->regmap, BQ25980_CHRGR_CTRL_2, &chg_ctrl_2); + if (ret) + return ret; + + state.ce = chg_ctrl_2 & BQ25980_CHG_EN; + val->intval = state.ce; + break; +/* + case POWER_SUPPLY_PROP_TI_ADC: + ret = bq25980_get_adc_enable(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_TI_BYPASS: + ret = regmap_read(bq->regmap, BQ25980_CHRGR_CTRL_2, &chg_ctrl_2); + if (ret) + return ret; + + state.bypass = chg_ctrl_2 & BQ25980_EN_BYPASS; + val->intval = state.bypass; + break; +*/ + case POWER_SUPPLY_PROP_PRESENT: + val->intval = bq->usb_present; + break; + case POWER_SUPPLY_PROP_UPDATE_NOW: + break; + case POWER_SUPPLY_PROP_CP_IRQ_STATUS: + val->intval = bq->irq_counts; + break; + default: + return -EINVAL; + } +// dev_err(bq->dev,"%s,psp:%d,value:%d\n",__func__,psp,val->intval); + return 0; +} + +static bool bq25980_state_changed(struct bq25980_device *bq, + struct bq25980_state *new_state) +{ + struct bq25980_state old_state; + + mutex_lock(&bq->lock); + old_state = bq->state; + mutex_unlock(&bq->lock); + + return (old_state.dischg != new_state->dischg || + old_state.ovp != new_state->ovp || + old_state.ocp != new_state->ocp || + old_state.online != new_state->online || + old_state.wdt != new_state->wdt || + old_state.tflt != new_state->tflt || + old_state.ce != new_state->ce || + old_state.hiz != new_state->hiz || + old_state.bypass != new_state->bypass); +} + +static irqreturn_t bq25980_irq_handler_thread(int irq, void *private) +{ + struct bq25980_device *bq = private; + struct bq25980_state state; + int ret; + + dev_err(bq->dev,"%s enter\n",__func__); + mutex_lock(&bq->irq_complete); + bq->irq_waiting = true; + if (!bq->resume_completed) { + dev_dbg(bq->dev, "IRQ triggered before device-resume\n"); + if (!bq->irq_disabled) { + disable_irq_nosync(irq); + bq->irq_disabled = true; + } + mutex_unlock(&bq->irq_complete); + return IRQ_HANDLED; + } + bq->irq_waiting = false; + + if(bq->irq_counts > INT_MAX -1) + bq->irq_counts = 0; + else + bq->irq_counts++; + + dump_all_reg(bq); + mutex_unlock(&bq->irq_complete); + + ret = bq25980_get_state(bq, &state); + if (ret < 0) + goto irq_out; + + if (!bq25980_state_changed(bq, &state)) + goto irq_out; + + mutex_lock(&bq->lock); + bq->state = state; + mutex_unlock(&bq->lock); + + power_supply_changed(bq->charger); + +irq_out: + return IRQ_HANDLED; +} + +static enum power_supply_property bq25980_power_supply_props[] = { + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_MODEL_NAME, + //POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_HEALTH, +// POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, +// POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CHARGING_ENABLED, +/* + POWER_SUPPLY_PROP_TI_ADC, + POWER_SUPPLY_PROP_TI_BYPASS, +*/ + + POWER_SUPPLY_PROP_CP_STATUS1, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CHARGING_ENABLED, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, + POWER_SUPPLY_PROP_CP_IRQ_STATUS, + POWER_SUPPLY_PROP_CHIP_VERSION, +}; +#if 0 +static enum power_supply_property bq25980_battery_props[] = { + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; +#endif +static char *bq25980_charger_supplied_to[] = { + "main-battery", +}; + +static int bq25980_property_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + switch (prop) { +// case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + case POWER_SUPPLY_PROP_CHARGING_ENABLED: +// case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + case POWER_SUPPLY_PROP_PRESENT: + case POWER_SUPPLY_PROP_UPDATE_NOW: + // case POWER_SUPPLY_PROP_TI_ADC: +// case POWER_SUPPLY_PROP_TI_BYPASS: + return true; + default: + return false; + } +} + +#if 0 +static struct power_supply_desc bq25980_battery_desc = { + .name = "bq25980-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = bq25980_get_battery_property, + .set_property = bq25980_set_battery_property, + .properties = bq25980_battery_props, + .num_properties = ARRAY_SIZE(bq25980_battery_props), + .property_is_writeable = bq25980_property_is_writeable, +}; +#endif + +static bool bq25980_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BQ25980_CHRGR_CTRL_2: + case BQ25980_STAT1...BQ25980_FLAG5: + case BQ25980_ADC_CONTROL1...BQ25980_TDIE_ADC_LSB: + return true; + default: + return false; + } +} + +static const struct regmap_config bq25980_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BQ25980_CHRGR_CTRL_6, + .reg_defaults = bq25980_reg_defs, + .num_reg_defaults = ARRAY_SIZE(bq25980_reg_defs), + .cache_type = REGCACHE_NONE, + .volatile_reg = bq25980_is_volatile_reg, +}; + +static const struct regmap_config bq25975_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BQ25980_CHRGR_CTRL_6, + .reg_defaults = bq25975_reg_defs, + .num_reg_defaults = ARRAY_SIZE(bq25975_reg_defs), + .cache_type = REGCACHE_NONE, + .volatile_reg = bq25980_is_volatile_reg, +}; + +static const struct regmap_config bq25960_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BQ25980_CHRGR_CTRL_6, + .reg_defaults = bq25960_reg_defs, + .num_reg_defaults = ARRAY_SIZE(bq25960_reg_defs), + .cache_type = REGCACHE_NONE, + .volatile_reg = bq25980_is_volatile_reg, +}; + +static const struct bq25980_chip_info bq25980_chip_info_tbl[] = { + [BQ25980] = { + .model_id = BQ25980, + .regmap_config = &bq25980_regmap_config, + .reg_init_values = bq25980_reg_init_val, + + .busocp_sc_def = BQ25980_BUSOCP_SC_DFLT_uA, + .busocp_byp_def = BQ25980_BUSOCP_BYP_DFLT_uA, + .busocp_sc_min = BQ25980_BUSOCP_MIN_uA, + .busocp_sc_max = BQ25980_BUSOCP_SC_MAX_uA, + .busocp_byp_max = BQ25980_BUSOCP_BYP_MAX_uA, + .busocp_byp_min = BQ25980_BUSOCP_MIN_uA, + .busocp_step = BQ25980_BUSOCP_STEP_uA, + .busocp_offset = BQ25980_BUSOCP_OFFSET_uA, + + .busovp_sc_def = BQ25980_BUSOVP_DFLT_uV, + .busovp_byp_def = BQ25980_BUSOVP_BYPASS_DFLT_uV, + .busovp_sc_step = BQ25980_BUSOVP_SC_STEP_uV, + .busovp_sc_offset = BQ25980_BUSOVP_SC_OFFSET_uV, + .busovp_byp_step = BQ25980_BUSOVP_BYP_STEP_uV, + .busovp_byp_offset = BQ25980_BUSOVP_BYP_OFFSET_uV, + .busovp_sc_min = BQ25980_BUSOVP_SC_MIN_uV, + .busovp_sc_max = BQ25980_BUSOVP_SC_MAX_uV, + .busovp_byp_min = BQ25980_BUSOVP_BYP_MIN_uV, + .busovp_byp_max = BQ25980_BUSOVP_BYP_MAX_uV, + + .batovp_def = BQ25980_BATOVP_DFLT_uV, + .batovp_max = BQ25980_BATOVP_MAX_uV, + .batovp_min = BQ25980_BATOVP_MIN_uV, + .batovp_step = BQ25980_BATOVP_STEP_uV, + .batovp_offset = BQ25980_BATOVP_OFFSET_uV, + + .batocp_def = BQ25980_BATOCP_DFLT_uA, + .batocp_max = BQ25980_BATOCP_MAX_uA, + + .adc_curr_step = BQ25980_ADC_CURR_STEP_IBUS_uA, + .adc_vbat_volt_step = BQ25980_ADC_VOLT_STEP_VBAT_deciuV, + .adc_vbus_volt_step = BQ25980_ADC_VOLT_STEP_VBUS_deciuV, + .adc_vbus_volt_offset = BQ25980_ADC_VOLT_OFFSET_VBUS, + }, + + [BQ25960] = { + .model_id = BQ25960, + .regmap_config = &bq25960_regmap_config, + .reg_init_values = bq25960_reg_init_val, + + .busocp_sc_def = BQ25960_BUSOCP_SC_DFLT_uA, + .busocp_byp_def = BQ25960_BUSOCP_BYP_DFLT_uA, + .busocp_sc_min = BQ25960_BUSOCP_MIN_uA, + .busocp_sc_max = BQ25960_BUSOCP_SC_MAX_uA, + .busocp_byp_min = BQ25960_BUSOCP_MIN_uA, + .busocp_byp_max = BQ25960_BUSOCP_BYP_MAX_uA, + .busocp_step = BQ25960_BUSOCP_STEP_uA, + .busocp_offset = BQ25960_BUSOCP_OFFSET_uA, + + .busovp_sc_def = BQ25975_BUSOVP_DFLT_uV, + .busovp_byp_def = BQ25975_BUSOVP_BYPASS_DFLT_uV, + .busovp_sc_step = BQ25960_BUSOVP_SC_STEP_uV, + .busovp_sc_offset = BQ25960_BUSOVP_SC_OFFSET_uV, + .busovp_byp_step = BQ25960_BUSOVP_BYP_STEP_uV, + .busovp_byp_offset = BQ25960_BUSOVP_BYP_OFFSET_uV, + .busovp_sc_min = BQ25960_BUSOVP_SC_MIN_uV, + .busovp_sc_max = BQ25960_BUSOVP_SC_MAX_uV, + .busovp_byp_min = BQ25960_BUSOVP_BYP_MIN_uV, + .busovp_byp_max = BQ25960_BUSOVP_BYP_MAX_uV, + + .batovp_def = BQ25960_BATOVP_DFLT_uV, + .batovp_max = BQ25960_BATOVP_MAX_uV, + .batovp_min = BQ25960_BATOVP_MIN_uV, + .batovp_step = BQ25960_BATOVP_STEP_uV, + .batovp_offset = BQ25960_BATOVP_OFFSET_uV, + + .batocp_def = BQ25960_BATOCP_DFLT_uA, + .batocp_max = BQ25960_BATOCP_MAX_uA, + + .adc_curr_step = BQ25960_ADC_CURR_STEP_uA, + .adc_vbat_volt_step = BQ25960_ADC_VOLT_STEP_deciuV, + .adc_vbus_volt_step = BQ25960_ADC_VOLT_STEP_deciuV, + .adc_vbus_volt_offset = 0, + }, + + [BQ25975] = { + .model_id = BQ25975, + .regmap_config = &bq25975_regmap_config, + + .busocp_sc_def = BQ25975_BUSOCP_DFLT_uA, + .busocp_byp_def = BQ25975_BUSOCP_DFLT_uA, + .busocp_sc_min = BQ25960_BUSOCP_MIN_uA, + .busocp_sc_max = BQ25975_BUSOCP_SC_MAX_uA, + .busocp_byp_min = BQ25960_BUSOCP_MIN_uA, + .busocp_byp_max = BQ25975_BUSOCP_BYP_MAX_uA, + .busocp_step = BQ25960_BUSOCP_STEP_uA, + .busocp_offset = BQ25960_BUSOCP_OFFSET_uA, + + .busovp_sc_def = BQ25975_BUSOVP_DFLT_uV, + .busovp_byp_def = BQ25975_BUSOVP_BYPASS_DFLT_uV, + .busovp_sc_step = BQ25975_BUSOVP_SC_STEP_uV, + .busovp_sc_offset = BQ25975_BUSOVP_SC_OFFSET_uV, + .busovp_byp_step = BQ25975_BUSOVP_BYP_STEP_uV, + .busovp_byp_offset = BQ25975_BUSOVP_BYP_OFFSET_uV, + .busovp_sc_min = BQ25975_BUSOVP_SC_MIN_uV, + .busovp_sc_max = BQ25975_BUSOVP_SC_MAX_uV, + .busovp_byp_min = BQ25975_BUSOVP_BYP_MIN_uV, + .busovp_byp_max = BQ25975_BUSOVP_BYP_MAX_uV, + + .batovp_def = BQ25975_BATOVP_DFLT_uV, + .batovp_max = BQ25975_BATOVP_MAX_uV, + .batovp_min = BQ25975_BATOVP_MIN_uV, + .batovp_step = BQ25975_BATOVP_STEP_uV, + .batovp_offset = BQ25975_BATOVP_OFFSET_uV, + + .batocp_def = BQ25980_BATOCP_DFLT_uA, + .batocp_max = BQ25980_BATOCP_MAX_uA, + + .adc_curr_step = BQ25960_ADC_CURR_STEP_uA, + .adc_vbat_volt_step = BQ25960_ADC_VOLT_STEP_deciuV, + .adc_vbus_volt_step = BQ25960_ADC_VOLT_STEP_deciuV, + .adc_vbus_volt_offset = 0, + }, + +}; + +static int bq25980_power_supply_init(struct bq25980_device *bq, + struct device *dev, + int driver_data) +{ + struct power_supply_config psy_cfg = { .drv_data = bq, + .of_node = dev->of_node, }; + + psy_cfg.supplied_to = bq25980_charger_supplied_to; + psy_cfg.num_supplicants = ARRAY_SIZE(bq25980_charger_supplied_to); + + switch (driver_data) { + case BQ25980_MASTER: + bq->psy_desc.name = "bq25980-master"; + break; + case BQ25980_SLAVE: + bq->psy_desc.name = "bq25980-slave"; + break; + case BQ25980_STANDALONE: + bq->psy_desc.name = "bq25980-standalone"; + break; + case BQ25960_MASTER: + bq->psy_desc.name = "bq25960-master"; + break; + case BQ25960_SLAVE: + bq->psy_desc.name = "bq25960-slave"; + break; + case BQ25960_STANDALONE: + bq->psy_desc.name = "bq25960-standalone"; + break; + default: + return -EINVAL; + } + + bq->psy_desc.type = POWER_SUPPLY_TYPE_MAINS, + bq->psy_desc.properties = bq25980_power_supply_props, + bq->psy_desc.num_properties = ARRAY_SIZE(bq25980_power_supply_props), + bq->psy_desc.get_property = bq25980_get_charger_property, + bq->psy_desc.set_property = bq25980_set_charger_property, + bq->psy_desc.property_is_writeable = bq25980_property_is_writeable, + + bq->charger = devm_power_supply_register(bq->dev, + &bq->psy_desc, + &psy_cfg); + if (IS_ERR(bq->charger)) { + dev_err(bq->dev, "bq register power supply fail"); + return -EINVAL; + } +/* + if (bq->mode != BQ_SLAVE) { + bq->battery = devm_power_supply_register(bq->dev, + &bq25980_battery_desc, + &psy_cfg); + if (IS_ERR(bq->battery)) { + dev_err(bq->dev, "battery register power supply fail"); + return -EINVAL; + } + } +*/ + return 0; +} + +static int bq25980_reg_init(struct bq25980_device *bq) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(bq25980_reg_init_val); i++) { + ret = regmap_update_bits(bq->regmap, bq->chip_info->reg_init_values[i].reg, + 0xFF, bq->chip_info->reg_init_values[i].def); + dev_notice(bq->dev, "init Reg[%02X] = 0x%02X\n", + bq->chip_info->reg_init_values[i].reg, + bq->chip_info->reg_init_values[i].def); + if (ret) + { + dev_err(bq->dev, "Reg init fail ret=%d", ret); + return ret; + } + } + return 0; +} +#if 0 +static int bq25980_hw_init(struct bq25980_device *bq) +{ + struct power_supply_battery_info bat_info = { }; + int wd_reg_val; + int ret = 0; + int curr_val; + int volt_val; + int i; + + if (!bq->watchdog_timer) { + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_3, + BQ25980_WATCHDOG_DIS, + BQ25980_WATCHDOG_DIS); + } else { + for (i = 0; i < BQ25980_NUM_WD_VAL; i++) { + if (bq->watchdog_timer > bq25980_watchdog_time[i] && + bq->watchdog_timer < bq25980_watchdog_time[i + 1]) { + wd_reg_val = i; + break; + } + } + + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_3, + BQ25980_WATCHDOG_MASK, wd_reg_val); + } + if (ret) + return ret; + + ret = power_supply_get_battery_info(bq->charger, &bat_info); + if (ret) { + dev_warn(bq->dev, "battery info missing\n"); + return -EINVAL; + } + + bq->init_data.ichg_max = bat_info.constant_charge_current_max_ua; + bq->init_data.vreg_max = bat_info.constant_charge_voltage_max_uv; + + if (bq->state.bypass) { + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_EN_BYPASS, BQ25980_EN_BYPASS); + if (ret) + return ret; + + curr_val = bq->init_data.bypass_ilim; + volt_val = bq->init_data.bypass_vlim; + } else { + curr_val = bq->init_data.sc_ilim; + volt_val = bq->init_data.sc_vlim; + } + + ret = bq25980_set_input_curr_lim(bq, curr_val); + if (ret) + return ret; + + ret = bq25980_set_input_volt_lim(bq, volt_val); + if (ret) + return ret; + + return regmap_update_bits(bq->regmap, BQ25980_ADC_CONTROL1, + BQ25980_ADC_EN, BQ25980_ADC_EN); +} +#endif +static int bq25980_parse_dt(struct bq25980_device *bq) +{ + int ret; + + ret = device_property_read_u32(bq->dev, "ti,watchdog-timeout-ms", + &bq->watchdog_timer); + if (ret) + bq->watchdog_timer = BQ25980_WATCHDOG_MIN; + + if (bq->watchdog_timer > BQ25980_WATCHDOG_MAX || + bq->watchdog_timer < BQ25980_WATCHDOG_MIN) + return -EINVAL; + + ret = device_property_read_u32(bq->dev, + "ti,sc-ovp-limit-microvolt", + &bq->init_data.sc_vlim); + if (ret) + bq->init_data.sc_vlim = bq->chip_info->busovp_sc_def; + + if (bq->init_data.sc_vlim > bq->chip_info->busovp_sc_max || + bq->init_data.sc_vlim < bq->chip_info->busovp_sc_min) { + dev_err(bq->dev, "SC ovp limit is out of range\n"); + return -EINVAL; + } + + ret = device_property_read_u32(bq->dev, + "ti,sc-ocp-limit-microamp", + &bq->init_data.sc_ilim); + if (ret) + bq->init_data.sc_ilim = bq->chip_info->busocp_sc_def; + + if (bq->init_data.sc_ilim > bq->chip_info->busocp_sc_max || + bq->init_data.sc_ilim < bq->chip_info->busocp_sc_min) { + dev_err(bq->dev, "SC ocp limit is out of range\n"); + return -EINVAL; + } + + ret = device_property_read_u32(bq->dev, + "ti,bypass-ovp-limit-microvolt", + &bq->init_data.bypass_vlim); + if (ret) + bq->init_data.bypass_vlim = bq->chip_info->busovp_byp_def; + + if (bq->init_data.bypass_vlim > bq->chip_info->busovp_byp_max || + bq->init_data.bypass_vlim < bq->chip_info->busovp_byp_min) { + dev_err(bq->dev, "Bypass ovp limit is out of range\n"); + return -EINVAL; + } + + ret = device_property_read_u32(bq->dev, + "ti,bypass-ocp-limit-microamp", + &bq->init_data.bypass_ilim); + if (ret) + bq->init_data.bypass_ilim = bq->chip_info->busocp_byp_def; + + if (bq->init_data.bypass_ilim > bq->chip_info->busocp_byp_max || + bq->init_data.bypass_ilim < bq->chip_info->busocp_byp_min) { + dev_err(bq->dev, "Bypass ocp limit is out of range\n"); + return -EINVAL; + } + + + bq->state.bypass = device_property_read_bool(bq->dev, + "ti,bypass-enable"); + return 0; +} + +static int bq25980_check_work_mode(struct bq25980_device *bq) +{ + int ret; + int val; + + ret = regmap_read(bq->regmap, BQ25980_CHRGR_CTRL_5, &val); + if (ret) { + dev_err(bq->dev, "Failed to read operation mode register\n"); + return ret; + } + + val = (val & BQ25980_MS_MASK); + if (bq->mode != val) { + dev_err(bq->dev, "dts mode %d mismatch with hardware mode %d\n", bq->mode, val); + return -EINVAL; + } + + dev_info(bq->dev, "work mode:%s\n", bq->mode == BQ_STANDALONE ? "Standalone" : + (bq->mode == BQ_SLAVE ? "Slave" : "Master")); + return 0; +} + +static int bq25980_check_device_id(struct bq25980_device *bq) +{ +#if 0 //IC device id not confirmed + int ret; + int val; + + ret = regmap_read(bq->regmap, BQ25980_DEVICE_INFO, &val); + if (ret) { + dev_err(bq->dev, "Failed to read device id\n"); + return ret; + } + + val = (val & BQ25980_DEVICE_ID_MASK); + if (bq->device_id != val) { + dev_err(bq->dev, "dts id %d mismatch with hardware id %d\n", bq->device_id, val); + return -EINVAL; + } +#endif + return 0; +} + +static int bq25980_parse_dt_id(struct bq25980_device *bq, int driver_data) +{ + switch (driver_data) { + case BQ25960_STANDALONE: + bq->device_id = BQ25960; + bq->mode = BQ_STANDALONE; + break; + case BQ25960_SLAVE: + bq->device_id = BQ25960; + bq->mode = BQ_SLAVE; + break; + case BQ25960_MASTER: + bq->device_id = BQ25960; + bq->mode = BQ_MASTER; + break; + case BQ25980_STANDALONE: + bq->device_id = BQ25980; + bq->mode = BQ_STANDALONE; + break; + case BQ25980_SLAVE: + bq->device_id = BQ25980; + bq->mode = BQ_SLAVE; + break; + case BQ25980_MASTER: + bq->device_id = BQ25980; + bq->mode = BQ_MASTER; + break; + default: + dev_err(bq->dev, "dts compatible id %d is unknown", driver_data); + return -EINVAL; + break; + } + + return 0; +} + +static int bq25980_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct bq25980_device *bq; + int ret;//, irq_gpio, irqn; + + printk("-------bq25980 driver probe--------\n"); + bq = devm_kzalloc(dev, sizeof(*bq), GFP_KERNEL); + if (!bq) { + dev_err(dev, "Out of memory\n"); + return -ENOMEM; + } + + bq->client = client; + bq->dev = dev; + + mutex_init(&bq->lock); + mutex_init(&bq->irq_complete); + + bq->resume_completed = true; + bq->irq_waiting = false; + + strncpy(bq->model_name, id->name, I2C_NAME_SIZE); + + ret = bq25980_parse_dt_id(bq, id->driver_data); + if (ret) + goto free_mem; + + bq->chip_info = &bq25980_chip_info_tbl[bq->device_id]; + + bq->regmap = devm_regmap_init_i2c(client, + bq->chip_info->regmap_config); + if (IS_ERR(bq->regmap)) { + dev_err(dev, "Failed to allocate register map\n"); + devm_kfree(bq->dev, bq); + return PTR_ERR(bq->regmap); + } + + i2c_set_clientdata(client, bq); + + ret = bq25980_check_device_id(bq); + if (ret) + goto free_mem; + + ret = bq25980_check_work_mode(bq); + if (ret) + goto free_mem; + + ret = bq25980_parse_dt(bq); + if (ret) { + dev_err(dev, "Failed to read device tree properties%d\n", ret); + goto free_mem; + } + +#ifdef CONFIG_INTERRUPT_AS_GPIO + irq_gpio = of_get_named_gpio(client->dev.of_node, "ti,irq-gpio", 0); + if (!gpio_is_valid(irq_gpio)) + { + dev_err(bq->dev, "%s: %d gpio get failed\n", __func__, irq_gpio); + goto free_mem; + } + ret = gpio_request(irq_gpio, "bq25980 irq pin"); + if (ret) { + dev_err(bq->dev, "%s: %d gpio request failed\n", __func__, irq_gpio); + goto free_mem; + } + gpio_direction_input(irq_gpio); + irqn = gpio_to_irq(irq_gpio); + if (irqn < 0) { + dev_err(bq->dev, "%s:%d gpio_to_irq failed\n", __func__, irqn); + goto free_mem; + } + client->irq = irqn; +#else + + bq->irq_pinctrl = + pinctrl_get_select(bq->dev, "bq25960_int_default"); + if (!bq->irq_pinctrl) { + dev_err(bq->dev,"Couldn't set pinctrl bq25960_int_default\n"); + goto free_mem; + } +#endif + + if (client->irq) { + ret = devm_request_threaded_irq(dev, client->irq, NULL, + bq25980_irq_handler_thread, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + dev_name(&client->dev), bq); + if (ret < 0) { + dev_err(bq->dev,"request irq for irq=%d failed, ret =%d\n", + client->irq, ret); + goto free_mem; + } + //enable_irq_wake(client->irq); + } + + ret = bq25980_power_supply_init(bq, dev, id->driver_data); + if (ret) + goto free_mem; + + ret = bq25980_reg_init(bq); + if (ret) { + dev_err(dev, "Cannot initialize the chip.\n"); + goto free_psy; + } + + dump_reg(bq,0x00,0x37); + printk("-------bq25980 driver probe success--------\n"); + return 0; +free_psy: + power_supply_unregister(bq->charger); + +free_mem: + devm_kfree(bq->dev, bq); + return ret; +} +static int bq25980_charger_remove(struct i2c_client *client) +{ + struct bq25980_device *bq = i2c_get_clientdata(client); + + bq25980_set_adc_enable(bq, false); + + power_supply_unregister(bq->charger); + + mutex_destroy(&bq->lock); + mutex_destroy(&bq->irq_complete); + dev_err(bq->dev,"remove Successfully\n"); + return 0; +} + + +static void bq25980_charger_shutdown(struct i2c_client *client) +{ + struct bq25980_device *bq = i2c_get_clientdata(client); + + bq25980_set_adc_enable(bq, false); + dev_err(bq->dev,"Shutdown Successfully\n"); +} + +static int bq25980_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq25980_device *bq = i2c_get_clientdata(client); + + mutex_lock(&bq->irq_complete); + bq->resume_completed = false; + mutex_unlock(&bq->irq_complete); + dev_err(bq->dev, "Suspend successfully!"); + + return 0; +} + +static int bq25980_suspend_noirq(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq25980_device *bq = i2c_get_clientdata(client); + + if (bq->irq_waiting) { + pr_err_ratelimited("Aborting suspend, an interrupt was detected while suspending\n"); + return -EBUSY; + } + return 0; +} + +static int bq25980_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq25980_device *bq = i2c_get_clientdata(client); + + + mutex_lock(&bq->irq_complete); + bq->resume_completed = true; + if (bq->irq_waiting) { + bq->irq_disabled = false; + enable_irq(client->irq); + mutex_unlock(&bq->irq_complete); + bq25980_irq_handler_thread(client->irq, bq); + } else { + mutex_unlock(&bq->irq_complete); + } + + power_supply_changed(bq->charger); + dev_err(bq->dev,"Resume successfully!"); + + return 0; +} + +static const struct dev_pm_ops bq25980_pm_ops = { + .resume = bq25980_resume, + .suspend_noirq = bq25980_suspend_noirq, + .suspend = bq25980_suspend, +}; + +static const struct i2c_device_id bq25980_i2c_ids[] = { + { "bq25980-standalone", BQ25980_STANDALONE }, + { "bq25980-master", BQ25980_MASTER }, + { "bq25980-slave", BQ25980_SLAVE }, + { "bq25960-standalone", BQ25960_STANDALONE }, + { "bq25960-master", BQ25960_MASTER }, + { "bq25960-slave", BQ25960_SLAVE }, + { "bq25975", BQ25975 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, bq25980_i2c_ids); + +static const struct of_device_id bq25980_of_match[] = { + { .compatible = "ti,bq25980-standalone", .data = (void *)BQ25980_STANDALONE}, + { .compatible = "ti,bq25980-master", .data = (void *)BQ25980_MASTER}, + { .compatible = "ti,bq25980-slave", .data = (void *)BQ25980_SLAVE}, + { .compatible = "ti,bq25960-standalone", .data = (void *)BQ25960_STANDALONE}, + { .compatible = "ti,bq25960-master", .data = (void *)BQ25960_MASTER}, + { .compatible = "ti,bq25960-slave", .data = (void *)BQ25960_SLAVE}, + { .compatible = "ti,bq25975", .data = (void *)BQ25975 }, + { }, +}; +MODULE_DEVICE_TABLE(of, bq25980_of_match); + +static struct i2c_driver bq25980_driver = { + .driver = { + .name = "bq25980-charger", + .of_match_table = bq25980_of_match, + .pm = &bq25980_pm_ops, + }, + .probe = bq25980_probe, + .id_table = bq25980_i2c_ids, + .remove = bq25980_charger_remove, + .shutdown = bq25980_charger_shutdown, +}; +module_i2c_driver(bq25980_driver); + +MODULE_AUTHOR("Dan Murphy "); +MODULE_AUTHOR("Ricardo Rivera-Matos "); +MODULE_DESCRIPTION("bq25980 charger driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/bq2597x_mmi/bq25980_charger.h b/drivers/power/bq2597x_mmi/bq25980_charger.h new file mode 100644 index 000000000000..b4b94d405b23 --- /dev/null +++ b/drivers/power/bq2597x_mmi/bq25980_charger.h @@ -0,0 +1,206 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ */ + +#ifndef BQ25980_CHARGER_H +#define BQ25980_CHARGER_H + +#define BQ25980_MANUFACTURER "Texas Instruments" + +#define BQ25980_BATOVP 0x0 +#define BQ25980_BATOVP_ALM 0x1 +#define BQ25980_BATOCP 0x2 +#define BQ25980_BATOCP_ALM 0x3 +#define BQ25980_CHRGR_CFG_1 0x4 +#define BQ25980_CHRGR_CTRL_1 0x5 +#define BQ25980_BUSOVP 0x6 +#define BQ25980_BUSOVP_ALM 0x7 +#define BQ25980_BUSOCP 0x8 +#define BQ25980_REG_09 0x9 +#define BQ25980_TEMP_CONTROL 0xA +#define BQ25980_TDIE_ALM 0xB +#define BQ25980_TSBUS_FLT 0xC +#define BQ25980_TSBAT_FLG 0xD +#define BQ25980_VAC_CONTROL 0xE +#define BQ25980_CHRGR_CTRL_2 0xF +#define BQ25980_CHRGR_CTRL_3 0x10 +#define BQ25980_CHRGR_CTRL_4 0x11 +#define BQ25980_CHRGR_CTRL_5 0x12 +#define BQ25980_STAT1 0x13 +#define BQ25980_STAT2 0x14 +#define BQ25980_STAT3 0x15 +#define BQ25980_STAT4 0x16 +#define BQ25980_STAT5 0x17 +#define BQ25980_FLAG1 0x18 +#define BQ25980_FLAG2 0x19 +#define BQ25980_FLAG3 0x1A +#define BQ25980_FLAG4 0x1B +#define BQ25980_FLAG5 0x1C +#define BQ25980_MASK1 0x1D +#define BQ25980_MASK2 0x1E +#define BQ25980_MASK3 0x1F +#define BQ25980_MASK4 0x20 +#define BQ25980_MASK5 0x21 +#define BQ25980_DEVICE_INFO 0x22 +#define BQ25980_ADC_CONTROL1 0x23 +#define BQ25980_ADC_CONTROL2 0x24 +#define BQ25980_IBUS_ADC_MSB 0x25 +#define BQ25980_IBUS_ADC_LSB 0x26 +#define BQ25980_VBUS_ADC_MSB 0x27 +#define BQ25980_VBUS_ADC_LSB 0x28 +#define BQ25980_VAC1_ADC_MSB 0x29 +#define BQ25980_VAC1_ADC_LSB 0x2A +#define BQ25980_VAC2_ADC_MSB 0x2B +#define BQ25980_VAC2_ADC_LSB 0x2C +#define BQ25980_VOUT_ADC_MSB 0x2D +#define BQ25980_VOUT_ADC_LSB 0x2E +#define BQ25980_VBAT_ADC_MSB 0x2F +#define BQ25980_VBAT_ADC_LSB 0x30 +#define BQ25980_IBAT_ADC_MSB 0x31 +#define BQ25980_IBAT_ADC_LSB 0x32 +#define BQ25980_TSBUS_ADC_MSB 0x33 +#define BQ25980_TSBUS_ADC_LSB 0x34 +#define BQ25980_TSBAT_ADC_MSB 0x35 +#define BQ25980_TSBAT_ADC_LSB 0x36 +#define BQ25980_TDIE_ADC_MSB 0x37 +#define BQ25980_TDIE_ADC_LSB 0x38 +#define BQ25980_DEGLITCH_TIME 0x39 +#define BQ25980_CHRGR_CTRL_6 0x3A + +#define BQ25980_BUSOCP_STEP_uA 262500 +#define BQ25980_BUSOCP_OFFSET_uA 1050000 +#define BQ25960_BUSOCP_STEP_uA 250000 +#define BQ25960_BUSOCP_OFFSET_uA 1000000 + + +#define BQ25980_BUSOCP_SC_DFLT_uA 4200000 +#define BQ25980_BUSOCP_BYP_DFLT_uA 8000000 +#define BQ25975_BUSOCP_DFLT_uA 4250000 +#define BQ25960_BUSOCP_SC_DFLT_uA 4000000 +#define BQ25960_BUSOCP_BYP_DFLT_uA 5000000 + +#define BQ25980_BUSOCP_MIN_uA 1050000 +#define BQ25960_BUSOCP_MIN_uA 1000000 + +#define BQ25980_BUSOCP_SC_MAX_uA 6037500 +#define BQ25975_BUSOCP_SC_MAX_uA 5750000 +#define BQ25960_BUSOCP_SC_MAX_uA 4500000 + +#define BQ25980_BUSOCP_BYP_MAX_uA 8925000 +#define BQ25975_BUSOCP_BYP_MAX_uA 8500000 +#define BQ25960_BUSOCP_BYP_MAX_uA 6500000 + +#define BQ25980_BUSOVP_SC_STEP_uV 100000 +#define BQ25975_BUSOVP_SC_STEP_uV 50000 +#define BQ25960_BUSOVP_SC_STEP_uV 50000 +#define BQ25980_BUSOVP_SC_OFFSET_uV 14000000 +#define BQ25975_BUSOVP_SC_OFFSET_uV 7000000 +#define BQ25960_BUSOVP_SC_OFFSET_uV 7000000 + +#define BQ25980_BUSOVP_BYP_STEP_uV 50000 +#define BQ25975_BUSOVP_BYP_STEP_uV 25000 +#define BQ25960_BUSOVP_BYP_STEP_uV 25000 +#define BQ25980_BUSOVP_BYP_OFFSET_uV 7000000 +#define BQ25975_BUSOVP_BYP_OFFSET_uV 3500000 +#define BQ25960_BUSOVP_BYP_OFFSET_uV 3500000 + +#define BQ25980_BUSOVP_DFLT_uV 17800000 +#define BQ25980_BUSOVP_BYPASS_DFLT_uV 8900000 +#define BQ25975_BUSOVP_DFLT_uV 8900000 +#define BQ25975_BUSOVP_BYPASS_DFLT_uV 4450000 +#define BQ25960_BUSOVP_DFLT_uV 8900000 + +#define BQ25980_BUSOVP_SC_MIN_uV 14000000 +#define BQ25975_BUSOVP_SC_MIN_uV 7000000 +#define BQ25960_BUSOVP_SC_MIN_uV 7000000 +#define BQ25980_BUSOVP_BYP_MIN_uV 7000000 +#define BQ25975_BUSOVP_BYP_MIN_uV 3500000 +#define BQ25960_BUSOVP_BYP_MIN_uV 3500000 + +#define BQ25980_BUSOVP_SC_MAX_uV 22000000 +#define BQ25975_BUSOVP_SC_MAX_uV 12750000 +#define BQ25960_BUSOVP_SC_MAX_uV 12750000 + +#define BQ25980_BUSOVP_BYP_MAX_uV 12750000 +#define BQ25975_BUSOVP_BYP_MAX_uV 6500000 +#define BQ25960_BUSOVP_BYP_MAX_uV 6500000 + +#define BQ25980_BATOVP_STEP_uV 20000 +#define BQ25975_BATOVP_STEP_uV 10000 +#define BQ25960_BATOVP_STEP_uV 10000 + +#define BQ25980_BATOVP_OFFSET_uV 7000000 +#define BQ25975_BATOVP_OFFSET_uV 3500000 +#define BQ25960_BATOVP_OFFSET_uV 3500000 + +#define BQ25980_BATOVP_DFLT_uV 14000000 +#define BQ25975_BATOVP_DFLT_uV 8900000 +#define BQ25960_BATOVP_DFLT_uV 8900000 + +#define BQ25980_BATOVP_MIN_uV 7000000 +#define BQ25975_BATOVP_MIN_uV 3500000 +#define BQ25960_BATOVP_MIN_uV 3500000 + +#define BQ25980_BATOVP_MAX_uV 9540000 +#define BQ25975_BATOVP_MAX_uV 4770000 +#define BQ25960_BATOVP_MAX_uV 4770000 + +#define BQ25980_BATOCP_STEP_uA 100000 + +#define BQ25980_BATOCP_MASK GENMASK(6, 0) + +#define BQ25980_BATOCP_DFLT_uA 8100000 +#define BQ25960_BATOCP_DFLT_uA 6100000 + +#define BQ25980_BATOCP_MIN_uA 2000000 + +#define BQ25980_BATOCP_MAX_uA 11000000 +#define BQ25975_BATOCP_MAX_uA 11000000 +#define BQ25960_BATOCP_MAX_uA 8500000 + +#define BQ25980_ENABLE_HIZ 0xff +#define BQ25980_DISABLE_HIZ 0x0 +#define BQ25980_EN_BYPASS BIT(3) +#define BQ25980_STAT1_OVP_MASK (BIT(7) | BIT(5) | BIT(1)) +#define BQ25980_STAT3_OVP_MASK (BIT(7) | BIT(6)) +#define BQ25980_STAT1_OCP_MASK BIT(4) +#define BQ25980_STAT2_OCP_MASK BIT(7) +#define BQ25980_STAT4_TFLT_MASK GENMASK(4, 2) +#define BQ25980_WD_STAT BIT(0) +#define BQ25980_PRESENT_MASK GENMASK(4, 2) +#define BQ25980_CHG_EN BIT(4) +#define BQ25980_EN_HIZ BIT(6) +#define BQ25980_ADC_EN BIT(7) +#define BQ25980_MS_MASK GENMASK(1, 0) +#define BQ25980_DEVICE_ID_MASK GENMASK(3, 0) + +#define BQ25980_ADC_CURR_STEP_IBUS_uA 1070 +#define BQ25980_ADC_VOLT_STEP_VBAT_deciuV 10055 +#define BQ25980_ADC_VOLT_STEP_VBUS_deciuV 10066 +#define BQ25980_ADC_VOLT_OFFSET_VBUS -60000 +#define BQ25960_ADC_VOLT_STEP_deciuV 10000 +#define BQ25960_ADC_VOLT_STEP_uV 1000 +#define BQ25960_ADC_CURR_STEP_uA 1000 +#define BQ25980_ADC_POLARITY_BIT BIT(7) + +#define BQ25980_WATCHDOG_MASK GENMASK(4, 3) +#define BQ25980_WATCHDOG_DIS BIT(2) +#define BQ25980_WATCHDOG_MAX 300000 +#define BQ25980_WATCHDOG_MIN 0 +#define BQ25980_NUM_WD_VAL 4 + +#define BQ25980_VAC1_CONTROL_MASK GENMASK(7, 5) +#define BQ25980_VAC1_CONTROL_SHIFT 5 +#define BQ25980_VAC2_CONTROL_MASK GENMASK(4, 2) +#define BQ25980_VAC2_CONTROL_SHIFT 2 + +#define BQ25980_STAT1_BAT_OVP_MASK BIT(7) +#define BQ25980_STAT1_BAT_OVP_ALM_MASK BIT(6) +#define BQ25980_STAT1_BAT_OCP_MASK BIT(4) +#define BQ25980_STAT1_BAT_OCP_ALM_MASK BIT(3) +#define BQ25980_STAT1_BUS_OVP_MASK BIT(1) +#define BQ25980_STAT1_BUS_OVP_ALM_MASK BIT(0) +#define BQ25980_STAT2_BUS_OCP_MASK BIT(7) +#define BQ25980_STAT2_BUS_OCP_ALM_MASK BIT(6) //reserved +#define BQ25980_STAT4_TSBAT_FLT_MASK BIT(3) + +#endif /* BQ25980_CHARGER_H */ \ No newline at end of file diff --git a/drivers/power/bq2597x_mmi_iio/Android.mk b/drivers/power/bq2597x_mmi_iio/Android.mk new file mode 100644 index 000000000000..7592a2a43ed8 --- /dev/null +++ b/drivers/power/bq2597x_mmi_iio/Android.mk @@ -0,0 +1,19 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := bq2597x_mmi_iio.ko + +ifeq ($(BQ2597X_DUAL_ENABLE),true) + KERNEL_CFLAGS += CONFIG_BQ2597X_DUAL=y +endif + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/bq2597x_mmi_iio/Kbuild b/drivers/power/bq2597x_mmi_iio/Kbuild new file mode 100644 index 000000000000..4a5406b5538b --- /dev/null +++ b/drivers/power/bq2597x_mmi_iio/Kbuild @@ -0,0 +1,7 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/bq2597x_mmi_iio + +obj-m += bq2597x_mmi_iio.o + diff --git a/drivers/power/bq2597x_mmi_iio/Makefile b/drivers/power/bq2597x_mmi_iio/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/power/bq2597x_mmi_iio/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/power/bq2597x_mmi_iio/bq25970_reg.h b/drivers/power/bq2597x_mmi_iio/bq25970_reg.h new file mode 100644 index 000000000000..8d3820bd16dc --- /dev/null +++ b/drivers/power/bq2597x_mmi_iio/bq25970_reg.h @@ -0,0 +1,809 @@ + +#ifndef __BQ2597X_HEADER__ +#define __BQ2597X_HEADER__ + +/* Register 00h */ +#define BQ2597X_REG_00 0x00 +#define BQ2597X_BAT_OVP_DIS_MASK 0x80 +#define BQ2597X_BAT_OVP_DIS_SHIFT 7 +#define BQ2597X_BAT_OVP_ENABLE 0 +#define BQ2597X_BAT_OVP_DISABLE 1 + +#define BQ2597X_BAT_OVP_MASK 0x3F +#define BQ2597X_BAT_OVP_SHIFT 0 +#define BQ2597X_BAT_OVP_BASE 3475 +#define BQ2597X_BAT_OVP_LSB 25 + +/* Register 01h */ +#define BQ2597X_REG_01 0x01 +#define BQ2597X_BAT_OVP_ALM_DIS_MASK 0x80 +#define BQ2597X_BAT_OVP_ALM_DIS_SHIFT 7 +#define BQ2597X_BAT_OVP_ALM_ENABLE 0 +#define BQ2597X_BAT_OVP_ALM_DISABLE 1 + +#define BQ2597X_BAT_OVP_ALM_MASK 0x3F +#define BQ2597X_BAT_OVP_ALM_SHIFT 0 +#define BQ2597X_BAT_OVP_ALM_BASE 3500 +#define BQ2597X_BAT_OVP_ALM_LSB 25 + +/* Register 02h */ +#define BQ2597X_REG_02 0x02 +#define BQ2597X_BAT_OCP_DIS_MASK 0x80 +#define BQ2597X_BAT_OCP_DIS_SHIFT 7 +#define BQ2597X_BAT_OCP_ENABLE 0 +#define BQ2597X_BAT_OCP_DISABLE 1 + +#define BQ2597X_BAT_OCP_MASK 0x7F +#define BQ2597X_BAT_OCP_SHIFT 0 +#define BQ2597X_BAT_OCP_BASE 2000 +#define BQ2597X_BAT_OCP_LSB 100 + +/* Register 03h */ +#define BQ2597X_REG_03 0x03 +#define BQ2597X_BAT_OCP_ALM_DIS_MASK 0x80 +#define BQ2597X_BAT_OCP_ALM_DIS_SHIFT 7 +#define BQ2597X_BAT_OCP_ALM_ENABLE 0 +#define BQ2597X_BAT_OCP_ALM_DISABLE 1 + +#define BQ2597X_BAT_OCP_ALM_MASK 0x7F +#define BQ2597X_BAT_OCP_ALM_SHIFT 0 +#define BQ2597X_BAT_OCP_ALM_BASE 2000 +#define BQ2597X_BAT_OCP_ALM_LSB 100 + +/* Register 04h */ +#define BQ2597X_REG_04 0x04 +#define BQ2597X_BAT_UCP_ALM_DIS_MASK 0x80 +#define BQ2597X_BAT_UCP_ALM_DIS_SHIFT 7 +#define BQ2597X_BAT_UCP_ALM_ENABLE 0 +#define BQ2597X_BAT_UCP_ALM_DISABLE 1 + +#define BQ2597X_BAT_UCP_ALM_MASK 0x7F +#define BQ2597X_BAT_UCP_ALM_SHIFT 0 +#define BQ2597X_BAT_UCP_ALM_BASE 0 +#define BQ2597X_BAT_UCP_ALM_LSB 50 + +/* Register 05h */ +#define BQ2597X_REG_05 0x05 +#define BQ2597X_AC_OVP_STAT_MASK 0x80 +#define BQ2597X_AC_OVP_STAT_SHIFT 7 + +#define BQ2597X_AC_OVP_FLAG_MASK 0x40 +#define BQ2597X_AC_OVP_FLAG_SHIFT 6 + +#define BQ2597X_AC_OVP_MASK_MASK 0x20 +#define BQ2597X_AC_OVP_MASK_SHIFT 5 + +#define BQ2597X_VDROP_THRESHOLD_SET_MASK 0x10 +#define BQ2597X_VDROP_THRESHOLD_SET_SHIFT 4 +#define BQ2597X_VDROP_THRESHOLD_300MV 0 +#define BQ2597X_VDROP_THRESHOLD_400MV 1 + +#define BQ2597X_VDROP_DEGLITCH_SET_MASK 0x08 +#define BQ2597X_VDROP_DEGLITCH_SET_SHIFT 3 +#define BQ2597X_VDROP_DEGLITCH_8US 0 +#define BQ2597X_VDROP_DEGLITCH_5MS 1 + +#define BQ2597X_AC_OVP_MASK 0x07 +#define BQ2597X_AC_OVP_SHIFT 0 +#define BQ2597X_AC_OVP_BASE 11 +#define BQ2597X_AC_OVP_LSB 1 +#define BQ2597X_AC_OVP_6P5V 65 + +/* Register 06h */ +#define BQ2597X_REG_06 0x06 +#define BQ2597X_VBUS_PD_EN_MASK 0x80 +#define BQ2597X_VBUS_PD_EN_SHIFT 7 +#define BQ2597X_VBUS_PD_ENABLE 1 +#define BQ2597X_VBUS_PD_DISABLE 0 + +#define BQ2597X_BUS_OVP_MASK 0x7F +#define BQ2597X_BUS_OVP_SHIFT 0 +#define BQ2597X_BUS_OVP_BASE 5950 +#define BQ2597X_BUS_OVP_LSB 50 + +/* Register 07h */ +#define BQ2597X_REG_07 0x07 +#define BQ2597X_BUS_OVP_ALM_DIS_MASK 0x80 +#define BQ2597X_BUS_OVP_ALM_DIS_SHIFT 7 +#define BQ2597X_BUS_OVP_ALM_ENABLE 0 +#define BQ2597X_BUS_OVP_ALM_DISABLE 1 + +#define BQ2597X_BUS_OVP_ALM_MASK 0x7F +#define BQ2597X_BUS_OVP_ALM_SHIFT 0 +#define BQ2597X_BUS_OVP_ALM_BASE 6000 +#define BQ2597X_BUS_OVP_ALM_LSB 50 + +/* Register 08h */ +#define BQ2597X_REG_08 0x08 +#define BQ2597X_BUS_OCP_DIS_MASK 0x80 +#define BQ2597X_BUS_OCP_DIS_SHIFT 7 +#define BQ2597X_BUS_OCP_ENABLE 0 +#define BQ2597X_BUS_OCP_DISABLE 1 + +#define BQ2597X_IBUS_UCP_RISE_FLAG_MASK 0x40 +#define BQ2597X_IBUS_UCP_RISE_FLAG_SHIFT 6 + +#define BQ2597X_IBUS_UCP_RISE_MASK_MASK 0x20 +#define BQ2597X_IBUS_UCP_RISE_MASK_SHIFT 5 +#define BQ2597X_IBUS_UCP_RISE_MASK_ENABLE 1 +#define BQ2597X_IBUS_UCP_RISE_MASK_DISABLE 0 + +#define BQ2597X_IBUS_UCP_FALL_FLAG_MASK 0x10 +#define BQ2597X_IBUS_UCP_FALL_FLAG_SHIFT 4 + +#define BQ2597X_BUS_OCP_MASK 0x0F +#define BQ2597X_BUS_OCP_SHIFT 0 +#define BQ2597X_BUS_OCP_BASE 1000 +#define BQ2597X_BUS_OCP_LSB 250 + +/* Register 09h */ +#define BQ2597X_REG_09 0x09 +#define BQ2597X_BUS_OCP_ALM_DIS_MASK 0x80 +#define BQ2597X_BUS_OCP_ALM_DIS_SHIFT 7 +#define BQ2597X_BUS_OCP_ALM_ENABLE 0 +#define BQ2597X_BUS_OCP_ALM_DISABLE 1 + +#define BQ2597X_BUS_OCP_ALM_MASK 0x7F +#define BQ2597X_BUS_OCP_ALM_SHIFT 0 +#define BQ2597X_BUS_OCP_ALM_BASE 0 +#define BQ2597X_BUS_OCP_ALM_LSB 50 + +/* Register 0Ah */ +#define BQ2597X_REG_0A 0x0A +#define BQ2597X_TSHUT_FLAG_MASK 0x80 +#define BQ2597X_TSHUT_FLAG_SHIFT 7 + +#define BQ2597X_TSHUT_STAT_MASK 0x40 +#define BQ2597X_TSHUT_STAT_SHIFT 6 + +#define BQ2597X_VBUS_ERRORLO_STAT_MASK 0x20 +#define BQ2597X_VBUS_ERRORLO_STAT_SHIFT 5 + +#define BQ2597X_VBUS_ERRORHI_STAT_MASK 0x10 +#define BQ2597X_VBUS_ERRORHI_STAT_SHIFT 4 + +#define BQ2597X_SS_TIMEOUT_FLAG_MASK 0x08 +#define BQ2597X_SS_TIMEOUT_FLAG_SHIFT 3 + +#define BQ2597X_CONV_SWITCHING_STAT_MASK 0x04 +#define BQ2597X_CONV_SWITCHING_STAT_SHIFT 2 + +#define BQ2597X_CONV_OCP_FLAG_MASK 0x02 +#define BQ2597X_CONV_OCP_FLAG_SHIFT 1 + +#define BQ2597X_PIN_DIAG_FALL_FLAG_MASK 0x01 +#define BQ2597X_PIN_DIAG_FALL_FLAG_SHIFT 0 + +/* Register 0Bh */ +#define BQ2597X_REG_0B 0x0B +#define BQ2597X_REG_RST_MASK 0x80 +#define BQ2597X_REG_RST_SHIFT 7 +#define BQ2597X_REG_RST_ENABLE 1 +#define BQ2597X_REG_RST_DISABLE 0 + +#define BQ2597X_FSW_SET_MASK 0x70 +#define BQ2597X_FSW_SET_SHIFT 4 +#define BQ2597X_FSW_SET_187P5KHZ 0 +#define BQ2597X_FSW_SET_250KHZ 1 +#define BQ2597X_FSW_SET_300KHZ 2 +#define BQ2597X_FSW_SET_375KHZ 3 +#define BQ2597X_FSW_SET_500KHZ 4 +#define BQ2597X_FSW_SET_750KHZ 5 + +#define BQ2597X_WD_TIMEOUT_FLAG_MASK 0x08 +#define BQ2597X_WD_TIMEOUT_SHIFT 3 + +#define BQ2597X_WATCHDOG_DIS_MASK 0x04 +#define BQ2597X_WATCHDOG_DIS_SHIFT 2 +#define BQ2597X_WATCHDOG_ENABLE 0 +#define BQ2597X_WATCHDOG_DISABLE 1 + +#define BQ2597X_WATCHDOG_MASK 0x03 +#define BQ2597X_WATCHDOG_SHIFT 0 +#define BQ2597X_WATCHDOG_0P5S 0 +#define BQ2597X_WATCHDOG_1S 1 +#define BQ2597X_WATCHDOG_5S 2 +#define BQ2597X_WATCHDOG_30S 3 + +/* Register 0Ch */ +#define BQ2597X_REG_0C 0x0C +#define BQ2597X_CHG_EN_MASK 0x80 +#define BQ2597X_CHG_EN_SHIFT 7 +#define BQ2597X_CHG_ENABLE 1 +#define BQ2597X_CHG_DISABLE 0 + +#define BQ2597X_MS_MASK 0x60 +#define BQ2597X_MS_SHIFT 5 +#define BQ2597X_MS_STANDALONE 0 +#define BQ2597X_MS_SLAVE 1 +#define BQ2597X_MS_MASTER 2 + +#define BQ2597X_FREQ_SHIFT_MASK 0x18 +#define BQ2597X_FREQ_SHIFT_SHIFT 3 +#define BQ2597X_FREQ_SHIFT_NORMINAL 0 +#define BQ2597X_FREQ_SHIFT_POSITIVE10 1 +#define BQ2597X_FREQ_SHIFT_NEGATIVE10 2 +#define BQ2597X_FREQ_SHIFT_SPREAD_SPECTRUM 3 + +#define BQ2597X_TSBUS_DIS_MASK 0x04 +#define BQ2597X_TSBUS_DIS_SHIFT 2 +#define BQ2597X_TSBUS_ENABLE 0 +#define BQ2597X_TSBUS_DISABLE 1 + +#define BQ2597X_TSBAT_DIS_MASK 0x02 +#define BQ2597X_TSBAT_DIS_SHIFT 1 +#define BQ2597X_TSBAT_ENABLE 0 +#define BQ2597X_TSBAT_DISABLE 1 + +#define BQ2597X_TDIE_DIS_MASK 0x01 +#define BQ2597X_TDIE_DIS_SHIFT 0 +#define BQ2597X_TDIE_ENABLE 0 +#define BQ2597X_TDIE_DISABLE 1 + +/* Register 0Dh */ +#define BQ2597X_REG_0D 0x0D +#define BQ2597X_BAT_OVP_ALM_STAT_MASK 0x80 +#define BQ2597X_BAT_OVP_ALM_STAT_SHIFT 7 + +#define BQ2597X_BAT_OCP_ALM_STAT_MASK 0x40 +#define BQ2597X_BAT_OCP_ALM_STAT_SHIFT 6 + +#define BQ2597X_BUS_OVP_ALM_STAT_MASK 0x20 +#define BQ2597X_BUS_OVP_ALM_STAT_SHIFT 5 + +#define BQ2597X_BUS_OCP_ALM_STAT_MASK 0x10 +#define BQ2597X_BUS_OCP_ALM_STAT_SHIFT 4 + +#define BQ2597X_BAT_UCP_ALM_STAT_MASK 0x08 +#define BQ2597X_BAT_UCP_ALM_STAT_SHIFT 3 + +#define BQ2597X_ADAPTER_INSERT_STAT_MASK 0x04 +#define BQ2597X_ADAPTER_INSERT_STAT_SHIFT 2 + +#define BQ2597X_VBAT_INSERT_STAT_MASK 0x02 +#define BQ2597X_VBAT_INSERT_STAT_SHIFT 1 + +#define BQ2597X_ADC_DONE_STAT_MASK 0x01 +#define BQ2597X_ADC_DONE_STAT_SHIFT 0 +#define BQ2597X_ADC_DONE_STAT_COMPLETE 1 +#define BQ2597X_ADC_DONE_STAT_NOTCOMPLETE 0 + +/* Register 0Eh */ +#define BQ2597X_REG_0E 0x0E +#define BQ2597X_BAT_OVP_ALM_FLAG_MASK 0x80 +#define BQ2597X_BAT_OVP_ALM_FLAG_SHIFT 7 + +#define BQ2597X_BAT_OCP_ALM_FLAG_MASK 0x40 +#define BQ2597X_BAT_OCP_ALM_FLAG_SHIFT 6 + +#define BQ2597X_BUS_OVP_ALM_FLAG_MASK 0x20 +#define BQ2597X_BUS_OVP_ALM_FLAG_SHIFT 5 + +#define BQ2597X_BUS_OCP_ALM_FLAG_MASK 0x10 +#define BQ2597X_BUS_OCP_ALM_FLAG_SHIFT 4 + +#define BQ2597X_BAT_UCP_ALM_FLAG_MASK 0x08 +#define BQ2597X_BAT_UCP_ALM_FLAG_SHIFT 3 + +#define BQ2597X_ADAPTER_INSERT_FLAG_MASK 0x04 +#define BQ2597X_ADAPTER_INSERT_FLAG_SHIFT 2 + +#define BQ2597X_VBAT_INSERT_FLAG_MASK 0x02 +#define BQ2597X_VBAT_INSERT_FLAG_SHIFT 1 + +#define BQ2597X_ADC_DONE_FLAG_MASK 0x01 +#define BQ2597X_ADC_DONE_FLAG_SHIFT 0 +#define BQ2597X_ADC_DONE_FLAG_COMPLETE 1 +#define BQ2597X_ADC_DONE_FLAG_NOTCOMPLETE 0 + +/* Register 0Fh */ +#define BQ2597X_REG_0F 0x0F +#define BQ2597X_BAT_OVP_ALM_MASK_MASK 0x80 +#define BQ2597X_BAT_OVP_ALM_MASK_SHIFT 7 +#define BQ2597X_BAT_OVP_ALM_MASK_ENABLE 1 +#define BQ2597X_BAT_OVP_ALM_MASK_DISABLE 0 + +#define BQ2597X_BAT_OCP_ALM_MASK_MASK 0x40 +#define BQ2597X_BAT_OCP_ALM_MASK_SHIFT 6 +#define BQ2597X_BAT_OCP_ALM_MASK_ENABLE 1 +#define BQ2597X_BAT_OCP_ALM_MASK_DISABLE 0 + +#define BQ2597X_BUS_OVP_ALM_MASK_MASK 0x20 +#define BQ2597X_BUS_OVP_ALM_MASK_SHIFT 5 +#define BQ2597X_BUS_OVP_ALM_MASK_ENABLE 1 +#define BQ2597X_BUS_OVP_ALM_MASK_DISABLE 0 + +#define BQ2597X_BUS_OCP_ALM_MASK_MASK 0x10 +#define BQ2597X_BUS_OCP_ALM_MASK_SHIFT 4 +#define BQ2597X_BUS_OCP_ALM_MASK_ENABLE 1 +#define BQ2597X_BUS_OCP_ALM_MASK_DISABLE 0 + +#define BQ2597X_BAT_UCP_ALM_MASK_MASK 0x08 +#define BQ2597X_BAT_UCP_ALM_MASK_SHIFT 3 +#define BQ2597X_BAT_UCP_ALM_MASK_ENABLE 1 +#define BQ2597X_BAT_UCP_ALM_MASK_DISABLE 0 + +#define BQ2597X_ADAPTER_INSERT_MASK_MASK 0x04 +#define BQ2597X_ADAPTER_INSERT_MASK_SHIFT 2 +#define BQ2597X_ADAPTER_INSERT_MASK_ENABLE 1 +#define BQ2597X_ADAPTER_INSERT_MASK_DISABLE 0 + +#define BQ2597X_VBAT_INSERT_MASK_MASK 0x02 +#define BQ2597X_VBAT_INSERT_MASK_SHIFT 1 +#define BQ2597X_VBAT_INSERT_MASK_ENABLE 1 +#define BQ2597X_VBAT_INSERT_MASK_DISABLE 0 + +#define BQ2597X_ADC_DONE_MASK_MASK 0x01 +#define BQ2597X_ADC_DONE_MASK_SHIFT 0 +#define BQ2597X_ADC_DONE_MASK_ENABLE 1 +#define BQ2597X_ADC_DONE_MASK_DISABLE 0 + +/* Register 10h */ +#define BQ2597X_REG_10 0x10 +#define BQ2597X_BAT_OVP_FLT_STAT_MASK 0x80 +#define BQ2597X_BAT_OVP_FLT_STAT_SHIFT 7 + +#define BQ2597X_BAT_OCP_FLT_STAT_MASK 0x40 +#define BQ2597X_BAT_OCP_FLT_STAT_SHIFT 6 + +#define BQ2597X_BUS_OVP_FLT_STAT_MASK 0x20 +#define BQ2597X_BUS_OVP_FLT_STAT_SHIFT 5 + +#define BQ2597X_BUS_OCP_FLT_STAT_MASK 0x10 +#define BQ2597X_BUS_OCP_FLT_STAT_SHIFT 4 + +#define BQ2597X_TSBUS_TSBAT_ALM_STAT_MASK 0x08 +#define BQ2597X_TSBUS_TSBAT_ALM_STAT_SHIFT 3 + +#define BQ2597X_TSBAT_FLT_STAT_MASK 0x04 +#define BQ2597X_TSBAT_FLT_STAT_SHIFT 2 + +#define BQ2597X_TSBUS_FLT_STAT_MASK 0x02 +#define BQ2597X_TSBUS_FLT_STAT_SHIFT 1 + +#define BQ2597X_TDIE_ALM_STAT_MASK 0x01 +#define BQ2597X_TDIE_ALM_STAT_SHIFT 0 + +/* Register 11h */ +#define BQ2597X_REG_11 0x11 +#define BQ2597X_BAT_OVP_FLT_FLAG_MASK 0x80 +#define BQ2597X_BAT_OVP_FLT_FLAG_SHIFT 7 + +#define BQ2597X_BAT_OCP_FLT_FLAG_MASK 0x40 +#define BQ2597X_BAT_OCP_FLT_FLAG_SHIFT 6 + +#define BQ2597X_BUS_OVP_FLT_FLAG_MASK 0x20 +#define BQ2597X_BUS_OVP_FLT_FLAG_SHIFT 5 + +#define BQ2597X_BUS_OCP_FLT_FLAG_MASK 0x10 +#define BQ2597X_BUS_OCP_FLT_FLAG_SHIFT 4 + +#define BQ2597X_TSBUS_TSBAT_ALM_FLAG_MASK 0x08 +#define BQ2597X_TSBUS_TSBAT_ALM_FLAG_SHIFT 3 + +#define BQ2597X_TSBAT_FLT_FLAG_MASK 0x04 +#define BQ2597X_TSBAT_FLT_FLAG_SHIFT 2 + +#define BQ2597X_TSBUS_FLT_FLAG_MASK 0x02 +#define BQ2597X_TSBUS_FLT_FLAG_SHIFT 1 + +#define BQ2597X_TDIE_ALM_FLAG_MASK 0x01 +#define BQ2597X_TDIE_ALM_FLAG_SHIFT 0 + +/* Register 12h */ +#define BQ2597X_REG_12 0x12 +#define BQ2597X_BAT_OVP_FLT_MASK_MASK 0x80 +#define BQ2597X_BAT_OVP_FLT_MASK_SHIFT 7 +#define BQ2597X_BAT_OVP_FLT_MASK_ENABLE 1 +#define BQ2597X_BAT_OVP_FLT_MASK_DISABLE 0 + +#define BQ2597X_BAT_OCP_FLT_MASK_MASK 0x40 +#define BQ2597X_BAT_OCP_FLT_MASK_SHIFT 6 +#define BQ2597X_BAT_OCP_FLT_MASK_ENABLE 1 +#define BQ2597X_BAT_OCP_FLT_MASK_DISABLE 0 + +#define BQ2597X_BUS_OVP_FLT_MASK_MASK 0x20 +#define BQ2597X_BUS_OVP_FLT_MASK_SHIFT 5 +#define BQ2597X_BUS_OVP_FLT_MASK_ENABLE 1 +#define BQ2597X_BUS_OVP_FLT_MASK_DISABLE 0 + +#define BQ2597X_BUS_OCP_FLT_MASK_MASK 0x10 +#define BQ2597X_BUS_OCP_FLT_MASK_SHIFT 4 +#define BQ2597X_BUS_OCP_FLT_MASK_ENABLE 1 +#define BQ2597X_BUS_OCP_FLT_MASK_DISABLE 0 + +#define BQ2597X_TSBUS_TSBAT_ALM_MASK_MASK 0x08 +#define BQ2597X_TSBUS_TSBAT_ALM_MASK_SHIFT 3 +#define BQ2597X_TSBUS_TSBAT_ALM_MASK_ENABLE 1 +#define BQ2597X_TSBUS_TSBAT_ALM_MASK_DISABLE 0 + +#define BQ2597X_TSBAT_FLT_MASK_MASK 0x04 +#define BQ2597X_TSBAT_FLT_MASK_SHIFT 2 +#define BQ2597X_TSBAT_FLT_MASK_ENABLE 1 +#define BQ2597X_TSBAT_FLT_MASK_DISABLE 0 + +#define BQ2597X_TSBUS_FLT_MASK_MASK 0x02 +#define BQ2597X_TSBUS_FLT_MASK_SHIFT 1 +#define BQ2597X_TSBUS_FLT_MASK_ENABLE 1 +#define BQ2597X_TSBUS_FLT_MASK_DISABLE 0 + +#define BQ2597X_TDIE_ALM_MASK_MASK 0x01 +#define BQ2597X_TDIE_ALM_MASK_SHIFT 0 +#define BQ2597X_TDIE_ALM_MASK_ENABLE 1 +#define BQ2597X_TDIE_ALM_MASK_DISABLE 0 + +/* Register 13h */ +#define BQ2597X_REG_13 0x13 +#define BQ2597X_DEV_ID_MASK 0x0F +#define BQ2597X_DEV_ID_SHIFT 0 +#define BQ2597X_DEV_REV_MASK 0xF0 +#define BQ2597X_DEV_REV_SHIFT 4 + +/* Register 14h */ +#define BQ2597X_REG_14 0x14 +#define BQ2597X_ADC_EN_MASK 0x80 +#define BQ2597X_ADC_EN_SHIFT 7 +#define BQ2597X_ADC_ENABLE 1 +#define BQ2597X_ADC_DISABLE 0 + +#define BQ2597X_ADC_RATE_MASK 0x40 +#define BQ2597X_ADC_RATE_SHIFT 6 +#define BQ2597X_ADC_RATE_CONTINOUS 0 +#define BQ2597X_ADC_RATE_ONESHOT 1 + +#define BQ2597X_ADC_AVG_MASK 0x20 +#define BQ2597X_ADC_AVG_SHIFT 5 +#define BQ2597X_ADC_AVG_DISABLE 0 +#define BQ2597X_ADC_AVG_ENABLE 1 + +#define BQ2597X_ADC_AVG_INIT_MASK 0x10 +#define BQ2597X_ADC_AVG_INIT_SHIFT 4 +#define BQ2597X_ADC_AVG_INIT_EXIST_REG_VAL 0 +#define BQ2597X_ADC_AVG_INIT_NEW_CONVERSION 1 + +#define BQ2597X_ADC_SAMPLE_MASK 0x0C +#define BQ2597X_ADC_SAMPLE_SHIFT 2 +#define BQ2597X_ADC_SAMPLE_15BITS 0 +#define BQ2597X_ADC_SAMPLE_14BITS 1 +#define BQ2597X_ADC_SAMPLE_13BITS 2 +#define BQ2597X_ADC_SAMPLE_12BITS 3 + +#define BQ2597X_IBUS_ADC_DIS_MASK 0x01 +#define BQ2597X_IBUS_ADC_DIS_SHIFT 0 +#define BQ2597X_IBUS_ADC_ENABLE 0 +#define BQ2597X_IBUS_ADC_DISABLE 1 + +/* Register 15h */ +#define BQ2597X_REG_15 0x15 +#define BQ2597X_VBUS_ADC_DIS_MASK 0x80 +#define BQ2597X_VBUS_ADC_DIS_SHIFT 7 +#define BQ2597X_VBUS_ADC_ENABLE 0 +#define BQ2597X_VBUS_ADC_DISABLE 1 + +#define BQ2597X_VAC_ADC_DIS_MASK 0x40 +#define BQ2597X_VAC_ADC_DIS_SHIFT 6 +#define BQ2597X_VAC_ADC_ENABLE 0 +#define BQ2597X_VAC_ADC_DISABLE 1 + +#define BQ2597X_VOUT_ADC_DIS_MASK 0x20 +#define BQ2597X_VOUT_ADC_DIS_SHIFT 5 +#define BQ2597X_VOUT_ADC_ENABLE 0 +#define BQ2597X_VOUT_ADC_DISABLE 1 + +#define BQ2597X_VBAT_ADC_DIS_MASK 0x10 +#define BQ2597X_VBAT_ADC_DIS_SHIFT 4 +#define BQ2597X_VBAT_ADC_ENABLE 0 +#define BQ2597X_VBAT_ADC_DISABLE 1 + +#define BQ2597X_IBAT_ADC_DIS_MASK 0x08 +#define BQ2597X_IBAT_ADC_DIS_SHIFT 3 +#define BQ2597X_IBAT_ADC_ENABLE 0 +#define BQ2597X_IBAT_ADC_DISABLE 1 + +#define BQ2597X_TSBUS_ADC_DIS_MASK 0x04 +#define BQ2597X_TSBUS_ADC_DIS_SHIFT 2 +#define BQ2597X_TSBUS_ADC_ENABLE 0 +#define BQ2597X_TSBUS_ADC_DISABLE 1 + +#define BQ2597X_TSBAT_ADC_DIS_MASK 0x02 +#define BQ2597X_TSBAT_ADC_DIS_SHIFT 1 +#define BQ2597X_TSBAT_ADC_ENABLE 0 +#define BQ2597X_TSBAT_ADC_DISABLE 1 + +#define BQ2597X_TDIE_ADC_DIS_MASK 0x01 +#define BQ2597X_TDIE_ADC_DIS_SHIFT 0 +#define BQ2597X_TDIE_ADC_ENABLE 0 +#define BQ2597X_TDIE_ADC_DISABLE 1 + +/* Register 16h */ +#define BQ2597X_REG_16 0x16 +#define BQ2597X_IBUS_POL_MASK 0x80 +#define BQ2597X_IBUS_POL_SHIFT 7 +#define BQ2597X_IBUS_POL_POSITIVE 0 +#define BQ2597X_IBUS_POL_NAGETIVE 1 + +#define BQ2597X_IBUS_ADC1_MASK 0x7F +#define BQ2597X_IBUS_ADC1_SHIFT 0 +#define BQ2597X_IBUS_ADC1_BASE 0 +#define BQ2597X_IBUS_ADC1_LSB 256 + +/* Register 17h */ +#define BQ2597X_REG_17 0x17 +#define BQ2597X_IBUS_ADC0_MASK 0xFF +#define BQ2597X_IBUS_ADC0_SHIFT 0 +#define BQ2597X_IBUS_ADC0_BASE 0 +#define BQ2597X_IBUS_ADC0_LSB 1 + +/* Register 18h */ +#define BQ2597X_REG_18 0x18 +#define BQ2597X_VBUS_POL_MASK 0x80 +#define BQ2597X_VBUS_POL_SHIFT 7 +#define BQ2597X_VBUS_POL_POSITIVE 0 +#define BQ2597X_VBUS_POL_NEGATIVE 1 + +#define BQ2597X_VBUS_ADC1_MASK 0x7F +#define BQ2597X_VBUS_ADC1_SHIFT 0 +#define BQ2597X_VBUS_ADC1_BASE 0 +#define BQ2597X_VBUS_ADC1_LSB 256 + +/* Register 19h */ +#define BQ2597X_REG_19 0x19 +#define BQ2597X_VBUS_ADC0_MASK 0xFF +#define BQ2597X_VBUS_ADC0_SHIFT 0 +#define BQ2597X_VBUS_ADC0_BASE 0 +#define BQ2597X_VBUS_ADC0_LSB 1 + +/* Register 1Ah */ +#define BQ2597X_REG_1A 0x1A +#define BQ2597X_VAC_POL_MASK 0x80 +#define BQ2597X_VAC_POL_SHIFT 7 +#define BQ2597X_VAC_POL_POSITIVE 0 +#define BQ2597X_VAC_POL_NEGATIVE 1 + +#define BQ2597X_VAC_ADC1_MASK 0x7F +#define BQ2597X_VAC_ADC1_SHIFT 0 +#define BQ2597X_VAC_ADC1_BASE 0 +#define BQ2597X_VAC_ADC1_LSB 256 + +/* Register 1Bh */ +#define BQ2597X_REG_1B 0x1B +#define BQ2597X_VAC_ADC0_MASK 0xFF +#define BQ2597X_VAC_ADC0_SHIFT 0 +#define BQ2597X_VAC_ADC0_BASE 0 +#define BQ2597X_VAC_ADC0_LSB 1 + +/* Register 1Ch */ +#define BQ2597X_REG_1C 0x1C +#define BQ2597X_VOUT_POL_MASK 0x80 +#define BQ2597X_VOUT_POL_SHIFT 7 +#define BQ2597X_VOUT_POL_POSITIVE 0 +#define BQ2597X_VOUT_POL_NEGATIVE 1 + +#define BQ2597X_VOUT_ADC1_MASK 0x7F +#define BQ2597X_VOUT_ADC1_SHIFT 0 +#define BQ2597X_VOUT_ADC1_BASE 0 +#define BQ2597X_VOUT_ADC1_LSB 256 + +/* Register 1Dh */ +#define BQ2597X_REG_1D 0x1D +#define BQ2597X_VOUT_ADC0_MASK 0xFF +#define BQ2597X_VOUT_ADC0_SHIFT 0 +#define BQ2597X_VOUT_ADC0_BASE 0 +#define BQ2597X_VOUT_ADC0_LSB 1 + +/* Register 1Eh */ +#define BQ2597X_REG_1E 0x1E +#define BQ2597X_VBAT_POL_MASK 0x80 +#define BQ2597X_VBAT_POL_SHIFT 7 +#define BQ2597X_VBAT_POL_POSITIVE 0 +#define BQ2597X_VBAT_POL_NEGATIVE 1 + +#define BQ2597X_VBAT_ADC1_MASK 0x7F +#define BQ2597X_VBAT_ADC1_SHIFT 0 +#define BQ2597X_VBAT_ADC1_BASE 0 +#define BQ2597X_VBAT_ADC1_LSB 256 + +/* Register 1Fh */ +#define BQ2597X_REG_1F 0x1F +#define BQ2597X_VBAT_ADC0_MASK 0xFF +#define BQ2597X_VBAT_ADC0_SHIFT 0 +#define BQ2597X_VBAT_ADC0_BASE 0 +#define BQ2597X_VBAT_ADC0_LSB 1 + +/* Register 20h */ +#define BQ2597X_REG_20 0x20 +#define BQ2597X_IBAT_POL_MASK 0x80 +#define BQ2597X_IBAT_POL_SHIFT 7 +#define BQ2597X_IBAT_POL_POSITIVE 0 +#define BQ2597X_IBAT_POL_NEGATIVE 1 + +#define BQ2597X_IBAT_ADC1_MASK 0x7F +#define BQ2597X_IBAT_ADC1_SHIFT 0 +#define BQ2597X_IBAT_ADC1_BASE 0 +#define BQ2597X_IBAT_ADC1_LSB 256 + +/* Register 21h */ +#define BQ2597X_REG_21 0x21 +#define BQ2597X_IBAT_ADC0_MASK 0xFF +#define BQ2597X_IBAT_ADC0_SHIFT 0 +#define BQ2597X_IBAT_ADC0_BASE 0 +#define BQ2597X_IBAT_ADC0_LSB 1 + +/* Register 22h */ +#define BQ2597X_REG_22 0x22 +#define BQ2597X_TSBUS_POL_MASK 0x80 +#define BQ2597X_TSBUS_POL_SHIFT 7 +#define BQ2597X_TSBUS_POL_POSITIVE 0 +#define BQ2597X_TSBUS_POL_NEGATIVE 1 + +#define BQ2597X_TSBUS_ADC1_MASK 0x7F +#define BQ2597X_TSBUS_ADC1_SHIFT 0 +#define BQ2597X_TSBUS_ADC1_BASE 0 +#define BQ2597X_TSBUS_ADC1_LSB 25 + +/* Register 23h */ +#define BQ2597X_REG_23 0x23 +#define BQ2597X_TSBUS_ADC0_MASK 0xFF +#define BQ2597X_TSBUS_ADC0_SHIFT 0 +#define BQ2597X_TSBUS_ADC0_BASE 0 +#define BQ2597X_TSBUS_ADC0_LSB 0.09766 + +/* Register 24h */ +#define BQ2597X_REG_24 0x24 +#define BQ2597X_TSBAT_POL_MASK 0x80 +#define BQ2597X_TSBAT_POL_SHIFT 7 +#define BQ2597X_TSBAT_POL_POSITIVE 0 +#define BQ2597X_TSBAT_POL_NEGATIVE 1 + +#define BQ2597X_TSBAT_ADC1_MASK 0x7F +#define BQ2597X_TSBAT_ADC1_SHIFT 0 +#define BQ2597X_TSBAT_ADC1_BASE 0 +#define BQ2597X_TSBAT_ADC1_LSB 25 + +/* Register 25h */ +#define BQ2597X_REG_25 0x25 +#define BQ2597X_TSBAT_ADC0_MASK 0xFF +#define BQ2597X_TSBAT_ADC0_SHIFT 0 +#define BQ2597X_TSBAT_ADC0_BASE 0 +#define BQ2597X_TSBAT_ADC0_LSB 0.09766 + +/* Register 26h */ +#define BQ2597X_REG_26 0x26 +#define BQ2597X_TDIE_POL_MASK 0x80 +#define BQ2597X_TDIE_POL_SHIFT 7 +#define BQ2597X_TDIE_POL_POSITIVE 0 +#define BQ2597X_TDIE_POL_NEGATIVE 1 + +#define BQ2597X_TDIE_ADC1_MASK 0x7F +#define BQ2597X_TDIE_ADC1_SHIFT 0 +#define BQ2597X_TDIE_ADC1_BASE 0 +#define BQ2597X_TDIE_ADC1_LSB 128 + +/* Register 27h */ +#define BQ2597X_REG_27 0x27 +#define BQ2597X_TDIE_ADC0_MASK 0xFF +#define BQ2597X_TDIE_ADC0_SHIFT 0 +#define BQ2597X_TDIE_ADC0_BASE 0 +#define BQ2597X_TDIE_ADC0_LSB 0.5 + +/* Register 28h */ +#define BQ2597X_REG_28 0x28 +#define BQ2597X_TSBUS_FLT1_MASK 0xFF +#define BQ2597X_TSBUS_FLT1_SHIFT 0 +#define BQ2597X_TSBUS_FLT1_BASE 0 +#define BQ2597X_TSBUS_FLT1_LSB 0.19531 + +/* Register 29h */ +#define BQ2597X_REG_29 0x29 +#define BQ2597X_TSBAT_FLT0_MASK 0xFF +#define BQ2597X_TSBAT_FLT0_SHIFT 0 +#define BQ2597X_TSBAT_FLT0_BASE 0 +#define BQ2597X_TSBAT_FLT0_LSB 0.19531 + +/* Register 2Ah */ +#define BQ2597X_REG_2A 0x2A +#define BQ2597X_TDIE_ALM_MASK 0xFF +#define BQ2597X_TDIE_ALM_SHIFT 0 +#define BQ2597X_TDIE_ALM_BASE 30 + +#define BQ2597X_TDIE_ALM_LSB 2 /*careful multiply is used for calc*/ + +/* Register 2Bh */ +#define BQ2597X_REG_2B 0x2B +#define BQ2597X_SS_TIMEOUT_SET_MASK 0xE0 +#define BQ2597X_SS_TIMEOUT_SET_SHIFT 5 +#define BQ2597X_SS_TIMEOUT_DISABLE 0 +#define BQ2597X_SS_TIMEOUT_SET_12P5MS 1 +#define BQ2597X_SS_TIMEOUT_SET_25MS 2 +#define BQ2597X_SS_TIMEOUT_SET_50MS 3 +#define BQ2597X_SS_TIMEOUT_SET_100MS 4 +#define BQ2597X_SS_TIMEOUT_SET_400MS 5 +#define BQ2597X_SS_TIMEOUT_SET_1500MS 6 +#define BQ2597X_SS_TIMEOUT_SET_100000MS 7 + +#define BQ2597X_EN_REGULATION_MASK 0x10 +#define BQ2597X_EN_REGULATION_SHIFT 4 +#define BQ2597X_EN_REGULATION_ENABLE 1 +#define BQ2597X_EN_REGULATION_DISABLE 0 + +#define BQ2597X_VOUT_OVP_DIS_MASK 0x08 +#define BQ2597X_VOUT_OVP_DIS_SHIFT 3 +#define BQ2597X_VOUT_OVP_ENABLE 1 +#define BQ2597X_VOUT_OVP_DISABLE 0 + +#define BQ2597X_IBUS_UCP_RISE_TH_MASK 0x04 +#define BQ2597X_IBUS_UCP_RISE_TH_SHIFT 2 +#define BQ2597X_IBUS_UCP_RISE_300MA 0 +#define BQ2597X_IBUS_UCP_RISE_500MA 1 + +#define BQ2597X_SET_IBAT_SNS_RES_MASK 0x02 +#define BQ2597X_SET_IBAT_SNS_RES_SHIFT 1 +#define BQ2597X_SET_IBAT_SNS_RES_2MHM 0 +#define BQ2597X_SET_IBAT_SNS_RES_5MHM 1 + + +#define BQ2597X_VAC_PD_EN_MASK 0x01 +#define BQ2597X_VAC_PD_EN_SHIFT 0 +#define BQ2597X_VAC_PD_ENABLE 1 +#define BQ2597X_VAC_PD_DISABLE 0 + +/* Register 2Ch */ +#define BQ2597X_REG_2C 0x2C +#define BQ2597X_IBAT_REG_MASK 0xC0 +#define BQ2597X_IBAT_REG_SHIFT 6 +#define BQ2597X_IBAT_REG_200MA 0 +#define BQ2597X_IBAT_REG_300MA 1 +#define BQ2597X_IBAT_REG_400MA 2 +#define BQ2597X_IBAT_REG_500MA 3 +#define BQ2597X_VBAT_REG_MASK 0x30 +#define BQ2597X_VBAT_REG_SHIFT 4 +#define BQ2597X_VBAT_REG_50MV 0 +#define BQ2597X_VBAT_REG_100MV 1 +#define BQ2597X_VBAT_REG_150MV 2 +#define BQ2597X_VBAT_REG_200MV 3 + +#define BQ2597X_VBAT_REG_ACTIVE_STAT_MASK 0x08 +#define BQ2597X_IBAT_REG_ACTIVE_STAT_MASK 0x04 +#define BQ2597X_VDROP_OVP_ACTIVE_STAT_MASK 0x02 +#define BQ2597X_VOUT_OVP_ACTIVE_STAT_MASK 0x01 + + +#define BQ2597X_REG_2D 0x2D +#define BQ2597X_VBAT_REG_ACTIVE_FLAG_MASK 0x80 +#define BQ2597X_IBAT_REG_ACTIVE_FLAG_MASK 0x40 +#define BQ2597X_VDROP_OVP_FLAG_MASK 0x20 +#define BQ2597X_VOUT_OVP_FLAG_MASK 0x10 +#define BQ2597X_VBAT_REG_ACTIVE_MASK_MASK 0x08 +#define BQ2597X_IBAT_REG_ACTIVE_MASK_MASK 0x04 +#define BQ2597X_VDROP_OVP_MASK_MASK 0x02 +#define BQ2597X_VOUT_OVP_MASK_MASK 0x01 + + +#define BQ2597X_REG_2E 0x2E +#define BQ2597X_VBUS_ERR_LOW_DG_MASK 0x10 +#define BQ2597X_VBUS_ERR_LOW_DG_SHIFT 4 +#define BQ2597X_VBUS_ERR_LOW_DG_10US 0 +#define BQ2597X_VBUS_ERR_LOW_DG_10MS 1 +#define BQ2597X_IBUS_LOW_DG_MASK 0x08 +#define BQ2597X_IBUS_LOW_DG_SHIFT 3 +#define BQ2597X_IBUS_LOW_DG_10US 0 +#define BQ2597X_IBUS_LOW_DG_5MS 1 + + +#define SC8551_IBUS_ADC_LSB_MUL 15625 +#define SC8551_IBUS_ADC_LSB_DIV 10000 +#define SC8551_VBUS_ADC_LSB_MUL 375 +#define SC8551_VBUS_ADC_LSB_DIV 100 +#define SC8551_VAC_ADC_LSB 5 +#define SC8551_VOUT_ADC_LSB_MUL 125 +#define SC8551_VOUT_ADC_LSB_DIV 100 +#define SC8551_VBAT_ADC_LSB_MUL 125 +#define SC8551_VBAT_ADC_LSB_DIV 100 +#define SC8551_IBAT_ADC_LSB_MUL 3125 +#define SC8551_IBAT_ADC_LSB_DIV 1000 + +#define SC8551_REG_34 0x34 +#define SC8551_ADC_TRIM_MASK 0x01 +#define SC8551_ADC_TRIM_VAL 1 + +#endif diff --git a/drivers/power/bq2597x_mmi_iio/bq2597x_charger_iio.h b/drivers/power/bq2597x_mmi_iio/bq2597x_charger_iio.h new file mode 100644 index 000000000000..0e5290182514 --- /dev/null +++ b/drivers/power/bq2597x_mmi_iio/bq2597x_charger_iio.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + +#ifndef __BQ2597X_IIO_H +#define __BQ2597X_IIO_H + +#include +#include + +struct bq2597x_iio_channels { + const char *datasheet_name; + int channel_num; + enum iio_chan_type type; + long info_mask; +}; + +#define bq2597x_IIO_CHAN(_name, _num, _type, _mask) \ + { \ + .datasheet_name = _name, \ + .channel_num = _num, \ + .type = _type, \ + .info_mask = _mask, \ + }, + +#define bq2597x_CHAN_INDEX(_name, _num) \ + bq2597x_IIO_CHAN(_name, _num, IIO_INDEX, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +#define bq2597x_CHAN_CUR(_name, _num) \ + bq2597x_IIO_CHAN(_name, _num, IIO_CURRENT, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +#define bq2597x_CHAN_VOLT(_name, _num) \ + bq2597x_IIO_CHAN(_name, _num, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +static const struct bq2597x_iio_channels bq2597x_iio_psy_channels[] = { + bq2597x_CHAN_INDEX("bq2597x_cp_enabled", PSY_IIO_CP_ENABLE) + bq2597x_CHAN_INDEX("bq2597x_online", PSY_IIO_ONLINE) + bq2597x_CHAN_CUR("bq2597x_current_now", PSY_IIO_CURRENT_NOW) + bq2597x_CHAN_VOLT("bq2597x_voltage_now", PSY_IIO_VOLTAGE_NOW) + bq2597x_CHAN_INDEX("bq2597x_input_current_now", PSY_IIO_MMI_CP_INPUT_CURRENT_NOW) + bq2597x_CHAN_INDEX("bq2597x_input_voltage_settled", PSY_IIO_MMI_CP_INPUT_VOLTAGE_NOW) + bq2597x_CHAN_INDEX("bq2597x_cp_status1", PSY_IIO_CP_STATUS1) + bq2597x_CHAN_INDEX("bq2597x_cp_clear_error", PSY_IIO_CP_CLEAR_ERROR) +}; + +#endif diff --git a/drivers/power/bq2597x_mmi_iio/bq2597x_mmi_iio.c b/drivers/power/bq2597x_mmi_iio/bq2597x_mmi_iio.c new file mode 100644 index 000000000000..51df178465ac --- /dev/null +++ b/drivers/power/bq2597x_mmi_iio/bq2597x_mmi_iio.c @@ -0,0 +1,2671 @@ +/* + * BQ2570x battery charging driver + * + * Copyright (C) 2017 Texas Instruments * + * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define pr_fmt(fmt) "[bq2597x] %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bq25970_reg.h" +#include "bq2597x_charger_iio.h" +#include +#include + +#ifdef pr_debug +#undef pr_debug +#define pr_debug pr_err + +#endif + +enum { + ADC_IBUS, + ADC_VBUS, + ADC_VAC, + ADC_VOUT, + ADC_VBAT, + ADC_IBAT, + ADC_TBUS, + ADC_TBAT, + ADC_TDIE, + ADC_MAX_NUM, +}; + +enum { + BQ25970 = 0x00, +}; + +#define BQ2597X_PART_NO 0x10 +#define SC8551_PART_NO 0x00 +#define SC8551A_PART_NO 0x51 +#define NU2105_PART_NO 0xC0 +#define NU2105_MAX_SHOW_REG_ADDR 0x2F +#define SC8551_MAX_SHOW_REG_ADDR 0x36 +#define BQ25970_MAX_SHOW_REG_ADDR 0x2A +#define BAT_OVP_ALARM BIT(7) +#define BAT_OCP_ALARM BIT(6) +#define BUS_OVP_ALARM BIT(5) +#define BUS_OCP_ALARM BIT(4) +#define BAT_UCP_ALARM BIT(3) +#define VBUS_INSERT BIT(2) +#define VBAT_INSERT BIT(1) +#define ADC_DONE BIT(0) + +#define BAT_OVP_FAULT BIT(7) +#define BAT_OCP_FAULT BIT(6) +#define BUS_OVP_FAULT BIT(5) +#define BUS_OCP_FAULT BIT(4) +#define TBUS_TBAT_ALARM BIT(3) +#define TS_BAT_FAULT BIT(2) +#define TS_BUS_FAULT BIT(1) +#define TS_DIE_FAULT BIT(0) + +/*below used for comm with other module*/ +#define BAT_OVP_FAULT_SHIFT 8 +#define BAT_OCP_FAULT_SHIFT 9 +#define BUS_OVP_FAULT_SHIFT 10 +#define BUS_OCP_FAULT_SHIFT 11 +#define BAT_THERM_FAULT_SHIFT 12 +#define BUS_THERM_FAULT_SHIFT 13 +#define DIE_THERM_FAULT_SHIFT 14 + +#define BAT_OVP_FAULT_MASK (1 << BAT_OVP_FAULT_SHIFT) +#define BAT_OCP_FAULT_MASK (1 << BAT_OCP_FAULT_SHIFT) +#define BUS_OVP_FAULT_MASK (1 << BUS_OVP_FAULT_SHIFT) +#define BUS_OCP_FAULT_MASK (1 << BUS_OCP_FAULT_SHIFT) +#define BAT_THERM_FAULT_MASK (1 << BAT_THERM_FAULT_SHIFT) +#define BUS_THERM_FAULT_MASK (1 << BUS_THERM_FAULT_SHIFT) +#define DIE_THERM_FAULT_MASK (1 << DIE_THERM_FAULT_SHIFT) + +#define BAT_OVP_ALARM_SHIFT 0 +#define BAT_OCP_ALARM_SHIFT 1 +#define BUS_OVP_ALARM_SHIFT 2 +#define BUS_OCP_ALARM_SHIFT 3 +#define BAT_THERM_ALARM_SHIFT 4 +#define BUS_THERM_ALARM_SHIFT 5 +#define DIE_THERM_ALARM_SHIFT 6 +#define BAT_UCP_ALARM_SHIFT 7 + +#define BAT_OVP_ALARM_MASK (1 << BAT_OVP_ALARM_SHIFT) +#define BAT_OCP_ALARM_MASK (1 << BAT_OCP_ALARM_SHIFT) +#define BUS_OVP_ALARM_MASK (1 << BUS_OVP_ALARM_SHIFT) +#define BUS_OCP_ALARM_MASK (1 << BUS_OCP_ALARM_SHIFT) +#define BAT_THERM_ALARM_MASK (1 << BAT_THERM_ALARM_SHIFT) +#define BUS_THERM_ALARM_MASK (1 << BUS_THERM_ALARM_SHIFT) +#define DIE_THERM_ALARM_MASK (1 << DIE_THERM_ALARM_SHIFT) +#define BAT_UCP_ALARM_MASK (1 << BAT_UCP_ALARM_SHIFT) + +#define VBAT_REG_STATUS_SHIFT 0 +#define IBAT_REG_STATUS_SHIFT 1 + +#define VBAT_REG_STATUS_MASK (1 << VBAT_REG_STATUS_SHIFT) +#define IBAT_REG_STATUS_MASK (1 << VBAT_REG_STATUS_SHIFT) +/*end*/ + +#define AUTO_ENABLE_SHOW_MAX_SIZE 50 + +struct bq2597x_cfg { + bool bat_ovp_disable; + bool bat_ocp_disable; + bool bat_ovp_alm_disable; + bool bat_ocp_alm_disable; + + int bat_ovp_th; + int bat_ovp_alm_th; + int bat_ocp_th; + int bat_ocp_alm_th; + + bool bus_ovp_alm_disable; + bool bus_ocp_disable; + bool bus_ocp_alm_disable; + + int bus_ovp_th; + int bus_ovp_alm_th; + int bus_ocp_th; + int bus_ocp_alm_th; + + bool bat_ucp_alm_disable; + + int bat_ucp_alm_th; + int ac_ovp_th; + + bool bat_therm_disable; + bool bus_therm_disable; + bool die_therm_disable; + + int bat_therm_th; /*in %*/ + int bus_therm_th; /*in %*/ + int die_therm_th; /*in degC*/ + + int sense_r_mohm; +}; + +struct bq2597x { + struct device *dev; + struct i2c_client *client; + + int device_id; + int part_no; + int revision; + struct iio_dev *indio_dev; + struct iio_chan_spec *iio_chan; + struct iio_channel *int_iio_chans; + struct iio_channel **ext_iio_chans; + + struct mutex data_lock; + struct mutex i2c_rw_lock; + struct mutex charging_disable_lock; + struct mutex irq_complete; + struct regmap *regmap; + + bool irq_waiting; + bool irq_disabled; + bool resume_completed; + + bool batt_present; + bool vbus_present; + + bool usb_present; + bool charge_enabled; /* Register bit status */ + + /* ADC reading */ + int vbat_volt; + int vbus_volt; + int vout_volt; + int vac_volt; + + int ibat_curr; + int ibus_curr; + + int bat_temp; + int bus_temp; + int die_temp; + + /* alarm/fault status */ + bool bat_ovp_fault; + bool bat_ocp_fault; + bool bus_ovp_fault; + bool bus_ocp_fault; + + bool bat_ovp_alarm; + bool bat_ocp_alarm; + bool bus_ovp_alarm; + bool bus_ocp_alarm; + + bool bat_ucp_alarm; + + bool bat_therm_alarm; + bool bus_therm_alarm; + bool die_therm_alarm; + + bool bat_therm_fault; + bool bus_therm_fault; + bool die_therm_fault; + + bool therm_shutdown_flag; + bool therm_shutdown_stat; + + bool vbat_reg; + bool ibat_reg; + + int prev_alarm; + int prev_fault; + + int chg_ma; + int chg_mv; + + int charge_state; + + struct bq2597x_cfg *cfg; + + int skip_writes; + int skip_reads; + + struct bq2597x_platform_data *platform_data; + + struct delayed_work monitor_work; + + struct dentry *debug_root; + + struct power_supply *fc2_psy; + + //struct regulator *vdd_i2c_vreg; + //struct pinctrl *irq_pinctrl; + struct gpio irq_gpio; +}; + +/************************************************************************/ + +struct bq2597x *g_chip; + +static int __bq2597x_read_byte(struct bq2597x *bq, u8 reg, u8 *data) +{ + s32 ret; + + ret = i2c_smbus_read_byte_data(bq->client, reg); + if (ret < 0) { + pr_err("i2c read fail: can't read from reg 0x%02X\n", reg); + return ret; + } + + *data = (u8) ret; + + return 0; +} + +static int __bq2597x_write_byte(struct bq2597x *bq, int reg, u8 val) +{ + s32 ret; + + ret = i2c_smbus_write_byte_data(bq->client, reg, val); + if (ret < 0) { + pr_err("i2c write fail: can't write 0x%02X to reg 0x%02X: %d\n", + val, reg, ret); + return ret; + } + return 0; +} + +static int __bq2597x_read_word(struct bq2597x *bq, u8 reg, u16 *data) +{ + s32 ret; + + ret = i2c_smbus_read_word_data(bq->client, reg); + if (ret < 0) { + pr_err("i2c read fail: can't read from reg 0x%02X\n", reg); + return ret; + } + + *data = (u16) ret; + + return 0; +} + +static int bq2597x_read_byte(struct bq2597x *bq, u8 reg, u8 *data) +{ + int ret; + + if (bq->skip_reads) { + *data = 0; + return 0; + } + + mutex_lock(&bq->i2c_rw_lock); + ret = __bq2597x_read_byte(bq, reg, data); + mutex_unlock(&bq->i2c_rw_lock); + + return ret; +} + +static int bq2597x_write_byte(struct bq2597x *bq, u8 reg, u8 data) +{ + int ret; + + if (bq->skip_writes) + return 0; + + mutex_lock(&bq->i2c_rw_lock); + ret = __bq2597x_write_byte(bq, reg, data); + mutex_unlock(&bq->i2c_rw_lock); + + return ret; +} + +static int bq2597x_read_word(struct bq2597x *bq, u8 reg, u16 *data) +{ + int ret; + + if (bq->skip_reads) { + *data = 0; + return 0; + } + + mutex_lock(&bq->i2c_rw_lock); + ret = __bq2597x_read_word(bq, reg, data); + mutex_unlock(&bq->i2c_rw_lock); + + return ret; +} + +static int bq2597x_update_bits(struct bq2597x *bq, u8 reg, + u8 mask, u8 data) +{ + int ret; + u8 tmp; + + if (bq->skip_reads || bq->skip_writes) + return 0; + + mutex_lock(&bq->i2c_rw_lock); + ret = __bq2597x_read_byte(bq, reg, &tmp); + if (ret) { + pr_err("Failed: reg=%02X, ret=%d\n", reg, ret); + goto out; + } + + tmp &= ~mask; + tmp |= data & mask; + + ret = __bq2597x_write_byte(bq, reg, tmp); + if (ret) + pr_err("Failed: reg=%02X, ret=%d\n", reg, ret); + +out: + mutex_unlock(&bq->i2c_rw_lock); + return ret; +} + +/*********************************************************************/ + +static int bq2597x_enable_charge(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_CHG_ENABLE; + else + val = BQ2597X_CHG_DISABLE; + + val <<= BQ2597X_CHG_EN_SHIFT; + + pr_err("bq2597x enable charge %d\n", enable); + + ret = bq2597x_update_bits(bq, BQ2597X_REG_0C, + BQ2597X_CHG_EN_MASK, val); + + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_charge); + +static int bq2597x_check_charge_enabled(struct bq2597x *bq, bool *enabled) +{ + int ret; + u8 val; + + ret = bq2597x_read_byte(bq, BQ2597X_REG_0C, &val); + if (!ret) + *enabled = !!(val & BQ2597X_CHG_EN_MASK); + return ret; +} + +static int bq2597x_enable_wdt(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_WATCHDOG_ENABLE; + else + val = BQ2597X_WATCHDOG_DISABLE; + + val <<= BQ2597X_WATCHDOG_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_0B, + BQ2597X_WATCHDOG_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_wdt); + +static int bq2597x_set_wdt(struct bq2597x *bq, int ms) +{ + int ret; + u8 val; + + if (ms == 500) + val = BQ2597X_WATCHDOG_0P5S; + else if (ms == 1000) + val = BQ2597X_WATCHDOG_1S; + else if (ms == 5000) + val = BQ2597X_WATCHDOG_5S; + else if (ms == 30000) + val = BQ2597X_WATCHDOG_30S; + else + val = BQ2597X_WATCHDOG_30S; + + val <<= BQ2597X_WATCHDOG_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_0B, + BQ2597X_WATCHDOG_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_wdt); + +static int bq2597x_enable_batovp(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_BAT_OVP_ENABLE; + else + val = BQ2597X_BAT_OVP_DISABLE; + + val <<= BQ2597X_BAT_OVP_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_00, + BQ2597X_BAT_OVP_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_batovp); + +static int bq2597x_set_batovp_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + pr_err("bq2597x bq2597x_set_batovp_th %d\n", threshold); + if (threshold < BQ2597X_BAT_OVP_BASE) + threshold = BQ2597X_BAT_OVP_BASE; + + val = (threshold - BQ2597X_BAT_OVP_BASE) / BQ2597X_BAT_OVP_LSB; + + val <<= BQ2597X_BAT_OVP_SHIFT; + pr_err("bq2597x bq2597x_set_batovp_th 0X%x\n", val); + ret = bq2597x_update_bits(bq, BQ2597X_REG_00, + BQ2597X_BAT_OVP_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_batovp_th); + +static int bq2597x_enable_batovp_alarm(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_BAT_OVP_ALM_ENABLE; + else + val = BQ2597X_BAT_OVP_ALM_DISABLE; + + val <<= BQ2597X_BAT_OVP_ALM_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_01, + BQ2597X_BAT_OVP_ALM_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_batovp_alarm); + +static int bq2597x_set_batovp_alarm_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold < BQ2597X_BAT_OVP_ALM_BASE) + threshold = BQ2597X_BAT_OVP_ALM_BASE; + + val = (threshold - BQ2597X_BAT_OVP_ALM_BASE) / BQ2597X_BAT_OVP_ALM_LSB; + + val <<= BQ2597X_BAT_OVP_ALM_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_01, + BQ2597X_BAT_OVP_ALM_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_batovp_alarm_th); + +static int bq2597x_enable_batocp(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_BAT_OCP_ENABLE; + else + val = BQ2597X_BAT_OCP_DISABLE; + + val <<= BQ2597X_BAT_OCP_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_02, + BQ2597X_BAT_OCP_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_batocp); + +static int bq2597x_set_batocp_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold < BQ2597X_BAT_OCP_BASE) + threshold = BQ2597X_BAT_OCP_BASE; + + val = (threshold - BQ2597X_BAT_OCP_BASE) / BQ2597X_BAT_OCP_LSB; + + val <<= BQ2597X_BAT_OCP_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_02, + BQ2597X_BAT_OCP_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_batocp_th); + +static int bq2597x_enable_batocp_alarm(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_BAT_OCP_ALM_ENABLE; + else + val = BQ2597X_BAT_OCP_ALM_DISABLE; + + val <<= BQ2597X_BAT_OCP_ALM_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_03, + BQ2597X_BAT_OCP_ALM_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_batocp_alarm); + +static int bq2597x_set_batocp_alarm_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold < BQ2597X_BAT_OCP_ALM_BASE) + threshold = BQ2597X_BAT_OCP_ALM_BASE; + + val = (threshold - BQ2597X_BAT_OCP_ALM_BASE) / BQ2597X_BAT_OCP_ALM_LSB; + + val <<= BQ2597X_BAT_OCP_ALM_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_03, + BQ2597X_BAT_OCP_ALM_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_batocp_alarm_th); + +static int bq2597x_set_busovp_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold < BQ2597X_BUS_OVP_BASE) + threshold = BQ2597X_BUS_OVP_BASE; + + val = (threshold - BQ2597X_BUS_OVP_BASE) / BQ2597X_BUS_OVP_LSB; + + val <<= BQ2597X_BUS_OVP_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_06, + BQ2597X_BUS_OVP_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_busovp_th); + +static int bq2597x_enable_busovp_alarm(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_BUS_OVP_ALM_ENABLE; + else + val = BQ2597X_BUS_OVP_ALM_DISABLE; + + val <<= BQ2597X_BUS_OVP_ALM_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_07, + BQ2597X_BUS_OVP_ALM_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_busovp_alarm); + +static int bq2597x_set_busovp_alarm_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold < BQ2597X_BUS_OVP_ALM_BASE) + threshold = BQ2597X_BUS_OVP_ALM_BASE; + + val = (threshold - BQ2597X_BUS_OVP_ALM_BASE) / BQ2597X_BUS_OVP_ALM_LSB; + + val <<= BQ2597X_BUS_OVP_ALM_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_07, + BQ2597X_BUS_OVP_ALM_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_busovp_alarm_th); + +static int bq2597x_enable_busocp(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_BUS_OCP_ENABLE; + else + val = BQ2597X_BUS_OCP_DISABLE; + + val <<= BQ2597X_BUS_OCP_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_08, + BQ2597X_BUS_OCP_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_busocp); + +static int bq2597x_set_busocp_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold < BQ2597X_BUS_OCP_BASE) + threshold = BQ2597X_BUS_OCP_BASE; + + val = (threshold - BQ2597X_BUS_OCP_BASE) / BQ2597X_BUS_OCP_LSB; + + val <<= BQ2597X_BUS_OCP_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_08, + BQ2597X_BUS_OCP_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_busocp_th); + +static int bq2597x_enable_busocp_alarm(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_BUS_OCP_ALM_ENABLE; + else + val = BQ2597X_BUS_OCP_ALM_DISABLE; + + val <<= BQ2597X_BUS_OCP_ALM_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_09, + BQ2597X_BUS_OCP_ALM_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_busocp_alarm); + +static int bq2597x_set_busocp_alarm_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold < BQ2597X_BUS_OCP_ALM_BASE) + threshold = BQ2597X_BUS_OCP_ALM_BASE; + + val = (threshold - BQ2597X_BUS_OCP_ALM_BASE) / BQ2597X_BUS_OCP_ALM_LSB; + + val <<= BQ2597X_BUS_OCP_ALM_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_09, + BQ2597X_BUS_OCP_ALM_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_busocp_alarm_th); + +static int bq2597x_enable_batucp_alarm(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_BAT_UCP_ALM_ENABLE; + else + val = BQ2597X_BAT_UCP_ALM_DISABLE; + + val <<= BQ2597X_BAT_UCP_ALM_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_04, + BQ2597X_BAT_UCP_ALM_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_batucp_alarm); + +static int bq2597x_set_batucp_alarm_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold < BQ2597X_BAT_UCP_ALM_BASE) + threshold = BQ2597X_BAT_UCP_ALM_BASE; + + val = (threshold - BQ2597X_BAT_UCP_ALM_BASE) / BQ2597X_BAT_UCP_ALM_LSB; + + val <<= BQ2597X_BAT_UCP_ALM_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_04, + BQ2597X_BAT_UCP_ALM_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_batucp_alarm_th); + +static int bq2597x_set_acovp_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold < BQ2597X_AC_OVP_BASE) + threshold = BQ2597X_AC_OVP_BASE; + + if (threshold == BQ2597X_AC_OVP_6P5V) + val = 0x07; + else + val = (threshold - BQ2597X_AC_OVP_BASE) / BQ2597X_AC_OVP_LSB; + + val <<= BQ2597X_AC_OVP_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_05, + BQ2597X_AC_OVP_MASK, val); + + return ret; + +} +EXPORT_SYMBOL_GPL(bq2597x_set_acovp_th); + +static int bq2597x_set_vdrop_th(struct bq2597x *bq, int threshold) +{ + int ret; + u8 val; + + if (threshold == 300) + val = BQ2597X_VDROP_THRESHOLD_300MV; + else + val = BQ2597X_VDROP_THRESHOLD_400MV; + + val <<= BQ2597X_VDROP_THRESHOLD_SET_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_05, + BQ2597X_VDROP_THRESHOLD_SET_MASK, + val); + + return ret; +} + +static int bq2597x_set_vdrop_deglitch(struct bq2597x *bq, int us) +{ + int ret; + u8 val; + + if (us == 8) + val = BQ2597X_VDROP_DEGLITCH_8US; + else + val = BQ2597X_VDROP_DEGLITCH_5MS; + + val <<= BQ2597X_VDROP_DEGLITCH_SET_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_05, + BQ2597X_VDROP_DEGLITCH_SET_MASK, + val); + return ret; +} + +static int bq2597x_enable_bat_therm(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_TSBAT_ENABLE; + else + val = BQ2597X_TSBAT_DISABLE; + + val <<= BQ2597X_TSBAT_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_0C, + BQ2597X_TSBAT_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_bat_therm); + +/* + * the input threshold is the raw value that would write to register directly. + */ +static int bq2597x_set_bat_therm_th(struct bq2597x *bq, u8 threshold) +{ + int ret; + + ret = bq2597x_write_byte(bq, BQ2597X_REG_29, threshold); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_bat_therm_th); + +static int bq2597x_enable_bus_therm(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_TSBUS_ENABLE; + else + val = BQ2597X_TSBUS_DISABLE; + + val <<= BQ2597X_TSBUS_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_0C, + BQ2597X_TSBUS_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_bus_therm); + +/* + * the input threshold is the raw value that would write to register directly. + */ +static int bq2597x_set_bus_therm_th(struct bq2597x *bq, u8 threshold) +{ + int ret; + + ret = bq2597x_write_byte(bq, BQ2597X_REG_28, threshold); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_bus_therm_th); + +static int bq2597x_enable_die_therm(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_TDIE_ENABLE; + else + val = BQ2597X_TDIE_DISABLE; + + val <<= BQ2597X_TDIE_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_0C, + BQ2597X_TDIE_DIS_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_die_therm); + +/* + * please be noted that the unit here is degC + */ +static int bq2597x_set_die_therm_th(struct bq2597x *bq, u8 threshold) +{ + int ret; + u8 val; + + /*BE careful, LSB is here is 1/LSB, so we use multiply here*/ + val = (threshold - BQ2597X_TDIE_ALM_BASE) * BQ2597X_TDIE_ALM_LSB; + val <<= BQ2597X_TDIE_ALM_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_2A, + BQ2597X_TDIE_ALM_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_die_therm_th); + +static int bq2597x_enable_adc(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_ADC_ENABLE; + else + val = BQ2597X_ADC_DISABLE; + + val <<= BQ2597X_ADC_EN_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_14, + BQ2597X_ADC_EN_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_enable_adc); + +static int bq2597x_set_adc_average(struct bq2597x *bq, bool avg) +{ + int ret; + u8 val; + + if (avg) + val = BQ2597X_ADC_AVG_ENABLE; + else + val = BQ2597X_ADC_AVG_DISABLE; + + val <<= BQ2597X_ADC_AVG_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_14, + BQ2597X_ADC_AVG_MASK, val); + return 0; +} +EXPORT_SYMBOL_GPL(bq2597x_set_adc_average); + +static int bq2597x_set_adc_scanrate(struct bq2597x *bq, bool oneshot) +{ + int ret; + u8 val; + + if (oneshot) + val = BQ2597X_ADC_RATE_ONESHOT; + else + val = BQ2597X_ADC_RATE_CONTINOUS; + + val <<= BQ2597X_ADC_RATE_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_14, + BQ2597X_ADC_EN_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_adc_scanrate); + +static int bq2597x_set_adc_bits(struct bq2597x *bq, int bits) +{ + int ret; + u8 val; + + if (bits > 15) + bits = 15; + if (bits < 12) + bits = 12; + val = 15 - bits; + + val <<= BQ2597X_ADC_SAMPLE_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_14, + BQ2597X_ADC_SAMPLE_MASK, val); + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_adc_bits); + +#define ADC_REG_BASE 0x16 +static int bq2597x_get_adc_data(struct bq2597x *bq, int channel, int *result) +{ + int ret; + u16 val; + s16 t; + int tmp; + u8 val_h, val_l; + + if (channel > ADC_MAX_NUM) + return -EINVAL; + + if (bq->part_no == NU2105_PART_NO) { + ret = bq2597x_read_byte(bq, ADC_REG_BASE + (channel << 1), &val_h); + ret |= bq2597x_read_byte(bq, ADC_REG_BASE + (channel << 1) + 1, &val_l); + if (ret < 0) { + pr_err(" nu2105 i2c read failed!\n"); + return ret; + } + t = (u16)val_l + ((u16)val_h << 8); + } else { + + ret = bq2597x_read_word(bq, ADC_REG_BASE + (channel << 1), &val); + if (ret < 0) { + pr_err(" bq2597x i2c read failed!\n"); + return ret; + } + t = val & 0xFF; + t <<= 8; + t |= (val >> 8) & 0xFF; + } + + if (bq->part_no == SC8551_PART_NO + ||bq->part_no == SC8551A_PART_NO) { + if (channel == ADC_IBUS) { + tmp = t * SC8551_IBUS_ADC_LSB_MUL; + t = tmp /SC8551_IBUS_ADC_LSB_DIV; + } else if (channel == ADC_VBUS) { + tmp = t * SC8551_VBUS_ADC_LSB_MUL; + t = tmp /SC8551_VBUS_ADC_LSB_DIV; + } else if (channel == ADC_VAC) + t *= SC8551_VAC_ADC_LSB; + else if (channel == ADC_VOUT) { + tmp = t * SC8551_VOUT_ADC_LSB_MUL; + t = tmp /SC8551_VOUT_ADC_LSB_DIV; + } else if (channel == ADC_VBAT) { + tmp = t * SC8551_VBAT_ADC_LSB_MUL; + t = tmp /SC8551_VBAT_ADC_LSB_DIV; + } else if (channel == ADC_IBAT) { + tmp = t * SC8551_IBAT_ADC_LSB_MUL; + t = tmp /SC8551_IBAT_ADC_LSB_DIV; + } + } + + *result = t; + + return 0; +} +EXPORT_SYMBOL_GPL(bq2597x_get_adc_data); + +static int bq2597x_set_adc_scan(struct bq2597x *bq, int channel, bool enable) +{ + int ret; + u8 reg; + u8 mask; + u8 shift; + u8 val; + + if (channel > ADC_MAX_NUM) + return -EINVAL; + + if (channel == ADC_IBUS) { + reg = BQ2597X_REG_14; + shift = BQ2597X_IBUS_ADC_DIS_SHIFT; + mask = BQ2597X_IBUS_ADC_DIS_MASK; + } else { + reg = BQ2597X_REG_15; + shift = 8 - channel; + mask = 1 << shift; + } + + if (enable) + val = 0 << shift; + else + val = 1 << shift; + + ret = bq2597x_update_bits(bq, reg, mask, val); + + return ret; +} + +static int bq2597x_set_alarm_int_mask(struct bq2597x *bq, u8 mask) +{ + int ret; + u8 val; + + ret = bq2597x_read_byte(bq, BQ2597X_REG_0F, &val); + if (ret) + return ret; + + val |= mask; + + ret = bq2597x_write_byte(bq, BQ2597X_REG_0F, val); + + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_alarm_int_mask); + +static int bq2597x_clear_alarm_int_mask(struct bq2597x *bq, u8 mask) +{ + int ret; + u8 val; + + ret = bq2597x_read_byte(bq, BQ2597X_REG_0F, &val); + if (ret) + return ret; + + val &= ~mask; + + ret = bq2597x_write_byte(bq, BQ2597X_REG_0F, val); + + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_clear_alarm_int_mask); + +static int bq2597x_set_fault_int_mask(struct bq2597x *bq, u8 mask) +{ + int ret; + u8 val; + + ret = bq2597x_read_byte(bq, BQ2597X_REG_12, &val); + if (ret) + return ret; + + val |= mask; + + ret = bq2597x_write_byte(bq, BQ2597X_REG_12, val); + + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_set_fault_int_mask); + +static int bq2597x_clear_fault_int_mask(struct bq2597x *bq, u8 mask) +{ + int ret; + u8 val; + + ret = bq2597x_read_byte(bq, BQ2597X_REG_12, &val); + if (ret) + return ret; + + val &= ~mask; + + ret = bq2597x_write_byte(bq, BQ2597X_REG_12, val); + + return ret; +} +EXPORT_SYMBOL_GPL(bq2597x_clear_fault_int_mask); + +static int bq2597x_set_sense_resistor(struct bq2597x *bq, int r_mohm) +{ + int ret; + u8 val; + + if (r_mohm == 2) + val = BQ2597X_SET_IBAT_SNS_RES_2MHM; + else if (r_mohm == 5) + val = BQ2597X_SET_IBAT_SNS_RES_5MHM; + else + return -EINVAL; + + val <<= BQ2597X_SET_IBAT_SNS_RES_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_2B, + BQ2597X_SET_IBAT_SNS_RES_MASK, + val); + return ret; +} + +static int bq2597x_set_ucp_threshold(struct bq2597x *bq, int curr_ma) +{ + int ret; + u8 val; + + if (curr_ma == 300) + val = BQ2597X_IBUS_UCP_RISE_300MA; + else if (curr_ma == 500) + val = BQ2597X_IBUS_UCP_RISE_500MA; + else + val = BQ2597X_IBUS_UCP_RISE_300MA; + + val <<= BQ2597X_IBUS_UCP_RISE_TH_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_2B, + BQ2597X_IBUS_UCP_RISE_TH_MASK, + val); + return ret; +} + +static int bq2597x_enable_regulation(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_EN_REGULATION_ENABLE; + else + val = BQ2597X_EN_REGULATION_DISABLE; + + val <<= BQ2597X_EN_REGULATION_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_2B, + BQ2597X_EN_REGULATION_MASK, + val); + + return ret; + +} + +static int bq2597x_set_ss_timeout(struct bq2597x *bq, int timeout) +{ + int ret; + u8 val; + + switch (timeout) { + case 0: + val = BQ2597X_SS_TIMEOUT_DISABLE; + break; + case 12: + val = BQ2597X_SS_TIMEOUT_SET_12P5MS; + break; + case 25: + val = BQ2597X_SS_TIMEOUT_SET_25MS; + break; + case 50: + val = BQ2597X_SS_TIMEOUT_SET_50MS; + break; + case 100: + val = BQ2597X_SS_TIMEOUT_SET_100MS; + break; + case 400: + val = BQ2597X_SS_TIMEOUT_SET_400MS; + break; + case 1500: + val = BQ2597X_SS_TIMEOUT_SET_1500MS; + break; + case 100000: + val = BQ2597X_SS_TIMEOUT_SET_100000MS; + break; + default: + val = BQ2597X_SS_TIMEOUT_DISABLE; + break; + } + + val <<= BQ2597X_SS_TIMEOUT_SET_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_2B, + BQ2597X_SS_TIMEOUT_SET_MASK, + val); + + return ret; +} + +static int bq2597x_set_ibat_reg_th(struct bq2597x *bq, int th_ma) +{ + int ret; + u8 val; + + if (th_ma == 200) + val = BQ2597X_IBAT_REG_200MA; + else if (th_ma == 300) + val = BQ2597X_IBAT_REG_300MA; + else if (th_ma == 400) + val = BQ2597X_IBAT_REG_400MA; + else if (th_ma == 500) + val = BQ2597X_IBAT_REG_500MA; + else + val = BQ2597X_IBAT_REG_500MA; + + val <<= BQ2597X_IBAT_REG_SHIFT; + ret = bq2597x_update_bits(bq, BQ2597X_REG_2C, + BQ2597X_IBAT_REG_MASK, + val); + + return ret; +} + +static int bq2597x_set_vbat_reg_th(struct bq2597x *bq, int th_mv) +{ + int ret; + u8 val; + + if (th_mv == 50) + val = BQ2597X_VBAT_REG_50MV; + else if (th_mv == 100) + val = BQ2597X_VBAT_REG_100MV; + else if (th_mv == 150) + val = BQ2597X_VBAT_REG_150MV; + else + val = BQ2597X_VBAT_REG_200MV; + + val <<= BQ2597X_VBAT_REG_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_2C, + BQ2597X_VBAT_REG_MASK, + val); + + return ret; +} + +static void bq2597x_dump_reg(struct bq2597x *bq) +{ + int ret; + u8 val; + u8 addr; + + for (addr = 0x00; addr <= 0x2E; addr++) { + ret = bq2597x_read_byte(bq, addr, &val); + if (!ret) + pr_err("Reg[%02X] = 0x%02X\n", addr, val); + } +} + +void bq2597x_dump_reg_g(void) +{ + int ret; + u8 val; + u8 addr; + + for (addr = 0x00; addr <= 0x2E; addr++) { + ret = bq2597x_read_byte(g_chip, addr, &val); + if (!ret) + pr_err("Reg[%02X] = 0x%02X\n", addr, val); + } +} + +EXPORT_SYMBOL(bq2597x_dump_reg_g); + +#if 0 +static int bq2597x_check_reg_status(struct bq2597x *bq) +{ + int ret; + u8 val; + + ret = bq2597x_read_byte(bq, BQ2597X_REG_2C, &val); + if (!ret) { + bq->vbat_reg = !!(val & BQ2597X_VBAT_REG_ACTIVE_STAT_MASK); + bq->ibat_reg = !!(val & BQ2597X_IBAT_REG_ACTIVE_STAT_MASK); + } + + return ret; +} +#endif + +static int bq2597x_detect_device(struct bq2597x *bq) +{ + int ret; + u8 data; + + ret = bq2597x_read_byte(bq, BQ2597X_REG_13, &data); + if (ret < 0) { + ret = bq2597x_read_byte(bq, BQ2597X_REG_13, &data); //Try again for NU2105 in poweroff charging + } + if (ret == 0) { + bq->part_no = data; + bq->device_id = (data & BQ2597X_DEV_ID_MASK); + bq->device_id >>= BQ2597X_DEV_ID_SHIFT; + bq->revision = (data & BQ2597X_DEV_REV_MASK); + bq->revision >>= BQ2597X_DEV_REV_SHIFT; + + if (bq->part_no == NU2105_PART_NO) { + pr_info("detect device PN:%x, ID:%x, REV:%x chip_name:NU2105\n!", + bq->part_no, bq->device_id, bq->revision); + } else if ((bq->part_no == SC8551_PART_NO) || (bq->part_no == SC8551A_PART_NO)) { + pr_info("detect device PN:%x, ID:%x, REV:%x chip_name:SC8551\n!", + bq->part_no, bq->device_id, bq->revision); + } else if (bq->part_no == BQ2597X_PART_NO) { + pr_info("detect device PN:%x, ID:%x, REV:%x chip_name:BQ2597X\n!", + bq->part_no, bq->device_id, bq->revision); + } else { + pr_info("detect device PN:%x, ID:%x, REV:%x chip_name:unknow\n!", + bq->part_no, bq->device_id, bq->revision); + } + } + + return ret; +} + +static int bq2597x_parse_dt(struct bq2597x *bq, struct device *dev) +{ + int ret; + int irqn = 0; + struct device_node *np = dev->of_node; + + bq->cfg = devm_kzalloc(dev, sizeof(struct bq2597x_cfg), + GFP_KERNEL); + + if (!bq->cfg) { + pr_err("Out of memory\n"); + return -ENOMEM; + } + +#if 0 + if (of_find_property(np, "vdd-i2c-supply", NULL)) { + bq->vdd_i2c_vreg = devm_regulator_get(bq->dev, "vdd-i2c"); + if (IS_ERR(bq->vdd_i2c_vreg)) { + pr_err("bq2597x get regulator failed\n"); + return PTR_ERR(bq->vdd_i2c_vreg); + } + } +#endif + + bq->cfg->bat_ovp_disable = of_property_read_bool(np, + "ti,bq2597x,bat-ovp-disable"); + bq->cfg->bat_ocp_disable = of_property_read_bool(np, + "ti,bq2597x,bat-ocp-disable"); + bq->cfg->bat_ovp_alm_disable = of_property_read_bool(np, + "ti,bq2597x,bat-ovp-alarm-disable"); + bq->cfg->bat_ocp_alm_disable = of_property_read_bool(np, + "ti,bq2597x,bat-ocp-alarm-disable"); + bq->cfg->bus_ocp_disable = of_property_read_bool(np, + "ti,bq2597x,bus-ocp-disable"); + bq->cfg->bus_ovp_alm_disable = of_property_read_bool(np, + "ti,bq2597x,bus-ovp-alarm-disable"); + bq->cfg->bus_ocp_alm_disable = of_property_read_bool(np, + "ti,bq2597x,bus-ocp-alarm-disable"); + bq->cfg->bat_ucp_alm_disable = of_property_read_bool(np, + "ti,bq2597x,bat-ucp-alarm-disable"); + bq->cfg->bat_therm_disable = of_property_read_bool(np, + "ti,bq2597x,bat-therm-disable"); + bq->cfg->bus_therm_disable = of_property_read_bool(np, + "ti,bq2597x,bus-therm-disable"); + bq->cfg->die_therm_disable = of_property_read_bool(np, + "ti,bq2597x,die-therm-disable"); + + ret = of_property_read_u32(np, "ti,bq2597x,bat-ovp-threshold", + &bq->cfg->bat_ovp_th); + if (ret) { + pr_err("failed to read bat-ovp-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bat-ovp-alarm-threshold", + &bq->cfg->bat_ovp_alm_th); + if (ret) { + pr_err("failed to read bat-ovp-alarm-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bat-ocp-threshold", + &bq->cfg->bat_ocp_th); + if (ret) { + pr_err("failed to read bat-ocp-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bat-ocp-alarm-threshold", + &bq->cfg->bat_ocp_alm_th); + if (ret) { + pr_err("failed to read bat-ocp-alarm-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bus-ovp-threshold", + &bq->cfg->bus_ovp_th); + if (ret) { + pr_err("failed to read bus-ovp-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bus-ovp-alarm-threshold", + &bq->cfg->bus_ovp_alm_th); + if (ret) { + pr_err("failed to read bus-ovp-alarm-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bus-ocp-threshold", + &bq->cfg->bus_ocp_th); + if (ret) { + pr_err("failed to read bus-ocp-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bus-ocp-alarm-threshold", + &bq->cfg->bus_ocp_alm_th); + if (ret) { + pr_err("failed to read bus-ocp-alarm-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bat-ucp-alarm-threshold", + &bq->cfg->bat_ucp_alm_th); + if (ret) { + pr_err("failed to read bat-ucp-alarm-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bat-therm-threshold", + &bq->cfg->bat_therm_th); + if (ret) { + pr_err("failed to read bat-therm-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,bus-therm-threshold", + &bq->cfg->bus_therm_th); + if (ret) { + pr_err("failed to read bus-therm-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "ti,bq2597x,die-therm-threshold", + &bq->cfg->die_therm_th); + if (ret) { + pr_err("failed to read die-therm-threshold\n"); + return ret; + } + + ret = of_property_read_u32(np, "ti,bq2597x,ac-ovp-threshold", + &bq->cfg->ac_ovp_th); + if (ret) { + pr_err("failed to read ac-ovp-threshold\n"); + return ret; + } + + bq->irq_gpio.gpio = of_get_named_gpio(bq->dev->of_node, "bq2597x,irq-gpio", 0); + + if (!gpio_is_valid(bq->irq_gpio.gpio)) { + + pr_err("Invalid gpio irq int=%d\n", bq->irq_gpio.gpio); + return -EINVAL; + } + + ret = gpio_request(bq->irq_gpio.gpio, "bq2597x irq pin"); + if (ret) { + dev_err(bq->dev, "%s: %d gpio request failed\n", __func__, bq->irq_gpio.gpio); + return ret; + } + gpio_direction_input(bq->irq_gpio.gpio); + irqn = gpio_to_irq(bq->irq_gpio.gpio); + + if (irqn < 0) { + dev_err(bq->dev, "%s:%d gpio_to_irq failed\n", __func__, irqn); + return irqn; + } + bq->client->irq = irqn; + + ret = of_property_read_u32(np, "ti,bq2597x,sense-resistor-mohm", + &bq->cfg->sense_r_mohm); + if (ret) { + pr_err("failed to read sense-resistor-mohm\n"); + return ret; + } + + return 0; +} + +static int bq2597x_init_protection(struct bq2597x *bq) +{ + int ret; + + ret = bq2597x_enable_batovp(bq, !bq->cfg->bat_ovp_disable); + pr_info("%s bat ovp %s\n", + bq->cfg->bat_ovp_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_batocp(bq, !bq->cfg->bat_ocp_disable); + pr_info("%s bat ocp %s\n", + bq->cfg->bat_ocp_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_batovp_alarm(bq, !bq->cfg->bat_ovp_alm_disable); + pr_info("%s bat ovp alarm %s\n", + bq->cfg->bat_ovp_alm_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_batocp_alarm(bq, !bq->cfg->bat_ocp_alm_disable); + pr_info("%s bat ocp alarm %s\n", + bq->cfg->bat_ocp_alm_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_batucp_alarm(bq, !bq->cfg->bat_ucp_alm_disable); + pr_info("%s bat ocp alarm %s\n", + bq->cfg->bat_ucp_alm_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_busovp_alarm(bq, !bq->cfg->bus_ovp_alm_disable); + pr_info("%s bus ovp alarm %s\n", + bq->cfg->bus_ovp_alm_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_busocp(bq, !bq->cfg->bus_ocp_disable); + pr_info("%s bus ocp %s\n", + bq->cfg->bus_ocp_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_busocp_alarm(bq, !bq->cfg->bus_ocp_alm_disable); + pr_info("%s bus ocp alarm %s\n", + bq->cfg->bus_ocp_alm_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_bat_therm(bq, !bq->cfg->bat_therm_disable); + pr_info("%s bat therm %s\n", + bq->cfg->bat_therm_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_bus_therm(bq, !bq->cfg->bus_therm_disable); + pr_info("%s bus therm %s\n", + bq->cfg->bus_therm_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_enable_die_therm(bq, !bq->cfg->die_therm_disable); + pr_info("%s die therm %s\n", + bq->cfg->die_therm_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = bq2597x_set_batovp_th(bq, bq->cfg->bat_ovp_th); + pr_info("set bat ovp th %d %s\n", bq->cfg->bat_ovp_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_batovp_alarm_th(bq, bq->cfg->bat_ovp_alm_th); + pr_info("set bat ovp alarm threshold %d %s\n", bq->cfg->bat_ovp_alm_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_batocp_th(bq, bq->cfg->bat_ocp_th); + pr_info("set bat ocp threshold %d %s\n", bq->cfg->bat_ocp_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_batocp_alarm_th(bq, bq->cfg->bat_ocp_alm_th); + pr_info("set bat ocp alarm threshold %d %s\n", bq->cfg->bat_ocp_alm_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_busovp_th(bq, bq->cfg->bus_ovp_th); + pr_info("set bus ovp threshold %d %s\n", bq->cfg->bus_ovp_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_busovp_alarm_th(bq, bq->cfg->bus_ovp_alm_th); + pr_info("set bus ovp alarm threshold %d %s\n", bq->cfg->bus_ovp_alm_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_busocp_th(bq, bq->cfg->bus_ocp_th); + pr_info("set bus ocp threshold %d %s\n", bq->cfg->bus_ocp_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_busocp_alarm_th(bq, bq->cfg->bus_ocp_alm_th); + pr_info("set bus ocp alarm th %d %s\n", bq->cfg->bus_ocp_alm_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_batucp_alarm_th(bq, bq->cfg->bat_ucp_alm_th); + pr_info("set bat ucp threshold %d %s\n", bq->cfg->bat_ucp_alm_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_bat_therm_th(bq, bq->cfg->bat_therm_th); + pr_info("set die therm threshold %d %s\n", bq->cfg->bat_therm_th, + !ret ? "successfully" : "failed"); + ret = bq2597x_set_bus_therm_th(bq, bq->cfg->bus_therm_th); + pr_info("set bus therm threshold %d %s\n", bq->cfg->bus_therm_th, + !ret ? "successfully" : "failed"); + ret = bq2597x_set_die_therm_th(bq, bq->cfg->die_therm_th); + pr_info("set die therm threshold %d %s\n", bq->cfg->die_therm_th, + !ret ? "successfully" : "failed"); + + ret = bq2597x_set_acovp_th(bq, bq->cfg->ac_ovp_th); + pr_info("set ac ovp threshold %d %s\n", bq->cfg->ac_ovp_th, + !ret ? "successfully" : "failed"); + + return 0; +} + +static int bq2597x_init_adc(struct bq2597x *bq) +{ + + bq2597x_set_adc_scanrate(bq, false); + bq2597x_set_adc_bits(bq, 13); + bq2597x_set_adc_average(bq, true); + bq2597x_set_adc_scan(bq, ADC_IBUS, true); + bq2597x_set_adc_scan(bq, ADC_VBUS, true); + bq2597x_set_adc_scan(bq, ADC_VOUT, false); + bq2597x_set_adc_scan(bq, ADC_VBAT, true); + bq2597x_set_adc_scan(bq, ADC_IBAT, true); + bq2597x_set_adc_scan(bq, ADC_TBUS, true); + bq2597x_set_adc_scan(bq, ADC_TBAT, true); + bq2597x_set_adc_scan(bq, ADC_TDIE, true); + bq2597x_set_adc_scan(bq, ADC_VAC, true); + + bq2597x_enable_adc(bq, true); + return 0; +} + +static int bq2597x_init_int_src(struct bq2597x *bq) +{ + int ret; + /*TODO:be careful ts bus and ts bat alarm bit mask is in + * fault mask register, so you need call + * bq2597x_set_fault_int_mask for tsbus and tsbat alarm*/ + ret = bq2597x_set_alarm_int_mask(bq, ADC_DONE + /* | BAT_UCP_ALARM*/ + | BAT_OVP_ALARM); + if (ret) { + pr_err("failed to set alarm mask:%d\n", ret); + return ret; + } +#if 0 + ret = bq2597x_set_fault_int_mask(bq, TS_BUS_FAULT); + if (ret) { + pr_err("failed to set fault mask:%d\n", ret); + return ret; + } +#endif + return ret; +} + +static int sc8551_init_adc_trim(struct bq2597x *bq) +{ + int ret; + + ret = bq2597x_update_bits(bq, SC8551_REG_34, + SC8551_ADC_TRIM_MASK, SC8551_ADC_TRIM_VAL); + return ret; +} + +static int sc8551_set_ibus_low_dg(struct bq2597x *bq, int low_dg) +{ + int ret; + u8 val; + + if (low_dg == 10) + val = BQ2597X_IBUS_LOW_DG_10US; + else + val = BQ2597X_IBUS_LOW_DG_5MS; + + val <<= BQ2597X_IBUS_LOW_DG_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_2E, + BQ2597X_IBUS_LOW_DG_MASK, + val); + + return ret; +} + +static int bq2597x_init_regulation(struct bq2597x *bq) +{ + bq2597x_set_ibat_reg_th(bq, 300); + bq2597x_set_vbat_reg_th(bq, 100); + + bq2597x_set_vdrop_deglitch(bq, 5000); + bq2597x_set_vdrop_th(bq, 400); + + if (bq->part_no == NU2105_PART_NO) { + bq2597x_update_bits(bq, BQ2597X_REG_0C, + BQ2597X_FREQ_SHIFT_MASK, (0x02 << BQ2597X_FREQ_SHIFT_SHIFT)); + } + bq2597x_enable_regulation(bq, false); + + return 0; +} + +static int bq2597x_enable_vout(struct bq2597x *bq, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = BQ2597X_VOUT_OVP_ENABLE; + else + val = BQ2597X_VOUT_OVP_DISABLE; + + val <<= BQ2597X_VOUT_OVP_DIS_SHIFT; + + ret = bq2597x_update_bits(bq, BQ2597X_REG_2B, + BQ2597X_VOUT_OVP_DIS_MASK, + val); + + return ret; + +} + +static int bq2597x_init_device(struct bq2597x *bq) +{ + bq2597x_enable_wdt(bq, false); + + + bq2597x_set_ss_timeout(bq, 0); + bq2597x_set_sense_resistor(bq, bq->cfg->sense_r_mohm); + bq2597x_set_ucp_threshold(bq, 300); + + bq2597x_init_protection(bq); + bq2597x_init_adc(bq); + bq2597x_init_int_src(bq); + + bq2597x_init_regulation(bq); + + bq2597x_enable_vout(bq, false); + + if (bq->part_no == SC8551_PART_NO + ||bq->part_no == SC8551A_PART_NO) { + sc8551_init_adc_trim(bq); + sc8551_set_ibus_low_dg(bq, 5000); + } + return 0; +} + +static int bq2597x_set_present(struct bq2597x *bq, bool present) +{ + bq->usb_present = present; + + if (present) + bq2597x_init_device(bq); + return 0; +} + +static ssize_t bq2597x_show_registers(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bq2597x *bq = dev_get_drvdata(dev); + u8 addr; + u8 val; + u8 tmpbuf[300]; + int len; + int idx = 0; + int ret; + u8 max_addr = 0; + + if (bq->part_no == NU2105_PART_NO) { + max_addr = NU2105_MAX_SHOW_REG_ADDR; + idx = snprintf(buf, PAGE_SIZE, "%s:\n", "nu2105"); + } else if ((bq->part_no == SC8551A_PART_NO) || (bq->part_no == SC8551_PART_NO)) { + max_addr = SC8551_MAX_SHOW_REG_ADDR; + idx = snprintf(buf, PAGE_SIZE, "%s:\n", "sc8551"); + } else { + max_addr = BQ25970_MAX_SHOW_REG_ADDR; + idx = snprintf(buf, PAGE_SIZE, "%s:\n", "bq25970"); + } + for (addr = 0x0; addr <= 0x2E; addr++) { + ret = bq2597x_read_byte(bq, addr, &val); + if (ret == 0) { + len = snprintf(tmpbuf, PAGE_SIZE - idx, + "Reg[%.2X] = 0x%.2x\n", addr, val); + memcpy(&buf[idx], tmpbuf, len); + idx += len; + } + } + + return idx; +} + +static ssize_t bq2597x_store_register(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct bq2597x *bq = dev_get_drvdata(dev); + int ret; + unsigned int reg; + unsigned int val; + + ret = sscanf(buf, "%x %x", ®, &val); + if (ret == 2 && reg <= 0x2E) + bq2597x_write_byte(bq, (unsigned char)reg, (unsigned char)val); + + return count; +} + +static DEVICE_ATTR(registers, 0660, bq2597x_show_registers, bq2597x_store_register); + +static struct attribute *bq2597x_attributes[] = { + &dev_attr_registers.attr, + NULL, +}; + +static const struct attribute_group bq2597x_attr_group = { + .attrs = bq2597x_attributes, +}; + +static enum power_supply_property bq2597x_charger_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, +}; + +static void bq2597x_check_alarm_status(struct bq2597x *bq); +static void bq2597x_check_fault_status(struct bq2597x *bq); + +static int bq2597x_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct bq2597x *bq = power_supply_get_drvdata(psy); + int ret; + int result; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = bq->vbus_present; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = bq2597x_get_adc_data(bq, ADC_VBAT, &result); + if (!ret) + bq->vbat_volt = result; + + val->intval = bq->vbat_volt; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = bq2597x_get_adc_data(bq, ADC_IBAT, &result); + if (!ret) + bq->ibat_curr = result; + + val->intval = bq->ibat_curr; + break; + default: + return -EINVAL; + + } + return 0; +} + +static int bq2597x_charger_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct bq2597x *bq = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_PRESENT: + bq2597x_set_present(bq, !!val->intval); + pr_info("set present :%d\n", val->intval); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int bq2597x_charger_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + int ret; + + switch (prop) { + default: + ret = 0; + break; + } + return ret; +} + +static const struct power_supply_desc bq2597x_psy_desc = { + .name = "cp-standalone", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = bq2597x_charger_props, + .num_properties = ARRAY_SIZE(bq2597x_charger_props), + .get_property = bq2597x_charger_get_property, + .set_property = bq2597x_charger_set_property, + .property_is_writeable = bq2597x_charger_is_writeable, +}; + +static int bq2597x_psy_register(struct bq2597x *bq) +{ + struct power_supply_config bq2597x_cfg = {}; + + bq2597x_cfg.drv_data = bq; + bq2597x_cfg.of_node = bq->dev->of_node; + bq->fc2_psy = power_supply_register(bq->dev, &bq2597x_psy_desc, &bq2597x_cfg); + if (IS_ERR(bq->fc2_psy)) { + pr_err("Couldn't register bq2597x power supply\n"); + return PTR_ERR(bq->fc2_psy); + } + + pr_info("power supply register bq2597x successfully\n"); + return 0; +} + +EXPORT_SYMBOL_GPL(bq2597x_dump_reg); + + +static int bq2597x_iio_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val1, + int val2, long mask) +{ + struct bq2597x *bq = iio_priv(indio_dev); + int rc = 0; + + switch (chan->channel) { + case PSY_IIO_CP_ENABLE: + bq2597x_enable_charge(bq, val1); + bq2597x_check_charge_enabled(bq, &bq->charge_enabled); + pr_info("PSY_IIO_CP_ENABLE: %s\n", + val1 ? "enable" : "disable"); + break; + case PSY_IIO_ONLINE: + bq2597x_set_present(bq, !!val1); + pr_info("set present :%d\n", val1); + break; + case PSY_IIO_CP_CLEAR_ERROR: + bq->bat_ovp_fault = false; + bq->bat_ocp_fault = false; + bq->bus_ovp_fault = false; + bq->bus_ocp_fault = false; + break; + default: + pr_err("Unsupported bq2597x IIO chan %d\n", chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) + pr_err("Couldn't write IIO channel %d, rc = %d\n", + chan->channel, rc); + + return rc; +} + +static int bq2597x_iio_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val1, + int *val2, long mask) +{ + struct bq2597x *bq = iio_priv(indio_dev); + int rc = 0; + int result; + *val1 = 0; + + switch (chan->channel) { + case PSY_IIO_CP_ENABLE: + bq2597x_check_charge_enabled(bq, &bq->charge_enabled); + *val1 = bq->charge_enabled; + pr_info("read bq2597x enable:%d\n",*val1); + break; + case PSY_IIO_ONLINE: + *val1 = bq->vbus_present; + break; + case PSY_IIO_MMI_CP_INPUT_VOLTAGE_NOW: + rc = bq2597x_get_adc_data(bq, ADC_VBUS, &result); + if (!rc) + bq->vbus_volt = result; + + *val1 = bq->vbus_volt; + break; + case PSY_IIO_MMI_CP_INPUT_CURRENT_NOW: + rc = bq2597x_get_adc_data(bq, ADC_IBUS, &result); + if (!rc) + bq->ibus_curr = result; + + *val1 = bq->ibus_curr; + break; + case PSY_IIO_CP_STATUS1: + bq2597x_check_alarm_status(bq); + bq2597x_check_fault_status(bq); + *val1 = ((bq->bat_ovp_alarm << BAT_OVP_ALARM_SHIFT) + | (bq->bat_ocp_alarm << BAT_OCP_ALARM_SHIFT) + | (bq->bat_ucp_alarm << BAT_UCP_ALARM_SHIFT) + | (bq->bus_ovp_alarm << BUS_OVP_ALARM_SHIFT) + | (bq->bus_ocp_alarm << BUS_OCP_ALARM_SHIFT) + | (bq->bat_therm_alarm << BAT_THERM_ALARM_SHIFT) + | (bq->bus_therm_alarm << BUS_THERM_ALARM_SHIFT) + | (bq->die_therm_alarm << DIE_THERM_ALARM_SHIFT) + | (bq->bat_ovp_fault << BAT_OVP_FAULT_SHIFT) + | (bq->bat_ocp_fault << BAT_OCP_FAULT_SHIFT) + | (bq->bus_ovp_fault << BUS_OVP_FAULT_SHIFT) + | (bq->bus_ocp_fault << BUS_OCP_FAULT_SHIFT) + | (bq->bat_therm_fault << BAT_THERM_FAULT_SHIFT) + | (bq->bus_therm_fault << BUS_THERM_FAULT_SHIFT) + | (bq->die_therm_fault << DIE_THERM_FAULT_SHIFT)); + break; + + default: + pr_err("Unsupported bq2597x IIO chan %d\n", chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) { + pr_err("Couldn't read IIO channel %d, rc = %d\n", + chan->channel, rc); + return rc; + } + + return IIO_VAL_INT; +} + +static int bq2597x_iio_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + struct bq2597x *bq = iio_priv(indio_dev); + struct iio_chan_spec *iio_chan = bq->iio_chan; + int i; + + for (i = 0; i < ARRAY_SIZE(bq2597x_iio_psy_channels); + i++, iio_chan++) + if (iio_chan->channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static const struct iio_info bq2597x_iio_info = { + .read_raw = bq2597x_iio_read_raw, + .write_raw = bq2597x_iio_write_raw, + .of_xlate = bq2597x_iio_of_xlate, +}; + +static int bq2597x_init_iio_psy(struct bq2597x *chip) +{ + struct iio_dev *indio_dev = chip->indio_dev; + struct iio_chan_spec *chan; + int bq2597x_num_iio_channels = ARRAY_SIZE(bq2597x_iio_psy_channels); + int rc, i; + + chip->iio_chan = devm_kcalloc(chip->dev, bq2597x_num_iio_channels, + sizeof(*chip->iio_chan), GFP_KERNEL); + if (!chip->iio_chan) + return -ENOMEM; + + chip->int_iio_chans = devm_kcalloc(chip->dev, + bq2597x_num_iio_channels, + sizeof(*chip->int_iio_chans), + GFP_KERNEL); + if (!chip->int_iio_chans) + return -ENOMEM; + + indio_dev->info = &bq2597x_iio_info; + indio_dev->dev.parent = chip->dev; + indio_dev->dev.of_node = chip->dev->of_node; + indio_dev->name = "bq2597x-charger"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = chip->iio_chan; + indio_dev->num_channels = bq2597x_num_iio_channels; + + for (i = 0; i < bq2597x_num_iio_channels; i++) { + chip->int_iio_chans[i].indio_dev = indio_dev; + chan = &chip->iio_chan[i]; + chip->int_iio_chans[i].channel = chan; + chan->address = i; + chan->channel = bq2597x_iio_psy_channels[i].channel_num; + chan->type = bq2597x_iio_psy_channels[i].type; + chan->datasheet_name = + bq2597x_iio_psy_channels[i].datasheet_name; + chan->extend_name = + bq2597x_iio_psy_channels[i].datasheet_name; + chan->info_mask_separate = + bq2597x_iio_psy_channels[i].info_mask; + } + + rc = devm_iio_device_register(chip->dev, indio_dev); + if (rc) + pr_err("Failed to register bq2597x IIO device, rc=%d\n", rc); + + return rc; +} + +static void bq2597x_check_alarm_status(struct bq2597x *bq) +{ + int ret; + u8 flag = 0; + u8 stat = 0; + bool changed = false; + + mutex_lock(&bq->data_lock); + ret = bq2597x_read_byte(bq, BQ2597X_REG_08, &flag); + if (!ret && (flag & BQ2597X_IBUS_UCP_FALL_FLAG_MASK)) + pr_debug("UCP_FLAG =0x%02X\n", !!(flag & BQ2597X_IBUS_UCP_FALL_FLAG_MASK)); + + + ret = bq2597x_read_byte(bq, BQ2597X_REG_2D, &flag); + if (!ret && (flag & BQ2597X_VDROP_OVP_FLAG_MASK)) + pr_debug("VDROP_OVP_FLAG =0x%02X\n", !!(flag & BQ2597X_VDROP_OVP_FLAG_MASK)); + /*read to clear alarm flag*/ + ret = bq2597x_read_byte(bq, BQ2597X_REG_0E, &flag); + if (!ret && flag) + pr_debug("INT_FLAG =0x%02X\n", flag); + + ret = bq2597x_read_byte(bq, BQ2597X_REG_0D, &stat); + if (!ret && stat != bq->prev_alarm) { + pr_debug("INT_STAT = 0X%02x\n", stat); + changed = true; + bq->prev_alarm = stat; + bq->bat_ovp_alarm = !!(stat & BAT_OVP_ALARM); + bq->bat_ocp_alarm = !!(stat & BAT_OCP_ALARM); + bq->bus_ovp_alarm = !!(stat & BUS_OVP_ALARM); + bq->bus_ocp_alarm = !!(stat & BUS_OCP_ALARM); + bq->batt_present = !!(stat & VBAT_INSERT); + bq->vbus_present = !!(stat & VBUS_INSERT); + bq->bat_ucp_alarm = !!(stat & BAT_UCP_ALARM); + } + + ret = bq2597x_read_byte(bq, BQ2597X_REG_08, &stat); + if (!ret && (stat & 0x50)) + pr_err("Reg[05]BUS_UCPOVP = 0x%02X\n", stat); + + ret = bq2597x_read_byte(bq, BQ2597X_REG_0A, &stat); + if (!ret && (stat & 0x02)) + pr_err("Reg[0A]CONV_OCP = 0x%02X\n", stat); + + mutex_unlock(&bq->data_lock); + + if (changed) + power_supply_changed(bq->fc2_psy); + +} + +static void bq2597x_check_fault_status(struct bq2597x *bq) +{ + int ret; + u8 flag = 0; + u8 stat = 0; + bool changed = false; + + mutex_lock(&bq->data_lock); + + ret = bq2597x_read_byte(bq, BQ2597X_REG_10, &stat); + if (!ret && stat) + pr_err("FAULT_STAT = 0x%02X\n", stat); + + ret = bq2597x_read_byte(bq, BQ2597X_REG_11, &flag); + if (!ret && flag) + pr_err("FAULT_FLAG = 0x%02X\n", flag); + + if (!ret && flag != bq->prev_fault) { + changed = true; + bq->prev_fault = flag; + bq->bat_ovp_fault = !!(flag & BAT_OVP_FAULT); + bq->bat_ocp_fault = !!(flag & BAT_OCP_FAULT); + bq->bus_ovp_fault = !!(flag & BUS_OVP_FAULT); + bq->bus_ocp_fault = !!(flag & BUS_OCP_FAULT); + bq->bat_therm_fault = !!(flag & TS_BAT_FAULT); + bq->bus_therm_fault = !!(flag & TS_BUS_FAULT); + + bq->bat_therm_alarm = !!(flag & TBUS_TBAT_ALARM); + bq->bus_therm_alarm = !!(flag & TBUS_TBAT_ALARM); + } + + mutex_unlock(&bq->data_lock); + + if (changed) + power_supply_changed(bq->fc2_psy); + +} + +static irqreturn_t bq2597x_charger_interrupt(int irq, void *dev_id) +{ + struct bq2597x *bq = dev_id; + + pr_debug("interrupt enter -\n"); + mutex_lock(&bq->irq_complete); + bq->irq_waiting = true; + if (!bq->resume_completed) { + dev_dbg(bq->dev, "IRQ triggered before device-resume\n"); + if (!bq->irq_disabled) { + disable_irq_nosync(irq); + bq->irq_disabled = true; + } + mutex_unlock(&bq->irq_complete); + return IRQ_HANDLED; + } + bq->irq_waiting = false; + + /* TODO */ + bq2597x_check_alarm_status(bq); + bq2597x_check_fault_status(bq); + + bq2597x_dump_reg(bq); + mutex_unlock(&bq->irq_complete); + + pr_err("stat=0x%02X, fault flag =0x%02X\n", bq->prev_alarm, bq->prev_fault); + + return IRQ_HANDLED; +} + +static void determine_initial_status(struct bq2597x *bq) +{ + bq2597x_charger_interrupt(bq->client->irq, bq); +} + +static int show_registers(struct seq_file *m, void *data) +{ + struct bq2597x *bq = m->private; + u8 addr; + int ret; + u8 val; + + for (addr = 0x0; addr <= 0x2B; addr++) { + ret = bq2597x_read_byte(bq, addr, &val); + if (!ret) + seq_printf(m, "Reg-[%02X] = 0x%02X\n", addr, val); + } + return 0; +} + +static int reg_debugfs_open(struct inode *inode, struct file *file) +{ + struct bq2597x *bq = inode->i_private; + + return single_open(file, show_registers, bq); +} + +static const struct file_operations reg_debugfs_ops = { + .owner = THIS_MODULE, + .open = reg_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void create_debugfs_entry(struct bq2597x *bq) +{ + bq->debug_root = debugfs_create_dir("bq2597x", NULL); + if (!bq->debug_root) + pr_err("Failed to create debug dir\n"); + + if (bq->debug_root) { + debugfs_create_file("registers", + S_IFREG | S_IRUGO, + bq->debug_root, bq, ®_debugfs_ops); + + debugfs_create_x32("skip_reads", + S_IFREG | S_IWUSR | S_IRUGO, + bq->debug_root, + &(bq->skip_reads)); + debugfs_create_x32("skip_writes", + S_IFREG | S_IWUSR | S_IRUGO, + bq->debug_root, + &(bq->skip_writes)); + } +} + +static const struct regmap_config bq2597x_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0xFFFF, +}; + +static ssize_t force_chg_auto_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + bool mode; + + struct bq2597x *bq = dev_get_drvdata(dev->parent); + if (!bq) { + pr_err("bq2597x_mmi_charger: chip not valid\n"); + return -ENODEV; + } + + r = kstrtobool(buf, &mode); + if (r) { + pr_err("bq2597x_mmi_charger: Invalid chrg enable value = %d\n", mode); + return -EINVAL; + } + + r = bq2597x_enable_charge(bq, mode); + if (r < 0) { + pr_err("bq2597x_mmi_charger Couldn't %s charging rc=%d\n", + mode ? "enable" : "disable", (int)r); + return r; + } + + return r ? r : count; +} + +static ssize_t force_chg_auto_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + int state = 0; + + struct bq2597x *bq = dev_get_drvdata(dev->parent); + + if (!bq) { + pr_err("mmi_discrete_charger: chip not valid\n"); + state = -ENODEV; + goto end; + } + + ret = bq2597x_check_charge_enabled(bq, &bq->charge_enabled); + + state = bq->charge_enabled; + +end: + return scnprintf(buf, AUTO_ENABLE_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_auto_enable, 0664, + force_chg_auto_enable_show, + force_chg_auto_enable_store); + +static struct attribute *bq2597x_force_attributes[] = { + &dev_attr_force_chg_auto_enable.attr, + NULL, +}; + +static const struct attribute_group bq2597x_attribute_group = { + .attrs = bq2597x_force_attributes, +}; + +static int bq2597x_create_sys(struct device *dev, const struct attribute_group * grp) +{ + int err = 0; + struct power_supply *cp_ply; + + pr_err("enter bq2597x create sys \n"); + cp_ply = power_supply_get_by_name("cp-standalone"); + + if(NULL == dev){ + pr_err("[BATT]: failed to register battery\n"); + return -EINVAL; + } + + if(cp_ply) + { + err = sysfs_create_group(&cp_ply->dev.kobj, grp); + + if (!err) + { + pr_info("creat BMT sysfs group ok\n"); + } + else + { + pr_err("creat BMT sysfs group fail\n"); + err = -EIO; + } + power_supply_put(cp_ply); + } + else + { + pr_err("don't have /sys/class/power_supply/cp-standalone dir\n"); + } + + return err; +} + +static int bq2597x_charger_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct bq2597x *bq; + int ret; + struct iio_dev *indio_dev; + //const struct of_device_id *match; + //struct device_node *node = client->dev.of_node; + pr_err("bq2597x_charger_probe start\n"); + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*bq)); + + + if (!indio_dev) + return -ENOMEM; + + bq = iio_priv(indio_dev); + if (!bq) { + pr_err("Out of memory\n"); + return -ENOMEM; + } + bq->indio_dev = indio_dev; + + bq->dev = &client->dev; + + bq->client = client; + i2c_set_clientdata(client, bq); + + mutex_init(&bq->i2c_rw_lock); + mutex_init(&bq->data_lock); + mutex_init(&bq->charging_disable_lock); + mutex_init(&bq->irq_complete); + + bq->resume_completed = true; + bq->irq_waiting = false; + + bq->regmap = regmap_init_i2c(client, &bq2597x_regmap_config); + if (IS_ERR(bq->regmap)) { + pr_err("Couldn't initialize register regmap rc = %ld\n", + PTR_ERR(bq->regmap)); + ret = PTR_ERR(bq->regmap); + goto free_mem; + } + + + ret = bq2597x_init_iio_psy(bq); + if (ret < 0) { + pr_err("Failed to initialize bq2597x IIO PSY, ret = %d\n", ret); + return ret; + } + + if (client->dev.of_node) { + ret = bq2597x_parse_dt(bq, &client->dev); + if (ret) + goto free_mem; + } + + ret = bq2597x_detect_device(bq); + if (ret) { + pr_err("No bq2597x device found!\n"); + goto free_mem; + } + + ret = bq2597x_init_device(bq); + if (ret) { + pr_err("Failed to init device\n"); + goto free_mem; + } + + ret = bq2597x_psy_register(bq); + if (ret) { + pr_err("Failed to register psy\n"); + goto free_mem; + } +/* + bq->irq_pinctrl = + pinctrl_get_select(bq->dev, "bq_int_default"); + if (!bq->irq_pinctrl) { + pr_err("Couldn't set pinctrl bq_int_default\n"); + goto free_psy; + } +*/ + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, bq2597x_charger_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "bq2597x charger irq", bq); + if (ret < 0) { + pr_err("request irq for irq=%d failed, ret =%d\n", + client->irq, ret); + goto free_gpio; + } + enable_irq_wake(client->irq); + } + + device_init_wakeup(bq->dev, 1); + create_debugfs_entry(bq); + ret = sysfs_create_group(&bq->dev->kobj, &bq2597x_attr_group); + if (ret) { + pr_err("failed to register sysfs. err: %d\n", ret); + goto free_irq; + } + + ret = bq2597x_create_sys(&(client->dev), &bq2597x_attribute_group); + if(ret){ + pr_err("[BATT]: Err failed to creat BMT attributes\n"); + goto free_irq; + } + + determine_initial_status(bq); + + pr_info("bq2597x probe successfully-, Part Num:%d\n!", + bq->part_no); + g_chip = bq; + return 0; + +free_irq: + devm_free_irq(&client->dev,client->irq,bq); + power_supply_unregister(bq->fc2_psy); +free_gpio: + gpio_free(bq->irq_gpio.gpio); + power_supply_unregister(bq->fc2_psy); +#if 0 +free_psy: + power_supply_unregister(bq->fc2_psy); + +disable_regator: + regulator_disable(bq->vdd_i2c_vreg); +free_regulator: + devm_regulator_put(bq->vdd_i2c_vreg); +#endif +free_mem: + devm_kfree(bq->dev, bq); + return ret; +} + +static inline bool is_device_suspended(struct bq2597x *bq) +{ + return !bq->resume_completed; +} + +static int bq2597x_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq2597x *bq = i2c_get_clientdata(client); + + mutex_lock(&bq->irq_complete); + bq->resume_completed = false; + mutex_unlock(&bq->irq_complete); + pr_err("Suspend successfully!"); + + return 0; +} + +static int bq2597x_suspend_noirq(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq2597x *bq = i2c_get_clientdata(client); + + if (bq->irq_waiting) { + pr_err_ratelimited("Aborting suspend, an interrupt was detected while suspending\n"); + return -EBUSY; + } + return 0; +} + +static int bq2597x_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq2597x *bq = i2c_get_clientdata(client); + + pr_err("Resume begin"); + mutex_lock(&bq->irq_complete); + bq->resume_completed = true; + if (bq->irq_waiting) { + bq->irq_disabled = false; + enable_irq(client->irq); + mutex_unlock(&bq->irq_complete); + bq2597x_charger_interrupt(client->irq, bq); + } else { + mutex_unlock(&bq->irq_complete); + } + + power_supply_changed(bq->fc2_psy); + pr_err("Resume successfully!"); + + return 0; +} +static int bq2597x_charger_remove(struct i2c_client *client) +{ + struct bq2597x *bq = i2c_get_clientdata(client); + + bq2597x_enable_adc(bq, false); + + //devm_regulator_put(bq->vdd_i2c_vreg); + devm_free_irq(&client->dev,client->irq,bq); + gpio_free(bq->irq_gpio.gpio); + power_supply_unregister(bq->fc2_psy); + + mutex_destroy(&bq->charging_disable_lock); + mutex_destroy(&bq->data_lock); + mutex_destroy(&bq->i2c_rw_lock); + mutex_destroy(&bq->irq_complete); + + debugfs_remove_recursive(bq->debug_root); + + sysfs_remove_group(&bq->dev->kobj, &bq2597x_attr_group); + sysfs_remove_group(&(bq->client->dev.kobj), &bq2597x_attribute_group); + + return 0; +} + +static void bq2597x_charger_shutdown(struct i2c_client *client) +{ + struct bq2597x *bq = i2c_get_clientdata(client); + bq2597x_enable_adc(bq, false); + pr_info("Shutdown Successfully\n"); +} + +static struct of_device_id bq2597x_charger_match_table[] = { + {.compatible = "ti,bq25970-charger",}, + {}, +}; +MODULE_DEVICE_TABLE(of, bq2597x_charger_match_table); + +static const struct i2c_device_id bq2597x_charger_id[] = { + { "bq25970-charger", BQ25970 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, bq2597x_charger_id); + +static const struct dev_pm_ops bq2597x_pm_ops = { + .resume = bq2597x_resume, + .suspend_noirq = bq2597x_suspend_noirq, + .suspend = bq2597x_suspend, +}; +static struct i2c_driver bq2597x_charger_driver = { + .driver = { + .name = "bq2597x-charger", + .owner = THIS_MODULE, + .of_match_table = bq2597x_charger_match_table, + .pm = &bq2597x_pm_ops, + }, + .id_table = bq2597x_charger_id, + + .probe = bq2597x_charger_probe, + .remove = bq2597x_charger_remove, + .shutdown = bq2597x_charger_shutdown, +}; + +module_i2c_driver(bq2597x_charger_driver); + +MODULE_DESCRIPTION("TI BQ2597x Charger Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Texas Instruments"); diff --git a/drivers/power/bq25980_mmi_iio/Android.mk b/drivers/power/bq25980_mmi_iio/Android.mk new file mode 100644 index 000000000000..073a6108fd35 --- /dev/null +++ b/drivers/power/bq25980_mmi_iio/Android.mk @@ -0,0 +1,19 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := bq25980_mmi_iio.ko + +ifeq ($(MMI_BQ25980_BULK_READ),true) + KERNEL_CFLAGS += CONFIG_MMI_BQ25980_BULK_READ=y +endif + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/bq25980_mmi_iio/Kbuild b/drivers/power/bq25980_mmi_iio/Kbuild new file mode 100644 index 000000000000..ab15aa923577 --- /dev/null +++ b/drivers/power/bq25980_mmi_iio/Kbuild @@ -0,0 +1,10 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/bq25980_mmi_iio + +ifneq ($(filter m y,$(CONFIG_MMI_BQ25980_BULK_READ)),) + EXTRA_CFLAGS += -DCONFIG_MMI_BQ25980_BULK_READ +endif + +obj-m += bq25980_mmi_iio.o diff --git a/drivers/power/bq25980_mmi_iio/Makefile b/drivers/power/bq25980_mmi_iio/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/power/bq25980_mmi_iio/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/power/bq25980_mmi_iio/bq25980_mmi_iio.c b/drivers/power/bq25980_mmi_iio/bq25980_mmi_iio.c new file mode 100644 index 000000000000..ed18ee5d32cf --- /dev/null +++ b/drivers/power/bq25980_mmi_iio/bq25980_mmi_iio.c @@ -0,0 +1,2737 @@ +// SPDX-License-Identifier: GPL-2.0 +// BQ25980 Battery Charger Driver +// Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bq25980_reg.h" +#include "bq25980_mmi_iio.h" + +//undef because of pinctrl +#define CONFIG_INTERRUPT_AS_GPIO + +/* +Pay attention to the order of the macro definitions +for alarm_status and fault_status.You can't modify + bq25980_alarm_status and bq25980_fault_status. + +#define BAT_OVP_ALARM_SHIFT 0 +#define BAT_OCP_ALARM_SHIFT 1 +#define BUS_OVP_ALARM_SHIFT 2 +#define BUS_OCP_ALARM_SHIFT 3 +#define BAT_THERM_ALARM_SHIFT 4 +#define BUS_THERM_ALARM_SHIFT 5 +#define DIE_THERM_ALARM_SHIFT 6 +#define BAT_UCP_ALARM_SHIFT 7 + +#define BAT_OVP_FAULT_SHIFT 8 +#define BAT_OCP_FAULT_SHIFT 9 +#define BUS_OVP_FAULT_SHIFT 10 +#define BUS_OCP_FAULT_SHIFT 11 +#define BAT_THERM_FAULT_SHIFT 12 +#define BUS_THERM_FAULT_SHIFT 13 +#define DIE_THERM_FAULT_SHIFT 14 +*/ + +union bq25980_alarm_status { + struct { + unsigned char bat_ovp_alarm:1; + unsigned char bat_ocp_alarm:1; + unsigned char bus_ovp_alarm:1; + unsigned char bus_ocp_alarm:1; + unsigned char bat_therm_alarm:1; + unsigned char bus_therm_alarm:1; + unsigned char die_therm_alarm:1; + unsigned char bat_ucp_alarm:1; + }bits; + unsigned char status; +}; + +union bq25980_fault_status { + struct { + unsigned char bat_ovp_fault:1; + unsigned char bat_ocp_fault:1; + unsigned char bus_ovp_fault:1; + unsigned char bus_ocp_fault:1; + unsigned char bat_therm_fault:1; + unsigned char bus_therm_fault:1; + unsigned char die_therm_fault:1; + unsigned char :1; + }bits; + unsigned char status; +}; + +struct bq25980_state { + bool dischg; + bool ovp; + bool ocp; + bool wdt; + bool tflt; + bool online; + bool ce; + bool hiz; + bool bypass; + bool cp_switch;//add cp_switch state + + u32 vbat_adc; + u32 vsys_adc; + u32 ibat_adc; + u32 fault_status; +}; + +enum bq_work_mode { + BQ_STANDALONE, + BQ_SLAVE, + BQ_MASTER, +}; + +#define BQ_MODE_COUNT 3 +#define BQ25960_PART_NO 0x00 +#define SC8541_PART_NO 0x41 + +enum bq_device_id { + BQ25980 = 0, + BQ25960 = 8, + BQ25975, +}; + +enum bq_compatible_id { + BQ25960_STANDALONE, + BQ25960_SLAVE, + BQ25960_MASTER, + + BQ25980_STANDALONE, + BQ25980_SLAVE, + BQ25980_MASTER, + + BQ25975_STANDALONE, +}; + +struct bq25980_chip_info { + + int model_id; + + const struct regmap_config *regmap_config; + + const struct reg_default *reg_init_values; + + int busocp_sc_def; + int busocp_byp_def; + int busocp_sc_max; + int busocp_byp_max; + int busocp_sc_min; + int busocp_byp_min; + int busocp_step; + int busocp_offset; + + int busovp_sc_def; + int busovp_byp_def; + int busovp_sc_step; + + int busovp_sc_offset; + int busovp_byp_step; + int busovp_byp_offset; + int busovp_sc_min; + int busovp_sc_max; + int busovp_byp_min; + int busovp_byp_max; + + int batovp_def; + int batovp_max; + int batovp_min; + int batovp_step; + int batovp_offset; + + int batocp_def; + int batocp_max; + + int vac_sc_ovp; + int vac_byp_ovp; + + int adc_curr_step; + int adc_vbat_volt_step; + int adc_vbus_volt_step; + int adc_vbus_volt_offset; + int adc_vout_volt_step; + int adc_vout_volt_offset; + +}; + +struct bq25980_init_data { + u32 ichg; + u32 bypass_ilim; + u32 sc_ilim; + u32 vreg; + u32 iterm; + u32 iprechg; + u32 bypass_vlim; + u32 sc_vlim; + u32 ichg_max; + u32 vreg_max; +}; + +struct bq25980_device { + struct i2c_client *client; + struct device *dev; + struct power_supply *charger; + struct power_supply *battery; + struct mutex lock; + struct regmap *regmap; + + char model_name[I2C_NAME_SIZE]; + + struct bq25980_init_data init_data; + const struct bq25980_chip_info *chip_info; + struct bq25980_state state; + int watchdog_timer; + int mode; + int device_id; + struct power_supply_desc psy_desc; + struct pinctrl *irq_pinctrl; + bool usb_present; + int irq_counts; + bool irq_waiting; + bool irq_disabled; + bool resume_completed; + struct mutex irq_complete; + + union bq25980_alarm_status alarm_status; + union bq25980_fault_status fault_status; + struct iio_dev *indio_dev; + struct iio_chan_spec *iio_chan; + struct iio_channel *int_iio_chans; + struct iio_channel **ext_iio_chans; + + int reg_addr; + int reg_data; + int part_no; + int sc8541_addr; +}; + +static struct reg_default bq25980_reg_init_val[] = { + {BQ25980_BATOVP, 0x69},//0x69:9100mV + {BQ25980_BATOVP_ALM, 0x61},//0x61:8940mV + {BQ25980_BATOCP, 0xEE},//0xEE:disable for dual //0x64:11000mA for standalone + {BQ25980_BATOCP_ALM, 0x7F},//0x7F:12700mA + {BQ25980_CHRGR_CFG_1, 0xA8}, + {BQ25980_CHRGR_CTRL_1, 0x4A}, + {BQ25980_BUSOVP, 0x3C},//0X3c:20000mV + {BQ25980_BUSOVP_ALM, 0x32},//0X32:19000mV + {BQ25980_BUSOCP, 0x0C},//0X0c:4200mA + {BQ25980_REG_09, 0x8C}, + {BQ25980_TEMP_CONTROL, 0x2C}, + {BQ25980_TDIE_ALM, 0x78},//0x78:85C + {BQ25980_TSBUS_FLT, 0x15}, + {BQ25980_TSBAT_FLG, 0x15}, + {BQ25980_VAC_CONTROL, 0xD8},//0xD8:22V + {BQ25980_CHRGR_CTRL_2, 0x00}, + {BQ25980_CHRGR_CTRL_3, 0x74},//0x74:watchdog disable 5s,275kHz + {BQ25980_CHRGR_CTRL_4, 0x01}, + {BQ25980_CHRGR_CTRL_5, 0x00}, + + {BQ25980_MASK1, 0x00}, + {BQ25980_MASK2, 0x00}, + {BQ25980_MASK3, 0x00}, + {BQ25980_MASK4, 0x00}, + {BQ25980_MASK5, 0x80}, + + {BQ25980_ADC_CONTROL1, 0x04},//sample 14 bit + {BQ25980_ADC_CONTROL2, 0xE6}, +}; + +static struct reg_default sc8541_reg_init_val[] = { + {BQ25980_BATOVP, 0x4a},//0x47:4550mV 0x4a:4580mv + {BQ25980_BATOVP_ALM, 0x42},//0x3f:4470mV 0x42:4500mv + {BQ25980_BATOCP, 0xEE},//0xEE:disable for dual //0x46:7000mA for standalone + {BQ25980_BATOCP_ALM, 0x7F},//0x7F:12700mA + {BQ25980_CHRGR_CFG_1, 0x00}, + {BQ25980_CHRGR_CTRL_1, 0x49}, + {BQ25980_BUSOVP, 0x64},//0X50:11000mV 0x64:12000mv + {BQ25980_BUSOVP_ALM, 0x50},//0X46:10500mV 0X50:11000mV + {BQ25980_BUSOCP, 0x0C},//0X0c:4000mA + {BQ25980_REG_09, 0x00}, + {BQ25980_TEMP_CONTROL, 0x2C}, + {BQ25980_TDIE_ALM, 0x78},//0x78:85C + {BQ25980_TSBUS_FLT, 0x15}, + {BQ25980_TSBAT_FLG, 0x15}, + {BQ25980_VAC_CONTROL, 0x6c},//0xb4:14v*2 for vacovp + {BQ25980_CHRGR_CTRL_2, 0x00}, + {BQ25980_CHRGR_CTRL_3, 0x94},//0x94:watchdog disable 5s,500kHz + {BQ25980_CHRGR_CTRL_4, 0xf1},//5m oum battery sense resister & ss_timeout is 10s + {BQ25980_CHRGR_CTRL_5, 0x60}, + + {BQ25980_MASK1, 0x00}, + {BQ25980_MASK2, 0x00}, + {BQ25980_MASK3, 0x00}, + {BQ25980_MASK4, 0x00}, + {BQ25980_MASK5, 0x00}, + + {BQ25980_ADC_CONTROL1, 0x00},//disable ADC_EN as default + {BQ25980_ADC_CONTROL2, 0x06}, //0x26: enable vac1 vac2 adc and vout adc + +}; + +static struct reg_default bq25960_reg_init_val[] = { + {BQ25980_BATOVP, 0x6c},//0x69:4550mV 0x6c:4580mv + {BQ25980_BATOVP_ALM, 0x64},//0x61:4470mV 0x64:4500mv + {BQ25980_BATOCP, 0xEE},//0xEE:disable for dual //0x46:7000mA for standalone + {BQ25980_BATOCP_ALM, 0x7F},//0x7F:12700mA + {BQ25980_CHRGR_CFG_1, 0xA8}, + {BQ25980_CHRGR_CTRL_1, 0x49}, + {BQ25980_BUSOVP, 0x64},//0X50:11000mV 0x64:12000mv + {BQ25980_BUSOVP_ALM, 0x50},//0X46:10500mV 0X50:11000mV + {BQ25980_BUSOCP, 0x0C},//0X0c:4000mA + {BQ25980_REG_09, 0x8C}, + {BQ25980_TEMP_CONTROL, 0x2C}, + {BQ25980_TDIE_ALM, 0x78},//0x78:85C + {BQ25980_TSBUS_FLT, 0x15}, + {BQ25980_TSBAT_FLG, 0x15}, + {BQ25980_VAC_CONTROL, 0x48},//0x48:12V*2 + {BQ25980_CHRGR_CTRL_2, 0x00}, + {BQ25980_CHRGR_CTRL_3, 0x94},//0x94:watchdog disable 5s,500kHz + {BQ25980_CHRGR_CTRL_4, 0xf1},//5m oum battery sense resister & ss_timeout is 10s + {BQ25980_CHRGR_CTRL_5, 0x60}, + + {BQ25980_MASK1, 0x00}, + {BQ25980_MASK2, 0x00}, + {BQ25980_MASK3, 0x00}, + {BQ25980_MASK4, 0x00}, + {BQ25980_MASK5, 0x80}, + + {BQ25980_ADC_CONTROL1, 0x04},//sample 14 bit + {BQ25980_ADC_CONTROL2, 0xE6}, + +}; + +static struct reg_default bq25980_reg_defs[] = { + {BQ25980_BATOVP, 0x5A}, + {BQ25980_BATOVP_ALM, 0x46}, + {BQ25980_BATOCP, 0x51}, + {BQ25980_BATOCP_ALM, 0x50}, + {BQ25980_CHRGR_CFG_1, 0x28}, + {BQ25980_CHRGR_CTRL_1, 0x0}, + {BQ25980_BUSOVP, 0x26}, + {BQ25980_BUSOVP_ALM, 0x22}, + {BQ25980_BUSOCP, 0xD}, + {BQ25980_REG_09, 0xC}, + {BQ25980_TEMP_CONTROL, 0x30}, + {BQ25980_TDIE_ALM, 0xC8}, + {BQ25980_TSBUS_FLT, 0x15}, + {BQ25980_TSBAT_FLG, 0x15}, + {BQ25980_VAC_CONTROL, 0x0}, + {BQ25980_CHRGR_CTRL_2, 0x0}, + {BQ25980_CHRGR_CTRL_3, 0x20}, + {BQ25980_CHRGR_CTRL_4, 0x1D}, + {BQ25980_CHRGR_CTRL_5, 0x18}, + {BQ25980_STAT1, 0x0}, + {BQ25980_STAT2, 0x0}, + {BQ25980_STAT3, 0x0}, + {BQ25980_STAT4, 0x0}, + {BQ25980_STAT5, 0x0}, + {BQ25980_FLAG1, 0x0}, + {BQ25980_FLAG2, 0x0}, + {BQ25980_FLAG3, 0x0}, + {BQ25980_FLAG4, 0x0}, + {BQ25980_FLAG5, 0x0}, + {BQ25980_MASK1, 0x0}, + {BQ25980_MASK2, 0x0}, + {BQ25980_MASK3, 0x0}, + {BQ25980_MASK4, 0x0}, + {BQ25980_MASK5, 0x0}, + {BQ25980_DEVICE_INFO, 0x8}, + {BQ25980_ADC_CONTROL1, 0x0}, + {BQ25980_ADC_CONTROL2, 0x0}, + {BQ25980_IBUS_ADC_LSB, 0x0}, + {BQ25980_IBUS_ADC_MSB, 0x0}, + {BQ25980_VBUS_ADC_LSB, 0x0}, + {BQ25980_VBUS_ADC_MSB, 0x0}, + {BQ25980_VAC1_ADC_LSB, 0x0}, + {BQ25980_VAC2_ADC_LSB, 0x0}, + {BQ25980_VOUT_ADC_LSB, 0x0}, + {BQ25980_VBAT_ADC_LSB, 0x0}, + {BQ25980_IBAT_ADC_MSB, 0x0}, + {BQ25980_IBAT_ADC_LSB, 0x0}, + {BQ25980_TSBUS_ADC_LSB, 0x0}, + {BQ25980_TSBAT_ADC_LSB, 0x0}, + {BQ25980_TDIE_ADC_LSB, 0x0}, + {BQ25980_DEGLITCH_TIME, 0x0}, + {BQ25980_CHRGR_CTRL_6, 0x0}, +}; + +static struct reg_default bq25975_reg_defs[] = { + {BQ25980_BATOVP, 0x5A}, + {BQ25980_BATOVP_ALM, 0x46}, + {BQ25980_BATOCP, 0x51}, + {BQ25980_BATOCP_ALM, 0x50}, + {BQ25980_CHRGR_CFG_1, 0x28}, + {BQ25980_CHRGR_CTRL_1, 0x0}, + {BQ25980_BUSOVP, 0x26}, + {BQ25980_BUSOVP_ALM, 0x22}, + {BQ25980_BUSOCP, 0xD}, + {BQ25980_REG_09, 0xC}, + {BQ25980_TEMP_CONTROL, 0x30}, + {BQ25980_TDIE_ALM, 0xC8}, + {BQ25980_TSBUS_FLT, 0x15}, + {BQ25980_TSBAT_FLG, 0x15}, + {BQ25980_VAC_CONTROL, 0x0}, + {BQ25980_CHRGR_CTRL_2, 0x0}, + {BQ25980_CHRGR_CTRL_3, 0x20}, + {BQ25980_CHRGR_CTRL_4, 0x1D}, + {BQ25980_CHRGR_CTRL_5, 0x18}, + {BQ25980_STAT1, 0x0}, + {BQ25980_STAT2, 0x0}, + {BQ25980_STAT3, 0x0}, + {BQ25980_STAT4, 0x0}, + {BQ25980_STAT5, 0x0}, + {BQ25980_FLAG1, 0x0}, + {BQ25980_FLAG2, 0x0}, + {BQ25980_FLAG3, 0x0}, + {BQ25980_FLAG4, 0x0}, + {BQ25980_FLAG5, 0x0}, + {BQ25980_MASK1, 0x0}, + {BQ25980_MASK2, 0x0}, + {BQ25980_MASK3, 0x0}, + {BQ25980_MASK4, 0x0}, + {BQ25980_MASK5, 0x0}, + {BQ25980_DEVICE_INFO, 0x8}, + {BQ25980_ADC_CONTROL1, 0x0}, + {BQ25980_ADC_CONTROL2, 0x0}, + {BQ25980_IBUS_ADC_LSB, 0x0}, + {BQ25980_IBUS_ADC_MSB, 0x0}, + {BQ25980_VBUS_ADC_LSB, 0x0}, + {BQ25980_VBUS_ADC_MSB, 0x0}, + {BQ25980_VAC1_ADC_LSB, 0x0}, + {BQ25980_VAC2_ADC_LSB, 0x0}, + {BQ25980_VOUT_ADC_LSB, 0x0}, + {BQ25980_VBAT_ADC_LSB, 0x0}, + {BQ25980_IBAT_ADC_MSB, 0x0}, + {BQ25980_IBAT_ADC_LSB, 0x0}, + {BQ25980_TSBUS_ADC_LSB, 0x0}, + {BQ25980_TSBAT_ADC_LSB, 0x0}, + {BQ25980_TDIE_ADC_LSB, 0x0}, + {BQ25980_DEGLITCH_TIME, 0x0}, + {BQ25980_CHRGR_CTRL_6, 0x0}, +}; + +static struct reg_default bq25960_reg_defs[] = { + {BQ25980_BATOVP, 0x5A}, + {BQ25980_BATOVP_ALM, 0x46}, + {BQ25980_BATOCP, 0x51}, + {BQ25980_BATOCP_ALM, 0x50}, + {BQ25980_CHRGR_CFG_1, 0x28}, + {BQ25980_CHRGR_CTRL_1, 0x0}, + {BQ25980_BUSOVP, 0x26}, + {BQ25980_BUSOVP_ALM, 0x22}, + {BQ25980_BUSOCP, 0xD}, + {BQ25980_REG_09, 0xC}, + {BQ25980_TEMP_CONTROL, 0x30}, + {BQ25980_TDIE_ALM, 0xC8}, + {BQ25980_TSBUS_FLT, 0x15}, + {BQ25980_TSBAT_FLG, 0x15}, + {BQ25980_VAC_CONTROL, 0x0}, + {BQ25980_CHRGR_CTRL_2, 0x0}, + {BQ25980_CHRGR_CTRL_3, 0x20}, + {BQ25980_CHRGR_CTRL_4, 0x1D}, + {BQ25980_CHRGR_CTRL_5, 0x18}, + {BQ25980_STAT1, 0x0}, + {BQ25980_STAT2, 0x0}, + {BQ25980_STAT3, 0x0}, + {BQ25980_STAT4, 0x0}, + {BQ25980_STAT5, 0x0}, + {BQ25980_FLAG1, 0x0}, + {BQ25980_FLAG2, 0x0}, + {BQ25980_FLAG3, 0x0}, + {BQ25980_FLAG4, 0x0}, + {BQ25980_FLAG5, 0x0}, + {BQ25980_MASK1, 0x0}, + {BQ25980_MASK2, 0x0}, + {BQ25980_MASK3, 0x0}, + {BQ25980_MASK4, 0x0}, + {BQ25980_MASK5, 0x0}, + {BQ25980_DEVICE_INFO, 0x8}, + {BQ25980_ADC_CONTROL1, 0x0}, + {BQ25980_ADC_CONTROL2, 0x0}, + {BQ25980_IBUS_ADC_LSB, 0x0}, + {BQ25980_IBUS_ADC_MSB, 0x0}, + {BQ25980_VBUS_ADC_LSB, 0x0}, + {BQ25980_VBUS_ADC_MSB, 0x0}, + {BQ25980_VAC1_ADC_LSB, 0x0}, + {BQ25980_VAC2_ADC_LSB, 0x0}, + {BQ25980_VOUT_ADC_LSB, 0x0}, + {BQ25980_VBAT_ADC_LSB, 0x0}, + {BQ25980_IBAT_ADC_MSB, 0x0}, + {BQ25980_IBAT_ADC_LSB, 0x0}, + {BQ25980_TSBUS_ADC_LSB, 0x0}, + {BQ25980_TSBAT_ADC_LSB, 0x0}, + {BQ25980_TDIE_ADC_LSB, 0x0}, + {BQ25980_DEGLITCH_TIME, 0x0}, + {BQ25980_CHRGR_CTRL_6, 0x0}, +}; + +static struct reg_default sc8541_reg_defs[] = { + {BQ25980_BATOVP, 0x4a}, + {BQ25980_BATOVP_ALM, 0x42}, + {BQ25980_BATOCP, 0x51}, + {BQ25980_BATOCP_ALM, 0x50}, + {BQ25980_CHRGR_CFG_1, 0x0}, + {BQ25980_CHRGR_CTRL_1, 0x0}, + {BQ25980_BUSOVP, 0x26}, + {BQ25980_BUSOVP_ALM, 0x22}, + {BQ25980_BUSOCP, 0xD}, + {BQ25980_REG_09, 0x0}, + {BQ25980_TEMP_CONTROL, 0x00}, + {BQ25980_TDIE_ALM, 0xC8}, + {BQ25980_TSBUS_FLT, 0x15}, + {BQ25980_TSBAT_FLG, 0x15}, + {BQ25980_VAC_CONTROL, 0x0}, + {BQ25980_CHRGR_CTRL_2, 0x0}, + {BQ25980_CHRGR_CTRL_3, 0x20}, + {BQ25980_CHRGR_CTRL_4, 0x1D}, + {BQ25980_CHRGR_CTRL_5, 0x18}, + {BQ25980_STAT1, 0x0}, + {BQ25980_STAT2, 0x0}, + {BQ25980_STAT3, 0x0}, + {BQ25980_STAT4, 0x0}, + {BQ25980_STAT5, 0x0}, + {BQ25980_FLAG1, 0x0}, + {BQ25980_FLAG2, 0x0}, + {BQ25980_FLAG3, 0x0}, + {BQ25980_FLAG4, 0x0}, + {BQ25980_FLAG5, 0x0}, + {BQ25980_MASK1, 0x0}, + {BQ25980_MASK2, 0x0}, + {BQ25980_MASK3, 0x0}, + {BQ25980_MASK4, 0x0}, + {BQ25980_MASK5, 0x0}, + {BQ25980_DEVICE_INFO, 0x41}, + {BQ25980_ADC_CONTROL1, 0x0}, + {BQ25980_ADC_CONTROL2, 0x0}, + {BQ25980_IBUS_ADC_LSB, 0x0}, + {BQ25980_IBUS_ADC_MSB, 0x0}, + {BQ25980_VBUS_ADC_LSB, 0x0}, + {BQ25980_VBUS_ADC_MSB, 0x0}, + {BQ25980_VAC1_ADC_LSB, 0x0}, + {BQ25980_VAC2_ADC_LSB, 0x0}, + {BQ25980_VOUT_ADC_LSB, 0x0}, + {BQ25980_VBAT_ADC_LSB, 0x0}, + {BQ25980_IBAT_ADC_MSB, 0x0}, + {BQ25980_IBAT_ADC_LSB, 0x0}, + {BQ25980_TSBUS_ADC_LSB, 0x0}, + {BQ25980_TSBAT_ADC_LSB, 0x0}, + {BQ25980_TDIE_ADC_LSB, 0x0}, + {BQ25980_DEGLITCH_TIME, 0x0}, + {BQ25980_CHRGR_CTRL_6, 0x0}, +}; + +static int bq25980_reg_init(struct bq25980_device *bq); +//static int bq25980_watchdog_time[BQ25980_NUM_WD_VAL] = {5000, 10000, 50000, +// 300000}; + +/*static void dump_reg(struct bq25980_device *bq, int start, int end) +{ + int ret; + unsigned int val; + int addr; + + for (addr = start; addr <= end; addr++) { + ret = regmap_read(bq->regmap, addr, &val); + if (!ret) + dev_err(bq->dev, "[%s] Reg[%02X] = 0x%02X\n", bq->model_name, addr, val); + } +}*/ + +static void dump_all_reg(struct bq25980_device *bq) +{ + int ret; + unsigned int val; + int addr; + + for (addr = 0x00; addr <= 0x37; addr++) { + ret = regmap_read(bq->regmap, addr, &val); + if (!ret) + dev_err(bq->dev, "[%s] Reg[%02X] = 0x%02X\n", bq->model_name, addr, val); + } +} +/* +static int bq25980_get_adc_enable(struct bq25980_device *bq) +{ + unsigned int reg_code; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_ADC_CONTROL1, ®_code); + if (ret) + return ret; + + return !!(reg_code & BQ25980_ADC_EN); +} +*/ +static int bq25980_set_adc_enable(struct bq25980_device *bq, bool enable) +{ + int ret; + + dev_notice(bq->dev, "%s %d", __FUNCTION__, enable); + + if (enable) + ret = regmap_update_bits(bq->regmap, BQ25980_ADC_CONTROL1, + BQ25980_ADC_EN, BQ25980_ADC_EN); + else + ret = regmap_update_bits(bq->regmap, BQ25980_ADC_CONTROL1, + BQ25980_ADC_EN, 0); + + return ret; +} + +/* +static int bq25980_get_input_curr_lim(struct bq25980_device *bq) +{ + unsigned int busocp_reg_code; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_BUSOCP, &busocp_reg_code); + if (ret) + return ret; + + return (busocp_reg_code * bq->chip_info->busocp_step) + bq->chip_info->busocp_offset; +} + +static int bq25980_set_hiz(struct bq25980_device *bq, int setting) +{ + return regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_EN_HIZ, setting); +}*/ +/* +static int bq25980_set_input_curr_lim(struct bq25980_device *bq, int busocp) +{ + unsigned int busocp_reg_code; + + if (!busocp) + return bq25980_set_hiz(bq, BQ25980_ENABLE_HIZ); + + bq25980_set_hiz(bq, BQ25980_DISABLE_HIZ); + + if (bq->state.bypass) { + busocp = max(busocp, bq->chip_info->busocp_byp_min); + busocp = min(busocp, bq->chip_info->busocp_byp_max); + } else { + busocp = max(busocp, bq->chip_info->busocp_sc_min); + busocp = min(busocp, bq->chip_info->busocp_sc_max); + } + + busocp_reg_code = (busocp - bq->chip_info->busocp_offset) + / bq->chip_info->busocp_step; + + return regmap_write(bq->regmap, BQ25980_BUSOCP, busocp_reg_code); +} + +static int bq25980_get_input_volt_lim(struct bq25980_device *bq) +{ + unsigned int busovp_reg_code; + unsigned int busovp_offset; + unsigned int busovp_step; + int ret; + + if (bq->state.bypass) { + busovp_step = bq->chip_info->busovp_byp_step; + busovp_offset = bq->chip_info->busovp_byp_offset; + } else { + busovp_step = bq->chip_info->busovp_sc_step; + busovp_offset = bq->chip_info->busovp_sc_offset; + } + + ret = regmap_read(bq->regmap, BQ25980_BUSOVP, &busovp_reg_code); + if (ret) + return ret; + + return (busovp_reg_code * busovp_step) + busovp_offset; +} + +static int bq25980_set_input_volt_lim(struct bq25980_device *bq, int busovp) +{ + unsigned int busovp_reg_code; + unsigned int busovp_step; + unsigned int busovp_offset; + int ret; + + if (bq->state.bypass) { + busovp_step = bq->chip_info->busovp_byp_step; + busovp_offset = bq->chip_info->busovp_byp_offset; + if (busovp > bq->chip_info->busovp_byp_max) + busovp = bq->chip_info->busovp_byp_max; + else if (busovp < bq->chip_info->busovp_byp_min) + busovp = bq->chip_info->busovp_byp_min; + } else { + busovp_step = bq->chip_info->busovp_sc_step; + busovp_offset = bq->chip_info->busovp_sc_offset; + if (busovp > bq->chip_info->busovp_sc_max) + busovp = bq->chip_info->busovp_sc_max; + else if (busovp < bq->chip_info->busovp_sc_min) + busovp = bq->chip_info->busovp_sc_min; + } + + busovp_reg_code = (busovp - busovp_offset) / busovp_step; + + ret = regmap_write(bq->regmap, BQ25980_BUSOVP, busovp_reg_code); + if (ret) + return ret; + + return regmap_write(bq->regmap, BQ25980_BUSOVP_ALM, busovp_reg_code); +} +*/ +static int bq25980_get_const_charge_curr(struct bq25980_device *bq) +{ + unsigned int batocp_reg_code; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_BATOCP, &batocp_reg_code); + if (ret) + return ret; + + return (batocp_reg_code & BQ25980_BATOCP_MASK) * + BQ25980_BATOCP_STEP_uA; +} +/* +static int bq25980_set_const_charge_curr(struct bq25980_device *bq, int batocp) +{ + unsigned int batocp_reg_code; + int ret; + + batocp = max(batocp, BQ25980_BATOCP_MIN_uA); + batocp = min(batocp, bq->chip_info->batocp_max); + + batocp_reg_code = batocp / BQ25980_BATOCP_STEP_uA; + + ret = regmap_update_bits(bq->regmap, BQ25980_BATOCP, + BQ25980_BATOCP_MASK, batocp_reg_code); + if (ret) + return ret; + + return regmap_update_bits(bq->regmap, BQ25980_BATOCP_ALM, + BQ25980_BATOCP_MASK, batocp_reg_code); +} +*/ +static int bq25980_get_const_charge_volt(struct bq25980_device *bq) +{ + unsigned int batovp_reg_code; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_BATOVP, &batovp_reg_code); + if (ret) + return ret; + + return ((batovp_reg_code * bq->chip_info->batovp_step) + + bq->chip_info->batovp_offset); +} +/* +static int bq25980_set_const_charge_volt(struct bq25980_device *bq, int batovp) +{ + unsigned int batovp_reg_code; + int ret; + + if (batovp < bq->chip_info->batovp_min) + batovp = bq->chip_info->batovp_min; + + if (batovp > bq->chip_info->batovp_max) + batovp = bq->chip_info->batovp_max; + + batovp_reg_code = (batovp - bq->chip_info->batovp_offset) / + bq->chip_info->batovp_step; + + ret = regmap_write(bq->regmap, BQ25980_BATOVP, batovp_reg_code); + if (ret) + return ret; + + return regmap_write(bq->regmap, BQ25980_BATOVP_ALM, batovp_reg_code); +}*/ +/* +static int bq25980_set_bypass(struct bq25980_device *bq, bool en_bypass) +{ + int ret; + + if (en_bypass) + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_EN_BYPASS, BQ25980_EN_BYPASS); + else + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_EN_BYPASS, en_bypass); + if (ret) + return ret; + + bq->state.bypass = en_bypass; + + return 0; +} +*/ + +static int bq25980_set_chg_en(struct bq25980_device *bq, bool en_chg) +{ + int ret; + + if (en_chg) + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_CHG_EN, BQ25980_CHG_EN); + else + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_CHG_EN, en_chg); + if (ret) + return ret; + + bq->state.ce = en_chg; + + return 0; +} + +static int bq25980_is_chg_en(struct bq25980_device *bq, bool *en_chg) +{ + int ret; + unsigned int chg_ctrl_2; + unsigned int stat5; + + ret = regmap_read(bq->regmap, BQ25980_CHRGR_CTRL_2, &chg_ctrl_2); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT5, &stat5); + if (ret) + return ret; + + *en_chg = (!!(chg_ctrl_2 & BQ25980_CHG_EN) & + !!(stat5 & BQ25980_SWITCHING_STAT)); + + return 0; +} + +#ifdef CONFIG_MMI_BQ25980_BULK_READ +static int bq25980_bulk_get_adc(struct bq25980_device *bq, int reg, uint8_t *val, int len) +{ + int ret; + + ret = regmap_bulk_read(bq->regmap, reg, val, len); + if (ret < 0) { + dev_err(bq->dev, "bq25980 read %02x block failed %d\n", reg, ret); + } + + return ret; +} + +static int bq25980_bulk_get_vbus(struct bq25980_device *bq) +{ + u8 val[2] = {0}; + int ret; + u16 vbus_adc; + + ret = bq25980_bulk_get_adc(bq, BQ25980_VBUS_ADC_MSB, val, 2); + if (ret) { + return ret; + } + + vbus_adc = (val[0] << 8) | val[1]; + return (bq->chip_info->adc_vbus_volt_offset + vbus_adc * bq->chip_info->adc_vbus_volt_step /10) /1000;//mV +} + +static int bq25980_bulk_get_ibus(struct bq25980_device *bq) +{ + u8 val[2] = {0}; + u16 ibus_adc; + int ret; + + ret = bq25980_bulk_get_adc(bq, BQ25980_IBUS_ADC_MSB, val, 2); + if (ret) { + return ret; + } + + ibus_adc = (val[0] << 8) | val[1]; + + if (val[0] & BQ25980_ADC_POLARITY_BIT) + return (((ibus_adc ^ 0xffff) + 1) * bq->chip_info->adc_curr_step) /1000;//mA + + return (ibus_adc * bq->chip_info->adc_curr_step) /1000; //mA +} + +static int bq25980_bulk_get_vbat(struct bq25980_device *bq) +{ + u8 val[2] = {0}; + u16 vsys_adc; + int ret; + + ret = bq25980_bulk_get_adc(bq, BQ25980_VBAT_ADC_MSB, val, 2); + if (ret) { + return ret; + } + + vsys_adc = (val[0] << 8) | val[1]; + + return (vsys_adc * bq->chip_info->adc_vbat_volt_step / 10) /1000;//mV +} + +static int bq25980_bulk_get_ibat(struct bq25980_device *bq) +{ + int ret; + u8 val[2] = {0}; + int ibat_adc; + + ret = bq25980_bulk_get_adc(bq, BQ25980_IBAT_ADC_MSB, val, 2); + if (ret) { + return ret; + } + + ibat_adc = (val[0] << 8) | val[1]; + + if (val[0] & BQ25980_ADC_POLARITY_BIT) + return (((ibat_adc ^ 0xffff) + 1) * BQ25960_ADC_CURR_STEP_uA) /1000;//mA + + return (ibat_adc * BQ25960_ADC_CURR_STEP_uA) /1000; //mA + +} +#else +static int bq25980_get_adc_ibus(struct bq25980_device *bq) +{ + int ibus_adc_lsb, ibus_adc_msb; + u16 ibus_adc; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_IBUS_ADC_MSB, &ibus_adc_msb); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_IBUS_ADC_LSB, &ibus_adc_lsb); + if (ret) + return ret; + + ibus_adc = (ibus_adc_msb << 8) | ibus_adc_lsb; + + if (ibus_adc_msb & BQ25980_ADC_POLARITY_BIT) + return (((ibus_adc ^ 0xffff) + 1) * bq->chip_info->adc_curr_step) /1000;//mA + + return (ibus_adc * bq->chip_info->adc_curr_step) /1000; //mA +} + +static int bq25980_get_adc_vbus(struct bq25980_device *bq) +{ + int vbus_adc_lsb, vbus_adc_msb; + u16 vbus_adc; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_VBUS_ADC_MSB, &vbus_adc_msb); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_VBUS_ADC_LSB, &vbus_adc_lsb); + if (ret) + return ret; + + vbus_adc = (vbus_adc_msb << 8) | vbus_adc_lsb; + + return (bq->chip_info->adc_vbus_volt_offset + vbus_adc * bq->chip_info->adc_vbus_volt_step /10) /1000;//mV +} + +static int bq25980_get_ibat_adc(struct bq25980_device *bq) +{ + int ret; + int ibat_adc_lsb, ibat_adc_msb; + int ibat_adc; + + ret = regmap_read(bq->regmap, BQ25980_IBAT_ADC_MSB, &ibat_adc_msb); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_IBAT_ADC_LSB, &ibat_adc_lsb); + if (ret) + return ret; + + ibat_adc = (ibat_adc_msb << 8) | ibat_adc_lsb; + + if (ibat_adc_msb & BQ25980_ADC_POLARITY_BIT) + return (((ibat_adc ^ 0xffff) + 1) * BQ25960_ADC_CURR_STEP_uA) /1000;//mA + + return (ibat_adc * BQ25960_ADC_CURR_STEP_uA) /1000; //mA +} + +static int bq25980_get_adc_vbat(struct bq25980_device *bq) +{ + int vsys_adc_lsb, vsys_adc_msb; + u16 vsys_adc; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_VBAT_ADC_MSB, &vsys_adc_msb); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_VBAT_ADC_LSB, &vsys_adc_lsb); + if (ret) + return ret; + + vsys_adc = (vsys_adc_msb << 8) | vsys_adc_lsb; + + return (vsys_adc * bq->chip_info->adc_vbat_volt_step / 10) /1000;//mV +} +#endif + +static int bq25980_get_state(struct bq25980_device *bq, + struct bq25980_state *state) +{ + unsigned int chg_ctrl_2; + unsigned int stat1; + unsigned int stat2; + unsigned int stat3; + unsigned int stat4; + unsigned int stat5; + unsigned int ibat_adc_msb; + int ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT1, &stat1); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT2, &stat2); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT3, &stat3); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT4, &stat4); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT5, &stat5); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_CHRGR_CTRL_2, &chg_ctrl_2); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_IBAT_ADC_MSB, &ibat_adc_msb); + if (ret) + return ret; + + state->dischg = ibat_adc_msb & BQ25980_ADC_POLARITY_BIT; + state->ovp = (stat1 & BQ25980_STAT1_OVP_MASK) | + (stat3 & BQ25980_STAT3_OVP_MASK); + state->ocp = (stat1 & BQ25980_STAT1_OCP_MASK) | + (stat2 & BQ25980_STAT2_OCP_MASK); + state->tflt = stat4 & BQ25980_STAT4_TFLT_MASK; + state->wdt = stat4 & BQ25980_WD_STAT; + state->online = stat3 & BQ25980_PRESENT_MASK; + state->ce = chg_ctrl_2 & BQ25980_CHG_EN; + state->hiz = chg_ctrl_2 & BQ25980_EN_HIZ; + state->bypass = chg_ctrl_2 & BQ25980_EN_BYPASS; + state->cp_switch = stat5 & BQ25980_STAT5_CP_SWITCH_MASK; + + bq->alarm_status.bits.bat_ovp_alarm = stat1 & BQ25980_STAT1_BAT_OVP_ALM_MASK; + bq->alarm_status.bits.bat_ocp_alarm = stat1 & BQ25980_STAT1_BAT_OCP_ALM_MASK; + bq->alarm_status.bits.bus_ovp_alarm = stat1 & BQ25980_STAT1_BUS_OVP_ALM_MASK; + bq->alarm_status.bits.bus_ocp_alarm = stat2 & BQ25980_STAT2_BUS_OCP_ALM_MASK; + bq->alarm_status.bits.bat_therm_alarm = stat4 & BQ25980_STAT4_TSBUS_TSBAT_ALM_MASK; + bq->alarm_status.bits.bus_therm_alarm = stat4 & BQ25980_STAT4_TSBUS_TSBAT_ALM_MASK; + bq->alarm_status.bits.die_therm_alarm = stat4 & BQ25980_STAT4_TDIE_ALM_MASK; + bq->alarm_status.bits.bat_ucp_alarm = stat1 & BQ25980_STAT1_BAT_UCP_ALM_MASK; + + bq->fault_status.bits.bat_ovp_fault = stat1 & BQ25980_STAT1_BAT_OVP_MASK; + bq->fault_status.bits.bat_ocp_fault = stat1 & BQ25980_STAT1_BAT_OCP_MASK; + bq->fault_status.bits.bus_ovp_fault = stat1 & BQ25980_STAT1_BUS_OVP_MASK; + bq->fault_status.bits.bus_ocp_fault = stat2 & BQ25980_STAT2_BUS_OCP_MASK; + bq->fault_status.bits.bat_therm_fault = stat4 & BQ25980_STAT4_TSBAT_FLT_MASK; + bq->fault_status.bits.bus_therm_fault = stat4 & BQ25980_STAT4_TSBUS_FLT_MASK; + bq->fault_status.bits.die_therm_fault = stat4 & BQ25980_STAT4_TDIE_FLT_MASK; + + return 0; +} +#if 0 +static int bq25980_set_battery_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct bq25980_device *bq = power_supply_get_drvdata(psy); + int ret = 0; + + if (ret) + return ret; + + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = bq25980_set_const_charge_curr(bq, val->intval); + if (ret) + return ret; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = bq25980_set_const_charge_volt(bq, val->intval); + if (ret) + return ret; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int bq25980_get_battery_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct bq25980_device *bq = power_supply_get_drvdata(psy); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + val->intval = bq->init_data.ichg_max; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + val->intval = bq->init_data.vreg_max; + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = bq25980_get_ibat_adc(bq); + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = bq25980_get_adc_vbat(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; + + default: + return -EINVAL; + } + + return 0; +} +#endif +static int bq25980_set_present(struct bq25980_device *bq, bool present) +{ + bq->usb_present = present; + + if (present) + bq25980_reg_init(bq); + return 0; +} + +static int bq25980_set_charger_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct bq25980_device *bq = power_supply_get_drvdata(psy); + int ret = -EINVAL; + + switch (prop) { +/* + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = bq25980_set_input_curr_lim(bq, val->intval); + if (ret) + return ret; + break; + + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = bq25980_set_input_volt_lim(bq, val->intval); + if (ret) + return ret; + break; +*/ +/* case POWER_SUPPLY_PROP_CHARGING_ENABLED: + dev_err(bq->dev,"%s,POWER_SUPPLY_PROP_CHARGING_ENABLED prop:%d,value:%d\n",__func__,prop,val->intval); + ret = bq25980_set_chg_en(bq, val->intval); + if (ret) + return ret; + break; +*/ +/* + case POWER_SUPPLY_PROP_TI_ADC: + ret = bq25980_set_adc_enable(bq, val->intval); + if (ret) + return ret; + break; + + case POWER_SUPPLY_PROP_TI_BYPASS: + ret = bq25980_set_bypass(bq, val->intval); + if (ret) + return ret; + if (val->intval) + ret = bq25980_set_input_curr_lim(bq, bq->chip_info->busocp_byp_def); + else + ret = bq25980_set_input_curr_lim(bq, bq->chip_info->busocp_sc_def); + if (ret) + return ret; + break; +*/ + case POWER_SUPPLY_PROP_PRESENT: + ret = bq25980_set_present(bq, !!val->intval); + pr_info("[%s] set present :%d ret=%d\n", bq->model_name, val->intval,ret); + //dev_err(bq->dev,"%s,POWER_SUPPLY_PROP_PRESENT prop:%d,value:%d\n",__func__,prop,val->intval); + break; +/* case POWER_SUPPLY_PROP_UPDATE_NOW: + dev_err(bq->dev,"%s,POWER_SUPPLY_PROP_UPDATE_NOW prop:%d,value:%d\n",__func__,prop,val->intval); + break; +*/ + default: + return -EINVAL; + } + + return 0; +} + +static int bq25980_get_charger_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct bq25980_device *bq = power_supply_get_drvdata(psy); + struct bq25980_state state; + int ret = 0; + //unsigned int chg_ctrl_2 = 0; + unsigned int stat1 = 0; + unsigned int stat2 = 0; + unsigned int stat3 = 0; + unsigned int stat4 = 0; + + switch (psp) { + //case POWER_SUPPLY_PROP_CHIP_VERSION: + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = BQ25980_MANUFACTURER; + break; + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = bq->model_name; + break; +/* case POWER_SUPPLY_PROP_ONLINE: + ret = regmap_read(bq->regmap, BQ25980_STAT3, &stat3); + if (ret) + return ret; + + state.online = stat3 & BQ25980_PRESENT_MASK; + val->intval = state.online; + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = bq25980_get_input_volt_lim(bq); + if (ret < 0) + return ret; + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = bq25980_get_input_curr_lim(bq); + if (ret < 0) + return ret; + val->intval = ret; + break; +*/ +/* case POWER_SUPPLY_PROP_CP_STATUS1: + val->intval = 0; + ((sc->bat_ovp_fault << SC8549_VBAT_OVP_FLAG_SHIFT) + | (sc->bat_ocp_fault << SC8549_IBAT_OCP_FLAG_SHIFT) + | (sc->bus_ovp_fault << SC8549_VBUS_OVP_FLAG_SHIFT) + | (sc->bus_ocp_fault << SC8549_IBUS_OCP_FLAG_SHIFT)); + break; +*/ + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + + ret = regmap_read(bq->regmap, BQ25980_STAT1, &stat1); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT2, &stat2); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT3, &stat3); + if (ret) + return ret; + + ret = regmap_read(bq->regmap, BQ25980_STAT4, &stat4); + if (ret) + return ret; + + state.ovp = (stat1 & BQ25980_STAT1_OVP_MASK) | + (stat3 & BQ25980_STAT3_OVP_MASK); + state.ocp = (stat1 & BQ25980_STAT1_OCP_MASK) | + (stat2 & BQ25980_STAT2_OCP_MASK); + state.tflt = stat4 & BQ25980_STAT4_TFLT_MASK; + + if (state.tflt) + val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; + else if (state.ovp) + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + else if (state.ocp) + val->intval = POWER_SUPPLY_HEALTH_OVERCURRENT; + + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: +#ifdef CONFIG_MMI_BQ25980_BULK_READ + ret = bq25980_bulk_get_ibat(bq); +#else + ret = bq25980_get_ibat_adc(bq); +#endif + if (ret < 0) + return ret; + + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: +#ifdef CONFIG_MMI_BQ25980_BULK_READ + ret = bq25980_bulk_get_vbat(bq); +#else + ret = bq25980_get_adc_vbat(bq); +#endif + if (ret < 0) + return ret; + + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = bq25980_get_const_charge_curr(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + ret = bq25980_get_const_charge_volt(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; +#ifndef CONFIG_MMI_BQ25980_BULK_READ + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = bq25980_get_adc_vbus(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; +#endif + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: +#ifdef CONFIG_MMI_BQ25980_BULK_READ + ret = bq25980_bulk_get_ibus(bq); +#else + ret = bq25980_get_adc_ibus(bq); +#endif + if (ret < 0) + return ret; + + val->intval = ret; + //dump_all_reg(bq); + break; + +/* case POWER_SUPPLY_PROP_CHARGING_ENABLED://undeclared identifier + ret = regmap_read(bq->regmap, BQ25980_CHRGR_CTRL_2, &chg_ctrl_2); + if (ret) + return ret; + + state.ce = chg_ctrl_2 & BQ25980_CHG_EN; + val->intval = state.ce; + break; +*/ +/* + case POWER_SUPPLY_PROP_TI_ADC: + ret = bq25980_get_adc_enable(bq); + if (ret < 0) + return ret; + + val->intval = ret; + break; + + case POWER_SUPPLY_PROP_TI_BYPASS: + ret = regmap_read(bq->regmap, BQ25980_CHRGR_CTRL_2, &chg_ctrl_2); + if (ret) + return ret; + + state.bypass = chg_ctrl_2 & BQ25980_EN_BYPASS; + val->intval = state.bypass; + break; +*/ + case POWER_SUPPLY_PROP_PRESENT: + val->intval = bq->state.online; + break; +// case POWER_SUPPLY_PROP_UPDATE_NOW: +// break; +/* case POWER_SUPPLY_PROP_CP_IRQ_STATUS: + val->intval = bq->irq_counts; + break; +*/ + default: + return -EINVAL; + } +// dev_err(bq->dev,"%s,psp:%d,value:%d\n",__func__,psp,val->intval); + return 0; +} + +static bool bq25980_state_changed(struct bq25980_device *bq, + struct bq25980_state *new_state) +{ + struct bq25980_state old_state; + + mutex_lock(&bq->lock); + old_state = bq->state; + mutex_unlock(&bq->lock); + + return (old_state.dischg != new_state->dischg || + old_state.ovp != new_state->ovp || + old_state.ocp != new_state->ocp || + old_state.online != new_state->online || + old_state.wdt != new_state->wdt || + old_state.tflt != new_state->tflt || + old_state.ce != new_state->ce || + old_state.hiz != new_state->hiz || + old_state.bypass != new_state->bypass || + old_state.cp_switch != new_state->cp_switch); +} + +static irqreturn_t bq25980_irq_handler_thread(int irq, void *private) +{ + struct bq25980_device *bq = private; + struct bq25980_state state; + int ret; + + dev_err(bq->dev,"[%s]%s enter\n",bq->model_name,__func__); + mutex_lock(&bq->irq_complete); + bq->irq_waiting = true; + if (!bq->resume_completed) { + dev_dbg(bq->dev, "IRQ triggered before device-resume\n"); + if (!bq->irq_disabled) { + disable_irq_nosync(irq); + bq->irq_disabled = true; + } + mutex_unlock(&bq->irq_complete); + return IRQ_HANDLED; + } + bq->irq_waiting = false; + + if(bq->irq_counts > INT_MAX -1) + bq->irq_counts = 0; + else + bq->irq_counts++; + + + mutex_unlock(&bq->irq_complete); + + ret = bq25980_get_state(bq, &state); + if (ret < 0) + goto irq_out; + + if(bq->alarm_status.status > 0 || + bq->fault_status.status > 0) + dump_all_reg(bq); + if (!bq25980_state_changed(bq, &state)) + goto irq_out; + + mutex_lock(&bq->lock); + bq->state = state; + mutex_unlock(&bq->lock); + + power_supply_changed(bq->charger); + +irq_out: + return IRQ_HANDLED; +} + +static enum power_supply_property bq25980_power_supply_props[] = { + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_MODEL_NAME, + //POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_HEALTH, +// POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, +// POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +// POWER_SUPPLY_PROP_CHARGING_ENABLED, +/* + POWER_SUPPLY_PROP_TI_ADC, + POWER_SUPPLY_PROP_TI_BYPASS, +*/ + + //POWER_SUPPLY_PROP_CP_STATUS1,// + POWER_SUPPLY_PROP_PRESENT, +// POWER_SUPPLY_PROP_CHARGING_ENABLED,//undeclared identifier +// POWER_SUPPLY_PROP_VOLTAGE_NOW, +// POWER_SUPPLY_PROP_CURRENT_NOW, +#ifndef CONFIG_MMI_BQ25980_BULK_READ + POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, +#endif + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + //POWER_SUPPLY_PROP_CP_IRQ_STATUS,//undeclared identifier + //POWER_SUPPLY_PROP_CHIP_VERSION, +}; +#if 0 +static enum power_supply_property bq25980_battery_props[] = { + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +}; +#endif +static char *bq25980_charger_supplied_to[] = { + "main-battery", +}; + +static int bq25980_property_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + switch (prop) { +// case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: +// case POWER_SUPPLY_PROP_CHARGING_ENABLED: +// case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + case POWER_SUPPLY_PROP_PRESENT: +// case POWER_SUPPLY_PROP_UPDATE_NOW: +// case POWER_SUPPLY_PROP_TI_ADC: +// case POWER_SUPPLY_PROP_TI_BYPASS: + return true; + default: + return false; + } +} + +#if 0 +static struct power_supply_desc bq25980_battery_desc = { + .name = "bq25980-battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = bq25980_get_battry_property, + .set_property = bq25980_set_battery_property, + .properties = bq25980_battery_props, + .num_properties = ARRAY_SIZE(bq25980_battery_props), + .property_is_writeable = bq25980_property_is_writeable, +}; +#endif + +static bool bq25980_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case BQ25980_CHRGR_CTRL_2: + case BQ25980_STAT1...BQ25980_FLAG5: + case BQ25980_ADC_CONTROL1...BQ25980_TDIE_ADC_LSB: + return true; + default: + return false; + } +} + +static const struct regmap_config bq25980_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BQ25980_CHRGR_CTRL_6, + .reg_defaults = bq25980_reg_defs, + .num_reg_defaults = ARRAY_SIZE(bq25980_reg_defs), + .cache_type = REGCACHE_NONE, + .volatile_reg = bq25980_is_volatile_reg, +}; + +static const struct regmap_config bq25975_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BQ25980_CHRGR_CTRL_6, + .reg_defaults = bq25975_reg_defs, + .num_reg_defaults = ARRAY_SIZE(bq25975_reg_defs), + .cache_type = REGCACHE_NONE, + .volatile_reg = bq25980_is_volatile_reg, +}; + +static const struct regmap_config bq25960_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = BQ25980_CHRGR_CTRL_6, + .reg_defaults = bq25960_reg_defs, + .num_reg_defaults = ARRAY_SIZE(bq25960_reg_defs), + .cache_type = REGCACHE_NONE, + .volatile_reg = bq25980_is_volatile_reg, +}; + +static const struct regmap_config sc8541_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = SC8541_CTRL6_REG, + .reg_defaults = sc8541_reg_defs, + .num_reg_defaults = ARRAY_SIZE(sc8541_reg_defs), + .cache_type = REGCACHE_NONE, + .volatile_reg = bq25980_is_volatile_reg, +}; + + +static const struct bq25980_chip_info sc8541_chip_info_tbl[] = { + [BQ25960] = { + .model_id = BQ25960, + .regmap_config = &sc8541_regmap_config, + .reg_init_values = sc8541_reg_init_val, + + .busocp_sc_def = BQ25960_BUSOCP_SC_DFLT_uA, + .busocp_byp_def = BQ25960_BUSOCP_BYP_DFLT_uA, + .busocp_sc_min = BQ25960_BUSOCP_MIN_uA, + .busocp_sc_max = BQ25960_BUSOCP_SC_MAX_uA, + .busocp_byp_min = BQ25960_BUSOCP_MIN_uA, + .busocp_byp_max = BQ25960_BUSOCP_BYP_MAX_uA, + .busocp_step = BQ25960_BUSOCP_STEP_uA, + .busocp_offset = BQ25960_BUSOCP_OFFSET_uA, + + .busovp_sc_def = BQ25975_BUSOVP_DFLT_uV, + .busovp_byp_def = BQ25975_BUSOVP_BYPASS_DFLT_uV, + .busovp_sc_step = BQ25960_BUSOVP_SC_STEP_uV, + .busovp_sc_offset = BQ25960_BUSOVP_SC_OFFSET_uV, + .busovp_byp_step = BQ25960_BUSOVP_BYP_STEP_uV, + .busovp_byp_offset = BQ25960_BUSOVP_BYP_OFFSET_uV, + .busovp_sc_min = BQ25960_BUSOVP_SC_MIN_uV, + .busovp_sc_max = BQ25960_BUSOVP_SC_MAX_uV, + .busovp_byp_min = BQ25960_BUSOVP_BYP_MIN_uV, + .busovp_byp_max = BQ25960_BUSOVP_BYP_MAX_uV, + + .batovp_def = SC8545_BATOVP_DFLT_uV, + .batovp_max = SC8545_BATOVP_MAX_uV, + .batovp_min = SC8545_BATOVP_MIN_uV, + .batovp_step = SC8545_BATOVP_STEP_uV, + .batovp_offset = SC8545_BATOVP_OFFSET_uV, + + .batocp_def = SC8545_BATOCP_DFLT_uA, + .batocp_max = SC8545_BATOCP_MAX_uA, + + .adc_curr_step = SC8541_ADC_CURR_STEP_IBUS_uA, + .adc_vbat_volt_step = SC8541_ADC_VOLT_STEP_VBAT_deciuV, + .adc_vbus_volt_step = SC8541_ADC_VOLT_STEP_VBUS_deciuV, + .adc_vbus_volt_offset = 0, + .adc_vout_volt_step = SC8541_ADC_VOLT_STEP_VOUT_deciuV, + .adc_vout_volt_offset = 0, + }, +}; + +static const struct bq25980_chip_info bq25980_chip_info_tbl[] = { + [BQ25980] = { + .model_id = BQ25980, + .regmap_config = &bq25980_regmap_config, + .reg_init_values = bq25980_reg_init_val, + + .busocp_sc_def = BQ25980_BUSOCP_SC_DFLT_uA, + .busocp_byp_def = BQ25980_BUSOCP_BYP_DFLT_uA, + .busocp_sc_min = BQ25980_BUSOCP_MIN_uA, + .busocp_sc_max = BQ25980_BUSOCP_SC_MAX_uA, + .busocp_byp_max = BQ25980_BUSOCP_BYP_MAX_uA, + .busocp_byp_min = BQ25980_BUSOCP_MIN_uA, + .busocp_step = BQ25980_BUSOCP_STEP_uA, + .busocp_offset = BQ25980_BUSOCP_OFFSET_uA, + + .busovp_sc_def = BQ25980_BUSOVP_DFLT_uV, + .busovp_byp_def = BQ25980_BUSOVP_BYPASS_DFLT_uV, + .busovp_sc_step = BQ25980_BUSOVP_SC_STEP_uV, + .busovp_sc_offset = BQ25980_BUSOVP_SC_OFFSET_uV, + .busovp_byp_step = BQ25980_BUSOVP_BYP_STEP_uV, + .busovp_byp_offset = BQ25980_BUSOVP_BYP_OFFSET_uV, + .busovp_sc_min = BQ25980_BUSOVP_SC_MIN_uV, + .busovp_sc_max = BQ25980_BUSOVP_SC_MAX_uV, + .busovp_byp_min = BQ25980_BUSOVP_BYP_MIN_uV, + .busovp_byp_max = BQ25980_BUSOVP_BYP_MAX_uV, + + .batovp_def = BQ25980_BATOVP_DFLT_uV, + .batovp_max = BQ25980_BATOVP_MAX_uV, + .batovp_min = BQ25980_BATOVP_MIN_uV, + .batovp_step = BQ25980_BATOVP_STEP_uV, + .batovp_offset = BQ25980_BATOVP_OFFSET_uV, + + .batocp_def = BQ25980_BATOCP_DFLT_uA, + .batocp_max = BQ25980_BATOCP_MAX_uA, + + .adc_curr_step = BQ25980_ADC_CURR_STEP_IBUS_uA, + .adc_vbat_volt_step = BQ25980_ADC_VOLT_STEP_VBAT_deciuV, + .adc_vbus_volt_step = BQ25980_ADC_VOLT_STEP_VBUS_deciuV, + .adc_vbus_volt_offset = BQ25980_ADC_VOLT_OFFSET_VBUS, + }, + + [BQ25960] = { + .model_id = BQ25960, + .regmap_config = &bq25960_regmap_config, + .reg_init_values = bq25960_reg_init_val, + + .busocp_sc_def = BQ25960_BUSOCP_SC_DFLT_uA, + .busocp_byp_def = BQ25960_BUSOCP_BYP_DFLT_uA, + .busocp_sc_min = BQ25960_BUSOCP_MIN_uA, + .busocp_sc_max = BQ25960_BUSOCP_SC_MAX_uA, + .busocp_byp_min = BQ25960_BUSOCP_MIN_uA, + .busocp_byp_max = BQ25960_BUSOCP_BYP_MAX_uA, + .busocp_step = BQ25960_BUSOCP_STEP_uA, + .busocp_offset = BQ25960_BUSOCP_OFFSET_uA, + + .busovp_sc_def = BQ25975_BUSOVP_DFLT_uV, + .busovp_byp_def = BQ25975_BUSOVP_BYPASS_DFLT_uV, + .busovp_sc_step = BQ25960_BUSOVP_SC_STEP_uV, + .busovp_sc_offset = BQ25960_BUSOVP_SC_OFFSET_uV, + .busovp_byp_step = BQ25960_BUSOVP_BYP_STEP_uV, + .busovp_byp_offset = BQ25960_BUSOVP_BYP_OFFSET_uV, + .busovp_sc_min = BQ25960_BUSOVP_SC_MIN_uV, + .busovp_sc_max = BQ25960_BUSOVP_SC_MAX_uV, + .busovp_byp_min = BQ25960_BUSOVP_BYP_MIN_uV, + .busovp_byp_max = BQ25960_BUSOVP_BYP_MAX_uV, + + .batovp_def = BQ25960_BATOVP_DFLT_uV, + .batovp_max = BQ25960_BATOVP_MAX_uV, + .batovp_min = BQ25960_BATOVP_MIN_uV, + .batovp_step = BQ25960_BATOVP_STEP_uV, + .batovp_offset = BQ25960_BATOVP_OFFSET_uV, + + .batocp_def = BQ25960_BATOCP_DFLT_uA, + .batocp_max = BQ25960_BATOCP_MAX_uA, + + .adc_curr_step = BQ25960_ADC_CURR_STEP_uA, + .adc_vbat_volt_step = BQ25960_ADC_VOLT_STEP_deciuV, + .adc_vbus_volt_step = BQ25960_ADC_VOLT_STEP_deciuV, + .adc_vbus_volt_offset = 0, + }, + + [BQ25975] = { + .model_id = BQ25975, + .regmap_config = &bq25975_regmap_config, + + .busocp_sc_def = BQ25975_BUSOCP_DFLT_uA, + .busocp_byp_def = BQ25975_BUSOCP_DFLT_uA, + .busocp_sc_min = BQ25960_BUSOCP_MIN_uA, + .busocp_sc_max = BQ25975_BUSOCP_SC_MAX_uA, + .busocp_byp_min = BQ25960_BUSOCP_MIN_uA, + .busocp_byp_max = BQ25975_BUSOCP_BYP_MAX_uA, + .busocp_step = BQ25960_BUSOCP_STEP_uA, + .busocp_offset = BQ25960_BUSOCP_OFFSET_uA, + + .busovp_sc_def = BQ25975_BUSOVP_DFLT_uV, + .busovp_byp_def = BQ25975_BUSOVP_BYPASS_DFLT_uV, + .busovp_sc_step = BQ25975_BUSOVP_SC_STEP_uV, + .busovp_sc_offset = BQ25975_BUSOVP_SC_OFFSET_uV, + .busovp_byp_step = BQ25975_BUSOVP_BYP_STEP_uV, + .busovp_byp_offset = BQ25975_BUSOVP_BYP_OFFSET_uV, + .busovp_sc_min = BQ25975_BUSOVP_SC_MIN_uV, + .busovp_sc_max = BQ25975_BUSOVP_SC_MAX_uV, + .busovp_byp_min = BQ25975_BUSOVP_BYP_MIN_uV, + .busovp_byp_max = BQ25975_BUSOVP_BYP_MAX_uV, + + .batovp_def = BQ25975_BATOVP_DFLT_uV, + .batovp_max = BQ25975_BATOVP_MAX_uV, + .batovp_min = BQ25975_BATOVP_MIN_uV, + .batovp_step = BQ25975_BATOVP_STEP_uV, + .batovp_offset = BQ25975_BATOVP_OFFSET_uV, + + .batocp_def = BQ25980_BATOCP_DFLT_uA, + .batocp_max = BQ25980_BATOCP_MAX_uA, + + .adc_curr_step = BQ25960_ADC_CURR_STEP_uA, + .adc_vbat_volt_step = BQ25960_ADC_VOLT_STEP_deciuV, + .adc_vbus_volt_step = BQ25960_ADC_VOLT_STEP_deciuV, + .adc_vbus_volt_offset = 0, + }, + +}; + +static int bq25980_power_supply_init(struct bq25980_device *bq, + struct device *dev, + int driver_data) +{ + struct power_supply_config psy_cfg = { .drv_data = bq, + .of_node = dev->of_node, }; + + psy_cfg.supplied_to = bq25980_charger_supplied_to; + psy_cfg.num_supplicants = ARRAY_SIZE(bq25980_charger_supplied_to); + + switch (driver_data) { + case BQ25980_MASTER: + bq->psy_desc.name = "bq25980-master"; + break; + case BQ25980_SLAVE: + bq->psy_desc.name = "bq25980-slave"; + break; + case BQ25980_STANDALONE: + bq->psy_desc.name = "bq25980-standalone"; + break; + case BQ25960_MASTER: + bq->psy_desc.name = "bq25960-master"; + break; + case BQ25960_SLAVE: + bq->psy_desc.name = "bq25960-slave"; + break; + case BQ25960_STANDALONE: + bq->psy_desc.name = "bq25960-standalone"; + break; + default: + return -EINVAL; + } + + bq->psy_desc.type = POWER_SUPPLY_TYPE_MAINS, + bq->psy_desc.properties = bq25980_power_supply_props, + bq->psy_desc.num_properties = ARRAY_SIZE(bq25980_power_supply_props), + bq->psy_desc.get_property = bq25980_get_charger_property, + bq->psy_desc.set_property = bq25980_set_charger_property, + bq->psy_desc.property_is_writeable = bq25980_property_is_writeable, + + bq->charger = devm_power_supply_register(bq->dev, + &bq->psy_desc, + &psy_cfg); + if (IS_ERR(bq->charger)) { + dev_err(bq->dev, "bq register power supply fail"); + return -EINVAL; + } +/* + if (bq->mode != BQ_SLAVE) { + bq->battery = devm_power_supply_register(bq->dev, + &bq25980_battery_desc, + &psy_cfg); + if (IS_ERR(bq->battery)) { + dev_err(bq->dev, "battery register power supply fail"); + return -EINVAL; + } + } +*/ + return 0; +} + +static int bq25980_reg_init(struct bq25980_device *bq) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(bq25980_reg_init_val); i++) { + ret = regmap_update_bits(bq->regmap, bq->chip_info->reg_init_values[i].reg, + 0xFF, bq->chip_info->reg_init_values[i].def); + dev_notice(bq->dev, "init Reg[%02X] = 0x%02X\n", + bq->chip_info->reg_init_values[i].reg, + bq->chip_info->reg_init_values[i].def); + if (ret) + { + dev_err(bq->dev, "Reg init fail ret=%d", ret); + return ret; + } + } + return 0; +} +#if 0 +static int bq25980_hw_init(struct bq25980_device *bq) +{ + struct power_supply_battery_info bat_info = { }; + int wd_reg_val; + int ret = 0; + int curr_val; + int volt_val; + int i; + + if (!bq->watchdog_timer) { + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_3, + BQ25980_WATCHDOG_DIS, + BQ25980_WATCHDOG_DIS); + } else { + for (i = 0; i < BQ25980_NUM_WD_VAL; i++) { + if (bq->watchdog_timer > bq25980_watchdog_time[i] && + bq->watchdog_timer < bq25980_watchdog_time[i + 1]) { + wd_reg_val = i; + break; + } + } + + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_3, + BQ25980_WATCHDOG_MASK, wd_reg_val); + } + if (ret) + return ret; + + ret = power_supply_get_battery_info(bq->charger, &bat_info); + if (ret) { + dev_warn(bq->dev, "battery info missing\n"); + return -EINVAL; + } + + bq->init_data.ichg_max = bat_info.constant_charge_current_max_ua; + bq->init_data.vreg_max = bat_info.constant_charge_voltage_max_uv; + + if (bq->state.bypass) { + ret = regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_EN_BYPASS, BQ25980_EN_BYPASS); + if (ret) + return ret; + + curr_val = bq->init_data.bypass_ilim; + volt_val = bq->init_data.bypass_vlim; + } else { + curr_val = bq->init_data.sc_ilim; + volt_val = bq->init_data.sc_vlim; + } + + ret = bq25980_set_input_curr_lim(bq, curr_val); + if (ret) + return ret; + + ret = bq25980_set_input_volt_lim(bq, volt_val); + if (ret) + return ret; + + return regmap_update_bits(bq->regmap, BQ25980_ADC_CONTROL1, + BQ25980_ADC_EN, BQ25980_ADC_EN); +} +#endif +static int bq25980_parse_dt(struct bq25980_device *bq) +{ + int ret; + + ret = device_property_read_u32(bq->dev, "ti,watchdog-timeout-ms", + &bq->watchdog_timer); + if (ret) + bq->watchdog_timer = BQ25980_WATCHDOG_MIN; + + if (bq->watchdog_timer > BQ25980_WATCHDOG_MAX || + bq->watchdog_timer < BQ25980_WATCHDOG_MIN) + return -EINVAL; + + ret = device_property_read_u32(bq->dev, + "ti,sc-ovp-limit-microvolt", + &bq->init_data.sc_vlim); + if (ret) + bq->init_data.sc_vlim = bq->chip_info->busovp_sc_def; + + if (bq->init_data.sc_vlim > bq->chip_info->busovp_sc_max || + bq->init_data.sc_vlim < bq->chip_info->busovp_sc_min) { + dev_err(bq->dev, "SC ovp limit is out of range\n"); + return -EINVAL; + } + + ret = device_property_read_u32(bq->dev, + "ti,sc-ocp-limit-microamp", + &bq->init_data.sc_ilim); + if (ret) + bq->init_data.sc_ilim = bq->chip_info->busocp_sc_def; + + if (bq->init_data.sc_ilim > bq->chip_info->busocp_sc_max || + bq->init_data.sc_ilim < bq->chip_info->busocp_sc_min) { + dev_err(bq->dev, "SC ocp limit is out of range\n"); + return -EINVAL; + } + + ret = device_property_read_u32(bq->dev, + "ti,bypass-ovp-limit-microvolt", + &bq->init_data.bypass_vlim); + if (ret) + bq->init_data.bypass_vlim = bq->chip_info->busovp_byp_def; + + if (bq->init_data.bypass_vlim > bq->chip_info->busovp_byp_max || + bq->init_data.bypass_vlim < bq->chip_info->busovp_byp_min) { + dev_err(bq->dev, "Bypass ovp limit is out of range\n"); + return -EINVAL; + } + + ret = device_property_read_u32(bq->dev, + "ti,bypass-ocp-limit-microamp", + &bq->init_data.bypass_ilim); + if (ret) + bq->init_data.bypass_ilim = bq->chip_info->busocp_byp_def; + + if (bq->init_data.bypass_ilim > bq->chip_info->busocp_byp_max || + bq->init_data.bypass_ilim < bq->chip_info->busocp_byp_min) { + dev_err(bq->dev, "Bypass ocp limit is out of range\n"); + return -EINVAL; + } + + + bq->state.bypass = device_property_read_bool(bq->dev, + "ti,bypass-enable"); + return 0; +} + +static int bq25980_check_work_mode(struct bq25980_device *bq) +{ + int ret; + int val; + + ret = regmap_read(bq->regmap, BQ25980_CHRGR_CTRL_5, &val); + if (ret) { + dev_err(bq->dev, "Failed to read operation mode register\n"); + return ret; + } + + val = (val & BQ25980_MS_MASK); + if (bq->mode != val) { + dev_err(bq->dev, "dts mode %d mismatch with hardware mode %d\n", bq->mode, val); + return -EINVAL; + } + + dev_info(bq->dev, "work mode:%s\n", bq->mode == BQ_STANDALONE ? "Standalone" : + (bq->mode == BQ_SLAVE ? "Slave" : "Master")); + return 0; +} +static int bq25890_get_part_no(struct bq25980_device *bq) +{ + struct i2c_client client; + int ret; + int len; + const char *sc8541_name; + + bq->part_no = 0; + ret = device_property_read_u32(bq->dev, "sc8541-addr", + &bq->sc8541_addr); + if (ret) + return BQ25960_PART_NO; + client = *(bq->client); + ret = i2c_smbus_read_byte_data(&client, BQ25980_DEVICE_INFO); + if(ret == BQ25960_PART_NO ) { + return BQ25960_PART_NO; //read success + }else { + pr_info("bq25890_get_part_no: orig addr = %d, sc8541 addr =%d\n ", + client.addr, bq->sc8541_addr); + client.addr = bq->sc8541_addr; + ret = i2c_smbus_read_byte_data(&client, BQ25980_DEVICE_INFO); + if(ret == SC8541_PART_NO) { + memset((void*)bq->model_name, 0x00, sizeof(bq->model_name)); + device_property_read_string(bq->dev, "sc8541-name", &sc8541_name); + len = strlen(sc8541_name); + strncpy(bq->model_name, sc8541_name, min(I2C_NAME_SIZE,len) ); + pr_err("[%s] model_name=%s\n", __func__ , bq->model_name); + } + } + + return ret; +} + +static int bq25980_check_device_id(struct bq25980_device *bq) +{ +#if 0 //IC device id not confirmed + int ret; + int val; + + ret = regmap_read(bq->regmap, BQ25980_DEVICE_INFO, &val); + if (ret) { + dev_err(bq->dev, "Failed to read device id\n"); + return ret; + } + + val = (val & BQ25980_DEVICE_ID_MASK); + if (bq->device_id != val) { + dev_err(bq->dev, "dts id %d mismatch with hardware id %d\n", bq->device_id, val); + return -EINVAL; + } +#endif + return 0; +} + +static ssize_t show_reg_addr(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct bq25980_device *bq = dev_get_drvdata(dev); + if (!bq) { + pr_err("[%s] chip not valid\n", bq->model_name); + return -ENODEV; + } + return sprintf(buf, "reg addr 0x%02x\n", bq->reg_addr); +} + +static ssize_t store_reg_addr(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int tmp; + struct bq25980_device *bq = dev_get_drvdata(dev); + if (!bq) { + pr_err("[%s] chip not valid\n", bq->model_name); + return -ENODEV; + } + + tmp = simple_strtoul(buf, NULL, 0); + bq->reg_addr = tmp; + + return count; +} +static DEVICE_ATTR(reg_addr, 0664, show_reg_addr, store_reg_addr); + + +static ssize_t show_reg_data(struct device *dev, struct device_attribute *attr, char *buf) +{ + int ret; + ssize_t size = 0; + struct bq25980_device *bq = dev_get_drvdata(dev); + if (!bq) { + pr_err("[%s] chip not valid\n", bq->model_name); + return -ENODEV; + } + if( ( bq->reg_addr >= 0 ) && (bq->reg_addr <= 0x37) ) + { + ret = regmap_read(bq->regmap, bq->reg_addr, &bq->reg_data); + size = sprintf(buf, "reg[%02X]=0x%02X\n", bq->reg_addr, bq->reg_data); + }else + { + size = sprintf(buf, "reg addr error\n"); + } + return size; +} + +static ssize_t store_reg_data(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int tmp; + struct bq25980_device *bq = dev_get_drvdata(dev); + if (!bq) { + pr_err("[%s] chip not valid\n", bq->model_name); + return -ENODEV; + } + + tmp = simple_strtoul(buf, NULL, 0); + bq->reg_data = tmp; + + if( (bq->reg_addr >= 0) && (bq->reg_addr <= 0x37) ) { + if( (bq->reg_data >= 0) && (bq->reg_data <= 0xFF) ) { + regmap_write(bq->regmap, bq->reg_addr, bq->reg_data); + }else + pr_err("reg data error : data=0x%X\n", bq->reg_data); + }else + pr_err("reg addr error : addr=0x%X\n", bq->reg_addr); + + return count; +} +static DEVICE_ATTR(reg_data, 0664, show_reg_data, store_reg_data); + +static ssize_t show_force_chg_auto_enable(struct device *dev, struct device_attribute *attr, char *buf) +{ + int ret; + int state = 0; + bool enable; + struct bq25980_device *bq = dev_get_drvdata(dev); + if (!bq) { + pr_err("[%s] chip not valid\n", bq->model_name); + state = -ENODEV; + }else { + ret = bq25980_is_chg_en(bq, &enable); + if (ret < 0) { + pr_err("[%s] bq25980_is_chg_en not valid\n", bq->model_name); + state = -ENODEV; + }else + state = enable; + } + return sprintf(buf, "%d\n", state); +} + +static ssize_t store_force_chg_auto_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + bool enable; + struct bq25980_device *bq = dev_get_drvdata(dev); + if (!bq) { + pr_err("[%s] chip not valid\n", bq->model_name); + return -ENODEV; + } + + enable = simple_strtoul(buf, NULL, 0); + ret = bq25980_set_chg_en(bq, enable); + if (ret) { + pr_err("[%s] Couldn't %s charging rc=%d\n", bq->model_name, + enable ? "enable" : "disable", (int)ret); + return ret; + } + + pr_info("[%s] %s charging \n", bq->model_name, + enable ? "enable" : "disable"); + + return count; +} +static DEVICE_ATTR(force_chg_auto_enable, 0664, show_force_chg_auto_enable, store_force_chg_auto_enable); + +static ssize_t show_reg_dump(struct device *dev, struct device_attribute *attr, char *buf) +{ + int ret; + unsigned int val; + int addr; + ssize_t size = 0; + struct bq25980_device *bq = dev_get_drvdata(dev); + if (!bq) { + pr_err("[%s] chip not valid\n", bq->model_name); + return -ENODEV; + } + + for (addr = 0; addr <= 0x3a; addr++) { + ret = regmap_read(bq->regmap, addr, &val); + if (!ret) { + pr_err("%s_dump_register Reg[%02X]=0x%02X\n", bq->model_name, addr, val); + size += snprintf(buf + size, PAGE_SIZE - size, + "reg[%02X]=[0x%02X]\n", addr,val); + }else { + size += snprintf(buf + size, PAGE_SIZE - size, + "reg[%02X]=[failed]\n", addr); + } + } + + return size; +} +static DEVICE_ATTR(reg_dump, 0444, show_reg_dump, NULL); + +static ssize_t show_vbus(struct device *dev, struct device_attribute *attr, char *buf) +{ + int vbus; + struct bq25980_device *bq = dev_get_drvdata(dev); + if (!bq) { + pr_err("[%s] chip not valid\n", bq->model_name); + return -ENODEV; + } +#ifdef CONFIG_MMI_BQ25980_BULK_READ + vbus = bq25980_bulk_get_vbus(bq); +#else + vbus = bq25980_get_adc_vbus(bq); +#endif + + return sprintf(buf, "%d\n", vbus); +} +static DEVICE_ATTR(vbus, 0444, show_vbus, NULL); + +static void bq25980_create_device_node(struct device *dev) +{ + device_create_file(dev, &dev_attr_force_chg_auto_enable); + device_create_file(dev, &dev_attr_reg_addr); + device_create_file(dev, &dev_attr_reg_data); + device_create_file(dev, &dev_attr_reg_dump); + device_create_file(dev, &dev_attr_vbus); +} + +static int bq25980_iio_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val1, + int val2, long mask) +{ + struct bq25980_device *bq = iio_priv(indio_dev); + int rc = 0; + + pr_err("[%s] iio_write_raw ch=%d val1=%d val2=%d\n", + bq->model_name, chan->channel, val1, val2); + switch (chan->channel) { + case PSY_IIO_CP_ENABLE: + bq25980_set_chg_en(bq, val1); + pr_info("[%s] set_chg_en: %s\n", bq->model_name, + val1 ? "enable" : "disable"); + break; + case PSY_IIO_ONLINE: + bq25980_set_present(bq, !!val1); + pr_info("[%s] set_present :%d\n", bq->model_name, val1); + break; + case PSY_IIO_CP_CLEAR_ERROR: + bq->fault_status.status = 0; + bq->alarm_status.status = 0; + break; + case PSY_IIO_CP_STATUS1: + if (val1 == MMI_DISABLE_ADC) + bq25980_set_adc_enable(bq, false); + else if (val1 == MMI_ENABLE_ADC) + bq25980_set_adc_enable(bq, true); + break; + default: + pr_err("Unsupported [%s] IIO chan %d\n", + bq->model_name, chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) + pr_err("[%s] Couldn't write IIO channel %d, rc = %d\n", + bq->model_name, + chan->channel, rc); + + return rc; +} + +static int bq25980_iio_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val1, + int *val2, long mask) +{ + struct bq25980_device *bq = iio_priv(indio_dev); + int rc = 0; + int result; + struct bq25980_state state; + + *val1 = 0; + pr_err("[%s] iio_read_raw ch=%d \n",bq->model_name,chan->channel); + switch (chan->channel) { + case PSY_IIO_CP_ENABLE: + rc = regmap_read(bq->regmap, BQ25980_CHRGR_CTRL_2, &result); + if (!rc) { + bq->state.ce = result & BQ25980_CHG_EN; + *val1 = bq->state.ce; + pr_info("[%s] read charge enable:%d\n", bq->model_name, *val1); + }else + pr_err("[%s] read charge enable err\n", bq->model_name); + break; + case PSY_IIO_ONLINE: + rc = regmap_read(bq->regmap, BQ25980_STAT3, &result); + if (!rc) { + bq->state.online = result & BQ25980_PRESENT_MASK; + *val1 = bq->state.online; + pr_info("[%s] read online:%d\n", bq->model_name, *val1); + }else + pr_err("[%s] read online err\n", bq->model_name); + break; + case PSY_IIO_MMI_CP_INPUT_VOLTAGE_NOW: +#ifdef CONFIG_MMI_BQ25980_BULK_READ + rc = bq25980_bulk_get_vbus(bq); +#else + rc = bq25980_get_adc_vbus(bq); +#endif + if ( rc < 0 ) { + pr_err("[%s] get_adc_vbus err %d\n", bq->model_name, rc); + }else { + *val1 = rc; + pr_info("[%s] get_adc_vbus %duV\n", bq->model_name, *val1); + } + break; + case PSY_IIO_MMI_CP_INPUT_CURRENT_NOW: +#ifdef CONFIG_MMI_BQ25980_BULK_READ + rc = bq25980_bulk_get_ibus(bq); +#else + rc = bq25980_get_adc_ibus(bq); +#endif + if ( rc < 0 ) { + pr_err("[%s] get_adc_ibus err %d\n", bq->model_name, rc); + }else { + *val1 = rc; + pr_info("[%s] get_adc_ibus %duA\n", bq->model_name, *val1); + } + break; + case PSY_IIO_CP_STATUS1: + rc = bq25980_get_state(bq,&state); + if (rc) { + pr_err("[%s] get_state err\n", bq->model_name); + return rc; + }else { + if (!bq25980_state_changed(bq, &state)){ + mutex_lock(&bq->lock); + bq->state = state; + mutex_unlock(&bq->lock); + power_supply_changed(bq->charger); + } + *val1 = bq->alarm_status.status | (bq->fault_status.status << 8) | (state.cp_switch << 18); + pr_err("[%s] get_state fault:0x%02X , alarm:0x%02X , cp_switch:%d\n", + bq->model_name, + bq->fault_status.status, + bq->alarm_status.status, + state.cp_switch); + } + break; + case PSY_IIO_CURRENT_NOW: +#ifdef CONFIG_MMI_BQ25980_BULK_READ + rc = bq25980_bulk_get_ibat(bq); +#else + rc = bq25980_get_ibat_adc(bq); +#endif + if ( rc < 0) { + pr_err("[%s] get_ibat_adc err %d\n", bq->model_name, rc); + }else { + *val1 = rc; + pr_info("[%s] get_ibat_adc %duA\n", bq->model_name, *val1); + } + break; + case PSY_IIO_VOLTAGE_NOW: +#ifdef CONFIG_MMI_BQ25980_BULK_READ + rc = bq25980_bulk_get_vbat(bq); +#else + rc = bq25980_get_adc_vbat(bq); +#endif + if ( rc < 0 ) { + pr_err("[%s] get_adc_vbat err %d\n", bq->model_name, rc); + }else { + *val1 = rc; + pr_info("[%s] get_adc_vbat %duV\n", bq->model_name, *val1); + } + break; + default: + pr_err("[%s] Unsupported bq25980 IIO chan %d\n", bq->model_name, chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) { + pr_err("[%s] Couldn't read IIO channel %d, rc = %d\n", bq->model_name, + chan->channel, rc); + return rc; + } + + return IIO_VAL_INT; +} + +static int bq25980_iio_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + struct bq25980_device *bq = iio_priv(indio_dev); + struct iio_chan_spec *iio_chan = bq->iio_chan; + int i; + + for (i = 0; i < ARRAY_SIZE(bq25980_iio_psy_channels); + i++, iio_chan++) + if (iio_chan->channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static const struct iio_info bq25980_iio_info = { + .read_raw = bq25980_iio_read_raw, + .write_raw = bq25980_iio_write_raw, + .of_xlate = bq25980_iio_of_xlate, +}; + +static int bq25980_init_iio_psy(struct bq25980_device *bq) +{ + struct iio_dev *indio_dev = bq->indio_dev; + struct iio_chan_spec *chan; + int bq25980_num_iio_channels = ARRAY_SIZE(bq25980_iio_psy_channels); + int rc, i; + + bq->iio_chan = devm_kcalloc(bq->dev, bq25980_num_iio_channels, + sizeof(*bq->iio_chan), GFP_KERNEL); + if (!bq->iio_chan) + return -ENOMEM; + + bq->int_iio_chans = devm_kcalloc(bq->dev, + bq25980_num_iio_channels, + sizeof(*bq->int_iio_chans), + GFP_KERNEL); + if (!bq->int_iio_chans) + return -ENOMEM; + + indio_dev->info = &bq25980_iio_info; + indio_dev->dev.parent = bq->dev; + indio_dev->dev.of_node = bq->dev->of_node; + indio_dev->name = bq->model_name;//"bq25980-charger"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = bq->iio_chan; + indio_dev->num_channels = bq25980_num_iio_channels; + + for (i = 0; i < bq25980_num_iio_channels; i++) { + bq->int_iio_chans[i].indio_dev = indio_dev; + chan = &bq->iio_chan[i]; + bq->int_iio_chans[i].channel = chan; + chan->address = i; + chan->channel = bq25980_iio_psy_channels[i].channel_num; + chan->type = bq25980_iio_psy_channels[i].type; + chan->datasheet_name = + bq25980_iio_psy_channels[i].datasheet_name; + chan->extend_name = + bq25980_iio_psy_channels[i].datasheet_name; + chan->info_mask_separate = + bq25980_iio_psy_channels[i].info_mask; + } + + rc = devm_iio_device_register(bq->dev, indio_dev); + if (rc) + pr_err("Failed to register [%s] IIO device, rc=%d\n", indio_dev->name,rc); + else + pr_err("Success to register [%s] IIO device\n", indio_dev->name); + + return rc; +} + +static int bq25980_parse_dt_id(struct bq25980_device *bq, int driver_data) +{ + switch (driver_data) { + case BQ25960_STANDALONE: + bq->device_id = BQ25960; + bq->mode = BQ_STANDALONE; + break; + case BQ25960_SLAVE: + bq->device_id = BQ25960; + bq->mode = BQ_SLAVE; + break; + case BQ25960_MASTER: + bq->device_id = BQ25960; + bq->mode = BQ_MASTER; + break; + case BQ25980_STANDALONE: + bq->device_id = BQ25980; + bq->mode = BQ_STANDALONE; + break; + case BQ25980_SLAVE: + bq->device_id = BQ25980; + bq->mode = BQ_SLAVE; + break; + case BQ25980_MASTER: + bq->device_id = BQ25980; + bq->mode = BQ_MASTER; + break; + default: + dev_err(bq->dev, "dts compatible id %d is unknown", driver_data); + return -EINVAL; + break; + } + + return 0; +} + +static int bq25980_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct bq25980_device *bq; + int ret, irq_gpio, irqn; + struct iio_dev *indio_dev; + + printk("-------[%s] driver probe--------\n",id->name); + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*bq)); + + if (!indio_dev) + return -ENOMEM; + + bq = iio_priv(indio_dev); + if (!bq) { + dev_err(dev, "Out of memory\n"); + return -ENOMEM; + } + + bq->indio_dev = indio_dev; + bq->client = client; + bq->dev = dev; + + mutex_init(&bq->lock); + mutex_init(&bq->irq_complete); + + bq->resume_completed = true; + bq->irq_waiting = false; + + strncpy(bq->model_name, id->name, I2C_NAME_SIZE); + + ret = bq25980_parse_dt_id(bq, id->driver_data); + if (ret) + goto free_mem; + bq->part_no = bq25890_get_part_no(bq); + + if ( bq->part_no == SC8541_PART_NO ) { + bq->client->addr = bq->sc8541_addr; + bq->chip_info = &sc8541_chip_info_tbl[bq->device_id]; + } else { + bq->chip_info = &bq25980_chip_info_tbl[bq->device_id]; + } + + bq->regmap = devm_regmap_init_i2c(client, + bq->chip_info->regmap_config); + if (IS_ERR(bq->regmap)) { + dev_err(dev, "Failed to allocate register map\n"); + devm_kfree(bq->dev, bq); + return PTR_ERR(bq->regmap); + } + + i2c_set_clientdata(client, bq); + + ret = bq25980_check_device_id(bq); + if (ret) + goto free_mem; + + ret = bq25980_check_work_mode(bq); + if (ret) + goto free_mem; + + ret = bq25980_parse_dt(bq); + if (ret) { + dev_err(dev, "Failed to read device tree properties%d\n", ret); + goto free_mem; + } + +#ifdef CONFIG_INTERRUPT_AS_GPIO + irq_gpio = of_get_named_gpio(client->dev.of_node, "irq-gpio", 0); + if (!gpio_is_valid(irq_gpio)) + { + dev_err(bq->dev, "%s: %d gpio get failed\n", __func__, irq_gpio); + goto free_mem; + } + ret = gpio_request(irq_gpio, "bq25980 irq pin"); + if (ret) { + dev_err(bq->dev, "%s: %d gpio request failed\n", __func__, irq_gpio); + goto free_mem; + } + gpio_direction_input(irq_gpio); + irqn = gpio_to_irq(irq_gpio); + if (irqn < 0) { + dev_err(bq->dev, "%s:%d gpio_to_irq failed\n", __func__, irqn); + goto free_mem; + } + client->irq = irqn; +#else + + bq->irq_pinctrl = + pinctrl_get_select(bq->dev, "bq25960_int_default"); + if (!bq->irq_pinctrl) { + dev_err(bq->dev,"Couldn't set pinctrl bq25960_int_default\n"); + goto free_mem; + } +#endif + + if (client->irq) { + ret = devm_request_threaded_irq(dev, client->irq, NULL, + bq25980_irq_handler_thread, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + dev_name(&client->dev), bq); + if (ret < 0) { + dev_err(bq->dev,"request irq for irq=%d failed, ret =%d\n", + client->irq, ret); + goto free_mem; + } + //enable_irq_wake(client->irq); + } + + ret = bq25980_power_supply_init(bq, dev, id->driver_data); + if (ret) + goto free_mem; + + ret = bq25980_reg_init(bq); + if (ret) { + dev_err(dev, "Cannot initialize the chip.\n"); + goto free_psy; + } + + bq25980_init_iio_psy(bq); + + bq25980_create_device_node(bq->dev); + + //dump_reg(bq,0x00,0x37); + + printk("-------[%s] driver probe success--------\n",bq->model_name); + return 0; +free_psy: + power_supply_unregister(bq->charger); + +free_mem: + devm_kfree(bq->dev, bq); + return ret; +} +static int bq25980_charger_remove(struct i2c_client *client) +{ + struct bq25980_device *bq = i2c_get_clientdata(client); + + bq25980_set_adc_enable(bq, false); + + power_supply_unregister(bq->charger); + + mutex_destroy(&bq->lock); + mutex_destroy(&bq->irq_complete); + + dev_err(bq->dev,"remove Successfully\n"); + return 0; +} + + +static void bq25980_charger_shutdown(struct i2c_client *client) +{ + struct bq25980_device *bq = i2c_get_clientdata(client); + + /*reg reset*/ + regmap_update_bits(bq->regmap, BQ25980_CHRGR_CTRL_2, + BQ25980_REG_RESET, BQ25980_REG_RESET); + bq25980_set_adc_enable(bq, false); + + regmap_write(bq->regmap, BQ25980_CHRGR_CTRL_2, 0); + + dev_err(bq->dev,"Shutdown Successfully\n"); +} + +static int bq25980_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq25980_device *bq = i2c_get_clientdata(client); + + mutex_lock(&bq->irq_complete); + bq->resume_completed = false; + mutex_unlock(&bq->irq_complete); + dev_err(bq->dev, "Suspend successfully!"); + + return 0; +} + +static int bq25980_suspend_noirq(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq25980_device *bq = i2c_get_clientdata(client); + + if (bq->irq_waiting) { + pr_err_ratelimited("Aborting suspend, an interrupt was detected while suspending\n"); + return -EBUSY; + } + return 0; +} + +static int bq25980_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq25980_device *bq = i2c_get_clientdata(client); + + + mutex_lock(&bq->irq_complete); + bq->resume_completed = true; + if (bq->irq_waiting) { + bq->irq_disabled = false; + enable_irq(client->irq); + mutex_unlock(&bq->irq_complete); + bq25980_irq_handler_thread(client->irq, bq); + } else { + mutex_unlock(&bq->irq_complete); + } + + power_supply_changed(bq->charger); + dev_err(bq->dev,"Resume successfully!"); + + return 0; +} + +static const struct dev_pm_ops bq25980_pm_ops = { + .resume = bq25980_resume, + .suspend_noirq = bq25980_suspend_noirq, + .suspend = bq25980_suspend, +}; + +static const struct i2c_device_id bq25980_i2c_ids[] = { + { "bq25980-standalone", BQ25980_STANDALONE }, + { "bq25980-master", BQ25980_MASTER }, + { "bq25980-slave", BQ25980_SLAVE }, + { "bq25960-standalone", BQ25960_STANDALONE }, + { "bq25960-master", BQ25960_MASTER }, + { "bq25960-slave", BQ25960_SLAVE }, + { "bq25975", BQ25975 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, bq25980_i2c_ids); + +static const struct of_device_id bq25980_of_match[] = { + { .compatible = "ti,bq25980-standalone", .data = (void *)BQ25980_STANDALONE}, + { .compatible = "ti,bq25980-master", .data = (void *)BQ25980_MASTER}, + { .compatible = "ti,bq25980-slave", .data = (void *)BQ25980_SLAVE}, + { .compatible = "ti,bq25960-standalone", .data = (void *)BQ25960_STANDALONE}, + { .compatible = "ti,bq25960-master", .data = (void *)BQ25960_MASTER}, + { .compatible = "ti,bq25960-slave", .data = (void *)BQ25960_SLAVE}, + { .compatible = "ti,bq25975", .data = (void *)BQ25975 }, + { }, +}; +MODULE_DEVICE_TABLE(of, bq25980_of_match); + +static struct i2c_driver bq25980_driver = { + .driver = { + .name = "bq25980-charger", + .of_match_table = bq25980_of_match, + .pm = &bq25980_pm_ops, + }, + .probe = bq25980_probe, + .id_table = bq25980_i2c_ids, + .remove = bq25980_charger_remove, + .shutdown = bq25980_charger_shutdown, +}; +module_i2c_driver(bq25980_driver); + +MODULE_AUTHOR("Dan Murphy "); +MODULE_AUTHOR("Ricardo Rivera-Matos "); +MODULE_DESCRIPTION("bq25980 charger driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/bq25980_mmi_iio/bq25980_mmi_iio.h b/drivers/power/bq25980_mmi_iio/bq25980_mmi_iio.h new file mode 100644 index 000000000000..cf6c169a4209 --- /dev/null +++ b/drivers/power/bq25980_mmi_iio/bq25980_mmi_iio.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ */ + +#ifndef BQ25980_IIO_H +#define BQ25980_IIO_H + + +#include +#include + + +struct bq25980_iio_channels { + const char *datasheet_name; + int channel_num; + enum iio_chan_type type; + long info_mask; +}; + +enum { + MMI_DISABLE_ADC = 0, + MMI_ENABLE_ADC, +}; + +#define bq25980_IIO_CHAN(_name, _num, _type, _mask) \ + { \ + .datasheet_name = _name, \ + .channel_num = _num, \ + .type = _type, \ + .info_mask = _mask, \ + }, + +#define bq25980_CHAN_INDEX(_name, _num) \ + bq25980_IIO_CHAN(_name, _num, IIO_INDEX, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +#define bq25980_CHAN_CUR(_name, _num) \ + bq25980_IIO_CHAN(_name, _num, IIO_CURRENT, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +#define bq25980_CHAN_VOLT(_name, _num) \ + bq25980_IIO_CHAN(_name, _num, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +static const struct bq25980_iio_channels bq25980_iio_psy_channels[] = { + bq25980_CHAN_INDEX("bq25980_cp_enabled", PSY_IIO_CP_ENABLE) + bq25980_CHAN_INDEX("bq25980_online", PSY_IIO_ONLINE) + bq25980_CHAN_CUR("bq25980_current_now", PSY_IIO_CURRENT_NOW) + bq25980_CHAN_VOLT("bq25980_voltage_now", PSY_IIO_VOLTAGE_NOW) + bq25980_CHAN_INDEX("bq25980_input_current_now", PSY_IIO_MMI_CP_INPUT_CURRENT_NOW) + bq25980_CHAN_INDEX("bq25980_input_voltage_settled", PSY_IIO_MMI_CP_INPUT_VOLTAGE_NOW) + bq25980_CHAN_INDEX("bq25980_cp_status1", PSY_IIO_CP_STATUS1) + bq25980_CHAN_INDEX("bq25980_cp_clear_error", PSY_IIO_CP_CLEAR_ERROR) +}; + +#endif /* BQ25980_IIO_H */ diff --git a/drivers/power/bq25980_mmi_iio/bq25980_reg.h b/drivers/power/bq25980_mmi_iio/bq25980_reg.h new file mode 100644 index 000000000000..f82e523beeaf --- /dev/null +++ b/drivers/power/bq25980_mmi_iio/bq25980_reg.h @@ -0,0 +1,249 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ */ + +#ifndef BQ25980_REG_H +#define BQ25980_REG_H + +#define BQ25980_MANUFACTURER "Texas Instruments" + +#define BQ25980_BATOVP 0x0 +#define BQ25980_BATOVP_ALM 0x1 +#define BQ25980_BATOCP 0x2 +#define BQ25980_BATOCP_ALM 0x3 +#define BQ25980_CHRGR_CFG_1 0x4 +#define BQ25980_CHRGR_CTRL_1 0x5 +#define BQ25980_BUSOVP 0x6 +#define BQ25980_BUSOVP_ALM 0x7 +#define BQ25980_BUSOCP 0x8 +#define BQ25980_REG_09 0x9 +#define BQ25980_TEMP_CONTROL 0xA +#define BQ25980_TDIE_ALM 0xB +#define BQ25980_TSBUS_FLT 0xC +#define BQ25980_TSBAT_FLG 0xD +#define BQ25980_VAC_CONTROL 0xE +#define BQ25980_CHRGR_CTRL_2 0xF +#define BQ25980_CHRGR_CTRL_3 0x10 +#define BQ25980_CHRGR_CTRL_4 0x11 +#define BQ25980_CHRGR_CTRL_5 0x12 +#define BQ25980_STAT1 0x13 +#define BQ25980_STAT2 0x14 +#define BQ25980_STAT3 0x15 +#define BQ25980_STAT4 0x16 +#define BQ25980_STAT5 0x17 +#define BQ25980_FLAG1 0x18 +#define BQ25980_FLAG2 0x19 +#define BQ25980_FLAG3 0x1A +#define BQ25980_FLAG4 0x1B +#define BQ25980_FLAG5 0x1C +#define BQ25980_MASK1 0x1D +#define BQ25980_MASK2 0x1E +#define BQ25980_MASK3 0x1F +#define BQ25980_MASK4 0x20 +#define BQ25980_MASK5 0x21 +#define BQ25980_DEVICE_INFO 0x22 +#define BQ25980_ADC_CONTROL1 0x23 +#define BQ25980_ADC_CONTROL2 0x24 +#define BQ25980_IBUS_ADC_MSB 0x25 +#define BQ25980_IBUS_ADC_LSB 0x26 +#define BQ25980_VBUS_ADC_MSB 0x27 +#define BQ25980_VBUS_ADC_LSB 0x28 +#define BQ25980_VAC1_ADC_MSB 0x29 +#define BQ25980_VAC1_ADC_LSB 0x2A +#define BQ25980_VAC2_ADC_MSB 0x2B +#define BQ25980_VAC2_ADC_LSB 0x2C +#define BQ25980_VOUT_ADC_MSB 0x2D +#define BQ25980_VOUT_ADC_LSB 0x2E +#define BQ25980_VBAT_ADC_MSB 0x2F +#define BQ25980_VBAT_ADC_LSB 0x30 +#define BQ25980_IBAT_ADC_MSB 0x31 +#define BQ25980_IBAT_ADC_LSB 0x32 +#define BQ25980_TSBUS_ADC_MSB 0x33 +#define BQ25980_TSBUS_ADC_LSB 0x34 +#define BQ25980_TSBAT_ADC_MSB 0x35 +#define BQ25980_TSBAT_ADC_LSB 0x36 +#define BQ25980_TDIE_ADC_MSB 0x37 +#define BQ25980_TDIE_ADC_LSB 0x38 +#define BQ25980_DEGLITCH_TIME 0x39 +#define BQ25980_CHRGR_CTRL_6 0x3A + +#define BQ25980_BUSOCP_STEP_uA 262500 +#define BQ25980_BUSOCP_OFFSET_uA 1050000 +#define BQ25960_BUSOCP_STEP_uA 250000 +#define BQ25960_BUSOCP_OFFSET_uA 1000000 + + +#define BQ25980_BUSOCP_SC_DFLT_uA 4200000 +#define BQ25980_BUSOCP_BYP_DFLT_uA 8000000 +#define BQ25975_BUSOCP_DFLT_uA 4250000 +#define BQ25960_BUSOCP_SC_DFLT_uA 4000000 +#define BQ25960_BUSOCP_BYP_DFLT_uA 5000000 + +#define BQ25980_BUSOCP_MIN_uA 1050000 +#define BQ25960_BUSOCP_MIN_uA 1000000 + +#define BQ25980_BUSOCP_SC_MAX_uA 6037500 +#define BQ25975_BUSOCP_SC_MAX_uA 5750000 +#define BQ25960_BUSOCP_SC_MAX_uA 4500000 + +#define BQ25980_BUSOCP_BYP_MAX_uA 8925000 +#define BQ25975_BUSOCP_BYP_MAX_uA 8500000 +#define BQ25960_BUSOCP_BYP_MAX_uA 6500000 + +#define BQ25980_BUSOVP_SC_STEP_uV 100000 +#define BQ25975_BUSOVP_SC_STEP_uV 50000 +#define BQ25960_BUSOVP_SC_STEP_uV 50000 +#define BQ25980_BUSOVP_SC_OFFSET_uV 14000000 +#define BQ25975_BUSOVP_SC_OFFSET_uV 7000000 +#define BQ25960_BUSOVP_SC_OFFSET_uV 7000000 + +#define BQ25980_BUSOVP_BYP_STEP_uV 50000 +#define BQ25975_BUSOVP_BYP_STEP_uV 25000 +#define BQ25960_BUSOVP_BYP_STEP_uV 25000 +#define BQ25980_BUSOVP_BYP_OFFSET_uV 7000000 +#define BQ25975_BUSOVP_BYP_OFFSET_uV 3500000 +#define BQ25960_BUSOVP_BYP_OFFSET_uV 3500000 + +#define BQ25980_BUSOVP_DFLT_uV 17800000 +#define BQ25980_BUSOVP_BYPASS_DFLT_uV 8900000 +#define BQ25975_BUSOVP_DFLT_uV 8900000 +#define BQ25975_BUSOVP_BYPASS_DFLT_uV 4450000 +#define BQ25960_BUSOVP_DFLT_uV 8900000 + +#define BQ25980_BUSOVP_SC_MIN_uV 14000000 +#define BQ25975_BUSOVP_SC_MIN_uV 7000000 +#define BQ25960_BUSOVP_SC_MIN_uV 7000000 +#define BQ25980_BUSOVP_BYP_MIN_uV 7000000 +#define BQ25975_BUSOVP_BYP_MIN_uV 3500000 +#define BQ25960_BUSOVP_BYP_MIN_uV 3500000 + +#define BQ25980_BUSOVP_SC_MAX_uV 22000000 +#define BQ25975_BUSOVP_SC_MAX_uV 12750000 +#define BQ25960_BUSOVP_SC_MAX_uV 12750000 + +#define BQ25980_BUSOVP_BYP_MAX_uV 12750000 +#define BQ25975_BUSOVP_BYP_MAX_uV 6500000 +#define BQ25960_BUSOVP_BYP_MAX_uV 6500000 + +#define BQ25980_BATOVP_STEP_uV 20000 +#define BQ25975_BATOVP_STEP_uV 10000 +#define BQ25960_BATOVP_STEP_uV 10000 + +#define BQ25980_BATOVP_OFFSET_uV 7000000 +#define BQ25975_BATOVP_OFFSET_uV 3500000 +#define BQ25960_BATOVP_OFFSET_uV 3500000 + +#define BQ25980_BATOVP_DFLT_uV 14000000 +#define BQ25975_BATOVP_DFLT_uV 8900000 +#define BQ25960_BATOVP_DFLT_uV 8900000 + +#define BQ25980_BATOVP_MIN_uV 7000000 +#define BQ25975_BATOVP_MIN_uV 3500000 +#define BQ25960_BATOVP_MIN_uV 3500000 + +#define BQ25980_BATOVP_MAX_uV 9540000 +#define BQ25975_BATOVP_MAX_uV 4770000 +#define BQ25960_BATOVP_MAX_uV 4770000 + +#define BQ25980_BATOCP_STEP_uA 100000 + +#define BQ25980_BATOCP_MASK GENMASK(6, 0) + +#define BQ25980_BATOCP_DFLT_uA 8100000 +#define BQ25960_BATOCP_DFLT_uA 6100000 + +#define BQ25980_BATOCP_MIN_uA 2000000 + +#define BQ25980_BATOCP_MAX_uA 11000000 +#define BQ25975_BATOCP_MAX_uA 11000000 +#define BQ25960_BATOCP_MAX_uA 8500000 + +#define BQ25980_ENABLE_HIZ 0xff +#define BQ25980_DISABLE_HIZ 0x0 +#define BQ25980_EN_BYPASS BIT(3) +#define BQ25980_REG_RESET BIT(7) +#define BQ25980_EN_OTG BIT(5) +#define BQ25980_DIS_MOS_BOTH BIT(2) +#define BQ25980_ENABLE_TYPEC_MOS BIT(1) +#define BQ25980_ENABLE_WLC_MOS BIT(0) +#define BQ25980_STAT1_OVP_MASK (BIT(7) | BIT(5) | BIT(1)) +#define BQ25980_STAT3_OVP_MASK (BIT(7) | BIT(6)) +#define BQ25980_STAT1_BATOVP_MASK BIT(7) +#define BQ25980_STAT1_VOUTOVP_MASK BIT(5) +#define BQ25980_STAT1_VBUSOVP_MASK BIT(1) +#define BQ25980_STAT1_OCP_MASK BIT(4) +#define BQ25980_STAT2_OCP_MASK BIT(7) +#define BQ25980_STAT2_BUSUCP_MASK BIT(5) +#define BQ25980_STAT4_TFLT_MASK GENMASK(4, 2) +#define BQ25980_WD_STAT BIT(0) +#define BQ25980_PRESENT_MASK GENMASK(4, 2) +#define BQ25980_CHG_EN BIT(4) +#define BQ25980_EN_HIZ BIT(6) +#define BQ25980_ADC_EN BIT(7) +#define BQ25980_MS_MASK GENMASK(1, 0) +#define BQ25980_DEVICE_ID_MASK GENMASK(3, 0) + +#define BQ25980_ADC_CURR_STEP_IBUS_uA 1070 +#define BQ25980_ADC_VOLT_STEP_VBAT_deciuV 10055 +#define BQ25980_ADC_VOLT_STEP_VBUS_deciuV 10066 +#define BQ25980_ADC_VOLT_OFFSET_VBUS -60000 +#define BQ25960_ADC_VOLT_STEP_deciuV 10000 +#define BQ25960_ADC_VOLT_STEP_uV 1000 +#define BQ25960_ADC_CURR_STEP_uA 1000 +#define BQ25980_ADC_POLARITY_BIT BIT(7) + +#define BQ25980_WATCHDOG_MASK GENMASK(4, 3) +#define BQ25980_WATCHDOG_DIS BIT(2) +#define BQ25980_WATCHDOG_MAX 300000 +#define BQ25980_WATCHDOG_MIN 0 +#define BQ25980_NUM_WD_VAL 4 + +#define BQ25980_VAC1_CONTROL_MASK GENMASK(7, 5) +#define BQ25980_VAC1_CONTROL_SHIFT 5 +#define BQ25980_VAC2_CONTROL_MASK GENMASK(4, 2) +#define BQ25980_VAC2_CONTROL_SHIFT 2 + +#define BQ25980_STAT1_BAT_OVP_MASK BIT(7) +#define BQ25980_STAT1_BAT_OVP_ALM_MASK BIT(6) +#define BQ25980_STAT1_BAT_OCP_MASK BIT(4) +#define BQ25980_STAT1_BAT_OCP_ALM_MASK BIT(3) +#define BQ25980_STAT1_BUS_OVP_MASK BIT(1) +#define BQ25980_STAT1_BUS_OVP_ALM_MASK BIT(0) +#define BQ25980_STAT2_BUS_OCP_MASK BIT(7) +#define BQ25980_STAT2_BUS_OCP_ALM_MASK BIT(6) //reserved +#define BQ25980_STAT4_TSBAT_FLT_MASK BIT(3) + +#define BQ25980_SWITCHING_STAT BIT(6) + +#define BQ25980_STAT1_BAT_UCP_ALM_MASK BIT(2) +#define BQ25980_STAT4_TSBUS_TSBAT_ALM_MASK BIT(5) +#define BQ25980_STAT4_TSBUS_FLT_MASK BIT(4) +#define BQ25980_STAT4_TDIE_FLT_MASK BIT(2) +#define BQ25980_STAT4_TDIE_ALM_MASK BIT(1) + +#define BQ25980_STAT5_CP_SWITCH_MASK BIT(6) + +//SC8545 +#define SC8541_CTRL6_REG 0x40 + +#define SC8541_ACDRV_MANUAL_EN BIT(6) +#define SC8541_ENABLE_TYPEC_MOS BIT(5) +#define SC8541_ENABLE_WLC_MOS BIT(4) +#define SC8541_VBUS_ERRPRHI_STAT BIT(4) +#define SC8541_VBUS_ERRPRLO_STAT BIT(3) + +#define SC8545_BATOVP_DFLT_uV 44000000 +#define SC8545_BATOVP_MAX_uV 47700000 +#define SC8545_BATOVP_MIN_uV 35000000 +#define SC8545_BATOVP_STEP_uV 10000 +#define SC8545_BATOVP_OFFSET_uV 35000000 + +#define SC8545_BATOCP_DFLT_uA 71000000 +#define SC8545_BATOCP_MAX_uA 127000000 + +#define SC8541_ADC_CURR_STEP_IBUS_uA 2500 +#define SC8541_ADC_VOLT_STEP_VBAT_deciuV 12500 +#define SC8541_ADC_VOLT_STEP_VOUT_deciuV 12500 +#define SC8541_ADC_VOLT_STEP_VBUS_deciuV 37500 + + +#endif /* BQ25980_CHARGER_H */ diff --git a/drivers/power/bq27426_fg_mmi/Android.mk b/drivers/power/bq27426_fg_mmi/Android.mk new file mode 100644 index 000000000000..27d23e99a321 --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/Android.mk @@ -0,0 +1,40 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := bq27426_fg_mmi.ko +LOCAL_MODULE_TAGS := optional + +ifeq ($(ATL_4000MAH_8A_BATTERY_PROFILE),true) + KERNEL_CFLAGS += CONFIG_ATL_4000MAH_8A_BATTERY_PROFILE=y + KBUILD_OPTIONS += CONFIG_ATL_4000MAH_8A_BATTERY_PROFILE=y +endif + +ifeq ($(SCUD_4000MAH_8A_BATTERY_PROFILE),true) + KERNEL_CFLAGS += CONFIG_SCUD_4000MAH_8A_BATTERY_PROFILE=y + KBUILD_OPTIONS += CONFIG_SCUD_4000MAH_8A_BATTERY_PROFILE=y +endif + +ifeq ($(ATL_LS40_1545MAH_BATTERY_PROFILE),true) + KERNEL_CFLAGS += CONFIG_ATL_LS40_1545MAH_BATTERY_PROFILE=y + KBUILD_OPTIONS += CONFIG_ATL_LS40_1545MAH_BATTERY_PROFILE=y +endif + +ifeq ($(ATL_LS30_1255MAH_BATTERY_PROFILE),true) + KERNEL_CFLAGS += CONFIG_ATL_LS30_1255MAH_BATTERY_PROFILE=y + KBUILD_OPTIONS += CONFIG_ATL_LS30_1255MAH_BATTERY_PROFILE=y +endif + +ifeq ($(ATL_NM40_712MAH_BATTERY_PROFILE),true) + KERNEL_CFLAGS += CONFIG_ATL_NM40_712MAH_BATTERY_PROFILE=y + KBUILD_OPTIONS += CONFIG_ATL_NM40_712MAH_BATTERY_PROFILE=y +endif + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/bq27426_fg_mmi/Kbuild b/drivers/power/bq27426_fg_mmi/Kbuild new file mode 100644 index 000000000000..7f4eb27f8b5d --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/Kbuild @@ -0,0 +1,26 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/bq27426_fg_mmi + +ifneq ($(filter m y,$(CONFIG_ATL_4000MAH_8A_BATTERY_PROFILE)),) + EXTRA_CFLAGS += -DATL_4000MAH_8A_BATTERY_PROFILE +endif + +ifneq ($(filter m y,$(CONFIG_SCUD_4000MAH_8A_BATTERY_PROFILE)),) + EXTRA_CFLAGS += -DSCUD_4000MAH_8A_BATTERY_PROFILE +endif + +ifneq ($(filter m y,$(CONFIG_ATL_LS40_1545MAH_BATTERY_PROFILE)),) + EXTRA_CFLAGS += -DATL_LS40_1545MAH_BATTERY_PROFILE +endif + +ifneq ($(filter m y,$(CONFIG_ATL_LS30_1255MAH_BATTERY_PROFILE)),) + EXTRA_CFLAGS += -DATL_LS30_1255MAH_BATTERY_PROFILE +endif + +ifneq ($(filter m y,$(CONFIG_ATL_NM40_712MAH_BATTERY_PROFILE)),) + EXTRA_CFLAGS += -DATL_NM40_712MAH_BATTERY_PROFILE +endif + +obj-m += bq27426_fg_mmi.o diff --git a/drivers/power/bq27426_fg_mmi/Makefile b/drivers/power/bq27426_fg_mmi/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/power/bq27426_fg_mmi/README b/drivers/power/bq27426_fg_mmi/README new file mode 100644 index 000000000000..ef9435a7d0d9 --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/README @@ -0,0 +1,189 @@ +Motorola Mobility. BQ27426 Fuel Gauge Specific Bindings + +This is a brief introduction on bq27426_fg_mmi, based on previous developments, +but edited and improved by Motorola. It is a DLKM, and messages on kernel logs +may be tagged as: +- "bq27426" +- "bq27426_bms" +- or no tag, if it's happening during probe + +This driver has the following components: +- README - this file :) +- build files (Makefile, Kbuild, Android.mk) +- command files (bqfs_cmd_type.h), which is the basic structure to interpret +the Battery Profile Header file. +- driver implementation (bq27426_fg_mmi.c) + +One of the main architectural differences in between Texas Instruments and +Qualcomm Fuel Gauge drivers is: +- battery profile format (QCOM provides as a .dtsi file, while TI shares it +as a .gm.fs) +- battery profile addition (as a DT file, QCOM requires it to be included on +OEM DT, while TI statically adds a battery profile to the driver itself). + +The following sections better detail the DT and how to add a battery profile to +a product. + +================================== + Mandatory Device Tree properties +================================== + +- design-capacity + Usage: mandatory + Value Type: + Definition: Defines maximum battery capacity, in mAh. + +- chem_id + Usage: mandatory + Value Type: + Definition: Each battery model has a unique identifier, called by Texas +Instruments as "Chemical ID" (or chem-id). This value is hard-stored to the +battery, and can be queried, similar to battery ID resistance in Qualcomm +architecture. This values comes from Texas Instruments Battery +Characterization Data. + +- serialnum + Usage: mandatory + Value Type: + Definition: Each battery model also has Serial Number, defining that battery +model. This is a reference value, not stored inside battery, but necessary to +control and differentiate battery models. This is a requirement per Texas +Instruments original Device Drive. + +================================= + Optional Device Tree properties +================================= + +- mmi,fg-batt-main + Usage: optional + Value Type: + Definition: Determines that component is measuring main battery. This is +helpful when using a dual battery architecture, to differentiate main and +flip batteries. + +- mmi,fg-batt-flip + Usage: optional + Value Type: + Definition: Determines that component is measuring flip battery. This is +helpful when using a dual battery architecture, to differentiate main and +flip batteries. + +======================================= + Performing a Battery Characterization +======================================= + +EE (Electronics Engineering), or Hardware Team starts this process. First, a +battery sample is requested from supplier. Then Texas Instruments is contacted +to verify how many samples they need to perform this characterization. + +At this point, it is important to emphasize to Texas Instruments that Fuel +Gauge data is stored on device runtime, and not on a test jig or factory, +because on the latter case, they will send a set of files to be used with a +specific software and hardware solution to hotplug the FG chip and store FG +characterization data. + +What Motorola needs is a file having .gm.fs extension containing command and +data sequence. This is also part of Texas Instruments process, so doesn't take +any extra effort/service on then to provide such data. + +Having that file, it is necessary to execute "battery_profil/convbqfs.pl" Perl +script. Parameters are: + +convbqfs.pl --input [--output {output file}] [--line] [--name {array name}] + if output file is not specified, default name is same with input file name with .h extension +Example: + convbqfs.pl --input bq27320.df.fs + convbqfs.pl --input bq27320.df.fs --output bq27320-bqfs-image.h + convbqfs.pl --input bq27320.df.fs --output bq27320-bqfs-image.h --line + convbqfs.pl --input bq27320.df.fs --output bq27320-bqfs-image.h --name bqfs_image + +===================================== + Adding a Battery Profile to Product +===================================== + +After file conversion is done by the Perl script, a .h file is generated. As a +recommended standard, files having this extension go to +"battery_profile/imported" folder, while .gm.fs files go to +"battery_profile/raw" folder. + +Once there, first step is to modify the Device Tree with the abovementioned DT +properties. Here's an example: + +bq27426_main: bq27426-battery-main@55 { + compatible = "ti,bq27426"; + reg = <0x55>; + status = "okay"; + pinctrl-names = "default"; + pinctrl-0 = <&bq27426_irq_main>; + interrupt-parent = <&tlmm>; + interrupts = <119 0>; + interrupt-names = "bq27426_irq"; + mmi,fg-batt-main; + design-capacity = <1400>; + battery_profile_0 { + chem_id = <2542>; + serialnum = "SB18C54570"; + }; + battery_profile_1 { + chem_id = <5>; + serialnum = "SB18C54580"; + }; +}; + + +It is also recommended to add multiple profiles as "battery_profile_X", being X +an incremental number starting from 0, based on which profile came first to +the product. + +Once there, next step is to change /device/moto//device.mk file, +adding a makefile variable to the battery. Example: + +ATL_4000MAH_8A_BATTERY_PROFILE := true +ATL_LS30_1255MAH_BATTERY_PROFILE := true + + +Later, with this variable, a CFlag can be created on +"bq27426_fg_mmi/Android.mk". Example: + +ifneq ($(filter m y,$(CONFIG_ATL_LS40_1545MAH_BATTERY_PROFILE)),) + EXTRA_CFLAGS += -DATL_LS40_1545MAH_BATTERY_PROFILE +endif + + +Next step is to add a Kernel CFlag using previous flag on +"bq27426_fg_mmi/Kbuild". Example: + +ifeq ($(SCUD_4000MAH_8A_BATTERY_PROFILE),true) + KERNEL_CFLAGS += CONFIG_SCUD_4000MAH_8A_BATTERY_PROFILE=y +endif + + +Finally, to include battery profile to the driver implementation, on +"bq27426_fg_mmi/bq27426_fg_mmi.c". Example: + +#ifdef ATL_4000MAH_8A_BATTERY_PROFILE +#include "battery_profile/imported/lenovo_atl_df1_gmfs.h" +#endif + +=========================== + Dual Battery Architecture +=========================== + +Although each battery has its own Fuel Gauge chip, main system is unaware of +anything different than the FG chip from the original architecture. In other +words, great are the chances you are implementing this dual battery solution +based on Qualcomm architecture, which uses either QG or FG-Gen4 Fuel Gauge +algorithms. + +So, QCOM FG system will still wait for battery data, which should exist as a +single battery. That means original system needs to receive some "fake" data, +although reliable enough to not break anything else. + +For such, first step is to prepare a single package sample (for that, ask EE +Team to join both batteries in parallel, into a single package, having a small +internal resistance, separating each one. Few units (probably 03) of those +packages will be requested by Qualcomm to be characterized. + +From here on, this package is handled as a single battery, and as a result, a +Device Tree file (dtsi) will be shared via proper QCOM SR, which needs to be +properly integrated, as most MOT products. diff --git a/drivers/power/bq27426_fg_mmi/battery_profile/convbqfs.pl b/drivers/power/bq27426_fg_mmi/battery_profile/convbqfs.pl new file mode 100755 index 000000000000..f52d71f7ed3a --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/battery_profile/convbqfs.pl @@ -0,0 +1,237 @@ +#!/usr/bin/perl + +use warnings; +use strict; +use autodie; +use Getopt::Long; +use 5.010; + +my $line_num = 0; +my $line; +my $input_file; +my $output_file; +my $array_name = "bqfs_image"; + +# Remove both leading and trailing whitespace +sub trim +{ + my @array = @_ ; + for(@array){ + s/^\s+|\s+$//g ; + } + return @array ; +} + + +sub output_title +{ + select OUTPUTFILE; + say "/*****************************************************************************"; + say "* Copyright 2010 Texas Instruments Corporation, All Rights Reserved."; + say "* TI makes NO WARRANTY as to software products, which are supplied \"AS-IS\""; + say "*****************************************************************************/"; + say ""; + say "/*****************************************************************************"; + say "* This code is automatically generated from bqfs/dffs file. *"; + say "* DO NOT MODIFY THIS FILE DIRECTLY *"; + say "*****************************************************************************/"; + say ""; + say "//this file is generated from $input_file at ".localtime; + say ""; + select STDOUT; +} + +sub output_header +{ + select OUTPUTFILE; + say "#ifndef __BQFS_FILE__"; + say "#define __BQFS_FILE__"; + say ""; + say "#include \"bqfs_cmd_type.h\""; + say ""; + select STDOUT; +} + +sub output_bqfs_img_head +{ + say OUTPUTFILE "const bqfs_cmd_t ".$array_name."[] = {"; +} + +sub output_bqfs_img_tail +{ + select OUTPUTFILE; + say "};"; + say "//end of const bqfs_cmd_t ".$array_name."[]"; + say "#endif"; + select STDOUT; +} + +sub output_bqfs_cmd_read +{ + select OUTPUTFILE; + printf "\t{\n"; + printf "\t\t.cmd_type\t= CMD_R,\n"; + printf "\t\t.addr\t\t= 0x".shift(@_).",\n"; + printf "\t\t.reg\t\t= 0x".shift(@_).",\n"; + if($line){ + printf "\t\t.line_num\t= $line_num,\n"; + } + printf "\t},\n"; + select STDOUT; + +} + +sub output_bqfs_cmd_write +{ + my $i = 0; + + select OUTPUTFILE; + + printf "\t{\n"; + printf "\t\t.cmd_type\t= CMD_W,\n"; + printf "\t\t.addr\t\t= 0x".shift(@_).",\n"; + printf "\t\t.reg\t\t= 0x".shift(@_).",\n"; + printf "\t\t.data\t\t= {.bytes = {"; + + foreach my $data (@_){ + if($i != scalar(@_) - 1){ + printf "0x$data, "; + } + else{#last one + printf "0x$data"; + } + $i = $i + 1; + } + + printf "}},\n"; + printf "\t\t.data_len\t= $i,\n"; + if($line){ + printf "\t\t.line_num\t= $line_num,\n"; + } + printf "\t},\n"; + + select STDOUT; + +} + +sub output_bqfs_cmd_compare +{ + my $i = 0; + + select OUTPUTFILE; + + printf "\t{\n"; + printf "\t\t.cmd_type\t= CMD_C,\n"; + printf "\t\t.addr\t\t= 0x".shift(@_).",\n"; + printf "\t\t.reg\t\t= 0x".shift(@_).",\n"; + printf "\t\t.data\t\t= {.bytes = {"; + foreach my $data (@_){ + if($i != scalar(@_) - 1){ + printf "0x$data, "; + } + else{#last one + printf "0x$data"; + } + $i++; + } + printf "}},\n"; + printf "\t\t.data_len\t= $i,\n"; + if($line){ + printf "\t\t.line_num\t= $line_num,\n"; + } + printf "\t},\n"; + + select STDOUT; +} + + + +sub output_bqfs_cmd_wait +{ + select OUTPUTFILE; + + printf "\t{\n"; + printf "\t\t.cmd_type\t= CMD_X,\n"; + printf "\t\t.data\t\t= {.delay = ".shift(@_)."},\n"; + if($line){ + printf "\t\t.line_num\t= $line_num,\n"; + } + printf "\t},\n"; + + select STDOUT; + +} + +sub parse_argument +{ + GetOptions( + 'line!' => \$line, + 'input:s' => \$input_file, + 'output:s' => \$output_file, + 'name:s' => \$array_name, + ); + if(!$output_file){ + $output_file = $input_file."\.h"; + } + + if($input_file){ + return 1; + } + return 0; + +} + + +#main function +if (!parse_argument()){ + say "Usage:\n\t $0 --input [--output {output file}] [--line] [--name {array name}]"; + say "\t\t if output file is not specified, default name is same with input file name with .h extension"; + say "Example:\n\t $0 --input bq27320.df.fs"; + say "\t $0 --input bq27320.df.fs --output bq27320-bqfs-image.h"; + say "\t $0 --input bq27320.df.fs --output bq27320-bqfs-image.h --line"; + say "\t $0 --input bq27320.df.fs --output bq27320-bqfs-image.h --name bqfs_image"; + exit; +} + +open INPUTFILE, '<', $input_file; +open OUTPUTFILE, '>', $output_file; + +&output_title(); +&output_header(); + +&output_bqfs_img_head(); +$| = 1; +print "Begin converting"; + +while(){ + $line_num++; + print "."; + chomp(); + my @line = split(/ /); + @line = trim(@line); + my $token = shift(@line); + + if($token eq "R:"){ + &output_bqfs_cmd_read(@line); + next; + } + if($token eq "W:"){ + &output_bqfs_cmd_write(@line); + next; + } + if($token eq "X:"){ + &output_bqfs_cmd_wait(@line); + next; + } + if ($token eq "C:"){ + &output_bqfs_cmd_compare(@line); + next; + } +} + +print "Done\n"; + +&output_bqfs_img_tail(); + +close INPUTFILE; +close OUTPUTFILE; diff --git a/drivers/power/bq27426_fg_mmi/battery_profile/imported/alt_nm40_712mah_bq27426g1.gm.fs.h b/drivers/power/bq27426_fg_mmi/battery_profile/imported/alt_nm40_712mah_bq27426g1.gm.fs.h new file mode 100644 index 000000000000..fdac1736ac34 --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/battery_profile/imported/alt_nm40_712mah_bq27426g1.gm.fs.h @@ -0,0 +1,975 @@ +/***************************************************************************** +* Copyright 2010 Texas Instruments Corporation, All Rights Reserved. +* TI makes NO WARRANTY as to software products, which are supplied "AS-IS" +*****************************************************************************/ + +/***************************************************************************** +* This code is automatically generated from bqfs/dffs file. * +* DO NOT MODIFY THIS FILE DIRECTLY * +*****************************************************************************/ + +//this file is generated from raw/0426_2_02-bq27426G1_L_flip_041122.gm.fs at Tue Apr 12 10:30:54 2022 + +#ifndef __FLIP_BQFS_FILE__ +#define __FLIP_BQFS_FILE__ + +#include "bqfs_cmd_type.h" + +const bqfs_cmd_t li_alt_flip_bqfs_image[] = { + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x01, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x26, 0x04}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x02, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x02, 0x02}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x13, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 1100}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x02, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x02, 0x26, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA5}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x02, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA5}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x24, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x19, 0x28, 0x63, 0x5F, 0xFF, 0x62, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x69}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x24, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x69}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x31, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x0A, 0x0F, 0x02, 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}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x31, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x40, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x65, 0x78, 0x0F, 0x9F, 0x23, 0x00, 0x00, 0x14, 0x04, 0x00, 0x09, 0x04, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x06}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x40, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x06}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x44, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x32, 0x01, 0xC2, 0x14, 0x00, 0x03, 0x08, 0x98, 0x01, 0x00, 0x3C, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x0E}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x44, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x0E}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x01, 0xF4, 0x00, 0x1E, 0xC8, 0x14, 0x08, 0x00, 0x3C, 0x0E, 0x10, 0x00, 0x0A, 0x46, 0x05, 0x14, 0x05, 0x0F, 0x03, 0x20, 0x00, 0x32, 0x00, 0x64, 0x46, 0x50, 0x0A, 0x01, 0x90, 0x00, 0x64, 0x19}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xCA}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xCA}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xDC, 0x5C, 0x60, 0x00, 0x7D, 0x00, 0x04, 0x03, 0x19, 0x25, 0x0F, 0x14, 0x0A, 0x78, 0x60, 0x28, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x43, 0x80, 0x04, 0x01, 0x14, 0x00, 0x08, 0x0B, 0xB8, 0x01}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDB}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDB}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x02}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x2C, 0x0A, 0x01, 0x0A, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x64, 0x02, 0x00, 0x00, 0x00, 0x00, 0x07, 0xD0, 0x01, 0x03, 0x5A, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x47}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x02}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x47}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x51, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0xDC, 0x00, 0x64, 0x01, 0x4A, 0x00, 0x3C, 0x3C, 0x01, 0xB3, 0xB3, 0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x04}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x51, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x04}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x52, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x45, 0x23, 0x00, 0x00, 0x00, 0x81, 0x02, 0x94, 0x09, 0x8A, 0x0C, 0x80, 0x00, 0xC8, 0x00, 0x32, 0x00, 0x14, 0x03, 0xE8, 0x01, 0x00, 0x20, 0x00, 0x0A, 0xFF, 0xCE, 0xFF, 0xCE, 0x00, 0x01, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA2}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x52, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA2}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x57, 0x25, 0x11, 0x71, 0xE2, 0xE4, 0xE7, 0xE5, 0xE4, 0xE2, 0xDF, 0xE0, 0xDF, 0xE0, 0xE1, 0xE2, 0xE3, 0xE5, 0xE3, 0xE7, 0xE0, 0xDF, 0xE6, 0xEA, 0xF1, 0xF1, 0xF2, 0xF4, 0xF5, 0xF6, 0xF9, 0xF7}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA4}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA4}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFB, 0xF3, 0xF3, 0xF2, 0xF0, 0xEB, 0xF9, 0xFD, 0xFA, 0xD6, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x0B}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x0B}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0xC1, 0x04, 0x01, 0xF7, 0x01, 0xFD, 0xFC, 0xFE, 0x06, 0x00, 0x05, 0x01, 0xFD, 0x02, 0xFE, 0xFE, 0x0B, 0x05, 0x01, 0xFD, 0x0F, 0x00, 0xFA, 0xFF, 0x01, 0x00, 0x03, 0xFF, 0xEC, 0xE8, 0xC4}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x92}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x92}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFA, 0xFA, 0xFB, 0x09, 0x06, 0x1C, 0xF8, 0xA0, 0xC8, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x04}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x04}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x55, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFE, 0xF9, 0x01, 0x0B, 0x13, 0x02, 0xF8, 0x09, 0x08, 0x00, 0x01, 0xFF, 0x00, 0x15, 0x3B, 0x6C, 0x9E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x84}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x55, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x84}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6C, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFE, 0x80, 0x01, 0xF1, 0x07, 0x08, 0x1D, 0xE5, 0x08, 0xD5, 0xFB, 0xFF, 0x01, 0xDE, 0xFC, 0x06, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x6E}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6C, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x6E}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x59, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x50, 0x00, 0x44, 0x00, 0x42, 0x00, 0x4B, 0x00, 0x5B, 0x00, 0x3F, 0x00, 0x49, 0x00, 0x3D, 0x00, 0x3D, 0x00, 0x3E, 0x00, 0x45, 0x00, 0x43, 0x00, 0x64, 0x01, 0x06, 0x05, 0x17, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x34}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x59, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x34}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6D, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x02, 0xC9, 0x0E, 0xDE, 0x0E, 0x61, 0x11, 0x9E, 0x11, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xE9}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6D, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xE9}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x68, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x00, 0x00, 0x00, 0xD4, 0x86, 0x4A, 0xC6, 0xB4, 0xC2, 0x6E, 0x2B, 0x03, 0x7C, 0x01, 0x48, 0xFD, 0xA3, 0xF6, 0x75, 0x12, 0x58, 0x2D, 0xB7, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xD4, 0x1A, 0x05}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA7}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x68, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA7}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x68, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x1C, 0x98, 0x02, 0xD3, 0xFF, 0xB9, 0x30, 0xEF, 0x05, 0x11, 0x05, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x3C, 0x00, 0x50, 0x3C, 0x00, 0x64, 0x3C, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xEA}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x68, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xEA}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x69, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFA, 0xA0, 0x0B, 0xC6, 0x7F, 0x60, 0x1A, 0x37, 0x93, 0x7E, 0xFA, 0x8F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xCA}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x69, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xCA}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6B, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xEF, 0x05, 0x11, 0x05, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x3C, 0x00, 0x50, 0x3C, 0x00, 0x64, 0x3C, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x5B}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6B, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x5B}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x70, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xFF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x70, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xFF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x00, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x42, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 2000}, + }, +}; +//end of const bqfs_cmd_t li_alt_flip_bqfs_image[] +#endif diff --git a/drivers/power/bq27426_fg_mmi/battery_profile/imported/bq27426_gmfs_coslight.h b/drivers/power/bq27426_fg_mmi/battery_profile/imported/bq27426_gmfs_coslight.h new file mode 100644 index 000000000000..9c855997d075 --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/battery_profile/imported/bq27426_gmfs_coslight.h @@ -0,0 +1,945 @@ +/***************************************************************************** +* Copyright 2010 Texas Instruments Corporation, All Rights Reserved. +* TI makes NO WARRANTY as to software products, which are supplied "AS-IS" +*****************************************************************************/ + +/***************************************************************************** +* This code is automatically generated from bqfs/dffs file. * +* DO NOT MODIFY THIS FILE DIRECTLY * +*****************************************************************************/ + + +#ifndef __BQFS_FILE__ +#define __BQFS_FILE__ + +#include "bqfs_cmd_type.h" + +const bqfs_cmd_t bqfs_coslight[] = { + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x01, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x26, 0x04}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x02, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x02, 0x02}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x02, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x02, 0x26, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA5}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x02, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA5}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x24, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x19, 0x28, 0x63, 0x5F, 0xFF, 0x62, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x69}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x24, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x69}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x31, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x0A, 0x0F, 0x02, 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}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x31, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x40, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x64, 0x79, 0x07, 0x9F, 0x23, 0x00, 0x00, 0x14, 0x04, 0x00, 0x09, 0x04, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x0E}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x40, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x0E}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x44, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x32, 0x01, 0xC2, 0x30, 0x00, 0x03, 0x08, 0x98, 0x01, 0x00, 0x3C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xF9}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x44, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xF9}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x01, 0xF4, 0x00, 0x1E, 0xC8, 0x14, 0x08, 0x00, 0x3C, 0x0E, 0x10, 0x00, 0x0A, 0x46, 0x05, 0x14, 0x05, 0x0F, 0x03, 0x20, 0x7F, 0xFF, 0x00, 0xF0, 0x46, 0x50, 0x18, 0x01, 0x90, 0x00, 0x64, 0x19}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xE4}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xE4}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xDC, 0x5C, 0x60, 0x00, 0x7D, 0x00, 0x04, 0x03, 0x19, 0x25, 0x0F, 0x14, 0x0A, 0x78, 0x60, 0x28, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x43, 0x80, 0x04, 0x01, 0x14, 0x00, 0x08, 0x0B, 0xB8, 0x01}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDB}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDB}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x02}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x2C, 0x0A, 0x01, 0x0A, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x64, 0x02, 0x00, 0x00, 0x00, 0x00, 0x07, 0xD0, 0x01, 0x03, 0x5A, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x59}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x02}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x59}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x51, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x01, 0x77, 0x01, 0x2C, 0x02, 0x58, 0x00, 0x3C, 0x3C, 0x01, 0xB3, 0xB3, 0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x90}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x51, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x90}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x52, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x40, 0x00, 0x02, 0x00, 0x32, 0x81, 0x0C, 0x08, 0x2E, 0x52, 0x0C, 0x1C, 0x00, 0xC8, 0x00, 0x32, 0x00, 0x14, 0x03, 0xE8, 0x01, 0x01, 0x00, 0x00, 0x0A, 0xFF, 0xCE, 0xFF, 0xCE, 0x00, 0x01, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xAE}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x52, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xAE}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x15, 0x07, 0x11, 0x2C, 0xD3, 0xD7, 0xE0, 0xE0, 0xE3, 0xE3, 0xE6, 0xE5, 0xE5, 0xEA, 0xF4, 0xD3, 0xCA, 0xF3, 0xF3, 0xEB, 0xF0, 0xE2, 0xF0, 0xEE, 0xF2, 0xF2, 0xF7, 0xF9, 0xFA, 0xFA, 0xFC, 0xFE}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x08}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x08}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xF8, 0xF9, 0xF8, 0xF6, 0xEF, 0xF0, 0xF0, 0xFE, 0xFD, 0xE9, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xED}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xED}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFB, 0x1D, 0x05, 0x09, 0x06, 0x03, 0x02, 0xFF, 0x00, 0xFC, 0xFF, 0xFD, 0xFF, 0xFA, 0x0E, 0x19, 0x02, 0x01, 0x02, 0xFB, 0xF5, 0xF5, 0x02, 0x01, 0x05, 0xFE, 0xFB, 0xFD, 0xFE, 0xFC, 0xF7, 0xFB}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xE3}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xE3}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFA, 0xFD, 0xFE, 0x03, 0xFA, 0x0B, 0xFE, 0x03, 0xB9, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xC7}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xC7}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x55, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFF, 0x4B, 0x01, 0x05, 0x02, 0x06, 0x06, 0xFF, 0x03, 0xFA, 0x01, 0xFD, 0xFA, 0x01, 0x1A, 0xE5, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x33}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x55, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x33}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6C, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFE, 0xE0, 0x00, 0x00, 0x28, 0x08, 0x42, 0xDF, 0xFE, 0xE5, 0xFB, 0xFD, 0xF8, 0xED, 0x0F, 0x09, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xEE}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6C, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xEE}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x59, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x0D, 0x00, 0x09, 0x00, 0x10, 0x00, 0x0E, 0x00, 0x23, 0x00, 0x15, 0x00, 0x18, 0x00, 0x17, 0x00, 0x17, 0x00, 0x18, 0x00, 0x18, 0x00, 0x16, 0x00, 0x24, 0x00, 0x25, 0x00, 0x5B, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x63}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x59, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x63}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6D, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x0E, 0xBB, 0x0E, 0xE3, 0x0E, 0x9C, 0x11, 0x30, 0x10, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x4C}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6D, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x4C}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x68, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x00, 0x00, 0x00, 0xD4, 0x86, 0x4A, 0xC6, 0xB4, 0xC2, 0x6E, 0x2B, 0x03, 0x7C, 0x01, 0x48, 0xFD, 0xA3, 0xF6, 0x75, 0x12, 0x58, 0x2D, 0xB7, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xD4, 0x1A, 0x05}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA7}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x68, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA7}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x68, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x1C, 0x98, 0x02, 0xD3, 0xFF, 0xB9, 0x30, 0xEF, 0x05, 0x11, 0x05, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x3C, 0x00, 0x50, 0x3C, 0x00, 0x64, 0x3C, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xEA}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x68, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xEA}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x69, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFF, 0xFF, 0x0B, 0xD6, 0x7E, 0x74, 0x6D, 0xC6, 0x93, 0x0B, 0x0D, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xCA}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x69, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xCA}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6B, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xEF, 0x05, 0x11, 0x05, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x3C, 0x00, 0x50, 0x3C, 0x00, 0x64, 0x3C, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x5B}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6B, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x5B}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x70, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xFF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x70, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xFF}}, + .data_len = 1, + }, +}; +//end of const bqfs_cmd_t bqfs_image[] +#endif diff --git a/drivers/power/bq27426_fg_mmi/battery_profile/imported/lenovo_atl_df1_gmfs.h b/drivers/power/bq27426_fg_mmi/battery_profile/imported/lenovo_atl_df1_gmfs.h new file mode 100644 index 000000000000..4b7ebfa1fbcc --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/battery_profile/imported/lenovo_atl_df1_gmfs.h @@ -0,0 +1,975 @@ +/***************************************************************************** +* Copyright 2010 Texas Instruments Corporation, All Rights Reserved. +* TI makes NO WARRANTY as to software products, which are supplied "AS-IS" +*****************************************************************************/ + +/***************************************************************************** +* This code is automatically generated from bqfs/dffs file. * +* DO NOT MODIFY THIS FILE DIRECTLY * +*****************************************************************************/ + +//this file is generated from ./LenovoRBTweak_NewRa.gg_RB0_-375_avgP_-47.gm.fs at Sat Sep 7 01:09:27 2019 + +#ifndef __ATL_BQFS_FILE__ +#define __ATL_BQFS_FILE__ + +#include "bqfs_cmd_type.h" + +const bqfs_cmd_t atl_bqfs_image[] = { + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x01, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x26, 0x04}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x02, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x02, 0x02}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x13, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 1100}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x02, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x02, 0x26, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA5}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x02, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA5}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x24, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x19, 0x28, 0x63, 0x5F, 0xFF, 0x62, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x69}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x24, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x69}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x31, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x0A, 0x0F, 0x02, 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}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x31, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x40, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x64, 0x71, 0x0F, 0x9F, 0x23, 0x02, 0x00, 0x14, 0x04, 0x00, 0x09, 0x04, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x0C}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x40, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x0C}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x44, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x32, 0x01, 0xC2, 0x30, 0x00, 0x03, 0x08, 0x98, 0x01, 0x00, 0x3C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xF9}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x44, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xF9}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x01, 0xF4, 0x00, 0x1E, 0xC8, 0x14, 0x08, 0x00, 0x3C, 0x0E, 0x10, 0x00, 0x0A, 0x46, 0x05, 0x14, 0x05, 0x0F, 0x03, 0x20, 0x7F, 0xFF, 0x00, 0xF0, 0x46, 0x50, 0x18, 0x01, 0x90, 0x00, 0x64, 0x19}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xE4}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xE4}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xDC, 0x5C, 0x60, 0x00, 0x7D, 0x00, 0x04, 0x03, 0x19, 0x25, 0x0F, 0x14, 0x0A, 0x78, 0x60, 0x28, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x43, 0x80, 0x04, 0x01, 0x14, 0x00, 0x08, 0x0B, 0xB8, 0x01}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDB}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDB}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x02}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x2C, 0x0A, 0x01, 0x0A, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x64, 0x02, 0x00, 0x00, 0x00, 0x00, 0x07, 0xD0, 0x01, 0x03, 0x5A, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x47}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x02}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x47}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x51, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0xA7, 0x00, 0x64, 0x00, 0xFA, 0x00, 0x3C, 0x3C, 0x01, 0xB3, 0xB3, 0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x8A}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x51, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x8A}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x52, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x40, 0xB4, 0x00, 0x00, 0x00, 0x81, 0x0F, 0xA0, 0x39, 0xD0, 0x0C, 0x80, 0x00, 0xC8, 0x00, 0x32, 0x00, 0x13, 0x02, 0x32, 0x01, 0x00, 0x85, 0x00, 0x0A, 0xFF, 0xD6, 0xFF, 0xD1, 0x00, 0x01, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xCF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x52, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xCF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x09, 0xEE, 0x11, 0x38, 0xDB, 0xDD, 0xE0, 0xE1, 0xE1, 0xE1, 0xE1, 0xE3, 0xE3, 0xE4, 0xE6, 0xE6, 0xE6, 0xE9, 0xEB, 0xE8, 0xE8, 0xE5, 0xE5, 0xF0, 0xF1, 0xF5, 0xF6, 0xF7, 0xF6, 0xFB, 0xF9, 0xFD}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x2F}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x2F}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFC, 0xF4, 0xF7, 0xF0, 0xF2, 0xEA, 0xFA, 0xFB, 0xFB, 0xDB, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x01}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x01}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFC, 0x0C, 0x03, 0x17, 0x02, 0x16, 0xFE, 0x09, 0x0B, 0x01, 0xF9, 0x07, 0x0D, 0xF7, 0x0F, 0x11, 0x07, 0x04, 0x0B, 0x15, 0x08, 0xFE, 0x09, 0x01, 0x02, 0xF1, 0xFA, 0x0D, 0xF1, 0xF3, 0x9F, 0xA5}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x31}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x31}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xF8, 0xD6, 0x44, 0xFA, 0x11, 0xFF, 0x28, 0xAB, 0xA0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xF0}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xF0}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x55, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFF, 0x85, 0x00, 0x19, 0xF3, 0xF5, 0xE2, 0x36, 0xFF, 0x0A, 0x02, 0x01, 0xE7, 0x01, 0xEC, 0xA3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x5F}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x55, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x5F}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6C, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFE, 0x89, 0x00, 0x00, 0x16, 0x16, 0x07, 0xEE, 0xFF, 0xED, 0xFF, 0xF7, 0xEF, 0x08, 0x12, 0x36, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xB7}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6C, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xB7}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x59, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x0D, 0x00, 0x0D, 0x00, 0x0E, 0x00, 0x0F, 0x00, 0x0F, 0x00, 0x0E, 0x00, 0x0F, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x12, 0x00, 0x16, 0x00, 0x55, 0x02, 0x5C, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x81}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x59, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x81}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6D, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x0F, 0xF6, 0x0E, 0xE5, 0x0E, 0xB0, 0x11, 0x22, 0x11, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xF3}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6D, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xF3}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x68, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x02, 0x00, 0x00, 0x00, 0xD4, 0x86, 0x4A, 0xC6, 0xB4, 0xC2, 0x6E, 0x2B, 0x03, 0x7C, 0x01, 0x48, 0xFD, 0xA3, 0xF6, 0x75, 0x12, 0x58, 0x2D, 0xB7, 0x00, 0x00, 0x00, 0x00, 0xCB, 0xD4, 0x1A, 0x05}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA5}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x68, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA5}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x68, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x1C, 0x98, 0x02, 0xD3, 0xFF, 0xB9, 0x30, 0xEF, 0x05, 0x11, 0x05, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x3C, 0x00, 0x50, 0x3C, 0x00, 0x64, 0x3C, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xEA}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x68, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xEA}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x69, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x00, 0x0B, 0xD6, 0x7F, 0x73, 0x1A, 0x6D, 0x94, 0x0A, 0x4C, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x42}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x69, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x42}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6B, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xEF, 0x05, 0x11, 0x05, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x3C, 0x00, 0x50, 0x3C, 0x00, 0x64, 0x3C, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x5B}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6B, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x5B}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x70, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xFF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x70, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xFF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x00, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x42, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 2000}, + }, +}; +//end of const bqfs_cmd_t bqfs_image[] +#endif diff --git a/drivers/power/bq27426_fg_mmi/battery_profile/imported/lenovo_scud_df1_gmfs.h b/drivers/power/bq27426_fg_mmi/battery_profile/imported/lenovo_scud_df1_gmfs.h new file mode 100644 index 000000000000..708c130c3aeb --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/battery_profile/imported/lenovo_scud_df1_gmfs.h @@ -0,0 +1,819 @@ +/***************************************************************************** +* Copyright 2010 Texas Instruments Corporation, All Rights Reserved. +* TI makes NO WARRANTY as to software products, which are supplied "AS-IS" +*****************************************************************************/ + +/***************************************************************************** +* This code is automatically generated from bqfs/dffs file. * +* DO NOT MODIFY THIS FILE DIRECTLY * +*****************************************************************************/ + +//this file is generated from Lenovo-2660-DF1_.gm.fs at Thu Nov 28 16:22:59 2019 + +#ifndef __SCUD_BQFS_FILE__ +#define __SCUD_BQFS_FILE__ + +#include "bqfs_cmd_type.h" + +const bqfs_cmd_t scud_bqfs_image[] = { + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x01, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x26, 0x04}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x02, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x02, 0x02}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x13, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 1100}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x02, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x02, 0x26, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA5}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x02, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA5}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x24, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x19, 0x28, 0x63, 0x5F, 0xFF, 0x62, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x69}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x24, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x69}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x31, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x0A, 0x0F, 0x02, 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}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x31, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x40, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x64, 0x71, 0x0F, 0x9F, 0x23, 0x01, 0x00, 0x14, 0x04, 0x00, 0x09, 0x04, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x0D}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x40, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x0D}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x44, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x32, 0x01, 0xC2, 0x30, 0x00, 0x03, 0x08, 0x98, 0x01, 0x00, 0x3C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xF9}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x44, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xF9}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x01, 0xF4, 0x00, 0x1E, 0xC8, 0x14, 0x08, 0x00, 0x3C, 0x0E, 0x10, 0x00, 0x0A, 0x46, 0x05, 0x14, 0x05, 0x0F, 0x03, 0x20, 0x7F, 0xFF, 0x00, 0xF0, 0x46, 0x50, 0x18, 0x01, 0x90, 0x00, 0x64, 0x19}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xE4}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xE4}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xDC, 0x5C, 0x60, 0x00, 0x7D, 0x00, 0x04, 0x03, 0x19, 0x25, 0x0F, 0x14, 0x0A, 0x78, 0x60, 0x28, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x43, 0x80, 0x04, 0x01, 0x14, 0x00, 0x08, 0x0B, 0xB8, 0x01}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDB}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDB}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x02}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x2C, 0x0A, 0x01, 0x0A, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x64, 0x02, 0x00, 0x00, 0x00, 0x00, 0x07, 0xD0, 0x01, 0x03, 0x5A, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x47}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x02}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x47}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x51, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0xA7, 0x00, 0x64, 0x00, 0xFA, 0x00, 0x3C, 0x3C, 0x01, 0xB3, 0xB3, 0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x8A}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x51, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x8A}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x52, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x40, 0xB4, 0x00, 0x00, 0x00, 0x81, 0x0F, 0xA0, 0x39, 0xD0, 0x0C, 0x80, 0x00, 0xC8, 0x00, 0x32, 0x00, 0x13, 0x02, 0x32, 0x01, 0x00, 0x85, 0x00, 0x0A, 0xFF, 0xD6, 0xFF, 0xD1, 0x00, 0x01, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xCF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x52, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xCF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x26, 0x60, 0x11, 0x2D, 0xE5, 0xE5, 0xE0, 0xE1, 0xE1, 0xE1, 0xE3, 0xE4, 0xE2, 0xE6, 0xF1, 0xE6, 0xC0, 0xEA, 0xF7, 0xE6, 0xE0, 0xEA, 0xEB, 0xF1, 0xF1, 0xF6, 0xF5, 0xF7, 0xF8, 0xFA, 0xFA, 0xFC}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA0}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA0}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFB, 0xF7, 0xF7, 0xF0, 0xF5, 0xE9, 0xF8, 0xFE, 0xFB, 0xDF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xF8}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xF8}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFD, 0x6C, 0x05, 0x02, 0x01, 0x03, 0x02, 0x00, 0x02, 0xFC, 0xFB, 0x03, 0xFA, 0xEE, 0xFC, 0x4D, 0xFB, 0xFB, 0xF9, 0xEC, 0x04, 0x01, 0x01, 0x04, 0xFB, 0x00, 0x00, 0xFD, 0xFB, 0xF9, 0xE4, 0xEA}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xBD}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xBD}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFD, 0xFA, 0x07, 0xFC, 0x09, 0x0D, 0x04, 0xD8, 0xF6, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x9C}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x9C}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x55, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFF, 0x37, 0x00, 0x15, 0x07, 0xFC, 0x0A, 0x10, 0x0B, 0x01, 0xF9, 0xF5, 0xFD, 0x0D, 0x26, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x02}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x55, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x02}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6C, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFE, 0x2A, 0x01, 0x00, 0x0C, 0x1C, 0xF7, 0xFB, 0x07, 0xC5, 0xF4, 0xF9, 0xFC, 0xED, 0xFF, 0x40, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x8A}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6C, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x8A}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x59, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x14, 0x00, 0x13, 0x00, 0x14, 0x00, 0x18, 0x00, 0x14, 0x00, 0x12, 0x00, 0x15, 0x00, 0x12, 0x00, 0x12, 0x00, 0x13, 0x00, 0x15, 0x00, 0x17, 0x00, 0x1C, 0x00, 0x30, 0x00, 0x62, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x60}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x59, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x60}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6D, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x0F, 0xE4, 0x0E, 0xE3, 0x0E, 0xB7, 0x11, 0x2D, 0x10, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x0D}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6D, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x0D}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x70, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xFF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x70, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xFF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x00, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x42, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 2000}, + }, +}; +//end of const bqfs_cmd_t bqfs_image[] +#endif diff --git a/drivers/power/bq27426_fg_mmi/battery_profile/imported/smith_battery_flip_LS40_0826-bq27426G1-2766.gm.fs.h b/drivers/power/bq27426_fg_mmi/battery_profile/imported/smith_battery_flip_LS40_0826-bq27426G1-2766.gm.fs.h new file mode 100644 index 000000000000..66c27bf1efeb --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/battery_profile/imported/smith_battery_flip_LS40_0826-bq27426G1-2766.gm.fs.h @@ -0,0 +1,946 @@ +/***************************************************************************** +* Copyright 2010 Texas Instruments Corporation, All Rights Reserved. +* TI makes NO WARRANTY as to software products, which are supplied "AS-IS" +*****************************************************************************/ + +/***************************************************************************** +* This code is automatically generated from bqfs/dffs file. * +* DO NOT MODIFY THIS FILE DIRECTLY * +*****************************************************************************/ + +//this file is generated from 0426_2_02-bq27426G1-Smith-flip-config.gm.fs at Thu Aug 27 17:04:58 2020 + +#ifndef __FLIP_BQFS_FILE__ +#define __FLIP_BQFS_FILE__ + +#include "bqfs_cmd_type.h" + +const bqfs_cmd_t smith_flip_bqfs_image[] = { + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x01, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x26, 0x04}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x02, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x02, 0x02}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x13, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 1100}, + }, + // chemid start + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x27, 0x66, 0x11, 0x59, 0xE2, 0xE1, 0xE4, 0xE3, 0xE0, 0xE1, 0xDF, 0xE1, 0xE1, 0xE2, 0xE2, 0xE4, 0xE4, 0xE6, 0xE7, 0xE6, 0xE7, 0xE2, 0xE3, 0xEF, 0xEF, 0xF3, 0xF4, 0xF6, 0xF6, 0xFA, 0xF8, 0xFD}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x91}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x91}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xF9, 0xF6, 0xF5, 0xF3, 0xF0, 0xEE, 0xF3, 0xFE, 0xFC, 0xE0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xFD}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xFD}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFF, 0x4D, 0x03, 0xEA, 0x00, 0xFD, 0x00, 0x05, 0x00, 0x05, 0xFF, 0x01, 0x03, 0x01, 0x05, 0x0A, 0x0E, 0x13, 0x02, 0xFA, 0x07, 0x00, 0x09, 0x0C, 0x03, 0xF7, 0x00, 0x01, 0xF3, 0xEF, 0x9D, 0xBB}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x3E}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x3E}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xBB, 0xF2, 0x27, 0x10, 0xD3, 0x59, 0x00, 0xE2, 0x8D, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xFF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xFF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x55, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFF, 0x71, 0x02, 0x01, 0x01, 0xFF, 0xFD, 0x08, 0x01, 0x00, 0x01, 0x01, 0xFF, 0x01, 0x0F, 0x61, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x02}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x55, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x02}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6C, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFE, 0xCE, 0x02, 0x03, 0x04, 0x00, 0x12, 0xF0, 0x07, 0xEC, 0x00, 0x00, 0x00, 0xFA, 0xE7, 0xB6, 0xF7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA7}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6C, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA7}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x59, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x22, 0x00, 0x22, 0x00, 0x25, 0x00, 0x2D, 0x00, 0x38, 0x00, 0x23, 0x00, 0x2B, 0x00, 0x23, 0x00, 0x23, 0x00, 0x23, 0x00, 0x26, 0x00, 0x27, 0x00, 0x2E, 0x00, 0x42, 0x00, 0xA0, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x1D}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x59, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x1D}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6D, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x06, 0x3E, 0x0E, 0xE3, 0x0E, 0xBD, 0x11, 0x59, 0x11, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x5D}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6D, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x5D}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 1100}, + }, + // end chemid + // start main config + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x02, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x02, 0x26, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA5}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x02, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA5}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x24, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x19, 0x28, 0x63, 0x5F, 0xFF, 0x62, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x69}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x24, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x69}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x31, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x0A, 0x0F, 0x02, 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}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x31, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x40, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x64, 0x79, 0x0F, 0x9F, 0x23, 0x05, 0x00, 0x14, 0x04, 0x00, 0x09, 0x04, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x01}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x40, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x01}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x44, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x32, 0x01, 0xC2, 0x30, 0x00, 0x03, 0x08, 0x98, 0x01, 0x00, 0x3C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xF9}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x44, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xF9}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x01, 0xF4, 0x00, 0x1E, 0xC8, 0x14, 0x08, 0x00, 0x3C, 0x0E, 0x10, 0x00, 0x0A, 0x46, 0x05, 0x14, 0x05, 0x0F, 0x03, 0x20, 0x7F, 0xFF, 0x00, 0xF0, 0x46, 0x50, 0x18, 0x01, 0x90, 0x00, 0x64, 0x19}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xE4}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xE4}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xDC, 0x5C, 0x60, 0x00, 0x7D, 0x00, 0x04, 0x03, 0x19, 0x25, 0x0F, 0x14, 0x0A, 0x78, 0x60, 0x28, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x43, 0x80, 0x04, 0x01, 0x14, 0x00, 0x08, 0x0B, 0xB8, 0x01}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDB}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDB}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x02}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x2C, 0x0A, 0x01, 0x0A, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x64, 0x02, 0x00, 0x00, 0x00, 0x00, 0x07, 0xD0, 0x01, 0x03, 0x5A, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x59}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x02}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x59}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x51, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0xC1, 0x00, 0x7D, 0x01, 0x35, 0x00, 0x3C, 0x3C, 0x01, 0xB3, 0xB3, 0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x1B}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x51, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x1B}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x52, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x41, 0x53, 0x00, 0x00, 0x00, 0x81, 0x06, 0x09, 0x17, 0x5B, 0x0B, 0xB8, 0x00, 0xC8, 0x00, 0x32, 0x00, 0x14, 0x03, 0xE8, 0x01, 0x00, 0x6E, 0x00, 0x0A, 0xFF, 0xCD, 0xFF, 0xCE, 0x00, 0x01, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x9A}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x52, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x9A}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x59, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x30, 0x00, 0x30, 0x00, 0x31, 0x00, 0x38, 0x00, 0x45, 0x00, 0x2E, 0x00, 0x3C, 0x00, 0x34, 0x00, 0x36, 0x00, 0x3A, 0x00, 0x3D, 0x00, 0x3C, 0x00, 0x46, 0x00, 0x6B, 0x00, 0xFC, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xBD}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x59, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xBD}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6D, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x06, 0x3E, 0x0E, 0xE3, 0x0E, 0xBD, 0x11, 0x59, 0x11, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x5D}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6D, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x5D}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x70, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xFF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x70, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xFF}}, + .data_len = 1, + }, + // end main config + // start cc_gain + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x69, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x00, 0x0B, 0xD6, 0x7F, 0x73, 0xB6, 0x46, 0x93, 0x0A, 0xA5, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xCC}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x69, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xCC}}, + .data_len = 1, + }, + // end cc_gain + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x00, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x42, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 2000}, + }, +}; +//end of const bqfs_cmd_t smith_flip_bqfs_image[] +#endif diff --git a/drivers/power/bq27426_fg_mmi/battery_profile/imported/smith_battery_main_LS30_0729-bq27426G1-2767.gm.fs.h b/drivers/power/bq27426_fg_mmi/battery_profile/imported/smith_battery_main_LS30_0729-bq27426G1-2767.gm.fs.h new file mode 100644 index 000000000000..6c8ac824ac4a --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/battery_profile/imported/smith_battery_main_LS30_0729-bq27426G1-2767.gm.fs.h @@ -0,0 +1,946 @@ +/***************************************************************************** +* Copyright 2010 Texas Instruments Corporation, All Rights Reserved. +* TI makes NO WARRANTY as to software products, which are supplied "AS-IS" +*****************************************************************************/ + +/***************************************************************************** +* This code is automatically generated from bqfs/dffs file. * +* DO NOT MODIFY THIS FILE DIRECTLY * +*****************************************************************************/ + +//this file is generated from 0729_2_02-bq27426G1-main.gm.fs at Thu Jul 30 13:03:02 2020 + +#ifndef __MAIN_BQFS_FILE__ +#define __MAIN_BQFS_FILE__ + +#include "bqfs_cmd_type.h" + +const bqfs_cmd_t smith_main_bqfs_image[] = { + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x01, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x26, 0x04}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x02, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x02, 0x02}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x13, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 1100}, + }, + // chemid start + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x27, 0x67, 0x11, 0x59, 0xDF, 0xE0, 0xE3, 0xE2, 0xE0, 0xE0, 0xE0, 0xE0, 0xE1, 0xE3, 0xE3, 0xE4, 0xE5, 0xE7, 0xE9, 0xE4, 0xE4, 0xE5, 0xE3, 0xF0, 0xEF, 0xF5, 0xF2, 0xF7, 0xF6, 0xF9, 0xF9, 0xFA}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x94}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x94}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFC, 0xF6, 0xF6, 0xF2, 0xF1, 0xEF, 0xF2, 0xFE, 0xFA, 0xE0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xFB}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x53, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xFB}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFE, 0x86, 0x03, 0xF7, 0xED, 0x00, 0x04, 0x05, 0x0E, 0xFF, 0x10, 0x03, 0xF7, 0x05, 0x07, 0x02, 0xFE, 0x0B, 0x28, 0x23, 0x01, 0xE1, 0x00, 0x16, 0xE6, 0x1D, 0xEE, 0x00, 0xF6, 0xEB, 0xC0, 0x9C}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xEC}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xEC}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xCD, 0xDD, 0x1D, 0x22, 0xCE, 0x52, 0x0A, 0x54, 0xFA, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x1D}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x54, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x1D}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x55, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFF, 0x52, 0x02, 0x06, 0x01, 0xFE, 0xFE, 0x08, 0x03, 0xFD, 0x03, 0x01, 0xFD, 0x04, 0x13, 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x33}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x55, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x33}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6C, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xFE, 0xCA, 0x02, 0x02, 0x04, 0x01, 0x17, 0xEA, 0x06, 0xF0, 0xFE, 0x00, 0x01, 0xF9, 0xDF, 0xAD, 0xF3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xC0}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6C, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xC0}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x59, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x28, 0x00, 0x28, 0x00, 0x2C, 0x00, 0x37, 0x00, 0x46, 0x00, 0x2A, 0x00, 0x32, 0x00, 0x2A, 0x00, 0x29, 0x00, 0x29, 0x00, 0x2D, 0x00, 0x2E, 0x00, 0x37, 0x00, 0x4E, 0x00, 0xA8, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA6}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x59, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA6}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6D, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x05, 0x05, 0x0E, 0xE5, 0x0E, 0xBA, 0x11, 0x59, 0x11, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x98}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6D, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x98}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 1100}, + }, + // end chemid + // start main config + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x02, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x02, 0x26, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA5}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x02, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xA5}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x24, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x19, 0x28, 0x63, 0x5F, 0xFF, 0x62, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x69}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x24, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x69}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x31, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x0A, 0x0F, 0x02, 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}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x31, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xDF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x40, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x64, 0x79, 0x0F, 0x9F, 0x23, 0x05, 0x00, 0x14, 0x04, 0x00, 0x09, 0x04, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x01}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x40, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x01}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x44, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x32, 0x01, 0xC2, 0x30, 0x00, 0x03, 0x08, 0x98, 0x01, 0x00, 0x3C, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xF9}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x44, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xF9}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x01, 0xF4, 0x00, 0x1E, 0xC8, 0x14, 0x08, 0x00, 0x3C, 0x0E, 0x10, 0x00, 0x0A, 0x46, 0x05, 0x14, 0x05, 0x0F, 0x03, 0x20, 0x00, 0x32, 0x00, 0x64, 0x46, 0x50, 0x18, 0x01, 0x90, 0x00, 0x64, 0x19}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xBC}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xBC}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0xDC, 0x5C, 0x60, 0x00, 0x7D, 0x00, 0x04, 0x03, 0x19, 0x25, 0x0F, 0x14, 0x0A, 0x78, 0x60, 0x28, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x43, 0x80, 0x04, 0x01, 0x14, 0x00, 0x0B, 0x0B, 0xB8, 0x01}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xD8}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x01}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xD8}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x02}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x2C, 0x0A, 0x01, 0x0A, 0x00, 0x00, 0x00, 0xC8, 0x00, 0x64, 0x02, 0x00, 0x00, 0x00, 0x00, 0x07, 0xD0, 0x01, 0x03, 0x5A, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x59}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x50, 0x02}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x59}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x51, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x9D, 0x00, 0x64, 0x00, 0xFA, 0x00, 0x3C, 0x3C, 0x01, 0xB3, 0xB3, 0x01, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x94}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x51, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x94}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x52, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x40, 0x75, 0x80, 0x00, 0x00, 0x81, 0x04, 0xE7, 0x12, 0xF8, 0x0B, 0xB8, 0x00, 0xC8, 0x00, 0x32, 0x00, 0x14, 0x03, 0xE8, 0x01, 0x00, 0x9C, 0x00, 0x0A, 0xFF, 0x9F, 0xFF, 0x93, 0x00, 0x01, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xC0}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x52, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xC0}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x59, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x32, 0x00, 0x32, 0x00, 0x31, 0x00, 0x38, 0x00, 0x46, 0x00, 0x2B, 0x00, 0x36, 0x00, 0x33, 0x00, 0x35, 0x00, 0x39, 0x00, 0x3F, 0x00, 0x40, 0x00, 0x4D, 0x00, 0x72, 0x01, 0xEB, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xC0}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x59, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xC0}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6D, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x05, 0x05, 0x0E, 0xE5, 0x0E, 0xBA, 0x11, 0x59, 0x11, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x98}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x6D, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0x98}}, + .data_len = 1, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x70, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x80, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xFF}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x70, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xFF}}, + .data_len = 1, + }, + // end main config + // start cc_gain + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x69, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x40, + .data = {.bytes = {0x00, 0x00, 0x0B, 0xD6, 0x7F, 0x73, 0xB6, 0x46, 0x93, 0x0A, 0xA5, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + .data_len = 32, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xCC}}, + .data_len = 1, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 10}, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x3E, + .data = {.bytes = {0x69, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_C, + .addr = 0xAA, + .reg = 0x60, + .data = {.bytes = {0xCC}}, + .data_len = 1, + }, + // end cc_gain + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x00, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_W, + .addr = 0xAA, + .reg = 0x00, + .data = {.bytes = {0x42, 0x00}}, + .data_len = 2, + }, + { + .cmd_type = CMD_X, + .data = {.delay = 2000}, + }, +}; +//end of const smith_main_bqfs_cmd_t bqfs_image[] +#endif diff --git a/drivers/power/bq27426_fg_mmi/battery_profile/raw/0426_2_02-bq27426G1-Smith-flip-config.gm.fs b/drivers/power/bq27426_fg_mmi/battery_profile/raw/0426_2_02-bq27426G1-Smith-flip-config.gm.fs new file mode 100755 index 000000000000..0fcbf653a6cc --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/battery_profile/raw/0426_2_02-bq27426G1-Smith-flip-config.gm.fs @@ -0,0 +1,99 @@ +;-------------------------------------------------------- +;Verify Existing Firmware Version +;-------------------------------------------------------- +W: AA 00 01 00 +C: AA 00 26 04 +W: AA 00 02 00 +C: AA 00 02 02 +;-------------------------------------------------------- +;SET_CFGUPDATE +;-------------------------------------------------------- +W: AA 00 13 00 +X: 1100 +;-------------------------------------------------------- +;Data Block +;-------------------------------------------------------- +W: AA 3E 02 00 +W: AA 40 02 26 00 00 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 A5 +X: 10 +W: AA 3E 02 00 +C: AA 60 A5 +W: AA 3E 24 00 +W: AA 40 00 19 28 63 5F FF 62 00 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 69 +X: 10 +W: AA 3E 24 00 +C: AA 60 69 +W: AA 3E 31 00 +W: AA 40 0A 0F 02 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 DF +X: 10 +W: AA 3E 31 00 +C: AA 60 DF +W: AA 3E 40 00 +W: AA 40 64 78 0F 9F 23 00 00 14 04 00 09 04 26 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 07 +X: 10 +W: AA 3E 40 00 +C: AA 60 07 +W: AA 3E 44 00 +W: AA 40 00 32 01 C2 30 00 03 08 98 01 00 3C 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 F9 +X: 10 +W: AA 3E 44 00 +C: AA 60 F9 +W: AA 3E 50 00 +W: AA 40 01 F4 00 1E C8 14 08 00 3C 0E 10 00 0A 46 05 14 05 0F 03 20 7F FF 00 F0 46 50 18 01 90 00 64 19 +W: AA 60 E4 +X: 10 +W: AA 3E 50 00 +C: AA 60 E4 +W: AA 3E 50 01 +W: AA 40 DC 5C 60 00 7D 00 04 03 19 25 0F 14 0A 78 60 28 01 F4 00 00 00 00 43 80 04 01 14 00 08 0B B8 01 +W: AA 60 DB +X: 10 +W: AA 3E 50 01 +C: AA 60 DB +W: AA 3E 50 02 +W: AA 40 2C 0A 01 0A 00 00 00 C8 00 64 02 00 00 00 00 07 D0 01 03 5A 02 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 59 +X: 10 +W: AA 3E 50 02 +C: AA 60 59 +W: AA 3E 51 00 +W: AA 40 00 C1 00 7D 01 35 00 3C 3C 01 B3 B3 01 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 1B +X: 10 +W: AA 3E 51 00 +C: AA 60 1B +W: AA 3E 52 00 +W: AA 40 41 1E 00 00 00 81 06 09 17 5B 0B B8 00 C8 00 32 00 14 03 E8 01 00 60 00 0A FF A2 FF A3 00 01 00 +W: AA 60 33 +X: 10 +W: AA 3E 52 00 +C: AA 60 33 +W: AA 3E 59 00 +W: AA 40 00 29 00 29 00 2B 00 32 00 41 00 2B 00 38 00 32 00 35 00 39 00 3D 00 3D 00 49 00 79 01 20 00 00 +W: AA 60 AF +X: 10 +W: AA 3E 59 00 +C: AA 60 AF +W: AA 3E 6D 00 +W: AA 40 06 3E 0E E3 0E BD 11 59 11 27 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 5D +X: 10 +W: AA 3E 6D 00 +C: AA 60 5D +W: AA 3E 70 00 +W: AA 40 80 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 FF +X: 10 +W: AA 3E 70 00 +C: AA 60 FF +;-------------------------------------------------------- +;Exit CFGUPDATE +;-------------------------------------------------------- +W: AA 00 00 00 +W: AA 00 42 00 +X: 2000 diff --git a/drivers/power/bq27426_fg_mmi/battery_profile/raw/0426_2_02-bq27426G1-Smith-main-config.gm.fs b/drivers/power/bq27426_fg_mmi/battery_profile/raw/0426_2_02-bq27426G1-Smith-main-config.gm.fs new file mode 100755 index 000000000000..ac0236bd31c6 --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/battery_profile/raw/0426_2_02-bq27426G1-Smith-main-config.gm.fs @@ -0,0 +1,99 @@ +;-------------------------------------------------------- +;Verify Existing Firmware Version +;-------------------------------------------------------- +W: AA 00 01 00 +C: AA 00 26 04 +W: AA 00 02 00 +C: AA 00 02 02 +;-------------------------------------------------------- +;SET_CFGUPDATE +;-------------------------------------------------------- +W: AA 00 13 00 +X: 1100 +;-------------------------------------------------------- +;Data Block +;-------------------------------------------------------- +W: AA 3E 02 00 +W: AA 40 02 26 00 00 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 A5 +X: 10 +W: AA 3E 02 00 +C: AA 60 A5 +W: AA 3E 24 00 +W: AA 40 00 19 28 63 5F FF 62 00 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 69 +X: 10 +W: AA 3E 24 00 +C: AA 60 69 +W: AA 3E 31 00 +W: AA 40 0A 0F 02 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 DF +X: 10 +W: AA 3E 31 00 +C: AA 60 DF +W: AA 3E 40 00 +W: AA 40 64 78 0F 9F 23 00 00 14 04 00 09 04 26 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 07 +X: 10 +W: AA 3E 40 00 +C: AA 60 07 +W: AA 3E 44 00 +W: AA 40 00 32 01 C2 30 00 03 08 98 01 00 3C 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 F9 +X: 10 +W: AA 3E 44 00 +C: AA 60 F9 +W: AA 3E 50 00 +W: AA 40 01 F4 00 1E C8 14 08 00 3C 0E 10 00 0A 46 05 14 05 0F 03 20 00 32 00 64 46 50 18 01 90 00 64 19 +W: AA 60 BC +X: 10 +W: AA 3E 50 00 +C: AA 60 BC +W: AA 3E 50 01 +W: AA 40 DC 5C 60 00 7D 00 04 03 19 25 0F 14 0A 78 60 28 01 F4 00 00 00 00 43 80 04 01 14 00 0B 0B B8 01 +W: AA 60 D8 +X: 10 +W: AA 3E 50 01 +C: AA 60 D8 +W: AA 3E 50 02 +W: AA 40 2C 0A 01 0A 00 00 00 C8 00 64 02 00 00 00 00 07 D0 01 03 5A 02 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 59 +X: 10 +W: AA 3E 50 02 +C: AA 60 59 +W: AA 3E 51 00 +W: AA 40 00 9D 00 64 00 FA 00 3C 3C 01 B3 B3 01 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 94 +X: 10 +W: AA 3E 51 00 +C: AA 60 94 +W: AA 3E 52 00 +W: AA 40 40 75 80 00 00 81 04 E7 12 F8 0B B8 00 C8 00 32 00 14 03 E8 01 00 7D 00 0A FF B5 FF B5 00 01 00 +W: AA 60 A7 +X: 10 +W: AA 3E 52 00 +C: AA 60 A7 +W: AA 3E 59 00 +W: AA 40 00 35 00 35 00 38 00 44 00 55 00 33 00 3E 00 36 00 36 00 39 00 3F 00 40 00 4C 00 74 00 F8 00 00 +W: AA 60 77 +X: 10 +W: AA 3E 59 00 +C: AA 60 77 +W: AA 3E 6D 00 +W: AA 40 05 05 0E E5 0E BA 11 59 11 27 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 98 +X: 10 +W: AA 3E 6D 00 +C: AA 60 98 +W: AA 3E 70 00 +W: AA 40 80 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 FF +X: 10 +W: AA 3E 70 00 +C: AA 60 FF +;-------------------------------------------------------- +;Exit CFGUPDATE +;-------------------------------------------------------- +W: AA 00 00 00 +W: AA 00 42 00 +X: 2000 diff --git a/drivers/power/bq27426_fg_mmi/battery_profile/raw/0426_2_02-bq27426G1-chemid-2766.gm.fs b/drivers/power/bq27426_fg_mmi/battery_profile/raw/0426_2_02-bq27426G1-chemid-2766.gm.fs new file mode 100755 index 000000000000..641434f4097a --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/battery_profile/raw/0426_2_02-bq27426G1-chemid-2766.gm.fs @@ -0,0 +1,69 @@ +;-------------------------------------------------------- +;Verify Existing Firmware Version +;-------------------------------------------------------- +W: AA 00 01 00 +C: AA 00 26 04 +W: AA 00 02 00 +C: AA 00 02 02 +;-------------------------------------------------------- +;SET_CFGUPDATE +;-------------------------------------------------------- +W: AA 00 13 00 +X: 1100 +;-------------------------------------------------------- +;Data Block +;-------------------------------------------------------- +W: AA 3E 53 00 +W: AA 40 27 66 11 59 E2 E1 E4 E3 E0 E1 DF E1 E1 E2 E2 E4 E4 E6 E7 E6 E7 E2 E3 EF EF F3 F4 F6 F6 FA F8 FD +W: AA 60 91 +X: 10 +W: AA 3E 53 00 +C: AA 60 91 +W: AA 3E 53 01 +W: AA 40 F9 F6 F5 F3 F0 EE F3 FE FC E0 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 FD +X: 10 +W: AA 3E 53 01 +C: AA 60 FD +W: AA 3E 54 00 +W: AA 40 FF 4D 03 EA 00 FD 00 05 00 05 FF 01 03 01 05 0A 0E 13 02 FA 07 00 09 0C 03 F7 00 01 F3 EF 9D BB +W: AA 60 3E +X: 10 +W: AA 3E 54 00 +C: AA 60 3E +W: AA 3E 54 01 +W: AA 40 BB F2 27 10 D3 59 00 E2 8D 81 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 FF +X: 10 +W: AA 3E 54 01 +C: AA 60 FF +W: AA 3E 55 00 +W: AA 40 FF 71 02 01 01 FF FD 08 01 00 01 01 FF 01 0F 61 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 02 +X: 10 +W: AA 3E 55 00 +C: AA 60 02 +W: AA 3E 6C 00 +W: AA 40 FE CE 02 03 04 00 12 F0 07 EC 00 00 00 FA E7 B6 F7 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 A7 +X: 10 +W: AA 3E 6C 00 +C: AA 60 A7 +W: AA 3E 59 00 +W: AA 40 00 22 00 22 00 25 00 2D 00 38 00 23 00 2B 00 23 00 23 00 23 00 26 00 27 00 2E 00 42 00 A0 00 00 +W: AA 60 1D +X: 10 +W: AA 3E 59 00 +C: AA 60 1D +W: AA 3E 6D 00 +W: AA 40 06 3E 0E E3 0E BD 11 59 11 27 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 5D +X: 10 +W: AA 3E 6D 00 +C: AA 60 5D +;-------------------------------------------------------- +;Exit CFGUPDATE +;-------------------------------------------------------- +W: AA 00 00 00 +W: AA 00 42 00 +X: 2000 diff --git a/drivers/power/bq27426_fg_mmi/battery_profile/raw/0426_2_02-bq27426G1-chemid-2767.gm.fs b/drivers/power/bq27426_fg_mmi/battery_profile/raw/0426_2_02-bq27426G1-chemid-2767.gm.fs new file mode 100755 index 000000000000..6e52869913bf --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/battery_profile/raw/0426_2_02-bq27426G1-chemid-2767.gm.fs @@ -0,0 +1,69 @@ +;-------------------------------------------------------- +;Verify Existing Firmware Version +;-------------------------------------------------------- +W: AA 00 01 00 +C: AA 00 26 04 +W: AA 00 02 00 +C: AA 00 02 02 +;-------------------------------------------------------- +;SET_CFGUPDATE +;-------------------------------------------------------- +W: AA 00 13 00 +X: 1100 +;-------------------------------------------------------- +;Data Block +;-------------------------------------------------------- +W: AA 3E 53 00 +W: AA 40 27 67 11 59 DF E0 E3 E2 E0 E0 E0 E0 E1 E3 E3 E4 E5 E7 E9 E4 E4 E5 E3 F0 EF F5 F2 F7 F6 F9 F9 FA +W: AA 60 94 +X: 10 +W: AA 3E 53 00 +C: AA 60 94 +W: AA 3E 53 01 +W: AA 40 FC F6 F6 F2 F1 EF F2 FE FA E0 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 FB +X: 10 +W: AA 3E 53 01 +C: AA 60 FB +W: AA 3E 54 00 +W: AA 40 FE 86 03 F7 ED 00 04 05 0E FF 10 03 F7 05 07 02 FE 0B 28 23 01 E1 00 16 E6 1D EE 00 F6 EB C0 9C +W: AA 60 EC +X: 10 +W: AA 3E 54 00 +C: AA 60 EC +W: AA 3E 54 01 +W: AA 40 CD DD 1D 22 CE 52 0A 54 FA 81 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 1D +X: 10 +W: AA 3E 54 01 +C: AA 60 1D +W: AA 3E 55 00 +W: AA 40 FF 52 02 06 01 FE FE 08 03 FD 03 01 FD 04 13 45 11 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 33 +X: 10 +W: AA 3E 55 00 +C: AA 60 33 +W: AA 3E 6C 00 +W: AA 40 FE CA 02 02 04 01 17 EA 06 F0 FE 00 01 F9 DF AD F3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 C0 +X: 10 +W: AA 3E 6C 00 +C: AA 60 C0 +W: AA 3E 59 00 +W: AA 40 00 28 00 28 00 2C 00 37 00 46 00 2A 00 32 00 2A 00 29 00 29 00 2D 00 2E 00 37 00 4E 00 A8 00 00 +W: AA 60 A6 +X: 10 +W: AA 3E 59 00 +C: AA 60 A6 +W: AA 3E 6D 00 +W: AA 40 05 05 0E E5 0E BA 11 59 11 27 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 98 +X: 10 +W: AA 3E 6D 00 +C: AA 60 98 +;-------------------------------------------------------- +;Exit CFGUPDATE +;-------------------------------------------------------- +W: AA 00 00 00 +W: AA 00 42 00 +X: 2000 diff --git a/drivers/power/bq27426_fg_mmi/battery_profile/raw/0426_2_02-bq27426G1_Lenovo_4000mAh_300mATaper_NTC_3200mVEDV_ID2542_learned_x_NoConv.gm.fs b/drivers/power/bq27426_fg_mmi/battery_profile/raw/0426_2_02-bq27426G1_Lenovo_4000mAh_300mATaper_NTC_3200mVEDV_ID2542_learned_x_NoConv.gm.fs new file mode 100644 index 000000000000..f244e4818557 --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/battery_profile/raw/0426_2_02-bq27426G1_Lenovo_4000mAh_300mATaper_NTC_3200mVEDV_ID2542_learned_x_NoConv.gm.fs @@ -0,0 +1,159 @@ +;-------------------------------------------------------- +;Verify Existing Firmware Version +;-------------------------------------------------------- +W: AA 00 01 00 +C: AA 00 26 04 +W: AA 00 02 00 +C: AA 00 02 02 +;-------------------------------------------------------- +;SET_CFGUPDATE +;-------------------------------------------------------- +W: AA 00 13 00 +X: 1100 +;-------------------------------------------------------- +;Data Block +;-------------------------------------------------------- +W: AA 3E 02 00 +W: AA 40 02 26 00 00 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 A5 +X: 10 +W: AA 3E 02 00 +C: AA 60 A5 +W: AA 3E 24 00 +W: AA 40 00 19 28 63 5F FF 62 00 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 69 +X: 10 +W: AA 3E 24 00 +C: AA 60 69 +W: AA 3E 31 00 +W: AA 40 0A 0F 02 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 DF +X: 10 +W: AA 3E 31 00 +C: AA 60 DF +W: AA 3E 40 00 +W: AA 40 64 71 0F 9F 23 00 00 14 04 00 09 04 26 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 0E +X: 10 +W: AA 3E 40 00 +C: AA 60 0E +W: AA 3E 44 00 +W: AA 40 00 32 01 C2 30 00 03 08 98 01 00 3C 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 F9 +X: 10 +W: AA 3E 44 00 +C: AA 60 F9 +W: AA 3E 50 00 +W: AA 40 01 F4 00 1E C8 14 08 00 3C 0E 10 00 0A 46 05 14 05 0F 03 20 7F FF 00 F0 46 50 18 01 90 00 64 19 +W: AA 60 E4 +X: 10 +W: AA 3E 50 00 +C: AA 60 E4 +W: AA 3E 50 01 +W: AA 40 DC 5C 60 00 7D 00 04 03 19 25 0F 14 0A 78 60 28 01 F4 00 00 00 00 43 80 04 01 14 00 08 0B B8 01 +W: AA 60 DB +X: 10 +W: AA 3E 50 01 +C: AA 60 DB +W: AA 3E 50 02 +W: AA 40 2C 0A 01 0A 00 00 00 C8 00 64 02 00 00 00 00 07 D0 01 03 5A 14 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 47 +X: 10 +W: AA 3E 50 02 +C: AA 60 47 +W: AA 3E 51 00 +W: AA 40 00 A7 00 64 00 FA 00 3C 3C 01 B3 B3 01 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 8A +X: 10 +W: AA 3E 51 00 +C: AA 60 8A +W: AA 3E 52 00 +W: AA 40 41 B2 00 00 00 81 0F A0 13 60 0C 80 00 C8 00 32 00 14 03 E8 01 00 85 00 0A FF CC FF EF 00 02 00 +W: AA 60 99 +X: 10 +W: AA 3E 52 00 +C: AA 60 99 +W: AA 3E 53 00 +W: AA 40 09 EE 11 38 DB DD E0 E1 E1 E1 E1 E3 E3 E4 E6 E6 E6 E9 EB E8 E8 E5 E5 F0 F1 F5 F6 F7 F6 FB F9 FD +W: AA 60 2F +X: 10 +W: AA 3E 53 00 +C: AA 60 2F +W: AA 3E 53 01 +W: AA 40 FC F4 F7 F0 F2 EA FA FB FB DB 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 01 +X: 10 +W: AA 3E 53 01 +C: AA 60 01 +W: AA 3E 54 00 +W: AA 40 FC 0C 03 17 02 16 FE 09 0B 01 F9 07 0D F7 0F 11 07 04 0B 15 08 FE 09 01 02 F1 FA 0D F1 F3 9F A5 +W: AA 60 31 +X: 10 +W: AA 3E 54 00 +C: AA 60 31 +W: AA 3E 54 01 +W: AA 40 F8 D6 44 FA 11 FF 28 AB A0 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 F0 +X: 10 +W: AA 3E 54 01 +C: AA 60 F0 +W: AA 3E 55 00 +W: AA 40 FF 85 00 19 F3 F5 E2 36 FF 0A 02 01 E7 01 EC A3 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 5F +X: 10 +W: AA 3E 55 00 +C: AA 60 5F +W: AA 3E 6C 00 +W: AA 40 FE 75 00 09 1B 1D 61 A3 23 90 04 0A EE 0B 18 48 7F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 AE +X: 10 +W: AA 3E 6C 00 +C: AA 60 AE +W: AA 3E 59 00 +W: AA 40 00 1D 00 1C 00 1C 00 1F 00 24 00 15 00 19 00 14 00 15 00 15 00 14 00 17 00 1E 00 6A 02 EF 00 00 +W: AA 60 57 +X: 10 +W: AA 3E 59 00 +C: AA 60 57 +W: AA 3E 6D 00 +W: AA 40 0F F6 0E E5 0E B0 11 26 11 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 EF +X: 10 +W: AA 3E 6D 00 +C: AA 60 EF +W: AA 3E 68 00 +W: AA 40 02 00 00 00 D4 86 4A C6 B4 C2 6E 2B 03 7C 01 48 FD A3 F6 75 12 58 2D B7 00 00 00 00 CB D4 1A 05 +W: AA 60 A5 +X: 10 +W: AA 3E 68 00 +C: AA 60 A5 +W: AA 3E 68 01 +W: AA 40 1C 98 02 D3 FF B9 30 EF 05 11 05 01 00 00 10 01 00 3C 00 50 3C 00 64 3C 00 20 00 00 00 00 00 00 +W: AA 60 EA +X: 10 +W: AA 3E 68 01 +C: AA 60 EA +W: AA 3E 69 00 +W: AA 40 00 01 0B D6 7F 73 1A 6D 94 0A 4C 79 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 41 +X: 10 +W: AA 3E 69 00 +C: AA 60 41 +W: AA 3E 6B 00 +W: AA 40 EF 05 11 05 01 00 00 10 01 00 3C 00 50 3C 00 64 3C 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 5B +X: 10 +W: AA 3E 6B 00 +C: AA 60 5B +W: AA 3E 70 00 +W: AA 40 80 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 FF +X: 10 +W: AA 3E 70 00 +C: AA 60 FF +;-------------------------------------------------------- +;Exit CFGUPDATE +;-------------------------------------------------------- +W: AA 00 00 00 +W: AA 00 42 00 +X: 2000 diff --git a/drivers/power/bq27426_fg_mmi/battery_profile/raw/Lenovo-2660-DF1_.gm.fs b/drivers/power/bq27426_fg_mmi/battery_profile/raw/Lenovo-2660-DF1_.gm.fs new file mode 100644 index 000000000000..25dc91f6efa7 --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/battery_profile/raw/Lenovo-2660-DF1_.gm.fs @@ -0,0 +1,135 @@ +;-------------------------------------------------------- +;Verify Existing Firmware Version +;-------------------------------------------------------- +W: AA 00 01 00 +C: AA 00 26 04 +W: AA 00 02 00 +C: AA 00 02 02 +;-------------------------------------------------------- +;SET_CFGUPDATE +;-------------------------------------------------------- +W: AA 00 13 00 +X: 1100 +;-------------------------------------------------------- +;Data Block +;-------------------------------------------------------- +W: AA 3E 02 00 +W: AA 40 02 26 00 00 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 A5 +X: 10 +W: AA 3E 02 00 +C: AA 60 A5 +W: AA 3E 24 00 +W: AA 40 00 19 28 63 5F FF 62 00 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 69 +X: 10 +W: AA 3E 24 00 +C: AA 60 69 +W: AA 3E 31 00 +W: AA 40 0A 0F 02 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 DF +X: 10 +W: AA 3E 31 00 +C: AA 60 DF +W: AA 3E 40 00 +W: AA 40 64 71 0F 9F 23 01 00 14 04 00 09 04 26 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 0D +X: 10 +W: AA 3E 40 00 +C: AA 60 0D +W: AA 3E 44 00 +W: AA 40 00 32 01 C2 30 00 03 08 98 01 00 3C 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 F9 +X: 10 +W: AA 3E 44 00 +C: AA 60 F9 +W: AA 3E 50 00 +W: AA 40 01 F4 00 1E C8 14 08 00 3C 0E 10 00 0A 46 05 14 05 0F 03 20 7F FF 00 F0 46 50 18 01 90 00 64 19 +W: AA 60 E4 +X: 10 +W: AA 3E 50 00 +C: AA 60 E4 +W: AA 3E 50 01 +W: AA 40 DC 5C 60 00 7D 00 04 03 19 25 0F 14 0A 78 60 28 01 F4 00 00 00 00 43 80 04 01 14 00 08 0B B8 01 +W: AA 60 DB +X: 10 +W: AA 3E 50 01 +C: AA 60 DB +W: AA 3E 50 02 +W: AA 40 2C 0A 01 0A 00 00 00 C8 00 64 02 00 00 00 00 07 D0 01 03 5A 14 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 47 +X: 10 +W: AA 3E 50 02 +C: AA 60 47 +W: AA 3E 51 00 +W: AA 40 00 A7 00 64 00 FA 00 3C 3C 01 B3 B3 01 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 8A +X: 10 +W: AA 3E 51 00 +C: AA 60 8A +W: AA 3E 52 00 +W: AA 40 40 B4 00 00 00 81 0F A0 39 D0 0C 80 00 C8 00 32 00 13 02 32 01 00 85 00 0A FF D6 FF D1 00 01 00 +W: AA 60 CF +X: 10 +W: AA 3E 52 00 +C: AA 60 CF +W: AA 3E 53 00 +W: AA 40 26 60 11 2D E5 E5 E0 E1 E1 E1 E3 E4 E2 E6 F1 E6 C0 EA F7 E6 E0 EA EB F1 F1 F6 F5 F7 F8 FA FA FC +W: AA 60 A0 +X: 10 +W: AA 3E 53 00 +C: AA 60 A0 +W: AA 3E 53 01 +W: AA 40 FB F7 F7 F0 F5 E9 F8 FE FB DF 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 F8 +X: 10 +W: AA 3E 53 01 +C: AA 60 F8 +W: AA 3E 54 00 +W: AA 40 FD 6C 05 02 01 03 02 00 02 FC FB 03 FA EE FC 4D FB FB F9 EC 04 01 01 04 FB 00 00 FD FB F9 E4 EA +W: AA 60 BD +X: 10 +W: AA 3E 54 00 +C: AA 60 BD +W: AA 3E 54 01 +W: AA 40 FD FA 07 FC 09 0D 04 D8 F6 81 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 9C +X: 10 +W: AA 3E 54 01 +C: AA 60 9C +W: AA 3E 55 00 +W: AA 40 FF 37 00 15 07 FC 0A 10 0B 01 F9 F5 FD 0D 26 6B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 02 +X: 10 +W: AA 3E 55 00 +C: AA 60 02 +W: AA 3E 6C 00 +W: AA 40 FE 2A 01 00 0C 1C F7 FB 07 C5 F4 F9 FC ED FF 40 51 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 8A +X: 10 +W: AA 3E 6C 00 +C: AA 60 8A +W: AA 3E 59 00 +W: AA 40 00 14 00 13 00 14 00 18 00 14 00 12 00 15 00 12 00 12 00 13 00 15 00 17 00 1C 00 30 00 62 00 00 +W: AA 60 60 +X: 10 +W: AA 3E 59 00 +C: AA 60 60 +W: AA 3E 6D 00 +W: AA 40 0F E4 0E E3 0E B7 11 2D 10 FB 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 0D +X: 10 +W: AA 3E 6D 00 +C: AA 60 0D +W: AA 3E 70 00 +W: AA 40 80 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 FF +X: 10 +W: AA 3E 70 00 +C: AA 60 FF +;-------------------------------------------------------- +;Exit CFGUPDATE +;-------------------------------------------------------- +W: AA 00 00 00 +W: AA 00 42 00 +X: 2000 diff --git a/drivers/power/bq27426_fg_mmi/battery_profile/raw/LenovoRBTweak_NewRa.gg_RB0_-375_avgP_-47_DM_2.gm.fs b/drivers/power/bq27426_fg_mmi/battery_profile/raw/LenovoRBTweak_NewRa.gg_RB0_-375_avgP_-47_DM_2.gm.fs new file mode 100644 index 000000000000..8c78e15e345a --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/battery_profile/raw/LenovoRBTweak_NewRa.gg_RB0_-375_avgP_-47_DM_2.gm.fs @@ -0,0 +1,159 @@ +;-------------------------------------------------------- +;Verify Existing Firmware Version +;-------------------------------------------------------- +W: AA 00 01 00 +C: AA 00 26 04 +W: AA 00 02 00 +C: AA 00 02 02 +;-------------------------------------------------------- +;SET_CFGUPDATE +;-------------------------------------------------------- +W: AA 00 13 00 +X: 1100 +;-------------------------------------------------------- +;Data Block +;-------------------------------------------------------- +W: AA 3E 02 00 +W: AA 40 02 26 00 00 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 A5 +X: 10 +W: AA 3E 02 00 +C: AA 60 A5 +W: AA 3E 24 00 +W: AA 40 00 19 28 63 5F FF 62 00 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 69 +X: 10 +W: AA 3E 24 00 +C: AA 60 69 +W: AA 3E 31 00 +W: AA 40 0A 0F 02 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 DF +X: 10 +W: AA 3E 31 00 +C: AA 60 DF +W: AA 3E 40 00 +W: AA 40 64 71 0F 9F 23 02 00 14 04 00 09 04 26 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 0C +X: 10 +W: AA 3E 40 00 +C: AA 60 0C +W: AA 3E 44 00 +W: AA 40 00 32 01 C2 30 00 03 08 98 01 00 3C 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 F9 +X: 10 +W: AA 3E 44 00 +C: AA 60 F9 +W: AA 3E 50 00 +W: AA 40 01 F4 00 1E C8 14 08 00 3C 0E 10 00 0A 46 05 14 05 0F 03 20 7F FF 00 F0 46 50 18 01 90 00 64 19 +W: AA 60 E4 +X: 10 +W: AA 3E 50 00 +C: AA 60 E4 +W: AA 3E 50 01 +W: AA 40 DC 5C 60 00 7D 00 04 03 19 25 0F 14 0A 78 60 28 01 F4 00 00 00 00 43 80 04 01 14 00 08 0B B8 01 +W: AA 60 DB +X: 10 +W: AA 3E 50 01 +C: AA 60 DB +W: AA 3E 50 02 +W: AA 40 2C 0A 01 0A 00 00 00 C8 00 64 02 00 00 00 00 07 D0 01 03 5A 14 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 47 +X: 10 +W: AA 3E 50 02 +C: AA 60 47 +W: AA 3E 51 00 +W: AA 40 00 A7 00 64 00 FA 00 3C 3C 01 B3 B3 01 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 8A +X: 10 +W: AA 3E 51 00 +C: AA 60 8A +W: AA 3E 52 00 +W: AA 40 40 B4 00 00 00 81 0F A0 39 D0 0C 80 00 C8 00 32 00 13 02 32 01 00 85 00 0A FF D6 FF D1 00 01 00 +W: AA 60 CF +X: 10 +W: AA 3E 52 00 +C: AA 60 CF +W: AA 3E 53 00 +W: AA 40 09 EE 11 38 DB DD E0 E1 E1 E1 E1 E3 E3 E4 E6 E6 E6 E9 EB E8 E8 E5 E5 F0 F1 F5 F6 F7 F6 FB F9 FD +W: AA 60 2F +X: 10 +W: AA 3E 53 00 +C: AA 60 2F +W: AA 3E 53 01 +W: AA 40 FC F4 F7 F0 F2 EA FA FB FB DB 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 01 +X: 10 +W: AA 3E 53 01 +C: AA 60 01 +W: AA 3E 54 00 +W: AA 40 FC 0C 03 17 02 16 FE 09 0B 01 F9 07 0D F7 0F 11 07 04 0B 15 08 FE 09 01 02 F1 FA 0D F1 F3 9F A5 +W: AA 60 31 +X: 10 +W: AA 3E 54 00 +C: AA 60 31 +W: AA 3E 54 01 +W: AA 40 F8 D6 44 FA 11 FF 28 AB A0 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 F0 +X: 10 +W: AA 3E 54 01 +C: AA 60 F0 +W: AA 3E 55 00 +W: AA 40 FF 85 00 19 F3 F5 E2 36 FF 0A 02 01 E7 01 EC A3 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 5F +X: 10 +W: AA 3E 55 00 +C: AA 60 5F +W: AA 3E 6C 00 +W: AA 40 FE 89 00 00 16 16 07 EE FF ED FF F7 EF 08 12 36 7F 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 B7 +X: 10 +W: AA 3E 6C 00 +C: AA 60 B7 +W: AA 3E 59 00 +W: AA 40 00 0D 00 0D 00 0E 00 0F 00 0F 00 0E 00 0F 00 10 00 10 00 10 00 10 00 12 00 16 00 55 02 5C 00 00 +W: AA 60 81 +X: 10 +W: AA 3E 59 00 +C: AA 60 81 +W: AA 3E 6D 00 +W: AA 40 0F F6 0E E5 0E B0 11 22 11 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 F3 +X: 10 +W: AA 3E 6D 00 +C: AA 60 F3 +W: AA 3E 68 00 +W: AA 40 02 00 00 00 D4 86 4A C6 B4 C2 6E 2B 03 7C 01 48 FD A3 F6 75 12 58 2D B7 00 00 00 00 CB D4 1A 05 +W: AA 60 A5 +X: 10 +W: AA 3E 68 00 +C: AA 60 A5 +W: AA 3E 68 01 +W: AA 40 1C 98 02 D3 FF B9 30 EF 05 11 05 01 00 00 10 01 00 3C 00 50 3C 00 64 3C 00 20 00 00 00 00 00 00 +W: AA 60 EA +X: 10 +W: AA 3E 68 01 +C: AA 60 EA +W: AA 3E 69 00 +W: AA 40 00 00 0B D6 7F 73 1A 6D 94 0A 4C 79 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 42 +X: 10 +W: AA 3E 69 00 +C: AA 60 42 +W: AA 3E 6B 00 +W: AA 40 EF 05 11 05 01 00 00 10 01 00 3C 00 50 3C 00 64 3C 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 5B +X: 10 +W: AA 3E 6B 00 +C: AA 60 5B +W: AA 3E 70 00 +W: AA 40 80 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 FF +X: 10 +W: AA 3E 70 00 +C: AA 60 FF +;-------------------------------------------------------- +;Exit CFGUPDATE +;-------------------------------------------------------- +W: AA 00 00 00 +W: AA 00 42 00 +X: 2000 diff --git a/drivers/power/bq27426_fg_mmi/battery_profile/raw/Lenovo_golden_atl_4000mha-5hom.gm.fs b/drivers/power/bq27426_fg_mmi/battery_profile/raw/Lenovo_golden_atl_4000mha-5hom.gm.fs new file mode 100644 index 000000000000..1be00cabff76 --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/battery_profile/raw/Lenovo_golden_atl_4000mha-5hom.gm.fs @@ -0,0 +1,117 @@ +;-------------------------------------------------------- +;Verify Existing Firmware Version +;-------------------------------------------------------- +W: AA 00 01 00 +C: AA 00 26 04 +W: AA 00 02 00 +C: AA 00 02 02 +;-------------------------------------------------------- +;SET_CFGUPDATE +;-------------------------------------------------------- +W: AA 00 13 00 +X: 1100 +;-------------------------------------------------------- +;Data Block +;-------------------------------------------------------- +W: AA 3E 02 00 +W: AA 40 02 26 00 00 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 A5 +X: 10 +W: AA 3E 02 00 +C: AA 60 A5 +W: AA 3E 24 00 +W: AA 40 00 19 28 63 5F FF 62 00 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 69 +X: 10 +W: AA 3E 24 00 +C: AA 60 69 +W: AA 3E 31 00 +W: AA 40 0A 0F 02 05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 DF +X: 10 +W: AA 3E 31 00 +C: AA 60 DF +W: AA 3E 40 00 +W: AA 40 64 78 0F 9F 23 00 00 14 04 00 09 04 26 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 07 +X: 10 +W: AA 3E 40 00 +C: AA 60 07 +W: AA 3E 44 00 +W: AA 40 00 32 01 C2 30 00 03 08 98 01 00 3C 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 F9 +X: 10 +W: AA 3E 44 00 +C: AA 60 F9 +W: AA 3E 50 00 +W: AA 40 01 F4 00 1E C8 14 08 00 3C 0E 10 00 0A 46 05 14 05 0F 03 20 7F FF 00 F0 46 50 18 01 90 00 64 19 +W: AA 60 E4 +X: 10 +W: AA 3E 50 00 +C: AA 60 E4 +W: AA 3E 50 01 +W: AA 40 DC 5C 60 00 7D 00 04 03 19 25 0F 14 0A 78 60 28 01 F4 00 00 00 00 43 80 04 01 14 00 08 0B B8 01 +W: AA 60 DB +X: 10 +W: AA 3E 50 01 +C: AA 60 DB +W: AA 3E 50 02 +W: AA 40 2C 0A 01 0A 00 00 00 C8 00 64 02 00 00 00 00 07 D0 01 03 5A 14 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 47 +X: 10 +W: AA 3E 50 02 +C: AA 60 47 +W: AA 3E 51 00 +W: AA 40 00 A7 00 64 00 FA 00 3C 3C 01 B3 B3 01 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 8A +X: 10 +W: AA 3E 51 00 +C: AA 60 8A +W: AA 3E 52 00 +W: AA 40 40 00 00 00 00 81 05 3C 13 60 0C 80 00 C8 00 32 00 14 03 E8 01 00 64 00 0A FF CE FF CE 00 01 00 +W: AA 60 FB +X: 10 +W: AA 3E 52 00 +C: AA 60 FB +W: AA 3E 59 00 +W: AA 40 00 27 00 2F 00 33 00 40 00 30 00 2F 00 3A 00 40 00 3F 00 42 00 44 00 47 00 52 00 66 00 E8 00 00 +W: AA 60 B1 +X: 10 +W: AA 3E 59 00 +C: AA 60 B1 +W: AA 3E 6D 00 +W: AA 40 10 4C 0E CE 0E 9C 10 F4 10 9A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 6F +X: 10 +W: AA 3E 6D 00 +C: AA 60 6F +W: AA 3E 68 00 +W: AA 40 01 00 00 F5 D4 86 4A C6 B4 C2 6E 2B 03 7C 01 48 FD A3 F6 75 12 58 2D B7 00 00 00 00 CB D4 1A 05 +W: AA 60 B1 +X: 10 +W: AA 3E 68 00 +C: AA 60 B1 +W: AA 3E 69 00 +W: AA 40 00 00 0B D6 7F 76 DF A4 94 0C 71 93 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 02 +X: 10 +W: AA 3E 69 00 +C: AA 60 02 +W: AA 3E 6B 00 +W: AA 40 EF 05 11 05 01 00 00 10 01 00 3C 00 50 3C 00 64 3C 00 20 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 5B +X: 10 +W: AA 3E 6B 00 +C: AA 60 5B +W: AA 3E 70 00 +W: AA 40 80 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +W: AA 60 FF +X: 10 +W: AA 3E 70 00 +C: AA 60 FF +;-------------------------------------------------------- +;Exit CFGUPDATE +;-------------------------------------------------------- +W: AA 00 00 00 +W: AA 00 42 00 +X: 2000 diff --git a/drivers/power/bq27426_fg_mmi/bq27426_fg_mmi.c b/drivers/power/bq27426_fg_mmi/bq27426_fg_mmi.c new file mode 100644 index 000000000000..fc7c54a56d14 --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/bq27426_fg_mmi.c @@ -0,0 +1,2691 @@ +/* + * bqGauge battery driver + * + * Copyright (C) 2008 Rodolfo Giometti + * Copyright (C) 2008 Eurotech S.p.A. + * Copyright (C) 2010-2011 Lars-Peter Clausen + * Copyright (C) 2011 Pali Rohár + * + * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc. + * + * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +/* + * Datasheets: + */ +#define pr_fmt(fmt) "bq27426-[%s]: " fmt, __func__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bqfs_cmd_type.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0) +#define ENABLE_EXTENDED_BATT_PROPS +#endif + +#ifdef ATL_4000MAH_8A_BATTERY_PROFILE +#include "battery_profile/imported/lenovo_atl_df1_gmfs.h" +#endif + +#ifdef SCUD_4000MAH_8A_BATTERY_PROFILE +#include "battery_profile/imported/lenovo_scud_df1_gmfs.h" +#endif + +#ifdef ATL_LS30_1255MAH_BATTERY_PROFILE +#include "battery_profile/imported/smith_battery_main_LS30_0729-bq27426G1-2767.gm.fs.h" +#endif + +#ifdef ATL_LS40_1545MAH_BATTERY_PROFILE +#include "battery_profile/imported/smith_battery_flip_LS40_0826-bq27426G1-2766.gm.fs.h" +#endif + +#ifdef ATL_NM40_712MAH_BATTERY_PROFILE +#include "battery_profile/imported/alt_nm40_712mah_bq27426g1.gm.fs.h" +#endif + +#undef pr_debug +#define pr_debug pr_err +#undef pr_info +#define pr_info pr_err +#undef dev_dbg +#define dev_dbg dev_err + +#define mmi_fg_err(chip, fmt, ...) \ + pr_err("%s: " fmt, chip->name, \ + ##__VA_ARGS__) \ + +#define mmi_fg_info(chip, fmt, ...) \ + pr_info("%s: " fmt, chip->name, \ + ##__VA_ARGS__) \ + +#define mmi_fg_dbg(chip, reason, fmt, ...) \ + do { \ + if (*chip->debug_mask & (reason)) \ + pr_info("%s: " fmt, chip->name, \ + ##__VA_ARGS__); \ + else \ + pr_debug("%s: " fmt, chip->name, \ + ##__VA_ARGS__); \ + } while (0) + +enum print_reason { + PR_INTERRUPT = BIT(0), + PR_MISC = BIT(2), + PR_DEBUG = BIT(3), + PR_MOTO = BIT(7), +}; + +static int __debug_mask = 0; +module_param_named( + debug_mask, __debug_mask, int, S_IRUSR | S_IWUSR +); + +#define FG_HEARTBEAT_RATE 60000 +#define MONITOR_ALARM_CHECK_NS 5000000000 +#define INVALID_REG_ADDR 0xFF +#define BQFS_UPDATE_KEY 0x8F91 + +#define FG_FLAGS_OT BIT(15) +#define FG_FLAGS_UT BIT(14) +#define FG_FLAGS_FC BIT(9) +#define FG_FLAGS_CHG BIT(8) +#define FG_FLAGS_OCVTAKEN BIT(7) +#define FG_FLAGS_ITPOR BIT(5) +#define FG_FLAGS_CFGUPMODE BIT(4) +#define FG_FLAGS_BAT_DET BIT(3) +#define FG_FLAGS_SOC1 BIT(2) +#define FG_FLAGS_SOCF BIT(1) +#define FG_FLAGS_DSG BIT(0) + +enum bq_fg_reg_idx { + BQ_FG_REG_CTRL = 0, + BQ_FG_REG_TEMP, /* Battery Temperature */ + BQ_FG_REG_VOLT, /* Battery Voltage */ + BQ_FG_REG_AI, /* Average Current */ + BQ_FG_REG_FLAGS, /* Flags */ + BQ_FG_REG_TTE, /* Time to Empty */ + BQ_FG_REG_TTF, /* Time to Full */ + BQ_FG_REG_FCC, /* Full Charge Capacity */ + BQ_FG_REG_RM, /* Remaining Capacity */ + BQ_FG_REG_CC, /* Cycle Count */ + BQ_FG_REG_SOC, /* Relative State of Charge */ + BQ_FG_REG_SOH, /* State of Health */ + BQ_FG_REG_VOCV, /* OCV voltage*/ + BQ_FG_REG_DC, /* Design Capacity */ + NUM_REGS, +}; + +enum bq_fg_subcmd { + FG_SUBCMD_CTRL_STATUS = 0x0000, + FG_SUBCMD_PART_NUM = 0x0001, + FG_SUBCMD_FW_VER = 0x0002, + FG_SUBCMD_DM_CODE = 0x0004, + FG_SUBCMD_CHEM_ID = 0x0008, + FG_SUBCMD_BAT_INSERT = 0x000C, + FG_SUBCMD_BAT_REMOVE = 0x000D, + FG_SUBCMD_SET_CFGUPDATE = 0x0013, + FG_SUBCMD_SEAL = 0x0020, + FG_SUBCMD_PULSE_SOC_INT = 0x0023, + FG_SUBCMD_CHEM_A = 0x0030, + FG_SUBCMD_CHEM_B = 0x0031, + FG_SUBCMD_CHEM_C = 0x0032, + FG_SUBCMD_RESET = 0x0041, + FG_SUBCMD_SOFT_RESET = 0x0042, + FG_SUBCMD_EXIT_CFGMODE = 0x0043, +}; + +enum { + SEAL_STATE_FA, + SEAL_STATE_UNSEALED, + SEAL_STATE_SEALED, +}; + +enum bq_fg_device { + BQ27X00, + BQ27426, +}; + +enum { + UPDATE_REASON_FG_RESET = 1, + UPDATE_REASON_NEW_VERSION, + UPDATE_REASON_FORCED, +}; + +struct fg_batt_profile { + const char * batt_type_str; + const bqfs_cmd_t * bqfs_image; + u32 array_size; + u32 chem_id; + u8 dm_ver; +}; + +struct batt_chem_id { + u16 id; + u16 cmd; +}; + +static struct batt_chem_id batt_chem_id_arr[] = { + {3230, FG_SUBCMD_CHEM_A}, + {1202, FG_SUBCMD_CHEM_B}, + {3142, FG_SUBCMD_CHEM_C}, +}; + +enum temp_source { + TEMP_SOURCE_INTERNAL = 0, + TEMP_SOURCE_EXTERNAL, + TEMP_SOURCE_HOST, +}; + +static const struct fg_batt_profile bqfs_image[] = { + +#ifdef ATL_4000MAH_8A_BATTERY_PROFILE + {.batt_type_str = "KG50_ATL_4000MAH", .bqfs_image = atl_bqfs_image, .array_size = ARRAY_SIZE(atl_bqfs_image), .chem_id = 2542, .dm_ver = 2}, +#endif + +#ifdef SCUD_4000MAH_8A_BATTERY_PROFILE + {.batt_type_str = "KG50_SCUD_4000MAH", .bqfs_image = scud_bqfs_image, .array_size = ARRAY_SIZE(scud_bqfs_image), .chem_id = 9824, .dm_ver = 1}, +#endif + +#ifdef ATL_LS30_1255MAH_BATTERY_PROFILE + {.batt_type_str = "LS30_ATL_1255MAH", .bqfs_image = smith_main_bqfs_image, .array_size = ARRAY_SIZE(smith_main_bqfs_image), .chem_id = 0x2767, .dm_ver = 5}, +#endif + +#ifdef ATL_LS40_1545MAH_BATTERY_PROFILE + {.batt_type_str = "LS40_ATL_1545MAH", .bqfs_image = smith_flip_bqfs_image, .array_size = ARRAY_SIZE(smith_flip_bqfs_image), .chem_id = 0x2766, .dm_ver = 5}, +#endif + +#ifdef ATL_NM40_712MAH_BATTERY_PROFILE + {.batt_type_str = "NM40_ATL_712MAH", .bqfs_image = li_alt_flip_bqfs_image, .array_size = ARRAY_SIZE(li_alt_flip_bqfs_image), .chem_id = 0x5725, .dm_ver = 0}, +#endif + +}; + +const unsigned char *device2str[] = { + "bq27x00", + "bq27426", +}; + +const unsigned char *update_reason_str[] = { + "Reset", + "New Version", + "Force" +}; + +static char *pm_bq27426_supplied_to[] = { + "battery", +}; + +static u8 bq27426_regs[NUM_REGS] = { + 0x00, /* CONTROL */ + 0x02, /* TEMP */ + 0x04, /* VOLT */ + 0x10, /* AVG CURRENT */ + 0x06, /* FLAGS */ + 0xFF, /* Time to empty */ + 0xFF, /* Time to full */ + 0x0E, /* Full charge capacity */ + 0x0C, /* Remaining Capacity */ + 0xFF, /* CycleCount */ + 0x1C, /* State of Charge */ + 0x20, /* State of Health */ + 0x24, /* OCV voltage*/ + 0xFF, /* Design Capacity */ +}; + +struct bq_fg_chip; + +struct bq_fg_chip { + struct device *dev; + struct i2c_client *client; + const char *name; + int *debug_mask; + + struct mutex i2c_rw_lock; + struct mutex data_lock; + struct mutex update_lock; + struct mutex irq_complete; + struct regmap *regmap; + + bool irq_waiting; + bool irq_disabled; + bool resume_completed; + + int force_update; + int fw_ver; + int df_ver; + + u8 chip; + u8 regs[NUM_REGS]; + + int batt_id; + /* status tracking */ + + bool batt_present; + bool batt_fc; + bool batt_ot; + bool batt_ut; + bool batt_soc1; + bool batt_socf; + bool batt_dsg; + bool allow_chg; + bool cfg_update_mode; + bool itpor; + bool device_initial; + + int seal_state; /* 0 - Full Access, 1 - Unsealed, 2 - Sealed */ + int batt_tte; + int batt_soc; + int batt_fcc; /* Full charge capacity */ + int batt_rm; /* Remaining capacity */ + int batt_dc; /* Design Capacity */ + int batt_dc_conf; /* Design Capacity */ + int batt_volt; + int batt_temp; + int batt_curr; + int batt_vocv; + + int batt_cyclecnt; /* cycle count */ + + + struct work_struct update_work; + unsigned long last_update; + const char *batt_profile_name; + const char *batt_name; + u32 temp_source; + int ibat_polority; + + /* debug */ + int skip_reads; + int skip_writes; + + int fake_soc; + int fake_temp; + + struct dentry *debug_root; + + struct power_supply *fg_psy; + struct power_supply_desc fg_psy_desc; + + struct regulator *vdd_i2c_vreg; + struct regulator *vdd; + u32 connected_rid; + + struct delayed_work heartbeat_work; /*cycle trig heartbeat work*/ + struct workqueue_struct *heartbeat_wq; +}; + +static void fg_check_soc(struct bq_fg_chip *bq); + +static int __fg_read_byte(struct i2c_client *client, u8 reg, u8 *val) +{ + s32 ret; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) { + pr_err("i2c read byte fail: can't read from reg 0x%02X\n", reg); + return ret; + } + + *val = (u8)ret; + + return 0; +} + +static int __fg_write_byte(struct i2c_client *client, u8 reg, u8 val) +{ + s32 ret; + + ret = i2c_smbus_write_byte_data(client, reg, val); + if (ret < 0) { + pr_err("i2c write byte fail: can't write 0x%02X to reg 0x%02X\n", + val, reg); + return ret; + } + + return 0; +} + + +static int __fg_read_word(struct i2c_client *client, u8 reg, u16 *val) +{ + s32 ret; + + ret = i2c_smbus_read_word_data(client, reg); + if (ret < 0) { + pr_err("i2c read word fail: can't read from reg 0x%02X\n", reg); + return ret; + } + + *val = (u16)ret; + + return 0; +} + + +static int __fg_write_word(struct i2c_client *client, u8 reg, u16 val) +{ + s32 ret; + + ret = i2c_smbus_write_word_data(client, reg, val); + if (ret < 0) { + pr_err("i2c write word fail: can't write 0x%02X to reg 0x%02X\n", + val, reg); + return ret; + } + + return 0; +} + +static int __fg_read_block(struct i2c_client *client, u8 reg, u8 *buf, u8 len) +{ + int ret; + struct i2c_msg msg[2]; + int i; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].buf = ® + msg[0].len = 1; + + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = buf; + msg[1].len = len; + + for (i = 0; i < 3; i++) { + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (ret >= 0) + return ret; + else + msleep(5); + } + return ret; +} + +static int __fg_write_block(struct i2c_client *client, u8 reg, u8 *buf, u8 len) +{ + int ret; + struct i2c_msg msg; + u8 data[64]; + char* addr; + int i = 0; + + addr = kmalloc(sizeof(data), GFP_KERNEL); + + data[0] = reg; + memcpy(&data[1], buf, len); + memcpy(addr, data, sizeof(data)); + + msg.addr = client->addr; + msg.flags = 0; + msg.buf = addr; + msg.len = len + 1; + + for (i = 0; i < 3; i++) { + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret >= 0) { + kfree(addr); + return ret; + } + else + msleep(5); + } + + kfree(addr); + return ret; +} + + +static int fg_read_byte(struct bq_fg_chip *bq, u8 reg, u8 *val) +{ + int ret; + + if (bq->skip_reads) { + *val = 0; + return 0; + } + + mutex_lock(&bq->i2c_rw_lock); + ret = __fg_read_byte(bq->client, reg, val); + mutex_unlock(&bq->i2c_rw_lock); + + return ret; +} + +static int fg_write_byte(struct bq_fg_chip *bq, u8 reg, u8 val) +{ + int ret; + + if (bq->skip_writes) + return 0; + + mutex_lock(&bq->i2c_rw_lock); + ret = __fg_write_byte(bq->client, reg, val); + mutex_unlock(&bq->i2c_rw_lock); + + return ret; +} + +static int fg_read_word(struct bq_fg_chip *bq, u8 reg, u16 *val) +{ + int ret; + + if (bq->skip_reads) { + *val = 0; + return 0; + } + + mutex_lock(&bq->i2c_rw_lock); + ret = __fg_read_word(bq->client, reg, val); + mutex_unlock(&bq->i2c_rw_lock); + + return ret; +} + +static int fg_write_word(struct bq_fg_chip *bq, u8 reg, u16 val) +{ + int ret; + + if (bq->skip_writes) + return 0; + + mutex_lock(&bq->i2c_rw_lock); + ret = __fg_write_word(bq->client, reg, val); + mutex_unlock(&bq->i2c_rw_lock); + + return ret; +} + +static int fg_read_block(struct bq_fg_chip *bq, u8 reg, u8 *buf, u8 len) +{ + int ret; + + if (bq->skip_reads) + return 0; + mutex_lock(&bq->i2c_rw_lock); + ret = __fg_read_block(bq->client, reg, buf, len); + mutex_unlock(&bq->i2c_rw_lock); + + return ret; + +} + +static int fg_write_block(struct bq_fg_chip *bq, u8 reg, u8 *data, u8 len) +{ + int ret; + + if (bq->skip_writes) + return 0; + + mutex_lock(&bq->i2c_rw_lock); + ret = __fg_write_block(bq->client, reg, data, len); + mutex_unlock(&bq->i2c_rw_lock); + + return ret; +} + +#define CTRL_REG 0x00 + +#define FG_DFT_UNSEAL_KEY1 0x80008000 +#define FG_DFT_UNSEAL_KEY2 0x36724614 + +#define FG_DFT_UNSEAL_FA_KEY 0xFFFFFFFF + +static u8 checksum(u8 *data, u8 len) +{ + u8 i; + u16 sum = 0; + + for (i = 0; i < len; i++) + sum += data[i]; + + sum &= 0xFF; + + return (0xFF - sum); +} + +static void fg_print_buf(const char *msg, u8 *buf, u8 len) +{ + int i; + int idx = 0; + int num; + u8 strbuf[128]; + + pr_err("%s buf: ", msg); + for(i = 0; i < len; i++) { + num = sprintf(&strbuf[idx], "%02X ", buf[i]); + idx += num; + } + pr_err("%s\n", strbuf); +} + +#define TIMEOUT_INIT_COMPLETED 100 +static int fg_check_init_completed(struct bq_fg_chip *bq) +{ + int ret; + int i = 0; + u16 status; + + ret = fg_write_word(bq, bq->regs[BQ_FG_REG_CTRL], + FG_SUBCMD_CTRL_STATUS); + if (ret < 0) { + mmi_fg_err(bq, "Failed to write control status cmd, ret = %d\n", ret); + return ret; + } + + msleep(5); + + while (i++ < TIMEOUT_INIT_COMPLETED) { + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_CTRL], &status); + if (ret >= 0 && (status & 0x0080)) + return 0; + msleep(100); + } + mmi_fg_err(bq, "wait for FG INITCOMP timeout\n"); + return ret; +} + +static int fg_get_seal_state(struct bq_fg_chip *bq) +{ + int ret; + u16 status; + + ret = fg_write_word(bq, bq->regs[BQ_FG_REG_CTRL], + FG_SUBCMD_CTRL_STATUS); + if (ret < 0) { + mmi_fg_err(bq, "Failed to write control status cmd, ret = %d\n", ret); + return ret; + } + + msleep(5); + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_CTRL], &status); + if (ret < 0) { + mmi_fg_err(bq, "Failed to read control status, ret = %d\n", ret); + return ret; + } + pr_debug("control_status = 0x%04X", status); + if (status & 0x2000) + bq->seal_state = SEAL_STATE_SEALED; + else + bq->seal_state = SEAL_STATE_UNSEALED; + + return 0; +} + +static int fg_unseal(struct bq_fg_chip *bq, u32 key) +{ + int ret; + int retry = 0; + + ret = fg_get_seal_state(bq); + if (ret) + return ret; + if (bq->seal_state == SEAL_STATE_UNSEALED) + return 0; + + mmi_fg_info(bq, ":key - 0x%08X\n", key); + + ret = fg_write_word(bq, bq->regs[BQ_FG_REG_CTRL], + key & 0xFFFF); + if (ret < 0) { + mmi_fg_err(bq, "unable to write unseal key step 1, ret = %d\n", ret); + return ret; + } + + msleep(5); + + ret = fg_write_word(bq, bq->regs[BQ_FG_REG_CTRL], + (key >> 16) & 0xFFFF); + if (ret < 0) { + mmi_fg_err(bq, "unable to write unseal key step 2, ret = %d\n", ret); + return ret; + } + + msleep(100); + + while (retry++ < 10) { + fg_get_seal_state(bq); + if (bq->seal_state == SEAL_STATE_UNSEALED) { + return 0; + } + msleep(100); + } + + return -1; +} + + +int fg_unseal_fa(struct bq_fg_chip *bq, u32 key) +{ + int ret; + int retry = 0; + + mmi_fg_info(bq, ":key - %d\n", key); + + ret = fg_write_word(bq, bq->regs[BQ_FG_REG_CTRL], + key & 0xFFFF); + if (ret < 0) { + mmi_fg_err(bq, "unable to write unseal key step 1, ret = %d\n", ret); + return ret; + } + + msleep(5); + + ret = fg_write_word(bq, bq->regs[BQ_FG_REG_CTRL], + (key >> 16) & 0xFFFF); + if (ret < 0) { + mmi_fg_err(bq, "unable to write unseal key step 2, ret = %d\n", ret); + return ret; + } + + msleep(100); + + while (retry++ < 10) { + fg_get_seal_state(bq); + if (bq->seal_state == SEAL_STATE_FA) { + return 0; + } + msleep(10); + } + + return -1; +} +EXPORT_SYMBOL_GPL(fg_unseal_fa); + +static int fg_seal(struct bq_fg_chip *bq) +{ + int ret; + int retry = 0; + + fg_get_seal_state(bq); + + if (bq->seal_state == SEAL_STATE_SEALED) + return 0; + msleep(5); + ret = fg_write_word(bq, bq->regs[BQ_FG_REG_CTRL], + FG_SUBCMD_SEAL); + + if (ret < 0) { + mmi_fg_err(bq, "Failed to send seal command\n"); + return ret; + } + + msleep(100); + while (retry++ < 10) { + fg_get_seal_state(bq); + if (bq->seal_state == SEAL_STATE_SEALED) + return 0; + msleep(200); + } + + return -1; +} + +static int fg_check_cfg_update_mode(struct bq_fg_chip *bq) +{ + int ret; + u16 flags; + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_FLAGS], &flags); + if (ret < 0) { + return ret; + } + + bq->cfg_update_mode = !!(flags & FG_FLAGS_CFGUPMODE); + + return 0; + +} + +static int fg_check_itpor(struct bq_fg_chip *bq) +{ + int ret; + u16 flags; + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_FLAGS], &flags); + if (ret < 0) { + return ret; + } + + bq->itpor = !!(flags & FG_FLAGS_ITPOR); + + return 0; +} + + +static int fg_read_dm_version(struct bq_fg_chip* bq, u8 *ver) +{ + int ret; + u16 dm_code = 0; + + ret = fg_write_word(bq, bq->regs[BQ_FG_REG_CTRL], + FG_SUBCMD_DM_CODE); + if (ret < 0) { + mmi_fg_err(bq, "Failed to write control status cmd, ret = %d\n", ret); + return ret; + } + + msleep(5); + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_CTRL], &dm_code); + if (!ret) + *ver = dm_code & 0xFF; + return ret; +} + +#define CFG_UPDATE_POLLING_RETRY_LIMIT 50 +int fg_dm_pre_access(struct bq_fg_chip *bq) +{ + int ret; + int i = 0; + + ret = fg_check_init_completed(bq); + if (ret < 0) + return ret; + ret = fg_unseal(bq, FG_DFT_UNSEAL_KEY1); + if (ret < 0) + return ret; + + ret = fg_write_word(bq, bq->regs[BQ_FG_REG_CTRL], + FG_SUBCMD_SET_CFGUPDATE); + if (ret < 0) + return ret; + + msleep(10); + + while(i++ < CFG_UPDATE_POLLING_RETRY_LIMIT) { + ret = fg_check_cfg_update_mode(bq); + if (!ret && bq->cfg_update_mode) + return 0; + msleep(400); + } + + mmi_fg_err(bq, "Failed to enter cfgupdate mode\n"); + + return -1; +} +EXPORT_SYMBOL_GPL(fg_dm_pre_access); + +int fg_dm_post_access(struct bq_fg_chip *bq) +{ + int ret; + int i = 0; + + + ret = fg_write_word(bq, bq->regs[BQ_FG_REG_CTRL], + FG_SUBCMD_SOFT_RESET); + if (ret < 0) + return ret; + + msleep(100); + + while(i++ < CFG_UPDATE_POLLING_RETRY_LIMIT) { + ret = fg_check_cfg_update_mode(bq); + if (!ret && !bq->cfg_update_mode) + break; + msleep(100); + } + + if (i == CFG_UPDATE_POLLING_RETRY_LIMIT) { + mmi_fg_err(bq, "Failed to exit cfgupdate mode\n"); + return -1; + } else { + return fg_seal(bq); + } +} +EXPORT_SYMBOL_GPL(fg_dm_post_access); + +static int fg_dm_enter_cfg_mode(struct bq_fg_chip *bq) +{ + return fg_dm_pre_access(bq); +} + +int fg_dm_exit_cfg_mode(struct bq_fg_chip *bq) +{ + int ret; + int i = 0; + + + ret = fg_write_word(bq, bq->regs[BQ_FG_REG_CTRL], + FG_SUBCMD_EXIT_CFGMODE); + if (ret < 0) + return ret; + + msleep(100); + + while(i++ < CFG_UPDATE_POLLING_RETRY_LIMIT) { + ret = fg_check_cfg_update_mode(bq); + if (!ret && !bq->cfg_update_mode) + break; + msleep(100); + } + + if (i == CFG_UPDATE_POLLING_RETRY_LIMIT) { + mmi_fg_err(bq, "Failed to exit cfgupdate mode\n"); + return -1; + } else { + return fg_seal(bq); + } +} +EXPORT_SYMBOL_GPL(fg_dm_exit_cfg_mode); + + + +#define DM_ACCESS_BLOCK_DATA_CHKSUM 0x60 +#define DM_ACCESS_BLOCK_DATA_CTRL 0x61 +#define DM_ACCESS_BLOCK_DATA_CLASS 0x3E +#define DM_ACCESS_DATA_BLOCK 0x3F +#define DM_ACCESS_BLOCK_DATA 0x40 + + +int fg_dm_read_block(struct bq_fg_chip *bq, u8 classid, + u8 offset, u8 *buf) +{ + int ret; + u8 cksum_calc, cksum; + u8 blk_offset = offset >> 5; + + mmi_fg_info(bq, "subclass:%d, offset:%d\n", classid, offset); + + ret = fg_write_byte(bq, DM_ACCESS_BLOCK_DATA_CTRL, 0); + if (ret < 0) + return ret; + msleep(5); + ret = fg_write_byte(bq, DM_ACCESS_BLOCK_DATA_CLASS, classid); + if (ret < 0) + return ret; + msleep(5); + ret = fg_write_byte(bq, DM_ACCESS_DATA_BLOCK, blk_offset); + if (ret < 0) + return ret; + msleep(5); + ret = fg_read_block(bq, DM_ACCESS_BLOCK_DATA, buf, 32); + if (ret < 0) + return ret; + + fg_print_buf(__func__, buf, 32); + + msleep(5); + cksum_calc = checksum(buf, 32); + ret = fg_read_byte(bq, DM_ACCESS_BLOCK_DATA_CHKSUM, &cksum); + if (!ret && cksum_calc == cksum) + return 0; + else + return 1; +} +EXPORT_SYMBOL_GPL(fg_dm_read_block); + +int fg_dm_write_block(struct bq_fg_chip *bq, u8 classid, + u8 offset, u8 *data) +{ + int ret; + u8 cksum; + u8 buf[64]; + u8 blk_offset = offset >> 5; + + mmi_fg_info(bq, "subclass:%d, offset:%d\n", classid, offset); + + ret = fg_write_byte(bq, DM_ACCESS_BLOCK_DATA_CTRL, 0); + if (ret < 0) + return ret; + msleep(5); + + ret = fg_write_byte(bq, DM_ACCESS_BLOCK_DATA_CLASS, classid); + if (ret < 0) + return ret; + msleep(5); + + ret = fg_write_byte(bq, DM_ACCESS_DATA_BLOCK, blk_offset); + if (ret < 0) + return ret; + + msleep(5); + ret = fg_write_block(bq, DM_ACCESS_BLOCK_DATA, data, 32); + msleep(10); + fg_print_buf(__func__, data, 32); + + cksum = checksum(data, 32); + ret = fg_write_byte(bq, DM_ACCESS_BLOCK_DATA_CHKSUM, cksum); + if (ret < 0) { + mmi_fg_info(bq, "write checksum failed\n"); + return ret; + } + msleep(5); + + ret = fg_write_byte(bq, DM_ACCESS_DATA_BLOCK, blk_offset); + if (ret < 0) + return ret; + msleep(5); + ret = fg_read_block(bq, DM_ACCESS_BLOCK_DATA, buf, 32); + if (ret < 0) + return ret; + fg_print_buf(__func__, buf, 32); + if (memcmp(data, buf, 32)) { + mmi_fg_err(bq, "Error updating subclass %d offset %d\n", + classid, offset); + return 1; + } + return 0; +} +EXPORT_SYMBOL_GPL(fg_dm_write_block); + +static int fg_read_fw_version(struct bq_fg_chip *bq) +{ + + int ret; + u16 version; + + ret = fg_write_word(bq, bq->regs[BQ_FG_REG_CTRL], 0x0002); + + if (ret < 0) { + mmi_fg_err(bq, "Failed to send firmware version subcommand:%d\n", ret); + return ret; + } + + mdelay(2); + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_CTRL], &version); + if (ret < 0) { + mmi_fg_err(bq, "Failed to read firmware version:%d\n", ret); + return ret; + } + + return version; +} + +#define BPD_TEMP_THRE (-300) +static int fg_read_temperature(struct bq_fg_chip *bq); +static int fg_read_status(struct bq_fg_chip *bq) +{ + int ret; + u16 flags; + int batt_temp; + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_FLAGS], &flags); + if (ret < 0) { + return ret; + } + + batt_temp = fg_read_temperature(bq) - 2730; + mutex_lock(&bq->data_lock); + if (batt_temp > BPD_TEMP_THRE) + bq->batt_present = !!(flags & FG_FLAGS_BAT_DET); + else + bq->batt_present = false; + bq->batt_ot = !!(flags & FG_FLAGS_OT); + bq->batt_ut = !!(flags & FG_FLAGS_UT); + bq->batt_fc = !!(flags & FG_FLAGS_FC); + bq->batt_soc1 = !!(flags & FG_FLAGS_SOC1); + bq->batt_socf = !!(flags & FG_FLAGS_SOCF); + bq->batt_dsg = !!(flags & FG_FLAGS_DSG); + bq->allow_chg = !!(flags & FG_FLAGS_CHG); + bq->itpor = !!(flags & FG_FLAGS_ITPOR); + bq->cfg_update_mode = !!(flags & FG_FLAGS_CFGUPMODE); + mutex_unlock(&bq->data_lock); + + return 0; +} + + +static int fg_read_rsoc(struct bq_fg_chip *bq) +{ + int ret; + u16 soc = 0; + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_SOC], &soc); + if (ret < 0) { + mmi_fg_err(bq, "could not read RSOC, ret = %d\n", ret); + return ret; + } + + return soc; + +} + +static int fg_read_temperature(struct bq_fg_chip *bq) +{ + int ret; + u16 temp = 0; + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_TEMP], &temp); + if (ret < 0) { + mmi_fg_err(bq, "could not read temperature, ret = %d\n", ret); + return ret; + } + + return temp; + +} + +static int fg_read_ocv_voltage(struct bq_fg_chip *bq) +{ + int ret; + u16 vocv = 0; + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_VOCV], &vocv); + if (ret < 0) { + mmi_fg_err(bq, "could not read ocv voltage, ret = %d\n", ret); + return ret; + } + + return vocv; + +} + +static int fg_read_volt(struct bq_fg_chip *bq) +{ + int ret; + u16 volt = 0; + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_VOLT], &volt); + if (ret < 0) { + mmi_fg_err(bq, "could not read voltage, ret = %d\n", ret); + return ret; + } + + return volt; + +} + +static int fg_read_current(struct bq_fg_chip *bq, int *curr) +{ + int ret; + u16 avg_curr = 0; + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_AI], &avg_curr); + if (ret < 0) { + mmi_fg_err(bq, "could not read current, ret = %d\n", ret); + return ret; + } + *curr = (int)((s16)avg_curr); + + return ret; +} + +static int fg_read_fcc(struct bq_fg_chip *bq) +{ + int ret; + u16 fcc; + + if (bq->regs[BQ_FG_REG_FCC] == INVALID_REG_ADDR) { + mmi_fg_err(bq, "FCC command not supported!\n"); + return 0; + } + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_FCC], &fcc); + + if (ret < 0) { + mmi_fg_err(bq, "could not read FCC, ret=%d\n", ret); + } + + return fcc; +} + +static int fg_read_dc(struct bq_fg_chip *bq) +{ + + int ret; + u16 dc; + + if (bq->regs[BQ_FG_REG_DC] == INVALID_REG_ADDR) { + mmi_fg_err(bq, "DesignCapacity command not supported!\n"); + return 0; + } + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_DC], &dc); + + if (ret < 0) { + mmi_fg_err(bq, "could not read DC, ret=%d\n", ret); + return ret; + } + + return dc; +} + + +static int fg_read_rm(struct bq_fg_chip *bq) +{ + int ret; + u16 rm; + + if (bq->regs[BQ_FG_REG_RM] == INVALID_REG_ADDR) { + mmi_fg_err(bq, "RemainingCapacity command not supported!\n"); + return 0; + } + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_RM], &rm); + + if (ret < 0) { + mmi_fg_err(bq, "could not read DC, ret=%d\n", ret); + return ret; + } + + return rm; + +} + +static int fg_read_cyclecount(struct bq_fg_chip *bq) +{ + int ret; + u16 cc; + + if (bq->regs[BQ_FG_REG_CC] == INVALID_REG_ADDR) { + mmi_fg_err(bq, "Cycle Count not supported!\n"); + return -1; + } + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_CC], &cc); + + if (ret < 0) { + mmi_fg_err(bq, "could not read Cycle Count, ret=%d\n", ret); + return ret; + } + + return cc; +} + +static int fg_read_tte(struct bq_fg_chip *bq) +{ + int ret; + u16 tte; + + if (bq->regs[BQ_FG_REG_TTE] == INVALID_REG_ADDR) { + mmi_fg_err(bq, "Time To Empty not supported!\n"); + return -1; + } + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_TTE], &tte); + + if (ret < 0) { + mmi_fg_err(bq, "could not read Time To Empty, ret=%d\n", ret); + return ret; + } + + if (ret == 0xFFFF) + return -ENODATA; + + return tte; +} + +static int fg_get_batt_status(struct bq_fg_chip *bq) +{ + int batt_status = 0; + + fg_read_status(bq); + + if (!bq->batt_present) + batt_status = POWER_SUPPLY_STATUS_UNKNOWN; + else if (bq->batt_fc) + batt_status = POWER_SUPPLY_STATUS_FULL; + else if (bq->batt_dsg) + batt_status = POWER_SUPPLY_STATUS_DISCHARGING; + else if (bq->batt_curr > 0) + batt_status = POWER_SUPPLY_STATUS_CHARGING; + else + batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + + if (bq->batt_soc == 100 + && batt_status == POWER_SUPPLY_STATUS_NOT_CHARGING) + batt_status = POWER_SUPPLY_STATUS_FULL; + + return batt_status; +} + +static int fg_get_batt_capacity_level(struct bq_fg_chip *bq) +{ + if (!bq->batt_present) + return POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; + else if (bq->batt_fc) + return POWER_SUPPLY_CAPACITY_LEVEL_FULL; + else if (bq->batt_soc1) + return POWER_SUPPLY_CAPACITY_LEVEL_LOW; + else if (bq->batt_socf) + return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + else + return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + +} + +static int fg_get_batt_health(struct bq_fg_chip *bq) +{ + if (!bq->batt_present) + return POWER_SUPPLY_HEALTH_UNKNOWN; + else if (bq->batt_ot) + return POWER_SUPPLY_HEALTH_OVERHEAT; + else if (bq->batt_ut) + return POWER_SUPPLY_HEALTH_COLD; + else + return POWER_SUPPLY_HEALTH_GOOD; + +} + +#ifdef ENABLE_EXTENDED_BATT_PROPS +static int fg_read_soh(struct bq_fg_chip *bq) +{ + int ret; + u16 soh = 0; + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_SOH], &soh); + if (ret < 0) { + mmi_fg_err(bq, "could not read state of health, ret = %d\n", ret); + return ret; + } + + return soh; +} +#endif + +static enum power_supply_property fg_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_VOLTAGE_OCV, + POWER_SUPPLY_PROP_CHARGE_COUNTER, +#ifdef ENABLE_EXTENDED_BATT_PROPS + POWER_SUPPLY_PROP_RESISTANCE_ID, + POWER_SUPPLY_PROP_UPDATE_NOW, + POWER_SUPPLY_PROP_BATTERY_TYPE, + POWER_SUPPLY_PROP_SOH, +#endif +}; + +static int fg_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct bq_fg_chip *bq = power_supply_get_drvdata(psy); + int ret; + + mutex_lock(&bq->update_lock); + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = fg_get_batt_status(bq); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = fg_read_volt(bq); + mutex_lock(&bq->data_lock); + if (ret >= 0) + bq->batt_volt = ret; + val->intval = bq->batt_volt * 1000; + mutex_unlock(&bq->data_lock); + + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = bq->batt_present; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + mutex_lock(&bq->data_lock); + fg_read_current(bq, &bq->batt_curr); + val->intval = -bq->batt_curr * 1000 * bq->ibat_polority; + mutex_unlock(&bq->data_lock); + break; + + case POWER_SUPPLY_PROP_CAPACITY: + if (bq->fake_soc >= 0) { + val->intval = bq->fake_soc; + break; + } + fg_check_soc(bq); + mutex_lock(&bq->data_lock); + val->intval = bq->batt_soc; + mutex_unlock(&bq->data_lock); + break; + + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + val->intval = fg_get_batt_capacity_level(bq); + break; + + case POWER_SUPPLY_PROP_TEMP: + if (bq->fake_temp != -EINVAL){ + val->intval = bq->fake_temp; + break; + } + ret = fg_read_temperature(bq); + mutex_lock(&bq->data_lock); + if (ret > 0) + bq->batt_temp = ret; + val->intval = bq->batt_temp - 2730; + mutex_unlock(&bq->data_lock); + break; + + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: + ret = fg_read_tte(bq); + mutex_lock(&bq->data_lock); + if (ret >=0) + bq->batt_tte = ret; + + val->intval = bq->batt_tte; + mutex_unlock(&bq->data_lock); + break; + + case POWER_SUPPLY_PROP_CHARGE_FULL: + ret = fg_read_fcc(bq); + mutex_lock(&bq->data_lock); + if (ret > 0) + bq->batt_fcc = ret; + val->intval = bq->batt_fcc * 1000; + mutex_unlock(&bq->data_lock); + break; + + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + + if (bq->batt_dc_conf > 0) { + val->intval = bq->batt_dc_conf * 1000; + } else { + ret = fg_read_dc(bq); + mutex_lock(&bq->data_lock); + if (ret > 0) + bq->batt_dc = ret; + val->intval = bq->batt_dc * 1000; + mutex_unlock(&bq->data_lock); + } + break; + + case POWER_SUPPLY_PROP_CYCLE_COUNT: + ret = fg_read_cyclecount(bq); + mutex_lock(&bq->data_lock); + if (ret >= 0) + bq->batt_cyclecnt = ret; + val->intval = bq->batt_cyclecnt; + mutex_unlock(&bq->data_lock); + break; + + case POWER_SUPPLY_PROP_HEALTH: + val->intval = fg_get_batt_health(bq); + break; + + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO; + break; + case POWER_SUPPLY_PROP_VOLTAGE_OCV: + ret = fg_read_ocv_voltage(bq); + mutex_lock(&bq->data_lock); + if (ret >= 0) + bq->batt_vocv= ret; + val->intval = bq->batt_vocv * 1000; + mutex_unlock(&bq->data_lock); + break; + + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + ret = fg_read_rm(bq); + mutex_lock(&bq->data_lock); + if (ret >= 0) + bq->batt_rm = ret; + val->intval = bq->batt_rm * 1000; + mutex_unlock(&bq->data_lock); + break; + +#ifdef ENABLE_EXTENDED_BATT_PROPS + case POWER_SUPPLY_PROP_RESISTANCE_ID: + val->intval = bq->connected_rid ; + break; + case POWER_SUPPLY_PROP_UPDATE_NOW: + val->intval = 0; + break; + case POWER_SUPPLY_PROP_BATTERY_TYPE: + if (bqfs_image[bq->batt_id].batt_type_str) + val->strval = bqfs_image[bq->batt_id].batt_type_str; + else val->strval = "No battery type"; + break; + case POWER_SUPPLY_PROP_SOH: + val->intval = fg_read_soh(bq); + break; +#endif + default: + mutex_unlock(&bq->update_lock); + return -EINVAL; + } + mutex_unlock(&bq->update_lock); + return 0; +} +static void fg_dump_registers(struct bq_fg_chip *bq); + +static int fg_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct bq_fg_chip *bq = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_TEMP: + bq->fake_temp = val->intval; + break; + case POWER_SUPPLY_PROP_CAPACITY: + bq->fake_soc = val->intval; + power_supply_changed(bq->fg_psy); + break; +#ifdef ENABLE_EXTENDED_BATT_PROPS + case POWER_SUPPLY_PROP_UPDATE_NOW: + fg_dump_registers(bq); + break; +#endif + default: + return -EINVAL; + } + + return 0; +} + + +static int fg_prop_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + int ret; + + switch (prop) { + case POWER_SUPPLY_PROP_TEMP: + case POWER_SUPPLY_PROP_CAPACITY: +#ifdef ENABLE_EXTENDED_BATT_PROPS + case POWER_SUPPLY_PROP_UPDATE_NOW: +#endif + case POWER_SUPPLY_PROP_VOLTAGE_OCV: + ret = 1; + break; + default: + ret = 0; + break; + } + return ret; +} + +static void fg_external_power_changed(struct power_supply *psy) // TODO replace +{ +#if 0 + struct bq_fg_chip *bq = power_supply_get_drvdata(psy); + + cancel_delayed_work(&bq->monitor_work); + schedule_delayed_work(&bq->monitor_work, 0); +#endif + +} + +static int fg_psy_register(struct bq_fg_chip *bq) +{ + struct power_supply_config fg_cfg = {}; + + bq->fg_psy_desc.name = bq->batt_name; +#ifdef ENABLE_EXTENDED_BATT_PROPS + bq->fg_psy_desc.type = POWER_SUPPLY_TYPE_BMS; +#else + bq->fg_psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN; +#endif + bq->fg_psy_desc.properties = fg_props; + bq->fg_psy_desc.num_properties = ARRAY_SIZE(fg_props); + bq->fg_psy_desc.get_property = fg_get_property; + bq->fg_psy_desc.set_property = fg_set_property; + bq->fg_psy_desc.external_power_changed = fg_external_power_changed; + bq->fg_psy_desc.property_is_writeable = fg_prop_is_writeable; + + fg_cfg.drv_data = bq; + fg_cfg.of_node = bq->dev->of_node; + fg_cfg.supplied_to = pm_bq27426_supplied_to; + fg_cfg.num_supplicants = ARRAY_SIZE(pm_bq27426_supplied_to); + bq->fg_psy = power_supply_register(bq->dev, + &bq->fg_psy_desc, + &fg_cfg); + if (IS_ERR(bq->fg_psy)) { + mmi_fg_err(bq, "Couldn't register FG power supply\n"); + return PTR_ERR(bq->fg_psy); + } + + return 0; +} + +static void fg_psy_unregister(struct bq_fg_chip *bq) +{ + + power_supply_unregister(bq->fg_psy); +} + +static int fg_read_chem_id(struct bq_fg_chip *bq, u16 *chem_id) +{ + int ret; + u16 chem_code = 0; + + ret = fg_write_word(bq, bq->regs[BQ_FG_REG_CTRL], FG_SUBCMD_CHEM_ID); + if (ret < 0) { + mmi_fg_err(bq, "Failed to write chemid subcmd, ret = %d\n", ret); + return ret; + } + + msleep(5); + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_CTRL], &chem_code); + if (!ret) + *chem_id = chem_code; + return ret; +} + +int fg_change_chem_id(struct bq_fg_chip *bq, u16 new_id) +{ + int ret; + u16 old_id; + int i; + + ret = fg_write_word(bq, bq->regs[BQ_FG_REG_CTRL], FG_SUBCMD_CHEM_ID); + if (ret < 0) { + mmi_fg_err(bq, "Failed to write chemid subcmd, ret = %d\n", ret); + return ret; + } + + msleep(5); + + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_CTRL], &old_id); + if (ret < 0) { + mmi_fg_err(bq, "Failed to read control status, ret = %d\n", ret); + return ret; + } + + if (new_id == old_id) { + mmi_fg_info(bq, "new chemid is same as old one, skip change\n"); + return 0; + } + + for (i = 0; i < ARRAY_SIZE(batt_chem_id_arr); i++) { + if (new_id == batt_chem_id_arr[i].id) + break; + } + + if (i == ARRAY_SIZE(batt_chem_id_arr)) { + mmi_fg_err(bq, "not supported chem_id %x\n", new_id); + return -1; + } + + msleep(5); + ret = fg_dm_pre_access(bq); + if (ret < 0) + return ret; + msleep(5); + ret = fg_write_word(bq, bq->regs[BQ_FG_REG_CTRL], batt_chem_id_arr[i].cmd); + if (ret < 0) { + mmi_fg_err(bq, "Failed to send chem_id command, ret=%d\n", ret); + fg_dm_post_access(bq); + return ret; + } + + msleep(5); + ret = fg_dm_post_access(bq); + if (ret < 0) + return ret; + + msleep(2000); + /* Read back checm id to confirm */ + ret = fg_read_word(bq, bq->regs[BQ_FG_REG_CTRL], &old_id); + if (ret < 0) { + mmi_fg_err(bq, "Failed to read control status, ret = %d\n", ret); + return ret; + } + + if (new_id == old_id) { + mmi_fg_info(bq, "chem ID changed successfully\n"); + return 0; + } else { + return -1; + } +} +EXPORT_SYMBOL_GPL(fg_change_chem_id); + +static int fg_check_update_necessary(struct bq_fg_chip *bq) +{ + int ret; + u16 chem_id = 0xFF; + u8 dm_ver = 0xFF; + ret = fg_check_itpor(bq); + if (!ret && bq->itpor) + return UPDATE_REASON_FG_RESET; + + ret = fg_read_chem_id(bq, &chem_id); + mmi_fg_info(bq, "chem id is %x\n", chem_id); + if (!ret && chem_id != bqfs_image[bq->batt_id].chem_id) { + mmi_fg_info(bq, "dm chem id %x, different from bqfs_image chem id %x\n", + chem_id, + bqfs_image[bq->batt_id].chem_id); + return UPDATE_REASON_NEW_VERSION; + } + + ret = fg_read_dm_version(bq, &dm_ver); + mmi_fg_info(bq, "dm ver is %d\n", dm_ver); + if (!ret && dm_ver < bqfs_image[bq->batt_id].dm_ver) { + mmi_fg_info(bq, "dm ver: %d is less than bqfs_image dm ver %d\n", + dm_ver, + bqfs_image[bq->batt_id].dm_ver); + return UPDATE_REASON_NEW_VERSION; + } + + return 0; +} + +bool fg_update_bqfs_execute_cmd(struct bq_fg_chip *bq, + const bqfs_cmd_t *cmd) +{ + int ret; + int i; + u8 tmp_buf[CMD_MAX_DATA_SIZE]; + + switch (cmd->cmd_type) { + case CMD_R: + ret = fg_read_block(bq, cmd->reg, (u8 *)&cmd->data.bytes, cmd->data_len); + if (ret < 0) + return false; + else + return true; + break; + case CMD_W: + //mmi_fg_info(bq, "WRT CMD: %02X\n", cmd->reg); + ret = fg_write_block(bq, cmd->reg, (u8 *)&cmd->data.bytes, cmd->data_len); + if (ret < 0) + return false; + else + return true; + break; + case CMD_C: + if (fg_read_block(bq, cmd->reg, tmp_buf, cmd->data_len) < 0) + return false; + if (memcmp(tmp_buf, cmd->data.bytes, cmd->data_len)) { + mmi_fg_info(bq, "CMD_C failed at line %d\n", cmd->line_num); + for(i = 0; i < cmd->data_len; i++) { + mmi_fg_err(bq, "Read: %02X, Cmp:%02X", tmp_buf[i], cmd->data.bytes[i]); + } + return false; + } + + return true; + break; + case CMD_X: + mdelay(cmd->data.delay); + return true; + break; + default: + mmi_fg_err(bq, "Unsupported command at line %d\n", cmd->line_num); + return false; + } + +} +EXPORT_SYMBOL_GPL(fg_update_bqfs_execute_cmd); + +void fg_update_bqfs(struct bq_fg_chip *bq) +{ + int i; + const bqfs_cmd_t *image; + int reason = 0; + bool found_profile = false; + + if (!bq->batt_profile_name) { + mmi_fg_err(bq, "Battery profile name is not set!\n"); + return; + } + + for (i = 0; i < ARRAY_SIZE(bqfs_image); i++) { + if (strncmp(bq->batt_profile_name, bqfs_image[i].batt_type_str, + strlen(bqfs_image[i].batt_type_str)) == 0) { + bq->batt_id = i; + pr_info("Found the right battery profile: " + "chem_id %x, batt_id %d\n", + bqfs_image[i].chem_id, bq->batt_id); + found_profile = true; + break; + } + } + + if (!found_profile) { + mmi_fg_err(bq, "Couldn't find battery profile!\n"); + return; + } + + if (bq->force_update == ~BQFS_UPDATE_KEY) + reason = UPDATE_REASON_FORCED; + else + reason = fg_check_update_necessary(bq); + + if (!reason) { + mmi_fg_info(bq, "Fuel gauge update not required, ignored\n"); + return; + } + + /* TODO:if unseal, enter cfg update mode cmd sequence are in gmfs file, + no need to do explicitly */ + fg_dm_pre_access(bq); + + mmi_fg_err(bq, "Fuel gauge update required, reason:%s, " + "chem_id:%x, batt_id=%d Starting...\n", + update_reason_str[reason - 1], + bqfs_image[bq->batt_id].chem_id, + bq->batt_id); + + mutex_lock(&bq->update_lock); + image = bqfs_image[bq->batt_id].bqfs_image; + for (i = 0; i < bqfs_image[bq->batt_id].array_size; i++) { + if (!fg_update_bqfs_execute_cmd(bq, &image[i])) { + mutex_unlock(&bq->update_lock); + mmi_fg_err(bq, "Failed at command: %d\n", i); + fg_dm_post_access(bq); + return; + } + mdelay(5); + } + mutex_unlock(&bq->update_lock); + + mmi_fg_err(bq, "Done!\n"); + + /* TODO:exit cfg update mode and seal device if these are not handled in gmfs file */ + fg_dm_post_access(bq); + return; + +} +EXPORT_SYMBOL_GPL(fg_update_bqfs); + +static const u8 fg_dump_regs[] = { + 0x00, 0x02, 0x04, 0x06, + 0x08, 0x0A, 0x0C, 0x0E, + 0x10, 0x16, 0x18, 0x1A, + 0x1C, 0x1E, 0x20, 0x28, + 0x2A, 0x2C, 0x2E, 0x30, + 0x66, 0x68, 0x6C, 0x6E, + 0x70, +}; + +static const u8 debug_fg_dump_regs[] = { + 0x02, 0x04, 0x08, 0x0A, + 0x0C, 0x0E, 0x10, 0x18, + 0x1C, 0x1E, 0x20, 0x28, + 0x2A, 0x2C, 0x30, +}; + +static int show_registers(struct seq_file *m, void *data) +{ + struct bq_fg_chip *bq = m->private; + int i; + int ret; + u16 val = 0; + + for (i = 0; i < ARRAY_SIZE(fg_dump_regs); i++) { + msleep(5); + ret = fg_read_word(bq, fg_dump_regs[i], &val); + if (!ret) + seq_printf(m, "Reg[%02X] = 0x%04X\n", + fg_dump_regs[i], val); + } + return 0; +} + +static int reg_debugfs_open(struct inode *inode, struct file *file) +{ + struct bq_fg_chip *bq = inode->i_private; + + return single_open(file, show_registers, bq); +} + +static const struct file_operations reg_debugfs_ops = { + .owner = THIS_MODULE, + .open = reg_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void create_debugfs_entry(struct bq_fg_chip *bq) +{ + bq->debug_root = debugfs_create_dir(bq->name, NULL); + if (!bq->debug_root) + mmi_fg_err(bq, "Failed to create debug dir\n"); + + if (bq->debug_root) { + + debugfs_create_file("registers", S_IFREG | S_IRUGO, + bq->debug_root, bq, ®_debugfs_ops); + + debugfs_create_x32("fake_soc", + S_IFREG | S_IWUSR | S_IRUGO, + bq->debug_root, + &(bq->fake_soc)); + + debugfs_create_x32("fake_temp", + S_IFREG | S_IWUSR | S_IRUGO, + bq->debug_root, + &(bq->fake_temp)); + + debugfs_create_x32("skip_reads", + S_IFREG | S_IWUSR | S_IRUGO, + bq->debug_root, + &(bq->skip_reads)); + debugfs_create_x32("skip_writes", + S_IFREG | S_IWUSR | S_IRUGO, + bq->debug_root, + &(bq->skip_writes)); + } +} + +static ssize_t fg_attr_show_qmax_ratable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq_fg_chip *bq = i2c_get_clientdata(client); + + int ret; + u8 rd_buf[1024]; + int len; + int idx = 0; + int i; + + mutex_lock(&bq->update_lock); + ret = fg_dm_pre_access(bq); + if (ret) { + mutex_unlock(&bq->update_lock); + return 0; + } + + ret = fg_dm_read_block(bq, 82, 0, rd_buf); //Qmax, offset 0 + if (ret) { + fg_dm_post_access(bq); + mutex_unlock(&bq->update_lock); + return 0; + } + + len = sprintf(&buf[idx], "Qmax Cell 0: %d\n", (rd_buf[0] << 8) | rd_buf[1]); + idx += len; + len = sprintf(&buf[idx], "Avg I Last Run: %d\n", (short)(rd_buf[25] << 8 | rd_buf[26])); + idx += len; + len = sprintf(&buf[idx], "Avg P Last Run:%d\n", (short)(rd_buf[27] << 8 | rd_buf[28])); + idx += len; + len = sprintf(&buf[idx], "Delta Voltage:%d\n", (rd_buf[29] << 8 | rd_buf[30])); + idx += len; + + ret = fg_dm_read_block(bq, 89, 0, rd_buf); //Ra Table + if (ret) { + fg_dm_post_access(bq); + mutex_unlock(&bq->update_lock); + return idx; + } + + len = sprintf(&buf[idx], "Ra Table: "); + idx += len; + + for (i = 0; i < 29; i += 2) { + len = sprintf(&buf[idx], "%d ", rd_buf[i] << 8 | rd_buf[i+1]); + idx += len; + } + + + ret = fg_dm_read_block(bq, 109, 6, rd_buf); //V at Chg Term + if (ret) { + fg_dm_post_access(bq); + mutex_unlock(&bq->update_lock); + return idx; + } + + len = sprintf(&buf[idx], "\nV at Chg Term:%d\n", rd_buf[6] << 8 | rd_buf[7]); + idx += len; + + fg_dm_post_access(bq); + + mutex_unlock(&bq->update_lock); + + return idx; +} + +static int mmi_perform_reset(struct bq_fg_chip *bq); + +static ssize_t fg_attr_store_update(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + + struct i2c_client *client = to_i2c_client(dev); + struct bq_fg_chip *bq = i2c_get_clientdata(client); + unsigned int key = 0; + + sscanf(buf, "%x", &key); + if (key == BQFS_UPDATE_KEY) { + bq->force_update = ~key; + schedule_work(&bq->update_work); + } else if (key == 0xFF) { + mmi_perform_reset(bq); + bq->device_initial = false; + cancel_delayed_work(&bq->heartbeat_work); + queue_delayed_work(bq->heartbeat_wq, &bq->heartbeat_work, msecs_to_jiffies(100)); + } + return count; +} + +static ssize_t fg_attr_show_dmcode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq_fg_chip *bq = i2c_get_clientdata(client); + + int ret; + u8 ver; + + ret = fg_read_dm_version(bq, &ver); + if (!ret) + return sprintf(buf, "0x%02X\n", ver); + else + return sprintf(buf, "Read DM code error"); +} + +static DEVICE_ATTR(qmax_ratable, S_IRUGO, fg_attr_show_qmax_ratable, NULL); +static DEVICE_ATTR(update, S_IWUSR, NULL, fg_attr_store_update); +static DEVICE_ATTR(dmcode, S_IRUGO, fg_attr_show_dmcode, NULL); + +static struct attribute *fg_attributes[] = { + &dev_attr_qmax_ratable.attr, + &dev_attr_update.attr, + &dev_attr_dmcode.attr, + NULL, +}; + +static const struct attribute_group fg_attr_group = { + .attrs = fg_attributes, +}; + +int fg_enable_sleep(struct bq_fg_chip *bq, bool enable) +{ + + int ret; + u8 rd_buf[64]; + + memset(rd_buf, 0, 64); + mutex_lock(&bq->update_lock); + ret = fg_dm_enter_cfg_mode(bq); + if (ret) { + mutex_unlock(&bq->update_lock); + return ret; + } + + ret = fg_dm_read_block(bq, 64, 0, rd_buf); //OpConfig + if (ret) { + fg_dm_exit_cfg_mode(bq); + mutex_unlock(&bq->update_lock); + return ret; + } + + if (enable) + rd_buf[1] |=0x20; // set SLEEP bit + else + rd_buf[1] &=~0x20; // clear SLEEP bit + + + ret = fg_dm_write_block(bq, 64, 0, rd_buf); + + fg_dm_exit_cfg_mode(bq); + + mutex_unlock(&bq->update_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(fg_enable_sleep); + +int fg_set_term_voltage(struct bq_fg_chip *bq, int volt) +{ + int ret; + u8 rd_buf[64]; + + memset(rd_buf, 0, 64); + mutex_lock(&bq->update_lock); + ret = fg_dm_enter_cfg_mode(bq); + if (ret) { + mutex_unlock(&bq->update_lock); + return ret; + } + + ret = fg_dm_read_block(bq, 82, 10, rd_buf); + if (ret) { + fg_dm_exit_cfg_mode(bq); + mutex_unlock(&bq->update_lock); + return ret; + } + + rd_buf[10] = (volt >> 8) & 0xFF; + rd_buf[11] = volt & 0xFF; + + ret = fg_dm_write_block(bq, 82, 10, rd_buf); + + fg_dm_exit_cfg_mode(bq); + + mutex_unlock(&bq->update_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(fg_set_term_voltage); + +int fg_set_term_curr(struct bq_fg_chip *bq, int val) +{ + int ret = -1, curr = 0; + u8 rd_buf[64]; + + switch (val) { + case 500: + curr = 80; + break; + case 400: + curr = 100; + break; + case 300: + curr = 140; + break; + case 200: + curr = 200; + break; + default: + mmi_fg_err(bq, "Unsupported curr setting %d\n", val); + return ret; + } + + mmi_fg_info(bq, "set term curr %dmA\n", val); + memset(rd_buf, 0, 64); + mutex_lock(&bq->update_lock); + ret = fg_dm_enter_cfg_mode(bq); + if (ret) { + mutex_unlock(&bq->update_lock); + return ret; + } + + ret = fg_dm_read_block(bq, 82, 21, rd_buf); + if (ret) { + fg_dm_exit_cfg_mode(bq); + mutex_unlock(&bq->update_lock); + return ret; + } + + rd_buf[21] = (curr >> 8) & 0xFF; + rd_buf[22] = curr & 0xFF; + + ret = fg_dm_write_block(bq, 82, 21, rd_buf); + + fg_dm_exit_cfg_mode(bq); + + mutex_unlock(&bq->update_lock); + return ret; +} +EXPORT_SYMBOL_GPL(fg_set_term_curr); + +#define TEMP_SOURCE_MASK 0x03 +int fg_select_external_temp(struct bq_fg_chip *bq) +{ + int ret; + u8 rd_buf[64]; + + memset(rd_buf, 0, 64); + mutex_lock(&bq->update_lock); + ret = fg_dm_enter_cfg_mode(bq); + if (ret) { + mutex_unlock(&bq->update_lock); + return ret; + } + + ret = fg_dm_read_block(bq, 64, 0, rd_buf); //OpConfig + if (ret) { + fg_dm_exit_cfg_mode(bq); + mutex_unlock(&bq->update_lock); + return ret; + } + + rd_buf[1] &= ~TEMP_SOURCE_MASK; + rd_buf[1] |= (bq->temp_source & TEMP_SOURCE_MASK); + + ret = fg_dm_write_block(bq, 64, 0, rd_buf); + + fg_dm_exit_cfg_mode(bq); + + mutex_unlock(&bq->update_lock); + return ret; +} +EXPORT_SYMBOL_GPL(fg_select_external_temp); + +int fg_dodateoc_delta_t(struct bq_fg_chip *bq) +{ + int ret; + u8 rd_buf[64]; + + memset(rd_buf, 0, 64); + mutex_lock(&bq->update_lock); + ret = fg_dm_enter_cfg_mode(bq); + if (ret) { + mutex_unlock(&bq->update_lock); + return ret; + } + + ret = fg_dm_read_block(bq, 36, 7, rd_buf); + if (ret) { + fg_dm_exit_cfg_mode(bq); + mutex_unlock(&bq->update_lock); + return ret; + } + + rd_buf[7] = 0x00; + rd_buf[8] = 0x0A; + + ret = fg_dm_write_block(bq, 36, 7, rd_buf); + + fg_dm_exit_cfg_mode(bq); + + mutex_unlock(&bq->update_lock); + return ret; +} +EXPORT_SYMBOL_GPL(fg_dodateoc_delta_t); + +static void fg_update_bqfs_workfunc(struct work_struct *work) +{ + struct bq_fg_chip *bq = container_of(work, + struct bq_fg_chip, update_work); + + fg_update_bqfs(bq); +} + +static void fg_dump_registers(struct bq_fg_chip *bq) +{ + int i; + int ret; + u16 val; + + for (i = 0; i < ARRAY_SIZE(fg_dump_regs); i++) { + msleep(5); + ret = fg_read_word(bq, fg_dump_regs[i], &val); + } +} + +static void debug_fg_dump_registers(struct bq_fg_chip *bq) +{ + int i, ret; + u16 val; + char buf[1024]; + int desc = 0; + + if (!(*bq->debug_mask & PR_DEBUG)) + return; + + for (i = 0; i < ARRAY_SIZE(debug_fg_dump_regs); i++) { + msleep(5); + ret = fg_read_word(bq, debug_fg_dump_regs[i], &val); + if (!ret) { + desc += + sprintf(buf + desc, "Reg[%02X] = %d, ", debug_fg_dump_regs[i], val); + } + } + mmi_fg_dbg(bq, PR_DEBUG, "%s\n", buf); +} + +#define ITERM 200 +static void fg_check_soc(struct bq_fg_chip *bq) +{ + int batt_status = 0; + fg_read_status(bq); + + if (!bq->batt_present) + batt_status = POWER_SUPPLY_STATUS_UNKNOWN; + else if (bq->batt_fc) + batt_status = POWER_SUPPLY_STATUS_FULL; + else if (bq->batt_dsg) + batt_status = POWER_SUPPLY_STATUS_DISCHARGING; + else if (bq->batt_curr > 0) + batt_status = POWER_SUPPLY_STATUS_CHARGING; + else + batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + + if (bq->batt_soc == 99 + && bq->batt_curr >= 0 + && bq->batt_curr < ITERM + && (batt_status == POWER_SUPPLY_STATUS_NOT_CHARGING + || batt_status == POWER_SUPPLY_STATUS_CHARGING)) { + pr_err("Force report soc 100, batt curr %d, batt status %d\n", + bq->batt_curr, batt_status); + bq->batt_soc = 100; + } +} + +static irqreturn_t fg_irq_thread(int irq, void *dev_id) +{ + struct bq_fg_chip *bq = dev_id; + int ret = 0; + bool last_batt_present; + mmi_fg_info(bq, "------ bq27426 IRQ triggered ------\n"); + mutex_lock(&bq->irq_complete); + bq->irq_waiting = true; + if (!bq->resume_completed) { + mmi_fg_info(bq, "IRQ triggered before device resume\n"); + if (!bq->irq_disabled) { + disable_irq_nosync(irq); + bq->irq_disabled = true; + } + mutex_unlock(&bq->irq_complete); + return IRQ_HANDLED; + } + bq->irq_waiting = false; + + last_batt_present = bq->batt_present; + + mutex_lock(&bq->update_lock); + fg_read_status(bq); + mutex_unlock(&bq->update_lock); + + fg_dump_registers(bq); + debug_fg_dump_registers(bq); + mmi_fg_info(bq, "itpor=%d, cfg_mode = %d, " + "seal_state=%d, batt_present=%d", + bq->itpor, bq->cfg_update_mode, + bq->seal_state, bq->batt_present); + + if (!last_batt_present && bq->batt_present ) {/* battery inserted */ + mmi_fg_info(bq, "Battery inserted\n"); + } else if (last_batt_present && !bq->batt_present) {/* battery removed */ + mmi_fg_info(bq, "Battery removed\n"); + bq->batt_soc = -ENODATA; + bq->batt_fcc = -ENODATA; + bq->batt_rm = -ENODATA; + bq->batt_volt = -ENODATA; + bq->batt_curr = -ENODATA; + bq->batt_temp = -ENODATA; + bq->batt_cyclecnt = -ENODATA; + } + + if (bq->batt_present) { + mutex_lock(&bq->update_lock); + + ret = fg_read_rsoc(bq); + if (ret >= 0) + bq->batt_soc = ret; + bq->batt_volt = fg_read_volt(bq); + fg_read_current(bq, &bq->batt_curr); + bq->batt_temp = fg_read_temperature(bq); + bq->batt_rm = fg_read_rm(bq); + + mutex_unlock(&bq->update_lock); + mmi_fg_err(bq, "RSOC:%d, Volt:%d, Current:%d, " + "Temperature:%d, connected_rid = %d\n", + bq->batt_soc, bq->batt_volt, + bq->batt_curr, bq->batt_temp - 2730, + bq->connected_rid); + } + + power_supply_changed(bq->fg_psy); + mutex_unlock(&bq->irq_complete); + + return IRQ_HANDLED; +} + + +static void determine_initial_status(struct bq_fg_chip *bq) +{ + if (bq->device_initial) + return; + + fg_update_bqfs(bq); + fg_select_external_temp(bq); + fg_irq_thread(bq->client->irq, bq); + bq->device_initial = true; + mmi_fg_info(bq, "Device initialization successfully completed!\n"); +} + +#define SMB_VTG_MIN_UV 1800000 +#define SMB_VTG_MAX_UV 1800000 + +static int bq_parse_dt(struct bq_fg_chip *bq) +{ + struct device_node *node = bq->dev->of_node; + int rc = 0; + + if (!node) + return -1; + + rc = of_property_read_string(node, "mmi,fg-psy-name", &bq->batt_name); + if (rc) + bq->batt_name = "fg_battery"; + bq->name = bq->batt_name; + + rc = of_property_read_string(node, "mmi,batt-profile-name", &bq->batt_profile_name); + if (rc) { + mmi_fg_err(bq, "battery profile name is not specified in dts\n"); + bq->batt_profile_name = NULL; + return rc; + } + + rc = of_property_read_u32(node, "design-capacity", &bq->batt_dc_conf); + if (rc < 0) { + bq->batt_dc_conf = 0; + rc = 0; + } + + rc = of_property_read_u32(node, "mmi,temp-source", &bq->temp_source); + if (rc < 0) { + bq->temp_source = TEMP_SOURCE_EXTERNAL; + rc = 0; + } + + if (of_property_read_bool(node, "mmi,ibat-invert-polority")) + bq->ibat_polority = -1; + else + bq->ibat_polority = 1; + + return rc; +} + +static int mmi_perform_reset(struct bq_fg_chip *bq) +{ + int rc = -1; + + fg_get_seal_state(bq); + + if (bq->seal_state != SEAL_STATE_UNSEALED) { + mmi_fg_info(bq, "Full fuel gauge reset requested - un-sealing fuel gauge\n"); + rc = fg_unseal(bq, FG_DFT_UNSEAL_KEY1); + if (rc < 0) { + mmi_fg_info(bq, "Unable to un-seal fuel gauge\n"); + return rc; + } + } + + mmi_fg_info(bq, "Performing full fuel gauge reset\n"); + rc = fg_write_word(bq, bq->regs[BQ_FG_REG_CTRL], FG_SUBCMD_RESET); + if (rc < 0) + mmi_fg_info(bq, "Unable to reset fuel gauge\n"); + + return rc; +} + +static const struct regmap_config bq27426_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0xFFFF, +}; + +static void bq_heartbeat_work(struct work_struct *work) +{ + struct bq_fg_chip *chip = container_of(work, + struct bq_fg_chip, heartbeat_work.work); + determine_initial_status(chip); + debug_fg_dump_registers(chip); + queue_delayed_work(chip->heartbeat_wq, + &chip->heartbeat_work, + msecs_to_jiffies(FG_HEARTBEAT_RATE)); +} + +static int bq_fg_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct bq_fg_chip *bq; + u8 *regs; + pr_err("--- bq27426 probe--\n"); + + bq = devm_kzalloc(&client->dev, sizeof(struct bq_fg_chip), GFP_KERNEL); + if (!bq) { + pr_err("Could not allocate memory\n"); + return -ENOMEM; + } + + bq->dev = &client->dev; + bq->client = client; + bq->chip = id->driver_data; + + bq->batt_soc = -ENODATA; + bq->batt_fcc = -ENODATA; + bq->batt_rm = -ENODATA; + bq->batt_dc = -ENODATA; + bq->batt_volt = -ENODATA; + bq->batt_temp = -ENODATA; + bq->batt_curr = -ENODATA; + bq->batt_cyclecnt = -ENODATA; + + bq->fake_soc = -EINVAL; + bq->fake_temp = -EINVAL; + + if (bq->chip == BQ27426) { + regs = bq27426_regs; + } else { + pr_err("unexpected fuel gauge: %d\n", bq->chip); + regs = bq27426_regs; + } + + bq->name = "bq27426_bms"; + bq->debug_mask = &__debug_mask; + + memcpy(bq->regs, regs, NUM_REGS); + + i2c_set_clientdata(client, bq); + + mutex_init(&bq->i2c_rw_lock); + mutex_init(&bq->data_lock); + mutex_init(&bq->update_lock); + mutex_init(&bq->irq_complete); + + bq->resume_completed = true; + bq->irq_waiting = false; + + bq->regmap = devm_regmap_init_i2c(client, &bq27426_regmap_config); + if (IS_ERR(bq->regmap)) { + pr_err("Couldn't initialize register regmap rc = %ld\n", + PTR_ERR(bq->regmap)); + ret = PTR_ERR(bq->regmap); + goto free_mem; + } + + ret = bq_parse_dt(bq); + if (ret < 0) { + dev_err(&client->dev, "Unable to parse DT nodes\n"); + goto free_mem; + } + + bq->vdd_i2c_vreg = devm_regulator_get_optional(&client->dev, "vdd-i2c"); + if (IS_ERR_OR_NULL(bq->vdd_i2c_vreg)) { + dev_err(&client->dev, "Could not get vdd-i2c power regulator\n"); + } else { + ret = regulator_enable(bq->vdd_i2c_vreg); + if (ret) + dev_err(&client->dev, "Could not enable vdd-i2c power regulator\n"); + } + + ret = fg_read_fw_version(bq); + if (ret < 0) { + mmi_fg_err(bq, "could not read fg version (maybe no fg connected?) ret = %d\n", ret); + ret = -EPROBE_DEFER; // Queue probe to try again + goto free_mem; + } + bq->fw_ver = ret; + + bq->heartbeat_wq = alloc_workqueue("%s", WQ_UNBOUND, 1, bq->name); + if (!bq->heartbeat_wq) { + dev_err(&client->dev, "Could not create workqueue\n"); + goto free_mem; + } + + //mmi_fg_err(bq, "I2C adapter is %s\n", dev_name(&bq->client->adapter->dev)); + INIT_WORK(&bq->update_work, fg_update_bqfs_workfunc); + + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, + fg_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + bq->batt_name, bq); + if (ret < 0) { + mmi_fg_err(bq, "request irq for irq=%d failed, ret = %d\n", client->irq, ret); + goto free_irq; + } + enable_irq_wake(client->irq); + } + + device_init_wakeup(bq->dev, 1); + fg_psy_register(bq); + + create_debugfs_entry(bq); + ret = sysfs_create_group(&bq->dev->kobj, &fg_attr_group); + if (ret) { + mmi_fg_err(bq, "Failed to register sysfs, err:%d\n", ret); + goto free_psy; + } + + fg_irq_thread(bq->client->irq, bq); + INIT_DELAYED_WORK(&bq->heartbeat_work, bq_heartbeat_work); + queue_delayed_work(bq->heartbeat_wq, + &bq->heartbeat_work, + msecs_to_jiffies(1000)); + power_supply_changed(bq->fg_psy); + mmi_fg_err(bq, "bq fuel gauge probe successfully, %s FW ver:%d\n", + device2str[bq->chip], bq->fw_ver); + + return 0; +free_psy: + fg_psy_unregister(bq); +free_irq: + devm_free_irq(&client->dev,client->irq,bq); +free_mem: + devm_kfree(bq->dev, bq); + return ret; +} + +static inline bool is_device_suspended(struct bq_fg_chip *bq) +{ + return !bq->resume_completed; +} + +static int bq_fg_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq_fg_chip *bq = i2c_get_clientdata(client); + + mutex_lock(&bq->irq_complete); + bq->resume_completed = false; + mutex_unlock(&bq->irq_complete); + + return 0; +} + +static int bq_fg_suspend_noirq(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq_fg_chip *bq = i2c_get_clientdata(client); + + if (bq->irq_waiting) { + pr_err_ratelimited("Aborting suspend, an interrupt was" + "detected while suspending\n"); + return -EBUSY; + } + return 0; +} + +static int bq_fg_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct bq_fg_chip *bq = i2c_get_clientdata(client); + + mutex_lock(&bq->irq_complete); + bq->resume_completed = true; + if (bq->irq_waiting) { + bq->irq_disabled = false; + enable_irq(client->irq); + mutex_unlock(&bq->irq_complete); + fg_irq_thread(client->irq, bq); + } else { + mutex_unlock(&bq->irq_complete); + } + + power_supply_changed(bq->fg_psy); + + return 0; +} + +static int bq_fg_remove(struct i2c_client *client) +{ + struct bq_fg_chip *bq = i2c_get_clientdata(client); + + fg_psy_unregister(bq); + + mutex_destroy(&bq->data_lock); + mutex_destroy(&bq->i2c_rw_lock); + mutex_destroy(&bq->update_lock); + mutex_destroy(&bq->irq_complete); + + debugfs_remove_recursive(bq->debug_root); + sysfs_remove_group(&bq->dev->kobj, &fg_attr_group); + + if (!IS_ERR_OR_NULL(bq->vdd_i2c_vreg)) { + if (regulator_is_enabled(bq->vdd_i2c_vreg)) + regulator_disable(bq->vdd_i2c_vreg); + devm_regulator_put(bq->vdd_i2c_vreg); + } + + return 0; +} + +static void bq_fg_shutdown(struct i2c_client *client) +{ + pr_info("bq fuel gauge driver shutdown!\n"); +} + +static struct of_device_id bq_fg_match_table[] = { + {.compatible = "ti,bq27426",}, + {}, +}; +MODULE_DEVICE_TABLE(of,bq_fg_match_table); + +static const struct i2c_device_id bq_fg_id[] = { + { "bq27426", BQ27426 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, bq_fg_id); + +static const struct dev_pm_ops bq_fg_pm_ops = { + .resume = bq_fg_resume, + .suspend_noirq = bq_fg_suspend_noirq, + .suspend = bq_fg_suspend, +}; + +static struct i2c_driver bq_fg_driver = { + .driver = { + .name = "bq27426_fg_mmi", + .owner = THIS_MODULE, + .of_match_table = bq_fg_match_table, + .pm = &bq_fg_pm_ops, + }, + .id_table = bq_fg_id, + + .probe = bq_fg_probe, + .remove = bq_fg_remove, + .shutdown = bq_fg_shutdown, + +}; + +module_i2c_driver(bq_fg_driver); + +MODULE_DESCRIPTION("TI BQ2742x Gauge Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Texas Instruments"); diff --git a/drivers/power/bq27426_fg_mmi/bqfs_cmd_type.h b/drivers/power/bq27426_fg_mmi/bqfs_cmd_type.h new file mode 100644 index 000000000000..311779ec6b5c --- /dev/null +++ b/drivers/power/bq27426_fg_mmi/bqfs_cmd_type.h @@ -0,0 +1,42 @@ + + +#ifndef __BQFS_CMD_TYPE__ +#define __BQFS_CMD_TYPE__ + + +#define CMD_MAX_DATA_SIZE 110 +#define RETRY_LIMIT 3 +#define CMD_RETRY_DELAY 100 /* in ms */ + +#ifdef __GNUC__ +#define __PACKED __attribute__((packed)) +#else +#error "Make sure structure cmd_t is packed" +#endif + +typedef enum { + CMD_INVALID = 0, + CMD_R, /* Read */ + CMD_W, /* Write */ + CMD_C, /* Compare */ + CMD_X, /* Delay */ +} cmd_type_t; + +/* + * DO NOT change the order of fields - particularly reg + * should be immediately followed by data + */ +typedef struct { + cmd_type_t cmd_type; + u8 addr; + u8 reg; + union { + u8 bytes[CMD_MAX_DATA_SIZE + 1]; + u16 delay; + } data; + u8 data_len; + u16 line_num; +} __PACKED bqfs_cmd_t; + + +#endif diff --git a/drivers/power/cps4019_wls_charger/Android.mk b/drivers/power/cps4019_wls_charger/Android.mk new file mode 100644 index 000000000000..aa462ae2f999 --- /dev/null +++ b/drivers/power/cps4019_wls_charger/Android.mk @@ -0,0 +1,19 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := cps4019_wls_charger.ko + +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_discrete_charger_class.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/cps4019_wls_charger/Kbuild b/drivers/power/cps4019_wls_charger/Kbuild new file mode 100644 index 000000000000..d427e8a8df92 --- /dev/null +++ b/drivers/power/cps4019_wls_charger/Kbuild @@ -0,0 +1,8 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/drivers/power/cps4019_wls_charger + +obj-m += cps4019_wls_charger.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_discrete_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/cps4019_wls_charger/Kconfig b/drivers/power/cps4019_wls_charger/Kconfig new file mode 100644 index 000000000000..070c1603c865 --- /dev/null +++ b/drivers/power/cps4019_wls_charger/Kconfig @@ -0,0 +1,10 @@ +menu "Qualcomm Technologies Inc Charger and Fuel Gauge support" + +config CPS_WLS_CHARGER + bool "CPS wireless charger driver" + depends on POWER_SUPPLY + depends on I2C + help + Say Y to enable support for CPS wireless charger. + +endmenu diff --git a/drivers/power/cps4019_wls_charger/Makefile b/drivers/power/cps4019_wls_charger/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/power/cps4019_wls_charger/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/power/cps4019_wls_charger/cps4019_bl.h b/drivers/power/cps4019_wls_charger/cps4019_bl.h new file mode 100644 index 000000000000..9b5ab26a93e4 --- /dev/null +++ b/drivers/power/cps4019_wls_charger/cps4019_bl.h @@ -0,0 +1,268 @@ +#ifndef __CPS4019_BL__ +#define __CPS4019_BL__ + +#define CPS4019_BL_SIZE 0x800 + +const unsigned char* CPS4019_BL_VER = "00_3A_V0.2_CRC5B31"; + +unsigned char CPS4019_BL[CPS4019_BL_SIZE] = { + // CPS4019_BL_00_3A_V0.2_CRC5B31 + 0x02, 0x00, 0x99, 0xEF, 0x8D, 0xF0, 0xA4, 0xA8, + 0xF0, 0xCF, 0x8C, 0xF0, 0xA4, 0x28, 0xCE, 0x8D, + 0xF0, 0xA4, 0x2E, 0xFE, 0x22, 0xEB, 0x9F, 0xF5, + 0xF0, 0xEA, 0x9E, 0x42, 0xF0, 0xE9, 0x9D, 0x42, + 0xF0, 0xEC, 0x64, 0x80, 0xC8, 0x64, 0x80, 0x98, + 0x45, 0xF0, 0x22, 0xEB, 0x9F, 0xF5, 0xF0, 0xEA, + 0x9E, 0x42, 0xF0, 0xE9, 0x9D, 0x42, 0xF0, 0xE8, + 0x9C, 0x45, 0xF0, 0x22, 0xE4, 0x93, 0xFC, 0x74, + 0x01, 0x93, 0xFD, 0x74, 0x02, 0x93, 0xFE, 0x74, + 0x03, 0x93, 0xFF, 0x22, 0xE4, 0x93, 0xF8, 0x74, + 0x01, 0x93, 0xF9, 0x74, 0x02, 0x93, 0xFA, 0x74, + 0x03, 0x93, 0xFB, 0x22, 0xEC, 0xF0, 0xA3, 0xED, + 0xF0, 0xA3, 0xEE, 0xF0, 0xA3, 0xEF, 0xF0, 0x22, + 0xA8, 0x82, 0x85, 0x83, 0xF0, 0xD0, 0x83, 0xD0, + 0x82, 0x12, 0x00, 0x7F, 0x12, 0x00, 0x7F, 0x12, + 0x00, 0x7F, 0x12, 0x00, 0x7F, 0xE4, 0x73, 0xE4, + 0x93, 0xA3, 0xC5, 0x83, 0xC5, 0xF0, 0xC5, 0x83, + 0xC8, 0xC5, 0x82, 0xC8, 0xF0, 0xA3, 0xC5, 0x83, + 0xC5, 0xF0, 0xC5, 0x83, 0xC8, 0xC5, 0x82, 0xC8, + 0x22, 0x75, 0x8E, 0x00, 0x75, 0x81, 0x17, 0x02, + 0x00, 0xD2, 0x90, 0xB0, 0x72, 0x74, 0xA0, 0xF0, + 0xA3, 0x74, 0x61, 0xF0, 0x22, 0x90, 0xB0, 0x72, + 0xE4, 0xF0, 0xA3, 0xF0, 0x22, 0xB1, 0x57, 0x74, + 0x73, 0xF0, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x3A, 0x00, 0x00, 0x02, 0x00, + 0x66, 0x05, 0x00, 0x00, 0xB1, 0x57, 0x74, 0x53, + 0xF0, 0x22, 0x90, 0x05, 0xF7, 0x74, 0x40, 0xF0, + 0xE4, 0x90, 0x05, 0xFF, 0xF0, 0x90, 0x05, 0xFB, + 0x74, 0x55, 0xF0, 0x71, 0x1A, 0x11, 0xA2, 0x11, + 0xB5, 0x74, 0xFF, 0xB1, 0x0D, 0x7F, 0x78, 0x7E, + 0x40, 0x51, 0xD4, 0x11, 0xCC, 0x91, 0x06, 0x71, + 0xC2, 0x11, 0xAD, 0xE4, 0xF5, 0x08, 0xF5, 0x09, + 0x90, 0x05, 0xFC, 0xE4, 0xF0, 0xA3, 0xF0, 0xA3, + 0xF0, 0x90, 0x05, 0xF8, 0xF0, 0xA3, 0xF0, 0xA3, + 0xF0, 0x90, 0x05, 0xF4, 0xF0, 0xA3, 0xF0, 0xA3, + 0xF0, 0x90, 0x05, 0xFF, 0xE0, 0xFF, 0x7E, 0x00, + 0x60, 0xF7, 0x8E, 0x0E, 0xF5, 0x0F, 0xE4, 0xF0, + 0xF5, 0x0C, 0xF5, 0x0D, 0x90, 0x05, 0xF7, 0xE0, + 0xD3, 0x94, 0x40, 0x40, 0x02, 0x41, 0xB6, 0x90, + 0x05, 0xFB, 0x74, 0x66, 0xF0, 0x11, 0xA2, 0x71, + 0xDC, 0xE5, 0x0E, 0x60, 0x02, 0x41, 0x9E, 0xE5, + 0x0F, 0x24, 0xF0, 0x70, 0x02, 0x21, 0xE4, 0x24, + 0xF0, 0x70, 0x02, 0x41, 0x10, 0x24, 0xC0, 0x70, + 0x02, 0x41, 0x67, 0x24, 0xE0, 0x60, 0x6F, 0x24, + 0xF0, 0x60, 0x31, 0x24, 0xD0, 0x70, 0x02, 0x41, + 0x8D, 0x24, 0x10, 0x60, 0x02, 0x41, 0x9E, 0x11, + 0xCC, 0x90, 0x00, 0xC8, 0xB1, 0x48, 0xB1, 0x16, + 0x94, 0x88, 0x40, 0x08, 0xAE, 0x0A, 0xAF, 0x0B, + 0x91, 0xFA, 0x41, 0x5F, 0xB1, 0x20, 0xE4, 0xFF, + 0xFE, 0x91, 0x53, 0xC3, 0x11, 0x2B, 0x70, 0x02, + 0x41, 0xAA, 0x41, 0x5F, 0x11, 0xCC, 0x90, 0x80, + 0xC8, 0xB1, 0x48, 0xC3, 0xE5, 0x0A, 0x64, 0x80, + 0x94, 0x80, 0x50, 0x02, 0x41, 0x5F, 0xB1, 0x16, + 0x94, 0xC0, 0x40, 0x02, 0x41, 0x5F, 0xC3, 0xE5, + 0x0B, 0x94, 0xC8, 0xE5, 0x0A, 0x64, 0x80, 0x94, + 0x80, 0x50, 0x02, 0x41, 0x5F, 0xB1, 0x20, 0x7F, + 0x00, 0x7E, 0x80, 0x91, 0x53, 0xC3, 0x11, 0x2B, + 0x70, 0x02, 0x41, 0xAA, 0x41, 0x5F, 0x11, 0xB5, + 0x75, 0x15, 0x85, 0x75, 0x14, 0xE1, 0x75, 0x13, + 0x24, 0x75, 0x12, 0x57, 0x7F, 0x78, 0x7E, 0x40, + 0x91, 0xDE, 0x41, 0x89, 0xB1, 0x05, 0x50, 0x26, + 0x91, 0xEE, 0xB1, 0x39, 0x98, 0x50, 0x1B, 0x91, + 0xE7, 0xC3, 0x33, 0xCE, 0x33, 0xCE, 0xD8, 0xF9, + 0x24, 0x00, 0xF5, 0x82, 0x74, 0x06, 0x91, 0xC5, + 0xB1, 0x2B, 0xE5, 0x0B, 0x70, 0x02, 0x05, 0x0A, + 0x80, 0xE0, 0x91, 0xF6, 0x80, 0x7B, 0x80, 0x4F, + 0xB1, 0x05, 0x50, 0x4B, 0x91, 0xEE, 0xB1, 0x39, + 0x98, 0x50, 0x33, 0x91, 0xE7, 0xC3, 0x33, 0xCE, + 0x33, 0xCE, 0xD8, 0xF9, 0x24, 0x00, 0xF5, 0x82, + 0x74, 0x07, 0x91, 0xC5, 0x91, 0xE7, 0xC3, 0x33, + 0xCE, 0x33, 0xCE, 0xD8, 0xF9, 0x24, 0x00, 0xF5, + 0x82, 0x74, 0x07, 0x3E, 0xF5, 0x83, 0x11, 0x68, + 0xFF, 0xFF, 0xFF, 0xFF, 0xB1, 0x2B, 0xE5, 0x0B, + 0x70, 0x02, 0x05, 0x0A, 0x80, 0xC8, 0xAE, 0x08, + 0xAF, 0x09, 0xEE, 0x33, 0x95, 0xE0, 0xFD, 0xFC, + 0x90, 0x07, 0x00, 0x11, 0x5C, 0x80, 0x2A, 0x75, + 0x0C, 0x00, 0x75, 0x0D, 0x01, 0x80, 0x43, 0x91, + 0xEE, 0x91, 0xE7, 0xC3, 0x33, 0xCE, 0x33, 0xCE, + 0xD8, 0xF9, 0xFF, 0xE4, 0xB1, 0x0D, 0x91, 0xDE, + 0x05, 0x0B, 0xE5, 0x0B, 0x70, 0x02, 0x05, 0x0A, + 0xC3, 0xE5, 0x0A, 0x64, 0x80, 0x94, 0x90, 0x40, + 0xE0, 0x11, 0xCC, 0x80, 0x1D, 0x90, 0x06, 0x00, + 0xA3, 0xA3, 0xE0, 0xFE, 0xA3, 0xE0, 0x8E, 0x08, + 0xF5, 0x09, 0x91, 0xF6, 0x80, 0x0C, 0x74, 0xFF, + 0xF5, 0x0C, 0xF5, 0x0D, 0x90, 0x05, 0xFB, 0x74, + 0x40, 0xF0, 0x11, 0xAD, 0xE5, 0x0D, 0xB4, 0xFF, + 0x0D, 0xE5, 0x0C, 0xB4, 0xFF, 0x08, 0x90, 0x05, + 0xFB, 0x74, 0x40, 0xF0, 0x21, 0x00, 0xE5, 0x0D, + 0x45, 0x0C, 0x60, 0x08, 0x90, 0x05, 0xFB, 0x74, + 0xAA, 0xF0, 0x21, 0x00, 0x90, 0x05, 0xFB, 0x74, + 0x55, 0xF0, 0x21, 0x00, 0xAB, 0x07, 0xAA, 0x06, + 0xAF, 0x15, 0xAE, 0x14, 0xAD, 0x13, 0xAC, 0x12, + 0x90, 0xA0, 0x00, 0x11, 0x5C, 0xEB, 0xAE, 0x02, + 0x78, 0x02, 0xCE, 0xC3, 0x13, 0xCE, 0x13, 0xD8, + 0xF9, 0xFF, 0x90, 0xA0, 0x06, 0xEE, 0xF0, 0xA3, + 0xEF, 0xF0, 0x90, 0xA0, 0x0F, 0x74, 0x01, 0xF0, + 0x90, 0xA0, 0x1B, 0xE0, 0xFF, 0x20, 0xE0, 0xF8, + 0x90, 0xA0, 0x0F, 0xE4, 0xF0, 0xEF, 0x7E, 0x00, + 0x30, 0xE1, 0x04, 0x7F, 0x01, 0x80, 0x02, 0x7F, + 0x00, 0x22, 0x11, 0xA2, 0x7F, 0x74, 0x7E, 0x55, + 0x7D, 0x87, 0x7C, 0x4E, 0x90, 0x40, 0x7C, 0x11, + 0x4C, 0xC3, 0x11, 0x15, 0x70, 0x26, 0xFF, 0xFE, + 0xE4, 0x2F, 0xFD, 0xEE, 0x34, 0x40, 0x8D, 0x82, + 0xF5, 0x83, 0xE4, 0x93, 0xFD, 0xE4, 0x2F, 0xFB, + 0xEE, 0x34, 0xFF, 0x8B, 0x82, 0xF5, 0x83, 0xED, + 0xF0, 0x0F, 0xBF, 0x00, 0x01, 0x0E, 0xEF, 0x64, + 0x78, 0x4E, 0x70, 0xDC, 0x01, 0xAD, 0x8C, 0x10, + 0x8D, 0x11, 0xE4, 0xF5, 0x14, 0xF5, 0x15, 0x75, + 0x16, 0x10, 0x75, 0x17, 0x21, 0x8E, 0x12, 0x8F, + 0x13, 0xFD, 0xFC, 0xC3, 0xED, 0x95, 0x11, 0xEC, + 0x95, 0x10, 0x50, 0x49, 0xE5, 0x13, 0x2D, 0xFF, + 0xE5, 0x12, 0x3C, 0x8F, 0x82, 0xF5, 0x83, 0xE4, + 0x93, 0xFE, 0xEE, 0x62, 0x14, 0xE4, 0xFB, 0xFA, + 0xE5, 0x14, 0x30, 0xE7, 0x13, 0xE5, 0x15, 0x25, + 0xE0, 0xFF, 0xE5, 0x14, 0x33, 0x65, 0x16, 0xF5, + 0x14, 0xE5, 0x17, 0x6F, 0xF5, 0x15, 0x80, 0x0B, + 0xE5, 0x15, 0x25, 0xE0, 0xF5, 0x15, 0xE5, 0x14, + 0x33, 0xF5, 0x14, 0x0B, 0xBB, 0x00, 0x01, 0x0A, + 0xEB, 0x64, 0x08, 0x4A, 0x70, 0xD2, 0x0D, 0xBD, + 0x00, 0x01, 0x0C, 0x80, 0xAE, 0xAE, 0x14, 0xAF, + 0x15, 0x22, 0x90, 0xF7, 0x03, 0x74, 0x04, 0xF0, + 0x90, 0xF7, 0x02, 0x74, 0x02, 0xF0, 0x90, 0xF7, + 0x0E, 0x04, 0xF0, 0xA3, 0x04, 0xF0, 0x90, 0xF7, + 0x00, 0x14, 0xF0, 0x22, 0x90, 0xF7, 0x00, 0x74, + 0x07, 0xF0, 0x90, 0xF7, 0x3A, 0xE0, 0xFE, 0xA3, + 0xE0, 0xFF, 0x7C, 0x00, 0x7D, 0x05, 0x11, 0x03, + 0x90, 0xF7, 0x00, 0x74, 0x03, 0xF0, 0xD3, 0xEF, + 0x94, 0xA0, 0xEE, 0x94, 0x0F, 0x40, 0x06, 0x90, + 0xFF, 0xC5, 0x74, 0x81, 0xF0, 0x22, 0x90, 0xFF, + 0x83, 0x74, 0x01, 0xF0, 0x90, 0xFF, 0x80, 0xE0, + 0x44, 0x10, 0xF0, 0x90, 0xFF, 0x1A, 0x74, 0x30, + 0xF0, 0x90, 0xFF, 0xAB, 0x74, 0x22, 0xF0, 0x90, + 0xFF, 0xC3, 0x74, 0x03, 0xF0, 0x90, 0xFF, 0xC7, + 0x14, 0xF0, 0x90, 0xFF, 0xC6, 0x74, 0x68, 0xF0, + 0x90, 0xFF, 0xC5, 0x74, 0x01, 0xF0, 0x90, 0xFF, + 0x87, 0xF0, 0x90, 0xF4, 0x0B, 0xF0, 0x90, 0xF4, + 0x02, 0xF0, 0xA3, 0x74, 0x0E, 0xF0, 0x90, 0xF4, + 0x07, 0x74, 0x7E, 0xF0, 0x90, 0xF4, 0x0F, 0x74, + 0x01, 0xF0, 0x22, 0x71, 0x56, 0xE4, 0xFC, 0xFD, + 0x90, 0x06, 0x00, 0x11, 0x5C, 0xE5, 0x0B, 0x24, + 0xFC, 0xFF, 0xE5, 0x0A, 0x34, 0xFF, 0x8F, 0x82, + 0xF5, 0x83, 0xE4, 0x93, 0xFF, 0xE4, 0xFC, 0xFD, + 0xFE, 0x90, 0x06, 0x04, 0x11, 0x5C, 0x90, 0x06, + 0x04, 0xE0, 0xF8, 0xA3, 0xE0, 0xF9, 0xA3, 0xE0, + 0xFA, 0xA3, 0xE0, 0xFB, 0xE5, 0x0B, 0x24, 0xFD, + 0xFF, 0xE5, 0x0A, 0x34, 0xFF, 0x8F, 0x82, 0xF5, + 0x83, 0xE4, 0x93, 0xFE, 0x33, 0x95, 0xE0, 0xFD, + 0xFC, 0xEB, 0xFF, 0xEA, 0x4E, 0xFE, 0xE9, 0x4D, + 0xFD, 0xE8, 0x4C, 0xFC, 0x90, 0x06, 0x04, 0x11, + 0x5C, 0x90, 0x06, 0x04, 0xA3, 0xA3, 0xE0, 0xFE, + 0xA3, 0xE0, 0xFF, 0xE4, 0xFC, 0xFD, 0x90, 0x06, + 0x00, 0xE0, 0xF8, 0xA3, 0xE0, 0xF9, 0xA3, 0xE0, + 0xFA, 0xA3, 0xE0, 0xFB, 0x22, 0x3E, 0xF5, 0x83, + 0xE0, 0xFC, 0xA3, 0xE0, 0xFD, 0xA3, 0xE0, 0xFE, + 0xA3, 0xE0, 0xF5, 0x15, 0x8E, 0x14, 0x8D, 0x13, + 0x8C, 0x12, 0xAF, 0x09, 0xAE, 0x08, 0x51, 0xD4, + 0xEF, 0x42, 0x0D, 0xEE, 0x42, 0x0C, 0x22, 0xE5, + 0x0B, 0xAE, 0x0A, 0x78, 0x02, 0x22, 0x11, 0xB5, + 0xE4, 0xF5, 0x0A, 0xF5, 0x0B, 0x22, 0xAE, 0x08, + 0xAF, 0x09, 0xEE, 0x33, 0x95, 0xE0, 0xFD, 0xFC, + 0x90, 0x06, 0x00, 0x01, 0x5C, 0xC3, 0xE5, 0x08, + 0x64, 0x80, 0x94, 0xC0, 0x22, 0xF5, 0x15, 0xF5, + 0x14, 0xF5, 0x13, 0xF5, 0x12, 0x22, 0xD3, 0xE5, + 0x0B, 0x94, 0x00, 0xE5, 0x0A, 0x64, 0x80, 0x22, + 0xE5, 0x0B, 0x24, 0xFC, 0xFD, 0xE5, 0x0A, 0x34, + 0xFF, 0xFC, 0x22, 0x74, 0x04, 0x25, 0x09, 0xF5, + 0x09, 0xE4, 0x35, 0x08, 0xF5, 0x08, 0x05, 0x0B, + 0x22, 0x90, 0x05, 0xF7, 0xE0, 0xFF, 0xC3, 0xE5, + 0x0B, 0x9F, 0x74, 0x80, 0xF8, 0x65, 0x0A, 0x22, + 0xE4, 0x93, 0x75, 0x0A, 0x00, 0xF5, 0x0B, 0xA3, + 0xE4, 0x93, 0xFE, 0xEE, 0x42, 0x0A, 0x22, 0x90, + 0xA0, 0x06, 0xE4, 0xF0, 0xA3, 0xF0, 0x90, 0xA0, + 0x13, 0x22, 0x31, 0x5B, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +#endif diff --git a/drivers/power/cps4019_wls_charger/cps4019_wls_charger.c b/drivers/power/cps4019_wls_charger/cps4019_wls_charger.c new file mode 100644 index 000000000000..e44bc4761d5a --- /dev/null +++ b/drivers/power/cps4019_wls_charger/cps4019_wls_charger.c @@ -0,0 +1,2244 @@ +/* + * Copyright © 2020, ConvenientPower + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_HAS_WAKELOCK +#include +#else +#include +#include +#endif + +#include +#include + +#include "cps4019_wls_charger.h" +#include "cps4019_bl.h" + +//namespace VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver; + +#define BOOTLOADER_FILE_NAME "/data/misc/cps/bootloader.hex" +#define FIRMWARE_FILE_NAME "/data/misc/cps/firmware.hex" + +#define CPS_WLS_CHRG_DRV_NAME "cps-wls-charger" + +#define CPS_WLS_CHRG_PSY_NAME "wireless" +#define FAN_CAPABILITY (1 << 2) +#define LIGHT_CAPABILITY (1 << 5) + +/***************************************************************************** + * Log + ****************************************************************************/ +#define CPS_LOG_NONE 0 +#define CPS_LOG_ERR 1 +#define CPS_LOG_DEBG 2 +#define CPS_LOG_FULL 3 + +#define ENABLE_CPS_LOG CPS_LOG_FULL + +#define CPS4019_CHIP_ID 0x4019 +#define CPS4019_WORK_VOL 2700000 //mV +#define CPS4019_DEFAULT_POWER 5//5W, unit w + +#define CPS4019_PPP_DATA_SIZE 8 + +#define cps_wls_log(num, fmt, args...) \ + do { \ + if (ENABLE_CPS_LOG >= (int)num) \ + pr_err("cps4019 " fmt,##args); \ + } while (0) + +/*-------------------------------------------------------------------*/ +struct cps_wls_chrg_chip { + struct i2c_client *client; + struct device *dev; + struct regmap *regmap; + struct regmap *regmap32; + char *name; + struct power_supply *wl_psy; + struct power_supply *batt_psy; + struct power_supply *usb_psy; + struct power_supply *dc_psy; + struct power_supply_desc wl_psd; + struct power_supply_config wl_psc; + struct power_supply_desc batt_psd; + struct power_supply_desc usb_psd; + struct power_supply_desc dc_psd; + struct pinctrl *cps_pinctrl; + struct pinctrl_state *cps_gpio_active; + struct pinctrl_state *cps_gpio_suspend; + struct iio_channel *otg_channel; + + #ifdef CONFIG_HAS_WAKELOCK + struct wake_lock cps_wls_wake_lock; + #else + struct wakeup_source *cps_wls_wake_lock; + #endif + + struct mutex irq_lock; + struct mutex i2c_lock; + int state; + int gpio_force_wls; + int wls_charge_int; + int cps_wls_irq; + int reg_addr; + int reg_data; + int rx_ovp; + int rx_ocp; + int rx_opp; + int rx_ht_thl; + int rx_vout_target; + int rx_ept_rsn; + int rx_iout; + int rx_vrect; + int rx_vout; + int rx_die_temp; + int rx_ntc; + int rx_neg_power; + int rx_neg_protocol; + int command_flag; + + /*main charger*/ + const char *main_charger_name; + struct charger_device *main_chg_dev; + + bool is_factory_mode; + bool is_charger_full; + + int op_mode; + int fan_speed; + int light_level; + int wlc_status; + + /*fw relative*/ + const char *wls_fw_name; + int fw_ver_major; + int fw_ver_minor; + bool use_bl_in_h; +}; + +static struct cps_wls_chrg_chip *chip = NULL; + +/*define cps rx reg enum*/ +typedef enum +{ + CPS_REG_CHIP_ID, + CPS_REG_FW_MAJOR_REV, + CPS_REG_FW_MINOR_REV, + CPS_REG_STATUS, + CPS_REG_INT, + CPS_REG_INT_ENABLE, + CPS_REG_INT_CLEAR, + CPS_REG_CMD, + CPS_REG_VOUT_SET, + CPS_REG_ILIM_SET, + CPS_REG_ADC_VOUT, + CPS_REG_ADC_VRECT, + CPS_REG_ADC_IOUT, + CPS_REG_ADC_DIE_TEMP, + CPS_REG_PRO_SET, + CPS_REG_PPP_HEADER, + CPS_REG_PPP_CMD, + CPS_REG_PPP_DATA, + CPS_REG_MAX +}cps_reg_e; + +typedef struct +{ + uint16_t reg_name; + uint16_t reg_bytes_len; + uint32_t reg_addr; +}cps_reg_s; + +cps_reg_s cps_reg_cfg[CPS_REG_MAX] = { + /* reg name bytes number reg address */ + {CPS_REG_CHIP_ID, 2, 0x0000}, + {CPS_REG_FW_MAJOR_REV, 2, 0x0002}, + {CPS_REG_FW_MINOR_REV, 2, 0x0004}, + {CPS_REG_STATUS, 2, 0x0007}, + {CPS_REG_INT, 2, 0x0009}, + {CPS_REG_INT_ENABLE, 2, 0x000B}, + {CPS_REG_INT_CLEAR, 2, 0x000D}, + {CPS_REG_CMD, 2, 0x000F}, + {CPS_REG_VOUT_SET, 2, 0x0013}, + {CPS_REG_ILIM_SET, 1, 0x0016}, + {CPS_REG_ADC_VOUT, 2, 0x0017}, + {CPS_REG_ADC_VRECT, 2, 0x0019}, + {CPS_REG_ADC_IOUT, 2, 0x001B}, + {CPS_REG_ADC_DIE_TEMP, 2, 0x001D}, + {CPS_REG_PRO_SET, 2, 0x0068}, + {CPS_REG_PPP_HEADER, 2, 0x0025}, + {CPS_REG_PPP_CMD, 2, 0x0026}, + {CPS_REG_PPP_DATA, 2, 0x0027} +}; + +typedef enum +{ + WLC_DISCONNECTED, + WLC_CONNECTED, + WLC_TX_TYPE_CHANGED, + WLC_TX_POWER_CHANGED, +}wlc_status; + +typedef enum { + MMI_WLC_NONE, + MMI_WLC_BPP, + MMI_WLC_EPP, + MMI_WLC_MOTO, +} mmi_wlc_type; + +typedef enum { + MMI_DOCK_LIGHT_OFF = 0x10, + MMI_DOCK_LIGHT_ON = 0x20, + MMI_DOCK_LIGHT_BREATH_2S = 0x30, + MMI_DOCK_LIGHT_BREATH_4S = 0x40, + MMI_DOCK_LIGHT_DEFAULT = MMI_DOCK_LIGHT_BREATH_4S, +} mmi_dock_light_ctrl; + +/* value = fan speed / 100 */ +typedef enum { + MMI_DOCK_FAN_SPEED_OFF= 0, + MMI_DOCK_FAN_SPEED_LOW = 20,//2000 + MMI_DOCK_FAN_SPEED_HIGH = 40,//4000 + MMI_DOCK_FAN_DEFAULT = MMI_DOCK_FAN_SPEED_HIGH, +} mmi_dock_fan_speed; + +typedef enum { + MMI_DOCK_LIGHT_INDEX = 0, + MMI_DOCK_FAN_INDEX =1, +} mmi_dock_light_fan_index; + +/*define MMI CMD enum*/ +typedef enum +{ + MMI_CMD_CHARGE_FULL, + MMI_CMD_FAN_LIGHT, + MMI_CMD_MAX +}mmi_cmd_e; + +typedef struct +{ + uint32_t header; + uint32_t cmd; + uint32_t data[CPS4019_PPP_DATA_SIZE]; + uint32_t len; +}mmi_cmd_s; + +#define MMI_WLC_DOCK_CMD_LEN 2 + +mmi_cmd_s mmi_cmd_cfg[MMI_CMD_MAX] = { + /* cmd_header cmd_cmd cmd_data cmd_len */ + {0x5, 0x64, {0}, 0},//MMI_CMD_CHARGE_FULL + {0x38, 0x05, {0}, MMI_WLC_DOCK_CMD_LEN},//MMI_CMD_FAN +}; + +//-------------------I2C APT start-------------------- +static const struct regmap_config cps4019_regmap_config = { + .reg_bits = 16, + .val_bits = 8, +}; + +static const struct regmap_config cps4019_regmap_32bit_config = { + .reg_bits = 32, + .val_bits = 8, +}; + +extern bool mmi_is_factory_mode(void); + +static int cps_wls_get_int_flag(void); +static int cps_wls_set_int_clr(int value); +static int cps_wls_get_chip_id(void); +static int cps_wls_get_sys_fw_major_version(void); +static int cps_wls_get_sys_fw_minor_version(void); +static int cps_wls_get_vrect(void); +static int cps_wls_get_iout(void); +static int cps_wls_get_vout(void); +static int cps_wls_get_die_tmp(void); +static int cps_wls_set_rx_vout_target(int value); +static int cps_wls_set_rx_ocp_threshold(int value); + +static int cps_wls_set_status(int status); + +static int cps_wls_read_word_addr32(int reg) +{ + int ret; + int value; + unsigned char data[4]; + int i ; + + mutex_lock(&chip->i2c_lock); + ret = regmap_raw_read(chip->regmap32, reg, data, 4); + mutex_unlock(&chip->i2c_lock); + + if (ret < 0) { + cps_wls_log(CPS_LOG_ERR, "[%s] i2c read 0x%x error!\n", __func__, reg); + return CPS_WLS_FAIL; + } + //pr_err("cps read32[%08X] %02X %02X %02X %02X\n", reg,data[0], data[1], data[2], data[3]); + value = 0; + for(i = 3 ; i >= 0 ; i--) { + value = (value <<8) | (data[i]); + } + + return value; +} +static int cps_wls_write_word_addr32(int reg, int value) +{ + int ret; + unsigned char data[4]; + int i ; + + for(i = 0; i < 4; i++) { + data[i] = (value >> (8*i) ) & 0xFF; + } + mutex_lock(&chip->i2c_lock); + ret = regmap_raw_write(chip->regmap32, reg, data, 4); + mutex_unlock(&chip->i2c_lock); + + if (ret < 0) { + cps_wls_log(CPS_LOG_ERR, "[%s] i2c write 0x%x error!\n", __func__, reg); + return CPS_WLS_FAIL; + } + + return CPS_WLS_SUCCESS; +} + + +static int cps_wls_write(int reg, int value) +{ + int ret; + + mutex_lock(&chip->i2c_lock); + ret = regmap_write(chip->regmap, reg, value); + mutex_unlock(&chip->i2c_lock); + + if (ret < 0) { + cps_wls_log(CPS_LOG_ERR, "[%s] i2c write 0x%x error!\n", __func__, reg); + return CPS_WLS_FAIL; + } + + return CPS_WLS_SUCCESS; +} + +/* +return -1 means fail, 0 means success +*/ +static int cps_wls_read(int reg) +{ + int ret; + int value; + + mutex_lock(&chip->i2c_lock); + ret = regmap_read(chip->regmap, reg, &value); + mutex_unlock(&chip->i2c_lock); + + if (ret < 0) { + cps_wls_log(CPS_LOG_ERR, "[%s] Chip not exist(rd %x!)\n", __func__, reg); + return CPS_WLS_FAIL; + } + return value; +} + +/* +return -1 means fail, 0 means success +*/ +static int cps_wls_write_reg(int reg, int value, int byte_len) +{ + int i=0, tmp=0; + for(i = 0; i < byte_len; i++) { + tmp = (value >> (i * 8)) & 0xff; + if(cps_wls_write((reg & 0xffff) + i, tmp) == CPS_WLS_FAIL) + goto write_fail; + } + return CPS_WLS_SUCCESS; + +write_fail: + return CPS_WLS_FAIL; +} + +/* +return -1 means fail, 0 means success +*/ +static int cps_wls_read_reg(int reg, int byte_len) +{ + int i=0,tmp=0,read_data=0; + for(i = 0; i < byte_len; i++) { + tmp = cps_wls_read((reg & 0xffff) + i); + if(tmp == CPS_WLS_FAIL) + goto read_fail; + read_data |= (tmp << (8 * i)); + } + return read_data; + +read_fail: + return CPS_WLS_FAIL; +} + +static int cps_wls_write_reg_bits(int reg, int value, int mask, int shift) +{ + int tmp=0; + + tmp = cps_wls_read(reg & 0xffff); + if(tmp == CPS_WLS_FAIL) + goto write_fail; + + tmp &= ~mask; + tmp |= (value << shift); + if(cps_wls_write(reg & 0xffff, tmp) == CPS_WLS_FAIL) + goto write_fail; + + return CPS_WLS_SUCCESS; + +write_fail: + return CPS_WLS_FAIL; +} +static int cps_wls_send_ppp_cmd(int header, int cmd, int *data, int len) +{ + int i=0, ret=0; + cps_reg_s *cps_reg; + + /*cmd header*/ + cps_reg = (cps_reg_s*)(&cps_reg_cfg[CPS_REG_PPP_HEADER]); + ret = cps_wls_write_reg(cps_reg->reg_addr, header, cps_reg->reg_bytes_len); + if(ret == CPS_WLS_FAIL) + goto send_fail; + + /*cmd*/ + cps_reg = (cps_reg_s*)(&cps_reg_cfg[CPS_REG_PPP_CMD]); + ret = cps_wls_write_reg(cps_reg->reg_addr, cmd, cps_reg->reg_bytes_len); + if(ret == CPS_WLS_FAIL) + goto send_fail; + + /*cmd data*/ + if (len > 0) { + cps_reg = (cps_reg_s*)(&cps_reg_cfg[CPS_REG_PPP_DATA]); + for(i = 0; i < len; i++) { + ret = cps_wls_write_reg(cps_reg->reg_addr + i, data[i], cps_reg->reg_bytes_len); + if(ret == CPS_WLS_FAIL) + goto send_fail; + } + } + + /*send cmd*/ + cps_reg = (cps_reg_s*)(&cps_reg_cfg[CPS_REG_CMD]); + ret = cps_wls_write_reg_bits(cps_reg->reg_addr, + 1, + REG_CMD_SEND_RX_MASK, + REG_CMD_SEND_RX_SHIFT); + if(ret == CPS_WLS_FAIL) + goto send_fail; + + return CPS_WLS_SUCCESS; + +send_fail: + return CPS_WLS_FAIL; +} + +/* + * @brief big and little endian convertion + * @param null + * @return + */ +int big_little_endian_convert(int dat) +{ + char *p; + char tmp[4]; + + p = (char *)(&dat); + tmp[0] = p[3]; + tmp[1] = p[2]; + tmp[2] = p[1]; + tmp[3] = p[0]; + return *(int *)(tmp); +} + +//*****************************for program************************ + +static int cps_wls_program_sram_addr32(int addr, u8 *data, int len) +{ + int ret; + + mutex_lock(&chip->i2c_lock); + ret = regmap_raw_write(chip->regmap32, addr, data, len); + mutex_unlock(&chip->i2c_lock); + + if (ret < 0) { + cps_wls_log(CPS_LOG_ERR, "[%s] i2c write error!\n", __func__); + return CPS_WLS_FAIL; + } + return CPS_WLS_SUCCESS; +} + +static int cps_wls_write_word(int addr, int value) +{ + int ret; + u8 write_data[4]; + + write_data[0] = value & 0xff; + write_data[1] = (value >> 8) & 0xff; + write_data[2] = (value >> 16) & 0xff; + write_data[3] = (value >> 24) & 0xff; + + mutex_lock(&chip->i2c_lock); + ret = regmap_raw_write(chip->regmap, addr, write_data, 4); + mutex_unlock(&chip->i2c_lock); + + if (ret < 0) { + cps_wls_log(CPS_LOG_ERR, "[%s] i2c write error!\n", __func__); + return CPS_WLS_FAIL; + } + return CPS_WLS_SUCCESS; +} + +static int cps_wls_read_word(int addr) +{ + int ret; + u8 read_data[4]; + + mutex_lock(&chip->i2c_lock); + ret = regmap_raw_read(chip->regmap, addr, read_data, 4); + mutex_unlock(&chip->i2c_lock); + + if (ret < 0) { + cps_wls_log(CPS_LOG_ERR, "[%s] i2c read error!\n", __func__); + return CPS_WLS_FAIL; + } + return *(int *)read_data; +} + +static int cps_wls_program_cmd_send(int cmd) +{ + return cps_wls_write_word_addr32(ADDR_CMD, big_little_endian_convert(cmd)); +} + +static int cps_wls_program_wait_cmd_done(void) +{ + int ret; + int wait_time_out = 500;//ms + while(1) { + ret = cps_wls_read_word_addr32(ADDR_FLAG); + if(ret == CPS_WLS_FAIL) + return CPS_WLS_FAIL; + wait_time_out--; + //pr_err("cps wait_time_out=%d\n",wait_time_out); + msleep(1); + if((big_little_endian_convert(ret) & 0xff) == PASS) { + break; + } + if(wait_time_out < 0) + return CPS_WLS_FAIL; + } + + return CPS_WLS_SUCCESS; +} + +static bool cps_check_fw_ver(void) +{ + int fw_major, fw_minor; + int ret; + + fw_major = cps_wls_get_sys_fw_major_version(); + fw_minor = cps_wls_get_sys_fw_minor_version(); + + cps_wls_log(CPS_LOG_ERR, "[%s] chip id %d.%d\n", __func__, fw_major, fw_minor); + + if ((chip->fw_ver_major > fw_major) + || (chip->fw_ver_minor > fw_minor)) + ret = true; + else + ret = false; + + return ret; +} + +static bool cps_check_chipid(void) +{ + int chip_id; + + chip_id = cps_wls_get_chip_id(); + + cps_wls_log(CPS_LOG_ERR, "[%s] chip id 0x%x\n", __func__, chip_id); + + return chip_id == CPS4019_CHIP_ID ? true : false; +} + +static void cps_wls_pm_set_awake(int awake) +{ + + cps_wls_log(CPS_LOG_DEBG,"%s lock %d wak %d\n", __func__, chip->cps_wls_wake_lock->active, awake); + + if(!chip->cps_wls_wake_lock->active && awake) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock(chip->cps_wls_wake_lock); +#else + __pm_stay_awake(chip->cps_wls_wake_lock); +#endif + } else if(chip->cps_wls_wake_lock->active && !awake) { +#ifdef CONFIG_HAS_WAKELOCK + wake_unlock(chip->cps_wls_wake_lock); +#else + __pm_relax(chip->cps_wls_wake_lock); +#endif + } +} + +static int cps_get_power_supply_prop(char* name, + enum power_supply_property psp, + union power_supply_propval* val) { + struct psy_in_chip_s { + char* name; + struct power_supply **ppsy; + }; + struct psy_in_chip_s psy_in_chip[] = { + {"battery", &chip->batt_psy}, + {"wireless", &chip->wl_psy}, + {"usb", &chip->usb_psy}, + {"dc", &chip->dc_psy} + }; + struct power_supply **ppsy = NULL; + struct power_supply *tmp_psy = NULL; + int i; + + /*Use a tmp psy as default for the psy that not in chip list*/ + ppsy = &tmp_psy; + for (i = 0; i < ARRAY_SIZE(psy_in_chip); i++) { + if (!strcmp(name, psy_in_chip[i].name)) { + ppsy = psy_in_chip[i].ppsy; + break; + } + } + + if ((!*ppsy) || IS_ERR(*ppsy)) { + *ppsy = power_supply_get_by_name(name); + if (!*ppsy || IS_ERR(*ppsy)) { + cps_wls_log(CPS_LOG_ERR,"%s Couldn't get psy %s\n",__func__, name); + val->intval = -1; + return CPS_WLS_FAIL; + } + } + + if (power_supply_get_property(*ppsy, psp, val)) { + val->intval = -1; + return CPS_WLS_FAIL; + } + + return CPS_WLS_SUCCESS; +} + +static int cps_mux_switch(bool on) +{ + cps_wls_log(CPS_LOG_DEBG,"%s set mux = %d\n", __func__, on); + + if (!chip->otg_channel) { + cps_wls_log(CPS_LOG_ERR,"%s otg iio dev exist\n", __func__); + return CPS_WLS_FAIL; + } + + iio_write_channel_raw(chip->otg_channel, !!on); + + return CPS_WLS_SUCCESS; +} + +static int cps_check_power(bool *en) +{ + static struct power_supply *chg_psy = NULL; + union power_supply_propval data; + + if (!chg_psy) { + chg_psy = power_supply_get_by_name("charger"); + if (!chg_psy || IS_ERR(chg_psy)) { + cps_wls_log(CPS_LOG_ERR,"%s Couldn't get chg_psy\n",__func__); + *en = true; + return CPS_WLS_FAIL; + } + } + + power_supply_get_property(chg_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &data); + if (data.intval > CPS4019_WORK_VOL) { + *en = false; + } else { + *en = true; + } + + return CPS_WLS_SUCCESS; +} + +static int cps_set_power(bool en) +{ + static bool flag = false; + + /*only work in enable*/ + if (en) + cps_check_power(&flag); + + cps_wls_pm_set_awake(!!en); + + /*only work when check power result is true*/ + if (flag) { + if (cps_mux_switch(!!en)) { + cps_wls_pm_set_awake(false); + return CPS_WLS_FAIL; + } + + /*wait 150ms for vbus boost stable*/ + msleep(150); + + if (!en) + flag = false; + } + + return CPS_WLS_SUCCESS; +} + +static int fp_size(struct file *f) +{ + int error = -EBADF; + struct kstat stat; + + //error = vfs_getattr(&f->f_path, &stat); + error = vfs_getattr(&f->f_path, &stat, STATX_SIZE, KSTAT_QUERY_FLAGS ); + + if (error == 0) { + pr_err("cps4019 get file file size:%d\n", (int)stat.size); + return stat.size; + } + else { + pr_err("cps4019 get file file stat error\n"); + return error; + } +} + +static int cps_file_read(char *filename, char **buf) +{ + struct file *fp; + //mm_segment_t fs; + int size = 0; + loff_t pos = 0; + + fp = filp_open(filename, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("cps4019 open %s file error\n", filename); + goto end; + } + + size = fp_size(fp); + if (size <= 0) { + pr_err("cps4019 load file:%s error\n", filename); + goto error; + } + + *buf = kzalloc(size + 1, GFP_KERNEL); + if( *buf == NULL ) { + pr_err("cps4019 kzalloc error\n"); + goto error; + } + + //fs = get_fs(); + //set_fs(KERNEL_DS); + //vfs_read(fp, *buf, size, &pos);//it's not EXPORT_SYMBOL + + size = kernel_read(fp, *buf, size, &pos); + pr_err("cps4019 kernel_read %s size:%d\n", filename, size); +error: + filp_close(fp, NULL); + //set_fs(fs); +end: + return size; +} + +static unsigned char chartoBcd(char iChar) +{ + unsigned char mBCD = 0; + + if (iChar >= '0' && iChar <= '9') + mBCD = iChar - '0'; + else if (iChar >= 'A' && iChar <= 'F') + mBCD = iChar - 'A' + 0x0a; + else if (iChar >= 'a' && iChar <= 'f') + mBCD = iChar - 'a' + 0x0a; + + return mBCD; +} + +static unsigned char* file_parse(char *buf, int size, + unsigned char *file, int *file_length) +{ + int i = 0, j = 0; + int file_index = 0; + char temp; + + if (!buf || !file) + return NULL; + for(i = 0; i < size; i++) { + if(buf[i] == '\n' || buf[i] == ' ' || buf[i] == '\r') { + file_index = 0; + continue; + } else{ + if (file_index == 1) { + file_index++; + file[j++] = (unsigned char)((chartoBcd(temp) << 4) + chartoBcd(buf[i])); + } else if (file_index == 0) { + file_index++; + temp = buf[i]; + } + } + } + // file[j] = '\0'; + *file_length = j; + return file; +} + +static int bootloader_load(unsigned char *bootloader, int *bootloader_length) +{ + char *buf = NULL; + int size = 0; + + cps_wls_log(CPS_LOG_DEBG,"%s use h file %d\n",__func__, chip->use_bl_in_h); + + if (chip->use_bl_in_h) { + memcpy(bootloader, CPS4019_BL, CPS4019_BL_SIZE); + *bootloader_length = CPS4019_BL_SIZE; + } else { + size = cps_file_read(BOOTLOADER_FILE_NAME, &buf); + if (size > 0) { + if (bootloader == NULL) { + pr_err("cps4019 file alloc error.\n"); + kfree(buf); + return -EINVAL; + } + + if(file_parse(buf, size, bootloader, bootloader_length) == NULL) { + pr_err("cps4019 file parse error\n"); + kfree(buf); + return -EINVAL; + } + + kfree(buf); + } + } + + cps_wls_log(CPS_LOG_DEBG,"cps_[%s] bootloader_length=%d\n", __func__, *bootloader_length); + + return 0; +} + +static int firmware_load(unsigned char *firmeware, int *firmeware_length) +{ + char *buf = NULL; + int size = 0; + int ret; + const struct firmware *fw; + + if (chip->wls_fw_name) { + ret = request_firmware(&fw, chip->wls_fw_name, chip->dev); + if (ret || fw->size <=0 ) { + cps_wls_log(CPS_LOG_ERR,"Couldn't get firmware rc=%d\n", ret); + return -EINVAL; + } + + size = fw->size; + buf = kzalloc(size+1, GFP_KERNEL); // 18K buffer + memset(buf, 0, size+1); + memcpy(buf, fw->data, size); + } else { + size = cps_file_read(FIRMWARE_FILE_NAME, &buf); + } + + if (size > 0) { + if (firmeware == NULL) { + kfree(buf); + pr_err("cps4019 file alloc error.\n"); + return -EINVAL; + } + if(file_parse(buf, size, firmeware, firmeware_length) == NULL) { + kfree(buf); + pr_err("cps4019 file parse error\n"); + return -EINVAL; + } + kfree(buf); + } + + return 0; +} + +static int update_firmware(bool forece) +{ + int ret, write_count, k; + int bootloader_length, firmware_length; + int buff0_flag = 0, buff1_flag = 0; + unsigned char *bootloader_buf; + unsigned char *firmware_buf; + unsigned char *p; + unsigned short int chip_id; + int result; + int *p_convert; + + if (cps_set_power(true)) { + cps_wls_log(CPS_LOG_ERR, "[%s] en power fail!!\n", __func__); + goto update_fail; + } + + /*Judge fw version(fw.hex file ver > fw in chip) and chip id(active chipid is 0x4019)*/ + if ( !forece && cps_check_chipid() && + !cps_check_fw_ver()) { + cps_wls_log(CPS_LOG_ERR, "[%s] fw already exist OR chip do not exist. Skip!!\n", __func__); + goto update_fail; + } + + bootloader_buf = kzalloc(CPS4019_BL_SIZE, GFP_KERNEL);// 2K buffer + firmware_buf = kzalloc(0x4000, GFP_KERNEL);// 16K buffer + +/*************************************************************************************** + * Step1, load to sram * + ***************************************************************************************/ + ret = bootloader_load(bootloader_buf, &bootloader_length);//load bootloader + if (ret != 0) { + cps_wls_log(CPS_LOG_DEBG, "[%s] ---- bootloader get error %d\n", __func__, ret); + goto update_fail; + } + + if(CPS_WLS_FAIL == cps_wls_write_word_addr32(0xFFFFFF00, big_little_endian_convert(0x0E000000))) + goto update_fail; /*enable 32bit i2c*/ + + if(CPS_WLS_FAIL == cps_wls_write_word_addr32(0x40040070, big_little_endian_convert(0x0000A061))) + goto update_fail; /*write password*/ + + if(CPS_WLS_FAIL == cps_wls_write_word_addr32(0x40040004, big_little_endian_convert(0x00000008))) + goto update_fail; /*reset and halt mcu*/ + + if(CPS_WLS_FAIL == cps_wls_program_sram_addr32(0x20000000, bootloader_buf, bootloader_length)) + goto update_fail;//program sram + + if(CPS_WLS_FAIL == cps_wls_write_word_addr32(0x40040010, big_little_endian_convert(0x00000001))) + goto update_fail; /*triming load function is disabled*/ + + if(CPS_WLS_FAIL == cps_wls_write_word_addr32(0x40040004, big_little_endian_convert(0x00000066))) + goto update_fail; /*enable remap function and reset the mcu*/ + + cps_wls_log(CPS_LOG_DEBG, "[%s] ---- system restart\n", __func__); + + msleep(10); + if(CPS_WLS_FAIL == cps_wls_write_word_addr32(0xFFFFFF00, big_little_endian_convert(0x0E000000))) + goto update_fail; /*enable 32bit i2c*/ + msleep(10); + +/*************************************************************************************** + * Step2, bootloader crc check * + ***************************************************************************************/ + cps_wls_program_cmd_send(CACL_CRC_TEST); + result = cps_wls_program_wait_cmd_done(); + + if(result != CPS_WLS_SUCCESS) { + cps_wls_log(CPS_LOG_ERR, "[%s] ---- bootloader crc fail\n", __func__); + goto update_fail; + } + cps_wls_log(CPS_LOG_DEBG, "[%s] ---- load bootloader successful\n", __func__); + + +/*************************************************************************************** + * Step3, load firmware to MTP * + ***************************************************************************************/ + memset(firmware_buf, 0, 0x4000); + ret = firmware_load(firmware_buf, &firmware_length);//load bootloader + if (ret != 0) { + cps_wls_log(CPS_LOG_ERR, "[%s] ---- firmware get error %d\n", __func__, ret); + goto update_fail; + } + pr_err("cps4019 firmware_length=%d\n", firmware_length); + //set write buffer size defalt 64 word + cps_wls_write_word(ADDR_BUF_SIZE, big_little_endian_convert(CPS_PROGRAM_BUFFER_SIZE)); + write_count = 0; + + p_convert = (int*)firmware_buf; + for(k = 0; k < 16 * 1024 / 4; k++) { + p_convert[k] = big_little_endian_convert(p_convert[k]); + } + +start_write_app_code: + + cps_wls_program_cmd_send(PGM_ERASER_0); + result = cps_wls_program_wait_cmd_done(); + + if(result != CPS_WLS_SUCCESS) { + cps_wls_log(CPS_LOG_ERR, "[%s] ---- bootloader crc fail\n", __func__); + goto update_fail; + } + p = (unsigned char*)p_convert; + write_count++; + + for (k = 0; k < (16 * 1024 / 4) / CPS_PROGRAM_BUFFER_SIZE; k++) { + if (buff0_flag == 0) { + //write buf0 + cps_wls_program_sram_addr32(ADDR_BUFFER0, p, CPS_PROGRAM_BUFFER_SIZE*4); + p = p + CPS_PROGRAM_BUFFER_SIZE*4; + if (buff1_flag == 1) { + //wait finish + cps_wls_program_wait_cmd_done(); + if(result != CPS_WLS_SUCCESS) { + cps_wls_log(CPS_LOG_ERR, "[%s] ---- buffer1 program fail\n", __func__); + goto update_fail; + } + buff1_flag = 0; + } + //write buff 0 CMD + cps_wls_program_cmd_send(PGM_BUFFER0); + buff0_flag = 1; + continue; + } + if (buff1_flag == 0) { + //write buf1 + cps_wls_program_sram_addr32(ADDR_BUFFER1, p, CPS_PROGRAM_BUFFER_SIZE*4); + p = p + CPS_PROGRAM_BUFFER_SIZE*4; + if (buff0_flag == 1) { + //wait finish + cps_wls_program_wait_cmd_done(); + if(result != CPS_WLS_SUCCESS) { + cps_wls_log(CPS_LOG_ERR, "[%s] ---- buffer0 program fail\n", __func__); + goto update_fail; + } + buff0_flag = 0; + } + //write buff 0 CMD + cps_wls_program_cmd_send(PGM_BUFFER1); + buff1_flag = 1; + continue; + } + } + if (buff0_flag == 1) { + //wait finish + cps_wls_program_wait_cmd_done(); + if(result != CPS_WLS_SUCCESS) { + cps_wls_log(CPS_LOG_ERR, "[%s] ---- buffer0 program fail\n", __func__); + goto update_fail; + } + buff0_flag = 0; + } + + if (buff1_flag == 1) { + //wait finish + cps_wls_program_wait_cmd_done(); + if(result != CPS_WLS_SUCCESS) { + cps_wls_log(CPS_LOG_ERR, "[%s] ---- buffer1 program fail\n", __func__); + goto update_fail; + } + buff1_flag = 0; + } + +/*************************************************************************************** + * Step4, check app CRC * + ***************************************************************************************/ + cps_wls_program_cmd_send(CACL_CRC_APP); + result = cps_wls_program_wait_cmd_done(); + + if(result != CPS_WLS_SUCCESS) { + cps_wls_log(CPS_LOG_ERR, "[%s] ---- TEST APP CRC fail", __func__); + if(write_count < 3) goto start_write_app_code; + else goto update_fail; + }else { + cps_wls_log(CPS_LOG_ERR, "[%s] ---- TEST APP CRC success", __func__); + } + +/*************************************************************************************** + * Step5, write mcu start flag * + ***************************************************************************************/ + cps_wls_program_cmd_send(PGM_WR_FLAG); + result = cps_wls_program_wait_cmd_done(); + + if(result != CPS_WLS_SUCCESS) { + cps_wls_log(CPS_LOG_ERR, "[%s] ---- WRITE MCU START FLAG fail", __func__); + goto update_fail; + }else { + cps_wls_log(CPS_LOG_ERR, "[%s] ---- WRITE MCU START FLAG success", __func__); + } + + if(CPS_WLS_FAIL == cps_wls_write_word_addr32(0x40040004, big_little_endian_convert(0x00000001))) + goto update_fail; /*reset all system*/ + msleep(100); + +/*************************************************************************************** + * Step6, check chip id * + ***************************************************************************************/ + chip_id = cps_wls_read_word(0x0000); + pr_err("cps4019 FW_MAJOR=0x%04X\n",big_little_endian_convert(cps_wls_read_word(0x0002))&0xFFFF); + pr_err("cps4019 FW_MINOR=0x%04X\n",big_little_endian_convert(cps_wls_read_word(0x0004))&0xFFFF); + if(chip_id != CPS4019_CHIP_ID) { + cps_wls_log(CPS_LOG_DEBG, "[%s] ---- CHECK CHIP ID fail = %x\n", __func__, chip_id); + goto update_fail; + } + + cps_set_power(false); + + cps_wls_log(CPS_LOG_DEBG, "[%s] ---- Program successful CHIP ID=0x%04x\n", __func__, chip_id); + + return CPS_WLS_SUCCESS; + +update_fail: + cps_set_power(false); + cps_wls_log(CPS_LOG_ERR, "[%s] ---- update fail\n", __func__); + + return CPS_WLS_FAIL; +} + +static int erase_firmware(void) +{ + int ret; + int bootloader_length; + unsigned char *bootloader_buf; + unsigned short int chip_id; + int result; + + bootloader_buf = kzalloc(CPS4019_BL_SIZE, GFP_KERNEL);// 2K buffer + +/*************************************************************************************** + * Step1, load to sram * + ***************************************************************************************/ + ret = bootloader_load(bootloader_buf, &bootloader_length);//load bootloader + if (ret != 0) { + cps_wls_log(CPS_LOG_DEBG, "[%s] ---- bootloader get error %d\n", __func__, ret); + goto erase_fail; + } + + if(CPS_WLS_FAIL == cps_wls_write_word_addr32(0xFFFFFF00, big_little_endian_convert(0x0E000000))) + goto erase_fail; /*enable 32bit i2c*/ + + if(CPS_WLS_FAIL == cps_wls_write_word_addr32(0x40040070, big_little_endian_convert(0x0000A061))) + goto erase_fail; /*write password*/ + + if(CPS_WLS_FAIL == cps_wls_write_word_addr32(0x40040004, big_little_endian_convert(0x00000008))) + goto erase_fail; /*reset and halt mcu*/ + + if(CPS_WLS_FAIL == cps_wls_program_sram_addr32(0x20000000, bootloader_buf, bootloader_length)) + goto erase_fail;//program sram + + if(CPS_WLS_FAIL == cps_wls_write_word_addr32(0x40040010, big_little_endian_convert(0x00000001))) + goto erase_fail; /*triming load function is disabled*/ + + if(CPS_WLS_FAIL == cps_wls_write_word_addr32(0x40040004, big_little_endian_convert(0x00000066))) + goto erase_fail; /*enable remap function and reset the mcu*/ + + cps_wls_log(CPS_LOG_DEBG, "[%s] ---- system restart\n", __func__); + + msleep(10); + if(CPS_WLS_FAIL == cps_wls_write_word_addr32(0xFFFFFF00, big_little_endian_convert(0x0E000000))) + goto erase_fail; /*enable 32bit i2c*/ + msleep(10); + +/*************************************************************************************** + * Step2, bootloader crc check * + ***************************************************************************************/ + cps_wls_program_cmd_send(CACL_CRC_TEST); + result = cps_wls_program_wait_cmd_done(); + + if(result != CPS_WLS_SUCCESS) { + cps_wls_log(CPS_LOG_ERR, "[%s] ---- bootloader crc fail\n", __func__); + goto erase_fail; + } + cps_wls_log(CPS_LOG_DEBG, "[%s] ---- load bootloader successful\n", __func__); + + cps_wls_program_cmd_send(PGM_ERASER_0); + result = cps_wls_program_wait_cmd_done(); + + if(result != CPS_WLS_SUCCESS) { + cps_wls_log(CPS_LOG_ERR, "[%s] ---- bootloader crc fail\n", __func__); + goto erase_fail; + } + + +/*************************************************************************************** + * check chip id * + ***************************************************************************************/ + chip_id = cps_wls_read_word(0x0000); + cps_wls_log(CPS_LOG_DEBG, "[%s] ---- CHECK CHIP ID = %x\n", __func__, chip_id); + if(chip_id == CPS4019_CHIP_ID) { + goto erase_fail; + } + + return CPS_WLS_SUCCESS; + +erase_fail: + cps_wls_log(CPS_LOG_ERR, "[%s] ---- erase fail\n", __func__); + + return CPS_WLS_FAIL; +} + +//-------------------CPS4019 system interface------------------- +static int cps_wls_get_int_flag(void) +{ + cps_reg_s *cps_reg; + cps_reg = (cps_reg_s*)(&cps_reg_cfg[CPS_REG_INT]); + return cps_wls_read_reg((int)cps_reg->reg_addr, (int)cps_reg->reg_bytes_len); +} + +static int cps_wls_set_int_clr(int value) +{ + cps_reg_s *cps_reg; + cps_reg = (cps_reg_s*)(&cps_reg_cfg[CPS_REG_INT_CLEAR]); + return cps_wls_write_reg(cps_reg->reg_addr, value, (int)cps_reg->reg_bytes_len); +} + +static int cps_wls_get_chip_id(void) +{ + cps_reg_s *cps_reg; + cps_reg = (cps_reg_s*)(&cps_reg_cfg[CPS_REG_CHIP_ID]); + return cps_wls_read_reg(cps_reg->reg_addr, (int)cps_reg->reg_bytes_len); +} + +static int cps_wls_get_sys_fw_major_version(void) +{ + cps_reg_s *cps_reg; + cps_reg = (cps_reg_s*)(&cps_reg_cfg[CPS_REG_FW_MAJOR_REV]); + return cps_wls_read_reg(cps_reg->reg_addr, (int)cps_reg->reg_bytes_len); +} + +static int cps_wls_get_sys_fw_minor_version(void) +{ + cps_reg_s *cps_reg; + cps_reg = (cps_reg_s*)(&cps_reg_cfg[CPS_REG_FW_MINOR_REV]); + return cps_wls_read_reg(cps_reg->reg_addr, (int)cps_reg->reg_bytes_len); +} + +static int cps_wls_get_vrect(void) +{ + cps_reg_s *cps_reg; + cps_reg = (cps_reg_s*)(&cps_reg_cfg[CPS_REG_ADC_VRECT]); + return cps_wls_read_reg((int)cps_reg->reg_addr, (int)cps_reg->reg_bytes_len); +} + +static int cps_wls_get_iout(void) +{ + cps_reg_s *cps_reg; + cps_reg = (cps_reg_s*)(&cps_reg_cfg[CPS_REG_ADC_IOUT]); + return cps_wls_read_reg((int)cps_reg->reg_addr, (int)cps_reg->reg_bytes_len); +} + +static int cps_wls_get_vout(void) +{ + cps_reg_s *cps_reg; + cps_reg = (cps_reg_s*)(&cps_reg_cfg[CPS_REG_ADC_VOUT]); + return cps_wls_read_reg((int)cps_reg->reg_addr, (int)cps_reg->reg_bytes_len) * 10;//request unit is 0.1mV +} + +static int cps_wls_get_state(void) +{ + cps_reg_s *cps_reg; + + cps_reg = (cps_reg_s*)(&cps_reg_cfg[CPS_REG_STATUS]); + return cps_wls_read_reg(cps_reg->reg_addr, (int)cps_reg->reg_bytes_len); +} + +static bool cps_wls_get_vout_state(void) +{ + int ret = cps_wls_get_state(); + + if (ret == CPS_WLS_FAIL) + ret = 0; + + return !!(ret & (1<<6)); +} + +static int cps_wls_get_die_tmp(void) +{ + cps_reg_s *cps_reg; + cps_reg = (cps_reg_s*)(&cps_reg_cfg[CPS_REG_ADC_DIE_TEMP]); + return cps_wls_read_reg(cps_reg->reg_addr, (int)cps_reg->reg_bytes_len); +} + + +static int cps_wls_set_rx_vout_target(int value) +{ + cps_reg_s *cps_reg; + cps_reg = (cps_reg_s*)(&cps_reg_cfg[CPS_REG_VOUT_SET]); + return cps_wls_write_reg(cps_reg->reg_addr, value, (int)cps_reg->reg_bytes_len); +} + +static int cps_wls_set_rx_ocp_threshold(int value) +{ + int value_temp; + cps_reg_s *cps_reg; + if(value < 100 || value > 1100) + return CPS_WLS_FAIL; + + value_temp = value / 50; /*1LSB 50mA*/ + + cps_reg = (cps_reg_s*)(&cps_reg_cfg[CPS_REG_ILIM_SET]); + return cps_wls_write_reg(cps_reg->reg_addr, value_temp, (int)cps_reg->reg_bytes_len); +} + +static int cps_wls_get_op_mode(void) +{ + cps_reg_s *cps_reg; + cps_reg = (cps_reg_s*)(&cps_reg_cfg[CPS_REG_PRO_SET]); + return cps_wls_read_reg((int)cps_reg->reg_addr, (int)cps_reg->reg_bytes_len); +} + +static int cps_wls_wlc_update_light_fan(void) +{ + mmi_cmd_s* mmi_cmd_p = &mmi_cmd_cfg[MMI_CMD_FAN_LIGHT]; + + if (chip->is_factory_mode) { + cps_wls_log(CPS_LOG_ERR, "[%s] factory mode, skip\n", __func__); + return 0; + } + + if (chip->is_charger_full) { + mmi_cmd_p->data[MMI_DOCK_LIGHT_INDEX] = MMI_DOCK_LIGHT_ON; + mmi_cmd_p->data[MMI_DOCK_FAN_INDEX] = MMI_DOCK_FAN_SPEED_OFF; + } else { + if (chip->fan_speed == 0) + mmi_cmd_p->data[MMI_DOCK_FAN_INDEX] = MMI_DOCK_FAN_SPEED_LOW; + else + mmi_cmd_p->data[MMI_DOCK_FAN_INDEX] = MMI_DOCK_FAN_SPEED_HIGH; + + + if (chip->light_level == 0) + mmi_cmd_p->data[MMI_DOCK_LIGHT_INDEX] = MMI_DOCK_LIGHT_OFF; + else + mmi_cmd_p->data[MMI_DOCK_LIGHT_INDEX] = MMI_DOCK_LIGHT_DEFAULT; + } + + cps_wls_send_ppp_cmd(mmi_cmd_p->header, + mmi_cmd_p->cmd, + mmi_cmd_p->data, + mmi_cmd_p->len); + cps_wls_log(CPS_LOG_ERR, " CPS_WLS: QI set fan/light, WLS_WLC_FAN_SPEED %d " + "CPS_RX_CHRG_FULL %d, WLS_WLC_LIGHT %d", + chip->fan_speed, chip->is_charger_full, chip->light_level); + cps_wls_log(CPS_LOG_ERR, " CPS_WLS: QI set fan/light, ight 0x%x, fan 0x%x", + mmi_cmd_p->data[MMI_DOCK_LIGHT_INDEX], mmi_cmd_p->data[MMI_DOCK_FAN_INDEX]); + + return 0; +} + +static int cps_wls_charger_notify_full(void) +{ + mmi_cmd_s* mmi_cmd_p; + union power_supply_propval data; + bool low_power_flag; + + if (cps_get_power_supply_prop("battery", POWER_SUPPLY_PROP_CAPACITY, &data)) { + return CPS_WLS_FAIL; + } + + cps_wls_log(CPS_LOG_DEBG, "[%s] soc = %d.\n", __func__, data.intval); + if (data.intval != 100) { + chip->is_charger_full = false; + return CPS_WLS_FAIL; + } + + chip->is_charger_full = true; + + /*get vbus status first*/ + cps_check_power(&low_power_flag); + if (low_power_flag) { + cps_wls_set_status(WLC_DISCONNECTED); + return CPS_WLS_FAIL; + } + + if (!cps_wls_get_vout_state()) { + return CPS_WLS_FAIL; + } + + if (chip->op_mode == MMI_WLC_MOTO) { + cps_wls_wlc_update_light_fan(); + } else { + mmi_cmd_p = &mmi_cmd_cfg[MMI_CMD_CHARGE_FULL]; + cps_wls_send_ppp_cmd(mmi_cmd_p->header, + mmi_cmd_p->cmd, + mmi_cmd_p->data, + mmi_cmd_p->len); + } + + return CPS_WLS_SUCCESS; +} + +static int cps_wls_notify_st_changed(void) +{ + static int pre_status = -1; + + if (pre_status != chip->wlc_status) { + cps_wls_log(CPS_LOG_DEBG,"%s st change %d -> %d\n",__func__, pre_status, chip->wlc_status); + pre_status = chip->wlc_status; + sysfs_notify(&chip->wl_psy->dev.parent->kobj, NULL, "wlc_st_changed"); + } + + return 0; +} + +static int cps_wls_set_status(int status) +{ + chip->wlc_status = status; + if (status == WLC_DISCONNECTED) + chip->op_mode = MMI_WLC_NONE; + cps_wls_notify_st_changed(); + + return 0; +} +//------------------------------IRQ Handler----------------------------------- +static int cps_wls_set_int_enable(void) +{ + uint16_t int_en; + cps_reg_s *cps_reg; + + int_en = 0xFFFF; + cps_reg = (cps_reg_s*)(&cps_reg_cfg[CPS_REG_INT_ENABLE]); + + if(CPS_WLS_FAIL == cps_wls_write_reg((int)cps_reg->reg_addr, int_en, (int)cps_reg->reg_bytes_len)) + goto set_int_fail; + return CPS_WLS_SUCCESS; + +set_int_fail: + return CPS_WLS_FAIL; +} + +static int cps_wls_irq_process(int int_flag) +{ + int rc = 0; + + if (int_flag & INT_TX_DATA_RECEIVED) { + //todo + } + + if(int_flag & INT_UV) { + //todo + } + + if(int_flag & INT_OT) { + } + if(int_flag & INT_OC) { + } + if(int_flag & INT_OV) { + } + if(int_flag & INT_ID_CFG_FINISH) { + cps_wls_set_int_enable(); + } + if(int_flag & INT_VOUT_STATE) { + cps_wls_set_status(WLC_CONNECTED); + } + + /*for 50w dock*/ + if ((int_flag & INT_HS_OK) || (int_flag & INT_HS_FAIL)) { + chip->op_mode = cps_wls_get_op_mode(); + cps_wls_log(CPS_LOG_DEBG, "[%s] op_mode %d\n", __func__, chip->op_mode); + if (chip->op_mode == MMI_WLC_MOTO) { + cps_wls_set_status(WLC_TX_TYPE_CHANGED); + } + + cps_wls_charger_notify_full(); + } + + //if(int_flag & INT_DATA_STORE){} + //if(int_flag & INT_AC_MIS_DET){} + + return rc; +} + +static irqreturn_t cps_wls_irq_handler(int irq, void *dev_id) +{ + int int_flag; + int int_clr; + cps_wls_log(CPS_LOG_DEBG, "[%s] IRQ triggered\n", __func__); + mutex_lock(&chip->irq_lock); + + int_flag = cps_wls_get_int_flag(); + cps_wls_log(CPS_LOG_DEBG, ">>>>>int_flag = %x\n", int_flag); + if(int_flag == CPS_WLS_FAIL) { + cps_wls_log(CPS_LOG_ERR, "[%s] read wls irq reg failed\n", __func__); + mutex_unlock(&chip->irq_lock); + return IRQ_HANDLED; + } + + int_clr = int_flag; + cps_wls_set_int_clr(int_flag); + mutex_unlock(&chip->irq_lock); + + cps_wls_irq_process(int_flag); + + return IRQ_HANDLED; +} + +static enum power_supply_property cps_wls_chrg_props[] = { + POWER_SUPPLY_PROP_CURRENT_MAX, + //POWER_SUPPLY_PROP_CHARGING_ENABLED, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, +}; + +static int cps_wls_chrg_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CURRENT_MAX: + //case POWER_SUPPLY_PROP_CHARGING_ENABLED: + return 1; + + default: + break; + } + + return 0; +} + +static int cps_wls_chrg_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + bool low_power_flag; + int ret = 0; + + /*get vbus status first*/ + cps_check_power(&low_power_flag); + if (low_power_flag) { + cps_wls_set_status(WLC_DISCONNECTED); + val->intval = 0; + return ret; + } + + switch(psp) { + case POWER_SUPPLY_PROP_ONLINE: + case POWER_SUPPLY_PROP_PRESENT: + val->intval = cps_wls_get_vout_state(); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + val->intval = 5000000;//Have no usage, just for voltage max propety. + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = cps_wls_get_vout(); + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = cps_wls_get_iout();//cps_get_power_supply_prop("battery", psp, val); + break; + default: + return -EINVAL; + break; + } + + return ret; +} + +static int cps_wls_chrg_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + int ret = 0; + struct cps_wls_chrg_chip *chip = power_supply_get_drvdata(psy); + cps_wls_log(CPS_LOG_DEBG, "[%s] psp = %d.\n", __func__, psp); + chip->state = 1; + return ret; +} + +static void cps_wls_charger_external_power_changed(struct power_supply *psy) +{ + cps_wls_charger_notify_full(); +} + +//-----------------------------reg addr---------------------------------- +static ssize_t show_reg_addr(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "reg addr 0x%08x\n", chip->reg_addr); +} + +static ssize_t store_reg_addr(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int tmp; + + tmp = simple_strtoul(buf, NULL, 0); + chip->reg_addr = tmp; + + return count; +} +static DEVICE_ATTR(reg_addr, 0664, show_reg_addr, store_reg_addr); + +//-----------------------------reg data---------------------------------- +static ssize_t show_reg_data(struct device *dev, struct device_attribute *attr, char *buf) +{ + chip->reg_data = cps_wls_read_reg(chip->reg_addr, 4); + return sprintf(buf, "reg addr 0x%08x -> 0x%08x\n", chip->reg_addr, chip->reg_data); +} + +static ssize_t store_reg_data(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int tmp; + + tmp = simple_strtoul(buf, NULL, 0); + chip->reg_data = tmp; + cps_wls_write_reg(chip->reg_addr, chip->reg_data, 4); + + return count; +} +static DEVICE_ATTR(reg_data, 0664, show_reg_data, store_reg_data); + +static ssize_t store_update_fw(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int tmp; + + tmp = simple_strtoul(buf, NULL, 0); + if(tmp != 0) { + cps_wls_log(CPS_LOG_DEBG, "[%s] -------start update fw\n", __func__); + update_firmware(true); + } + + return count; +} +static DEVICE_ATTR(update_fw, 0220, NULL, store_update_fw); + +static ssize_t store_erase_fw(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int tmp; + + /*Only work in factory mode*/ + if (!chip->is_factory_mode) + return count; + + tmp = simple_strtoul(buf, NULL, 0); + /*TO void unfriend attacking, we just regard 0x55aa as a valid cmd value*/ + if(tmp == 0x55aa) { + cps_wls_log(CPS_LOG_DEBG, "[%s] -------start erase fw\n", __func__); + erase_firmware(); + } else { + cps_wls_log(CPS_LOG_DEBG, "[%s] unsupport operation\n", __func__); + } + + return count; +} +static DEVICE_ATTR(erase_fw, 0220, NULL, store_erase_fw); + +static ssize_t show_iout(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "iout = %dmA\n", cps_wls_get_iout()); +} +static DEVICE_ATTR(get_iout, 0444, show_iout, NULL); + +static ssize_t show_vrect(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "vrect = %dmV\n", cps_wls_get_vrect()); +} +static DEVICE_ATTR(get_vrect, 0444, show_vrect, NULL); + +static ssize_t show_vout(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "vout = %dmV\n", cps_wls_get_vout()); +} +static DEVICE_ATTR(get_vout, 0444, show_vout, NULL); + +static ssize_t show_chip_id(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "0x%x\n", cps_wls_get_chip_id()); +} +static DEVICE_ATTR(chip_id, 0444, show_chip_id, NULL); + +static ssize_t show_fw_major_ver(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "fw major version = 0x%x\n", cps_wls_get_sys_fw_major_version()); +} +static DEVICE_ATTR(get_fw_major_ver, 0444, show_fw_major_ver, NULL); + +static ssize_t show_fw_minor_ver(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "fw minor version = 0x%x\n", cps_wls_get_sys_fw_minor_version()); +} +static DEVICE_ATTR(get_fw_minor_ver, 0444, show_fw_minor_ver, NULL); + +static ssize_t show_die_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "die temp = %d\n", cps_wls_get_die_tmp()); +} +static DEVICE_ATTR(get_die_temp, 0444, show_die_temp, NULL); + +static ssize_t store_vout_target(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int tmp; + + tmp = simple_strtoul(buf, NULL, 0); + cps_wls_log(CPS_LOG_DEBG, "[%s] -------set vout target = %dmV\n", __func__ , tmp); + cps_wls_set_rx_vout_target(tmp); + + return count; +} +static DEVICE_ATTR(set_vout_target, 0220, NULL, store_vout_target); + +static ssize_t store_ocp_thres(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int tmp; + + tmp = simple_strtoul(buf, NULL, 0); + cps_wls_log(CPS_LOG_DEBG, "[%s] -------set ocp threshold = %dmA\n", __func__ , tmp); + cps_wls_set_rx_ocp_threshold(tmp); + + return count; +} +static DEVICE_ATTR(set_ocp_thres, 0220, NULL, store_ocp_thres); + +static ssize_t store_usb_keep_on(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + bool force_wls; + + force_wls = !!simple_strtoul(buf, NULL, 0); + + if (chip->main_chg_dev) + charger_dev_set_dp_dm(chip->main_chg_dev, force_wls ? + MMI_POWER_SUPPLY_IGNORE_REQUEST_DPDM : + MMI_POWER_SUPPLY_DONOT_IGNORE_REQUEST_DPDM); + + if(gpio_is_valid(chip->gpio_force_wls)) + gpio_direction_output(chip->gpio_force_wls, force_wls); + + return count; +} +static DEVICE_ATTR(usb_keep_on, 0220, NULL, store_usb_keep_on); + +static ssize_t show_wlc_fan_speed(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, " %d\n", chip->fan_speed); +} + +static ssize_t store_wlc_fan_speed(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + if (chip->op_mode != MMI_WLC_MOTO) { + cps_wls_log(CPS_LOG_ERR, "[%s] not moto 50w dock %d, skip\n", __func__ , chip->op_mode); + return count; + } + + chip->fan_speed = simple_strtoul(buf, NULL, 0); + cps_wls_wlc_update_light_fan(); + + return count; +} +static DEVICE_ATTR(wlc_fan_speed, S_IRUGO|S_IWUSR, show_wlc_fan_speed, store_wlc_fan_speed); + +static ssize_t show_wlc_light_ctl(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, " %d\n", chip->light_level); +} + +static ssize_t store_wlc_light_ctl(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + if (chip->op_mode != MMI_WLC_MOTO) { + cps_wls_log(CPS_LOG_ERR, "[%s] not moto 50w dock %d, skip\n", __func__ , chip->op_mode); + return count; + } + + chip->light_level = simple_strtoul(buf, NULL, 0); + cps_wls_wlc_update_light_fan(); + + return count; +} +static DEVICE_ATTR(wlc_light_ctl, S_IRUGO|S_IWUSR, show_wlc_light_ctl, store_wlc_light_ctl); + +static ssize_t show_wlc_tx_power(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", CPS4019_DEFAULT_POWER); +} +static DEVICE_ATTR(wlc_tx_power, 0444, show_wlc_tx_power, NULL); + +static ssize_t show_wlc_tx_type(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", chip->op_mode); +} + +static DEVICE_ATTR(wlc_tx_type, 0444, show_wlc_tx_type, NULL); + +static ssize_t show_wlc_tx_capability(struct device *dev, struct device_attribute *attr, char *buf) +{ + if (chip->op_mode == MMI_WLC_MOTO) + return sprintf(buf, "%d\n", FAN_CAPABILITY | LIGHT_CAPABILITY); + else + return sprintf(buf, "%d\n", 0); +} + +static DEVICE_ATTR(wlc_tx_capability, S_IRUGO, show_wlc_tx_capability, NULL); + +static ssize_t show_wlc_st_changed(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", chip->wlc_status); +} + +static DEVICE_ATTR(wlc_st_changed, S_IRUGO, show_wlc_st_changed, NULL); + +static ssize_t store_ppp_cmd(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int cmd, ret; + mmi_cmd_s* mmi_cmd_p; + + cmd = simple_strtoul(buf, NULL, 0); + + mmi_cmd_p = &mmi_cmd_cfg[cmd]; + ret = cps_wls_send_ppp_cmd(mmi_cmd_p->header, + mmi_cmd_p->cmd, + mmi_cmd_p->data, + mmi_cmd_p->len); + + cps_wls_log(CPS_LOG_ERR, "ppp cmd-%d 0x%x ret %d\n", + cmd, mmi_cmd_p->cmd, ret); + + return count; +} +static DEVICE_ATTR(ppp_cmd, 0220, NULL, store_ppp_cmd); + +static void cps_wls_create_device_node(struct device *dev) +{ + device_create_file(dev, &dev_attr_reg_addr); + device_create_file(dev, &dev_attr_reg_data); +//-----------------------program--------------------- + device_create_file(dev, &dev_attr_update_fw); + device_create_file(dev, &dev_attr_erase_fw); + +//-----------------------RX-------------------------- + device_create_file(dev, &dev_attr_get_iout); + device_create_file(dev, &dev_attr_get_vrect); + device_create_file(dev, &dev_attr_get_vout); + device_create_file(dev, &dev_attr_chip_id); + + device_create_file(dev, &dev_attr_get_fw_major_ver); + device_create_file(dev, &dev_attr_get_fw_minor_ver); + device_create_file(dev, &dev_attr_get_die_temp); + + device_create_file(dev, &dev_attr_set_vout_target); + device_create_file(dev, &dev_attr_set_ocp_thres); + + device_create_file(dev, &dev_attr_usb_keep_on); + + device_create_file(dev, &dev_attr_wlc_fan_speed); + device_create_file(dev, &dev_attr_wlc_light_ctl); + device_create_file(dev, &dev_attr_wlc_tx_power); + device_create_file(dev, &dev_attr_wlc_tx_type); + device_create_file(dev, &dev_attr_wlc_tx_capability); + device_create_file(dev, &dev_attr_wlc_st_changed); + + device_create_file(dev, &dev_attr_ppp_cmd); +} + +static int cps_wls_parse_dt(struct cps_wls_chrg_chip *chip) +{ + struct device_node *node = chip->dev->of_node; + + if(!node) { + cps_wls_log(CPS_LOG_ERR, "devices tree node missing \n"); + return -EINVAL; + } + + chip->wls_charge_int = of_get_named_gpio(node, "cps_wls_int", 0); + if(!gpio_is_valid(chip->wls_charge_int)) { + cps_wls_log(CPS_LOG_ERR, "wls_charge_int is not valid %d\n",chip->wls_charge_int ); + return -EINVAL; + } + + chip->gpio_force_wls = of_get_named_gpio(node, "force_wls_pin", 0); + if(gpio_is_valid(chip->gpio_force_wls)) { + if (!gpio_request(chip->gpio_force_wls, "mmi force wls pin")) + gpio_direction_output(chip->gpio_force_wls, 0); + else + dev_err(chip->dev, "%s: %d gpio(wls en) request failed\n", + __func__, chip->gpio_force_wls); + } + + chip->main_charger_name = NULL; + of_property_read_string(node, "main-charger-name", &chip->main_charger_name); + + chip->fw_ver_major = 0; + chip->fw_ver_minor = 0; + of_property_read_u32(node, "fw_ver_major", &chip->fw_ver_major); + of_property_read_u32(node, "fw_ver_minor", &chip->fw_ver_minor); + + chip->wls_fw_name = NULL; + of_property_read_string(node, "wireless-fw-name", &chip->wls_fw_name); + + + chip->use_bl_in_h = of_property_read_bool(node, "use_bl_in_h"); + + cps_wls_log(CPS_LOG_ERR, + "[%s] wls_charge_int %d gpio_force_wls %d \ + main_charger_name %s wls_fw_name: %s ver %d.%d use_bl_h %s\n", + __func__, + chip->wls_charge_int, chip->gpio_force_wls, + chip->main_charger_name ? chip->main_charger_name : "null", + chip->wls_fw_name ? chip->wls_fw_name : "null", + chip->fw_ver_major, chip->fw_ver_minor, + chip->use_bl_in_h ? "true" : "false"); + return 0; +} + +static int cps_wls_gpio_request(struct cps_wls_chrg_chip *chip) +{ + int ret =0; + int irqn = 0; + + if(gpio_is_valid(chip->wls_charge_int)) { + ret = gpio_request_one(chip->wls_charge_int, GPIOF_DIR_IN, "cps4019_ap_int"); + if(ret) { + cps_wls_log(CPS_LOG_ERR, "[%s] int gpio request failed\n", __func__); + goto err_irq_gpio; + } + irqn = gpio_to_irq(chip->wls_charge_int); + if(irqn < 0) { + ret = irqn; + cps_wls_log(CPS_LOG_ERR, "[%s] failed to gpio to irq\n", __func__); + goto err_irq_gpio; + } + chip->cps_wls_irq = irqn; + } else { + cps_wls_log(CPS_LOG_ERR, "[%s] reset gpio not provided\n", __func__); + goto err_irq_gpio; + } + +err_irq_gpio: + gpio_free(chip->wls_charge_int); + + return ret; +} + +static void cps_wls_lock_work_init(struct cps_wls_chrg_chip *chip) +{ + mutex_init(&chip->irq_lock); + mutex_init(&chip->i2c_lock); + //wake_lock_init(&chip->cps_wls_wake_lock, WAKE_LOCK_SUSPEND, "cps_wls_wake_lock"); + chip->cps_wls_wake_lock = wakeup_source_create( "cps_wls_wake_lock" ); + +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_init(&chip->cps_wls_wake_lock, WAKE_LOCK_SUSPEND, "cps_wls_wake_lock"); +#else + PM_WAKEUP_REGISTER(chip->dev, chip->cps_wls_wake_lock, "pen_suspend_blocker"); + if(!chip->cps_wls_wake_lock) { + dev_err(chip->dev,"%s: Failed to allocate wakeup source\n",__func__); + //goto fail_wakeup_init; + } +#endif + //INIT_DELAYED_WORK(&chip->cps_wls_monitor_work, cps_wls_monitor_work_func); +} + + +static void cps_wls_lock_destroy(struct cps_wls_chrg_chip *chip) +{ + mutex_destroy(&chip->irq_lock); + mutex_destroy(&chip->i2c_lock); + //wake_lock_destroy(&chip->cps_wls_wake_lock); + //wakeup_source_destroy(chip->cps_wls_wake_lock); +#ifdef CONFIG_HAS_WAKELOCK + wake_lock_destroy(&chip->cps_wls_wake_lock); +#else + PM_WAKEUP_UNREGISTER(chip->cps_wls_wake_lock); +#endif + //cancel_delayed_work_sync(&chip->cps_wls_monitor_work); +} + +static void cps_wls_free_gpio(struct cps_wls_chrg_chip *chip) +{ + if(gpio_is_valid(chip->wls_charge_int)) + gpio_free(chip->wls_charge_int); +} + +static int cps_wls_set_suppliers(struct cps_wls_chrg_chip *chip) +{ + int count, i; + int ret = 0; + + if (chip->wl_psy->num_supplies && chip->batt_psy->supplied_from) { + cps_wls_log(CPS_LOG_ERR, "already set\n"); + return 0; + } + + count = of_property_count_strings(chip->dev->of_node, "supplied-from"); + if (count <= 0) { + cps_wls_log(CPS_LOG_ERR, "No supplier found, rc=%d\n", count); + return -EINVAL; + } + + chip->wl_psy->supplied_from = devm_kmalloc_array(&chip->wl_psy->dev, + count, + sizeof(char *), + GFP_KERNEL); + if (!chip->wl_psy->supplied_from) { + cps_wls_log(CPS_LOG_ERR, "Failed to get supplied-from, %d\n", ret); + return -ENOMEM; + } + + ret = of_property_read_string_array(chip->dev->of_node, "supplied-from", + (const char **)chip->wl_psy->supplied_from, + count); + if (ret < 0) { + cps_wls_log(CPS_LOG_ERR, "Failed to get supplied-from, %d\n", ret); + return ret; + } + + chip->wl_psy->num_supplies = count; + + for (i = 0; i < chip->wl_psy->num_supplies; i++) { + cps_wls_log(CPS_LOG_ERR, "Supplier-%d=%s\n", i, + chip->wl_psy->supplied_from[i]); + } + + return 0; +} + +static int cps_wls_register_psy(struct cps_wls_chrg_chip *chip) +{ + struct power_supply_config cps_wls_psy_cfg = {}; + + chip->wl_psd.name = CPS_WLS_CHRG_PSY_NAME; + chip->wl_psd.type = POWER_SUPPLY_TYPE_WIRELESS; + chip->wl_psd.properties = cps_wls_chrg_props; + chip->wl_psd.num_properties = ARRAY_SIZE(cps_wls_chrg_props); + chip->wl_psd.get_property = cps_wls_chrg_get_property; + chip->wl_psd.set_property = cps_wls_chrg_set_property; + chip->wl_psd.property_is_writeable= cps_wls_chrg_property_is_writeable; + chip->wl_psd.external_power_changed = cps_wls_charger_external_power_changed; + + cps_wls_psy_cfg.drv_data = chip; + cps_wls_psy_cfg.of_node = chip->dev->of_node; + chip->wl_psy = power_supply_register(chip->dev, &chip->wl_psd, &cps_wls_psy_cfg); + if(IS_ERR(chip->wl_psy)) { + return PTR_ERR(chip->wl_psy); + } + + cps_wls_set_suppliers(chip); + + return CPS_WLS_SUCCESS; +} + +static int cps_wls_chrg_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret=0; + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) { + cps_wls_log(CPS_LOG_ERR,"[%s] cps_debug: Unable to allocate memory\n", __func__); + return -ENOMEM; + } + chip->client = client; + chip->dev = &client->dev; + chip->name = "cps_wls"; + chip->is_charger_full = false; + chip->op_mode = MMI_WLC_NONE; + chip->fan_speed = 1; + chip->light_level = 1; + + chip->regmap = devm_regmap_init_i2c(client, &cps4019_regmap_config); + if (IS_ERR(chip->regmap)) { + cps_wls_log(CPS_LOG_ERR, "[%s] Failed to allocate regmap!\n", __func__); + devm_kfree(&client->dev, chip); + return PTR_ERR(chip->regmap); + } + chip->regmap32 = devm_regmap_init_i2c(client, &cps4019_regmap_32bit_config); + if (IS_ERR(chip->regmap32)) { + cps_wls_log(CPS_LOG_ERR, "[%s] Failed to allocate regmap32!\n", __func__); + devm_kfree(&client->dev, chip); + return PTR_ERR(chip->regmap32); + } + i2c_set_clientdata(client, chip); + dev_set_drvdata(&(client->dev), chip); + + ret = cps_wls_parse_dt(chip); + if(ret < 0) { + cps_wls_log(CPS_LOG_ERR, "[%s] Couldn't parse DT nodes ret = %d\n", __func__, ret); + goto free_source; + } + + ret = cps_wls_gpio_request(chip); + if(ret < 0) { + cps_wls_log(CPS_LOG_ERR, "[%s] gpio request failed ret = %d\n", __func__, ret); + goto free_source; + } + + if(chip->cps_wls_irq) { + ret = devm_request_threaded_irq(&client->dev, chip->cps_wls_irq, NULL, + cps_wls_irq_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "cps_wls_irq", chip); + if(ret) { + cps_wls_log(CPS_LOG_ERR, "[%s] request cps_wls_int irq failed ret = %d\n", __func__, ret); + goto free_source; + } + enable_irq_wake(chip->cps_wls_irq); + } + cps_wls_lock_work_init(chip); + + if (chip->main_charger_name) { + chip->main_chg_dev = get_charger_by_name(chip->main_charger_name); + if (!chip->main_chg_dev) { + cps_wls_log(chip, "*** Error : can't find main charger %s***\n", chip->main_charger_name); + } + } + + chip->is_factory_mode = mmi_is_factory_mode(); + + cps_wls_create_device_node(&(client->dev)); + + chip->otg_channel = devm_iio_channel_get(chip->dev, "otg_enable"); + if (IS_ERR(chip->otg_channel)) { + cps_wls_log(CPS_LOG_ERR, "[%s] get otg iio dev failed. Waiting for retry\n", __func__); + ret = -1;//EPROBE_DEFER; + goto free_source; + } + + ret = cps_wls_register_psy(chip); + if(IS_ERR(chip->wl_psy)) { + cps_wls_log(CPS_LOG_ERR, "[%s] power_supply_register wireless failed , ret = %d\n", __func__, ret); + goto free_source; + } + + cps_wls_log(CPS_LOG_DEBG, "[%s] wireless charger addr low probe successful!\n", __func__); + + update_firmware(false); + + if (cps_wls_get_vout_state() && + chip->main_chg_dev) { + charger_dev_notify(chip->main_chg_dev); + } + + return ret; + +free_source: + cps_wls_free_gpio(chip); + cps_wls_lock_destroy(chip); + kfree(chip); + cps_wls_log(CPS_LOG_ERR, "[%s] error: free resource.\n", __func__); + + return ret; +} + +static void not_called_api(void) +{ + /*int rc; + rc = cps_wls_get_rx_ss_pkt_value(); + rc = cps_wls_get_rx_ce_pkt_value(); + rc = cps_wls_get_rx_rp_pkt_value(); + rc = cps_wls_get_rx_fop_value(); + rc = cps_wls_get_rx_ept_code(); + rc = cps_wls_get_rx_neg_power(); + rc = cps_wls_get_rx_neg_pro(); + rc = cps_wls_get_rx_vrect(); + rc = cps_wls_get_rx_iout(); + rc = cps_wls_get_rx_vout(); + rc = cps_wls_get_rx_die_tmp(); + rc = cps_wls_set_rx_vout_target(5000); + rc = cps_wls_set_rx_neg_power(20); + rc = cps_wls_get_tx_ce_value(); + rc = cps_wls_get_tx_rp_value();*/ + return; +} + +static int cps_wls_chrg_remove(struct i2c_client *client) +{ + not_called_api(); + //cps_wls_lock_destroy(chip); + kfree(chip); + return 0; +} + +static const struct i2c_device_id cps_wls_charger_id[] = { + {"cps-wls-charger", 0}, + {}, +}; + +static const struct of_device_id cps_wls_chrg_of_tbl[] = { + { .compatible = "cps,wls-charger-cps4019", .data = NULL}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, cps_wls_charger_id); + +static struct i2c_driver cps_wls_charger_driver = { + .driver = { + .name = CPS_WLS_CHRG_DRV_NAME, + .owner = THIS_MODULE, + .of_match_table = cps_wls_chrg_of_tbl, + }, + .probe = cps_wls_chrg_probe, + .remove = cps_wls_chrg_remove, + .id_table = cps_wls_charger_id, +}; + +static int __init cps_wls_driver_init(void) +{ + return i2c_add_driver(&cps_wls_charger_driver); +} + +late_initcall(cps_wls_driver_init); + +static void __exit cps_wls_driver_exit(void) +{ + i2c_del_driver(&cps_wls_charger_driver); +} + +module_exit(cps_wls_driver_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("jian.deng@convenientpower.com"); +MODULE_DESCRIPTION("cps_wls_charger driver"); +MODULE_ALIAS("i2c:cps_wls_charger"); + +/*WARNING: module cps4019_wls_charger uses symbol vfs_getattr from + namespace VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver, +but does not import it. +*/ +MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); diff --git a/drivers/power/cps4019_wls_charger/cps4019_wls_charger.h b/drivers/power/cps4019_wls_charger/cps4019_wls_charger.h new file mode 100644 index 000000000000..cff0928701d9 --- /dev/null +++ b/drivers/power/cps4019_wls_charger/cps4019_wls_charger.h @@ -0,0 +1,76 @@ +/* + * Copyright © 2020, ConvenientPower + * + * 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. + * + */ + +#ifndef __CPS_WLS_CHARGER_H__ +#define __CPS_WLS_CHARGER_H__ + +#define CPS_WLS_FAIL -1 +#define CPS_WLS_SUCCESS 0 + +/*regester operation*/ +#define REG_CMD_SEND_RX_SHIFT 0 +#define REG_CMD_SEND_RX_MASK (1 << REG_CMD_SEND_RX_SHIFT) + +/*interupt define*/ +#define INT_TX_DATA_RECEIVED (0x01 << 0) +#define INT_UV (0x01 << 1) +#define INT_OT (0x01 << 2) +#define INT_OV (0x01 << 3) +#define INT_OC (0x01 << 4) +#define INT_ID_CFG_FINISH (0x01 << 5) +#define INT_VOUT_STATE (0x01 << 6) +#define INT_HS_OK (0x01 << 7) +#define INT_HS_FAIL (0x01 << 8) +#define INT_RSV_BIT_9 (0x01 << 9) +#define INT_RSV_BIT_A (0x01 << 10) +#define INT_RSV_BIT_B (0x01 << 11) +#define INT_RSV_BIT_C (0x01 << 12) +#define INT_RSV_BIT_D (0x01 << 13) +#define INT_RSV_BIT_E (0x01 << 14) +#define INT_RSV_BIT_F (0x01 << 15) + +/*command define*/ +#define CMD_SEND_RX_DATA (0x01 << 0) +#define CMD_LDO_TOGGLE (0x01 << 1) +#define CMD_MCU_RESET (0x01 << 2) +#define CMD_SEND_EPT_PKT (0x01 << 3) +#define CMD_SEND_CS_PKT (0x01 << 4) +#define CMD_CLEAR_INT (0x01 << 5) +//#define CMD_WATCHDOG_RESET (0x01 << 6) +#define CMD_WATCHDOG_EN (0x01 << 7) + +/*firmware download define*/ +#define ADDR_BUFFER0 0x20000600 +#define ADDR_BUFFER1 0x20000700 + +#define ADDR_CMD 0x200005FC +#define ADDR_FLAG 0x200005F8 +#define ADDR_BUF_SIZE 0x200005F4 + +#define PGM_BUFFER0 0x10 +#define PGM_BUFFER1 0x20 +#define PGM_BUFFER2 0x30 +#define PGM_BUFFER3 0x40 +#define PGM_BUFFER0_1 0x50 +#define PGM_ERASER_0 0x60 +#define PGM_ERASER_1 0x70 +#define PGM_WR_FLAG 0x80 + +#define CACL_CRC_APP 0x90 +#define CACL_CRC_TEST 0xB0 + +#define PGM_ADDR_SET 0xC0 + +#define RUNNING 0x66 +#define PASS 0x55 +#define FAIL 0xAA +#define ILLEGAL 0x40 +#define CPS_PROGRAM_BUFFER_SIZE 64 + +#endif diff --git a/drivers/power/cw2217b_fg_mmi/Android.mk b/drivers/power/cw2217b_fg_mmi/Android.mk new file mode 100644 index 000000000000..e65718be8956 --- /dev/null +++ b/drivers/power/cw2217b_fg_mmi/Android.mk @@ -0,0 +1,15 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := cw2217b_fg_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + +ifeq ($(DYNAMIC_UPDATE_UI_FULL),true) + KERNEL_CFLAGS += CONFIG_DYNAMIC_UPDATE_UI_FULL=y + KBUILD_OPTIONS += CONFIG_DYNAMIC_UPDATE_UI_FULL=y +endif +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/cw2217b_fg_mmi/Kbuild b/drivers/power/cw2217b_fg_mmi/Kbuild new file mode 100644 index 000000000000..a84af03167d3 --- /dev/null +++ b/drivers/power/cw2217b_fg_mmi/Kbuild @@ -0,0 +1,11 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/drivers/power/cw2217b_fg_mmi + +ifneq ($(filter m y,$(CONFIG_DYNAMIC_UPDATE_UI_FULL)),) + EXTRA_CFLAGS += -DDYNAMIC_UPDATE_UI_FULL +endif + +cw2217b_fg_mmi-objs += cw2217B_fuel_gauge_base.o +obj-m += cw2217b_fg_mmi.o diff --git a/drivers/power/cw2217b_fg_mmi/Makefile b/drivers/power/cw2217b_fg_mmi/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/power/cw2217b_fg_mmi/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/power/cw2217b_fg_mmi/cw2217B_fuel_gauge_base.c b/drivers/power/cw2217b_fg_mmi/cw2217B_fuel_gauge_base.c new file mode 100644 index 000000000000..51cbfdd71f27 --- /dev/null +++ b/drivers/power/cw2217b_fg_mmi/cw2217B_fuel_gauge_base.c @@ -0,0 +1,1413 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CWFG_ENABLE_LOG 1 /* CHANGE Customer need to change this for enable/disable log */ + +#define CW_PROPERTIES cw_bat->bms_name + +#define REG_CHIP_ID 0x00 +#define REG_VCELL_H 0x02 +#define REG_VCELL_L 0x03 +#define REG_SOC_INT 0x04 +#define REG_SOC_DECIMAL 0x05 +#define REG_TEMP 0x06 +#define REG_MODE_CONFIG 0x08 +#define REG_GPIO_CONFIG 0x0A +#define REG_SOC_ALERT 0x0B +#define REG_TEMP_MAX 0x0C +#define REG_TEMP_MIN 0x0D +#define REG_CURRENT_H 0x0E +#define REG_CURRENT_L 0x0F +#define REG_T_HOST_H 0xA0 +#define REG_T_HOST_L 0xA1 +#define REG_USER_CONF 0xA2 +#define REG_CYCLE_H 0xA4 +#define REG_CYCLE_L 0xA5 +#define REG_SOH 0xA6 +#define REG_IC_STATE 0xA7 +#define REG_STB_CUR_H 0xA8 +#define REG_STB_CUR_L 0xA9 +#define REG_FW_VERSION 0xAB +#define REG_BAT_PROFILE 0x10 + +#define CONFIG_MODE_RESTART 0x30 +#define CONFIG_MODE_ACTIVE 0x00 +#define CONFIG_MODE_SLEEP 0xF0 +#define CONFIG_UPDATE_FLG 0x80 +#define IC_VCHIP_ID 0xA0 +#define IC_READY_MARK 0x0C + +#define GPIO_ENABLE_MIN_TEMP 0 +#define GPIO_ENABLE_MAX_TEMP 0 +#define GPIO_ENABLE_SOC_CHANGE 0 +#define GPIO_SOC_IRQ_VALUE 0x0 /* 0x7F */ +#define DEFINED_MAX_TEMP 45 +#define DEFINED_MIN_TEMP 0 + +#define CWFG_NAME "cw2217" +#define SIZE_OF_PROFILE 80 +#define USER_RSENSE (10*1000) /* mhom rsense * 1000 for convenience calculation */ + +#define queue_delayed_work_time 8000 +#define queue_start_work_time 50 + +#define CW_SLEEP_20MS 20 +#define CW_SLEEP_10MS 10 +#define CW_UI_FULL 100 +#define COMPLEMENT_CODE_U16 0x8000 +#define CW_SLEEP_100MS 100 +#define CW_SLEEP_200MS 200 +#define CW_SLEEP_COUNTS 50 +#define CW_TRUE 1 +#define CW_RETRY_COUNT 3 +#define CW_VOL_UNIT 1000 +#define CW_CUR_UNIT 1000 + +#define CW_CUR_ACCURACY 10000 + +#define CW2217_NOT_ACTIVE 1 +#define CW2217_PROFILE_NOT_READY 2 +#define CW2217_PROFILE_NEED_UPDATE 3 + +#define CW_BPD_TEMP (-400) + +#define cw_printk(fmt, arg...) \ + { \ + if (CWFG_ENABLE_LOG) \ + printk("FG_CW2217 : %s-%d : " fmt, __FUNCTION__ ,__LINE__,##arg); \ + else {} \ + } + +#define cw_info(fmt, arg...) \ + printk("FG_CW2217 : %s-%d : " fmt, __FUNCTION__ ,__LINE__,##arg) + + +static unsigned char config_profile_info[SIZE_OF_PROFILE] = {0}; +/* +static unsigned char config_profile_info[SIZE_OF_PROFILE] = { + 0x5A,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xA6,0xB1,0xB2,0xCE,0xC1,0xC1,0x9E,0x5F, + 0x36,0xFF,0xFF,0xF9,0xBA,0x92,0x7F,0x6B, + 0x60,0x5D,0x58,0x94,0xE7,0xDE,0x9A,0xDC, + 0xC2,0xCA,0xD2,0xD7,0xD6,0xD3,0xD1,0xCC, + 0xCA,0xC9,0xCE,0xB8,0x9C,0x92,0x88,0x81, + 0x75,0x71,0x7F,0x91,0xAB,0x83,0x71,0x70, + 0x20,0x00,0x57,0x10,0x02,0xB0,0x5D,0x00, + 0x00,0x00,0x64,0x26,0xD3,0x51,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x94, +}; +*/ +struct cw_battery { + struct i2c_client *client; + + struct workqueue_struct *cwfg_workqueue; + struct delayed_work battery_delay_work; + struct work_struct hw_init_work; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct power_supply cw_bat; +#else + struct power_supply *cw_bat; + struct power_supply *batt_psy; + struct power_supply *usb_psy; +#endif + int chip_id; + int voltage; + int ic_soc_h; + int ic_soc_l; + int ui_soc; + int raw_soc; + int temp; + long cw_current; + int cycle; + int soh; + int fw_version; + int fcc_design; + int fcc; + int ui_full; + int ntc_exist; + int batt_status; + bool present; + bool factory_mode; + int sense_r_mohm; +#if 0 + long stb_current; +#endif + int ibat_polority; + const char *bms_name; + struct regulator *vdd_i2c_vreg; +}; + +/* CW2217 iic read function */ +static int cw_read(struct i2c_client *client, unsigned char reg, unsigned char buf[]) +{ + int ret; + + ret = i2c_smbus_read_i2c_block_data( client, reg, 1, buf); + if (ret < 0) + printk("IIC error %d\n", ret); + + return ret; +} + +/* CW2217 iic write function */ +static int cw_write(struct i2c_client *client, unsigned char reg, unsigned char const buf[]) +{ + int ret; + + ret = i2c_smbus_write_i2c_block_data( client, reg, 1, &buf[0] ); + if (ret < 0) + printk("IIC error %d\n", ret); + + return ret; +} + +/* CW2217 iic read word function */ +static int cw_read_word(struct i2c_client *client, unsigned char reg, unsigned char buf[]) +{ + int ret; + unsigned char reg_val[2] = { 0, 0 }; + unsigned int temp_val_buff; + unsigned int temp_val_second; + + ret = i2c_smbus_read_i2c_block_data( client, reg, 2, reg_val ); + if (ret < 0) + printk("IIC error %d\n", ret); + temp_val_buff = (reg_val[0] << 8) + reg_val[1]; + + msleep(1); + ret = i2c_smbus_read_i2c_block_data( client, reg, 2, reg_val ); + if (ret < 0) + printk("IIC error %d\n", ret); + temp_val_second = (reg_val[0] << 8) + reg_val[1]; + + if (temp_val_buff != temp_val_second) { + msleep(1); + ret = i2c_smbus_read_i2c_block_data( client, reg, 2, reg_val ); + if (ret < 0) + printk("IIC error %d\n", ret); + temp_val_buff = (reg_val[0] << 8) + reg_val[1]; + } + + buf[0] = reg_val[0]; + buf[1] = reg_val[1]; + + return ret; +} + +/* CW2217 iic write profile function */ +static int cw_write_profile(struct i2c_client *client, unsigned char const buf[]) +{ + int ret; + int i; + + for (i = 0; i < SIZE_OF_PROFILE; i++) { + ret = cw_write(client, REG_BAT_PROFILE + i, &buf[i]); + if (ret < 0) { + printk("IIC error %d\n", ret); + return ret; + } + } + + return ret; +} + +/* + * CW2217 Active function + * The CONFIG register is used for the host MCU to configure the fuel gauge IC. The default value is 0xF0, + * SLEEP and RESTART bits are set. To power up the IC, the host MCU needs to write 0x30 to exit shutdown + * mode, and then write 0x00 to restart the gauge to enter active mode. To reset the IC, the host MCU needs + * to write 0xF0, 0x30 and 0x00 in sequence to this register to complete the restart procedure. The CW2217B + * will reload relevant parameters and settings and restart SOC calculation. Note that the SOC may be a + * different value after reset operation since it is a brand-new calculation based on the latest battery status. + * CONFIG [3:0] is reserved. Don't do any operation with it. + */ +static int cw2217_active(struct cw_battery *cw_bat) +{ + int ret; + unsigned char reg_val = CONFIG_MODE_RESTART; + + cw_printk("\n"); + + ret = cw_write(cw_bat->client, REG_MODE_CONFIG, ®_val); + if (ret < 0) + return ret; + msleep(CW_SLEEP_20MS); /* Here delay must >= 20 ms */ + + reg_val = CONFIG_MODE_ACTIVE; + ret = cw_write(cw_bat->client, REG_MODE_CONFIG, ®_val); + if (ret < 0) + return ret; + msleep(CW_SLEEP_10MS); + + return 0; +} + +/* + * CW2217 Sleep function + * The CONFIG register is used for the host MCU to configure the fuel gauge IC. The default value is 0xF0, + * SLEEP and RESTART bits are set. To power up the IC, the host MCU needs to write 0x30 to exit shutdown + * mode, and then write 0x00 to restart the gauge to enter active mode. To reset the IC, the host MCU needs + * to write 0xF0, 0x30 and 0x00 in sequence to this register to complete the restart procedure. The CW2217B + * will reload relevant parameters and settings and restart SOC calculation. Note that the SOC may be a + * different value after reset operation since it is a brand-new calculation based on the latest battery status. + * CONFIG [3:0] is reserved. Don't do any operation with it. + */ +static int cw2217_sleep(struct cw_battery *cw_bat) +{ + int ret; + unsigned char reg_val = CONFIG_MODE_RESTART; + + cw_printk("\n"); + + ret = cw_write(cw_bat->client, REG_MODE_CONFIG, ®_val); + if (ret < 0) + return ret; + msleep(CW_SLEEP_20MS); /* Here delay must >= 20 ms */ + + reg_val = CONFIG_MODE_SLEEP; + ret = cw_write(cw_bat->client, REG_MODE_CONFIG, ®_val); + if (ret < 0) + return ret; + msleep(CW_SLEEP_10MS); + + return 0; +} + +/* + * The 0x00 register is an UNSIGNED 8bit read-only register. Its value is fixed to 0xA0 in shutdown + * mode and active mode. + */ +static int cw_get_chip_id(struct cw_battery *cw_bat) +{ + int ret; + unsigned char reg_val; + int chip_id; + + ret = cw_read(cw_bat->client, REG_CHIP_ID, ®_val); + if (ret < 0) + return ret; + + chip_id = reg_val; /* This value must be 0xA0! */ + cw_printk("chip_id = %d\n", chip_id); + cw_bat->chip_id = chip_id; + + return 0; +} + +/* + * The VCELL register(0x02 0x03) is an UNSIGNED 14bit read-only register that updates the battery voltage continuously. + * Battery voltage is measured between the VCELL pin and VSS pin, which is the ground reference. A 14bit + * sigma-delta A/D converter is used and the voltage resolution is 312.5uV. (0.3125mV is *5/16) + */ +static int cw_get_voltage(struct cw_battery *cw_bat) +{ + int ret; + unsigned char reg_val[2] = {0 , 0}; + unsigned int voltage; + + ret = cw_read_word(cw_bat->client, REG_VCELL_H, reg_val); + if (ret < 0) + return ret; + voltage = (reg_val[0] << 8) + reg_val[1]; + voltage = voltage * 5 / 16; + cw_bat->voltage = voltage; + + return 0; +} + +/* + * The SOC register(0x04 0x05) is an UNSIGNED 16bit read-only register that indicates the SOC of the battery. The + * SOC shows in % format, which means how much percent of the battery's total available capacity is + * remaining in the battery now. The SOC can intrinsically adjust itself to cater to the change of battery status, + * including load, temperature and aging etc. + * The high byte(0x04) contains the SOC in 1% unit which can be directly used if this resolution is good + * enough for the application. The low byte(0x05) provides more accurate fractional part of the SOC and its + * LSB is (1/256) %. + */ + #ifndef MAX_VAL + #define MAX_VAL( x, y ) ( ((x) > (y)) ? (x) : (y) ) +#endif + +#ifdef DYNAMIC_UPDATE_UI_FULL +#define TIMER_INTERVALS 1000 * 60 /* unit:ms */ +static bool jiffies_timer_expire(bool clear_flag) { + unsigned long cur_jiffies = jiffies; + static unsigned long last_jiffies = 0; + bool ret = false; + + if (!last_jiffies || clear_flag) { + last_jiffies = cur_jiffies; + return ret; + } + + if (time_after(cur_jiffies, msecs_to_jiffies(TIMER_INTERVALS) + last_jiffies)) + ret = true; + else + ret = false; + + if (ret) { + cw_printk("expire 60s, curr_jiffies:%dms, last jiffies %d", + jiffies_to_msecs(cur_jiffies), jiffies_to_msecs(last_jiffies)); + last_jiffies = cur_jiffies; + + } + return ret; +} + +static int cw_set_ui_full(struct cw_battery *cw_bat, int ui_full) +{ + int i, ret = 0; + int retry = 3; + unsigned char reg_val = ui_full; + + for (i = 0; i < retry; i++) { + ret = cw_write(cw_bat->client, REG_USER_CONF, ®_val); + if (ret < 0) { + msleep(CW_SLEEP_10MS); + cw_printk("failed to write ui_full to reg, retry = %d", i); + } else { + cw_printk("success save ui_full %d to reg", ui_full); + break; + } + } + return ret; +} + +static int cw_get_ui_full(struct cw_battery *cw_bat, int *ui_full) { + int i, ret = 0; + int retry = 3; + unsigned char reg_val; + + for (i = 0; i < retry; i++) { + ret = cw_read(cw_bat->client, REG_USER_CONF, ®_val); + if (ret >= 0 && reg_val <= CW_UI_FULL && reg_val >= cw_bat->ui_full) { + *ui_full = reg_val; + cw_printk("success read ui_full %d from reg", *ui_full); + break; + } + msleep(CW_SLEEP_10MS); + } + return ret; +} +#endif + +static void cw_get_batt_status(struct cw_battery *cw_bat) +{ + long batt_curr = 0; + batt_curr = cw_bat->cw_current * CW_CUR_UNIT; + batt_curr *= cw_bat->ibat_polority; + + if (cw_bat->voltage <= 0 || cw_bat->temp <= CW_BPD_TEMP) + cw_bat->present = 0; + else + cw_bat->present = 1; + + if (!cw_bat->present) { + cw_bat->batt_status = POWER_SUPPLY_STATUS_UNKNOWN; + } else if (cw_bat->ui_soc == CW_UI_FULL) { + cw_bat->batt_status = POWER_SUPPLY_STATUS_FULL; + } else if (abs(batt_curr) < CW_CUR_ACCURACY) { + cw_bat->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else if (batt_curr < 0) { + cw_bat->batt_status = POWER_SUPPLY_STATUS_CHARGING; + } else { + cw_bat->batt_status = POWER_SUPPLY_STATUS_DISCHARGING; + } +} + +static int cw_get_capacity(struct cw_battery *cw_bat) +{ + int ret; + unsigned char reg_val[2] = { 0, 0 }; + int soc_h; + int soc_l; + int ui_soc; + int remainder; + int chr_st_now = 0;// 0 discharge, 1 charging; + union power_supply_propval usb_online = {0}; +#ifndef DYNAMIC_UPDATE_UI_FULL + int ui_100 = cw_bat->ui_full; +#else + static int ui_full_pre = 0; + int ui_full_temp = 0; +#endif + + cw_get_batt_status(cw_bat); + + if (cw_bat->batt_status == POWER_SUPPLY_STATUS_CHARGING) + chr_st_now = 1; + else + chr_st_now = 0; + + ret = cw_read_word(cw_bat->client, REG_SOC_INT, reg_val); + if (ret < 0) + return ret; + soc_h = reg_val[0]; + soc_l = reg_val[1]; + cw_bat->raw_soc = soc_h; + +#ifndef DYNAMIC_UPDATE_UI_FULL + ui_soc = ((soc_h * 256 + soc_l) * 100)/ (ui_100 * 256); + remainder = (((soc_h * 256 + soc_l) * 100 * 100) / (ui_100 * 256)) % 100; +#else + if (ui_full_pre == 0) { + ret = cw_get_ui_full(cw_bat, &ui_full_temp); + if (ret < 0 || ui_full_temp == 0) { + ui_full_pre = cw_bat->ui_full; + } else { + ui_full_pre = ui_full_temp; + } + cw_printk("CW2015[%d]: UI_FULL INIT is %d", __LINE__, ui_full_pre); + } + ui_full_temp = ui_full_pre; + + if (chr_st_now) { + if (ui_full_temp > cw_bat->ui_full) { + if (jiffies_timer_expire(false)) { + ui_full_temp -= 1; + cw_set_ui_full(cw_bat, ui_full_temp); + cw_printk("CW2015[%d]: UI_FULL-- %d!!!!\n", __LINE__, ui_full_temp); + } + } + } + + if (!chr_st_now) { + if ((soc_h * 256 + soc_l) / 256 > ui_full_temp) { + cw_printk("CW2015[%d]: update UI_FULL to %d from %d !!!!\n", __LINE__, (soc_h * 256 + soc_l) / 256, ui_full_temp); + ui_full_temp = (soc_h * 256 + soc_l) / 256; + cw_set_ui_full(cw_bat, ui_full_temp); + } + jiffies_timer_expire(true); + } + + ui_full_pre = ui_full_temp; + ui_soc = ((soc_h * 256 + soc_l) * 100)/ (ui_full_pre * 256); + remainder = (((soc_h * 256 + soc_l) * 100 * 100) / (ui_full_pre * 256)) % 100; +#endif + + if (ui_soc >= 100){ + cw_printk("CW2015[%d]: UI_SOC = %d larger 100!!!!\n", __LINE__, ui_soc); + ui_soc = 100; + } + + if (cw_bat->usb_psy) { + power_supply_get_property(cw_bat->usb_psy, POWER_SUPPLY_PROP_ONLINE, &usb_online); + } + + if (usb_online.intval && (cw_bat->ui_soc == 100) && (ui_soc < cw_bat->ui_soc) && + (cw_bat->batt_status != POWER_SUPPLY_STATUS_DISCHARGING)) { + cw_printk("CW2015[%d]: usb online = %d, ui_soc_reg = %d, remainder:%d", __LINE__, usb_online.intval, ui_soc, remainder); + ui_soc = 100; + } + + if ((cw_bat->ui_soc == 0) || + (chr_st_now && ui_soc > cw_bat->ui_soc) || + (!chr_st_now && ui_soc < cw_bat->ui_soc)) { + cw_bat->ui_soc = ui_soc; + } + + if (cw_bat->ui_soc != ui_soc) { + cw_printk("CW2015[%d]: not update, cw_bat->ui_soc %d, ui_soc %d, chrg_st_now %d", + __LINE__, cw_bat->ui_soc, ui_soc, chr_st_now); + } + + cw_bat->ic_soc_h = soc_h; + cw_bat->ic_soc_l = soc_l; + + return 0; +} + +bool is_factory_mode(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool factory_mode = false; + const char *bootargs = NULL; + char *bootmode = NULL; + char *end = NULL; + + if (!np) + return factory_mode; + + if (!of_property_read_string(np, "bootargs", &bootargs)) { + bootmode = strstr(bootargs, "androidboot.mode="); + if (bootmode) { + end = strpbrk(bootmode, " "); + bootmode = strpbrk(bootmode, "="); + } + if (bootmode && + end > bootmode && + strnstr(bootmode, "mot-factory", end - bootmode)) { + factory_mode = true; + } + } + of_node_put(np); + + return factory_mode; +} + +/* + * The TEMP register is an UNSIGNED 8bit read only register. + * It reports the real-time battery temperature + * measured at TS pin. The scope is from -40 to 87.5 degrees Celsius, + * LSB is 0.5 degree Celsius. TEMP(C) = - 40 + Value(0x06 Reg) / 2 + */ +static int cw_get_temp(struct cw_battery *cw_bat) +{ + int ret; + unsigned char reg_val; + int temp; + + ret = cw_read(cw_bat->client, REG_TEMP, ®_val); + if (ret < 0) + return ret; + + temp = (int)reg_val * 10 / 2 - 400; + + if(cw_bat->factory_mode && !cw_bat->ntc_exist && (-400 == temp)) + temp = 250; + + cw_bat->temp = temp; + + return 0; +} + +/* get complement code function, unsigned short must be U16 */ +static long get_complement_code(unsigned short raw_code) +{ + long complement_code; + int dir; + + if (0 != (raw_code & COMPLEMENT_CODE_U16)){ + dir = -1; + raw_code = (~raw_code) + 1; + } else { + dir = 1; + } + complement_code = (long)raw_code * dir; + + return complement_code; +} + +/* + * CURRENT is a SIGNED 16bit register(0x0E 0x0F) that reports current A/D converter result of the voltage across the + * current sense resistor, 10mohm typical. The result is stored as a two's complement value to show positive + * and negative current. Voltages outside the minimum and maximum register values are reported as the + * minimum or maximum value. + * The register value should be divided by the sense resistance to convert to amperes. The value of the + * sense resistor determines the resolution and the full-scale range of the current readings. The LSB of 0x0F + * is (52.4/32768)uV. + * The default value is 0x0000, stands for 0mA. 0x7FFF stands for the maximum charging current and 0x8001 stands for + * the maximum discharging current. + */ +static int cw_get_current(struct cw_battery *cw_bat) +{ + int ret; + unsigned char reg_val[2] = {0 , 0}; + long long cw_current; /* use long long type to guarantee 8 bytes space*/ + unsigned short current_reg; /* unsigned short must u16 */ + + ret = cw_read_word(cw_bat->client, REG_CURRENT_H, reg_val); + if (ret < 0) + return ret; + + current_reg = (reg_val[0] << 8) + reg_val[1]; + cw_current = get_complement_code(current_reg); +// cw_current = cw_current * 160 * 1000 / USER_RSENSE / 100; + cw_current = cw_current * 160 * 1000 / cw_bat->sense_r_mohm / 100; + cw_bat->cw_current = cw_current; + + return 0; +} + +/* + * CYCLECNT is an UNSIGNED 16bit register(0xA4 0xA5) that counts cycle life of the battery. The LSB of 0xA5 stands + * for 1/16 cycle. This register will be clear after enters shutdown mode + */ +static int cw_get_cycle_count(struct cw_battery *cw_bat) +{ + int ret; + unsigned char reg_val[2] = {0, 0}; + int cycle; + + ret = cw_read_word(cw_bat->client, REG_CYCLE_H, reg_val); + if (ret < 0) + return ret; + + cycle = (reg_val[0] << 8) + reg_val[1]; + cw_bat->cycle = cycle / 16; + + return 0; +} + +/* + * SOH (State of Health) is an UNSIGNED 8bit register(0xA6) that represents the level of battery aging by tracking + * battery internal impedance increment. When the device enters active mode, this register refresh to 0x64 + * by default. Its range is 0x00 to 0x64, indicating 0 to 100%. This register will be clear after enters shutdown + * mode. + */ +static int cw_get_soh(struct cw_battery *cw_bat) +{ + int ret; + unsigned char reg_val; + int soh; + + ret = cw_read(cw_bat->client, REG_SOH, ®_val); + if (ret < 0) + return ret; + + soh = reg_val; + cw_bat->soh = soh; + + return 0; +} + +#if 0 +static int cw_get_stb_current(struct cw_battery *cw_bat) +{ + int ret; + unsigned char reg_val[2] = {0 , 0}; + long long stb_current; + unsigned short stb_current_reg; /*unsigned short must u16*/ + + ret = cw_read_word(cw_bat->client, REG_STB_CUR_H, reg_val); + if (ret < 0) + return ret; + + stb_current_reg = (reg_val[0] << 8) + reg_val[1]; + stb_current = get_complement_code(STB_current_reg); + stb_current = stb_current * 160 * 1000 / 16 / USER_RSENSE / 100; + cw_bat->stb_current = stb_current; + + return 0; +} +#endif + +/* + * FW_VERSION register reports the firmware (FW) running in the chip. It is fixed to 0x00 when the chip is + * in shutdown mode. When in active mode, Bit [7:6] are fixed to '01', which stand for the CW2217B and Bit + * [5:0] stand for the FW version running in the chip. Note that the FW version is subject to update and contact + * sales office for confirmation when necessary. +*/ +static int cw_get_fw_version(struct cw_battery *cw_bat) +{ + int ret; + unsigned char reg_val; + int fw_version; + + ret = cw_read(cw_bat->client, REG_FW_VERSION, ®_val); + if (ret < 0) + return ret; + + fw_version = reg_val; + cw_bat->fw_version = fw_version; + + return 0; +} + +static int cw_update_data(struct cw_battery *cw_bat) +{ + int ret = 0; + + ret += cw_get_voltage(cw_bat); + ret += cw_get_capacity(cw_bat); + ret += cw_get_temp(cw_bat); + ret += cw_get_current(cw_bat); + ret += cw_get_cycle_count(cw_bat); + ret += cw_get_soh(cw_bat); + cw_printk("vol = %d current = %ld cap = %d temp = %d raw_soc = %d age=%d\n", + cw_bat->voltage, cw_bat->cw_current, cw_bat->ui_soc, cw_bat->temp, cw_bat->raw_soc, cw_bat->soh); + + return ret; +} + +static int cw_init_data(struct cw_battery *cw_bat) +{ + int ret = 0; + + ret += cw_get_chip_id(cw_bat); + ret += cw_get_voltage(cw_bat); + ret += cw_get_capacity(cw_bat); + ret += cw_get_temp(cw_bat); + ret += cw_get_current(cw_bat); + ret += cw_get_cycle_count(cw_bat); + ret += cw_get_soh(cw_bat); + ret += cw_get_fw_version(cw_bat); + cw_printk("chip_id = %d vol = %d cur = %ld cap = %d raw_soc = %d temp = %d fw_version = %d\n", + cw_bat->chip_id, cw_bat->voltage, cw_bat->cw_current, cw_bat->ui_soc, cw_bat->raw_soc, cw_bat->temp, cw_bat->fw_version); + + return ret; +} + +/*CW2217 update profile function, Often called during initialization*/ +static int cw_config_start_ic(struct cw_battery *cw_bat) +{ + int ret; + unsigned char reg_val; + int count = 0; + + ret = cw2217_sleep(cw_bat); + if (ret < 0) + return ret; + + /* update new battery info */ + ret = cw_write_profile(cw_bat->client, config_profile_info); + if (ret < 0) + return ret; + + /* set UPDATE_FLAG AND SOC INTTERRUP VALUE*/ + reg_val = CONFIG_UPDATE_FLG | GPIO_SOC_IRQ_VALUE; + ret = cw_write(cw_bat->client, REG_SOC_ALERT, ®_val); + if (ret < 0) + return ret; + + /*close all interruptes*/ + reg_val = 0; + ret = cw_write(cw_bat->client, REG_GPIO_CONFIG, ®_val); + if (ret < 0) + return ret; + + ret = cw2217_active(cw_bat); + if (ret < 0) + return ret; + + while (CW_TRUE) { + msleep(CW_SLEEP_100MS); + cw_read(cw_bat->client, REG_IC_STATE, ®_val); + if (IC_READY_MARK == (reg_val & IC_READY_MARK)) + break; + count++; + if (count >= CW_SLEEP_COUNTS) { + cw2217_sleep(cw_bat); + return -1; + } + } + + return 0; +} + +/* + * Get the cw2217 running state + * Determine whether the profile needs to be updated +*/ +static int cw2217_get_state(struct cw_battery *cw_bat) +{ + int ret; + unsigned char reg_val; + int i; + int reg_profile; + + ret = cw_read(cw_bat->client, REG_MODE_CONFIG, ®_val); + if (ret < 0) + return ret; + + cw_printk("REG_MODE_CONFIG 0x%2x = 0x%2x\n", REG_MODE_CONFIG, reg_val); + if (reg_val != CONFIG_MODE_ACTIVE) + return CW2217_NOT_ACTIVE; + + ret = cw_read(cw_bat->client, REG_SOC_ALERT, ®_val); + if (ret < 0) + return ret; + if (0x00 == (reg_val & CONFIG_UPDATE_FLG)) + return CW2217_PROFILE_NOT_READY; + + for (i = 0; i < SIZE_OF_PROFILE; i++) { + ret = cw_read(cw_bat->client, (REG_BAT_PROFILE + i), ®_val); + if (ret < 0) + return ret; + reg_profile = REG_BAT_PROFILE + i; + if (config_profile_info[i] != reg_val) { + cw_printk("config profile mismatch, config_profile_info[%d]: 0x%2x, reg_val[0x%2x]: 0x%2x\n", + i, config_profile_info[i], reg_profile, reg_val); + break; + } + } + if ( i != SIZE_OF_PROFILE) + return CW2217_PROFILE_NEED_UPDATE; + + return 0; +} + +/*CW2217 init function, Often called during initialization*/ +static int cw_init(struct cw_battery *cw_bat) +{ + int ret; + + cw_printk("\n"); + ret = cw2217_get_state(cw_bat); + if (ret < 0) { + printk("iic read write error"); + return ret; + } + + if (ret != 0) { + ret = cw_config_start_ic(cw_bat); + if (ret < 0) + return ret; + } + cw_printk("cw2217 init success!\n"); + + return 0; +} + +static void cw_bat_work(struct work_struct *work) +{ + struct delayed_work *delay_work; + struct cw_battery *cw_bat; + int ret; + int ui_soc; + uint32_t elapsed_ms; + struct timespec64 now; + static struct timespec64 start = {0}; + + delay_work = container_of(work, struct delayed_work, work); + cw_bat = container_of(delay_work, struct cw_battery, battery_delay_work); + + ktime_get_real_ts64(&now); + if (now.tv_sec >= start.tv_sec) { + elapsed_ms = (now.tv_sec - start.tv_sec) * 1000; + elapsed_ms += (now.tv_nsec - start.tv_nsec) / 1000000; + } else { + elapsed_ms = 0; + start = now; + } + + ui_soc = cw_bat->ui_soc; + if (elapsed_ms >= queue_delayed_work_time) { + ret = cw_update_data(cw_bat); + if (ret < 0) + printk(KERN_ERR "iic read error when update data"); + } + + /* get battery power supply */ + if (!cw_bat->batt_psy) { + cw_bat->batt_psy = power_supply_get_by_name("battery"); + if (!cw_bat->batt_psy) + cw_printk("%s: get batt_psy fail\n", __func__); + } + + if (!cw_bat->usb_psy) { + cw_bat->usb_psy = power_supply_get_by_name("usb"); + if (!cw_bat->usb_psy) + cw_printk("%s: get batt_psy fail\n", __func__); + } + + if (cw_bat->batt_psy && ui_soc != cw_bat->ui_soc) { + power_supply_changed(cw_bat->batt_psy); + } +} + +static void cw_hw_init_work(struct work_struct *work) +{ + struct cw_battery *cw_bat; + int ret; + int loop = 0; + + cw_bat = container_of(work, struct cw_battery, hw_init_work); + + ret = cw_init_data(cw_bat); + if (ret) { + printk("%s : cw2217 init data fail!\n", __func__); + } + + ret = cw_init(cw_bat); + while ((loop++ < CW_RETRY_COUNT) && (ret != 0)) { + msleep(CW_SLEEP_200MS); + ret = cw_init(cw_bat); + } + if (ret) { + printk("%s : cw2217 init fail!\n", __func__); + return ; + } + + ret = cw_init_data(cw_bat); + if (ret) { + printk("%s : cw2217 init data fail!\n", __func__); + return ; + } + + queue_delayed_work(cw_bat->cwfg_workqueue, &cw_bat->battery_delay_work , msecs_to_jiffies(queue_start_work_time)); + +} + +static const char *cw_get_battery_serialnumber(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + const char *battsn_buf; + int retval; + + battsn_buf = NULL; + + if (np) + retval = of_property_read_string(np, "mmi,battid", + &battsn_buf); + else + return NULL; + + if ((retval == -EINVAL) || !battsn_buf) { + cw_info(" Battsn unused\n"); + of_node_put(np); + return NULL; + + } else + cw_info("Battsn = %s\n", battsn_buf); + + of_node_put(np); + + return battsn_buf; +} + +static struct device_node *cw_get_profile_by_serialnumber( + const struct device_node *np) +{ + struct device_node *node, *df_node, *sn_node; + const char *sn_buf, *df_sn, *dev_sn; + int rc; + + if (!np) + return NULL; + + dev_sn = NULL; + df_sn = NULL; + sn_buf = NULL; + df_node = NULL; + sn_node = NULL; + + dev_sn = cw_get_battery_serialnumber(); + + rc = of_property_read_string(np, "df-serialnum", + &df_sn); + if (rc) + cw_info("No Default Serial Number defined\n"); + else if (df_sn) + cw_info("Default Serial Number %s\n", df_sn); + + for_each_child_of_node(np, node) { + rc = of_property_read_string(node, "serialnum", + &sn_buf); + if (!rc && sn_buf) { + if (dev_sn) + if (strnstr(dev_sn, sn_buf, 32)) + sn_node = node; + if (df_sn) + if (strnstr(df_sn, sn_buf, 32)) + df_node = node; + } + } + + if (sn_node) { + node = sn_node; + df_node = NULL; + cw_info("Battery Match Found using %s\n", sn_node->name); + } else if (df_node) { + node = df_node; + sn_node = NULL; + cw_info("Battery Match Found using default %s\n", + df_node->name); + } else { + cw_info("No Battery Match Found!\n"); + return NULL; + } + + return node; +} + +static int cw_parse_dts(struct cw_battery *cw_bat) +{ + struct device_node *np = cw_bat->client->dev.of_node; + struct device_node *batt_profile_node = NULL; + int rc; + + rc = of_property_read_u32(np, "sense_r_mohm", &cw_bat->sense_r_mohm); + if(rc < 0) + cw_bat->sense_r_mohm = USER_RSENSE; + else + cw_bat->sense_r_mohm *= 1000; + + batt_profile_node = cw_get_profile_by_serialnumber(np); + if (!batt_profile_node) + return -1; + + rc = of_property_read_u8_array(batt_profile_node, "config_profile_info", config_profile_info, SIZE_OF_PROFILE); + if (rc < 0) + cw_info("error,get profile_info fail from dts,exit \n"); + + rc = of_property_read_u32(batt_profile_node, "fcc_design", &cw_bat->fcc_design); + if (rc < 0) + cw_info("error,get fcc_design,exit \n"); + + rc = of_property_read_u32(batt_profile_node, "ui_full", &cw_bat->ui_full); + if (rc < 0) { + cw_bat->ui_full = CW_UI_FULL; + cw_info("dts get ui_full fail. use default ui_full=%d \n", CW_UI_FULL); + rc = 0; + } + + rc = of_property_read_u32(np, "factory_mode_ntc_exist", &cw_bat->ntc_exist); + if (rc < 0) { + cw_bat->ntc_exist = true; + cw_info("dts get ntc_exist fail. use default ntc_exist=%d \n", cw_bat->ntc_exist); + rc = 0; + } + + rc = of_property_read_string(np, "fg-psy-name", &cw_bat->bms_name); + if (rc) { + cw_bat->bms_name = "bms"; + rc = 0; + } + + if (of_property_read_bool(np, "ibat-invert-polority")) + cw_bat->ibat_polority = 1; + else + cw_bat->ibat_polority = -1; + + cw_info("ibat_polority=%d \n", cw_bat->ibat_polority); + return rc; +} + +#ifdef CW_PROPERTIES +static int cw_battery_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + int ret = 0; + struct cw_battery *cw_bat; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + cw_bat = container_of(psy, struct cw_battery, cw_bat); +#else + cw_bat = power_supply_get_drvdata(psy); +#endif + + switch(psp) { + default: + ret = -EINVAL; + break; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + cw_info("%s, cw ntc_exist:%d\n", __func__, cw_bat->ntc_exist); +#endif + + return ret; +} + +static unsigned int cw_get_charge_counter(struct cw_battery *cw_bat) +{ + int charge_counter; + int full_capacity; + + full_capacity = (cw_bat->fcc_design * cw_bat->soh * 1000) / 100; + charge_counter = div_s64(full_capacity * cw_bat->ui_soc, 100); + + return charge_counter; +} + +static int cw_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + struct cw_battery *cw_bat; + cw_bat = container_of(psy, struct cw_battery, cw_bat); +#else + struct cw_battery *cw_bat = power_supply_get_drvdata(psy); +#endif + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + cw_get_current(cw_bat); + cw_get_batt_status(cw_bat); + val->intval = cw_bat->batt_status; + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + cw_get_cycle_count(cw_bat); + val->intval = cw_bat->cycle; + break; + case POWER_SUPPLY_PROP_CAPACITY: + cw_get_capacity(cw_bat); + val->intval = cw_bat->ui_soc; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval= POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_PRESENT: + if (cw_bat->voltage <= 0) + val->intval = 0; + else if (cw_bat->temp <= CW_BPD_TEMP) + val->intval = 0; + else + val->intval = 1; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + cw_get_voltage(cw_bat); + val->intval = cw_bat->voltage * CW_VOL_UNIT; + break; + case POWER_SUPPLY_PROP_VOLTAGE_OCV: + /* voltage_ocv invalid, use voltage_now instead*/ + val->intval = cw_bat->voltage * CW_VOL_UNIT; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + cw_get_current(cw_bat); + val->intval = cw_bat->cw_current * CW_CUR_UNIT; + val->intval *= cw_bat->ibat_polority; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + val->intval = cw_bat->fcc_design * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + val->intval = (cw_bat->fcc_design * cw_bat->soh * 1000)/100; + break; + case POWER_SUPPLY_PROP_TEMP: + cw_get_temp(cw_bat); + val->intval = cw_bat->temp; + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + cw_get_soh(cw_bat); + val->intval = cw_get_charge_counter(cw_bat); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static enum power_supply_property cw_battery_properties[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_OCV, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CHARGE_COUNTER, +}; +#endif + +static int cw2217_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int ret; + struct cw_battery *cw_bat; +#ifdef CW_PROPERTIES +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) + struct power_supply_desc *psy_desc; + struct power_supply_config psy_cfg = {0}; +#endif +#endif + + cw_printk("\n"); + + cw_bat = devm_kzalloc(&client->dev, sizeof(*cw_bat), GFP_KERNEL); + if (!cw_bat) { + printk("%s : cw_bat create fail!\n", __func__); + return -ENOMEM; + } + + i2c_set_clientdata(client, cw_bat); + cw_bat->client = client; + + ret = cw_parse_dts(cw_bat); + if (ret) { + printk("%s : cw2217 prase dts fail!\n", __func__); + return ret; + } + + cw_bat->vdd_i2c_vreg = devm_regulator_get_optional( + &cw_bat->client->dev, + "vdd-i2c"); + if (IS_ERR_OR_NULL(cw_bat->vdd_i2c_vreg)) { + printk("%s: Could not get vdd-i2c power regulator\n", __func__); + cw_bat->vdd_i2c_vreg = NULL; + } else { + ret = regulator_enable(cw_bat->vdd_i2c_vreg); + printk("%s: Enable vdd-i2c, ret=%d\n", __func__, ret); + ret = 0; + } + + ret = cw_get_chip_id(cw_bat); + if (ret < 0) { + printk("iic read write error"); + goto error; + } + if (cw_bat->chip_id != IC_VCHIP_ID){ + printk("not cw2217B\n"); + goto error; + } + +#ifdef CW_PROPERTIES +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) + cw_bat->cw_bat.name = CW_PROPERTIES; + cw_bat->cw_bat.type = POWER_SUPPLY_TYPE_MAINS; + cw_bat->cw_bat.properties = cw_battery_properties; + cw_bat->cw_bat.num_properties = ARRAY_SIZE(cw_battery_properties); + cw_bat->cw_bat.get_property = cw_battery_get_property; + cw_bat->cw_bat.set_property = cw_battery_set_property; + ret = power_supply_register(&client->dev, &cw_bat->cw_bat); + if (ret < 0) { + power_supply_unregister(&cw_bat->cw_bat); + goto error; + } +#else + psy_desc = devm_kzalloc(&client->dev, sizeof(*psy_desc), GFP_KERNEL); + if (!psy_desc) { + ret = -ENOMEM; + goto error; + } + psy_cfg.drv_data = cw_bat; + psy_desc->name = CW_PROPERTIES; + psy_desc->type = POWER_SUPPLY_TYPE_MAINS; + psy_desc->properties = cw_battery_properties; + psy_desc->num_properties = ARRAY_SIZE(cw_battery_properties); + psy_desc->get_property = cw_battery_get_property; + psy_desc->set_property = cw_battery_set_property; + cw_bat->cw_bat = power_supply_register(&client->dev, psy_desc, &psy_cfg); + if (IS_ERR(cw_bat->cw_bat)) { + ret = PTR_ERR(cw_bat->cw_bat); + printk(KERN_ERR"failed to register battery: %d\n", ret); + goto error; + } +#endif +#endif + + cw_bat->cwfg_workqueue = create_singlethread_workqueue("cwfg_gauge"); + INIT_DELAYED_WORK(&cw_bat->battery_delay_work, cw_bat_work); + + INIT_WORK(&cw_bat->hw_init_work, cw_hw_init_work); + schedule_work(&cw_bat->hw_init_work); + + if(is_factory_mode()) + cw_bat->factory_mode = true; + + cw_printk("cw2217 driver probe success!\n"); + + return 0; + +error: + if (cw_bat->vdd_i2c_vreg) { + if (regulator_is_enabled(cw_bat->vdd_i2c_vreg)) + regulator_disable(cw_bat->vdd_i2c_vreg); + devm_regulator_put(cw_bat->vdd_i2c_vreg); + } + return ret; +} + +#if (KERNEL_VERSION(6, 1, 25) > LINUX_VERSION_CODE) +static int cw2217_remove(struct i2c_client *client) +#else +static void cw2217_remove(struct i2c_client *client) +#endif +{ + struct cw_battery *cw_bat = i2c_get_clientdata(client); + + cw_printk("\n"); + if (cw_bat->vdd_i2c_vreg) { + if (regulator_is_enabled(cw_bat->vdd_i2c_vreg)) + regulator_disable(cw_bat->vdd_i2c_vreg); + devm_regulator_put(cw_bat->vdd_i2c_vreg); + } +#if (KERNEL_VERSION(6, 1, 25) > LINUX_VERSION_CODE) + return 0; +#endif +} + +#ifdef CONFIG_PM +static int cw_bat_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cw_battery *cw_bat = i2c_get_clientdata(client); + + cancel_delayed_work(&cw_bat->battery_delay_work); + return 0; +} + +static int cw_bat_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct cw_battery *cw_bat = i2c_get_clientdata(client); + + queue_delayed_work(cw_bat->cwfg_workqueue, &cw_bat->battery_delay_work, msecs_to_jiffies(20)); + return 0; +} + +static const struct dev_pm_ops cw_bat_pm_ops = { + .suspend = cw_bat_suspend, + .resume = cw_bat_resume, +}; +#endif + +static const struct i2c_device_id cw2217_id_table[] = { + { CWFG_NAME, 0 }, + { } +}; + +static struct of_device_id cw2217_match_table[] = { + { .compatible = "cellwise,cw2217", }, + { }, +}; + +static struct i2c_driver cw2217_driver = { + .driver = { + .name = CWFG_NAME, +#ifdef CONFIG_PM + .pm = &cw_bat_pm_ops, +#endif + .owner = THIS_MODULE, + .of_match_table = cw2217_match_table, + }, + .probe = cw2217_probe, + .remove = cw2217_remove, + .id_table = cw2217_id_table, +}; + +/* + //Add to dsti file + cw2217@64 { + compatible = "cellwise,cw2217"; + reg = <0x64>; + }; +*/ + +static int __init cw2217_init(void) +{ + cw_printk("\n"); + i2c_add_driver(&cw2217_driver); + return 0; +} + +static void __exit cw2217_exit(void) +{ + i2c_del_driver(&cw2217_driver); +} + +module_init(cw2217_init); +module_exit(cw2217_exit); + +MODULE_AUTHOR("Cellwise FAE"); +MODULE_DESCRIPTION("CW2217 FGADC Device Driver V1.2"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/fet-control/Android.mk b/drivers/power/fet-control/Android.mk new file mode 100755 index 000000000000..842253aaa641 --- /dev/null +++ b/drivers/power/fet-control/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := fet_control.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/fet-control/Kbuild b/drivers/power/fet-control/Kbuild new file mode 100755 index 000000000000..d95b919ec30c --- /dev/null +++ b/drivers/power/fet-control/Kbuild @@ -0,0 +1,6 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/fet_control +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += fet_control.o diff --git a/drivers/power/fet-control/Makefile b/drivers/power/fet-control/Makefile new file mode 100755 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/power/fet-control/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/power/fet-control/fet_control.c b/drivers/power/fet-control/fet_control.c new file mode 100755 index 000000000000..d1016dfd3956 --- /dev/null +++ b/drivers/power/fet-control/fet_control.c @@ -0,0 +1,632 @@ +/* + * Copyright (c) 2021 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ +#define pr_fmt(fmt) "fet_control-[%s]: " fmt, __func__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RBALANCE_VDIFF_MV 200 +#define GPIO_SET_DELAY 50 +#define CHRG_FULLCURR_EN 1 +#define CHRG_FULLCURR_DIS 0 +#define HBDLY_DISCHARGE_SEC 30 +#define HBDLY_CHARGE_SEC 6 + +static struct fet_control_data { + struct device *dev; + struct power_supply *main_batt_psy; + struct power_supply *usb_psy; + struct notifier_block ps_notif; + struct delayed_work update; + bool init_done; + bool ps_is_present; + int flip_chg_curr_max; + int main_chg_curr_max; + int battplus_en_gpio; + int balance_en_n_gpio; + int chrg_fullcurr_en_gpio; + int vbus_ocp_fault_n_gpio; + struct iio_channel *vbatt2_sns_chan; +} fetControlData; + +int battplus_state = -1; +int balance_state = -1; +int fullcurr_state = -1; +int ocp_fault_n_state = -1; + +static int get_ps_int_prop(struct power_supply *psy, enum power_supply_property prop) +{ + int rc; + union power_supply_propval val; + + if (!psy) + return -1; + + rc = power_supply_get_property(psy, + prop, &val); + if (rc < 0) { + pr_err("Error getting prop %d rc = %d\n", prop, rc); + return -1; + } + + return val.intval; +} + +static int update_state_gpio(int gpio, int val) +{ + int readval; + + gpio_set_value(gpio, val); + udelay(GPIO_SET_DELAY); + + readval = gpio_get_value(gpio); + + if (readval != val) { + pr_err("FAIL set fet-ctrl gpio[%d], wrote:%d, read:%d\n", + gpio, + val, + readval); + return -EINVAL; + } + + return 0; +} + +static int set_battplus_state(const char *val, const struct kernel_param *kp) +{ + int rc; + long mode; + + if (!fetControlData.init_done) + return -ENODEV; + + rc = kstrtol(val, 0, &mode); + if (rc) + return rc; + + rc = update_state_gpio(fetControlData.battplus_en_gpio, !!mode); + if (rc) { + pr_err("Update fet-ctl gpio [%d] failed\n", fetControlData.battplus_en_gpio); + return rc; + } else { + battplus_state = !!mode; + pr_debug("Set using flip_battplus gpio[%d], VAL:[%d]\n", + fetControlData.battplus_en_gpio, + gpio_get_value(fetControlData.battplus_en_gpio)); + } + + return 0; +} + +static int set_balance_state(const char *val, const struct kernel_param *kp) +{ + int rc; + long mode; + + if (!fetControlData.init_done) + return -ENODEV; + + rc = kstrtol(val, 0, &mode); + if (rc) + return rc; + + rc = update_state_gpio(fetControlData.balance_en_n_gpio, !!mode); + if (rc) { + pr_err("Update fet-ctl gpio [%d] failed\n", fetControlData.balance_en_n_gpio); + return rc; + } else { + balance_state = !!mode; + pr_debug("Set using flip_balance_enn gpio[%d], VAL:[%d]\n", + fetControlData.balance_en_n_gpio, + gpio_get_value(fetControlData.balance_en_n_gpio)); + } + + return 0; +} + +static int set_fullcurr_state(const char *val, const struct kernel_param *kp) +{ + int rc; + long mode; + + if (!fetControlData.init_done) + return -ENODEV; + + rc = kstrtol(val, 0, &mode); + if (rc) + return rc; + + + rc = update_state_gpio(fetControlData.chrg_fullcurr_en_gpio, !!mode); + if (rc) { + pr_err("Update fet-ctl gpio [%d] failed\n", fetControlData.chrg_fullcurr_en_gpio); + return rc; + } else { + fullcurr_state = !!mode; + pr_debug("Set using Chrg fullcurr_en gpio[%d], VAL:[%d]\n", + fetControlData.chrg_fullcurr_en_gpio, + gpio_get_value(fetControlData.chrg_fullcurr_en_gpio)); + } + + return 0; +} + +static int set_ocp_fault_n_state(const char *val, const struct kernel_param *kp) +{ + int rc; + long mode; + + if (!fetControlData.init_done) + return -ENODEV; + + rc = kstrtol(val, 0, &mode); + if (rc) + return rc; + + rc = update_state_gpio(fetControlData.vbus_ocp_fault_n_gpio, !!mode); + if (rc) { + pr_err("Update fet-ctl gpio [%d] failed\n", fetControlData.vbus_ocp_fault_n_gpio); + return rc; + } else { + ocp_fault_n_state = !!mode; + pr_debug("Set vbus_ocp_fault_n gpio[%d], VAL:[%d]\n", + fetControlData.vbus_ocp_fault_n_gpio, + gpio_get_value(fetControlData.vbus_ocp_fault_n_gpio)); + } + + return 0; +} + +static struct kernel_param_ops battplus_ops = +{ + .set = &set_battplus_state, + .get = param_get_int, +}; + +static struct kernel_param_ops balance_ops = +{ + .set = &set_balance_state, + .get = param_get_int, +}; + +static struct kernel_param_ops fullcurr_ops = +{ + .set = &set_fullcurr_state, + .get = param_get_int, +}; + +static struct kernel_param_ops ocpfault_ops = +{ + .set = &set_ocp_fault_n_state, + .get = param_get_int, +}; + +module_param_cb(battplus_state, + &battplus_ops, + &battplus_state, + S_IRUGO | S_IWUSR +); + +module_param_cb(balance_state, + &balance_ops, + &balance_state, + S_IRUGO | S_IWUSR +); + +module_param_cb(fullcurr_state, + &fullcurr_ops, + &fullcurr_state, + S_IRUGO | S_IWUSR +); + +module_param_cb(ocp_fault_n_state, + &ocpfault_ops, + &ocp_fault_n_state, + S_IRUGO | S_IWUSR +); + +static int mmi_is_factory(void) +{ + const char *bootargs_str = NULL; + char *kvpair = NULL; + char *value = NULL; + int ret = -1; + struct device_node *n = of_find_node_by_path("/chosen"); + size_t bootargs_str_len = 0; + + if (n == NULL) + goto ret; + + if (of_property_read_string(n, "mmi,bootconfig", &bootargs_str) != 0) + goto putnode; + + bootargs_str_len = strlen(bootargs_str); + kvpair = strnstr(bootargs_str, "androidboot.mode=", strlen(bootargs_str)); + if (kvpair) { // found key + value = strchr(kvpair, '=') + 1; + if (!strncmp(value, "mot-factory", 11)) + ret = 1; + else + ret = 0; + } + +putnode: + of_node_put(n); +ret: + return ret; +} + +static void update_work(struct work_struct *work) +{ + struct fet_control_data *data = container_of(work, struct fet_control_data, update.work); + int main_curr, main_mv, main_ocv, flip_mv = 0, usbtype; + int main_chg = 1, rc = 0; + int hb_sched_time = HBDLY_DISCHARGE_SEC; + int vbatt2_sns_uv = -EINVAL; + const char * usb_types[] = {"UNKNOWN","SDP","DCP","CDP","ACA","Type-C","PD","PD_DRP","PD_PPS","BRICKID"}; + + fullcurr_state = CHRG_FULLCURR_DIS; + + usbtype = get_ps_int_prop(data->usb_psy, + POWER_SUPPLY_PROP_USB_TYPE); + pr_info("USB-TYPE found:%d [%s]\n", usbtype,usb_types[usbtype] ); + + /* For no Charger connected & battfet already closed, nothing more to be done */ + if (mmi_is_factory()) { + pr_info("Factory Mode- Cancel Work.. \n"); + cancel_delayed_work(&data->update); + return; + } else if (usbtype == POWER_SUPPLY_USB_TYPE_UNKNOWN && battplus_state) { + pr_info("BATTFET Closed & Not Charging- Cancel Work..\n"); + cancel_delayed_work(&data->update); + return; + } + + /* VMain in mV */ + main_mv = get_ps_int_prop(data->main_batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW); + main_mv /= 1000; + + main_ocv = get_ps_int_prop(data->main_batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_OCV); + main_ocv /= 1000; + + /* Main current in uA */ + main_curr = get_ps_int_prop(data->main_batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW); + if (main_curr > 0 || usbtype != POWER_SUPPLY_USB_TYPE_UNKNOWN) + pr_info("Charging Main\n"); + else + main_chg = -1; // Discharging Main + + data->main_chg_curr_max = get_ps_int_prop(data->main_batt_psy, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN); + if (data->main_chg_curr_max < 0) { + pr_err("Failed to get Main Max chrg curr\n"); + return; + } + + ocp_fault_n_state = gpio_get_value(fetControlData.vbus_ocp_fault_n_gpio); + pr_info("ocp_fault_n GPIO:[%d], VAL:[%d]\n", + fetControlData.vbus_ocp_fault_n_gpio, ocp_fault_n_state); + + /* Leave FULLCURR_DIS until charger is connected */ + if (usbtype != POWER_SUPPLY_USB_TYPE_UNKNOWN) { + if ( (main_chg * main_curr > data->main_chg_curr_max / 2) || + !ocp_fault_n_state ) { + fullcurr_state = CHRG_FULLCURR_DIS; + pr_err("WARNING! Main-OC or OCP-TRIP: Main_curr:%d, ocp_fault_n:%d\n", + main_curr, ocp_fault_n_state); + } else { + fullcurr_state = CHRG_FULLCURR_EN; + } + } + + /* Read the ADC pm8350b_vbatt2_sns div3, scale *3 and to mV */ + rc = iio_read_channel_processed(fetControlData.vbatt2_sns_chan, &vbatt2_sns_uv); + if (rc < 0) { + pr_err("Error Reading pm8350b_vbatt2_sns_voltage- rc:%d\n", rc); + return; + } else { + pr_info("Read pm8350b_vbatt2_sns_voltage- VAL-uV:%d\n", vbatt2_sns_uv); + flip_mv = (3 * vbatt2_sns_uv) / 1000; + } + + pr_info("Main_curr:%d, main_Ichg_MAX:%d\n", + main_curr, data->main_chg_curr_max); + pr_err("Flip-Vbatt: %d, Main-Vbatt: %d, Main-OCV: %d\n", flip_mv, main_mv, main_ocv); + + /* FET BattFET paths */ + /* Leave Balance dflt-en & toggle in/out parallel low-Z battplus fet */ + balance_state = 0; + if (!battplus_state) { + if ( abs(main_ocv - flip_mv) < RBALANCE_VDIFF_MV) { + battplus_state = 1; + pr_info("battplus-EN: %d\n", battplus_state); + } else { + battplus_state = 0; + pr_info("battplus-DIS: %d\n", battplus_state); + } + } + if (main_chg == 1) + hb_sched_time = HBDLY_CHARGE_SEC; + + /* Update GPIO controls */ + update_state_gpio(fetControlData.battplus_en_gpio, battplus_state); + update_state_gpio(fetControlData.balance_en_n_gpio, balance_state); + update_state_gpio(fetControlData.chrg_fullcurr_en_gpio, fullcurr_state); + + schedule_delayed_work(&data->update, hb_sched_time * HZ); +} + +static int ps_notify_callback(struct notifier_block *nb, + unsigned long event, void *p) +{ + struct fet_control_data *data = container_of(nb, struct fet_control_data, ps_notif); + struct power_supply *psy = p; + union power_supply_propval pval = {0}; + int retval; + bool present_ps; + + if ((event == PSY_EVENT_PROP_CHANGED) && + psy && psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, "usb", sizeof("usb")) && data) { + pr_info("psy notif: event = %lu\n", event); + + retval = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, + &pval); + if (retval) { + pr_err("%s psy get property failed, ERR: %d\n", psy->desc->name, retval); + return retval; + } + present_ps = (pval.intval) ? true : false; + pr_info("%s is %s\n", psy->desc->name, + (present_ps) ? "present" : "not present"); + + if (event == PSY_EVENT_PROP_CHANGED) { + if (data->ps_is_present == present_ps) { + pr_info("ps present state unchanged\n"); + return 0; + } + } + data->ps_is_present = present_ps; + + cancel_delayed_work(&data->update); + schedule_delayed_work(&data->update, 3 * HZ); + } + + return 0; +} + +static int parse_dt(struct device_node *node) +{ + int rc = 0; + const char *main_batt_psy_name; + + rc |= of_property_read_string(node, "mmi,main-batt-psy", &main_batt_psy_name); + if (!rc && main_batt_psy_name) { + fetControlData.main_batt_psy = power_supply_get_by_name(main_batt_psy_name); + if (!fetControlData.main_batt_psy) { + pr_debug("Could not get main batt psy, maybe we are early - defer."); + return -EPROBE_DEFER; + } + } + + fetControlData.battplus_en_gpio = of_get_named_gpio(node, + "mmi,flip_battplus_en_gpio", 0); + if (!gpio_is_valid(fetControlData.battplus_en_gpio)) { + pr_err("battplus_en_gpio is not valid!\n"); + return -ENODEV; + } + + fetControlData.balance_en_n_gpio = of_get_named_gpio(node, + "mmi,flip_balance_en_n_gpio", 0); + if (!gpio_is_valid(fetControlData.balance_en_n_gpio)) { + pr_err("balance_en_n_gpio is not valid!\n"); + return -ENODEV; + } + + fetControlData.chrg_fullcurr_en_gpio = of_get_named_gpio(node, + "mmi,chrg-fullcurr-en-gpio", 0); + if (!gpio_is_valid(fetControlData.chrg_fullcurr_en_gpio)) { + pr_err("chrg-fullcurr-en-gpio is not valid!\n"); + return -ENODEV; + } + + fetControlData.vbus_ocp_fault_n_gpio = of_get_named_gpio(node, + "mmi,vbus-ocp-fault-n-gpio", 0); + if (!gpio_is_valid(fetControlData.vbus_ocp_fault_n_gpio)) { + pr_err("ocp-fault-gpio is not valid!\n"); + return -ENODEV; + } + + return rc; +} + +static int fet_control_probe(struct platform_device *pdev) +{ + int rc = 1; + + fetControlData.init_done = 0; + fetControlData.dev = &pdev->dev; + + rc = parse_dt(fetControlData.dev->of_node); + if (rc) { + if (rc != -EPROBE_DEFER) + pr_err("Failed to parse device tree\n"); + goto fail; + } + + fetControlData.usb_psy = power_supply_get_by_name("usb"); + if (!fetControlData.usb_psy) { + pr_debug("Could not get usb psy, maybe we are early - defer."); + return -EPROBE_DEFER; + } + + /* Get the ADC device instance pm8350b_vbatt2_sns, gpio3_pu0_div3 */ + fetControlData.vbatt2_sns_chan = iio_channel_get(fetControlData.dev,"pm8350b_vbatt2_sns"); + if (!fetControlData.vbatt2_sns_chan) { + pr_err("Error Getting pm8350b_vbatt2_sns_voltage channel- rc:%d\n",rc); + return -EINVAL; + } else { + pr_info("Found pm8350b_vbatt2_sns voltage channel\n"); + } + + rc = gpio_request(fetControlData.battplus_en_gpio, + "mmi,flip_battplus_en_gpio"); + if (rc) { + pr_err("Failed request battplus_en_gpio\n"); + goto fail; + } + + rc = gpio_request(fetControlData.balance_en_n_gpio, + "mmi,flip_balance_en_n_gpio"); + if (rc) { + pr_err("Failed request balance_en_n_gpio\n"); + goto fail; + } + + rc = gpio_request(fetControlData.chrg_fullcurr_en_gpio, + "mmi,chrg-fullcurr-en-gpio"); + if (rc) { + pr_err("Failed request chrg-fullcurr-en-gpio\n"); + goto fail; + } + + rc = gpio_request(fetControlData.vbus_ocp_fault_n_gpio, + "mmi,vbus-ocp-fault-n-gpio"); + if (rc) { + pr_err("Failed request vbus-ocp-fault-n-gpio\n"); + goto fail; + } + + /* Set default Flip Battery Path FETs: battplus path disable*/ + rc = gpio_direction_output(fetControlData.battplus_en_gpio, 0);//Path Disabled + if (rc) { + pr_err("Unable to set DIR flip_battplus_en [%d]\n", + fetControlData.battplus_en_gpio); + goto fail; + } + battplus_state = 0; + + /* Set Flip Rbalance path enable */ + rc = gpio_direction_output(fetControlData.balance_en_n_gpio, 0);// Path Enabled + if (rc) { + pr_err("Unable to set DIR/VAL Flip_balance_en_n [%d]\n", + fetControlData.balance_en_n_gpio); + goto fail; + } + balance_state = 0; + + /* Set charger fullcurr enable */ + rc = gpio_direction_output(fetControlData.chrg_fullcurr_en_gpio, 0);// ChrgFullCurr dflt DISABLE + if (rc) { + pr_err("Unable to set DIR/VAL chrg_fullcurr_en_gpio [%d]\n", + fetControlData.chrg_fullcurr_en_gpio); + goto fail; + } + fullcurr_state = CHRG_FULLCURR_DIS; + + /* Set OCP_FAULT input */ + rc = gpio_direction_input(fetControlData.vbus_ocp_fault_n_gpio); + if (rc) { + pr_err("Unable to set DIR vbus_ocp_fault_n [%d]\n", + fetControlData.vbus_ocp_fault_n_gpio); + goto fail; + } + + pr_info("Flip_battplus_en Init GPIO:[%d], VAL:[%d]\n", + fetControlData.battplus_en_gpio, + gpio_get_value(fetControlData.battplus_en_gpio) ); + pr_info("Flip_balance_en_n Init GPIO:[%d], VAL:[%d]\n", + fetControlData.balance_en_n_gpio, + gpio_get_value(fetControlData.balance_en_n_gpio) ); + pr_info("chrg_fullcurr Init GPIO:[%d], VAL:[%d]\n", + fetControlData.chrg_fullcurr_en_gpio, + gpio_get_value(fetControlData.chrg_fullcurr_en_gpio) ); + pr_info("ocp_fault_n Init GPIO:[%d], VAL:[%d]\n", + fetControlData.vbus_ocp_fault_n_gpio, + gpio_get_value(fetControlData.vbus_ocp_fault_n_gpio) ); + + // Notify on plug/unplug + fetControlData.ps_notif.notifier_call = ps_notify_callback; + if (power_supply_reg_notifier(&fetControlData.ps_notif)) { + pr_err("Failed to register notifier\n"); + goto fail; + } + + // Work to update the fet & charge current states. + INIT_DELAYED_WORK(&fetControlData.update, update_work); + schedule_delayed_work(&fetControlData.update, HBDLY_DISCHARGE_SEC * HZ); + + fetControlData.init_done = true; + return 0; +fail: + return rc; +} + +static int fet_control_remove(struct platform_device *pdev) +{ + + if (fetControlData.init_done) { + cancel_delayed_work(&fetControlData.update); + power_supply_unreg_notifier(&fetControlData.ps_notif); + gpio_free(fetControlData.battplus_en_gpio); + gpio_free(fetControlData.balance_en_n_gpio); + gpio_free(fetControlData.chrg_fullcurr_en_gpio); + gpio_free(fetControlData.vbus_ocp_fault_n_gpio); + } + + return 0; +} + +static const struct of_device_id match_table[] = { + { .compatible = "mmi,fet-control", }, + { }, +}; + +static struct platform_driver fet_control_driver = { + .driver = { + .name = "mmi,fet-control", + .owner = THIS_MODULE, + .of_match_table = match_table, + }, + .probe = fet_control_probe, + .remove = fet_control_remove, +}; +module_platform_driver(fet_control_driver); + +MODULE_DESCRIPTION("Motorola Flip FET Control"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/max170xx/Android.mk b/drivers/power/max170xx/Android.mk new file mode 100644 index 000000000000..d09eeb6ca66b --- /dev/null +++ b/drivers/power/max170xx/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := max170xx_battery_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/max170xx/Kbuild b/drivers/power/max170xx/Kbuild new file mode 100644 index 000000000000..fa5b3dc819f7 --- /dev/null +++ b/drivers/power/max170xx/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +obj-m += max170xx_battery_mmi.o diff --git a/drivers/power/max170xx/Makefile b/drivers/power/max170xx/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/power/max170xx/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/power/max170xx/max170xx_battery_mmi.c b/drivers/power/max170xx/max170xx_battery_mmi.c new file mode 100644 index 000000000000..e9c3d9288d21 --- /dev/null +++ b/drivers/power/max170xx/max170xx_battery_mmi.c @@ -0,0 +1,2694 @@ +/* + * Fuel gauge driver for Maxim 17042 / 8966 / 8997 + * Note that Maxim 8966 and 8997 are mfd and this is its subdevice. + * + * Copyright (C) 2011 Samsung Electronics + * MyungJoo Ham + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * This driver is based on max17042_battery.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define FUEL_GAUGE_REPORT "Fuel-Gauge_Report" +#define MAX_FULL_CAP "MAX1704X - Full Cap = %d\n" +#define FUEL_GAUGE_REPORT_SIZE 64 + +/* Status register bits */ +#define STATUS_POR_BIT (1 << 1) +#define STATUS_BST_BIT (1 << 3) +#define STATUS_VMN_BIT (1 << 8) +#define STATUS_TMN_BIT (1 << 9) +#define STATUS_SMN_BIT (1 << 10) +#define STATUS_BI_BIT (1 << 11) +#define STATUS_VMX_BIT (1 << 12) +#define STATUS_TMX_BIT (1 << 13) +#define STATUS_SMX_BIT (1 << 14) +#define STATUS_BR_BIT (1 << 15) + +/* Interrupt mask bits */ +#define CONFIG_ALRT_BIT_ENBL (1 << 2) +#define STATUS_INTR_VMIN_BIT (1 << 8) +#define STATUS_INTR_TEMPMIN_BIT (1 << 9) +#define STATUS_INTR_SOCMIN_BIT (1 << 10) +#define STATUS_INTR_VMAX_BIT (1 << 12) +#define STATUS_INTR_TEMP_BIT (1 << 13) +#define STATUS_INTR_SOCMAX_BIT (1 << 14) + +#define VFSOC0_LOCK 0x0000 +#define VFSOC0_UNLOCK 0x0080 +#define MODEL_UNLOCK1 0X0059 +#define MODEL_UNLOCK2 0X00C4 +#define MODEL_LOCK1 0X0000 +#define MODEL_LOCK2 0X0000 + +#define dQ_ACC_DIV 16 +#define dP_ACC_200 0xC80 + +#define MAX17050_CFG_REV_REG MAX17042_ManName +#define MAX17050_CFG_REV_MASK 0x0007 + +#define MAX17050_FORCE_POR 0x000F +#define MAX17050_POR_WAIT_MS 20 + +#define INIT_DATA_PROPERTY "maxim,regs-init-data" +#define CONFIG_NODE "maxim,configuration" +#define CONFIG_PROPERTY "config" +#define FULL_SOC_THRESH_PROPERTY "full_soc_thresh" +#define DESIGN_CAP_PROPERTY "design_cap" +#define ICHGT_TERM_PROPERTY "ichgt_term" +#define LEARN_CFG_PROPERTY "learn_cfg" +#define FILTER_CFG_PROPERTY "filter_cfg" +#define RELAX_CFG_PROPERTY "relax_cfg" +#define MISC_CFG_PROPERTY "misc_cfg" +#define FULLCAP_PROPERTY "fullcap" +#define FULLCAPNOM_PROPERTY "fullcapnom" +#define LAVG_EMPTY_PROPERTY "lavg_empty" +#define QRTBL00_PROPERTY "qrtbl00" +#define QRTBL10_PROPERTY "qrtbl10" +#define QRTBL20_PROPERTY "qrtbl20" +#define QRTBL30_PROPERTY "qrtbl30" +#define RCOMP0_PROPERTY "rcomp0" +#define TCOMPC0_PROPERTY "tcompc0" +#define CELL_CHAR_TBL_PROPERTY "maxim,cell-char-tbl" +#define TGAIN_PROPERTY "tgain" +#define TOFF_PROPERTY "toff" +#define CGAIN_PROPERTY "cgain" +#define COFF_PROPERTY "coff" +#define TEMP_CONV_NODE "maxim,temp-conv" +#define RESULT_PROPERTY "result" +#define START_PROPERTY "start" +#define REVISION "rev" + +#define RETRY_COUNT 5 +int retry_sleep_us[RETRY_COUNT] = { + 100, 200, 300, 400, 500 +}; + +struct max17042_wakeup_source { + struct wakeup_source source; + unsigned long disabled; +}; + +#define MAX17042_VMAX_TOLERANCE 50 /* 50 mV */ + +DEFINE_MUTEX(instance_lock); + +struct max17042_chip { + struct i2c_client *client; + struct regmap *regmap; + struct power_supply *battery; + enum max170xx_chip_type chip_type; + struct max17042_platform_data *pdata; + struct work_struct work; + struct work_struct check_temp_work; + struct mutex check_temp_lock; + int init_complete; + bool batt_undervoltage; + struct dentry *debugfs_root; + u8 debugfs_addr; + + struct power_supply *batt_psy; + int temp_state; + int hotspot_temp; + int batt_temp_dc; + + struct delayed_work iterm_work; + struct delayed_work thread_work; + struct max17042_wakeup_source max17042_wake_source; + int charge_full_des; + int taper_reached; + bool factory_mode; + bool is_factory_image; + char fg_report_str[FUEL_GAUGE_REPORT_SIZE]; + bool fullcap_report_sent; + int last_fullcap; + int fctr_uah_bit; + int batt_max_uv; +}; + +#define CHRG_CONV_WR(fctr, val) ((val * 1000) / fctr) +#define CURR_CONV(r_sns, val) ((val * 1562500) / r_sns) +#define CURR_CONV_WR(r_sns, val) ((val * r_sns) / 1562) + +static irqreturn_t max17042_thread_handler(int id, void *dev); + +static void max17042_stay_awake(struct max17042_wakeup_source *source) +{ + if (__test_and_clear_bit(0, &source->disabled)) { + __pm_stay_awake(&source->source); + pr_debug("enabled source %s\n", source->source.name); + } +} + +static void max17042_relax(struct max17042_wakeup_source *source) +{ + if (!__test_and_set_bit(0, &source->disabled)) { + __pm_relax(&source->source); + pr_debug("disabled source %s\n", source->source.name); + } +} + +static enum power_supply_property max17042_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_AVG, + POWER_SUPPLY_PROP_VOLTAGE_OCV, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TEMP_ALERT_MIN, + POWER_SUPPLY_PROP_TEMP_ALERT_MAX, + POWER_SUPPLY_PROP_TEMP_MIN, + POWER_SUPPLY_PROP_TEMP_MAX, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_HEALTH, +}; + +/* input and output temperature is in deci-centigrade */ +static int max17042_conv_temp(struct max17042_temp_conv *conv, int t) +{ + int i; /* conversion table index */ + s16 *r = conv->result; + int dt; + + /* + * conv->result[0] corresponds to conv->start temp, conv->result[1] to + * conv->start + 1 temp, etc. Find index to the conv->result table for + * t to be between index and index + 1 temperatures. + */ + i = t / 10 - conv->start; /* t is in 1/10th C, conv->start is in C */ + if (t < 0) + i -= 1; + + /* Interpolate linearly if index and index + 1 are within the table */ + if (i < 0) { + return r[0]; + } else if (i >= conv->num_result - 1) { + return r[conv->num_result - 1]; + } else { + dt = t - (conv->start + i) * 10; /* in 1/10th C */ + return r[i] + (r[i + 1] - r[i]) * dt / 10; + } +} + +/* input and output temperature is in centigrade */ +static int max17042_find_temp_threshold(struct max17042_temp_conv *conv, + int t, bool low_side) +{ + int i; /* conversion table index */ + s16 *r = conv->result; + int dt; + + dt = t * 10; + + if (low_side) { + for (i = (conv->num_result - 1); i >= 0; i--) { + if (r[i] <= dt) + break; + } + } else { + for (i = 0; i < conv->num_result; i++) { + if (r[i] >= dt) + break; + } + } + + return conv->start + i; +} + +int max17042_read_charge_counter(struct max17042_chip *chip, int ql) +{ + int uah = 0, uah_old = 0, uahl = 0; + int i; + int ret; + struct regmap *map = chip->regmap; + + ret = regmap_read(map, MAX17042_QH, &uah); + if (ret < 0) + return ret; + if (ql) { + /* check if QH data-change between QH read and QL read */ + for (i = 0; i < 3; i++) { + ret = regmap_read(map, MAX17042_QL, &uahl); + if (ret < 0) + return ret; + uah_old = uah; + ret = regmap_read(map, MAX17042_QH, &uah); + if (ret < 0) + return ret; + if (uah == uah_old) + break; + } + if (uah == uah_old) + uah = uah * chip->fctr_uah_bit + + ((uahl * chip->fctr_uah_bit) >> 16); + else + uah = -ETIMEDOUT; + } else + uah = uah * chip->fctr_uah_bit; + return uah; +} + +int max17042_write_temp(struct max17042_chip *chip) +{ + int ret; + int intval; + struct regmap *map = chip->regmap; + + intval = chip->batt_temp_dc * 256 / 10; + + /* The value is signed. */ + if (chip->batt_temp_dc < 0) { + intval--; + intval |= 0x8000; + } else + intval++; + + ret = regmap_write(map, MAX17042_TEMP, intval); + if (ret < 0) + return ret; + ret = regmap_write(map, MAX17042_AvgTA, intval); + if (ret < 0) + return ret; + + return 0; +} + +int max17042_read_temp(struct max17042_chip *chip, int *temp_dc) +{ + int ret; + int intval; + struct regmap *map = chip->regmap; + + ret = regmap_read(map, MAX17042_TEMP, &intval); + if (ret < 0) + return ret; + + /* The value is signed. */ + if (intval & 0x8000) { + intval = (0x7fff & ~intval) + 1; + intval *= -1; + } + + /* The value is converted into deci-centigrade scale */ + /* Units of LSB = 1 / 256 degree Celsius */ + intval = intval * 10 / 256; + + /* Convert IC temp to "real" temp */ + if (!chip->pdata->use_external_temp && chip->pdata->tcnv) + intval = max17042_conv_temp(chip->pdata->tcnv, + intval); + *temp_dc = intval; + + return 0; +} + +static int max17042_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct max17042_chip *chip = power_supply_get_drvdata(psy); + struct regmap *map = chip->regmap; + u32 data; + int ret; + int8_t temp; + + switch (prop) { + case POWER_SUPPLY_PROP_TEMP: + chip->batt_temp_dc = val->intval; + schedule_work(&chip->check_temp_work); + break; + case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: + ret = regmap_read(map, MAX17042_TALRT_Th, &data); + if (ret < 0) + return ret; + + /* Input in deci-centigrade, convert to centigrade */ + temp = val->intval / 10; + /* force min < max */ + if (temp >= (int8_t)(data >> 8)) + temp = (int8_t)(data >> 8) - 1; + /* Write both MAX and MIN ALERT */ + data = (data & 0xff00) + temp; + ret = regmap_write(map, MAX17042_TALRT_Th, data); + break; + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + ret = regmap_read(map, MAX17042_TALRT_Th, &data); + if (ret < 0) + return ret; + + /* Input in Deci-Centigrade, convert to centigrade */ + temp = val->intval / 10; + /* force max > min */ + if (temp <= (int8_t)(data & 0xff)) + temp = (int8_t)(data & 0xff) + 1; + /* Write both MAX and MIN ALERT */ + data = (data & 0xff) + (temp << 8); + ret = regmap_write(map, MAX17042_TALRT_Th, data); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + chip->batt_max_uv = val->intval; + ret = regmap_read(map, MAX17042_VALRT_Th, &data); + if (ret < 0) + return ret; + + data &= (~MAX17042_ALRT_MSK_MAX); + /* Units of LSB = 20mV */ + data |= (chip->batt_max_uv / 20000) << 8; + + ret = regmap_write(map, MAX17042_VALRT_Th, data); + if (ret < 0) + return ret; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int max17042_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + int rc; + + switch (prop) { + case POWER_SUPPLY_PROP_TEMP: + case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + rc = 1; + break; + default: + rc = 0; + break; + } + return rc; +} + +static int max17042_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct max17042_chip *chip = power_supply_get_drvdata(psy); + struct regmap *map = chip->regmap; + int ret, cfd_max; + u32 data; + long int int64val; + + if (!chip->init_complete) + return -EAGAIN; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + ret = regmap_read(map, MAX17042_STATUS, &data); + if (ret < 0) + return ret; + + if (data & MAX17042_STATUS_BattAbsent) + val->intval = 0; + else + val->intval = 1; + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + ret = regmap_read(map, MAX17042_FullCAP, &data); + if (ret < 0) + return ret; + if (!chip->charge_full_des) + return data; + val->intval = data * chip->fctr_uah_bit; + val->intval *= 100; + val->intval /= chip->charge_full_des; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + ret = regmap_read(map, MAX17042_VALRT_Th, &data); + if (ret < 0) + return ret; + + val->intval = data >> 8; + val->intval *= 20000; /* Units of LSB = 20mV */ + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) + ret = regmap_read(map, MAX17042_V_empty, &data); + else + ret = regmap_read(map, MAX17047_V_empty, &data); + if (ret < 0) + return ret; + + val->intval = data >> 7; + val->intval *= 10000; /* Units of LSB = 10mV */ + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = regmap_read(map, MAX17042_VCELL, &data); + if (ret < 0) + return ret; + + val->intval = data * 625 / 8; + break; + case POWER_SUPPLY_PROP_VOLTAGE_AVG: + ret = regmap_read(map, MAX17042_AvgVCELL, &data); + if (ret < 0) + return ret; + + val->intval = data * 625 / 8; + break; + case POWER_SUPPLY_PROP_VOLTAGE_OCV: + ret = regmap_read(map, MAX17042_OCVInternal, &data); + if (ret < 0) + return ret; + + val->intval = data * 625 / 8; + break; + case POWER_SUPPLY_PROP_CAPACITY: + ret = regmap_read(map, MAX17042_RepSOC, &data); + if (ret < 0) + return ret; + + if ((data & 0xFF) != 0) { + data >>= 8; + data++; /* Round up */ + } else + data >>= 8; + + if (data > 100) + data = 100; + + if (data == 0 && + chip->pdata->batt_undervoltage_zero_soc) { + if (chip->batt_undervoltage) + val->intval = 0; + else + val->intval = 1; + } else + val->intval = data; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + /* High Threshold for FCC reporting is 104% of design cap */ + cfd_max = + (chip->charge_full_des + + ((chip->charge_full_des * 4) / 100)); + ret = regmap_read(map, MAX17042_FullCAP, &data); + if (ret < 0) + return ret; + + val->intval = data * chip->fctr_uah_bit; + + if (chip->factory_mode) + break; + + /* If Full Cap deviates from the range report it once */ + if (!chip->fullcap_report_sent && + (val->intval > cfd_max || + val->intval < (chip->charge_full_des / 2))) { + snprintf(chip->fg_report_str, FUEL_GAUGE_REPORT_SIZE, + MAX_FULL_CAP, val->intval); + chip->fullcap_report_sent = true; + } else if (chip->fullcap_report_sent) { + snprintf(chip->fg_report_str, FUEL_GAUGE_REPORT_SIZE, + MAX_FULL_CAP, val->intval); + chip->fullcap_report_sent = false; + } + + if (val->intval < (chip->charge_full_des / 2)) { + dev_warn(&chip->client->dev, + "Error fullcap too small! Forcing POR!\n"); + dev_warn(&chip->client->dev, + "FullCap %d uAhr\n", + val->intval); + regmap_write(map, MAX17042_VFSOC0Enable, + MAX17050_FORCE_POR); + msleep(MAX17050_POR_WAIT_MS); + schedule_delayed_work(&chip->thread_work, + msecs_to_jiffies(0)); + } + + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + val->intval = chip->charge_full_des; + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + ret = max17042_read_charge_counter(chip, 1); + if (ret < 0) + return ret; + + val->intval = ret; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + regmap_read(map, MAX17042_RepCap, &data); + + val->intval = (data & 0xFFFF) * chip->fctr_uah_bit; + + break; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + regmap_read(map, MAX17042_STATUS, &data); + + if (data & STATUS_INTR_VMAX_BIT) + val->intval = 1; + else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_TEMP: + ret = max17042_read_temp(chip, &val->intval); + if (ret < 0) + return ret; + break; + case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: + ret = regmap_read(map, MAX17042_TALRT_Th, &data); + if (ret < 0) + return ret; + /* LSB is Alert Minimum. In deci-centigrade */ + val->intval = (data & 0xff) * 10; + break; + case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: + ret = regmap_read(map, MAX17042_TALRT_Th, &data); + if (ret < 0) + return ret; + /* MSB is Alert Maximum. In deci-centigrade */ + val->intval = (data >> 8) * 10; + break; + case POWER_SUPPLY_PROP_TEMP_MIN: + val->intval = chip->pdata->temp_min; + break; + case POWER_SUPPLY_PROP_TEMP_MAX: + val->intval = chip->pdata->temp_max; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + if (chip->pdata->enable_current_sense) { + ret = regmap_read(map, MAX17042_Current, &data); + if (ret < 0) + return ret; + + int64val = data; + if (int64val & 0x8000) { + /* Negative */ + int64val = ~int64val & 0x7fff; + int64val++; + int64val *= -1; + } + + int64val = CURR_CONV(chip->pdata->r_sns, int64val); + val->intval = (int) int64val; + } else { + return -EINVAL; + } + break; + case POWER_SUPPLY_PROP_CURRENT_AVG: + if (chip->pdata->enable_current_sense) { + ret = regmap_read(map, MAX17042_AvgCurrent, &data); + if (ret < 0) + return ret; + + int64val = data; + if (int64val & 0x8000) { + /* Negative */ + int64val = ~int64val & 0x7fff; + int64val++; + int64val *= -1; + } + + int64val = CURR_CONV(chip->pdata->r_sns, int64val); + val->intval = (int) int64val; + } else { + return -EINVAL; + } + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = chip->temp_state; + break; + default: + return -EINVAL; + } + return 0; +} + +#define USLEEP_RANGE 50 +static int max17042_write_verify_reg(struct regmap *map, u8 reg, u32 value) +{ + int ret; + u32 read_value; + int i = 0; + + for (i = 0; i < RETRY_COUNT; i++) { + ret = regmap_write(map, reg, value); + if (ret < 0) { + /* sleep for few micro sec before retrying */ + usleep_range(retry_sleep_us[i], + retry_sleep_us[i] + USLEEP_RANGE); + } else { + regmap_read(map, reg, &read_value); + if (read_value != value) { + ret = -EIO; + } + break; + } + } + + if (ret < 0) { + pr_err("%s: err %d\n", __func__, ret); + return ret; + } + + return read_value; +} + +static inline void max17042_override_por(struct regmap *map, + u8 reg, u16 value) +{ + if (value) + regmap_write(map, reg, value); +} + +static inline void max10742_unlock_model(struct max17042_chip *chip) +{ + struct regmap *map = chip->regmap; + + regmap_write(map, MAX17042_MLOCKReg1, MODEL_UNLOCK1); + regmap_write(map, MAX17042_MLOCKReg2, MODEL_UNLOCK2); +} + +static inline void max10742_lock_model(struct max17042_chip *chip) +{ + struct regmap *map = chip->regmap; + + regmap_write(map, MAX17042_MLOCKReg1, MODEL_LOCK1); + regmap_write(map, MAX17042_MLOCKReg2, MODEL_LOCK2); +} + +static inline void max17042_write_model_data(struct max17042_chip *chip, + u8 addr, int size) +{ + struct regmap *map = chip->regmap; + int i; + + for (i = 0; i < size; i++) + regmap_write(map, addr + i, + chip->pdata->config_data->cell_char_tbl[i]); +} + +static inline void max17042_read_model_data(struct max17042_chip *chip, + u8 addr, u16 *data, int size) +{ + struct regmap *map = chip->regmap; + int i; + u32 data_raw; + + for (i = 0; i < size; i++) { + regmap_read(map, addr + i, &data_raw); + data[i] = (u16)data_raw; + data_raw = 0x0000; + } +} + +static inline int max17042_model_data_compare(struct max17042_chip *chip, + u16 *data1, u16 *data2, int size) +{ + int i; + + if (memcmp(data1, data2, size)) { + dev_err(&chip->client->dev, "%s compare failed\n", __func__); + for (i = 0; i < size; i++) + dev_info(&chip->client->dev, "0x%x, 0x%x", + data1[i], data2[i]); + dev_info(&chip->client->dev, "\n"); + return -EINVAL; + } + return 0; +} + +static int max17042_init_model(struct max17042_chip *chip) +{ + int ret; + int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl); + u16 *temp_data; + + temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL); + if (!temp_data) + return -ENOMEM; + + max10742_unlock_model(chip); + max17042_write_model_data(chip, MAX17042_MODELChrTbl, + table_size); + max17042_read_model_data(chip, MAX17042_MODELChrTbl, temp_data, + table_size); + + ret = max17042_model_data_compare( + chip, + chip->pdata->config_data->cell_char_tbl, + temp_data, + table_size); + + max10742_lock_model(chip); + kfree(temp_data); + + return ret; +} + +static int max17042_verify_model_lock(struct max17042_chip *chip) +{ + int i; + int table_size = ARRAY_SIZE(chip->pdata->config_data->cell_char_tbl); + u16 *temp_data; + int ret = 0; + + temp_data = kcalloc(table_size, sizeof(*temp_data), GFP_KERNEL); + if (!temp_data) + return -ENOMEM; + + max17042_read_model_data(chip, MAX17042_MODELChrTbl, temp_data, + table_size); + for (i = 0; i < table_size; i++) + if (temp_data[i]) + ret = -EINVAL; + + kfree(temp_data); + return ret; +} + +static void max17042_write_config_regs(struct max17042_chip *chip) +{ + struct max17042_config_data *config = chip->pdata->config_data; + struct regmap *map = chip->regmap; + + regmap_write(map, MAX17042_CONFIG, config->config); + regmap_write(map, MAX17042_LearnCFG, config->learn_cfg); + regmap_write(map, MAX17042_FilterCFG, + config->filter_cfg); + regmap_write(map, MAX17042_RelaxCFG, config->relax_cfg); + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047 || + chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050) + regmap_write(map, MAX17047_FullSOCThr, + config->full_soc_thresh); +} + +static void max17042_write_custom_regs(struct max17042_chip *chip) +{ + struct max17042_config_data *config = chip->pdata->config_data; + struct regmap *map = chip->regmap; + + max17042_write_verify_reg(map, MAX17042_RCOMP0, config->rcomp0); + max17042_write_verify_reg(map, MAX17042_TempCo, config->tcompc0); + max17042_write_verify_reg(map, MAX17042_ICHGTerm, + CURR_CONV_WR(chip->pdata->r_sns, + config->ichgt_term)); + max17042_write_verify_reg(map, MAX17042_LAvg_empty, + CURR_CONV_WR(chip->pdata->r_sns, + config->lavg_empty)); + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) { + regmap_write(map, MAX17042_EmptyTempCo, config->empty_tempco); + max17042_write_verify_reg(map, MAX17042_K_empty0, + config->kempty0); + } else { + max17042_write_verify_reg(map, MAX17047_QRTbl00, + config->qrtbl00); + max17042_write_verify_reg(map, MAX17047_QRTbl10, + config->qrtbl10); + max17042_write_verify_reg(map, MAX17047_QRTbl20, + config->qrtbl20); + max17042_write_verify_reg(map, MAX17047_QRTbl30, + config->qrtbl30); + } +} + +static void max17042_update_capacity_regs(struct max17042_chip *chip) +{ + struct max17042_config_data *config = chip->pdata->config_data; + struct regmap *map = chip->regmap; + + max17042_write_verify_reg(map, MAX17042_FullCAP, + CHRG_CONV_WR(chip->fctr_uah_bit, + config->fullcap)); + regmap_write(map, MAX17042_DesignCap, + CHRG_CONV_WR(chip->fctr_uah_bit, + config->design_cap)); + max17042_write_verify_reg(map, MAX17042_FullCAPNom, + CHRG_CONV_WR(chip->fctr_uah_bit, + config->fullcapnom)); +} + +static void max17042_reset_vfsoc0_qh0_regs(struct max17042_chip *chip) +{ + unsigned int data; + struct regmap *map = chip->regmap; + + regmap_read(map, MAX17042_VFSOC, &data); + regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_UNLOCK); + max17042_write_verify_reg(map, MAX17042_VFSOC0, data); + regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_LOCK); + + regmap_read(map, MAX17042_QH, &data); + regmap_write(map, MAX17042_QH0, data); +} + +static void max17042_load_new_capacity_params(struct max17042_chip *chip) +{ + u32 rem_cap, vfSoc, rep_cap, dq_acc; + + struct max17042_config_data *config = chip->pdata->config_data; + struct regmap *map = chip->regmap; + + regmap_read(map, MAX17042_VFSOC, &vfSoc); + + /* vfSoc needs to shifted by 8 bits to get the + * perc in 1% accuracy, to get the right rem_cap multiply + * fullcapnom by vfSoc and devide by 100 + */ + + rem_cap = ((vfSoc >> 8) * config->fullcapnom) / 100; + rem_cap = CHRG_CONV_WR(chip->fctr_uah_bit, rem_cap); + max17042_write_verify_reg(map, MAX17042_RemCap, rem_cap); + + rep_cap = rem_cap; + max17042_write_verify_reg(map, MAX17042_RepCap, rep_cap); + + /* Write dQ_acc to 200% of Capacity and dP_acc to 200% */ + dq_acc = CHRG_CONV_WR(chip->fctr_uah_bit, + config->fullcapnom) / dQ_ACC_DIV; + max17042_write_verify_reg(map, MAX17042_dQacc, dq_acc); + max17042_write_verify_reg(map, MAX17042_dPacc, dP_ACC_200); + + max17042_write_verify_reg(map, MAX17042_FullCAP, + CHRG_CONV_WR(chip->fctr_uah_bit, + config->fullcap)); + regmap_write(map, MAX17042_DesignCap, + CHRG_CONV_WR(chip->fctr_uah_bit, + config->design_cap)); + max17042_write_verify_reg(map, MAX17042_FullCAPNom, + CHRG_CONV_WR(chip->fctr_uah_bit, + config->fullcapnom)); + /* Update SOC register with new SOC */ + regmap_write(map, MAX17042_RepSOC, vfSoc); +} + +/* + * Block write all the override values coming from platform data. + * This function MUST be called before the POR initialization proceedure + * specified by maxim. + */ +#define POR_VALRT_THRESHOLD 0xDF00 +static inline void max17042_override_por_values(struct max17042_chip *chip) +{ + struct regmap *map = chip->regmap; + struct max17042_config_data *config = chip->pdata->config_data; + + max17042_override_por(map, MAX17042_TGAIN, config->tgain); + max17042_override_por(map, MAx17042_TOFF, config->toff); + max17042_override_por(map, MAX17042_CGAIN, config->cgain); + max17042_override_por(map, MAX17042_COFF, config->coff); + + if (!chip->factory_mode) + max17042_override_por(map, MAX17042_VALRT_Th, + POR_VALRT_THRESHOLD); + max17042_override_por(map, MAX17042_TALRT_Th, config->talrt_thresh); + max17042_override_por(map, MAX17042_SALRT_Th, + config->soc_alrt_thresh); + max17042_override_por(map, MAX17042_CONFIG, config->config); + max17042_override_por(map, MAX17042_SHDNTIMER, config->shdntimer); + + max17042_override_por(map, MAX17042_DesignCap, + CHRG_CONV_WR(chip->fctr_uah_bit, config->design_cap)); + max17042_override_por(map, MAX17042_ICHGTerm, + CURR_CONV_WR(chip->pdata->r_sns, config->ichgt_term)); + + max17042_override_por(map, MAX17042_AtRate, + CURR_CONV_WR(chip->pdata->r_sns, config->at_rate)); + max17042_override_por(map, MAX17042_LearnCFG, config->learn_cfg); + max17042_override_por(map, MAX17042_FilterCFG, config->filter_cfg); + max17042_override_por(map, MAX17042_RelaxCFG, config->relax_cfg); + max17042_override_por(map, MAX17042_MiscCFG, config->misc_cfg); + max17042_override_por(map, MAX17042_MaskSOC, config->masksoc); + + max17042_override_por(map, MAX17042_FullCAP, + CHRG_CONV_WR(chip->fctr_uah_bit, config->fullcap)); + max17042_override_por(map, MAX17042_FullCAPNom, + CHRG_CONV_WR(chip->fctr_uah_bit, config->fullcapnom)); + + if (chip->last_fullcap != -EINVAL) { + max17042_override_por(map, MAX17042_FullCAP, + CHRG_CONV_WR(chip->fctr_uah_bit, + chip->last_fullcap / 1000)); + max17042_override_por(map, MAX17042_FullCAPNom, + CHRG_CONV_WR(chip->fctr_uah_bit, + chip->last_fullcap / 1000)); + } else { + max17042_override_por(map, MAX17042_FullCAP, + CHRG_CONV_WR(chip->fctr_uah_bit, + config->fullcap)); + max17042_override_por(map, MAX17042_FullCAPNom, + CHRG_CONV_WR(chip->fctr_uah_bit, + config->fullcapnom)); + } + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) + max17042_override_por(map, MAX17042_SOC_empty, + config->socempty); + max17042_override_por(map, MAX17042_LAvg_empty, + CURR_CONV_WR(chip->pdata->r_sns, config->lavg_empty)); + max17042_override_por(map, MAX17042_dQacc, config->dqacc); + max17042_override_por(map, MAX17042_dPacc, config->dpacc); + + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) + max17042_override_por(map, MAX17042_V_empty, config->vempty); + else + max17042_override_por(map, MAX17047_V_empty, config->vempty); + max17042_override_por(map, MAX17042_TempNom, config->temp_nom); + max17042_override_por(map, MAX17042_TempLim, config->temp_lim); + max17042_override_por(map, MAX17042_FCTC, config->fctc); + max17042_override_por(map, MAX17042_RCOMP0, config->rcomp0); + max17042_override_por(map, MAX17042_TempCo, config->tcompc0); + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) { + max17042_override_por(map, MAX17042_EmptyTempCo, + config->empty_tempco); + max17042_override_por(map, MAX17042_K_empty0, + config->kempty0); + } +} + +static int max17042_init_chip(struct max17042_chip *chip) +{ + struct regmap *map = chip->regmap; + int ret; + u32 val; + struct max17042_config_data *config = chip->pdata->config_data; + + max17042_override_por_values(chip); + /* After Power up, the MAX17042 requires 500mS in order + * to perform signal debouncing and initial SOC reporting + */ + msleep(500); + + /* Initialize configaration */ + max17042_write_config_regs(chip); + + /* write cell characterization data */ + ret = max17042_init_model(chip); + if (ret) { + dev_err(&chip->client->dev, "%s init failed\n", + __func__); + return -EIO; + } + + ret = max17042_verify_model_lock(chip); + if (ret) { + dev_err(&chip->client->dev, "%s lock verify failed\n", + __func__); + return -EIO; + } + /* write custom parameters */ + max17042_write_custom_regs(chip); + + /* update capacity params */ + max17042_update_capacity_regs(chip); + + /* delay must be atleast 350mS to allow VFSOC + * to be calculated from the new configuration + */ + msleep(350); + + /* reset vfsoc0 and qh0 regs */ + max17042_reset_vfsoc0_qh0_regs(chip); + + /* load new capacity params */ + max17042_load_new_capacity_params(chip); + + /* Set Config Revision bits */ + regmap_read(map, MAX17050_CFG_REV_REG, &val); + val &= (~MAX17050_CFG_REV_MASK); + val |= config->revision; + regmap_write(map, MAX17050_CFG_REV_REG, val); + + /* Init complete, Clear the POR bit */ + regmap_update_bits(map, MAX17042_STATUS, STATUS_POR_BIT, 0x0); + return 0; +} + +static void max17042_force_reset(struct max17042_chip *chip) +{ + if (chip->pdata->main_psy) + dev_warn(&chip->client->dev, "Forcing reset of main fg\n"); + else + dev_warn(&chip->client->dev, "Forcing reset of flip fg\n"); + + regmap_write(chip->regmap, MAX17042_VFSOC0Enable, + MAX17050_FORCE_POR); + schedule_delayed_work(&chip->thread_work, + msecs_to_jiffies(MAX17050_POR_WAIT_MS)); +} + +#define MAX_DIFFERENTIAL 15 +static void max17042_check_fuel_gauge_diff(struct max17042_chip *this_instance) +{ + static struct max17042_chip *other_instance = NULL; + static int other_instance_capacity = 0; + union power_supply_propval pval = {0}; + + mutex_lock(&instance_lock); + + if (!this_instance->battery) + goto out; + + if (power_supply_get_property(this_instance->battery, + POWER_SUPPLY_PROP_CAPACITY, &pval)) + goto out; + + if (this_instance->pdata->main_psy) + dev_info(&this_instance->client->dev, + "Main fg capacity is %d\n", pval.intval); + else + dev_info(&this_instance->client->dev, + "Flip fg capacity is %d\n", pval.intval); + + if (other_instance) { + if (abs(pval.intval - other_instance_capacity) + >= MAX_DIFFERENTIAL) { + dev_warn(&this_instance->client->dev, + "Main and flip fg capacity diff exceeds %d!\n", + MAX_DIFFERENTIAL); + max17042_force_reset(this_instance); + max17042_force_reset(other_instance); + } + } else { + other_instance = this_instance; + other_instance_capacity = pval.intval; + } + +out: + mutex_unlock(&instance_lock); +} + +static void max17042_set_temp_threshold(struct max17042_chip *chip, + int max_c, + int min_c) +{ + u16 temp_tr; + s8 max; + s8 min; + struct regmap *map = chip->regmap; + + if (chip->pdata->use_external_temp) + return; + + max = (s8)max17042_find_temp_threshold(chip->pdata->tcnv, max_c, + false); + min = (s8)max17042_find_temp_threshold(chip->pdata->tcnv, min_c, + true); + + temp_tr = 0; + temp_tr |= min; + temp_tr &= 0x00FF; + + temp_tr |= (max << 8) & 0xFF00; + + pr_err("Program Temp Thresholds = 0x%X\n", temp_tr); + + regmap_write(map, MAX17042_TALRT_Th, temp_tr); +} + +#define HYSTERISIS_DEGC 2 +static int max17042_check_temp(struct max17042_chip *chip) +{ + int batt_temp; + int hotspot; + int max_t = 0; + int min_t = 0; + struct max17042_platform_data *pdata; + int temp_state = POWER_SUPPLY_HEALTH_GOOD; + union power_supply_propval ps = {0, }; + const char *batt_psy_name; + int ret = 0; + + if (!chip) + return 0; + + if (chip->pdata->use_external_temp) { + max17042_write_temp(chip); + return 0; + } + + mutex_lock(&chip->check_temp_lock); + pdata = chip->pdata; + + max17042_read_temp(chip, &batt_temp); + + /* Convert to Degrees C */ + batt_temp /= 10; + hotspot = chip->hotspot_temp / 1000; + + /* Override batt_temp if battery hot spot condition + is active */ + if ((batt_temp > pdata->cool_temp_c) && + (hotspot > batt_temp) && + (hotspot >= pdata->hotspot_thrs_c)) { + batt_temp = hotspot; + } + + if (chip->temp_state == POWER_SUPPLY_HEALTH_WARM) { + if (batt_temp >= pdata->hot_temp_c) + /* Warm to Hot */ + temp_state = POWER_SUPPLY_HEALTH_OVERHEAT; + else if (batt_temp <= + pdata->warm_temp_c - HYSTERISIS_DEGC) + /* Warm to Normal */ + temp_state = POWER_SUPPLY_HEALTH_GOOD; + else + /* Stay Warm */ + temp_state = POWER_SUPPLY_HEALTH_WARM; + } else if ((chip->temp_state == POWER_SUPPLY_HEALTH_GOOD) || + (chip->temp_state == POWER_SUPPLY_HEALTH_UNKNOWN)) { + if (batt_temp >= pdata->warm_temp_c) + /* Normal to Warm */ + temp_state = POWER_SUPPLY_HEALTH_WARM; + else if (batt_temp <= pdata->cool_temp_c) + /* Normal to Cool */ + temp_state = POWER_SUPPLY_HEALTH_COOL; + else + /* Stay Normal */ + temp_state = POWER_SUPPLY_HEALTH_GOOD; + } else if (chip->temp_state == POWER_SUPPLY_HEALTH_COOL) { + if (batt_temp >= + pdata->cool_temp_c + HYSTERISIS_DEGC) + /* Cool to Normal */ + temp_state = POWER_SUPPLY_HEALTH_GOOD; + else if (batt_temp <= pdata->cold_temp_c) + /* Cool to Cold */ + temp_state = POWER_SUPPLY_HEALTH_COLD; + else + /* Stay Cool */ + temp_state = POWER_SUPPLY_HEALTH_COOL; + } else if (chip->temp_state == POWER_SUPPLY_HEALTH_COLD) { + if (batt_temp >= + pdata->cold_temp_c + HYSTERISIS_DEGC) + /* Cold to Cool */ + temp_state = POWER_SUPPLY_HEALTH_COOL; + else + /* Stay Cold */ + temp_state = POWER_SUPPLY_HEALTH_COLD; + } else if (chip->temp_state == POWER_SUPPLY_HEALTH_OVERHEAT) { + if (batt_temp <= + pdata->hot_temp_c - HYSTERISIS_DEGC) + /* Hot to Warm */ + temp_state = POWER_SUPPLY_HEALTH_WARM; + else + /* Stay Hot */ + temp_state = POWER_SUPPLY_HEALTH_OVERHEAT; + } + + if (chip->temp_state != temp_state) { + chip->temp_state = temp_state; + if (chip->temp_state == POWER_SUPPLY_HEALTH_WARM) { + max_t = pdata->hot_temp_c; + min_t = pdata->warm_temp_c - HYSTERISIS_DEGC; + } else if (chip->temp_state == POWER_SUPPLY_HEALTH_GOOD) { + max_t = pdata->warm_temp_c; + min_t = pdata->cool_temp_c; + } else if (chip->temp_state == POWER_SUPPLY_HEALTH_COOL) { + max_t = pdata->cool_temp_c + HYSTERISIS_DEGC; + min_t = pdata->cold_temp_c; + } else if (chip->temp_state == POWER_SUPPLY_HEALTH_COLD) { + max_t = pdata->cold_temp_c + HYSTERISIS_DEGC; + min_t = pdata->cold_temp_c * 2; + } else if (chip->temp_state == POWER_SUPPLY_HEALTH_OVERHEAT) { + max_t = pdata->hot_temp_c * 2; + min_t = pdata->hot_temp_c - HYSTERISIS_DEGC; + } + + max17042_set_temp_threshold(chip, max_t, min_t); + pr_warn("Battery Temp State = %d\n", chip->temp_state); + + if (!chip->batt_psy && chip->pdata->batt_psy_name) { + batt_psy_name = chip->pdata->batt_psy_name; + chip->batt_psy = + power_supply_get_by_name((char *)batt_psy_name); + } + + if (chip->batt_psy) { + ps.intval = chip->temp_state; + power_supply_set_property(chip->batt_psy, + POWER_SUPPLY_PROP_HEALTH, &ps); + } + + power_supply_changed(chip->battery); + ret = 1; + } + + mutex_unlock(&chip->check_temp_lock); + + return ret; +} + +#define ZERO_PERC_SOC_THRESHOLD 0x0100 +static void max17042_set_soc_threshold(struct max17042_chip *chip, u16 off) +{ + struct regmap *map = chip->regmap; + u32 soc, soc_tr; + + /* program interrupt thesholds such that we should + * get interrupt for every 'off' perc change in the soc + */ + regmap_read(map, MAX17042_RepSOC, &soc); + soc >>= 8; + if (soc <= 0) + regmap_write(map, MAX17042_SALRT_Th, ZERO_PERC_SOC_THRESHOLD); + else { + soc_tr = (soc + off) << 8; + soc_tr |= (soc - off); + regmap_write(map, MAX17042_SALRT_Th, soc_tr); + } +} + +static irqreturn_t max17042_thread_handler(int id, void *dev) +{ + struct max17042_chip *chip = dev; + + schedule_delayed_work(&chip->thread_work, + msecs_to_jiffies(10)); + + return IRQ_HANDLED; +} + +static void max17042_thread_worker(struct work_struct *work) +{ + u32 val, val2; + union power_supply_propval ret = {0, }; + const char *batt_psy_name; + struct max17042_chip *chip = + container_of(work, struct max17042_chip, + thread_work.work); + struct regmap *map = chip->regmap; + int retval = 0; + + if (!chip->batt_psy && chip->pdata->batt_psy_name) { + batt_psy_name = chip->pdata->batt_psy_name; + chip->batt_psy = + power_supply_get_by_name((char *)batt_psy_name); + } + + regmap_read(chip->regmap, MAX17042_STATUS, &val); + if ((val & STATUS_POR_BIT) && + (chip->init_complete || chip->factory_mode || chip->is_factory_image)) { + retval = regmap_read(map, MAX17042_OCVInternal, &val); + if (retval < 0) + val = -EINVAL; + else + val = val * 625 / 8; + retval = regmap_read(map, MAX17042_OCV, &val2); + if (retval < 0) + val2 = -EINVAL; + else + val2 = val2 * 625 / 8; + + dev_warn(&chip->client->dev, + "After OCV = %d, OCV Internal = %d\n", val2, val); + dev_warn(&chip->client->dev, "POR Detected, Loading Config\n"); + chip->init_complete = 0; + schedule_work(&chip->work); + + return; + } + if ((val & STATUS_INTR_SOCMIN_BIT) || + (val & STATUS_INTR_SOCMAX_BIT)) { + dev_info(&chip->client->dev, "SOC threshold INTR\n"); + max17042_set_soc_threshold(chip, 1); + } + + if (val & STATUS_INTR_VMIN_BIT) { + dev_info(&chip->client->dev, "Battery undervoltage INTR\n"); + chip->batt_undervoltage = true; + } else if (val & STATUS_INTR_VMAX_BIT) { + dev_info(&chip->client->dev, "Battery overvoltage INTR\n"); + if (!chip->factory_mode) + regmap_write(map, MAX17042_VALRT_Th, + chip->pdata->config_data->valrt_thresh); + } + + if ((val & STATUS_INTR_TEMP_BIT) || + (val & STATUS_INTR_TEMPMIN_BIT)) { + dev_info(&chip->client->dev, "Temperature INTR\n"); + max17042_check_temp(chip); + } + + + power_supply_changed(chip->battery); + + if (chip->batt_psy) + power_supply_set_property(chip->batt_psy, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, &ret); + return; +} + +static void max17042_init_worker(struct work_struct *work) +{ + struct max17042_chip *chip = container_of(work, + struct max17042_chip, work); + int ret; + + /* Initialize registers according to values from the platform data */ + if (chip->pdata->enable_por_init && chip->pdata->config_data) { + ret = max17042_init_chip(chip); + if (ret) + return; + } + + max17042_set_soc_threshold(chip, 1); + chip->temp_state = POWER_SUPPLY_HEALTH_UNKNOWN; + max17042_check_temp(chip); + + chip->init_complete = 1; +} + +static void max17042_check_temp_worker(struct work_struct *work) +{ + struct max17042_chip *chip = container_of(work, + struct max17042_chip, + check_temp_work); + + max17042_check_temp(chip); +} + +struct max17042_config_data default_mmi_config = { + /* External current sense resistor value in milli-ohms */ + .cur_sense_val = 10, + + /* A/D measurement */ + .tgain = 0xE71C, /* 0x2C */ + .toff = 0x251A, /* 0x2D */ + .cgain = 0x7FFF, /* 0x2E */ + .coff = 0x0000, /* 0x2F */ + + /* Alert / Status */ + .valrt_thresh = 0xFF97, /* 0x01 */ + .talrt_thresh = 0x7F80, /* 0x02 */ + .soc_alrt_thresh = 0xFF00, /* 0x03 */ + .config = 0x0214, /* 0x01D */ + .shdntimer = 0xE000, /* 0x03F */ + + /* App data */ + .full_soc_thresh = 0x6200, /* 0x13 */ + .design_cap = 1086, /* 0x18 mAHr*/ + .ichgt_term = 70, /* 0x1E mA */ + + /* MG3 config */ + .filter_cfg = 0x87A4, /* 0x29 */ + + /* MG3 save and restore */ + .fullcap = 1086, /* 0x10 mAh*/ + .fullcapnom = 1086, /* 0x23 mAh */ + .qrtbl00 = 0x1E01, /* 0x12 */ + .qrtbl10 = 0x1281, /* 0x22 */ + .qrtbl20 = 0x0781, /* 0x32 */ + .qrtbl30 = 0x0681, /* 0x42 */ + + /* Cell technology from power_supply.h */ + .cell_technology = POWER_SUPPLY_TECHNOLOGY_LION, + + /* Cell Data */ + .vempty = 0x7D5A, /* 0x12 */ + .temp_nom = 0x1400, /* 0x24 */ + .rcomp0 = 0x004A, /* 0x38 */ + .tcompc0 = 0x243A, /* 0x39 */ + .cell_char_tbl = { + 0x9B00, + 0xA470, + 0xB2E0, + 0xB7B0, + 0xB9B0, + 0xBB70, + 0xBC20, + 0xBD00, + 0xBE10, + 0xC060, + 0xC220, + 0xC750, + 0xCB80, + 0xCF10, + 0xD2A0, + 0xD840, + 0x0070, + 0x0020, + 0x0900, + 0x0D00, + 0x0B00, + 0x1B40, + 0x1B00, + 0x2030, + 0x0FB0, + 0x0BD0, + 0x08F0, + 0x08D0, + 0x06D0, + 0x06D0, + 0x07D0, + 0x07D0, + 0x0180, + 0x0180, + 0x0180, + 0x0180, + 0x0180, + 0x0180, + 0x0180, + 0x0180, + 0x0180, + 0x0180, + 0x0180, + 0x0180, + 0x0180, + 0x0180, + 0x0180, + 0x0180, + }, +}; + +#ifdef CONFIG_OF +static struct gpio * +max17042_get_gpio_list(struct device *dev, int *num_gpio_list) +{ + struct device_node *np = dev->of_node; + struct gpio *gpio_list; + int i, num_gpios, gpio_list_size; + enum of_gpio_flags flags; + + if (!np) + return NULL; + + num_gpios = of_gpio_count(np); + if (num_gpios <= 0) + return NULL; + + gpio_list_size = sizeof(struct gpio) * num_gpios; + gpio_list = devm_kzalloc(dev, gpio_list_size, GFP_KERNEL); + + if (!gpio_list) + return NULL; + + *num_gpio_list = num_gpios; + for (i = 0; i < num_gpios; i++) { + gpio_list[i].gpio = of_get_gpio_flags(np, i, &flags); + gpio_list[i].flags = flags; + of_property_read_string_index(np, "gpio-names", i, + &gpio_list[i].label); + } + + return gpio_list; +} + +static struct max17042_reg_data * +max17042_get_init_data(struct device *dev, int *num_init_data) +{ + struct device_node *np = dev->of_node; + const __be32 *property; + static struct max17042_reg_data *init_data; + int i, lenp, num_cells, init_data_size; + + if (!np) + return NULL; + + property = of_get_property(np, INIT_DATA_PROPERTY, &lenp); + + if (!property || lenp <= 0) + return NULL; + + /* + * Check data validity and whether number of cells is even + */ + if (lenp % sizeof(*property)) { + dev_err(dev, "%s has invalid data\n", INIT_DATA_PROPERTY); + return NULL; + } + + num_cells = lenp / sizeof(*property); + if (num_cells % 2) { + dev_err(dev, "%s must have even number of cells\n", + INIT_DATA_PROPERTY); + return NULL; + } + + *num_init_data = num_cells / 2; + init_data_size = sizeof(struct max17042_reg_data) * (num_cells / 2); + init_data = (struct max17042_reg_data *) + devm_kzalloc(dev, init_data_size, GFP_KERNEL); + + if (init_data) { + for (i = 0; i < num_cells / 2; i++) { + init_data[i].addr = be32_to_cpu(property[2 * i]); + init_data[i].data = be32_to_cpu(property[2 * i + 1]); + } + } + + return init_data; +} + +static int max17042_get_cell_char_tbl(struct device *dev, + struct device_node *np, + struct max17042_config_data *config_data) +{ + const __be16 *property; + int i, lenp; + + property = of_get_property(np, CELL_CHAR_TBL_PROPERTY, &lenp); + if (!property) + return -ENODEV; + + if (lenp != sizeof(*property) * MAX17042_CHARACTERIZATION_DATA_SIZE) { + dev_err(dev, "%s must have %d cells\n", CELL_CHAR_TBL_PROPERTY, + MAX17042_CHARACTERIZATION_DATA_SIZE); + return -EINVAL; + } + + for (i = 0; i < MAX17042_CHARACTERIZATION_DATA_SIZE; i++) + config_data->cell_char_tbl[i] = be16_to_cpu(property[i]); + + return 0; +} + +static int max17042_cfg_rqrd_prop(struct device *dev, + struct device_node *np, + struct max17042_config_data *config_data) +{ + if (of_property_read_u16(np, CONFIG_PROPERTY, + &config_data->config)) + return -EINVAL; + if (of_property_read_u16(np, FILTER_CFG_PROPERTY, + &config_data->filter_cfg)) + return -EINVAL; + if (of_property_read_u16(np, RELAX_CFG_PROPERTY, + &config_data->relax_cfg)) + return -EINVAL; + if (of_property_read_u16(np, LEARN_CFG_PROPERTY, + &config_data->learn_cfg)) + return -EINVAL; + if (of_property_read_u16(np, FULL_SOC_THRESH_PROPERTY, + &config_data->full_soc_thresh)) + return -EINVAL; + if (of_property_read_u16(np, RCOMP0_PROPERTY, + &config_data->rcomp0)) + return -EINVAL; + if (of_property_read_u16(np, TCOMPC0_PROPERTY, + &config_data->tcompc0)) + return -EINVAL; + if (of_property_read_u16(np, ICHGT_TERM_PROPERTY, + &config_data->ichgt_term)) + return -EINVAL; + if (of_property_read_u16(np, QRTBL00_PROPERTY, + &config_data->qrtbl00)) + return -EINVAL; + if (of_property_read_u16(np, QRTBL10_PROPERTY, + &config_data->qrtbl10)) + return -EINVAL; + if (of_property_read_u16(np, QRTBL20_PROPERTY, + &config_data->qrtbl20)) + return -EINVAL; + if (of_property_read_u16(np, QRTBL30_PROPERTY, + &config_data->qrtbl30)) + return -EINVAL; + if (of_property_read_u16(np, FULLCAP_PROPERTY, + &config_data->fullcap)) + return -EINVAL; + if (of_property_read_u16(np, DESIGN_CAP_PROPERTY, + &config_data->design_cap)) + return -EINVAL; + if (of_property_read_u16(np, FULLCAPNOM_PROPERTY, + &config_data->fullcapnom)) + return -EINVAL; + if (of_property_read_u16(np, LAVG_EMPTY_PROPERTY, + &config_data->lavg_empty)) + return -EINVAL; + + return max17042_get_cell_char_tbl(dev, np, config_data); +} + +static void max17042_cfg_optnl_prop(struct device_node *np, + struct max17042_config_data *config_data) +{ + of_property_read_u16(np, TGAIN_PROPERTY, &config_data->tgain); + of_property_read_u16(np, TOFF_PROPERTY, &config_data->toff); + of_property_read_u16(np, CGAIN_PROPERTY, &config_data->cgain); + of_property_read_u16(np, COFF_PROPERTY, &config_data->coff); + of_property_read_u16(np, MISC_CFG_PROPERTY, &config_data->misc_cfg); + of_property_read_u16(np, REVISION, &config_data->revision); + config_data->revision &= MAX17050_CFG_REV_MASK; +} + +static const char *max17042_get_mmi_battid(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + const char *battsn_buf; + int retval; + + battsn_buf = NULL; + + if (np) + retval = of_property_read_string(np, "mmi,battid", + &battsn_buf); + else + return NULL; + + if ((retval == -EINVAL) || !battsn_buf) { + pr_err("Battsn unused\n"); + of_node_put(np); + return NULL; + + } else + pr_err("Battsn = %s\n", battsn_buf); + + of_node_put(np); + + return battsn_buf; +} + +static struct max17042_config_data * +max17042_get_config_data(struct device *dev) +{ + struct max17042_config_data *config_data; + struct device_node *np = dev->of_node; + struct device_node *node, *df_node, *sn_node; + const char *sn_buf, *df_sn, *dev_sn; + int rc; + bool df_read_sn; + + if (!np) + return NULL; + + df_read_sn = of_property_read_bool(np, "maxim,df-read-battsn"); + + np = of_get_child_by_name(np, CONFIG_NODE); + if (!np) + return NULL; + + dev_sn = NULL; + df_sn = NULL; + sn_buf = NULL; + df_node = NULL; + sn_node = NULL; + + dev_sn = max17042_get_mmi_battid(); + + rc = of_property_read_string(np, "df-serialnum", + &df_sn); + if (rc) + dev_warn(dev, "No Default Serial Number defined"); + else if (df_sn) + dev_warn(dev, "Default Serial Number %s", df_sn); + + for_each_child_of_node(np, node) { + rc = of_property_read_string(node, "serialnum", + &sn_buf); + if (!rc && sn_buf) { + if (dev_sn) + if (strnstr(dev_sn, sn_buf, 32)) + sn_node = node; + if (df_sn) + if (strnstr(df_sn, sn_buf, 32)) + df_node = node; + } + } + + if (sn_node) { + np = sn_node; + df_node = NULL; + dev_warn(dev, "Battery Match Found using %s", + sn_node->name); + } else if (df_node) { + np = df_node; + sn_node = NULL; + dev_warn(dev, "Battery Match Found using default %s", + df_node->name); + } else { + dev_warn(dev, "No Battery Match Found!"); + return NULL; + } + + config_data = devm_kzalloc(dev, sizeof(*config_data), GFP_KERNEL); + if (!config_data) + return NULL; + + if (max17042_cfg_rqrd_prop(dev, np, config_data)) { + devm_kfree(dev, config_data); + return NULL; + } + + max17042_cfg_optnl_prop(np, config_data); + + if ((np == df_node) && !df_read_sn) + config_data->revision = 1; + + dev_warn(dev, "Config Revision = %d", config_data->revision); + + config_data->cur_sense_val = 10; + config_data->valrt_thresh = 0xDF97; + config_data->talrt_thresh = 0x7F80; + config_data->soc_alrt_thresh = 0xFF00; + config_data->config = 0x0214; + config_data->shdntimer = 0xE000; + config_data->cell_technology = POWER_SUPPLY_TECHNOLOGY_LION; + config_data->vempty = 0x964C; + config_data->temp_nom = 0x1400; + + return config_data; +} + +static struct max17042_temp_conv * +max17042_get_conv_table(struct device *dev) +{ + struct device_node *np = dev->of_node; + struct max17042_temp_conv *temp_conv; + const __be16 *property; + int i, lenp, num; + u16 temp; + s16 start; + + if (!np) + return NULL; + + np = of_get_child_by_name(np, TEMP_CONV_NODE); + if (!np) + return NULL; + + property = of_get_property(np, RESULT_PROPERTY, &lenp); + if (!property || lenp <= 0) { + dev_err(dev, "%s must have %s property\n", TEMP_CONV_NODE, + RESULT_PROPERTY); + return NULL; + } + + if (of_property_read_u16(np, START_PROPERTY, &temp)) { + dev_err(dev, "%s must have %s property\n", TEMP_CONV_NODE, + START_PROPERTY); + return NULL; + } + + start = (s16) temp; + + temp_conv = devm_kzalloc(dev, sizeof(*temp_conv), GFP_KERNEL); + if (!temp_conv) + return NULL; + + num = lenp / sizeof(*property); + temp_conv->result = devm_kzalloc(dev, sizeof(s16) * num, GFP_KERNEL); + if (!temp_conv->result) { + devm_kfree(dev, temp_conv); + return NULL; + } + + temp_conv->start = start; + temp_conv->num_result = num; + + for (i = 0; i < num; i++) { + temp = be16_to_cpu(property[i]); + temp_conv->result[i] = (s16) temp; + } + + return temp_conv; +} + + +static struct max17042_platform_data * +max17042_get_pdata(struct device *dev) +{ + struct device_node *np = dev->of_node; + u32 prop; + struct max17042_platform_data *pdata; + int rc; + + if (!np) + return dev->platform_data; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return NULL; + + pdata->init_data = max17042_get_init_data(dev, &pdata->num_init_data); + pdata->gpio_list = max17042_get_gpio_list(dev, &pdata->num_gpio_list); + + /* + * Require current sense resistor value to be specified for + * current-sense functionality to be enabled at all. + */ + if (of_property_read_u32(np, "maxim,rsns-microohm", &prop) == 0) { + pdata->r_sns = prop; + pdata->enable_current_sense = true; + } + + pdata->main_psy = + of_property_read_bool(np, "maxim,main-psy"); + + pdata->enable_por_init = + of_property_read_bool(np, "maxim,enable_por_init"); + + pdata->batt_undervoltage_zero_soc = + of_property_read_bool(np, "maxim,batt_undervoltage_zero_soc"); + + rc = of_property_read_string(np, "maxim,batt-psy-name", + &pdata->batt_psy_name); + if (rc) + pdata->batt_psy_name = NULL; + + if (pdata->batt_psy_name) + pr_warn("BATT SUPPLY NAME = %s\n", pdata->batt_psy_name); + + pdata->use_external_temp = of_property_read_bool(np, + "maxim,use-external-temp"); + if (!pdata->use_external_temp) { + of_property_read_u32(np, "maxim,warm-temp-c", + &pdata->warm_temp_c); + of_property_read_u32(np, "maxim,hot-temp-c", + &pdata->hot_temp_c); + of_property_read_u32(np, "maxim,cool-temp-c", + &pdata->cool_temp_c); + of_property_read_u32(np, "maxim,cold-temp-c", + &pdata->cold_temp_c); + rc = of_property_read_u32(np, "maxim,hotspot-thrs-c", + &pdata->hotspot_thrs_c); + if (rc) { + pdata->hotspot_thrs_c = 50; + pr_debug("DT hotspot threshold not avail using %d\n", + pdata->hotspot_thrs_c); + } + + pdata->tcnv = max17042_get_conv_table(dev); + } + + pdata->config_data = max17042_get_config_data(dev); + + if (!pdata->config_data) { + dev_warn(dev, "config data is missing\n"); + + pdata->config_data = &default_mmi_config; + pdata->enable_por_init = true; + } + + if (pdata->use_external_temp) { + pdata->config_data->config &= ~MAX17042_CONFIG_TEN; + pdata->config_data->config |= MAX17042_CONFIG_TEX; + } + + /* Read Valrt threshold override */ + if (of_property_read_u32(np, "maxim,valrt-threshold", &prop) == 0) + pdata->config_data->valrt_thresh = (u16)prop; + + if (of_property_read_s32(np, "maxim,cold-temp", &pdata->temp_min)) + pdata->temp_min = INT_MIN; + if (of_property_read_s32(np, "maxim,over-heat-temp", &pdata->temp_max)) + pdata->temp_max = INT_MAX; + if (of_property_read_s32(np, "maxim,dead-volt", &pdata->vmin)) + pdata->vmin = INT_MIN; + if (of_property_read_s32(np, "maxim,over-volt", &pdata->vmax)) + pdata->vmax = INT_MAX; + + return pdata; +} +#else +static struct max17042_platform_data * +max17042_get_pdata(struct device *dev) +{ + return dev->platform_data; +} +#endif + +static int max17042_debugfs_read_addr(void *data, u64 *val) +{ + struct max17042_chip *chip = (struct max17042_chip *)data; + *val = chip->debugfs_addr; + return 0; +} + +static int max17042_debugfs_write_addr(void *data, u64 val) +{ + struct max17042_chip *chip = (struct max17042_chip *)data; + chip->debugfs_addr = val; + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(addr_fops, max17042_debugfs_read_addr, + max17042_debugfs_write_addr, "0x%02llx\n"); + +static int max17042_debugfs_read_data(void *data, u64 *val) +{ + struct max17042_chip *chip = (struct max17042_chip *)data; + struct regmap *map = chip->regmap; + int rd; + int ret = regmap_read(map, chip->debugfs_addr, &rd); + + if (ret < 0) + return ret; + + *val = rd; + return 0; +} + +static int max17042_debugfs_write_data(void *data, u64 val) +{ + struct max17042_chip *chip = (struct max17042_chip *)data; + struct regmap *map = chip->regmap; + return regmap_write(map, chip->debugfs_addr, val); +} +DEFINE_SIMPLE_ATTRIBUTE(data_fops, max17042_debugfs_read_data, + max17042_debugfs_write_data, "0x%02llx\n"); + +static int max17042_debugfs_create(struct max17042_chip *chip) +{ + chip->debugfs_root = debugfs_create_dir(dev_name(&chip->client->dev), + NULL); + if (!chip->debugfs_root) + return -ENOMEM; + + if (!debugfs_create_file("addr", S_IRUGO | S_IWUSR, chip->debugfs_root, + chip, &addr_fops)) + goto err_debugfs; + + if (!debugfs_create_file("data", S_IRUGO | S_IWUSR, chip->debugfs_root, + chip, &data_fops)) + goto err_debugfs; + + return 0; + +err_debugfs: + debugfs_remove_recursive(chip->debugfs_root); + chip->debugfs_root = NULL; + return -ENOMEM; +} + +#define MAX_TAPER_RETRY 5 +static void iterm_work(struct work_struct *work) +{ + int ret = 0; + int repsoc; + int repcap, fullcap; + int repcap_p, i; + int socvf, fullsocthr; + long int curr_avg, curr_inst, iterm_max, iterm_min; + int resch_time = 60000; + int cfd_max; + int val; + int taper_hit = 0; + struct max17042_chip *chip = + container_of(work, struct max17042_chip, + iterm_work.work); + struct regmap *map = chip->regmap; + + max17042_stay_awake(&chip->max17042_wake_source); + if (!chip->init_complete) + goto iterm_fail; + + /* check to see if Already Full */ + ret = regmap_read(map, MAX17042_RepSOC, &val); + if (ret < 0) + goto iterm_fail; + + repsoc = (val >> 8) & 0xFF; + if ((val & 0xFF) != 0) + repsoc++; /* Round up */ + + dev_dbg(&chip->client->dev, "ITERM RepSOC %d!\n", repsoc); + + cfd_max = + (chip->charge_full_des + ((chip->charge_full_des * 3) / 100)); + + if (repsoc >= 100) { + ret = regmap_read(map, MAX17042_FullCAP, &val); + if (ret < 0) + goto iterm_fail; + + fullcap = (val & 0xFFFF) * chip->fctr_uah_bit; + + /* Check that FullCap */ + /* is not less then half of Design Capacity */ + if (fullcap < (chip->charge_full_des / 2)) { + dev_warn(&chip->client->dev, + "Error fullcap too small! Forcing POR!\n"); + dev_warn(&chip->client->dev, + "FullCap %d uAhr\n", + fullcap); + regmap_write(map, MAX17042_VFSOC0Enable, + MAX17050_FORCE_POR); + msleep(MAX17050_POR_WAIT_MS); + schedule_delayed_work(&chip->thread_work, + msecs_to_jiffies(0)); + goto iterm_fail; + } + + /* Catch Increasing FullCap*/ + if (fullcap > cfd_max) { + dev_warn(&chip->client->dev, + "Error fullcap too big! %d uAhr\n", + fullcap); + fullcap = cfd_max; + ret = regmap_write(map, + MAX17042_RepCap, + CHRG_CONV_WR(chip->fctr_uah_bit, + fullcap / 1000)); + if (ret < 0) + dev_err(&chip->client->dev, + "Can't update Rep Cap!\n"); + ret = regmap_write(map, + MAX17042_FullCAP, + CHRG_CONV_WR(chip->fctr_uah_bit, + fullcap / 1000)); + if (ret < 0) + dev_err(&chip->client->dev, + "Can't update Full Cap!\n"); + } + } + + ret = regmap_read(map, MAX17042_Current, &val); + if (ret < 0) + goto iterm_fail; + + /* check for discharging */ + if (val & 0x8000) { + dev_dbg(&chip->client->dev, "ITERM Curr Inst Discharge!\n"); + goto iterm_fail; + } + + if (chip->taper_reached) { + taper_hit = 1; + goto iterm_fail; + } + + curr_inst = val & 0xFFFF; + dev_dbg(&chip->client->dev, "ITERM Curr Inst %ld uA!\n", + CURR_CONV(chip->pdata->r_sns, curr_inst)); + + ret = regmap_read(map, MAX17042_AvgCurrent, &val); + if (ret < 0) + goto iterm_fail; + + /* check for discharging */ + if (val & 0x8000) { + dev_dbg(&chip->client->dev, "ITERM Curr Avg Discharge!\n"); + goto iterm_fail; + } + + /* No Discharge so Monitor Faster */ + resch_time = 10000; + + curr_avg = val & 0xFFFF; + dev_dbg(&chip->client->dev, "ITERM Curr Avg %ld uA!\n", + CURR_CONV(chip->pdata->r_sns, curr_inst)); + + ret = regmap_read(map, MAX17042_ICHGTerm, &val); + if (ret < 0) + goto iterm_fail; + + iterm_min = val & 0xFFFF; + iterm_min = (iterm_min >> 2) & 0xFFFF; + + iterm_max = (val & 0xFFFF) + iterm_min; + + iterm_min = (iterm_min >> 1) & 0xFFFF; + + if ((curr_inst <= iterm_min) || (curr_inst >= iterm_max) || + (curr_avg <= iterm_min) || (curr_avg >= iterm_max)) + goto iterm_fail; + + ret = regmap_read(map, MAX17042_RepCap, &val); + if (ret < 0) + goto iterm_fail; + + repcap = (val & 0xFFFF) * chip->fctr_uah_bit; + dev_dbg(&chip->client->dev, "ITERM RepCap %d uAhr!\n", + repcap); + + ret = regmap_read(map, MAX17042_FullCAP, &val); + if (ret < 0) + goto iterm_fail; + + fullcap = (val & 0xFFFF) * chip->fctr_uah_bit; + dev_dbg(&chip->client->dev, "ITERM FullCap %d uAhr!\n", + fullcap); + + if (repcap >= fullcap) { + taper_hit = 1; + goto iterm_fail; + } + + ret = regmap_read(map, MAX17042_VFSOC, &val); + if (ret < 0) + goto iterm_fail; + + socvf = (val >> 8) & 0xFF; + + ret = regmap_read(map, MAX17047_FullSOCThr, &val); + if (ret < 0) + goto iterm_fail; + + fullsocthr = (val >> 8) & 0xFF; + + dev_dbg(&chip->client->dev, "ITERM VFSOC %d!\n", socvf); + dev_dbg(&chip->client->dev, "ITERM FullSOCThr %d!\n", fullsocthr); + if (socvf <= fullsocthr) + goto iterm_fail; + + /* Check that RepCap or FullCap */ + /* is not less then half of Design Capacity */ + if ((repcap < (chip->charge_full_des / 2)) || + (fullcap < (chip->charge_full_des / 2))) { + dev_warn(&chip->client->dev, + "Error fullcap too small! Forcing POR!\n"); + dev_warn(&chip->client->dev, + "FullCap %d uAhr\n", + fullcap); + regmap_write(map, MAX17042_VFSOC0Enable, + MAX17050_FORCE_POR); + msleep(MAX17050_POR_WAIT_MS); + schedule_delayed_work(&chip->thread_work, + msecs_to_jiffies(0)); + + goto iterm_fail; + } + + /* Catch Increasing FullCap*/ + if (fullcap > cfd_max) { + dev_warn(&chip->client->dev, "Error fullcap too big!\n"); + dev_warn(&chip->client->dev, + "RepCap %d uAhr FullCap %d uAhr\n", + repcap, fullcap); + repcap = cfd_max; + fullcap = repcap; + ret = regmap_write(map, MAX17042_RepSOC, + CHRG_CONV_WR(chip->fctr_uah_bit, + repcap / 1000)); + if (ret < 0) + dev_err(&chip->client->dev, + "Can't update Rep Cap!\n"); + } + + chip->last_fullcap = repcap; + + dev_warn(&chip->client->dev, "Taper Reached!\n"); + dev_warn(&chip->client->dev, "RepCap %d uAhr FullCap %d uAhr\n", + repcap, fullcap); + + repcap_p = repcap; + taper_hit = 1; + + + ret = regmap_write(map, MAX17042_FullCAP, + CHRG_CONV_WR(chip->fctr_uah_bit, + repcap / 1000)); + if (ret < 0) + dev_err(&chip->client->dev, "Can't update Full Cap!\n"); + + for (i = 0; i < MAX_TAPER_RETRY; i++) { + ret = regmap_read(map, MAX17042_RepCap, &val); + if (ret < 0) + goto iterm_fail; + + repcap = (val & 0xFFFF) * chip->fctr_uah_bit; + + ret = regmap_read(map, MAX17042_FullCAP, &val); + if (ret < 0) + goto iterm_fail; + + fullcap = (val & 0xFFFF) * chip->fctr_uah_bit; + dev_warn(&chip->client->dev, + "Checking RepCap %d uAhr FullCap %d uAhr\n", + repcap, fullcap); + if ((repcap > (repcap_p + 2000)) || + (repcap < (repcap_p - 2000)) || + (fullcap != repcap_p)) { + dev_warn(&chip->client->dev, + "ITERM values don't match rewrite!\n"); + dev_warn(&chip->client->dev, + "RepCap %d uAhr FullCap %d uAhr\n", + repcap, fullcap); + ret = regmap_write(map, + MAX17042_FullCAP, + CHRG_CONV_WR(chip->fctr_uah_bit, + repcap_p / 1000)); + if (ret < 0) + dev_err(&chip->client->dev, + "Can't update Full Cap!\n"); + ret = regmap_write(map, + MAX17042_RepCap, + CHRG_CONV_WR(chip->fctr_uah_bit, + repcap_p / 1000)); + if (ret < 0) + dev_err(&chip->client->dev, + "Can't update Rep Cap!\n"); + } else + break; + } + + +iterm_fail: + dev_dbg(&chip->client->dev, + "SW ITERM Done!\n"); + chip->taper_reached = taper_hit; + schedule_delayed_work(&chip->iterm_work, + msecs_to_jiffies(resch_time)); + max17042_relax(&chip->max17042_wake_source); + return; +} + +enum { + MMI_FACTORY_MODE, + MMI_FACTORY_BUILD, +}; + +static bool mmi_factory_check(int type) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool factory = false; + const char *bootargs = NULL; + char *bl_version = NULL; + + if (!np) + return factory; + + switch (type) { + case MMI_FACTORY_MODE: + factory = of_property_read_bool(np, + "mmi,factory-cable"); + break; + case MMI_FACTORY_BUILD: + if (!of_property_read_string(np, + "bootargs", &bootargs)) { + bl_version = strstr(bootargs, + "androidboot.bootloader="); + if (bl_version && strstr(bl_version, "factory")) + factory = true; + } + break; + default: + factory = false; + } + of_node_put(np); + + return factory; +} + +static const struct regmap_config max17042_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .val_format_endian = REGMAP_ENDIAN_NATIVE, +}; + +static const struct power_supply_desc max17042_psy_desc = { + .name = "max170xx_battery", + .type = POWER_SUPPLY_TYPE_MAIN, + .get_property = max17042_get_property, + .set_property = max17042_set_property, + .property_is_writeable = max17042_is_writeable, + .properties = max17042_battery_props, + .num_properties = ARRAY_SIZE(max17042_battery_props), +}; + +static const struct power_supply_desc max17042_main_psy_desc = { + .name = "max17_main_battery", + .type = POWER_SUPPLY_TYPE_MAIN, + .get_property = max17042_get_property, + .set_property = max17042_set_property, + .property_is_writeable = max17042_is_writeable, + .properties = max17042_battery_props, + .num_properties = ARRAY_SIZE(max17042_battery_props), +}; + +static const struct power_supply_desc max17042_flip_psy_desc = { + .name = "max17_flip_battery", + .type = POWER_SUPPLY_TYPE_MAIN, + .get_property = max17042_get_property, + .set_property = max17042_set_property, + .property_is_writeable = max17042_is_writeable, + .properties = max17042_battery_props, + .num_properties = ARRAY_SIZE(max17042_battery_props), +}; + +static const struct power_supply_desc max17042_no_current_sense_psy_desc = { + .name = "max170xx_battery", + .type = POWER_SUPPLY_TYPE_MAIN, + .get_property = max17042_get_property, + .set_property = max17042_set_property, + .property_is_writeable = max17042_is_writeable, + .properties = max17042_battery_props, + .num_properties = ARRAY_SIZE(max17042_battery_props) - 2, +}; + +static ssize_t age_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct power_supply *psy; + struct max17042_chip *chip; + int age, ret; + u32 data; + psy = dev_get_drvdata(dev); + if (psy) + chip = power_supply_get_drvdata(psy); + else { + pr_err("MAX170xx: No Age PSY\n"); + return 0; + } + + ret = regmap_read(chip->regmap, MAX17042_FullCAP, &data); + if (ret < 0) + return 0; + if (!chip->charge_full_des) + return 0; + + age = data * chip->fctr_uah_bit; + age *= 100; + age /= chip->charge_full_des; + + return scnprintf(buf, 50, "%d\n", age); +} +static DEVICE_ATTR(age, S_IRUGO, age_show, NULL); + +static struct attribute * max_g[] = { + &dev_attr_age.attr, + NULL, +}; + +static const struct attribute_group power_supply_max_attr_group = { + .attrs = max_g, +}; + +#define MAX_CGAIN 0x7FFF +static int max17042_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + const struct power_supply_desc *max17042_desc = &max17042_psy_desc; + struct power_supply_config psy_cfg = {}; + struct max17042_chip *chip; + struct max17042_config_data *config; + int ret; + int i; + u32 val, val2; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -EIO; + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->client = client; + chip->regmap = devm_regmap_init_i2c(client, &max17042_regmap_config); + if (IS_ERR(chip->regmap)) { + dev_err(&client->dev, "Failed to initialize regmap\n"); + return -EINVAL; + } + + chip->pdata = max17042_get_pdata(&client->dev); + if (!chip->pdata) { + dev_err(&client->dev, "no platform data provided\n"); + return -EINVAL; + } + + chip->last_fullcap = -EINVAL; + chip->factory_mode = false; + chip->charge_full_des = + (chip->pdata->config_data->design_cap) * 1000; + + i2c_set_clientdata(client, chip); + chip->chip_type = id->driver_data; + psy_cfg.drv_data = chip; + + if (chip->pdata->main_psy) + max17042_desc = &max17042_main_psy_desc; + else + max17042_desc = &max17042_flip_psy_desc; + + /* When current is not measured, + * CURRENT_NOW and CURRENT_AVG properties should be invisible. */ + if (!chip->pdata->enable_current_sense) + max17042_desc = &max17042_no_current_sense_psy_desc; + + if (chip->pdata->r_sns == 0) + chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR; + + if (chip->pdata->config_data->cgain == MAX_CGAIN) { + dev_err(&client->dev, "RSNS is %d uOhm\n", + chip->pdata->r_sns); + chip->pdata->r_sns *= 2; + dev_err(&client->dev, "MAX CGAIN RSNS now %d uOhm\n", + chip->pdata->r_sns); + } + + chip->fctr_uah_bit = 5000 / (chip->pdata->r_sns / 1000); + + if (chip->pdata->init_data) + for (i = 0; i < chip->pdata->num_init_data; i++) + regmap_write(chip->regmap, + chip->pdata->init_data[i].addr, + chip->pdata->init_data[i].data); + + if (!chip->pdata->enable_current_sense) { + regmap_write(chip->regmap, MAX17042_CGAIN, 0x0000); + regmap_write(chip->regmap, MAX17042_MiscCFG, 0x0003); + regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007); + } + + chip->battery = devm_power_supply_register(&client->dev, max17042_desc, + &psy_cfg); + if (IS_ERR(chip->battery)) { + dev_err(&client->dev, "failed: power supply register\n"); + return PTR_ERR(chip->battery); + } + ret = sysfs_create_group(&chip->battery->dev.kobj, + &power_supply_max_attr_group); + if (ret) + dev_err(&client->dev, "failed: attr create\n"); + + chip->factory_mode = mmi_factory_check(MMI_FACTORY_MODE); + chip->is_factory_image = mmi_factory_check(MMI_FACTORY_BUILD); + if (chip->factory_mode) + dev_info(&client->dev, "max17042: Factory Mode\n"); + + chip->temp_state = POWER_SUPPLY_HEALTH_UNKNOWN; + chip->taper_reached = 0; + mutex_init(&chip->check_temp_lock); + max17042_check_temp(chip); + INIT_WORK(&chip->check_temp_work, max17042_check_temp_worker); + + ret = gpio_request_array(chip->pdata->gpio_list, + chip->pdata->num_gpio_list); + if (ret) { + dev_err(&client->dev, "cannot request GPIOs\n"); + return ret; + } + + for (i = 0; i < chip->pdata->num_gpio_list; i++) { + if (chip->pdata->gpio_list[i].flags & GPIOF_EXPORT) { + ret = gpio_export_link(&client->dev, + chip->pdata->gpio_list[i].label, + chip->pdata->gpio_list[i].gpio); + if (ret) { + dev_err(&client->dev, + "Failed to link GPIO %s: %d\n", + chip->pdata->gpio_list[i].label, + chip->pdata->gpio_list[i].gpio); + return ret; + } + } + } + + /* Override Voltage Alert Threshold */ + max17042_override_por(chip->regmap, MAX17042_VALRT_Th, + chip->pdata->config_data->valrt_thresh); + + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, + max17042_thread_handler, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + chip->battery->desc->name, + chip); + if (!ret) { + regmap_update_bits(chip->regmap, MAX17042_CONFIG, + CONFIG_ALRT_BIT_ENBL, + CONFIG_ALRT_BIT_ENBL); + max17042_set_soc_threshold(chip, 1); + } else { + client->irq = 0; + dev_err(&client->dev, "%s(): cannot get IRQ\n", + __func__); + } + } + + regmap_read(chip->regmap, MAX17042_STATUS, &val); + regmap_read(chip->regmap, MAX17050_CFG_REV_REG, &val2); + val2 &= MAX17050_CFG_REV_MASK; + + INIT_WORK(&chip->work, max17042_init_worker); + INIT_DELAYED_WORK(&chip->thread_work, max17042_thread_worker); + + if (chip->factory_mode || chip->is_factory_image) { + ret = regmap_read(chip->regmap, MAX17042_OCVInternal, &val); + if (ret < 0) + val = -EINVAL; + else + val = val * 625 / 8; + ret = regmap_read(chip->regmap, MAX17042_OCV, &val2); + if (ret < 0) + val2 = -EINVAL; + else + val2 = val2 * 625 / 8; + + dev_warn(&client->dev, "Factory Mode so Force POR\n"); + dev_warn(&client->dev, "OCV = %d, OCV Internal = %d\n", + val2, val); + + regmap_write(chip->regmap, MAX17042_VFSOC0Enable, + MAX17050_FORCE_POR); + chip->init_complete = 0; + schedule_delayed_work(&chip->thread_work, + msecs_to_jiffies(MAX17050_POR_WAIT_MS)); + } else if (val & STATUS_POR_BIT) { + dev_warn(&client->dev, "POR Detected, Loading Config\n"); + schedule_work(&chip->work); + } else if (val2 != chip->pdata->config_data->revision) { + dev_warn(&client->dev, "Revision Change, Loading Config\n"); + schedule_work(&chip->work); + } else { + config = chip->pdata->config_data; + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047) + regmap_write(chip->regmap, MAX17047_FullSOCThr, + config->full_soc_thresh); + chip->init_complete = 1; + } + + /* Check if we need a reset, if the diff between this fuel + * gauge and the other is too large, there is probably something wrong. + */ + max17042_check_fuel_gauge_diff(chip); + + ret = max17042_debugfs_create(chip); + if (ret) { + dev_err(&client->dev, "cannot create debugfs\n"); + return ret; + } + + wakeup_source_init(&chip->max17042_wake_source.source, + "max17042_wake"); + INIT_DELAYED_WORK(&chip->iterm_work, + iterm_work); + + schedule_delayed_work(&chip->iterm_work, + msecs_to_jiffies(10000)); + + return 0; +} + +static int max17042_remove(struct i2c_client *client) +{ + struct max17042_chip *chip = i2c_get_clientdata(client); + + cancel_work_sync(&chip->check_temp_work); + mutex_destroy(&chip->check_temp_lock); + + debugfs_remove_recursive(chip->debugfs_root); + + gpio_free_array(chip->pdata->gpio_list, chip->pdata->num_gpio_list); + + wakeup_source_trash(&chip->max17042_wake_source.source); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int max17042_suspend(struct device *dev) +{ + struct max17042_chip *chip = dev_get_drvdata(dev); + + /* + * disable the irq and enable irq_wake + * capability to the interrupt line. + */ + if (chip->client->irq) { + disable_irq(chip->client->irq); + enable_irq_wake(chip->client->irq); + } + + return 0; +} + +static int max17042_resume(struct device *dev) +{ + struct max17042_chip *chip = dev_get_drvdata(dev); + + if (chip->client->irq) { + disable_irq_wake(chip->client->irq); + enable_irq(chip->client->irq); + /* re-program the SOC thresholds to 1% change */ + max17042_set_soc_threshold(chip, 1); + } + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(max17042_pm_ops, max17042_suspend, + max17042_resume); + +#ifdef CONFIG_OF +static const struct of_device_id max17042_dt_match[] = { + { .compatible = "maxim,max17042" }, + { .compatible = "maxim,max17047" }, + { .compatible = "maxim,max17050" }, + { }, +}; +MODULE_DEVICE_TABLE(of, max17042_dt_match); +#endif + +static const struct i2c_device_id max17042_id[] = { + { "max17042", MAXIM_DEVICE_TYPE_MAX17042 }, + { "max17047", MAXIM_DEVICE_TYPE_MAX17047 }, + { "max17050", MAXIM_DEVICE_TYPE_MAX17050 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max17042_id); + +static struct i2c_driver max17042_i2c_driver = { + .driver = { + .name = "max170xx", + .of_match_table = of_match_ptr(max17042_dt_match), + .pm = &max17042_pm_ops, + }, + .probe = max17042_probe, + .remove = max17042_remove, + .id_table = max17042_id, +}; +module_i2c_driver(max17042_i2c_driver); + +MODULE_AUTHOR("MyungJoo Ham "); +MODULE_DESCRIPTION("MAX170XX MMI Fuel Gauge"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/mm8013c_fg_mmi/Android.mk b/drivers/power/mm8013c_fg_mmi/Android.mk new file mode 100644 index 000000000000..a5b54a35b17f --- /dev/null +++ b/drivers/power/mm8013c_fg_mmi/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mm8013c_fg_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/mm8013c_fg_mmi/Kbuild b/drivers/power/mm8013c_fg_mmi/Kbuild new file mode 100644 index 000000000000..66ea64620b81 --- /dev/null +++ b/drivers/power/mm8013c_fg_mmi/Kbuild @@ -0,0 +1,7 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/drivers/power/mm8013c_fg_mmi + +mm8013c_fg_mmi-objs += mm8013c_battery.o +obj-m += mm8013c_fg_mmi.o diff --git a/drivers/power/mm8013c_fg_mmi/Makefile b/drivers/power/mm8013c_fg_mmi/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/power/mm8013c_fg_mmi/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/power/mm8013c_fg_mmi/mm8013c_battery.c b/drivers/power/mm8013c_fg_mmi/mm8013c_battery.c new file mode 100644 index 000000000000..247ebc6a0cf5 --- /dev/null +++ b/drivers/power/mm8013c_fg_mmi/mm8013c_battery.c @@ -0,0 +1,2287 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * MM8xxx battery driver + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MM8XXX_MANUFACTURER "MITSUMI ELECTRIC" +#define MM8XXX_BATT_PHY "bms" +#define mm_info(fmt, arg...) \ + printk("FG_MM8xxx : %s-%d : " fmt, __FUNCTION__ ,__LINE__,##arg) + +static DEFINE_IDR(battery_id); +static DEFINE_MUTEX(battery_mutex); + +enum mm8xxx_chip { + MM8118G01 = 1, + MM8118W02, + MM8013C10, +}; + +struct mm8xxx_device_info; + +struct mm8xxx_access_methods { + int (*read)(struct mm8xxx_device_info *di, u8 cmd); + int (*write)(struct mm8xxx_device_info *di, u8 cmd, int value); +}; + +struct mm8xxx_state_cache { + int temperature; + int avg_time_to_empty; + int full_charge_capacity; + int cycle_count; + int soc; + int flags; + int health; + int elapsed_months; + int elapsed_days; + int elapsed_hours; +}; + +struct mm8xxx_device_info { + struct device *dev; + int id; + enum mm8xxx_chip chip; + const char *name; + struct mm8xxx_access_methods bus; + struct mm8xxx_state_cache cache; + int charge_design_full; + unsigned long last_update; + struct delayed_work work; + struct power_supply *psy; + struct power_supply *batt_psy; + struct power_supply *usb_psy; + struct power_supply *dc_psy; + struct list_head list; + struct mutex lock; + u8 *cmds; + struct iio_channel *Batt_NTC_channel; + bool fake_battery; + u32 latest_fw_version; + u32 first_battery_param_ver; + u32 second_battery_param_ver; + u32 first_battery_id; + u32 second_battery_id; +}; + +struct mm8xxx_device_info *di = NULL; + +static int mm8xxx_battery_read_Nbyte(struct mm8xxx_device_info *di, u8 cmd, unsigned char *data, unsigned char length); +static int mm8xxx_battery_read(struct mm8xxx_device_info *di, u8 cmd); +static int mm8xxx_battery_write(struct mm8xxx_device_info *di, u8 cmd,int value); +static int mm8xxx_battery_write_Nbyte(struct mm8xxx_device_info *di, u8 cmd,unsigned int value, int byte_num); +static int mm8xxx_battery_write_4byteCmd(struct mm8xxx_device_info *di, unsigned int cmd,unsigned int value); +static u32 mmi_get_battery_info(struct mm8xxx_device_info *di, u32 cmd); +/****************************FW / Parameter update****************************************/ +#define ENABLE_VERIFICATION + +#define MM8013_HW_VERSION 0x0021 +#define MM8013_LATEST_FW_VERSION 0x0812 +#define MM8013_PARAMETER_VERSION 0x0201 + +#define MM8013_DEFAULT_BATTERY_ID 0x0103 +#define MM8013_1TH_BATTERY_ID 0x0103 +#define MM8013_2TH_BATTERY_ID 0x0102 + +#define ADDRESS_PROGRAM (0x00008000) +#define ADDRESS_PARAMETER (0x00014000) +#define SIZE_PROGRAM (0x4000) +#define SIZE_PARAMETER (0x3C0) +#ifdef ENABLE_VERIFICATION +#define SIZE_READBUFFER (16) +#endif + +#define COMMAND_CONTROL (0x00) +#define COMMAND_FGCONDITION (0x6E) +#define COMMAND_MODECONTROL (0x88) +#ifdef ENABLE_VERIFICATION +#define COMMAND_READNVMDATA (0x8B) +#endif +#define COMMAND_ERASENVM (0x8C) +#define COMMAND_WRITENVMDATA (0x8D) +#define REQCODE_CONTROL_STATUS (0x0000) +#define FG_CMD_READ_FAILED (0x10000) +#define BIT_SS (0x2000) + +#define FW_VER_CMD (0x0002) +#define HW_VER_CMD (0x0003) +#define PARAM_VER_CMD (0x000C) +#define BATTERY_ID_CMD (0x0008) + +enum PARTITION_INDEX { + PARTITION_PROGRAM = 0, + PARTITION_PARAMETER, + PARTITION_MAX +}; + +enum UPDATE_INDEX { + UPDATE_PROGRAM = 0, + UPDATE_PARAMETER, + UPDATE_ALL, + UPDATE_NONE +}; + +static unsigned char chartoBcd(char iChar) +{ + unsigned char mBCD = 0; + + if (iChar >= '0' && iChar <= '9') + mBCD = iChar - '0'; + else if (iChar >= 'A' && iChar <= 'F') + mBCD = iChar - 'A' + 0x0A; + else if (iChar >= 'a' && iChar <= 'f') + mBCD = iChar - 'a' + 0x0a; + + return mBCD; +} + +static int tohex(char *pbuf, unsigned char length) +{ + int i = 0; + int value = 0; + + if (length >4) + return -EINVAL; + + for(i = 0; i < length; i++) + { + value += (chartoBcd(pbuf[i]) << (4*(length -1-i))); + } + + return value; +} +/** + * parse a FG firmware / parameter file + * arguments: + * hexfile_path - file path of FGFW/parameter + * + * return value: + * pointer of jagged arrays + * or NULL if it fails + * jagged arrays details are below. + * [0] - pointer of PROGRAM data array + * or NULL if the specfied file does not contain any PROGRAM data. + * [1] - pointer of PARAMETER data array + * or NULL if the specfied file does not contain any PARAMETER data. + */ +static unsigned char *parse_hexfile(char *hexfile_name, struct mm8xxx_device_info *di) +{ + unsigned char *result = NULL; + char *fbuf = NULL; + char *pbuf = NULL; + char *ebuf = NULL; + int i,j; + unsigned int bcnt; + unsigned int addr; + unsigned int rtype; + unsigned int csum; + unsigned int eladdr; + int offset; + int cont; + int tmp; + int ptsize; + int size = 0; + int ret; + const struct firmware *fw; + + ret = request_firmware(&fw, hexfile_name, di->dev); + if (ret || fw->size <=0 ) { + mm_info("Couldn't get firmware rc=%d\n", ret); + goto FAILED; + } + size = fw->size; + fbuf = kzalloc(size+1, GFP_KERNEL); + memset(fbuf, 0, size+1); + memcpy(fbuf, fw->data, size); + fbuf[size] = '\0'; + j = 0; + for (i = 0; i < size; i++) { + if (iscntrl(fbuf[i]) || (fbuf[i] == ' ')) { + continue; + } + fbuf[j++] = fbuf[i]; + } + + ebuf = fbuf + j; + *ebuf = '\0'; + cont = 1; + pbuf = fbuf; + eladdr = 0; + do { + if ((ebuf - pbuf) < 11) + goto FAILED; + if ((*pbuf) != ':') + goto FAILED; + pbuf++; + /* Byte count */ + if ((tmp = tohex(pbuf, 2)) < 0) + goto FAILED; + bcnt = tmp; + pbuf += 2; + + if ((ebuf - pbuf) < (8 + bcnt * 2)) + goto FAILED; + csum = bcnt; + /* Address */ + if ((tmp = tohex(pbuf, 4)) < 0) + goto FAILED; + addr = tmp; + pbuf += 4; + + csum = (csum + ((addr & 0xFF00) >> 8) + (addr & 0x00FF)) & 0xFF; + /* Record type */ + if ((tmp = tohex(pbuf, 2)) < 0) + goto FAILED; + rtype = tmp; + pbuf += 2; + + csum = (csum + rtype) & 0xFF; + /* Data */ + switch (rtype) + { + case 0: + /* Data */ + offset = addr + eladdr; + if ((offset >= ADDRESS_PROGRAM) && + ((offset + bcnt) <= (ADDRESS_PROGRAM + SIZE_PROGRAM))) { + offset = offset - ADDRESS_PROGRAM; + ptsize = SIZE_PROGRAM; + } else if ((offset >= ADDRESS_PARAMETER) && + ((offset + bcnt) <= (ADDRESS_PARAMETER + SIZE_PARAMETER))) { + offset = offset - ADDRESS_PARAMETER; + ptsize = SIZE_PARAMETER; + } else { + offset = -1; + ptsize = 0; + } + + if (offset >= 0) { + if (!result) { + result = (unsigned char *)kzalloc(4 * ptsize, GFP_KERNEL); + for (i = 0; i < ptsize; i++) { + result[i] = (unsigned char)0xFF; + } + } + } + + for (i = 0; i < bcnt; i++) { + if ((tmp = tohex(pbuf, 2)) < 0) + goto FAILED; + j = tmp; + pbuf += 2; + csum = (csum + j) & 0xFF; + + if ((0 <= offset) && (offset < ptsize)) + result[offset++] = (unsigned char)j; + } + break; + case 1: + /* EOF */ + cont = 0; + break; + case 2: + /* Extended Segment Address */ + if (bcnt != 2) + goto FAILED; + if ((tmp = tohex(pbuf, 4)) < 0) + goto FAILED; + eladdr = tmp; + pbuf += 4; + csum = (csum + ((eladdr & 0xFF00) >> 8) + (eladdr & 0x00FF)) & 0xFF; + eladdr <<= 4; + break; + case 4: + /* Extended Linear Address */ + if (bcnt != 2) + goto FAILED; + if ((tmp = tohex(pbuf, 4)) < 0) + goto FAILED; + eladdr = tmp; + pbuf += 4; + csum = (csum + ((eladdr & 0xFF00) >> 8) + (eladdr & 0x00FF)) & 0xFF; + eladdr <<= 16; + break; + case 3: + case 5: + /* skip: Start Segment Address */ + /* skip: Start Linear Address */ + for (i = 0; i < bcnt; i++) { + if ((tmp = tohex(pbuf, 2)) < 0) + goto FAILED; + j = tmp; + pbuf += 2; + csum = (csum + j) & 0xFF; + } + break; + default: + goto FAILED; + } + + /* Checksum */ + if ((tmp = tohex(pbuf, 2)) < 0) + goto FAILED; + j = tmp; + pbuf += 2; + if ((csum + j) & 0xFF) + goto FAILED; + } while (cont); + + kfree(fbuf); + + return result; +FAILED: + if (fbuf) { + kfree(fbuf); + fbuf = NULL; + } + if (result) { + kfree(result); + result = NULL; + } + return NULL; +} + +static int fg_read_control_status(struct mm8xxx_device_info *di) +{ + if (mm8xxx_battery_write(di, COMMAND_CONTROL,REQCODE_CONTROL_STATUS) < 0) + return -EINVAL; + + return mm8xxx_battery_read(di, COMMAND_CONTROL); +} + +/* Additional library functions */ +/** + * set FG to UNSEAL mode + * + * arguments: + * fd - file descriptor of an i2c device + * s2us_code - Seal to Unseal code + * + * return value: + * 0 if it successes + * or 1 if it fails. + * + */ +static int unseal_request(struct mm8xxx_device_info *di, unsigned int s2us_code) +{ + int ret; + /* Check CONTROL_STATUS */ + ret=fg_read_control_status(di); + if ( ret < 0 ) { + return -EINVAL; + } + + if (ret & BIT_SS) { + if ((mm8xxx_battery_write(di, COMMAND_CONTROL, s2us_code & 0xFFFF) < 0) || + (mm8xxx_battery_write(di, COMMAND_CONTROL, (s2us_code >> 16) & 0xFFFF) <0)) { + return -EINVAL; + } + + if ((ret = fg_read_control_status(di)) < 0) { + return -EINVAL; + } + if (ret & BIT_SS) { + /* failed to set to UNSEALED mode. */ + return -EINVAL; + } + } + + return 0; +} + +static int Lock_release_request(struct mm8xxx_device_info *di) +{ + return mm8xxx_battery_write(di, COMMAND_FGCONDITION, 0x0340); +} + +static int NVM_write_mode(struct mm8xxx_device_info *di) +{ + if (mm8xxx_battery_write(di, COMMAND_FGCONDITION, 0x00A0) < 0) + return -EINVAL; + + mdelay(100); + + if (mm8xxx_battery_write_Nbyte(di, COMMAND_MODECONTROL, 0x88, 1) < 0) + return -EINVAL; + + return 0; +} + +static int erase_param_nvm(struct mm8xxx_device_info *di) +{ + + if ( (mm8xxx_battery_write(di, 0x00, 0x2E03) < 0) || + (mm8xxx_battery_write(di, 0x02, 0x0000) < 0) || + (mm8xxx_battery_write(di, 0x04, 0xDE83) < 0) || + (mm8xxx_battery_write(di, 0x64, 0x0192) < 0)) { + return -EINVAL; + } + mdelay(100); + + if ( (mm8xxx_battery_write(di, 0x00, 0x2F03) < 0) || + (mm8xxx_battery_write(di, 0x02, 0x0000) < 0) || + (mm8xxx_battery_write(di, 0x04, 0xDE83) < 0) || + (mm8xxx_battery_write(di, 0x64, 0x0193) < 0)) { + return -EINVAL; + } + mdelay(100); + + return 0; +} + +static int erase_program_nvm(struct mm8xxx_device_info *di) +{ + if (mm8xxx_battery_write_Nbyte(di, COMMAND_ERASENVM, 0x80, 1) < 0) + return -EINVAL; + mdelay(2000); + + return 0; +} + +static int write_nvm(struct mm8xxx_device_info *di, enum PARTITION_INDEX ptindex, unsigned char *data) +{ + unsigned int offset; + unsigned int size; + int i; +#ifdef ENABLE_VERIFICATION + int j; + int addr; + unsigned char rbuf[SIZE_READBUFFER]; +#endif + + if (!data) { + /* specified pointer is NULL */ + return -EINVAL; + } + + switch (ptindex) { + case PARTITION_PROGRAM: + offset = ADDRESS_PROGRAM; + size = SIZE_PROGRAM; + break; + + case PARTITION_PARAMETER: + offset = ADDRESS_PARAMETER; + size = SIZE_PARAMETER; + break; + + default: + /* unexpected partition index */ + return -EINVAL; + } + + for (i = 0; i < size; i += 4) { + if (mm8xxx_battery_write_4byteCmd(di, offset + i, + data[i] | (data[i + 1] << 8) | (data[i + 2] << 16) | (data[i + 3] << 24)) < 0) { + mm_info("%s, mm8xxx_battery_write_4byteCmd fail\n", __func__); + return -EINVAL; + } + } + +#ifdef ENABLE_VERIFICATION + for (i = 0; i < size; i += SIZE_READBUFFER) { + addr = offset + i; + + if ((mm8xxx_battery_write_Nbyte(di, COMMAND_READNVMDATA, addr, 4) < 0) || + (mm8xxx_battery_read_Nbyte(di, COMMAND_READNVMDATA, rbuf, SIZE_READBUFFER) < 0)) { + mm_info("%s, verification fail\n", __func__); + return -EINVAL; + } + + for (j = 0; j < SIZE_READBUFFER; j++) { + if (data[i + j] != rbuf[j]) { + mm_info("%s, verification fail\n", __func__); + return -EINVAL; + } + } + } +#endif + + return 0; +} + +static int Write_program_data(struct mm8xxx_device_info *di, unsigned char *array) +{ + return write_nvm(di, PARTITION_PROGRAM, array); +} +static int Write_parameter_data(struct mm8xxx_device_info *di, unsigned char *array) +{ + return write_nvm(di, PARTITION_PARAMETER, array); +} + +static int write_program_and_parameter(struct mm8xxx_device_info *di, + unsigned char *program_array, + unsigned char *parameter_array) +{ + int ret = -EINVAL; + + /* Unseal Request */ + mm_info("Requesting set to Unseal mode ... "); + if (unseal_request(di, 0x56781234) < 0) { + mm_info("unseal_request failed\n"); + goto EXIT; + } + + /* Lock Release Request */ + mm_info("Requesting lock releasing ... "); + if (Lock_release_request(di) < 0) { + mm_info("Lock_release_request failed\n"); + goto EXIT; + } + + /* NVM Write mode Request */ + mm_info("Requesting set to NVM Write mode ... "); + if (NVM_write_mode(di) < 0) { + mm_info("Requesting set to NVM Write mode failed\n"); + goto EXIT; + } + + /* Program Erase And wait 2 second */ + if (program_array) { + mm_info("Erasing 'PROGRAM' partition ... "); + if(erase_program_nvm(di) < 0) { + mm_info("Erasing 'PROGRAM' failed\n"); + goto EXIT; + } + } + + /* Parameter Erase And wait 2 second */ + if (parameter_array) { + mm_info("Erasing 'PARAMETER' partition ... "); + if(erase_param_nvm(di) < 0) { + mm_info("Erasing 'PARAMETER' failed\n"); + goto EXIT; + } + } + + /* + * Initialization of variables + * Copy the data of the target from the hex file + * Write Command 0x8D and Data + * Increment the write size + * 16384byte / 4 = 4096Loop + */ + if (program_array) { + mm_info("Writing 'PROGRAM' partition ... "); + if(Write_program_data(di, program_array) < 0) { + mm_info("Writing 'PROGRAM' failed\n"); + goto EXIT; + } + } + + /* + * Initialization of variables + * Copy the data of the target from the hex file + * Write Command 0x8D and Data + * Increment the write size + * 960byte / 4 = 250Loop + */ + if (parameter_array) { + mm_info("Writing 'PARAMETER' partition ... "); + if(Write_parameter_data(di, parameter_array) < 0) { + mm_info("Writing 'PARAMETER' failed\n"); + goto EXIT; + } + } + + /* System Reset Request */ + mm_info("Requesting system resetting ... "); + if (mm8xxx_battery_write_Nbyte(di, COMMAND_MODECONTROL, 0x80, 1) < 0) { + mm_info("Reset fg system failed\n"); + goto EXIT; + } + mdelay(100); + ret = 0; + +EXIT: + return ret; +} + +static const char *get_battery_serialnumber(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + const char *battsn_buf; + int retval; + + battsn_buf = NULL; + + if (np) + retval = of_property_read_string(np, "mmi,battid", + &battsn_buf); + else + return NULL; + + if ((retval == -EINVAL) || !battsn_buf) { + mm_info(" Battsn unused\n"); + of_node_put(np); + return NULL; + + } else + mm_info("Battsn = %s\n", battsn_buf); + + of_node_put(np); + + return battsn_buf; +} + +static u32 seach_batt_id(void) +{ + struct device_node *np = di->dev->of_node; + const char *dev_sn = NULL; + const char *first_batt_sn = NULL; + const char *second_batt_sn = NULL; + u32 battery_id = di->first_battery_id;; + + dev_sn = get_battery_serialnumber(); + if (dev_sn != NULL) { + of_property_read_string(np, "first_batt_sn", + &first_batt_sn); + of_property_read_string(np, "second_batt_sn", + &second_batt_sn); + if (first_batt_sn != NULL && second_batt_sn != NULL) + { + if (strnstr(dev_sn, first_batt_sn, 10)) + battery_id = di->first_battery_id; + else if (strnstr(dev_sn, second_batt_sn, 10)) + battery_id = di->second_battery_id; + } + } + + return battery_id; +} + +static int mm8xxx_battery_update_program_and_parameter(struct mm8xxx_device_info *di, enum UPDATE_INDEX update_index) +{ + unsigned char *fw_hexfile_data = NULL; + unsigned char *param_hexfile_data = NULL; + u32 battery_id; + int i; + char param_name[30] = {0}; + + if (update_index != UPDATE_PROGRAM){ + battery_id = mmi_get_battery_info(di, BATTERY_ID_CMD); + if (battery_id !=0) + { + if (battery_id != di->first_battery_id && battery_id != di->second_battery_id){ + mm_info("Error: the battery is not suitable for this poject.\n"); + goto EXIT; + } + } + else { + battery_id = seach_batt_id(); + mm_info("Error getting battery_id , try to get batt sn via utag info and get the correct batt_id=%04x\n", battery_id); + } + + sprintf(param_name,"%s%04x%s", "mm8013c_parameter_", battery_id, ".hex"); + mm_info("battery parameter name=%s\n", param_name); + } + + switch (update_index ) + { + case UPDATE_PROGRAM: + mm_info("Parsing the FW_HEX file ... "); + if (!(fw_hexfile_data = parse_hexfile("mm8013c_fw.hex", di))) { + mm_info("Parse fw hex data failed\n"); + goto EXIT; + } + break; + case UPDATE_PARAMETER: + mm_info("Parsing the parameter_HEX file ... "); + if (!(param_hexfile_data = parse_hexfile(param_name, di))) { + mm_info("Parse parameter hex date failed\n"); + goto EXIT; + } + break; + case UPDATE_ALL: + mm_info("Parsing the FW_HEX file ... "); + if (!(fw_hexfile_data = parse_hexfile("mm8013c_fw.hex", di))) { + mm_info("failed\n"); + goto EXIT; + } + mm_info("Parsing the parameter_HEX file ... "); + if (!(param_hexfile_data = parse_hexfile(param_name, di))) { + mm_info("failed\n"); + goto EXIT; + } + break; + default: + return -EINVAL; + } + + for (i=1; i<= 3; i++) + { + /* start TRM sequence */ + if (write_program_and_parameter(di, + fw_hexfile_data, param_hexfile_data) < 0) { + mm_info("FAILED TO UPDATE program_and_parameter,fail count=%d\n", i); + } + else { + mm_info("successfully update program_and_parameter !!\n"); + break; + } + } + +EXIT: + if (fw_hexfile_data) { + kfree(fw_hexfile_data); + fw_hexfile_data = NULL; + } + if (param_hexfile_data) { + kfree(param_hexfile_data); + param_hexfile_data = NULL; + } + + return 0; +} + +/********************************************************************/ + +static ssize_t mm8xxx_battery_fw_ver_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%04x\n", mmi_get_battery_info(di, FW_VER_CMD)); +} +static DEVICE_ATTR(fw_ver, S_IRUGO| (S_IWUSR|S_IWGRP), mm8xxx_battery_fw_ver_show, NULL); + +static ssize_t mm8xxx_battery_param_ver_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%04x\n", mmi_get_battery_info(di, PARAM_VER_CMD)); +} +static DEVICE_ATTR(param_ver, S_IRUGO| (S_IWUSR|S_IWGRP), mm8xxx_battery_param_ver_show, NULL); + +static ssize_t mm8xxx_battery_batt_id_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%04x\n", mmi_get_battery_info(di, BATTERY_ID_CMD)); +} +static DEVICE_ATTR(batt_id, S_IRUGO| (S_IWUSR|S_IWGRP), mm8xxx_battery_batt_id_show, NULL); + +static struct attribute *mm8xxx_attributes[] = { + &dev_attr_fw_ver.attr, + &dev_attr_param_ver.attr, + &dev_attr_batt_id.attr, + NULL, +}; + +static struct attribute_group mm8xxx_attribute_group = { + .attrs = mm8xxx_attributes, +}; + +static void mm8xxx_battery_update(struct mm8xxx_device_info *di); +static irqreturn_t mm8xxx_battery_irq_handler_thread(int irq, void *data) +{ + struct mm8xxx_device_info *di = data; + mm8xxx_battery_update(di); + return IRQ_HANDLED; +} + +static int mm8xxx_battery_read(struct mm8xxx_device_info *di, u8 cmd) +{ + struct i2c_client *client = to_i2c_client(di->dev); + struct i2c_msg msg[2]; + u8 data[2]; + int ret; + + if (!client->adapter) + return -ENODEV; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].buf = &cmd; + msg[0].len = sizeof(cmd); + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = data; + msg[1].len = 2; + + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) + return ret; + + ret = get_unaligned_le16(data); + + return ret; +} +#ifdef ENABLE_VERIFICATION +static int mm8xxx_battery_read_Nbyte(struct mm8xxx_device_info *di, u8 cmd, unsigned char *data, unsigned char length) +{ + struct i2c_client *client = to_i2c_client(di->dev); + struct i2c_msg msg[2]; + int ret; +//mm_info("%s, begin\n", __func__); + if (!client->adapter) + return -ENODEV; + + msg[0].addr = client->addr; + msg[0].flags = 0; + msg[0].buf = &cmd; + msg[0].len = sizeof(cmd); + msg[1].addr = client->addr; + msg[1].flags = I2C_M_RD; + msg[1].buf = data; + msg[1].len = length; + + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) + return ret; +//mm_info("%s, end\n", __func__); + return ret; +} +#endif + +static int mm8xxx_battery_write(struct mm8xxx_device_info *di, u8 cmd, + int value) +{ + struct i2c_client *client = to_i2c_client(di->dev); + struct i2c_msg msg; + u8 data[4]; + int ret; + + if (!client->adapter) + return -ENODEV; + + data[0] = cmd; + put_unaligned_le16(value, &data[1]); + msg.len = 3; + + msg.buf = data; + msg.addr = client->addr; + msg.flags = 0; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) + return ret; + else if (ret != 1) + return -EINVAL; + + return 0; +} + +static int mm8xxx_battery_write_Nbyte(struct mm8xxx_device_info *di, u8 cmd, + unsigned int value, int byte_num) +{ + struct i2c_client *client = to_i2c_client(di->dev); + struct i2c_msg msg; + u8 data[16]; + int ret,i=0; + + if (byte_num > 15) + return -ENODEV; + if (!client->adapter) + return -ENODEV; + + data[0] = cmd; + for (i=0; i < byte_num; i++) + { + data[i+1] = (value >> (8 * i)) & 0xFF; + } + + msg.len = byte_num+1; + + msg.buf = data; + msg.addr = client->addr; + msg.flags = 0; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) + return ret; + else if (ret != 1) + return -EINVAL; + + return 0; +} + +static int mm8xxx_battery_write_4byteCmd(struct mm8xxx_device_info *di, unsigned int cmd, unsigned int value) +{ + struct i2c_client *client = to_i2c_client(di->dev); + struct i2c_msg msg; + u8 data[9]; + int ret; + + if (!client->adapter) + return -ENODEV; + + data[0] = COMMAND_WRITENVMDATA; + data[1] = cmd & 0xFF; + data[2] = (cmd >> 8) & 0xFF; + data[3] = (cmd >> 16) & 0xFF; + data[4] = (cmd >> 24) & 0xFF; + + data[5] = value & 0xFF; + data[6] = (value >> 8) & 0xFF; + data[7] = (value >> 16) & 0xFF; + data[8] = (value >> 24) & 0xFF; + + msg.len = 9; + msg.buf = data; + msg.addr = client->addr; + msg.flags = 0; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) + return ret; + else if (ret != 1) + return -EINVAL; + + return 0; +} + +/* MM8XXX Flags */ +#define MM8XXX_FLAG_DSG BIT(0) +#define MM8XXX_FLAG_SOCF BIT(1) +#define MM8XXX_FLAG_SOC1 BIT(2) +#define MM8XXX_FLAG_UT BIT(3) +#define MM8XXX_FLAG_OT BIT(4) +#define MM8XXX_FLAG_ODC BIT(5) +#define MM8XXX_FLAG_OCC BIT(6) +#define MM8XXX_FLAG_OCVTAKEN BIT(7) +#define MM8XXX_FLAG_CHG BIT(8) +#define MM8XXX_FLAG_FC BIT(9) +#define MM8XXX_FLAG_CHGINH BIT(11) +#define MM8XXX_FLAG_BATLOW BIT(12) +#define MM8XXX_FLAG_BATHI BIT(13) +#define MM8XXX_FLAG_OTD BIT(14) +#define MM8XXX_FLAG_OTC BIT(15) + +#define INVALID_COMMAND 0xff + +enum mm8xxx_cmd_index { + MM8XXX_CMD_CONTROL = 0, + MM8XXX_CMD_TEMPERATURE, + MM8XXX_CMD_VOLTAGE, + MM8XXX_CMD_FLAGS, + MM8XXX_CMD_REMAININGCAPACITY, + MM8XXX_CMD_FULLCHARGECAPACITY, + MM8XXX_CMD_AVERAGECURRENT, + MM8XXX_CMD_AVERAGETIMETOEMPTY, + MM8XXX_CMD_CHARGETYPE, + MM8XXX_CMD_CYCLECOUNT, + MM8XXX_CMD_STATEOFCHARGE, + MM8XXX_CMD_CHARGEVOLTAGE, + MM8XXX_CMD_DESIGNCAPACITY, + MM8XXX_CMD_ELAPSEDTIMEM, + MM8XXX_CMD_ELAPSEDTIMED, + MM8XXX_CMD_ELAPSEDTIMEH, + + MM8XXX_CMD_EOI, +}; + +static u8 + mm8118g01_cmds[MM8XXX_CMD_EOI] = { + [MM8XXX_CMD_CONTROL] = 0x00, + [MM8XXX_CMD_TEMPERATURE] = 0x06, + [MM8XXX_CMD_VOLTAGE] = 0x08, + [MM8XXX_CMD_FLAGS] = 0x0A, + [MM8XXX_CMD_REMAININGCAPACITY] = 0x10, + [MM8XXX_CMD_FULLCHARGECAPACITY] = 0x12, + [MM8XXX_CMD_AVERAGECURRENT] = 0x14, + [MM8XXX_CMD_AVERAGETIMETOEMPTY] = 0x16, + [MM8XXX_CMD_CYCLECOUNT] = 0x2A, + [MM8XXX_CMD_STATEOFCHARGE] = 0x2C, + [MM8XXX_CMD_CHARGEVOLTAGE] = INVALID_COMMAND, + [MM8XXX_CMD_DESIGNCAPACITY] = 0x3C, + [MM8XXX_CMD_ELAPSEDTIMEM] = INVALID_COMMAND, + [MM8XXX_CMD_ELAPSEDTIMED] = INVALID_COMMAND, + [MM8XXX_CMD_ELAPSEDTIMEH] = INVALID_COMMAND, + }, + mm8118w02_cmds[MM8XXX_CMD_EOI] = { + [MM8XXX_CMD_CONTROL] = 0x00, + [MM8XXX_CMD_TEMPERATURE] = 0x06, + [MM8XXX_CMD_VOLTAGE] = 0x08, + [MM8XXX_CMD_FLAGS] = 0x0A, + [MM8XXX_CMD_REMAININGCAPACITY] = 0x10, + [MM8XXX_CMD_FULLCHARGECAPACITY] = 0x12, + [MM8XXX_CMD_AVERAGECURRENT] = 0x14, + [MM8XXX_CMD_AVERAGETIMETOEMPTY] = 0x16, + [MM8XXX_CMD_CYCLECOUNT] = 0x2A, + [MM8XXX_CMD_STATEOFCHARGE] = 0x2C, + [MM8XXX_CMD_CHARGEVOLTAGE] = INVALID_COMMAND, + [MM8XXX_CMD_DESIGNCAPACITY] = 0x3C, + [MM8XXX_CMD_ELAPSEDTIMEM] = INVALID_COMMAND, + [MM8XXX_CMD_ELAPSEDTIMED] = INVALID_COMMAND, + [MM8XXX_CMD_ELAPSEDTIMEH] = INVALID_COMMAND, + }, + mm8013c10_cmds[MM8XXX_CMD_EOI] = { + [MM8XXX_CMD_CONTROL] = 0x00, + [MM8XXX_CMD_TEMPERATURE] = 0x06, + [MM8XXX_CMD_VOLTAGE] = 0x08, + [MM8XXX_CMD_FLAGS] = 0x0A, + [MM8XXX_CMD_REMAININGCAPACITY] = 0x10, + [MM8XXX_CMD_FULLCHARGECAPACITY] = 0x12, + [MM8XXX_CMD_AVERAGECURRENT] = 0x14, + [MM8XXX_CMD_AVERAGETIMETOEMPTY] = 0x16, + [MM8XXX_CMD_CHARGETYPE] = 0x20, + [MM8XXX_CMD_CYCLECOUNT] = 0x2A, + [MM8XXX_CMD_STATEOFCHARGE] = 0x2C, + [MM8XXX_CMD_CHARGEVOLTAGE] = 0x30, + [MM8XXX_CMD_DESIGNCAPACITY] = 0x3C, + [MM8XXX_CMD_ELAPSEDTIMEM] = 0x74, + [MM8XXX_CMD_ELAPSEDTIMED] = 0x76, + [MM8XXX_CMD_ELAPSEDTIMEH] = 0x78, + }; + +static enum power_supply_property mm8118g01_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_POWER_AVG, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_MANUFACTURER, +}; +#define mm8118w02_props mm8118g01_props + +static enum power_supply_property mm8013c10_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_TYPE, + POWER_SUPPLY_PROP_MANUFACTURER, +}; + +#define MM8XXX_DATA(ref) { \ + .cmds = ref##_cmds, \ + .props = ref##_props, \ + .props_size = ARRAY_SIZE(ref##_props) } + +static struct { + u8 *cmds; + enum power_supply_property *props; + size_t props_size; +} mm8xxx_chip_data[] = { + [MM8118G01] = MM8XXX_DATA(mm8118g01), + [MM8118W02] = MM8XXX_DATA(mm8118w02), + [MM8013C10] = MM8XXX_DATA(mm8013c10), +}; + +static DEFINE_MUTEX(mm8xxx_list_lock); +static LIST_HEAD(mm8xxx_battery_devices); + +#define MM8XXX_MSLEEP(i) usleep_range((i)*1000, (i)*1000+500) + +static int poll_interval_param_set(const char *val, + const struct kernel_param *kp) +{ + struct mm8xxx_device_info *di; + unsigned int prev_val = *(unsigned int *) kp->arg; + int ret; + + ret = param_set_uint(val, kp); + if ((ret < 0) || (prev_val == *(unsigned int *) kp->arg)) + return ret; + + mutex_lock(&mm8xxx_list_lock); + list_for_each_entry(di, &mm8xxx_battery_devices, list) { + cancel_delayed_work_sync(&di->work); + schedule_delayed_work(&di->work, 0); + } + mutex_unlock(&mm8xxx_list_lock); + + return ret; +} + +static const struct kernel_param_ops param_ops_poll_interval = { + .get = param_get_uint, + .set = poll_interval_param_set, +}; + +static unsigned int poll_interval = 360; +module_param_cb(poll_interval, ¶m_ops_poll_interval, &poll_interval, 0644); +MODULE_PARM_DESC(poll_interval, + "battery poll interval in seconds - 0 disables polling"); + +static inline int mm8xxx_read(struct mm8xxx_device_info *di, int cmd_index) +{ + int ret; + + if ((!di) || (di->cmds[cmd_index] == INVALID_COMMAND)) + return -EINVAL; + + if (!di->bus.read) + return -EPERM; + + ret = di->bus.read(di, di->cmds[cmd_index]); + if (ret < 0) + dev_dbg(di->dev, "failed to read command 0x%02x (index %d)\n", + di->cmds[cmd_index], cmd_index); + + return ret; +} + +static inline int mm8xxx_write(struct mm8xxx_device_info *di, int cmd_index, + u16 value) +{ + int ret; + + if ((!di) || (di->cmds[cmd_index] == INVALID_COMMAND)) + return -EINVAL; + + if (!di->bus.write) + return -EPERM; + + ret = di->bus.write(di, di->cmds[cmd_index], value); + if (ret < 0) + dev_dbg(di->dev, "failed to write command 0x%02x (index %d)\n", + di->cmds[cmd_index], cmd_index); + + return ret; +} + + static int mm8xxx_battery_temp(struct mm8xxx_device_info *di, int *temp) +{ + return iio_read_channel_processed(di->Batt_NTC_channel, temp); +} + +static int mm8xxx_battery_temp_to_FG(struct mm8xxx_device_info *di) +{ + static int pre_temp=250; + int temp = 0; + int ret = 0; + + ret = mm8xxx_battery_temp(di, &temp); + if (ret < 0) { + dev_err(di->dev, "error mm8xxx_battery_temp \n"); + return ret; + } + temp = temp / 100; + + if(temp < 0) { + temp = 65536 - temp; + } + + if ( pre_temp != temp) + { + pre_temp = temp; + ret =mm8xxx_write(di, MM8XXX_CMD_TEMPERATURE, (u16)temp); + if (ret < 0) { + dev_err(di->dev, "error writing temperature to fg \n"); + } + } + + return ret; +} + +static int mm8xxx_battery_chargeType_to_FG(struct mm8xxx_device_info *di, int type) +{ + int ret = 0; + + ret =mm8xxx_write(di, MM8XXX_CMD_CHARGETYPE, (u16)type); + if (ret < 0) { + dev_err(di->dev, "error writing chargeType to fg\n"); + } + + return ret; +} + +/* + * Return the battery State-of-Charge + * Or < 0 if somthing fails. + */ +static int mm8xxx_battery_read_stateofcharge(struct mm8xxx_device_info *di) +{ + int soc; + + soc = mm8xxx_read(di, MM8XXX_CMD_STATEOFCHARGE); + + if (soc < 0) + dev_dbg(di->dev, "error reading State-of-Charge\n"); + + return soc; +} + +/* + * Return the battery Remaining Capacity in μAh + * Or < 0 if something fails. + */ +static int mm8xxx_battery_read_remainingcapacity(struct mm8xxx_device_info *di) +{ + int rc; + + rc = mm8xxx_read(di, MM8XXX_CMD_REMAININGCAPACITY); + if (rc < 0) + dev_dbg(di->dev, "error reading Remaining Capacity\n"); + + rc *= 1000; + + return rc; +} + +/* + * Return the battery Full Charge Capacity in μAh + * Or < 0 if something fails. + */ +static int mm8xxx_battery_read_fullchargecapacity(struct mm8xxx_device_info *di) +{ + int fcc; + + fcc = mm8xxx_read(di, MM8XXX_CMD_FULLCHARGECAPACITY); + if (fcc < 0) + dev_dbg(di->dev, "error reading Full Charge Capacity\n"); + + fcc *= 1000; + + return fcc; +} + +/* + * Return the battery Design Capacity in μAh + * Or < 0 if something fails. + */ +static int mm8xxx_battery_read_designcapacity(struct mm8xxx_device_info *di) +{ + int dc; + + dc = mm8xxx_read(di, MM8XXX_CMD_DESIGNCAPACITY); + if (dc < 0) + dev_dbg(di->dev, "error reading Design Capacity\n"); + + dc *= 1000; + + return dc; +} + +/* + * Return the battery temperature in tenths of degree Kelvin + * Or < 0 if something fails. + */ +static int mm8xxx_battery_read_temperature(struct mm8xxx_device_info *di) +{ + int temp; + + temp = mm8xxx_read(di, MM8XXX_CMD_TEMPERATURE); + if (temp < 0) + dev_err(di->dev, "error reading temperature\n"); + + return temp; +} + +/* + * Return the battery Cycle Count + * Or < 0 if something fails. + */ +static int mm8xxx_battery_read_cyclecount(struct mm8xxx_device_info *di) +{ + int cc; + + cc = mm8xxx_read(di, MM8XXX_CMD_CYCLECOUNT); + if (cc < 0) + dev_err(di->dev, "error reading cycle count\n"); + + return cc; +} + +/* + * Return the battery usable time + * Or < 0 if something fails. + */ +static int mm8xxx_battery_read_averagetimetoempty(struct mm8xxx_device_info *di) +{ + int atte; + + atte = mm8xxx_read(di, MM8XXX_CMD_AVERAGETIMETOEMPTY); + if (atte < 0) { + dev_dbg(di->dev, "error reading average time to empty\n"); + return atte; + } + + if (atte == 65535) + return -ENODATA; + + return atte * 60; +} + +/* + * Returns true if a battery over temperature condition is detected. + */ +static inline bool mm8xxx_battery_overtemperature(u16 flags) +{ + return flags & MM8XXX_FLAG_OT; +} + +/* + * Returns true if a battery under temperature condition is detected. + */ +static inline bool mm8xxx_battery_undertemperature(u16 flags) +{ + return flags & MM8XXX_FLAG_UT; +} + +/* + * Returns true if a low state of charge condition is detected. + */ +static inline bool mm8xxx_battery_dead(u16 flags) +{ + return flags & MM8XXX_FLAG_SOCF; +} + +/* + * Returns POWER_SUPPLY_HEALTH_(OVERHEAT/COLD/DEAD) if a battery is in abnormal + * condition, POWER_SUPPLY_HEALTH_GOOD otherwise. + */ +static int mm8xxx_battery_read_health(struct mm8xxx_device_info *di) +{ + if (unlikely(mm8xxx_battery_overtemperature(di->cache.flags))) + return POWER_SUPPLY_HEALTH_OVERHEAT; + if (unlikely(mm8xxx_battery_undertemperature(di->cache.flags))) + return POWER_SUPPLY_HEALTH_COLD; + if (unlikely(mm8xxx_battery_dead(di->cache.flags))) + return POWER_SUPPLY_HEALTH_DEAD; + + return POWER_SUPPLY_HEALTH_GOOD; +} + +#ifdef CONFIG_BATTERY_MM8XXX_DYNAMIC_CHARGE_VOLTAGE +/* + * Return the battery Charge Voltage + * Or < 0 if something fails. + */ +static int mm8xxx_battery_read_chargevoltage(struct mm8xxx_device_info *di) +{ + int cv; + + cv = mm8xxx_read(di, MM8XXX_CMD_CHARGEVOLTAGE); + if (cv < 0) + dev_err(di->dev, "error reading charge voltage\n"); + + return cv; +} + +/* + * Write the battery Charge Voltage. + */ +static int mm8xxx_battery_write_chargevoltage(struct mm8xxx_device_info *di, + u16 cv) +{ + int ret; + + ret = mm8xxx_write(di, MM8XXX_CMD_CHARGEVOLTAGE, cv); + if (ret < 0) { + dev_err(di->dev, "error writing charge voltage\n"); + return ret; + } + + return 0; +} +#endif + +/* + * Return the battery usage time in month + * Or < 0 if something fails. + */ +static int mm8xxx_battery_read_elapsedmonths(struct mm8xxx_device_info *di) +{ + int elapsed; + + elapsed = mm8xxx_read(di, MM8XXX_CMD_ELAPSEDTIMEM); + if (elapsed < 0) + dev_err(di->dev, "error reading elapsed months\n"); + + return elapsed; +} + +/* + * Return the battery usage time in day + * Or < 0 if something fails. + */ +static int mm8xxx_battery_read_elapseddays(struct mm8xxx_device_info *di) +{ + int elapsed; + + elapsed = mm8xxx_read(di, MM8XXX_CMD_ELAPSEDTIMED); + if (elapsed < 0) + dev_err(di->dev, "error reading elapsed days\n"); + + return elapsed; +} + +/* + * Return the battery usage time in hour + * Or < 0 if something fails. + */ +static int mm8xxx_battery_read_elapsedhours(struct mm8xxx_device_info *di) +{ + int elapsed; + + elapsed = mm8xxx_read(di, MM8XXX_CMD_ELAPSEDTIMEH); + if (elapsed < 0) + dev_err(di->dev, "error reading elapsed hours\n"); + + return elapsed; +} + +static bool is_input_present(struct mm8xxx_device_info *di) +{ + int rc = 0, input_present = 0; + union power_supply_propval pval = {0, }; + + if (!di->usb_psy) + di->usb_psy = power_supply_get_by_name("usb"); + if (di->usb_psy) { + rc = power_supply_get_property(di->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &pval); + if (rc < 0) + pr_err("Couldn't read USB Present status, rc=%d\n", rc); + else + input_present |= pval.intval; + } + + if (!di->dc_psy) + di->dc_psy = power_supply_get_by_name("dc"); + if (di->dc_psy) { + rc = power_supply_get_property(di->dc_psy, + POWER_SUPPLY_PROP_PRESENT, &pval); + if (rc < 0) + pr_err("Couldn't read DC Present status, rc=%d\n", rc); + else + input_present |= pval.intval; + } + pr_info("input present = %d\n", input_present); + if (input_present) + return true; + + return false; +} + +#define CAP(min, max, value) \ + ((min > value) ? min : ((value > max) ? max : value)) + +static void mm8xxx_battery_update(struct mm8xxx_device_info *di) +{ + struct mm8xxx_state_cache cache = {0, }; + bool input_present = is_input_present(di); +#ifdef CONFIG_BATTERY_MM8XXX_DYNAMIC_CHARGE_VOLTAGE + int cv; + int req = 0; +#endif + + /* get battery power supply */ + if (!di->batt_psy) { + di->batt_psy = power_supply_get_by_name("battery"); + if (!di->batt_psy) + mm_info("get batt_psy fail\n"); + } + + cache.flags = mm8xxx_read(di, MM8XXX_CMD_FLAGS); + if ((cache.flags & 0xFFFF) == 0xFFFF) + cache.flags = -1; + if (cache.flags < 0) + goto out; + + cache.temperature = mm8xxx_battery_read_temperature(di); + cache.avg_time_to_empty = mm8xxx_battery_read_averagetimetoempty(di); + cache.soc = mm8xxx_battery_read_stateofcharge(di); + cache.full_charge_capacity = mm8xxx_battery_read_fullchargecapacity(di); + di->cache.flags = cache.flags; + cache.health = mm8xxx_battery_read_health(di); + cache.cycle_count = mm8xxx_battery_read_cyclecount(di); + mm8xxx_battery_temp_to_FG(di); + + mm_info("soc = %d, ui_soc = %d\n", cache.soc, di->cache.soc); + if (di->cache.soc == 0) + di->cache.soc = cache.soc; + + if (cache.soc > di->cache.soc) { + /* SOC increased */ + if (input_present) {/* Increment if input is present */ + cache.soc = di->cache.soc + 1; + } else + cache.soc = di->cache.soc; + + } else if (cache.soc < di->cache.soc) { + /* SOC dropped */ + cache.soc = di->cache.soc - 1; + } + cache.soc = CAP(0, 100, cache.soc); + + if (di->charge_design_full <= 0) + di->charge_design_full = mm8xxx_battery_read_designcapacity(di); + + if (di->cmds[MM8XXX_CMD_ELAPSEDTIMEM] != INVALID_COMMAND) + cache.elapsed_months = mm8xxx_battery_read_elapsedmonths(di); + if (di->cmds[MM8XXX_CMD_ELAPSEDTIMED] != INVALID_COMMAND) + cache.elapsed_days = mm8xxx_battery_read_elapseddays(di); + if (di->cmds[MM8XXX_CMD_ELAPSEDTIMEH] != INVALID_COMMAND) + cache.elapsed_hours = mm8xxx_battery_read_elapsedhours(di); + +#ifdef CONFIG_BATTERY_MM8XXX_DYNAMIC_CHARGE_VOLTAGE + if (di->chip != MM8013C10) + goto out; + + cv = mm8xxx_battery_read_chargevoltage(di); + if (cv < 0) + goto out; + + /* + * TODO: Change cycle-counts and voltages according to each devices. + */ + if ((cache.cycle_count < 250) && (cv != 4400)) + req = 4400; + else if ((cache.cycle_count < 500) && (cv != 4350)) + req = 4350; + else if ((cache.cycle_count < 800) && (cv != 4300)) + req = 4300; + else if (cv != 4250) + req = 4250; + + if (req == 0) + goto out; + + mm8xxx_battery_write_chargevoltage(di, cv); +#endif + +out: + if ((di->cache.soc != cache.soc) || + (di->cache.flags != cache.flags)) { + if (di->batt_psy) + power_supply_changed(di->batt_psy); + } + + if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) + di->cache = cache; + + di->last_update = jiffies; +} + +static void mm8xxx_battery_poll(struct work_struct *work) +{ + struct mm8xxx_device_info *di = + container_of(work, struct mm8xxx_device_info, work.work); + + mm8xxx_battery_update(di); + + if (poll_interval > 0) + schedule_delayed_work(&di->work, poll_interval * HZ); +} + +/* + * Gets the battery average current in μA and returns 0 + * Or < 0 if something fails. + */ +static int mm8xxx_battery_current(struct mm8xxx_device_info *di, + union power_supply_propval *val) +{ + int curr; + curr = mm8xxx_read(di, MM8XXX_CMD_AVERAGECURRENT); + if (curr < 0) { + dev_err(di->dev, "error reading current\n"); + return curr; + } + + curr = (int)((s16)curr) * 1000; + val->intval = curr; + + return 0; +} + +static int mm8xxx_battery_status(struct mm8xxx_device_info *di, + union power_supply_propval *val) +{ + int status; + + if (di->cache.flags & MM8XXX_FLAG_CHG) + status = POWER_SUPPLY_STATUS_CHARGING; + else if (di->cache.flags & MM8XXX_FLAG_DSG) + status = POWER_SUPPLY_STATUS_DISCHARGING; + else if (di->cache.flags & MM8XXX_FLAG_FC) + status = POWER_SUPPLY_STATUS_FULL; + else + status = POWER_SUPPLY_STATUS_NOT_CHARGING; + + val->intval = status; + + return 0; +} + +static int mm8xxx_battery_capacity_level(struct mm8xxx_device_info *di, + union power_supply_propval *val) +{ + int level; + + if (di->cache.flags & MM8XXX_FLAG_FC) + level = POWER_SUPPLY_CAPACITY_LEVEL_FULL; + else if (di->cache.flags & MM8XXX_FLAG_SOCF) + level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + else + level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + + val->intval = level; + + return 0; +} + +/* + * Gets the battery Voltage in μV and returns 0 + * Or < 0 if something fails. + */ +static int mm8xxx_battery_voltage(struct mm8xxx_device_info *di, + union power_supply_propval *val) +{ + int voltage; + u32 fg_fw_ver; + u32 fg_param_ver; + static bool reset_fg_flag = false; + + voltage = mm8xxx_read(di, MM8XXX_CMD_VOLTAGE); + if (voltage < 0) { + dev_err(di->dev, "error reading voltage\n"); + return voltage; + } + + val->intval = voltage * 1000; + + if (voltage == 0) { + fg_fw_ver = mmi_get_battery_info(di, FW_VER_CMD); + fg_param_ver = mmi_get_battery_info(di, PARAM_VER_CMD); + mm_info("voltage 0mv, fg_fw_ver=0x%04x, fg_param_ver=0x%04x, reset_fg_flag %d\n", + fg_fw_ver, fg_param_ver, reset_fg_flag); + if ((fg_fw_ver == 0x0024) && (fg_param_ver == 0x0006) && (reset_fg_flag == false)) { + /* System Reset Request */ + mm_info("The FG is in boot mode, Requesting system resetting ... \n"); + if (mm8xxx_battery_write_Nbyte(di, COMMAND_MODECONTROL, 0x80, 1) < 0) { + mm_info("Reset fg system failed\n"); + } + reset_fg_flag = true; + } + } else { + reset_fg_flag = false; + } + + return 0; +} + +static int mm8xxx_simple_value(int value, union power_supply_propval *val) +{ + if (value < 0) + return value; + + val->intval = value; + + return 0; +} + + +static int mm8xxx_battery_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + int ret = 0; + struct mm8xxx_device_info *di = power_supply_get_drvdata(psy); + + if ((psp != POWER_SUPPLY_PROP_PRESENT) && (di->cache.flags < 0)) + return -ENODEV; + + switch (psp) { + case POWER_SUPPLY_PROP_TEMP: + mm8xxx_battery_temp_to_FG(di); + break; + case POWER_SUPPLY_PROP_TYPE: + mm8xxx_battery_chargeType_to_FG(di, val->intval); + break; + default: + return -EINVAL; + } + + return ret; +} +static int mm8xxx_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + struct mm8xxx_device_info *di = power_supply_get_drvdata(psy); + + mutex_lock(&di->lock); + if (time_is_before_jiffies(di->last_update + 5 * HZ)) { + cancel_delayed_work_sync(&di->work); + mm8xxx_battery_poll(&di->work.work); + } + mutex_unlock(&di->lock); + + if ((psp != POWER_SUPPLY_PROP_PRESENT) && (di->cache.flags < 0)) + return -ENODEV; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + ret = mm8xxx_battery_status(di, val); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = mm8xxx_battery_voltage(di, val); + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = di->cache.flags < 0 ? 0 : 1; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = mm8xxx_battery_current(di, val); + val->intval = val->intval * (-1); + break; + case POWER_SUPPLY_PROP_CAPACITY: + ret = mm8xxx_simple_value(di->cache.soc, val); + break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + ret = mm8xxx_battery_capacity_level(di, val); + break; + case POWER_SUPPLY_PROP_TEMP: + mm8xxx_battery_temp(di, &val->intval); + val->intval = val->intval / 100; + break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: + ret = mm8xxx_simple_value(di->cache.avg_time_to_empty, val); + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + ret = mm8xxx_simple_value( + mm8xxx_battery_read_remainingcapacity(di), val); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + ret = mm8xxx_simple_value(di->cache.full_charge_capacity, val); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + ret = mm8xxx_simple_value(di->charge_design_full, val); + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + ret = mm8xxx_simple_value(di->cache.cycle_count, val); + break; + case POWER_SUPPLY_PROP_HEALTH: + ret = mm8xxx_simple_value(di->cache.health, val); + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = MM8XXX_MANUFACTURER; + break; + + /* + * TODO: Implement appropriate POWER_SUPPLY_PROP property and read + * elapsed time. + * + * di->cache.elapsed_months + * di->cache.elapsed_days + * di->cache.elapsed_hours + */ + + default: + return -EINVAL; + } + + return ret; +} + +static void mm8xxx_external_power_changed(struct power_supply *psy) +{ + struct mm8xxx_device_info *di = power_supply_get_drvdata(psy); + + cancel_delayed_work_sync(&di->work); + schedule_delayed_work(&di->work, 0); +} + +static int mm8xxx_battery_setup(struct mm8xxx_device_info *di) +{ + struct power_supply_desc *ps_desc; + struct power_supply_config ps_cfg = { + .of_node = di->dev->of_node, + .drv_data = di, + }; + + INIT_DELAYED_WORK(&di->work, mm8xxx_battery_poll); + mutex_init(&di->lock); + + ps_desc = devm_kzalloc(di->dev, sizeof(*ps_desc), GFP_KERNEL); + if (!ps_desc) + return -ENOMEM; + + ps_desc->name = MM8XXX_BATT_PHY; + ps_desc->type = POWER_SUPPLY_TYPE_MAINS; + ps_desc->properties = mm8xxx_chip_data[di->chip].props; + ps_desc->num_properties = mm8xxx_chip_data[di->chip].props_size; + ps_desc->get_property = mm8xxx_battery_get_property; + ps_desc->set_property = mm8xxx_battery_set_property; + ps_desc->external_power_changed = mm8xxx_external_power_changed; + + di->psy = power_supply_register(di->dev, ps_desc, &ps_cfg); + if (IS_ERR(di->psy)) { + dev_err(di->dev, "failed to register battery\n"); + return PTR_ERR(di->psy); + } + + if (sysfs_create_group(&di->psy->dev.kobj, &mm8xxx_attribute_group)) { + mm_info(" Error failed to creat attributes\n"); + } + + mm8xxx_battery_update(di); + + mutex_lock(&mm8xxx_list_lock); + list_add(&di->list, &mm8xxx_battery_devices); + mutex_unlock(&mm8xxx_list_lock); + + return 0; +} + +static void mm8xxx_battery_teardown(struct mm8xxx_device_info *di) +{ + poll_interval = 0; + + cancel_delayed_work_sync(&di->work); + power_supply_unregister(di->psy); + + mutex_lock(&mm8xxx_list_lock); + list_del(&di->list); + mutex_unlock(&mm8xxx_list_lock); + + mutex_destroy(&di->lock); +} + +static void mm8xxx_fake_battery_update(struct mm8xxx_device_info *di) +{ + di->cache.temperature = 250; + di->cache.avg_time_to_empty = 3600; + di->cache.soc = 10; + di->cache.full_charge_capacity = 4020000; + di->cache.flags = 0; + di->cache.health = POWER_SUPPLY_HEALTH_GOOD; + di->cache.cycle_count = 0; + + di->charge_design_full = 4020000; + + di->last_update = jiffies; +} + +static void mm8xxx_fake_battery_poll(struct work_struct *work) +{ + struct mm8xxx_device_info *di = + container_of(work, struct mm8xxx_device_info, work.work); + + mm8xxx_fake_battery_update(di); + + if (poll_interval > 0) + schedule_delayed_work(&di->work, poll_interval * HZ); +} + +static int mm8xxx_fake_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + struct mm8xxx_device_info *di = power_supply_get_drvdata(psy); + + mutex_lock(&di->lock); + if (time_is_before_jiffies(di->last_update + 5 * HZ)) { + cancel_delayed_work_sync(&di->work); + mm8xxx_fake_battery_poll(&di->work.work); + } + mutex_unlock(&di->lock); + + if ((psp != POWER_SUPPLY_PROP_PRESENT) && (di->cache.flags < 0)) + return -ENODEV; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = 4200 * 1000; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = 0; + break; + case POWER_SUPPLY_PROP_CAPACITY: + ret = mm8xxx_simple_value(di->cache.soc, val); + break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = di->cache.temperature; + break; + case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: + ret = mm8xxx_simple_value(di->cache.avg_time_to_empty, val); + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + val->intval = di->cache.full_charge_capacity * di->cache.soc / 100; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + ret = mm8xxx_simple_value(di->cache.full_charge_capacity, val); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + ret = mm8xxx_simple_value(di->charge_design_full, val); + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + ret = mm8xxx_simple_value(di->cache.cycle_count, val); + break; + case POWER_SUPPLY_PROP_HEALTH: + ret = mm8xxx_simple_value(di->cache.health, val); + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = MM8XXX_MANUFACTURER; + break; + + /* + * TODO: Implement appropriate POWER_SUPPLY_PROP property and read + * elapsed time. + * + * di->cache.elapsed_months + * di->cache.elapsed_days + * di->cache.elapsed_hours + */ + + default: + return -EINVAL; + } + + return ret; +} + +static void mm8xxx_fake_external_power_changed(struct power_supply *psy) +{ + struct mm8xxx_device_info *di = power_supply_get_drvdata(psy); + + cancel_delayed_work_sync(&di->work); + schedule_delayed_work(&di->work, 0); +} + +static int mm8xxx_fake_battery_setup(struct mm8xxx_device_info *di) +{ + struct power_supply_desc *ps_desc; + struct power_supply_config ps_cfg = { + .of_node = di->dev->of_node, + .drv_data = di, + }; + + INIT_DELAYED_WORK(&di->work, mm8xxx_fake_battery_poll); + mutex_init(&di->lock); + + ps_desc = devm_kzalloc(di->dev, sizeof(*ps_desc), GFP_KERNEL); + if (!ps_desc) + return -ENOMEM; + + ps_desc->name = "bms"; + ps_desc->type = POWER_SUPPLY_TYPE_MAINS; + ps_desc->properties = mm8xxx_chip_data[di->chip].props; + ps_desc->num_properties = mm8xxx_chip_data[di->chip].props_size; + ps_desc->get_property = mm8xxx_fake_battery_get_property; + ps_desc->external_power_changed = mm8xxx_fake_external_power_changed; + + di->psy = power_supply_register(di->dev, ps_desc, &ps_cfg); + if (IS_ERR(di->psy)) { + dev_err(di->dev, "failed to register battery\n"); + return PTR_ERR(di->psy); + } + + mm8xxx_fake_battery_update(di); + + mutex_lock(&mm8xxx_list_lock); + list_add(&di->list, &mm8xxx_battery_devices); + mutex_unlock(&mm8xxx_list_lock); + + return 0; +} + +static u32 mmi_get_battery_info(struct mm8xxx_device_info *di, u32 cmd) +{ + int ret = 0; + + ret = mm8xxx_write(di, MM8XXX_CMD_CONTROL, cmd); + if (ret < 0) { + dev_err(di->dev, "error writing cmd control\n"); + return ret; + } + + ret = mm8xxx_read(di, MM8XXX_CMD_CONTROL); + if (ret < 0) { + dev_err(di->dev, "error read cmd control\n"); + return ret; + } + + return (u32)ret; +} + +static int mm8xxx_battery_parse_dts(struct mm8xxx_device_info *di) +{ + struct device_node *np = di->dev->of_node; + int rc; + + rc = of_property_read_u32(np, "latest_fw_version", &di->latest_fw_version); + if(rc < 0){ + di->latest_fw_version = MM8013_LATEST_FW_VERSION; + mm_info("dts no config fw version, use default fw version=0x%04x\n", di->latest_fw_version); + } + + rc = of_property_read_u32(np, "first_battery_param_ver", &di->first_battery_param_ver); + if (rc < 0) { + di->first_battery_param_ver = MM8013_PARAMETER_VERSION; + mm_info("dts no config parameter version, first_battery_param_ver=0x%04x\n", di->first_battery_param_ver); + } + + rc = of_property_read_u32(np, "second_battery_param_ver", &di->second_battery_param_ver); + if (rc < 0) { + di->second_battery_param_ver = MM8013_PARAMETER_VERSION; + mm_info("dts no config parameter version, second_battery_param_ver=0x%04x\n", di->second_battery_param_ver); + } + + rc = of_property_read_u32(np, "first_battery_id", &di->first_battery_id); + if (rc < 0) { + di->first_battery_id = MM8013_1TH_BATTERY_ID; + } + + rc = of_property_read_u32(np, "second_battery_id", &di->second_battery_id); + if (rc < 0) { + di->second_battery_id = MM8013_2TH_BATTERY_ID; + } + + return rc; +} + +bool is_factory_mode(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool factory_mode = false; + const char *bootargs = NULL; + char *bootmode = NULL; + char *end = NULL; + + if (!np) + return factory_mode; + + if (!of_property_read_string(np, "bootargs", &bootargs)) { + bootmode = strstr(bootargs, "androidboot.mode="); + if (bootmode) { + end = strpbrk(bootmode, " "); + bootmode = strpbrk(bootmode, "="); + } + if (bootmode && + end > bootmode && + strnstr(bootmode, "mot-factory", end - bootmode)) { + factory_mode = true; + } + } + of_node_put(np); + + return factory_mode; +} + +static int mm8xxx_battery_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + char *name; + int num; + u32 fg_fw_ver; + u32 fg_param_ver; + u32 fg_battery_id; + u32 parameter_version = 0xFFFF; + enum UPDATE_INDEX update_index =UPDATE_NONE; + + mm_info("MM8013 prob begin\n"); + + mutex_lock(&battery_mutex); + num = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL); + mutex_unlock(&battery_mutex); + if (num < 0) + return num; + + name = devm_kasprintf(&client->dev, GFP_KERNEL, "%s-%d", id->name, num); + if (!name) + goto mem_err; + + di = devm_kzalloc(&client->dev, sizeof(*di), GFP_KERNEL); + if (!di) + goto mem_err; + + i2c_set_clientdata(client, di); + + di->id = num; + di->dev = &client->dev; + di->chip = id->driver_data; + di->name = name; + + di->bus.read = mm8xxx_battery_read; + di->bus.write = mm8xxx_battery_write; + di->cmds = mm8xxx_chip_data[di->chip].cmds; + + mm8xxx_battery_parse_dts(di); + + ret = mmi_get_battery_info(di, HW_VER_CMD); + if (ret != MM8013_HW_VERSION) { + di->fake_battery = true; + mm_info("don't have real battery,use fake battery\n"); + } + + if (!di->fake_battery) + { + fg_fw_ver = mmi_get_battery_info(di, FW_VER_CMD); + fg_param_ver = mmi_get_battery_info(di, PARAM_VER_CMD); + fg_battery_id = mmi_get_battery_info(di, BATTERY_ID_CMD); + mm_info("From fg ic,fg_fw_ver=0x%04x, battery_id=0x%04x,fg_param_ver=0x%04x\n", \ + fg_fw_ver, fg_battery_id, fg_param_ver); + + if ((fg_fw_ver == 0x0024) && (fg_param_ver == 0x0006)) { + /* System Reset Request */ + mm_info("The FG is in boot mode, Requesting system resetting ... "); + if (mm8xxx_battery_write_Nbyte(di, COMMAND_MODECONTROL, 0x80, 1) < 0) { + mm_info("Reset fg system failed\n"); + } + } + + if (is_factory_mode()) + { + if (fg_fw_ver < 0 || fg_param_ver < 0 || fg_battery_id < 0) { + update_index = UPDATE_NONE; + goto update_begin; + } + else if (fg_battery_id == di->first_battery_id) { + parameter_version = di->first_battery_param_ver; + } + else if (fg_battery_id == di->second_battery_id) { + parameter_version = di->second_battery_param_ver; + } + if (fg_fw_ver < di->latest_fw_version) { + if (fg_param_ver < parameter_version) + update_index = UPDATE_ALL; + else if (fg_param_ver == parameter_version) + update_index = UPDATE_PROGRAM; + else if (fg_param_ver == 0xa200) + update_index = UPDATE_ALL; + } + else if (fg_param_ver < parameter_version || fg_param_ver == 0xa200) + update_index = UPDATE_PARAMETER; + + update_begin: + if (update_index != UPDATE_NONE) + mm8xxx_battery_update_program_and_parameter(di, update_index); + } + } + + di->Batt_NTC_channel = devm_iio_channel_get(&client->dev, "batt_therm"); + if (IS_ERR(di->Batt_NTC_channel)) { + dev_err(&client->dev, "failed to get batt_therm IIO channel\n"); + ret = PTR_ERR(di->Batt_NTC_channel); + goto failed; + } + + if (di->fake_battery) + ret = mm8xxx_fake_battery_setup(di); + else + ret = mm8xxx_battery_setup(di); + + if (ret) + goto failed; + + schedule_delayed_work(&di->work, 60 * HZ); + + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, mm8xxx_battery_irq_handler_thread, + IRQF_ONESHOT, + di->name, di); + if (ret) { + dev_err(&client->dev, + "Unable to register IRQ %d error %d\n", + client->irq, ret); + + return ret; + } + } + + mm_info("MM8013 driver probe success\n"); + return 0; + +mem_err: + ret = -ENOMEM; + +failed: + mutex_lock(&battery_mutex); + idr_remove(&battery_id, num); + mutex_unlock(&battery_mutex); + + return ret; +} + +static int mm8xxx_battery_remove(struct i2c_client *client) +{ + struct mm8xxx_device_info *di = i2c_get_clientdata(client); + + mm8xxx_battery_teardown(di); + + mutex_lock(&battery_mutex); + idr_remove(&battery_id, di->id); + mutex_unlock(&battery_mutex); + + return 0; +} + +static const struct i2c_device_id mm8xxx_battery_id_table[] = { + { "mm8118g01", MM8118G01 }, + { "mm8118w02", MM8118W02 }, + { "mm8013c10", MM8013C10 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, mm8xxx_battery_id_table); + +#ifdef CONFIG_OF +static const struct of_device_id mm8xxx_battery_of_match_table[] = { + { .compatible = "mitsumi,mm8118g01" }, + { .compatible = "mitsumi,mm8118w02" }, + { .compatible = "mitsumi,mm8013c10" }, + {}, +}; +MODULE_DEVICE_TABLE(of, mm8xxx_battery_of_match_table); +#endif + +static struct i2c_driver mm8xxx_battery_driver = { + .driver = { + .name = "mm8xxx-battery", + .of_match_table = of_match_ptr(mm8xxx_battery_of_match_table), + }, + .probe = mm8xxx_battery_probe, + .remove = mm8xxx_battery_remove, + .id_table = mm8xxx_battery_id_table, +}; +module_i2c_driver(mm8xxx_battery_driver); + +MODULE_AUTHOR("Takayuki Sugaya"); +MODULE_AUTHOR("Yasuhiro Kinoshita"); +MODULE_DESCRIPTION("MM8xxx battery monitor"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/mmi-smbcharger-iio/Android.mk b/drivers/power/mmi-smbcharger-iio/Android.mk new file mode 100755 index 000000000000..912ff6a94624 --- /dev/null +++ b/drivers/power/mmi-smbcharger-iio/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi-smbcharger-iio.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/power/mmi-smbcharger-iio/Kbuild b/drivers/power/mmi-smbcharger-iio/Kbuild new file mode 100755 index 000000000000..2ae7e9a8fe0e --- /dev/null +++ b/drivers/power/mmi-smbcharger-iio/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += mmi-smbcharger-iio.o diff --git a/drivers/power/mmi-smbcharger-iio/Makefile b/drivers/power/mmi-smbcharger-iio/Makefile new file mode 100755 index 000000000000..34b09dd815a7 --- /dev/null +++ b/drivers/power/mmi-smbcharger-iio/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean \ No newline at end of file diff --git a/drivers/power/mmi-smbcharger-iio/README b/drivers/power/mmi-smbcharger-iio/README new file mode 100644 index 000000000000..e01ce089fe41 --- /dev/null +++ b/drivers/power/mmi-smbcharger-iio/README @@ -0,0 +1,382 @@ +Motorola Mobility. SMBMMI Charger Specific Bindings + +This is an introduction to qpnp-smbcharger-mmi wrapper/driver. It is a DLKM, +and messages on kernel logs may be tagged as: +- "SMBMMI" +- "SMB_MMI" +- PMIC model (like "pm8150b_charger", or "pm660") +- or no tag, if it's happening during probe + +It depends on QCOM PMIC driver implementation (located in +kernel/drivers/power/supply/qcom), and requires different DT properties in order +to configure itself. + +All products rely on parse_mmi_dt() to look for necessary DT properties, and in +case it has a dual-battery architecture, it uses the additional +parse_mmi_dual_dt() function. + +When dual-battery architecture, references to battery will be "main" and "flip", +referring to where in the phone battery is physically located. Prefix "max" is +legacy, since first Fuel Gauge solution was from Maxim Integrated, but other FG +solutions are possible, like Texas Instruments (TI). + +In terms of DT properties, some of them are: + + +- qcom,mmi-temp-zones + Usage: optional + Value Type: Array of tuples of + Definition: Unlimited array of arrays. Each internal array needs to have a set +of 4 bytes, each one representing this sequence: Temperature (in degC), Step +"Normalized" Voltage (in millivolts), Full Rate "FCC" Current (in milliamps), +and Taper Rate "FCC Normalized" Current (in milliamps). These values are +related to charging limitations based on temperature specifications. Note that +setting a voltage to '0' means that it doesn't matter the battery voltage on +that defined temperature range to perform necessary tapering. To understand how +those values are used, please check "IEEE Charging Compliance" section below. + +- qcom,iterm-ma + Usage: optional + Value Type: + Definition: It will define taper current on battery (in milliamps). If not +defined, defaults to 150. + +- qcom,vfloat-comp-uv + Usage: optional + Value Type: + Definition: This is a tolerance, in uV, for battery floating voltage when +charging is being limited. Such situation can happen when either battery is +close to full or when there's thermal mitigation on charging. If not set, +defaults to 0. + +- qcom,enable-charging-limit + Usage: optional + Value Type: + Definition: Boolean flag to set capacity limits to battery when plugged on +a charger and is executing a Factory Image. It will use upper and lower +capacities defined by the Device Tree. + +- qcom,enable-factory-poweroff + Usage: optional + Value Type: + Definition: Boolean flag which enables Factory Kill when executing under a +Factory Image. By default, Factory Kill is a feature only in Factory Mode, +which turns device off when charger is removed. + +- qcom,upper-limit-capacity + Usage: optional + Value Type: + Definition: Limits minimum battery SoC (in percentage). In other words, starts +charging when it reaches this value. Especially useful for factory procedures, +which needs to limit SoC. Requires qcom,enable-charging-limit. If not defined, +defaults to 100. + +- qcom,lower-limit-capacity + Usage: optional + Value Type: + Definition: Limits maximum battery SoC (in percentage). In other words, stop +charging when it reaches this value. Especially useful for factory procedures, +which needs to limit SoC. Requires qcom,enable-charging-limit. If not defined, +defaults to 0. + +- qcom,dc-icl-ma + Usage: optional + Value Type: + Definition: Set this value to allow ICL for DC current, used by MOD or +wireless charging. If not set, it won't be used. + +- qcom,force-hvdcp-5v +Usage: optional +Value Type: +Definition: Boolean flag to maintain USB input to 5V, so no PD or other +protocols request higher voltages. + +- mmi,max-main-psy + Usage: optional + Value Type: + Definition: Battery name on battery main description defined in FG driver. +(as an example, see max170xx_battery_mmi.c) + +- mmi,max-flip-psy + Usage: optional + Value Type: + Definition: Battery name on battery flip description defined in FG driver. +(as an example, see max170xx_battery_mmi.c) + +- qcom,mmi-temp-zones-main + Usage: optional + Value Type: Array of + Definition: Depends on mmi,max-main-psy. Analoguous to qcom,mmi-temp-zones . + +- qcom,iterm-ma-main + Usage: optional + Value Type: + Definition: Depends on mmi,max-main-psy. Analoguous to qcom,iterm-ma. + +- qcom,mmi-temp-zones-flip + Usage: optional + Value Type: Array of + Definition: Depends on mmi,max-main-psy. Analoguous to qcom,mmi-temp-zones. + +- qcom,iterm-ma-flip + Usage: optional + Value Type: + Definition: Depends on mmi,max-flip-psy. Analoguous to qcom,iterm-ma. + + +========================== + IEEE Charging Compliance +========================== + +In order to be compliant to IEEE rules, there are a series of limitations on +how much a battery can be charged, based on temperature. That will limit how +much current can be drawn, or maximum battery voltage to be achieved during +charging. + +Current is always tapered when voltage achieves maximum voltage specification +(which is generally 4.4V, but may have a different value, depending a product +battery specs). It is tapered to some small value, possibly around 100mA, +which is based on product specifications and capacity testing, defined by +EE (Electrical Engineering) Team/Hardware. + +If temperature is below -20 degC or above 60 degC, it should automatically +stop charging device. + +If temperature is in between 15 and 45 deg C, it can achieve maximum charging +current, and such value varies across products. Generally, if it supports PD, +this will be a much higher current than non-PD products, which will also have +a much higher current than dual-battery products. Important to note that this +is a rule-of-thumb, so always check product specification. + +If temperature is between 0 and 10 deg C, but battery voltage is above 4.20V, +current should be tapered to 1000mA. + +Finally, if temperature is in between -20 and 0 deg C, current should be +tapered to 1000mA. + +NOTE1: whenever dealing with IEEE certification, most documentation and +variable notation across bootloader and kernel may refer to JEITA. This is a +convention on such standards and values to which temperature, voltage and +current levels are accepted in different scenarios for Li-ion batteries. So +JEITA can be seen as a general standard, but IEEE enforces it as well as +provide ways to certify a product according to it (is mandatory to certify +any commercial product that has a lithium-ion battery embedded or plugged to +it). + +NOTE2: taper current mostly refers to when battery is above maximum allowed +voltage on a specific temperature region, based on rules above. So be aware +that the rest of this document may refer as "taper" to a current that is +limiting the maximum value, but before achieving a stage in which current +is limited to something around 100mA (so an intermediate stage). Also +"taper voltage" will refer to a voltage level in which this mentioned +"taper" current (intermediate current level) is triggered. + + +========================================= + How to define qcom,mmi-temp-zones array +========================================= + +When defining qcom,mmi-temp-zones array, it is important to always have in +mind the IEEE rules described above, as well the battery specifications. + +Each tuple in that array represents a different value, as shown here: + + +"temperature" defines maximum temperature value. So setting "35" means a +definition to anything below 35 degC. + +"taper_volt" means maximum allowed battery voltage in that temperature +range. So setting "4200" means that tapering will happen once battery +is above 4.2V and is still charging. + +"max_cur_below_taper_volt" defines maximum allowed current value in case +battery is below "taper_volt" value. So setting it to "700" means that +current won't go above 700mA, as long as battery is below "taper_volt" +voltage. + +"max_cur_above_taper_volt" defines maximum allowed current value in case +battery is above "taper_volt" value. So setting it to "0" means that it +won't charge in case battery is above "taper_volt" voltage. + +Here's an example from Voyager: + +qcom,mmi-temp-zones-flip = <0 4200 250 0>, + <10 4200 610 250>, + <15 0 610 610>, + <45 4200 1225 860>, + <60 4200 610 0>; + +First line states "If temperature is below 0 degC, and flip battery voltage +is below 4.2V, device can be charged up to 250mA. But if flip battery voltage +is above 4.2V, device cannot be charged". + +Third line states "If temperature is below 15 degC, flip battery can be charged +up to 610mA, no matter the battery voltage". + + +================================================= + Understanding the IEEE algorithm implementation +================================================= + +Now that IEEE rules were understood, as well as how qcom,mmi-temp-zones is set, +just to stress out this driver implementation, the thermal charging limitation +implementation will be described. + +mmi_basic_charge_sm() or mmi_dual_charge_sm() are the functions responsible +to enforce such rules. In that way, before we check the implementation, here +are some conventions: + +STEP_NONE: no charger present +STEP_STOP: in zone hot or zone cold, which limits charging considerably +STEP_NORM: limits current to some value +STEP_MAX: allows maximum current value, when in between 0 and 45 degC +STEP_FULL: battery is almost or completely full, tapering to a small current +STEP_FLOAT: TODO + +Important to note that even when moving from one state to another, temperature +may change. Hence, taper current and voltage will no longer correspond to such +state, and so is always necessary to check on every state in which temperature +range device is. + +"batt_ma" refers to battery current (equivalent to current_now on battery +sysfs). +"taper_ma" is "fcc_norm_ma" in case is in STATE_MAX, which means battery is +above taper voltage. If in any other state, it will refer to "chrg_iterm", +which will depend on device specifications (4.4V batteries have 97mA as +current standards, and 4.45V batteries have 121mA as taper current). + +Now, let's check the algorithms for a bit: + +----------------------------------------------------- +current-step: STEP_NONE | STEP_STOP + if norm_mv AND (batt_mv + 50 >= norm_mv) // (1) + if fcc_norm_ma // (2) + -goto STEP_NORM // (2) + else + -goto STEP_STOP // (2) + else + -goto STEP_MAX // (1) + +(1) are we in a range which has "norm_mv" defined, and battery is above this +value? +This corresponds to most temperature ranges, except in between 15 and 45 degC. +If that's the case, allow charging up to maximum current defined in product +spec. + +(2) is "fcc_norm_ma" a value different than zero? +This happens if temperature is in range between 0 and 45 degC. Due to previous +consideration, it is only missing range 0 to 15 degC here. If that's the case, +limit maximum current. Otherwise, battery is above "norm_mv" (which probably +is defined as 4.2V), and at that voltage and temperature, limiting charging +considerably. + +----------------------------------------------------- +current-step: STEP_MAX + if !norm_mv // (1) + taper_cnt = 0 + if (batt_mv + 50) > max_fv_mv // (2) + -goto STEP_NORM + else + -goto STEP_MAX + else if (batt_mv + 50) < norm_mv // (1) + taper_cnt = 0 + -goto STEP_MAX + else if !fcc_norm_ma // (3) + -goto STEP_FLOAT + else if current_has_tapered // (4) + taper_cnt = 0 + if charge_halted + vote chg_dis_votable + -goto STEP_NORM + +(1) are we in a range which has "norm_mv" NOT defined, or below taper voltage? +This is only for 15 to 45 degC region, which has no current tapering. + +(2) are we above taper voltage? +This checks whether taper voltage was achieved, making current to taper to +defined values, depending on which temperature region battery is at this +moment. + +(3) at current temperature, is taper current defined? +If there is a taper current value to current temperature region (which is +usually in between 0 and 15 degC as defined in current implementation), allow +TODO STEP_FLOAT + +(4) has current been limited due to other thermal rules? +Function mmi_has_current_tapered() requests to power supply votable module +what is the allowed maximum current. Then such value will be compared to taper +current value, and the minimum will be chosen. Additionally, it checks whether +there's still a charger plugged to device or if charging is disabled, and if +any, mark a flag on system displaying no charging is going on. Finally, move +to taper current state (STEP_NORM). + +----------------------------------------------------- +current-step: STEP_NORM + if !fcc_norm_ma // (1) + -goto STEP_FLOAT + else if batt_soc < 100 or (batt_mv + 50) < max_fv_mv // (2) + taper_cnt = 0 + -goto STEP_NORM + else if current_has_tapered // (2) + -goto STEP_FULL + +(1) although device is in STEP_NORM, is there a defined taper current for +current temperature region device is in? +It may happen that temperature has changed, and device no longer needs to +be tapered. Hence, move to STEP_FLOAT. + +(2) is battery NOT full or below taper voltage? +Stay on this state, because tapering is still happening. Otherwise, it may +be the case that battery is above taper voltage, or device is already fully +charged, forcing it to prepare to stop charging (STEP_FULL). + +----------------------------------------------------- +current-step: STEP_FULL + if batt_soc < 99 // (1) + taper_cnt = 0 + goto STEP_NORM + +(1) is battery no longer full? +It may happen that battery device was previously full, but no longer is. So, +first move to a limited current state for safety, and then in such state, it +may happen that maximum current levels can be achieved. + +----------------------------------------------------- +current-step: STEP_FLOAT + if fcc_norm_ma or (batt_mv + 50) < norm_mv // (1) + goto STEP_MAX + if current_has_tapered // (2) + goto STEP_STOP + +(1) does battery temperature allow current to be tapered to something above +0mA, or battery is below taper voltage? +This means that temperature is in between 0 and 15 degC. If so, try to achieve +maximum current value (STEP_MAX). + +(2) does current needs to be tapered? +In case mmi_has_current_tapered() needs to decrease current, it will request +charger to stop charging. + +----------------------------------------------------- +Setting current values based on state machine: + +STEP_FLOAT +STEP_MAX + target_fcc = fcc_max_ma + target_fv: + if !norm_mv + target_fv = max_fv_mv + chip->vfloat_comp_mv + else + target_fv = norm_mv + chip->vfloat_comp_mv + +STEP_FULL: + target_fcc = -EINVAL + target_fv = max_fv_mv + +STEP_NORM: + target_fcc = fcc_norm_ma + target_fv = max_fv_mv + chip->vfloat_comp_mv + +STEP_NONE: + target_fv = max_fv_mv + target_fcc = fcc_norm_ma diff --git a/drivers/power/mmi-smbcharger-iio/mmi-smbcharger-iio.c b/drivers/power/mmi-smbcharger-iio/mmi-smbcharger-iio.c new file mode 100755 index 000000000000..30f8f3686c49 --- /dev/null +++ b/drivers/power/mmi-smbcharger-iio/mmi-smbcharger-iio.c @@ -0,0 +1,5634 @@ +/* Copyright (c) 2017, 2018 The Linux Foundation. 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 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 61)) +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_LOG "SMBMMI" + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 61)) +#define get_effective_result(votable) \ + mmi_get_effective_result(votable) + +#define vote(votable, client_str, enabled, val) \ + mmi_vote(votable, client_str, enabled, val) + +#define find_votable(name) mmi_find_votable(name) + +#define pmic_vote_force_val_set(votable, val) \ + mmi_pmic_vote_force_val_set(votable, val) + +#define pmic_vote_force_active_set(votable, val) \ + mmi_pmic_vote_force_active_set(votable, val) +#endif + +#define mmi_err(chg, fmt, ...) \ + do { \ + pr_err("%s: %s: %s: " fmt, chg->name, \ + MODULE_LOG, __func__, ##__VA_ARGS__); \ + ipc_log_string(chg->ipc_log, \ + "ERR:%s: " fmt, __func__, ##__VA_ARGS__); \ + } while (0) + +#define mmi_warn(chg, fmt, ...) \ + do { \ + pr_warn("%s: %s: %s: " fmt, chg->name, \ + MODULE_LOG, __func__, ##__VA_ARGS__); \ + ipc_log_string(chg->ipc_log, \ + "WARN:%s: " fmt, __func__, ##__VA_ARGS__); \ + } while (0) + +#define mmi_info(chg, fmt, ...) \ + do { \ + pr_info("%s: %s: %s: " fmt, chg->name, \ + MODULE_LOG, __func__, ##__VA_ARGS__); \ + ipc_log_string(chg->ipc_log, \ + "INFO:%s: " fmt, __func__, ##__VA_ARGS__); \ + } while (0) + +#define mmi_dbg(chg, fmt, ...) \ + do { \ + if (*chg->debug_enabled) \ + pr_info("%s: %s: %s: " fmt, chg->name, \ + MODULE_LOG, __func__, ##__VA_ARGS__); \ + else \ + pr_debug("%s: %s: %s: " fmt, chg->name, \ + MODULE_LOG, __func__, ##__VA_ARGS__); \ + ipc_log_string(chg->ipc_log, \ + "DBG:%s: " fmt, __func__, ##__VA_ARGS__); \ + } while (0) + +#define MMI_LOG_PAGES (50) + +static bool debug_enabled; +module_param(debug_enabled, bool, 0600); +MODULE_PARM_DESC(debug_enabled, "Enable debug for SMBMMI driver"); + +struct mmi_smbcharger_iio_channels { + const char *datasheet_name; + int channel_num; + enum iio_chan_type type; + long info_mask; +}; + +#define MMI_SMBCHARGER_IIO_CHAN(_name, _num, _type, _mask) \ + { \ + .datasheet_name = _name, \ + .channel_num = _num, \ + .type = _type, \ + .info_mask = _mask, \ + }, + +#define MMI_SMBCHARGER_CHAN_INDEX(_name, _num) \ + MMI_SMBCHARGER_IIO_CHAN(_name, _num, IIO_INDEX, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +static const struct mmi_smbcharger_iio_channels mmi_smbcharger_iio_psy_channels[] = { + MMI_SMBCHARGER_CHAN_INDEX("mmi_cp_enabled_status", PSY_IIO_CP_ENABLE) +}; + +static struct smb_mmi_charger *this_chip = NULL; + +enum pmic_type { + PM8150B, + PM7250B, + PM6150, + PMI632, +}; + +#define CHGR_BASE 0x1000 +#define DCDC_BASE 0x1100 +#define BATIF_BASE 0x1200 +#define USBIN_BASE 0x1300 +#define DCIN_BASE 0x1400 +#define TYPEC_BASE 0X1500 +#define MISC_BASE 0x1600 + +#define BATTERY_CHARGER_STATUS_1_REG (CHGR_BASE + 0x06) +#define BATTERY_CHARGER_STATUS_MASK (GENMASK(2, 0)) +#define PM8150B_JEITA_EN_CFG_REG (CHGR_BASE + 0x90) + +#define POWER_PATH_STATUS_REG (DCDC_BASE + 0x0B) +#define MISC_POWER_PATH_STATUS_REG (MISC_BASE + 0x0B) +#define USBIN_SUSPEND_STS_BIT BIT(6) + +#define DCDC_CFG_REF_MAX_PSNS_REG (DCDC_BASE + 0x8C) + +#define APSD_STATUS_REG (USBIN_BASE + 0x07) +#define APSD_STATUS_7_BIT BIT(7) +#define HVDCP_CHECK_TIMEOUT_BIT BIT(6) +#define SLOW_PLUGIN_TIMEOUT_BIT BIT(5) +#define ENUMERATION_DONE_BIT BIT(4) +#define VADP_CHANGE_DONE_AFTER_AUTH_BIT BIT(3) +#define QC_AUTH_DONE_STATUS_BIT BIT(2) +#define QC_CHARGER_BIT BIT(1) +#define APSD_DTC_STATUS_DONE_BIT BIT(0) +#define APSD_RESULT_STATUS_REG (USBIN_BASE + 0x08) +#define APSD_RESULT_STATUS_7_BIT BIT(7) +#define APSD_RESULT_STATUS_MASK GENMASK(6, 0) +#define QC_3P0_BIT BIT(6) +#define QC_2P0_BIT BIT(5) +#define FLOAT_CHARGER_BIT BIT(4) +#define DCP_CHARGER_BIT BIT(3) +#define CDP_CHARGER_BIT BIT(2) +#define OCP_CHARGER_BIT BIT(1) +#define SDP_CHARGER_BIT BIT(0) +#define USBIN_INT_RT_STS (USBIN_BASE + 0x10) +#define USBIN_PLUGIN_RT_STS_BIT BIT(4) +#define USBIN_INT_EN_CLR (USBIN_BASE + 0x16) +#define USBIN_OV_EN_CLR BIT(3) +#define CMD_APSD_REG (USBIN_BASE + 0x41) +#define APSD_RERUN_BIT BIT(0) +#define ICL_OVERRIDE_BIT BIT(1) +#define USBIN_CMD_ICL_OVERRIDE_REG (USBIN_BASE + 0x42) +#define USBIN_ICL_OVERRIDE_BIT BIT(0) +#define HVDCP_PULSE_COUNT_MAX_REG (USBIN_BASE + 0x5B) +#define HVDCP_PULSE_COUNT_MAX_QC2_MASK GENMASK(7, 6) +enum { + HVDCP_PULSE_COUNT_MAX_QC2_5V = 0, + HVDCP_PULSE_COUNT_MAX_QC2_9V = 0x40, + HVDCP_PULSE_COUNT_MAX_QC2_12V = 0x80, + HVDCP_PULSE_COUNT_MAX_QC2_INVALID = 0xC0 +}; + +#define HVDCP_PULSE_COUNT_MAX_QC3_MASK GENMASK(5, 0) +#define USBIN_ICL_OPTIONS_REG (USBIN_BASE + 0x66) +#define USBIN_MODE_CHG_BIT BIT(0) +#define USBIN_LOAD_CFG_REG (USBIN_BASE + 0x65) +#define ICL_OVERRIDE_AFTER_APSD_BIT BIT(4) +#define USBIN_AICL_OPTIONS_CFG_REG (USBIN_BASE + 0x80) +#define LEGACY_CABLE_CFG_REG (TYPEC_BASE + 0x5A) +#define USBIN_ADAPTER_ALLOW_CFG_REG (USBIN_BASE + 0x60) +#define USBIN_ADAPTER_ALLOW_MASK GENMASK(3, 0) + +/* DCIN Interrupt Bits */ +#define DCIN_PLUGIN_RT_STS_BIT BIT(4) +#define DCIN_INT_RT_STS (DCIN_BASE + 0x10) + +enum { + USBIN_ADAPTER_ALLOW_5V = 0, + USBIN_ADAPTER_ALLOW_9V = 2, + USBIN_ADAPTER_ALLOW_5V_OR_9V = 3, + USBIN_ADAPTER_ALLOW_12V = 4, + USBIN_ADAPTER_ALLOW_5V_OR_12V = 5, + USBIN_ADAPTER_ALLOW_9V_TO_12V = 6, + USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V = 7, + USBIN_ADAPTER_ALLOW_5V_TO_9V = 8, + USBIN_ADAPTER_ALLOW_5V_TO_12V = 12, +}; + +#define LOW_BATT_MV 3300 +#define SHUT_BATT_MV 3100 +#define REV_BST_THRESH 4700 +#define REV_BST_DROP 150 +#define REV_BST_BULK_DROP 100 +#define REV_BST_MA -10 +#define BOOST_BACK_VOTER "BOOST_BACK_VOTER" +#define MMI_HB_VOTER "MMI_HB_VOTER" +#define BATT_PROFILE_VOTER "BATT_PROFILE_VOTER" +#define DEMO_VOTER "DEMO_VOTER" +#define MMI_VOTER "MMI_VOTER" +#define HYST_STEP_MV 50 +#define HYST_STEP_FLIP_MV (HYST_STEP_MV*2) +#define DEMO_MODE_HYS_SOC 5 +#define DEMO_MODE_VOLTAGE 4000 +#define WARM_TEMP 45 +#define COOL_TEMP 0 +#define WEAK_CHARGER_WAIT 30000 +#define EVENT_WEAK_CHARGER_WAIT 0 + +#define HEARTBEAT_DELAY_MS 5000 +#define HEARTBEAT_DUAL_DELAY_MS 10000 +#define HEARTBEAT_DUAL_DELAY_OCP_MS 1000 +#define HEARTBEAT_OCP_SETTLE_CNT 7 +#define HEARTBEAT_FACTORY_MS 1000 +#define HEARTBEAT_DISCHARGE_MS 100000 + +#define EMPTY_CYCLES 101 + +enum { + TRICKLE_CHARGE = 0, + PRE_CHARGE, + FAST_CHARGE, + FULLON_CHARGE, + TAPER_CHARGE, + TERMINATE_CHARGE, + INHIBIT_CHARGE, + DISABLE_CHARGE, +}; + +enum { + INHIBIT_CHARGE_V5 = 0, + TRICKLE_CHARGE_V5, + PRE_CHARGE_V5, + FULLON_CHARGE_V5, + TAPER_CHARGE_V5, + TERMINATE_CHARGE_V5, + PAUSE_CHARGE_V5, + DISABLE_CHARGE_V5, +}; + +enum { + POWER_SUPPLY_CHARGE_RATE_NONE = 0, + POWER_SUPPLY_CHARGE_RATE_NORMAL, + POWER_SUPPLY_CHARGE_RATE_WEAK, + POWER_SUPPLY_CHARGE_RATE_TURBO, +}; + +enum { + CHG_BC1P2_SDP = 0, + CHG_BC1P2_OCP, + CHG_BC1P2_CDP, + CHG_BC1P2_DCP, + CHG_BC1P2_FLOAT, + CHG_BC1P2_QC_2P0, + CHG_BC1P2_QC_3P0, + CHG_BC1P2_UNKNOWN, +}; + +typedef enum { + CHARGER_FFC_STATE_INITIAL, + CHARGER_FFC_STATE_PROBING, + CHARGER_FFC_STATE_STANDBY, + CHARGER_FFC_STATE_GOREADY, + CHARGER_FFC_STATE_RUNNING, + CHARGER_FFC_STATE_AVGEXIT, + CHARGER_FFC_STATE_FFCDONE, + CHARGER_FFC_STATE_INVALID, +} CHARGER_FFC_STATE_T; + +enum { + CHARGER_FLAG_INVALID, + CHARGER_FLAG_FFC, + CHARGER_FLAG_NON_FFC, +}; + +static char *charge_rate[] = { + "None", "Normal", "Weak", "Turbo" +}; + +struct mmi_temp_zone { + int temp_c; + int norm_mv; + int fcc_max_ma; + int fcc_norm_ma; +}; + +#define ZONE_35C_TO_45C 5 +#define MAX_NUM_STEPS 10 +enum mmi_temp_zones { + ZONE_FIRST = 0, + /* states 0-9 are reserved for zones */ + ZONE_LAST = MAX_NUM_STEPS + ZONE_FIRST - 1, + ZONE_HOT, + ZONE_COLD, + ZONE_NONE = 0xFF, +}; + +enum { + BASE_BATT = 0, + MAIN_BATT, + FLIP_BATT, +}; + +enum mmi_chrg_step { + STEP_MAX, + STEP_NORM, + STEP_FULL, + STEP_FLOAT, + STEP_DEMO, + STEP_STOP, + STEP_NONE = 0xFF, +}; + +static char *stepchg_str[] = { + [STEP_MAX] = "MAX", + [STEP_NORM] = "NORMAL", + [STEP_FULL] = "FULL", + [STEP_FLOAT] = "FLOAT", + [STEP_DEMO] = "DEMO", + [STEP_STOP] = "STOP", + [STEP_NONE] = "NONE", +}; + +struct smb_mmi_chg_status { + int batt_mv; + int batt_ma; + int batt_soc; + int batt_temp; + int usb_mv; + int dc_mv; + int charger_present; + int vbus_present; + int dc_present; +}; + +struct smb_mmi_chg_param { + const char *name; + u16 reg; + int min_u; + int max_u; + int step_u; + int (*get_proc)(struct smb_mmi_chg_param *param, + u8 val_raw); + int (*set_proc)(struct smb_mmi_chg_param *param, + int val_u, + u8 *val_raw); +}; + +struct smb_mmi_chg_freq { + unsigned int freq_5V; + unsigned int freq_6V_8V; + unsigned int freq_9V; + unsigned int freq_12V; +}; + +struct smb_mmi_params { + struct smb_mmi_chg_param fcc; + struct smb_mmi_chg_param fv; + struct smb_mmi_chg_param usb_icl; + struct smb_mmi_chg_param dc_icl; + struct smb_mmi_chg_param aicl_cont_threshold; + struct smb_mmi_chg_param freq_switcher; +}; + +struct mmi_ffc_zone { + int ffc_max_mv; + int ffc_chg_iterm; + int ffc_qg_iterm; +}; + +struct mmi_sm_params { + int num_temp_zones; + struct mmi_ffc_zone *ffc_zones; + struct mmi_temp_zone *temp_zones; + enum mmi_temp_zones pres_temp_zone; + enum mmi_chrg_step pres_chrg_step; + int chrg_taper_cnt; + int batt_health; + int chrg_iterm; + int target_fcc; + int target_fv; + int ocp[MAX_NUM_STEPS]; + int demo_mode_prev_soc; +}; + +enum charging_limit_modes { + CHARGING_LIMIT_OFF, + CHARGING_LIMIT_RUN, + CHARGING_LIMIT_UNKNOWN, +}; + +#define IS_SUSPENDED BIT(0) +#define WAS_SUSPENDED BIT(1) + +struct smb_mmi_charger { + struct device *dev; + struct regmap *regmap; + struct smb_mmi_params param; + struct smb_mmi_chg_freq chg_freq; + char *name; + int smb_version; + + struct iio_channel **ext_iio_chans; + struct iio_dev *indio_dev; + struct iio_chan_spec *iio_chan; + struct iio_channel *int_iio_chans; + bool cp_active; + + bool enable_factory_mode_aicl; + bool factory_mode; + int demo_mode; + struct notifier_block smb_reboot; + + struct power_supply *mmi_psy; + struct power_supply *batt_psy; + struct power_supply *qcom_psy; + struct power_supply *usb_psy; + struct power_supply *bms_psy; + struct power_supply *pc_port_psy; + struct power_supply *dc_psy; + struct power_supply *wls_psy; + struct power_supply *max_main_psy; + struct power_supply *max_flip_psy; + struct notifier_block mmi_psy_notifier; + struct delayed_work heartbeat_work; + struct delayed_work weakcharger_work; + + struct votable *chg_dis_votable; + struct votable *fcc_votable; + struct votable *fv_votable; + struct votable *usb_icl_votable; + struct votable *dc_suspend_votable; + + bool enable_charging_limit; + bool enable_factory_poweroff; + bool is_factory_image; + enum charging_limit_modes charging_limit_modes; + int upper_limit_capacity; + int lower_limit_capacity; + int max_chrg_temp; + int last_iusb_ua; + bool factory_kill_armed; + bool demo_mode_usb_suspend; + unsigned long flags; + + /* Charge Profile */ + struct mmi_sm_params sm_param[3]; + int base_fv_mv; + int vfloat_comp_mv; + int suspended; + bool awake; + int last_reported_soc; + int last_reported_status; + struct regulator *vbus; + bool vbus_enabled; + int prev_chg_rate; + int charger_rate; + int power_watt; + int pd_power; + int age; + int cycles; + int soc_cycles_start; + bool shut_batt; + int dc_cl_ma; + + bool *debug_enabled; + void *ipc_log; + + struct usbpd *pd_handle; + struct usbpd_pdo_info mmi_pdo_info[PD_MAX_PDO_NUM]; + int pd_power_max; + + int hvdcp_power_max; + int inc_hvdcp_cnt; + int hb_startup_cnt; + bool ocp_flag; + bool hvdcp2_force_9v; + + /* none ffc paramter */ + int noffc_chg_iterm; + int noffc_chg_iterm_45c; + int noffc_qg_iterm; + int noffc_qg_iterm_45c; + int noffc_max_fv; + int pd_pps_active; + int real_charger_type; + + /*DCP FFC feature*/ + CHARGER_FFC_STATE_T ffc_state; + int ffc_entry_threshold; + int ffc_exit_threshold; + int ffc_uisoc_threshold; + long ffc_ibat_windowsum; + long ffc_ibat_count; + int ffc_ibat_windowsize; + int ffc_iavg; + unsigned long ffc_iavg_update_timestamp; + + /* DCP FFC */ + bool ffc_stop_chg; + bool enable_dcp_ffc; + + bool enable_fcc_large_qg_iterm; +}; + +#define CHGR_FAST_CHARGE_CURRENT_CFG_REG (CHGR_BASE + 0x61) +#define CHGR_FLOAT_VOLTAGE_CFG_REG (CHGR_BASE + 0x70) +#define USBIN_CURRENT_LIMIT_CFG_REG (USBIN_BASE + 0x70) +#define DCIN_CURRENT_LIMIT_CFG_REG (DCIN_BASE + 0x70) +#define USBIN_CONT_AICL_THRESHOLD_REG (USBIN_BASE + 0x84) +#define DCDC_FSW_SEL_REG (DCDC_BASE + 0x50) + +#define AICL_RANGE2_MIN_MV 5600 +#define AICL_RANGE2_STEP_DELTA_MV 200 +#define AICL_RANGE2_OFFSET 16 +int smblib_get_aicl_cont_threshold(struct smb_mmi_chg_param *param, u8 val_raw) +{ + int base = param->min_u; + u8 reg = val_raw; + int step = param->step_u; + + + if (val_raw >= AICL_RANGE2_OFFSET) { + reg = val_raw - AICL_RANGE2_OFFSET; + base = AICL_RANGE2_MIN_MV; + step = AICL_RANGE2_STEP_DELTA_MV; + } + + return base + (reg * step); +} + +int smblib_set_aicl_cont_threshold(struct smb_mmi_chg_param *param, + int val_u, u8 *val_raw) +{ + int base = param->min_u; + int offset = 0; + int step = param->step_u; + + if (val_u > param->max_u) + val_u = param->max_u; + if (val_u < param->min_u) + val_u = param->min_u; + + if (val_u >= AICL_RANGE2_MIN_MV) { + base = AICL_RANGE2_MIN_MV; + step = AICL_RANGE2_STEP_DELTA_MV; + offset = AICL_RANGE2_OFFSET; + }; + + *val_raw = ((val_u - base) / step) + offset; + + return 0; +} + +/******************** + * REGISTER SETTERS * + ********************/ + struct smb_buck_boost_freq { + int freq_khz; + u8 val; +}; +static const struct smb_buck_boost_freq chg_freq_list[] = { + [0] = { + .freq_khz = 2400, + .val = 7, + }, + [1] = { + .freq_khz = 2100, + .val = 8, + }, + [2] = { + .freq_khz = 1600, + .val = 11, + }, + [3] = { + .freq_khz = 1200, + .val = 15, + }, +}; + +int smblib_set_chg_freq(struct smb_mmi_chg_param *param, + int val_u, u8 *val_raw) +{ + u8 i; + + if (val_u > param->max_u || val_u < param->min_u) + return -EINVAL; + + /* Charger FSW is the configured freqency / 2 */ + val_u *= 2; + for (i = 0; i < ARRAY_SIZE(chg_freq_list); i++) { + if (chg_freq_list[i].freq_khz == val_u) + break; + } + if (i == ARRAY_SIZE(chg_freq_list)) { + pr_err("Invalid frequency %d Hz\n", val_u / 2); + return -EINVAL; + } + + *val_raw = chg_freq_list[i].val; + + return 0; +} + +static struct smb_mmi_params smb5_pm8150b_params = { + .fcc = { + .name = "fast charge current", + .reg = CHGR_FAST_CHARGE_CURRENT_CFG_REG, + .min_u = 0, + .max_u = 8000000, + .step_u = 50000, + }, + .fv = { + .name = "float voltage", + .reg = CHGR_FLOAT_VOLTAGE_CFG_REG, + .min_u = 3600000, + .max_u = 4790000, + .step_u = 10000, + }, + .usb_icl = { + .name = "usb input current limit", + .reg = USBIN_CURRENT_LIMIT_CFG_REG, + .min_u = 0, + .max_u = 5000000, + .step_u = 50000, + }, + .dc_icl = { + .name = "DC input current limit", + .reg = DCDC_CFG_REF_MAX_PSNS_REG, + .min_u = 0, + .max_u = 1500000, + .step_u = 50000, + }, + .aicl_cont_threshold = { + .name = "AICL CONT threshold", + .reg = USBIN_CONT_AICL_THRESHOLD_REG, + .min_u = 4000, + .max_u = 11800, + .step_u = 100, + .get_proc = smblib_get_aicl_cont_threshold, + .set_proc = smblib_set_aicl_cont_threshold, + }, + .freq_switcher = { + .name = "switching frequency", + .reg = DCDC_FSW_SEL_REG, + .min_u = 600, + .max_u = 1200, + .step_u = 400, + .set_proc = smblib_set_chg_freq, + }, +}; + +static struct smb_mmi_params smb5_pmi632_params = { + .fcc = { + .name = "fast charge current", + .reg = CHGR_FAST_CHARGE_CURRENT_CFG_REG, + .min_u = 0, + .max_u = 3000000, + .step_u = 50000, + }, + .fv = { + .name = "float voltage", + .reg = CHGR_FLOAT_VOLTAGE_CFG_REG, + .min_u = 3600000, + .max_u = 4800000, + .step_u = 10000, + }, + .usb_icl = { + .name = "usb input current limit", + .reg = USBIN_CURRENT_LIMIT_CFG_REG, + .min_u = 0, + .max_u = 3000000, + .step_u = 50000, + }, + .dc_icl = { + .name = "dc input current limit", + /* TODO: For now USBIN seems to be the way to set this */ + .reg = USBIN_CURRENT_LIMIT_CFG_REG, + .min_u = 0, + .max_u = 3000000, + .step_u = 50000, + }, + .freq_switcher = { + .name = "switching frequency", + .reg = DCDC_FSW_SEL_REG, + .min_u = 600, + .max_u = 1200, + .step_u = 400, + .set_proc = smblib_set_chg_freq, + }, +}; + +enum smb_mmi_ext_iio_channels { + //smb5 + SMB5_CTM_CURRENT_MAX = 0, + SMB5_CURRENT_MAX, + SMB5_TYPEC_MODE, + SMB5_DP_DM, + SMB5_PD_ACTIVE, + SMB5_USB_REAL_TYPE, + SMB5_HW_CURRENT_MAX, + SMB5_USB_INPUT_CURRENT_SETTLED, + SMB5_RERUN_AICL, + SMB5_SW_JEITA_ENABLED, + SMB5_PD_CURRENT_MAX, + //bms + SMB5_QG_VOLTAGE_NOW, + SMB5_QG_CURRENT_NOW, + SMB5_QG_TEMP, + SMB5_QG_CAPACITY, + SMB5_QG_CHARGE_FULL, + SMB5_QG_CHARGE_FULL_DESIGN, + SMB5_QG_BATT_FULL_CURRENT, +}; + +static const char * const smb_mmi_ext_iio_chan_name[] = { + //smb5 + [SMB5_CTM_CURRENT_MAX] = "usb_ctm_current_max", + [SMB5_CURRENT_MAX] = "main_current_max", + [SMB5_TYPEC_MODE] = "usb_typec_mode", + [SMB5_DP_DM] = "battery_dp_dm", + [SMB5_PD_ACTIVE] = "usb_pd_active", + [SMB5_USB_REAL_TYPE] = "usb_real_type", + [SMB5_HW_CURRENT_MAX] = "usb_hw_current_max", + [SMB5_USB_INPUT_CURRENT_SETTLED] = "usb_input_current_settled", + [SMB5_RERUN_AICL] = "battery_rerun_aicl", + [SMB5_SW_JEITA_ENABLED] = "battery_sw_jeita_enabled", + [SMB5_PD_CURRENT_MAX] = "usb_pd_current_max", + //bms + [SMB5_QG_VOLTAGE_NOW] = "voltage_now", + [SMB5_QG_CURRENT_NOW] = "current_now", + [SMB5_QG_TEMP] = "temp", + [SMB5_QG_CAPACITY] = "capacity", + [SMB5_QG_CHARGE_FULL] = "charge_full", + [SMB5_QG_CHARGE_FULL_DESIGN] = "charge_full_design", + [SMB5_QG_BATT_FULL_CURRENT] = "batt_full_current", +}; + +bool is_chan_valid(struct smb_mmi_charger *chip, + enum smb_mmi_ext_iio_channels chan) +{ + int rc; + + if (IS_ERR(chip->ext_iio_chans[chan])) + return false; + + if (!chip->ext_iio_chans[chan]) { + chip->ext_iio_chans[chan] = iio_channel_get(chip->dev, + smb_mmi_ext_iio_chan_name[chan]); + if (IS_ERR(chip->ext_iio_chans[chan])) { + rc = PTR_ERR(chip->ext_iio_chans[chan]); + if (rc == -EPROBE_DEFER) + chip->ext_iio_chans[chan] = NULL; + + pr_err("Failed to get IIO channel %s, rc=%d\n", + smb_mmi_ext_iio_chan_name[chan], rc); + return false; + } + } + + return true; +} + +int smb_mmi_read_iio_chan(struct smb_mmi_charger *chip, + enum smb_mmi_ext_iio_channels chan, int *val) +{ + int rc; + + if (is_chan_valid(chip, chan)) { + rc = iio_read_channel_processed( + chip->ext_iio_chans[chan], val); + return (rc < 0) ? rc : 0; + } + + return -EINVAL; +} + +int smb_mmi_write_iio_chan(struct smb_mmi_charger *chip, + enum smb_mmi_ext_iio_channels chan, int val) +{ + if (is_chan_valid(chip, chan)) + return iio_write_channel_raw(chip->ext_iio_chans[chan], + val); + + return -EINVAL; +} + +int smblib_read_mmi(struct smb_mmi_charger *chg, u16 addr, u8 *val) +{ + unsigned int value; + int rc = 0; + + rc = regmap_read(chg->regmap, addr, &value); + if (rc >= 0) + *val = (u8)value; + + return rc; +} + +int smblib_write_mmi(struct smb_mmi_charger *chg, u16 addr, u8 val) +{ + return regmap_write(chg->regmap, addr, val); +} + +int smblib_masked_write_mmi(struct smb_mmi_charger *chg, u16 addr, u8 mask, u8 val) +{ + return regmap_update_bits(chg->regmap, addr, mask, val); +} + +int smblib_get_apsd_result(struct smb_mmi_charger *chg, int *type) +{ + int rc = 0; + u8 stat; + int apsd; + + rc = smblib_read_mmi(chg, APSD_STATUS_REG, &stat); + if (rc < 0) { + mmi_err(chg, "Couldn't read APSD_STATUS_REG rc=%d\n", rc); + return rc; + } + if (!(stat & APSD_DTC_STATUS_DONE_BIT)) + return -EBUSY; + + rc = smblib_read_mmi(chg, APSD_RESULT_STATUS_REG, &stat); + if (rc < 0) { + mmi_err(chg, "Couldn't read APSD_RESULT_STATUS_REG rc=%d\n", rc); + return rc; + } + + if (stat & QC_3P0_BIT) + apsd = CHG_BC1P2_QC_3P0; + else if (stat & QC_2P0_BIT) + apsd = CHG_BC1P2_QC_2P0; + else if (stat & FLOAT_CHARGER_BIT) + apsd = CHG_BC1P2_FLOAT; + else if (stat & DCP_CHARGER_BIT) + apsd = CHG_BC1P2_DCP; + else if (stat & CDP_CHARGER_BIT) + apsd = CHG_BC1P2_CDP; + else if (stat & OCP_CHARGER_BIT) + apsd = CHG_BC1P2_OCP; + else if (stat & SDP_CHARGER_BIT) + apsd = CHG_BC1P2_SDP; + else + apsd = CHG_BC1P2_UNKNOWN; + + *type = apsd; + + return rc; +} + +#define CHG_SHOW_MAX_SIZE 50 +static u16 smblib_mmi_address = 0; +static ssize_t smblib_mmi_address_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long rc; + unsigned long mode; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + rc = kstrtoul(buf, 0, &mode); + if (rc) { + pr_err("SMBMMI: Invalid smblib mmi address value = %lu\n", mode); + return -EINVAL; + } + + smblib_mmi_address = (u16)mode; + mmi_info(mmi_chip, "smblib mmi address value = 0x%04x\n", \ + smblib_mmi_address); + + return rc ? rc : count; +} + +static DEVICE_ATTR(smblib_mmi_address, 0644, + NULL, + smblib_mmi_address_store); + +static ssize_t smblib_mmi_data_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u8 data = 0; + int rc = 0; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + rc = smblib_read_mmi(mmi_chip, smblib_mmi_address, &data); + if (rc) { + mmi_err(mmi_chip, "Couldn't read address 0x%x rc=%d\n", \ + smblib_mmi_address, rc); + return rc; + } + + mmi_info(mmi_chip, "smblib mmi read data value = 0x%02x\n", data); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%04x: %02x\n", \ + smblib_mmi_address, data); +} + +static ssize_t smblib_mmi_data_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long data = 0; + int rc = 0; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + rc = kstrtoul(buf, 0, &data); + if (rc) { + pr_err("SMBMMI: Invalid smblib mmi data value = %lu\n", data); + return -EINVAL; + } + + rc = smblib_write_mmi(mmi_chip, smblib_mmi_address, (u8)data); + if (rc) { + mmi_err(mmi_chip, "Couldn't write address 0x%x rc=%d\n", \ + smblib_mmi_address, rc); + return rc; + } + + mmi_info(mmi_chip, "smblib mmi write data value = 0x%02x\n", (u8)data); + + return rc ? rc : count; +} + +static DEVICE_ATTR(smblib_mmi_data, 0644, + smblib_mmi_data_show, + smblib_mmi_data_store); + +static ssize_t factory_image_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("SMBMMI: Invalid factory image mode value = %lu\n", mode); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + mmi_chip->is_factory_image = (mode) ? true : false; + + return r ? r : count; +} + +static ssize_t factory_image_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + state = (mmi_chip->is_factory_image) ? 1 : 0; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(factory_image_mode, 0644, + factory_image_mode_show, + factory_image_mode_store); + +static ssize_t factory_charge_upper_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + state = mmi_chip->upper_limit_capacity; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(factory_charge_upper, 0444, + factory_charge_upper_show, + NULL); + +#define MMI_CHIP_MODE_LOWER_LIMIT 35 +#define MMI_CHIP_MODE_UPPER_LIMIT 80 +static ssize_t force_demo_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("SMBMMI: Invalid demo mode value = %lu\n", mode); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + if ((mode >= MMI_CHIP_MODE_LOWER_LIMIT) && + (mode <= MMI_CHIP_MODE_UPPER_LIMIT)) + mmi_chip->demo_mode = mode; + else + mmi_chip->demo_mode = MMI_CHIP_MODE_LOWER_LIMIT; + + return r ? r : count; +} + +static ssize_t force_demo_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + state = mmi_chip->demo_mode; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_demo_mode, 0644, + force_demo_mode_show, + force_demo_mode_store); + +#define MIN_TEMP_C -20 +#define MAX_TEMP_C 60 +#define MIN_MAX_TEMP_C 47 +#define HYSTERESIS_DEGC 2 +static ssize_t force_max_chrg_temp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("SMBMMI: Invalid max temp value = %lu\n", mode); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + if ((mode >= MIN_MAX_TEMP_C) && (mode <= MAX_TEMP_C)) + mmi_chip->max_chrg_temp = mode; + else + mmi_chip->max_chrg_temp = MAX_TEMP_C; + + return r ? r : count; +} + +static ssize_t force_max_chrg_temp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + state = mmi_chip->max_chrg_temp; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_max_chrg_temp, 0644, + force_max_chrg_temp_show, + force_max_chrg_temp_store); + +#define USBIN_CMD_IL_REG (USBIN_BASE + 0x40) +#define USBIN_SUSPEND_BIT BIT(0) +int smblib_set_usb_suspend(struct smb_mmi_charger *chg, bool suspend) +{ + int rc = 0; + + rc = smblib_masked_write_mmi(chg, USBIN_CMD_IL_REG, USBIN_SUSPEND_BIT, + suspend ? USBIN_SUSPEND_BIT : 0); + if (rc < 0) + mmi_err(chg, "Couldn't write %s to USBIN suspend rc=%d\n", + suspend ? "suspend" : "resume", rc); + + return rc; +} + +int smblib_get_usb_suspend(struct smb_mmi_charger *chg, int *suspend) +{ + int rc = 0; + u8 temp = 0; + + rc = smblib_read_mmi(chg, POWER_PATH_STATUS_REG, &temp); + if (rc < 0) { + mmi_err(chg, "Couldn't read POWER_PATH_STATUS_REG rc=%d\n", rc); + return rc; + } + *suspend = temp & USBIN_SUSPEND_STS_BIT; + + return rc; +} + +static ssize_t force_chg_usb_suspend_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("SMBMMI: Invalid usb suspend mode value = %lu\n", mode); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + r = smblib_set_usb_suspend(mmi_chip, (bool)mode); + + return r ? r : count; +} + +static ssize_t force_chg_usb_suspend_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int ret; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + ret = smblib_get_usb_suspend(mmi_chip, &state); + if (ret) { + mmi_err(mmi_chip, "USBIN_SUSPEND_BIT failed ret = %d\n", ret); + state = -EFAULT; + goto end; + } + +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_usb_suspend, 0664, + force_chg_usb_suspend_show, + force_chg_usb_suspend_store); + +static ssize_t force_chg_fail_clear_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("SMBMMI: Invalid chg fail mode value = %lu\n", mode); + return -EINVAL; + } + + /* do nothing for SMBCHG */ + r = 0; + + return r ? r : count; +} + +static ssize_t force_chg_fail_clear_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + /* do nothing for SMBCHG */ + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "0\n"); +} + +static DEVICE_ATTR(force_chg_fail_clear, 0664, + force_chg_fail_clear_show, + force_chg_fail_clear_store); + +#define CHARGING_ENABLE_CMD_REG (CHGR_BASE + 0x42) +#define CHARGING_ENABLE_CMD_BIT BIT(0) +static ssize_t force_chg_auto_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("SMBMMI: Invalid chrg enable value = %lu\n", mode); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + r = smblib_masked_write_mmi(mmi_chip, CHARGING_ENABLE_CMD_REG, + CHARGING_ENABLE_CMD_BIT, + mode ? CHARGING_ENABLE_CMD_BIT : 0); + if (r < 0) { + mmi_err(mmi_chip, "Factory Couldn't %s charging rc=%d\n", + mode ? "enable" : "disable", (int)r); + return r; + } + + return r ? r : count; +} + +static ssize_t force_chg_auto_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int ret; + u8 value; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + state = -ENODEV; + goto end; + } + + ret = smblib_read_mmi(mmi_chip, CHARGING_ENABLE_CMD_REG, &value); + if (ret) { + mmi_err(mmi_chip, "CHG_EN_BIT failed ret = %d\n", ret); + state = -EFAULT; + goto end; + } + + state = (CHARGING_ENABLE_CMD_BIT & value) ? 1 : 0; +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_auto_enable, 0664, + force_chg_auto_enable_show, + force_chg_auto_enable_store); + +int smblib_set_charge_param(struct smb_mmi_charger *chg, + struct smb_mmi_chg_param *param, int val_u) +{ + int rc = 0; + u8 val_raw; + + if (param->set_proc) { + rc = param->set_proc(param, val_u, &val_raw); + if (rc < 0) + return -EINVAL; + } else { + if (val_u > param->max_u || val_u < param->min_u) + mmi_dbg(chg, "%s: %d is out of range [%d, %d]\n", + param->name, val_u, + param->min_u, param->max_u); + + if (val_u > param->max_u) + val_u = param->max_u; + if (val_u < param->min_u) + val_u = param->min_u; + + val_raw = (val_u - param->min_u) / param->step_u; + } + + rc = smblib_write_mmi(chg, param->reg, val_raw); + if (rc < 0) { + mmi_err(chg, "%s: Couldn't write 0x%02x to 0x%04x rc=%d\n", + param->name, val_raw, param->reg, rc); + return rc; + } + + mmi_dbg(chg, "%s = %d (0x%02x)\n", param->name, val_u, val_raw); + + return rc; +} + +int smblib_get_charge_param(struct smb_mmi_charger *chg, + struct smb_mmi_chg_param *param, int *val_u) +{ + int rc = 0; + u8 val_raw; + + rc = smblib_read_mmi(chg, param->reg, &val_raw); + if (rc < 0) { + mmi_err(chg, "%s: Couldn't read from 0x%04x rc=%d\n", + param->name, param->reg, rc); + return rc; + } + + if (param->get_proc) + *val_u = param->get_proc(param, val_raw); + else + *val_u = val_raw * param->step_u + param->min_u; + mmi_dbg(chg, "%s = %d (0x%02x)\n", param->name, *val_u, val_raw); + + return rc; +} + +static ssize_t force_chg_ibatt_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long chg_current; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &chg_current); + if (r) { + pr_err("SMBMMI: Invalid ibatt value = %lu\n", chg_current); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + chg_current *= 1000; /* Convert to uA */ + if(mmi_chip->fcc_votable) { + pmic_vote_force_val_set(mmi_chip->fcc_votable, chg_current); + pmic_vote_force_active_set(mmi_chip->fcc_votable, 1); + } + + return r ? r : count; +} + +static ssize_t force_chg_ibatt_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int ret; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + state = -ENODEV; + goto end; + } + + ret = smblib_get_charge_param(mmi_chip, &mmi_chip->param.fcc, &state); + if (ret < 0) { + mmi_err(mmi_chip, "Factory Couldn't get master fcc rc=%d\n", (int)ret); + return ret; + } + + state /= 1000; /* Convert to mA */ +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_ibatt, 0664, + force_chg_ibatt_show, + force_chg_ibatt_store); + +static ssize_t force_chg_iusb_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long usb_curr; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &usb_curr); + if (r) { + pr_err("SMBMMI: Invalid iusb value = %lu\n", usb_curr); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + usb_curr *= 1000; /* Convert to uA */ + if (mmi_chip->usb_icl_votable) { + pmic_vote_force_val_set(mmi_chip->usb_icl_votable, usb_curr); + pmic_vote_force_active_set(mmi_chip->usb_icl_votable, 1); + } + + r = smblib_masked_write_mmi(mmi_chip, USBIN_ICL_OPTIONS_REG, + USBIN_MODE_CHG_BIT, USBIN_MODE_CHG_BIT); + if (r < 0) + mmi_err(mmi_chip, "Couldn't set USBIN_ICL_OPTIONS r=%d\n", (int)r); + + r = smblib_masked_write_mmi(mmi_chip, USBIN_LOAD_CFG_REG, + ICL_OVERRIDE_AFTER_APSD_BIT, + ICL_OVERRIDE_AFTER_APSD_BIT); + if (r < 0) + mmi_err(mmi_chip, "Couldn't set USBIN_LOAD_CFG rc=%d\n", (int)r); + + r = smblib_masked_write_mmi(mmi_chip, USBIN_AICL_OPTIONS_CFG_REG, + 0xFF, 0x00); + if (r < 0) + mmi_err(mmi_chip, "Couldn't set USBIN_AICL_OPTIONS rc=%d\n", (int)r); + + return r ? r : count; +} + +static ssize_t force_chg_iusb_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int r; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("chip not valid\n"); + state = -ENODEV; + goto end; + } + + r = smblib_get_charge_param(mmi_chip, &mmi_chip->param.usb_icl, &state); + if (r < 0) { + mmi_err(mmi_chip, "Factory Couldn't get usb_icl rc=%d\n", (int)r); + return r; + } + state /= 1000; /* Convert to mA */ +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_iusb, 0664, + force_chg_iusb_show, + force_chg_iusb_store); + +static ssize_t force_chg_idc_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long dc_curr; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &dc_curr); + if (r) { + pr_err("SMBMMI: Invalid idc value = %lu\n", dc_curr); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + dc_curr *= 1000; /* Convert to uA */ + r = smblib_set_charge_param(mmi_chip, &mmi_chip->param.dc_icl, dc_curr); + if (r < 0) { + mmi_err(mmi_chip, "Factory Couldn't set dc icl = %d rc=%d\n", + (int)dc_curr, (int)r); + return r; + } + + return r ? r : count; +} + +static ssize_t force_chg_idc_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int r; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + state = -ENODEV; + goto end; + } + + r = smblib_get_charge_param(mmi_chip, &mmi_chip->param.dc_icl, &state); + if (r < 0) { + mmi_err(mmi_chip, "Factory Couldn't get dc_icl rc=%d\n", (int)r); + return r; + } + state /= 1000; /* Convert to mA */ +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_idc, 0664, + force_chg_idc_show, + force_chg_idc_store); + +#define PRE_CHARGE_CURRENT_CFG_REG (CHGR_BASE + 0x60) +#define PRE_CHARGE_CURRENT_SETTING_MASK 0x07 +#define PRE_CHARGE_CONV_MV 50 +#define PRE_CHARGE_MAX 7 +#define PRE_CHARGE_MIN 100 +#define PRE_CHARGE_SMB2_CONV_MV 25 +#define PRE_CHARGE_SMB2_MAX 0x3F +static ssize_t force_chg_itrick_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long chg_current; + u8 value, mask; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &chg_current); + if (r) { + pr_err("SMBMMI: Invalid pre-charge value = %lu\n", chg_current); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + switch (mmi_chip->smb_version) { + case PM8150B: + case PM7250B: + case PMI632: + mask = PRE_CHARGE_CURRENT_SETTING_MASK; + if (chg_current < PRE_CHARGE_MIN) + chg_current = 0; + else + chg_current -= PRE_CHARGE_MIN; + + chg_current /= PRE_CHARGE_CONV_MV; + + if (chg_current > PRE_CHARGE_MAX) + value = PRE_CHARGE_MAX; + else + value = (u8)chg_current; + break; + default: + mmi_err(mmi_chip, "Set ITRICK PMIC subtype %d not supported\n", + mmi_chip->smb_version); + return -EINVAL; + } + + r = smblib_masked_write_mmi(mmi_chip, PRE_CHARGE_CURRENT_CFG_REG, + mask, + value); + if (r < 0) { + mmi_err(mmi_chip, "Factory Couldn't set ITRICK %d mV rc=%d\n", + (int)value, (int)r); + return r; + } + + return r ? r : count; +} + +static ssize_t force_chg_itrick_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int ret; + u8 value; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + state = -ENODEV; + goto end; + } + + ret = smblib_read_mmi(mmi_chip, PRE_CHARGE_CURRENT_CFG_REG, &value); + if (ret) { + mmi_err(mmi_chip, "Pre Chg ITrick failed ret = %d\n", ret); + state = -EFAULT; + goto end; + } + + switch (mmi_chip->smb_version) { + case PM8150B: + case PM7250B: + case PMI632: + value &= PRE_CHARGE_CURRENT_SETTING_MASK; + state = (value * PRE_CHARGE_CONV_MV) + PRE_CHARGE_MIN; + break; + default: + mmi_err(mmi_chip, "Get ITRICK PMIC subtype %d not supported\n", + mmi_chip->smb_version); + return -EINVAL; + } +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_itrick, 0664, + force_chg_itrick_show, + force_chg_itrick_store); + +static ssize_t chg_watt_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int power_watt = -ENODEV; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (mmi_chip) { + power_watt = mmi_chip->power_watt / 1000; + mmi_info(mmi_chip, "power_watt %dW\n", power_watt); + } else { + pr_err("chip not valid\n"); + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", power_watt); +} + +static DEVICE_ATTR(chg_watt, 0444, + chg_watt_show, + NULL); + +enum { + MMI_FACTORY_MODE, + MMI_FACTORY_BUILD, +}; + +static bool mmi_factory_check(int type) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool factory = false; + const char *bootargs = NULL; + char *bl_version = NULL; + char *end = NULL; + + if (!np) + return factory; + + switch (type) { + case MMI_FACTORY_MODE: + factory = of_property_read_bool(np, + "mmi,factory-cable"); + break; + case MMI_FACTORY_BUILD: + if (!of_property_read_string(np, "bootargs", &bootargs)) { + bl_version = strstr(bootargs, "androidboot.bootloader="); + if (bl_version) { + end = strpbrk(bl_version, " "); + bl_version = strpbrk(bl_version, "="); + } + if (bl_version && end > bl_version && + strnstr(bl_version, "factory", end - bl_version)) { + factory = true; + } + } + break; + default: + factory = false; + } + of_node_put(np); + + return factory; +} + +#define CHARGER_POWER_15W 15000 +#define CHARGER_POWER_18W 18000 +#define CHARGER_POWER_20W 20000 +static void mmi_charger_power_support(struct smb_mmi_charger *chg) +{ + struct device_node *np = chg->dev->of_node; + const char *charger_ability = NULL; + int retval; + + retval = of_property_read_u32(np, "qcom,pd-power-max", + &chg->pd_power_max); + if (retval) { + chg->pd_power_max = CHARGER_POWER_18W; + } + + chg->pd_handle = + devm_usbpd_get_by_phandle(chg->dev, "qcom,usbpd-phandle"); + if (IS_ERR_OR_NULL(chg->pd_handle)) { + dev_err(chg->dev, "Error getting the pd phandle %ld\n", + PTR_ERR(chg->pd_handle)); + chg->pd_handle = NULL; + } + + if (of_property_read_bool(np, "qcom,force-hvdcp-5v")) { + chg->hvdcp_power_max = CHARGER_POWER_15W; + return; + } + + retval = of_property_read_u32(np, "qcom,hvdcp-power-max", + &chg->hvdcp_power_max); + if (retval) { + chg->hvdcp_power_max = 0; + return; + } + + np = of_find_node_by_path("/chosen"); + + if (!np) + return; + + retval = of_property_read_string(np, "mmi,charger", + &charger_ability); + + if ((retval == -EINVAL) || !charger_ability) { + mmi_err(chg, "mmi,charger unused\n"); + of_node_put(np); + return; + + } else + mmi_info(chg, "charger = %s\n", charger_ability); + + if (strstr(charger_ability, "15W")) + chg->hvdcp_power_max = CHARGER_POWER_15W; + else if (strstr(charger_ability, "18W")) + chg->hvdcp_power_max = CHARGER_POWER_18W; + else if (strstr(charger_ability, "20W")) + chg->hvdcp_power_max = CHARGER_POWER_20W; + + of_node_put(np); + + return; +} + +static enum power_supply_property smb_mmi_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CURRENT_MAX, +}; + +static int smb_mmi_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + val->intval = -EINVAL; + break; + default: + return -EINVAL; + } + return 0; +} + +static int smb_mmi_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct smb_mmi_charger *chip = power_supply_get_drvdata(psy); + int rc = 0; + int override = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_CURRENT_MAX: + if (val->intval) { + rc = smb_mmi_write_iio_chan(chip, + SMB5_CTM_CURRENT_MAX, val->intval); + rc = smb_mmi_write_iio_chan(chip, + SMB5_CURRENT_MAX, val->intval); + override = USBIN_ICL_OVERRIDE_BIT; + } + mmi_warn(chip, "Request for ICL to %d uA\n", val->intval); + + rc = smblib_masked_write_mmi(chip, + USBIN_CMD_ICL_OVERRIDE_REG, + USBIN_ICL_OVERRIDE_BIT, + override); + if (rc < 0) + mmi_err(chip, + "Couldn't set USBIN_CMD_ICL_OVERRIDE rc=%d\n", rc); + break; + default: + rc = -EINVAL; + } + + return rc; +} + +static bool mmi_find_temp_zone(struct smb_mmi_charger *chg, + struct mmi_sm_params *chip, + int temp_c) +{ + int prev_zone, num_zones; + struct mmi_temp_zone *zones; + int hotter_t, hotter_fcc; + int colder_t, colder_fcc; + int i; + int max_temp; + + if (!chg) { + pr_debug("SMBMMI: called before chg valid!\n"); + return false; + } + + zones = chip->temp_zones; + num_zones = chip->num_temp_zones; + prev_zone = chip->pres_temp_zone; + + if (chg->max_chrg_temp >= MIN_MAX_TEMP_C) + max_temp = chg->max_chrg_temp; + else + max_temp = zones[num_zones - 1].temp_c; + + if (prev_zone == ZONE_NONE) { + for (i = num_zones - 1; i >= 0; i--) { + if (temp_c >= zones[i].temp_c) { + if (i == num_zones - 1) + chip->pres_temp_zone = ZONE_HOT; + else + chip->pres_temp_zone = i + 1; + return true; + } + } + chip->pres_temp_zone = ZONE_COLD; + return true; + } + + if (prev_zone == ZONE_COLD) { + if (temp_c >= MIN_TEMP_C + HYSTERESIS_DEGC) + chip->pres_temp_zone = ZONE_FIRST; + } else if (prev_zone == ZONE_HOT) { + if (temp_c <= max_temp - HYSTERESIS_DEGC) + chip->pres_temp_zone = num_zones - 1; + } else { + if (prev_zone == ZONE_FIRST) { + hotter_fcc = zones[prev_zone + 1].fcc_max_ma; + colder_fcc = 0; + hotter_t = zones[prev_zone].temp_c; + colder_t = MIN_TEMP_C; + } else if (prev_zone == num_zones - 1) { + hotter_fcc = 0; + colder_fcc = zones[prev_zone - 1].fcc_max_ma; + hotter_t = zones[prev_zone].temp_c; + colder_t = zones[prev_zone - 1].temp_c; + } else { + hotter_fcc = zones[prev_zone + 1].fcc_max_ma; + colder_fcc = zones[prev_zone - 1].fcc_max_ma; + hotter_t = zones[prev_zone].temp_c; + colder_t = zones[prev_zone - 1].temp_c; + } + + if (zones[prev_zone].fcc_max_ma < hotter_fcc) + hotter_t += HYSTERESIS_DEGC; + + if (zones[prev_zone].fcc_max_ma < colder_fcc) + colder_t -= HYSTERESIS_DEGC; + + if (temp_c < MIN_TEMP_C) + chip->pres_temp_zone = ZONE_COLD; + else if (temp_c >= max_temp) + chip->pres_temp_zone = ZONE_HOT; + else if (temp_c >= hotter_t) + chip->pres_temp_zone++; + else if (temp_c < colder_t) + chip->pres_temp_zone--; + } + + if (prev_zone != chip->pres_temp_zone) { + mmi_dbg(chg, "Entered Temp Zone %d!\n", + chip->pres_temp_zone); + return true; + } + + return false; +} + +static int get_prop_batt_voltage_now(struct smb_mmi_charger *chg, + struct power_supply *psy, + union power_supply_propval *val) +{ + int rc; + + if (!psy) + return -EINVAL; + + if (!strcmp(psy->desc->name, "bms")) { + rc = smb_mmi_read_iio_chan(chg, SMB5_QG_VOLTAGE_NOW, &val->intval); + } else + rc = power_supply_get_property(psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, val); + return rc; +} + +static int get_prop_batt_current_now(struct smb_mmi_charger *chg, + struct power_supply *psy, + union power_supply_propval *val) +{ + int rc; + + if (!psy) + return -EINVAL; + + if (!strcmp(psy->desc->name, "bms")) { + rc = smb_mmi_read_iio_chan(chg, SMB5_QG_CURRENT_NOW, &val->intval); + } else + rc = power_supply_get_property(psy, + POWER_SUPPLY_PROP_CURRENT_NOW, val); + return rc; +} + +static int get_prop_usb_voltage_now(struct smb_mmi_charger *chg, + union power_supply_propval *val) +{ + int rc; + + if (!chg->usb_psy) + return -EINVAL; + + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, val); + return rc; +} + +static int get_prop_dc_voltage_now(struct smb_mmi_charger *chg, + union power_supply_propval *val) +{ + int rc; + + if (!chg->dc_psy) + return -EINVAL; + + rc = power_supply_get_property(chg->dc_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, val); + return rc; +} + +static int get_prop_batt_temp(struct smb_mmi_charger *chg, + struct power_supply *psy, + union power_supply_propval *val) +{ + int rc; + + if (!psy) + return -EINVAL; + + if (!strcmp(psy->desc->name, "bms")) { + rc = smb_mmi_read_iio_chan(chg, SMB5_QG_TEMP, &val->intval); + } else + rc = power_supply_get_property(psy, + POWER_SUPPLY_PROP_TEMP, val); + return rc; +} + +static int get_prop_batt_capacity(struct smb_mmi_charger *chg, + struct power_supply *psy, + union power_supply_propval *val) +{ + int rc = -EINVAL; + + if (!psy) + return -EINVAL; + + if (!strcmp(psy->desc->name, "bms")) { + rc = smb_mmi_read_iio_chan(chg, SMB5_QG_CAPACITY, &val->intval); + } else + rc = power_supply_get_property(psy, + POWER_SUPPLY_PROP_CAPACITY, val); + return rc; +} + +static int get_prop_usb_present(struct smb_mmi_charger *chg, + union power_supply_propval *val) +{ + int rc; + u8 stat; + + rc = smblib_read_mmi(chg, USBIN_INT_RT_STS, &stat); + if (rc < 0) { + mmi_err(chg, "Couldn't read USBIN_RT_STS rc=%d\n", rc); + return rc; + } + + val->intval = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); + + return 0; +} + +static int get_prop_dc_present(struct smb_mmi_charger *chg, + union power_supply_propval *val) +{ + int rc; + u8 stat; + + /* + * PMI632 has no DCIN support and address at 0x14XX is undefined, + * so just return 0 to avoid too much err log and heatbeat_work + * break + */ + if (chg->smb_version == PMI632) { + val->intval = 0; + return 0; + } + + rc = smblib_read_mmi(chg, DCIN_INT_RT_STS, &stat); + if (rc < 0) { + mmi_err(chg, "Couldn't read DCIN_RT_STS rc=%d\n", rc); + return rc; + } + + val->intval = (bool)(stat & DCIN_PLUGIN_RT_STS_BIT); + + return 0; +} + +static int get_prop_charger_present(struct smb_mmi_charger *chg, + union power_supply_propval *val) +{ + int rc = -EINVAL; + + val->intval = 0; + + if (chg->usb_psy) { + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_ONLINE, val); + if (rc < 0) + mmi_err(chg, "Couldn't read USB online rc=%d\n", rc); + else if (val->intval) + return rc; + } + + if (chg->pc_port_psy) { + rc = power_supply_get_property(chg->pc_port_psy, + POWER_SUPPLY_PROP_ONLINE, val); + if (rc < 0) + mmi_err(chg, "Couldn't read PC online rc=%d\n", rc); + else if (val->intval) + return rc; + } + + rc = smb_mmi_read_iio_chan(chg, + SMB5_TYPEC_MODE, &val->intval); + if (rc < 0) + mmi_err(chg, "Couldn't read typec mode rc=%d\n", rc); + else if (val->intval == QTI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT || + val->intval == QTI_POWER_SUPPLY_TYPEC_SOURCE_MEDIUM || + val->intval == QTI_POWER_SUPPLY_TYPEC_SOURCE_HIGH) { + val->intval = 1; + return rc; + } + + if (chg->wls_psy) { + rc = power_supply_get_property(chg->wls_psy, + POWER_SUPPLY_PROP_ONLINE, val); + if (rc < 0) + mmi_err(chg, "Couldn't read wls online rc=%d\n", rc); + else if (val->intval) + return rc; + } + + //intval can be changed by get_property call we should reset to 0 + //this is true for a OTG cable + val->intval = 0; + return rc; +} + +#define USBIN_500MA 500000 +#define USBIN_900MA 900000 +int mmi_usb_icl_override(struct smb_mmi_charger *chg, int icl) +{ + int rc = 0; + int apsd; + u8 usb51_mode, icl_override, apsd_override; + union power_supply_propval val; + + if (chg->factory_mode) + return rc; + + val.intval = QTI_POWER_SUPPLY_TYPEC_NONE; + rc = smb_mmi_read_iio_chan(chg, + SMB5_TYPEC_MODE, &val.intval); + if (rc < 0 || val.intval != QTI_POWER_SUPPLY_TYPEC_NONE) + return rc; + + rc = smblib_get_apsd_result(chg, &apsd); + if (rc < 0 || apsd == CHG_BC1P2_UNKNOWN) + return rc; + + if (apsd == CHG_BC1P2_SDP && + (icl == USBIN_900MA || icl <= USBIN_500MA)) { + usb51_mode = 0; + apsd_override = 0; + icl_override = USBIN_ICL_OVERRIDE_BIT; + } else { + usb51_mode = USBIN_MODE_CHG_BIT; + apsd_override = ICL_OVERRIDE_AFTER_APSD_BIT; + icl_override = USBIN_ICL_OVERRIDE_BIT; + } + + rc = smb_mmi_write_iio_chan(chg, + SMB5_CURRENT_MAX, icl); + if (rc < 0) { + mmi_err(chg, "Couldn't set usb icl, rc=%d\n", rc); + } + + rc = smblib_masked_write_mmi(chg, USBIN_ICL_OPTIONS_REG, + USBIN_MODE_CHG_BIT, + usb51_mode); + if (rc < 0) + mmi_err(chg, + "Couldn't set USBIN_ICL_OPTIONS rc=%d\n", rc); + + rc = smblib_masked_write_mmi(chg, USBIN_CMD_ICL_OVERRIDE_REG, + USBIN_ICL_OVERRIDE_BIT, + icl_override); + if (rc < 0) + mmi_err(chg, + "Couldn't set USBIN_CMD_ICL_OVERRIDE rc=%d\n", rc); + + rc = smblib_masked_write_mmi(chg, USBIN_LOAD_CFG_REG, + ICL_OVERRIDE_AFTER_APSD_BIT, + apsd_override); + if (rc < 0) + mmi_err(chg, + "Couldn't set USBIN_LOAD_CFG rc=%d\n", rc); + + return rc; +} + +#define HVDCP_VOLTAGE_BASIC (this_chip->hvdcp_power_max / 3) +#define HVDCP_VOLTAGE_NOM (HVDCP_VOLTAGE_BASIC - 200) +#define HVDCP_VOLTAGE_MAX (HVDCP_VOLTAGE_BASIC + 200) +#define HVDCP_VOLTAGE_MIN 4000 +#define HVDCP_PULSE_COUNT_MAX ((HVDCP_VOLTAGE_BASIC - 5000) / 200 + 2) +static int mmi_increase_vbus_power(struct smb_mmi_charger *chg, int cur_mv) +{ + int rc = -EINVAL; + union power_supply_propval val = {0, }; + int pulse_cnt; + + rc = smb_mmi_read_iio_chan(chg, SMB5_DP_DM, &val.intval); + if (rc < 0) { + mmi_err(chg, "Couldn't read dpdm pulse count rc=%d\n", rc); + return cur_mv; + } else { + mmi_info(chg, "DP DM pulse count = %d\n", val.intval); + pulse_cnt = val.intval; + } + + /* Maintain vbus voltage for QC3.0 power/3A charging */ + if (pulse_cnt == 0 || chg->inc_hvdcp_cnt != HVDCP_PULSE_COUNT_MAX) { + + chg->inc_hvdcp_cnt = HVDCP_PULSE_COUNT_MAX; + vote(chg->chg_dis_votable, MMI_HB_VOTER, true, 0); + msleep(100); + + while (cur_mv < HVDCP_VOLTAGE_NOM && pulse_cnt < chg->inc_hvdcp_cnt) { + val.intval = QTI_POWER_SUPPLY_DP_DM_DP_PULSE; + rc = smb_mmi_write_iio_chan(chg, SMB5_DP_DM, val.intval); + if (rc < 0) { + mmi_err(chg, "Couldn't set dpdm pulse rc=%d\n", rc); + break; + } + + msleep(100); + + rc = get_prop_usb_voltage_now(chg, &val); + if (rc < 0) { + mmi_err(chg, "Error getting USB Voltage rc = %d\n", rc); + break; + } else + cur_mv = val.intval / 1000; + + rc = smb_mmi_read_iio_chan(chg, SMB5_DP_DM, &val.intval); + if (rc < 0) { + mmi_err(chg, "Couldn't read dpdm pulse count rc=%d\n", rc); + break; + } else { + pulse_cnt = val.intval; + } + + mmi_info(chg, "pulse count = %d, cur_mv = %d\n", pulse_cnt, cur_mv); + } + + rc = smblib_set_charge_param(chg, &chg->param.aicl_cont_threshold, + HVDCP_VOLTAGE_NOM - 1000); + if (rc < 0) { + mmi_err(chg, "Couldn't set aicl cont threshold to 9V rc=%d\n", rc); + } + + vote(chg->chg_dis_votable, MMI_HB_VOTER, false, 0); + } + + return cur_mv; +} + +static ssize_t force_hvdcp_power_max_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long power; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &power); + if (r) { + pr_err("SMBMMI: Invalid hvdcp_power_max value = %lu\n", power); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + if ((power >= CHARGER_POWER_15W) && + (power <= CHARGER_POWER_20W) && + (mmi_chip->hvdcp_power_max != power)) { + mmi_chip->hvdcp_power_max = power; + + r = smblib_masked_write_mmi(mmi_chip, HVDCP_PULSE_COUNT_MAX_REG, + HVDCP_PULSE_COUNT_MAX_QC3_MASK, + HVDCP_PULSE_COUNT_MAX); + if (r < 0) { + mmi_err(mmi_chip, "Could not set HVDCP pulse count max\n"); + return r; + } + cancel_delayed_work(&mmi_chip->heartbeat_work); + schedule_delayed_work(&mmi_chip->heartbeat_work, + msecs_to_jiffies(100)); + mmi_info(mmi_chip, "Reset hvdcp power max as %d, " + "Reschedule heartbeat\n", + mmi_chip->hvdcp_power_max); + } + + return r ? r : count; +} + +static ssize_t force_hvdcp_power_max_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int power; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + power = mmi_chip->hvdcp_power_max; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", power); +} + +static DEVICE_ATTR(force_hvdcp_power_max, 0644, + force_hvdcp_power_max_show, + force_hvdcp_power_max_store); + +static ssize_t force_pd_power_max_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long power; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &power); + if (r) { + pr_err("SMBMMI: Invalid pd_power_max value = %lu\n", power); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + if ((power >= CHARGER_POWER_15W) && + (power <= CHARGER_POWER_20W) && + (mmi_chip->pd_power_max != power)) { + mmi_chip->pd_power_max = power; + + cancel_delayed_work(&mmi_chip->heartbeat_work); + schedule_delayed_work(&mmi_chip->heartbeat_work, + msecs_to_jiffies(100)); + mmi_info(mmi_chip, "Reset pd power max as %d, " + "Reschedule heartbeat\n", + mmi_chip->pd_power_max); + } + + return r ? r : count; +} + +static ssize_t force_pd_power_max_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int power; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + power = mmi_chip->pd_power_max; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", power); +} + +static DEVICE_ATTR(force_pd_power_max, 0644, + force_pd_power_max_show, + force_pd_power_max_store); + +static int smblib_set_opt_switcher_freq(struct smb_mmi_charger *chg, int fsw_khz) +{ + int rc = 0; + + rc = smblib_set_charge_param(chg, &chg->param.freq_switcher, fsw_khz); + if (rc < 0) + mmi_err(chg, "Error in setting freq_buck rc=%d\n", rc); + + return rc; +} + +#define MICRO_1V 1000000 +#define MICRO_5V 5000000 +#define MICRO_9V 9000000 +#define MICRO_12V 12000000 +static int smblib_set_usb_pd_fsw(struct smb_mmi_charger *chg, int voltage) +{ + int rc = 0; + + if (voltage == MICRO_5V) + rc = smblib_set_opt_switcher_freq(chg, chg->chg_freq.freq_5V); + else if (voltage > MICRO_5V && voltage < MICRO_9V) + rc = smblib_set_opt_switcher_freq(chg, + chg->chg_freq.freq_6V_8V); + else if (voltage >= MICRO_9V && voltage < MICRO_12V) + rc = smblib_set_opt_switcher_freq(chg, chg->chg_freq.freq_9V); + else if (voltage == MICRO_12V) + rc = smblib_set_opt_switcher_freq(chg, chg->chg_freq.freq_12V); + else { + mmi_err(chg, "Couldn't set Fsw: invalid voltage %d\n", + voltage); + return -EINVAL; + } + + return rc; +} + +#define PD_SRC_PDO_TYPE_FIXED 0 +#define PD_SRC_PDO_TYPE_BATTERY 1 +#define PD_SRC_PDO_TYPE_VARIABLE 2 +#define PD_SRC_PDO_TYPE_AUGMENTED 3 +#define PPS_VOLT_MIN 5000000 +#define PPS_CURR_MIN 2000000 +#define PPS_CURR_MAX 3000000 +static void mmi_chrg_usb_vin_pd_config(struct smb_mmi_charger *chg, int vbus_mv) +{ + int rc = -EINVAL, i =0, req_pdo = -1, req_pd_volt = 0, req_pd_curr = 0; + int fixed_pd_volt = 0; + union power_supply_propval val = {0, }; + bool pd_active = false, vbus_present = false, pps_active = false, fixed_active = false; + static bool fsw_setted = false, fixed_power_done = false; + + if (!chg->usb_psy || !chg->pd_handle || chg->cp_active) + return; + + rc = get_prop_usb_present(chg, &val); + if (rc) { + mmi_err(chg, "Unable to read USB PRESENT: %d\n", rc); + return; + } + vbus_present = val.intval; + + rc = smb_mmi_read_iio_chan(chg, SMB5_PD_ACTIVE, &val.intval); + if (rc) { + mmi_err(chg, "Unable to read PD ACTIVE: %d\n", rc); + return; + } + pd_active = val.intval; + + if (!pd_active || !vbus_present) { + fsw_setted = false; + fixed_power_done = false; + chg->pd_power = 0; + return; + } + + memset(chg->mmi_pdo_info, 0, + sizeof(struct usbpd_pdo_info) * PD_MAX_PDO_NUM); + + rc = usbpd_get_pdo_info(chg->pd_handle, chg->mmi_pdo_info, PD_MAX_PDO_NUM); + if (rc ) { + mmi_err(chg, "PD not ready, %d\n", rc); + return; + } + + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + if ((chg->mmi_pdo_info[i].type == PD_SRC_PDO_TYPE_AUGMENTED) + && chg->mmi_pdo_info[i].uv_max >= PPS_VOLT_MIN + && chg->mmi_pdo_info[i].ua >= PPS_CURR_MIN + && (chg->mmi_pdo_info[i].uv_max / 1000) * (chg->mmi_pdo_info[i].ua / 1000) / 1000 + >= chg->pd_power_max) { + req_pdo = chg->mmi_pdo_info[i].pdo_pos; + req_pd_curr = chg->mmi_pdo_info[i].ua; + + if (req_pd_curr > PPS_CURR_MAX) + req_pd_curr = PPS_CURR_MAX; + + req_pd_volt = (chg->pd_power_max * 1000 / (req_pd_curr / 1000)) * 1000 % 20000; + req_pd_volt = (chg->pd_power_max * 1000 / (req_pd_curr / 1000)) * 1000 - req_pd_volt; + req_pd_volt = max(req_pd_volt, PPS_VOLT_MIN); + req_pd_volt = min(req_pd_volt, chg->mmi_pdo_info[i].uv_max); + chg->pd_power = chg->pd_power_max; + pps_active = true; + break; + } + } + + if (!pps_active) { + + if (chg->pd_power_max < CHARGER_POWER_18W) + fixed_pd_volt = MICRO_5V; + else + fixed_pd_volt = MICRO_9V; + + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + if ((chg->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_FIXED) + && chg->mmi_pdo_info[i].uv_max <= fixed_pd_volt + && chg->mmi_pdo_info[i].uv_max >= MICRO_5V) { + req_pdo = chg->mmi_pdo_info[i].pdo_pos; + req_pd_volt = chg->mmi_pdo_info[i].uv_max; + req_pd_curr = min((chg->pd_power_max / (req_pd_volt / 1000)) * 1000,chg->mmi_pdo_info[i].ua); + chg->pd_power = ((req_pd_volt/1000) * req_pd_curr)/1000; + fixed_active = true; + } + } + + if (fixed_active == true && vbus_mv * 1000 < req_pd_volt - MICRO_1V) + fixed_power_done = false; + } + + if (!pps_active && !fixed_active) + return; + else if (!fsw_setted){ + smblib_set_usb_pd_fsw(chg, req_pd_volt); + fsw_setted = true; + } + + if (!fixed_power_done && fixed_active) { + rc = usbpd_select_pdo(chg->pd_handle, req_pdo, req_pd_volt, req_pd_curr); + if (rc < 0) { + mmi_err(chg, "select pdo failed\n"); + return; + } + val.intval = req_pd_curr * 1000; /* mA->uA */ + rc = smb_mmi_write_iio_chan(chg, SMB5_PD_CURRENT_MAX, val.intval); + if (rc) { + mmi_err(chg, "set PD current MAX failed\n"); + return; + } + fixed_power_done = true; + mmi_err(chg, "Request fixed power , re-vote USB_ICL %duA\n", val.intval); + } else if (pps_active){ + rc = usbpd_select_pdo(chg->pd_handle, req_pdo, req_pd_volt, req_pd_curr); + if (rc < 0) { + mmi_err(chg, "select pdo failed\n"); + return; + } + } + + mmi_err(chg, + "Request PD power, fixed %d, pps %d, pdo %d, " + "req volt %dmV, req curr %dmA, vbus %dmV\n", + fixed_active, + pps_active, + req_pdo, + req_pd_volt, + req_pd_curr, + vbus_mv); + return; +} + +static void mmi_chrg_usb_vin_config(struct smb_mmi_charger *chg, int cur_mv) +{ + int rc = -EINVAL; + int val = 0; + + if (!chg->usb_psy || !chg->qcom_psy) + return; + + chg->pd_pps_active = 0; + chg->real_charger_type = 0; + + if (cur_mv < HVDCP_VOLTAGE_MIN) + return; + + rc = smb_mmi_read_iio_chan(chg, SMB5_PD_ACTIVE, &val); + if (rc < 0) { + mmi_err(chg, "Couldn't read PD active rc=%d\n", rc); + } else if (val) { + mmi_dbg(chg, "Skip usb vbus voltage config for PD charger\n"); + chg->pd_pps_active = val; + return; + } + + rc = smb_mmi_read_iio_chan(chg, SMB5_USB_REAL_TYPE, &val); + if (rc < 0) { + mmi_err(chg, "Couldn't read charger type rc=%d\n", rc); + return; + } + chg->real_charger_type = val; + if (val == QTI_POWER_SUPPLY_TYPE_USB_HVDCP && chg->hvdcp2_force_9v) { + val = QTI_POWER_SUPPLY_DP_DM_FORCE_9V; + rc = smb_mmi_write_iio_chan(chg, SMB5_DP_DM, val); + if (rc < 0) { + mmi_err(chg, "Couldn't set force HVDCP2 9V rc=%d\n", rc); + } + return; + } + + if (chg->real_charger_type != QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3) + return; + + if (chg->hvdcp_power_max > CHARGER_POWER_15W) { + mmi_increase_vbus_power(chg, cur_mv); + return; + } + + /* Maintain vbus voltage for QC3.0 5V/3A 15W charging */ + rc = smb_mmi_read_iio_chan(chg, SMB5_DP_DM, &val); + if (rc < 0) { + mmi_err(chg, "Couldn't read dpdm pulse count rc=%d\n", rc); + return; + } else + mmi_info(chg, "DP DM pulse count = %d\n", val); + + if (cur_mv < HVDCP_VOLTAGE_NOM && val < chg->inc_hvdcp_cnt) + val = QTI_POWER_SUPPLY_DP_DM_DP_PULSE; + else if (cur_mv > HVDCP_VOLTAGE_MAX && val > 0) + val = QTI_POWER_SUPPLY_DP_DM_DM_PULSE; + else { + mmi_dbg(chg, "No need to change hvdcp voltage\n"); + return; + } + rc = smb_mmi_write_iio_chan(chg, SMB5_DP_DM, val); + if (rc < 0) { + mmi_err(chg, "Couldn't set dpdm pulse rc=%d\n", rc); + return; + } + power_supply_changed(chg->usb_psy); +} + +static void mmi_chrg_input_config(struct smb_mmi_charger *chg, + struct smb_mmi_chg_status *stat) +{ + if (chg->hvdcp_power_max) + mmi_chrg_usb_vin_config(chg, stat->usb_mv); + + if (chg->pd_power_max) + mmi_chrg_usb_vin_pd_config(chg, stat->usb_mv); +} + +static void mmi_weakcharger_work(struct work_struct *work) +{ + struct smb_mmi_charger *chip = container_of(work, + struct smb_mmi_charger, + weakcharger_work.work); + + set_bit(EVENT_WEAK_CHARGER_WAIT, &chip->flags); + cancel_delayed_work(&chip->heartbeat_work); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(100)); + + mmi_dbg(chip, "Weak timer expired\n"); +} + +int is_wls_online(struct smb_mmi_charger *chg) +{ + int rc; + union power_supply_propval val; + + if (!chg->wls_psy) + return 0; + + rc = power_supply_get_property(chg->wls_psy, + POWER_SUPPLY_PROP_ONLINE, &val); + if (rc < 0) { + mmi_err(chg, "Error wls online rc = %d\n", rc); + return 0; + } + + return val.intval; +} + +#define WEAK_CHRG_THRSH 450 +#define TURBO_CHRG_THRSH 2500 +#define PD_CHRG_THRSH 8500 +void mmi_chrg_rate_check(struct smb_mmi_charger *chg) +{ + union power_supply_propval val; + union power_supply_propval wls_val; + int chrg_v_mv = 0; + int chrg_cm_ma = 0; + int chrg_cs_ma = 0; + int rc = -EINVAL; + + if (!chg->usb_psy) { + mmi_err(chg, "No usb PSY\n"); + return; + } + + val.intval = 0; + chg->prev_chg_rate = chg->charger_rate; + + rc = get_prop_charger_present(chg, &val); + if (rc < 0) { + mmi_err(chg, "Error getting Charger Present rc = %d\n", rc); + return; + } + + if (val.intval && is_wls_online(chg)) { + rc = power_supply_get_property(chg->wls_psy, + POWER_SUPPLY_PROP_CHARGE_TYPE, &wls_val); + if (rc >= 0 && wls_val.intval == POWER_SUPPLY_CHARGE_TYPE_FAST) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_TURBO; + else + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_NORMAL; + goto end_rate_check; + } + + if (val.intval) { + val.intval = 0; + rc = smb_mmi_read_iio_chan(chg, SMB5_HW_CURRENT_MAX, &val.intval); + if (rc < 0) { + mmi_err(chg, "Error getting HW Current Max rc = %d\n", rc); + return; + } + chrg_cm_ma = val.intval / 1000; + + val.intval = 0; + rc = smb_mmi_read_iio_chan(chg, + SMB5_USB_INPUT_CURRENT_SETTLED, &val.intval); + if (rc < 0) { + mmi_err(chg, "Error getting ICL Settled rc = %d\n", rc); + return; + } + chrg_cs_ma = val.intval / 1000; + + rc = get_prop_usb_voltage_now(chg, &val); + if (rc != -ENOMEM && rc < 0) { + mmi_err(chg, "Error getting USB Input Voltage rc = %d\n", rc); + return; + } + chrg_v_mv = val.intval / 1000; + } else { + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_NONE; + goto end_rate_check; + } + + mmi_dbg(chg, "cm %d, cs %d, v %d\n", chrg_cm_ma, chrg_cs_ma, chrg_v_mv); + if (chrg_cm_ma >= TURBO_CHRG_THRSH || chrg_v_mv >= PD_CHRG_THRSH) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_TURBO; + else if ((chrg_cm_ma > WEAK_CHRG_THRSH) && + (chrg_cs_ma < WEAK_CHRG_THRSH) && + test_bit(EVENT_WEAK_CHARGER_WAIT, &chg->flags)) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_WEAK; + else if (chg->prev_chg_rate == POWER_SUPPLY_CHARGE_RATE_NONE) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_NORMAL; + else if ((chg->prev_chg_rate == POWER_SUPPLY_CHARGE_RATE_WEAK) && + (chrg_cm_ma > WEAK_CHRG_THRSH) && + (chrg_cs_ma >= WEAK_CHRG_THRSH)) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_NORMAL; + +end_rate_check: + if (chg->prev_chg_rate != chg->charger_rate) { + clear_bit(EVENT_WEAK_CHARGER_WAIT, &chg->flags); + cancel_delayed_work(&chg->weakcharger_work); + if(chg->charger_rate == POWER_SUPPLY_CHARGE_RATE_NORMAL) + schedule_delayed_work(&chg->weakcharger_work, + msecs_to_jiffies(WEAK_CHARGER_WAIT)); + + mmi_info(chg, "%s Charger Detected\n", + charge_rate[chg->charger_rate]); + } +} + +#define TAPER_COUNT 2 +#define TAPER_DROP_MA 100 +static bool mmi_has_current_tapered(int batt, struct smb_mmi_charger *chg, + struct mmi_sm_params *chip, + int batt_ma, int taper_ma) +{ + bool change_state = false; + int allowed_fcc, target_ma; + + if (!chg) { + mmi_dbg(chg, "called before chip valid!\n"); + return false; + } + + allowed_fcc = get_effective_result(chg->fcc_votable) / 1000; + + if (allowed_fcc >= taper_ma) + target_ma = taper_ma; + else + target_ma = allowed_fcc - TAPER_DROP_MA; + + if (batt == BASE_BATT) + batt_ma *= -1; + + if (batt_ma < 0) { + if (chip->chrg_taper_cnt >= TAPER_COUNT) { + change_state = true; + chip->chrg_taper_cnt = 0; + } else + chip->chrg_taper_cnt++; + } else { + if (batt_ma <= target_ma) + if (chip->chrg_taper_cnt >= TAPER_COUNT) { + change_state = true; + chip->chrg_taper_cnt = 0; + } else + chip->chrg_taper_cnt++; + else + chip->chrg_taper_cnt = 0; + } + + return change_state; +} + +bool mmi_charge_halted(struct smb_mmi_charger *chg) +{ + u8 stat; + int rc; + bool flag = false; + + rc = smblib_read_mmi(chg, BATTERY_CHARGER_STATUS_1_REG, &stat); + if (rc < 0) { + mmi_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", + rc); + return flag; + } + stat = stat & BATTERY_CHARGER_STATUS_MASK; + + if (chg->smb_version == PM8150B || + chg->smb_version == PM7250B || + chg->smb_version == PMI632) { + switch (stat) { + case TERMINATE_CHARGE_V5: + case INHIBIT_CHARGE_V5: + flag = true; + break; + default: + break; + } + } else { + switch (stat) { + case TERMINATE_CHARGE: + case INHIBIT_CHARGE: + flag = true; + break; + default: + break; + } + } + + return flag; +} + +static int mmi_dual_charge_sm(struct smb_mmi_charger *chg, + struct smb_mmi_chg_status *stat, + int batt, int fv_offset) +{ + int max_fv_mv; + int i; + struct mmi_temp_zone *zone; + struct mmi_sm_params *chip = &chg->sm_param[batt]; + int start_tz = chip->pres_temp_zone; + int start_step = chip->pres_chrg_step; + + if (!chip->temp_zones) { + mmi_err(chg, "No Temp Zone Defined for batt %d!\n", batt); + return -ENODEV; + } + + if (chg->base_fv_mv == 0) { + chg->base_fv_mv = get_effective_result(chg->fv_votable); + chg->base_fv_mv /= 1000; + vote(chg->fv_votable, + BATT_PROFILE_VOTER, false, 0); + } + max_fv_mv = chg->base_fv_mv; + + mmi_find_temp_zone(chg, chip, stat->batt_temp); + zone = &chip->temp_zones[chip->pres_temp_zone]; + + if (!stat->charger_present) { + chip->pres_chrg_step = STEP_NONE; + for (i = 0; i < MAX_NUM_STEPS; i++) + chip->ocp[i] = 0; + } else if ((chip->pres_temp_zone == ZONE_HOT) || + (chip->pres_temp_zone == ZONE_COLD) || + (chg->charging_limit_modes == CHARGING_LIMIT_RUN)) { + chip->pres_chrg_step = STEP_STOP; + } else if (chg->demo_mode) { /* Demo Mode */ + chip->pres_chrg_step = STEP_DEMO; + } else if ((chip->pres_chrg_step == STEP_NONE) || + (chip->pres_chrg_step == STEP_STOP)) { + if (zone->norm_mv && (stat->batt_mv >= zone->norm_mv)) { + if (zone->fcc_norm_ma) + chip->pres_chrg_step = STEP_NORM; + else + chip->pres_chrg_step = STEP_STOP; + } else + chip->pres_chrg_step = STEP_MAX; + } else if (chip->pres_chrg_step == STEP_MAX) { + if (!zone->norm_mv) { + /* No Step in this Zone */ + chip->chrg_taper_cnt = 0; + if ((stat->batt_mv + fv_offset) >= max_fv_mv) + chip->pres_chrg_step = STEP_NORM; + else + chip->pres_chrg_step = STEP_MAX; + } else if ((stat->batt_mv + fv_offset) < zone->norm_mv) { + chip->chrg_taper_cnt = 0; + chip->pres_chrg_step = STEP_MAX; + } else if (!zone->fcc_norm_ma) + chip->pres_chrg_step = STEP_FLOAT; + else if (mmi_has_current_tapered(batt, chg, chip, stat->batt_ma, + zone->fcc_norm_ma)) { + chip->chrg_taper_cnt = 0; + for (i = 0; i < MAX_NUM_STEPS; i++) + chip->ocp[i] = 0; + chip->pres_chrg_step = STEP_NORM; + } + } else if (chip->pres_chrg_step == STEP_NORM) { + if (!zone->fcc_norm_ma) + chip->pres_chrg_step = STEP_STOP; + else if ((stat->batt_soc < 100) || + (stat->batt_mv + fv_offset) < max_fv_mv) { + chip->chrg_taper_cnt = 0; + chip->pres_chrg_step = STEP_NORM; + } else if (mmi_has_current_tapered(batt, chg, chip, stat->batt_ma, + chip->chrg_iterm)) { + chip->pres_chrg_step = STEP_FULL; + } + } else if (chip->pres_chrg_step == STEP_FULL) { + if (stat->batt_soc <= 99) { + chip->chrg_taper_cnt = 0; + chip->pres_chrg_step = STEP_NORM; + } + } else if (chip->pres_chrg_step == STEP_FLOAT) { + if ((zone->fcc_norm_ma) || + ((stat->batt_mv + fv_offset) < zone->norm_mv)) + chip->pres_chrg_step = STEP_MAX; + } + + /* Take State actions */ + switch (chip->pres_chrg_step) { + case STEP_FLOAT: + case STEP_MAX: + if (!zone->norm_mv) + chip->target_fv = max_fv_mv; + else + chip->target_fv = zone->norm_mv; + chip->target_fcc = zone->fcc_max_ma; + break; + case STEP_FULL: + chip->target_fv = max_fv_mv; + chip->target_fcc = -EINVAL; + break; + case STEP_NORM: + chip->target_fv = max_fv_mv + chg->vfloat_comp_mv; + chip->target_fcc = zone->fcc_norm_ma; + break; + case STEP_NONE: + chip->target_fv = max_fv_mv; + chip->target_fcc = zone->fcc_norm_ma; + break; + case STEP_STOP: + chip->target_fv = max_fv_mv; + chip->target_fcc = -EINVAL; + break; + case STEP_DEMO: + chip->target_fv = DEMO_MODE_VOLTAGE; + chip->target_fcc = zone->fcc_max_ma; + break; + default: + chip->target_fv = max_fv_mv; + chip->target_fcc = zone->fcc_norm_ma; + break; + } + + if (chip->pres_temp_zone == ZONE_HOT) { + chip->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; + } else if (chip->pres_temp_zone == ZONE_COLD) { + chip->batt_health = POWER_SUPPLY_HEALTH_COLD; + } else if (stat->batt_temp >= WARM_TEMP) { + if (chip->pres_chrg_step == STEP_STOP) + chip->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; + else + chip->batt_health = POWER_SUPPLY_HEALTH_GOOD; + } else if (stat->batt_temp <= COOL_TEMP) { + if (chip->pres_chrg_step == STEP_STOP) + chip->batt_health = POWER_SUPPLY_HEALTH_COLD; + else + chip->batt_health = POWER_SUPPLY_HEALTH_GOOD; + } else + chip->batt_health = POWER_SUPPLY_HEALTH_GOOD; + + if ((start_tz != chip->pres_temp_zone) || + (start_step != chip->pres_chrg_step)) { + mmi_info(chg, "Batt %d: batt_mv = %d, batt_ma %d, batt_soc %d," + " batt_temp %d, usb_mv %d, cp %d, vp %d\n", + batt, + stat->batt_mv, + stat->batt_ma, + stat->batt_soc, + stat->batt_temp, + stat->usb_mv, + stat->charger_present, + stat->vbus_present); + mmi_info(chg, "Batt %d Step State = %s, Temp Zone %d, Health %d\n", + batt, + stepchg_str[(int)chip->pres_chrg_step], + chip->pres_temp_zone, + chip->batt_health); + chg->hb_startup_cnt = HEARTBEAT_OCP_SETTLE_CNT; + chg->ocp_flag = true; // 1x settle per state chng + return 1; + } else { + mmi_dbg(chg, "Batt %d: batt_mv = %d, batt_ma %d, batt_soc %d," + " batt_temp %d, usb_mv %d, cp %d, vp %d\n", + batt, + stat->batt_mv, + stat->batt_ma, + stat->batt_soc, + stat->batt_temp, + stat->usb_mv, + stat->charger_present, + stat->vbus_present); + mmi_dbg(chg, "Batt %d Step State = %s, Temp Zone %d, Health %d\n", + batt, + stepchg_str[(int)chip->pres_chrg_step], + chip->pres_temp_zone, + chip->batt_health); + } + + return 0; +} + +static int mmi_dual_charge_control(struct smb_mmi_charger *chg, + struct smb_mmi_chg_status *stat) +{ + int rc; + int target_fcc; + int target_fv; + int effective_fv; + int effective_fcc; + int scaled_fcc =0; + int ocp; + int sm_update; + bool voltage_full; + int i; + int sched_time = HEARTBEAT_DUAL_DELAY_MS; + struct smb_mmi_chg_status chg_stat_main, chg_stat_flip; + union power_supply_propval pval; + struct mmi_sm_params *main_p = &chg->sm_param[MAIN_BATT]; + struct mmi_sm_params *flip_p = &chg->sm_param[FLIP_BATT]; + static int demo_full_soc = 100; + bool is_chg_dis = get_effective_result(chg->chg_dis_votable); + + chg_stat_main.charger_present = stat->charger_present; + chg_stat_flip.charger_present = stat->charger_present; + chg_stat_main.vbus_present = stat->vbus_present; + chg_stat_flip.vbus_present = stat->vbus_present; + chg_stat_main.usb_mv = stat->usb_mv; + chg_stat_flip.usb_mv = stat->usb_mv; + + rc = get_prop_batt_voltage_now(chg, chg->max_main_psy, &pval); + if (rc < 0) { + mmi_err(chg, "Error getting Main Batt Voltage rc = %d\n", rc); + return sched_time; + } else + chg_stat_main.batt_mv = pval.intval / 1000; + + rc = get_prop_batt_voltage_now(chg, chg->max_flip_psy, &pval); + if (rc < 0) { + mmi_err(chg, "Error getting Flip Batt Voltage rc = %d\n", rc); + return sched_time; + } else + chg_stat_flip.batt_mv = pval.intval / 1000; + + rc = get_prop_batt_current_now(chg, chg->max_main_psy, &pval); + if (rc < 0) { + mmi_err(chg, "Error getting Main Batt Current rc = %d\n", rc); + return sched_time; + } else + chg_stat_main.batt_ma = (pval.intval / 1000) * -1; + + rc = get_prop_batt_current_now(chg, chg->max_flip_psy, &pval); + if (rc < 0) { + mmi_err(chg, "Error getting Flip Batt Current rc = %d\n", rc); + return sched_time; + } else + chg_stat_flip.batt_ma = (pval.intval / 1000) * -1; + + rc = get_prop_batt_capacity(chg, chg->max_main_psy, &pval); + if (rc < 0) { + mmi_err(chg, "Error getting Main Batt Capacity rc = %d\n", rc); + return sched_time; + } else + chg_stat_main.batt_soc = pval.intval; + + rc = get_prop_batt_capacity(chg, chg->max_flip_psy, &pval); + if (rc < 0) { + mmi_err(chg, "Error getting Flip Batt Capacity rc = %d\n", rc); + return sched_time; + } else + chg_stat_flip.batt_soc = pval.intval; + + rc = get_prop_batt_temp(chg, chg->max_main_psy, &pval); + if (rc < 0) { + mmi_err(chg, "Error getting Main Batt Temperature rc = %d\n", rc); + return sched_time; + } else + chg_stat_main.batt_temp = pval.intval / 10; + + rc = get_prop_batt_temp(chg, chg->max_flip_psy, &pval); + if (rc < 0) { + mmi_err(chg, "Error getting Flip Batt Temperature rc = %d\n", rc); + return sched_time; + } else + chg_stat_flip.batt_temp = pval.intval / 10; + + sm_update = mmi_dual_charge_sm(chg, &chg_stat_main, + MAIN_BATT, HYST_STEP_MV); + if (sm_update < 0) + return sched_time; + + sm_update = mmi_dual_charge_sm(chg, &chg_stat_flip, + FLIP_BATT, HYST_STEP_FLIP_MV); + if (sm_update < 0) + return sched_time; + + effective_fv = get_effective_result(chg->fv_votable) / 1000; + effective_fcc = get_effective_result(chg->fcc_votable); + + /* OCP Check - Scale down fcc if too high for initial set */ + if (chg->hb_startup_cnt > 1) { + chg->hb_startup_cnt--; + sched_time = HEARTBEAT_DUAL_DELAY_OCP_MS; + } else if ( (effective_fcc >= (main_p->target_fcc + flip_p->target_fcc) *1000) && + (chg->ocp_flag) ) { + chg->ocp_flag = false; + sched_time = HEARTBEAT_DUAL_DELAY_OCP_MS; + chg->hb_startup_cnt = HEARTBEAT_OCP_SETTLE_CNT; + scaled_fcc = effective_fcc /10; + effective_fcc -= scaled_fcc; + mmi_dbg(chg, "fccScaled: %d, AdjFCC: %d\n", scaled_fcc, effective_fcc); + main_p->ocp[main_p->pres_temp_zone] =0; + flip_p->ocp[flip_p->pres_temp_zone] =0; + } + + /* Check for Charge None */ + if ((main_p->pres_chrg_step == STEP_NONE) || + (flip_p->pres_chrg_step == STEP_NONE)) { + chg->sm_param[BASE_BATT].pres_chrg_step = STEP_NONE; + target_fcc = main_p->target_fcc; + target_fv = chg->base_fv_mv; + sched_time = HEARTBEAT_DELAY_MS; + goto vote_now; + /* Check for Charge Demo */ + } else if ((main_p->pres_chrg_step == STEP_DEMO) || + (flip_p->pres_chrg_step == STEP_DEMO)) { + mmi_info(chg, "Battery in Demo Mode charging limited " + "%d%%\n", chg->demo_mode); + + voltage_full = ((chg->demo_mode_usb_suspend == false) && + ((stat->batt_mv + HYST_STEP_MV) >= DEMO_MODE_VOLTAGE) + && mmi_has_current_tapered(MAIN_BATT, chg, main_p, stat->batt_ma, + (main_p->chrg_iterm * 2))); + + if ((chg->demo_mode_usb_suspend == false) && + ((chg->last_reported_soc >= chg->demo_mode) || + voltage_full)) { + demo_full_soc = chg->last_reported_soc; + vote(chg->usb_icl_votable, DEMO_VOTER, true, 0); + vote(chg->dc_suspend_votable, DEMO_VOTER, true, 1); + chg->demo_mode_usb_suspend = true; + main_p->chrg_taper_cnt = 0; + flip_p->chrg_taper_cnt = 0; + for (i = 0; i < MAX_NUM_STEPS; i++) { + main_p->ocp[i] = 0; + flip_p->ocp[i] = 0; + } + } else if (chg->demo_mode_usb_suspend == true && + (chg->last_reported_soc <= + (demo_full_soc - DEMO_MODE_HYS_SOC))) { + vote(chg->usb_icl_votable, DEMO_VOTER, false, 0); + vote(chg->dc_suspend_votable, DEMO_VOTER, false, 0); + chg->demo_mode_usb_suspend = false; + main_p->chrg_taper_cnt = 0; + flip_p->chrg_taper_cnt = 0; + for (i = 0; i < MAX_NUM_STEPS; i++) { + main_p->ocp[i] = 0; + flip_p->ocp[i] = 0; + } + } + + target_fv = DEMO_MODE_VOLTAGE; + target_fcc = main_p->target_fcc; + goto vote_now; + /* Check for Charge FULL from each */ + } else if ((main_p->pres_chrg_step == STEP_FULL) && + (flip_p->pres_chrg_step == STEP_FULL)) { + chg->sm_param[BASE_BATT].pres_chrg_step = STEP_FULL; + target_fcc = -EINVAL; + target_fv = chg->base_fv_mv; + sched_time = HEARTBEAT_DELAY_MS; + chg->last_reported_status = POWER_SUPPLY_STATUS_FULL; + goto vote_now; + /* Align FULL between batteries */ + } else if ((main_p->pres_chrg_step == STEP_FULL) && + ((flip_p->pres_chrg_step == STEP_MAX) || + (flip_p->pres_chrg_step == STEP_NORM)) && + (chg_stat_flip.batt_soc >= 95) && + ((chg_stat_flip.batt_mv + HYST_STEP_FLIP_MV) >= + chg->base_fv_mv)) { + chg->sm_param[BASE_BATT].pres_chrg_step = + flip_p->pres_chrg_step; + target_fcc = flip_p->target_fcc; + target_fv = flip_p->target_fv; + mmi_info(chg, "Align Flip to Main FULL\n"); + sched_time = HEARTBEAT_DUAL_DELAY_MS; + goto vote_now; + } else if ((flip_p->pres_chrg_step == STEP_FULL) && + ((main_p->pres_chrg_step == STEP_MAX) || + (main_p->pres_chrg_step == STEP_NORM)) && + (chg_stat_main.batt_soc >= 95) && + ((chg_stat_main.batt_mv + HYST_STEP_MV) >= + chg->base_fv_mv)) { + chg->sm_param[BASE_BATT].pres_chrg_step = + main_p->pres_chrg_step; + target_fcc = main_p->target_fcc; + target_fv = main_p->target_fv; + mmi_info(chg, "Align Main to Flip FULL\n"); + sched_time = HEARTBEAT_DUAL_DELAY_MS; + goto vote_now; + /* Check for Charge Disable from each */ + } else if ((main_p->target_fcc < 0) || + (flip_p->target_fcc < 0)) { + chg->sm_param[BASE_BATT].pres_chrg_step = STEP_STOP; + target_fcc = -EINVAL; + target_fv = chg->base_fv_mv; + goto vote_now; + } + + chg->sm_param[BASE_BATT].pres_chrg_step = main_p->pres_chrg_step; + chg->last_reported_status = -1; + if (main_p->target_fv < flip_p->target_fv) + target_fv = main_p->target_fv; + else + target_fv = flip_p->target_fv; + + if ((chg_stat_main.batt_ma > main_p->target_fcc) && + chg->hb_startup_cnt %2) { //Allow settle time + sched_time = HEARTBEAT_DUAL_DELAY_OCP_MS; + ocp = chg_stat_main.batt_ma - main_p->target_fcc; + main_p->ocp[main_p->pres_temp_zone] += ocp; + mmi_info(chg, "Main Exceed by %d mA\n", + main_p->ocp[main_p->pres_temp_zone]); + } + if ((chg_stat_flip.batt_ma > flip_p->target_fcc) && + chg->hb_startup_cnt %2) { //Allow settle time + sched_time = HEARTBEAT_DUAL_DELAY_OCP_MS; + ocp = chg_stat_flip.batt_ma - flip_p->target_fcc; + flip_p->ocp[flip_p->pres_temp_zone] += ocp; + mmi_info(chg, "Flip Exceed by %d mA\n", + flip_p->ocp[flip_p->pres_temp_zone]); + } + + target_fcc = main_p->target_fcc + flip_p->target_fcc; + target_fcc -= scaled_fcc/1000; + target_fcc -= main_p->ocp[main_p->pres_temp_zone]; + target_fcc -= flip_p->ocp[flip_p->pres_temp_zone]; + if ( (target_fcc < main_p->target_fcc) && + (chg_stat_flip.batt_ma < flip_p->target_fcc *7/10) ) { + mmi_info(chg, "Target FCC adjust too much\n"); + chg->hb_startup_cnt = HEARTBEAT_OCP_SETTLE_CNT; + if (main_p->target_fcc < flip_p->target_fcc) + target_fcc = (main_p->target_fcc)*5/2; + else + target_fcc = (flip_p->target_fcc)*5/2; + main_p->ocp[main_p->pres_temp_zone] =0; + flip_p->ocp[flip_p->pres_temp_zone] =0; + } + + if (((main_p->pres_chrg_step == STEP_MAX) || + (flip_p->pres_chrg_step == STEP_MAX) || + (main_p->pres_chrg_step == STEP_NORM) || + (flip_p->pres_chrg_step == STEP_NORM)) && + mmi_charge_halted(chg)) { + vote(chg->chg_dis_votable, + MMI_HB_VOTER, true, 0); + mmi_err(chg, "Charge Halt..Toggle\n"); + msleep(50); + } + +vote_now: + /* Votes for State */ + vote(chg->fv_votable, MMI_HB_VOTER, true, target_fv * 1000); + + vote(chg->chg_dis_votable, MMI_HB_VOTER, + (target_fcc < 0), 0); + + vote(chg->fcc_votable, MMI_HB_VOTER, + true, (target_fcc >= 0) ? (target_fcc * 1000) : 0); + + /* Rerun AICL to recover USB ICL from recharge */ + if (is_chg_dis != get_effective_result(chg->chg_dis_votable)) { + smb_mmi_write_iio_chan(chg, + SMB5_RERUN_AICL, 0); + } + + if (!stat->charger_present && + (chg_stat_main.batt_mv <= LOW_BATT_MV) && + (chg_stat_flip.batt_mv <= LOW_BATT_MV)) { + if ((chg_stat_main.batt_mv <= SHUT_BATT_MV) || + (chg_stat_flip.batt_mv <= SHUT_BATT_MV)) + chg->shut_batt = true; + } else + chg->shut_batt = false; + + if (sm_update) + mmi_info(chg, "IMPOSED: FV = %d, CDIS = %d, FCC = %d, USBICL = %d\n", + effective_fv, + get_effective_result(chg->chg_dis_votable), + target_fcc, + get_effective_result(chg->usb_icl_votable)); + else + mmi_dbg(chg, "IMPOSED: FV = %d, CDIS = %d, FCC = %d, USBICL = %d\n", + effective_fv, + get_effective_result(chg->chg_dis_votable), + target_fcc, + get_effective_result(chg->usb_icl_votable)); + + return sched_time; +} + +static int mmi_set_qg_iterm(struct smb_mmi_charger *chip, int qg_iterm) +{ + int rc = 0; + int fcc = 0; + union power_supply_propval val; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, &val); + if (rc < 0) { + mmi_err(chip, "Couldn't get batt FCC, rc=%d\n", rc); + return rc; + } + + fcc = val.intval / 1000; + + if (chip->enable_fcc_large_qg_iterm && (qg_iterm > (fcc - 50))) + qg_iterm = fcc - 50; + + rc = smb_mmi_write_iio_chan(chip, + SMB5_QG_BATT_FULL_CURRENT, qg_iterm); + if (rc < 0) { + mmi_err(chip, "Couldn't set batt full current, rc=%d\n", rc); + } + + return rc; +} + +static int mmi_get_ffc_fv(struct smb_mmi_charger *chip, int zone, bool force_ffc) +{ + int rc; + int ffc_max_fv; + struct mmi_sm_params *prm = &chip->sm_param[BASE_BATT]; + + if (prm->ffc_zones == NULL || zone >= prm->num_temp_zones) + return chip->base_fv_mv; + + if ((force_ffc == false) && + (chip->noffc_chg_iterm != -EINVAL) && + (chip->noffc_qg_iterm != -EINVAL) && + (chip->noffc_max_fv != -EINVAL)) { + + if ((zone == ZONE_35C_TO_45C) && (chip->noffc_chg_iterm_45c != -EINVAL)) { + prm->chrg_iterm = chip->noffc_chg_iterm_45c; + + rc = mmi_set_qg_iterm(chip, + (chip->noffc_qg_iterm_45c != -EINVAL) ? + chip->noffc_qg_iterm_45c : chip->noffc_qg_iterm); + if (rc < 0) { + mmi_err(chip, "Couldn't set batt full current, rc=%d\n", rc); + } + } else { + prm->chrg_iterm = chip->noffc_chg_iterm; + + rc = mmi_set_qg_iterm(chip, chip->noffc_qg_iterm); + if (rc < 0) { + mmi_err(chip, "Couldn't set batt full current, rc=%d\n", rc); + } + } + ffc_max_fv = chip->noffc_max_fv; + mmi_info(chip,"NONEFFC temp zone %d, fv %d mV, chg iterm %d mA, qg iterm %d mA\n", + zone, ffc_max_fv, prm->chrg_iterm, chip->noffc_qg_iterm); + return ffc_max_fv; + } + + rc = mmi_set_qg_iterm(chip, prm->ffc_zones[zone].ffc_qg_iterm); + if (rc < 0) { + mmi_err(chip, "Couldn't set batt full current, rc=%d\n", rc); + } + + prm->chrg_iterm = prm->ffc_zones[zone].ffc_chg_iterm; + ffc_max_fv = prm->ffc_zones[zone].ffc_max_mv; + mmi_info(chip, + "FFC temp zone %d, fv %d mV, chg iterm %d mA, qg iterm %d mA\n", + zone, ffc_max_fv, prm->chrg_iterm, prm->ffc_zones[zone].ffc_qg_iterm); + + return ffc_max_fv; +} + +static void mmi_charger_ffc_init(struct smb_mmi_charger *chip) +{ + chip->ffc_state = CHARGER_FFC_STATE_INITIAL; + chip->ffc_ibat_windowsum = 0; + chip->ffc_ibat_count = 0; + chip->ffc_ibat_windowsize = 6; + chip->ffc_iavg = 0; + chip->ffc_iavg_update_timestamp = 0; + + chip->ffc_stop_chg = false; + mmi_info(chip,"ffc initilaize...\n"); +} + +static int mmi_charger_check_ffc_status(struct smb_mmi_charger *chip, struct smb_mmi_chg_status *stat) +{ + bool loop = true; + unsigned long target_timestamp; + int batt_ma, batt_mv; + struct mmi_sm_params *prm = &chip->sm_param[BASE_BATT]; + int max_fv_mv = mmi_get_ffc_fv(chip, prm->pres_temp_zone, false); + + batt_ma = stat->batt_ma * (-1); + batt_mv = stat->batt_mv; + + do { + + mmi_info(chip,"ffc_state:%d, charge stage:%d, uisoc:%d, chg_type:%d\n", chip->ffc_state, prm->pres_chrg_step, stat->batt_soc, chip->real_charger_type); + switch (chip->ffc_state) { + case CHARGER_FFC_STATE_INITIAL: + if (prm->pres_chrg_step == STEP_MAX) { + + if (stat->batt_soc >= chip->ffc_uisoc_threshold) { + chip->ffc_state = CHARGER_FFC_STATE_PROBING; + pr_debug("uisoc is up to %d, ffc probing starts\n", chip->ffc_uisoc_threshold); + } + } + + loop = false; + break; + case CHARGER_FFC_STATE_PROBING: + if (prm->pres_chrg_step == STEP_MAX || prm->pres_chrg_step == STEP_NORM) { + if (batt_ma < 0) { + mmi_info(chip,"still in discharging at:%d\n", batt_ma); + loop = false; + break; + } + + if (batt_ma > chip->ffc_entry_threshold) { + mmi_info(chip,"charging current can bump up to entry threshold:%d\n", chip->ffc_entry_threshold); + chip->ffc_state = CHARGER_FFC_STATE_STANDBY; + } + else if (batt_ma == 0) { + mmi_info(chip,"charger doesnt support to bump high level current\n"); + chip->ffc_state = CHARGER_FFC_STATE_INVALID; + } + else { + mmi_info(chip,"current: %d not reach ffc entry threshold\n", batt_ma); + loop = false; + } + } + else { + chip->ffc_state = CHARGER_FFC_STATE_INVALID; + mmi_info(chip,"invalid stage:%d, ffc session quit\n", prm->pres_chrg_step); + } + break; + case CHARGER_FFC_STATE_STANDBY: + if (prm->pres_chrg_step != STEP_MAX && prm->pres_chrg_step != STEP_NORM) { + mmi_info(chip,"invalid stage:%d in ffc_state:%d\n", prm->pres_chrg_step, chip->ffc_state); + chip->ffc_state = CHARGER_FFC_STATE_INVALID; + break; + } + + if (prm->pres_chrg_step == STEP_NORM && chip->ffc_iavg >= chip->ffc_entry_threshold) { + chip->ffc_state = CHARGER_FFC_STATE_GOREADY; + break; + } + + target_timestamp = chip->ffc_iavg_update_timestamp + msecs_to_jiffies(10 * 1000); + + if (time_is_before_eq_jiffies(target_timestamp) || chip->ffc_iavg_update_timestamp == 0) { + /* iavg update required */ + if (batt_ma < 0) { + loop = false; + mmi_info(chip,"still in discharging at:%d\n", batt_ma); + break; + } + + chip->ffc_iavg_update_timestamp = jiffies; + chip->ffc_ibat_count++; + chip->ffc_ibat_windowsum += batt_ma; + chip->ffc_iavg = chip->ffc_ibat_windowsum / chip->ffc_ibat_count; + mmi_info(chip,"iavg:%d, total:%ld, count:%ld\n", chip->ffc_iavg, chip->ffc_ibat_windowsum, chip->ffc_ibat_count); + } + + loop = false; + break; + case CHARGER_FFC_STATE_GOREADY: + if (prm->pres_chrg_step != STEP_NORM) { + if (prm->pres_chrg_step == STEP_MAX) { + chip->ffc_state = CHARGER_FFC_STATE_STANDBY; + } else { + mmi_info(chip,"invalid stage:%d in ffc_state:%d, quit ffc session\n", prm->pres_chrg_step, chip->ffc_state); + chip->ffc_state = CHARGER_FFC_STATE_INVALID; + } + break; + } + chip->ffc_ibat_count = 0; + chip->ffc_ibat_windowsum = 0; + chip->ffc_iavg = 0; + chip->ffc_iavg_update_timestamp = 0; + /* set target voltage as the ffc target */ + max_fv_mv = mmi_get_ffc_fv(chip, prm->pres_temp_zone, true); + chip->ffc_state = CHARGER_FFC_STATE_RUNNING; + loop = false; + mmi_info(chip,"target max_fv_mv:%d\n", max_fv_mv); + break; + case CHARGER_FFC_STATE_RUNNING: + if (prm->pres_chrg_step != STEP_NORM) { + if (prm->pres_chrg_step == STEP_MAX) { + chip->ffc_state = CHARGER_FFC_STATE_STANDBY; + } else { + mmi_info(chip,"invalid stage:%d in ffc_state:%d, quit ffc session\n", prm->pres_chrg_step, chip->ffc_state); + chip->ffc_state = CHARGER_FFC_STATE_INVALID; + } + break; + } + + if (batt_ma < 0) { + mmi_info(chip,"still in discharging at:%d, retry\n", batt_ma); + loop = false; + break; + } + + max_fv_mv = mmi_get_ffc_fv(chip, prm->pres_temp_zone, true); + + /* float down offset 5mV to fit in more robust ffc state */ + if (batt_mv < (max_fv_mv - 5)) { + chip->ffc_ibat_count++; + chip->ffc_ibat_windowsum += batt_ma; + loop = false; + + if ((chip->ffc_ibat_count % chip->ffc_ibat_windowsize) == 0) { + chip->ffc_iavg = chip->ffc_ibat_windowsum / chip->ffc_ibat_count; + if (chip->ffc_iavg <= chip->ffc_exit_threshold) { + loop = true; + chip->ffc_state = CHARGER_FFC_STATE_AVGEXIT; + } + mmi_info(chip,"ffc_iavg:%d in ffc, max_fv_mv:%d, vbatt:%d\n", chip->ffc_iavg, max_fv_mv, batt_mv); + } + } + else { + mmi_info(chip,"ffc charging to target voltage:%d, quit ffc session \n", max_fv_mv); + chip->ffc_state = CHARGER_FFC_STATE_FFCDONE; + break; + } + break; + case CHARGER_FFC_STATE_AVGEXIT: + max_fv_mv = mmi_get_ffc_fv(chip, prm->pres_temp_zone, false); + chip->ffc_ibat_count = 0; + chip->ffc_ibat_windowsum = 0; + chip->ffc_iavg = 0; + chip->ffc_stop_chg = true; + + chip->ffc_state = CHARGER_FFC_STATE_INVALID; + loop = false; + break; + case CHARGER_FFC_STATE_FFCDONE: + max_fv_mv = mmi_get_ffc_fv(chip, prm->pres_temp_zone, true); + mmi_info(chip,"ffc session done, max_fv_mv:%d\n", max_fv_mv); + loop = false; + break; + case CHARGER_FFC_STATE_INVALID: + max_fv_mv = mmi_get_ffc_fv(chip, prm->pres_temp_zone, false); + if (chip->ffc_stop_chg && max_fv_mv >= batt_mv) + chip->ffc_stop_chg = false; + loop = false; + break; + } + + } while (loop); + + return max_fv_mv; +} + +static void mmi_basic_charge_sm(struct smb_mmi_charger *chip, + struct smb_mmi_chg_status *stat) +{ + int target_fcc; + int target_fv; + int max_fv_mv; + struct mmi_temp_zone *zone; + struct mmi_sm_params *prm = &chip->sm_param[BASE_BATT]; + bool voltage_full; + bool is_chg_dis = get_effective_result(chip->chg_dis_votable); + static int demo_full_soc = 100; + + mmi_info(chip, "batt_mv = %d, batt_ma %d, batt_soc %d," + " batt_temp %d, usb_mv %d, dc_mv %d, cp %d, vp %d dp %d\n", + stat->batt_mv, + stat->batt_ma, + stat->batt_soc, + stat->batt_temp, + stat->usb_mv, + stat->dc_mv, + stat->charger_present, + stat->vbus_present, + stat->dc_present); + + if (!prm->temp_zones) { + mmi_dbg(chip, "Skipping SM since No Temp Zone Defined!\n"); + return; + } + + if (chip->base_fv_mv == 0) { + chip->base_fv_mv = get_effective_result(chip->fv_votable); + chip->base_fv_mv /= 1000; + vote(chip->fv_votable, + BATT_PROFILE_VOTER, false, 0); + } + + if (!chip->enable_dcp_ffc) { + mmi_info(chip,"real_charger_type=%d, pd_pps_active=%d\n",chip->real_charger_type, + chip->pd_pps_active); + if ((chip->real_charger_type != QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5) && + (chip->pd_pps_active != QTI_POWER_SUPPLY_PD_PPS_ACTIVE)) + max_fv_mv = mmi_get_ffc_fv(chip, prm->pres_temp_zone, false); + else + max_fv_mv = mmi_get_ffc_fv(chip, prm->pres_temp_zone, true); + } else + max_fv_mv = mmi_charger_check_ffc_status(chip, stat); + if (max_fv_mv == 0) + max_fv_mv = chip->base_fv_mv; + + mmi_find_temp_zone(chip, prm, stat->batt_temp); + if (prm->pres_temp_zone >= prm->num_temp_zones) + zone = &prm->temp_zones[0]; + else + zone = &prm->temp_zones[prm->pres_temp_zone]; + + if (!stat->charger_present && !is_wls_online(chip)) { + prm->pres_chrg_step = STEP_NONE; + mmi_charger_ffc_init(chip); + } else if ((prm->pres_temp_zone == ZONE_HOT) || + (prm->pres_temp_zone == ZONE_COLD) || + (chip->charging_limit_modes == CHARGING_LIMIT_RUN)) { + prm->pres_chrg_step = STEP_STOP; + } else if (chip->demo_mode) { /* Demo Mode */ + prm->pres_chrg_step = STEP_DEMO; + mmi_info(chip, "Battery in Demo Mode charging limited " + "%d%%\n", chip->demo_mode); + + voltage_full = ((chip->demo_mode_usb_suspend == false) && + ((stat->batt_mv + HYST_STEP_MV) >= DEMO_MODE_VOLTAGE) + && mmi_has_current_tapered(BASE_BATT, chip, prm, stat->batt_ma, + prm->chrg_iterm)); + + if ((chip->demo_mode_usb_suspend == false) && + ((stat->batt_soc >= chip->demo_mode) || voltage_full)) { + demo_full_soc = stat->batt_soc; + vote(chip->usb_icl_votable, DEMO_VOTER, true, 0); + vote(chip->dc_suspend_votable, DEMO_VOTER, true, 0); + chip->demo_mode_usb_suspend = true; + } else if (chip->demo_mode_usb_suspend == true && + (stat->batt_soc <= (demo_full_soc - DEMO_MODE_HYS_SOC))) { + vote(chip->usb_icl_votable, DEMO_VOTER, false, 0); + vote(chip->dc_suspend_votable, DEMO_VOTER, false, 0); + chip->demo_mode_usb_suspend = false; + prm->chrg_taper_cnt = 0; + } + } else if ((prm->pres_chrg_step == STEP_NONE) || + (prm->pres_chrg_step == STEP_STOP)) { + if (zone->norm_mv && ((stat->batt_mv + HYST_STEP_MV) >= zone->norm_mv)) { + if (zone->fcc_norm_ma) + prm->pres_chrg_step = STEP_NORM; + else + prm->pres_chrg_step = STEP_STOP; + } else + prm->pres_chrg_step = STEP_MAX; + } else if (prm->pres_chrg_step == STEP_MAX) { + if (!zone->norm_mv) { + /* No Step in this Zone */ + prm->chrg_taper_cnt = 0; + if ((stat->batt_mv + HYST_STEP_MV) >= max_fv_mv) + prm->pres_chrg_step = STEP_NORM; + else + prm->pres_chrg_step = STEP_MAX; + } else if ((stat->batt_mv + HYST_STEP_MV) < zone->norm_mv) { + prm->chrg_taper_cnt = 0; + prm->pres_chrg_step = STEP_MAX; + } else if (!zone->fcc_norm_ma) + prm->pres_chrg_step = STEP_FLOAT; + else if (mmi_has_current_tapered(BASE_BATT, chip, prm, stat->batt_ma, + zone->fcc_norm_ma)) { + prm->chrg_taper_cnt = 0; + + if (mmi_charge_halted(chip)) { + vote(chip->chg_dis_votable, + MMI_HB_VOTER, true, 0); + mmi_warn(chip, "Charge Halt..Toggle\n"); + msleep(50); + } + + prm->pres_chrg_step = STEP_NORM; + } + } else if (prm->pres_chrg_step == STEP_NORM) { + if (!zone->fcc_norm_ma) + prm->pres_chrg_step = STEP_FLOAT; + else if ((stat->batt_mv + HYST_STEP_MV/2) < max_fv_mv) { + prm->chrg_taper_cnt = 0; + prm->pres_chrg_step = STEP_NORM; + } else if (mmi_has_current_tapered(BASE_BATT, chip, prm, stat->batt_ma, + prm->chrg_iterm)) { + prm->pres_chrg_step = STEP_FULL; + } + } else if (prm->pres_chrg_step == STEP_FULL) { + if (stat->batt_mv < (max_fv_mv - HYST_STEP_MV * 2)) { + prm->chrg_taper_cnt = 0; + prm->pres_chrg_step = STEP_NORM; + } + } else if (prm->pres_chrg_step == STEP_FLOAT) { + if ((zone->fcc_norm_ma) || + ((stat->batt_mv + HYST_STEP_MV) < zone->norm_mv)) + prm->pres_chrg_step = STEP_MAX; + else if (mmi_has_current_tapered(BASE_BATT, chip, prm, stat->batt_ma, + prm->chrg_iterm)) + prm->pres_chrg_step = STEP_STOP; + } + + /* Take State actions */ + switch (prm->pres_chrg_step) { + case STEP_FLOAT: + case STEP_MAX: + if (!zone->norm_mv) + target_fv = max_fv_mv + chip->vfloat_comp_mv; + else + target_fv = zone->norm_mv + chip->vfloat_comp_mv; + target_fcc = zone->fcc_max_ma; + chip->last_reported_status = -1; + break; + case STEP_FULL: + target_fv = max_fv_mv; + target_fcc = -EINVAL; + chip->last_reported_status = POWER_SUPPLY_STATUS_FULL; + break; + case STEP_NORM: + target_fv = max_fv_mv + chip->vfloat_comp_mv; + target_fcc = zone->fcc_norm_ma; + chip->last_reported_status = -1; + break; + case STEP_NONE: + target_fv = max_fv_mv; + if (is_wls_online(chip)) + target_fcc = zone->fcc_norm_ma; + else if (stat->dc_present) + target_fcc = 500; + else + target_fcc = zone->fcc_norm_ma; + + chip->last_reported_status = -1; + break; + case STEP_STOP: + target_fv = max_fv_mv; + target_fcc = -EINVAL; + chip->last_reported_status = -1; + break; + case STEP_DEMO: + target_fv = DEMO_MODE_VOLTAGE; + target_fcc = zone->fcc_max_ma; + chip->last_reported_status = -1; + break; + default: + target_fv = max_fv_mv; + target_fcc = zone->fcc_norm_ma; + chip->last_reported_status = -1; + break; + } + + /* Votes for State */ + if (stat->dc_present) + vote(chip->dc_suspend_votable, MMI_HB_VOTER, false, 0); + else + vote(chip->dc_suspend_votable, MMI_HB_VOTER, true, 1); + + vote(chip->fv_votable, MMI_HB_VOTER, true, target_fv * 1000); + + if (chip->enable_dcp_ffc) { + if (target_fcc == -EINVAL) + mmi_charger_ffc_init(chip); + + if (chip->ffc_stop_chg) { + target_fcc = -EINVAL; + } + } + + vote(chip->chg_dis_votable, MMI_HB_VOTER, + (target_fcc < 0), 0); + + vote(chip->fcc_votable, MMI_HB_VOTER, + true, (target_fcc >= 0) ? (target_fcc * 1000) : 0); + + /* Override USB ICL using the target value */ + if (stat->charger_present) { + mmi_usb_icl_override(chip, + get_effective_result(chip->usb_icl_votable)); + } + + /* Rerun AICL to recover USB ICL from recharge */ + if (is_chg_dis != get_effective_result(chip->chg_dis_votable)) { + smb_mmi_write_iio_chan(chip, + SMB5_RERUN_AICL, 0); + } + + if (prm->pres_temp_zone == ZONE_HOT) { + prm->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; + } else if (prm->pres_temp_zone == ZONE_COLD) { + prm->batt_health = POWER_SUPPLY_HEALTH_COLD; + } else if (stat->batt_temp >= WARM_TEMP) { + if (prm->pres_chrg_step == STEP_STOP) + prm->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; + else + prm->batt_health = POWER_SUPPLY_HEALTH_GOOD; + } else if (stat->batt_temp <= COOL_TEMP) { + if (prm->pres_chrg_step == STEP_STOP) + prm->batt_health = POWER_SUPPLY_HEALTH_COLD; + else + prm->batt_health = POWER_SUPPLY_HEALTH_GOOD; + } else + prm->batt_health = POWER_SUPPLY_HEALTH_GOOD; + + mmi_info(chip, "Step State = %s, Temp Zone %d, Health %d\n", + stepchg_str[(int)prm->pres_chrg_step], + prm->pres_temp_zone, + prm->batt_health); + mmi_info(chip, "IMPOSED: FV = %d, CDIS = %d, FCC = %d, USBICL = %d\n", + get_effective_result(chip->fv_votable), + get_effective_result(chip->chg_dis_votable), + get_effective_result(chip->fcc_votable), + get_effective_result(chip->usb_icl_votable)); +} + +static void smb_mmi_awake_vote(struct smb_mmi_charger *chip, bool awake) +{ + if (awake == chip->awake) + return; + + chip->awake = awake; + if (awake) + pm_stay_awake(chip->dev); + else + pm_relax(chip->dev); +} + +void update_charging_limit_modes(struct smb_mmi_charger *chip, int batt_soc) +{ + enum charging_limit_modes charging_limit_modes; + + charging_limit_modes = chip->charging_limit_modes; + if ((charging_limit_modes != CHARGING_LIMIT_RUN) + && (batt_soc >= chip->upper_limit_capacity)) + charging_limit_modes = CHARGING_LIMIT_RUN; + else if ((charging_limit_modes != CHARGING_LIMIT_OFF) + && (batt_soc <= chip->lower_limit_capacity)) + charging_limit_modes = CHARGING_LIMIT_OFF; + + if (charging_limit_modes != chip->charging_limit_modes) + chip->charging_limit_modes = charging_limit_modes; +} + +static void smb_mmi_power_supply_changed(struct power_supply *psy, + char *envp_ext[]) +{ + if (this_chip && this_chip->prev_chg_rate != this_chip->charger_rate) { + dev_err(&psy->dev, "SMBMMI: %s: %s\n", __func__, envp_ext[0]); + } + + kobject_uevent_env(&psy->dev.kobj, KOBJ_CHANGE, envp_ext); + power_supply_changed(psy); +} + +static void smb_mmi_power_watt_report(struct smb_mmi_charger *chip) +{ + char *uevent_string = NULL; + char *envp[2]; + + uevent_string = kmalloc(CHG_SHOW_MAX_SIZE, GFP_KERNEL); + if (!uevent_string) { + mmi_err(chip, "Failed to Get Uevent Mem\n"); + envp[0] = NULL; + } else { + scnprintf(uevent_string, CHG_SHOW_MAX_SIZE, + "POWER_SUPPLY_POWER_WATT=%d", + chip->power_watt / 1000); + envp[0] = uevent_string; + envp[1] = NULL; + } + + if (chip->batt_psy) { + smb_mmi_power_supply_changed(chip->batt_psy, envp); + } else if (chip->qcom_psy) { + smb_mmi_power_supply_changed(chip->qcom_psy, envp); + } + + kfree(uevent_string); +} + +static int smb_mmi_get_chg_info(struct smb_mmi_charger *chip, struct smb_mmi_chg_status *stat) +{ + int usb_type; + int usb_icl; + int power_watt = 0; + + if (stat->charger_present) { + usb_type = chip->real_charger_type; + if (usb_type == POWER_SUPPLY_TYPE_USB) + power_watt = 2500; + else if (usb_type == POWER_SUPPLY_TYPE_USB_CDP) + power_watt = 7500; + else if (usb_type == POWER_SUPPLY_TYPE_USB_DCP) + power_watt = 10000; + else if (usb_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP) + if (chip->hvdcp2_force_9v) + power_watt = 15000; + else + power_watt = 7500; + else if (usb_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3) + power_watt = chip->hvdcp_power_max; + else if (usb_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5) + power_watt = 30000; + else if (chip->pd_pps_active) { + if (chip->pd_pps_active == QTI_POWER_SUPPLY_PD_PPS_ACTIVE) + power_watt = 30000; + else + power_watt = (chip->pd_power != 0) ? chip->pd_power : 2500; + } else { + power_watt = 2500; + } + + usb_icl = get_effective_result(chip->usb_icl_votable); + if (power_watt < (usb_icl * 5 / 1000)) + power_watt = usb_icl * 5 / 1000; + + } + + return power_watt; +} + +static int factory_kill_disable; +module_param(factory_kill_disable, int, 0644); +#define TWO_VOLT 2000000 +#define MONOTONIC_SOC 2 /* 2 percent */ +static void mmi_heartbeat_work(struct work_struct *work) +{ + struct smb_mmi_charger *chip = container_of(work, + struct smb_mmi_charger, + heartbeat_work.work); + int hb_resch_time = HEARTBEAT_DELAY_MS; + union power_supply_propval pval; + int rc, usb_suspend; + int batt_cap = 0; + int main_cap = 0; + int main_cap_full = 0; + int main_age = 0; + int flip_cap = 0; + int flip_cap_full = 0; + int flip_age = 0; + int power_watt = 0; + int cap_err; + int report_cap; + int pc_online; + int pres_chrg_step, prev_chrg_step; + static int prev_vbus_mv = -1; + char *chrg_rate_string = NULL; + char *envp[2]; + + struct smb_mmi_chg_status chg_stat; + + /* Have not been resumed so wait another 100 ms */ + if (chip->suspended & IS_SUSPENDED) { + mmi_err(chip, "HB running before Resume\n"); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(100)); + return; + } + + smb_mmi_awake_vote(chip, true); + + mmi_dbg(chip, "Heartbeat!\n"); + + chg_stat.charger_present = false; + rc = get_prop_batt_voltage_now(chip, chip->bms_psy, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Voltage rc = %d\n", rc); + goto sch_hb; + } else + chg_stat.batt_mv = pval.intval / 1000; + + rc = get_prop_batt_current_now(chip, chip->bms_psy, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Current rc = %d\n", rc); + goto sch_hb; + } else + chg_stat.batt_ma = pval.intval / 1000; + + rc = get_prop_usb_voltage_now(chip, &pval); + if (rc < 0) { + chg_stat.usb_mv = 0; + if (rc != -ENODATA) + mmi_err(chip, "Error getting USB Voltage rc = %d\n", rc); + } else + chg_stat.usb_mv = pval.intval / 1000; + + if (prev_vbus_mv == -1) + prev_vbus_mv = chg_stat.usb_mv; + + rc = get_prop_dc_voltage_now(chip, &pval); + if (rc < 0) { + mmi_dbg(chip, "Error getting DC Voltage rc = %d\n", rc); + chg_stat.dc_mv = 0; + } else + chg_stat.dc_mv = pval.intval / 1000; + + rc = get_prop_batt_capacity(chip, chip->bms_psy, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Capacity rc = %d\n", rc); + return; + } else + chg_stat.batt_soc = pval.intval; + + if (chip->last_reported_soc != -1) + chg_stat.batt_soc = chip->last_reported_soc; + + rc = get_prop_batt_temp(chip, chip->bms_psy, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Temperature rc = %d\n", rc); + return; + } else + chg_stat.batt_temp = pval.intval / 10; + + rc = get_prop_usb_present(chip, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting USB Present rc = %d\n", rc); + return; + } else + chg_stat.vbus_present = pval.intval; + + rc = get_prop_dc_present(chip, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting DC Present rc = %d\n", rc); + return; + } else + chg_stat.dc_present = pval.intval; + + rc = get_prop_charger_present(chip, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Charger Present rc = %d\n", rc); + return; + } else + chg_stat.charger_present = pval.intval & chg_stat.vbus_present; + + if (chip->factory_mode) + hb_resch_time = HEARTBEAT_FACTORY_MS; + else if (chg_stat.charger_present || chg_stat.dc_present) + hb_resch_time = HEARTBEAT_DELAY_MS; + else + hb_resch_time = HEARTBEAT_DISCHARGE_MS; + + mmi_chrg_rate_check(chip); + + if (chip->vbus_enabled && chip->vbus && chg_stat.charger_present) { + rc = regulator_disable(chip->vbus); + if (rc) + mmi_err(chip, "Unable to disable vbus (%d)\n", rc); + else { + chip->vbus_enabled = false; + mmi_info(chip, "VBUS Disable due to Charger\n"); + } + } + + if (chip->enable_charging_limit && chip->is_factory_image) + update_charging_limit_modes(chip, chg_stat.batt_soc); + + if (chip->charging_limit_modes == CHARGING_LIMIT_RUN) + mmi_warn(chip, "Factory Mode/Image so Limiting Charging!!!\n"); + + mmi_chrg_input_config(chip, &chg_stat); + power_watt = smb_mmi_get_chg_info(chip,&chg_stat); + + if (chip->max_main_psy && chip->max_flip_psy) { + cap_err = 0; + rc = power_supply_get_property(chip->max_main_psy, + POWER_SUPPLY_PROP_CAPACITY, + &pval); + if (rc < 0) { + mmi_err(chip, "Couldn't get maxim main capacity\n"); + cap_err = rc; + } else if (pval.intval <0) { + cap_err = -EAGAIN; // TI FG RESET may return neg cap prior to init, so delay + } else + main_cap = pval.intval; + + rc = power_supply_get_property(chip->max_main_psy, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + &pval); + if (rc < 0) { + mmi_err(chip, "Couldn't get maxim main chrg full\n"); + cap_err = rc; + } else + main_cap_full = pval.intval; + + rc = power_supply_get_property(chip->max_flip_psy, + POWER_SUPPLY_PROP_CAPACITY, + &pval); + if (rc < 0) { + mmi_err(chip, "Couldn't get maxim flip capacity\n"); + cap_err = rc; + } else if (pval.intval <0) { + cap_err = -EAGAIN; // TI FG RESET may return neg cap prior to init, so delay + } else + flip_cap = pval.intval; + + rc = power_supply_get_property(chip->max_flip_psy, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + &pval); + if (rc < 0) { + mmi_err(chip, "Couldn't get maxim flip chrg full\n"); + cap_err = rc; + } else + flip_cap_full = pval.intval; + + if (cap_err == -EAGAIN) { + mmi_err(chip, "The FGs are not ready yet\n"); + hb_resch_time = HEARTBEAT_DUAL_DELAY_MS; + goto sch_hb; + } + + report_cap = main_cap * main_cap_full; + report_cap += flip_cap * flip_cap_full; + report_cap /= main_cap_full + flip_cap_full; + + if (chip->shut_batt || (report_cap < 0)) + report_cap = 0; + else if (report_cap > 100) + report_cap = 100; + + if (chip->suspended & WAS_SUSPENDED) + batt_cap = -1; + else + batt_cap = chip->last_reported_soc; + + if ((batt_cap == -1) || + ((report_cap != batt_cap) && + (report_cap <= (batt_cap + MONOTONIC_SOC)) && + (report_cap >= (batt_cap - MONOTONIC_SOC)))) { + mmi_info(chip, "Updating Reported Capacity to %d, main_cap= %d, flip_cap= %d, main_cap_full= %d, flip_cap_full= %d\n", + report_cap, main_cap, flip_cap, main_cap_full, flip_cap_full); + chip->last_reported_soc = report_cap; + }else if ((batt_cap < 100) && (report_cap > (batt_cap + MONOTONIC_SOC))) { + chip->last_reported_soc++; + mmi_info(chip, "Alter Up Reported Capacity to %d target %d\n", + chip->last_reported_soc, report_cap); + }else if ((batt_cap > 0) && (report_cap < (batt_cap - MONOTONIC_SOC))) { + chip->last_reported_soc--; + mmi_info(chip, "Alter Down Reported Capacity to %d target %d\n", + chip->last_reported_soc, report_cap); + } + + /* Age calculation */ + rc = power_supply_get_property(chip->max_main_psy, + POWER_SUPPLY_PROP_CHARGE_FULL, + &pval); + if (rc < 0) { + mmi_err(chip, "Couldn't get maxim main charge full\n"); + cap_err = rc; + } else + main_cap = pval.intval; + + rc = power_supply_get_property(chip->max_flip_psy, + POWER_SUPPLY_PROP_CHARGE_FULL, + &pval); + if (rc < 0) { + mmi_err(chip, "Couldn't get maxim flip charge full\n"); + cap_err = rc; + } else + flip_cap = pval.intval; + + main_age = ((main_cap / 10) / (main_cap_full / 1000)); + flip_age = ((flip_cap / 10) / (flip_cap_full / 1000)); + + /* Block age output for now until FG can be vetted */ + if (cap_err == 0) + chip->age = 100; + + mmi_dbg(chip, "Age %d, Main Age %d, Flip Age %d\n", + chip->age, main_age, flip_age); + + prev_chrg_step = chip->sm_param[BASE_BATT].pres_chrg_step; + + /* Dual Step and Thermal Charging */ + hb_resch_time = mmi_dual_charge_control(chip, &chg_stat); + + /* Check to see if Voltage is shutdown */ + if (chip->shut_batt) + chip->last_reported_soc = 0; + + pres_chrg_step = chip->sm_param[BASE_BATT].pres_chrg_step; + + if ((prev_chrg_step == STEP_NONE) && + ((pres_chrg_step == STEP_MAX) || + (pres_chrg_step == STEP_NORM))) + chip->soc_cycles_start = chip->last_reported_soc; + else if ((pres_chrg_step == STEP_NONE) && + ((prev_chrg_step == STEP_MAX) || + (prev_chrg_step == STEP_NORM)) && + (chip->last_reported_soc > chip->soc_cycles_start)) { + + chip->cycles += (chip->last_reported_soc - + chip->soc_cycles_start); + chip->soc_cycles_start = EMPTY_CYCLES; + + } else if ((pres_chrg_step == STEP_FULL) && + ((prev_chrg_step == STEP_MAX) || + (prev_chrg_step == STEP_NORM)) && + (100 > chip->soc_cycles_start)) { + + chip->cycles += (100 - chip->soc_cycles_start); + chip->soc_cycles_start = EMPTY_CYCLES; + } + + } else if (!chip->factory_mode) { + cap_err = 0; + rc = smb_mmi_read_iio_chan(chip, + SMB5_QG_CHARGE_FULL, + &pval.intval); + if (rc < 0) { + mmi_err(chip, "Couldn't get charge full\n"); + cap_err = rc; + } else + main_cap = pval.intval; + + rc = smb_mmi_read_iio_chan(chip, + SMB5_QG_CHARGE_FULL_DESIGN, + &pval.intval); + if (rc < 0) { + mmi_err(chip, "Couldn't get charge full design\n"); + cap_err = rc; + } else + main_cap_full = pval.intval; + + if (cap_err == 0) + chip->age = ((main_cap / 10) / (main_cap_full / 1000)); + + mmi_dbg(chip, "Age %d\n", chip->age); + + /* Fall here for Basic Step and Thermal Charging */ + mmi_basic_charge_sm(chip, &chg_stat); + } + + mmi_dbg(chip, "SMBMMI: batt_mv %d, usb_mv %d, prev_usb_mv %d batt_ma %d\n", + chg_stat.batt_mv, chg_stat.usb_mv, + prev_vbus_mv, chg_stat.batt_ma); + + rc = smblib_get_usb_suspend(chip, &usb_suspend); + if (rc < 0) { + usb_suspend = 0; + mmi_err(chip, "Couldn't get USB suspend rc = %d\n", rc); + } + if (!usb_suspend && + (abs(chg_stat.usb_mv - chg_stat.batt_mv) < REV_BST_BULK_DROP) && + ((chg_stat.usb_mv*1000) > TWO_VOLT)) { + if (((chg_stat.usb_mv < REV_BST_THRESH) && + ((prev_vbus_mv - REV_BST_DROP) > chg_stat.usb_mv)) || + (chg_stat.batt_ma > REV_BST_MA)) { + mmi_err(chip, "Reverse Boosted: Clear, USB Suspend. usb_mv: %d prev_vbus_mv: %d batt_ma: %d \n", chg_stat.usb_mv, prev_vbus_mv, chg_stat.batt_ma); + if (chip->factory_mode) + smblib_set_usb_suspend(chip, true); + else + vote(chip->usb_icl_votable, BOOST_BACK_VOTER, + true, 0); + msleep(50); + if (chip->factory_mode) + smblib_set_usb_suspend(chip, false); + else + vote(chip->usb_icl_votable, BOOST_BACK_VOTER, + false, 0); + } else { + mmi_err(chip, "Reverse Boosted: USB %d mV PUSB %d mV\n", + chg_stat.usb_mv, prev_vbus_mv); + } + } + prev_vbus_mv = chg_stat.usb_mv; + chip->suspended = 0; + + if (chip->factory_mode || + (chip->is_factory_image && chip->enable_factory_poweroff)) { + rc = power_supply_get_property(chip->pc_port_psy, + POWER_SUPPLY_PROP_ONLINE, + &pval); + if (rc < 0) + goto sch_hb; + + pc_online = pval.intval; + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_ONLINE, + &pval); + if (rc < 0) + goto sch_hb; + + mmi_dbg(chip, "Factory Kill check pc %d, usb %d, susp %d\n", + pc_online, pval.intval, usb_suspend); + if (pc_online || + pval.intval || + (usb_suspend && ((chg_stat.usb_mv*1000) > TWO_VOLT))) { + mmi_dbg(chip, "Factory Kill Armed\n"); + chip->factory_kill_armed = true; + } else if (chip->factory_kill_armed && !factory_kill_disable) { + mmi_warn(chip, "Factory kill power off\n"); + orderly_poweroff(true); + } else + chip->factory_kill_armed = false; + } + +sch_hb: + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(hb_resch_time)); + + if (!chg_stat.charger_present) + smb_mmi_awake_vote(chip, false); + + if ((chip->power_watt / 1000) != (power_watt / 1000)) { + chip->power_watt = power_watt; + smb_mmi_power_watt_report(chip); + mmi_info(chip, "charger power is %d mW\n", chip->power_watt); + } + + chrg_rate_string = kmalloc(CHG_SHOW_MAX_SIZE, GFP_KERNEL); + if (!chrg_rate_string) { + mmi_err(chip, "Failed to Get Uevent Mem\n"); + envp[0] = NULL; + } else { + scnprintf(chrg_rate_string, CHG_SHOW_MAX_SIZE, + "POWER_SUPPLY_CHARGE_RATE=%s", + charge_rate[chip->charger_rate]); + envp[0] = chrg_rate_string; + envp[1] = NULL; + } + + if (chip->batt_psy) { + smb_mmi_power_supply_changed(chip->batt_psy, envp); + } else if (chip->qcom_psy) { + smb_mmi_power_supply_changed(chip->qcom_psy, envp); + } + + kfree(chrg_rate_string); + +} + +static int mmi_psy_notifier_call(struct notifier_block *nb, unsigned long val, + void *v) +{ + struct smb_mmi_charger *chip = container_of(nb, + struct smb_mmi_charger, mmi_psy_notifier); + struct power_supply *psy = v; + static bool first_boot = true; + + if (!chip) { + pr_err("SMBMMI: called before chip valid!\n"); + return NOTIFY_DONE; + } + + if (val != PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + if (psy && + ((strcmp(psy->desc->name, "usb") == 0) || + (strcmp(psy->desc->name, "wireless") == 0) || + (strcmp(psy->desc->name, "dc") == 0))) { + + mmi_info(chip, "Psy notifier call %s\n", psy->desc->name); + if(first_boot && chip->factory_mode) + { + //In factory mode for the first heartbeat make sure to wait 10 second until boot is complete + cancel_delayed_work(&chip->heartbeat_work); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(10000)); + first_boot = false; + } + else + { + cancel_delayed_work(&chip->heartbeat_work); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(0)); + first_boot = false; + } + } + + return NOTIFY_OK; +} + +static int smbchg_reboot(struct notifier_block *nb, + unsigned long event, void *unused) +{ + struct smb_mmi_charger *chg = container_of(nb, struct smb_mmi_charger, + smb_reboot); + union power_supply_propval val; + int rc; + + pr_err("SMBMMI: Reboot/POFF\n"); + if (!chg) { + pr_err("SMBMMI: called before chip valid!\n"); + return NOTIFY_DONE; + } + + if (chg->factory_mode) { + switch (event) { + case SYS_POWER_OFF: + /* Disable Factory Kill */ + factory_kill_disable = true; + /* Disable Charging */ + smblib_masked_write_mmi(chg, CHARGING_ENABLE_CMD_REG, + CHARGING_ENABLE_CMD_BIT, + 0); + + /* Suspend USB and DC */ + smblib_set_usb_suspend(chg, true); + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + &val); + while ((rc >= 0) && (val.intval > TWO_VOLT)) { + msleep(100); + rc = power_supply_get_property( + chg->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + &val); + mmi_info(chg, "Wait for VBUS to decay\n"); + } + + mmi_info(chg, "VBUS UV wait 1 sec!\n"); + /* Delay 1 sec to allow more VBUS decay */ + msleep(1000); + break; + default: + break; + } + } + + return NOTIFY_DONE; +} + +static const struct power_supply_desc mmi_psy_desc = { + .name = "mmi_battery", + .type = POWER_SUPPLY_TYPE_MAINS, + .get_property = smb_mmi_get_property, + .set_property = smb_mmi_set_property, + .properties = smb_mmi_battery_props, + .num_properties = ARRAY_SIZE(smb_mmi_battery_props), +}; + +static enum power_supply_property batt_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, +}; + +static int batt_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct smb_mmi_charger *chip = power_supply_get_drvdata(psy); + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (chip->last_reported_status == -1) + rc = power_supply_get_property(chip->qcom_psy, + psp, val); + else + val->intval = chip->last_reported_status; + break; + case POWER_SUPPLY_PROP_CAPACITY: + if (chip->last_reported_soc == -1) + rc = power_supply_get_property(chip->qcom_psy, + psp, val); + else + val->intval = chip->last_reported_soc; + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + val->intval = chip->cycles / 100; + break; + case POWER_SUPPLY_PROP_HEALTH: + if (chip->max_main_psy && chip->max_flip_psy) + val->intval = chip->sm_param[MAIN_BATT].batt_health; + else + val->intval = chip->sm_param[BASE_BATT].batt_health; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + val->intval = get_effective_result(chip->fcc_votable); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + if (chip->smb_version == PMI632) { + val->intval = get_effective_result(chip->fcc_votable); + break; + } + rc = power_supply_get_property(chip->qcom_psy, psp, val); + if (rc < 0) { + /* soft fail so uevents are not blocked */ + rc = 0; + val->intval = -EINVAL; + } + break; + case POWER_SUPPLY_PROP_TEMP: + if (chip->max_main_psy && chip->max_flip_psy) { + union power_supply_propval main_psy_val; + union power_supply_propval flip_psy_val; + + /* Get the highest temp between main psy and flip psy */ + rc = power_supply_get_property(chip->max_main_psy, psp, &main_psy_val); + if (rc >= 0) { + rc = power_supply_get_property(chip->max_flip_psy, psp, &flip_psy_val); + if (rc >= 0) { + if (main_psy_val.intval > flip_psy_val.intval) + val->intval = main_psy_val.intval; + else + val->intval = flip_psy_val.intval; + break; + } + } + } + /* Fall through */ + default: + rc = power_supply_get_property(chip->qcom_psy, psp, val); + if (rc < 0) { + mmi_dbg(chip, "Get Unknown prop %d rc = %d\n", psp, rc); + /* soft fail so uevents are not blocked */ + rc = 0; + val->intval = -EINVAL; + } + break; + } + + return rc; +} + +static int batt_set_prop(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + int rc = 0; + struct smb_mmi_charger *chip = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_CYCLE_COUNT: + chip->cycles += val->intval * 100; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + if (val->intval < 0) { + vote(chip->fcc_votable, MMI_VOTER, false, 0); + } else + vote(chip->fcc_votable, MMI_VOTER, true, val->intval); + break; + default: + rc = power_supply_set_property(chip->qcom_psy, prop, val); + if (rc < 0) { + mmi_dbg(chip, "Get Unknown prop %d rc = %d\n", prop, rc); + /* soft fail so uevents are not blocked */ + rc = 0; + } + break; + } + return rc; +} + +static int batt_prop_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + case POWER_SUPPLY_PROP_CAPACITY: + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_CYCLE_COUNT: + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + case POWER_SUPPLY_PROP_CURRENT_MAX: + return 1; + default: + break; + } + + return 0; +} + +static const struct power_supply_desc batt_psy_desc = { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = batt_get_prop, + .set_property = batt_set_prop, + .property_is_writeable = batt_prop_is_writeable, + .properties = batt_props, + .num_properties = ARRAY_SIZE(batt_props), +}; + +static int parse_mmi_dt(struct smb_mmi_charger *chg) +{ + struct device_node *node = chg->dev->of_node; + int rc = 0; + int byte_len; + int i; + struct mmi_sm_params *chip; + + if (!node) { + mmi_err(chg, "mmi dtree info. missing\n"); + return -ENODEV; + } + + chip = &chg->sm_param[BASE_BATT]; + if (of_find_property(node, "qcom,mmi-temp-zones", &byte_len)) { + if ((byte_len / sizeof(u32)) % 4) { + mmi_err(chg, "DT error wrong mmi temp zones\n"); + return -ENODEV; + } + + chip->temp_zones = (struct mmi_temp_zone *) + devm_kzalloc(chg->dev, byte_len, GFP_KERNEL); + + if (chip->temp_zones == NULL) + return -ENOMEM; + + chip->num_temp_zones = + byte_len / sizeof(struct mmi_temp_zone); + + rc = of_property_read_u32_array(node, + "qcom,mmi-temp-zones", + (u32 *)chip->temp_zones, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_err(chg, "Couldn't read mmi temp zones rc = %d\n", rc); + return rc; + } + + mmi_info(chg, "mmi temp zones: Num: %d\n", chip->num_temp_zones); + for (i = 0; i < chip->num_temp_zones; i++) { + mmi_info(chg, "mmi temp zones: Zone %d, Temp %d C, " \ + "Step Volt %d mV, Full Rate %d mA, " \ + "Taper Rate %d mA\n", i, + chip->temp_zones[i].temp_c, + chip->temp_zones[i].norm_mv, + chip->temp_zones[i].fcc_max_ma, + chip->temp_zones[i].fcc_norm_ma); + } + chip->pres_temp_zone = ZONE_NONE; + chip->pres_chrg_step = STEP_NONE; + } + + if (of_find_property(node, "qcom,mmi-ffc-zones", &byte_len)) { + if ((byte_len / sizeof(struct mmi_ffc_zone) != chip->num_temp_zones) + || ((byte_len / sizeof(u32)) % 3)) { + mmi_err(chg, "DT error wrong mmi ffc zones\n"); + return -ENODEV; + } + + chip->ffc_zones = (struct mmi_ffc_zone *) + devm_kzalloc(chg->dev, byte_len, GFP_KERNEL); + + if (chip->ffc_zones == NULL) + return -ENOMEM; + + rc = of_property_read_u32_array(node, + "qcom,mmi-ffc-zones", + (u32 *)chip->ffc_zones, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_err(chg, "Couldn't read mmi ffc zones rc = %d\n", rc); + return rc; + } + + for (i = 0; i < chip->num_temp_zones; i++) { + mmi_info(chg, "FFC:Zone %d,Volt %d,Ich %d,Iqg %d", i, + chip->ffc_zones[i].ffc_max_mv, + chip->ffc_zones[i].ffc_chg_iterm, + chip->ffc_zones[i].ffc_qg_iterm); + } + } else + chip->ffc_zones = NULL; + + rc = of_property_read_u32(node, "qcom,iterm-ma", + &chip->chrg_iterm); + if (rc) + chip->chrg_iterm = 150; + + rc = of_property_read_u32(node, "qcom,vfloat-comp-uv", + &chg->vfloat_comp_mv); + if (rc) + chg->vfloat_comp_mv = 0; + chg->vfloat_comp_mv /= 1000; + + // none ffc parameter + rc = of_property_read_u32(node, "qcom,noffc-chg-iterm", + &chg->noffc_chg_iterm); + if (rc) + chg->noffc_chg_iterm = -EINVAL; + + rc = of_property_read_u32(node, "qcom,noffc-chg-iterm-45c", + &chg->noffc_chg_iterm_45c); + if (rc) + chg->noffc_chg_iterm_45c = -EINVAL; + + rc = of_property_read_u32(node, "qcom,noffc-qg-iterm", + &chg->noffc_qg_iterm); + if (rc) + chg->noffc_qg_iterm = -EINVAL; + + rc = of_property_read_u32(node, "qcom,noffc-qg-iterm-45c", + &chg->noffc_qg_iterm_45c); + if (rc) + chg->noffc_qg_iterm_45c = -EINVAL; + + rc = of_property_read_u32(node, "qcom,noffc-max-fv", + &chg->noffc_max_fv); + if (rc) + chg->noffc_max_fv = -EINVAL; + + chg->enable_charging_limit = + of_property_read_bool(node, "qcom,enable-charging-limit"); + + chg->enable_factory_poweroff = + of_property_read_bool(node, "qcom,enable-factory-poweroff"); + + rc = of_property_read_u32(node, "qcom,upper-limit-capacity", + &chg->upper_limit_capacity); + if (rc) + chg->upper_limit_capacity = 100; + + rc = of_property_read_u32(node, "qcom,lower-limit-capacity", + &chg->lower_limit_capacity); + if (rc) + chg->lower_limit_capacity = 0; + + rc = of_property_read_u32(node, "qcom,dc-icl-ma", + &chg->dc_cl_ma); + if (rc) + chg->dc_cl_ma = -EINVAL; + + chg->enable_factory_mode_aicl = + of_property_read_bool(node, "mmi,enable-factory-mode-aicl"); + + mmi_charger_power_support(chg); + + rc = of_property_read_u32(node, "qcom,inc-hvdcp-cnt", + &chg->inc_hvdcp_cnt); + if (rc) + chg->inc_hvdcp_cnt = HVDCP_PULSE_COUNT_MAX; + + chg->hvdcp2_force_9v = of_property_read_bool(node, "mmi,hvdcp2-force-9v"); + + rc = of_property_read_u32(node, "mmi,ffc-entry-threshold", + &chg->ffc_entry_threshold); + if (rc) + chg->ffc_entry_threshold = 2000; + + rc = of_property_read_u32(node, "mmi,ffc-exit-threshold", + &chg->ffc_exit_threshold); + if (rc) + chg->ffc_exit_threshold = 1800; + + rc = of_property_read_u32(node, "mmi,ffc-uisoc-threshold", + &chg->ffc_uisoc_threshold); + if (rc) + chg->ffc_uisoc_threshold = 70; + + chg->enable_dcp_ffc = of_property_read_bool(node, "mmi,enable-dcp-ffc"); + + chg->enable_fcc_large_qg_iterm = of_property_read_bool(node, "mmi,enable-fcc-large-qg-iterm"); + + return rc; +} + +static int parse_mmi_dual_dt(struct smb_mmi_charger *chg) +{ + struct device_node *node = chg->dev->of_node; + int rc = 0; + int byte_len; + int i; + struct mmi_sm_params *chip; + + if (!node) { + mmi_err(chg, "mmi dtree info. missing\n"); + return -ENODEV; + } + + chip = &chg->sm_param[MAIN_BATT]; + if (of_find_property(node, "qcom,mmi-temp-zones-main", &byte_len)) { + if ((byte_len / sizeof(u32)) % 4) { + mmi_err(chg, "DT error wrong mmi temp zones\n"); + return -ENODEV; + } + + chip->temp_zones = (struct mmi_temp_zone *) + devm_kzalloc(chg->dev, byte_len, GFP_KERNEL); + + if (chip->temp_zones == NULL) + return -ENOMEM; + + chip->num_temp_zones = + byte_len / sizeof(struct mmi_temp_zone); + + rc = of_property_read_u32_array(node, + "qcom,mmi-temp-zones-main", + (u32 *)chip->temp_zones, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_err(chg, "Couldn't read mmi temp zones rc = %d\n", rc); + return rc; + } + + mmi_info(chg, "mmi temp zones main: Num: %d\n", chip->num_temp_zones); + for (i = 0; i < chip->num_temp_zones; i++) { + mmi_info(chg, "mmi temp zones: Zone %d, Temp %d C, " \ + "Step Volt %d mV, Full Rate %d mA, " \ + "Taper Rate %d mA\n", i, + chip->temp_zones[i].temp_c, + chip->temp_zones[i].norm_mv, + chip->temp_zones[i].fcc_max_ma, + chip->temp_zones[i].fcc_norm_ma); + } + chip->pres_temp_zone = ZONE_NONE; + } + + rc = of_property_read_u32(node, "qcom,iterm-ma-main", + &chip->chrg_iterm); + if (rc) + chip->chrg_iterm = 150; + + chip = &chg->sm_param[FLIP_BATT]; + if (of_find_property(node, "qcom,mmi-temp-zones-flip", &byte_len)) { + if ((byte_len / sizeof(u32)) % 4) { + mmi_err(chg, "DT error wrong mmi temp zones\n"); + return -ENODEV; + } + + chip->temp_zones = (struct mmi_temp_zone *) + devm_kzalloc(chg->dev, byte_len, GFP_KERNEL); + + if (chip->temp_zones == NULL) + return -ENOMEM; + + chip->num_temp_zones = + byte_len / sizeof(struct mmi_temp_zone); + + rc = of_property_read_u32_array(node, + "qcom,mmi-temp-zones-flip", + (u32 *)chip->temp_zones, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_err(chg, "Couldn't read mmi temp zones rc = %d\n", rc); + return rc; + } + + mmi_info(chg, "mmi temp zones main: Num: %d\n", chip->num_temp_zones); + for (i = 0; i < chip->num_temp_zones; i++) { + mmi_info(chg, "mmi temp zones: Zone %d, Temp %d C, " \ + "Step Volt %d mV, Full Rate %d mA, " \ + "Taper Rate %d mA\n", i, + chip->temp_zones[i].temp_c, + chip->temp_zones[i].norm_mv, + chip->temp_zones[i].fcc_max_ma, + chip->temp_zones[i].fcc_norm_ma); + } + chip->pres_temp_zone = ZONE_NONE; + } + + rc = of_property_read_u32(node, "qcom,iterm-ma-flip", + &chip->chrg_iterm); + if (rc) + chip->chrg_iterm = 150; + + return rc; +} + +static int smb_mmi_chg_config_init(struct smb_mmi_charger *chip) +{ + int subtype = (u8)of_device_get_match_data(chip->dev); + + switch (subtype) { + case PM8150B: + chip->smb_version = PM8150B; + chip->name = "pm8150b_charger"; + chip->param = smb5_pm8150b_params; + break; + case PM7250B: + chip->smb_version = PM7250B; + chip->name = "pm7250b_charger"; + chip->param = smb5_pm8150b_params; + break; + case PMI632: + chip->smb_version = PMI632; + chip->name = "pmi632_charger"; + chip->param = smb5_pmi632_params; + break; + default: + pr_err("SMBMMI: PMIC subtype %d not supported\n", + subtype); + return -EINVAL; + } + + chip->chg_freq.freq_5V = 600; + chip->chg_freq.freq_6V_8V = 800; + chip->chg_freq.freq_9V = 1050; + chip->chg_freq.freq_12V = 1200; + + pr_err("SMBMMI: PMIC %d is %s\n", chip->smb_version, chip->name); + + return 0; +} + +static ssize_t charge_rate_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (!this_chip) { + pr_err("SMBMMI: mmi_chip is not initialized\n"); + return 0; + } + + mmi_chrg_rate_check(this_chip); + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%s\n", + charge_rate[this_chip->charger_rate]); +} +static DEVICE_ATTR(charge_rate, S_IRUGO, charge_rate_show, NULL); + +static ssize_t age_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (!this_chip) { + pr_err("SMBMMI: mmi_chip is not initialized\n"); + return 0; + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", this_chip->age); +} +static DEVICE_ATTR(age, S_IRUGO, age_show, NULL); + +static struct attribute * mmi_g[] = { + &dev_attr_charge_rate.attr, + &dev_attr_age.attr, + NULL, +}; + +static const struct attribute_group power_supply_mmi_attr_group = { + .attrs = mmi_g, +}; + +#if defined(CONFIG_DEBUG_FS) +static int register_dump_read(struct seq_file *m, void *data) +{ + int rc; + u8 stat; + int i; + struct smb_mmi_charger *chip = m->private; + + for (i = CHGR_BASE; i < MISC_BASE + 0x100; i++) { + rc = smblib_read_mmi(chip, i, &stat); + if (rc < 0) + continue; + seq_printf(m, "REG:0x%x: 0x%x\n", i, stat); + } + + return 0; +} + +static int register_dump_debugfs_open(struct inode *inode, struct file *file) +{ + struct smb_mmi_charger *chip = inode->i_private; + + return single_open(file, register_dump_read, chip); +} + +static const struct file_operations register_dump_debugfs_ops = { + .owner = THIS_MODULE, + .open = register_dump_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void smb_mmi_create_debugfs(struct smb_mmi_charger *chip) +{ + struct dentry *dfs_root, *file; + + dfs_root = debugfs_create_dir("charger_mmi", NULL); + if (IS_ERR_OR_NULL(dfs_root)) { + pr_err("SMBMMI: Couldn't create charger debugfs rc=%ld\n", + (long)dfs_root); + return; + } + + file = debugfs_create_file("register_dump", + S_IRUSR | S_IRGRP | S_IROTH, + dfs_root, chip, ®ister_dump_debugfs_ops); + if (IS_ERR_OR_NULL(file)) + pr_err("SMBMMI: Couldn't create register_dump file rc=%ld\n", + (long)file); +} +#else +static void smb_mmi_create_debugfs(struct smb_mmi_charger *chip) +{} +#endif + +static int mmi_smbcharger_iio_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val1, + int val2, long mask) +{ + struct smb_mmi_charger *chip = iio_priv(indio_dev); + int rc = 0; + + switch (chan->channel) { + case PSY_IIO_CP_ENABLE: + chip->cp_active = !!val1; + break; + default: + pr_err("Unsupported mmi_charger IIO chan %d\n", chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) + pr_err("Couldn't write IIO channel %d, rc = %d\n", + chan->channel, rc); + + return rc; +} + +static int mmi_smbcharger_iio_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val1, + int *val2, long mask) +{ + struct smb_mmi_charger *chip = iio_priv(indio_dev); + int rc = 0; + + *val1 = 0; + + switch (chan->channel) { + case PSY_IIO_CP_ENABLE: + *val1 = chip->cp_active; + break; + default: + pr_err("Unsupported mmi_charger IIO chan %d\n", chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) { + pr_err("Couldn't read IIO channel %d, rc = %d\n", + chan->channel, rc); + return rc; + } + + return IIO_VAL_INT; +} + +static int mmi_smbcharger_iio_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + struct smb_mmi_charger *chip = iio_priv(indio_dev); + struct iio_chan_spec *iio_chan = chip->iio_chan; + int i; + + for (i = 0; i < ARRAY_SIZE(mmi_smbcharger_iio_psy_channels); + i++, iio_chan++) + if (iio_chan->channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static const struct iio_info mmi_smbcharger_iio_info = { + .read_raw = mmi_smbcharger_iio_read_raw, + .write_raw = mmi_smbcharger_iio_write_raw, + .of_xlate = mmi_smbcharger_iio_of_xlate, +}; + +static int smb_mmi_init_iio_psy(struct smb_mmi_charger *chip, + struct platform_device *pdev) +{ + struct iio_dev *indio_dev = chip->indio_dev; + struct iio_chan_spec *chan; + int mmi_smbcharger_num_iio_channels = ARRAY_SIZE(mmi_smbcharger_iio_psy_channels); + int rc, i; + + chip->iio_chan = devm_kcalloc(chip->dev, mmi_smbcharger_num_iio_channels, + sizeof(*chip->iio_chan), GFP_KERNEL); + if (!chip->iio_chan) + return -ENOMEM; + + chip->int_iio_chans = devm_kcalloc(chip->dev, + mmi_smbcharger_num_iio_channels, + sizeof(*chip->int_iio_chans), + GFP_KERNEL); + if (!chip->int_iio_chans) + return -ENOMEM; + + indio_dev->info = &mmi_smbcharger_iio_info; + indio_dev->dev.parent = chip->dev; + indio_dev->dev.of_node = chip->dev->of_node; + indio_dev->name = "mmi-smbcharger-iio"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = chip->iio_chan; + indio_dev->num_channels = mmi_smbcharger_num_iio_channels; + + for (i = 0; i < mmi_smbcharger_num_iio_channels; i++) { + chip->int_iio_chans[i].indio_dev = indio_dev; + chan = &chip->iio_chan[i]; + chip->int_iio_chans[i].channel = chan; + chan->address = i; + chan->channel = mmi_smbcharger_iio_psy_channels[i].channel_num; + chan->type = mmi_smbcharger_iio_psy_channels[i].type; + chan->datasheet_name = + mmi_smbcharger_iio_psy_channels[i].datasheet_name; + chan->extend_name = + mmi_smbcharger_iio_psy_channels[i].datasheet_name; + chan->info_mask_separate = + mmi_smbcharger_iio_psy_channels[i].info_mask; + } + + rc = devm_iio_device_register(chip->dev, indio_dev); + if (rc) { + pr_err("Failed to register mmi-smbcharger-iio IIO device, rc=%d\n", rc); + return rc; + } + + chip->ext_iio_chans = devm_kcalloc(chip->dev, + ARRAY_SIZE(smb_mmi_ext_iio_chan_name), + sizeof(*chip->ext_iio_chans), + GFP_KERNEL); + if (!chip->ext_iio_chans) + return -ENOMEM; + + return rc; +} + +static int smb_mmi_probe(struct platform_device *pdev) +{ + struct smb_mmi_charger *chip; + struct iio_dev *indio_dev; + int rc = 0; + union power_supply_propval val; + struct power_supply_config psy_cfg = {}; + const char *max_main_name, *max_flip_name; + union power_supply_propval pval; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + + chip = iio_priv(indio_dev); + if (!chip) { + dev_err(&pdev->dev, + "Unable to alloc memory for mmi_smbcharger_iio\n"); + return -ENOMEM; + } + chip->indio_dev = indio_dev; + + chip->dev = &pdev->dev; + psy_cfg.drv_data = chip; + platform_set_drvdata(pdev, chip); + chip->suspended = 0; + chip->awake = false; + this_chip = chip; + device_init_wakeup(chip->dev, true); + chip->hb_startup_cnt = HEARTBEAT_OCP_SETTLE_CNT; + chip->ocp_flag = true; + + smb_mmi_chg_config_init(chip); + + chip->debug_enabled = &debug_enabled; + chip->ipc_log = ipc_log_context_create(MMI_LOG_PAGES, "charger", 0); + if (!chip->ipc_log) + mmi_err(chip, "Failed to create SMBMMI IPC log\n"); + else + mmi_info(chip, "IPC logging is enabled for SMBMMI\n"); + + chip->regmap = dev_get_regmap(chip->dev->parent, NULL); + if (!chip->regmap) { + mmi_err(chip, "Parent regmap is missing\n"); + return -EINVAL; + } + + smb_mmi_init_iio_psy(chip, pdev); + + parse_mmi_dt(chip); + + INIT_DELAYED_WORK(&chip->heartbeat_work, mmi_heartbeat_work); + INIT_DELAYED_WORK(&chip->weakcharger_work, mmi_weakcharger_work); + + chip->mmi_psy = devm_power_supply_register(chip->dev, &mmi_psy_desc, + &psy_cfg); + if (IS_ERR(chip->mmi_psy)) { + mmi_err(chip, "failed: mmi power supply register\n"); + return PTR_ERR(chip->mmi_psy); + } + + chip->age = 100; + chip->cycles = 0; + chip->shut_batt = false; + chip->soc_cycles_start = EMPTY_CYCLES; + chip->last_reported_soc = -1; + chip->last_reported_status = -1; + mmi_charger_ffc_init(chip); + chip->qcom_psy = power_supply_get_by_name("qcom_battery"); + if (chip->qcom_psy) { + chip->batt_psy = devm_power_supply_register(chip->dev, + &batt_psy_desc, + &psy_cfg); + if (IS_ERR(chip->batt_psy)) { + mmi_err(chip, + "Failed: batt power supply register\n"); + return PTR_ERR(chip->batt_psy); + } + + rc = sysfs_create_group(&chip->batt_psy->dev.kobj, + &power_supply_mmi_attr_group); + if (rc) + mmi_err(chip, "Failed: attr create\n"); + } else { + chip->qcom_psy = power_supply_get_by_name("battery"); + chip->batt_psy = NULL; + if (chip->qcom_psy) { + rc = sysfs_create_group(&chip->qcom_psy->dev.kobj, + &power_supply_mmi_attr_group); + if (rc) + mmi_err(chip, "Failed: attr create\n"); + } else { + mmi_err(chip, "Failed: SMB MMI probed failed, wait SMB5 prob\n"); + return -EPROBE_DEFER; + } + } + + chip->bms_psy = power_supply_get_by_name("bms"); + chip->usb_psy = power_supply_get_by_name("usb"); + chip->pc_port_psy = power_supply_get_by_name("pc_port"); + chip->dc_psy = power_supply_get_by_name("dc"); + chip->wls_psy = power_supply_get_by_name("wireless"); + /* parse the dc power supply configuration */ + rc = of_property_read_string(pdev->dev.of_node, + "mmi,max-main-psy", &max_main_name); + rc |= of_property_read_string(pdev->dev.of_node, + "mmi,max-flip-psy", &max_flip_name); + if (!rc && max_main_name && max_flip_name) { + chip->max_main_psy = power_supply_get_by_name(max_main_name); + chip->max_flip_psy = power_supply_get_by_name(max_flip_name); + parse_mmi_dual_dt(chip); + } + + chip->chg_dis_votable = find_votable("CHG_DISABLE"); + if (IS_ERR(chip->chg_dis_votable)) + chip->chg_dis_votable = NULL; + chip->fcc_votable = find_votable("FCC"); + if (IS_ERR(chip->fcc_votable)) + chip->fcc_votable = NULL; + chip->fv_votable = find_votable("FV"); + if (IS_ERR(chip->fv_votable)) + chip->fv_votable = NULL; + chip->usb_icl_votable = find_votable("USB_ICL"); + if (IS_ERR(chip->usb_icl_votable)) + chip->usb_icl_votable = NULL; + chip->dc_suspend_votable = find_votable("DC_SUSPEND"); + if (IS_ERR(chip->dc_suspend_votable)) + chip->dc_suspend_votable = NULL; + + if (chip->smb_version == PM8150B) { + if (smblib_masked_write_mmi(chip, LEGACY_CABLE_CFG_REG, + 0xFF, 0)) + mmi_err(chip, "Could not set Legacy Cable CFG\n"); + } + + if (chip->smb_version == PM8150B || + chip->smb_version == PM7250B || + chip->smb_version == PMI632) { + mmi_err(chip, "DISABLE all QCOM JEITA\n"); + /* Ensure SW JEITA is DISABLED */ + pval.intval = 0; + smb_mmi_write_iio_chan(chip, + SMB5_SW_JEITA_ENABLED, pval.intval); + /* Ensure HW JEITA is DISABLED */ + if (smblib_masked_write_mmi(chip, PM8150B_JEITA_EN_CFG_REG, + 0xFF, 0x00)) + mmi_err(chip, "Could not disable JEITA CFG\n"); + } + + if ((chip->smb_version == PM8150B) || + (chip->smb_version == PM7250B) || + (chip->smb_version == PMI632)) + if (smblib_masked_write_mmi(chip, USBIN_ADAPTER_ALLOW_CFG_REG, + USBIN_ADAPTER_ALLOW_MASK, + USBIN_ADAPTER_ALLOW_5V_TO_9V)) + mmi_err(chip, "Could not set USB Adapter CFG\n"); + + /* + * Only 5V is allowed for QC2.0 and QC3.0, so set pulse count max + * accordingly to limit voltage increment. + */ + if (chip->hvdcp_power_max) { + rc = smblib_masked_write_mmi(chip, HVDCP_PULSE_COUNT_MAX_REG, + HVDCP_PULSE_COUNT_MAX_QC3_MASK, + chip->inc_hvdcp_cnt); + if (rc < 0) + mmi_err(chip, "Could not set HVDCP3 pulse count max\n"); + } + + rc = smblib_masked_write_mmi(chip, HVDCP_PULSE_COUNT_MAX_REG, + HVDCP_PULSE_COUNT_MAX_QC2_MASK, + chip->hvdcp2_force_9v ? + HVDCP_PULSE_COUNT_MAX_QC2_9V: + HVDCP_PULSE_COUNT_MAX_QC2_5V); + if (rc < 0) + mmi_err(chip, "Could not set HVDCP2 pulse count max\n"); + + chip->factory_mode = mmi_factory_check(MMI_FACTORY_MODE); + chip->is_factory_image = mmi_factory_check(MMI_FACTORY_BUILD); + chip->charging_limit_modes = CHARGING_LIMIT_UNKNOWN; + + if (chip->dc_cl_ma >= 0) { + rc = smblib_set_charge_param(chip, &chip->param.dc_icl, + chip->dc_cl_ma * 1000); + if (rc) + mmi_err(chip, "Failed to set DC ICL %d\n", + chip->dc_cl_ma); + } + + /* Workaround for some cables that collapse on boot */ + if (!chip->factory_mode) { + dev_err(chip->dev, "SMBMMI: Suspending USB for 50 ms to clear\n"); + smblib_set_usb_suspend(chip, true); + msleep(50); + smblib_set_usb_suspend(chip, false); + } + + if (chip->factory_mode && + (chip->smb_version == PMI632)) { + rc = smblib_masked_write_mmi(chip, USBIN_INT_EN_CLR, + 0xFF, USBIN_OV_EN_CLR); + if (rc) { + mmi_err(chip, "Could Not disable usbin ov irq\n"); + } + } + + rc = device_create_file(chip->dev, + &dev_attr_force_demo_mode); + if (rc) { + mmi_err(chip, "Couldn't create force_demo_mode\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_max_chrg_temp); + if (rc) { + mmi_err(chip, "Couldn't create force_max_chrg_temp\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_factory_image_mode); + if (rc) + mmi_err(chip, "Couldn't create factory_image_mode\n"); + + rc = device_create_file(chip->dev, + &dev_attr_factory_charge_upper); + if (rc) + mmi_err(chip, "Couldn't create factory_charge_upper\n"); + + rc = device_create_file(chip->dev, + &dev_attr_force_hvdcp_power_max); + if (rc) + mmi_err(chip, "Couldn't create force_hvdcp_power_max\n"); + + rc = device_create_file(chip->dev, + &dev_attr_force_pd_power_max); + if (rc) + mmi_err(chip, "Couldn't create force_pd_power_max\n"); + + rc = device_create_file(chip->dev, + &dev_attr_chg_watt); + if (rc) { + mmi_err(chip, "Couldn't create chg_watt\n"); + } + + /* Register the notifier for the psy updates*/ + chip->mmi_psy_notifier.notifier_call = mmi_psy_notifier_call; + rc = power_supply_reg_notifier(&chip->mmi_psy_notifier); + if (rc) + mmi_err(chip, "Failed to reg notifier: %d\n", rc); + + if (chip->factory_mode) { + mmi_info(chip, "Entering Factory Mode SMB!\n"); + rc = smblib_masked_write_mmi(chip, USBIN_ICL_OPTIONS_REG, + USBIN_MODE_CHG_BIT, + USBIN_MODE_CHG_BIT); + if (rc < 0) + mmi_err(chip, + "Couldn't set USBIN_ICL_OPTIONS rc=%d\n", rc); + + rc = smblib_masked_write_mmi(chip, USBIN_LOAD_CFG_REG, + ICL_OVERRIDE_AFTER_APSD_BIT, + ICL_OVERRIDE_AFTER_APSD_BIT); + if (rc < 0) + mmi_err(chip, + "Couldn't set USBIN_LOAD_CFG rc=%d\n", rc); + + if (!chip->enable_factory_mode_aicl) { + rc = smblib_masked_write_mmi(chip, USBIN_AICL_OPTIONS_CFG_REG, + 0xFF, 0x00); + if (rc < 0) + mmi_err(chip, + "Couldn't set USBIN_AICL_OPTIONS rc=%d\n", rc); + } + chip->smb_reboot.notifier_call = smbchg_reboot; + chip->smb_reboot.next = NULL; + chip->smb_reboot.priority = 1; + rc = register_reboot_notifier(&chip->smb_reboot); + if (rc) + mmi_err(chip, "Register for reboot failed\n"); + rc = power_supply_get_property(chip->pc_port_psy, + POWER_SUPPLY_PROP_ONLINE, + &val); + if (rc >= 0 && val.intval) { + mmi_info(chip, "Factory Kill Armed\n"); + chip->factory_kill_armed = true; + } + + if(chip->chg_dis_votable) { + pmic_vote_force_val_set(chip->chg_dis_votable, 1); + pmic_vote_force_active_set(chip->chg_dis_votable, 1); + } + if(chip->fcc_votable) { + pmic_vote_force_val_set(chip->fcc_votable, 3000000); + pmic_vote_force_active_set(chip->fcc_votable, 1); + } + if(chip->fv_votable) { + pmic_vote_force_val_set(chip->fv_votable, 4400000); + pmic_vote_force_active_set(chip->fv_votable, 1); + } + + /* Some Cables need a more forced approach */ + rc = smblib_set_charge_param(chip, &chip->param.usb_icl, + 3000000); + if (rc < 0) + mmi_err(chip, + "Factory Couldn't set usb icl = 3000 rc=%d\n", + (int)rc); + + rc = device_create_file(chip->dev, + &dev_attr_smblib_mmi_address); + if (rc) { + mmi_err(chip, + "Couldn't create smblib_mmi_address\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_smblib_mmi_data); + if (rc) { + mmi_err(chip, + "Couldn't create smblib_mmi_data\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_usb_suspend); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_usb_suspend\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_fail_clear); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_fail_clear\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_auto_enable); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_auto_enable\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_ibatt); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_ibatt\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_iusb); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_iusb\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_idc); + if (rc) { + mmi_err(chip, "Couldn't create force_chg_idc\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_itrick); + if (rc) { + mmi_err(chip, "Couldn't create force_chg_itrick\n"); + } + + } + + smb_mmi_create_debugfs(chip); + cancel_delayed_work(&chip->heartbeat_work); + if(chip->factory_mode) + { + //delay heartbeat in factory mode until after boot + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(10000)); + } + else + { + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(0)); + } + + mmi_info(chip, "QPNP SMB MMI probed successfully!\n"); + + return rc; +} + +static void smb_mmi_shutdown(struct platform_device *pdev) +{ + struct smb_mmi_charger *chip = platform_get_drvdata(pdev); + + if (chip->qcom_psy) + power_supply_put(chip->qcom_psy); + if (chip->bms_psy) + power_supply_put(chip->bms_psy); + if (chip->usb_psy) + power_supply_put(chip->usb_psy); + if (chip->pc_port_psy) + power_supply_put(chip->pc_port_psy); + if (chip->dc_psy) + power_supply_put(chip->dc_psy); + if (chip->max_main_psy) + power_supply_put(chip->max_main_psy); + if (chip->max_flip_psy) + power_supply_put(chip->max_flip_psy); + + return; +} + +#ifdef CONFIG_PM_SLEEP +static int smb_mmi_suspend(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct smb_mmi_charger *chip = platform_get_drvdata(pdev); + + chip->suspended &= ~WAS_SUSPENDED; + chip->suspended |= IS_SUSPENDED; + + return 0; +} + +static int smb_mmi_resume(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct smb_mmi_charger *chip = platform_get_drvdata(pdev); + + chip->suspended &= ~IS_SUSPENDED; + chip->suspended |= WAS_SUSPENDED; + + return 0; +} +#else +#define smb_mmi_suspend NULL +#define smb_mmi_resume NULL +#endif + +static const struct dev_pm_ops smb_mmi_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(smb_mmi_suspend, smb_mmi_resume) +}; + +static const struct of_device_id match_table[] = { + { + .compatible = "qcom,mmi-pm8150-smb5", + .data = (void *)PM8150B, + }, + { + .compatible = "qcom,mmi-pm7250b-smb5", + .data = (void *)PM7250B, + }, + { + .compatible = "qcom,mmi-pm6150-smb5", + .data = (void *)PM6150, + }, + { + .compatible = "qcom,mmi-pmi632-smb5", + .data = (void *)PMI632, + }, + { }, +}; + +static struct platform_driver smb_mmi_driver = { + .driver = { + .name = "qcom,mmi-smbcharger-iio", + .owner = THIS_MODULE, + .pm = &smb_mmi_dev_pm_ops, + .of_match_table = match_table, + }, + .probe = smb_mmi_probe, + .shutdown = smb_mmi_shutdown, +}; +module_platform_driver(smb_mmi_driver); + +MODULE_DESCRIPTION("QPNP SMB MMI Charger Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/mmi_charger/Android.mk b/drivers/power/mmi_charger/Android.mk new file mode 100755 index 000000000000..65b51656d681 --- /dev/null +++ b/drivers/power/mmi_charger/Android.mk @@ -0,0 +1,17 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_charger.ko +LOCAL_MODULE_TAGS := optional + +ifeq ($(MMI_RECHARGER_HAWAO_MODE),true) + KERNEL_CFLAGS += CONFIG_MMI_RECHARGER_HAWAO_MODE=y +endif + +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_info.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/drivers/power/mmi_charger/Kbuild b/drivers/power/mmi_charger/Kbuild new file mode 100755 index 000000000000..4740d681513b --- /dev/null +++ b/drivers/power/mmi_charger/Kbuild @@ -0,0 +1,16 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/mmi_charger +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +ifeq ($(TARGET_BUILD_VARIANT),userdebug) + EXTRA_CFLAGS += -DMMI_GKI_API_ALLOWANCE +endif + +ifneq ($(filter m y,$(CONFIG_MMI_RECHARGER_HAWAO_MODE)),) + EXTRA_CFLAGS += -DCONFIG_MMI_RECHARGER_HAWAO_MODE +endif + +obj-m += mmi_charger.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/mmi_charger/Makefile b/drivers/power/mmi_charger/Makefile new file mode 100755 index 000000000000..180b695cf050 --- /dev/null +++ b/drivers/power/mmi_charger/Makefile @@ -0,0 +1,16 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../mmi_info/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/mmi_charger/README b/drivers/power/mmi_charger/README new file mode 100644 index 000000000000..d4d4d4b13501 --- /dev/null +++ b/drivers/power/mmi_charger/README @@ -0,0 +1,660 @@ +Motorola Mobility. MMI Charger Specific Bindings + +As a common charger driver mmi_charger inherits Motorola legacy battery +charging features, including Demo charging, Step charging, Flash Fast +charging, and common features for factory test support. + +More importantly, it provides a cross-platform solution to support +various battery charging design, not only Qualcomm SoC but also Samsung, +MTK, even remote system over network, and especially suite multiple +battery packs and multiple charger ICs in one system. + +To achieve this goal, besides mmi_charger driver, there need a low level +charger driver for each battery charging solution(a couple of charger IC +& battery pack) to register to mmi_charger driver with implementation of +required interfaces depending on their own HW design. + +With mmi_charger driver, Motorola common features have been stripped +from the HW dependent charger drivers(older Motorola charger drivers), +in which all the common features have to be implemented by each driver +of its own. So a low level charger driver can focus on HW specific +features and function development. + +Motorola charger driver architecture: +----------------------------------------------------------------------------------------------------------- +Userspace Common Interfaces for Charging Features and Factory Test +----------------------------------------------------------------------------------------------------------- +Kernel mmi_charger (Motorola demo, step, flash-fast charging, + / | \ factory and debugging interfaces) + / | \ + qti_glink_charger x_charger N_charger (basic charging functions HW + | | | specific factory debugging interfaces) + | | | +------------glink----i2c------spmi-------------------------------------------------------------------------- + | | | + | | | + ADSP i2c chip PMIC + +Benefit from this new Motorola charger driver design: +1. Easy upgrade, migration, and maintenance, as common driver is + cross-platform and HW independent. +2. Reduce develop workload, as developers can pay more energy and + efforts on HW or platform specific business logic. +3. Easy to support complex battery charging solution, as it can support + N couples of charger IC & battery pack in one system at the same time + theoretically. + +Motorola common charger driver is built as DLKM: +Module Name: mmi_charger.ko +Log Tag: mmi_charger +IPC Log: /d/ipc_logging/mmi_charger/log +Support Platform: platform independent + +Motorola platform charger driver is built as DLKM: +(1) qti_glink_charger +Module Name: qti_glink_charger.ko +Log Tag: qti_glink_charger +IPC Log: /d/ipc_logging/mmi_charger/log +Support Platform: SM8350/SM7325(adsp and HLOS connection over glink) +(2) qpnp_smb_basic_charger +Module Name: qpnp_smb_basic_charger.ko +Log Tag: pmxxx_charger +IPC Log: /d/ipc_logging/mmi_charger/log +Support Platform: SM6125/SM710/SM636/SM7250/SM6350(qpnp smb charger with multi-battery support) + +devicetree properties description: +mmi_charger: +- mmi,enable-charging-limit + Usage: optional + Value Type: + Definition: Boolean flag to set capacity limits to battery when plugged on +a charger and is executing a Factory Image. It will use upper and lower +capacities defined by the Device Tree. + +- mmi,enable-factory-poweroff + Usage: optional + Value Type: + Definition: Boolean flag which enables Factory Kill when executing under a +Factory Image. By default, Factory Kill is a feature only in Factory Mode, +which turns the device off when charger is removed. + +- mmi,upper-limit-capacity + Usage: optional + Value Type: + Definition: Limits minimum battery SoC (in percentage). In other words, starts +charging when it reaches this value. Especially useful for factory procedures, +which needs to limit SoC. Requires qcom,enable-charging-limit. If not defined, +defaults to 100. + +- mmi,lower-limit-capacity + Usage: optional + Value Type: + Definition: Limits maximum battery SoC (in percentage). In other words, stop +charging when it reaches this value. Especially useful for factory procedures, +which needs to limit SoC. Requires qcom,enable-charging-limit. If not defined, +defaults to 0. + +- mmi,dcp-power-max + Usage: optional + Value Type: + Definition: Set this value to allow power in mW draw from DC charger, used + when a normal DCP charger is detected. If not set, 7.5W power is allowed. + +- mmi,hvdcp-power-max + Usage: optional + Value Type: + Definition: Set this value to allow power in mW draw from QC3.0 charger, used + when a QC3.0 charger is detected. If not set, 15W power is allowed. + +- mmi,pd-power-max + Usage: optional + Value Type: + Definition: Set this value to allow power in mW draw from pd charger, used + when a pd charger is detected. If not set, 15W power is allowed. + +- mmi,wls-power-max + Usage: optional + Value Type: + Definition: Set this value to allow power in mW draw from wireless charger, used + when a wireless charger is detected. If not set, 5W power is allowed. + +- mmi,heartbeat-interval +Usage: optional +Value Type: +Definition: Set the heartbeat interval of in ms, 60s by default if not set. + +Example: +mmi_charger: mmi,charger { + compatible = "mmi,mmi-charger"; + mmi,enable-charging-limit; + mmi,enable-factory-poweroff; + mmi,upper-limit-capacity = <75>; + mmi,lower-limit-capacity = <60>; + mmi,heartbeat-interval = <60000>; + mmi,dcp-power-max = <10000>; + mmi,hvdcp-power-max = <200000>; + mmi,pd-power-max = <200000>; + mmi,wls-power-max = <100000>; +}; + +platform low level charger: +- mmi,mmi-temp-zones + Usage: optional + Value Type: Array of tuples of + Definition: Unlimited array of arrays. Each internal array needs to have a set +of 4 bytes, each one representing this sequence: Temperature (in degC), Step +"Normalized" Voltage (in millivolts), Full Rate "FCC" Current (in milliamps), +and Taper Rate "FCC Normalized" Current (in milliamps). These values are +related to charging limitations based on temperature specifications. Note that +setting a voltage to '0' means that it doesn't matter the battery voltage on +that defined temperature range to perform necessary tapering. To understand how +Those values are used, please check the "IEEE Charging Compliance" section below. +In order to support charging current limit based on battery voltage in the same +temperature zone, one zone allows to have multiple lines with ascending sequence +by float voltage. First find the temperature zone, and then find the right one +among the same zone according to current battery voltage. + +- mmi,df-serialnum + Usage: optional + Value Type: + Definition: The default battery serial number for charging profile selection +and battery identification. + +- mmi,chrg-iterm-ma + Usage: optional + Value Type: + Definition: It will define taper current on battery (in milliamps). If charging +current becomes below this value, it indicates FULL charging. If not defined, +defaults to 300. + +- mmi,fg-iterm-ma + Usage: optional + Value Type: + Definition: It will define charging current on battery (in milliamps) as the +threshold for the fuel gauge to decide reporting FULL capacity. If not defined, +defaults to 350. + +- mmi,vfloat-comp-uv + Usage: optional + Value Type: + Definition: This is a tolerance, in uV, for battery floating voltage when +charging is being limited. Such situation can happen when either battery is +close to full or when there's thermal mitigation on charging. If not set, +defaults to 0. + +- mmi,max-fv-mv + Usage: optional + Value Type: + Definition: It will define the maximum battery charging voltage (in milliamps). +This value is used as the max float voltage for battery charging. If not +defined, defaults to 4400. + +- mmi,max-fcc-ma + Usage: optional + Value Type: + Definition: It will define maximum charging current on battery (in milliamps) +If not defined, defaults to 4000. + +- mmi,demo-fv-mv + Usage: optional + Value Type: + Definition: It will define the maximum battery charging voltage (in milliamps) +in demo mode. This value is used as the max float voltage for battery charging in +demo mode. If not defined, defaults to 4000. + +- profile-ids-map + Usage: optional(qti_glink_charger specific) + Value Type: Array of + Definition: Unlimited array of arrays. Each internal array needs to have a set +of 2 data members, each one representing this sequence: Battery Profile ID +(in integer value), Battery Serial Number(provided by battery spec). Battery +profile ID will be set to 0 if this property is not specified. + +- mmi,mmi-ffc-zones + Usage: optional + Value Type: Array of tuples of + Definition: Unlimited array of arrays. Each internal array needs to have a set +of 4 data members, each one representing this sequence: Temperature (in degC), +maximum Float Voltage (in millivolts), charging terminated current(in milliamps), +and Fuel Gauge FULL terminated current (in milliamps). These values are +related to charging limitations based on temperature specifications when the +battery supports Flash Fast Charging. + +- mmi,profile-data-block-size + Usage: optional(qti_glink_charger specific) + Value Type: + Definition: It will define the profile data member count each block line +in the profile data array. If not defined, defaults to 4. + +- mmi,profile-data + Usage: optional(qti_glink_charger specific) + Value Type: Array of tuples of + Definition: Unlimited array of arrays. Each internal array needs to have a set +with member count specified by property "mmi,profile-data-block-size". The content +for each line is defined by the target user of the profile data. + +- battery-names + Usage: optional(pm7250b_mmi specific) + Value Type: Array of + Definition: Unlimited array of arrays. Each internal array specifics a power supply + name which is used by the driver to get the battery information. + +- supplied-from + Usage: optional(pm7250b_mmi specific) + Value Type: Array of + Definition: Unlimited array of arrays. Each internal array specifics a power supply + name, the change of which the battery is sensitive to. + +- mmi,paired-id + Usage: optional + Value Type: + Definition: It will define the paired id for dual battery matching, high-16bits is used +for reference battery paired id, low-16bits is used for target battery paired id, +Only when the target battery paired id is equal to reference battery, then reference battery +information will be notified to the target battery when it is updated. +If not defined, defaults to 0. + +Example: +qti_glink_charger: mmi,qti-glink-charger { + compatible = "mmi,qti-glink-charger"; + mmi,df-serialnum = "unknown-sn"; + mmi,chrg-iterm-ma = <300>; + mmi,fg-iterm-ma = <350>; + mmi,max-fv-mv = <4400>; + mmi,max-fcc-ma = <4000>; + mmi,demo-fv-mv = <4000>; + mmi,vfloat-comp-uv = <20000>; + mmi,mmi-temp-zones = + <0 4200 880 0>, + <10 4200 2185 880>, + <15 0 2185 2185>, + <45 4000 3600 3600>, + <45 4250 3600 3000>, + <45 4350 3000 2000>, + <60 4200 2185 0>; + mmi,mmi-ffc-zones = + <0 4450 243 500>, + <23 4480 670 980>, + <35 4480 770 1045>, + <45 4480 840 1120>, + <60 4450 243 500>; + + mmi,profile-data-block-size = <4>; + mmi,profile-data = + <0 4200 880 0>, + <10 4200 2185 880>, + <15 0 2185 2185>, + <45 4000 3600 3600>, + <45 4250 3600 3000>, + <45 4350 3000 2000>, + <60 4200 2185 0>; + profile-ids-map = "0", "unknown-sn", "100750", "SB18C85232"; + mmi,paired-id = <0x00010000>; +}; + +Single-battery Pack: +pm7250b_mmi: qcom,qpnp-smb5-mmi { + status = "ok"; + compatible = "mmi,qpnp-smb-basic-charger"; + mmi,pmic-revid = <&pm7250b_revid>; + mmi,usbpd-phandle = <&pm7250b_pdphy>; + battery-names = "battery"; + supplied-from = "qcom_battery", "mmi_battery"; + + mmi,chrg-iterm-ma = <243>; + mmi,vfloat-comp-uv = <20000>; + mmi,fg-iterm-ma = <400>; + mmi,max-fv-mv = <4450>; + mmi,max-fcc-ma = <4000>; + mmi,demo-fv-mv = <4000>; + mmi,df-serialnum = "SB18C85232"; + mmi,mmi-temp-zones = <0 4200 1000 0>, + <10 4200 2500 1000>, + <15 0 2500 2500>, + <45 4200 4000 4000>, + <60 4200 3600 0>; +}; + +Multi-battery Pack: +pm7250b_mmi: qcom,qpnp-smb5-mmi { + status = "ok"; + compatible = "mmi,qpnp-smb-basic-charger"; + mmi,pmic-revid = <&pm7250b_revid>; + mmi,usbpd-phandle = <&pm7250b_pdphy>; + battery-names = "fg_main_battery", "fg_flip_battery"; + supplied-from = "qcom_battery", "mmi_battery", "fg_main_battery", "fg_flip_battery"; +}; + +&main_battery { + mmi,chrg-iterm-ma = <50>; + mmi,vfloat-comp-uv = <20000>; + mmi,fg-iterm-ma = <100>; + mmi,max-fv-mv = <4450>; + mmi,max-fcc-ma = <4000>; + mmi,demo-fv-mv = <4000>; + mmi,df-serialnum = "main_battery-sn"; + mmi,mmi-temp-zones = <0 4200 243 0>, + <10 4200 608 243>, + <15 0 608 608>, + <45 4450 1210 1200>, + <60 4200 608 0>; + mmi,paired-id = <0x00010000>; +}; + +&flip_battery { + mmi,chrg-iterm-ma = <50>; + mmi,vfloat-comp-uv = <20000>; + mmi,fg-iterm-ma = <100>; + mmi,max-fv-mv = <4450>; + mmi,max-fcc-ma = <4000>; + mmi,demo-fv-mv = <4000>; + mmi,df-serialnum = "flip_battery-sn"; + mmi,mmi-temp-zones = <0 4200 300 0>, + <10 4200 750 300>, + <15 0 750 750>, + <45 4450 1250 1200>, + <60 4200 750 0>; + mmi,paired-id = <0x00000001>; +}; + +========================== + IEEE Charging Compliance +========================== + +In order to be compliant to IEEE rules, there are a series of limitations on +how much a battery can be charged, based on temperature. That will limit how +much current can be drawn, or maximum battery voltage to be achieved during +charging. + +Current is always tapered when voltage achieves maximum voltage specification +(which is generally 4.4V, but may have a different value, depending a product +battery specs). It is tapered to some small value, possibly around 100mA, +which is based on product specifications and capacity testing, defined by +EE (Electrical Engineering) Team/Hardware. + +If temperature is below -20 degC or above 60 degC, it should automatically +stop charging the device. + +If temperature is in between 15 and 45 deg C, it can achieve maximum charging +current, and such value varies across products. Generally, if it supports PD, +this will be a much higher current than non-PD products, which will also have +a much higher current than dual-battery products. Important to note that this +is a rule-of-thumb, so always check product specifications. + +If temperature is between 0 and 10 deg C, but battery voltage is above 4.20V, +current should be tapered to 1000mA. + +Finally, if temperature is in between -20 and 0 deg C, current should be +tapered to 1000mA. + +NOTE1: whenever dealing with IEEE certification, most documentation and +variable notation across the bootloader and kernel may refer to JEITA. This is a +convention on such standards and values to which temperature, voltage and +current levels are accepted in different scenarios for Li-ion batteries. So +JEITA can be seen as a general standard, but IEEE enforces it as well as +provide ways to certify a product according to it (is mandatory to certify +any commercial product that has a lithium-ion battery embedded or plugged to +it). + +NOTE2: taper current mostly refers to when battery is above maximum allowed +voltage on a specific temperature region, based on rules above. So be aware +that the rest of this document may refer as "taper" to a current that is +limiting the maximum value, but before achieving a stage in which current +is limited to something around 100mA (so an intermediate stage). Also +"taper voltage" will refer to a voltage level in which this mentioned +"taper" current (intermediate current level) is triggered. + + +========================================= + How to define mmi,mmi-temp-zones array +========================================= + +When defining qcom,mmi-temp-zones array, it is important to always have in +mind the IEEE rules described above, as well the battery specifications. + +Each tuple in that array represents a different value, as shown here: + + +"temperature" defines maximum temperature value. So setting "35" means a +definition to anything below 35 degC. + +"taper_volt" means maximum allowed battery voltage in that temperature +range. So setting "4200" means that tapering will happen once battery +is above 4.2V and is still charging. + +"max_cur_below_taper_volt" defines maximum allowed current value in case +the battery is below "taper_volt" value. So setting it to "700" means that +current won't go above 700mA, as long as battery is below "taper_volt" +voltage. + +"max_cur_above_taper_volt" defines maximum allowed current value in case +battery is above "taper_volt" value. So setting it to "0" means that it +won't charge in case the battery is above "taper_volt" voltage. + +Here's an example from Dubai: + mmi,mmi-temp-zones = + <0 4200 880 0>, + <10 4200 2185 880>, + <15 0 2185 2185>, + <45 4000 3600 3600>, + <45 4250 3600 3000>, + <45 4350 3000 2000>, + <60 4200 2185 0>; +The first line states "If temperature is below 0 degC, and the battery voltage +is below 4.2V, device can be charged up to 880mA. But if the battery voltage +is above 4.2V, device cannot be charged". + +The third line states "If temperature is below 15 degC, the battery can be charged +up to 2185mA, no matter the battery voltage". + +The fourth to sixth line states that there are three lines in the same zone with +different taper_volt by ascending sequence, the charging current is limited by +both temperature and battery voltage. + +================================================= + Understanding the IEEE algorithm implementation +================================================= + +Now that IEEE rules were understood, as well as how qcom,mmi-temp-zones is set, +just to stress out this driver implementation, the thermal charging limitation +implementation will be described. + +mmi_basic_charge_sm() or mmi_dual_charge_sm() are the functions responsible +to enforce such rules. In that way, before we check the implementation, here +are some conventions: + +STEP_NONE: no charger present +STEP_STOP: in zone hot or zone cold, which limits charging considerably +STEP_NORM: limits current to some value +STEP_MAX: allows maximum current value, when in between 0 and 45 degC +STEP_FULL: battery is almost or completely full, tapering to a small current +STEP_FLOAT: TODO + +Important to note that even when moving from one state to another, temperature +may change. Hence, taper current and voltage will no longer correspond to such +state, and so is always necessary to check on every state in which temperature +range device is. + +"batt_ma" refers to battery current (equivalent to current_now on battery +sysfs). +"taper_ma" is "fcc_norm_ma" in case is in STATE_MAX, which means battery is +above taper voltage. If in any other state, it will refer to "chrg_iterm", +which will depend on device specifications (4.4V batteries have 97mA as +current standards, and 4.45V batteries have 121mA as taper current). + +Now, let's check the algorithms for a bit: + +----------------------------------------------------- +current-step: STEP_NONE | STEP_STOP + if norm_mv AND (batt_mv + 50 >= norm_mv) // (1) + if fcc_norm_ma // (2) + -goto STEP_NORM // (2) + else + -goto STEP_STOP // (2) + else + -goto STEP_MAX // (1) + +(1) are we in a range which has "norm_mv" defined, and battery is above this +value? +This corresponds to most temperature ranges, except in between 15 and 45 degC. +If that's the case, allow charging up to maximum current defined in product +spec. + +(2) is "fcc_norm_ma" a value different from zero? +This happens if temperature is in range between 0 and 45 degC. Due to previous +consideration, it is only missing range 0 to 15 degC here. If that's the case, +limit maximum current. Otherwise, battery is above "norm_mv" (which probably +is defined as 4.2V), and at that voltage and temperature, limiting charging +considerably. + +----------------------------------------------------- +current-step: STEP_MAX + if !norm_mv // (1) + taper_cnt = 0 + if (batt_mv + 50) > max_fv_mv // (2) + -goto STEP_NORM + else + -goto STEP_MAX + else if (batt_mv + 50) < norm_mv // (1) + taper_cnt = 0 + -goto STEP_MAX + else if !fcc_norm_ma // (3) + -goto STEP_FLOAT + else if current_has_tapered // (4) + taper_cnt = 0 + if charge_halted + vote chg_dis_votable + -goto STEP_NORM + +(1) are we in a range which has "norm_mv" NOT defined, or below taper voltage? +This is only for the 15 to 45 degC region, which has no current tapering. + +(2) are we above taper voltage? +This checks whether taper voltage was achieved, making current to taper to +defined values, depending on which temperature region battery is at this +moment. + +(3) at current temperature, is taper current defined? +If there is a taper current value to current temperature region (which is +usually in between 0 and 15 degC as defined in current implementation), allow +TODO STEP_FLOAT + +(4) has current been limited due to other thermal rules? +Function mmi_has_current_tapered() requests to power supply votable module +what is the allowed maximum current. Then such value will be compared to taper +current value, and the minimum will be chosen. Additionally, it checks whether +there's still a charger plugged to device or if charging is disabled, and if +any, mark a flag on the system displaying no charging is going on. Finally, move +to taper the current state (STEP_NORM). + +----------------------------------------------------- +current-step: STEP_NORM + if !fcc_norm_ma // (1) + -goto STEP_FLOAT + else if batt_soc < 100 or (batt_mv + 50) < max_fv_mv // (2) + taper_cnt = 0 + -goto STEP_NORM + else if current_has_tapered // (2) + -goto STEP_FULL + +(1) although device is in STEP_NORM, is there a defined taper current for +current temperature region device? +It may happen that temperature has changed, and device no longer needs to +be tapered. Hence, move to STEP_FLOAT. + +(2) is the battery NOT full or below taper voltage? +Stay in this state, because tapering is still happening. Otherwise, it may +be the case that battery is above taper voltage, or device is already fully +charged, forcing it to prepare to stop charging (STEP_FULL). + +----------------------------------------------------- +current-step: STEP_FULL + if batt_soc < 99 // (1) + taper_cnt = 0 + goto STEP_NORM + +(1) Is the battery no longer full? +It may happen that the battery device was previously full, but no longer is. So, +first move to a limited current state for safety, and then in such state, it +may happen that maximum current levels can be achieved. + +----------------------------------------------------- +current-step: STEP_FLOAT + if fcc_norm_ma or (batt_mv + 50) < norm_mv // (1) + goto STEP_MAX + if current_has_tapered // (2) + goto STEP_STOP + +(1) does battery temperature allow current to be tapered to something above +0mA, or battery is below taper voltage? +This means that temperature is in between 0 and 15 degC. If so, try to achieve +maximum current value (STEP_MAX). + +(2) does current need to be tapered? +In case mmi_has_current_tapered() needs to decrease current, it will request +charger to stop charging. + +----------------------------------------------------- +Setting current values based on state machine: + +STEP_FLOAT +STEP_MAX + target_fcc = fcc_max_ma + target_fv: + if !norm_mv + target_fv = max_fv_mv + chip->vfloat_comp_mv + else + target_fv = norm_mv + chip->vfloat_comp_mv + +STEP_FULL: + target_fcc = -EINVAL + target_fv = max_fv_mv + +STEP_NORM: + target_fcc = fcc_norm_ma + target_fv = max_fv_mv + chip->vfloat_comp_mv + +STEP_NONE: + target_fv = max_fv_mv + target_fcc = fcc_norm_ma + +========================================= + How to define mmi,mmi-ffc-zones array +========================================= + +When defining mmi,mmi-ffc-zones array, it is important to always have in +mind the rules described in the battery specifications, only the battery +support Flash Fast Charging can use it, otherwise battery damage or over +voltage protection may happen. + +Each tuple in that array represents a different value, as shown here: + + +"temperature" defines maximum temperature value. So setting "35" means a +definition to anything below 35 degC. + +"max_fv_volt" means maximum allowed battery voltage in that temperature +range. So setting "4480" means that the maximum float voltage is limited +to 4480. + +"charging_term_cur" means charging terminated current threshold in that +temperature range. Once the charging current becomes below the threshold, +the charging will be terminated. + +"fg_term_cur" means charging current threshold for FG to report full +charged in that temperature range. Once the charging current becomes +below the threshold, the FG will report FULL state of charge. + +Here's an example from Dubai: + mmi,mmi-ffc-zones = + <0 4450 243 500>, + <23 4480 670 980>, + <35 4480 770 1045>, + <45 4480 840 1120>, + <60 4450 243 500>; + +The third line states "If temperature is below 35 degC, and the maximum float voltage +is 4.48V, charging terminated current sets to 770mA, Fuel Gauge terminated current +sets to 1045mA. + + diff --git a/drivers/power/mmi_charger/ext_charger/Android.mk b/drivers/power/mmi_charger/ext_charger/Android.mk new file mode 100755 index 000000000000..1e03da262ed2 --- /dev/null +++ b/drivers/power/mmi_charger/ext_charger/Android.mk @@ -0,0 +1,10 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := ext_charger.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_charger.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/mmi_charger/ext_charger/Kbuild b/drivers/power/mmi_charger/ext_charger/Kbuild new file mode 100755 index 000000000000..19b07f6c0997 --- /dev/null +++ b/drivers/power/mmi_charger/ext_charger/Kbuild @@ -0,0 +1,7 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/mmi_charger + +obj-m += ext_charger.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/mmi_charger/ext_charger/ext_charger.c b/drivers/power/mmi_charger/ext_charger/ext_charger.c new file mode 100755 index 000000000000..97e9342abda1 --- /dev/null +++ b/drivers/power/mmi_charger/ext_charger/ext_charger.c @@ -0,0 +1,556 @@ +/* + * Copyright (C) 2021 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mmi_charger.h" + +#define BATT_SN_UNKNOWN "unknown-sn" + +static bool debug_enabled; +module_param(debug_enabled, bool, 0600); +MODULE_PARM_DESC(debug_enabled, "Enable debug for ext charger driver"); + +struct charger_profile_info { + int fg_iterm; + int chrg_iterm; + int max_fv_uv; + int max_fcc_ua; + int vfloat_comp_uv; + int demo_fv_uv; + int data_bk_size; /* number of byte for each data block */ + int data_size; /* number of byte for while data */ + int profile_id; /* profile id for charging profile selection in ADSP */ +}; + +struct ext_mmi_chg_t { + char *name; + struct device *dev; + bool *debug_enabled; + u32 chrg_taper_cnt; + struct mmi_battery_info batt_info; + struct mmi_charger_info chg_info; + struct mmi_charger_cfg chg_cfg; + struct mmi_charger_constraint constraint; + struct mmi_charger_driver *driver; + u32 *profile_data; + struct charger_profile_info profile_info; + void *ipc_log; + struct power_supply *fg_psy; + struct power_supply *chg_psy; + struct power_supply *usb_psy; +}; + +static int get_ps_int_prop(struct power_supply *psy, enum power_supply_property prop) +{ + int rc; + union power_supply_propval val; + + if (!psy) + return -1; + + rc = power_supply_get_property(psy, prop, &val); + if (rc < 0) { + pr_err("Error getting prop %d rc = %d\n", prop, rc); + return -1; + } + + return val.intval; +} + +static int set_ps_int_prop(struct power_supply *psy, enum power_supply_property prop, int value) +{ + int rc; + union power_supply_propval val; + + if (!psy) + return -1; + + val.intval = value; + + rc = power_supply_set_property(psy, prop, &val); + if (rc < 0) { + pr_err("Error getting prop %d rc = %d\n", prop, rc); + return -1; + } + + return 0; +} + +static int ext_mmi_chg_get_batt_info(void *data, struct mmi_battery_info *batt_info) +{ + struct ext_mmi_chg_t *chg = data; + + if (!chg || !chg->fg_psy) + return -1; + + chg->batt_info.batt_mv = get_ps_int_prop(chg->fg_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW); + chg->batt_info.batt_ma = get_ps_int_prop(chg->fg_psy, POWER_SUPPLY_PROP_CURRENT_NOW); + chg->batt_info.batt_soc = get_ps_int_prop(chg->fg_psy, POWER_SUPPLY_PROP_CAPACITY); + chg->batt_info.batt_temp = get_ps_int_prop(chg->fg_psy, POWER_SUPPLY_PROP_TEMP); + chg->batt_info.batt_status = get_ps_int_prop(chg->fg_psy, POWER_SUPPLY_PROP_STATUS); + chg->batt_info.batt_full_uah = get_ps_int_prop(chg->fg_psy, POWER_SUPPLY_PROP_CHARGE_FULL); + chg->batt_info.batt_design_uah = get_ps_int_prop(chg->fg_psy, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN); + + if (chg->chg_cfg.full_charged) + chg->batt_info.batt_status = POWER_SUPPLY_STATUS_FULL; + + chg->batt_info.batt_ma /= 1000; + chg->batt_info.batt_mv /= 1000; + chg->batt_info.batt_temp /= 10; + memcpy(batt_info, &chg->batt_info, sizeof(struct mmi_battery_info)); + + return 0; +} + +static int ext_mmi_chg_get_chg_info(void *data, struct mmi_charger_info *chg_info) +{ + int rc = 0; + struct ext_mmi_chg_t *chg = data; + int current_max, voltage_max; + + chg->chg_info.chrg_mv = get_ps_int_prop(chg->usb_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW); + chg->chg_info.chrg_ma = get_ps_int_prop(chg->usb_psy, POWER_SUPPLY_PROP_CURRENT_NOW); + chg->chg_info.chrg_type = get_ps_int_prop(chg->usb_psy, POWER_SUPPLY_PROP_USB_TYPE); + chg->chg_info.chrg_present = get_ps_int_prop(chg->usb_psy, POWER_SUPPLY_PROP_ONLINE); + + current_max = get_ps_int_prop(chg->usb_psy, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT); + voltage_max = get_ps_int_prop(chg->usb_psy, POWER_SUPPLY_PROP_VOLTAGE_MAX); + + if (current_max > 0 && voltage_max > 0) + chg->chg_info.chrg_pmax_mw = ((current_max/1000) * (voltage_max/1000)) / 1000; + else + chg->chg_info.chrg_pmax_mw = 0; + + chg->chg_info.chrg_mv /= 1000; + chg->chg_info.chrg_ma /= 1000; + memcpy(chg_info, &chg->chg_info, sizeof(struct mmi_charger_info)); + + return rc; +} + +static int ext_mmi_chg_config_charge(void *data, struct mmi_charger_cfg *config) +{ + int rc = 0; + u32 value; + struct ext_mmi_chg_t *chg = data; + + /* configure the charger if changed */ + if (config->target_fv != chg->chg_cfg.target_fv) { + value = config->target_fv * 1000; + rc = set_ps_int_prop(chg->chg_psy, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, value); + if (!rc) + chg->chg_cfg.target_fv = config->target_fv; + } + if (config->target_fcc != chg->chg_cfg.target_fcc) { + value = config->target_fcc * 1000; + rc = set_ps_int_prop(chg->chg_psy, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, value); + if (!rc) + chg->chg_cfg.target_fcc = config->target_fcc; + } + if (config->charger_suspend != chg->chg_cfg.charger_suspend) { + value = config->charger_suspend; + /* TODO write suspend, how is it different than disable */ + if (!rc) + chg->chg_cfg.charger_suspend = config->charger_suspend; + } + if (config->charging_disable != chg->chg_cfg.charging_disable) { + value = config->charging_disable; + rc = set_ps_int_prop(chg->chg_psy, POWER_SUPPLY_PROP_ONLINE, value); + if (!rc) + chg->chg_cfg.charging_disable = config->charging_disable; + } + + if (config->taper_kickoff != chg->chg_cfg.taper_kickoff) { + chg->chg_cfg.taper_kickoff = config->taper_kickoff; + chg->chrg_taper_cnt = 0; + } + + if (config->full_charged != chg->chg_cfg.full_charged) { + chg->chg_cfg.full_charged = config->full_charged; + } + + if (config->chrg_iterm != chg->chg_cfg.chrg_iterm) { + /* Unused configure in device tree for charger */ + } + if (config->fg_iterm != chg->chg_cfg.fg_iterm) { + /* Unused configure in device tree for fuel gauge or fuel gauge driver */ + } + + if (config->charging_reset != chg->chg_cfg.charging_reset) { + if (config->charging_reset) { + value = 1; + rc = set_ps_int_prop(chg->chg_psy, POWER_SUPPLY_PROP_ONLINE, value); + msleep(200); + value = 0; + rc = set_ps_int_prop(chg->chg_psy, POWER_SUPPLY_PROP_ONLINE, value); + } + chg->chg_cfg.charging_reset = config->charging_reset; + } + + return 0; +} + +#define TAPER_COUNT 2 +static bool ext_mmi_chg_is_charge_tapered(void *data, int tapered_ma) +{ + bool is_tapered = false; + struct ext_mmi_chg_t *chg = data; + + if (abs(chg->batt_info.batt_ma) <= tapered_ma) { + if (chg->chrg_taper_cnt >= TAPER_COUNT) { + is_tapered = true; + chg->chrg_taper_cnt = 0; + } else + chg->chrg_taper_cnt++; + } else + chg->chrg_taper_cnt = 0; + + return is_tapered; +} + +static bool ext_mmi_chg_is_charge_halt(void *data) +{ + struct ext_mmi_chg_t *chg = data; + + if (chg->batt_info.batt_status == POWER_SUPPLY_STATUS_NOT_CHARGING || + chg->batt_info.batt_status == POWER_SUPPLY_STATUS_FULL) + return true; + + return false; +} + +static void ext_mmi_chg_set_constraint(void *data, + struct mmi_charger_constraint *constraint) +{ + int rc = 0; + struct ext_mmi_chg_t *chg = data; + + if (constraint->demo_mode != chg->constraint.demo_mode) { + /*value = constraint->demo_mode; + TODO write value */ + if (!rc) + chg->constraint.demo_mode = constraint->demo_mode; + } + + if (constraint->factory_version != chg->constraint.factory_version) { + /*value = constraint->factory_version; + TODO write value */ + if (!rc) + chg->constraint.factory_version = constraint->factory_version; + } + + if (constraint->factory_mode != chg->constraint.factory_mode) { + /*value = constraint->factory_mode; + TODO write value */ + if (!rc) + chg->constraint.factory_mode = constraint->factory_mode; + } + + if (constraint->dcp_pmax != chg->constraint.dcp_pmax) { + /*value = constraint->dcp_pmax; + TODO write value */ + if (!rc) + chg->constraint.dcp_pmax = constraint->dcp_pmax; + } + + if (constraint->hvdcp_pmax != chg->constraint.hvdcp_pmax) { + /*value = constraint->hvdcp_pmax; + TODO write value */ + if (!rc) + chg->constraint.hvdcp_pmax = constraint->hvdcp_pmax; + } + + if (constraint->pd_pmax != chg->constraint.pd_pmax) { + /*value = constraint->pd_pmax; + TODO write value */ + if (!rc) + chg->constraint.pd_pmax = constraint->pd_pmax; + } + + if (constraint->wls_pmax != chg->constraint.wls_pmax) { + /*value = constraint->wls_pmax; + TODO write value */ + if (!rc) + chg->constraint.wls_pmax = constraint->wls_pmax; + } +} + +static int find_profile_id(const char *batt_sn) +{ + int i; + struct profile_sn_map { + int id; + const char *sn; + } map_table[] = { + {0, BATT_SN_UNKNOWN}, + }; + + i = sizeof(map_table) / sizeof(struct profile_sn_map); + while (i > 0 && map_table[--i].sn && strcmp(map_table[i].sn, batt_sn)); + + return map_table[i].id; +} + +static int ext_mmi_chg_parse_dt(struct ext_mmi_chg_t *chg) +{ + int rc; + int i, j; + int n = 0; + int bk_num; + int bk_size; + char bk_buf[128]; + int byte_len; + const char *df_sn = NULL, *dev_sn = NULL; + struct device_node *node; + const char *fg_psy_name, *chg_psy_name; + + node = chg->dev->of_node; + + rc = of_property_read_string(node, "mmi,fg-psy", &fg_psy_name); + rc |= of_property_read_string(node, "mmi,chg-psy", &chg_psy_name); + if (!rc && fg_psy_name && chg_psy_name) { + chg->fg_psy = power_supply_get_by_name(fg_psy_name); + chg->chg_psy = power_supply_get_by_name(chg_psy_name); + if (!chg->fg_psy || !chg->chg_psy) { + mmi_dbg(chg, "Could not get fg psy and chg psy, maybe we are early - defer."); + return -EPROBE_DEFER; + } + } + + dev_sn = mmi_get_battery_serialnumber(); + if (!dev_sn) { + rc = of_property_read_string(node, "mmi,df-serialnum", + &df_sn); + if (!rc && df_sn) { + mmi_dbg(chg, "Default Serial Number %s\n", df_sn); + } else { + mmi_err(chg, "No Default Serial Number defined\n"); + df_sn = BATT_SN_UNKNOWN; + } + strcpy(chg->batt_info.batt_sn, df_sn); + } else { + strcpy(chg->batt_info.batt_sn, dev_sn); + } + chg->profile_info.profile_id = find_profile_id(chg->batt_info.batt_sn); + + rc = of_property_read_u32(node, "mmi,chrg-iterm-ma", + &chg->profile_info.chrg_iterm); + if (rc) { + chg->profile_info.chrg_iterm = 300000; + } else { + chg->profile_info.chrg_iterm *= 1000; + } + + rc = of_property_read_u32(node, "mmi,fg-iterm-ma", + &chg->profile_info.fg_iterm); + if (rc) { + chg->profile_info.fg_iterm = + chg->profile_info.chrg_iterm + 50000; + } else { + chg->profile_info.fg_iterm *= 1000; + } + + rc = of_property_read_u32(node, "mmi,vfloat-comp-uv", + &chg->profile_info.vfloat_comp_uv); + if (rc) + chg->profile_info.vfloat_comp_uv = 0; + + rc = of_property_read_u32(node, "mmi,max-fv-mv", + &chg->profile_info.max_fv_uv); + if (rc) + chg->profile_info.max_fv_uv = 4400; + chg->profile_info.max_fv_uv *= 1000; + + rc = of_property_read_u32(node, "mmi,max-fcc-ma", + &chg->profile_info.max_fcc_ua); + if (rc) + chg->profile_info.max_fcc_ua = 4000; + chg->profile_info.max_fcc_ua *= 1000; + + rc = of_property_read_u32(node, "mmi,demo-fv-mv", + &chg->profile_info.demo_fv_uv); + if (rc) + chg->profile_info.demo_fv_uv = 4000; + chg->profile_info.demo_fv_uv *= 1000; + + rc = of_property_read_u32(node, "mmi,profile-data-block-size", + &chg->profile_info.data_bk_size); + if (rc) + chg->profile_info.data_bk_size = 4; + chg->profile_info.data_bk_size *= 4; + + chg->profile_info.data_size = 0; + if (of_find_property(node, "mmi,profile-data", &byte_len)) { + if (byte_len % chg->profile_info.data_bk_size) { + mmi_err(chg, "DT error wrong profile data\n"); + chg->profile_info.data_bk_size = 0; + return -ENODEV; + } + bk_num = byte_len / chg->profile_info.data_bk_size; + chg->profile_data = (u32 *)devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + if (chg->profile_data == NULL) { + chg->profile_info.data_bk_size = 0; + return -ENOMEM; + } + + rc = of_property_read_u32_array(node, + "mmi,profile-data", + chg->profile_data, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_err(chg, "Couldn't read profile data, rc = %d\n", rc); + devm_kfree(chg->dev, chg->profile_data); + chg->profile_data = NULL; + chg->profile_info.data_bk_size = 0; + return rc; + } + + chg->profile_info.data_size = byte_len; + mmi_info(chg, "profile data: block size: %d, num: %d\n", + chg->profile_info.data_bk_size, bk_num); + bk_size = chg->profile_info.data_bk_size / 4; + for (i = 0; i < bk_num; i++) { + memset(bk_buf, '\0', sizeof(bk_buf)); + n = sprintf(bk_buf, "block%d:", i); + for (j = 0; j < bk_size; j++) { + n += sprintf(bk_buf + n, " %d", + chg->profile_data[i * bk_size + j]); + } + mmi_info(chg, "%s\n", bk_buf); + } + } + + return 0; +} + +static int ext_mmi_chg_init_driver(struct ext_mmi_chg_t *chg) +{ + struct mmi_charger_driver *driver; + int rc; + + if (chg->driver) { + mmi_warn(chg, "charger has already inited\n"); + return 0; + } + + driver = devm_kzalloc(chg->dev, + sizeof(struct mmi_charger_driver), + GFP_KERNEL); + if (!driver) + return -ENOMEM; + + /* init driver */ + driver->name = chg->name; + driver->dev = chg->dev; + driver->data = chg; + driver->get_batt_info = ext_mmi_chg_get_batt_info; + driver->get_chg_info = ext_mmi_chg_get_chg_info; + driver->config_charge = ext_mmi_chg_config_charge; + driver->is_charge_tapered = ext_mmi_chg_is_charge_tapered; + driver->is_charge_halt = ext_mmi_chg_is_charge_halt; + driver->set_constraint = ext_mmi_chg_set_constraint; + chg->driver = driver; + + /* register driver to mmi charger */ + rc = mmi_register_charger_driver(driver); + if (rc) { + mmi_err(chg, "mmi charger init failed, rc=%d\n", rc); + return rc; + } else { + mmi_info(chg, "mmi charger init successfully\n"); + } + + // TODO force update in mmi_charger on register to set correct parameters. + + return 0; +} + +static int ext_mmi_chg_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ext_mmi_chg_t *chg; + int rc; + + chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL); + if (!chg) + return -ENOMEM; + + platform_set_drvdata(pdev, chg); + chg->dev = dev; + chg->name = "ext_charger"; + chg->debug_enabled = &debug_enabled; + + chg->ipc_log = ipc_log_context_create(MMI_LOG_PAGES, MMI_LOG_DIR, 0); + if (!chg->ipc_log) + mmi_warn(chg, "Error in creating ipc_log_context\n"); + + rc = ext_mmi_chg_parse_dt(chg); + if (rc) { + if (rc != -EPROBE_DEFER) + mmi_err(chg, "dt parser failed, rc=%d\n", rc); + return rc; + } + + chg->usb_psy = power_supply_get_by_name("usb"); + if (!chg->usb_psy) { + mmi_dbg(chg, "Could not get usb psy, maybe we are early - defer.\n"); + return -EPROBE_DEFER; + } + + if (ext_mmi_chg_init_driver(chg)) { + mmi_err(chg, "Failed to init chg driver %d\n", rc); + return -ENODEV; + } + + return 0; +} + +static int ext_mmi_chg_remove(struct platform_device *pdev) +{ + // TODO implement + return 0; +} + +static const struct of_device_id ext_mmi_chg_match_table[] = { + {.compatible = "mmi,ext-mmi-chg"}, + {}, +}; + +static struct platform_driver ext_mmi_chg = { + .driver = { + .name = "ext_charger", + .of_match_table = ext_mmi_chg_match_table, + }, + .probe = ext_mmi_chg_probe, + .remove = ext_mmi_chg_remove, +}; + +module_platform_driver(ext_mmi_chg); + +MODULE_DESCRIPTION("External Charger Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/mmi_charger/mmi_charger.c b/drivers/power/mmi_charger/mmi_charger.c new file mode 100644 index 000000000000..9fa081203381 --- /dev/null +++ b/drivers/power/mmi_charger/mmi_charger.c @@ -0,0 +1,3107 @@ +/* + * Copyright (C) 2020 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mmi_charger.h" + +#define HYST_STEP_MV 50 +#define DEMO_MODE_HYS_SOC 5 +#define WARM_TEMP 45 +#define COOL_TEMP 0 + +#define BATT_PAIR_ID_BITS 16 +#define BATT_PAIR_ID_MASK ((1 << BATT_PAIR_ID_BITS) - 1) + +#define HEARTBEAT_DELAY_MS 60000 +#define HEARTBEAT_FACTORY_MS 1000 +#define HEARTBEAT_DISCHARGE_MS 100000 +#define HEARTBEAT_WAKEUP_INTRVAL_NS 70000000000 + +static bool debug_enabled; +module_param(debug_enabled, bool, 0600); +MODULE_PARM_DESC(debug_enabled, "Enable debug for mmi charger driver"); + +static int factory_kill_disable; +module_param(factory_kill_disable, int, 0644); +static int suspend_wakeups; +module_param(suspend_wakeups, int, 0644); + +static bool shutdown_triggered = false; + +static struct mmi_charger_chip *this_chip = NULL; + +enum { + MMI_POWER_SUPPLY_CHARGE_RATE_NONE = 0, + MMI_POWER_SUPPLY_CHARGE_RATE_NORMAL, + MMI_POWER_SUPPLY_CHARGE_RATE_WEAK, + MMI_POWER_SUPPLY_CHARGE_RATE_TURBO, + MMI_POWER_SUPPLY_CHARGE_RATE_TURBO_30W, + MMI_POWER_SUPPLY_CHARGE_RATE_HYPER, +}; + +enum { + NOTIFY_EVENT_TYPE_CHG_RATE = 0, + NOTIFY_EVENT_TYPE_LPD_PRESENT, + NOTIFY_EVENT_TYPE_VBUS_PRESENT, + NOTIFY_EVENT_TYPE_POWER_WATT, +}; + +static char *charge_rate[] = { + "None", "Normal", "Weak", "Turbo", "Turbo_30W", "Hyper" +}; + +#define MAX_NUM_TEMP_ZONE 10 +enum mmi_temp_zones { + ZONE_FIRST = 0, + /* states 0-9 are reserved for zones */ + ZONE_LAST = MAX_NUM_TEMP_ZONE + ZONE_FIRST - 1, + ZONE_HOT, + ZONE_COLD, + ZONE_NONE, +}; + +struct mmi_temp_zone { + int temp_c; + int norm_mv; + int fcc_max_ma; + int fcc_norm_ma; +}; + +enum mmi_chrg_step { + STEP_MAX, + STEP_NORM, + STEP_FULL, + STEP_FLOAT, + STEP_DEMO, + STEP_STOP, + STEP_NONE, +}; + +static char *stepchg_str[] = { + [STEP_MAX] = "MAX", + [STEP_NORM] = "NORMAL", + [STEP_FULL] = "FULL", + [STEP_FLOAT] = "FLOAT", + [STEP_DEMO] = "DEMO", + [STEP_STOP] = "STOP", + [STEP_NONE] = "NONE", +}; + +struct mmi_ffc_zone { + int temp; + int fv; + int chrg_iterm; + int fg_iterm; +}; + +enum charging_limit_modes { + CHARGING_LIMIT_OFF, + CHARGING_LIMIT_RUN, + CHARGING_LIMIT_UNKNOWN, +}; + +struct mmi_battery_pack { + int health; + int status; + int age; + int cycles; + int pending; + int reference; + int charger_rate; + int soc_cycles_start; + int paired_id; + struct mmi_battery_pack *paired_batt; + struct mmi_battery_info *info; + char sn[MMI_BATT_SN_LEN]; + struct list_head list; +}; + +struct mmi_charger_profile { + //for not FFC battery profile + int noffc_fg_iterm; + int noffc_chrg_iterm; + int noffc_chrg_iterm_35_45c; + int noffc_max_fv_mv; + + int shutdown_empty_vbat_mv; + + int fg_iterm; + int chrg_iterm; + int max_fv_mv; + int max_fcc_ma; + int vfloat_comp_mv; + int demo_fv_mv; + int num_temp_zones; + struct mmi_temp_zone *temp_zones; + int num_ffc_zones; + struct mmi_ffc_zone *ffc_zones; +}; + +struct mmi_charger_status { + int demo_full_soc; + bool demo_chrg_suspend; + struct mmi_temp_zone *temp_zone; + enum mmi_temp_zones pres_temp_zone; + enum mmi_chrg_step pres_chrg_step; + enum charging_limit_modes charging_limit_modes; +}; + +struct mmi_charger { + struct mmi_charger_profile profile; + struct mmi_charger_status status; + struct mmi_charger_driver *driver; + struct mmi_battery_info batt_info; + struct mmi_charger_info chg_info; + struct mmi_charger_cfg cfg; + struct mmi_charger_constraint constraint; + struct mmi_battery_pack *battery; + struct list_head list; +}; + +#define MMI_VOTE_NUM_MAX 32 +struct mmi_vote { + const char *name; + int votes[MMI_VOTE_NUM_MAX]; + const char *voters[MMI_VOTE_NUM_MAX]; +}; + +#define IS_SUSPENDED BIT(0) +#define WAS_SUSPENDED BIT(1) + +struct mmi_charger_chip { + char *name; + struct device *dev; + struct list_head charger_list; + struct mutex charger_lock; + struct list_head battery_list; + struct mutex battery_lock; + + struct power_supply *mmi_psy; + struct power_supply *batt_psy; + char *batt_uenvp[2]; + int combo_cycles; + int combo_soc; + int combo_age; + int combo_voltage_mv; + int combo_current_ma; + int combo_status; + int combo_health; + int combo_temp; + int combo_charge_counter; + int charge_full; + int charge_full_design; + int init_cycles; + int max_charger_rate; + bool vbus_present; + bool lpd_present; + int power_watt; + + int suspended; + int demo_mode; + bool factory_mode; + bool factory_version; + bool factory_kill_armed; + bool force_charger_disabled; + bool force_charging_enabled; + + int dcp_pmax; + int hvdcp_pmax; + int pd_pmax; + int wls_pmax; + int max_chrg_temp; + bool enable_charging_limit; + bool enable_factory_poweroff; + bool factory_syspoweroff_wait; + bool start_factory_kill_disabled; + int upper_limit_capacity; + int lower_limit_capacity; + + struct wakeup_source *mmi_hb_wake_source; + struct alarm heartbeat_alarm; + int heartbeat_interval; + struct notifier_block mmi_reboot; + struct notifier_block mmi_psy_notifier; + struct delayed_work heartbeat_work; + + bool *debug_enabled; + void *ipc_log; + + struct mmi_vote suspend_charger_vote; + struct mmi_vote disable_charging_vote; + uint32_t factory_kill_debounce_ms; + + bool empty_vbat_shutdown_triggered; + + int heartbeat_dischg_ms; + uint32_t ibat_calc_alignment_time; +}; + +static int mmi_vote(struct mmi_vote *vote, const char *voter, + bool enabled, int value) +{ + int i; + int empty = -EINVAL; + struct mmi_charger_chip *chip = this_chip; + + if (!chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + if (!voter) { + mmi_err(chip, "%s: Invalid voter\n", vote->name); + return -EINVAL; + } + + for (i = 0; i < MMI_VOTE_NUM_MAX; i++) { + if (vote->voters[i] && + !strcmp(vote->voters[i], voter)) { + break; + } else if (empty < 0 && !vote->voters[i]) { + empty = i; + } + } + + if (i < MMI_VOTE_NUM_MAX) { + if (enabled) { + vote->voters[i] = voter; + vote->votes[i] = value; + } else { + vote->voters[i] = NULL; + vote->votes[i] = 0; + } + } else { + if (!enabled) { + return 0; + } else if (empty < 0) { + mmi_err(this_chip, "No entry found\n"); + return -ENOENT; + } else { + vote->voters[empty] = voter; + vote->votes[empty] = value; + } + } + + mmi_info(chip, "%s:%s voter: en:%d, val:%d\n", + vote->name, voter, enabled, value); + + return 0; +} + +static int mmi_get_effective_voter(struct mmi_vote *vote) +{ + int i; + int win_voter = -EINVAL; + int win_vote = INT_MAX; + + for (i = 0; i < MMI_VOTE_NUM_MAX; i++) { + if (vote->voters[i] && + vote->votes[i] < win_vote) { + win_voter = i; + win_vote = vote->votes[i]; + } + } + + return win_voter; +} + +static void mmi_notify_charger_event(struct mmi_charger_chip *chip, int type); +static ssize_t state_sync_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + if (!this_chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &mode); + if (r) { + mmi_err(this_chip, "Invalid state_sync value = %lu\n", mode); + return -EINVAL; + } + + if (mode) { + mutex_lock(&this_chip->charger_lock); + mmi_notify_charger_event(this_chip, + NOTIFY_EVENT_TYPE_CHG_RATE); + mmi_notify_charger_event(this_chip, + NOTIFY_EVENT_TYPE_LPD_PRESENT); + mmi_notify_charger_event(this_chip, + NOTIFY_EVENT_TYPE_VBUS_PRESENT); + mmi_notify_charger_event(this_chip, + NOTIFY_EVENT_TYPE_POWER_WATT); + mutex_unlock(&this_chip->charger_lock); + cancel_delayed_work(&this_chip->heartbeat_work); + schedule_delayed_work(&this_chip->heartbeat_work, + msecs_to_jiffies(0)); + mmi_info(this_chip, "charger state sync received\n"); + } + + return count; +} +static DEVICE_ATTR(state_sync, 0200, NULL, state_sync_store); + +#define CHARGER_POWER_5W 5000 +#define CHARGER_POWER_7P5W 7500 +#define CHARGER_POWER_10W 10000 +#define CHARGER_POWER_15W 15000 +#define CHARGER_POWER_18W 18000 +#define CHARGER_POWER_20W 20000 +#define CHARGER_POWER_MAX 100000 +static ssize_t dcp_pmax_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long pmax; + + if (!this_chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &pmax); + if (r) { + mmi_err(this_chip, "Invalid dcp pmax value = %lu\n", pmax); + return -EINVAL; + } + + if (this_chip->dcp_pmax != pmax && + (pmax >= CHARGER_POWER_5W && pmax <= CHARGER_POWER_10W)) { + this_chip->dcp_pmax = pmax; + cancel_delayed_work(&this_chip->heartbeat_work); + schedule_delayed_work(&this_chip->heartbeat_work, + msecs_to_jiffies(0)); + } + + return r ? r : count; +} + +static ssize_t dcp_pmax_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (!this_chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", this_chip->dcp_pmax); +} + +static DEVICE_ATTR(dcp_pmax, 0644, + dcp_pmax_show, + dcp_pmax_store); + +static ssize_t hvdcp_pmax_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long pmax; + + if (!this_chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &pmax); + if (r) { + mmi_err(this_chip, "Invalid hvdcp pmax value = %lu\n", pmax); + return -EINVAL; + } + + if (this_chip->hvdcp_pmax != pmax && + (pmax >= CHARGER_POWER_7P5W && pmax <= CHARGER_POWER_MAX)) { + this_chip->hvdcp_pmax = pmax; + cancel_delayed_work(&this_chip->heartbeat_work); + schedule_delayed_work(&this_chip->heartbeat_work, + msecs_to_jiffies(0)); + } + + return r ? r : count; +} + +static ssize_t hvdcp_pmax_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (!this_chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", this_chip->hvdcp_pmax); +} + +static DEVICE_ATTR(hvdcp_pmax, 0644, + hvdcp_pmax_show, + hvdcp_pmax_store); + +static ssize_t pd_pmax_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long pmax; + + if (!this_chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &pmax); + if (r) { + mmi_err(this_chip, "Invalid pd pmax value = %lu\n", pmax); + return -EINVAL; + } + + if (this_chip->pd_pmax != pmax && + (pmax >= CHARGER_POWER_15W && pmax <= CHARGER_POWER_MAX)) { + this_chip->pd_pmax = pmax; + cancel_delayed_work(&this_chip->heartbeat_work); + schedule_delayed_work(&this_chip->heartbeat_work, + msecs_to_jiffies(0)); + } + + return r ? r : count; +} + +static ssize_t pd_pmax_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (!this_chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", this_chip->pd_pmax); +} + +static DEVICE_ATTR(pd_pmax, 0644, + pd_pmax_show, + pd_pmax_store); + +static ssize_t wls_pmax_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long pmax; + + if (!this_chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &pmax); + if (r) { + mmi_err(this_chip, "Invalid wireless pmax value = %lu\n", pmax); + return -EINVAL; + } + + if (this_chip->wls_pmax != pmax && + (pmax >= CHARGER_POWER_5W && pmax <= CHARGER_POWER_MAX)) { + this_chip->wls_pmax = pmax; + cancel_delayed_work(&this_chip->heartbeat_work); + schedule_delayed_work(&this_chip->heartbeat_work, + msecs_to_jiffies(0)); + } + + return r ? r : count; +} + +static ssize_t wls_pmax_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (!this_chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", this_chip->wls_pmax); +} + +static DEVICE_ATTR(wls_pmax, 0644, + wls_pmax_show, + wls_pmax_store); + +static ssize_t factory_image_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + if (!this_chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &mode); + if (r) { + mmi_err(this_chip, "Invalid factory image mode value = %lu\n", mode); + return -EINVAL; + } + + this_chip->factory_version = (mode) ? true : false; + + return r ? r : count; +} + +static ssize_t factory_image_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + + if (!this_chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + state = (this_chip->factory_version) ? 1 : 0; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(factory_image_mode, 0644, + factory_image_mode_show, + factory_image_mode_store); + +static ssize_t factory_charge_upper_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + + if (!this_chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + state = this_chip->upper_limit_capacity; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(factory_charge_upper, 0444, + factory_charge_upper_show, + NULL); + +#define MMI_CHIP_MODE_LOWER_LIMIT 35 +#define MMI_CHIP_MODE_UPPER_LIMIT 80 +static ssize_t force_demo_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + if (!this_chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &mode); + if (r) { + mmi_err(this_chip, "Invalid demo mode value = %lu\n", mode); + return -EINVAL; + } + + if ((mode >= MMI_CHIP_MODE_LOWER_LIMIT) && + (mode <= MMI_CHIP_MODE_UPPER_LIMIT)) + this_chip->demo_mode = mode; + else + this_chip->demo_mode = MMI_CHIP_MODE_LOWER_LIMIT; + + return r ? r : count; +} + +static ssize_t force_demo_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + + if (!this_chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + state = this_chip->demo_mode; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_demo_mode, 0644, + force_demo_mode_show, + force_demo_mode_store); + +#define MIN_TEMP_C -20 +#define MAX_TEMP_C 60 +#define MIN_MAX_TEMP_C 47 +#define HYSTERESIS_DEGC 2 +static ssize_t force_max_chrg_temp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + if (!this_chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &mode); + if (r) { + mmi_err(this_chip, "Invalid max temp value = %lu\n", mode); + return -EINVAL; + } + + if ((mode >= MIN_MAX_TEMP_C) && (mode <= MAX_TEMP_C)) + this_chip->max_chrg_temp = mode; + else + this_chip->max_chrg_temp = MAX_TEMP_C; + + return r ? r : count; +} + +static ssize_t force_max_chrg_temp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + + if (!this_chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + state = this_chip->max_chrg_temp; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_max_chrg_temp, 0644, + force_max_chrg_temp_show, + force_max_chrg_temp_store); + +static ssize_t charge_rate_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (!this_chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%s\n", + charge_rate[this_chip->max_charger_rate]); +} +static DEVICE_ATTR(charge_rate, S_IRUGO, charge_rate_show, NULL); + +static ssize_t age_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (!this_chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -ENODEV; + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", this_chip->combo_age); +} +static DEVICE_ATTR(age, S_IRUGO, age_show, NULL); + +static ssize_t force_charger_suspend_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + + if (!this_chip) { + pr_err("mmi_charger: chip not valid\n"); + return -ENODEV; + } + + state = this_chip->force_charger_disabled; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static ssize_t force_charger_suspend_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + if (!this_chip) { + pr_err("mmi_charger: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("mmi_charger: Invalid charger suspend value = %lu\n", mode); + return -EINVAL; + } + + this_chip->force_charger_disabled = (mode) ? true : false; + cancel_delayed_work(&this_chip->heartbeat_work); + schedule_delayed_work(&this_chip->heartbeat_work, msecs_to_jiffies(0)); + mmi_info(this_chip, "%s force_charger_disabled\n", (mode)? "set" : "clear"); + + return count; +} + +static DEVICE_ATTR(force_charger_suspend, 0644, + force_charger_suspend_show, + force_charger_suspend_store); + +static ssize_t force_charging_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + + if (!this_chip) { + pr_err("mmi_charger: chip not valid\n"); + return -ENODEV; + } + + state = this_chip->force_charging_enabled; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static ssize_t force_charging_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + if (!this_chip) { + pr_err("mmi_charger: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("mmi_charger: Invalid charger enable value = %lu\n", mode); + return -EINVAL; + } + + this_chip->force_charging_enabled = (mode) ? true : false; + cancel_delayed_work(&this_chip->heartbeat_work); + schedule_delayed_work(&this_chip->heartbeat_work, msecs_to_jiffies(0)); + mmi_info(this_chip, "%s force_charging_enabled\n", (mode)? "set" : "clear"); + + return count; +} + +static DEVICE_ATTR(force_charging_enable, 0644, + force_charging_enable_show, + force_charging_enable_store); + +static ssize_t force_charging_disable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + if (!this_chip) { + pr_err("mmi_charger: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("mmi_charger: Invalid charger disable value = %lu\n", mode); + return -EINVAL; + } + + mmi_vote_charging_disable("MMI_USER", !!mode); + cancel_delayed_work(&this_chip->heartbeat_work); + schedule_delayed_work(&this_chip->heartbeat_work, msecs_to_jiffies(0)); + mmi_info(this_chip, "%s force_charging_disable\n", (mode)? "set" : "clear"); + + return count; +} + +static DEVICE_ATTR(force_charging_disable, 0200, + NULL, + force_charging_disable_store); + +static struct attribute * mmi_g[] = { + &dev_attr_charge_rate.attr, + &dev_attr_age.attr, + NULL, +}; + +static const struct attribute_group power_supply_mmi_attr_group = { + .attrs = mmi_g, +}; + +static void mmi_battery_supply_init(struct mmi_charger_chip *chip) +{ + int rc; + + if (chip->batt_psy) + return; + + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) { + mmi_err(chip, "No battery supply found\n"); + return; + } + + rc = sysfs_create_group(&chip->batt_psy->dev.kobj, + &power_supply_mmi_attr_group); + if (rc) + mmi_err(chip, "failed: attr create\n"); + + rc = device_create_file(chip->batt_psy->dev.parent, + &dev_attr_force_demo_mode); + if (rc) { + mmi_err(chip, "couldn't create force_demo_mode\n"); + } + + rc = device_create_file(chip->batt_psy->dev.parent, + &dev_attr_force_max_chrg_temp); + if (rc) { + mmi_err(chip, "couldn't create force_max_chrg_temp\n"); + } + + rc = device_create_file(chip->batt_psy->dev.parent, + &dev_attr_factory_image_mode); + if (rc) { + mmi_err(chip, "couldn't create factory_image_mode\n"); + } + + rc = device_create_file(chip->batt_psy->dev.parent, + &dev_attr_factory_charge_upper); + if (rc) + mmi_err(chip, "couldn't create factory_charge_upper\n"); + + rc = device_create_file(chip->batt_psy->dev.parent, + &dev_attr_force_charger_suspend); + if (rc) + mmi_err(chip, "couldn't create force_charger_suspend\n"); + + rc = device_create_file(chip->batt_psy->dev.parent, + &dev_attr_force_charging_enable); + if (rc) + mmi_err(chip, "couldn't create force_charging_enable\n"); + + rc = device_create_file(chip->batt_psy->dev.parent, + &dev_attr_force_charging_disable); + if (rc) + mmi_err(chip, "couldn't create force_charging_disable\n"); + + mmi_info(chip, "battery supply is initialized\n"); +} + +static int mmi_get_charger_profile(struct mmi_charger_chip *chip, + struct mmi_charger *charger) +{ + int rc; + int i; + int byte_len; + struct device_node *node; + + if (!charger->driver || !charger->driver->dev) { + mmi_err(chip, "mmi charger driver is invalid\n"); + return -EINVAL; + } + + node = charger->driver->dev->of_node; + rc = of_property_read_u32(node, "mmi,shutdown-empty-vbat-mv", + &charger->profile.shutdown_empty_vbat_mv); + if (rc) + charger->profile.shutdown_empty_vbat_mv = -EINVAL; + + rc = of_property_read_u32(node, "mmi,chrg-iterm-ma", + &charger->profile.chrg_iterm); + if (rc) + charger->profile.chrg_iterm = 300; + + charger->profile.noffc_chrg_iterm = charger->profile.chrg_iterm; + + rc = of_property_read_u32(node, "mmi,chrg-iterm-ma-noffc-35-45c", + &charger->profile.noffc_chrg_iterm_35_45c); + if (rc) + charger->profile.noffc_chrg_iterm_35_45c = -1; + + rc = of_property_read_u32(node, "mmi,fg-iterm-ma", + &charger->profile.fg_iterm); + if (rc) + charger->profile.fg_iterm = charger->profile.chrg_iterm + 50; + + charger->profile.noffc_fg_iterm = charger->profile.fg_iterm; + + rc = of_property_read_u32(node, "mmi,vfloat-comp-uv", + &charger->profile.vfloat_comp_mv); + if (rc) + charger->profile.vfloat_comp_mv = 0; + charger->profile.vfloat_comp_mv /= 1000; + + rc = of_property_read_u32(node, "mmi,max-fv-mv", + &charger->profile.max_fv_mv); + if (rc) + charger->profile.max_fv_mv = 4400; + + charger->profile.noffc_max_fv_mv = charger->profile.max_fv_mv; + + rc = of_property_read_u32(node, "mmi,max-fcc-ma", + &charger->profile.max_fcc_ma); + if (rc) + charger->profile.max_fcc_ma = 4400; + + rc = of_property_read_u32(node, "mmi,demo-fv-mv", + &charger->profile.demo_fv_mv); + if (rc) + charger->profile.demo_fv_mv = 4000; + + if (of_find_property(node, "mmi,mmi-temp-zones", &byte_len)) { + if ((byte_len / sizeof(u32)) % 4) { + mmi_err(chip, "[C:%s]: DT error wrong mmi temp zones\n", + charger->driver->name); + return -ENODEV; + } + + charger->profile.temp_zones = (struct mmi_temp_zone *) + devm_kzalloc(chip->dev, byte_len, GFP_KERNEL); + + if (charger->profile.temp_zones == NULL) + return -ENOMEM; + + charger->profile.num_temp_zones = + byte_len / sizeof(struct mmi_temp_zone); + + rc = of_property_read_u32_array(node, + "mmi,mmi-temp-zones", + (u32 *)charger->profile.temp_zones, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_err(chip, "[C:%s]: Couldn't read mmi temp zones rc = %d\n", + charger->driver->name, rc); + devm_kfree(chip->dev, charger->profile.temp_zones); + charger->profile.temp_zones = NULL; + return rc; + } + + mmi_info(chip, "[C:%s]: mmi temp zones: Num: %d\n", + charger->driver->name, + charger->profile.num_temp_zones); + for (i = 0; i < charger->profile.num_temp_zones; i++) { + mmi_info(chip, "[C:%s]: mmi temp zones: Zone %d, Temp %d C, " \ + "Step Volt %d mV, Full Rate %d mA, " \ + "Taper Rate %d mA\n", charger->driver->name, i, + charger->profile.temp_zones[i].temp_c, + charger->profile.temp_zones[i].norm_mv, + charger->profile.temp_zones[i].fcc_max_ma, + charger->profile.temp_zones[i].fcc_norm_ma); + } + } + + if (of_find_property(node, "mmi,mmi-ffc-zones", &byte_len)) { + if ((byte_len / sizeof(u32)) % 4) { + mmi_err(chip, "[C:%s]: DT error wrong mmi ffc zones\n", + charger->driver->name); + return -ENODEV; + } + + charger->profile.ffc_zones = (struct mmi_ffc_zone *) + devm_kzalloc(chip->dev, byte_len, GFP_KERNEL); + + if (charger->profile.ffc_zones == NULL) + return -ENOMEM; + + charger->profile.num_ffc_zones = + byte_len / sizeof(struct mmi_ffc_zone); + + rc = of_property_read_u32_array(node, + "mmi,mmi-ffc-zones", + (u32 *)charger->profile.ffc_zones, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_err(chip, "[C:%s]: Couldn't read mmi ffc zones rc = %d\n", + charger->driver->name, rc); + devm_kfree(chip->dev, charger->profile.ffc_zones); + charger->profile.ffc_zones = NULL; + return rc; + } + + mmi_info(chip, "[C:%s]: mmi ffc zones: Num: %d\n", + charger->driver->name, + charger->profile.num_ffc_zones); + for (i = 0; i < charger->profile.num_ffc_zones; i++) { + mmi_info(chip, "[C:%s]: mmi ffc zones: Zone %d, Temp %d C, " \ + "FV %d mV, Chg Iterm %d mA, Fg Iterm %d mA\n", + charger->driver->name, i, + charger->profile.ffc_zones[i].temp, + charger->profile.ffc_zones[i].fv, + charger->profile.ffc_zones[i].chrg_iterm, + charger->profile.ffc_zones[i].fg_iterm); + } + } + + return 0; +} + +#define TURBO_CHRG_FFC_THRSH_MW 25000 +static bool mmi_is_ffc_enabled(struct mmi_charger *charger) +{ + struct mmi_charger_info *chg_info = &charger->chg_info; + + if (!charger->profile.num_ffc_zones || !charger->profile.ffc_zones) + return false; + + if (charger->driver->is_ffc_enabled) { + return charger->driver->is_ffc_enabled(charger->driver->data,charger->status.pres_chrg_step); + } + else if (chg_info->chrg_pmax_mw > TURBO_CHRG_FFC_THRSH_MW) { + return true; + } + else { + return false; + } +} + + +static void mmi_update_charger_profile(struct mmi_charger_chip *chip, + struct mmi_charger *charger) +{ + int i = 0; + int temp; + int num_zones; + struct mmi_ffc_zone *zones; + + if (!chip) { + pr_err("called before chg valid!\n"); + return; + } + + if (!mmi_is_ffc_enabled(charger)) { + charger->profile.max_fv_mv = charger->profile.noffc_max_fv_mv; + charger->profile.fg_iterm = charger->profile.noffc_fg_iterm; + charger->profile.chrg_iterm = charger->profile.noffc_chrg_iterm; + + temp = charger->batt_info.batt_temp; + if(charger->profile.noffc_chrg_iterm_35_45c > 0 && temp >= 35 && temp <= 45) { + charger->profile.chrg_iterm = charger->profile.noffc_chrg_iterm_35_45c; + pr_info("enter mmi_update_charger_profile with temp:%d, chrg_iterm:%d, noffc_chrg_iterm_35_45c:%d\n", temp, charger->profile.chrg_iterm, charger->profile.noffc_chrg_iterm_35_45c); + } + return; + } + + temp = charger->batt_info.batt_temp; + num_zones = charger->profile.num_ffc_zones; + zones = charger->profile.ffc_zones; + while (i < num_zones && temp > zones[i++].temp); + zones = i > 0? &zones[i - 1] : NULL; + if (zones) { + charger->profile.max_fv_mv = zones->fv; + charger->profile.fg_iterm = zones->fg_iterm; + charger->profile.chrg_iterm = zones->chrg_iterm; + mmi_dbg(chip, "[C:%s]: Max FV:%d, FG Iterm:%d, Chrg Iterm:%d\n", + charger->driver->name, + charger->profile.max_fv_mv, + charger->profile.fg_iterm, + charger->profile.chrg_iterm); + } +} + +static int mmi_find_colder_temp_zone(int pres_zone, int vbat, + struct mmi_temp_zone *zones, + int num_zones) +{ + int i; + int colder_zone; + int target_zone; + + if (pres_zone == ZONE_HOT) + colder_zone = num_zones - 1; + else if (pres_zone == ZONE_COLD || + zones[pres_zone].temp_c == zones[ZONE_FIRST].temp_c) + return ZONE_COLD; + else { + for (i = pres_zone - 1; i >= ZONE_FIRST; i--) { + if (zones[pres_zone].temp_c > zones[i].temp_c) { + colder_zone = i; + break; + } + } + if (i < 0) + return ZONE_COLD; + } + + target_zone = colder_zone; + for (i = ZONE_FIRST; i < colder_zone; i++) { + if (zones[colder_zone].temp_c == zones[i].temp_c) { + target_zone = i; + if (vbat < zones[i].norm_mv) + break; + } + } + + return target_zone; +} + +static int mmi_find_hotter_temp_zone(int pres_zone, int vbat, + struct mmi_temp_zone *zones, + int num_zones) +{ + int i; + int hotter_zone; + int target_zone; + + if (pres_zone == ZONE_COLD) + hotter_zone = ZONE_FIRST; + else if (pres_zone == ZONE_HOT || + zones[pres_zone].temp_c == zones[num_zones - 1].temp_c) + return ZONE_HOT; + else { + for (i = pres_zone + 1; i < num_zones; i++) { + if (zones[pres_zone].temp_c < zones[i].temp_c) { + hotter_zone = i; + break; + } + } + if (i >= num_zones) + return ZONE_HOT; + } + + target_zone = hotter_zone; + for (i = hotter_zone; i < num_zones; i++) { + if (zones[hotter_zone].temp_c == zones[i].temp_c) { + target_zone = i; + if (vbat < zones[i].norm_mv) + break; + } + } + return target_zone; +} + +static int mmi_refresh_temp_zone(int pres_zone, int vbat, + struct mmi_temp_zone *zones, + int num_zones) +{ + int i; + int target_zone; + + if (pres_zone == ZONE_COLD || pres_zone == ZONE_HOT) + return pres_zone; + + target_zone = pres_zone; + for (i = ZONE_FIRST; i < num_zones; i++) { + if (zones[pres_zone].temp_c == zones[i].temp_c) { + target_zone = i; + if (vbat < zones[i].norm_mv) + break; + } + } + return target_zone; +} + +static void mmi_get_temp_zone(struct mmi_charger_chip *chip, + struct mmi_charger *charger) +{ + int i; + int temp_c; + int vbat_mv; + int max_temp; + int prev_zone, num_zones; + int hotter_zone, colder_zone; + struct mmi_temp_zone *zones; + int hotter_t, hotter_fcc; + int colder_t, colder_fcc; + + if (!chip) { + pr_err("called before chg valid!\n"); + return; + } + + temp_c = charger->batt_info.batt_temp; + vbat_mv = charger->batt_info.batt_mv; + prev_zone = charger->status.pres_temp_zone; + num_zones = charger->profile.num_temp_zones; + if (!charger->profile.temp_zones) { + zones = NULL; + num_zones = 0; + if (chip->max_chrg_temp >= MIN_MAX_TEMP_C) + max_temp = chip->max_chrg_temp; + else + max_temp = MAX_TEMP_C; + } else { + zones = charger->profile.temp_zones; + if (chip->max_chrg_temp >= MIN_MAX_TEMP_C) + max_temp = chip->max_chrg_temp; + else + max_temp = zones[num_zones - 1].temp_c; + } + + if (prev_zone == ZONE_NONE && zones) { + for (i = num_zones - 1; i >= 0; i--) { + if (temp_c >= zones[i].temp_c) { + charger->status.pres_temp_zone = + mmi_find_hotter_temp_zone(i, + vbat_mv, + zones, + num_zones); + goto exit; + } + } + if (temp_c < MIN_TEMP_C) + charger->status.pres_temp_zone = ZONE_COLD; + else + charger->status.pres_temp_zone = + mmi_find_hotter_temp_zone(ZONE_COLD, + vbat_mv, + zones, + num_zones); + goto exit; + } + + if (prev_zone == ZONE_COLD) { + if (temp_c >= MIN_TEMP_C + HYSTERESIS_DEGC) { + if (!num_zones) + charger->status.pres_temp_zone = ZONE_FIRST; + else + charger->status.pres_temp_zone = + mmi_find_hotter_temp_zone(prev_zone, + vbat_mv, + zones, + num_zones); + } + } else if (prev_zone == ZONE_HOT) { + if (temp_c <= max_temp - HYSTERESIS_DEGC) { + if (!num_zones) + charger->status.pres_temp_zone = ZONE_FIRST; + else + charger->status.pres_temp_zone = + mmi_find_colder_temp_zone(prev_zone, + vbat_mv, + zones, + num_zones); + } + } else if (zones) { + hotter_zone = mmi_find_hotter_temp_zone(prev_zone, + vbat_mv, + zones, + num_zones); + colder_zone = mmi_find_colder_temp_zone(prev_zone, + vbat_mv, + zones, + num_zones); + if (hotter_zone == ZONE_HOT) { + hotter_fcc = 0; + hotter_t = zones[prev_zone].temp_c; + } else { + hotter_fcc = zones[hotter_zone].fcc_max_ma; + hotter_t = zones[prev_zone].temp_c; + } + + if (colder_zone == ZONE_COLD) { + colder_fcc = 0; + colder_t = MIN_TEMP_C; + } else { + colder_fcc = zones[colder_zone].fcc_max_ma; + colder_t = zones[colder_zone].temp_c; + } + + if (zones[prev_zone].fcc_max_ma < hotter_fcc) + hotter_t += HYSTERESIS_DEGC; + + if (zones[prev_zone].fcc_max_ma < colder_fcc) + colder_t -= HYSTERESIS_DEGC; + + if (temp_c < MIN_TEMP_C) + charger->status.pres_temp_zone = ZONE_COLD; + else if (temp_c >= max_temp) + charger->status.pres_temp_zone = ZONE_HOT; + else if (temp_c >= hotter_t) + charger->status.pres_temp_zone = hotter_zone; + else if (temp_c < colder_t) + charger->status.pres_temp_zone = colder_zone; + else + charger->status.pres_temp_zone = + mmi_refresh_temp_zone(prev_zone, + vbat_mv, + zones, + num_zones); + } else { + if (temp_c < MIN_TEMP_C) + charger->status.pres_temp_zone = ZONE_COLD; + else if (temp_c >= max_temp) + charger->status.pres_temp_zone = ZONE_HOT; + else + charger->status.pres_temp_zone = ZONE_FIRST; + } + +exit: + if (!zones || + charger->status.pres_temp_zone == ZONE_COLD || + charger->status.pres_temp_zone == ZONE_HOT) { + charger->status.temp_zone = NULL; + } else { + charger->status.temp_zone = &zones[charger->status.pres_temp_zone]; + } + if (prev_zone != charger->status.pres_temp_zone) { + mmi_info(chip, "[C:%s]: temp zone switch %x -> %x\n", + charger->driver->name, + prev_zone, + charger->status.pres_temp_zone); + } +} + +static void mmi_update_paired_battery( + struct mmi_charger_chip *chip, + struct mmi_battery_pack *battery) +{ + struct mmi_battery_pack *batt = NULL; + + if (!battery->paired_id) { + battery->paired_batt = NULL; + return; + } + + list_for_each_entry(batt, &chip->battery_list, list) { + if ((batt->paired_id >> BATT_PAIR_ID_BITS) == + (battery->paired_id & BATT_PAIR_ID_MASK)) { + battery->paired_batt = batt; + } + if ((battery->paired_id >> BATT_PAIR_ID_BITS) == + (batt->paired_id & BATT_PAIR_ID_MASK)) { + batt->paired_batt = battery; + } + } +} + +static void mmi_notify_paired_battery(struct mmi_charger *charger) +{ + if (!charger->battery->paired_batt || + !charger->driver->notify_paired_battery) + return; + + charger->driver->notify_paired_battery(charger->driver->data, + charger->battery->paired_batt->info); +} + +static int mmi_get_cur_thermal_level(struct mmi_charger_chip *chip, int *val) +{ + union power_supply_propval prop; + int ret; + if (!chip->batt_psy) { + mmi_err(chip, "No battery supply found\n"); + return -ENODEV; + } + + ret = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &prop); + if (!ret) + *val = prop.intval; + + return ret; +} + +static void mmi_get_charger_info(struct mmi_charger_chip *chip, + struct mmi_charger *charger) +{ + struct mmi_battery_info *batt_info = &charger->batt_info; + struct mmi_charger_info *chg_info = &charger->chg_info; + int thermal_level = 0; + + mmi_get_cur_thermal_level(chip, &thermal_level); + charger->driver->get_batt_info(charger->driver->data, batt_info); + charger->driver->get_chg_info(charger->driver->data, chg_info); + mmi_info(chip, "[C:%s]: batt_mv %d, batt_ma %d, batt_soc %d," + " batt_temp %d, batt_status %d, batt_sn %s, batt_fv_mv %d," + " batt_fcc_ma %d\n", + charger->driver->name, + batt_info->batt_mv, + batt_info->batt_ma, + batt_info->batt_soc, + batt_info->batt_temp, + batt_info->batt_status, + batt_info->batt_sn, + batt_info->batt_fv_mv, + batt_info->batt_fcc_ma); + mmi_info(chip, "[C:%s]: chrg_present %d, chrg_type %d, chrg_pmax_mw %d," + " chrg_mv %d, chrg_ma %d, chrg_otg_enabled %d, thermal_level %d\n", + charger->driver->name, + chg_info->chrg_present, + chg_info->chrg_type, + chg_info->chrg_pmax_mw, + chg_info->chrg_mv, + chg_info->chrg_ma, + chg_info->chrg_otg_enabled, + thermal_level); + +} + +static void mmi_update_charger_status(struct mmi_charger_chip *chip, + struct mmi_charger *charger) +{ + bool voltage_full; + int stop_recharge_hyst; + enum charging_limit_modes charging_limit_modes; + struct mmi_charger_profile *profile = &charger->profile; + struct mmi_charger_status *status = &charger->status; + struct mmi_battery_info *batt_info = &charger->batt_info; + struct mmi_charger_info *chg_info = &charger->chg_info; + struct mmi_charger_cfg *cfg = &charger->cfg; + + if (profile->shutdown_empty_vbat_mv > 0 && + profile->shutdown_empty_vbat_mv >= batt_info->batt_mv) { + mmi_err(chip, "[C:%s]: trigger shutdown, vbat=%d, empty_vbat=%d\n", + charger->driver->name, + batt_info->batt_mv, + profile->shutdown_empty_vbat_mv); + chip->empty_vbat_shutdown_triggered = true; + } + + if (chip->enable_charging_limit && chip->factory_version) { + charging_limit_modes = status->charging_limit_modes; + if ((charging_limit_modes != CHARGING_LIMIT_RUN) + && (batt_info->batt_soc >= chip->upper_limit_capacity)) + charging_limit_modes = CHARGING_LIMIT_RUN; + else if ((charging_limit_modes != CHARGING_LIMIT_OFF) + && (batt_info->batt_soc <= chip->lower_limit_capacity)) + charging_limit_modes = CHARGING_LIMIT_OFF; + + if (charging_limit_modes != charger->status.charging_limit_modes) { + status->charging_limit_modes = charging_limit_modes; + if (status->charging_limit_modes == CHARGING_LIMIT_RUN) + mmi_warn(chip, "Factory Mode/Image so Limiting Charging!!!\n"); + } + } + + mmi_get_temp_zone(chip, charger); + if (!chg_info->chrg_present) { + status->pres_chrg_step = STEP_NONE; + } else if ((status->pres_temp_zone == ZONE_HOT) || + (status->pres_temp_zone == ZONE_COLD) || + (status->charging_limit_modes == CHARGING_LIMIT_RUN)) { + status->pres_chrg_step = STEP_STOP; + } else if (chip->demo_mode) { /* Demo Mode */ + status->pres_chrg_step = STEP_DEMO; + voltage_full = ((status->demo_chrg_suspend == false) && + ((batt_info->batt_mv + HYST_STEP_MV) >= profile->demo_fv_mv) && + charger->driver->is_charge_tapered(charger->driver->data, profile->chrg_iterm)); + + if ((status->demo_chrg_suspend == false) && + ((batt_info->batt_soc >= chip->demo_mode) || voltage_full)) { + status->demo_full_soc = batt_info->batt_soc; + status->demo_chrg_suspend = true; + } else if (status->demo_chrg_suspend == true && + (batt_info->batt_soc <= (status->demo_full_soc - DEMO_MODE_HYS_SOC))) { + status->demo_chrg_suspend = false; + cfg->taper_kickoff = true; + } + } else if (!status->temp_zone) { + status->pres_chrg_step = STEP_MAX; + /* Skip for empty temperature zone */ + } else if (status->pres_chrg_step == STEP_NONE) { + if (status->temp_zone->norm_mv && + ((batt_info->batt_mv + HYST_STEP_MV) >= status->temp_zone->norm_mv)) { + if (status->temp_zone->fcc_norm_ma) + status->pres_chrg_step = STEP_NORM; + else + status->pres_chrg_step = STEP_STOP; + } else + status->pres_chrg_step = STEP_MAX; + } else if (status->pres_chrg_step == STEP_STOP) { + if (batt_info->batt_temp > COOL_TEMP) + stop_recharge_hyst = 2 * HYST_STEP_MV; + else + stop_recharge_hyst = 5 * HYST_STEP_MV; + if (status->temp_zone->norm_mv && + ((batt_info->batt_mv + stop_recharge_hyst) >= status->temp_zone->norm_mv)) { + if (status->temp_zone->fcc_norm_ma) + status->pres_chrg_step = STEP_NORM; + else + status->pres_chrg_step = STEP_STOP; + } else + status->pres_chrg_step = STEP_MAX; + } else if (status->pres_chrg_step == STEP_MAX) { + if (!status->temp_zone->norm_mv) { + /* No Step in this Zone */ + cfg->taper_kickoff = true; + if ((batt_info->batt_mv + HYST_STEP_MV) >= profile->max_fv_mv) + status->pres_chrg_step = STEP_NORM; + else + status->pres_chrg_step = STEP_MAX; + } else if ((batt_info->batt_mv + HYST_STEP_MV) < status->temp_zone->norm_mv) { + cfg->taper_kickoff = true; + status->pres_chrg_step = STEP_MAX; + } else if (!status->temp_zone->fcc_norm_ma) + status->pres_chrg_step = STEP_FLOAT; + else if (charger->driver->is_charge_tapered(charger->driver->data, status->temp_zone->fcc_norm_ma)) { + cfg->taper_kickoff = true; + if (charger->driver->is_charge_halt && + charger->driver->is_charge_halt(charger->driver->data)) { + cfg->charging_reset = true; + mmi_warn(chip, "[C:%s]: Charge Halt..Toggle\n", + charger->driver->name); + } + status->pres_chrg_step = STEP_NORM; + } + } else if (status->pres_chrg_step == STEP_NORM) { + if (!status->temp_zone->fcc_norm_ma) + status->pres_chrg_step = STEP_FLOAT; + else if ((batt_info->batt_soc < 100) || + (batt_info->batt_mv + HYST_STEP_MV) < profile->max_fv_mv) { + cfg->taper_kickoff = true; + status->pres_chrg_step = STEP_NORM; + } else if (charger->driver->is_charge_tapered(charger->driver->data, profile->chrg_iterm)) { + status->pres_chrg_step = STEP_FULL; + } + } else if (status->pres_chrg_step == STEP_FULL) { +#ifdef CONFIG_MMI_RECHARGER_HAWAO_MODE + if ((batt_info->batt_soc <= 98) || + batt_info->batt_mv < (profile->max_fv_mv - 100 * 2)) +#else + if ((batt_info->batt_soc <= 99) || + batt_info->batt_mv < (profile->max_fv_mv - HYST_STEP_MV * 2)) +#endif + { + cfg->taper_kickoff = true; + status->pres_chrg_step = STEP_NORM; + } + } else if (status->pres_chrg_step == STEP_FLOAT) { + if ((status->temp_zone->fcc_norm_ma) || + ((batt_info->batt_mv + HYST_STEP_MV) < status->temp_zone->norm_mv)) + status->pres_chrg_step = STEP_MAX; + else if (charger->driver->is_charge_tapered(charger->driver->data, profile->chrg_iterm)) + status->pres_chrg_step = STEP_STOP; + } + + charger->battery->pending++; + + mmi_info(chip, "[C:%s]: StepChg: %s, TempZone: %d, LimitMode: %d, DemoSuspend: %d\n", + charger->driver->name, + stepchg_str[(int)status->pres_chrg_step], + status->pres_temp_zone, + status->charging_limit_modes, + status->demo_chrg_suspend); +} + +static void mmi_reset_charger_configure(struct mmi_charger_chip *chip, + struct mmi_charger *charger) +{ + charger->cfg.target_fv = charger->profile.max_fv_mv; + charger->cfg.target_fcc = -EINVAL; + charger->cfg.chrg_iterm = charger->profile.chrg_iterm; + charger->cfg.fg_iterm = charger->profile.fg_iterm; + charger->cfg.taper_kickoff = false; + charger->cfg.charging_reset = false; + charger->cfg.full_charged = false; + charger->cfg.charging_disable = false; + charger->cfg.charger_suspend = false; + + charger->constraint.demo_mode = chip->demo_mode; + charger->constraint.factory_version = chip->factory_version; + charger->constraint.factory_mode = chip->factory_mode; + charger->constraint.dcp_pmax = chip->dcp_pmax; + charger->constraint.hvdcp_pmax = chip->hvdcp_pmax; + charger->constraint.pd_pmax = chip->pd_pmax; + charger->constraint.wls_pmax = chip->wls_pmax; +} + +static void mmi_configure_charger(struct mmi_charger_chip *chip, + struct mmi_charger *charger) +{ + struct mmi_charger_profile *profile = &charger->profile; + struct mmi_charger_status *status = &charger->status; + struct mmi_charger_cfg *cfg = &charger->cfg; + + switch (status->pres_chrg_step) { + case STEP_FLOAT: + case STEP_MAX: + if (!status->temp_zone || !status->temp_zone->norm_mv) + cfg->target_fv = profile->max_fv_mv + profile->vfloat_comp_mv; + else + cfg->target_fv = status->temp_zone->norm_mv + profile->vfloat_comp_mv; + if (!status->temp_zone) + cfg->target_fcc = profile->max_fcc_ma; + else + cfg->target_fcc = status->temp_zone->fcc_max_ma; + break; + case STEP_FULL: + cfg->target_fv = profile->max_fv_mv; + cfg->target_fcc = -EINVAL; + cfg->full_charged = true; + break; + case STEP_NORM: + cfg->target_fv = profile->max_fv_mv + profile->vfloat_comp_mv; + if (!status->temp_zone) + cfg->target_fcc = profile->max_fcc_ma; + else + cfg->target_fcc = status->temp_zone->fcc_norm_ma; + break; + case STEP_NONE: + cfg->target_fv = profile->max_fv_mv; + if (!status->temp_zone) + cfg->target_fcc = profile->max_fcc_ma; + else + cfg->target_fcc = status->temp_zone->fcc_norm_ma; + break; + case STEP_STOP: + cfg->target_fv = profile->max_fv_mv; + cfg->target_fcc = -EINVAL; + break; + case STEP_DEMO: + cfg->target_fv = profile->demo_fv_mv; + if (!status->temp_zone) + cfg->target_fcc = profile->max_fcc_ma; + else + cfg->target_fcc = status->temp_zone->fcc_norm_ma; + break; + default: + cfg->target_fv = profile->max_fv_mv; + if (!status->temp_zone) + cfg->target_fcc = profile->max_fcc_ma; + else + cfg->target_fcc = status->temp_zone->fcc_norm_ma; + break; + } + + if (cfg->target_fcc < 0 || + mmi_get_effective_voter(&chip->disable_charging_vote) >= 0) + cfg->charging_disable = true; + else + cfg->charging_disable = false; + + if (chip->factory_mode) { + cfg->target_fv = 4400; + cfg->target_fcc = 3000; + cfg->charging_disable = true; + cfg->taper_kickoff = false; + cfg->charging_reset = false; + } + + if (chip->force_charger_disabled || + status->demo_chrg_suspend || + mmi_get_effective_voter(&chip->suspend_charger_vote) >= 0) + cfg->charger_suspend = true; + + if (chip->force_charging_enabled) { + cfg->charging_disable = false; + } + + mmi_notify_paired_battery(charger); + charger->driver->set_constraint(charger->driver->data, &charger->constraint); + charger->driver->config_charge(charger->driver->data, cfg); + + mmi_info(chip, "[C:%s]: FV=%d, FCC=%d, CDIS=%d," + " CSUS=%d, CRES=%d, CFULL=%d\n", + charger->driver->name, + cfg->target_fv, + cfg->target_fcc, + cfg->charging_disable, + cfg->charger_suspend, + cfg->charging_reset, + cfg->full_charged); +} + +static void mmi_notify_charger_event(struct mmi_charger_chip *chip, int type) +{ + char *event_string = NULL; + + if (!chip->batt_psy) { + mmi_battery_supply_init(chip); + if (!chip->batt_psy) + return; + } + + event_string = kmalloc(CHG_SHOW_MAX_SIZE, GFP_KERNEL); + if (event_string) { + switch (type) { + case NOTIFY_EVENT_TYPE_CHG_RATE: + scnprintf(event_string, CHG_SHOW_MAX_SIZE, + "POWER_SUPPLY_CHARGE_RATE=%s", + charge_rate[chip->max_charger_rate]); + break; + case NOTIFY_EVENT_TYPE_LPD_PRESENT: + scnprintf(event_string, CHG_SHOW_MAX_SIZE, + "POWER_SUPPLY_LPD_PRESENT=%s", + chip->lpd_present? "true" : "false"); + break; + case NOTIFY_EVENT_TYPE_VBUS_PRESENT: + scnprintf(event_string, CHG_SHOW_MAX_SIZE, + "POWER_SUPPLY_VBUS_PRESENT=%s", + chip->vbus_present? "true" : "false"); + break; + case NOTIFY_EVENT_TYPE_POWER_WATT: + scnprintf(event_string, CHG_SHOW_MAX_SIZE, + "POWER_SUPPLY_POWER_WATT=%d", + chip->power_watt / 1000); + break; + default: + mmi_err(chip, "Invalid notify event type %d\n", type); + kfree(event_string); + return; + } + + if (chip->batt_uenvp[0]) + kfree(chip->batt_uenvp[0]); + chip->batt_uenvp[0] = event_string; + chip->batt_uenvp[1] = NULL; + kobject_uevent_env(&chip->batt_psy->dev.kobj, + KOBJ_CHANGE, + chip->batt_uenvp); + } +} + +#define WEAK_CHRG_THRSH_MW 2500 +#define TURBO_CHRG_THRSH_MW 12500 +#define TURBO_CHRG_30W_THRSH_MW 25000 +#define HYPER_CHRG_THRSH_MW 40000 +int mmi_get_battery_charger_rate(struct mmi_charger *charger) +{ + int rate; + struct mmi_charger_info *info; + + info = &charger->chg_info; + if (!info->chrg_present) + rate = MMI_POWER_SUPPLY_CHARGE_RATE_NONE; + else if (info->chrg_pmax_mw < WEAK_CHRG_THRSH_MW) + rate = MMI_POWER_SUPPLY_CHARGE_RATE_WEAK; + else if (info->chrg_pmax_mw < TURBO_CHRG_THRSH_MW) + rate = MMI_POWER_SUPPLY_CHARGE_RATE_NORMAL; + else if (info->chrg_pmax_mw < TURBO_CHRG_30W_THRSH_MW) + rate = MMI_POWER_SUPPLY_CHARGE_RATE_TURBO; + else if (info->chrg_pmax_mw < HYPER_CHRG_THRSH_MW) + rate = MMI_POWER_SUPPLY_CHARGE_RATE_TURBO_30W; + else + rate = MMI_POWER_SUPPLY_CHARGE_RATE_HYPER; + + return rate; +} + +static int mmi_get_battery_health(struct mmi_charger *charger) +{ + int batt_health = POWER_SUPPLY_HEALTH_UNKNOWN; + struct mmi_charger_status *status = &charger->status; + struct mmi_battery_info *batt_info = &charger->batt_info; + + if (status->pres_temp_zone == ZONE_HOT) { + batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; + } else if (status->pres_temp_zone == ZONE_COLD) { + batt_health = POWER_SUPPLY_HEALTH_COLD; + } else if (batt_info->batt_temp >= WARM_TEMP) { + if (status->pres_chrg_step == STEP_STOP) + batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; + else + batt_health = POWER_SUPPLY_HEALTH_GOOD; + } else if (batt_info->batt_temp <= COOL_TEMP) { + if (status->pres_chrg_step == STEP_STOP) + batt_health = POWER_SUPPLY_HEALTH_COLD; + else + batt_health = POWER_SUPPLY_HEALTH_GOOD; + } else + batt_health = POWER_SUPPLY_HEALTH_GOOD; + + return batt_health; +} + +static int mmi_combine_battery_soc(struct mmi_charger_chip *chip) +{ + int soc = 0; + int remain = 0, full = 0; + struct mmi_battery_pack *battery = NULL; + + list_for_each_entry(battery, &chip->battery_list, list) { + remain += battery->info->batt_soc * battery->info->batt_full_uah; + full += battery->info->batt_full_uah; + } + + if (full > 0 && remain >= 0) { + soc = remain / full; + if (soc > 100) + soc = 100; + } else + soc = -1; + + return soc; +} + +static int mmi_combine_battery_status(struct mmi_charger_chip *chip) +{ + int status = POWER_SUPPLY_STATUS_UNKNOWN; + struct mmi_battery_pack *battery = NULL; + + list_for_each_entry(battery, &chip->battery_list, list) { + if (battery->status == POWER_SUPPLY_STATUS_CHARGING) { + status = POWER_SUPPLY_STATUS_CHARGING; + break; + } + if (battery->status == POWER_SUPPLY_STATUS_NOT_CHARGING || + status == POWER_SUPPLY_STATUS_NOT_CHARGING) { + status = POWER_SUPPLY_STATUS_NOT_CHARGING; + continue; + } + if (battery->status == POWER_SUPPLY_STATUS_DISCHARGING || + status == POWER_SUPPLY_STATUS_DISCHARGING) { + status = POWER_SUPPLY_STATUS_DISCHARGING; + continue; + } + status = battery->status; + } + + return status; +} + +static int mmi_combine_battery_cycles(struct mmi_charger_chip *chip) +{ + int cycles = 0; + int full = 0; + struct mmi_battery_pack *battery = NULL; + + list_for_each_entry(battery, &chip->battery_list, list) { + cycles += battery->cycles * battery->info->batt_full_uah; + full += battery->info->batt_full_uah; + } + + if (full > 0 && cycles >= 0) + cycles /= full; + else + cycles = -1; + + return cycles; +} + +static int mmi_combine_battery_age(struct mmi_charger_chip *chip) +{ + int age = 100; + int full = 0; + int full_design = 0; + struct mmi_battery_pack *battery = NULL; + + list_for_each_entry(battery, &chip->battery_list, list) { + full += battery->info->batt_full_uah; + full_design += battery->info->batt_design_uah; + } + chip->charge_full = full; + chip->charge_full_design = full_design; + + if (full_design > 0 && full > 0) { + age = full * 100 / full_design; + if (age > 100) + age = 100; + } else + age = -1; + + return age; +} + +static int mmi_combine_battery_current(struct mmi_charger_chip *chip) +{ + int current_ma = 0; + struct mmi_battery_pack *battery = NULL; + + list_for_each_entry(battery, &chip->battery_list, list) { + current_ma += battery->info->batt_ma; + } + + return current_ma; +} + +static int mmi_combine_charge_counter(struct mmi_charger_chip *chip) +{ + int counter = 0; + struct mmi_battery_pack *battery = NULL; + + list_for_each_entry(battery, &chip->battery_list, list) { + counter += battery->info->batt_chg_counter; + } + + return counter; +} + +static int mmi_combine_battery_voltage(struct mmi_charger_chip *chip) +{ + int voltage_mv = 0; + int count = 0; + struct mmi_battery_pack *battery = NULL; + + list_for_each_entry(battery, &chip->battery_list, list) { + voltage_mv += battery->info->batt_mv; + count++; + } + + if (count) + voltage_mv /= count; + + return voltage_mv; +} + +static void mmi_update_battery_status(struct mmi_charger_chip *chip) +{ + int soc; + int age; + int status; + int cycles; + int voltage_mv; + int current_ma; + int charge_counter; + bool mmi_changed = false; + int batt_temp; + int batt_health = POWER_SUPPLY_HEALTH_UNKNOWN; + int charger_rate = MMI_POWER_SUPPLY_CHARGE_RATE_NONE; + int max_charger_rate = MMI_POWER_SUPPLY_CHARGE_RATE_NONE; + struct mmi_charger *charger = NULL; + struct mmi_battery_pack *battery = NULL; + struct mmi_battery_info *batt_info = NULL; + bool vbus_present = false; + bool lpd_present = false; + int power_watt = 0; + + mutex_lock(&chip->battery_lock); + list_for_each_entry(charger, &chip->charger_list, list) { + battery = charger->battery; + batt_info = &charger->batt_info; + + /* Do it only for the first charger */ + if (battery->pending == battery->reference) { + /* init for going through all chargers */ + battery->status = POWER_SUPPLY_STATUS_DISCHARGING; + battery->charger_rate = MMI_POWER_SUPPLY_CHARGE_RATE_NONE; + + /* update only at the first charger */ + battery->health = mmi_get_battery_health(charger); + if (batt_info->batt_design_uah > 0) + battery->age = batt_info->batt_full_uah * 100 \ + / batt_info->batt_design_uah; + if (battery->soc_cycles_start < batt_info->batt_soc) + battery->cycles += \ + batt_info->batt_soc - battery->soc_cycles_start; + battery->soc_cycles_start = batt_info->batt_soc; + battery->info = batt_info; + } + + /* update charger rate */ + charger_rate = mmi_get_battery_charger_rate(charger); + if (battery->charger_rate < charger_rate) + battery->charger_rate = charger_rate; + + if (charger->chg_info.chrg_pmax_mw > power_watt) + power_watt = charger->chg_info.chrg_pmax_mw; + + /* update charging status */ + if (batt_info->batt_status == POWER_SUPPLY_STATUS_FULL || + battery->status == POWER_SUPPLY_STATUS_FULL || + charger->status.pres_chrg_step == STEP_FULL) + battery->status = POWER_SUPPLY_STATUS_FULL; + else if (batt_info->batt_status == POWER_SUPPLY_STATUS_CHARGING || + battery->status == POWER_SUPPLY_STATUS_CHARGING) + battery->status = POWER_SUPPLY_STATUS_CHARGING; + else if (batt_info->batt_status == POWER_SUPPLY_STATUS_NOT_CHARGING || + battery->status == POWER_SUPPLY_STATUS_NOT_CHARGING) + battery->status = POWER_SUPPLY_STATUS_NOT_CHARGING; + else + battery->status = POWER_SUPPLY_STATUS_DISCHARGING; + + battery->pending--; + /* Do it only for the last charger */ + if (battery->pending <= 0) { + if (max_charger_rate < battery->charger_rate) + max_charger_rate = battery->charger_rate; + battery->pending = 0; + mmi_dbg(chip, "[B:%s]: health:%d, status:%d," + " age:%d, cycles:%d, rate:%s\n", + battery->sn, + battery->health, + battery->status, + battery->age, + battery->cycles, + charge_rate[battery->charger_rate]); + } + /* update vbus and liquid present detection status */ + if (!vbus_present && charger->chg_info.vbus_present) + vbus_present = true; + if (!lpd_present && charger->chg_info.lpd_present) + lpd_present = true; + } + + soc = mmi_combine_battery_soc(chip); + status = mmi_combine_battery_status(chip); + cycles = mmi_combine_battery_cycles(chip); + age = mmi_combine_battery_age(chip); + voltage_mv = mmi_combine_battery_voltage(chip); + current_ma = mmi_combine_battery_current(chip); + charge_counter = mmi_combine_charge_counter(chip); + if (soc >= 0 && chip->combo_soc != soc) { + mmi_changed = true; + chip->combo_soc = soc; + } + if (chip->combo_status != status) { + mmi_changed = true; + chip->combo_status = status; + } + if (cycles >= 0 && + (chip->combo_cycles / 100) != (cycles / 100)) { + mmi_changed = true; + chip->combo_cycles = cycles; + } + if (age > 0 && chip->combo_age != age) { + mmi_changed = true; + chip->combo_age = age; + } + if (voltage_mv > 0 && chip->combo_voltage_mv != voltage_mv) { + chip->combo_voltage_mv = voltage_mv; + } + if (chip->combo_current_ma != current_ma) { + chip->combo_current_ma = current_ma; + } + if (chip->combo_charge_counter != charge_counter) { + chip->combo_charge_counter = charge_counter; + } + if (chip->max_charger_rate != max_charger_rate) { + mmi_changed = true; + chip->max_charger_rate = max_charger_rate; + mmi_notify_charger_event(chip, NOTIFY_EVENT_TYPE_CHG_RATE); + mmi_err(chip, "%s charger is detected\n", + charge_rate[chip->max_charger_rate]); + } + + if (chip->lpd_present != lpd_present) { + mmi_changed = true; + chip->lpd_present = lpd_present; + mmi_notify_charger_event(chip, NOTIFY_EVENT_TYPE_LPD_PRESENT); + mmi_info(chip, "lpd is %s\n", + lpd_present? "present" : "absent"); + } + + if (chip->vbus_present != vbus_present) { + mmi_changed = true; + chip->vbus_present = vbus_present; + mmi_notify_charger_event(chip, NOTIFY_EVENT_TYPE_VBUS_PRESENT); + mmi_info(chip, "vbus is %s\n", + vbus_present? "present" : "absent"); + } + + if ((chip->power_watt / 1000) != (power_watt / 1000)) { + mmi_changed = true; + chip->power_watt = power_watt; + mmi_notify_charger_event(chip, NOTIFY_EVENT_TYPE_POWER_WATT); + mmi_info(chip, "charger power is %d mW\n", power_watt); + } + + list_for_each_entry(battery, &chip->battery_list, list) { + if (batt_health == POWER_SUPPLY_HEALTH_UNKNOWN) { + batt_health = battery->health; + batt_temp = battery->info->batt_temp; + continue; + } + if (battery->info->batt_temp > batt_temp) { + batt_temp = battery->info->batt_temp; + batt_health = battery->health; + } + } + if (chip->combo_health != batt_health || + chip->combo_temp != batt_temp) { + mmi_changed = true; + chip->combo_health = batt_health; + chip->combo_temp = batt_temp; + } + + if (mmi_changed) { + power_supply_changed(chip->mmi_psy); + mmi_info(chip, "Combo status: soc:%d, status:%d, temp:%d," + " health:%d, age:%d, cycles:%d, voltage:%d, current:%d," + " counter:%d, rate:%s, lpd:%d, vbus:%d\n", + chip->combo_soc, + chip->combo_status, + chip->combo_temp, + chip->combo_health, + chip->combo_age, + chip->combo_cycles, + chip->combo_voltage_mv, + chip->combo_current_ma, + chip->combo_charge_counter, + charge_rate[chip->max_charger_rate], + chip->lpd_present, + chip->vbus_present); + } + + mutex_unlock(&chip->battery_lock); +} + +static void mmi_charger_heartbeat_work(struct work_struct *work) +{ + int hb_resch_time; + struct mmi_charger *charger = NULL; + struct mmi_charger_chip *chip = container_of(work, + struct mmi_charger_chip, + heartbeat_work.work); + struct timespec64 now; + static struct timespec64 start; + uint32_t elapsed_ms; + + /* Have not been resumed so wait another 100 ms */ + if (chip->suspended & IS_SUSPENDED) { + mmi_err(chip, "HB running before Resume\n"); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(100)); + return; + } + + mmi_dbg(chip, "Heartbeat!\n"); + + pm_stay_awake(chip->dev); + alarm_cancel(&chip->heartbeat_alarm); + + mutex_lock(&chip->charger_lock); + list_for_each_entry(charger, &chip->charger_list, list) { + mmi_get_charger_info(chip, charger); + mmi_update_charger_profile(chip, charger); + mmi_reset_charger_configure(chip, charger); + mmi_update_charger_status(chip, charger); + } + list_for_each_entry(charger, &chip->charger_list, list) { + mmi_configure_charger(chip, charger); + } + mmi_update_battery_status(chip); + mutex_unlock(&chip->charger_lock); + + mmi_dbg(chip, "DemoMode:%d, FactoryVersion:%d, FactoryMode:%d," + " dcp_pmax:%d, hvdcp_pmax:%d, pd_pmax:%d, wls_pmax:%d\n", + chip->demo_mode, + chip->factory_version, + chip->factory_mode, + chip->dcp_pmax, + chip->hvdcp_pmax, + chip->pd_pmax, + chip->wls_pmax); + + if (chip->factory_mode || + (chip->factory_version && chip->enable_factory_poweroff)) { + if (chip->max_charger_rate > MMI_POWER_SUPPLY_CHARGE_RATE_NONE) { + mmi_dbg(chip, "Factory Kill Armed\n"); + chip->factory_kill_armed = true; + ktime_get_real_ts64(&start); + } else if (chip->factory_kill_armed && !factory_kill_disable) { + ktime_get_real_ts64(&now); + elapsed_ms = (now.tv_sec - start.tv_sec) * 1000; + elapsed_ms += (now.tv_nsec - start.tv_nsec) / 1000000; + if (elapsed_ms < chip->factory_kill_debounce_ms) { + mmi_err(chip, "Factory kill debounce elapsed_ms:%d\n", + elapsed_ms); + } else if(!shutdown_triggered) { + mmi_err(chip, "Factory kill power off\n"); + shutdown_triggered = true; +#if (KERNEL_VERSION(5, 10, 0) > LINUX_VERSION_CODE) || defined(MMI_GKI_API_ALLOWANCE) + orderly_poweroff(true); +#else + kernel_power_off(); +#endif + } + } else { + chip->factory_kill_armed = false; + } + } + + if (chip->empty_vbat_shutdown_triggered && !shutdown_triggered) { + mmi_err(chip, "shutdown for empty battery voltage\n"); + shutdown_triggered = true; +#if (KERNEL_VERSION(5, 10, 0) > LINUX_VERSION_CODE) || defined(MMI_GKI_API_ALLOWANCE) + orderly_poweroff(true); +#else + kernel_power_off(); +#endif + } + + chip->suspended = 0; + + if (chip->factory_mode) + hb_resch_time = HEARTBEAT_FACTORY_MS; + else if (chip->max_charger_rate != MMI_POWER_SUPPLY_CHARGE_RATE_NONE + && chip->combo_status != POWER_SUPPLY_STATUS_FULL) + hb_resch_time = chip->heartbeat_interval; + else + hb_resch_time = chip->heartbeat_dischg_ms; + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(hb_resch_time)); + if (suspend_wakeups || + chip->max_charger_rate != MMI_POWER_SUPPLY_CHARGE_RATE_NONE) + alarm_start_relative(&chip->heartbeat_alarm, + ns_to_ktime(HEARTBEAT_WAKEUP_INTRVAL_NS)); + + if (chip->max_charger_rate == MMI_POWER_SUPPLY_CHARGE_RATE_NONE) + pm_relax(chip->dev); + + PM_RELAX(chip->mmi_hb_wake_source); +} + +const char *mmi_get_battery_serialnumber(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + const char *battsn_buf; + int retval; + + battsn_buf = NULL; + + if (np) + retval = of_property_read_string(np, "mmi,battid", + &battsn_buf); + else + return NULL; + + if ((retval == -EINVAL) || !battsn_buf) { + pr_err("Battsn unused\n"); + of_node_put(np); + return NULL; + + } else + pr_err("Battsn = %s\n", battsn_buf); + + of_node_put(np); + + return battsn_buf; +} +EXPORT_SYMBOL(mmi_get_battery_serialnumber); + +bool mmi_is_factory_mode(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool factory_mode = false; + const char *bootargs = NULL; + char *bootmode = NULL; + char *end = NULL; + + if ((this_chip && this_chip->factory_mode) || + !strncmp(bi_bootmode(), "mot-factory", 11)) + return true; + + if (!np) + return factory_mode; + + if (!of_property_read_string(np, "bootargs", &bootargs)) { + bootmode = strstr(bootargs, "androidboot.mode="); + if (bootmode) { + end = strpbrk(bootmode, " "); + bootmode = strpbrk(bootmode, "="); + } + if (bootmode && + end > bootmode && + strnstr(bootmode, "factory", end - bootmode)) { + factory_mode = true; + } + } + of_node_put(np); + + return factory_mode; +} +EXPORT_SYMBOL(mmi_is_factory_mode); + +bool mmi_is_factory_version(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool factory_version = false; + const char *bootargs = NULL; + char *bootloader = NULL; + char *end = NULL; + + if (this_chip && this_chip->factory_version) + return true; + + if (!np) + return factory_version; + + if (!of_property_read_string(np, "bootargs", &bootargs)) { + bootloader = strstr(bootargs, "androidboot.bootloader="); + if (bootloader) { + end = strpbrk(bootloader, " "); + bootloader = strpbrk(bootloader, "="); + } + if (bootloader && + end > bootloader && + strnstr(bootloader, "factory", end - bootloader)) { + factory_version = true; + } + } + of_node_put(np); + + return factory_version; +} +EXPORT_SYMBOL(mmi_is_factory_version); + +void mmi_get_charger_configure(struct mmi_charger_driver *driver) +{ + struct mmi_charger *charger = NULL; + struct mmi_charger_chip *chip = this_chip; + + if (!chip) { + pr_err("mmi_charger: chip is invalid\n"); + return; + } + + mutex_lock(&chip->charger_lock); + list_for_each_entry(charger, &chip->charger_list, list) { + if (charger->driver == driver) { + mmi_get_charger_info(chip, charger); + mmi_update_charger_profile(chip, charger); + mmi_reset_charger_configure(chip, charger); + mmi_update_charger_status(chip, charger); + mmi_configure_charger(chip, charger); + break; + } + } + mmi_update_battery_status(chip); + mutex_unlock(&chip->charger_lock); +} +EXPORT_SYMBOL(mmi_get_charger_configure); + +int mmi_vote_charging_disable(const char *voter, bool enable) +{ + struct mmi_charger_chip *chip = this_chip; + + if (!chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -EINVAL; + } + + return mmi_vote(&chip->disable_charging_vote, voter, enable, 0); +} +EXPORT_SYMBOL(mmi_vote_charging_disable); + +int mmi_vote_charger_suspend(const char *voter, bool enable) +{ + struct mmi_charger_chip *chip = this_chip; + + if (!chip) { + pr_err("mmi_charger: chip is invalid\n"); + return -EINVAL; + } + + return mmi_vote(&chip->suspend_charger_vote, voter, enable, 0); +} +EXPORT_SYMBOL(mmi_vote_charger_suspend); + +int mmi_register_charger_driver(struct mmi_charger_driver *driver) +{ + int ret = 0; + struct mmi_charger_chip *chip = this_chip; + struct mmi_charger *charger = NULL; + struct mmi_battery_pack *battery = NULL; + struct mmi_battery_info batt_info; + struct mmi_charger_info chg_info; + + if (!chip) { + pr_err("%s: No mmi charger chip\n", __func__); + return -ENODEV; + } + + if (!driver) { + mmi_err(chip, "Invalid mmi charger driver\n"); + return -EINVAL; + } + + if (!driver->get_batt_info || + !driver->get_chg_info || + !driver->config_charge || + !driver->is_charge_tapered || + !driver->set_constraint) { + mmi_err(chip, "[C:%s]: mmi charger function is empty\n", + driver->name); + return -EINVAL; + } + + memset(&batt_info, 0, sizeof(batt_info)); + memset(&chg_info, 0, sizeof(chg_info)); + driver->get_batt_info(driver->data, &batt_info); + driver->get_chg_info(driver->data, &chg_info); + mutex_lock(&chip->charger_lock); + list_for_each_entry(charger, &chip->charger_list, list) { + if (charger->driver == driver) { + ret = -EEXIST; + mmi_err(chip, + "[C:%s]: driver has already registered\n", + driver->name); + goto exit; + } + if (!battery && + !strcmp(charger->battery->sn, batt_info.batt_sn)) + battery = charger->battery; + } + + charger = devm_kzalloc(chip->dev, sizeof(struct mmi_charger), + GFP_KERNEL); + if (!charger) { + ret = -ENOMEM; + goto exit; + } + + if (!battery) { + battery = devm_kzalloc(chip->dev, sizeof(struct mmi_battery_pack), + GFP_KERNEL); + if (!battery) { + devm_kfree(chip->dev, charger); + ret = -ENOMEM; + goto exit; + } + battery->reference++; + battery->age = 100; + battery->cycles = 0; + battery->soc_cycles_start = 100; + memcpy(battery->sn, batt_info.batt_sn, MMI_BATT_SN_LEN); + of_property_read_u32(driver->dev->of_node, "mmi,paired-id", + &battery->paired_id); + mmi_update_paired_battery(chip, battery); + + mutex_lock(&chip->battery_lock); + list_add_tail(&battery->list, &chip->battery_list); + mutex_unlock(&chip->battery_lock); + } else { + mutex_lock(&chip->battery_lock); + battery->reference++; + mutex_unlock(&chip->battery_lock); + } + + memset(&charger->status, 0, sizeof(struct mmi_charger_status)); + charger->status.pres_temp_zone = ZONE_NONE; + charger->status.pres_chrg_step = STEP_NONE; + charger->status.demo_full_soc = 100; + charger->status.charging_limit_modes = CHARGING_LIMIT_UNKNOWN; + memcpy(&charger->batt_info, &batt_info, sizeof(batt_info)); + memcpy(&charger->chg_info, &chg_info, sizeof(chg_info)); + charger->driver = driver; + charger->battery = battery; + charger->battery->info = &charger->batt_info; + mmi_get_charger_profile(chip, charger); + list_add_tail(&charger->list, &chip->charger_list); + + if (chip->batt_psy) { + mmi_info(chip, "[C:%s] register charger succesfully, Throw out BATT_PSY change to update battery info\n", driver->name); + power_supply_changed(chip->batt_psy); + } +exit: + mutex_unlock(&chip->charger_lock); + + return ret; +} +EXPORT_SYMBOL(mmi_register_charger_driver); + +int mmi_unregister_charger_driver(struct mmi_charger_driver *driver) +{ + int ret = 0; + struct mmi_charger_chip *chip = this_chip; + struct mmi_charger *charger = NULL, *tmp = NULL; + + if (!chip) { + pr_err("%s: No mmi charger chip\n", __func__); + return -ENODEV; + } + + if (!driver) { + mmi_err(chip, "Invalid mmi charger driver\n"); + return -EINVAL; + } + + mutex_lock(&chip->charger_lock); + list_for_each_entry(tmp, &chip->charger_list, list) { + if (tmp->driver == driver) { + charger = tmp; + break; + } + } + + if (charger) { + mutex_lock(&chip->battery_lock); + charger->battery->reference--; + if (charger->battery->reference <= 0) { + list_del(&charger->battery->list); + devm_kfree(chip->dev, charger->battery); + } + mutex_unlock(&chip->battery_lock); + + list_del(&charger->list); + devm_kfree(chip->dev, charger); + } else { + mmi_err(chip, "[C:%s]: driver has not registered yet\n", + driver->name); + ret = -ENOENT; + } + + mutex_unlock(&chip->charger_lock); + return ret; +} +EXPORT_SYMBOL(mmi_unregister_charger_driver); + +static enum alarmtimer_restart mmi_heartbeat_alarm_cb(struct alarm *alarm, + ktime_t now) +{ + struct mmi_charger_chip *chip = container_of(alarm, + struct mmi_charger_chip, + heartbeat_alarm); + + mmi_info(chip, "HB alarm fired\n"); + + PM_STAY_AWAKE(chip->mmi_hb_wake_source); + cancel_delayed_work(&chip->heartbeat_work); + /* Delay by 500 ms to allow devices to resume. */ + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(500)); + + return ALARMTIMER_NORESTART; +} + +static int mmi_psy_notifier_call(struct notifier_block *nb, unsigned long val, + void *v) +{ + struct mmi_charger_chip *chip = container_of(nb, + struct mmi_charger_chip, mmi_psy_notifier); + struct power_supply *psy = v; + + if (!chip) { + pr_err("called before chip valid!\n"); + return NOTIFY_DONE; + } + + if (val != PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + if (psy && + ((strcmp(psy->desc->name, "battery") == 0) || + (strcmp(psy->desc->name, "usb") == 0) || + (strcmp(psy->desc->name, "wireless") == 0))) { + cancel_delayed_work(&chip->heartbeat_work); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(0)); + } + + return NOTIFY_OK; +} + +static int mmi_charger_reboot(struct notifier_block *nb, + unsigned long event, void *unused) +{ + struct mmi_charger_chip *chip = container_of(nb, struct mmi_charger_chip, + mmi_reboot); + + if (!chip) { + pr_err("called before chip valid!\n"); + return NOTIFY_DONE; + } + + if (!chip->factory_mode) + return NOTIFY_DONE; + + switch (event) { + case SYS_POWER_OFF: + factory_kill_disable = true; + chip->force_charger_disabled = true; + schedule_delayed_work(&chip->heartbeat_work, msecs_to_jiffies(0)); + while (chip->max_charger_rate != MMI_POWER_SUPPLY_CHARGE_RATE_NONE && + (shutdown_triggered || chip->factory_syspoweroff_wait) && !chip->empty_vbat_shutdown_triggered) { + mmi_info(chip, "Wait for charger removal\n"); + msleep(100); + } + break; + default: + break; + } + + return NOTIFY_DONE; +} + +static enum power_supply_property mmi_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_COUNTER, +}; + +static int mmi_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mmi_charger_chip *chip = power_supply_get_drvdata(psy); + int rc = 0; + uint32_t elapsed_ms; + struct timespec64 now; + static struct timespec64 start = {0}; + + if (psp == POWER_SUPPLY_PROP_CURRENT_NOW && + chip->ibat_calc_alignment_time != UINT_MAX) { + ktime_get_real_ts64(&now); + if (now.tv_sec >= start.tv_sec) { + elapsed_ms = (now.tv_sec - start.tv_sec) * 1000; + elapsed_ms += (now.tv_nsec - start.tv_nsec) / 1000000; + } else { + elapsed_ms = 0; + start = now; + } + if (elapsed_ms >= chip->ibat_calc_alignment_time) { + cancel_delayed_work(&chip->heartbeat_work); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(0)); + start = now; + } + } + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = chip->combo_status; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = chip->combo_health; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = chip->combo_temp * 10; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = chip->combo_soc; + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + val->intval = chip->init_cycles + chip->combo_cycles / 100; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + val->intval = chip->charge_full_design; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + val->intval = chip->charge_full; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = chip->combo_voltage_mv * 1000; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = chip->combo_current_ma * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + val->intval = chip->combo_charge_counter; + break; + default: + val->intval = -EINVAL; + break; + } + + return rc; +} + +static int mmi_set_prop(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + int rc = 0; + struct mmi_charger_chip *chip = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_CYCLE_COUNT: + chip->init_cycles = val->intval; + break; + default: + break; + } + return rc; +} + +static int mmi_prop_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_CYCLE_COUNT: + return 1; + default: + break; + } + + return 0; +} + +static const struct power_supply_desc mmi_psy_desc = { + .name = "mmi_battery", + .type = POWER_SUPPLY_TYPE_MAINS, + .get_property = mmi_get_prop, + .set_property = mmi_set_prop, + .property_is_writeable = mmi_prop_is_writeable, + .properties = mmi_props, + .num_properties = ARRAY_SIZE(mmi_props), +}; + +static int mmi_parse_dt(struct mmi_charger_chip *chip) +{ + int rc = 0; + const char *charger_ability = NULL; + struct device_node *node = chip->dev->of_node; + + chip->enable_charging_limit = + of_property_read_bool(node, "mmi,enable-charging-limit"); + + chip->enable_factory_poweroff = + of_property_read_bool(node, "mmi,enable-factory-poweroff"); + + chip->factory_syspoweroff_wait = + of_property_read_bool(node, "mmi,factory-syspoweroff-wait"); + + chip->start_factory_kill_disabled = + of_property_read_bool(node, "mmi,start-factory-kill-disabled"); + + rc = of_property_read_u32(node, "mmi,factory-kill-debounce-ms", + &chip->factory_kill_debounce_ms); + if (rc) + chip->factory_kill_debounce_ms = 0; + + rc = of_property_read_u32(node, "mmi,upper-limit-capacity", + &chip->upper_limit_capacity); + if (rc) + chip->upper_limit_capacity = 100; + + rc = of_property_read_u32(node, "mmi,lower-limit-capacity", + &chip->lower_limit_capacity); + if (rc) + chip->lower_limit_capacity = 0; + + rc = of_property_read_u32(node, "mmi,heartbeat-interval", + &chip->heartbeat_interval); + if (rc) + chip->heartbeat_interval = HEARTBEAT_DELAY_MS; + + rc = of_property_read_u32(node, "mmi,dcp-power-max", + &chip->dcp_pmax); + if (rc) + chip->dcp_pmax = CHARGER_POWER_7P5W; + + rc = of_property_read_u32(node, "mmi,hvdcp-power-max", + &chip->hvdcp_pmax); + if (rc) + chip->hvdcp_pmax = CHARGER_POWER_15W; + + rc = of_property_read_u32(node, "mmi,pd-power-max", + &chip->pd_pmax); + if (rc) + chip->pd_pmax = CHARGER_POWER_18W; + + rc = of_property_read_u32(node, "mmi,wls-power-max", + &chip->wls_pmax); + if (rc) + chip->wls_pmax = CHARGER_POWER_10W; + + rc = of_property_read_u32(node, "mmi,heartbeat-discharger-ms", + &chip->heartbeat_dischg_ms); + if (rc) + chip->heartbeat_dischg_ms = HEARTBEAT_DISCHARGE_MS; + + mmi_warn(chip, "mmi,heartbeat dischg ms %d\n", chip->heartbeat_dischg_ms); + + rc = of_property_read_u32(node, "mmi,ibat-calc-alignment-time", + &chip->ibat_calc_alignment_time); + if (rc) + chip->ibat_calc_alignment_time = UINT_MAX; + + node = of_find_node_by_path("/chosen"); + + if (!node) + return 0; + + rc = of_property_read_string(node, "mmi,charger", &charger_ability); + if ((rc == -EINVAL) || !charger_ability) { + mmi_warn(chip, "mmi,charger is unused\n"); + } else { + mmi_info(chip, "QC charger ability = %s\n", charger_ability); + if (strstr(charger_ability, "15W")) + chip->hvdcp_pmax = CHARGER_POWER_15W; + else if (strstr(charger_ability, "18W")) + chip->hvdcp_pmax = CHARGER_POWER_18W; + else if (strstr(charger_ability, "20W")) + chip->hvdcp_pmax = CHARGER_POWER_20W; + } + + charger_ability = NULL; + rc = of_property_read_string(node, "mmi,usb_dcp", &charger_ability); + if ((rc == -EINVAL) || !charger_ability) { + mmi_warn(chip, "mmi,usb_dcp is unused\n"); + } else { + mmi_info(chip, "DCP charger ability = %s\n", charger_ability); + if (strstr(charger_ability, "1.5A")) + chip->dcp_pmax = CHARGER_POWER_7P5W; + else if (strstr(charger_ability, "2A")) + chip->dcp_pmax = CHARGER_POWER_10W; + } + + of_node_put(node); + + return 0; +} + +static int mmi_charger_probe(struct platform_device *pdev) +{ + int rc = 0; + struct mmi_charger_chip *chip; + struct power_supply_config psy_cfg = {}; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->name = "mmi_charger"; + chip->dev = &pdev->dev; + psy_cfg.drv_data = chip; + psy_cfg.of_node = chip->dev->of_node; + chip->suspended = 0; + chip->combo_status = 0; + chip->combo_age = 100; + chip->combo_voltage_mv = 0; + chip->combo_current_ma = 0; + chip->combo_cycles = 0; + chip->combo_soc = 100; + chip->combo_charge_counter = 0; + chip->charge_full = 0; + chip->charge_full_design = 0; + chip->init_cycles = 0; + chip->factory_version = mmi_is_factory_version(); + chip->factory_mode = mmi_is_factory_mode(); + chip->disable_charging_vote.name = "disable_charging"; + chip->suspend_charger_vote.name = "suspend_charger"; + platform_set_drvdata(pdev, chip); + device_init_wakeup(chip->dev, true); + + chip->debug_enabled = &debug_enabled; + chip->ipc_log = ipc_log_context_create(MMI_LOG_PAGES, MMI_LOG_DIR, 0); + if (!chip->ipc_log) + mmi_err(chip, "Failed to create mmi charger IPC log\n"); + else + mmi_info(chip, "IPC logging is enabled for mmi charger\n"); + + rc = mmi_parse_dt(chip); + if (rc) { + mmi_err(chip, "Failed to parse device tree\n"); + rc = -EINVAL; + goto exit; + } + + this_chip = chip; + mutex_init(&chip->charger_lock); + mutex_init(&chip->battery_lock); + INIT_LIST_HEAD(&chip->charger_list); + INIT_LIST_HEAD(&chip->battery_list); + INIT_DELAYED_WORK(&chip->heartbeat_work, mmi_charger_heartbeat_work); + PM_WAKEUP_REGISTER(chip->dev, chip->mmi_hb_wake_source, "mmi_hb_wake"); + alarm_init(&chip->heartbeat_alarm, ALARM_BOOTTIME, + mmi_heartbeat_alarm_cb); + + chip->mmi_psy = devm_power_supply_register(chip->dev, + &mmi_psy_desc, + &psy_cfg); + if (IS_ERR(chip->mmi_psy)) { + mmi_err(chip, "failed: mmi power supply register\n"); + rc = PTR_ERR(chip->mmi_psy); + goto exit; + } + + mmi_battery_supply_init(chip); + + rc = device_create_file(chip->dev, + &dev_attr_state_sync); + if (rc) { + mmi_err(chip, "couldn't create state_sync\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_dcp_pmax); + if (rc) { + mmi_err(chip, "couldn't create dcp_pmax\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_hvdcp_pmax); + if (rc) { + mmi_err(chip, "couldn't create hvdcp_pmax\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_pd_pmax); + if (rc) { + mmi_err(chip, "couldn't create pd_pmax\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_wls_pmax); + if (rc) { + mmi_err(chip, "couldn't create wls_pmax\n"); + } + + /* Register the notifier for the psy updates*/ + chip->mmi_psy_notifier.notifier_call = mmi_psy_notifier_call; + rc = power_supply_reg_notifier(&chip->mmi_psy_notifier); + if (rc) + mmi_err(chip, "Failed to reg notifier: %d\n", rc); + + if (chip->factory_mode) { + mmi_info(chip, "Entering Factory Mode!\n"); + chip->mmi_reboot.notifier_call = mmi_charger_reboot; + chip->mmi_reboot.next = NULL; + chip->mmi_reboot.priority = 1; + rc = register_reboot_notifier(&chip->mmi_reboot); + if (rc) + mmi_err(chip, "Register for reboot failed\n"); + } + + if (chip->start_factory_kill_disabled) + factory_kill_disable = 1; + + cancel_delayed_work(&chip->heartbeat_work); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(0)); + + mmi_info(chip, "MMI charger probed successfully!\n"); + return 0; +exit: + ipc_log_context_destroy(chip->ipc_log); + + return rc; +} + +static int mmi_charger_remove(struct platform_device *pdev) +{ + struct mmi_charger_chip *chip = platform_get_drvdata(pdev); + + if (!list_empty(&chip->charger_list)) { + return -EBUSY; + } + + cancel_delayed_work(&chip->heartbeat_work); + + if (chip->factory_mode) + unregister_reboot_notifier(&chip->mmi_reboot); + power_supply_unreg_notifier(&chip->mmi_psy_notifier); + device_remove_file(chip->dev, &dev_attr_state_sync); + device_remove_file(chip->dev, &dev_attr_dcp_pmax); + device_remove_file(chip->dev, &dev_attr_hvdcp_pmax); + device_remove_file(chip->dev, &dev_attr_pd_pmax); + device_remove_file(chip->dev, &dev_attr_wls_pmax); + if (chip->batt_psy) { + if (chip->batt_uenvp[0]) { + kfree(chip->batt_uenvp[0]); + chip->batt_uenvp[0] = NULL; + } + device_remove_file(chip->batt_psy->dev.parent, + &dev_attr_force_demo_mode); + device_remove_file(chip->batt_psy->dev.parent, + &dev_attr_force_max_chrg_temp); + device_remove_file(chip->batt_psy->dev.parent, + &dev_attr_factory_image_mode); + device_remove_file(chip->batt_psy->dev.parent, + &dev_attr_factory_charge_upper); + device_remove_file(chip->batt_psy->dev.parent, + &dev_attr_force_charger_suspend); + device_remove_file(chip->batt_psy->dev.parent, + &dev_attr_force_charging_enable); + device_remove_file(chip->batt_psy->dev.parent, + &dev_attr_force_charging_disable); + sysfs_remove_group(&chip->batt_psy->dev.kobj, + &power_supply_mmi_attr_group); + power_supply_put(chip->batt_psy); + } + PM_WAKEUP_UNREGISTER(chip->mmi_hb_wake_source); + ipc_log_context_destroy(chip->ipc_log); + + return 0; +} + +static void mmi_charger_shutdown(struct platform_device *pdev) +{ + struct mmi_charger_chip *chip = platform_get_drvdata(pdev); + + mmi_info(chip, "MMI charger shutdown\n"); + + return; +} + +#ifdef CONFIG_PM_SLEEP +static int mmi_charger_suspend(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct mmi_charger_chip *chip = platform_get_drvdata(pdev); + + chip->suspended &= ~WAS_SUSPENDED; + chip->suspended |= IS_SUSPENDED; + + return 0; +} + +static int mmi_charger_resume(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct mmi_charger_chip *chip = platform_get_drvdata(pdev); + + chip->suspended &= ~IS_SUSPENDED; + chip->suspended |= WAS_SUSPENDED; + + return 0; +} +#else +#define mmi_charger_suspend NULL +#define mmi_charger_resume NULL +#endif + +static const struct dev_pm_ops mmi_charger_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mmi_charger_suspend, mmi_charger_resume) +}; + +static const struct of_device_id match_table[] = { + { .compatible = "mmi,mmi-charger", }, + { }, +}; + +static struct platform_driver mmi_charger_driver = { + .driver = { + .name = "mmi,mmi-charger", + .owner = THIS_MODULE, + .pm = &mmi_charger_dev_pm_ops, + .of_match_table = match_table, + }, + .probe = mmi_charger_probe, + .remove = mmi_charger_remove, + .shutdown = mmi_charger_shutdown, +}; +module_platform_driver(mmi_charger_driver); + +MODULE_DESCRIPTION("MMI Charger Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/mmi_charger/mmi_charger.h b/drivers/power/mmi_charger/mmi_charger.h new file mode 100644 index 000000000000..7a58d6f34a5a --- /dev/null +++ b/drivers/power/mmi_charger/mmi_charger.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2020 Motorola Mobility LLC + * + * 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. + */ + +#ifndef __MMI_CHARGER_H__ +#define __MMI_CHARGER_H__ + +#include +#include + +#define mmi_err(chg, fmt, ...) \ + do { \ + pr_err("%s: %s: " fmt, chg->name, \ + __func__, ##__VA_ARGS__); \ + ipc_log_string(chg->ipc_log, \ + "E %s: %s: " fmt, chg->name, __func__, ##__VA_ARGS__); \ + } while (0) + +#define mmi_warn(chg, fmt, ...) \ + do { \ + pr_warn("%s: %s: " fmt, chg->name, \ + __func__, ##__VA_ARGS__); \ + ipc_log_string(chg->ipc_log, \ + "W %s: %s: " fmt, chg->name, __func__, ##__VA_ARGS__); \ + } while (0) + +#define mmi_info(chg, fmt, ...) \ + do { \ + pr_info("%s: %s: " fmt, chg->name, \ + __func__, ##__VA_ARGS__); \ + ipc_log_string(chg->ipc_log, \ + "I %s: %s: " fmt, chg->name, __func__, ##__VA_ARGS__); \ + } while (0) + +#define mmi_dbg(chg, fmt, ...) \ + do { \ + if (*chg->debug_enabled) \ + pr_info("%s: %s: " fmt, chg->name, \ + __func__, ##__VA_ARGS__); \ + else \ + pr_debug("%s: %s: " fmt, chg->name, \ + __func__, ##__VA_ARGS__); \ + ipc_log_string(chg->ipc_log, \ + "D %s: %s: " fmt, chg->name, __func__, ##__VA_ARGS__); \ + } while (0) + +#define MMI_LOG_PAGES (50) +#define MMI_LOG_DIR "mmi_charger" +#define MMI_BATT_SN_LEN 16 +#define CHG_SHOW_MAX_SIZE 50 + +struct mmi_battery_info { + int batt_mv; + int batt_ma; + int batt_soc; + int batt_temp; + int batt_status; + int batt_full_uah; + int batt_design_uah; + int batt_chg_counter; + int batt_fv_mv; + int batt_fcc_ma; + char batt_sn[MMI_BATT_SN_LEN]; +}; + +struct mmi_charger_info { + int chrg_mv; + int chrg_ma; + int chrg_type; + int chrg_pmax_mw; + int chrg_present; + bool chrg_otg_enabled; + int lpd_present; + int vbus_present; +}; + +struct mmi_charger_cfg { + int target_fcc; + int target_fv; + int fg_iterm; + int chrg_iterm; + bool full_charged; + bool charging_reset; + bool taper_kickoff; + bool charging_disable; + bool charger_suspend; +}; + +struct mmi_charger_constraint { + int demo_mode; + bool factory_mode; + bool factory_version; + int dcp_pmax; + int hvdcp_pmax; + int pd_pmax; + int wls_pmax; +}; + +struct mmi_charger_driver { + const char *name; + struct device *dev; + int (*get_batt_info)(void *data, struct mmi_battery_info *batt_info); + int (*get_chg_info)(void *data, struct mmi_charger_info *chg_info); + int (*config_charge)(void *data, struct mmi_charger_cfg *config); + bool (*is_charge_tapered)(void *data, int tapered_ma); + bool (*is_charge_halt)(void *data); + bool (*is_ffc_enabled)(void *data, int chrg_step); + void (*set_constraint)(void *data, struct mmi_charger_constraint *constraint); + void (*notify_paired_battery)(void *data, struct mmi_battery_info *batt_info); + void *data; +}; + +bool mmi_is_factory_mode(void); +bool mmi_is_factory_version(void); +const char *mmi_get_battery_serialnumber(void); +void mmi_get_charger_configure(struct mmi_charger_driver *driver); +int mmi_register_charger_driver(struct mmi_charger_driver *driver); +int mmi_vote_charging_disable(const char *voter, bool enable); +int mmi_vote_charger_suspend(const char *voter, bool enable); +int mmi_unregister_charger_driver(struct mmi_charger_driver *driver); + +#endif diff --git a/drivers/power/mmi_chg_tcmd/Android.mk b/drivers/power/mmi_chg_tcmd/Android.mk new file mode 100755 index 000000000000..e341d9156100 --- /dev/null +++ b/drivers/power/mmi_chg_tcmd/Android.mk @@ -0,0 +1,10 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_chg_tcmd.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/drivers/power/mmi_chg_tcmd/Kbuild b/drivers/power/mmi_chg_tcmd/Kbuild new file mode 100755 index 000000000000..7138dafdf95d --- /dev/null +++ b/drivers/power/mmi_chg_tcmd/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += mmi_chg_tcmd.o diff --git a/drivers/power/mmi_chg_tcmd/Makefile b/drivers/power/mmi_chg_tcmd/Makefile new file mode 100755 index 000000000000..34b09dd815a7 --- /dev/null +++ b/drivers/power/mmi_chg_tcmd/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean \ No newline at end of file diff --git a/drivers/power/mmi_chg_tcmd/mmi_chg_tcmd.c b/drivers/power/mmi_chg_tcmd/mmi_chg_tcmd.c new file mode 100644 index 000000000000..ea619aa3aca1 --- /dev/null +++ b/drivers/power/mmi_chg_tcmd/mmi_chg_tcmd.c @@ -0,0 +1,1277 @@ +/* + * Copyright (C) 2021 Motorola Mobility, Inc. + * + * Adding this driver is for mmi chg tcmd's kernel interface. + * So after then, tcmd app do not need to change file access folder's name. + * It will more convinient for all projects with different charger IC. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef USE_LIST_HEAD +#include +#endif + +#define DRIVERNAME "mmi_chg_tcmd" + +/*-20 ~ 80 num is 2*10 + 1*/ +#define NTC_TABLE_VALID_NUM 21 + + +struct mmi_chg_tcmd_data { + struct platform_device *pdev; + struct power_supply *bat_psy; + struct CHARGER_TEMPERATURE* ntc_table; + + bool factory_mode; + const char* bat_psy_name; + unsigned char reg_addr; + unsigned char reg_val; + + int force_usb_suspend_flag; + int pre_usb_current; + int usb_current; + + int force_chg_enable_flag; + int pre_chg_current; + int chg_current; + int chg_type; + + int usb_voltage; + + int bat_temp; + int bat_voltage; + int bat_ocv; + int bat_id; + + int batid_v_ref; + int batid_r_pull; + int ntc_v_ref; + int ntc_r_pull; + int adc_channel_list[MOTO_ADC_TCMD_CHANNEL_MAX]; + int cur_adc_channel; +}; + +/*Struct pointer's work efficiency may be bester than list head*/ +/*So if we just have battery and charger clients, we use struct pointer firstly*/ +/*If there are much more clients later, we would use list head*/ +#ifdef USE_LIST_HEAD +static LIST_HEAD(client_list); +#else +static struct mmi_chg_tcmd_client* chg_client; +static struct mmi_chg_tcmd_client* bat_client; +static struct mmi_chg_tcmd_client* pm_adc_client; +static struct mmi_chg_tcmd_client* ap_adc_client; +#endif + +/* NCP15WF104F03RC(100K) */ +static struct CHARGER_TEMPERATURE ntc_table1[] = { + {-40, 4397119}, + {-35, 3088599}, + {-30, 2197225}, + {-25, 1581881}, + {-20, 1151037}, + {-15, 846579}, + {-10, 628988}, + {-5, 471632}, + {0, 357012}, + {5, 272500}, + {10, 209710}, + {15, 162651}, + {20, 127080}, + {25, 100000}, /* 100K */ + {30, 79222}, + {35, 63167}, +#if defined(APPLY_PRECISE_NTC_TABLE) + {40, 50677}, + {41, 48528}, + {42, 46482}, + {43, 44533}, + {44, 42675}, + {45, 40904}, + {46, 39213}, + {47, 37601}, + {48, 36063}, + {49, 34595}, + {50, 33195}, + {51, 31859}, + {52, 30584}, + {53, 29366}, + {54, 28203}, + {55, 27091}, + {56, 26028}, + {57, 25013}, + {58, 24042}, + {59, 23113}, + {60, 22224}, +#else + {40, 50677}, + {45, 40904}, + {50, 33195}, + {55, 27091}, + {60, 22224}, +#endif + {65, 18323}, + {70, 15184}, + {75, 12635}, + {80, 10566}, + {85, 8873}, + {90, 7481}, + {95, 6337}, + {100, 5384}, + {105, 4594}, + {110, 3934}, + {115, 3380}, + {120, 2916}, + {125, 2522} +}; + +static int mmi_chg_tcmd_res_to_temp(struct mmi_chg_tcmd_data *data, + int vol, int vol_ntc, int r_pull); + +static int mmi_chg_get_adc_value(struct mmi_chg_tcmd_data *data, + struct mmi_chg_tcmd_client* client, int channel, int* val) +{ + int ret; + + if ((channel < 0) || (client==NULL)) { + ret = channel; + pr_err("%s adc client or channel %d is not defined\n", __func__, ret); + goto end; + } + + pr_info("%s read adc channel %d\n", __func__, channel); + ret = client->get_adc_value(client->data, + channel, + val); + if (ret) { + *val = ret; + pr_err("%s read adc %d error %d\n", + __func__, + channel, + ret); + goto end; + } + +end: + return ret; +} + +static ssize_t mmi_chg_tcmd_addr_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + + return snprintf(buf, PAGE_SIZE, "%x\n", data->reg_addr); +} + +static ssize_t mmi_chg_tcmd_addr_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + int ret; + unsigned char val; + + ret = kstrtou8(buf, 16, &val); + if (ret) { + pr_info("%s, %s not a valide buf(%d)\n", __func__, buf, ret); + goto end; + } + + data->reg_addr = val; + +end: + return count; +} +static DEVICE_ATTR(address, S_IWUSR | S_IRUGO, mmi_chg_tcmd_addr_show, mmi_chg_tcmd_addr_store); + +static ssize_t mmi_chg_tcmd_data_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + unsigned char val; + int ret; + + if (!chg_client) { + pr_err("%s chg_client is null\n", __func__); + goto end; + } + + ret = chg_client->reg_read(chg_client->data, + data->reg_addr, + &val); + if (ret) { + pr_err("%s read reg 0x%x fail %d\n", __func__, + data->reg_addr, ret); + data->reg_val = ret; + goto end; + } + + data->reg_val = val; + +end: + return snprintf(buf, PAGE_SIZE, "%x\n", data->reg_val); +} + +static ssize_t mmi_chg_tcmd_data_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + int ret; + unsigned char val; + + if (!chg_client) { + pr_err("%s chg_client is null\n", __func__); + goto end; + } + + ret = kstrtou8(buf, 16, &val); + if (ret) { + pr_info("%s, %s not a valide buf(%d)\n", __func__, buf, ret); + goto end; + } + + ret = chg_client->reg_write(chg_client->data, + data->reg_addr, + val); + if (ret) { + pr_err("%s write reg 0x%x = 0x%x fail %d\n", __func__, + data->reg_addr, val, ret); + goto end; + } + +end: + return count; +} +static DEVICE_ATTR(data, S_IWUSR | S_IRUGO, mmi_chg_tcmd_data_show, mmi_chg_tcmd_data_store); + +static ssize_t force_usb_suspend_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + + return snprintf(buf, PAGE_SIZE, "%d\n", data->force_usb_suspend_flag); +} + +static ssize_t force_usb_suspend_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + int ret; + int val; + int cur; + + if (!chg_client) { + pr_err("%s chg_client is null\n", __func__); + goto end; + } + + ret = kstrtouint(buf, 10, &val); + if (ret) { + pr_info("%s, %s not a valide buf(%d)\n", __func__, buf, ret); + goto end; + } + + val = !val; + /*If charger driver has usb enable interface*/ + if (chg_client->set_usb_enable) { + ret = chg_client->set_usb_enable(chg_client->data, val); + if (ret) { + pr_err("%s set usb en fail %d\n", __func__, ret); + goto end; + } + + data->force_usb_suspend_flag = val; + goto end; + } + + /*Or if charger driver do not have usb enable interface*/ + if (val) { + ret = chg_client->get_usb_current(chg_client->data, + &cur); + if (ret) { + pr_err("%s get usb cur fail %d\n", __func__, ret); + goto end; + } + data->pre_usb_current = val; + } + + ret = chg_client->set_usb_current(chg_client->data, + val ? 0 : data->pre_usb_current); + if (ret) { + pr_err("%s set usb cur fail %d\n", __func__, ret); + goto end; + } + + data->force_usb_suspend_flag = val; + data->usb_current = val ? 0 : data->pre_usb_current; + +end: + return count; +} +static DEVICE_ATTR(force_chg_usb_suspend, S_IWUSR | S_IRUGO, + force_usb_suspend_show,force_usb_suspend_store); + +static ssize_t force_chg_auto_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + + return snprintf(buf, PAGE_SIZE, "%d\n", data->force_chg_enable_flag); +} + +static ssize_t force_chg_auto_enable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + int ret; + int val; + + if (!chg_client) { + pr_err("%s chg_client is null\n", __func__); + goto end; + } + + ret = kstrtouint(buf, 10, &val); + if (ret) { + pr_info("%s, %s not a valide buf(%d)\n", __func__, buf, ret); + goto end; + } + + val = !!val; + /*If charger driver has chg enable interface*/ + if (chg_client->set_chg_enable) { + ret = chg_client->set_chg_enable(chg_client->data, val); + if (ret) { + pr_err("%s set chg en fail %d\n", __func__, ret); + goto end; + } + + data->force_chg_enable_flag = val; + goto end; + } + + /*Or if charger driver do not have chg enable interface*/ + if (!val) { + ret = chg_client->get_chg_current(chg_client->data, + &val); + if (ret) { + pr_err("%s get chg cur fail %d\n", __func__, ret); + goto end; + } + data->pre_chg_current = val; + } + + ret = chg_client->set_chg_current(chg_client->data, + val ? data->pre_chg_current : 0); + if (ret) { + pr_err("%s set chg cur fail %d\n", __func__, ret); + goto end; + } + + data->force_chg_enable_flag = val; + data->chg_current = val ? data->pre_chg_current : 0; + +end: + return count; +} +static DEVICE_ATTR(force_chg_auto_enable, S_IWUSR | S_IRUGO, + force_chg_auto_enable_show, force_chg_auto_enable_store); + +static ssize_t force_chg_iusb_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + + return snprintf(buf, PAGE_SIZE, "%d\n", data->usb_current); +} + +static ssize_t force_chg_iusb_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + int ret; + int val; + + if (!chg_client) { + pr_err("%s chg_client is null\n", __func__); + goto end; + } + + ret = kstrtoint(buf, 10, &val); + if (ret) { + pr_info("%s, %s not a valide buf(%d)\n", __func__, buf, ret); + goto end; + } + + ret = chg_client->set_usb_current(chg_client->data, + val); + if (ret) { + pr_err("%s set usb cur %d fail %d\n", __func__, val, ret); + goto end; + } + + data->usb_current = val; + +end: + return count; +} +static DEVICE_ATTR(force_chg_iusb, S_IWUSR | S_IRUGO, + force_chg_iusb_show, force_chg_iusb_store); + +static ssize_t force_chg_ibatt_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + int ret; + int val; + + val = data->chg_current; + if (val <= 0) { + ret = chg_client->get_chg_current(chg_client->data, + &val); + if (ret) { + pr_err("%s get chg cur fail %d\n", __func__, ret); + goto end; + } + data->chg_current = val; + } + +end: + return snprintf(buf, PAGE_SIZE, "%d\n", data->chg_current); +} + +static ssize_t force_chg_ibatt_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + int ret; + int val; + + if (!chg_client) { + pr_err("%s chg_client is null\n", __func__); + goto end; + } + + ret = kstrtoint(buf, 10, &val); + if (ret) { + pr_info("%s, %s not a valide buf(%d)\n", __func__, buf, ret); + goto end; + } + + ret = chg_client->set_chg_current(chg_client->data, + val); + if (ret) { + pr_err("%s set usb cur %d fail %d\n", __func__, val, ret); + goto end; + } + + data->chg_current = val; + +end: + return count; +} +static DEVICE_ATTR(force_chg_ibatt, S_IWUSR | S_IRUGO, + force_chg_ibatt_show,force_chg_ibatt_store); + + +static ssize_t force_chg_fail_clear_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", "not valid"); +} + +static ssize_t force_chg_fail_clear_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + int val; + + if (!chg_client) { + pr_err("%s chg_client is null\n", __func__); + goto end; + } + + ret = kstrtoint(buf, 10, &val); + if (ret) { + pr_info("%s, %s not a valide buf(%d)\n", __func__, buf, ret); + goto end; + } + + pr_info("%s just a dummy func\n", __func__); +end: + return count; +} +static DEVICE_ATTR(force_chg_fail_clear, S_IWUSR | S_IRUGO, + force_chg_fail_clear_show,force_chg_fail_clear_store); + +static ssize_t usb_voltage_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + int val; + int ret; + + /*if (!chg_client) { + pr_err("%s chg_client is null\n", __func__); + goto end; + }*/ + + ret = chg_client->get_usb_voltage(chg_client->data, + &val); + if (ret) { + pr_err("%s get usb vol fail %d\n", __func__, ret); + val = -EINVAL; + goto end; + } + + data->usb_voltage = val; + +end: + return snprintf(buf, PAGE_SIZE, "%d\n", val); +} + +static ssize_t usb_voltage_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + pr_info("%s un-supported\n", __func__); + return count; +} +static DEVICE_ATTR(usb_voltage, S_IWUSR | S_IRUGO, + usb_voltage_show, usb_voltage_store); + +static ssize_t bat_temp_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + int val; + int ret; + + /*if (!bat_client) { + pr_err("%s bat_client is null\n", __func__); + goto end; + }*/ + + ret = bat_client->get_bat_temp(bat_client->data, + &val); + if (ret) { + pr_err("%s get bat temp fail %d\n", __func__, ret); + val = -EINVAL; + goto end; + } + +end: + data->bat_temp = val; + + return snprintf(buf, PAGE_SIZE, "%d\n", data->bat_temp); +} + +static ssize_t bat_temp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + pr_info("%s un-supported\n", __func__); + return count; +} +static DEVICE_ATTR(bat_temp, S_IWUSR | S_IRUGO, + bat_temp_show, bat_temp_store); + +static ssize_t bat_voltage_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + int val; + int ret; + + /*if (!bat_client) { + pr_err("%s bat_client is null\n", __func__); + goto end; + }*/ + + ret = bat_client->get_bat_voltage(bat_client->data, + &val); + if (ret) { + pr_err("%s get bat vol fail %d\n", __func__, ret); + val = -EINVAL; + goto end; + } + +end: + data->bat_voltage = val; + + return snprintf(buf, PAGE_SIZE, "%d\n", data->bat_voltage); +} + +static ssize_t bat_voltage_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + pr_info("%s un-supported\n", __func__); + return count; +} +static DEVICE_ATTR(bat_voltage, S_IWUSR | S_IRUGO, + bat_voltage_show, bat_voltage_store); + +static ssize_t bat_ocv_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + int val; + int ret; + + /*if (!bat_client) { + pr_err("%s bat client is null\n", __func__); + goto end; + }*/ + + ret = bat_client->get_bat_ocv(bat_client->data, + &val); + if (ret) { + pr_err("%s get bat vol fail %d\n", __func__, ret); + val = ret; + goto end; + } + +end: + data->bat_ocv = val; + + return snprintf(buf, PAGE_SIZE, "%d\n", data->bat_ocv); +} + +static ssize_t bat_ocv_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + pr_info("%s un-supported\n", __func__); + return count; +} +static DEVICE_ATTR(bat_ocv, S_IWUSR | S_IRUGO, + bat_ocv_show, bat_ocv_store); + +static ssize_t chg_type_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + int type; + int ret; + + /*if (!chg_client) { + pr_err("%s bat client is null\n", __func__); + goto end; + }*/ + + ret = chg_client->get_charger_type(chg_client->data, + &type); + if (ret) { + pr_err("%s get bat vol fail %d\n", __func__, ret); + type = ret; + goto end; + } + +end: + data->chg_type = type; + + return snprintf(buf, PAGE_SIZE, "%02d\n", data->chg_type); +} + +static ssize_t chg_type_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + pr_info("%s un-supported\n", __func__); + return count; +} +static DEVICE_ATTR(chg_type, S_IWUSR | S_IRUGO, + chg_type_show, chg_type_store); + +static ssize_t bat_id_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + int val; + int ret; + + /*if (!bat_client) { + pr_err("%s bat client is null\n", __func__); + goto end; + }*/ + + ret = bat_client->get_bat_id(bat_client->data, + &val); + if (ret) { + pr_err("%s get bat vol fail %d\n", __func__, ret); + val = ret; + goto end; + } + ret = (val * 1000 / (data->batid_v_ref- val) ) * data->batid_r_pull/ 1000; + +end: + data->bat_id = ret; + + return snprintf(buf, PAGE_SIZE, "%d\n", data->bat_id); +} + +static ssize_t bat_id_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + pr_info("%s un-supported\n", __func__); + return count; +} +static DEVICE_ATTR(bat_id, S_IWUSR | S_IRUGO, + bat_id_show, bat_id_store); + +static ssize_t factory_kill_disable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int ret; + + /*if (!chg_client) { + pr_err("%s bat_client is null\n", __func__); + ret = -ENODEV; + goto end; + }*/ + + ret = chg_client->factory_kill_disable; + +/*end:*/ + + return snprintf(buf, PAGE_SIZE, "%d\n", ret); +} + +static ssize_t factory_kill_disable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int val; + int ret; + + if (!chg_client) { + pr_err("%s chg_client is null\n", __func__); + goto end; + } + + ret = kstrtoint(buf, 10, &val); + if (ret) { + pr_info("%s, %s not a valide buf(%d)\n", __func__, buf, ret); + goto end; + } + + chg_client->factory_kill_disable = !!val; + pr_info("%s, factory_kill_disable(%d)\n", __func__, chg_client->factory_kill_disable); +end: + return count; +} +static DEVICE_ATTR(factory_kill_disable, S_IWUSR | S_IRUGO, + factory_kill_disable_show, factory_kill_disable_store); + +static ssize_t adc_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + struct mmi_chg_tcmd_client *client; + int channel; + int ret; + + channel = data->cur_adc_channel; + if (channel < 100) { + client = ap_adc_client; + } else { + client = pm_adc_client; + channel -= 100; + } + + if (!client) { + pr_err("%s bat_client is null\n", __func__); + ret = -ENODEV; + goto end; + } + + mmi_chg_get_adc_value(data, client, data->cur_adc_channel, &ret); + +end: + return snprintf(buf, PAGE_SIZE, "%d\n", ret); +} + +static ssize_t adc_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + int val; + int ret; + + ret = kstrtoint(buf, 10, &val); + if (ret) { + pr_info("%s, %s not a valide buf(%d)\n", __func__, buf, ret); + goto end; + } + + data->cur_adc_channel = data->adc_channel_list[val]; + +end: + return count; +} + +static DEVICE_ATTR(adc, S_IWUSR | S_IRUGO, adc_show, adc_store); + +static ssize_t cpu_temp_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + struct mmi_chg_tcmd_client *client; + int channel; + int ret; + + channel = data->adc_channel_list[MOTO_ADC_TCMD_CHANNEL_CPU]; + if (channel < 100) { + client = ap_adc_client; + } else { + client = pm_adc_client; + channel -= 100; + } + + if (!client) { + pr_err("%s adc_client(%d) is null\n", __func__, channel); + ret = -ENODEV; + goto end; + } + + mmi_chg_get_adc_value(data, client, channel, &ret); + ret = mmi_chg_tcmd_res_to_temp(data, data->ntc_v_ref, ret, data->ntc_r_pull); + +end: + return snprintf(buf, PAGE_SIZE, "%d\n", ret); +} + +static ssize_t cpu_temp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + pr_info("%s do not support store\n", __func__); + return count; +} + +static DEVICE_ATTR(cpu_temp, S_IWUSR | S_IRUGO, + cpu_temp_show, cpu_temp_store); + +static ssize_t pa_temp_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + struct mmi_chg_tcmd_client *client; + int channel; + int ret; + + channel = data->adc_channel_list[MOTO_ADC_TCMD_CHANNEL_PA]; + if (channel < 100) { + client = ap_adc_client; + } else { + client = pm_adc_client; + channel -= 100; + } + + if (!client) { + pr_err("%s pa_client(%d) is null\n", __func__, channel); + ret = -ENODEV; + goto end; + } + + mmi_chg_get_adc_value(data, client, channel, &ret); + ret = mmi_chg_tcmd_res_to_temp(data, data->ntc_v_ref, ret, data->ntc_r_pull); + +end: + return snprintf(buf, PAGE_SIZE, "%d\n", ret); +} + +static ssize_t pa_temp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + pr_info("%s do not support store\n", __func__); + return count; +} + +static DEVICE_ATTR(pa_temp, S_IWUSR | S_IRUGO, + pa_temp_show, pa_temp_store); + +static ssize_t charger_temp_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + struct mmi_chg_tcmd_client *client; + int channel; + int ret; + + channel = data->adc_channel_list[MOTO_ADC_TCMD_CHANNEL_CHG]; + if (channel < 100) { + client = ap_adc_client; + } else { + client = pm_adc_client; + channel -= 100; + } + + if (!client) { + pr_err("%s adc client(%d) is null\n", __func__, channel); + ret = -ENODEV; + goto end; + } + + mmi_chg_get_adc_value(data, client, channel, &ret); + ret = mmi_chg_tcmd_res_to_temp(data, data->ntc_v_ref, ret, data->ntc_r_pull); + +end: + return snprintf(buf, PAGE_SIZE, "%d\n", ret); +} + +static ssize_t charger_temp_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + pr_info("%s do not support store\n", __func__); + return count; +} + +static DEVICE_ATTR(charger_temp, S_IWUSR | S_IRUGO, + charger_temp_show, charger_temp_store); + +static ssize_t adc_vbat_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mmi_chg_tcmd_data *data = platform_get_drvdata(pdev); + struct mmi_chg_tcmd_client *client; + int channel; + int ret; + + channel = data->adc_channel_list[MOTO_ADC_TCMD_CHANNEL_VBAT]; + if (channel < 100) { + client = ap_adc_client; + } else { + client = pm_adc_client; + channel -= 100; + } + + if (!client) { + pr_err("%s adc client(%s) is null\n", __func__, + channel < 100 ? "ap" : "pm"); + ret = -ENODEV; + goto end; + } + + mmi_chg_get_adc_value(data, client, channel, &ret); + /*if ret is valid and its value is < 2500000 uv(the lowest battery voltage threshold)*/ + if ((ret > 0) && (ret < 2500000)) + ret *= 1000; +end: + return snprintf(buf, PAGE_SIZE, "%d\n", ret); +} + +static ssize_t adc_vbat_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + pr_info("%s do not support store\n", __func__); + return count; +} + +static DEVICE_ATTR(adc_vbat, S_IWUSR | S_IRUGO, + adc_vbat_show, adc_vbat_store); + +static struct attribute *mmi_chg_tcmd_attrs[] = { + &dev_attr_address.attr, + &dev_attr_data.attr, + &dev_attr_force_chg_usb_suspend.attr, + &dev_attr_force_chg_auto_enable.attr, + &dev_attr_force_chg_iusb.attr, + &dev_attr_force_chg_ibatt.attr, + &dev_attr_force_chg_fail_clear.attr, + &dev_attr_usb_voltage.attr, + &dev_attr_bat_temp.attr, + &dev_attr_bat_voltage.attr, + &dev_attr_bat_ocv.attr, + &dev_attr_bat_id.attr, + &dev_attr_factory_kill_disable.attr, + &dev_attr_chg_type.attr, + &dev_attr_adc.attr, + &dev_attr_cpu_temp.attr, + &dev_attr_charger_temp.attr, + &dev_attr_pa_temp.attr, + &dev_attr_adc_vbat.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(mmi_chg_tcmd); + +int mmi_chg_tcmd_get_bat_psy_prop(struct mmi_chg_tcmd_data *data, + int prop, + int* value) +{ + union power_supply_propval val = {0, }; + int ret; + + if (!data->bat_psy) { + data->bat_psy = power_supply_get_by_name(data->bat_psy_name); + if (!data->bat_psy) { + pr_err("%s get bat psy error\n", __func__); + ret = -ENODEV; + goto end; + } + } + + ret = power_supply_get_property(data->bat_psy, prop, &val); + if (ret) { + pr_err("battery psy reading Prop %d rc = %d\n", prop, ret); + ret = -EINVAL; + goto end; + } + + *value = val.intval; + +end: + return ret; +} + +int mmi_chg_tcmd_register(struct mmi_chg_tcmd_client *client) +{ +#ifdef USE_LIST_HEAD + struct mmi_chg_tcmd_client *existing; +#endif + + if (!client) { + pr_err("%s invalide chg tcmd client(null)\n", __func__); + return -EINVAL; + } + +#ifdef USE_LIST_HEAD + list_add_tail(&client->list, &head_list); +#else + switch (client->client_id) { + case MOTO_CHG_TCMD_CLIENT_CHG: + chg_client = client; + break; + case MOTO_CHG_TCMD_CLIENT_BAT: + bat_client = client; + break; + case MOTO_CHG_TCMD_CLIENT_PM_ADC: + pm_adc_client = client; + break; + case MOTO_CHG_TCMD_CLIENT_AP_ADC: + ap_adc_client = client; + break; + default : + pr_err("%s invalide client id %d\n", + __func__, + client->client_id); + break; + } +#endif + + return 0; +} + +static int mmi_chg_tcmd_res_to_temp(struct mmi_chg_tcmd_data *data, + int vol, int vol_ntc, int r_pull) +{ + struct CHARGER_TEMPERATURE* table; + int t_ntc = 80; + int r_ntc; + int i; + + table = data->ntc_table; + if (table == NULL) { + pr_err("%s no ntc table\n", __func__); + return -20; + } + + //(vol - vol_ntc) / vol_ntc = r_pull / r_ntc + r_ntc = (vol_ntc * 1000 / (vol - vol_ntc) ) * r_pull / 1000; + pr_info("%s r_ntc %d, v_ntc %d, v_ref %d, r_pull %d\n", __func__, + r_ntc, vol_ntc, vol, r_pull); + for(i=0;i table[i].TemperatureR) { + t_ntc = table[i].CHARGER_Temp; + break; + } + } + + return t_ntc; +} + +static int mmi_chg_tcmd_get_ntc_table(struct mmi_chg_tcmd_data *data, int id) +{ + switch (id) { + case 1: + data->ntc_table = ntc_table1; + break; + default: + data->ntc_table = ntc_table1; + pr_info("%s do not find id %d. Using default ntc table\n", __func__, id); + break; + } + + return 0; +} + +static int mmi_chg_tcmd_parse_dt(struct mmi_chg_tcmd_data *data) +{ + struct device_node *node = data->pdev->dev.of_node; + char* channel_dt_list[MOTO_ADC_TCMD_CHANNEL_MAX] = {"mmi,adc-channel-cpu", + "mmi,adc-channel-charger", + "mmi,adc-channel-pa", + "mmi,adc-channel-batid", + "mmi,adc-channel-vbat"}; + int i; + int val; + int ret = 0; + + if (!node) { + pr_err("%s dtree info. missing\n", __func__); + return -ENODEV; + } + + ret = of_property_read_string(node, "mmi,bat-supply-name", + &data->bat_psy_name); + if (ret) { + pr_info("%s do not define battery psy name, used default\n", __func__); + data->bat_psy_name = "battery"; + } + + for (i = 0; i < MOTO_ADC_TCMD_CHANNEL_MAX; i++) { + ret = of_property_read_u32(node, channel_dt_list[i], &val); + if (ret) { + pr_err("%s donot find %s in dt\n", __func__, channel_dt_list[i]); + val = -ENODEV; + } + data->adc_channel_list[i] = val; + } + + ret = of_property_read_u32(node, "mmi,ntc_table", &val); + if (ret) { + pr_err("%s donot find ntc_table in dt\n", __func__); + val = -ENODEV; + } + mmi_chg_tcmd_get_ntc_table(data, val); + + ret = of_property_read_u32(node, "mmi,ntc_v_ref", &val); + if (ret) { + pr_err("%s donot find ntc v ref in dt\n", __func__); + val = -ENODEV; + } + data->ntc_v_ref = val; + + ret = of_property_read_u32(node, "mmi,ntc_r_pull", &val); + if (ret) { + pr_err("%s donot find ntc r pull in dt\n", __func__); + val = -ENODEV; + } + data->ntc_r_pull = val; + + ret = of_property_read_u32(node, "mmi,batid_v_ref", &val); + if (ret) { + pr_err("%s donot find batid v ref in dt\n", __func__); + val = -ENODEV; + } + data->batid_v_ref = val; + + ret = of_property_read_u32(node, "mmi,batid_r_pull", &val); + if (ret) { + pr_err("%s donot find batid r pull in dt\n", __func__); + val = -ENODEV; + } + data->batid_r_pull = val; + + return 0; +} + +static int mmi_chg_tcmd_probe(struct platform_device *pdev) +{ + struct mmi_chg_tcmd_data *data; + int ret; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->pdev = pdev; + platform_set_drvdata(pdev, data); + + ret = sysfs_create_groups(&pdev->dev.kobj, mmi_chg_tcmd_groups); + if (ret) { + dev_err(&pdev->dev, "%s Failed to create sysfs attributes\n", __func__); + return ret; + } + + mmi_chg_tcmd_parse_dt(data); + + data->bat_psy = power_supply_get_by_name(data->bat_psy_name); + + dev_info(&pdev->dev, "%s probe success\n", __func__); + + return 0; +} + +static int mmi_chg_tcmd_remove(struct platform_device *pdev) +{ + sysfs_remove_groups(&pdev->dev.kobj, mmi_chg_tcmd_groups); + //kobject_uevent(&pdev->dev.kobj, KOBJ_REMOVE); + platform_set_drvdata(pdev, NULL); + + dev_info(&pdev->dev, "%s remove success\n", __func__); + + return 0; +} + + +static struct of_device_id mmi_chg_tcmd_match_table[] = { + { .compatible = "mmi,mmi-chg-tcmd"}, + {} +}; + + +static struct platform_driver mmi_chg_tcmd_driver = { + .probe = mmi_chg_tcmd_probe, + .remove = mmi_chg_tcmd_remove, + .driver = { + .name = DRIVERNAME, + .of_match_table = mmi_chg_tcmd_match_table, + }, +}; + +int __init mmi_chg_tcmd_init(void) +{ + int ret; + + ret = platform_driver_register(&mmi_chg_tcmd_driver); + + return ret; +} + +void mmi_chg_tcmd_exit(void) +{ + platform_driver_unregister(&mmi_chg_tcmd_driver); +} + +module_init(mmi_chg_tcmd_init); +module_exit(mmi_chg_tcmd_exit); + +MODULE_DESCRIPTION("mmi-chg-tcmd Driver"); +MODULE_AUTHOR("Motorola"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/mmi_discrete_charger/Android.mk b/drivers/power/mmi_discrete_charger/Android.mk new file mode 100644 index 000000000000..480600643a98 --- /dev/null +++ b/drivers/power/mmi_discrete_charger/Android.mk @@ -0,0 +1,21 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_discrete_charger.ko +LOCAL_MODULE_TAGS := optional + +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/adapter_class.ko +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_charger.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +KBUILD_OPTIONS += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) +include $(DLKM_DIR)/AndroidKernelModule.mk + + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_discrete_charger_class.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/mmi_discrete_charger/Kbuild b/drivers/power/mmi_discrete_charger/Kbuild new file mode 100644 index 000000000000..7c149225341f --- /dev/null +++ b/drivers/power/mmi_discrete_charger/Kbuild @@ -0,0 +1,16 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/mmi_charger +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/mmi_discrete_charger + +obj-m += mmi_discrete_charger.o +mmi_discrete_charger-objs += mmi_discrete_charger_core.o +mmi_discrete_charger-objs += mmi_discrete_charger_iio.o +mmi_discrete_charger-objs += mmi_discrete_voter.o +mmi_discrete_charger-objs += mmi_discrete_factory_tcmd.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../usb/typec/adapter_class/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers + +obj-m += mmi_discrete_charger_class.o diff --git a/drivers/power/mmi_discrete_charger/Makefile b/drivers/power/mmi_discrete_charger/Makefile new file mode 100644 index 000000000000..9022da00e490 --- /dev/null +++ b/drivers/power/mmi_discrete_charger/Makefile @@ -0,0 +1,34 @@ +ifeq ($(strip $(MODULE_KERNEL_VERSION)), 5.15) + +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../usb/typec/adapter_class/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers + +else + +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + +endif diff --git a/drivers/power/mmi_discrete_charger/mmi_discrete_charger_class.c b/drivers/power/mmi_discrete_charger/mmi_discrete_charger_class.c new file mode 100644 index 000000000000..da2fbb3a2e02 --- /dev/null +++ b/drivers/power/mmi_discrete_charger/mmi_discrete_charger_class.c @@ -0,0 +1,431 @@ +/* Copyright (c) 2020, 2021 The Linux Foundation. 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 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. + */ + +#include +#include +#include +#include +#include +#include + +#include + +static struct class *charger_class; + +int charger_dev_is_charge_halted(struct charger_device *chg_dev, bool *en) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->is_charge_halted) + return chg_dev->ops->is_charge_halted(chg_dev, en); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_is_charge_halted); + +int charger_dev_set_constant_voltage(struct charger_device *chg_dev, u32 uV) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->set_constant_voltage) + return chg_dev->ops->set_constant_voltage(chg_dev, uV); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_set_constant_voltage); + +int charger_dev_set_charging_current(struct charger_device *chg_dev, u32 uA) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->set_charging_current) + return chg_dev->ops->set_charging_current(chg_dev, uA); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_set_charging_current); + +int charger_dev_get_charging_current(struct charger_device *chg_dev, u32 *uA) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->get_charging_current) + return chg_dev->ops->get_charging_current(chg_dev, uA); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_get_charging_current); + +int charger_dev_enable_charging(struct charger_device *chg_dev, bool en) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->enable_charging) + return chg_dev->ops->enable_charging(chg_dev, en); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_enable_charging); + +int charger_dev_enable_hz(struct charger_device *chg_dev, bool en) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->enable_hz) + return chg_dev->ops->enable_hz(chg_dev, en); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_enable_hz); + +int charger_dev_enable_termination(struct charger_device *chg_dev, bool en) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->enable_termination) + return chg_dev->ops->enable_termination(chg_dev, en); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_enable_termination); + +int charger_dev_is_enabled_charging(struct charger_device *chg_dev, bool *en) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->is_enabled_charging) + return chg_dev->ops->is_enabled_charging(chg_dev, en); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_is_enabled_charging); + +int charger_dev_dump_registers(struct charger_device *chg_dev, struct seq_file *m) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->dump_registers) + return chg_dev->ops->dump_registers(chg_dev, m); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_dump_registers); + +int charger_dev_rerun_aicl(struct charger_device *chg_dev) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->rerun_aicl) + return chg_dev->ops->rerun_aicl(chg_dev); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_rerun_aicl); + +int charger_dev_enable_otg(struct charger_device *chg_dev, bool en) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->enable_otg) + return chg_dev->ops->enable_otg(chg_dev, en); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_enable_otg); + +int charger_dev_set_boost_current_limit(struct charger_device *chg_dev, u32 uA) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->set_boost_current_limit) + return chg_dev->ops->set_boost_current_limit(chg_dev, uA); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_set_boost_current_limit); + +int charger_dev_enable_hw_jeita(struct charger_device *chg_dev, bool enable) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->enable_hw_jeita) + return chg_dev->ops->enable_hw_jeita(chg_dev, enable); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_enable_hw_jeita); + +int charger_dev_get_input_current(struct charger_device *chg_dev, u32 *uA) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->get_input_current) + return chg_dev->ops->get_input_current(chg_dev, uA); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_get_input_current); + +int charger_dev_set_input_current(struct charger_device *chg_dev, u32 uA) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->set_input_current) + return chg_dev->ops->set_input_current(chg_dev, uA); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_set_input_current); + +int charger_dev_set_input_voltage(struct charger_device *chg_dev, u32 uV) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->set_input_voltage_limit) + return chg_dev->ops->set_input_voltage_limit(chg_dev, uV); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_set_input_voltage); + +int charger_dev_is_usb_suspend(struct charger_device *chg_dev, bool *suspend_en) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->is_usb_suspend) + return chg_dev->ops->is_usb_suspend(chg_dev, suspend_en); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_is_usb_suspend); + +int charger_dev_set_usb_suspend(struct charger_device *chg_dev, bool suspend_en) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->set_usb_suspend) + return chg_dev->ops->set_usb_suspend(chg_dev, suspend_en); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_set_usb_suspend); + +int charger_dev_get_pulse_cnt(struct charger_device *chg_dev, int *count) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->get_pulse_cnt) + return chg_dev->ops->get_pulse_cnt(chg_dev, count); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_get_pulse_cnt); + +int charger_dev_set_dp_dm(struct charger_device *chg_dev, int val) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->set_dp_dm) + return chg_dev->ops->set_dp_dm(chg_dev, val); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_set_dp_dm); + +int charger_dev_get_real_charger_type(struct charger_device *chg_dev, int *charger_type) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->get_real_charger_type) + return chg_dev->ops->get_real_charger_type(chg_dev, charger_type); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_get_real_charger_type); + +int charger_dev_get_qc3p_power(struct charger_device *chg_dev, int *qc3p_power) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->get_qc3p_power) + return chg_dev->ops->get_qc3p_power(chg_dev, qc3p_power); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_get_qc3p_power); + +int charger_dev_config_pd_active(struct charger_device *chg_dev, int val) +{ + if (chg_dev != NULL && chg_dev->ops != NULL && + chg_dev->ops->config_pd_active) + return chg_dev->ops->config_pd_active(chg_dev, val); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(charger_dev_config_pd_active); + +int charger_dev_notify(struct charger_device *chg_dev) +{ + return srcu_notifier_call_chain( + &chg_dev->evt_nh, 0, &chg_dev->noti); +} +EXPORT_SYMBOL(charger_dev_notify); + +int register_charger_device_notifier(struct charger_device *chg_dev, + struct notifier_block *nb) +{ + int ret; + + ret = srcu_notifier_chain_register(&chg_dev->evt_nh, nb); + return ret; +} +EXPORT_SYMBOL(register_charger_device_notifier); + +int unregister_charger_device_notifier(struct charger_device *chg_dev, + struct notifier_block *nb) +{ + return srcu_notifier_chain_unregister(&chg_dev->evt_nh, nb); +} +EXPORT_SYMBOL(unregister_charger_device_notifier); + +static void charger_device_release(struct device *dev) +{ + struct charger_device *chg_dev = to_charger_device(dev); + + kfree(chg_dev); +} + +/** + * charger_device_register - create and register a new object of + * charger_device class. + * @name: the name of the new object + * @parent: a pointer to the parent device + * @devdata: an optional pointer to be stored for private driver use. + * The methods may retrieve it by using charger_get_data(charger_dev). + * @ops: the charger operations structure. + * + * Creates and registers new charger device. Returns either an + * ERR_PTR() or a pointer to the newly allocated device. + */ +struct charger_device *charger_device_register(const char *name, + struct device *parent, void *devdata, + const struct charger_ops *ops, + const struct charger_properties *props) +{ + struct charger_device *chg_dev; + static struct lock_class_key key; + struct srcu_notifier_head *head; + int rc; + + pr_debug("%s: name=%s\n", __func__, name); + chg_dev = kzalloc(sizeof(*chg_dev), GFP_KERNEL); + if (!chg_dev) + return ERR_PTR(-ENOMEM); + + head = &chg_dev->evt_nh; + srcu_init_notifier_head(head); + /* Rename srcu's lock to avoid LockProve warning */ + lockdep_init_map(&(&head->srcu)->dep_map, name, &key, 0); + mutex_init(&chg_dev->ops_lock); + chg_dev->dev.class = charger_class; + chg_dev->dev.parent = parent; + chg_dev->dev.release = charger_device_release; + dev_set_name(&chg_dev->dev, "%s", name); + dev_set_drvdata(&chg_dev->dev, devdata); + + /* Copy properties */ + if (props) { + memcpy(&chg_dev->props, props, + sizeof(struct charger_properties)); + } + rc = device_register(&chg_dev->dev); + if (rc) { + kfree(chg_dev); + return ERR_PTR(rc); + } + chg_dev->ops = ops; + return chg_dev; +} +EXPORT_SYMBOL(charger_device_register); + +/** + * charger_device_unregister - unregisters a discrete charger device + * object. + * @charger_dev: the discrete charger device object to be unregistered + * and freed. + * + * Unregisters a previously registered via charger_device_register object. + */ +void charger_device_unregister(struct charger_device *chg_dev) +{ + if (!chg_dev) + return; + + mutex_lock(&chg_dev->ops_lock); + chg_dev->ops = NULL; + mutex_unlock(&chg_dev->ops_lock); + device_unregister(&chg_dev->dev); +} +EXPORT_SYMBOL(charger_device_unregister); + +static int charger_match_device_by_name(struct device *dev, + const void *data) +{ + const char *name = data; + + return strcmp(dev_name(dev), name) == 0; +} + +struct charger_device *get_charger_by_name(const char *name) +{ + struct device *dev; + + if (!name) + return (struct charger_device *)NULL; + dev = class_find_device(charger_class, NULL, name, + charger_match_device_by_name); + + return dev ? to_charger_device(dev) : NULL; + +} +EXPORT_SYMBOL(get_charger_by_name); + +static ssize_t name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct charger_device *chg_dev = to_charger_device(dev); + + return snprintf(buf, 20, "%s\n", + chg_dev->props.alias_name ? + chg_dev->props.alias_name : "anonymous"); +} + +static DEVICE_ATTR_RO(name); + +static struct attribute *charger_class_attrs[] = { + &dev_attr_name.attr, + NULL, +}; + +static const struct attribute_group charger_group = { + .attrs = charger_class_attrs, +}; + +static const struct attribute_group *charger_groups[] = { + &charger_group, + NULL, +}; + +static void __exit charger_class_exit(void) +{ + class_destroy(charger_class); +} + +static int __init charger_class_init(void) +{ + charger_class = class_create(THIS_MODULE, "mmi_discrete_charger"); + if (IS_ERR(charger_class)) { + pr_err("Unable to create mmi discrete charger class; errno = %ld\n", + PTR_ERR(charger_class)); + return PTR_ERR(charger_class); + } + charger_class->dev_groups = charger_groups; + pr_info("success to create mmi discrete charger class \n"); + return 0; +} + +module_init(charger_class_init); +module_exit(charger_class_exit); + +MODULE_DESCRIPTION("Mmi Discrete Charger Class Device"); +MODULE_AUTHOR("Motorola Mobility LLC"); +MODULE_VERSION("1.0.0"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/power/mmi_discrete_charger/mmi_discrete_charger_core.c b/drivers/power/mmi_discrete_charger/mmi_discrete_charger_core.c new file mode 100644 index 000000000000..13f93532828b --- /dev/null +++ b/drivers/power/mmi_discrete_charger/mmi_discrete_charger_core.c @@ -0,0 +1,3768 @@ +/* Copyright (c) 2020, 2021 The Linux Foundation. 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 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_discrete_charger_core.h" +#include "mmi_discrete_voter.h" +#include "mmi_discrete_charger_iio.h" +#include "mmi_discrete_factory_tcmd.h" +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +#include +#endif + +static bool debug_enabled; +module_param(debug_enabled, bool, 0600); +MODULE_PARM_DESC(debug_enabled, "Enable debug for MMI DISCRETE CHARGER driver"); + +static struct mmi_discrete_charger *this_chip = NULL; + +#define typec_rp_med_high(chg, typec_mode) \ + ((typec_mode == MMI_POWER_SUPPLY_TYPEC_SOURCE_MEDIUM \ + || typec_mode == MMI_POWER_SUPPLY_TYPEC_SOURCE_HIGH)) + +#define MICRO_1PA 1000000 +#define MICRO_1P5A 1500000 +#define MICRO_3PA 3000000 +static int mmi_discrete_parse_dts(struct mmi_discrete_charger *chip) +{ + int rc = 0, byte_len; + struct device_node *node = chip->dev->of_node; + + rc = of_property_read_u32(node, + "mmi,batt-profile-fv-mv", &chip->batt_profile_fv_uv); + if (rc < 0) { + rc = of_property_read_u32(node, + "mmi,max-fv-mv", &chip->batt_profile_fv_uv); + } + + if (rc < 0) + chip->batt_profile_fv_uv = -EINVAL; + else + chip->batt_profile_fv_uv = chip->batt_profile_fv_uv * 1000; + + rc = of_property_read_u32(node, + "mmi,max-fcc-ma", &chip->batt_profile_fcc_ua); + if (rc < 0) + chip->batt_profile_fcc_ua = -EINVAL; + else + chip->batt_profile_fcc_ua = chip->batt_profile_fcc_ua * 1000; + + rc = of_property_read_u32(node, + "mmi,hw-max-icl-ua", &chip->hw_max_icl_ua); + if (rc < 0) + chip->hw_max_icl_ua = MICRO_3PA; + + rc = of_property_read_u32(node, + "mmi,otg-cl-ua", &chip->otg_cl_ua); + if (rc < 0) + chip->otg_cl_ua = MICRO_1PA; + + rc = of_property_read_u32(node, + "mmi,bat-ocp-ua", &chip->bat_ocp_ua); + if (rc < 0) + chip->bat_ocp_ua = 0; + + of_property_read_u32(node, "mmi,hvdcp2-max-icl-ua", + &chip->hvdcp2_max_icl_ua); + if (chip->hvdcp2_max_icl_ua <= 0) + chip->hvdcp2_max_icl_ua = MICRO_1P5A; + + of_property_read_u32(node, "mmi,wls-max-icl-ua", + &chip->wls_max_icl_ua); + if (chip->wls_max_icl_ua <= 0) + chip->wls_max_icl_ua = WIRELESS_CURRENT_DEFAULT_UA; + + rc = of_property_read_u32(node, "mmi,dc-icl-ma", + &chip->dc_cl_ma); + if (rc) + chip->dc_cl_ma = -EINVAL; + + if (of_find_property(node, "mmi,thermal-mitigation", &byte_len)) { + chip->thermal_mitigation = devm_kzalloc(chip->dev, byte_len, + GFP_KERNEL); + + if (chip->thermal_mitigation == NULL) + return -ENOMEM; + + chip->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "mmi,thermal-mitigation", + chip->thermal_mitigation, + chip->thermal_levels); + if (rc < 0) { + mmi_err(chip, "Couldn't read thermal limits rc = %d\n", rc); + return rc; + } + } + + rc = of_property_read_u32(node, "mmi,dcp-noneffc-cv", + &chip->dcp_noneffc_cv); + if (rc) + chip->dcp_noneffc_cv = 4500; + + chip->enable_10w_ffc = of_property_read_bool(node, "mmi,enable-10w-ffc"); + chip->mosfet_supported = of_property_read_bool(node, "mmi,usb-mosfet-supported"); + chip->pd_supported = of_property_read_bool(node, "mmi,usb-pd-supported"); + + /*mm8013 fg need charging mode info*/ + chip->chgmod_to_fg = of_property_read_bool(node, "mmi,cfg-chgmod-to-fg"); + + return 0; +} + +int get_prop_batt_present(struct mmi_discrete_charger *chg, + union power_supply_propval *val) +{ + int rc = 0; + + if (!chg->bms_psy) + return -EINVAL; + + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_PRESENT, val); + if (rc) + mmi_err(chg, "Couldn't get batt present prop rc=%d\n", rc); + + return rc; +} + +int get_prop_batt_charge_type(struct mmi_discrete_charger *chg, + union power_supply_propval *val) +{ + int rc = 0; + + if (!chg->charger_psy) + return -EINVAL; + + rc = power_supply_get_property(chg->charger_psy, + POWER_SUPPLY_PROP_CHARGE_TYPE, val); + if (rc) + mmi_err(chg, "Couldn't get batt charge type prop rc=%d\n", rc); + + return rc; +} + +int get_prop_system_temp_level(struct mmi_discrete_charger *chg, + union power_supply_propval *val) +{ + val->intval = chg->system_temp_level; + return 0; +} + +int get_prop_system_temp_level_max(struct mmi_discrete_charger *chg, + union power_supply_propval *val) +{ + val->intval = chg->thermal_levels; + return 0; +} + +int get_batt_current_now(struct mmi_discrete_charger *chg, + union power_supply_propval *val) +{ + int rc; + + if (!chg->bms_psy) + return -EINVAL; + + rc = power_supply_get_property(chg->bms_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, val); + if (!rc) + val->intval *= (-1); + else + mmi_err(chg, "Couldn't get current_now prop rc=%d\n", rc); + + return rc; +} + +int get_prop_input_suspend(struct mmi_discrete_charger *chg, + union power_supply_propval *val) +{ + val->intval = (get_client_vote(chg->usb_icl_votable, USER_VOTER) == 0) + && get_client_vote(chg->dc_suspend_votable, USER_VOTER); + + return 0; +} + +int set_prop_input_suspend(struct mmi_discrete_charger *chg, + const union power_supply_propval *val) +{ + int rc; + + /* vote 0mA when suspended */ + rc = vote(chg->usb_icl_votable, USER_VOTER, (bool)val->intval, 0); + if (rc < 0) { + mmi_err(chg, "Couldn't vote to %s USB rc=%d\n", + (bool)val->intval ? "suspend" : "resume", rc); + return rc; + } + + rc = vote(chg->dc_suspend_votable, USER_VOTER, (bool)val->intval, 0); + if (rc < 0) { + mmi_err(chg, "Couldn't vote to %s DC rc=%d\n", + (bool)val->intval ? "suspend" : "resume", rc); + return rc; + } + + power_supply_changed(chg->batt_psy); + + return rc; +} + +int set_prop_system_temp_level(struct mmi_discrete_charger *chg, + const union power_supply_propval *val) +{ + if (val->intval < 0) + return -EINVAL; + + if (chg->thermal_levels <= 0) + return -EINVAL; + + if (val->intval > chg->thermal_levels) + return -EINVAL; + + chg->system_temp_level = val->intval; + + if (chg->system_temp_level == chg->thermal_levels) + return vote(chg->chg_disable_votable, + THERMAL_DAEMON_VOTER, true, 0); + + vote(chg->chg_disable_votable, THERMAL_DAEMON_VOTER, false, 0); + if (chg->system_temp_level == 0) + return vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, false, 0); + + vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, true, + chg->thermal_mitigation[chg->system_temp_level]); + return 0; +} + +static int get_prop_usb_voltage_now(struct mmi_discrete_charger *chip, + union power_supply_propval *val) +{ + int rc; + + if (!chip->charger_psy) + return -EINVAL; + + rc = power_supply_get_property(chip->charger_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, val); + return rc; +} + +static int get_prop_usb_current_now(struct mmi_discrete_charger *chip, + union power_supply_propval *val) +{ + int rc; + + if (!chip->charger_psy) + return -EINVAL; + + rc = power_supply_get_property(chip->charger_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, val); + if (rc) + mmi_err(chip, "Couldn't get usb current now prop rc=%d\n", rc); + + return rc; +} + +static int get_prop_usb_present(struct mmi_discrete_charger *chip, + union power_supply_propval *val) +{ + int rc; + + if (!chip->charger_psy) + return -EINVAL; + + rc = power_supply_get_property(chip->charger_psy, + POWER_SUPPLY_PROP_PRESENT, val); + if (rc) + mmi_err(chip, "Couldn't get usb present prop rc=%d\n", rc); + + return rc; +} + +int get_prop_usb_online(struct mmi_discrete_charger *chip, + union power_supply_propval *val) +{ + int rc; + + if (!chip->charger_psy) + return -EINVAL; + + rc = power_supply_get_property(chip->charger_psy, + POWER_SUPPLY_PROP_ONLINE, val); + if (rc) + mmi_err(chip, "Couldn't get usb online prop rc=%d\n", rc); + + return rc; +} + +static int is_wls_online(struct mmi_discrete_charger *chip) +{ + int rc; + union power_supply_propval val; + + if (!chip->wls_psy){ + chip->wls_psy = power_supply_get_by_name("wireless"); + if (!chip->wls_psy) { + return 0; + } + } + + rc = power_supply_get_property(chip->wls_psy, + POWER_SUPPLY_PROP_ONLINE, &val); + if (rc < 0) { + mmi_err(chip, "Error wls online rc = %d\n", rc); + return 0; + } + + return val.intval; +} + +static int get_usb_online(struct mmi_discrete_charger *chip, + union power_supply_propval *val) +{ + int rc; + + rc = get_prop_usb_online(chip, val); + if (!val->intval) + return rc; + + //typec logic mask, bring up bc1.2 firstly + if ((chip->typec_mode == MMI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) + && chip->real_charger_type == POWER_SUPPLY_TYPE_USB) + val->intval = 0; + else + val->intval = 1; + + if (is_wls_online(chip)) + val->intval = 0; + + if (chip->real_charger_type == POWER_SUPPLY_TYPE_UNKNOWN) + val->intval = 0; + + return rc; +} + +static int get_usb_port_online(struct mmi_discrete_charger *chip, + union power_supply_propval *val) +{ + int rc; + + rc = get_prop_usb_online(chip, val); + if (!val->intval) + return rc; + + //typec logic mask, bring up bc1.2 firstly + if ((chip->typec_mode == + MMI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT) + && chip->real_charger_type == POWER_SUPPLY_TYPE_USB) + val->intval = 1; + else + val->intval = 0; + + return rc; +} + +static int get_prop_input_current_settled(struct mmi_discrete_charger *chip, + union power_supply_propval *val) +{ + int rc; + + if (!chip->charger_psy) + return -EINVAL; + + rc = power_supply_get_property(chip->charger_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, val); + if (rc) + mmi_err(chip, "Couldn't get usb input current limit prop rc=%d\n", rc); + + return rc; +} + +static int get_prop_dc_present(struct mmi_discrete_charger *chip, + union power_supply_propval *val) +{ + int rc; + + if (!chip->wls_psy) { + chip->wls_psy = power_supply_get_by_name("wireless"); + if (!chip->wls_psy) { + return -EINVAL; + } + } + + rc = power_supply_get_property(chip->wls_psy, + POWER_SUPPLY_PROP_PRESENT, val); + if (rc) + mmi_err(chip, "Couldn't get dc present prop rc=%d\n", rc); + + return rc; +} + +static int get_prop_dc_online(struct mmi_discrete_charger *chip, + union power_supply_propval *val) +{ + int rc; + + if (!chip->wls_psy) { + chip->wls_psy = power_supply_get_by_name("wireless"); + if (!chip->wls_psy) { + return -EINVAL; + } + } + + rc = power_supply_get_property(chip->wls_psy, + POWER_SUPPLY_PROP_ONLINE, val); + if (rc) + mmi_err(chip, "Couldn't get dc online prop rc=%d\n", rc); + + return rc; +} + +static int get_prop_dc_voltage_now(struct mmi_discrete_charger *chip, + union power_supply_propval *val) +{ + int rc = 0; + + if (!chip->wls_psy) { + chip->wls_psy = power_supply_get_by_name("wireless"); + if (!chip->wls_psy) { + val->intval = -EINVAL; + return 0; + } + } + + rc = power_supply_get_property(chip->wls_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + val); + if (rc < 0) { + mmi_err(chip, "Couldn't get POWER_SUPPLY_PROP_VOLTAGE_NOW, rc=%d\n", + rc); + return rc; + } + return rc; +} + +static int get_prop_dc_current_now(struct mmi_discrete_charger *chg, + union power_supply_propval *val) +{ + int rc = 0; + + if (!chg->wls_psy) { + chg->wls_psy = power_supply_get_by_name("wireless"); + if (!chg->wls_psy) { + val->intval = -EINVAL; + return 0; + } + } + + if (chg->wls_psy) { + rc = power_supply_get_property(chg->wls_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, + val); + if (rc < 0) { + mmi_err(chg, "Couldn't get VOLTAGE_MAX, rc=%d\n", + rc); + return rc; + } + } + return rc; +} + +static int get_prop_dc_current_max(struct mmi_discrete_charger *chip, + union power_supply_propval *val) +{ + int rc; + + if (!chip->wls_psy) + return -EINVAL; + + rc = power_supply_get_property(chip->wls_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, val); + if (rc) + mmi_err(chip, "Couldn't get dc current max prop rc=%d\n", rc); + + return rc; +} + +static int get_prop_dc_voltage_max(struct mmi_discrete_charger *chg, + union power_supply_propval *val) +{ + int rc = 0; + + if (!chg->wls_psy) { + chg->wls_psy = power_supply_get_by_name("wireless"); + if (!chg->wls_psy) { + val->intval = -EINVAL; + return 0; + } + } + + if (chg->wls_psy) { + rc = power_supply_get_property(chg->wls_psy, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + val); + if (rc < 0) { + mmi_err(chg, "Couldn't get VOLTAGE_MAX, rc=%d\n", + rc); + return rc; + } + } + return 0; +} + +static int set_prop_dc_current_max(struct mmi_discrete_charger *chip, + const union power_supply_propval *val) +{ + int rc; + + if (!chip->wls_psy) + return -EINVAL; + + rc = power_supply_set_property(chip->wls_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, val); + if (rc) + mmi_err(chip, "Couldn't set dc current max prop rc=%d\n", rc); + + return rc; +} + +static int mmi_get_rp_based_dcp_current(struct mmi_discrete_charger *chg, int typec_mode) +{ + int rp_ua; + + switch (typec_mode) { + case MMI_POWER_SUPPLY_TYPEC_SOURCE_HIGH: + rp_ua = TYPEC_HIGH_CURRENT_UA; + break; + case MMI_POWER_SUPPLY_TYPEC_SOURCE_MEDIUM: + rp_ua = TYPEC_MEDIUM_CURRENT_UA; + break; + case MMI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT: + /* fall through */ + default: + rp_ua = DCP_CURRENT_UA; + if (chg->constraint.dcp_pmax > 0) + rp_ua = (chg->constraint.dcp_pmax / 5) * 1000; + } + + return rp_ua; +} + +static int mmi_discrete_get_batt_capacity_level(struct mmi_discrete_charger *chg) +{ + union power_supply_propval batt_soc; + + power_supply_get_property(chg->mmi_psy, POWER_SUPPLY_PROP_CAPACITY, &batt_soc); + + if (batt_soc.intval >= 100) + return POWER_SUPPLY_CAPACITY_LEVEL_FULL; + else if (batt_soc.intval >= 80 && batt_soc.intval < 100) + return POWER_SUPPLY_CAPACITY_LEVEL_HIGH; + else if (batt_soc.intval >= 20 && batt_soc.intval < 80) + return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + else if (batt_soc.intval > 0 && batt_soc.intval < 20) + return POWER_SUPPLY_CAPACITY_LEVEL_LOW; + else if (batt_soc.intval == 0) + return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + else + return POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; +} + +static int mmi_discrete_handle_usb_current(struct mmi_discrete_charger *chg, + int usb_current) +{ + int rc = 0, rp_ua; + union power_supply_propval val = {0, }; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + if (chg->real_charger_type == QTI_POWER_SUPPLY_TYPE_USB_FLOAT) { +#else + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) { +#endif + if (usb_current == -ETIMEDOUT) { + /* + * Valid FLOAT charger, report the current + * based of Rp. + */ + rp_ua = mmi_get_rp_based_dcp_current(chg, + chg->typec_mode); + rc = vote(chg->usb_icl_votable, + SW_ICL_MAX_VOTER, true, rp_ua); + if (rc < 0) + return rc; + } else { + /* + * FLOAT charger detected as SDP by USB driver, + * charge with the requested current and update the + * real_charger_type + */ + chg->real_charger_type = POWER_SUPPLY_TYPE_USB; + rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, + true, usb_current); + if (rc < 0) + return rc; + rc = vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, + false, 0); + if (rc < 0) + return rc; + } + } else { + rc = get_prop_usb_present(chg, &val); + if (!rc && !val.intval) + return 0; + + if (typec_rp_med_high(chg, chg->typec_mode)) { + vote(chg->usb_icl_votable, USB_PSY_VOTER, + false, 0); + return 0; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_CDP) + return 0; +#endif + + rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, true, + usb_current); + if (rc < 0) { + mmi_err(chg, "Couldn't vote ICL USB_PSY_VOTER rc=%d\n", rc); + return rc; + } + + rc = vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0); + if (rc < 0) { + mmi_err(chg, "Couldn't remove SW_ICL_MAX vote rc=%d\n", rc); + return rc; + } + + } + + return 0; +} + +static int set_prop_sdp_current_max(struct mmi_discrete_charger *chg, + int val) +{ + union power_supply_propval pval; + int rc = 0; + + if (!chg->pd_active) { + rc = get_prop_usb_present(chg, &pval); + if (rc < 0) { + mmi_err(chg, "Couldn't get usb present rc = %d\n", + rc); + return rc; + } + + /* handle the request only when USB is present */ + if (pval.intval) + rc = mmi_discrete_handle_usb_current(chg, val); + } + + return rc; +} + +static int mmi_discrete_update_usb_type(struct mmi_discrete_charger *chip) +{ + int rc = 0; + int chg_type = 0; + + if (!chip->master_chg_dev) + return -EINVAL; + + if (is_wls_online(chip)) { + mmi_info(chip, "wireless charger detected\n"); + chip->real_charger_type = POWER_SUPPLY_TYPE_WIRELESS; + + return 0; + } + + rc = charger_dev_get_real_charger_type(chip->master_chg_dev, &chg_type); + + if (rc) { + mmi_err(chip, "Can't get apsd result\n"); + return rc; + } + + chip->bc1p2_charger_type = chg_type; + + if (chip->pd_active) { + chip->real_charger_type = POWER_SUPPLY_TYPE_USB_PD; + } else { + chip->real_charger_type = chg_type; + } + + mmi_info(chip, "APSD=%d PD=%d real type=%d\n", + chip->bc1p2_charger_type, chip->pd_active, chip->real_charger_type); + return rc; +} + +int mmi_discrete_set_usb_suspend(struct mmi_discrete_charger *chip, bool suspend) +{ + int rc = 0; + static int old_current = 0; + static bool old_suspend = false; + + if (!chip->constraint.factory_mode || !chip->usb_icl_votable) + return rc; + + pr_info("%s:suspend=%d, old_suspend=%d, old_current=%d\n", + __func__, suspend, old_suspend, old_current); + + if (suspend == true && old_suspend == false) { + old_suspend = true; + old_current = get_effective_result(chip->usb_icl_votable); + /* vote 0mA when suspended */ + pmic_vote_force_val_set(chip->usb_icl_votable, 0); + pmic_vote_force_active_set(chip->usb_icl_votable, 1); + } else if (suspend == false && old_suspend == true) { + old_suspend = false; + if (get_effective_result(chip->usb_icl_votable) == 0) { + pmic_vote_force_val_set(chip->usb_icl_votable, (u32)old_current); + pmic_vote_force_active_set(chip->usb_icl_votable, 1); + } + } + + return rc; +} + +static int mmi_discrete_set_dc_suspend(struct mmi_discrete_charger *chip, bool suspend) +{ + //need to set dc suspend + return 0; +} + +int mmi_discrete_is_usb_suspended(struct mmi_discrete_charger *chip) +{ + bool enable = 0; + + if (chip->usb_icl_votable) + enable = get_effective_result(chip->usb_icl_votable) == 0; + + return enable; +} + +static int mmi_discrete_disable_hw_jeita(struct mmi_discrete_charger *chip) +{ + int rc = 0; + + rc = charger_dev_enable_hw_jeita(chip->master_chg_dev, false); + + if (rc) + mmi_err(chip, "Couldn't disable hw jeita rc=%d\n", rc); + + return rc; +} + +int mmi_discrete_otg_enable(struct mmi_discrete_charger *chip, bool en) +{ + int rc = 0; + + if (en == !chip->vbus_enabled) { + rc = charger_dev_enable_otg(chip->master_chg_dev, en); + if (rc) { + mmi_err(chip, "Unable to %s otg (%d)\n", en?"enable":"disable", rc); + return rc; + } + chip->vbus_enabled = en; + + if (chip->bat_ocp_ua) { + if (en == true) + schedule_delayed_work(&chip->monitor_ibat_work, msecs_to_jiffies(1000)); + else + cancel_delayed_work(&chip->monitor_ibat_work); + } + } + + return rc; +} + +static void mmi_discrete_monitor_ibat_work(struct work_struct *work) +{ + struct mmi_discrete_charger *chip = container_of(work, + struct mmi_discrete_charger, + monitor_ibat_work.work); + union power_supply_propval val = {0, }; + int ret = 0; + int total_current = 0; + int average_current = 0; + int i = 0; + + for (i = 0; i < 3; i++) { + ret = get_batt_current_now(chip, &val); + if (ret) { + val.intval = 0; + } + + total_current += val.intval; + val.intval = 0; + msleep(100); + } + + average_current = total_current / i; + + if (average_current < 0) + average_current = average_current * (-1); + + if (average_current > chip->bat_ocp_ua) { + mmi_discrete_otg_enable(chip, false); + mmi_info(chip, "battery current %d > %d, disable OTG\n", + average_current, chip->bat_ocp_ua); + } else + schedule_delayed_work(&chip->monitor_ibat_work, msecs_to_jiffies(1000)); + +} + +static void mmi_discrete_check_otg_power(struct mmi_discrete_charger *chip) +{ + + if (chip->vbus_enabled) { + mmi_discrete_otg_enable(chip, false); + mmi_info(chip, "VBUS OTG Disable due to Charger\n"); + } +} + +static void mmi_set_pd20_cap(struct mmi_discrete_charger *chg, + int pdo, int mv, int ma) +{ + int ret; + + ret = adapter_dev_set_cap(chg->pd_adapter_dev, MMI_PD_FIXED, + pdo, mv, ma); + if (ret != MMI_ADAPTER_OK) + mmi_info(chg, "set pd20 cap fail ret=%d\n", ret); +} + +static void mmi_pd30_start(struct mmi_discrete_charger *chg, + int pdo, int mv, int ma) +{ + int ret; + + ret = adapter_dev_set_cap(chg->pd_adapter_dev, MMI_PD_APDO_START, + pdo, mv, ma); + if (ret != MMI_ADAPTER_OK) + mmi_info(chg, "set pd30 cap start fail ret=%d\n", ret); +} + +static void mmi_set_pd30_cap(struct mmi_discrete_charger *chg, + int pdo, int mv, int ma) +{ + int ret; + + ret = adapter_dev_set_cap(chg->pd_adapter_dev, MMI_PD_APDO, + pdo, mv, ma); + if (ret != MMI_ADAPTER_OK) + mmi_info(chg, "set pd30 cap fail ret=%d\n", ret); +} + + +#define CHARGER_POWER_18W 18000 +#define PD_POWER_MIN 15000 +#define PPS_VOLT_MV_MIN 5000 +#define PPS_CURR_MA_MIN 2000 +#define PD_CURR_MA_MAX 3000 +#define MICRO_MV_5V 5000 +#define MICRO_MV_9V 9000 +#define MICRO_MV_1V 1000 +static void mmi_discrete_config_pd_charger(struct mmi_discrete_charger *chg) +{ + int rc = -EINVAL, i =0, req_pdo = -1, req_pd_volt = 0, req_pd_curr = 0; + int fixed_pd_volt = 0; + union power_supply_propval val = {0, }; + bool vbus_present = false, pps_active = false, fixed_active = false; + static bool pps_start = false, fixed_power_done = false; + struct adapter_power_cap cap; + int vbus_mv; + + if (chg->pd_active && chg->cp_active) { + vote(chg->usb_icl_votable, PD_VOTER, true, TYPEC_HIGH_CURRENT_UA); + } + + rc = get_prop_usb_present(chg, &val); + if (rc < 0) { + mmi_err(chg, "Couldn't get usb present rc = %d\n", rc); + return; + } + vbus_present = val.intval; + if (chg->pd_active == MMI_POWER_SUPPLY_PD_INACTIVE + || !vbus_present + || chg->cp_active) { + pps_start = false; + fixed_power_done = false; + return; + } + + if (!chg->pd_adapter_dev) { + chg->pd_adapter_dev = get_adapter_by_name("pd_adapter"); + if (!chg->pd_adapter_dev) { + mmi_err(chg, "Failed: pd_adapter has not registered yet\n"); + return; + } + } + + for (i = 0; i < ADAPTER_CAP_MAX_NR; i++) { + cap.max_mv[i] = 0; + cap.min_mv[i] = 0; + cap.ma[i] = 0; + cap.type[i] = 0; + cap.pwr_limit[i] = 0; + } + + rc = adapter_dev_get_cap(chg->pd_adapter_dev, MMI_PD_ALL, &cap); + if (MMI_ADAPTER_OK != rc) { + mmi_err(chg, "Couldn't get pdo rc = %d\n", rc); + return; + } + + rc = get_prop_usb_voltage_now(chg, &val); + if (rc < 0) { + mmi_err(chg, "Couldn't get usb vbus rc = %d\n", rc); + return; + } + vbus_mv = val.intval; + + for (i = 0; i < cap.nr; i++) { + if (cap.type[i] == MMI_PD_APDO + && cap.max_mv[i] >= PPS_VOLT_MV_MIN + && cap.ma[i] >= PPS_CURR_MA_MIN + && cap.max_mv[i] * cap.ma[i] / 1000 >= this_chip->constraint.pd_pmax) { + req_pdo = i; + req_pd_curr = cap.ma[i]; + req_pd_curr = min(req_pd_curr, PD_CURR_MA_MAX); + req_pd_volt = (this_chip->constraint.pd_pmax* 1000 / req_pd_curr) % 20; + req_pd_volt = (this_chip->constraint.pd_pmax * 1000 / req_pd_curr) - req_pd_volt; + req_pd_volt = max(req_pd_volt, PPS_VOLT_MV_MIN); + req_pd_volt = min(req_pd_volt, cap.max_mv[i]); + pps_active = true; + mmi_info(chg, "pps active: i=%d,pps_volt=%d, pps_curr=%d\n", + i, req_pd_volt, req_pd_curr); + break; + } + } + + if (!pps_active) { + pps_start = false; + + if (this_chip->constraint.pd_pmax < CHARGER_POWER_18W) + fixed_pd_volt = MICRO_MV_5V; + else + fixed_pd_volt = MICRO_MV_9V; + + for (i = 0; i < cap.nr; i++) { + if ((cap.type[i] == MMI_PD_FIXED) + && cap.max_mv[i] <= fixed_pd_volt + && cap.max_mv[i] >= MICRO_MV_5V) { + req_pdo = i; + req_pd_volt = cap.max_mv[i]; + req_pd_curr = this_chip->constraint.pd_pmax * 1000 / req_pd_volt; + req_pd_curr = min(req_pd_curr, cap.ma[i]); + req_pd_curr = min(req_pd_curr, PD_CURR_MA_MAX); + fixed_active = true; + mmi_info(chg, "fixed pdo active:i=%d,fixed_volt=%d, fixed_curr=%d\n", + i, req_pd_volt, req_pd_curr); + } + } + + if (fixed_active && vbus_mv < req_pd_volt - MICRO_MV_1V) + fixed_power_done = false; + } + + if (!pps_active && !fixed_active) + return; + + if (!fixed_power_done && fixed_active) { + mmi_set_pd20_cap(chg, req_pdo, req_pd_volt, req_pd_curr); + fixed_power_done = true; + mmi_info(chg, "Request fixed power , re-vote USB_ICL %dmA\n", req_pd_curr); + } else if (pps_active) { + if (!pps_start) { + mmi_pd30_start(chg, req_pdo, req_pd_volt, req_pd_curr); + pps_start = true; + } + mmi_set_pd30_cap(chg, req_pdo, req_pd_volt, req_pd_curr); + } + + /* + * Third charging ic IINLIM bits will be changed auto + * when BC1.2 done. So we need to Ignore the PD + * vote unless BC1.2 done. + */ + if (chg->bc1p2_charger_type != POWER_SUPPLY_TYPE_UNKNOWN) { + rc = vote(chg->usb_icl_votable, PD_VOTER, true, req_pd_curr * 1000); + if (rc < 0) + mmi_info(chg, "vote PD USB_ICL failed %duA\n", req_pd_curr); + } else + mmi_info(chg, "Ignore PD VOTER ICL before bc1.2\n"); + + mmi_info(chg, + "Request PD power, fixed %d, pps %d, pdo %d, " + "req volt %dmV, req curr %dmA, vbus %dmV\n", + fixed_active, + pps_active, + req_pdo, + req_pd_volt, + req_pd_curr, + vbus_mv); + return; +} + +#define HVDCP_POWER_MIN 15000 +#define HVDCP_VOLTAGE_BASIC (this_chip->constraint.hvdcp_pmax / 3) +#define HVDCP_VOLTAGE_NOM (HVDCP_VOLTAGE_BASIC - 200) +#define HVDCP_VOLTAGE_MAX (HVDCP_VOLTAGE_BASIC + 200) +#define HVDCP_VOLTAGE_MIN 4000 +#define HVDCP_PULSE_COUNT_MAX ((HVDCP_VOLTAGE_BASIC - 5000) / 200 + 2) +static void mmi_discrete_config_qc_charger(struct mmi_discrete_charger *chg) +{ + int rc = -EINVAL; + int pulse_cnt; + int vbus_mv; + bool stepwise; + union power_supply_propval val = {0, }; + + rc = charger_dev_get_pulse_cnt(chg->master_chg_dev, &pulse_cnt); + if (rc < 0) + return; + + /* + * Two hvdcp voltage regulation algorithm are supported: + * stepwise and supplement. stepwise will be used if QC3.0 + * charger power is more than 15W. + * It only runs once for stepwise algorithm after QC3.0 + * charger is detected, while it will run all the time in + * supplement algorithm if QC3.0 charger is present. + */ + stepwise = chg->constraint.hvdcp_pmax > HVDCP_POWER_MIN; + if (stepwise && pulse_cnt) + return; + + if (stepwise) { + vote(chg->chg_disable_votable, MMI_HB_VOTER, true, 0); + mmi_info(chg, "Disable charging before stepwise configure\n"); + } + + do { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + if (chg->real_charger_type != QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3) { +#else + if (chg->real_charger_type != POWER_SUPPLY_TYPE_USB_HVDCP_3) { +#endif + mmi_warn(chg, "Exit for QC3.0 charger removal\n"); + break; + } + + rc = get_prop_usb_voltage_now(chg, &val); + if (rc < 0) + break; + vbus_mv = val.intval / 1000; + + mmi_info(chg, "pulse_cnt=%d, vbus_mv=%d\n", pulse_cnt, vbus_mv); + if (vbus_mv < HVDCP_VOLTAGE_NOM && + pulse_cnt < HVDCP_PULSE_COUNT_MAX) + val.intval = MMI_POWER_SUPPLY_DP_DM_DP_PULSE; + else if (vbus_mv > HVDCP_VOLTAGE_MAX && + pulse_cnt > 0 && !stepwise) + val.intval = MMI_POWER_SUPPLY_DP_DM_DM_PULSE; + else { + mmi_info(chg, "QC3.0 output configure completed\n"); + break; + } + + rc = charger_dev_set_dp_dm(chg->master_chg_dev, val.intval); + if (rc < 0) + break; + else if (!stepwise) { + power_supply_changed(chg->usb_psy); + break; + } + msleep(100); + + rc = charger_dev_get_pulse_cnt(chg->master_chg_dev, &pulse_cnt); + if (rc < 0) + break; + + } while (stepwise); + + if (stepwise) { + charger_dev_set_input_voltage(chg->master_chg_dev, + (HVDCP_VOLTAGE_NOM - 1000)*1000); + vote(chg->chg_disable_votable, MMI_HB_VOTER, false, 0); + mmi_info(chg, "Recover charging after stepwise configure\n"); + } +} + +#define WLS_ICL_INCREASE_STEP 100000 +#define WLS_ICL_MIN 500000 +static void mmi_discrete_wireless_icl_work(struct work_struct *work) +{ + struct mmi_discrete_charger *chip = container_of(work, + struct mmi_discrete_charger, + wireless_icl_work.work); + int wls_icl = 0; + union power_supply_propval wls_output = {0, }; + int ret = 0; + + wls_icl = get_client_vote(chip->usb_icl_votable, SW_ICL_MAX_VOTER); + + if (wls_icl >= chip->wls_max_icl_ua)//wls icl have been setted + return; + + while((wls_icl + WLS_ICL_INCREASE_STEP) <= chip->wls_max_icl_ua) { + if(!is_wls_online(chip)) + return; + wls_icl += WLS_ICL_INCREASE_STEP; + + if (wls_icl < WLS_ICL_MIN) + wls_icl = WLS_ICL_MIN; + + vote(chip->usb_icl_votable, SW_ICL_MAX_VOTER, true, wls_icl); + msleep(500); + wls_icl = get_client_vote(chip->usb_icl_votable, SW_ICL_MAX_VOTER); + mmi_info(chip, "vote wireless charging icl %d ua\n", wls_icl); + } + + if (!chip->wls_psy) + return; + + ret = power_supply_get_property(chip->wls_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &wls_output); + if (ret) + mmi_err(chip, "Couldn't get wls current now prop rc=%d\n", ret); + + mmi_info(chip, "get wls current now =%d\n", wls_output.intval); + if (wls_output.intval >= 500) + return; + + wls_icl = 0; + while((wls_icl + WLS_ICL_INCREASE_STEP) <= chip->wls_max_icl_ua) { + if(!is_wls_online(chip)) + return; + wls_icl += WLS_ICL_INCREASE_STEP; + + vote(chip->usb_icl_votable, SW_ICL_MAX_VOTER, true, wls_icl); + msleep(500); + wls_icl = get_client_vote(chip->usb_icl_votable, SW_ICL_MAX_VOTER); + mmi_info(chip, "vote wireless charging icl %d ua\n", wls_icl); + } + charger_dev_rerun_aicl(chip->master_chg_dev); + +} + +/*MIN is 2.5W -> default icl 500mA * input vol 5V*/ +#define WLS_POWER_MIN 2500 +static void mmi_discrete_config_wls_charger(struct mmi_discrete_charger *chg) +{ + mmi_dbg(chg, "Configure wireless charger\n"); + + if (chg->bc1p2_charger_type != POWER_SUPPLY_TYPE_UNKNOWN) { + cancel_delayed_work(&chg->wireless_icl_work); + schedule_delayed_work(&chg->wireless_icl_work, msecs_to_jiffies(0)); + } +} + +static void mmi_discrete_config_charger_input(struct mmi_discrete_charger *chip) +{ + int rc; + int i; + bool charger_suspend = false; + union power_supply_propval val; + struct mmi_charger_cfg *cfg; + + if (!chip->chg_clients || !chip->chg_client_num) { + mmi_err(chip, "Invalid charger client\n"); + return; + } + + for (i = 0; i < chip->chg_client_num; i++) { + cfg = &chip->chg_clients[i].chg_cfg; + if (cfg->charger_suspend) { + charger_suspend = true; + break; + } + } + + if (charger_suspend) { + if (chip->constraint.factory_mode) { + mmi_discrete_set_usb_suspend(chip, true); + mmi_discrete_set_dc_suspend(chip, true); + } else { + vote(chip->usb_icl_votable, MMI_HB_VOTER, true, 0); + vote(chip->dc_suspend_votable, MMI_HB_VOTER, true, 1); + } + return; + } else { + if (chip->constraint.factory_mode) { + mmi_discrete_set_usb_suspend(chip, false); + mmi_discrete_set_dc_suspend(chip, false); + } else { + rc = get_prop_dc_present(chip, &val); + if (!rc && val.intval) + vote(chip->dc_suspend_votable, MMI_HB_VOTER, + false, 0); + else + vote(chip->dc_suspend_votable, MMI_HB_VOTER, + true, 1); + + vote(chip->usb_icl_votable, MMI_HB_VOTER, false, 0); + } + } + + if (chip->usb_psy && + chip->constraint.pd_pmax >= PD_POWER_MIN) + mmi_discrete_config_pd_charger(chip); + + if (chip->charger_psy && chip->usb_psy && + chip->constraint.hvdcp_pmax >= HVDCP_POWER_MIN && + chip->chg_info.chrg_mv > HVDCP_VOLTAGE_MIN && +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + chip->chg_info.chrg_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3 && +#else + chip->chg_info.chrg_type == POWER_SUPPLY_TYPE_USB_HVDCP_3 && +#endif + chip->pd_active == MMI_POWER_SUPPLY_PD_INACTIVE) + mmi_discrete_config_qc_charger(chip); + + if (chip->wls_psy && + chip->constraint.wls_pmax > WLS_POWER_MIN && + chip->chg_info.chrg_type == POWER_SUPPLY_TYPE_WIRELESS) + mmi_discrete_config_wls_charger(chip); +} + +static void mmi_discrete_config_charger_output(struct mmi_discrete_charger *chip) +{ + int i; + int target_fcc = INT_MAX; + int target_fv = INT_MAX; + bool chg_dis; + bool charging_reset = false; + bool charging_disable = false; + struct mmi_charger_cfg *cfg; + + if (!chip->chg_clients || !chip->chg_client_num) { + mmi_err(chip, "Invalid charger client\n"); + return; + } + + chg_dis = get_effective_result(chip->chg_disable_votable); + for (i = 0; i < chip->chg_client_num; i++) { + cfg = &chip->chg_clients[i].chg_cfg; + target_fcc = min(target_fcc, cfg->target_fcc); + target_fv = min(target_fv, cfg->target_fv); + if (cfg->charging_disable || target_fcc < 0) + charging_disable = true; + if (cfg->charging_reset) + charging_reset = true; + if (cfg->taper_kickoff) + chip->chg_clients[i].chrg_taper_cnt = 0; + } + + if (chip->ffc_stop_chrg && chip->enable_10w_ffc) + charging_disable = true; + + if (charging_reset && !charging_disable && !chg_dis) { + vote(chip->chg_disable_votable, MMI_HB_VOTER, true, 0); + mmi_warn(chip, "Charge Halt..Toggle\n"); + msleep(50); + } + + vote(chip->fv_votable, MMI_HB_VOTER, true, target_fv * 1000); + if (charging_disable) { + vote(chip->fcc_votable, MMI_HB_VOTER, true, 0); + vote(chip->chg_disable_votable, MMI_HB_VOTER, true, 0); + } else { + vote(chip->fcc_votable, MMI_HB_VOTER, true, target_fcc * 1000); + vote(chip->chg_disable_votable, MMI_HB_VOTER, false, 0); + } + + charging_disable = get_effective_result(chip->chg_disable_votable); + /* Rerun AICL to recover USB ICL from recharge */ + if (chg_dis != charging_disable || charging_reset) { + charger_dev_rerun_aicl(chip->master_chg_dev); + mmi_warn(chip, "need to rerun AICL\n"); + } +} + +enum { + MMI_CHARGER_MODE_FFC = 0, + MMI_CHARGER_MODE_NORMAL, + MMI_CHARGER_MODE_USB, +}; + +#define TURBO_CHRG_FFC_THRSH_MW 25000 + +static void mmi_discrete_config_chgmod_to_fg(struct mmi_discrete_charger *chip) +{ + union power_supply_propval val; + int rc = 0; + if (!chip->bms_psy) + return; + if (chip->chg_info.chrg_pmax_mw > TURBO_CHRG_FFC_THRSH_MW|| + (chip->is_ffc_enable && chip->enable_10w_ffc)) { + val.intval = MMI_CHARGER_MODE_FFC; + } else if (chip->chg_info.chrg_type == POWER_SUPPLY_TYPE_USB) { + val.intval = MMI_CHARGER_MODE_USB; + } else { + val.intval = MMI_CHARGER_MODE_NORMAL; + } + + rc = power_supply_set_property(chip->bms_psy, POWER_SUPPLY_PROP_TYPE, &val); + mmi_dbg(chip,"%s config charger mode to fg\n", rc? "Can't" : "success"); + +} + +static void mmi_discrete_charger_work(struct work_struct *work) +{ + struct mmi_discrete_charger *chip = container_of(work, + struct mmi_discrete_charger, + charger_work.work); + + pm_stay_awake(chip->dev); + mmi_dbg(chip, "MMI Discrete Charger Work!\n"); + + if (chip->chg_info.chrg_present) { + mmi_discrete_check_otg_power(chip); + } + mmi_discrete_config_charger_input(chip); + mmi_discrete_config_charger_output(chip); + + mmi_discrete_config_chgmod_to_fg(chip); + + mmi_info(chip, "FV=%d, FCC=%d, CHGDIS=%d, USBICL=%d, USBDIS=%d\n", + get_effective_result(chip->fv_votable), + get_effective_result(chip->fcc_votable), + get_effective_result(chip->chg_disable_votable), + get_effective_result(chip->usb_icl_votable), + mmi_discrete_is_usb_suspended(chip)); + pm_relax(chip->dev); +} + +/************************ + * BATTERY PSY REGISTRATION * + ************************/ +static enum power_supply_property batt_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, +}; + +static int batt_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int rc = 0; + struct mmi_discrete_charger *chip = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_TEMP: + case POWER_SUPPLY_PROP_STATUS: + case POWER_SUPPLY_PROP_HEALTH: + case POWER_SUPPLY_PROP_CAPACITY: + case POWER_SUPPLY_PROP_CYCLE_COUNT: + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + rc = power_supply_get_property(chip->mmi_psy, psp, val); + break; + case POWER_SUPPLY_PROP_PRESENT: + rc = get_prop_batt_present(chip, val); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + rc = get_prop_batt_charge_type(chip, val); + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + rc = get_prop_input_suspend(chip, val); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + val->intval = get_client_vote(chip->fv_votable, BATT_PROFILE_VOTER); + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + rc = get_batt_current_now(chip, val); + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + val->intval = get_effective_result(chip->fcc_votable); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + val->intval = get_client_vote(chip->fcc_votable, BATT_PROFILE_VOTER); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + val->intval = get_effective_result(chip->fcc_votable); + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: + rc = get_prop_system_temp_level_max(chip, val); + break; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + rc = get_prop_system_temp_level(chip, val); + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + rc = power_supply_get_property(chip->bms_psy, psp, val); + break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + val->intval = mmi_discrete_get_batt_capacity_level(chip); + break; + default: + mmi_err(chip, "Get not support prop %d rc = %d\n", psp, rc); + rc = -EINVAL; + break; + } + + if (rc < 0) { + pr_debug("Couldn't get prop %d rc = %d\n", psp, rc); + return -ENODATA; + } + + return rc; +} + +static int batt_set_prop(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + int rc = 0; + struct mmi_discrete_charger *chip = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + rc = set_prop_system_temp_level(chip, val); + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + rc = set_prop_input_suspend(chip, val); + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + rc = power_supply_set_property(chip->mmi_psy, prop, val); + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + if (val->intval < 0) { + vote(chip->fcc_votable, USER_VOTER, false, 0); + } else + vote(chip->fcc_votable, USER_VOTER, true, val->intval); + break; + default: + mmi_dbg(chip, "Set not support prop %d rc = %d\n", prop, rc); + rc = -EINVAL; + break; + } + + return rc; +} + +static int batt_prop_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_CYCLE_COUNT: + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + case POWER_SUPPLY_PROP_CURRENT_MAX: + return 1; + default: + break; + } + + return 0; +} + +static void batt_external_power_changed(struct power_supply *psy) +{ + struct mmi_discrete_charger *chip = power_supply_get_drvdata(psy); + + cancel_delayed_work(&chip->charger_work); + schedule_delayed_work(&chip->charger_work, msecs_to_jiffies(0)); + + if (chip->batt_psy) + power_supply_changed(chip->batt_psy); +} + +static const struct power_supply_desc batt_psy_desc = { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = batt_get_prop, + .set_property = batt_set_prop, + .external_power_changed = batt_external_power_changed, + .property_is_writeable = batt_prop_is_writeable, + .properties = batt_props, + .num_properties = ARRAY_SIZE(batt_props), +}; + +/************************ + * USB PSY REGISTRATION * + ************************/ +static enum power_supply_property mmi_discrete_usb_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_TYPE, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, +}; + +static int mmi_discrete_usb_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mmi_discrete_charger *chip = power_supply_get_drvdata(psy); + int rc = 0; + + val->intval = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + /*when in COM, pd will trigger hard reset to let source send source_cap. + *but pd hard reset will closed vbus and re-enable vbus by adaptor, so usb + *present will return 0 to let COM shutdown. Typec mode is always on.*/ + if (chip->pd_supported && + (chip->typec_mode == MMI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT + || chip->typec_mode == MMI_POWER_SUPPLY_TYPEC_SOURCE_MEDIUM + || chip->typec_mode == MMI_POWER_SUPPLY_TYPEC_SOURCE_HIGH)) { + val->intval = 1; + break; + } + rc = get_prop_usb_present(chip, val); + break; + case POWER_SUPPLY_PROP_ONLINE: + rc = get_usb_online(chip, val); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + rc = get_prop_usb_voltage_now(chip, val); + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + rc = get_prop_usb_current_now(chip, val); + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + rc = mmi_discrete_get_hw_current_max(chip, &val->intval); + break; + case POWER_SUPPLY_PROP_TYPE: + val->intval = POWER_SUPPLY_TYPE_USB_PD; + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + /* USB uses this to set SDP current */ + val->intval = get_client_vote(chip->usb_icl_votable, + USB_PSY_VOTER); + break; + default: + pr_err("get prop %d is not supported in usb\n", psp); + rc = -EINVAL; + break; + } + + if (rc < 0) { + pr_debug("Couldn't get prop %d rc = %d\n", psp, rc); + return -ENODATA; + } + + return 0; +} + +static int mmi_discrete_usb_set_prop(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct mmi_discrete_charger *chip = power_supply_get_drvdata(psy); + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + rc = set_prop_sdp_current_max(chip, val->intval); + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + if (val->intval < 0) { + vote(chip->usb_icl_votable, USER_VOTER, false, 0); + } else + vote(chip->usb_icl_votable, USER_VOTER, true, val->intval); + break; + default: + pr_err("Set prop %d is not supported in usb psy\n", + psp); + rc = -EINVAL; + break; + } + + return rc; +} + +static int mmi_discrete_usb_prop_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_CURRENT_MAX: + return 1; + default: + break; + } + + return 0; +} + +static const struct power_supply_desc usb_psy_desc = { + .name = "usb", + .type = POWER_SUPPLY_TYPE_USB_PD, + .properties = mmi_discrete_usb_props, + .num_properties = ARRAY_SIZE(mmi_discrete_usb_props), + .get_property = mmi_discrete_usb_get_prop, + .set_property = mmi_discrete_usb_set_prop, + .property_is_writeable = mmi_discrete_usb_prop_is_writeable, +}; + +static int mmi_discrete_init_usb_psy(struct mmi_discrete_charger *chip) +{ + struct power_supply_config usb_cfg = {}; + + usb_cfg.drv_data = chip; + usb_cfg.of_node = chip->dev->of_node; + chip->usb_psy = devm_power_supply_register(chip->dev, + &usb_psy_desc, + &usb_cfg); + if (IS_ERR(chip->usb_psy)) { + mmi_err(chip, "Couldn't register USB power supply\n"); + return PTR_ERR(chip->usb_psy); + } + + return 0; +} + +/******************************** + * USB PC_PORT PSY REGISTRATION * + ********************************/ +static enum power_supply_property mmi_discrete_usb_port_props[] = { + POWER_SUPPLY_PROP_TYPE, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_CURRENT_MAX, +}; + +static int mmi_discrete_usb_port_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mmi_discrete_charger *chip = power_supply_get_drvdata(psy); + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_TYPE: + val->intval = POWER_SUPPLY_TYPE_USB; + break; + case POWER_SUPPLY_PROP_ONLINE: + rc = get_usb_port_online(chip, val); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + val->intval = 5000000; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + rc = get_prop_input_current_settled(chip, val); + break; + default: + mmi_err(chip, "Get prop %d is not supported in pc_port\n", + psp); + return -EINVAL; + } + + if (rc < 0) { + mmi_dbg(chip, "Couldn't get prop %d rc = %d\n", psp, rc); + return -ENODATA; + } + + return 0; +} + +static int mmi_discrete_usb_port_set_prop(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct mmi_discrete_charger *chip = power_supply_get_drvdata(psy); + int rc = 0; + + switch (psp) { + default: + mmi_err(chip, "Set prop %d is not supported in pc_port\n", + psp); + rc = -EINVAL; + break; + } + + return rc; +} + +static const struct power_supply_desc usb_port_psy_desc = { + .name = "pc_port", + .type = POWER_SUPPLY_TYPE_USB, + .properties = mmi_discrete_usb_port_props, + .num_properties = ARRAY_SIZE(mmi_discrete_usb_port_props), + .get_property = mmi_discrete_usb_port_get_prop, + .set_property = mmi_discrete_usb_port_set_prop, +}; + +static int mmi_discrete_init_usb_port_psy(struct mmi_discrete_charger *chip) +{ + struct power_supply_config usb_port_cfg = {}; + + usb_port_cfg.drv_data = chip; + usb_port_cfg.of_node = chip->dev->of_node; + chip->usb_port_psy = devm_power_supply_register(chip->dev, + &usb_port_psy_desc, + &usb_port_cfg); + if (IS_ERR(chip->usb_port_psy)) { + mmi_err(chip, "Couldn't register USB pc_port power supply\n"); + return PTR_ERR(chip->usb_port_psy); + } + + return 0; +} + +/************************* + * DC PSY REGISTRATION * + *************************/ + +static enum power_supply_property mmi_discrete_dc_props[] = { + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_VOLTAGE_MAX, +}; + +static int mmi_discrete_dc_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mmi_discrete_charger *chip = power_supply_get_drvdata(psy); + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + /* For DC, INPUT_CURRENT_LIMIT equates to INPUT_SUSPEND */ + val->intval = get_effective_result(chip->dc_suspend_votable); + break; + case POWER_SUPPLY_PROP_PRESENT: + rc = get_prop_dc_present(chip, val); + break; + case POWER_SUPPLY_PROP_ONLINE: + rc = get_prop_dc_online(chip, val); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + rc = get_prop_dc_voltage_now(chip, val); + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + rc = get_prop_dc_current_max(chip, val); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + rc = get_prop_dc_voltage_max(chip, val); + break; + default: + return -EINVAL; + } + if (rc < 0) { + mmi_dbg(chip,"Couldn't get prop %d rc = %d\n", psp, rc); + return -ENODATA; + } + return 0; +} + +static int mmi_discrete_dc_set_prop(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct mmi_discrete_charger *chip = power_supply_get_drvdata(psy); + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + rc = vote(chip->dc_suspend_votable, WBC_VOTER, + (bool)val->intval, 0); + case POWER_SUPPLY_PROP_CURRENT_MAX: + rc = set_prop_dc_current_max(chip, val); + break; + default: + return -EINVAL; + } + + return rc; +} + +static int mmi_discrete_dc_prop_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_CURRENT_MAX: + return 1; + default: + break; + } + + return 0; +} + +static const struct power_supply_desc dc_psy_desc = { + .name = "dc", + .type = POWER_SUPPLY_TYPE_WIRELESS, + .properties = mmi_discrete_dc_props, + .num_properties = ARRAY_SIZE(mmi_discrete_dc_props), + .get_property = mmi_discrete_dc_get_prop, + .set_property = mmi_discrete_dc_set_prop, + .property_is_writeable = mmi_discrete_dc_prop_is_writeable, +}; + +static int mmi_discrete_init_dc_psy(struct mmi_discrete_charger *chip) +{ + struct power_supply_config dc_cfg = {}; + + dc_cfg.drv_data = chip; + dc_cfg.of_node = chip->dev->of_node; + chip->dc_psy = devm_power_supply_register(chip->dev, + &dc_psy_desc, + &dc_cfg); + if (IS_ERR(chip->dc_psy)) { + mmi_err(chip, "Couldn't register dc power supply\n"); + return PTR_ERR(chip->dc_psy); + } + + return 0; +} + +/************************* + * USB COOLER START * + *************************/ +static int usb_therm_set_mosfet(struct mmi_discrete_charger *chip,bool enable) +{ + int ret = 0; + + /*set typec mosfet output*/ + if (gpio_is_valid(chip->mos_en_gpio)) { + mmi_err(chip, "%s,set mos en: %d.",__func__,enable); + if(enable){ + ret = charger_dev_enable_charging(chip->master_chg_dev,false); + ret = charger_dev_enable_hz(chip->master_chg_dev,true); + udelay(300); + if ((chip->bc1p2_charger_type != POWER_SUPPLY_TYPE_USB)&& + (chip->bc1p2_charger_type !=POWER_SUPPLY_TYPE_USB_CDP)) { + gpio_direction_output(chip->mos_en_gpio, enable); + mmi_err(chip, "%s,open mos en: %d %d",__func__,enable,ret); + } + } + else{ + gpio_direction_output(chip->mos_en_gpio, enable); + udelay(300); + ret = charger_dev_enable_charging(chip->master_chg_dev,true); + ret = charger_dev_enable_hz(chip->master_chg_dev,false); + mmi_err(chip, "%s,close mos en: %d %d",__func__,enable,ret); + } + } + + return ret; +} + +static int usb_therm_get_mosfet(struct mmi_discrete_charger *chip) +{ + int ret = 0; + + /*get typec mosfet output*/ + if (gpio_is_valid(chip->mos_en_gpio)) { +// mmi_err(chip, "%s,get mos en.",__func__); + return gpio_get_value(chip->mos_en_gpio); + } + + return ret; +} + + +static int usb_therm_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = 1; + + return 0; +} + +static int usb_therm_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct mmi_discrete_charger *chip = cdev->devdata; + + *state = usb_therm_get_mosfet(chip); + + return 0; +} + +static int usb_therm_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct mmi_discrete_charger *chip = cdev->devdata; + if (state) { + mmi_info(chip, "Enable typec mosfet."); + usb_therm_set_mosfet(chip, true); + } else { + mmi_info(chip, "Disable typec mosfet."); + usb_therm_set_mosfet(chip, false); + } + + return 0; +} + +static const struct thermal_cooling_device_ops usb_therm_ops = { + .get_max_state = usb_therm_get_max_state, + .get_cur_state = usb_therm_get_cur_state, + .set_cur_state = usb_therm_set_cur_state, +}; + +static int mmi_discrete_init_usb_therm_cooler(struct mmi_discrete_charger *chip) +{ + int ret; + /* Register thermal zone cooling device */ + chip->cdev = thermal_of_cooling_device_register(dev_of_node(chip->dev), + "usb_therm_cooler", chip, &usb_therm_ops); + + if (IS_ERR(chip->cdev)) { + mmi_err(chip, "Cooling register failed for usb_therm, ret:%ld\n", + PTR_ERR(chip->cdev)); + return PTR_ERR(chip->cdev); + } + mmi_info(chip, "Cooling register success for usb_therm."); + + /*typec mosfet outout en control*/ + chip->mos_en_gpio = of_get_named_gpio(chip->dev->of_node, "mmi,mos-en-gpio", 0); + if (gpio_is_valid(chip->mos_en_gpio)) + { + ret = gpio_request(chip->mos_en_gpio, "mmi mos en pin"); + if (ret) { + mmi_err(chip, "%s: %d gpio(mos en) request failed.", __func__, chip->mos_en_gpio); + return ret; + } + + gpio_direction_output(chip->mos_en_gpio, 0);//default enable mos charge + } + return 0; +} + +#if defined(CONFIG_DEBUG_FS) +static int register_dump_read(struct seq_file *m, void *data) +{ + int rc = 0; + struct mmi_discrete_charger *chip = m->private; + + rc = charger_dev_dump_registers(chip->master_chg_dev, m); + + if (rc) + mmi_err(chip, "Couldn't dump charger registers\n"); + + return rc; +} + +static int register_dump_debugfs_open(struct inode *inode, struct file *file) +{ + struct mmi_discrete_charger *chip = inode->i_private; + + return single_open(file, register_dump_read, chip); +} + +static const struct file_operations register_dump_debugfs_ops = { + .owner = THIS_MODULE, + .open = register_dump_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void mmi_discrete_create_debugfs(struct mmi_discrete_charger *chip) +{ + struct dentry *dfs_root, *file; + + dfs_root = debugfs_create_dir("charger_mmi", NULL); + if (IS_ERR_OR_NULL(dfs_root)) { + mmi_err(chip, "Couldn't create charger debugfs rc=%ld\n", + (long)dfs_root); + return; + } + + file = debugfs_create_file("register_dump", + S_IRUSR | S_IRGRP | S_IROTH, + dfs_root, chip, ®ister_dump_debugfs_ops); + if (IS_ERR_OR_NULL(file)) + mmi_err(chip, "Couldn't create register_dump file rc=%ld\n", + (long)file); +} +#else +static void mmi_discrete_create_debugfs(struct mmi_discrete_charger *chip) +{} +#endif + +static int chg_disable_vote_callback(struct votable *votable, + void *data, int chg_disable, const char *client) +{ + int rc = 0; + struct mmi_discrete_charger *chip = data; + + rc = charger_dev_enable_charging(chip->master_chg_dev, + chg_disable ? false : true); + if (rc) + mmi_err(chip, "Couldn't %s charging rc=%d\n", + chg_disable ? "disable" : "enable", rc); + + return rc; +} + +static int dc_suspend_vote_callback(struct votable *votable, + void *data, int dc_suspend, const char *client) +{ + //need to set dc suspend + return 0; +} + +static int fcc_vote_callback(struct votable *votable, void *data, + int total_fcc_ua, const char *client) +{ + int rc = 0; + struct mmi_discrete_charger *chip = data; + + rc = charger_dev_set_charging_current(chip->master_chg_dev, total_fcc_ua); + + if (rc) + mmi_err(chip, "Couldn't set charging current rc=%d\n", rc); + + return rc; +} + +static int fv_vote_callback(struct votable *votable, void *data, + int fv_uv, const char *client) +{ + int rc = 0; + struct mmi_discrete_charger *chip = data; + + rc = charger_dev_set_constant_voltage(chip->master_chg_dev, fv_uv); + + if (rc) + mmi_err(chip, "Couldn't set constant voltage rc=%d\n", rc); + + return rc; +} + +static int usb_icl_vote_callback(struct votable *votable, void *data, + int icl_ua, const char *client) +{ + int rc = 0; + struct mmi_discrete_charger *chip = data; + + rc = charger_dev_set_input_current(chip->master_chg_dev, icl_ua); + + if (rc) + mmi_err(chip, "Couldn't set input current rc=%d\n", rc); + + return rc; +} + + +int mmi_get_prop_typec_cc_orientation(struct mmi_discrete_charger *chg, + int *val) +{ + int rc = 0; + //need to get typec cc orientation + return rc; +} + +static void mmi_handle_hvdcp_check_timeout(struct mmi_discrete_charger *chg, + bool hvdcp_done) +{ + u32 hvdcp_ua = 0; + + if (chg->pd_active) + return; + + if (hvdcp_done) { + hvdcp_ua = (chg->real_charger_type == +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + QTI_POWER_SUPPLY_TYPE_USB_HVDCP) ? +#else + POWER_SUPPLY_TYPE_USB_HVDCP) ? +#endif + chg->hvdcp2_max_icl_ua : + HVDCP_CURRENT_UA; + + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + hvdcp_ua); + } + + mmi_dbg(chg, "notify: %s %s\n", __func__, + hvdcp_done ? "hvdcp done" : "no hvdcp"); +} + +static void mmi_notify_extcon_props(struct mmi_discrete_charger *chg, int id) +{ + union extcon_property_value val; + int prop_val; + + mmi_get_prop_typec_cc_orientation(chg, &prop_val); + val.intval = ((prop_val == 2) ? 1 : 0); + extcon_set_property(chg->extcon, id, + EXTCON_PROP_USB_TYPEC_POLARITY, val); + val.intval = true; + extcon_set_property(chg->extcon, id, + EXTCON_PROP_USB_SS, val); +} + +static void mmi_notify_device_mode(struct mmi_discrete_charger *chg, bool enable) +{ + if (enable) + mmi_notify_extcon_props(chg, EXTCON_USB); + + extcon_set_state_sync(chg->extcon, EXTCON_USB, enable); +} + +static void update_sw_icl_max(struct mmi_discrete_charger *chg) +{ + int rp_ua; + + /* while PD is active it should have complete ICL control */ + if (chg->pd_active) + return; + + if (chg->typec_mode == MMI_POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER) { + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, 500000); + return; + } + + /* + * HVDCP 2/3, handled separately + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + if (chg->real_charger_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP + || chg->real_charger_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3 + || chg->real_charger_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5) +#else + if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP + || chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3 + || chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3P5) +#endif + return; + + /* TypeC rp med or high, use rp value */ + if (typec_rp_med_high(chg, chg->typec_mode)) { + rp_ua = mmi_get_rp_based_dcp_current(chg, chg->typec_mode); + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, rp_ua); + vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); + return; + } + + /* rp-std or legacy, USB BC 1.2 */ + switch (chg->real_charger_type) { + case POWER_SUPPLY_TYPE_USB: + if (get_client_vote(chg->usb_icl_votable, + USB_PSY_VOTER) < SDP_CURRENT_UA) { + vote(chg->usb_icl_votable, USB_PSY_VOTER, true, + SDP_CURRENT_UA); + } + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0); + break; + case POWER_SUPPLY_TYPE_USB_CDP: + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + CDP_CURRENT_UA); + vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); + break; + case POWER_SUPPLY_TYPE_USB_DCP: + rp_ua = mmi_get_rp_based_dcp_current(chg, chg->typec_mode); + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, rp_ua); + vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0); + break; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + case QTI_POWER_SUPPLY_TYPE_USB_FLOAT: +#else + case POWER_SUPPLY_TYPE_USB_FLOAT: +#endif + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + SDP_CURRENT_UA); + break; + case POWER_SUPPLY_TYPE_WIRELESS: + cancel_delayed_work(&chg->wireless_icl_work); + schedule_delayed_work(&chg->wireless_icl_work, + msecs_to_jiffies(0)); + break; + case POWER_SUPPLY_TYPE_UNKNOWN: + default: + vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true, + SDP_100_MA); + break; + } +} + +static void mmi_handle_apsd_done(struct mmi_discrete_charger *chip, bool apsd_done) +{ + if (!apsd_done) + return; + + mmi_discrete_update_usb_type(chip); + + update_sw_icl_max(chip); + + switch (chip->real_charger_type) { + case POWER_SUPPLY_TYPE_USB: + case POWER_SUPPLY_TYPE_USB_CDP: + if (chip->use_extcon) + mmi_notify_device_mode(chip, true); + break; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + case QTI_POWER_SUPPLY_TYPE_USB_FLOAT: +#else + case POWER_SUPPLY_TYPE_USB_FLOAT: +#endif + case POWER_SUPPLY_TYPE_USB_DCP: + break; + default: + break; + } + + mmi_dbg(chip, "notify: apsd-done; %d detected\n", + chip->real_charger_type); +} + +int mmi_discrete_config_typec_mode(struct mmi_discrete_charger *chip, int val) +{ + mmi_info(chip, "config typec mode is %d old mode is %d\n", val, chip->typec_mode); + + if (val != chip->typec_mode) { + chip->typec_mode = val; + + /* + * Third charging ic IINLIM bits will be changed auto + * when BC1.2 done. So we need to Ignore the Rp + * changes unless BC1.2 done. + */ + if (chip->bc1p2_charger_type != POWER_SUPPLY_TYPE_UNKNOWN) + update_sw_icl_max(chip); + + if (chip->usb_psy) + power_supply_changed(chip->usb_psy); + } + + return 0; +} + +int mmi_discrete_get_typec_accessory_mode(struct mmi_discrete_charger *chip, int *val) +{ + mmi_info(chip, ": %d\n", chip->typec_mode); + + switch (chip->typec_mode) { + case MMI_POWER_SUPPLY_TYPEC_NONE: + *val = MMI_POWER_SUPPLY_TYPEC_ACCESSORY_NONE; + break; + case MMI_POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER: + *val = MMI_POWER_SUPPLY_TYPEC_ACCESSORY_AUDIO; + break; + case MMI_POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY: + *val = MMI_POWER_SUPPLY_TYPEC_ACCESSORY_DEBUG; + break; + default: + *val = -EINVAL; + } + return 0; +} + +int mmi_discrete_get_hw_current_max(struct mmi_discrete_charger *chip, int *total_current_ua) +{ + int current_ua = 0; + + if (chip->pd_active) { + *total_current_ua = + get_client_vote_locked(chip->usb_icl_votable, PD_VOTER); + return 0; + } + + /* QC 2.0/3.0 adapter */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + if (chip->real_charger_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP) { +#else + if (chip->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP) { +#endif + *total_current_ua = chip->hvdcp2_max_icl_ua; + return 0; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + } else if (chip->real_charger_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3 + || chip->real_charger_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5) { +#else + } else if (chip->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3 + || chip->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3P5) { +#endif + *total_current_ua = HVDCP_CURRENT_UA; + return 0; + } + + switch (chip->typec_mode) { + case MMI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT: + switch (chip->real_charger_type) { + case POWER_SUPPLY_TYPE_USB_CDP: + current_ua = CDP_CURRENT_UA; + break; + case POWER_SUPPLY_TYPE_USB_DCP: + current_ua = DCP_CURRENT_UA; + break; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + case QTI_POWER_SUPPLY_TYPE_USB_FLOAT: +#else + case POWER_SUPPLY_TYPE_USB_FLOAT: +#endif + case POWER_SUPPLY_TYPE_USB: + current_ua = SDP_CURRENT_UA; + break; + case POWER_SUPPLY_TYPE_WIRELESS: + current_ua = chip->wls_max_icl_ua; + break; + default: + current_ua = 0; + break; + } + break; + case MMI_POWER_SUPPLY_TYPEC_SOURCE_MEDIUM: + current_ua = TYPEC_MEDIUM_CURRENT_UA; + break; + case MMI_POWER_SUPPLY_TYPEC_SOURCE_HIGH: + current_ua = TYPEC_HIGH_CURRENT_UA; + break; + case MMI_POWER_SUPPLY_TYPEC_NON_COMPLIANT: + case MMI_POWER_SUPPLY_TYPEC_NONE: + default: + current_ua = 0; + break; + } + + *total_current_ua = current_ua; + + mmi_dbg(chip, "get input current max :%d\n", + *total_current_ua); + return 0; +} + +int mmi_discrete_config_charging_enabled(struct mmi_discrete_charger *chip, int val) +{ + int rc = 0; + + mmi_dbg(chip, "mmi_discrete_config_charging_enabled val:%d\n",val); + + rc = charger_dev_enable_charging(chip->master_chg_dev, + val ? true:false); + if (rc) + mmi_err(chip, "Couldn't %s charging rc=%d\n", + val ? "enable":"disable", rc); + + return rc; +} + +int mmi_discrete_config_termination_enabled(struct mmi_discrete_charger *chip, int val) +{ + int rc = 0; + + mmi_dbg(chip, "mmi_discrete_config_termination_enabled val:%d\n",val); + + rc = charger_dev_enable_termination(chip->master_chg_dev, + val ? true:false); + if (rc) + mmi_err(chip, "Couldn't %s termination rc=%d\n", + val ? "enable":"disable", rc); + + return rc; +} + +int mmi_discrete_get_charging_enabled(struct mmi_discrete_charger *chip, bool *val) +{ + int rc = 0; + + rc = charger_dev_is_enabled_charging(chip->master_chg_dev, val); + if (rc) + mmi_err(chip, "Couldn't get charging status rc=%d\n", rc); + + mmi_dbg(chip, "mmi_discrete_get_charging_enabled val:%d\n",*val); + + return rc; +} + +int mmi_discrete_get_charger_suspend(struct mmi_discrete_charger *chip, bool *val) +{ + int i; + bool charger_suspend = false; + struct mmi_charger_cfg *cfg; + + if (!chip->chg_clients || !chip->chg_client_num) { + mmi_err(chip, "Invalid charger client\n"); + return -1; + } + + for (i = 0; i < chip->chg_client_num; i++) { + cfg = &chip->chg_clients[i].chg_cfg; + if (cfg->charger_suspend) { + charger_suspend = true; + break; + } + } + + *val = charger_suspend; + mmi_dbg(chip, "mmi_discrete_get_charger_suspend val:%d\n",*val); + + return 0; +} + +int mmi_discrete_get_qc3p_power(struct mmi_discrete_charger *chip, int *val) +{ + int rc = 0; + + rc = charger_dev_get_qc3p_power(chip->master_chg_dev, val); + if (rc) + mmi_err(chip, "Couldn't get qc3p power rc=%d\n", rc); + + mmi_dbg(chip, "mmi_discrete_get_qc3p_power val:%d\n",*val); + + return rc; +} + +int mmi_discrete_get_pulse_cnt(struct mmi_discrete_charger *chip, int *count) +{ + int rc = 0; + + rc = charger_dev_get_pulse_cnt(chip->master_chg_dev, count); + if (rc) + mmi_err(chip, "Couldn't get pulse cnt rc=%d\n", rc); + + mmi_dbg(chip, "mmi_discrete_get_pulse_cnt count:%d\n",*count); + + return rc; +} + +int mmi_discrete_set_dp_dm(struct mmi_discrete_charger *chip, int val) +{ + int rc = 0; + + rc = charger_dev_set_dp_dm(chip->master_chg_dev, val); + if (rc) + mmi_err(chip, "Couldn't set dp dm rc=%d\n", rc); + + mmi_dbg(chip, "mmi_discrete_set_dp_dm val:%d\n",val); + + return rc; +} + +int mmi_discrete_config_input_current_settled(struct mmi_discrete_charger *chip, int val) +{ + + update_sw_icl_max(chip); + + mmi_dbg(chip, "mmi_discrete_config_input_current_settled val:%d\n",val); + + vote(chip->usb_icl_votable, QC3P_VOTER, true, val*1000); + if (chip->usb_psy) + power_supply_changed(chip->usb_psy); + return 0; +} + +#define MMI_USBIN_500MA 500000 +int mmi_discrete_config_pd_active(struct mmi_discrete_charger *chip, int val) +{ + mmi_info(chip, "config pd active is %d old mode is %d\n", val, chip->pd_active); + + if (chip->pd_active && chip->pd_active == val) + return 0; + + chip->pd_active = val; + + charger_dev_config_pd_active(chip->master_chg_dev, chip->pd_active); + update_sw_icl_max(chip); + + if (chip->pd_active) { + vote(chip->usb_icl_votable, PD_VOTER, true, MMI_USBIN_500MA); + vote(chip->usb_icl_votable, USB_PSY_VOTER, false, 0); + vote(chip->usb_icl_votable, SW_ICL_MAX_VOTER, false, 0); + } else { + vote(chip->usb_icl_votable, PD_VOTER, false, 0); + + } + + mmi_discrete_update_usb_type(chip); + + if (chip->usb_psy) + power_supply_changed(chip->usb_psy); + + return 0; +} + +int mmi_charger_pd_vdm_verify(struct mmi_discrete_charger *chip, int val) +{ + mmi_info(chip, "config pd_vdm_verify is %d old mode is %d\n", val, chip->pd_vdm_verify); + + if (chip->pd_vdm_verify && chip->pd_vdm_verify == val) + return 0; + + chip->pd_vdm_verify = val; + + if (chip->usb_psy && chip->pd_vdm_verify) + power_supply_changed(chip->usb_psy); + + return 0; +} + +/************************* + * DCP FFC START * + *************************/ +static char *fcc_state_str[] = { + "STATE_INITIAL", "STATE_PROBING", "STATE_STANDBY", "STATE_GOREADY", "STATE_RUNNING", "STATE_AVGEXIT", "STATE_FFCDONE", "STATE_INVALID" +}; + +enum mmi_chrg_step { + STEP_MAX, + STEP_NORM, + STEP_FULL, + STEP_FLOAT, + STEP_DEMO, + STEP_STOP, + STEP_NONE, +}; + +static void mmi_charger_ffc_init(struct mmi_discrete_charger *chip) +{ + chip->ffc_entry_threshold = 1900; + chip->ffc_exit_threshold = 1800; + chip->ffc_ibat_windowsum = 0; + chip->ffc_ibat_count = 0; + chip->ffc_ibat_windowsize = 6; + chip->ffc_iavg = 0; + chip->ffc_iavg_update_timestamp = 0; + chip->ffc_uisoc_threshold = 75; + chip->ffc_stop_chrg = false; + chip->ffc_state = CHARGER_FFC_STATE_INITIAL; + chip->is_ffc_enable = false; + mmi_info(chip,"ffc initilaize...\n"); +} + +static int mmi_charger_check_dcp_ffc_status(struct mmi_discrete_charger *chip, int batt_soc, int vbatt_mv, int ibatt_ma) +{ + bool loop = false; + int vbatt, vcurr, chrg_step, batt_mv,batt_ma; + unsigned long target_timestamp; + + batt_ma = ibatt_ma * (-1); + batt_mv = vbatt_mv; + chrg_step = chip->chrg_step; + + if(chrg_step != STEP_MAX && chrg_step != STEP_NORM) + return 0; + do { + mmi_info(chip,"ffc_state:%s, StepChg:%d, uisoc:%d, chg_type:%d batt_mv:%d batt_ma:%d\n", fcc_state_str[chip->ffc_state], chrg_step, batt_soc, chip->chg_info.chrg_type, batt_mv, batt_ma); + switch (chip->ffc_state) { + case CHARGER_FFC_STATE_INITIAL: + if ((chrg_step == STEP_MAX) && + (chip->chg_info.chrg_type == POWER_SUPPLY_TYPE_USB_DCP || +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + chip->chg_info.chrg_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3) && +#else + chip->chg_info.chrg_type == POWER_SUPPLY_TYPE_USB_HVDCP_3) && +#endif + (chip->chg_info.chrg_pmax_mw <= 15000)) { + if (batt_soc >= chip->ffc_uisoc_threshold) { + chip->ffc_state = CHARGER_FFC_STATE_PROBING; + mmi_info(chip,"uisoc is up to %d, ffc probing starts\n", chip->ffc_uisoc_threshold); + } + } + else { + if (POWER_SUPPLY_TYPE_UNKNOWN == chip->chg_info.chrg_type) { + mmi_info(chip,"chrg_type is unknown"); + } else + chip->ffc_state = CHARGER_FFC_STATE_INVALID; + mmi_err(chip,"ui_soc:%d, charge_type:%d not for ffc\n", batt_soc, chip->chg_info.chrg_type); + } + loop = false; + break; + case CHARGER_FFC_STATE_PROBING: + if (chrg_step == STEP_MAX || chrg_step == STEP_NORM) { + if (batt_ma < 0) { + mmi_err(chip,"still in discharging at:%d\n", batt_ma); + loop = false; + break; + } + + if (batt_ma > chip->ffc_entry_threshold) { + mmi_err(chip,"charging current can bump up to entry threshold:%d\n", chip->ffc_entry_threshold); + chip->ffc_state = CHARGER_FFC_STATE_STANDBY; + } + else if (batt_ma == 0) { + mmi_err(chip,"charger doesnt support to bump high level current\n"); + chip->ffc_state = CHARGER_FFC_STATE_INVALID; + } + else { + mmi_info(chip,"current: %d not reach ffc entry threshold\n", batt_ma); + loop = false; + } + } + else { + chip->ffc_state = CHARGER_FFC_STATE_INVALID; + mmi_info(chip,"invalid stage:%d, ffc session quit\n", chrg_step); + } + break; + case CHARGER_FFC_STATE_STANDBY: + if (chrg_step != STEP_MAX && chrg_step != STEP_NORM) { + mmi_info(chip,"invalid stage:%d in ffc_state:%d\n", chrg_step, chip->ffc_state); + chip->ffc_state = CHARGER_FFC_STATE_INVALID; + break; + } + + if ((chrg_step == STEP_NORM) && (chip->ffc_iavg >= chip->ffc_entry_threshold)) { + chip->ffc_state = CHARGER_FFC_STATE_GOREADY; + break; + } + + target_timestamp = chip->ffc_iavg_update_timestamp + msecs_to_jiffies(10 * 1000); + + if (time_is_before_eq_jiffies(target_timestamp) || chip->ffc_iavg_update_timestamp == 0) { + /* iavg update required */ + if (batt_ma < 0) { + loop = false; + mmi_err(chip,"still in discharging at:%d\n", batt_ma); + break; + } + + chip->ffc_iavg_update_timestamp = jiffies; + chip->ffc_ibat_count++; + chip->ffc_ibat_windowsum += batt_ma; + chip->ffc_iavg = chip->ffc_ibat_windowsum / chip->ffc_ibat_count; + mmi_info(chip,"iavg:%d, total:%lu, count:%lu\n", chip->ffc_iavg, chip->ffc_ibat_windowsum, chip->ffc_ibat_count); + } + + loop = false; + break; + case CHARGER_FFC_STATE_GOREADY: + if (chrg_step != STEP_NORM) { + if (chrg_step == STEP_MAX) { + chip->ffc_state = CHARGER_FFC_STATE_STANDBY; + } else { + mmi_err(chip,"invalid stage:%d in ffc_state:%d, quit ffc session\n", chrg_step, chip->ffc_state); + chip->ffc_state = CHARGER_FFC_STATE_INVALID; + } + break; + } + chip->ffc_ibat_count = 0; + chip->ffc_ibat_windowsum = 0; + chip->ffc_iavg = 0; + chip->ffc_iavg_update_timestamp = 0; + /* set target voltage as the ffc target */ + chip->is_ffc_enable = true; + chip->ffc_state = CHARGER_FFC_STATE_RUNNING; + loop = false; + mmi_info(chip,"target max_fv_mv:%d\n", chip->max_fv_mv); + break; + case CHARGER_FFC_STATE_RUNNING: + if (chrg_step != STEP_NORM) { + if (chrg_step == STEP_MAX) { + chip->ffc_state = CHARGER_FFC_STATE_STANDBY; + } else { + mmi_err(chip,"invalid stage:%d in ffc_state:%d, quit ffc session\n", chrg_step, chip->ffc_state); + chip->ffc_state = CHARGER_FFC_STATE_INVALID; + } + break; + } + + if (batt_ma < 0) { + mmi_err(chip,"still in discharging at:%d, retry\n", batt_ma); + loop = false; + break; + } + + vcurr = batt_ma; + vbatt = batt_mv; + chip->is_ffc_enable = true; + /* float down offset 30mV to fit in more robust ffc state */ + if (vbatt < (chip->max_fv_mv - 30)) { + chip->ffc_ibat_count++; + chip->ffc_ibat_windowsum += vcurr; + loop = false; + + if ((chip->ffc_ibat_count % chip->ffc_ibat_windowsize) == 0) { + chip->ffc_iavg = chip->ffc_ibat_windowsum / chip->ffc_ibat_count; + if (chip->ffc_iavg <= chip->ffc_exit_threshold) { + loop = true; + chip->ffc_state = CHARGER_FFC_STATE_AVGEXIT; + } + mmi_info(chip,"ffc_iavg:%d in ffc, max_fv_mv:%d, vbatt:%d,ffc_exit_threshold:%d\n", chip->ffc_iavg, chip->max_fv_mv, vbatt, + chip->ffc_exit_threshold); + } + } + else { + mmi_err(chip,"ffc charging to target voltage:%d, quit ffc session \n", chip->max_fv_mv); + chip->ffc_state = CHARGER_FFC_STATE_FFCDONE; + break; + } + break; + case CHARGER_FFC_STATE_AVGEXIT: + chip->is_ffc_enable = false; + chip->ffc_ibat_count = 0; + chip->ffc_ibat_windowsum = 0; + chip->ffc_iavg = 0; + chip->ffc_state = CHARGER_FFC_STATE_INVALID; + if (batt_mv > chip->dcp_noneffc_cv) + chip->ffc_stop_chrg = true; + loop = false; + break; + case CHARGER_FFC_STATE_FFCDONE: + chip->is_ffc_enable = true; + mmi_info(chip,"ffc session done, max_fv_mv:%d\n", chip->max_fv_mv); + loop = false; + break; + case CHARGER_FFC_STATE_INVALID: + chip->is_ffc_enable = false; + if (chip->ffc_stop_chrg && batt_mv <= chip->dcp_noneffc_cv) + chip->ffc_stop_chrg = false; + loop = false; + break; + } + } while (loop); + + return 0; +} + +static void mmi_discrete_monitor_10w_ffc(struct mmi_discrete_chg_client *chg, struct mmi_discrete_charger *chip) +{ + mmi_dbg(chip, "MMI Discrete monitor_10w_ffc!\n"); + + chip->max_fv_mv = get_effective_result(chip->fv_votable)/1000; + if (chip->chg_info.chrg_present) { + mmi_charger_check_dcp_ffc_status(chip, chg->batt_info.batt_soc, chg->batt_info.batt_mv, chg->batt_info.batt_ma); + } else { + mmi_charger_ffc_init(chip); + } +} + +bool mmi_discrete_is_ffc_enabled(void *data, int chrg_step) +{ + struct mmi_discrete_chg_client *chg = data; + struct mmi_discrete_charger *chip = chg->chip; + + chip->chrg_step = chrg_step; + mmi_discrete_monitor_10w_ffc(chg, chip); + + return chip->is_ffc_enable; +} +/************************* + * DCP FFC END * + *************************/ + +static int mmi_discrete_usb_plugout(struct mmi_discrete_charger * chip) +{ + chip->real_charger_type = POWER_SUPPLY_TYPE_UNKNOWN; + chip->bc1p2_charger_type = POWER_SUPPLY_TYPE_UNKNOWN; + chip->pd_active = MMI_POWER_SUPPLY_PD_INACTIVE; + chip->pd_vdm_verify = false; + if (chip->use_extcon) + mmi_notify_device_mode(chip, false); + vote(chip->usb_icl_votable, SW_ICL_MAX_VOTER, true, SDP_100_MA); + vote(chip->usb_icl_votable, USB_PSY_VOTER, false, 0); + vote(chip->usb_icl_votable, PD_VOTER, false, 0); + vote(chip->usb_icl_votable, QC3P_VOTER, false, 0); + vote(chip->chg_disable_votable, MMI_HB_VOTER, true, 0); + power_supply_changed(chip->usb_psy); + mmi_info(chip, "%s\n",__func__); + return 0; +} + +static int usb_source_change_notify_handler(struct notifier_block *nb, unsigned long event, + void *v) +{ + struct mmi_discrete_charger *chip = + container_of(nb, struct mmi_discrete_charger, master_chg_nb); + struct chgdev_notify *data = v; + union power_supply_propval pval; + int rc = 0; + + rc = get_prop_usb_present(chip, &pval); + if (rc < 0) { + mmi_err(chip, "Couldn't get usb present rc = %d\n", rc); + return NOTIFY_DONE; + } + + if (!pval.intval) { + mmi_discrete_usb_plugout(chip); + return NOTIFY_DONE; + } + + mmi_handle_apsd_done(chip, data->apsd_done); + + mmi_handle_hvdcp_check_timeout(chip, data->hvdcp_done); + + power_supply_changed(chip->usb_psy); + + mmi_dbg(chip, "apsd done %d, hvdcp done %d\n", + data->apsd_done, data->hvdcp_done); + return NOTIFY_DONE; +} + +static int mmi_discrete_get_batt_info(void *data, struct mmi_battery_info *batt_info) +{ + int rc; + union power_supply_propval pval; + struct mmi_discrete_chg_client *chg = data; + struct mmi_discrete_charger *chip = chg->chip; + struct power_supply *client_batt_psy; + + if (!chip->charger_psy) + return -EINVAL; + + rc = power_supply_get_property(chip->charger_psy, + POWER_SUPPLY_PROP_STATUS, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Status rc = %d\n", rc); + } else if (chg->chg_cfg.full_charged) + chg->batt_info.batt_status = POWER_SUPPLY_STATUS_FULL; + else + chg->batt_info.batt_status = pval.intval; + + if (chg->client_batt_psy == chip->batt_psy) + client_batt_psy = chip->bms_psy; + else + client_batt_psy = chg->client_batt_psy; + + rc = power_supply_get_property(client_batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Voltage rc = %d\n", rc); + } else + chg->batt_info.batt_mv = pval.intval / 1000; + + rc = power_supply_get_property(client_batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Current rc = %d\n", rc); + } else + chg->batt_info.batt_ma = pval.intval / 1000; + + rc = power_supply_get_property(client_batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Capacity rc = %d\n", rc); + } else + chg->batt_info.batt_soc = pval.intval; + + rc = power_supply_get_property(client_batt_psy, + POWER_SUPPLY_PROP_TEMP, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Temperature rc = %d\n", rc); + } else + chg->batt_info.batt_temp = pval.intval / 10; + + rc = power_supply_get_property(client_batt_psy, + POWER_SUPPLY_PROP_CHARGE_FULL, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt charge_full rc = %d\n", rc); + } else + chg->batt_info.batt_full_uah = pval.intval; + + rc = power_supply_get_property(client_batt_psy, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt charge_full_design rc = %d\n", rc); + } else + chg->batt_info.batt_design_uah = pval.intval; + + memcpy(batt_info, &chg->batt_info, sizeof(struct mmi_battery_info)); + + return rc; +} + +static int mmi_discrete_get_chg_info(void *data, struct mmi_charger_info *chg_info) +{ + int rc; + int usb_type; + int usb_icl; + union power_supply_propval val; + struct mmi_discrete_chg_client *chg = data; + struct mmi_discrete_charger *chip = chg->chip; + + /* USB charger */ + val.intval = 0; + rc = get_prop_usb_present(chip, &val); + if (rc < 0) + mmi_err(chip, "Couldn't read usb present rc=%d\n", rc); + else if (val.intval) { + chip->chg_info.chrg_present = 1; + val.intval = 0; + rc = get_prop_usb_voltage_now(chip, &val); + if (rc < 0) + mmi_err(chip, "Couldn't read usb voltage rc=%d\n", rc); + chip->chg_info.chrg_mv = val.intval / 1000; + val.intval = 0; + rc = get_prop_usb_current_now(chip, &val); + if (rc < 0) + mmi_err(chip, "Couldn't read usb current rc=%d\n", rc); + chip->chg_info.chrg_ma = val.intval / 1000; + + chip->chg_info.chrg_type = chip->real_charger_type; + + usb_type = chip->chg_info.chrg_type; + if (usb_type == POWER_SUPPLY_TYPE_USB) + chip->chg_info.chrg_pmax_mw = 2500; + else if (usb_type == POWER_SUPPLY_TYPE_USB_CDP) + chip->chg_info.chrg_pmax_mw = 7500; + else if (usb_type == POWER_SUPPLY_TYPE_USB_DCP) + chip->chg_info.chrg_pmax_mw = chip->constraint.dcp_pmax; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + else if (usb_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP) +#else + else if (usb_type == POWER_SUPPLY_TYPE_USB_HVDCP) +#endif + chip->chg_info.chrg_pmax_mw = 7500; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + else if (usb_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3) +#else + else if (usb_type == POWER_SUPPLY_TYPE_USB_HVDCP_3) +#endif + chip->chg_info.chrg_pmax_mw = chip->constraint.hvdcp_pmax; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + else if (usb_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5) +#else + else if (usb_type == POWER_SUPPLY_TYPE_USB_HVDCP_3P5) +#endif + chip->chg_info.chrg_pmax_mw = 30000; + else if (usb_type == POWER_SUPPLY_TYPE_USB_PD) { + if (chip->pd_active == MMI_POWER_SUPPLY_PD_PPS_ACTIVE) + chip->chg_info.chrg_pmax_mw = 30000; + else + chip->chg_info.chrg_pmax_mw = chip->constraint.pd_pmax; + } else if (usb_type == POWER_SUPPLY_TYPE_WIRELESS) + chip->chg_info.chrg_pmax_mw = chip->constraint.wls_pmax; + else + chip->chg_info.chrg_pmax_mw = 2500; + + usb_icl = get_effective_result(chip->usb_icl_votable); + if (chip->chg_info.chrg_pmax_mw < (usb_icl * 5 / 1000)) + chip->chg_info.chrg_pmax_mw = usb_icl * 5 / 1000; + + if(!typec_rp_med_high(chip, chip->typec_mode) + && (usb_type == POWER_SUPPLY_TYPE_USB_DCP) + && (chip->chg_info.chrg_pmax_mw > 12500)) { + chip->chg_info.chrg_pmax_mw = 12000; //Show normal charge rate for default DCP + } + rc = 0; + goto completed; + } + + /* DC charger */ + val.intval = 0; + rc = get_prop_dc_present(chip, &val); + if (rc < 0) + mmi_err(chip, "Couldn't read dc present rc=%d\n", rc); + else if (val.intval) { + chip->chg_info.chrg_present = 1; + val.intval = 0; + rc = get_prop_dc_voltage_now(chip, &val); + if (rc < 0) + mmi_err(chip, "Couldn't read dc voltage rc=%d\n", rc); + chip->chg_info.chrg_mv = val.intval / 1000; + val.intval = 0; + rc = get_prop_dc_current_now(chip, &val); + if (rc < 0) + mmi_err(chip, "Couldn't read dc current rc=%d\n", rc); + chip->chg_info.chrg_ma = val.intval / 1000; + + chip->chg_info.chrg_type = POWER_SUPPLY_TYPE_WIRELESS; + chip->chg_info.chrg_pmax_mw = chip->constraint.wls_pmax; + + rc = 0; + goto completed; + } + + /* Wireless charger */ + if (is_wls_online(chip)) { + chip->real_charger_type = POWER_SUPPLY_TYPE_WIRELESS; + chip->chg_info.chrg_present = 1; + val.intval = 0; + rc = get_prop_dc_voltage_now(chip, &val); + if (rc < 0) + mmi_err(chip, "Couldn't read wls voltage rc=%d\n", rc); + chip->chg_info.chrg_mv = val.intval / 1000; + val.intval = 0; + rc = get_prop_dc_current_now(chip, &val); + if (rc < 0) + mmi_err(chip, "Couldn't read wls current rc=%d\n", rc); + chip->chg_info.chrg_ma = val.intval / 1000; + + chip->chg_info.chrg_type = POWER_SUPPLY_TYPE_WIRELESS; + chip->chg_info.chrg_pmax_mw = chip->constraint.wls_pmax; + + rc = 0; + goto completed; + } + + chip->chg_info.chrg_mv = 0; + chip->chg_info.chrg_ma = 0; + chip->chg_info.chrg_type = 0; + chip->chg_info.chrg_pmax_mw = 0; + chip->chg_info.chrg_present = 0; + +completed: + memcpy(chg_info, &chip->chg_info, sizeof(struct mmi_charger_info)); + + return rc; +} + +static int mmi_discrete_config_charge(void *data, struct mmi_charger_cfg *config) +{ + struct mmi_discrete_chg_client *chg = data; + + memcpy(&chg->chg_cfg, config, sizeof(struct mmi_charger_cfg)); + cancel_delayed_work(&chg->chip->charger_work); + schedule_delayed_work(&chg->chip->charger_work, + msecs_to_jiffies(0)); + + return 0; +} + +#define TAPER_COUNT 2 +#define TAPER_DROP_MA 100 +static bool mmi_discrete_has_current_tapered(void *data, int taper_ma) +{ + bool change_state = false; + int allowed_fcc, target_ma; + int batt_ma; + struct mmi_discrete_chg_client *chg = data; + + if (!chg) { + mmi_err(chg->chip, "Charger chip is unavailable!\n"); + return false; + } + + allowed_fcc = get_effective_result(chg->chip->fcc_votable) / 1000; + + if (allowed_fcc >= taper_ma) + target_ma = taper_ma; + else + target_ma = allowed_fcc - TAPER_DROP_MA; + + batt_ma = chg->batt_info.batt_ma; + if (batt_ma < 0) { + batt_ma *= -1; + if (batt_ma <= target_ma) + if (chg->chrg_taper_cnt >= TAPER_COUNT) { + change_state = true; + chg->chrg_taper_cnt = 0; + } else + chg->chrg_taper_cnt++; + else + chg->chrg_taper_cnt = 0; + } else { + if (chg->chrg_taper_cnt >= TAPER_COUNT) { + change_state = true; + chg->chrg_taper_cnt = 0; + } else + chg->chrg_taper_cnt++; + } + + return change_state; +} + +static bool mmi_discrete_charge_halted(void *data) +{ + bool flag = false; + int rc = 0; + struct mmi_discrete_chg_client *chg = data; + struct mmi_discrete_charger *chip = chg->chip; + + rc = charger_dev_is_charge_halted(chip->master_chg_dev, &flag); + + if (rc) + mmi_err(chip, "Couldn't get if charge halted rc =%d\n",rc); + + return flag; +} + +static void mmi_discrete_set_constraint(void *data, + struct mmi_charger_constraint *constraint) +{ + struct mmi_discrete_chg_client *chg = data; + + if (memcmp(constraint, &chg->chip->constraint, + sizeof(struct mmi_charger_constraint))) { + memcpy(&chg->chip->constraint, constraint, + sizeof(struct mmi_charger_constraint)); + cancel_delayed_work(&chg->chip->charger_work); + schedule_delayed_work(&chg->chip->charger_work, + msecs_to_jiffies(0)); + } +} + +static int mmi_discrete_charger_init(struct mmi_discrete_charger *chip) +{ + int rc; + int i; + int count; + const char *str = NULL; + struct power_supply *batt_psy; + struct mmi_charger_driver *driver; + struct mmi_discrete_chg_client *client; + struct device_node *np = chip->dev->of_node; + const char *batt_sn = NULL; + const char *df_sn = NULL; + + chip->constraint.factory_mode = mmi_is_factory_mode(); + + count = of_property_count_strings(np, "battery-names"); + if (count <= 0) { + mmi_err(chip, "Invalid battery-names in dt, count=%d\n", count); + return -EINVAL; + } + + client = devm_kzalloc(chip->dev, + sizeof(struct mmi_discrete_chg_client) * count, + GFP_KERNEL); + driver = devm_kzalloc(chip->dev, + sizeof(struct mmi_charger_driver) * count, + GFP_KERNEL); + if (!client || !driver) { + rc = -ENOMEM; + goto free_mem; + } + + batt_sn = mmi_get_battery_serialnumber(); + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(np, "battery-names", i, &str); + if (rc) + break; + batt_psy = power_supply_get_by_name(str); + if (!batt_psy) { + rc = -ENODEV; + break; + } + + client[i].chip = chip; + client[i].driver = &driver[i]; + client[i].client_batt_psy = batt_psy; + + driver[i].name = str; + driver[i].dev = batt_psy->dev.parent; + driver[i].data = &client[i]; + driver[i].get_batt_info = mmi_discrete_get_batt_info; + driver[i].get_chg_info = mmi_discrete_get_chg_info; + driver[i].config_charge = mmi_discrete_config_charge; + driver[i].is_charge_tapered = mmi_discrete_has_current_tapered; + driver[i].is_charge_halt = mmi_discrete_charge_halted; + driver[i].set_constraint = mmi_discrete_set_constraint; + if (chip->enable_10w_ffc) { + driver[i].is_ffc_enabled = mmi_discrete_is_ffc_enabled; + } + + if (!batt_sn) { + df_sn = "unknown-sn"; + of_property_read_string(driver[i].dev->of_node, + "mmi,df-serialnum", &df_sn); + strcpy(client[i].batt_info.batt_sn, df_sn); + } else { + strcpy(client[i].batt_info.batt_sn, batt_sn); + } + mmi_info(chip, "%s Serial Number is %s\n", + str, client[i].batt_info.batt_sn); + + rc = mmi_register_charger_driver(&driver[i]); + if (rc) + break; + + power_supply_changed(client[i].client_batt_psy); + mmi_info(chip, "charger driver on %s init successfully\n", + driver[i].name); + } + + chip->chg_client_num = i; + if (!chip->chg_client_num) + goto free_mem; + + chip->chg_clients = client; + + if (chip->master_chg_dev) { + chip->master_chg_nb.notifier_call = usb_source_change_notify_handler; + register_charger_device_notifier(chip->master_chg_dev, + &chip->master_chg_nb); + } else { + goto free_mem; + } + + if (chip->constraint.factory_mode) { + mmi_info(chip, "Entering Factory Mode !\n"); + + if(chip->chg_disable_votable) { + pmic_vote_force_val_set(chip->chg_disable_votable, 1); + pmic_vote_force_active_set(chip->chg_disable_votable, 1); + } + if(chip->fcc_votable) { + pmic_vote_force_val_set(chip->fcc_votable, 3000000); + pmic_vote_force_active_set(chip->fcc_votable, 1); + } + if(chip->fv_votable) { + pmic_vote_force_val_set(chip->fv_votable, 4400000); + pmic_vote_force_active_set(chip->fv_votable, 1); + } + if (chip->usb_icl_votable) { + pmic_vote_force_val_set(chip->usb_icl_votable, 3000000); + pmic_vote_force_active_set(chip->usb_icl_votable, 1); + } + + } + + /*export charging operation interface for AUTHEN version*/ + mmi_discrete_create_factory_testcase(chip); + + return 0; +free_mem: + if (client) + devm_kfree(chip->dev, client); + if (driver) + devm_kfree(chip->dev, driver); + chip->chg_client_num = 0; + chip->chg_clients = NULL; + + return rc; +} + +static void mmi_discrete_charger_deinit(struct mmi_discrete_charger *chip) +{ + int i; + + for (i = 0; i < chip->chg_client_num; i++) { + mmi_unregister_charger_driver(chip->chg_clients[i].driver); + power_supply_put(chip->chg_clients[i].client_batt_psy); + } + + if (chip->chg_client_num) { + devm_kfree(chip->dev, chip->chg_clients->driver); + devm_kfree(chip->dev, chip->chg_clients); + } +} + +static int mmi_discrete_check_battery_supplies(struct mmi_discrete_charger *chip) +{ + int i; + int rc = 0; + int count; + + if (chip->batt_psy->num_supplies && chip->batt_psy->supplied_from) + goto print_supplies; + + count = of_property_count_strings(chip->dev->of_node, "supplied-from"); + if (count <= 0) { + mmi_err(chip, "Invalid supplied-from in DT, rc=%d\n", count); + return -EINVAL; + } + + chip->batt_psy->supplied_from = devm_kmalloc_array(&chip->batt_psy->dev, + count, + sizeof(char *), + GFP_KERNEL); + if (!chip->batt_psy->supplied_from) + return -ENOMEM; + + rc = of_property_read_string_array(chip->dev->of_node, "supplied-from", + (const char **)chip->batt_psy->supplied_from, + count); + if (rc < 0) { + mmi_err(chip, "Failed to get supplied-from, rc=%d\n", rc); + return rc; + } + + chip->batt_psy->num_supplies = count; + +print_supplies: + for (i = 0; i < chip->batt_psy->num_supplies; i++) { + mmi_info(chip, "battery supplied_from[%d]=%s\n", i, + chip->batt_psy->supplied_from[i]); + } + + return 0; +} + +static int mmi_discrete_create_votables(struct mmi_discrete_charger *chip) +{ + int rc = 0; + + chip->chg_disable_votable = create_votable("CHG_DISABLE", VOTE_SET_ANY, + chg_disable_vote_callback, + chip); + if (IS_ERR(chip->chg_disable_votable)) { + rc = PTR_ERR(chip->chg_disable_votable); + chip->chg_disable_votable = NULL; + return rc; + } + + chip->fcc_votable = create_votable("FCC", VOTE_MIN, + fcc_vote_callback, + chip); + if (IS_ERR(chip->fcc_votable)) { + rc = PTR_ERR(chip->fcc_votable); + chip->fcc_votable = NULL; + return rc; + } + + chip->fv_votable = create_votable("FV", VOTE_MIN, + fv_vote_callback, + chip); + if (IS_ERR(chip->fv_votable)) { + rc = PTR_ERR(chip->fv_votable); + chip->fv_votable = NULL; + return rc; + } + + chip->usb_icl_votable = create_votable("USB_ICL", VOTE_MIN, + usb_icl_vote_callback, + chip); + if (IS_ERR(chip->usb_icl_votable)) { + rc = PTR_ERR(chip->usb_icl_votable); + chip->usb_icl_votable = NULL; + return rc; + } + + chip->dc_suspend_votable = create_votable("DC_SUSPEND", VOTE_SET_ANY, + dc_suspend_vote_callback, + chip); + if (IS_ERR(chip->dc_suspend_votable)) { + rc = PTR_ERR(chip->dc_suspend_votable); + chip->dc_suspend_votable = NULL; + return rc; + } + + return 0; +} + +static void mmi_discrete_destroy_votables(struct mmi_discrete_charger *chip) +{ + if (chip->chg_disable_votable) + destroy_votable(chip->chg_disable_votable); + if (chip->fv_votable) + destroy_votable(chip->fv_votable); + if (chip->fcc_votable) + destroy_votable(chip->fcc_votable); + if (chip->usb_icl_votable) + destroy_votable(chip->usb_icl_votable); + if (chip->dc_suspend_votable) + destroy_votable(chip->dc_suspend_votable); +} + +static int mmi_discrete_init(struct mmi_discrete_charger *chip) +{ + int rc = 0; + + rc = mmi_discrete_create_votables(chip); + if (rc < 0) { + mmi_err(chip, "Couldn't create votables rc=%d\n", + rc); + return rc; + } + + return 0; +} +static int mmi_discrete_deinit(struct mmi_discrete_charger *chip) +{ + mmi_discrete_destroy_votables(chip); + + return 0; +} + +static int mmi_discrete_extcon_init(struct mmi_discrete_charger *chip) +{ + int rc; + + /* extcon registration */ + chip->extcon = devm_extcon_dev_allocate(chip->dev, mmi_discrete_extcon_cable); + if (IS_ERR(chip->extcon)) { + rc = PTR_ERR(chip->extcon); + mmi_err(chip, "failed to allocate extcon device rc=%d\n", + rc); + return rc; + } + + rc = devm_extcon_dev_register(chip->dev, chip->extcon); + if (rc < 0) { + mmi_err(chip, "failed to register extcon device rc=%d\n", + rc); + return rc; + } + + /* Support reporting polarity and speed via properties */ + rc = extcon_set_property_capability(chip->extcon, + EXTCON_USB, EXTCON_PROP_USB_TYPEC_POLARITY); + rc |= extcon_set_property_capability(chip->extcon, + EXTCON_USB, EXTCON_PROP_USB_SS); + rc |= extcon_set_property_capability(chip->extcon, + EXTCON_USB_HOST, EXTCON_PROP_USB_TYPEC_POLARITY); + rc |= extcon_set_property_capability(chip->extcon, + EXTCON_USB_HOST, EXTCON_PROP_USB_SS); + if (rc < 0) + mmi_err(chip, + "failed to configure extcon capabilities\n"); + + return rc; +} + +static int mmi_discrete_init_hw(struct mmi_discrete_charger *chip) +{ + int rc = 0; + + /* set OTG current limit */ + rc = charger_dev_set_boost_current_limit(chip->master_chg_dev, + chip->otg_cl_ua); + if (rc < 0) { + mmi_err(chip, "Couldn't set otg current limit rc=%d\n", rc); + return rc; + } + + vote(chip->fcc_votable, + BATT_PROFILE_VOTER, chip->batt_profile_fcc_ua > 0, + chip->batt_profile_fcc_ua); + vote(chip->fv_votable, + BATT_PROFILE_VOTER, chip->batt_profile_fv_uv > 0, + chip->batt_profile_fv_uv); + + /* Some h/w limit maximum supported ICL */ + vote(chip->usb_icl_votable, HW_LIMIT_VOTER, + chip->hw_max_icl_ua > 0, chip->hw_max_icl_ua); + + /* enable the charging path */ + rc = vote(chip->chg_disable_votable, DEFAULT_VOTER, false, 0); + if (rc < 0) { + mmi_err(chip, "Couldn't enable charging rc=%d\n", rc); + return rc; + } + + /* Ensure HW JEITA is DISABLED */ + mmi_info(chip, "DISABLE all HW JEITA\n"); + mmi_discrete_disable_hw_jeita(chip); + + if (chip->dc_cl_ma >= 0) { + //need to set dc icl + mmi_err(chip, "need to set dc icl\n"); + } + + return 0; +} + +/************************* + * battery cooling device * + *************************/ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +static int battery_tcd_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct mmi_discrete_charger *chip = cdev->devdata; + union power_supply_propval val; + int rc = 0; + + if (!chip->batt_psy) + return -EINVAL; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, &val); + if (rc) + mmi_err(chip, "Couldn't get battery charge control limit max prop rc=%d\n", rc); + + *state = val.intval; + + return 0; +} + +static int battery_tcd_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct mmi_discrete_charger *chip = cdev->devdata; + union power_supply_propval val; + int rc = 0; + + if (!chip->batt_psy) + return -EINVAL; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val); + if (rc) + mmi_err(chip, "Couldn't get battery charge control limit prop rc=%d\n", rc); + + *state = val.intval; + + return 0; +} + +static int battery_tcd_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct mmi_discrete_charger *chip = cdev->devdata; + union power_supply_propval val; + int rc = 0; + + if (!chip->batt_psy) + return -EINVAL; + + val.intval = state; + rc = power_supply_set_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val); + if (rc) + mmi_err(chip, "Couldn't set battery charge control limit prop rc=%d\n", rc); + + return 0; +} + +static const struct thermal_cooling_device_ops battery_tcd_ops = { + .get_max_state = battery_tcd_get_max_state, + .get_cur_state = battery_tcd_get_cur_state, + .set_cur_state = battery_tcd_set_cur_state, +}; +#endif + +static int mmi_discrete_probe(struct platform_device *pdev) +{ + int rc = 0; + struct mmi_discrete_charger *chip; + struct iio_dev *indio_dev; + struct power_supply_config psy_cfg = {}; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + + chip = iio_priv(indio_dev); + if (!chip) { + dev_err(&pdev->dev, + "Unable to alloc memory for mmi_discrete_iio\n"); + return -ENOMEM; + } + + chip->indio_dev = indio_dev; + + chip->dev = &pdev->dev; + chip->name = "mmi_discrete"; + psy_cfg.drv_data = chip; + psy_cfg.of_node = chip->dev->of_node; + platform_set_drvdata(pdev, chip); + this_chip = chip; + device_init_wakeup(chip->dev, true); + + chip->master_chg_dev = get_charger_by_name("master_chg"); + if (chip->master_chg_dev) { + mmi_info(chip, "Found master charger\n"); + charger_dev_set_drvdata(chip->master_chg_dev, chip); + } else { + mmi_err(chip, "*** Error : can't find master charger ***\n"); + return -EPROBE_DEFER; + } + + chip->mmi_psy = power_supply_get_by_name("mmi_battery"); + if (!chip->mmi_psy) { + mmi_err(chip, "Failed: mmi_psy has not registered yet\n"); + return -EPROBE_DEFER; + } + + chip->bms_psy = power_supply_get_by_name("bms"); + if (!chip->bms_psy) { + mmi_err(chip, "Failed: bms_psy has not registered yet\n"); + return -EPROBE_DEFER; + } + + chip->charger_psy = power_supply_get_by_name("charger"); + if (!chip->charger_psy) { + mmi_err(chip, "Failed: charger_psy has not registered yet\n"); + return -EPROBE_DEFER; + } + + chip->wls_psy = power_supply_get_by_name("wireless"); + + rc = mmi_discrete_init_iio_psy(chip, pdev); + if (rc < 0) { + mmi_err(chip, "Failed to init iio psy\n"); + return rc; + } + + rc = mmi_discrete_parse_dts(chip); + if (rc < 0) { + mmi_err(chip, "Failed to parse dts\n"); + return rc; + } + + rc = mmi_discrete_init(chip); + if (rc < 0) { + mmi_err(chip, "mmi_discrete_init Failed rc = %d\n", rc); + goto cleanup; + } + + rc = mmi_discrete_extcon_init(chip); + if (rc < 0) { + mmi_err(chip, "mmi_discrete_extcon_init Failed rc = %d\n", rc); + goto cleanup; + } + + rc = mmi_discrete_init_hw(chip); + if (rc < 0) { + mmi_err(chip, "mmi_discrete_init_hw Failed rc = %d\n", rc); + goto cleanup; + } + + chip->debug_enabled = &debug_enabled; + chip->ipc_log = ipc_log_context_create(MMI_LOG_PAGES, MMI_LOG_DIR, 0); + if (!chip->ipc_log) + mmi_err(chip, "Failed to create MMI DISCRETE IPC log\n"); + else + mmi_info(chip, "IPC logging is enabled for MMI DISCRETE\n"); + + INIT_DELAYED_WORK(&chip->charger_work, mmi_discrete_charger_work); + INIT_DELAYED_WORK(&chip->monitor_ibat_work, mmi_discrete_monitor_ibat_work); + INIT_DELAYED_WORK(&chip->wireless_icl_work, mmi_discrete_wireless_icl_work); + if (chip->enable_10w_ffc) { + mmi_charger_ffc_init(chip); + } + + chip->batt_psy = devm_power_supply_register(chip->dev, + &batt_psy_desc, + &psy_cfg); + if (IS_ERR(chip->batt_psy)) { + mmi_err(chip, + "Failed: batt power supply register\n"); + rc = PTR_ERR(chip->batt_psy); + goto cleanup; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + /* Register thermal zone cooling device for battery */ + chip->cdev = thermal_of_cooling_device_register(dev_of_node(chip->dev), + batt_psy_desc.name, chip, &battery_tcd_ops); + + if (IS_ERR(chip->cdev)) { + mmi_err(chip, "Cooling register failed for battery, ret:%ld\n", + PTR_ERR(chip->cdev)); + //return PTR_ERR(chip->cdev); + } else { + mmi_info(chip, "Cooling register success for battery."); + } +#endif + + rc = mmi_discrete_check_battery_supplies(chip); + if (rc) { + mmi_err(chip, "No valid battery supplies\n"); + goto cleanup; + } + + rc = mmi_discrete_init_usb_psy(chip); + if (rc) { + mmi_err(chip, "init usb psy error\n"); + goto cleanup; + } + + rc = mmi_discrete_init_usb_port_psy(chip); + if (rc < 0) { + mmi_err(chip, "Couldn't initialize usb pc_port psy rc=%d\n", rc); + goto cleanup; + } + + rc = mmi_discrete_init_dc_psy(chip); + if (rc < 0) { + mmi_err(chip, "Couldn't initialize dc psy rc=%d\n", rc); + goto cleanup; + } + + if(chip->mosfet_supported) { + rc = mmi_discrete_init_usb_therm_cooler(chip); + if (rc < 0) { + mmi_err(chip, "Couldn't initialize usb therm cooler rc=%d.", rc); + //goto cleanup; + } + } + + mmi_discrete_charger_init(chip); + + usb_source_change_notify_handler(&chip->master_chg_nb, 0, &chip->master_chg_dev->noti); + + mmi_discrete_create_debugfs(chip); + + mmi_info(chip, "MMI Discrete Charger Core probed successfully!\n"); + + return rc; + +cleanup: + mmi_discrete_deinit(chip); + platform_set_drvdata(pdev, NULL); + return rc; +} + +static int mmi_discrete_remove(struct platform_device *pdev) +{ + struct mmi_discrete_charger *chip = platform_get_drvdata(pdev); + + cancel_delayed_work(&chip->charger_work); + mmi_discrete_charger_deinit(chip); + + /*for usb thermal*/ + if(chip->mosfet_supported) + gpio_free(chip->mos_en_gpio); + + if (chip->mmi_psy) + power_supply_put(chip->mmi_psy); + if (chip->bms_psy) + power_supply_put(chip->bms_psy); + if (chip->charger_psy) + power_supply_put(chip->charger_psy); + if (chip->wls_psy) + power_supply_put(chip->wls_psy); + + return 0; +} + +static const struct of_device_id match_table[] = { + { .compatible = "mmi,mmi-discrete-charger", }, + { }, +}; + +static struct platform_driver mmi_discrete_driver = { + .driver = { + .name = "mmi,mmi-discrete-charger", + .owner = THIS_MODULE, + .of_match_table = match_table, + }, + .probe = mmi_discrete_probe, + .remove = mmi_discrete_remove, +}; +module_platform_driver(mmi_discrete_driver); + +MODULE_DESCRIPTION("MMI Discrete Charger Policy Driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/power/mmi_discrete_charger/mmi_discrete_charger_core.h b/drivers/power/mmi_discrete_charger/mmi_discrete_charger_core.h new file mode 100644 index 000000000000..532707d5536e --- /dev/null +++ b/drivers/power/mmi_discrete_charger/mmi_discrete_charger_core.h @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2020 Motorola Mobility LLC + * + * 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. + */ + #ifndef __MMI_DISCRETE_CHARGER_CORE_H__ +#define __MMI_DISCRETE_CHARGER_CORE_H__ + +#include +#include +#include +#include "mmi_charger.h" +#include +#include + +#define MMI_HB_VOTER "MMI_HB_VOTER" +#define USER_VOTER "USER_VOTER" +#define USB_PSY_VOTER "USB_PSY_VOTER" +#define BATT_PROFILE_VOTER "BATT_PROFILE_VOTER" +#define THERMAL_DAEMON_VOTER "THERMAL_DAEMON_VOTER" +#define WBC_VOTER "WBC_VOTER" +#define SW_ICL_MAX_VOTER "SW_ICL_MAX_VOTER" +#define HW_LIMIT_VOTER "HW_LIMIT_VOTER" +#define DEFAULT_VOTER "DEFAULT_VOTER" +#define PD_VOTER "PD_VOTER" +#define QC3P_VOTER "QC3P_VOTER" + +#define SDP_100_MA 100000 +#define SDP_CURRENT_UA 500000 +#define CDP_CURRENT_UA 1500000 +#define DCP_CURRENT_UA 1500000 +#define HVDCP_CURRENT_UA 3000000 +#define TYPEC_DEFAULT_CURRENT_UA 900000 +#define TYPEC_MEDIUM_CURRENT_UA 1500000 +#define TYPEC_HIGH_CURRENT_UA 3000000 +#define WIRELESS_CURRENT_DEFAULT_UA 1000000 + +static const unsigned int mmi_discrete_extcon_cable[] = { + EXTCON_USB, + EXTCON_USB_HOST, + EXTCON_NONE, +}; + +struct mmi_discrete_chg_client { + struct mmi_discrete_charger *chip; + struct mmi_battery_info batt_info; + struct mmi_charger_cfg chg_cfg; + struct mmi_charger_driver *driver; + struct power_supply *client_batt_psy; + u32 chrg_taper_cnt; +}; + +typedef enum { + CHARGER_FFC_STATE_INITIAL, + CHARGER_FFC_STATE_PROBING, + CHARGER_FFC_STATE_STANDBY, + CHARGER_FFC_STATE_GOREADY, + CHARGER_FFC_STATE_RUNNING, + CHARGER_FFC_STATE_AVGEXIT, + CHARGER_FFC_STATE_FFCDONE, + CHARGER_FFC_STATE_INVALID, +} CHARGER_FFC_STATE_T; + +struct mmi_discrete_charger { + struct device *dev; + char *name; + struct power_supply *mmi_psy; + struct power_supply *batt_psy; + struct power_supply *usb_psy; + struct power_supply *bms_psy; + struct power_supply *charger_psy; + struct power_supply *usb_port_psy; + struct power_supply *dc_psy; + struct power_supply *wls_psy; + + struct votable *chg_disable_votable; + struct votable *fcc_votable; + struct votable *fv_votable; + struct votable *usb_icl_votable; + struct votable *dc_suspend_votable; + + struct delayed_work charger_work; + struct mmi_charger_info chg_info; + struct mmi_charger_constraint constraint; + int chg_client_num; + struct mmi_discrete_chg_client *chg_clients; + + struct charger_device *master_chg_dev; + struct notifier_block master_chg_nb; + + bool vbus_enabled; + int real_charger_type; + int bc1p2_charger_type; + int hvdcp2_max_icl_ua; + int wls_max_icl_ua; + + struct delayed_work monitor_ibat_work; + int bat_ocp_ua; + + struct delayed_work wireless_icl_work; + + /* extcon for VBUS / ID notification to USB for type-c only */ + struct extcon_dev *extcon; + + /*IIO*/ + struct iio_dev *indio_dev; + struct iio_chan_spec *iio_chan; + struct iio_channel *int_iio_chans; + + /*cooling device*/ + struct thermal_cooling_device *cdev; + + /*mos output en/dis control*/ + int mos_en_gpio; + bool mosfet_supported; + + /*CP*/ + bool cp_active; + + /*PD*/ + struct adapter_device *pd_adapter_dev; + int pd_active; + int pd_vdm_verify; + bool pd_supported; + + int dc_cl_ma; + int batt_profile_fv_uv; + int batt_profile_fcc_ua; + int hw_max_icl_ua; + int otg_cl_ua; + + bool *debug_enabled; + void *ipc_log; + + /* cached status */ + int thermal_levels; + int system_temp_level; + int *thermal_mitigation; + int typec_mode; + bool use_extcon; + + /*FG*/ + bool chgmod_to_fg; + + //For 10w ffc + bool enable_10w_ffc; + int dcp_noneffc_cv; + CHARGER_FFC_STATE_T ffc_state; + int ffc_entry_threshold; + int ffc_exit_threshold; + int ffc_uisoc_threshold; + long ffc_ibat_windowsum; + long ffc_ibat_count; + int ffc_ibat_windowsize; + int ffc_iavg; + unsigned long ffc_iavg_update_timestamp; + bool is_ffc_enable; + int chrg_step; + int max_fv_mv; + bool ffc_stop_chrg; +}; + +int mmi_discrete_otg_enable(struct mmi_discrete_charger *chip, bool en); +int mmi_discrete_is_usb_suspended(struct mmi_discrete_charger *chip); +int mmi_discrete_config_typec_mode(struct mmi_discrete_charger *chip, int val); +int mmi_discrete_config_pd_active(struct mmi_discrete_charger *chip, int val); +int mmi_charger_pd_vdm_verify(struct mmi_discrete_charger *chip, int val); +int mmi_discrete_get_hw_current_max(struct mmi_discrete_charger *chip, int *val); +int mmi_discrete_config_charging_enabled(struct mmi_discrete_charger *chip, int val); +int mmi_discrete_get_charging_enabled(struct mmi_discrete_charger *chip, bool *val); +int mmi_discrete_config_input_current_settled(struct mmi_discrete_charger *chip, int val); +int mmi_discrete_config_termination_enabled(struct mmi_discrete_charger *chip, int val); +int mmi_discrete_get_qc3p_power(struct mmi_discrete_charger *chip, int *val); +int mmi_discrete_get_pulse_cnt(struct mmi_discrete_charger *chip, int *count); +int mmi_discrete_set_dp_dm(struct mmi_discrete_charger *chip, int val); +int mmi_discrete_get_charger_suspend(struct mmi_discrete_charger *chip, bool *val); +int mmi_discrete_get_typec_accessory_mode(struct mmi_discrete_charger *chip, int *val); + +#endif diff --git a/drivers/power/mmi_discrete_charger/mmi_discrete_charger_iio.c b/drivers/power/mmi_discrete_charger/mmi_discrete_charger_iio.c new file mode 100644 index 000000000000..1e00b046d838 --- /dev/null +++ b/drivers/power/mmi_discrete_charger/mmi_discrete_charger_iio.c @@ -0,0 +1,204 @@ +/* Copyright (c) 2020, 2021 The Linux Foundation. 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 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. + */ + +#include +#include "mmi_discrete_charger_iio.h" +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +#include +#endif + +static int mmi_discrete_iio_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val1, + int val2, long mask) +{ + struct mmi_discrete_charger *chip = iio_priv(indio_dev); + int rc = 0; + + switch (chan->channel) { + case PSY_IIO_MMI_OTG_ENABLE: + rc = mmi_discrete_otg_enable(chip, !!val1); + break; + case PSY_IIO_TYPEC_MODE: + rc = mmi_discrete_config_typec_mode(chip, val1); + break; + case PSY_IIO_PD_ACTIVE: + rc = mmi_discrete_config_pd_active(chip, val1); + break; + case PSY_IIO_USB_CHARGING_ENABLED: + rc = mmi_discrete_config_charging_enabled(chip, val1); + break; + case PSY_IIO_INPUT_CURRENT_SETTLED: + rc = mmi_discrete_config_input_current_settled(chip, val1); + break; + case PSY_IIO_USB_TERMINATION_ENABLED: + rc = mmi_discrete_config_termination_enabled(chip, val1); + break; + case PSY_IIO_DP_DM: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + if (chip->real_charger_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3 + || chip->real_charger_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5) { +#else + if (chip->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3 + || chip->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3P5) { +#endif + rc = mmi_discrete_set_dp_dm(chip, val1); + } + break; + case PSY_IIO_CP_ENABLE: + chip->cp_active = !!val1; + break; + case PSY_IIO_MMI_PD_VDM_VERIFY: + rc = mmi_charger_pd_vdm_verify(chip, val1); + break; + default: + pr_err("Unsupported mmi_discrete IIO chan %d\n", chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) + pr_err("Couldn't write IIO channel %d, rc = %d\n", + chan->channel, rc); + + return rc; +} + +static int mmi_discrete_iio_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val1, + int *val2, long mask) +{ + struct mmi_discrete_charger *chip = iio_priv(indio_dev); + int rc = 0; + bool val_bool = false, val_suspend = false; + *val1 = 0; + + switch (chan->channel) { + case PSY_IIO_USB_REAL_TYPE: + *val1 = chip->bc1p2_charger_type; + break; + case PSY_IIO_HW_CURRENT_MAX: + rc = mmi_discrete_get_hw_current_max(chip, val1); + break; + case PSY_IIO_USB_CHARGING_ENABLED: + mmi_discrete_get_charging_enabled(chip, &val_bool); + mmi_discrete_get_charger_suspend(chip, &val_suspend); + if(val_bool && !val_suspend) + *val1 = 1; + else + *val1 = 0; + break; + case PSY_IIO_MMI_QC3P_POWER: + rc = mmi_discrete_get_qc3p_power(chip, val1); + break; + case PSY_IIO_DP_DM: + rc = mmi_discrete_get_pulse_cnt(chip, val1); + break; + case PSY_IIO_TYPEC_ACCESSORY_MODE: + rc = mmi_discrete_get_typec_accessory_mode(chip, val1); + break; + case PSY_IIO_PD_ACTIVE: + *val1 = chip->pd_active; + break; + case PSY_IIO_CP_ENABLE: + *val1 = chip->cp_active; + break; + case PSY_IIO_MMI_PD_VDM_VERIFY: + *val1 = chip->pd_vdm_verify; + break; + default: + pr_err("Unsupported mmi_discrete IIO chan %d\n", chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) { + pr_err("Couldn't read IIO channel %d, rc = %d\n", + chan->channel, rc); + return rc; + } + + return IIO_VAL_INT; +} + +static int mmi_discrete_iio_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + struct mmi_discrete_charger *chip = iio_priv(indio_dev); + struct iio_chan_spec *iio_chan = chip->iio_chan; + int i; + + for (i = 0; i < ARRAY_SIZE(mmi_discrete_iio_psy_channels); + i++, iio_chan++) + if (iio_chan->channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static const struct iio_info mmi_discrete_iio_info = { + .read_raw = mmi_discrete_iio_read_raw, + .write_raw = mmi_discrete_iio_write_raw, + .of_xlate = mmi_discrete_iio_of_xlate, +}; + +int mmi_discrete_init_iio_psy(struct mmi_discrete_charger *chip, + struct platform_device *pdev) +{ + struct iio_dev *indio_dev = chip->indio_dev; + struct iio_chan_spec *chan; + int mmi_discrete_num_iio_channels = ARRAY_SIZE(mmi_discrete_iio_psy_channels); + int rc, i; + + chip->iio_chan = devm_kcalloc(chip->dev, mmi_discrete_num_iio_channels, + sizeof(*chip->iio_chan), GFP_KERNEL); + if (!chip->iio_chan) + return -ENOMEM; + + chip->int_iio_chans = devm_kcalloc(chip->dev, + mmi_discrete_num_iio_channels, + sizeof(*chip->int_iio_chans), + GFP_KERNEL); + if (!chip->int_iio_chans) + return -ENOMEM; + + indio_dev->info = &mmi_discrete_iio_info; + indio_dev->dev.parent = chip->dev; + indio_dev->dev.of_node = chip->dev->of_node; + indio_dev->name = "mmi-discrete-iio"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = chip->iio_chan; + indio_dev->num_channels = mmi_discrete_num_iio_channels; + + for (i = 0; i < mmi_discrete_num_iio_channels; i++) { + chip->int_iio_chans[i].indio_dev = indio_dev; + chan = &chip->iio_chan[i]; + chip->int_iio_chans[i].channel = chan; + chan->address = i; + chan->channel = mmi_discrete_iio_psy_channels[i].channel_num; + chan->type = mmi_discrete_iio_psy_channels[i].type; + chan->datasheet_name = + mmi_discrete_iio_psy_channels[i].datasheet_name; + chan->extend_name = + mmi_discrete_iio_psy_channels[i].datasheet_name; + chan->info_mask_separate = + mmi_discrete_iio_psy_channels[i].info_mask; + } + + rc = devm_iio_device_register(chip->dev, indio_dev); + if (rc) { + pr_err("Failed to register mmi-discrete-iio IIO device, rc=%d\n", rc); + return rc; + } + + return rc; +} diff --git a/drivers/power/mmi_discrete_charger/mmi_discrete_charger_iio.h b/drivers/power/mmi_discrete_charger/mmi_discrete_charger_iio.h new file mode 100644 index 000000000000..5ca844fb596e --- /dev/null +++ b/drivers/power/mmi_discrete_charger/mmi_discrete_charger_iio.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2020 Motorola Mobility LLC + * + * 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. + */ + +#ifndef __MMI_DISCRETE_CHARGER_IIO_H__ +#define __MMI_DISCRETE_CHARGER_IIO_H__ + +#include +#include +#include +#include +#include "mmi_discrete_charger_core.h" + +struct mmi_discrete_iio_channels { + const char *datasheet_name; + int channel_num; + enum iio_chan_type type; + long info_mask; +}; + +#define MMI_DISCRETE_IIO_CHAN(_name, _num, _type, _mask) \ + { \ + .datasheet_name = _name, \ + .channel_num = _num, \ + .type = _type, \ + .info_mask = _mask, \ + }, + +#define MMI_DISCRETE_CHAN_COUNT(_name, _num) \ + MMI_DISCRETE_IIO_CHAN(_name, _num,IIO_COUNT, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +#define MMI_DISCRETE_CHAN_INDEX(_name, _num) \ + MMI_DISCRETE_IIO_CHAN(_name, _num, IIO_INDEX, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +static const struct mmi_discrete_iio_channels mmi_discrete_iio_psy_channels[] = { + MMI_DISCRETE_CHAN_INDEX("usb_real_type", PSY_IIO_USB_REAL_TYPE) + MMI_DISCRETE_CHAN_INDEX("otg_enable", PSY_IIO_MMI_OTG_ENABLE) + MMI_DISCRETE_CHAN_INDEX("typec_mode", PSY_IIO_TYPEC_MODE) + MMI_DISCRETE_CHAN_INDEX("pd_active", PSY_IIO_PD_ACTIVE) + MMI_DISCRETE_CHAN_INDEX("hw_current_max", PSY_IIO_HW_CURRENT_MAX) + MMI_DISCRETE_CHAN_INDEX("charging_enabled", PSY_IIO_USB_CHARGING_ENABLED) + MMI_DISCRETE_CHAN_INDEX("input_current_settled", PSY_IIO_INPUT_CURRENT_SETTLED) + MMI_DISCRETE_CHAN_INDEX("termination_enabled", PSY_IIO_USB_TERMINATION_ENABLED) + MMI_DISCRETE_CHAN_COUNT("battery_dp_dm", PSY_IIO_DP_DM) + MMI_DISCRETE_CHAN_INDEX("usb_qc3p_power", PSY_IIO_MMI_QC3P_POWER) + MMI_DISCRETE_CHAN_INDEX("typec_accessory_mode", PSY_IIO_TYPEC_ACCESSORY_MODE) + MMI_DISCRETE_CHAN_INDEX("mmi_cp_enabled_status", PSY_IIO_CP_ENABLE) + MMI_DISCRETE_CHAN_INDEX("mmi_pd_vdm_verify", PSY_IIO_MMI_PD_VDM_VERIFY) +}; + +int mmi_discrete_init_iio_psy(struct mmi_discrete_charger *chip, + struct platform_device *pdev); + +#endif diff --git a/drivers/power/mmi_discrete_charger/mmi_discrete_factory_tcmd.c b/drivers/power/mmi_discrete_charger/mmi_discrete_factory_tcmd.c new file mode 100644 index 000000000000..b94833396ace --- /dev/null +++ b/drivers/power/mmi_discrete_charger/mmi_discrete_factory_tcmd.c @@ -0,0 +1,377 @@ +/* Copyright (c) 2020, 2021 The Linux Foundation. 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 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. + */ + +#include +#include +#include +#include +#include +#include +#include "mmi_discrete_charger_core.h" +#include "mmi_discrete_voter.h" + + +#define CHG_SHOW_MAX_SIZE 50 +static ssize_t force_chg_usb_suspend_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + static int old_current = 0; + static bool old_suspend = false; + + struct platform_device *pdev = to_platform_device(dev); + struct mmi_discrete_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("mmi_discrete_charger: Invalid usb suspend mode value = %lu\n", mode); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("mmi_discrete_charger: chip not valid\n"); + return -ENODEV; + } + pr_info("%s:mode=%d, old_suspend=%d, old_current=%d\n", __func__,(bool)mode, old_suspend, old_current); + if (mode == true && old_suspend == false) { + old_suspend = true; + old_current = get_effective_result(mmi_chip->usb_icl_votable); + /* vote 0mA when suspended */ + pmic_vote_force_val_set(mmi_chip->usb_icl_votable, 0); + pmic_vote_force_active_set(mmi_chip->usb_icl_votable, 1); + } else if (mode == false && old_suspend == true) { + old_suspend = false; + if (get_effective_result(mmi_chip->usb_icl_votable) == 0) { + pmic_vote_force_val_set(mmi_chip->usb_icl_votable, (u32)old_current); + pmic_vote_force_active_set(mmi_chip->usb_icl_votable, 1); + } + } + + return r ? r : count; +} + +static ssize_t force_chg_usb_suspend_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + bool state; + struct platform_device *pdev = to_platform_device(dev); + struct mmi_discrete_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("mmi_discrete_charger: chip not valid\n"); + return -ENODEV; + } + + state = mmi_discrete_is_usb_suspended(mmi_chip); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d", state); +} + +static DEVICE_ATTR(force_chg_usb_suspend, 0664, + force_chg_usb_suspend_show, + force_chg_usb_suspend_store); + +static ssize_t force_chg_auto_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + bool mode; + struct platform_device *pdev = to_platform_device(dev); + struct mmi_discrete_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtobool(buf, &mode); + if (r) { + pr_err("mmi_discrete_charger: Invalid chrg enable value = %d\n", mode); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("mmi_discrete_charger: chip not valid\n"); + return -ENODEV; + } + r = charger_dev_enable_charging(mmi_chip->master_chg_dev, mode); + if (r < 0) { + mmi_err(mmi_chip, "Factory Couldn't %s charging rc=%d\n", + mode ? "enable" : "disable", (int)r); + return r; + } + + return r ? r : count; +} + +static ssize_t force_chg_auto_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + int state; + bool value; + + struct platform_device *pdev = to_platform_device(dev); + struct mmi_discrete_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("mmi_discrete_charger: chip not valid\n"); + state = -ENODEV; + goto end; + } + ret = charger_dev_is_enabled_charging(mmi_chip->master_chg_dev, &value); + if (ret) { + mmi_err(mmi_chip, "CHG_EN_BIT failed ret = %d\n", ret); + state = -EFAULT; + goto end; + } + state = value; +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d", state); +} + +static DEVICE_ATTR(force_chg_auto_enable, 0664, + force_chg_auto_enable_show, + force_chg_auto_enable_store); + + +static ssize_t force_chg_ibatt_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long chg_current; + struct platform_device *pdev = to_platform_device(dev); + struct mmi_discrete_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &chg_current); + if (r) { + pr_err("mmi_discrete_charger: Invalid ibatt value = %lu\n", chg_current); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("mmi_discrete_charger: chip not valid\n"); + return -ENODEV; + } + + chg_current *= 1000; /* Convert to uA */ + r = charger_dev_set_charging_current(mmi_chip->master_chg_dev, chg_current); + if (r < 0) + mmi_err(mmi_chip, "Couldn't set ibatt value rc=%d\n", (int)r); + + return r ? r : count; +} + +static ssize_t force_chg_ibatt_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u32 state; + + struct platform_device *pdev = to_platform_device(dev); + struct mmi_discrete_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("mmi_discrete_charger: chip not valid\n"); + state = -ENODEV; + goto end; + } + ret = charger_dev_get_charging_current(mmi_chip->master_chg_dev, &state); + if (ret < 0) { + mmi_err(mmi_chip, "Factory Couldn't get ibatt rc=%d\n", ret); + return ret; + } + + state /= 1000; /* Convert to mA */ +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%ul", state); +} + +static DEVICE_ATTR(force_chg_ibatt, 0664, + force_chg_ibatt_show, + force_chg_ibatt_store); + +static ssize_t force_chg_iusb_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long usb_curr; + struct platform_device *pdev = to_platform_device(dev); + struct mmi_discrete_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &usb_curr); + if (r) { + pr_err("mmi_discrete_charger: Invalid iusb value = %lu\n", usb_curr); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("mmi_discrete_charger: chip not valid\n"); + return -ENODEV; + } + + usb_curr *= 1000; /* Convert to uA */ + pmic_vote_force_val_set(mmi_chip->usb_icl_votable, (u32)usb_curr); + pmic_vote_force_active_set(mmi_chip->usb_icl_votable, 1); + + return r ? r : count; +} + +static ssize_t force_chg_iusb_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int usb_curr; + struct platform_device *pdev = to_platform_device(dev); + struct mmi_discrete_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("chip not valid\n"); + usb_curr = -ENODEV; + goto end; + } + usb_curr = get_effective_result(mmi_chip->usb_icl_votable); + + if (usb_curr < 0) { + mmi_err(mmi_chip, "Factory Couldn't get usb_icl rc=%d\n", usb_curr); + return usb_curr; + } + usb_curr /= 1000; /* Convert to mA */ +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d", usb_curr); +} + +static DEVICE_ATTR(force_chg_iusb, 0664, + force_chg_iusb_show, + force_chg_iusb_store); + +static ssize_t chg_type_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + pr_err("%s un-supported\n", __func__); + return count; +} + +static ssize_t chg_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int charger_type; + struct platform_device *pdev = to_platform_device(dev); + struct mmi_discrete_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("chip not valid\n"); + charger_type = -ENODEV; + goto end; + } + //Because, Mtk platform TYPE DCP is 4. + // In order to be compatible with MTK platform. return "4" when real_charge_type=DCP + if (POWER_SUPPLY_TYPE_USB_DCP == mmi_chip->real_charger_type) + charger_type = 4; + else + charger_type = 1; // 1 is SDP in mkt platform +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d", charger_type); +} + +static DEVICE_ATTR(chg_type, 0664, + chg_type_show, + chg_type_store); + + +static ssize_t force_chg_fail_clear_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("mmi_discrete_charger: Invalid chg fail mode value = %lu\n", mode); + return -EINVAL; + } + + /* do nothing for mmi_discrete_charger */ + r = 0; + + return r ? r : count; +} + +static ssize_t force_chg_fail_clear_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + /* do nothing for mmi_discrete_charger */ + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "0"); +} + +static DEVICE_ATTR(force_chg_fail_clear, 0664, + force_chg_fail_clear_show, + force_chg_fail_clear_store); + + +int mmi_discrete_create_factory_testcase(struct mmi_discrete_charger *chip) +{ + int rc; + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_usb_suspend); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_usb_suspend\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_auto_enable); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_auto_enable\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_ibatt); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_ibatt\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_iusb); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_iusb\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_fail_clear); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_fail_clear\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_chg_type); + if (rc) { + mmi_err(chip, + "Couldn't create chg_type\n"); + } + + + return rc; +} + diff --git a/drivers/power/mmi_discrete_charger/mmi_discrete_factory_tcmd.h b/drivers/power/mmi_discrete_charger/mmi_discrete_factory_tcmd.h new file mode 100644 index 000000000000..823ad9c08fa7 --- /dev/null +++ b/drivers/power/mmi_discrete_charger/mmi_discrete_factory_tcmd.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2020, 2021 The Linux Foundation. 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 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. + */ + +#ifndef _MMI_DISCRETE_FACTROY_TCMD_H_ +#define _MMI_DISCRETE_FACTROY_TCMD_H_ + + +extern int mmi_discrete_create_factory_testcase(struct mmi_discrete_charger *chip); + + + +#endif diff --git a/drivers/power/mmi_discrete_charger/mmi_discrete_voter.c b/drivers/power/mmi_discrete_charger/mmi_discrete_voter.c new file mode 100644 index 000000000000..8f7640a75583 --- /dev/null +++ b/drivers/power/mmi_discrete_charger/mmi_discrete_voter.c @@ -0,0 +1,856 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2017, 2019-2020, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mmi_discrete_voter.h" + +#define NUM_MAX_CLIENTS 32 +#define DEBUG_FORCE_CLIENT "DEBUG_FORCE_CLIENT" + +static DEFINE_SPINLOCK(votable_list_slock); +static LIST_HEAD(votable_list); + +static struct dentry *debug_root; + +struct client_vote { + bool enabled; + int value; +}; + +struct votable { + const char *name; + const char *override_client; + struct list_head list; + struct client_vote votes[NUM_MAX_CLIENTS]; + int num_clients; + int type; + int effective_client_id; + int effective_result; + int override_result; + struct mutex vote_lock; + void *data; + int (*callback)(struct votable *votable, + void *data, + int effective_result, + const char *effective_client); + char *client_strs[NUM_MAX_CLIENTS]; + bool voted_on; + struct dentry *root; + struct dentry *status_ent; + u32 force_val; + struct dentry *force_val_ent; + bool force_active; + struct dentry *force_active_ent; +}; + +/** + * vote_set_any() + * @votable: votable object + * @client_id: client number of the latest voter + * @eff_res: sets 0 or 1 based on the voting + * @eff_id: Always returns the client_id argument + * + * Note that for SET_ANY voter, the value is always same as enabled. There is + * no idea of a voter abstaining from the election. Hence there is never a + * situation when the effective_id will be invalid, during election. + * + * Context: + * Must be called with the votable->lock held + */ +static void vote_set_any(struct votable *votable, int client_id, + int *eff_res, int *eff_id) +{ + int i; + + *eff_res = 0; + + for (i = 0; i < votable->num_clients && votable->client_strs[i]; i++) + *eff_res |= votable->votes[i].enabled; + + *eff_id = client_id; +} + +/** + * vote_min() - + * @votable: votable object + * @client_id: client number of the latest voter + * @eff_res: sets this to the min. of all the values amongst enabled voters. + * If there is no enabled client, this is set to INT_MAX + * @eff_id: sets this to the client id that has the min value amongst all + * the enabled clients. If there is no enabled client, sets this + * to -EINVAL + * + * Context: + * Must be called with the votable->lock held + */ +static void vote_min(struct votable *votable, int client_id, + int *eff_res, int *eff_id) +{ + int i; + + *eff_res = INT_MAX; + *eff_id = -EINVAL; + for (i = 0; i < votable->num_clients && votable->client_strs[i]; i++) { + if (votable->votes[i].enabled + && *eff_res > votable->votes[i].value) { + *eff_res = votable->votes[i].value; + *eff_id = i; + } + } + if (*eff_id == -EINVAL) + *eff_res = -EINVAL; +} + +/** + * vote_max() - + * @votable: votable object + * @client_id: client number of the latest voter + * @eff_res: sets this to the max. of all the values amongst enabled voters. + * If there is no enabled client, this is set to -EINVAL + * @eff_id: sets this to the client id that has the max value amongst all + * the enabled clients. If there is no enabled client, sets this to + * -EINVAL + * + * Context: + * Must be called with the votable->lock held + */ +static void vote_max(struct votable *votable, int client_id, + int *eff_res, int *eff_id) +{ + int i; + + *eff_res = INT_MIN; + *eff_id = -EINVAL; + for (i = 0; i < votable->num_clients && votable->client_strs[i]; i++) { + if (votable->votes[i].enabled && + *eff_res < votable->votes[i].value) { + *eff_res = votable->votes[i].value; + *eff_id = i; + } + } + if (*eff_id == -EINVAL) + *eff_res = -EINVAL; +} + +static int get_client_id(struct votable *votable, const char *client_str) +{ + int i; + + for (i = 0; i < votable->num_clients; i++) { + if (votable->client_strs[i] + && (strcmp(votable->client_strs[i], client_str) == 0)) + return i; + } + + /* new client */ + for (i = 0; i < votable->num_clients; i++) { + if (!votable->client_strs[i]) { + votable->client_strs[i] + = kstrdup(client_str, GFP_KERNEL); + if (!votable->client_strs[i]) + return -ENOMEM; + return i; + } + } + return -EINVAL; +} + +static char *get_client_str(struct votable *votable, int client_id) +{ + if (!votable || (client_id == -EINVAL)) + return NULL; + + return votable->client_strs[client_id]; +} + +void lock_votable(struct votable *votable) +{ + mutex_lock(&votable->vote_lock); +} + +void unlock_votable(struct votable *votable) +{ + mutex_unlock(&votable->vote_lock); +} + +/** + * is_override_vote_enabled() - + * is_override_vote_enabled_locked() - + * The unlocked and locked variants of getting whether override + vote is enabled. + * @votable: the votable object + * + * Returns: + * True if the client's vote is enabled; false otherwise. + */ +bool is_override_vote_enabled_locked(struct votable *votable) +{ + if (!votable) + return false; + + return votable->override_result != -EINVAL; +} + +bool is_override_vote_enabled(struct votable *votable) +{ + bool enable; + + if (!votable) + return false; + + lock_votable(votable); + enable = is_override_vote_enabled_locked(votable); + unlock_votable(votable); + + return enable; +} + +/** + * is_client_vote_enabled() - + * is_client_vote_enabled_locked() - + * The unlocked and locked variants of getting whether a client's + vote is enabled. + * @votable: the votable object + * @client_str: client of interest + * + * Returns: + * True if the client's vote is enabled; false otherwise. + */ +bool is_client_vote_enabled_locked(struct votable *votable, + const char *client_str) +{ + + int client_id; + + if (!votable || !client_str) + return false; + + client_id = get_client_id(votable, client_str); + if (client_id < 0) + return false; + + return votable->votes[client_id].enabled; +} + +bool is_client_vote_enabled(struct votable *votable, const char *client_str) +{ + bool enabled; + + if (!votable || !client_str) + return false; + + lock_votable(votable); + enabled = is_client_vote_enabled_locked(votable, client_str); + unlock_votable(votable); + return enabled; +} + +/** + * get_client_vote() - + * get_client_vote_locked() - + * The unlocked and locked variants of getting a client's voted + * value. + * @votable: the votable object + * @client_str: client of interest + * + * Returns: + * The value the client voted for. -EINVAL is returned if the client + * is not enabled or the client is not found. + */ +int get_client_vote_locked(struct votable *votable, const char *client_str) +{ + int client_id; + + if (!votable || !client_str) + return -EINVAL; + + client_id = get_client_id(votable, client_str); + if (client_id < 0) + return -EINVAL; + + if ((votable->type != VOTE_SET_ANY) + && !votable->votes[client_id].enabled) + return -EINVAL; + + return votable->votes[client_id].value; +} + +int get_client_vote(struct votable *votable, const char *client_str) +{ + int value; + + if (!votable || !client_str) + return -EINVAL; + + lock_votable(votable); + value = get_client_vote_locked(votable, client_str); + unlock_votable(votable); + return value; +} + +/** + * get_effective_result() - + * get_effective_result_locked() - + * The unlocked and locked variants of getting the effective value + * amongst all the enabled voters. + * + * @votable: the votable object + * + * Returns: + * The effective result. + * For MIN and MAX votable, returns -EINVAL when the votable + * object has been created but no clients have casted their votes or + * the last enabled client disables its vote. + * For SET_ANY votable it returns 0 when no clients have casted their votes + * because for SET_ANY there is no concept of abstaining from election. The + * votes for all the clients of SET_ANY votable is defaulted to false. + */ +int get_effective_result_locked(struct votable *votable) +{ + if (!votable) + return -EINVAL; + + if (votable->force_active) + return votable->force_val; + + if (votable->override_result != -EINVAL) + return votable->override_result; + + return votable->effective_result; +} + +int get_effective_result(struct votable *votable) +{ + int value; + + if (!votable) + return -EINVAL; + + lock_votable(votable); + value = get_effective_result_locked(votable); + unlock_votable(votable); + return value; +} + +/** + * get_effective_client() - + * get_effective_client_locked() - + * The unlocked and locked variants of getting the effective client + * amongst all the enabled voters. + * + * @votable: the votable object + * + * Returns: + * The effective client. + * For MIN and MAX votable, returns NULL when the votable + * object has been created but no clients have casted their votes or + * the last enabled client disables its vote. + * For SET_ANY votable it returns NULL too when no clients have casted + * their votes. But for SET_ANY since there is no concept of abstaining + * from election, the only client that casts a vote or the client that + * caused the result to change is returned. + */ +const char *get_effective_client_locked(struct votable *votable) +{ + if (!votable) + return NULL; + + if (votable->force_active) + return DEBUG_FORCE_CLIENT; + + if (votable->override_result != -EINVAL) + return votable->override_client; + + return get_client_str(votable, votable->effective_client_id); +} + +const char *get_effective_client(struct votable *votable) +{ + const char *client_str; + + if (!votable) + return NULL; + + lock_votable(votable); + client_str = get_effective_client_locked(votable); + unlock_votable(votable); + return client_str; +} + +/** + * vote() - + * + * @votable: the votable object + * @client_str: the voting client + * @enabled: This provides a means for the client to exclude himself from + * election. This clients val (the next argument) will be + * considered only when he has enabled his participation. + * Note that this takes a differnt meaning for SET_ANY type, as + * there is no concept of abstaining from participation. + * Enabled is treated as the boolean value the client is voting. + * @val: The vote value. This is ignored for SET_ANY votable types. + * For MIN, MAX votable types this value is used as the + * clients vote value when the enabled is true, this value is + * ignored if enabled is false. + * + * The callback is called only when there is a change in the election results or + * if it is the first time someone is voting. + * + * Returns: + * The return from the callback when present and needs to be called + * or zero. + */ +int vote(struct votable *votable, const char *client_str, bool enabled, int val) +{ + int effective_id = -EINVAL; + int effective_result; + int client_id; + int rc = 0; + bool similar_vote = false; + + if (!votable || !client_str) + return -EINVAL; + + lock_votable(votable); + + client_id = get_client_id(votable, client_str); + if (client_id < 0) { + rc = client_id; + goto out; + } + + /* + * for SET_ANY the val is to be ignored, set it + * to enabled so that the election still works based on + * value regardless of the type + */ + if (votable->type == VOTE_SET_ANY) + val = enabled; + + if ((votable->votes[client_id].enabled == enabled) && + (votable->votes[client_id].value == val)) { + pr_debug("%s: %s,%d same vote %s of val=%d\n", + votable->name, + client_str, client_id, + enabled ? "on" : "off", + val); + similar_vote = true; + } + + votable->votes[client_id].enabled = enabled; + votable->votes[client_id].value = val; + + if (similar_vote && votable->voted_on) { + pr_debug("%s: %s,%d Ignoring similar vote %s of val=%d\n", + votable->name, + client_str, client_id, enabled ? "on" : "off", val); + goto out; + } + + pr_debug("%s: %s,%d voting %s of val=%d\n", + votable->name, + client_str, client_id, enabled ? "on" : "off", val); + switch (votable->type) { + case VOTE_MIN: + vote_min(votable, client_id, &effective_result, &effective_id); + break; + case VOTE_MAX: + vote_max(votable, client_id, &effective_result, &effective_id); + break; + case VOTE_SET_ANY: + vote_set_any(votable, client_id, + &effective_result, &effective_id); + break; + default: + return -EINVAL; + } + + /* + * Note that the callback is called with a NULL string and -EINVAL + * result when there are no enabled votes + */ + if (!votable->voted_on + || (effective_result != votable->effective_result)) { + votable->effective_client_id = effective_id; + votable->effective_result = effective_result; + pr_debug("%s: effective vote is now %d voted by %s,%d\n", + votable->name, effective_result, + get_client_str(votable, effective_id), + effective_id); + if (votable->callback && !votable->force_active + && (votable->override_result == -EINVAL)) + rc = votable->callback(votable, votable->data, + effective_result, + get_client_str(votable, effective_id)); + } + + votable->voted_on = true; +out: + unlock_votable(votable); + return rc; +} + +/** + * vote_override() - + * + * @votable: The votable object + * @override_client: The voting client that will override other client's + * votes, that are already present. When force_active + * and override votes are set on a votable, force_active's + * client will have the higher priority and it's vote will + * be the effective one. + * @enabled: This provides a means for the override client to exclude + * itself from election. This client's vote + * (the next argument) will be considered only when + * it has enabled its participation. When this is + * set true, this will force a value on a MIN/MAX votable + * irrespective of its current value. + * @val: The vote value. This will be effective only if enabled + * is set true. + * Returns: + * The result of vote. 0 is returned if the vote + * is successfully set by the overriding client, when enabled is set. + */ +int vote_override(struct votable *votable, const char *override_client, + bool enabled, int val) +{ + int rc = 0; + + if (!votable || !override_client) + return -EINVAL; + + lock_votable(votable); + if (votable->force_active) { + votable->override_result = enabled ? val : -EINVAL; + goto out; + } + + if (enabled) { + rc = votable->callback(votable, votable->data, + val, override_client); + if (!rc) { + votable->override_client = override_client; + votable->override_result = val; + } + } else { + rc = votable->callback(votable, votable->data, + votable->effective_result, + get_client_str(votable, votable->effective_client_id)); + votable->override_result = -EINVAL; + } + +out: + unlock_votable(votable); + return rc; +} + +int rerun_election(struct votable *votable) +{ + int rc = 0; + int effective_result; + + if (!votable) + return -EINVAL; + + lock_votable(votable); + effective_result = get_effective_result_locked(votable); + if (votable->callback) + rc = votable->callback(votable, + votable->data, + effective_result, + get_client_str(votable, votable->effective_client_id)); + unlock_votable(votable); + return rc; +} + +struct votable *find_votable(const char *name) +{ + unsigned long flags; + struct votable *v; + bool found = false; + + if (!name) + return NULL; + + spin_lock_irqsave(&votable_list_slock, flags); + if (list_empty(&votable_list)) + goto out; + + list_for_each_entry(v, &votable_list, list) { + if (strcmp(v->name, name) == 0) { + found = true; + break; + } + } +out: + spin_unlock_irqrestore(&votable_list_slock, flags); + + if (found) + return v; + else + return NULL; +} + +static int force_active_get(void *data, u64 *val) +{ + struct votable *votable = data; + + *val = votable->force_active; + + return 0; +} + +static int force_active_set(void *data, u64 val) +{ + struct votable *votable = data; + int rc = 0; + int effective_result; + const char *client; + + lock_votable(votable); + votable->force_active = !!val; + + if (!votable->callback) + goto out; + + if (votable->force_active) { + rc = votable->callback(votable, votable->data, + votable->force_val, + DEBUG_FORCE_CLIENT); + } else { + if (votable->override_result != -EINVAL) { + effective_result = votable->override_result; + client = votable->override_client; + } else { + effective_result = votable->effective_result; + client = get_client_str(votable, + votable->effective_client_id); + } + rc = votable->callback(votable, votable->data, effective_result, + client); + } +out: + unlock_votable(votable); + return rc; +} +DEFINE_DEBUGFS_ATTRIBUTE(votable_force_ops, force_active_get, force_active_set, + "%lld\n"); + +int pmic_vote_force_val_set(struct votable *votable, u32 val) { + if(votable) { + votable->force_val = val; + return 0; + } + + return -EINVAL; +} + +int pmic_vote_force_active_get(struct votable *votable, u64 *val) { + if(votable) + return force_active_get(votable, val); + + return -EINVAL; +} + +int pmic_vote_force_active_set(struct votable *votable, u64 val) { + if(votable) + return force_active_set(votable, val); + + return -EINVAL; +} + +static int show_votable_clients(struct seq_file *m, void *data) +{ + struct votable *votable = m->private; + int i; + char *type_str = "Unkonwn"; + const char *effective_client_str; + + lock_votable(votable); + + for (i = 0; i < votable->num_clients; i++) { + if (votable->client_strs[i]) { + seq_printf(m, "%s: %s:\t\t\ten=%d v=%d\n", + votable->name, + votable->client_strs[i], + votable->votes[i].enabled, + votable->votes[i].value); + } + } + + switch (votable->type) { + case VOTE_MIN: + type_str = "Min"; + break; + case VOTE_MAX: + type_str = "Max"; + break; + case VOTE_SET_ANY: + type_str = "Set_any"; + break; + } + + effective_client_str = get_effective_client_locked(votable); + seq_printf(m, "%s: effective=%s type=%s v=%d\n", + votable->name, + effective_client_str ? effective_client_str : "none", + type_str, + get_effective_result_locked(votable)); + unlock_votable(votable); + + return 0; +} + +static int votable_status_open(struct inode *inode, struct file *file) +{ + struct votable *votable = inode->i_private; + + return single_open(file, show_votable_clients, votable); +} + +static const struct file_operations votable_status_ops = { + .owner = THIS_MODULE, + .open = votable_status_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +struct votable *create_votable(const char *name, + int votable_type, + int (*callback)(struct votable *votable, + void *data, + int effective_result, + const char *effective_client), + void *data) +{ + struct votable *votable; + unsigned long flags; + + if (!name) + return ERR_PTR(-EINVAL); + + votable = find_votable(name); + if (votable) + return ERR_PTR(-EEXIST); + + if (debug_root == NULL) { + debug_root = debugfs_create_dir("pmic-votable", NULL); + if (!debug_root) { + pr_err("Couldn't create debug dir\n"); + return ERR_PTR(-ENOMEM); + } + } + + if (votable_type >= NUM_VOTABLE_TYPES) { + pr_err("Invalid votable_type specified for voter\n"); + return ERR_PTR(-EINVAL); + } + + votable = kzalloc(sizeof(struct votable), GFP_KERNEL); + if (!votable) + return ERR_PTR(-ENOMEM); + + votable->name = kstrdup(name, GFP_KERNEL); + if (!votable->name) { + kfree(votable); + return ERR_PTR(-ENOMEM); + } + + votable->num_clients = NUM_MAX_CLIENTS; + votable->callback = callback; + votable->type = votable_type; + votable->data = data; + votable->override_result = -EINVAL; + mutex_init(&votable->vote_lock); + + /* + * Because effective_result and client states are invalid + * before the first vote, initialize them to -EINVAL + */ + votable->effective_result = -EINVAL; + if (votable->type == VOTE_SET_ANY) + votable->effective_result = 0; + votable->effective_client_id = -EINVAL; + + spin_lock_irqsave(&votable_list_slock, flags); + list_add(&votable->list, &votable_list); + spin_unlock_irqrestore(&votable_list_slock, flags); + + votable->root = debugfs_create_dir(name, debug_root); + if (!votable->root) { + pr_err("Couldn't create debug dir %s\n", name); + kfree(votable->name); + kfree(votable); + return ERR_PTR(-ENOMEM); + } + + votable->status_ent = debugfs_create_file("status", S_IFREG | 0444, + votable->root, votable, + &votable_status_ops); + if (!votable->status_ent) { + pr_err("Couldn't create status dbg file for %s\n", name); + debugfs_remove_recursive(votable->root); + kfree(votable->name); + kfree(votable); + return ERR_PTR(-EEXIST); + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0) + votable->force_val_ent = (struct dentry *)debugfs_create_u32("force_val", + S_IFREG | 0644, + votable->root, + &(votable->force_val)); + + if (!votable->force_val_ent) { + pr_err("Couldn't create force_val dbg file for %s\n", name); + debugfs_remove_recursive(votable->root); + kfree(votable->name); + kfree(votable); + return ERR_PTR(-EEXIST); + } +#endif + votable->force_active_ent = debugfs_create_file("force_active", + S_IFREG | 0444, + votable->root, votable, + &votable_force_ops); + if (!votable->force_active_ent) { + pr_err("Couldn't create force_active dbg file for %s\n", name); + debugfs_remove_recursive(votable->root); + kfree(votable->name); + kfree(votable); + return ERR_PTR(-EEXIST); + } + + return votable; +} + +void destroy_votable(struct votable *votable) +{ + unsigned long flags; + int i; + + if (!votable) + return; + + spin_lock_irqsave(&votable_list_slock, flags); + list_del(&votable->list); + spin_unlock_irqrestore(&votable_list_slock, flags); + + debugfs_remove_recursive(votable->root); + + for (i = 0; i < votable->num_clients && votable->client_strs[i]; i++) + kfree(votable->client_strs[i]); + + kfree(votable->name); + kfree(votable); +} diff --git a/drivers/power/mmi_discrete_charger/mmi_discrete_voter.h b/drivers/power/mmi_discrete_charger/mmi_discrete_voter.h new file mode 100644 index 000000000000..85dd4a33e964 --- /dev/null +++ b/drivers/power/mmi_discrete_charger/mmi_discrete_voter.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. + */ + +#ifndef __MMI_DISCRETE_VOTER_H +#define __MMI_DISCRETE_VOTER_H + +#include + +struct votable; + +enum votable_type { + VOTE_MIN, + VOTE_MAX, + VOTE_SET_ANY, + NUM_VOTABLE_TYPES, +}; + +bool is_client_vote_enabled(struct votable *votable, const char *client_str); +bool is_client_vote_enabled_locked(struct votable *votable, + const char *client_str); +bool is_override_vote_enabled(struct votable *votable); +bool is_override_vote_enabled_locked(struct votable *votable); +int get_client_vote(struct votable *votable, const char *client_str); +int get_client_vote_locked(struct votable *votable, const char *client_str); +int get_effective_result(struct votable *votable); +int get_effective_result_locked(struct votable *votable); +const char *get_effective_client(struct votable *votable); +const char *get_effective_client_locked(struct votable *votable); +int vote(struct votable *votable, const char *client_str, bool state, int val); +int vote_override(struct votable *votable, const char *override_client, + bool state, int val); +int rerun_election(struct votable *votable); +struct votable *find_votable(const char *name); +struct votable *create_votable(const char *name, + int votable_type, + int (*callback)(struct votable *votable, + void *data, + int effective_result, + const char *effective_client), + void *data); +void destroy_votable(struct votable *votable); +void lock_votable(struct votable *votable); +void unlock_votable(struct votable *votable); + +int pmic_vote_force_val_set(struct votable *votable, u32 val); +int pmic_vote_force_active_get(struct votable *votable, u64 *val); +int pmic_vote_force_active_set(struct votable *votable, u64 val); + +#endif /* __MMI_DISCRETE_VOTER_H */ diff --git a/drivers/power/mmi_discrete_turbo_charger/Android.mk b/drivers/power/mmi_discrete_turbo_charger/Android.mk new file mode 100644 index 000000000000..9239d4d631df --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/Android.mk @@ -0,0 +1,20 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_discrete_turbo_charger.ko +LOCAL_MODULE_TAGS := optional + +ifeq ($(MMI_QC3P_WT6670_DETECTED),true) + KERNEL_CFLAGS += CONFIG_MMI_QC3P_WT6670_DETECTED=y +endif + +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/adapter_class.ko + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/mmi_discrete_turbo_charger/Kbuild b/drivers/power/mmi_discrete_turbo_charger/Kbuild new file mode 100644 index 000000000000..b6d3765de57c --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/Kbuild @@ -0,0 +1,22 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/mmi_discrete_turbo_charger + +ifneq ($(filter m y,$(CONFIG_MMI_QC3P_WT6670_DETECTED)),) + EXTRA_CFLAGS += -DCONFIG_MMI_QC3P_WT6670_DETECTED +endif + +obj-m += mmi_discrete_turbo_charger.o +mmi_discrete_turbo_charger-objs += mmi_charger_class.o +mmi_discrete_turbo_charger-objs += mmi_charger_core.o +mmi_discrete_turbo_charger-objs += mmi_charger_pump_policy.o +mmi_discrete_turbo_charger-objs += mmi_cp_charger.o +mmi_discrete_turbo_charger-objs += qpnp_pmic_charger.o +mmi_discrete_turbo_charger-objs += mmi_qc3p.o +mmi_discrete_turbo_charger-objs += mmi_qc3p_cp_policy.o + +#ifeq ($(CONFIG_MMI_PL_CP_POLICY), y) +#endif + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../usb/typec/adapter_class/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/mmi_discrete_turbo_charger/Makefile b/drivers/power/mmi_discrete_turbo_charger/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/power/mmi_discrete_turbo_charger/mmi_charger_class.c b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_class.c new file mode 100644 index 000000000000..fc44c0df0935 --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_class.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" + +static struct class *mmi_charger_class; + +int mmi_enable_charging(struct mmi_charger_device *chrg, bool en) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->enable) + return chrg->ops->enable(chrg,en); + + return -ENOTSUPP; +} + +int mmi_is_charging_enabled(struct mmi_charger_device *chrg, bool *en) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->is_enabled) + return chrg->ops->is_enabled(chrg,en); + + return -ENOTSUPP; +} + +int mmi_get_charing_current(struct mmi_charger_device *chrg, u32 *uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->get_charging_current) + return chrg->ops->get_charging_current(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_set_charing_current(struct mmi_charger_device *chrg, u32 uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->set_charging_current) + return chrg->ops->set_charging_current(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_get_vbus(struct mmi_charger_device *chrg, u32 *mv) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->get_vbus) + return chrg->ops->get_vbus(chrg,mv); + + return -ENOTSUPP; +} + +int mmi_get_input_current_settled(struct mmi_charger_device *chrg, u32 *uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->get_input_current_settled) + return chrg->ops->get_input_current_settled(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_get_input_current(struct mmi_charger_device *chrg, u32 *uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->get_input_current) + return chrg->ops->get_input_current(chrg,uA); + + return -ENOTSUPP; + +} + +int mmi_set_input_current(struct mmi_charger_device *chrg, u32 uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->set_input_current) + return chrg->ops->set_input_current(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_update_charger_status(struct mmi_charger_device *chrg) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->update_charger_status) + return chrg->ops->update_charger_status(chrg); + + return -ENOTSUPP; +} + +int mmi_update_charger_error(struct mmi_charger_device *chrg) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->update_charger_error) + return chrg->ops->update_charger_error(chrg); + + return -ENOTSUPP; +} + +int mmi_clear_charger_error(struct mmi_charger_device *chrg) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->clear_charger_error) + return chrg->ops->clear_charger_error(chrg); + + return -ENOTSUPP; +} + +static void mmi_charger_device_release(struct device *dev) +{ + struct mmi_charger_device *charger_dev = to_mmi_charger_device(dev); + + kfree(charger_dev); + return; +} + +static int charger_match_device_by_name(struct device *dev, + const void *data) +{ + const char *name = data; + return strcmp(dev_name(dev), name) == 0; +} + +struct mmi_charger_device *get_charger_by_name(const char *name) +{ + struct device *dev; + + if(!name) + return (struct mmi_charger_device *)NULL; + dev = class_find_device(mmi_charger_class, NULL, name, + charger_match_device_by_name); + + return dev ? to_mmi_charger_device(dev) : NULL; +} + +int is_charger_exist(const char *name) +{ + if (get_charger_by_name(name) == NULL) + return 0; + return 1; +} + +struct mmi_charger_device *mmi_charger_device_register(const char *name, + const char *psy_name,struct device *parent, void *devdata, + const struct mmi_charger_ops *ops) +{ + struct mmi_charger_device *charger_dev; + int rc; + + pr_info("mmi charger device register: name=%s\n",name); + charger_dev = kzalloc(sizeof(struct mmi_charger_device),GFP_KERNEL); + if (!charger_dev) + return ERR_PTR(-ENOMEM); + + mutex_init(&charger_dev->ops_lock); + charger_dev->dev.class = mmi_charger_class; + charger_dev->dev.parent = parent; + charger_dev->dev.release = mmi_charger_device_release; + charger_dev->name = name; + dev_set_name(&charger_dev->dev, "%s", name); + dev_set_drvdata(&charger_dev->dev, devdata); + + rc = device_register(&charger_dev->dev); + if (rc) { + kfree(charger_dev); + return ERR_PTR(rc); + } + charger_dev->chrg_psy = power_supply_get_by_name(psy_name); + charger_dev->ops = ops; + if (!charger_dev->chrg_psy) + return ERR_PTR(-ENODEV); + pr_info("mmi charger device register: name=%s, successfully\n",name); + return charger_dev; +} + +void mmi_charger_device_unregister(struct mmi_charger_device* charger_dev) +{ + if (!charger_dev) + return; + + mutex_lock(&charger_dev->ops_lock); + charger_dev->ops = NULL; + mutex_unlock(&charger_dev->ops_lock); + device_unregister(&charger_dev->dev); +} + +void mmi_charger_class_exit(void) +{ + class_destroy(mmi_charger_class); +} + +int mmi_charger_class_init(void) +{ + mmi_charger_class = class_create(THIS_MODULE, "mmi_charger"); + if (IS_ERR(mmi_charger_class)) { + pr_err("Unable to create mmi charger class; error = %ld\n", + PTR_ERR(mmi_charger_class)); + return PTR_ERR(mmi_charger_class); + } + pr_info("success to create mmi charger class \n"); + return 0; +} diff --git a/drivers/power/mmi_discrete_turbo_charger/mmi_charger_class.h b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_class.h new file mode 100644 index 000000000000..55c22c41b86c --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_class.h @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + #ifndef __MMI_CHRG_CLASS_H_ + #define __MMI_CHRG_CLASS_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define chrg_dev_info(chip, fmt, ...) \ + pr_info("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__) \ + +struct mmi_charger_info { + bool vbus_pres; + int vbatt_volt; + int vbus_volt; + int ibatt_curr; + int ibus_curr; + int batt_temp; +}; + +#define MMI_BAT_OVP_ALARM_BIT 0 +#define MMI_BAT_OCP_ALARM_BIT 1 +#define MMI_BAT_UCP_ALARM_BIT 2 +#define MMI_BUS_OVP_ALARM_BIT 3 +#define MMI_BUS_OCP_ALARM_BIT 4 +#define MMI_BUS_UCP_ALARM_BIT 5 +#define MMI_BAT_OVP_FAULT_BIT 6 +#define MMI_BAT_OCP_FAULT_BIT 7 +#define MMI_BAT_UCP_FAULT_BIT 8 +#define MMI_BUS_OVP_FAULT_BIT 9 +#define MMI_BUS_OCP_FAULT_BIT 10 +#define MMI_BUS_UCP_FAULT_BIT 11 +#define MMI_CONV_OCP_FAULT_BIT 12 +#define MMI_THERM_ALARM_BIT 13 +#define MMI_THERM_FAULT_BIT 14 +#define MMI_CP_SWITCH_BIT 18 + +struct mmi_charger_error_info { + int chrg_err_type; + int bus_ucp_err_cnt; + int bus_ocp_err_cnt; +}; + +struct mmi_charger_device { + const char *name; /*charger device name*/ + struct power_supply *chrg_psy; /*power supply handle*/ + const struct mmi_charger_ops *ops; /*operation function*/ + struct mutex ops_lock; + struct device dev; + struct mmi_charger_info charger_data; + struct mmi_charger_error_info charger_error; + int *debug_mask; + int input_curr_setted; /*save input current*/ + int charging_curr_limited; /*charger current limitation*/ + int charging_curr_min; /*The minimum charging current supported by this device*/ + bool charger_enabled; /*is this charger device enabled*/ + bool charger_limited; /*is the charging current of this device limited*/ +}; + +struct mmi_charger_ops { + int (*enable)(struct mmi_charger_device *chrg, bool en); + int (*is_enabled)(struct mmi_charger_device *chrg, bool *en); + int (*get_charging_current)(struct mmi_charger_device *chrg, u32 *uA); + int (*set_charging_current)(struct mmi_charger_device *chrg, u32 uA); + int (*get_input_current_settled)(struct mmi_charger_device *chrg, u32 *uA); + int (*get_vbus)(struct mmi_charger_device *chrg, u32 *mv); + int (*get_input_current)(struct mmi_charger_device *chrg, u32 *uA); + int (*set_input_current)(struct mmi_charger_device *chrg, u32 uA); + int (*update_charger_status)(struct mmi_charger_device *chrg); + int (*update_charger_error)(struct mmi_charger_device *chrg); + int (*clear_charger_error)(struct mmi_charger_device *chrg); +}; + + +#define to_mmi_charger_device(obj) container_of(obj, struct mmi_charger_device, dev) +#define mmi_chrg_name(x) (dev_name(&(x)->dev)) + +extern int is_charger_exist(const char *name); +extern int mmi_charger_class_init(void); +extern void mmi_charger_class_exit(void); +extern int mmi_enable_charging(struct mmi_charger_device *chrg, bool en); +extern int mmi_is_charging_enabled(struct mmi_charger_device *chrg, bool *en); +extern int mmi_get_charing_current(struct mmi_charger_device *chrg, u32 *uA); +extern int mmi_set_charing_current(struct mmi_charger_device *chrg, u32 uA); +extern int mmi_get_input_current_settled(struct mmi_charger_device *chrg, u32 *uA); +extern int mmi_get_input_current(struct mmi_charger_device *chrg, u32 *uA); +extern int mmi_set_input_current(struct mmi_charger_device *chrg, u32 uA); +extern int mmi_update_charger_status(struct mmi_charger_device *chrg); +extern int mmi_update_charger_error(struct mmi_charger_device *chrg); +extern int mmi_clear_charger_error(struct mmi_charger_device *chrg); +extern int mmi_get_vbus(struct mmi_charger_device *chrg, u32 *mv); +extern struct mmi_charger_ops cp_charger_ops; +extern struct mmi_charger_ops qpnp_pmic_charger_ops; +extern struct mmi_charger_device *mmi_charger_device_register(const char *name, + const char *psy_name,struct device *parent, void *devdata, + const struct mmi_charger_ops *ops); +extern void mmi_charger_device_unregister(struct mmi_charger_device* charger_dev); +#endif diff --git a/drivers/power/mmi_discrete_turbo_charger/mmi_charger_core.c b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_core.c new file mode 100644 index 000000000000..49409c315f03 --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_core.c @@ -0,0 +1,1837 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include "mmi_charger_core.h" +#include "mmi_charger_core_iio.h" +#include "mmi_charger_policy.h" +#include "mmi_qc3p.h" +#include + +static int __debug_mask = 0x85; +module_param_named( + debug_mask, __debug_mask, int, S_IRUSR | S_IWUSR +); + +struct mmi_cp_policy_dev g_chrg_list = {0}; +const struct mmi_chrg_dev_ops dev_ops[] = { + { + .dev_name = "pmic-sw", + .ops = &qpnp_pmic_charger_ops, + }, + { + .dev_name = "cp-master", + .ops = &cp_charger_ops, + }, + { + .dev_name = "cp-slave", + .ops = &cp_charger_ops, + }, +}; + +bool is_chan_valid(struct mmi_charger_manager *chip, + enum mmi_charger_ext_iio_channels chan) +{ + int rc; + + if (IS_ERR(chip->ext_iio_chans[chan])) + return false; + + if (!chip->ext_iio_chans[chan]) { + chip->ext_iio_chans[chan] = iio_channel_get(chip->dev, + mmi_charger_ext_iio_chan_name[chan]); + if (IS_ERR(chip->ext_iio_chans[chan])) { + rc = PTR_ERR(chip->ext_iio_chans[chan]); + if (rc == -EPROBE_DEFER) + chip->ext_iio_chans[chan] = NULL; + + mmi_chrg_err(chip, "Failed to get IIO channel %s, rc=%d\n", + mmi_charger_ext_iio_chan_name[chan], rc); + return false; + } + } + + return true; +} + +int mmi_charger_read_iio_chan(struct mmi_charger_manager *chip, + enum mmi_charger_ext_iio_channels chan, int *val) +{ + int rc; + + if (is_chan_valid(chip, chan)) { + rc = iio_read_channel_processed( + chip->ext_iio_chans[chan], val); + return (rc < 0) ? rc : 0; + } + + return -EINVAL; +} + +int mmi_charger_write_iio_chan(struct mmi_charger_manager *chip, + enum mmi_charger_ext_iio_channels chan, int val) +{ + if (is_chan_valid(chip, chan)) + return iio_write_channel_raw(chip->ext_iio_chans[chan], + val); + + return -EINVAL; +} + +int start_qc3p_policy(struct mmi_charger_manager *chip) +{ + if(chip == NULL) { + mmi_chrg_err(chip, "mmi_charger_manager NULL\n"); + return -EINVAL; + } + + mmi_chrg_err(chip, "start_qc3p_policy\n"); + + schedule_work(&chip->psy_changed_work); + cancel_delayed_work(&chip->heartbeat_work); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(1000)); + + return 0; +} + +static int mmi_charger_iio_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val1, + int val2, long mask) +{ + struct mmi_charger_manager *chip = iio_priv(indio_dev); + int rc = 0; + + switch (chan->channel) { + case PSY_IIO_CP_ENABLE: + if (!chip->factory_mode) { + if (chip->pd_pps_support) + mmi_chrg_enable_all_cp(chip, val1); + else if (chip->qc3p_active) + mmi_qc3p_chrg_enable_all_cp(chip, val1); + } + case PSY_IIO_QC3P_START_POLICY: + start_qc3p_policy(chip); + break; + default: + pr_err("Unsupported mmi_charger IIO chan %d\n", chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) + pr_err("Couldn't write IIO channel %d, rc = %d\n", + chan->channel, rc); + + return rc; +} + +static int mmi_charger_iio_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val1, + int *val2, long mask) +{ + struct mmi_charger_manager *chip = iio_priv(indio_dev); + int rc = 0; + + *val1 = 0; + + switch (chan->channel) { + case PSY_IIO_CP_ENABLE: + *val1 = !chip->cp_disable; + break; + default: + pr_err("Unsupported mmi_charger IIO chan %d\n", chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) { + pr_err("Couldn't read IIO channel %d, rc = %d\n", + chan->channel, rc); + return rc; + } + + return IIO_VAL_INT; +} + +static int mmi_charger_iio_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + struct mmi_charger_manager *chip = iio_priv(indio_dev); + struct iio_chan_spec *iio_chan = chip->iio_chan; + int i; + + for (i = 0; i < ARRAY_SIZE(mmi_charger_iio_psy_channels); + i++, iio_chan++) + if (iio_chan->channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static const struct iio_info mmi_charger_iio_info = { + .read_raw = mmi_charger_iio_read_raw, + .write_raw = mmi_charger_iio_write_raw, + .of_xlate = mmi_charger_iio_of_xlate, +}; + +static int mmi_charger_init_iio_psy(struct mmi_charger_manager *chip, + struct platform_device *pdev) +{ + struct iio_dev *indio_dev = chip->indio_dev; + struct iio_chan_spec *chan; + int mmi_charger_num_iio_channels = ARRAY_SIZE(mmi_charger_iio_psy_channels); + int rc, i; + + chip->iio_chan = devm_kcalloc(chip->dev, mmi_charger_num_iio_channels, + sizeof(*chip->iio_chan), GFP_KERNEL); + if (!chip->iio_chan) + return -ENOMEM; + + chip->int_iio_chans = devm_kcalloc(chip->dev, + mmi_charger_num_iio_channels, + sizeof(*chip->int_iio_chans), + GFP_KERNEL); + if (!chip->int_iio_chans) + return -ENOMEM; + + indio_dev->info = &mmi_charger_iio_info; + indio_dev->dev.parent = chip->dev; + indio_dev->dev.of_node = chip->dev->of_node; + indio_dev->name = "mmi-parallel-charger"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = chip->iio_chan; + indio_dev->num_channels = mmi_charger_num_iio_channels; + + for (i = 0; i < mmi_charger_num_iio_channels; i++) { + chip->int_iio_chans[i].indio_dev = indio_dev; + chan = &chip->iio_chan[i]; + chip->int_iio_chans[i].channel = chan; + chan->address = i; + chan->channel = mmi_charger_iio_psy_channels[i].channel_num; + chan->type = mmi_charger_iio_psy_channels[i].type; + chan->datasheet_name = + mmi_charger_iio_psy_channels[i].datasheet_name; + chan->extend_name = + mmi_charger_iio_psy_channels[i].datasheet_name; + chan->info_mask_separate = + mmi_charger_iio_psy_channels[i].info_mask; + } + + rc = devm_iio_device_register(chip->dev, indio_dev); + if (rc) { + pr_err("Failed to register cp IIO device, rc=%d\n", rc); + return rc; + } + + chip->ext_iio_chans = devm_kcalloc(chip->dev, + ARRAY_SIZE(mmi_charger_ext_iio_chan_name), + sizeof(*chip->ext_iio_chans), + GFP_KERNEL); + if (!chip->ext_iio_chans) + return -ENOMEM; + + return 0; +} + +static int qc3p_volt_max_init = 0; +static int pd_volt_max_init = 0; +static int pd_curr_max_init = 0; +static int *dt_temp_zones; +static struct mmi_chrg_dts_info *chrg_name_list; + +#define MIN_TEMP_C -20 +#define MAX_TEMP_C 60 +#define HYSTEREISIS_DEGC 2 +void mmi_chrg_rate_check(struct mmi_charger_manager *chg); +bool mmi_find_temp_zone(struct mmi_charger_manager *chip, int temp_c, bool ignore_hysteresis_degc) +{ + int prev_zone, num_zones; + struct mmi_chrg_temp_zone *zones; + int hotter_t = 0, hotter_fcc = 0; + int colder_t = 0, colder_fcc = 0; + int i; + int max_temp; + + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return false; + } + + zones = chip->temp_zones; + num_zones = chip->num_temp_zones; + prev_zone = chip->pres_temp_zone; + + mmi_chrg_info(chip, "prev zone %d, temp_c %d\n",prev_zone, temp_c); + max_temp = zones[num_zones - 1].temp_c; + + if (prev_zone == ZONE_NONE) { + for (i = num_zones - 1; i >= 0; i--) { + if (temp_c >= zones[i].temp_c) { + if (i == num_zones - 1) + chip->pres_temp_zone = ZONE_HOT; + else + chip->pres_temp_zone = i + 1; + return true; + } + } + chip->pres_temp_zone = ZONE_COLD; + return true; + } + + if (prev_zone == ZONE_COLD) { + if (temp_c >= MIN_TEMP_C + HYSTEREISIS_DEGC) + chip->pres_temp_zone = ZONE_FIRST; + } else if (prev_zone == ZONE_HOT) { + if (temp_c <= max_temp - HYSTEREISIS_DEGC) + chip->pres_temp_zone = num_zones - 1; + } else { + if (prev_zone == ZONE_FIRST) { + hotter_t = zones[prev_zone].temp_c; + colder_t = MIN_TEMP_C; + hotter_fcc = zones[prev_zone + 1].chrg_step_power->chrg_step_curr; + colder_fcc = 0; + } else if (prev_zone == num_zones - 1) { + hotter_t = zones[prev_zone].temp_c; + colder_t = zones[prev_zone - 1].temp_c; + hotter_fcc = 0; + colder_fcc = zones[prev_zone - 1].chrg_step_power->chrg_step_curr; + } else { + hotter_t = zones[prev_zone].temp_c; + colder_t = zones[prev_zone - 1].temp_c; + hotter_fcc = zones[prev_zone + 1].chrg_step_power->chrg_step_curr; + colder_fcc = zones[prev_zone - 1].chrg_step_power->chrg_step_curr; + } + + if (!ignore_hysteresis_degc) { + if (zones[prev_zone].chrg_step_power->chrg_step_curr < hotter_fcc) + hotter_t += HYSTEREISIS_DEGC; + if (zones[prev_zone].chrg_step_power->chrg_step_curr < colder_fcc) + colder_t -= HYSTEREISIS_DEGC; + } + + if (temp_c <= MIN_TEMP_C) + chip->pres_temp_zone = ZONE_COLD; + else if (temp_c >= max_temp) + chip->pres_temp_zone = ZONE_HOT; + else if (temp_c >= hotter_t) + chip->pres_temp_zone++; + else if (temp_c < colder_t) + chip->pres_temp_zone--; + } + + mmi_chrg_info(chip, "batt temp_c %d, prev zone %d, pres zone %d, " + "hotter_fcc %dmA, colder_fcc %dmA, " + "hotter_t %dC, colder_t %dC\n", + temp_c,prev_zone, chip->pres_temp_zone, + hotter_fcc, colder_fcc, hotter_t, colder_t); + + if (prev_zone != chip->pres_temp_zone) { + mmi_chrg_info(chip, + "Entered Temp Zone %d!\n", + chip->pres_temp_zone); + return true; + } + return false; +} + +bool mmi_find_chrg_step(struct mmi_charger_manager *chip, int temp_zone, int vbatt_volt) +{ + int batt_volt, i; + bool find_step = false; + struct mmi_chrg_temp_zone zone; + struct mmi_chrg_step_power *chrg_steps; + struct mmi_chrg_step_info chrg_step_inline; + struct mmi_chrg_step_info prev_step; + + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return false; + } + + if (chip->pres_temp_zone == ZONE_HOT || + chip->pres_temp_zone == ZONE_COLD || + chip->pres_temp_zone < ZONE_FIRST) { + mmi_chrg_err(chip, "pres temp zone is HOT or COLD, " + "can't find chrg step\n"); + return false; + } + + zone = chip->temp_zones[chip->pres_temp_zone]; + chrg_steps = zone.chrg_step_power; + prev_step = chip->chrg_step; + + batt_volt = vbatt_volt; + chrg_step_inline.temp_c = zone.temp_c; + + mmi_chrg_info(chip, "batt_volt %d, chrg step %d, step nums %d\n", + batt_volt, prev_step.pres_chrg_step, + chip->chrg_step_nums); + + /*In the first search cycle, find out the vbatt is less than step volt*/ + + for (i = 0; i < chip->chrg_step_nums; i++) { + mmi_chrg_info(chip, + "first cycle,i %d, step volt %d, batt_volt %d\n", + i, chrg_steps[i].chrg_step_volt, batt_volt); + if (chrg_steps[i].chrg_step_volt > 0 + && batt_volt < chrg_steps[i].chrg_step_volt) { + if ( (i + 1) < chip->chrg_step_nums + && chrg_steps[i + 1].chrg_step_volt > 0) { + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i + 1].chrg_step_curr; + } else + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cc_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cv_volt = + chrg_steps[i].chrg_step_volt; + chrg_step_inline.pres_chrg_step = i; + find_step = true; + mmi_chrg_info(chip, "find chrg step\n"); + break; + } + } + + if (find_step) { + mmi_chrg_info(chip, "chrg step %d, " + "step cc curr %d, step cv volt %d, " + "step cv tapper curr %d\n", + chrg_step_inline.pres_chrg_step, + chrg_step_inline.chrg_step_cc_curr, + chrg_step_inline.chrg_step_cv_volt, + chrg_step_inline.chrg_step_cv_tapper_curr); + chip->chrg_step = chrg_step_inline; + } else { + + /*If can't find out any chrg step in the first search cycle,*/ + /*it means that vbatt is already greater than all chrg step volt, */ + /*therefore, start to enter second search cycle, */ + /*to find out the maximal chrg step volt*/ + for (i = 0; i < chip->chrg_step_nums; i++) { + mmi_chrg_info(chip, + "second cycle, i %d, step volt %d, batt_volt %d\n", + i, chrg_steps[i].chrg_step_volt, batt_volt); + if (chrg_steps[i].chrg_step_volt > 0 + && batt_volt > chrg_steps[i].chrg_step_volt) { + if ( (i + 1) < chip->chrg_step_nums + && chrg_steps[i + 1].chrg_step_volt > 0) { + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i + 1].chrg_step_curr; + } else + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cc_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cv_volt = + chrg_steps[i].chrg_step_volt; + chrg_step_inline.pres_chrg_step = i; + find_step = true; + mmi_chrg_info(chip, "find chrg step\n"); + } + } + + if (find_step) { + mmi_chrg_info(chip, "chrg step %d, " + "step cc curr %d, step cv volt %d, " + "step cv tapper curr %d\n", + chrg_step_inline.pres_chrg_step, + chrg_step_inline.chrg_step_cc_curr, + chrg_step_inline.chrg_step_cv_volt, + chrg_step_inline.chrg_step_cv_tapper_curr); + chip->chrg_step = chrg_step_inline; + } + } + + + if (find_step) { + if (chip->chrg_step.chrg_step_cc_curr == + chip->chrg_step.chrg_step_cv_tapper_curr) + chip->chrg_step.last_step = true; + else + chip->chrg_step.last_step = false; + + mmi_chrg_info(chip,"Temp zone %d, " + "select chrg step %d, step cc curr %d," + "step cv volt %d, step cv tapper curr %d, " + "is the last chrg step %d\n", + chip->pres_temp_zone, + chip->chrg_step.pres_chrg_step, + chip->chrg_step.chrg_step_cc_curr, + chip->chrg_step.chrg_step_cv_volt, + chip->chrg_step.chrg_step_cv_tapper_curr, + chip->chrg_step.last_step); + + if (prev_step.pres_chrg_step != chip->chrg_step.pres_chrg_step) { + mmi_chrg_info(chip, "Find the next chrg step\n"); + return true; + } + } + return false; +} + +void mmi_set_pps_result_history(struct mmi_charger_manager *chip, int pps_result) +{ + if (chip->pps_result_history_idx >= PPS_RET_HISTORY_SIZE -1) + chip->pps_result_history_idx = 0; + + if (pps_result < 0) + chip->pps_result_history[chip->pps_result_history_idx] = 1; + else + chip->pps_result_history[chip->pps_result_history_idx] = 0; + + chip->pps_result_history_idx++; +} + +bool mmi_get_pps_result_history(struct mmi_charger_manager *chip) +{ + int i = 0; + int result = 0; + for (i = 0; i < PPS_RET_HISTORY_SIZE; i++) + result += chip->pps_result_history[i]; + + if (result >= PPS_RET_HISTORY_SIZE) + return RESET_POWER; + else if (result >= PPS_RET_HISTORY_SIZE / 2) + return BLANCE_POWER; + else + return NO_ERROR; +} + +void mmi_clear_pps_result_history(struct mmi_charger_manager *chip) +{ + int i = 0;; + for (i = 0; i < PPS_RET_HISTORY_SIZE; i++) + chip->pps_result_history[i] = 0; +} + +int mmi_calculate_delta_volt(int pps_voltage, int pps_current, int delta_curr) +{ + u64 power; + int delta_volt; + u64 pps_volt; + u64 pps_curr; + + pps_volt = pps_voltage; + pps_curr = pps_current; + power = pps_volt * pps_curr; + + delta_volt = (int)(power / (pps_curr - delta_curr) - pps_volt); + delta_volt -= delta_volt % 20000; + + if (delta_volt > 0) + return delta_volt; + else + return 0; +} + +void mmi_update_all_charger_status(struct mmi_charger_manager *chip) +{ + int chrg_num, i; + struct mmi_charger_device *chrg_dev; + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return; + } + + chrg_num = chip->mmi_chrg_dev_num; + for (i = 0; i < chrg_num; i++) { + chrg_dev = chip->chrg_list[i]; + mmi_update_charger_status(chrg_dev); + } + return; +} + +void mmi_update_all_charger_error(struct mmi_charger_manager *chip) +{ + int chrg_num, i; + struct mmi_charger_device *chrg_dev; + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return; + } + + chrg_num = chip->mmi_chrg_dev_num; + for (i = 0; i < chrg_num; i++) { + chrg_dev = chip->chrg_list[i]; + mmi_update_charger_error(chrg_dev); + } + return; +} + +void mmi_dump_charger_error(struct mmi_charger_manager *chip, + struct mmi_charger_device *chrg_dev) +{ + if (!chip || !chrg_dev) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return; + } + mmi_chrg_dbg(chip, PR_MOTO, "%s: charger error type %d, " + "bus ucp err cnt %d, bus ocp err cnt %d\n", + chrg_dev->name, + chrg_dev->charger_error.chrg_err_type, + chrg_dev->charger_error.bus_ucp_err_cnt, + chrg_dev->charger_error.bus_ocp_err_cnt); + return; +} + +static enum power_supply_property mmi_chrg_mgr_props[] = { + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX +}; + +static int mmi_chrg_mgr_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mmi_charger_manager *chip = power_supply_get_drvdata(psy); + union power_supply_propval prop = {0,}; + int rc, i, chrg_cnt = 0; + + if (!chip->batt_psy) { + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) + return -ENODEV; + } + + switch (psp) { + case POWER_SUPPLY_PROP_CURRENT_NOW: + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + val->intval = prop.intval; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + val->intval = prop.intval; + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + if (chip->mmi_chrg_dev_num) { + for (i = 0; i < chip->mmi_chrg_dev_num; i++) { + if (chip->chrg_list[i]->charger_enabled) + chrg_cnt++; + } + val->intval = chrg_cnt; + } else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + val->intval = chip->system_thermal_level; + break; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: + val->intval = chip->thermal_levels; + break; + default: + return -EINVAL; + + } + return 0; +} + +static int mmi_chrg_mgr_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct mmi_charger_manager *chip = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + if (val->intval < 0) + return -EINVAL; + + if (chip->thermal_levels <= 0) + return -EINVAL; + + if (val->intval >= chip->thermal_levels) + chip->system_thermal_level = + chip->thermal_levels - 1; + else + chip->system_thermal_level = val->intval; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mmi_chrg_mgr_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + int ret; + + switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + ret = 1; + break; + default: + ret = 0; + break; + } + return ret; +} + +static const struct power_supply_desc mmi_chrg_mgr_psy_desc = { + .name = "mmi_chrg_manager", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = mmi_chrg_mgr_props, + .num_properties = ARRAY_SIZE(mmi_chrg_mgr_props), + .get_property = mmi_chrg_mgr_get_property, + .set_property = mmi_chrg_mgr_set_property, + .property_is_writeable = mmi_chrg_mgr_is_writeable, +}; + +static int mmi_chrg_mgr_psy_register(struct mmi_charger_manager *chip) +{ + struct power_supply_config mmi_chrg_mgr_cfg = {}; + + mmi_chrg_mgr_cfg.drv_data = chip; + mmi_chrg_mgr_cfg.of_node = chip->dev->of_node; + chip->mmi_chrg_mgr_psy = power_supply_register(chip->dev, + &mmi_chrg_mgr_psy_desc, + &mmi_chrg_mgr_cfg); + if (IS_ERR(chip->mmi_chrg_mgr_psy)) { + pr_err("Couldn't register mmi_chrg_mgr_psy power supply\n"); + return PTR_ERR(chip->mmi_chrg_mgr_psy); + } + + pr_info("power supply register mmi_chrg_mgr_psy successfully\n"); + return 0; +} + +static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) +{ + struct mmi_charger_manager *chip = + container_of(nb, struct mmi_charger_manager, psy_nb); + +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED + return NOTIFY_OK; +#endif + + if (!chip->usb_psy) { + mmi_chrg_err(chip, "usb psy is NULL, direct return\n"); + return NOTIFY_OK; + } + + if (ptr == chip->usb_psy && evt == PSY_EVENT_PROP_CHANGED) { + schedule_work(&chip->psy_changed_work); + + cancel_delayed_work(&chip->heartbeat_work); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(1000)); + + } + + return NOTIFY_OK; +} + +#define REV_CP_THRESH_UV 4400000 +#define CP_IBUS_THRESH_MA 20 +#define IBAT_THRESH_UA 10000 +#define REV_CP_DOUBLE_THRESH_UV 100000 +bool mmi_is_cable_plugout(struct mmi_charger_manager *chip) +{ + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + int vbus_volt, ibatt_curr, ibus_curr, vbatt_volt; + bool cp_chrg_enable, cp_sw_en; + + if (chip->extrn_sense) { + ibatt_curr = chrg_list->chrg_dev[CP_MASTER]->charger_data.ibatt_curr; + ibatt_curr *= 1000; + vbatt_volt = chrg_list->chrg_dev[CP_MASTER]->charger_data.vbatt_volt; + vbatt_volt *= 1000; + } else { + ibatt_curr= chrg_list->chrg_dev[PMIC_SW]->charger_data.ibatt_curr; + vbatt_volt = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbatt_volt; + } + vbus_volt = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbus_volt; + ibus_curr = chrg_list->chrg_dev[CP_MASTER]->charger_data.ibus_curr; + cp_sw_en = chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT); + cp_chrg_enable = chrg_list->chrg_dev[CP_MASTER]->charger_enabled; + mmi_chrg_info(chip, "ibat:%d, vbat:%d, vbus:%d, ibus:%d, cp_chg_en:%d, cp_sw_en:%d\n", + ibatt_curr, vbatt_volt, vbus_volt, ibus_curr, cp_chrg_enable, cp_sw_en); + + if (!cp_chrg_enable ||ibus_curr > CP_IBUS_THRESH_MA || ibatt_curr > IBAT_THRESH_UA) + return false; + + if (vbatt_volt >= vbus_volt + && vbus_volt < REV_CP_THRESH_UV) { + mmi_chrg_info(chip, "cable plug out: cp reverse\n"); + return true; + } + + if (cp_sw_en && vbus_volt < 2 * vbatt_volt - REV_CP_DOUBLE_THRESH_UV) { + mmi_chrg_info(chip, "cable plug out: cp double reverse\n"); + return true; + } + + return false; +} + +static bool mmi_factory_check(struct mmi_charger_manager *chip) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool factory_mode = false; + const char *bootargs = NULL; + char *bootmode = NULL; + char *end = NULL; + + if (!np) + return factory_mode; + + if (!of_property_read_string(np, "bootargs", &bootargs)) { + bootmode = strstr(bootargs, "androidboot.mode="); + if (bootmode) { + end = strpbrk(bootmode, " "); + bootmode = strpbrk(bootmode, "="); + } + if (bootmode && + end > bootmode && + strnstr(bootmode, "mot-factory", end - bootmode)) { + factory_mode = true; + } + } + of_node_put(np); + + return factory_mode; +} + + +static void kick_sm(struct mmi_charger_manager *chip, int ms) +{ + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + int ret; + + if (!chip->sm_work_running) { + mmi_chrg_dbg(chip, PR_INTERRUPT, + "launch mmi chrg sm work\n"); + mmi_chrg_policy_clear(chip); + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(ms)); + chip->sm_work_running = true; + + ret = mmi_charger_write_iio_chan(chip, CP_STATUS1, MMI_ENABLE_ADC); + if (ret) + mmi_chrg_err(chip, "Unable to write master CP adc enable status: %d\n", ret); + + if (chrg_list->cp_slave) { + ret = mmi_charger_write_iio_chan(chip, CP_SLAVE_STATUS1, MMI_ENABLE_ADC); + if (ret) + mmi_chrg_err(chip, "Unable to write slave CP adc enable status: %d\n", ret); + } + + ret = mmi_charger_write_iio_chan(chip, MMI_CP_ENABLE_STATUS, true); + if (ret) + mmi_chrg_err(chip, "Unable to write CP enable status: %d\n", ret); + } else + mmi_chrg_dbg(chip, PR_INTERRUPT, + "mmi chrg sm work already existed\n"); +} + +static void cancel_sm(struct mmi_charger_manager *chip) +{ + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + int ret; + + cancel_delayed_work_sync(&chip->mmi_chrg_sm_work); + flush_delayed_work(&chip->mmi_chrg_sm_work); + mmi_chrg_policy_clear(chip); + chip->sm_work_running = false; + chip->pd_volt_max = pd_volt_max_init; + chip->pd_curr_max = pd_curr_max_init; + + ret = mmi_charger_write_iio_chan(chip, CP_STATUS1, chip->factory_mode? MMI_ENABLE_ADC : MMI_DISABLE_ADC); + if (ret) + mmi_chrg_err(chip, "Unable to write master CP adc disable status: %d\n", ret); + + if (chrg_list->cp_slave) { + ret = mmi_charger_write_iio_chan(chip, CP_SLAVE_STATUS1, chip->factory_mode? MMI_ENABLE_ADC : MMI_DISABLE_ADC); + if (ret) + mmi_chrg_err(chip, "Unable to write slave CP adc disable status: %d\n", ret); + } + + mmi_chrg_dbg(chip, PR_INTERRUPT, + "cancel sync and flush mmi chrg sm work\n"); +} + +void chrg_policy_error_clear(struct mmi_charger_manager *chip, + struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num = 0, i = 0; + struct mmi_charger_device *chrg_dev; + chrg_num = chip->mmi_chrg_dev_num; + + for (i = 0; i < chrg_num; i++) { + switch (i) { + case PMIC_SW: + break; + + case CP_MASTER: + if (is_charger_exist(dev_ops[CP_MASTER].dev_name)) { + chrg_dev = chrg_list->chrg_dev[CP_MASTER]; + mmi_clear_charger_error(chrg_dev); + } + break; + + case CP_SLAVE: + break; + + default: + mmi_chrg_err(chip,"mmi_chrg_dev not found %d !\n",i); + break; + } + } + + return; +} + +void clear_chrg_dev_error_cnt(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num, i; + struct mmi_charger_device *chrg_dev; + chrg_num = chip->mmi_chrg_dev_num; + + for (i = 0; i < chrg_num; i++) { + if (is_charger_exist(dev_ops[i].dev_name)) { + chrg_dev = chrg_list->chrg_dev[i]; + chrg_dev->charger_error.chrg_err_type = 0; + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + chrg_dev->charger_error.bus_ocp_err_cnt = 0; + } + } + return; +} + +static void kick_qc3p_sm(struct mmi_charger_manager *chip, int ms) +{ + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + int ret; + + if (!chip->qc3p_sm_work_running) { + mmi_chrg_dbg(chip, PR_INTERRUPT, + "launch mmi qc3p chrg sm work\n"); + mmi_qc3p_chrg_policy_clear(chip); //todo + schedule_delayed_work(&chip->mmi_qc3p_chrg_sm_work, // + msecs_to_jiffies(ms)); //todo + chip->qc3p_sm_work_running = true; + + ret = mmi_charger_write_iio_chan(chip, CP_STATUS1, MMI_ENABLE_ADC); + if (ret) + mmi_chrg_err(chip, "Unable to write master CP adc enable status: %d\n", ret); + + if (chrg_list->cp_slave) { + ret = mmi_charger_write_iio_chan(chip, CP_SLAVE_STATUS1, MMI_ENABLE_ADC); + if (ret) + mmi_chrg_err(chip, "Unable to write slave CP adc enable status: %d\n", ret); + } + } else + mmi_chrg_dbg(chip, PR_INTERRUPT, + "mmi chrg qc3p sm work already existed\n"); +} + +static void cancel_qc3p_sm(struct mmi_charger_manager *chip) +{ + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + int ret; + + cancel_delayed_work_sync(&chip->mmi_qc3p_chrg_sm_work); + flush_delayed_work(&chip->mmi_qc3p_chrg_sm_work); + mmi_qc3p_chrg_policy_clear(chip); + chip->qc3p_sm_work_running = false; + ret = mmi_charger_write_iio_chan(chip, MMI_CP_ENABLE_STATUS, false); + if (ret) + mmi_chrg_err(chip, "Unable to write CP disable status: %d\n", ret); + chip->qc3p_volt_max = qc3p_volt_max_init; + + ret = mmi_charger_write_iio_chan(chip, CP_STATUS1, chip->factory_mode? MMI_ENABLE_ADC : MMI_DISABLE_ADC); + if (ret) + mmi_chrg_err(chip, "Unable to write master CP adc disable status: %d\n", ret); + + if (chrg_list->cp_slave) { + ret = mmi_charger_write_iio_chan(chip, CP_SLAVE_STATUS1, chip->factory_mode? MMI_ENABLE_ADC : MMI_DISABLE_ADC); + if (ret) + mmi_chrg_err(chip, "Unable to write slave CP adc disable status: %d\n", ret); + } + + mmi_chrg_dbg(chip, PR_INTERRUPT, + "cancel sync and flush mmi chrg qc3p sm work\n"); +} + +static void mmi_awake_vote(struct mmi_charger_manager *chip, bool awake) +{ + if (awake == chip->awake) + return; + + chip->awake = awake; + if (awake) + pm_stay_awake(chip->dev); + else + pm_relax(chip->dev); +} + +int mmi_charger_update_pd_capacity(struct mmi_charger_manager *chip) +{ + union power_supply_propval val; + bool pd_active; + int ret = 0, i = 0; + int maxwatt = 0, max_mv = 0, max_ma = 0; + + chip->pd_pps_support = false; + chip->mmi_pd_pdo_idx = 0; + ret = mmi_charger_read_iio_chan(chip, SMB5_USB_PD_ACTIVE, &val.intval); + if (ret) { + mmi_chrg_err(chip, "Unable to read PD ACTIVE: %d\n", ret); + return -EINVAL; + } + pd_active = val.intval; + + if (pd_active) { + + if (!chip->pd_adapter_dev) { + chip->pd_adapter_dev = get_adapter_by_name("pd_adapter"); + if (IS_ERR_OR_NULL(chip->pd_adapter_dev)) { + mmi_chrg_err(chip, "Error getting the pd phandle %ld\n", + PTR_ERR(chip->pd_adapter_dev)); + chip->pd_adapter_dev = NULL; + return -EINVAL; + } + } + + ret = adapter_dev_get_cap(chip->pd_adapter_dev, MMI_PD_ALL, &chip->mmi_pdo_info); + if (MMI_ADAPTER_OK != ret) { + mmi_chrg_err(chip, "Couldn't get pdo rc = %d\n", ret); + return -EINVAL; + } + + mmi_chrg_info(chip, "check all effective pdo info\n"); + for (i = 0; i < chip->mmi_pdo_info.nr; i++) { + if ((chip->mmi_pdo_info.type[i] == MMI_PD_APDO) + && (chip->mmi_pdo_info.max_mv[i] * 1000) >= PUMP_CHARGER_PPS_MIN_VOLT + && (chip->mmi_pdo_info.ma[i] * 1000) >= chip->typec_middle_current) { + + chip->pd_pps_support = true; + if ((chip->mmi_pdo_info.max_mv[i] * 1000) > PUMP_CHARGER_PPS_MAX_VOLT) { + chip->mmi_pdo_info.max_mv[i] = PUMP_CHARGER_PPS_MAX_VOLT / 1000; + chip->mmi_pdo_info.maxwatt[i] = chip->mmi_pdo_info.ma[i] * chip->mmi_pdo_info.max_mv[i]; + } + + if (maxwatt == 0) { + maxwatt = chip->mmi_pdo_info.maxwatt[i]; + max_mv = chip->mmi_pdo_info.max_mv[i]; + max_ma = chip->mmi_pdo_info.ma[i]; + chip->mmi_pd_pdo_idx = i ; + } else if (maxwatt == chip->mmi_pdo_info.maxwatt[i] + && (max_mv < chip->mmi_pdo_info.max_mv[i])) { + max_mv = chip->mmi_pdo_info.max_mv[i]; + max_ma = chip->mmi_pdo_info.ma[i]; + chip->mmi_pd_pdo_idx = i ; + } else if (maxwatt < chip->mmi_pdo_info.maxwatt[i]) { + maxwatt = chip->mmi_pdo_info.maxwatt[i]; + max_mv = chip->mmi_pdo_info.max_mv[i]; + max_ma = chip->mmi_pdo_info.ma[i]; + chip->mmi_pd_pdo_idx = i ; + } + + } + } + + mmi_chrg_info(chip, + "pd charger support pps, pdo %d, " + "volt %d, curr %d \n", + chip->mmi_pd_pdo_idx, + chip->mmi_pdo_info.max_mv[chip->mmi_pd_pdo_idx], + chip->mmi_pdo_info.ma[chip->mmi_pd_pdo_idx]); + + chip->pd_volt_max = min((max_mv * 1000), pd_volt_max_init); + chip->pd_curr_max = min((max_ma * 1000), pd_curr_max_init); + + } + + return 0; +} + +#define HEARTBEAT_DELAY_MS 60000 +#define SMBCHG_HEARTBEAT_INTRVAL_NS 70000000000 +#define CHG_SHOW_MAX_SIZE 50 +static void mmi_heartbeat_work(struct work_struct *work) +{ + struct mmi_charger_manager *chip = container_of(work, + struct mmi_charger_manager, heartbeat_work.work); + int hb_resch_time = 0, ret = 0; + union power_supply_propval val; + + mmi_chrg_info(chip, "MMI: Heartbeat!\n"); + /* Have not been resumed so wait another 100 ms */ + if (chip->suspended) { + mmi_chrg_err(chip, "SMBMMI: HB running before Resume\n"); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(100)); + return; + } + + mmi_awake_vote(chip, true); + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) { + mmi_chrg_err(chip, + "Could not get USB power_supply, deferring probe\n"); + return; + } + } + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &val); + if (ret) { + mmi_chrg_err(chip, "Unable to read USB PRESENT: %d\n", ret); + return; + } + chip->vbus_present = val.intval; + + hb_resch_time = HEARTBEAT_DELAY_MS; + + if (!chip->vbus_present) + mmi_awake_vote(chip, false); + + if (!chip->sm_work_running && chip->vbus_present) { + ret = mmi_charger_update_pd_capacity(chip); + if (ret) + goto schedule_work; + } + + if (!chip->qc3p_sm_work_running && chip->vbus_present) + chip->qc3p_active = mmi_qc3p_power_active(chip); + + if (chip->pd_pps_support + && !chip->factory_mode) { + mmi_chrg_info(chip, "MMI: Heartbeat!, launch sm work\n"); + kick_sm(chip, 100); + } else if(chip->qc3p_active + && !chip->factory_mode) { + mmi_chrg_info(chip, "MMI: Heartbeat!, launch qc3p sm work\n"); + kick_qc3p_sm(chip, 100); + } + +schedule_work: + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(hb_resch_time)); +} + +static void psy_changed_work_func(struct work_struct *work) +{ + struct mmi_charger_manager *chip = container_of(work, + struct mmi_charger_manager, psy_changed_work); + union power_supply_propval val; + int ret; + + mmi_chrg_info(chip, "kick psy changed work.\n"); + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) { + mmi_chrg_err(chip, + "Could not get USB power_supply, deferring probe\n"); + return; + } + } + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &val); + if (ret) { + mmi_chrg_err(chip, "Unable to read USB PRESENT: %d\n", ret); + return; + } + chip->vbus_present = val.intval; + + if (chip->vbus_present) + mmi_charger_update_pd_capacity(chip); + + mmi_chrg_info(chip, "vbus present %d, pd pps support %d, " + "pps max voltage %d, pps max curr %d\n", + chip->vbus_present, + chip->pd_pps_support, + chip->pd_volt_max, + chip->pd_curr_max); + if (chip->vbus_present) + chip->qc3p_active = mmi_qc3p_power_active(chip); + + if (chip->vbus_present + && chip->pd_pps_support + && !chip->factory_mode) { + kick_sm(chip, 100); + } else if (chip->vbus_present + && chip->qc3p_active + && !chip->factory_mode) { + kick_qc3p_sm(chip, 100); + } else { + cancel_qc3p_sm(chip); + chip->qc3p_active = false; + cancel_sm(chip); + chip->pd_pps_support = false; + } + + return; +} +#define DEFAULT_IBUS_MAX_MA 3000 +#define DEFAULT_QC3P_VOLT_STEPS 20000 +#define DEFAULT_QC3P_VOLT_MAX 11000000 +#define DEFAULT_BATT_OVP_LMT 4475000 +#define DEFAULT_PPS_VOLT_STEPS 20000 +#define DEFAULT_PPS_CURR_STEPS 50000 +#define DEFAULT_PPS_VOLT_MAX 11000000 +#define DEFAULT_PPS_CURR_MAX 4000000 +#define MMI_TEMP_ZONES 5 +static int mmi_chrg_manager_parse_dt(struct mmi_charger_manager *chip) +{ + struct device_node *node = chip->dev->of_node, *child; + int rc, byte_len, i, j, chrg_idx = 0, step_cnt = 0, idx = 0; + const int *ptr; + + rc = of_property_read_u32(node, + "mmi,batt-ovp-lmt", + &chip->batt_ovp_lmt); + if (rc < 0) + chip->batt_ovp_lmt = + DEFAULT_BATT_OVP_LMT; + + chip->extrn_fg = of_property_read_bool(node, + "mmi,extrn-fg"); + + if (chip->extrn_fg) { + byte_len = of_property_count_strings(node, "mmi,extrn-fg-name"); + if (byte_len <= 0) { + mmi_chrg_err(chip, "Cannot parse mmi, extrn-fg: %d\n", byte_len); + return byte_len; + } + + for (i = 0; i < byte_len; i++) { + rc = of_property_read_string_index(node, "mmi,extrn-fg-name", + i, &chip->extrn_fg_name); + if (rc < 0) { + mmi_chrg_err(chip, "Cannot parse extrn-fg-name\n"); + return rc; + } + } + } + + chip->extrn_sense = of_property_read_bool(node, + "mmi,extrn-sense"); + + chip->dont_rerun_aicl= of_property_read_bool(node, + "mmi,dont-rerun-aicl"); + + rc = of_property_read_u32(node, + "mmi,typec-middle-current", + &chip->typec_middle_current); + if (rc < 0) + chip->typec_middle_current = + TYPEC_MIDDLE_CURRENT_UA; + + rc = of_property_read_u32(node, + "mmi,pd-allow-min-current", + &chip->pd_allow_min_current); + if (rc < 0) + chip->pd_allow_min_current = + PD_ALLOW_MIN_CURRENT_UA; + + rc = of_property_read_u32(node, + "mmi,step-first-current-comp", + &chip->step_first_curr_comp); + if (rc < 0) + chip->step_first_curr_comp = + STEP_FIREST_CURR_COMP; + + rc = of_property_read_u32(node, + "mmi,pps-volt-steps", + &chip->pps_volt_steps); + if (rc < 0) + chip->pps_volt_steps = + DEFAULT_PPS_VOLT_STEPS; + + rc = of_property_read_u32(node, + "mmi,pps-curr-steps", + &chip->pps_curr_steps); + if (rc < 0) + chip->pps_curr_steps = + DEFAULT_PPS_CURR_STEPS; + + rc = of_property_read_u32(node, + "mmi,pd-volt-max", + &chip->pd_volt_max); + if (rc < 0) + chip->pd_volt_max = + DEFAULT_PPS_VOLT_MAX; + pd_volt_max_init = chip->pd_volt_max; + rc = of_property_read_u32(node, + "mmi,pd-curr-max", + &chip->pd_curr_max); + if (rc < 0) + chip->pd_curr_max = + DEFAULT_PPS_CURR_MAX; + pd_curr_max_init = chip->pd_curr_max; + + rc = of_property_read_u32(node, + "mmi,qc3p-max-ibus-ma", + &chip->qc3p_max_ibus_ma); + if (rc < 0) + chip->qc3p_max_ibus_ma = + DEFAULT_IBUS_MAX_MA; + rc = of_property_read_u32(node, + "mmi,qc3p-volt-steps", + &chip->qc3p_volt_steps); + if (rc < 0) + chip->qc3p_volt_steps = + DEFAULT_QC3P_VOLT_STEPS; + + rc = of_property_read_u32(node, + "mmi,qc3p-volt-max", + &chip->qc3p_volt_max); + if (rc < 0) + chip->qc3p_volt_max = + DEFAULT_QC3P_VOLT_MAX; + qc3p_volt_max_init = chip->qc3p_volt_max; + + for_each_child_of_node(node, child) + chip->mmi_chrg_dev_num++; + + if (!chip->mmi_chrg_dev_num) { + mmi_chrg_err(chip,"No mmi_chrg_dev list !\n"); + return -ENODEV; + } + + chrg_name_list = (struct mmi_chrg_dts_info *)devm_kzalloc(chip->dev, + sizeof(struct mmi_chrg_dts_info) * + chip->mmi_chrg_dev_num, GFP_KERNEL); + if (!chrg_name_list) { + mmi_chrg_err(chip,"No memory for mmi charger device name list !\n"); + goto cleanup; + } + + chip->chrg_list = (struct mmi_charger_device **)devm_kzalloc(chip->dev, + sizeof(struct mmi_charger_device *) * + chip->mmi_chrg_dev_num, GFP_KERNEL); + if (!chip->chrg_list) { + mmi_chrg_err(chip,"No memory for mmi charger device list !\n"); + goto cleanup; + } + + for_each_child_of_node(node, child) { + byte_len = of_property_count_strings(child, "chrg-name"); + if (byte_len <= 0) { + mmi_chrg_err(chip, "Cannot parse chrg-name: %d\n", byte_len); + goto cleanup; + } + + for (i = 0; i < byte_len; i++) { + rc = of_property_read_string_index(child, "chrg-name", + i, &chrg_name_list[chrg_idx].chrg_name); + if (rc < 0) { + mmi_chrg_err(chip, "Cannot parse chrg-name\n"); + goto cleanup; + } + } + + byte_len = of_property_count_strings(child, "psy-name"); + if (byte_len <= 0) { + mmi_chrg_err(chip, "Cannot parse psy-name: %d\n", byte_len); + goto cleanup; + } + + for (i = 0; i < byte_len; i++) { + rc = of_property_read_string_index(child, "psy-name", + i, &chrg_name_list[chrg_idx].psy_name); + if (rc < 0) { + mmi_chrg_err(chip, "Cannot parse psy-name\n"); + goto cleanup; + } + } + + mmi_chrg_info(chip, "mmi,chrg-name: %s, psy-name: %s\n", + chrg_name_list[chrg_idx].chrg_name, + chrg_name_list[chrg_idx].psy_name); + + of_property_read_u32(child, "charging-curr-limited", + &chrg_name_list[chrg_idx].charging_curr_limited); + + of_property_read_u32(child, "charging-curr-min", + &chrg_name_list[chrg_idx].charging_curr_min); + + chrg_idx++; + } + + rc = of_property_read_u32(node, + "mmi,chrg-temp-zones-num", + &chip->num_temp_zones); + if (rc < 0) + chip->num_temp_zones = + MMI_TEMP_ZONES; + + chip->temp_zones = (struct mmi_chrg_temp_zone *) + devm_kzalloc(chip->dev, + sizeof(struct mmi_chrg_temp_zone ) + * chip->num_temp_zones, + GFP_KERNEL); + if (chip->temp_zones == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + if (of_find_property(node, "mmi,mmi-chrg-temp-zones", &byte_len)) { + if (byte_len <= 0) { + mmi_chrg_err(chip, + "Cannot parse mmi-chrg-temp-zones %d\n",byte_len); + goto cleanup; + } + + step_cnt = (byte_len / chip->num_temp_zones - sizeof(u32)) + / (sizeof(u32) * 2); + mmi_chrg_info(chip, "mmi chrg step number is %d\n", step_cnt); + + dt_temp_zones = (u32 *) devm_kzalloc(chip->dev, byte_len, GFP_KERNEL); + if (dt_temp_zones == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + rc = of_property_read_u32_array(node, + "mmi,mmi-chrg-temp-zones", + (u32 *)dt_temp_zones, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_chrg_err(chip, + "Couldn't read mmi chrg temp zones rc = %d\n", rc); + goto zones_failed; + } + + mmi_chrg_err(chip, "temp zone nums: %d , byte_len %d, " + "num %d, row size %d\n", + chip->num_temp_zones, byte_len, + (int)(byte_len / sizeof(u32)), + (int)(sizeof(struct mmi_chrg_step_power) * step_cnt)); + ptr = dt_temp_zones; + + for (i = 0; i < byte_len / sizeof(u32); i++) { + if (!(i % 9)) { + printk("\n"); + } + printk("%u,", ptr[i]); + } + printk("\n"); + + for (i = 0; i < chip->num_temp_zones; i++) { + idx = (int)((sizeof(u32) + + sizeof(struct mmi_chrg_step_power) * step_cnt ) + * i / sizeof(u32)); + chip->temp_zones[i].temp_c = dt_temp_zones[idx]; + } + + for (i = 0; i < chip->num_temp_zones; i++) { + idx = (int)(((sizeof(u32) + + sizeof(struct mmi_chrg_step_power) * step_cnt ) + * i + sizeof(u32)) / sizeof(u32)); + chip->temp_zones[i].chrg_step_power = + (struct mmi_chrg_step_power *)&dt_temp_zones[idx]; + } + + for (i = 0; i < chip->num_temp_zones; i++) { + + printk( "mmi temp zones: Zone: %d, Temp: %d C " , i, + chip->temp_zones[i].temp_c); + for (j = 0; j < step_cnt; j++) { + chip->temp_zones[i].chrg_step_power[j].chrg_step_curr *= 1000; + chip->temp_zones[i].chrg_step_power[j].chrg_step_volt *= 1000; + + printk("step_volt %dmV, step_curr %dmA, ", + chip->temp_zones[i].chrg_step_power[j].chrg_step_volt, + chip->temp_zones[i].chrg_step_power[j].chrg_step_curr); + } + printk("\n"); + } + + chip->pres_temp_zone = ZONE_NONE; + chip->chrg_step_nums = step_cnt; + } + + if (of_find_property(node, "mmi,thermal-mitigation", &byte_len)) { + chip->thermal_mitigation = devm_kzalloc(chip->dev, byte_len, + GFP_KERNEL); + + if (chip->thermal_mitigation == NULL) { + rc = -ENOMEM; + goto zones_failed; + } + chip->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "mmi,thermal-mitigation", + chip->thermal_mitigation, + chip->thermal_levels); + if (rc < 0) { + mmi_chrg_err(chip, + "Couldn't parse thermal-mitigation rc = %d\n", rc); + goto thermal_failed; + } + } + + return rc; +thermal_failed: + devm_kfree(chip->dev,chip->thermal_mitigation); +zones_failed: + devm_kfree(chip->dev,chip->temp_zones); +cleanup: + chip->mmi_chrg_dev_num = 0; + devm_kfree(chip->dev,chip->chrg_list); + return rc; +} + +void chrg_dev_init(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num, i; + chrg_num = chip->mmi_chrg_dev_num; + mmi_chrg_err(chip,"runing in chrg dev init!\n"); + + for (i = 0; i < chrg_num; i++) { + + switch (i) { + case PMIC_SW: + if (is_charger_exist(dev_ops[PMIC_SW].dev_name)) { + chrg_list->pmic_sw = true; + chrg_list->chrg_dev[PMIC_SW] = chip->chrg_list[PMIC_SW]; + } + break; + case CP_MASTER: + if (is_charger_exist(dev_ops[CP_MASTER].dev_name)) { + chrg_list->cp_master = true; + chrg_list->chrg_dev[CP_MASTER] = chip->chrg_list[CP_MASTER]; + } + break; + case CP_SLAVE: + if (is_charger_exist(dev_ops[CP_SLAVE].dev_name)) { + chrg_list->cp_slave = true; + chrg_list->cp_clave_later = false; + chrg_list->chrg_dev[CP_SLAVE] = chip->chrg_list[CP_SLAVE]; + } + break; + default: + mmi_chrg_err(chip,"No mmi_chrg_dev found %d !\n",i); + break; + } + } + return; +} + +int mmi_chrg_policy_init(struct mmi_charger_manager *chip, + struct mmi_chrg_dts_info *chrg_dts, + int chrg_cnt) { + int i = 0, ops_cnt = 0, chrg_idx = 0; + struct mmi_charger_device *chrg_dev; + + if (!chip ||!chrg_dts) { + mmi_chrg_err(chip, "invalid input param!\n"); + return -EINVAL; + } + + ops_cnt = ARRAY_SIZE(dev_ops); + if (chrg_cnt > ops_cnt || + chrg_cnt > chip->mmi_chrg_dev_num) { + mmi_chrg_err(chip, "invalid input param!, chrg_cnt %d , ops_cnt %d, " + "mmi_chrg_dev_num %d\n", + chrg_cnt, ops_cnt, chip->mmi_chrg_dev_num); + return -EINVAL; + } + + mmi_chrg_err(chip, "chrg_cnt %d\n", chrg_cnt); + for (i = 0; i < chrg_cnt; i++) { + if (!strcmp(chrg_dts[i].chrg_name, dev_ops[i].dev_name)) { + + chrg_dev = mmi_charger_device_register(chrg_dts[i].chrg_name, + chrg_dts[i].psy_name, chip->dev, chip, + dev_ops[i].ops); + if (IS_ERR_OR_NULL(chrg_dev) + || !is_charger_exist(chrg_dts[i].chrg_name)) { + mmi_chrg_err(chip, + "register mmi charger %s failed\n", + chrg_dts[i].chrg_name); + return -EINVAL; + } else { + mmi_chrg_info(chip, + "register mmi charger %s successfully, i %d, chrg_idx %d\n", + chrg_dts[i].chrg_name, i, chrg_idx); + chrg_dev->charging_curr_limited = + chrg_dts[i].charging_curr_limited; + mmi_chrg_err(chip, "charging_curr_limited %d\n", + chrg_dts[i].charging_curr_limited); + chrg_dev->charging_curr_min = + chrg_dts[i].charging_curr_min; + mmi_chrg_err(chip, "charging_curr_min %d, chrg_id %d\n", + chrg_dts[i].charging_curr_min, chrg_idx); + chip->chrg_list[chrg_idx] = chrg_dev; + mmi_chrg_err(chip, "--- over ----\n"); + chrg_idx++; + } + } + } + + mmi_chrg_info(chip,"chrg_cnt %d, ops_cnt %d, mmi_chrg_dev_num %d, " + "chrg_idx %d\n", + chrg_cnt, ops_cnt, chip->mmi_chrg_dev_num, chrg_idx); + + if (chrg_idx != chip->mmi_chrg_dev_num + && chrg_idx > 0) { + + mmi_chrg_err(chip, "chrg_id %d != charger num %d\n", + chrg_idx, chip->mmi_chrg_dev_num); + for (i = 0; i < chrg_idx; i++) + mmi_charger_device_unregister(chip->chrg_list[i]); + return -EINVAL; + } + + chrg_dev_init(chip, &g_chrg_list); + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + INIT_DELAYED_WORK(&chip->mmi_chrg_sm_work, mmi_chrg_sm_work_func); + chip->qc3p_volt_comp = QC3P_INIT_VOLT_COMP; + INIT_DELAYED_WORK(&chip->mmi_qc3p_chrg_sm_work, mmi_qc3p_chrg_sm_work_func); + return 0; +} + +static int mmi_chrg_manager_probe(struct platform_device *pdev) +{ + int ret = 0; + struct mmi_charger_manager *chip; + struct iio_dev *indio_dev; + + if (!pdev) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + chip = iio_priv(indio_dev); + if (!chip) { + dev_err(&pdev->dev, + "Unable to alloc memory for mmi_charger_manager\n"); + return -ENOMEM; + } + chip->indio_dev = indio_dev; + + chip->dev = &pdev->dev; + chip->name = "mmi_chrg_manager"; + chip->debug_mask = &__debug_mask; + chip->suspended = false; + + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) { + mmi_chrg_err(chip, "Could not get battery power_supply\n"); + return -EPROBE_DEFER; + } + + chip->bms_psy = power_supply_get_by_name("bms"); + if (!chip->bms_psy) { + mmi_chrg_err(chip, "Could not get bms power_supply\n"); + return -EPROBE_DEFER; + } + + chip->charger_psy = power_supply_get_by_name("charger"); + if (!chip->charger_psy) { + mmi_chrg_err(chip, "Could not get charger power_supply\n"); + return -EPROBE_DEFER; + } + + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) { + mmi_chrg_err(chip, "Could not get USB power_supply\n"); + return -EPROBE_DEFER; + } + + ret = mmi_charger_class_init(); + if (ret < 0) { + dev_err(&pdev->dev, + "mmi charger class init failed\n"); + goto cleanup; + } + + ret = mmi_chrg_manager_parse_dt(chip); + if (ret < 0) { + dev_err(&pdev->dev, + "parse dt failed\n"); + goto cleanup; + } + + ret = mmi_charger_init_iio_psy(chip, pdev); + if (ret) { + dev_err(&pdev->dev, + "mmi charger iio psy init failed\n"); + goto cleanup; + } + + chip->factory_mode = mmi_factory_check(chip); + + + + + ret = mmi_chrg_policy_init(chip, chrg_name_list, + chip->mmi_chrg_dev_num); + if (ret < 0) { + dev_err(&pdev->dev, + "mmi chrg policy init failed\n"); + goto cleanup; + } + + chip->pd_adapter_dev = get_adapter_by_name("pd_adapter"); + if (IS_ERR_OR_NULL(chip->pd_adapter_dev)) { + dev_err(&pdev->dev, "Error getting the pd phandle %ld\n", + PTR_ERR(chip->pd_adapter_dev)); + chip->pd_adapter_dev = NULL; + } + + INIT_WORK(&chip->psy_changed_work, psy_changed_work_func); + INIT_DELAYED_WORK(&chip->heartbeat_work, mmi_heartbeat_work); + + ret = mmi_chrg_mgr_psy_register(chip); + if (ret) + goto cleanup; + + chip->psy_nb.notifier_call = psy_changed; + ret = power_supply_reg_notifier(&chip->psy_nb); + if (ret) + goto cleanup; + + init_completion(&chip->sm_completion); + platform_set_drvdata(pdev, chip); + + //create_sysfs_entries(chip); + schedule_work(&chip->psy_changed_work); + mmi_chrg_info(chip, "mmi chrg manager initialized successfully, ret %d\n", ret); + return 0; +cleanup: + mmi_charger_class_exit(); + devm_kfree(&pdev->dev, chip); + return -EIO; +} + +static int mmi_chrg_manager_remove(struct platform_device *pdev) +{ + struct mmi_charger_manager *chip = platform_get_drvdata(pdev); + + power_supply_unreg_notifier(&chip->psy_nb); + power_supply_unregister(chip->mmi_chrg_mgr_psy ); + platform_set_drvdata(pdev, NULL); + devm_kfree(&pdev->dev, chip); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mmi_chrg_suspend(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct mmi_charger_manager *chip = platform_get_drvdata(pdev); + + chip->suspended = true; + + return 0; +} + +static int mmi_chrg_resume(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct mmi_charger_manager *chip = platform_get_drvdata(pdev); + + chip->suspended = false; + + return 0; +} +#else +#define smb_mmi_suspend NULL +#define smb_mmi_resume NULL +#endif + +static const struct dev_pm_ops mmi_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mmi_chrg_suspend, mmi_chrg_resume) +}; + +static const struct of_device_id mmi_chrg_manager_match_table[] = { + {.compatible = "mmi,chrg-manager"}, + {}, +}; +MODULE_DEVICE_TABLE(of, mmi_chrg_manager_match_table); + +static struct platform_driver mmi_chrg_manager_driver = { + .probe = mmi_chrg_manager_probe, + .remove = mmi_chrg_manager_remove, + .driver = { + .name = "mmi_chrg_manager", + .owner = THIS_MODULE, + .pm = &mmi_dev_pm_ops, + .of_match_table = mmi_chrg_manager_match_table, + }, +}; + +static int __init mmi_chrg_manager_init(void) +{ + int ret; + ret = platform_driver_register(&mmi_chrg_manager_driver); + if (ret) { + pr_err("mmi_chrg_manager failed to register driver\n"); + return ret; + } + return 0; +} + +static void __exit mmi_chrg_manager_exit(void) +{ + platform_driver_unregister(&mmi_chrg_manager_driver); +} + +module_init(mmi_chrg_manager_init); +module_exit(mmi_chrg_manager_exit); + +MODULE_ALIAS("platform:mmi parallel charger"); +MODULE_AUTHOR("Motorola Mobility LLC"); +MODULE_DESCRIPTION("Motorola Mobility parallel charger"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/mmi_discrete_turbo_charger/mmi_charger_core.h b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_core.h new file mode 100644 index 000000000000..cceda9c61c5d --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_core.h @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + +#ifndef __MMI_CHRG_CORE_H_ +#define __MMI_CHRG_CORE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include +#include +#include +#include +#include +#include + +#define mmi_chrg_err(chip, fmt, ...) \ + pr_err("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__) \ + +#define mmi_chrg_info(chip, fmt, ...) \ + pr_info("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__) \ + +#define mmi_chrg_dbg(chip, reason, fmt, ...) \ + do { \ + if (*chip->debug_mask & (reason)) \ + pr_info("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__); \ + else \ + pr_debug("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__); \ + } while (0) + +enum print_reason { + PR_INTERRUPT = BIT(0), + PR_MISC = BIT(2), + PR_MOTO = BIT(7), +}; + +static inline void wakeup_source_init_internal(struct wakeup_source *ws, + const char *name) +{ + if(ws) { + memset(ws, 0, sizeof(*ws)); + ws->name = name; + } + + wakeup_source_add(ws); +} + +#define MAX_NUM_STEPS 10 +enum mmi_chrg_temp_zones { + ZONE_FIRST = 0, + /* states 0-9 are reserved for zones */ + ZONE_LAST = MAX_NUM_STEPS + ZONE_FIRST - 1, + ZONE_HOT, + ZONE_COLD, + ZONE_NONE = 0xFF, +}; + +enum mmi_chrg_steps { + STEP_FIRST = 0, + STEP_LAST = MAX_NUM_STEPS + STEP_FIRST - 1, + STEP_NONE = 0xFF, +}; + +enum mmi_pd_pps_result { + NO_ERROR, + BLANCE_POWER, + RESET_POWER, +}; + +struct mmi_chrg_step_power { + u32 chrg_step_volt; + u32 chrg_step_curr; +}; + +struct mmi_chrg_temp_zone { + int temp_c; /*temperature*/ + struct mmi_chrg_step_power *chrg_step_power; +}; + +struct mmi_chrg_step_info { + enum mmi_chrg_steps pres_chrg_step; + int temp_c; + int chrg_step_cc_curr; + int chrg_step_cv_volt; + int chrg_step_cv_tapper_curr; + bool last_step; +}; + +struct mmi_chrg_dev_ops { + const char *dev_name; + struct mmi_charger_ops *ops; +}; + +struct mmi_chrg_dts_info { + const char *chrg_name; + const char *psy_name; + int charging_curr_limited; + int charging_curr_min; +}; + +enum mmi_chrg_dev { + PMIC_SW = 0, + CP_MASTER, + CP_SLAVE, + CHRG_NUM, +}; + +struct mmi_cp_policy_dev { + bool pmic_sw; + bool cp_master; + bool cp_slave; + bool cp_clave_later; + struct mmi_charger_device *chrg_dev[CHRG_NUM]; +}; + +#define PPS_RET_HISTORY_SIZE 8 +#define PD_SRC_PDO_TYPE_FIXED 0 +#define PD_SRC_PDO_TYPE_BATTERY 1 +#define PD_SRC_PDO_TYPE_VARIABLE 2 +#define PD_SRC_PDO_TYPE_AUGMENTED 3 +#define STEP_FIREST_CURR_COMP 300000 +#define TYPEC_HIGH_CURRENT_UA 3000000 +#define TYPEC_MIDDLE_CURRENT_UA 2000000 +#define PD_ALLOW_MIN_CURRENT_UA 1500000 +#define SWITCH_CHARGER_PPS_VOLT 5000000 +#define PUMP_CHARGER_PPS_MIN_VOLT 8000000 +#define PUMP_CHARGER_PPS_MAX_VOLT 11000000 +#define COOLING_HYSTERISIS_DEGC 2 +struct mmi_charger_manager { + const char *name; + struct device *dev; + struct power_supply *batt_psy; + struct power_supply *usb_psy; + struct power_supply *mmi_chrg_mgr_psy; + struct adapter_device *pd_adapter_dev; + struct adapter_power_cap mmi_pdo_info; + struct notifier_block psy_nb; + + struct iio_channel **ext_iio_chans; + struct iio_dev *indio_dev; + struct iio_chan_spec *iio_chan; + struct iio_channel *int_iio_chans; + + bool factory_mode; + bool suspended; + bool awake; + bool cp_disable; + + int *debug_mask; + int mmi_pd_pdo_idx; /*request the pdo idx of PD*/ + int pps_volt_steps; /*PPS voltage, programming step size*/ + int pps_curr_steps; /*pps current, programming step size*/ + + int pd_volt_max; /*the Maximum request PD Voltage*/ + int pd_curr_max; /*the Maximum request PD current*/ + int batt_ovp_lmt; /*the battery over current limitation*/ + + int typec_middle_current; + int pd_allow_min_current; + int step_first_curr_comp; + int pps_volt_comp; + int pd_request_volt; + int pd_request_curr; + /*the request PD power*/ + int pd_request_volt_prev; + int pd_request_curr_prev; + /*the previous request PD power*/ + int pd_sys_therm_volt; + int pd_sys_therm_curr; + /*the thermal PD power*/ + + int pd_batt_therm_volt; + int pd_batt_therm_curr; + + int pd_target_volt; + int pd_target_curr; + /*the final commited request PD power*/ + + bool pps_start; + int pps_result; + int pps_result_history[PPS_RET_HISTORY_SIZE]; + int pps_result_history_idx; + /*save the result of the return from PD request*/ + + bool vbus_present; + bool pd_pps_support; + bool pd_pps_balance; + bool freeze_pd_power; + bool extrn_fg; + const char *extrn_fg_name; + bool extrn_sense; + bool recovery_pmic_chrg; + bool dont_rerun_aicl; + + bool sys_therm_cooling; + bool sys_therm_force_pmic_chrg; + bool batt_therm_cooling; + int batt_therm_cooling_cnt; + + struct delayed_work mmi_chrg_sm_work; /*mmi charger state machine work*/ + struct delayed_work heartbeat_work; /*cycle trig heartbeat work*/ + struct completion sm_completion; + struct work_struct psy_changed_work; /*the change notification of power supply*/ + bool sm_work_running; + int num_temp_zones; + struct mmi_chrg_temp_zone *temp_zones; /*the temperature zone of charging*/ + enum mmi_chrg_temp_zones pres_temp_zone; /*the present zone idx*/ + + int *thermal_mitigation; /*thermal mitigation array*/ + int thermal_levels; + int system_thermal_level; /*thermal level setting*/ + + int chrg_step_nums; + struct mmi_chrg_step_info chrg_step; /*step charger info*/ + + int mmi_chrg_dev_num; + struct mmi_charger_device **chrg_list; /*charger device list*/ + + /*qc3p*/ + int qc3p_max_ibus_ma; + int qc3p_power; + bool qc3p_active; + bool qc3p_sm_work_running; + struct delayed_work mmi_qc3p_chrg_sm_work; + int qc3p_request_volt; + int qc3p_request_volt_prev; + int qc3p_target_volt; + int qc3p_batt_therm_volt; + int qc3p_sys_therm_volt; + bool qc3p_recovery_pmic_chrg; + bool qc3p_sys_therm_cooling; + bool qc3p_sys_therm_force_pmic_chrg; + bool qc3p_batt_therm_cooling; + int qc3p_batt_therm_cooling_cnt; + int qc3p_volt_comp; + int qc3p_ibus_max_volt; + int qc3p_volt_steps; + int qc3p_volt_max; + struct power_supply *bms_psy; + struct power_supply *charger_psy; +}; + +#define PPS_INIT_VOLT_COMP 500000 +#define QC3P_INIT_VOLT_COMP 500000 +//For discrete main charger have no ibus adc capability +#define CP_ENABLED_MAIN_INPUT_LIMIT 300*1000 + +extern void mmi_qc3p_chrg_sm_work_func(struct work_struct *work); +extern struct mmi_cp_policy_dev g_chrg_list; +extern const struct mmi_chrg_dev_ops dev_ops[]; +extern void clear_chrg_dev_error_cnt(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_list); +extern void chrg_policy_error_clear(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_list); +extern void mmi_chrg_sm_work_func(struct work_struct *work); +extern void chrg_dev_init(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_list); +extern bool mmi_get_pps_result_history(struct mmi_charger_manager *chip); +extern void mmi_set_pps_result_history(struct mmi_charger_manager *chip, int pps_result); +extern void mmi_clear_pps_result_history(struct mmi_charger_manager *chip); +extern int mmi_calculate_delta_volt(int pps_voltage, int pps_current, int delta_curr); +extern bool mmi_find_chrg_step(struct mmi_charger_manager *chip, int temp_zone, int vbatt_volt); +extern bool mmi_find_temp_zone(struct mmi_charger_manager *chip, int temp_c, bool ignore_hysteresis_degc); +extern void mmi_update_all_charger_status(struct mmi_charger_manager *chip); +extern void mmi_update_all_charger_error(struct mmi_charger_manager *chip); +extern void mmi_dump_charger_error(struct mmi_charger_manager *chip, + struct mmi_charger_device *chrg_dev); +extern bool mmi_is_cable_plugout(struct mmi_charger_manager *chip); +extern int mmi_charger_update_pd_capacity(struct mmi_charger_manager *chip); +extern ssize_t mmi_get_factory_image_mode(void); +extern ssize_t mmi_set_factory_image_mode(int mode); +extern ssize_t mmi_get_factory_charge_upper(void); +extern ssize_t mmi_get_demo_mode(void); +extern ssize_t mmi_set_demo_mode(int mode); +extern ssize_t mmi_get_max_chrg_temp(void); +extern ssize_t mmi_set_max_chrg_temp(int value); +#endif diff --git a/drivers/power/mmi_discrete_turbo_charger/mmi_charger_core_iio.h b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_core_iio.h new file mode 100644 index 000000000000..012eadf2f8f5 --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_core_iio.h @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + +#ifndef __MMI_CHRG_CORE_IIO_H +#define __MMI_CHRG_CORE_IIO_H + +#include +#include + +struct mmi_charger_iio_channels { + const char *datasheet_name; + int channel_num; + enum iio_chan_type type; + long info_mask; +}; + +enum { + MMI_DISABLE_ADC = 0, + MMI_ENABLE_ADC, +}; + +#define MMI_CHARGER_IIO_CHAN(_name, _num, _type, _mask) \ + { \ + .datasheet_name = _name, \ + .channel_num = _num, \ + .type = _type, \ + .info_mask = _mask, \ + }, + +#define MMI_CHARGER_CHAN_INDEX(_name, _num) \ + MMI_CHARGER_IIO_CHAN(_name, _num, IIO_INDEX, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +static const struct mmi_charger_iio_channels mmi_charger_iio_psy_channels[] = { + MMI_CHARGER_CHAN_INDEX("cp_charging_enabled", PSY_IIO_CP_ENABLE) + MMI_CHARGER_CHAN_INDEX("qc3p_start_policy", PSY_IIO_QC3P_START_POLICY) +}; +enum mmi_charger_ext_iio_channels { + /*smb5*/ + SMB5_HW_CURRENT_MAX, + SMB5_CHARGING_ENABLED, + SMB5_TYPEC_MODE, + SMB5_USB_INPUT_CURRENT_SETTLED, + SMB5_USB_PD_ACTIVE, + /*qc3p*/ + SMB5_USB_REAL_TYPE, + SMB5_DP_DM, + SMB5_QC3P_POWER, + /*cp*/ + CP_ENABLE, + CP_INPUT_CURRENT_NOW, + CP_INPUT_VOLTAGE_NOW, + CP_STATUS1, + CP_CLEAR_ERROR, + /*cp_slave*/ + CP_SLAVE_ENABLE, + CP_SLAVE_INPUT_CURRENT_NOW, + CP_SLAVE_INPUT_VOLTAGE_NOW, + CP_SLAVE_STATUS1, + CP_SLAVE_CLEAR_ERROR, + /*mmi-smb5charger-iio*/ + MMI_CP_ENABLE_STATUS, + MMI_USB_TERMINATION_ENABLED, +}; + +static const char * const mmi_charger_ext_iio_chan_name[] = { + /*discrete*/ + [SMB5_HW_CURRENT_MAX] = "hw_current_max", + [SMB5_CHARGING_ENABLED] = "charging_enabled", + [SMB5_TYPEC_MODE] = "typec_mode", + [SMB5_USB_INPUT_CURRENT_SETTLED] = "input_current_settled", + [SMB5_USB_PD_ACTIVE] = "pd_active", + [MMI_USB_TERMINATION_ENABLED] = "termination_enabled", + /*qc3p*/ +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED + [SMB5_USB_REAL_TYPE] = "wt6670_usb_real_type", + [SMB5_DP_DM] = "wt6670_battery_dp_dm", + [SMB5_QC3P_POWER] = "wt6670_usb_qc3p_power", +#else + [SMB5_USB_REAL_TYPE] = "usb_real_type", + [SMB5_DP_DM] = "battery_dp_dm", + [SMB5_QC3P_POWER] = "usb_qc3p_power", +#endif + /*cp*/ + [CP_ENABLE] = "cp_enabled", + [CP_INPUT_CURRENT_NOW] = "cp_input_current_now", + [CP_INPUT_VOLTAGE_NOW] = "cp_input_voltage_settled", + [CP_STATUS1] = "cp_status1", + [CP_CLEAR_ERROR] = "cp_clear_error", + /*cp_slave*/ + [CP_SLAVE_ENABLE] = "cp_slave_enabled", + [CP_SLAVE_INPUT_CURRENT_NOW] = "cp_slave_input_current_now", + [CP_SLAVE_INPUT_VOLTAGE_NOW] = "cp_slave_input_voltage_settled", + [CP_SLAVE_STATUS1] = "cp_slave_status1", + [CP_SLAVE_CLEAR_ERROR] = "cp_slave_clear_error", + /*mmi-smb5charger-iio*/ + [MMI_CP_ENABLE_STATUS] = "mmi_cp_enabled_status", +}; + +int mmi_charger_read_iio_chan(struct mmi_charger_manager *chip, + enum mmi_charger_ext_iio_channels chan, int *val); + +int mmi_charger_write_iio_chan(struct mmi_charger_manager *chip, + enum mmi_charger_ext_iio_channels chan, int val); + +#endif diff --git a/drivers/power/mmi_discrete_turbo_charger/mmi_charger_policy.h b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_policy.h new file mode 100644 index 000000000000..62db9d556e9a --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_policy.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + #ifndef __MMI_CHRG_POLICY_H_ + #define __MMI_CHRG_POLICY_H_ +extern void mmi_chrg_policy_clear(struct mmi_charger_manager *chip); +extern void mmi_chrg_enable_all_cp(struct mmi_charger_manager *chip, int val); +extern void mmi_qc3p_chrg_policy_clear(struct mmi_charger_manager *chip); +extern void mmi_qc3p_chrg_enable_all_cp(struct mmi_charger_manager *chip, int val); +#endif diff --git a/drivers/power/mmi_discrete_turbo_charger/mmi_charger_pump_policy.c b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_pump_policy.c new file mode 100644 index 000000000000..ff8718793175 --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_charger_pump_policy.c @@ -0,0 +1,1504 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_core.h" + +typedef enum { + PM_STATE_DISCONNECT, + PM_STATE_ENTRY, + PM_STATE_SW_ENTRY, + PM_STATE_SW_LOOP, + PM_STATE_CHRG_PUMP_ENTRY, + PM_STATE_SINGLE_CP_ENTRY, + PM_STATE_DULE_CP_ENTRY, + PM_STATE_PPS_TUNNING_CURR, + PM_STATE_PPS_TUNNING_VOLT, + PM_STATE_CP_CC_LOOP, + PM_STATE_CP_CV_LOOP, + PM_STATE_CP_QUIT, + PM_STATE_RECOVERY_SW, + PM_STATE_STOP_CHARGE, + PM_STATE_COOLING_LOOP, +} pm_sm_state_t; + +const unsigned char *pm_state_str[] = { + "PM_STATE_DISCONNECT", + "PM_STATE_ENTRY", + "PM_STATE_SW_ENTRY", + "PM_STATE_SW_LOOP", + "PM_STATE_CHRG_PUMP_ENTRY", + "PM_STATE_SINGLE_CP_ENTRY", + "PM_STATE_DULE_CP_ENTRY", + "PM_STATE_PPS_TUNNING_CURR", + "PM_STATE_PPS_TUNNING_VOLT", + "PM_STATE_CP_CC_LOOP", + "PM_STATE_CP_CV_LOOP", + "PM_STATE_CP_QUIT", + "PM_STATE_RECOVERY_SW", + "PM_STATE_STOP_CHARGE", + "PM_STATE_COOLING_LOOP", +}; + +static pm_sm_state_t sm_state = PM_STATE_DISCONNECT; +static int chrg_cc_power_tunning_cnt = 0; +static int chrg_cv_taper_tunning_cnt = 0; +static int chrg_cv_delta_volt = 0; +static int quit_slave_chrg_cnt = 0; +static int batt_curr_roof = 0; +static int pd_constant_power_cnt = 0; +static int batt_ovp_cnt = 0; + +static void mmi_chrg_sm_move_state(struct mmi_charger_manager *chip, pm_sm_state_t state) +{ + mmi_chrg_dbg(chip, PR_INTERRUPT, "pm_state change:%s -> %s\n", + pm_state_str[sm_state], pm_state_str[state]); + sm_state = state; + pd_constant_power_cnt = 0; + batt_curr_roof = 0; +} + +#define PPS_VOLT_COMP_DELTA 300000 +static void chrg_policy_error_recovery(struct mmi_charger_manager *chip, + struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num = 0, i = 0, chrg_error_type= 0; + struct mmi_charger_device *chrg_dev; + chrg_num = chip->mmi_chrg_dev_num; + + for (i = 0; i < chrg_num; i++) { + + switch (i) { + case PMIC_SW: + break; + case CP_MASTER: + if (is_charger_exist(dev_ops[CP_MASTER].dev_name)) { + chrg_dev = chrg_list->chrg_dev[CP_MASTER]; + chrg_error_type = chrg_dev->charger_error.chrg_err_type; + if (chrg_error_type & (1 << MMI_BUS_UCP_ALARM_BIT) || + chrg_error_type & (1 << MMI_BUS_UCP_FAULT_BIT)) { + mmi_chrg_info(chip,"CP master bus ucp error %d, cnt %d," + "pps volt comp %dmV\n", + chrg_error_type, + chrg_dev->charger_error.bus_ucp_err_cnt, + chip->pps_volt_comp); + + if (chrg_dev->charger_error.bus_ucp_err_cnt > 3) { + if (chrg_list->cp_slave) { + chrg_list->cp_slave = false; + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + mmi_chrg_sm_move_state(chip, + PM_STATE_CHRG_PUMP_ENTRY); + } else { + chip->recovery_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, + PM_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + } + } else if (chrg_dev->charger_error.bus_ucp_err_cnt > 6) { + chip->recovery_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, + PM_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + } + + chrg_dev->charger_error.bus_ucp_err_cnt++; + chip->pps_volt_comp += PPS_VOLT_COMP_DELTA; + mmi_chrg_info(chip,"Restart charging, " + "increase pps volt comp %dmV\n", + chip->pps_volt_comp); + mmi_chrg_sm_move_state(chip, + PM_STATE_CHRG_PUMP_ENTRY); + mmi_clear_charger_error(chrg_dev); + }else if (chrg_error_type & (1 << MMI_BUS_OCP_ALARM_BIT) || + chrg_error_type & (1 << MMI_BUS_OCP_FAULT_BIT) || + chrg_error_type & (1 << MMI_CONV_OCP_FAULT_BIT)) { + mmi_chrg_info(chip,"CP master ocp error %d, cnt %d," + "pps volt comp %dmV\n", + chrg_error_type, + chrg_dev->charger_error.bus_ocp_err_cnt, + chip->pps_volt_comp); + if (chrg_dev->charger_error.bus_ocp_err_cnt > 3) { + chip->recovery_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, + PM_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ocp_err_cnt = 0; + } + chrg_dev->charger_error.bus_ocp_err_cnt++; + chip->pps_volt_comp -= PPS_VOLT_COMP_DELTA; + if (chip->pps_volt_comp < 0) + chip->pps_volt_comp = 0; + mmi_chrg_info(chip,"Restart charging, " + "decrease pps volt comp %dmV\n", + chip->pps_volt_comp); + mmi_chrg_sm_move_state(chip, + PM_STATE_CHRG_PUMP_ENTRY); + mmi_clear_charger_error(chrg_dev); + } + } + break; + case CP_SLAVE: + break; + default: + mmi_chrg_err(chip,"No mmi_chrg_dev found %d !\n",i); + break; + } + } + return; +} + +void mmi_chrg_enable_all_cp(struct mmi_charger_manager *chip, int val) +{ + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + bool enable = !!val; + + mmi_chrg_dbg(chip, PR_MOTO,"enable all cp = %d\n", enable); + if (enable) { + if (chip->cp_disable == false) + return; + + cancel_delayed_work_sync(&chip->mmi_chrg_sm_work); + mmi_chrg_sm_move_state(chip, PM_STATE_CHRG_PUMP_ENTRY); + chip->cp_disable = false; + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(0)); + + } else { + if(chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) + return; + + cancel_delayed_work_sync(&chip->mmi_chrg_sm_work); + + chip->cp_disable = true; + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + mmi_chrg_sm_move_state(chip, PM_STATE_RECOVERY_SW); + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(0)); + } +} + +#define HEARTBEAT_SHORT_DELAY_MS 1000 +#define HEARTBEAT_lOOP_WAIT_MS 3000 +#define HEARTBEAT_PPS_TUNNING_MS 100 +#define HEARTBEAT_NEXT_STATE_MS 100 +#define HEARTBEAT_CANCEL -1 +#define CC_CURR_DEBOUNCE 100000 +#define CV_TAPPER_COUNT 3 +#define CC_POWER_COUNT 3 +#define CV_DELTA_VOLT 100000 +#define THERMAL_TUNNING_CURR 100000 +#define COOLING_DELTA_POWER 100000 +#define COOLING_MAX_CNT 5 +#define PPS_SELECT_PDO_RETRY_COUNT 3 +#define DISABLE_CHRG_LIMIT -1 +#define CP_CHRG_SOC_LIMIT 90 +#define PD_CONT_PWR_CNT 5 +#define BATT_OVP_COUNT 5 + +static void clear_chg_manager(struct mmi_charger_manager *chip) +{ + mmi_chrg_dbg(chip, PR_INTERRUPT, "clear mmi chrg manager!\n"); + chip->pd_request_volt = 0; + chip->pd_request_curr = 0; + chip->pd_request_volt_prev = 0; + chip->pd_request_curr_prev = 0; + chip->pd_target_curr = 0; + chip->pd_target_volt = 0; + chip->pd_batt_therm_volt = 0; + chip->pd_batt_therm_curr = 0; + chip->pd_sys_therm_volt = 0; + chip->pd_sys_therm_curr = 0; + chip->recovery_pmic_chrg = false; + chip->sys_therm_cooling= false; + chip->sys_therm_force_pmic_chrg = false; + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + chip->pps_start = false; + chip->mmi_pd_pdo_idx = 0; + + memset(&chip->mmi_pdo_info, 0, + sizeof(struct adapter_power_cap)); +} + +void mmi_chrg_policy_clear(struct mmi_charger_manager *chip) { + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + chrg_dev_init(chip, &g_chrg_list); + clear_chrg_dev_error_cnt(chip, &g_chrg_list);; + clear_chg_manager(chip); + if (chrg_list->cp_slave) + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + if (chrg_list->cp_master) + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + sm_state = PM_STATE_DISCONNECT; + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + quit_slave_chrg_cnt = 0; + chrg_cc_power_tunning_cnt = 0; + chrg_cv_taper_tunning_cnt = 0; + chrg_cv_delta_volt = 0; + pd_constant_power_cnt = 0; + batt_curr_roof = 0; + return; +} + +void mmi_chrg_sm_work_func(struct work_struct *work) +{ + struct mmi_charger_manager *chip = container_of(work, + struct mmi_charger_manager, mmi_chrg_sm_work.work); + int i = 0, rc = 0; + int ibatt_curr = 0, vbatt_volt = 0, batt_temp = 0, vbus_pres = 0; + int batt_soc = 0; + int heartbeat_dely_ms = 0; + int cooling_curr = 0; + int cooling_volt = 0; + int pmic_sys_therm_level = 0; + bool zone_change = false; + bool ignore_hysteresis_degc = false; + struct mmi_chrg_step_info *chrg_step; + union power_supply_propval prop = {0,}; + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + + mmi_chrg_dbg(chip, PR_MOTO, "\n\n\n"); + + mmi_chrg_dbg(chip, PR_MOTO, "schedule SM work, sm state %s \n", + pm_state_str[sm_state]); + + mmi_chrg_dbg(chip, PR_MOTO, "pmic-sw is exist: %d " + "cp-master is exist: %d " + "cp-slave is exist: %d\n", + chrg_list->pmic_sw, + chrg_list->cp_master, + chrg_list->cp_slave); + if (!chrg_list->pmic_sw) { + mmi_chrg_err(chip,"PMIC-SW isn't exist, force quite mmi chrg sm work !\n"); + return; + } + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &prop); + if (!rc) + pmic_sys_therm_level = prop.intval; + + ibatt_curr = chrg_list->chrg_dev[PMIC_SW]->charger_data.ibatt_curr; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_TEMP, &prop); + if (!rc) + batt_temp = prop.intval / 10; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &prop); + if (!rc) + batt_soc = prop.intval; + + + if (ibatt_curr < 0) + ibatt_curr *= -1; + + + mmi_update_all_charger_status(chip); + mmi_update_all_charger_error(chip); + + if (chip->extrn_sense) { + ibatt_curr = chrg_list->chrg_dev[CP_MASTER]->charger_data.ibatt_curr; + ibatt_curr *= 1000; + if (ibatt_curr < 0) + ibatt_curr *= -1; + } + vbatt_volt = chrg_list->chrg_dev[CP_MASTER]->charger_data.vbatt_volt; + vbatt_volt *= 1000; + + vbus_pres = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbus_pres; + if (!vbus_pres) { + for (i = 0; i < 3; i++) { + mmi_update_all_charger_status(chip); + vbus_pres = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbus_pres; + mmi_chrg_info(chip, "Retry check charger status, vbus %d\n", vbus_pres); + if (vbus_pres) { + mmi_chrg_info(chip, "Get vbus present , continue to charging\n"); + break; + } + msleep(100); + } + } + + mmi_chrg_info(chip, "battery current %d\n", ibatt_curr); + mmi_chrg_info(chip, "battery voltage %d\n", vbatt_volt); + mmi_chrg_info(chip, "battery temp %d\n", batt_temp); + mmi_chrg_info(chip, "battery capacity %d\n", batt_soc); + + if (vbus_pres && mmi_is_cable_plugout(chip)) + vbus_pres = 0; + + if (vbus_pres && + (sm_state == PM_STATE_PPS_TUNNING_CURR + || sm_state == PM_STATE_PPS_TUNNING_VOLT + || sm_state == PM_STATE_CP_CC_LOOP + || sm_state == PM_STATE_CP_CV_LOOP)) { + mmi_dump_charger_error(chip, chrg_list->chrg_dev[CP_MASTER]); + chrg_policy_error_recovery(chip, chrg_list); + } + + if (sm_state == PM_STATE_CP_CV_LOOP) + ignore_hysteresis_degc = true; + zone_change = mmi_find_temp_zone(chip, batt_temp, ignore_hysteresis_degc); + chrg_step = &chip->chrg_step; + + if (chip->pres_temp_zone == ZONE_COLD + || chip->pres_temp_zone == ZONE_HOT + || !chrg_list->chrg_dev[PMIC_SW]->charger_enabled) { + + mmi_chrg_info(chip, "Force stop charging, " + "pres_temp_zone %d, " + "pmic charger enabled %d\n", + chip->pres_temp_zone, + chrg_list->chrg_dev[PMIC_SW]->charger_enabled); + mmi_chrg_sm_move_state(chip, PM_STATE_STOP_CHARGE); + } + + if (vbatt_volt > chip->batt_ovp_lmt) + batt_ovp_cnt ++; + else + batt_ovp_cnt = 0; + + if (batt_ovp_cnt >= BATT_OVP_COUNT) { + mmi_chrg_info(chip, "Force stop charging, " + "vbatt_volt %dmv, " + "batt ovp limit %dmv\n", + vbatt_volt, chip->batt_ovp_lmt); + mmi_chrg_sm_move_state(chip, PM_STATE_STOP_CHARGE); + } + + if (!vbus_pres) { + mmi_chrg_sm_move_state(chip, PM_STATE_DISCONNECT); + } else if (sm_state == PM_STATE_ENTRY + || sm_state == PM_STATE_STOP_CHARGE) { + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + } else if (sm_state == PM_STATE_DISCONNECT) { + mmi_get_input_current_settled(chrg_list->chrg_dev[PMIC_SW], + &chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } else if (zone_change && + chip->pres_temp_zone != ZONE_COLD && + chip->pres_temp_zone != ZONE_HOT) { + + if (batt_temp >= chrg_step->temp_c) { + mmi_chrg_info(chip, "battery temp %d, temp thre %d " + "Enter into COOLING LOOP !\n", + batt_temp, chrg_step->temp_c); + chip->batt_therm_cooling = true; + chip->pd_batt_therm_volt = chip->pd_request_volt_prev; + chip->pd_batt_therm_curr = chip->pd_request_curr_prev; + } else if (!chip->batt_therm_cooling && + !ignore_hysteresis_degc) { + mmi_chrg_info(chip, "battery temp %d, temp thre %d " + "Restart select chrg step and temp zone !\n", + batt_temp, chrg_step->temp_c); + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } + } + + mmi_chrg_dbg(chip, PR_MOTO, "temp zone %d, is_changed %d, " + "chrg_step %d, " + "step cc curr %d, step cv volt %d, " + "step cv tapper curr %d\n", + chip->pres_temp_zone, zone_change, + chrg_step->pres_chrg_step, + chrg_step->chrg_step_cc_curr, + chrg_step->chrg_step_cv_volt, + chrg_step->chrg_step_cv_tapper_curr); + switch (sm_state) { + case PM_STATE_DISCONNECT: + mmi_chrg_info(chip,"vbus disconnect !, jump to PM_STATE_DISCONNECT," + "recovery PMIC-SW limitation, and close CP charg\n"); + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + heartbeat_dely_ms = HEARTBEAT_CANCEL; + break; + case PM_STATE_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chip->pd_pps_support + && chrg_list->cp_master + && chrg_step->pres_chrg_step != chip->chrg_step_nums - 1 + && chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min + && batt_soc < CP_CHRG_SOC_LIMIT) { + + mmi_chrg_dbg(chip, PR_MOTO, "Enter into CHRG PUMP, " + "vbatt %d uV, " + "pps support %d, " + "chrg step %d, " + "chrg step cc curr %d uA, " + "CP master charging curr min %d uA\n", + vbatt_volt, + chip->pd_pps_support, + chrg_step->pres_chrg_step, + chrg_step->chrg_step_cc_curr, + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min); + mmi_chrg_sm_move_state(chip, PM_STATE_CHRG_PUMP_ENTRY); + + } else { + mmi_chrg_dbg(chip, PR_MOTO, "Enter into PMIC switch charging, " + "the reason is : vbatt %d uV, " + "pps support %d, " + "chrg step %d\n", + vbatt_volt, + chip->pd_pps_support, + chrg_step->pres_chrg_step); + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + + } + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt= 0; + chip->pd_sys_therm_volt= chip->pd_request_volt_prev; + chip->pd_sys_therm_curr = chip->pd_request_curr_prev; + chip->pd_batt_therm_volt = chip->pd_request_volt_prev; + chip->pd_batt_therm_curr = chip->pd_request_curr_prev; + chip->sys_therm_cooling = false; + chip->sys_therm_force_pmic_chrg = false; + chip->recovery_pmic_chrg = false; + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_SW_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip, "Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + if (!chip->dont_rerun_aicl) { + mmi_chrg_info(chip, "Do an rerun usb AICL for PMIC-SW\n"); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], false); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], true); + } + mmi_chrg_info(chip, "Check all effective pdo info again\n"); + + rc = adapter_dev_get_cap(chip->pd_adapter_dev, MMI_PD_ALL, &chip->mmi_pdo_info); + if (MMI_ADAPTER_OK != rc) { + mmi_chrg_err(chip, "Couldn't get pdo rc = %d\n", rc); + } + + mmi_chrg_info(chip, "Select FIXED pdo for switch charging !\n"); + for (i = 0; i < chip->mmi_pdo_info.nr; i++) { + mmi_chrg_info(chip,"find pdo %d, max volt %d, max curr %d\n", + chip->mmi_pdo_info.type[i], + chip->mmi_pdo_info.max_mv[i], + chip->mmi_pdo_info.ma[i]); + if (chip->mmi_pdo_info.type[i] == MMI_PD_FIXED + && (chip->mmi_pdo_info.max_mv[i] * 1000) >= SWITCH_CHARGER_PPS_VOLT + && (chip->mmi_pdo_info.ma[i] * 1000) >= TYPEC_HIGH_CURRENT_UA) { + mmi_chrg_dbg(chip, PR_MOTO, "select 5V/3A pps, pdo %d\n", i); + chip->mmi_pd_pdo_idx = i ; + break; + } + } + + chip->pd_request_volt = SWITCH_CHARGER_PPS_VOLT; + chip->pd_request_curr = TYPEC_HIGH_CURRENT_UA; + mmi_chrg_info(chip,"Select pdo %d, pd request curr %d, volt %d\n", + chip->mmi_pd_pdo_idx, + chip->pd_request_curr, + chip->pd_request_volt); + + mmi_chrg_sm_move_state(chip, PM_STATE_SW_LOOP); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_SW_LOOP: + if (chip->pd_pps_support + && chip->cp_disable == false + && chrg_step->pres_chrg_step != chip->chrg_step_nums - 1 + && chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min + && !chip->recovery_pmic_chrg + && !chip->sys_therm_force_pmic_chrg + && batt_soc < CP_CHRG_SOC_LIMIT) { + mmi_chrg_info(chip, "Enter CP, the reason is : " + "pd pps support %d, " + "vbatt %duV, chrg step %d\n", + chip->pd_pps_support, + vbatt_volt, chrg_step->pres_chrg_step); + mmi_chrg_sm_move_state(chip, PM_STATE_CHRG_PUMP_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + mmi_chrg_dbg(chip, PR_MOTO, "Continue to SW charging, " + "vbatt %d uV, ibatt %d uA\n", + vbatt_volt, ibatt_curr); + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + } + break; + case PM_STATE_CHRG_PUMP_ENTRY: + mmi_chrg_info(chip,"CP master exist %d, CP slave exist %d !\n", + chrg_list->cp_master, + chrg_list->cp_slave); + if (chrg_list->cp_slave) { + mmi_chrg_info(chip,"CP slave is exist !\n"); + mmi_chrg_info(chip,"chrg step cc curr %d uA, " + "CP slave charging curr min %d uA\n", + chrg_step->chrg_step_cc_curr, + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min); + if (chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min) { + mmi_chrg_sm_move_state(chip, + PM_STATE_DULE_CP_ENTRY); + } else + mmi_chrg_sm_move_state(chip, + PM_STATE_SINGLE_CP_ENTRY); + } else { + mmi_chrg_info(chip,"CP slave isn't exist !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_SINGLE_CP_ENTRY); + } + + mmi_chrg_info(chip,"Set PMIC SW FCC limits!\n"); + if (!chrg_list->chrg_dev[PMIC_SW]->charger_limited + && chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited > 0) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited); + mmi_chrg_info(chip,"Set PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = true; + } + + mmi_chrg_info(chip, "Check all effective pdo info again\n"); + + mmi_charger_update_pd_capacity(chip); + + /*Initial setup pps request power by the battery voltage*/ + chip->pd_request_volt = (2 * vbatt_volt) % 20000; + chip->pd_request_volt = 2 * vbatt_volt - chip->pd_request_volt + + chip->pps_volt_comp; + chip->pd_request_curr = + min(chip->pd_curr_max, chip->typec_middle_current); + mmi_chrg_info(chip,"pps init , volt %dmV, curr %dmA, volt comp %dmv\n", + chip->pd_request_volt, chip->pd_request_curr, chip->pps_volt_comp); + chrg_policy_error_clear(chip, chrg_list); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_SINGLE_CP_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_chrg_info(chip,"Disable Slave Charger Pump !\n"); + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Enable Master Charger Pump !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_PPS_TUNNING_CURR); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_STATE_DULE_CP_ENTRY: + if (chrg_list->cp_master) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Enable Master Charger Pump !\n"); + } + + if (chrg_list->cp_slave) { + chrg_list->cp_clave_later = true; + mmi_chrg_info(chip,"For ibus UCP, " + "delay start Slave Charger Pump in TUNNING_VOLT stage !\n"); + } + + if (chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_sm_move_state(chip, PM_STATE_PPS_TUNNING_CURR); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_STATE_PPS_TUNNING_CURR: + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + if (chrg_list->cp_master + && (!chrg_list->chrg_dev[CP_MASTER]->charger_enabled + || (chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into " + "SW directly\n"); + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + }else if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + if (chip->pd_request_curr - chip->pps_curr_steps + > chip->typec_middle_current) + chip->pd_request_curr -= chip->pps_curr_steps; + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_chrg_info(chip,"During the curr going up process, " + "the chrg step was changed," + "stop increase pps curr and Enter into " + "CC stage as soon!\n"); + } else if (chip->pps_result < 0) { + if (mmi_get_pps_result_history(chip) != NO_ERROR) { + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_chrg_info(chip,"Too many pdo request failed," + " Enter into CC stage directly!\n"); + mmi_clear_pps_result_history(chip); + } + chip->pd_request_curr = chip->pd_request_curr_prev; + goto schedule; + } else if (chip->pd_request_curr + chip->pps_curr_steps + <= chip->pd_curr_max + && vbatt_volt < chrg_step->chrg_step_cv_volt + && ibatt_curr < chrg_step->chrg_step_cc_curr) { + chip->pd_request_curr += chip->pps_curr_steps; + mmi_chrg_dbg(chip, PR_MOTO, "Increase pps curr %d\n", + chip->pd_request_curr); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + } else { + mmi_chrg_info(chip,"Enter into tunning pps volt\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_PPS_TUNNING_VOLT); + } + break; + case PM_STATE_PPS_TUNNING_VOLT: + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + if (chrg_list->cp_slave + && chrg_list->cp_clave_later + && !chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], true); + mmi_chrg_info(chip,"Enable Slave Charger Pump !\n"); + } + + if (chrg_list->cp_master + && (!chrg_list->chrg_dev[CP_MASTER]->charger_enabled + || (chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, " + "Enter into SW directly\n"); + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + } else if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + chip->pd_request_volt -= chip->pps_volt_steps; + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_chrg_info(chip,"Duing the volt going up process, " + "the chrg step was changed," + "stop increase pps volt and" + " Enter into CC stage as soon!\n"); + } else if (chip->pps_result < 0) { + if (mmi_get_pps_result_history(chip) != NO_ERROR) { + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_clear_pps_result_history(chip); + mmi_chrg_info(chip,"Too many pdo request failed," + " Enter into CC stage directly!\n"); + } + chip->pd_request_volt = chip->pd_request_volt_prev; + goto schedule; + } else if (chip->pd_request_volt + chip->pps_volt_steps + <= chip->pd_volt_max + && vbatt_volt < chrg_step->chrg_step_cv_volt + && ibatt_curr < ((chrg_step->pres_chrg_step == STEP_FIRST) ? + chrg_step->chrg_step_cc_curr + chip->step_first_curr_comp: + chrg_step->chrg_step_cc_curr)) { + chip->pd_request_volt += chip->pps_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, "Increase pps volt %d\n", + chip->pd_request_volt); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + } else { + mmi_chrg_info(chip,"Enter into CC loop stage !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_CC_LOOP); + } + + if (ibatt_curr > batt_curr_roof) { + batt_curr_roof = ibatt_curr; + } + if (ibatt_curr < batt_curr_roof) + pd_constant_power_cnt++; + else + pd_constant_power_cnt = 0; + + if (pd_constant_power_cnt > PD_CONT_PWR_CNT) { + mmi_chrg_info(chip,"PD adapter was ready in constant power state, " + "Enter into CC loop stage !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_CC_LOOP); + } + mmi_chrg_info(chip, "pd_constant_power_cnt %d, " + "batt_curr_roof %d\n", + pd_constant_power_cnt, + batt_curr_roof); + break; + case PM_STATE_CP_CC_LOOP: + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CC loop, chrg_step %d, " + "vbatt %dmV, ibatt %dmA, " + "CC target curr %dmA, " + "next CV target volt %dmV\n ", + chrg_step->pres_chrg_step, + vbatt_volt, ibatt_curr, + chrg_step->chrg_step_cc_curr, + chrg_step->chrg_step_cv_volt); + if (chrg_list->cp_master + && (!chrg_list->chrg_dev[CP_MASTER]->charger_enabled + || (chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into SW directly\n"); + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + goto schedule; + } + + if (chip->pps_result < 0) { + mmi_chrg_err(chip, "Last select pdo failed\n"); + chip->pps_result = mmi_get_pps_result_history(chip); + switch (chip->pps_result) { + case BLANCE_POWER: + chip->pd_pps_balance = true; + mmi_chrg_err(chip, "Enable pps power balance\n"); + mmi_clear_pps_result_history(chip); + break; + case RESET_POWER: + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + mmi_chrg_err(chip, "Hard reset charger policy to recovery power," + "Since too many pdo failed\n"); + mmi_clear_pps_result_history(chip); + break; + default: + break; + } + + chip->pd_request_volt = chip->pd_request_volt_prev; + if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->pd_request_volt < chip->pd_volt_max) + chrg_cc_power_tunning_cnt = + CC_POWER_COUNT; + goto schedule; + } + + if (!chip->sys_therm_cooling + && !chip->batt_therm_cooling + && pd_constant_power_cnt <= PD_CONT_PWR_CNT) { + if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->pd_request_volt < chip->pd_volt_max + && chrg_cc_power_tunning_cnt >= + CC_POWER_COUNT) { + if (chip->pd_pps_balance + && (chip->pd_request_curr - chip->pps_curr_steps) + > chip->pd_allow_min_current) { + chip->pd_request_curr -= + chip->pps_curr_steps; + chip->pd_request_volt += + mmi_calculate_delta_volt(chip->pd_request_volt_prev, + chip->pd_request_curr_prev, + chip->pps_curr_steps); + mmi_chrg_dbg(chip, PR_MOTO, + "Request curr decreass %dmA, " + "Request volt increase %dmV \n", + chip->pd_request_curr, + chip->pd_request_volt); + } else { + if (chip->pd_request_curr + chip->pps_curr_steps + < chip->pd_curr_max) { + chip->pd_request_curr += + chip->pps_curr_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "Request curr decreass %dmA\n ", + chip->pd_request_curr); + } else if (chip->pd_request_volt + chip->pps_volt_steps + < chip->pd_volt_max) { + chip->pd_request_volt += + chip->pps_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "Request volt decreass %duV\n ", + chip->pd_request_volt); + } + } + chrg_cc_power_tunning_cnt = 0; + mmi_chrg_dbg(chip, PR_MOTO, + "For keeping CC chrg POWER, " + "Have to adjust pd input volt or curr" + "Request volt %dmV, " + "Request curr %dmA\n", + chip->pd_request_volt, + chip->pd_request_curr); + }else if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->pd_request_volt < chip->pd_volt_max) { + chrg_cc_power_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CC tunning cnt %d\n", + chrg_cc_power_tunning_cnt); + } else if (ibatt_curr > chrg_step->chrg_step_cc_curr + + CC_CURR_DEBOUNCE) { + chip->pd_request_volt -= chip->pps_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "In the CC step , the ibatt is greater than CC curr, " + "Request volt decreass %duV to remain CC step\n ", + chip->pd_request_volt); + } else + chrg_cc_power_tunning_cnt = 0; + + if(ibatt_curr < chrg_step->chrg_step_cc_curr) + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + } + + if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + if (chrg_cv_taper_tunning_cnt > + CV_TAPPER_COUNT) { + mmi_chrg_sm_move_state(chip, PM_STATE_CP_CV_LOOP); + chrg_cv_taper_tunning_cnt = 0; + chrg_cv_delta_volt = CV_DELTA_VOLT; + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV taper cnt %d, " + "chrg step cv volt %dmV, " + "vbatt %dmV\n", + chrg_cv_taper_tunning_cnt, + chrg_step->chrg_step_cv_volt, + vbatt_volt); + } + } else + chrg_cv_taper_tunning_cnt = 0; + + break; + case PM_STATE_CP_CV_LOOP: + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV loop, chrg_step %d, " + "vbatt %dmV, ibatt %dmA, " + "CV target volt %dmV, " + "CV taper curr %dmA\n ", + chrg_step->pres_chrg_step, + vbatt_volt, ibatt_curr, + chrg_step->chrg_step_cv_volt, + chrg_step->chrg_step_cv_tapper_curr); + if (chrg_list->cp_master + && (!chrg_list->chrg_dev[CP_MASTER]->charger_enabled + || (chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into SW directly\n"); + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + goto schedule; + } + + if (chip->pps_result < 0) { + mmi_chrg_err(chip, "Last select pdo failed\n"); + chip->pps_result = mmi_get_pps_result_history(chip); + switch (chip->pps_result) { + case BLANCE_POWER: + chip->pd_request_curr -= + chip->pps_curr_steps; + mmi_chrg_err(chip, "Reduce pps curr,for pps power balance\n"); + mmi_clear_pps_result_history(chip); + break; + case RESET_POWER: + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + mmi_chrg_err(chip, "Hard reset charger policy to recovery power," + "Since too many pdo failed\n"); + mmi_clear_pps_result_history(chip); + break; + default: + break; + } + chip->pd_request_volt = chip->pd_request_volt_prev; + goto schedule; + } + + if (vbatt_volt >= chrg_step->chrg_step_cv_volt + && ((!chrg_step->last_step && + ibatt_curr < chrg_step->chrg_step_cv_tapper_curr) + || ibatt_curr < chrg_list->chrg_dev[CP_MASTER]->charging_curr_min)) { + if (chrg_cv_taper_tunning_cnt >= CV_TAPPER_COUNT) { + if (ibatt_curr < + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min) { + mmi_chrg_info(chip, "Ready quite CP chrg stage, " + "and Enter into PMIC switch chrg stage, " + "chrg step %d, ibatt %dmA\n", + chrg_step->pres_chrg_step, ibatt_curr); + mmi_find_chrg_step(chip, + chip->pres_temp_zone, vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_QUIT); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + if (mmi_find_chrg_step(chip, + chip->pres_temp_zone, vbatt_volt)) { + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + mmi_chrg_info(chip,"Jump to next chrg step\n"); + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + } else { + mmi_chrg_info(chip,"Can't find next chrg step\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_QUIT); + } + } + chrg_cv_taper_tunning_cnt = 0; + } else { + chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, "chrg cv taper cnt ++, %d\n", + chrg_cv_taper_tunning_cnt); + } + + }else if (!chip->sys_therm_cooling + && !chip->batt_therm_cooling) { + + chrg_cv_taper_tunning_cnt = 0; + if (vbatt_volt > chrg_step->chrg_step_cv_volt + 10000) { + if (chrg_cv_delta_volt > 20000) + chip->pd_request_volt -= chrg_cv_delta_volt; + else + chip->pd_request_volt -= 20000; + mmi_chrg_info(chip, + "For keeping CV stage, decrease volt %dmV, " + "cv delta volt %dmV\n", + chip->pd_request_volt, chrg_cv_delta_volt); + } else if (vbatt_volt < chrg_step->chrg_step_cv_volt - 10000) { + chrg_cv_delta_volt -= 20000; + chip->pd_request_volt += 20000; + mmi_chrg_info(chip, + "For keeping CV stage, increase volt %dmV, " + "cv delta volt %dmV\n", + chip->pd_request_volt, chrg_cv_delta_volt); + } else { + mmi_chrg_dbg(chip, PR_MOTO, "CV loop work well, " + "keep pd power, volt %dmV, curr %dmA\n", + chip->pd_request_volt, chip->pd_request_curr); + } + + }else { + /*In this case, sys_therm_cooling or batt_therm_cooling is ture*/ + + if (vbatt_volt > chrg_step->chrg_step_cv_volt + 10000) { + if (chrg_cv_delta_volt > 20000) + chip->pd_request_volt -= chrg_cv_delta_volt; + else + chip->pd_request_volt -= 20000; + mmi_chrg_info(chip, + "For keeping CV stage, decrease volt %dmV, " + "cv delta volt %dmV\n", + chip->pd_request_volt, chrg_cv_delta_volt); + } else { + mmi_chrg_dbg(chip, PR_MOTO, "CV loop work well, " + "keep pd power, volt %dmV, curr %dmA\n", + chip->pd_request_volt, chip->pd_request_curr); + } + } + + if (chrg_list->cp_slave) { + if (quit_slave_chrg_cnt > 3 + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + mmi_chrg_info(chip,"Quit slave chrg, the reason is :ibatt %duA, " + "CP slave charging curr min %d uA\n", + ibatt_curr, + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Restart CP master again\n"); + } + + if (ibatt_curr < + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min) + quit_slave_chrg_cnt++; + else + quit_slave_chrg_cnt = 0; + } + break; + case PM_STATE_CP_QUIT: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + mmi_chrg_sm_move_state(chip, PM_STATE_RECOVERY_SW); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_RECOVERY_SW: + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + + if (!chip->dont_rerun_aicl) { + mmi_chrg_info(chip,"Do an rerun usb AICL for PMIC-SW\n"); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], false); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], true); + } + chip->recovery_pmic_chrg = true; + chrg_cv_taper_tunning_cnt = 0; + } + + chip->pd_request_curr = TYPEC_HIGH_CURRENT_UA; + mmi_chrg_info(chip,"ibatt : %dmA, step cc curr : %dmA\n", + ibatt_curr, chrg_step->chrg_step_cc_curr); + if (ibatt_curr > chrg_step->chrg_step_cc_curr) { + chip->pd_request_volt -= CV_DELTA_VOLT; + + mmi_chrg_dbg(chip, PR_MOTO, "Reduce pps volt %dmV, curr %dmA\n ", + chip->pd_request_volt, chip->pd_request_curr); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + chrg_cv_taper_tunning_cnt = 0; + } else { + chrg_cv_taper_tunning_cnt++; + + } + + if (chrg_cv_taper_tunning_cnt > CV_TAPPER_COUNT){ + mmi_chrg_sm_move_state(chip, PM_STATE_SW_LOOP); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_STATE_STOP_CHARGE: + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + if (chip->pres_temp_zone != ZONE_COLD + && chip->pres_temp_zone != ZONE_HOT + && chrg_list->chrg_dev[PMIC_SW]->charger_enabled + && chrg_step->chrg_step_cc_curr > 0) { + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + chip->pd_request_volt = SWITCH_CHARGER_PPS_VOLT; + chip->pd_request_curr = TYPEC_HIGH_CURRENT_UA; + break; + case PM_STATE_COOLING_LOOP: + mmi_chrg_info(chip,"In cooling loop, batt temp %d, cmp temp %d\n", + batt_temp, chrg_step->temp_c); + + if (batt_temp > chrg_step->temp_c + COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, Cooling loop failed, " + "force enter PM_STATE_ENTRY " + "restart this charger process !\n", + batt_temp); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + + } else if (batt_temp > chrg_step->temp_c) { + cooling_curr = + min(chip->pd_curr_max, chip->pd_allow_min_current); + if (chip->pd_request_curr > cooling_curr) { + chip->pd_request_curr -= COOLING_DELTA_POWER; + } else { + mmi_chrg_info(chip, "pd request curr %dmA, " + "battery temp %d, " + "cooling failed, Restart PM_STATE_ENTRY !\n", + chip->pd_request_curr, batt_temp); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + } + + } else if (batt_temp < chrg_step->temp_c - COOLING_DELTA_POWER) { + + mmi_chrg_info(chip,"Batt temp %d, successful exit from COOLing loop, " + "and Enter int TUNNING_CURR again !\n", + batt_temp); + mmi_chrg_sm_move_state(chip, + PM_STATE_PPS_TUNNING_CURR); + } + + if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + if (chrg_cv_taper_tunning_cnt > + CV_TAPPER_COUNT) { + + mmi_chrg_info(chip, "vbatt_volt %dmV, " + "battery temp %d, " + "chrg step cv volt %dmV" + "cooling failed, Restart PM_STATE_ENTRY !\n", + vbatt_volt, batt_temp, chrg_step->chrg_step_cv_volt); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + chrg_cv_taper_tunning_cnt = 0; + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV taper cnt %d, " + "chrg step cv volt %dmV, " + "vbatt %dmV\n", + chrg_cv_taper_tunning_cnt, + chrg_step->chrg_step_cv_volt, + vbatt_volt); + } + } else + chrg_cv_taper_tunning_cnt = 0; + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + break; + } + +schedule: + + chip->pd_target_volt = min(chip->pd_request_volt, chip->pd_volt_max); + chip->pd_target_curr = min(chip->pd_request_curr, chip->pd_curr_max); + + if (chip->system_thermal_level == 0) { + chip->sys_therm_cooling= false; + chip->sys_therm_force_pmic_chrg = false; + } else if ((chip->system_thermal_level == chip->thermal_levels - 1) + && !chip->sys_therm_force_pmic_chrg) { + chip->sys_therm_cooling = true; + chip->sys_therm_force_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + mmi_chrg_info(chip, "Thermal is the highest, level %d, " + "Force enter into single pmic charging !\n", + chip->system_thermal_level); + + } else if (chip->system_thermal_level > 0 && + (sm_state == PM_STATE_CP_CC_LOOP || + sm_state == PM_STATE_CP_CV_LOOP)) { + + mmi_chrg_dbg(chip, PR_MOTO, "Thermal level is %d\n", + chip->system_thermal_level); + if (!chip->sys_therm_cooling) { + chip->sys_therm_cooling = true; + chip->pd_sys_therm_volt = chip->pd_request_volt_prev; + chip->pd_sys_therm_curr = chip->pd_request_curr_prev; + } + + if (ibatt_curr > + chip->thermal_mitigation[chip->system_thermal_level] + + CC_CURR_DEBOUNCE) { + if (chip->pd_sys_therm_curr - THERMAL_TUNNING_CURR >= + chip->pd_allow_min_current) { + chip->pd_sys_therm_curr -= THERMAL_TUNNING_CURR; + mmi_chrg_dbg(chip, PR_MOTO, "For thermal, decrease pps curr %d\n", + chip->pd_sys_therm_curr); + + } else { + mmi_chrg_dbg(chip, PR_MOTO, + "pd_sys_therm_curr %dmA was less than %dmA, " + "Give up thermal mitigation!", + chip->pd_sys_therm_curr - THERMAL_TUNNING_CURR, + chip->pd_allow_min_current); + } + } else if (ibatt_curr < + chip->thermal_mitigation[chip->system_thermal_level] + - CC_CURR_DEBOUNCE) { + if (chip->pd_sys_therm_curr + THERMAL_TUNNING_CURR <= + chip->pd_curr_max) { + chip->pd_sys_therm_curr += THERMAL_TUNNING_CURR; + mmi_chrg_dbg(chip, PR_MOTO, "For thermal, increase pps curr %d\n", + chip->pd_sys_therm_curr); + } + } + + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + } else if (chip->system_thermal_level > 0 && + chip->system_thermal_level != chip->thermal_levels - 1 && + sm_state == PM_STATE_SW_LOOP && + chip->sys_therm_force_pmic_chrg) { + mmi_chrg_dbg(chip, PR_MOTO, "Try to recovery charger pump!\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } + + if (chip->sys_therm_cooling) { + chip->pd_target_volt = min(chip->pd_target_volt, chip->pd_sys_therm_volt); + chip->pd_target_curr = min(chip->pd_target_curr, chip->pd_sys_therm_curr); + } + + if (chip->batt_therm_cooling && !chip->sys_therm_force_pmic_chrg) { + if (batt_temp > chrg_step->temp_c + COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, Cooling loop failed, " + "force enter PM_STATE_ENTRY " + "restart this charger process !\n", + batt_temp); + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt= 0; + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + + } else if (batt_temp > chrg_step->temp_c) { + cooling_curr = + min(chip->pd_curr_max, chip->pd_allow_min_current); + cooling_volt = (2 * vbatt_volt) % 20000; + cooling_volt = 2 * vbatt_volt - cooling_volt + + chip->pps_volt_comp; + if (ibatt_curr > TYPEC_HIGH_CURRENT_UA + && chip->pd_batt_therm_curr > cooling_curr) { + + if (chip->pd_batt_therm_curr - COOLING_DELTA_POWER >= + chip->pd_allow_min_current) + chip->pd_batt_therm_curr -= COOLING_DELTA_POWER; + mmi_chrg_info(chip, "Do chrg power cooling" + "pd_batt_therm_curr %dmA, " + "battery temp %d\n", + chip->pd_batt_therm_curr, batt_temp); + } else if (ibatt_curr > TYPEC_HIGH_CURRENT_UA + && chip->pd_batt_therm_volt > cooling_volt) { + chip->pd_batt_therm_volt -= COOLING_DELTA_POWER; + mmi_chrg_info(chip, "Do chrg power cooling" + "pd request volt %dmA, " + "battery temp %d\n", + chip->pd_batt_therm_volt, batt_temp); + + } else { + if (chip->batt_therm_cooling_cnt > COOLING_MAX_CNT) { + + mmi_chrg_info(chip, "Do chrg power cooling failed" + "pd_batt_therm_currr %dmA, " + "pd_batt_therm_volt %dmV" + "battery temp %d, " + "Restart PM_STATE_ENTRY !\n", + chip->pd_batt_therm_curr, + chip->pd_batt_therm_volt, + batt_temp); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + + } else { + mmi_chrg_info(chip, "It's already the lowest cooling chrg power" + "waiting for a while, cooling cnt %d, " + "battery temp %d\n", + batt_temp, chip->batt_therm_cooling); + chip->batt_therm_cooling_cnt++; + } + } + + } else if (batt_temp < chrg_step->temp_c - COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, " + "Exit successfully from COOLing loop!\n", + batt_temp); + + if (sm_state == PM_STATE_CP_CC_LOOP) { + mmi_chrg_info(chip,"Jump into CURR tunning" + "for chrg power poerformance!\n"); + mmi_chrg_sm_move_state(chip, + PM_STATE_PPS_TUNNING_CURR); + } + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + } + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + } + + if (chip->batt_therm_cooling) { + chip->pd_target_volt = min(chip->pd_target_volt, chip->pd_batt_therm_volt); + chip->pd_target_curr = min(chip->pd_target_curr, chip->pd_batt_therm_curr); + } + + mmi_chrg_dbg(chip, PR_MOTO, "chrg sm work,%s, " + "battery soc %d, " + "battery temp %d, " + "battery current %d, " + "battery voltage %d, " + "sys therm level %d, " + "pmic therm level %d, " + "sys therm cooling %d, " + "batt therm cooling %d, " + "sys therm force pmic chrg %d, " + "recovery pmic chrg %d\n", + pm_state_str[sm_state], + batt_soc, batt_temp, + ibatt_curr, vbatt_volt, + chip->system_thermal_level, + pmic_sys_therm_level, + chip->sys_therm_cooling, + chip->batt_therm_cooling, + chip->sys_therm_force_pmic_chrg, + chip->recovery_pmic_chrg); + + mmi_chrg_dbg(chip, PR_MOTO, "pd request volt %dmV, " + "pd request curr %dmA, " + "pd target volt %dmV, " + "pd target curr %dmA, " + "sys therm volt %dmV, " + "sys therm curr %dmA, " + "batt therm volt %dmV, " + "batt therm curr %dmA\n", + chip->pd_request_volt, + chip->pd_request_curr, + chip->pd_target_volt, + chip->pd_target_curr, + chip->pd_sys_therm_volt, + chip->pd_sys_therm_curr, + chip->pd_batt_therm_volt, + chip->pd_batt_therm_curr); + + if (chip->pd_target_volt < SWITCH_CHARGER_PPS_VOLT + || chip->pd_target_curr < chip->pd_allow_min_current) { + + if (sm_state == PM_STATE_PPS_TUNNING_CURR + || sm_state == PM_STATE_PPS_TUNNING_VOLT + || sm_state == PM_STATE_CP_CC_LOOP + || sm_state == PM_STATE_CP_CV_LOOP) { + + mmi_chrg_err(chip, "%s, wrong pd voltage or current , " + "request pd voltage %d, " + "request pd current %d\n", + pm_state_str[sm_state], + chip->pd_target_volt, + chip->pd_target_curr); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } + goto skip_pd_select; + } + + if (!chip->pps_start) { + chip->pps_result = adapter_dev_set_cap(chip->pd_adapter_dev, MMI_PD_APDO_START, + chip->mmi_pd_pdo_idx, + chip->pd_target_volt / 1000, + chip->pd_target_curr / 1000); + if (chip->pps_result != MMI_ADAPTER_OK) + mmi_chrg_err(chip, "set pd30 cap start fail ret=%d\n", chip->pps_result); + else + chip->pps_start = true; + } else { + chip->pps_result = adapter_dev_set_cap(chip->pd_adapter_dev, MMI_PD_APDO, + chip->mmi_pd_pdo_idx, + chip->pd_target_volt / 1000, + chip->pd_target_curr / 1000); + if (chip->pps_result != MMI_ADAPTER_OK) + mmi_chrg_err(chip, "set pd30 cap fail ret=%d\n", chip->pps_result); + } + + mmi_set_pps_result_history(chip, chip->pps_result); + if (!chip->pps_result) { + chip->pd_request_volt_prev = chip->pd_target_volt; + chip->pd_request_curr_prev = chip->pd_target_curr; + } + +skip_pd_select: + + if (heartbeat_dely_ms > 0) { + mmi_chrg_err(chip, "schedule work timer %dms\n", heartbeat_dely_ms); + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(heartbeat_dely_ms)); + } else { + mmi_chrg_policy_clear(chip); + chip->sm_work_running = false; + chip->pd_pps_support = false; + mmi_chrg_err(chip, "exit sm work\n"); + } + + return; +} + + diff --git a/drivers/power/mmi_discrete_turbo_charger/mmi_cp_charger.c b/drivers/power/mmi_discrete_turbo_charger/mmi_cp_charger.c new file mode 100644 index 000000000000..625aba039355 --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_cp_charger.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include +#include "mmi_charger_core.h" +#include "mmi_charger_core_iio.h" + +static int is_cp_slave_pump(struct mmi_charger_device *chrg) +{ + if (strcmp(chrg->name, "cp-slave") == 0) + return true; + else + return false; +} + +static int cp_enable_charging(struct mmi_charger_device *chrg, bool en) +{ + int rc; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + if (is_cp_slave_pump(chrg)) + rc = mmi_charger_write_iio_chan(chip, CP_SLAVE_ENABLE, en); + else + rc = mmi_charger_write_iio_chan(chip, CP_ENABLE, en); + + if (!rc) { + chrg->charger_enabled = !!en; + } else + chrg->charger_enabled = false; + + if (!is_cp_slave_pump(chrg)) { + rc = mmi_charger_write_iio_chan(chip, MMI_USB_TERMINATION_ENABLED, !chrg->charger_enabled); + if(rc) + chrg_dev_info(chrg, "%s, enable termination fail\n",__func__); + } + + chrg_dev_info(chrg, "%s end, en:%d\n",__func__,en); + return rc; +} + +static int cp_is_charging_enabled(struct mmi_charger_device *chrg, bool *en) +{ + int rc; + int value; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + if (is_cp_slave_pump(chrg)) + rc = mmi_charger_read_iio_chan(chip, CP_SLAVE_ENABLE, &value); + else + rc = mmi_charger_read_iio_chan(chip, CP_ENABLE, &value); + + if (!rc) { + chrg->charger_enabled = !!value; + } else + chrg->charger_enabled = false; + + *en = chrg->charger_enabled; + chrg_dev_info(chrg, "%s end, en:%d\n",__func__,*en); + return rc; +} + +static int cp_get_charging_current(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + *uA = !!prop.intval; + chrg_dev_info(chrg, "%s end, uA:%d\n",__func__,*uA); + return rc; +} + +static int cp_get_vbus(struct mmi_charger_device *chrg, u32 *mv) +{ + int rc, val; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + if (!chip) + return -ENODEV; + + if (is_cp_slave_pump(chrg)) + rc = mmi_charger_read_iio_chan(chip, CP_SLAVE_INPUT_VOLTAGE_NOW, &val); + else + rc = mmi_charger_read_iio_chan(chip, CP_INPUT_VOLTAGE_NOW, &val); + + if (!rc) + *mv = val; + chrg_dev_info(chrg, "%s end, mv:%d\n",__func__,*mv); + return rc; +} + +static int cp_get_input_current(struct mmi_charger_device *chrg, u32 *uA) //ibus +{ + int rc; + int value; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + if (is_cp_slave_pump(chrg)) + rc = mmi_charger_read_iio_chan(chip, CP_SLAVE_INPUT_CURRENT_NOW, &value); + else + rc = mmi_charger_read_iio_chan(chip, CP_INPUT_CURRENT_NOW, &value); + + if (!rc) + *uA = value; + chrg_dev_info(chrg, "%s end, uA:%d\n",__func__,*uA); + return rc; +} + +static int cp_update_charger_status(struct mmi_charger_device *chrg) +{ + int rc; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + union power_supply_propval prop = {0,}; + + if (!chip) + return -ENODEV; + + if (!chrg->chrg_psy) + return -ENODEV; + + if (!chip->batt_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &prop); + if (!rc) + chrg->charger_data.ibus_curr= prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + chrg->charger_data.vbatt_volt = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + chrg->charger_data.ibatt_curr = prop.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_TEMP, &prop); + if (!rc) + chrg->charger_data.batt_temp = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (!rc) + chrg->charger_data.vbus_pres = !!prop.intval; + + if (is_cp_slave_pump(chrg)) { + + rc = mmi_charger_read_iio_chan(chip, CP_SLAVE_INPUT_VOLTAGE_NOW, &prop.intval); + if (!rc) + chrg->charger_data.vbus_volt = prop.intval; + + rc = mmi_charger_read_iio_chan(chip, CP_SLAVE_INPUT_CURRENT_NOW, &prop.intval); + if (!rc) + chrg->charger_data.ibus_curr = prop.intval; + + rc = mmi_charger_read_iio_chan(chip, CP_SLAVE_ENABLE, &prop.intval); + if (!rc) + chrg->charger_enabled = !!prop.intval; + + } else { + + rc = mmi_charger_read_iio_chan(chip, CP_INPUT_VOLTAGE_NOW, &prop.intval); + if (!rc) + chrg->charger_data.vbus_volt = prop.intval; + + rc = mmi_charger_read_iio_chan(chip, CP_INPUT_CURRENT_NOW, &prop.intval); + if (!rc) + chrg->charger_data.ibus_curr = prop.intval; + + rc = mmi_charger_read_iio_chan(chip, CP_ENABLE, &prop.intval); + if (!rc) + chrg->charger_enabled = !!prop.intval; + } + + + chrg_dev_info(chrg, "CP chrg: %s status update: --- info---\n",chrg->name); + chrg_dev_info(chrg, "vbatt %d\n", chrg->charger_data.vbatt_volt); + chrg_dev_info(chrg, "ibatt %d\n", chrg->charger_data.ibatt_curr); + chrg_dev_info(chrg, "batt temp %d\n", chrg->charger_data.batt_temp); + chrg_dev_info(chrg, "vbus %d\n", chrg->charger_data.vbus_volt); + chrg_dev_info(chrg, "ibus %d\n", chrg->charger_data.ibus_curr); + chrg_dev_info(chrg, "vbus pres %d\n", chrg->charger_data.vbus_pres); + chrg_dev_info(chrg, "charger_enabled %d\n", chrg->charger_enabled); + + return rc; +} + +static int cp_update_charger_error_status(struct mmi_charger_device *chrg) +{ + int rc; + int value = 0; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + if (is_cp_slave_pump(chrg)) + rc = mmi_charger_read_iio_chan(chip, CP_SLAVE_STATUS1, &value); + else + rc = mmi_charger_read_iio_chan(chip, CP_STATUS1, &value); + + if (!rc) { + chrg->charger_error.chrg_err_type = value; + } + chrg_dev_info(chrg, "%s end, status:%d\n",__func__,value); + return rc; +} + +static int cp_clear_charger_error(struct mmi_charger_device *chrg) +{ + int rc; + int value = 0; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + if (is_cp_slave_pump(chrg)) + rc = mmi_charger_write_iio_chan(chip, CP_SLAVE_CLEAR_ERROR, value); + else + rc = mmi_charger_write_iio_chan(chip, CP_CLEAR_ERROR, value); + + chrg_dev_info(chrg, "%s end, status:%d\n",__func__,value); + return rc; +} + +struct mmi_charger_ops cp_charger_ops = { + .enable = cp_enable_charging, + .is_enabled = cp_is_charging_enabled, + .get_charging_current = cp_get_charging_current, + .get_vbus = cp_get_vbus, + .get_input_current = cp_get_input_current, + .update_charger_status = cp_update_charger_status, + .update_charger_error = cp_update_charger_error_status, + .clear_charger_error = cp_clear_charger_error, +}; + diff --git a/drivers/power/mmi_discrete_turbo_charger/mmi_qc3p.c b/drivers/power/mmi_discrete_turbo_charger/mmi_qc3p.c new file mode 100644 index 000000000000..f2796be95019 --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_qc3p.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2012 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 "mmi_charger_core.h" +#include "mmi_charger_core_iio.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define USB_VBUS_IRQ_THRESHOLD 3800 +bool mmi_qc3p_power_active(struct mmi_charger_manager *chg) +{ + int ret, iio_val; + + ret = mmi_charger_read_iio_chan(chg, SMB5_USB_REAL_TYPE, &iio_val); + if (ret < 0) { + mmi_chrg_err(chg, "Couldn't read charger type rc=%d\n", ret); + return false; + } +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED + if (iio_val != MMI_POWER_SUPPLY_QC3P_27W && iio_val != MMI_POWER_SUPPLY_QC3P_45W) + return false; +#else + if (iio_val != POWER_SUPPLY_TYPE_USB_HVDCP_3P5) + return false; +#endif + ret = mmi_charger_read_iio_chan(chg, SMB5_QC3P_POWER, &iio_val); + if (ret < 0) { + mmi_chrg_err(chg, "Couldn't read qc3p power rc=%d\n", ret); + return false; + } + chg->qc3p_power = iio_val; + mmi_chrg_info(chg, "qc3p_power =%d\n", chg->qc3p_power); + + if (chg->qc3p_power == MMI_POWER_SUPPLY_QC3P_27W || + chg->qc3p_power == MMI_POWER_SUPPLY_QC3P_45W) + return true; + else + return false; +} + +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED +int mmi_qc3p_set_vbus_voltage(struct mmi_charger_manager *chg, int target_mv) +{ + int rc = -EINVAL; + int curr_vbus_mv = 0, step = 0,curr_vbus_ma = 0; + int impendance_vubs_volt = 0; + int sleep_ms_wt = 0; + + rc = mmi_get_vbus(chg->chrg_list[CP_MASTER], + &curr_vbus_mv); + if (rc) { + mmi_chrg_err(chg, "Unable to read USB voltage: %d\n", rc); + return -EINVAL; + } + + rc = mmi_get_input_current(chg->chrg_list[CP_MASTER], + &curr_vbus_ma); + if (rc) { + mmi_chrg_err(chg, "Unable to read USB ma: %d\n", rc); + return -EINVAL; + } + +// impendance_vubs_volt = (DEFAULT_VBUS_RESISTANCE * curr_vbus_ma) / 1000; + impendance_vubs_volt = 0;//200; + curr_vbus_mv -= impendance_vubs_volt ; + + if(target_mv < curr_vbus_mv) { + step = (curr_vbus_mv - target_mv) / 20; + step = step *-1; + } else { + step = (target_mv - curr_vbus_mv) / 20; + } + + mmi_chrg_info(chg, "curr_vbus= %d, target_vbus = %d, step = %d, impent_v:%d\n",curr_vbus_mv, target_mv, step,impendance_vubs_volt); + if (chg->vbus_present && (curr_vbus_mv >= USB_VBUS_IRQ_THRESHOLD)) { + rc = mmi_charger_write_iio_chan(chg, SMB5_DP_DM, step); + if (rc < 0) { + mmi_chrg_err(chg, "Couldn't set dpdm pulse rc=%d\n", rc); + return -EINVAL; + } + + //need wait dp/dm send out + sleep_ms_wt = abs(step) * 10; + msleep(sleep_ms_wt); + } + + rc = mmi_get_vbus(chg->chrg_list[CP_MASTER], + &curr_vbus_mv); + if (rc) { + mmi_chrg_err(chg, "Unable to read VBUS: %d\n", rc); + return -EINVAL; + } + mmi_chrg_info(chg, "result_vbus= %d\n",curr_vbus_mv); + return curr_vbus_mv; +} + +#else + +#define QC3P_PULSE_COUNT_MAX ((11000 - 5000) / 20 + 10) +int mmi_qc3p_set_vbus_voltage(struct mmi_charger_manager *chg, int target_mv) +{ + int rc = -EINVAL; + union power_supply_propval val = {0, }; + int pulse_cnt, curr_vbus_mv, step, i; + + rc = mmi_charger_read_iio_chan(chg, SMB5_DP_DM, &val.intval); + if (rc < 0) { + mmi_chrg_err(chg, "Couldn't read dpdm pulse count rc=%d\n", rc); + return -EINVAL; + } else { + mmi_chrg_info(chg, "DP DM pulse count = %d\n", val.intval); + pulse_cnt = val.intval; + } + + rc = mmi_get_vbus(chg->chrg_list[CP_MASTER], + &curr_vbus_mv); + if (rc) { + mmi_chrg_err(chg, "Unable to read USB voltage: %d\n", rc); + return -EINVAL; + } + + if(target_mv < curr_vbus_mv) { + step = (curr_vbus_mv - target_mv) / 20; + val.intval = MMI_POWER_SUPPLY_DP_DM_DM_PULSE; + if (pulse_cnt <= step) + step = pulse_cnt; + } else { + step = (target_mv - curr_vbus_mv) / 20; + val.intval = MMI_POWER_SUPPLY_DP_DM_DP_PULSE; + if (step + pulse_cnt > QC3P_PULSE_COUNT_MAX) + step = QC3P_PULSE_COUNT_MAX - pulse_cnt; + } + + mmi_chrg_info(chg, "curr_vbus= %d, target_vbus = %d, step = %d\n",curr_vbus_mv, target_mv, step); + for (i = 0; i < step; i++) { + rc = mmi_charger_write_iio_chan(chg, SMB5_DP_DM, val.intval); + if (rc < 0) { + mmi_chrg_err(chg, "Couldn't set dpdm pulse rc=%d\n", rc); + break; + } + + if (i < step - 1) + udelay(5000); + } + + rc = mmi_get_vbus(chg->chrg_list[CP_MASTER], + &curr_vbus_mv); + if (rc) { + mmi_chrg_err(chg, "Unable to read VBUS: %d\n", rc); + return -EINVAL; + } + mmi_chrg_info(chg, "result_vbus= %d\n",curr_vbus_mv); + return curr_vbus_mv; +} +#endif + diff --git a/drivers/power/mmi_discrete_turbo_charger/mmi_qc3p.h b/drivers/power/mmi_discrete_turbo_charger/mmi_qc3p.h new file mode 100644 index 000000000000..7b3af65832c8 --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_qc3p.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + +extern bool mmi_qc3p_power_active(struct mmi_charger_manager *chg); +extern int mmi_qc3p_set_vbus_voltage(struct mmi_charger_manager *chg, int target_mv); + diff --git a/drivers/power/mmi_discrete_turbo_charger/mmi_qc3p_cp_policy.c b/drivers/power/mmi_discrete_turbo_charger/mmi_qc3p_cp_policy.c new file mode 100644 index 000000000000..b55818e23264 --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/mmi_qc3p_cp_policy.c @@ -0,0 +1,1221 @@ +/* + * Copyright (c) 2021 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_core.h" +#include "mmi_qc3p.h" + +#define SWITCH_CHARGER_QC3P_VOLT 5300000 +typedef enum { + PM_QC3P_STATE_DISCONNECT, + PM_QC3P_STATE_ENTRY, + PM_QC3P_STATE_SW_ENTRY, + PM_QC3P_STATE_SW_LOOP, + PM_QC3P_STATE_CHRG_PUMP_ENTRY, + PM_QC3P_STATE_SINGLE_CP_ENTRY, + PM_QC3P_STATE_DULE_CP_ENTRY, + PM_QC3P_STATE_TUNNING_VOLT, + PM_QC3P_STATE_CP_CC_LOOP, + PM_QC3P_STATE_CP_CV_LOOP, + PM_QC3P_STATE_CP_QUIT, + PM_QC3P_STATE_RECOVERY_SW, + PM_QC3P_STATE_STOP_CHARGE, +} pm_sm_qc3p_state_t; + +const unsigned char *pm_qc3p_state_str[] = { + "PM_QC3P_STATE_DISCONNECT", + "PM_QC3P_STATE_ENTRY", + "PM_QC3P_STATE_SW_ENTRY", + "PM_QC3P_STATE_SW_LOOP", + "PM_QC3P_STATE_CHRG_PUMP_ENTRY", + "PM_QC3P_STATE_SINGLE_CP_ENTRY", + "PM_QC3P_STATE_DULE_CP_ENTRY", + "PM_QC3P_STATE_TUNNING_VOLT", + "PM_QC3P_STATE_CP_CC_LOOP", + "PM_QC3P_STATE_CP_CV_LOOP", + "PM_QC3P_STATE_CP_QUIT", + "PM_QC3P_STATE_RECOVERY_SW", + "PM_QC3P_STATE_STOP_CHARGE", +}; + +static pm_sm_qc3p_state_t qc3p_sm_state = PM_QC3P_STATE_DISCONNECT; +static int qc3p_chrg_cc_power_tunning_cnt = 0; +static int qc3p_chrg_cv_taper_tunning_cnt = 0; +static int qc3p_chrg_cv_delta_volt = 0; +static int qc3p_quit_slave_chrg_cnt = 0; +static int qc3p_batt_curr_roof = 0; +static int qc3p_constant_power_cnt = 0; +static int qc3p_batt_ovp_cnt = 0; + +static void mmi_chrg_qc3p_sm_move_state(struct mmi_charger_manager *chip, pm_sm_qc3p_state_t state) +{ + mmi_chrg_dbg(chip, PR_INTERRUPT, "pm_state change:%s -> %s\n", + pm_qc3p_state_str[qc3p_sm_state], pm_qc3p_state_str[state]); + qc3p_sm_state = state; + qc3p_constant_power_cnt = 0; + qc3p_batt_curr_roof = 0; +} + +#define QC3P_VOLT_COMP_DELTA 300000 +static void qc3p_chrg_policy_error_recovery(struct mmi_charger_manager *chip, + struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num = 0, i = 0, chrg_error_type= 0; + struct mmi_charger_device *chrg_dev; + chrg_num = chip->mmi_chrg_dev_num; + + for (i = 0; i < chrg_num; i++) { + + switch (i) { + case PMIC_SW: + break; + case CP_MASTER: + if (is_charger_exist(dev_ops[CP_MASTER].dev_name)) { + chrg_dev = chrg_list->chrg_dev[CP_MASTER]; + chrg_error_type = chrg_dev->charger_error.chrg_err_type; + if (chrg_error_type & (1 << MMI_BUS_UCP_ALARM_BIT) || + chrg_error_type & (1 << MMI_BUS_UCP_FAULT_BIT)) { + mmi_chrg_info(chip,"CP master bus ucp error %d, cnt %d," + "qc3p volt comp %dmV\n", + chrg_error_type, + chrg_dev->charger_error.bus_ucp_err_cnt, + chip->qc3p_volt_comp); + + if (chrg_dev->charger_error.bus_ucp_err_cnt > 3) { + if (chrg_list->cp_slave) { + chrg_list->cp_slave = false; + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_CHRG_PUMP_ENTRY); + } else { + chip->qc3p_recovery_pmic_chrg = true; + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + } + } else if (chrg_dev->charger_error.bus_ucp_err_cnt > 6) { + chip->qc3p_recovery_pmic_chrg = true; + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + } + + chrg_dev->charger_error.bus_ucp_err_cnt++; + chip->qc3p_volt_comp += QC3P_VOLT_COMP_DELTA; + mmi_chrg_info(chip,"Restart charging, " + "increase qc3p volt comp %dmV\n", + chip->qc3p_volt_comp); + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_CHRG_PUMP_ENTRY); + mmi_clear_charger_error(chrg_dev); + }else if (chrg_error_type & (1 << MMI_BUS_OCP_ALARM_BIT) || + chrg_error_type & (1 << MMI_BUS_OCP_FAULT_BIT) || + chrg_error_type & (1 << MMI_CONV_OCP_FAULT_BIT)) { + mmi_chrg_info(chip,"CP master ocp error %d, cnt %d," + "qc3p volt comp %dmV\n", + chrg_error_type, + chrg_dev->charger_error.bus_ocp_err_cnt, + chip->qc3p_volt_comp); + if (chrg_dev->charger_error.bus_ocp_err_cnt > 3) { + chip->qc3p_recovery_pmic_chrg = true; + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ocp_err_cnt = 0; + } + chrg_dev->charger_error.bus_ocp_err_cnt++; + chip->qc3p_volt_comp -= QC3P_VOLT_COMP_DELTA; + if (chip->qc3p_volt_comp < 0) + chip->qc3p_volt_comp = 0; + mmi_chrg_info(chip,"Restart charging, " + "decrease qc3p volt comp %dmV\n", + chip->qc3p_volt_comp); + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_CHRG_PUMP_ENTRY); + mmi_clear_charger_error(chrg_dev); + } + } + break; + case CP_SLAVE: + break; + default: + mmi_chrg_err(chip,"No mmi_chrg_dev found %d !\n",i); + break; + } + } + return; +} + +void mmi_qc3p_chrg_enable_all_cp(struct mmi_charger_manager *chip, int val) +{ + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + bool enable = !!val; + + mmi_chrg_dbg(chip, PR_MOTO,"enable all cp = %d\n", enable); + if (enable) { + if (chip->cp_disable == false) + return; + + cancel_delayed_work_sync(&chip->mmi_qc3p_chrg_sm_work); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CHRG_PUMP_ENTRY); + chip->cp_disable = false; + schedule_delayed_work(&chip->mmi_qc3p_chrg_sm_work, + msecs_to_jiffies(0)); + + } else { + if(chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) + return; + + cancel_delayed_work_sync(&chip->mmi_qc3p_chrg_sm_work); + + chip->cp_disable = true; + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_RECOVERY_SW); + schedule_delayed_work(&chip->mmi_qc3p_chrg_sm_work, + msecs_to_jiffies(0)); + } +} + +#define QC3P_HEARTBEAT_SHORT_DELAY_MS 1000 +#define QC3P_HEARTBEAT_lOOP_WAIT_MS 3000 +#define QC3P_HEARTBEAT_TUNNING_MS 100 +#define QC3P_HEARTBEAT_SHORT_DELAY_MS 1000 +#define QC3P_HEARTBEAT_NEXT_STATE_MS 100 +#define QC3P_HEARTBEAT_CANCEL -1 +#define QC3P_CC_CURR_DEBOUNCE 100000 +#define QC3P_CV_TAPPER_COUNT 3 +#define QC3P_CC_POWER_COUNT 3 +#define QC3P_CV_DELTA_VOLT 100000 +#define QC3P_COOLING_DELTA_POWER 100000 +#define QC3P_COOLING_MAX_CNT 5 +#define QC3P_DISABLE_CHRG_LIMIT -1 +#define QC3P_CP_CHRG_SOC_LIMIT 90 +#define QC3P_CONT_PWR_CNT 5 +#define CP_CV_VOLT_TOLERANCE 10000 //10mV +#define QC3P_BATT_OVP_COUNT 5 + +void qc3p_clear_chg_manager(struct mmi_charger_manager *chip) +{ + mmi_chrg_dbg(chip, PR_INTERRUPT, "clear mmi qc3p chrg manager!\n"); + chip->qc3p_request_volt = 0; + chip->qc3p_request_volt_prev = 0; + chip->qc3p_target_volt = 0; + chip->qc3p_batt_therm_volt = 0; + chip->qc3p_sys_therm_volt = 0; + chip->qc3p_recovery_pmic_chrg = false; + chip->qc3p_sys_therm_cooling= false; + chip->qc3p_sys_therm_force_pmic_chrg = false; + chip->qc3p_batt_therm_cooling = false; + chip->qc3p_batt_therm_cooling_cnt = 0; + +} + +void mmi_qc3p_chrg_policy_clear(struct mmi_charger_manager *chip) { + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + chrg_dev_init(chip, &g_chrg_list); + clear_chrg_dev_error_cnt(chip, &g_chrg_list); + qc3p_clear_chg_manager(chip); + if (chrg_list->cp_slave) + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + if (chrg_list->cp_master) + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + QC3P_DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + qc3p_sm_state = PM_QC3P_STATE_DISCONNECT; + chip->qc3p_volt_comp = QC3P_INIT_VOLT_COMP; + qc3p_quit_slave_chrg_cnt = 0; + qc3p_chrg_cc_power_tunning_cnt = 0; + qc3p_chrg_cv_taper_tunning_cnt = 0; + qc3p_chrg_cv_delta_volt = 0; + qc3p_constant_power_cnt = 0; + qc3p_batt_curr_roof = 0; + return; +} + +void mmi_qc3p_chrg_sm_work_func(struct work_struct *work) +{ + struct mmi_charger_manager *chip = container_of(work, + struct mmi_charger_manager, mmi_qc3p_chrg_sm_work.work); + int i = 0, rc = 0; + int ibatt_curr = 0, vbatt_volt = 0, batt_temp = 0, vbus_pres = 0; + int batt_soc = 0; + int heartbeat_dely_ms = 0; + int pmic_sys_therm_level = 0; + bool zone_change = false; + int ibus_total_ma = 0; + int cooling_volt = 0; + bool ignore_hysteresis_degc = false; + struct mmi_chrg_step_info *chrg_step; + union power_supply_propval prop = {0,}; + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + int thermal_cooling_current = 0; + + mmi_chrg_dbg(chip, PR_MOTO, "\n\n\n"); + + mmi_chrg_dbg(chip, PR_MOTO, "schedule SM work, sm state %s \n", + pm_qc3p_state_str[qc3p_sm_state]); + + mmi_chrg_dbg(chip, PR_MOTO, "pmic-sw is exist: %d " + "cp-master is exist: %d " + "cp-slave is exist: %d\n", + chrg_list->pmic_sw, + chrg_list->cp_master, + chrg_list->cp_slave); + if (!chrg_list->pmic_sw) { + mmi_chrg_err(chip,"PMIC-SW isn't exist, force quite mmi chrg sm work !\n"); + return; + } + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &prop); + if (!rc) + pmic_sys_therm_level = prop.intval; + + ibatt_curr = chrg_list->chrg_dev[PMIC_SW]->charger_data.ibatt_curr; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_TEMP, &prop); + if (!rc) + batt_temp = prop.intval / 10; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &prop); + if (!rc) + batt_soc = prop.intval; + + + if (ibatt_curr < 0) + ibatt_curr *= -1; + + + mmi_update_all_charger_status(chip); + mmi_update_all_charger_error(chip); + + if (chip->extrn_sense) { + ibatt_curr = chrg_list->chrg_dev[CP_MASTER]->charger_data.ibatt_curr; + ibatt_curr *= 1000; + if (ibatt_curr < 0) + ibatt_curr *= -1; + } + vbatt_volt = chrg_list->chrg_dev[CP_MASTER]->charger_data.vbatt_volt; + vbatt_volt *= 1000; + + vbus_pres = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbus_pres; + if (!vbus_pres) { + for (i = 0; i < 3; i++) { + mmi_update_all_charger_status(chip); + vbus_pres = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbus_pres; + mmi_chrg_info(chip, "Retry check charger status, vbus %d\n", vbus_pres); + if (vbus_pres) { + mmi_chrg_info(chip, "Get vbus present , continue to charging\n"); + break; + } + msleep(100); + } + } + + mmi_chrg_info(chip, "battery current %d\n", ibatt_curr); + mmi_chrg_info(chip, "battery voltage %d\n", vbatt_volt); + mmi_chrg_info(chip, "battery temp %d\n", batt_temp); + mmi_chrg_info(chip, "battery capacity %d\n", batt_soc); + + if (vbus_pres && mmi_is_cable_plugout(chip)) + vbus_pres = 0; + + if (vbus_pres && + (qc3p_sm_state == PM_QC3P_STATE_TUNNING_VOLT + || qc3p_sm_state == PM_QC3P_STATE_CP_CC_LOOP + || qc3p_sm_state == PM_QC3P_STATE_CP_CV_LOOP)) { + mmi_dump_charger_error(chip, chrg_list->chrg_dev[CP_MASTER]); + qc3p_chrg_policy_error_recovery(chip, chrg_list); + } + + if (qc3p_sm_state == PM_QC3P_STATE_CP_CV_LOOP) + ignore_hysteresis_degc = true; + zone_change = mmi_find_temp_zone(chip, batt_temp, ignore_hysteresis_degc); + chrg_step = &chip->chrg_step; + + if (chip->pres_temp_zone == ZONE_COLD + || chip->pres_temp_zone == ZONE_HOT + || !chrg_list->chrg_dev[PMIC_SW]->charger_enabled) { + + mmi_chrg_info(chip, "Force stop charging, " + "pres_temp_zone %d, " + "pmic charger enabled %d\n", + chip->pres_temp_zone, + chrg_list->chrg_dev[PMIC_SW]->charger_enabled); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_STOP_CHARGE); + } + + if (vbatt_volt > chip->batt_ovp_lmt) + qc3p_batt_ovp_cnt ++; + else + qc3p_batt_ovp_cnt = 0; + + if (qc3p_batt_ovp_cnt >= QC3P_BATT_OVP_COUNT) { + mmi_chrg_info(chip, "Force stop charging, " + "vbatt_volt %dmv, " + "batt ovp limit %dmv\n", + vbatt_volt, chip->batt_ovp_lmt); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_STOP_CHARGE); + } + + if (!vbus_pres) { + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_DISCONNECT); + } else if (qc3p_sm_state == PM_QC3P_STATE_ENTRY + || qc3p_sm_state == PM_QC3P_STATE_STOP_CHARGE) { + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + } else if (qc3p_sm_state == PM_QC3P_STATE_DISCONNECT) { + mmi_get_input_current_settled(chrg_list->chrg_dev[PMIC_SW], + &chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_ENTRY); + } else if (zone_change && + chip->pres_temp_zone != ZONE_COLD && + chip->pres_temp_zone != ZONE_HOT) { + + if (batt_temp >= chrg_step->temp_c) { + mmi_chrg_info(chip, "battery temp %d, temp thre %d " + "Enter into COOLING LOOP !\n", + batt_temp, chrg_step->temp_c); + chip->qc3p_batt_therm_cooling = true; + chip->qc3p_batt_therm_volt = chip->qc3p_request_volt_prev; + } else if (!chip->qc3p_batt_therm_cooling && + !ignore_hysteresis_degc) { + mmi_chrg_info(chip, "battery temp %d, temp thre %d " + "Restart select chrg step and temp zone !\n", + batt_temp, chrg_step->temp_c); + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_ENTRY); + } + } + + mmi_chrg_dbg(chip, PR_MOTO, "temp zone %d, is_changed %d, " + "chrg_step %d, " + "step cc curr %d, step cv volt %d, " + "step cv tapper curr %d\n", + chip->pres_temp_zone, zone_change, + chrg_step->pres_chrg_step, + chrg_step->chrg_step_cc_curr, + chrg_step->chrg_step_cv_volt, + chrg_step->chrg_step_cv_tapper_curr); + switch (qc3p_sm_state) { + case PM_QC3P_STATE_DISCONNECT: + mmi_chrg_info(chip,"vbus disconnect !, jump to PM_QC3P_STATE_DISCONNECT," + "recovery PMIC-SW limitation, and close CP charg\n"); + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + QC3P_DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + heartbeat_dely_ms = QC3P_HEARTBEAT_CANCEL; + break; + case PM_QC3P_STATE_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chip->qc3p_active + && chrg_list->cp_master + && chrg_step->pres_chrg_step != chip->chrg_step_nums - 1 + && chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min + && batt_soc < QC3P_CP_CHRG_SOC_LIMIT) { + + mmi_chrg_dbg(chip, PR_MOTO, "Enter into CHRG PUMP, " + "vbatt %d uV, " + "qc3p support %d, " + "chrg step %d, " + "chrg step cc curr %d uA, " + "CP master charging curr min %d uA\n", + vbatt_volt, + chip->qc3p_active, + chrg_step->pres_chrg_step, + chrg_step->chrg_step_cc_curr, + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CHRG_PUMP_ENTRY); + + } else { + mmi_chrg_dbg(chip, PR_MOTO, "Enter into PMIC switch charging, " + "the reason is : vbatt %d uV, " + "qc3p support %d, " + "chrg step %d\n", + vbatt_volt, + chip->qc3p_active, + chrg_step->pres_chrg_step); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_ENTRY); + + } + chip->qc3p_batt_therm_cooling = false; + chip->qc3p_batt_therm_cooling_cnt= 0; + chip->qc3p_sys_therm_volt= chip->qc3p_request_volt_prev; + chip->qc3p_batt_therm_volt = chip->qc3p_request_volt_prev; + chip->qc3p_ibus_max_volt = chip->qc3p_request_volt_prev; + chip->qc3p_sys_therm_cooling = false; + chip->qc3p_sys_therm_force_pmic_chrg = false; + chip->qc3p_recovery_pmic_chrg = false; + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + break; + case PM_QC3P_STATE_SW_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip, "Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + QC3P_DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + if (!chip->dont_rerun_aicl) { + mmi_chrg_info(chip, "Do an rerun usb AICL for PMIC-SW\n"); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], false); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], true); + } + chip->qc3p_request_volt = SWITCH_CHARGER_QC3P_VOLT; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_LOOP); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + break; + case PM_QC3P_STATE_SW_LOOP: + if (chip->qc3p_active + && chip->cp_disable == false + && chrg_step->pres_chrg_step != chip->chrg_step_nums - 1 + && chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min + && !chip->qc3p_recovery_pmic_chrg + && !chip->qc3p_sys_therm_force_pmic_chrg + && batt_soc < QC3P_CP_CHRG_SOC_LIMIT) { + mmi_chrg_info(chip, "Enter CP, the reason is : " + "qc3p support %d, " + "vbatt %duV, chrg step %d\n", + chip->qc3p_active, + vbatt_volt, chrg_step->pres_chrg_step); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CHRG_PUMP_ENTRY); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } else { + mmi_chrg_dbg(chip, PR_MOTO, "Continue to SW charging, " + "vbatt %d uV, ibatt %d uA\n", + vbatt_volt, ibatt_curr); + heartbeat_dely_ms = QC3P_HEARTBEAT_lOOP_WAIT_MS; + } + break; + case PM_QC3P_STATE_CHRG_PUMP_ENTRY: + mmi_chrg_info(chip,"CP master exist %d, CP slave exist %d !\n", + chrg_list->cp_master, + chrg_list->cp_slave); + if (chrg_list->cp_slave) { + mmi_chrg_info(chip,"CP slave is exist !\n"); + mmi_chrg_info(chip,"chrg step cc curr %d uA, " + "CP slave charging curr min %d uA\n", + chrg_step->chrg_step_cc_curr, + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min); + if (chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min) { + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_DULE_CP_ENTRY); + } else + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_SINGLE_CP_ENTRY); + } else { + mmi_chrg_info(chip,"CP slave isn't exist !\n"); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SINGLE_CP_ENTRY); + } + + mmi_chrg_info(chip,"Set PMIC SW FCC limits!\n"); + if (!chrg_list->chrg_dev[PMIC_SW]->charger_limited + && chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited > 0) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited); + mmi_chrg_info(chip,"Set PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = true; + } + + /*Initial setup qc3p request power by the battery voltage*/ + chip->qc3p_request_volt = (2 * vbatt_volt) % 20000; + chip->qc3p_request_volt = 2 * vbatt_volt - chip->qc3p_request_volt + + chip->qc3p_volt_comp; + mmi_chrg_info(chip,"qc3p init , volt %dmV, volt comp %dmv\n", + chip->qc3p_request_volt, chip->qc3p_volt_comp); + chrg_policy_error_clear(chip, chrg_list); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + break; + case PM_QC3P_STATE_SINGLE_CP_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_chrg_info(chip,"Disable Slave Charger Pump !\n"); + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Enable Master Charger Pump !\n"); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_TUNNING_VOLT); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_QC3P_STATE_DULE_CP_ENTRY: + if (chrg_list->cp_master) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Enable Master Charger Pump !\n"); + } + + if (chrg_list->cp_slave) { + chrg_list->cp_clave_later = true; + mmi_chrg_info(chip,"For ibus UCP, " + "delay start Slave Charger Pump in TUNNING_VOLT stage !\n"); + } + + if (chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_TUNNING_VOLT); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_QC3P_STATE_TUNNING_VOLT: + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + if (chrg_list->cp_slave + && chrg_list->cp_clave_later + && !chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], true); + mmi_chrg_info(chip,"Enable Slave Charger Pump !\n"); + } + + if (chrg_list->cp_master + && (!chrg_list->chrg_dev[CP_MASTER]->charger_enabled + || (chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, " + "Enter into SW directly\n"); + chip->qc3p_volt_comp = QC3P_INIT_VOLT_COMP; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_ENTRY); + } else if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + chip->qc3p_request_volt -= chip->qc3p_volt_steps; + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_CP_CC_LOOP); + mmi_chrg_info(chip,"Duing the volt going up process, " + "the chrg step was changed," + "stop increase qc3p volt and" + " Enter into CC stage as soon!\n"); + } else if (chip->qc3p_request_volt + chip->qc3p_volt_steps + <= chip->qc3p_volt_max + && vbatt_volt < chrg_step->chrg_step_cv_volt + && ibatt_curr < ((chrg_step->pres_chrg_step == STEP_FIRST) ? + chrg_step->chrg_step_cc_curr + chip->step_first_curr_comp: + chrg_step->chrg_step_cc_curr)) { + chip->qc3p_request_volt += chip->qc3p_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, "Increase qc3p volt %d\n", + chip->qc3p_request_volt); + heartbeat_dely_ms = QC3P_HEARTBEAT_TUNNING_MS; + } else { + mmi_chrg_info(chip,"Enter into CC loop stage !\n"); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CP_CC_LOOP); + } + + if (ibatt_curr > qc3p_batt_curr_roof) { + qc3p_batt_curr_roof = ibatt_curr; + } + if (ibatt_curr < qc3p_batt_curr_roof) + qc3p_constant_power_cnt++; + else + qc3p_constant_power_cnt = 0; + + if (qc3p_constant_power_cnt > QC3P_CONT_PWR_CNT) { + mmi_chrg_info(chip,"QC3P adapter was ready in constant power state, " + "Enter into CC loop stage !\n"); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CP_CC_LOOP); + } + mmi_chrg_info(chip, "qc3p_constant_power_cnt %d, " + "qc3p_batt_curr_roof %d\n", + qc3p_constant_power_cnt, + qc3p_batt_curr_roof); + break; + case PM_QC3P_STATE_CP_CC_LOOP: + heartbeat_dely_ms = QC3P_HEARTBEAT_lOOP_WAIT_MS; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CC loop, chrg_step %d, " + "vbatt %dmV, ibatt %dmA, " + "CC target curr %dmA, " + "next CV target volt %dmV\n ", + chrg_step->pres_chrg_step, + vbatt_volt, ibatt_curr, + chrg_step->chrg_step_cc_curr, + chrg_step->chrg_step_cv_volt); + if (chrg_list->cp_master + && (!chrg_list->chrg_dev[CP_MASTER]->charger_enabled + || (chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into SW directly\n"); + chip->qc3p_volt_comp = QC3P_INIT_VOLT_COMP; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_ENTRY); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + goto schedule; + } + + if (!chip->qc3p_sys_therm_cooling + && !chip->qc3p_batt_therm_cooling + && qc3p_constant_power_cnt <= QC3P_CONT_PWR_CNT) { + if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->qc3p_request_volt < chip->qc3p_volt_max + && qc3p_chrg_cc_power_tunning_cnt >= + QC3P_CC_POWER_COUNT) { + if (chip->qc3p_request_volt + chip->qc3p_volt_steps + < chip->qc3p_volt_max) { + chip->qc3p_request_volt += + chip->qc3p_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "Request volt increass %duV\n ", + chip->qc3p_request_volt); + } + qc3p_chrg_cc_power_tunning_cnt = 0; + }else if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->qc3p_request_volt < chip->qc3p_volt_max) { + qc3p_chrg_cc_power_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CC tunning cnt %d\n", + qc3p_chrg_cc_power_tunning_cnt); + } else if (ibatt_curr > chrg_step->chrg_step_cc_curr + + QC3P_CC_CURR_DEBOUNCE) { + chip->qc3p_request_volt -= chip->qc3p_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "In the CC step , the ibatt is greater than CC curr, " + "Request volt decreass %duV to remain CC step\n ", + chip->qc3p_request_volt); + } else + qc3p_chrg_cc_power_tunning_cnt = 0; + + if(ibatt_curr < chrg_step->chrg_step_cc_curr) + heartbeat_dely_ms = QC3P_HEARTBEAT_SHORT_DELAY_MS; + } + + if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + if (qc3p_chrg_cv_taper_tunning_cnt > + QC3P_CV_TAPPER_COUNT) { + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CP_CV_LOOP); + qc3p_chrg_cv_taper_tunning_cnt = 0; + qc3p_chrg_cv_delta_volt = QC3P_CV_DELTA_VOLT; + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } else { + qc3p_chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV taper cnt %d, " + "chrg step cv volt %dmV, " + "vbatt %dmV\n", + qc3p_chrg_cv_taper_tunning_cnt, + chrg_step->chrg_step_cv_volt, + vbatt_volt); + } + } else + qc3p_chrg_cv_taper_tunning_cnt = 0; + + break; + case PM_QC3P_STATE_CP_CV_LOOP: + heartbeat_dely_ms = QC3P_HEARTBEAT_SHORT_DELAY_MS; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV loop, chrg_step %d, " + "vbatt %dmV, ibatt %dmA, " + "CV target volt %dmV, " + "CV taper curr %dmA\n ", + chrg_step->pres_chrg_step, + vbatt_volt, ibatt_curr, + chrg_step->chrg_step_cv_volt, + chrg_step->chrg_step_cv_tapper_curr); + if (chrg_list->cp_master + && (!chrg_list->chrg_dev[CP_MASTER]->charger_enabled + || (chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into SW directly\n"); + chip->qc3p_volt_comp = QC3P_INIT_VOLT_COMP; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_ENTRY); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + goto schedule; + } + + if (((vbatt_volt + CP_CV_VOLT_TOLERANCE) >= chrg_step->chrg_step_cv_volt) + && ((!chrg_step->last_step && + ibatt_curr < chrg_step->chrg_step_cv_tapper_curr) + || ibatt_curr < chrg_list->chrg_dev[CP_MASTER]->charging_curr_min)) { + if (qc3p_chrg_cv_taper_tunning_cnt >= QC3P_CV_TAPPER_COUNT) { + if (ibatt_curr < + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min) { + mmi_chrg_info(chip, "Ready quite CP chrg stage, " + "and Enter into PMIC switch chrg stage, " + "chrg step %d, ibatt %dmA\n", + chrg_step->pres_chrg_step, ibatt_curr); + mmi_find_chrg_step(chip, + chip->pres_temp_zone, vbatt_volt + CP_CV_VOLT_TOLERANCE); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CP_QUIT); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } else { + if (mmi_find_chrg_step(chip, + chip->pres_temp_zone, vbatt_volt + CP_CV_VOLT_TOLERANCE)) { + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + mmi_chrg_info(chip,"Jump to next chrg step\n"); + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_CP_CC_LOOP); + } else { + mmi_chrg_info(chip,"Can't find next chrg step\n"); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CP_QUIT); + } + } + qc3p_chrg_cv_taper_tunning_cnt = 0; + } else { + qc3p_chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, "chrg cv taper cnt ++, %d\n", + qc3p_chrg_cv_taper_tunning_cnt); + } + + }else if (!chip->qc3p_sys_therm_cooling + && !chip->qc3p_batt_therm_cooling) { + + qc3p_chrg_cv_taper_tunning_cnt = 0; + if (vbatt_volt > chrg_step->chrg_step_cv_volt + 10000) { + if (qc3p_chrg_cv_delta_volt > 20000) + chip->qc3p_request_volt -= qc3p_chrg_cv_delta_volt; + else + chip->qc3p_request_volt -= 20000; + mmi_chrg_info(chip, + "For keeping CV stage, decrease volt %dmV, " + "cv delta volt %dmV\n", + chip->qc3p_request_volt, qc3p_chrg_cv_delta_volt); + } else if (vbatt_volt < chrg_step->chrg_step_cv_volt - 10000) { + qc3p_chrg_cv_delta_volt -= 20000; + chip->qc3p_request_volt += 20000; + mmi_chrg_info(chip, + "For keeping CV stage, increase volt %dmV, " + "cv delta volt %dmV\n", + chip->qc3p_request_volt, qc3p_chrg_cv_delta_volt); + } else { + mmi_chrg_dbg(chip, PR_MOTO, "CV loop work well, " + "keep qc3p power, volt %dmV\n", + chip->qc3p_request_volt); + } + + }else { + /*In this case, qc3p_sys_therm_cooling or qc3p_batt_therm_cooling is ture*/ + + if (vbatt_volt > chrg_step->chrg_step_cv_volt + 10000) { + if (qc3p_chrg_cv_delta_volt > 20000) + chip->qc3p_request_volt -= qc3p_chrg_cv_delta_volt; + else + chip->qc3p_request_volt -= 20000; + mmi_chrg_info(chip, + "For keeping CV stage, decrease volt %dmV, " + "cv delta volt %dmV\n", + chip->qc3p_request_volt, qc3p_chrg_cv_delta_volt); + } else { + mmi_chrg_dbg(chip, PR_MOTO, "CV loop work well, " + "keep qc3p power, volt %dmV\n", + chip->qc3p_request_volt); + } + } + + if (chrg_list->cp_slave) { + if (qc3p_quit_slave_chrg_cnt > 3 + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + mmi_chrg_info(chip,"Quit slave chrg, the reason is :ibatt %duA, " + "CP slave charging curr min %d uA\n", + ibatt_curr, + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Restart CP master again\n"); + } + + if (ibatt_curr < + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min) + qc3p_quit_slave_chrg_cnt++; + else + qc3p_quit_slave_chrg_cnt = 0; + } + break; + case PM_QC3P_STATE_CP_QUIT: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + chip->qc3p_batt_therm_cooling = false; + chip->qc3p_batt_therm_cooling_cnt = 0; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_RECOVERY_SW); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + break; + case PM_QC3P_STATE_RECOVERY_SW: + heartbeat_dely_ms = QC3P_HEARTBEAT_SHORT_DELAY_MS; + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + QC3P_DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + + if (!chip->dont_rerun_aicl) { + mmi_chrg_info(chip,"Do an rerun usb AICL for PMIC-SW\n"); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], false); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], true); + } + chip->qc3p_recovery_pmic_chrg = true; + qc3p_chrg_cv_taper_tunning_cnt = 0; + } + + mmi_chrg_info(chip,"ibatt : %dmA, step cc curr : %dmA\n", + ibatt_curr, chrg_step->chrg_step_cc_curr); + if (ibatt_curr > chrg_step->chrg_step_cc_curr) { + chip->qc3p_request_volt -= QC3P_CV_DELTA_VOLT; + + mmi_chrg_dbg(chip, PR_MOTO, "Reduce qc3p volt %dmV\n ", + chip->qc3p_request_volt); + heartbeat_dely_ms = QC3P_HEARTBEAT_TUNNING_MS; + qc3p_chrg_cv_taper_tunning_cnt = 0; + } else { + qc3p_chrg_cv_taper_tunning_cnt++; + + } + + if (qc3p_chrg_cv_taper_tunning_cnt > QC3P_CV_TAPPER_COUNT){ + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_LOOP); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_QC3P_STATE_STOP_CHARGE: + heartbeat_dely_ms = QC3P_HEARTBEAT_lOOP_WAIT_MS; + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + QC3P_DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + if (chip->pres_temp_zone != ZONE_COLD + && chip->pres_temp_zone != ZONE_HOT + && chrg_list->chrg_dev[PMIC_SW]->charger_enabled + && chrg_step->chrg_step_cc_curr > 0) { + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_ENTRY); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } + chip->qc3p_request_volt = SWITCH_CHARGER_QC3P_VOLT; + break; + } + +schedule: + + chip->qc3p_target_volt = min(chip->qc3p_request_volt, chip->qc3p_volt_max); + + if (chip->system_thermal_level == 0) { + chip->qc3p_sys_therm_cooling= false; + chip->qc3p_sys_therm_force_pmic_chrg = false; + } else if ((chip->system_thermal_level == chip->thermal_levels - 1) + && !chip->qc3p_sys_therm_force_pmic_chrg) { + chip->qc3p_sys_therm_cooling = true; + chip->qc3p_sys_therm_force_pmic_chrg = true; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_ENTRY); + mmi_chrg_info(chip, "Thermal is the highest, level %d, " + "Force enter into single pmic charging !\n", + chip->system_thermal_level); + + } else if (chip->system_thermal_level > 0 && + (qc3p_sm_state == PM_QC3P_STATE_CP_CC_LOOP || + qc3p_sm_state == PM_QC3P_STATE_CP_CV_LOOP)) { + + mmi_chrg_dbg(chip, PR_MOTO, "Thermal level is %d\n", + chip->system_thermal_level); + if (!chip->qc3p_sys_therm_cooling) { + chip->qc3p_sys_therm_cooling = true; + chip->qc3p_sys_therm_volt = chip->qc3p_request_volt_prev; + } + + thermal_cooling_current = min(chip->thermal_mitigation[chip->system_thermal_level],chrg_step->chrg_step_cc_curr); + if (ibatt_curr > + thermal_cooling_current + QC3P_CC_CURR_DEBOUNCE) { + if (ibatt_curr - thermal_cooling_current > 300000) + chip->qc3p_sys_therm_volt -= chip->qc3p_volt_steps *3; + else + chip->qc3p_sys_therm_volt -= chip->qc3p_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, "For thermal, decrease qc3 volt %d\n", + chip->qc3p_sys_therm_volt); + } else if ((ibatt_curr < + thermal_cooling_current)) { + if (thermal_cooling_current - ibatt_curr > 300000) + chip->qc3p_sys_therm_volt += chip->qc3p_volt_steps *3; + else + chip->qc3p_sys_therm_volt += chip->qc3p_volt_steps; + chip->qc3p_sys_therm_volt = min(chip->qc3p_volt_max, chip->qc3p_sys_therm_volt); + mmi_chrg_dbg(chip, PR_MOTO, "For thermal, increase qc3 volt %d\n", + chip->qc3p_sys_therm_volt); + } + + heartbeat_dely_ms = QC3P_HEARTBEAT_TUNNING_MS; + } else if (chip->system_thermal_level > 0 && + chip->system_thermal_level != chip->thermal_levels - 1 && + qc3p_sm_state == PM_QC3P_STATE_SW_LOOP && + chip->qc3p_sys_therm_force_pmic_chrg) { + mmi_chrg_dbg(chip, PR_MOTO, "Try to recovery charger pump!\n"); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_ENTRY); + } + + + if (chip->qc3p_sys_therm_cooling){ + mmi_chrg_dbg(chip, PR_MOTO, "at thermal cooling status ,judge ibatt_curr:%d, thermal_current_limited:%d ,chrg_step_cc_curr:%d,thermal_cooling_curr:%d\n", + ibatt_curr, chip->thermal_mitigation[chip->system_thermal_level],chrg_step->chrg_step_cc_curr,thermal_cooling_current); + mmi_chrg_dbg(chip, PR_MOTO, "at thermal cooling status ,qc3p_target_volt:%d, qc3p_sys_therm_volt:%d\n", chip->qc3p_target_volt, chip->qc3p_sys_therm_volt); + if(qc3p_sm_state == PM_QC3P_STATE_CP_CC_LOOP ) + chip->qc3p_target_volt = chip->qc3p_sys_therm_volt ; + else + chip->qc3p_target_volt = min(chip->qc3p_target_volt, chip->qc3p_sys_therm_volt); + + chip->qc3p_request_volt = chip->qc3p_target_volt; + chip->qc3p_sys_therm_volt = chip->qc3p_target_volt; + heartbeat_dely_ms = QC3P_HEARTBEAT_SHORT_DELAY_MS; + } + + if (chip->qc3p_batt_therm_cooling && !chip->qc3p_sys_therm_force_pmic_chrg) { + if (batt_temp > chrg_step->temp_c + COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, Cooling loop failed, " + "force enter PM_QC3P_STATE_ENTRY " + "restart this charger process !\n", + batt_temp); + chip->qc3p_batt_therm_cooling = false; + chip->qc3p_batt_therm_cooling_cnt= 0; + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_ENTRY); + + } else if (batt_temp > chrg_step->temp_c) { + cooling_volt = (2 * vbatt_volt) % 20000; + cooling_volt = 2 * vbatt_volt - cooling_volt + + chip->qc3p_volt_comp; + if (ibatt_curr > TYPEC_HIGH_CURRENT_UA + && chip->qc3p_batt_therm_volt > cooling_volt) { + chip->qc3p_batt_therm_volt -= QC3P_COOLING_DELTA_POWER; + mmi_chrg_info(chip, "Do chrg power cooling" + "qc3p request volt %dmA, " + "battery temp %d\n", + chip->qc3p_batt_therm_volt, batt_temp); + } else { + if (chip->qc3p_batt_therm_cooling_cnt > QC3P_COOLING_MAX_CNT) { + mmi_chrg_info(chip, "Do chrg power cooling failed" + "qc3p_batt_therm_volt %dmV" + "battery temp %d, " + "Restart PM_STATE_ENTRY !\n", + chip->qc3p_batt_therm_volt, + batt_temp); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + chip->qc3p_batt_therm_cooling = false; + chip->qc3p_batt_therm_cooling_cnt = 0; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_ENTRY); + } else { + mmi_chrg_info(chip, "It's already the lowest cooling chrg power" + "waiting for a while, cooling cnt %d, " + "battery temp %d\n", + batt_temp, chip->qc3p_batt_therm_cooling); + chip->qc3p_batt_therm_cooling_cnt++; + } + } + } else if (batt_temp < chrg_step->temp_c - COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, " + "Exit successfully from COOLing loop!\n", + batt_temp); + + if (qc3p_sm_state == PM_QC3P_STATE_CP_CC_LOOP) { + mmi_chrg_info(chip,"Jump into CURR tunning" + "for chrg power poerformance!\n"); + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_TUNNING_VOLT); + } + chip->qc3p_batt_therm_cooling = false; + chip->qc3p_batt_therm_cooling_cnt = 0; + } + heartbeat_dely_ms = QC3P_HEARTBEAT_lOOP_WAIT_MS; + } + + if (chip->qc3p_batt_therm_cooling) + chip->qc3p_target_volt = min(chip->qc3p_target_volt, chip->qc3p_batt_therm_volt); + + ibus_total_ma = chrg_list->chrg_dev[PMIC_SW]->charger_data.ibus_curr / 1000 + chrg_list->chrg_dev[CP_MASTER]->charger_data.ibus_curr; + if (chrg_list->cp_slave && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + ibus_total_ma += chrg_list->chrg_dev[CP_SLAVE]->charger_data.ibus_curr; + } + + if(ibus_total_ma > chip->qc3p_max_ibus_ma) { + chip->qc3p_ibus_max_volt -= 2 * chip->qc3p_volt_steps; + chip->qc3p_target_volt = min(chip->qc3p_target_volt, chip->qc3p_ibus_max_volt); + chip->qc3p_request_volt = chip->qc3p_target_volt; + mmi_chrg_info(chip,"Limit ibus current: ibus is %dmA, max allow is %dmA, ibus volt is %duV, target volt is %duV \n", + ibus_total_ma, chip->qc3p_max_ibus_ma, chip->qc3p_ibus_max_volt, chip->qc3p_target_volt); + } + + mmi_chrg_dbg(chip, PR_MOTO, "chrg sm work,%s, " + "battery soc %d, " + "battery temp %d, " + "battery current %d, " + "battery voltage %d, " + "sys therm level %d, " + "pmic therm level %d, " + "sys therm cooling %d, " + "batt therm cooling %d, " + "sys therm force pmic chrg %d, " + "recovery pmic chrg %d", + pm_qc3p_state_str[qc3p_sm_state], + batt_soc, batt_temp, + ibatt_curr, vbatt_volt, + chip->system_thermal_level, + pmic_sys_therm_level, + chip->qc3p_sys_therm_cooling, + chip->qc3p_batt_therm_cooling, + chip->qc3p_sys_therm_force_pmic_chrg, + chip->qc3p_recovery_pmic_chrg); + + mmi_chrg_dbg(chip, PR_MOTO, "qc3p request volt %dmV, " + "qc3p target volt %dmV, " + "sys therm volt %dmV, " + "batt therm volt %dmV," + "qc3p_ibus_max_volt %dmV," + "qc3p_max_ibus_ma %dmA," + "ibus total %dmA\n", + chip->qc3p_request_volt, + chip->qc3p_target_volt, + chip->qc3p_sys_therm_volt, + chip->qc3p_batt_therm_volt, + chip->qc3p_ibus_max_volt, + chip->qc3p_max_ibus_ma, + ibus_total_ma); + + if (chip->qc3p_target_volt < SWITCH_CHARGER_QC3P_VOLT) { + + if (qc3p_sm_state == PM_QC3P_STATE_TUNNING_VOLT + || qc3p_sm_state == PM_QC3P_STATE_CP_CC_LOOP + || qc3p_sm_state == PM_QC3P_STATE_CP_CV_LOOP) { + + mmi_chrg_err(chip, "%s, wrong qc3p voltage or current , " + "request qc3p voltage %d, ", + pm_qc3p_state_str[qc3p_sm_state], + chip->qc3p_target_volt); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_ENTRY); + } + goto skip_pd_select; + } + + rc = mmi_qc3p_set_vbus_voltage(chip, chip->qc3p_target_volt / 1000); + if (rc > 0) { + chip->qc3p_request_volt_prev = chip->qc3p_target_volt; + chip->qc3p_ibus_max_volt = chip->qc3p_target_volt; + } +skip_pd_select: + + if (heartbeat_dely_ms > 0) { + mmi_chrg_err(chip, "schedule work timer %dms\n", heartbeat_dely_ms); + schedule_delayed_work(&chip->mmi_qc3p_chrg_sm_work, + msecs_to_jiffies(heartbeat_dely_ms)); + } else { + mmi_qc3p_chrg_policy_clear(chip); + chip->sm_work_running = false; + chip->qc3p_active = false; + mmi_chrg_err(chip, "exit sm work\n"); + } + + return; +} + + diff --git a/drivers/power/mmi_discrete_turbo_charger/qpnp_pmic_charger.c b/drivers/power/mmi_discrete_turbo_charger/qpnp_pmic_charger.c new file mode 100644 index 000000000000..340ba4e3730a --- /dev/null +++ b/drivers/power/mmi_discrete_turbo_charger/qpnp_pmic_charger.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include +#include "mmi_charger_core.h" +#include "mmi_charger_core_iio.h" + +static int qpnp_pmic_enable_charging(struct mmi_charger_device *chrg, bool en) +{ + int rc = 0; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_write_iio_chan(chip, SMB5_CHARGING_ENABLED, en); + if (!rc) { + chrg->charger_enabled = en; + } else { + chrg->charger_enabled = false; + } + chrg_dev_info(chrg, "%s end, en:%d\n",__func__,en); + return rc; +} + +static int qpnp_pmic_is_charging_enabled(struct mmi_charger_device *chrg, bool *en) +{ + int rc = 0; + int value; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_read_iio_chan(chip, SMB5_CHARGING_ENABLED, &value); + if (!rc) { + chrg->charger_enabled = !!value; + } else + chrg->charger_enabled = false; + + *en = chrg->charger_enabled; + chrg_dev_info(chrg, "%s end, en:%d\n",__func__,*en); + return rc; +} + +static int qpnp_pmic_get_charging_current(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc; + union power_supply_propval prop = {0,}; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + if (!chip->bms_psy) + return -ENODEV; + + rc = power_supply_get_property(chip->bms_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + *uA = prop.intval * -1; + + chrg_dev_info(chrg, "%s end, uA:%d\n",__func__,*uA); + return rc; +} + +static int qpnp_pmic_get_ibus(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + union power_supply_propval prop = {0,}; + + if (!chip) + return -ENODEV; + + if (!chip->usb_psy) + return -ENODEV; + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + *uA = prop.intval; + chrg_dev_info(chrg, "%s end, uA:%d\n",__func__,*uA); + return rc; +} + +static int qpnp_pmic_set_charging_current(struct mmi_charger_device *chrg, u32 uA) +{ + int rc; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + union power_supply_propval prop = {0,}; + + if (!chip) + return -ENODEV; + + if (!chip->usb_psy) + return -ENODEV; + + prop.intval = uA; + rc = power_supply_set_property(chip->usb_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, &prop); +// if (!rc) +// chrg->charger_limited = true; + chrg_dev_info(chrg, "%s end, uA:%d\n",__func__,uA); + return rc; +} + +static int qpnp_pmic_get_input_current_settled(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc = 0; + int value; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_read_iio_chan(chip, SMB5_HW_CURRENT_MAX, &value); + if (!rc) + chrg->input_curr_setted = value; + *uA = chrg->input_curr_setted; + chrg_dev_info(chrg, "%s end, uA:%d\n",__func__,*uA); + return rc; +} + +static int qpnp_pmic_update_charger_status(struct mmi_charger_device *chrg) +{ + int rc; + bool value = 0; + union power_supply_propval prop = {0,}; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + if (!chip->bms_psy) + return -ENODEV; + + if (!chrg->chrg_psy) + return -ENODEV; + + if (!chip->usb_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + chrg->charger_data.vbatt_volt = prop.intval; + + rc = power_supply_get_property(chip->bms_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + chrg->charger_data.ibatt_curr = prop.intval * -1; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_TEMP, &prop); + if (!rc) + chrg->charger_data.batt_temp = prop.intval / 10; + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + chrg->charger_data.vbus_volt = prop.intval; + + /*main charger of discrete IC don't support detecting input current.*/ + chrg->charger_data.ibus_curr = CP_ENABLED_MAIN_INPUT_LIMIT; + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (!rc) + chrg->charger_data.vbus_pres = !!prop.intval; + + rc = qpnp_pmic_is_charging_enabled(chrg, &value); + if (!rc) + chrg->charger_enabled= !!value; + + chrg_dev_info(chrg, "QPNP SW chrg: status update: --- info---"); + chrg_dev_info(chrg, "vbatt %d\n", chrg->charger_data.vbatt_volt); + chrg_dev_info(chrg, "ibatt %d\n", chrg->charger_data.ibatt_curr); + chrg_dev_info(chrg, "batt temp %d\n", chrg->charger_data.batt_temp); + chrg_dev_info(chrg, "vbus %d\n", chrg->charger_data.vbus_volt); + chrg_dev_info(chrg, "ibus %d\n", chrg->charger_data.ibus_curr); + chrg_dev_info(chrg, "vbus pres %d\n", chrg->charger_data.vbus_pres); + chrg_dev_info(chrg, "charger_enabled %d\n", chrg->charger_enabled); + chrg_dev_info(chrg, "charger_limited %d\n", chrg->charger_limited); + return rc; +} + +struct mmi_charger_ops qpnp_pmic_charger_ops = { + .enable = qpnp_pmic_enable_charging, + .is_enabled = qpnp_pmic_is_charging_enabled, + .get_charging_current = qpnp_pmic_get_charging_current, + .get_input_current = qpnp_pmic_get_ibus, + .set_charging_current = qpnp_pmic_set_charging_current, + .get_input_current_settled = qpnp_pmic_get_input_current_settled, + .update_charger_status = qpnp_pmic_update_charger_status, +}; diff --git a/drivers/power/mmi_lpd_mitigate/Android.mk b/drivers/power/mmi_lpd_mitigate/Android.mk new file mode 100755 index 000000000000..7ecf85556f1d --- /dev/null +++ b/drivers/power/mmi_lpd_mitigate/Android.mk @@ -0,0 +1,11 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_lpd_mitigate.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/mmi_relay.ko +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/qti_glink_charger.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/mmi_lpd_mitigate/Kbuild b/drivers/power/mmi_lpd_mitigate/Kbuild new file mode 100755 index 000000000000..f11052e735f8 --- /dev/null +++ b/drivers/power/mmi_lpd_mitigate/Kbuild @@ -0,0 +1,9 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/qti_glink_charger + +obj-m += mmi_lpd_mitigate.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../mmi_relay/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../qti_glink_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/mmi_lpd_mitigate/Makefile b/drivers/power/mmi_lpd_mitigate/Makefile new file mode 100755 index 000000000000..594cf286add4 --- /dev/null +++ b/drivers/power/mmi_lpd_mitigate/Makefile @@ -0,0 +1,17 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../mmi_relay/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../qti_glink_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/mmi_lpd_mitigate/mmi_lpd_mitigate.c b/drivers/power/mmi_lpd_mitigate/mmi_lpd_mitigate.c new file mode 100644 index 000000000000..14816e47d692 --- /dev/null +++ b/drivers/power/mmi_lpd_mitigate/mmi_lpd_mitigate.c @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2022 Motorola Mobility LLC + * + * 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. + */ + +#define pr_fmt(fmt) "LPD_MITIGATE: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qti_glink_charger.h" + +#define LPD_SHOW_MAX_SIZE 128 + +struct lpd_info { + int lpd_present; + int lpd_rsbu1; + int lpd_rsbu2; + int lpd_cid; +}; + +/* + * Lpd Mitigation Mode + */ +enum { + LPD_MITIGATE_DRP, + LPD_MITIGATE_SNK, + LPD_MITIGATE_SRC, + LPD_MITIGATE_DISABLE, + LPD_MITIGATE_INVALID, +}; + +enum { + LPD_STATUS_OFFLINE = 0, + LPD_STATUS_ONLINE = 1, +}; + +struct lpd_mitigate_device { + struct device *dev; + u32 lpd_status; + u32 tud_status; + int cid_status; + u32 lpd_mitigate_mode; + u32 mitigate_mode_cfg; + u32 def_mitigate_mode_cfg; + struct work_struct lpd_mitigate_work; + struct notifier_block lpd_mitigate_notify; + struct notifier_block lpd_psy_notifier; + struct power_supply *batt_psy; + char *uenvp[2]; +}; + +static void lpd_notify_uevent(struct lpd_mitigate_device *lpd_device, int event) +{ + char uevent_buf[LPD_SHOW_MAX_SIZE]; + + if (!lpd_device->batt_psy) { + lpd_device->batt_psy = power_supply_get_by_name("battery"); + if (!lpd_device->batt_psy) { + pr_err("No battery supply found\n"); + return; + } + } + + memset(uevent_buf, '\0', LPD_SHOW_MAX_SIZE); + if (event == NOTIFY_EVENT_LPD_STATUS) { + scnprintf(uevent_buf, LPD_SHOW_MAX_SIZE, + "POWER_SUPPLY_LPD_PRESENT=%s", + lpd_device->lpd_status? "true" : "false"); + } else if (event == NOTIFY_EVENT_TUD_STATUS) { + scnprintf(uevent_buf, LPD_SHOW_MAX_SIZE, + "POWER_SUPPLY_TUD_PRESENT=%s", + lpd_device->tud_status? "true" : "false"); + } else if (event == NOTIFY_EVENT_CID_STATUS) { + scnprintf(uevent_buf, LPD_SHOW_MAX_SIZE, + "POWER_SUPPLY_CID_STATUS=%d", + lpd_device->cid_status); + } else { + pr_err("Invalid lpd notify event: %d\n", event); + return; + } + lpd_device->uenvp[0] = uevent_buf; + lpd_device->uenvp[1] = NULL; + kobject_uevent_env(&lpd_device->batt_psy->dev.kobj, + KOBJ_CHANGE, lpd_device->uenvp); +} + +static int lpd_mitigate_notify_cb(struct notifier_block *self, + unsigned long event, void *p) +{ + struct lpd_mitigate_device *lpd_device = container_of(self, + struct lpd_mitigate_device, lpd_mitigate_notify); + + if (lpd_device && event == NOTIFY_EVENT_TUD_STATUS) { + u32 tud_status = *(int *)p; + pr_info("TUD status notify: %d\n", tud_status); + if (tud_status > 0) + tud_status = LPD_STATUS_ONLINE; + else + tud_status = LPD_STATUS_OFFLINE; + if (tud_status != lpd_device->tud_status) { + pr_info("TUD status transit: %d -> %d\n", + lpd_device->tud_status, tud_status); + lpd_device->tud_status = tud_status; + schedule_work(&lpd_device->lpd_mitigate_work); + } + } + + return 0; +} + +static void lpd_mitigate_work(struct work_struct *work) +{ + int rc; + struct lpd_info lpd_info = {0}; + u32 lpd_status; + u32 lpd_mitigate_mode; + static u32 tud_status = INT_MAX; + struct lpd_mitigate_device *lpd_device = container_of(work, + struct lpd_mitigate_device, lpd_mitigate_work); + + if (!lpd_device) { + pr_err("Invalid lpd device\n"); + return; + } + + if (tud_status != lpd_device->tud_status || + lpd_device->tud_status == INT_MAX) { + if (lpd_device->tud_status == INT_MAX) + lpd_device->tud_status = LPD_STATUS_OFFLINE; + tud_status = lpd_device->tud_status; + lpd_notify_uevent(lpd_device, NOTIFY_EVENT_TUD_STATUS); + } + + rc = qti_charger_get_property(OEM_PROP_LPD_INFO, + &lpd_info, sizeof(lpd_info)); + if (rc) { + pr_err("Failed to read lpd info, rc=%d\n", rc); + return; + } + + lpd_status = lpd_device->lpd_status; + if (lpd_info.lpd_present && + lpd_device->lpd_status != LPD_STATUS_ONLINE) { + lpd_status = LPD_STATUS_ONLINE; + } else if (!lpd_info.lpd_present && + lpd_device->lpd_status != LPD_STATUS_OFFLINE) { + lpd_status = LPD_STATUS_OFFLINE; + } + if (lpd_status != lpd_device->lpd_status) { + pr_info("LPD status transit: %d -> %d\n", + lpd_device->lpd_status, lpd_status); + lpd_device->lpd_status = lpd_status; + relay_notifier_fire(BLOCKING, LPD, + NOTIFY_EVENT_LPD_STATUS, + (void *)&lpd_status); + lpd_notify_uevent(lpd_device, NOTIFY_EVENT_LPD_STATUS); + } + + if (lpd_info.lpd_cid != lpd_device->cid_status) { + pr_info("CID status transit: %d -> %d\n", + lpd_device->cid_status, lpd_info.lpd_cid); + lpd_device->cid_status = lpd_info.lpd_cid; + relay_notifier_fire(BLOCKING, LPD, + NOTIFY_EVENT_CID_STATUS, + (void *)&lpd_device->cid_status); + lpd_notify_uevent(lpd_device, NOTIFY_EVENT_CID_STATUS); + } + + lpd_mitigate_mode = lpd_device->lpd_mitigate_mode; + if (lpd_device->lpd_status == LPD_STATUS_ONLINE && + lpd_device->tud_status == LPD_STATUS_ONLINE && + lpd_device->lpd_mitigate_mode != lpd_device->mitigate_mode_cfg) { + lpd_mitigate_mode = lpd_device->mitigate_mode_cfg; + } else if (lpd_device->lpd_status == LPD_STATUS_OFFLINE && + lpd_device->lpd_mitigate_mode != lpd_device->def_mitigate_mode_cfg) { + lpd_mitigate_mode = lpd_device->def_mitigate_mode_cfg; + } else if (lpd_device->lpd_mitigate_mode >= LPD_MITIGATE_INVALID) { + lpd_mitigate_mode = lpd_device->def_mitigate_mode_cfg; + } + if (lpd_mitigate_mode != lpd_device->lpd_mitigate_mode) { + rc = qti_charger_set_property(OEM_PROP_LPD_MITIGATE_MODE, + &lpd_mitigate_mode, + sizeof(lpd_mitigate_mode)); + if (rc) { + pr_err("Failed to set lpd mitigate mode, rc=%d\n", rc); + } else { + pr_info("LPD mitigate mode transit: %d -> %d\n", + lpd_device->lpd_mitigate_mode, + lpd_mitigate_mode); + lpd_device->lpd_mitigate_mode = lpd_mitigate_mode; + } + } +} + +static int lpd_psy_notifier_call(struct notifier_block *nb, unsigned long val, + void *v) +{ + struct lpd_mitigate_device *lpd_device = container_of(nb, + struct lpd_mitigate_device, lpd_psy_notifier); + struct power_supply *psy = v; + + if (!lpd_device) { + pr_err("called before lpd_device valid!\n"); + return NOTIFY_DONE; + } + + if (val != PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + if (psy && strcmp(psy->desc->name, "usb") == 0) { + schedule_work(&lpd_device->lpd_mitigate_work); + } + + return NOTIFY_OK; +} + +static ssize_t def_mitigate_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + struct lpd_mitigate_device *lpd_device = dev_get_drvdata(dev); + + if (!lpd_device) { + pr_err("lpd device not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("Invalid mode = %lu\n", mode); + return -EINVAL; + } + + if (mode >= LPD_MITIGATE_INVALID) + lpd_device->def_mitigate_mode_cfg = LPD_MITIGATE_INVALID; + else + lpd_device->def_mitigate_mode_cfg = mode; + + schedule_work(&lpd_device->lpd_mitigate_work); + + return count; +} + +static ssize_t def_mitigate_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct lpd_mitigate_device *lpd_device = dev_get_drvdata(dev); + + if (!lpd_device) { + pr_err("lpd device not valid\n"); + return -ENODEV; + } + + return scnprintf(buf, LPD_SHOW_MAX_SIZE, "%d\n", + lpd_device->def_mitigate_mode_cfg); +} +DEVICE_ATTR_RW(def_mitigate_mode); + +static ssize_t mitigate_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + struct lpd_mitigate_device *lpd_device = dev_get_drvdata(dev); + + if (!lpd_device) { + pr_err("lpd device not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("Invalid mode = %lu\n", mode); + return -EINVAL; + } + + if (mode >= LPD_MITIGATE_INVALID) + lpd_device->mitigate_mode_cfg = LPD_MITIGATE_INVALID; + else + lpd_device->mitigate_mode_cfg = mode; + + schedule_work(&lpd_device->lpd_mitigate_work); + + return count; +} + +static ssize_t mitigate_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct lpd_mitigate_device *lpd_device = dev_get_drvdata(dev); + + if (!lpd_device) { + pr_err("lpd device not valid\n"); + return -ENODEV; + } + + return scnprintf(buf, LPD_SHOW_MAX_SIZE, "%d\n", + lpd_device->mitigate_mode_cfg); +} +DEVICE_ATTR_RW(mitigate_mode); + +static int lpd_mitigate_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct lpd_mitigate_device *lpd_device; + int rc; + + lpd_device = devm_kzalloc(dev, sizeof(*lpd_device), GFP_KERNEL); + if (!lpd_device) + return -ENOMEM; + + INIT_WORK(&lpd_device->lpd_mitigate_work, lpd_mitigate_work); + platform_set_drvdata(pdev, lpd_device); + lpd_device->dev = dev; + lpd_device->cid_status = INT_MAX; + lpd_device->lpd_status = INT_MAX; + lpd_device->tud_status = INT_MAX; + lpd_device->lpd_mitigate_mode = INT_MAX; + + rc = of_property_read_u32(dev->of_node, + "mmi,default-mitigate-mode-cfg", + &lpd_device->def_mitigate_mode_cfg); + if (rc || lpd_device->def_mitigate_mode_cfg > LPD_MITIGATE_INVALID) + lpd_device->mitigate_mode_cfg = LPD_MITIGATE_SNK; + + rc = of_property_read_u32(dev->of_node, + "mmi,mitigate-mode-cfg", + &lpd_device->mitigate_mode_cfg); + if (rc || lpd_device->mitigate_mode_cfg > LPD_MITIGATE_INVALID) + lpd_device->mitigate_mode_cfg = LPD_MITIGATE_DISABLE; + + lpd_device->lpd_mitigate_notify.notifier_call = lpd_mitigate_notify_cb; + /*register a blocking notification to receive LPD events*/ + rc = relay_register_action(BLOCKING, LPD, &lpd_device->lpd_mitigate_notify); + if (rc < 0) { + pr_err("Failed to register tud_notifier: %d\n", rc); + return rc; + } + + lpd_device->lpd_psy_notifier.notifier_call = lpd_psy_notifier_call; + rc = power_supply_reg_notifier(&lpd_device->lpd_psy_notifier); + if (rc) { + pr_err("Failed to register lpd_psy_notifier: %d\n", rc); + return rc; + } + + rc = device_create_file(lpd_device->dev, &dev_attr_def_mitigate_mode); + if (rc) { + pr_err("Couldn't create default mitigate mode\n"); + return rc; + } + rc = device_create_file(lpd_device->dev, &dev_attr_mitigate_mode); + if (rc) { + pr_err("Couldn't create mitigate mode\n"); + return rc; + } + + pr_info("lpd mitigate driver probe complete\n"); + return 0; +} + +static int lpd_mitigate_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct lpd_mitigate_device *lpd_device = dev_get_drvdata(dev); + + cancel_work_sync(&lpd_device->lpd_mitigate_work); + device_remove_file(lpd_device->dev, &dev_attr_mitigate_mode); + device_remove_file(lpd_device->dev, &dev_attr_def_mitigate_mode); + power_supply_unreg_notifier(&lpd_device->lpd_psy_notifier); + relay_unregister_action(BLOCKING, LPD, &lpd_device->lpd_mitigate_notify); + if (lpd_device->batt_psy) { + power_supply_put(lpd_device->batt_psy); + lpd_device->batt_psy = NULL; + } + + return 0; +} + +static const struct of_device_id lpd_mitigate_match_table[] = { + {.compatible = "mmi,lpd-mitigate"}, + {}, +}; + +static struct platform_driver lpd_mitigate_driver = { + .driver = { + .name = "mmi_lpd_mitigate", + .of_match_table = lpd_mitigate_match_table, + }, + .probe = lpd_mitigate_probe, + .remove = lpd_mitigate_remove, +}; + +module_platform_driver(lpd_mitigate_driver); + +MODULE_DESCRIPTION("MMI LPD Mitigate Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/mmi_parallel_charger/Android.mk b/drivers/power/mmi_parallel_charger/Android.mk new file mode 100644 index 000000000000..de1f141bafee --- /dev/null +++ b/drivers/power/mmi_parallel_charger/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_parallel_charger.ko +LOCAL_MODULE_TAGS := optional + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/mmi_parallel_charger/Kbuild b/drivers/power/mmi_parallel_charger/Kbuild new file mode 100644 index 000000000000..3c93b07997cf --- /dev/null +++ b/drivers/power/mmi_parallel_charger/Kbuild @@ -0,0 +1,14 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/power/mmi_parallel_charger + +obj-m += mmi_parallel_charger.o +mmi_parallel_charger-objs += mmi_charger_class.o +mmi_parallel_charger-objs += mmi_charger_core.o +mmi_parallel_charger-objs += mmi_charger_pump_policy.o +mmi_parallel_charger-objs += bq2597x_charger.o +mmi_parallel_charger-objs += qpnp_pmic_charger.o + +#ifeq ($(CONFIG_MMI_PL_CP_POLICY), y) +#endif diff --git a/drivers/power/mmi_parallel_charger/Makefile b/drivers/power/mmi_parallel_charger/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/power/mmi_parallel_charger/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/power/mmi_parallel_charger/bq2597x_charger.c b/drivers/power/mmi_parallel_charger/bq2597x_charger.c new file mode 100644 index 000000000000..5860f298498b --- /dev/null +++ b/drivers/power/mmi_parallel_charger/bq2597x_charger.c @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include +#include "mmi_charger_core.h" + +#define BAT_OVP_FAULT_SHIFT 8 +#define BAT_OCP_FAULT_SHIFT 9 +#define BUS_OVP_FAULT_SHIFT 10 +#define BUS_OCP_FAULT_SHIFT 11 +#define BAT_THERM_FAULT_SHIFT 12 +#define BUS_THERM_FAULT_SHIFT 13 +#define DIE_THERM_FAULT_SHIFT 14 +#define CONV_OCP_FAULT_SHIFT 15 +#define SS_TIMEOUT_FAULT_SHIFT 16 +#define TS_SHUT_FAULT_SHIFT 17 + +#define BAT_OVP_FAULT_MASK (1 << BAT_OVP_FAULT_SHIFT) +#define BAT_OCP_FAULT_MASK (1 << BAT_OCP_FAULT_SHIFT) +#define BUS_OVP_FAULT_MASK (1 << BUS_OVP_FAULT_SHIFT) +#define BUS_OCP_FAULT_MASK (1 << BUS_OCP_FAULT_SHIFT) +#define BAT_THERM_FAULT_MASK (1 << BAT_THERM_FAULT_SHIFT) +#define BUS_THERM_FAULT_MASK (1 << BUS_THERM_FAULT_SHIFT) +#define DIE_THERM_FAULT_MASK (1 << DIE_THERM_FAULT_SHIFT) +#define CONV_OCP_FAULT_MASK (1 << CONV_OCP_FAULT_SHIFT) +#define SS_TIMEOUT_FAULT_MASK (1 << SS_TIMEOUT_FAULT_SHIFT) +#define TS_SHUT_FAULT_MASK (1 << TS_SHUT_FAULT_SHIFT) + +#define BAT_OVP_ALARM_SHIFT 0 +#define BAT_OCP_ALARM_SHIFT 1 +#define BUS_OVP_ALARM_SHIFT 2 +#define BUS_OCP_ALARM_SHIFT 3 +#define BUS_UCP_FAULT_SHIFT 4 +#define BUS_THERM_ALARM_SHIFT 5 +#define DIE_THERM_ALARM_SHIFT 6 +#define BAT_UCP_ALARM_SHIFT 7 + +#define BAT_OVP_ALARM_MASK (1 << BAT_OVP_ALARM_SHIFT) +#define BAT_OCP_ALARM_MASK (1 << BAT_OCP_ALARM_SHIFT) +#define BUS_OVP_ALARM_MASK (1 << BUS_OVP_ALARM_SHIFT) +#define BUS_OCP_ALARM_MASK (1 << BUS_OCP_ALARM_SHIFT) +#define BUS_UCP_FAULT_MASK (1 << BUS_UCP_FAULT_SHIFT) +#define BUS_THERM_ALARM_MASK (1 << BUS_THERM_ALARM_SHIFT) +#define DIE_THERM_ALARM_MASK (1 << DIE_THERM_ALARM_SHIFT) +#define BAT_UCP_ALARM_MASK (1 << BAT_UCP_ALARM_SHIFT) + +static int bq2597x_enable_charging(struct mmi_charger_device *chrg, bool en) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + prop.intval = en; + rc = power_supply_set_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop); + + if (!rc) { + chrg->charger_enabled = !!prop.intval; + } else + chrg->charger_enabled = false; + + return rc; +} + +static int bq2597x_is_charging_enabled(struct mmi_charger_device *chrg, bool *en) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop); + if (!rc) { + chrg->charger_enabled = !!prop.intval; + } else + chrg->charger_enabled = false; + + *en = chrg->charger_enabled; + + return rc; +} + +static int bq2597x_get_charging_current(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + *uA = !!prop.intval; + + return rc; +} + +static int bq2597x_get_input_current(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, &prop); + if (!rc) + *uA = prop.intval; + + return rc; +} + +static int bq2597x_update_charger_status(struct mmi_charger_device *chrg) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + chrg->charger_data.vbatt_volt = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + chrg->charger_data.ibatt_curr = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_TEMP, &prop); + if (!rc) + chrg->charger_data.batt_temp = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED, &prop); + if (!rc) + chrg->charger_data.vbus_volt = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, &prop); + if (!rc) + chrg->charger_data.ibus_curr = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (!rc) + chrg->charger_data.vbus_pres = !!prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop); + if (!rc) + chrg->charger_enabled = !!prop.intval; + + chrg_dev_info(chrg, "BQ2597x chrg: %s status update: --- info---\n",chrg->name); + chrg_dev_info(chrg, "vbatt %d\n", chrg->charger_data.vbatt_volt); + chrg_dev_info(chrg, "ibatt %d\n", chrg->charger_data.ibatt_curr); + chrg_dev_info(chrg, "batt temp %d\n", chrg->charger_data.batt_temp); + chrg_dev_info(chrg, "vbus %d\n", chrg->charger_data.vbus_volt); + chrg_dev_info(chrg, "ibus %d\n", chrg->charger_data.ibus_curr); + chrg_dev_info(chrg, "vbus pres %d\n", chrg->charger_data.vbus_pres); + chrg_dev_info(chrg, "charger_enabled %d\n", chrg->charger_enabled); + + return rc; +} + +static int bq2597x_update_charger_error_status(struct mmi_charger_device *chrg) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CP_STATUS1, &prop); + if (!rc) { + chrg->charger_error.chrg_err_type = + ((!!(prop.intval & BAT_OVP_ALARM_MASK)) ? + 1 << MMI_BAT_OVP_ALARM_BIT : + 0 << MMI_BAT_OVP_ALARM_BIT) | + ((!!(prop.intval & BAT_OCP_ALARM_MASK)) ? + 1 << MMI_BAT_OCP_ALARM_BIT : + 0 << MMI_BAT_OCP_ALARM_BIT) | + ((!!(prop.intval & BAT_UCP_ALARM_MASK)) ? + 1 << MMI_BAT_UCP_ALARM_BIT : + 0 << MMI_BAT_UCP_ALARM_BIT) | + ((!!(prop.intval & BUS_OVP_ALARM_MASK)) ? + 1 << MMI_BUS_OVP_ALARM_BIT : + 0 << MMI_BUS_OVP_ALARM_BIT) | + ((!!(prop.intval & BUS_OCP_ALARM_MASK)) ? + 1 << MMI_BUS_OCP_ALARM_BIT : + 0 << MMI_BUS_OCP_ALARM_BIT) | + ((!!(prop.intval & BAT_OVP_FAULT_MASK)) ? + 1 << MMI_BAT_OVP_FAULT_BIT : + 0 << MMI_BAT_OVP_FAULT_BIT) | + ((!!(prop.intval & BAT_OCP_FAULT_MASK)) ? + 1 << MMI_BAT_OCP_FAULT_BIT : + 0 << MMI_BAT_OCP_FAULT_BIT) | + ((!!(prop.intval & BUS_OVP_FAULT_MASK)) ? + 1 << MMI_BUS_OVP_FAULT_BIT : + 0 << MMI_BUS_OVP_FAULT_BIT) | + ((!!(prop.intval & BUS_OCP_FAULT_MASK)) ? + 1 << MMI_BUS_OCP_FAULT_BIT : + 0 << MMI_BUS_OCP_FAULT_BIT) | + ((!!(prop.intval & BUS_UCP_FAULT_MASK)) ? + 1 << MMI_BUS_UCP_FAULT_BIT : + 0 << MMI_BUS_UCP_FAULT_BIT) | + ((!!(prop.intval & CONV_OCP_FAULT_MASK)) ? + 1 << MMI_CONV_OCP_FAULT_BIT : + 0 << MMI_CONV_OCP_FAULT_BIT); + } + return rc; +} + +static int bq2597x_clear_charger_error(struct mmi_charger_device *chrg) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_set_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_UPDATE_NOW, &prop); + + return rc; +} + +struct mmi_charger_ops bq2597x_charger_ops = { + .enable = bq2597x_enable_charging, + .is_enabled = bq2597x_is_charging_enabled, + .get_charging_current = bq2597x_get_charging_current, + .get_input_current = bq2597x_get_input_current, + .update_charger_status = bq2597x_update_charger_status, + .update_charger_error = bq2597x_update_charger_error_status, + .clear_charger_error = bq2597x_clear_charger_error, +}; diff --git a/drivers/power/mmi_parallel_charger/mmi_charger_class.c b/drivers/power/mmi_parallel_charger/mmi_charger_class.c new file mode 100644 index 000000000000..bb0d752b61b1 --- /dev/null +++ b/drivers/power/mmi_parallel_charger/mmi_charger_class.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" + +static struct class *mmi_charger_class; + +int mmi_enable_charging(struct mmi_charger_device *chrg, bool en) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->enable) + return chrg->ops->enable(chrg,en); + + return -ENOTSUPP; +} + +int mmi_is_charging_enabled(struct mmi_charger_device *chrg, bool *en) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->is_enabled) + return chrg->ops->is_enabled(chrg,en); + + return -ENOTSUPP; +} + +int mmi_get_charing_current(struct mmi_charger_device *chrg, u32 *uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->get_charging_current) + return chrg->ops->get_charging_current(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_set_charing_current(struct mmi_charger_device *chrg, u32 uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->set_charging_current) + return chrg->ops->set_charging_current(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_get_input_current_settled(struct mmi_charger_device *chrg, u32 *uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->get_input_current_settled) + return chrg->ops->get_input_current_settled(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_get_input_current(struct mmi_charger_device *chrg, u32 *uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->get_input_current) + return chrg->ops->get_input_current(chrg,uA); + + return -ENOTSUPP; + +} + +int mmi_set_input_current(struct mmi_charger_device *chrg, u32 uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->set_input_current) + return chrg->ops->set_input_current(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_update_charger_status(struct mmi_charger_device *chrg) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->update_charger_status) + return chrg->ops->update_charger_status(chrg); + + return -ENOTSUPP; +} + +int mmi_update_charger_error(struct mmi_charger_device *chrg) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->update_charger_error) + return chrg->ops->update_charger_error(chrg); + + return -ENOTSUPP; +} + +int mmi_clear_charger_error(struct mmi_charger_device *chrg) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->clear_charger_error) + return chrg->ops->clear_charger_error(chrg); + + return -ENOTSUPP; +} + +static void mmi_charger_device_release(struct device *dev) +{ + struct mmi_charger_device *charger_dev = to_mmi_charger_device(dev); + + kfree(charger_dev); + return; +} + +static int charger_match_device_by_name(struct device *dev, + const void *data) +{ + const char *name = data; + return strcmp(dev_name(dev), name) == 0; +} + +struct mmi_charger_device *get_charger_by_name(const char *name) +{ + struct device *dev; + + if(!name) + return (struct mmi_charger_device *)NULL; + dev = class_find_device(mmi_charger_class, NULL, name, + charger_match_device_by_name); + + return dev ? to_mmi_charger_device(dev) : NULL; +} + +int is_charger_exist(const char *name) +{ + if (get_charger_by_name(name) == NULL) + return 0; + return 1; +} + +struct mmi_charger_device *mmi_charger_device_register(const char *name, + const char *psy_name,struct device *parent, void *devdata, + const struct mmi_charger_ops *ops) +{ + struct mmi_charger_device *charger_dev; + int rc; + + pr_info("mmi charger device register: name=%s\n",name); + charger_dev = kzalloc(sizeof(struct mmi_charger_device),GFP_KERNEL); + if (!charger_dev) + return ERR_PTR(-ENOMEM); + + mutex_init(&charger_dev->ops_lock); + charger_dev->dev.class = mmi_charger_class; + charger_dev->dev.parent = parent; + charger_dev->dev.release = mmi_charger_device_release; + charger_dev->name = name; + dev_set_name(&charger_dev->dev, "%s", name); + dev_set_drvdata(&charger_dev->dev, devdata); + + rc = device_register(&charger_dev->dev); + if (rc) { + kfree(charger_dev); + return ERR_PTR(rc); + } + charger_dev->chrg_psy = power_supply_get_by_name(psy_name); + charger_dev->ops = ops; + if (!charger_dev->chrg_psy) + return ERR_PTR(-ENODEV); + pr_info("mmi charger device register: name=%s, successfully\n",name); + return charger_dev; +} + +EXPORT_SYMBOL(mmi_charger_device_register); + +void mmi_charger_device_unregister(struct mmi_charger_device* charger_dev) +{ + if (!charger_dev) + return; + + mutex_lock(&charger_dev->ops_lock); + charger_dev->ops = NULL; + mutex_unlock(&charger_dev->ops_lock); + device_unregister(&charger_dev->dev); +} + +EXPORT_SYMBOL(mmi_charger_device_unregister); + +void mmi_charger_class_exit(void) +{ + class_destroy(mmi_charger_class); +} + +int mmi_charger_class_init(void) +{ + mmi_charger_class = class_create(THIS_MODULE, "mmi_charger"); + if (IS_ERR(mmi_charger_class)) { + pr_err("Unable to create mmi charger class; error = %ld\n", + PTR_ERR(mmi_charger_class)); + return PTR_ERR(mmi_charger_class); + } + pr_info("success to create mmi charger class \n"); + return 0; +} diff --git a/drivers/power/mmi_parallel_charger/mmi_charger_class.h b/drivers/power/mmi_parallel_charger/mmi_charger_class.h new file mode 100644 index 000000000000..1b3cb6503eaa --- /dev/null +++ b/drivers/power/mmi_parallel_charger/mmi_charger_class.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + #ifndef __MMI_CHRG_CLASS_H_ + #define __MMI_CHRG_CLASS_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define chrg_dev_info(chip, fmt, ...) \ + pr_info("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__) \ + +struct mmi_charger_info { + bool vbus_pres; + int vbatt_volt; + int vbus_volt; + int ibatt_curr; + int ibus_curr; + int batt_temp; +}; + +#define MMI_BAT_OVP_ALARM_BIT 0 +#define MMI_BAT_OCP_ALARM_BIT 1 +#define MMI_BAT_UCP_ALARM_BIT 2 +#define MMI_BUS_OVP_ALARM_BIT 3 +#define MMI_BUS_OCP_ALARM_BIT 4 +#define MMI_BUS_UCP_ALARM_BIT 5 +#define MMI_BAT_OVP_FAULT_BIT 6 +#define MMI_BAT_OCP_FAULT_BIT 7 +#define MMI_BAT_UCP_FAULT_BIT 8 +#define MMI_BUS_OVP_FAULT_BIT 9 +#define MMI_BUS_OCP_FAULT_BIT 10 +#define MMI_BUS_UCP_FAULT_BIT 11 +#define MMI_CONV_OCP_FAULT_BIT 12 +#define MMI_THERM_ALARM_BIT 13 +#define MMI_THERM_FAULT_BIT 14 + +struct mmi_charger_error_info { + int chrg_err_type; + int bus_ucp_err_cnt; + int bus_ocp_err_cnt; +}; + +struct mmi_charger_device { + const char *name; /*charger device name*/ + struct power_supply *chrg_psy; /*power supply handle*/ + const struct mmi_charger_ops *ops; /*operation function*/ + struct mutex ops_lock; + struct device dev; + struct mmi_charger_info charger_data; + struct mmi_charger_error_info charger_error; + int *debug_mask; + int input_curr_setted; /*save input current*/ + int charging_curr_limited; /*charger current limitation*/ + int charging_curr_min; /*The minimum charging current supported by this device*/ + bool charger_enabled; /*is this charger device enabled*/ + bool charger_limited; /*is the charging current of this device limited*/ +}; + +struct mmi_charger_ops { + int (*enable)(struct mmi_charger_device *chrg, bool en); + int (*is_enabled)(struct mmi_charger_device *chrg, bool *en); + int (*get_charging_current)(struct mmi_charger_device *chrg, u32 *uA); + int (*set_charging_current)(struct mmi_charger_device *chrg, u32 uA); + int (*get_input_current_settled)(struct mmi_charger_device *chrg, u32 *uA); + int (*get_input_current)(struct mmi_charger_device *chrg, u32 *uA); + int (*set_input_current)(struct mmi_charger_device *chrg, u32 uA); + int (*update_charger_status)(struct mmi_charger_device *chrg); + int (*update_charger_error)(struct mmi_charger_device *chrg); + int (*clear_charger_error)(struct mmi_charger_device *chrg); +}; + + +#define to_mmi_charger_device(obj) container_of(obj, struct mmi_charger_device, dev) +#define mmi_chrg_name(x) (dev_name(&(x)->dev)) + +extern struct mmi_charger_device *get_charger_by_name(const char *name); +extern int is_charger_exist(const char *name); +extern int mmi_charger_class_init(void); +extern void mmi_charger_class_exit(void); +extern int mmi_enable_charging(struct mmi_charger_device *chrg, bool en); +extern int mmi_is_charging_enabled(struct mmi_charger_device *chrg, bool *en); +extern int mmi_get_charing_current(struct mmi_charger_device *chrg, u32 *uA); +extern int mmi_set_charing_current(struct mmi_charger_device *chrg, u32 uA); +extern int mmi_get_input_current_settled(struct mmi_charger_device *chrg, u32 *uA); +extern int mmi_get_input_current(struct mmi_charger_device *chrg, u32 *uA); +extern int mmi_set_input_current(struct mmi_charger_device *chrg, u32 uA); +extern int mmi_update_charger_status(struct mmi_charger_device *chrg); +extern int mmi_update_charger_error(struct mmi_charger_device *chrg); +extern int mmi_clear_charger_error(struct mmi_charger_device *chrg); +extern struct mmi_charger_device *mmi_charger_device_register(const char *name, + const char *psy_name,struct device *parent, void *devdata, + const struct mmi_charger_ops *ops); +extern void mmi_charger_device_unregister(struct mmi_charger_device* charger_dev); +#endif diff --git a/drivers/power/mmi_parallel_charger/mmi_charger_core.c b/drivers/power/mmi_parallel_charger/mmi_charger_core.c new file mode 100644 index 000000000000..090159e5e558 --- /dev/null +++ b/drivers/power/mmi_parallel_charger/mmi_charger_core.c @@ -0,0 +1,2021 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include "mmi_charger_core.h" +#include "mmi_charger_policy.h" + +static int __debug_mask = 0x85; +module_param_named( + debug_mask, __debug_mask, int, S_IRUSR | S_IWUSR +); + +static int pd_volt_max_init = 0; +static int pd_curr_max_init = 0; +static int *dt_temp_zones; +static struct mmi_chrg_dts_info *chrg_name_list; +static char *charge_rate[] = { + "None", "Normal", "Weak", "Turbo", "Turbo_30W", "Hyper" +}; +#define MIN_TEMP_C -20 +#define MAX_TEMP_C 60 +#define HYSTEREISIS_DEGC 2 +bool mmi_chrg_rate_check(struct mmi_charger_manager *chg); +bool mmi_find_temp_zone(struct mmi_charger_manager *chip, int temp_c, bool ignore_hysteresis_degc) +{ + int prev_zone, num_zones; + struct mmi_chrg_temp_zone *zones; + int hotter_t = 0, hotter_fcc = 0; + int colder_t = 0, colder_fcc = 0; + int i; + int max_temp; + + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return false; + } + + zones = chip->temp_zones; + num_zones = chip->num_temp_zones; + prev_zone = chip->pres_temp_zone; + + mmi_chrg_info(chip, "prev zone %d, temp_c %d\n",prev_zone, temp_c); + max_temp = zones[num_zones - 1].temp_c; + + if (prev_zone == ZONE_NONE) { + for (i = num_zones - 1; i >= 0; i--) { + if (temp_c >= zones[i].temp_c) { + if (i == num_zones - 1) + chip->pres_temp_zone = ZONE_HOT; + else + chip->pres_temp_zone = i + 1; + return true; + } + } + chip->pres_temp_zone = ZONE_COLD; + return true; + } + + if (prev_zone == ZONE_COLD) { + if (temp_c >= MIN_TEMP_C + HYSTEREISIS_DEGC) + chip->pres_temp_zone = ZONE_FIRST; + } else if (prev_zone == ZONE_HOT) { + if (temp_c <= max_temp - HYSTEREISIS_DEGC) + chip->pres_temp_zone = num_zones - 1; + } else { + if (prev_zone == ZONE_FIRST) { + hotter_t = zones[prev_zone].temp_c; + colder_t = MIN_TEMP_C; + hotter_fcc = zones[prev_zone + 1].chrg_step_power->chrg_step_curr; + colder_fcc = 0; + } else if (prev_zone == num_zones - 1) { + hotter_t = zones[prev_zone].temp_c; + colder_t = zones[prev_zone - 1].temp_c; + hotter_fcc = 0; + colder_fcc = zones[prev_zone - 1].chrg_step_power->chrg_step_curr; + } else { + hotter_t = zones[prev_zone].temp_c; + colder_t = zones[prev_zone - 1].temp_c; + hotter_fcc = zones[prev_zone + 1].chrg_step_power->chrg_step_curr; + colder_fcc = zones[prev_zone - 1].chrg_step_power->chrg_step_curr; + } + + if (!ignore_hysteresis_degc) { + if (zones[prev_zone].chrg_step_power->chrg_step_curr < hotter_fcc) + hotter_t += HYSTEREISIS_DEGC; + if (zones[prev_zone].chrg_step_power->chrg_step_curr < colder_fcc) + colder_t -= HYSTEREISIS_DEGC; + } + + if (temp_c <= MIN_TEMP_C) + chip->pres_temp_zone = ZONE_COLD; + else if (temp_c >= max_temp) + chip->pres_temp_zone = ZONE_HOT; + else if (temp_c >= hotter_t) + chip->pres_temp_zone++; + else if (temp_c < colder_t) + chip->pres_temp_zone--; + } + + mmi_chrg_info(chip, "batt temp_c %d, prev zone %d, pres zone %d, " + "hotter_fcc %dmA, colder_fcc %dmA, " + "hotter_t %dC, colder_t %dC\n", + temp_c,prev_zone, chip->pres_temp_zone, + hotter_fcc, colder_fcc, hotter_t, colder_t); + + if (prev_zone != chip->pres_temp_zone) { + mmi_chrg_info(chip, + "Entered Temp Zone %d!\n", + chip->pres_temp_zone); + return true; + } + return false; +} + +bool mmi_find_chrg_step(struct mmi_charger_manager *chip, int temp_zone, int vbatt_volt) +{ + int batt_volt, i; + bool find_step = false; + struct mmi_chrg_temp_zone zone; + struct mmi_chrg_step_power *chrg_steps; + struct mmi_chrg_step_info chrg_step_inline; + struct mmi_chrg_step_info prev_step; + + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return false; + } + + if (chip->pres_temp_zone == ZONE_HOT || + chip->pres_temp_zone == ZONE_COLD || + chip->pres_temp_zone < ZONE_FIRST) { + mmi_chrg_err(chip, "pres temp zone is HOT or COLD, " + "can't find chrg step\n"); + return false; + } + + zone = chip->temp_zones[chip->pres_temp_zone]; + chrg_steps = zone.chrg_step_power; + prev_step = chip->chrg_step; + + batt_volt = vbatt_volt; + chrg_step_inline.temp_c = zone.temp_c; + + mmi_chrg_info(chip, "batt_volt %d, chrg step %d, step nums %d\n", + batt_volt, prev_step.pres_chrg_step, + chip->chrg_step_nums); + + /*In the first search cycle, find out the vbatt is less than step volt*/ + + for (i = 0; i < chip->chrg_step_nums; i++) { + mmi_chrg_info(chip, + "first cycle,i %d, step volt %d, batt_volt %d\n", + i, chrg_steps[i].chrg_step_volt, batt_volt); + if (chrg_steps[i].chrg_step_volt > 0 + && batt_volt < chrg_steps[i].chrg_step_volt) { + if ( (i + 1) < chip->chrg_step_nums + && chrg_steps[i + 1].chrg_step_volt > 0) { + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i + 1].chrg_step_curr; + } else + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cc_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cv_volt = + chrg_steps[i].chrg_step_volt; + chrg_step_inline.pres_chrg_step = i; + find_step = true; + mmi_chrg_info(chip, "find chrg step\n"); + break; + } + } + + if (find_step) { + mmi_chrg_info(chip, "chrg step %d, " + "step cc curr %d, step cv volt %d, " + "step cv tapper curr %d\n", + chrg_step_inline.pres_chrg_step, + chrg_step_inline.chrg_step_cc_curr, + chrg_step_inline.chrg_step_cv_volt, + chrg_step_inline.chrg_step_cv_tapper_curr); + chip->chrg_step = chrg_step_inline; + } else { + + /*If can't find out any chrg step in the first search cycle,*/ + /*it means that vbatt is already greater than all chrg step volt, */ + /*therefore, start to enter second search cycle, */ + /*to find out the maximal chrg step volt*/ + for (i = 0; i < chip->chrg_step_nums; i++) { + mmi_chrg_info(chip, + "second cycle, i %d, step volt %d, batt_volt %d\n", + i, chrg_steps[i].chrg_step_volt, batt_volt); + if (chrg_steps[i].chrg_step_volt > 0 + && batt_volt > chrg_steps[i].chrg_step_volt) { + if ( (i + 1) < chip->chrg_step_nums + && chrg_steps[i + 1].chrg_step_volt > 0) { + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i + 1].chrg_step_curr; + } else + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cc_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cv_volt = + chrg_steps[i].chrg_step_volt; + chrg_step_inline.pres_chrg_step = i; + find_step = true; + mmi_chrg_info(chip, "find chrg step\n"); + } + } + + if (find_step) { + mmi_chrg_info(chip, "chrg step %d, " + "step cc curr %d, step cv volt %d, " + "step cv tapper curr %d\n", + chrg_step_inline.pres_chrg_step, + chrg_step_inline.chrg_step_cc_curr, + chrg_step_inline.chrg_step_cv_volt, + chrg_step_inline.chrg_step_cv_tapper_curr); + chip->chrg_step = chrg_step_inline; + } + } + + + if (find_step) { + if (chip->chrg_step.chrg_step_cc_curr == + chip->chrg_step.chrg_step_cv_tapper_curr) + chip->chrg_step.last_step = true; + else + chip->chrg_step.last_step = false; + + mmi_chrg_info(chip,"Temp zone %d, " + "select chrg step %d, step cc curr %d," + "step cv volt %d, step cv tapper curr %d, " + "is the last chrg step %d\n", + chip->pres_temp_zone, + chip->chrg_step.pres_chrg_step, + chip->chrg_step.chrg_step_cc_curr, + chip->chrg_step.chrg_step_cv_volt, + chip->chrg_step.chrg_step_cv_tapper_curr, + chip->chrg_step.last_step); + + if (prev_step.pres_chrg_step != chip->chrg_step.pres_chrg_step) { + mmi_chrg_info(chip, "Find the next chrg step\n"); + return true; + } + } + return false; +} + +void mmi_set_pps_result_history(struct mmi_charger_manager *chip, int pps_result) +{ + if (chip->pps_result_history_idx >= PPS_RET_HISTORY_SIZE -1) + chip->pps_result_history_idx = 0; + + if (pps_result < 0) + chip->pps_result_history[chip->pps_result_history_idx] = 1; + else + chip->pps_result_history[chip->pps_result_history_idx] = 0; + + chip->pps_result_history_idx++; +} + +bool mmi_get_pps_result_history(struct mmi_charger_manager *chip) +{ + int i = 0; + int result = 0; + for (i = 0; i < PPS_RET_HISTORY_SIZE; i++) + result += chip->pps_result_history[i]; + + if (result >= PPS_RET_HISTORY_SIZE) + return RESET_POWER; + else if (result >= PPS_RET_HISTORY_SIZE / 2) + return BLANCE_POWER; + else + return NO_ERROR; +} + +void mmi_clear_pps_result_history(struct mmi_charger_manager *chip) +{ + int i = 0;; + for (i = 0; i < PPS_RET_HISTORY_SIZE; i++) + chip->pps_result_history[i] = 0; +} + +int mmi_calculate_delta_volt(int pps_voltage, int pps_current, int delta_curr) +{ + u64 power; + int delta_volt; + u64 pps_volt; + u64 pps_curr; + + pps_volt = pps_voltage; + pps_curr = pps_current; + power = pps_volt * pps_curr; + + delta_volt = (int)(power / (pps_curr - delta_curr) - pps_volt); + delta_volt -= delta_volt % 20000; + + if (delta_volt > 0) + return delta_volt; + else + return 0; +} + +void mmi_update_all_charger_status(struct mmi_charger_manager *chip) +{ + int chrg_num, i; + struct mmi_charger_device *chrg_dev; + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return; + } + + chrg_num = chip->mmi_chrg_dev_num; + for (i = 0; i < chrg_num; i++) { + chrg_dev = chip->chrg_list[i]; + mmi_update_charger_status(chrg_dev); + } + return; +} + +void mmi_update_all_charger_error(struct mmi_charger_manager *chip) +{ + int chrg_num, i; + struct mmi_charger_device *chrg_dev; + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return; + } + + chrg_num = chip->mmi_chrg_dev_num; + for (i = 0; i < chrg_num; i++) { + chrg_dev = chip->chrg_list[i]; + mmi_update_charger_error(chrg_dev); + } + return; +} + +void mmi_dump_charger_error(struct mmi_charger_manager *chip, + struct mmi_charger_device *chrg_dev) +{ + if (!chip || !chrg_dev) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return; + } + mmi_chrg_dbg(chip, PR_MOTO, "%s: charger error type %d, " + "bus ucp err cnt %d, bus ocp err cnt %d\n", + chrg_dev->name, + chrg_dev->charger_error.chrg_err_type, + chrg_dev->charger_error.bus_ucp_err_cnt, + chrg_dev->charger_error.bus_ocp_err_cnt); + return; +} +#if 0 +static enum power_supply_property batt_props[] = { + POWER_SUPPLY_PROP_INPUT_SUSPEND, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGER_TEMP, + POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_QNOVO, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_QNOVO, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, + POWER_SUPPLY_PROP_SW_JEITA_ENABLED, + POWER_SUPPLY_PROP_CHARGE_DONE, + POWER_SUPPLY_PROP_PARALLEL_DISABLE, + POWER_SUPPLY_PROP_SET_SHIP_MODE, + POWER_SUPPLY_PROP_DIE_HEALTH, + POWER_SUPPLY_PROP_RERUN_AICL, + POWER_SUPPLY_PROP_DP_DM, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_RECHARGE_SOC, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_FORCE_RECHARGE, + POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE, + POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, + POWER_SUPPLY_PROP_NUM_SYSTEM_TEMP_LEVELS, + POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, + POWER_SUPPLY_PROP_CHARGE_RATE, + POWER_SUPPLY_PROP_AGE, + POWER_SUPPLY_PROP_CURRENT_MAX, +}; + +static int batt_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mmi_charger_manager *chip = power_supply_get_drvdata(psy); + union power_supply_propval prop = {0,}; + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_CAPACITY: + case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_TEMP: + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + rc = power_supply_get_property(chip->extrn_psy, + psp, &prop); + if (rc < 0) { + mmi_chrg_err(chip, "Get Unknown prop %d from extrn_psy rc = %d\n", + psp, rc); + rc = power_supply_get_property(chip->qcom_psy, + psp, &prop); + } + if (!rc) + val->intval = prop.intval; + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + if (chip->use_batt_age) { + val->intval = chip->cycles; + } else { + rc = power_supply_get_property(chip->qcom_psy, + psp, &prop); + if (rc < 0) { + mmi_chrg_err(chip, "Get Unknown prop %d rc = %d\n", psp, rc); + rc = 0; + val->intval = -EINVAL; + } + if (!rc) + val->intval = prop.intval; + } + break; + case POWER_SUPPLY_PROP_STATUS: + rc = power_supply_get_property(chip->qcom_psy, + psp, &prop); + if (rc < 0) { + mmi_chrg_err(chip, "Get Unknown prop %d rc = %d\n", psp, rc); + rc = 0; + val->intval = -EINVAL; + } else { + val->intval = prop.intval; + if (prop.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) { + rc = power_supply_get_property(chip->extrn_psy, + psp, &prop); + if (!rc && (prop.intval == POWER_SUPPLY_STATUS_CHARGING + || prop.intval == POWER_SUPPLY_STATUS_FULL)) + val->intval = prop.intval; + } + } + break; + case POWER_SUPPLY_PROP_CHARGE_RATE: + mmi_chrg_rate_check(chip); + val->intval = chip->charger_rate; + break; + default: + rc = power_supply_get_property(chip->qcom_psy, + psp, &prop); + if (rc < 0) { + mmi_chrg_err(chip, "Get Unknown prop %d rc = %d\n", psp, rc); + rc = 0; + val->intval = -EINVAL; + } + if (!rc) + val->intval = prop.intval; + break; + + } + return rc; +} + +static int batt_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct mmi_charger_manager *chip = power_supply_get_drvdata(psy); + int rc = 0; + + switch (prop) { + case POWER_SUPPLY_PROP_CYCLE_COUNT: + chip->cycles = val->intval; + break; + default: + rc = power_supply_set_property(chip->qcom_psy, + prop, val); + if (rc < 0) { + mmi_chrg_err(chip, "Get Unknown prop %d rc = %d\n", prop, rc); + rc = 0; + } + break; + } + return rc; +} + +static int batt_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + switch (prop) { + case POWER_SUPPLY_PROP_STATUS: + case POWER_SUPPLY_PROP_INPUT_SUSPEND: + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + case POWER_SUPPLY_PROP_CAPACITY: + case POWER_SUPPLY_PROP_PARALLEL_DISABLE: + case POWER_SUPPLY_PROP_DP_DM: + case POWER_SUPPLY_PROP_RERUN_AICL: + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: + case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: + case POWER_SUPPLY_PROP_DIE_HEALTH: + case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED: + case POWER_SUPPLY_PROP_CYCLE_COUNT: + return 1; + default: + break; + } + return 0; +} +static const struct power_supply_desc batt_psy_desc = { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = batt_props, + .num_properties = ARRAY_SIZE(batt_props), + .get_property = batt_get_property, + .set_property = batt_set_property, + .property_is_writeable = batt_is_writeable, +}; +static int batt_psy_register(struct mmi_charger_manager *chip) +{ + struct power_supply_config batt_cfg = {}; + + batt_cfg.drv_data = chip; + batt_cfg.of_node = chip->dev->of_node; + chip->batt_psy = power_supply_register(chip->dev, + &batt_psy_desc, + &batt_cfg); + if (IS_ERR(chip->batt_psy)) { + pr_err("Couldn't register batt_psy power supply\n"); + return PTR_ERR(chip->batt_psy); + } + + pr_info("power supply register batt_psy successfully\n"); + return 0; +} +#endif +static enum power_supply_property mmi_chrg_mgr_props[] = { + POWER_SUPPLY_PROP_PD_CURRENT_MAX, + POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, + POWER_SUPPLY_PROP_NUM_SYSTEM_TEMP_LEVELS, + POWER_SUPPLY_PROP_CP_ENABLE, +}; + +static int mmi_chrg_mgr_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mmi_charger_manager *chip = power_supply_get_drvdata(psy); + union power_supply_propval prop = {0,}; + int rc, i, chrg_cnt = 0; + + if (!chip->batt_psy) { + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) + return -ENODEV; + } + + switch (psp) { + case POWER_SUPPLY_PROP_PD_CURRENT_MAX: + val->intval = chip->pd_request_curr_prev; + break; + case POWER_SUPPLY_PROP_PD_VOLTAGE_MAX: + val->intval = chip->pd_request_volt_prev; + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED: + val->intval = chip->pd_target_curr; + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED: + val->intval = chip->pd_target_volt; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + val->intval = prop.intval; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + val->intval = prop.intval; + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + if (chip->mmi_chrg_dev_num) { + for (i = 0; i < chip->mmi_chrg_dev_num; i++) { + if (chip->chrg_list[i]->charger_enabled) + chrg_cnt++; + } + val->intval = chrg_cnt; + } else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + val->intval = chip->system_thermal_level; + break; + case POWER_SUPPLY_PROP_NUM_SYSTEM_TEMP_LEVELS: + val->intval = chip->thermal_levels; + break; + case POWER_SUPPLY_PROP_CP_ENABLE: + val->intval = !chip->cp_disable; + break; + default: + return -EINVAL; + + } + return 0; +} + +static int mmi_chrg_mgr_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct mmi_charger_manager *chip = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + if (val->intval < 0) + return -EINVAL; + + if (chip->thermal_levels <= 0) + return -EINVAL; + + if (val->intval >= chip->thermal_levels) + chip->system_thermal_level = + chip->thermal_levels - 1; + else + chip->system_thermal_level = val->intval; + + break; + case POWER_SUPPLY_PROP_PD_CURRENT_MAX: + if (val->intval > chip->pd_curr_max) + return -EINVAL; + chip->pd_curr_max = val->intval; + break; + case POWER_SUPPLY_PROP_PD_VOLTAGE_MAX: + if (val->intval > chip->pd_volt_max) + return -EINVAL; + chip->pd_volt_max= val->intval; + break; + case POWER_SUPPLY_PROP_CP_ENABLE: + if (chip->pd_pps_support + && !chip->factory_mode) + mmi_chrg_enable_all_cp(chip, val->intval); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mmi_chrg_mgr_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + int ret; + + switch (prop) { + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + case POWER_SUPPLY_PROP_PD_CURRENT_MAX: + case POWER_SUPPLY_PROP_PD_VOLTAGE_MAX: + case POWER_SUPPLY_PROP_CP_ENABLE: + ret = 1; + break; + default: + ret = 0; + break; + } + return ret; +} + +static const struct power_supply_desc mmi_chrg_mgr_psy_desc = { + .name = "mmi_chrg_manager", + .type = POWER_SUPPLY_TYPE_PARALLEL, + .properties = mmi_chrg_mgr_props, + .num_properties = ARRAY_SIZE(mmi_chrg_mgr_props), + .get_property = mmi_chrg_mgr_get_property, + .set_property = mmi_chrg_mgr_set_property, + .property_is_writeable = mmi_chrg_mgr_is_writeable, +}; + +static int mmi_chrg_mgr_psy_register(struct mmi_charger_manager *chip) +{ + struct power_supply_config mmi_chrg_mgr_cfg = {}; + + mmi_chrg_mgr_cfg.drv_data = chip; + mmi_chrg_mgr_cfg.of_node = chip->dev->of_node; + chip->mmi_chrg_mgr_psy = power_supply_register(chip->dev, + &mmi_chrg_mgr_psy_desc, + &mmi_chrg_mgr_cfg); + if (IS_ERR(chip->mmi_chrg_mgr_psy)) { + pr_err("Couldn't register mmi_chrg_mgr_psy power supply\n"); + return PTR_ERR(chip->mmi_chrg_mgr_psy); + } + + pr_info("power supply register mmi_chrg_mgr_psy successfully\n"); + return 0; +} + +static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) +{ + struct mmi_charger_manager *chip = + container_of(nb, struct mmi_charger_manager, psy_nb); + + if (!chip->usb_psy) { + mmi_chrg_err(chip, "usb psy is NULL, direct return\n"); + return NOTIFY_OK; + } + + if (ptr == chip->usb_psy && evt == PSY_EVENT_PROP_CHANGED) { + schedule_work(&chip->psy_changed_work); + + cancel_delayed_work(&chip->heartbeat_work); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(1000)); + + } + + return NOTIFY_OK; +} + +static int get_prop_charger_present(struct mmi_charger_manager *chg, + union power_supply_propval *val) +{ + int rc = -EINVAL; + val->intval = 0; + + if (chg->usb_psy) + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_TYPEC_MODE, val); + if (rc < 0) { + mmi_chrg_err(chg, "Couldn't read TypeC Mode rc=%d\n", rc); + return rc; + } + + switch (val->intval) { + case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT: + case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM: + case POWER_SUPPLY_TYPEC_SOURCE_HIGH: + val->intval = 1; + break; + default: + val->intval = 0; + break; + } + + return rc; +} + +#define WEAK_CHRG_THRSH 450 +#define TURBO_CHRG_THRSH 2500 +#define TURBO_30W_CHRG_THRSH_UW 25000000 +#define HYPER_CHRG_THRSH_UW 40000000 + +bool mmi_chrg_rate_check(struct mmi_charger_manager *chg) +{ + union power_supply_propval val; + int chrg_cm_ma = 0; + int chrg_cs_ma = 0; + int charger_power = 0; + int prev_chg_rate = chg->charger_rate; + int rc = -EINVAL; + + if (!chg->usb_psy) { + mmi_chrg_err(chg, "No usb PSY\n"); + return false; + } + + val.intval = 0; + rc = get_prop_charger_present(chg, &val); + if (rc < 0) { + mmi_chrg_err(chg, "Error getting Charger Present rc = %d\n", rc); + return false; + } + + if (val.intval) { + val.intval = 0; + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_HW_CURRENT_MAX, &val); + if (rc < 0) { + mmi_chrg_info(chg, "Error getting HW Current Max rc = %d\n", rc); + return false; + } + chrg_cm_ma = val.intval / 1000; + + val.intval = 0; + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, &val); + if (rc < 0) { + mmi_chrg_err(chg, "Error getting ICL Settled rc = %d\n", rc); + return false; + } + chrg_cs_ma = val.intval / 1000; + } else { + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_NONE; + goto end_rate_check; + } + + mmi_chrg_info(chg, "SMBMMI: cm %d, cs %d\n", chrg_cm_ma, chrg_cs_ma); + if (chrg_cm_ma >= TURBO_CHRG_THRSH) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_TURBO; + else if ((chrg_cm_ma > WEAK_CHRG_THRSH) && (chrg_cs_ma < WEAK_CHRG_THRSH)) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_WEAK; + else if (prev_chg_rate == POWER_SUPPLY_CHARGE_RATE_NONE) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_NORMAL; + + if (chg->pd_pps_support) { + charger_power = (chg->pd_curr_max / 1000) * (chg->pd_volt_max / 1000); + if (charger_power >= HYPER_CHRG_THRSH_UW) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_HYPER; + else if (charger_power >= TURBO_30W_CHRG_THRSH_UW) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_TURBO_30W; + } +end_rate_check: + if (prev_chg_rate != chg->charger_rate) { + mmi_chrg_info(chg, "%s Charger Detected\n", + charge_rate[chg->charger_rate]); + return true; + } else + return false; +} +#if 0 +#define CHG_SHOW_MAX_SIZE 50 +static ssize_t factory_image_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("Invalid factory image mode value = %lu\n", mode); + return -EINVAL; + } + + mmi_set_factory_image_mode(mode); + + return r ? r : count; +} + +static ssize_t factory_image_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + state = mmi_get_factory_image_mode() ? 1 : 0; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(factory_image_mode, 0644, + factory_image_mode_show, + factory_image_mode_store); + +static ssize_t factory_charge_upper_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + state = mmi_get_factory_charge_upper(); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(factory_charge_upper, 0444, + factory_charge_upper_show, + NULL); + +static ssize_t force_demo_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("Invalid demo mode value = %lu\n", mode); + return -EINVAL; + } + + mmi_set_demo_mode(mode); + + return r ? r : count; +} + +static ssize_t force_demo_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + + state = mmi_get_demo_mode(); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_demo_mode, 0644, + force_demo_mode_show, + force_demo_mode_store); + +static ssize_t force_max_chrg_temp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("Invalid max temp value = %lu\n", mode); + return -EINVAL; + } + + mmi_set_max_chrg_temp(mode); + + return r ? r : count; +} + +static ssize_t force_max_chrg_temp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + state = mmi_get_max_chrg_temp(); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_max_chrg_temp, 0644, + force_max_chrg_temp_show, + force_max_chrg_temp_store); +#endif +static bool mmi_factory_check(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool factory = false; + + if (np) + factory = of_property_read_bool(np, "mmi,factory-cable"); + + of_node_put(np); + + return factory; +} + + +static void kick_sm(struct mmi_charger_manager *chip, int ms) +{ + union power_supply_propval val; + int ret; + + if (!chip->sm_work_running) { + mmi_chrg_dbg(chip, PR_INTERRUPT, + "launch mmi chrg sm work\n"); + mmi_chrg_policy_clear(chip); + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(ms)); + chip->sm_work_running = true; + val.intval = true; + ret = power_supply_set_property(chip->usb_psy, + POWER_SUPPLY_PROP_CP_ENABLE, &val); + if (ret) + mmi_chrg_err(chip, "Unable to enable CP: %d\n", ret); + } else + mmi_chrg_dbg(chip, PR_INTERRUPT, + "mmi chrg sm work already existed\n"); +} + +static void cancel_sm(struct mmi_charger_manager *chip) +{ + union power_supply_propval val; + int ret; + + cancel_delayed_work_sync(&chip->mmi_chrg_sm_work); + flush_delayed_work(&chip->mmi_chrg_sm_work); + mmi_chrg_policy_clear(chip); + chip->sm_work_running = false; + val.intval = false; + ret = power_supply_set_property(chip->usb_psy, + POWER_SUPPLY_PROP_CP_ENABLE, &val); + if (ret) + mmi_chrg_err(chip, "Unable to disable CP: %d\n", ret); + chip->pd_volt_max = pd_volt_max_init; + chip->pd_curr_max = pd_curr_max_init; + mmi_chrg_dbg(chip, PR_INTERRUPT, + "cancel sync and flush mmi chrg sm work\n"); +} + +void clear_chg_manager(struct mmi_charger_manager *chip) +{ + mmi_chrg_dbg(chip, PR_INTERRUPT, "clear mmi chrg manager!\n"); + chip->pd_request_volt = 0; + chip->pd_request_curr = 0; + chip->pd_request_volt_prev = 0; + chip->pd_request_curr_prev = 0; + chip->pd_target_curr = 0; + chip->pd_target_volt = 0; + chip->pd_batt_therm_volt = 0; + chip->pd_batt_therm_curr = 0; + chip->pd_sys_therm_volt = 0; + chip->pd_sys_therm_curr = 0; + chip->recovery_pmic_chrg = false; + chip->sys_therm_cooling= false; + chip->sys_therm_force_pmic_chrg = false; + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + + memset(chip->mmi_pdo_info, 0, + sizeof(struct usbpd_pdo_info) * PD_MAX_PDO_NUM); +} + +static void mmi_awake_vote(struct mmi_charger_manager *chip, bool awake) +{ + if (awake == chip->awake) + return; + + chip->awake = awake; + if (awake) + pm_stay_awake(chip->dev); + else + pm_relax(chip->dev); +} + +static void mmi_batt_age_init(struct mmi_charger_manager *chip) +{ + int rc = 0; + union power_supply_propval pval; + if (!chip->batt_psy) { + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) + return; + } + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_STATUS, &pval); + if (rc < 0) { + mmi_chrg_err(chip, "Couldn't get batt status\n"); + return; + } + chip->pres_batt_status = pval.intval; + chip->prev_batt_status = chip->pres_batt_status; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &pval); + if (rc < 0) { + mmi_chrg_err(chip, "Couldn't get batt capacity\n"); + return; + } + + chip->soc_cycles_start= pval.intval; + chip->batt_cap_delta = 0; + return; +} + +static void mmi_cycle_counts(struct mmi_charger_manager *chip) +{ + int rc = 0; + int batt_design_capacity = 0, new_cycles = 0; + int soc_report = 0, soc_delta = 0, batt_delta_capacity = 0; + union power_supply_propval pval; + if (!chip->batt_psy) { + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) + return; + } + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, &pval); + if (rc < 0) { + mmi_chrg_err(chip, "Couldn't get batt design capacity\n"); + return; + } + batt_design_capacity = pval.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_STATUS, &pval); + if (rc < 0) { + mmi_chrg_err(chip, "Couldn't get batt status\n"); + return; + } + chip ->pres_batt_status = pval.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &pval); + if (rc < 0) { + mmi_chrg_err(chip, "Couldn't get batt capacity\n"); + return; + } + soc_report = pval.intval; + + mmi_chrg_dbg(chip, PR_MOTO, "cycle_count %d, pres %d, prev %d, soc_report %d, " + "soc_cycles_start %d, batt_cap_delta %d, " + "design_capcaity %d, batt_soc_delta %d\n", + chip->cycles, chip->pres_batt_status, + chip->prev_batt_status, + soc_report, chip->soc_cycles_start, + chip->batt_cap_delta, batt_design_capacity, + chip->batt_soc_delta); + + if (chip->prev_batt_status == POWER_SUPPLY_STATUS_CHARGING + && chip->pres_batt_status != POWER_SUPPLY_STATUS_CHARGING) { + chip->soc_cycles_start = soc_report; + } else if (chip->prev_batt_status != POWER_SUPPLY_STATUS_CHARGING + && chip->pres_batt_status == POWER_SUPPLY_STATUS_CHARGING) { + + soc_delta = chip->soc_cycles_start - soc_report; + if (soc_delta > 0) { + chip->batt_soc_delta += soc_delta; + + batt_delta_capacity = (chip->batt_soc_delta * batt_design_capacity) / 100 + + chip->batt_cap_delta; + + new_cycles = batt_delta_capacity / batt_design_capacity; + if (new_cycles > 0) { + chip->cycles += batt_delta_capacity / batt_design_capacity; + chip->batt_cap_delta = batt_delta_capacity % batt_design_capacity; + chip->batt_soc_delta = 0; + } + } + chip->soc_cycles_start = soc_report; + } + + chip->prev_batt_status = chip->pres_batt_status; +} + +static bool __mmi_ps_is_supplied_by(struct power_supply *supplier, + struct power_supply *supply) +{ + int i; + + if (!supply->supplied_from && !supplier->supplied_to) + return false; + + /* Support both supplied_to and supplied_from modes */ + if (supply->supplied_from) { + if (!supplier->desc->name) + return false; + for (i = 0; i < supply->num_supplies; i++) + if (!strcmp(supplier->desc->name, + supply->supplied_from[i])) + return true; + } else { + if (!supply->desc->name) + return false; + for (i = 0; i < supplier->num_supplicants; i++) + if (!strcmp(supplier->supplied_to[i], + supply->desc->name)) + return true; + } + + return false; +} + +static int __mmi_ps_changed(struct device *dev, void *data) +{ + struct power_supply *psy = data; + struct power_supply *pst = dev_get_drvdata(dev); + + if (__mmi_ps_is_supplied_by(psy, pst)) { + if (pst->desc->external_power_changed) + pst->desc->external_power_changed(pst); + } + + return 0; +} + +static void mmi_power_supply_changed(struct power_supply *psy, + char *envp_ext[]) +{ + dev_err(&psy->dev, "%s: %s\n", __func__, envp_ext[0]); + + class_for_each_device(power_supply_class, NULL, psy, + __mmi_ps_changed); + atomic_notifier_call_chain(&power_supply_notifier, + PSY_EVENT_PROP_CHANGED, psy); + kobject_uevent_env(&psy->dev.kobj, KOBJ_CHANGE, envp_ext); +} + +static enum alarmtimer_restart mmi_heartbeat_alarm_cb(struct alarm *alarm, + ktime_t now) +{ + struct mmi_charger_manager *chip = container_of(alarm, + struct mmi_charger_manager, + heartbeat_alarm); + + mmi_chrg_info(chip, "MMI: HB alarm fired\n"); + + __pm_stay_awake(&chip->mmi_hb_wake_source); + cancel_delayed_work(&chip->heartbeat_work); + /* Delay by 500 ms to allow devices to resume. */ + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(500)); + + return ALARMTIMER_NORESTART; +} + +#define HEARTBEAT_DELAY_MS 60000 +#define SMBCHG_HEARTBEAT_INTRVAL_NS 70000000000 +#define CHG_SHOW_MAX_SIZE 50 +static void mmi_heartbeat_work(struct work_struct *work) +{ + struct mmi_charger_manager *chip = container_of(work, + struct mmi_charger_manager, heartbeat_work.work); + int hb_resch_time = 0, ret = 0, i = 0; + char *chrg_rate_string = NULL; + char *envp[2]; + union power_supply_propval val; + bool pd_active; + + mmi_chrg_info(chip, "MMI: Heartbeat!\n"); + /* Have not been resumed so wait another 100 ms */ + if (chip->suspended) { + mmi_chrg_err(chip, "SMBMMI: HB running before Resume\n"); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(100)); + return; + } + + mmi_awake_vote(chip, true); + alarm_try_to_cancel(&chip->heartbeat_alarm); + + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) { + mmi_chrg_err(chip, + "Could not get USB power_supply, deferring probe\n"); + return; + } + } + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &val); + if (ret) { + mmi_chrg_err(chip, "Unable to read USB PRESENT: %d\n", ret); + return; + } + chip->vbus_present = val.intval; + + hb_resch_time = HEARTBEAT_DELAY_MS; + + if (chip->vbus_present) + alarm_start_relative(&chip->heartbeat_alarm, + ns_to_ktime(SMBCHG_HEARTBEAT_INTRVAL_NS)); + + if (!chip->vbus_present) + mmi_awake_vote(chip, false); + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PD_ACTIVE, &val); + if (ret) { + mmi_chrg_err(chip, "Unable to read PD ACTIVE: %d\n", ret); + goto schedule_work; + } + pd_active = val.intval; + + if (!chip->pd_handle) { + chip->pd_handle = devm_usbpd_get_by_phandle(chip->dev, + "qcom,usbpd-phandle"); + if (IS_ERR_OR_NULL(chip->pd_handle)) { + mmi_chrg_err(chip, "Error getting the pd phandle %ld\n", + PTR_ERR(chip->pd_handle)); + chip->pd_handle = NULL; + goto schedule_work; + } + } + + if (!chip->sm_work_running && chip->vbus_present + && pd_active) { + usbpd_get_pdo_info(chip->pd_handle, chip->mmi_pdo_info,PD_MAX_PDO_NUM); + mmi_chrg_info(chip, "check all effective pdo info\n"); + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + if ((chip->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_AUGMENTED) + && chip->mmi_pdo_info[i].uv_max >= PUMP_CHARGER_PPS_MIN_VOLT + && chip->mmi_pdo_info[i].ua >= chip->typec_middle_current) { + chip->mmi_pd_pdo_idx = chip->mmi_pdo_info[i].pdo_pos; + mmi_chrg_info(chip, + "pd charger support pps, pdo %d, " + "volt %d, curr %d \n", + chip->mmi_pd_pdo_idx, + chip->mmi_pdo_info[i].uv_max, + chip->mmi_pdo_info[i].ua); + chip->pd_pps_support = true; + + if (chip->mmi_pdo_info[i].uv_max < + chip->pd_volt_max) { + chip->pd_volt_max = + chip->mmi_pdo_info[i].uv_max; + } + if (chip->mmi_pdo_info[i].ua < + chip->pd_curr_max) { + chip->pd_curr_max = + chip->mmi_pdo_info[i].ua; + } + break; + } + } + + if (chip->pd_pps_support + && !chip->factory_mode) { + mmi_chrg_info(chip, "MMI: Heartbeat!, launch sm work\n"); + kick_sm(chip, 100); + } + } + + if (chip->use_batt_age) + mmi_cycle_counts(chip); + + if(mmi_chrg_rate_check(chip)) { + if (!chip->extrn_fg) { + val.intval = chip->charger_rate; + ret = power_supply_set_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_RATE, &val); + if (ret) + mmi_chrg_err(chip, "Unable to set charge rate: %d\n", ret); + } + + chrg_rate_string = kmalloc(CHG_SHOW_MAX_SIZE, GFP_KERNEL); + if (!chrg_rate_string) { + mmi_chrg_err(chip, "SMBMMI: Failed to Get Uevent Mem\n"); + envp[0] = NULL; + } else { + scnprintf(chrg_rate_string, CHG_SHOW_MAX_SIZE, + "POWER_SUPPLY_CHARGE_RATE=%s", + charge_rate[chip->charger_rate]); + envp[0] = chrg_rate_string; + envp[1] = NULL; + } + + if (chip->qcom_psy) { + mmi_power_supply_changed(chip->batt_psy, envp); + } + + kfree(chrg_rate_string); + } + + __pm_relax(&chip->mmi_hb_wake_source); + +schedule_work: + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(hb_resch_time)); +} + +static void psy_changed_work_func(struct work_struct *work) +{ + struct mmi_charger_manager *chip = container_of(work, + struct mmi_charger_manager, psy_changed_work); + union power_supply_propval val; + bool pd_active; + int ret, i; + + mmi_chrg_info(chip, "kick psy changed work.\n"); + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) { + mmi_chrg_err(chip, + "Could not get USB power_supply, deferring probe\n"); + return; + } + } + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &val); + if (ret) { + mmi_chrg_err(chip, "Unable to read USB PRESENT: %d\n", ret); + return; + } + chip->vbus_present = val.intval; + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PD_ACTIVE, &val); + if (ret) { + mmi_chrg_err(chip, "Unable to read PD ACTIVE: %d\n", ret); + return; + } + pd_active = val.intval; + + if (!chip->pd_handle) { + chip->pd_handle = devm_usbpd_get_by_phandle(chip->dev, + "qcom,usbpd-phandle"); + if (IS_ERR_OR_NULL(chip->pd_handle)) { + mmi_chrg_err(chip, "Error getting the pd phandle %ld\n", + PTR_ERR(chip->pd_handle)); + chip->pd_handle = NULL; + return; + } + } + + if (pd_active && chip->vbus_present) { + usbpd_get_pdo_info(chip->pd_handle, chip->mmi_pdo_info,PD_MAX_PDO_NUM); + mmi_chrg_info(chip, "check all effective pdo info\n"); + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + if ((chip->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_AUGMENTED) + && chip->mmi_pdo_info[i].uv_max >= PUMP_CHARGER_PPS_MIN_VOLT + && chip->mmi_pdo_info[i].ua >= chip->typec_middle_current) { + chip->mmi_pd_pdo_idx = chip->mmi_pdo_info[i].pdo_pos; + mmi_chrg_info(chip, + "pd charger support pps, pdo %d, " + "volt %d, curr %d \n", + chip->mmi_pd_pdo_idx, + chip->mmi_pdo_info[i].uv_max, + chip->mmi_pdo_info[i].ua); + chip->pd_pps_support = true; + + if (chip->mmi_pdo_info[i].uv_max < + chip->pd_volt_max) { + chip->pd_volt_max = + chip->mmi_pdo_info[i].uv_max; + } + + if (chip->mmi_pdo_info[i].ua < + chip->pd_curr_max) { + chip->pd_curr_max = + chip->mmi_pdo_info[i].ua; + } + + break; + } + } + } + + mmi_chrg_info(chip, "vbus present %d, pd pps support %d, " + "pps max voltage %d, pps max curr %d\n", + chip->vbus_present, + chip->pd_pps_support, + chip->pd_volt_max, + chip->pd_curr_max); + + if (chip->vbus_present + && chip->pd_pps_support + && !chip->factory_mode) { + kick_sm(chip, 100); + } else { + cancel_sm(chip); + clear_chg_manager(chip); + chip->pd_pps_support = false; + } + return; +} + +#define DEFAULT_BATT_OVP_LMT 4475000 +#define DEFAULT_PL_CHRG_VBATT_MIN 3600000 +#define DEFAULT_PPS_VOLT_STEPS 20000 +#define DEFAULT_PPS_CURR_STEPS 50000 +#define DEFAULT_PPS_VOLT_MAX 11000000 +#define DEFAULT_PPS_CURR_MAX 4000000 +#define MMI_TEMP_ZONES 5 +static int mmi_chrg_manager_parse_dt(struct mmi_charger_manager *chip) +{ + struct device_node *node = chip->dev->of_node, *child; + int rc, byte_len, i, j, chrg_idx = 0, step_cnt = 0, idx = 0; + const int *ptr; + + rc = of_property_read_u32(node, + "mmi,batt-ovp-lmt", + &chip->batt_ovp_lmt); + if (rc < 0) + chip->batt_ovp_lmt = + DEFAULT_BATT_OVP_LMT; + + rc = of_property_read_u32(node, + "mmi,pl-chrg-vbatt-min", + &chip->pl_chrg_vbatt_min); + if (rc < 0) + chip->pl_chrg_vbatt_min = + DEFAULT_PL_CHRG_VBATT_MIN; + + chip->use_batt_age = of_property_read_bool(node, + "mmi,use-batt-age"); + + chip->extrn_fg = of_property_read_bool(node, + "mmi,extrn-fg"); + + if (chip->extrn_fg) { + byte_len = of_property_count_strings(node, "mmi,extrn-fg-name"); + if (byte_len <= 0) { + mmi_chrg_err(chip, "Cannot parse mmi, extrn-fg: %d\n", byte_len); + return byte_len; + } + + for (i = 0; i < byte_len; i++) { + rc = of_property_read_string_index(node, "mmi,extrn-fg-name", + i, &chip->extrn_fg_name); + if (rc < 0) { + mmi_chrg_err(chip, "Cannot parse extrn-fg-name\n"); + return rc; + } + } + } + + chip->extrn_sense = of_property_read_bool(node, + "mmi,extrn-sense"); + + chip->dont_rerun_aicl= of_property_read_bool(node, + "mmi,dont-rerun-aicl"); + + rc = of_property_read_u32(node, + "mmi,typec-middle-current", + &chip->typec_middle_current); + if (rc < 0) + chip->typec_middle_current = + TYPEC_MIDDLE_CURRENT_UA; + + rc = of_property_read_u32(node, + "mmi,step-first-current-comp", + &chip->step_first_curr_comp); + if (rc < 0) + chip->step_first_curr_comp = + STEP_FIREST_CURR_COMP; + + rc = of_property_read_u32(node, + "mmi,pps-volt-steps", + &chip->pps_volt_steps); + if (rc < 0) + chip->pps_volt_steps = + DEFAULT_PPS_VOLT_STEPS; + + rc = of_property_read_u32(node, + "mmi,pps-curr-steps", + &chip->pps_curr_steps); + if (rc < 0) + chip->pps_curr_steps = + DEFAULT_PPS_CURR_STEPS; + + rc = of_property_read_u32(node, + "mmi,pd-volt-max", + &chip->pd_volt_max); + if (rc < 0) + chip->pd_volt_max = + DEFAULT_PPS_VOLT_MAX; + pd_volt_max_init = chip->pd_volt_max; + rc = of_property_read_u32(node, + "mmi,pd-curr-max", + &chip->pd_curr_max); + if (rc < 0) + chip->pd_curr_max = + DEFAULT_PPS_CURR_MAX; + pd_curr_max_init = chip->pd_curr_max; + + for_each_child_of_node(node, child) + chip->mmi_chrg_dev_num++; + + if (!chip->mmi_chrg_dev_num) { + mmi_chrg_err(chip,"No mmi_chrg_dev list !\n"); + return -ENODEV; + } + + chrg_name_list = (struct mmi_chrg_dts_info *)devm_kzalloc(chip->dev, + sizeof(struct mmi_chrg_dts_info) * + chip->mmi_chrg_dev_num, GFP_KERNEL); + if (!chrg_name_list) { + mmi_chrg_err(chip,"No memory for mmi charger device name list !\n"); + goto cleanup; + } + + chip->chrg_list = (struct mmi_charger_device **)devm_kzalloc(chip->dev, + sizeof(struct mmi_charger_device *) * + chip->mmi_chrg_dev_num, GFP_KERNEL); + if (!chip->chrg_list) { + mmi_chrg_err(chip,"No memory for mmi charger device list !\n"); + goto cleanup; + } + + for_each_child_of_node(node, child) { + byte_len = of_property_count_strings(child, "chrg-name"); + if (byte_len <= 0) { + mmi_chrg_err(chip, "Cannot parse chrg-name: %d\n", byte_len); + goto cleanup; + } + + for (i = 0; i < byte_len; i++) { + rc = of_property_read_string_index(child, "chrg-name", + i, &chrg_name_list[chrg_idx].chrg_name); + if (rc < 0) { + mmi_chrg_err(chip, "Cannot parse chrg-name\n"); + goto cleanup; + } + } + + byte_len = of_property_count_strings(child, "psy-name"); + if (byte_len <= 0) { + mmi_chrg_err(chip, "Cannot parse psy-name: %d\n", byte_len); + goto cleanup; + } + + for (i = 0; i < byte_len; i++) { + rc = of_property_read_string_index(child, "psy-name", + i, &chrg_name_list[chrg_idx].psy_name); + if (rc < 0) { + mmi_chrg_err(chip, "Cannot parse psy-name\n"); + goto cleanup; + } + } + + mmi_chrg_info(chip, "mmi,chrg-name: %s, psy-name: %s\n", + chrg_name_list[chrg_idx].chrg_name, + chrg_name_list[chrg_idx].psy_name); + + of_property_read_u32(child, "charging-curr-limited", + &chrg_name_list[chrg_idx].charging_curr_limited); + + of_property_read_u32(child, "charging-curr-min", + &chrg_name_list[chrg_idx].charging_curr_min); + + chrg_idx++; + } + + rc = of_property_read_u32(node, + "mmi,chrg-temp-zones-num", + &chip->num_temp_zones); + if (rc < 0) + chip->num_temp_zones = + MMI_TEMP_ZONES; + + chip->temp_zones = (struct mmi_chrg_temp_zone *) + devm_kzalloc(chip->dev, + sizeof(struct mmi_chrg_temp_zone ) + * chip->num_temp_zones, + GFP_KERNEL); + if (chip->temp_zones == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + if (of_find_property(node, "mmi,mmi-chrg-temp-zones", &byte_len)) { + if (byte_len <= 0) { + mmi_chrg_err(chip, + "Cannot parse mmi-chrg-temp-zones %d\n",byte_len); + goto cleanup; + } + + step_cnt = (byte_len / chip->num_temp_zones - sizeof(u32)) + / (sizeof(u32) * 2); + mmi_chrg_info(chip, "mmi chrg step number is %d\n", step_cnt); + + dt_temp_zones = (u32 *) devm_kzalloc(chip->dev, byte_len, GFP_KERNEL); + if (dt_temp_zones == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + rc = of_property_read_u32_array(node, + "mmi,mmi-chrg-temp-zones", + (u32 *)dt_temp_zones, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_chrg_err(chip, + "Couldn't read mmi chrg temp zones rc = %d\n", rc); + goto zones_failed; + } + + mmi_chrg_err(chip, "temp zone nums: %d , byte_len %d, " + "num %d, row size %d\n", + chip->num_temp_zones, byte_len, + (int)(byte_len / sizeof(u32)), + (int)(sizeof(struct mmi_chrg_step_power) * step_cnt)); + ptr = dt_temp_zones; + + for (i = 0; i < byte_len / sizeof(u32); i++) { + if (!(i % 9)) { + printk("\n"); + } + printk("%u,", ptr[i]); + } + printk("\n"); + + for (i = 0; i < chip->num_temp_zones; i++) { + idx = (int)((sizeof(u32) + + sizeof(struct mmi_chrg_step_power) * step_cnt ) + * i / sizeof(u32)); + chip->temp_zones[i].temp_c = dt_temp_zones[idx]; + } + + for (i = 0; i < chip->num_temp_zones; i++) { + idx = (int)(((sizeof(u32) + + sizeof(struct mmi_chrg_step_power) * step_cnt ) + * i + sizeof(u32)) / sizeof(u32)); + chip->temp_zones[i].chrg_step_power = + (struct mmi_chrg_step_power *)&dt_temp_zones[idx]; + } + + for (i = 0; i < chip->num_temp_zones; i++) { + + printk( "mmi temp zones: Zone: %d, Temp: %d C " , i, + chip->temp_zones[i].temp_c); + for (j = 0; j < step_cnt; j++) { + chip->temp_zones[i].chrg_step_power[j].chrg_step_curr *= 1000; + chip->temp_zones[i].chrg_step_power[j].chrg_step_volt *= 1000; + + printk("step_volt %dmV, step_curr %dmA, ", + chip->temp_zones[i].chrg_step_power[j].chrg_step_volt, + chip->temp_zones[i].chrg_step_power[j].chrg_step_curr); + } + printk("\n"); + } + + chip->pres_temp_zone = ZONE_NONE; + chip->chrg_step_nums = step_cnt; + } + + if (of_find_property(node, "mmi,thermal-mitigation", &byte_len)) { + chip->thermal_mitigation = devm_kzalloc(chip->dev, byte_len, + GFP_KERNEL); + + if (chip->thermal_mitigation == NULL) { + rc = -ENOMEM; + goto zones_failed; + } + chip->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "mmi,thermal-mitigation", + chip->thermal_mitigation, + chip->thermal_levels); + if (rc < 0) { + mmi_chrg_err(chip, + "Couldn't parse thermal-mitigation rc = %d\n", rc); + goto thermal_failed; + } + } + + chip->afvc_volt_comp = 0; + of_property_read_u32(node, + "mmi,afvc_enable_compensation_uv", + &chip->afvc_volt_comp); + return rc; +thermal_failed: + devm_kfree(chip->dev,chip->thermal_mitigation); +zones_failed: + devm_kfree(chip->dev,chip->temp_zones); +cleanup: + chip->mmi_chrg_dev_num = 0; + devm_kfree(chip->dev,chip->chrg_list); + return rc; +} + +static int mmi_chrg_manager_probe(struct platform_device *pdev) +{ + int ret = 0; + struct mmi_charger_manager *chip; + + if (!pdev) + return -ENODEV; + + chip = devm_kzalloc(&pdev->dev, sizeof(struct mmi_charger_manager), + GFP_KERNEL); + if (!chip) { + dev_err(&pdev->dev, + "Unable to alloc memory for mmi_charger_manager\n"); + return -ENOMEM; + } + + chip->dev = &pdev->dev; + chip->name = "mmi_chrg_manager"; + chip->debug_mask = &__debug_mask; + chip->suspended = false; + + ret = mmi_charger_class_init(); + if (ret < 0) { + dev_err(&pdev->dev, + "mmi charger class init failed\n"); + goto cleanup; + } + + ret = mmi_chrg_manager_parse_dt(chip); + if (ret < 0) { + dev_err(&pdev->dev, + "parse dt failed\n"); + goto cleanup; + } + + chip->factory_mode = mmi_factory_check(); + + chip->qcom_psy = power_supply_get_by_name("qcom_battery"); +#if 0 + if (chip->qcom_psy && chip->extrn_fg) { + + chip->extrn_psy = power_supply_get_by_name(chip->extrn_fg_name); + if (!chip->extrn_psy) { + mmi_chrg_err(chip, "Get extrn psy failed\n"); + goto cleanup; + } + ret = batt_psy_register(chip); + if (ret) { + mmi_chrg_err(chip, "Batt psy register failed\n"); + goto cleanup; + } + + ret = device_create_file(chip->dev, + &dev_attr_factory_image_mode); + if (ret) { + mmi_chrg_err(chip, "couldn't create factory_image_mode\n"); + } + + ret = device_create_file(chip->dev, + &dev_attr_force_demo_mode); + if (ret) { + mmi_chrg_err(chip, "couldn't create force_demo_mode\n"); + } + + ret = device_create_file(chip->dev, + &dev_attr_force_max_chrg_temp); + if (ret) { + mmi_chrg_err(chip, "couldn't create force_max_chrg_temp\n"); + } + + ret = device_create_file(chip->dev, + &dev_attr_factory_charge_upper); + if (ret) + mmi_chrg_err(chip, "couldn't create factory_charge_upper\n"); + + } else +#endif + chip->batt_psy = power_supply_get_by_name("battery"); + + if (!chip->batt_psy) { + mmi_chrg_err(chip, "Could not get battery power_supply\n"); + goto cleanup; + } + + + ret = mmi_chrg_policy_init(chip, chrg_name_list, + chip->mmi_chrg_dev_num); + if (ret < 0) { + dev_err(&pdev->dev, + "mmi chrg policy init failed\n"); + goto cleanup; + } + + chip->pd_handle = + devm_usbpd_get_by_phandle(chip->dev, "qcom,usbpd-phandle"); + if (IS_ERR_OR_NULL(chip->pd_handle)) { + dev_err(&pdev->dev, "Error getting the pd phandle %ld\n", + PTR_ERR(chip->pd_handle)); + chip->pd_handle = NULL; + } + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) + mmi_chrg_err(chip, "Could not get USB power_supply\n"); + } + + INIT_WORK(&chip->psy_changed_work, psy_changed_work_func); + INIT_DELAYED_WORK(&chip->heartbeat_work, mmi_heartbeat_work); + wakeup_source_init_internal(&chip->mmi_hb_wake_source, + "mmi_hb_wake"); + alarm_init(&chip->heartbeat_alarm, ALARM_BOOTTIME, + mmi_heartbeat_alarm_cb); + + + ret = mmi_chrg_mgr_psy_register(chip); + if (ret) + goto cleanup; + + chip->psy_nb.notifier_call = psy_changed; + ret = power_supply_reg_notifier(&chip->psy_nb); + if (ret) + goto cleanup; + + mmi_batt_age_init(chip); + + init_completion(&chip->sm_completion); + platform_set_drvdata(pdev, chip); + + //create_sysfs_entries(chip); + schedule_work(&chip->psy_changed_work); + mmi_chrg_info(chip, "mmi chrg manager initialized successfully, ret %d\n", ret); + return 0; +cleanup: + mmi_charger_class_exit(); + devm_kfree(&pdev->dev, chip); + return ret; +} + +static int mmi_chrg_manager_remove(struct platform_device *pdev) +{ + struct mmi_charger_manager *chip = platform_get_drvdata(pdev); + + //remove_sysfs_entries(chip); + //cancel_delayed_work_sync(&chip->mmi_chrg_sm_work); + power_supply_unreg_notifier(&chip->psy_nb); + power_supply_unregister(chip->mmi_chrg_mgr_psy ); +// wakeup_source_trash_internal(&chip->mmi_hb_wake_source); + platform_set_drvdata(pdev, NULL); + devm_kfree(&pdev->dev, chip); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mmi_chrg_suspend(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct mmi_charger_manager *chip = platform_get_drvdata(pdev); + + chip->suspended = true; + + return 0; +} + +static int mmi_chrg_resume(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct mmi_charger_manager *chip = platform_get_drvdata(pdev); + + chip->suspended = false; + + return 0; +} +#else +#define smb_mmi_suspend NULL +#define smb_mmi_resume NULL +#endif + +static const struct dev_pm_ops mmi_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mmi_chrg_suspend, mmi_chrg_resume) +}; + +static const struct of_device_id mmi_chrg_manager_match_table[] = { + {.compatible = "mmi,chrg-manager"}, + {}, +}; +MODULE_DEVICE_TABLE(of, mmi_chrg_manager_match_table); + +static struct platform_driver mmi_chrg_manager_driver = { + .probe = mmi_chrg_manager_probe, + .remove = mmi_chrg_manager_remove, + .driver = { + .name = "mmi_chrg_manager", + .owner = THIS_MODULE, + .pm = &mmi_dev_pm_ops, + .of_match_table = mmi_chrg_manager_match_table, + }, +}; + +static int __init mmi_chrg_manager_init(void) +{ + int ret; + ret = platform_driver_register(&mmi_chrg_manager_driver); + if (ret) { + pr_err("mmi_chrg_manager failed to register driver\n"); + return ret; + } + return 0; +} + +static void __exit mmi_chrg_manager_exit(void) +{ + platform_driver_unregister(&mmi_chrg_manager_driver); +} + +module_init(mmi_chrg_manager_init); +module_exit(mmi_chrg_manager_exit); + +MODULE_ALIAS("platform:mmi parallel charger"); +MODULE_AUTHOR("Motorola Mobility LLC"); +MODULE_DESCRIPTION("Motorola Mobility parallel charger"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/mmi_parallel_charger/mmi_charger_core.h b/drivers/power/mmi_parallel_charger/mmi_charger_core.h new file mode 100644 index 000000000000..d9cd8544fa62 --- /dev/null +++ b/drivers/power/mmi_parallel_charger/mmi_charger_core.h @@ -0,0 +1,287 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + +#ifndef __MMI_CHRG_CORE_H_ +#define __MMI_CHRG_CORE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include + +#define mmi_chrg_err(chip, fmt, ...) \ + pr_err("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__) \ + +#define mmi_chrg_info(chip, fmt, ...) \ + pr_info("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__) \ + +#define mmi_chrg_dbg(chip, reason, fmt, ...) \ + do { \ + if (*chip->debug_mask & (reason)) \ + pr_info("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__); \ + else \ + pr_debug("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__); \ + } while (0) + +enum print_reason { + PR_INTERRUPT = BIT(0), + PR_MISC = BIT(2), + PR_MOTO = BIT(7), +}; + +enum { + POWER_SUPPLY_CHARGE_RATE_NONE = 0, + POWER_SUPPLY_CHARGE_RATE_NORMAL, + POWER_SUPPLY_CHARGE_RATE_WEAK, + POWER_SUPPLY_CHARGE_RATE_TURBO, + POWER_SUPPLY_CHARGE_RATE_TURBO_30W, + POWER_SUPPLY_CHARGE_RATE_HYPER, +}; + +static inline void wakeup_source_init_internal(struct wakeup_source *ws, + const char *name) +{ + if(ws) { + memset(ws, 0, sizeof(*ws)); + ws->name = name; + } + + wakeup_source_add(ws); +} +/* +static inline void wakeup_source_trash_internal(struct wakeup_source *ws) +{ + + wakeup_source_remove(ws); + if(!ws) + return; + __pm_relex(ws); +}*/ + +#define MAX_NUM_STEPS 10 +enum mmi_chrg_temp_zones { + ZONE_FIRST = 0, + /* states 0-9 are reserved for zones */ + ZONE_LAST = MAX_NUM_STEPS + ZONE_FIRST - 1, + ZONE_HOT, + ZONE_COLD, + ZONE_NONE = 0xFF, +}; + +enum mmi_chrg_steps { + STEP_FIRST = 0, + STEP_LAST = MAX_NUM_STEPS + STEP_FIRST - 1, + STEP_NONE = 0xFF, +}; + +enum mmi_pd_pps_result { + NO_ERROR, + BLANCE_POWER, + RESET_POWER, +}; + +struct mmi_chrg_step_power { + u32 chrg_step_volt; + u32 chrg_step_curr; +}; + +struct mmi_chrg_temp_zone { + int temp_c; /*temperature*/ + struct mmi_chrg_step_power *chrg_step_power; +}; + +struct mmi_chrg_step_info { + enum mmi_chrg_steps pres_chrg_step; + int temp_c; + int chrg_step_cc_curr; + int chrg_step_cv_volt; + int chrg_step_cv_tapper_curr; + bool last_step; +}; + +struct mmi_chrg_dev_ops { + const char *dev_name; + struct mmi_charger_ops *ops; +}; + +struct mmi_chrg_dts_info { + const char *chrg_name; + const char *psy_name; + int charging_curr_limited; + int charging_curr_min; +}; + +#define PPS_RET_HISTORY_SIZE 8 +#define PD_SRC_PDO_TYPE_FIXED 0 +#define PD_SRC_PDO_TYPE_BATTERY 1 +#define PD_SRC_PDO_TYPE_VARIABLE 2 +#define PD_SRC_PDO_TYPE_AUGMENTED 3 +#define STEP_FIREST_CURR_COMP 300000 +#define TYPEC_HIGH_CURRENT_UA 3000000 +#define TYPEC_MIDDLE_CURRENT_UA 2000000 +#define SWITCH_CHARGER_PPS_VOLT 5000000 +#define PUMP_CHARGER_PPS_MIN_VOLT 8000000 +#define COOLING_HYSTERISIS_DEGC 2 +struct mmi_charger_manager { + const char *name; + struct device *dev; + struct power_supply *batt_psy; + struct power_supply *qcom_psy; + struct power_supply *extrn_psy; + struct power_supply *usb_psy; + struct power_supply *mmi_chrg_mgr_psy; + struct usbpd *pd_handle; + struct usbpd_pdo_info mmi_pdo_info[PD_MAX_PDO_NUM]; + struct notifier_block psy_nb; + + struct wakeup_source mmi_hb_wake_source; + struct alarm heartbeat_alarm; + bool factory_mode; + bool suspended; + bool awake; + bool cp_disable; + + int *debug_mask; + int mmi_pd_pdo_idx; /*request the pdo idx of PD*/ + int pps_volt_steps; /*PPS voltage, programming step size*/ + int pps_curr_steps; /*pps current, programming step size*/ + + int pd_volt_max; /*the Maximum request PD Voltage*/ + int pd_curr_max; /*the Maximum request PD current*/ + int batt_ovp_lmt; /*the battery over current limitation*/ + int pl_chrg_vbatt_min; /*the minimum battery voltage to enable parallel charging*/ + + int typec_middle_current; + int step_first_curr_comp; + int pps_volt_comp; + int pd_request_volt; + int pd_request_curr; + /*the request PD power*/ + int pd_request_volt_prev; + int pd_request_curr_prev; + /*the previous request PD power*/ + int pd_sys_therm_volt; + int pd_sys_therm_curr; + /*the thermal PD power*/ + + int pd_batt_therm_volt; + int pd_batt_therm_curr; + + int pd_target_volt; + int pd_target_curr; + /*the final commited request PD power*/ + + int pps_result; + int pps_result_history[PPS_RET_HISTORY_SIZE]; + int pps_result_history_idx; + /*save the result of the return from PD request*/ + + int charger_rate; + bool vbus_present; + bool pd_pps_support; + bool pd_pps_balance; + bool freeze_pd_power; + bool extrn_fg; + const char *extrn_fg_name; + bool extrn_sense; + bool recovery_pmic_chrg; + bool dont_rerun_aicl; + + bool sys_therm_cooling; + bool sys_therm_force_pmic_chrg; + bool batt_therm_cooling; + int batt_therm_cooling_cnt; + + bool use_batt_age; + int cycles; + int batt_cap_delta; + int batt_soc_delta; + int soc_cycles_start; + int pres_batt_status; + int prev_batt_status; + + struct delayed_work mmi_chrg_sm_work; /*mmi charger state machine work*/ + struct delayed_work heartbeat_work; /*cycle trig heartbeat work*/ + struct completion sm_completion; + struct work_struct psy_changed_work; /*the change notification of power supply*/ + bool sm_work_running; + int num_temp_zones; + struct mmi_chrg_temp_zone *temp_zones; /*the temperature zone of charging*/ + enum mmi_chrg_temp_zones pres_temp_zone; /*the present zone idx*/ + + int *thermal_mitigation; /*thermal mitigation array*/ + int thermal_levels; + int system_thermal_level; /*thermal level setting*/ + + int chrg_step_nums; + struct mmi_chrg_step_info chrg_step; /*step charger info*/ + + int mmi_chrg_dev_num; + struct mmi_charger_device **chrg_list; /*charger device list*/ + int afvc_volt_comp; /*afvc enabled ,volt compensation uv*/ +}; + +extern bool mmi_get_pps_result_history(struct mmi_charger_manager *chip); +extern void mmi_set_pps_result_history(struct mmi_charger_manager *chip, int pps_result); +extern void mmi_clear_pps_result_history(struct mmi_charger_manager *chip); +extern int mmi_calculate_delta_volt(int pps_voltage, int pps_current, int delta_curr); +extern bool mmi_find_chrg_step(struct mmi_charger_manager *chip, int temp_zone, int vbatt_volt); +extern bool mmi_find_temp_zone(struct mmi_charger_manager *chip, int temp_c, bool ignore_hysteresis_degc); +extern void clear_chg_manager(struct mmi_charger_manager *chip); +extern void mmi_update_all_charger_status(struct mmi_charger_manager *chip); +extern void mmi_update_all_charger_error(struct mmi_charger_manager *chip); +extern void mmi_dump_charger_error(struct mmi_charger_manager *chip, + struct mmi_charger_device *chrg_dev); +extern ssize_t mmi_get_factory_image_mode(void); +extern ssize_t mmi_set_factory_image_mode(int mode); +extern ssize_t mmi_get_factory_charge_upper(void); +extern ssize_t mmi_get_demo_mode(void); +extern ssize_t mmi_set_demo_mode(int mode); +extern ssize_t mmi_get_max_chrg_temp(void); +extern ssize_t mmi_set_max_chrg_temp(int value); +#endif diff --git a/drivers/power/mmi_parallel_charger/mmi_charger_policy.h b/drivers/power/mmi_parallel_charger/mmi_charger_policy.h new file mode 100644 index 000000000000..e3dfc285caa8 --- /dev/null +++ b/drivers/power/mmi_parallel_charger/mmi_charger_policy.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + #ifndef __MMI_CHRG_POLICY_H_ + #define __MMI_CHRG_POLICY_H_ +extern int mmi_chrg_policy_init(struct mmi_charger_manager *chip, + struct mmi_chrg_dts_info *chrg_dts, + int chrg_cnt); +extern void mmi_chrg_policy_clear(struct mmi_charger_manager *chip); +extern void mmi_chrg_enable_all_cp(struct mmi_charger_manager *chip, int val); +#endif diff --git a/drivers/power/mmi_parallel_charger/mmi_charger_pump_policy.c b/drivers/power/mmi_parallel_charger/mmi_charger_pump_policy.c new file mode 100644 index 000000000000..932e1e33328b --- /dev/null +++ b/drivers/power/mmi_parallel_charger/mmi_charger_pump_policy.c @@ -0,0 +1,1670 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_core.h" + +extern struct mmi_charger_ops bq2597x_charger_ops; +extern struct mmi_charger_ops qpnp_pmic_charger_ops; + +static const struct mmi_chrg_dev_ops dev_ops[] = { + { + .dev_name = "pmic-sw", + .ops = &qpnp_pmic_charger_ops, + }, + { + .dev_name = "cp-master", + .ops = &bq2597x_charger_ops, + }, + { + .dev_name = "cp-slave", + .ops = &bq2597x_charger_ops, + }, +}; + +enum mmi_chrg_dev { + PMIC_SW = 0, + CP_MASTER, + CP_SLAVE, + CHRG_NUM, +}; + +struct mmi_cp_policy_dev { + bool pmic_sw; + bool cp_master; + bool cp_slave; + bool cp_clave_later; + struct mmi_charger_device *chrg_dev[CHRG_NUM]; +}; + +typedef enum { + PM_STATE_DISCONNECT, + PM_STATE_ENTRY, + PM_STATE_SW_ENTRY, + PM_STATE_SW_LOOP, + PM_STATE_CHRG_PUMP_ENTRY, + PM_STATE_SINGLE_CP_ENTRY, + PM_STATE_DULE_CP_ENTRY, + PM_STATE_PPS_TUNNING_CURR, + PM_STATE_PPS_TUNNING_VOLT, + PM_STATE_CP_CC_LOOP, + PM_STATE_CP_CV_LOOP, + PM_STATE_CP_QUIT, + PM_STATE_RECOVERY_SW, + PM_STATE_STOP_CHARGE, + PM_STATE_COOLING_LOOP, +} pm_sm_state_t; + +const unsigned char *pm_state_str[] = { + "PM_STATE_DISCONNECT", + "PM_STATE_ENTRY", + "PM_STATE_SW_ENTRY", + "PM_STATE_SW_LOOP", + "PM_STATE_CHRG_PUMP_ENTRY", + "PM_STATE_SINGLE_CP_ENTRY", + "PM_STATE_DULE_CP_ENTRY", + "PM_STATE_PPS_TUNNING_CURR", + "PM_STATE_PPS_TUNNING_VOLT", + "PM_STATE_CP_CC_LOOP", + "PM_STATE_CP_CV_LOOP", + "PM_STATE_CP_QUIT", + "PM_STATE_RECOVERY_SW", + "PM_STATE_STOP_CHARGE", + "PM_STATE_COOLING_LOOP", +}; + +static pm_sm_state_t sm_state = PM_STATE_DISCONNECT; +static int chrg_cc_power_tunning_cnt = 0; +static int chrg_cv_taper_tunning_cnt = 0; +static int chrg_cv_delta_volt = 0; +static int quit_slave_chrg_cnt = 0; +static int batt_curr_roof = 0; +static int pd_constant_power_cnt = 0; +static struct mmi_cp_policy_dev g_chrg_list = {0}; + +static void mmi_chrg_sm_move_state(struct mmi_charger_manager *chip, pm_sm_state_t state) +{ + mmi_chrg_dbg(chip, PR_INTERRUPT, "pm_state change:%s -> %s\n", + pm_state_str[sm_state], pm_state_str[state]); + sm_state = state; + pd_constant_power_cnt = 0; + batt_curr_roof = 0; +} + +static void chrg_dev_init(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num, i; + chrg_num = chip->mmi_chrg_dev_num; + mmi_chrg_err(chip,"runing in chrg dev init!\n"); + + for (i = 0; i < chrg_num; i++) { + + switch (i) { + case PMIC_SW: + if (is_charger_exist(dev_ops[PMIC_SW].dev_name)) { + chrg_list->pmic_sw = true; + chrg_list->chrg_dev[PMIC_SW] = chip->chrg_list[PMIC_SW]; + } + break; + case CP_MASTER: + if (is_charger_exist(dev_ops[CP_MASTER].dev_name)) { + chrg_list->cp_master = true; + chrg_list->chrg_dev[CP_MASTER] = chip->chrg_list[CP_MASTER]; + } + break; + case CP_SLAVE: + if (is_charger_exist(dev_ops[CP_SLAVE].dev_name)) { + chrg_list->cp_slave = true; + chrg_list->cp_clave_later = false; + chrg_list->chrg_dev[CP_SLAVE] = chip->chrg_list[CP_SLAVE]; + } + break; + default: + mmi_chrg_err(chip,"No mmi_chrg_dev found %d !\n",i); + break; + } + } + return; +} + +static void clear_chrg_dev_error_cnt(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num, i; + struct mmi_charger_device *chrg_dev; + chrg_num = chip->mmi_chrg_dev_num; + + for (i = 0; i < chrg_num; i++) { + if (is_charger_exist(dev_ops[i].dev_name)) { + chrg_dev = chrg_list->chrg_dev[i]; + chrg_dev->charger_error.chrg_err_type = 0; + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + chrg_dev->charger_error.bus_ocp_err_cnt = 0; + } + } + return; +} + +#define PPS_INIT_VOLT_COMP 500000 +#define PPS_VOLT_COMP_DELTA 300000 +static void chrg_policy_error_recovery(struct mmi_charger_manager *chip, + struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num = 0, i = 0, chrg_error_type= 0; + struct mmi_charger_device *chrg_dev; + chrg_num = chip->mmi_chrg_dev_num; + + for (i = 0; i < chrg_num; i++) { + + switch (i) { + case PMIC_SW: + break; + case CP_MASTER: + if (is_charger_exist(dev_ops[CP_MASTER].dev_name)) { + chrg_dev = chrg_list->chrg_dev[CP_MASTER]; + chrg_error_type = chrg_dev->charger_error.chrg_err_type; + if (chrg_error_type & (1 << MMI_BUS_UCP_ALARM_BIT) || + chrg_error_type & (1 << MMI_BUS_UCP_FAULT_BIT)) { + mmi_chrg_info(chip,"CP master bus ucp error %d, cnt %d," + "pps volt comp %dmV\n", + chrg_error_type, + chrg_dev->charger_error.bus_ucp_err_cnt, + chip->pps_volt_comp); + + if (chrg_dev->charger_error.bus_ucp_err_cnt > 3) { + if (chrg_list->cp_slave) { + chrg_list->cp_slave = false; + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + mmi_chrg_sm_move_state(chip, + PM_STATE_CHRG_PUMP_ENTRY); + } else { + chip->recovery_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, + PM_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + } + } else if (chrg_dev->charger_error.bus_ucp_err_cnt > 6) { + chip->recovery_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, + PM_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + } + + chrg_dev->charger_error.bus_ucp_err_cnt++; + chip->pps_volt_comp += PPS_VOLT_COMP_DELTA; + mmi_chrg_info(chip,"Restart charging, " + "increase pps volt comp %dmV\n", + chip->pps_volt_comp); + mmi_chrg_sm_move_state(chip, + PM_STATE_CHRG_PUMP_ENTRY); + mmi_clear_charger_error(chrg_dev); + }else if (chrg_error_type & (1 << MMI_BUS_OCP_ALARM_BIT) || + chrg_error_type & (1 << MMI_BUS_OCP_FAULT_BIT) || + chrg_error_type & (1 << MMI_CONV_OCP_FAULT_BIT)) { + mmi_chrg_info(chip,"CP master ocp error %d, cnt %d," + "pps volt comp %dmV\n", + chrg_error_type, + chrg_dev->charger_error.bus_ocp_err_cnt, + chip->pps_volt_comp); + if (chrg_dev->charger_error.bus_ocp_err_cnt > 3) { + chip->recovery_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, + PM_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ocp_err_cnt = 0; + } + chrg_dev->charger_error.bus_ocp_err_cnt++; + chip->pps_volt_comp -= PPS_VOLT_COMP_DELTA; + if (chip->pps_volt_comp < 0) + chip->pps_volt_comp = 0; + mmi_chrg_info(chip,"Restart charging, " + "decrease pps volt comp %dmV\n", + chip->pps_volt_comp); + mmi_chrg_sm_move_state(chip, + PM_STATE_CHRG_PUMP_ENTRY); + mmi_clear_charger_error(chrg_dev); + } + } + break; + case CP_SLAVE: + break; + default: + mmi_chrg_err(chip,"No mmi_chrg_dev found %d !\n",i); + break; + } + } + return; +} + +static void chrg_policy_error_clear(struct mmi_charger_manager *chip, + struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num = 0, i = 0; + struct mmi_charger_device *chrg_dev; + chrg_num = chip->mmi_chrg_dev_num; + + for (i = 0; i < chrg_num; i++) { + switch (i) { + case PMIC_SW: + break; + + case CP_MASTER: + if (is_charger_exist(dev_ops[CP_MASTER].dev_name)) { + chrg_dev = chrg_list->chrg_dev[CP_MASTER]; + mmi_clear_charger_error(chrg_dev); + } + break; + + case CP_SLAVE: + break; + + default: + mmi_chrg_err(chip,"mmi_chrg_dev not found %d !\n",i); + break; + } + } + + return; +} + +void mmi_chrg_enable_all_cp(struct mmi_charger_manager *chip, int val) +{ + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + bool enable = !!val; + + mmi_chrg_dbg(chip, PR_MOTO,"enable all cp = %d\n", enable); + if (enable) { + if (chip->cp_disable == false) + return; + + cancel_delayed_work_sync(&chip->mmi_chrg_sm_work); + mmi_chrg_sm_move_state(chip, PM_STATE_CHRG_PUMP_ENTRY); + chip->cp_disable = false; + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(0)); + + } else { + if(chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) + return; + + cancel_delayed_work_sync(&chip->mmi_chrg_sm_work); + + chip->cp_disable = true; + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + mmi_chrg_sm_move_state(chip, PM_STATE_RECOVERY_SW); + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(0)); + } +} + +#define HEARTBEAT_SHORT_DELAY_MS 1000 +#define HEARTBEAT_lOOP_WAIT_MS 3000 +#define HEARTBEAT_PPS_TUNNING_MS 100 +#define HEARTBEAT_NEXT_STATE_MS 100 +#define HEARTBEAT_CANCEL -1 +#define CC_CURR_DEBOUNCE 100000 +#define CV_TAPPER_COUNT 3 +#define CC_POWER_COUNT 3 +#define CV_DELTA_VOLT 100000 +#define THERMAL_TUNNING_CURR 100000 +#define COOLING_DELTA_POWER 100000 +#define COOLING_MAX_CNT 5 +#define PPS_SELECT_PDO_RETRY_COUNT 3 +#define DISABLE_CHRG_LIMIT -1 +#define CP_CHRG_SOC_LIMIT 90 +#define PD_CONT_PWR_CNT 5 + +void mmi_chrg_policy_clear(struct mmi_charger_manager *chip) { + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + chrg_dev_init(chip, &g_chrg_list); + clear_chrg_dev_error_cnt(chip, &g_chrg_list);; + clear_chg_manager(chip); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + sm_state = PM_STATE_DISCONNECT; + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + quit_slave_chrg_cnt = 0; + chrg_cc_power_tunning_cnt = 0; + chrg_cv_taper_tunning_cnt = 0; + chrg_cv_delta_volt = 0; + pd_constant_power_cnt = 0; + batt_curr_roof = 0; + return; +} + +static void mmi_chrg_sm_work_func(struct work_struct *work) +{ + struct mmi_charger_manager *chip = container_of(work, + struct mmi_charger_manager, mmi_chrg_sm_work.work); + int i = 0, rc = 0; + int ibatt_curr = 0, vbatt_volt = 0, batt_temp = 0, vbus_pres = 0; + int batt_soc = 0; + int heartbeat_dely_ms = 0; + int cooling_curr = 0; + int cooling_volt = 0; + int pmic_sys_therm_level = 0; + bool zone_change = false; + bool ignore_hysteresis_degc = false; + struct mmi_chrg_step_info *chrg_step; + union power_supply_propval prop = {0,}; + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + + mmi_chrg_dbg(chip, PR_MOTO, "\n\n\n"); + + mmi_chrg_dbg(chip, PR_MOTO, "schedule SM work, sm state %s \n", + pm_state_str[sm_state]); + + mmi_chrg_dbg(chip, PR_MOTO, "pmic-sw is exist: %d " + "cp-master is exist: %d " + "cp-slave is exist: %d\n", + chrg_list->pmic_sw, + chrg_list->cp_master, + chrg_list->cp_slave); + if (!chrg_list->pmic_sw) { + mmi_chrg_err(chip,"PMIC-SW isn't exist, force quite mmi chrg sm work !\n"); + return; + } + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, &prop); + if (!rc) + pmic_sys_therm_level = prop.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + vbatt_volt = prop.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + ibatt_curr = prop.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_TEMP, &prop); + if (!rc) + batt_temp = prop.intval / 10; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &prop); + if (!rc) + batt_soc = prop.intval; + + + if (ibatt_curr < 0) + ibatt_curr *= -1; + + + mmi_update_all_charger_status(chip); + mmi_update_all_charger_error(chip); + + if (chip->extrn_sense) { + ibatt_curr = chrg_list->chrg_dev[CP_MASTER]->charger_data.ibatt_curr; + ibatt_curr *= 1000; + if (ibatt_curr < 0) + ibatt_curr *= -1; + vbatt_volt = chrg_list->chrg_dev[CP_MASTER]->charger_data.vbatt_volt; + vbatt_volt *= 1000; + } + + vbus_pres = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbus_pres; + if (!vbus_pres) { + for (i = 0; i < 3; i++) { + mmi_update_all_charger_status(chip); + vbus_pres = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbus_pres; + mmi_chrg_info(chip, "Retry check charger status, vbus %d\n", vbus_pres); + if (vbus_pres) { + mmi_chrg_info(chip, "Get vbus present , continue to charging\n"); + break; + } + msleep(100); + } + } + + mmi_chrg_info(chip, "battery current %d\n", ibatt_curr); + mmi_chrg_info(chip, "battery voltage %d\n", vbatt_volt); + mmi_chrg_info(chip, "battery temp %d\n", batt_temp); + mmi_chrg_info(chip, "battery capacity %d\n", batt_soc); + + if (vbus_pres && + (sm_state == PM_STATE_PPS_TUNNING_CURR + || sm_state == PM_STATE_PPS_TUNNING_VOLT + || sm_state == PM_STATE_CP_CC_LOOP + || sm_state == PM_STATE_CP_CV_LOOP)) { + mmi_dump_charger_error(chip, chrg_list->chrg_dev[CP_MASTER]); + chrg_policy_error_recovery(chip, chrg_list); + } + + if (sm_state == PM_STATE_CP_CV_LOOP) + ignore_hysteresis_degc = true; + zone_change = mmi_find_temp_zone(chip, batt_temp, ignore_hysteresis_degc); + chrg_step = &chip->chrg_step; + + if (chip->pres_temp_zone == ZONE_COLD + || chip->pres_temp_zone == ZONE_HOT + || !chrg_list->chrg_dev[PMIC_SW]->charger_enabled + || vbatt_volt > chip->batt_ovp_lmt) { + + mmi_chrg_info(chip, "Force stop charging, " + "pres_temp_zone %d, " + "pmic charger enabled %d, " + "vbatt_volt %dmv, " + "batt ovp limit %dmv\n", + chip->pres_temp_zone, + chrg_list->chrg_dev[PMIC_SW]->charger_enabled, + vbatt_volt, chip->batt_ovp_lmt); + mmi_chrg_sm_move_state(chip, PM_STATE_STOP_CHARGE); + } + + if (!vbus_pres) { + mmi_chrg_sm_move_state(chip, PM_STATE_DISCONNECT); + } else if (sm_state == PM_STATE_ENTRY + || sm_state == PM_STATE_STOP_CHARGE) { + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + } else if (sm_state == PM_STATE_DISCONNECT) { + mmi_get_input_current_settled(chrg_list->chrg_dev[PMIC_SW], + &chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } else if (zone_change && + chip->pres_temp_zone != ZONE_COLD && + chip->pres_temp_zone != ZONE_HOT) { + + if (batt_temp >= chrg_step->temp_c) { + mmi_chrg_info(chip, "battery temp %d, temp thre %d " + "Enter into COOLING LOOP !\n", + batt_temp, chrg_step->temp_c); + chip->batt_therm_cooling = true; + chip->pd_batt_therm_volt = chip->pd_request_volt_prev; + chip->pd_batt_therm_curr = chip->pd_request_curr_prev; + } else if (!chip->batt_therm_cooling && + !ignore_hysteresis_degc) { + mmi_chrg_info(chip, "battery temp %d, temp thre %d " + "Restart select chrg step and temp zone !\n", + batt_temp, chrg_step->temp_c); + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } + } + + mmi_chrg_dbg(chip, PR_MOTO, "temp zone %d, is_changed %d, " + "chrg_step %d, " + "step cc curr %d, step cv volt %d, " + "step cv tapper curr %d\n", + chip->pres_temp_zone, zone_change, + chrg_step->pres_chrg_step, + chrg_step->chrg_step_cc_curr, + chrg_step->chrg_step_cv_volt, + chrg_step->chrg_step_cv_tapper_curr); + switch (sm_state) { + case PM_STATE_DISCONNECT: + mmi_chrg_info(chip,"vbus disconnect !, jump to PM_STATE_DISCONNECT," + "recovery PMIC-SW limitation, and close CP charg\n"); + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + heartbeat_dely_ms = HEARTBEAT_CANCEL; + break; + case PM_STATE_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chip->pd_pps_support + && chrg_list->cp_master + && vbatt_volt > chip->pl_chrg_vbatt_min + && chrg_step->pres_chrg_step != chip->chrg_step_nums - 1 + && chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min + && batt_soc < CP_CHRG_SOC_LIMIT) { + + mmi_chrg_dbg(chip, PR_MOTO, "Enter into CHRG PUMP, " + "vbatt %d uV, " + "pps support %d, " + "chrg step %d, " + "chrg step cc curr %d uA, " + "CP master charging curr min %d uA\n", + vbatt_volt, + chip->pd_pps_support, + chrg_step->pres_chrg_step, + chrg_step->chrg_step_cc_curr, + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min); + mmi_chrg_sm_move_state(chip, PM_STATE_CHRG_PUMP_ENTRY); + + } else { + mmi_chrg_dbg(chip, PR_MOTO, "Enter into PMIC switch charging, " + "the reason is : vbatt %d uV, " + "pl chrg vbatt min %d uV, " + "pps support %d, " + "chrg step %d\n", + vbatt_volt, chip->pl_chrg_vbatt_min, + chip->pd_pps_support, + chrg_step->pres_chrg_step); + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + + } + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt= 0; + chip->pd_sys_therm_volt= chip->pd_request_volt_prev; + chip->pd_sys_therm_curr = chip->pd_request_curr_prev; + chip->pd_batt_therm_volt = chip->pd_request_volt_prev; + chip->pd_batt_therm_curr = chip->pd_request_curr_prev; + chip->sys_therm_cooling = false; + chip->sys_therm_force_pmic_chrg = false; + chip->recovery_pmic_chrg = false; + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_SW_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip, "Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + if (!chip->dont_rerun_aicl) { + mmi_chrg_info(chip, "Do an rerun usb AICL for PMIC-SW\n"); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], false); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], true); + } + mmi_chrg_info(chip, "Check all effective pdo info again\n"); + usbpd_get_pdo_info(chip->pd_handle, chip->mmi_pdo_info,PD_MAX_PDO_NUM); + mmi_chrg_info(chip, "Select FIXED pdo for switch charging !\n"); + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + mmi_chrg_info(chip,"find pdo %d, max volt %d, max curr %d\n", + chip->mmi_pdo_info[i].type, + chip->mmi_pdo_info[i].uv_max, + chip->mmi_pdo_info[i].ua); + if (chip->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_FIXED + && chip->mmi_pdo_info[i].uv_max >= SWITCH_CHARGER_PPS_VOLT + && chip->mmi_pdo_info[i].ua >= TYPEC_HIGH_CURRENT_UA) { + mmi_chrg_dbg(chip, PR_MOTO, "select 5V/3A pps, pdo %d\n", i); + chip->mmi_pd_pdo_idx = + chip->mmi_pdo_info[i].pdo_pos; + break; + } + } + + chip->pd_request_volt = SWITCH_CHARGER_PPS_VOLT; + chip->pd_request_curr = TYPEC_HIGH_CURRENT_UA; + mmi_chrg_info(chip,"Select pdo %d, pd request curr %d, volt %d\n", + chip->mmi_pd_pdo_idx, + chip->pd_request_curr, + chip->pd_request_volt); + + mmi_chrg_sm_move_state(chip, PM_STATE_SW_LOOP); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_SW_LOOP: + if (chip->pd_pps_support + && chip->cp_disable == false + && vbatt_volt > chip->pl_chrg_vbatt_min + && chrg_step->pres_chrg_step != chip->chrg_step_nums - 1 + && chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min + && !chip->recovery_pmic_chrg + && !chip->sys_therm_force_pmic_chrg + && batt_soc < CP_CHRG_SOC_LIMIT) { + mmi_chrg_info(chip, "Enter CP, the reason is : " + "pd pps support %d, " + "vbatt %duV, chrg step %d\n", + chip->pd_pps_support, + vbatt_volt, chrg_step->pres_chrg_step); + mmi_chrg_sm_move_state(chip, PM_STATE_CHRG_PUMP_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + mmi_chrg_dbg(chip, PR_MOTO, "Continue to SW charging, " + "vbatt %d uV, ibatt %d uA\n", + vbatt_volt, ibatt_curr); + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + } + break; + case PM_STATE_CHRG_PUMP_ENTRY: + mmi_chrg_info(chip,"CP master exist %d, CP slave exist %d !\n", + chrg_list->cp_master, + chrg_list->cp_slave); + if (chrg_list->cp_slave) { + mmi_chrg_info(chip,"CP slave is exist !\n"); + mmi_chrg_info(chip,"chrg step cc curr %d uA, " + "CP slave charging curr min %d uA\n", + chrg_step->chrg_step_cc_curr, + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min); + if (chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min) { + mmi_chrg_sm_move_state(chip, + PM_STATE_DULE_CP_ENTRY); + } else + mmi_chrg_sm_move_state(chip, + PM_STATE_SINGLE_CP_ENTRY); + } else { + mmi_chrg_info(chip,"CP slave isn't exist !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_SINGLE_CP_ENTRY); + } + + mmi_chrg_info(chip,"Set PMIC SW FCC limits!\n"); + if (!chrg_list->chrg_dev[PMIC_SW]->charger_limited + && chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited > 0) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited); + mmi_chrg_info(chip,"Set PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = true; + } + + mmi_chrg_info(chip, "Check all effective pdo info again\n"); + usbpd_get_pdo_info(chip->pd_handle, chip->mmi_pdo_info,PD_MAX_PDO_NUM); + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + if ((chip->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_AUGMENTED) + && chip->mmi_pdo_info[i].uv_max >= PUMP_CHARGER_PPS_MIN_VOLT + && chip->mmi_pdo_info[i].ua >= chip->typec_middle_current) { + chip->mmi_pd_pdo_idx = chip->mmi_pdo_info[i].pdo_pos; + mmi_chrg_info(chip, + "Pd charger support pps, pdo %d, " + "volt %d, curr %d \n", + chip->mmi_pd_pdo_idx, + chip->mmi_pdo_info[i].uv_max, + chip->mmi_pdo_info[i].ua); + chip->pd_pps_support = true; + + if (chip->mmi_pdo_info[i].uv_max < + chip->pd_volt_max) { + chip->pd_volt_max = + chip->mmi_pdo_info[i].uv_max; + } + + if (chip->mmi_pdo_info[i].ua < + chip->pd_curr_max) { + chip->pd_curr_max = + chip->mmi_pdo_info[i].ua; + } + + break; + } + } + + /*Initial setup pps request power by the battery voltage*/ + chip->pd_request_volt = (2 * vbatt_volt) % 20000; + chip->pd_request_volt = 2 * vbatt_volt - chip->pd_request_volt + + chip->pps_volt_comp; + chip->pd_request_curr = + min(chip->pd_curr_max, chip->typec_middle_current); + mmi_chrg_info(chip,"pps init , volt %dmV, curr %dmA, volt comp %dmv\n", + chip->pd_request_volt, chip->pd_request_curr, chip->pps_volt_comp); + chrg_policy_error_clear(chip, chrg_list); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_SINGLE_CP_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_chrg_info(chip,"Disable Slave Charger Pump !\n"); + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Enable Master Charger Pump !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_PPS_TUNNING_CURR); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_STATE_DULE_CP_ENTRY: + if (chrg_list->cp_master) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Enable Master Charger Pump !\n"); + } + + if (chrg_list->cp_slave) { + chrg_list->cp_clave_later = true; + mmi_chrg_info(chip,"For ibus UCP, " + "delay start Slave Charger Pump in TUNNING_VOLT stage !\n"); + } + + if (chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_sm_move_state(chip, PM_STATE_PPS_TUNNING_CURR); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_STATE_PPS_TUNNING_CURR: + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + if (chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into " + "SW directly\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + }else if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + if (chip->pd_request_curr - chip->pps_curr_steps + > chip->typec_middle_current) + chip->pd_request_curr -= chip->pps_curr_steps; + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_chrg_info(chip,"During the curr going up process, " + "the chrg step was changed," + "stop increase pps curr and Enter into " + "CC stage as soon!\n"); + } else if (chip->pps_result < 0) { + if (mmi_get_pps_result_history(chip) != NO_ERROR) { + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_chrg_info(chip,"Too many pdo request failed," + " Enter into CC stage directly!\n"); + mmi_clear_pps_result_history(chip); + } + chip->pd_request_curr = chip->pd_request_curr_prev; + goto schedule; + } else if (chip->pd_request_curr + chip->pps_curr_steps + <= chip->pd_curr_max + && vbatt_volt < chrg_step->chrg_step_cv_volt + && ibatt_curr < chrg_step->chrg_step_cc_curr) { + chip->pd_request_curr += chip->pps_curr_steps; + mmi_chrg_dbg(chip, PR_MOTO, "Increase pps curr %d\n", + chip->pd_request_curr); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + } else { + mmi_chrg_info(chip,"Enter into tunning pps volt\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_PPS_TUNNING_VOLT); + } + break; + case PM_STATE_PPS_TUNNING_VOLT: + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + if (chrg_list->cp_slave + && chrg_list->cp_clave_later + && !chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], true); + mmi_chrg_info(chip,"Enable Slave Charger Pump !\n"); + } + + if (chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_info(chip,"CP MASTER was disabled, " + "Enter into SW directly\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + } else if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + chip->pd_request_volt -= chip->pps_volt_steps; + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_chrg_info(chip,"Duing the volt going up process, " + "the chrg step was changed," + "stop increase pps volt and" + " Enter into CC stage as soon!\n"); + } else if (chip->pps_result < 0) { + if (mmi_get_pps_result_history(chip) != NO_ERROR) { + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_clear_pps_result_history(chip); + mmi_chrg_info(chip,"Too many pdo request failed," + " Enter into CC stage directly!\n"); + } + chip->pd_request_volt = chip->pd_request_volt_prev; + goto schedule; + } else if (chip->pd_request_volt + chip->pps_volt_steps + <= chip->pd_volt_max + && vbatt_volt < chrg_step->chrg_step_cv_volt + && ibatt_curr < ((chrg_step->pres_chrg_step == STEP_FIRST) ? + chrg_step->chrg_step_cc_curr + chip->step_first_curr_comp: + chrg_step->chrg_step_cc_curr)) { + chip->pd_request_volt += chip->pps_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, "Increase pps volt %d\n", + chip->pd_request_volt); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + } else { + mmi_chrg_info(chip,"Enter into CC loop stage !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_CC_LOOP); + } + + if (ibatt_curr > batt_curr_roof) { + batt_curr_roof = ibatt_curr; + } + if (ibatt_curr < batt_curr_roof) + pd_constant_power_cnt++; + else + pd_constant_power_cnt = 0; + + if (pd_constant_power_cnt > PD_CONT_PWR_CNT) { + mmi_chrg_info(chip,"PD adapter was ready in constant power state, " + "Enter into CC loop stage !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_CC_LOOP); + } + mmi_chrg_info(chip, "pd_constant_power_cnt %d, " + "batt_curr_roof %d\n", + pd_constant_power_cnt, + batt_curr_roof); + break; + case PM_STATE_CP_CC_LOOP: + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CC loop, chrg_step %d, " + "vbatt %dmV, ibatt %dmA, " + "CC target curr %dmA, " + "next CV target volt %dmV\n ", + chrg_step->pres_chrg_step, + vbatt_volt, ibatt_curr, + chrg_step->chrg_step_cc_curr, + chrg_step->chrg_step_cv_volt); + if (chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into SW directly\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + goto schedule; + } + + if (chip->pps_result < 0) { + mmi_chrg_err(chip, "Last select pdo failed\n"); + chip->pps_result = mmi_get_pps_result_history(chip); + switch (chip->pps_result) { + case BLANCE_POWER: + chip->pd_pps_balance = true; + mmi_chrg_err(chip, "Enable pps power balance\n"); + mmi_clear_pps_result_history(chip); + break; + case RESET_POWER: + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + mmi_chrg_err(chip, "Hard reset charger policy to recovery power," + "Since too many pdo failed\n"); + mmi_clear_pps_result_history(chip); + break; + default: + break; + } + + chip->pd_request_volt = chip->pd_request_volt_prev; + if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->pd_request_volt < chip->pd_volt_max) + chrg_cc_power_tunning_cnt = + CC_POWER_COUNT; + goto schedule; + } + + if (!chip->sys_therm_cooling + && !chip->batt_therm_cooling + && pd_constant_power_cnt <= PD_CONT_PWR_CNT) { + if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->pd_request_volt < chip->pd_volt_max + && chrg_cc_power_tunning_cnt >= + CC_POWER_COUNT) { + if (chip->pd_pps_balance + && (chip->pd_request_curr - chip->pps_curr_steps) + > chip->typec_middle_current) { + chip->pd_request_curr -= + chip->pps_curr_steps; + chip->pd_request_volt += + mmi_calculate_delta_volt(chip->pd_request_volt_prev, + chip->pd_request_curr_prev, + chip->pps_curr_steps); + mmi_chrg_dbg(chip, PR_MOTO, + "Request curr decreass %dmA, " + "Request volt increase %dmV \n", + chip->pd_request_curr, + chip->pd_request_volt); + } else { + if (chip->pd_request_curr + chip->pps_curr_steps + < chip->pd_curr_max) { + chip->pd_request_curr += + chip->pps_curr_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "Request curr decreass %dmA\n ", + chip->pd_request_curr); + } else if (chip->pd_request_volt + chip->pps_volt_steps + < chip->pd_volt_max) { + chip->pd_request_volt += + chip->pps_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "Request volt decreass %duV\n ", + chip->pd_request_volt); + } + } + chrg_cc_power_tunning_cnt = 0; + mmi_chrg_dbg(chip, PR_MOTO, + "For keeping CC chrg POWER, " + "Have to adjust pd input volt or curr" + "Request volt %dmV, " + "Request curr %dmA\n", + chip->pd_request_volt, + chip->pd_request_curr); + }else if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->pd_request_volt < chip->pd_volt_max) { + chrg_cc_power_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CC tunning cnt %d\n", + chrg_cc_power_tunning_cnt); + } else if (ibatt_curr > chrg_step->chrg_step_cc_curr + + CC_CURR_DEBOUNCE) { + chip->pd_request_volt -= chip->pps_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "In the CC step , the ibatt is greater than CC curr, " + "Request volt decreass %duV to remain CC step\n ", + chip->pd_request_volt); + } else + chrg_cc_power_tunning_cnt = 0; + + if(ibatt_curr < chrg_step->chrg_step_cc_curr) + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + } + + if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + if (chrg_cv_taper_tunning_cnt > + CV_TAPPER_COUNT) { + mmi_chrg_sm_move_state(chip, PM_STATE_CP_CV_LOOP); + chrg_cv_taper_tunning_cnt = 0; + chrg_cv_delta_volt = CV_DELTA_VOLT; + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV taper cnt %d, " + "chrg step cv volt %dmV, " + "vbatt %dmV\n", + chrg_cv_taper_tunning_cnt, + chrg_step->chrg_step_cv_volt, + vbatt_volt); + } + } else + chrg_cv_taper_tunning_cnt = 0; + + break; + case PM_STATE_CP_CV_LOOP: + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV loop, chrg_step %d, " + "vbatt %dmV, ibatt %dmA, " + "CV target volt %dmV, " + "CV taper curr %dmA\n ", + chrg_step->pres_chrg_step, + vbatt_volt, ibatt_curr, + chrg_step->chrg_step_cv_volt, + chrg_step->chrg_step_cv_tapper_curr); + if (chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into SW directly\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + goto schedule; + } + + if (chip->pps_result < 0) { + mmi_chrg_err(chip, "Last select pdo failed\n"); + chip->pps_result = mmi_get_pps_result_history(chip); + switch (chip->pps_result) { + case BLANCE_POWER: + chip->pd_request_curr -= + chip->pps_curr_steps; + mmi_chrg_err(chip, "Reduce pps curr,for pps power balance\n"); + mmi_clear_pps_result_history(chip); + break; + case RESET_POWER: + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + mmi_chrg_err(chip, "Hard reset charger policy to recovery power," + "Since too many pdo failed\n"); + mmi_clear_pps_result_history(chip); + break; + default: + break; + } + chip->pd_request_volt = chip->pd_request_volt_prev; + goto schedule; + } + + if (vbatt_volt >= chrg_step->chrg_step_cv_volt + && ((!chrg_step->last_step && + ibatt_curr < chrg_step->chrg_step_cv_tapper_curr) + || ibatt_curr < chrg_list->chrg_dev[CP_MASTER]->charging_curr_min)) { + if (chrg_cv_taper_tunning_cnt >= CV_TAPPER_COUNT) { + if (ibatt_curr < + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min) { + mmi_chrg_info(chip, "Ready quite CP chrg stage, " + "and Enter into PMIC switch chrg stage, " + "chrg step %d, ibatt %dmA\n", + chrg_step->pres_chrg_step, ibatt_curr); + mmi_find_chrg_step(chip, + chip->pres_temp_zone, vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_QUIT); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + if (mmi_find_chrg_step(chip, + chip->pres_temp_zone, vbatt_volt)) { + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + mmi_chrg_info(chip,"Jump to next chrg step\n"); + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + } else { + mmi_chrg_info(chip,"Can't find next chrg step\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_QUIT); + } + } + chrg_cv_taper_tunning_cnt = 0; + } else { + chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, "chrg cv taper cnt ++, %d\n", + chrg_cv_taper_tunning_cnt); + } + + }else if (!chip->sys_therm_cooling + && !chip->batt_therm_cooling) { + + chrg_cv_taper_tunning_cnt = 0; + if (vbatt_volt > chrg_step->chrg_step_cv_volt + 10000 + chip->afvc_volt_comp) { + if (chrg_cv_delta_volt > 20000) + chip->pd_request_volt -= chrg_cv_delta_volt; + else + chip->pd_request_volt -= 20000; + mmi_chrg_info(chip, + "For keeping CV stage, decrease volt %dmV, " + "cv delta volt %dmV\n", + chip->pd_request_volt, chrg_cv_delta_volt); + } else if (vbatt_volt < chrg_step->chrg_step_cv_volt - 10000) { + chrg_cv_delta_volt -= 20000; + chip->pd_request_volt += 20000; + mmi_chrg_info(chip, + "For keeping CV stage, increase volt %dmV, " + "cv delta volt %dmV\n", + chip->pd_request_volt, chrg_cv_delta_volt); + } else { + mmi_chrg_dbg(chip, PR_MOTO, "CV loop work well, " + "keep pd power, volt %dmV, curr %dmA\n", + chip->pd_request_volt, chip->pd_request_curr); + } + + }else { + /*In this case, sys_therm_cooling or batt_therm_cooling is ture*/ + + if (vbatt_volt > chrg_step->chrg_step_cv_volt + 10000 + chip->afvc_volt_comp) { + if (chrg_cv_delta_volt > 20000) + chip->pd_request_volt -= chrg_cv_delta_volt; + else + chip->pd_request_volt -= 20000; + mmi_chrg_info(chip, + "For keeping CV stage, decrease volt %dmV, " + "cv delta volt %dmV\n", + chip->pd_request_volt, chrg_cv_delta_volt); + } else { + mmi_chrg_dbg(chip, PR_MOTO, "CV loop work well, " + "keep pd power, volt %dmV, curr %dmA\n", + chip->pd_request_volt, chip->pd_request_curr); + } + } + + if (chrg_list->cp_slave) { + if (quit_slave_chrg_cnt > 3 + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + mmi_chrg_info(chip,"Quit slave chrg, the reason is :ibatt %duA, " + "CP slave charging curr min %d uA\n", + ibatt_curr, + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Restart CP master again\n"); + } + + if (ibatt_curr < + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min) + quit_slave_chrg_cnt++; + else + quit_slave_chrg_cnt = 0; + } + break; + case PM_STATE_CP_QUIT: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + mmi_chrg_sm_move_state(chip, PM_STATE_RECOVERY_SW); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_RECOVERY_SW: + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + + if (!chip->dont_rerun_aicl) { + mmi_chrg_info(chip,"Do an rerun usb AICL for PMIC-SW\n"); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], false); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], true); + } + chip->recovery_pmic_chrg = true; + chrg_cv_taper_tunning_cnt = 0; + } + + chip->pd_request_curr = TYPEC_HIGH_CURRENT_UA; + mmi_chrg_info(chip,"ibatt : %dmA, step cc curr : %dmA\n", + ibatt_curr, chrg_step->chrg_step_cc_curr); + if (ibatt_curr > chrg_step->chrg_step_cc_curr) { + chip->pd_request_volt -= CV_DELTA_VOLT; + + mmi_chrg_dbg(chip, PR_MOTO, "Reduce pps volt %dmV, curr %dmA\n ", + chip->pd_request_volt, chip->pd_request_curr); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + chrg_cv_taper_tunning_cnt = 0; + } else { + chrg_cv_taper_tunning_cnt++; + + } + + if (chrg_cv_taper_tunning_cnt > CV_TAPPER_COUNT){ + mmi_chrg_sm_move_state(chip, PM_STATE_SW_LOOP); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_STATE_STOP_CHARGE: + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + if (chip->pres_temp_zone != ZONE_COLD + && chip->pres_temp_zone != ZONE_HOT + && chrg_list->chrg_dev[PMIC_SW]->charger_enabled + && chrg_step->chrg_step_cc_curr > 0) { + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + chip->pd_request_volt = SWITCH_CHARGER_PPS_VOLT; + chip->pd_request_curr = TYPEC_HIGH_CURRENT_UA; + break; + case PM_STATE_COOLING_LOOP: + mmi_chrg_info(chip,"In cooling loop, batt temp %d, cmp temp %d\n", + batt_temp, chrg_step->temp_c); + + if (batt_temp > chrg_step->temp_c + COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, Cooling loop failed, " + "force enter PM_STATE_ENTRY " + "restart this charger process !\n", + batt_temp); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + + } else if (batt_temp > chrg_step->temp_c) { + cooling_curr = + min(chip->pd_curr_max, chip->typec_middle_current); + if (chip->pd_request_curr > cooling_curr) { + chip->pd_request_curr -= COOLING_DELTA_POWER; + } else { + mmi_chrg_info(chip, "pd request curr %dmA, " + "battery temp %d, " + "cooling failed, Restart PM_STATE_ENTRY !\n", + chip->pd_request_curr, batt_temp); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + } + + } else if (batt_temp < chrg_step->temp_c - COOLING_DELTA_POWER) { + + mmi_chrg_info(chip,"Batt temp %d, successful exit from COOLing loop, " + "and Enter int TUNNING_CURR again !\n", + batt_temp); + mmi_chrg_sm_move_state(chip, + PM_STATE_PPS_TUNNING_CURR); + } + + if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + if (chrg_cv_taper_tunning_cnt > + CV_TAPPER_COUNT) { + + mmi_chrg_info(chip, "vbatt_volt %dmV, " + "battery temp %d, " + "chrg step cv volt %dmV" + "cooling failed, Restart PM_STATE_ENTRY !\n", + vbatt_volt, batt_temp, chrg_step->chrg_step_cv_volt); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + chrg_cv_taper_tunning_cnt = 0; + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV taper cnt %d, " + "chrg step cv volt %dmV, " + "vbatt %dmV\n", + chrg_cv_taper_tunning_cnt, + chrg_step->chrg_step_cv_volt, + vbatt_volt); + } + } else + chrg_cv_taper_tunning_cnt = 0; + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + break; + } + +schedule: + + chip->pd_target_volt = min(chip->pd_request_volt, chip->pd_volt_max); + chip->pd_target_curr = min(chip->pd_request_curr, chip->pd_curr_max); + + if (chip->system_thermal_level == 0) { + chip->sys_therm_cooling= false; + chip->sys_therm_force_pmic_chrg = false; + } else if ((chip->system_thermal_level == chip->thermal_levels - 1) + && !chip->sys_therm_force_pmic_chrg) { + chip->sys_therm_cooling = true; + chip->sys_therm_force_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + mmi_chrg_info(chip, "Thermal is the highest, level %d, " + "Force enter into single pmic charging !\n", + chip->system_thermal_level); + if(chip->pd_sys_therm_volt == 0) + chip->pd_sys_therm_volt = SWITCH_CHARGER_PPS_VOLT; + if(chip->pd_sys_therm_curr == 0) + chip->pd_sys_therm_curr = TYPEC_HIGH_CURRENT_UA; + if(chip->pd_batt_therm_volt == 0) + chip->pd_batt_therm_volt = SWITCH_CHARGER_PPS_VOLT; + if(chip->pd_batt_therm_curr == 0) + chip->pd_batt_therm_curr = TYPEC_HIGH_CURRENT_UA; + } else if (chip->system_thermal_level > 0 && + (sm_state == PM_STATE_CP_CC_LOOP || + sm_state == PM_STATE_CP_CV_LOOP)) { + + mmi_chrg_dbg(chip, PR_MOTO, "Thermal level is %d\n", + chip->system_thermal_level); + if (!chip->sys_therm_cooling) { + chip->sys_therm_cooling = true; + chip->pd_sys_therm_volt = chip->pd_request_volt_prev; + chip->pd_sys_therm_curr = chip->pd_request_curr_prev; + } + + if (ibatt_curr > + chip->thermal_mitigation[chip->system_thermal_level] + + CC_CURR_DEBOUNCE) { + if (chip->pd_sys_therm_curr - THERMAL_TUNNING_CURR >= + chip->typec_middle_current) { + chip->pd_sys_therm_curr -= THERMAL_TUNNING_CURR; + mmi_chrg_dbg(chip, PR_MOTO, "For thermal, decrease pps curr %d\n", + chip->pd_sys_therm_curr); + + } else { + mmi_chrg_dbg(chip, PR_MOTO, + "pd_sys_therm_curr %dmA was less than %dmA, " + "Give up thermal mitigation!", + chip->pd_sys_therm_curr - THERMAL_TUNNING_CURR, + chip->typec_middle_current); + } + } else if (ibatt_curr < + chip->thermal_mitigation[chip->system_thermal_level] + - CC_CURR_DEBOUNCE) { + if (chip->pd_sys_therm_curr + THERMAL_TUNNING_CURR <= + chip->pd_curr_max) { + chip->pd_sys_therm_curr += THERMAL_TUNNING_CURR; + mmi_chrg_dbg(chip, PR_MOTO, "For thermal, increase pps curr %d\n", + chip->pd_sys_therm_curr); + } + } + + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + } else if (chip->system_thermal_level > 0 && + chip->system_thermal_level != chip->thermal_levels - 1 && + sm_state == PM_STATE_SW_LOOP && + chip->sys_therm_force_pmic_chrg) { + mmi_chrg_dbg(chip, PR_MOTO, "Try to recovery charger pump!\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } + + if (chip->sys_therm_cooling) { + chip->pd_target_volt = min(chip->pd_target_volt, chip->pd_sys_therm_volt); + chip->pd_target_curr = min(chip->pd_target_curr, chip->pd_sys_therm_curr); + } + + if (chip->batt_therm_cooling && !chip->sys_therm_force_pmic_chrg) { + if (batt_temp > chrg_step->temp_c + COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, Cooling loop failed, " + "force enter PM_STATE_ENTRY " + "restart this charger process !\n", + batt_temp); + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt= 0; + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + + } else if (batt_temp > chrg_step->temp_c) { + cooling_curr = + min(chip->pd_curr_max, chip->typec_middle_current); + cooling_volt = (2 * vbatt_volt) % 20000; + cooling_volt = 2 * vbatt_volt - cooling_volt + + chip->pps_volt_comp; + if (ibatt_curr > TYPEC_HIGH_CURRENT_UA + && chip->pd_batt_therm_curr > cooling_curr) { + + if (chip->pd_batt_therm_curr - COOLING_DELTA_POWER >= + chip->typec_middle_current) + chip->pd_batt_therm_curr -= COOLING_DELTA_POWER; + mmi_chrg_info(chip, "Do chrg power cooling" + "pd_batt_therm_curr %dmA, " + "battery temp %d\n", + chip->pd_batt_therm_curr, batt_temp); + } else if (ibatt_curr > TYPEC_HIGH_CURRENT_UA + && chip->pd_batt_therm_volt > cooling_volt) { + chip->pd_batt_therm_volt -= COOLING_DELTA_POWER; + mmi_chrg_info(chip, "Do chrg power cooling" + "pd request volt %dmA, " + "battery temp %d\n", + chip->pd_batt_therm_volt, batt_temp); + + } else { + if (chip->batt_therm_cooling_cnt > COOLING_MAX_CNT) { + + mmi_chrg_info(chip, "Do chrg power cooling failed" + "pd_batt_therm_currr %dmA, " + "pd_batt_therm_volt %dmV" + "battery temp %d, " + "Restart PM_STATE_ENTRY !\n", + chip->pd_batt_therm_curr, + chip->pd_batt_therm_volt, + batt_temp); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + + } else { + mmi_chrg_info(chip, "It's already the lowest cooling chrg power" + "waiting for a while, cooling cnt %d, " + "battery temp %d\n", + batt_temp, chip->batt_therm_cooling); + chip->batt_therm_cooling_cnt++; + } + } + + } else if (batt_temp < chrg_step->temp_c - COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, " + "Exit successfully from COOLing loop!\n", + batt_temp); + + if (sm_state == PM_STATE_CP_CC_LOOP) { + mmi_chrg_info(chip,"Jump into CURR tunning" + "for chrg power poerformance!\n"); + mmi_chrg_sm_move_state(chip, + PM_STATE_PPS_TUNNING_CURR); + } + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + } + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + } + + if (chip->batt_therm_cooling) { + chip->pd_target_volt = min(chip->pd_target_volt, chip->pd_batt_therm_volt); + chip->pd_target_curr = min(chip->pd_target_curr, chip->pd_batt_therm_curr); + } + + mmi_chrg_dbg(chip, PR_MOTO, "chrg sm work,%s, " + "battery soc %d, " + "battery temp %d, " + "battery current %d, " + "battery voltage %d, " + "sys therm level %d, " + "pmic therm level %d, " + "sys therm cooling %d, " + "batt therm cooling %d, " + "sys therm force pmic chrg %d, " + "recovery pmic chrg %d\n", + pm_state_str[sm_state], + batt_soc, batt_temp, + ibatt_curr, vbatt_volt, + chip->system_thermal_level, + pmic_sys_therm_level, + chip->sys_therm_cooling, + chip->batt_therm_cooling, + chip->sys_therm_force_pmic_chrg, + chip->recovery_pmic_chrg); + + mmi_chrg_dbg(chip, PR_MOTO, "pd request volt %dmV, " + "pd request curr %dmA, " + "pd target volt %dmV, " + "pd target curr %dmA, " + "sys therm volt %dmV, " + "sys therm curr %dmA, " + "batt therm volt %dmV, " + "batt therm curr %dmA\n", + chip->pd_request_volt, + chip->pd_request_curr, + chip->pd_target_volt, + chip->pd_target_curr, + chip->pd_sys_therm_volt, + chip->pd_sys_therm_curr, + chip->pd_batt_therm_volt, + chip->pd_batt_therm_curr); + + if (chip->pd_target_volt < SWITCH_CHARGER_PPS_VOLT + || chip->pd_target_curr < chip->typec_middle_current) { + + if (sm_state == PM_STATE_PPS_TUNNING_CURR + || sm_state == PM_STATE_PPS_TUNNING_VOLT + || sm_state == PM_STATE_CP_CC_LOOP + || sm_state == PM_STATE_CP_CV_LOOP) { + + mmi_chrg_err(chip, "%s, wrong pd voltage or current , " + "request pd voltage %d, " + "request pd current %d\n", + pm_state_str[sm_state], + chip->pd_target_volt, + chip->pd_target_curr); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } + goto skip_pd_select; + } + + chip->pps_result = usbpd_select_pdo(chip->pd_handle, + chip->mmi_pd_pdo_idx, + chip->pd_target_volt, + chip->pd_target_curr); + mmi_set_pps_result_history(chip, chip->pps_result); + if (!chip->pps_result) { + chip->pd_request_volt_prev = chip->pd_target_volt; + chip->pd_request_curr_prev = chip->pd_target_curr; + } + +skip_pd_select: + + if (heartbeat_dely_ms > 0) { + mmi_chrg_err(chip, "schedule work timer %dms\n", heartbeat_dely_ms); + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(heartbeat_dely_ms)); + } else { + mmi_chrg_policy_clear(chip); + chip->sm_work_running = false; + chip->pd_pps_support = false; + mmi_chrg_err(chip, "exit sm work\n"); + } + + return; +} + +int mmi_chrg_policy_init(struct mmi_charger_manager *chip, + struct mmi_chrg_dts_info *chrg_dts, + int chrg_cnt) { + int i = 0, ops_cnt = 0, chrg_idx = 0; + struct mmi_charger_device *chrg_dev; + + if (!chip ||!chrg_dts) { + mmi_chrg_err(chip, "invalid input param!\n"); + return -EINVAL; + } + + ops_cnt = ARRAY_SIZE(dev_ops); + if (chrg_cnt > ops_cnt || + chrg_cnt > chip->mmi_chrg_dev_num) { + mmi_chrg_err(chip, "invalid input param!, chrg_cnt %d , ops_cnt %d, " + "mmi_chrg_dev_num %d\n", + chrg_cnt, ops_cnt, chip->mmi_chrg_dev_num); + return -EINVAL; + } + + mmi_chrg_err(chip, "chrg_cnt %d\n", chrg_cnt); + for (i = 0; i < chrg_cnt; i++) { + if (!strcmp(chrg_dts[i].chrg_name, dev_ops[i].dev_name)) { + + chrg_dev = mmi_charger_device_register(chrg_dts[i].chrg_name, + chrg_dts[i].psy_name, chip->dev, chip, + dev_ops[i].ops); + if (IS_ERR_OR_NULL(chrg_dev) + || !is_charger_exist(chrg_dts[i].chrg_name)) { + mmi_chrg_err(chip, + "register mmi charger %s failed\n", + chrg_dts[i].chrg_name); + return -EINVAL; + } else { + mmi_chrg_info(chip, + "register mmi charger %s successfully, i %d, chrg_idx %d\n", + chrg_dts[i].chrg_name, i, chrg_idx); + chrg_dev->charging_curr_limited = + chrg_dts[i].charging_curr_limited; + mmi_chrg_err(chip, "charging_curr_limited %d\n", + chrg_dts[i].charging_curr_limited); + chrg_dev->charging_curr_min = + chrg_dts[i].charging_curr_min; + mmi_chrg_err(chip, "charging_curr_min %d, chrg_id %d\n", + chrg_dts[i].charging_curr_min, chrg_idx); + chip->chrg_list[chrg_idx] = chrg_dev; + mmi_chrg_err(chip, "--- over ----\n"); + chrg_idx++; + } + } + } + + mmi_chrg_info(chip,"chrg_cnt %d, ops_cnt %d, mmi_chrg_dev_num %d, " + "chrg_idx %d\n", + chrg_cnt, ops_cnt, chip->mmi_chrg_dev_num, chrg_idx); + + if (chrg_idx != chip->mmi_chrg_dev_num + && chrg_idx > 0) { + + mmi_chrg_err(chip, "chrg_id %d != charger num %d\n", + chrg_idx, chip->mmi_chrg_dev_num); + for (i = 0; i < chrg_idx; i++) + mmi_charger_device_unregister(chip->chrg_list[i]); + return -EINVAL; + } + + chrg_dev_init(chip, &g_chrg_list); + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + INIT_DELAYED_WORK(&chip->mmi_chrg_sm_work, mmi_chrg_sm_work_func); + return 0; +} + diff --git a/drivers/power/mmi_parallel_charger/qpnp_pmic_charger.c b/drivers/power/mmi_parallel_charger/qpnp_pmic_charger.c new file mode 100644 index 000000000000..5c2083dfb4bf --- /dev/null +++ b/drivers/power/mmi_parallel_charger/qpnp_pmic_charger.c @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include +#include "mmi_charger_core.h" + +static int qpnp_pmic_enable_charging(struct mmi_charger_device *chrg, bool en) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + prop.intval = en; + rc = power_supply_set_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, &prop); + + if (!rc) { + chrg->charger_enabled = !!prop.intval; + } else + chrg->charger_enabled = false; + + return rc; +} + +static int qpnp_pmic_is_charging_enabled(struct mmi_charger_device *chrg, bool *en) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, &prop); + if (!rc) { + chrg->charger_enabled = !!prop.intval; + } else + chrg->charger_enabled = false; + + *en = chrg->charger_enabled; + + return rc; +} + +static int qpnp_pmic_get_charging_current(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + *uA = !!prop.intval; + + return rc; +} + +static int qpnp_pmic_set_charging_current(struct mmi_charger_device *chrg, u32 uA) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + prop.intval = uA; + rc = power_supply_set_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, &prop); +// if (!rc) +// chrg->charger_limited = true; + + return rc; +} + +static int qpnp_pmic_get_input_current_settled(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc; + struct power_supply *chrg_psy; + union power_supply_propval prop = {0,}; + + chrg_psy = power_supply_get_by_name("usb"); + if (!chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg_psy, + POWER_SUPPLY_PROP_HW_CURRENT_MAX, &prop); + if (!rc) + chrg->input_curr_setted = prop.intval; + *uA = chrg->input_curr_setted; + + return rc; +} + +static int qpnp_pmic_update_charger_status(struct mmi_charger_device *chrg) +{ + int rc; + struct power_supply *usb_psy; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + usb_psy = power_supply_get_by_name("usb"); + if (!usb_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + chrg->charger_data.vbatt_volt = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + chrg->charger_data.ibatt_curr = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_TEMP, &prop); + if (!rc) + chrg->charger_data.batt_temp = prop.intval / 10; + + rc = power_supply_get_property(usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + chrg->charger_data.vbus_volt = prop.intval; + + rc = power_supply_get_property(usb_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, &prop); + if (!rc) + chrg->charger_data.ibus_curr = prop.intval; + + rc = power_supply_get_property(usb_psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (!rc) + chrg->charger_data.vbus_pres = !!prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, &prop); + if (!rc) + chrg->charger_enabled= !!prop.intval; + + chrg_dev_info(chrg, "QPNP SW chrg: status update: --- info---"); + chrg_dev_info(chrg, "vbatt %d\n", chrg->charger_data.vbatt_volt); + chrg_dev_info(chrg, "ibatt %d\n", chrg->charger_data.ibatt_curr); + chrg_dev_info(chrg, "batt temp %d\n", chrg->charger_data.batt_temp); + chrg_dev_info(chrg, "vbus %d\n", chrg->charger_data.vbus_volt); + chrg_dev_info(chrg, "ibus %d\n", chrg->charger_data.ibus_curr); + chrg_dev_info(chrg, "vbus pres %d\n", chrg->charger_data.vbus_pres); + chrg_dev_info(chrg, "charger_enabled %d\n", chrg->charger_enabled); + chrg_dev_info(chrg, "charger_limited %d\n", chrg->charger_limited); + return rc; +} + +struct mmi_charger_ops qpnp_pmic_charger_ops = { + .enable = qpnp_pmic_enable_charging, + .is_enabled = qpnp_pmic_is_charging_enabled, + .get_charging_current = qpnp_pmic_get_charging_current, + .set_charging_current = qpnp_pmic_set_charging_current, + .get_input_current_settled = qpnp_pmic_get_input_current_settled, + .update_charger_status = qpnp_pmic_update_charger_status, +}; diff --git a/drivers/power/mmi_parallel_charger_iio/Android.mk b/drivers/power/mmi_parallel_charger_iio/Android.mk new file mode 100644 index 000000000000..dd1795de2357 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_parallel_charger_iio.ko +LOCAL_MODULE_TAGS := optional + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/mmi_parallel_charger_iio/Kbuild b/drivers/power/mmi_parallel_charger_iio/Kbuild new file mode 100644 index 000000000000..6e171daa0029 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/Kbuild @@ -0,0 +1,16 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/power/mmi_parallel_charger_iio + +obj-m += mmi_parallel_charger_iio.o +mmi_parallel_charger_iio-objs += mmi_charger_class.o +mmi_parallel_charger_iio-objs += mmi_charger_core.o +mmi_parallel_charger_iio-objs += mmi_charger_pump_policy.o +mmi_parallel_charger_iio-objs += mmi_cp_charger.o +mmi_parallel_charger_iio-objs += qpnp_pmic_charger.o +mmi_parallel_charger_iio-objs += mmi_qc3p.o +mmi_parallel_charger_iio-objs += mmi_qc3p_cp_policy.o + +#ifeq ($(CONFIG_MMI_PL_CP_POLICY), y) +#endif diff --git a/drivers/power/mmi_parallel_charger_iio/Makefile b/drivers/power/mmi_parallel_charger_iio/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/power/mmi_parallel_charger_iio/mmi_charger_class.c b/drivers/power/mmi_parallel_charger_iio/mmi_charger_class.c new file mode 100644 index 000000000000..fc44c0df0935 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/mmi_charger_class.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" + +static struct class *mmi_charger_class; + +int mmi_enable_charging(struct mmi_charger_device *chrg, bool en) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->enable) + return chrg->ops->enable(chrg,en); + + return -ENOTSUPP; +} + +int mmi_is_charging_enabled(struct mmi_charger_device *chrg, bool *en) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->is_enabled) + return chrg->ops->is_enabled(chrg,en); + + return -ENOTSUPP; +} + +int mmi_get_charing_current(struct mmi_charger_device *chrg, u32 *uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->get_charging_current) + return chrg->ops->get_charging_current(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_set_charing_current(struct mmi_charger_device *chrg, u32 uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->set_charging_current) + return chrg->ops->set_charging_current(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_get_vbus(struct mmi_charger_device *chrg, u32 *mv) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->get_vbus) + return chrg->ops->get_vbus(chrg,mv); + + return -ENOTSUPP; +} + +int mmi_get_input_current_settled(struct mmi_charger_device *chrg, u32 *uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->get_input_current_settled) + return chrg->ops->get_input_current_settled(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_get_input_current(struct mmi_charger_device *chrg, u32 *uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->get_input_current) + return chrg->ops->get_input_current(chrg,uA); + + return -ENOTSUPP; + +} + +int mmi_set_input_current(struct mmi_charger_device *chrg, u32 uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->set_input_current) + return chrg->ops->set_input_current(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_update_charger_status(struct mmi_charger_device *chrg) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->update_charger_status) + return chrg->ops->update_charger_status(chrg); + + return -ENOTSUPP; +} + +int mmi_update_charger_error(struct mmi_charger_device *chrg) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->update_charger_error) + return chrg->ops->update_charger_error(chrg); + + return -ENOTSUPP; +} + +int mmi_clear_charger_error(struct mmi_charger_device *chrg) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->clear_charger_error) + return chrg->ops->clear_charger_error(chrg); + + return -ENOTSUPP; +} + +static void mmi_charger_device_release(struct device *dev) +{ + struct mmi_charger_device *charger_dev = to_mmi_charger_device(dev); + + kfree(charger_dev); + return; +} + +static int charger_match_device_by_name(struct device *dev, + const void *data) +{ + const char *name = data; + return strcmp(dev_name(dev), name) == 0; +} + +struct mmi_charger_device *get_charger_by_name(const char *name) +{ + struct device *dev; + + if(!name) + return (struct mmi_charger_device *)NULL; + dev = class_find_device(mmi_charger_class, NULL, name, + charger_match_device_by_name); + + return dev ? to_mmi_charger_device(dev) : NULL; +} + +int is_charger_exist(const char *name) +{ + if (get_charger_by_name(name) == NULL) + return 0; + return 1; +} + +struct mmi_charger_device *mmi_charger_device_register(const char *name, + const char *psy_name,struct device *parent, void *devdata, + const struct mmi_charger_ops *ops) +{ + struct mmi_charger_device *charger_dev; + int rc; + + pr_info("mmi charger device register: name=%s\n",name); + charger_dev = kzalloc(sizeof(struct mmi_charger_device),GFP_KERNEL); + if (!charger_dev) + return ERR_PTR(-ENOMEM); + + mutex_init(&charger_dev->ops_lock); + charger_dev->dev.class = mmi_charger_class; + charger_dev->dev.parent = parent; + charger_dev->dev.release = mmi_charger_device_release; + charger_dev->name = name; + dev_set_name(&charger_dev->dev, "%s", name); + dev_set_drvdata(&charger_dev->dev, devdata); + + rc = device_register(&charger_dev->dev); + if (rc) { + kfree(charger_dev); + return ERR_PTR(rc); + } + charger_dev->chrg_psy = power_supply_get_by_name(psy_name); + charger_dev->ops = ops; + if (!charger_dev->chrg_psy) + return ERR_PTR(-ENODEV); + pr_info("mmi charger device register: name=%s, successfully\n",name); + return charger_dev; +} + +void mmi_charger_device_unregister(struct mmi_charger_device* charger_dev) +{ + if (!charger_dev) + return; + + mutex_lock(&charger_dev->ops_lock); + charger_dev->ops = NULL; + mutex_unlock(&charger_dev->ops_lock); + device_unregister(&charger_dev->dev); +} + +void mmi_charger_class_exit(void) +{ + class_destroy(mmi_charger_class); +} + +int mmi_charger_class_init(void) +{ + mmi_charger_class = class_create(THIS_MODULE, "mmi_charger"); + if (IS_ERR(mmi_charger_class)) { + pr_err("Unable to create mmi charger class; error = %ld\n", + PTR_ERR(mmi_charger_class)); + return PTR_ERR(mmi_charger_class); + } + pr_info("success to create mmi charger class \n"); + return 0; +} diff --git a/drivers/power/mmi_parallel_charger_iio/mmi_charger_class.h b/drivers/power/mmi_parallel_charger_iio/mmi_charger_class.h new file mode 100644 index 000000000000..f39ea60fe51f --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/mmi_charger_class.h @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + #ifndef __MMI_CHRG_CLASS_H_ + #define __MMI_CHRG_CLASS_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define chrg_dev_info(chip, fmt, ...) \ + pr_info("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__) \ + +struct mmi_charger_info { + bool vbus_pres; + int vbatt_volt; + int vbus_volt; + int ibatt_curr; + int ibus_curr; + int batt_temp; +}; + +#define MMI_BAT_OVP_ALARM_BIT 0 +#define MMI_BAT_OCP_ALARM_BIT 1 +#define MMI_BAT_UCP_ALARM_BIT 2 +#define MMI_BUS_OVP_ALARM_BIT 3 +#define MMI_BUS_OCP_ALARM_BIT 4 +#define MMI_BUS_UCP_ALARM_BIT 5 +#define MMI_BAT_OVP_FAULT_BIT 6 +#define MMI_BAT_OCP_FAULT_BIT 7 +#define MMI_BAT_UCP_FAULT_BIT 8 +#define MMI_BUS_OVP_FAULT_BIT 9 +#define MMI_BUS_OCP_FAULT_BIT 10 +#define MMI_BUS_UCP_FAULT_BIT 11 +#define MMI_CONV_OCP_FAULT_BIT 12 +#define MMI_THERM_ALARM_BIT 13 +#define MMI_THERM_FAULT_BIT 14 +#define MMI_CP_SWITCH_BIT 18 + +struct mmi_charger_error_info { + int chrg_err_type; + int bus_ucp_err_cnt; + int bus_ocp_err_cnt; +}; + +struct mmi_charger_device { + const char *name; /*charger device name*/ + struct power_supply *chrg_psy; /*power supply handle*/ + const struct mmi_charger_ops *ops; /*operation function*/ + struct mutex ops_lock; + struct device dev; + struct mmi_charger_info charger_data; + struct mmi_charger_error_info charger_error; + int *debug_mask; + int input_curr_setted; /*save input current*/ + int charging_curr_limited; /*charger current limitation*/ + int charging_curr_min; /*The minimum charging current supported by this device*/ + bool charger_enabled; /*is this charger device enabled*/ + bool charger_limited; /*is the charging current of this device limited*/ +}; + +struct mmi_charger_ops { + int (*enable)(struct mmi_charger_device *chrg, bool en); + int (*is_enabled)(struct mmi_charger_device *chrg, bool *en); + int (*get_charging_current)(struct mmi_charger_device *chrg, u32 *uA); + int (*set_charging_current)(struct mmi_charger_device *chrg, u32 uA); + int (*get_input_current_settled)(struct mmi_charger_device *chrg, u32 *uA); + int (*get_vbus)(struct mmi_charger_device *chrg, u32 *mv); + int (*get_input_current)(struct mmi_charger_device *chrg, u32 *uA); + int (*set_input_current)(struct mmi_charger_device *chrg, u32 uA); + int (*update_charger_status)(struct mmi_charger_device *chrg); + int (*update_charger_error)(struct mmi_charger_device *chrg); + int (*clear_charger_error)(struct mmi_charger_device *chrg); +}; + + +#define to_mmi_charger_device(obj) container_of(obj, struct mmi_charger_device, dev) +#define mmi_chrg_name(x) (dev_name(&(x)->dev)) + +extern struct mmi_charger_device *get_charger_by_name(const char *name); +extern int is_charger_exist(const char *name); +extern int mmi_charger_class_init(void); +extern void mmi_charger_class_exit(void); +extern int mmi_enable_charging(struct mmi_charger_device *chrg, bool en); +extern int mmi_is_charging_enabled(struct mmi_charger_device *chrg, bool *en); +extern int mmi_get_charing_current(struct mmi_charger_device *chrg, u32 *uA); +extern int mmi_set_charing_current(struct mmi_charger_device *chrg, u32 uA); +extern int mmi_get_input_current_settled(struct mmi_charger_device *chrg, u32 *uA); +extern int mmi_get_input_current(struct mmi_charger_device *chrg, u32 *uA); +extern int mmi_set_input_current(struct mmi_charger_device *chrg, u32 uA); +extern int mmi_update_charger_status(struct mmi_charger_device *chrg); +extern int mmi_update_charger_error(struct mmi_charger_device *chrg); +extern int mmi_clear_charger_error(struct mmi_charger_device *chrg); +extern int mmi_get_vbus(struct mmi_charger_device *chrg, u32 *mv); +extern struct mmi_charger_ops cp_charger_ops; +extern struct mmi_charger_ops qpnp_pmic_charger_ops; +extern struct mmi_charger_device *mmi_charger_device_register(const char *name, + const char *psy_name,struct device *parent, void *devdata, + const struct mmi_charger_ops *ops); +extern void mmi_charger_device_unregister(struct mmi_charger_device* charger_dev); +#endif diff --git a/drivers/power/mmi_parallel_charger_iio/mmi_charger_core.c b/drivers/power/mmi_parallel_charger_iio/mmi_charger_core.c new file mode 100644 index 000000000000..a680ee3ab666 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/mmi_charger_core.c @@ -0,0 +1,1765 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include "mmi_charger_core.h" +#include "mmi_charger_core_iio.h" +#include "mmi_charger_policy.h" +#include "mmi_qc3p.h" +#include +#include + +static int __debug_mask = 0x85; +module_param_named( + debug_mask, __debug_mask, int, S_IRUSR | S_IWUSR +); + +struct mmi_cp_policy_dev g_chrg_list = {0}; +const struct mmi_chrg_dev_ops dev_ops[] = { + { + .dev_name = "pmic-sw", + .ops = &qpnp_pmic_charger_ops, + }, + { + .dev_name = "cp-master", + .ops = &cp_charger_ops, + }, + { + .dev_name = "cp-slave", + .ops = &cp_charger_ops, + }, +}; + +bool is_chan_valid(struct mmi_charger_manager *chip, + enum mmi_charger_ext_iio_channels chan) +{ + int rc; + + if (IS_ERR(chip->ext_iio_chans[chan])) + return false; + + if (!chip->ext_iio_chans[chan]) { + chip->ext_iio_chans[chan] = iio_channel_get(chip->dev, + mmi_charger_ext_iio_chan_name[chan]); + if (IS_ERR(chip->ext_iio_chans[chan])) { + rc = PTR_ERR(chip->ext_iio_chans[chan]); + if (rc == -EPROBE_DEFER) + chip->ext_iio_chans[chan] = NULL; + + mmi_chrg_err(chip, "Failed to get IIO channel %s, rc=%d\n", + mmi_charger_ext_iio_chan_name[chan], rc); + return false; + } + } + + return true; +} + +int mmi_charger_read_iio_chan(struct mmi_charger_manager *chip, + enum mmi_charger_ext_iio_channels chan, int *val) +{ + int rc; + + if (is_chan_valid(chip, chan)) { + rc = iio_read_channel_processed( + chip->ext_iio_chans[chan], val); + return (rc < 0) ? rc : 0; + } + + return -EINVAL; +} + +int mmi_charger_write_iio_chan(struct mmi_charger_manager *chip, + enum mmi_charger_ext_iio_channels chan, int val) +{ + if (is_chan_valid(chip, chan)) + return iio_write_channel_raw(chip->ext_iio_chans[chan], + val); + + return -EINVAL; +} + +static int mmi_charger_iio_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val1, + int val2, long mask) +{ + struct mmi_charger_manager *chip = iio_priv(indio_dev); + int rc = 0; + + switch (chan->channel) { + case PSY_IIO_CP_ENABLE: + if (!chip->factory_mode) { + if (chip->pd_pps_support) + mmi_chrg_enable_all_cp(chip, val1); + else if (chip->qc3p_active) + mmi_qc3p_chrg_enable_all_cp(chip, val1); + } + break; + default: + pr_err("Unsupported mmi_charger IIO chan %d\n", chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) + pr_err("Couldn't write IIO channel %d, rc = %d\n", + chan->channel, rc); + + return rc; +} + +static int mmi_charger_iio_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val1, + int *val2, long mask) +{ + struct mmi_charger_manager *chip = iio_priv(indio_dev); + int rc = 0; + + *val1 = 0; + + switch (chan->channel) { + case PSY_IIO_CP_ENABLE: + *val1 = !chip->cp_disable; + break; + default: + pr_err("Unsupported mmi_charger IIO chan %d\n", chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) { + pr_err("Couldn't read IIO channel %d, rc = %d\n", + chan->channel, rc); + return rc; + } + + return IIO_VAL_INT; +} + +static int mmi_charger_iio_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + struct mmi_charger_manager *chip = iio_priv(indio_dev); + struct iio_chan_spec *iio_chan = chip->iio_chan; + int i; + + for (i = 0; i < ARRAY_SIZE(mmi_charger_iio_psy_channels); + i++, iio_chan++) + if (iio_chan->channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static const struct iio_info mmi_charger_iio_info = { + .read_raw = mmi_charger_iio_read_raw, + .write_raw = mmi_charger_iio_write_raw, + .of_xlate = mmi_charger_iio_of_xlate, +}; + +static int mmi_charger_init_iio_psy(struct mmi_charger_manager *chip, + struct platform_device *pdev) +{ + struct iio_dev *indio_dev = chip->indio_dev; + struct iio_chan_spec *chan; + int mmi_charger_num_iio_channels = ARRAY_SIZE(mmi_charger_iio_psy_channels); + int rc, i; + + chip->iio_chan = devm_kcalloc(chip->dev, mmi_charger_num_iio_channels, + sizeof(*chip->iio_chan), GFP_KERNEL); + if (!chip->iio_chan) + return -ENOMEM; + + chip->int_iio_chans = devm_kcalloc(chip->dev, + mmi_charger_num_iio_channels, + sizeof(*chip->int_iio_chans), + GFP_KERNEL); + if (!chip->int_iio_chans) + return -ENOMEM; + + indio_dev->info = &mmi_charger_iio_info; + indio_dev->dev.parent = chip->dev; + indio_dev->dev.of_node = chip->dev->of_node; + indio_dev->name = "mmi-parallel-charger"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = chip->iio_chan; + indio_dev->num_channels = mmi_charger_num_iio_channels; + + for (i = 0; i < mmi_charger_num_iio_channels; i++) { + chip->int_iio_chans[i].indio_dev = indio_dev; + chan = &chip->iio_chan[i]; + chip->int_iio_chans[i].channel = chan; + chan->address = i; + chan->channel = mmi_charger_iio_psy_channels[i].channel_num; + chan->type = mmi_charger_iio_psy_channels[i].type; + chan->datasheet_name = + mmi_charger_iio_psy_channels[i].datasheet_name; + chan->extend_name = + mmi_charger_iio_psy_channels[i].datasheet_name; + chan->info_mask_separate = + mmi_charger_iio_psy_channels[i].info_mask; + } + + rc = devm_iio_device_register(chip->dev, indio_dev); + if (rc) { + pr_err("Failed to register cp IIO device, rc=%d\n", rc); + return rc; + } + + chip->ext_iio_chans = devm_kcalloc(chip->dev, + ARRAY_SIZE(mmi_charger_ext_iio_chan_name), + sizeof(*chip->ext_iio_chans), + GFP_KERNEL); + if (!chip->ext_iio_chans) + return -ENOMEM; + + return 0; +} + +static int qc3p_volt_max_init = 0; +static int pd_volt_max_init = 0; +static int pd_curr_max_init = 0; +static int *dt_temp_zones; +static struct mmi_chrg_dts_info *chrg_name_list; + +#define MIN_TEMP_C -20 +#define MAX_TEMP_C 60 +#define HYSTEREISIS_DEGC 2 +void mmi_chrg_rate_check(struct mmi_charger_manager *chg); +bool mmi_find_temp_zone(struct mmi_charger_manager *chip, int temp_c, bool ignore_hysteresis_degc) +{ + int prev_zone, num_zones; + struct mmi_chrg_temp_zone *zones; + int hotter_t = 0, hotter_fcc = 0; + int colder_t = 0, colder_fcc = 0; + int i; + int max_temp; + + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return false; + } + + zones = chip->temp_zones; + num_zones = chip->num_temp_zones; + prev_zone = chip->pres_temp_zone; + + mmi_chrg_info(chip, "prev zone %d, temp_c %d\n",prev_zone, temp_c); + max_temp = zones[num_zones - 1].temp_c; + + if (prev_zone == ZONE_NONE) { + for (i = num_zones - 1; i >= 0; i--) { + if (temp_c >= zones[i].temp_c) { + if (i == num_zones - 1) + chip->pres_temp_zone = ZONE_HOT; + else + chip->pres_temp_zone = i + 1; + return true; + } + } + chip->pres_temp_zone = ZONE_COLD; + return true; + } + + if (prev_zone == ZONE_COLD) { + if (temp_c >= MIN_TEMP_C + HYSTEREISIS_DEGC) + chip->pres_temp_zone = ZONE_FIRST; + } else if (prev_zone == ZONE_HOT) { + if (temp_c <= max_temp - HYSTEREISIS_DEGC) + chip->pres_temp_zone = num_zones - 1; + } else { + if (prev_zone == ZONE_FIRST) { + hotter_t = zones[prev_zone].temp_c; + colder_t = MIN_TEMP_C; + hotter_fcc = zones[prev_zone + 1].chrg_step_power->chrg_step_curr; + colder_fcc = 0; + } else if (prev_zone == num_zones - 1) { + hotter_t = zones[prev_zone].temp_c; + colder_t = zones[prev_zone - 1].temp_c; + hotter_fcc = 0; + colder_fcc = zones[prev_zone - 1].chrg_step_power->chrg_step_curr; + } else { + hotter_t = zones[prev_zone].temp_c; + colder_t = zones[prev_zone - 1].temp_c; + hotter_fcc = zones[prev_zone + 1].chrg_step_power->chrg_step_curr; + colder_fcc = zones[prev_zone - 1].chrg_step_power->chrg_step_curr; + } + + if (!ignore_hysteresis_degc) { + if (zones[prev_zone].chrg_step_power->chrg_step_curr < hotter_fcc) + hotter_t += HYSTEREISIS_DEGC; + if (zones[prev_zone].chrg_step_power->chrg_step_curr < colder_fcc) + colder_t -= HYSTEREISIS_DEGC; + } + + if (temp_c <= MIN_TEMP_C) + chip->pres_temp_zone = ZONE_COLD; + else if (temp_c >= max_temp) + chip->pres_temp_zone = ZONE_HOT; + else if (temp_c >= hotter_t) + chip->pres_temp_zone++; + else if (temp_c < colder_t) + chip->pres_temp_zone--; + } + + mmi_chrg_info(chip, "batt temp_c %d, prev zone %d, pres zone %d, " + "hotter_fcc %dmA, colder_fcc %dmA, " + "hotter_t %dC, colder_t %dC\n", + temp_c,prev_zone, chip->pres_temp_zone, + hotter_fcc, colder_fcc, hotter_t, colder_t); + + if (prev_zone != chip->pres_temp_zone) { + mmi_chrg_info(chip, + "Entered Temp Zone %d!\n", + chip->pres_temp_zone); + return true; + } + return false; +} + +bool mmi_find_chrg_step(struct mmi_charger_manager *chip, int temp_zone, int vbatt_volt) +{ + int batt_volt, i; + bool find_step = false; + struct mmi_chrg_temp_zone zone; + struct mmi_chrg_step_power *chrg_steps; + struct mmi_chrg_step_info chrg_step_inline; + struct mmi_chrg_step_info prev_step; + + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return false; + } + + if (chip->pres_temp_zone == ZONE_HOT || + chip->pres_temp_zone == ZONE_COLD || + chip->pres_temp_zone < ZONE_FIRST) { + mmi_chrg_err(chip, "pres temp zone is HOT or COLD, " + "can't find chrg step\n"); + return false; + } + + zone = chip->temp_zones[chip->pres_temp_zone]; + chrg_steps = zone.chrg_step_power; + prev_step = chip->chrg_step; + + batt_volt = vbatt_volt; + chrg_step_inline.temp_c = zone.temp_c; + + mmi_chrg_info(chip, "batt_volt %d, chrg step %d, step nums %d\n", + batt_volt, prev_step.pres_chrg_step, + chip->chrg_step_nums); + + /*In the first search cycle, find out the vbatt is less than step volt*/ + + for (i = 0; i < chip->chrg_step_nums; i++) { + mmi_chrg_info(chip, + "first cycle,i %d, step volt %d, batt_volt %d\n", + i, chrg_steps[i].chrg_step_volt, batt_volt); + if (chrg_steps[i].chrg_step_volt > 0 + && batt_volt < chrg_steps[i].chrg_step_volt) { + if ( (i + 1) < chip->chrg_step_nums + && chrg_steps[i + 1].chrg_step_volt > 0) { + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i + 1].chrg_step_curr; + } else + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cc_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cv_volt = + chrg_steps[i].chrg_step_volt; + chrg_step_inline.pres_chrg_step = i; + find_step = true; + mmi_chrg_info(chip, "find chrg step\n"); + break; + } + } + + if (find_step) { + mmi_chrg_info(chip, "chrg step %d, " + "step cc curr %d, step cv volt %d, " + "step cv tapper curr %d\n", + chrg_step_inline.pres_chrg_step, + chrg_step_inline.chrg_step_cc_curr, + chrg_step_inline.chrg_step_cv_volt, + chrg_step_inline.chrg_step_cv_tapper_curr); + chip->chrg_step = chrg_step_inline; + } else { + + /*If can't find out any chrg step in the first search cycle,*/ + /*it means that vbatt is already greater than all chrg step volt, */ + /*therefore, start to enter second search cycle, */ + /*to find out the maximal chrg step volt*/ + for (i = 0; i < chip->chrg_step_nums; i++) { + mmi_chrg_info(chip, + "second cycle, i %d, step volt %d, batt_volt %d\n", + i, chrg_steps[i].chrg_step_volt, batt_volt); + if (chrg_steps[i].chrg_step_volt > 0 + && batt_volt > chrg_steps[i].chrg_step_volt) { + if ( (i + 1) < chip->chrg_step_nums + && chrg_steps[i + 1].chrg_step_volt > 0) { + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i + 1].chrg_step_curr; + } else + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cc_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cv_volt = + chrg_steps[i].chrg_step_volt; + chrg_step_inline.pres_chrg_step = i; + find_step = true; + mmi_chrg_info(chip, "find chrg step\n"); + } + } + + if (find_step) { + mmi_chrg_info(chip, "chrg step %d, " + "step cc curr %d, step cv volt %d, " + "step cv tapper curr %d\n", + chrg_step_inline.pres_chrg_step, + chrg_step_inline.chrg_step_cc_curr, + chrg_step_inline.chrg_step_cv_volt, + chrg_step_inline.chrg_step_cv_tapper_curr); + chip->chrg_step = chrg_step_inline; + } + } + + + if (find_step) { + if (chip->chrg_step.chrg_step_cc_curr == + chip->chrg_step.chrg_step_cv_tapper_curr) + chip->chrg_step.last_step = true; + else + chip->chrg_step.last_step = false; + + mmi_chrg_info(chip,"Temp zone %d, " + "select chrg step %d, step cc curr %d," + "step cv volt %d, step cv tapper curr %d, " + "is the last chrg step %d\n", + chip->pres_temp_zone, + chip->chrg_step.pres_chrg_step, + chip->chrg_step.chrg_step_cc_curr, + chip->chrg_step.chrg_step_cv_volt, + chip->chrg_step.chrg_step_cv_tapper_curr, + chip->chrg_step.last_step); + + if (prev_step.pres_chrg_step != chip->chrg_step.pres_chrg_step) { + mmi_chrg_info(chip, "Find the next chrg step\n"); + return true; + } + } + return false; +} + +void mmi_set_pps_result_history(struct mmi_charger_manager *chip, int pps_result) +{ + if (chip->pps_result_history_idx >= PPS_RET_HISTORY_SIZE -1) + chip->pps_result_history_idx = 0; + + if (pps_result < 0) + chip->pps_result_history[chip->pps_result_history_idx] = 1; + else + chip->pps_result_history[chip->pps_result_history_idx] = 0; + + chip->pps_result_history_idx++; +} + +bool mmi_get_pps_result_history(struct mmi_charger_manager *chip) +{ + int i = 0; + int result = 0; + for (i = 0; i < PPS_RET_HISTORY_SIZE; i++) + result += chip->pps_result_history[i]; + + if (result >= PPS_RET_HISTORY_SIZE) + return RESET_POWER; + else if (result >= PPS_RET_HISTORY_SIZE / 2) + return BLANCE_POWER; + else + return NO_ERROR; +} + +void mmi_clear_pps_result_history(struct mmi_charger_manager *chip) +{ + int i = 0;; + for (i = 0; i < PPS_RET_HISTORY_SIZE; i++) + chip->pps_result_history[i] = 0; +} + +int mmi_calculate_delta_volt(int pps_voltage, int pps_current, int delta_curr) +{ + u64 power; + int delta_volt; + u64 pps_volt; + u64 pps_curr; + + pps_volt = pps_voltage; + pps_curr = pps_current; + power = pps_volt * pps_curr; + + delta_volt = (int)(power / (pps_curr - delta_curr) - pps_volt); + delta_volt -= delta_volt % 20000; + + if (delta_volt > 0) + return delta_volt; + else + return 0; +} + +void mmi_update_all_charger_status(struct mmi_charger_manager *chip) +{ + int chrg_num, i; + struct mmi_charger_device *chrg_dev; + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return; + } + + chrg_num = chip->mmi_chrg_dev_num; + for (i = 0; i < chrg_num; i++) { + chrg_dev = chip->chrg_list[i]; + mmi_update_charger_status(chrg_dev); + } + return; +} + +void mmi_update_all_charger_error(struct mmi_charger_manager *chip) +{ + int chrg_num, i; + struct mmi_charger_device *chrg_dev; + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return; + } + + chrg_num = chip->mmi_chrg_dev_num; + for (i = 0; i < chrg_num; i++) { + chrg_dev = chip->chrg_list[i]; + mmi_update_charger_error(chrg_dev); + } + return; +} + +void mmi_dump_charger_error(struct mmi_charger_manager *chip, + struct mmi_charger_device *chrg_dev) +{ + if (!chip || !chrg_dev) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return; + } + mmi_chrg_dbg(chip, PR_MOTO, "%s: charger error type %d, " + "bus ucp err cnt %d, bus ocp err cnt %d\n", + chrg_dev->name, + chrg_dev->charger_error.chrg_err_type, + chrg_dev->charger_error.bus_ucp_err_cnt, + chrg_dev->charger_error.bus_ocp_err_cnt); + return; +} + +static enum power_supply_property mmi_chrg_mgr_props[] = { + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX +}; + +static int mmi_chrg_mgr_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mmi_charger_manager *chip = power_supply_get_drvdata(psy); + union power_supply_propval prop = {0,}; + int rc, i, chrg_cnt = 0; + + if (!chip->batt_psy) { + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) + return -ENODEV; + } + + switch (psp) { + case POWER_SUPPLY_PROP_CURRENT_NOW: + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + val->intval = prop.intval; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + val->intval = prop.intval; + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + if (chip->mmi_chrg_dev_num) { + for (i = 0; i < chip->mmi_chrg_dev_num; i++) { + if (chip->chrg_list[i]->charger_enabled) + chrg_cnt++; + } + val->intval = chrg_cnt; + } else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + val->intval = chip->system_thermal_level; + break; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: + val->intval = chip->thermal_levels; + break; + default: + return -EINVAL; + + } + return 0; +} + +static int mmi_chrg_mgr_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct mmi_charger_manager *chip = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + if (val->intval < 0) + return -EINVAL; + + if (chip->thermal_levels <= 0) + return -EINVAL; + + if (val->intval >= chip->thermal_levels) + chip->system_thermal_level = + chip->thermal_levels - 1; + else + chip->system_thermal_level = val->intval; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mmi_chrg_mgr_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + int ret; + + switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + ret = 1; + break; + default: + ret = 0; + break; + } + return ret; +} + +static const struct power_supply_desc mmi_chrg_mgr_psy_desc = { + .name = "mmi_chrg_manager", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = mmi_chrg_mgr_props, + .num_properties = ARRAY_SIZE(mmi_chrg_mgr_props), + .get_property = mmi_chrg_mgr_get_property, + .set_property = mmi_chrg_mgr_set_property, + .property_is_writeable = mmi_chrg_mgr_is_writeable, +}; + +static int mmi_chrg_mgr_psy_register(struct mmi_charger_manager *chip) +{ + struct power_supply_config mmi_chrg_mgr_cfg = {}; + + mmi_chrg_mgr_cfg.drv_data = chip; + mmi_chrg_mgr_cfg.of_node = chip->dev->of_node; + chip->mmi_chrg_mgr_psy = power_supply_register(chip->dev, + &mmi_chrg_mgr_psy_desc, + &mmi_chrg_mgr_cfg); + if (IS_ERR(chip->mmi_chrg_mgr_psy)) { + pr_err("Couldn't register mmi_chrg_mgr_psy power supply\n"); + return PTR_ERR(chip->mmi_chrg_mgr_psy); + } + + pr_info("power supply register mmi_chrg_mgr_psy successfully\n"); + return 0; +} + +static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) +{ + struct mmi_charger_manager *chip = + container_of(nb, struct mmi_charger_manager, psy_nb); + + if (!chip->usb_psy) { + mmi_chrg_err(chip, "usb psy is NULL, direct return\n"); + return NOTIFY_OK; + } + + if (ptr == chip->usb_psy && evt == PSY_EVENT_PROP_CHANGED) { + schedule_work(&chip->psy_changed_work); + + cancel_delayed_work(&chip->heartbeat_work); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(1000)); + + } + + return NOTIFY_OK; +} + +#define REV_CP_THRESH_UV 4400000 +#define CP_IBUS_THRESH_MA 20 +#define IBAT_THRESH_UA 10000 +#define REV_CP_DOUBLE_THRESH_UV 100000 +bool mmi_is_cable_plugout(struct mmi_charger_manager *chip) +{ + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + int vbus_volt, ibatt_curr, ibus_curr, vbatt_volt; + bool cp_chrg_enable, cp_sw_en; + + if (chip->extrn_sense) { + ibatt_curr = chrg_list->chrg_dev[CP_MASTER]->charger_data.ibatt_curr; + ibatt_curr *= 1000; + vbatt_volt = chrg_list->chrg_dev[CP_MASTER]->charger_data.vbatt_volt; + vbatt_volt *= 1000; + } else { + ibatt_curr= chrg_list->chrg_dev[PMIC_SW]->charger_data.ibatt_curr; + vbatt_volt = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbatt_volt; + } + vbus_volt = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbus_volt; + ibus_curr = chrg_list->chrg_dev[CP_MASTER]->charger_data.ibus_curr; + cp_sw_en = chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT); + cp_chrg_enable = chrg_list->chrg_dev[CP_MASTER]->charger_enabled; + mmi_chrg_info(chip, "ibat:%d, vbat:%d, vbus:%d, ibus:%d, cp_chg_en:%d, cp_sw_en:%d\n", + ibatt_curr, vbatt_volt, vbus_volt, ibus_curr, cp_chrg_enable, cp_sw_en); + + if (!cp_chrg_enable ||ibus_curr > CP_IBUS_THRESH_MA || ibatt_curr > IBAT_THRESH_UA) + return false; + + if (vbatt_volt >= vbus_volt + && vbus_volt < REV_CP_THRESH_UV) { + mmi_chrg_info(chip, "cable plug out: cp reverse\n"); + return true; + } + + if (cp_sw_en && vbus_volt < 2 * vbatt_volt - REV_CP_DOUBLE_THRESH_UV) { + mmi_chrg_info(chip, "cable plug out: cp double reverse\n"); + return true; + } + + return false; +} + +static bool mmi_factory_check(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool factory = false; + + if (np) + factory = of_property_read_bool(np, "mmi,factory-cable"); + + of_node_put(np); + + return factory; +} + + +static void kick_sm(struct mmi_charger_manager *chip, int ms) +{ + int ret; + + if (!chip->sm_work_running) { + mmi_chrg_dbg(chip, PR_INTERRUPT, + "launch mmi chrg sm work\n"); + mmi_chrg_policy_clear(chip); + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(ms)); + chip->sm_work_running = true; + ret = mmi_charger_write_iio_chan(chip, MMI_CP_ENABLE_STATUS, true); + if (ret) + mmi_chrg_err(chip, "Unable to write CP enable status: %d\n", ret); + } else + mmi_chrg_dbg(chip, PR_INTERRUPT, + "mmi chrg sm work already existed\n"); +} + +static void cancel_sm(struct mmi_charger_manager *chip) +{ + int ret; + + cancel_delayed_work_sync(&chip->mmi_chrg_sm_work); + flush_delayed_work(&chip->mmi_chrg_sm_work); + mmi_chrg_policy_clear(chip); + chip->sm_work_running = false; + ret = mmi_charger_write_iio_chan(chip, MMI_CP_ENABLE_STATUS, false); + if (ret) + mmi_chrg_err(chip, "Unable to write CP disable status: %d\n", ret); + chip->pd_volt_max = pd_volt_max_init; + chip->pd_curr_max = pd_curr_max_init; + mmi_chrg_dbg(chip, PR_INTERRUPT, + "cancel sync and flush mmi chrg sm work\n"); +} + +void chrg_policy_error_clear(struct mmi_charger_manager *chip, + struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num = 0, i = 0; + struct mmi_charger_device *chrg_dev; + chrg_num = chip->mmi_chrg_dev_num; + + for (i = 0; i < chrg_num; i++) { + switch (i) { + case PMIC_SW: + break; + + case CP_MASTER: + if (is_charger_exist(dev_ops[CP_MASTER].dev_name)) { + chrg_dev = chrg_list->chrg_dev[CP_MASTER]; + mmi_clear_charger_error(chrg_dev); + } + break; + + case CP_SLAVE: + break; + + default: + mmi_chrg_err(chip,"mmi_chrg_dev not found %d !\n",i); + break; + } + } + + return; +} + +void clear_chrg_dev_error_cnt(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num, i; + struct mmi_charger_device *chrg_dev; + chrg_num = chip->mmi_chrg_dev_num; + + for (i = 0; i < chrg_num; i++) { + if (is_charger_exist(dev_ops[i].dev_name)) { + chrg_dev = chrg_list->chrg_dev[i]; + chrg_dev->charger_error.chrg_err_type = 0; + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + chrg_dev->charger_error.bus_ocp_err_cnt = 0; + } + } + return; +} + +static void kick_qc3p_sm(struct mmi_charger_manager *chip, int ms) +{ + + + if (!chip->qc3p_sm_work_running) { + mmi_chrg_dbg(chip, PR_INTERRUPT, + "launch mmi qc3p chrg sm work\n"); + mmi_qc3p_chrg_policy_clear(chip); //todo + schedule_delayed_work(&chip->mmi_qc3p_chrg_sm_work, // + msecs_to_jiffies(ms)); //todo + chip->qc3p_sm_work_running = true; + } else + mmi_chrg_dbg(chip, PR_INTERRUPT, + "mmi chrg qc3p sm work already existed\n"); +} + +static void cancel_qc3p_sm(struct mmi_charger_manager *chip) +{ + cancel_delayed_work_sync(&chip->mmi_qc3p_chrg_sm_work); + flush_delayed_work(&chip->mmi_qc3p_chrg_sm_work); + mmi_qc3p_chrg_policy_clear(chip); + chip->qc3p_sm_work_running = false; + chip->qc3p_volt_max = qc3p_volt_max_init; + mmi_chrg_dbg(chip, PR_INTERRUPT, + "cancel sync and flush mmi chrg qc3p sm work\n"); +} + +static void mmi_awake_vote(struct mmi_charger_manager *chip, bool awake) +{ + if (awake == chip->awake) + return; + + chip->awake = awake; + if (awake) + pm_stay_awake(chip->dev); + else + pm_relax(chip->dev); +} + +#define HEARTBEAT_DELAY_MS 60000 +#define SMBCHG_HEARTBEAT_INTRVAL_NS 70000000000 +#define CHG_SHOW_MAX_SIZE 50 +static void mmi_heartbeat_work(struct work_struct *work) +{ + struct mmi_charger_manager *chip = container_of(work, + struct mmi_charger_manager, heartbeat_work.work); + int hb_resch_time = 0, ret = 0, i = 0; + union power_supply_propval val; + bool pd_active; + + mmi_chrg_info(chip, "MMI: Heartbeat!\n"); + /* Have not been resumed so wait another 100 ms */ + if (chip->suspended) { + mmi_chrg_err(chip, "SMBMMI: HB running before Resume\n"); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(100)); + return; + } + + mmi_awake_vote(chip, true); + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) { + mmi_chrg_err(chip, + "Could not get USB power_supply, deferring probe\n"); + return; + } + } + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &val); + if (ret) { + mmi_chrg_err(chip, "Unable to read USB PRESENT: %d\n", ret); + return; + } + chip->vbus_present = val.intval; + + hb_resch_time = HEARTBEAT_DELAY_MS; + + if (!chip->vbus_present) + mmi_awake_vote(chip, false); + + ret = mmi_charger_read_iio_chan(chip, SMB5_USB_PD_ACTIVE, &val.intval); + if (ret) { + mmi_chrg_err(chip, "Unable to read PD ACTIVE: %d\n", ret); + goto schedule_work; + } + pd_active = val.intval; + + if (!chip->pd_handle) { + chip->pd_handle = devm_usbpd_get_by_phandle(chip->dev, + "qcom,usbpd-phandle"); + if (IS_ERR_OR_NULL(chip->pd_handle)) { + mmi_chrg_err(chip, "Error getting the pd phandle %ld\n", + PTR_ERR(chip->pd_handle)); + chip->pd_handle = NULL; + goto schedule_work; + } + } + + if (!chip->sm_work_running && chip->vbus_present + && pd_active) { + usbpd_get_pdo_info(chip->pd_handle, chip->mmi_pdo_info,PD_MAX_PDO_NUM); + mmi_chrg_info(chip, "check all effective pdo info\n"); + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + if ((chip->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_AUGMENTED) + && chip->mmi_pdo_info[i].uv_max >= PUMP_CHARGER_PPS_MIN_VOLT + && chip->mmi_pdo_info[i].ua >= chip->typec_middle_current) { + chip->mmi_pd_pdo_idx = chip->mmi_pdo_info[i].pdo_pos; + mmi_chrg_info(chip, + "pd charger support pps, pdo %d, " + "volt %d, curr %d \n", + chip->mmi_pd_pdo_idx, + chip->mmi_pdo_info[i].uv_max, + chip->mmi_pdo_info[i].ua); + chip->pd_pps_support = true; + + if (chip->mmi_pdo_info[i].uv_max < + chip->pd_volt_max) { + chip->pd_volt_max = + chip->mmi_pdo_info[i].uv_max; + } + if (chip->mmi_pdo_info[i].ua < + chip->pd_curr_max) { + chip->pd_curr_max = + chip->mmi_pdo_info[i].ua; + } + break; + } + } + } + if (!chip->qc3p_sm_work_running && chip->vbus_present) + chip->qc3p_active = mmi_qc3p_power_active(chip); + + if (chip->pd_pps_support + && !chip->factory_mode) { + mmi_chrg_info(chip, "MMI: Heartbeat!, launch sm work\n"); + kick_sm(chip, 100); + } else if(chip->qc3p_active + && !chip->factory_mode) { + mmi_chrg_info(chip, "MMI: Heartbeat!, launch qc3p sm work\n"); + kick_qc3p_sm(chip, 100); + } + +schedule_work: + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(hb_resch_time)); +} + +static void psy_changed_work_func(struct work_struct *work) +{ + struct mmi_charger_manager *chip = container_of(work, + struct mmi_charger_manager, psy_changed_work); + union power_supply_propval val; + bool pd_active; + int ret, i; + + mmi_chrg_info(chip, "kick psy changed work.\n"); + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) { + mmi_chrg_err(chip, + "Could not get USB power_supply, deferring probe\n"); + return; + } + } + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &val); + if (ret) { + mmi_chrg_err(chip, "Unable to read USB PRESENT: %d\n", ret); + return; + } + chip->vbus_present = val.intval; + + ret = mmi_charger_read_iio_chan(chip, SMB5_USB_PD_ACTIVE, &val.intval); + if (ret) { + mmi_chrg_err(chip, "Unable to read PD ACTIVE: %d\n", ret); + return; + } + pd_active = val.intval; + + if (!chip->pd_handle) { + chip->pd_handle = devm_usbpd_get_by_phandle(chip->dev, + "qcom,usbpd-phandle"); + if (IS_ERR_OR_NULL(chip->pd_handle)) { + mmi_chrg_err(chip, "Error getting the pd phandle %ld\n", + PTR_ERR(chip->pd_handle)); + chip->pd_handle = NULL; + return; + } + } + + if (pd_active && chip->vbus_present) { + usbpd_get_pdo_info(chip->pd_handle, chip->mmi_pdo_info,PD_MAX_PDO_NUM); + mmi_chrg_info(chip, "check all effective pdo info\n"); + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + if ((chip->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_AUGMENTED) + && chip->mmi_pdo_info[i].uv_max >= PUMP_CHARGER_PPS_MIN_VOLT + && chip->mmi_pdo_info[i].ua >= chip->typec_middle_current) { + chip->mmi_pd_pdo_idx = chip->mmi_pdo_info[i].pdo_pos; + mmi_chrg_info(chip, + "pd charger support pps, pdo %d, " + "volt %d, curr %d \n", + chip->mmi_pd_pdo_idx, + chip->mmi_pdo_info[i].uv_max, + chip->mmi_pdo_info[i].ua); + chip->pd_pps_support = true; + + if (chip->mmi_pdo_info[i].uv_max < + chip->pd_volt_max) { + chip->pd_volt_max = + chip->mmi_pdo_info[i].uv_max; + } + + if (chip->mmi_pdo_info[i].ua < + chip->pd_curr_max) { + chip->pd_curr_max = + chip->mmi_pdo_info[i].ua; + } + + break; + } + } + } + + mmi_chrg_info(chip, "vbus present %d, pd pps support %d, " + "pps max voltage %d, pps max curr %d\n", + chip->vbus_present, + chip->pd_pps_support, + chip->pd_volt_max, + chip->pd_curr_max); + if (chip->vbus_present) + chip->qc3p_active = mmi_qc3p_power_active(chip); + + ret = mmi_charger_write_iio_chan(chip, CP_STATUS1, chip->vbus_present); + if (ret) + mmi_chrg_err(chip, "Unable to enable CP ADC: %d\n", ret); + + if (chip->vbus_present + && chip->pd_pps_support + && !chip->factory_mode) { + kick_sm(chip, 100); + } else if (chip->vbus_present + && chip->qc3p_active + && !chip->factory_mode) { + kick_qc3p_sm(chip, 100); + } else { + cancel_sm(chip); + cancel_qc3p_sm(chip); + chip->pd_pps_support = false; + chip->qc3p_active = false; + } + + return; +} +#define DEFAULT_IBUS_MAX_MA 3000 +#define DEFAULT_QC3P_VOLT_STEPS 20000 +#define DEFAULT_QC3P_VOLT_MAX 11000000 +#define DEFAULT_BATT_OVP_LMT 4475000 +#define DEFAULT_PL_CHRG_VBATT_MIN 3600000 +#define DEFAULT_PPS_VOLT_STEPS 20000 +#define DEFAULT_PPS_CURR_STEPS 50000 +#define DEFAULT_PPS_VOLT_MAX 11000000 +#define DEFAULT_PPS_CURR_MAX 4000000 +#define MMI_TEMP_ZONES 5 +static int mmi_chrg_manager_parse_dt(struct mmi_charger_manager *chip) +{ + struct device_node *node = chip->dev->of_node, *child; + int rc, byte_len, i, j, chrg_idx = 0, step_cnt = 0, idx = 0; + const int *ptr; + + rc = of_property_read_u32(node, + "mmi,batt-ovp-lmt", + &chip->batt_ovp_lmt); + if (rc < 0) + chip->batt_ovp_lmt = + DEFAULT_BATT_OVP_LMT; + + rc = of_property_read_u32(node, + "mmi,pl-chrg-vbatt-min", + &chip->pl_chrg_vbatt_min); + if (rc < 0) + chip->pl_chrg_vbatt_min = + DEFAULT_PL_CHRG_VBATT_MIN; + + chip->extrn_fg = of_property_read_bool(node, + "mmi,extrn-fg"); + + if (chip->extrn_fg) { + byte_len = of_property_count_strings(node, "mmi,extrn-fg-name"); + if (byte_len <= 0) { + mmi_chrg_err(chip, "Cannot parse mmi, extrn-fg: %d\n", byte_len); + return byte_len; + } + + for (i = 0; i < byte_len; i++) { + rc = of_property_read_string_index(node, "mmi,extrn-fg-name", + i, &chip->extrn_fg_name); + if (rc < 0) { + mmi_chrg_err(chip, "Cannot parse extrn-fg-name\n"); + return rc; + } + } + } + + chip->extrn_sense = of_property_read_bool(node, + "mmi,extrn-sense"); + + chip->dont_rerun_aicl= of_property_read_bool(node, + "mmi,dont-rerun-aicl"); + + rc = of_property_read_u32(node, + "mmi,typec-middle-current", + &chip->typec_middle_current); + if (rc < 0) + chip->typec_middle_current = + TYPEC_MIDDLE_CURRENT_UA; + + rc = of_property_read_u32(node, + "mmi,pd-allow-min-current", + &chip->pd_allow_min_current); + if (rc < 0) + chip->pd_allow_min_current = + PD_ALLOW_MIN_CURRENT_UA; + + rc = of_property_read_u32(node, + "mmi,step-first-current-comp", + &chip->step_first_curr_comp); + if (rc < 0) + chip->step_first_curr_comp = + STEP_FIREST_CURR_COMP; + + rc = of_property_read_u32(node, + "mmi,pps-volt-steps", + &chip->pps_volt_steps); + if (rc < 0) + chip->pps_volt_steps = + DEFAULT_PPS_VOLT_STEPS; + + rc = of_property_read_u32(node, + "mmi,pps-curr-steps", + &chip->pps_curr_steps); + if (rc < 0) + chip->pps_curr_steps = + DEFAULT_PPS_CURR_STEPS; + + rc = of_property_read_u32(node, + "mmi,pd-volt-max", + &chip->pd_volt_max); + if (rc < 0) + chip->pd_volt_max = + DEFAULT_PPS_VOLT_MAX; + pd_volt_max_init = chip->pd_volt_max; + rc = of_property_read_u32(node, + "mmi,pd-curr-max", + &chip->pd_curr_max); + if (rc < 0) + chip->pd_curr_max = + DEFAULT_PPS_CURR_MAX; + pd_curr_max_init = chip->pd_curr_max; + + rc = of_property_read_u32(node, + "mmi,qc3p-max-ibus-ma", + &chip->qc3p_max_ibus_ma); + if (rc < 0) + chip->qc3p_max_ibus_ma = + DEFAULT_IBUS_MAX_MA; + rc = of_property_read_u32(node, + "mmi,qc3p-volt-steps", + &chip->qc3p_volt_steps); + if (rc < 0) + chip->qc3p_volt_steps = + DEFAULT_QC3P_VOLT_STEPS; + + rc = of_property_read_u32(node, + "mmi,qc3p-volt-max", + &chip->qc3p_volt_max); + if (rc < 0) + chip->qc3p_volt_max = + DEFAULT_QC3P_VOLT_MAX; + qc3p_volt_max_init = chip->qc3p_volt_max; + + for_each_child_of_node(node, child) + chip->mmi_chrg_dev_num++; + + if (!chip->mmi_chrg_dev_num) { + mmi_chrg_err(chip,"No mmi_chrg_dev list !\n"); + return -ENODEV; + } + + chrg_name_list = (struct mmi_chrg_dts_info *)devm_kzalloc(chip->dev, + sizeof(struct mmi_chrg_dts_info) * + chip->mmi_chrg_dev_num, GFP_KERNEL); + if (!chrg_name_list) { + mmi_chrg_err(chip,"No memory for mmi charger device name list !\n"); + goto cleanup; + } + + chip->chrg_list = (struct mmi_charger_device **)devm_kzalloc(chip->dev, + sizeof(struct mmi_charger_device *) * + chip->mmi_chrg_dev_num, GFP_KERNEL); + if (!chip->chrg_list) { + mmi_chrg_err(chip,"No memory for mmi charger device list !\n"); + goto cleanup; + } + + for_each_child_of_node(node, child) { + byte_len = of_property_count_strings(child, "chrg-name"); + if (byte_len <= 0) { + mmi_chrg_err(chip, "Cannot parse chrg-name: %d\n", byte_len); + goto cleanup; + } + + for (i = 0; i < byte_len; i++) { + rc = of_property_read_string_index(child, "chrg-name", + i, &chrg_name_list[chrg_idx].chrg_name); + if (rc < 0) { + mmi_chrg_err(chip, "Cannot parse chrg-name\n"); + goto cleanup; + } + } + + byte_len = of_property_count_strings(child, "psy-name"); + if (byte_len <= 0) { + mmi_chrg_err(chip, "Cannot parse psy-name: %d\n", byte_len); + goto cleanup; + } + + for (i = 0; i < byte_len; i++) { + rc = of_property_read_string_index(child, "psy-name", + i, &chrg_name_list[chrg_idx].psy_name); + if (rc < 0) { + mmi_chrg_err(chip, "Cannot parse psy-name\n"); + goto cleanup; + } + } + + mmi_chrg_info(chip, "mmi,chrg-name: %s, psy-name: %s\n", + chrg_name_list[chrg_idx].chrg_name, + chrg_name_list[chrg_idx].psy_name); + + of_property_read_u32(child, "charging-curr-limited", + &chrg_name_list[chrg_idx].charging_curr_limited); + + of_property_read_u32(child, "charging-curr-min", + &chrg_name_list[chrg_idx].charging_curr_min); + + chrg_idx++; + } + + rc = of_property_read_u32(node, + "mmi,chrg-temp-zones-num", + &chip->num_temp_zones); + if (rc < 0) + chip->num_temp_zones = + MMI_TEMP_ZONES; + + chip->temp_zones = (struct mmi_chrg_temp_zone *) + devm_kzalloc(chip->dev, + sizeof(struct mmi_chrg_temp_zone ) + * chip->num_temp_zones, + GFP_KERNEL); + if (chip->temp_zones == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + if (of_find_property(node, "mmi,mmi-chrg-temp-zones", &byte_len)) { + if (byte_len <= 0) { + mmi_chrg_err(chip, + "Cannot parse mmi-chrg-temp-zones %d\n",byte_len); + goto cleanup; + } + + step_cnt = (byte_len / chip->num_temp_zones - sizeof(u32)) + / (sizeof(u32) * 2); + mmi_chrg_info(chip, "mmi chrg step number is %d\n", step_cnt); + + dt_temp_zones = (u32 *) devm_kzalloc(chip->dev, byte_len, GFP_KERNEL); + if (dt_temp_zones == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + rc = of_property_read_u32_array(node, + "mmi,mmi-chrg-temp-zones", + (u32 *)dt_temp_zones, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_chrg_err(chip, + "Couldn't read mmi chrg temp zones rc = %d\n", rc); + goto zones_failed; + } + + mmi_chrg_err(chip, "temp zone nums: %d , byte_len %d, " + "num %d, row size %d\n", + chip->num_temp_zones, byte_len, + (int)(byte_len / sizeof(u32)), + (int)(sizeof(struct mmi_chrg_step_power) * step_cnt)); + ptr = dt_temp_zones; + + for (i = 0; i < byte_len / sizeof(u32); i++) { + if (!(i % 9)) { + printk("\n"); + } + printk("%u,", ptr[i]); + } + printk("\n"); + + for (i = 0; i < chip->num_temp_zones; i++) { + idx = (int)((sizeof(u32) + + sizeof(struct mmi_chrg_step_power) * step_cnt ) + * i / sizeof(u32)); + chip->temp_zones[i].temp_c = dt_temp_zones[idx]; + } + + for (i = 0; i < chip->num_temp_zones; i++) { + idx = (int)(((sizeof(u32) + + sizeof(struct mmi_chrg_step_power) * step_cnt ) + * i + sizeof(u32)) / sizeof(u32)); + chip->temp_zones[i].chrg_step_power = + (struct mmi_chrg_step_power *)&dt_temp_zones[idx]; + } + + for (i = 0; i < chip->num_temp_zones; i++) { + + printk( "mmi temp zones: Zone: %d, Temp: %d C " , i, + chip->temp_zones[i].temp_c); + for (j = 0; j < step_cnt; j++) { + chip->temp_zones[i].chrg_step_power[j].chrg_step_curr *= 1000; + chip->temp_zones[i].chrg_step_power[j].chrg_step_volt *= 1000; + + printk("step_volt %dmV, step_curr %dmA, ", + chip->temp_zones[i].chrg_step_power[j].chrg_step_volt, + chip->temp_zones[i].chrg_step_power[j].chrg_step_curr); + } + printk("\n"); + } + + chip->pres_temp_zone = ZONE_NONE; + chip->chrg_step_nums = step_cnt; + } + + if (of_find_property(node, "mmi,thermal-mitigation", &byte_len)) { + chip->thermal_mitigation = devm_kzalloc(chip->dev, byte_len, + GFP_KERNEL); + + if (chip->thermal_mitigation == NULL) { + rc = -ENOMEM; + goto zones_failed; + } + chip->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "mmi,thermal-mitigation", + chip->thermal_mitigation, + chip->thermal_levels); + if (rc < 0) { + mmi_chrg_err(chip, + "Couldn't parse thermal-mitigation rc = %d\n", rc); + goto thermal_failed; + } + } + + return rc; +thermal_failed: + devm_kfree(chip->dev,chip->thermal_mitigation); +zones_failed: + devm_kfree(chip->dev,chip->temp_zones); +cleanup: + chip->mmi_chrg_dev_num = 0; + devm_kfree(chip->dev,chip->chrg_list); + return rc; +} + +void chrg_dev_init(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num, i; + chrg_num = chip->mmi_chrg_dev_num; + mmi_chrg_err(chip,"runing in chrg dev init!\n"); + + for (i = 0; i < chrg_num; i++) { + + switch (i) { + case PMIC_SW: + if (is_charger_exist(dev_ops[PMIC_SW].dev_name)) { + chrg_list->pmic_sw = true; + chrg_list->chrg_dev[PMIC_SW] = chip->chrg_list[PMIC_SW]; + } + break; + case CP_MASTER: + if (is_charger_exist(dev_ops[CP_MASTER].dev_name)) { + chrg_list->cp_master = true; + chrg_list->chrg_dev[CP_MASTER] = chip->chrg_list[CP_MASTER]; + } + break; + case CP_SLAVE: + if (is_charger_exist(dev_ops[CP_SLAVE].dev_name)) { + chrg_list->cp_slave = true; + chrg_list->cp_clave_later = false; + chrg_list->chrg_dev[CP_SLAVE] = chip->chrg_list[CP_SLAVE]; + } + break; + default: + mmi_chrg_err(chip,"No mmi_chrg_dev found %d !\n",i); + break; + } + } + return; +} + +int mmi_chrg_policy_init(struct mmi_charger_manager *chip, + struct mmi_chrg_dts_info *chrg_dts, + int chrg_cnt) { + int i = 0, ops_cnt = 0, chrg_idx = 0; + struct mmi_charger_device *chrg_dev; + + if (!chip ||!chrg_dts) { + mmi_chrg_err(chip, "invalid input param!\n"); + return -EINVAL; + } + + ops_cnt = ARRAY_SIZE(dev_ops); + if (chrg_cnt > ops_cnt || + chrg_cnt > chip->mmi_chrg_dev_num) { + mmi_chrg_err(chip, "invalid input param!, chrg_cnt %d , ops_cnt %d, " + "mmi_chrg_dev_num %d\n", + chrg_cnt, ops_cnt, chip->mmi_chrg_dev_num); + return -EINVAL; + } + + mmi_chrg_err(chip, "chrg_cnt %d\n", chrg_cnt); + for (i = 0; i < chrg_cnt; i++) { + if (!strcmp(chrg_dts[i].chrg_name, dev_ops[i].dev_name)) { + + chrg_dev = mmi_charger_device_register(chrg_dts[i].chrg_name, + chrg_dts[i].psy_name, chip->dev, chip, + dev_ops[i].ops); + if (IS_ERR_OR_NULL(chrg_dev) + || !is_charger_exist(chrg_dts[i].chrg_name)) { + mmi_chrg_err(chip, + "register mmi charger %s failed\n", + chrg_dts[i].chrg_name); + return -EINVAL; + } else { + mmi_chrg_info(chip, + "register mmi charger %s successfully, i %d, chrg_idx %d\n", + chrg_dts[i].chrg_name, i, chrg_idx); + chrg_dev->charging_curr_limited = + chrg_dts[i].charging_curr_limited; + mmi_chrg_err(chip, "charging_curr_limited %d\n", + chrg_dts[i].charging_curr_limited); + chrg_dev->charging_curr_min = + chrg_dts[i].charging_curr_min; + mmi_chrg_err(chip, "charging_curr_min %d, chrg_id %d\n", + chrg_dts[i].charging_curr_min, chrg_idx); + chip->chrg_list[chrg_idx] = chrg_dev; + mmi_chrg_err(chip, "--- over ----\n"); + chrg_idx++; + } + } + } + + mmi_chrg_info(chip,"chrg_cnt %d, ops_cnt %d, mmi_chrg_dev_num %d, " + "chrg_idx %d\n", + chrg_cnt, ops_cnt, chip->mmi_chrg_dev_num, chrg_idx); + + if (chrg_idx != chip->mmi_chrg_dev_num + && chrg_idx > 0) { + + mmi_chrg_err(chip, "chrg_id %d != charger num %d\n", + chrg_idx, chip->mmi_chrg_dev_num); + for (i = 0; i < chrg_idx; i++) + mmi_charger_device_unregister(chip->chrg_list[i]); + return -EINVAL; + } + + chrg_dev_init(chip, &g_chrg_list); + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + INIT_DELAYED_WORK(&chip->mmi_chrg_sm_work, mmi_chrg_sm_work_func); + chip->qc3p_volt_comp = QC3P_INIT_VOLT_COMP; + INIT_DELAYED_WORK(&chip->mmi_qc3p_chrg_sm_work, mmi_qc3p_chrg_sm_work_func); + return 0; +} + +static int mmi_chrg_manager_probe(struct platform_device *pdev) +{ + int ret = 0; + struct mmi_charger_manager *chip; + struct iio_dev *indio_dev; + + if (!pdev) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + chip = iio_priv(indio_dev); + if (!chip) { + dev_err(&pdev->dev, + "Unable to alloc memory for mmi_charger_manager\n"); + return -ENOMEM; + } + chip->indio_dev = indio_dev; + + chip->dev = &pdev->dev; + chip->name = "mmi_chrg_manager"; + chip->debug_mask = &__debug_mask; + chip->suspended = false; + + ret = mmi_charger_class_init(); + if (ret < 0) { + dev_err(&pdev->dev, + "mmi charger class init failed\n"); + goto cleanup; + } + + ret = mmi_chrg_manager_parse_dt(chip); + if (ret < 0) { + dev_err(&pdev->dev, + "parse dt failed\n"); + goto cleanup; + } + + ret = mmi_charger_init_iio_psy(chip, pdev); + if (ret) { + dev_err(&pdev->dev, + "mmi charger iio psy init failed\n"); + goto cleanup; + } + + chip->factory_mode = mmi_factory_check(); + + chip->qcom_psy = power_supply_get_by_name("qcom_battery"); + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) { + mmi_chrg_err(chip, "Could not get battery power_supply\n"); + goto cleanup; + } + + + ret = mmi_chrg_policy_init(chip, chrg_name_list, + chip->mmi_chrg_dev_num); + if (ret < 0) { + dev_err(&pdev->dev, + "mmi chrg policy init failed\n"); + goto cleanup; + } + + chip->pd_handle = + devm_usbpd_get_by_phandle(chip->dev, "qcom,usbpd-phandle"); + if (IS_ERR_OR_NULL(chip->pd_handle)) { + dev_err(&pdev->dev, "Error getting the pd phandle %ld\n", + PTR_ERR(chip->pd_handle)); + chip->pd_handle = NULL; + } + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) + mmi_chrg_err(chip, "Could not get USB power_supply\n"); + } + + INIT_WORK(&chip->psy_changed_work, psy_changed_work_func); + INIT_DELAYED_WORK(&chip->heartbeat_work, mmi_heartbeat_work); + + ret = mmi_chrg_mgr_psy_register(chip); + if (ret) + goto cleanup; + + chip->psy_nb.notifier_call = psy_changed; + ret = power_supply_reg_notifier(&chip->psy_nb); + if (ret) + goto cleanup; + + init_completion(&chip->sm_completion); + platform_set_drvdata(pdev, chip); + + //create_sysfs_entries(chip); + schedule_work(&chip->psy_changed_work); + mmi_chrg_info(chip, "mmi chrg manager initialized successfully, ret %d\n", ret); + return 0; +cleanup: + mmi_charger_class_exit(); + devm_kfree(&pdev->dev, chip); + return ret; +} + +static int mmi_chrg_manager_remove(struct platform_device *pdev) +{ + struct mmi_charger_manager *chip = platform_get_drvdata(pdev); + + power_supply_unreg_notifier(&chip->psy_nb); + power_supply_unregister(chip->mmi_chrg_mgr_psy ); + platform_set_drvdata(pdev, NULL); + devm_kfree(&pdev->dev, chip); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mmi_chrg_suspend(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct mmi_charger_manager *chip = platform_get_drvdata(pdev); + + chip->suspended = true; + + return 0; +} + +static int mmi_chrg_resume(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct mmi_charger_manager *chip = platform_get_drvdata(pdev); + + chip->suspended = false; + + return 0; +} +#else +#define smb_mmi_suspend NULL +#define smb_mmi_resume NULL +#endif + +static const struct dev_pm_ops mmi_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mmi_chrg_suspend, mmi_chrg_resume) +}; + +static const struct of_device_id mmi_chrg_manager_match_table[] = { + {.compatible = "mmi,chrg-manager"}, + {}, +}; +MODULE_DEVICE_TABLE(of, mmi_chrg_manager_match_table); + +static struct platform_driver mmi_chrg_manager_driver = { + .probe = mmi_chrg_manager_probe, + .remove = mmi_chrg_manager_remove, + .driver = { + .name = "mmi_chrg_manager", + .owner = THIS_MODULE, + .pm = &mmi_dev_pm_ops, + .of_match_table = mmi_chrg_manager_match_table, + }, +}; + +static int __init mmi_chrg_manager_init(void) +{ + int ret; + ret = platform_driver_register(&mmi_chrg_manager_driver); + if (ret) { + pr_err("mmi_chrg_manager failed to register driver\n"); + return ret; + } + return 0; +} + +static void __exit mmi_chrg_manager_exit(void) +{ + platform_driver_unregister(&mmi_chrg_manager_driver); +} + +module_init(mmi_chrg_manager_init); +module_exit(mmi_chrg_manager_exit); + +MODULE_ALIAS("platform:mmi parallel charger"); +MODULE_AUTHOR("Motorola Mobility LLC"); +MODULE_DESCRIPTION("Motorola Mobility parallel charger"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/mmi_parallel_charger_iio/mmi_charger_core.h b/drivers/power/mmi_parallel_charger_iio/mmi_charger_core.h new file mode 100644 index 000000000000..449ad5a9d62b --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/mmi_charger_core.h @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + +#ifndef __MMI_CHRG_CORE_H_ +#define __MMI_CHRG_CORE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include +#include +#include + +#define mmi_chrg_err(chip, fmt, ...) \ + pr_err("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__) \ + +#define mmi_chrg_info(chip, fmt, ...) \ + pr_info("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__) \ + +#define mmi_chrg_dbg(chip, reason, fmt, ...) \ + do { \ + if (*chip->debug_mask & (reason)) \ + pr_info("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__); \ + else \ + pr_debug("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__); \ + } while (0) + +enum print_reason { + PR_INTERRUPT = BIT(0), + PR_MISC = BIT(2), + PR_MOTO = BIT(7), +}; + +enum { + POWER_SUPPLY_CHARGE_RATE_NONE = 0, + POWER_SUPPLY_CHARGE_RATE_NORMAL, + POWER_SUPPLY_CHARGE_RATE_WEAK, + POWER_SUPPLY_CHARGE_RATE_TURBO, + POWER_SUPPLY_CHARGE_RATE_TURBO_30W, + POWER_SUPPLY_CHARGE_RATE_HYPER, +}; + +static inline void wakeup_source_init_internal(struct wakeup_source *ws, + const char *name) +{ + if(ws) { + memset(ws, 0, sizeof(*ws)); + ws->name = name; + } + + wakeup_source_add(ws); +} + +#define MAX_NUM_STEPS 10 +enum mmi_chrg_temp_zones { + ZONE_FIRST = 0, + /* states 0-9 are reserved for zones */ + ZONE_LAST = MAX_NUM_STEPS + ZONE_FIRST - 1, + ZONE_HOT, + ZONE_COLD, + ZONE_NONE = 0xFF, +}; + +enum mmi_chrg_steps { + STEP_FIRST = 0, + STEP_LAST = MAX_NUM_STEPS + STEP_FIRST - 1, + STEP_NONE = 0xFF, +}; + +enum mmi_pd_pps_result { + NO_ERROR, + BLANCE_POWER, + RESET_POWER, +}; + +struct mmi_chrg_step_power { + u32 chrg_step_volt; + u32 chrg_step_curr; +}; + +struct mmi_chrg_temp_zone { + int temp_c; /*temperature*/ + struct mmi_chrg_step_power *chrg_step_power; +}; + +struct mmi_chrg_step_info { + enum mmi_chrg_steps pres_chrg_step; + int temp_c; + int chrg_step_cc_curr; + int chrg_step_cv_volt; + int chrg_step_cv_tapper_curr; + bool last_step; +}; + +struct mmi_chrg_dev_ops { + const char *dev_name; + struct mmi_charger_ops *ops; +}; + +struct mmi_chrg_dts_info { + const char *chrg_name; + const char *psy_name; + int charging_curr_limited; + int charging_curr_min; +}; + +enum mmi_chrg_dev { + PMIC_SW = 0, + CP_MASTER, + CP_SLAVE, + CHRG_NUM, +}; + +struct mmi_cp_policy_dev { + bool pmic_sw; + bool cp_master; + bool cp_slave; + bool cp_clave_later; + struct mmi_charger_device *chrg_dev[CHRG_NUM]; +}; + +#define PPS_RET_HISTORY_SIZE 8 +#define PD_SRC_PDO_TYPE_FIXED 0 +#define PD_SRC_PDO_TYPE_BATTERY 1 +#define PD_SRC_PDO_TYPE_VARIABLE 2 +#define PD_SRC_PDO_TYPE_AUGMENTED 3 +#define STEP_FIREST_CURR_COMP 300000 +#define TYPEC_HIGH_CURRENT_UA 3000000 +#define TYPEC_MIDDLE_CURRENT_UA 2000000 +#define PD_ALLOW_MIN_CURRENT_UA 1500000 +#define SWITCH_CHARGER_PPS_VOLT 5000000 +#define PUMP_CHARGER_PPS_MIN_VOLT 8000000 +#define COOLING_HYSTERISIS_DEGC 2 +struct mmi_charger_manager { + const char *name; + struct device *dev; + struct power_supply *batt_psy; + struct power_supply *qcom_psy; + struct power_supply *usb_psy; + struct power_supply *mmi_chrg_mgr_psy; + struct usbpd *pd_handle; + struct usbpd_pdo_info mmi_pdo_info[PD_MAX_PDO_NUM]; + struct notifier_block psy_nb; + + struct iio_channel **ext_iio_chans; + struct iio_dev *indio_dev; + struct iio_chan_spec *iio_chan; + struct iio_channel *int_iio_chans; + + bool factory_mode; + bool suspended; + bool awake; + bool cp_disable; + + int *debug_mask; + int mmi_pd_pdo_idx; /*request the pdo idx of PD*/ + int pps_volt_steps; /*PPS voltage, programming step size*/ + int pps_curr_steps; /*pps current, programming step size*/ + + int pd_volt_max; /*the Maximum request PD Voltage*/ + int pd_curr_max; /*the Maximum request PD current*/ + int batt_ovp_lmt; /*the battery over current limitation*/ + int pl_chrg_vbatt_min; /*the minimum battery voltage to enable parallel charging*/ + + int typec_middle_current; + int pd_allow_min_current; + int step_first_curr_comp; + int pps_volt_comp; + int pd_request_volt; + int pd_request_curr; + /*the request PD power*/ + int pd_request_volt_prev; + int pd_request_curr_prev; + /*the previous request PD power*/ + int pd_sys_therm_volt; + int pd_sys_therm_curr; + /*the thermal PD power*/ + + int pd_batt_therm_volt; + int pd_batt_therm_curr; + + int pd_target_volt; + int pd_target_curr; + /*the final commited request PD power*/ + + int pps_result; + int pps_result_history[PPS_RET_HISTORY_SIZE]; + int pps_result_history_idx; + /*save the result of the return from PD request*/ + + bool vbus_present; + bool pd_pps_support; + bool pd_pps_balance; + bool freeze_pd_power; + bool extrn_fg; + const char *extrn_fg_name; + bool extrn_sense; + bool recovery_pmic_chrg; + bool dont_rerun_aicl; + + bool sys_therm_cooling; + bool sys_therm_force_pmic_chrg; + bool batt_therm_cooling; + int batt_therm_cooling_cnt; + + struct delayed_work mmi_chrg_sm_work; /*mmi charger state machine work*/ + struct delayed_work heartbeat_work; /*cycle trig heartbeat work*/ + struct completion sm_completion; + struct work_struct psy_changed_work; /*the change notification of power supply*/ + bool sm_work_running; + int num_temp_zones; + struct mmi_chrg_temp_zone *temp_zones; /*the temperature zone of charging*/ + enum mmi_chrg_temp_zones pres_temp_zone; /*the present zone idx*/ + + int *thermal_mitigation; /*thermal mitigation array*/ + int thermal_levels; + int system_thermal_level; /*thermal level setting*/ + + int chrg_step_nums; + struct mmi_chrg_step_info chrg_step; /*step charger info*/ + + int mmi_chrg_dev_num; + struct mmi_charger_device **chrg_list; /*charger device list*/ + + /*qc3p*/ + int qc3p_max_ibus_ma; + int qc3p_power; + bool qc3p_active; + bool qc3p_sm_work_running; + struct delayed_work mmi_qc3p_chrg_sm_work; + int qc3p_request_volt; + int qc3p_request_volt_prev; + int qc3p_target_volt; + int qc3p_batt_therm_volt; + int qc3p_sys_therm_volt; + bool qc3p_recovery_pmic_chrg; + bool qc3p_sys_therm_cooling; + bool qc3p_sys_therm_force_pmic_chrg; + bool qc3p_batt_therm_cooling; + int qc3p_batt_therm_cooling_cnt; + int qc3p_volt_comp; + int qc3p_ibus_max_volt; + int qc3p_volt_steps; + int qc3p_volt_max; +}; + +#define PPS_INIT_VOLT_COMP 500000 +#define QC3P_INIT_VOLT_COMP 500000 +extern void mmi_qc3p_chrg_sm_work_func(struct work_struct *work); +extern struct mmi_cp_policy_dev g_chrg_list; +extern const struct mmi_chrg_dev_ops dev_ops[]; +extern void clear_chrg_dev_error_cnt(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_list); +extern void chrg_policy_error_clear(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_list); +extern void mmi_chrg_sm_work_func(struct work_struct *work); +extern void chrg_dev_init(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_list); +extern bool mmi_get_pps_result_history(struct mmi_charger_manager *chip); +extern void mmi_set_pps_result_history(struct mmi_charger_manager *chip, int pps_result); +extern void mmi_clear_pps_result_history(struct mmi_charger_manager *chip); +extern int mmi_calculate_delta_volt(int pps_voltage, int pps_current, int delta_curr); +extern bool mmi_find_chrg_step(struct mmi_charger_manager *chip, int temp_zone, int vbatt_volt); +extern bool mmi_find_temp_zone(struct mmi_charger_manager *chip, int temp_c, bool ignore_hysteresis_degc); +extern void mmi_update_all_charger_status(struct mmi_charger_manager *chip); +extern void mmi_update_all_charger_error(struct mmi_charger_manager *chip); +extern void mmi_dump_charger_error(struct mmi_charger_manager *chip, + struct mmi_charger_device *chrg_dev); +extern bool mmi_is_cable_plugout(struct mmi_charger_manager *chip); +extern ssize_t mmi_get_factory_image_mode(void); +extern ssize_t mmi_set_factory_image_mode(int mode); +extern ssize_t mmi_get_factory_charge_upper(void); +extern ssize_t mmi_get_demo_mode(void); +extern ssize_t mmi_set_demo_mode(int mode); +extern ssize_t mmi_get_max_chrg_temp(void); +extern ssize_t mmi_set_max_chrg_temp(int value); +#endif diff --git a/drivers/power/mmi_parallel_charger_iio/mmi_charger_core_iio.h b/drivers/power/mmi_parallel_charger_iio/mmi_charger_core_iio.h new file mode 100644 index 000000000000..e4f049f53428 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/mmi_charger_core_iio.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + +#ifndef __MMI_CHRG_CORE_IIO_H +#define __MMI_CHRG_CORE_IIO_H + +#include +#include + +struct mmi_charger_iio_channels { + const char *datasheet_name; + int channel_num; + enum iio_chan_type type; + long info_mask; +}; + +#define MMI_CHARGER_IIO_CHAN(_name, _num, _type, _mask) \ + { \ + .datasheet_name = _name, \ + .channel_num = _num, \ + .type = _type, \ + .info_mask = _mask, \ + }, + +#define MMI_CHARGER_CHAN_INDEX(_name, _num) \ + MMI_CHARGER_IIO_CHAN(_name, _num, IIO_INDEX, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +static const struct mmi_charger_iio_channels mmi_charger_iio_psy_channels[] = { + MMI_CHARGER_CHAN_INDEX("cp_charging_enabled", PSY_IIO_CP_ENABLE) +}; +enum mmi_charger_ext_iio_channels { + /*smb5*/ + SMB5_HW_CURRENT_MAX, + SMB5_CHARGING_ENABLED, + SMB5_TYPEC_MODE, + SMB5_USB_INPUT_CURRENT_SETTLED, + SMB5_USB_PD_ACTIVE, + /*qc3p*/ + SMB5_USB_REAL_TYPE, + SMB5_DP_DM, + SMB5_QC3P_POWER, + /*cp*/ + CP_ENABLE, + CP_INPUT_CURRENT_NOW, + CP_INPUT_VOLTAGE_NOW, + CP_STATUS1, + CP_CLEAR_ERROR, + /*mmi-smb5charger-iio*/ + MMI_CP_ENABLE_STATUS, +}; + +static const char * const mmi_charger_ext_iio_chan_name[] = { + /*smb5*/ + [SMB5_HW_CURRENT_MAX] = "usb_hw_current_max", + [SMB5_CHARGING_ENABLED] = "charging_enabled", + [SMB5_TYPEC_MODE] = "usb_typec_mode", + [SMB5_USB_INPUT_CURRENT_SETTLED] = "usb_input_current_settled", + [SMB5_USB_PD_ACTIVE] = "usb_pd_active", + /*qc3p*/ + [SMB5_USB_REAL_TYPE] = "usb_real_type", + [SMB5_DP_DM] = "battery_dp_dm", + [SMB5_QC3P_POWER] = "usb_qc3p_power", + /*cp*/ + [CP_ENABLE] = "cp_enable", + [CP_INPUT_CURRENT_NOW] = "cp_input_current_now", + [CP_INPUT_VOLTAGE_NOW] = "cp_input_voltage_now", + [CP_STATUS1] = "cp_status1", + [CP_CLEAR_ERROR] = "cp_clear_error", + /*mmi-smb5charger-iio*/ + [MMI_CP_ENABLE_STATUS] = "mmi_cp_enabled_status", +}; + +int mmi_charger_read_iio_chan(struct mmi_charger_manager *chip, + enum mmi_charger_ext_iio_channels chan, int *val); + +int mmi_charger_write_iio_chan(struct mmi_charger_manager *chip, + enum mmi_charger_ext_iio_channels chan, int val); + +#endif diff --git a/drivers/power/mmi_parallel_charger_iio/mmi_charger_policy.h b/drivers/power/mmi_parallel_charger_iio/mmi_charger_policy.h new file mode 100644 index 000000000000..62db9d556e9a --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/mmi_charger_policy.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + #ifndef __MMI_CHRG_POLICY_H_ + #define __MMI_CHRG_POLICY_H_ +extern void mmi_chrg_policy_clear(struct mmi_charger_manager *chip); +extern void mmi_chrg_enable_all_cp(struct mmi_charger_manager *chip, int val); +extern void mmi_qc3p_chrg_policy_clear(struct mmi_charger_manager *chip); +extern void mmi_qc3p_chrg_enable_all_cp(struct mmi_charger_manager *chip, int val); +#endif diff --git a/drivers/power/mmi_parallel_charger_iio/mmi_charger_pump_policy.c b/drivers/power/mmi_parallel_charger_iio/mmi_charger_pump_policy.c new file mode 100644 index 000000000000..9994782e3287 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/mmi_charger_pump_policy.c @@ -0,0 +1,1527 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_core.h" + +#define MINIMUM_CHARGER_VOLT 5100000 + +typedef enum { + PM_STATE_DISCONNECT, + PM_STATE_ENTRY, + PM_STATE_SW_ENTRY, + PM_STATE_SW_LOOP, + PM_STATE_CHRG_PUMP_ENTRY, + PM_STATE_SINGLE_CP_ENTRY, + PM_STATE_DULE_CP_ENTRY, + PM_STATE_PPS_TUNNING_CURR, + PM_STATE_PPS_TUNNING_VOLT, + PM_STATE_CP_CC_LOOP, + PM_STATE_CP_CV_LOOP, + PM_STATE_CP_QUIT, + PM_STATE_RECOVERY_SW, + PM_STATE_STOP_CHARGE, + PM_STATE_COOLING_LOOP, +} pm_sm_state_t; + +const unsigned char *pm_state_str[] = { + "PM_STATE_DISCONNECT", + "PM_STATE_ENTRY", + "PM_STATE_SW_ENTRY", + "PM_STATE_SW_LOOP", + "PM_STATE_CHRG_PUMP_ENTRY", + "PM_STATE_SINGLE_CP_ENTRY", + "PM_STATE_DULE_CP_ENTRY", + "PM_STATE_PPS_TUNNING_CURR", + "PM_STATE_PPS_TUNNING_VOLT", + "PM_STATE_CP_CC_LOOP", + "PM_STATE_CP_CV_LOOP", + "PM_STATE_CP_QUIT", + "PM_STATE_RECOVERY_SW", + "PM_STATE_STOP_CHARGE", + "PM_STATE_COOLING_LOOP", +}; + +static pm_sm_state_t sm_state = PM_STATE_DISCONNECT; +static int chrg_cc_power_tunning_cnt = 0; +static int chrg_cv_taper_tunning_cnt = 0; +static int chrg_cv_delta_volt = 0; +static int quit_slave_chrg_cnt = 0; +static int batt_curr_roof = 0; +static int pd_constant_power_cnt = 0; + +static void mmi_chrg_sm_move_state(struct mmi_charger_manager *chip, pm_sm_state_t state) +{ + mmi_chrg_dbg(chip, PR_INTERRUPT, "pm_state change:%s -> %s\n", + pm_state_str[sm_state], pm_state_str[state]); + sm_state = state; + pd_constant_power_cnt = 0; + batt_curr_roof = 0; +} + +#define PPS_VOLT_COMP_DELTA 300000 +static void chrg_policy_error_recovery(struct mmi_charger_manager *chip, + struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num = 0, i = 0, chrg_error_type= 0; + struct mmi_charger_device *chrg_dev; + chrg_num = chip->mmi_chrg_dev_num; + + for (i = 0; i < chrg_num; i++) { + + switch (i) { + case PMIC_SW: + break; + case CP_MASTER: + if (is_charger_exist(dev_ops[CP_MASTER].dev_name)) { + chrg_dev = chrg_list->chrg_dev[CP_MASTER]; + chrg_error_type = chrg_dev->charger_error.chrg_err_type; + if (chrg_error_type & (1 << MMI_BUS_UCP_ALARM_BIT) || + chrg_error_type & (1 << MMI_BUS_UCP_FAULT_BIT)) { + mmi_chrg_info(chip,"CP master bus ucp error %d, cnt %d," + "pps volt comp %dmV\n", + chrg_error_type, + chrg_dev->charger_error.bus_ucp_err_cnt, + chip->pps_volt_comp); + + if (chrg_dev->charger_error.bus_ucp_err_cnt > 3) { + if (chrg_list->cp_slave) { + chrg_list->cp_slave = false; + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + mmi_chrg_sm_move_state(chip, + PM_STATE_CHRG_PUMP_ENTRY); + } else { + chip->recovery_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, + PM_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + } + } else if (chrg_dev->charger_error.bus_ucp_err_cnt > 6) { + chip->recovery_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, + PM_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + } + + chrg_dev->charger_error.bus_ucp_err_cnt++; + chip->pps_volt_comp += PPS_VOLT_COMP_DELTA; + mmi_chrg_info(chip,"Restart charging, " + "increase pps volt comp %dmV\n", + chip->pps_volt_comp); + mmi_chrg_sm_move_state(chip, + PM_STATE_CHRG_PUMP_ENTRY); + mmi_clear_charger_error(chrg_dev); + }else if (chrg_error_type & (1 << MMI_BUS_OCP_ALARM_BIT) || + chrg_error_type & (1 << MMI_BUS_OCP_FAULT_BIT) || + chrg_error_type & (1 << MMI_CONV_OCP_FAULT_BIT)) { + mmi_chrg_info(chip,"CP master ocp error %d, cnt %d," + "pps volt comp %dmV\n", + chrg_error_type, + chrg_dev->charger_error.bus_ocp_err_cnt, + chip->pps_volt_comp); + if (chrg_dev->charger_error.bus_ocp_err_cnt > 3) { + chip->recovery_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, + PM_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ocp_err_cnt = 0; + } + chrg_dev->charger_error.bus_ocp_err_cnt++; + chip->pps_volt_comp -= PPS_VOLT_COMP_DELTA; + if (chip->pps_volt_comp < 0) + chip->pps_volt_comp = 0; + mmi_chrg_info(chip,"Restart charging, " + "decrease pps volt comp %dmV\n", + chip->pps_volt_comp); + mmi_chrg_sm_move_state(chip, + PM_STATE_CHRG_PUMP_ENTRY); + mmi_clear_charger_error(chrg_dev); + } + } + break; + case CP_SLAVE: + break; + default: + mmi_chrg_err(chip,"No mmi_chrg_dev found %d !\n",i); + break; + } + } + return; +} + +void mmi_chrg_enable_all_cp(struct mmi_charger_manager *chip, int val) +{ + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + bool enable = !!val; + + mmi_chrg_dbg(chip, PR_MOTO,"enable all cp = %d\n", enable); + if (enable) { + if (chip->cp_disable == false) + return; + + cancel_delayed_work_sync(&chip->mmi_chrg_sm_work); + mmi_chrg_sm_move_state(chip, PM_STATE_CHRG_PUMP_ENTRY); + chip->cp_disable = false; + chip->recovery_pmic_chrg = false; + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(0)); + + } else { + if(chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) + return; + + cancel_delayed_work_sync(&chip->mmi_chrg_sm_work); + + chip->cp_disable = true; + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + mmi_chrg_sm_move_state(chip, PM_STATE_RECOVERY_SW); + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(0)); + } +} + +#define HEARTBEAT_SHORT_DELAY_MS 1000 +#define HEARTBEAT_lOOP_WAIT_MS 3000 +#define HEARTBEAT_PPS_TUNNING_MS 100 +#define HEARTBEAT_NEXT_STATE_MS 100 +#define HEARTBEAT_CANCEL -1 +#define CC_CURR_DEBOUNCE 100000 +#define CV_TAPPER_COUNT 3 +#define CC_POWER_COUNT 3 +#define CV_DELTA_VOLT 100000 +#define THERMAL_TUNNING_CURR 100000 +#define COOLING_DELTA_POWER 100000 +#define COOLING_MAX_CNT 5 +#define PPS_SELECT_PDO_RETRY_COUNT 3 +#define DISABLE_CHRG_LIMIT -1 +#define CP_CHRG_SOC_LIMIT 90 +#define PD_CONT_PWR_CNT 5 + +static void clear_chg_manager(struct mmi_charger_manager *chip) +{ + mmi_chrg_dbg(chip, PR_INTERRUPT, "clear mmi chrg manager!\n"); + chip->pd_request_volt = 0; + chip->pd_request_curr = 0; + chip->pd_request_volt_prev = 0; + chip->pd_request_curr_prev = 0; + chip->pd_target_curr = 0; + chip->pd_target_volt = 0; + chip->pd_batt_therm_volt = 0; + chip->pd_batt_therm_curr = 0; + chip->pd_sys_therm_volt = 0; + chip->pd_sys_therm_curr = 0; + chip->recovery_pmic_chrg = false; + chip->sys_therm_cooling= false; + chip->sys_therm_force_pmic_chrg = false; + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + + memset(chip->mmi_pdo_info, 0, + sizeof(struct usbpd_pdo_info) * PD_MAX_PDO_NUM); +} + +void mmi_chrg_policy_clear(struct mmi_charger_manager *chip) { + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + chrg_dev_init(chip, &g_chrg_list); + clear_chrg_dev_error_cnt(chip, &g_chrg_list);; + clear_chg_manager(chip); + if (chrg_list->cp_slave) + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + if (chrg_list->cp_master) + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + sm_state = PM_STATE_DISCONNECT; + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + quit_slave_chrg_cnt = 0; + chrg_cc_power_tunning_cnt = 0; + chrg_cv_taper_tunning_cnt = 0; + chrg_cv_delta_volt = 0; + pd_constant_power_cnt = 0; + batt_curr_roof = 0; + return; +} + +void mmi_chrg_sm_work_func(struct work_struct *work) +{ + struct mmi_charger_manager *chip = container_of(work, + struct mmi_charger_manager, mmi_chrg_sm_work.work); + int i = 0, rc = 0; + int ibatt_curr = 0, vbatt_volt = 0, batt_temp = 0, vbus_pres = 0; + int batt_soc = 0; + int heartbeat_dely_ms = 0; + int cooling_curr = 0; + int cooling_volt = 0; + int pmic_sys_therm_level = 0; + bool zone_change = false; + bool ignore_hysteresis_degc = false; + struct mmi_chrg_step_info *chrg_step; + union power_supply_propval prop = {0,}; + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + + mmi_chrg_dbg(chip, PR_MOTO, "\n\n\n"); + + mmi_chrg_dbg(chip, PR_MOTO, "schedule SM work, sm state %s \n", + pm_state_str[sm_state]); + + mmi_chrg_dbg(chip, PR_MOTO, "pmic-sw is exist: %d " + "cp-master is exist: %d " + "cp-slave is exist: %d\n", + chrg_list->pmic_sw, + chrg_list->cp_master, + chrg_list->cp_slave); + if (!chrg_list->pmic_sw) { + mmi_chrg_err(chip,"PMIC-SW isn't exist, force quite mmi chrg sm work !\n"); + return; + } + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &prop); + if (!rc) + pmic_sys_therm_level = prop.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + ibatt_curr = prop.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_TEMP, &prop); + if (!rc) + batt_temp = prop.intval / 10; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &prop); + if (!rc) + batt_soc = prop.intval; + + + if (ibatt_curr < 0) + ibatt_curr *= -1; + + + mmi_update_all_charger_status(chip); + mmi_update_all_charger_error(chip); + + if (chip->extrn_sense) { + ibatt_curr = chrg_list->chrg_dev[CP_MASTER]->charger_data.ibatt_curr; + ibatt_curr *= 1000; + if (ibatt_curr < 0) + ibatt_curr *= -1; + } + vbatt_volt = chrg_list->chrg_dev[CP_MASTER]->charger_data.vbatt_volt; + vbatt_volt *= 1000; + + vbus_pres = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbus_pres; + if (!vbus_pres) { + for (i = 0; i < 3; i++) { + mmi_update_all_charger_status(chip); + vbus_pres = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbus_pres; + mmi_chrg_info(chip, "Retry check charger status, vbus %d\n", vbus_pres); + if (vbus_pres) { + mmi_chrg_info(chip, "Get vbus present , continue to charging\n"); + break; + } + msleep(100); + } + } + + mmi_chrg_info(chip, "battery current %d\n", ibatt_curr); + mmi_chrg_info(chip, "battery voltage %d\n", vbatt_volt); + mmi_chrg_info(chip, "battery temp %d\n", batt_temp); + mmi_chrg_info(chip, "battery capacity %d\n", batt_soc); + + if (vbus_pres && mmi_is_cable_plugout(chip)) + vbus_pres = 0; + + if (vbus_pres && + (sm_state == PM_STATE_PPS_TUNNING_CURR + || sm_state == PM_STATE_PPS_TUNNING_VOLT + || sm_state == PM_STATE_CP_CC_LOOP + || sm_state == PM_STATE_CP_CV_LOOP)) { + mmi_dump_charger_error(chip, chrg_list->chrg_dev[CP_MASTER]); + chrg_policy_error_recovery(chip, chrg_list); + } + + if (sm_state == PM_STATE_CP_CV_LOOP) + ignore_hysteresis_degc = true; + zone_change = mmi_find_temp_zone(chip, batt_temp, ignore_hysteresis_degc); + chrg_step = &chip->chrg_step; + + if (chip->pres_temp_zone == ZONE_COLD + || chip->pres_temp_zone == ZONE_HOT + || !chrg_list->chrg_dev[PMIC_SW]->charger_enabled + || vbatt_volt > chip->batt_ovp_lmt) { + + mmi_chrg_info(chip, "Force stop charging, " + "pres_temp_zone %d, " + "pmic charger enabled %d, " + "vbatt_volt %dmv, " + "batt ovp limit %dmv\n", + chip->pres_temp_zone, + chrg_list->chrg_dev[PMIC_SW]->charger_enabled, + vbatt_volt, chip->batt_ovp_lmt); + mmi_chrg_sm_move_state(chip, PM_STATE_STOP_CHARGE); + } + + if (!vbus_pres) { + mmi_chrg_sm_move_state(chip, PM_STATE_DISCONNECT); + } else if (sm_state == PM_STATE_ENTRY + || sm_state == PM_STATE_STOP_CHARGE) { + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + } else if (sm_state == PM_STATE_DISCONNECT) { + mmi_get_input_current_settled(chrg_list->chrg_dev[PMIC_SW], + &chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } else if (zone_change && + chip->pres_temp_zone != ZONE_COLD && + chip->pres_temp_zone != ZONE_HOT) { + + if (batt_temp >= chrg_step->temp_c) { + mmi_chrg_info(chip, "battery temp %d, temp thre %d " + "Enter into COOLING LOOP !\n", + batt_temp, chrg_step->temp_c); + chip->batt_therm_cooling = true; + chip->pd_batt_therm_volt = chip->pd_request_volt_prev; + chip->pd_batt_therm_curr = chip->pd_request_curr_prev; + } else if (!chip->batt_therm_cooling && + !ignore_hysteresis_degc) { + mmi_chrg_info(chip, "battery temp %d, temp thre %d " + "Restart select chrg step and temp zone !\n", + batt_temp, chrg_step->temp_c); + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } + } + + mmi_chrg_dbg(chip, PR_MOTO, "temp zone %d, is_changed %d, " + "chrg_step %d, " + "step cc curr %d, step cv volt %d, " + "step cv tapper curr %d\n", + chip->pres_temp_zone, zone_change, + chrg_step->pres_chrg_step, + chrg_step->chrg_step_cc_curr, + chrg_step->chrg_step_cv_volt, + chrg_step->chrg_step_cv_tapper_curr); + switch (sm_state) { + case PM_STATE_DISCONNECT: + mmi_chrg_info(chip,"vbus disconnect !, jump to PM_STATE_DISCONNECT," + "recovery PMIC-SW limitation, and close CP charg\n"); + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + heartbeat_dely_ms = HEARTBEAT_CANCEL; + break; + case PM_STATE_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chip->pd_pps_support + && chrg_list->cp_master + && vbatt_volt > chip->pl_chrg_vbatt_min + && chrg_step->pres_chrg_step != chip->chrg_step_nums - 1 + && chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min + && batt_soc < CP_CHRG_SOC_LIMIT) { + + mmi_chrg_dbg(chip, PR_MOTO, "Enter into CHRG PUMP, " + "vbatt %d uV, " + "pps support %d, " + "chrg step %d, " + "chrg step cc curr %d uA, " + "CP master charging curr min %d uA\n", + vbatt_volt, + chip->pd_pps_support, + chrg_step->pres_chrg_step, + chrg_step->chrg_step_cc_curr, + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min); + mmi_chrg_sm_move_state(chip, PM_STATE_CHRG_PUMP_ENTRY); + + } else { + mmi_chrg_dbg(chip, PR_MOTO, "Enter into PMIC switch charging, " + "the reason is : vbatt %d uV, " + "pl chrg vbatt min %d uV, " + "pps support %d, " + "chrg step %d\n", + vbatt_volt, chip->pl_chrg_vbatt_min, + chip->pd_pps_support, + chrg_step->pres_chrg_step); + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + + } + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt= 0; + chip->pd_sys_therm_volt= chip->pd_request_volt_prev; + chip->pd_sys_therm_curr = chip->pd_request_curr_prev; + chip->pd_batt_therm_volt = chip->pd_request_volt_prev; + chip->pd_batt_therm_curr = chip->pd_request_curr_prev; + chip->sys_therm_cooling = false; + chip->sys_therm_force_pmic_chrg = false; + chip->recovery_pmic_chrg = false; + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_SW_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip, "Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + if (!chip->dont_rerun_aicl) { + mmi_chrg_info(chip, "Do an rerun usb AICL for PMIC-SW\n"); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], false); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], true); + } + mmi_chrg_info(chip, "Check all effective pdo info again\n"); + usbpd_get_pdo_info(chip->pd_handle, chip->mmi_pdo_info,PD_MAX_PDO_NUM); + mmi_chrg_info(chip, "Select FIXED pdo for switch charging !\n"); + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + mmi_chrg_info(chip,"find pdo %d, max volt %d, max curr %d\n", + chip->mmi_pdo_info[i].type, + chip->mmi_pdo_info[i].uv_max, + chip->mmi_pdo_info[i].ua); + if (chip->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_FIXED + && chip->mmi_pdo_info[i].uv_max >= SWITCH_CHARGER_PPS_VOLT + && chip->mmi_pdo_info[i].ua >= TYPEC_HIGH_CURRENT_UA) { + mmi_chrg_dbg(chip, PR_MOTO, "select 5V/3A pps, pdo %d\n", i); + chip->mmi_pd_pdo_idx = + chip->mmi_pdo_info[i].pdo_pos; + break; + } + } + + chip->pd_request_volt = SWITCH_CHARGER_PPS_VOLT; + chip->pd_request_curr = TYPEC_HIGH_CURRENT_UA; + mmi_chrg_info(chip,"Select pdo %d, pd request curr %d, volt %d\n", + chip->mmi_pd_pdo_idx, + chip->pd_request_curr, + chip->pd_request_volt); + + mmi_chrg_sm_move_state(chip, PM_STATE_SW_LOOP); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_SW_LOOP: + if (chip->pd_pps_support + && chip->cp_disable == false + && vbatt_volt > chip->pl_chrg_vbatt_min + && chrg_step->pres_chrg_step != chip->chrg_step_nums - 1 + && chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min + && !chip->recovery_pmic_chrg + && !chip->sys_therm_force_pmic_chrg + && batt_soc < CP_CHRG_SOC_LIMIT) { + mmi_chrg_info(chip, "Enter CP, the reason is : " + "pd pps support %d, " + "vbatt %duV, chrg step %d\n", + chip->pd_pps_support, + vbatt_volt, chrg_step->pres_chrg_step); + mmi_chrg_sm_move_state(chip, PM_STATE_CHRG_PUMP_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + mmi_chrg_dbg(chip, PR_MOTO, "Continue to SW charging, " + "vbatt %d uV, ibatt %d uA\n", + vbatt_volt, ibatt_curr); + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + } + break; + case PM_STATE_CHRG_PUMP_ENTRY: + mmi_chrg_info(chip,"CP master exist %d, CP slave exist %d !\n", + chrg_list->cp_master, + chrg_list->cp_slave); + if (chrg_list->cp_slave) { + mmi_chrg_info(chip,"CP slave is exist !\n"); + mmi_chrg_info(chip,"chrg step cc curr %d uA, " + "CP slave charging curr min %d uA\n", + chrg_step->chrg_step_cc_curr, + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min); + if (chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min) { + mmi_chrg_sm_move_state(chip, + PM_STATE_DULE_CP_ENTRY); + } else + mmi_chrg_sm_move_state(chip, + PM_STATE_SINGLE_CP_ENTRY); + } else { + mmi_chrg_info(chip,"CP slave isn't exist !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_SINGLE_CP_ENTRY); + } + + mmi_chrg_info(chip,"Set PMIC SW FCC limits!\n"); + if (!chrg_list->chrg_dev[PMIC_SW]->charger_limited + && chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited > 0) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited); + mmi_chrg_info(chip,"Set PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = true; + } + + mmi_chrg_info(chip, "Check all effective pdo info again\n"); + usbpd_get_pdo_info(chip->pd_handle, chip->mmi_pdo_info,PD_MAX_PDO_NUM); + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + if ((chip->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_AUGMENTED) + && chip->mmi_pdo_info[i].uv_max >= PUMP_CHARGER_PPS_MIN_VOLT + && chip->mmi_pdo_info[i].ua >= chip->typec_middle_current) { + chip->mmi_pd_pdo_idx = chip->mmi_pdo_info[i].pdo_pos; + mmi_chrg_info(chip, + "Pd charger support pps, pdo %d, " + "volt %d, curr %d \n", + chip->mmi_pd_pdo_idx, + chip->mmi_pdo_info[i].uv_max, + chip->mmi_pdo_info[i].ua); + chip->pd_pps_support = true; + + if (chip->mmi_pdo_info[i].uv_max < + chip->pd_volt_max) { + chip->pd_volt_max = + chip->mmi_pdo_info[i].uv_max; + } + + if (chip->mmi_pdo_info[i].ua < + chip->pd_curr_max) { + chip->pd_curr_max = + chip->mmi_pdo_info[i].ua; + } + + break; + } + } + + if (batt_soc >= 65) { + //Read the vbat aggin after the soc above 65% + msleep(20); + rc = power_supply_get_property(chrg_list->chrg_dev[CP_MASTER]->chrg_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + vbatt_volt = prop.intval*1000; + } + + /*Initial setup pps request power by the battery voltage*/ + chip->pd_request_volt = (2 * vbatt_volt) % 20000; + chip->pd_request_volt = 2 * vbatt_volt - chip->pd_request_volt + + chip->pps_volt_comp; + chip->pd_request_curr = + min(chip->pd_curr_max, chip->typec_middle_current); + mmi_chrg_info(chip,"pps init , volt %dmV, curr %dmA, volt comp %dmv\n", + chip->pd_request_volt, chip->pd_request_curr, chip->pps_volt_comp); + chrg_policy_error_clear(chip, chrg_list); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_SINGLE_CP_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_chrg_info(chip,"Disable Slave Charger Pump !\n"); + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Enable Master Charger Pump !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_PPS_TUNNING_CURR); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_STATE_DULE_CP_ENTRY: + if (chrg_list->cp_master) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Enable Master Charger Pump !\n"); + } + + if (chrg_list->cp_slave) { + chrg_list->cp_clave_later = true; + mmi_chrg_info(chip,"For ibus UCP, " + "delay start Slave Charger Pump in TUNNING_VOLT stage !\n"); + } + + if (chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_sm_move_state(chip, PM_STATE_PPS_TUNNING_CURR); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_STATE_PPS_TUNNING_CURR: + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + if (chrg_list->cp_master + && (!chrg_list->chrg_dev[CP_MASTER]->charger_enabled + || !(chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into " + "SW directly\n"); + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + }else if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + if (chip->pd_request_curr - chip->pps_curr_steps + > chip->typec_middle_current) + chip->pd_request_curr -= chip->pps_curr_steps; + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_chrg_info(chip,"During the curr going up process, " + "the chrg step was changed," + "stop increase pps curr and Enter into " + "CC stage as soon!\n"); + } else if (chip->pps_result < 0) { + if (mmi_get_pps_result_history(chip) != NO_ERROR) { + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_chrg_info(chip,"Too many pdo request failed," + " Enter into CC stage directly!\n"); + mmi_clear_pps_result_history(chip); + } + chip->pd_request_curr = chip->pd_request_curr_prev; + goto schedule; + } else if (chip->pd_request_curr + chip->pps_curr_steps + <= chip->pd_curr_max + && vbatt_volt < chrg_step->chrg_step_cv_volt + && ibatt_curr < chrg_step->chrg_step_cc_curr) { + chip->pd_request_curr += chip->pps_curr_steps; + mmi_chrg_dbg(chip, PR_MOTO, "Increase pps curr %d\n", + chip->pd_request_curr); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + } else { + mmi_chrg_info(chip,"Enter into tunning pps volt\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_PPS_TUNNING_VOLT); + } + break; + case PM_STATE_PPS_TUNNING_VOLT: + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + if (chrg_list->cp_slave + && chrg_list->cp_clave_later + && !chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], true); + mmi_chrg_info(chip,"Enable Slave Charger Pump !\n"); + } + + if (chrg_list->cp_master + && (!chrg_list->chrg_dev[CP_MASTER]->charger_enabled + || !(chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, " + "Enter into SW directly\n"); + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + } else if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + chip->pd_request_volt -= chip->pps_volt_steps; + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_chrg_info(chip,"Duing the volt going up process, " + "the chrg step was changed," + "stop increase pps volt and" + " Enter into CC stage as soon!\n"); + } else if (chip->pps_result < 0) { + if (mmi_get_pps_result_history(chip) != NO_ERROR) { + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_clear_pps_result_history(chip); + mmi_chrg_info(chip,"Too many pdo request failed," + " Enter into CC stage directly!\n"); + } + chip->pd_request_volt = chip->pd_request_volt_prev; + goto schedule; + } else if (chip->pd_request_volt + chip->pps_volt_steps + <= chip->pd_volt_max + && vbatt_volt < chrg_step->chrg_step_cv_volt + && ibatt_curr < ((chrg_step->pres_chrg_step == STEP_FIRST) ? + chrg_step->chrg_step_cc_curr + chip->step_first_curr_comp: + chrg_step->chrg_step_cc_curr)) { + chip->pd_request_volt += chip->pps_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, "Increase pps volt %d\n", + chip->pd_request_volt); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + } else { + mmi_chrg_info(chip,"Enter into CC loop stage !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_CC_LOOP); + } + + if (ibatt_curr > batt_curr_roof) { + batt_curr_roof = ibatt_curr; + } + if (ibatt_curr < batt_curr_roof) + pd_constant_power_cnt++; + else + pd_constant_power_cnt = 0; + + if (pd_constant_power_cnt > PD_CONT_PWR_CNT) { + mmi_chrg_info(chip,"PD adapter was ready in constant power state, " + "Enter into CC loop stage !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_CC_LOOP); + } + mmi_chrg_info(chip, "pd_constant_power_cnt %d, " + "batt_curr_roof %d\n", + pd_constant_power_cnt, + batt_curr_roof); + break; + case PM_STATE_CP_CC_LOOP: + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CC loop, chrg_step %d, " + "vbatt %dmV, ibatt %dmA, " + "CC target curr %dmA, " + "next CV target volt %dmV\n ", + chrg_step->pres_chrg_step, + vbatt_volt, ibatt_curr, + chrg_step->chrg_step_cc_curr, + chrg_step->chrg_step_cv_volt); + if (chrg_list->cp_master + && (!chrg_list->chrg_dev[CP_MASTER]->charger_enabled + || !(chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into SW directly\n"); + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + goto schedule; + } + + if (chip->pps_result < 0) { + mmi_chrg_err(chip, "Last select pdo failed\n"); + chip->pps_result = mmi_get_pps_result_history(chip); + switch (chip->pps_result) { + case BLANCE_POWER: + chip->pd_pps_balance = true; + mmi_chrg_err(chip, "Enable pps power balance\n"); + mmi_clear_pps_result_history(chip); + break; + case RESET_POWER: + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + mmi_chrg_err(chip, "Hard reset charger policy to recovery power," + "Since too many pdo failed\n"); + mmi_clear_pps_result_history(chip); + break; + default: + break; + } + + chip->pd_request_volt = chip->pd_request_volt_prev; + if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->pd_request_volt < chip->pd_volt_max) + chrg_cc_power_tunning_cnt = + CC_POWER_COUNT; + goto schedule; + } + + if (!chip->sys_therm_cooling + && !chip->batt_therm_cooling + && pd_constant_power_cnt <= PD_CONT_PWR_CNT) { + if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->pd_request_volt < chip->pd_volt_max + && chrg_cc_power_tunning_cnt >= + CC_POWER_COUNT) { + if (chip->pd_pps_balance + && (chip->pd_request_curr - chip->pps_curr_steps) + > chip->pd_allow_min_current) { + chip->pd_request_curr -= + chip->pps_curr_steps; + chip->pd_request_volt += + mmi_calculate_delta_volt(chip->pd_request_volt_prev, + chip->pd_request_curr_prev, + chip->pps_curr_steps); + mmi_chrg_dbg(chip, PR_MOTO, + "Request curr decreass %dmA, " + "Request volt increase %dmV \n", + chip->pd_request_curr, + chip->pd_request_volt); + } else { + if (chip->pd_request_curr + chip->pps_curr_steps + < chip->pd_curr_max) { + chip->pd_request_curr += + chip->pps_curr_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "Request curr decreass %dmA\n ", + chip->pd_request_curr); + } else if (chip->pd_request_volt + chip->pps_volt_steps + < chip->pd_volt_max) { + chip->pd_request_volt += + chip->pps_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "Request volt decreass %duV\n ", + chip->pd_request_volt); + } + } + chrg_cc_power_tunning_cnt = 0; + mmi_chrg_dbg(chip, PR_MOTO, + "For keeping CC chrg POWER, " + "Have to adjust pd input volt or curr" + "Request volt %dmV, " + "Request curr %dmA\n", + chip->pd_request_volt, + chip->pd_request_curr); + }else if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->pd_request_volt < chip->pd_volt_max) { + chrg_cc_power_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CC tunning cnt %d\n", + chrg_cc_power_tunning_cnt); + } else if (ibatt_curr > chrg_step->chrg_step_cc_curr + + CC_CURR_DEBOUNCE) { + chip->pd_request_volt -= chip->pps_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "In the CC step , the ibatt is greater than CC curr, " + "Request volt decreass %duV to remain CC step\n ", + chip->pd_request_volt); + } else + chrg_cc_power_tunning_cnt = 0; + + if(ibatt_curr < chrg_step->chrg_step_cc_curr) + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + } + + if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + if (chrg_cv_taper_tunning_cnt > + CV_TAPPER_COUNT) { + mmi_chrg_sm_move_state(chip, PM_STATE_CP_CV_LOOP); + chrg_cv_taper_tunning_cnt = 0; + chrg_cv_delta_volt = CV_DELTA_VOLT; + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV taper cnt %d, " + "chrg step cv volt %dmV, " + "vbatt %dmV\n", + chrg_cv_taper_tunning_cnt, + chrg_step->chrg_step_cv_volt, + vbatt_volt); + } + } else + chrg_cv_taper_tunning_cnt = 0; + + break; + case PM_STATE_CP_CV_LOOP: + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV loop, chrg_step %d, " + "vbatt %dmV, ibatt %dmA, " + "CV target volt %dmV, " + "CV taper curr %dmA\n ", + chrg_step->pres_chrg_step, + vbatt_volt, ibatt_curr, + chrg_step->chrg_step_cv_volt, + chrg_step->chrg_step_cv_tapper_curr); + if (chrg_list->cp_master + && (!chrg_list->chrg_dev[CP_MASTER]->charger_enabled + || !(chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into SW directly\n"); + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + goto schedule; + } + + if (chip->pps_result < 0) { + mmi_chrg_err(chip, "Last select pdo failed\n"); + chip->pps_result = mmi_get_pps_result_history(chip); + switch (chip->pps_result) { + case BLANCE_POWER: + chip->pd_request_curr -= + chip->pps_curr_steps; + mmi_chrg_err(chip, "Reduce pps curr,for pps power balance\n"); + mmi_clear_pps_result_history(chip); + break; + case RESET_POWER: + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + mmi_chrg_err(chip, "Hard reset charger policy to recovery power," + "Since too many pdo failed\n"); + mmi_clear_pps_result_history(chip); + break; + default: + break; + } + chip->pd_request_volt = chip->pd_request_volt_prev; + goto schedule; + } + + if (vbatt_volt >= chrg_step->chrg_step_cv_volt + && ((!chrg_step->last_step && + ibatt_curr < chrg_step->chrg_step_cv_tapper_curr) + || ibatt_curr < chrg_list->chrg_dev[CP_MASTER]->charging_curr_min)) { + if (chrg_cv_taper_tunning_cnt >= CV_TAPPER_COUNT) { + if (ibatt_curr < + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min) { + mmi_chrg_info(chip, "Ready quite CP chrg stage, " + "and Enter into PMIC switch chrg stage, " + "chrg step %d, ibatt %dmA\n", + chrg_step->pres_chrg_step, ibatt_curr); + mmi_find_chrg_step(chip, + chip->pres_temp_zone, vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_QUIT); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + if (mmi_find_chrg_step(chip, + chip->pres_temp_zone, vbatt_volt)) { + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + mmi_chrg_info(chip,"Jump to next chrg step\n"); + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + } else { + mmi_chrg_info(chip,"Can't find next chrg step\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_QUIT); + } + } + chrg_cv_taper_tunning_cnt = 0; + } else { + chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, "chrg cv taper cnt ++, %d\n", + chrg_cv_taper_tunning_cnt); + } + + }else if (!chip->sys_therm_cooling + && !chip->batt_therm_cooling) { + + chrg_cv_taper_tunning_cnt = 0; + if (vbatt_volt > chrg_step->chrg_step_cv_volt + 10000) { + if (chrg_cv_delta_volt > 20000) + chip->pd_request_volt -= chrg_cv_delta_volt; + else + chip->pd_request_volt -= 20000; + mmi_chrg_info(chip, + "For keeping CV stage, decrease volt %dmV, " + "cv delta volt %dmV\n", + chip->pd_request_volt, chrg_cv_delta_volt); + } else if (vbatt_volt < chrg_step->chrg_step_cv_volt - 10000) { + chrg_cv_delta_volt -= 20000; + chip->pd_request_volt += 20000; + mmi_chrg_info(chip, + "For keeping CV stage, increase volt %dmV, " + "cv delta volt %dmV\n", + chip->pd_request_volt, chrg_cv_delta_volt); + } else { + mmi_chrg_dbg(chip, PR_MOTO, "CV loop work well, " + "keep pd power, volt %dmV, curr %dmA\n", + chip->pd_request_volt, chip->pd_request_curr); + } + + }else { + /*In this case, sys_therm_cooling or batt_therm_cooling is ture*/ + + if (vbatt_volt > chrg_step->chrg_step_cv_volt + 10000) { + if (chrg_cv_delta_volt > 20000) + chip->pd_request_volt -= chrg_cv_delta_volt; + else + chip->pd_request_volt -= 20000; + mmi_chrg_info(chip, + "For keeping CV stage, decrease volt %dmV, " + "cv delta volt %dmV\n", + chip->pd_request_volt, chrg_cv_delta_volt); + } else { + mmi_chrg_dbg(chip, PR_MOTO, "CV loop work well, " + "keep pd power, volt %dmV, curr %dmA\n", + chip->pd_request_volt, chip->pd_request_curr); + } + } + + if (chrg_list->cp_slave) { + if (quit_slave_chrg_cnt > 3 + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + mmi_chrg_info(chip,"Quit slave chrg, the reason is :ibatt %duA, " + "CP slave charging curr min %d uA\n", + ibatt_curr, + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Restart CP master again\n"); + } + + if (ibatt_curr < + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min) + quit_slave_chrg_cnt++; + else + quit_slave_chrg_cnt = 0; + } + break; + case PM_STATE_CP_QUIT: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + mmi_chrg_sm_move_state(chip, PM_STATE_RECOVERY_SW); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_RECOVERY_SW: + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + + if (!chip->dont_rerun_aicl) { + mmi_chrg_info(chip,"Do an rerun usb AICL for PMIC-SW\n"); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], false); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], true); + } + chip->recovery_pmic_chrg = true; + chrg_cv_taper_tunning_cnt = 0; + } + + chip->pd_request_curr = TYPEC_HIGH_CURRENT_UA; + mmi_chrg_info(chip,"ibatt : %dmA, step cc curr : %dmA\n", + ibatt_curr, chrg_step->chrg_step_cc_curr); + if ((ibatt_curr > chrg_step->chrg_step_cc_curr) && (chip->pd_request_volt > MINIMUM_CHARGER_VOLT)) { + chip->pd_request_volt -= CV_DELTA_VOLT; + + mmi_chrg_dbg(chip, PR_MOTO, "Reduce pps volt %dmV, curr %dmA\n ", + chip->pd_request_volt, chip->pd_request_curr); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + chrg_cv_taper_tunning_cnt = 0; + } else { + chrg_cv_taper_tunning_cnt++; + + } + + if (chrg_cv_taper_tunning_cnt > CV_TAPPER_COUNT){ + mmi_chrg_sm_move_state(chip, PM_STATE_SW_LOOP); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_STATE_STOP_CHARGE: + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + if (chip->pres_temp_zone != ZONE_COLD + && chip->pres_temp_zone != ZONE_HOT + && chrg_list->chrg_dev[PMIC_SW]->charger_enabled + && chrg_step->chrg_step_cc_curr > 0) { + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + chip->pd_request_volt = SWITCH_CHARGER_PPS_VOLT; + chip->pd_request_curr = TYPEC_HIGH_CURRENT_UA; + break; + case PM_STATE_COOLING_LOOP: + mmi_chrg_info(chip,"In cooling loop, batt temp %d, cmp temp %d\n", + batt_temp, chrg_step->temp_c); + + if (batt_temp > chrg_step->temp_c + COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, Cooling loop failed, " + "force enter PM_STATE_ENTRY " + "restart this charger process !\n", + batt_temp); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + + } else if (batt_temp > chrg_step->temp_c) { + cooling_curr = + min(chip->pd_curr_max, chip->pd_allow_min_current); + if (chip->pd_request_curr > cooling_curr) { + chip->pd_request_curr -= COOLING_DELTA_POWER; + } else { + mmi_chrg_info(chip, "pd request curr %dmA, " + "battery temp %d, " + "cooling failed, Restart PM_STATE_ENTRY !\n", + chip->pd_request_curr, batt_temp); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + } + + } else if (batt_temp < chrg_step->temp_c - COOLING_DELTA_POWER) { + + mmi_chrg_info(chip,"Batt temp %d, successful exit from COOLing loop, " + "and Enter int TUNNING_CURR again !\n", + batt_temp); + mmi_chrg_sm_move_state(chip, + PM_STATE_PPS_TUNNING_CURR); + } + + if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + if (chrg_cv_taper_tunning_cnt > + CV_TAPPER_COUNT) { + + mmi_chrg_info(chip, "vbatt_volt %dmV, " + "battery temp %d, " + "chrg step cv volt %dmV" + "cooling failed, Restart PM_STATE_ENTRY !\n", + vbatt_volt, batt_temp, chrg_step->chrg_step_cv_volt); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + chrg_cv_taper_tunning_cnt = 0; + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV taper cnt %d, " + "chrg step cv volt %dmV, " + "vbatt %dmV\n", + chrg_cv_taper_tunning_cnt, + chrg_step->chrg_step_cv_volt, + vbatt_volt); + } + } else + chrg_cv_taper_tunning_cnt = 0; + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + break; + } + +schedule: + + chip->pd_target_volt = min(chip->pd_request_volt, chip->pd_volt_max); + chip->pd_target_curr = min(chip->pd_request_curr, chip->pd_curr_max); + + if (chip->system_thermal_level == 0) { + chip->sys_therm_cooling= false; + chip->sys_therm_force_pmic_chrg = false; + } else if ((chip->system_thermal_level == chip->thermal_levels - 1) + && !chip->sys_therm_force_pmic_chrg) { + chip->sys_therm_cooling = true; + chip->sys_therm_force_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + mmi_chrg_info(chip, "Thermal is the highest, level %d, " + "Force enter into single pmic charging !\n", + chip->system_thermal_level); + if(chip->pd_sys_therm_volt == 0) + chip->pd_sys_therm_volt = SWITCH_CHARGER_PPS_VOLT; + if(chip->pd_sys_therm_curr == 0) + chip->pd_sys_therm_curr = TYPEC_HIGH_CURRENT_UA; + if(chip->pd_batt_therm_volt == 0) + chip->pd_batt_therm_volt = SWITCH_CHARGER_PPS_VOLT; + if(chip->pd_batt_therm_curr == 0) + chip->pd_batt_therm_curr = TYPEC_HIGH_CURRENT_UA; + + } else if (chip->system_thermal_level > 0 && + (sm_state == PM_STATE_CP_CC_LOOP || + sm_state == PM_STATE_CP_CV_LOOP)) { + + mmi_chrg_dbg(chip, PR_MOTO, "Thermal level is %d\n", + chip->system_thermal_level); + if (!chip->sys_therm_cooling) { + chip->sys_therm_cooling = true; + chip->pd_sys_therm_volt = chip->pd_request_volt_prev; + chip->pd_sys_therm_curr = chip->pd_request_curr_prev; + } + + if (ibatt_curr > + chip->thermal_mitigation[chip->system_thermal_level] + + CC_CURR_DEBOUNCE) { + if (chip->pd_sys_therm_curr - THERMAL_TUNNING_CURR >= + chip->pd_allow_min_current) { + chip->pd_sys_therm_curr -= THERMAL_TUNNING_CURR; + mmi_chrg_dbg(chip, PR_MOTO, "For thermal, decrease pps curr %d\n", + chip->pd_sys_therm_curr); + + } else { + mmi_chrg_dbg(chip, PR_MOTO, + "pd_sys_therm_curr %dmA was less than %dmA, " + "Give up thermal mitigation!", + chip->pd_sys_therm_curr - THERMAL_TUNNING_CURR, + chip->pd_allow_min_current); + } + } else if (ibatt_curr < + chip->thermal_mitigation[chip->system_thermal_level] + - CC_CURR_DEBOUNCE) { + if (chip->pd_sys_therm_curr + THERMAL_TUNNING_CURR <= + chip->pd_curr_max) { + chip->pd_sys_therm_curr += THERMAL_TUNNING_CURR; + mmi_chrg_dbg(chip, PR_MOTO, "For thermal, increase pps curr %d\n", + chip->pd_sys_therm_curr); + } + } + + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + } else if (chip->system_thermal_level > 0 && + chip->system_thermal_level != chip->thermal_levels - 1 && + sm_state == PM_STATE_SW_LOOP && + chip->sys_therm_force_pmic_chrg) { + mmi_chrg_dbg(chip, PR_MOTO, "Try to recovery charger pump!\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } + + if (chip->sys_therm_cooling) { + chip->pd_target_volt = min(chip->pd_target_volt, chip->pd_sys_therm_volt); + chip->pd_target_curr = min(chip->pd_target_curr, chip->pd_sys_therm_curr); + } + + if (chip->batt_therm_cooling && !chip->sys_therm_force_pmic_chrg) { + if (batt_temp > chrg_step->temp_c + COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, Cooling loop failed, " + "force enter PM_STATE_ENTRY " + "restart this charger process !\n", + batt_temp); + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt= 0; + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + + } else if (batt_temp > chrg_step->temp_c) { + cooling_curr = + min(chip->pd_curr_max, chip->pd_allow_min_current); + cooling_volt = (2 * vbatt_volt) % 20000; + cooling_volt = 2 * vbatt_volt - cooling_volt + + chip->pps_volt_comp; + if (ibatt_curr > TYPEC_HIGH_CURRENT_UA + && chip->pd_batt_therm_curr > cooling_curr) { + + if (chip->pd_batt_therm_curr - COOLING_DELTA_POWER >= + chip->pd_allow_min_current) + chip->pd_batt_therm_curr -= COOLING_DELTA_POWER; + mmi_chrg_info(chip, "Do chrg power cooling" + "pd_batt_therm_curr %dmA, " + "battery temp %d\n", + chip->pd_batt_therm_curr, batt_temp); + } else if (ibatt_curr > TYPEC_HIGH_CURRENT_UA + && chip->pd_batt_therm_volt > cooling_volt) { + chip->pd_batt_therm_volt -= COOLING_DELTA_POWER; + mmi_chrg_info(chip, "Do chrg power cooling" + "pd request volt %dmA, " + "battery temp %d\n", + chip->pd_batt_therm_volt, batt_temp); + + } else { + if (chip->batt_therm_cooling_cnt > COOLING_MAX_CNT) { + + mmi_chrg_info(chip, "Do chrg power cooling failed" + "pd_batt_therm_currr %dmA, " + "pd_batt_therm_volt %dmV" + "battery temp %d, " + "Restart PM_STATE_ENTRY !\n", + chip->pd_batt_therm_curr, + chip->pd_batt_therm_volt, + batt_temp); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + + } else { + mmi_chrg_info(chip, "It's already the lowest cooling chrg power" + "waiting for a while, cooling cnt %d, " + "battery temp %d\n", + batt_temp, chip->batt_therm_cooling); + chip->batt_therm_cooling_cnt++; + } + } + + } else if (batt_temp < chrg_step->temp_c - COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, " + "Exit successfully from COOLing loop!\n", + batt_temp); + + if (sm_state == PM_STATE_CP_CC_LOOP) { + mmi_chrg_info(chip,"Jump into CURR tunning" + "for chrg power poerformance!\n"); + mmi_chrg_sm_move_state(chip, + PM_STATE_PPS_TUNNING_CURR); + } + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + } + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + } + + if (chip->batt_therm_cooling) { + chip->pd_target_volt = min(chip->pd_target_volt, chip->pd_batt_therm_volt); + chip->pd_target_curr = min(chip->pd_target_curr, chip->pd_batt_therm_curr); + } + + mmi_chrg_dbg(chip, PR_MOTO, "chrg sm work,%s, " + "battery soc %d, " + "battery temp %d, " + "battery current %d, " + "battery voltage %d, " + "sys therm level %d, " + "pmic therm level %d, " + "sys therm cooling %d, " + "batt therm cooling %d, " + "sys therm force pmic chrg %d, " + "recovery pmic chrg %d\n", + pm_state_str[sm_state], + batt_soc, batt_temp, + ibatt_curr, vbatt_volt, + chip->system_thermal_level, + pmic_sys_therm_level, + chip->sys_therm_cooling, + chip->batt_therm_cooling, + chip->sys_therm_force_pmic_chrg, + chip->recovery_pmic_chrg); + + mmi_chrg_dbg(chip, PR_MOTO, "pd request volt %dmV, " + "pd request curr %dmA, " + "pd target volt %dmV, " + "pd target curr %dmA, " + "sys therm volt %dmV, " + "sys therm curr %dmA, " + "batt therm volt %dmV, " + "batt therm curr %dmA\n", + chip->pd_request_volt, + chip->pd_request_curr, + chip->pd_target_volt, + chip->pd_target_curr, + chip->pd_sys_therm_volt, + chip->pd_sys_therm_curr, + chip->pd_batt_therm_volt, + chip->pd_batt_therm_curr); + + if (chip->pd_target_volt < SWITCH_CHARGER_PPS_VOLT + || chip->pd_target_curr < chip->pd_allow_min_current) { + + if (sm_state == PM_STATE_PPS_TUNNING_CURR + || sm_state == PM_STATE_PPS_TUNNING_VOLT + || sm_state == PM_STATE_CP_CC_LOOP + || sm_state == PM_STATE_CP_CV_LOOP) { + + mmi_chrg_err(chip, "%s, wrong pd voltage or current , " + "request pd voltage %d, " + "request pd current %d\n", + pm_state_str[sm_state], + chip->pd_target_volt, + chip->pd_target_curr); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } + goto skip_pd_select; + } + + chip->pps_result = usbpd_select_pdo(chip->pd_handle, + chip->mmi_pd_pdo_idx, + chip->pd_target_volt, + chip->pd_target_curr); + mmi_set_pps_result_history(chip, chip->pps_result); + if (!chip->pps_result) { + chip->pd_request_volt_prev = chip->pd_target_volt; + chip->pd_request_curr_prev = chip->pd_target_curr; + } + +skip_pd_select: + + if (heartbeat_dely_ms > 0) { + mmi_chrg_err(chip, "schedule work timer %dms\n", heartbeat_dely_ms); + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(heartbeat_dely_ms)); + } else { + mmi_chrg_policy_clear(chip); + chip->sm_work_running = false; + chip->pd_pps_support = false; + mmi_chrg_err(chip, "exit sm work\n"); + } + + return; +} + + diff --git a/drivers/power/mmi_parallel_charger_iio/mmi_cp_charger.c b/drivers/power/mmi_parallel_charger_iio/mmi_cp_charger.c new file mode 100644 index 000000000000..2d6f4939781e --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/mmi_cp_charger.c @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include +#include "mmi_charger_core.h" +#include "mmi_charger_core_iio.h" + +static int cp_enable_charging(struct mmi_charger_device *chrg, bool en) +{ + int rc; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_write_iio_chan(chip, CP_ENABLE, en); + + if (!rc) { + chrg->charger_enabled = !!en; + } else + chrg->charger_enabled = false; + + return rc; +} + +static int cp_is_charging_enabled(struct mmi_charger_device *chrg, bool *en) +{ + int rc; + int value; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_read_iio_chan(chip, CP_ENABLE, &value); + + if (!rc) { + chrg->charger_enabled = !!value; + } else + chrg->charger_enabled = false; + + *en = chrg->charger_enabled; + + return rc; +} + +static int cp_get_charging_current(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + *uA = !!prop.intval; + + return rc; +} + +static int cp_get_vbus(struct mmi_charger_device *chrg, u32 *mv) +{ + int rc, val; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + if (!chip) + return -ENODEV; + + rc = mmi_charger_read_iio_chan(chip, CP_INPUT_VOLTAGE_NOW, &val); + if (!rc) + *mv = val; + + return rc; +} + +static int cp_get_input_current(struct mmi_charger_device *chrg, u32 *uA) //ibus +{ + int rc; + int value; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_read_iio_chan(chip, CP_INPUT_CURRENT_NOW, &value); + + if (!rc) + *uA = value; + + return rc; +} + +static int cp_update_charger_status(struct mmi_charger_device *chrg) +{ + int rc; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &prop); + if (!rc) + chrg->charger_data.ibus_curr= prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + chrg->charger_data.vbatt_volt = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + chrg->charger_data.ibatt_curr = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_TEMP, &prop); + if (!rc) + chrg->charger_data.batt_temp = prop.intval; + + rc = mmi_charger_read_iio_chan(chip, CP_INPUT_VOLTAGE_NOW, &prop.intval); + if (!rc) + chrg->charger_data.vbus_volt = prop.intval; + + rc = mmi_charger_read_iio_chan(chip, CP_INPUT_CURRENT_NOW, &prop.intval); + if (!rc) + chrg->charger_data.ibus_curr = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (!rc) + chrg->charger_data.vbus_pres = !!prop.intval; + + rc = mmi_charger_read_iio_chan(chip, CP_ENABLE, &prop.intval); + if (!rc) + chrg->charger_enabled = !!prop.intval; + + chrg_dev_info(chrg, "CP chrg: %s status update: --- info---\n",chrg->name); + chrg_dev_info(chrg, "vbatt %d\n", chrg->charger_data.vbatt_volt); + chrg_dev_info(chrg, "ibatt %d\n", chrg->charger_data.ibatt_curr); + chrg_dev_info(chrg, "batt temp %d\n", chrg->charger_data.batt_temp); + chrg_dev_info(chrg, "vbus %d\n", chrg->charger_data.vbus_volt); + chrg_dev_info(chrg, "ibus %d\n", chrg->charger_data.ibus_curr); + chrg_dev_info(chrg, "vbus pres %d\n", chrg->charger_data.vbus_pres); + chrg_dev_info(chrg, "charger_enabled %d\n", chrg->charger_enabled); + + return rc; +} + +static int cp_update_charger_error_status(struct mmi_charger_device *chrg) +{ + int rc; + int value = 0; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_read_iio_chan(chip, CP_STATUS1, &value); + if (!rc) { + chrg->charger_error.chrg_err_type = value; + } + return rc; +} + +static int cp_clear_charger_error(struct mmi_charger_device *chrg) +{ + int rc; + int value = 0; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_write_iio_chan(chip, CP_CLEAR_ERROR, value); + + return rc; +} + +struct mmi_charger_ops cp_charger_ops = { + .enable = cp_enable_charging, + .is_enabled = cp_is_charging_enabled, + .get_charging_current = cp_get_charging_current, + .get_vbus = cp_get_vbus, + .get_input_current = cp_get_input_current, + .update_charger_status = cp_update_charger_status, + .update_charger_error = cp_update_charger_error_status, + .clear_charger_error = cp_clear_charger_error, +}; + diff --git a/drivers/power/mmi_parallel_charger_iio/mmi_qc3p.c b/drivers/power/mmi_parallel_charger_iio/mmi_qc3p.c new file mode 100644 index 000000000000..bc465469c5f1 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/mmi_qc3p.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2012 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 "mmi_charger_core.h" +#include "mmi_charger_core_iio.h" +#include + +bool mmi_qc3p_power_active(struct mmi_charger_manager *chg) +{ + int ret, iio_val; + + ret = mmi_charger_read_iio_chan(chg, SMB5_USB_REAL_TYPE, &iio_val); + if (ret < 0) { + mmi_chrg_err(chg, "Couldn't read charger type rc=%d\n", ret); + return false; + } + if (iio_val != QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5) + return false; + + ret = mmi_charger_read_iio_chan(chg, SMB5_QC3P_POWER, &iio_val); + if (ret < 0) { + mmi_chrg_err(chg, "Couldn't read qc3p power rc=%d\n", ret); + return false; + } + chg->qc3p_power = iio_val; + mmi_chrg_info(chg, "qc3p_power =%d\n", chg->qc3p_power); + + if (chg->qc3p_power == QTI_POWER_SUPPLY_QC3P_27W || + chg->qc3p_power == QTI_POWER_SUPPLY_QC3P_45W) + return true; + else + return false; +} + +#define QC3P_PULSE_COUNT_MAX ((11000 - 5000) / 20 + 10) +int mmi_qc3p_set_vbus_voltage(struct mmi_charger_manager *chg, int target_mv) +{ + int rc = -EINVAL; + union power_supply_propval val = {0, }; + int pulse_cnt, curr_vbus_mv, step, i; + + rc = mmi_charger_read_iio_chan(chg, SMB5_DP_DM, &val.intval); + if (rc < 0) { + mmi_chrg_err(chg, "Couldn't read dpdm pulse count rc=%d\n", rc); + return -EINVAL; + } else { + mmi_chrg_info(chg, "DP DM pulse count = %d\n", val.intval); + pulse_cnt = val.intval; + } + + rc = mmi_get_vbus(chg->chrg_list[CP_MASTER], + &curr_vbus_mv); + if (rc) { + mmi_chrg_err(chg, "Unable to read USB voltage: %d\n", rc); + return -EINVAL; + } + + if(target_mv < curr_vbus_mv) { + step = (curr_vbus_mv - target_mv) / 20; + val.intval = QTI_POWER_SUPPLY_DP_DM_DM_PULSE; + if (pulse_cnt <= step) + step = pulse_cnt; + } else { + step = (target_mv - curr_vbus_mv) / 20; + val.intval = QTI_POWER_SUPPLY_DP_DM_DP_PULSE; + if (step + pulse_cnt > QC3P_PULSE_COUNT_MAX) + step = QC3P_PULSE_COUNT_MAX - pulse_cnt; + } + + mmi_chrg_info(chg, "curr_vbus= %d, target_vbus = %d, step = %d\n",curr_vbus_mv, target_mv, step); + for (i = 0; i < step; i++) { + rc = mmi_charger_write_iio_chan(chg, SMB5_DP_DM, val.intval); + if (rc < 0) { + mmi_chrg_err(chg, "Couldn't set dpdm pulse rc=%d\n", rc); + break; + } + + if (i < step - 1) + udelay(5000); + } + + rc = mmi_get_vbus(chg->chrg_list[CP_MASTER], + &curr_vbus_mv); + if (rc) { + mmi_chrg_err(chg, "Unable to read VBUS: %d\n", rc); + return -EINVAL; + } + mmi_chrg_info(chg, "result_vbus= %d\n",curr_vbus_mv); + return curr_vbus_mv; +} diff --git a/drivers/power/mmi_parallel_charger_iio/mmi_qc3p.h b/drivers/power/mmi_parallel_charger_iio/mmi_qc3p.h new file mode 100644 index 000000000000..7b3af65832c8 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/mmi_qc3p.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + +extern bool mmi_qc3p_power_active(struct mmi_charger_manager *chg); +extern int mmi_qc3p_set_vbus_voltage(struct mmi_charger_manager *chg, int target_mv); + diff --git a/drivers/power/mmi_parallel_charger_iio/mmi_qc3p_cp_policy.c b/drivers/power/mmi_parallel_charger_iio/mmi_qc3p_cp_policy.c new file mode 100644 index 000000000000..5ee74c14864d --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/mmi_qc3p_cp_policy.c @@ -0,0 +1,1217 @@ +/* + * Copyright (c) 2021 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_core.h" +#include "mmi_qc3p.h" + +#define SWITCH_CHARGER_QC3P_VOLT 5000000 +#define MINIMUM_CHARGER_VOLT 4700000 + +typedef enum { + PM_QC3P_STATE_DISCONNECT, + PM_QC3P_STATE_ENTRY, + PM_QC3P_STATE_SW_ENTRY, + PM_QC3P_STATE_SW_LOOP, + PM_QC3P_STATE_CHRG_PUMP_ENTRY, + PM_QC3P_STATE_SINGLE_CP_ENTRY, + PM_QC3P_STATE_DULE_CP_ENTRY, + PM_QC3P_STATE_TUNNING_VOLT, + PM_QC3P_STATE_CP_CC_LOOP, + PM_QC3P_STATE_CP_CV_LOOP, + PM_QC3P_STATE_CP_QUIT, + PM_QC3P_STATE_RECOVERY_SW, + PM_QC3P_STATE_STOP_CHARGE, +} pm_sm_qc3p_state_t; + +const unsigned char *pm_qc3p_state_str[] = { + "PM_QC3P_STATE_DISCONNECT", + "PM_QC3P_STATE_ENTRY", + "PM_QC3P_STATE_SW_ENTRY", + "PM_QC3P_STATE_SW_LOOP", + "PM_QC3P_STATE_CHRG_PUMP_ENTRY", + "PM_QC3P_STATE_SINGLE_CP_ENTRY", + "PM_QC3P_STATE_DULE_CP_ENTRY", + "PM_QC3P_STATE_TUNNING_VOLT", + "PM_QC3P_STATE_CP_CC_LOOP", + "PM_QC3P_STATE_CP_CV_LOOP", + "PM_QC3P_STATE_CP_QUIT", + "PM_QC3P_STATE_RECOVERY_SW", + "PM_QC3P_STATE_STOP_CHARGE", +}; + +static pm_sm_qc3p_state_t qc3p_sm_state = PM_QC3P_STATE_DISCONNECT; +static int qc3p_chrg_cc_power_tunning_cnt = 0; +static int qc3p_chrg_cv_taper_tunning_cnt = 0; +static int qc3p_chrg_cv_delta_volt = 0; +static int qc3p_quit_slave_chrg_cnt = 0; +static int qc3p_batt_curr_roof = 0; +static int qc3p_constant_power_cnt = 0; + +static void mmi_chrg_qc3p_sm_move_state(struct mmi_charger_manager *chip, pm_sm_qc3p_state_t state) +{ + mmi_chrg_dbg(chip, PR_INTERRUPT, "pm_state change:%s -> %s\n", + pm_qc3p_state_str[qc3p_sm_state], pm_qc3p_state_str[state]); + qc3p_sm_state = state; + qc3p_constant_power_cnt = 0; + qc3p_batt_curr_roof = 0; +} + +#define QC3P_VOLT_COMP_DELTA 300000 +static void qc3p_chrg_policy_error_recovery(struct mmi_charger_manager *chip, + struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num = 0, i = 0, chrg_error_type= 0; + struct mmi_charger_device *chrg_dev; + chrg_num = chip->mmi_chrg_dev_num; + + for (i = 0; i < chrg_num; i++) { + + switch (i) { + case PMIC_SW: + break; + case CP_MASTER: + if (is_charger_exist(dev_ops[CP_MASTER].dev_name)) { + chrg_dev = chrg_list->chrg_dev[CP_MASTER]; + chrg_error_type = chrg_dev->charger_error.chrg_err_type; + if (chrg_error_type & (1 << MMI_BUS_UCP_ALARM_BIT) || + chrg_error_type & (1 << MMI_BUS_UCP_FAULT_BIT)) { + mmi_chrg_info(chip,"CP master bus ucp error %d, cnt %d," + "qc3p volt comp %dmV\n", + chrg_error_type, + chrg_dev->charger_error.bus_ucp_err_cnt, + chip->qc3p_volt_comp); + + if (chrg_dev->charger_error.bus_ucp_err_cnt > 3) { + if (chrg_list->cp_slave) { + chrg_list->cp_slave = false; + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_CHRG_PUMP_ENTRY); + } else { + chip->qc3p_recovery_pmic_chrg = true; + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + } + } else if (chrg_dev->charger_error.bus_ucp_err_cnt > 6) { + chip->qc3p_recovery_pmic_chrg = true; + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + } + + chrg_dev->charger_error.bus_ucp_err_cnt++; + chip->qc3p_volt_comp += QC3P_VOLT_COMP_DELTA; + mmi_chrg_info(chip,"Restart charging, " + "increase qc3p volt comp %dmV\n", + chip->qc3p_volt_comp); + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_CHRG_PUMP_ENTRY); + mmi_clear_charger_error(chrg_dev); + }else if (chrg_error_type & (1 << MMI_BUS_OCP_ALARM_BIT) || + chrg_error_type & (1 << MMI_BUS_OCP_FAULT_BIT) || + chrg_error_type & (1 << MMI_CONV_OCP_FAULT_BIT)) { + mmi_chrg_info(chip,"CP master ocp error %d, cnt %d," + "qc3p volt comp %dmV\n", + chrg_error_type, + chrg_dev->charger_error.bus_ocp_err_cnt, + chip->qc3p_volt_comp); + if (chrg_dev->charger_error.bus_ocp_err_cnt > 3) { + chip->qc3p_recovery_pmic_chrg = true; + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ocp_err_cnt = 0; + } + chrg_dev->charger_error.bus_ocp_err_cnt++; + chip->qc3p_volt_comp -= QC3P_VOLT_COMP_DELTA; + if (chip->qc3p_volt_comp < 0) + chip->qc3p_volt_comp = 0; + mmi_chrg_info(chip,"Restart charging, " + "decrease qc3p volt comp %dmV\n", + chip->qc3p_volt_comp); + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_CHRG_PUMP_ENTRY); + mmi_clear_charger_error(chrg_dev); + } + } + break; + case CP_SLAVE: + break; + default: + mmi_chrg_err(chip,"No mmi_chrg_dev found %d !\n",i); + break; + } + } + return; +} + +void mmi_qc3p_chrg_enable_all_cp(struct mmi_charger_manager *chip, int val) +{ + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + bool enable = !!val; + + mmi_chrg_dbg(chip, PR_MOTO,"enable all cp = %d\n", enable); + if (enable) { + if (chip->cp_disable == false) + return; + + cancel_delayed_work_sync(&chip->mmi_qc3p_chrg_sm_work); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CHRG_PUMP_ENTRY); + chip->cp_disable = false; + chip->qc3p_recovery_pmic_chrg = false; + schedule_delayed_work(&chip->mmi_qc3p_chrg_sm_work, + msecs_to_jiffies(0)); + + } else { + if(chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) + return; + + cancel_delayed_work_sync(&chip->mmi_qc3p_chrg_sm_work); + + chip->cp_disable = true; + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_RECOVERY_SW); + schedule_delayed_work(&chip->mmi_qc3p_chrg_sm_work, + msecs_to_jiffies(0)); + } +} + +#define QC3P_HEARTBEAT_SHORT_DELAY_MS 1000 +#define QC3P_HEARTBEAT_lOOP_WAIT_MS 3000 +#define HEARTBEAT_THERMAL_DELAY_MS 300 +#define QC3P_HEARTBEAT_TUNNING_MS 100 +#define QC3P_HEARTBEAT_NEXT_STATE_MS 100 +#define QC3P_HEARTBEAT_CANCEL -1 +#define QC3P_CC_CURR_DEBOUNCE 100000 +#define QC3P_CV_TAPPER_COUNT 3 +#define QC3P_CC_POWER_COUNT 3 +#define QC3P_CV_DELTA_VOLT 100000 +#define QC3P_COOLING_DELTA_POWER 100000 +#define QC3P_COOLING_MAX_CNT 5 +#define QC3P_DISABLE_CHRG_LIMIT -1 +#define QC3P_CP_CHRG_SOC_LIMIT 90 +#define QC3P_CONT_PWR_CNT 5 + +void qc3p_clear_chg_manager(struct mmi_charger_manager *chip) +{ + mmi_chrg_dbg(chip, PR_INTERRUPT, "clear mmi qc3p chrg manager!\n"); + chip->qc3p_request_volt = 0; + chip->qc3p_request_volt_prev = 0; + chip->qc3p_target_volt = 0; + chip->qc3p_batt_therm_volt = 0; + chip->qc3p_sys_therm_volt = 0; + chip->qc3p_recovery_pmic_chrg = false; + chip->qc3p_sys_therm_cooling= false; + chip->qc3p_sys_therm_force_pmic_chrg = false; + chip->qc3p_batt_therm_cooling = false; + chip->qc3p_batt_therm_cooling_cnt = 0; + +} + +void mmi_qc3p_chrg_policy_clear(struct mmi_charger_manager *chip) { + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + chrg_dev_init(chip, &g_chrg_list); + clear_chrg_dev_error_cnt(chip, &g_chrg_list); + qc3p_clear_chg_manager(chip); + if (chrg_list->cp_slave) + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + if (chrg_list->cp_master) + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + QC3P_DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + qc3p_sm_state = PM_QC3P_STATE_DISCONNECT; + chip->qc3p_volt_comp = QC3P_INIT_VOLT_COMP; + qc3p_quit_slave_chrg_cnt = 0; + qc3p_chrg_cc_power_tunning_cnt = 0; + qc3p_chrg_cv_taper_tunning_cnt = 0; + qc3p_chrg_cv_delta_volt = 0; + qc3p_constant_power_cnt = 0; + qc3p_batt_curr_roof = 0; + return; +} + +void mmi_qc3p_chrg_sm_work_func(struct work_struct *work) +{ + struct mmi_charger_manager *chip = container_of(work, + struct mmi_charger_manager, mmi_qc3p_chrg_sm_work.work); + int i = 0, rc = 0; + int ibatt_curr = 0, vbatt_volt = 0, batt_temp = 0, vbus_pres = 0; + int ibatt_vote = 0; + int batt_soc = 0; + int heartbeat_dely_ms = 0; + int pmic_sys_therm_level = 0; + bool zone_change = false; + int ibus_total_ma = 0; + int cooling_volt = 0; + bool ignore_hysteresis_degc = false; + struct mmi_chrg_step_info *chrg_step; + union power_supply_propval prop = {0,}; + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + + mmi_chrg_dbg(chip, PR_MOTO, "\n\n\n"); + + mmi_chrg_dbg(chip, PR_MOTO, "schedule SM work, sm state %s \n", + pm_qc3p_state_str[qc3p_sm_state]); + + mmi_chrg_dbg(chip, PR_MOTO, "pmic-sw is exist: %d " + "cp-master is exist: %d " + "cp-slave is exist: %d\n", + chrg_list->pmic_sw, + chrg_list->cp_master, + chrg_list->cp_slave); + if (!chrg_list->pmic_sw) { + mmi_chrg_err(chip,"PMIC-SW isn't exist, force quite mmi chrg sm work !\n"); + return; + } + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &prop); + if (!rc) + pmic_sys_therm_level = prop.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + ibatt_curr = prop.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_TEMP, &prop); + if (!rc) + batt_temp = prop.intval / 10; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &prop); + if (!rc) + batt_soc = prop.intval; + + + if (ibatt_curr < 0) + ibatt_curr *= -1; + + + mmi_update_all_charger_status(chip); + mmi_update_all_charger_error(chip); + + if (chip->extrn_sense) { + ibatt_curr = chrg_list->chrg_dev[CP_MASTER]->charger_data.ibatt_curr; + ibatt_curr *= 1000; + if (ibatt_curr < 0) + ibatt_curr *= -1; + } + vbatt_volt = chrg_list->chrg_dev[CP_MASTER]->charger_data.vbatt_volt; + vbatt_volt *= 1000; + + vbus_pres = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbus_pres; + if (!vbus_pres) { + for (i = 0; i < 3; i++) { + mmi_update_all_charger_status(chip); + vbus_pres = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbus_pres; + mmi_chrg_info(chip, "Retry check charger status, vbus %d\n", vbus_pres); + if (vbus_pres) { + mmi_chrg_info(chip, "Get vbus present , continue to charging\n"); + break; + } + msleep(100); + } + } + + mmi_chrg_info(chip, "battery current %d\n", ibatt_curr); + mmi_chrg_info(chip, "battery voltage %d\n", vbatt_volt); + mmi_chrg_info(chip, "battery temp %d\n", batt_temp); + mmi_chrg_info(chip, "battery capacity %d\n", batt_soc); + + if (vbus_pres && mmi_is_cable_plugout(chip)) + vbus_pres = 0; + + if (vbus_pres && + (qc3p_sm_state == PM_QC3P_STATE_TUNNING_VOLT + || qc3p_sm_state == PM_QC3P_STATE_CP_CC_LOOP + || qc3p_sm_state == PM_QC3P_STATE_CP_CV_LOOP)) { + mmi_dump_charger_error(chip, chrg_list->chrg_dev[CP_MASTER]); + qc3p_chrg_policy_error_recovery(chip, chrg_list); + } + + if (qc3p_sm_state == PM_QC3P_STATE_CP_CV_LOOP) + ignore_hysteresis_degc = true; + zone_change = mmi_find_temp_zone(chip, batt_temp, ignore_hysteresis_degc); + chrg_step = &chip->chrg_step; + + if (chip->pres_temp_zone == ZONE_COLD + || chip->pres_temp_zone == ZONE_HOT + || !chrg_list->chrg_dev[PMIC_SW]->charger_enabled + || vbatt_volt > chip->batt_ovp_lmt) { + + mmi_chrg_info(chip, "Force stop charging, " + "pres_temp_zone %d, " + "pmic charger enabled %d, " + "vbatt_volt %dmv, " + "batt ovp limit %dmv\n", + chip->pres_temp_zone, + chrg_list->chrg_dev[PMIC_SW]->charger_enabled, + vbatt_volt, chip->batt_ovp_lmt); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_STOP_CHARGE); + } + + if (!vbus_pres) { + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_DISCONNECT); + } else if (qc3p_sm_state == PM_QC3P_STATE_ENTRY + || qc3p_sm_state == PM_QC3P_STATE_STOP_CHARGE) { + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + } else if (qc3p_sm_state == PM_QC3P_STATE_DISCONNECT) { + mmi_get_input_current_settled(chrg_list->chrg_dev[PMIC_SW], + &chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_ENTRY); + } else if (zone_change && + chip->pres_temp_zone != ZONE_COLD && + chip->pres_temp_zone != ZONE_HOT) { + + if (batt_temp >= chrg_step->temp_c) { + mmi_chrg_info(chip, "battery temp %d, temp thre %d " + "Enter into COOLING LOOP !\n", + batt_temp, chrg_step->temp_c); + chip->qc3p_batt_therm_cooling = true; + chip->qc3p_batt_therm_volt = chip->qc3p_request_volt_prev; + } else if (!chip->qc3p_batt_therm_cooling && + !ignore_hysteresis_degc) { + mmi_chrg_info(chip, "battery temp %d, temp thre %d " + "Restart select chrg step and temp zone !\n", + batt_temp, chrg_step->temp_c); + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_ENTRY); + } + } + + mmi_chrg_dbg(chip, PR_MOTO, "temp zone %d, is_changed %d, " + "chrg_step %d, " + "step cc curr %d, step cv volt %d, " + "step cv tapper curr %d\n", + chip->pres_temp_zone, zone_change, + chrg_step->pres_chrg_step, + chrg_step->chrg_step_cc_curr, + chrg_step->chrg_step_cv_volt, + chrg_step->chrg_step_cv_tapper_curr); + switch (qc3p_sm_state) { + case PM_QC3P_STATE_DISCONNECT: + mmi_chrg_info(chip,"vbus disconnect !, jump to PM_QC3P_STATE_DISCONNECT," + "recovery PMIC-SW limitation, and close CP charg\n"); + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + QC3P_DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + heartbeat_dely_ms = QC3P_HEARTBEAT_CANCEL; + break; + case PM_QC3P_STATE_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chip->qc3p_active + && chrg_list->cp_master + && vbatt_volt > chip->pl_chrg_vbatt_min + && chrg_step->pres_chrg_step != chip->chrg_step_nums - 1 + && chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min + && batt_soc < QC3P_CP_CHRG_SOC_LIMIT) { + + mmi_chrg_dbg(chip, PR_MOTO, "Enter into CHRG PUMP, " + "vbatt %d uV, " + "qc3p support %d, " + "chrg step %d, " + "chrg step cc curr %d uA, " + "CP master charging curr min %d uA\n", + vbatt_volt, + chip->qc3p_active, + chrg_step->pres_chrg_step, + chrg_step->chrg_step_cc_curr, + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CHRG_PUMP_ENTRY); + + } else { + mmi_chrg_dbg(chip, PR_MOTO, "Enter into PMIC switch charging, " + "the reason is : vbatt %d uV, " + "pl chrg vbatt min %d uV, " + "qc3p support %d, " + "chrg step %d\n", + vbatt_volt, chip->pl_chrg_vbatt_min, + chip->qc3p_active, + chrg_step->pres_chrg_step); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_ENTRY); + + } + chip->qc3p_batt_therm_cooling = false; + chip->qc3p_batt_therm_cooling_cnt= 0; + chip->qc3p_sys_therm_volt= chip->qc3p_request_volt_prev; + chip->qc3p_batt_therm_volt = chip->qc3p_request_volt_prev; + chip->qc3p_ibus_max_volt = chip->qc3p_request_volt_prev; + chip->qc3p_sys_therm_cooling = false; + chip->qc3p_sys_therm_force_pmic_chrg = false; + chip->qc3p_recovery_pmic_chrg = false; + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + break; + case PM_QC3P_STATE_SW_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip, "Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + QC3P_DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + if (!chip->dont_rerun_aicl) { + mmi_chrg_info(chip, "Do an rerun usb AICL for PMIC-SW\n"); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], false); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], true); + } + chip->qc3p_request_volt = SWITCH_CHARGER_QC3P_VOLT; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_LOOP); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + break; + case PM_QC3P_STATE_SW_LOOP: + if (chip->qc3p_active + && chip->cp_disable == false + && vbatt_volt > chip->pl_chrg_vbatt_min + && chrg_step->pres_chrg_step != chip->chrg_step_nums - 1 + && chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min + && !chip->qc3p_recovery_pmic_chrg + && !chip->qc3p_sys_therm_force_pmic_chrg + && batt_soc < QC3P_CP_CHRG_SOC_LIMIT) { + mmi_chrg_info(chip, "Enter CP, the reason is : " + "qc3p support %d, " + "vbatt %duV, chrg step %d\n", + chip->qc3p_active, + vbatt_volt, chrg_step->pres_chrg_step); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CHRG_PUMP_ENTRY); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } else { + mmi_chrg_dbg(chip, PR_MOTO, "Continue to SW charging, " + "vbatt %d uV, ibatt %d uA\n", + vbatt_volt, ibatt_curr); + heartbeat_dely_ms = QC3P_HEARTBEAT_lOOP_WAIT_MS; + } + break; + case PM_QC3P_STATE_CHRG_PUMP_ENTRY: + mmi_chrg_info(chip,"CP master exist %d, CP slave exist %d !\n", + chrg_list->cp_master, + chrg_list->cp_slave); + if (chrg_list->cp_slave) { + mmi_chrg_info(chip,"CP slave is exist !\n"); + mmi_chrg_info(chip,"chrg step cc curr %d uA, " + "CP slave charging curr min %d uA\n", + chrg_step->chrg_step_cc_curr, + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min); + if (chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min) { + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_DULE_CP_ENTRY); + } else + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_SINGLE_CP_ENTRY); + } else { + mmi_chrg_info(chip,"CP slave isn't exist !\n"); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SINGLE_CP_ENTRY); + } + + mmi_chrg_info(chip,"Set PMIC SW FCC limits!\n"); + if (!chrg_list->chrg_dev[PMIC_SW]->charger_limited + && chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited > 0) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited); + mmi_chrg_info(chip,"Set PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = true; + } + + if (batt_soc >= 65) { + //Read the vbat aggin after the soc above 65% + msleep(20); + rc = power_supply_get_property(chrg_list->chrg_dev[CP_MASTER]->chrg_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + vbatt_volt = prop.intval*1000; + } + + /*Initial setup qc3p request power by the battery voltage*/ + chip->qc3p_request_volt = (2 * vbatt_volt) % 20000; + chip->qc3p_request_volt = 2 * vbatt_volt - chip->qc3p_request_volt + + chip->qc3p_volt_comp; + mmi_chrg_info(chip,"qc3p init , volt %dmV, volt comp %dmv\n", + chip->qc3p_request_volt, chip->qc3p_volt_comp); + chrg_policy_error_clear(chip, chrg_list); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + break; + case PM_QC3P_STATE_SINGLE_CP_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_chrg_info(chip,"Disable Slave Charger Pump !\n"); + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Enable Master Charger Pump !\n"); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_TUNNING_VOLT); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_QC3P_STATE_DULE_CP_ENTRY: + if (chrg_list->cp_master) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Enable Master Charger Pump !\n"); + } + + if (chrg_list->cp_slave) { + chrg_list->cp_clave_later = true; + mmi_chrg_info(chip,"For ibus UCP, " + "delay start Slave Charger Pump in TUNNING_VOLT stage !\n"); + } + + if (chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_TUNNING_VOLT); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_QC3P_STATE_TUNNING_VOLT: + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + if (chrg_list->cp_slave + && chrg_list->cp_clave_later + && !chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], true); + mmi_chrg_info(chip,"Enable Slave Charger Pump !\n"); + } + + if (chrg_list->cp_master + && (!chrg_list->chrg_dev[CP_MASTER]->charger_enabled + || !(chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, " + "Enter into SW directly\n"); + chip->qc3p_volt_comp = QC3P_INIT_VOLT_COMP; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_ENTRY); + } else if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + chip->qc3p_request_volt -= chip->qc3p_volt_steps; + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_CP_CC_LOOP); + mmi_chrg_info(chip,"Duing the volt going up process, " + "the chrg step was changed," + "stop increase qc3p volt and" + " Enter into CC stage as soon!\n"); + } else if (chip->qc3p_request_volt + chip->qc3p_volt_steps + <= chip->qc3p_volt_max + && vbatt_volt < chrg_step->chrg_step_cv_volt + && ibatt_curr < ((chrg_step->pres_chrg_step == STEP_FIRST) ? + chrg_step->chrg_step_cc_curr + chip->step_first_curr_comp: + chrg_step->chrg_step_cc_curr)) { + chip->qc3p_request_volt += chip->qc3p_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, "Increase qc3p volt %d\n", + chip->qc3p_request_volt); + heartbeat_dely_ms = QC3P_HEARTBEAT_TUNNING_MS; + } else { + mmi_chrg_info(chip,"Enter into CC loop stage !\n"); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CP_CC_LOOP); + } + + if (ibatt_curr > qc3p_batt_curr_roof) { + qc3p_batt_curr_roof = ibatt_curr; + } + if (ibatt_curr < qc3p_batt_curr_roof) + qc3p_constant_power_cnt++; + else + qc3p_constant_power_cnt = 0; + + if (qc3p_constant_power_cnt > QC3P_CONT_PWR_CNT) { + mmi_chrg_info(chip,"QC3P adapter was ready in constant power state, " + "Enter into CC loop stage !\n"); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CP_CC_LOOP); + } + mmi_chrg_info(chip, "qc3p_constant_power_cnt %d, " + "qc3p_batt_curr_roof %d\n", + qc3p_constant_power_cnt, + qc3p_batt_curr_roof); + break; + case PM_QC3P_STATE_CP_CC_LOOP: + heartbeat_dely_ms = QC3P_HEARTBEAT_lOOP_WAIT_MS; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CC loop, chrg_step %d, " + "vbatt %dmV, ibatt %dmA, " + "CC target curr %dmA, " + "next CV target volt %dmV\n ", + chrg_step->pres_chrg_step, + vbatt_volt, ibatt_curr, + chrg_step->chrg_step_cc_curr, + chrg_step->chrg_step_cv_volt); + if (chrg_list->cp_master + && (!chrg_list->chrg_dev[CP_MASTER]->charger_enabled + || !(chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into SW directly\n"); + chip->qc3p_volt_comp = QC3P_INIT_VOLT_COMP; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_ENTRY); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + goto schedule; + } + + if (!chip->qc3p_sys_therm_cooling + && !chip->qc3p_batt_therm_cooling + && qc3p_constant_power_cnt <= QC3P_CONT_PWR_CNT) { + if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->qc3p_request_volt < chip->qc3p_volt_max + && qc3p_chrg_cc_power_tunning_cnt >= + QC3P_CC_POWER_COUNT) { + if (chip->qc3p_request_volt + chip->qc3p_volt_steps + < chip->qc3p_volt_max) { + chip->qc3p_request_volt += + chip->qc3p_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "Request volt increass %duV\n ", + chip->qc3p_request_volt); + } + qc3p_chrg_cc_power_tunning_cnt = 0; + }else if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->qc3p_request_volt < chip->qc3p_volt_max) { + qc3p_chrg_cc_power_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CC tunning cnt %d\n", + qc3p_chrg_cc_power_tunning_cnt); + } else if (ibatt_curr > chrg_step->chrg_step_cc_curr + + QC3P_CC_CURR_DEBOUNCE) { + chip->qc3p_request_volt -= chip->qc3p_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "In the CC step , the ibatt is greater than CC curr, " + "Request volt decreass %duV to remain CC step\n ", + chip->qc3p_request_volt); + } else + qc3p_chrg_cc_power_tunning_cnt = 0; + + if(ibatt_curr < chrg_step->chrg_step_cc_curr) + heartbeat_dely_ms = QC3P_HEARTBEAT_SHORT_DELAY_MS; + } + + if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + if (qc3p_chrg_cv_taper_tunning_cnt > + QC3P_CV_TAPPER_COUNT) { + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CP_CV_LOOP); + qc3p_chrg_cv_taper_tunning_cnt = 0; + qc3p_chrg_cv_delta_volt = QC3P_CV_DELTA_VOLT; + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } else { + qc3p_chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV taper cnt %d, " + "chrg step cv volt %dmV, " + "vbatt %dmV\n", + qc3p_chrg_cv_taper_tunning_cnt, + chrg_step->chrg_step_cv_volt, + vbatt_volt); + } + } else + qc3p_chrg_cv_taper_tunning_cnt = 0; + + break; + case PM_QC3P_STATE_CP_CV_LOOP: + heartbeat_dely_ms = QC3P_HEARTBEAT_SHORT_DELAY_MS; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV loop, chrg_step %d, " + "vbatt %dmV, ibatt %dmA, " + "CV target volt %dmV, " + "CV taper curr %dmA\n ", + chrg_step->pres_chrg_step, + vbatt_volt, ibatt_curr, + chrg_step->chrg_step_cv_volt, + chrg_step->chrg_step_cv_tapper_curr); + if (chrg_list->cp_master + && (!chrg_list->chrg_dev[CP_MASTER]->charger_enabled + || !(chrg_list->chrg_dev[CP_MASTER]->charger_error.chrg_err_type & (1<< MMI_CP_SWITCH_BIT)))) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into SW directly\n"); + chip->qc3p_volt_comp = QC3P_INIT_VOLT_COMP; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_ENTRY); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + goto schedule; + } + + if (vbatt_volt >= chrg_step->chrg_step_cv_volt + && ((!chrg_step->last_step && + ibatt_curr < chrg_step->chrg_step_cv_tapper_curr) + || ibatt_curr < chrg_list->chrg_dev[CP_MASTER]->charging_curr_min)) { + if (qc3p_chrg_cv_taper_tunning_cnt >= QC3P_CV_TAPPER_COUNT) { + if (ibatt_curr < + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min) { + mmi_chrg_info(chip, "Ready quite CP chrg stage, " + "and Enter into PMIC switch chrg stage, " + "chrg step %d, ibatt %dmA\n", + chrg_step->pres_chrg_step, ibatt_curr); + mmi_find_chrg_step(chip, + chip->pres_temp_zone, vbatt_volt); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CP_QUIT); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } else { + if (mmi_find_chrg_step(chip, + chip->pres_temp_zone, vbatt_volt)) { + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + mmi_chrg_info(chip,"Jump to next chrg step\n"); + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_CP_CC_LOOP); + } else { + mmi_chrg_info(chip,"Can't find next chrg step\n"); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_CP_QUIT); + } + } + qc3p_chrg_cv_taper_tunning_cnt = 0; + } else { + qc3p_chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, "chrg cv taper cnt ++, %d\n", + qc3p_chrg_cv_taper_tunning_cnt); + } + + }else if (!chip->qc3p_sys_therm_cooling + && !chip->qc3p_batt_therm_cooling) { + + qc3p_chrg_cv_taper_tunning_cnt = 0; + if (vbatt_volt > chrg_step->chrg_step_cv_volt + 10000) { + if (qc3p_chrg_cv_delta_volt > 20000) + chip->qc3p_request_volt -= qc3p_chrg_cv_delta_volt; + else + chip->qc3p_request_volt -= 20000; + mmi_chrg_info(chip, + "For keeping CV stage, decrease volt %dmV, " + "cv delta volt %dmV\n", + chip->qc3p_request_volt, qc3p_chrg_cv_delta_volt); + } else if (vbatt_volt < chrg_step->chrg_step_cv_volt - 10000) { + qc3p_chrg_cv_delta_volt -= 20000; + chip->qc3p_request_volt += 20000; + mmi_chrg_info(chip, + "For keeping CV stage, increase volt %dmV, " + "cv delta volt %dmV\n", + chip->qc3p_request_volt, qc3p_chrg_cv_delta_volt); + } else { + mmi_chrg_dbg(chip, PR_MOTO, "CV loop work well, " + "keep qc3p power, volt %dmV\n", + chip->qc3p_request_volt); + } + + }else { + /*In this case, qc3p_sys_therm_cooling or qc3p_batt_therm_cooling is ture*/ + + if (vbatt_volt > chrg_step->chrg_step_cv_volt + 10000) { + if (qc3p_chrg_cv_delta_volt > 20000) + chip->qc3p_request_volt -= qc3p_chrg_cv_delta_volt; + else + chip->qc3p_request_volt -= 20000; + mmi_chrg_info(chip, + "For keeping CV stage, decrease volt %dmV, " + "cv delta volt %dmV\n", + chip->qc3p_request_volt, qc3p_chrg_cv_delta_volt); + } else { + mmi_chrg_dbg(chip, PR_MOTO, "CV loop work well, " + "keep qc3p power, volt %dmV\n", + chip->qc3p_request_volt); + } + } + + if (chrg_list->cp_slave) { + if (qc3p_quit_slave_chrg_cnt > 3 + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + mmi_chrg_info(chip,"Quit slave chrg, the reason is :ibatt %duA, " + "CP slave charging curr min %d uA\n", + ibatt_curr, + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Restart CP master again\n"); + } + + if (ibatt_curr < + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min) + qc3p_quit_slave_chrg_cnt++; + else + qc3p_quit_slave_chrg_cnt = 0; + } + break; + case PM_QC3P_STATE_CP_QUIT: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + chip->qc3p_batt_therm_cooling = false; + chip->qc3p_batt_therm_cooling_cnt = 0; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_RECOVERY_SW); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + break; + case PM_QC3P_STATE_RECOVERY_SW: + heartbeat_dely_ms = QC3P_HEARTBEAT_SHORT_DELAY_MS; + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + QC3P_DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + + if (!chip->dont_rerun_aicl) { + mmi_chrg_info(chip,"Do an rerun usb AICL for PMIC-SW\n"); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], false); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], true); + } + chip->qc3p_recovery_pmic_chrg = true; + qc3p_chrg_cv_taper_tunning_cnt = 0; + } + + mmi_chrg_info(chip,"ibatt : %dmA, step cc curr : %dmA\n", + ibatt_curr, chrg_step->chrg_step_cc_curr); + if ((ibatt_curr > chrg_step->chrg_step_cc_curr) && (chip->qc3p_request_volt > MINIMUM_CHARGER_VOLT)) { + chip->qc3p_request_volt -= QC3P_CV_DELTA_VOLT; + + mmi_chrg_dbg(chip, PR_MOTO, "Reduce qc3p volt %dmV\n ", + chip->qc3p_request_volt); + heartbeat_dely_ms = QC3P_HEARTBEAT_TUNNING_MS; + qc3p_chrg_cv_taper_tunning_cnt = 0; + } else { + qc3p_chrg_cv_taper_tunning_cnt++; + + } + + if (qc3p_chrg_cv_taper_tunning_cnt > QC3P_CV_TAPPER_COUNT){ + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_LOOP); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_QC3P_STATE_STOP_CHARGE: + heartbeat_dely_ms = QC3P_HEARTBEAT_lOOP_WAIT_MS; + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + QC3P_DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + if (chip->pres_temp_zone != ZONE_COLD + && chip->pres_temp_zone != ZONE_HOT + && chrg_list->chrg_dev[PMIC_SW]->charger_enabled + && chrg_step->chrg_step_cc_curr > 0) { + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_ENTRY); + heartbeat_dely_ms = QC3P_HEARTBEAT_NEXT_STATE_MS; + } + chip->qc3p_request_volt = SWITCH_CHARGER_QC3P_VOLT; + break; + } + +schedule: + + chip->qc3p_target_volt = min(chip->qc3p_request_volt, chip->qc3p_volt_max); + + if (chip->system_thermal_level == 0) { + chip->qc3p_sys_therm_cooling= false; + chip->qc3p_sys_therm_force_pmic_chrg = false; + } else if ((chip->system_thermal_level == chip->thermal_levels - 1) + && !chip->qc3p_sys_therm_force_pmic_chrg) { + chip->qc3p_sys_therm_cooling = true; + chip->qc3p_sys_therm_force_pmic_chrg = true; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_SW_ENTRY); + mmi_chrg_info(chip, "Thermal is the highest, level %d, " + "Force enter into single pmic charging !\n", + chip->system_thermal_level); + + } else if (chip->system_thermal_level > 0 && + (qc3p_sm_state == PM_QC3P_STATE_CP_CC_LOOP || + qc3p_sm_state == PM_QC3P_STATE_CP_CV_LOOP)) { + + mmi_chrg_dbg(chip, PR_MOTO, "Thermal level is %d\n", + chip->system_thermal_level); + if (!chip->qc3p_sys_therm_cooling) { + chip->qc3p_sys_therm_cooling = true; + chip->qc3p_sys_therm_volt = chip->qc3p_request_volt_prev; + } + ibatt_vote = min(chip->thermal_mitigation[chip->system_thermal_level],chrg_step->chrg_step_cc_curr); + if (ibatt_curr > ibatt_vote + QC3P_CC_CURR_DEBOUNCE) { + if (ibatt_curr - ibatt_vote > 300000) + chip->qc3p_sys_therm_volt -= chip->qc3p_volt_steps *3; + else + chip->qc3p_sys_therm_volt -= chip->qc3p_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, "For thermal, decrease qc3 volt %d\n", + chip->qc3p_sys_therm_volt); + } else if ((ibatt_curr < ibatt_vote) &&(vbatt_volt <= chrg_step->chrg_step_cv_volt - 10000) ) { + if (ibatt_vote - ibatt_curr > 300000) + chip->qc3p_sys_therm_volt += chip->qc3p_volt_steps *3; + else + chip->qc3p_sys_therm_volt += chip->qc3p_volt_steps; + chip->qc3p_sys_therm_volt = min(chip->qc3p_volt_max, chip->qc3p_sys_therm_volt); + mmi_chrg_dbg(chip, PR_MOTO, "For thermal, increase qc3 volt %d\n", + chip->qc3p_sys_therm_volt); + chip->qc3p_target_volt = chip->qc3p_sys_therm_volt; + chip->qc3p_request_volt = chip->qc3p_target_volt; + } + + heartbeat_dely_ms = HEARTBEAT_THERMAL_DELAY_MS; + } else if (chip->system_thermal_level > 0 && + chip->system_thermal_level != chip->thermal_levels - 1 && + qc3p_sm_state == PM_QC3P_STATE_SW_LOOP && + chip->qc3p_sys_therm_force_pmic_chrg) { + mmi_chrg_dbg(chip, PR_MOTO, "Try to recovery charger pump!\n"); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_ENTRY); + } + + if (chip->qc3p_sys_therm_cooling) { + chip->qc3p_target_volt = min(chip->qc3p_target_volt, chip->qc3p_sys_therm_volt); + if (qc3p_sm_state == PM_QC3P_STATE_CP_CV_LOOP) + chip->qc3p_sys_therm_volt = chip->qc3p_target_volt; + } + + if (chip->qc3p_batt_therm_cooling && !chip->qc3p_sys_therm_force_pmic_chrg) { + if (batt_temp > chrg_step->temp_c + COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, Cooling loop failed, " + "force enter PM_QC3P_STATE_ENTRY " + "restart this charger process !\n", + batt_temp); + chip->qc3p_batt_therm_cooling = false; + chip->qc3p_batt_therm_cooling_cnt= 0; + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_ENTRY); + + } else if (batt_temp > chrg_step->temp_c) { + cooling_volt = (2 * vbatt_volt) % 20000; + cooling_volt = 2 * vbatt_volt - cooling_volt + + chip->qc3p_volt_comp; + if (ibatt_curr > TYPEC_HIGH_CURRENT_UA + && chip->qc3p_batt_therm_volt > cooling_volt) { + chip->qc3p_batt_therm_volt -= QC3P_COOLING_DELTA_POWER; + mmi_chrg_info(chip, "Do chrg power cooling" + "qc3p request volt %dmA, " + "battery temp %d\n", + chip->qc3p_batt_therm_volt, batt_temp); + } else { + if (chip->qc3p_batt_therm_cooling_cnt > QC3P_COOLING_MAX_CNT) { + mmi_chrg_info(chip, "Do chrg power cooling failed" + "qc3p_batt_therm_volt %dmV" + "battery temp %d, " + "Restart PM_STATE_ENTRY !\n", + chip->qc3p_batt_therm_volt, + batt_temp); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + chip->qc3p_batt_therm_cooling = false; + chip->qc3p_batt_therm_cooling_cnt = 0; + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_ENTRY); + } else { + mmi_chrg_info(chip, "It's already the lowest cooling chrg power" + "waiting for a while, cooling cnt %d, " + "battery temp %d\n", + batt_temp, chip->qc3p_batt_therm_cooling); + chip->qc3p_batt_therm_cooling_cnt++; + } + } + } else if (batt_temp < chrg_step->temp_c - COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, " + "Exit successfully from COOLing loop!\n", + batt_temp); + + if (qc3p_sm_state == PM_QC3P_STATE_CP_CC_LOOP) { + mmi_chrg_info(chip,"Jump into CURR tunning" + "for chrg power poerformance!\n"); + mmi_chrg_qc3p_sm_move_state(chip, + PM_QC3P_STATE_TUNNING_VOLT); + } + chip->qc3p_batt_therm_cooling = false; + chip->qc3p_batt_therm_cooling_cnt = 0; + } + heartbeat_dely_ms = QC3P_HEARTBEAT_lOOP_WAIT_MS; + } + + if (chip->qc3p_batt_therm_cooling) + chip->qc3p_target_volt = min(chip->qc3p_target_volt, chip->qc3p_batt_therm_volt); + + ibus_total_ma = chrg_list->chrg_dev[PMIC_SW]->charger_data.ibus_curr / 1000 + chrg_list->chrg_dev[CP_MASTER]->charger_data.ibus_curr; + if (chrg_list->cp_slave && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + ibus_total_ma += chrg_list->chrg_dev[CP_SLAVE]->charger_data.ibus_curr; + } + + if(ibus_total_ma > chip->qc3p_max_ibus_ma) { + chip->qc3p_ibus_max_volt -= 2 * chip->qc3p_volt_steps; + chip->qc3p_target_volt = min(chip->qc3p_target_volt, chip->qc3p_ibus_max_volt); + chip->qc3p_request_volt = chip->qc3p_target_volt; + mmi_chrg_info(chip,"Limit ibus current: ibus is %dmA, max allow is %dmA, ibus volt is %duV, target volt is %duV \n", + ibus_total_ma, chip->qc3p_max_ibus_ma, chip->qc3p_ibus_max_volt, chip->qc3p_target_volt); + } + + mmi_chrg_dbg(chip, PR_MOTO, "chrg sm work,%s, " + "battery soc %d, " + "battery temp %d, " + "battery current %d, " + "battery voltage %d, " + "sys therm level %d, " + "pmic therm level %d, " + "sys therm cooling %d, " + "batt therm cooling %d, " + "sys therm force pmic chrg %d, " + "recovery pmic chrg %d", + pm_qc3p_state_str[qc3p_sm_state], + batt_soc, batt_temp, + ibatt_curr, vbatt_volt, + chip->system_thermal_level, + pmic_sys_therm_level, + chip->qc3p_sys_therm_cooling, + chip->qc3p_batt_therm_cooling, + chip->qc3p_sys_therm_force_pmic_chrg, + chip->qc3p_recovery_pmic_chrg); + + mmi_chrg_dbg(chip, PR_MOTO, "qc3p request volt %dmV, " + "qc3p target volt %dmV, " + "sys therm volt %dmV, " + "batt therm volt %dmV," + "qc3p_ibus_max_volt %dmV," + "qc3p_max_ibus_ma %dmA," + "ibus total %dmA\n", + chip->qc3p_request_volt, + chip->qc3p_target_volt, + chip->qc3p_sys_therm_volt, + chip->qc3p_batt_therm_volt, + chip->qc3p_ibus_max_volt, + chip->qc3p_max_ibus_ma, + ibus_total_ma); + + if (chip->qc3p_target_volt < SWITCH_CHARGER_QC3P_VOLT) { + + if (qc3p_sm_state == PM_QC3P_STATE_TUNNING_VOLT + || qc3p_sm_state == PM_QC3P_STATE_CP_CC_LOOP + || qc3p_sm_state == PM_QC3P_STATE_CP_CV_LOOP) { + + mmi_chrg_err(chip, "%s, wrong qc3p voltage or current , " + "request qc3p voltage %d, ", + pm_qc3p_state_str[qc3p_sm_state], + chip->qc3p_target_volt); + mmi_chrg_qc3p_sm_move_state(chip, PM_QC3P_STATE_ENTRY); + } + goto skip_pd_select; + } + + rc = mmi_qc3p_set_vbus_voltage(chip, chip->qc3p_target_volt / 1000); + if (rc > 0) { + chip->qc3p_request_volt_prev = chip->qc3p_target_volt; + chip->qc3p_ibus_max_volt = chip->qc3p_target_volt; + } +skip_pd_select: + + if (heartbeat_dely_ms > 0) { + mmi_chrg_err(chip, "schedule work timer %dms\n", heartbeat_dely_ms); + schedule_delayed_work(&chip->mmi_qc3p_chrg_sm_work, + msecs_to_jiffies(heartbeat_dely_ms)); + } else { + mmi_qc3p_chrg_policy_clear(chip); + chip->sm_work_running = false; + chip->qc3p_active = false; + mmi_chrg_err(chip, "exit sm work\n"); + } + + return; +} + + diff --git a/drivers/power/mmi_parallel_charger_iio/qpnp_pmic_charger.c b/drivers/power/mmi_parallel_charger_iio/qpnp_pmic_charger.c new file mode 100644 index 000000000000..7a831e37cf6d --- /dev/null +++ b/drivers/power/mmi_parallel_charger_iio/qpnp_pmic_charger.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include +#include "mmi_charger_core.h" +#include "mmi_charger_core_iio.h" + +static int qpnp_pmic_enable_charging(struct mmi_charger_device *chrg, bool en) +{ + int rc = 0; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_write_iio_chan(chip, SMB5_CHARGING_ENABLED, en); + if (!rc) { + chrg->charger_enabled = en; + } else { + chrg->charger_enabled = false; + } + + return rc; +} + +static int qpnp_pmic_is_charging_enabled(struct mmi_charger_device *chrg, bool *en) +{ + int rc = 0; + int value; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_read_iio_chan(chip, SMB5_CHARGING_ENABLED, &value); + if (!rc) { + chrg->charger_enabled = !!value; + } else + chrg->charger_enabled = false; + + *en = chrg->charger_enabled; + + return rc; +} + +static int qpnp_pmic_get_charging_current(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + *uA = prop.intval; + + return rc; +} + +static int qpnp_pmic_get_ibus(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc; + static struct power_supply *usb_psy; + union power_supply_propval prop = {0,}; + + if (!usb_psy) + usb_psy = power_supply_get_by_name("usb"); + if (!usb_psy) + return -ENODEV; + + rc = power_supply_get_property(usb_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + *uA = prop.intval; + + return rc; +} + +static int qpnp_pmic_set_charging_current(struct mmi_charger_device *chrg, u32 uA) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + prop.intval = uA; + rc = power_supply_set_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, &prop); +// if (!rc) +// chrg->charger_limited = true; + + return rc; +} + +static int qpnp_pmic_get_input_current_settled(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc = 0; + int value; + struct mmi_charger_manager *chip = dev_get_drvdata(&chrg->dev); + + if (!chip) + return -ENODEV; + + rc = mmi_charger_read_iio_chan(chip, SMB5_HW_CURRENT_MAX, &value); + if (!rc) + chrg->input_curr_setted = value; + *uA = chrg->input_curr_setted; + + return rc; +} + +static int qpnp_pmic_update_charger_status(struct mmi_charger_device *chrg) +{ + int rc; + bool value = 0; + static struct power_supply *usb_psy = NULL; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + if (!usb_psy) + usb_psy = power_supply_get_by_name("usb"); + if (!usb_psy) + return -ENODEV; + + rc = power_supply_get_property(usb_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + chrg->charger_data.ibus_curr= prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + chrg->charger_data.vbatt_volt = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + chrg->charger_data.ibatt_curr = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_TEMP, &prop); + if (!rc) + chrg->charger_data.batt_temp = prop.intval / 10; + + rc = power_supply_get_property(usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + chrg->charger_data.vbus_volt = prop.intval; + + rc = power_supply_get_property(usb_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + chrg->charger_data.ibus_curr = prop.intval; + + rc = power_supply_get_property(usb_psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (!rc) + chrg->charger_data.vbus_pres = !!prop.intval; + + rc = qpnp_pmic_is_charging_enabled(chrg, &value); + if (!rc) + chrg->charger_enabled= !!value; + + chrg_dev_info(chrg, "QPNP SW chrg: status update: --- info---"); + chrg_dev_info(chrg, "vbatt %d\n", chrg->charger_data.vbatt_volt); + chrg_dev_info(chrg, "ibatt %d\n", chrg->charger_data.ibatt_curr); + chrg_dev_info(chrg, "batt temp %d\n", chrg->charger_data.batt_temp); + chrg_dev_info(chrg, "vbus %d\n", chrg->charger_data.vbus_volt); + chrg_dev_info(chrg, "ibus %d\n", chrg->charger_data.ibus_curr); + chrg_dev_info(chrg, "vbus pres %d\n", chrg->charger_data.vbus_pres); + chrg_dev_info(chrg, "charger_enabled %d\n", chrg->charger_enabled); + chrg_dev_info(chrg, "charger_limited %d\n", chrg->charger_limited); + return rc; +} + +struct mmi_charger_ops qpnp_pmic_charger_ops = { + .enable = qpnp_pmic_enable_charging, + .is_enabled = qpnp_pmic_is_charging_enabled, + .get_charging_current = qpnp_pmic_get_charging_current, + .get_input_current = qpnp_pmic_get_ibus, + .set_charging_current = qpnp_pmic_set_charging_current, + .get_input_current_settled = qpnp_pmic_get_input_current_settled, + .update_charger_status = qpnp_pmic_update_charger_status, +}; diff --git a/drivers/power/mmi_parallel_charger_qc3p/Android.mk b/drivers/power/mmi_parallel_charger_qc3p/Android.mk new file mode 100644 index 000000000000..27ebc985d1ef --- /dev/null +++ b/drivers/power/mmi_parallel_charger_qc3p/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_parallel_charger_qc3p.ko +LOCAL_MODULE_TAGS := optional + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/mmi_parallel_charger_qc3p/Kbuild b/drivers/power/mmi_parallel_charger_qc3p/Kbuild new file mode 100644 index 000000000000..9b204fdcd9c7 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_qc3p/Kbuild @@ -0,0 +1,14 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/power/mmi_parallel_charger_qc3p + +obj-m += mmi_parallel_charger_qc3p.o +mmi_parallel_charger_qc3p-objs += mmi_charger_class.o +mmi_parallel_charger_qc3p-objs += mmi_charger_core.o +mmi_parallel_charger_qc3p-objs += mmi_charger_pump_policy.o +mmi_parallel_charger_qc3p-objs += bq2597x_charger.o +mmi_parallel_charger_qc3p-objs += qpnp_pmic_charger.o + +#ifeq ($(CONFIG_MMI_PL_CP_POLICY), y) +#endif diff --git a/drivers/power/mmi_parallel_charger_qc3p/Makefile b/drivers/power/mmi_parallel_charger_qc3p/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_qc3p/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/power/mmi_parallel_charger_qc3p/bq2597x_charger.c b/drivers/power/mmi_parallel_charger_qc3p/bq2597x_charger.c new file mode 100644 index 000000000000..5860f298498b --- /dev/null +++ b/drivers/power/mmi_parallel_charger_qc3p/bq2597x_charger.c @@ -0,0 +1,278 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include +#include "mmi_charger_core.h" + +#define BAT_OVP_FAULT_SHIFT 8 +#define BAT_OCP_FAULT_SHIFT 9 +#define BUS_OVP_FAULT_SHIFT 10 +#define BUS_OCP_FAULT_SHIFT 11 +#define BAT_THERM_FAULT_SHIFT 12 +#define BUS_THERM_FAULT_SHIFT 13 +#define DIE_THERM_FAULT_SHIFT 14 +#define CONV_OCP_FAULT_SHIFT 15 +#define SS_TIMEOUT_FAULT_SHIFT 16 +#define TS_SHUT_FAULT_SHIFT 17 + +#define BAT_OVP_FAULT_MASK (1 << BAT_OVP_FAULT_SHIFT) +#define BAT_OCP_FAULT_MASK (1 << BAT_OCP_FAULT_SHIFT) +#define BUS_OVP_FAULT_MASK (1 << BUS_OVP_FAULT_SHIFT) +#define BUS_OCP_FAULT_MASK (1 << BUS_OCP_FAULT_SHIFT) +#define BAT_THERM_FAULT_MASK (1 << BAT_THERM_FAULT_SHIFT) +#define BUS_THERM_FAULT_MASK (1 << BUS_THERM_FAULT_SHIFT) +#define DIE_THERM_FAULT_MASK (1 << DIE_THERM_FAULT_SHIFT) +#define CONV_OCP_FAULT_MASK (1 << CONV_OCP_FAULT_SHIFT) +#define SS_TIMEOUT_FAULT_MASK (1 << SS_TIMEOUT_FAULT_SHIFT) +#define TS_SHUT_FAULT_MASK (1 << TS_SHUT_FAULT_SHIFT) + +#define BAT_OVP_ALARM_SHIFT 0 +#define BAT_OCP_ALARM_SHIFT 1 +#define BUS_OVP_ALARM_SHIFT 2 +#define BUS_OCP_ALARM_SHIFT 3 +#define BUS_UCP_FAULT_SHIFT 4 +#define BUS_THERM_ALARM_SHIFT 5 +#define DIE_THERM_ALARM_SHIFT 6 +#define BAT_UCP_ALARM_SHIFT 7 + +#define BAT_OVP_ALARM_MASK (1 << BAT_OVP_ALARM_SHIFT) +#define BAT_OCP_ALARM_MASK (1 << BAT_OCP_ALARM_SHIFT) +#define BUS_OVP_ALARM_MASK (1 << BUS_OVP_ALARM_SHIFT) +#define BUS_OCP_ALARM_MASK (1 << BUS_OCP_ALARM_SHIFT) +#define BUS_UCP_FAULT_MASK (1 << BUS_UCP_FAULT_SHIFT) +#define BUS_THERM_ALARM_MASK (1 << BUS_THERM_ALARM_SHIFT) +#define DIE_THERM_ALARM_MASK (1 << DIE_THERM_ALARM_SHIFT) +#define BAT_UCP_ALARM_MASK (1 << BAT_UCP_ALARM_SHIFT) + +static int bq2597x_enable_charging(struct mmi_charger_device *chrg, bool en) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + prop.intval = en; + rc = power_supply_set_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop); + + if (!rc) { + chrg->charger_enabled = !!prop.intval; + } else + chrg->charger_enabled = false; + + return rc; +} + +static int bq2597x_is_charging_enabled(struct mmi_charger_device *chrg, bool *en) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop); + if (!rc) { + chrg->charger_enabled = !!prop.intval; + } else + chrg->charger_enabled = false; + + *en = chrg->charger_enabled; + + return rc; +} + +static int bq2597x_get_charging_current(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + *uA = !!prop.intval; + + return rc; +} + +static int bq2597x_get_input_current(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, &prop); + if (!rc) + *uA = prop.intval; + + return rc; +} + +static int bq2597x_update_charger_status(struct mmi_charger_device *chrg) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + chrg->charger_data.vbatt_volt = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + chrg->charger_data.ibatt_curr = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_TEMP, &prop); + if (!rc) + chrg->charger_data.batt_temp = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED, &prop); + if (!rc) + chrg->charger_data.vbus_volt = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, &prop); + if (!rc) + chrg->charger_data.ibus_curr = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (!rc) + chrg->charger_data.vbus_pres = !!prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop); + if (!rc) + chrg->charger_enabled = !!prop.intval; + + chrg_dev_info(chrg, "BQ2597x chrg: %s status update: --- info---\n",chrg->name); + chrg_dev_info(chrg, "vbatt %d\n", chrg->charger_data.vbatt_volt); + chrg_dev_info(chrg, "ibatt %d\n", chrg->charger_data.ibatt_curr); + chrg_dev_info(chrg, "batt temp %d\n", chrg->charger_data.batt_temp); + chrg_dev_info(chrg, "vbus %d\n", chrg->charger_data.vbus_volt); + chrg_dev_info(chrg, "ibus %d\n", chrg->charger_data.ibus_curr); + chrg_dev_info(chrg, "vbus pres %d\n", chrg->charger_data.vbus_pres); + chrg_dev_info(chrg, "charger_enabled %d\n", chrg->charger_enabled); + + return rc; +} + +static int bq2597x_update_charger_error_status(struct mmi_charger_device *chrg) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CP_STATUS1, &prop); + if (!rc) { + chrg->charger_error.chrg_err_type = + ((!!(prop.intval & BAT_OVP_ALARM_MASK)) ? + 1 << MMI_BAT_OVP_ALARM_BIT : + 0 << MMI_BAT_OVP_ALARM_BIT) | + ((!!(prop.intval & BAT_OCP_ALARM_MASK)) ? + 1 << MMI_BAT_OCP_ALARM_BIT : + 0 << MMI_BAT_OCP_ALARM_BIT) | + ((!!(prop.intval & BAT_UCP_ALARM_MASK)) ? + 1 << MMI_BAT_UCP_ALARM_BIT : + 0 << MMI_BAT_UCP_ALARM_BIT) | + ((!!(prop.intval & BUS_OVP_ALARM_MASK)) ? + 1 << MMI_BUS_OVP_ALARM_BIT : + 0 << MMI_BUS_OVP_ALARM_BIT) | + ((!!(prop.intval & BUS_OCP_ALARM_MASK)) ? + 1 << MMI_BUS_OCP_ALARM_BIT : + 0 << MMI_BUS_OCP_ALARM_BIT) | + ((!!(prop.intval & BAT_OVP_FAULT_MASK)) ? + 1 << MMI_BAT_OVP_FAULT_BIT : + 0 << MMI_BAT_OVP_FAULT_BIT) | + ((!!(prop.intval & BAT_OCP_FAULT_MASK)) ? + 1 << MMI_BAT_OCP_FAULT_BIT : + 0 << MMI_BAT_OCP_FAULT_BIT) | + ((!!(prop.intval & BUS_OVP_FAULT_MASK)) ? + 1 << MMI_BUS_OVP_FAULT_BIT : + 0 << MMI_BUS_OVP_FAULT_BIT) | + ((!!(prop.intval & BUS_OCP_FAULT_MASK)) ? + 1 << MMI_BUS_OCP_FAULT_BIT : + 0 << MMI_BUS_OCP_FAULT_BIT) | + ((!!(prop.intval & BUS_UCP_FAULT_MASK)) ? + 1 << MMI_BUS_UCP_FAULT_BIT : + 0 << MMI_BUS_UCP_FAULT_BIT) | + ((!!(prop.intval & CONV_OCP_FAULT_MASK)) ? + 1 << MMI_CONV_OCP_FAULT_BIT : + 0 << MMI_CONV_OCP_FAULT_BIT); + } + return rc; +} + +static int bq2597x_clear_charger_error(struct mmi_charger_device *chrg) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_set_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_UPDATE_NOW, &prop); + + return rc; +} + +struct mmi_charger_ops bq2597x_charger_ops = { + .enable = bq2597x_enable_charging, + .is_enabled = bq2597x_is_charging_enabled, + .get_charging_current = bq2597x_get_charging_current, + .get_input_current = bq2597x_get_input_current, + .update_charger_status = bq2597x_update_charger_status, + .update_charger_error = bq2597x_update_charger_error_status, + .clear_charger_error = bq2597x_clear_charger_error, +}; diff --git a/drivers/power/mmi_parallel_charger_qc3p/mmi_charger_class.c b/drivers/power/mmi_parallel_charger_qc3p/mmi_charger_class.c new file mode 100644 index 000000000000..c4a793dd6d8c --- /dev/null +++ b/drivers/power/mmi_parallel_charger_qc3p/mmi_charger_class.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" + +static struct class *mmi_charger_class; + +int mmi_enable_charging(struct mmi_charger_device *chrg, bool en) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->enable) + return chrg->ops->enable(chrg,en); + + return -ENOTSUPP; +} + +int mmi_is_charging_enabled(struct mmi_charger_device *chrg, bool *en) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->is_enabled) + return chrg->ops->is_enabled(chrg,en); + + return -ENOTSUPP; +} + +int mmi_get_charing_current(struct mmi_charger_device *chrg, u32 *uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->get_charging_current) + return chrg->ops->get_charging_current(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_set_charing_current(struct mmi_charger_device *chrg, u32 uA) +{ + + if(chrg != NULL && chrg->ops != NULL && chrg->ops->set_charging_current) + return chrg->ops->set_charging_current(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_get_input_current_settled(struct mmi_charger_device *chrg, u32 *uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->get_input_current_settled) + return chrg->ops->get_input_current_settled(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_get_input_current(struct mmi_charger_device *chrg, u32 *uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->get_input_current) + return chrg->ops->get_input_current(chrg,uA); + + return -ENOTSUPP; + +} + +int mmi_set_input_current(struct mmi_charger_device *chrg, u32 uA) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->set_input_current) + return chrg->ops->set_input_current(chrg,uA); + + return -ENOTSUPP; +} + +int mmi_update_charger_status(struct mmi_charger_device *chrg) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->update_charger_status) + return chrg->ops->update_charger_status(chrg); + + return -ENOTSUPP; +} + +int mmi_update_charger_error(struct mmi_charger_device *chrg) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->update_charger_error) + return chrg->ops->update_charger_error(chrg); + + return -ENOTSUPP; +} + +int mmi_clear_charger_error(struct mmi_charger_device *chrg) +{ + if(chrg != NULL && chrg->ops != NULL && chrg->ops->clear_charger_error) + return chrg->ops->clear_charger_error(chrg); + + return -ENOTSUPP; +} + +static void mmi_charger_device_release(struct device *dev) +{ + struct mmi_charger_device *charger_dev = to_mmi_charger_device(dev); + + kfree(charger_dev); + return; +} + +static int charger_match_device_by_name(struct device *dev, + const void *data) +{ + const char *name = data; + return strcmp(dev_name(dev), name) == 0; +} + +struct mmi_charger_device *get_charger_by_name(const char *name) +{ + struct device *dev; + + if(!name) + return (struct mmi_charger_device *)NULL; + dev = class_find_device(mmi_charger_class, NULL, name, + charger_match_device_by_name); + + return dev ? to_mmi_charger_device(dev) : NULL; +} + +int is_charger_exist(const char *name) +{ + if (get_charger_by_name(name) == NULL) + return 0; + return 1; +} + +struct mmi_charger_device *mmi_charger_device_register(const char *name, + const char *psy_name,struct device *parent, void *devdata, + const struct mmi_charger_ops *ops) +{ + struct mmi_charger_device *charger_dev; + int rc; + + pr_info("mmi charger device register: name=%s\n",name); + charger_dev = kzalloc(sizeof(struct mmi_charger_device),GFP_KERNEL); + if (!charger_dev) + return ERR_PTR(-ENOMEM); + + mutex_init(&charger_dev->ops_lock); + charger_dev->dev.class = mmi_charger_class; + charger_dev->dev.parent = parent; + charger_dev->dev.release = mmi_charger_device_release; + charger_dev->name = name; + dev_set_name(&charger_dev->dev, "%s", name); + dev_set_drvdata(&charger_dev->dev, devdata); + + rc = device_register(&charger_dev->dev); + if (rc) { + kfree(charger_dev); + return ERR_PTR(rc); + } + charger_dev->chrg_psy = power_supply_get_by_name(psy_name); + charger_dev->ops = ops; + if (!charger_dev->chrg_psy) + return ERR_PTR(-ENODEV); + pr_info("mmi charger device register: name=%s, successfully\n",name); + return charger_dev; +} + +//EXPORT_SYMBOL(mmi_charger_device_register); + +void mmi_charger_device_unregister(struct mmi_charger_device* charger_dev) +{ + if (!charger_dev) + return; + + mutex_lock(&charger_dev->ops_lock); + charger_dev->ops = NULL; + mutex_unlock(&charger_dev->ops_lock); + device_unregister(&charger_dev->dev); +} + +//EXPORT_SYMBOL(mmi_charger_device_unregister); + +void mmi_charger_class_exit(void) +{ + class_destroy(mmi_charger_class); +} + +int mmi_charger_class_init(void) +{ + mmi_charger_class = class_create(THIS_MODULE, "mmi_charger_qc3p"); + if (IS_ERR(mmi_charger_class)) { + pr_err("Unable to create mmi charger class; error = %ld\n", + PTR_ERR(mmi_charger_class)); + return PTR_ERR(mmi_charger_class); + } + pr_info("success to create mmi charger class \n"); + return 0; +} diff --git a/drivers/power/mmi_parallel_charger_qc3p/mmi_charger_class.h b/drivers/power/mmi_parallel_charger_qc3p/mmi_charger_class.h new file mode 100644 index 000000000000..1b3cb6503eaa --- /dev/null +++ b/drivers/power/mmi_parallel_charger_qc3p/mmi_charger_class.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + #ifndef __MMI_CHRG_CLASS_H_ + #define __MMI_CHRG_CLASS_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define chrg_dev_info(chip, fmt, ...) \ + pr_info("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__) \ + +struct mmi_charger_info { + bool vbus_pres; + int vbatt_volt; + int vbus_volt; + int ibatt_curr; + int ibus_curr; + int batt_temp; +}; + +#define MMI_BAT_OVP_ALARM_BIT 0 +#define MMI_BAT_OCP_ALARM_BIT 1 +#define MMI_BAT_UCP_ALARM_BIT 2 +#define MMI_BUS_OVP_ALARM_BIT 3 +#define MMI_BUS_OCP_ALARM_BIT 4 +#define MMI_BUS_UCP_ALARM_BIT 5 +#define MMI_BAT_OVP_FAULT_BIT 6 +#define MMI_BAT_OCP_FAULT_BIT 7 +#define MMI_BAT_UCP_FAULT_BIT 8 +#define MMI_BUS_OVP_FAULT_BIT 9 +#define MMI_BUS_OCP_FAULT_BIT 10 +#define MMI_BUS_UCP_FAULT_BIT 11 +#define MMI_CONV_OCP_FAULT_BIT 12 +#define MMI_THERM_ALARM_BIT 13 +#define MMI_THERM_FAULT_BIT 14 + +struct mmi_charger_error_info { + int chrg_err_type; + int bus_ucp_err_cnt; + int bus_ocp_err_cnt; +}; + +struct mmi_charger_device { + const char *name; /*charger device name*/ + struct power_supply *chrg_psy; /*power supply handle*/ + const struct mmi_charger_ops *ops; /*operation function*/ + struct mutex ops_lock; + struct device dev; + struct mmi_charger_info charger_data; + struct mmi_charger_error_info charger_error; + int *debug_mask; + int input_curr_setted; /*save input current*/ + int charging_curr_limited; /*charger current limitation*/ + int charging_curr_min; /*The minimum charging current supported by this device*/ + bool charger_enabled; /*is this charger device enabled*/ + bool charger_limited; /*is the charging current of this device limited*/ +}; + +struct mmi_charger_ops { + int (*enable)(struct mmi_charger_device *chrg, bool en); + int (*is_enabled)(struct mmi_charger_device *chrg, bool *en); + int (*get_charging_current)(struct mmi_charger_device *chrg, u32 *uA); + int (*set_charging_current)(struct mmi_charger_device *chrg, u32 uA); + int (*get_input_current_settled)(struct mmi_charger_device *chrg, u32 *uA); + int (*get_input_current)(struct mmi_charger_device *chrg, u32 *uA); + int (*set_input_current)(struct mmi_charger_device *chrg, u32 uA); + int (*update_charger_status)(struct mmi_charger_device *chrg); + int (*update_charger_error)(struct mmi_charger_device *chrg); + int (*clear_charger_error)(struct mmi_charger_device *chrg); +}; + + +#define to_mmi_charger_device(obj) container_of(obj, struct mmi_charger_device, dev) +#define mmi_chrg_name(x) (dev_name(&(x)->dev)) + +extern struct mmi_charger_device *get_charger_by_name(const char *name); +extern int is_charger_exist(const char *name); +extern int mmi_charger_class_init(void); +extern void mmi_charger_class_exit(void); +extern int mmi_enable_charging(struct mmi_charger_device *chrg, bool en); +extern int mmi_is_charging_enabled(struct mmi_charger_device *chrg, bool *en); +extern int mmi_get_charing_current(struct mmi_charger_device *chrg, u32 *uA); +extern int mmi_set_charing_current(struct mmi_charger_device *chrg, u32 uA); +extern int mmi_get_input_current_settled(struct mmi_charger_device *chrg, u32 *uA); +extern int mmi_get_input_current(struct mmi_charger_device *chrg, u32 *uA); +extern int mmi_set_input_current(struct mmi_charger_device *chrg, u32 uA); +extern int mmi_update_charger_status(struct mmi_charger_device *chrg); +extern int mmi_update_charger_error(struct mmi_charger_device *chrg); +extern int mmi_clear_charger_error(struct mmi_charger_device *chrg); +extern struct mmi_charger_device *mmi_charger_device_register(const char *name, + const char *psy_name,struct device *parent, void *devdata, + const struct mmi_charger_ops *ops); +extern void mmi_charger_device_unregister(struct mmi_charger_device* charger_dev); +#endif diff --git a/drivers/power/mmi_parallel_charger_qc3p/mmi_charger_core.c b/drivers/power/mmi_parallel_charger_qc3p/mmi_charger_core.c new file mode 100644 index 000000000000..75d7d777b5c6 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_qc3p/mmi_charger_core.c @@ -0,0 +1,2024 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include "mmi_charger_core.h" +#include "mmi_charger_policy.h" + +static int __debug_mask = 0x85; +module_param_named( + debug_mask, __debug_mask, int, S_IRUSR | S_IWUSR +); + +static int pd_volt_max_init = 0; +static int pd_curr_max_init = 0; +static int *dt_temp_zones; +static struct mmi_chrg_dts_info *chrg_name_list; +static char *charge_rate[] = { + "None", "Normal", "Weak", "Turbo", "Turbo_30W", "Hyper" +}; +#define MIN_TEMP_C -20 +#define MAX_TEMP_C 60 +#define HYSTEREISIS_DEGC 2 +bool mmi_chrg_rate_check(struct mmi_charger_manager *chg); +bool mmi_find_temp_zone(struct mmi_charger_manager *chip, int temp_c, bool ignore_hysteresis_degc) +{ + int prev_zone, num_zones; + struct mmi_chrg_temp_zone *zones; + int hotter_t = 0, hotter_fcc = 0; + int colder_t = 0, colder_fcc = 0; + int i; + int max_temp; + + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return false; + } + + zones = chip->temp_zones; + num_zones = chip->num_temp_zones; + prev_zone = chip->pres_temp_zone; + + mmi_chrg_info(chip, "prev zone %d, temp_c %d\n",prev_zone, temp_c); + max_temp = zones[num_zones - 1].temp_c; + + if (prev_zone == ZONE_NONE) { + for (i = num_zones - 1; i >= 0; i--) { + if (temp_c >= zones[i].temp_c) { + if (i == num_zones - 1) + chip->pres_temp_zone = ZONE_HOT; + else + chip->pres_temp_zone = i + 1; + return true; + } + } + chip->pres_temp_zone = ZONE_COLD; + return true; + } + + if (prev_zone == ZONE_COLD) { + if (temp_c >= MIN_TEMP_C + HYSTEREISIS_DEGC) + chip->pres_temp_zone = ZONE_FIRST; + } else if (prev_zone == ZONE_HOT) { + if (temp_c <= max_temp - HYSTEREISIS_DEGC) + chip->pres_temp_zone = num_zones - 1; + } else { + if (prev_zone == ZONE_FIRST) { + hotter_t = zones[prev_zone].temp_c; + colder_t = MIN_TEMP_C; + hotter_fcc = zones[prev_zone + 1].chrg_step_power->chrg_step_curr; + colder_fcc = 0; + } else if (prev_zone == num_zones - 1) { + hotter_t = zones[prev_zone].temp_c; + colder_t = zones[prev_zone - 1].temp_c; + hotter_fcc = 0; + colder_fcc = zones[prev_zone - 1].chrg_step_power->chrg_step_curr; + } else { + hotter_t = zones[prev_zone].temp_c; + colder_t = zones[prev_zone - 1].temp_c; + hotter_fcc = zones[prev_zone + 1].chrg_step_power->chrg_step_curr; + colder_fcc = zones[prev_zone - 1].chrg_step_power->chrg_step_curr; + } + + if (!ignore_hysteresis_degc) { + if (zones[prev_zone].chrg_step_power->chrg_step_curr < hotter_fcc) + hotter_t += HYSTEREISIS_DEGC; + if (zones[prev_zone].chrg_step_power->chrg_step_curr < colder_fcc) + colder_t -= HYSTEREISIS_DEGC; + } + + if (temp_c <= MIN_TEMP_C) + chip->pres_temp_zone = ZONE_COLD; + else if (temp_c >= max_temp) + chip->pres_temp_zone = ZONE_HOT; + else if (temp_c >= hotter_t) + chip->pres_temp_zone++; + else if (temp_c < colder_t) + chip->pres_temp_zone--; + } + + mmi_chrg_info(chip, "batt temp_c %d, prev zone %d, pres zone %d, " + "hotter_fcc %dmA, colder_fcc %dmA, " + "hotter_t %dC, colder_t %dC\n", + temp_c,prev_zone, chip->pres_temp_zone, + hotter_fcc, colder_fcc, hotter_t, colder_t); + + if (prev_zone != chip->pres_temp_zone) { + mmi_chrg_info(chip, + "Entered Temp Zone %d!\n", + chip->pres_temp_zone); + return true; + } + return false; +} + +bool mmi_find_chrg_step(struct mmi_charger_manager *chip, int temp_zone, int vbatt_volt) +{ + int batt_volt, i; + bool find_step = false; + struct mmi_chrg_temp_zone zone; + struct mmi_chrg_step_power *chrg_steps; + struct mmi_chrg_step_info chrg_step_inline; + struct mmi_chrg_step_info prev_step; + + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return false; + } + + if (chip->pres_temp_zone == ZONE_HOT || + chip->pres_temp_zone == ZONE_COLD || + chip->pres_temp_zone < ZONE_FIRST) { + mmi_chrg_err(chip, "pres temp zone is HOT or COLD, " + "can't find chrg step\n"); + return false; + } + + zone = chip->temp_zones[chip->pres_temp_zone]; + chrg_steps = zone.chrg_step_power; + prev_step = chip->chrg_step; + + batt_volt = vbatt_volt; + chrg_step_inline.temp_c = zone.temp_c; + + mmi_chrg_info(chip, "batt_volt %d, chrg step %d, step nums %d\n", + batt_volt, prev_step.pres_chrg_step, + chip->chrg_step_nums); + + /*In the first search cycle, find out the vbatt is less than step volt*/ + + for (i = 0; i < chip->chrg_step_nums; i++) { + mmi_chrg_info(chip, + "first cycle,i %d, step volt %d, batt_volt %d\n", + i, chrg_steps[i].chrg_step_volt, batt_volt); + if (chrg_steps[i].chrg_step_volt > 0 + && batt_volt < chrg_steps[i].chrg_step_volt) { + if ( (i + 1) < chip->chrg_step_nums + && chrg_steps[i + 1].chrg_step_volt > 0) { + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i + 1].chrg_step_curr; + } else + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cc_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cv_volt = + chrg_steps[i].chrg_step_volt; + chrg_step_inline.pres_chrg_step = i; + find_step = true; + mmi_chrg_info(chip, "find chrg step\n"); + break; + } + } + + if (find_step) { + mmi_chrg_info(chip, "chrg step %d, " + "step cc curr %d, step cv volt %d, " + "step cv tapper curr %d\n", + chrg_step_inline.pres_chrg_step, + chrg_step_inline.chrg_step_cc_curr, + chrg_step_inline.chrg_step_cv_volt, + chrg_step_inline.chrg_step_cv_tapper_curr); + chip->chrg_step = chrg_step_inline; + } else { + + /*If can't find out any chrg step in the first search cycle,*/ + /*it means that vbatt is already greater than all chrg step volt, */ + /*therefore, start to enter second search cycle, */ + /*to find out the maximal chrg step volt*/ + for (i = 0; i < chip->chrg_step_nums; i++) { + mmi_chrg_info(chip, + "second cycle, i %d, step volt %d, batt_volt %d\n", + i, chrg_steps[i].chrg_step_volt, batt_volt); + if (chrg_steps[i].chrg_step_volt > 0 + && batt_volt > chrg_steps[i].chrg_step_volt) { + if ( (i + 1) < chip->chrg_step_nums + && chrg_steps[i + 1].chrg_step_volt > 0) { + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i + 1].chrg_step_curr; + } else + chrg_step_inline.chrg_step_cv_tapper_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cc_curr = + chrg_steps[i].chrg_step_curr; + chrg_step_inline.chrg_step_cv_volt = + chrg_steps[i].chrg_step_volt; + chrg_step_inline.pres_chrg_step = i; + find_step = true; + mmi_chrg_info(chip, "find chrg step\n"); + } + } + + if (find_step) { + mmi_chrg_info(chip, "chrg step %d, " + "step cc curr %d, step cv volt %d, " + "step cv tapper curr %d\n", + chrg_step_inline.pres_chrg_step, + chrg_step_inline.chrg_step_cc_curr, + chrg_step_inline.chrg_step_cv_volt, + chrg_step_inline.chrg_step_cv_tapper_curr); + chip->chrg_step = chrg_step_inline; + } + } + + + if (find_step) { + if (chip->chrg_step.chrg_step_cc_curr == + chip->chrg_step.chrg_step_cv_tapper_curr) + chip->chrg_step.last_step = true; + else + chip->chrg_step.last_step = false; + + mmi_chrg_info(chip,"Temp zone %d, " + "select chrg step %d, step cc curr %d," + "step cv volt %d, step cv tapper curr %d, " + "is the last chrg step %d\n", + chip->pres_temp_zone, + chip->chrg_step.pres_chrg_step, + chip->chrg_step.chrg_step_cc_curr, + chip->chrg_step.chrg_step_cv_volt, + chip->chrg_step.chrg_step_cv_tapper_curr, + chip->chrg_step.last_step); + + if (prev_step.pres_chrg_step != chip->chrg_step.pres_chrg_step) { + mmi_chrg_info(chip, "Find the next chrg step\n"); + return true; + } + } + return false; +} + +void mmi_set_pps_result_history(struct mmi_charger_manager *chip, int pps_result) +{ + if (chip->pps_result_history_idx >= PPS_RET_HISTORY_SIZE -1) + chip->pps_result_history_idx = 0; + + if (pps_result < 0) + chip->pps_result_history[chip->pps_result_history_idx] = 1; + else + chip->pps_result_history[chip->pps_result_history_idx] = 0; + + chip->pps_result_history_idx++; +} + +bool mmi_get_pps_result_history(struct mmi_charger_manager *chip) +{ + int i = 0; + int result = 0; + for (i = 0; i < PPS_RET_HISTORY_SIZE; i++) + result += chip->pps_result_history[i]; + + if (result >= PPS_RET_HISTORY_SIZE) + return RESET_POWER; + else if (result >= PPS_RET_HISTORY_SIZE / 2) + return BLANCE_POWER; + else + return NO_ERROR; +} + +void mmi_clear_pps_result_history(struct mmi_charger_manager *chip) +{ + int i = 0;; + for (i = 0; i < PPS_RET_HISTORY_SIZE; i++) + chip->pps_result_history[i] = 0; +} + +int mmi_calculate_delta_volt(int pps_voltage, int pps_current, int delta_curr) +{ + u64 power; + int delta_volt; + u64 pps_volt; + u64 pps_curr; + + pps_volt = pps_voltage; + pps_curr = pps_current; + power = pps_volt * pps_curr; + + delta_volt = (int)(power / (pps_curr - delta_curr) - pps_volt); + delta_volt -= delta_volt % 20000; + + if (delta_volt > 0) + return delta_volt; + else + return 0; +} + +void mmi_update_all_charger_status(struct mmi_charger_manager *chip) +{ + int chrg_num, i; + struct mmi_charger_device *chrg_dev; + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return; + } + + chrg_num = chip->mmi_chrg_dev_num; + for (i = 0; i < chrg_num; i++) { + chrg_dev = chip->chrg_list[i]; + mmi_update_charger_status(chrg_dev); + } + return; +} + +void mmi_update_all_charger_error(struct mmi_charger_manager *chip) +{ + int chrg_num, i; + struct mmi_charger_device *chrg_dev; + if (!chip) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return; + } + + chrg_num = chip->mmi_chrg_dev_num; + for (i = 0; i < chrg_num; i++) { + chrg_dev = chip->chrg_list[i]; + mmi_update_charger_error(chrg_dev); + } + return; +} + +void mmi_dump_charger_error(struct mmi_charger_manager *chip, + struct mmi_charger_device *chrg_dev) +{ + if (!chip || !chrg_dev) { + mmi_chrg_err(chip, "called before chip valid!\n"); + return; + } + mmi_chrg_dbg(chip, PR_MOTO, "%s: charger error type %d, " + "bus ucp err cnt %d, bus ocp err cnt %d\n", + chrg_dev->name, + chrg_dev->charger_error.chrg_err_type, + chrg_dev->charger_error.bus_ucp_err_cnt, + chrg_dev->charger_error.bus_ocp_err_cnt); + return; +} +#if 0 +static enum power_supply_property batt_props[] = { + POWER_SUPPLY_PROP_INPUT_SUSPEND, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGER_TEMP, + POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_QNOVO, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_QNOVO, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, + POWER_SUPPLY_PROP_SW_JEITA_ENABLED, + POWER_SUPPLY_PROP_CHARGE_DONE, + POWER_SUPPLY_PROP_PARALLEL_DISABLE, + POWER_SUPPLY_PROP_SET_SHIP_MODE, + POWER_SUPPLY_PROP_DIE_HEALTH, + POWER_SUPPLY_PROP_RERUN_AICL, + POWER_SUPPLY_PROP_DP_DM, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_RECHARGE_SOC, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_FORCE_RECHARGE, + POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE, + POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, + POWER_SUPPLY_PROP_NUM_SYSTEM_TEMP_LEVELS, + POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, + POWER_SUPPLY_PROP_CHARGE_RATE, + POWER_SUPPLY_PROP_AGE, + POWER_SUPPLY_PROP_CURRENT_MAX, +}; + +static int batt_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mmi_charger_manager *chip = power_supply_get_drvdata(psy); + union power_supply_propval prop = {0,}; + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_CAPACITY: + case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_TEMP: + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + rc = power_supply_get_property(chip->extrn_psy, + psp, &prop); + if (rc < 0) { + mmi_chrg_err(chip, "Get Unknown prop %d from extrn_psy rc = %d\n", + psp, rc); + rc = power_supply_get_property(chip->qcom_psy, + psp, &prop); + } + if (!rc) + val->intval = prop.intval; + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + if (chip->use_batt_age) { + val->intval = chip->cycles; + } else { + rc = power_supply_get_property(chip->qcom_psy, + psp, &prop); + if (rc < 0) { + mmi_chrg_err(chip, "Get Unknown prop %d rc = %d\n", psp, rc); + rc = 0; + val->intval = -EINVAL; + } + if (!rc) + val->intval = prop.intval; + } + break; + case POWER_SUPPLY_PROP_STATUS: + rc = power_supply_get_property(chip->qcom_psy, + psp, &prop); + if (rc < 0) { + mmi_chrg_err(chip, "Get Unknown prop %d rc = %d\n", psp, rc); + rc = 0; + val->intval = -EINVAL; + } else { + val->intval = prop.intval; + if (prop.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) { + rc = power_supply_get_property(chip->extrn_psy, + psp, &prop); + if (!rc && (prop.intval == POWER_SUPPLY_STATUS_CHARGING + || prop.intval == POWER_SUPPLY_STATUS_FULL)) + val->intval = prop.intval; + } + } + break; + case POWER_SUPPLY_PROP_CHARGE_RATE: + mmi_chrg_rate_check(chip); + val->intval = chip->charger_rate; + break; + default: + rc = power_supply_get_property(chip->qcom_psy, + psp, &prop); + if (rc < 0) { + mmi_chrg_err(chip, "Get Unknown prop %d rc = %d\n", psp, rc); + rc = 0; + val->intval = -EINVAL; + } + if (!rc) + val->intval = prop.intval; + break; + + } + return rc; +} + +static int batt_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct mmi_charger_manager *chip = power_supply_get_drvdata(psy); + int rc = 0; + + switch (prop) { + case POWER_SUPPLY_PROP_CYCLE_COUNT: + chip->cycles = val->intval; + break; + default: + rc = power_supply_set_property(chip->qcom_psy, + prop, val); + if (rc < 0) { + mmi_chrg_err(chip, "Get Unknown prop %d rc = %d\n", prop, rc); + rc = 0; + } + break; + } + return rc; +} + +static int batt_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + switch (prop) { + case POWER_SUPPLY_PROP_STATUS: + case POWER_SUPPLY_PROP_INPUT_SUSPEND: + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + case POWER_SUPPLY_PROP_CAPACITY: + case POWER_SUPPLY_PROP_PARALLEL_DISABLE: + case POWER_SUPPLY_PROP_DP_DM: + case POWER_SUPPLY_PROP_RERUN_AICL: + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: + case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: + case POWER_SUPPLY_PROP_DIE_HEALTH: + case POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED: + case POWER_SUPPLY_PROP_CYCLE_COUNT: + return 1; + default: + break; + } + return 0; +} +static const struct power_supply_desc batt_psy_desc = { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = batt_props, + .num_properties = ARRAY_SIZE(batt_props), + .get_property = batt_get_property, + .set_property = batt_set_property, + .property_is_writeable = batt_is_writeable, +}; +static int batt_psy_register(struct mmi_charger_manager *chip) +{ + struct power_supply_config batt_cfg = {}; + + batt_cfg.drv_data = chip; + batt_cfg.of_node = chip->dev->of_node; + chip->batt_psy = power_supply_register(chip->dev, + &batt_psy_desc, + &batt_cfg); + if (IS_ERR(chip->batt_psy)) { + pr_err("Couldn't register batt_psy power supply\n"); + return PTR_ERR(chip->batt_psy); + } + + pr_info("power supply register batt_psy successfully\n"); + return 0; +} +#endif +static enum power_supply_property mmi_chrg_mgr_props[] = { + POWER_SUPPLY_PROP_PD_CURRENT_MAX, + POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, + POWER_SUPPLY_PROP_NUM_SYSTEM_TEMP_LEVELS, + POWER_SUPPLY_PROP_CP_ENABLE, +}; + +static int mmi_chrg_mgr_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mmi_charger_manager *chip = power_supply_get_drvdata(psy); + union power_supply_propval prop = {0,}; + int rc, i, chrg_cnt = 0; + + if (!chip->batt_psy) { + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) + return -ENODEV; + } + + switch (psp) { + case POWER_SUPPLY_PROP_PD_CURRENT_MAX: + val->intval = chip->pd_request_curr_prev; + break; + case POWER_SUPPLY_PROP_PD_VOLTAGE_MAX: + val->intval = chip->pd_request_volt_prev; + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED: + val->intval = chip->pd_target_curr; + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED: + val->intval = chip->pd_target_volt; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + val->intval = prop.intval; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + val->intval = prop.intval; + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + if (chip->mmi_chrg_dev_num) { + for (i = 0; i < chip->mmi_chrg_dev_num; i++) { + if (chip->chrg_list[i]->charger_enabled) + chrg_cnt++; + } + val->intval = chrg_cnt; + } else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + val->intval = chip->system_thermal_level; + break; + case POWER_SUPPLY_PROP_NUM_SYSTEM_TEMP_LEVELS: + val->intval = chip->thermal_levels; + break; + case POWER_SUPPLY_PROP_CP_ENABLE: + val->intval = !chip->cp_disable; + break; + default: + return -EINVAL; + + } + return 0; +} + +static int mmi_chrg_mgr_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct mmi_charger_manager *chip = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + if (val->intval < 0) + return -EINVAL; + + if (chip->thermal_levels <= 0) + return -EINVAL; + + if (val->intval >= chip->thermal_levels) + chip->system_thermal_level = + chip->thermal_levels - 1; + else + chip->system_thermal_level = val->intval; + + break; + case POWER_SUPPLY_PROP_PD_CURRENT_MAX: + if (val->intval > chip->pd_curr_max) + return -EINVAL; + chip->pd_curr_max = val->intval; + break; + case POWER_SUPPLY_PROP_PD_VOLTAGE_MAX: + if (val->intval > chip->pd_volt_max) + return -EINVAL; + chip->pd_volt_max= val->intval; + break; + case POWER_SUPPLY_PROP_CP_ENABLE: + if (chip->pd_pps_support + && !chip->factory_mode) + mmi_chrg_enable_all_cp(chip, val->intval); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mmi_chrg_mgr_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + int ret; + + switch (prop) { + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + case POWER_SUPPLY_PROP_PD_CURRENT_MAX: + case POWER_SUPPLY_PROP_PD_VOLTAGE_MAX: + case POWER_SUPPLY_PROP_CP_ENABLE: + ret = 1; + break; + default: + ret = 0; + break; + } + return ret; +} + +static const struct power_supply_desc mmi_chrg_mgr_psy_desc = { + .name = "mmi_chrg_manager_qc3p", + .type = POWER_SUPPLY_TYPE_PARALLEL, + .properties = mmi_chrg_mgr_props, + .num_properties = ARRAY_SIZE(mmi_chrg_mgr_props), + .get_property = mmi_chrg_mgr_get_property, + .set_property = mmi_chrg_mgr_set_property, + .property_is_writeable = mmi_chrg_mgr_is_writeable, +}; + +static int mmi_chrg_mgr_psy_register(struct mmi_charger_manager *chip) +{ + struct power_supply_config mmi_chrg_mgr_cfg = {}; + + mmi_chrg_mgr_cfg.drv_data = chip; + mmi_chrg_mgr_cfg.of_node = chip->dev->of_node; + chip->mmi_chrg_mgr_psy = power_supply_register(chip->dev, + &mmi_chrg_mgr_psy_desc, + &mmi_chrg_mgr_cfg); + if (IS_ERR(chip->mmi_chrg_mgr_psy)) { + pr_err("Couldn't register mmi_chrg_mgr_psy power supply\n"); + return PTR_ERR(chip->mmi_chrg_mgr_psy); + } + + pr_info("power supply register mmi_chrg_mgr_psy successfully\n"); + return 0; +} + +static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) +{ + struct mmi_charger_manager *chip = + container_of(nb, struct mmi_charger_manager, psy_nb); + + if (!chip->usb_psy) { + mmi_chrg_err(chip, "usb psy is NULL, direct return\n"); + return NOTIFY_OK; + } + + if (ptr == chip->usb_psy && evt == PSY_EVENT_PROP_CHANGED) { + schedule_work(&chip->psy_changed_work); + + cancel_delayed_work(&chip->heartbeat_work); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(1000)); + + } + + return NOTIFY_OK; +} + +static int get_prop_charger_present(struct mmi_charger_manager *chg, + union power_supply_propval *val) +{ + int rc = -EINVAL; + val->intval = 0; + + if (chg->usb_psy) + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_TYPEC_MODE, val); + if (rc < 0) { + mmi_chrg_err(chg, "Couldn't read TypeC Mode rc=%d\n", rc); + return rc; + } + + switch (val->intval) { + case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT: + case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM: + case POWER_SUPPLY_TYPEC_SOURCE_HIGH: + val->intval = 1; + break; + default: + val->intval = 0; + break; + } + + return rc; +} + +#define WEAK_CHRG_THRSH 450 +#define TURBO_CHRG_THRSH 2500 +#define TURBO_30W_CHRG_THRSH_UW 25000000 +#define HYPER_CHRG_THRSH_UW 40000000 + +bool mmi_chrg_rate_check(struct mmi_charger_manager *chg) +{ + union power_supply_propval val; + int chrg_cm_ma = 0; + int chrg_cs_ma = 0; + int charger_power = 0; + int prev_chg_rate = chg->charger_rate; + int rc = -EINVAL; + + if (!chg->usb_psy) { + mmi_chrg_err(chg, "No usb PSY\n"); + return false; + } + + val.intval = 0; + rc = get_prop_charger_present(chg, &val); + if (rc < 0) { + mmi_chrg_err(chg, "Error getting Charger Present rc = %d\n", rc); + return false; + } + + if (val.intval) { + val.intval = 0; + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_HW_CURRENT_MAX, &val); + if (rc < 0) { + mmi_chrg_info(chg, "Error getting HW Current Max rc = %d\n", rc); + return false; + } + chrg_cm_ma = val.intval / 1000; + + val.intval = 0; + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, &val); + if (rc < 0) { + mmi_chrg_err(chg, "Error getting ICL Settled rc = %d\n", rc); + return false; + } + chrg_cs_ma = val.intval / 1000; + } else { + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_NONE; + goto end_rate_check; + } + + mmi_chrg_info(chg, "SMBMMI: cm %d, cs %d\n", chrg_cm_ma, chrg_cs_ma); + if (chrg_cm_ma >= TURBO_CHRG_THRSH) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_TURBO; + else if ((chrg_cm_ma > WEAK_CHRG_THRSH) && (chrg_cs_ma < WEAK_CHRG_THRSH)) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_WEAK; + else if (prev_chg_rate == POWER_SUPPLY_CHARGE_RATE_NONE) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_NORMAL; + + if (chg->pd_pps_support) { + charger_power = (chg->pd_curr_max / 1000) * (chg->pd_volt_max / 1000); + if (charger_power >= HYPER_CHRG_THRSH_UW) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_HYPER; + else if (charger_power >= TURBO_30W_CHRG_THRSH_UW) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_TURBO_30W; + } +end_rate_check: + if (prev_chg_rate != chg->charger_rate) { + mmi_chrg_info(chg, "%s Charger Detected\n", + charge_rate[chg->charger_rate]); + return true; + } else + return false; +} +#if 0 +#define CHG_SHOW_MAX_SIZE 50 +static ssize_t factory_image_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("Invalid factory image mode value = %lu\n", mode); + return -EINVAL; + } + + mmi_set_factory_image_mode(mode); + + return r ? r : count; +} + +static ssize_t factory_image_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + state = mmi_get_factory_image_mode() ? 1 : 0; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(factory_image_mode, 0644, + factory_image_mode_show, + factory_image_mode_store); + +static ssize_t factory_charge_upper_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + state = mmi_get_factory_charge_upper(); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(factory_charge_upper, 0444, + factory_charge_upper_show, + NULL); + +static ssize_t force_demo_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("Invalid demo mode value = %lu\n", mode); + return -EINVAL; + } + + mmi_set_demo_mode(mode); + + return r ? r : count; +} + +static ssize_t force_demo_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + + state = mmi_get_demo_mode(); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_demo_mode, 0644, + force_demo_mode_show, + force_demo_mode_store); + +static ssize_t force_max_chrg_temp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("Invalid max temp value = %lu\n", mode); + return -EINVAL; + } + + mmi_set_max_chrg_temp(mode); + + return r ? r : count; +} + +static ssize_t force_max_chrg_temp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + state = mmi_get_max_chrg_temp(); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_max_chrg_temp, 0644, + force_max_chrg_temp_show, + force_max_chrg_temp_store); +#endif +static bool mmi_factory_check(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool factory = false; + + if (np) + factory = of_property_read_bool(np, "mmi,factory-cable"); + + of_node_put(np); + + return factory; +} + + +static void kick_sm(struct mmi_charger_manager *chip, int ms) +{ + union power_supply_propval val; + int ret; + + if (!chip->sm_work_running) { + mmi_chrg_dbg(chip, PR_INTERRUPT, + "launch mmi chrg sm work\n"); + mmi_chrg_policy_clear(chip); + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(ms)); + chip->sm_work_running = true; + val.intval = true; + ret = power_supply_set_property(chip->usb_psy, + POWER_SUPPLY_PROP_CP_ENABLE, &val); + if (ret) + mmi_chrg_err(chip, "Unable to enable CP: %d\n", ret); + } else + mmi_chrg_dbg(chip, PR_INTERRUPT, + "mmi chrg sm work already existed\n"); +} + +static void cancel_sm(struct mmi_charger_manager *chip) +{ + union power_supply_propval val; + int ret; + + cancel_delayed_work_sync(&chip->mmi_chrg_sm_work); + flush_delayed_work(&chip->mmi_chrg_sm_work); + mmi_chrg_policy_clear(chip); + chip->sm_work_running = false; + val.intval = false; + ret = power_supply_set_property(chip->usb_psy, + POWER_SUPPLY_PROP_CP_ENABLE, &val); + if (ret) + mmi_chrg_err(chip, "Unable to disable CP: %d\n", ret); + chip->pd_volt_max = pd_volt_max_init; + chip->pd_curr_max = pd_curr_max_init; + mmi_chrg_dbg(chip, PR_INTERRUPT, + "cancel sync and flush mmi chrg sm work\n"); +} + +void clear_chg_manager(struct mmi_charger_manager *chip) +{ + mmi_chrg_dbg(chip, PR_INTERRUPT, "clear mmi chrg manager!\n"); + chip->pd_request_volt = 0; + chip->pd_request_curr = 0; + chip->pd_request_volt_prev = 0; + chip->pd_request_curr_prev = 0; + chip->pd_target_curr = 0; + chip->pd_target_volt = 0; + chip->pd_batt_therm_volt = 0; + chip->pd_batt_therm_curr = 0; + chip->pd_sys_therm_volt = 0; + chip->pd_sys_therm_curr = 0; + chip->recovery_pmic_chrg = false; + chip->sys_therm_cooling= false; + chip->sys_therm_force_pmic_chrg = false; + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + + memset(chip->mmi_pdo_info, 0, + sizeof(struct usbpd_pdo_info) * PD_MAX_PDO_NUM); +} + +static void mmi_awake_vote(struct mmi_charger_manager *chip, bool awake) +{ + if (awake == chip->awake) + return; + + chip->awake = awake; + if (awake) + pm_stay_awake(chip->dev); + else + pm_relax(chip->dev); +} + +static void mmi_batt_age_init(struct mmi_charger_manager *chip) +{ + int rc = 0; + union power_supply_propval pval; + if (!chip->batt_psy) { + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) + return; + } + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_STATUS, &pval); + if (rc < 0) { + mmi_chrg_err(chip, "Couldn't get batt status\n"); + return; + } + chip->pres_batt_status = pval.intval; + chip->prev_batt_status = chip->pres_batt_status; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &pval); + if (rc < 0) { + mmi_chrg_err(chip, "Couldn't get batt capacity\n"); + return; + } + + chip->soc_cycles_start= pval.intval; + chip->batt_cap_delta = 0; + return; +} + +static void mmi_cycle_counts(struct mmi_charger_manager *chip) +{ + int rc = 0; + int batt_design_capacity = 0, new_cycles = 0; + int soc_report = 0, soc_delta = 0, batt_delta_capacity = 0; + union power_supply_propval pval; + if (!chip->batt_psy) { + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) + return; + } + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, &pval); + if (rc < 0) { + mmi_chrg_err(chip, "Couldn't get batt design capacity\n"); + return; + } + batt_design_capacity = pval.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_STATUS, &pval); + if (rc < 0) { + mmi_chrg_err(chip, "Couldn't get batt status\n"); + return; + } + chip ->pres_batt_status = pval.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &pval); + if (rc < 0) { + mmi_chrg_err(chip, "Couldn't get batt capacity\n"); + return; + } + soc_report = pval.intval; + + mmi_chrg_dbg(chip, PR_MOTO, "cycle_count %d, pres %d, prev %d, soc_report %d, " + "soc_cycles_start %d, batt_cap_delta %d, " + "design_capcaity %d, batt_soc_delta %d\n", + chip->cycles, chip->pres_batt_status, + chip->prev_batt_status, + soc_report, chip->soc_cycles_start, + chip->batt_cap_delta, batt_design_capacity, + chip->batt_soc_delta); + + if (chip->prev_batt_status == POWER_SUPPLY_STATUS_CHARGING + && chip->pres_batt_status != POWER_SUPPLY_STATUS_CHARGING) { + chip->soc_cycles_start = soc_report; + } else if (chip->prev_batt_status != POWER_SUPPLY_STATUS_CHARGING + && chip->pres_batt_status == POWER_SUPPLY_STATUS_CHARGING) { + + soc_delta = chip->soc_cycles_start - soc_report; + if (soc_delta > 0) { + chip->batt_soc_delta += soc_delta; + + batt_delta_capacity = (chip->batt_soc_delta * batt_design_capacity) / 100 + + chip->batt_cap_delta; + + new_cycles = batt_delta_capacity / batt_design_capacity; + if (new_cycles > 0) { + chip->cycles += batt_delta_capacity / batt_design_capacity; + chip->batt_cap_delta = batt_delta_capacity % batt_design_capacity; + chip->batt_soc_delta = 0; + } + } + chip->soc_cycles_start = soc_report; + } + + chip->prev_batt_status = chip->pres_batt_status; +} + +static bool __mmi_ps_is_supplied_by(struct power_supply *supplier, + struct power_supply *supply) +{ + int i; + + if (!supply->supplied_from && !supplier->supplied_to) + return false; + + /* Support both supplied_to and supplied_from modes */ + if (supply->supplied_from) { + if (!supplier->desc->name) + return false; + for (i = 0; i < supply->num_supplies; i++) + if (!strcmp(supplier->desc->name, + supply->supplied_from[i])) + return true; + } else { + if (!supply->desc->name) + return false; + for (i = 0; i < supplier->num_supplicants; i++) + if (!strcmp(supplier->supplied_to[i], + supply->desc->name)) + return true; + } + + return false; +} + +static int __mmi_ps_changed(struct device *dev, void *data) +{ + struct power_supply *psy = data; + struct power_supply *pst = dev_get_drvdata(dev); + + if (__mmi_ps_is_supplied_by(psy, pst)) { + if (pst->desc->external_power_changed) + pst->desc->external_power_changed(pst); + } + + return 0; +} + +static void mmi_power_supply_changed(struct power_supply *psy, + char *envp_ext[]) +{ + dev_err(&psy->dev, "%s: %s\n", __func__, envp_ext[0]); + + class_for_each_device(power_supply_class, NULL, psy, + __mmi_ps_changed); + atomic_notifier_call_chain(&power_supply_notifier, + PSY_EVENT_PROP_CHANGED, psy); + kobject_uevent_env(&psy->dev.kobj, KOBJ_CHANGE, envp_ext); +} + +static enum alarmtimer_restart mmi_heartbeat_alarm_cb(struct alarm *alarm, + ktime_t now) +{ + struct mmi_charger_manager *chip = container_of(alarm, + struct mmi_charger_manager, + heartbeat_alarm); + + mmi_chrg_info(chip, "MMI: HB alarm fired\n"); + + __pm_stay_awake(&chip->mmi_hb_wake_source); + cancel_delayed_work(&chip->heartbeat_work); + /* Delay by 500 ms to allow devices to resume. */ + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(500)); + + return ALARMTIMER_NORESTART; +} + +#define HEARTBEAT_DELAY_MS 60000 +#define SMBCHG_HEARTBEAT_INTRVAL_NS 70000000000 +#define CHG_SHOW_MAX_SIZE 50 +static void mmi_heartbeat_work(struct work_struct *work) +{ + struct mmi_charger_manager *chip = container_of(work, + struct mmi_charger_manager, heartbeat_work.work); + int hb_resch_time = 0, ret = 0; + char *chrg_rate_string = NULL; + char *envp[2]; + union power_supply_propval val; + + mmi_chrg_info(chip, "MMI: Heartbeat!\n"); + /* Have not been resumed so wait another 100 ms */ + if (chip->suspended) { + mmi_chrg_err(chip, "SMBMMI: HB running before Resume\n"); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(100)); + return; + } + + mmi_awake_vote(chip, true); + alarm_try_to_cancel(&chip->heartbeat_alarm); + + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) { + mmi_chrg_err(chip, + "Could not get USB power_supply, deferring probe\n"); + return; + } + } + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &val); + if (ret) { + mmi_chrg_err(chip, "Unable to read USB PRESENT: %d\n", ret); + return; + } + chip->vbus_present = val.intval; + + hb_resch_time = HEARTBEAT_DELAY_MS; + + if (chip->vbus_present) + alarm_start_relative(&chip->heartbeat_alarm, + ns_to_ktime(SMBCHG_HEARTBEAT_INTRVAL_NS)); + + if (!chip->vbus_present) + mmi_awake_vote(chip, false); + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PD_ACTIVE, &val); + if (ret) { + mmi_chrg_err(chip, "Unable to read PD ACTIVE: %d\n", ret); + goto schedule_work; + } + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_REAL_TYPE, &val); + if (ret ) { + mmi_chrg_err(chip, "Unable to read real type return: %d\n", ret); + goto schedule_work; + } + + if(val.intval == POWER_SUPPLY_TYPE_USB_HVDCP_3P5) + chip->qc3p_active = true; + else + chip->qc3p_active = false; + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_HVDCP_POWER, &val); + if (ret) { + mmi_chrg_err(chip, "Unable to read qc3p power: %d\n", ret); + goto schedule_work; + } + + chip->qc3p_power = val.intval; + mmi_chrg_err(chip, "qc3p detected power:%d\n",chip->qc3p_power); + + if (!chip->sm_work_running && chip->vbus_present + && chip->qc3p_active) { + chip->pd_pps_support = true; + calculate_qc3p_vc_based_power_type(chip); + + if (chip->pd_pps_support + && !chip->factory_mode) { + mmi_chrg_info(chip, "MMI: Heartbeat!, launch sm work\n"); + kick_sm(chip, 100); + } + } + + if (chip->use_batt_age) + mmi_cycle_counts(chip); + + if(mmi_chrg_rate_check(chip)) { + if (!chip->extrn_fg) { + val.intval = chip->charger_rate; + ret = power_supply_set_property(chip->batt_psy, + POWER_SUPPLY_PROP_CHARGE_RATE, &val); + if (ret) + mmi_chrg_err(chip, "Unable to set charge rate: %d\n", ret); + } + + chrg_rate_string = kmalloc(CHG_SHOW_MAX_SIZE, GFP_KERNEL); + if (!chrg_rate_string) { + mmi_chrg_err(chip, "SMBMMI: Failed to Get Uevent Mem\n"); + envp[0] = NULL; + } else { + scnprintf(chrg_rate_string, CHG_SHOW_MAX_SIZE, + "POWER_SUPPLY_CHARGE_RATE=%s", + charge_rate[chip->charger_rate]); + envp[0] = chrg_rate_string; + envp[1] = NULL; + } + + if (chip->qcom_psy) { + mmi_power_supply_changed(chip->batt_psy, envp); + } + + kfree(chrg_rate_string); + } + + __pm_relax(&chip->mmi_hb_wake_source); + +schedule_work: + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(hb_resch_time)); +} + +void calculate_qc3p_vc_based_power_type(struct mmi_charger_manager *chip){ + int volt_max,curr_max; + + switch(chip->qc3p_power) { + case QC3P_POWER_18W: + volt_max = 9500000; + curr_max = 1900000; + break; + case QC3P_POWER_27W: + volt_max = 11000000; + curr_max = 3000000; + break; + case QC3P_POWER_45W: + volt_max = 15000000; + curr_max = 3000000; + break; + default: + volt_max = 5000000; + curr_max = 500000; + break; + } + + chip->pd_volt_max = volt_max; + chip->pd_curr_max = curr_max; + + mmi_chrg_info(chip, "calculate qc3p vc base power, volt_max:%d,curr_max:%d\n",chip->pd_volt_max, chip->pd_curr_max ); +} + +static void psy_changed_work_func(struct work_struct *work) +{ + struct mmi_charger_manager *chip = container_of(work, + struct mmi_charger_manager, psy_changed_work); + union power_supply_propval val; + int ret; + + mmi_chrg_info(chip, "kick psy changed work.\n"); + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) { + mmi_chrg_err(chip, + "Could not get USB power_supply, deferring probe\n"); + return; + } + } + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &val); + if (ret) { + mmi_chrg_err(chip, "Unable to read USB PRESENT: %d\n", ret); + return; + } + chip->vbus_present = val.intval; + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_REAL_TYPE, &val); + if (ret) { + mmi_chrg_err(chip, "Unable to read qc3p type or not the qc3p5 type return: %d\n", ret); + return; + } + + if(val.intval == POWER_SUPPLY_TYPE_USB_HVDCP_3P5 ) + chip->qc3p_active = true; + else + chip->qc3p_active = false; + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_HVDCP_POWER, &val); + if (ret) { + mmi_chrg_err(chip, "Unable to read qc3p power: %d\n", ret); + return; + } + + chip->qc3p_power = val.intval; + mmi_chrg_err(chip, "qc3p detected power:%d\n",chip->qc3p_power); + + if (chip->qc3p_active && chip->vbus_present) { + chip->pd_pps_support = true; + calculate_qc3p_vc_based_power_type(chip); + } + + mmi_chrg_info(chip, "vbus present %d, qc3p pps support %d, " + "pps max voltage %d, pps max curr %d\n", + chip->vbus_present, + chip->pd_pps_support, + chip->pd_volt_max, + chip->pd_curr_max); + + if (chip->vbus_present + && chip->pd_pps_support + && !chip->factory_mode) { + kick_sm(chip, 100); + } else { + cancel_sm(chip); + clear_chg_manager(chip); + chip->pd_pps_support = false; + } + return; +} + +#define DEFAULT_BATT_OVP_LMT 4475000 +#define DEFAULT_PL_CHRG_VBATT_MIN 3600000 +#define DEFAULT_PPS_VOLT_STEPS 20000 +#define DEFAULT_PPS_CURR_STEPS 50000 +#define DEFAULT_PPS_VOLT_MAX 11000000 +#define DEFAULT_PPS_CURR_MAX 4000000 +#define DEFAULT_HVDCP_POWER_MAX 15000 +#define MMI_TEMP_ZONES 5 +static int mmi_chrg_manager_parse_dt(struct mmi_charger_manager *chip) +{ + struct device_node *node = chip->dev->of_node, *child; + int rc, byte_len, i, j, chrg_idx = 0, step_cnt = 0, idx = 0; + const int *ptr; + + rc = of_property_read_u32(node, + "mmi,batt-ovp-lmt", + &chip->batt_ovp_lmt); + if (rc < 0) + chip->batt_ovp_lmt = + DEFAULT_BATT_OVP_LMT; + + rc = of_property_read_u32(node, + "mmi,pl-chrg-vbatt-min", + &chip->pl_chrg_vbatt_min); + if (rc < 0) + chip->pl_chrg_vbatt_min = + DEFAULT_PL_CHRG_VBATT_MIN; + + chip->use_batt_age = of_property_read_bool(node, + "mmi,use-batt-age"); + + chip->extrn_fg = of_property_read_bool(node, + "mmi,extrn-fg"); + + if (chip->extrn_fg) { + byte_len = of_property_count_strings(node, "mmi,extrn-fg-name"); + if (byte_len <= 0) { + mmi_chrg_err(chip, "Cannot parse mmi, extrn-fg: %d\n", byte_len); + return byte_len; + } + + for (i = 0; i < byte_len; i++) { + rc = of_property_read_string_index(node, "mmi,extrn-fg-name", + i, &chip->extrn_fg_name); + if (rc < 0) { + mmi_chrg_err(chip, "Cannot parse extrn-fg-name\n"); + return rc; + } + } + } + + chip->extrn_sense = of_property_read_bool(node, + "mmi,extrn-sense"); + + chip->dont_rerun_aicl= of_property_read_bool(node, + "mmi,dont-rerun-aicl"); + + rc = of_property_read_u32(node, + "mmi,typec-middle-current", + &chip->typec_middle_current); + if (rc < 0) + chip->typec_middle_current = + TYPEC_MIDDLE_CURRENT_UA; + + rc = of_property_read_u32(node, + "mmi,step-first-current-comp", + &chip->step_first_curr_comp); + if (rc < 0) + chip->step_first_curr_comp = + STEP_FIREST_CURR_COMP; + + rc = of_property_read_u32(node, + "mmi,pps-volt-steps", + &chip->pps_volt_steps); + if (rc < 0) + chip->pps_volt_steps = + DEFAULT_PPS_VOLT_STEPS; + + rc = of_property_read_u32(node, + "mmi,pps-curr-steps", + &chip->pps_curr_steps); + if (rc < 0) + chip->pps_curr_steps = + DEFAULT_PPS_CURR_STEPS; + + rc = of_property_read_u32(node, + "mmi,pd-volt-max", + &chip->pd_volt_max); + if (rc < 0) + chip->pd_volt_max = + DEFAULT_PPS_VOLT_MAX; + pd_volt_max_init = chip->pd_volt_max; + rc = of_property_read_u32(node, + "mmi,pd-curr-max", + &chip->pd_curr_max); + if (rc < 0) + chip->pd_curr_max = + DEFAULT_PPS_CURR_MAX; + pd_curr_max_init = chip->pd_curr_max; + + rc = of_property_read_u32(node, + "mmi,hvdcp-power-max", + &chip->hvdcp_power_max); + if (rc < 0) + chip->hvdcp_power_max = + DEFAULT_HVDCP_POWER_MAX; + + chip->switch_charger_pps_volt = (chip->hvdcp_power_max /60) * 20000; + mmi_chrg_err(chip,"qc3p switch_charger_pps_volt:%d\n",chip->switch_charger_pps_volt); + + for_each_child_of_node(node, child) + chip->mmi_chrg_dev_num++; + + if (!chip->mmi_chrg_dev_num) { + mmi_chrg_err(chip,"No mmi_chrg_dev list !\n"); + return -ENODEV; + } + + chrg_name_list = (struct mmi_chrg_dts_info *)devm_kzalloc(chip->dev, + sizeof(struct mmi_chrg_dts_info) * + chip->mmi_chrg_dev_num, GFP_KERNEL); + if (!chrg_name_list) { + mmi_chrg_err(chip,"No memory for mmi charger device name list !\n"); + goto cleanup; + } + + chip->chrg_list = (struct mmi_charger_device **)devm_kzalloc(chip->dev, + sizeof(struct mmi_charger_device *) * + chip->mmi_chrg_dev_num, GFP_KERNEL); + if (!chip->chrg_list) { + mmi_chrg_err(chip,"No memory for mmi charger device list !\n"); + goto cleanup; + } + + for_each_child_of_node(node, child) { + byte_len = of_property_count_strings(child, "chrg-name"); + if (byte_len <= 0) { + mmi_chrg_err(chip, "Cannot parse chrg-name: %d\n", byte_len); + goto cleanup; + } + + for (i = 0; i < byte_len; i++) { + rc = of_property_read_string_index(child, "chrg-name", + i, &chrg_name_list[chrg_idx].chrg_name); + if (rc < 0) { + mmi_chrg_err(chip, "Cannot parse chrg-name\n"); + goto cleanup; + } + } + + byte_len = of_property_count_strings(child, "psy-name"); + if (byte_len <= 0) { + mmi_chrg_err(chip, "Cannot parse psy-name: %d\n", byte_len); + goto cleanup; + } + + for (i = 0; i < byte_len; i++) { + rc = of_property_read_string_index(child, "psy-name", + i, &chrg_name_list[chrg_idx].psy_name); + if (rc < 0) { + mmi_chrg_err(chip, "Cannot parse psy-name\n"); + goto cleanup; + } + } + + mmi_chrg_info(chip, "mmi,chrg-name: %s, psy-name: %s\n", + chrg_name_list[chrg_idx].chrg_name, + chrg_name_list[chrg_idx].psy_name); + + of_property_read_u32(child, "charging-curr-limited", + &chrg_name_list[chrg_idx].charging_curr_limited); + + of_property_read_u32(child, "charging-curr-min", + &chrg_name_list[chrg_idx].charging_curr_min); + + chrg_idx++; + } + + rc = of_property_read_u32(node, + "mmi,chrg-temp-zones-num", + &chip->num_temp_zones); + if (rc < 0) + chip->num_temp_zones = + MMI_TEMP_ZONES; + + chip->temp_zones = (struct mmi_chrg_temp_zone *) + devm_kzalloc(chip->dev, + sizeof(struct mmi_chrg_temp_zone ) + * chip->num_temp_zones, + GFP_KERNEL); + if (chip->temp_zones == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + if (of_find_property(node, "mmi,mmi-chrg-temp-zones", &byte_len)) { + if (byte_len <= 0) { + mmi_chrg_err(chip, + "Cannot parse mmi-chrg-temp-zones %d\n",byte_len); + goto cleanup; + } + + step_cnt = (byte_len / chip->num_temp_zones - sizeof(u32)) + / (sizeof(u32) * 2); + mmi_chrg_info(chip, "mmi chrg step number is %d\n", step_cnt); + + dt_temp_zones = (u32 *) devm_kzalloc(chip->dev, byte_len, GFP_KERNEL); + if (dt_temp_zones == NULL) { + rc = -ENOMEM; + goto cleanup; + } + + rc = of_property_read_u32_array(node, + "mmi,mmi-chrg-temp-zones", + (u32 *)dt_temp_zones, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_chrg_err(chip, + "Couldn't read mmi chrg temp zones rc = %d\n", rc); + goto zones_failed; + } + + mmi_chrg_err(chip, "temp zone nums: %d , byte_len %d, " + "num %d, row size %d\n", + chip->num_temp_zones, byte_len, + (int)(byte_len / sizeof(u32)), + (int)(sizeof(struct mmi_chrg_step_power) * step_cnt)); + ptr = dt_temp_zones; + + for (i = 0; i < byte_len / sizeof(u32); i++) { + if (!(i % 9)) { + printk("\n"); + } + printk("%u,", ptr[i]); + } + printk("\n"); + + for (i = 0; i < chip->num_temp_zones; i++) { + idx = (int)((sizeof(u32) + + sizeof(struct mmi_chrg_step_power) * step_cnt ) + * i / sizeof(u32)); + chip->temp_zones[i].temp_c = dt_temp_zones[idx]; + } + + for (i = 0; i < chip->num_temp_zones; i++) { + idx = (int)(((sizeof(u32) + + sizeof(struct mmi_chrg_step_power) * step_cnt ) + * i + sizeof(u32)) / sizeof(u32)); + chip->temp_zones[i].chrg_step_power = + (struct mmi_chrg_step_power *)&dt_temp_zones[idx]; + } + + for (i = 0; i < chip->num_temp_zones; i++) { + + printk( "mmi temp zones: Zone: %d, Temp: %d C " , i, + chip->temp_zones[i].temp_c); + for (j = 0; j < step_cnt; j++) { + chip->temp_zones[i].chrg_step_power[j].chrg_step_curr *= 1000; + chip->temp_zones[i].chrg_step_power[j].chrg_step_volt *= 1000; + + printk("step_volt %dmV, step_curr %dmA, ", + chip->temp_zones[i].chrg_step_power[j].chrg_step_volt, + chip->temp_zones[i].chrg_step_power[j].chrg_step_curr); + } + printk("\n"); + } + + chip->pres_temp_zone = ZONE_NONE; + chip->chrg_step_nums = step_cnt; + } + + if (of_find_property(node, "mmi,thermal-mitigation", &byte_len)) { + chip->thermal_mitigation = devm_kzalloc(chip->dev, byte_len, + GFP_KERNEL); + + if (chip->thermal_mitigation == NULL) { + rc = -ENOMEM; + goto zones_failed; + } + chip->thermal_levels = byte_len / sizeof(u32); + rc = of_property_read_u32_array(node, + "mmi,thermal-mitigation", + chip->thermal_mitigation, + chip->thermal_levels); + if (rc < 0) { + mmi_chrg_err(chip, + "Couldn't parse thermal-mitigation rc = %d\n", rc); + goto thermal_failed; + } + } + + rc = of_property_read_u32(node, + "mmi,afvc_enable_compensation_uv", + &chip->afvc_volt_comp); + if (rc < 0) + chip->afvc_volt_comp = 0; + return rc; +thermal_failed: + devm_kfree(chip->dev,chip->thermal_mitigation); +zones_failed: + devm_kfree(chip->dev,chip->temp_zones); +cleanup: + chip->mmi_chrg_dev_num = 0; + devm_kfree(chip->dev,chip->chrg_list); + return rc; +} + +void init_qc3p_variable(struct mmi_charger_manager *chip) +{ + chip->qc3p_power = QC3P_POWER_NONE; + chip->qc3p_active = false; +} + +static int mmi_chrg_manager_probe(struct platform_device *pdev) +{ + int ret = 0; + struct mmi_charger_manager *chip; + + if (!pdev) + return -ENODEV; + + chip = devm_kzalloc(&pdev->dev, sizeof(struct mmi_charger_manager), + GFP_KERNEL); + if (!chip) { + dev_err(&pdev->dev, + "Unable to alloc memory for mmi_charger_manager\n"); + return -ENOMEM; + } + + chip->dev = &pdev->dev; + chip->name = "mmi_chrg_manager_qc3p"; + chip->debug_mask = &__debug_mask; + chip->suspended = false; + + ret = mmi_charger_class_init(); + if (ret < 0) { + dev_err(&pdev->dev, + "mmi charger class init failed\n"); + goto cleanup; + } + + ret = mmi_chrg_manager_parse_dt(chip); + if (ret < 0) { + dev_err(&pdev->dev, + "parse dt failed\n"); + goto cleanup; + } + + chip->factory_mode = mmi_factory_check(); + + chip->qcom_psy = power_supply_get_by_name("qcom_battery"); +#if 0 + if (chip->qcom_psy && chip->extrn_fg) { + + chip->extrn_psy = power_supply_get_by_name(chip->extrn_fg_name); + if (!chip->extrn_psy) { + mmi_chrg_err(chip, "Get extrn psy failed\n"); + goto cleanup; + } + ret = batt_psy_register(chip); + if (ret) { + mmi_chrg_err(chip, "Batt psy register failed\n"); + goto cleanup; + } + + ret = device_create_file(chip->dev, + &dev_attr_factory_image_mode); + if (ret) { + mmi_chrg_err(chip, "couldn't create factory_image_mode\n"); + } + + ret = device_create_file(chip->dev, + &dev_attr_force_demo_mode); + if (ret) { + mmi_chrg_err(chip, "couldn't create force_demo_mode\n"); + } + + ret = device_create_file(chip->dev, + &dev_attr_force_max_chrg_temp); + if (ret) { + mmi_chrg_err(chip, "couldn't create force_max_chrg_temp\n"); + } + + ret = device_create_file(chip->dev, + &dev_attr_factory_charge_upper); + if (ret) + mmi_chrg_err(chip, "couldn't create factory_charge_upper\n"); + + } else +#endif + chip->batt_psy = power_supply_get_by_name("battery"); + + if (!chip->batt_psy) { + mmi_chrg_err(chip, "Could not get battery power_supply\n"); + goto cleanup; + } + + + ret = mmi_chrg_policy_init(chip, chrg_name_list, + chip->mmi_chrg_dev_num); + if (ret < 0) { + dev_err(&pdev->dev, + "mmi chrg policy init failed\n"); + goto cleanup; + } + + chip->pd_handle = + devm_usbpd_get_by_phandle(chip->dev, "qcom,usbpd-phandle"); + if (IS_ERR_OR_NULL(chip->pd_handle)) { + dev_err(&pdev->dev, "Error getting the pd phandle %ld\n", + PTR_ERR(chip->pd_handle)); + chip->pd_handle = NULL; + } + + init_qc3p_variable(chip); + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) + mmi_chrg_err(chip, "Could not get USB power_supply\n"); + } + + INIT_WORK(&chip->psy_changed_work, psy_changed_work_func); + INIT_DELAYED_WORK(&chip->heartbeat_work, mmi_heartbeat_work); + wakeup_source_init_internal(&chip->mmi_hb_wake_source, + "mmi_hb_wake"); + alarm_init(&chip->heartbeat_alarm, ALARM_BOOTTIME, + mmi_heartbeat_alarm_cb); + + + ret = mmi_chrg_mgr_psy_register(chip); + if (ret) + goto cleanup; + + chip->psy_nb.notifier_call = psy_changed; + ret = power_supply_reg_notifier(&chip->psy_nb); + if (ret) + goto cleanup; + + mmi_batt_age_init(chip); + + init_completion(&chip->sm_completion); + platform_set_drvdata(pdev, chip); + + //create_sysfs_entries(chip); + schedule_work(&chip->psy_changed_work); + mmi_chrg_info(chip, "mmi chrg manager initialized successfully, ret %d\n", ret); + return 0; +cleanup: + mmi_charger_class_exit(); + devm_kfree(&pdev->dev, chip); + return ret; +} + +static int mmi_chrg_manager_remove(struct platform_device *pdev) +{ + struct mmi_charger_manager *chip = platform_get_drvdata(pdev); + + //remove_sysfs_entries(chip); + //cancel_delayed_work_sync(&chip->mmi_chrg_sm_work); + power_supply_unreg_notifier(&chip->psy_nb); + power_supply_unregister(chip->mmi_chrg_mgr_psy ); +// wakeup_source_trash_internal(&chip->mmi_hb_wake_source); + platform_set_drvdata(pdev, NULL); + devm_kfree(&pdev->dev, chip); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mmi_chrg_suspend(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct mmi_charger_manager *chip = platform_get_drvdata(pdev); + + chip->suspended = true; + + return 0; +} + +static int mmi_chrg_resume(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct mmi_charger_manager *chip = platform_get_drvdata(pdev); + + chip->suspended = false; + + return 0; +} +#else +#define smb_mmi_suspend NULL +#define smb_mmi_resume NULL +#endif + +static const struct dev_pm_ops mmi_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(mmi_chrg_suspend, mmi_chrg_resume) +}; + +static const struct of_device_id mmi_chrg_manager_match_table[] = { + {.compatible = "mmi,chrg-manager_qc3p"}, + {}, +}; +MODULE_DEVICE_TABLE(of, mmi_chrg_manager_match_table); + +static struct platform_driver mmi_chrg_manager_driver = { + .probe = mmi_chrg_manager_probe, + .remove = mmi_chrg_manager_remove, + .driver = { + .name = "mmi_chrg_manager_qc3p", + .owner = THIS_MODULE, + .pm = &mmi_dev_pm_ops, + .of_match_table = mmi_chrg_manager_match_table, + }, +}; + +static int __init mmi_chrg_manager_init(void) +{ + int ret; + ret = platform_driver_register(&mmi_chrg_manager_driver); + if (ret) { + pr_err("mmi_chrg_manager failed to register driver\n"); + return ret; + } + return 0; +} + +static void __exit mmi_chrg_manager_exit(void) +{ + platform_driver_unregister(&mmi_chrg_manager_driver); +} + +module_init(mmi_chrg_manager_init); +module_exit(mmi_chrg_manager_exit); + +MODULE_ALIAS("platform:mmi parallel charger"); +MODULE_AUTHOR("Motorola Mobility LLC"); +MODULE_DESCRIPTION("Motorola Mobility parallel charger"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/mmi_parallel_charger_qc3p/mmi_charger_core.h b/drivers/power/mmi_parallel_charger_qc3p/mmi_charger_core.h new file mode 100644 index 000000000000..7e7148d8c13d --- /dev/null +++ b/drivers/power/mmi_parallel_charger_qc3p/mmi_charger_core.h @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + +#ifndef __MMI_CHRG_CORE_H_ +#define __MMI_CHRG_CORE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include + +#define mmi_chrg_err(chip, fmt, ...) \ + pr_err("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__) \ + +#define mmi_chrg_info(chip, fmt, ...) \ + pr_info("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__) \ + +#define mmi_chrg_dbg(chip, reason, fmt, ...) \ + do { \ + if (*chip->debug_mask & (reason)) \ + pr_info("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__); \ + else \ + pr_debug("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__); \ + } while (0) + +enum print_reason { + PR_INTERRUPT = BIT(0), + PR_MISC = BIT(2), + PR_MOTO = BIT(7), +}; + +enum { + POWER_SUPPLY_CHARGE_RATE_NONE = 0, + POWER_SUPPLY_CHARGE_RATE_NORMAL, + POWER_SUPPLY_CHARGE_RATE_WEAK, + POWER_SUPPLY_CHARGE_RATE_TURBO, + POWER_SUPPLY_CHARGE_RATE_TURBO_30W, + POWER_SUPPLY_CHARGE_RATE_HYPER, +}; + +static inline void wakeup_source_init_internal(struct wakeup_source *ws, + const char *name) +{ + if(ws) { + memset(ws, 0, sizeof(*ws)); + ws->name = name; + } + + wakeup_source_add(ws); +} +/* +static inline void wakeup_source_trash_internal(struct wakeup_source *ws) +{ + + wakeup_source_remove(ws); + if(!ws) + return; + __pm_relex(ws); +}*/ + +#define MAX_NUM_STEPS 10 +enum mmi_chrg_temp_zones { + ZONE_FIRST = 0, + /* states 0-9 are reserved for zones */ + ZONE_LAST = MAX_NUM_STEPS + ZONE_FIRST - 1, + ZONE_HOT, + ZONE_COLD, + ZONE_NONE = 0xFF, +}; + +enum mmi_chrg_steps { + STEP_FIRST = 0, + STEP_LAST = MAX_NUM_STEPS + STEP_FIRST - 1, + STEP_NONE = 0xFF, +}; + +enum mmi_pd_pps_result { + NO_ERROR, + BLANCE_POWER, + RESET_POWER, +}; + +struct mmi_chrg_step_power { + u32 chrg_step_volt; + u32 chrg_step_curr; +}; + +struct mmi_chrg_temp_zone { + int temp_c; /*temperature*/ + struct mmi_chrg_step_power *chrg_step_power; +}; + +struct mmi_chrg_step_info { + enum mmi_chrg_steps pres_chrg_step; + int temp_c; + int chrg_step_cc_curr; + int chrg_step_cv_volt; + int chrg_step_cv_tapper_curr; + bool last_step; +}; + +struct mmi_chrg_dev_ops { + const char *dev_name; + struct mmi_charger_ops *ops; +}; + +struct mmi_chrg_dts_info { + const char *chrg_name; + const char *psy_name; + int charging_curr_limited; + int charging_curr_min; +}; + +enum qc3p_power_type{ + QC3P_POWER_NONE = 0, + QC3P_POWER_18W, + QC3P_POWER_27W, + QC3P_POWER_45W +}; + +#define PPS_RET_HISTORY_SIZE 8 +#define PD_SRC_PDO_TYPE_FIXED 0 +#define PD_SRC_PDO_TYPE_BATTERY 1 +#define PD_SRC_PDO_TYPE_VARIABLE 2 +#define PD_SRC_PDO_TYPE_AUGMENTED 3 +#define STEP_FIREST_CURR_COMP 300000 +#define TYPEC_HIGH_CURRENT_UA 3000000 +#define TYPEC_MIDDLE_CURRENT_UA 2000000 +#define SWITCH_CHARGER_PPS_VOLT 5000000 +#define PUMP_CHARGER_PPS_MIN_VOLT 8000000 +#define COOLING_HYSTERISIS_DEGC 2 +struct mmi_charger_manager { + const char *name; + struct device *dev; + struct power_supply *batt_psy; + struct power_supply *qcom_psy; + struct power_supply *extrn_psy; + struct power_supply *usb_psy; + struct power_supply *mmi_chrg_mgr_psy; + struct usbpd *pd_handle; + struct usbpd_pdo_info mmi_pdo_info[PD_MAX_PDO_NUM]; + struct notifier_block psy_nb; + + struct wakeup_source mmi_hb_wake_source; + struct alarm heartbeat_alarm; + bool factory_mode; + bool suspended; + bool awake; + bool cp_disable; + + int *debug_mask; + int mmi_pd_pdo_idx; /*request the pdo idx of PD*/ + int pps_volt_steps; /*PPS voltage, programming step size*/ + int pps_curr_steps; /*pps current, programming step size*/ + + int pd_volt_max; /*the Maximum request PD Voltage*/ + int pd_curr_max; /*the Maximum request PD current*/ + int batt_ovp_lmt; /*the battery over current limitation*/ + int pl_chrg_vbatt_min; /*the minimum battery voltage to enable parallel charging*/ + int hvdcp_power_max; //pmic charging max support power + int switch_charger_pps_volt;//pmic pps volt + + int typec_middle_current; + int step_first_curr_comp; + int pps_volt_comp; + int pd_request_volt; + int pd_request_curr; + /*the request PD power*/ + int pd_request_volt_prev; + int pd_request_curr_prev; + /*the previous request PD power*/ + int pd_sys_therm_volt; + int pd_sys_therm_curr; + /*the thermal PD power*/ + + int pd_batt_therm_volt; + int pd_batt_therm_curr; + + int pd_target_volt; + int pd_target_curr; + /*the final commited request PD power*/ + + int pps_result; + int pps_result_history[PPS_RET_HISTORY_SIZE]; + int pps_result_history_idx; + /*save the result of the return from PD request*/ + + int charger_rate; + bool vbus_present; + bool pd_pps_support; + bool pd_pps_balance; + bool freeze_pd_power; + bool extrn_fg; + const char *extrn_fg_name; + bool extrn_sense; + bool recovery_pmic_chrg; + bool dont_rerun_aicl; + + bool sys_therm_cooling; + bool sys_therm_force_pmic_chrg; + bool batt_therm_cooling; + int batt_therm_cooling_cnt; + + bool use_batt_age; + int cycles; + int batt_cap_delta; + int batt_soc_delta; + int soc_cycles_start; + int pres_batt_status; + int prev_batt_status; + + struct delayed_work mmi_chrg_sm_work; /*mmi charger state machine work*/ + struct delayed_work heartbeat_work; /*cycle trig heartbeat work*/ + struct completion sm_completion; + struct work_struct psy_changed_work; /*the change notification of power supply*/ + bool sm_work_running; + int num_temp_zones; + struct mmi_chrg_temp_zone *temp_zones; /*the temperature zone of charging*/ + enum mmi_chrg_temp_zones pres_temp_zone; /*the present zone idx*/ + + int *thermal_mitigation; /*thermal mitigation array*/ + int thermal_levels; + int system_thermal_level; /*thermal level setting*/ + + int chrg_step_nums; + struct mmi_chrg_step_info chrg_step; /*step charger info*/ + + int mmi_chrg_dev_num; + struct mmi_charger_device **chrg_list; /*charger device list*/ + enum qc3p_power_type qc3p_power; + bool qc3p_active; + int afvc_volt_comp; /*afvc enabled ,volt compensation uv*/ +}; + +extern bool mmi_get_pps_result_history(struct mmi_charger_manager *chip); +extern void mmi_set_pps_result_history(struct mmi_charger_manager *chip, int pps_result); +extern void mmi_clear_pps_result_history(struct mmi_charger_manager *chip); +extern int mmi_calculate_delta_volt(int pps_voltage, int pps_current, int delta_curr); +extern bool mmi_find_chrg_step(struct mmi_charger_manager *chip, int temp_zone, int vbatt_volt); +extern bool mmi_find_temp_zone(struct mmi_charger_manager *chip, int temp_c, bool ignore_hysteresis_degc); +extern void clear_chg_manager(struct mmi_charger_manager *chip); +extern void mmi_update_all_charger_status(struct mmi_charger_manager *chip); +extern void mmi_update_all_charger_error(struct mmi_charger_manager *chip); +extern void mmi_dump_charger_error(struct mmi_charger_manager *chip, + struct mmi_charger_device *chrg_dev); +extern ssize_t mmi_get_factory_image_mode(void); +extern ssize_t mmi_set_factory_image_mode(int mode); +extern ssize_t mmi_get_factory_charge_upper(void); +extern ssize_t mmi_get_demo_mode(void); +extern ssize_t mmi_set_demo_mode(int mode); +extern ssize_t mmi_get_max_chrg_temp(void); +extern ssize_t mmi_set_max_chrg_temp(int value); +extern void calculate_qc3p_vc_based_power_type(struct mmi_charger_manager *chip); +#endif diff --git a/drivers/power/mmi_parallel_charger_qc3p/mmi_charger_policy.h b/drivers/power/mmi_parallel_charger_qc3p/mmi_charger_policy.h new file mode 100644 index 000000000000..e3dfc285caa8 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_qc3p/mmi_charger_policy.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + #ifndef __MMI_CHRG_POLICY_H_ + #define __MMI_CHRG_POLICY_H_ +extern int mmi_chrg_policy_init(struct mmi_charger_manager *chip, + struct mmi_chrg_dts_info *chrg_dts, + int chrg_cnt); +extern void mmi_chrg_policy_clear(struct mmi_charger_manager *chip); +extern void mmi_chrg_enable_all_cp(struct mmi_charger_manager *chip, int val); +#endif diff --git a/drivers/power/mmi_parallel_charger_qc3p/mmi_charger_pump_policy.c b/drivers/power/mmi_parallel_charger_qc3p/mmi_charger_pump_policy.c new file mode 100644 index 000000000000..b4dc24775272 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_qc3p/mmi_charger_pump_policy.c @@ -0,0 +1,1985 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mmi_charger_core.h" + +extern struct mmi_charger_ops bq2597x_charger_ops; +extern struct mmi_charger_ops qpnp_pmic_charger_ops; +int qc3p_select_pdo(struct mmi_charger_manager *chip,int target_uv, int target_ua); +int get_caculated_real_ibus_vbus(struct mmi_charger_manager *chip, int *ibus_curr,int *calculated_vbus); +static const struct mmi_chrg_dev_ops dev_ops[] = { + { + .dev_name = "pmic-sw", + .ops = &qpnp_pmic_charger_ops, + }, + { + .dev_name = "cp-master", + .ops = &bq2597x_charger_ops, + }, + { + .dev_name = "cp-slave", + .ops = &bq2597x_charger_ops, + }, +}; + +enum mmi_chrg_dev { + PMIC_SW = 0, + CP_MASTER, + CP_SLAVE, + CHRG_NUM, +}; + +struct mmi_cp_policy_dev { + bool pmic_sw; + bool cp_master; + bool cp_slave; + bool cp_clave_later; + struct mmi_charger_device *chrg_dev[CHRG_NUM]; +}; + +typedef enum { + PM_STATE_DISCONNECT, + PM_STATE_ENTRY, + PM_STATE_SW_ENTRY, + PM_STATE_SW_LOOP, + PM_STATE_CHRG_PUMP_ENTRY, + PM_STATE_SINGLE_CP_ENTRY, + PM_STATE_DULE_CP_ENTRY, + PM_STATE_PPS_TUNNING_CURR, + PM_STATE_PPS_TUNNING_VOLT, + PM_STATE_CP_CC_LOOP, + PM_STATE_CP_CV_LOOP, + PM_STATE_CP_QUIT, + PM_STATE_RECOVERY_SW, + PM_STATE_STOP_CHARGE, + PM_STATE_COOLING_LOOP, + PM_STATE_POWER_LIMIT_LOOP, +} pm_sm_state_t; + +const unsigned char *pm_state_str[] = { + "PM_STATE_DISCONNECT", + "PM_STATE_ENTRY", + "PM_STATE_SW_ENTRY", + "PM_STATE_SW_LOOP", + "PM_STATE_CHRG_PUMP_ENTRY", + "PM_STATE_SINGLE_CP_ENTRY", + "PM_STATE_DULE_CP_ENTRY", + "PM_STATE_PPS_TUNNING_CURR", + "PM_STATE_PPS_TUNNING_VOLT", + "PM_STATE_CP_CC_LOOP", + "PM_STATE_CP_CV_LOOP", + "PM_STATE_CP_QUIT", + "PM_STATE_RECOVERY_SW", + "PM_STATE_STOP_CHARGE", + "PM_STATE_COOLING_LOOP", + "PM_STATE_POWER_LIMIT_LOOP", +}; + +static pm_sm_state_t sm_state = PM_STATE_DISCONNECT; +static pm_sm_state_t prev_sm_state = PM_STATE_DISCONNECT; +static int chrg_cc_power_tunning_cnt = 0; +static int chrg_cv_taper_tunning_cnt = 0; +static int chrg_cv_delta_volt = 0; +static int quit_slave_chrg_cnt = 0; +static int batt_curr_roof = 0; +static int pd_constant_power_cnt = 0; +static struct mmi_cp_policy_dev g_chrg_list = {0}; + +static void mmi_chrg_sm_move_state(struct mmi_charger_manager *chip, pm_sm_state_t state) +{ + mmi_chrg_dbg(chip, PR_INTERRUPT, "pm_state change:%s -> %s\n", + pm_state_str[sm_state], pm_state_str[state]); + + //add for PM_STATE_POWER_LIMIT_LOOP + prev_sm_state = sm_state; + sm_state = state; + pd_constant_power_cnt = 0; + batt_curr_roof = 0; +} + +static void chrg_dev_init(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num, i; + chrg_num = chip->mmi_chrg_dev_num; + mmi_chrg_err(chip,"runing in chrg dev init!\n"); + + for (i = 0; i < chrg_num; i++) { + + switch (i) { + case PMIC_SW: + if (is_charger_exist(dev_ops[PMIC_SW].dev_name)) { + chrg_list->pmic_sw = true; + chrg_list->chrg_dev[PMIC_SW] = chip->chrg_list[PMIC_SW]; + } + break; + case CP_MASTER: + if (is_charger_exist(dev_ops[CP_MASTER].dev_name)) { + chrg_list->cp_master = true; + chrg_list->chrg_dev[CP_MASTER] = chip->chrg_list[CP_MASTER]; + } + break; + case CP_SLAVE: + if (is_charger_exist(dev_ops[CP_SLAVE].dev_name)) { + chrg_list->cp_slave = true; + chrg_list->cp_clave_later = false; + chrg_list->chrg_dev[CP_SLAVE] = chip->chrg_list[CP_SLAVE]; + } + break; + default: + mmi_chrg_err(chip,"No mmi_chrg_dev found %d !\n",i); + break; + } + } + return; +} + +static void clear_chrg_dev_error_cnt(struct mmi_charger_manager *chip, struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num, i; + struct mmi_charger_device *chrg_dev; + chrg_num = chip->mmi_chrg_dev_num; + + for (i = 0; i < chrg_num; i++) { + if (is_charger_exist(dev_ops[i].dev_name)) { + chrg_dev = chrg_list->chrg_dev[i]; + chrg_dev->charger_error.chrg_err_type = 0; + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + chrg_dev->charger_error.bus_ocp_err_cnt = 0; + } + } + return; +} + +#define PPS_INIT_VOLT_COMP 500000 +#define PPS_VOLT_COMP_DELTA 300000 +static void chrg_policy_error_recovery(struct mmi_charger_manager *chip, + struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num = 0, i = 0, chrg_error_type= 0; + struct mmi_charger_device *chrg_dev; + chrg_num = chip->mmi_chrg_dev_num; + + for (i = 0; i < chrg_num; i++) { + + switch (i) { + case PMIC_SW: + break; + case CP_MASTER: + if (is_charger_exist(dev_ops[CP_MASTER].dev_name)) { + chrg_dev = chrg_list->chrg_dev[CP_MASTER]; + chrg_error_type = chrg_dev->charger_error.chrg_err_type; + if (chrg_error_type & (1 << MMI_BUS_UCP_ALARM_BIT) || + chrg_error_type & (1 << MMI_BUS_UCP_FAULT_BIT)) { + mmi_chrg_info(chip,"CP master bus ucp error %d, cnt %d," + "pps volt comp %dmV\n", + chrg_error_type, + chrg_dev->charger_error.bus_ucp_err_cnt, + chip->pps_volt_comp); + + if (chrg_dev->charger_error.bus_ucp_err_cnt > 3) { + if (chrg_list->cp_slave) { + chrg_list->cp_slave = false; + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + mmi_chrg_sm_move_state(chip, + PM_STATE_CHRG_PUMP_ENTRY); + } else { + chip->recovery_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, + PM_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + } + } else if (chrg_dev->charger_error.bus_ucp_err_cnt > 6) { + chip->recovery_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, + PM_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ucp_err_cnt = 0; + } + + chrg_dev->charger_error.bus_ucp_err_cnt++; + chip->pps_volt_comp += PPS_VOLT_COMP_DELTA; + mmi_chrg_info(chip,"Restart charging, " + "increase pps volt comp %dmV\n", + chip->pps_volt_comp); + mmi_chrg_sm_move_state(chip, + PM_STATE_CHRG_PUMP_ENTRY); + mmi_clear_charger_error(chrg_dev); + }else if (chrg_error_type & (1 << MMI_BUS_OCP_ALARM_BIT) || + chrg_error_type & (1 << MMI_BUS_OCP_FAULT_BIT) || + chrg_error_type & (1 << MMI_CONV_OCP_FAULT_BIT)) { + mmi_chrg_info(chip,"CP master ocp error %d, cnt %d," + "pps volt comp %dmV\n", + chrg_error_type, + chrg_dev->charger_error.bus_ocp_err_cnt, + chip->pps_volt_comp); + if (chrg_dev->charger_error.bus_ocp_err_cnt > 3) { + chip->recovery_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, + PM_STATE_SW_ENTRY); + chrg_dev->charger_error.bus_ocp_err_cnt = 0; + } + chrg_dev->charger_error.bus_ocp_err_cnt++; + chip->pps_volt_comp -= PPS_VOLT_COMP_DELTA; + if (chip->pps_volt_comp < 0) + chip->pps_volt_comp = 0; + mmi_chrg_info(chip,"Restart charging, " + "decrease pps volt comp %dmV\n", + chip->pps_volt_comp); + mmi_chrg_sm_move_state(chip, + PM_STATE_CHRG_PUMP_ENTRY); + mmi_clear_charger_error(chrg_dev); + } + } + break; + case CP_SLAVE: + break; + default: + mmi_chrg_err(chip,"No mmi_chrg_dev found %d !\n",i); + break; + } + } + return; +} + +static void chrg_policy_error_clear(struct mmi_charger_manager *chip, + struct mmi_cp_policy_dev *chrg_list) +{ + int chrg_num = 0, i = 0; + struct mmi_charger_device *chrg_dev; + chrg_num = chip->mmi_chrg_dev_num; + + for (i = 0; i < chrg_num; i++) { + switch (i) { + case PMIC_SW: + break; + + case CP_MASTER: + if (is_charger_exist(dev_ops[CP_MASTER].dev_name)) { + chrg_dev = chrg_list->chrg_dev[CP_MASTER]; + mmi_clear_charger_error(chrg_dev); + } + break; + + case CP_SLAVE: + break; + + default: + mmi_chrg_err(chip,"mmi_chrg_dev not found %d !\n",i); + break; + } + } + + return; +} + +void mmi_chrg_enable_all_cp(struct mmi_charger_manager *chip, int val) +{ + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + bool enable = !!val; + + mmi_chrg_dbg(chip, PR_MOTO,"enable all cp = %d\n", enable); + if (enable) { + if (chip->cp_disable == false) + return; + + cancel_delayed_work_sync(&chip->mmi_chrg_sm_work); + mmi_chrg_sm_move_state(chip, PM_STATE_CHRG_PUMP_ENTRY); + chip->cp_disable = false; + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(0)); + + } else { + if(chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) + return; + + cancel_delayed_work_sync(&chip->mmi_chrg_sm_work); + + chip->cp_disable = true; + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + mmi_chrg_sm_move_state(chip, PM_STATE_RECOVERY_SW); + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(0)); + } +} + +#define HEARTBEAT_SHORT_DELAY_MS 1000 +#define HEARTBEAT_lOOP_WAIT_MS 2000 +#define HEARTBEAT_PPS_TUNNING_MS 100 +#define HEARTBEAT_NEXT_STATE_MS 100 +#define HEARTBEAT_CANCEL -1 +#define CC_CURR_DEBOUNCE 100000 +#define CV_TAPPER_COUNT 3 +#define CC_POWER_COUNT 3 +#define CV_DELTA_VOLT 100000 +#define THERMAL_TUNNING_CURR 100000 +#define COOLING_DELTA_POWER 100000 +#define COOLING_MAX_CNT 5 +#define PPS_SELECT_PDO_RETRY_COUNT 3 +#define DISABLE_CHRG_LIMIT -1 +#define CP_CHRG_SOC_LIMIT 90 +#define PD_CONT_PWR_CNT 5 +#define AICL_DEFALLT_THREHOLD 4500 +#define QC3P_V_STEP 20000 +#define CURR_TO_VOLT_STEP 5 +#define DEFAULT_HVDCP_POWER_MAX 15000 +void mmi_chrg_policy_clear(struct mmi_charger_manager *chip) { + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + chrg_dev_init(chip, &g_chrg_list); + clear_chrg_dev_error_cnt(chip, &g_chrg_list);; + clear_chg_manager(chip); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + sm_state = PM_STATE_DISCONNECT; + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + quit_slave_chrg_cnt = 0; + chrg_cc_power_tunning_cnt = 0; + chrg_cv_taper_tunning_cnt = 0; + chrg_cv_delta_volt = 0; + pd_constant_power_cnt = 0; + batt_curr_roof = 0; + return; +} + +int qc3p_config_aicl_threshold(struct mmi_charger_manager *chip, int threshold) +{ + int rc; + union power_supply_propval prop = {0,}; + int prev_threshold = 0; + + if(threshold < AICL_DEFALLT_THREHOLD) + return -1; + + if(!chip->batt_psy) { + chip->batt_psy = power_supply_get_by_name("qcom_battery"); + if(!chip->batt_psy) + return -ENODEV; + } + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_QC3P_AICL_THRESHOLD,&prop); + if(!rc) + prev_threshold = prop.intval; + + if(threshold != prev_threshold){ + prop.intval = threshold; + rc = power_supply_set_property(chip->batt_psy, + POWER_SUPPLY_PROP_QC3P_AICL_THRESHOLD, &prop); + } + + return 0; +} + +static void mmi_chrg_sm_work_func(struct work_struct *work) +{ + struct mmi_charger_manager *chip = container_of(work, + struct mmi_charger_manager, mmi_chrg_sm_work.work); + int i = 0, rc = 0; + int ibatt_curr = 0, vbatt_volt = 0, batt_temp = 0, vbus_pres = 0; + int batt_soc = 0; + int heartbeat_dely_ms = 0; + int cooling_curr = 0; + int cooling_volt = 0; + int pmic_sys_therm_level = 0; + bool zone_change = false; + bool ignore_hysteresis_degc = false; + struct mmi_chrg_step_info *chrg_step; + union power_supply_propval prop = {0,}; + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + int ibus_curr = 0, calculated_vbus = 0,ibus_cov_to_volt = 0, vbus_cov_to_volt = 0; + + mmi_chrg_dbg(chip, PR_MOTO, "\n\n\n"); + + mmi_chrg_dbg(chip, PR_MOTO, "schedule SM work, sm state %s \n", + pm_state_str[sm_state]); + + mmi_chrg_dbg(chip, PR_MOTO, "pmic-sw is exist: %d " + "cp-master is exist: %d " + "cp-slave is exist: %d\n", + chrg_list->pmic_sw, + chrg_list->cp_master, + chrg_list->cp_slave); + if (!chrg_list->pmic_sw) { + mmi_chrg_err(chip,"PMIC-SW isn't exist, force quite mmi chrg sm work !\n"); + return; + } + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, &prop); + if (!rc) + pmic_sys_therm_level = prop.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + vbatt_volt = prop.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + ibatt_curr = prop.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_TEMP, &prop); + if (!rc) + batt_temp = prop.intval / 10; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &prop); + if (!rc) + batt_soc = prop.intval; + + + if (ibatt_curr < 0) + ibatt_curr *= -1; + + mmi_update_all_charger_status(chip); + mmi_update_all_charger_error(chip); + + if (chip->extrn_sense) { + ibatt_curr = chrg_list->chrg_dev[CP_MASTER]->charger_data.ibatt_curr; + ibatt_curr *= 1000; + if (ibatt_curr < 0) + ibatt_curr *= -1; + vbatt_volt = chrg_list->chrg_dev[CP_MASTER]->charger_data.vbatt_volt; + vbatt_volt *= 1000; + } + + vbus_pres = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbus_pres; + if (!vbus_pres) { + for (i = 0; i < 3; i++) { + mmi_update_all_charger_status(chip); + vbus_pres = chrg_list->chrg_dev[PMIC_SW]->charger_data.vbus_pres; + mmi_chrg_info(chip, "Retry check charger status, vbus %d\n", vbus_pres); + if (vbus_pres) { + mmi_chrg_info(chip, "Get vbus present , continue to charging\n"); + break; + } + msleep(100); + } + } + + mmi_chrg_info(chip, "battery current %d\n", ibatt_curr); + mmi_chrg_info(chip, "battery voltage %d\n", vbatt_volt); + mmi_chrg_info(chip, "battery temp %d\n", batt_temp); + mmi_chrg_info(chip, "battery capacity %d\n", batt_soc); + + if (vbus_pres && + (sm_state == PM_STATE_PPS_TUNNING_CURR + || sm_state == PM_STATE_PPS_TUNNING_VOLT + || sm_state == PM_STATE_CP_CC_LOOP + || sm_state == PM_STATE_CP_CV_LOOP)) { + mmi_dump_charger_error(chip, chrg_list->chrg_dev[CP_MASTER]); + chrg_policy_error_recovery(chip, chrg_list); + } + + if (sm_state == PM_STATE_CP_CV_LOOP) + ignore_hysteresis_degc = true; + zone_change = mmi_find_temp_zone(chip, batt_temp, ignore_hysteresis_degc); + chrg_step = &chip->chrg_step; + + if (chip->pres_temp_zone == ZONE_COLD + || chip->pres_temp_zone == ZONE_HOT + || !chrg_list->chrg_dev[PMIC_SW]->charger_enabled + || vbatt_volt > chip->batt_ovp_lmt) { + + mmi_chrg_info(chip, "Force stop charging, " + "pres_temp_zone %d, " + "pmic charger enabled %d, " + "vbatt_volt %dmv, " + "batt ovp limit %dmv\n", + chip->pres_temp_zone, + chrg_list->chrg_dev[PMIC_SW]->charger_enabled, + vbatt_volt, chip->batt_ovp_lmt); + mmi_chrg_sm_move_state(chip, PM_STATE_STOP_CHARGE); + } + + if (!vbus_pres) { + mmi_chrg_sm_move_state(chip, PM_STATE_DISCONNECT); + } else if (sm_state == PM_STATE_ENTRY + || sm_state == PM_STATE_STOP_CHARGE) { + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + } else if (sm_state == PM_STATE_DISCONNECT) { + mmi_get_input_current_settled(chrg_list->chrg_dev[PMIC_SW], + &chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + //one issue found , plug out charger and sm_stat stay at PM_STATE_DISCONNECT,but disabled by cancel_delay_work sync + //so charger_limited stay true and not limt pmic current to 500mA. + //if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + // chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + //} + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } else if (zone_change && + chip->pres_temp_zone != ZONE_COLD && + chip->pres_temp_zone != ZONE_HOT) { + + if (batt_temp >= chrg_step->temp_c) { + mmi_chrg_info(chip, "battery temp %d, temp thre %d " + "Enter into COOLING LOOP !\n", + batt_temp, chrg_step->temp_c); + chip->batt_therm_cooling = true; + chip->pd_batt_therm_volt = chip->pd_request_volt_prev; + chip->pd_batt_therm_curr = chip->pd_request_curr_prev; + } else if (!chip->batt_therm_cooling && + !ignore_hysteresis_degc) { + mmi_chrg_info(chip, "battery temp %d, temp thre %d " + "Restart select chrg step and temp zone !\n", + batt_temp, chrg_step->temp_c); + mmi_find_chrg_step(chip, chip->pres_temp_zone, vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } + } + + mmi_chrg_dbg(chip, PR_MOTO, "temp zone %d, is_changed %d, " + "chrg_step %d, " + "step cc curr %d, step cv volt %d, " + "step cv tapper curr %d\n", + chip->pres_temp_zone, zone_change, + chrg_step->pres_chrg_step, + chrg_step->chrg_step_cc_curr, + chrg_step->chrg_step_cv_volt, + chrg_step->chrg_step_cv_tapper_curr); + switch (sm_state) { + case PM_STATE_DISCONNECT: + mmi_chrg_info(chip,"vbus disconnect !, jump to PM_STATE_DISCONNECT," + "recovery PMIC-SW limitation, and close CP charg\n"); + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + heartbeat_dely_ms = HEARTBEAT_CANCEL; + break; + case PM_STATE_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chip->pd_pps_support + && chrg_list->cp_master + && vbatt_volt > chip->pl_chrg_vbatt_min + && chrg_step->pres_chrg_step != chip->chrg_step_nums - 1 + && chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min + && batt_soc < CP_CHRG_SOC_LIMIT) { + + mmi_chrg_dbg(chip, PR_MOTO, "Enter into CHRG PUMP, " + "vbatt %d uV, " + "pps support %d, " + "chrg step %d, " + "chrg step cc curr %d uA, " + "CP master charging curr min %d uA\n", + vbatt_volt, + chip->pd_pps_support, + chrg_step->pres_chrg_step, + chrg_step->chrg_step_cc_curr, + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min); + mmi_chrg_sm_move_state(chip, PM_STATE_CHRG_PUMP_ENTRY); + + } else { + mmi_chrg_dbg(chip, PR_MOTO, "Enter into PMIC switch charging, " + "the reason is : vbatt %d uV, " + "pl chrg vbatt min %d uV, " + "pps support %d, " + "chrg step %d\n", + vbatt_volt, chip->pl_chrg_vbatt_min, + chip->pd_pps_support, + chrg_step->pres_chrg_step); + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + + } + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt= 0; + chip->pd_sys_therm_volt= chip->pd_request_volt_prev; + chip->pd_sys_therm_curr = chip->pd_request_curr_prev; + chip->pd_batt_therm_volt = chip->pd_request_volt_prev; + chip->pd_batt_therm_curr = chip->pd_request_curr_prev; + chip->sys_therm_cooling = false; + chip->sys_therm_force_pmic_chrg = false; + chip->recovery_pmic_chrg = false; + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_SW_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip, "Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + if (!chip->dont_rerun_aicl) { + mmi_chrg_info(chip, "Do an rerun usb AICL for PMIC-SW\n"); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], false); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], true); + } + mmi_chrg_info(chip, "Check all effective pdo info again\n"); +#if 0 + usbpd_get_pdo_info(chip->pd_handle, chip->mmi_pdo_info,PD_MAX_PDO_NUM); + mmi_chrg_info(chip, "Select FIXED pdo for switch charging !\n"); + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + mmi_chrg_info(chip,"find pdo %d, max volt %d, max curr %d\n", + chip->mmi_pdo_info[i].type, + chip->mmi_pdo_info[i].uv_max, + chip->mmi_pdo_info[i].ua); + if (chip->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_FIXED + && chip->mmi_pdo_info[i].uv_max >= SWITCH_CHARGER_PPS_VOLT + && chip->mmi_pdo_info[i].ua >= TYPEC_HIGH_CURRENT_UA) { + mmi_chrg_dbg(chip, PR_MOTO, "select 5V/3A pps, pdo %d\n", i); + chip->mmi_pd_pdo_idx = + chip->mmi_pdo_info[i].pdo_pos; + break; + } + } +#endif + chip->pd_request_volt = chip->switch_charger_pps_volt; + chip->pd_request_curr = TYPEC_HIGH_CURRENT_UA; + mmi_chrg_info(chip,"Select pdo %d, pd request curr %d, volt %d\n", + chip->mmi_pd_pdo_idx, + chip->pd_request_curr, + chip->pd_request_volt); + + mmi_chrg_sm_move_state(chip, PM_STATE_SW_LOOP); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_SW_LOOP: + if (chip->pd_pps_support + && chip->cp_disable == false + && vbatt_volt > chip->pl_chrg_vbatt_min + && chrg_step->pres_chrg_step != chip->chrg_step_nums - 1 + && chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min + && !chip->recovery_pmic_chrg + && !chip->sys_therm_force_pmic_chrg + && batt_soc < CP_CHRG_SOC_LIMIT) { + mmi_chrg_info(chip, "Enter CP, the reason is : " + "pd pps support %d, " + "vbatt %duV, chrg step %d\n", + chip->pd_pps_support, + vbatt_volt, chrg_step->pres_chrg_step); + mmi_chrg_sm_move_state(chip, PM_STATE_CHRG_PUMP_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + mmi_chrg_dbg(chip, PR_MOTO, "Continue to SW charging, " + "vbatt %d uV, ibatt %d uA\n", + vbatt_volt, ibatt_curr); + + if(chip->hvdcp_power_max > DEFAULT_HVDCP_POWER_MAX) + qc3p_config_aicl_threshold(chip,AICL_DEFALLT_THREHOLD); + + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + } + break; + case PM_STATE_CHRG_PUMP_ENTRY: + mmi_chrg_info(chip,"CP master exist %d, CP slave exist %d !\n", + chrg_list->cp_master, + chrg_list->cp_slave); + if (chrg_list->cp_slave) { + mmi_chrg_info(chip,"CP slave is exist !\n"); + mmi_chrg_info(chip,"chrg step cc curr %d uA, " + "CP slave charging curr min %d uA\n", + chrg_step->chrg_step_cc_curr, + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min); + if (chrg_step->chrg_step_cc_curr >= + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min) { + mmi_chrg_sm_move_state(chip, + PM_STATE_DULE_CP_ENTRY); + } else + mmi_chrg_sm_move_state(chip, + PM_STATE_SINGLE_CP_ENTRY); + } else { + mmi_chrg_info(chip,"CP slave isn't exist !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_SINGLE_CP_ENTRY); + } + + mmi_chrg_info(chip,"Set PMIC SW FCC limits!\n"); + if (!chrg_list->chrg_dev[PMIC_SW]->charger_limited + && chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited > 0) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited); + mmi_chrg_info(chip,"Set PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->charging_curr_limited); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = true; + } + + mmi_chrg_info(chip, "Check all effective pdo info again\n"); +#if 1 + calculate_qc3p_vc_based_power_type(chip); +#else + usbpd_get_pdo_info(chip->pd_handle, chip->mmi_pdo_info,PD_MAX_PDO_NUM); + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + if ((chip->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_AUGMENTED) + && chip->mmi_pdo_info[i].uv_max >= PUMP_CHARGER_PPS_MIN_VOLT + && chip->mmi_pdo_info[i].ua >= chip->typec_middle_current) { + chip->mmi_pd_pdo_idx = chip->mmi_pdo_info[i].pdo_pos; + mmi_chrg_info(chip, + "Pd charger support pps, pdo %d, " + "volt %d, curr %d \n", + chip->mmi_pd_pdo_idx, + chip->mmi_pdo_info[i].uv_max, + chip->mmi_pdo_info[i].ua); + chip->pd_pps_support = true; + + if (chip->mmi_pdo_info[i].uv_max < + chip->pd_volt_max) { + chip->pd_volt_max = + chip->mmi_pdo_info[i].uv_max; + } + + if (chip->mmi_pdo_info[i].ua < + chip->pd_curr_max) { + chip->pd_curr_max = + chip->mmi_pdo_info[i].ua; + } + + break; + } + } +#endif + /*Initial setup pps request power by the battery voltage*/ + chip->pd_request_volt = (2 * vbatt_volt) % 20000; + chip->pd_request_volt = 2 * vbatt_volt - chip->pd_request_volt + + chip->pps_volt_comp; + chip->pd_request_curr = + min(chip->pd_curr_max, chip->typec_middle_current); + mmi_chrg_info(chip,"pps init , volt %dmV, curr %dmA, volt comp %dmv\n", + chip->pd_request_volt, chip->pd_request_curr, chip->pps_volt_comp); + chrg_policy_error_clear(chip, chrg_list); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_SINGLE_CP_ENTRY: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_chrg_info(chip,"Disable Slave Charger Pump !\n"); + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Enable Master Charger Pump !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_PPS_TUNNING_CURR); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_STATE_DULE_CP_ENTRY: + if (chrg_list->cp_master) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Enable Master Charger Pump !\n"); + } + + if (chrg_list->cp_slave) { + chrg_list->cp_clave_later = true; + mmi_chrg_info(chip,"For ibus UCP, " + "delay start Slave Charger Pump in TUNNING_VOLT stage !\n"); + } + + if (chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_sm_move_state(chip, PM_STATE_PPS_TUNNING_CURR); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_STATE_PPS_TUNNING_CURR: + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + if (chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into " + "SW directly\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + }else if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + if (chip->pd_request_curr - chip->pps_curr_steps + > chip->typec_middle_current) { + chip->pd_request_curr -= chip->pps_curr_steps; + } + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_chrg_info(chip,"During the curr going up process, " + "the chrg step was changed," + "stop increase pps curr and Enter into " + "CC stage as soon!\n"); + } else if (chip->pps_result < 0) { + if (mmi_get_pps_result_history(chip) != NO_ERROR) { + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_chrg_info(chip,"Too many pdo request failed," + " Enter into CC stage directly!\n"); + mmi_clear_pps_result_history(chip); + } + chip->pd_request_curr = chip->pd_request_curr_prev; + goto schedule; + } else if (chip->pd_request_curr + chip->pps_curr_steps + <= chip->pd_curr_max + && vbatt_volt < chrg_step->chrg_step_cv_volt + && ibatt_curr < chrg_step->chrg_step_cc_curr + /*&& ibus_curr < chip->pd_curr_max*/) { + chip->pd_request_curr += chip->pps_curr_steps; + mmi_chrg_dbg(chip, PR_MOTO, "Increase pps curr %d\n", + chip->pd_request_curr); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + } else { + mmi_chrg_info(chip,"Enter into tunning pps volt\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_PPS_TUNNING_VOLT); + } + break; + case PM_STATE_PPS_TUNNING_VOLT: + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + if (chrg_list->cp_slave + && chrg_list->cp_clave_later + && !chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], true); + mmi_chrg_info(chip,"Enable Slave Charger Pump !\n"); + } + + if (chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_info(chip,"CP MASTER was disabled, " + "Enter into SW directly\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + } else if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + chip->pd_request_volt -= chip->pps_volt_steps; + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_chrg_info(chip,"Duing the volt going up process, " + "the chrg step was changed," + "stop increase pps volt and" + " Enter into CC stage as soon!\n"); + } else if (chip->pps_result < 0) { + if (mmi_get_pps_result_history(chip) != NO_ERROR) { + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + mmi_clear_pps_result_history(chip); + mmi_chrg_info(chip,"Too many pdo request failed," + " Enter into CC stage directly!\n"); + } + chip->pd_request_volt = chip->pd_request_volt_prev; + goto schedule; + } else if (chip->pd_request_volt + chip->pps_volt_steps + <= chip->pd_volt_max + && vbatt_volt < chrg_step->chrg_step_cv_volt + && ibatt_curr < ((chrg_step->pres_chrg_step == STEP_FIRST) ? + chrg_step->chrg_step_cc_curr + chip->step_first_curr_comp: + chrg_step->chrg_step_cc_curr)) { + chip->pd_request_volt += chip->pps_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, "Increase pps volt %d\n", + chip->pd_request_volt); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + } else { + mmi_chrg_info(chip,"Enter into CC loop stage !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_CC_LOOP); + } + + if (ibatt_curr > batt_curr_roof) { + batt_curr_roof = ibatt_curr; + } + if (ibatt_curr < batt_curr_roof) + pd_constant_power_cnt++; + else + pd_constant_power_cnt = 0; + + if (pd_constant_power_cnt > PD_CONT_PWR_CNT) { + mmi_chrg_info(chip,"PD adapter was ready in constant power state, " + "Enter into CC loop stage !\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_CC_LOOP); + } + mmi_chrg_info(chip, "pd_constant_power_cnt %d, " + "batt_curr_roof %d\n", + pd_constant_power_cnt, + batt_curr_roof); + break; + case PM_STATE_CP_CC_LOOP: + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CC loop, chrg_step %d, " + "vbatt %dmV, ibatt %dmA, " + "CC target curr %dmA, " + "next CV target volt %dmV\n ", + chrg_step->pres_chrg_step, + vbatt_volt, ibatt_curr, + chrg_step->chrg_step_cc_curr, + chrg_step->chrg_step_cv_volt); + if (chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into SW directly\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + goto schedule; + } + + if (chip->pps_result < 0) { + mmi_chrg_err(chip, "Last select pdo failed\n"); + chip->pps_result = mmi_get_pps_result_history(chip); + switch (chip->pps_result) { + case BLANCE_POWER: + chip->pd_pps_balance = true; + mmi_chrg_err(chip, "Enable pps power balance\n"); + mmi_clear_pps_result_history(chip); + break; + case RESET_POWER: + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + mmi_chrg_err(chip, "Hard reset charger policy to recovery power," + "Since too many pdo failed\n"); + mmi_clear_pps_result_history(chip); + break; + default: + break; + } + + chip->pd_request_volt = chip->pd_request_volt_prev; + if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->pd_request_volt < chip->pd_volt_max) + chrg_cc_power_tunning_cnt = + CC_POWER_COUNT; + goto schedule; + } + + if (!chip->sys_therm_cooling + && !chip->batt_therm_cooling + && pd_constant_power_cnt <= PD_CONT_PWR_CNT) { + if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->pd_request_volt < chip->pd_volt_max + && chrg_cc_power_tunning_cnt >= + CC_POWER_COUNT) { + if (chip->pd_pps_balance + && (chip->pd_request_curr - chip->pps_curr_steps) + > chip->typec_middle_current) { + chip->pd_request_curr -= + chip->pps_curr_steps; + chip->pd_request_volt += + mmi_calculate_delta_volt(chip->pd_request_volt_prev, + chip->pd_request_curr_prev, + chip->pps_curr_steps); + mmi_chrg_dbg(chip, PR_MOTO, + "Request curr decreass %dmA, " + "Request volt increase %dmV \n", + chip->pd_request_curr, + chip->pd_request_volt); + } else { + if (chip->pd_request_curr + chip->pps_curr_steps + < chip->pd_curr_max + /*&& ibus_curr < chip->pd_curr_max*/) { + chip->pd_request_curr += + chip->pps_curr_steps; + + mmi_chrg_dbg(chip, PR_MOTO, + "Request curr decreass %dmA\n ", + chip->pd_request_curr); + } else if (chip->pd_request_volt + chip->pps_volt_steps + < chip->pd_volt_max) { + chip->pd_request_volt += + chip->pps_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "Request volt decreass %duV\n ", + chip->pd_request_volt); + } + } + chrg_cc_power_tunning_cnt = 0; + mmi_chrg_dbg(chip, PR_MOTO, + "For keeping CC chrg POWER, " + "Have to adjust pd input volt or curr" + "Request volt %dmV, " + "Request curr %dmA\n", + chip->pd_request_volt, + chip->pd_request_curr); + }else if (ibatt_curr < chrg_step->chrg_step_cc_curr + && chip->pd_request_volt < chip->pd_volt_max) { + chrg_cc_power_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CC tunning cnt %d\n", + chrg_cc_power_tunning_cnt); + } else if (ibatt_curr > chrg_step->chrg_step_cc_curr + + CC_CURR_DEBOUNCE) { + chip->pd_request_volt -= chip->pps_volt_steps; + mmi_chrg_dbg(chip, PR_MOTO, + "In the CC step , the ibatt is greater than CC curr, " + "Request volt decreass %duV to remain CC step\n ", + chip->pd_request_volt); + } else + chrg_cc_power_tunning_cnt = 0; + + if(ibatt_curr < chrg_step->chrg_step_cc_curr) + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + } + + if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + if (chrg_cv_taper_tunning_cnt > + CV_TAPPER_COUNT) { + mmi_chrg_sm_move_state(chip, PM_STATE_CP_CV_LOOP); + chrg_cv_taper_tunning_cnt = 0; + chrg_cv_delta_volt = CV_DELTA_VOLT; + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV taper cnt %d, " + "chrg step cv volt %dmV, " + "vbatt %dmV\n", + chrg_cv_taper_tunning_cnt, + chrg_step->chrg_step_cv_volt, + vbatt_volt); + } + } else + chrg_cv_taper_tunning_cnt = 0; + + break; + case PM_STATE_CP_CV_LOOP: + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV loop, chrg_step %d, " + "vbatt %dmV, ibatt %dmA, " + "CV target volt %dmV, " + "CV taper curr %dmA\n ", + chrg_step->pres_chrg_step, + vbatt_volt, ibatt_curr, + chrg_step->chrg_step_cv_volt, + chrg_step->chrg_step_cv_tapper_curr); + if (chrg_list->cp_master + && !chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_chrg_info(chip,"CP MASTER was disabled, Enter into SW directly\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + goto schedule; + } + + if (chip->pps_result < 0) { + mmi_chrg_err(chip, "Last select pdo failed\n"); + chip->pps_result = mmi_get_pps_result_history(chip); + switch (chip->pps_result) { + case BLANCE_POWER: + chip->pd_request_curr -= + chip->pps_curr_steps; + + mmi_chrg_err(chip, "Reduce pps curr,for pps power balance\n"); + mmi_clear_pps_result_history(chip); + break; + case RESET_POWER: + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + mmi_chrg_err(chip, "Hard reset charger policy to recovery power," + "Since too many pdo failed\n"); + mmi_clear_pps_result_history(chip); + break; + default: + break; + } + chip->pd_request_volt = chip->pd_request_volt_prev; + goto schedule; + } + + if (vbatt_volt >= chrg_step->chrg_step_cv_volt + && ((!chrg_step->last_step && + ibatt_curr < chrg_step->chrg_step_cv_tapper_curr) + || ibatt_curr < chrg_list->chrg_dev[CP_MASTER]->charging_curr_min)) { + if (chrg_cv_taper_tunning_cnt >= CV_TAPPER_COUNT) { + if (ibatt_curr < + chrg_list->chrg_dev[CP_MASTER]->charging_curr_min) { + mmi_chrg_info(chip, "Ready quite CP chrg stage, " + "and Enter into PMIC switch chrg stage, " + "chrg step %d, ibatt %dmA\n", + chrg_step->pres_chrg_step, ibatt_curr); + mmi_find_chrg_step(chip, + chip->pres_temp_zone, vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_QUIT); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + if (mmi_find_chrg_step(chip, + chip->pres_temp_zone, vbatt_volt)) { + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + mmi_chrg_info(chip,"Jump to next chrg step\n"); + mmi_chrg_sm_move_state(chip, + PM_STATE_CP_CC_LOOP); + } else { + mmi_chrg_info(chip,"Can't find next chrg step\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_CP_QUIT); + } + } + chrg_cv_taper_tunning_cnt = 0; + } else { + chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, "chrg cv taper cnt ++, %d\n", + chrg_cv_taper_tunning_cnt); + } + + }else if (!chip->sys_therm_cooling + && !chip->batt_therm_cooling) { + + chrg_cv_taper_tunning_cnt = 0; + if (vbatt_volt > chrg_step->chrg_step_cv_volt + 10000 + chip->afvc_volt_comp) { + if (chrg_cv_delta_volt > 20000) + chip->pd_request_volt -= chrg_cv_delta_volt; + else + chip->pd_request_volt -= 20000; + mmi_chrg_info(chip, + "For keeping CV stage, decrease volt %dmV, " + "cv delta volt %dmV\n", + chip->pd_request_volt, chrg_cv_delta_volt); + } else if (vbatt_volt < chrg_step->chrg_step_cv_volt - 10000) { + chrg_cv_delta_volt -= 20000; + chip->pd_request_volt += 20000; + mmi_chrg_info(chip, + "For keeping CV stage, increase volt %dmV, " + "cv delta volt %dmV\n", + chip->pd_request_volt, chrg_cv_delta_volt); + } else { + mmi_chrg_dbg(chip, PR_MOTO, "CV loop work well, " + "keep pd power, volt %dmV, curr %dmA\n", + chip->pd_request_volt, chip->pd_request_curr); + } + + }else { + /*In this case, sys_therm_cooling or batt_therm_cooling is ture*/ + + if (vbatt_volt > chrg_step->chrg_step_cv_volt + 10000 + chip->afvc_volt_comp) { + if (chrg_cv_delta_volt > 20000) + chip->pd_request_volt -= chrg_cv_delta_volt; + else + chip->pd_request_volt -= 20000; + mmi_chrg_info(chip, + "For keeping CV stage, decrease volt %dmV, " + "cv delta volt %dmV\n", + chip->pd_request_volt, chrg_cv_delta_volt); + } else { + mmi_chrg_dbg(chip, PR_MOTO, "CV loop work well, " + "keep pd power, volt %dmV, curr %dmA\n", + chip->pd_request_volt, chip->pd_request_curr); + } + } + + if (chrg_list->cp_slave) { + if (quit_slave_chrg_cnt > 3 + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + mmi_chrg_info(chip,"Quit slave chrg, the reason is :ibatt %duA, " + "CP slave charging curr min %d uA\n", + ibatt_curr, + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], true); + mmi_chrg_info(chip,"Restart CP master again\n"); + } + + if (ibatt_curr < + chrg_list->chrg_dev[CP_SLAVE]->charging_curr_min) + quit_slave_chrg_cnt++; + else + quit_slave_chrg_cnt = 0; + } + break; + case PM_STATE_CP_QUIT: + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + mmi_chrg_sm_move_state(chip, PM_STATE_RECOVERY_SW); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_RECOVERY_SW: + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + + if (!chip->dont_rerun_aicl) { + mmi_chrg_info(chip,"Do an rerun usb AICL for PMIC-SW\n"); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], false); + msleep(100); + mmi_enable_charging(chrg_list->chrg_dev[PMIC_SW], true); + } + chip->recovery_pmic_chrg = true; + chrg_cv_taper_tunning_cnt = 0; + } + + chip->pd_request_curr = TYPEC_HIGH_CURRENT_UA; + mmi_chrg_info(chip,"ibatt : %dmA, step cc curr : %dmA\n", + ibatt_curr, chrg_step->chrg_step_cc_curr); + if (ibatt_curr > chrg_step->chrg_step_cc_curr) { + chip->pd_request_volt -= CV_DELTA_VOLT; + + mmi_chrg_dbg(chip, PR_MOTO, "Reduce pps volt %dmV, curr %dmA\n ", + chip->pd_request_volt, chip->pd_request_curr); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + chrg_cv_taper_tunning_cnt = 0; + } else { + chrg_cv_taper_tunning_cnt++; + + } + + if (chrg_cv_taper_tunning_cnt > CV_TAPPER_COUNT){ + mmi_chrg_sm_move_state(chip, PM_STATE_SW_LOOP); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + break; + case PM_STATE_STOP_CHARGE: + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + if (chrg_list->cp_slave + && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_SLAVE], false); + } + + if (chrg_list->cp_master + && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) { + mmi_enable_charging(chrg_list->chrg_dev[CP_MASTER], false); + } + + if (chrg_list->chrg_dev[PMIC_SW]->charger_limited) { + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_chrg_info(chip,"Recovery PMIC-SW ichg lmt ,%d uA\n", + chrg_list->chrg_dev[PMIC_SW]->input_curr_setted); + mmi_set_charing_current(chrg_list->chrg_dev[PMIC_SW], + DISABLE_CHRG_LIMIT); + chrg_list->chrg_dev[PMIC_SW]->charger_limited = false; + } + + if (chip->pres_temp_zone != ZONE_COLD + && chip->pres_temp_zone != ZONE_HOT + && chrg_list->chrg_dev[PMIC_SW]->charger_enabled + && chrg_step->chrg_step_cc_curr > 0) { + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + chip->pd_request_volt = chip->switch_charger_pps_volt; + chip->pd_request_curr = TYPEC_HIGH_CURRENT_UA; + break; + case PM_STATE_COOLING_LOOP: + mmi_chrg_info(chip,"In cooling loop, batt temp %d, cmp temp %d\n", + batt_temp, chrg_step->temp_c); + + if (batt_temp > chrg_step->temp_c + COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, Cooling loop failed, " + "force enter PM_STATE_ENTRY " + "restart this charger process !\n", + batt_temp); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + + } else if (batt_temp > chrg_step->temp_c) { + cooling_curr = + min(chip->pd_curr_max, chip->typec_middle_current); + if (chip->pd_request_curr > cooling_curr) { + chip->pd_request_curr -= COOLING_DELTA_POWER; + } else { + mmi_chrg_info(chip, "pd request curr %dmA, " + "battery temp %d, " + "cooling failed, Restart PM_STATE_ENTRY !\n", + chip->pd_request_curr, batt_temp); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + } + + } else if (batt_temp < chrg_step->temp_c - COOLING_DELTA_POWER) { + + mmi_chrg_info(chip,"Batt temp %d, successful exit from COOLing loop, " + "and Enter int TUNNING_CURR again !\n", + batt_temp); + mmi_chrg_sm_move_state(chip, + PM_STATE_PPS_TUNNING_CURR); + } + + if (vbatt_volt > chrg_step->chrg_step_cv_volt) { + if (chrg_cv_taper_tunning_cnt > + CV_TAPPER_COUNT) { + + mmi_chrg_info(chip, "vbatt_volt %dmV, " + "battery temp %d, " + "chrg step cv volt %dmV" + "cooling failed, Restart PM_STATE_ENTRY !\n", + vbatt_volt, batt_temp, chrg_step->chrg_step_cv_volt); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + chrg_cv_taper_tunning_cnt = 0; + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + chrg_cv_taper_tunning_cnt++; + mmi_chrg_dbg(chip, PR_MOTO, + "Chrg CV taper cnt %d, " + "chrg step cv volt %dmV, " + "vbatt %dmV\n", + chrg_cv_taper_tunning_cnt, + chrg_step->chrg_step_cv_volt, + vbatt_volt); + } + } else + chrg_cv_taper_tunning_cnt = 0; + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + break; + case PM_STATE_POWER_LIMIT_LOOP: + mmi_chrg_info(chip, "ibus_curr over, enter limitation status:%s\n",pm_state_str[sm_state]); + get_caculated_real_ibus_vbus(chip,&ibus_curr,&calculated_vbus); + + if( ibus_curr <= (chip->pd_curr_max ) && calculated_vbus < (chip->pd_volt_max ) ) { + mmi_chrg_info(chip, "ibus_curr and vbus back to normal, resume prev status:%s\n",pm_state_str[prev_sm_state]); + mmi_chrg_sm_move_state(chip, prev_sm_state); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + goto schedule; + } + + ibus_cov_to_volt = (((ibus_curr - chip->pd_curr_max + chip->pps_curr_steps)/100000) * CURR_TO_VOLT_STEP); + if(ibus_cov_to_volt <0) + ibus_cov_to_volt = 0; + vbus_cov_to_volt= (((calculated_vbus -chip->pd_volt_max + chip->pps_volt_steps*2)/QC3P_V_STEP) ); + if(vbus_cov_to_volt <0) + vbus_cov_to_volt = 0; + + mmi_chrg_info(chip, "ibus_curr abnormal, ibus decrease:%d,vbus decrease:%d\n", ibus_cov_to_volt, vbus_cov_to_volt ); + + chip->pd_request_volt -= max(ibus_cov_to_volt, vbus_cov_to_volt ) * chip->pps_volt_steps; + + mmi_chrg_info(chip, "ibus_curr abnormal, decrease pps volt:%d\n", chip->pd_request_volt ); + + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + break; + default: + break; + } + +schedule: + + chip->pd_target_volt = min(chip->pd_request_volt, chip->pd_volt_max); + chip->pd_target_curr = min(chip->pd_request_curr, chip->pd_curr_max); + + if (chip->system_thermal_level == 0) { + chip->sys_therm_cooling= false; + chip->sys_therm_force_pmic_chrg = false; + } else if ((chip->system_thermal_level == chip->thermal_levels - 1) + && !chip->sys_therm_force_pmic_chrg) { + chip->sys_therm_cooling = true; + chip->sys_therm_force_pmic_chrg = true; + mmi_chrg_sm_move_state(chip, PM_STATE_SW_ENTRY); + mmi_chrg_info(chip, "Thermal is the highest, level %d, " + "Force enter into single pmic charging !\n", + chip->system_thermal_level); + + } else if (chip->system_thermal_level > 0 && + (sm_state == PM_STATE_CP_CC_LOOP || + sm_state == PM_STATE_CP_CV_LOOP || +// sm_state == PM_STATE_PPS_TUNNING_CURR || +// sm_state == PM_STATE_PPS_TUNNING_VOLT || + sm_state == PM_STATE_POWER_LIMIT_LOOP )) { + + mmi_chrg_dbg(chip, PR_MOTO, "Thermal level is %d, thermal target curr:%d\n", + chip->system_thermal_level,chip->thermal_mitigation[chip->system_thermal_level]); + if (!chip->sys_therm_cooling) { + chip->sys_therm_cooling = true; + chip->pd_sys_therm_volt = chip->pd_request_volt_prev; + chip->pd_sys_therm_curr = chip->pd_request_curr_prev; + } + + if (ibatt_curr > + chip->thermal_mitigation[chip->system_thermal_level] + + CC_CURR_DEBOUNCE) { + if (chip->pd_sys_therm_curr - THERMAL_TUNNING_CURR >= + chip->typec_middle_current) { + chip->pd_sys_therm_curr -= THERMAL_TUNNING_CURR; + mmi_chrg_dbg(chip, PR_MOTO, "For thermal, decrease pps curr %d\n", + chip->pd_sys_therm_curr); + + } else { + mmi_chrg_dbg(chip, PR_MOTO, + "pd_sys_therm_curr %dmA was less than %dmA, " + "Give up thermal mitigation!", + chip->pd_sys_therm_curr - THERMAL_TUNNING_CURR, + chip->typec_middle_current); + } + } else if (ibatt_curr < + chip->thermal_mitigation[chip->system_thermal_level] + - CC_CURR_DEBOUNCE) { + if (chip->pd_sys_therm_curr + THERMAL_TUNNING_CURR <= + chip->pd_curr_max) { + chip->pd_sys_therm_curr += THERMAL_TUNNING_CURR; + mmi_chrg_dbg(chip, PR_MOTO, "For thermal, increase pps curr %d\n", + chip->pd_sys_therm_curr); + } + } + + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + } else if (chip->system_thermal_level > 0 && + chip->system_thermal_level != chip->thermal_levels - 1 && + sm_state == PM_STATE_SW_LOOP && + chip->sys_therm_force_pmic_chrg) { + mmi_chrg_dbg(chip, PR_MOTO, "Try to recovery charger pump!\n"); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } + + if (chip->sys_therm_cooling) { + chip->pd_target_volt = min(chip->pd_target_volt, chip->pd_sys_therm_volt); + chip->pd_target_curr = min(chip->pd_target_curr, chip->pd_sys_therm_curr); + } + + if (chip->batt_therm_cooling && !chip->sys_therm_force_pmic_chrg) { + if (batt_temp > chrg_step->temp_c + COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, Cooling loop failed, " + "force enter PM_STATE_ENTRY " + "restart this charger process !\n", + batt_temp); + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt= 0; + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + + } else if (batt_temp > chrg_step->temp_c) { + cooling_curr = + min(chip->pd_curr_max, chip->typec_middle_current); + cooling_volt = (2 * vbatt_volt) % 20000; + cooling_volt = 2 * vbatt_volt - cooling_volt + + chip->pps_volt_comp; + if (ibatt_curr > TYPEC_HIGH_CURRENT_UA + && chip->pd_batt_therm_curr > cooling_curr) { + + if (chip->pd_batt_therm_curr - COOLING_DELTA_POWER >= + chip->typec_middle_current) + chip->pd_batt_therm_curr -= COOLING_DELTA_POWER; + mmi_chrg_info(chip, "Do chrg power cooling" + "pd_batt_therm_curr %dmA, " + "battery temp %d\n", + chip->pd_batt_therm_curr, batt_temp); + } else if (ibatt_curr > TYPEC_HIGH_CURRENT_UA + && chip->pd_batt_therm_volt > cooling_volt) { + chip->pd_batt_therm_volt -= COOLING_DELTA_POWER; + mmi_chrg_info(chip, "Do chrg power cooling" + "pd request volt %dmA, " + "battery temp %d\n", + chip->pd_batt_therm_volt, batt_temp); + + } else { + if (chip->batt_therm_cooling_cnt > COOLING_MAX_CNT) { + + mmi_chrg_info(chip, "Do chrg power cooling failed" + "pd_batt_therm_currr %dmA, " + "pd_batt_therm_volt %dmV" + "battery temp %d, " + "Restart PM_STATE_ENTRY !\n", + chip->pd_batt_therm_curr, + chip->pd_batt_therm_volt, + batt_temp); + mmi_find_chrg_step(chip, chip->pres_temp_zone, + vbatt_volt); + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + mmi_chrg_sm_move_state(chip, + PM_STATE_ENTRY); + + } else { + mmi_chrg_info(chip, "It's already the lowest cooling chrg power" + "waiting for a while, cooling cnt %d, " + "battery temp %d\n", + batt_temp, chip->batt_therm_cooling); + chip->batt_therm_cooling_cnt++; + } + } + + } else if (batt_temp < chrg_step->temp_c - COOLING_HYSTERISIS_DEGC) { + + mmi_chrg_info(chip,"Batt temp %d, " + "Exit successfully from COOLing loop!\n", + batt_temp); + + if (sm_state == PM_STATE_CP_CC_LOOP) { + mmi_chrg_info(chip,"Jump into CURR tunning" + "for chrg power poerformance!\n"); + mmi_chrg_sm_move_state(chip, + PM_STATE_PPS_TUNNING_CURR); + } + chip->batt_therm_cooling = false; + chip->batt_therm_cooling_cnt = 0; + } + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + } + + if (chip->batt_therm_cooling) { + chip->pd_target_volt = min(chip->pd_target_volt, chip->pd_batt_therm_volt); + chip->pd_target_curr = min(chip->pd_target_curr, chip->pd_batt_therm_curr); + } + + mmi_chrg_dbg(chip, PR_MOTO, "chrg sm work,%s, " + "battery soc %d, " + "battery temp %d, " + "battery current %d, " + "battery voltage %d, " + "sys therm level %d, " + "pmic therm level %d, " + "sys therm cooling %d, " + "batt therm cooling %d, " + "sys therm force pmic chrg %d, " + "recovery pmic chrg %d\n", + pm_state_str[sm_state], + batt_soc, batt_temp, + ibatt_curr, vbatt_volt, + chip->system_thermal_level, + pmic_sys_therm_level, + chip->sys_therm_cooling, + chip->batt_therm_cooling, + chip->sys_therm_force_pmic_chrg, + chip->recovery_pmic_chrg); + + mmi_chrg_dbg(chip, PR_MOTO, "pd request volt %dmV, " + "pd request curr %dmA, " + "pd target volt %dmV, " + "pd target curr %dmA, " + "sys therm volt %dmV, " + "sys therm curr %dmA, " + "batt therm volt %dmV, " + "batt therm curr %dmA\n", + chip->pd_request_volt, + chip->pd_request_curr, + chip->pd_target_volt, + chip->pd_target_curr, + chip->pd_sys_therm_volt, + chip->pd_sys_therm_curr, + chip->pd_batt_therm_volt, + chip->pd_batt_therm_curr); + + if (chip->pd_target_volt < chip->switch_charger_pps_volt + || chip->pd_target_curr < chip->typec_middle_current) { + + if (sm_state == PM_STATE_PPS_TUNNING_CURR + || sm_state == PM_STATE_PPS_TUNNING_VOLT + || sm_state == PM_STATE_CP_CC_LOOP + || sm_state == PM_STATE_CP_CV_LOOP) { + + mmi_chrg_err(chip, "%s, wrong pd voltage or current , " + "request pd voltage %d, " + "request pd current %d\n", + pm_state_str[sm_state], + chip->pd_target_volt, + chip->pd_target_curr); + mmi_chrg_sm_move_state(chip, PM_STATE_ENTRY); + } + goto skip_pd_select; + } +#if 1 + chip->pps_result = qc3p_select_pdo(chip,chip->pd_target_volt,chip->pd_target_curr); + +#else + chip->pps_result = usbpd_select_pdo(chip->pd_handle, + chip->mmi_pd_pdo_idx, + chip->pd_target_volt, + chip->pd_target_curr); +#endif + mmi_set_pps_result_history(chip, chip->pps_result); + if (!chip->pps_result) { + mmi_chrg_err(chip, "current pd voltage,current level volt:%d,curr:%d\n",chip->pd_target_volt,chip->pd_target_curr); + chip->pd_request_volt_prev = chip->pd_target_volt; + chip->pd_request_curr_prev = chip->pd_target_curr; + } + +skip_pd_select: + + if (heartbeat_dely_ms > 0) { + mmi_chrg_err(chip, "schedule work timer %dms\n", heartbeat_dely_ms); + schedule_delayed_work(&chip->mmi_chrg_sm_work, + msecs_to_jiffies(heartbeat_dely_ms)); + } else { + mmi_chrg_policy_clear(chip); + chip->sm_work_running = false; + chip->pd_pps_support = false; + mmi_chrg_err(chip, "exit sm work\n"); + } + + return; +} + +int get_caculated_real_ibus_vbus(struct mmi_charger_manager *chip, int *ibus_curr,int *calculated_vbus) { + int rc; + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + union power_supply_propval prop = {0,}; + int vbus_volt = 0,ibus_curr_temp = 0,ibus_usb = 0,ibus_pump = 0,ibus_pump_slave = 0; + int calculated_vbus_temp = 0; + + rc = power_supply_get_property(chrg_list->chrg_dev[CP_MASTER]->chrg_psy, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED, &prop); + if (!rc) + vbus_volt = prop.intval; + + rc = power_supply_get_property(chrg_list->chrg_dev[CP_MASTER]->chrg_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, &prop); + if (!rc) + ibus_pump = prop.intval; + + if(ibus_pump < 0) + ibus_pump = 0; + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, &prop); + if (!rc) + ibus_usb = prop.intval; + if(ibus_usb < 0) + ibus_usb = 0; + + if (chrg_list->cp_slave && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + rc = power_supply_get_property(chrg_list->chrg_dev[CP_SLAVE]->chrg_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, &prop); + if (!rc) + ibus_pump_slave = prop.intval; + if(ibus_pump_slave < 0) + ibus_pump_slave = 0; + } + + //calculate the real ibus vbus + ibus_curr_temp = ibus_usb + ibus_pump+ibus_pump_slave; + calculated_vbus_temp = vbus_volt+ (ibus_curr_temp *200)/1000; // Define vbus resistance 200 mili oum + + *ibus_curr = ibus_curr_temp; + *calculated_vbus = calculated_vbus_temp; + mmi_chrg_err(chip, "calculated ibus:%d,vbus:%d\n",*ibus_curr,*calculated_vbus); + + return 0; +} + +#define QC3P_PULSE_COUNT_MAX ((11000 - 5000) / 20 ) +int qc3p_select_pdo(struct mmi_charger_manager *chip,int target_uv, int target_ua){ + int rc; + struct mmi_cp_policy_dev *chrg_list = &g_chrg_list; + union power_supply_propval prop = {0,}; + int ibatt_curr = 0, vbatt_volt = 0, vbus_volt = 0,ibus_curr = 0,ibus_usb = 0,ibus_pump = 0,ibus_pump_slave = 0; + int i = 0; + int calculated_vbus = 0; + int req_volt_inc_step = 0,req_volt_dec_step = 0; + int req_curr_inc_step = 0,req_curr_dec_step = 0; + int real_inc_step = 0,real_dec_step =0; + union power_supply_propval val = {0, }; + int pulse_cnt_curr; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + vbatt_volt = prop.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + ibatt_curr = prop.intval; + + rc = power_supply_get_property(chrg_list->chrg_dev[CP_MASTER]->chrg_psy, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED, &prop); + if (!rc) + vbus_volt = prop.intval; + + rc = power_supply_get_property(chrg_list->chrg_dev[CP_MASTER]->chrg_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, &prop); + if (!rc) + ibus_pump = prop.intval; + + if(ibus_pump < 0) + ibus_pump = 0; + + if (chrg_list->cp_slave && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + rc = power_supply_get_property(chrg_list->chrg_dev[CP_SLAVE]->chrg_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, &prop); + if (!rc) + ibus_pump_slave= prop.intval; + } + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, &prop); + if (!rc) + ibus_usb = prop.intval; + if(ibus_usb < 0) + ibus_usb = 0; + + mmi_chrg_err(chip, "ibus_usb:%d,ibus_pump:%d,ibus_pump_slave:%d\n",ibus_usb , ibus_pump, ibus_pump_slave); + + //calculate the real ibus vbus + ibus_curr = ibus_usb + ibus_pump; + if (chrg_list->cp_slave && chrg_list->chrg_dev[CP_SLAVE]->charger_enabled) { + ibus_curr += ibus_pump_slave; + } + calculated_vbus = vbus_volt+ (ibus_curr *200)/1000; // Define vbus resistance 200 mili oum + + mmi_chrg_err(chip, "qc3p_select_pdo current vbat:%d,ibatt:%d,vbus:%d,calculated_vbus:%d,ibus:%d,target_uv:%d,prev_uv:%d,target_ua:%d,prev_ua:%d\n",vbatt_volt,ibatt_curr,vbus_volt,calculated_vbus,ibus_curr,chip->pd_target_volt,chip->pd_request_volt_prev,chip->pd_target_curr,chip->pd_request_curr_prev); + + if(((ibus_curr > chip->pd_curr_max && chrg_list->chrg_dev[CP_MASTER]->charger_enabled) || calculated_vbus > chip->pd_volt_max) && sm_state != PM_STATE_POWER_LIMIT_LOOP && sm_state != PM_STATE_SW_LOOP ){ + if(chip->pd_request_volt_prev != 0 && chip->pd_request_curr_prev != 0) { + chip->pd_request_volt = chip->pd_request_volt_prev; + chip->pd_target_volt = chip->pd_request_volt; + chip->pd_request_curr = chip->pd_request_curr_prev; + chip->pd_target_curr = chip->pd_request_curr; + } + mmi_chrg_sm_move_state(chip,PM_STATE_POWER_LIMIT_LOOP); + return 0; + } + + if(chip->pd_request_volt_prev == 0) + chip->pd_request_volt_prev = 5000000; + + //req current step + if(chip->pd_target_curr > chip->pd_request_curr_prev) { //100mA -> 100mv + req_curr_inc_step = (chip->pd_target_curr -chip->pd_request_curr_prev)/100000 * CURR_TO_VOLT_STEP; + if(req_curr_inc_step < 0 ) + req_curr_inc_step = 0; + + }else if(chip->pd_target_curr < chip->pd_request_curr_prev) { + req_curr_dec_step = (chip->pd_request_curr_prev - chip->pd_target_curr)/100000 * CURR_TO_VOLT_STEP; + if(req_curr_dec_step < 0) + req_curr_dec_step = 0; + + //check the steps + if(ibus_curr <= chip->pd_target_curr ) { + mmi_chrg_err(chip,"ibus <= target curr,not decrease curr"); + req_volt_inc_step = 0; + } + } + + //req voltage step + if(chip->pd_target_volt >= chip->pd_request_volt_prev) { + req_volt_inc_step = (chip->pd_target_volt -chip->pd_request_volt_prev)/QC3P_V_STEP; + if(req_volt_inc_step<0) + req_volt_inc_step = 0; + + }else { + req_volt_dec_step = (chip->pd_request_volt_prev - chip->pd_target_volt)/QC3P_V_STEP; + if(req_volt_dec_step < 0) + req_volt_dec_step = 0; + + } + + //check the steps + if(ibus_curr > chip->pd_curr_max || calculated_vbus > chip->pd_volt_max) { + mmi_chrg_err(chip,"ibus > max curr or vbus, skip inc volt"); + req_volt_inc_step = 0; + req_volt_inc_step = 0; + } + + mmi_chrg_err(chip, "prev_calculate current inc:%d,dec:%d,voltage inc:%d,dec:%d\n",req_curr_inc_step,req_curr_dec_step,req_volt_inc_step,req_volt_dec_step); + + real_inc_step = max(req_volt_inc_step,req_curr_inc_step); + real_dec_step = max(req_volt_dec_step,req_curr_dec_step); + + mmi_chrg_err(chip, "after calculated, voltage inc:%d,dec:%d\n",real_inc_step,real_dec_step); + + //get current dp dm count + rc = power_supply_get_property(chip->qcom_psy, + POWER_SUPPLY_PROP_DP_DM, &val); + if (rc < 0) { + mmi_chrg_err(chip, "Couldn't read dpdm pulse count rc=%d\n", rc); + return -EINVAL; + } else { + mmi_chrg_err(chip, "DP DM pulse count = %d\n", val.intval); + pulse_cnt_curr = val.intval; + } + + //limit not beyond max pulse + if(real_inc_step + pulse_cnt_curr > QC3P_PULSE_COUNT_MAX) { + real_inc_step = QC3P_PULSE_COUNT_MAX - pulse_cnt_curr; + mmi_chrg_err(chip, "volt increase step bigger than max, real_increse_step:%d\n",real_inc_step); + } + + if(real_dec_step > pulse_cnt_curr) { + real_dec_step = pulse_cnt_curr; + mmi_chrg_err(chip, "vol decrese step bigger than current pulse, set to curr_pulse:%d\n",real_dec_step); + } + + if(real_inc_step > 0 +// && (chip->pd_target_volt >= calculated_vbus) + && (calculated_vbus <= chip->pd_volt_max) + && ibus_curr <= chip->pd_curr_max){ //increase + + prop.intval = POWER_SUPPLY_DP_DM_DP_PULSE; + mmi_chrg_err(chip, "increase voltage pulse:%d\n", real_inc_step); + for(i=0;iqcom_psy, + POWER_SUPPLY_PROP_DP_DM, &prop); + if (rc < 0) { + mmi_chrg_err(chip, "Couldn't set dp pulse\n"); + } + udelay(5000); + } + if(chip->pd_request_volt_prev != 0) { + chip->pd_request_volt = chip->pd_request_volt_prev + real_inc_step*QC3P_V_STEP; + chip->pd_target_volt = chip->pd_request_volt; + } + } else { + + prop.intval = POWER_SUPPLY_DP_DM_DM_PULSE; + mmi_chrg_err(chip, "decrease volt pulse:%d\n", real_dec_step); + for(i=0;iqcom_psy, + POWER_SUPPLY_PROP_DP_DM, &prop); + if (rc < 0) { + mmi_chrg_err(chip, "Couldn't set dm pulse\n"); + } + udelay(5000); + } + if(chip->pd_request_volt_prev != 0) { + chip->pd_request_volt = chip->pd_request_volt_prev - real_dec_step*QC3P_V_STEP; + chip->pd_target_volt = chip->pd_request_volt; + } + } + return 0; +} + +int mmi_chrg_policy_init(struct mmi_charger_manager *chip, + struct mmi_chrg_dts_info *chrg_dts, + int chrg_cnt) { + int i = 0, ops_cnt = 0, chrg_idx = 0; + struct mmi_charger_device *chrg_dev; + + if (!chip ||!chrg_dts) { + mmi_chrg_err(chip, "invalid input param!\n"); + return -EINVAL; + } + + ops_cnt = ARRAY_SIZE(dev_ops); + if (chrg_cnt > ops_cnt || + chrg_cnt > chip->mmi_chrg_dev_num) { + mmi_chrg_err(chip, "invalid input param!, chrg_cnt %d , ops_cnt %d, " + "mmi_chrg_dev_num %d\n", + chrg_cnt, ops_cnt, chip->mmi_chrg_dev_num); + return -EINVAL; + } + + mmi_chrg_err(chip, "chrg_cnt %d\n", chrg_cnt); + for (i = 0; i < chrg_cnt; i++) { + if (!strcmp(chrg_dts[i].chrg_name, dev_ops[i].dev_name)) { + + chrg_dev = mmi_charger_device_register(chrg_dts[i].chrg_name, + chrg_dts[i].psy_name, chip->dev, chip, + dev_ops[i].ops); + if (IS_ERR_OR_NULL(chrg_dev) + || !is_charger_exist(chrg_dts[i].chrg_name)) { + mmi_chrg_err(chip, + "register mmi charger %s failed\n", + chrg_dts[i].chrg_name); + return -EINVAL; + } else { + mmi_chrg_info(chip, + "register mmi charger %s successfully, i %d, chrg_idx %d\n", + chrg_dts[i].chrg_name, i, chrg_idx); + chrg_dev->charging_curr_limited = + chrg_dts[i].charging_curr_limited; + mmi_chrg_err(chip, "charging_curr_limited %d\n", + chrg_dts[i].charging_curr_limited); + chrg_dev->charging_curr_min = + chrg_dts[i].charging_curr_min; + mmi_chrg_err(chip, "charging_curr_min %d, chrg_id %d\n", + chrg_dts[i].charging_curr_min, chrg_idx); + chip->chrg_list[chrg_idx] = chrg_dev; + mmi_chrg_err(chip, "--- over ----\n"); + chrg_idx++; + } + } + } + + mmi_chrg_info(chip,"chrg_cnt %d, ops_cnt %d, mmi_chrg_dev_num %d, " + "chrg_idx %d\n", + chrg_cnt, ops_cnt, chip->mmi_chrg_dev_num, chrg_idx); + + if (chrg_idx != chip->mmi_chrg_dev_num + && chrg_idx > 0) { + + mmi_chrg_err(chip, "chrg_id %d != charger num %d\n", + chrg_idx, chip->mmi_chrg_dev_num); + for (i = 0; i < chrg_idx; i++) + mmi_charger_device_unregister(chip->chrg_list[i]); + return -EINVAL; + } + + chrg_dev_init(chip, &g_chrg_list); + chip->pps_volt_comp = PPS_INIT_VOLT_COMP; + INIT_DELAYED_WORK(&chip->mmi_chrg_sm_work, mmi_chrg_sm_work_func); + return 0; +} + diff --git a/drivers/power/mmi_parallel_charger_qc3p/qpnp_pmic_charger.c b/drivers/power/mmi_parallel_charger_qc3p/qpnp_pmic_charger.c new file mode 100644 index 000000000000..156ae7ec44e5 --- /dev/null +++ b/drivers/power/mmi_parallel_charger_qc3p/qpnp_pmic_charger.c @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include "mmi_charger_class.h" +#include +#include "mmi_charger_core.h" + +static int qpnp_pmic_enable_charging(struct mmi_charger_device *chrg, bool en) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + prop.intval = en; + rc = power_supply_set_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, &prop); + + if (!rc) { + chrg->charger_enabled = !!prop.intval; + } else + chrg->charger_enabled = false; + + return rc; +} + +static int qpnp_pmic_is_charging_enabled(struct mmi_charger_device *chrg, bool *en) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, &prop); + if (!rc) { + chrg->charger_enabled = !!prop.intval; + } else + chrg->charger_enabled = false; + + *en = chrg->charger_enabled; + + return rc; +} + +static int qpnp_pmic_get_charging_current(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + *uA = !!prop.intval; + + return rc; +} + +static int qpnp_pmic_set_charging_current(struct mmi_charger_device *chrg, u32 uA) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + prop.intval = uA; + rc = power_supply_set_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_QC3P_CURRENT_MAX, &prop); +// if (!rc) +// chrg->charger_limited = true; + + return rc; +} + +static int qpnp_pmic_get_input_current_settled(struct mmi_charger_device *chrg, u32 *uA) +{ + int rc; + struct power_supply *chrg_psy; + union power_supply_propval prop = {0,}; + + chrg_psy = power_supply_get_by_name("usb"); + if (!chrg_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg_psy, + POWER_SUPPLY_PROP_HW_CURRENT_MAX, &prop); + if (!rc) + chrg->input_curr_setted = prop.intval; + *uA = chrg->input_curr_setted; + + return rc; +} + +static int qpnp_pmic_update_charger_status(struct mmi_charger_device *chrg) +{ + int rc; + struct power_supply *usb_psy; + union power_supply_propval prop = {0,}; + + if (!chrg->chrg_psy) + return -ENODEV; + + usb_psy = power_supply_get_by_name("usb"); + if (!usb_psy) + return -ENODEV; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + chrg->charger_data.vbatt_volt = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + chrg->charger_data.ibatt_curr = prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_TEMP, &prop); + if (!rc) + chrg->charger_data.batt_temp = prop.intval / 10; + + rc = power_supply_get_property(usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + chrg->charger_data.vbus_volt = prop.intval; + + rc = power_supply_get_property(usb_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, &prop); + if (!rc) + chrg->charger_data.ibus_curr = prop.intval; + + rc = power_supply_get_property(usb_psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (!rc) + chrg->charger_data.vbus_pres = !!prop.intval; + + rc = power_supply_get_property(chrg->chrg_psy, + POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, &prop); + if (!rc) + chrg->charger_enabled= !!prop.intval; + + chrg_dev_info(chrg, "QPNP SW chrg: status update: --- info---"); + chrg_dev_info(chrg, "vbatt %d\n", chrg->charger_data.vbatt_volt); + chrg_dev_info(chrg, "ibatt %d\n", chrg->charger_data.ibatt_curr); + chrg_dev_info(chrg, "batt temp %d\n", chrg->charger_data.batt_temp); + chrg_dev_info(chrg, "vbus %d\n", chrg->charger_data.vbus_volt); + chrg_dev_info(chrg, "ibus %d\n", chrg->charger_data.ibus_curr); + chrg_dev_info(chrg, "vbus pres %d\n", chrg->charger_data.vbus_pres); + chrg_dev_info(chrg, "charger_enabled %d\n", chrg->charger_enabled); + chrg_dev_info(chrg, "charger_limited %d\n", chrg->charger_limited); + return rc; +} + +struct mmi_charger_ops qpnp_pmic_charger_ops = { + .enable = qpnp_pmic_enable_charging, + .is_enabled = qpnp_pmic_is_charging_enabled, + .get_charging_current = qpnp_pmic_get_charging_current, + .set_charging_current = qpnp_pmic_set_charging_current, + .get_input_current_settled = qpnp_pmic_get_input_current_settled, + .update_charger_status = qpnp_pmic_update_charger_status, +}; diff --git a/drivers/power/mmi_pl_chg_manager/Android.mk b/drivers/power/mmi_pl_chg_manager/Android.mk new file mode 100644 index 000000000000..dd5d188cfd17 --- /dev/null +++ b/drivers/power/mmi_pl_chg_manager/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_pl_chg_manager.ko +LOCAL_MODULE_TAGS := optional + +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/mmi_pl_chg_manager/Kbuild b/drivers/power/mmi_pl_chg_manager/Kbuild new file mode 100644 index 000000000000..c3cf641155be --- /dev/null +++ b/drivers/power/mmi_pl_chg_manager/Kbuild @@ -0,0 +1,6 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include \ + -I$(TOP)/motorola/kernel/modules/drivers/power/mmi_pl_chg_manager + +obj-m += mmi_pl_chg_manager.o diff --git a/drivers/power/mmi_pl_chg_manager/Makefile b/drivers/power/mmi_pl_chg_manager/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/power/mmi_pl_chg_manager/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/power/mmi_pl_chg_manager/mmi_pl_chg_manager.c b/drivers/power/mmi_pl_chg_manager/mmi_pl_chg_manager.c new file mode 100644 index 000000000000..52b2d9c42583 --- /dev/null +++ b/drivers/power/mmi_pl_chg_manager/mmi_pl_chg_manager.c @@ -0,0 +1,2557 @@ +/* + * Copyright (c) 2018 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define mmi_pl_err(chip, fmt, ...) \ + pr_err("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__) \ + +#define mmi_pl_dbg(chip, reason, fmt, ...) \ + do { \ + if (*chip->debug_mask & (reason)) \ + pr_info("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__); \ + else \ + pr_debug("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__); \ + } while (0) + +#define PD_SRC_PDO_TYPE_FIXED 0 +#define PD_SRC_PDO_TYPE_BATTERY 1 +#define PD_SRC_PDO_TYPE_VARIABLE 2 +#define PD_SRC_PDO_TYPE_AUGMENTED 3 +#define TYPEC_HIGH_CURRENT_UA 3000000 +#define TYPEC_MIDDLE_CURRENT_UA 2000000 +#define SWITCH_CHARGER_PPS_VOLT 5000000 +#define FLASH_CHARGER_PPS_MIN_VOLT 8000000 +#define FLASH_CHARGER_PPS_VOLT 9000000 +#define FLASH_CHARGER_PPS_MAX_VOLT 10000000 + +#define BAT_OVP_FAULT_SHIFT 8 +#define BAT_OCP_FAULT_SHIFT 9 +#define BUS_OVP_FAULT_SHIFT 10 +#define BUS_OCP_FAULT_SHIFT 11 +#define BAT_THERM_FAULT_SHIFT 12 +#define BUS_THERM_FAULT_SHIFT 13 +#define DIE_THERM_FAULT_SHIFT 14 + +#define BAT_OVP_FAULT_MASK (1 << BAT_OVP_FAULT_SHIFT) +#define BAT_OCP_FAULT_MASK (1 << BAT_OCP_FAULT_SHIFT) +#define BUS_OVP_FAULT_MASK (1 << BUS_OVP_FAULT_SHIFT) +#define BUS_OCP_FAULT_MASK (1 << BUS_OCP_FAULT_SHIFT) +#define BAT_THERM_FAULT_MASK (1 << BAT_THERM_FAULT_SHIFT) +#define BUS_THERM_FAULT_MASK (1 << BUS_THERM_FAULT_SHIFT) +#define DIE_THERM_FAULT_MASK (1 << DIE_THERM_FAULT_SHIFT) + +#define BAT_OVP_ALARM_SHIFT 0 +#define BAT_OCP_ALARM_SHIFT 1 +#define BUS_OVP_ALARM_SHIFT 2 +#define BUS_OCP_ALARM_SHIFT 3 +#define BAT_THERM_ALARM_SHIFT 4 +#define BUS_THERM_ALARM_SHIFT 5 +#define DIE_THERM_ALARM_SHIFT 6 +#define BAT_UCP_ALARM_SHIFT 7 + +#define BAT_OVP_ALARM_MASK (1 << BAT_OVP_ALARM_SHIFT) +#define BAT_OCP_ALARM_MASK (1 << BAT_OCP_ALARM_SHIFT) +#define BUS_OVP_ALARM_MASK (1 << BUS_OVP_ALARM_SHIFT) +#define BUS_OCP_ALARM_MASK (1 << BUS_OCP_ALARM_SHIFT) +#define BAT_THERM_ALARM_MASK (1 << BAT_THERM_ALARM_SHIFT) +#define BUS_THERM_ALARM_MASK (1 << BUS_THERM_ALARM_SHIFT) +#define DIE_THERM_ALARM_MASK (1 << DIE_THERM_ALARM_SHIFT) +#define BAT_UCP_ALARM_MASK (1 << BAT_UCP_ALARM_SHIFT) + +#define VBAT_REG_STATUS_SHIFT 0 +#define IBAT_REG_STATUS_SHIFT 1 + +#define VBAT_REG_STATUS_MASK (1 << VBAT_REG_STATUS_SHIFT) +#define IBAT_REG_STATUS_MASK (1 << VBAT_REG_STATUS_SHIFT) + +#define FLASHC_SHOW_MAX_SIZE 32 +#define PPS_RET_HISTORY_SIZE 8 + +enum print_reason { + PR_INTERRUPT = BIT(0), + PR_MISC = BIT(2), + PR_MOTO = BIT(7), +}; + +static int __debug_mask = PR_INTERRUPT; +module_param_named( + debug_mask, __debug_mask, int, S_IRUSR | S_IWUSR +); + +typedef enum { + PM_STATE_DISCONNECT, + PM_STATE_ENTRY, + PM_STATE_SW_ENTRY, + PM_STATE_SW_LOOP, + PM_STATE_FLASHC_ENTRY, + PM_STATE_FLASHC_TUNNING_CURR, + PM_STATE_FLASHC_TUNNING_VOLT, + PM_STATE_FLASHC_CC_LOOP, + PM_STATE_FLASHC_CV_LOOP, + PM_STATE_FLASHC_QUIT_1, + PM_STATE_FLASHC_QUIT_2, + PM_STATE_STOP_CHARGE, +} pm_sm_state_t; + +struct mmi_pl_temp_zone { + int temp_c; + int super_uv; + int fcc_super_ua; + int high_uv; + int fcc_high_ua; + int norm_uv; + int fcc_norm_ua; +}; + +struct mmi_thermal_power_zone { + int power_voltage; + int power_current; +}; + +#define MAX_NUM_STEPS 10 +enum mmi_pl_temp_zones { + ZONE_FIRST = 0, + /* states 0-9 are reserved for zones */ + ZONE_LAST = MAX_NUM_STEPS + ZONE_FIRST - 1, + ZONE_HOT, + ZONE_COLD, + ZONE_NONE = 0xFF, +}; + +enum mmi_chrg_step { + STEP_SUPER, + STEP_HIGH, + STEP_NORMAL, + STEP_NUM, +}; + +enum mmi_pd_pps_result { + NO_ERROR, + BLANCE_POWER, + RESET_POWER, +}; + +struct sys_config { + u32 flashc_volt_hysteresis; + u32 flashc_curr_hysteresis; + u32 flashc_volt_up_steps; + u32 flashc_curr_up_steps; + u32 flashc_curr_down_steps; + u32 flashc_volt_down_steps; + u32 flashc_min_vbat_start; + u32 pmic_curr_lp_lmt; + u32 batt_ovp_lmt; +}; + +struct flashc_info { + bool charge_enabled; + bool batt_pres; + bool vbus_pres; + + /* alarm/fault status */ + bool bat_ovp_fault; + bool bat_ocp_fault; + bool bus_ovp_fault; + bool bus_ocp_fault; + + bool bat_ovp_alarm; + bool bat_ocp_alarm; + bool bus_ovp_alarm; + bool bus_ocp_alarm; + bool bat_ucp_alarm; + + bool bat_therm_alarm; + bool bus_therm_alarm; + bool die_therm_alarm; + bool bat_therm_fault; + bool bus_therm_fault; + bool die_therm_fault; + + bool therm_shutdown_flag; + bool therm_shutdown_stat; + + bool vbat_reg; + bool ibat_reg; + int vbat_volt; + int vbus_volt; + int ibat_curr; + int ibus_curr; + int bat_temp; + int bus_temp; + int die_temp; +}; + +struct pmic_info { + bool charge_enabled; + bool batt_pres; + bool vbus_pres; + int vbat_volt; + int vbus_volt; + int ibat_curr; + int ibus_curr; + int bat_temp; +}; + +struct mmi_pl_chg_manager { + struct device *dev; + const char *name; + const char *flashc_name; + int *debug_mask; + + struct sys_config sys_configs; + struct flashc_info flashc_handle; + struct pmic_info pmic_handle; + + struct power_supply *flashc_psy; + struct power_supply *batt_psy; + struct power_supply *usb_psy; + struct power_supply *mmi_pl_pm_psy; + + struct notifier_block psy_nb; + struct usbpd *pd_handle; + struct usbpd_pdo_info mmi_pdo_info[PD_MAX_PDO_NUM]; + int mmi_pps_pdo_idx; + + int pmic_ichg_val; + bool pmic_ichg_limited; + int pmic_step_volt; + int pmic_step_curr; + + int flashc_cc_max_curr_pre; + int flashc_cv_max_volt_pre; + int flashc_cv_taper_curr_pre; + int request_volt; + int request_current; + int request_volt_pre; + int request_curr_pre; + int target_volt; + int target_curr; + int pps_current_max; + int pps_voltage_max; + bool pps_increase_volt; + bool pps_power_balance; + int pps_result; + int pps_result_history[PPS_RET_HISTORY_SIZE]; + int pps_result_history_idx; + int flashc_taper_delta_volt; + int flashc_taper_cnt; + int flashc_cc_tunning_cnt; + bool vbus_present; + bool force_pmic_chg; + bool enter_sw_loop; + bool pd_pps_support; + bool pd_pps_mitigation; + bool flashc_alarm; + bool flashc_fault; + int flashc_recovery_count; + pm_sm_state_t sm_state; + u64 fg_esr_pulse_timeout; + struct delayed_work mmi_pl_sm_work; + struct work_struct psy_changed_work; + struct completion sm_completion; + + int num_temp_zones; + struct mmi_pl_temp_zone *temp_zones; + enum mmi_pl_temp_zones pres_temp_zone; + + int num_thermal_zones; + struct mmi_thermal_power_zone *thermal_mitigation_zones; + int thermal_mitigation_level; + enum mmi_chrg_step pres_chrg_step; +}; + +static int __param_flashc_cc_max_ua; +module_param_named( + param_flashc_cc_max_ua, + __param_flashc_cc_max_ua, int, S_IRUSR | S_IWUSR); + +static void mmi_update_pmic_status(struct mmi_pl_chg_manager *chip) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chip->batt_psy) { + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) + return; + } + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) + return; + } + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + chip->pmic_handle.vbat_volt = prop.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + chip->pmic_handle.ibat_curr = prop.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_TEMP, &prop); + if (!rc) + chip->pmic_handle.bat_temp = prop.intval; + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + chip->pmic_handle.vbus_volt = prop.intval; + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, &prop); + if (!rc) + chip->pmic_handle.ibus_curr = prop.intval; + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (!rc) + chip->pmic_handle.vbus_pres = !!prop.intval; + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, &prop); + if (!rc) + chip->pmic_handle.charge_enabled= !!prop.intval; + + mmi_pl_dbg(chip, PR_MOTO, "main chargr IC : ----- status update ----\n"); + mmi_pl_dbg(chip, PR_MOTO, "vbat_volt %d \n", + chip->pmic_handle.vbat_volt); + mmi_pl_dbg(chip, PR_MOTO, "ibat_curr %d \n", + chip->pmic_handle.ibat_curr); + mmi_pl_dbg(chip, PR_MOTO, "vbus_volt %d \n", + chip->pmic_handle.vbus_volt); + mmi_pl_dbg(chip, PR_MOTO, "ibus_curr %d \n", + chip->pmic_handle.ibus_curr); + mmi_pl_dbg(chip, PR_MOTO, "bat_temp %d \n", + chip->pmic_handle.bat_temp); + mmi_pl_dbg(chip, PR_MOTO, "vbus_pres %d \n", + chip->pmic_handle.vbus_pres); + mmi_pl_dbg(chip, PR_MOTO, "charge_enabled %d \n", + chip->pmic_handle.charge_enabled); + + +} + +static void mmi_update_flashc_status(struct mmi_pl_chg_manager *chip) +{ + + int rc; + union power_supply_propval prop = {0,}; + + chip->flashc_psy = power_supply_get_by_name(chip->flashc_name); + if (!chip->flashc_psy) + return; + + rc = power_supply_get_property(chip->flashc_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (!rc) + chip->flashc_handle.vbat_volt = prop.intval*1000; + + rc = power_supply_get_property(chip->flashc_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (!rc) + chip->flashc_handle.ibat_curr = prop.intval*1000; + + rc = power_supply_get_property(chip->flashc_psy, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED, &prop); + if (!rc) + chip->flashc_handle.vbus_volt = prop.intval*1000; + + rc = power_supply_get_property(chip->flashc_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, &prop); + if (!rc) + chip->flashc_handle.ibus_curr = prop.intval*1000; + + rc = power_supply_get_property(chip->flashc_psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (!rc) + chip->flashc_handle.vbus_pres = !!prop.intval; + + rc = power_supply_get_property(chip->flashc_psy, + POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop); + if (!rc) + chip->flashc_handle.charge_enabled = !!prop.intval; + + rc = power_supply_get_property(chip->flashc_psy, + POWER_SUPPLY_PROP_CHARGE_NOW_ERROR, &prop); + if (!rc) { + chip->flashc_handle.bat_ovp_alarm = + !!(prop.intval & BAT_OVP_ALARM_MASK); + chip->flashc_handle.bat_ocp_alarm = + !!(prop.intval & BAT_OCP_ALARM_MASK); + chip->flashc_handle.bus_ovp_alarm = + !!(prop.intval & BUS_OVP_ALARM_MASK); + chip->flashc_handle.bus_ocp_alarm = + !!(prop.intval & BUS_OCP_ALARM_MASK); + chip->flashc_handle.bat_ucp_alarm = + !!(prop.intval & BAT_UCP_ALARM_MASK); + chip->flashc_handle.bat_therm_alarm = + !!(prop.intval & BAT_THERM_ALARM_MASK); + chip->flashc_handle.bus_therm_alarm = + !!(prop.intval & BUS_THERM_ALARM_MASK); + chip->flashc_handle.die_therm_alarm = + !!(prop.intval & DIE_THERM_ALARM_MASK); + chip->flashc_handle.bat_ovp_fault = + !!(prop.intval & BAT_OVP_FAULT_MASK); + chip->flashc_handle.bat_ocp_fault = + !!(prop.intval & BAT_OCP_FAULT_MASK); + chip->flashc_handle.bus_ovp_fault = + !!(prop.intval & BUS_OVP_FAULT_MASK); + chip->flashc_handle.bus_ocp_fault = + !!(prop.intval & BUS_OCP_FAULT_MASK); + chip->flashc_handle.bat_therm_fault = + !!(prop.intval & BAT_THERM_FAULT_MASK); + chip->flashc_handle.bus_therm_fault = + !!(prop.intval & BUS_THERM_FAULT_MASK); + chip->flashc_handle.die_therm_fault = + !!(prop.intval & DIE_THERM_FAULT_MASK); + } + power_supply_put(chip->flashc_psy); + + mmi_pl_dbg(chip, PR_MOTO, "flash charge IC : ---- status update ---\n"); + mmi_pl_dbg(chip, PR_MOTO, "vbat_volt %d \n", + chip->flashc_handle.vbat_volt); + mmi_pl_dbg(chip, PR_MOTO, "ibat_curr %d \n", + chip->flashc_handle.ibat_curr); + mmi_pl_dbg(chip, PR_MOTO, "vbus_volt %d \n", + chip->flashc_handle.vbus_volt); + mmi_pl_dbg(chip, PR_MOTO, "ibus_curr %d \n", + chip->flashc_handle.ibus_curr); + mmi_pl_dbg(chip, PR_MOTO, "bus_temp %d \n", + chip->flashc_handle.bus_temp); + mmi_pl_dbg(chip, PR_MOTO, "bat_temp %d \n", + chip->flashc_handle.bat_temp); + mmi_pl_dbg(chip, PR_MOTO, "die_temp %d \n", + chip->flashc_handle.die_temp); + mmi_pl_dbg(chip, PR_MOTO, "batt_pres %d \n", + chip->flashc_handle.batt_pres); + mmi_pl_dbg(chip, PR_MOTO, "vbus_pres %d \n", + chip->flashc_handle.vbus_pres); + mmi_pl_dbg(chip, PR_MOTO, "charge_enabled %d \n", + chip->flashc_handle.charge_enabled); + mmi_pl_dbg(chip, PR_MOTO, "bat_ovp_alarm %d \n", + chip->flashc_handle.bat_ovp_alarm); + mmi_pl_dbg(chip, PR_MOTO, "bat_ocp_alarm %d \n", + chip->flashc_handle.bat_ocp_alarm); + mmi_pl_dbg(chip, PR_MOTO, "bus_ovp_alarm %d \n", + chip->flashc_handle.bus_ovp_alarm); + mmi_pl_dbg(chip, PR_MOTO, "bus_ocp_alarm %d \n", + chip->flashc_handle.bus_ocp_alarm); + mmi_pl_dbg(chip, PR_MOTO, "bat_ucp_alarm %d \n", + chip->flashc_handle.bat_ucp_alarm); + mmi_pl_dbg(chip, PR_MOTO, "bat_therm_alarm %d \n", + chip->flashc_handle.bat_therm_alarm); + mmi_pl_dbg(chip, PR_MOTO, "bus_therm_alarm %d \n", + chip->flashc_handle.bus_therm_alarm); + mmi_pl_dbg(chip, PR_MOTO, "die_therm_alarm %d \n", + chip->flashc_handle.die_therm_alarm); + + mmi_pl_dbg(chip, PR_MOTO, "bat_ovp_fault %d \n", + chip->flashc_handle.bat_ovp_fault); + mmi_pl_dbg(chip, PR_MOTO, "bat_ocp_fault %d \n", + chip->flashc_handle.bat_ocp_fault); + mmi_pl_dbg(chip, PR_MOTO, "bus_ovp_fault %d \n", + chip->flashc_handle.bus_ovp_fault); + mmi_pl_dbg(chip, PR_MOTO, "bus_ocp_fault %d \n", + chip->flashc_handle.bus_ocp_fault); + mmi_pl_dbg(chip, PR_MOTO, "bat_therm_fault %d \n", + chip->flashc_handle.bat_therm_fault); + mmi_pl_dbg(chip, PR_MOTO, "bus_therm_fault %d \n", + chip->flashc_handle.bus_therm_fault); + mmi_pl_dbg(chip, PR_MOTO, "die_therm_fault %d \n", + chip->flashc_handle.die_therm_fault); + mmi_pl_dbg(chip, PR_MOTO, "flash charge : ---- over ---\n"); + +} + +static int mmi_pl_pm_flashc_enable(struct mmi_pl_chg_manager *chip, bool enable) +{ + int rc; + union power_supply_propval prop = {0,}; + + chip->flashc_psy = power_supply_get_by_name(chip->flashc_name); + if (!chip->flashc_psy) + return -ENODEV; + + prop.intval = enable; + rc = power_supply_set_property(chip->flashc_psy, + POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop); + if (rc < 0) { + mmi_pl_err(chip, "Couldn't disable flashc charging, rc=%d\n", rc); + power_supply_put(chip->flashc_psy); + return rc; + } + power_supply_put(chip->flashc_psy); + + return rc; +} + +static int mmi_pl_pm_check_flashc_enable(struct mmi_pl_chg_manager *chip) +{ + int rc; + union power_supply_propval prop = {0,}; + + chip->flashc_psy = power_supply_get_by_name(chip->flashc_name); + if (!chip->flashc_psy) { + chip->flashc_handle.charge_enabled = false; + return -ENODEV; + } + + rc = power_supply_get_property(chip->flashc_psy, + POWER_SUPPLY_PROP_CHARGING_ENABLED, &prop); + if (!rc) { + chip->flashc_handle.charge_enabled = !!prop.intval; + } else + chip->flashc_handle.charge_enabled = false; + + power_supply_put(chip->flashc_psy); + + return rc; +} + +static int mmi_pl_pm_pmic_enable(struct mmi_pl_chg_manager *chip, bool enable) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chip->batt_psy) { + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) + return -ENODEV; + } + + prop.intval = enable; + rc = power_supply_set_property(chip->batt_psy, + POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, &prop); + if (rc < 0) { + mmi_pl_err(chip, "Couldn't disable pmic charging, rc=%d\n", rc); + return rc; + } + + return rc; +} + +static int mmi_pl_pm_check_pmic_enable(struct mmi_pl_chg_manager *chip) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chip->batt_psy) { + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) { + chip->pmic_handle.charge_enabled = false; + return -ENODEV; + } + } + + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED, &prop); + if (!rc) { + chip->pmic_handle.charge_enabled = !!prop.intval; + } else + chip->pmic_handle.charge_enabled = false; + + return rc; +} + +static int mmi_pl_pm_pmic_ichg_lmt(struct mmi_pl_chg_manager *chip, unsigned uA) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chip->batt_psy) { + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) + return -ENODEV; + } + + prop.intval = uA; + rc = power_supply_set_property(chip->batt_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, &prop); + if (rc < 0) { + mmi_pl_err(chip, "Couldn't limit usb current, rc=%d\n", rc); + return rc; + } + + chip->pmic_ichg_limited = true; + + return rc; +} + +static int mmi_pl_pm_get_usb_icl(struct mmi_pl_chg_manager *chip) +{ + int rc; + union power_supply_propval prop = {0,}; + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) + return -ENODEV; + } + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_HW_CURRENT_MAX, &prop); + if (rc < 0) { + mmi_pl_err(chip, "Couldn't limit usb current, rc=%d\n", rc); + return rc; + } + + chip->pmic_ichg_val = prop.intval; + return rc; +} + +#define MIN_TEMP_C -20 +#define MAX_TEMP_C 60 +#define MIN_MAX_TEMP_C 47 +#define HYSTERISIS_DEGC 2 +static bool mmi_find_temp_zone(struct mmi_pl_chg_manager *chip, int temp_c) +{ + int prev_zone, num_zones; + struct mmi_pl_temp_zone *zones; + int hotter_t; + int colder_t; + int i; + int max_temp; + + if (!chip) { + mmi_pl_err(chip, "called before chip valid!\n"); + return false; + } + + zones = chip->temp_zones; + num_zones = chip->num_temp_zones; + prev_zone = chip->pres_temp_zone; + + max_temp = zones[num_zones - 1].temp_c; + + if (prev_zone == ZONE_NONE) { + for (i = num_zones - 1; i >= 0; i--) { + if (temp_c >= zones[i].temp_c) { + if (i == num_zones - 1) + chip->pres_temp_zone = ZONE_HOT; + else + chip->pres_temp_zone = i + 1; + return true; + } + } + chip->pres_temp_zone = ZONE_COLD; + return true; + } + + if (prev_zone == ZONE_COLD) { + if (temp_c >= MIN_TEMP_C + HYSTERISIS_DEGC) + chip->pres_temp_zone = ZONE_FIRST; + } else if (prev_zone == ZONE_HOT) { + if (temp_c <= max_temp - HYSTERISIS_DEGC) + chip->pres_temp_zone = num_zones - 1; + } else { + if (prev_zone == ZONE_FIRST) { + hotter_t = zones[prev_zone].temp_c; + colder_t = MIN_TEMP_C; + } else if (prev_zone == num_zones - 1) { + hotter_t = zones[prev_zone].temp_c + HYSTERISIS_DEGC; + colder_t = zones[prev_zone - 1].temp_c; + } else { + hotter_t = zones[prev_zone].temp_c + HYSTERISIS_DEGC; + colder_t = zones[prev_zone - 1].temp_c; + } + + if (temp_c < MIN_TEMP_C) + chip->pres_temp_zone = ZONE_COLD; + else if (temp_c >= max_temp) + chip->pres_temp_zone = ZONE_HOT; + else if (temp_c >= hotter_t) + chip->pres_temp_zone++; + else if (temp_c < colder_t) + chip->pres_temp_zone--; + } + + if (prev_zone != chip->pres_temp_zone) { + mmi_pl_dbg(chip, PR_MOTO, + "Entered Temp Zone %d!\n", + chip->pres_temp_zone); + return true; + } + + return false; +} + +const unsigned char *pm_state_str[] = { + "PM_STATE_DISCONNECT", + "PM_STATE_ENTRY", + "PM_STATE_SW_ENTRY", + "PM_STATE_SW_LOOP", + "PM_STATE_FLASHC_ENTRY", + "PM_STATE_FLASHC_TUNNING_CURR", + "PM_STATE_FLASHC_TUNNING_VOLT", + "PM_STATE_FLASHC_CC_LOOP", + "PM_STATE_FLASHC_CV_LOOP", + "PM_STATE_FLASHC_QUIT_1", + "PM_STATE_FLASHC_QUIT_2", + "PM_STATE_STOP_CHARGE", +}; + +static int mmi_calculate_delta_volt(int pps_voltage, int pps_current, int delta_curr) +{ + u64 power; + int delta_volt; + u64 pps_volt; + u64 pps_curr; + + pps_volt = pps_voltage; + pps_curr = pps_current; + power = pps_volt * pps_curr; + + delta_volt = (int)(power / (pps_curr - delta_curr) - pps_volt); + delta_volt -= delta_volt % 20000; + + if (delta_volt > 0) + return delta_volt; + else + return 0; +} + +static void mmi_pl_pm_move_state(struct mmi_pl_chg_manager *chip, pm_sm_state_t state) +{ + mmi_pl_dbg(chip, PR_INTERRUPT, "pm_state change:%s -> %s\n", + pm_state_str[chip->sm_state], pm_state_str[state]); + chip->sm_state = state; +} + +static void mmi_set_pps_result_history(struct mmi_pl_chg_manager *chip, int pps_result) +{ + if (chip->pps_result_history_idx >= PPS_RET_HISTORY_SIZE -1) + chip->pps_result_history_idx = 0; + + if (pps_result < 0) + chip->pps_result_history[chip->pps_result_history_idx] = 1; + else + chip->pps_result_history[chip->pps_result_history_idx] = 0; + + chip->pps_result_history_idx++; +} + +static bool mmi_get_pps_result_history(struct mmi_pl_chg_manager *chip) +{ + int i = 0; + int result = 0; + for (i = 0; i < PPS_RET_HISTORY_SIZE; i++) + result += chip->pps_result_history[i]; + + if (result >= PPS_RET_HISTORY_SIZE) + return RESET_POWER; + else if (result >= PPS_RET_HISTORY_SIZE / 2) + return BLANCE_POWER; + else + return NO_ERROR; +} + +static void kick_sm(struct mmi_pl_chg_manager *chip, int ms) +{ + if (!delayed_work_pending(&chip->mmi_pl_sm_work)) { + + mmi_pl_dbg(chip, PR_INTERRUPT, + "lunch flash charge state machine\n"); + schedule_delayed_work(&chip->mmi_pl_sm_work, + msecs_to_jiffies(ms)); + } else + mmi_pl_dbg(chip, PR_INTERRUPT, + "flash charge state machine already existed\n"); +} + +static void cancel_sm(struct mmi_pl_chg_manager *chip) +{ + mmi_pl_dbg(chip, PR_INTERRUPT, "flush and cancel flash charge sm\n"); + flush_delayed_work(&chip->mmi_pl_sm_work); + cancel_delayed_work(&chip->mmi_pl_sm_work); +} + +static void clear_chg_manager(struct mmi_pl_chg_manager *chip) +{ + mmi_pl_dbg(chip, PR_INTERRUPT, "clear pl chg manager!\n"); + + if (chip->flashc_handle.charge_enabled) { + mmi_pl_err(chip, "disable flashc charge\n"); + mmi_pl_pm_flashc_enable(chip, false); + mmi_pl_pm_check_flashc_enable(chip); + } + + if (chip->pmic_ichg_limited) { + mmi_pl_dbg(chip, PR_MOTO, "recovery ichg lmt\n"); + mmi_pl_pm_pmic_ichg_lmt(chip, chip->pmic_ichg_val); + chip->pmic_ichg_limited = false; + } + + mmi_update_pmic_status(chip); + mmi_update_flashc_status(chip); + chip->sm_state = PM_STATE_DISCONNECT; + chip->request_volt = 0; + chip->request_current = 0; + chip->target_curr = 0; + chip->target_volt = 0; + chip->pps_current_max = 0; + chip->pps_voltage_max = 0; + chip->force_pmic_chg = false; + chip->enter_sw_loop = false; + memset(chip->mmi_pdo_info, 0, + sizeof(struct usbpd_pdo_info) * PD_MAX_PDO_NUM); +} + +#define FG_ESR_PULSE_MAX_TIMEOUT 60000 +#define FG_ESR_DECREMENT_UA 300000 +#define FG_ESR_DECREMENT_UV 300000 +#define FG_ESR_MIN_CURR_UA 1500000 +#define FLASHC_CV_DECREMENT_UV 100000 +#define HEARTBEAT_lOOP_WAIT_MS 5000 +#define HEARTBEAT_PPS_TUNNING_MS 50 +#define HEARTBEAT_SHORT_DELAY_MS 500 +#define HEARTBEAT_NEXT_STATE_MS 100 +#define HEARTBEAT_CANNEL -1 +#define FLASHC_TAPPER_COUNT 3 +#define PPS_SELECT_PDO_RETRY_COUNT 3 +#define FLASHC_ALARM_RECOVERY_COUNT 5 +#define FLASHC_FAULT_RECOVERY_COUNT 10 + +static void mmi_pl_sm_work_func(struct work_struct *work) +{ + struct mmi_pl_chg_manager *chip = container_of(work, + struct mmi_pl_chg_manager, mmi_pl_sm_work.work); + int rc = 0, i = 0; + int heartbeat_dely_ms = 0; + int request_volt = 0; + int request_curr = 0; + int ibat_curr = 0; + int vbat_volt = 0; + int bat_temp = 0; + int flashc_cc_max_curr = 0; + int flashc_cv_max_volt = 0; + int flashc_cv_taper_curr = 0; + int pluse_curr = 0; + int pluse_volt = 0; + int chrg_step_max = 0; + enum mmi_chrg_step chrg_step; + struct mmi_pl_temp_zone *zone; + bool zone_change = false; + bool flashc_fault_now = false; + + mmi_pl_dbg(chip, PR_MOTO, "schedule state matchine work, state : %s\n", + pm_state_str[chip->sm_state]); + if (!chip->usb_psy) + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) { + mmi_pl_err(chip, "USB psy not found\n"); + return; + } + + mmi_update_pmic_status(chip); + mmi_update_flashc_status(chip); + + if (chip->flashc_handle.bat_ocp_alarm + || chip->flashc_handle.bat_ovp_alarm + || chip->flashc_handle.bat_therm_alarm + || chip->flashc_handle.bus_ocp_alarm + || chip->flashc_handle.bus_ovp_alarm + || chip->flashc_handle.bus_therm_alarm + || chip->flashc_handle.die_therm_alarm) { + chip->request_volt -= chip->sys_configs.flashc_volt_up_steps; + chip->flashc_alarm = true; + chip->flashc_recovery_count = FLASHC_ALARM_RECOVERY_COUNT; + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + goto schedule; + } else if (chip->flashc_alarm) { + chip->flashc_recovery_count--; + + if (chip->flashc_recovery_count <= 0) { + chip->flashc_alarm = false; + chip->flashc_recovery_count = 0; + mmi_pl_pm_move_state(chip, PM_STATE_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + } + + if (chip->flashc_handle.bat_ocp_fault + || chip->flashc_handle.bat_ovp_fault + || chip->flashc_handle.bat_therm_fault + || chip->flashc_handle.bus_ocp_fault + || chip->flashc_handle.bus_ovp_fault + || chip->flashc_handle.bus_therm_fault + || chip->flashc_handle.die_therm_fault) { + chip->flashc_recovery_count = FLASHC_FAULT_RECOVERY_COUNT; + mmi_pl_pm_move_state(chip, PM_STATE_STOP_CHARGE); + chip->flashc_fault = true; + flashc_fault_now = true; + } + + ibat_curr = chip->pmic_handle.ibat_curr; + if (ibat_curr < 0) + ibat_curr *= -1; + + vbat_volt = chip->pmic_handle.vbat_volt; + bat_temp = chip->pmic_handle.bat_temp / 10; + + zone_change = mmi_find_temp_zone(chip, bat_temp); + zone = &chip->temp_zones[chip->pres_temp_zone]; + + mmi_pl_dbg(chip, PR_MOTO, "zone info: %d, temp %d, " + "super_uv %d, fcc super ua %d, " + "high_uv %d, fcc_high_uv %d, " + "norm_uv %d, fcc_norm_ua %d\n", + chip->pres_temp_zone, + zone->temp_c, zone->super_uv, + zone->fcc_super_ua, + zone->high_uv, zone->fcc_high_ua, + zone->norm_uv, zone->fcc_norm_ua); + + if (chip->pres_temp_zone == ZONE_HOT + || chip->pres_temp_zone == ZONE_COLD + || !chip->pmic_handle.charge_enabled) { + mmi_pl_pm_move_state(chip, PM_STATE_STOP_CHARGE); + } + + if (!chip->pmic_handle.vbus_pres) { + chip->sm_state = PM_STATE_DISCONNECT; + } else if (chip->sm_state == PM_STATE_DISCONNECT + || zone_change + || chip->sm_state == PM_STATE_ENTRY) { + mmi_pl_pm_get_usb_icl(chip); + mmi_pl_pm_move_state(chip, PM_STATE_ENTRY); + + if (vbat_volt > zone->high_uv && zone->norm_uv > 0) { + chip->pres_chrg_step = STEP_NORMAL; + chip->flashc_cc_max_curr_pre = zone->fcc_norm_ua; + chip->flashc_cv_taper_curr_pre = zone->fcc_norm_ua; + chip->flashc_cv_max_volt_pre = zone->norm_uv; + } else if (vbat_volt > zone->super_uv && zone->high_uv > 0) { + chip->pres_chrg_step = STEP_HIGH; + chip->flashc_cc_max_curr_pre = zone->fcc_high_ua; + chip->flashc_cv_max_volt_pre = zone->high_uv; + if (zone->fcc_norm_ua > 0) + chip->flashc_cv_taper_curr_pre = + zone->fcc_norm_ua; + else + chip->flashc_cv_taper_curr_pre = + zone->fcc_high_ua; + + } else { + chip->pres_chrg_step = STEP_SUPER; + chip->flashc_cc_max_curr_pre = zone->fcc_super_ua; + chip->flashc_cv_max_volt_pre = zone->super_uv; + if (zone->fcc_high_ua > 0) + chip->flashc_cv_taper_curr_pre = zone->fcc_high_ua; + else + chip->flashc_cv_taper_curr_pre = zone->fcc_super_ua; + } + + chip->flashc_cc_max_curr_pre = + min(chip->flashc_cc_max_curr_pre, chip->pps_current_max * 2); + } + + if (vbat_volt > zone->high_uv && zone->norm_uv > 0) { + chrg_step = STEP_NORMAL; + flashc_cc_max_curr = zone->fcc_norm_ua; + flashc_cv_max_volt = zone->norm_uv; + flashc_cv_taper_curr = zone->fcc_norm_ua; + } else if (vbat_volt > zone->super_uv && zone->high_uv > 0) { + chrg_step = STEP_HIGH; + flashc_cc_max_curr = zone->fcc_high_ua; + flashc_cv_max_volt = zone->high_uv; + if (zone->fcc_norm_ua > 0) + flashc_cv_taper_curr = zone->fcc_norm_ua; + else + flashc_cv_taper_curr = zone->fcc_high_ua; + } else { + chrg_step = STEP_SUPER; + flashc_cc_max_curr = zone->fcc_super_ua; + flashc_cv_max_volt = zone->super_uv; + if (zone->fcc_high_ua > 0) + flashc_cv_taper_curr = zone->fcc_high_ua; + else + flashc_cv_taper_curr = zone->fcc_super_ua; + } + + chrg_step_max = max(chrg_step,chip->pres_chrg_step); + if (flashc_cc_max_curr > + chip->pps_current_max * 2 - chip->sys_configs.pmic_curr_lp_lmt) + flashc_cc_max_curr = + chip->pps_current_max * 2 - chip->sys_configs.pmic_curr_lp_lmt; + + if (__param_flashc_cc_max_ua > 0) + chip->flashc_cc_max_curr_pre = __param_flashc_cc_max_ua; + + mmi_pl_dbg(chip, PR_MOTO, + " chrg_step %d, chip->pre_chrg_step %d, " + "chrg_step_max %d, " + "chip->flashc_cc_max_curr_pre %d, " + "chip->flashc_cv_max_volt_pre %d, " + "chip->flashc_cv_taper_curr_pre %d\n", + chrg_step, chip->pres_chrg_step, + chrg_step_max, + chip->flashc_cc_max_curr_pre, + chip->flashc_cv_max_volt_pre, + chip->flashc_cv_taper_curr_pre); + + switch (chip->sm_state) { + case PM_STATE_DISCONNECT: + mmi_pl_dbg(chip, PR_MOTO, + " PM_STATE_DiSCONNECT,flashc charge_enable %d, " + "pmic charge_enable %d, " + " pmic_ichg_limited %d \n", + chip->flashc_handle.charge_enabled, + chip->pmic_handle.charge_enabled, + chip->pmic_ichg_limited); + + if (chip->flashc_handle.charge_enabled) { + mmi_pl_pm_flashc_enable(chip, false); + mmi_pl_pm_check_flashc_enable(chip); + } + + if (chip->pmic_ichg_limited) { + rc = mmi_pl_pm_pmic_ichg_lmt(chip, chip->pmic_ichg_val); + mmi_pl_dbg(chip, PR_MOTO, "recovery ichg lmt, %d\n", + chip->pmic_ichg_val); + if (rc < 0) + mmi_pl_err(chip, + "Couldn't recovery usb current, rc=%d\n", rc); + + chip->pmic_ichg_limited = false; + } + heartbeat_dely_ms = HEARTBEAT_CANNEL; + break; + case PM_STATE_ENTRY: + if (!chip->pd_pps_support + || chip->pmic_handle.vbat_volt + < chip->sys_configs.flashc_min_vbat_start + || chip->flashc_cc_max_curr_pre + <= chip->pmic_step_curr) { + mmi_pl_dbg(chip, PR_MOTO, "start switch charge due to : " + "vbat %d, flashc_min_vbat %d, pps_support %d, " + "flashc_cc_max_curr %d\n", + chip->pmic_handle.vbat_volt, + chip->sys_configs.flashc_min_vbat_start, + chip->pd_pps_support, + chip->flashc_cc_max_curr_pre); + mmi_pl_pm_move_state(chip, PM_STATE_SW_ENTRY); + } else if (chip->flashc_cc_max_curr_pre > + chip->pmic_step_curr + && chip->pres_chrg_step < STEP_NORMAL + && !chip->force_pmic_chg) { + mmi_pl_dbg(chip, PR_MOTO, + "battery volt %d , flashc max curr %d," + "start flash charging\n", + vbat_volt, chip->flashc_cc_max_curr_pre); + mmi_pl_pm_move_state(chip, PM_STATE_FLASHC_ENTRY); + chip->flashc_taper_cnt = 0; + } else { + mmi_pl_dbg(chip, PR_MOTO, "zone change %d ,battery volt %d, " + "flashc max curr %d, max volt %d," + "start pmic charging\n", + zone_change, vbat_volt, + chip->flashc_cc_max_curr_pre, + chip->flashc_cv_max_volt_pre); + mmi_pl_pm_move_state(chip, PM_STATE_SW_ENTRY); + } + + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_SW_ENTRY: + if (chip->flashc_handle.charge_enabled) { + mmi_pl_pm_flashc_enable(chip, false); + mmi_pl_pm_check_flashc_enable(chip); + } + + if (chip->pmic_ichg_limited) { + rc = mmi_pl_pm_pmic_ichg_lmt(chip, chip->pmic_ichg_val); + if (rc < 0) + mmi_pl_err(chip, "Couldn't recovery usb current, rc=%d\n", rc); + chip->pmic_ichg_limited = false; + } + + mmi_pl_err(chip, "recovery and rerun usb AICL, rc=%d\n", rc); + mmi_pl_pm_pmic_enable(chip, false); + msleep(100); + mmi_pl_pm_pmic_enable(chip, true); + mmi_pl_pm_check_pmic_enable(chip); + + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + + if (chip->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_FIXED + && chip->mmi_pdo_info[i].uv_max >= SWITCH_CHARGER_PPS_VOLT + && chip->mmi_pdo_info[i].ua >= TYPEC_HIGH_CURRENT_UA) { + mmi_pl_dbg(chip, PR_MOTO, "select 5V/3A pps, pdo %d\n", i); + chip->mmi_pps_pdo_idx = + chip->mmi_pdo_info[i].pdo_pos; + break; + } + } + + chip->pps_current_max = TYPEC_HIGH_CURRENT_UA; + chip->pps_voltage_max = SWITCH_CHARGER_PPS_VOLT; + chip->request_current = chip->pps_current_max; + chip->request_volt = chip->pps_voltage_max; + + mmi_pl_dbg(chip, PR_MOTO, "select pdo %d, volt %d, curr %d\n", + chip->mmi_pps_pdo_idx, SWITCH_CHARGER_PPS_VOLT, + TYPEC_HIGH_CURRENT_UA); + mmi_pl_pm_move_state(chip, PM_STATE_SW_LOOP); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + break; + case PM_STATE_SW_LOOP: + if (chip->pd_pps_support + && (vbat_volt + > chip->sys_configs.flashc_min_vbat_start) + && chip->flashc_cc_max_curr_pre > + chip->pmic_step_curr + && chrg_step_max < STEP_NORMAL + && !chip->force_pmic_chg + && !chip->enter_sw_loop) { + + mmi_pl_dbg(chip, PR_MOTO, + "battery volt %d, start flash charge\n", + chip->pmic_handle.vbat_volt); + mmi_pl_pm_move_state(chip, PM_STATE_FLASHC_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + mmi_pl_dbg(chip, PR_MOTO, + "keep in sw charge, vbat %d ,ibat %d\n", + chip->pmic_handle.vbat_volt, + chip->pmic_handle.ibat_curr); + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + } + break; + case PM_STATE_FLASHC_ENTRY: + if (!chip->pmic_ichg_limited) { + mmi_pl_dbg(chip, PR_MOTO, + "flashc charge, limit pmic output curr %d\n", + chip->sys_configs.pmic_curr_lp_lmt); + rc = mmi_pl_pm_pmic_ichg_lmt(chip, + chip->sys_configs.pmic_curr_lp_lmt); + if (rc < 0) + mmi_pl_err(chip, + "Couldn't limit pmic ichg current, rc=%d\n", rc); + chip->pmic_ichg_limited = true; + } + + usbpd_get_pdo_info(chip->pd_handle, chip->mmi_pdo_info); + mmi_pl_dbg(chip, PR_MOTO, "check all effective pdo info\n"); + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + + if (chip->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_AUGMENTED + && chip->mmi_pdo_info[i].uv_max + >= FLASH_CHARGER_PPS_MIN_VOLT + && chip->mmi_pdo_info[i].ua + >= TYPEC_MIDDLE_CURRENT_UA) { + chip->mmi_pps_pdo_idx = + chip->mmi_pdo_info[i].pdo_pos; + mmi_pl_dbg(chip, PR_MOTO, + "pd charger support pps, pdo %d," + "volt %d, curr %d \n", + chip->mmi_pps_pdo_idx, + chip->mmi_pdo_info[i].uv_max, + chip->mmi_pdo_info[i].ua); + chip->pd_pps_support = true; + + if (chip->mmi_pdo_info[i].uv_max + > FLASH_CHARGER_PPS_MAX_VOLT) { + chip->pps_voltage_max + = FLASH_CHARGER_PPS_MAX_VOLT; + } else + chip->pps_voltage_max + = chip->mmi_pdo_info[i].uv_max; + + if (chip->mmi_pdo_info[i].ua + > TYPEC_HIGH_CURRENT_UA) { + chip->pps_current_max + = TYPEC_HIGH_CURRENT_UA; + } else + chip->pps_current_max + = chip->mmi_pdo_info[i].ua; + break; + } + } + + request_volt = (2 * vbat_volt) % 20000; + request_volt = 2 * vbat_volt - request_volt + + chip->sys_configs.flashc_volt_hysteresis; + request_volt = min(request_volt,FLASH_CHARGER_PPS_VOLT); + + if (chip->pps_current_max > TYPEC_MIDDLE_CURRENT_UA) + request_curr = TYPEC_MIDDLE_CURRENT_UA; + else + request_curr = chip->pps_current_max; + + chip->request_current = request_curr; + chip->request_volt = request_volt; + + mmi_pl_dbg(chip, PR_MOTO, "flashc charge ready 1 step," + "init pps output volt %d (2*vbat + %d uv)," + "output curr %d, pdo %d\n", + request_volt, + chip->sys_configs.flashc_volt_hysteresis, + request_curr, + chip->mmi_pps_pdo_idx); + if (!chip->flashc_handle.charge_enabled) { + mmi_pl_pm_flashc_enable(chip, true); + mmi_pl_pm_check_flashc_enable(chip); + } + + mmi_pl_pm_move_state(chip, PM_STATE_FLASHC_TUNNING_CURR); + heartbeat_dely_ms = HEARTBEAT_SHORT_DELAY_MS; + break; + case PM_STATE_FLASHC_TUNNING_CURR: + mmi_pl_dbg(chip, PR_MOTO, "flashc charge ready 2 step," + "increase pps current\n"); + if ((chip->request_current + + chip->sys_configs.flashc_curr_up_steps) + <= chip->pps_current_max + && ((chip->pmic_handle.vbat_volt < + chip->flashc_cv_max_volt_pre + && ibat_curr < chip->flashc_cc_max_curr_pre) + || chrg_step < STEP_HIGH)) { + chip->request_current += + chip->sys_configs.flashc_curr_up_steps; + mmi_pl_dbg(chip, PR_MOTO, "increase pps current %d\n", + chip->request_current); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + } else { + + chip->pps_increase_volt = true; + mmi_pl_pm_move_state(chip, + PM_STATE_FLASHC_TUNNING_VOLT); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + + if (chrg_step !=chip->pres_chrg_step) { + if (vbat_volt > zone->high_uv && zone->norm_uv > 0) { + chip->pres_chrg_step = STEP_NORMAL; + chip->flashc_cc_max_curr_pre = zone->fcc_norm_ua; + chip->flashc_cv_taper_curr_pre = zone->fcc_norm_ua; + chip->flashc_cv_max_volt_pre = zone->norm_uv; + } else if (vbat_volt > zone->super_uv && zone->high_uv > 0) { + chip->pres_chrg_step = STEP_HIGH; + chip->flashc_cc_max_curr_pre = zone->fcc_high_ua; + chip->flashc_cv_max_volt_pre = zone->high_uv; + if (zone->fcc_norm_ua > 0) + chip->flashc_cv_taper_curr_pre = + zone->fcc_norm_ua; + else + chip->flashc_cv_taper_curr_pre = + zone->fcc_high_ua; + + } else { + chip->pres_chrg_step = STEP_SUPER; + chip->flashc_cc_max_curr_pre = zone->fcc_super_ua; + chip->flashc_cv_max_volt_pre = zone->super_uv; + if (zone->fcc_high_ua > 0) + chip->flashc_cv_taper_curr_pre = zone->fcc_high_ua; + else + chip->flashc_cv_taper_curr_pre = zone->fcc_super_ua; + } + + chip->flashc_cc_max_curr_pre = + min(chip->flashc_cc_max_curr_pre, chip->pps_current_max * 2); + mmi_pl_dbg(chip, PR_INTERRUPT, + "update flashc parameters for float voltage\n"); + } + } + + if (!chip->flashc_handle.charge_enabled) { + mmi_pl_dbg(chip, PR_MOTO, + "flashc was disable,jump to SW change\n"); + mmi_pl_pm_move_state(chip, PM_STATE_SW_ENTRY); + } + break; + case PM_STATE_FLASHC_TUNNING_VOLT: + mmi_pl_dbg(chip, PR_MOTO, "flashc charge ready 3 step," + "increase pps voltage, effective flashc cc max curr %d," + "__param_flashc_cc_max_ua %d," + " allocated from pps max curr %d\n", + chip->flashc_cc_max_curr_pre, + __param_flashc_cc_max_ua, + chip->pps_current_max * 2 - chip->sys_configs.pmic_curr_lp_lmt); + + if (chip->pps_result < 0) { + mmi_pl_err(chip, "last select pdo failed\n"); + if (mmi_get_pps_result_history(chip) != NO_ERROR) { + mmi_pl_pm_move_state(chip, PM_STATE_FLASHC_CC_LOOP); + mmi_pl_err(chip, "direct access to cc loop," + "because of too many pdo failed\n"); + } + + chip->request_volt = chip->request_volt_pre; + goto schedule; + } + + if (chrg_step !=chip->pres_chrg_step) { + if (vbat_volt > zone->high_uv && zone->norm_uv > 0) { + chip->pres_chrg_step = STEP_NORMAL; + chip->flashc_cc_max_curr_pre = zone->fcc_norm_ua; + chip->flashc_cv_taper_curr_pre = zone->fcc_norm_ua; + chip->flashc_cv_max_volt_pre = zone->norm_uv; + } else if (vbat_volt > zone->super_uv && zone->high_uv > 0) { + chip->pres_chrg_step = STEP_HIGH; + chip->flashc_cc_max_curr_pre = zone->fcc_high_ua; + chip->flashc_cv_max_volt_pre = zone->high_uv; + if (zone->fcc_norm_ua > 0) + chip->flashc_cv_taper_curr_pre = + zone->fcc_norm_ua; + else + chip->flashc_cv_taper_curr_pre = + zone->fcc_high_ua; + + } else { + chip->pres_chrg_step = STEP_SUPER; + chip->flashc_cc_max_curr_pre = zone->fcc_super_ua; + chip->flashc_cv_max_volt_pre = zone->super_uv; + if (zone->fcc_high_ua > 0) + chip->flashc_cv_taper_curr_pre = zone->fcc_high_ua; + else + chip->flashc_cv_taper_curr_pre = zone->fcc_super_ua; + } + + mmi_pl_dbg(chip, PR_INTERRUPT, + "update flashc parameters for float voltage\n"); + } + + if (chip->pps_increase_volt + && ibat_curr < + ((chip->pres_chrg_step == STEP_SUPER) ? + chip->flashc_cc_max_curr_pre + + chip->sys_configs.flashc_curr_hysteresis : + chip->flashc_cc_max_curr_pre) + && chip->request_volt < + chip->pps_voltage_max + && chip->pmic_handle.vbat_volt < + chip->flashc_cv_max_volt_pre) { + + if ((chip->request_current + + chip->sys_configs.flashc_curr_up_steps) + < chip->pps_current_max) { + chip->request_current += + chip->sys_configs.flashc_curr_up_steps; + mmi_pl_dbg(chip, PR_MOTO, + "increase pps current %d\n", chip->request_current); + } else { + chip->request_volt += + chip->sys_configs.flashc_volt_up_steps; + mmi_pl_dbg(chip, PR_MOTO, + "increase pps voltage %d\n", chip->request_volt); + } + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + } else if (!chip->pps_increase_volt + && ibat_curr > chip->flashc_cc_max_curr_pre + && chip->request_volt > + SWITCH_CHARGER_PPS_VOLT) { + chip->request_volt -= + chip->sys_configs.flashc_volt_up_steps; + mmi_pl_dbg(chip, PR_MOTO, + "decrease pps voltage %d\n", chip->request_volt); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + } else { + mmi_pl_pm_move_state(chip, PM_STATE_FLASHC_CC_LOOP); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + + if (!chip->flashc_handle.charge_enabled) { + mmi_pl_dbg(chip, PR_MOTO, + "flashc was disable,access to SW change\n"); + mmi_pl_pm_move_state(chip, PM_STATE_SW_ENTRY); + } + break; + case PM_STATE_FLASHC_CC_LOOP: + mmi_pl_dbg(chip, PR_MOTO, "flashc charge CC loop\n"); + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + + if (chip->pps_result < 0) { + mmi_pl_err(chip, "last select pdo failed\n"); + chip->pps_result = mmi_get_pps_result_history(chip); + switch (chip->pps_result) { + case BLANCE_POWER: + chip->pps_power_balance = true; + mmi_pl_err(chip, "enable pps power blance\n"); + break; + case RESET_POWER: + mmi_pl_pm_move_state(chip, PM_STATE_ENTRY); + mmi_pl_err(chip, "hard reset charge policy to recovery power," + "too many pdo failed\n"); + break; + default: + break; + } + + chip->request_volt = chip->request_volt_pre; + if (ibat_curr < chip->flashc_cc_max_curr_pre + && chip->request_volt < chip->pps_voltage_max) + chip->flashc_cc_tunning_cnt = + PPS_SELECT_PDO_RETRY_COUNT; + goto schedule; + } + + if (ibat_curr < chip->flashc_cc_max_curr_pre + && chip->request_volt < chip->pps_voltage_max + && chip->flashc_cc_tunning_cnt >= + PPS_SELECT_PDO_RETRY_COUNT + && chip->thermal_mitigation_level == 0) { + if (chip->pps_power_balance) { + chip->request_current -= + chip->sys_configs.flashc_curr_down_steps; + chip->request_volt += + mmi_calculate_delta_volt(chip->request_volt_pre, + chip->request_curr_pre, + chip->sys_configs.flashc_curr_down_steps); + mmi_pl_dbg(chip, PR_MOTO, "request_curr decrease to %dmA, " + "request_volt increase to %d\n", + chip->request_current, + chip->request_volt); + } else { + if ((chip->request_current + + chip->sys_configs.flashc_curr_up_steps) + < chip->pps_current_max) { + chip->request_current += + chip->sys_configs.flashc_curr_up_steps; + mmi_pl_dbg(chip, PR_MOTO, + "increase pps current %d\n", chip->request_current); + } else { + chip->request_volt += chip->sys_configs.flashc_volt_up_steps; + mmi_pl_dbg(chip, PR_MOTO, "request_volt increase %dmv\n", + chip->sys_configs.flashc_volt_up_steps); + } + } + + chip->flashc_cc_tunning_cnt = 0; + mmi_pl_dbg(chip, PR_MOTO, "need increase pps voltage," + "or decrease pps current" + "to keep CC charger power," + "request volt %d, " + "request curr %d, " + "thermal level %d \n", + chip->request_volt, + chip->request_current,chip->thermal_mitigation_level); + } else if (ibat_curr < + chip->flashc_cc_max_curr_pre + && chip->request_volt < chip->pps_voltage_max) { + chip->flashc_cc_tunning_cnt++; + mmi_pl_dbg(chip, PR_MOTO, + "flashc charge CC tunning cnt++ : %d \n", + chip->flashc_cc_tunning_cnt); + } else + chip->flashc_cc_tunning_cnt = 0; + + if (chip->pmic_handle.vbat_volt + > chip->flashc_cv_max_volt_pre) { + if (chip->flashc_taper_cnt > FLASHC_TAPPER_COUNT) { + mmi_pl_pm_move_state(chip, PM_STATE_FLASHC_CV_LOOP); + chip->flashc_taper_cnt = 0; + chip->flashc_taper_delta_volt = + FLASHC_CV_DECREMENT_UV; + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } else { + chip->flashc_taper_cnt++; + mmi_pl_dbg(chip, PR_MOTO, + "flashc CC charge taper_cnt %d, " + "flashc_cv_max_volt - 20mv %d\n", + chip->flashc_taper_cnt, + chip->flashc_cv_max_volt_pre - 20000); + } + } else + chip->flashc_taper_cnt = 0; + + if (!chip->flashc_handle.charge_enabled) { + mmi_pl_dbg(chip, PR_MOTO, + "flashc was disable,access to SW change\n"); + mmi_pl_pm_move_state(chip, PM_STATE_SW_ENTRY); + } + break; + case PM_STATE_FLASHC_CV_LOOP: + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + mmi_pl_dbg(chip, PR_MOTO, "flashc charge CV loop," + "chrg_step %d , pre_chrg_step %d \n", + chrg_step, chip->pres_chrg_step); + + if (chip->pps_result < 0) { + mmi_pl_err(chip, "last select pdo failed\n"); + chip->pps_result = mmi_get_pps_result_history(chip); + switch (chip->pps_result) { + case BLANCE_POWER: + chip->request_current -= + chip->sys_configs.flashc_curr_down_steps; + mmi_pl_err(chip, "decrease pps curr %d uA for balnce power\n", + chip->sys_configs.flashc_curr_down_steps); + break; + case RESET_POWER: + mmi_pl_pm_move_state(chip, PM_STATE_ENTRY); + mmi_pl_err(chip, "hard reset charge policy to recovery power," + "too many pdo failed\n"); + break; + default: + break; + } + + chip->request_volt = chip->request_volt_pre; + goto schedule; + } + + if (vbat_volt > chip->flashc_cv_max_volt_pre + 20000) { + if (chip->flashc_taper_delta_volt > 20000) + chip->request_volt -= chip->flashc_taper_delta_volt; + else chip->request_volt -= 20000; + mmi_pl_dbg(chip, PR_MOTO, "For keeping flashc constant voltage step," + "decrease pps voltage %d ,delta volta %d \n", + chip->request_volt, chip->flashc_taper_delta_volt); + } else if (chip->pmic_handle.vbat_volt + < chip->flashc_cv_max_volt_pre - 20000) { + chip->flashc_taper_delta_volt -= 20000; + chip->request_volt += 20000; + mmi_pl_dbg(chip, PR_MOTO, "For keeping flashc constant voltage step," + "increase pps voltage %d , delta volta %d \n", + chip->request_volt, chip->flashc_taper_delta_volt); + } else { + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + mmi_pl_dbg(chip, PR_MOTO, "Flashc CV loop work well," + "go on keeping pps voltage %d ,pps current %d\n", + chip->request_volt, chip->request_current); + } + + if (ibat_curr <= chip->flashc_cv_taper_curr_pre || + ibat_curr < chip->pmic_step_curr) { + if (chip->flashc_taper_cnt >= FLASHC_TAPPER_COUNT) { + if (ibat_curr < chip->pmic_step_curr) { + mmi_pl_dbg(chip, PR_MOTO, + "Ready to quit flashc CV charge, " + "chrg step %d ," + "battery curr %d, " + "flashc max curr %d, " + "flashc max cv %d, " + "flashc cv taper curr %d, " + "pmic step volt %d, " + "pimc step curr %d\n", + chrg_step, ibat_curr, + chip->flashc_cc_max_curr_pre, + chip->flashc_cv_max_volt_pre, + chip->flashc_cv_taper_curr_pre, + chip->pmic_step_volt, + chip->pmic_step_curr); + mmi_pl_pm_move_state(chip, PM_STATE_FLASHC_QUIT_1); + } else if (chrg_step != chip->pres_chrg_step + && flashc_cc_max_curr < chip->flashc_cc_max_curr_pre) { + chip->pres_chrg_step = chrg_step; + chip->flashc_cc_max_curr_pre = flashc_cc_max_curr; + chip->flashc_cv_max_volt_pre = flashc_cv_max_volt; + chip->flashc_cv_taper_curr_pre = flashc_cv_taper_curr; + + mmi_pl_dbg(chip, PR_MOTO, + "Start jump to the next charge step %d \n", + chip->pres_chrg_step); + + mmi_pl_pm_move_state(chip, PM_STATE_FLASHC_CC_LOOP); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + chip->flashc_taper_cnt = 0; + } else { + chip->flashc_taper_cnt++; + mmi_pl_dbg(chip, PR_MOTO, + "Flashc CV taper cnt ++,%d \n", chip->flashc_taper_cnt); + } + } else + chip->flashc_taper_cnt = 0; + + if (!chip->flashc_handle.charge_enabled) { + mmi_pl_dbg(chip, PR_MOTO, + "flashc was disable,jump to SW change\n"); + mmi_pl_pm_move_state(chip, PM_STATE_SW_ENTRY); + } + break; + case PM_STATE_FLASHC_QUIT_1: + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + if (chip->flashc_handle.charge_enabled) { + mmi_pl_pm_flashc_enable(chip, false); + mmi_pl_pm_check_flashc_enable(chip); + } + + if (chip->pmic_ichg_limited) { + rc = mmi_pl_pm_pmic_ichg_lmt(chip, chip->pmic_ichg_val); + if (rc < 0) + mmi_pl_err(chip, "Couldn't recovery ichg, rc=%d\n", rc); + chip->pmic_ichg_limited = false; + } + + mmi_pl_pm_move_state(chip, PM_STATE_FLASHC_QUIT_2); + break; + case PM_STATE_FLASHC_QUIT_2: + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + if (chip->request_volt > SWITCH_CHARGER_PPS_VOLT) { + chip->request_volt -= + chip->sys_configs.flashc_volt_down_steps; + chip->request_current = TYPEC_HIGH_CURRENT_UA; + mmi_pl_dbg(chip, PR_MOTO, + "decrease pps voltage %d\n", chip->request_volt); + heartbeat_dely_ms = HEARTBEAT_PPS_TUNNING_MS; + } else { + mmi_pl_pm_pmic_enable(chip, false); + msleep(100); + mmi_pl_pm_pmic_enable(chip, true); + mmi_pl_pm_check_pmic_enable(chip); + mmi_pl_pm_move_state(chip, PM_STATE_SW_LOOP); + chip->enter_sw_loop = true; + mmi_pl_dbg(chip, PR_MOTO, + "enter pmic charge loop\n"); + } + + break; + case PM_STATE_STOP_CHARGE: + heartbeat_dely_ms = HEARTBEAT_lOOP_WAIT_MS; + if (chip->flashc_handle.charge_enabled) { + mmi_pl_pm_flashc_enable(chip, false); + mmi_pl_pm_check_flashc_enable(chip); + } + + if (chip->pmic_ichg_limited) { + rc = mmi_pl_pm_pmic_ichg_lmt(chip, chip->pmic_ichg_val); + if (rc < 0) + mmi_pl_err(chip, "Couldn't recovery ichg, rc=%d\n", rc); + chip->pmic_ichg_limited = false; + } + + if (chip->flashc_recovery_count > 0 + && chip->flashc_fault + && !flashc_fault_now){ + chip->flashc_recovery_count-- ; + } else if (chip->flashc_recovery_count <= 0 + && chip->flashc_fault + && !flashc_fault_now) { + mmi_pl_pm_move_state(chip, PM_STATE_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + chip->flashc_fault = false; + } else if (chip->pres_temp_zone != ZONE_HOT + && chip->pres_temp_zone != ZONE_COLD + && !flashc_fault_now + && chip->pmic_handle.charge_enabled) { + mmi_pl_pm_move_state(chip, PM_STATE_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + } + + chip->request_current = TYPEC_HIGH_CURRENT_UA; + chip->request_volt = SWITCH_CHARGER_PPS_VOLT; + break; + } + +schedule: + + if(vbat_volt > chip->sys_configs.batt_ovp_lmt) { + chip->request_volt -= chip->sys_configs.flashc_volt_down_steps; + mmi_pl_dbg(chip, PR_MOTO, "trigger batt OVP, reduce volt %d, " + "request volt %d\n", + chip->sys_configs.flashc_volt_down_steps, + chip->request_volt); + } + chip->target_volt = min(chip->request_volt, chip->pps_voltage_max); + chip->target_curr = min(chip->request_current, chip->pps_current_max); + + if (chip->thermal_mitigation_level > 0 + && chip->thermal_mitigation_level < chip->num_thermal_zones - 1 + && (chip->sm_state == PM_STATE_FLASHC_CC_LOOP + || chip->sm_state == PM_STATE_FLASHC_CV_LOOP + || chip->sm_state == PM_STATE_SW_LOOP)) { + chip->target_volt = min(chip->target_volt, + chip->thermal_mitigation_zones[chip->thermal_mitigation_level].power_voltage); + chip->target_curr = min(chip->target_curr, + chip->thermal_mitigation_zones[chip->thermal_mitigation_level].power_current); + + mmi_pl_dbg(chip, PR_MOTO, "thermal level %d, " + "thermal_voltage %d, thermal_current %d\n", + chip->thermal_mitigation_level, + chip->thermal_mitigation_zones[chip->thermal_mitigation_level].power_voltage, + chip->thermal_mitigation_zones[chip->thermal_mitigation_level].power_current); + + if (chip->force_pmic_chg) { + chip->force_pmic_chg = false; + mmi_pl_pm_move_state(chip, PM_STATE_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + mmi_pl_dbg(chip, PR_MOTO, "thermal level is %d ," + "cannel force pmic chg, recovery parallel chg !\n", + chip->thermal_mitigation_level); + } + } else if (chip->thermal_mitigation_level == chip->num_thermal_zones -1 + && !chip->force_pmic_chg) { + chip->force_pmic_chg = true; + mmi_pl_pm_move_state(chip, PM_STATE_SW_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + mmi_pl_dbg(chip, PR_MOTO, "thermal level is %d ," + "force to only pmic chg !\n", + chip->thermal_mitigation_level); + } else if (chip->thermal_mitigation_level == 0 + && chip->force_pmic_chg) { + chip->force_pmic_chg = false; + mmi_pl_pm_move_state(chip, PM_STATE_ENTRY); + heartbeat_dely_ms = HEARTBEAT_NEXT_STATE_MS; + mmi_pl_dbg(chip, PR_MOTO, "thermal level is %d ," + "cannel force pmic chg, recovery parallel chg !\n", + chip->thermal_mitigation_level); + } + + mmi_pl_dbg(chip, PR_INTERRUPT, "sm work step %s,select power: " + "target voltage %dmV, target current %dmA," + "request voltage %dmV, request current %dmA," + "thermal level %d\n", + pm_state_str[chip->sm_state],chip->target_volt, + chip->target_curr, + chip->request_volt,chip->request_current, + chip->thermal_mitigation_level); + + chip->pps_result = usbpd_select_pdo(chip->pd_handle, + chip->mmi_pps_pdo_idx, + chip->target_volt, chip->target_curr); + mmi_set_pps_result_history(chip, chip->pps_result); + + if (chip->thermal_mitigation_level == 0 + && !chip->pps_result) { + chip->request_volt_pre = chip->request_volt; + chip->request_curr_pre = chip->request_current; + } + + if (chip->flashc_handle.charge_enabled + && chip->pres_chrg_step < STEP_NORMAL + && (chip->sm_state == PM_STATE_FLASHC_CC_LOOP + || chip->sm_state == PM_STATE_FLASHC_CV_LOOP) + && chip->flashc_handle.ibus_curr > FG_ESR_MIN_CURR_UA) { + if (chip->fg_esr_pulse_timeout >= + FG_ESR_PULSE_MAX_TIMEOUT) { + pluse_curr = chip->flashc_handle.ibus_curr % 50000; + pluse_curr = chip->flashc_handle.ibus_curr - pluse_curr; + + + pluse_volt = chip->flashc_handle.vbus_volt % 20000; + pluse_volt = chip->flashc_handle.vbus_volt - pluse_volt; + + if (chip->sm_state == PM_STATE_FLASHC_CV_LOOP) { + + usbpd_select_pdo(chip->pd_handle, + chip->mmi_pps_pdo_idx, + pluse_volt - FG_ESR_DECREMENT_UV, + pluse_curr - FG_ESR_DECREMENT_UA); + mmi_pl_dbg(chip, PR_MOTO, "kick a ESR pulse, " + "pps vbus volt %d " + "pps ibus curr %d\n", + pluse_volt - FG_ESR_DECREMENT_UV, + pluse_curr - FG_ESR_DECREMENT_UA); + + } else { + usbpd_select_pdo(chip->pd_handle, + chip->mmi_pps_pdo_idx, + chip->target_volt, + pluse_curr - FG_ESR_DECREMENT_UA); + mmi_pl_dbg(chip, PR_MOTO, "kick a ESR pulse, " + "pps ibus curr %d\n", + pluse_curr - FG_ESR_DECREMENT_UA); + } + + msleep(5000); + usbpd_select_pdo(chip->pd_handle, + chip->mmi_pps_pdo_idx, + chip->target_volt, + chip->target_curr); + mmi_pl_dbg(chip, PR_MOTO, "recovery pps curr %d\n", + chip->target_curr); + chip->fg_esr_pulse_timeout = 0; + } + chip->fg_esr_pulse_timeout += + heartbeat_dely_ms; + + if(chip->sm_state == PM_STATE_ENTRY) { + usbpd_select_pdo(chip->pd_handle, + chip->mmi_pps_pdo_idx, + SWITCH_CHARGER_PPS_VOLT, + TYPEC_HIGH_CURRENT_UA); + msleep(1000); + mmi_pl_dbg(chip, PR_MOTO, + "reset 5V/3A,clear up float voltage\n"); + } + + } + + if (heartbeat_dely_ms >= 0) { + if (chip->pps_result < 0) + heartbeat_dely_ms = 0; + schedule_delayed_work(&chip->mmi_pl_sm_work, + msecs_to_jiffies(heartbeat_dely_ms)); + } else { + clear_chg_manager(chip); + chip->pd_pps_support = false; + } + + return; +} + +static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr) +{ + struct mmi_pl_chg_manager *chip = container_of(nb, struct mmi_pl_chg_manager, psy_nb); + + if (!chip->usb_psy) { + mmi_pl_dbg(chip, PR_MOTO, "usb psy is NULL, direct return\n"); + return NOTIFY_OK; + } + + if (ptr == chip->usb_psy && evt == PSY_EVENT_PROP_CHANGED) + schedule_work(&chip->psy_changed_work); + + return NOTIFY_OK; +} + +static void psy_changed_work_func(struct work_struct *work) +{ + struct mmi_pl_chg_manager *chip = container_of(work, + struct mmi_pl_chg_manager, psy_changed_work); + union power_supply_propval val; + bool pd_active; + int ret, i; + + mmi_pl_dbg(chip, PR_MOTO, "kick psy changed work\n"); + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) { + mmi_pl_err(chip, + "Could not get USB power_supply, deferring probe\n"); + return; + } + } + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &val); + if (ret) { + mmi_pl_err(chip, "Unable to read USB PRESENT: %d\n", ret); + return; + } + chip->vbus_present = val.intval; + + ret = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PD_ACTIVE, &val); + if (ret) { + mmi_pl_err(chip, "Unable to read PD ACTIVE: %d\n", ret); + return; + } + pd_active = val.intval; + + if (!chip->pd_handle) { + chip->pd_handle = devm_usbpd_get_by_phandle(chip->dev, + "qcom,usbpd-phandle"); + if (IS_ERR_OR_NULL(chip->pd_handle)) { + mmi_pl_err(chip, "Error getting the pd phandle %ld\n", + PTR_ERR(chip->pd_handle)); + chip->pd_handle = NULL; + return; + } + } + + if (pd_active && chip->vbus_present) { + usbpd_get_pdo_info(chip->pd_handle, chip->mmi_pdo_info); + mmi_pl_dbg(chip, PR_MOTO, "check all effective pdo info\n"); + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + if ((chip->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_AUGMENTED) + && chip->mmi_pdo_info[i].uv_max >= FLASH_CHARGER_PPS_MIN_VOLT + && chip->mmi_pdo_info[i].ua >= TYPEC_MIDDLE_CURRENT_UA) { + chip->mmi_pps_pdo_idx = chip->mmi_pdo_info[i].pdo_pos; + mmi_pl_dbg(chip, PR_INTERRUPT, + "pd charger support pps, pdo %d, " + "volt %d, curr %d \n", + chip->mmi_pps_pdo_idx, + chip->mmi_pdo_info[i].uv_max, + chip->mmi_pdo_info[i].ua); + chip->pd_pps_support = true; + + if (chip->mmi_pdo_info[i].uv_max > + FLASH_CHARGER_PPS_MAX_VOLT) { + chip->pps_voltage_max = FLASH_CHARGER_PPS_MAX_VOLT; + } else + chip->pps_voltage_max = + chip->mmi_pdo_info[i].uv_max; + + if (chip->mmi_pdo_info[i].ua > + TYPEC_HIGH_CURRENT_UA) { + chip->pps_current_max = + TYPEC_HIGH_CURRENT_UA; + } else + chip->pps_current_max = + chip->mmi_pdo_info[i].ua; + break; + } + } + } + + mmi_pl_dbg(chip, PR_INTERRUPT, "vbus present %d, pd pps support %d, " + "pps max voltage %d, pps max curr %d\n", + chip->vbus_present, + chip->pd_pps_support, + chip->pps_voltage_max, + chip->pps_current_max); + + mmi_pl_pm_check_flashc_enable(chip); + mmi_pl_pm_check_pmic_enable(chip); + mmi_pl_dbg(chip, PR_INTERRUPT, "flashc enable %d, pmic enable %d\n", + chip->flashc_handle.charge_enabled, + chip->pmic_handle.charge_enabled); + + if (chip->vbus_present && chip->pd_pps_support) { + kick_sm(chip, 100); + } else { + cancel_sm(chip); + clear_chg_manager(chip); + chip->pd_pps_support = false; + } + return; +} + +#define DEFAULT_FLASHC_BATT_VOLT_LMT 4200000 +#define DEFAULT_FLASHC_BATT_CURR_LP_LMT 5500000 +#define DEFAULT_BATT_OVP_LMT 4475000 +#define DEFAULT_PMIC_CURR_LP_LMT 500000 +#define DEFAULT_PMIC_STEP_VOLT 4400000 +#define DEFAULT_PMIC_STEP_CURR 2000000 +#define DEFAULT_FLASHC_TAPER_CURR_LMT 2000000 +#define DEFAULT_FLASHC_VOLT_HYSTERESIS 100000 +#define DEFAULT_FLASHC_CURR_HYSTERESIS 300000 +#define DEFAULT_FLASHC_VOLT_UP_STEPS 100000 +#define DEFAULT_FLASHC_CURR_UP_STEPS 100000 +#define DEFAULT_FLASHC_VOLT_DOWN_STEPS 100000 +#define DEFAULT_FLASHC_CURR_DOWN_STEPS 250000 +#define DEFAULT_FLASHC_MIN_VBAT_START 3600000 + +static int mmi_pl_chg_manager_parse_dt(struct mmi_pl_chg_manager *chip) +{ + struct device_node *node = chip->dev->of_node; + int rc; + int byte_len, i; + + rc = of_property_read_u32(node, + "mmi,batt-ovp-lmt", + &chip->sys_configs.batt_ovp_lmt); + if (rc < 0) + chip->sys_configs.batt_ovp_lmt = + DEFAULT_BATT_OVP_LMT; + + rc = of_property_read_u32(node, + "mmi,pmic-curr-lp-lmt", + &chip->sys_configs.pmic_curr_lp_lmt); + if (rc < 0) + chip->sys_configs.pmic_curr_lp_lmt = + DEFAULT_PMIC_CURR_LP_LMT; + + rc = of_property_read_u32(node, + "mmi,pmic-step-volt", + &chip->pmic_step_volt); + if (rc < 0) + chip->pmic_step_volt = + DEFAULT_PMIC_STEP_VOLT; + + rc = of_property_read_u32(node, + "mmi,pmic-step-curr", + &chip->pmic_step_curr); + if (rc < 0) + chip->pmic_step_curr = + DEFAULT_PMIC_STEP_CURR; + + rc = of_property_read_u32(node, + "mmi,flashc-volt-hysteresis", + &chip->sys_configs.flashc_volt_hysteresis); + if (rc < 0) + chip->sys_configs.flashc_volt_hysteresis = + DEFAULT_FLASHC_VOLT_HYSTERESIS; + + rc = of_property_read_u32(node, + "mmi,flashc-curr-hysteresis", + &chip->sys_configs.flashc_curr_hysteresis); + if (rc < 0) + chip->sys_configs.flashc_curr_hysteresis = + DEFAULT_FLASHC_CURR_HYSTERESIS; + + rc = of_property_read_u32(node, + "mmi,flashc-volt-up-steps", + &chip->sys_configs.flashc_volt_up_steps); + if (rc < 0) + chip->sys_configs.flashc_volt_up_steps = + DEFAULT_FLASHC_VOLT_UP_STEPS; + + rc = of_property_read_u32(node, + "mmi,flashc-curr-up-steps", + &chip->sys_configs.flashc_curr_up_steps); + if (rc < 0) + chip->sys_configs.flashc_curr_up_steps = + DEFAULT_FLASHC_CURR_UP_STEPS; + + rc = of_property_read_u32(node, + "mmi,flashc-volt-down-steps", + &chip->sys_configs.flashc_volt_down_steps); + if (rc < 0) + chip->sys_configs.flashc_volt_down_steps = + DEFAULT_FLASHC_VOLT_DOWN_STEPS; + + rc = of_property_read_u32(node, + "mmi,flashc-curr-down-steps", + &chip->sys_configs.flashc_curr_down_steps); + if (rc < 0) + chip->sys_configs.flashc_curr_down_steps = + DEFAULT_FLASHC_CURR_DOWN_STEPS; + + rc = of_property_read_u32(node, + "mmi,flashc-min-vbat-start", + &chip->sys_configs.flashc_min_vbat_start); + if (rc < 0) + chip->sys_configs.flashc_min_vbat_start = + DEFAULT_FLASHC_MIN_VBAT_START; + + byte_len = of_property_count_strings(node, "flashc-name"); + if (byte_len < 0) { + mmi_pl_err(chip, "Cannot parse flashc-name: %d\n", + byte_len); + return byte_len; + } + + for (i = 0; i < byte_len; i++) { + rc = of_property_read_string_index(node, "flashc-name", + i, &chip->flashc_name); + } + + if (of_find_property(node, "mmi,mmi-pl-temp-zones", &byte_len)) { + if ((byte_len / sizeof(u32)) % 7) { + mmi_pl_err(chip, + "DT error wrong mmi temp zones 7\n"); + return -ENODEV; + } + + chip->temp_zones = (struct mmi_pl_temp_zone *) + devm_kzalloc(chip->dev, byte_len, GFP_KERNEL); + + if (chip->temp_zones == NULL) + return -ENOMEM; + + chip->num_temp_zones = + byte_len / sizeof(struct mmi_pl_temp_zone); + + rc = of_property_read_u32_array(node, + "mmi,mmi-pl-temp-zones", + (u32 *)chip->temp_zones, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_pl_err(chip, + "Couldn't read mmi temp zones rc = %d\n", + rc); + return rc; + } + mmi_pl_dbg(chip, PR_MOTO, "mmi temp zones: Num: %d\n", + chip->num_temp_zones); + for (i = 0; i < chip->num_temp_zones; i++) { + chip->temp_zones[i].super_uv *= 1000; + chip->temp_zones[i].fcc_super_ua *= 1000; + chip->temp_zones[i].high_uv *= 1000; + chip->temp_zones[i].fcc_high_ua *= 1000; + chip->temp_zones[i].norm_uv *= 1000; + chip->temp_zones[i].fcc_norm_ua *= 1000; + + mmi_pl_dbg(chip, PR_MOTO, + "mmi temp zones: Zone %d, Temp %d C, " \ + "super_mv %d, fcc super ma %d, " \ + "high_mv %d, fcc_high_mv %d, " \ + "norm_mv %d, fcc_norm_ma %d\n", i, + chip->temp_zones[i].temp_c, + chip->temp_zones[i].super_uv, + chip->temp_zones[i].fcc_super_ua, + chip->temp_zones[i].high_uv, + chip->temp_zones[i].fcc_high_ua, + chip->temp_zones[i].norm_uv, + chip->temp_zones[i].fcc_norm_ua); + } + chip->pres_temp_zone = ZONE_NONE; + } + + if (of_find_property(node, "mmi,mmi-thermal-zones", &byte_len)) { + if ((byte_len / sizeof(u32)) % 2) { + mmi_pl_err(chip, + "DT error wrong mmi thermal zones 2\n"); + return -ENODEV; + } + + chip->thermal_mitigation_zones = (struct mmi_thermal_power_zone *) + devm_kzalloc(chip->dev, byte_len, GFP_KERNEL); + + if (chip->thermal_mitigation_zones == NULL) + return -ENOMEM; + + chip->num_thermal_zones = + byte_len / sizeof(struct mmi_thermal_power_zone); + + rc = of_property_read_u32_array(node, + "mmi,mmi-thermal-zones", + (u32 *)chip->thermal_mitigation_zones, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_pl_err(chip, + "Couldn't read mmi thermal zones rc = %d\n", + rc); + return rc; + } + mmi_pl_dbg(chip, PR_MOTO, "mmi thermal zones: Num: %d\n", + chip->num_thermal_zones); + for (i = 0; i < chip->num_thermal_zones; i++) { + + mmi_pl_dbg(chip, PR_MOTO, + "mmi thermal mitigation zones: Zone %d, " \ + "power_current %d, " \ + "power_voltage %d\n", i, + chip->thermal_mitigation_zones[i].power_current, + chip->thermal_mitigation_zones[i].power_voltage); + } + chip->thermal_mitigation_level = 0; + } + + return rc; +} + +static ssize_t flashc_vbat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int rc; + union power_supply_propval prop = {0,}; + struct mmi_pl_chg_manager *chip = dev_get_drvdata(dev); + + chip->flashc_psy = power_supply_get_by_name(chip->flashc_name); + if (!chip->flashc_psy) + return rc; + + rc = power_supply_get_property(chip->flashc_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &prop); + if (rc) { + power_supply_put(chip->flashc_psy); + return rc; + } + + power_supply_put(chip->flashc_psy); + + return scnprintf(buf, FLASHC_SHOW_MAX_SIZE, "%d\n", prop.intval*1000); +} +static DEVICE_ATTR(flashc_vbat, S_IRUGO, flashc_vbat_show, NULL); + +static ssize_t factory_flashc_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + struct mmi_pl_chg_manager *chip = dev_get_drvdata(dev); + + if (!chip) { + pr_err("chip not valid\n"); + return -ENODEV; + } + + mmi_pl_pm_check_flashc_enable(chip); + + state = (chip->flashc_handle.charge_enabled) ? 1 : 0; + return scnprintf(buf, FLASHC_SHOW_MAX_SIZE, "%d\n", state); +} + +static ssize_t factory_flashc_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long enable; + struct mmi_pl_chg_manager *chip = dev_get_drvdata(dev); + + r = kstrtoul(buf, 0, &enable); + if (r) { + mmi_pl_err(chip, "Invalid enable value = %ld\n", enable); + return -EINVAL; + } + + mmi_pl_pm_flashc_enable(chip, enable); + mmi_pl_pm_check_flashc_enable(chip); + + return r ? r : count; +} + +static DEVICE_ATTR(factory_flashc_enable, + 0664, factory_flashc_enable_show, factory_flashc_enable_store); + +static ssize_t factory_pmic_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + struct mmi_pl_chg_manager *chip = dev_get_drvdata(dev); + + if (!chip) { + pr_err("chip not valid\n"); + return -ENODEV; + } + + mmi_pl_pm_check_pmic_enable(chip); + + state = (chip->pmic_handle.charge_enabled) ? 1 : 0; + return scnprintf(buf, FLASHC_SHOW_MAX_SIZE, "%d\n", state); +} + +static ssize_t factory_pmic_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long enable; + struct mmi_pl_chg_manager *chip = dev_get_drvdata(dev); + + r = kstrtoul(buf, 0, &enable); + if (r) { + mmi_pl_err(chip, "Invalid enable value = %ld\n", enable); + return -EINVAL; + } + + mmi_pl_pm_pmic_enable(chip, enable); + mmi_pl_pm_check_pmic_enable(chip); + + return r ? r : count; +} + +static DEVICE_ATTR(factory_pmic_enable, + 0664, factory_pmic_enable_show, factory_pmic_enable_store); + +static void create_sysfs_entries(struct mmi_pl_chg_manager *chip) +{ + int rc; + rc = device_create_file(chip->dev, &dev_attr_flashc_vbat); + if (rc) + mmi_pl_err(chip, "Couldn't create flashc_vbat\n"); + + rc = device_create_file(chip->dev, &dev_attr_factory_flashc_enable); + if (rc) + mmi_pl_err(chip, "Couldn't create factory_flashc_enable\n"); + + rc = device_create_file(chip->dev, &dev_attr_factory_pmic_enable); + if (rc) + mmi_pl_err(chip, "Couldn't create factory_pmic_enable\n"); +} + +static void remove_sysfs_entries(struct mmi_pl_chg_manager *chip) +{ + device_remove_file(chip->dev, &dev_attr_flashc_vbat); + device_remove_file(chip->dev, &dev_attr_factory_flashc_enable); + device_remove_file(chip->dev, &dev_attr_factory_pmic_enable); +} + +static enum power_supply_property mmi_pl_pm_props[] = { + POWER_SUPPLY_PROP_PD_CURRENT_MAX, + POWER_SUPPLY_PROP_PD_VOLTAGE_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_PARALLEL_DISABLE, + POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, + POWER_SUPPLY_PROP_NUM_SYSTEM_TEMP_LEVELS, +}; + +static int mmi_pl_pm_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct mmi_pl_chg_manager *chip = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_PD_CURRENT_MAX: + val->intval = chip->request_current; + break; + case POWER_SUPPLY_PROP_PD_VOLTAGE_MAX: + val->intval = chip->request_volt; + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED: + val->intval = chip->target_curr; + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_SETTLED: + val->intval = chip->target_volt; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = chip->pmic_handle.ibat_curr; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = chip->pmic_handle.vbat_volt; + break; + case POWER_SUPPLY_PROP_PARALLEL_DISABLE: + val->intval = !chip->flashc_handle.charge_enabled; + break; + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + val->intval = chip->thermal_mitigation_level; + break; + case POWER_SUPPLY_PROP_NUM_SYSTEM_TEMP_LEVELS: + val->intval = chip->num_thermal_zones; + break; + default: + return -EINVAL; + + } + return 0; +} + +static int mmi_pl_pm_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct mmi_pl_chg_manager *chip = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + if (val->intval < 0) + return -EINVAL; + + if (chip->num_thermal_zones<= 0) + return -EINVAL; + + if (val->intval >= chip->num_thermal_zones) + chip->thermal_mitigation_level = + chip->num_thermal_zones - 1; + else + chip->thermal_mitigation_level = val->intval; + + break; + case POWER_SUPPLY_PROP_PD_CURRENT_MAX: + if (val->intval > TYPEC_HIGH_CURRENT_UA) + return -EINVAL; + chip->request_current = val->intval; + break; + case POWER_SUPPLY_PROP_PD_VOLTAGE_MAX: + if (val->intval > FLASH_CHARGER_PPS_MAX_VOLT) + return -EINVAL; + chip->request_volt = val->intval; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int mmi_pl_pm_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + int ret; + + switch (prop) { + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + case POWER_SUPPLY_PROP_PD_CURRENT_MAX: + case POWER_SUPPLY_PROP_PD_VOLTAGE_MAX: + ret = 1; + break; + default: + ret = 0; + break; + } + return ret; +} + +static const struct power_supply_desc mmi_pl_pm_psy_desc = { + .name = "mmi_pl_chg_manager", + .type = POWER_SUPPLY_TYPE_PARALLEL, + .properties = mmi_pl_pm_props, + .num_properties = ARRAY_SIZE(mmi_pl_pm_props), + .get_property = mmi_pl_pm_get_property, + .set_property = mmi_pl_pm_set_property, + .property_is_writeable = mmi_pl_pm_is_writeable, +}; + +static int mmi_pl_pm_psy_register(struct mmi_pl_chg_manager *chip) +{ + struct power_supply_config mmi_pl_pm_cfg = {}; + + mmi_pl_pm_cfg.drv_data = chip; + mmi_pl_pm_cfg.of_node = chip->dev->of_node; + chip->mmi_pl_pm_psy = power_supply_register(chip->dev, + &mmi_pl_pm_psy_desc, + &mmi_pl_pm_cfg); + if (IS_ERR(chip->mmi_pl_pm_psy)) { + pr_err("Couldn't register mmi_pl_pm_psy power supply\n"); + return PTR_ERR(chip->mmi_pl_pm_psy); + } + + pr_info("power supply register mmi_pl_pm_psy successfully\n"); + return 0; +} + +static int mmi_pl_chg_manager_probe(struct platform_device *pdev) +{ + int ret = 0; + struct mmi_pl_chg_manager *chip; + + if (!pdev) + return -ENODEV; + + chip = devm_kzalloc(&pdev->dev, sizeof(struct mmi_pl_chg_manager), + GFP_KERNEL); + if (!chip) { + dev_err(&pdev->dev, + "Unable to alloc memory for mmi_pl_chg_manager\n"); + return -ENOMEM; + } + + chip->dev = &pdev->dev; + chip->name = "mmi_pl_chg_manager"; + chip->debug_mask = &__debug_mask; + ret = mmi_pl_chg_manager_parse_dt(chip); + if (ret < 0) { + dev_err(&pdev->dev, + "parse dt failed\n"); + goto cleanup; + } + + chip->pd_handle = + devm_usbpd_get_by_phandle(chip->dev, "qcom,usbpd-phandle"); + if (IS_ERR_OR_NULL(chip->pd_handle)) { + dev_err(&pdev->dev, "Error getting the pd phandle %ld\n", + PTR_ERR(chip->pd_handle)); + chip->pd_handle = NULL; + } + + if (!chip->usb_psy) { + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) + mmi_pl_err(chip, "Could not get USB power_supply\n"); + } + + if (!chip->batt_psy) { + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) + mmi_pl_err(chip, "Could not get battery power_supply\n"); + } + + INIT_DELAYED_WORK(&chip->mmi_pl_sm_work, mmi_pl_sm_work_func); + INIT_WORK(&chip->psy_changed_work, psy_changed_work_func); + + ret = mmi_pl_pm_psy_register(chip); + if (ret) + goto cleanup; + + chip->psy_nb.notifier_call = psy_changed; + ret = power_supply_reg_notifier(&chip->psy_nb); + if (ret) + goto cleanup; + + init_completion(&chip->sm_completion); + platform_set_drvdata(pdev, chip); + + chip->pd_handle = + devm_usbpd_get_by_phandle(chip->dev, + "qcom,usbpd-phandle"); + if (IS_ERR_OR_NULL(chip->pd_handle)) { + dev_err(&pdev->dev, + "Error getting the pd phandle %ld\n", + PTR_ERR(chip->pd_handle)); + chip->pd_handle = NULL; + } + + create_sysfs_entries(chip); + mmi_pl_err(chip, "mmi pl chg manager initialized successfully, ret %d\n", ret); + return 0; +cleanup: + + devm_kfree(&pdev->dev, chip); + return ret; +} + +static int mmi_pl_chg_manager_remove(struct platform_device *pdev) +{ + struct mmi_pl_chg_manager *chip = platform_get_drvdata(pdev); + + remove_sysfs_entries(chip); + cancel_delayed_work_sync(&chip->mmi_pl_sm_work); + + power_supply_unreg_notifier(&chip->psy_nb); + power_supply_unregister(chip->mmi_pl_pm_psy ); + + platform_set_drvdata(pdev, NULL); + devm_kfree(&pdev->dev, chip); + return 0; +} + +static const struct of_device_id mmi_pl_chg_manager_match_table[] = { + {.compatible = "mmi,pl-chg-manager"}, + {}, +}; +MODULE_DEVICE_TABLE(of, mmi_pl_chg_manager_match_table); + +static struct platform_driver mmi_pl_chg_manager_driver = { + .probe = mmi_pl_chg_manager_probe, + .remove = mmi_pl_chg_manager_remove, + .driver = { + .name = "mmi_pl_chg_manager", + .owner = THIS_MODULE, + .of_match_table = mmi_pl_chg_manager_match_table, + }, +}; + +static int __init mmi_pl_chg_manager_init(void) +{ + int ret; + ret = platform_driver_register(&mmi_pl_chg_manager_driver); + if (ret) { + pr_err("mmi_pl_chg_manager failed to register driver\n"); + return ret; + } + return 0; +} + +static void __exit mmi_pl_chg_manager_exit(void) +{ + platform_driver_unregister(&mmi_pl_chg_manager_driver); +} + +module_init(mmi_pl_chg_manager_init); +module_exit(mmi_pl_chg_manager_exit); + +MODULE_ALIAS("platform:mmi_pl_chg_manager"); +MODULE_AUTHOR("Motorola Mobility LLC"); +MODULE_DESCRIPTION("Motorola Mobility parallel charge"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/mmi_qc3p_wt6670f/Android.mk b/drivers/power/mmi_qc3p_wt6670f/Android.mk new file mode 100644 index 000000000000..616a776bf853 --- /dev/null +++ b/drivers/power/mmi_qc3p_wt6670f/Android.mk @@ -0,0 +1,12 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(MMI_QC3P_TURBO_CHARGER),true) + KERNEL_CFLAGS += CONFIG_MMI_QC3P_TURBO_CHARGER_ISP_5G=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := mmi_qc3p_wt6670f.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/mmi_qc3p_wt6670f/Kbuild b/drivers/power/mmi_qc3p_wt6670f/Kbuild new file mode 100644 index 000000000000..3aa60e03232d --- /dev/null +++ b/drivers/power/mmi_qc3p_wt6670f/Kbuild @@ -0,0 +1,9 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/drivers/power/mmi_qc3p_wt6670f +ifneq ($(filter m y,$(CONFIG_MMI_QC3P_TURBO_CHARGER_ISP_5G)),) + EXTRA_CFLAGS += -DCONFIG_MMI_QC3P_TURBO_CHARGER_ISP_5G +endif +mmi_qc3p_wt6670f-objs += wt6670f_qc3p.o wt6670f_upgrade.o +obj-m += mmi_qc3p_wt6670f.o diff --git a/drivers/power/mmi_qc3p_wt6670f/Makefile b/drivers/power/mmi_qc3p_wt6670f/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/power/mmi_qc3p_wt6670f/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/power/mmi_qc3p_wt6670f/wt6670f.h b/drivers/power/mmi_qc3p_wt6670f/wt6670f.h new file mode 100644 index 000000000000..14ba6200cb8f --- /dev/null +++ b/drivers/power/mmi_qc3p_wt6670f/wt6670f.h @@ -0,0 +1,81 @@ +#ifndef __QC3P_WT6670_ +#define __QC3P_WT6670_ +#include +#include +#include + +#define WT6670_FIRMWARE_VERSION 0x03 + +struct wt6670f { + struct device *dev; + struct i2c_client *client; + + int i2c_scl_pin; + int i2c_sda_pin; + int reset_pin; + int intb_pin; + + int count; + int chg_type; + bool chg_ready; + struct iio_dev *indio_dev; + struct iio_chan_spec *iio_chan; + struct iio_channel *int_iio_chans; + + struct mutex i2c_rw_lock; +}; + +enum { + QC3P_WT6670F, + QC3P_Z350, +}; + +enum wt6670_charger_type{ + WT6670_CHG_TYPE_BEGIN = 0, + WT6670_CHG_TYPE_FC, + WT6670_CHG_TYPE_SDP, + WT6670_CHG_TYPE_CDP, + WT6670_CHG_TYPE_DCP, + WT6670_CHG_TYPE_QC2, + WT6670_CHG_TYPE_QC3, + WT6670_CHG_TYPE_OCP, + WT6670_CHG_TYPE_QC3P_18W, + WT6670_CHG_TYPE_QC3P_27W, + WT6670_CHG_TYPE_UNKNOWN, +}; + +struct wt6670_iio_channels { + const char *datasheet_name; + int channel_num; + enum iio_chan_type type; + long info_mask; +}; + +#define wt6670_IIO_CHAN(_name, _num, _type, _mask) \ + { \ + .datasheet_name = _name, \ + .channel_num = _num, \ + .type = _type, \ + .info_mask = _mask, \ + }, + +#define wt6670_CHAN_INDEX(_name, _num) \ + wt6670_IIO_CHAN(_name, _num, IIO_INDEX, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +static const struct wt6670_iio_channels wt6670_iio_psy_channels[] = { + wt6670_CHAN_INDEX("wt6670_usb_real_type", PSY_IIO_QC3P_REAL_TYPE) + wt6670_CHAN_INDEX("wt6670_usb_qc3p_power", PSY_IIO_QC3P_POWER) + wt6670_CHAN_INDEX("wt6670_battery_dp_dm", PSY_IIO_BATTERY_DP_DM) + wt6670_CHAN_INDEX("wt6670_start_detection", PSY_IIO_START_DETECTION) + wt6670_CHAN_INDEX("wt6670_detection_ready", PSY_IIO_DETECTION_READY) + wt6670_CHAN_INDEX("wt6670_firmware_num", PSY_IIO_QC3P_FIRMWARE_NUM) + wt6670_CHAN_INDEX("wt6670_start_bc12_detection", PSY_IIO_START_BC12_DETECTION) + wt6670_CHAN_INDEX("wt6670_detection_bc12_ready", PSY_IIO_DETECTION_BC12_READY) + wt6670_CHAN_INDEX("read_usbin_voltage", PSY_IIO_READ_USBIN_VOLTAGE) + wt6670_CHAN_INDEX("wt6670_bc12_chg_type", PSY_IIO_BC12_CHG_TYPE) +}; + +int wt6670f_isp_flow(struct wt6670f *chip); +int wt6670f_get_firmware_version(void); +#endif //__QC3P_WT6670_ diff --git a/drivers/power/mmi_qc3p_wt6670f/wt6670f_qc3p.c b/drivers/power/mmi_qc3p_wt6670f/wt6670f_qc3p.c new file mode 100644 index 000000000000..af95bbe45f59 --- /dev/null +++ b/drivers/power/mmi_qc3p_wt6670f/wt6670f_qc3p.c @@ -0,0 +1,951 @@ +#include +#include /* For init/exit macros */ +#include /* For MODULE_ marcros */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +//#include +#include +#include +#include +#include +#include "wt6670f.h" +#include +#include + +#define MAX_USER_STR 1024 + +static DEFINE_MUTEX(wt6670f_i2c_access); +//static DEFINE_MUTEX(wt6670f_access_lock); +//static struct i2c_client *new_client; +//static int wt6670f_reset_pin = -1; +static int wt6670f_int_pin = -1; +struct wt6670f *_wt = NULL; +int g_qc3p_id = 0; +int m_chg_type = 0; +bool qc3p_z350_init_ok = false; +static struct proc_dir_entry *procfs_file; +int wt6670f_get_firmware_version(void); +int z350_get_firmware_version(void); +static int mmi_wt6670f_seq_show(struct seq_file *f, void *ptr) +{ + int result; + + if(QC3P_WT6670F == g_qc3p_id){ + result = wt6670f_get_firmware_version(); + pr_info("wt6670 get wt6670f_firmware_num:%d\n", result); + } else if(QC3P_Z350 == g_qc3p_id){ + result = z350_get_firmware_version(); + pr_info("z350 get z350_get_firmware_version:0x%x\n", result); + } else { + result = 0; + pr_info("could not get device id for used\n"); + } + + seq_printf(f, "%d", result); + + return 0; +} + +static int mmi_wt6670f_open(struct inode *inode, struct file *file) +{ + return single_open(file, mmi_wt6670f_seq_show, inode->i_private); +} + +static ssize_t mmi_wt6670f_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset) +{ + char buffer[MAX_USER_STR]; + const size_t maxlen = sizeof(buffer) - 1; + + memset(buffer, 0, sizeof(buffer)); + if (copy_from_user(buffer, buf, count > maxlen ? maxlen : count)) + return -EFAULT; + + return count; +} + +static const struct file_operations mmi_wt6670f_operations = { + .open = mmi_wt6670f_open, + .read = seq_read, + .write = mmi_wt6670f_write, + .llseek = seq_lseek, + .release = single_release, +}; + + +static int __wt6670f_write_word(struct wt6670f *wt, u8 reg, u16 data) +{ + s32 ret; + + ret = i2c_smbus_write_word_data(wt->client, reg, data); + if (ret < 0) { + pr_err("i2c write fail: can't write from reg 0x%02X\n", reg); + return ret; + } + + return 0; +} + + +static int __wt6670f_read_word(struct wt6670f *wt, u8 reg, u16 *data) +{ + s32 ret; + + ret = i2c_smbus_read_word_data(wt->client, reg); + if (ret < 0) { + pr_err("i2c read fail: can't read from reg 0x%02X\n", reg); + return ret; + } + + *data = (u16) ret; + + return 0; +} + +static int wt6670f_read_word(struct wt6670f *wt, u8 reg, u16 *data) +{ + int ret; + + mutex_lock(&wt->i2c_rw_lock); + ret = __wt6670f_read_word(wt, reg, data); + mutex_unlock(&wt->i2c_rw_lock); + + return ret; +} + +static int wt6670f_write_word(struct wt6670f *wt, u8 reg, u16 data) +{ + int ret; + + mutex_lock(&wt->i2c_rw_lock); + ret = __wt6670f_write_word(wt, reg, data); + mutex_unlock(&wt->i2c_rw_lock); + + return ret; +} + +static u16 wt6670f_get_vbus_voltage(void) +{ + int ret = 0; + u8 data[2]; + u16 tmp; + if(QC3P_WT6670F == g_qc3p_id) + ret = wt6670f_read_word(_wt, 0xBE, (u16 *)data); + else if(QC3P_Z350 == g_qc3p_id) + ret = wt6670f_read_word(_wt, 0x12, (u16 *)data); + else + ret = -1; + if(ret < 0) + { + pr_err("wt6670f get vbus voltage fail\n"); + return ret; + } + + tmp = ((data[0] << 8) | data[1]) & 0x3ff; + + pr_err(">>>>>>wt6670f get vbus voltage = %04x %02x %02x\n", tmp, + data[0], data[1]); + return (u16)(tmp * 1898 / 100); +} + +static u16 wt6670f_get_id(u8 reg) +{ + int ret; + u16 data; + + ret = wt6670f_read_word(_wt, reg, &data);//wt6670f-0xBC,Z350-0x13 + + if(ret < 0) + { + pr_err("wt6670f get id fail\n"); + return ret; + } + + pr_err(">>>>>>wt6670f get id = %x\n", data); + return data; +} + +int wt6670f_start_detection(void) +{ + int ret; + u16 data = 0x01; + + ret = wt6670f_write_word(_wt, 0xB6, data); + if (ret < 0) + { + pr_info("wt6670f start detection fail\n"); + return ret; + } + + return data & 0xff; +} +EXPORT_SYMBOL_GPL(wt6670f_start_detection); + +int wt6670f_re_run_apsd(void) +{ + int ret; + u16 data = 0x01; + + ret = wt6670f_write_word(_wt, 0xB6, data); + + if (ret < 0) + { + pr_info("wt6670f re run apsd fail\n"); + return ret; + } + + return data & 0xff; +} +EXPORT_SYMBOL_GPL(wt6670f_re_run_apsd); + +int wt6670f_en_hvdcp(void) +{ + int ret; + u16 data = 0x01; + + ret = wt6670f_write_word(_wt, 0x05, data); + + if (ret < 0) + { + pr_info("z350 en hvdcp fail\n"); + return ret; + } + + return data & 0xff; +} +EXPORT_SYMBOL_GPL(wt6670f_en_hvdcp); + +int wt6670f_get_protocol(void) +{ + int ret = 0; + u16 data; + u8 data1, data2; + if(QC3P_WT6670F == g_qc3p_id){ + ret = wt6670f_read_word(_wt, 0xBD, &data); + pr_err("wt6670f get protocol %x\n",data); + } + else if(QC3P_Z350 == g_qc3p_id){ + ret = wt6670f_read_word(_wt, 0x11, &data); + pr_err("z350 get protocol %x\n",data); + } + else + ret = -1; + + if (ret < 0) + { + pr_err("wt6670f get protocol fail\n"); + return ret; + } + + // Get data2 part + data1 = data & 0xFF; + data2 = data >> 8; + if((QC3P_Z350 == g_qc3p_id)&&(data1 <= 7)) + data1--; + pr_err("Get charger type, rowdata = 0X%04x, data1= 0X%02x, data2=0X%02x \n", data, data1, data2); + if((data2 == 0x03) && ((data1 > 0x9) || (data1 == 0x7))) + { + pr_err("fail to get charger type, error happens!\n"); + return -EINVAL; + } + + if(data2 == 0x04) + { + pr_err("detected QC3+ charger:0X%02x!\n", data1); + } + + if((data1 > 0x00 && data1 < 0x07) || + (data1 > 0x07 && data1 < 0x0a) ||(QC3P_Z350 == g_qc3p_id && data1 == 0x10)){ + ret = data1; + } + else { + ret = 0x00; + } + + _wt->chg_type = ret; +// return data & 0xff; + return ret; +} +EXPORT_SYMBOL_GPL(wt6670f_get_protocol); + +int wt6670f_get_charger_type(void) +{ + return _wt->chg_type; +} +EXPORT_SYMBOL_GPL(wt6670f_get_charger_type); + +bool wt6670f_is_charger_ready(void) +{ + return _wt->chg_ready; +} +EXPORT_SYMBOL_GPL(wt6670f_is_charger_ready); + +int wt6670f_get_firmware_version(void) +{ + int ret; + u16 data = 0; + int read_firmware_retry = 3; + + ret = wt6670f_read_word(_wt, 0xBF, &data); + if (ret < 0) + { + pr_err("wt6670f get firmware fail,retry\n"); + gpio_direction_output(_wt->intb_pin,0); + usleep_range(5000,6000); + gpio_direction_output(_wt->intb_pin,1); + usleep_range(5000,6000); + while(read_firmware_retry--){ + ret = wt6670f_read_word(_wt, 0xBF, &data); + if(ret == 0) + break; + } + gpio_direction_input(wt6670f_int_pin); + if (ret < 0) { + pr_err("wt6670f get firmware fail\n"); + return ret; + } + } + + return data & 0xff; +} +EXPORT_SYMBOL_GPL(wt6670f_get_firmware_version); + + +int z350_get_firmware_version(void) +{ + int ret; + u16 data; + + ret = wt6670f_read_word(_wt, 0x14, &data); + if (ret < 0) + { + pr_err("z350 get firmware fail\n"); + return ret; + } + return data; +} + +int wt6670f_set_voltage(u16 voltage) +{ + int ret = 0; + u16 step; + u16 voltage_now; + + voltage_now = wt6670f_get_vbus_voltage(); + pr_err("wt6670f current voltage = %d, set voltage = %d\n", voltage_now, voltage); + + if(voltage - voltage_now < 0) + { + step = (u16)((voltage_now - voltage) / 20); + _wt->count -= step; + step &= 0x7FFF; + step = ((step & 0xff) << 8) | ((step >> 8) & 0xff); + + if(_wt->count < 0) + _wt->count = 0; + } + else + { + step = (u16)((voltage - voltage_now) / 20); + _wt->count += step; + step |= 0x8000; + step = ((step & 0xff) << 8) | ((step >> 8) & 0xff); + } + pr_err("---->southchip count = %d %04x,QC3P_WT6670F=%d,g_qc3p_id=%d,voltage=%d,voltage_now=%d\n", _wt->count, step,QC3P_WT6670F,g_qc3p_id,voltage,voltage_now); + + if(QC3P_WT6670F == g_qc3p_id) + ret = wt6670f_write_word(_wt, 0xBB, step); + else if(QC3P_Z350 == g_qc3p_id) + ret = wt6670f_write_word(_wt, 0x83, step); + else + ret = -1; + + return ret; +} +EXPORT_SYMBOL_GPL(wt6670f_set_voltage); + +int wt6670f_set_volt_count(int count) +{ + int ret = 0; + u16 step = abs(count); + + pr_err("Set vbus with %d pulse!\n!", count); + + if(count < 0) + { + _wt->count -= step; + step &= 0x7FFF; + step = ((step & 0xff) << 8) | ((step >> 8) & 0xff); + + if(_wt->count < 0) + _wt->count = 0; + } + else + { + _wt->count += step; + step |= 0x8000; + step = ((step & 0xff) << 8) | ((step >> 8) & 0xff); + } + + if(QC3P_WT6670F == g_qc3p_id) + ret = wt6670f_write_word(_wt, 0xBB, step); + else if(QC3P_Z350 == g_qc3p_id) + ret = wt6670f_write_word(_wt, 0x83, step); + + return ret; +} +EXPORT_SYMBOL_GPL(wt6670f_set_volt_count); + +static ssize_t wt6670f_show_test(struct device *dev, + struct device_attribute *attr, char *buf) +{ +// struct wt6670f *wt = dev_get_drvdata(dev); + + int idx = 0; + u8 data; + + data = wt6670f_get_protocol(); + idx = snprintf(buf, PAGE_SIZE, ">>> protocol = %02x\n", data); + return idx; +} + +static ssize_t wt6670f_store_test(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ +// struct wt6670f *wt = dev_get_drvdata(dev); + + int val; + int ret; + + ret = sscanf(buf, "%d", &val); + + ret = wt6670f_set_voltage(val); + + pr_info("%s ret:%d",__func__, ret); + return count; + +} + + +static ssize_t wt6670f_show_registers(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct wt6670f *wt = dev_get_drvdata(dev); + + int idx = 0; + int ret = 0; + u16 data; + if(QC3P_WT6670F == g_qc3p_id){ + ret = wt6670f_read_word(wt, 0xBD, &data); + idx = snprintf(buf, PAGE_SIZE, ">>> reg[0xBD] = %04x\n", data); + pr_err(">>>>>>>>>>WT6670F test southchip 0xBD = %04x\n", data); + } + + if(QC3P_Z350 == g_qc3p_id){ + ret = wt6670f_read_word(wt, 0x11, &data); + idx = snprintf(buf, PAGE_SIZE, ">>> reg[0x11] = %04x\n", data); + pr_err(">>>>>>>>>>Z350 test southchip 0x11 = %04x\n", data); + } + pr_info("%s ret:%d",__func__, ret); + return idx; +} + +static ssize_t wt6670f_store_registers(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct wt6670f *wt = dev_get_drvdata(dev); + + int val; + int ret = 0; + + ret = sscanf(buf, "%x", &val); + + if(QC3P_WT6670F == g_qc3p_id) + ret = wt6670f_write_word(wt, 0xBB, val); + + if(QC3P_Z350 == g_qc3p_id) + ret = wt6670f_write_word(wt, 0x83, val); + pr_info("%s ret:%d",__func__, ret); + return count; + +} + + +static DEVICE_ATTR(test, 0660, wt6670f_show_test, wt6670f_store_test); +static DEVICE_ATTR(registers, 0660, wt6670f_show_registers, wt6670f_store_registers); + +static void wt6670f_create_device_node(struct device *dev) +{ + device_create_file(dev, &dev_attr_registers); + device_create_file(dev, &dev_attr_test); +} + +int wt6670f_do_reset(void) +{ + gpio_direction_output(_wt->reset_pin,1); + msleep(1); + gpio_direction_output(_wt->reset_pin,0); + msleep(10); + + return 0; +} +EXPORT_SYMBOL_GPL(wt6670f_do_reset); + +int wt6670f_isp_do_reset(void) +{ + gpio_direction_output(_wt->reset_pin,1); + usleep_range(100,200); + gpio_direction_output(_wt->reset_pin,0); + usleep_range(2000,3000); + + return 0; +} +EXPORT_SYMBOL_GPL(wt6670f_isp_do_reset); + +void wt6670f_reset_chg_type(void) +{ + _wt->chg_type = 0; + _wt->chg_ready = false; +} +EXPORT_SYMBOL_GPL(wt6670f_reset_chg_type); + +int moto_tcmd_wt6670f_get_firmware_version(void) +{ + int wt6670f_fm_ver = 0; + int z350_fm_ver = 0; + wt6670f_do_reset(); + wt6670f_fm_ver = wt6670f_get_firmware_version(); + z350_fm_ver = z350_get_firmware_version(); + pr_info("%s: get firmware wt6670f:%x,z350:%x!\n", __func__,wt6670f_fm_ver,z350_fm_ver); + if((3 == wt6670f_fm_ver)||(0x080a == z350_fm_ver)||(0x0f0a == z350_fm_ver)) + return 1; + else + return 0; +} +EXPORT_SYMBOL_GPL(moto_tcmd_wt6670f_get_firmware_version); + +static void clear_irq_signal(void) +{ + int ret = 0; + u16 data = 0; + + if(QC3P_Z350 == g_qc3p_id){ + ret = wt6670f_read_word(_wt, 0x11, &data); + pr_err("%s: data:%x, ret:%d\n", __func__, data, ret); + } +} + +static irqreturn_t wt6670f_intr_handler(int irq, void *data) +{ + m_chg_type = 0xff; + pr_info("%s,chg_type 0x:%x\n", __func__,m_chg_type); + _wt->chg_ready = true; + + return IRQ_HANDLED; +} + +static int wt6670f_parse_dt(struct device *dev) +{ + struct device_node *np = dev->of_node; + int ret = 0; + + pr_info("%s\n", __func__); + if (!np) { + pr_info("%s: no of node\n", __func__); + return -ENODEV; + } + + _wt->reset_pin = of_get_named_gpio(np, "wt6670f-reset-gpio", 0); + if (_wt->reset_pin < 0) + pr_info("[%s] of get wt6670 gpio failed, reset_pin:%d\n",__func__, _wt->reset_pin); + + if (gpio_request(_wt->reset_pin, "wt6670_reset_pin")) { + pr_info("[%s]reset gpio_request failed", __func__); + } + if (gpio_direction_output(_wt->reset_pin, 0)) + pr_info("[%s] gpio_direction_output reset_pin failed", __func__); + + wt6670f_int_pin = of_get_named_gpio(np,"wt6670f-int-gpio",0); + if(wt6670f_int_pin < 0){ + pr_info("%s: get wt6670f-int-gpio failed\n", __func__); + } + + if (gpio_request(wt6670f_int_pin,"wt6670f_int")) { + pr_info("[%s]wt6670f_int gpio_request failed", __func__); + } + if (gpio_direction_input(wt6670f_int_pin)) + pr_info("[%s] gpio_direction_input wt6670f_int_pin failed", __func__); + _wt->intb_pin = wt6670f_int_pin; + + ret = request_irq(gpio_to_irq(wt6670f_int_pin), wt6670f_intr_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "wt6670f int", dev); + enable_irq_wake(gpio_to_irq(wt6670f_int_pin)); + pr_info("%s ret:%d",__func__, ret); + return 0; +} + +static int wt6670_iio_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val1, + int val2, long mask) +{ + int rc = 0; + + switch (chan->channel) { + case PSY_IIO_START_DETECTION: + wt6670f_reset_chg_type(); + wt6670f_start_detection(); + pr_info("wt6670 start detection\n"); + break; + case PSY_IIO_BATTERY_DP_DM: + wt6670f_set_volt_count(val1); + pr_info("wt6670 set volt count:%d\n",val1); + break; + case PSY_IIO_START_BC12_DETECTION: + clear_irq_signal(); + wt6670f_reset_chg_type(); + wt6670f_start_detection(); + pr_info("wt6670 start bc1.2 detection\n"); + break; + default: + pr_err("Unsupported wt6670 IIO chan %d\n", chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) + pr_err("Couldn't write IIO channel %d, rc = %d\n", + chan->channel, rc); + + return rc; +} + +static int wt6670_iio_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val1, + int *val2, long mask) +{ + int rc = 0; + int result; + int wait_count = 0; + *val1 = 0; + + + switch (chan->channel) { + case PSY_IIO_QC3P_POWER: + result = wt6670f_get_charger_type(); + pr_info("wt6670 get charger type for qc3p power:%d\n",result); + if(result == WT6670_CHG_TYPE_QC3P_18W || result == WT6670_CHG_TYPE_QC3P_27W) + *val1 = result; + break; + case PSY_IIO_BC12_CHG_TYPE: + wt6670f_get_protocol(); + result = wt6670f_get_charger_type(); + pr_info("wt6670 get bc1.2 charger type:%d\n", result); + *val1 = result; + break; + case PSY_IIO_QC3P_REAL_TYPE: + wt6670f_get_protocol(); + result = wt6670f_get_charger_type(); + + if((result == 0x10) &&(QC3P_Z350 == g_qc3p_id)) { + wt6670f_en_hvdcp(); + pr_info("wt6670 wt6670f_en_hvdcp\n"); + wait_count = 0; + while((result != 0xff)&&(wait_count<30)){ + msleep(100); + wait_count++; + } + wt6670f_get_protocol(); + result = wt6670f_get_charger_type(); + } + + pr_info("wt6670 get charger type:%d\n", result); + *val1 = result; + break; + case PSY_IIO_DETECTION_READY: + result = wt6670f_is_charger_ready(); + pr_info("wt6670 get wt6670f_is_charger_ready status:%d\n", result); + *val1 = result; + break; + case PSY_IIO_QC3P_FIRMWARE_NUM: + + if(QC3P_WT6670F == g_qc3p_id){ + result = wt6670f_get_firmware_version(); + pr_info("wt6670 get wt6670f_firmware_num:%d\n", result); + } else if(QC3P_Z350 == g_qc3p_id){ + result = z350_get_firmware_version(); + pr_info("z350 get z350_get_firmware_version:0x%x\n", result); + } else { + result = 0; + pr_info("could not get device id for used\n"); + } + + *val1 = result; + break; + case PSY_IIO_DETECTION_BC12_READY: + result = wt6670f_is_charger_ready(); + pr_info("wt6670 get bc1.2 ready status:%d\n", result); + *val1 = result; + break; + case PSY_IIO_READ_USBIN_VOLTAGE: + result = wt6670f_get_vbus_voltage(); + pr_info("wt6670 get vbus voltage:%d\n", result); + *val1 = result; + break; + default: + pr_err("Unsupported wt6670 IIO chan %d\n", chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) { + pr_err("Couldn't read IIO channel %d, rc = %d\n", + chan->channel, rc); + return rc; + } + + return IIO_VAL_INT; +} + +static int wt6670_iio_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + struct wt6670f *bq = iio_priv(indio_dev); + struct iio_chan_spec *iio_chan = bq->iio_chan; + int i; + + for (i = 0; i < ARRAY_SIZE(wt6670_iio_psy_channels); + i++, iio_chan++) + if (iio_chan->channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static const struct iio_info wt6670_iio_info = { + .read_raw = wt6670_iio_read_raw, + .write_raw = wt6670_iio_write_raw, + .of_xlate = wt6670_iio_of_xlate, +}; + +static int wt6670_init_iio_psy(struct wt6670f *chip) +{ + struct iio_dev *indio_dev = chip->indio_dev; + struct iio_chan_spec *chan; + int wt6670_num_iio_channels = ARRAY_SIZE(wt6670_iio_psy_channels); + int rc, i; + + chip->iio_chan = devm_kcalloc(chip->dev, wt6670_num_iio_channels, + sizeof(*chip->iio_chan), GFP_KERNEL); + if (!chip->iio_chan) + return -ENOMEM; + + chip->int_iio_chans = devm_kcalloc(chip->dev, + wt6670_num_iio_channels, + sizeof(*chip->int_iio_chans), + GFP_KERNEL); + if (!chip->int_iio_chans) + return -ENOMEM; + + indio_dev->info = &wt6670_iio_info; + indio_dev->dev.parent = chip->dev; + indio_dev->dev.of_node = chip->dev->of_node; + indio_dev->name = "wt6670"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = chip->iio_chan; + indio_dev->num_channels = wt6670_num_iio_channels; + + for (i = 0; i < wt6670_num_iio_channels; i++) { + chip->int_iio_chans[i].indio_dev = indio_dev; + chan = &chip->iio_chan[i]; + chip->int_iio_chans[i].channel = chan; + chan->address = i; + chan->channel = wt6670_iio_psy_channels[i].channel_num; + chan->type = wt6670_iio_psy_channels[i].type; + chan->datasheet_name = + wt6670_iio_psy_channels[i].datasheet_name; + chan->extend_name = + wt6670_iio_psy_channels[i].datasheet_name; + chan->info_mask_separate = + wt6670_iio_psy_channels[i].info_mask; + } + + rc = devm_iio_device_register(chip->dev, indio_dev); + if (rc) + pr_err("Failed to register wt6670 IIO device, rc=%d\n", rc); + + return rc; +} + +extern int wt6670f_isp_flow(struct wt6670f *chip); + +static int wt6670f_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + u16 firmware_version = 0; + u16 wt6670f_id = 0; + int in_isp_flow_count = 3; + struct wt6670f *wt; + struct iio_dev *indio_dev; + + pr_info("[%s]\n", __func__); + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*wt)); + if (!indio_dev) + return -ENOMEM; + + wt = iio_priv(indio_dev); + if (!wt) { + pr_err("Out of memory\n"); + return -ENOMEM; + } + wt->indio_dev = indio_dev; + + wt->dev = &client->dev; + wt->client = client; + i2c_set_clientdata(client, wt); + mutex_init(&wt->i2c_rw_lock); + + _wt = wt; + + ret = wt6670f_parse_dt(&client->dev); + if (ret < 0) + pr_info("%s: parse dt error\n", __func__); + + wt6670f_create_device_node(&(client->dev)); + + //wt6670f_do_reset(); + + gpio_direction_output(_wt->reset_pin,1); + usleep_range(5000,6000); + gpio_direction_output(_wt->reset_pin,0); + usleep_range(5000,6000); + + wt6670f_id = wt6670f_get_id(0x13); + if(0x3349 == wt6670f_id){ + g_qc3p_id = QC3P_Z350; + qc3p_z350_init_ok = true; + //gpio_direction_output(_wt->reset_pin, 1); + pr_info("[%s] wt is z350\n", __func__); + + ret = wt6670_init_iio_psy(wt); + if (ret < 0) + pr_info("%s: init iio sys error\n", __func__); + + if (gpio_direction_input(wt6670f_int_pin)) + pr_info("[%s] gpio_direction_input wt6670f_int_pin failed", __func__); + goto probe_out; + } + + wt6670f_reset_chg_type(); + gpio_direction_output(_wt->intb_pin,0); + usleep_range(5000,6000); + gpio_direction_output(_wt->intb_pin,1); + usleep_range(5000,6000); + firmware_version = wt6670f_get_firmware_version(); + + pr_info("[%s] firmware_version = %d, chg_type = 0x%x\n", __func__,firmware_version, _wt->chg_type); + + if(firmware_version != WT6670_FIRMWARE_VERSION){ + pr_info("[%s]: firmware need upgrade, run wt6670_isp!", __func__); + while(in_isp_flow_count--){ + ret = wt6670f_isp_flow(wt); + if(ret == 0) + break; + } + } + wt6670f_id = wt6670f_get_id(0xBC); + if(0x5457 == wt6670f_id){ + g_qc3p_id = QC3P_WT6670F; + pr_info("[%s] is wt6670f,firmware_version = %x\n", __func__,firmware_version); + } + + ret = wt6670_init_iio_psy(wt); + if (ret < 0) + pr_info("%s: init iio sys error\n", __func__); + if (gpio_direction_input(wt6670f_int_pin)) + pr_info("[%s] gpio_direction_input wt6670f_int_pin failed", __func__); +probe_out: + /* Create the procfs file at /proc/driver/wt6670f_firmware_num */ + procfs_file = proc_create("driver/wt6670f_firmware_num",0777, NULL, &mmi_wt6670f_operations); + return 0; +} + +static void wt6670f_shutdown(struct i2c_client *client) +{ + int ret = 0; + u16 data = 0x01; + + if(QC3P_WT6670F == g_qc3p_id){ + ret = wt6670f_write_word(_wt, 0xB6, data); + pr_info("%s set voltage ok wt6670\n", __func__); + }else if(QC3P_Z350 == g_qc3p_id){ + ret = wt6670f_write_word(_wt, 0x02, data); + pr_info("%s set voltage ok z350\n", __func__); + }else{ + ret = wt6670f_write_word(_wt, 0xB6, data); + pr_info("%s set voltage ok wt6670\n", __func__); + } + if (ret < 0) + { + pr_info("%s set voltage fail\n",__func__); + } +} +//#define WT6670F_PM_OPS (NULL) + +static const struct i2c_device_id wt6670f_id_table[] = { + {"wt6670f", 0}, + {}, +}; + +static const struct of_device_id wt_match_table[] = { + {.compatible = "mediatek,wt6670f_qc3p",}, + {}, +}; + +static struct i2c_driver wt6670f_driver = { + .driver = { + .name = "wt6670f_qc3p", + .owner = THIS_MODULE, + .of_match_table = wt_match_table, + //.pm = WT6670F_PM_OPS, + }, + .probe = wt6670f_i2c_probe, + //.remove = wt6670f_i2c_remove, + .shutdown = wt6670f_shutdown, + .id_table = wt6670f_id_table, +}; + +static int __init wt6670f_init(void) +{ + pr_info("[%s] init start with i2c DTS", __func__); + if (i2c_add_driver(&wt6670f_driver) != 0) { + pr_info( + "[%s] failed to register wt6670f i2c driver.\n",__func__); + } else { + pr_info( + "[%s] Success to register wt6670f i2c driver.\n",__func__); + } + return 0; + //return i2c_add_driver(&wt6670f_driver); +} + +static void __exit wt6670f_exit(void) +{ + i2c_del_driver(&wt6670f_driver); +} +module_init(wt6670f_init); +module_exit(wt6670f_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lai Du "); +MODULE_DESCRIPTION("WT6670F QC3P Driver"); diff --git a/drivers/power/mmi_qc3p_wt6670f/wt6670f_upgrade.c b/drivers/power/mmi_qc3p_wt6670f/wt6670f_upgrade.c new file mode 100644 index 000000000000..db479adb77b7 --- /dev/null +++ b/drivers/power/mmi_qc3p_wt6670f/wt6670f_upgrade.c @@ -0,0 +1,1528 @@ +#include +#include /* For init/exit macros */ +#include /* For MODULE_ marcros */ +#include +#include +#include +#include +#include +#ifdef CONFIG_OF +#include +#include +#include +#include +#endif +//#include +//#include +#include +#include +#include +#include + +#include +#include "wt6670f.h" + +extern int wt6670f_isp_do_reset(void); +//extern unsigned int get_i2c_speed(int id); +//extern void set_i2c_speed(int id, unsigned int speed_hz); + +#define WT6670_ISP_I2C_ADDR 0x34 +#ifdef CONFIG_MMI_QC3P_TURBO_CHARGER_ISP_5G +#define WT6670_WRITE_BYTE 0x01 +#else +#define WT6670_WRITE_BYTE 0x40 +#endif +#if 1 +unsigned char WT70F_QC3p_V02_210326_FBA1_bin[] = { +0x02, 0x0A, 0x54, 0x02, 0x07, 0xB8, 0xC2, 0x8D, 0x05, 0x0E, 0x32, 0x02, 0x00, 0x06, 0x75, 0x4D, +0x01, 0x22, 0x32, 0x02, 0x00, 0x12, 0xE5, 0x24, 0x30, 0xE7, 0x05, 0x75, 0x54, 0x18, 0x80, 0x03, +0x75, 0x54, 0x19, 0xE5, 0x24, 0x54, 0x7F, 0xFE, 0x7C, 0x00, 0xE4, 0x25, 0x25, 0xFF, 0xEC, 0x3E, +0xAD, 0x07, 0xFC, 0xE4, 0xFB, 0xAF, 0x54, 0x02, 0x0A, 0x77, 0x32, 0x02, 0x00, 0x3A, 0x03, 0x00, +0x32, 0xFF, 0xFF, 0x02, 0x00, 0x40, 0x30, 0x06, 0x14, 0xC3, 0xE5, 0x1C, 0x94, 0xF4, 0xE5, 0x1B, +0x94, 0x01, 0x40, 0x09, 0xC2, 0x06, 0x90, 0x00, 0x11, 0xE0, 0x44, 0x08, 0xF0, 0xC3, 0xE5, 0x0D, +0x94, 0x64, 0xE5, 0x0C, 0x94, 0x00, 0x40, 0x12, 0xE4, 0xF5, 0x0C, 0xF5, 0x0D, 0x7F, 0x02, 0x12, +0x08, 0x00, 0xAD, 0x07, 0xAC, 0x06, 0x8C, 0x50, 0x8D, 0x51, 0x22, 0x02, 0x02, 0x00, 0x20, 0x01, +0x02, 0x21, 0x3E, 0xAF, 0x23, 0xEF, 0xD3, 0x94, 0xB5, 0x50, 0x06, 0x74, 0x2B, 0x2F, 0xF8, 0xA6, +0x24, 0xEF, 0x24, 0x50, 0xB4, 0x0C, 0x00, 0x40, 0x02, 0x21, 0x39, 0x90, 0x00, 0xA5, 0x25, 0xE0, +0x50, 0x02, 0x05, 0x83, 0x73, 0x01, 0xBD, 0x01, 0xCB, 0x21, 0x39, 0x01, 0xD2, 0x01, 0xEB, 0x21, +0x04, 0x21, 0x13, 0x21, 0x39, 0x21, 0x39, 0x21, 0x39, 0x21, 0x28, 0x21, 0x30, 0x75, 0x2C, 0x01, +0xE4, 0xF5, 0x30, 0x90, 0x00, 0x81, 0xF0, 0x7D, 0x04, 0x80, 0x54, 0xAF, 0x24, 0x12, 0x0A, 0x2C, +0x80, 0x67, 0xE4, 0x90, 0x00, 0x81, 0xF0, 0x90, 0x00, 0x80, 0xF0, 0x12, 0x0B, 0x02, 0x75, 0x2F, +0x01, 0x75, 0x4D, 0x01, 0xE4, 0xF5, 0x11, 0xF5, 0x12, 0x80, 0x4E, 0xE5, 0x24, 0x64, 0x01, 0x70, +0x48, 0x90, 0x00, 0x81, 0xF0, 0x75, 0x43, 0x11, 0xF5, 0x44, 0xF5, 0x47, 0xF5, 0x4D, 0xF5, 0x19, +0xF5, 0x1A, 0x80, 0x35, 0xE5, 0x24, 0x64, 0x01, 0x70, 0x2F, 0xF5, 0x11, 0xF5, 0x12, 0x12, 0x0A, +0x98, 0x80, 0x26, 0xE5, 0x24, 0xB4, 0x01, 0x21, 0xE5, 0x4D, 0xB4, 0x02, 0x1C, 0x7D, 0x1A, 0x7F, +0xF4, 0x7E, 0x01, 0x12, 0x0B, 0x4D, 0x80, 0x11, 0x85, 0x24, 0x33, 0x85, 0x25, 0x34, 0x80, 0x06, +0x85, 0x24, 0x35, 0x85, 0x25, 0x36, 0x12, 0x00, 0x16, 0xE4, 0xF5, 0x23, 0xC2, 0x01, 0x30, 0x02, +0x17, 0xE5, 0x23, 0x24, 0x4D, 0x60, 0x09, 0x24, 0x03, 0x70, 0x08, 0xE4, 0xF5, 0x2C, 0x80, 0x03, +0xE4, 0xF5, 0x2F, 0xE4, 0xF5, 0x23, 0xC2, 0x02, 0x22, 0x90, 0x00, 0xA1, 0xE0, 0x20, 0xE2, 0x77, +0x30, 0xE3, 0x06, 0x12, 0x0B, 0x75, 0xC2, 0x00, 0x22, 0x20, 0x00, 0x49, 0x90, 0x00, 0xA3, 0xE0, +0xF5, 0x23, 0xE5, 0x23, 0x24, 0x42, 0x60, 0x12, 0x14, 0x60, 0x1A, 0x24, 0x02, 0x70, 0x1F, 0x75, +0x3C, 0x00, 0x75, 0x3D, 0x00, 0x75, 0x3E, 0x43, 0x80, 0x14, 0x75, 0x3C, 0x00, 0x75, 0x3D, 0x00, +0x75, 0x3E, 0x50, 0x80, 0x09, 0x75, 0x3C, 0xFF, 0x75, 0x3D, 0x00, 0x75, 0x3E, 0x3E, 0xAB, 0x3C, +0xAA, 0x3D, 0xA9, 0x3E, 0x12, 0x07, 0x1D, 0x90, 0x00, 0xA3, 0xF0, 0xD2, 0x00, 0x75, 0x22, 0x01, +0x75, 0x3B, 0x01, 0x80, 0x0E, 0x90, 0x00, 0xA3, 0xE0, 0xFF, 0x74, 0x23, 0x25, 0x22, 0xF8, 0xA6, +0x07, 0x05, 0x22, 0x12, 0x0B, 0x75, 0x90, 0x00, 0xA1, 0xE0, 0x30, 0xE5, 0x32, 0x90, 0x00, 0xA0, +0xE0, 0x44, 0x03, 0xF0, 0xD2, 0x01, 0x22, 0xAB, 0x3C, 0xAA, 0x3D, 0xA9, 0x3E, 0x85, 0x3B, 0x82, +0x75, 0x83, 0x00, 0x12, 0x07, 0x36, 0x90, 0x00, 0xA3, 0xF0, 0x05, 0x3B, 0x12, 0x0B, 0x75, 0x90, +0x00, 0xA1, 0xE0, 0x30, 0xE5, 0x09, 0x90, 0x00, 0xA0, 0xE0, 0x44, 0x01, 0xF0, 0xD2, 0x02, 0x22, +0xC0, 0xE0, 0xC0, 0xF0, 0xC0, 0x83, 0xC0, 0x82, 0xC0, 0xD0, 0xC0, 0x00, 0x75, 0xD0, 0x00, 0xC3, +0xE5, 0x81, 0x94, 0x07, 0xF8, 0xA6, 0xFD, 0x08, 0xA6, 0xF5, 0x90, 0x00, 0x78, 0x74, 0xA0, 0xF0, +0xD2, 0xFB, 0x74, 0xA0, 0x55, 0xF8, 0xB4, 0xA0, 0xF9, 0xE5, 0xF9, 0xB4, 0x00, 0x02, 0x41, 0xF7, +0xB4, 0x01, 0x02, 0x80, 0x60, 0xB4, 0x02, 0x02, 0x80, 0x6E, 0xB4, 0x03, 0x02, 0x80, 0x72, 0xB4, +0x04, 0x02, 0x80, 0x79, 0xB4, 0x05, 0x02, 0x80, 0x7D, 0xB4, 0x06, 0x02, 0x41, 0xD2, 0xB4, 0x07, +0x02, 0x41, 0xE3, 0xB4, 0x16, 0x02, 0x80, 0x07, 0xB4, 0x17, 0x02, 0x80, 0x1C, 0x80, 0xC3, 0x30, +0xFD, 0xFD, 0x85, 0xF9, 0x83, 0x30, 0xFD, 0xFD, 0x85, 0xF9, 0x82, 0xE0, 0xF5, 0xF9, 0xA3, 0x20, +0xFE, 0x02, 0x80, 0xF7, 0x20, 0xFC, 0x1C, 0x80, 0xF6, 0x30, 0xFD, 0xFD, 0x85, 0xF9, 0x83, 0x30, +0xFD, 0xFD, 0x85, 0xF9, 0x82, 0x30, 0xFD, 0x06, 0xE5, 0xF9, 0xF0, 0xA3, 0x80, 0xF7, 0x20, 0xFC, +0x02, 0x80, 0xF2, 0x80, 0x8D, 0x30, 0xFD, 0xFD, 0x85, 0xF9, 0x83, 0x30, 0xFD, 0xFD, 0x85, 0xF9, +0x82, 0x74, 0x00, 0x93, 0xF5, 0xF9, 0x41, 0x22, 0x30, 0xFD, 0xFD, 0xA8, 0xF9, 0x86, 0xF9, 0x41, +0x22, 0x30, 0xFD, 0xFD, 0xA8, 0xF9, 0x30, 0xFD, 0xFD, 0xA6, 0xF9, 0x41, 0x22, 0x30, 0xFD, 0xFD, +0xE5, 0xF9, 0xF5, 0xF9, 0x41, 0x22, 0x30, 0xFD, 0xFD, 0xE5, 0xF9, 0x30, 0xFD, 0xFD, 0xF5, 0xF9, +0x41, 0x22, 0x30, 0xFD, 0xFD, 0x85, 0xF9, 0x83, 0x30, 0xFD, 0xFD, 0x85, 0xF9, 0x82, 0xE0, 0xF5, +0xF9, 0x41, 0x22, 0x30, 0xFD, 0xFD, 0x85, 0xF9, 0x83, 0x30, 0xFD, 0xFD, 0x85, 0xF9, 0x82, 0x30, +0xFD, 0xFD, 0xE5, 0xF9, 0xF0, 0x41, 0x22, 0x53, 0x88, 0x5F, 0x90, 0x00, 0x7C, 0xE0, 0x44, 0x20, +0xF0, 0x90, 0x00, 0xB2, 0xE0, 0x44, 0x10, 0xF0, 0xC2, 0xFB, 0xD0, 0x00, 0xD0, 0xD0, 0xD0, 0x82, +0xD0, 0x83, 0xD0, 0xF0, 0xD0, 0xE0, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE5, 0x47, 0x14, 0xB4, +0x1A, 0x00, 0x40, 0x02, 0xC1, 0x32, 0x90, 0x03, 0x30, 0x25, 0xE0, 0x50, 0x02, 0x05, 0x83, 0x73, +0x61, 0x64, 0x61, 0x72, 0x61, 0x80, 0x61, 0x9B, 0x61, 0xA8, 0x61, 0xCA, 0x61, 0xE8, 0x61, 0xF6, +0x81, 0x28, 0x81, 0x36, 0x81, 0x56, 0x81, 0x61, 0x81, 0xA4, 0x81, 0xBD, 0x81, 0xC5, 0x81, 0xFB, +0xA1, 0x03, 0xA1, 0x32, 0xA1, 0x3A, 0xA1, 0x7C, 0xA1, 0x7E, 0xA1, 0xE0, 0xA1, 0xEF, 0xC1, 0x15, +0xC1, 0x23, 0x61, 0x6E, 0xD2, 0x05, 0xE4, 0xF5, 0x0C, 0xF5, 0x0D, 0xF5, 0x47, 0x22, 0x7D, 0x02, +0x80, 0x52, 0x90, 0x00, 0x81, 0x74, 0x01, 0xF0, 0x7D, 0x05, 0x7F, 0x88, 0x7E, 0x13, 0xA1, 0xEC, +0xC3, 0xE5, 0x14, 0x95, 0x41, 0xE5, 0x13, 0x95, 0x40, 0x50, 0x02, 0xC1, 0x32, 0x85, 0x49, 0x47, +0xE4, 0xF5, 0x15, 0xF5, 0x16, 0xF5, 0x17, 0xF5, 0x18, 0x81, 0x9F, 0x90, 0x00, 0x81, 0x74, 0x01, +0xF0, 0x7D, 0x05, 0x7F, 0xF4, 0xFE, 0xA1, 0xEC, 0x12, 0x06, 0x33, 0x50, 0x02, 0xC1, 0x32, 0x12, +0x08, 0xA2, 0x40, 0x08, 0x75, 0x43, 0x07, 0xE4, 0xF5, 0x44, 0x81, 0x51, 0x90, 0x00, 0x81, 0x74, +0x42, 0xF0, 0x7D, 0x06, 0x7F, 0xF4, 0x7E, 0x01, 0xA1, 0xEC, 0x12, 0x06, 0x33, 0x50, 0x02, 0xC1, +0x32, 0x12, 0x08, 0xA2, 0xE4, 0x90, 0x00, 0x81, 0x40, 0x05, 0xF0, 0x7D, 0x07, 0x80, 0x03, 0xF0, +0x7D, 0x09, 0x7F, 0xF4, 0x7E, 0x01, 0xA1, 0xEC, 0x90, 0x00, 0x81, 0x74, 0x14, 0xF0, 0x7D, 0x08, +0x7F, 0xF4, 0x7E, 0x01, 0xA1, 0xEC, 0x12, 0x06, 0x51, 0x12, 0x08, 0xBE, 0x50, 0x02, 0xC1, 0x32, +0x12, 0x08, 0xA2, 0x40, 0x11, 0x75, 0x43, 0x04, 0xE4, 0xF5, 0x44, 0x7D, 0x0B, 0x7F, 0xF4, 0x7E, +0x01, 0x12, 0x0B, 0x4D, 0x80, 0x0C, 0x75, 0x43, 0x03, 0xE4, 0xF5, 0x44, 0x75, 0x47, 0x01, 0x12, +0x0B, 0x3E, 0xE4, 0x90, 0x00, 0x81, 0xF0, 0x22, 0x90, 0x00, 0x81, 0x74, 0x21, 0xF0, 0x7D, 0x0A, +0x7F, 0xF4, 0x7E, 0x01, 0xA1, 0xEC, 0x12, 0x06, 0x51, 0x12, 0x08, 0xBE, 0x50, 0x02, 0xC1, 0x32, +0x12, 0x08, 0xA2, 0x40, 0x05, 0x75, 0x43, 0x01, 0x80, 0x03, 0x75, 0x43, 0x02, 0xE4, 0xF5, 0x44, +0xE4, 0x90, 0x00, 0x81, 0xC1, 0x0E, 0x12, 0x08, 0xF1, 0x7D, 0x0C, 0x7F, 0xF4, 0x7E, 0x01, 0xA1, +0xEC, 0xC3, 0xE5, 0x16, 0x94, 0x98, 0xE5, 0x15, 0x94, 0x3A, 0x40, 0x02, 0xA1, 0x7C, 0x7F, 0x01, +0x12, 0x08, 0x00, 0x8E, 0x55, 0x8F, 0x56, 0xC3, 0x12, 0x08, 0xA2, 0x50, 0x21, 0xC3, 0x12, 0x08, +0xBE, 0x50, 0x02, 0xC1, 0x32, 0xE5, 0x16, 0x94, 0x40, 0xE5, 0x15, 0x94, 0x1F, 0x40, 0x0B, 0xE4, +0xF5, 0x44, 0x7D, 0x0D, 0x7F, 0xD0, 0x7E, 0x07, 0xA1, 0xEC, 0x75, 0x47, 0x01, 0x22, 0xE4, 0xF5, +0x0A, 0xF5, 0x0B, 0x22, 0x90, 0x00, 0x81, 0x74, 0x48, 0xF0, 0x7B, 0x0E, 0x7D, 0x10, 0x7C, 0x00, +0x7F, 0x18, 0x12, 0x0A, 0x77, 0x7D, 0x18, 0x7F, 0xB0, 0x7E, 0x04, 0xA1, 0xEC, 0x7D, 0x0F, 0x7F, +0xF4, 0x7E, 0x01, 0xA1, 0xEC, 0x12, 0x06, 0x46, 0xE5, 0x56, 0x94, 0x9B, 0xE5, 0x55, 0x94, 0x01, +0x40, 0x1B, 0x7B, 0x10, 0x7D, 0x10, 0x7C, 0x00, 0x7F, 0x19, 0x12, 0x0A, 0x77, 0x7D, 0x19, 0x7F, +0xF4, 0x7E, 0x01, 0x12, 0x0B, 0x4D, 0x75, 0x43, 0x06, 0xE4, 0xF5, 0x44, 0x22, 0x75, 0x44, 0x03, +0x12, 0x08, 0xF1, 0x7D, 0x16, 0x7F, 0xE8, 0x7E, 0x03, 0xA1, 0xEC, 0x7D, 0x11, 0x7F, 0xB0, 0x7E, +0x04, 0xA1, 0xEC, 0x12, 0x08, 0xF8, 0x40, 0x02, 0x80, 0x35, 0x12, 0x06, 0x46, 0xE5, 0x56, 0x94, +0x17, 0xE5, 0x55, 0x94, 0x01, 0x40, 0x15, 0xE5, 0x18, 0x94, 0x64, 0xE5, 0x17, 0x94, 0x00, 0x50, +0x02, 0xC1, 0x32, 0x7B, 0x12, 0x7D, 0x06, 0x7F, 0xA8, 0x02, 0x0B, 0x18, 0xE4, 0xF5, 0x17, 0xF5, +0x18, 0x22, 0x7D, 0x13, 0x7F, 0xC8, 0x7E, 0x00, 0xA1, 0xEC, 0x12, 0x08, 0xF8, 0x40, 0x05, 0x75, +0x44, 0x03, 0xC1, 0x0F, 0x12, 0x06, 0x5C, 0xE5, 0x18, 0x94, 0x64, 0xE5, 0x17, 0x94, 0x00, 0x50, +0x02, 0xC1, 0x32, 0xE5, 0x56, 0x94, 0x81, 0xE5, 0x55, 0x94, 0x01, 0x40, 0x08, 0x12, 0x06, 0x67, +0x75, 0x43, 0x09, 0x80, 0x13, 0xC3, 0xE5, 0x56, 0x94, 0x4C, 0xE5, 0x55, 0x94, 0x01, 0x50, 0x02, +0xC1, 0x32, 0x12, 0x06, 0x67, 0x75, 0x43, 0x08, 0x75, 0x44, 0x04, 0x22, 0xC1, 0x0F, 0x12, 0x08, +0x98, 0x40, 0x02, 0xC1, 0x2F, 0xC3, 0xE5, 0x09, 0x95, 0x42, 0xE5, 0x08, 0x94, 0x00, 0x50, 0x02, +0xC1, 0x32, 0x90, 0x00, 0x81, 0xE0, 0xF5, 0x54, 0xE5, 0x4C, 0x30, 0xE7, 0x1B, 0x53, 0x54, 0x0F, +0x20, 0x04, 0x07, 0xD2, 0x04, 0x43, 0x54, 0x80, 0x80, 0x19, 0xC2, 0x04, 0x43, 0x54, 0x40, 0x12, +0x08, 0xB6, 0x70, 0x20, 0x05, 0x45, 0x80, 0x1C, 0x53, 0x54, 0xF0, 0x30, 0x03, 0x0A, 0xC2, 0x03, +0x43, 0x54, 0x04, 0x75, 0x42, 0x16, 0x80, 0x12, 0xD2, 0x03, 0x43, 0x54, 0x08, 0x12, 0x08, 0xB6, +0x70, 0x02, 0x05, 0x45, 0xE5, 0x4C, 0x25, 0xE0, 0xF5, 0x4C, 0xE5, 0x54, 0x12, 0x08, 0xAC, 0x22, +0x90, 0x00, 0x81, 0x74, 0x84, 0xF0, 0x7D, 0x17, 0x7F, 0xE8, 0x7E, 0x03, 0x02, 0x0B, 0x4D, 0x12, +0x06, 0x5C, 0xE5, 0x18, 0x94, 0x32, 0xE5, 0x17, 0x94, 0x00, 0x40, 0x36, 0xE5, 0x56, 0x94, 0xC0, +0xE5, 0x55, 0x94, 0x01, 0x40, 0x03, 0x75, 0x43, 0x05, 0x90, 0x00, 0x81, 0x74, 0x40, 0xF0, 0x75, +0x47, 0x01, 0x02, 0x0B, 0x3E, 0x12, 0x09, 0x97, 0x8F, 0x54, 0xE5, 0x54, 0xD3, 0x94, 0x00, 0x40, +0x11, 0x80, 0x0C, 0x12, 0x09, 0xCD, 0x8F, 0x54, 0xE5, 0x54, 0xD3, 0x94, 0x00, 0x40, 0x03, 0x85, +0x3F, 0x47, 0x22, 0x7F, 0x01, 0x12, 0x08, 0x00, 0x8E, 0x55, 0x8F, 0x56, 0xC3, 0xE5, 0x0B, 0x94, +0x32, 0xE5, 0x0A, 0x94, 0x00, 0x22, 0x7F, 0x02, 0x12, 0x08, 0x00, 0x8E, 0x55, 0x8F, 0x56, 0xC3, +0x22, 0xE4, 0xFF, 0x12, 0x08, 0x00, 0x8E, 0x55, 0x8F, 0x56, 0xC3, 0x22, 0x7F, 0x02, 0x12, 0x08, +0x00, 0x8E, 0x55, 0x8F, 0x56, 0xC3, 0x22, 0x7B, 0x14, 0x7D, 0x04, 0x7F, 0xC0, 0x12, 0x0B, 0x18, +0x22, 0x7F, 0x02, 0x12, 0x08, 0x00, 0x8E, 0x54, 0x8F, 0x55, 0xE5, 0x4D, 0x14, 0x60, 0x17, 0x14, +0x60, 0x6D, 0x24, 0x02, 0x60, 0x02, 0xE1, 0x1C, 0x12, 0x08, 0xD3, 0x50, 0x02, 0xE1, 0x1C, 0xE4, +0xF5, 0x19, 0xF5, 0x1A, 0x80, 0x4E, 0xC3, 0xE5, 0x55, 0x94, 0xB8, 0xE5, 0x54, 0x94, 0x00, 0x40, +0x24, 0xD3, 0xE5, 0x55, 0x94, 0x3C, 0xE5, 0x54, 0x94, 0x01, 0x50, 0x19, 0x12, 0x08, 0xDD, 0x40, +0x19, 0xE4, 0xF5, 0x11, 0xF5, 0x12, 0x75, 0x4D, 0x02, 0x90, 0x00, 0x81, 0xF0, 0x90, 0x00, 0x80, +0x74, 0xC0, 0xF0, 0x80, 0x05, 0xE4, 0xF5, 0x11, 0xF5, 0x12, 0x12, 0x08, 0xE7, 0x50, 0x18, 0x12, +0x08, 0xD3, 0x40, 0x48, 0xE4, 0xF5, 0x19, 0xF5, 0x1A, 0x05, 0x48, 0xE5, 0x48, 0x94, 0x0A, 0x40, +0x3B, 0xE4, 0xF5, 0x48, 0x02, 0x0A, 0x98, 0xE4, 0xF5, 0x48, 0xF5, 0x19, 0xF5, 0x1A, 0x22, 0x12, +0x08, 0xE7, 0x50, 0x23, 0x12, 0x08, 0xDD, 0x40, 0x23, 0x75, 0x43, 0x11, 0xE4, 0xF5, 0x44, 0xF5, +0x11, 0xF5, 0x12, 0xF5, 0x19, 0xF5, 0x1A, 0xF5, 0x48, 0x75, 0x4D, 0x01, 0xF5, 0x47, 0xC2, 0x05, +0x90, 0x00, 0x81, 0xF0, 0x02, 0x0B, 0x02, 0xE4, 0xF5, 0x11, 0xF5, 0x12, 0x22, 0xBB, 0x01, 0x06, +0x89, 0x82, 0x8A, 0x83, 0xE0, 0x22, 0x50, 0x02, 0xE7, 0x22, 0xBB, 0xFE, 0x02, 0xE3, 0x22, 0x89, +0x82, 0x8A, 0x83, 0xE4, 0x93, 0x22, 0xBB, 0x01, 0x0C, 0xE5, 0x82, 0x29, 0xF5, 0x82, 0xE5, 0x83, +0x3A, 0xF5, 0x83, 0xE0, 0x22, 0x50, 0x06, 0xE9, 0x25, 0x82, 0xF8, 0xE6, 0x22, 0xBB, 0xFE, 0x06, +0xE9, 0x25, 0x82, 0xF8, 0xE2, 0x22, 0xE5, 0x82, 0x29, 0xF5, 0x82, 0xE5, 0x83, 0x3A, 0xF5, 0x83, +0xE4, 0x93, 0x22, 0xBC, 0x00, 0x0B, 0xBE, 0x00, 0x29, 0xEF, 0x8D, 0xF0, 0x84, 0xFF, 0xAD, 0xF0, +0x22, 0xE4, 0xCC, 0xF8, 0x75, 0xF0, 0x08, 0xEF, 0x2F, 0xFF, 0xEE, 0x33, 0xFE, 0xEC, 0x33, 0xFC, +0xEE, 0x9D, 0xEC, 0x98, 0x40, 0x05, 0xFC, 0xEE, 0x9D, 0xFE, 0x0F, 0xD5, 0xF0, 0xE9, 0xE4, 0xCE, +0xFD, 0x22, 0xED, 0xF8, 0xF5, 0xF0, 0xEE, 0x84, 0x20, 0xD2, 0x1C, 0xFE, 0xAD, 0xF0, 0x75, 0xF0, +0x08, 0xEF, 0x2F, 0xFF, 0xED, 0x33, 0xFD, 0x40, 0x07, 0x98, 0x50, 0x06, 0xD5, 0xF0, 0xF2, 0x22, +0xC3, 0x98, 0xFD, 0x0F, 0xD5, 0xF0, 0xEA, 0x22, 0xC0, 0xE0, 0xC0, 0xF0, 0xC0, 0x83, 0xC0, 0x82, +0xC0, 0xD0, 0x75, 0xD0, 0x00, 0xC0, 0x00, 0xC0, 0x01, 0xC0, 0x02, 0xC0, 0x03, 0xC0, 0x04, 0xC0, +0x05, 0xC0, 0x06, 0xC0, 0x07, 0x90, 0x00, 0x35, 0xE0, 0x30, 0xE1, 0x03, 0x12, 0x01, 0x59, 0xD0, +0x07, 0xD0, 0x06, 0xD0, 0x05, 0xD0, 0x04, 0xD0, 0x03, 0xD0, 0x02, 0xD0, 0x01, 0xD0, 0x00, 0xD0, +0xD0, 0xD0, 0x82, 0xD0, 0x83, 0xD0, 0xF0, 0xD0, 0xE0, 0x32, 0x12, 0x0A, 0xB8, 0x02, 0x0B, 0x5C, +0xA9, 0x07, 0x90, 0x08, 0x10, 0x12, 0x0A, 0xE4, 0x90, 0x08, 0x11, 0xE0, 0x7C, 0x00, 0x2F, 0xFF, +0xEC, 0x35, 0xF0, 0xAD, 0x07, 0xFC, 0x90, 0x08, 0x12, 0x12, 0x0A, 0xE4, 0xAE, 0xF0, 0x90, 0x08, +0x13, 0xE0, 0x7A, 0x00, 0x2F, 0xF5, 0x58, 0xEA, 0x3E, 0xF5, 0x57, 0x90, 0x00, 0xD3, 0xE9, 0xF0, +0x00, 0x00, 0x90, 0x00, 0xD0, 0xE0, 0x44, 0x40, 0xF0, 0x00, 0x90, 0x00, 0xD2, 0xE0, 0x30, 0xE2, +0xF8, 0x90, 0x00, 0xD6, 0x12, 0x0A, 0xE4, 0x90, 0x00, 0xD7, 0xE0, 0x54, 0x03, 0x7A, 0x00, 0x2F, +0xFF, 0xEA, 0x35, 0xF0, 0xFE, 0xE9, 0x64, 0x02, 0x70, 0x3B, 0xD3, 0xEF, 0x94, 0x0A, 0xEE, 0x94, +0x00, 0x40, 0x32, 0xE4, 0x2D, 0xFB, 0xEC, 0x34, 0x01, 0xFA, 0xC3, 0xEF, 0x9B, 0xEE, 0x9A, 0x50, +0x24, 0xC3, 0xEF, 0x9D, 0xEE, 0x9C, 0x40, 0x0E, 0xEF, 0x9D, 0x12, 0x0A, 0xD3, 0xEF, 0x24, 0x07, +0xFF, 0xEE, 0x34, 0x01, 0xFE, 0x22, 0xC3, 0xED, 0x9F, 0x12, 0x0A, 0xD3, 0xC3, 0x74, 0x07, 0x9F, +0xFF, 0x74, 0x01, 0x9E, 0xFE, 0x22, 0xE4, 0xFF, 0xC3, 0xE5, 0x46, 0x95, 0x4B, 0xE5, 0x45, 0x95, +0x4A, 0x22, 0xE5, 0x56, 0x94, 0xAA, 0xE5, 0x55, 0x94, 0x00, 0x22, 0xEE, 0x90, 0x00, 0x81, 0xF0, +0xE4, 0xF5, 0x08, 0xF5, 0x09, 0x22, 0x75, 0x42, 0x16, 0x05, 0x46, 0xE5, 0x46, 0x22, 0xE5, 0x0B, +0x94, 0x32, 0xE5, 0x0A, 0x94, 0x00, 0x22, 0xE5, 0x42, 0xFD, 0xC3, 0xE5, 0x09, 0x9D, 0xE5, 0x08, +0x94, 0x00, 0x22, 0xC3, 0xE5, 0x1A, 0x94, 0x10, 0xE5, 0x19, 0x94, 0x27, 0x22, 0xC3, 0xE5, 0x12, +0x94, 0xC8, 0xE5, 0x11, 0x94, 0x00, 0x22, 0xD3, 0xE5, 0x55, 0x94, 0x9E, 0xE5, 0x54, 0x94, 0x00, +0x22, 0x90, 0x00, 0x81, 0x74, 0x40, 0xF0, 0x22, 0xC3, 0xE5, 0x16, 0x94, 0xD0, 0xE5, 0x15, 0x94, +0x07, 0x22, 0x75, 0x08, 0x00, 0x75, 0x09, 0x16, 0x75, 0x42, 0x16, 0x22, 0xC0, 0xD0, 0x75, 0xD0, +0x00, 0x90, 0x00, 0x73, 0x74, 0x80, 0xF0, 0x90, 0x00, 0x78, 0xE0, 0x44, 0xA0, 0xF0, 0x12, 0x0A, +0xEB, 0x12, 0x0B, 0x2E, 0x12, 0x07, 0xFA, 0x12, 0x00, 0x0E, 0x12, 0x0B, 0x02, 0x90, 0x08, 0x10, +0xE0, 0xF5, 0x52, 0x90, 0x08, 0x11, 0xE0, 0xF5, 0x53, 0x90, 0x08, 0x12, 0xE0, 0xF5, 0x4E, 0x90, +0x08, 0x13, 0xE0, 0xF5, 0x4F, 0xD2, 0xB8, 0xD2, 0xA8, 0xD2, 0xAF, 0x12, 0x0A, 0x03, 0x12, 0x00, +0x46, 0x12, 0x06, 0x71, 0x12, 0x03, 0x1C, 0x12, 0x00, 0x7E, 0x80, 0xEF, 0x90, 0x00, 0x01, 0x74, +0xB8, 0xF0, 0x90, 0x00, 0x07, 0x74, 0xA4, 0xF0, 0xE4, 0x90, 0x00, 0x20, 0xF0, 0x90, 0x00, 0x10, +0xF0, 0x90, 0x00, 0x12, 0xF0, 0x90, 0x00, 0x80, 0xF0, 0x90, 0x00, 0x81, 0xF0, 0x90, 0x00, 0xD0, +0x74, 0x80, 0xF0, 0x90, 0x00, 0x60, 0x74, 0x20, 0xF0, 0x90, 0x00, 0x61, 0x74, 0x08, 0xF0, 0x90, +0x00, 0x6A, 0xE0, 0x44, 0x80, 0xF0, 0x22, 0x12, 0x08, 0x96, 0x50, 0x29, 0x12, 0x08, 0xC7, 0x40, +0x24, 0x90, 0x00, 0x81, 0xE0, 0xFE, 0x53, 0x06, 0x0F, 0x20, 0x04, 0x0A, 0xD2, 0x04, 0x43, 0x06, +0x80, 0x75, 0x42, 0x16, 0x80, 0x0C, 0xC2, 0x04, 0x43, 0x06, 0x40, 0x12, 0x08, 0xB6, 0x70, 0x02, +0x05, 0x45, 0x12, 0x08, 0xAB, 0x12, 0x08, 0x98, 0x40, 0x02, 0x7F, 0x01, 0x22, 0x12, 0x08, 0x96, +0x50, 0x29, 0x12, 0x08, 0xC7, 0x40, 0x24, 0x90, 0x00, 0x81, 0xE0, 0xFE, 0x53, 0x06, 0xF0, 0x30, +0x03, 0x0A, 0xC2, 0x03, 0x43, 0x06, 0x04, 0x75, 0x42, 0x16, 0x80, 0x0C, 0xD2, 0x03, 0x43, 0x06, +0x08, 0x12, 0x08, 0xB6, 0x70, 0x02, 0x05, 0x45, 0x12, 0x08, 0xAB, 0x12, 0x08, 0x98, 0x40, 0x02, +0x7F, 0x01, 0x22, 0xAF, 0x0E, 0xEF, 0xC3, 0x94, 0x01, 0x40, 0x20, 0xE4, 0xF5, 0x0E, 0xFE, 0xEF, +0xFD, 0xEE, 0x25, 0xE0, 0x24, 0x0F, 0x12, 0x0B, 0x6A, 0xBE, 0x07, 0xF3, 0xE4, 0xFE, 0xEF, 0xFD, +0xEE, 0x25, 0xE0, 0x24, 0x08, 0x12, 0x0B, 0x6A, 0xBE, 0x03, 0xF3, 0x22, 0xEF, 0x24, 0xFE, 0x60, +0x0E, 0x14, 0x60, 0x12, 0x14, 0x60, 0x16, 0x24, 0x03, 0x70, 0x18, 0x12, 0x08, 0xF1, 0x22, 0x90, +0x00, 0x81, 0x74, 0x84, 0xF0, 0x22, 0x90, 0x00, 0x81, 0x74, 0x44, 0xF0, 0x22, 0x90, 0x00, 0x81, +0x74, 0x48, 0xF0, 0x22, 0x75, 0xA0, 0x00, 0x75, 0x81, 0x58, 0x78, 0x7F, 0xE4, 0xF6, 0xD8, 0xFD, +0x90, 0x01, 0x00, 0x7F, 0x00, 0x7E, 0x01, 0xE4, 0xF0, 0xA3, 0xDF, 0xFC, 0xDE, 0xFA, 0x43, 0xB8, +0x00, 0x75, 0xA8, 0x00, 0x02, 0x09, 0x0C, 0xE4, 0xF5, 0x45, 0xF5, 0x46, 0x8C, 0x4A, 0x8D, 0x4B, +0xBF, 0x18, 0x07, 0xC2, 0x04, 0x75, 0x47, 0x18, 0x80, 0x08, 0xBF, 0x19, 0x05, 0xD2, 0x03, 0x75, +0x47, 0x19, 0x12, 0x09, 0x02, 0x8B, 0x3F, 0x22, 0x12, 0x09, 0x5C, 0x00, 0x00, 0x00, 0x90, 0x00, +0x06, 0x74, 0x52, 0xF0, 0x00, 0x00, 0x00, 0x90, 0x00, 0x7C, 0xE0, 0x44, 0x80, 0xF0, 0x12, 0x0A, +0xEB, 0x12, 0x07, 0xFA, 0x75, 0x4D, 0x01, 0x22, 0x90, 0x00, 0xA4, 0x74, 0x6A, 0xF0, 0x90, 0x00, +0xA1, 0xE0, 0x44, 0x01, 0xF0, 0x90, 0x00, 0xA0, 0x74, 0x83, 0xF0, 0x90, 0x00, 0x30, 0xE0, 0x44, +0x02, 0xF0, 0x22, 0xFF, 0xEF, 0x7F, 0x00, 0xFE, 0xC3, 0xE5, 0x58, 0x9D, 0xFD, 0xE5, 0x57, 0x9C, +0xFC, 0x02, 0x07, 0x63, 0xE0, 0x75, 0xF0, 0x04, 0xA4, 0xFF, 0x22, 0x90, 0x00, 0x11, 0x74, 0x08, +0xF0, 0x90, 0x00, 0x10, 0xF0, 0x90, 0x00, 0x12, 0x74, 0x03, 0xF0, 0x90, 0x00, 0x20, 0x74, 0x34, +0xF0, 0x22, 0x7F, 0x10, 0x78, 0x2B, 0xE4, 0xF6, 0x08, 0xDF, 0xFC, 0x75, 0x2E, 0x01, 0x75, 0x31, +0x01, 0x75, 0x43, 0x11, 0xE4, 0xF5, 0x44, 0x22, 0xE4, 0xF5, 0x45, 0xF5, 0x46, 0xED, 0x75, 0x4A, +0x00, 0xF5, 0x4B, 0x8F, 0x4C, 0x75, 0x47, 0x15, 0x8B, 0x3F, 0x12, 0x09, 0x02, 0x22, 0xC2, 0x8C, +0x43, 0x89, 0x02, 0x75, 0x8A, 0x9C, 0x75, 0x8C, 0x9C, 0xD2, 0x8C, 0xD2, 0xA9, 0x22, 0x90, 0x00, +0x11, 0xE0, 0x54, 0xF7, 0xF0, 0xD2, 0x06, 0xE4, 0xF5, 0x1B, 0xF5, 0x1C, 0x22, 0xE4, 0xF5, 0x13, +0xF5, 0x14, 0x8E, 0x40, 0x8F, 0x41, 0x75, 0x47, 0x03, 0x8D, 0x49, 0x22, 0x90, 0x00, 0xD1, 0x74, +0x81, 0xF0, 0x90, 0x00, 0xD0, 0xE0, 0x54, 0x7F, 0xF0, 0x22, 0xF8, 0x08, 0xED, 0x26, 0xF6, 0x18, +0xE4, 0x36, 0xF6, 0x0E, 0x22, 0x90, 0x00, 0xA0, 0xE0, 0x44, 0x02, 0xF0, 0x22 +}; +unsigned int WT70F_QC3p_V02_210326_FBA1_bin_len = 2941; +#endif +#if 0 +unsigned char WT70F_QC3p_V02_210326_FBA1_bin[] = {//with id EB84 + 0x02, 0x07, 0xca, 0x02, 0x0a, 0x38, 0xc2, 0x8d, 0x05, 0x0e, 0x32, 0x02, + 0x00, 0x06, 0x75, 0x4e, 0x01, 0x22, 0x32, 0x02, 0x00, 0x12, 0xe5, 0x25, + 0x30, 0xe7, 0x05, 0x75, 0x55, 0x18, 0x80, 0x03, 0x75, 0x55, 0x19, 0xe5, + 0x25, 0x54, 0x7f, 0xfe, 0x7c, 0x00, 0xe4, 0x25, 0x26, 0xff, 0xec, 0x3e, + 0xad, 0x07, 0xfc, 0xe4, 0xfb, 0xaf, 0x55, 0x02, 0x0b, 0x37, 0x32, 0x02, + 0x00, 0x3a, 0x03, 0x00, 0x32, 0xff, 0xff, 0x02, 0x00, 0x40, 0x90, 0x00, + 0x01, 0x74, 0xb8, 0xf0, 0x90, 0x00, 0x07, 0x74, 0xa4, 0xf0, 0xe4, 0x90, + 0x00, 0x20, 0xf0, 0x90, 0x00, 0x10, 0xf0, 0x90, 0x00, 0x12, 0xf0, 0x90, + 0x00, 0x80, 0xf0, 0x90, 0x00, 0x81, 0xf0, 0x90, 0x00, 0xd0, 0x74, 0x80, + 0xf0, 0x90, 0x00, 0x60, 0x74, 0x20, 0xf0, 0x90, 0x00, 0x6a, 0xe0, 0x44, + 0x80, 0xf0, 0x22, 0x02, 0x02, 0x00, 0x20, 0x05, 0x02, 0x21, 0x58, 0xaf, + 0x24, 0xef, 0xd3, 0x94, 0xb5, 0x50, 0x06, 0x74, 0x2c, 0x2f, 0xf8, 0xa6, + 0x25, 0xef, 0x24, 0x50, 0xb4, 0x0c, 0x00, 0x40, 0x02, 0x21, 0x53, 0x90, + 0x00, 0xa5, 0x25, 0xe0, 0x50, 0x02, 0x05, 0x83, 0x73, 0x01, 0xbd, 0x01, + 0xcf, 0x01, 0xd6, 0x01, 0xe2, 0x01, 0xfb, 0x21, 0x1e, 0x21, 0x2d, 0x21, + 0x53, 0x21, 0x53, 0x21, 0x53, 0x21, 0x42, 0x21, 0x4a, 0x75, 0x2d, 0x01, + 0xe4, 0xf5, 0x31, 0x90, 0x00, 0x81, 0xf0, 0x7d, 0x04, 0x7f, 0xf4, 0x7e, + 0x01, 0x80, 0x6e, 0xaf, 0x25, 0x12, 0x0b, 0x0f, 0x80, 0x7d, 0xe5, 0x25, + 0x70, 0x04, 0xc2, 0x02, 0x80, 0x75, 0xd2, 0x02, 0x80, 0x71, 0xe4, 0x90, + 0x00, 0x81, 0xf0, 0x90, 0x00, 0x80, 0xf0, 0x12, 0x0b, 0xcb, 0x75, 0x30, + 0x01, 0x75, 0x4e, 0x01, 0xe4, 0xf5, 0x11, 0xf5, 0x12, 0x80, 0x58, 0xe5, + 0x25, 0xb4, 0x01, 0x0a, 0x75, 0x4e, 0x01, 0xe4, 0xf5, 0x11, 0xf5, 0x12, + 0x80, 0x0e, 0xe4, 0x90, 0x00, 0x81, 0xf0, 0x75, 0x44, 0x11, 0xf5, 0x45, + 0xf5, 0x48, 0xf5, 0x4e, 0xf5, 0x19, 0xf5, 0x1a, 0x80, 0x35, 0xe5, 0x25, + 0x64, 0x01, 0x70, 0x2f, 0xf5, 0x11, 0xf5, 0x12, 0x12, 0x0b, 0x78, 0x80, + 0x26, 0xe5, 0x25, 0xb4, 0x01, 0x21, 0xe5, 0x4e, 0xb4, 0x02, 0x1c, 0x7d, + 0x02, 0x7f, 0xe8, 0x7e, 0x03, 0x12, 0x0c, 0x19, 0x80, 0x11, 0x85, 0x25, + 0x34, 0x85, 0x26, 0x35, 0x80, 0x06, 0x85, 0x25, 0x36, 0x85, 0x26, 0x37, + 0x12, 0x00, 0x16, 0xe4, 0xf5, 0x24, 0xc2, 0x05, 0x30, 0x06, 0x17, 0xe5, + 0x24, 0x24, 0x4d, 0x60, 0x09, 0x24, 0x03, 0x70, 0x08, 0xe4, 0xf5, 0x2d, + 0x80, 0x03, 0xe4, 0xf5, 0x30, 0xe4, 0xf5, 0x24, 0xc2, 0x06, 0x22, 0xe4, + 0xff, 0xc3, 0xe5, 0x47, 0x95, 0x4c, 0xe5, 0x46, 0x95, 0x4b, 0x22, 0xe5, + 0x57, 0x94, 0xaa, 0xe5, 0x56, 0x94, 0x00, 0x22, 0xee, 0x90, 0x00, 0x81, + 0xf0, 0xe4, 0xf5, 0x08, 0xf5, 0x09, 0x22, 0x75, 0x43, 0x16, 0x05, 0x47, + 0xe5, 0x47, 0x22, 0xd3, 0xe5, 0x56, 0x94, 0x9e, 0xe5, 0x55, 0x94, 0x00, + 0x22, 0xe5, 0x0b, 0x94, 0x32, 0xe5, 0x0a, 0x94, 0x00, 0x22, 0xe5, 0x43, + 0xfd, 0xc3, 0xe5, 0x09, 0x9d, 0xe5, 0x08, 0x94, 0x00, 0x22, 0xc3, 0xe5, + 0x1a, 0x94, 0x10, 0xe5, 0x19, 0x94, 0x27, 0x22, 0xc3, 0xe5, 0x12, 0x94, + 0xc8, 0xe5, 0x11, 0x94, 0x00, 0x22, 0x90, 0x00, 0x81, 0x74, 0x40, 0xf0, + 0x22, 0xc3, 0xe5, 0x16, 0x94, 0xd0, 0xe5, 0x15, 0x94, 0x07, 0x22, 0x75, + 0x08, 0x00, 0x75, 0x09, 0x16, 0x75, 0x43, 0x16, 0x22, 0x90, 0x00, 0x11, + 0x74, 0x08, 0xf0, 0x90, 0x00, 0x10, 0xf0, 0x90, 0x00, 0x12, 0x74, 0x03, + 0xf0, 0x90, 0x00, 0x20, 0x74, 0x34, 0xf0, 0x22, 0xc0, 0xe0, 0xc0, 0xf0, + 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0xc0, 0x00, 0x75, 0xd0, 0x00, 0xc3, + 0xe5, 0x81, 0x94, 0x07, 0xf8, 0xa6, 0xfd, 0x08, 0xa6, 0xf5, 0x90, 0x00, + 0x78, 0x74, 0xa0, 0xf0, 0xd2, 0xfb, 0x74, 0xa0, 0x55, 0xf8, 0xb4, 0xa0, + 0xf9, 0xe5, 0xf9, 0xb4, 0x00, 0x02, 0x41, 0xf7, 0xb4, 0x01, 0x02, 0x80, + 0x60, 0xb4, 0x02, 0x02, 0x80, 0x6e, 0xb4, 0x03, 0x02, 0x80, 0x72, 0xb4, + 0x04, 0x02, 0x80, 0x79, 0xb4, 0x05, 0x02, 0x80, 0x7d, 0xb4, 0x06, 0x02, + 0x41, 0xd2, 0xb4, 0x07, 0x02, 0x41, 0xe3, 0xb4, 0x16, 0x02, 0x80, 0x07, + 0xb4, 0x17, 0x02, 0x80, 0x1c, 0x80, 0xc3, 0x30, 0xfd, 0xfd, 0x85, 0xf9, + 0x83, 0x30, 0xfd, 0xfd, 0x85, 0xf9, 0x82, 0xe0, 0xf5, 0xf9, 0xa3, 0x20, + 0xfe, 0x02, 0x80, 0xf7, 0x20, 0xfc, 0x1c, 0x80, 0xf6, 0x30, 0xfd, 0xfd, + 0x85, 0xf9, 0x83, 0x30, 0xfd, 0xfd, 0x85, 0xf9, 0x82, 0x30, 0xfd, 0x06, + 0xe5, 0xf9, 0xf0, 0xa3, 0x80, 0xf7, 0x20, 0xfc, 0x02, 0x80, 0xf2, 0x80, + 0x8d, 0x30, 0xfd, 0xfd, 0x85, 0xf9, 0x83, 0x30, 0xfd, 0xfd, 0x85, 0xf9, + 0x82, 0x74, 0x00, 0x93, 0xf5, 0xf9, 0x41, 0x22, 0x30, 0xfd, 0xfd, 0xa8, + 0xf9, 0x86, 0xf9, 0x41, 0x22, 0x30, 0xfd, 0xfd, 0xa8, 0xf9, 0x30, 0xfd, + 0xfd, 0xa6, 0xf9, 0x41, 0x22, 0x30, 0xfd, 0xfd, 0xe5, 0xf9, 0xf5, 0xf9, + 0x41, 0x22, 0x30, 0xfd, 0xfd, 0xe5, 0xf9, 0x30, 0xfd, 0xfd, 0xf5, 0xf9, + 0x41, 0x22, 0x30, 0xfd, 0xfd, 0x85, 0xf9, 0x83, 0x30, 0xfd, 0xfd, 0x85, + 0xf9, 0x82, 0xe0, 0xf5, 0xf9, 0x41, 0x22, 0x30, 0xfd, 0xfd, 0x85, 0xf9, + 0x83, 0x30, 0xfd, 0xfd, 0x85, 0xf9, 0x82, 0x30, 0xfd, 0xfd, 0xe5, 0xf9, + 0xf0, 0x41, 0x22, 0x53, 0x88, 0x5f, 0x90, 0x00, 0x7c, 0xe0, 0x44, 0x20, + 0xf0, 0x90, 0x00, 0xb2, 0xe0, 0x44, 0x10, 0xf0, 0xc2, 0xfb, 0xd0, 0x00, + 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe5, 0x48, 0x14, 0xb4, 0x19, 0x00, 0x40, 0x02, + 0xc1, 0x2c, 0x90, 0x03, 0x30, 0x25, 0xe0, 0x50, 0x02, 0x05, 0x83, 0x73, + 0x61, 0x62, 0x61, 0x6c, 0x61, 0x7a, 0x61, 0x95, 0x61, 0xa2, 0x61, 0xc4, + 0x61, 0xe2, 0x61, 0xf0, 0x81, 0x22, 0x81, 0x30, 0x81, 0x50, 0x81, 0x5b, + 0x81, 0x9e, 0x81, 0xb7, 0x81, 0xbf, 0x81, 0xf5, 0x81, 0xfd, 0xa1, 0x2c, + 0xa1, 0x34, 0xa1, 0x76, 0xa1, 0x78, 0xa1, 0xda, 0xa1, 0xe9, 0xc1, 0x0f, + 0xc1, 0x1d, 0xd2, 0x03, 0xe4, 0xf5, 0x0c, 0xf5, 0x0d, 0xf5, 0x48, 0x22, + 0x90, 0x00, 0x81, 0x74, 0x01, 0xf0, 0x7d, 0x05, 0x7f, 0x88, 0x7e, 0x13, + 0xa1, 0xe6, 0xc3, 0xe5, 0x14, 0x95, 0x42, 0xe5, 0x13, 0x95, 0x41, 0x50, + 0x02, 0xc1, 0x2c, 0x85, 0x4a, 0x48, 0xe4, 0xf5, 0x15, 0xf5, 0x16, 0xf5, + 0x17, 0xf5, 0x18, 0x81, 0x99, 0x90, 0x00, 0x81, 0x74, 0x01, 0xf0, 0x7d, + 0x05, 0x7f, 0xf4, 0xfe, 0xa1, 0xe6, 0x12, 0x06, 0x2d, 0x50, 0x02, 0xc1, + 0x2c, 0x12, 0x01, 0x7f, 0x40, 0x08, 0x75, 0x44, 0x07, 0xe4, 0xf5, 0x45, + 0x81, 0x4b, 0x90, 0x00, 0x81, 0x74, 0x42, 0xf0, 0x7d, 0x06, 0x7f, 0xf4, + 0x7e, 0x01, 0xa1, 0xe6, 0x12, 0x06, 0x2d, 0x50, 0x02, 0xc1, 0x2c, 0x12, + 0x01, 0x7f, 0xe4, 0x90, 0x00, 0x81, 0x40, 0x05, 0xf0, 0x7d, 0x07, 0x80, + 0x03, 0xf0, 0x7d, 0x09, 0x7f, 0xf4, 0x7e, 0x01, 0xa1, 0xe6, 0x90, 0x00, + 0x81, 0x74, 0x14, 0xf0, 0x7d, 0x08, 0x7f, 0xf4, 0x7e, 0x01, 0xa1, 0xe6, + 0x12, 0x06, 0x4b, 0x12, 0x01, 0xa5, 0x50, 0x02, 0xc1, 0x2c, 0x12, 0x01, + 0x7f, 0x40, 0x11, 0x75, 0x44, 0x04, 0xe4, 0xf5, 0x45, 0x7d, 0x0b, 0x7f, + 0xf4, 0x7e, 0x01, 0x12, 0x0c, 0x19, 0x80, 0x0c, 0x75, 0x44, 0x03, 0xe4, + 0xf5, 0x45, 0x75, 0x48, 0x01, 0x12, 0x0b, 0xf7, 0xe4, 0x90, 0x00, 0x81, + 0xf0, 0x22, 0x90, 0x00, 0x81, 0x74, 0x21, 0xf0, 0x7d, 0x0a, 0x7f, 0xf4, + 0x7e, 0x01, 0xa1, 0xe6, 0x12, 0x06, 0x4b, 0x12, 0x01, 0xa5, 0x50, 0x02, + 0xc1, 0x2c, 0x12, 0x01, 0x7f, 0x40, 0x05, 0x75, 0x44, 0x01, 0x80, 0x03, + 0x75, 0x44, 0x02, 0xe4, 0xf5, 0x45, 0xe4, 0x90, 0x00, 0x81, 0xc1, 0x08, + 0x12, 0x01, 0xce, 0x7d, 0x0c, 0x7f, 0xf4, 0x7e, 0x01, 0xa1, 0xe6, 0xc3, + 0xe5, 0x16, 0x94, 0x98, 0xe5, 0x15, 0x94, 0x3a, 0x40, 0x02, 0xa1, 0x76, + 0x7f, 0x01, 0x12, 0x09, 0x08, 0x8e, 0x56, 0x8f, 0x57, 0xc3, 0x12, 0x01, + 0x7f, 0x50, 0x21, 0xc3, 0x12, 0x01, 0xa5, 0x50, 0x02, 0xc1, 0x2c, 0xe5, + 0x16, 0x94, 0x40, 0xe5, 0x15, 0x94, 0x1f, 0x40, 0x0b, 0xe4, 0xf5, 0x45, + 0x7d, 0x0d, 0x7f, 0xd0, 0x7e, 0x07, 0xa1, 0xe6, 0x75, 0x48, 0x01, 0x22, + 0xe4, 0xf5, 0x0a, 0xf5, 0x0b, 0x22, 0x90, 0x00, 0x81, 0x74, 0x48, 0xf0, + 0x7b, 0x0e, 0x7d, 0x10, 0x7c, 0x00, 0x7f, 0x18, 0x12, 0x0b, 0x37, 0x7d, + 0x18, 0x7f, 0xb0, 0x7e, 0x04, 0xa1, 0xe6, 0x7d, 0x0f, 0x7f, 0xf4, 0x7e, + 0x01, 0xa1, 0xe6, 0x12, 0x06, 0x40, 0xe5, 0x57, 0x94, 0x9b, 0xe5, 0x56, + 0x94, 0x01, 0x40, 0x1b, 0x7b, 0x10, 0x7d, 0x10, 0x7c, 0x00, 0x7f, 0x19, + 0x12, 0x0b, 0x37, 0x7d, 0x19, 0x7f, 0xf4, 0x7e, 0x01, 0x12, 0x0c, 0x19, + 0x75, 0x44, 0x06, 0xe4, 0xf5, 0x45, 0x22, 0x75, 0x45, 0x03, 0x12, 0x01, + 0xce, 0x7d, 0x16, 0x7f, 0xe8, 0x7e, 0x03, 0xa1, 0xe6, 0x7d, 0x11, 0x7f, + 0xb0, 0x7e, 0x04, 0xa1, 0xe6, 0x12, 0x01, 0xd5, 0x40, 0x02, 0x80, 0x35, + 0x12, 0x06, 0x40, 0xe5, 0x57, 0x94, 0x1a, 0xe5, 0x56, 0x94, 0x01, 0x40, + 0x15, 0xe5, 0x18, 0x94, 0x64, 0xe5, 0x17, 0x94, 0x00, 0x50, 0x02, 0xc1, + 0x2c, 0x7b, 0x12, 0x7d, 0x06, 0x7f, 0xa8, 0x02, 0x0b, 0xe1, 0xe4, 0xf5, + 0x17, 0xf5, 0x18, 0x22, 0x7d, 0x13, 0x7f, 0xc8, 0x7e, 0x00, 0xa1, 0xe6, + 0x12, 0x01, 0xd5, 0x40, 0x05, 0x75, 0x45, 0x03, 0xc1, 0x09, 0x12, 0x06, + 0x56, 0xe5, 0x18, 0x94, 0x64, 0xe5, 0x17, 0x94, 0x00, 0x50, 0x02, 0xc1, + 0x2c, 0xe5, 0x57, 0x94, 0x85, 0xe5, 0x56, 0x94, 0x01, 0x40, 0x08, 0x12, + 0x06, 0x61, 0x75, 0x44, 0x09, 0x80, 0x13, 0xc3, 0xe5, 0x57, 0x94, 0x50, + 0xe5, 0x56, 0x94, 0x01, 0x50, 0x02, 0xc1, 0x2c, 0x12, 0x06, 0x61, 0x75, + 0x44, 0x08, 0x75, 0x45, 0x04, 0x22, 0xc1, 0x09, 0x12, 0x01, 0x75, 0x40, + 0x02, 0xc1, 0x29, 0xc3, 0xe5, 0x09, 0x95, 0x43, 0xe5, 0x08, 0x94, 0x00, + 0x50, 0x02, 0xc1, 0x2c, 0x90, 0x00, 0x81, 0xe0, 0xf5, 0x55, 0xe5, 0x4d, + 0x30, 0xe7, 0x1b, 0x53, 0x55, 0x0f, 0x20, 0x01, 0x07, 0xd2, 0x01, 0x43, + 0x55, 0x80, 0x80, 0x19, 0xc2, 0x01, 0x43, 0x55, 0x40, 0x12, 0x01, 0x93, + 0x70, 0x20, 0x05, 0x46, 0x80, 0x1c, 0x53, 0x55, 0xf0, 0x30, 0x00, 0x0a, + 0xc2, 0x00, 0x43, 0x55, 0x04, 0x75, 0x43, 0x16, 0x80, 0x12, 0xd2, 0x00, + 0x43, 0x55, 0x08, 0x12, 0x01, 0x93, 0x70, 0x02, 0x05, 0x46, 0xe5, 0x4d, + 0x25, 0xe0, 0xf5, 0x4d, 0xe5, 0x55, 0x12, 0x01, 0x89, 0x22, 0x90, 0x00, + 0x81, 0x74, 0x84, 0xf0, 0x7d, 0x17, 0x7f, 0xe8, 0x7e, 0x03, 0x02, 0x0c, + 0x19, 0x12, 0x06, 0x56, 0xe5, 0x18, 0x94, 0x32, 0xe5, 0x17, 0x94, 0x00, + 0x40, 0x36, 0xe5, 0x57, 0x94, 0xbf, 0xe5, 0x56, 0x94, 0x01, 0x40, 0x03, + 0x75, 0x44, 0x05, 0x90, 0x00, 0x81, 0x74, 0x40, 0xf0, 0x75, 0x48, 0x01, + 0x02, 0x0b, 0xf7, 0x12, 0x0a, 0x7a, 0x8f, 0x55, 0xe5, 0x55, 0xd3, 0x94, + 0x00, 0x40, 0x11, 0x80, 0x0c, 0x12, 0x0a, 0xb0, 0x8f, 0x55, 0xe5, 0x55, + 0xd3, 0x94, 0x00, 0x40, 0x03, 0x85, 0x40, 0x48, 0x22, 0x7f, 0x01, 0x12, + 0x09, 0x08, 0x8e, 0x56, 0x8f, 0x57, 0xc3, 0xe5, 0x0b, 0x94, 0x32, 0xe5, + 0x0a, 0x94, 0x00, 0x22, 0x7f, 0x02, 0x12, 0x09, 0x08, 0x8e, 0x56, 0x8f, + 0x57, 0xc3, 0x22, 0xe4, 0xff, 0x12, 0x09, 0x08, 0x8e, 0x56, 0x8f, 0x57, + 0xc3, 0x22, 0x7f, 0x02, 0x12, 0x09, 0x08, 0x8e, 0x56, 0x8f, 0x57, 0xc3, + 0x22, 0x7b, 0x14, 0x7d, 0x04, 0x7f, 0xc0, 0x12, 0x0b, 0xe1, 0x22, 0x90, + 0x00, 0xa1, 0xe0, 0x30, 0xe2, 0x02, 0xc1, 0xfa, 0x30, 0xe3, 0x06, 0x12, + 0x0c, 0x4a, 0xc2, 0x04, 0x22, 0x20, 0x04, 0x58, 0x90, 0x00, 0xa3, 0xe0, + 0xf5, 0x24, 0xe5, 0x24, 0x24, 0x44, 0x60, 0x21, 0x24, 0xfe, 0x60, 0x12, + 0x14, 0x60, 0x25, 0x24, 0x02, 0x70, 0x2a, 0x75, 0x3d, 0x00, 0x75, 0x3e, + 0x00, 0x75, 0x3f, 0x44, 0x80, 0x1f, 0x75, 0x3d, 0x00, 0x75, 0x3e, 0x00, + 0x75, 0x3f, 0x51, 0x80, 0x14, 0x75, 0x3d, 0x00, 0x75, 0x3e, 0x00, 0x75, + 0x3f, 0x5a, 0x80, 0x09, 0x75, 0x3d, 0xff, 0x75, 0x3e, 0x00, 0x75, 0x3f, + 0x3e, 0xab, 0x3d, 0xaa, 0x3e, 0xa9, 0x3f, 0x12, 0x08, 0x6d, 0x90, 0x00, + 0xa3, 0xf0, 0xd2, 0x04, 0x75, 0x23, 0x01, 0x75, 0x3c, 0x01, 0x80, 0x0e, + 0x90, 0x00, 0xa3, 0xe0, 0xff, 0x74, 0x24, 0x25, 0x23, 0xf8, 0xa6, 0x07, + 0x05, 0x23, 0x12, 0x0c, 0x4a, 0x90, 0x00, 0xa1, 0xe0, 0x30, 0xe5, 0x32, + 0x90, 0x00, 0xa0, 0xe0, 0x44, 0x03, 0xf0, 0xd2, 0x05, 0x22, 0xab, 0x3d, + 0xaa, 0x3e, 0xa9, 0x3f, 0x85, 0x3c, 0x82, 0x75, 0x83, 0x00, 0x12, 0x08, + 0x86, 0x90, 0x00, 0xa3, 0xf0, 0x05, 0x3c, 0x12, 0x0c, 0x4a, 0x90, 0x00, + 0xa1, 0xe0, 0x30, 0xe5, 0x09, 0x90, 0x00, 0xa0, 0xe0, 0x44, 0x01, 0xf0, + 0xd2, 0x06, 0x22, 0x7f, 0x02, 0x12, 0x09, 0x08, 0x8e, 0x55, 0x8f, 0x56, + 0xe5, 0x4e, 0x14, 0x60, 0x17, 0x14, 0x60, 0x68, 0x24, 0x02, 0x60, 0x02, + 0xe1, 0xc9, 0x12, 0x01, 0xba, 0x50, 0x02, 0xe1, 0xc9, 0xe4, 0xf5, 0x19, + 0xf5, 0x1a, 0x80, 0x49, 0xc3, 0x12, 0x01, 0x9c, 0x40, 0x24, 0xd3, 0xe5, + 0x56, 0x94, 0x41, 0xe5, 0x55, 0x94, 0x01, 0x50, 0x19, 0x12, 0x01, 0xc4, + 0x40, 0x19, 0xe4, 0xf5, 0x11, 0xf5, 0x12, 0x75, 0x4e, 0x02, 0x90, 0x00, + 0x81, 0xf0, 0x90, 0x00, 0x80, 0x74, 0xc0, 0xf0, 0x80, 0x05, 0xe4, 0xf5, + 0x11, 0xf5, 0x12, 0x12, 0x01, 0x9b, 0x50, 0x18, 0x12, 0x01, 0xba, 0x40, + 0x48, 0xe4, 0xf5, 0x19, 0xf5, 0x1a, 0x05, 0x49, 0xe5, 0x49, 0x94, 0x0a, + 0x40, 0x3b, 0xe4, 0xf5, 0x49, 0x02, 0x0b, 0x78, 0xe4, 0xf5, 0x49, 0xf5, + 0x19, 0xf5, 0x1a, 0x22, 0x12, 0x01, 0x9b, 0x50, 0x23, 0x12, 0x01, 0xc4, + 0x40, 0x23, 0x75, 0x44, 0x11, 0xe4, 0xf5, 0x45, 0xf5, 0x11, 0xf5, 0x12, + 0xf5, 0x19, 0xf5, 0x1a, 0xf5, 0x49, 0x75, 0x4e, 0x01, 0xf5, 0x48, 0xc2, + 0x03, 0x90, 0x00, 0x81, 0xf0, 0x02, 0x0b, 0xcb, 0xe4, 0xf5, 0x11, 0xf5, + 0x12, 0x22, 0x75, 0xa0, 0x00, 0x75, 0x81, 0x5d, 0x78, 0x7f, 0xe4, 0xf6, + 0xd8, 0xfd, 0x90, 0x01, 0x00, 0x7f, 0x00, 0x7e, 0x01, 0xe4, 0xf0, 0xa3, + 0xdf, 0xfc, 0xde, 0xfa, 0x43, 0xb8, 0x00, 0x75, 0xa8, 0x00, 0x02, 0x08, + 0x28, 0x02, 0x09, 0x9e, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, + 0x03, 0xf6, 0x80, 0x01, 0xf2, 0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, + 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33, 0xc4, 0x54, 0x0f, + 0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, + 0xdf, 0xe4, 0x80, 0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, + 0x90, 0x0c, 0x41, 0xe4, 0x7e, 0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff, 0x54, + 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93, 0xa3, 0x60, 0x01, + 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, + 0xa3, 0xfa, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, + 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, + 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe, 0xbb, 0x01, 0x06, + 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, 0xfe, + 0x02, 0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22, 0xbb, 0x01, + 0x0c, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, + 0x22, 0x50, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe6, 0x22, 0xbb, 0xfe, 0x06, + 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, + 0x83, 0x3a, 0xf5, 0x83, 0xe4, 0x93, 0x22, 0xbc, 0x00, 0x0b, 0xbe, 0x00, + 0x29, 0xef, 0x8d, 0xf0, 0x84, 0xff, 0xad, 0xf0, 0x22, 0xe4, 0xcc, 0xf8, + 0x75, 0xf0, 0x08, 0xef, 0x2f, 0xff, 0xee, 0x33, 0xfe, 0xec, 0x33, 0xfc, + 0xee, 0x9d, 0xec, 0x98, 0x40, 0x05, 0xfc, 0xee, 0x9d, 0xfe, 0x0f, 0xd5, + 0xf0, 0xe9, 0xe4, 0xce, 0xfd, 0x22, 0xed, 0xf8, 0xf5, 0xf0, 0xee, 0x84, + 0x20, 0xd2, 0x1c, 0xfe, 0xad, 0xf0, 0x75, 0xf0, 0x08, 0xef, 0x2f, 0xff, + 0xed, 0x33, 0xfd, 0x40, 0x07, 0x98, 0x50, 0x06, 0xd5, 0xf0, 0xf2, 0x22, + 0xc3, 0x98, 0xfd, 0x0f, 0xd5, 0xf0, 0xea, 0x22, 0xa9, 0x07, 0x90, 0x08, + 0x10, 0x12, 0x0b, 0xc4, 0x90, 0x08, 0x11, 0xe0, 0x7c, 0x00, 0x2f, 0xff, + 0xec, 0x35, 0xf0, 0xad, 0x07, 0xfc, 0x90, 0x08, 0x12, 0x12, 0x0b, 0xc4, + 0xae, 0xf0, 0x90, 0x08, 0x13, 0xe0, 0x7a, 0x00, 0x2f, 0xf5, 0x59, 0xea, + 0x3e, 0xf5, 0x58, 0x90, 0x00, 0xd3, 0xe9, 0xf0, 0x00, 0x00, 0x90, 0x00, + 0xd0, 0xe0, 0x44, 0x40, 0xf0, 0x00, 0x90, 0x00, 0xd2, 0xe0, 0x30, 0xe2, + 0xf8, 0x90, 0x00, 0xd6, 0x12, 0x0b, 0xc4, 0x90, 0x00, 0xd7, 0xe0, 0x54, + 0x03, 0x7a, 0x00, 0x2f, 0xff, 0xea, 0x35, 0xf0, 0xfe, 0xe9, 0x64, 0x02, + 0x70, 0x3b, 0xd3, 0xef, 0x94, 0x0a, 0xee, 0x94, 0x00, 0x40, 0x32, 0xe4, + 0x2d, 0xfb, 0xec, 0x34, 0x01, 0xfa, 0xc3, 0xef, 0x9b, 0xee, 0x9a, 0x50, + 0x24, 0xc3, 0xef, 0x9d, 0xee, 0x9c, 0x40, 0x0e, 0xef, 0x9d, 0x12, 0x0b, + 0xb3, 0xef, 0x24, 0x07, 0xff, 0xee, 0x34, 0x01, 0xfe, 0x22, 0xc3, 0xed, + 0x9f, 0x12, 0x0b, 0xb3, 0xc3, 0x74, 0x07, 0x9f, 0xff, 0x74, 0x01, 0x9e, + 0xfe, 0x22, 0xc0, 0xd0, 0x75, 0xd0, 0x00, 0x90, 0x00, 0x73, 0x74, 0x80, + 0xf0, 0x90, 0x00, 0x78, 0xe0, 0x44, 0xa0, 0xf0, 0x12, 0x01, 0xe9, 0x12, + 0x0c, 0x09, 0x12, 0x0c, 0x52, 0x12, 0x00, 0x0e, 0x12, 0x0b, 0xcb, 0x90, + 0x08, 0x10, 0xe0, 0xf5, 0x53, 0x90, 0x08, 0x11, 0xe0, 0xf5, 0x54, 0x90, + 0x08, 0x12, 0xe0, 0xf5, 0x4f, 0x90, 0x08, 0x13, 0xe0, 0xf5, 0x50, 0xd2, + 0xb8, 0xd2, 0xa8, 0xd2, 0xaf, 0x12, 0x0a, 0xe6, 0x12, 0x09, 0xee, 0x12, + 0x07, 0x23, 0x12, 0x03, 0x1c, 0x12, 0x00, 0x7e, 0x80, 0xef, 0x20, 0x07, + 0x12, 0xc3, 0xe5, 0x10, 0x94, 0x30, 0xe5, 0x0f, 0x94, 0x75, 0x40, 0x07, + 0xe4, 0xf5, 0x0f, 0xf5, 0x10, 0xd2, 0x07, 0x30, 0x08, 0x14, 0xc3, 0xe5, + 0x1c, 0x94, 0xf4, 0xe5, 0x1b, 0x94, 0x01, 0x40, 0x09, 0xc2, 0x08, 0x90, + 0x00, 0x11, 0xe0, 0x44, 0x08, 0xf0, 0xc3, 0xe5, 0x0d, 0x94, 0x64, 0xe5, + 0x0c, 0x94, 0x00, 0x40, 0x12, 0xe4, 0xf5, 0x0c, 0xf5, 0x0d, 0x7f, 0x02, + 0x12, 0x09, 0x08, 0xad, 0x07, 0xac, 0x06, 0x8c, 0x51, 0x8d, 0x52, 0x22, + 0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, + 0x00, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, + 0x05, 0xc0, 0x06, 0xc0, 0x07, 0x90, 0x00, 0x35, 0xe0, 0x30, 0xe1, 0x03, + 0x12, 0x06, 0x6b, 0xd0, 0x07, 0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, + 0x03, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0xd0, 0xd0, 0xd0, 0x82, 0xd0, + 0x83, 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0x12, 0x01, 0x73, 0x50, 0x29, 0x12, + 0x01, 0xae, 0x40, 0x24, 0x90, 0x00, 0x81, 0xe0, 0xfe, 0x53, 0x06, 0x0f, + 0x20, 0x01, 0x0a, 0xd2, 0x01, 0x43, 0x06, 0x80, 0x75, 0x43, 0x16, 0x80, + 0x0c, 0xc2, 0x01, 0x43, 0x06, 0x40, 0x12, 0x01, 0x93, 0x70, 0x02, 0x05, + 0x46, 0x12, 0x01, 0x88, 0x12, 0x01, 0x75, 0x40, 0x02, 0x7f, 0x01, 0x22, + 0x12, 0x01, 0x73, 0x50, 0x29, 0x12, 0x01, 0xae, 0x40, 0x24, 0x90, 0x00, + 0x81, 0xe0, 0xfe, 0x53, 0x06, 0xf0, 0x30, 0x00, 0x0a, 0xc2, 0x00, 0x43, + 0x06, 0x04, 0x75, 0x43, 0x16, 0x80, 0x0c, 0xd2, 0x00, 0x43, 0x06, 0x08, + 0x12, 0x01, 0x93, 0x70, 0x02, 0x05, 0x46, 0x12, 0x01, 0x88, 0x12, 0x01, + 0x75, 0x40, 0x02, 0x7f, 0x01, 0x22, 0xaf, 0x0e, 0xef, 0xc3, 0x94, 0x01, + 0x40, 0x20, 0xe4, 0xf5, 0x0e, 0xfe, 0xef, 0xfd, 0xee, 0x25, 0xe0, 0x24, + 0x0f, 0x12, 0x0c, 0x36, 0xbe, 0x07, 0xf3, 0xe4, 0xfe, 0xef, 0xfd, 0xee, + 0x25, 0xe0, 0x24, 0x08, 0x12, 0x0c, 0x36, 0xbe, 0x03, 0xf3, 0x22, 0xef, + 0x24, 0xfe, 0x60, 0x0e, 0x14, 0x60, 0x12, 0x14, 0x60, 0x16, 0x24, 0x03, + 0x70, 0x18, 0x12, 0x01, 0xce, 0x22, 0x90, 0x00, 0x81, 0x74, 0x84, 0xf0, + 0x22, 0x90, 0x00, 0x81, 0x74, 0x44, 0xf0, 0x22, 0x90, 0x00, 0x81, 0x74, + 0x48, 0xf0, 0x22, 0xe4, 0xf5, 0x46, 0xf5, 0x47, 0x8c, 0x4b, 0x8d, 0x4c, + 0xbf, 0x18, 0x07, 0xc2, 0x01, 0x75, 0x48, 0x18, 0x80, 0x08, 0xbf, 0x19, + 0x05, 0xd2, 0x00, 0x75, 0x48, 0x19, 0x12, 0x01, 0xdf, 0x8b, 0x40, 0x22, + 0xe4, 0xfd, 0xfc, 0xc3, 0xed, 0x9f, 0xec, 0x9e, 0x50, 0x15, 0xe4, 0xfb, + 0xfa, 0x0b, 0xbb, 0x00, 0x01, 0x0a, 0xeb, 0x64, 0xbb, 0x4a, 0x70, 0xf5, + 0x0d, 0xbd, 0x00, 0x01, 0x0c, 0x80, 0xe4, 0x22, 0x12, 0x00, 0x46, 0x00, + 0x00, 0x00, 0x90, 0x00, 0x06, 0x74, 0x52, 0xf0, 0x00, 0x00, 0x00, 0x90, + 0x00, 0x7c, 0xe0, 0x44, 0x80, 0xf0, 0x12, 0x01, 0xe9, 0x12, 0x0c, 0x52, + 0x75, 0x4e, 0x01, 0x22, 0x90, 0x00, 0xa4, 0x74, 0x6a, 0xf0, 0x90, 0x00, + 0xa1, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x00, 0xa0, 0x74, 0x83, 0xf0, 0x90, + 0x00, 0x30, 0xe0, 0x44, 0x02, 0xf0, 0x22, 0xff, 0xef, 0x7f, 0x00, 0xfe, + 0xc3, 0xe5, 0x59, 0x9d, 0xfd, 0xe5, 0x58, 0x9c, 0xfc, 0x02, 0x08, 0xb3, + 0xe0, 0x75, 0xf0, 0x04, 0xa4, 0xff, 0x22, 0x7f, 0x10, 0x78, 0x2c, 0xe4, + 0xf6, 0x08, 0xdf, 0xfc, 0x75, 0x2f, 0x01, 0x75, 0x32, 0x01, 0x75, 0x44, + 0x11, 0xe4, 0xf5, 0x45, 0x22, 0xe4, 0xf5, 0x46, 0xf5, 0x47, 0xed, 0x75, + 0x4b, 0x00, 0xf5, 0x4c, 0x8f, 0x4d, 0x75, 0x48, 0x15, 0x8b, 0x40, 0x12, + 0x01, 0xdf, 0x22, 0x30, 0x02, 0x0e, 0x90, 0x00, 0x11, 0xe0, 0x54, 0xf7, + 0xf0, 0xd2, 0x08, 0xe4, 0xf5, 0x1b, 0xf5, 0x1c, 0x22, 0xc2, 0x8c, 0x43, + 0x89, 0x02, 0x75, 0x8a, 0x9c, 0x75, 0x8c, 0x9c, 0xd2, 0x8c, 0xd2, 0xa9, + 0x22, 0xe4, 0xf5, 0x13, 0xf5, 0x14, 0x8e, 0x41, 0x8f, 0x42, 0x75, 0x48, + 0x03, 0x8d, 0x4a, 0x22, 0x90, 0x00, 0xd1, 0x74, 0x81, 0xf0, 0x90, 0x00, + 0xd0, 0xe0, 0x54, 0x7f, 0xf0, 0x22, 0xf8, 0x08, 0xed, 0x26, 0xf6, 0x18, + 0xe4, 0x36, 0xf6, 0x0e, 0x22, 0x04, 0x5a, 0x57, 0x54, 0x37, 0x30, 0xc1, + 0x82, 0x00, 0x90, 0x00, 0xa0, 0xe0, 0x44, 0x02, 0xf0, 0x22, 0x12, 0x0b, + 0x98, 0x02, 0x0c, 0x28 + }; +unsigned int WT70F_QC3p_V02_210326_FBA1_bin_len = 3160; + +#elif 0 +unsigned char WT70F_QC3p_V02_210326_FBA1_bin[] = { + 0x02, 0x07, 0xb9, 0x02, 0x0a, 0x27, 0xc2, 0x8d, 0x05, 0x0e, 0x32, 0x02, + 0x00, 0x06, 0x75, 0x4e, 0x01, 0x22, 0x32, 0x02, 0x00, 0x12, 0xe5, 0x25, + 0x30, 0xe7, 0x05, 0x75, 0x55, 0x18, 0x80, 0x03, 0x75, 0x55, 0x19, 0xe5, + 0x25, 0x54, 0x7f, 0xfe, 0x7c, 0x00, 0xe4, 0x25, 0x26, 0xff, 0xec, 0x3e, + 0xad, 0x07, 0xfc, 0xe4, 0xfb, 0xaf, 0x55, 0x02, 0x0b, 0x26, 0x32, 0x02, + 0x00, 0x3a, 0x02, 0x00, 0x32, 0xff, 0xff, 0x02, 0x00, 0x40, 0x90, 0x00, + 0x01, 0x74, 0xb8, 0xf0, 0x90, 0x00, 0x07, 0x74, 0xa4, 0xf0, 0xe4, 0x90, + 0x00, 0x20, 0xf0, 0x90, 0x00, 0x10, 0xf0, 0x90, 0x00, 0x12, 0xf0, 0x90, + 0x00, 0x80, 0xf0, 0x90, 0x00, 0x81, 0xf0, 0x90, 0x00, 0xd0, 0x74, 0x80, + 0xf0, 0x90, 0x00, 0x60, 0x74, 0x20, 0xf0, 0x90, 0x00, 0x6a, 0xe0, 0x44, + 0x80, 0xf0, 0x22, 0x02, 0x02, 0x00, 0x20, 0x05, 0x02, 0x21, 0x58, 0xaf, + 0x24, 0xef, 0xd3, 0x94, 0xb5, 0x50, 0x06, 0x74, 0x2c, 0x2f, 0xf8, 0xa6, + 0x25, 0xef, 0x24, 0x50, 0xb4, 0x0c, 0x00, 0x40, 0x02, 0x21, 0x53, 0x90, + 0x00, 0xa5, 0x25, 0xe0, 0x50, 0x02, 0x05, 0x83, 0x73, 0x01, 0xbd, 0x01, + 0xcf, 0x01, 0xd6, 0x01, 0xe2, 0x01, 0xfb, 0x21, 0x1e, 0x21, 0x2d, 0x21, + 0x53, 0x21, 0x53, 0x21, 0x53, 0x21, 0x42, 0x21, 0x4a, 0x75, 0x2d, 0x01, + 0xe4, 0xf5, 0x31, 0x90, 0x00, 0x81, 0xf0, 0x7d, 0x04, 0x7f, 0xf4, 0x7e, + 0x01, 0x80, 0x6e, 0xaf, 0x25, 0x12, 0x0a, 0xfe, 0x80, 0x7d, 0xe5, 0x25, + 0x70, 0x04, 0xc2, 0x02, 0x80, 0x75, 0xd2, 0x02, 0x80, 0x71, 0xe4, 0x90, + 0x00, 0x81, 0xf0, 0x90, 0x00, 0x80, 0xf0, 0x12, 0x0b, 0xba, 0x75, 0x30, + 0x01, 0x75, 0x4e, 0x01, 0xe4, 0xf5, 0x11, 0xf5, 0x12, 0x80, 0x58, 0xe5, + 0x25, 0xb4, 0x01, 0x0a, 0x75, 0x4e, 0x01, 0xe4, 0xf5, 0x11, 0xf5, 0x12, + 0x80, 0x0e, 0xe4, 0x90, 0x00, 0x81, 0xf0, 0x75, 0x44, 0x11, 0xf5, 0x45, + 0xf5, 0x48, 0xf5, 0x4e, 0xf5, 0x19, 0xf5, 0x1a, 0x80, 0x35, 0xe5, 0x25, + 0x64, 0x01, 0x70, 0x2f, 0xf5, 0x11, 0xf5, 0x12, 0x12, 0x0b, 0x67, 0x80, + 0x26, 0xe5, 0x25, 0xb4, 0x01, 0x21, 0xe5, 0x4e, 0xb4, 0x02, 0x1c, 0x7d, + 0x02, 0x7f, 0xe8, 0x7e, 0x03, 0x12, 0x0c, 0x08, 0x80, 0x11, 0x85, 0x25, + 0x34, 0x85, 0x26, 0x35, 0x80, 0x06, 0x85, 0x25, 0x36, 0x85, 0x26, 0x37, + 0x12, 0x00, 0x16, 0xe4, 0xf5, 0x24, 0xc2, 0x05, 0x30, 0x06, 0x17, 0xe5, + 0x24, 0x24, 0x4d, 0x60, 0x09, 0x24, 0x03, 0x70, 0x08, 0xe4, 0xf5, 0x2d, + 0x80, 0x03, 0xe4, 0xf5, 0x30, 0xe4, 0xf5, 0x24, 0xc2, 0x06, 0x22, 0xe4, + 0xff, 0xc3, 0xe5, 0x47, 0x95, 0x4c, 0xe5, 0x46, 0x95, 0x4b, 0x22, 0xe5, + 0x57, 0x94, 0xaa, 0xe5, 0x56, 0x94, 0x00, 0x22, 0xee, 0x90, 0x00, 0x81, + 0xf0, 0xe4, 0xf5, 0x08, 0xf5, 0x09, 0x22, 0x75, 0x43, 0x16, 0x05, 0x47, + 0xe5, 0x47, 0x22, 0xd3, 0xe5, 0x56, 0x94, 0x9e, 0xe5, 0x55, 0x94, 0x00, + 0x22, 0xe5, 0x0b, 0x94, 0x32, 0xe5, 0x0a, 0x94, 0x00, 0x22, 0xe5, 0x43, + 0xfd, 0xc3, 0xe5, 0x09, 0x9d, 0xe5, 0x08, 0x94, 0x00, 0x22, 0xc3, 0xe5, + 0x1a, 0x94, 0x10, 0xe5, 0x19, 0x94, 0x27, 0x22, 0xc3, 0xe5, 0x12, 0x94, + 0xc8, 0xe5, 0x11, 0x94, 0x00, 0x22, 0x90, 0x00, 0x81, 0x74, 0x40, 0xf0, + 0x22, 0xc3, 0xe5, 0x16, 0x94, 0xd0, 0xe5, 0x15, 0x94, 0x07, 0x22, 0x75, + 0x08, 0x00, 0x75, 0x09, 0x16, 0x75, 0x43, 0x16, 0x22, 0x90, 0x00, 0x11, + 0x74, 0x08, 0xf0, 0x90, 0x00, 0x10, 0xf0, 0x90, 0x00, 0x12, 0x74, 0x03, + 0xf0, 0x90, 0x00, 0x20, 0x74, 0x34, 0xf0, 0x22, 0xc0, 0xe0, 0xc0, 0xf0, + 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0xc0, 0x00, 0x75, 0xd0, 0x00, 0xc3, + 0xe5, 0x81, 0x94, 0x07, 0xf8, 0xa6, 0xfd, 0x08, 0xa6, 0xf5, 0x90, 0x00, + 0x78, 0x74, 0xa0, 0xf0, 0xd2, 0xfb, 0x74, 0xa0, 0x55, 0xf8, 0xb4, 0xa0, + 0xf9, 0xe5, 0xf9, 0xb4, 0x00, 0x02, 0x41, 0xf7, 0xb4, 0x01, 0x02, 0x80, + 0x60, 0xb4, 0x02, 0x02, 0x80, 0x6e, 0xb4, 0x03, 0x02, 0x80, 0x72, 0xb4, + 0x04, 0x02, 0x80, 0x79, 0xb4, 0x05, 0x02, 0x80, 0x7d, 0xb4, 0x06, 0x02, + 0x41, 0xd2, 0xb4, 0x07, 0x02, 0x41, 0xe3, 0xb4, 0x16, 0x02, 0x80, 0x07, + 0xb4, 0x17, 0x02, 0x80, 0x1c, 0x80, 0xc3, 0x30, 0xfd, 0xfd, 0x85, 0xf9, + 0x83, 0x30, 0xfd, 0xfd, 0x85, 0xf9, 0x82, 0xe0, 0xf5, 0xf9, 0xa3, 0x20, + 0xfe, 0x02, 0x80, 0xf7, 0x20, 0xfc, 0x1c, 0x80, 0xf6, 0x30, 0xfd, 0xfd, + 0x85, 0xf9, 0x83, 0x30, 0xfd, 0xfd, 0x85, 0xf9, 0x82, 0x30, 0xfd, 0x06, + 0xe5, 0xf9, 0xf0, 0xa3, 0x80, 0xf7, 0x20, 0xfc, 0x02, 0x80, 0xf2, 0x80, + 0x8d, 0x30, 0xfd, 0xfd, 0x85, 0xf9, 0x83, 0x30, 0xfd, 0xfd, 0x85, 0xf9, + 0x82, 0x74, 0x00, 0x93, 0xf5, 0xf9, 0x41, 0x22, 0x30, 0xfd, 0xfd, 0xa8, + 0xf9, 0x86, 0xf9, 0x41, 0x22, 0x30, 0xfd, 0xfd, 0xa8, 0xf9, 0x30, 0xfd, + 0xfd, 0xa6, 0xf9, 0x41, 0x22, 0x30, 0xfd, 0xfd, 0xe5, 0xf9, 0xf5, 0xf9, + 0x41, 0x22, 0x30, 0xfd, 0xfd, 0xe5, 0xf9, 0x30, 0xfd, 0xfd, 0xf5, 0xf9, + 0x41, 0x22, 0x30, 0xfd, 0xfd, 0x85, 0xf9, 0x83, 0x30, 0xfd, 0xfd, 0x85, + 0xf9, 0x82, 0xe0, 0xf5, 0xf9, 0x41, 0x22, 0x30, 0xfd, 0xfd, 0x85, 0xf9, + 0x83, 0x30, 0xfd, 0xfd, 0x85, 0xf9, 0x82, 0x30, 0xfd, 0xfd, 0xe5, 0xf9, + 0xf0, 0x41, 0x22, 0x53, 0x88, 0x5f, 0x90, 0x00, 0x7c, 0xe0, 0x44, 0x20, + 0xf0, 0x90, 0x00, 0xb2, 0xe0, 0x44, 0x10, 0xf0, 0xc2, 0xfb, 0xd0, 0x00, + 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe5, 0x48, 0x14, 0xb4, 0x19, 0x00, 0x40, 0x02, + 0xc1, 0x2c, 0x90, 0x03, 0x30, 0x25, 0xe0, 0x50, 0x02, 0x05, 0x83, 0x73, + 0x61, 0x62, 0x61, 0x6c, 0x61, 0x7a, 0x61, 0x95, 0x61, 0xa2, 0x61, 0xc4, + 0x61, 0xe2, 0x61, 0xf0, 0x81, 0x22, 0x81, 0x30, 0x81, 0x50, 0x81, 0x5b, + 0x81, 0x9e, 0x81, 0xb7, 0x81, 0xbf, 0x81, 0xf5, 0x81, 0xfd, 0xa1, 0x2c, + 0xa1, 0x34, 0xa1, 0x76, 0xa1, 0x78, 0xa1, 0xda, 0xa1, 0xe9, 0xc1, 0x0f, + 0xc1, 0x1d, 0xd2, 0x03, 0xe4, 0xf5, 0x0c, 0xf5, 0x0d, 0xf5, 0x48, 0x22, + 0x90, 0x00, 0x81, 0x74, 0x01, 0xf0, 0x7d, 0x05, 0x7f, 0x88, 0x7e, 0x13, + 0xa1, 0xe6, 0xc3, 0xe5, 0x14, 0x95, 0x42, 0xe5, 0x13, 0x95, 0x41, 0x50, + 0x02, 0xc1, 0x2c, 0x85, 0x4a, 0x48, 0xe4, 0xf5, 0x15, 0xf5, 0x16, 0xf5, + 0x17, 0xf5, 0x18, 0x81, 0x99, 0x90, 0x00, 0x81, 0x74, 0x01, 0xf0, 0x7d, + 0x05, 0x7f, 0xf4, 0xfe, 0xa1, 0xe6, 0x12, 0x06, 0x2d, 0x50, 0x02, 0xc1, + 0x2c, 0x12, 0x01, 0x7f, 0x40, 0x08, 0x75, 0x44, 0x07, 0xe4, 0xf5, 0x45, + 0x81, 0x4b, 0x90, 0x00, 0x81, 0x74, 0x42, 0xf0, 0x7d, 0x06, 0x7f, 0xf4, + 0x7e, 0x01, 0xa1, 0xe6, 0x12, 0x06, 0x2d, 0x50, 0x02, 0xc1, 0x2c, 0x12, + 0x01, 0x7f, 0xe4, 0x90, 0x00, 0x81, 0x40, 0x05, 0xf0, 0x7d, 0x07, 0x80, + 0x03, 0xf0, 0x7d, 0x09, 0x7f, 0xf4, 0x7e, 0x01, 0xa1, 0xe6, 0x90, 0x00, + 0x81, 0x74, 0x14, 0xf0, 0x7d, 0x08, 0x7f, 0xf4, 0x7e, 0x01, 0xa1, 0xe6, + 0x12, 0x06, 0x4b, 0x12, 0x01, 0xa5, 0x50, 0x02, 0xc1, 0x2c, 0x12, 0x01, + 0x7f, 0x40, 0x11, 0x75, 0x44, 0x04, 0xe4, 0xf5, 0x45, 0x7d, 0x0b, 0x7f, + 0xf4, 0x7e, 0x01, 0x12, 0x0c, 0x08, 0x80, 0x0c, 0x75, 0x44, 0x03, 0xe4, + 0xf5, 0x45, 0x75, 0x48, 0x01, 0x12, 0x0b, 0xe6, 0xe4, 0x90, 0x00, 0x81, + 0xf0, 0x22, 0x90, 0x00, 0x81, 0x74, 0x21, 0xf0, 0x7d, 0x0a, 0x7f, 0xf4, + 0x7e, 0x01, 0xa1, 0xe6, 0x12, 0x06, 0x4b, 0x12, 0x01, 0xa5, 0x50, 0x02, + 0xc1, 0x2c, 0x12, 0x01, 0x7f, 0x40, 0x05, 0x75, 0x44, 0x01, 0x80, 0x03, + 0x75, 0x44, 0x02, 0xe4, 0xf5, 0x45, 0xe4, 0x90, 0x00, 0x81, 0xc1, 0x08, + 0x12, 0x01, 0xce, 0x7d, 0x0c, 0x7f, 0xf4, 0x7e, 0x01, 0xa1, 0xe6, 0xc3, + 0xe5, 0x16, 0x94, 0x98, 0xe5, 0x15, 0x94, 0x3a, 0x40, 0x02, 0xa1, 0x76, + 0x7f, 0x01, 0x12, 0x08, 0xf7, 0x8e, 0x56, 0x8f, 0x57, 0xc3, 0x12, 0x01, + 0x7f, 0x50, 0x21, 0xc3, 0x12, 0x01, 0xa5, 0x50, 0x02, 0xc1, 0x2c, 0xe5, + 0x16, 0x94, 0x40, 0xe5, 0x15, 0x94, 0x1f, 0x40, 0x0b, 0xe4, 0xf5, 0x45, + 0x7d, 0x0d, 0x7f, 0xd0, 0x7e, 0x07, 0xa1, 0xe6, 0x75, 0x48, 0x01, 0x22, + 0xe4, 0xf5, 0x0a, 0xf5, 0x0b, 0x22, 0x90, 0x00, 0x81, 0x74, 0x48, 0xf0, + 0x7b, 0x0e, 0x7d, 0x10, 0x7c, 0x00, 0x7f, 0x18, 0x12, 0x0b, 0x26, 0x7d, + 0x18, 0x7f, 0xb0, 0x7e, 0x04, 0xa1, 0xe6, 0x7d, 0x0f, 0x7f, 0xf4, 0x7e, + 0x01, 0xa1, 0xe6, 0x12, 0x06, 0x40, 0xe5, 0x57, 0x94, 0x9b, 0xe5, 0x56, + 0x94, 0x01, 0x40, 0x1b, 0x7b, 0x10, 0x7d, 0x10, 0x7c, 0x00, 0x7f, 0x19, + 0x12, 0x0b, 0x26, 0x7d, 0x19, 0x7f, 0xf4, 0x7e, 0x01, 0x12, 0x0c, 0x08, + 0x75, 0x44, 0x06, 0xe4, 0xf5, 0x45, 0x22, 0x75, 0x45, 0x03, 0x12, 0x01, + 0xce, 0x7d, 0x16, 0x7f, 0xe8, 0x7e, 0x03, 0xa1, 0xe6, 0x7d, 0x11, 0x7f, + 0xb0, 0x7e, 0x04, 0xa1, 0xe6, 0x12, 0x01, 0xd5, 0x40, 0x02, 0x80, 0x35, + 0x12, 0x06, 0x40, 0xe5, 0x57, 0x94, 0x1a, 0xe5, 0x56, 0x94, 0x01, 0x40, + 0x15, 0xe5, 0x18, 0x94, 0x64, 0xe5, 0x17, 0x94, 0x00, 0x50, 0x02, 0xc1, + 0x2c, 0x7b, 0x12, 0x7d, 0x06, 0x7f, 0xa8, 0x02, 0x0b, 0xd0, 0xe4, 0xf5, + 0x17, 0xf5, 0x18, 0x22, 0x7d, 0x13, 0x7f, 0xc8, 0x7e, 0x00, 0xa1, 0xe6, + 0x12, 0x01, 0xd5, 0x40, 0x05, 0x75, 0x45, 0x03, 0xc1, 0x09, 0x12, 0x06, + 0x56, 0xe5, 0x18, 0x94, 0x64, 0xe5, 0x17, 0x94, 0x00, 0x50, 0x02, 0xc1, + 0x2c, 0xe5, 0x57, 0x94, 0x85, 0xe5, 0x56, 0x94, 0x01, 0x40, 0x08, 0x12, + 0x06, 0x61, 0x75, 0x44, 0x09, 0x80, 0x13, 0xc3, 0xe5, 0x57, 0x94, 0x50, + 0xe5, 0x56, 0x94, 0x01, 0x50, 0x02, 0xc1, 0x2c, 0x12, 0x06, 0x61, 0x75, + 0x44, 0x08, 0x75, 0x45, 0x04, 0x22, 0xc1, 0x09, 0x12, 0x01, 0x75, 0x40, + 0x02, 0xc1, 0x29, 0xc3, 0xe5, 0x09, 0x95, 0x43, 0xe5, 0x08, 0x94, 0x00, + 0x50, 0x02, 0xc1, 0x2c, 0x90, 0x00, 0x81, 0xe0, 0xf5, 0x55, 0xe5, 0x4d, + 0x30, 0xe7, 0x1b, 0x53, 0x55, 0x0f, 0x20, 0x01, 0x07, 0xd2, 0x01, 0x43, + 0x55, 0x80, 0x80, 0x19, 0xc2, 0x01, 0x43, 0x55, 0x40, 0x12, 0x01, 0x93, + 0x70, 0x20, 0x05, 0x46, 0x80, 0x1c, 0x53, 0x55, 0xf0, 0x30, 0x00, 0x0a, + 0xc2, 0x00, 0x43, 0x55, 0x04, 0x75, 0x43, 0x16, 0x80, 0x12, 0xd2, 0x00, + 0x43, 0x55, 0x08, 0x12, 0x01, 0x93, 0x70, 0x02, 0x05, 0x46, 0xe5, 0x4d, + 0x25, 0xe0, 0xf5, 0x4d, 0xe5, 0x55, 0x12, 0x01, 0x89, 0x22, 0x90, 0x00, + 0x81, 0x74, 0x84, 0xf0, 0x7d, 0x17, 0x7f, 0xe8, 0x7e, 0x03, 0x02, 0x0c, + 0x08, 0x12, 0x06, 0x56, 0xe5, 0x18, 0x94, 0x32, 0xe5, 0x17, 0x94, 0x00, + 0x40, 0x36, 0xe5, 0x57, 0x94, 0xbf, 0xe5, 0x56, 0x94, 0x01, 0x40, 0x03, + 0x75, 0x44, 0x05, 0x90, 0x00, 0x81, 0x74, 0x40, 0xf0, 0x75, 0x48, 0x01, + 0x02, 0x0b, 0xe6, 0x12, 0x0a, 0x69, 0x8f, 0x55, 0xe5, 0x55, 0xd3, 0x94, + 0x00, 0x40, 0x11, 0x80, 0x0c, 0x12, 0x0a, 0x9f, 0x8f, 0x55, 0xe5, 0x55, + 0xd3, 0x94, 0x00, 0x40, 0x03, 0x85, 0x40, 0x48, 0x22, 0x7f, 0x01, 0x12, + 0x08, 0xf7, 0x8e, 0x56, 0x8f, 0x57, 0xc3, 0xe5, 0x0b, 0x94, 0x32, 0xe5, + 0x0a, 0x94, 0x00, 0x22, 0x7f, 0x02, 0x12, 0x08, 0xf7, 0x8e, 0x56, 0x8f, + 0x57, 0xc3, 0x22, 0xe4, 0xff, 0x12, 0x08, 0xf7, 0x8e, 0x56, 0x8f, 0x57, + 0xc3, 0x22, 0x7f, 0x02, 0x12, 0x08, 0xf7, 0x8e, 0x56, 0x8f, 0x57, 0xc3, + 0x22, 0x7b, 0x14, 0x7d, 0x04, 0x7f, 0xc0, 0x12, 0x0b, 0xd0, 0x22, 0x90, + 0x00, 0xa1, 0xe0, 0x20, 0xe2, 0x77, 0x30, 0xe3, 0x06, 0x12, 0x0c, 0x39, + 0xc2, 0x04, 0x22, 0x20, 0x04, 0x49, 0x90, 0x00, 0xa3, 0xe0, 0xf5, 0x24, + 0xe5, 0x24, 0x24, 0x42, 0x60, 0x12, 0x14, 0x60, 0x1a, 0x24, 0x02, 0x70, + 0x1f, 0x75, 0x3d, 0x00, 0x75, 0x3e, 0x00, 0x75, 0x3f, 0x44, 0x80, 0x14, + 0x75, 0x3d, 0x00, 0x75, 0x3e, 0x00, 0x75, 0x3f, 0x51, 0x80, 0x09, 0x75, + 0x3d, 0xff, 0x75, 0x3e, 0x00, 0x75, 0x3f, 0x3e, 0xab, 0x3d, 0xaa, 0x3e, + 0xa9, 0x3f, 0x12, 0x08, 0x5c, 0x90, 0x00, 0xa3, 0xf0, 0xd2, 0x04, 0x75, + 0x23, 0x01, 0x75, 0x3c, 0x01, 0x80, 0x0e, 0x90, 0x00, 0xa3, 0xe0, 0xff, + 0x74, 0x24, 0x25, 0x23, 0xf8, 0xa6, 0x07, 0x05, 0x23, 0x12, 0x0c, 0x39, + 0x90, 0x00, 0xa1, 0xe0, 0x30, 0xe5, 0x32, 0x90, 0x00, 0xa0, 0xe0, 0x44, + 0x03, 0xf0, 0xd2, 0x05, 0x22, 0xab, 0x3d, 0xaa, 0x3e, 0xa9, 0x3f, 0x85, + 0x3c, 0x82, 0x75, 0x83, 0x00, 0x12, 0x08, 0x75, 0x90, 0x00, 0xa3, 0xf0, + 0x05, 0x3c, 0x12, 0x0c, 0x39, 0x90, 0x00, 0xa1, 0xe0, 0x30, 0xe5, 0x09, + 0x90, 0x00, 0xa0, 0xe0, 0x44, 0x01, 0xf0, 0xd2, 0x06, 0x22, 0x7f, 0x02, + 0x12, 0x08, 0xf7, 0x8e, 0x55, 0x8f, 0x56, 0xe5, 0x4e, 0x14, 0x60, 0x17, + 0x14, 0x60, 0x68, 0x24, 0x02, 0x60, 0x02, 0xe1, 0xb8, 0x12, 0x01, 0xba, + 0x50, 0x02, 0xe1, 0xb8, 0xe4, 0xf5, 0x19, 0xf5, 0x1a, 0x80, 0x49, 0xc3, + 0x12, 0x01, 0x9c, 0x40, 0x24, 0xd3, 0xe5, 0x56, 0x94, 0x41, 0xe5, 0x55, + 0x94, 0x01, 0x50, 0x19, 0x12, 0x01, 0xc4, 0x40, 0x19, 0xe4, 0xf5, 0x11, + 0xf5, 0x12, 0x75, 0x4e, 0x02, 0x90, 0x00, 0x81, 0xf0, 0x90, 0x00, 0x80, + 0x74, 0xc0, 0xf0, 0x80, 0x05, 0xe4, 0xf5, 0x11, 0xf5, 0x12, 0x12, 0x01, + 0x9b, 0x50, 0x18, 0x12, 0x01, 0xba, 0x40, 0x48, 0xe4, 0xf5, 0x19, 0xf5, + 0x1a, 0x05, 0x49, 0xe5, 0x49, 0x94, 0x0a, 0x40, 0x3b, 0xe4, 0xf5, 0x49, + 0x02, 0x0b, 0x67, 0xe4, 0xf5, 0x49, 0xf5, 0x19, 0xf5, 0x1a, 0x22, 0x12, + 0x01, 0x9b, 0x50, 0x23, 0x12, 0x01, 0xc4, 0x40, 0x23, 0x75, 0x44, 0x11, + 0xe4, 0xf5, 0x45, 0xf5, 0x11, 0xf5, 0x12, 0xf5, 0x19, 0xf5, 0x1a, 0xf5, + 0x49, 0x75, 0x4e, 0x01, 0xf5, 0x48, 0xc2, 0x03, 0x90, 0x00, 0x81, 0xf0, + 0x02, 0x0b, 0xba, 0xe4, 0xf5, 0x11, 0xf5, 0x12, 0x22, 0x75, 0xa0, 0x00, + 0x75, 0x81, 0x5d, 0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x90, 0x01, 0x00, + 0x7f, 0x00, 0x7e, 0x01, 0xe4, 0xf0, 0xa3, 0xdf, 0xfc, 0xde, 0xfa, 0x43, + 0xb8, 0x00, 0x75, 0xa8, 0x00, 0x02, 0x08, 0x17, 0x02, 0x09, 0x8d, 0xe4, + 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2, + 0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, + 0x0c, 0xc8, 0xc3, 0x33, 0xc4, 0x54, 0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, + 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf, 0xe4, 0x80, 0x0b, 0x01, + 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x0c, 0x30, 0xe4, 0x7e, + 0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, + 0x1f, 0xfe, 0xe4, 0x93, 0xa3, 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, + 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3, 0xfa, 0xe4, 0x93, 0xa3, + 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, + 0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, + 0xde, 0xe7, 0x80, 0xbe, 0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, + 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, 0xfe, 0x02, 0xe3, 0x22, 0x89, 0x82, + 0x8a, 0x83, 0xe4, 0x93, 0x22, 0xbb, 0x01, 0x0c, 0xe5, 0x82, 0x29, 0xf5, + 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50, 0x06, 0xe9, 0x25, + 0x82, 0xf8, 0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, + 0x22, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe4, + 0x93, 0x22, 0xbc, 0x00, 0x0b, 0xbe, 0x00, 0x29, 0xef, 0x8d, 0xf0, 0x84, + 0xff, 0xad, 0xf0, 0x22, 0xe4, 0xcc, 0xf8, 0x75, 0xf0, 0x08, 0xef, 0x2f, + 0xff, 0xee, 0x33, 0xfe, 0xec, 0x33, 0xfc, 0xee, 0x9d, 0xec, 0x98, 0x40, + 0x05, 0xfc, 0xee, 0x9d, 0xfe, 0x0f, 0xd5, 0xf0, 0xe9, 0xe4, 0xce, 0xfd, + 0x22, 0xed, 0xf8, 0xf5, 0xf0, 0xee, 0x84, 0x20, 0xd2, 0x1c, 0xfe, 0xad, + 0xf0, 0x75, 0xf0, 0x08, 0xef, 0x2f, 0xff, 0xed, 0x33, 0xfd, 0x40, 0x07, + 0x98, 0x50, 0x06, 0xd5, 0xf0, 0xf2, 0x22, 0xc3, 0x98, 0xfd, 0x0f, 0xd5, + 0xf0, 0xea, 0x22, 0xa9, 0x07, 0x90, 0x08, 0x10, 0x12, 0x0b, 0xb3, 0x90, + 0x08, 0x11, 0xe0, 0x7c, 0x00, 0x2f, 0xff, 0xec, 0x35, 0xf0, 0xad, 0x07, + 0xfc, 0x90, 0x08, 0x12, 0x12, 0x0b, 0xb3, 0xae, 0xf0, 0x90, 0x08, 0x13, + 0xe0, 0x7a, 0x00, 0x2f, 0xf5, 0x59, 0xea, 0x3e, 0xf5, 0x58, 0x90, 0x00, + 0xd3, 0xe9, 0xf0, 0x00, 0x00, 0x90, 0x00, 0xd0, 0xe0, 0x44, 0x40, 0xf0, + 0x00, 0x90, 0x00, 0xd2, 0xe0, 0x30, 0xe2, 0xf8, 0x90, 0x00, 0xd6, 0x12, + 0x0b, 0xb3, 0x90, 0x00, 0xd7, 0xe0, 0x54, 0x03, 0x7a, 0x00, 0x2f, 0xff, + 0xea, 0x35, 0xf0, 0xfe, 0xe9, 0x64, 0x02, 0x70, 0x3b, 0xd3, 0xef, 0x94, + 0x0a, 0xee, 0x94, 0x00, 0x40, 0x32, 0xe4, 0x2d, 0xfb, 0xec, 0x34, 0x01, + 0xfa, 0xc3, 0xef, 0x9b, 0xee, 0x9a, 0x50, 0x24, 0xc3, 0xef, 0x9d, 0xee, + 0x9c, 0x40, 0x0e, 0xef, 0x9d, 0x12, 0x0b, 0xa2, 0xef, 0x24, 0x07, 0xff, + 0xee, 0x34, 0x01, 0xfe, 0x22, 0xc3, 0xed, 0x9f, 0x12, 0x0b, 0xa2, 0xc3, + 0x74, 0x07, 0x9f, 0xff, 0x74, 0x01, 0x9e, 0xfe, 0x22, 0xc0, 0xd0, 0x75, + 0xd0, 0x00, 0x90, 0x00, 0x73, 0x74, 0x80, 0xf0, 0x90, 0x00, 0x78, 0xe0, + 0x44, 0xa0, 0xf0, 0x12, 0x01, 0xe9, 0x12, 0x0b, 0xf8, 0x12, 0x0c, 0x41, + 0x12, 0x00, 0x0e, 0x12, 0x0b, 0xba, 0x90, 0x08, 0x10, 0xe0, 0xf5, 0x53, + 0x90, 0x08, 0x11, 0xe0, 0xf5, 0x54, 0x90, 0x08, 0x12, 0xe0, 0xf5, 0x4f, + 0x90, 0x08, 0x13, 0xe0, 0xf5, 0x50, 0xd2, 0xb8, 0xd2, 0xa8, 0xd2, 0xaf, + 0x12, 0x0a, 0xd5, 0x12, 0x09, 0xdd, 0x12, 0x07, 0x12, 0x12, 0x03, 0x1c, + 0x12, 0x00, 0x7e, 0x80, 0xef, 0x20, 0x07, 0x12, 0xc3, 0xe5, 0x10, 0x94, + 0x30, 0xe5, 0x0f, 0x94, 0x75, 0x40, 0x07, 0xe4, 0xf5, 0x0f, 0xf5, 0x10, + 0xd2, 0x07, 0x30, 0x08, 0x14, 0xc3, 0xe5, 0x1c, 0x94, 0xf4, 0xe5, 0x1b, + 0x94, 0x01, 0x40, 0x09, 0xc2, 0x08, 0x90, 0x00, 0x11, 0xe0, 0x44, 0x08, + 0xf0, 0xc3, 0xe5, 0x0d, 0x94, 0x64, 0xe5, 0x0c, 0x94, 0x00, 0x40, 0x12, + 0xe4, 0xf5, 0x0c, 0xf5, 0x0d, 0x7f, 0x02, 0x12, 0x08, 0xf7, 0xad, 0x07, + 0xac, 0x06, 0x8c, 0x51, 0x8d, 0x52, 0x22, 0xc0, 0xe0, 0xc0, 0xf0, 0xc0, + 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x00, 0xc0, 0x00, 0xc0, 0x01, + 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x06, 0xc0, 0x07, + 0x90, 0x00, 0x35, 0xe0, 0x30, 0xe1, 0x03, 0x12, 0x06, 0x6b, 0xd0, 0x07, + 0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, 0x02, 0xd0, 0x01, + 0xd0, 0x00, 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xf0, 0xd0, 0xe0, + 0x32, 0x12, 0x01, 0x73, 0x50, 0x29, 0x12, 0x01, 0xae, 0x40, 0x24, 0x90, + 0x00, 0x81, 0xe0, 0xfe, 0x53, 0x06, 0x0f, 0x20, 0x01, 0x0a, 0xd2, 0x01, + 0x43, 0x06, 0x80, 0x75, 0x43, 0x16, 0x80, 0x0c, 0xc2, 0x01, 0x43, 0x06, + 0x40, 0x12, 0x01, 0x93, 0x70, 0x02, 0x05, 0x46, 0x12, 0x01, 0x88, 0x12, + 0x01, 0x75, 0x40, 0x02, 0x7f, 0x01, 0x22, 0x12, 0x01, 0x73, 0x50, 0x29, + 0x12, 0x01, 0xae, 0x40, 0x24, 0x90, 0x00, 0x81, 0xe0, 0xfe, 0x53, 0x06, + 0xf0, 0x30, 0x00, 0x0a, 0xc2, 0x00, 0x43, 0x06, 0x04, 0x75, 0x43, 0x16, + 0x80, 0x0c, 0xd2, 0x00, 0x43, 0x06, 0x08, 0x12, 0x01, 0x93, 0x70, 0x02, + 0x05, 0x46, 0x12, 0x01, 0x88, 0x12, 0x01, 0x75, 0x40, 0x02, 0x7f, 0x01, + 0x22, 0xaf, 0x0e, 0xef, 0xc3, 0x94, 0x01, 0x40, 0x20, 0xe4, 0xf5, 0x0e, + 0xfe, 0xef, 0xfd, 0xee, 0x25, 0xe0, 0x24, 0x0f, 0x12, 0x0c, 0x25, 0xbe, + 0x07, 0xf3, 0xe4, 0xfe, 0xef, 0xfd, 0xee, 0x25, 0xe0, 0x24, 0x08, 0x12, + 0x0c, 0x25, 0xbe, 0x03, 0xf3, 0x22, 0xef, 0x24, 0xfe, 0x60, 0x0e, 0x14, + 0x60, 0x12, 0x14, 0x60, 0x16, 0x24, 0x03, 0x70, 0x18, 0x12, 0x01, 0xce, + 0x22, 0x90, 0x00, 0x81, 0x74, 0x84, 0xf0, 0x22, 0x90, 0x00, 0x81, 0x74, + 0x44, 0xf0, 0x22, 0x90, 0x00, 0x81, 0x74, 0x48, 0xf0, 0x22, 0xe4, 0xf5, + 0x46, 0xf5, 0x47, 0x8c, 0x4b, 0x8d, 0x4c, 0xbf, 0x18, 0x07, 0xc2, 0x01, + 0x75, 0x48, 0x18, 0x80, 0x08, 0xbf, 0x19, 0x05, 0xd2, 0x00, 0x75, 0x48, + 0x19, 0x12, 0x01, 0xdf, 0x8b, 0x40, 0x22, 0xe4, 0xfd, 0xfc, 0xc3, 0xed, + 0x9f, 0xec, 0x9e, 0x50, 0x15, 0xe4, 0xfb, 0xfa, 0x0b, 0xbb, 0x00, 0x01, + 0x0a, 0xeb, 0x64, 0xbb, 0x4a, 0x70, 0xf5, 0x0d, 0xbd, 0x00, 0x01, 0x0c, + 0x80, 0xe4, 0x22, 0x12, 0x00, 0x46, 0x00, 0x00, 0x00, 0x90, 0x00, 0x06, + 0x74, 0x52, 0xf0, 0x00, 0x00, 0x00, 0x90, 0x00, 0x7c, 0xe0, 0x44, 0x80, + 0xf0, 0x12, 0x01, 0xe9, 0x12, 0x0c, 0x41, 0x75, 0x4e, 0x01, 0x22, 0x90, + 0x00, 0xa4, 0x74, 0x6a, 0xf0, 0x90, 0x00, 0xa1, 0xe0, 0x44, 0x01, 0xf0, + 0x90, 0x00, 0xa0, 0x74, 0x83, 0xf0, 0x90, 0x00, 0x30, 0xe0, 0x44, 0x02, + 0xf0, 0x22, 0xff, 0xef, 0x7f, 0x00, 0xfe, 0xc3, 0xe5, 0x59, 0x9d, 0xfd, + 0xe5, 0x58, 0x9c, 0xfc, 0x02, 0x08, 0xa2, 0xe0, 0x75, 0xf0, 0x04, 0xa4, + 0xff, 0x22, 0x7f, 0x10, 0x78, 0x2c, 0xe4, 0xf6, 0x08, 0xdf, 0xfc, 0x75, + 0x2f, 0x01, 0x75, 0x32, 0x01, 0x75, 0x44, 0x11, 0xe4, 0xf5, 0x45, 0x22, + 0xe4, 0xf5, 0x46, 0xf5, 0x47, 0xed, 0x75, 0x4b, 0x00, 0xf5, 0x4c, 0x8f, + 0x4d, 0x75, 0x48, 0x15, 0x8b, 0x40, 0x12, 0x01, 0xdf, 0x22, 0x30, 0x02, + 0x0e, 0x90, 0x00, 0x11, 0xe0, 0x54, 0xf7, 0xf0, 0xd2, 0x08, 0xe4, 0xf5, + 0x1b, 0xf5, 0x1c, 0x22, 0xc2, 0x8c, 0x43, 0x89, 0x02, 0x75, 0x8a, 0x9c, + 0x75, 0x8c, 0x9c, 0xd2, 0x8c, 0xd2, 0xa9, 0x22, 0xe4, 0xf5, 0x13, 0xf5, + 0x14, 0x8e, 0x41, 0x8f, 0x42, 0x75, 0x48, 0x03, 0x8d, 0x4a, 0x22, 0x90, + 0x00, 0xd1, 0x74, 0x81, 0xf0, 0x90, 0x00, 0xd0, 0xe0, 0x54, 0x7f, 0xf0, + 0x22, 0xf8, 0x08, 0xed, 0x26, 0xf6, 0x18, 0xe4, 0x36, 0xf6, 0x0e, 0x22, + 0x04, 0x5a, 0x57, 0x54, 0x37, 0x30, 0xc1, 0x82, 0x00, 0x90, 0x00, 0xa0, + 0xe0, 0x44, 0x02, 0xf0, 0x22, 0x12, 0x0b, 0x87, 0x02, 0x0c, 0x17 +}; + +unsigned int WT70F_QC3p_V02_210326_FBA1_bin_len = 3143; +#endif +#if 0 +unsigned char WT70F_QC3p_V02_210326_FBA1_bin[] = { + 0x02, 0x07, 0xc5, 0x02, 0x0a, 0x0e, 0xc2, 0x8d, 0x05, 0x0e, 0x32, 0x02, + 0x00, 0x06, 0x75, 0x4e, 0x01, 0x22, 0x32, 0x02, 0x00, 0x12, 0xe5, 0x25, + 0x30, 0xe7, 0x05, 0x75, 0x55, 0x18, 0x80, 0x03, 0x75, 0x55, 0x19, 0xe5, + 0x25, 0x54, 0x7f, 0xfe, 0x7c, 0x00, 0xe4, 0x25, 0x26, 0xff, 0xec, 0x3e, + 0xad, 0x07, 0xfc, 0xe4, 0xfb, 0xaf, 0x55, 0x02, 0x0b, 0x0d, 0x32, 0x02, + 0x00, 0x3a, 0x01, 0x00, 0x32, 0xff, 0xff, 0x02, 0x00, 0x40, 0x90, 0x00, + 0x01, 0x74, 0xb8, 0xf0, 0x90, 0x00, 0x07, 0x74, 0xa4, 0xf0, 0xe4, 0x90, + 0x00, 0x20, 0xf0, 0x90, 0x00, 0x10, 0xf0, 0x90, 0x00, 0x12, 0xf0, 0x90, + 0x00, 0x80, 0xf0, 0x90, 0x00, 0x81, 0xf0, 0x90, 0x00, 0xd0, 0x74, 0x80, + 0xf0, 0x90, 0x00, 0x60, 0x74, 0x20, 0xf0, 0x90, 0x00, 0x6a, 0xe0, 0x44, + 0x80, 0xf0, 0x22, 0x02, 0x02, 0x00, 0x20, 0x05, 0x02, 0x21, 0x46, 0xaf, + 0x24, 0xef, 0xd3, 0x94, 0xb5, 0x50, 0x06, 0x74, 0x2c, 0x2f, 0xf8, 0xa6, + 0x25, 0xef, 0x24, 0x50, 0xb4, 0x0c, 0x00, 0x40, 0x02, 0x21, 0x41, 0x90, + 0x00, 0xa5, 0x25, 0xe0, 0x50, 0x02, 0x05, 0x83, 0x73, 0x01, 0xbd, 0x01, + 0xd2, 0x01, 0xd9, 0x01, 0xe5, 0x01, 0xfe, 0x21, 0x21, 0x21, 0x41, 0x21, + 0x41, 0x21, 0x41, 0x21, 0x41, 0x21, 0x30, 0x21, 0x38, 0x75, 0x2d, 0x01, + 0xe4, 0xf5, 0x31, 0x90, 0x00, 0x81, 0xf0, 0x7d, 0x04, 0x7f, 0xf4, 0x7e, + 0x01, 0x12, 0x0c, 0x06, 0x80, 0x6f, 0xaf, 0x25, 0x12, 0x0a, 0xe5, 0x80, + 0x68, 0xe5, 0x25, 0x70, 0x04, 0xc2, 0x02, 0x80, 0x60, 0xd2, 0x02, 0x80, + 0x5c, 0xe4, 0x90, 0x00, 0x81, 0xf0, 0x90, 0x00, 0x80, 0xf0, 0x12, 0x0b, + 0xb8, 0x75, 0x30, 0x01, 0x75, 0x4e, 0x01, 0xe4, 0xf5, 0x11, 0xf5, 0x12, + 0x80, 0x43, 0xe5, 0x25, 0xb4, 0x01, 0x0a, 0x75, 0x4e, 0x01, 0xe4, 0xf5, + 0x11, 0xf5, 0x12, 0x80, 0x0e, 0xe4, 0x90, 0x00, 0x81, 0xf0, 0x75, 0x44, + 0x11, 0xf5, 0x45, 0xf5, 0x48, 0xf5, 0x4e, 0xf5, 0x19, 0xf5, 0x1a, 0x80, + 0x20, 0xe5, 0x25, 0xb4, 0x01, 0x1b, 0xe4, 0xf5, 0x11, 0xf5, 0x12, 0x12, + 0x0b, 0x4e, 0x80, 0x11, 0x85, 0x25, 0x34, 0x85, 0x26, 0x35, 0x80, 0x06, + 0x85, 0x25, 0x36, 0x85, 0x26, 0x37, 0x12, 0x00, 0x16, 0xe4, 0xf5, 0x24, + 0xc2, 0x05, 0x30, 0x06, 0x17, 0xe5, 0x24, 0x24, 0x4d, 0x60, 0x09, 0x24, + 0x03, 0x70, 0x08, 0xe4, 0xf5, 0x2d, 0x80, 0x03, 0xe4, 0xf5, 0x30, 0xe4, + 0xf5, 0x24, 0xc2, 0x06, 0x22, 0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, + 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, 0xfe, 0x02, 0xe3, 0x22, 0x89, + 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22, 0xbb, 0x01, 0x0c, 0xe5, 0x82, 0x29, + 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50, 0x06, 0xe9, + 0x25, 0x82, 0xf8, 0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, + 0xe2, 0x22, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, + 0xe4, 0x93, 0x22, 0xbc, 0x00, 0x0b, 0xbe, 0x00, 0x29, 0xef, 0x8d, 0xf0, + 0x84, 0xff, 0xad, 0xf0, 0x22, 0xe4, 0xcc, 0xf8, 0x75, 0xf0, 0x08, 0xef, + 0x2f, 0xff, 0xee, 0x33, 0xfe, 0xec, 0x33, 0xfc, 0xee, 0x9d, 0xec, 0x98, + 0x40, 0x05, 0xfc, 0xee, 0x9d, 0xfe, 0x0f, 0xd5, 0xf0, 0xe9, 0xe4, 0xce, + 0xfd, 0x22, 0xed, 0xf8, 0xf5, 0xf0, 0xee, 0x84, 0x20, 0xd2, 0x1c, 0xfe, + 0xad, 0xf0, 0x75, 0xf0, 0x08, 0xef, 0x2f, 0xff, 0xed, 0x33, 0xfd, 0x40, + 0x07, 0x98, 0x50, 0x06, 0xd5, 0xf0, 0xf2, 0x22, 0xc3, 0x98, 0xfd, 0x0f, + 0xd5, 0xf0, 0xea, 0x22, 0xff, 0xff, 0xff, 0xff, 0xc0, 0xe0, 0xc0, 0xf0, + 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0xc0, 0x00, 0x75, 0xd0, 0x00, 0xc3, + 0xe5, 0x81, 0x94, 0x07, 0xf8, 0xa6, 0xfd, 0x08, 0xa6, 0xf5, 0x90, 0x00, + 0x78, 0x74, 0xa0, 0xf0, 0xd2, 0xfb, 0x74, 0xa0, 0x55, 0xf8, 0xb4, 0xa0, + 0xf9, 0xe5, 0xf9, 0xb4, 0x00, 0x02, 0x41, 0xf7, 0xb4, 0x01, 0x02, 0x80, + 0x60, 0xb4, 0x02, 0x02, 0x80, 0x6e, 0xb4, 0x03, 0x02, 0x80, 0x72, 0xb4, + 0x04, 0x02, 0x80, 0x79, 0xb4, 0x05, 0x02, 0x80, 0x7d, 0xb4, 0x06, 0x02, + 0x41, 0xd2, 0xb4, 0x07, 0x02, 0x41, 0xe3, 0xb4, 0x16, 0x02, 0x80, 0x07, + 0xb4, 0x17, 0x02, 0x80, 0x1c, 0x80, 0xc3, 0x30, 0xfd, 0xfd, 0x85, 0xf9, + 0x83, 0x30, 0xfd, 0xfd, 0x85, 0xf9, 0x82, 0xe0, 0xf5, 0xf9, 0xa3, 0x20, + 0xfe, 0x02, 0x80, 0xf7, 0x20, 0xfc, 0x1c, 0x80, 0xf6, 0x30, 0xfd, 0xfd, + 0x85, 0xf9, 0x83, 0x30, 0xfd, 0xfd, 0x85, 0xf9, 0x82, 0x30, 0xfd, 0x06, + 0xe5, 0xf9, 0xf0, 0xa3, 0x80, 0xf7, 0x20, 0xfc, 0x02, 0x80, 0xf2, 0x80, + 0x8d, 0x30, 0xfd, 0xfd, 0x85, 0xf9, 0x83, 0x30, 0xfd, 0xfd, 0x85, 0xf9, + 0x82, 0x74, 0x00, 0x93, 0xf5, 0xf9, 0x41, 0x22, 0x30, 0xfd, 0xfd, 0xa8, + 0xf9, 0x86, 0xf9, 0x41, 0x22, 0x30, 0xfd, 0xfd, 0xa8, 0xf9, 0x30, 0xfd, + 0xfd, 0xa6, 0xf9, 0x41, 0x22, 0x30, 0xfd, 0xfd, 0xe5, 0xf9, 0xf5, 0xf9, + 0x41, 0x22, 0x30, 0xfd, 0xfd, 0xe5, 0xf9, 0x30, 0xfd, 0xfd, 0xf5, 0xf9, + 0x41, 0x22, 0x30, 0xfd, 0xfd, 0x85, 0xf9, 0x83, 0x30, 0xfd, 0xfd, 0x85, + 0xf9, 0x82, 0xe0, 0xf5, 0xf9, 0x41, 0x22, 0x30, 0xfd, 0xfd, 0x85, 0xf9, + 0x83, 0x30, 0xfd, 0xfd, 0x85, 0xf9, 0x82, 0x30, 0xfd, 0xfd, 0xe5, 0xf9, + 0xf0, 0x41, 0x22, 0x53, 0x88, 0x5f, 0x90, 0x00, 0x7c, 0xe0, 0x44, 0x20, + 0xf0, 0x90, 0x00, 0xb2, 0xe0, 0x44, 0x10, 0xf0, 0xc2, 0xfb, 0xd0, 0x00, + 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xf0, 0xd0, 0xe0, 0x32, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe5, 0x48, 0x14, 0xb4, 0x19, 0x00, 0x40, 0x02, + 0xc1, 0x2c, 0x90, 0x03, 0x30, 0x25, 0xe0, 0x50, 0x02, 0x05, 0x83, 0x73, + 0x61, 0x62, 0x61, 0x6c, 0x61, 0x7a, 0x61, 0x95, 0x61, 0xa2, 0x61, 0xc4, + 0x61, 0xe2, 0x61, 0xf0, 0x81, 0x22, 0x81, 0x30, 0x81, 0x50, 0x81, 0x5b, + 0x81, 0x9e, 0x81, 0xb7, 0x81, 0xbf, 0x81, 0xf5, 0x81, 0xfd, 0xa1, 0x2c, + 0xa1, 0x34, 0xa1, 0x76, 0xa1, 0x78, 0xa1, 0xda, 0xa1, 0xe9, 0xc1, 0x0f, + 0xc1, 0x1d, 0xd2, 0x03, 0xe4, 0xf5, 0x0c, 0xf5, 0x0d, 0xf5, 0x48, 0x22, + 0x90, 0x00, 0x81, 0x74, 0x01, 0xf0, 0x7d, 0x05, 0x7f, 0x88, 0x7e, 0x13, + 0xa1, 0xe6, 0xc3, 0xe5, 0x14, 0x95, 0x42, 0xe5, 0x13, 0x95, 0x41, 0x50, + 0x02, 0xc1, 0x2c, 0x85, 0x4a, 0x48, 0xe4, 0xf5, 0x15, 0xf5, 0x16, 0xf5, + 0x17, 0xf5, 0x18, 0x81, 0x99, 0x90, 0x00, 0x81, 0x74, 0x01, 0xf0, 0x7d, + 0x05, 0x7f, 0xf4, 0xfe, 0xa1, 0xe6, 0x12, 0x06, 0x2d, 0x50, 0x02, 0xc1, + 0x2c, 0x12, 0x09, 0x0a, 0x40, 0x08, 0x75, 0x44, 0x07, 0xe4, 0xf5, 0x45, + 0x81, 0x4b, 0x90, 0x00, 0x81, 0x74, 0x42, 0xf0, 0x7d, 0x06, 0x7f, 0xf4, + 0x7e, 0x01, 0xa1, 0xe6, 0x12, 0x06, 0x2d, 0x50, 0x02, 0xc1, 0x2c, 0x12, + 0x09, 0x0a, 0xe4, 0x90, 0x00, 0x81, 0x40, 0x05, 0xf0, 0x7d, 0x07, 0x80, + 0x03, 0xf0, 0x7d, 0x09, 0x7f, 0xf4, 0x7e, 0x01, 0xa1, 0xe6, 0x90, 0x00, + 0x81, 0x74, 0x14, 0xf0, 0x7d, 0x08, 0x7f, 0xf4, 0x7e, 0x01, 0xa1, 0xe6, + 0x12, 0x06, 0x4b, 0x12, 0x09, 0x30, 0x50, 0x02, 0xc1, 0x2c, 0x12, 0x09, + 0x0a, 0x40, 0x11, 0x75, 0x44, 0x04, 0xe4, 0xf5, 0x45, 0x7d, 0x0b, 0x7f, + 0xf4, 0x7e, 0x01, 0x12, 0x0c, 0x06, 0x80, 0x0c, 0x75, 0x44, 0x03, 0xe4, + 0xf5, 0x45, 0x75, 0x48, 0x01, 0x12, 0x0b, 0xe4, 0xe4, 0x90, 0x00, 0x81, + 0xf0, 0x22, 0x90, 0x00, 0x81, 0x74, 0x21, 0xf0, 0x7d, 0x0a, 0x7f, 0xf4, + 0x7e, 0x01, 0xa1, 0xe6, 0x12, 0x06, 0x4b, 0x12, 0x09, 0x30, 0x50, 0x02, + 0xc1, 0x2c, 0x12, 0x09, 0x0a, 0x40, 0x05, 0x75, 0x44, 0x01, 0x80, 0x03, + 0x75, 0x44, 0x02, 0xe4, 0xf5, 0x45, 0xe4, 0x90, 0x00, 0x81, 0xc1, 0x08, + 0x12, 0x09, 0x59, 0x7d, 0x0c, 0x7f, 0xf4, 0x7e, 0x01, 0xa1, 0xe6, 0xc3, + 0xe5, 0x16, 0x94, 0x98, 0xe5, 0x15, 0x94, 0x3a, 0x40, 0x02, 0xa1, 0x76, + 0x7f, 0x01, 0x12, 0x08, 0x68, 0x8e, 0x56, 0x8f, 0x57, 0xc3, 0x12, 0x09, + 0x0a, 0x50, 0x21, 0xc3, 0x12, 0x09, 0x30, 0x50, 0x02, 0xc1, 0x2c, 0xe5, + 0x16, 0x94, 0x40, 0xe5, 0x15, 0x94, 0x1f, 0x40, 0x0b, 0xe4, 0xf5, 0x45, + 0x7d, 0x0d, 0x7f, 0xd0, 0x7e, 0x07, 0xa1, 0xe6, 0x75, 0x48, 0x01, 0x22, + 0xe4, 0xf5, 0x0a, 0xf5, 0x0b, 0x22, 0x90, 0x00, 0x81, 0x74, 0x48, 0xf0, + 0x7b, 0x0e, 0x7d, 0x10, 0x7c, 0x00, 0x7f, 0x18, 0x12, 0x0b, 0x0d, 0x7d, + 0x18, 0x7f, 0xb0, 0x7e, 0x04, 0xa1, 0xe6, 0x7d, 0x0f, 0x7f, 0xf4, 0x7e, + 0x01, 0xa1, 0xe6, 0x12, 0x06, 0x40, 0xe5, 0x57, 0x94, 0x9b, 0xe5, 0x56, + 0x94, 0x01, 0x40, 0x1b, 0x7b, 0x10, 0x7d, 0x10, 0x7c, 0x00, 0x7f, 0x19, + 0x12, 0x0b, 0x0d, 0x7d, 0x19, 0x7f, 0xf4, 0x7e, 0x01, 0x12, 0x0c, 0x06, + 0x75, 0x44, 0x06, 0xe4, 0xf5, 0x45, 0x22, 0x75, 0x45, 0x03, 0x12, 0x09, + 0x59, 0x7d, 0x16, 0x7f, 0xe8, 0x7e, 0x03, 0xa1, 0xe6, 0x7d, 0x11, 0x7f, + 0xb0, 0x7e, 0x04, 0xa1, 0xe6, 0x12, 0x09, 0x60, 0x40, 0x02, 0x80, 0x35, + 0x12, 0x06, 0x40, 0xe5, 0x57, 0x94, 0x1a, 0xe5, 0x56, 0x94, 0x01, 0x40, + 0x15, 0xe5, 0x18, 0x94, 0x64, 0xe5, 0x17, 0x94, 0x00, 0x50, 0x02, 0xc1, + 0x2c, 0x7b, 0x12, 0x7d, 0x06, 0x7f, 0xa8, 0x02, 0x0b, 0xce, 0xe4, 0xf5, + 0x17, 0xf5, 0x18, 0x22, 0x7d, 0x13, 0x7f, 0xc8, 0x7e, 0x00, 0xa1, 0xe6, + 0x12, 0x09, 0x60, 0x40, 0x05, 0x75, 0x45, 0x03, 0xc1, 0x09, 0x12, 0x06, + 0x56, 0xe5, 0x18, 0x94, 0x64, 0xe5, 0x17, 0x94, 0x00, 0x50, 0x02, 0xc1, + 0x2c, 0xe5, 0x57, 0x94, 0x85, 0xe5, 0x56, 0x94, 0x01, 0x40, 0x08, 0x12, + 0x06, 0x61, 0x75, 0x44, 0x09, 0x80, 0x13, 0xc3, 0xe5, 0x57, 0x94, 0x50, + 0xe5, 0x56, 0x94, 0x01, 0x50, 0x02, 0xc1, 0x2c, 0x12, 0x06, 0x61, 0x75, + 0x44, 0x08, 0x75, 0x45, 0x04, 0x22, 0xc1, 0x09, 0x12, 0x09, 0x00, 0x40, + 0x02, 0xc1, 0x29, 0xc3, 0xe5, 0x09, 0x95, 0x43, 0xe5, 0x08, 0x94, 0x00, + 0x50, 0x02, 0xc1, 0x2c, 0x90, 0x00, 0x81, 0xe0, 0xf5, 0x55, 0xe5, 0x4d, + 0x30, 0xe7, 0x1b, 0x53, 0x55, 0x0f, 0x20, 0x01, 0x07, 0xd2, 0x01, 0x43, + 0x55, 0x80, 0x80, 0x19, 0xc2, 0x01, 0x43, 0x55, 0x40, 0x12, 0x09, 0x1e, + 0x70, 0x20, 0x05, 0x46, 0x80, 0x1c, 0x53, 0x55, 0xf0, 0x30, 0x00, 0x0a, + 0xc2, 0x00, 0x43, 0x55, 0x04, 0x75, 0x43, 0x16, 0x80, 0x12, 0xd2, 0x00, + 0x43, 0x55, 0x08, 0x12, 0x09, 0x1e, 0x70, 0x02, 0x05, 0x46, 0xe5, 0x4d, + 0x25, 0xe0, 0xf5, 0x4d, 0xe5, 0x55, 0x12, 0x09, 0x14, 0x22, 0x90, 0x00, + 0x81, 0x74, 0x84, 0xf0, 0x7d, 0x17, 0x7f, 0xe8, 0x7e, 0x03, 0x02, 0x0c, + 0x06, 0x12, 0x06, 0x56, 0xe5, 0x18, 0x94, 0x32, 0xe5, 0x17, 0x94, 0x00, + 0x40, 0x36, 0xe5, 0x57, 0x94, 0xbf, 0xe5, 0x56, 0x94, 0x01, 0x40, 0x03, + 0x75, 0x44, 0x05, 0x90, 0x00, 0x81, 0x74, 0x40, 0xf0, 0x75, 0x48, 0x01, + 0x02, 0x0b, 0xe4, 0x12, 0x0a, 0x50, 0x8f, 0x55, 0xe5, 0x55, 0xd3, 0x94, + 0x00, 0x40, 0x11, 0x80, 0x0c, 0x12, 0x0a, 0x86, 0x8f, 0x55, 0xe5, 0x55, + 0xd3, 0x94, 0x00, 0x40, 0x03, 0x85, 0x40, 0x48, 0x22, 0x7f, 0x01, 0x12, + 0x08, 0x68, 0x8e, 0x56, 0x8f, 0x57, 0xc3, 0xe5, 0x0b, 0x94, 0x32, 0xe5, + 0x0a, 0x94, 0x00, 0x22, 0x7f, 0x02, 0x12, 0x08, 0x68, 0x8e, 0x56, 0x8f, + 0x57, 0xc3, 0x22, 0xe4, 0xff, 0x12, 0x08, 0x68, 0x8e, 0x56, 0x8f, 0x57, + 0xc3, 0x22, 0x7f, 0x02, 0x12, 0x08, 0x68, 0x8e, 0x56, 0x8f, 0x57, 0xc3, + 0x22, 0x7b, 0x14, 0x7d, 0x04, 0x7f, 0xc0, 0x12, 0x0b, 0xce, 0x22, 0x7f, + 0x02, 0x12, 0x08, 0x68, 0x8e, 0x55, 0x8f, 0x56, 0xe5, 0x4e, 0x14, 0x60, + 0x17, 0x14, 0x60, 0x74, 0x24, 0x02, 0x60, 0x02, 0xe1, 0x1d, 0x12, 0x09, + 0x45, 0x50, 0x02, 0xe1, 0x1d, 0xe4, 0xf5, 0x19, 0xf5, 0x1a, 0x80, 0x55, + 0xc3, 0x12, 0x09, 0x27, 0x40, 0x30, 0xd3, 0xe5, 0x56, 0x94, 0x41, 0xe5, + 0x55, 0x94, 0x01, 0x50, 0x25, 0x12, 0x09, 0x4f, 0x40, 0x25, 0xe4, 0xf5, + 0x11, 0xf5, 0x12, 0x75, 0x4e, 0x02, 0x90, 0x00, 0x81, 0xf0, 0x90, 0x00, + 0x80, 0x74, 0xc0, 0xf0, 0x75, 0x48, 0x02, 0x7d, 0x02, 0x7f, 0xe8, 0x7e, + 0x03, 0x12, 0x0c, 0x06, 0x80, 0x05, 0xe4, 0xf5, 0x11, 0xf5, 0x12, 0x12, + 0x09, 0x26, 0x50, 0x18, 0x12, 0x09, 0x45, 0x40, 0x48, 0xe4, 0xf5, 0x19, + 0xf5, 0x1a, 0x05, 0x49, 0xe5, 0x49, 0x94, 0x0a, 0x40, 0x3b, 0xe4, 0xf5, + 0x49, 0x02, 0x0b, 0x4e, 0xe4, 0xf5, 0x49, 0xf5, 0x19, 0xf5, 0x1a, 0x22, + 0x12, 0x09, 0x26, 0x50, 0x23, 0x12, 0x09, 0x4f, 0x40, 0x23, 0x75, 0x44, + 0x11, 0xe4, 0xf5, 0x45, 0xf5, 0x11, 0xf5, 0x12, 0xf5, 0x19, 0xf5, 0x1a, + 0xf5, 0x49, 0x75, 0x4e, 0x01, 0xf5, 0x48, 0xc2, 0x03, 0x90, 0x00, 0x81, + 0xf0, 0x02, 0x0b, 0xb8, 0xe4, 0xf5, 0x11, 0xf5, 0x12, 0x22, 0x90, 0x00, + 0xa1, 0xe0, 0x20, 0xe2, 0x77, 0x30, 0xe3, 0x06, 0x12, 0x0c, 0x37, 0xc2, + 0x04, 0x22, 0x20, 0x04, 0x49, 0x90, 0x00, 0xa3, 0xe0, 0xf5, 0x24, 0xe5, + 0x24, 0x24, 0x42, 0x60, 0x12, 0x14, 0x60, 0x1a, 0x24, 0x02, 0x70, 0x1f, + 0x75, 0x3d, 0x00, 0x75, 0x3e, 0x00, 0x75, 0x3f, 0x44, 0x80, 0x14, 0x75, + 0x3d, 0x00, 0x75, 0x3e, 0x00, 0x75, 0x3f, 0x51, 0x80, 0x09, 0x75, 0x3d, + 0xff, 0x75, 0x3e, 0x00, 0x75, 0x3f, 0x3e, 0xab, 0x3d, 0xaa, 0x3e, 0xa9, + 0x3f, 0x12, 0x01, 0x61, 0x90, 0x00, 0xa3, 0xf0, 0xd2, 0x04, 0x75, 0x23, + 0x01, 0x75, 0x3c, 0x01, 0x80, 0x0e, 0x90, 0x00, 0xa3, 0xe0, 0xff, 0x74, + 0x24, 0x25, 0x23, 0xf8, 0xa6, 0x07, 0x05, 0x23, 0x12, 0x0c, 0x37, 0x90, + 0x00, 0xa1, 0xe0, 0x30, 0xe5, 0x32, 0x90, 0x00, 0xa0, 0xe0, 0x44, 0x03, + 0xf0, 0xd2, 0x05, 0x22, 0xab, 0x3d, 0xaa, 0x3e, 0xa9, 0x3f, 0x85, 0x3c, + 0x82, 0x75, 0x83, 0x00, 0x12, 0x01, 0x7a, 0x90, 0x00, 0xa3, 0xf0, 0x05, + 0x3c, 0x12, 0x0c, 0x37, 0x90, 0x00, 0xa1, 0xe0, 0x30, 0xe5, 0x09, 0x90, + 0x00, 0xa0, 0xe0, 0x44, 0x01, 0xf0, 0xd2, 0x06, 0x22, 0x75, 0xa0, 0x00, + 0x75, 0x81, 0x5d, 0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x90, 0x01, 0x00, + 0x7f, 0x00, 0x7e, 0x01, 0xe4, 0xf0, 0xa3, 0xdf, 0xfc, 0xde, 0xfa, 0x43, + 0xb8, 0x00, 0x75, 0xa8, 0x00, 0x02, 0x08, 0x23, 0x02, 0x09, 0x74, 0xe4, + 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2, + 0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, + 0x0c, 0xc8, 0xc3, 0x33, 0xc4, 0x54, 0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, + 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf, 0xe4, 0x80, 0x0b, 0x01, + 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x0c, 0x2e, 0xe4, 0x7e, + 0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, + 0x1f, 0xfe, 0xe4, 0x93, 0xa3, 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, + 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3, 0xfa, 0xe4, 0x93, 0xa3, + 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, + 0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, + 0xde, 0xe7, 0x80, 0xbe, 0xa9, 0x07, 0x90, 0x08, 0x10, 0x12, 0x0b, 0x9a, + 0x90, 0x08, 0x11, 0xe0, 0x7c, 0x00, 0x2f, 0xff, 0xec, 0x35, 0xf0, 0xad, + 0x07, 0xfc, 0x90, 0x08, 0x12, 0x12, 0x0b, 0x9a, 0xae, 0xf0, 0x90, 0x08, + 0x13, 0xe0, 0x7a, 0x00, 0x2f, 0xf5, 0x59, 0xea, 0x3e, 0xf5, 0x58, 0x90, + 0x00, 0xd3, 0xe9, 0xf0, 0x00, 0x00, 0x90, 0x00, 0xd0, 0xe0, 0x44, 0x40, + 0xf0, 0x00, 0x90, 0x00, 0xd2, 0xe0, 0x30, 0xe2, 0xf8, 0x90, 0x00, 0xd6, + 0x12, 0x0b, 0x9a, 0x90, 0x00, 0xd7, 0xe0, 0x54, 0x03, 0x7a, 0x00, 0x2f, + 0xff, 0xea, 0x35, 0xf0, 0xfe, 0xe9, 0x64, 0x02, 0x70, 0x3b, 0xd3, 0xef, + 0x94, 0x0a, 0xee, 0x94, 0x00, 0x40, 0x32, 0xe4, 0x2d, 0xfb, 0xec, 0x34, + 0x01, 0xfa, 0xc3, 0xef, 0x9b, 0xee, 0x9a, 0x50, 0x24, 0xc3, 0xef, 0x9d, + 0xee, 0x9c, 0x40, 0x0e, 0xef, 0x9d, 0x12, 0x0b, 0x89, 0xef, 0x24, 0x07, + 0xff, 0xee, 0x34, 0x01, 0xfe, 0x22, 0xc3, 0xed, 0x9f, 0x12, 0x0b, 0x89, + 0xc3, 0x74, 0x07, 0x9f, 0xff, 0x74, 0x01, 0x9e, 0xfe, 0x22, 0xe4, 0xff, + 0xc3, 0xe5, 0x47, 0x95, 0x4c, 0xe5, 0x46, 0x95, 0x4b, 0x22, 0xe5, 0x57, + 0x94, 0xaa, 0xe5, 0x56, 0x94, 0x00, 0x22, 0xee, 0x90, 0x00, 0x81, 0xf0, + 0xe4, 0xf5, 0x08, 0xf5, 0x09, 0x22, 0x75, 0x43, 0x16, 0x05, 0x47, 0xe5, + 0x47, 0x22, 0xd3, 0xe5, 0x56, 0x94, 0x9e, 0xe5, 0x55, 0x94, 0x00, 0x22, + 0xe5, 0x0b, 0x94, 0x32, 0xe5, 0x0a, 0x94, 0x00, 0x22, 0xe5, 0x43, 0xfd, + 0xc3, 0xe5, 0x09, 0x9d, 0xe5, 0x08, 0x94, 0x00, 0x22, 0xc3, 0xe5, 0x1a, + 0x94, 0x10, 0xe5, 0x19, 0x94, 0x27, 0x22, 0xc3, 0xe5, 0x12, 0x94, 0xc8, + 0xe5, 0x11, 0x94, 0x00, 0x22, 0x90, 0x00, 0x81, 0x74, 0x40, 0xf0, 0x22, + 0xc3, 0xe5, 0x16, 0x94, 0xd0, 0xe5, 0x15, 0x94, 0x07, 0x22, 0x75, 0x08, + 0x00, 0x75, 0x09, 0x16, 0x75, 0x43, 0x16, 0x22, 0xc0, 0xd0, 0x75, 0xd0, + 0x00, 0x90, 0x00, 0x73, 0x74, 0x80, 0xf0, 0x90, 0x00, 0x78, 0xe0, 0x44, + 0xa0, 0xf0, 0x12, 0x0b, 0xa1, 0x12, 0x0b, 0xf6, 0x12, 0x0c, 0x3f, 0x12, + 0x00, 0x0e, 0x12, 0x0b, 0xb8, 0x90, 0x08, 0x10, 0xe0, 0xf5, 0x53, 0x90, + 0x08, 0x11, 0xe0, 0xf5, 0x54, 0x90, 0x08, 0x12, 0xe0, 0xf5, 0x4f, 0x90, + 0x08, 0x13, 0xe0, 0xf5, 0x50, 0xd2, 0xb8, 0xd2, 0xa8, 0xd2, 0xaf, 0x12, + 0x0a, 0xbc, 0x12, 0x09, 0xc4, 0x12, 0x06, 0x6b, 0x12, 0x03, 0x1c, 0x12, + 0x00, 0x7e, 0x80, 0xef, 0x20, 0x07, 0x12, 0xc3, 0xe5, 0x10, 0x94, 0x30, + 0xe5, 0x0f, 0x94, 0x75, 0x40, 0x07, 0xe4, 0xf5, 0x0f, 0xf5, 0x10, 0xd2, + 0x07, 0x30, 0x08, 0x14, 0xc3, 0xe5, 0x1c, 0x94, 0xf4, 0xe5, 0x1b, 0x94, + 0x01, 0x40, 0x09, 0xc2, 0x08, 0x90, 0x00, 0x11, 0xe0, 0x44, 0x08, 0xf0, + 0xc3, 0xe5, 0x0d, 0x94, 0x64, 0xe5, 0x0c, 0x94, 0x00, 0x40, 0x12, 0xe4, + 0xf5, 0x0c, 0xf5, 0x0d, 0x7f, 0x02, 0x12, 0x08, 0x68, 0xad, 0x07, 0xac, + 0x06, 0x8c, 0x51, 0x8d, 0x52, 0x22, 0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83, + 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x00, 0xc0, 0x00, 0xc0, 0x01, 0xc0, + 0x02, 0xc0, 0x03, 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x06, 0xc0, 0x07, 0x90, + 0x00, 0x35, 0xe0, 0x30, 0xe1, 0x03, 0x12, 0x07, 0x1e, 0xd0, 0x07, 0xd0, + 0x06, 0xd0, 0x05, 0xd0, 0x04, 0xd0, 0x03, 0xd0, 0x02, 0xd0, 0x01, 0xd0, + 0x00, 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xf0, 0xd0, 0xe0, 0x32, + 0x12, 0x08, 0xfe, 0x50, 0x29, 0x12, 0x09, 0x39, 0x40, 0x24, 0x90, 0x00, + 0x81, 0xe0, 0xfe, 0x53, 0x06, 0x0f, 0x20, 0x01, 0x0a, 0xd2, 0x01, 0x43, + 0x06, 0x80, 0x75, 0x43, 0x16, 0x80, 0x0c, 0xc2, 0x01, 0x43, 0x06, 0x40, + 0x12, 0x09, 0x1e, 0x70, 0x02, 0x05, 0x46, 0x12, 0x09, 0x13, 0x12, 0x09, + 0x00, 0x40, 0x02, 0x7f, 0x01, 0x22, 0x12, 0x08, 0xfe, 0x50, 0x29, 0x12, + 0x09, 0x39, 0x40, 0x24, 0x90, 0x00, 0x81, 0xe0, 0xfe, 0x53, 0x06, 0xf0, + 0x30, 0x00, 0x0a, 0xc2, 0x00, 0x43, 0x06, 0x04, 0x75, 0x43, 0x16, 0x80, + 0x0c, 0xd2, 0x00, 0x43, 0x06, 0x08, 0x12, 0x09, 0x1e, 0x70, 0x02, 0x05, + 0x46, 0x12, 0x09, 0x13, 0x12, 0x09, 0x00, 0x40, 0x02, 0x7f, 0x01, 0x22, + 0xaf, 0x0e, 0xef, 0xc3, 0x94, 0x01, 0x40, 0x20, 0xe4, 0xf5, 0x0e, 0xfe, + 0xef, 0xfd, 0xee, 0x25, 0xe0, 0x24, 0x0f, 0x12, 0x0c, 0x23, 0xbe, 0x07, + 0xf3, 0xe4, 0xfe, 0xef, 0xfd, 0xee, 0x25, 0xe0, 0x24, 0x08, 0x12, 0x0c, + 0x23, 0xbe, 0x03, 0xf3, 0x22, 0xef, 0x24, 0xfe, 0x60, 0x0e, 0x14, 0x60, + 0x12, 0x14, 0x60, 0x16, 0x24, 0x03, 0x70, 0x18, 0x12, 0x09, 0x59, 0x22, + 0x90, 0x00, 0x81, 0x74, 0x84, 0xf0, 0x22, 0x90, 0x00, 0x81, 0x74, 0x44, + 0xf0, 0x22, 0x90, 0x00, 0x81, 0x74, 0x48, 0xf0, 0x22, 0xe4, 0xf5, 0x46, + 0xf5, 0x47, 0x8c, 0x4b, 0x8d, 0x4c, 0xbf, 0x18, 0x07, 0xc2, 0x01, 0x75, + 0x48, 0x18, 0x80, 0x08, 0xbf, 0x19, 0x05, 0xd2, 0x00, 0x75, 0x48, 0x19, + 0x12, 0x09, 0x6a, 0x8b, 0x40, 0x22, 0xe4, 0xfd, 0xfc, 0xc3, 0xed, 0x9f, + 0xec, 0x9e, 0x50, 0x15, 0xe4, 0xfb, 0xfa, 0x0b, 0xbb, 0x00, 0x01, 0x0a, + 0xeb, 0x64, 0xbb, 0x4a, 0x70, 0xf5, 0x0d, 0xbd, 0x00, 0x01, 0x0c, 0x80, + 0xe4, 0x22, 0x12, 0x00, 0x46, 0x00, 0x00, 0x00, 0x90, 0x00, 0x06, 0x74, + 0x52, 0xf0, 0x00, 0x00, 0x00, 0x90, 0x00, 0x7c, 0xe0, 0x44, 0x80, 0xf0, + 0x12, 0x0b, 0xa1, 0x12, 0x0c, 0x3f, 0x75, 0x4e, 0x01, 0x22, 0x90, 0x00, + 0xa4, 0x74, 0x6a, 0xf0, 0x90, 0x00, 0xa1, 0xe0, 0x44, 0x01, 0xf0, 0x90, + 0x00, 0xa0, 0x74, 0x83, 0xf0, 0x90, 0x00, 0x30, 0xe0, 0x44, 0x02, 0xf0, + 0x22, 0xff, 0xef, 0x7f, 0x00, 0xfe, 0xc3, 0xe5, 0x59, 0x9d, 0xfd, 0xe5, + 0x58, 0x9c, 0xfc, 0x02, 0x01, 0xa7, 0xe0, 0x75, 0xf0, 0x04, 0xa4, 0xff, + 0x22, 0x90, 0x00, 0x11, 0x74, 0x08, 0xf0, 0x90, 0x00, 0x10, 0xf0, 0x90, + 0x00, 0x12, 0x74, 0x03, 0xf0, 0x90, 0x00, 0x20, 0x74, 0x34, 0xf0, 0x22, + 0x7f, 0x10, 0x78, 0x2c, 0xe4, 0xf6, 0x08, 0xdf, 0xfc, 0x75, 0x2f, 0x01, + 0x75, 0x32, 0x01, 0x75, 0x44, 0x11, 0xe4, 0xf5, 0x45, 0x22, 0xe4, 0xf5, + 0x46, 0xf5, 0x47, 0xed, 0x75, 0x4b, 0x00, 0xf5, 0x4c, 0x8f, 0x4d, 0x75, + 0x48, 0x15, 0x8b, 0x40, 0x12, 0x09, 0x6a, 0x22, 0x30, 0x02, 0x0e, 0x90, + 0x00, 0x11, 0xe0, 0x54, 0xf7, 0xf0, 0xd2, 0x08, 0xe4, 0xf5, 0x1b, 0xf5, + 0x1c, 0x22, 0xc2, 0x8c, 0x43, 0x89, 0x02, 0x75, 0x8a, 0x9c, 0x75, 0x8c, + 0x9c, 0xd2, 0x8c, 0xd2, 0xa9, 0x22, 0xe4, 0xf5, 0x13, 0xf5, 0x14, 0x8e, + 0x41, 0x8f, 0x42, 0x75, 0x48, 0x03, 0x8d, 0x4a, 0x22, 0x90, 0x00, 0xd1, + 0x74, 0x81, 0xf0, 0x90, 0x00, 0xd0, 0xe0, 0x54, 0x7f, 0xf0, 0x22, 0xf8, + 0x08, 0xed, 0x26, 0xf6, 0x18, 0xe4, 0x36, 0xf6, 0x0e, 0x22, 0x04, 0x5a, + 0x57, 0x54, 0x37, 0x30, 0xc1, 0x82, 0x00, 0x90, 0x00, 0xa0, 0xe0, 0x44, + 0x02, 0xf0, 0x22, 0x12, 0x0b, 0x6e, 0x02, 0x0c, 0x15 +}; +unsigned int WT70F_QC3p_V02_210326_FBA1_bin_len = 3141; + +#endif + +/** + * Enter ISP Mode Sequence + */ + +void wt_enter_isp_mode_sequence(struct wt6670f *chip) +{ + int ret; + u8 data[1]={0x00}; + struct i2c_msg msgs[] = { + { + .addr = 0x2B, + .flags = 0, + .len = 1, + .buf = data, + }, + { + .addr = 0x48, + .flags = 0, + .len = 1, + .buf = data, + }, + }; + + mutex_lock(&chip->i2c_rw_lock); + ret = i2c_transfer(chip->client->adapter, msgs, 2); + if (ret != 2) + { + pr_info("[%s] i2c transfer failed, ret:%d\n", __func__, ret); + } + mutex_unlock(&chip->i2c_rw_lock); +} +/** + * ENABLE ISP Command + * (1) This command is used to enable ISP through I2C interface. + * (2) WT6670F will ACK all bytes, HOST must use READ CHIP ID command to + * check that ISP mode is enabled + */ +int wt_enable_isp(struct wt6670f *chip) +{ + u8 cmd_buf[7] = {0x57, 0x54, 0x36, 0x36, 0x37, 0x30, 0x46}; + struct i2c_msg msg; + int ret = 0; + + memset(&msg, 0, sizeof(struct i2c_msg)); + + msg.addr = WT6670_ISP_I2C_ADDR; + msg.flags = 0; + msg.buf = cmd_buf; + msg.len = sizeof(cmd_buf); + + mutex_lock(&chip->i2c_rw_lock); + ret = i2c_transfer(chip->client->adapter, &msg, 1); + if (ret < 0) + { + pr_info("[%s] i2c transfer failed, ret:%d\n", __func__, ret); + } + mutex_unlock(&chip->i2c_rw_lock); + + return ret; +} + +/** + * HOLD 8051 Command + * (1) This command will hold 8051 CPU and stop executing instruction. + */ +int wt_hold_8051(struct wt6670f *chip) +{ + u8 cmd_buf[3] = {0x10, 0x00, 0x01}; + struct i2c_msg msg; + int ret = 0; + + memset(&msg, 0, sizeof(struct i2c_msg)); + + msg.addr = WT6670_ISP_I2C_ADDR; + msg.flags = 0 | I2C_M_STOP; + msg.buf = cmd_buf; + msg.len = sizeof(cmd_buf); + + mutex_lock(&chip->i2c_rw_lock); + ret = i2c_transfer(chip->client->adapter, &msg, 1); + if (ret < 0) + { + pr_info("[%s] i2c transfer failed, ret:%d\n", __func__, ret); + } + mutex_unlock(&chip->i2c_rw_lock); + + return ret; +} + +/** + * READ CHIP ID Command + * (1) The Chip ID of WT6670F is 0x70H + */ +int wt_read_chip_id(struct wt6670f *chip, u8 *r_buf, u8 len) //id should be 0x70 +{ + int ret = 0; + struct i2c_adapter *adap = chip->client->adapter; + struct i2c_msg msg[2]; + u8 w_buf[2] = {0x80, 0x00}; + + memset(msg, 0, 2 * sizeof(struct i2c_msg)); + + msg[0].addr = WT6670_ISP_I2C_ADDR; + msg[0].flags = 0 | I2C_M_STOP; + msg[0].len = sizeof(w_buf); + msg[0].buf = w_buf; + + msg[1].addr = WT6670_ISP_I2C_ADDR; + msg[1].flags = (I2C_M_STOP & I2C_M_NOSTART) | I2C_M_RD; + msg[1].len = len; + msg[1].buf = r_buf; + + mutex_lock(&chip->i2c_rw_lock); + ret = i2c_transfer(adap, msg, 2); + if (ret < 0) + { + pr_info("[%s] i2c transfer failed, ret:%d\n", __func__, ret); + } + mutex_unlock(&chip->i2c_rw_lock); + + return ret; +} + + +/** + * ENABLE ISP FLASH MODE Command + * (1) This command will enable ISP access control to flash memory + */ +int wt_enable_isp_flash_mode(struct wt6670f *chip) +{ + u8 cmd_buf[3] = {0x10, 0x02, 0x08}; + struct i2c_msg msg; + int ret = 0; + + memset(&msg, 0, sizeof(struct i2c_msg)); + + msg.addr = WT6670_ISP_I2C_ADDR; + msg.flags = 0 | I2C_M_STOP; + msg.buf = cmd_buf; + msg.len = sizeof(cmd_buf); + + mutex_lock(&chip->i2c_rw_lock); + ret = i2c_transfer(chip->client->adapter, &msg, 1); + if (ret < 0) + { + pr_info("[%s] i2c transfer failed, ret:%d\n", __func__, ret); + } + mutex_unlock(&chip->i2c_rw_lock); + + return ret; +} + +/** + * CHIP ERASE Command + * (1) This command will erase whole 4K bytes flash memory. + * (2) After send this command, host must wait 20ms ~ 40ms + * and then send FINISH command. + */ +int wt_chip_erase(struct wt6670f *chip) +{ + u8 cmd_buf[3] = {0x20, 0x00, 0x00}; + struct i2c_msg msg; + int ret = 0; + + memset(&msg, 0, sizeof(struct i2c_msg)); + + msg.addr = WT6670_ISP_I2C_ADDR; + msg.flags = 0 | I2C_M_STOP; + msg.buf = cmd_buf; + msg.len = sizeof(cmd_buf); + + mutex_lock(&chip->i2c_rw_lock); + ret = i2c_transfer(chip->client->adapter, &msg, 1); + if (ret < 0) + { + pr_info("[%s] i2c transfer failed, ret:%d\n", __func__, ret); + } + mutex_unlock(&chip->i2c_rw_lock); + + return ret; +} + +/** + * SET ADDRESS HIGH-BYTE Command + * (1) This command will set flash address bit11 ~ bit8. + */ +int wt_set_address_high_byte(struct wt6670f *chip, u8 addr_bit_15_8) +{ + u8 cmd_buf[3] = {0x10, 0x01, addr_bit_15_8}; + struct i2c_msg msg; + int ret = 0; + + memset(&msg, 0, sizeof(struct i2c_msg)); + + msg.addr = WT6670_ISP_I2C_ADDR; + msg.flags = 0 | I2C_M_STOP; + msg.buf = cmd_buf; + msg.len = sizeof(cmd_buf); + + mutex_lock(&chip->i2c_rw_lock); + ret = i2c_transfer(chip->client->adapter, &msg, 1); + if (ret < 0) + { + pr_info("[%s] i2c transfer failed, ret:%d\n", __func__, ret); + } + mutex_unlock(&chip->i2c_rw_lock); + + return ret; +} + +/** + * PROGRAM Command + * (1) ADDR[11:6] select a 64-byte row of flash memory. + * ADDR[5:0] access one byte of one 64-byte row. + * (2) When Host program 64-byte data from 0x0300 to 0x033F, + * first set address ADDR[11:8] = 0x03,command is S+68+10+01+03+P. + * And then, command S+68+41+00+64-byte data +P is to program 64-byte data. + * (3) If I2C speed is 400khz and a series of data more than + * 64 bytes will be programmed, Host needs to send a “FINISH COMMAND” + * after the end of the 64nd data. + * Ex. Program 64 bytes data from address 0x0300 to 0x033F + * S+68+10+01+03+P + * S+68+41+00+Data1+Data2+…+Data64+P + * S+68+00+00+00+P + * (4) If ISP I2C speed is 400Khz, a byte to byte delay is needed + * for program flash spec. + */ +int wt_program_flash_64byte(struct wt6670f *chip, u8 addr_bit_7_0, u8 *buf, u8 len) +{ + int ret = 0; + struct i2c_adapter *adap = chip->client->adapter; + struct i2c_msg msg; + u8 *w_buf = NULL; + + memset(&msg, 0, sizeof(struct i2c_msg)); + + w_buf = kzalloc(len + 2, GFP_KERNEL); + if (w_buf == NULL) + return -1; + + w_buf[0] = 0x41; + w_buf[1] = addr_bit_7_0; + memcpy(w_buf + 2, buf, len); + + msg.addr = WT6670_ISP_I2C_ADDR; + msg.flags = 0 | I2C_M_STOP; + msg.len = len + 2; + msg.buf = w_buf; + + mutex_lock(&chip->i2c_rw_lock); + ret = i2c_transfer(adap, &msg, 1); + if (ret < 0) + { + pr_info("[%s] i2c transfer failed, ret:%d\n", __func__, ret); + } + mutex_unlock(&chip->i2c_rw_lock); + + kfree(w_buf); + return ret; +} + +/** + * FINISH Command + * (1) FINISH COMMAND stops erase/program function + */ +int wt_finish_erase_or_program(struct wt6670f *chip) +{ + u8 cmd_buf[3] = {0x00, 0x00, 0x00}; + struct i2c_msg msg; + int ret = 0; + + memset(&msg, 0, sizeof(struct i2c_msg)); + + msg.addr = WT6670_ISP_I2C_ADDR; + msg.flags = 0 | I2C_M_STOP; + msg.buf = cmd_buf; + msg.len = sizeof(cmd_buf); + + mutex_lock(&chip->i2c_rw_lock); + ret = i2c_transfer(chip->client->adapter, &msg, 1); + if (ret < 0) + { + pr_info("[%s] i2c transfer failed, ret:%d\n", __func__, ret); + } + mutex_unlock(&chip->i2c_rw_lock); + + return ret; +} + +/** + * READ Command + * (1) READ COMMAND can read maximum 64 bytes flash data. + * (2) If Host read 64-byte data from 0x0700 ~ 0x073F, + * set address ADDR[13:8] = 0x07 first. + * READ COMMAND is S+68+61+00+rS+69+64-byte data+P + */ +int wt_read_flash_64byte(struct wt6670f *chip, u8 addr_bit_7_0, u8 *buf, u8 len) +{ + int ret = 0; + struct i2c_msg msg[2]; + u8 w_buf[2] = {0x61, addr_bit_7_0}; + u8 *r_buf = NULL; + + memset(msg, 0, 2 * sizeof(struct i2c_msg)); + + r_buf = kzalloc(len, GFP_KERNEL); + if (r_buf == NULL) + return -1; + + msg[0].addr = WT6670_ISP_I2C_ADDR; + msg[0].flags = 0 | I2C_M_STOP; + msg[0].len = sizeof(w_buf); + msg[0].buf = w_buf; + + msg[1].addr = WT6670_ISP_I2C_ADDR; + msg[1].flags = 1; + msg[1].len = len; + msg[1].buf = r_buf; + + mutex_lock(&chip->i2c_rw_lock); + ret = i2c_transfer(chip->client->adapter, msg, 2); + if (ret < 0) + { + pr_info("[%s] i2c transfer failed, ret:%d\n", __func__, ret); + } + mutex_unlock(&chip->i2c_rw_lock); + + memcpy(buf, r_buf, len); + + kfree(r_buf); + return ret; +} + +/** + * END Command + * (1) END COMMAND will reset WT6670F. + * Then WT6670F restarts to execute user’s code + */ +int wt_restart_chip(struct wt6670f *chip) +{ + u8 cmd_buf[3] = {0x10, 0x00, 0x02}; + struct i2c_msg msg; + int ret = 0; + + memset(&msg, 0, sizeof(struct i2c_msg)); + + msg.addr = WT6670_ISP_I2C_ADDR; + msg.flags = 0 | I2C_M_STOP; + msg.buf = cmd_buf; + msg.len = sizeof(cmd_buf); + + mutex_lock(&chip->i2c_rw_lock); + ret = i2c_transfer(chip->client->adapter, &msg, 1); + if (ret < 0) + { + pr_info("[%s] i2c transfer failed, ret:%d\n", __func__, ret); + } + mutex_unlock(&chip->i2c_rw_lock); + + return ret; +} + +int wt6670f_isp_flow(struct wt6670f *chip) +{ + u8 chip_id = 0; + int programed_cnt = 0x0; + int addr_bit_15_8 = 0x0; + int addr_bit_7_0 = 0x0; + u8 *code = NULL; + int i = 0; + int len = 0; + int read_flash_cnt = 0x0; + u16 fw_ver = 0; + int ret = -1; + + code = kzalloc(WT6670_WRITE_BYTE, GFP_KERNEL); + if (code == NULL) + return -1; + wt6670f_isp_do_reset(); + + wt_enter_isp_mode_sequence(chip); + + msleep(10); + //while(check_i2c_bus_free); + + wt_enable_isp(chip); + + wt_read_chip_id(chip, &chip_id, 1); + if (chip_id != 0x70) + { + pr_info("[%s]wt6670 err, chip_id:%d, skip isp\n", __func__, chip_id); + goto isp_err; + } + pr_info("[%s]wt6670 chip_id=%d \n", __func__, chip_id); + + wt_enable_isp_flash_mode(chip); + wt_chip_erase(chip); + msleep(30); + wt_finish_erase_or_program(chip); + + //wt_enable_isp_flash_mode(chip->client); + + while (programed_cnt < WT70F_QC3p_V02_210326_FBA1_bin_len) + { + addr_bit_15_8 = (programed_cnt >> 8) & 0x0f; + addr_bit_7_0 = programed_cnt % 0x100; + + memset(code, 0, WT6670_WRITE_BYTE); + + wt_set_address_high_byte(chip, addr_bit_15_8); + + if ((programed_cnt + WT6670_WRITE_BYTE) > WT70F_QC3p_V02_210326_FBA1_bin_len) + { + len = WT70F_QC3p_V02_210326_FBA1_bin_len % WT6670_WRITE_BYTE; + } + else + { + len = WT6670_WRITE_BYTE; + } + + for (i = 0; i < len; i++) + { + code[i] = WT70F_QC3p_V02_210326_FBA1_bin[programed_cnt + i]; + } + + if (len != WT6670_WRITE_BYTE) + { + for (i = len; i < WT6670_WRITE_BYTE; i++) + { + code[i] = 0xff; + } + } + + wt_program_flash_64byte(chip, addr_bit_7_0, code, WT6670_WRITE_BYTE); + + wt_finish_erase_or_program(chip); + programed_cnt += WT6670_WRITE_BYTE; + } + + pr_info("[%s] finish program flash, start verify...\n", __func__); + + //wt_enable_isp_flash_mode(chip->client); + + while (read_flash_cnt < 0x1000) + { + addr_bit_15_8 = (read_flash_cnt >> 8) & 0x0f; + addr_bit_7_0 = read_flash_cnt % 0x100; + + memset(code, 0, WT6670_WRITE_BYTE); + + wt_set_address_high_byte(chip, addr_bit_15_8); + wt_read_flash_64byte(chip, addr_bit_7_0, code, WT6670_WRITE_BYTE); + + if (read_flash_cnt > WT70F_QC3p_V02_210326_FBA1_bin_len) + { + + } + else if (read_flash_cnt + WT6670_WRITE_BYTE > WT70F_QC3p_V02_210326_FBA1_bin_len) + { + for (i = 0; i < WT70F_QC3p_V02_210326_FBA1_bin_len % WT6670_WRITE_BYTE; i++) + { + if (code[i] != WT70F_QC3p_V02_210326_FBA1_bin[read_flash_cnt + i]) + { + pr_info("[%s] verify flash data failed, fail_addr:0x%x, fail_data:0x%x, correct_data:0x%x\n", __func__, read_flash_cnt + i, + code[i], WT70F_QC3p_V02_210326_FBA1_bin[read_flash_cnt + i]); + goto isp_err; + } + } + + //need to verify 0xff + } + else + { + for (i = 0; i < WT6670_WRITE_BYTE; i++) + { + if (code[i] != WT70F_QC3p_V02_210326_FBA1_bin[read_flash_cnt + i]) + { + pr_info("[%s] verify flash data failed, fail_addr:0x%x, fail_data:0x%x, correct_data:0x%x\n", __func__, read_flash_cnt + i, + code[i], WT70F_QC3p_V02_210326_FBA1_bin[read_flash_cnt + i]); + goto isp_err; + } + } + } + + read_flash_cnt += WT6670_WRITE_BYTE; + } + pr_info("[%s] firmware update success!!!\n", __func__); + ret = 0; + +isp_err: + wt_restart_chip(chip); + kfree(code); + //do not comment this line + msleep(10); + fw_ver = wt6670f_get_firmware_version(); + pr_info("%s: wt6670f firmware:%d!\n", __func__,fw_ver); + return ret; +} +EXPORT_SYMBOL(wt6670f_isp_flow); diff --git a/drivers/power/mmi_sc8549/Android.mk b/drivers/power/mmi_sc8549/Android.mk new file mode 100644 index 000000000000..882f9f832f99 --- /dev/null +++ b/drivers/power/mmi_sc8549/Android.mk @@ -0,0 +1,13 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := mmi_sc8549.ko + + +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + + + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/mmi_sc8549/Kbuild b/drivers/power/mmi_sc8549/Kbuild new file mode 100644 index 000000000000..e79ade637f48 --- /dev/null +++ b/drivers/power/mmi_sc8549/Kbuild @@ -0,0 +1,8 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/mmi_sc8549 + +obj-m += mmi_sc8549.o + + diff --git a/drivers/power/mmi_sc8549/Makefile b/drivers/power/mmi_sc8549/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/power/mmi_sc8549/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/power/mmi_sc8549/mmi_sc8549.c b/drivers/power/mmi_sc8549/mmi_sc8549.c new file mode 100644 index 000000000000..9b96ef3e3b8c --- /dev/null +++ b/drivers/power/mmi_sc8549/mmi_sc8549.c @@ -0,0 +1,1726 @@ +/* + * SC8549 battery charging driver + * + * Copyright (C) 2021 South Chip * + * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#define pr_fmt(fmt) "[sc8549] %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "mmi_sc8549.h" +#include "mmi_sc8549_iio.h" +#include +#include + +enum { + ADC_IBUS, + ADC_VBUS, + ADC_VAC, + ADC_VOUT, + ADC_VBAT, + ADC_IBAT, + ADC_TDIE, + ADC_MAX_NUM, +}; + + +/*below used for comm with other module*/ +#define MMI_BAT_OVP_FAULT_BIT 6 +#define MMI_BAT_OCP_FAULT_BIT 7 +#define MMI_BUS_OVP_FAULT_BIT 9 +#define MMI_BUS_OCP_FAULT_BIT 10 +#define MMI_SS_TIMEOUT_FAULT_BIT 16 +#define MMI_TS_SHUT_FAULT_BIT 17 +#define MMI_CP_SWITCH_BIT 18 + +#define MMI_BAT_OVP_FAULT_MASK (1 << MMI_BAT_OVP_FAULT_BIT) +#define MMI_BAT_OCP_FAULT_MASK (1 << MMI_BAT_OCP_FAULT_BIT) +#define MMI_BUS_OVP_FAULT_MASK (1 << MMI_BUS_OVP_FAULT_BIT) +#define MMI_BUS_OCP_FAULT_MASK (1 << MMI_BUS_OCP_FAULT_BIT) +#define MMI_SS_TIMEOUT_FAULT_MASK (1 << MMI_SS_TIMEOUT_FAULT_BIT) +#define MMI_TS_SHUT_FAULT_MASK (1 << MMI_TS_SHUT_FAULT_BIT) +#define MMI_CP_SWITCH_MASK (1 << MMI_CP_SWITCH_BIT) + +#define sc_err(fmt, ...) \ +do { \ + printk(KERN_ERR "[CP]:%s:" \ + fmt, __func__, ##__VA_ARGS__);\ +} while(0); + +#define sc_info(fmt, ...) \ +do { \ + printk(KERN_INFO "[CP]:%s:" \ + fmt, __func__, ##__VA_ARGS__);\ +} while(0); + +#define sc_debug(fmt, ...) \ +do { \ + printk(KERN_DEBUG "[CP]:%s:" \ + fmt, __func__, ##__VA_ARGS__);\ +} while(0); + + +struct sc8549_cfg { + bool bat_ovp_disable; + bool bat_ocp_disable; + + int bat_ovp_th; + int bat_ocp_th; + + bool bus_ocp_disable; + + int bus_ovp_th; + int bus_ocp_th; + + int ac_ovp_th; + + int sense_r_mohm; +}; + +struct sc8549 { + struct device *dev; + struct i2c_client *client; + + struct iio_dev *indio_dev; + struct iio_chan_spec *iio_chan; + struct iio_channel *int_iio_chans; + struct iio_channel **ext_iio_chans; + + int device_id; + int part_no; + int revision; + int int_gpio; + int gpio_irq; + + struct mutex data_lock; + struct mutex i2c_rw_lock; + struct mutex charging_disable_lock; + struct mutex irq_complete; + struct regmap *regmap; + + bool irq_waiting; + bool irq_disabled; + bool resume_completed; + int irq_counts; + + bool batt_present; + bool vbus_present; + + bool usb_present; + bool charge_enabled; /* Register bit status */ + + /* ADC reading */ + int vbat_volt; + int vbus_volt; + int vout_volt; + int vac_volt; + + int ibat_curr; + int ibus_curr; + + int die_temp; + + /* alarm/fault status */ + bool ac_ovp_fault; + bool drop_ovp_fault; + bool bat_ovp_fault; + bool bat_ocp_fault; + bool bus_ovp_fault; + bool bus_ocp_fault; + bool ss_timeout_fault; + bool ts_shut_fault; + bool cp_switch; + + bool vbat_reg; + bool ibat_reg; + + int prev_fault; + int prev_fault2; + + + int chg_ma; + int chg_mv; + + int charge_state; + + struct sc8549_cfg *cfg; + + int skip_writes; + int skip_reads; + + struct sc8549_platform_data *platform_data; + + struct delayed_work monitor_work; + + struct dentry *debug_root; + + struct power_supply *fc2_psy; + struct pinctrl *irq_pinctrl; +}; + +static int __sc8549_i2c_read(struct i2c_client * client,u8 reg,void *buff,size_t size) +{ + int ret; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = client->flags, + .len = 1, + .buf = ®, + }, + { + .addr = client->addr, + .flags = client->flags | I2C_M_RD, + .len = size, + .buf = buff, + } + }; + + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + + + if(ret < 0){ + sc_err("i2c read fail: can't read from reg 0x%02X\n",reg); + return -EIO; + } + + return ret; +} + +static int __sc8549_i2c_write(struct i2c_client * client,u8 reg,void *buff,size_t size) +{ + int ret; + u8 buffer[5]; + struct i2c_msg msg; + + buffer[0] = reg; + memcpy(&(buffer[1]), buff, size); + msg.addr = client->addr, + msg.flags = client->flags, + msg .len = size + 1, + msg.buf = buffer, + + ret = i2c_transfer(client->adapter, &msg, 1); + + if(ret < 0){ + sc_err("i2c write fail: can't write from reg 0x%02X\n",reg); + return -EIO; + } + + return ret; +} +/************************************************************************/ +static int __sc8549_read_byte(struct sc8549 *sc, u8 reg, u8 *data) +{ + s32 ret; + + ret = __sc8549_i2c_read(sc->client, reg, data, 1); + if (ret < 0) { + sc_err("i2c read fail: can't read from reg 0x%02X\n", reg); + return ret; + } + + return 0; +} + +static int __sc8549_write_byte(struct sc8549 *sc, int reg, u8 val) +{ + s32 ret; + + ret = __sc8549_i2c_write(sc->client, reg, &val, 1); + if (ret < 0) { + sc_err("i2c write fail: can't write 0x%02X to reg 0x%02X: %d\n", + val, reg, ret); + return ret; + } + return 0; +} + +static int __sc8549_read_word(struct sc8549 *sc, u8 reg, u16 *data) +{ + s32 ret; + + ret = __sc8549_i2c_read(sc->client, reg, data, 2); + if (ret < 0) { + sc_err("i2c read fail: can't read from reg 0x%02X\n", reg); + return ret; + } + + return 0; +} + +static int sc8549_read_byte(struct sc8549 *sc, u8 reg, u8 *data) +{ + int ret; + + if (sc->skip_reads) { + *data = 0; + return 0; + } + + mutex_lock(&sc->i2c_rw_lock); + ret = __sc8549_read_byte(sc, reg, data); + mutex_unlock(&sc->i2c_rw_lock); + + return ret; +} + +static int sc8549_write_byte(struct sc8549 *sc, u8 reg, u8 data) +{ + int ret; + + if (sc->skip_writes) + return 0; + + mutex_lock(&sc->i2c_rw_lock); + ret = __sc8549_write_byte(sc, reg, data); + mutex_unlock(&sc->i2c_rw_lock); + + return ret; +} + +static int sc8549_read_word(struct sc8549 *sc, u8 reg, u16 *data) +{ + int ret; + + if (sc->skip_reads) { + *data = 0; + return 0; + } + + mutex_lock(&sc->i2c_rw_lock); + ret = __sc8549_read_word(sc, reg, data); + mutex_unlock(&sc->i2c_rw_lock); + + return ret; +} + +static int sc8549_update_bits(struct sc8549*sc, u8 reg, + u8 mask, u8 data) +{ + int ret; + u8 tmp; + + if (sc->skip_reads || sc->skip_writes) + return 0; + + mutex_lock(&sc->i2c_rw_lock); + ret = __sc8549_read_byte(sc, reg, &tmp); + if (ret) { + sc_err("Failed: reg=%02X, ret=%d\n", reg, ret); + goto out; + } + + tmp &= ~mask; + tmp |= data & mask; + + ret = __sc8549_write_byte(sc, reg, tmp); + if (ret) + sc_err("Failed: reg=%02X, ret=%d\n", reg, ret); + +out: + mutex_unlock(&sc->i2c_rw_lock); + return ret; +} + +/*********************************************************************/ + +static int sc8549_enable_charge(struct sc8549 *sc, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = SC8549_CHG_ENABLE; + else + val = SC8549_CHG_DISABLE; + + val <<= SC8549_CHG_EN_SHIFT; + + sc_err("sc8549 charger %s\n", enable == false ? "disable" : "enable"); + ret = sc8549_update_bits(sc, SC8549_REG_07, + SC8549_CHG_EN_MASK, val); + + return ret; +} + +static int sc8549_check_charge_enabled(struct sc8549 *sc, bool *enabled) +{ + int ret; + u8 val; + + ret = sc8549_read_byte(sc, SC8549_REG_07, &val); + sc_info(">>>reg [0x0c] = 0x%02x\n", val); + if (!ret) + *enabled = !!(val & SC8549_CHG_EN_MASK); + return ret; +} + +static int sc8549_set_wdt(struct sc8549 *sc, int ms) +{ + int ret; + u8 val; + + if (ms == 0) + val = SC8549_WATCHDOG_DISABLE; + if (ms == 200) + val = SC8549_WATCHDOG_0P2S; + else if (ms == 500) + val = SC8549_WATCHDOG_0P5S; + else if (ms == 1000) + val = SC8549_WATCHDOG_1S; + else if (ms == 5000) + val = SC8549_WATCHDOG_5S; + else + val = SC8549_WATCHDOG_30S; + + val <<= SC8549_WATCHDOG_TIMEOUT_SHIFT; + + ret = sc8549_update_bits(sc, SC8549_REG_09, + SC8549_WATCHDOG_TIMEOUT_MASK, val); + return ret; +} + +static int sc8549_enable_batovp(struct sc8549 *sc, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = SC8549_BAT_OVP_ENABLE; + else + val = SC8549_BAT_OVP_DISABLE; + + val <<= SC8549_BAT_OVP_DIS_SHIFT; + + ret = sc8549_update_bits(sc, SC8549_REG_00, + SC8549_BAT_OVP_DIS_MASK, val); + return ret; +} + +static int sc8549_set_batovp_th(struct sc8549 *sc, int threshold) +{ + int ret; + u8 val; + + if (threshold < SC8549_BAT_OVP_BASE) + threshold = SC8549_BAT_OVP_BASE; + + val = (threshold - SC8549_BAT_OVP_BASE) / SC8549_BAT_OVP_LSB; + + val <<= SC8549_BAT_OVP_SHIFT; + + ret = sc8549_update_bits(sc, SC8549_REG_00, + SC8549_BAT_OVP_MASK, val); + return ret; +} + +static int sc8549_enable_batocp(struct sc8549 *sc, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = SC8549_BAT_OCP_ENABLE; + else + val = SC8549_BAT_OCP_DISABLE; + + val <<= SC8549_BAT_OCP_DIS_SHIFT; + + ret = sc8549_update_bits(sc, SC8549_REG_01, + SC8549_BAT_OCP_DIS_MASK, val); + return ret; +} + +static int sc8549_set_batocp_th(struct sc8549 *sc, int threshold) +{ + int ret; + u8 val; + + if (threshold < SC8549_BAT_OCP_BASE) + threshold = SC8549_BAT_OCP_BASE; + + val = (threshold - SC8549_BAT_OCP_BASE) / SC8549_BAT_OCP_LSB; + + val <<= SC8549_BAT_OCP_SHIFT; + + ret = sc8549_update_bits(sc, SC8549_REG_01, + SC8549_BAT_OCP_MASK, val); + return ret; +} + +static int sc8549_set_busovp_th(struct sc8549 *sc, int threshold) +{ + int ret; + u8 val; + + if (threshold < SC8549_BUS_OVP_BASE) + threshold = SC8549_BUS_OVP_BASE; + + val = (threshold - SC8549_BUS_OVP_BASE) / SC8549_BUS_OVP_LSB; + + val <<= SC8549_BUS_OVP_SHIFT; + + ret = sc8549_update_bits(sc, SC8549_REG_04, + SC8549_BUS_OVP_MASK, val); + return ret; +} + +static int sc8549_enable_busocp(struct sc8549 *sc, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = SC8549_BUS_OCP_ENABLE; + else + val = SC8549_BUS_OCP_DISABLE; + + val <<= SC8549_BUS_OCP_DIS_SHIFT; + + ret = sc8549_update_bits(sc, SC8549_REG_05, + SC8549_BUS_OCP_DIS_MASK, val); + return ret; +} + +static int sc8549_set_busocp_th(struct sc8549 *sc, int threshold) +{ + int ret; + u8 val; + + if (threshold < SC8549_BUS_OCP_BASE) + threshold = SC8549_BUS_OCP_BASE; + + val = (threshold - SC8549_BUS_OCP_BASE) / SC8549_BUS_OCP_LSB; + + val <<= SC8549_BUS_OCP_SHIFT; + + ret = sc8549_update_bits(sc, SC8549_REG_05, + SC8549_BUS_OCP_MASK, val); + return ret; +} + +static int sc8549_set_acovp_th(struct sc8549 *sc, int threshold) +{ + int ret; + u8 val; + + if (threshold < SC8549_AC_OVP_BASE) + threshold = SC8549_AC_OVP_BASE; + + if (threshold == SC8549_AC_OVP_6P5V) + val = 0x07; + else + val = (threshold - SC8549_AC_OVP_BASE) / SC8549_AC_OVP_LSB; + + val <<= SC8549_AC_OVP_SHIFT; + + ret = sc8549_update_bits(sc, SC8549_REG_02, + SC8549_AC_OVP_MASK, val); + + return ret; + +} + +static int sc8549_set_vdrop_th(struct sc8549 *sc, int threshold) +{ + int ret; + u8 val; + + if (threshold == 300) + val = SC8549_VDROP_THRESHOLD_300MV; + else + val = SC8549_VDROP_THRESHOLD_400MV; + + val <<= SC8549_VDROP_THRESHOLD_SET_SHIFT; + + ret = sc8549_update_bits(sc, SC8549_REG_03, + SC8549_VDROP_THRESHOLD_SET_MASK, + val); + + return ret; +} + +static int sc8549_set_vdrop_deglitch(struct sc8549 *sc, int us) +{ + int ret; + u8 val; + + if (us == 8) + val = SC8549_VDROP_DEGLITCH_8US; + else + val = SC8549_VDROP_DEGLITCH_5MS; + + val <<= SC8549_VDROP_DEGLITCH_SET_SHIFT; + + ret = sc8549_update_bits(sc, SC8549_REG_03, + SC8549_VDROP_DEGLITCH_SET_MASK, + val); + return ret; +} + +static int sc8549_enable_adc(struct sc8549 *sc, bool enable) +{ + int ret; + u8 val; + + if (enable) + val = SC8549_ADC_ENABLE; + else + val = SC8549_ADC_DISABLE; + + val <<= SC8549_ADC_EN_SHIFT; + + ret = sc8549_update_bits(sc, SC8549_REG_11, + SC8549_ADC_EN_MASK, val); + return ret; +} + +static int sc8549_set_adc_scanrate(struct sc8549 *sc, bool oneshot) +{ + int ret; + u8 val; + + if (oneshot) + val = SC8549_ADC_RATE_ONESHOT; + else + val = SC8549_ADC_RATE_CONTINOUS; + + val <<= SC8549_ADC_RATE_SHIFT; + + ret = sc8549_update_bits(sc, SC8549_REG_11, + SC8549_ADC_EN_MASK, val); + return ret; +} + +#define ADC_REG_BASE SC8549_REG_13 +static int sc8549_get_adc_data(struct sc8549 *sc, int channel, int *result) +{ + int ret; + u16 val; + s16 t; + int tmp; + + if(channel >= ADC_MAX_NUM) return 0; + + ret = sc8549_read_word(sc, ADC_REG_BASE + (channel << 1), &val); + + if (ret < 0) + return ret; + + t = val & 0xFF; + t <<= 8; + t |= (val >> 8) & 0xFF; + + switch (channel) { + case ADC_IBUS: + tmp = t * SC8549_IBUS_ADC_LSB_MUL; + t = tmp / SC8549_IBUS_ADC_LSB_DIV; + break; + case ADC_VBUS: + tmp = t * SC8549_VBUS_ADC_LSB_MUL; + t = tmp / SC8549_VBUS_ADC_LSB_DIV; + break; + case ADC_VAC: + t = t * SC8549_VAC_ADC_LSB; + break; + case ADC_VOUT: + tmp = t * SC8549_VOUT_ADC_LSB_MUL; + t = tmp / SC8549_VOUT_ADC_LSB_DIV; + break; + case ADC_VBAT: + tmp = t * SC8549_VBAT_ADC_LSB_MUL; + t = tmp / SC8549_VBAT_ADC_LSB_DIV; + break; + case ADC_TDIE: + tmp = t * SC8549_TDIE_ADC_LSB_MUL; + t = tmp / SC8549_TDIE_ADC_LSB_DIV; + break; + default: + t = 0; + break; + } + + *result = t; + + return 0; +} + +static int sc8549_set_adc_scan(struct sc8549 *sc, int channel, bool enable) +{ + int ret; + u8 mask; + u8 shift; + u8 val; + + if (channel > ADC_MAX_NUM) + return -EINVAL; + + switch (channel) { + case ADC_IBUS: + shift = SC8549_IBUS_ADC_DIS_SHIFT; + mask = SC8549_IBUS_ADC_DIS_MASK; + break; + case ADC_VBUS: + shift = SC8549_VBUS_ADC_DIS_SHIFT; + mask = SC8549_VBUS_ADC_DIS_MASK; + break; + case ADC_VAC: + shift = SC8549_VAC_ADC_DIS_SHIFT; + mask = SC8549_VAC_ADC_DIS_MASK; + break; + case ADC_VOUT: + shift = SC8549_VOUT_ADC_DIS_SHIFT; + mask = SC8549_VOUT_ADC_DIS_MASK; + break; + case ADC_VBAT: + shift = SC8549_VBAT_ADC_DIS_SHIFT; + mask = SC8549_VBAT_ADC_DIS_MASK; + break; + case ADC_TDIE: + shift = SC8549_TDIE_ADC_DIS_SHIFT; + mask = SC8549_TDIE_ADC_DIS_MASK; + break; + default: + return -EINVAL; + break; + } + + if (enable) + val = 0 << shift; + else + val = 1 << shift; + + ret = sc8549_update_bits(sc, SC8549_REG_15, mask, val); + + return ret; +} + + +static int sc8549_set_sense_resistor(struct sc8549 *sc, int r_mohm) +{ + int ret; + u8 val; + + if (r_mohm == 2) + val = SC8549_SET_IBAT_SNS_RES_2MHM; + else if (r_mohm == 5) + val = SC8549_SET_IBAT_SNS_RES_5MHM; + else + return -EINVAL; + + val <<= SC8549_SET_IBAT_SNS_RES_SHIFT; + + ret = sc8549_update_bits(sc, SC8549_REG_08, + SC8549_SET_IBAT_SNS_RES_MASK, + val); + return ret; +} + +static int sc8549_set_ss_timeout(struct sc8549 *sc, int timeout) +{ + int ret; + u8 val; + + switch (timeout) { + case 0: + val = SC8549_SS_TIMEOUT_DISABLE; + break; + case 40: + val = SC8549_SS_TIMEOUT_40MS; + break; + case 80: + val = SC8549_SS_TIMEOUT_80MS; + break; + case 320: + val = SC8549_SS_TIMEOUT_320MS; + break; + case 1280: + val = SC8549_SS_TIMEOUT_1280MS; + break; + case 5120: + val = SC8549_SS_TIMEOUT_5120MS; + break; + case 20480: + val = SC8549_SS_TIMEOUT_20480MS; + break; + default: + val = SC8549_SS_TIMEOUT_81920MS; + break; + } + + val <<= SC8549_SS_TIMEOUT_SET_SHIFT; + + ret = sc8549_update_bits(sc, SC8549_REG_08, + SC8549_SS_TIMEOUT_SET_MASK, + val); + + return ret; +} + +static int sc8549_set_ibat_reg_th(struct sc8549 *sc, int th_ma) +{ + int ret; + u8 val; + + if (th_ma == 200) + val = SC8549_IBAT_REG_200MA; + else if (th_ma == 300) + val = SC8549_IBAT_REG_300MA; + else if (th_ma == 400) + val = SC8549_IBAT_REG_400MA; + else if (th_ma == 500) + val = SC8549_IBAT_REG_500MA; + else + val = SC8549_IBAT_REG_500MA; + + val <<= SC8549_IBAT_REG_SHIFT; + ret = sc8549_update_bits(sc, SC8549_REG_0B, + SC8549_IBAT_REG_MASK, + val); + + return ret; + +} + +static int sc8549_set_vbat_reg_th(struct sc8549 *sc, int th_mv) +{ + int ret; + u8 val; + + if (th_mv == 50) + val = SC8549_VBAT_REG_50MV; + else if (th_mv == 100) + val = SC8549_VBAT_REG_100MV; + else if (th_mv == 150) + val = SC8549_VBAT_REG_150MV; + else + val = SC8549_VBAT_REG_200MV; + + val <<= SC8549_VBAT_REG_SHIFT; + + ret = sc8549_update_bits(sc, SC8549_REG_0A, + SC8549_VBAT_REG_MASK, + val); + + return ret; +} + +static int sc8549_register_reset(struct sc8549 *sc) +{ + int ret; + u8 val; + + val = SC8549_REG_RST_ENABLE; + + val <<= SC8549_REG_RST_SHIFT; + + ret = sc8549_update_bits(sc, SC8549_REG_07, + SC8549_REG_RST_MASK, + val); + return ret; +} + + + +static int sc8549_detect_device(struct sc8549 *sc) +{ + int ret; + u8 data; + + ret = sc8549_read_byte(sc, SC8549_REG_36, &data); + if (ret == 0) { + sc->part_no = data; + sc_info("detect device PN:%x, ID:%x, REV:%x \n!", + sc->part_no, sc->device_id, sc->revision); + + if (sc->part_no != SC8549_DEVICE_ID) + return -EINVAL; + } + + return ret; +} + +static int sc8549_parse_dt(struct sc8549 *sc, struct device *dev) +{ + int ret; + struct device_node *np = dev->of_node; + + sc->cfg = devm_kzalloc(dev, sizeof(struct sc8549_cfg), + GFP_KERNEL); + + if (!sc->cfg) + return -ENOMEM; + + sc->cfg->bat_ovp_disable = of_property_read_bool(np, + "sc,sc8549,bat-ovp-disable"); + sc->cfg->bat_ocp_disable = of_property_read_bool(np, + "sc,sc8549,bat-ocp-disable"); + sc->cfg->bus_ocp_disable = of_property_read_bool(np, + "sc,sc8549,bus-ocp-disable"); + + ret = of_property_read_u32(np, "sc,sc8549,bat-ovp-threshold", + &sc->cfg->bat_ovp_th); + if (ret) { + sc_err("failed to read bat-ovp-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "sc,sc8549,bat-ocp-threshold", + &sc->cfg->bat_ocp_th); + if (ret) { + sc_err("failed to read bat-ocp-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "sc,sc8549,bus-ovp-threshold", + &sc->cfg->bus_ovp_th); + if (ret) { + sc_err("failed to read bus-ovp-threshold\n"); + return ret; + } + ret = of_property_read_u32(np, "sc,sc8549,bus-ocp-threshold", + &sc->cfg->bus_ocp_th); + if (ret) { + sc_err("failed to read bus-ocp-threshold\n"); + return ret; + } + + ret = of_property_read_u32(np, "sc,sc8549,ac-ovp-threshold", + &sc->cfg->ac_ovp_th); + if (ret) { + sc_err("failed to read ac-ovp-threshold\n"); + return ret; + } + + ret = of_property_read_u32(np, "sc,sc8549,sense-resistor-mohm", + &sc->cfg->sense_r_mohm); + if (ret) { + sc_err("failed to read sense-resistor-mohm\n"); + return ret; + } + + return 0; +} + + +static int sc8549_init_protection(struct sc8549 *sc) +{ + int ret; + + ret = sc8549_enable_batovp(sc, !sc->cfg->bat_ovp_disable); + sc_info("%s bat ovp %s\n", + sc->cfg->bat_ovp_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = sc8549_enable_batocp(sc, !sc->cfg->bat_ocp_disable); + sc_info("%s bat ocp %s\n", + sc->cfg->bat_ocp_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = sc8549_enable_busocp(sc, !sc->cfg->bus_ocp_disable); + sc_info("%s bus ocp %s\n", + sc->cfg->bus_ocp_disable ? "disable" : "enable", + !ret ? "successfullly" : "failed"); + + ret = sc8549_set_batovp_th(sc, sc->cfg->bat_ovp_th); + sc_info("set bat ovp th %d %s\n", sc->cfg->bat_ovp_th, + !ret ? "successfully" : "failed"); + + ret = sc8549_set_batocp_th(sc, sc->cfg->bat_ocp_th); + sc_info("set bat ocp threshold %d %s\n", sc->cfg->bat_ocp_th, + !ret ? "successfully" : "failed"); + + ret = sc8549_set_busovp_th(sc, sc->cfg->bus_ovp_th); + sc_info("set bus ovp threshold %d %s\n", sc->cfg->bus_ovp_th, + !ret ? "successfully" : "failed"); + + ret = sc8549_set_busocp_th(sc, sc->cfg->bus_ocp_th); + sc_info("set bus ocp threshold %d %s\n", sc->cfg->bus_ocp_th, + !ret ? "successfully" : "failed"); + + ret = sc8549_set_acovp_th(sc, sc->cfg->ac_ovp_th); + sc_info("set ac ovp threshold %d %s\n", sc->cfg->ac_ovp_th, + !ret ? "successfully" : "failed"); + + return 0; +} + +static int sc8549_init_int_src(struct sc8549 *sc) +{ + int ret; + + ret = sc8549_update_bits(sc, SC8549_REG_11, + SC8549_ADC_DONE_MASK_MASK, + 1); + + return 0; +} + +static int sc8549_init_adc(struct sc8549 *sc) +{ + sc8549_set_adc_scanrate(sc, false); + sc8549_set_adc_scan(sc, ADC_IBUS, true); + sc8549_set_adc_scan(sc, ADC_VBUS, true); + sc8549_set_adc_scan(sc, ADC_VOUT, false); + sc8549_set_adc_scan(sc, ADC_VBAT, true); + sc8549_set_adc_scan(sc, ADC_IBAT, true); + sc8549_set_adc_scan(sc, ADC_TDIE, true); + sc8549_set_adc_scan(sc, ADC_VAC, true); + + sc8549_enable_adc(sc, true); + + return 0; +} + +static int sc8549_init_regulation(struct sc8549 *sc) +{ + sc8549_set_ibat_reg_th(sc, 300); + sc8549_set_vbat_reg_th(sc, 100); + + sc8549_set_vdrop_deglitch(sc, 5000); + sc8549_set_vdrop_th(sc, 400); + + return 0; +} + +static int sc8549_init_device(struct sc8549 *sc) +{ + sc8549_register_reset(sc); + + sc8549_set_wdt(sc, 0); + + sc8549_set_ss_timeout(sc, 0); + sc8549_set_sense_resistor(sc, sc->cfg->sense_r_mohm); + + sc8549_init_protection(sc); + + sc8549_init_adc(sc); + sc8549_init_int_src(sc); + + sc8549_init_regulation(sc); + + return 0; +} + + +static int sc8549_set_present(struct sc8549 *sc, bool present) +{ + sc->usb_present = present; + + if (present) + sc8549_init_device(sc); + return 0; +} + +static int sc8549_get_vbus_present(struct sc8549 *sc) +{ + int ret; + u8 val; + + ret= sc8549_read_byte(sc, SC8549_REG_0E, &val); + if (ret) { + sc_err("get vbus present faile \n"); + return ret; + } + sc->vbus_present = !!(val & SC8549_ADAPTER_INSERT_STAT_MASK); + + return ret; +} +static ssize_t sc8549_show_registers(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sc8549 *sc = dev_get_drvdata(dev); + u8 addr; + u8 val; + u8 tmpbuf[300]; + int len; + int idx = 0; + int ret; + + idx = snprintf(buf, PAGE_SIZE, "%s:\n", "sc8549"); + for (addr = 0x0; addr <= 0x36; addr++) { + ret = sc8549_read_byte(sc, addr, &val); + if (ret == 0) { + len = snprintf(tmpbuf, PAGE_SIZE - idx, + "Reg[%.2X] = 0x%.2x\n", addr, val); + memcpy(&buf[idx], tmpbuf, len); + idx += len; + } + } + + return idx; +} + +static ssize_t sc8549_store_register(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sc8549 *sc = dev_get_drvdata(dev); + int ret; + unsigned int reg; + unsigned int val; + + ret = sscanf(buf, "%x %x", ®, &val); + if (ret == 2 && reg <= 0x36) + sc8549_write_byte(sc, (unsigned char)reg, (unsigned char)val); + + return count; +} + + + +static DEVICE_ATTR(registers, 0660, sc8549_show_registers, sc8549_store_register); + +static struct attribute *sc8549_attributes[] = { + &dev_attr_registers.attr, + NULL, +}; + +static const struct attribute_group sc8549_attr_group = { + .attrs = sc8549_attributes, +}; + +static enum power_supply_property sc8549_charger_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, +}; + +static void sc8549_check_fault_status(struct sc8549 *sc); + +static int sc8549_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct sc8549 *sc = power_supply_get_drvdata(psy); + int ret; + int result; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + ret = sc8549_get_vbus_present(sc); + if (!ret) + val->intval = sc->vbus_present; + else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = sc8549_get_adc_data(sc, ADC_VBAT, &result); + if (!ret) + sc->vbat_volt = result; + + val->intval = sc->vbat_volt; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = sc8549_get_adc_data(sc, ADC_IBAT, &result); + if (!ret) + sc->ibat_curr = result; + + val->intval = sc->ibat_curr; + break; + default: + return -EINVAL; + } + return 0; +} + +static int sc8549_charger_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct sc8549 *sc = power_supply_get_drvdata(psy); + switch (prop) { + case POWER_SUPPLY_PROP_PRESENT: + sc8549_set_present(sc, !!val->intval); + sc_info("set present :%d\n", val->intval); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int sc8549_charger_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + int ret; + + switch (prop) { + default: + ret = 0; + break; + } + return ret; +} + + +static const struct power_supply_desc sc8549_standalone_psy_desc = { + .name = "cp-standalone", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = sc8549_charger_props, + .num_properties = ARRAY_SIZE(sc8549_charger_props), + .get_property = sc8549_charger_get_property, + .set_property = sc8549_charger_set_property, + .property_is_writeable = sc8549_charger_is_writeable, +}; + +static int sc8549_psy_register(struct sc8549 *sc) +{ + struct power_supply_config sc8549_cfg = {}; + + + sc8549_cfg.drv_data = sc; + sc8549_cfg.of_node = sc->dev->of_node; + + sc->fc2_psy = devm_power_supply_register(sc->dev, + &sc8549_standalone_psy_desc, &sc8549_cfg); + if (IS_ERR(sc->fc2_psy)) { + pr_err("Couldn't register sc8549 power supply\n"); + return PTR_ERR(sc->fc2_psy); + } + + pr_info("power supply register sc8549 successfully\n"); + return 0; +} + +static void sc8549_check_fault_status(struct sc8549 *sc) +{ + int ret; + u8 flag = 0; + u8 stat = 0; + + mutex_lock(&sc->data_lock); + + ret = sc8549_read_byte(sc, SC8549_REG_02, &stat); + if (!ret) { + sc_err("VAC_STAT = 0x%02X\n", stat); + sc->ac_ovp_fault = !!(stat & (SC8549_AC_OVP_STAT_MASK | SC8549_AC_OVP_FLAG_MASK)); + } + + ret = sc8549_read_byte(sc, SC8549_REG_03, &stat); + if (!ret) { + sc_err("VDROP_STAT = 0x%02X\n", stat); + sc->drop_ovp_fault = !!(stat & (SC8549_VDROP_OVP_STAT_MASK | SC8549_VDROP_OVP_FLAG_MASK)); + } + + ret = sc8549_read_byte(sc, SC8549_REG_06, &stat); + if (!ret) { + sc_err("REG06_STAT = 0x%02X\n", stat); + sc->ts_shut_fault = !!(stat & SC8549_TSHUT_FLAG_MASK); + sc->ss_timeout_fault = !!(stat & SC8549_SS_TIMEOUT_FLAG_MASK); + sc->cp_switch = !!(stat & SC8549_CONV_SWITCHING_STAT_MASK); + } + + ret = sc8549_read_byte(sc, SC8549_REG_0F, &flag); + if (!ret && flag) + sc_err("FAULT_FLAG = 0x%02X\n", flag); + + if (!ret && flag != sc->prev_fault) { + sc->prev_fault = flag; + sc->bat_ovp_fault = !!(flag & SC8549_VBAT_OVP_FLAG_MASK); + sc->bat_ocp_fault = !!(flag & SC8549_IBAT_OCP_FLAG_MASK); + sc->bus_ovp_fault = !!(flag & SC8549_VBUS_OVP_FLAG_MASK); + sc->bus_ocp_fault = !!(flag & SC8549_IBUS_OCP_FLAG_MASK); + } + + mutex_unlock(&sc->data_lock); +} + + +static irqreturn_t sc8549_charger_interrupt(int irq, void *dev_id) +{ + struct sc8549 *sc = dev_id; + sc_err("------------sc8549 interrupt--------------\n"); + mutex_lock(&sc->irq_complete); + sc->irq_waiting = true; + if (!sc->resume_completed) { + dev_dbg(sc->dev, "IRQ triggered before device-resume\n"); + if (!sc->irq_disabled) { + disable_irq_nosync(irq); + sc->irq_disabled = true; + } + mutex_unlock(&sc->irq_complete); + return IRQ_HANDLED; + } + sc->irq_waiting = false; + if (sc->irq_counts > INT_MAX -1) + sc->irq_counts = 0; + else + sc->irq_counts++; + + sc8549_check_fault_status(sc); + + mutex_unlock(&sc->irq_complete); + + sc_err("report fault flag =0x%02X\n",sc->prev_fault); + return IRQ_HANDLED; +} + +static void determine_initial_status(struct sc8549 *sc) +{ + if (sc->client->irq) + sc8549_charger_interrupt(sc->client->irq, sc); +} + +#if defined(CONFIG_DEBUG_FS) +static int show_registers(struct seq_file *m, void *data) +{ + struct sc8549 *sc = m->private; + u8 addr; + int ret; + u8 val; + + for (addr = 0x0; addr <= 0x36; addr++) { + if (addr <= 0x24 || addr == 0x36) { + ret = sc8549_read_byte(sc, addr, &val); + if (!ret) + seq_printf(m, "Reg[%02X] = 0x%02X\n", addr, val); + } + } + return 0; +} + +static int reg_debugfs_open(struct inode *inode, struct file *file) +{ + struct sc8549 *sc = inode->i_private; + + return single_open(file, show_registers, sc); +} + + +static const struct file_operations reg_debugfs_ops = { + .owner = THIS_MODULE, + .open = reg_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void create_debugfs_entry(struct sc8549 *sc) +{ + sc->debug_root = debugfs_create_dir("sc8549-standalone", NULL); + + if (!sc->debug_root) + sc_err("Failed to create debug dir\n"); + + if (sc->debug_root) { + debugfs_create_file("registers", + S_IFREG | S_IRUGO, + sc->debug_root, sc, ®_debugfs_ops); + + debugfs_create_x32("skip_reads", + S_IFREG | S_IWUSR | S_IRUGO, + sc->debug_root, + &(sc->skip_reads)); + debugfs_create_x32("skip_writes", + S_IFREG | S_IWUSR | S_IRUGO, + sc->debug_root, + &(sc->skip_writes)); + } +} +#else +static void create_debugfs_entry(struct sc8549 *sc) +{} +#endif +static struct of_device_id sc8549_charger_match_table[] = { + { + .compatible = "sc,sc8549-standalone" + }, + {}, +}; +MODULE_DEVICE_TABLE(of, sc8549_charger_match_table); + +static int sc8549_iio_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val1, + int val2, long mask) +{ + struct sc8549 *chip = iio_priv(indio_dev); + int rc = 0; + + switch (chan->channel) { + case PSY_IIO_CP_ENABLE: + sc8549_enable_charge(chip, val1); + sc8549_check_charge_enabled(chip, &chip->charge_enabled); + sc_info("sc8549 cp enable: %s, %d\n", + val1 ? "enable" : "disable", chip->charge_enabled); + break; + case PSY_IIO_MMI_CP_CLEAR_ERROR: + chip->bat_ovp_fault = false; + chip->bat_ocp_fault = false; + chip->bus_ovp_fault = false; + chip->bus_ocp_fault = false; + chip->drop_ovp_fault = false; + chip->ac_ovp_fault = false; + chip->ss_timeout_fault = false; + chip->ts_shut_fault = false; + break; + default: + sc_err("Unsupported sc8549 IIO chan %d\n", chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) + sc_err("Couldn't write IIO channel %d, rc = %d\n", + chan->channel, rc); + + return rc; +} + +static int sc8549_iio_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val1, + int *val2, long mask) +{ + struct sc8549 *chip = iio_priv(indio_dev); + int rc = 0; + + *val1 = 0; + + switch (chan->channel) { + case PSY_IIO_CP_ENABLE: + sc8549_check_charge_enabled(chip, &chip->charge_enabled); + *val1 = chip->charge_enabled; + break; + case PSY_IIO_MMI_CP_INPUT_CURRENT_NOW: + rc = sc8549_get_adc_data(chip, ADC_IBUS, val1); + if (!rc) + chip->ibus_curr = *val1; + break; + case PSY_IIO_MMI_CP_INPUT_VOLTAGE_NOW: + rc = sc8549_get_adc_data(chip, ADC_VBUS, val1); + if (!rc) + chip->vbus_volt = *val1; + break; + case PSY_IIO_CP_STATUS1: + sc8549_check_fault_status(chip); + *val1 = (chip->bat_ovp_fault << MMI_BAT_OVP_FAULT_BIT) + | (chip->bat_ocp_fault << MMI_BAT_OCP_FAULT_BIT) + | (chip->bus_ovp_fault << MMI_BUS_OVP_FAULT_BIT) + | (chip->bus_ocp_fault << MMI_BUS_OCP_FAULT_BIT) + | (chip->ss_timeout_fault << MMI_SS_TIMEOUT_FAULT_BIT) + | (chip->ts_shut_fault << MMI_TS_SHUT_FAULT_BIT) + | (chip->cp_switch << MMI_CP_SWITCH_BIT); + break; + default: + sc_err("Unsupported sc8549 IIO chan %d\n", chan->channel); + rc = -EINVAL; + break; + } + + if (rc < 0) { + sc_err("Couldn't read IIO channel %d, rc = %d\n", + chan->channel, rc); + return rc; + } + + return IIO_VAL_INT; +} + +static int sc8549_iio_of_xlate(struct iio_dev *indio_dev, + const struct of_phandle_args *iiospec) +{ + struct sc8549 *chip = iio_priv(indio_dev); + struct iio_chan_spec *iio_chan = chip->iio_chan; + int i; + + for (i = 0; i < ARRAY_SIZE(sc8549_iio_psy_channels); + i++, iio_chan++) + if (iio_chan->channel == iiospec->args[0]) + return i; + + return -EINVAL; +} + +static const struct iio_info sc8549_iio_info = { + .read_raw = sc8549_iio_read_raw, + .write_raw = sc8549_iio_write_raw, + .of_xlate = sc8549_iio_of_xlate, +}; + +static int sc8549_init_iio_psy(struct sc8549 *chip) +{ + struct iio_dev *indio_dev = chip->indio_dev; + struct iio_chan_spec *chan; + int sc8549_num_iio_channels = ARRAY_SIZE(sc8549_iio_psy_channels); + int rc, i; + + chip->iio_chan = devm_kcalloc(chip->dev, sc8549_num_iio_channels, + sizeof(*chip->iio_chan), GFP_KERNEL); + if (!chip->iio_chan) + return -ENOMEM; + + chip->int_iio_chans = devm_kcalloc(chip->dev, + sc8549_num_iio_channels, + sizeof(*chip->int_iio_chans), + GFP_KERNEL); + if (!chip->int_iio_chans) + return -ENOMEM; + + indio_dev->info = &sc8549_iio_info; + indio_dev->dev.parent = chip->dev; + indio_dev->dev.of_node = chip->dev->of_node; + indio_dev->name = "sc8549-charger"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = chip->iio_chan; + indio_dev->num_channels = sc8549_num_iio_channels; + + for (i = 0; i < sc8549_num_iio_channels; i++) { + chip->int_iio_chans[i].indio_dev = indio_dev; + chan = &chip->iio_chan[i]; + chip->int_iio_chans[i].channel = chan; + chan->address = i; + chan->channel = sc8549_iio_psy_channels[i].channel_num; + chan->type = sc8549_iio_psy_channels[i].type; + chan->datasheet_name = + sc8549_iio_psy_channels[i].datasheet_name; + chan->extend_name = + sc8549_iio_psy_channels[i].datasheet_name; + chan->info_mask_separate = + sc8549_iio_psy_channels[i].info_mask; + } + + rc = devm_iio_device_register(chip->dev, indio_dev); + if (rc) + pr_err("Failed to register sc8549 IIO device, rc=%d\n", rc); + + return rc; +} + +static int sc8549_charger_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct sc8549 *sc; + struct iio_dev *indio_dev; + const struct of_device_id *match; + struct device_node *node = client->dev.of_node; + int ret; + printk("-----------sc8549---------\n"); + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*sc)); + if (!indio_dev) + return -ENOMEM; + + sc = iio_priv(indio_dev); + if (!sc) { + sc_err("Out of memory\n"); + return -ENOMEM; + } + sc->indio_dev = indio_dev; + + sc->dev = &client->dev; + + sc->client = client; + i2c_set_clientdata(client, sc); + mutex_init(&sc->i2c_rw_lock); + mutex_init(&sc->data_lock); + mutex_init(&sc->charging_disable_lock); + mutex_init(&sc->irq_complete); + + sc->resume_completed = true; + sc->irq_waiting = false; + + ret = sc8549_detect_device(sc); + if (ret) { + sc_err("No sc8549 device found!\n"); + return -ENODEV; + } + + match = of_match_node(sc8549_charger_match_table, node); + if (match == NULL) { + sc_err("device tree match not found!\n"); + return -ENODEV; + } + + ret = sc8549_init_iio_psy(sc); + if (ret < 0) { + sc_err("Failed to initialize sc8549 IIO PSY, ret = %d\n", ret); + return ret; + } + + ret = sc8549_parse_dt(sc, &client->dev); + if (ret) + return -EIO; + + ret = sc8549_init_device(sc); + if (ret) { + sc_err("Failed to init device\n"); + return ret; + } + + ret = sc8549_psy_register(sc); + if (ret) + return ret; + + sc->irq_pinctrl = + pinctrl_get_select(sc->dev, "sc8549_int_default"); + if (!sc->irq_pinctrl) { + sc_err("Couldn't set pinctrl sc8549_int_default\n"); + return ret; + } + + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, sc8549_charger_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "sc8549 charger irq", sc); + if (ret < 0) { + sc_err("request irq for irq=%d failed, ret =%d\n", + client->irq, ret); + return ret; + } + enable_irq_wake(client->irq); + } + + device_init_wakeup(sc->dev, 1); + create_debugfs_entry(sc); + + ret = sysfs_create_group(&sc->dev->kobj, &sc8549_attr_group); + if (ret) { + sc_err("failed to register sysfs. err: %d\n", ret); + return ret; + } + + determine_initial_status(sc); + + sc_info("sc8549 probe successfully, Part Num:%d\n!", + sc->part_no); + return 0; +} + + +static inline bool is_device_suspended(struct sc8549 *sc) +{ + return !sc->resume_completed; +} + +static int sc8549_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sc8549 *sc = i2c_get_clientdata(client); + + mutex_lock(&sc->irq_complete); + sc->resume_completed = false; + mutex_unlock(&sc->irq_complete); + sc_err("Suspend successfully!"); + + return 0; +} + +static int sc8549_suspend_noirq(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sc8549 *sc = i2c_get_clientdata(client); + + if (sc->irq_waiting) { + pr_err_ratelimited("Aborting suspend, an interrupt was detected while suspending\n"); + return -EBUSY; + } + return 0; +} + +static int sc8549_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sc8549 *sc = i2c_get_clientdata(client); + + mutex_lock(&sc->irq_complete); + sc->resume_completed = true; + if (sc->irq_waiting) { + sc->irq_disabled = false; + enable_irq(client->irq); + mutex_unlock(&sc->irq_complete); + sc8549_charger_interrupt(client->irq, sc); + } else { + mutex_unlock(&sc->irq_complete); + } + + power_supply_changed(sc->fc2_psy); + sc_err("Resume successfully!"); + + return 0; +} +static int sc8549_charger_remove(struct i2c_client *client) +{ + struct sc8549 *sc = i2c_get_clientdata(client); + + sc8549_enable_adc(sc, false); + + mutex_destroy(&sc->charging_disable_lock); + mutex_destroy(&sc->data_lock); + mutex_destroy(&sc->i2c_rw_lock); + mutex_destroy(&sc->irq_complete); +#if defined(CONFIG_DEBUG_FS) + debugfs_remove_recursive(sc->debug_root); +#endif + sysfs_remove_group(&sc->dev->kobj, &sc8549_attr_group); + + return 0; +} + +static void sc8549_charger_shutdown(struct i2c_client *client) +{ + struct sc8549 *sc = i2c_get_clientdata(client); + sc8549_enable_adc(sc, false); + pr_info("Shutdown Successfully\n"); +} + +static const struct dev_pm_ops sc8549_pm_ops = { + .resume = sc8549_resume, + .suspend_noirq = sc8549_suspend_noirq, + .suspend = sc8549_suspend, +}; + +static const struct i2c_device_id sc8549_charger_id[] = { + {"sc8549-standalone", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, sc8549_charger_id); + +static struct i2c_driver sc8549_charger_driver = { + .driver = { + .name = "sc8549-charger", + .owner = THIS_MODULE, + .of_match_table = sc8549_charger_match_table, + .pm = &sc8549_pm_ops, + }, + .id_table = sc8549_charger_id, + + .probe = sc8549_charger_probe, + .remove = sc8549_charger_remove, + .shutdown = sc8549_charger_shutdown, +}; + +module_i2c_driver(sc8549_charger_driver); + +MODULE_DESCRIPTION("SC SC8549 Charge Pump Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Southchip"); + diff --git a/drivers/power/mmi_sc8549/mmi_sc8549.h b/drivers/power/mmi_sc8549/mmi_sc8549.h new file mode 100644 index 000000000000..e496cdb6af82 --- /dev/null +++ b/drivers/power/mmi_sc8549/mmi_sc8549.h @@ -0,0 +1,659 @@ + +#ifndef __SC8549_HEADER__ +#define __SC8549_HEADER__ + +/* Register 00h */ +#define SC8549_REG_00 0x00 +#define SC8549_BAT_OVP_DIS_MASK 0x80 +#define SC8549_BAT_OVP_DIS_SHIFT 7 +#define SC8549_BAT_OVP_ENABLE 0 +#define SC8549_BAT_OVP_DISABLE 1 + +#define SC8549_BAT_OVP_MASK 0x3F +#define SC8549_BAT_OVP_SHIFT 0 +#define SC8549_BAT_OVP_BASE 3500 +#define SC8549_BAT_OVP_LSB 25 + +/* Register 01h */ +#define SC8549_REG_01 0x01 +#define SC8549_BAT_OCP_DIS_MASK 0x80 +#define SC8549_BAT_OCP_DIS_SHIFT 7 +#define SC8549_BAT_OCP_ENABLE 0 +#define SC8549_BAT_OCP_DISABLE 1 + +#define SC8549_BAT_OCP_MASK 0x3F +#define SC8549_BAT_OCP_SHIFT 0 +#define SC8549_BAT_OCP_BASE 2000 +#define SC8549_BAT_OCP_LSB 100 + +/* Register 02h */ +#define SC8549_REG_02 0x02 +#define SC8549_AC_OVP_STAT_MASK 0x20 +#define SC8549_AC_OVP_STAT_SHIFT 5 + +#define SC8549_AC_OVP_FLAG_MASK 0x10 +#define SC8549_AC_OVP_FLAG_SHIFT 4 + +#define SC8549_AC_OVP_MASK_MASK 0x08 +#define SC8549_AC_OVP_MASK_SHIFT 3 + +#define SC8549_AC_OVP_MASK 0x07 +#define SC8549_AC_OVP_SHIFT 0 +#define SC8549_AC_OVP_BASE 11 +#define SC8549_AC_OVP_LSB 1 +#define SC8549_AC_OVP_6P5V 65 + +/* Register 03h */ +#define SC8549_REG_03 0x03 +#define SC8549_VDROP_OVP_DIS_MASK 0x80 +#define SC8549_VDROP_OVP_DIS_SHIFT 7 +#define SC8549_VDROP_OVP_ENABLE 0 +#define SC8549_VDROP_OVP_DISABLE 1 + +#define SC8549_VDROP_OVP_STAT_MASK 0x10 +#define SC8549_VDROP_OVP_STAT_SHIFT 4 + +#define SC8549_VDROP_OVP_FLAG_MASK 0x08 +#define SC8549_VDROP_OVP_FLAG_SHIFT 3 + +#define SC8549_VDROP_OVP_MASK_MASK 0x04 +#define SC8549_VDROP_OVP_MASK_SHIFT 2 + +#define SC8549_VDROP_THRESHOLD_SET_MASK 0x02 +#define SC8549_VDROP_THRESHOLD_SET_SHIFT 1 +#define SC8549_VDROP_THRESHOLD_300MV 0 +#define SC8549_VDROP_THRESHOLD_400MV 1 + +#define SC8549_VDROP_DEGLITCH_SET_MASK 0x01 +#define SC8549_VDROP_DEGLITCH_SET_SHIFT 0 +#define SC8549_VDROP_DEGLITCH_8US 1 +#define SC8549_VDROP_DEGLITCH_5MS 0 + +/* Register 04h */ +#define SC8549_REG_04 0x04 +#define SC8549_VBUS_OVP_DIS_MASK 0x80 +#define SC8549_VBUS_OVP_DIS_SHIFT 7 +#define SC8549_VBUS_OVP_ENABLE 0 +#define SC8549_VBUS_OVP_DISABLE 1 + +#define SC8549_BUS_OVP_MASK 0x7F +#define SC8549_BUS_OVP_SHIFT 0 +#define SC8549_BUS_OVP_BASE 6000 +#define SC8549_BUS_OVP_LSB 50 + +/* Register 05h */ +#define SC8549_REG_05 0x05 +#define SC8549_BUS_UCP_DIS_MASK 0x80 +#define SC8549_BUS_UCP_DIS_SHIFT 7 +#define SC8549_BUS_UCP_ENABLE 0 +#define SC8549_BUS_UCP_DISABLE 1 + +#define SC8549_BUS_OCP_DIS_MASK 0x40 +#define SC8549_BUS_OCP_DIS_SHIFT 6 +#define SC8549_BUS_OCP_ENABLE 0 +#define SC8549_BUS_OCP_DISABLE 1 + +#define SC8549_BUS_UCP_FALL_DEGLITCH_MASK 0x20 +#define SC8549_BUS_UCP_FALL_DEGLITCH_SHIFT 5 +#define SC8549_BUS_UCP_FALL_DEGLITCH_10US 0 +#define SC8549_BUS_UCP_FALL_DEGLITCH_5MS 1 + +#define SC8549_BUS_OCP_MASK 0x0F +#define SC8549_BUS_OCP_SHIFT 0 +#define SC8549_BUS_OCP_BASE 1200 +#define SC8549_BUS_OCP_LSB 300 + +/* Register 06h */ +#define SC8549_REG_06 0x06 +#define SC8549_TSHUT_FLAG_MASK 0x80 +#define SC8549_TSHUT_FLAG_SHIFT 7 + +#define SC8549_TSHUT_STAT_MASK 0x40 +#define SC8549_TSHUT_STAT_SHIFT 6 + +#define SC8549_VBUS_ERRORLO_STAT_MASK 0x20 +#define SC8549_VBUS_ERRORLO_STAT_SHIFT 5 + +#define SC8549_VBUS_ERRORHI_STAT_MASK 0x10 +#define SC8549_VBUS_ERRORHI_STAT_SHIFT 4 + +#define SC8549_SS_TIMEOUT_FLAG_MASK 0x08 +#define SC8549_SS_TIMEOUT_FLAG_SHIFT 3 + +#define SC8549_CONV_SWITCHING_STAT_MASK 0x04 +#define SC8549_CONV_SWITCHING_STAT_SHIFT 2 + +#define SC8549_REG_TIMEOUT_FLAG_MASK 0x02 +#define SC8549_REG_TIMEOUT_FLAG_SHIFT 1 + +#define SC8549_PIN_DIAG_FALL_FLAG_MASK 0x01 +#define SC8549_PIN_DIAG_FALL_FLAG_SHIFT 0 + +/* Register 07h */ +#define SC8549_REG_07 0x07 +#define SC8549_CHG_EN_MASK 0x80 +#define SC8549_CHG_EN_SHIFT 7 +#define SC8549_CHG_ENABLE 1 +#define SC8549_CHG_DISABLE 0 + +#define SC8549_REG_RST_MASK 0x40 +#define SC8549_REG_RST_SHIFT 6 +#define SC8549_REG_RST_ENABLE 1 +#define SC8549_REG_RST_DISABLE 0 + +#define SC8549_FREQ_SHIFT_MASK 0x18 +#define SC8549_FREQ_SHIFT_SHIFT 3 +#define SC8549_FREQ_SHIFT_NORMINAL 0 +#define SC8549_FREQ_SHIFT_POSITIVE10 1 +#define SC8549_FREQ_SHIFT_NEGATIVE10 2 +#define SC8549_FREQ_SHIFT_SPREAD_SPECTRUM 3 + +#define SC8549_FSW_SET_MASK 0x07 +#define SC8549_FSW_SET_SHIFT 0 +#define SC8549_FSW_SET_300KHZ 0 +#define SC8549_FSW_SET_350KHZ 1 +#define SC8549_FSW_SET_400KHZ 2 +#define SC8549_FSW_SET_450KHZ 3 +#define SC8549_FSW_SET_500KHZ 4 +#define SC8549_FSW_SET_550KHZ 5 +#define SC8549_FSW_SET_600KHZ 6 +#define SC8549_FSW_SET_750KHZ 7 + +/* Register 08h */ +#define SC8549_REG_08 0x08 +#define SC8549_SS_TIMEOUT_SET_MASK 0xE0 +#define SC8549_SS_TIMEOUT_SET_SHIFT 5 +#define SC8549_SS_TIMEOUT_DISABLE 0 +#define SC8549_SS_TIMEOUT_40MS 1 +#define SC8549_SS_TIMEOUT_80MS 2 +#define SC8549_SS_TIMEOUT_320MS 3 +#define SC8549_SS_TIMEOUT_1280MS 4 +#define SC8549_SS_TIMEOUT_5120MS 5 +#define SC8549_SS_TIMEOUT_20480MS 6 +#define SC8549_SS_TIMEOUT_81920MS 7 + +#define SC8549_REG_TIMEOUT_DIS_MASK 0x10 +#define SC8549_REG_TIMEOUT_DIS_SHIFT 4 +#define SC8549_REG_TIMEOUT_ENABLE 0 +#define SC8549_REG_TIMEOUT_DISABLE 1 + +#define SC8549_VOUT_OVP_DIS_MASK 0x08 +#define SC8549_VOUT_OVP_DIS_SHIFT 3 +#define SC8549_VOUT_OVP_ENABLE 0 +#define SC8549_VOUT_OVP_DISABLE 1 + +#define SC8549_SET_IBAT_SNS_RES_MASK 0x04 +#define SC8549_SET_IBAT_SNS_RES_SHIFT 2 +#define SC8549_SET_IBAT_SNS_RES_5MHM 0 +#define SC8549_SET_IBAT_SNS_RES_2MHM 1 + +#define SC8549_VBUS_PD_EN_MASK 0x02 +#define SC8549_VBUS_PD_EN_SHIFT 1 +#define SC8549_VBUS_PD_ENABLE 1 +#define SC8549_VBUS_PD_DISABLE 0 + +#define SC8549_VAC_PD_EN_MASK 0x01 +#define SC8549_VAC_PD_EN_SHIFT 0 +#define SC8549_VAC_PD_ENABLE 1 +#define SC8549_VAC_PD_DISABLE 0 + +/* Register 09h */ +#define SC8549_REG_09 0x09 +#define SC8549_CHARGE_MODE_MASK 0x80 +#define SC8549_CHARGE_MODE_SHIFT 7 +#define SC8549_CHARGE_MODE_2_1 0 +#define SC8549_CHARGE_MODE_1_1 1 + +#define SC8549_PRO_FLAG_MASK 0x40 +#define SC8549_PRO_FLAG_SHIFT 6 +#define SC8549_PRO_FLAG_HAPPENED 1 +#define SC8549_PRO_FLAG_NOT_HAPPENED 0 + +#define SC8549_IBUS_UCP_RISE_FLAG_MASK 0x20 +#define SC8549_IBUS_UCP_RISE_FLAG_SHIFT 5 + +#define SC8549_IBUS_UCP_RISE_MASK_MASK 0x10 +#define SC8549_IBUS_UCP_RISE_MASK_SHIFT 4 +#define SC8549_IBUS_UCP_RISE_MASK_ENABLE 1 +#define SC8549_IBUS_UCP_RISE_MASK_DISABLE 0 + +#define SC8549_WD_TIMEOUT_FLAG_MASK 0x08 +#define SC8549_WD_TIMEOUT_FLAG_SHIFT 3 + +#define SC8549_WATCHDOG_TIMEOUT_MASK 0x07 +#define SC8549_WATCHDOG_TIMEOUT_SHIFT 0 +#define SC8549_WATCHDOG_DISABLE 0 +#define SC8549_WATCHDOG_0P2S 1 +#define SC8549_WATCHDOG_0P5S 2 +#define SC8549_WATCHDOG_1S 3 +#define SC8549_WATCHDOG_5S 4 +#define SC8549_WATCHDOG_30S 5 + +/* Register 0Ah */ +#define SC8549_REG_0A 0x0A +#define SC8549_VBAT_REG_EN_MASK 0x80 +#define SC8549_VBAT_REG_EN_SHIFT 7 +#define SC8549_VBAT_REG_ENABLE 1 +#define SC8549_VBAT_REG_DISABLE 0 + +#define SC8549_VBAT_REG_ACTIVE_STAT_MASK 0x10 +#define SC8549_VBAT_REG_ACTIVE_STAT_SHIFT 4 + +#define SC8549_VBAT_REG_ACTIVE_FLAG_MASK 0x08 +#define SC8549_VBAT_REG_ACTIVE_FLAG_SHIFT 3 + +#define SC8549_VBAT_REG_ACTIVE_MASK_MASK 0x04 +#define SC8549_VBAT_REG_ACTIVE_MASK_SHIFT 2 +#define SC8549_VBAT_REG_ACTIVE_NOT_MASK 0 +#define SC8549_VBAT_REG_ACTIVE_MASK 1 + +#define SC8549_VBAT_REG_MASK 0x03 +#define SC8549_VBAT_REG_SHIFT 0 +#define SC8549_VBAT_REG_50MV 0 +#define SC8549_VBAT_REG_100MV 1 +#define SC8549_VBAT_REG_150MV 2 +#define SC8549_VBAT_REG_200MV 3 + +/* Register 0Bh */ +#define SC8549_REG_0B 0x0B +#define SC8549_IBAT_REG_EN_MASK 0x80 +#define SC8549_IBAT_REG_EN_SHIFT 7 +#define SC8549_IBAT_REG_ENABLE 1 +#define SC8549_IBAT_REG_DISABLE 0 + +#define SC8549_IBAT_REG_ACTIVE_STAT_MASK 0x10 +#define SC8549_IBAT_REG_ACTIVE_STAT_SHIFT 4 + +#define SC8549_IBAT_REG_ACTIVE_FLAG_MASK 0x08 +#define SC8549_IBAT_REG_ACTIVE_FLAG_SHIFT 3 + +#define SC8549_IBAT_REG_ACTIVE_MASK_MASK 0x04 +#define SC8549_IBAT_REG_ACTIVE_MASK_SHIFT 2 +#define SC8549_IBAT_REG_ACTIVE_NOT_MASK 0 +#define SC8549_IBAT_REG_ACTIVE_MASK 1 + +#define SC8549_IBAT_REG_MASK 0x03 +#define SC8549_IBAT_REG_SHIFT 0 +#define SC8549_IBAT_REG_200MA 0 +#define SC8549_IBAT_REG_300MA 1 +#define SC8549_IBAT_REG_400MA 2 +#define SC8549_IBAT_REG_500MA 3 + +/* Register 0Ch */ +#define SC8549_REG_0C 0x0C +#define SC8549_IBUS_REG_EN_MASK 0x80 +#define SC8549_IBUS_REG_EN_SHIFT 7 +#define SC8549_IBUS_REG_ENABLE 1 +#define SC8549_IBUS_REG_DISABLE 0 + +#define SC8549_IBUS_REG_ACTIVE_STAT_MASK 0x40 +#define SC8549_IBUS_REG_ACTIVE_STAT_SHIFT 6 + +#define SC8549_IBUS_REG_ACTIVE_FLAG_MASK 0x20 +#define SC8549_IBUS_REG_ACTIVE_FLAG_SHIFT 5 + +#define SC8549_IBUS_REG_ACTIVE_MASK_MASK 0x10 +#define SC8549_IBUS_REG_ACTIVE_MASK_SHIFT 4 +#define SC8549_IBUS_REG_ACTIVE_NOT_MASK 0 +#define SC8549_IBUS_REG_ACTIVE_MASK 1 + +#define SC8549_IBUS_REG_MASK 0x0F +#define SC8549_IBUS_REG_SHIFT 0 +#define SC8549_IBUS_REG_BASE 1200 +#define SC8549_IBUS_REG_LSB 300 + +/* Register 0Dh */ +#define SC8549_REG_0D 0x0D +#define SC8549_PMID2OUT_UVP_MASK 0xC0 +#define SC8549_PMID2OUT_UVP_SHIFT 6 +#define SC8549_PMID2OUT_UVP_50MV 0 +#define SC8549_PMID2OUT_UVP_100MV 1 +#define SC8549_PMID2OUT_UVP_150MV 2 +#define SC8549_PMID2OUT_UVP_200MV 3 + +#define SC8549_PMID2OUT_OVP_MASK 0x30 +#define SC8549_PMID2OUT_OVP_SHIFT 4 +#define SC8549_PMID2OUT_OVP_200MV 0 +#define SC8549_PMID2OUT_OVP_300MV 1 +#define SC8549_PMID2OUT_OVP_400MV 2 +#define SC8549_PMID2OUT_OVP_500MV 3 + +#define SC8549_PMID2OUT_UVP_FLAG_MASK 0x08 +#define SC8549_PMID2OUT_UVP_FLAG_SHIFT 3 + +#define SC8549_PMID2OUT_OVP_FLAG_MASK 0x04 +#define SC8549_PMID2OUT_OVP_FLAG_SHIFT 2 + +#define SC8549_PMID2OUT_UVP_STAT_MASK 0x02 +#define SC8549_PMID2OUT_UVP_STAT_SHIFT 1 + +#define SC8549_PMID2OUT_OVP_STAT_MASK 0x01 +#define SC8549_PMID2OUT_OVP_STAT_SHIFT 0 + +/* Register 0Eh */ +#define SC8549_REG_0E 0x0E +#define SC8549_VOUT_OVP_STAT_MASK 0x80 +#define SC8549_VOUT_OVP_STAT_SHIFT 7 + +#define SC8549_VBAT_OVP_STAT_MASK 0x40 +#define SC8549_VBAT_OVP_STAT_SHIFT 6 + +#define SC8549_IBAT_OCP_STAT_MASK 0x20 +#define SC8549_IBAT_OCP_STAT_SHIFT 5 + +#define SC8549_VBUS_OVP_STAT_MASK 0x10 +#define SC8549_VBUS_OVP_STAT_SHIFT 4 + +#define SC8549_IBUS_OCP_STAT_MASK 0x08 +#define SC8549_IBUS_OCP_STAT_SHIFT 3 + +#define SC8549_IBUS_UCP_FALL_STAT_MASK 0x04 +#define SC8549_IBUS_UCP_FALL_STAT_SHIFT 2 + +#define SC8549_ADAPTER_INSERT_STAT_MASK 0x02 +#define SC8549_ADAPTER_INSERT_STAT_SHIFT 1 + +#define SC8549_VBAT_INSERT_STAT_MASK 0x01 +#define SC8549_VBAT_INSERT_STAT_SHIFT 0 + +/* Register 0Fh */ +#define SC8549_REG_0F 0x0F +#define SC8549_VOUT_OVP_FLAG_MASK 0x80 +#define SC8549_VOUT_OVP_FLAG_SHIFT 7 + +#define SC8549_VBAT_OVP_FLAG_MASK 0x40 +#define SC8549_VBAT_OVP_FLAG_SHIFT 6 + +#define SC8549_IBAT_OCP_FLAG_MASK 0x20 +#define SC8549_IBAT_OCP_FLAG_SHIFT 5 + +#define SC8549_VBUS_OVP_FLAG_MASK 0x10 +#define SC8549_VBUS_OVP_FLAG_SHIFT 4 + +#define SC8549_IBUS_OCP_FLAG_MASK 0x08 +#define SC8549_IBUS_OCP_FLAG_SHIFT 3 + +#define SC8549_IBUS_UCP_FALL_FLAG_MASK 0x04 +#define SC8549_IBUS_UCP_FALL_FLAG_SHIFT 2 + +#define SC8549_ADAPTER_INSERT_FLAG_MASK 0x02 +#define SC8549_ADAPTER_INSERT_FLAG_SHIFT 1 + +#define SC8549_VBAT_INSERT_FLAG_MASK 0x01 +#define SC8549_VBAT_INSERT_FLAG_SHIFT 0 + +/* Register 10h */ +#define SC8549_REG_10 0x10 +#define SC8549_VOUT_OVP_MASK_MASK 0x80 +#define SC8549_VOUT_OVP_MASK_SHIFT 7 + +#define SC8549_VBAT_OVP_MASK_MASK 0x40 +#define SC8549_VBAT_OVP_MASK_SHIFT 6 + +#define SC8549_IBAT_OCP_MASK_MASK 0x20 +#define SC8549_IBAT_OCP_MASK_SHIFT 5 + +#define SC8549_VBUS_OVP_MASK_MASK 0x10 +#define SC8549_VBUS_OVP_MASK_SHIFT 4 + +#define SC8549_IBUS_OCP_MASK_MASK 0x08 +#define SC8549_IBUS_OCP_MASK_SHIFT 3 + +#define SC8549_IBUS_UCP_FALL_MASK_MASK 0x04 +#define SC8549_IBUS_UCP_FALL_MASK_SHIFT 2 + +#define SC8549_ADAPTER_INSERT_MASK_MASK 0x02 +#define SC8549_ADAPTER_INSERT_MASK_SHIFT 1 + +#define SC8549_VBAT_INSERT_MASK_MASK 0x01 +#define SC8549_VBAT_INSERT_MASK_SHIFT 0 + +/* Register 11h */ +#define SC8549_REG_11 0x11 +#define SC8549_ADC_EN_MASK 0x80 +#define SC8549_ADC_EN_SHIFT 7 +#define SC8549_ADC_ENABLE 1 +#define SC8549_ADC_DISABLE 0 + +#define SC8549_ADC_RATE_MASK 0x40 +#define SC8549_ADC_RATE_SHIFT 6 +#define SC8549_ADC_RATE_CONTINOUS 0 +#define SC8549_ADC_RATE_ONESHOT 1 + +#define SC8549_ADC_DONE_STAT_MASK 0x04 +#define SC8549_ADC_DONE_STAT_SHIFT 2 + +#define SC8549_ADC_DONE_FLAG_MASK 0x02 +#define SC8549_ADC_DONE_FLAG_SHIFT 1 + +#define SC8549_ADC_DONE_MASK_MASK 0x01 +#define SC8549_ADC_DONE_MASK_SHIFT 0 + +/* Register 12h */ +#define SC8549_REG_12 0x12 +#define SC8549_VBUS_ADC_DIS_MASK 0x40 +#define SC8549_VBUS_ADC_DIS_SHIFT 6 +#define SC8549_VBUS_ADC_ENABLE 0 +#define SC8549_VBUS_ADC_DISABLE 1 + +#define SC8549_VAC_ADC_DIS_MASK 0x20 +#define SC8549_VAC_ADC_DIS_SHIFT 5 +#define SC8549_VAC_ADC_ENABLE 0 +#define SC8549_VAC_ADC_DISABLE 1 + +#define SC8549_VOUT_ADC_DIS_MASK 0x10 +#define SC8549_VOUT_ADC_DIS_SHIFT 4 +#define SC8549_VOUT_ADC_ENABLE 0 +#define SC8549_VOUT_ADC_DISABLE 1 + +#define SC8549_VBAT_ADC_DIS_MASK 0x08 +#define SC8549_VBAT_ADC_DIS_SHIFT 3 +#define SC8549_VBAT_ADC_ENABLE 0 +#define SC8549_VBAT_ADC_DISABLE 1 + +#define SC8549_IBAT_ADC_DIS_MASK 0x04 +#define SC8549_IBAT_ADC_DIS_SHIFT 2 +#define SC8549_IBAT_ADC_ENABLE 0 +#define SC8549_IBAT_ADC_DISABLE 1 + +#define SC8549_IBUS_ADC_DIS_MASK 0x02 +#define SC8549_IBUS_ADC_DIS_SHIFT 1 +#define SC8549_IBUS_ADC_ENABLE 0 +#define SC8549_IBUS_ADC_DISABLE 1 + +#define SC8549_TDIE_ADC_DIS_MASK 0x01 +#define SC8549_TDIE_ADC_DIS_SHIFT 0 +#define SC8549_TDIE_ADC_ENABLE 0 +#define SC8549_TDIE_ADC_DISABLE 1 + +/* Register 13h */ +#define SC8549_REG_13 0x13 +#define SC8549_IBUS_POL_H_MASK 0x0F +#define SC8549_IBUS_ADC_LSB_MUL 1875 +#define SC8549_IBUS_ADC_LSB_DIV 1000 + +/* Register 14h */ +#define SC8549_REG_14 0x14 +#define SC8549_IBUS_POL_L_MASK 0xFF + +/* Register 15h */ +#define SC8549_REG_15 0x15 +#define SC8549_VBUS_POL_H_MASK 0x0F +#define SC8549_VBUS_ADC_LSB_MUL 375 +#define SC8549_VBUS_ADC_LSB_DIV 100 + +/* Register 16h */ +#define SC8549_REG_16 0x16 +#define SC8549_VBUS_POL_L_MASK 0xFF + +/* Register 17h */ +#define SC8549_REG_17 0x17 +#define SC8549_VAC_POL_H_MASK 0x0F +#define SC8549_VAC_ADC_LSB 5 + +/* Register 18h */ +#define SC8549_REG_18 0x18 +#define SC8549_VAC_POL_L_MASK 0xFF + +/* Register 19h */ +#define SC8549_REG_19 0x19 +#define SC8549_VOUT_POL_H_MASK 0x0F +#define SC8549_VOUT_ADC_LSB_MUL 125 +#define SC8549_VOUT_ADC_LSB_DIV 100 + +/* Register 1Ah */ +#define SC8549_REG_1A 0x1A +#define SC8549_VOUT_POL_L_MASK 0x0F + +/* Register 1Bh */ +#define SC8549_REG_1B 0x1B +#define SC8549_VBAT_POL_H_MASK 0x0F +#define SC8549_VBAT_ADC_LSB_MUL 125 +#define SC8549_VBAT_ADC_LSB_DIV 100 + +/* Register 1Ch */ +#define SC8549_REG_1C 0x1C +#define SC8549_VBAT_POL_L_MASK 0xFF + +/* Register 1Dh */ +#define SC8549_REG_1D 0x1D +#define SC8549_IBAT_POL_H_MASK 0x0F +#define SC8549_IBAT_ADC_LSB_MUL 3125 +#define SC8549_IBAT_ADC_LSB_DIV 1000 + +/* Register 1Eh */ +#define SC8549_REG_1E 0x1E +#define SC8549_IBAT_POL_L_MASK 0xFF + +/* Register 1Fh */ +#define SC8549_REG_1F 0x1F +#define SC8549_TDIE_POL_H_MASK 0x01 +#define SC8549_TDIE_ADC_LSB_MUL 5 +#define SC8549_TDIE_ADC_LSB_DIV 10 + +/* Register 20h */ +#define SC8549_REG_20 0x20 +#define SC8549_TDIE_POL_L_MASK 0xFF + +/* Register 21h */ +#define SC8549_REG_21 0x21 +#define SC8549_DM_500K_PD_EN_MASK 0x80 +#define SC8549_DM_500K_PD_EN_SHIFT 7 +#define SC8549_DM_500K_PD_ENABLE 1 +#define SC8549_DM_500K_PD_DISABLE 0 + +#define SC8549_DP_500K_PD_EN_MASK 0x40 +#define SC8549_DP_500K_PD_EN_SHIFT 6 +#define SC8549_DP_500K_PD_ENABLE 1 +#define SC8549_DP_500K_PD_DISABLE 0 + +#define SC8549_DM_20K_PD_EN_MASK 0x20 +#define SC8549_DM_20K_PD_EN_SHIFT 5 +#define SC8549_DM_20K_PD_ENABLE 1 +#define SC8549_DM_20K_PD_DISABLE 0 + +#define SC8549_DP_20K_PD_EN_MASK 0x10 +#define SC8549_DP_20K_PD_EN_SHIFT 4 +#define SC8549_DP_20K_PD_ENABLE 1 +#define SC8549_DP_20K_PD_DISABLE 0 + +#define SC8549_DM_SINK_EN_MASK 0x08 +#define SC8549_DM_SINK_EN_SHIFT 3 +#define SC8549_DM_SINK_ENABLE 1 +#define SC8549_DM_SINK_DISABLE 0 + +#define SC8549_DP_SINK_EN_MASK 0x04 +#define SC8549_DP_SINK_EN_SHIFT 2 +#define SC8549_DP_SINK_ENABLE 1 +#define SC8549_DP_SINK_DISABLE 0 + +#define SC8549_DP_SRC_10UA_MASK 0x02 +#define SC8549_DP_SRC_10UA_SHIFT 1 +#define SC8549_DP_SRC_10UA 1 +#define SC8549_DP_SRC_MORE_THAN_250UA 0 + +#define SC8549_DPDM_EN_MASK 0x01 +#define SC8549_DPDM_EN_SHIFT 0 +#define SC8549_DPDM_ENABLE 1 +#define SC8549_DPDM_DISABLE 0 + +/* Register 22h */ +#define SC8549_REG_22 0x22 +#define SC8549_DPDM_OVP_DIS_MASK 0x40 +#define SC8549_DPDM_OVP_DIS_SHIFT 6 +#define SC8549_DPDM_OVP_ENABLE 0 +#define SC8549_DPDM_OVP_DISABLE 1 + +#define SC8549_DM_BUFF_MASK 0x30 +#define SC8549_DM_BUFF_SHIFT 4 +#define SC8549_DM_BUFF_0P6V 0 +#define SC8549_DM_BUFF_2V 1 +#define SC8549_DM_BUFF_2P7V 2 +#define SC8549_DM_BUFF_3P3V 3 + +#define SC8549_DP_BUFF_MASK 0x0C +#define SC8549_DP_BUFF_SHIFT 2 +#define SC8549_DP_BUFF_0P6V 0 +#define SC8549_DP_BUFF_2V 1 +#define SC8549_DP_BUFF_2P7V 2 +#define SC8549_DP_BUFF_3P3V 3 + +#define SC8549_DM_BUFF_EN_MASK 0x02 +#define SC8549_DM_BUFF_EN_SHIFT 1 +#define SC8549_DM_BUFF_ENABLE 1 +#define SC8549_DM_BUFF_DISABLE 0 + +#define SC8549_DP_BUFF_EN_MASK 0x01 +#define SC8549_DP_BUFF_EN_SHIFT 0 +#define SC8549_DP_BUFF_ENABLE 1 +#define SC8549_DP_BUFF_DISABLE 0 + +/* Register 23h */ +#define SC8549_REG_23 0x23 +#define SC8549_VDM_READ_MASK 0x38 +#define SC8549_VDM_READ_SHIFT 3 +#define SC8549_VDM_READ_0V_0P325V 0 +#define SC8549_VDM_READ_0P325V_1V 1 +#define SC8549_VDM_READ_1V_1P35V 2 +#define SC8549_VDM_READ_1P35V_2P2V 3 +#define SC8549_VDM_READ_2P2V_3V 4 +#define SC8549_VDM_READ_3V_3P3V 5 + +#define SC8549_VDP_READ_MASK 0x07 +#define SC8549_VDP_READ_SHIFT 0 +#define SC8549_VDP_READ_0V_0P325V 0 +#define SC8549_VDP_READ_0P325V_1V 1 +#define SC8549_VDP_READ_1V_1P35V 2 +#define SC8549_VDP_READ_1P35V_2P2V 3 +#define SC8549_VDP_READ_2P2V_3V 4 +#define SC8549_VDP_READ_3V_3P3V 5 + +/* Register 24h */ +#define SC8549_REG_24 0x24 +#define SC8549_DM_LOW_MASK_MASK 0x40 +#define SC8549_DM_LOW_MASK_SHIFT 6 + +#define SC8549_DM_LOW_FLAG_MASK 0x20 +#define SC8549_DM_LOW_FLAG_SHIFT 5 + +#define SC8549_DP_LOW_MASK_MASK 0x10 +#define SC8549_DP_LOW_MASK_SHIFT 4 + +#define SC8549_DP_LOW_FLAG_MASK 0x08 +#define SC8549_DP_LOW_FLAG_SHIFT 3 + +#define SC8549_DPDM_OVP_MASK_MASK 0x04 +#define SC8549_DPDM_OVP_MASK_SHIFT 2 + +#define SC8549_DPDM_OVP_FLAG_MASK 0x02 +#define SC8549_DPDM_OVP_FLAG_SHIFT 1 + +#define SC8549_DPDM_OVP_STAT_MASK 0x01 +#define SC8549_DPDM_OVP_STAT_SHIFT 0 + +/* Register 36h */ +#define SC8549_REG_36 0x36 +#define SC8549_DEVICE_ID 0x66 + + +#endif diff --git a/drivers/power/mmi_sc8549/mmi_sc8549_iio.h b/drivers/power/mmi_sc8549/mmi_sc8549_iio.h new file mode 100644 index 000000000000..62c971d83dcc --- /dev/null +++ b/drivers/power/mmi_sc8549/mmi_sc8549_iio.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + */ + +#ifndef __SC8549_IIO_H +#define __SC8549_IIO_H + +#include +#include + +struct sc8549_iio_channels { + const char *datasheet_name; + int channel_num; + enum iio_chan_type type; + long info_mask; +}; + +#define SC8549_IIO_CHAN(_name, _num, _type, _mask) \ + { \ + .datasheet_name = _name, \ + .channel_num = _num, \ + .type = _type, \ + .info_mask = _mask, \ + }, + +#define SC8549_CHAN_INDEX(_name, _num) \ + SC8549_IIO_CHAN(_name, _num, IIO_INDEX, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +#define SC8549_CHAN_CUR(_name, _num) \ + SC8549_IIO_CHAN(_name, _num, IIO_CURRENT, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +#define SC8549_CHAN_VOLT(_name, _num) \ + SC8549_IIO_CHAN(_name, _num, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_PROCESSED)) + +static const struct sc8549_iio_channels sc8549_iio_psy_channels[] = { + SC8549_CHAN_INDEX("sc8549_cp_enable", PSY_IIO_CP_ENABLE) + SC8549_CHAN_CUR("sc8549_input_current_now", PSY_IIO_MMI_CP_INPUT_CURRENT_NOW) + SC8549_CHAN_VOLT("sc8549_input_voltage_now", PSY_IIO_MMI_CP_INPUT_VOLTAGE_NOW) + SC8549_CHAN_INDEX("sc8549_cp_status1", PSY_IIO_CP_STATUS1) + SC8549_CHAN_INDEX("sc8549_cp_clear_error", PSY_IIO_MMI_CP_CLEAR_ERROR) +}; + +#endif \ No newline at end of file diff --git a/drivers/power/p938x_charger/Android.mk b/drivers/power/p938x_charger/Android.mk new file mode 100755 index 000000000000..e5a125dae4c4 --- /dev/null +++ b/drivers/power/p938x_charger/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := p938x_charger.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/p938x_charger/Kbuild b/drivers/power/p938x_charger/Kbuild new file mode 100755 index 000000000000..e2ccdf514caf --- /dev/null +++ b/drivers/power/p938x_charger/Kbuild @@ -0,0 +1,9 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +ifeq ($(TARGET_BUILD_VARIANT),userdebug) + EXTRA_CFLAGS += -DP938X_USER_DEBUG +endif + +obj-m += p938x_charger.o diff --git a/drivers/power/p938x_charger/Makefile b/drivers/power/p938x_charger/Makefile new file mode 100755 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/power/p938x_charger/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/power/p938x_charger/p938x_charger.c b/drivers/power/p938x_charger/p938x_charger.c new file mode 100644 index 000000000000..b0b9a83e7846 --- /dev/null +++ b/drivers/power/p938x_charger/p938x_charger.c @@ -0,0 +1,3094 @@ +/* + * Copyright (c) 2019 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CHIP_ID_REG 0x0000 +#define HW_VER_REG 0x0002 +#define CUST_ID_REG 0x0003 +#define MTP_FW_MAJ_VER_REG 0x0004 +#define MTP_FW_MIN_VER_REG 0x0006 +#define MTP_FW_DATE_REG 0x0008 +#define E2PROM_FW_VER_REG 0x001c +#define DEV_STATUS_REG 0x0034 +#define IRQ_STATUS_REG 0x0036 +#define IRQ_ENABLE_REG 0x0038 +#define IRQ_CLEAR_REG 0x003a + +#define VOUT_SET_REG 0x003c +#define ILIMIT_SET_REG 0x003d + +#define VRECT_READ_REG 0x0040 +#define VOUT_READ_REG 0x0042 +#define IOUT_READ_REG 0x0044 +#define OPT_FREQ_REG 0x0048 +#define DIE_TEMP_REG 0x0066 + +#define SYS_MODE_REG 0x004a +#define SYS_CMD_REG 0x004e + +#define EPP_QFACTOR_REG 0x0083 +#define EPP_TX_GUARANTEED 0x0084 +#define EPP_TX_POTENTIAL 0x0085 + +#define EPT_REASON_REG 0x00BE +#define FOD_CFG_REG 0x0070 +#define TX_FOD_CFG_REG 0x00AC +#define SYS_TM_MODE_REG 0x0069 +#define ST_TM_MODE_EN 3 +#define ST_TM_MODE_DIS 0 + +#define ST_TX_CONFLICT BIT(14) +#define ST_RX_CONN BIT(13) +#define ST_EPT_TYPE_INT BIT(12) +#define ST_ADT_ERR BIT(11) +#define ST_ADT_RCV BIT(9) +#define ST_ADT_SENT BIT(8) +#define ST_VOUT_ON BIT(7) +#define ST_VRECT_ON BIT(6) +#define ST_MODE_CHANGE BIT(5) +#define ST_OVER_TEMP BIT(2) +#define ST_OVER_VOLT BIT(1) +#define ST_OVER_CURR BIT(0) + +#define SYS_MODE_RAMCODE BIT(6) +#define SYS_MODE_EXTENDED BIT(3) +#define SYS_MODE_TX BIT(2) +#define SYS_MODE_WPCMODE BIT(0) + +#define CMD_TX_RM_POWER_TOGGLE BIT(10) +#define CMD_RX_RENEGOTIATE BIT(7) +#define CMD_RX_SWITCH_RAM BIT(6) +#define CMD_RX_CLR_IRQ BIT(5) +#define CMD_RX_SEND_CSP BIT(4) +#define CMD_RX_SEND_EPT BIT(3) +#define CMD_RX_CFG_TABLE BIT(2) +#define CMD_RX_TOGGLE_LDO BIT(1) +#define CMD_RX_SEND_RX_DATA BIT(0) + +#define EPT_POCP BIT(15) +#define EPT_OTP BIT(14) +#define EPT_FOD BIT(13) +#define EPT_LVP BIT(12) +#define EPT_OVP BIT(11) +#define EPT_OCP BIT(10) +#define EPT_CMD BIT(0) + +#define WAIT_FOR_AUTH_MS 1000 +#define WAIT_FOR_RCVD_TIMEOUT_MS 1000 +#define HEARTBEAT_INTERVAL_MS 3000 +#define HEARTBEAT_REPORT_INTERVAL 60000 +#define TXMODEWORK_INTERVAL_MS 2000 + +#define BPP_MAX_VOUT 5000 +#define BPP_MAX_IOUT 1600 +#define BPP_MAX_IDC 1000 +#define EPP_MAX_VOUT 12000 +#define EPP_MAX_IOUT 1600 +#define EPP_MAX_IDC 1250 + +#define MIN_VOUT_SET 5000 +#define MAX_VOUT_SET 12000 +#define MIN_IOUT_SET 500 +#define MAX_IOUT_SET 3000 + +#define FOD_MAX_LEN 16 +#define TX_FOD_WRITE_LEN 6 +#define TX_FOD_REG_LEN 13 + +#define MAX_EPT_FOD_COUNT 3 +#define MAX_EPT_POCP_COUNT 3 +#define MAX_TX_MODE_RECOVER 10 + +#define WLS_SHOW_MAX_SIZE 32 + +#define MIN_CHIP_VERS 0x9380 +#define MAX_CHIP_VERS 0x9389 +#define CHIP_VENDOR "idt" + +#define IDC_THERMAL_RESTORE_TIME 300000 /* 5 minutes */ +#define IDC_THERMAL_BACKOFF_MA 100 +#define IDC_THERMAL_MIN_CURRENT 400 + +#define FAST_CHARGE_MW 15000 + +#define DETACH_ON_READ_FAILURE + +#define p938x_err(chip, fmt, ...) \ + pr_err("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__) \ + +#define p938x_dbg(chip, reason, fmt, ...) \ + do { \ + if (*chip->debug_mask & (reason)) \ + pr_info("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__); \ + else \ + pr_debug("%s: %s: " fmt, chip->name, \ + __func__, ##__VA_ARGS__); \ + } while (0) + +static char mtp_downloader[] = { +0x00, 0x04, 0x00, 0x20, 0xE7, 0x00, 0x00, 0x00, +0x41, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0xFE, 0xE7, 0x00, 0x00, 0xF0, 0xB5, 0x42, 0x49, +0x00, 0x20, 0x0A, 0x88, 0x05, 0x46, 0x93, 0x06, +0x00, 0xD5, 0x04, 0x20, 0xD2, 0x06, 0x07, 0xD5, +0x8A, 0x78, 0x0B, 0x79, 0x1A, 0x43, 0x92, 0x07, +0x02, 0xD1, 0x20, 0x22, 0x10, 0x43, 0x01, 0x25, +0x3A, 0x4B, 0x5A, 0x22, 0x1A, 0x74, 0x39, 0x4A, +0x20, 0x3A, 0x10, 0x72, 0x02, 0x20, 0x40, 0x1C, +0x20, 0x28, 0xFC, 0xD3, 0xFF, 0x20, 0x36, 0x4B, +0x01, 0x30, 0x98, 0x81, 0x48, 0x88, 0xDC, 0x13, +0x04, 0x19, 0x00, 0x2D, 0x09, 0xD0, 0x00, 0x20, +0x03, 0xE0, 0x45, 0x18, 0xAD, 0x68, 0x25, 0x50, +0x00, 0x1D, 0x8D, 0x88, 0x85, 0x42, 0xF8, 0xD8, +0x08, 0xE0, 0x00, 0x20, 0x03, 0xE0, 0x45, 0x18, +0x2D, 0x7A, 0x25, 0x54, 0x40, 0x1C, 0x8D, 0x88, +0x85, 0x42, 0xF8, 0xD8, 0x00, 0x20, 0x10, 0x72, +0x28, 0x48, 0x98, 0x81, 0x02, 0x20, 0x00, 0x23, +0x1A, 0x46, 0x0B, 0xE0, 0x57, 0x18, 0x3E, 0x7A, +0xA5, 0x5C, 0xAE, 0x42, 0x05, 0xD0, 0x3D, 0x72, +0x00, 0x2B, 0x00, 0xD1, 0x4A, 0x80, 0x04, 0x20, +0x5B, 0x1C, 0x52, 0x1C, 0x8D, 0x88, 0x95, 0x42, +0xF0, 0xD8, 0x8B, 0x80, 0xF0, 0xBD, 0x1F, 0x49, +0x1D, 0x48, 0x08, 0x60, 0x1A, 0x48, 0x08, 0x25, +0x40, 0x38, 0x85, 0x83, 0x5A, 0x21, 0x01, 0x70, +0x01, 0x21, 0x01, 0x71, 0x05, 0x21, 0x01, 0x72, +0x19, 0x49, 0x81, 0x81, 0x12, 0x4F, 0x00, 0x20, +0x38, 0x80, 0xFF, 0x20, 0x40, 0x1E, 0xFD, 0xD1, +0x02, 0x26, 0x38, 0x78, 0x3C, 0x46, 0xC0, 0x07, +0xFB, 0xD0, 0x60, 0x88, 0xA2, 0x88, 0x10, 0x18, +0x81, 0xB2, 0x00, 0x20, 0x04, 0xE0, 0x03, 0x19, +0x1B, 0x7A, 0x59, 0x18, 0x89, 0xB2, 0x40, 0x1C, +0x82, 0x42, 0xF8, 0xD8, 0xE0, 0x88, 0x88, 0x42, +0x01, 0xD0, 0x3D, 0x80, 0xE9, 0xE7, 0x00, 0x2A, +0x03, 0xD0, 0xFF, 0xF7, 0x7F, 0xFF, 0x20, 0x80, +0xE3, 0xE7, 0x3E, 0x80, 0xE1, 0xE7, 0x00, 0x00, +0x00, 0x04, 0x00, 0x20, 0x40, 0x5C, 0x00, 0x40, +0x40, 0x30, 0x00, 0x40, 0xFF, 0x01, 0x00, 0x00, +0xFF, 0x0F, 0x00, 0x00, 0x80, 0xE1, 0x00, 0xE0, +0x04, 0x0E, 0x00, 0x00 }; + +enum { + PROGRAM_FW_NONE, + PROGRAM_FW_PENDING, + PROGRAM_FW_SUCCESS, + PROGRAM_FW_FAIL, +}; + +enum { + TX_MODE_OVERHEAT = -1, + TX_MODE_NOT_CONNECTED = 0, + TX_MODE_POWER_SHARE = 2, +}; + +enum print_reason { + PR_INTERRUPT = BIT(0), + PR_IMPORTANT = BIT(1), + PR_MISC = BIT(2), + PR_MOTO = BIT(7), + PR_FWPROG = BIT(6) +}; + +static int __debug_mask = PR_IMPORTANT; +module_param_named( + debug_mask, __debug_mask, int, S_IRUSR | S_IWUSR +); + +struct p938x_limits { + u32 tx_capability_mw; + u32 idc_max_ma; +}; + +struct p938x_charger { + const char *name; + int *debug_mask; + struct i2c_client *client; + struct device *dev; + struct regmap *regmap; + + struct regulator *vdd_i2c_vreg; + struct gpio wchg_int_n; + struct gpio wchg_en_n; + struct gpio wchg_det; + struct gpio wchg_sleep; + struct gpio wchg_boost; + int wchg_det_irq; + struct pinctrl *pinctrl_irq; + const char *pinctrl_name; + long long unsigned error_cnt; + + u16 wls_vout_max; + u16 wls_iout_max; + int wls_idc_max; + + u16 stat; + u16 irq_stat; + + struct delayed_work heartbeat_work; + struct delayed_work tx_mode_work; + struct delayed_work removal_work; + struct delayed_work restore_idc_work; + int heartbeat_count; + + u32 peek_poke_address; + struct dentry *debug_root; + + struct power_supply *usb_psy; + struct power_supply *wls_psy; + struct power_supply *dc_psy; + struct power_supply *batt_psy; + + bool batt_hot; + + char fw_name[NAME_MAX]; + int program_fw_stat; + + unsigned long flags; + + struct wakeup_source *wls_wake_source; + bool epp_mode; + + u32 bpp_current_limit; + struct p938x_limits *epp_current_limits; + int epp_limit_num; + u32 bpp_voltage; + u32 epp_voltage; + + u8 fod_array_bpp[FOD_MAX_LEN]; + u8 fod_array_epp[FOD_MAX_LEN]; + u8 fod_array_tx[TX_FOD_WRITE_LEN]; + int fod_array_bpp_len; + int fod_array_epp_len; + int fod_array_tx_len; + bool use_q_factor; + u8 q_factor; + u8 ept_fod_count; + u8 ept_pocp_count; + u8 tx_mode_recover_cnt; + + struct thermal_cooling_device *tcd; + struct mutex disconnect_lock; + struct mutex txmode_lock; +}; + +#define WLS_FLAG_BOOST_ENABLED 0 +#define WLS_FLAG_TX_ATTACHED 1 +#define WLS_FLAG_KEEP_AWAKE 2 +#define WLS_FLAG_TX_MODE_EN 3 +#define WLS_FLAG_USB_CONNECTED 4 +#define WLS_FLAG_USB_KEEP_ON 5 +#define WLS_FLAG_OVERHEAT 6 + +/* Send our notications to the battery */ +static char *pm_wls_supplied_to[] = { + "battery", +}; + +/* Get notifications from supplies */ +static char *pm_wls_supplied_from[] = { + "usb", + "battery", +}; + +static inline int p938x_is_chip_on(struct p938x_charger *chip) +{ + return (test_bit(WLS_FLAG_BOOST_ENABLED, &chip->flags) || + test_bit(WLS_FLAG_TX_ATTACHED, &chip->flags)); +} + +static inline int p938x_is_tx_connected(struct p938x_charger *chip) +{ + return test_bit(WLS_FLAG_TX_ATTACHED, &chip->flags); +} + +static inline int p938x_is_ldo_on(struct p938x_charger *chip) +{ + return (p938x_is_chip_on(chip) && (chip->stat & ST_VOUT_ON) + && gpio_get_value(chip->wchg_det.gpio)); +} + +static int p938x_read_reg(struct p938x_charger *chip, u16 reg, u8 *val) +{ + int rc; + unsigned int temp; + + rc = regmap_read(chip->regmap, reg, &temp); + if (rc >= 0) + *val = (u8)temp; + + return rc; +} + +static int p938x_write_reg(struct p938x_charger *chip, u16 reg, u8 val) +{ + return regmap_write(chip->regmap, reg, val); +} + +static void p938x_reset(struct p938x_charger *chip) +{ + p938x_write_reg(chip, 0x3040, 0x80); + msleep(100); +} + +static void p938x_pm_set_awake(struct p938x_charger *chip, int awake) +{ + if(!test_bit(WLS_FLAG_KEEP_AWAKE, &chip->flags) && awake) { + PM_STAY_AWAKE(chip->wls_wake_source); + set_bit(WLS_FLAG_KEEP_AWAKE, &chip->flags); + } else if(test_bit(WLS_FLAG_KEEP_AWAKE, &chip->flags) && !awake) { + PM_RELAX(chip->wls_wake_source); + clear_bit(WLS_FLAG_KEEP_AWAKE, &chip->flags); + } +} + +static int p938x_get_rx_vrect(struct p938x_charger *chip); +static int p938x_set_dc_en_override(struct p938x_charger *chip, int en); + +static void p938x_handle_wls_removal(struct p938x_charger *chip) +{ + if (!mutex_trylock(&chip->disconnect_lock)) { + cancel_delayed_work(&chip->removal_work); + schedule_delayed_work(&chip->removal_work, msecs_to_jiffies(500)); + return; + } + + if(test_bit(WLS_FLAG_TX_ATTACHED, &chip->flags)) { + /* Make sure we aren't actually connect */ + if (gpio_get_value(chip->wchg_det.gpio)) + goto unlock; + + clear_bit(WLS_FLAG_TX_ATTACHED, &chip->flags); + power_supply_changed(chip->wls_psy); + cancel_delayed_work(&chip->heartbeat_work); + + chip->stat = 0; + chip->irq_stat = 0; + chip->heartbeat_count = 0; + + p938x_set_dc_en_override(chip, 0); + + if (!test_bit(WLS_FLAG_BOOST_ENABLED, &chip->flags)) + p938x_pm_set_awake(chip, 0); + + p938x_dbg(chip, PR_IMPORTANT, "Wireless charger is removed\n"); + } + +unlock: + mutex_unlock(&chip->disconnect_lock); +} + +static void p938x_removal_work(struct work_struct *work) +{ + struct p938x_charger *chip = container_of(work, + struct p938x_charger, removal_work.work); + + p938x_handle_wls_removal(chip); +} + +static int p938x_read_buffer(struct p938x_charger *chip, u16 reg, + u8 *buf, u32 size) +{ + int rc; + + rc = regmap_bulk_read(chip->regmap, reg, buf, size); +#ifdef DETACH_ON_READ_FAILURE + if(rc) + p938x_handle_wls_removal(chip); +#endif + + return rc; +} + +static int p938x_write_buffer(struct p938x_charger *chip, u16 reg, + u8 *buf, u32 size) +{ + int rc = 0; + + while (size--) { + rc = regmap_write(chip->regmap, reg++, *buf++); + if (rc < 0) + break; + } + + return rc; +} + +static int p938x_get_rx_vout(struct p938x_charger *chip) +{ + int rc; + u16 volt; + + if(!p938x_is_tx_connected(chip)) + return 0; + + rc = p938x_read_buffer(chip, VOUT_READ_REG, (u8 *)&volt, 2); + if (rc < 0) { + p938x_err(chip, "Failed to read rx voltage, rc = %d\n", rc); + return rc; + } + + return volt; +} + +static int p938x_get_rx_vrect(struct p938x_charger *chip) +{ + int rc; + u16 volt; + + if(!p938x_is_tx_connected(chip)) + return 0; + + rc = p938x_read_buffer(chip, VRECT_READ_REG, (u8 *)&volt, 2); + if (rc < 0) { + p938x_err(chip, "Failed to read rx voltage, rc = %d\n", rc); + return rc; + } + + return volt; +} + +static int p938x_get_rx_vout_set(struct p938x_charger *chip) +{ + int rc; + u8 volt; + + if(!p938x_is_chip_on(chip)) + return 0; + + rc = p938x_read_buffer(chip, VOUT_SET_REG, &volt, 1); + if (rc < 0) { + p938x_err(chip, "Failed to read rx voltage, rc = %d\n", rc); + return rc; + } + + return (volt * 100); +} + +static int p938x_set_rx_vout(struct p938x_charger *chip, u16 mv) +{ + int rc; + + if(!p938x_is_chip_on(chip)) + return 1; + + if (mv < MIN_VOUT_SET) + mv = MIN_VOUT_SET; + else if (mv > MAX_VOUT_SET) + mv = MAX_VOUT_SET; + + rc = p938x_write_reg(chip, VOUT_SET_REG, (u8)(mv / 100)); + if (rc < 0) + p938x_err(chip, "Failed to set rx voltage, rc = %d\n", rc); + else + p938x_dbg(chip, PR_MOTO, "Set VOUT to %u mV\n", mv); + + return rc; +} + +static int p938x_set_rx_ocl(struct p938x_charger *chip, u16 ma) +{ + int rc; + + if(!p938x_is_chip_on(chip)) + return 1; + + if (ma < MIN_IOUT_SET) + ma = MIN_IOUT_SET; + else if (ma > MAX_IOUT_SET) + ma = MAX_IOUT_SET; + + rc = p938x_write_reg(chip, ILIMIT_SET_REG, (u8)((ma - 100) / 100)); + if (rc < 0) + p938x_err(chip, "Failed to set rx current, rc = %d\n", rc); + else + p938x_dbg(chip, PR_MOTO, "Set ILIMIT to %u mA\n", ma); + + return rc; +} + +static int p938x_get_rx_iout(struct p938x_charger *chip) +{ + int rc; + u16 ma; + + if(!p938x_is_chip_on(chip)) + return 0; + + rc = p938x_read_buffer(chip, IOUT_READ_REG, (u8 *)&ma, 2); + if (rc < 0) { + p938x_err(chip, "Failed to read rx current, rc = %d\n", rc); + return rc; + } + + return ma; +} + +static int p938x_get_rx_ocl(struct p938x_charger *chip) +{ + int rc; + u16 ma; + + if(!p938x_is_chip_on(chip)) + return 0; + + rc = p938x_read_buffer(chip, ILIMIT_SET_REG, (u8 *)&ma, 2); + if (rc < 0) { + p938x_err(chip, "Failed to read rx current, rc = %d\n", rc); + return rc; + } + + return (ma & 0xf) * 100 + 100; +} + +static int p938x_enable_charging(struct p938x_charger *chip, bool on) +{ + int rc = 0; + + if(!p938x_is_tx_connected(chip)) + return 1; + + rc = p938x_write_reg(chip, SYS_CMD_REG, CMD_RX_TOGGLE_LDO); + if (rc < 0) + p938x_err(chip, "Failed to %s RX ldo, rc = %d\n", + on ? "enable":"disable", rc); + else { + p938x_dbg(chip, PR_MOTO, "RX ldo is %s\n", + on ? "enabled":"disabled"); + } + + return rc; +} + +static inline void p938x_set_boost(struct p938x_charger *chip, int val) +{ + /* Assume if we turned the boost on we want to stay awake */ + gpio_set_value(chip->wchg_boost.gpio, !!val); + + if(val) { + set_bit(WLS_FLAG_BOOST_ENABLED, &chip->flags); + p938x_pm_set_awake(chip, 1); + gpio_set_value(chip->wchg_en_n.gpio, 0); + } else { + clear_bit(WLS_FLAG_BOOST_ENABLED, &chip->flags); + if (!p938x_is_chip_on(chip)) + p938x_pm_set_awake(chip, 0); + } +} + +static inline int p938x_get_boost(struct p938x_charger *chip) +{ + return gpio_get_value(chip->wchg_boost.gpio); +} + +static void p938x_toggle_power(struct p938x_charger *chip, int extra_delay_ms) +{ + p938x_set_boost(chip, 0); + msleep(100 + extra_delay_ms); + p938x_set_boost(chip, 1); + msleep(100); +} + +static int p938x_get_dc_now_icl_ma(struct p938x_charger *chip, unsigned int *idc); +static int p938x_set_dc_now_icl_ma(struct p938x_charger *chip, unsigned int idc); + +static int p938x_update_supplies_status(struct p938x_charger *chip) +{ + int rc; + union power_supply_propval prop = {0,}; + int wired_connection; + unsigned int dc_current_now; + unsigned int dc_current_new; + + if (!chip->usb_psy) + chip->usb_psy = power_supply_get_by_name("usb"); + if (!chip->usb_psy) { + pr_debug("USB psy not found\n"); + return -EINVAL; + } + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &prop); + if (rc) { + p938x_err(chip, "Couldn't read USB present prop, rc=%d\n", rc); + return rc; + } + + wired_connection = !!prop.intval; + + /* TODO For now disable wireless charging when a usb cable is connected */ + if (wired_connection) { + set_bit(WLS_FLAG_USB_CONNECTED, &chip->flags); + if(test_bit(WLS_FLAG_TX_ATTACHED, &chip->flags) && + !test_bit(WLS_FLAG_USB_KEEP_ON, &chip->flags)) { + gpio_set_value(chip->wchg_en_n.gpio, 1); + /* Turn off boost too just in case */ + p938x_set_boost(chip, 0); + p938x_dbg(chip, PR_MOTO, "usb and tx connected, disabled wls\n"); + } + } + else { + clear_bit(WLS_FLAG_USB_CONNECTED, &chip->flags); + gpio_set_value(chip->wchg_en_n.gpio, 0); + } + + if (!chip->batt_psy) + chip->batt_psy = power_supply_get_by_name("battery"); + if (!chip->batt_psy) { + pr_debug("batt psy not found\n"); + return -EINVAL; + } + rc = power_supply_get_property(chip->batt_psy, + POWER_SUPPLY_PROP_HOT_TEMP, &prop); + if (rc) { + p938x_err(chip, "Couldn't read batt temp zone prop, rc=%d\n", rc); + return rc; + } + + if (!chip->batt_hot && prop.intval) { + /* If the battery is too hot, lower the current + * setting to try to keep us in optimal temp zone + */ + cancel_delayed_work(&chip->restore_idc_work); + chip->batt_hot = 1; + + rc = p938x_get_dc_now_icl_ma(chip, &dc_current_now); + if (rc) { + p938x_err(chip, "Couldn't read dc current, rc=%d\n", rc); + return rc; + } + + dc_current_new = dc_current_now - IDC_THERMAL_BACKOFF_MA; + if (dc_current_new < IDC_THERMAL_MIN_CURRENT) + dc_current_new = IDC_THERMAL_MIN_CURRENT; + + p938x_set_dc_now_icl_ma(chip, dc_current_new); + p938x_dbg(chip, PR_IMPORTANT, "Reduced dc current to %u mA due to hot battery\n", + dc_current_new); + } else if (chip->batt_hot && !prop.intval) { + /* Set work to restore original current settings */ + chip->batt_hot = 0; + cancel_delayed_work(&chip->restore_idc_work); + schedule_delayed_work(&chip->restore_idc_work, + msecs_to_jiffies(IDC_THERMAL_RESTORE_TIME)); + } + + return 0; +} + +static void p938x_clear_irq(struct p938x_charger *chip, u16 mask) +{ + p938x_dbg(chip, PR_MOTO, "IRQ Clear 0x%02X\n", mask); + p938x_write_buffer(chip, IRQ_CLEAR_REG, (u8 *)&mask, 2); + p938x_write_reg(chip, SYS_CMD_REG, CMD_RX_CLR_IRQ); +} + +static void p938x_heartbeat_work(struct work_struct *work) +{ + struct p938x_charger *chip = container_of(work, + struct p938x_charger, heartbeat_work.work); + int vout = 0; + int iout = 0; + int vout_set = 0; + int iout_limit = 0; + int vrect = 0; + + if (p938x_is_tx_connected(chip)) { + /* Check if we disconnected */ + vrect = p938x_get_rx_vrect(chip); + if (vrect <= 0) { + p938x_err(chip, "Detected wireless charger is not attached.\n"); + p938x_handle_wls_removal(chip); + return; + } + + if (!p938x_is_ldo_on(chip)) { + p938x_dbg(chip, PR_MOTO, "LDO is not on yet\n"); + /* This is where we will trigger it after the first VRECT_ON, so + * first indication to system we have a power supply attached is VRECT_ON + * + initial delay to hb + */ + power_supply_changed(chip->wls_psy); + chip->heartbeat_count = 0; + goto schedule_hb; + } + + vout = p938x_get_rx_vout(chip); + vout_set = p938x_get_rx_vout_set(chip); + iout = p938x_get_rx_iout(chip); + iout_limit = p938x_get_rx_ocl(chip); + + if (!chip->heartbeat_count) { + p938x_dbg(chip, PR_IMPORTANT, + "mode=%s vout=%d vout_set=%d vrect=%d iout=%d ocl=%d\n", + chip->epp_mode ? "epp" : "bpp", + vout, vout_set, vrect, iout, iout_limit); + } + + chip->heartbeat_count++; + if (HEARTBEAT_INTERVAL_MS * chip->heartbeat_count >= + HEARTBEAT_REPORT_INTERVAL) + chip->heartbeat_count = 0; + +schedule_hb: + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(HEARTBEAT_INTERVAL_MS)); + } +} + +static int p938x_init_gpio(struct p938x_charger *chip, + struct gpio *p_gpio, int dir_out, int out_val) +{ + int rc; + + rc = gpio_request_one(p_gpio->gpio, p_gpio->flags, p_gpio->label); + if (rc) { + p938x_err(chip, "Failed to request gpio %d\n", p_gpio->gpio); + return rc; + } + + if (dir_out) + rc = gpio_direction_output(p_gpio->gpio, out_val); + else + rc = gpio_direction_input(p_gpio->gpio); + + if (rc) { + p938x_err(chip, "Failed to set gpio direction for gpio %d\n", + p_gpio->gpio); + return rc; + } + + return 0; +} + +static int p938x_hw_init(struct p938x_charger *chip) +{ + int rc; + + if (regulator_count_voltages(chip->vdd_i2c_vreg) > 0) { + rc = regulator_set_voltage(chip->vdd_i2c_vreg, + 1800000, 1800000); + if (rc) { + p938x_err(chip, "Failed to set vreg voltage, rc=%d\n", + rc); + return rc; + } + } + rc = regulator_enable(chip->vdd_i2c_vreg); + if (rc) { + p938x_err(chip, "Failed to enable vdd vreg, rc=%d\n", rc); + return rc; + } + + if (chip->pinctrl_name) { + chip->pinctrl_irq = pinctrl_get_select(chip->dev, + chip->pinctrl_name); + if (IS_ERR(chip->pinctrl_irq)) { + p938x_err(chip, + "Couldn't get/set %s pinctrl state rc=%ld\n", + chip->pinctrl_name, + PTR_ERR(chip->pinctrl_irq)); + rc = PTR_ERR(chip->pinctrl_irq); + goto disable_vreg; + } + } + + if (p938x_init_gpio(chip, &chip->wchg_int_n, 0, 0)) + goto disable_vreg; + + if (p938x_init_gpio(chip, &chip->wchg_det, 0, 0)) + goto disable_vreg; + + if (p938x_init_gpio(chip, &chip->wchg_en_n, 1, 0)) + goto disable_vreg; + + if (p938x_init_gpio(chip, &chip->wchg_boost, 1, 0)) + goto disable_vreg; + + if (p938x_init_gpio(chip, &chip->wchg_sleep, 1, 0)) + goto disable_vreg; + + chip->wchg_det_irq = gpio_to_irq(chip->wchg_det.gpio); + +disable_vreg: + regulator_disable(chip->vdd_i2c_vreg); + + return rc; +} + +static int p938x_set_dc_psp_prop(struct p938x_charger *chip, + enum power_supply_property psp, + union power_supply_propval val) +{ + int rc = 1; + + if (chip->dc_psy) { + rc = power_supply_set_property(chip->dc_psy, psp, &val); + if (rc < 0) { + p938x_err(chip, "Couldn't set dc prop %d, rc=%d\n", psp, rc); + } + } + + return rc; +} + +static int p938x_set_dc_suspend(struct p938x_charger *chip, int en) +{ + union power_supply_propval val; + + val.intval = en; + + return p938x_set_dc_psp_prop(chip, + POWER_SUPPLY_PROP_INPUT_SUSPEND, + val); +} + +static int p938x_set_dc_en_override(struct p938x_charger *chip, int en) +{ + union power_supply_propval val; + + val.intval = en; + + return p938x_set_dc_psp_prop(chip, + POWER_SUPPLY_PROP_PIN_ENABLED, + val); +} + +static int p938x_set_dc_max_icl_ma(struct p938x_charger *chip, unsigned int idc) +{ + union power_supply_propval val; + + p938x_dbg(chip, PR_MOTO, "Setting IDC max to %u mA\n", idc); + + idc *= 1000; /* Convert to uA */ + val.intval = idc; + + return p938x_set_dc_psp_prop(chip, + POWER_SUPPLY_PROP_CURRENT_MAX, + val); +} + +static int p938x_set_dc_now_icl_ma(struct p938x_charger *chip, unsigned int idc) +{ + union power_supply_propval val; + + p938x_dbg(chip, PR_MOTO, "Setting IDC to %u mA\n", idc); + + idc *= 1000; /* Convert to uA */ + val.intval = idc; + + return p938x_set_dc_psp_prop(chip, + POWER_SUPPLY_PROP_CURRENT_NOW, + val); +} + +static int p938x_get_dc_now_icl_ma(struct p938x_charger *chip, unsigned int *idc) +{ + union power_supply_propval prop = {0,}; + int rc = 1; + + if (chip->dc_psy) { + rc = power_supply_get_property(chip->dc_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &prop); + if (rc) { + p938x_err(chip, "Couldn't read batt temp zone prop, rc=%d\n", rc); + return rc; + } + + *idc = prop.intval / 1000; + } + + return rc; +} + +static int p938x_set_dc_aicl_rerun(struct p938x_charger *chip) +{ + union power_supply_propval val; + + /* unused */ + val.intval = 1; + + return p938x_set_dc_psp_prop(chip, + POWER_SUPPLY_PROP_RERUN_AICL, + val); +} + +static void p938x_restore_idc_work(struct work_struct *work) +{ + struct p938x_charger *chip = container_of(work, + struct p938x_charger, restore_idc_work.work); + + p938x_dbg(chip, PR_IMPORTANT, "Running AICL to get back to max dc current\n"); + p938x_set_dc_aicl_rerun(chip); +} + +static int p938x_program_tx_fod(struct p938x_charger *chip, + u8 *array, int array_len) +{ + int rc; + + if (array_len <= 0 || array_len > TX_FOD_WRITE_LEN) { + p938x_dbg(chip, PR_MOTO, "FOD length is not valid: %d\n", array_len); + return 1; + } + + rc = p938x_write_buffer(chip, TX_FOD_CFG_REG, + array, array_len); + if (rc < 0) { + p938x_err(chip, "Failed to program fod: %d\n", rc); + return 1; + } + + return 0; +} + +static int p938x_program_fod(struct p938x_charger *chip, + u8 *array, int array_len) +{ + int rc; + + if (array_len <= 0 || array_len > FOD_MAX_LEN) { + p938x_dbg(chip, PR_MOTO, "FOD length is not valid: %d\n", array_len); + return 1; + } + + rc = p938x_write_buffer(chip, FOD_CFG_REG, + array, array_len); + if (rc < 0) { + p938x_err(chip, "Failed to program fod: %d\n", rc); + return 1; + } + + return 0; +} + +static void p938x_configure_tx_mode(struct p938x_charger *chip) +{ + u16 buf = 0; + int rc; + + p938x_dbg(chip, PR_MOTO, "Configuring tx mode\n"); + + /* Configure the TX FOD parameters */ + p938x_program_tx_fod(chip, chip->fod_array_tx, + chip->fod_array_tx_len); + + /* Set to disable on tx error */ + rc = p938x_read_buffer(chip, SYS_CMD_REG, (u8 *)&buf, 2); + if (rc < 0) { + p938x_err(chip, "Failed to read 0x%04x, rc=%d\n", + SYS_CMD_REG, rc); + buf = 0; + } + buf = buf | CMD_TX_RM_POWER_TOGGLE; + p938x_write_buffer(chip, SYS_CMD_REG, (u8 *)&buf, 2); +} + +static int p938x_disable_tx_mode(struct p938x_charger *chip) +{ + int rc; + u16 buf = 0; + + if (!test_bit(WLS_FLAG_TX_MODE_EN, &chip->flags)) { + p938x_dbg(chip, PR_MOTO, "Tx mode already disabled\n"); + return 1; + } + + /* Make sure it won't accidently get re-enabled, there is a small + * window where there is a chance if we don't cancel this + */ + cancel_delayed_work_sync(&chip->tx_mode_work); + + rc = p938x_write_reg(chip, SYS_TM_MODE_REG, ST_TM_MODE_DIS); + if (rc < 0) + p938x_err(chip, "Failed to write 0x%04x(%d), rc=%d\n", + SYS_TM_MODE_REG, ST_TM_MODE_EN, rc); + + rc = p938x_read_buffer(chip, SYS_CMD_REG, (u8 *)&buf, 2); + if (rc < 0) { + p938x_err(chip, "Failed to read 0x%04x, rc=%d\n", + SYS_CMD_REG, rc); + buf = 0; + } + + buf = buf & ~ CMD_TX_RM_POWER_TOGGLE; + p938x_write_buffer(chip, SYS_CMD_REG, (u8 *)&buf, 2); + if (rc < 0) + p938x_err(chip, "Failed to write 0x%04x, rc=%d\n", + SYS_CMD_REG, rc); + + p938x_set_boost(chip, 0); + p938x_set_dc_en_override(chip, 0); + p938x_set_dc_suspend(chip, 0); + + clear_bit(WLS_FLAG_TX_MODE_EN, &chip->flags); + sysfs_notify(&chip->dev->kobj, NULL, "tx_mode"); + + return 0; +} + +static inline void p938x_set_tx_mode(struct p938x_charger *chip, int val) +{ + int rc; + + if (chip->program_fw_stat == PROGRAM_FW_PENDING) { + p938x_err(chip, "Tx mode request rejected, fw programming.\n"); + return; + } + + mutex_lock(&chip->txmode_lock); + + if (test_bit(WLS_FLAG_TX_ATTACHED, &chip->flags)) { + p938x_err(chip, "Tx mode request rejected, charger is attached.\n"); + goto unlock; + } + + if (val) { + if (test_bit(WLS_FLAG_TX_MODE_EN, &chip->flags)) { + p938x_dbg(chip, PR_MOTO, "Tx mode already enabled\n"); + goto unlock; + } + + if (test_bit(WLS_FLAG_OVERHEAT, &chip->flags)) { + p938x_dbg(chip, PR_IMPORTANT, "Device too hot to enable tx mode\n"); + goto unlock; + } + + /* Force dc in off so system doesn't see charger attached */ + /* TODO are both dc_en and dc suspend needed? */ + p938x_set_dc_suspend(chip, 1); + p938x_set_dc_en_override(chip, 1); + + /* Power on and wait for boot */ + if(!p938x_is_chip_on(chip)) { + p938x_set_boost(chip, 1); + msleep(100); + } + + rc = p938x_write_reg(chip, SYS_TM_MODE_REG, ST_TM_MODE_EN); + if (rc < 0) { + p938x_err(chip, "Failed to write 0x%04x(%d), rc=%d\n", + SYS_TM_MODE_REG, ST_TM_MODE_EN, rc); + p938x_set_boost(chip, 0); + p938x_set_dc_en_override(chip, 0); + p938x_set_dc_suspend(chip, 0); + } else { + p938x_dbg(chip, PR_MOTO, "tx mode enabled OK\n"); + set_bit(WLS_FLAG_TX_MODE_EN, &chip->flags); + sysfs_notify(&chip->dev->kobj, NULL, "tx_mode"); + chip->ept_fod_count = 0; + chip->ept_pocp_count = 0; + chip->tx_mode_recover_cnt = 0; + cancel_delayed_work(&chip->tx_mode_work); + schedule_delayed_work(&chip->tx_mode_work, + msecs_to_jiffies(TXMODEWORK_INTERVAL_MS)); + } + } else { + if (!p938x_disable_tx_mode(chip)) + p938x_dbg(chip, PR_MOTO, "tx mode disabled\n"); + } + +unlock: + mutex_unlock(&chip->txmode_lock); +} + +static int p938x_tcd_get_max_state(struct thermal_cooling_device *tcd, + unsigned long *state) +{ + *state = 1; + + return 0; +} + +static int p938x_tcd_get_cur_state(struct thermal_cooling_device *tcd, + unsigned long *state) +{ + struct p938x_charger *chip = tcd->devdata; + + *state = test_bit(WLS_FLAG_OVERHEAT, &chip->flags); + + return 0; +} + +static int p938x_tcd_set_cur_state(struct thermal_cooling_device *tcd, + unsigned long state) +{ + struct p938x_charger *chip = tcd->devdata; + + if (state && !test_bit(WLS_FLAG_OVERHEAT, &chip->flags)) { + p938x_dbg(chip, PR_IMPORTANT, "Wireless charger overtemp\n"); + set_bit(WLS_FLAG_OVERHEAT, &chip->flags); + p938x_set_tx_mode(chip, 0); + sysfs_notify(&chip->dev->kobj, NULL, "rx_connected"); + } else if (!state && test_bit(WLS_FLAG_OVERHEAT, &chip->flags)) { + p938x_dbg(chip, PR_IMPORTANT, "Wireless charger temp OK\n"); + clear_bit(WLS_FLAG_OVERHEAT, &chip->flags); + sysfs_notify(&chip->dev->kobj, NULL, "rx_connected"); + } + + return 0; +} + +static const struct thermal_cooling_device_ops p938x_tcd_ops = { + .get_max_state = p938x_tcd_get_max_state, + .get_cur_state = p938x_tcd_get_cur_state, + .set_cur_state = p938x_tcd_set_cur_state, +}; + +static int p938x_program_mtp_downloader(struct p938x_charger *chip) +{ + int i; + int rc = 0; + + /* + * Transfer 9382 boot loader code "MTPBootloader" to 9382 SRAM + * - Setup 9382 registers before transferring the boot loader code + * - Transfer the boot loader code to 9382 SRAM + * - Reset 9382 => 9382 M0 runs the boot loader + */ + rc = p938x_write_reg(chip, 0x3000, 0x5a); + if (rc < 0) { + p938x_err(chip, "Failed to write 0x3000(5a), rc=%d\n", rc); + return rc; + } + + msleep(10); + + rc = p938x_write_reg(chip, 0x3040, 0x10); + if (rc < 0) { + p938x_err(chip, "Failed to write 0x3040(10), rc=%d\n", rc); + return rc; + } + + msleep(10); + + /* Write MTP downloader data */ + for (i = 0; i < sizeof(mtp_downloader); i++) { + rc = p938x_write_reg(chip, 0x1c00 + i, mtp_downloader[i]); + if (rc < 0) { + p938x_err(chip, "Failed to program MTP downloader\n"); + return rc; + } + } + + rc = p938x_write_reg(chip, 0x400, 0x00); + if (rc < 0) { + p938x_err(chip, "Failed to write 0x400(0), rc=%d\n", rc); + return rc; + } + + rc = p938x_write_reg(chip, 0x3048, 0x80); + if (rc < 0) { + p938x_err(chip, "Failed to write 0x3048(80), rc=%d\n", rc); + return rc; + } + + /* ignoreNAK */ + p938x_reset(chip); + + return rc; +} + +static inline int align_16(u32 val) +{ + return ((val + 15) / 16 ) * 16; +} + +#define WLS_LOG_BUF_SIZE 1024 +static void p938x_print_packet(struct p938x_charger *chip, u16 addr, char *buf, int len) +{ + int i; + char log_buf[WLS_LOG_BUF_SIZE]; + int offset = 0; + + for (i=0; i= 0; i--) + check_sum += buf[i + FW_MTP_PACK_HEADER_LEN]; + check_sum += code_len; + + /*(3) Fill up StartAddr, CodeLength, CheckSum of the current packet.*/ + memcpy(buf + 2, &addr, 2); + memcpy(buf + 4, &code_len, 2); + memcpy(buf + 6, &check_sum, 2); + + p938x_dbg(chip, PR_FWPROG, "Writing MTP to 0x%04x (%d;%d)\n", + addr, code_len, packet_len); + p938x_print_packet(chip, addr, buf, packet_len); + + /* Send the current packet to SRAM via I2C + * read status is guaranteed to be != 1 at this point + */ + for (i = 0; i < packet_len; i++) { + rc = p938x_write_reg(chip, 0x400 + i, buf[i]); + if (rc < 0) { + p938x_err(chip, "ERROR: on writing to MTP buffer\n"); + return rc; + } + } + + /* Write 0x11 to the Status in the SRAM. This informs the 9382 to + * start programming the new packet + * from SRAM to OTP memory + */ + rc = p938x_write_reg(chip, 0x400, 0x1); + if (rc < 0) { + p938x_err(chip, "ERROR: on MTP buffer validation\n"); + return rc; + } + + /* Wait for 9382 bootloader to complete programming the current + * packet image data from SRAM to the OTP. + * The boot loader will update the Status in the SRAM as follows: + * Status: + * "0" - reset value (from AP) + * "1" - buffer validated / busy (from AP) + * "2" - finish "OK" (from the boot loader) + * "4" - programming error (from the boot loader) + * "8" - wrong check sum (from the boot loader) + * "16"- programming not possible (try to write "0" to bit location + * already programmed to "1") + * (from the boot loader) + * DateTime startT = DateTime.Now; + */ + do { + msleep(20); + p938x_read_reg(chip, 0x400, buf); + if (retry_cnt++ > FW_MTP_CHECK_ST_RETRY_CNT) { + p938x_err(chip, "Status timed out"); + break; + } + } while (buf[0] == 1); + + if (buf[0] != 2) { + p938x_err(chip, + "ERROR: Programming MTP buffer status:%02x at:%d\n", + buf[0], addr); + return -EAGAIN; + } + + return 0; +} + +static int p938x_program_mtp(struct p938x_charger *chip, const u8 *src, u32 size) +{ + int i; + int rc; + + /* Program MTP image data to 9382 memory */ + for (i = 0; i < size; i += FW_MTP_PACK_DATA_LEN) { + rc = p938x_program_mtp_package(chip, src, i, size); + if (rc < 0) { + p938x_err(chip, "Program MTP failed at 0x%04x\n", i); + return rc; + } + src += FW_MTP_PACK_DATA_LEN; + } + + /* + * Need to reset or power cycle 9382 to run the OTP code + */ + rc = p938x_write_reg(chip, 0x3000, 0x5a); + if (rc < 0) { + p938x_err(chip, "Failed to write 0x3000(5a), rc=%d\n", rc); + return rc; + } + + rc = p938x_write_reg(chip, 0x3048, 0x00); + if (rc < 0) { + p938x_err(chip, "Failed to write 0x3048(00), rc=%d\n", rc); + return rc; + } + + /* ignoreNAK */ + p938x_reset(chip); + + return rc; +} + +static int p938x_program_fw(struct p938x_charger *chip) +{ + int rc = 0; + const struct firmware *fw = NULL; + + if (!strlen(chip->fw_name)) { + p938x_err(chip, "No FW name given\n"); + return -EINVAL; + } + + if (chip->program_fw_stat == PROGRAM_FW_PENDING) { + p938x_err(chip, "Programming FW is ongoing\n"); + return -EBUSY; + } + chip->program_fw_stat = PROGRAM_FW_PENDING; + + rc = request_firmware(&fw, chip->fw_name, chip->dev); + if (rc < 0) { + p938x_err(chip, "Request firmware (%s) failed, rc=%d\n", + chip->fw_name, rc); + chip->program_fw_stat = PROGRAM_FW_FAIL; + return rc; + } + + mutex_lock(&chip->disconnect_lock); + mutex_lock(&chip->txmode_lock); + + /* Make sure tx mode is off or we'll fail */ + p938x_disable_tx_mode(chip); + msleep(100); + + /* Turn on the boost so i2c works */ + p938x_set_boost(chip, 1); + + /* Brief delay for ic init */ + msleep(100); + + p938x_dbg(chip, PR_FWPROG, "Loading FW programmer...\n"); + + rc = p938x_program_mtp_downloader(chip); + if (rc < 0) { + p938x_err(chip, "Programming MTP downloader failed, rc=%d\n", + rc); + chip->program_fw_stat = PROGRAM_FW_FAIL; + goto release_fw; + } + + p938x_dbg(chip, PR_FWPROG, "Starting FW programming...\n"); + + rc = p938x_program_mtp(chip, fw->data, fw->size); + if (rc < 0) { + p938x_err(chip, "Programming MTP failed, rc=%d\n", rc); + chip->program_fw_stat = PROGRAM_FW_FAIL; + goto release_fw; + } + + chip->program_fw_stat = PROGRAM_FW_SUCCESS; + p938x_dbg(chip, PR_IMPORTANT, "Programming FW success\n"); + +release_fw: + mutex_unlock(&chip->txmode_lock); + mutex_unlock(&chip->disconnect_lock); + release_firmware(fw); + p938x_set_boost(chip, 0); + return rc; +} + +static int p938x_get_tx_capability(struct p938x_charger *chip, int *guaranteed, + int *potential) +{ + u8 tmp; + + if (!p938x_read_reg(chip, EPP_TX_POTENTIAL, &tmp)) + *potential = 500 * tmp; + else + return 1; + + if (!p938x_read_reg(chip, EPP_TX_GUARANTEED, &tmp)) + *guaranteed = 500 * tmp; + else + return 1; + + return 0; +} + +static void p938x_check_ept_reason(struct p938x_charger *chip) +{ + u16 ept_reason = 0; + int rc; + + /* Set to disable on tx error */ + rc = p938x_read_buffer(chip, EPT_REASON_REG, (u8 *)&ept_reason, 2); + if (rc < 0) { + p938x_err(chip, + "Failed to read 0x%04x. Shutting off tx mode, rc=%d\n", + EPT_REASON_REG, rc); + p938x_set_tx_mode(chip, 0); + } + + cancel_delayed_work_sync(&chip->tx_mode_work); + + if (ept_reason & EPT_POCP) { + p938x_dbg(chip, PR_INTERRUPT, "EPT: EPT_POCP\n"); + chip->ept_pocp_count++; + if(chip->ept_pocp_count == MAX_EPT_POCP_COUNT) { + p938x_set_tx_mode(chip, 0); + p938x_err(chip, "EPT_POCP triggered %d times. Disabled tx mode.\n", + MAX_EPT_POCP_COUNT); + } else + p938x_toggle_power(chip, 1000); + } + + if (ept_reason & EPT_OTP) { + p938x_dbg(chip, PR_INTERRUPT, "EPT: EPT_OTP\n"); + p938x_set_tx_mode(chip, 0); + p938x_err(chip, "EPT_OTP triggered. Disabled tx mode.\n"); + } + + if (ept_reason & EPT_FOD) { + p938x_dbg(chip, PR_INTERRUPT, "EPT: EPT_FOD\n"); + chip->ept_fod_count++; + if(chip->ept_fod_count == MAX_EPT_FOD_COUNT) { + p938x_set_tx_mode(chip, 0); + p938x_err(chip, "EPT_FOD triggered %d times. Disabled tx mode.\n", + MAX_EPT_FOD_COUNT); + } else + p938x_toggle_power(chip, 5000); + } + + if (ept_reason & EPT_LVP) { + p938x_dbg(chip, PR_INTERRUPT, "EPT: EPT_LVP\n"); + p938x_toggle_power(chip, 1000); + } + + if (ept_reason & EPT_OVP) { + p938x_dbg(chip, PR_INTERRUPT, "EPT: EPT_OVP\n"); + p938x_toggle_power(chip, 1000); + } + + if (ept_reason & EPT_OCP) { + p938x_dbg(chip, PR_INTERRUPT, "EPT: EPT_OCP\n"); + p938x_toggle_power(chip, 2000); + } + + if (ept_reason & EPT_CMD) { + p938x_dbg(chip, PR_INTERRUPT, "EPT: EPT_CMD\n"); + p938x_toggle_power(chip, 1000); + } + + schedule_delayed_work(&chip->tx_mode_work, + msecs_to_jiffies(0)); +} + +static int p938x_check_system_mode(struct p938x_charger *chip) +{ + int rc; + int i; + u8 mode = 0; + u8 tx_mode = 0; + int tx_guaranteed; + int tx_potential; + struct p938x_limits epp_limit; + + rc = p938x_read_buffer(chip, SYS_MODE_REG, &mode, 1); + if (rc < 0) + return rc; + + rc = p938x_read_reg(chip, SYS_TM_MODE_REG, &tx_mode); + if (rc < 0) + return rc; + + p938x_dbg(chip, PR_MOTO, "MODE=0x%02x\n", mode); + + if (mode & SYS_MODE_RAMCODE) + p938x_dbg(chip, PR_INTERRUPT, "MODE: SYS_MODE_RAMCODE\n"); + if (mode & SYS_MODE_TX) { + p938x_dbg(chip, PR_INTERRUPT, "MODE: SYS_MODE_TXMODE\n"); + p938x_configure_tx_mode(chip); + } + if (mode & SYS_MODE_WPCMODE) { + p938x_dbg(chip, PR_INTERRUPT, "MODE: SYS_MODE_WPCMODE\n"); + if (mode & SYS_MODE_EXTENDED) { + p938x_dbg(chip, PR_INTERRUPT, "MODE: SYS_MODE_EXTENDED\n"); + chip->epp_mode = true; + + if (chip->epp_limit_num && + !p938x_get_tx_capability(chip, &tx_guaranteed, &tx_potential)) { + p938x_dbg(chip, PR_INTERRUPT, "EPP GUARANTEED %dmW, POTENTIAL %dmW\n", + tx_guaranteed, tx_potential); + + /* Default value is the lowest setting, start at the highest setting + * and go backwards, if we find one more appropriate use that one + * instead + */ + epp_limit = chip->epp_current_limits[0]; + for(i=chip->epp_limit_num-1; i >= 0; i--) { + if(tx_guaranteed >= chip->epp_current_limits[i].tx_capability_mw) { + epp_limit = chip->epp_current_limits[i]; + break; + } + } + + p938x_dbg(chip, PR_INTERRUPT, "EPP LIMIT is %d mW -> %d mA\n", + epp_limit.tx_capability_mw, epp_limit.idc_max_ma); + + p938x_set_dc_max_icl_ma(chip, epp_limit.idc_max_ma); + } else + p938x_set_dc_max_icl_ma(chip, EPP_MAX_IDC); + + p938x_set_rx_vout(chip, chip->epp_voltage); + p938x_set_rx_ocl(chip, EPP_MAX_IOUT); + p938x_program_fod(chip, chip->fod_array_epp, + chip->fod_array_epp_len); + } else { + chip->epp_mode = false; + p938x_set_dc_max_icl_ma(chip, chip->bpp_current_limit); + p938x_set_rx_vout(chip, chip->bpp_voltage); + p938x_set_rx_ocl(chip, BPP_MAX_IOUT); + p938x_program_fod(chip, chip->fod_array_bpp, + chip->fod_array_bpp_len); + } + + /* Override if set */ + if(chip->wls_vout_max) + p938x_set_rx_vout(chip, chip->wls_vout_max); + if(chip->wls_iout_max) + p938x_set_rx_ocl(chip, chip->wls_iout_max); + if(chip->wls_idc_max) + p938x_set_dc_max_icl_ma(chip, chip->wls_idc_max); + } + + return 0; +} + +static int p938x_get_status(struct p938x_charger *chip, u16 *stat, u16 *irq_stat) +{ + int rc; + u16 irq_en; + u32 stat_and_irq_stat; + + rc = p938x_read_buffer(chip, IRQ_ENABLE_REG, (u8 *)&irq_en, 2); + if (rc < 0) + return rc; + + /* Read both DEV_STATUS_REG and IRQ_STATUS_REG together */ + rc = p938x_read_buffer(chip, DEV_STATUS_REG, (u8 *)&stat_and_irq_stat, 4); + if (rc < 0) + return rc; + + p938x_dbg(chip, PR_MOTO, "IRQ_ENABLE=0x%04x, IRQ_ST=0x%04x, STATUS=0x%04x\n", + irq_en, ((u16 *)(&stat_and_irq_stat))[1], ((u16 *)(&stat_and_irq_stat))[0]); + + if(stat) + *stat = ((u16 *)(&stat_and_irq_stat))[0]; + if(irq_stat) + *irq_stat = ((u16 *)(&stat_and_irq_stat))[1]; + + return rc; +} + +static void p938x_debug_status(struct p938x_charger *chip, u16 status) +{ + if (status & ST_TX_CONFLICT) + p938x_dbg(chip, PR_INTERRUPT, "STATUS: ST_TX_CONFLICT\n"); + if (status & ST_RX_CONN) + p938x_dbg(chip, PR_INTERRUPT, "STATUS: ST_RX_CONN\n"); + if (status & ST_EPT_TYPE_INT) + p938x_dbg(chip, PR_INTERRUPT, "STATUS: ST_EPT_TYPE_INT\n"); + if (status & ST_ADT_ERR) + p938x_dbg(chip, PR_INTERRUPT, "STATUS: ST_ADT_ERR\n"); + if (status & ST_ADT_RCV) + p938x_dbg(chip, PR_INTERRUPT, "STATUS: ST_ADT_RCV\n"); + if (status & ST_ADT_SENT) + p938x_dbg(chip, PR_INTERRUPT, "STATUS: ST_ADT_SENT\n"); + if (status & ST_VOUT_ON) + p938x_dbg(chip, PR_INTERRUPT, "STATUS: ST_VOUT_ON\n"); + if (status & ST_VRECT_ON) + p938x_dbg(chip, PR_INTERRUPT, "STATUS: ST_VRECT_ON\n"); + if (status & ST_MODE_CHANGE) + p938x_dbg(chip, PR_INTERRUPT, "STATUS: ST_MODE_CHANGE\n"); + if (status & ST_OVER_TEMP) + p938x_dbg(chip, PR_INTERRUPT, "STATUS: ST_OVER_TEMP\n"); + if (status & ST_OVER_VOLT) + p938x_dbg(chip, PR_INTERRUPT, "STATUS: ST_OVER_VOLT\n"); + if (status & ST_OVER_CURR) + p938x_dbg(chip, PR_INTERRUPT, "STATUS: ST_OVER_CURR\n"); +} + +static void p938x_debug_irq(struct p938x_charger *chip, u16 irq_status) +{ + if (irq_status & ST_TX_CONFLICT) + p938x_dbg(chip, PR_INTERRUPT, "IRQ: ST_TX_CONFLICT\n"); + if (irq_status & ST_RX_CONN) + p938x_dbg(chip, PR_INTERRUPT, "IRQ: ST_RX_CONN\n"); + if (irq_status & ST_EPT_TYPE_INT) + p938x_dbg(chip, PR_INTERRUPT, "IRQ: ST_EPT_TYPE_INT\n"); + if (irq_status & ST_ADT_ERR) + p938x_dbg(chip, PR_INTERRUPT, "IRQ: ST_ADT_ERR\n"); + if (irq_status & ST_ADT_RCV) + p938x_dbg(chip, PR_INTERRUPT, "IRQ: ST_ADT_RCV\n"); + if (irq_status & ST_ADT_SENT) + p938x_dbg(chip, PR_INTERRUPT, "IRQ: ST_ADT_SENT\n"); + if (irq_status & ST_VOUT_ON) + p938x_dbg(chip, PR_INTERRUPT, "IRQ: ST_VOUT_ON\n"); + if (irq_status & ST_VRECT_ON) + p938x_dbg(chip, PR_INTERRUPT, "IRQ: ST_VRECT_ON\n"); + if (irq_status & ST_MODE_CHANGE) + p938x_dbg(chip, PR_INTERRUPT, "IRQ: ST_MODE_CHANGE\n"); + if (irq_status & ST_OVER_TEMP) + p938x_dbg(chip, PR_INTERRUPT, "IRQ: ST_OVER_TEMP\n"); + if (irq_status & ST_OVER_VOLT) + p938x_dbg(chip, PR_INTERRUPT, "IRQ: ST_OVER_VOLT\n"); + if (irq_status & ST_OVER_CURR) + p938x_dbg(chip, PR_INTERRUPT, "IRQ: ST_OVER_CURR\n"); +} + +static int p938x_check_status(struct p938x_charger *chip) +{ + u16 status; + u16 irq_status; + + if (p938x_get_status(chip, &status, &irq_status)) { + p938x_dbg(chip, PR_MOTO, "Could not read status registers"); + return 1; + } + + chip->stat = status; + chip->irq_stat = irq_status; + + p938x_debug_status(chip, status); + p938x_debug_irq(chip, irq_status); + + if (irq_status & status & ST_TX_CONFLICT) { + p938x_set_tx_mode(chip, 0); + p938x_dbg(chip, PR_IMPORTANT, "Tx mode conflict. Disabled tx mode\n"); + } + + if ((irq_status & ST_EPT_TYPE_INT) && + test_bit(WLS_FLAG_TX_MODE_EN, &chip->flags)) + p938x_check_ept_reason(chip); + + if (test_bit(WLS_FLAG_TX_MODE_EN, &chip->flags)) { + sysfs_notify(&chip->dev->kobj, NULL, "rx_connected"); + } + + if ((irq_status & status & ST_VRECT_ON) || + (!test_bit(WLS_FLAG_TX_ATTACHED, &chip->flags) && (status & ST_VRECT_ON))) { + if (test_bit(WLS_FLAG_TX_MODE_EN, &chip->flags)) { + p938x_dbg(chip, PR_IMPORTANT, "Connected to tx pad. Disabling tx mode\n"); + p938x_set_tx_mode(chip, 0); + } + + if (chip->use_q_factor) + p938x_write_reg(chip, EPP_QFACTOR_REG, chip->q_factor); + + set_bit(WLS_FLAG_TX_ATTACHED, &chip->flags); + /* Call p938x_pm_set_awake before update supplies status. Update supplies + * status will turn it back off if we have a cable attached + */ + p938x_pm_set_awake(chip, 1); + /* Update usb status incase we powered on with it connected */ + p938x_update_supplies_status(chip); + + cancel_delayed_work(&chip->heartbeat_work); + /* The power_supply_changed call is in the heartbeat. This delay is how long + * we wait for a disconnect before changing the state to charging. Without this + * we might report we are charging even if the connection is not stable enough + * to charge + */ + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(1500)); + p938x_dbg(chip, PR_IMPORTANT, "Wireless charger is inserted\n"); + } + + if (irq_status & status & ST_VOUT_ON) { + p938x_dbg(chip, PR_IMPORTANT, "Wireless charger ldo is on\n"); + p938x_check_system_mode(chip); + cancel_delayed_work(&chip->heartbeat_work); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(0)); + power_supply_changed(chip->wls_psy); + } + + if ((irq_status & ST_MODE_CHANGE) && + test_bit(WLS_FLAG_TX_MODE_EN, &chip->flags)) { + p938x_dbg(chip, PR_IMPORTANT, "Wireless mode changed\n"); + if(chip->tx_mode_recover_cnt >= MAX_TX_MODE_RECOVER) { + p938x_err(chip, "Tx mode recovered too many times. Turning off.\n"); + p938x_set_tx_mode(chip, 0); + } else + p938x_check_system_mode(chip); + } + + p938x_clear_irq(chip, irq_status); + + return 0; +} + +/* Sometimes the tx mode turns off on its own... make sure the user doesn't + * notice + */ + +static void p938x_tx_mode_work(struct work_struct *work) +{ + u8 buf; + int rc; + struct p938x_charger *chip = container_of(work, + struct p938x_charger, tx_mode_work.work); + + if (test_bit(WLS_FLAG_TX_MODE_EN, &chip->flags)) { + rc = p938x_read_reg(chip, SYS_TM_MODE_REG, &buf); + if (rc >= 0) { + if (buf != ST_TM_MODE_EN) { + p938x_dbg(chip, PR_INTERRUPT, + "Tx mode is off and it should be on, turning back on.\n"); + chip->tx_mode_recover_cnt++; + + rc = p938x_write_reg(chip, SYS_TM_MODE_REG, + ST_TM_MODE_EN); + if (rc < 0) { + p938x_err(chip, "Failed to write 0x%04x(%d), rc=%d\n", + SYS_TM_MODE_REG, ST_TM_MODE_EN, rc); + } + } + } else { + /* If we are in tx mode and we can't read the register, something is wrong + * so force a reset + */ + p938x_err(chip, "Something went wrong, resetting IC..."); + p938x_toggle_power(chip, 0); + } + + sysfs_notify(&chip->dev->kobj, NULL, "rx_connected"); + + schedule_delayed_work(&chip->tx_mode_work, + msecs_to_jiffies(TXMODEWORK_INTERVAL_MS)); + } +} + +static irqreturn_t p938x_irq_handler(int irq, void *dev_id) +{ + struct p938x_charger *chip = dev_id; + + if (!chip) { + p938x_err(chip, "Invalid chip\n"); + return IRQ_HANDLED; + } + + if (chip->program_fw_stat == PROGRAM_FW_PENDING) { + p938x_err(chip, "Skip irq for FW programming\n"); + return IRQ_HANDLED; + } + + mutex_lock(&chip->disconnect_lock); + + if (p938x_check_status(chip)) { + p938x_err(chip, + "Error checking status. Check charging pad alignment\n"); + chip->error_cnt++; + sysfs_notify(&chip->dev->kobj, NULL, "error_cnt"); + } + + mutex_unlock(&chip->disconnect_lock); + + return IRQ_HANDLED; +} + +static irqreturn_t p938x_det_irq_handler(int irq, void *dev_id) +{ + struct p938x_charger *chip = dev_id; + int tx_detected = gpio_get_value(chip->wchg_det.gpio); + + if (tx_detected) { + p938x_dbg(chip, PR_IMPORTANT, "Detected an attach event.\n"); + /* Force dcin-en on */ + p938x_set_dc_en_override(chip, 2); + } else { + p938x_dbg(chip, PR_IMPORTANT, "Detected a detach event.\n"); + p938x_handle_wls_removal(chip); + } + + return IRQ_HANDLED; +} + +/* Only allow usb_keep_on and boost in userdebug builds */ +#ifdef P938X_USER_DEBUG +static ssize_t usb_keep_on_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long value; + struct p938x_charger *chip = dev_get_drvdata(dev); + + r = kstrtoul(buf, 0, &value); + if (r) { + p938x_err(chip, "Invalid usb keep on value = %ld\n", value); + return -EINVAL; + } + + if (value) { + set_bit(WLS_FLAG_USB_KEEP_ON, &chip->flags); + if (test_bit(WLS_FLAG_USB_CONNECTED, &chip->flags)) + gpio_set_value(chip->wchg_en_n.gpio, 0); + } else { + clear_bit(WLS_FLAG_USB_KEEP_ON, &chip->flags); + if (test_bit(WLS_FLAG_USB_CONNECTED, &chip->flags) && + test_bit(WLS_FLAG_TX_ATTACHED, &chip->flags)) + gpio_set_value(chip->wchg_en_n.gpio, 1); + } + + return r ? r : count; +} + +static ssize_t usb_keep_on_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct p938x_charger *chip = dev_get_drvdata(dev); + + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "%d\n", + test_bit(WLS_FLAG_USB_KEEP_ON, &chip->flags)); +} + +static ssize_t boost_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long value; + struct p938x_charger *chip = dev_get_drvdata(dev); + + r = kstrtoul(buf, 0, &value); + if (r) { + p938x_err(chip, "Invalid boost value = %ld\n", value); + return -EINVAL; + } + + p938x_set_boost(chip, value); + + return r ? r : count; +} + +static ssize_t boost_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct p938x_charger *chip = dev_get_drvdata(dev); + + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "%d\n", + p938x_get_boost(chip)); +} + +static ssize_t force_idc_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long value; + struct p938x_charger *chip = dev_get_drvdata(dev); + + r = kstrtoul(buf, 0, &value); + if (r) { + p938x_err(chip, "Invalid idc value = %ld\n", value); + return -EINVAL; + } + + /* Store to persist change */ + chip->wls_idc_max = value; + + p938x_set_dc_now_icl_ma(chip, value); + p938x_set_dc_max_icl_ma(chip, value); + + return r ? r : count; +} + +static ssize_t force_idc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int rc; + union power_supply_propval prop = {0,}; + struct p938x_charger *chip = dev_get_drvdata(dev); + + rc = power_supply_get_property(chip->dc_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, &prop); + if (rc) { + p938x_err(chip, "Couldn't read IDC prop, rc=%d\n", rc); + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "Unknown\n"); + } + + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "%d\n", + prop.intval / 1000); +} + +static int fod_store(struct p938x_charger *chip, const char *buf, + u8 *fod_array, u32 *fod_array_len) +{ + int i = 0, ret = 0, sum = 0; + char *buffer; + unsigned int temp; + u8 fod_array_temp[FOD_MAX_LEN]; + + buffer = (char *)buf; + + for (i = 0; i < FOD_MAX_LEN; i++) { + ret = sscanf((const char *)buffer, "%x,%s", &temp, buffer); + fod_array_temp[i] = temp; + sum++; + if (ret != 2) + break; + } + + if (sum > 0 && sum <= FOD_MAX_LEN) { + memcpy(fod_array, fod_array_temp, sum); + *fod_array_len = sum; + p938x_program_fod(chip, fod_array, *fod_array_len); + } + + return sum; +} + +static ssize_t fod_epp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct p938x_charger *chip = dev_get_drvdata(dev); + + fod_store(chip, buf, chip->fod_array_epp, + &chip->fod_array_epp_len); + + return count; +} + +static ssize_t fod_bpp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct p938x_charger *chip = dev_get_drvdata(dev); + + fod_store(chip, buf, chip->fod_array_bpp, + &chip->fod_array_bpp_len); + + return count; +} + +static ssize_t fod_tx_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct p938x_charger *chip = dev_get_drvdata(dev); + int i = 0, ret = 0, sum = 0; + char *buffer; + unsigned int temp; + u8 fod_array_temp[TX_FOD_WRITE_LEN]; + + buffer = (char *)buf; + + for (i = 0; i < TX_FOD_WRITE_LEN; i++) { + ret = sscanf((const char *)buffer, "%x,%s", &temp, buffer); + fod_array_temp[i] = temp; + sum++; + if (ret != 2) + break; + } + + if (sum > 0 && sum <= TX_FOD_WRITE_LEN) { + memcpy(chip->fod_array_tx, fod_array_temp, sum); + chip->fod_array_tx_len = sum; + p938x_program_tx_fod(chip, chip->fod_array_tx, chip->fod_array_tx_len); + } + + return count; +} + +static ssize_t fod_epp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int count = 0, i = 0; + struct p938x_charger *chip = dev_get_drvdata(dev); + + for (i = 0; i < chip->fod_array_epp_len; i++) { + count += scnprintf(buf+count, WLS_SHOW_MAX_SIZE, + "0x%02x ", chip->fod_array_epp[i]); + if (i == chip->fod_array_epp_len - 1) + count += scnprintf(buf+count, WLS_SHOW_MAX_SIZE, "\n"); + } + + return count; +} + +static ssize_t fod_bpp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int count = 0, i = 0; + struct p938x_charger *chip = dev_get_drvdata(dev); + + for (i = 0; i < chip->fod_array_bpp_len; i++) { + count += scnprintf(buf+count, WLS_SHOW_MAX_SIZE, + "0x%02x ", chip->fod_array_bpp[i]); + if (i == chip->fod_array_bpp_len - 1) + count += scnprintf(buf+count, WLS_SHOW_MAX_SIZE, "\n"); + } + + return count; +} + +static ssize_t fod_tx_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int count = 0, i = 0; + struct p938x_charger *chip = dev_get_drvdata(dev); + + for (i = 0; i < chip->fod_array_tx_len; i++) { + count += scnprintf(buf+count, WLS_SHOW_MAX_SIZE, + "0x%02x ", chip->fod_array_tx[i]); + if (i == chip->fod_array_tx_len - 1) + count += scnprintf(buf+count, WLS_SHOW_MAX_SIZE, "\n"); + } + + return count; +} + +static ssize_t q_factor_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long value; + struct p938x_charger *chip = dev_get_drvdata(dev); + + r = kstrtoul(buf, 0, &value); + if (r) { + p938x_err(chip, "Invalid q factor value = %ld\n", value); + return -EINVAL; + } + + chip->q_factor = value; + chip->use_q_factor = true; + + return r ? r : count; +} + +static ssize_t q_factor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct p938x_charger *chip = dev_get_drvdata(dev); + + if (chip->use_q_factor) + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "0x%02X\n", + chip->q_factor); + else + return scnprintf(buf, WLS_SHOW_MAX_SIZE, + "Using default\n"); +} +#endif + +static ssize_t tx_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long value; + struct p938x_charger *chip = dev_get_drvdata(dev); + + r = kstrtoul(buf, 0, &value); + if (r) { + p938x_err(chip, "Invalid boost value = %ld\n", value); + return -EINVAL; + } + + p938x_set_tx_mode(chip, value); + + return r ? r : count; +} + +static ssize_t tx_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct p938x_charger *chip = dev_get_drvdata(dev); + + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "%d\n", + test_bit(WLS_FLAG_TX_MODE_EN, &chip->flags)); +} + +static ssize_t chip_id_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "0x%04x\n", + MAX_CHIP_VERS); +} + +static ssize_t chip_id_min_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "0x%04x\n", + MIN_CHIP_VERS); +} + +static ssize_t chip_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "%s\n", + CHIP_VENDOR); +} + +static ssize_t chip_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct p938x_charger *chip = dev_get_drvdata(dev); + int turn_off = 0; + u8 data[2]; + + if(!p938x_is_chip_on(chip)) { + turn_off = 1; + p938x_set_boost(chip, 1); + msleep(100); + } + + p938x_read_buffer(chip, CHIP_ID_REG, data, 2); + + if(turn_off) + p938x_set_boost(chip, 0); + + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "0x%02x%02x\n", + data[1], data[0]); +} + +static ssize_t fw_ver_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct p938x_charger *chip = dev_get_drvdata(dev); + int turn_off = 0; + u16 minor_vers = 0x00; + u16 major_vers = 0x00; + + if(!p938x_is_chip_on(chip)) { + turn_off = 1; + p938x_set_boost(chip, 1); + msleep(100); + } + + p938x_read_buffer(chip, MTP_FW_MAJ_VER_REG, (u8 *)&major_vers, 2); + p938x_read_buffer(chip, MTP_FW_MIN_VER_REG, (u8 *)&minor_vers, 2); + + if(turn_off) + p938x_set_boost(chip, 0); + + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "%04x%04x\n", + major_vers, minor_vers); +} + +static ssize_t fw_name_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct p938x_charger *chip = dev_get_drvdata(dev); + + if(count && strlen(buf)) { + strncpy(chip->fw_name, buf, NAME_MAX); + if(chip->fw_name[strlen(chip->fw_name)-1] == '\n') + chip->fw_name[strlen(chip->fw_name)-1] = '\0'; + } + + return count; +} + +static ssize_t program_fw_stat_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct p938x_charger *chip = dev_get_drvdata(dev); + + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "%d\n", chip->program_fw_stat); +} + +static ssize_t program_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + unsigned long r; + unsigned long flash; + struct p938x_charger *chip = dev_get_drvdata(dev); + + r = kstrtoul(buf, 0, &flash); + if (r) { + p938x_err(chip, "Invalid flash value = %ld\n", flash); + return -EINVAL; + } + + if (flash) { + rc = p938x_program_fw(chip); + if (rc < 0) + return rc; + } + return r ? r : count; +} + +static ssize_t tx_capability_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct p938x_charger *chip = dev_get_drvdata(dev); + int tx_potential; + int tx_guaranteed; + + if (!test_bit(WLS_FLAG_TX_ATTACHED, &chip->flags)) + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "No charger attached\n"); + + if (!p938x_is_ldo_on(chip)) + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "Unknown\n"); + + if (chip->epp_mode) { + if (!p938x_get_tx_capability(chip, &tx_guaranteed, &tx_potential)) + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "EPP %dmW (Potential %dmW)\n", + tx_guaranteed, tx_potential); + else + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "EPP Unknown\n"); + } else + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "BPP\n"); +} + +static ssize_t rx_connected_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct p938x_charger *chip = dev_get_drvdata(dev); + int rx_connected; + + if (test_bit(WLS_FLAG_OVERHEAT, &chip->flags)) + rx_connected = TX_MODE_OVERHEAT; + else if (test_bit(WLS_FLAG_TX_MODE_EN, &chip->flags) && + (chip->stat & ST_RX_CONN) && + p938x_get_rx_iout(chip)) + rx_connected = TX_MODE_POWER_SHARE; + else + rx_connected = TX_MODE_NOT_CONNECTED; + + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "%d\n", + rx_connected); +} + +static ssize_t error_cnt_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct p938x_charger *chip = dev_get_drvdata(dev); + + return scnprintf(buf, WLS_SHOW_MAX_SIZE, "%llu\n", + chip->error_cnt); +} + +#ifdef P938X_USER_DEBUG +static DEVICE_ATTR(usb_keep_on, S_IRUGO|S_IWUSR, usb_keep_on_show, usb_keep_on_store); +static DEVICE_ATTR(boost, S_IRUGO|S_IWUSR, boost_show, boost_store); +static DEVICE_ATTR(force_idc, S_IRUGO|S_IWUSR, force_idc_show, force_idc_store); +static DEVICE_ATTR(fod_epp, S_IRUGO|S_IWUSR, fod_epp_show, fod_epp_store); +static DEVICE_ATTR(fod_bpp, S_IRUGO|S_IWUSR, fod_bpp_show, fod_bpp_store); +static DEVICE_ATTR(fod_tx, S_IRUGO|S_IWUSR, fod_tx_show, fod_tx_store); +static DEVICE_ATTR(q_factor, S_IRUGO|S_IWUSR, q_factor_show, q_factor_store); +#endif +static DEVICE_ATTR(tx_mode, S_IRUGO|S_IWUSR, tx_mode_show, tx_mode_store); +static DEVICE_ATTR(chip_id, S_IRUGO, chip_id_show, NULL); +static DEVICE_ATTR(vendor, S_IRUGO, chip_vendor_show, NULL); +static DEVICE_ATTR(chip_id_max, S_IRUGO, chip_id_max_show, NULL); +static DEVICE_ATTR(chip_id_min, S_IRUGO, chip_id_min_show, NULL); +static DEVICE_ATTR(fw_ver, S_IRUGO, fw_ver_show, NULL); +static DEVICE_ATTR(fw_name, S_IWUSR, NULL, fw_name_store); +static DEVICE_ATTR(program_fw_stat, S_IRUGO, program_fw_stat_show, NULL); +static DEVICE_ATTR(program_fw, S_IWUSR, NULL, program_fw_store); +static DEVICE_ATTR(tx_capability, S_IRUGO, tx_capability_show, NULL); +static DEVICE_ATTR(rx_connected, S_IRUGO, rx_connected_show, NULL); +static DEVICE_ATTR(error_cnt, S_IRUGO, error_cnt_show, NULL); + +static struct attribute *p938x_attrs[] = { +#ifdef P938X_USER_DEBUG + &dev_attr_usb_keep_on.attr, + &dev_attr_boost.attr, + &dev_attr_force_idc.attr, + &dev_attr_fod_epp.attr, + &dev_attr_fod_bpp.attr, + &dev_attr_fod_tx.attr, + &dev_attr_q_factor.attr, +#endif + &dev_attr_tx_mode.attr, + &dev_attr_chip_id_max.attr, + &dev_attr_chip_id_min.attr, + &dev_attr_chip_id.attr, + &dev_attr_vendor.attr, + &dev_attr_fw_ver.attr, + &dev_attr_fw_name.attr, + &dev_attr_program_fw_stat.attr, + &dev_attr_program_fw.attr, + &dev_attr_tx_capability.attr, + &dev_attr_rx_connected.attr, + &dev_attr_error_cnt.attr, + NULL +}; + +ATTRIBUTE_GROUPS(p938x); + +static void show_dump_flags(struct seq_file *m, + struct p938x_charger *chip) +{ + seq_printf(m, "WLS_FLAG_BOOST_ENABLED: %d\n", + test_bit(WLS_FLAG_BOOST_ENABLED, &chip->flags)); + seq_printf(m, "WLS_FLAG_KEEP_AWAKE: %d\n", + test_bit(WLS_FLAG_KEEP_AWAKE, &chip->flags)); + seq_printf(m, "WLS_FLAG_TX_ATTACHED: %d\n", + test_bit(WLS_FLAG_TX_ATTACHED, &chip->flags)); + seq_printf(m, "WLS_FLAG_TX_MODE_EN: %d\n", + test_bit(WLS_FLAG_TX_MODE_EN, &chip->flags)); + seq_printf(m, "WLS_FLAG_USB_CONNECTED: %d\n", + test_bit(WLS_FLAG_USB_CONNECTED, &chip->flags)); + seq_printf(m, "WLS_FLAG_USB_KEEP_ON: %d\n", + test_bit(WLS_FLAG_USB_KEEP_ON, &chip->flags)); + seq_printf(m, "WLS_FLAG_OVERHEAT: %d\n", + test_bit(WLS_FLAG_OVERHEAT, &chip->flags)); +} + +static int show_dump_regs(struct seq_file *m, void *data) +{ + struct p938x_charger *chip = m->private; + u8 buf[16]; + + p938x_read_buffer(chip, CHIP_ID_REG, buf, 2); + seq_printf(m, "CHIP_ID: 0x%02x%02x\n", buf[1], buf[0]); + p938x_read_reg(chip, HW_VER_REG, &buf[0]); + seq_printf(m, "HW_VER: 0x%02x\n", buf[0]); + p938x_read_reg(chip, CUST_ID_REG, &buf[0]); + seq_printf(m, "CUST_ID: 0x%02x\n", buf[0]); + p938x_read_buffer(chip, MTP_FW_MAJ_VER_REG, buf, 4); + seq_printf(m, "MTP_FW_VER: 0x%02x%02x:0x%02x%02x\n", + buf[0], buf[1], buf[2], buf[3]); + p938x_read_buffer(chip, MTP_FW_DATE_REG, buf, 12); + seq_printf(m, "MTP_FW_DATE: %s\n", buf); + p938x_read_buffer(chip, E2PROM_FW_VER_REG, buf, 4); + seq_printf(m, "E2PROM_FW_VER: 0x%02x%02x:0x%02x%02x\n", + buf[0], buf[1], buf[2], buf[3]); + p938x_read_buffer(chip, DEV_STATUS_REG, buf, 2); + seq_printf(m, "DEV STATUS: 0x%02x%02x\n", buf[1], buf[0]); + p938x_read_buffer(chip, IRQ_STATUS_REG, buf, 2); + seq_printf(m, "IRQ STATUS: 0x%02x%02x\n", buf[1], buf[0]); + p938x_read_buffer(chip, IRQ_ENABLE_REG, buf, 2); + seq_printf(m, "IRQ ENABLE: 0x%02x%02x\n", buf[1], buf[0]); + p938x_read_buffer(chip, IRQ_CLEAR_REG, buf, 2); + seq_printf(m, "IRQ CLEAR: 0x%02x%02x\n", buf[1], buf[0]); + p938x_read_reg(chip, SYS_CMD_REG, &buf[0]); + seq_printf(m, "CMD_REG: 0x%02x\n", buf[0]); + p938x_read_buffer(chip, VOUT_READ_REG, buf, 2); + seq_printf(m, "VOUT: %dmV\n", (buf[1] << 8) | buf[0]); + p938x_read_reg(chip, VOUT_SET_REG, &buf[0]); + seq_printf(m, "VOUT SET: %dmV\n", buf[0] * 100); + p938x_read_buffer(chip, VRECT_READ_REG, buf, 2); + seq_printf(m, "VRECT: %dmV\n", (buf[1] << 8) | buf[0]); + p938x_read_buffer(chip, IOUT_READ_REG, buf, 2); + seq_printf(m, "IOUT: %dmA\n", (buf[1] << 8) | buf[0]); + p938x_read_buffer(chip, DIE_TEMP_REG, buf, 2); + /* TODO conv is NOT right. */ + seq_printf(m, "DIE TEMP: %d %dC\n",((buf[1] << 8)| buf[0]), + (((buf[1] << 8)| buf[0]) - 1350) * 83 / 444 - 273); + p938x_read_buffer(chip, OPT_FREQ_REG, buf, 2); + seq_printf(m, "OPT FREQ: %dkHz\n", (buf[1] << 8) | buf[0]); + p938x_read_buffer(chip, ILIMIT_SET_REG, buf, 1); + seq_printf(m, "ILIMIT_SET: %dmA\n", buf[0] * 100 + 100); + p938x_read_reg(chip, SYS_MODE_REG, &buf[0]); + seq_printf(m, "SYS_MODE: 0x%02x\n", buf[0]); + p938x_read_reg(chip, SYS_TM_MODE_REG, &buf[0]); + seq_printf(m, "TX_MODE: %d\n", buf[0]); + p938x_read_reg(chip, EPP_TX_GUARANTEED, &buf[0]); + seq_printf(m, "EPP_TX_GUARANTEED: 0x%02x\n", buf[0]); + p938x_read_reg(chip, EPP_TX_POTENTIAL, &buf[0]); + seq_printf(m, "EPP_TX_POTENTIAL: 0x%02x\n", buf[0]); + p938x_read_reg(chip, EPP_QFACTOR_REG, &buf[0]); + seq_printf(m, "EPP_QFACTOR_REG: 0x%02x\n", buf[0]); + p938x_read_buffer(chip, FOD_CFG_REG, buf, FOD_MAX_LEN); + seq_printf(m, "FOD_CFG_REG: %02X%02X %02X%02X " + "%02X%02X %02X%02X " + "%02X%02X %02X%02X " + "%02X%02X %02X%02X\n", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], + buf[12], buf[13], buf[14], buf[15]); + p938x_read_buffer(chip, TX_FOD_CFG_REG, buf, TX_FOD_REG_LEN); + seq_printf(m, "TX_FOD_CFG_REG: %02X%02X %02X%02X " + "%02X%02X %02X%02X " + "%02X%02X %02X " + "%02X%02X\n", + buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], + buf[12]); + + show_dump_flags(m, chip); + + return 0; +} + +static int dump_regs_debugfs_open(struct inode *inode, struct file *file) +{ + struct p938x_charger *chip = inode->i_private; + + return single_open(file, show_dump_regs, chip); +} + +static const struct file_operations dump_regs_debugfs_ops = { + .owner = THIS_MODULE, + .open = dump_regs_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int get_reg(void *data, u64 *val) +{ + struct p938x_charger *chip = data; + int rc; + u8 temp; + + rc = p938x_read_reg(chip, chip->peek_poke_address, &temp); + if (rc) { + p938x_err(chip, "Couldn't read reg %x rc = %d\n", + chip->peek_poke_address, rc); + return -EAGAIN; + } + *val = temp; + return 0; +} + +static int set_reg(void *data, u64 val) +{ + struct p938x_charger *chip = data; + int rc; + u8 temp; + + temp = (u8) val; + rc = p938x_write_reg(chip, chip->peek_poke_address, temp); + if (rc) { + p938x_err(chip, "Couldn't write 0x%02x to 0x%02x rc= %d\n", + temp, chip->peek_poke_address, rc); + return -EAGAIN; + } + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(poke_poke_debug_ops, get_reg, set_reg, "0x%02llx\n"); + +static void create_debugfs_entries(struct p938x_charger *chip) +{ + struct dentry *ent; + + chip->debug_root = debugfs_create_dir("p938x", NULL); + if (!chip->debug_root) { + p938x_err(chip, "Couldn't create debug dir\n"); + return; + } + + ent = debugfs_create_file("dump_regs", S_IFREG | S_IRUGO, + chip->debug_root, chip, + &dump_regs_debugfs_ops); + if (!ent) + p938x_err(chip, "Couldn't create dump_regs debug file\n"); + + ent = debugfs_create_x32("address", S_IFREG | S_IWUSR | S_IRUGO, + chip->debug_root, + &(chip->peek_poke_address)); + if (!ent) + p938x_err(chip, "Couldn't create address debug file\n"); + + ent = debugfs_create_file("data", S_IFREG | S_IWUSR | S_IRUGO, + chip->debug_root, chip, + &poke_poke_debug_ops); + if (!ent) + p938x_err(chip, "Couldn't create data debug file\n"); +} + +static int p938x_parse_gpio(struct device_node *node, struct gpio *gpio, int idx) +{ + gpio->gpio = of_get_gpio_flags(node, idx, + (enum of_gpio_flags *)&gpio->flags); + of_property_read_string_index(node, "gpio-names", idx, + &gpio->label); + + if (!gpio_is_valid(gpio->gpio)) + return 1; + + return 0; +} + +static int p938x_parse_dt(struct p938x_charger *chip) +{ + struct device_node *node = chip->dev->of_node; + int rc; + unsigned tmp; + int byte_len; + int i; + + if (!node) { + p938x_err(chip, "device tree info. missing\n"); + return -EINVAL; + } + + if (of_find_property(node, "vdd-i2c-supply", NULL)) { + chip->vdd_i2c_vreg = devm_regulator_get(chip->dev, "vdd-i2c"); + if (IS_ERR(chip->vdd_i2c_vreg)) + return PTR_ERR(chip->vdd_i2c_vreg); + } + + if (of_gpio_count(node) < 0) { + p938x_err(chip, "No GPIOS defined.\n"); + return -EINVAL; + } + + if (p938x_parse_gpio(node, &chip->wchg_int_n, 0)) { + p938x_err(chip, "Invalid gpio wchg_int_n=%d\n", + chip->wchg_int_n.gpio); + return -EINVAL; + } + + if (p938x_parse_gpio(node, &chip->wchg_en_n, 1)) { + p938x_err(chip, "Invalid gpio wchg_en_n=%d\n", + chip->wchg_int_n.gpio); + return -EINVAL; + } + + if (p938x_parse_gpio(node, &chip->wchg_det, 2)) { + p938x_err(chip, "Invalid gpio wchg_det=%d\n", + chip->wchg_int_n.gpio); + return -EINVAL; + } + + if (p938x_parse_gpio(node, &chip->wchg_sleep, 3)) { + p938x_err(chip, "Invalid gpio wchg_sleep=%d\n", + chip->wchg_int_n.gpio); + return -EINVAL; + } + + if (p938x_parse_gpio(node, &chip->wchg_boost, 4)) { + p938x_err(chip, "Invalid gpio wchg_boost=%d\n", + chip->wchg_int_n.gpio); + return -EINVAL; + } + + chip->pinctrl_name = of_get_property(chip->dev->of_node, + "pinctrl-names", NULL); + + chip->fod_array_bpp_len = + of_property_count_u8_elems(node, + "fod-array-bpp-val"); + + chip->fod_array_epp_len = + of_property_count_u8_elems(node, + "fod-array-epp-val"); + + chip->fod_array_tx_len = + of_property_count_u8_elems(node, + "fod-array-tx-val"); + + of_property_read_u8_array(chip->dev->of_node, "fod-array-bpp-val", + chip->fod_array_bpp, + chip->fod_array_bpp_len); + of_property_read_u8_array(chip->dev->of_node, "fod-array-epp-val", + chip->fod_array_epp, + chip->fod_array_epp_len); + of_property_read_u8_array(chip->dev->of_node, "fod-array-tx-val", + chip->fod_array_tx, + chip->fod_array_tx_len); + + rc = of_property_read_u32(node, "epp-q-factor", + &tmp); + if (!rc) { + chip->use_q_factor = true; + chip->q_factor = tmp; + } + + if (of_find_property(node, "epp-current-table", &byte_len)) { + chip->epp_current_limits = (struct p938x_limits *) + devm_kzalloc(chip->dev, byte_len, GFP_KERNEL); + + if (chip->epp_current_limits == NULL) + return -ENOMEM; + + rc = of_property_read_u32_array(node, + "epp-current-table", + (u32 *)chip->epp_current_limits, + byte_len / sizeof(u32)); + if (rc < 0) { + p938x_err(chip, "Couldn't read epp current limits rc = %d\n", rc); + return rc; + } + + chip->epp_limit_num = + byte_len / sizeof(struct p938x_limits); + + p938x_dbg(chip, PR_IMPORTANT, "epp current limits: Num: %d\n", chip->epp_limit_num); + for (i = 0; i < chip->epp_limit_num; i++) { + p938x_dbg(chip, PR_IMPORTANT, "epp current limit: %dW, %dmA", + chip->epp_current_limits[i].tx_capability_mw / 1000, + chip->epp_current_limits[i].idc_max_ma); + } + } + + rc = of_property_read_u32(node, "epp-voltage", &chip->epp_voltage); + if (rc) + chip->epp_voltage = EPP_MAX_VOUT; + + rc = of_property_read_u32(node, "bpp-current", &chip->bpp_current_limit); + if (rc) + chip->bpp_current_limit = BPP_MAX_IDC; + + rc = of_property_read_u32(node, "bpp-voltage", &chip->bpp_voltage); + if (rc) + chip->bpp_voltage = BPP_MAX_VOUT; + + return 0; +} + +static enum power_supply_property p938x_wls_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_REAL_TYPE, + POWER_SUPPLY_PROP_CHARGING_ENABLED, + POWER_SUPPLY_PROP_CHARGE_TYPE, +}; + +static int p938x_wls_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct p938x_charger *chip = power_supply_get_drvdata(psy); + int tx_guaranteed; + int tx_potential; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = p938x_is_ldo_on(chip); + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = p938x_is_ldo_on(chip); + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = p938x_get_rx_iout(chip) * 1000; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + val->intval = p938x_get_rx_ocl(chip) * 1000; + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION: + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = p938x_get_rx_vout(chip) * 1000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + val->intval = p938x_get_rx_vout_set(chip) * 1000; + break; + case POWER_SUPPLY_PROP_REAL_TYPE: + val->intval = POWER_SUPPLY_TYPE_WIRELESS; + break; + case POWER_SUPPLY_PROP_CHARGING_ENABLED: + val->intval = p938x_is_ldo_on(chip); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + if (p938x_is_ldo_on(chip)) { + if (chip->epp_mode && + !p938x_get_tx_capability(chip, &tx_guaranteed, &tx_potential) && + tx_guaranteed >= FAST_CHARGE_MW) + val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; + else + val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + } else + val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int p938x_wls_set_prop(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct p938x_charger *chip = power_supply_get_drvdata(psy); + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_CURRENT_MAX: + chip->wls_iout_max = val->intval / 1000; + rc = p938x_set_rx_ocl(chip, chip->wls_iout_max); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + chip->wls_vout_max = val->intval / 1000; + rc = p938x_set_rx_vout(chip, chip->wls_vout_max); + break; + case POWER_SUPPLY_PROP_CHARGING_ENABLED: + rc = p938x_enable_charging(chip, val->intval); + break; + default: + return -EINVAL; + } + + power_supply_changed(chip->wls_psy); + return rc; +} + +static int p938x_wls_prop_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + int rc; + + switch (psp) { +#ifdef P938X_USER_DEBUG + case POWER_SUPPLY_PROP_CURRENT_MAX: + case POWER_SUPPLY_PROP_VOLTAGE_MAX: +#endif + case POWER_SUPPLY_PROP_CHARGING_ENABLED: + rc = 1; + break; + default: + rc = 0; + break; + } + + return rc; +} + +/* Called when something we are "supplied_from" reports a change */ +static void p938x_external_power_changed(struct power_supply *psy) +{ + struct p938x_charger *chip = power_supply_get_drvdata(psy); + + p938x_update_supplies_status(chip); + + cancel_delayed_work(&chip->heartbeat_work); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(0)); +} + +static const struct power_supply_desc wls_psy_desc = { + .name = "wireless", + .type = POWER_SUPPLY_TYPE_WIRELESS, + .get_property = p938x_wls_get_prop, + .set_property = p938x_wls_set_prop, + .property_is_writeable = p938x_wls_prop_is_writeable, + .properties = p938x_wls_props, + .num_properties = ARRAY_SIZE(p938x_wls_props), + .external_power_changed = p938x_external_power_changed, +}; + +static int p938x_register_power_supply(struct p938x_charger *chip) +{ + struct power_supply_config wls_psy_cfg = {}; + int rc = 0; + + wls_psy_cfg.drv_data = chip; + wls_psy_cfg.supplied_to = pm_wls_supplied_to; + wls_psy_cfg.num_supplicants = ARRAY_SIZE(pm_wls_supplied_to); + + chip->wls_psy = power_supply_register(chip->dev, + &wls_psy_desc, + &wls_psy_cfg); + + if (IS_ERR(chip->wls_psy)) { + p938x_err(chip, "Couldn't register wls psy rc=%ld\n", + PTR_ERR(chip->wls_psy)); + rc = PTR_ERR(chip->wls_psy); + } else { + chip->wls_psy->supplied_from = pm_wls_supplied_from; + chip->wls_psy->num_supplies = ARRAY_SIZE(pm_wls_supplied_from); + } + + return rc; +} + +static const struct regmap_config p938x_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0xFFFF, +}; + +static int p938x_charger_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc; + struct p938x_charger *chip; + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->client = client; + chip->dev = &client->dev; + chip->name = "WLS"; + chip->debug_mask = &__debug_mask; + INIT_DELAYED_WORK(&chip->heartbeat_work, p938x_heartbeat_work); + INIT_DELAYED_WORK(&chip->tx_mode_work, p938x_tx_mode_work); + INIT_DELAYED_WORK(&chip->removal_work, p938x_removal_work); + INIT_DELAYED_WORK(&chip->restore_idc_work, p938x_restore_idc_work); + device_init_wakeup(chip->dev, true); + + chip->regmap = regmap_init_i2c(client, &p938x_regmap_config); + if (IS_ERR(chip->regmap)) { + pr_err("Couldn't initialize register regmap rc = %ld\n", + PTR_ERR(chip->regmap)); + rc = PTR_ERR(chip->regmap); + goto free_mem; + } + + rc = p938x_parse_dt(chip); + if (rc) { + p938x_err(chip, "Couldn't parse DT nodes rc=%d\n", rc); + goto free_mem; + } + + i2c_set_clientdata(client, chip); + dev_set_drvdata(chip->dev, chip); + + rc = p938x_register_power_supply(chip); + if (rc) { + p938x_err(chip, "Couldn't register power supply rc=%d\n", rc); + goto free_mem; + } + + rc = p938x_hw_init(chip); + if (rc < 0) { + p938x_err(chip, "Failed to init hw, rc=%d\n", rc); + goto free_psy; + } + + chip->dc_psy = power_supply_get_by_name("dc"); + if (!chip->dc_psy) { + p938x_err(chip, "Couldn't get dc psy\n"); + goto free_psy; + } + + mutex_init(&chip->disconnect_lock); + mutex_init(&chip->txmode_lock); + + PM_WAKEUP_REGISTER(chip->dev, chip->wls_wake_source, "p938x wireless charger"); + + if (!chip->wls_wake_source) { + p938x_err(chip, "failed to allocate wakeup source\n"); + rc = -ENOMEM; + goto free_psy; + } + + /* In case we are already powered on */ + p938x_check_status(chip); + p938x_check_system_mode(chip); + + /* This IRQ handler is the primary one, and detects when a wireless charger + * is attached + */ + rc = devm_request_threaded_irq(&client->dev, client->irq, NULL, + p938x_irq_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "p938x_irq", chip); + if (rc) { + p938x_err(chip, "Failed irq=%d request rc = %d\n", + client->irq, rc); + goto free_psy; + } + + enable_irq_wake(client->irq); + + /* This IRQ handler is for detachment only. The chip is powered off when + * the transmitter is removed, so we need to rely on a separate IRQ to + * handle that event + */ + rc = devm_request_threaded_irq(&client->dev, chip->wchg_det_irq, NULL, + p938x_det_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "p938x_det_irq", chip); + if (rc) { + p938x_err(chip, "Failed irq=%d request rc = %d\n", + chip->wchg_det_irq, rc); + goto free_psy; + } + + /* Force dc-en on if we boot attached, see p938x_det_irq_handler */ + if (gpio_get_value(chip->wchg_det.gpio)) { + p938x_dbg(chip, PR_IMPORTANT, "Detected attached at probe.\n"); + p938x_set_dc_en_override(chip, 2); + /* Without a dc-in interrupt, the qcom driver will not set the current + * so re-run aicl to get the correct max current + */ + p938x_set_dc_aicl_rerun(chip); + } + + /* Register thermal zone cooling device */ + chip->tcd = thermal_of_cooling_device_register(dev_of_node(chip->dev), + "p938x_charger", chip, &p938x_tcd_ops); + + create_debugfs_entries(chip); + if (sysfs_create_groups(&chip->dev->kobj, p938x_groups)) + p938x_err(chip, "Failed to create sysfs attributes\n"); + + pr_info("p938x wireless receiver initialized successfully\n"); + + power_supply_changed(chip->wls_psy); + + return 0; + +free_psy: + power_supply_unregister(chip->wls_psy); +free_mem: + devm_kfree(chip->dev, chip); + return rc; +} + +static int p938x_charger_remove(struct i2c_client *client) +{ + struct p938x_charger *chip = i2c_get_clientdata(client); + + PM_WAKEUP_UNREGISTER(chip->wls_wake_source); + sysfs_remove_groups(&chip->dev->kobj, p938x_groups); + cancel_delayed_work_sync(&chip->heartbeat_work); + cancel_delayed_work_sync(&chip->tx_mode_work); + debugfs_remove_recursive(chip->debug_root); + if(chip->tcd) + thermal_cooling_device_unregister(chip->tcd); + + return 0; +} + +static void p938x_shutdown(struct i2c_client *client) +{ + struct p938x_charger *chip = i2c_get_clientdata(client); + + p938x_dbg(chip, PR_MOTO, "Wireless charger shutdown\n"); +} + +static int p938x_suspend(struct device *dev) +{ + struct p938x_charger *chip = dev_get_drvdata(dev); + + if (regulator_is_enabled(chip->vdd_i2c_vreg)) + return regulator_disable(chip->vdd_i2c_vreg); + + return 0; +} + +static int p938x_resume(struct device *dev) +{ + struct p938x_charger *chip = dev_get_drvdata(dev); + + if (!regulator_is_enabled(chip->vdd_i2c_vreg)) + return regulator_enable(chip->vdd_i2c_vreg); + + return 0; +} + +static const struct dev_pm_ops p938x_pm_ops = { + .suspend = p938x_suspend, + .resume = p938x_resume, +}; + +static const struct of_device_id p938x_match_table[] = { + { .compatible = "idt,p938x-charger",}, + { }, +}; + +static const struct i2c_device_id p938x_charger_id[] = { + {"p938x-charger", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, p938x_charger_id); + +static struct i2c_driver p938x_charger_driver = { + .driver = { + .name = "p938x-charger", + .owner = THIS_MODULE, + .of_match_table = p938x_match_table, + .pm = &p938x_pm_ops, + }, + .probe = p938x_charger_probe, + .remove = p938x_charger_remove, + .id_table = p938x_charger_id, + .shutdown = p938x_shutdown, +}; + +module_i2c_driver(p938x_charger_driver); + +MODULE_DESCRIPTION("p938x charger"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("i2c:p938x-charger"); diff --git a/drivers/power/qpnp-smbcharger-mmi/Android.mk b/drivers/power/qpnp-smbcharger-mmi/Android.mk new file mode 100755 index 000000000000..ad3d320a2b9a --- /dev/null +++ b/drivers/power/qpnp-smbcharger-mmi/Android.mk @@ -0,0 +1,17 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(MOTO_PD_CHARGER),true) + KERNEL_CFLAGS += CONFIG_PD_CHARGER_MMI=y +endif + +ifeq ($(BQ2597X_DUAL_ENABLE),true) + KERNEL_CFLAGS += CONFIG_BQ2597X_DUAL=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := qpnp-smbcharger-mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/power/qpnp-smbcharger-mmi/Kbuild b/drivers/power/qpnp-smbcharger-mmi/Kbuild new file mode 100755 index 000000000000..149850849b04 --- /dev/null +++ b/drivers/power/qpnp-smbcharger-mmi/Kbuild @@ -0,0 +1,13 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +ifneq ($(filter m y,$(CONFIG_PD_CHARGER_MMI)),) + EXTRA_CFLAGS += -DCONFIG_PD_CHARGER_MMI +endif + +ifneq ($(filter m y, $(CONFIG_BQ2597X_DUAL)),) + EXTRA_CFLAGS += -DCONFIG_BQ2597X_DUAL +endif + +obj-m += qpnp-smbcharger-mmi.o diff --git a/drivers/power/qpnp-smbcharger-mmi/Makefile b/drivers/power/qpnp-smbcharger-mmi/Makefile new file mode 100755 index 000000000000..34b09dd815a7 --- /dev/null +++ b/drivers/power/qpnp-smbcharger-mmi/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean \ No newline at end of file diff --git a/drivers/power/qpnp-smbcharger-mmi/README b/drivers/power/qpnp-smbcharger-mmi/README new file mode 100644 index 000000000000..e01ce089fe41 --- /dev/null +++ b/drivers/power/qpnp-smbcharger-mmi/README @@ -0,0 +1,382 @@ +Motorola Mobility. SMBMMI Charger Specific Bindings + +This is an introduction to qpnp-smbcharger-mmi wrapper/driver. It is a DLKM, +and messages on kernel logs may be tagged as: +- "SMBMMI" +- "SMB_MMI" +- PMIC model (like "pm8150b_charger", or "pm660") +- or no tag, if it's happening during probe + +It depends on QCOM PMIC driver implementation (located in +kernel/drivers/power/supply/qcom), and requires different DT properties in order +to configure itself. + +All products rely on parse_mmi_dt() to look for necessary DT properties, and in +case it has a dual-battery architecture, it uses the additional +parse_mmi_dual_dt() function. + +When dual-battery architecture, references to battery will be "main" and "flip", +referring to where in the phone battery is physically located. Prefix "max" is +legacy, since first Fuel Gauge solution was from Maxim Integrated, but other FG +solutions are possible, like Texas Instruments (TI). + +In terms of DT properties, some of them are: + + +- qcom,mmi-temp-zones + Usage: optional + Value Type: Array of tuples of + Definition: Unlimited array of arrays. Each internal array needs to have a set +of 4 bytes, each one representing this sequence: Temperature (in degC), Step +"Normalized" Voltage (in millivolts), Full Rate "FCC" Current (in milliamps), +and Taper Rate "FCC Normalized" Current (in milliamps). These values are +related to charging limitations based on temperature specifications. Note that +setting a voltage to '0' means that it doesn't matter the battery voltage on +that defined temperature range to perform necessary tapering. To understand how +those values are used, please check "IEEE Charging Compliance" section below. + +- qcom,iterm-ma + Usage: optional + Value Type: + Definition: It will define taper current on battery (in milliamps). If not +defined, defaults to 150. + +- qcom,vfloat-comp-uv + Usage: optional + Value Type: + Definition: This is a tolerance, in uV, for battery floating voltage when +charging is being limited. Such situation can happen when either battery is +close to full or when there's thermal mitigation on charging. If not set, +defaults to 0. + +- qcom,enable-charging-limit + Usage: optional + Value Type: + Definition: Boolean flag to set capacity limits to battery when plugged on +a charger and is executing a Factory Image. It will use upper and lower +capacities defined by the Device Tree. + +- qcom,enable-factory-poweroff + Usage: optional + Value Type: + Definition: Boolean flag which enables Factory Kill when executing under a +Factory Image. By default, Factory Kill is a feature only in Factory Mode, +which turns device off when charger is removed. + +- qcom,upper-limit-capacity + Usage: optional + Value Type: + Definition: Limits minimum battery SoC (in percentage). In other words, starts +charging when it reaches this value. Especially useful for factory procedures, +which needs to limit SoC. Requires qcom,enable-charging-limit. If not defined, +defaults to 100. + +- qcom,lower-limit-capacity + Usage: optional + Value Type: + Definition: Limits maximum battery SoC (in percentage). In other words, stop +charging when it reaches this value. Especially useful for factory procedures, +which needs to limit SoC. Requires qcom,enable-charging-limit. If not defined, +defaults to 0. + +- qcom,dc-icl-ma + Usage: optional + Value Type: + Definition: Set this value to allow ICL for DC current, used by MOD or +wireless charging. If not set, it won't be used. + +- qcom,force-hvdcp-5v +Usage: optional +Value Type: +Definition: Boolean flag to maintain USB input to 5V, so no PD or other +protocols request higher voltages. + +- mmi,max-main-psy + Usage: optional + Value Type: + Definition: Battery name on battery main description defined in FG driver. +(as an example, see max170xx_battery_mmi.c) + +- mmi,max-flip-psy + Usage: optional + Value Type: + Definition: Battery name on battery flip description defined in FG driver. +(as an example, see max170xx_battery_mmi.c) + +- qcom,mmi-temp-zones-main + Usage: optional + Value Type: Array of + Definition: Depends on mmi,max-main-psy. Analoguous to qcom,mmi-temp-zones . + +- qcom,iterm-ma-main + Usage: optional + Value Type: + Definition: Depends on mmi,max-main-psy. Analoguous to qcom,iterm-ma. + +- qcom,mmi-temp-zones-flip + Usage: optional + Value Type: Array of + Definition: Depends on mmi,max-main-psy. Analoguous to qcom,mmi-temp-zones. + +- qcom,iterm-ma-flip + Usage: optional + Value Type: + Definition: Depends on mmi,max-flip-psy. Analoguous to qcom,iterm-ma. + + +========================== + IEEE Charging Compliance +========================== + +In order to be compliant to IEEE rules, there are a series of limitations on +how much a battery can be charged, based on temperature. That will limit how +much current can be drawn, or maximum battery voltage to be achieved during +charging. + +Current is always tapered when voltage achieves maximum voltage specification +(which is generally 4.4V, but may have a different value, depending a product +battery specs). It is tapered to some small value, possibly around 100mA, +which is based on product specifications and capacity testing, defined by +EE (Electrical Engineering) Team/Hardware. + +If temperature is below -20 degC or above 60 degC, it should automatically +stop charging device. + +If temperature is in between 15 and 45 deg C, it can achieve maximum charging +current, and such value varies across products. Generally, if it supports PD, +this will be a much higher current than non-PD products, which will also have +a much higher current than dual-battery products. Important to note that this +is a rule-of-thumb, so always check product specification. + +If temperature is between 0 and 10 deg C, but battery voltage is above 4.20V, +current should be tapered to 1000mA. + +Finally, if temperature is in between -20 and 0 deg C, current should be +tapered to 1000mA. + +NOTE1: whenever dealing with IEEE certification, most documentation and +variable notation across bootloader and kernel may refer to JEITA. This is a +convention on such standards and values to which temperature, voltage and +current levels are accepted in different scenarios for Li-ion batteries. So +JEITA can be seen as a general standard, but IEEE enforces it as well as +provide ways to certify a product according to it (is mandatory to certify +any commercial product that has a lithium-ion battery embedded or plugged to +it). + +NOTE2: taper current mostly refers to when battery is above maximum allowed +voltage on a specific temperature region, based on rules above. So be aware +that the rest of this document may refer as "taper" to a current that is +limiting the maximum value, but before achieving a stage in which current +is limited to something around 100mA (so an intermediate stage). Also +"taper voltage" will refer to a voltage level in which this mentioned +"taper" current (intermediate current level) is triggered. + + +========================================= + How to define qcom,mmi-temp-zones array +========================================= + +When defining qcom,mmi-temp-zones array, it is important to always have in +mind the IEEE rules described above, as well the battery specifications. + +Each tuple in that array represents a different value, as shown here: + + +"temperature" defines maximum temperature value. So setting "35" means a +definition to anything below 35 degC. + +"taper_volt" means maximum allowed battery voltage in that temperature +range. So setting "4200" means that tapering will happen once battery +is above 4.2V and is still charging. + +"max_cur_below_taper_volt" defines maximum allowed current value in case +battery is below "taper_volt" value. So setting it to "700" means that +current won't go above 700mA, as long as battery is below "taper_volt" +voltage. + +"max_cur_above_taper_volt" defines maximum allowed current value in case +battery is above "taper_volt" value. So setting it to "0" means that it +won't charge in case battery is above "taper_volt" voltage. + +Here's an example from Voyager: + +qcom,mmi-temp-zones-flip = <0 4200 250 0>, + <10 4200 610 250>, + <15 0 610 610>, + <45 4200 1225 860>, + <60 4200 610 0>; + +First line states "If temperature is below 0 degC, and flip battery voltage +is below 4.2V, device can be charged up to 250mA. But if flip battery voltage +is above 4.2V, device cannot be charged". + +Third line states "If temperature is below 15 degC, flip battery can be charged +up to 610mA, no matter the battery voltage". + + +================================================= + Understanding the IEEE algorithm implementation +================================================= + +Now that IEEE rules were understood, as well as how qcom,mmi-temp-zones is set, +just to stress out this driver implementation, the thermal charging limitation +implementation will be described. + +mmi_basic_charge_sm() or mmi_dual_charge_sm() are the functions responsible +to enforce such rules. In that way, before we check the implementation, here +are some conventions: + +STEP_NONE: no charger present +STEP_STOP: in zone hot or zone cold, which limits charging considerably +STEP_NORM: limits current to some value +STEP_MAX: allows maximum current value, when in between 0 and 45 degC +STEP_FULL: battery is almost or completely full, tapering to a small current +STEP_FLOAT: TODO + +Important to note that even when moving from one state to another, temperature +may change. Hence, taper current and voltage will no longer correspond to such +state, and so is always necessary to check on every state in which temperature +range device is. + +"batt_ma" refers to battery current (equivalent to current_now on battery +sysfs). +"taper_ma" is "fcc_norm_ma" in case is in STATE_MAX, which means battery is +above taper voltage. If in any other state, it will refer to "chrg_iterm", +which will depend on device specifications (4.4V batteries have 97mA as +current standards, and 4.45V batteries have 121mA as taper current). + +Now, let's check the algorithms for a bit: + +----------------------------------------------------- +current-step: STEP_NONE | STEP_STOP + if norm_mv AND (batt_mv + 50 >= norm_mv) // (1) + if fcc_norm_ma // (2) + -goto STEP_NORM // (2) + else + -goto STEP_STOP // (2) + else + -goto STEP_MAX // (1) + +(1) are we in a range which has "norm_mv" defined, and battery is above this +value? +This corresponds to most temperature ranges, except in between 15 and 45 degC. +If that's the case, allow charging up to maximum current defined in product +spec. + +(2) is "fcc_norm_ma" a value different than zero? +This happens if temperature is in range between 0 and 45 degC. Due to previous +consideration, it is only missing range 0 to 15 degC here. If that's the case, +limit maximum current. Otherwise, battery is above "norm_mv" (which probably +is defined as 4.2V), and at that voltage and temperature, limiting charging +considerably. + +----------------------------------------------------- +current-step: STEP_MAX + if !norm_mv // (1) + taper_cnt = 0 + if (batt_mv + 50) > max_fv_mv // (2) + -goto STEP_NORM + else + -goto STEP_MAX + else if (batt_mv + 50) < norm_mv // (1) + taper_cnt = 0 + -goto STEP_MAX + else if !fcc_norm_ma // (3) + -goto STEP_FLOAT + else if current_has_tapered // (4) + taper_cnt = 0 + if charge_halted + vote chg_dis_votable + -goto STEP_NORM + +(1) are we in a range which has "norm_mv" NOT defined, or below taper voltage? +This is only for 15 to 45 degC region, which has no current tapering. + +(2) are we above taper voltage? +This checks whether taper voltage was achieved, making current to taper to +defined values, depending on which temperature region battery is at this +moment. + +(3) at current temperature, is taper current defined? +If there is a taper current value to current temperature region (which is +usually in between 0 and 15 degC as defined in current implementation), allow +TODO STEP_FLOAT + +(4) has current been limited due to other thermal rules? +Function mmi_has_current_tapered() requests to power supply votable module +what is the allowed maximum current. Then such value will be compared to taper +current value, and the minimum will be chosen. Additionally, it checks whether +there's still a charger plugged to device or if charging is disabled, and if +any, mark a flag on system displaying no charging is going on. Finally, move +to taper current state (STEP_NORM). + +----------------------------------------------------- +current-step: STEP_NORM + if !fcc_norm_ma // (1) + -goto STEP_FLOAT + else if batt_soc < 100 or (batt_mv + 50) < max_fv_mv // (2) + taper_cnt = 0 + -goto STEP_NORM + else if current_has_tapered // (2) + -goto STEP_FULL + +(1) although device is in STEP_NORM, is there a defined taper current for +current temperature region device is in? +It may happen that temperature has changed, and device no longer needs to +be tapered. Hence, move to STEP_FLOAT. + +(2) is battery NOT full or below taper voltage? +Stay on this state, because tapering is still happening. Otherwise, it may +be the case that battery is above taper voltage, or device is already fully +charged, forcing it to prepare to stop charging (STEP_FULL). + +----------------------------------------------------- +current-step: STEP_FULL + if batt_soc < 99 // (1) + taper_cnt = 0 + goto STEP_NORM + +(1) is battery no longer full? +It may happen that battery device was previously full, but no longer is. So, +first move to a limited current state for safety, and then in such state, it +may happen that maximum current levels can be achieved. + +----------------------------------------------------- +current-step: STEP_FLOAT + if fcc_norm_ma or (batt_mv + 50) < norm_mv // (1) + goto STEP_MAX + if current_has_tapered // (2) + goto STEP_STOP + +(1) does battery temperature allow current to be tapered to something above +0mA, or battery is below taper voltage? +This means that temperature is in between 0 and 15 degC. If so, try to achieve +maximum current value (STEP_MAX). + +(2) does current needs to be tapered? +In case mmi_has_current_tapered() needs to decrease current, it will request +charger to stop charging. + +----------------------------------------------------- +Setting current values based on state machine: + +STEP_FLOAT +STEP_MAX + target_fcc = fcc_max_ma + target_fv: + if !norm_mv + target_fv = max_fv_mv + chip->vfloat_comp_mv + else + target_fv = norm_mv + chip->vfloat_comp_mv + +STEP_FULL: + target_fcc = -EINVAL + target_fv = max_fv_mv + +STEP_NORM: + target_fcc = fcc_norm_ma + target_fv = max_fv_mv + chip->vfloat_comp_mv + +STEP_NONE: + target_fv = max_fv_mv + target_fcc = fcc_norm_ma diff --git a/drivers/power/qpnp-smbcharger-mmi/qpnp-smbcharger-mmi.c b/drivers/power/qpnp-smbcharger-mmi/qpnp-smbcharger-mmi.c new file mode 100755 index 000000000000..8bb559ecda53 --- /dev/null +++ b/drivers/power/qpnp-smbcharger-mmi/qpnp-smbcharger-mmi.c @@ -0,0 +1,5101 @@ +/* Copyright (c) 2017, 2018 The Linux Foundation. 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 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MODULE_LOG "SMBMMI" + +#define mmi_err(chg, fmt, ...) \ + do { \ + pr_err("%s: %s: %s: " fmt, chg->name, \ + MODULE_LOG, __func__, ##__VA_ARGS__); \ + ipc_log_string(chg->ipc_log, \ + "ERR:%s: " fmt, __func__, ##__VA_ARGS__); \ + } while (0) + +#define mmi_warn(chg, fmt, ...) \ + do { \ + pr_warn("%s: %s: %s: " fmt, chg->name, \ + MODULE_LOG, __func__, ##__VA_ARGS__); \ + ipc_log_string(chg->ipc_log, \ + "WARN:%s: " fmt, __func__, ##__VA_ARGS__); \ + } while (0) + +#define mmi_info(chg, fmt, ...) \ + do { \ + pr_info("%s: %s: %s: " fmt, chg->name, \ + MODULE_LOG, __func__, ##__VA_ARGS__); \ + ipc_log_string(chg->ipc_log, \ + "INFO:%s: " fmt, __func__, ##__VA_ARGS__); \ + } while (0) + +#define mmi_dbg(chg, fmt, ...) \ + do { \ + if (*chg->debug_enabled) \ + pr_info("%s: %s: %s: " fmt, chg->name, \ + MODULE_LOG, __func__, ##__VA_ARGS__); \ + else \ + pr_debug("%s: %s: %s: " fmt, chg->name, \ + MODULE_LOG, __func__, ##__VA_ARGS__); \ + ipc_log_string(chg->ipc_log, \ + "DBG:%s: " fmt, __func__, ##__VA_ARGS__); \ + } while (0) + +#define MMI_LOG_PAGES (50) + +static bool debug_enabled; +module_param(debug_enabled, bool, 0600); +MODULE_PARM_DESC(debug_enabled, "Enable debug for SMBMMI driver"); + +static struct smb_mmi_charger *this_chip = NULL; + +#ifndef PM8150B_SUBTYPE +#define PM8150B_SUBTYPE PM855B_SUBTYPE +#endif +#ifndef PM7250B_SUBTYPE +#define PM7250B_SUBTYPE 0x2E +#endif + +#define CHGR_BASE 0x1000 +#define DCDC_BASE 0x1100 +#define BATIF_BASE 0x1200 +#define USBIN_BASE 0x1300 +#define DCIN_BASE 0x1400 +#define TYPEC_BASE 0X1500 +#define MISC_BASE 0x1600 + +#define BATTERY_CHARGER_STATUS_1_REG (CHGR_BASE + 0x06) +#define BATTERY_CHARGER_STATUS_MASK (GENMASK(2, 0)) +#define PM8150B_JEITA_EN_CFG_REG (CHGR_BASE + 0x90) + +#define POWER_PATH_STATUS_REG (DCDC_BASE + 0x0B) +#define MISC_POWER_PATH_STATUS_REG (MISC_BASE + 0x0B) +#define USBIN_SUSPEND_STS_BIT BIT(6) + +#define DCDC_CFG_REF_MAX_PSNS_REG (DCDC_BASE + 0x8C) + +#define APSD_STATUS_REG (USBIN_BASE + 0x07) +#define APSD_STATUS_7_BIT BIT(7) +#define HVDCP_CHECK_TIMEOUT_BIT BIT(6) +#define SLOW_PLUGIN_TIMEOUT_BIT BIT(5) +#define ENUMERATION_DONE_BIT BIT(4) +#define VADP_CHANGE_DONE_AFTER_AUTH_BIT BIT(3) +#define QC_AUTH_DONE_STATUS_BIT BIT(2) +#define QC_CHARGER_BIT BIT(1) +#define APSD_DTC_STATUS_DONE_BIT BIT(0) +#define APSD_RESULT_STATUS_REG (USBIN_BASE + 0x08) +#define APSD_RESULT_STATUS_7_BIT BIT(7) +#define APSD_RESULT_STATUS_MASK GENMASK(6, 0) +#define QC_3P0_BIT BIT(6) +#define QC_2P0_BIT BIT(5) +#define FLOAT_CHARGER_BIT BIT(4) +#define DCP_CHARGER_BIT BIT(3) +#define CDP_CHARGER_BIT BIT(2) +#define OCP_CHARGER_BIT BIT(1) +#define SDP_CHARGER_BIT BIT(0) +#define USBIN_INT_RT_STS (USBIN_BASE + 0x10) +#define USBIN_PLUGIN_RT_STS_BIT BIT(4) +#define USBIN_INT_EN_CLR (USBIN_BASE + 0x16) +#define USBIN_OV_EN_CLR BIT(3) +#define CMD_APSD_REG (USBIN_BASE + 0x41) +#define APSD_RERUN_BIT BIT(0) +#define ICL_OVERRIDE_BIT BIT(1) +#define USBIN_CMD_ICL_OVERRIDE_REG (USBIN_BASE + 0x42) +#define USBIN_ICL_OVERRIDE_BIT BIT(0) +#define HVDCP_PULSE_COUNT_MAX_REG (USBIN_BASE + 0x5B) +#define HVDCP_PULSE_COUNT_MAX_QC2_MASK GENMASK(7, 6) +#define HVDCP_PULSE_COUNT_MAX_QC3_MASK GENMASK(5, 0) +#define USBIN_ICL_OPTIONS_REG (USBIN_BASE + 0x66) +#define USBIN_MODE_CHG_BIT BIT(0) +#define USBIN_LOAD_CFG_REG (USBIN_BASE + 0x65) +#define ICL_OVERRIDE_AFTER_APSD_BIT BIT(4) +#define USBIN_AICL_OPTIONS_CFG_REG (USBIN_BASE + 0x80) +#define LEGACY_CABLE_CFG_REG (TYPEC_BASE + 0x5A) +#define USBIN_ADAPTER_ALLOW_CFG_REG (USBIN_BASE + 0x60) +#define USBIN_ADAPTER_ALLOW_MASK GENMASK(3, 0) + +/* DCIN Interrupt Bits */ +#define DCIN_PLUGIN_RT_STS_BIT BIT(4) +#define DCIN_INT_RT_STS (DCIN_BASE + 0x10) + +enum { + USBIN_ADAPTER_ALLOW_5V = 0, + USBIN_ADAPTER_ALLOW_9V = 2, + USBIN_ADAPTER_ALLOW_5V_OR_9V = 3, + USBIN_ADAPTER_ALLOW_12V = 4, + USBIN_ADAPTER_ALLOW_5V_OR_12V = 5, + USBIN_ADAPTER_ALLOW_9V_TO_12V = 6, + USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V = 7, + USBIN_ADAPTER_ALLOW_5V_TO_9V = 8, + USBIN_ADAPTER_ALLOW_5V_TO_12V = 12, +}; + +#define LOW_BATT_MV 3300 +#define SHUT_BATT_MV 3100 +#define REV_BST_THRESH 4700 +#define REV_BST_DROP 150 +#define REV_BST_BULK_DROP 100 +#define REV_BST_MA -10 +#define BOOST_BACK_VOTER "BOOST_BACK_VOTER" +#define MMI_HB_VOTER "MMI_HB_VOTER" +#define BATT_PROFILE_VOTER "BATT_PROFILE_VOTER" +#define DEMO_VOTER "DEMO_VOTER" +#define HYST_STEP_MV 50 +#define HYST_STEP_FLIP_MV (HYST_STEP_MV*2) +#define DEMO_MODE_HYS_SOC 5 +#define DEMO_MODE_VOLTAGE 4000 +#define WARM_TEMP 45 +#define COOL_TEMP 0 +#define WEAK_CHARGER_WAIT 30000 +#define EVENT_WEAK_CHARGER_WAIT 0 + +#define HEARTBEAT_DELAY_MS 5000 +#define HEARTBEAT_DUAL_DELAY_MS 10000 +#define HEARTBEAT_DUAL_DELAY_OCP_MS 1000 +#define HEARTBEAT_OCP_SETTLE_CNT 7 +#define HEARTBEAT_FACTORY_MS 1000 +#define HEARTBEAT_DISCHARGE_MS 100000 + +#define EMPTY_CYCLES 101 + +enum { + TRICKLE_CHARGE = 0, + PRE_CHARGE, + FAST_CHARGE, + FULLON_CHARGE, + TAPER_CHARGE, + TERMINATE_CHARGE, + INHIBIT_CHARGE, + DISABLE_CHARGE, +}; + +enum { + INHIBIT_CHARGE_V5 = 0, + TRICKLE_CHARGE_V5, + PRE_CHARGE_V5, + FULLON_CHARGE_V5, + TAPER_CHARGE_V5, + TERMINATE_CHARGE_V5, + PAUSE_CHARGE_V5, + DISABLE_CHARGE_V5, +}; + +enum { + POWER_SUPPLY_CHARGE_RATE_NONE = 0, + POWER_SUPPLY_CHARGE_RATE_NORMAL, + POWER_SUPPLY_CHARGE_RATE_WEAK, + POWER_SUPPLY_CHARGE_RATE_TURBO, +}; + +enum { + CHG_BC1P2_SDP = 0, + CHG_BC1P2_OCP, + CHG_BC1P2_CDP, + CHG_BC1P2_DCP, + CHG_BC1P2_FLOAT, + CHG_BC1P2_QC_2P0, + CHG_BC1P2_QC_3P0, + CHG_BC1P2_UNKNOWN, +}; + +static char *charge_rate[] = { + "None", "Normal", "Weak", "Turbo" +}; + +struct mmi_temp_zone { + int temp_c; + int norm_mv; + int fcc_max_ma; + int fcc_norm_ma; +}; + +#define MAX_NUM_STEPS 10 +enum mmi_temp_zones { + ZONE_FIRST = 0, + /* states 0-9 are reserved for zones */ + ZONE_LAST = MAX_NUM_STEPS + ZONE_FIRST - 1, + ZONE_HOT, + ZONE_COLD, + ZONE_NONE = 0xFF, +}; + +enum { + BASE_BATT = 0, + MAIN_BATT, + FLIP_BATT, +}; + +enum mmi_chrg_step { + STEP_MAX, + STEP_NORM, + STEP_FULL, + STEP_FLOAT, + STEP_DEMO, + STEP_STOP, + STEP_NONE = 0xFF, +}; + +static char *stepchg_str[] = { + [STEP_MAX] = "MAX", + [STEP_NORM] = "NORMAL", + [STEP_FULL] = "FULL", + [STEP_FLOAT] = "FLOAT", + [STEP_DEMO] = "DEMO", + [STEP_STOP] = "STOP", + [STEP_NONE] = "NONE", +}; + +struct smb_mmi_chg_status { + int batt_mv; + int batt_ma; + int batt_soc; + int batt_temp; + int usb_mv; + int dc_mv; + int charger_present; + int vbus_present; + int dc_present; +}; + +struct smb_mmi_chg_param { + const char *name; + u16 reg; + int min_u; + int max_u; + int step_u; + int (*get_proc)(struct smb_mmi_chg_param *param, + u8 val_raw); + int (*set_proc)(struct smb_mmi_chg_param *param, + int val_u, + u8 *val_raw); +}; + +struct smb_mmi_chg_freq { + unsigned int freq_5V; + unsigned int freq_6V_8V; + unsigned int freq_9V; + unsigned int freq_12V; +}; + +struct smb_mmi_params { + struct smb_mmi_chg_param fcc; + struct smb_mmi_chg_param fv; + struct smb_mmi_chg_param usb_icl; + struct smb_mmi_chg_param dc_icl; + struct smb_mmi_chg_param aicl_cont_threshold; + struct smb_mmi_chg_param freq_switcher; +}; + +struct mmi_ffc_zone { + int ffc_max_mv; + int ffc_chg_iterm; + int ffc_qg_iterm; +}; + +struct mmi_sm_params { + int num_temp_zones; + struct mmi_temp_zone *temp_zones; + enum mmi_temp_zones pres_temp_zone; + enum mmi_chrg_step pres_chrg_step; + int chrg_taper_cnt; + int batt_health; + int chrg_iterm; + int target_fcc; + int target_fv; + int ocp[MAX_NUM_STEPS]; + int demo_mode_prev_soc; + struct mmi_ffc_zone *ffc_zones; +}; + +enum charging_limit_modes { + CHARGING_LIMIT_OFF, + CHARGING_LIMIT_RUN, + CHARGING_LIMIT_UNKNOWN, +}; + +#define IS_SUSPENDED BIT(0) +#define WAS_SUSPENDED BIT(1) + +struct smb_mmi_charger { + struct device *dev; + struct regmap *regmap; + struct smb_mmi_params param; + struct smb_mmi_chg_freq chg_freq; + char *name; + int smb_version; + + bool factory_mode; + int demo_mode; + struct notifier_block smb_reboot; + + struct power_supply *mmi_psy; + struct power_supply *batt_psy; + struct power_supply *qcom_psy; + struct power_supply *usb_psy; + struct power_supply *bms_psy; + struct power_supply *main_psy; + struct power_supply *pc_port_psy; + struct power_supply *dc_psy; + struct power_supply *wls_psy; + struct power_supply *max_main_psy; + struct power_supply *max_flip_psy; + struct notifier_block mmi_psy_notifier; + struct delayed_work heartbeat_work; + struct delayed_work weakcharger_work; + + struct votable *chg_dis_votable; + struct votable *fcc_votable; + struct votable *fv_votable; + struct votable *usb_icl_votable; + struct votable *dc_suspend_votable; + + bool enable_charging_limit; + bool enable_factory_poweroff; + bool is_factory_image; + enum charging_limit_modes charging_limit_modes; + int upper_limit_capacity; + int lower_limit_capacity; + int max_chrg_temp; + int last_iusb_ua; + bool factory_kill_armed; + int gen_log_rate_s; + bool demo_mode_usb_suspend; + unsigned long flags; + + /* Charge Profile */ + struct mmi_sm_params sm_param[3]; + int base_fv_mv; + int vfloat_comp_mv; + struct wakeup_source *smb_mmi_hb_wake_source; + struct alarm heartbeat_alarm; + int suspended; + bool awake; + int last_reported_soc; + int last_reported_status; + struct regulator *vbus; + bool vbus_enabled; + int prev_chg_rate; + int charger_rate; + int age; + int cycles; + int soc_cycles_start; + bool shut_batt; + int dc_cl_ma; + + bool *debug_enabled; + void *ipc_log; + + struct usbpd *pd_handle; +#ifdef CONFIG_PD_CHARGER_MMI + struct usbpd_pdo_info mmi_pdo_info[PD_MAX_PDO_NUM]; +#endif + int pd_power_max; + + int hvdcp_power_max; + int inc_hvdcp_cnt; + int hb_startup_cnt; + bool ocp_flag; +#ifdef CONFIG_QC3P_PUMP_SUPPORT + int hvdcp_apsd_count; //for qc3p shalcomm charger , run apsd twice. +#endif +}; + +#define CHGR_FAST_CHARGE_CURRENT_CFG_REG (CHGR_BASE + 0x61) +#define CHGR_FLOAT_VOLTAGE_CFG_REG (CHGR_BASE + 0x70) +#define USBIN_CURRENT_LIMIT_CFG_REG (USBIN_BASE + 0x70) +#define DCIN_CURRENT_LIMIT_CFG_REG (DCIN_BASE + 0x70) +#define USBIN_CONT_AICL_THRESHOLD_REG (USBIN_BASE + 0x84) +#define DCDC_FSW_SEL_REG (DCDC_BASE + 0x50) +#define AICL_RANGE2_MIN_MV 5600 +#define AICL_RANGE2_STEP_DELTA_MV 200 +#define AICL_RANGE2_OFFSET 16 +int smblib_get_aicl_cont_threshold(struct smb_mmi_chg_param *param, u8 val_raw) +{ + int base = param->min_u; + u8 reg = val_raw; + int step = param->step_u; + + + if (val_raw >= AICL_RANGE2_OFFSET) { + reg = val_raw - AICL_RANGE2_OFFSET; + base = AICL_RANGE2_MIN_MV; + step = AICL_RANGE2_STEP_DELTA_MV; + } + + return base + (reg * step); +} + +int smblib_set_aicl_cont_threshold(struct smb_mmi_chg_param *param, + int val_u, u8 *val_raw) +{ + int base = param->min_u; + int offset = 0; + int step = param->step_u; + + if (val_u > param->max_u) + val_u = param->max_u; + if (val_u < param->min_u) + val_u = param->min_u; + + if (val_u >= AICL_RANGE2_MIN_MV) { + base = AICL_RANGE2_MIN_MV; + step = AICL_RANGE2_STEP_DELTA_MV; + offset = AICL_RANGE2_OFFSET; + }; + + *val_raw = ((val_u - base) / step) + offset; + + return 0; +} + +/******************** + * REGISTER SETTERS * + ********************/ + struct smb_buck_boost_freq { + int freq_khz; + u8 val; +}; +static const struct smb_buck_boost_freq chg_freq_list[] = { + [0] = { + .freq_khz = 2400, + .val = 7, + }, + [1] = { + .freq_khz = 2100, + .val = 8, + }, + [2] = { + .freq_khz = 1600, + .val = 11, + }, + [3] = { + .freq_khz = 1200, + .val = 15, + }, +}; + +int smblib_set_chg_freq(struct smb_mmi_chg_param *param, + int val_u, u8 *val_raw) +{ + u8 i; + + if (val_u > param->max_u || val_u < param->min_u) + return -EINVAL; + + /* Charger FSW is the configured freqency / 2 */ + val_u *= 2; + for (i = 0; i < ARRAY_SIZE(chg_freq_list); i++) { + if (chg_freq_list[i].freq_khz == val_u) + break; + } + if (i == ARRAY_SIZE(chg_freq_list)) { + pr_err("Invalid frequency %d Hz\n", val_u / 2); + return -EINVAL; + } + + *val_raw = chg_freq_list[i].val; + + return 0; +} + +static struct smb_mmi_params smb5_pm8150b_params = { + .fcc = { + .name = "fast charge current", + .reg = CHGR_FAST_CHARGE_CURRENT_CFG_REG, + .min_u = 0, + .max_u = 8000000, + .step_u = 50000, + }, + .fv = { + .name = "float voltage", + .reg = CHGR_FLOAT_VOLTAGE_CFG_REG, + .min_u = 3600000, + .max_u = 4790000, + .step_u = 10000, + }, + .usb_icl = { + .name = "usb input current limit", + .reg = USBIN_CURRENT_LIMIT_CFG_REG, + .min_u = 0, + .max_u = 5000000, + .step_u = 50000, + }, + .dc_icl = { + .name = "DC input current limit", + .reg = DCDC_CFG_REF_MAX_PSNS_REG, + .min_u = 0, + .max_u = 1500000, + .step_u = 50000, + }, + .aicl_cont_threshold = { + .name = "AICL CONT threshold", + .reg = USBIN_CONT_AICL_THRESHOLD_REG, + .min_u = 4000, + .max_u = 11800, + .step_u = 100, + .get_proc = smblib_get_aicl_cont_threshold, + .set_proc = smblib_set_aicl_cont_threshold, + }, + .freq_switcher = { + .name = "switching frequency", + .reg = DCDC_FSW_SEL_REG, + .min_u = 600, + .max_u = 1200, + .step_u = 400, + .set_proc = smblib_set_chg_freq, + }, +}; + +static struct smb_mmi_params smb5_pmi632_params = { + .fcc = { + .name = "fast charge current", + .reg = CHGR_FAST_CHARGE_CURRENT_CFG_REG, + .min_u = 0, + .max_u = 3000000, + .step_u = 50000, + }, + .fv = { + .name = "float voltage", + .reg = CHGR_FLOAT_VOLTAGE_CFG_REG, + .min_u = 3600000, + .max_u = 4800000, + .step_u = 10000, + }, + .usb_icl = { + .name = "usb input current limit", + .reg = USBIN_CURRENT_LIMIT_CFG_REG, + .min_u = 0, + .max_u = 3000000, + .step_u = 50000, + }, + .dc_icl = { + .name = "dc input current limit", + /* TODO: For now USBIN seems to be the way to set this */ + .reg = USBIN_CURRENT_LIMIT_CFG_REG, + .min_u = 0, + .max_u = 3000000, + .step_u = 50000, + }, + .freq_switcher = { + .name = "switching frequency", + .reg = DCDC_FSW_SEL_REG, + .min_u = 600, + .max_u = 1200, + .step_u = 400, + .set_proc = smblib_set_chg_freq, + }, +}; + +static struct smb_mmi_params smb2_pm660_params = { + .fcc = { + .name = "fast charge current", + .reg = CHGR_FAST_CHARGE_CURRENT_CFG_REG, + .min_u = 0, + .max_u = 4500000, + .step_u = 25000, + }, + .fv = { + .name = "float voltage", + .reg = CHGR_FLOAT_VOLTAGE_CFG_REG, + .min_u = 3487500, + .max_u = 4920000, + .step_u = 7500, + }, + .usb_icl = { + .name = "usb input current limit", + .reg = USBIN_CURRENT_LIMIT_CFG_REG, + .min_u = 0, + .max_u = 4800000, + .step_u = 25000, + }, + .dc_icl = { + .name = "dc input current limit", + .reg = DCIN_CURRENT_LIMIT_CFG_REG, + .min_u = 0, + .max_u = 6000000, + .step_u = 25000, + }, +}; + +int smblib_read_mmi(struct smb_mmi_charger *chg, u16 addr, u8 *val) +{ + unsigned int value; + int rc = 0; + + rc = regmap_read(chg->regmap, addr, &value); + if (rc >= 0) + *val = (u8)value; + + return rc; +} + +int smblib_write_mmi(struct smb_mmi_charger *chg, u16 addr, u8 val) +{ + return regmap_write(chg->regmap, addr, val); +} + +int smblib_masked_write_mmi(struct smb_mmi_charger *chg, u16 addr, u8 mask, u8 val) +{ + return regmap_update_bits(chg->regmap, addr, mask, val); +} + +int smblib_get_apsd_result(struct smb_mmi_charger *chg, int *type) +{ + int rc = 0; + u8 stat; + int apsd; + + rc = smblib_read_mmi(chg, APSD_STATUS_REG, &stat); + if (rc < 0) { + mmi_err(chg, "Couldn't read APSD_STATUS_REG rc=%d\n", rc); + return rc; + } + if (!(stat & APSD_DTC_STATUS_DONE_BIT)) + return -EBUSY; + + rc = smblib_read_mmi(chg, APSD_RESULT_STATUS_REG, &stat); + if (rc < 0) { + mmi_err(chg, "Couldn't read APSD_RESULT_STATUS_REG rc=%d\n", rc); + return rc; + } + + if (stat & QC_3P0_BIT) + apsd = CHG_BC1P2_QC_3P0; + else if (stat & QC_2P0_BIT) + apsd = CHG_BC1P2_QC_2P0; + else if (stat & FLOAT_CHARGER_BIT) + apsd = CHG_BC1P2_FLOAT; + else if (stat & DCP_CHARGER_BIT) + apsd = CHG_BC1P2_DCP; + else if (stat & CDP_CHARGER_BIT) + apsd = CHG_BC1P2_CDP; + else if (stat & OCP_CHARGER_BIT) + apsd = CHG_BC1P2_OCP; + else if (stat & SDP_CHARGER_BIT) + apsd = CHG_BC1P2_SDP; + else + apsd = CHG_BC1P2_UNKNOWN; + + *type = apsd; + + return rc; +} + +#define CHG_SHOW_MAX_SIZE 50 +static ssize_t factory_image_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("SMBMMI: Invalid factory image mode value = %lu\n", mode); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + mmi_chip->is_factory_image = (mode) ? true : false; + + return r ? r : count; +} + +static ssize_t factory_image_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + state = (mmi_chip->is_factory_image) ? 1 : 0; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(factory_image_mode, 0644, + factory_image_mode_show, + factory_image_mode_store); + +static ssize_t factory_charge_upper_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + state = mmi_chip->upper_limit_capacity; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(factory_charge_upper, 0444, + factory_charge_upper_show, + NULL); + +#define MMI_CHIP_MODE_LOWER_LIMIT 35 +#define MMI_CHIP_MODE_UPPER_LIMIT 80 +static ssize_t force_demo_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("SMBMMI: Invalid demo mode value = %lu\n", mode); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + if ((mode >= MMI_CHIP_MODE_LOWER_LIMIT) && + (mode <= MMI_CHIP_MODE_UPPER_LIMIT)) + mmi_chip->demo_mode = mode; + else + mmi_chip->demo_mode = MMI_CHIP_MODE_LOWER_LIMIT; + + return r ? r : count; +} + +static ssize_t force_demo_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + state = mmi_chip->demo_mode; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_demo_mode, 0644, + force_demo_mode_show, + force_demo_mode_store); + +#define MIN_TEMP_C -20 +#define MAX_TEMP_C 60 +#define MIN_MAX_TEMP_C 47 +#define HYSTERESIS_DEGC 2 +static ssize_t force_max_chrg_temp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("SMBMMI: Invalid max temp value = %lu\n", mode); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + if ((mode >= MIN_MAX_TEMP_C) && (mode <= MAX_TEMP_C)) + mmi_chip->max_chrg_temp = mode; + else + mmi_chip->max_chrg_temp = MAX_TEMP_C; + + return r ? r : count; +} + +static ssize_t force_max_chrg_temp_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + state = mmi_chip->max_chrg_temp; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_max_chrg_temp, 0644, + force_max_chrg_temp_show, + force_max_chrg_temp_store); + +#define USBIN_CMD_IL_REG (USBIN_BASE + 0x40) +#define USBIN_SUSPEND_BIT BIT(0) +int smblib_set_usb_suspend(struct smb_mmi_charger *chg, bool suspend) +{ + int rc = 0; + + rc = smblib_masked_write_mmi(chg, USBIN_CMD_IL_REG, USBIN_SUSPEND_BIT, + suspend ? USBIN_SUSPEND_BIT : 0); + if (rc < 0) + mmi_err(chg, "Couldn't write %s to USBIN suspend rc=%d\n", + suspend ? "suspend" : "resume", rc); + + return rc; +} + +int smblib_get_usb_suspend(struct smb_mmi_charger *chg, int *suspend) +{ + int rc = 0; + u8 temp = 0; + + if (chg->smb_version == PM660_SUBTYPE) { + rc = smblib_read_mmi(chg, MISC_POWER_PATH_STATUS_REG, &temp); + } else + rc = smblib_read_mmi(chg, POWER_PATH_STATUS_REG, &temp); + if (rc < 0) { + mmi_err(chg, "Couldn't read POWER_PATH_STATUS_REG rc=%d\n", rc); + return rc; + } + *suspend = temp & USBIN_SUSPEND_STS_BIT; + + return rc; +} + +static ssize_t force_chg_usb_suspend_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("SMBMMI: Invalid usb suspend mode value = %lu\n", mode); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + r = smblib_set_usb_suspend(mmi_chip, (bool)mode); + + return r ? r : count; +} + +static ssize_t force_chg_usb_suspend_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int ret; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + ret = smblib_get_usb_suspend(mmi_chip, &state); + if (ret) { + mmi_err(mmi_chip, "USBIN_SUSPEND_BIT failed ret = %d\n", ret); + state = -EFAULT; + goto end; + } + +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_usb_suspend, 0664, + force_chg_usb_suspend_show, + force_chg_usb_suspend_store); + +static ssize_t force_chg_fail_clear_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("SMBMMI: Invalid chg fail mode value = %lu\n", mode); + return -EINVAL; + } + + /* do nothing for SMBCHG */ + r = 0; + + return r ? r : count; +} + +static ssize_t force_chg_fail_clear_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + /* do nothing for SMBCHG */ + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "0\n"); +} + +static DEVICE_ATTR(force_chg_fail_clear, 0664, + force_chg_fail_clear_show, + force_chg_fail_clear_store); + +#define CHARGING_ENABLE_CMD_REG (CHGR_BASE + 0x42) +#define CHARGING_ENABLE_CMD_BIT BIT(0) +static ssize_t force_chg_auto_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &mode); + if (r) { + pr_err("SMBMMI: Invalid chrg enable value = %lu\n", mode); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + r = smblib_masked_write_mmi(mmi_chip, CHARGING_ENABLE_CMD_REG, + CHARGING_ENABLE_CMD_BIT, + mode ? CHARGING_ENABLE_CMD_BIT : 0); + if (r < 0) { + mmi_err(mmi_chip, "Factory Couldn't %s charging rc=%d\n", + mode ? "enable" : "disable", (int)r); + return r; + } + + return r ? r : count; +} + +static ssize_t force_chg_auto_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int ret; + u8 value; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + state = -ENODEV; + goto end; + } + + ret = smblib_read_mmi(mmi_chip, CHARGING_ENABLE_CMD_REG, &value); + if (ret) { + mmi_err(mmi_chip, "CHG_EN_BIT failed ret = %d\n", ret); + state = -EFAULT; + goto end; + } + + state = (CHARGING_ENABLE_CMD_BIT & value) ? 1 : 0; +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_auto_enable, 0664, + force_chg_auto_enable_show, + force_chg_auto_enable_store); + +int smblib_set_charge_param(struct smb_mmi_charger *chg, + struct smb_mmi_chg_param *param, int val_u) +{ + int rc = 0; + u8 val_raw; + + if (param->set_proc) { + rc = param->set_proc(param, val_u, &val_raw); + if (rc < 0) + return -EINVAL; + } else { + if (val_u > param->max_u || val_u < param->min_u) + mmi_dbg(chg, "%s: %d is out of range [%d, %d]\n", + param->name, val_u, + param->min_u, param->max_u); + + if (val_u > param->max_u) + val_u = param->max_u; + if (val_u < param->min_u) + val_u = param->min_u; + + val_raw = (val_u - param->min_u) / param->step_u; + } + + rc = smblib_write_mmi(chg, param->reg, val_raw); + if (rc < 0) { + mmi_err(chg, "%s: Couldn't write 0x%02x to 0x%04x rc=%d\n", + param->name, val_raw, param->reg, rc); + return rc; + } + + mmi_dbg(chg, "%s = %d (0x%02x)\n", param->name, val_u, val_raw); + + return rc; +} + +int smblib_get_charge_param(struct smb_mmi_charger *chg, + struct smb_mmi_chg_param *param, int *val_u) +{ + int rc = 0; + u8 val_raw; + + rc = smblib_read_mmi(chg, param->reg, &val_raw); + if (rc < 0) { + mmi_err(chg, "%s: Couldn't read from 0x%04x rc=%d\n", + param->name, param->reg, rc); + return rc; + } + + if (param->get_proc) + *val_u = param->get_proc(param, val_raw); + else + *val_u = val_raw * param->step_u + param->min_u; + mmi_dbg(chg, "%s = %d (0x%02x)\n", param->name, *val_u, val_raw); + + return rc; +} + +static ssize_t force_chg_ibatt_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long chg_current; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &chg_current); + if (r) { + pr_err("SMBMMI: Invalid ibatt value = %lu\n", chg_current); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + chg_current *= 1000; /* Convert to uA */ + if(mmi_chip->fcc_votable) { + pmic_vote_force_val_set(mmi_chip->fcc_votable, chg_current); + pmic_vote_force_active_set(mmi_chip->fcc_votable, 1); + } + + return r ? r : count; +} + +static ssize_t force_chg_ibatt_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int ret; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + state = -ENODEV; + goto end; + } + + ret = smblib_get_charge_param(mmi_chip, &mmi_chip->param.fcc, &state); + if (ret < 0) { + mmi_err(mmi_chip, "Factory Couldn't get master fcc rc=%d\n", (int)ret); + return ret; + } + + state /= 1000; /* Convert to mA */ +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_ibatt, 0664, + force_chg_ibatt_show, + force_chg_ibatt_store); + +static ssize_t force_chg_iusb_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long usb_curr; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &usb_curr); + if (r) { + pr_err("SMBMMI: Invalid iusb value = %lu\n", usb_curr); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + usb_curr *= 1000; /* Convert to uA */ + if (mmi_chip->usb_icl_votable) { + pmic_vote_force_val_set(mmi_chip->usb_icl_votable, usb_curr); + pmic_vote_force_active_set(mmi_chip->usb_icl_votable, 1); + } + + r = smblib_masked_write_mmi(mmi_chip, USBIN_ICL_OPTIONS_REG, + USBIN_MODE_CHG_BIT, USBIN_MODE_CHG_BIT); + if (r < 0) + mmi_err(mmi_chip, "Couldn't set USBIN_ICL_OPTIONS r=%d\n", (int)r); + + r = smblib_masked_write_mmi(mmi_chip, USBIN_LOAD_CFG_REG, + ICL_OVERRIDE_AFTER_APSD_BIT, + ICL_OVERRIDE_AFTER_APSD_BIT); + if (r < 0) + mmi_err(mmi_chip, "Couldn't set USBIN_LOAD_CFG rc=%d\n", (int)r); + + r = smblib_masked_write_mmi(mmi_chip, USBIN_AICL_OPTIONS_CFG_REG, + 0xFF, 0x00); + if (r < 0) + mmi_err(mmi_chip, "Couldn't set USBIN_AICL_OPTIONS rc=%d\n", (int)r); + + return r ? r : count; +} + +static ssize_t force_chg_iusb_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int r; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("chip not valid\n"); + state = -ENODEV; + goto end; + } + + r = smblib_get_charge_param(mmi_chip, &mmi_chip->param.usb_icl, &state); + if (r < 0) { + mmi_err(mmi_chip, "Factory Couldn't get usb_icl rc=%d\n", (int)r); + return r; + } + state /= 1000; /* Convert to mA */ +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_iusb, 0664, + force_chg_iusb_show, + force_chg_iusb_store); + +static ssize_t force_chg_idc_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long dc_curr; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &dc_curr); + if (r) { + pr_err("SMBMMI: Invalid idc value = %lu\n", dc_curr); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + dc_curr *= 1000; /* Convert to uA */ + r = smblib_set_charge_param(mmi_chip, &mmi_chip->param.dc_icl, dc_curr); + if (r < 0) { + mmi_err(mmi_chip, "Factory Couldn't set dc icl = %d rc=%d\n", + (int)dc_curr, (int)r); + return r; + } + + return r ? r : count; +} + +static ssize_t force_chg_idc_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int r; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + state = -ENODEV; + goto end; + } + + r = smblib_get_charge_param(mmi_chip, &mmi_chip->param.dc_icl, &state); + if (r < 0) { + mmi_err(mmi_chip, "Factory Couldn't get dc_icl rc=%d\n", (int)r); + return r; + } + state /= 1000; /* Convert to mA */ +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_idc, 0664, + force_chg_idc_show, + force_chg_idc_store); + +#define PRE_CHARGE_CURRENT_CFG_REG (CHGR_BASE + 0x60) +#define PRE_CHARGE_CURRENT_SETTING_MASK 0x07 +#define PRE_CHARGE_CONV_MV 50 +#define PRE_CHARGE_MAX 7 +#define PRE_CHARGE_MIN 100 +#define PRE_CHARGE_SMB2_CONV_MV 25 +#define PRE_CHARGE_SMB2_MAX 0x3F +static ssize_t force_chg_itrick_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long chg_current; + u8 value, mask; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &chg_current); + if (r) { + pr_err("SMBMMI: Invalid pre-charge value = %lu\n", chg_current); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + switch (mmi_chip->smb_version) { + case PM8150B_SUBTYPE: + case PM7250B_SUBTYPE: + case PMI632_SUBTYPE: + mask = PRE_CHARGE_CURRENT_SETTING_MASK; + if (chg_current < PRE_CHARGE_MIN) + chg_current = 0; + else + chg_current -= PRE_CHARGE_MIN; + + chg_current /= PRE_CHARGE_CONV_MV; + + if (chg_current > PRE_CHARGE_MAX) + value = PRE_CHARGE_MAX; + else + value = (u8)chg_current; + break; + case PM660_SUBTYPE: + mask = PRE_CHARGE_SMB2_MAX; + chg_current /= PRE_CHARGE_SMB2_CONV_MV; + + if (chg_current > PRE_CHARGE_SMB2_MAX) + value = PRE_CHARGE_SMB2_MAX; + else + value = (u8)chg_current; + break; + default: + mmi_err(mmi_chip, "Set ITRICK PMIC subtype %d not supported\n", + mmi_chip->smb_version); + return -EINVAL; + } + + r = smblib_masked_write_mmi(mmi_chip, PRE_CHARGE_CURRENT_CFG_REG, + mask, + value); + if (r < 0) { + mmi_err(mmi_chip, "Factory Couldn't set ITRICK %d mV rc=%d\n", + (int)value, (int)r); + return r; + } + + return r ? r : count; +} + +static ssize_t force_chg_itrick_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int ret; + u8 value; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + state = -ENODEV; + goto end; + } + + ret = smblib_read_mmi(mmi_chip, PRE_CHARGE_CURRENT_CFG_REG, &value); + if (ret) { + mmi_err(mmi_chip, "Pre Chg ITrick failed ret = %d\n", ret); + state = -EFAULT; + goto end; + } + + switch (mmi_chip->smb_version) { + case PM8150B_SUBTYPE: + case PM7250B_SUBTYPE: + case PMI632_SUBTYPE: + value &= PRE_CHARGE_CURRENT_SETTING_MASK; + state = (value * PRE_CHARGE_CONV_MV) + PRE_CHARGE_MIN; + break; + case PM660_SUBTYPE: + value &= PRE_CHARGE_SMB2_MAX; + state = value * PRE_CHARGE_SMB2_CONV_MV; + break; + default: + mmi_err(mmi_chip, "Get ITRICK PMIC subtype %d not supported\n", + mmi_chip->smb_version); + return -EINVAL; + } +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_itrick, 0664, + force_chg_itrick_show, + force_chg_itrick_store); + +enum { + MMI_FACTORY_MODE, + MMI_FACTORY_BUILD, +}; + +static bool mmi_factory_check(int type) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool factory = false; + const char *bootargs = NULL; + char *bl_version = NULL; + char *end = NULL; + + if (!np) + return factory; + + switch (type) { + case MMI_FACTORY_MODE: + factory = of_property_read_bool(np, + "mmi,factory-cable"); + break; + case MMI_FACTORY_BUILD: + if (!of_property_read_string(np, "bootargs", &bootargs)) { + bl_version = strstr(bootargs, "androidboot.bootloader="); + if (bl_version) { + end = strpbrk(bl_version, " "); + bl_version = strpbrk(bl_version, "="); + } + if (bl_version && end > bl_version && + strnstr(bl_version, "factory", end - bl_version)) { + factory = true; + } + } + break; + default: + factory = false; + } + of_node_put(np); + + return factory; +} + +#define CHARGER_POWER_15W 15000 +#define CHARGER_POWER_18W 18000 +#define CHARGER_POWER_20W 20000 +static void mmi_charger_power_support(struct smb_mmi_charger *chg) +{ + struct device_node *np = chg->dev->of_node; + const char *charger_ability = NULL; + int retval; + + retval = of_property_read_u32(np, "qcom,pd-power-max", + &chg->pd_power_max); + if (retval) { + chg->pd_power_max = 0; + } + + chg->pd_handle = + devm_usbpd_get_by_phandle(chg->dev, "qcom,usbpd-phandle"); + if (IS_ERR_OR_NULL(chg->pd_handle)) { + dev_err(chg->dev, "Error getting the pd phandle %ld\n", + PTR_ERR(chg->pd_handle)); + chg->pd_handle = NULL; + } + + if (of_property_read_bool(np, "qcom,force-hvdcp-5v")) { + chg->hvdcp_power_max = CHARGER_POWER_15W; + return; + } + + retval = of_property_read_u32(np, "qcom,hvdcp-power-max", + &chg->hvdcp_power_max); + if (retval) { + chg->hvdcp_power_max = 0; + return; + } + + np = of_find_node_by_path("/chosen"); + + if (!np) + return; + + retval = of_property_read_string(np, "mmi,charger", + &charger_ability); + + if ((retval == -EINVAL) || !charger_ability) { + mmi_err(chg, "mmi,charger unused\n"); + of_node_put(np); + return; + + } else + mmi_info(chg, "charger = %s\n", charger_ability); + + if (strstr(charger_ability, "15W")) + chg->hvdcp_power_max = CHARGER_POWER_15W; + else if (strstr(charger_ability, "18W")) + chg->hvdcp_power_max = CHARGER_POWER_18W; + else if (strstr(charger_ability, "20W")) + chg->hvdcp_power_max = CHARGER_POWER_20W; + + of_node_put(np); + + return; +} + +static enum power_supply_property smb_mmi_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_UPDATE_NOW, + POWER_SUPPLY_PROP_USB_OTG, +}; + +static int smb_mmi_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct smb_mmi_charger *chip = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + val->intval = -EINVAL; + break; + case POWER_SUPPLY_PROP_UPDATE_NOW: + val->intval = chip->gen_log_rate_s; + break; + case POWER_SUPPLY_PROP_USB_OTG: + if (chip->vbus && + regulator_is_enabled(chip->vbus) && + chip->vbus_enabled) + val->intval = 1; + else + val->intval = 0; + break; + default: + return -EINVAL; + } + return 0; +} + +static int smb_mmi_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct smb_mmi_charger *chip = power_supply_get_drvdata(psy); + int rc = 0; + int override = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_CURRENT_MAX: + if (val->intval) { + rc = power_supply_set_property(chip->usb_psy, + POWER_SUPPLY_PROP_CTM_CURRENT_MAX, + val); + rc = power_supply_set_property(chip->main_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, + val); + if (chip->smb_version == PM660_SUBTYPE) + override = ICL_OVERRIDE_BIT; + else + override = USBIN_ICL_OVERRIDE_BIT; + } + mmi_warn(chip, "Request for ICL to %d uA\n", val->intval); + + if (chip->smb_version == PM660_SUBTYPE) { + rc = smblib_masked_write_mmi(chip, CMD_APSD_REG, + ICL_OVERRIDE_BIT, + override); + if (rc < 0) + mmi_err(chip, + "Couldn't set ICL_OVERRIDE_BIT rc=%d\n", rc); + } else { + rc = smblib_masked_write_mmi(chip, + USBIN_CMD_ICL_OVERRIDE_REG, + USBIN_ICL_OVERRIDE_BIT, + override); + if (rc < 0) + mmi_err(chip, + "Couldn't set USBIN_CMD_ICL_OVERRIDE rc=%d\n", rc); + } + break; + case POWER_SUPPLY_PROP_UPDATE_NOW: + chip->gen_log_rate_s = val->intval; + power_supply_changed(psy); + break; + case POWER_SUPPLY_PROP_USB_OTG: + if (!chip->vbus) { + chip->vbus = devm_regulator_get(chip->dev, "vbus"); + if (IS_ERR(chip->vbus)) { + mmi_err(chip, "Unable to get vbus\n"); + return -EINVAL; + } + } + + if (val->intval) { + rc = regulator_enable(chip->vbus); + mmi_info(chip, "VBUS Enable\n"); + } else if (chip->vbus_enabled) { + rc = regulator_disable(chip->vbus); + mmi_info(chip, "VBUS Disable\n"); + } + + if (rc) + mmi_err(chip, "Unable to %s vbus (%d)\n", + val->intval ? "enable" : "disable", rc); + else if (val->intval) + chip->vbus_enabled = true; + else + chip->vbus_enabled = false; + + break; + default: + rc = -EINVAL; + } + + return rc; +} + +static bool mmi_find_temp_zone(struct smb_mmi_charger *chg, + struct mmi_sm_params *chip, + int temp_c) +{ + int prev_zone, num_zones; + struct mmi_temp_zone *zones; + int hotter_t, hotter_fcc; + int colder_t, colder_fcc; + int i; + int max_temp; + + if (!chg) { + pr_debug("SMBMMI: called before chg valid!\n"); + return false; + } + + zones = chip->temp_zones; + num_zones = chip->num_temp_zones; + prev_zone = chip->pres_temp_zone; + + if (chg->max_chrg_temp >= MIN_MAX_TEMP_C) + max_temp = chg->max_chrg_temp; + else + max_temp = zones[num_zones - 1].temp_c; + + if (prev_zone == ZONE_NONE) { + for (i = num_zones - 1; i >= 0; i--) { + if (temp_c >= zones[i].temp_c) { + if (i == num_zones - 1) + chip->pres_temp_zone = ZONE_HOT; + else + chip->pres_temp_zone = i + 1; + return true; + } + } + chip->pres_temp_zone = ZONE_COLD; + return true; + } + + if (prev_zone == ZONE_COLD) { + if (temp_c >= MIN_TEMP_C + HYSTERESIS_DEGC) + chip->pres_temp_zone = ZONE_FIRST; + } else if (prev_zone == ZONE_HOT) { + if (temp_c <= max_temp - HYSTERESIS_DEGC) + chip->pres_temp_zone = num_zones - 1; + } else { + if (prev_zone == ZONE_FIRST) { + hotter_fcc = zones[prev_zone + 1].fcc_max_ma; + colder_fcc = 0; + hotter_t = zones[prev_zone].temp_c; + colder_t = MIN_TEMP_C; + } else if (prev_zone == num_zones - 1) { + hotter_fcc = 0; + colder_fcc = zones[prev_zone - 1].fcc_max_ma; + hotter_t = zones[prev_zone].temp_c; + colder_t = zones[prev_zone - 1].temp_c; + } else { + hotter_fcc = zones[prev_zone + 1].fcc_max_ma; + colder_fcc = zones[prev_zone - 1].fcc_max_ma; + hotter_t = zones[prev_zone].temp_c; + colder_t = zones[prev_zone - 1].temp_c; + } + + if (zones[prev_zone].fcc_max_ma < hotter_fcc) + hotter_t += HYSTERESIS_DEGC; + + if (zones[prev_zone].fcc_max_ma < colder_fcc) + colder_t -= HYSTERESIS_DEGC; + + if (temp_c < MIN_TEMP_C) + chip->pres_temp_zone = ZONE_COLD; + else if (temp_c >= max_temp) + chip->pres_temp_zone = ZONE_HOT; + else if (temp_c >= hotter_t) + chip->pres_temp_zone++; + else if (temp_c < colder_t) + chip->pres_temp_zone--; + } + + if (prev_zone != chip->pres_temp_zone) { + mmi_dbg(chg, "Entered Temp Zone %d!\n", + chip->pres_temp_zone); + return true; + } + + return false; +} + +static int get_prop_batt_voltage_now(struct smb_mmi_charger *chg, + struct power_supply *psy, + union power_supply_propval *val) +{ + int rc; + + if (!psy) + return -EINVAL; + + rc = power_supply_get_property(psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, val); + return rc; +} + +static int get_prop_batt_current_now(struct smb_mmi_charger *chg, + struct power_supply *psy, + union power_supply_propval *val) +{ + int rc; + + if (!psy) + return -EINVAL; + + rc = power_supply_get_property(psy, + POWER_SUPPLY_PROP_CURRENT_NOW, val); + return rc; +} + +static int get_prop_usb_voltage_now(struct smb_mmi_charger *chg, + union power_supply_propval *val) +{ + int rc; + + if (!chg->usb_psy) + return -EINVAL; + + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, val); + return rc; +} + +static int get_prop_dc_voltage_now(struct smb_mmi_charger *chg, + union power_supply_propval *val) +{ + int rc; + + if (!chg->dc_psy) + return -EINVAL; + + rc = power_supply_get_property(chg->dc_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, val); + return rc; +} + +static int get_prop_batt_temp(struct smb_mmi_charger *chg, + struct power_supply *psy, + union power_supply_propval *val) +{ + int rc; + + if (!psy) + return -EINVAL; + + rc = power_supply_get_property(psy, + POWER_SUPPLY_PROP_TEMP, val); + return rc; +} + +static int get_prop_batt_capacity(struct smb_mmi_charger *chg, + struct power_supply *psy, + union power_supply_propval *val) +{ + int rc = -EINVAL; + + if (psy) + rc = power_supply_get_property(psy, + POWER_SUPPLY_PROP_CAPACITY, val); + return rc; +} + +static int get_prop_usb_present(struct smb_mmi_charger *chg, + union power_supply_propval *val) +{ + int rc; + u8 stat; + + rc = smblib_read_mmi(chg, USBIN_INT_RT_STS, &stat); + if (rc < 0) { + mmi_err(chg, "Couldn't read USBIN_RT_STS rc=%d\n", rc); + return rc; + } + + val->intval = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); + + return 0; +} + +static int get_prop_dc_present(struct smb_mmi_charger *chg, + union power_supply_propval *val) +{ + int rc; + u8 stat; + + /* + * PMI632 has no DCIN support and address at 0x14XX is undefined, + * so just return 0 to avoid too much err log and heatbeat_work + * break + */ + if (chg->smb_version == PMI632_SUBTYPE) { + val->intval = 0; + return 0; + } + + rc = smblib_read_mmi(chg, DCIN_INT_RT_STS, &stat); + if (rc < 0) { + mmi_err(chg, "Couldn't read DCIN_RT_STS rc=%d\n", rc); + return rc; + } + + val->intval = (bool)(stat & DCIN_PLUGIN_RT_STS_BIT); + + return 0; +} + +static int get_prop_charger_present(struct smb_mmi_charger *chg, + union power_supply_propval *val) +{ + int rc = -EINVAL; + + val->intval = 0; + + if (chg->usb_psy) { + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_ONLINE, val); + if (rc < 0) + mmi_err(chg, "Couldn't read USB online rc=%d\n", rc); + else if (val->intval) + return rc; + } + + if (chg->pc_port_psy) { + rc = power_supply_get_property(chg->pc_port_psy, + POWER_SUPPLY_PROP_ONLINE, val); + if (rc < 0) + mmi_err(chg, "Couldn't read PC online rc=%d\n", rc); + else if (val->intval) + return rc; + } + + if (chg->usb_psy) { + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_TYPEC_MODE, val); + if (rc < 0) + mmi_err(chg, "Couldn't read typec mode rc=%d\n", rc); + else if (val->intval == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT || + val->intval == POWER_SUPPLY_TYPEC_SOURCE_MEDIUM || + val->intval == POWER_SUPPLY_TYPEC_SOURCE_HIGH) { + val->intval = 1; + return rc; + } + } + + if (chg->wls_psy) { + rc = power_supply_get_property(chg->wls_psy, + POWER_SUPPLY_PROP_ONLINE, val); + if (rc < 0) + mmi_err(chg, "Couldn't read wls online rc=%d\n", rc); + else if (val->intval) + return rc; + } + + //intval can be changed by get_property call we should reset to 0 + //this is true for a OTG cable + val->intval = 0; + return rc; +} + +#define USBIN_500MA 500000 +#define USBIN_900MA 900000 +int mmi_usb_icl_override(struct smb_mmi_charger *chg, int icl) +{ + int rc = 0; + int apsd; + u8 usb51_mode, icl_override, apsd_override; + union power_supply_propval val; + + if (chg->factory_mode) + return rc; + + if (chg->usb_psy) { + val.intval = POWER_SUPPLY_TYPEC_NONE; + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_TYPEC_MODE, &val); + if (rc < 0 || val.intval != POWER_SUPPLY_TYPEC_NONE) + return rc; + } + + rc = smblib_get_apsd_result(chg, &apsd); + if (rc < 0 || apsd == CHG_BC1P2_UNKNOWN) + return rc; + + if (apsd == CHG_BC1P2_SDP && + (icl == USBIN_900MA || icl <= USBIN_500MA)) { + usb51_mode = 0; + apsd_override = 0; + if (chg->smb_version == PM660_SUBTYPE) + icl_override = ICL_OVERRIDE_BIT; + else + icl_override = USBIN_ICL_OVERRIDE_BIT; + } else { + usb51_mode = USBIN_MODE_CHG_BIT; + apsd_override = ICL_OVERRIDE_AFTER_APSD_BIT; + if (chg->smb_version == PM660_SUBTYPE) + icl_override = ICL_OVERRIDE_BIT; + else + icl_override = USBIN_ICL_OVERRIDE_BIT; + } + + if (chg->main_psy) { + val.intval = icl; + rc = power_supply_set_property(chg->main_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, + &val); + if (rc < 0) { + mmi_err(chg, "Couldn't set usb icl, rc=%d\n", rc); + } + } + + rc = smblib_masked_write_mmi(chg, USBIN_ICL_OPTIONS_REG, + USBIN_MODE_CHG_BIT, + usb51_mode); + if (rc < 0) + mmi_err(chg, + "Couldn't set USBIN_ICL_OPTIONS rc=%d\n", rc); + + if (chg->smb_version == PM660_SUBTYPE) { + rc = smblib_masked_write_mmi(chg, CMD_APSD_REG, + ICL_OVERRIDE_BIT, + icl_override); + if (rc < 0) + mmi_err(chg, + "Couldn't set ICL_OVERRIDE_BIT rc=%d\n", rc); + } else { + rc = smblib_masked_write_mmi(chg, USBIN_CMD_ICL_OVERRIDE_REG, + USBIN_ICL_OVERRIDE_BIT, + icl_override); + if (rc < 0) + mmi_err(chg, + "Couldn't set USBIN_CMD_ICL_OVERRIDE rc=%d\n", rc); + } + + rc = smblib_masked_write_mmi(chg, USBIN_LOAD_CFG_REG, + ICL_OVERRIDE_AFTER_APSD_BIT, + apsd_override); + if (rc < 0) + mmi_err(chg, + "Couldn't set USBIN_LOAD_CFG rc=%d\n", rc); + + return rc; +} + +#define HVDCP_VOLTAGE_BASIC (this_chip->hvdcp_power_max / 3) +#define HVDCP_VOLTAGE_NOM (HVDCP_VOLTAGE_BASIC - 200) +#define HVDCP_VOLTAGE_MAX (HVDCP_VOLTAGE_BASIC + 200) +#define HVDCP_VOLTAGE_MIN 4000 +#define HVDCP_PULSE_COUNT_MAX ((HVDCP_VOLTAGE_BASIC - 5000) / 200 + 2) +static int mmi_increase_vbus_power(struct smb_mmi_charger *chg, int cur_mv) +{ + int rc = -EINVAL; + union power_supply_propval val = {0, }; + int pulse_cnt; + bool usb_present = false; + union power_supply_propval charger_val = {0, }; + + rc = power_supply_get_property(chg->qcom_psy, + POWER_SUPPLY_PROP_DP_DM, &val); + if (rc < 0) { + mmi_err(chg, "Couldn't read dpdm pulse count rc=%d\n", rc); + return cur_mv; + } else { + mmi_info(chg, "DP DM pulse count = %d\n", val.intval); + pulse_cnt = val.intval; + } + + /* Maintain vbus voltage for QC3.0 power/3A charging */ + if (pulse_cnt == 0 || chg->inc_hvdcp_cnt != HVDCP_PULSE_COUNT_MAX) { + + chg->inc_hvdcp_cnt = HVDCP_PULSE_COUNT_MAX; + vote(chg->chg_dis_votable, MMI_HB_VOTER, true, 0); + + while (cur_mv < HVDCP_VOLTAGE_NOM && pulse_cnt < chg->inc_hvdcp_cnt) { + val.intval = POWER_SUPPLY_DP_DM_DP_PULSE; + rc = power_supply_set_property(chg->qcom_psy, + POWER_SUPPLY_PROP_DP_DM, &val); + if (rc < 0) { + mmi_err(chg, "Couldn't set dpdm pulse rc=%d\n", rc); + break; + } + + msleep(100); + + rc = get_prop_usb_voltage_now(chg, &val); + if (rc < 0) { + mmi_err(chg, "Error getting USB Voltage rc = %d\n", rc); + break; + } else + cur_mv = val.intval / 1000; + + rc = power_supply_get_property(chg->qcom_psy, + POWER_SUPPLY_PROP_DP_DM, &val); + if (rc < 0) { + mmi_err(chg, "Couldn't read dpdm pulse count rc=%d\n", rc); + break; + } else { + pulse_cnt = val.intval; + } + + rc = get_prop_usb_present(chg,&charger_val); + if (rc < 0){ + mmi_err(chg, "Couldn't read usb_present rc=%d\n", rc); + }else + usb_present = charger_val.intval; + + if(cur_mv <= 3000 && usb_present == false ){ + mmi_err(chg, "%s, charger detect error, exit increase voltage\n", __func__); + break; + } + + mmi_info(chg, "pulse count = %d, cur_mv = %d\n", pulse_cnt, cur_mv); + } + + rc = smblib_set_charge_param(chg, &chg->param.aicl_cont_threshold, + HVDCP_VOLTAGE_NOM - 1000); + if (rc < 0) { + mmi_err(chg, "Couldn't set aicl cont threshold to 9V rc=%d\n", rc); + } +#ifdef CONFIG_QC3P_PUMP_SUPPORT + if(cur_mv < 5600) { + mmi_err(chg, "voltage increase failed, reset aicl threshold otherwise charging failure\n"); + rc = smblib_set_charge_param(chg, &chg->param.aicl_cont_threshold, 4500); + if (rc < 0) { + mmi_err(chg, "Couldn't set aicl cont threshold rc=%d\n", rc); + } + } +#endif + vote(chg->chg_dis_votable, MMI_HB_VOTER, false, 0); + } + + return cur_mv; +} + +static ssize_t force_hvdcp_power_max_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long power; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &power); + if (r) { + pr_err("SMBMMI: Invalid hvdcp_power_max value = %lu\n", power); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + if ((power >= CHARGER_POWER_15W) && + (power <= CHARGER_POWER_20W) && + (mmi_chip->hvdcp_power_max != power)) { + mmi_chip->hvdcp_power_max = power; + + r = smblib_masked_write_mmi(mmi_chip, HVDCP_PULSE_COUNT_MAX_REG, + HVDCP_PULSE_COUNT_MAX_QC2_MASK | + HVDCP_PULSE_COUNT_MAX_QC3_MASK, + HVDCP_PULSE_COUNT_MAX); + if (r < 0) { + mmi_err(mmi_chip, "Could not set HVDCP pulse count max\n"); + return r; + } + cancel_delayed_work(&mmi_chip->heartbeat_work); + schedule_delayed_work(&mmi_chip->heartbeat_work, + msecs_to_jiffies(100)); + mmi_info(mmi_chip, "Reset hvdcp power max as %d, " + "Reschedule heartbeat\n", + mmi_chip->hvdcp_power_max); + } + + return r ? r : count; +} + +static ssize_t force_hvdcp_power_max_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int power; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + power = mmi_chip->hvdcp_power_max; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", power); +} + +static DEVICE_ATTR(force_hvdcp_power_max, 0644, + force_hvdcp_power_max_show, + force_hvdcp_power_max_store); + +static ssize_t force_pd_power_max_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long power; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + r = kstrtoul(buf, 0, &power); + if (r) { + pr_err("SMBMMI: Invalid pd_power_max value = %lu\n", power); + return -EINVAL; + } + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + if ((power >= CHARGER_POWER_15W) && + (power <= CHARGER_POWER_20W) && + (mmi_chip->pd_power_max != power)) { + mmi_chip->pd_power_max = power; + + cancel_delayed_work(&mmi_chip->heartbeat_work); + schedule_delayed_work(&mmi_chip->heartbeat_work, + msecs_to_jiffies(100)); + mmi_info(mmi_chip, "Reset pd power max as %d, " + "Reschedule heartbeat\n", + mmi_chip->pd_power_max); + } + + return r ? r : count; +} + +static ssize_t force_pd_power_max_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int power; + struct platform_device *pdev = to_platform_device(dev); + struct smb_mmi_charger *mmi_chip = platform_get_drvdata(pdev); + + if (!mmi_chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + power = mmi_chip->pd_power_max; + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", power); +} + +static DEVICE_ATTR(force_pd_power_max, 0644, + force_pd_power_max_show, + force_pd_power_max_store); + +#ifdef CONFIG_PD_CHARGER_MMI +static int smblib_set_opt_switcher_freq(struct smb_mmi_charger *chg, int fsw_khz) +{ + int rc = 0; + + rc = smblib_set_charge_param(chg, &chg->param.freq_switcher, fsw_khz); + if (rc < 0) + mmi_err(chg, "Error in setting freq_buck rc=%d\n", rc); + + return rc; +} + +#define MICRO_1V 1000000 +#define MICRO_5V 5000000 +#define MICRO_9V 9000000 +#define MICRO_12V 12000000 +static int smblib_set_usb_pd_fsw(struct smb_mmi_charger *chg, int voltage) +{ + int rc = 0; + + if (voltage == MICRO_5V) + rc = smblib_set_opt_switcher_freq(chg, chg->chg_freq.freq_5V); + else if (voltage > MICRO_5V && voltage < MICRO_9V) + rc = smblib_set_opt_switcher_freq(chg, + chg->chg_freq.freq_6V_8V); + else if (voltage >= MICRO_9V && voltage < MICRO_12V) + rc = smblib_set_opt_switcher_freq(chg, chg->chg_freq.freq_9V); + else if (voltage == MICRO_12V) + rc = smblib_set_opt_switcher_freq(chg, chg->chg_freq.freq_12V); + else { + mmi_err(chg, "Couldn't set Fsw: invalid voltage %d\n", + voltage); + return -EINVAL; + } + + return rc; +} + +#define PD_SRC_PDO_TYPE_FIXED 0 +#define PD_SRC_PDO_TYPE_BATTERY 1 +#define PD_SRC_PDO_TYPE_VARIABLE 2 +#define PD_SRC_PDO_TYPE_AUGMENTED 3 +#define PPS_VOLT_MIN 5000000 +#define PPS_CURR_MIN 2000000 +#define PPS_CURR_MAX 3000000 +static void mmi_chrg_usb_vin_pd_config(struct smb_mmi_charger *chg, int vbus_mv) +{ + int rc = -EINVAL, i =0, req_pdo = -1, req_pd_volt = 0, req_pd_curr = 0; + int fixed_pd_volt = 0; + union power_supply_propval val = {0, }; + bool pd_active = false, vbus_present = false, pps_active = false, fixed_active = false; + static bool fsw_setted = false, fixed_power_done = false; + + if (!chg->usb_psy || !chg->pd_handle) + return; + + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &val); + if (rc) { + mmi_err(chg, "Unable to read USB PRESENT: %d\n", rc); + return; + } + vbus_present = val.intval; + + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_PD_ACTIVE, &val); + if (rc) { + mmi_err(chg, "Unable to read PD ACTIVE: %d\n", rc); + return; + } + pd_active = val.intval; + + if (!pd_active || !vbus_present) { + fsw_setted = false; + fixed_power_done = false; + return; + } + + memset(chg->mmi_pdo_info, 0, + sizeof(struct usbpd_pdo_info) * PD_MAX_PDO_NUM); + + rc = usbpd_get_pdo_info(chg->pd_handle, chg->mmi_pdo_info, PD_MAX_PDO_NUM); + if (rc ) { + mmi_err(chg, "PD not ready, %d\n", rc); + return; + } + + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + if ((chg->mmi_pdo_info[i].type == PD_SRC_PDO_TYPE_AUGMENTED) + && chg->mmi_pdo_info[i].uv_max >= PPS_VOLT_MIN + && chg->mmi_pdo_info[i].ua >= PPS_CURR_MIN + && (chg->mmi_pdo_info[i].uv_max / 1000) * (chg->mmi_pdo_info[i].ua / 1000) / 1000 + >= chg->pd_power_max) { + req_pdo = chg->mmi_pdo_info[i].pdo_pos; + req_pd_curr = chg->mmi_pdo_info[i].ua; + + if (req_pd_curr > PPS_CURR_MAX) + req_pd_curr = PPS_CURR_MAX; + + req_pd_volt = (chg->pd_power_max * 1000 / (req_pd_curr / 1000)) * 1000 % 20000; + req_pd_volt = (chg->pd_power_max * 1000 / (req_pd_curr / 1000)) * 1000 - req_pd_volt; + req_pd_volt = max(req_pd_volt, PPS_VOLT_MIN); + req_pd_volt = min(req_pd_volt, chg->mmi_pdo_info[i].uv_max); + pps_active = true; + break; + } + } + + if (!pps_active) { + + if (chg->pd_power_max < CHARGER_POWER_18W) + fixed_pd_volt = MICRO_5V; + else + fixed_pd_volt = MICRO_9V; + + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + if ((chg->mmi_pdo_info[i].type == + PD_SRC_PDO_TYPE_FIXED) + && chg->mmi_pdo_info[i].uv_max <= fixed_pd_volt + && chg->mmi_pdo_info[i].uv_max >= MICRO_5V) { + req_pdo = chg->mmi_pdo_info[i].pdo_pos; + req_pd_volt = chg->mmi_pdo_info[i].uv_max; + req_pd_curr = (chg->pd_power_max / (req_pd_volt / 1000)) * 1000; + fixed_active = true; + } + } + + if (fixed_active == true && vbus_mv * 1000 < req_pd_volt - MICRO_1V) + fixed_power_done = false; + } + + if (!pps_active && !fixed_active) + return; + else if (!fsw_setted){ + smblib_set_usb_pd_fsw(chg, req_pd_volt); + fsw_setted = true; + } + + if (!fixed_power_done && fixed_active) { + rc = usbpd_select_pdo(chg->pd_handle, req_pdo, req_pd_volt, req_pd_curr); + if (rc < 0) { + mmi_err(chg, "select pdo failed\n"); + return; + } + val.intval = req_pd_curr * 1000; /* mA->uA */ + rc = power_supply_set_property(chg->usb_psy, + POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val); + if (rc) { + mmi_err(chg, "set PD current MAX failed\n"); + return; + } + fixed_power_done = true; + mmi_err(chg, "Request fixed power , re-vote USB_ICL %duA\n", val.intval); + } else if (pps_active){ + rc = usbpd_select_pdo(chg->pd_handle, req_pdo, req_pd_volt, req_pd_curr); + if (rc < 0) { + mmi_err(chg, "select pdo failed\n"); + return; + } + } + + mmi_err(chg, + "Request PD power, fixed %d, pps %d, pdo %d, " + "req volt %dmV, req curr %dmA, vbus %dmV\n", + fixed_active, + pps_active, + req_pdo, + req_pd_volt, + req_pd_curr, + vbus_mv); + return; +} +#endif + +static void mmi_chrg_usb_vin_config(struct smb_mmi_charger *chg, int cur_mv) +{ + int rc = -EINVAL; + union power_supply_propval val = {0, }; + + if (!chg->usb_psy || !chg->qcom_psy) + return; + + if (cur_mv < HVDCP_VOLTAGE_MIN) { +#ifdef CONFIG_QC3P_PUMP_SUPPORT + chg->hvdcp_apsd_count = 0; +#endif + return; + } + + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_PD_ACTIVE, &val); + if (rc < 0) { + mmi_err(chg, "Couldn't read PD active rc=%d\n", rc); + } else if (val.intval) { + mmi_dbg(chg, "Skip usb vbus voltage config for PD charger\n"); + return; + } + + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_REAL_TYPE, &val); + if (rc < 0) { + mmi_err(chg, "Couldn't read charger type rc=%d\n", rc); + return; + } + + if (val.intval != POWER_SUPPLY_TYPE_USB_HVDCP_3) { +#ifdef CONFIG_QC3P_PUMP_SUPPORT + chg->hvdcp_apsd_count = 0; +#endif + return; + } + + if (chg->hvdcp_power_max > CHARGER_POWER_15W) { +#ifdef CONFIG_QC3P_PUMP_SUPPORT + if(chg->hvdcp_apsd_count == 0) { + chg->hvdcp_apsd_count = 1; + mmi_info(chg, "Charge pump shalcomm adapter workaround APSD twice , skip increase volt once\n"); + return; + } +#endif + mmi_increase_vbus_power(chg, cur_mv); + return; + } + + /* Maintain vbus voltage for QC3.0 5V/3A 15W charging */ + rc = power_supply_get_property(chg->qcom_psy, + POWER_SUPPLY_PROP_DP_DM, &val); + if (rc < 0) { + mmi_err(chg, "Couldn't read dpdm pulse count rc=%d\n", rc); + return; + } else + mmi_info(chg, "DP DM pulse count = %d\n", val.intval); + + if (cur_mv < HVDCP_VOLTAGE_NOM && val.intval < chg->inc_hvdcp_cnt) + val.intval = POWER_SUPPLY_DP_DM_DP_PULSE; + else if (cur_mv > HVDCP_VOLTAGE_MAX && val.intval > 0) + val.intval = POWER_SUPPLY_DP_DM_DM_PULSE; + else { + mmi_dbg(chg, "No need to change hvdcp voltage\n"); + return; + } + rc = power_supply_set_property(chg->qcom_psy, + POWER_SUPPLY_PROP_DP_DM, &val); + if (rc < 0) { + mmi_err(chg, "Couldn't set dpdm pulse rc=%d\n", rc); + return; + } + power_supply_changed(chg->usb_psy); +} + +static void mmi_chrg_input_config(struct smb_mmi_charger *chg, + struct smb_mmi_chg_status *stat) +{ + if (chg->hvdcp_power_max) + mmi_chrg_usb_vin_config(chg, stat->usb_mv); + +#ifdef CONFIG_PD_CHARGER_MMI + if (chg->pd_power_max) + mmi_chrg_usb_vin_pd_config(chg, stat->usb_mv); +#endif +} + +static void mmi_weakcharger_work(struct work_struct *work) +{ + struct smb_mmi_charger *chip = container_of(work, + struct smb_mmi_charger, + weakcharger_work.work); + + set_bit(EVENT_WEAK_CHARGER_WAIT, &chip->flags); + cancel_delayed_work(&chip->heartbeat_work); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(100)); + + mmi_dbg(chip, "Weak timer expired\n"); +} + +int is_wls_online(struct smb_mmi_charger *chg) +{ + int rc; + union power_supply_propval val; + + if (!chg->wls_psy) + return 0; + + rc = power_supply_get_property(chg->wls_psy, + POWER_SUPPLY_PROP_ONLINE, &val); + if (rc < 0) { + mmi_err(chg, "Error wls online rc = %d\n", rc); + return 0; + } + + return val.intval; +} + +#define WEAK_CHRG_THRSH 450 +#define TURBO_CHRG_THRSH 2500 +#define PD_CHRG_THRSH 8500 +void mmi_chrg_rate_check(struct smb_mmi_charger *chg) +{ + union power_supply_propval val; + union power_supply_propval wls_val; + int chrg_v_mv = 0; + int chrg_cm_ma = 0; + int chrg_cs_ma = 0; + int rc = -EINVAL; + + if (!chg->usb_psy) { + mmi_err(chg, "No usb PSY\n"); + return; + } + + val.intval = 0; + chg->prev_chg_rate = chg->charger_rate; + + rc = get_prop_charger_present(chg, &val); + if (rc < 0) { + mmi_err(chg, "Error getting Charger Present rc = %d\n", rc); + return; + } + + if (val.intval && is_wls_online(chg)) { + rc = power_supply_get_property(chg->wls_psy, + POWER_SUPPLY_PROP_CHARGE_TYPE, &wls_val); + if (rc >= 0 && wls_val.intval == POWER_SUPPLY_CHARGE_TYPE_FAST) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_TURBO; + else + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_NORMAL; + goto end_rate_check; + } + + if (val.intval) { + val.intval = 0; + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_HW_CURRENT_MAX, &val); + if (rc < 0) { + mmi_err(chg, "Error getting HW Current Max rc = %d\n", rc); + return; + } + chrg_cm_ma = val.intval / 1000; + + val.intval = 0; + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, &val); + if (rc < 0) { + mmi_err(chg, "Error getting ICL Settled rc = %d\n", rc); + return; + } + chrg_cs_ma = val.intval / 1000; + + rc = get_prop_usb_voltage_now(chg, &val); + if (rc != -ENOMEM && rc < 0) { + mmi_err(chg, "Error getting USB Input Voltage rc = %d\n", rc); + return; + } + chrg_v_mv = val.intval / 1000; + } else { + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_NONE; + goto end_rate_check; + } + + mmi_dbg(chg, "cm %d, cs %d, v %d\n", chrg_cm_ma, chrg_cs_ma, chrg_v_mv); + if (chrg_cm_ma >= TURBO_CHRG_THRSH || chrg_v_mv >= PD_CHRG_THRSH) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_TURBO; + else if ((chrg_cm_ma > WEAK_CHRG_THRSH) && + (chrg_cs_ma < WEAK_CHRG_THRSH) && + test_bit(EVENT_WEAK_CHARGER_WAIT, &chg->flags)) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_WEAK; + else if (chg->prev_chg_rate == POWER_SUPPLY_CHARGE_RATE_NONE) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_NORMAL; + else if ((chg->prev_chg_rate == POWER_SUPPLY_CHARGE_RATE_WEAK) && + (chrg_cm_ma > WEAK_CHRG_THRSH) && + (chrg_cs_ma >= WEAK_CHRG_THRSH)) + chg->charger_rate = POWER_SUPPLY_CHARGE_RATE_NORMAL; + +end_rate_check: + if (chg->prev_chg_rate != chg->charger_rate) { + clear_bit(EVENT_WEAK_CHARGER_WAIT, &chg->flags); + cancel_delayed_work(&chg->weakcharger_work); + if(chg->charger_rate == POWER_SUPPLY_CHARGE_RATE_NORMAL) + schedule_delayed_work(&chg->weakcharger_work, + msecs_to_jiffies(WEAK_CHARGER_WAIT)); + + mmi_info(chg, "%s Charger Detected\n", + charge_rate[chg->charger_rate]); + } +} + +#define TAPER_COUNT 2 +#define TAPER_DROP_MA 100 +static bool mmi_has_current_tapered(int batt, struct smb_mmi_charger *chg, + struct mmi_sm_params *chip, + int batt_ma, int taper_ma) +{ + bool change_state = false; + int allowed_fcc, target_ma; + + if (!chg) { + mmi_dbg(chg, "called before chip valid!\n"); + return false; + } + + allowed_fcc = get_effective_result(chg->fcc_votable) / 1000; + + if (allowed_fcc >= taper_ma) + target_ma = taper_ma; + else + target_ma = allowed_fcc - TAPER_DROP_MA; + + if (batt == BASE_BATT) + batt_ma *= -1; + + if (batt_ma < 0) { + if (chip->chrg_taper_cnt >= TAPER_COUNT) { + change_state = true; + chip->chrg_taper_cnt = 0; + } else + chip->chrg_taper_cnt++; + } else { + if (batt_ma <= target_ma) + if (chip->chrg_taper_cnt >= TAPER_COUNT) { + change_state = true; + chip->chrg_taper_cnt = 0; + } else + chip->chrg_taper_cnt++; + else + chip->chrg_taper_cnt = 0; + } + + return change_state; +} + +bool mmi_charge_halted(struct smb_mmi_charger *chg) +{ + u8 stat; + int rc; + bool flag = false; + + rc = smblib_read_mmi(chg, BATTERY_CHARGER_STATUS_1_REG, &stat); + if (rc < 0) { + mmi_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", + rc); + return flag; + } + stat = stat & BATTERY_CHARGER_STATUS_MASK; + + if (chg->smb_version == PM8150B_SUBTYPE || + chg->smb_version == PM7250B_SUBTYPE || + chg->smb_version == PMI632_SUBTYPE) { + switch (stat) { + case TERMINATE_CHARGE_V5: + case INHIBIT_CHARGE_V5: + flag = true; + break; + default: + break; + } + } else { + switch (stat) { + case TERMINATE_CHARGE: + case INHIBIT_CHARGE: + flag = true; + break; + default: + break; + } + } + + return flag; +} + +static enum alarmtimer_restart mmi_heartbeat_alarm_cb(struct alarm *alarm, + ktime_t now) +{ + struct smb_mmi_charger *chip = container_of(alarm, + struct smb_mmi_charger, + heartbeat_alarm); + + mmi_dbg(chip, "HB alarm fired\n"); + + PM_STAY_AWAKE(chip->smb_mmi_hb_wake_source); + cancel_delayed_work(&chip->heartbeat_work); + /* Delay by 500 ms to allow devices to resume. */ + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(500)); + + return ALARMTIMER_NORESTART; +} + +static int mmi_dual_charge_sm(struct smb_mmi_charger *chg, + struct smb_mmi_chg_status *stat, + int batt, int fv_offset) +{ + int max_fv_mv; + int i; + struct mmi_temp_zone *zone; + struct mmi_sm_params *chip = &chg->sm_param[batt]; + int start_tz = chip->pres_temp_zone; + int start_step = chip->pres_chrg_step; + + if (!chip->temp_zones) { + mmi_err(chg, "No Temp Zone Defined for batt %d!\n", batt); + return -ENODEV; + } + + if (chg->base_fv_mv == 0) { + chg->base_fv_mv = get_effective_result(chg->fv_votable); + chg->base_fv_mv /= 1000; + vote(chg->fv_votable, + BATT_PROFILE_VOTER, false, 0); + } + max_fv_mv = chg->base_fv_mv; + + mmi_find_temp_zone(chg, chip, stat->batt_temp); + zone = &chip->temp_zones[chip->pres_temp_zone]; + + if (!stat->charger_present) { + chip->pres_chrg_step = STEP_NONE; + for (i = 0; i < MAX_NUM_STEPS; i++) + chip->ocp[i] = 0; + } else if ((chip->pres_temp_zone == ZONE_HOT) || + (chip->pres_temp_zone == ZONE_COLD) || + (chg->charging_limit_modes == CHARGING_LIMIT_RUN)) { + chip->pres_chrg_step = STEP_STOP; + } else if (chg->demo_mode) { /* Demo Mode */ + chip->pres_chrg_step = STEP_DEMO; + } else if ((chip->pres_chrg_step == STEP_NONE) || + (chip->pres_chrg_step == STEP_STOP)) { + if (zone->norm_mv && (stat->batt_mv >= zone->norm_mv)) { + if (zone->fcc_norm_ma) + chip->pres_chrg_step = STEP_NORM; + else + chip->pres_chrg_step = STEP_STOP; + } else + chip->pres_chrg_step = STEP_MAX; + } else if (chip->pres_chrg_step == STEP_MAX) { + if (!zone->norm_mv) { + /* No Step in this Zone */ + chip->chrg_taper_cnt = 0; + if ((stat->batt_mv + fv_offset) >= max_fv_mv) + chip->pres_chrg_step = STEP_NORM; + else + chip->pres_chrg_step = STEP_MAX; + } else if ((stat->batt_mv + fv_offset) < zone->norm_mv) { + chip->chrg_taper_cnt = 0; + chip->pres_chrg_step = STEP_MAX; + } else if (!zone->fcc_norm_ma) + chip->pres_chrg_step = STEP_FLOAT; + else if (mmi_has_current_tapered(batt, chg, chip, stat->batt_ma, + zone->fcc_norm_ma)) { + chip->chrg_taper_cnt = 0; + for (i = 0; i < MAX_NUM_STEPS; i++) + chip->ocp[i] = 0; + chip->pres_chrg_step = STEP_NORM; + } + } else if (chip->pres_chrg_step == STEP_NORM) { + if (!zone->fcc_norm_ma) + chip->pres_chrg_step = STEP_STOP; + else if ((stat->batt_soc < 100) || + (stat->batt_mv + fv_offset) < max_fv_mv) { + chip->chrg_taper_cnt = 0; + chip->pres_chrg_step = STEP_NORM; + } else if (mmi_has_current_tapered(batt, chg, chip, stat->batt_ma, + chip->chrg_iterm)) { + chip->pres_chrg_step = STEP_FULL; + } + } else if (chip->pres_chrg_step == STEP_FULL) { + if (stat->batt_soc <= 99) { + chip->chrg_taper_cnt = 0; + chip->pres_chrg_step = STEP_NORM; + } + } else if (chip->pres_chrg_step == STEP_FLOAT) { + if ((zone->fcc_norm_ma) || + ((stat->batt_mv + fv_offset) < zone->norm_mv)) + chip->pres_chrg_step = STEP_MAX; + } + + /* Take State actions */ + switch (chip->pres_chrg_step) { + case STEP_FLOAT: + case STEP_MAX: + if (!zone->norm_mv) + chip->target_fv = max_fv_mv; + else + chip->target_fv = zone->norm_mv; + chip->target_fcc = zone->fcc_max_ma; + break; + case STEP_FULL: + chip->target_fv = max_fv_mv; + chip->target_fcc = -EINVAL; + break; + case STEP_NORM: + chip->target_fv = max_fv_mv + chg->vfloat_comp_mv; + chip->target_fcc = zone->fcc_norm_ma; + break; + case STEP_NONE: + chip->target_fv = max_fv_mv; + chip->target_fcc = zone->fcc_norm_ma; + break; + case STEP_STOP: + chip->target_fv = max_fv_mv; + chip->target_fcc = -EINVAL; + break; + case STEP_DEMO: + chip->target_fv = DEMO_MODE_VOLTAGE; + chip->target_fcc = zone->fcc_max_ma; + break; + default: + chip->target_fv = max_fv_mv; + chip->target_fcc = zone->fcc_norm_ma; + break; + } + + if (chip->pres_temp_zone == ZONE_HOT) { + chip->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; + } else if (chip->pres_temp_zone == ZONE_COLD) { + chip->batt_health = POWER_SUPPLY_HEALTH_COLD; + } else if (stat->batt_temp >= WARM_TEMP) { + if (chip->pres_chrg_step == STEP_STOP) + chip->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; + else + chip->batt_health = POWER_SUPPLY_HEALTH_GOOD; + } else if (stat->batt_temp <= COOL_TEMP) { + if (chip->pres_chrg_step == STEP_STOP) + chip->batt_health = POWER_SUPPLY_HEALTH_COLD; + else + chip->batt_health = POWER_SUPPLY_HEALTH_GOOD; + } else + chip->batt_health = POWER_SUPPLY_HEALTH_GOOD; + + if ((start_tz != chip->pres_temp_zone) || + (start_step != chip->pres_chrg_step)) { + mmi_info(chg, "Batt %d: batt_mv = %d, batt_ma %d, batt_soc %d," + " batt_temp %d, usb_mv %d, cp %d, vp %d\n", + batt, + stat->batt_mv, + stat->batt_ma, + stat->batt_soc, + stat->batt_temp, + stat->usb_mv, + stat->charger_present, + stat->vbus_present); + mmi_info(chg, "Batt %d Step State = %s, Temp Zone %d, Health %d\n", + batt, + stepchg_str[(int)chip->pres_chrg_step], + chip->pres_temp_zone, + chip->batt_health); + chg->hb_startup_cnt = HEARTBEAT_OCP_SETTLE_CNT; + chg->ocp_flag = true; // 1x settle per state chng + return 1; + } else { + mmi_dbg(chg, "Batt %d: batt_mv = %d, batt_ma %d, batt_soc %d," + " batt_temp %d, usb_mv %d, cp %d, vp %d\n", + batt, + stat->batt_mv, + stat->batt_ma, + stat->batt_soc, + stat->batt_temp, + stat->usb_mv, + stat->charger_present, + stat->vbus_present); + mmi_dbg(chg, "Batt %d Step State = %s, Temp Zone %d, Health %d\n", + batt, + stepchg_str[(int)chip->pres_chrg_step], + chip->pres_temp_zone, + chip->batt_health); + } + + return 0; +} + +static int mmi_dual_charge_control(struct smb_mmi_charger *chg, + struct smb_mmi_chg_status *stat) +{ + int rc; + int target_fcc; + int target_fv; + int effective_fv; + int effective_fcc; + int scaled_fcc =0; + int ocp; + int sm_update; + bool voltage_full; + int i; + int sched_time = HEARTBEAT_DUAL_DELAY_MS; + struct smb_mmi_chg_status chg_stat_main, chg_stat_flip; + union power_supply_propval pval; + struct mmi_sm_params *main_p = &chg->sm_param[MAIN_BATT]; + struct mmi_sm_params *flip_p = &chg->sm_param[FLIP_BATT]; + static int demo_full_soc = 100; + bool is_chg_dis = get_effective_result(chg->chg_dis_votable); + + chg_stat_main.charger_present = stat->charger_present; + chg_stat_flip.charger_present = stat->charger_present; + chg_stat_main.vbus_present = stat->vbus_present; + chg_stat_flip.vbus_present = stat->vbus_present; + chg_stat_main.usb_mv = stat->usb_mv; + chg_stat_flip.usb_mv = stat->usb_mv; + + rc = get_prop_batt_voltage_now(chg, chg->max_main_psy, &pval); + if (rc < 0) { + mmi_err(chg, "Error getting Main Batt Voltage rc = %d\n", rc); + return sched_time; + } else + chg_stat_main.batt_mv = pval.intval / 1000; + + rc = get_prop_batt_voltage_now(chg, chg->max_flip_psy, &pval); + if (rc < 0) { + mmi_err(chg, "Error getting Flip Batt Voltage rc = %d\n", rc); + return sched_time; + } else + chg_stat_flip.batt_mv = pval.intval / 1000; + + rc = get_prop_batt_current_now(chg, chg->max_main_psy, &pval); + if (rc < 0) { + mmi_err(chg, "Error getting Main Batt Current rc = %d\n", rc); + return sched_time; + } else + chg_stat_main.batt_ma = (pval.intval / 1000) * -1; + + rc = get_prop_batt_current_now(chg, chg->max_flip_psy, &pval); + if (rc < 0) { + mmi_err(chg, "Error getting Flip Batt Current rc = %d\n", rc); + return sched_time; + } else + chg_stat_flip.batt_ma = (pval.intval / 1000) * -1; + + rc = get_prop_batt_capacity(chg, chg->max_main_psy, &pval); + if (rc < 0) { + mmi_err(chg, "Error getting Main Batt Capacity rc = %d\n", rc); + return sched_time; + } else + chg_stat_main.batt_soc = pval.intval; + + rc = get_prop_batt_capacity(chg, chg->max_flip_psy, &pval); + if (rc < 0) { + mmi_err(chg, "Error getting Flip Batt Capacity rc = %d\n", rc); + return sched_time; + } else + chg_stat_flip.batt_soc = pval.intval; + + rc = get_prop_batt_temp(chg, chg->max_main_psy, &pval); + if (rc < 0) { + mmi_err(chg, "Error getting Main Batt Temperature rc = %d\n", rc); + return sched_time; + } else + chg_stat_main.batt_temp = pval.intval / 10; + + rc = get_prop_batt_temp(chg, chg->max_flip_psy, &pval); + if (rc < 0) { + mmi_err(chg, "Error getting Flip Batt Temperature rc = %d\n", rc); + return sched_time; + } else + chg_stat_flip.batt_temp = pval.intval / 10; + + sm_update = mmi_dual_charge_sm(chg, &chg_stat_main, + MAIN_BATT, HYST_STEP_MV); + if (sm_update < 0) + return sched_time; + + sm_update = mmi_dual_charge_sm(chg, &chg_stat_flip, + FLIP_BATT, HYST_STEP_FLIP_MV); + if (sm_update < 0) + return sched_time; + + effective_fv = get_effective_result(chg->fv_votable) / 1000; + effective_fcc = get_effective_result(chg->fcc_votable); + + /* OCP Check - Scale down fcc if too high for initial set */ + if (chg->hb_startup_cnt > 1) { + chg->hb_startup_cnt--; + sched_time = HEARTBEAT_DUAL_DELAY_OCP_MS; + } else if ( (effective_fcc >= (main_p->target_fcc + flip_p->target_fcc) *1000) && + (chg->ocp_flag) ) { + chg->ocp_flag = false; + sched_time = HEARTBEAT_DUAL_DELAY_OCP_MS; + chg->hb_startup_cnt = HEARTBEAT_OCP_SETTLE_CNT; + scaled_fcc = effective_fcc /10; + effective_fcc -= scaled_fcc; + mmi_dbg(chg, "fccScaled: %d, AdjFCC: %d\n", scaled_fcc, effective_fcc); + main_p->ocp[main_p->pres_temp_zone] =0; + flip_p->ocp[flip_p->pres_temp_zone] =0; + } + + /* Check for Charge None */ + if ((main_p->pres_chrg_step == STEP_NONE) || + (flip_p->pres_chrg_step == STEP_NONE)) { + chg->sm_param[BASE_BATT].pres_chrg_step = STEP_NONE; + target_fcc = main_p->target_fcc; + target_fv = chg->base_fv_mv; + sched_time = HEARTBEAT_DELAY_MS; + goto vote_now; + /* Check for Charge Demo */ + } else if ((main_p->pres_chrg_step == STEP_DEMO) || + (flip_p->pres_chrg_step == STEP_DEMO)) { + mmi_info(chg, "Battery in Demo Mode charging limited " + "%d%%\n", chg->demo_mode); + + voltage_full = ((chg->demo_mode_usb_suspend == false) && + ((stat->batt_mv + HYST_STEP_MV) >= DEMO_MODE_VOLTAGE) + && mmi_has_current_tapered(MAIN_BATT, chg, main_p, stat->batt_ma, + (main_p->chrg_iterm * 2))); + + if ((chg->demo_mode_usb_suspend == false) && + ((chg->last_reported_soc >= chg->demo_mode) || + voltage_full)) { + demo_full_soc = chg->last_reported_soc; + vote(chg->usb_icl_votable, DEMO_VOTER, true, 0); + vote(chg->dc_suspend_votable, DEMO_VOTER, true, 1); + chg->demo_mode_usb_suspend = true; + main_p->chrg_taper_cnt = 0; + flip_p->chrg_taper_cnt = 0; + for (i = 0; i < MAX_NUM_STEPS; i++) { + main_p->ocp[i] = 0; + flip_p->ocp[i] = 0; + } + } else if (chg->demo_mode_usb_suspend == true && + (chg->last_reported_soc <= + (demo_full_soc - DEMO_MODE_HYS_SOC))) { + vote(chg->usb_icl_votable, DEMO_VOTER, false, 0); + vote(chg->dc_suspend_votable, DEMO_VOTER, false, 0); + chg->demo_mode_usb_suspend = false; + main_p->chrg_taper_cnt = 0; + flip_p->chrg_taper_cnt = 0; + for (i = 0; i < MAX_NUM_STEPS; i++) { + main_p->ocp[i] = 0; + flip_p->ocp[i] = 0; + } + } + + target_fv = DEMO_MODE_VOLTAGE; + target_fcc = main_p->target_fcc; + goto vote_now; + /* Check for Charge FULL from each */ + } else if ((main_p->pres_chrg_step == STEP_FULL) && + (flip_p->pres_chrg_step == STEP_FULL)) { + chg->sm_param[BASE_BATT].pres_chrg_step = STEP_FULL; + target_fcc = -EINVAL; + target_fv = chg->base_fv_mv; + sched_time = HEARTBEAT_DELAY_MS; + chg->last_reported_status = POWER_SUPPLY_STATUS_FULL; + goto vote_now; + /* Align FULL between batteries */ + } else if ((main_p->pres_chrg_step == STEP_FULL) && + ((flip_p->pres_chrg_step == STEP_MAX) || + (flip_p->pres_chrg_step == STEP_NORM)) && + (chg_stat_flip.batt_soc >= 95) && + ((chg_stat_flip.batt_mv + HYST_STEP_FLIP_MV) >= + chg->base_fv_mv)) { + chg->sm_param[BASE_BATT].pres_chrg_step = + flip_p->pres_chrg_step; + target_fcc = flip_p->target_fcc; + target_fv = flip_p->target_fv; + mmi_info(chg, "Align Flip to Main FULL\n"); + sched_time = HEARTBEAT_DUAL_DELAY_MS; + goto vote_now; + } else if ((flip_p->pres_chrg_step == STEP_FULL) && + ((main_p->pres_chrg_step == STEP_MAX) || + (main_p->pres_chrg_step == STEP_NORM)) && + (chg_stat_main.batt_soc >= 95) && + ((chg_stat_main.batt_mv + HYST_STEP_MV) >= + chg->base_fv_mv)) { + chg->sm_param[BASE_BATT].pres_chrg_step = + main_p->pres_chrg_step; + target_fcc = main_p->target_fcc; + target_fv = main_p->target_fv; + mmi_info(chg, "Align Main to Flip FULL\n"); + sched_time = HEARTBEAT_DUAL_DELAY_MS; + goto vote_now; + /* Check for Charge Disable from each */ + } else if ((main_p->target_fcc < 0) || + (flip_p->target_fcc < 0)) { + chg->sm_param[BASE_BATT].pres_chrg_step = STEP_STOP; + target_fcc = -EINVAL; + target_fv = chg->base_fv_mv; + goto vote_now; + } + + chg->sm_param[BASE_BATT].pres_chrg_step = main_p->pres_chrg_step; + chg->last_reported_status = -1; + if (main_p->target_fv < flip_p->target_fv) + target_fv = main_p->target_fv; + else + target_fv = flip_p->target_fv; + + if ((chg_stat_main.batt_ma > main_p->target_fcc) && + chg->hb_startup_cnt %2) { //Allow settle time + sched_time = HEARTBEAT_DUAL_DELAY_OCP_MS; + ocp = chg_stat_main.batt_ma - main_p->target_fcc; + main_p->ocp[main_p->pres_temp_zone] += ocp; + mmi_info(chg, "Main Exceed by %d mA\n", + main_p->ocp[main_p->pres_temp_zone]); + } + if ((chg_stat_flip.batt_ma > flip_p->target_fcc) && + chg->hb_startup_cnt %2) { //Allow settle time + sched_time = HEARTBEAT_DUAL_DELAY_OCP_MS; + ocp = chg_stat_flip.batt_ma - flip_p->target_fcc; + flip_p->ocp[flip_p->pres_temp_zone] += ocp; + mmi_info(chg, "Flip Exceed by %d mA\n", + flip_p->ocp[flip_p->pres_temp_zone]); + } + + target_fcc = main_p->target_fcc + flip_p->target_fcc; + target_fcc -= scaled_fcc/1000; + target_fcc -= main_p->ocp[main_p->pres_temp_zone]; + target_fcc -= flip_p->ocp[flip_p->pres_temp_zone]; + if ( (target_fcc < main_p->target_fcc) && + (chg_stat_flip.batt_ma < flip_p->target_fcc *7/10) ) { + mmi_info(chg, "Target FCC adjust too much\n"); + chg->hb_startup_cnt = HEARTBEAT_OCP_SETTLE_CNT; + if (main_p->target_fcc < flip_p->target_fcc) + target_fcc = (main_p->target_fcc)*5/2; + else + target_fcc = (flip_p->target_fcc)*5/2; + main_p->ocp[main_p->pres_temp_zone] =0; + flip_p->ocp[flip_p->pres_temp_zone] =0; + } + + if (((main_p->pres_chrg_step == STEP_MAX) || + (flip_p->pres_chrg_step == STEP_MAX) || + (main_p->pres_chrg_step == STEP_NORM) || + (flip_p->pres_chrg_step == STEP_NORM)) && + mmi_charge_halted(chg)) { + vote(chg->chg_dis_votable, + MMI_HB_VOTER, true, 0); + mmi_err(chg, "Charge Halt..Toggle\n"); + msleep(50); + } + +vote_now: + /* Votes for State */ + vote(chg->fv_votable, MMI_HB_VOTER, true, target_fv * 1000); + + vote(chg->chg_dis_votable, MMI_HB_VOTER, + (target_fcc < 0), 0); + + vote(chg->fcc_votable, MMI_HB_VOTER, + true, (target_fcc >= 0) ? (target_fcc * 1000) : 0); + + /* Rerun AICL to recover USB ICL from recharge */ + if (is_chg_dis != get_effective_result(chg->chg_dis_votable)) { + power_supply_set_property(chg->qcom_psy, + POWER_SUPPLY_PROP_RERUN_AICL, + NULL); + } + + if (!stat->charger_present && + (chg_stat_main.batt_mv <= LOW_BATT_MV) && + (chg_stat_flip.batt_mv <= LOW_BATT_MV)) { + if ((chg_stat_main.batt_mv <= SHUT_BATT_MV) || + (chg_stat_flip.batt_mv <= SHUT_BATT_MV)) + chg->shut_batt = true; + } else + chg->shut_batt = false; + + if (sm_update) + mmi_info(chg, "IMPOSED: FV = %d, CDIS = %d, FCC = %d, USBICL = %d\n", + effective_fv, + get_effective_result(chg->chg_dis_votable), + target_fcc, + get_effective_result(chg->usb_icl_votable)); + else + mmi_dbg(chg, "IMPOSED: FV = %d, CDIS = %d, FCC = %d, USBICL = %d\n", + effective_fv, + get_effective_result(chg->chg_dis_votable), + target_fcc, + get_effective_result(chg->usb_icl_votable)); + + return sched_time; +} + +int mmi_set_prop_to_bms(struct smb_mmi_charger *chg, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int rc; + + if (!chg->bms_psy) + return -EINVAL; + + rc = power_supply_set_property(chg->bms_psy, psp, val); + + return rc; +} + +static int mmi_get_ffc_fv(struct smb_mmi_charger *chg, int zone,int batt) +{ + union power_supply_propval prop = {0,}; + int rc; + int ffc_max_fv; + struct mmi_sm_params *chip = &chg->sm_param[batt]; + + if (chip->ffc_zones == NULL + || zone >= chip->num_temp_zones) + return 0; + + prop.intval = chip->ffc_zones[zone].ffc_qg_iterm; + rc = mmi_set_prop_to_bms(chg, + POWER_SUPPLY_PROP_BATT_FULL_CURRENT, &prop); + if (rc < 0) { + mmi_err(chg, "Set bat full curr fail rc=%d\n", rc); + return 0; + } + + chip->chrg_iterm = chip->ffc_zones[zone].ffc_chg_iterm; + ffc_max_fv = chip->ffc_zones[zone].ffc_max_mv; + mmi_err(chg, + "FFC temp zone %d, fv %d mV, chg iterm %d mA, qg iterm %d mA\n", + zone, ffc_max_fv, chip->chrg_iterm, + chip->ffc_zones[zone].ffc_qg_iterm); + + return ffc_max_fv; +} + +static void mmi_basic_charge_sm(struct smb_mmi_charger *chip, + struct smb_mmi_chg_status *stat) +{ + int target_fcc; + int target_fv; + int max_fv_mv; + struct mmi_temp_zone *zone; + struct mmi_sm_params *prm = &chip->sm_param[BASE_BATT]; + bool voltage_full; + bool is_chg_dis = get_effective_result(chip->chg_dis_votable); + static int demo_full_soc = 100; + + mmi_info(chip, "batt_mv = %d, batt_ma %d, batt_soc %d," + " batt_temp %d, usb_mv %d, dc_mv %d, cp %d, vp %d dp %d\n", + stat->batt_mv, + stat->batt_ma, + stat->batt_soc, + stat->batt_temp, + stat->usb_mv, + stat->dc_mv, + stat->charger_present, + stat->vbus_present, + stat->dc_present); + + if (!prm->temp_zones) { + mmi_dbg(chip, "Skipping SM since No Temp Zone Defined!\n"); + return; + } + + if (chip->base_fv_mv == 0) { + chip->base_fv_mv = get_effective_result(chip->fv_votable); + chip->base_fv_mv /= 1000; + vote(chip->fv_votable, + BATT_PROFILE_VOTER, false, 0); + } + + max_fv_mv = mmi_get_ffc_fv(chip, prm->pres_temp_zone,BASE_BATT); + if (max_fv_mv == 0) + max_fv_mv = chip->base_fv_mv; + mmi_info(chip,"max_fv_mv:%d\n",max_fv_mv); + + mmi_find_temp_zone(chip, prm, stat->batt_temp); + if (prm->pres_temp_zone >= prm->num_temp_zones) + zone = &prm->temp_zones[0]; + else + zone = &prm->temp_zones[prm->pres_temp_zone]; + + if (!stat->charger_present && !is_wls_online(chip)) { + prm->pres_chrg_step = STEP_NONE; + } else if ((prm->pres_temp_zone == ZONE_HOT) || + (prm->pres_temp_zone == ZONE_COLD) || + (chip->charging_limit_modes == CHARGING_LIMIT_RUN)) { + prm->pres_chrg_step = STEP_STOP; + } else if (chip->demo_mode) { /* Demo Mode */ + prm->pres_chrg_step = STEP_DEMO; + mmi_info(chip, "Battery in Demo Mode charging limited " + "%d%%\n", chip->demo_mode); + + voltage_full = ((chip->demo_mode_usb_suspend == false) && + ((stat->batt_mv + HYST_STEP_MV) >= DEMO_MODE_VOLTAGE) + && mmi_has_current_tapered(BASE_BATT, chip, prm, stat->batt_ma, + prm->chrg_iterm)); + + if ((chip->demo_mode_usb_suspend == false) && + ((stat->batt_soc >= chip->demo_mode) || voltage_full)) { + demo_full_soc = stat->batt_soc; + vote(chip->usb_icl_votable, DEMO_VOTER, true, 0); + vote(chip->dc_suspend_votable, DEMO_VOTER, true, 0); + chip->demo_mode_usb_suspend = true; + } else if (chip->demo_mode_usb_suspend == true && + (stat->batt_soc <= (demo_full_soc - DEMO_MODE_HYS_SOC))) { + vote(chip->usb_icl_votable, DEMO_VOTER, false, 0); + vote(chip->dc_suspend_votable, DEMO_VOTER, false, 0); + chip->demo_mode_usb_suspend = false; + prm->chrg_taper_cnt = 0; + } + } else if ((prm->pres_chrg_step == STEP_NONE) || + (prm->pres_chrg_step == STEP_STOP)) { + if (zone->norm_mv && ((stat->batt_mv + HYST_STEP_MV) >= zone->norm_mv)) { + if (zone->fcc_norm_ma) + prm->pres_chrg_step = STEP_NORM; + else + prm->pres_chrg_step = STEP_STOP; + } else + prm->pres_chrg_step = STEP_MAX; + } else if (prm->pres_chrg_step == STEP_MAX) { + if (!zone->norm_mv) { + /* No Step in this Zone */ + prm->chrg_taper_cnt = 0; + if ((stat->batt_mv + HYST_STEP_MV) >= max_fv_mv) + prm->pres_chrg_step = STEP_NORM; + else + prm->pres_chrg_step = STEP_MAX; + } else if ((stat->batt_mv + HYST_STEP_MV) < zone->norm_mv) { + prm->chrg_taper_cnt = 0; + prm->pres_chrg_step = STEP_MAX; + } else if (!zone->fcc_norm_ma) + prm->pres_chrg_step = STEP_FLOAT; + else if (mmi_has_current_tapered(BASE_BATT, chip, prm, stat->batt_ma, + zone->fcc_norm_ma)) { + prm->chrg_taper_cnt = 0; + + if (mmi_charge_halted(chip)) { + vote(chip->chg_dis_votable, + MMI_HB_VOTER, true, 0); + mmi_warn(chip, "Charge Halt..Toggle\n"); + msleep(50); + } + + prm->pres_chrg_step = STEP_NORM; + } + } else if (prm->pres_chrg_step == STEP_NORM) { + if (!zone->fcc_norm_ma) + prm->pres_chrg_step = STEP_FLOAT; + else if ((stat->batt_soc < 100) || + (stat->batt_mv + HYST_STEP_MV) < max_fv_mv) { + prm->chrg_taper_cnt = 0; + prm->pres_chrg_step = STEP_NORM; + } else if (mmi_has_current_tapered(BASE_BATT, chip, prm, stat->batt_ma, + prm->chrg_iterm)) { + prm->pres_chrg_step = STEP_FULL; + } + } else if (prm->pres_chrg_step == STEP_FULL) { + if ((stat->batt_soc <= 99) || + stat->batt_mv < (max_fv_mv - HYST_STEP_MV * 2)) { + prm->chrg_taper_cnt = 0; + prm->pres_chrg_step = STEP_NORM; + } + } else if (prm->pres_chrg_step == STEP_FLOAT) { + if ((zone->fcc_norm_ma) || + ((stat->batt_mv + HYST_STEP_MV) < zone->norm_mv)) + prm->pres_chrg_step = STEP_MAX; + else if (mmi_has_current_tapered(BASE_BATT, chip, prm, stat->batt_ma, + prm->chrg_iterm)) + prm->pres_chrg_step = STEP_STOP; + } + + /* Take State actions */ + switch (prm->pres_chrg_step) { + case STEP_FLOAT: + case STEP_MAX: + if (!zone->norm_mv) + target_fv = max_fv_mv + chip->vfloat_comp_mv; + else + target_fv = zone->norm_mv + chip->vfloat_comp_mv; + target_fcc = zone->fcc_max_ma; + chip->last_reported_status = -1; + break; + case STEP_FULL: + target_fv = max_fv_mv; + target_fcc = -EINVAL; + chip->last_reported_status = POWER_SUPPLY_STATUS_FULL; + break; + case STEP_NORM: + target_fv = max_fv_mv + chip->vfloat_comp_mv; + target_fcc = zone->fcc_norm_ma; + chip->last_reported_status = -1; + break; + case STEP_NONE: + target_fv = max_fv_mv; + if (is_wls_online(chip)) + target_fcc = zone->fcc_norm_ma; + else if (stat->dc_present) + target_fcc = 500; + else + target_fcc = zone->fcc_norm_ma; + + chip->last_reported_status = -1; + break; + case STEP_STOP: + target_fv = max_fv_mv; + target_fcc = -EINVAL; + chip->last_reported_status = -1; + break; + case STEP_DEMO: + target_fv = DEMO_MODE_VOLTAGE; + target_fcc = zone->fcc_max_ma; + chip->last_reported_status = -1; + break; + default: + target_fv = max_fv_mv; + target_fcc = zone->fcc_norm_ma; + chip->last_reported_status = -1; + break; + } + + /* Votes for State */ + if (stat->dc_present) + vote(chip->dc_suspend_votable, MMI_HB_VOTER, false, 0); + else + vote(chip->dc_suspend_votable, MMI_HB_VOTER, true, 1); + + vote(chip->fv_votable, MMI_HB_VOTER, true, target_fv * 1000); + + vote(chip->chg_dis_votable, MMI_HB_VOTER, + (target_fcc < 0), 0); + + vote(chip->fcc_votable, MMI_HB_VOTER, + true, (target_fcc >= 0) ? (target_fcc * 1000) : 0); + + /* Override USB ICL using the target value */ + if (stat->charger_present) { + mmi_usb_icl_override(chip, + get_effective_result(chip->usb_icl_votable)); + } + + /* Rerun AICL to recover USB ICL from recharge */ + if (is_chg_dis != get_effective_result(chip->chg_dis_votable)) { + power_supply_set_property(chip->qcom_psy, + POWER_SUPPLY_PROP_RERUN_AICL, + NULL); + } + + if (prm->pres_temp_zone == ZONE_HOT) { + prm->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; + } else if (prm->pres_temp_zone == ZONE_COLD) { + prm->batt_health = POWER_SUPPLY_HEALTH_COLD; + } else if (stat->batt_temp >= WARM_TEMP) { + if (prm->pres_chrg_step == STEP_STOP) + prm->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT; + else + prm->batt_health = POWER_SUPPLY_HEALTH_GOOD; + } else if (stat->batt_temp <= COOL_TEMP) { + if (prm->pres_chrg_step == STEP_STOP) + prm->batt_health = POWER_SUPPLY_HEALTH_COLD; + else + prm->batt_health = POWER_SUPPLY_HEALTH_GOOD; + } else + prm->batt_health = POWER_SUPPLY_HEALTH_GOOD; + + mmi_info(chip, "Step State = %s, Temp Zone %d, Health %d\n", + stepchg_str[(int)prm->pres_chrg_step], + prm->pres_temp_zone, + prm->batt_health); + mmi_info(chip, "IMPOSED: FV = %d, CDIS = %d, FCC = %d, USBICL = %d\n", + get_effective_result(chip->fv_votable), + get_effective_result(chip->chg_dis_votable), + get_effective_result(chip->fcc_votable), + get_effective_result(chip->usb_icl_votable)); +} + +static void smb_mmi_awake_vote(struct smb_mmi_charger *chip, bool awake) +{ + if (awake == chip->awake) + return; + + chip->awake = awake; + if (awake) + pm_stay_awake(chip->dev); + else + pm_relax(chip->dev); +} + +void update_charging_limit_modes(struct smb_mmi_charger *chip, int batt_soc) +{ + enum charging_limit_modes charging_limit_modes; + + charging_limit_modes = chip->charging_limit_modes; + if ((charging_limit_modes != CHARGING_LIMIT_RUN) + && (batt_soc >= chip->upper_limit_capacity)) + charging_limit_modes = CHARGING_LIMIT_RUN; + else if ((charging_limit_modes != CHARGING_LIMIT_OFF) + && (batt_soc <= chip->lower_limit_capacity)) + charging_limit_modes = CHARGING_LIMIT_OFF; + + if (charging_limit_modes != chip->charging_limit_modes) + chip->charging_limit_modes = charging_limit_modes; +} + +static bool __smb_mmi_ps_is_supplied_by(struct power_supply *supplier, + struct power_supply *supply) +{ + int i; + + if (!supply->supplied_from && !supplier->supplied_to) + return false; + + /* Support both supplied_to and supplied_from modes */ + if (supply->supplied_from) { + if (!supplier->desc->name) + return false; + for (i = 0; i < supply->num_supplies; i++) + if (!strcmp(supplier->desc->name, + supply->supplied_from[i])) + return true; + } else { + if (!supply->desc->name) + return false; + for (i = 0; i < supplier->num_supplicants; i++) + if (!strcmp(supplier->supplied_to[i], + supply->desc->name)) + return true; + } + + return false; +} + +static int __smb_mmi_ps_changed(struct device *dev, void *data) +{ + struct power_supply *psy = data; + struct power_supply *pst = dev_get_drvdata(dev); + + if (__smb_mmi_ps_is_supplied_by(psy, pst)) { + if (pst->desc->external_power_changed) + pst->desc->external_power_changed(pst); + } + + return 0; +} + +static void smb_mmi_power_supply_changed(struct power_supply *psy, + char *envp_ext[]) +{ + if (this_chip && this_chip->prev_chg_rate != this_chip->charger_rate) { + dev_err(&psy->dev, "SMBMMI: %s: %s\n", __func__, envp_ext[0]); + } + + class_for_each_device(power_supply_class, NULL, psy, + __smb_mmi_ps_changed); + atomic_notifier_call_chain(&power_supply_notifier, + PSY_EVENT_PROP_CHANGED, psy); + kobject_uevent_env(&psy->dev.kobj, KOBJ_CHANGE, envp_ext); +} + +static int factory_kill_disable; +module_param(factory_kill_disable, int, 0644); +static int suspend_wakeups; +module_param(suspend_wakeups, int, 0644); +#define TWO_VOLT 2000000 +#define SMBCHG_HEARTBEAT_INTRVAL_NS 70000000000 +#define MONOTONIC_SOC 2 /* 2 percent */ +static void mmi_heartbeat_work(struct work_struct *work) +{ + struct smb_mmi_charger *chip = container_of(work, + struct smb_mmi_charger, + heartbeat_work.work); + int hb_resch_time = HEARTBEAT_DELAY_MS; + union power_supply_propval pval; + int rc, usb_suspend; + int batt_cap = 0; + int main_cap = 0; + int main_cap_full = 0; + int main_age = 0; + int flip_cap = 0; + int flip_cap_full = 0; + int flip_age = 0; + int cap_err; + int report_cap; + int pc_online; + int pres_chrg_step, prev_chrg_step; + static int prev_vbus_mv = -1; + char *chrg_rate_string = NULL; + char *envp[2]; + + struct smb_mmi_chg_status chg_stat; + + /* Have not been resumed so wait another 100 ms */ + if (chip->suspended & IS_SUSPENDED) { + mmi_err(chip, "HB running before Resume\n"); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(100)); + return; + } + + smb_mmi_awake_vote(chip, true); + alarm_try_to_cancel(&chip->heartbeat_alarm); + + mmi_dbg(chip, "Heartbeat!\n"); + + chg_stat.charger_present = false; + rc = get_prop_batt_voltage_now(chip, chip->bms_psy, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Voltage rc = %d\n", rc); + goto sch_hb; + } else + chg_stat.batt_mv = pval.intval / 1000; + + rc = get_prop_batt_current_now(chip, chip->bms_psy, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Current rc = %d\n", rc); + goto sch_hb; + } else + chg_stat.batt_ma = pval.intval / 1000; + + rc = get_prop_usb_voltage_now(chip, &pval); + if (rc < 0) { + chg_stat.usb_mv = 0; + if (rc != -ENODATA) + mmi_err(chip, "Error getting USB Voltage rc = %d\n", rc); + } else + chg_stat.usb_mv = pval.intval / 1000; + + if (prev_vbus_mv == -1) + prev_vbus_mv = chg_stat.usb_mv; + + rc = get_prop_dc_voltage_now(chip, &pval); + if (rc < 0) { + mmi_dbg(chip, "Error getting DC Voltage rc = %d\n", rc); + chg_stat.dc_mv = 0; + } else + chg_stat.dc_mv = pval.intval / 1000; + + rc = get_prop_batt_capacity(chip, chip->bms_psy, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Capacity rc = %d\n", rc); + return; + } else + chg_stat.batt_soc = pval.intval; + + if (chip->last_reported_soc != -1) + chg_stat.batt_soc = chip->last_reported_soc; + + rc = get_prop_batt_temp(chip, chip->bms_psy, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Temperature rc = %d\n", rc); + return; + } else + chg_stat.batt_temp = pval.intval / 10; + + rc = get_prop_usb_present(chip, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting USB Present rc = %d\n", rc); + return; + } else + chg_stat.vbus_present = pval.intval; + + rc = get_prop_dc_present(chip, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting DC Present rc = %d\n", rc); + return; + } else + chg_stat.dc_present = pval.intval; + + rc = get_prop_charger_present(chip, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Charger Present rc = %d\n", rc); + return; + } else + chg_stat.charger_present = pval.intval & chg_stat.vbus_present; + + if (chip->factory_mode) + hb_resch_time = HEARTBEAT_FACTORY_MS; + else if (chg_stat.charger_present || chg_stat.dc_present) + hb_resch_time = HEARTBEAT_DELAY_MS; + else + hb_resch_time = HEARTBEAT_DISCHARGE_MS; + + mmi_chrg_rate_check(chip); + + if (chip->vbus_enabled && chip->vbus && chg_stat.charger_present) { + rc = regulator_disable(chip->vbus); + if (rc) + mmi_err(chip, "Unable to disable vbus (%d)\n", rc); + else { + chip->vbus_enabled = false; + mmi_info(chip, "VBUS Disable due to Charger\n"); + } + } + + if (chip->enable_charging_limit && chip->is_factory_image) + update_charging_limit_modes(chip, chg_stat.batt_soc); + + if (chip->charging_limit_modes == CHARGING_LIMIT_RUN) + mmi_warn(chip, "Factory Mode/Image so Limiting Charging!!!\n"); + + mmi_chrg_input_config(chip, &chg_stat); + + if (chip->max_main_psy && chip->max_flip_psy) { + cap_err = 0; + rc = power_supply_get_property(chip->max_main_psy, + POWER_SUPPLY_PROP_CAPACITY, + &pval); + if (rc < 0) { + mmi_err(chip, "Couldn't get maxim main capacity\n"); + cap_err = rc; + } else if (pval.intval <0) { + cap_err = -EAGAIN; // TI FG RESET may return neg cap prior to init, so delay + } else + main_cap = pval.intval; + + rc = power_supply_get_property(chip->max_main_psy, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + &pval); + if (rc < 0) { + mmi_err(chip, "Couldn't get maxim main chrg full\n"); + cap_err = rc; + } else + main_cap_full = pval.intval; + + rc = power_supply_get_property(chip->max_flip_psy, + POWER_SUPPLY_PROP_CAPACITY, + &pval); + if (rc < 0) { + mmi_err(chip, "Couldn't get maxim flip capacity\n"); + cap_err = rc; + } else if (pval.intval <0) { + cap_err = -EAGAIN; // TI FG RESET may return neg cap prior to init, so delay + } else + flip_cap = pval.intval; + + rc = power_supply_get_property(chip->max_flip_psy, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + &pval); + if (rc < 0) { + mmi_err(chip, "Couldn't get maxim flip chrg full\n"); + cap_err = rc; + } else + flip_cap_full = pval.intval; + + if (cap_err == -EAGAIN) { + mmi_err(chip, "The FGs are not ready yet\n"); + hb_resch_time = HEARTBEAT_DUAL_DELAY_MS; + goto sch_hb; + } + + report_cap = main_cap * main_cap_full; + report_cap += flip_cap * flip_cap_full; + report_cap /= main_cap_full + flip_cap_full; + + if (chip->shut_batt || (report_cap < 0)) + report_cap = 0; + else if (report_cap > 100) + report_cap = 100; + + if (chip->suspended & WAS_SUSPENDED) + batt_cap = -1; + else + batt_cap = chip->last_reported_soc; + + if ((batt_cap == -1) || + ((report_cap != batt_cap) && + (report_cap <= (batt_cap + MONOTONIC_SOC)) && + (report_cap >= (batt_cap - MONOTONIC_SOC)))) { + mmi_info(chip, "Updating Reported Capacity to %d, main_cap= %d, flip_cap= %d, main_cap_full= %d, flip_cap_full= %d\n", + report_cap, main_cap, flip_cap, main_cap_full, flip_cap_full); + chip->last_reported_soc = report_cap; + }else if ((batt_cap < 100) && (report_cap > (batt_cap + MONOTONIC_SOC))) { + chip->last_reported_soc++; + mmi_info(chip, "Alter Up Reported Capacity to %d target %d\n", + chip->last_reported_soc, report_cap); + }else if ((batt_cap > 0) && (report_cap < (batt_cap - MONOTONIC_SOC))) { + chip->last_reported_soc--; + mmi_info(chip, "Alter Down Reported Capacity to %d target %d\n", + chip->last_reported_soc, report_cap); + } + + /* Age calculation */ + rc = power_supply_get_property(chip->max_main_psy, + POWER_SUPPLY_PROP_CHARGE_FULL, + &pval); + if (rc < 0) { + mmi_err(chip, "Couldn't get maxim main charge full\n"); + cap_err = rc; + } else + main_cap = pval.intval; + + rc = power_supply_get_property(chip->max_flip_psy, + POWER_SUPPLY_PROP_CHARGE_FULL, + &pval); + if (rc < 0) { + mmi_err(chip, "Couldn't get maxim flip charge full\n"); + cap_err = rc; + } else + flip_cap = pval.intval; + + main_age = ((main_cap / 10) / (main_cap_full / 1000)); + flip_age = ((flip_cap / 10) / (flip_cap_full / 1000)); + + /* Block age output for now until FG can be vetted */ + if (cap_err == 0) + chip->age = 100; + + mmi_dbg(chip, "Age %d, Main Age %d, Flip Age %d\n", + chip->age, main_age, flip_age); + + prev_chrg_step = chip->sm_param[BASE_BATT].pres_chrg_step; + + /* Dual Step and Thermal Charging */ + hb_resch_time = mmi_dual_charge_control(chip, &chg_stat); + + /* Check to see if Voltage is shutdown */ + if (chip->shut_batt) + chip->last_reported_soc = 0; + + pres_chrg_step = chip->sm_param[BASE_BATT].pres_chrg_step; + + if ((prev_chrg_step == STEP_NONE) && + ((pres_chrg_step == STEP_MAX) || + (pres_chrg_step == STEP_NORM))) + chip->soc_cycles_start = chip->last_reported_soc; + else if ((pres_chrg_step == STEP_NONE) && + ((prev_chrg_step == STEP_MAX) || + (prev_chrg_step == STEP_NORM)) && + (chip->last_reported_soc > chip->soc_cycles_start)) { + + chip->cycles += (chip->last_reported_soc - + chip->soc_cycles_start); + chip->soc_cycles_start = EMPTY_CYCLES; + + } else if ((pres_chrg_step == STEP_FULL) && + ((prev_chrg_step == STEP_MAX) || + (prev_chrg_step == STEP_NORM)) && + (100 > chip->soc_cycles_start)) { + + chip->cycles += (100 - chip->soc_cycles_start); + chip->soc_cycles_start = EMPTY_CYCLES; + } + + } else if (!chip->factory_mode) { + cap_err = 0; + rc = power_supply_get_property(chip->bms_psy, + POWER_SUPPLY_PROP_CHARGE_FULL, + &pval); + if (rc < 0) { + mmi_err(chip, "Couldn't get charge full\n"); + cap_err = rc; + } else + main_cap = pval.intval; + + rc = power_supply_get_property(chip->bms_psy, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + &pval); + if (rc < 0) { + mmi_err(chip, "Couldn't get charge full design\n"); + cap_err = rc; + } else + main_cap_full = pval.intval; + + if (cap_err == 0) + chip->age = ((main_cap / 10) / (main_cap_full / 1000)); + + mmi_dbg(chip, "Age %d\n", chip->age); + + /* Fall here for Basic Step and Thermal Charging */ + mmi_basic_charge_sm(chip, &chg_stat); + } + + mmi_dbg(chip, "SMBMMI: batt_mv %d, usb_mv %d, prev_usb_mv %d batt_ma %d\n", + chg_stat.batt_mv, chg_stat.usb_mv, + prev_vbus_mv, chg_stat.batt_ma); + + rc = smblib_get_usb_suspend(chip, &usb_suspend); + if (rc < 0) { + usb_suspend = 0; + mmi_err(chip, "Couldn't get USB suspend rc = %d\n", rc); + } + if (!usb_suspend && + (abs(chg_stat.usb_mv - chg_stat.batt_mv) < REV_BST_BULK_DROP) && + ((chg_stat.usb_mv*1000) > TWO_VOLT)) { + if (((chg_stat.usb_mv < REV_BST_THRESH) && + ((prev_vbus_mv - REV_BST_DROP) > chg_stat.usb_mv)) || + (chg_stat.batt_ma > REV_BST_MA)) { + mmi_err(chip, "Reverse Boosted: Clear, USB Suspend. usb_mv: %d prev_vbus_mv: %d batt_ma: %d \n", chg_stat.usb_mv, prev_vbus_mv, chg_stat.batt_ma); + if (chip->factory_mode) + smblib_set_usb_suspend(chip, true); + else + vote(chip->usb_icl_votable, BOOST_BACK_VOTER, + true, 0); + msleep(50); + if (chip->factory_mode) + smblib_set_usb_suspend(chip, false); + else + vote(chip->usb_icl_votable, BOOST_BACK_VOTER, + false, 0); + } else { + mmi_err(chip, "Reverse Boosted: USB %d mV PUSB %d mV\n", + chg_stat.usb_mv, prev_vbus_mv); + } + } + prev_vbus_mv = chg_stat.usb_mv; + chip->suspended = 0; + + if (chip->factory_mode || + (chip->is_factory_image && chip->enable_factory_poweroff)) { + rc = power_supply_get_property(chip->pc_port_psy, + POWER_SUPPLY_PROP_ONLINE, + &pval); + if (rc < 0) + goto sch_hb; + + pc_online = pval.intval; + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_ONLINE, + &pval); + if (rc < 0) + goto sch_hb; + + mmi_dbg(chip, "Factory Kill check pc %d, usb %d, susp %d\n", + pc_online, pval.intval, usb_suspend); + if (pc_online || + pval.intval || + (usb_suspend && ((chg_stat.usb_mv*1000) > TWO_VOLT))) { + mmi_dbg(chip, "Factory Kill Armed\n"); + chip->factory_kill_armed = true; + } else if (chip->factory_kill_armed && !factory_kill_disable) { + mmi_warn(chip, "Factory kill power off\n"); + kernel_power_off(); + } else + chip->factory_kill_armed = false; + } + +sch_hb: + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(hb_resch_time)); + if (suspend_wakeups || chg_stat.charger_present) + alarm_start_relative(&chip->heartbeat_alarm, + ns_to_ktime(SMBCHG_HEARTBEAT_INTRVAL_NS)); + + if (!chg_stat.charger_present) + smb_mmi_awake_vote(chip, false); + + chrg_rate_string = kmalloc(CHG_SHOW_MAX_SIZE, GFP_KERNEL); + if (!chrg_rate_string) { + mmi_err(chip, "Failed to Get Uevent Mem\n"); + envp[0] = NULL; + } else { + scnprintf(chrg_rate_string, CHG_SHOW_MAX_SIZE, + "POWER_SUPPLY_CHARGE_RATE=%s", + charge_rate[chip->charger_rate]); + envp[0] = chrg_rate_string; + envp[1] = NULL; + } + + if (chip->batt_psy) { + smb_mmi_power_supply_changed(chip->batt_psy, envp); + } else if (chip->qcom_psy) { + smb_mmi_power_supply_changed(chip->qcom_psy, envp); + } + + kfree(chrg_rate_string); + + PM_RELAX(chip->smb_mmi_hb_wake_source); +} + +static int mmi_psy_notifier_call(struct notifier_block *nb, unsigned long val, + void *v) +{ + struct smb_mmi_charger *chip = container_of(nb, + struct smb_mmi_charger, mmi_psy_notifier); + struct power_supply *psy = v; + static bool first_boot = true; + + if (!chip) { + pr_err("SMBMMI: called before chip valid!\n"); + return NOTIFY_DONE; + } + + if (val != PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + if (psy && + ((strcmp(psy->desc->name, "usb") == 0) || + (strcmp(psy->desc->name, "wireless") == 0) || + (strcmp(psy->desc->name, "dc") == 0))) { + + mmi_info(chip, "Psy notifier call %s\n", psy->desc->name); + if(first_boot && chip->factory_mode) + { + //In factory mode for the first heartbeat make sure to wait 10 second until boot is complete + cancel_delayed_work(&chip->heartbeat_work); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(10000)); + first_boot = false; + } + else + { + cancel_delayed_work(&chip->heartbeat_work); + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(0)); + first_boot = false; + } + } + + return NOTIFY_OK; +} + +static int smbchg_reboot(struct notifier_block *nb, + unsigned long event, void *unused) +{ + struct smb_mmi_charger *chg = container_of(nb, struct smb_mmi_charger, + smb_reboot); + union power_supply_propval val; + int rc; + + pr_err("SMBMMI: Reboot/POFF\n"); + if (!chg) { + pr_err("SMBMMI: called before chip valid!\n"); + return NOTIFY_DONE; + } + + if (chg->factory_mode) { + switch (event) { + case SYS_POWER_OFF: + /* Disable Factory Kill */ + factory_kill_disable = true; + /* Disable Charging */ + smblib_masked_write_mmi(chg, CHARGING_ENABLE_CMD_REG, + CHARGING_ENABLE_CMD_BIT, + 0); + + /* Suspend USB and DC */ + smblib_set_usb_suspend(chg, true); + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + &val); + while ((rc >= 0) && (val.intval > TWO_VOLT)) { + msleep(100); + rc = power_supply_get_property( + chg->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + &val); + mmi_info(chg, "Wait for VBUS to decay\n"); + } + + mmi_info(chg, "VBUS UV wait 1 sec!\n"); + /* Delay 1 sec to allow more VBUS decay */ + msleep(1000); + break; + default: + break; + } + } + + return NOTIFY_DONE; +} + +static const struct power_supply_desc mmi_psy_desc = { + .name = "mmi_battery", + .type = POWER_SUPPLY_TYPE_MAIN, + .get_property = smb_mmi_get_property, + .set_property = smb_mmi_set_property, + .properties = smb_mmi_battery_props, + .num_properties = ARRAY_SIZE(smb_mmi_battery_props), +}; + +static enum power_supply_property batt_props[] = { + POWER_SUPPLY_PROP_INPUT_SUSPEND, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGER_TEMP, + POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_QNOVO, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_QNOVO, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, + POWER_SUPPLY_PROP_SW_JEITA_ENABLED, + POWER_SUPPLY_PROP_CHARGE_DONE, + POWER_SUPPLY_PROP_PARALLEL_DISABLE, + POWER_SUPPLY_PROP_SET_SHIP_MODE, + POWER_SUPPLY_PROP_DIE_HEALTH, + POWER_SUPPLY_PROP_RERUN_AICL, + POWER_SUPPLY_PROP_DP_DM, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_RECHARGE_SOC, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE, + POWER_SUPPLY_PROP_HOT_TEMP, +#ifdef CONFIG_QC3P_PUMP_SUPPORT + POWER_SUPPLY_PROP_QC3P_CURRENT_MAX, +#endif +}; + +static int batt_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct smb_mmi_charger *chip = power_supply_get_drvdata(psy); + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (chip->last_reported_status == -1) + rc = power_supply_get_property(chip->qcom_psy, + psp, val); + else + val->intval = chip->last_reported_status; + break; + case POWER_SUPPLY_PROP_CAPACITY: + if (chip->last_reported_soc == -1) + rc = power_supply_get_property(chip->qcom_psy, + psp, val); + else + val->intval = chip->last_reported_soc; + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + val->intval = chip->cycles / 100; + break; + case POWER_SUPPLY_PROP_HEALTH: + if (chip->max_main_psy && chip->max_flip_psy) + val->intval = chip->sm_param[MAIN_BATT].batt_health; + else + val->intval = chip->sm_param[BASE_BATT].batt_health; + break; + case POWER_SUPPLY_PROP_CHARGE_DONE: + if (chip->last_reported_status == POWER_SUPPLY_STATUS_FULL) + val->intval = 1; + else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + val->intval = get_effective_result(chip->fcc_votable); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + if (chip->smb_version == PMI632_SUBTYPE) { + val->intval = get_effective_result(chip->fcc_votable); + break; + } + rc = power_supply_get_property(chip->qcom_psy, psp, val); + if (rc < 0) { + /* soft fail so uevents are not blocked */ + rc = 0; + val->intval = -EINVAL; + } + break; + case POWER_SUPPLY_PROP_HOT_TEMP: + if (chip->max_main_psy && chip->max_flip_psy) { + if (chip->sm_param[MAIN_BATT].pres_temp_zone == + (chip->sm_param[MAIN_BATT].num_temp_zones - 1) || + chip->sm_param[MAIN_BATT].pres_temp_zone == ZONE_HOT) + val->intval = 1; + else if (chip->sm_param[FLIP_BATT].pres_temp_zone == + (chip->sm_param[FLIP_BATT].num_temp_zones - 1) || + chip->sm_param[FLIP_BATT].pres_temp_zone == ZONE_HOT) + val->intval = 1; + } else if (chip->sm_param[BASE_BATT].pres_temp_zone == + (chip->sm_param[BASE_BATT].num_temp_zones - 1) || + chip->sm_param[BASE_BATT].pres_temp_zone == ZONE_HOT) + val->intval = 1; + else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_TEMP: + if (chip->max_main_psy && chip->max_flip_psy) { + union power_supply_propval main_psy_val; + union power_supply_propval flip_psy_val; + + /* Get the highest temp between main psy and flip psy */ + rc = power_supply_get_property(chip->max_main_psy, psp, &main_psy_val); + if (rc >= 0) { + rc = power_supply_get_property(chip->max_flip_psy, psp, &flip_psy_val); + if (rc >= 0) { + if (main_psy_val.intval > flip_psy_val.intval) + val->intval = main_psy_val.intval; + else + val->intval = flip_psy_val.intval; + break; + } + } + } + /* Fall through */ + default: + rc = power_supply_get_property(chip->qcom_psy, psp, val); + if (rc < 0) { + mmi_dbg(chip, "Get Unknown prop %d rc = %d\n", psp, rc); + /* soft fail so uevents are not blocked */ + rc = 0; + val->intval = -EINVAL; + } + break; + } + + return rc; +} + +static int batt_set_prop(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + int rc = 0; + struct smb_mmi_charger *chip = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_CYCLE_COUNT: + chip->cycles += val->intval * 100; + break; + case POWER_SUPPLY_PROP_DP_DM: + /* Skip the hvdcp request from hvdcp daemon */ + if (chip->hvdcp_power_max) + break; + default: + rc = power_supply_set_property(chip->qcom_psy, prop, val); + if (rc < 0) { + mmi_dbg(chip, "Get Unknown prop %d rc = %d\n", prop, rc); + /* soft fail so uevents are not blocked */ + rc = 0; + } + break; + } + return rc; +} + +static int batt_prop_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + case POWER_SUPPLY_PROP_INPUT_SUSPEND: + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + case POWER_SUPPLY_PROP_CAPACITY: + case POWER_SUPPLY_PROP_PARALLEL_DISABLE: + case POWER_SUPPLY_PROP_DP_DM: + case POWER_SUPPLY_PROP_RERUN_AICL: + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: + case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: + case POWER_SUPPLY_PROP_SW_JEITA_ENABLED: + case POWER_SUPPLY_PROP_DIE_HEALTH: + case POWER_SUPPLY_PROP_CYCLE_COUNT: + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + case POWER_SUPPLY_PROP_CURRENT_MAX: +#ifdef CONFIG_QC3P_PUMP_SUPPORT + case POWER_SUPPLY_PROP_QC3P_CURRENT_MAX: +#endif + return 1; + default: + break; + } + + return 0; +} + +static const struct power_supply_desc batt_psy_desc = { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = batt_get_prop, + .set_property = batt_set_prop, + .property_is_writeable = batt_prop_is_writeable, + .properties = batt_props, + .num_properties = ARRAY_SIZE(batt_props), +}; + +static int parse_mmi_dt(struct smb_mmi_charger *chg) +{ + struct device_node *node = chg->dev->of_node; + int rc = 0; + int byte_len; + int i; + struct mmi_sm_params *chip; + + if (!node) { + mmi_err(chg, "mmi dtree info. missing\n"); + return -ENODEV; + } + + chip = &chg->sm_param[BASE_BATT]; + if (of_find_property(node, "qcom,mmi-temp-zones", &byte_len)) { + if ((byte_len / sizeof(u32)) % 4) { + mmi_err(chg, "DT error wrong mmi temp zones\n"); + return -ENODEV; + } + + chip->temp_zones = (struct mmi_temp_zone *) + devm_kzalloc(chg->dev, byte_len, GFP_KERNEL); + + if (chip->temp_zones == NULL) + return -ENOMEM; + + chip->num_temp_zones = + byte_len / sizeof(struct mmi_temp_zone); + + rc = of_property_read_u32_array(node, + "qcom,mmi-temp-zones", + (u32 *)chip->temp_zones, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_err(chg, "Couldn't read mmi temp zones rc = %d\n", rc); + return rc; + } + + mmi_info(chg, "mmi temp zones: Num: %d\n", chip->num_temp_zones); + for (i = 0; i < chip->num_temp_zones; i++) { + mmi_info(chg, "mmi temp zones: Zone %d, Temp %d C, " \ + "Step Volt %d mV, Full Rate %d mA, " \ + "Taper Rate %d mA\n", i, + chip->temp_zones[i].temp_c, + chip->temp_zones[i].norm_mv, + chip->temp_zones[i].fcc_max_ma, + chip->temp_zones[i].fcc_norm_ma); + } + chip->pres_temp_zone = ZONE_NONE; + chip->pres_chrg_step = STEP_NONE; + } + + if (of_find_property(node, "qcom,mmi-ffc-zones", &byte_len)) { + if ((byte_len / sizeof(struct mmi_ffc_zone) + != chip->num_temp_zones) + || ((byte_len / sizeof(u32)) % 3)) { + mmi_err(chg, + "DT error wrong mmi ffc zones\n"); + return -ENODEV; + } + + chip->ffc_zones = (struct mmi_ffc_zone *) + devm_kzalloc(chg->dev, byte_len, GFP_KERNEL); + + if (chip->ffc_zones == NULL) + return -ENOMEM; + + rc = of_property_read_u32_array(node, + "qcom,mmi-ffc-zones", + (u32 *)chip->ffc_zones, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_err(chg, + "Couldn't read mmi ffc zones rc = %d\n", + rc); + return rc; + } + + for (i = 0; i < chip->num_temp_zones; i++) { + mmi_err(chg, + "FFC:Zone %d,Volt %d,Ich %d,Iqg %d", i, + chip->ffc_zones[i].ffc_max_mv, + chip->ffc_zones[i].ffc_chg_iterm, + chip->ffc_zones[i].ffc_qg_iterm); + } + } else + chip->ffc_zones = NULL; + + rc = of_property_read_u32(node, "qcom,iterm-ma", + &chip->chrg_iterm); + if (rc) + chip->chrg_iterm = 150; + + rc = of_property_read_u32(node, "qcom,vfloat-comp-uv", + &chg->vfloat_comp_mv); + if (rc) + chg->vfloat_comp_mv = 0; + chg->vfloat_comp_mv /= 1000; + + chg->enable_charging_limit = + of_property_read_bool(node, "qcom,enable-charging-limit"); + + chg->enable_factory_poweroff = + of_property_read_bool(node, "qcom,enable-factory-poweroff"); + + rc = of_property_read_u32(node, "qcom,upper-limit-capacity", + &chg->upper_limit_capacity); + if (rc) + chg->upper_limit_capacity = 100; + + rc = of_property_read_u32(node, "qcom,lower-limit-capacity", + &chg->lower_limit_capacity); + if (rc) + chg->lower_limit_capacity = 0; + + rc = of_property_read_u32(node, "qcom,dc-icl-ma", + &chg->dc_cl_ma); + if (rc) + chg->dc_cl_ma = -EINVAL; + + mmi_charger_power_support(chg); + + rc = of_property_read_u32(node, "qcom,inc-hvdcp-cnt", + &chg->inc_hvdcp_cnt); + if (rc) + chg->inc_hvdcp_cnt = HVDCP_PULSE_COUNT_MAX; + + return rc; +} + +static int parse_mmi_dual_dt(struct smb_mmi_charger *chg) +{ + struct device_node *node = chg->dev->of_node; + int rc = 0; + int byte_len; + int i; + struct mmi_sm_params *chip; + + if (!node) { + mmi_err(chg, "mmi dtree info. missing\n"); + return -ENODEV; + } + + chip = &chg->sm_param[MAIN_BATT]; + if (of_find_property(node, "qcom,mmi-temp-zones-main", &byte_len)) { + if ((byte_len / sizeof(u32)) % 4) { + mmi_err(chg, "DT error wrong mmi temp zones\n"); + return -ENODEV; + } + + chip->temp_zones = (struct mmi_temp_zone *) + devm_kzalloc(chg->dev, byte_len, GFP_KERNEL); + + if (chip->temp_zones == NULL) + return -ENOMEM; + + chip->num_temp_zones = + byte_len / sizeof(struct mmi_temp_zone); + + rc = of_property_read_u32_array(node, + "qcom,mmi-temp-zones-main", + (u32 *)chip->temp_zones, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_err(chg, "Couldn't read mmi temp zones rc = %d\n", rc); + return rc; + } + + mmi_info(chg, "mmi temp zones main: Num: %d\n", chip->num_temp_zones); + for (i = 0; i < chip->num_temp_zones; i++) { + mmi_info(chg, "mmi temp zones: Zone %d, Temp %d C, " \ + "Step Volt %d mV, Full Rate %d mA, " \ + "Taper Rate %d mA\n", i, + chip->temp_zones[i].temp_c, + chip->temp_zones[i].norm_mv, + chip->temp_zones[i].fcc_max_ma, + chip->temp_zones[i].fcc_norm_ma); + } + chip->pres_temp_zone = ZONE_NONE; + } + + rc = of_property_read_u32(node, "qcom,iterm-ma-main", + &chip->chrg_iterm); + if (rc) + chip->chrg_iterm = 150; + + chip = &chg->sm_param[FLIP_BATT]; + if (of_find_property(node, "qcom,mmi-temp-zones-flip", &byte_len)) { + if ((byte_len / sizeof(u32)) % 4) { + mmi_err(chg, "DT error wrong mmi temp zones\n"); + return -ENODEV; + } + + chip->temp_zones = (struct mmi_temp_zone *) + devm_kzalloc(chg->dev, byte_len, GFP_KERNEL); + + if (chip->temp_zones == NULL) + return -ENOMEM; + + chip->num_temp_zones = + byte_len / sizeof(struct mmi_temp_zone); + + rc = of_property_read_u32_array(node, + "qcom,mmi-temp-zones-flip", + (u32 *)chip->temp_zones, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_err(chg, "Couldn't read mmi temp zones rc = %d\n", rc); + return rc; + } + + mmi_info(chg, "mmi temp zones main: Num: %d\n", chip->num_temp_zones); + for (i = 0; i < chip->num_temp_zones; i++) { + mmi_info(chg, "mmi temp zones: Zone %d, Temp %d C, " \ + "Step Volt %d mV, Full Rate %d mA, " \ + "Taper Rate %d mA\n", i, + chip->temp_zones[i].temp_c, + chip->temp_zones[i].norm_mv, + chip->temp_zones[i].fcc_max_ma, + chip->temp_zones[i].fcc_norm_ma); + } + chip->pres_temp_zone = ZONE_NONE; + } + + rc = of_property_read_u32(node, "qcom,iterm-ma-flip", + &chip->chrg_iterm); + if (rc) + chip->chrg_iterm = 150; + + return rc; +} + +static int smb_mmi_chg_config_init(struct smb_mmi_charger *chip) +{ + struct pmic_revid_data *pmic_rev_id; + struct device_node *revid_dev_node; + + revid_dev_node = of_parse_phandle(chip->dev->of_node, + "qcom,pmic-revid", 0); + if (!revid_dev_node) { + pr_err("SMBMMI: Missing qcom,pmic-revid property\n"); + return -EINVAL; + } + + pmic_rev_id = get_revid_data(revid_dev_node); + if (IS_ERR_OR_NULL(pmic_rev_id)) { + /* + * the revid peripheral must be registered, any failure + * here only indicates that the rev-id module has not + * probed yet. + */ + return -EPROBE_DEFER; + } + + switch (pmic_rev_id->pmic_subtype) { + case PM8150B_SUBTYPE: + chip->smb_version = PM8150B_SUBTYPE; + chip->name = "pm8150b_charger"; + chip->param = smb5_pm8150b_params; + break; + case PM7250B_SUBTYPE: + chip->smb_version = PM7250B_SUBTYPE; + chip->name = "pm7250b_charger"; + chip->param = smb5_pm8150b_params; + break; + case PMI632_SUBTYPE: + chip->smb_version = PMI632_SUBTYPE; + chip->name = "pmi632_charger"; + chip->param = smb5_pmi632_params; + break; + case PM660_SUBTYPE: + chip->smb_version = PM660_SUBTYPE; + chip->name = "pm660_charger"; + chip->param = smb2_pm660_params; + break; + default: + pr_err("SMBMMI: PMIC subtype %d not supported\n", + pmic_rev_id->pmic_subtype); + return -EINVAL; + } + + chip->chg_freq.freq_5V = 600; + chip->chg_freq.freq_6V_8V = 800; + chip->chg_freq.freq_9V = 1050; + chip->chg_freq.freq_12V = 1200; + + pr_err("SMBMMI: PMIC %d is %s\n", chip->smb_version, chip->name); + + return 0; +} + +static ssize_t charge_rate_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (!this_chip) { + pr_err("SMBMMI: mmi_chip is not initialized\n"); + return 0; + } + + mmi_chrg_rate_check(this_chip); + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%s\n", + charge_rate[this_chip->charger_rate]); +} +static DEVICE_ATTR(charge_rate, S_IRUGO, charge_rate_show, NULL); + +static ssize_t age_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + if (!this_chip) { + pr_err("SMBMMI: mmi_chip is not initialized\n"); + return 0; + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", this_chip->age); +} +static DEVICE_ATTR(age, S_IRUGO, age_show, NULL); + +static struct attribute * mmi_g[] = { + &dev_attr_charge_rate.attr, + &dev_attr_age.attr, + NULL, +}; + +static const struct attribute_group power_supply_mmi_attr_group = { + .attrs = mmi_g, +}; + +#if defined(CONFIG_DEBUG_FS) +static int register_dump_read(struct seq_file *m, void *data) +{ + int rc; + u8 stat; + int i; + struct smb_mmi_charger *chip = m->private; + + for (i = CHGR_BASE; i < MISC_BASE + 0x100; i++) { + rc = smblib_read_mmi(chip, i, &stat); + if (rc < 0) + continue; + seq_printf(m, "REG:0x%x: 0x%x\n", i, stat); + } + + return 0; +} + +static int register_dump_debugfs_open(struct inode *inode, struct file *file) +{ + struct smb_mmi_charger *chip = inode->i_private; + + return single_open(file, register_dump_read, chip); +} + +static const struct file_operations register_dump_debugfs_ops = { + .owner = THIS_MODULE, + .open = register_dump_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void smb_mmi_create_debugfs(struct smb_mmi_charger *chip) +{ + struct dentry *dfs_root, *file; + + dfs_root = debugfs_create_dir("charger_mmi", NULL); + if (IS_ERR_OR_NULL(dfs_root)) { + pr_err("SMBMMI: Couldn't create charger debugfs rc=%ld\n", + (long)dfs_root); + return; + } + + file = debugfs_create_file("register_dump", + S_IRUSR | S_IRGRP | S_IROTH, + dfs_root, chip, ®ister_dump_debugfs_ops); + if (IS_ERR_OR_NULL(file)) + pr_err("SMBMMI: Couldn't create register_dump file rc=%ld\n", + (long)file); +} +#else +static void smb_mmi_create_debugfs(struct smb_mmi_charger *chip) +{} +#endif + +static int smb_mmi_probe(struct platform_device *pdev) +{ + struct smb_mmi_charger *chip; + int rc = 0; + union power_supply_propval val; + struct power_supply_config psy_cfg = {}; + const char *max_main_name, *max_flip_name; + union power_supply_propval pval; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &pdev->dev; + psy_cfg.drv_data = chip; + platform_set_drvdata(pdev, chip); + chip->suspended = 0; + chip->awake = false; + this_chip = chip; + device_init_wakeup(chip->dev, true); + chip->hb_startup_cnt = HEARTBEAT_OCP_SETTLE_CNT; + chip->ocp_flag = true; + + smb_mmi_chg_config_init(chip); + + chip->debug_enabled = &debug_enabled; + chip->ipc_log = ipc_log_context_create(MMI_LOG_PAGES, "charger", 0); + if (!chip->ipc_log) + mmi_err(chip, "Failed to create SMBMMI IPC log\n"); + else + mmi_info(chip, "IPC logging is enabled for SMBMMI\n"); + + chip->regmap = dev_get_regmap(chip->dev->parent, NULL); + if (!chip->regmap) { + mmi_err(chip, "Parent regmap is missing\n"); + return -EINVAL; + } + + parse_mmi_dt(chip); + + INIT_DELAYED_WORK(&chip->heartbeat_work, mmi_heartbeat_work); + INIT_DELAYED_WORK(&chip->weakcharger_work, mmi_weakcharger_work); + + PM_WAKEUP_REGISTER(chip->dev, chip->smb_mmi_hb_wake_source, "smb_mmi_hb_wake"); + if (!chip->smb_mmi_hb_wake_source) { + mmi_err(chip, "failed to allocate wakeup source\n"); + return -ENOMEM; + } + + alarm_init(&chip->heartbeat_alarm, ALARM_BOOTTIME, + mmi_heartbeat_alarm_cb); + + chip->mmi_psy = devm_power_supply_register(chip->dev, &mmi_psy_desc, + &psy_cfg); + if (IS_ERR(chip->mmi_psy)) { + mmi_err(chip, "failed: mmi power supply register\n"); + return PTR_ERR(chip->mmi_psy); + } + + chip->age = 100; + chip->cycles = 0; + chip->shut_batt = false; + chip->soc_cycles_start = EMPTY_CYCLES; + chip->last_reported_soc = -1; + chip->last_reported_status = -1; + chip->qcom_psy = power_supply_get_by_name("qcom_battery"); + if (chip->qcom_psy) { + chip->batt_psy = devm_power_supply_register(chip->dev, + &batt_psy_desc, + &psy_cfg); + if (IS_ERR(chip->batt_psy)) { + mmi_err(chip, + "Failed: batt power supply register\n"); + return PTR_ERR(chip->batt_psy); + } + + rc = sysfs_create_group(&chip->batt_psy->dev.kobj, + &power_supply_mmi_attr_group); + if (rc) + mmi_err(chip, "Failed: attr create\n"); + } else { + chip->qcom_psy = power_supply_get_by_name("battery"); + chip->batt_psy = NULL; + + rc = sysfs_create_group(&chip->qcom_psy->dev.kobj, + &power_supply_mmi_attr_group); + if (rc) + mmi_err(chip, "Failed: attr create\n"); + } + + chip->bms_psy = power_supply_get_by_name("bms"); + chip->usb_psy = power_supply_get_by_name("usb"); + chip->main_psy = power_supply_get_by_name("main"); + chip->pc_port_psy = power_supply_get_by_name("pc_port"); + chip->dc_psy = power_supply_get_by_name("dc"); + chip->wls_psy = power_supply_get_by_name("wireless"); + /* parse the dc power supply configuration */ + rc = of_property_read_string(pdev->dev.of_node, + "mmi,max-main-psy", &max_main_name); + rc |= of_property_read_string(pdev->dev.of_node, + "mmi,max-flip-psy", &max_flip_name); + if (!rc && max_main_name && max_flip_name) { + chip->max_main_psy = power_supply_get_by_name(max_main_name); + chip->max_flip_psy = power_supply_get_by_name(max_flip_name); + parse_mmi_dual_dt(chip); + } + + chip->chg_dis_votable = find_votable("CHG_DISABLE"); + if (IS_ERR(chip->chg_dis_votable)) + chip->chg_dis_votable = NULL; + chip->fcc_votable = find_votable("FCC"); + if (IS_ERR(chip->fcc_votable)) + chip->fcc_votable = NULL; + chip->fv_votable = find_votable("FV"); + if (IS_ERR(chip->fv_votable)) + chip->fv_votable = NULL; + chip->usb_icl_votable = find_votable("USB_ICL"); + if (IS_ERR(chip->usb_icl_votable)) + chip->usb_icl_votable = NULL; + chip->dc_suspend_votable = find_votable("DC_SUSPEND"); + if (IS_ERR(chip->dc_suspend_votable)) + chip->dc_suspend_votable = NULL; + + if (chip->smb_version == PM8150B_SUBTYPE) { + if (smblib_masked_write_mmi(chip, LEGACY_CABLE_CFG_REG, + 0xFF, 0)) + mmi_err(chip, "Could not set Legacy Cable CFG\n"); + } + + if (chip->smb_version == PM8150B_SUBTYPE || + chip->smb_version == PM7250B_SUBTYPE || + chip->smb_version == PMI632_SUBTYPE || + chip->smb_version == PM660_SUBTYPE) { + mmi_err(chip, "DISABLE all QCOM JEITA\n"); + /* Ensure SW JEITA is DISABLED */ + pval.intval = 0; + power_supply_set_property(chip->qcom_psy, + POWER_SUPPLY_PROP_SW_JEITA_ENABLED, + &pval); + /* Ensure HW JEITA is DISABLED */ + if (smblib_masked_write_mmi(chip, PM8150B_JEITA_EN_CFG_REG, + 0xFF, 0x00)) + mmi_err(chip, "Could not disable JEITA CFG\n"); + } + + if ((chip->smb_version == PM8150B_SUBTYPE) || + (chip->smb_version == PM7250B_SUBTYPE) || + (chip->smb_version == PMI632_SUBTYPE) || + (chip->smb_version == PM660_SUBTYPE)) + if (smblib_masked_write_mmi(chip, USBIN_ADAPTER_ALLOW_CFG_REG, + USBIN_ADAPTER_ALLOW_MASK, + USBIN_ADAPTER_ALLOW_5V_TO_9V)) + mmi_err(chip, "Could not set USB Adapter CFG\n"); + + /* + * Only 5V is allowed for QC2.0 and QC3.0, so set pulse count max + * accordingly to limit voltage increment. + */ + if (chip->hvdcp_power_max) { + rc = smblib_masked_write_mmi(chip, HVDCP_PULSE_COUNT_MAX_REG, + HVDCP_PULSE_COUNT_MAX_QC2_MASK | + HVDCP_PULSE_COUNT_MAX_QC3_MASK, + chip->inc_hvdcp_cnt); + if (rc < 0) + mmi_err(chip, "Could not set HVDCP pulse count max\n"); + } + + chip->factory_mode = mmi_factory_check(MMI_FACTORY_MODE); + chip->is_factory_image = mmi_factory_check(MMI_FACTORY_BUILD); + chip->charging_limit_modes = CHARGING_LIMIT_UNKNOWN; + + if (chip->dc_cl_ma >= 0) { + rc = smblib_set_charge_param(chip, &chip->param.dc_icl, + chip->dc_cl_ma * 1000); + if (rc) + mmi_err(chip, "Failed to set DC ICL %d\n", + chip->dc_cl_ma); + } + + /* Workaround for some cables that collapse on boot */ + if (!chip->factory_mode) { + dev_err(chip->dev, "SMBMMI: Suspending USB for 50 ms to clear\n"); + smblib_set_usb_suspend(chip, true); + msleep(50); + smblib_set_usb_suspend(chip, false); + } + + if (chip->factory_mode && + (chip->smb_version == PMI632_SUBTYPE)) { + rc = smblib_masked_write_mmi(chip, USBIN_INT_EN_CLR, + 0xFF, USBIN_OV_EN_CLR); + if (rc) { + mmi_err(chip, "Could Not disable usbin ov irq\n"); + } + } + + rc = device_create_file(chip->dev, + &dev_attr_force_demo_mode); + if (rc) { + mmi_err(chip, "Couldn't create force_demo_mode\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_max_chrg_temp); + if (rc) { + mmi_err(chip, "Couldn't create force_max_chrg_temp\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_factory_image_mode); + if (rc) + mmi_err(chip, "Couldn't create factory_image_mode\n"); + + rc = device_create_file(chip->dev, + &dev_attr_factory_charge_upper); + if (rc) + mmi_err(chip, "Couldn't create factory_charge_upper\n"); + + rc = device_create_file(chip->dev, + &dev_attr_force_hvdcp_power_max); + if (rc) + mmi_err(chip, "Couldn't create force_hvdcp_power_max\n"); + + rc = device_create_file(chip->dev, + &dev_attr_force_pd_power_max); + if (rc) + mmi_err(chip, "Couldn't create force_pd_power_max\n"); + + /* Register the notifier for the psy updates*/ + chip->mmi_psy_notifier.notifier_call = mmi_psy_notifier_call; + rc = power_supply_reg_notifier(&chip->mmi_psy_notifier); + if (rc) + mmi_err(chip, "Failed to reg notifier: %d\n", rc); + + if (chip->factory_mode) { + mmi_info(chip, "Entering Factory Mode SMB!\n"); + rc = smblib_masked_write_mmi(chip, USBIN_ICL_OPTIONS_REG, + USBIN_MODE_CHG_BIT, + USBIN_MODE_CHG_BIT); + if (rc < 0) + mmi_err(chip, + "Couldn't set USBIN_ICL_OPTIONS rc=%d\n", rc); + + rc = smblib_masked_write_mmi(chip, USBIN_LOAD_CFG_REG, + ICL_OVERRIDE_AFTER_APSD_BIT, + ICL_OVERRIDE_AFTER_APSD_BIT); + if (rc < 0) + mmi_err(chip, + "Couldn't set USBIN_LOAD_CFG rc=%d\n", rc); + + rc = smblib_masked_write_mmi(chip, USBIN_AICL_OPTIONS_CFG_REG, + 0xFF, 0x00); + if (rc < 0) + mmi_err(chip, + "Couldn't set USBIN_AICL_OPTIONS rc=%d\n", rc); + + chip->smb_reboot.notifier_call = smbchg_reboot; + chip->smb_reboot.next = NULL; + chip->smb_reboot.priority = 1; + rc = register_reboot_notifier(&chip->smb_reboot); + if (rc) + mmi_err(chip, "Register for reboot failed\n"); + rc = power_supply_get_property(chip->pc_port_psy, + POWER_SUPPLY_PROP_ONLINE, + &val); + if (rc >= 0 && val.intval) { + mmi_info(chip, "Factory Kill Armed\n"); + chip->factory_kill_armed = true; + } + + if(chip->chg_dis_votable) { + pmic_vote_force_val_set(chip->chg_dis_votable, 1); + pmic_vote_force_active_set(chip->chg_dis_votable, 1); + } + if(chip->fcc_votable) { + pmic_vote_force_val_set(chip->fcc_votable, 3000000); + pmic_vote_force_active_set(chip->fcc_votable, 1); + } + if(chip->fv_votable) { + pmic_vote_force_val_set(chip->fv_votable, 4400000); + pmic_vote_force_active_set(chip->fv_votable, 1); + } + + /* Some Cables need a more forced approach */ + rc = smblib_set_charge_param(chip, &chip->param.usb_icl, + 3000000); + if (rc < 0) + mmi_err(chip, + "Factory Couldn't set usb icl = 3000 rc=%d\n", + (int)rc); + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_usb_suspend); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_usb_suspend\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_fail_clear); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_fail_clear\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_auto_enable); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_auto_enable\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_ibatt); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_ibatt\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_iusb); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_iusb\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_idc); + if (rc) { + mmi_err(chip, "Couldn't create force_chg_idc\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_itrick); + if (rc) { + mmi_err(chip, "Couldn't create force_chg_itrick\n"); + } + + } + + smb_mmi_create_debugfs(chip); + cancel_delayed_work(&chip->heartbeat_work); + if(chip->factory_mode) + { + //delay heartbeat in factory mode until after boot + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(10000)); + } + else + { + schedule_delayed_work(&chip->heartbeat_work, + msecs_to_jiffies(0)); + } + + mmi_info(chip, "QPNP SMB MMI probed successfully!\n"); + + return rc; +} + +static void smb_mmi_shutdown(struct platform_device *pdev) +{ + struct smb_mmi_charger *chip = platform_get_drvdata(pdev); + + if (chip->qcom_psy) + power_supply_put(chip->qcom_psy); + if (chip->bms_psy) + power_supply_put(chip->bms_psy); + if (chip->usb_psy) + power_supply_put(chip->usb_psy); + if (chip->main_psy) + power_supply_put(chip->main_psy); + if (chip->pc_port_psy) + power_supply_put(chip->pc_port_psy); + if (chip->dc_psy) + power_supply_put(chip->dc_psy); + if (chip->max_main_psy) + power_supply_put(chip->max_main_psy); + if (chip->max_flip_psy) + power_supply_put(chip->max_flip_psy); + + PM_WAKEUP_UNREGISTER(chip->smb_mmi_hb_wake_source); + + return; +} + +#ifdef CONFIG_PM_SLEEP +static int smb_mmi_suspend(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct smb_mmi_charger *chip = platform_get_drvdata(pdev); + + chip->suspended &= ~WAS_SUSPENDED; + chip->suspended |= IS_SUSPENDED; + + return 0; +} + +static int smb_mmi_resume(struct device *device) +{ + struct platform_device *pdev = to_platform_device(device); + struct smb_mmi_charger *chip = platform_get_drvdata(pdev); + + chip->suspended &= ~IS_SUSPENDED; + chip->suspended |= WAS_SUSPENDED; + + return 0; +} +#else +#define smb_mmi_suspend NULL +#define smb_mmi_resume NULL +#endif + +static const struct dev_pm_ops smb_mmi_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(smb_mmi_suspend, smb_mmi_resume) +}; + +static const struct of_device_id match_table[] = { + { .compatible = "qcom,qpnp-smbcharger-mmi", }, + { }, +}; + +static struct platform_driver smb_mmi_driver = { + .driver = { + .name = "qcom,qpnp-smbcharger-mmi", + .owner = THIS_MODULE, + .pm = &smb_mmi_dev_pm_ops, + .of_match_table = match_table, + }, + .probe = smb_mmi_probe, + .shutdown = smb_mmi_shutdown, +}; +module_platform_driver(smb_mmi_driver); + +MODULE_DESCRIPTION("QPNP SMB MMI Charger Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/qpnp_adaptive_charge/Android.mk b/drivers/power/qpnp_adaptive_charge/Android.mk new file mode 100755 index 000000000000..64d813794407 --- /dev/null +++ b/drivers/power/qpnp_adaptive_charge/Android.mk @@ -0,0 +1,22 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := qpnp_adaptive_charge.ko +LOCAL_MODULE_TAGS := optional + +ifeq ($(USE_MMI_CHARGER), true) + KERNEL_CFLAGS += CONFIG_USE_MMI_CHARGER=y + KBUILD_OPTIONS += CONFIG_USE_MMI_CHARGER=y + LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_charger.ko +endif + +ifeq ($(ADAPTIVE_TOLERANCE_OPTIMIZATION),true) + KERNEL_CFLAGS += ADAPTIVE_TOLERANCE_OPTIMIZATION=y + KBUILD_OPTIONS += ADAPTIVE_TOLERANCE_OPTIMIZATION=y +endif + +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/power/qpnp_adaptive_charge/Kbuild b/drivers/power/qpnp_adaptive_charge/Kbuild new file mode 100755 index 000000000000..8471c67a4891 --- /dev/null +++ b/drivers/power/qpnp_adaptive_charge/Kbuild @@ -0,0 +1,15 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +ifneq ($(filter m y,$(CONFIG_USE_MMI_CHARGER)),) + EXTRA_CFLAGS += -DUSE_MMI_CHARGER + EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/mmi_charger + KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers +endif + +ifneq ($(filter m y, $(ADAPTIVE_TOLERANCE_OPTIMIZATION)),) + EXTRA_CFLAGS += -DADAPTIVE_TOLERANCE_OPTIMIZATION +endif + +obj-m += qpnp_adaptive_charge.o diff --git a/drivers/power/qpnp_adaptive_charge/Makefile b/drivers/power/qpnp_adaptive_charge/Makefile new file mode 100755 index 000000000000..d588e1c83b72 --- /dev/null +++ b/drivers/power/qpnp_adaptive_charge/Makefile @@ -0,0 +1,17 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers + diff --git a/drivers/power/qpnp_adaptive_charge/qpnp_adaptive_charge.c b/drivers/power/qpnp_adaptive_charge/qpnp_adaptive_charge.c new file mode 100755 index 000000000000..1394a74d7d17 --- /dev/null +++ b/drivers/power/qpnp_adaptive_charge/qpnp_adaptive_charge.c @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2020 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ +#define pr_fmt(fmt) "qpnp_adap_chg-[%s]: " fmt, __func__ +#include +#include +#ifdef USE_MMI_CHARGER +#include "mmi_charger.h" +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 61)) +#include +#else +#include +#endif +#include +#include +#include + +#ifdef USE_MMI_CHARGER +#elif (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 61)) +#define vote(votable, client_str, enabled, val) \ + mmi_vote(votable, client_str, enabled, val) + +#define find_votable(name) mmi_find_votable(name) +#endif + +static struct adap_chg_data { + struct power_supply *batt_psy; + struct power_supply *dc_psy; + struct power_supply *usb_psy; +#ifdef USE_MMI_CHARGER +#else + struct votable *usb_icl_votable; + struct votable *dc_suspend_votable; + struct votable *fcc_votable; + struct votable *chg_dis_votable; +#endif + int batt_capacity; + struct notifier_block ps_notif; + struct work_struct update; + bool init_success; + bool charging_suspended; + bool charging_stopped; + struct delayed_work reinit_work; +} adap_chg_data; + +int upper_limit = -1; +int lower_limit = -1; +int blocking = 0; + +#define ADAPTIVE_CHARGING_VOTER "ADAPTIVE_CHARGING_VOTER" + +static int get_ps_int_prop(struct power_supply *psy, enum power_supply_property prop) +{ + int rc; + union power_supply_propval val; + + if (!psy) + return -1; + + rc = power_supply_get_property(psy, + prop, &val); + if (rc < 0) { + pr_err("Error getting prop %d rc = %d\n", prop, rc); + return -1; + } + + return val.intval; +} + +/* Stop charging, no charging icon */ +static void suspend_charging(bool on) +{ + if(!adap_chg_data.charging_suspended && on) { +#ifdef USE_MMI_CHARGER + mmi_vote_charger_suspend(ADAPTIVE_CHARGING_VOTER, on); +#else + vote(adap_chg_data.usb_icl_votable, ADAPTIVE_CHARGING_VOTER, true, 0); + vote(adap_chg_data.dc_suspend_votable, ADAPTIVE_CHARGING_VOTER, true, 0); +#endif + adap_chg_data.charging_suspended = true; + pr_info("Set suspended on\n"); + } + + if(adap_chg_data.charging_suspended && !on) { +#ifdef USE_MMI_CHARGER + mmi_vote_charger_suspend(ADAPTIVE_CHARGING_VOTER, on); +#else + vote(adap_chg_data.usb_icl_votable, ADAPTIVE_CHARGING_VOTER, false, 0); + vote(adap_chg_data.dc_suspend_votable, ADAPTIVE_CHARGING_VOTER, false, 0); +#endif + adap_chg_data.charging_suspended = false; + pr_info("Set suspended off"); + } +} + +/* Limit charging to 0 mA, but keep charging icon, mimic what happens in charge_full state */ +static void stop_charging(bool on) +{ + if(!adap_chg_data.charging_stopped && on) { +#ifdef USE_MMI_CHARGER + mmi_vote_charging_disable(ADAPTIVE_CHARGING_VOTER, on); +#else + vote(adap_chg_data.fcc_votable, ADAPTIVE_CHARGING_VOTER, true, 0); + vote(adap_chg_data.chg_dis_votable, ADAPTIVE_CHARGING_VOTER, true, 0); +#endif + adap_chg_data.charging_stopped = true; + pr_info("Set stop charging on\n"); + } + + if(adap_chg_data.charging_stopped && !on) { +#ifdef USE_MMI_CHARGER + mmi_vote_charging_disable(ADAPTIVE_CHARGING_VOTER, on); +#else + vote(adap_chg_data.fcc_votable, ADAPTIVE_CHARGING_VOTER, false, 0); + vote(adap_chg_data.chg_dis_votable, ADAPTIVE_CHARGING_VOTER, false, 0); +#endif + adap_chg_data.charging_stopped = false; + pr_info("Set stop charging off\n"); + } +} + +static void update(struct adap_chg_data *data) +{ + data->batt_capacity = get_ps_int_prop(data->batt_psy, + POWER_SUPPLY_PROP_CAPACITY); + if (data->batt_capacity < 0) { + pr_err("Failed to get battery capacity\n"); + return; + } + + pr_info("Batt cap is %d, upper limit is %d, lower limit is %d\n", + data->batt_capacity, upper_limit, lower_limit); + + if (upper_limit != -1) { + /* If no lower limit is defined, we are in Auto Mode */ + if (lower_limit == -1) { +#ifdef ADAPTIVE_TOLERANCE_OPTIMIZATION + if (data->batt_capacity > (upper_limit + 2)) { +#else + if (data->batt_capacity > (upper_limit + 1)) { +#endif + suspend_charging(true); + stop_charging(true); + } else if (data->batt_capacity == (upper_limit + 1)) { + suspend_charging(false); + stop_charging(true); + } else if (data->batt_capacity < upper_limit) { + suspend_charging(false); + stop_charging(false); + } + /* Manual mode */ + } else { + if (data->batt_capacity >= upper_limit) { + suspend_charging(true); + stop_charging(false); + } else if (data->batt_capacity <= lower_limit) { + suspend_charging(false); + stop_charging(false); + } + } + /* Disable */ + } else { + suspend_charging(false); + stop_charging(false); + } +} + +static void update_work(struct work_struct *w) +{ + struct adap_chg_data *data = container_of(w, struct adap_chg_data, update); + int batt_capacity; + + batt_capacity = get_ps_int_prop(data->batt_psy, + POWER_SUPPLY_PROP_CAPACITY); + if (batt_capacity < 0) { + pr_err("Failed to get battery capacity\n"); + return; + } + + if (batt_capacity != data->batt_capacity) + update(data); +} + +static int set_upper_limit(const char *val, const struct kernel_param *kp) +{ + int rc; + long new_limit; + + if (!adap_chg_data.init_success) + return -ENODEV; + + rc = kstrtol(val, 0, &new_limit); + if (rc) + return rc; + + /* If both lower is off then turn it off */ + if (lower_limit == -1 && new_limit == -1) + upper_limit = new_limit; + /* Else check that we are valid (between 0 and 100 and bigger than lower) */ + else if (new_limit > 0 && new_limit <= 100 && new_limit > lower_limit) + upper_limit = new_limit; + /* Else force to 100 since lower is set */ + else + upper_limit = 100; + + pr_info("set upper limit to %d", upper_limit); + update(&adap_chg_data); + + return 0; +} + +static int set_lower_limit(const char *val, const struct kernel_param *kp) +{ + int rc; + long new_limit; + + if (!adap_chg_data.init_success) + return -ENODEV; + + rc = kstrtol(val, 0, &new_limit); + if (rc) + return rc; + + if (new_limit >= -1 && new_limit < upper_limit) + lower_limit = new_limit; + else + lower_limit = -1; + + pr_info("set lower limit to %d", lower_limit); + update(&adap_chg_data); + + return 0; +} + +static int set_blocking(const char *val, const struct kernel_param *kp) +{ + return -EINVAL; +} + +static int get_blocking(char *buffer, const struct kernel_param *kp) +{ + bool dc_connected = false; + bool usb_connected = false; + + if (adap_chg_data.init_success) { +#ifdef USE_MMI_CHARGER + usb_connected = (get_ps_int_prop(adap_chg_data.usb_psy, + POWER_SUPPLY_PROP_ONLINE) > 0 ? true : false); + + dc_connected = (get_ps_int_prop(adap_chg_data.dc_psy, + POWER_SUPPLY_PROP_ONLINE) > 0 ? true : false); +#else + usb_connected = (get_ps_int_prop(adap_chg_data.usb_psy, + POWER_SUPPLY_PROP_PRESENT) > 0 ? true : false); + + dc_connected = (get_ps_int_prop(adap_chg_data.dc_psy, + POWER_SUPPLY_PROP_PRESENT) > 0 ? true : false); +#endif + blocking = ((adap_chg_data.charging_suspended || adap_chg_data.charging_stopped) && + (dc_connected || usb_connected)); + } else + blocking = 0; + + return scnprintf(buffer, PAGE_SIZE, "%d", blocking); +} + +static int ps_notify_callback(struct notifier_block *nb, + unsigned long event, void *p) +{ + struct adap_chg_data *data = container_of(nb, struct adap_chg_data, ps_notif); + struct power_supply *psy = p; + + if (event == PSY_EVENT_PROP_CHANGED && + psy && psy->desc->get_property && psy->desc->name && + !strncmp(psy->desc->name, "battery", sizeof("battery"))) { + if (upper_limit != -1) + schedule_work(&data->update); + } + + return 0; +} + +static struct kernel_param_ops upper_limit_ops = +{ + .set = &set_upper_limit, + .get = param_get_int, +}; + +static struct kernel_param_ops lower_limit_ops = +{ + .set = &set_lower_limit, + .get = param_get_int, +}; + +static struct kernel_param_ops blocking_ops = +{ + .set = &set_blocking, + .get = &get_blocking, +}; + +module_param_cb(upper_limit, + &upper_limit_ops, + &upper_limit, + S_IRUGO | S_IWUSR +); + +module_param_cb(lower_limit, + &lower_limit_ops, + &lower_limit, + S_IRUGO | S_IWUSR +); + +module_param_cb(blocking, + &blocking_ops, + &blocking, + S_IRUGO +); + +#define ADAP_INIT_SUCCESS 0 +#define ADAP_INIT_FAILED 1 +#define ADAP_INIT_RERUN 2 +static int qpnp_adap_chg_init_work(void) +{ + upper_limit = -1; + lower_limit = -1; + adap_chg_data.init_success = false; + adap_chg_data.charging_suspended = false; + adap_chg_data.charging_stopped = false; + + adap_chg_data.batt_psy = power_supply_get_by_name("battery"); + if (!adap_chg_data.batt_psy) { + pr_err("Failed to get battery power supply\n"); + goto psy_fail; + } + + adap_chg_data.usb_psy = power_supply_get_by_name("usb"); + if (!adap_chg_data.usb_psy) { + pr_err("Failed to get usb power supply\n"); + goto psy_fail; + } + +#ifdef USE_MMI_CHARGER + adap_chg_data.dc_psy = power_supply_get_by_name("wireless"); + if (!adap_chg_data.dc_psy) + pr_info("No wireless power supply found\n"); +#else + adap_chg_data.dc_psy = power_supply_get_by_name("dc"); + if (!adap_chg_data.dc_psy) + pr_info("No dc power supply found\n"); +#endif + + adap_chg_data.batt_capacity = get_ps_int_prop(adap_chg_data.batt_psy, + POWER_SUPPLY_PROP_CAPACITY); + if (adap_chg_data.batt_capacity < 0) { + pr_err("Failed to get initial battery capacity\n"); + goto fail; + } + +#ifdef USE_MMI_CHARGER +#else + adap_chg_data.usb_icl_votable = find_votable("USB_ICL"); + if (IS_ERR(adap_chg_data.usb_icl_votable)) { + pr_err("Failed to get USB_ICL votable\n"); + goto fail; + } + + adap_chg_data.dc_suspend_votable = find_votable("DC_SUSPEND"); + if (IS_ERR(adap_chg_data.dc_suspend_votable)) { + pr_err("Failed to get DC_SUSPEND votable\n"); + goto fail; + } + + adap_chg_data.fcc_votable = find_votable("FCC"); + if (IS_ERR(adap_chg_data.fcc_votable)) { + pr_err("Failed to get FCC votable\n"); + goto fail; + } + + adap_chg_data.chg_dis_votable = find_votable("CHG_DISABLE"); + if (IS_ERR(adap_chg_data.chg_dis_votable)) { + pr_err("Failed to get CHG_DISABLE votable\n"); + goto fail; + } +#endif + + adap_chg_data.ps_notif.notifier_call = ps_notify_callback; + if (power_supply_reg_notifier(&adap_chg_data.ps_notif)) { + pr_err("Failed to register notifier\n"); + goto fail; + } + + INIT_WORK(&adap_chg_data.update, update_work); + schedule_work(&adap_chg_data.update); + + adap_chg_data.init_success = true; + return ADAP_INIT_SUCCESS; +fail: + return ADAP_INIT_FAILED; +psy_fail: + return ADAP_INIT_RERUN; + +} + +static void adap_reinit_work(struct work_struct *work) +{ + int ret = ADAP_INIT_FAILED; + ret = qpnp_adap_chg_init_work(); + + if (ret == ADAP_INIT_RERUN) { + cancel_delayed_work(&adap_chg_data.reinit_work); + schedule_delayed_work(&adap_chg_data.reinit_work, + msecs_to_jiffies(5000)); + } +} + +static int __init qpnp_adap_chg_init(void) +{ + int ret = ADAP_INIT_FAILED; + + ret = qpnp_adap_chg_init_work(); + + if (ret == ADAP_INIT_RERUN) { + INIT_DELAYED_WORK(&adap_chg_data.reinit_work, adap_reinit_work); + schedule_delayed_work(&adap_chg_data.reinit_work, + msecs_to_jiffies(5000)); + return ADAP_INIT_SUCCESS; + } else + return ret; +} + +static void qpnp_adap_chg_exit(void) +{ + if(adap_chg_data.init_success) { + cancel_work_sync(&adap_chg_data.update); + power_supply_unreg_notifier(&adap_chg_data.ps_notif); + } + + stop_charging(false); + suspend_charging(false); +} + +module_init(qpnp_adap_chg_init); +module_exit(qpnp_adap_chg_exit); + +MODULE_DESCRIPTION("Motorola QPNP adaptive charging"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/qpnp_smb_basic_charger/Android.mk b/drivers/power/qpnp_smb_basic_charger/Android.mk new file mode 100755 index 000000000000..6f506170c87f --- /dev/null +++ b/drivers/power/qpnp_smb_basic_charger/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(MOTO_PD_CHARGER),true) + KERNEL_CFLAGS += CONFIG_PD_CHARGER_MMI=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := qpnp_smb_basic_charger.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_charger.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/qpnp_smb_basic_charger/Kbuild b/drivers/power/qpnp_smb_basic_charger/Kbuild new file mode 100755 index 000000000000..a1a7eaead6bc --- /dev/null +++ b/drivers/power/qpnp_smb_basic_charger/Kbuild @@ -0,0 +1,12 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/mmi_charger +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +ifneq ($(filter m y,$(CONFIG_PD_CHARGER_MMI)),) + EXTRA_CFLAGS += -DCONFIG_PD_CHARGER_MMI +endif + +obj-m += qpnp_smb_basic_charger.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/qpnp_smb_basic_charger/Makefile b/drivers/power/qpnp_smb_basic_charger/Makefile new file mode 100755 index 000000000000..faa5fc435592 --- /dev/null +++ b/drivers/power/qpnp_smb_basic_charger/Makefile @@ -0,0 +1,12 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/qpnp_smb_basic_charger/qpnp_smb_basic_charger.c b/drivers/power/qpnp_smb_basic_charger/qpnp_smb_basic_charger.c new file mode 100644 index 000000000000..28a955f9e49b --- /dev/null +++ b/drivers/power/qpnp_smb_basic_charger/qpnp_smb_basic_charger.c @@ -0,0 +1,2889 @@ +/* Copyright (c) 2020, 2021 The Linux Foundation. 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 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mmi_charger.h" + +static bool debug_enabled; +module_param(debug_enabled, bool, 0600); +MODULE_PARM_DESC(debug_enabled, "Enable debug for QPNP SMB BASIC MMI driver"); + +static struct smb_mmi_charger *this_chip = NULL; + +#ifndef PM8150B_SUBTYPE +#define PM8150B_SUBTYPE PM855B_SUBTYPE +#endif +#ifndef PM7250B_SUBTYPE +#define PM7250B_SUBTYPE 0x2E +#endif + +#define CHGR_BASE 0x1000 +#define DCDC_BASE 0x1100 +#define BATIF_BASE 0x1200 +#define USBIN_BASE 0x1300 +#define DCIN_BASE 0x1400 +#define TYPEC_BASE 0X1500 +#define MISC_BASE 0x1600 + +#define BATTERY_CHARGER_STATUS_1_REG (CHGR_BASE + 0x06) +#define BATTERY_CHARGER_STATUS_MASK (GENMASK(2, 0)) +#define PM8150B_JEITA_EN_CFG_REG (CHGR_BASE + 0x90) + +#define POWER_PATH_STATUS_REG (DCDC_BASE + 0x0B) +#define MISC_POWER_PATH_STATUS_REG (MISC_BASE + 0x0B) +#define USBIN_SUSPEND_STS_BIT BIT(6) + +#define DCDC_CFG_REF_MAX_PSNS_REG (DCDC_BASE + 0x8C) + +#define APSD_STATUS_REG (USBIN_BASE + 0x07) +#define APSD_STATUS_7_BIT BIT(7) +#define HVDCP_CHECK_TIMEOUT_BIT BIT(6) +#define SLOW_PLUGIN_TIMEOUT_BIT BIT(5) +#define ENUMERATION_DONE_BIT BIT(4) +#define VADP_CHANGE_DONE_AFTER_AUTH_BIT BIT(3) +#define QC_AUTH_DONE_STATUS_BIT BIT(2) +#define QC_CHARGER_BIT BIT(1) +#define APSD_DTC_STATUS_DONE_BIT BIT(0) +#define APSD_RESULT_STATUS_REG (USBIN_BASE + 0x08) +#define APSD_RESULT_STATUS_7_BIT BIT(7) +#define APSD_RESULT_STATUS_MASK GENMASK(6, 0) +#define QC_3P0_BIT BIT(6) +#define QC_2P0_BIT BIT(5) +#define FLOAT_CHARGER_BIT BIT(4) +#define DCP_CHARGER_BIT BIT(3) +#define CDP_CHARGER_BIT BIT(2) +#define OCP_CHARGER_BIT BIT(1) +#define SDP_CHARGER_BIT BIT(0) +#define USBIN_INT_RT_STS (USBIN_BASE + 0x10) +#define USBIN_PLUGIN_RT_STS_BIT BIT(4) +#define USBIN_INT_EN_CLR (USBIN_BASE + 0x16) +#define USBIN_OV_EN_CLR BIT(3) +#define CMD_APSD_REG (USBIN_BASE + 0x41) +#define APSD_RERUN_BIT BIT(0) +#define ICL_OVERRIDE_BIT BIT(1) +#define USBIN_CMD_ICL_OVERRIDE_REG (USBIN_BASE + 0x42) +#define USBIN_ICL_OVERRIDE_BIT BIT(0) +#define HVDCP_PULSE_COUNT_MAX_REG (USBIN_BASE + 0x5B) +#define HVDCP_PULSE_COUNT_MAX_QC2_MASK GENMASK(7, 6) +#define HVDCP_PULSE_COUNT_MAX_QC3_MASK GENMASK(5, 0) +#define USBIN_ICL_OPTIONS_REG (USBIN_BASE + 0x66) +#define USBIN_MODE_CHG_BIT BIT(0) +#define USBIN_LOAD_CFG_REG (USBIN_BASE + 0x65) +#define ICL_OVERRIDE_AFTER_APSD_BIT BIT(4) +#define USBIN_AICL_OPTIONS_CFG_REG (USBIN_BASE + 0x80) +#define LEGACY_CABLE_CFG_REG (TYPEC_BASE + 0x5A) +#define USBIN_ADAPTER_ALLOW_CFG_REG (USBIN_BASE + 0x60) +#define USBIN_ADAPTER_ALLOW_MASK GENMASK(3, 0) + +/* DCIN Interrupt Bits */ +#define DCIN_PLUGIN_RT_STS_BIT BIT(4) +#define DCIN_INT_RT_STS (DCIN_BASE + 0x10) + +enum { + USBIN_ADAPTER_ALLOW_5V = 0, + USBIN_ADAPTER_ALLOW_9V = 2, + USBIN_ADAPTER_ALLOW_5V_OR_9V = 3, + USBIN_ADAPTER_ALLOW_12V = 4, + USBIN_ADAPTER_ALLOW_5V_OR_12V = 5, + USBIN_ADAPTER_ALLOW_9V_TO_12V = 6, + USBIN_ADAPTER_ALLOW_5V_OR_9V_TO_12V = 7, + USBIN_ADAPTER_ALLOW_5V_TO_9V = 8, + USBIN_ADAPTER_ALLOW_5V_TO_12V = 12, +}; + +#define BOOST_BACK_VOTER "BOOST_BACK_VOTER" +#define MMI_HB_VOTER "MMI_HB_VOTER" +#define BATT_PROFILE_VOTER "BATT_PROFILE_VOTER" +#define DEMO_VOTER "DEMO_VOTER" +#define HYST_STEP_MV 50 +#define HYST_STEP_FLIP_MV (HYST_STEP_MV*2) +#define DEMO_MODE_HYS_SOC 5 +#define DEMO_MODE_VOLTAGE 4000 +#define WARM_TEMP 45 +#define COOL_TEMP 0 +#define WEAK_CHARGER_WAIT 30000 +#define EVENT_WEAK_CHARGER_WAIT 0 + +#define HEARTBEAT_DELAY_MS 5000 +#define HEARTBEAT_DUAL_DELAY_MS 10000 +#define HEARTBEAT_FACTORY_MS 1000 +#define HEARTBEAT_DISCHARGE_MS 60000 + +#define EMPTY_CYCLES 101 + +enum { + TRICKLE_CHARGE = 0, + PRE_CHARGE, + FAST_CHARGE, + FULLON_CHARGE, + TAPER_CHARGE, + TERMINATE_CHARGE, + INHIBIT_CHARGE, + DISABLE_CHARGE, +}; + +enum { + INHIBIT_CHARGE_V5 = 0, + TRICKLE_CHARGE_V5, + PRE_CHARGE_V5, + FULLON_CHARGE_V5, + TAPER_CHARGE_V5, + TERMINATE_CHARGE_V5, + PAUSE_CHARGE_V5, + DISABLE_CHARGE_V5, +}; + +enum { + CHG_BC1P2_SDP = 0, + CHG_BC1P2_OCP, + CHG_BC1P2_CDP, + CHG_BC1P2_DCP, + CHG_BC1P2_FLOAT, + CHG_BC1P2_QC_2P0, + CHG_BC1P2_QC_3P0, + CHG_BC1P2_UNKNOWN, +}; + +enum { + CHG_CLIENT_PRIMARY = 0, + CHG_CLIENT_SECONDARY, +}; + +struct smb_mmi_chg_param { + const char *name; + u16 reg; + int min_u; + int max_u; + int step_u; + int (*get_proc)(struct smb_mmi_chg_param *param, + u8 val_raw); + int (*set_proc)(struct smb_mmi_chg_param *param, + int val_u, + u8 *val_raw); +}; + +struct smb_mmi_params { + struct smb_mmi_chg_param fcc; + struct smb_mmi_chg_param fv; + struct smb_mmi_chg_param usb_icl; + struct smb_mmi_chg_param dc_icl; + struct smb_mmi_chg_param aicl_cont_threshold; + struct smb_mmi_chg_param freq_switcher; +}; + +struct smb_mmi_chg_freq { + unsigned int freq_5V; + unsigned int freq_6V_8V; + unsigned int freq_9V; + unsigned int freq_12V; +}; + +struct smb_mmi_chg_client { + struct smb_mmi_charger *chip; + struct mmi_battery_info batt_info; + struct mmi_charger_cfg chg_cfg; + struct mmi_charger_driver *driver; + struct power_supply *client_batt_psy; + u32 chrg_taper_cnt; +}; + +struct smb_mmi_charger { + struct device *dev; + struct regmap *regmap; + struct smb_mmi_params param; + struct smb_mmi_chg_freq chg_freq; + const char *name; + int smb_version; + + struct power_supply *mmi_psy; + struct power_supply *batt_psy; + struct power_supply *qcom_psy; + struct power_supply *usb_psy; + struct power_supply *bms_psy; + struct power_supply *main_psy; + struct power_supply *pc_port_psy; + struct power_supply *dc_psy; + struct power_supply *wls_psy; + struct delayed_work charger_work; + + struct votable *chg_dis_votable; + struct votable *fcc_votable; + struct votable *fv_votable; + struct votable *usb_icl_votable; + struct votable *dc_suspend_votable; + + struct mmi_charger_info chg_info; + struct mmi_charger_constraint constraint; + int chg_client_num; + struct smb_mmi_chg_client *chg_clients; + struct regulator *vbus; + bool vbus_enabled; + int dc_cl_ma; + + bool *debug_enabled; + void *ipc_log; + struct usbpd *pd_handle; + int pd_current_pdo; +}; + +#define CHGR_FAST_CHARGE_CURRENT_CFG_REG (CHGR_BASE + 0x61) +#define CHGR_FLOAT_VOLTAGE_CFG_REG (CHGR_BASE + 0x70) +#define USBIN_CURRENT_LIMIT_CFG_REG (USBIN_BASE + 0x70) +#define DCIN_CURRENT_LIMIT_CFG_REG (DCIN_BASE + 0x70) +#define USBIN_CONT_AICL_THRESHOLD_REG (USBIN_BASE + 0x84) +#define DCDC_FSW_SEL_REG (DCDC_BASE + 0x50) +#define AICL_RANGE2_MIN_MV 5600 +#define AICL_RANGE2_STEP_DELTA_MV 200 +#define AICL_RANGE2_OFFSET 16 +int smblib_get_aicl_cont_threshold(struct smb_mmi_chg_param *param, u8 val_raw) +{ + int base = param->min_u; + u8 reg = val_raw; + int step = param->step_u; + + + if (val_raw >= AICL_RANGE2_OFFSET) { + reg = val_raw - AICL_RANGE2_OFFSET; + base = AICL_RANGE2_MIN_MV; + step = AICL_RANGE2_STEP_DELTA_MV; + } + + return base + (reg * step); +} + +int smblib_set_aicl_cont_threshold(struct smb_mmi_chg_param *param, + int val_u, u8 *val_raw) +{ + int base = param->min_u; + int offset = 0; + int step = param->step_u; + + if (val_u > param->max_u) + val_u = param->max_u; + if (val_u < param->min_u) + val_u = param->min_u; + + if (val_u >= AICL_RANGE2_MIN_MV) { + base = AICL_RANGE2_MIN_MV; + step = AICL_RANGE2_STEP_DELTA_MV; + offset = AICL_RANGE2_OFFSET; + }; + + *val_raw = ((val_u - base) / step) + offset; + + return 0; +} + +/******************** + * REGISTER SETTERS * + ********************/ + struct smb_buck_boost_freq { + int freq_khz; + u8 val; +}; +static const struct smb_buck_boost_freq chg_freq_list[] = { + [0] = { + .freq_khz = 2400, + .val = 7, + }, + [1] = { + .freq_khz = 2100, + .val = 8, + }, + [2] = { + .freq_khz = 1600, + .val = 11, + }, + [3] = { + .freq_khz = 1200, + .val = 15, + }, +}; + +int smblib_set_chg_freq(struct smb_mmi_chg_param *param, + int val_u, u8 *val_raw) +{ + u8 i; + + if (val_u > param->max_u || val_u < param->min_u) + return -EINVAL; + + /* Charger FSW is the configured freqency / 2 */ + val_u *= 2; + for (i = 0; i < ARRAY_SIZE(chg_freq_list); i++) { + if (chg_freq_list[i].freq_khz == val_u) + break; + } + if (i == ARRAY_SIZE(chg_freq_list)) { + pr_err("Invalid frequency %d Hz\n", val_u / 2); + return -EINVAL; + } + + *val_raw = chg_freq_list[i].val; + + return 0; +} + +static struct smb_mmi_params smb5_pm8150b_params = { + .fcc = { + .name = "fast charge current", + .reg = CHGR_FAST_CHARGE_CURRENT_CFG_REG, + .min_u = 0, + .max_u = 8000000, + .step_u = 50000, + }, + .fv = { + .name = "float voltage", + .reg = CHGR_FLOAT_VOLTAGE_CFG_REG, + .min_u = 3600000, + .max_u = 4790000, + .step_u = 10000, + }, + .usb_icl = { + .name = "usb input current limit", + .reg = USBIN_CURRENT_LIMIT_CFG_REG, + .min_u = 0, + .max_u = 5000000, + .step_u = 50000, + }, + .dc_icl = { + .name = "DC input current limit", + .reg = DCDC_CFG_REF_MAX_PSNS_REG, + .min_u = 0, + .max_u = 1500000, + .step_u = 50000, + }, + .aicl_cont_threshold = { + .name = "AICL CONT threshold", + .reg = USBIN_CONT_AICL_THRESHOLD_REG, + .min_u = 4000, + .max_u = 11800, + .step_u = 100, + .get_proc = smblib_get_aicl_cont_threshold, + .set_proc = smblib_set_aicl_cont_threshold, + }, + .freq_switcher = { + .name = "switching frequency", + .reg = DCDC_FSW_SEL_REG, + .min_u = 600, + .max_u = 1200, + .step_u = 400, + .set_proc = smblib_set_chg_freq, + }, +}; + +static struct smb_mmi_params smb5_pmi632_params = { + .fcc = { + .name = "fast charge current", + .reg = CHGR_FAST_CHARGE_CURRENT_CFG_REG, + .min_u = 0, + .max_u = 3000000, + .step_u = 50000, + }, + .fv = { + .name = "float voltage", + .reg = CHGR_FLOAT_VOLTAGE_CFG_REG, + .min_u = 3600000, + .max_u = 4800000, + .step_u = 10000, + }, + .usb_icl = { + .name = "usb input current limit", + .reg = USBIN_CURRENT_LIMIT_CFG_REG, + .min_u = 0, + .max_u = 3000000, + .step_u = 50000, + }, + .dc_icl = { + .name = "dc input current limit", + /* TODO: For now USBIN seems to be the way to set this */ + .reg = USBIN_CURRENT_LIMIT_CFG_REG, + .min_u = 0, + .max_u = 3000000, + .step_u = 50000, + }, + .freq_switcher = { + .name = "switching frequency", + .reg = DCDC_FSW_SEL_REG, + .min_u = 600, + .max_u = 1200, + .step_u = 400, + .set_proc = smblib_set_chg_freq, + }, +}; + +static struct smb_mmi_params smb2_pm660_params = { + .fcc = { + .name = "fast charge current", + .reg = CHGR_FAST_CHARGE_CURRENT_CFG_REG, + .min_u = 0, + .max_u = 4500000, + .step_u = 25000, + }, + .fv = { + .name = "float voltage", + .reg = CHGR_FLOAT_VOLTAGE_CFG_REG, + .min_u = 3487500, + .max_u = 4920000, + .step_u = 7500, + }, + .usb_icl = { + .name = "usb input current limit", + .reg = USBIN_CURRENT_LIMIT_CFG_REG, + .min_u = 0, + .max_u = 4800000, + .step_u = 25000, + }, + .dc_icl = { + .name = "dc input current limit", + .reg = DCIN_CURRENT_LIMIT_CFG_REG, + .min_u = 0, + .max_u = 6000000, + .step_u = 25000, + }, + .freq_switcher = { + .name = "switching frequency", + .reg = DCDC_FSW_SEL_REG, + .min_u = 600, + .max_u = 1200, + .step_u = 400, + .set_proc = smblib_set_chg_freq, + }, +}; + +static int smblib_read_mmi(struct smb_mmi_charger *chg, u16 addr, u8 *val) +{ + unsigned int value; + int rc = 0; + + rc = regmap_read(chg->regmap, addr, &value); + if (rc >= 0) + *val = (u8)value; + + return rc; +} + +static int smblib_write_mmi(struct smb_mmi_charger *chg, u16 addr, u8 val) +{ + return regmap_write(chg->regmap, addr, val); +} + +static int smblib_masked_write_mmi(struct smb_mmi_charger *chg, u16 addr, u8 mask, u8 val) +{ + return regmap_update_bits(chg->regmap, addr, mask, val); +} + +static int smblib_get_apsd_result(struct smb_mmi_charger *chg, int *type) +{ + int rc = 0; + u8 stat; + int apsd; + + rc = smblib_read_mmi(chg, APSD_STATUS_REG, &stat); + if (rc < 0) { + mmi_err(chg, "Couldn't read APSD_STATUS_REG rc=%d\n", rc); + return rc; + } + if (!(stat & APSD_DTC_STATUS_DONE_BIT)) + return -EBUSY; + + rc = smblib_read_mmi(chg, APSD_RESULT_STATUS_REG, &stat); + if (rc < 0) { + mmi_err(chg, "Couldn't read APSD_RESULT_STATUS_REG rc=%d\n", rc); + return rc; + } + + if (stat & QC_3P0_BIT) + apsd = CHG_BC1P2_QC_3P0; + else if (stat & QC_2P0_BIT) + apsd = CHG_BC1P2_QC_2P0; + else if (stat & FLOAT_CHARGER_BIT) + apsd = CHG_BC1P2_FLOAT; + else if (stat & DCP_CHARGER_BIT) + apsd = CHG_BC1P2_DCP; + else if (stat & CDP_CHARGER_BIT) + apsd = CHG_BC1P2_CDP; + else if (stat & OCP_CHARGER_BIT) + apsd = CHG_BC1P2_OCP; + else if (stat & SDP_CHARGER_BIT) + apsd = CHG_BC1P2_SDP; + else + apsd = CHG_BC1P2_UNKNOWN; + + *type = apsd; + + return rc; +} + +#define DCIN_CMD_IL_REG (DCIN_BASE + 0x40) +#define DCIN_SUSPEND_BIT BIT(0) +static int smblib_set_dc_suspend(struct smb_mmi_charger *chg, bool suspend) +{ + int rc = 0; + + rc = smblib_masked_write_mmi(chg, DCIN_CMD_IL_REG, DCIN_SUSPEND_BIT, + suspend ? DCIN_SUSPEND_BIT : 0); + if (rc < 0) + mmi_err(chg, "Couldn't write %s to DCIN_SUSPEND_BIT rc=%d\n", + suspend ? "suspend" : "resume", rc); + + return rc; +} + +#define USBIN_CMD_IL_REG (USBIN_BASE + 0x40) +#define USBIN_SUSPEND_BIT BIT(0) +static int smblib_set_usb_suspend(struct smb_mmi_charger *chg, bool suspend) +{ + int rc = 0; + + rc = smblib_masked_write_mmi(chg, USBIN_CMD_IL_REG, USBIN_SUSPEND_BIT, + suspend ? USBIN_SUSPEND_BIT : 0); + if (rc < 0) + mmi_err(chg, "Couldn't write %s to USBIN suspend rc=%d\n", + suspend ? "suspend" : "resume", rc); + + return rc; +} + +static int smblib_is_usb_suspended(struct smb_mmi_charger *chg) +{ + int rc = 0; + u8 temp = 0; + + if (chg->smb_version == PM660_SUBTYPE) { + rc = smblib_read_mmi(chg, MISC_POWER_PATH_STATUS_REG, &temp); + } else + rc = smblib_read_mmi(chg, POWER_PATH_STATUS_REG, &temp); + if (rc < 0) + mmi_err(chg, "Couldn't read POWER_PATH_STATUS_REG rc=%d\n", rc); + + return (temp & USBIN_SUSPEND_STS_BIT); +} + +static int smblib_set_charge_param(struct smb_mmi_charger *chg, + struct smb_mmi_chg_param *param, int val_u) +{ + int rc = 0; + u8 val_raw; + + if (param->set_proc) { + rc = param->set_proc(param, val_u, &val_raw); + if (rc < 0) + return -EINVAL; + } else { + if (val_u > param->max_u || val_u < param->min_u) + mmi_dbg(chg, "%s: %d is out of range [%d, %d]\n", + param->name, val_u, + param->min_u, param->max_u); + + if (val_u > param->max_u) + val_u = param->max_u; + if (val_u < param->min_u) + val_u = param->min_u; + + val_raw = (val_u - param->min_u) / param->step_u; + } + + rc = smblib_write_mmi(chg, param->reg, val_raw); + if (rc < 0) { + mmi_err(chg, "%s: Couldn't write 0x%02x to 0x%04x rc=%d\n", + param->name, val_raw, param->reg, rc); + return rc; + } + + mmi_dbg(chg, "%s = %d (0x%02x)\n", param->name, val_u, val_raw); + + return rc; +} + +static int smblib_get_charge_param(struct smb_mmi_charger *chg, + struct smb_mmi_chg_param *param, int *val_u) +{ + int rc = 0; + u8 val_raw; + + rc = smblib_read_mmi(chg, param->reg, &val_raw); + if (rc < 0) { + mmi_err(chg, "%s: Couldn't read from 0x%04x rc=%d\n", + param->name, param->reg, rc); + return rc; + } + + if (param->get_proc) + *val_u = param->get_proc(param, val_raw); + else + *val_u = val_raw * param->step_u + param->min_u; + mmi_dbg(chg, "%s = %d (0x%02x)\n", param->name, *val_u, val_raw); + + return rc; +} + +static ssize_t force_chg_usb_suspend_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + struct smb_mmi_charger *chip = dev_get_drvdata(dev); + + if (!chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &mode); + if (r) { + mmi_err(chip, "Invalid usb suspend mode value = %lu\n", mode); + return -EINVAL; + } + + r = smblib_set_usb_suspend(chip, (bool)mode); + + return r ? r : count; +} + +static ssize_t force_chg_usb_suspend_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + struct smb_mmi_charger *chip = dev_get_drvdata(dev); + + if (!chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + state = smblib_is_usb_suspended(chip); + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_usb_suspend, 0664, + force_chg_usb_suspend_show, + force_chg_usb_suspend_store); + +static ssize_t force_chg_fail_clear_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + struct smb_mmi_charger *chip = dev_get_drvdata(dev); + + if (!chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &mode); + if (r) { + mmi_err(chip, "Invalid chg fail mode value = %lu\n", mode); + return -EINVAL; + } + + /* do nothing for SMBCHG */ + r = 0; + + return r ? r : count; +} + +static ssize_t force_chg_fail_clear_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + /* do nothing for SMBCHG */ + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "0\n"); +} + +static DEVICE_ATTR(force_chg_fail_clear, 0664, + force_chg_fail_clear_show, + force_chg_fail_clear_store); + +#define CHARGING_ENABLE_CMD_REG (CHGR_BASE + 0x42) +#define CHARGING_ENABLE_CMD_BIT BIT(0) +static ssize_t force_chg_auto_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + struct smb_mmi_charger *chip = dev_get_drvdata(dev); + + if (!chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &mode); + if (r) { + mmi_err(chip, "Invalid chrg enable value = %lu\n", mode); + return -EINVAL; + } + + r = smblib_masked_write_mmi(chip, CHARGING_ENABLE_CMD_REG, + CHARGING_ENABLE_CMD_BIT, + mode ? CHARGING_ENABLE_CMD_BIT : 0); + if (r < 0) { + mmi_err(chip, "Factory Couldn't %s charging rc=%d\n", + mode ? "enable" : "disable", (int)r); + return r; + } + + return r ? r : count; +} + +static ssize_t force_chg_auto_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int ret; + u8 value; + struct smb_mmi_charger *chip = dev_get_drvdata(dev); + + if (!chip) { + pr_err("SMBMMI: chip not valid\n"); + state = -ENODEV; + goto end; + } + + ret = smblib_read_mmi(chip, CHARGING_ENABLE_CMD_REG, &value); + if (ret) { + mmi_err(chip, "CHG_EN_BIT failed ret = %d\n", ret); + state = -EFAULT; + goto end; + } + + state = (CHARGING_ENABLE_CMD_BIT & value) ? 1 : 0; +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_auto_enable, 0664, + force_chg_auto_enable_show, + force_chg_auto_enable_store); + +static ssize_t force_chg_ibatt_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long chg_current; + struct smb_mmi_charger *chip = dev_get_drvdata(dev); + + if (!chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &chg_current); + if (r) { + mmi_err(chip, "Invalid ibatt value = %lu\n", chg_current); + return -EINVAL; + } + + chg_current *= 1000; /* Convert to uA */ + if(chip->fcc_votable) { + pmic_vote_force_val_set(chip->fcc_votable, chg_current); + pmic_vote_force_active_set(chip->fcc_votable, 1); + } + + return r ? r : count; +} + +static ssize_t force_chg_ibatt_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int ret; + struct smb_mmi_charger *chip = dev_get_drvdata(dev); + + if (!chip) { + pr_err("SMBMMI: chip not valid\n"); + state = -ENODEV; + goto end; + } + + ret = smblib_get_charge_param(chip, &chip->param.fcc, &state); + if (ret < 0) { + mmi_err(chip, "Factory Couldn't get master fcc rc=%d\n", (int)ret); + return ret; + } + + state /= 1000; /* Convert to mA */ +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_ibatt, 0664, + force_chg_ibatt_show, + force_chg_ibatt_store); + +static ssize_t force_chg_iusb_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long usb_curr; + struct smb_mmi_charger *chip = dev_get_drvdata(dev); + + if (!chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &usb_curr); + if (r) { + mmi_err(chip, "Invalid iusb value = %lu\n", usb_curr); + return -EINVAL; + } + + usb_curr *= 1000; /* Convert to uA */ + if (chip->usb_icl_votable) { + pmic_vote_force_val_set(chip->usb_icl_votable, usb_curr); + pmic_vote_force_active_set(chip->usb_icl_votable, 1); + } + + r = smblib_masked_write_mmi(chip, USBIN_ICL_OPTIONS_REG, + USBIN_MODE_CHG_BIT, USBIN_MODE_CHG_BIT); + if (r < 0) + mmi_err(chip, "Couldn't set USBIN_ICL_OPTIONS r=%d\n", (int)r); + + r = smblib_masked_write_mmi(chip, USBIN_LOAD_CFG_REG, + ICL_OVERRIDE_AFTER_APSD_BIT, + ICL_OVERRIDE_AFTER_APSD_BIT); + if (r < 0) + mmi_err(chip, "Couldn't set USBIN_LOAD_CFG rc=%d\n", (int)r); + + r = smblib_masked_write_mmi(chip, USBIN_AICL_OPTIONS_CFG_REG, + 0xFF, 0x00); + if (r < 0) + mmi_err(chip, "Couldn't set USBIN_AICL_OPTIONS rc=%d\n", (int)r); + + return r ? r : count; +} + +static ssize_t force_chg_iusb_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int r; + struct smb_mmi_charger *chip = dev_get_drvdata(dev); + + if (!chip) { + pr_err("SMBMMI: chip not valid\n"); + state = -ENODEV; + goto end; + } + + r = smblib_get_charge_param(chip, &chip->param.usb_icl, &state); + if (r < 0) { + mmi_err(chip, "Factory Couldn't get usb_icl rc=%d\n", (int)r); + return r; + } + state /= 1000; /* Convert to mA */ +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_iusb, 0664, + force_chg_iusb_show, + force_chg_iusb_store); + +static ssize_t force_chg_idc_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long dc_curr; + struct smb_mmi_charger *chip = dev_get_drvdata(dev); + + if (!chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &dc_curr); + if (r) { + mmi_err(chip, "Invalid idc value = %lu\n", dc_curr); + return -EINVAL; + } + + dc_curr *= 1000; /* Convert to uA */ + r = smblib_set_charge_param(chip, &chip->param.dc_icl, dc_curr); + if (r < 0) { + mmi_err(chip, "Factory Couldn't set dc icl = %d rc=%d\n", + (int)dc_curr, (int)r); + return r; + } + + return r ? r : count; +} + +static ssize_t force_chg_idc_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int r; + struct smb_mmi_charger *chip = dev_get_drvdata(dev); + + if (!chip) { + pr_err("SMBMMI: chip not valid\n"); + state = -ENODEV; + goto end; + } + + r = smblib_get_charge_param(chip, &chip->param.dc_icl, &state); + if (r < 0) { + mmi_err(chip, "Factory Couldn't get dc_icl rc=%d\n", (int)r); + return r; + } + state /= 1000; /* Convert to mA */ +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_idc, 0664, + force_chg_idc_show, + force_chg_idc_store); + +#define PRE_CHARGE_CURRENT_CFG_REG (CHGR_BASE + 0x60) +#define PRE_CHARGE_CURRENT_SETTING_MASK 0x07 +#define PRE_CHARGE_CONV_MV 50 +#define PRE_CHARGE_MAX 7 +#define PRE_CHARGE_MIN 100 +#define PRE_CHARGE_SMB2_CONV_MV 25 +#define PRE_CHARGE_SMB2_MAX 0x3F +static ssize_t force_chg_itrick_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long chg_current; + u8 value, mask; + struct smb_mmi_charger *chip = dev_get_drvdata(dev); + + if (!chip) { + pr_err("SMBMMI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &chg_current); + if (r) { + mmi_err(chip, "Invalid pre-charge value = %lu\n", chg_current); + return -EINVAL; + } + + switch (chip->smb_version) { + case PM8150B_SUBTYPE: + case PM7250B_SUBTYPE: + case PMI632_SUBTYPE: + mask = PRE_CHARGE_CURRENT_SETTING_MASK; + if (chg_current < PRE_CHARGE_MIN) + chg_current = 0; + else + chg_current -= PRE_CHARGE_MIN; + + chg_current /= PRE_CHARGE_CONV_MV; + + if (chg_current > PRE_CHARGE_MAX) + value = PRE_CHARGE_MAX; + else + value = (u8)chg_current; + break; + case PM660_SUBTYPE: + mask = PRE_CHARGE_SMB2_MAX; + chg_current /= PRE_CHARGE_SMB2_CONV_MV; + + if (chg_current > PRE_CHARGE_SMB2_MAX) + value = PRE_CHARGE_SMB2_MAX; + else + value = (u8)chg_current; + break; + default: + mmi_err(chip, "Set ITRICK PMIC subtype %d not supported\n", + chip->smb_version); + return -EINVAL; + } + + r = smblib_masked_write_mmi(chip, PRE_CHARGE_CURRENT_CFG_REG, + mask, + value); + if (r < 0) { + mmi_err(chip, "Factory Couldn't set ITRICK %d mV rc=%d\n", + (int)value, (int)r); + return r; + } + + return r ? r : count; +} + +static ssize_t force_chg_itrick_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int state; + int ret; + u8 value; + struct smb_mmi_charger *chip = dev_get_drvdata(dev); + + if (!chip) { + pr_err("SMBMMI: chip not valid\n"); + state = -ENODEV; + goto end; + } + + ret = smblib_read_mmi(chip, PRE_CHARGE_CURRENT_CFG_REG, &value); + if (ret) { + mmi_err(chip, "Pre Chg ITrick failed ret = %d\n", ret); + state = -EFAULT; + goto end; + } + + switch (chip->smb_version) { + case PM8150B_SUBTYPE: + case PM7250B_SUBTYPE: + case PMI632_SUBTYPE: + value &= PRE_CHARGE_CURRENT_SETTING_MASK; + state = (value * PRE_CHARGE_CONV_MV) + PRE_CHARGE_MIN; + break; + case PM660_SUBTYPE: + value &= PRE_CHARGE_SMB2_MAX; + state = value * PRE_CHARGE_SMB2_CONV_MV; + break; + default: + mmi_err(chip, "Get ITRICK PMIC subtype %d not supported\n", + chip->smb_version); + return -EINVAL; + } +end: + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state); +} + +static DEVICE_ATTR(force_chg_itrick, 0664, + force_chg_itrick_show, + force_chg_itrick_store); + +static int get_prop_usb_real_type(struct smb_mmi_charger *chip, + union power_supply_propval *val) +{ + int rc; + + if (!chip->usb_psy) + return -EINVAL; + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_REAL_TYPE, val); + return rc; +} + +static int get_prop_usb_voltage_now(struct smb_mmi_charger *chip, + union power_supply_propval *val) +{ + int rc; + + if (!chip->usb_psy) + return -EINVAL; + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, val); + return rc; +} + +static int get_prop_dc_voltage_now(struct smb_mmi_charger *chip, + union power_supply_propval *val) +{ + int rc; + + if (!chip->dc_psy) + return -EINVAL; + + rc = power_supply_get_property(chip->dc_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, val); + return rc; +} + +static int get_prop_usb_current_now(struct smb_mmi_charger *chip, + union power_supply_propval *val) +{ + int rc; + + if (!chip->usb_psy) + return -EINVAL; + + rc = power_supply_get_property(chip->usb_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, val); + return rc; +} + +static int get_prop_dc_current_now(struct smb_mmi_charger *chip, + union power_supply_propval *val) +{ + int rc; + + if (!chip->dc_psy) + return -EINVAL; + + rc = power_supply_get_property(chip->dc_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, val); + return rc; +} + +static int get_prop_usb_present(struct smb_mmi_charger *chip, + union power_supply_propval *val) +{ + int rc; + u8 stat; + + rc = smblib_read_mmi(chip, USBIN_INT_RT_STS, &stat); + if (rc < 0) { + mmi_err(chip, "Couldn't read USBIN_RT_STS rc=%d\n", rc); + return rc; + } + + val->intval = (bool)(stat & USBIN_PLUGIN_RT_STS_BIT); + + return rc; +} + +static int get_prop_dc_present(struct smb_mmi_charger *chip, + union power_supply_propval *val) +{ + int rc; + u8 stat; + + /* + * PMI632 has no DCIN support and address at 0x14XX is undefined, + * so just return 0 to avoid too much err log and heatbeat_work + * break + */ + if (chip->smb_version == PMI632_SUBTYPE) { + val->intval = 0; + return 0; + } + + rc = smblib_read_mmi(chip, DCIN_INT_RT_STS, &stat); + if (rc < 0) { + mmi_err(chip, "Couldn't read DCIN_RT_STS rc=%d\n", rc); + return rc; + } + + val->intval = (bool)(stat & DCIN_PLUGIN_RT_STS_BIT); + + return 0; +} + +static int is_wls_online(struct smb_mmi_charger *chip) +{ + int rc; + union power_supply_propval val; + + if (!chip->wls_psy) + return 0; + + rc = power_supply_get_property(chip->wls_psy, + POWER_SUPPLY_PROP_ONLINE, &val); + if (rc < 0) { + mmi_err(chip, "Error wls online rc = %d\n", rc); + return 0; + } + + return val.intval; +} + +#define TAPER_COUNT 2 +#define TAPER_DROP_MA 100 +static bool smb_mmi_has_current_tapered(void *data, int taper_ma) +{ + bool change_state = false; + int allowed_fcc, target_ma; + int batt_ma; + struct smb_mmi_chg_client *chg = data; + + if (!chg) { + mmi_err(chg->chip, "Charger chip is unavailable!\n"); + return false; + } + + allowed_fcc = get_effective_result(chg->chip->fcc_votable) / 1000; + + if (allowed_fcc >= taper_ma) + target_ma = taper_ma; + else + target_ma = allowed_fcc - TAPER_DROP_MA; + + batt_ma = chg->batt_info.batt_ma; + if (batt_ma < 0) { + batt_ma *= -1; + if (batt_ma <= target_ma) + if (chg->chrg_taper_cnt >= TAPER_COUNT) { + change_state = true; + chg->chrg_taper_cnt = 0; + } else + chg->chrg_taper_cnt++; + else + chg->chrg_taper_cnt = 0; + } else { + if (chg->chrg_taper_cnt >= TAPER_COUNT) { + change_state = true; + chg->chrg_taper_cnt = 0; + } else + chg->chrg_taper_cnt++; + } + + return change_state; +} + +static bool smb_mmi_charge_halted(void *data) +{ + u8 stat; + int rc; + bool flag = false; + struct smb_mmi_chg_client *chg = data; + + rc = smblib_read_mmi(chg->chip, BATTERY_CHARGER_STATUS_1_REG, &stat); + if (rc < 0) { + mmi_err(chg->chip, "Couldn't read BATTERY_CHARGER_STATUS_1 rc=%d\n", + rc); + return flag; + } + stat = stat & BATTERY_CHARGER_STATUS_MASK; + + if (chg->chip->smb_version == PM8150B_SUBTYPE || + chg->chip->smb_version == PM7250B_SUBTYPE || + chg->chip->smb_version == PMI632_SUBTYPE) { + switch (stat) { + case TERMINATE_CHARGE_V5: + case INHIBIT_CHARGE_V5: + flag = true; + break; + default: + break; + } + } else { + switch (stat) { + case TERMINATE_CHARGE: + case INHIBIT_CHARGE: + flag = true; + break; + default: + break; + } + } + + return flag; +} + +static int smb_mmi_get_batt_info(void *data, struct mmi_battery_info *batt_info) +{ + int rc; + union power_supply_propval pval; + struct smb_mmi_chg_client *chg = data; + struct smb_mmi_charger *chip = chg->chip; + struct power_supply *client_batt_psy; + + rc = power_supply_get_property(chip->qcom_psy, + POWER_SUPPLY_PROP_STATUS, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Status rc = %d\n", rc); + } else if (chg->chg_cfg.full_charged) + chg->batt_info.batt_status = POWER_SUPPLY_STATUS_FULL; + else + chg->batt_info.batt_status = pval.intval; + + if (chg->client_batt_psy == chip->batt_psy) + client_batt_psy = chip->qcom_psy; + else + client_batt_psy = chg->client_batt_psy; + + rc = power_supply_get_property(client_batt_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Voltage rc = %d\n", rc); + } else + chg->batt_info.batt_mv = pval.intval / 1000; + + rc = power_supply_get_property(client_batt_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Current rc = %d\n", rc); + } else + chg->batt_info.batt_ma = pval.intval / 1000; + + rc = power_supply_get_property(client_batt_psy, + POWER_SUPPLY_PROP_CAPACITY, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Capacity rc = %d\n", rc); + } else + chg->batt_info.batt_soc = pval.intval; + + rc = power_supply_get_property(client_batt_psy, + POWER_SUPPLY_PROP_TEMP, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Temperature rc = %d\n", rc); + } else + chg->batt_info.batt_temp = pval.intval / 10; + + rc = power_supply_get_property(client_batt_psy, + POWER_SUPPLY_PROP_CHARGE_FULL, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt charge_full rc = %d\n", rc); + } else + chg->batt_info.batt_full_uah = pval.intval; + + rc = power_supply_get_property(client_batt_psy, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, &pval); + if (rc < 0) { + mmi_err(chip, "Error getting Batt charge_full_design rc = %d\n", rc); + } else + chg->batt_info.batt_design_uah = pval.intval; + + memcpy(batt_info, &chg->batt_info, sizeof(struct mmi_battery_info)); + + return rc; +} + +static int smb_mmi_get_chg_info(void *data, struct mmi_charger_info *chg_info) +{ + int rc; + int usb_type; + int usb_icl; + union power_supply_propval val; + struct smb_mmi_chg_client *chg = data; + struct smb_mmi_charger *chip = chg->chip; + + /* USB charger */ + val.intval = 0; + rc = get_prop_usb_present(chip, &val); + if (rc < 0) + mmi_err(chip, "Couldn't read usb present rc=%d\n", rc); + else if (val.intval) { + chip->chg_info.chrg_present = 1; + val.intval = 0; + rc = get_prop_usb_voltage_now(chip, &val); + if (rc < 0) + mmi_err(chip, "Couldn't read usb voltage rc=%d\n", rc); + chip->chg_info.chrg_mv = val.intval / 1000; + val.intval = 0; + rc = get_prop_usb_current_now(chip, &val); + if (rc < 0) + mmi_err(chip, "Couldn't read usb current rc=%d\n", rc); + chip->chg_info.chrg_ma = val.intval / 1000; + val.intval = 0; + rc = get_prop_usb_real_type(chip, &val); + if (rc < 0) + mmi_err(chip, "Couldn't read usb type rc=%d\n", rc); + chip->chg_info.chrg_type = val.intval; + + usb_type = chip->chg_info.chrg_type; + if (usb_type == POWER_SUPPLY_TYPE_USB) + chip->chg_info.chrg_pmax_mw = 2500; + else if (usb_type == POWER_SUPPLY_TYPE_USB_CDP) + chip->chg_info.chrg_pmax_mw = 7500; + else if (usb_type == POWER_SUPPLY_TYPE_USB_DCP) + chip->chg_info.chrg_pmax_mw = chip->constraint.dcp_pmax; + else if (usb_type == POWER_SUPPLY_TYPE_USB_HVDCP) + chip->chg_info.chrg_pmax_mw = 7500; + else if (usb_type == POWER_SUPPLY_TYPE_USB_HVDCP_3 || + usb_type == POWER_SUPPLY_TYPE_USB_HVDCP_3P5) + chip->chg_info.chrg_pmax_mw = chip->constraint.hvdcp_pmax; + else if (usb_type == POWER_SUPPLY_TYPE_USB_PD) + chip->chg_info.chrg_pmax_mw = chip->constraint.pd_pmax; + else + chip->chg_info.chrg_pmax_mw = 2500; + + usb_icl = get_effective_result(chip->usb_icl_votable); + if (chip->chg_info.chrg_pmax_mw < (usb_icl * 5 / 1000)) + chip->chg_info.chrg_pmax_mw = usb_icl * 5 / 1000; + + rc = 0; + goto completed; + } + + /* DC charger */ + val.intval = 0; + rc = get_prop_dc_present(chip, &val); + if (rc < 0) + mmi_err(chip, "Couldn't read dc present rc=%d\n", rc); + else if (val.intval) { + chip->chg_info.chrg_present = 1; + val.intval = 0; + rc = get_prop_dc_voltage_now(chip, &val); + if (rc < 0) + mmi_err(chip, "Couldn't read dc voltage rc=%d\n", rc); + chip->chg_info.chrg_mv = val.intval / 1000; + val.intval = 0; + rc = get_prop_dc_current_now(chip, &val); + if (rc < 0) + mmi_err(chip, "Couldn't read dc current rc=%d\n", rc); + chip->chg_info.chrg_ma = val.intval / 1000; + + chip->chg_info.chrg_type = POWER_SUPPLY_TYPE_WIPOWER; + chip->chg_info.chrg_pmax_mw = chip->constraint.wls_pmax; + + rc = 0; + goto completed; + } + + /* Wireless charger */ + if (is_wls_online(chip)) { + chip->chg_info.chrg_present = 1; + val.intval = 0; + rc = get_prop_dc_voltage_now(chip, &val); + if (rc < 0) + mmi_err(chip, "Couldn't read wls voltage rc=%d\n", rc); + chip->chg_info.chrg_mv = val.intval / 1000; + val.intval = 0; + rc = get_prop_dc_current_now(chip, &val); + if (rc < 0) + mmi_err(chip, "Couldn't read wls current rc=%d\n", rc); + chip->chg_info.chrg_ma = val.intval / 1000; + + chip->chg_info.chrg_type = POWER_SUPPLY_TYPE_WIPOWER; + chip->chg_info.chrg_pmax_mw = chip->constraint.wls_pmax; + + rc = 0; + goto completed; + } + + chip->chg_info.chrg_mv = 0; + chip->chg_info.chrg_ma = 0; + chip->chg_info.chrg_type = 0; + chip->chg_info.chrg_pmax_mw = 0; + chip->chg_info.chrg_present = 0; + +completed: + memcpy(chg_info, &chip->chg_info, sizeof(struct mmi_charger_info)); + + return rc; +} + +static int smb_mmi_config_charge(void *data, struct mmi_charger_cfg *config) +{ + struct smb_mmi_chg_client *chg = data; + + if (memcmp(config, &chg->chg_cfg, sizeof(struct mmi_charger_cfg))) { + memcpy(&chg->chg_cfg, config, sizeof(struct mmi_charger_cfg)); + cancel_delayed_work(&chg->chip->charger_work); + schedule_delayed_work(&chg->chip->charger_work, + msecs_to_jiffies(0)); + } + + return 0; +} + +static void smb_mmi_set_constraint(void *data, + struct mmi_charger_constraint *constraint) +{ + struct smb_mmi_chg_client *chg = data; + + if (memcmp(constraint, &chg->chip->constraint, + sizeof(struct mmi_charger_constraint))) { + memcpy(&chg->chip->constraint, constraint, + sizeof(struct mmi_charger_constraint)); + cancel_delayed_work(&chg->chip->charger_work); + schedule_delayed_work(&chg->chip->charger_work, + msecs_to_jiffies(0)); + } +} + +#define USBIN_500MA 500000 +#define USBIN_900MA 900000 +static int smb_mmi_usb_icl_override(struct smb_mmi_charger *chg, int icl) +{ + int rc = 0; + int apsd; + u8 usb51_mode, icl_override, apsd_override; + union power_supply_propval val; + + if (chg->constraint.factory_mode) + return rc; + + if (chg->usb_psy) { + val.intval = POWER_SUPPLY_TYPEC_NONE; + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_TYPEC_MODE, &val); + if (rc < 0 || val.intval != POWER_SUPPLY_TYPEC_NONE) + return rc; + } + + rc = smblib_get_apsd_result(chg, &apsd); + if (rc < 0 || apsd == CHG_BC1P2_UNKNOWN) + return rc; + + if (apsd == CHG_BC1P2_SDP && + (icl == USBIN_900MA || icl <= USBIN_500MA)) { + usb51_mode = 0; + apsd_override = 0; + if (chg->smb_version == PM660_SUBTYPE) + icl_override = ICL_OVERRIDE_BIT; + else + icl_override = USBIN_ICL_OVERRIDE_BIT; + } else { + usb51_mode = USBIN_MODE_CHG_BIT; + apsd_override = ICL_OVERRIDE_AFTER_APSD_BIT; + if (chg->smb_version == PM660_SUBTYPE) + icl_override = ICL_OVERRIDE_BIT; + else + icl_override = USBIN_ICL_OVERRIDE_BIT; + } + + if (chg->main_psy) { + val.intval = icl; + rc = power_supply_set_property(chg->main_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, + &val); + if (rc < 0) { + mmi_err(chg, "Couldn't set usb icl, rc=%d\n", rc); + } + } + + rc = smblib_masked_write_mmi(chg, USBIN_ICL_OPTIONS_REG, + USBIN_MODE_CHG_BIT, + usb51_mode); + if (rc < 0) + mmi_err(chg, + "Couldn't set USBIN_ICL_OPTIONS rc=%d\n", rc); + + if (chg->smb_version == PM660_SUBTYPE) { + rc = smblib_masked_write_mmi(chg, CMD_APSD_REG, + ICL_OVERRIDE_BIT, + icl_override); + if (rc < 0) + mmi_err(chg, + "Couldn't set ICL_OVERRIDE_BIT rc=%d\n", rc); + } else { + rc = smblib_masked_write_mmi(chg, USBIN_CMD_ICL_OVERRIDE_REG, + USBIN_ICL_OVERRIDE_BIT, + icl_override); + if (rc < 0) + mmi_err(chg, + "Couldn't set USBIN_CMD_ICL_OVERRIDE rc=%d\n", rc); + } + + rc = smblib_masked_write_mmi(chg, USBIN_LOAD_CFG_REG, + ICL_OVERRIDE_AFTER_APSD_BIT, + apsd_override); + if (rc < 0) + mmi_err(chg, + "Couldn't set USBIN_LOAD_CFG rc=%d\n", rc); + + return rc; +} + +#define HVDCP_POWER_MIN 15000 +#define HVDCP_VOLTAGE_BASIC (this_chip->constraint.hvdcp_pmax / 3) +#define HVDCP_VOLTAGE_NOM (HVDCP_VOLTAGE_BASIC - 200) +#define HVDCP_VOLTAGE_MAX (HVDCP_VOLTAGE_BASIC + 200) +#define HVDCP_VOLTAGE_MIN 4000 +#define HVDCP_PULSE_COUNT_MAX ((HVDCP_VOLTAGE_BASIC - 5000) / 200 + 2) +static void smb_mmi_config_qc_charger(struct smb_mmi_charger *chg) +{ + int rc = -EINVAL; + int pulse_cnt; + int vbus_mv; + bool stepwise; + union power_supply_propval val = {0, }; + + rc = power_supply_get_property(chg->qcom_psy, + POWER_SUPPLY_PROP_DP_DM, &val); + if (rc < 0) + return; + pulse_cnt = val.intval; + + /* + * Two hvdcp voltage regulation algorithm are supported: + * stepwise and supplement. stepwise will be used if QC3.0 + * charger power is more than 15W. + * It only runs once for stepwise algorithm after QC3.0 + * charger is detected, while it will run all the time in + * supplement algorithm if QC3.0 charger is present. + */ + stepwise = chg->constraint.hvdcp_pmax > HVDCP_POWER_MIN; + if (stepwise && pulse_cnt) + return; + + if (!pulse_cnt) { + rc = smblib_masked_write_mmi(chg, + HVDCP_PULSE_COUNT_MAX_REG, + HVDCP_PULSE_COUNT_MAX_QC2_MASK | + HVDCP_PULSE_COUNT_MAX_QC3_MASK, + HVDCP_PULSE_COUNT_MAX); + if (rc < 0) { + mmi_err(chg, "Couldn't set dpdm pulse max\n"); + return; + } + + rc = smblib_set_charge_param(chg, + &chg->param.aicl_cont_threshold, + HVDCP_VOLTAGE_NOM - 1000); + if (rc < 0) { + mmi_err(chg, "Couldn't set aicl_cont_threshold\n"); + return; + } + mmi_info(chg, "Configure QC3.0 output range:(%d-%d)mV\n", + HVDCP_VOLTAGE_NOM, HVDCP_VOLTAGE_MAX); + } + + if (stepwise) { + vote(chg->chg_dis_votable, MMI_HB_VOTER, true, 0); + mmi_info(chg, "Disable charging before stepwise configure\n"); + } + + do { + rc = get_prop_usb_real_type(chg, &val); + if (rc < 0) + break; + else if (val.intval != POWER_SUPPLY_TYPE_USB_HVDCP_3) { + mmi_warn(chg, "Exit for QC3.0 charger removal\n"); + break; + } + + rc = get_prop_usb_voltage_now(chg, &val); + if (rc < 0) + break; + vbus_mv = val.intval / 1000; + + mmi_info(chg, "pulse_cnt=%d, vbus_mv=%d\n", pulse_cnt, vbus_mv); + if (vbus_mv < HVDCP_VOLTAGE_NOM && + pulse_cnt < HVDCP_PULSE_COUNT_MAX) + val.intval = POWER_SUPPLY_DP_DM_DP_PULSE; + else if (vbus_mv > HVDCP_VOLTAGE_MAX && + pulse_cnt > 0 && !stepwise) + val.intval = POWER_SUPPLY_DP_DM_DM_PULSE; + else { + mmi_info(chg, "QC3.0 output configure completed\n"); + break; + } + + rc = power_supply_set_property(chg->qcom_psy, + POWER_SUPPLY_PROP_DP_DM, &val); + if (rc < 0) + break; + else if (!stepwise) { + power_supply_changed(chg->usb_psy); + break; + } + msleep(100); + + rc = power_supply_get_property(chg->qcom_psy, + POWER_SUPPLY_PROP_DP_DM, &val); + if (rc < 0) + break; + pulse_cnt = val.intval; + } while (stepwise); + + if (stepwise) { + vote(chg->chg_dis_votable, MMI_HB_VOTER, false, 0); + mmi_info(chg, "Recover charging after stepwise configure\n"); + } +} + +#define PD_POWER_MIN 15000 +#ifdef CONFIG_PD_CHARGER_MMI +#define MICRO_1V 1000000 +#define MICRO_5V 5000000 +#define MICRO_9V 9000000 +#define MICRO_12V 12000000 +#define PPS_VOLT_MIN 5000000 +#define PPS_CURR_MIN 2000000 +#define PPS_CURR_MAX 3000000 +#define PD_SRC_PDO_TYPE_FIXED 0 +#define PD_SRC_PDO_TYPE_BATTERY 1 +#define PD_SRC_PDO_TYPE_VARIABLE 2 +#define PD_SRC_PDO_TYPE_AUGMENTED 3 +#define PD_HEARTBEAT_DELAY_MS 10000 +static int smblib_set_usb_pd_fsw(struct smb_mmi_charger *chg, int voltage) +{ + int rc; + + if (voltage == MICRO_5V) + rc = smblib_set_charge_param(chg, + &chg->param.freq_switcher, + chg->chg_freq.freq_5V); + else if (voltage > MICRO_5V && voltage < MICRO_9V) + rc = smblib_set_charge_param(chg, + &chg->param.freq_switcher, + chg->chg_freq.freq_6V_8V); + else if (voltage >= MICRO_9V && voltage < MICRO_12V) + rc = smblib_set_charge_param(chg, + &chg->param.freq_switcher, + chg->chg_freq.freq_9V); + else if (voltage == MICRO_12V) + rc = smblib_set_charge_param(chg, + &chg->param.freq_switcher, + chg->chg_freq.freq_12V); + else { + mmi_err(chg, "Couldn't set Fsw: invalid voltage %d\n", + voltage); + rc = -EINVAL; + } + + return rc; +} + +static void smb_mmi_config_pd_charger(struct smb_mmi_charger *chg) +{ + int rc; + int i; + int vbus_mv; + int pdo_pmax; + int pdo_vmax; + int req_volt; + int req_curr; + int req_type = -1; + int req_pdo = -1; + struct usbpd_pdo_info *info; + struct usbpd_pdo_info pdo_list[PD_MAX_PDO_NUM]; + union power_supply_propval val = {0, }; + + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_PRESENT, &val); + if (rc < 0 || !val.intval) { + mmi_warn(chg, "USB is not present, rc=%d\n", rc); + return; + } + + rc = power_supply_get_property(chg->usb_psy, + POWER_SUPPLY_PROP_PD_ACTIVE, &val); + if (rc < 0 || !val.intval) { + mmi_err(chg, "USB PD is not active, rc=%d\n", rc); + return; + } + + rc = get_prop_usb_voltage_now(chg, &val); + if (rc < 0) { + mmi_err(chg, "Couldn't get usb voltage, rc=%d\n", rc); + return; + } + vbus_mv = val.intval / 1000; + + memset(pdo_list, 0, sizeof(struct usbpd_pdo_info) * PD_MAX_PDO_NUM); + rc = usbpd_get_pdo_info(chg->pd_handle, pdo_list, PD_MAX_PDO_NUM); + if (rc < 0) { + mmi_err(chg, "Couldn't get pdo info, rc=%d\n", rc); + return; + } + + for (i = 0; i < PD_MAX_PDO_NUM; i++) { + info = &pdo_list[i]; + if (info->type == PD_SRC_PDO_TYPE_AUGMENTED) { + pdo_pmax = (info->uv_max / 1000) * (info->ua / 1000); + pdo_pmax /= 1000; + if (info->uv_max >= PPS_VOLT_MIN && + info->ua >= PPS_CURR_MIN && + pdo_pmax >= chg->constraint.pd_pmax) { + req_pdo = info->pdo_pos; + req_curr = min(info->ua, PPS_CURR_MAX); + req_volt = (chg->constraint.pd_pmax * 1000 \ + / (req_curr / 1000)) * 1000; + req_volt -= (req_volt % 20000); + req_volt = max(req_volt, PPS_VOLT_MIN); + req_volt = min(req_volt, info->uv_max); + req_type = PD_SRC_PDO_TYPE_AUGMENTED; + break; + } + } else if (info->type == PD_SRC_PDO_TYPE_FIXED) { + if (chg->constraint.pd_pmax <= PD_POWER_MIN) + pdo_vmax = MICRO_5V; + else + pdo_vmax = MICRO_9V; + + if (info->uv_max >= MICRO_5V && + info->uv_max <= pdo_vmax) { + req_pdo = info->pdo_pos; + req_volt = info->uv_max; + req_curr = (chg->constraint.pd_pmax * 1000 \ + / (req_volt / 1000)) * 1000; + req_type = PD_SRC_PDO_TYPE_FIXED; + } + } + } + + if (req_pdo < 0) { + mmi_info(chg, "No matched pdo is found\n"); + return; + } + + if (req_type == PD_SRC_PDO_TYPE_FIXED && + vbus_mv < (req_volt - MICRO_1V) / 1000) + chg->pd_current_pdo = -1; + if (chg->pd_current_pdo == req_pdo && + req_type == PD_SRC_PDO_TYPE_FIXED) + return; + else if (chg->pd_current_pdo != req_pdo) { + smblib_set_usb_pd_fsw(chg, req_volt); + mmi_info(chg, "Request %s pdo%d(%duV, %duA), vbus:%dmV\n", + req_type != PD_SRC_PDO_TYPE_FIXED? "pps" : "fixed", + req_pdo, req_volt, req_curr, vbus_mv); + } + + rc = usbpd_select_pdo(chg->pd_handle, req_pdo, req_volt, req_curr); + if (rc < 0) { + mmi_err(chg, "Couldn't select pdo%d, rc=%d\n", req_pdo, rc); + } else { + if (req_type == PD_SRC_PDO_TYPE_FIXED) { + val.intval = req_curr; + rc = power_supply_set_property(chg->usb_psy, + POWER_SUPPLY_PROP_PD_CURRENT_MAX, + &val); + if (rc) + mmi_err(chg, "Couldn't set PD current max\n"); + } else { + schedule_delayed_work(&chg->charger_work, + msecs_to_jiffies(PD_HEARTBEAT_DELAY_MS)); + } + chg->pd_current_pdo = req_pdo; + } +} +#else +static void smb_mmi_config_pd_charger(struct smb_mmi_charger *chg) +{ + mmi_dbg(chg, "Configure PD charger is not supported\n"); +} +#endif + +#define WLS_POWER_MIN 5000 +static void smb_mmi_config_wls_charger(struct smb_mmi_charger *chg) +{ + mmi_dbg(chg, "Configure wireless charger\n"); +} + +static void smb_mmi_config_charger_input(struct smb_mmi_charger *chip) +{ + int rc; + int i; + int usb_icl; + bool charger_suspend = false; + union power_supply_propval val; + struct mmi_charger_cfg *cfg; + + if (!chip->chg_clients || !chip->chg_client_num) { + mmi_err(chip, "Invalid charger client\n"); + return; + } + + for (i = 0; i < chip->chg_client_num; i++) { + cfg = &chip->chg_clients[i].chg_cfg; + if (cfg->charger_suspend) { + charger_suspend = true; + break; + } + } + + if (charger_suspend) { + if (chip->constraint.factory_mode) { + smblib_set_usb_suspend(chip, true); + smblib_set_dc_suspend(chip, true); + } else { + vote(chip->usb_icl_votable, MMI_HB_VOTER, true, 0); + vote(chip->dc_suspend_votable, MMI_HB_VOTER, true, 1); + } + return; + } else { + if (chip->constraint.factory_mode) { + smblib_set_usb_suspend(chip, false); + smblib_set_dc_suspend(chip, false); + } else { + rc = get_prop_dc_present(chip, &val); + if (!rc && val.intval) + vote(chip->dc_suspend_votable, MMI_HB_VOTER, + false, 0); + else + vote(chip->dc_suspend_votable, MMI_HB_VOTER, + true, 1); + vote(chip->usb_icl_votable, MMI_HB_VOTER, false, 0); + usb_icl = get_effective_result(chip->usb_icl_votable); + smb_mmi_usb_icl_override(chip, usb_icl); + } + } + + if (chip->pd_handle && chip->usb_psy && + chip->constraint.pd_pmax >= PD_POWER_MIN && + chip->chg_info.chrg_type == POWER_SUPPLY_TYPE_USB_PD) + smb_mmi_config_pd_charger(chip); + else + chip->pd_current_pdo = -1; + + if (chip->qcom_psy && chip->usb_psy && + chip->constraint.hvdcp_pmax >= HVDCP_POWER_MIN && + chip->chg_info.chrg_mv > HVDCP_VOLTAGE_MIN && + chip->chg_info.chrg_type == POWER_SUPPLY_TYPE_USB_HVDCP_3) + smb_mmi_config_qc_charger(chip); + + if (chip->wls_psy && + chip->constraint.wls_pmax > WLS_POWER_MIN && + chip->chg_info.chrg_type == POWER_SUPPLY_TYPE_WIPOWER) + smb_mmi_config_wls_charger(chip); +} + +static void smb_mmi_config_charger_output(struct smb_mmi_charger *chip) +{ + int i; + int target_fcc = INT_MAX; + int target_fv = INT_MAX; + bool chg_dis; + bool charging_reset = false; + bool charging_disable = false; + struct mmi_charger_cfg *cfg; + + if (!chip->chg_clients || !chip->chg_client_num) { + mmi_err(chip, "Invalid charger client\n"); + return; + } + + chg_dis = get_effective_result(chip->chg_dis_votable); + for (i = 0; i < chip->chg_client_num; i++) { + cfg = &chip->chg_clients[i].chg_cfg; + target_fcc = min(target_fcc, cfg->target_fcc); + target_fv = min(target_fv, cfg->target_fv); + if (cfg->charging_disable || target_fcc < 0) + charging_disable = true; + if (cfg->charging_reset) + charging_reset = true; + if (cfg->taper_kickoff) + chip->chg_clients[i].chrg_taper_cnt = 0; + } + + if (charging_reset && !charging_disable && !chg_dis) { + vote(chip->chg_dis_votable, MMI_HB_VOTER, true, 0); + mmi_warn(chip, "Charge Halt..Toggle\n"); + msleep(50); + } + + vote(chip->fv_votable, MMI_HB_VOTER, true, target_fv * 1000); + if (charging_disable) { + vote(chip->fcc_votable, MMI_HB_VOTER, true, 0); + vote(chip->chg_dis_votable, MMI_HB_VOTER, true, 0); + } else { + vote(chip->fcc_votable, MMI_HB_VOTER, true, target_fcc * 1000); + vote(chip->chg_dis_votable, MMI_HB_VOTER, false, 0); + } + + charging_disable = get_effective_result(chip->chg_dis_votable); + /* Rerun AICL to recover USB ICL from recharge */ + if (chg_dis != charging_disable || charging_reset) { + power_supply_set_property(chip->qcom_psy, + POWER_SUPPLY_PROP_RERUN_AICL, + NULL); + } +} + +#define REV_BST_THRESH 4700 +#define REV_BST_DROP 150 +#define REV_BST_BULK_DROP 100 +#define REV_BST_MA -10 +static void smb_mmi_check_reversed_boost(struct smb_mmi_charger *chip) +{ + int rc; + int batt_ma; + int batt_mv; + int vbus_mv; + int usb_suspend; + union power_supply_propval val; + static int prev_vbus_mv = -1; + + usb_suspend = smblib_is_usb_suspended(chip); + if (usb_suspend) + return; + + rc = get_prop_usb_voltage_now(chip, &val); + if (rc < 0) { + mmi_err(chip, "Failed to get usb voltage, rc=%d\n", rc); + return; + } + vbus_mv = val.intval / 1000; + + rc = power_supply_get_property(chip->bms_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + &val); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Voltage rc = %d\n", rc); + return; + } else + batt_mv = val.intval / 1000; + + rc = power_supply_get_property(chip->bms_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, + &val); + if (rc < 0) { + mmi_err(chip, "Error getting Batt Current rc = %d\n", rc); + return; + } else + batt_ma = val.intval / 1000; + + prev_vbus_mv = prev_vbus_mv < 0 ? vbus_mv : prev_vbus_mv; + if (abs(vbus_mv - batt_mv) < REV_BST_BULK_DROP) { + if (((vbus_mv < REV_BST_THRESH) && + ((prev_vbus_mv - REV_BST_DROP) > vbus_mv)) || + (batt_ma > REV_BST_MA)) { + mmi_err(chip, "Clear Reversed Boost, Suspend USBIN.\n"); + if (chip->constraint.factory_mode) + smblib_set_usb_suspend(chip, true); + else + vote(chip->usb_icl_votable, BOOST_BACK_VOTER, + true, 0); + msleep(50); + if (chip->constraint.factory_mode) + smblib_set_usb_suspend(chip, false); + else + vote(chip->usb_icl_votable, BOOST_BACK_VOTER, + false, 0); + } else { + mmi_err(chip, "Suspicious Reversed Boost.\n"); + } + mmi_warn(chip, "vbus:%d, prev_vbus:%d, batt_mv:%d, batt_ma:%d\n", + vbus_mv, + prev_vbus_mv, + batt_mv, + batt_ma); + } + prev_vbus_mv = vbus_mv; +} + +static void smb_mmi_check_otg_power(struct smb_mmi_charger *chip) +{ + int rc; + + if (chip->vbus_enabled && chip->vbus) { + rc = regulator_disable(chip->vbus); + if (rc) + mmi_err(chip, "Unable to disable vbus (%d)\n", rc); + else { + chip->vbus_enabled = false; + mmi_info(chip, "VBUS Disable due to Charger\n"); + } + } +} + +static void smb_mmi_charger_work(struct work_struct *work) +{ + struct smb_mmi_charger *chip = container_of(work, + struct smb_mmi_charger, + charger_work.work); + + pm_stay_awake(chip->dev); + mmi_dbg(chip, "SMB MMI Charger Work!\n"); + + if (chip->chg_info.chrg_present) { + smb_mmi_check_otg_power(chip); + smb_mmi_check_reversed_boost(chip); + } + smb_mmi_config_charger_input(chip); + smb_mmi_config_charger_output(chip); + + mmi_info(chip, "FV=%d, FCC=%d, CHGDIS=%d, USBICL=%d, USBDIS=%d\n", + get_effective_result(chip->fv_votable), + get_effective_result(chip->fcc_votable), + get_effective_result(chip->chg_dis_votable), + get_effective_result(chip->usb_icl_votable), + smblib_is_usb_suspended(chip)); + pm_relax(chip->dev); +} + +static enum power_supply_property batt_props[] = { + POWER_SUPPLY_PROP_INPUT_SUSPEND, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGER_TEMP, + POWER_SUPPLY_PROP_CHARGER_TEMP_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_QNOVO, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_QNOVO, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, + POWER_SUPPLY_PROP_SW_JEITA_ENABLED, + POWER_SUPPLY_PROP_CHARGE_DONE, + POWER_SUPPLY_PROP_PARALLEL_DISABLE, + POWER_SUPPLY_PROP_SET_SHIP_MODE, + POWER_SUPPLY_PROP_DIE_HEALTH, + POWER_SUPPLY_PROP_RERUN_AICL, + POWER_SUPPLY_PROP_DP_DM, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_RECHARGE_SOC, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE, + POWER_SUPPLY_PROP_USB_OTG, +}; + +static int batt_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int rc = 0; + struct smb_mmi_charger *chip = power_supply_get_drvdata(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_TEMP: + case POWER_SUPPLY_PROP_STATUS: + case POWER_SUPPLY_PROP_HEALTH: + case POWER_SUPPLY_PROP_CAPACITY: + case POWER_SUPPLY_PROP_CYCLE_COUNT: + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + rc = power_supply_get_property(chip->mmi_psy, psp, val); + break; + case POWER_SUPPLY_PROP_CHARGE_DONE: + rc = power_supply_get_property(chip->mmi_psy, + POWER_SUPPLY_PROP_STATUS, val); + if (!rc && val->intval == POWER_SUPPLY_STATUS_FULL) + val->intval = 1; + else + val->intval = 0; + break; + case POWER_SUPPLY_PROP_CURRENT_MAX: + val->intval = get_effective_result(chip->fcc_votable); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + if (chip->smb_version == PMI632_SUBTYPE) { + val->intval = get_effective_result(chip->fcc_votable); + break; + } + rc = power_supply_get_property(chip->qcom_psy, psp, val); + if (rc < 0) { + /* soft fail so uevents are not blocked */ + rc = 0; + val->intval = -EINVAL; + } + break; + case POWER_SUPPLY_PROP_USB_OTG: + if (chip->vbus && + regulator_is_enabled(chip->vbus) && + chip->vbus_enabled) + val->intval = 1; + else + val->intval = 0; + break; + default: + rc = power_supply_get_property(chip->qcom_psy, psp, val); + if (rc < 0) { + mmi_dbg(chip, "Get Unknown prop %d rc = %d\n", psp, rc); + /* soft fail so uevents are not blocked */ + rc = 0; + val->intval = -EINVAL; + } + break; + } + + return rc; +} + +static int batt_set_prop(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + int rc = 0; + struct smb_mmi_charger *chip = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_CYCLE_COUNT: + rc = power_supply_set_property(chip->mmi_psy, prop, val); + break; + case POWER_SUPPLY_PROP_DP_DM: + /* Skip the hvdcp request from hvdcp daemon */ + if (chip->constraint.hvdcp_pmax) + break; + case POWER_SUPPLY_PROP_USB_OTG: + if (!chip->vbus) { + chip->vbus = devm_regulator_get(chip->dev, "vbus"); + if (IS_ERR(chip->vbus)) { + mmi_err(chip, "Unable to get vbus\n"); + return -EINVAL; + } + } + + if (val->intval) { + rc = regulator_enable(chip->vbus); + mmi_info(chip, "VBUS Enable\n"); + } else if (chip->vbus_enabled) { + rc = regulator_disable(chip->vbus); + mmi_info(chip, "VBUS Disable\n"); + } + + if (rc) + mmi_err(chip, "Unable to %s vbus (%d)\n", + val->intval ? "enable" : "disable", rc); + else if (val->intval) + chip->vbus_enabled = true; + else + chip->vbus_enabled = false; + + break; + default: + rc = power_supply_set_property(chip->qcom_psy, prop, val); + if (rc < 0) { + mmi_dbg(chip, "Get Unknown prop %d rc = %d\n", prop, rc); + /* soft fail so uevents are not blocked */ + rc = 0; + } + break; + } + return rc; +} + +static int batt_prop_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + case POWER_SUPPLY_PROP_INPUT_SUSPEND: + case POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL: + case POWER_SUPPLY_PROP_CAPACITY: + case POWER_SUPPLY_PROP_PARALLEL_DISABLE: + case POWER_SUPPLY_PROP_DP_DM: + case POWER_SUPPLY_PROP_RERUN_AICL: + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED: + case POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED: + case POWER_SUPPLY_PROP_SW_JEITA_ENABLED: + case POWER_SUPPLY_PROP_DIE_HEALTH: + case POWER_SUPPLY_PROP_CYCLE_COUNT: + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + return 1; + default: + break; + } + + return 0; +} + +static void batt_external_power_changed(struct power_supply *psy) +{ + struct smb_mmi_charger *chip = power_supply_get_drvdata(psy); + + cancel_delayed_work(&chip->charger_work); + schedule_delayed_work(&chip->charger_work, msecs_to_jiffies(0)); + + power_supply_changed(chip->batt_psy); +} + +static const struct power_supply_desc batt_psy_desc = { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .get_property = batt_get_prop, + .set_property = batt_set_prop, + .external_power_changed = batt_external_power_changed, + .property_is_writeable = batt_prop_is_writeable, + .properties = batt_props, + .num_properties = ARRAY_SIZE(batt_props), +}; + +#if defined(CONFIG_DEBUG_FS) +static int register_dump_read(struct seq_file *m, void *data) +{ + int rc; + u8 stat; + int i; + struct smb_mmi_charger *chip = m->private; + + for (i = CHGR_BASE; i < MISC_BASE + 0x100; i++) { + rc = smblib_read_mmi(chip, i, &stat); + if (rc < 0) + continue; + seq_printf(m, "REG:0x%x: 0x%x\n", i, stat); + } + + return 0; +} + +static int register_dump_debugfs_open(struct inode *inode, struct file *file) +{ + struct smb_mmi_charger *chip = inode->i_private; + + return single_open(file, register_dump_read, chip); +} + +static const struct file_operations register_dump_debugfs_ops = { + .owner = THIS_MODULE, + .open = register_dump_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void smb_mmi_create_debugfs(struct smb_mmi_charger *chip) +{ + struct dentry *dfs_root, *file; + + dfs_root = debugfs_create_dir("charger_mmi", NULL); + if (IS_ERR_OR_NULL(dfs_root)) { + mmi_err(chip, "Couldn't create charger debugfs rc=%ld\n", + (long)dfs_root); + return; + } + + file = debugfs_create_file("register_dump", + S_IRUSR | S_IRGRP | S_IROTH, + dfs_root, chip, ®ister_dump_debugfs_ops); + if (IS_ERR_OR_NULL(file)) + mmi_err(chip, "Couldn't create register_dump file rc=%ld\n", + (long)file); +} +#else +static void smb_mmi_create_debugfs(struct smb_mmi_charger *chip) +{} +#endif + +static int smb_mmi_charger_init(struct smb_mmi_charger *chip) +{ + int rc; + int i; + int count; + const char *str = NULL; + union power_supply_propval pval; + struct power_supply *batt_psy; + struct mmi_charger_driver *driver; + struct smb_mmi_chg_client *client; + struct device_node *np = chip->dev->of_node; + const char *batt_sn = NULL; + const char *df_sn = NULL; + + chip->constraint.factory_mode = mmi_is_factory_mode(); + + count = of_property_count_strings(np, "battery-names"); + if (count <= 0) { + mmi_err(chip, "Invalid battery-names in dt, count=%d\n", count); + return -EINVAL; + } + + client = devm_kzalloc(chip->dev, + sizeof(struct smb_mmi_chg_client) * count, + GFP_KERNEL); + driver = devm_kzalloc(chip->dev, + sizeof(struct mmi_charger_driver) * count, + GFP_KERNEL); + if (!client || !driver) { + rc = -ENOMEM; + goto free_mem; + } + + batt_sn = mmi_get_battery_serialnumber(); + for (i = 0; i < count; i++) { + rc = of_property_read_string_index(np, "battery-names", i, &str); + if (rc) + break; + batt_psy = power_supply_get_by_name(str); + if (!batt_psy) { + rc = -ENODEV; + break; + } + + client[i].chip = chip; + client[i].driver = &driver[i]; + client[i].client_batt_psy = batt_psy; + + driver[i].name = str; + driver[i].dev = batt_psy->dev.parent; + driver[i].data = &client[i]; + driver[i].get_batt_info = smb_mmi_get_batt_info; + driver[i].get_chg_info = smb_mmi_get_chg_info; + driver[i].config_charge = smb_mmi_config_charge; + driver[i].is_charge_tapered = smb_mmi_has_current_tapered; + driver[i].is_charge_halt = smb_mmi_charge_halted; + driver[i].set_constraint = smb_mmi_set_constraint; + + if (!batt_sn) { + df_sn = "unknown-sn"; + of_property_read_string(driver[i].dev->of_node, + "mmi,df-serialnum", &df_sn); + strcpy(client[i].batt_info.batt_sn, df_sn); + } else { + strcpy(client[i].batt_info.batt_sn, batt_sn); + } + mmi_info(chip, "%s Serial Number is %s\n", + str, client[i].batt_info.batt_sn); + + rc = mmi_register_charger_driver(&driver[i]); + if (rc) + break; + + power_supply_changed(client[i].client_batt_psy); + mmi_info(chip, "charger driver on %s init successfully\n", + driver[i].name); + } + + chip->chg_client_num = i; + if (!chip->chg_client_num) + goto free_mem; + + chip->chg_clients = client; + + /* Configure the charger chip */ + if (chip->smb_version == PM8150B_SUBTYPE) { + rc = smblib_masked_write_mmi(chip, LEGACY_CABLE_CFG_REG, + 0xFF, 0); + if (rc) + mmi_err(chip, "Could not set Legacy Cable CFG\n"); + } + + if (chip->smb_version == PM8150B_SUBTYPE || + chip->smb_version == PM7250B_SUBTYPE || + chip->smb_version == PMI632_SUBTYPE || + chip->smb_version == PM660_SUBTYPE) { + mmi_info(chip, "DISABLE all QCOM JEITA\n"); + /* Ensure SW JEITA is DISABLED */ + pval.intval = 0; + power_supply_set_property(chip->qcom_psy, + POWER_SUPPLY_PROP_SW_JEITA_ENABLED, + &pval); + /* Ensure HW JEITA is DISABLED */ + rc = smblib_masked_write_mmi(chip, PM8150B_JEITA_EN_CFG_REG, + 0xFF, 0x00); + if (rc) + mmi_err(chip, "Could not disable JEITA CFG\n"); + } + + if ((chip->smb_version == PM8150B_SUBTYPE) || + (chip->smb_version == PM7250B_SUBTYPE) || + (chip->smb_version == PMI632_SUBTYPE) || + (chip->smb_version == PM660_SUBTYPE)) { + rc = smblib_masked_write_mmi(chip, USBIN_ADAPTER_ALLOW_CFG_REG, + USBIN_ADAPTER_ALLOW_MASK, + USBIN_ADAPTER_ALLOW_5V_TO_9V); + if (rc) + mmi_err(chip, "Could not set USB Adapter CFG\n"); + } + + if (chip->dc_cl_ma >= 0) { + rc = smblib_set_charge_param(chip, &chip->param.dc_icl, + chip->dc_cl_ma * 1000); + if (rc) + mmi_err(chip, "Failed to set DC ICL %d\n", + chip->dc_cl_ma); + } + + /* Workaround for some cables that collapse on boot */ + if (!chip->constraint.factory_mode) { + mmi_err(chip, "Suspending USB for 50 ms to clear\n"); + smblib_set_usb_suspend(chip, true); + msleep(50); + smblib_set_usb_suspend(chip, false); + } + + if (chip->constraint.factory_mode) { + mmi_info(chip, "Entering Factory Mode SMB!\n"); + if (chip->smb_version == PMI632_SUBTYPE) { + rc = smblib_masked_write_mmi(chip, USBIN_INT_EN_CLR, + 0xFF, USBIN_OV_EN_CLR); + if (rc) + mmi_err(chip, + "Could Not disable usbin ov irq\n"); + } + + rc = smblib_masked_write_mmi(chip, USBIN_ICL_OPTIONS_REG, + USBIN_MODE_CHG_BIT, + USBIN_MODE_CHG_BIT); + if (rc < 0) + mmi_err(chip, + "Couldn't set USBIN_ICL_OPTIONS rc=%d\n", rc); + + rc = smblib_masked_write_mmi(chip, USBIN_LOAD_CFG_REG, + ICL_OVERRIDE_AFTER_APSD_BIT, + ICL_OVERRIDE_AFTER_APSD_BIT); + if (rc < 0) + mmi_err(chip, + "Couldn't set USBIN_LOAD_CFG rc=%d\n", rc); + + rc = smblib_masked_write_mmi(chip, USBIN_AICL_OPTIONS_CFG_REG, + 0xFF, 0x00); + if (rc < 0) + mmi_err(chip, + "Couldn't set USBIN_AICL_OPTIONS rc=%d\n", rc); + + if(chip->chg_dis_votable) { + pmic_vote_force_val_set(chip->chg_dis_votable, 1); + pmic_vote_force_active_set(chip->chg_dis_votable, 1); + } + if(chip->fcc_votable) { + pmic_vote_force_val_set(chip->fcc_votable, 3000000); + pmic_vote_force_active_set(chip->fcc_votable, 1); + } + if(chip->fv_votable) { + pmic_vote_force_val_set(chip->fv_votable, 4400000); + pmic_vote_force_active_set(chip->fv_votable, 1); + } + + /* Some Cables need a more forced approach */ + rc = smblib_set_charge_param(chip, &chip->param.usb_icl, + 3000000); + if (rc < 0) + mmi_err(chip, "Failed to set 3000mA icl rc=%d\n", rc); + } + + return 0; +free_mem: + if (client) + devm_kfree(chip->dev, client); + if (driver) + devm_kfree(chip->dev, driver); + chip->chg_client_num = 0; + chip->chg_clients = NULL; + + return rc; +} + +static void smb_mmi_charger_deinit(struct smb_mmi_charger *chip) +{ + int i; + + for (i = 0; i < chip->chg_client_num; i++) { + mmi_unregister_charger_driver(chip->chg_clients[i].driver); + power_supply_put(chip->chg_clients[i].client_batt_psy); + } + + if (chip->chg_client_num) { + devm_kfree(chip->dev, chip->chg_clients->driver); + devm_kfree(chip->dev, chip->chg_clients); + } +} + +static int smb_mmi_chip_setup(struct smb_mmi_charger *chip) +{ + int rc; + struct pmic_revid_data *pmic_rev_id; + struct device_node *revid_dev_node; + + revid_dev_node = of_parse_phandle(chip->dev->of_node, + "mmi,pmic-revid", 0); + if (!revid_dev_node) { + mmi_err(chip, "Missing qcom,pmic-revid property\n"); + return -EINVAL; + } + + pmic_rev_id = get_revid_data(revid_dev_node); + if (IS_ERR_OR_NULL(pmic_rev_id)) { + /* + * the revid peripheral must be registered, any failure + * here only indicates that the rev-id module has not + * probed yet. + */ + return -EPROBE_DEFER; + } + + switch (pmic_rev_id->pmic_subtype) { + case PM8150B_SUBTYPE: + chip->smb_version = PM8150B_SUBTYPE; + chip->name = "pm8150b_charger"; + chip->param = smb5_pm8150b_params; + break; + case PM7250B_SUBTYPE: + chip->smb_version = PM7250B_SUBTYPE; + chip->name = "pm7250b_charger"; + chip->param = smb5_pm8150b_params; + break; + case PMI632_SUBTYPE: + chip->smb_version = PMI632_SUBTYPE; + chip->name = "pmi632_charger"; + chip->param = smb5_pmi632_params; + break; + case PM660_SUBTYPE: + chip->smb_version = PM660_SUBTYPE; + chip->name = "pm660_charger"; + chip->param = smb2_pm660_params; + break; + default: + mmi_err(chip, "PMIC subtype %d not supported\n", + pmic_rev_id->pmic_subtype); + return -EINVAL; + } + + chip->chg_freq.freq_5V = 600; + chip->chg_freq.freq_6V_8V = 800; + chip->chg_freq.freq_9V = 1050; + chip->chg_freq.freq_12V = 1200; + + chip->pd_handle = devm_usbpd_get_by_phandle(chip->dev, + "mmi,usbpd-phandle"); + if (IS_ERR_OR_NULL(chip->pd_handle)) { + mmi_err(chip, "Error getting the pd phandle %ld\n", + PTR_ERR(chip->pd_handle)); + chip->pd_handle = NULL; + } else { + chip->pd_current_pdo = -1; + } + + rc = of_property_read_u32(chip->dev->of_node, "mmi,dc-icl-ma", + &chip->dc_cl_ma); + if (rc) + chip->dc_cl_ma = -EINVAL; + + mmi_info(chip, "PMIC %d is %s\n", chip->smb_version, chip->name); + + return 0; +} + +static int smb_mmi_check_battery_supplies(struct smb_mmi_charger *chip) +{ + int i; + int rc = 0; + int count; + + if (chip->batt_psy->num_supplies && chip->batt_psy->supplied_from) + goto print_supplies; + + count = of_property_count_strings(chip->dev->of_node, "supplied-from"); + if (count <= 0) { + mmi_err(chip, "Invalid supplied-from in DT, rc=%d\n", count); + return -EINVAL; + } + + chip->batt_psy->supplied_from = devm_kmalloc_array(&chip->batt_psy->dev, + count, + sizeof(char *), + GFP_KERNEL); + if (!chip->batt_psy->supplied_from) + return -ENOMEM; + + rc = of_property_read_string_array(chip->dev->of_node, "supplied-from", + (const char **)chip->batt_psy->supplied_from, + count); + if (rc < 0) { + mmi_err(chip, "Failed to get supplied-from, rc=%d\n", rc); + return rc; + } + + chip->batt_psy->num_supplies = count; + +print_supplies: + for (i = 0; i < chip->batt_psy->num_supplies; i++) { + mmi_info(chip, "battery supplied_from[%d]=%s\n", i, + chip->batt_psy->supplied_from[i]); + } + + return 0; +} + +static int smb_mmi_probe(struct platform_device *pdev) +{ + int rc = 0; + struct smb_mmi_charger *chip; + struct power_supply_config psy_cfg = {}; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &pdev->dev; + psy_cfg.drv_data = chip; + psy_cfg.of_node = chip->dev->of_node; + platform_set_drvdata(pdev, chip); + this_chip = chip; + device_init_wakeup(chip->dev, true); + + rc = smb_mmi_chip_setup(chip); + if (rc < 0) { + mmi_err(chip, "Failed to init smb chip\n"); + return rc; + } + + chip->debug_enabled = &debug_enabled; + chip->ipc_log = ipc_log_context_create(MMI_LOG_PAGES, MMI_LOG_DIR, 0); + if (!chip->ipc_log) + mmi_err(chip, "Failed to create SMBMMI IPC log\n"); + else + mmi_info(chip, "IPC logging is enabled for SMBMMI\n"); + + chip->regmap = dev_get_regmap(chip->dev->parent, NULL); + if (!chip->regmap) { + mmi_err(chip, "Parent regmap is missing\n"); + return -EINVAL; + } + + chip->mmi_psy = power_supply_get_by_name("mmi_battery"); + if (!chip->mmi_psy) { + mmi_err(chip, "Failed: mmi_battery has not registered yet\n"); + return -EPROBE_DEFER; + } + + chip->qcom_psy = power_supply_get_by_name("qcom_battery"); + if (chip->qcom_psy) { + chip->batt_psy = devm_power_supply_register(chip->dev, + &batt_psy_desc, + &psy_cfg); + if (IS_ERR(chip->batt_psy)) { + mmi_err(chip, + "Failed: batt power supply register\n"); + return PTR_ERR(chip->batt_psy); + } + + rc = smb_mmi_check_battery_supplies(chip); + if (rc) { + mmi_err(chip, "No valid battery supplies\n"); + return rc; + } + } else { + chip->qcom_psy = power_supply_get_by_name("battery"); + chip->batt_psy = NULL; + } + + chip->bms_psy = power_supply_get_by_name("bms"); + chip->usb_psy = power_supply_get_by_name("usb"); + chip->main_psy = power_supply_get_by_name("main"); + chip->pc_port_psy = power_supply_get_by_name("pc_port"); + chip->dc_psy = power_supply_get_by_name("dc"); + chip->wls_psy = power_supply_get_by_name("wireless"); + + chip->chg_dis_votable = find_votable("CHG_DISABLE"); + if (IS_ERR(chip->chg_dis_votable)) + chip->chg_dis_votable = NULL; + chip->fcc_votable = find_votable("FCC"); + if (IS_ERR(chip->fcc_votable)) + chip->fcc_votable = NULL; + chip->fv_votable = find_votable("FV"); + if (IS_ERR(chip->fv_votable)) + chip->fv_votable = NULL; + chip->usb_icl_votable = find_votable("USB_ICL"); + if (IS_ERR(chip->usb_icl_votable)) + chip->usb_icl_votable = NULL; + chip->dc_suspend_votable = find_votable("DC_SUSPEND"); + if (IS_ERR(chip->dc_suspend_votable)) + chip->dc_suspend_votable = NULL; + + INIT_DELAYED_WORK(&chip->charger_work, smb_mmi_charger_work); + smb_mmi_charger_init(chip); + + if (chip->constraint.factory_mode) { + rc = device_create_file(chip->dev, + &dev_attr_force_chg_usb_suspend); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_usb_suspend\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_fail_clear); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_fail_clear\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_auto_enable); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_auto_enable\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_ibatt); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_ibatt\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_iusb); + if (rc) { + mmi_err(chip, + "Couldn't create force_chg_iusb\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_idc); + if (rc) { + mmi_err(chip, "Couldn't create force_chg_idc\n"); + } + + rc = device_create_file(chip->dev, + &dev_attr_force_chg_itrick); + if (rc) { + mmi_err(chip, "Couldn't create force_chg_itrick\n"); + } + + } + + smb_mmi_create_debugfs(chip); + + mmi_info(chip, "QPNP SMB BASIC MMI Charger probed successfully!\n"); + + return rc; +} + +static int smb_mmi_remove(struct platform_device *pdev) +{ + struct smb_mmi_charger *chip = platform_get_drvdata(pdev); + + cancel_delayed_work(&chip->charger_work); + smb_mmi_charger_deinit(chip); + + if (chip->mmi_psy) + power_supply_put(chip->mmi_psy); + if (chip->qcom_psy) + power_supply_put(chip->qcom_psy); + if (chip->bms_psy) + power_supply_put(chip->bms_psy); + if (chip->usb_psy) + power_supply_put(chip->usb_psy); + if (chip->main_psy) + power_supply_put(chip->main_psy); + if (chip->pc_port_psy) + power_supply_put(chip->pc_port_psy); + if (chip->dc_psy) + power_supply_put(chip->dc_psy); + + return 0; +} + +static const struct of_device_id match_table[] = { + { .compatible = "mmi,qpnp-smb-basic-charger", }, + { }, +}; + +static struct platform_driver smb_mmi_driver = { + .driver = { + .name = "mmi,qpnp-smb-basic-charger", + .owner = THIS_MODULE, + .of_match_table = match_table, + }, + .probe = smb_mmi_probe, + .remove = smb_mmi_remove, +}; +module_platform_driver(smb_mmi_driver); + +MODULE_DESCRIPTION("QPNP SMB BASIC MMI Charger Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/qti_battery_charger_mmi/Android.mk b/drivers/power/qti_battery_charger_mmi/Android.mk new file mode 100755 index 000000000000..63ecd8572105 --- /dev/null +++ b/drivers/power/qti_battery_charger_mmi/Android.mk @@ -0,0 +1,11 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := qti_battery_charger_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/drivers/power/qti_battery_charger_mmi/Kbuild b/drivers/power/qti_battery_charger_mmi/Kbuild new file mode 100755 index 000000000000..b078ca741a82 --- /dev/null +++ b/drivers/power/qti_battery_charger_mmi/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += qti_battery_charger_mmi.o diff --git a/drivers/power/qti_battery_charger_mmi/qti_battery_charger_mmi.c b/drivers/power/qti_battery_charger_mmi/qti_battery_charger_mmi.c new file mode 100755 index 000000000000..5b1b2dbaac6e --- /dev/null +++ b/drivers/power/qti_battery_charger_mmi/qti_battery_charger_mmi.c @@ -0,0 +1,1542 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + */ + +#define pr_fmt(fmt) "qti_battery_mmi: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MSG_OWNER_BC 32778 +#define MSG_TYPE_REQ_RESP 1 +#define MSG_TYPE_NOTIFY 2 + +/* opcode for battery charger */ +#define BC_SET_NOTIFY_REQ 0x04 +#define BC_NOTIFY_IND 0x07 +#define BC_BATTERY_STATUS_GET 0x30 +#define BC_BATTERY_STATUS_SET 0x31 +#define BC_USB_STATUS_GET 0x32 +#define BC_USB_STATUS_SET 0x33 +#define BC_SHIP_MODE_REQ_SET 0x36 +#define BC_SHUTDOWN_NOTIFY 0x47 +#define BC_GENERIC_NOTIFY 0x80 + +/* Generic definitions */ +#define MAX_STR_LEN 128 +#define BC_WAIT_TIME_MS 1000 +#define DEFAULT_RESTRICT_FCC_UA 1000000 + +enum psy_type { + PSY_TYPE_BATTERY, + PSY_TYPE_USB, + PSY_TYPE_MAX, +}; + +enum ship_mode_type { + SHIP_MODE_PMIC, + SHIP_MODE_PACK_SIDE, +}; + +/* property ids */ +enum battery_property_id { + BATT_STATUS, + BATT_HEALTH, + BATT_PRESENT, + BATT_CHG_TYPE, + BATT_CAPACITY, + BATT_SOH, + BATT_VOLT_OCV, + BATT_VOLT_NOW, + BATT_VOLT_MAX, + BATT_CURR_NOW, + BATT_CHG_CTRL_LIM, + BATT_CHG_CTRL_LIM_MAX, + BATT_TEMP, + BATT_TECHNOLOGY, + BATT_CHG_COUNTER, + BATT_CYCLE_COUNT, + BATT_CHG_FULL_DESIGN, + BATT_CHG_FULL, + BATT_MODEL_NAME, + BATT_TTF_AVG, + BATT_TTE_AVG, + BATT_RESISTANCE, + BATT_POWER_NOW, + BATT_POWER_AVG, + BATT_PROP_MAX, +}; + +enum usb_property_id { + USB_ONLINE, + USB_VOLT_NOW, + USB_VOLT_MAX, + USB_CURR_NOW, + USB_CURR_MAX, + USB_INPUT_CURR_LIMIT, + USB_TYPE, + USB_ADAP_TYPE, + USB_MOISTURE_DET_EN, + USB_MOISTURE_DET_STS, + USB_TEMP, + USB_REAL_TYPE, + USB_TYPEC_COMPLIANT, + USB_PROP_MAX, +}; + +enum { + QTI_POWER_SUPPLY_USB_TYPE_HVDCP = 0x80, + QTI_POWER_SUPPLY_USB_TYPE_HVDCP_3, + QTI_POWER_SUPPLY_USB_TYPE_HVDCP_3P5, +}; + +struct battery_charger_set_notify_msg { + struct pmic_glink_hdr hdr; + u32 battery_id; + u32 power_state; + u32 low_capacity; + u32 high_capacity; +}; + +struct battery_charger_notify_msg { + struct pmic_glink_hdr hdr; + u32 notification; +}; + +struct battery_charger_req_msg { + struct pmic_glink_hdr hdr; + u32 battery_id; + u32 property_id; + u32 value; +}; + +struct battery_charger_resp_msg { + struct pmic_glink_hdr hdr; + u32 property_id; + u32 value; + u32 ret_code; +}; + +struct battery_model_resp_msg { + struct pmic_glink_hdr hdr; + u32 property_id; + char model[MAX_STR_LEN]; +}; + +struct battery_charger_ship_mode_req_msg { + struct pmic_glink_hdr hdr; + u32 ship_mode_type; +}; + +struct psy_state { + struct power_supply *psy; + char *model; + const int *map; + u32 *prop; + u32 prop_count; + u32 opcode_get; + u32 opcode_set; +}; + +struct battery_chg_dev { + struct device *dev; + struct class battery_class; + struct pmic_glink_client *client; + struct mutex rw_lock; + struct completion ack; + struct psy_state psy_list[PSY_TYPE_MAX]; + struct power_supply *combo_psy; + struct power_supply *mmi_psy; + struct dentry *debugfs_dir; + u32 *thermal_levels; + int curr_thermal_level; + int num_thermal_levels; + atomic_t state; + struct work_struct subsys_up_work; + struct work_struct usb_type_work; + int fake_soc; + bool block_tx; + bool ship_mode_en; + bool debug_battery_detected; + struct notifier_block reboot_notifier; + u32 thermal_fcc_ua; + u32 restrict_fcc_ua; + u32 last_fcc_ua; + u32 usb_icl_ua; + bool restrict_chg_en; + /* To track the driver initialization status */ + bool initialized; +}; + +static const int battery_prop_map[BATT_PROP_MAX] = { + [BATT_STATUS] = POWER_SUPPLY_PROP_STATUS, + [BATT_HEALTH] = POWER_SUPPLY_PROP_HEALTH, + [BATT_PRESENT] = POWER_SUPPLY_PROP_PRESENT, + [BATT_CHG_TYPE] = POWER_SUPPLY_PROP_CHARGE_TYPE, + [BATT_CAPACITY] = POWER_SUPPLY_PROP_CAPACITY, + [BATT_VOLT_OCV] = POWER_SUPPLY_PROP_VOLTAGE_OCV, + [BATT_VOLT_NOW] = POWER_SUPPLY_PROP_VOLTAGE_NOW, + [BATT_VOLT_MAX] = POWER_SUPPLY_PROP_VOLTAGE_MAX, + [BATT_CURR_NOW] = POWER_SUPPLY_PROP_CURRENT_NOW, + [BATT_CHG_CTRL_LIM] = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + [BATT_CHG_CTRL_LIM_MAX] = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, + [BATT_TEMP] = POWER_SUPPLY_PROP_TEMP, + [BATT_TECHNOLOGY] = POWER_SUPPLY_PROP_TECHNOLOGY, + [BATT_CHG_COUNTER] = POWER_SUPPLY_PROP_CHARGE_COUNTER, + [BATT_CYCLE_COUNT] = POWER_SUPPLY_PROP_CYCLE_COUNT, + [BATT_CHG_FULL_DESIGN] = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + [BATT_CHG_FULL] = POWER_SUPPLY_PROP_CHARGE_FULL, + [BATT_MODEL_NAME] = POWER_SUPPLY_PROP_MODEL_NAME, + [BATT_TTF_AVG] = POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, + [BATT_TTE_AVG] = POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, + [BATT_POWER_NOW] = POWER_SUPPLY_PROP_POWER_NOW, + [BATT_POWER_AVG] = POWER_SUPPLY_PROP_POWER_AVG, +}; + +static const int usb_prop_map[USB_PROP_MAX] = { + [USB_ONLINE] = POWER_SUPPLY_PROP_ONLINE, + [USB_VOLT_NOW] = POWER_SUPPLY_PROP_VOLTAGE_NOW, + [USB_VOLT_MAX] = POWER_SUPPLY_PROP_VOLTAGE_MAX, + [USB_CURR_NOW] = POWER_SUPPLY_PROP_CURRENT_NOW, + [USB_CURR_MAX] = POWER_SUPPLY_PROP_CURRENT_MAX, + [USB_INPUT_CURR_LIMIT] = POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + [USB_ADAP_TYPE] = POWER_SUPPLY_PROP_USB_TYPE, + [USB_TEMP] = POWER_SUPPLY_PROP_TEMP, +}; + +/* Standard usb_type definitions similar to power_supply_sysfs.c */ +static const char * const power_supply_usb_type_text[] = { + "Unknown", "SDP", "DCP", "CDP", "ACA", "C", + "PD", "PD_DRP", "PD_PPS", "BrickID" +}; + +/* Custom usb_type definitions */ +static const char * const qc_power_supply_usb_type_text[] = { + "HVDCP", "HVDCP_3", "HVDCP_3P5" +}; + +static int battery_chg_write(struct battery_chg_dev *bcdev, void *data, + int len) +{ + int rc; + + /* + * When the subsystem goes down, it's better to return the last + * known values until it comes back up. Hence, return 0 so that + * pmic_glink_write() is not attempted until pmic glink is up. + */ + if (atomic_read(&bcdev->state) == PMIC_GLINK_STATE_DOWN) { + pr_debug("glink state is down\n"); + return 0; + } + + if (bcdev->debug_battery_detected && bcdev->block_tx) + return 0; + + mutex_lock(&bcdev->rw_lock); + reinit_completion(&bcdev->ack); + rc = pmic_glink_write(bcdev->client, data, len); + if (!rc) { + rc = wait_for_completion_timeout(&bcdev->ack, + msecs_to_jiffies(BC_WAIT_TIME_MS)); + if (!rc) { + pr_err("Error, timed out sending message\n"); + mutex_unlock(&bcdev->rw_lock); + return -ETIMEDOUT; + } + + rc = 0; + } + mutex_unlock(&bcdev->rw_lock); + + return rc; +} + +static int write_property_id(struct battery_chg_dev *bcdev, + struct psy_state *pst, u32 prop_id, u32 val) +{ + struct battery_charger_req_msg req_msg = { { 0 } }; + + req_msg.property_id = prop_id; + req_msg.battery_id = 0; + req_msg.value = val; + req_msg.hdr.owner = MSG_OWNER_BC; + req_msg.hdr.type = MSG_TYPE_REQ_RESP; + req_msg.hdr.opcode = pst->opcode_set; + + pr_debug("psy: %s prop_id: %u val: %u\n", pst->psy->desc->name, + req_msg.property_id, val); + + return battery_chg_write(bcdev, &req_msg, sizeof(req_msg)); +} + +static int read_property_id(struct battery_chg_dev *bcdev, + struct psy_state *pst, u32 prop_id) +{ + struct battery_charger_req_msg req_msg = { { 0 } }; + + req_msg.property_id = prop_id; + req_msg.battery_id = 0; + req_msg.value = 0; + req_msg.hdr.owner = MSG_OWNER_BC; + req_msg.hdr.type = MSG_TYPE_REQ_RESP; + req_msg.hdr.opcode = pst->opcode_get; + + pr_debug("psy: %s prop_id: %u\n", pst->psy->desc->name, + req_msg.property_id); + + return battery_chg_write(bcdev, &req_msg, sizeof(req_msg)); +} + +static int get_property_id(struct psy_state *pst, + enum power_supply_property prop) +{ + u32 i; + + for (i = 0; i < pst->prop_count; i++) + if (pst->map[i] == prop) + return i; + + pr_err("No property id for property %d in psy %s\n", prop, + pst->psy->desc->name); + + return -ENOENT; +} + +static void battery_chg_notify_enable(struct battery_chg_dev *bcdev) +{ + struct battery_charger_set_notify_msg req_msg = { { 0 } }; + int rc; + + /* Send request to enable notification */ + req_msg.hdr.owner = MSG_OWNER_BC; + req_msg.hdr.type = MSG_TYPE_NOTIFY; + req_msg.hdr.opcode = BC_SET_NOTIFY_REQ; + + rc = battery_chg_write(bcdev, &req_msg, sizeof(req_msg)); + if (rc < 0) + pr_err("Failed to enable notification rc=%d\n", rc); +} + +static void battery_chg_state_cb(void *priv, enum pmic_glink_state state) +{ + struct battery_chg_dev *bcdev = priv; + + pr_debug("state: %d\n", state); + + atomic_set(&bcdev->state, state); + if (state == PMIC_GLINK_STATE_UP) + schedule_work(&bcdev->subsys_up_work); +} + +static bool validate_message(struct battery_charger_resp_msg *resp_msg, + size_t len) +{ + if (len != sizeof(*resp_msg)) { + pr_err("Incorrect response length %zu for opcode %#x\n", len, + resp_msg->hdr.opcode); + return false; + } + + if (resp_msg->ret_code) { + pr_err("Error in response for opcode %#x prop_id %u, rc=%d\n", + resp_msg->hdr.opcode, resp_msg->property_id, + (int)resp_msg->ret_code); + return false; + } + + return true; +} + +#define MODEL_DEBUG_BOARD "Debug_Board" +static void handle_message(struct battery_chg_dev *bcdev, void *data, + size_t len) +{ + struct battery_charger_resp_msg *resp_msg = data; + struct battery_model_resp_msg *model_resp_msg = data; + struct psy_state *pst; + bool ack_set = false; + + switch (resp_msg->hdr.opcode) { + case BC_BATTERY_STATUS_GET: + pst = &bcdev->psy_list[PSY_TYPE_BATTERY]; + + /* Handle model response uniquely as it's a string */ + if (pst->model && len == sizeof(*model_resp_msg)) { + memcpy(pst->model, model_resp_msg->model, MAX_STR_LEN); + ack_set = true; + bcdev->debug_battery_detected = !strcmp(pst->model, + MODEL_DEBUG_BOARD); + break; + } + + /* Other response should be of same type as they've u32 value */ + if (validate_message(resp_msg, len) && + resp_msg->property_id < pst->prop_count) { + pst->prop[resp_msg->property_id] = resp_msg->value; + ack_set = true; + } + + break; + case BC_USB_STATUS_GET: + pst = &bcdev->psy_list[PSY_TYPE_USB]; + if (validate_message(resp_msg, len) && + resp_msg->property_id < pst->prop_count) { + pst->prop[resp_msg->property_id] = resp_msg->value; + ack_set = true; + } + + break; + case BC_BATTERY_STATUS_SET: + case BC_USB_STATUS_SET: + if (validate_message(data, len)) + ack_set = true; + + break; + case BC_SET_NOTIFY_REQ: + /* Always ACK response for notify request */ + ack_set = true; + break; + default: + pr_err("Unknown opcode: %u\n", resp_msg->hdr.opcode); + break; + } + + if (ack_set) + complete(&bcdev->ack); +} + +static struct power_supply_desc usb_psy_desc; + +static void battery_chg_update_usb_type_work(struct work_struct *work) +{ + struct battery_chg_dev *bcdev = container_of(work, + struct battery_chg_dev, usb_type_work); + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_USB]; + int rc; + + rc = read_property_id(bcdev, pst, USB_ADAP_TYPE); + if (rc < 0) { + pr_err("Failed to read USB_ADAP_TYPE rc=%d\n", rc); + return; + } + + /* Reset usb_icl_ua whenever USB adapter type changes */ + if (pst->prop[USB_ADAP_TYPE] != POWER_SUPPLY_USB_TYPE_SDP && + pst->prop[USB_ADAP_TYPE] != POWER_SUPPLY_USB_TYPE_PD) + bcdev->usb_icl_ua = 0; + + pr_debug("usb_adap_type: %u\n", pst->prop[USB_ADAP_TYPE]); + + switch (pst->prop[USB_ADAP_TYPE]) { + case POWER_SUPPLY_USB_TYPE_SDP: + usb_psy_desc.type = POWER_SUPPLY_TYPE_USB; + break; + case POWER_SUPPLY_USB_TYPE_DCP: + case POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID: + case QTI_POWER_SUPPLY_USB_TYPE_HVDCP: + case QTI_POWER_SUPPLY_USB_TYPE_HVDCP_3: + case QTI_POWER_SUPPLY_USB_TYPE_HVDCP_3P5: + usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_DCP; + break; + case POWER_SUPPLY_USB_TYPE_CDP: + usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_CDP; + break; + case POWER_SUPPLY_USB_TYPE_ACA: + usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_ACA; + break; + case POWER_SUPPLY_USB_TYPE_C: + usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_TYPE_C; + break; + case POWER_SUPPLY_USB_TYPE_PD: + case POWER_SUPPLY_USB_TYPE_PD_DRP: + case POWER_SUPPLY_USB_TYPE_PD_PPS: + usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_PD; + break; + default: + usb_psy_desc.type = POWER_SUPPLY_TYPE_USB; + break; + } +} + +static void handle_notification(struct battery_chg_dev *bcdev, void *data, + size_t len) +{ + struct battery_charger_notify_msg *notify_msg = data; + struct psy_state *pst = NULL; + + if (len != sizeof(*notify_msg)) { + pr_err("Incorrect response length %zu\n", len); + return; + } + + pr_debug("notification: %#x\n", notify_msg->notification); + + switch (notify_msg->notification) { + case BC_BATTERY_STATUS_GET: + case BC_GENERIC_NOTIFY: + pst = &bcdev->psy_list[PSY_TYPE_BATTERY]; + break; + case BC_USB_STATUS_GET: + pst = &bcdev->psy_list[PSY_TYPE_USB]; + schedule_work(&bcdev->usb_type_work); + break; + default: + break; + } + + if (pst && pst->psy) { + /* + * For charger mode, keep the device awake at least for 50 ms + * so that device won't enter suspend when a non-SDP charger + * is removed. This would allow the userspace process like + * "charger" to be able to read power supply uevents to take + * appropriate actions (e.g. shutting down when the charger is + * unplugged). + */ + power_supply_changed(pst->psy); + pm_wakeup_dev_event(bcdev->dev, 50, true); + } +} + +static int battery_chg_callback(void *priv, void *data, size_t len) +{ + struct pmic_glink_hdr *hdr = data; + struct battery_chg_dev *bcdev = priv; + + pr_debug("owner: %u type: %u opcode: %#x len: %zu\n", hdr->owner, + hdr->type, hdr->opcode, len); + + if (!bcdev->initialized) { + pr_debug("Driver initialization failed: Dropping glink callback message\n"); + return 0; + } + + if (hdr->opcode == BC_NOTIFY_IND) + handle_notification(bcdev, data, len); + else + handle_message(bcdev, data, len); + + return 0; +} + +static const char *get_usb_type_name(u32 usb_type) +{ + u32 i; + + if (usb_type >= QTI_POWER_SUPPLY_USB_TYPE_HVDCP && + usb_type <= QTI_POWER_SUPPLY_USB_TYPE_HVDCP_3P5) { + for (i = 0; i < ARRAY_SIZE(qc_power_supply_usb_type_text); + i++) { + if (i == (usb_type - QTI_POWER_SUPPLY_USB_TYPE_HVDCP)) + return qc_power_supply_usb_type_text[i]; + } + return "Unknown"; + } + + for (i = 0; i < ARRAY_SIZE(power_supply_usb_type_text); i++) { + if (i == usb_type) + return power_supply_usb_type_text[i]; + } + + return "Unknown"; +} + +static int usb_psy_set_icl(struct battery_chg_dev *bcdev, u32 prop_id, int val) +{ + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_USB]; + u32 temp; + int rc; + + rc = read_property_id(bcdev, pst, USB_ADAP_TYPE); + if (rc < 0) { + pr_err("Failed to read prop USB_ADAP_TYPE, rc=%d\n", rc); + return rc; + } + + /* Allow this only for SDP or USB_PD and not for other charger types */ + if (pst->prop[USB_ADAP_TYPE] != POWER_SUPPLY_USB_TYPE_SDP && + pst->prop[USB_ADAP_TYPE] != POWER_SUPPLY_USB_TYPE_PD) + return -EINVAL; + + /* + * Input current limit (ICL) can be set by different clients. E.g. USB + * driver can request for a current of 500/900 mA depending on the + * port type. Also, clients like EUD driver can pass 0 or -22 to + * suspend or unsuspend the input for its use case. + */ + + temp = val; + if (val < 0) + temp = UINT_MAX; + + rc = write_property_id(bcdev, pst, prop_id, temp); + if (rc < 0) { + pr_err("Failed to set ICL (%u uA) rc=%d\n", temp, rc); + } else { + pr_debug("Set ICL to %u\n", temp); + bcdev->usb_icl_ua = temp; + } + + return rc; +} + +static int usb_psy_get_prop(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *pval) +{ + struct battery_chg_dev *bcdev = power_supply_get_drvdata(psy); + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_USB]; + int prop_id, rc; + + pval->intval = -ENODATA; + + prop_id = get_property_id(pst, prop); + if (prop_id < 0) + return prop_id; + + rc = read_property_id(bcdev, pst, prop_id); + if (rc < 0) + return rc; + + pval->intval = pst->prop[prop_id]; + if (prop == POWER_SUPPLY_PROP_TEMP) + pval->intval = DIV_ROUND_CLOSEST((int)pval->intval, 10); + + return 0; +} + +static int usb_psy_set_prop(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *pval) +{ + struct battery_chg_dev *bcdev = power_supply_get_drvdata(psy); + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_USB]; + int prop_id, rc = 0; + + prop_id = get_property_id(pst, prop); + if (prop_id < 0) + return prop_id; + + switch (prop) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + rc = usb_psy_set_icl(bcdev, prop_id, pval->intval); + break; + default: + break; + } + + return rc; +} + +static int usb_psy_prop_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + switch (prop) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + return 1; + default: + break; + } + + return 0; +} + +static enum power_supply_property usb_props[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_TEMP, +}; + +static enum power_supply_usb_type usb_psy_supported_types[] = { + POWER_SUPPLY_USB_TYPE_UNKNOWN, + POWER_SUPPLY_USB_TYPE_SDP, + POWER_SUPPLY_USB_TYPE_DCP, + POWER_SUPPLY_USB_TYPE_CDP, + POWER_SUPPLY_USB_TYPE_ACA, + POWER_SUPPLY_USB_TYPE_C, + POWER_SUPPLY_USB_TYPE_PD, + POWER_SUPPLY_USB_TYPE_PD_DRP, + POWER_SUPPLY_USB_TYPE_PD_PPS, + POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID, +}; + +static struct power_supply_desc usb_psy_desc = { + .name = "usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = usb_props, + .num_properties = ARRAY_SIZE(usb_props), + .get_property = usb_psy_get_prop, + .set_property = usb_psy_set_prop, + .usb_types = usb_psy_supported_types, + .num_usb_types = ARRAY_SIZE(usb_psy_supported_types), + .property_is_writeable = usb_psy_prop_is_writeable, +}; + +static int __battery_psy_set_charge_current(struct battery_chg_dev *bcdev, + u32 fcc_ua) +{ + int rc; + + if (bcdev->restrict_chg_en) { + fcc_ua = min_t(u32, fcc_ua, bcdev->restrict_fcc_ua); + fcc_ua = min_t(u32, fcc_ua, bcdev->thermal_fcc_ua); + } + + rc = write_property_id(bcdev, &bcdev->psy_list[PSY_TYPE_BATTERY], + BATT_CHG_CTRL_LIM, fcc_ua); + if (rc < 0) { + pr_err("Failed to set FCC %u, rc=%d\n", fcc_ua, rc); + } else { + pr_debug("Set FCC to %u uA\n", fcc_ua); + bcdev->last_fcc_ua = fcc_ua; + } + + return rc; +} + +static int battery_psy_set_charge_current(struct battery_chg_dev *bcdev, + int val) +{ + int rc; + u32 fcc_ua, prev_fcc_ua; + + if (!bcdev->num_thermal_levels) + return 0; + + if (bcdev->num_thermal_levels < 0) { + pr_err("Incorrect num_thermal_levels\n"); + return -EINVAL; + } + + if (val < 0 || val > bcdev->num_thermal_levels) + return -EINVAL; + + fcc_ua = bcdev->thermal_levels[val]; + prev_fcc_ua = bcdev->thermal_fcc_ua; + bcdev->thermal_fcc_ua = fcc_ua; + + rc = __battery_psy_set_charge_current(bcdev, fcc_ua); + if (!rc) + bcdev->curr_thermal_level = val; + else + bcdev->thermal_fcc_ua = prev_fcc_ua; + + return rc; +} + +static int battery_psy_get_prop(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *pval) +{ + struct battery_chg_dev *bcdev = power_supply_get_drvdata(psy); + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_BATTERY]; + int prop_id, rc; + + pval->intval = -ENODATA; + + /* + * The prop id of TIME_TO_FULL_NOW and TIME_TO_FULL_AVG is same. + * So, map the prop id of TIME_TO_FULL_AVG for TIME_TO_FULL_NOW. + */ + if (prop == POWER_SUPPLY_PROP_TIME_TO_FULL_NOW) + prop = POWER_SUPPLY_PROP_TIME_TO_FULL_AVG; + + prop_id = get_property_id(pst, prop); + if (prop_id < 0) + return prop_id; + + rc = read_property_id(bcdev, pst, prop_id); + if (rc < 0) + return rc; + + switch (prop) { + case POWER_SUPPLY_PROP_MODEL_NAME: + pval->strval = pst->model; + break; + case POWER_SUPPLY_PROP_CAPACITY: + pval->intval = DIV_ROUND_CLOSEST(pst->prop[prop_id], 100); + if (IS_ENABLED(CONFIG_QTI_PMIC_GLINK_CLIENT_DEBUG) && + (bcdev->fake_soc >= 0 && bcdev->fake_soc <= 100)) + pval->intval = bcdev->fake_soc; + break; + case POWER_SUPPLY_PROP_TEMP: + pval->intval = DIV_ROUND_CLOSEST((int)pst->prop[prop_id], 10); + break; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + pval->intval = bcdev->curr_thermal_level; + break; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: + pval->intval = bcdev->num_thermal_levels; + break; + default: + pval->intval = pst->prop[prop_id]; + break; + } + + return rc; +} + +static int battery_psy_set_prop(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *pval) +{ + struct battery_chg_dev *bcdev = power_supply_get_drvdata(psy); + + switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + return battery_psy_set_charge_current(bcdev, pval->intval); + default: + return -EINVAL; + } + + return 0; +} + +static int battery_psy_prop_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + switch (prop) { + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + return 1; + default: + break; + } + + return 0; +} + +static enum power_supply_property battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_VOLTAGE_OCV, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, + POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, + POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, + POWER_SUPPLY_PROP_POWER_NOW, + POWER_SUPPLY_PROP_POWER_AVG, +}; + +static const struct power_supply_desc batt_psy_desc = { + .name = "main_battery", + .type = POWER_SUPPLY_TYPE_UNKNOWN, + .properties = battery_props, + .num_properties = ARRAY_SIZE(battery_props), + .get_property = battery_psy_get_prop, + .set_property = battery_psy_set_prop, + .property_is_writeable = battery_psy_prop_is_writeable, +}; + +static int combo_psy_get_prop(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *pval) +{ + struct battery_chg_dev *bcdev = power_supply_get_drvdata(psy); + int rc = 0; + + if (!bcdev->mmi_psy) { + bcdev->mmi_psy = power_supply_get_by_name("mmi_battery"); + if (!bcdev->mmi_psy) { + pr_err("No mmi battery supply found.\n\n"); + pval->intval = -EINVAL; + return rc; + } + } + + switch (prop) { + case POWER_SUPPLY_PROP_PRESENT: + pval->intval = 1; + break; + default: + rc = power_supply_get_property(bcdev->mmi_psy, prop, pval); + if (rc < 0) { + pr_err("Get Unknown prop %d rc = %d\n", prop, rc); + /* soft fail so uevents are not blocked */ + rc = 0; + pval->intval = -EINVAL; + } + break; + } + + return rc; +} + +static int combo_psy_set_prop(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *pval) +{ + struct battery_chg_dev *bcdev = power_supply_get_drvdata(psy); + int rc = 0; + + if (!bcdev->mmi_psy) { + bcdev->mmi_psy = power_supply_get_by_name("mmi_battery"); + if (!bcdev->mmi_psy) { + pr_err("No mmi battery supply found.\n\n"); + return -EINVAL; + } + } + + switch (prop) { + default: + rc = power_supply_set_property(bcdev->mmi_psy, prop, pval); + if (rc < 0) { + pr_err("Get Unknown prop %d rc = %d\n", prop, rc); + /* soft fail so uevents are not blocked */ + rc = 0; + } + break; + } + + return rc; +} + +static int combo_psy_prop_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + switch (prop) { + case POWER_SUPPLY_PROP_CYCLE_COUNT: + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + return 1; + default: + break; + } + + return 0; +} + +static enum power_supply_property combo_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, +}; + +static const struct power_supply_desc combo_psy_desc = { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = combo_props, + .num_properties = ARRAY_SIZE(combo_props), + .get_property = combo_psy_get_prop, + .set_property = combo_psy_set_prop, + .property_is_writeable = combo_psy_prop_is_writeable, +}; + +static int battery_chg_init_psy(struct battery_chg_dev *bcdev) +{ + struct power_supply_config psy_cfg = {}; + int rc; + + psy_cfg.drv_data = bcdev; + psy_cfg.of_node = bcdev->dev->of_node; + bcdev->psy_list[PSY_TYPE_BATTERY].psy = + devm_power_supply_register(bcdev->dev, &batt_psy_desc, + &psy_cfg); + if (IS_ERR(bcdev->psy_list[PSY_TYPE_BATTERY].psy)) { + rc = PTR_ERR(bcdev->psy_list[PSY_TYPE_BATTERY].psy); + pr_err("Failed to register battery power supply, rc=%d\n", rc); + return rc; + } + + bcdev->psy_list[PSY_TYPE_USB].psy = + devm_power_supply_register(bcdev->dev, &usb_psy_desc, &psy_cfg); + if (IS_ERR(bcdev->psy_list[PSY_TYPE_USB].psy)) { + rc = PTR_ERR(bcdev->psy_list[PSY_TYPE_USB].psy); + pr_err("Failed to register USB power supply, rc=%d\n", rc); + return rc; + } + + bcdev->combo_psy = + devm_power_supply_register(bcdev->dev, &combo_psy_desc, &psy_cfg); + if (IS_ERR(bcdev->combo_psy)) { + rc = PTR_ERR(bcdev->combo_psy); + pr_err("Failed to register combo power supply, rc=%d\n", rc); + return rc; + } + + return 0; +} + +static void battery_chg_subsys_up_work(struct work_struct *work) +{ + struct battery_chg_dev *bcdev = container_of(work, + struct battery_chg_dev, subsys_up_work); + int rc; + + battery_chg_notify_enable(bcdev); + + /* + * Give some time after enabling notification so that USB adapter type + * information can be obtained properly which is essential for setting + * USB ICL. + */ + msleep(200); + + if (bcdev->last_fcc_ua) { + rc = __battery_psy_set_charge_current(bcdev, + bcdev->last_fcc_ua); + if (rc < 0) + pr_err("Failed to set FCC (%u uA), rc=%d\n", + bcdev->last_fcc_ua, rc); + } + + if (bcdev->usb_icl_ua) { + rc = usb_psy_set_icl(bcdev, USB_INPUT_CURR_LIMIT, + bcdev->usb_icl_ua); + if (rc < 0) + pr_err("Failed to set ICL(%u uA), rc=%d\n", + bcdev->usb_icl_ua, rc); + } +} + +static ssize_t usb_typec_compliant_show(struct class *c, + struct class_attribute *attr, char *buf) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_USB]; + int rc; + + rc = read_property_id(bcdev, pst, USB_TYPEC_COMPLIANT); + if (rc < 0) + return rc; + + return scnprintf(buf, PAGE_SIZE, "%d\n", + (int)pst->prop[USB_TYPEC_COMPLIANT]); +} +static CLASS_ATTR_RO(usb_typec_compliant); + +static ssize_t usb_real_type_show(struct class *c, + struct class_attribute *attr, char *buf) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_USB]; + int rc; + + rc = read_property_id(bcdev, pst, USB_REAL_TYPE); + if (rc < 0) + return rc; + + return scnprintf(buf, PAGE_SIZE, "%s\n", + get_usb_type_name(pst->prop[USB_REAL_TYPE])); +} +static CLASS_ATTR_RO(usb_real_type); + +static ssize_t restrict_cur_store(struct class *c, struct class_attribute *attr, + const char *buf, size_t count) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + int rc; + u32 fcc_ua, prev_fcc_ua; + + if (kstrtou32(buf, 0, &fcc_ua) || fcc_ua > bcdev->thermal_fcc_ua) + return -EINVAL; + + prev_fcc_ua = bcdev->restrict_fcc_ua; + bcdev->restrict_fcc_ua = fcc_ua; + if (bcdev->restrict_chg_en) { + rc = __battery_psy_set_charge_current(bcdev, fcc_ua); + if (rc < 0) { + bcdev->restrict_fcc_ua = prev_fcc_ua; + return rc; + } + } + + return count; +} + +static ssize_t restrict_cur_show(struct class *c, struct class_attribute *attr, + char *buf) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + + return scnprintf(buf, PAGE_SIZE, "%u\n", bcdev->restrict_fcc_ua); +} +static CLASS_ATTR_RW(restrict_cur); + +static ssize_t restrict_chg_store(struct class *c, struct class_attribute *attr, + const char *buf, size_t count) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + int rc; + bool val; + + if (kstrtobool(buf, &val)) + return -EINVAL; + + bcdev->restrict_chg_en = val; + rc = __battery_psy_set_charge_current(bcdev, bcdev->restrict_chg_en ? + bcdev->restrict_fcc_ua : bcdev->thermal_fcc_ua); + if (rc < 0) + return rc; + + return count; +} + +static ssize_t restrict_chg_show(struct class *c, struct class_attribute *attr, + char *buf) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + + return scnprintf(buf, PAGE_SIZE, "%d\n", bcdev->restrict_chg_en); +} +static CLASS_ATTR_RW(restrict_chg); + +static ssize_t fake_soc_store(struct class *c, struct class_attribute *attr, + const char *buf, size_t count) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_BATTERY]; + int val; + + if (kstrtoint(buf, 0, &val)) + return -EINVAL; + + bcdev->fake_soc = val; + pr_debug("Set fake soc to %d\n", val); + + if (IS_ENABLED(CONFIG_QTI_PMIC_GLINK_CLIENT_DEBUG) && pst->psy) + power_supply_changed(pst->psy); + + return count; +} + +static ssize_t fake_soc_show(struct class *c, struct class_attribute *attr, + char *buf) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + + return scnprintf(buf, PAGE_SIZE, "%d\n", bcdev->fake_soc); +} +static CLASS_ATTR_RW(fake_soc); + + +static ssize_t moisture_detection_en_store(struct class *c, + struct class_attribute *attr, + const char *buf, size_t count) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + int rc; + bool val; + + if (kstrtobool(buf, &val)) + return -EINVAL; + + rc = write_property_id(bcdev, &bcdev->psy_list[PSY_TYPE_USB], + USB_MOISTURE_DET_EN, val); + if (rc < 0) + return rc; + + return count; +} + +static ssize_t moisture_detection_en_show(struct class *c, + struct class_attribute *attr, char *buf) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_USB]; + int rc; + + rc = read_property_id(bcdev, pst, USB_MOISTURE_DET_EN); + if (rc < 0) + return rc; + + return scnprintf(buf, PAGE_SIZE, "%d\n", + pst->prop[USB_MOISTURE_DET_EN]); +} +static CLASS_ATTR_RW(moisture_detection_en); + +static ssize_t moisture_detection_status_show(struct class *c, + struct class_attribute *attr, char *buf) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_USB]; + int rc; + + rc = read_property_id(bcdev, pst, USB_MOISTURE_DET_STS); + if (rc < 0) + return rc; + + return scnprintf(buf, PAGE_SIZE, "%d\n", + pst->prop[USB_MOISTURE_DET_STS]); +} +static CLASS_ATTR_RO(moisture_detection_status); + +static ssize_t resistance_show(struct class *c, + struct class_attribute *attr, char *buf) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_BATTERY]; + int rc; + + rc = read_property_id(bcdev, pst, BATT_RESISTANCE); + if (rc < 0) + return rc; + + return scnprintf(buf, PAGE_SIZE, "%u\n", pst->prop[BATT_RESISTANCE]); +} +static CLASS_ATTR_RO(resistance); + +static ssize_t soh_show(struct class *c, struct class_attribute *attr, + char *buf) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_BATTERY]; + int rc; + + rc = read_property_id(bcdev, pst, BATT_SOH); + if (rc < 0) + return rc; + + return scnprintf(buf, PAGE_SIZE, "%d\n", pst->prop[BATT_SOH]); +} +static CLASS_ATTR_RO(soh); + +static ssize_t ship_mode_en_store(struct class *c, struct class_attribute *attr, + const char *buf, size_t count) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + + if (kstrtobool(buf, &bcdev->ship_mode_en)) + return -EINVAL; + + return count; +} + +static ssize_t ship_mode_en_show(struct class *c, struct class_attribute *attr, + char *buf) +{ + struct battery_chg_dev *bcdev = container_of(c, struct battery_chg_dev, + battery_class); + + return scnprintf(buf, PAGE_SIZE, "%d\n", bcdev->ship_mode_en); +} +static CLASS_ATTR_RW(ship_mode_en); + +static struct attribute *battery_class_attrs[] = { + &class_attr_soh.attr, + &class_attr_resistance.attr, + &class_attr_moisture_detection_status.attr, + &class_attr_moisture_detection_en.attr, + &class_attr_fake_soc.attr, + &class_attr_ship_mode_en.attr, + &class_attr_restrict_chg.attr, + &class_attr_restrict_cur.attr, + &class_attr_usb_real_type.attr, + &class_attr_usb_typec_compliant.attr, + NULL, +}; +ATTRIBUTE_GROUPS(battery_class); + +#ifdef CONFIG_DEBUG_FS +static void battery_chg_add_debugfs(struct battery_chg_dev *bcdev) +{ + int rc; + struct dentry *dir, *file; + + dir = debugfs_create_dir("battery_charger", NULL); + if (IS_ERR(dir)) { + rc = PTR_ERR(dir); + pr_err("Failed to create charger debugfs directory, rc=%d\n", + rc); + return; + } + + file = debugfs_create_bool("block_tx", 0600, dir, &bcdev->block_tx); + if (IS_ERR(file)) { + rc = PTR_ERR(file); + pr_err("Failed to create block_tx debugfs file, rc=%d\n", + rc); + goto error; + } + + bcdev->debugfs_dir = dir; + + return; +error: + debugfs_remove_recursive(dir); +} +#else +static void battery_chg_add_debugfs(struct battery_chg_dev *bcdev) { } +#endif + +static int battery_chg_parse_dt(struct battery_chg_dev *bcdev) +{ + struct device_node *node = bcdev->dev->of_node; + struct psy_state *pst = &bcdev->psy_list[PSY_TYPE_BATTERY]; + int i, rc, len; + u32 prev, val; + + rc = of_property_count_elems_of_size(node, "qcom,thermal-mitigation", + sizeof(u32)); + if (rc <= 0) + return 0; + + len = rc; + + rc = read_property_id(bcdev, pst, BATT_CHG_CTRL_LIM_MAX); + if (rc < 0) { + pr_err("Failed to read prop BATT_CHG_CTRL_LIM_MAX, rc=%d\n", + rc); + return rc; + } + + prev = pst->prop[BATT_CHG_CTRL_LIM_MAX]; + + for (i = 0; i < len; i++) { + rc = of_property_read_u32_index(node, "qcom,thermal-mitigation", + i, &val); + if (rc < 0) + return rc; + + if (val > prev) { + pr_err("Thermal levels should be in descending order\n"); + bcdev->num_thermal_levels = -EINVAL; + return 0; + } + + prev = val; + } + + bcdev->thermal_levels = devm_kcalloc(bcdev->dev, len + 1, + sizeof(*bcdev->thermal_levels), + GFP_KERNEL); + if (!bcdev->thermal_levels) + return -ENOMEM; + + /* + * Element 0 is for normal charging current. Elements from index 1 + * onwards is for thermal mitigation charging currents. + */ + + bcdev->thermal_levels[0] = pst->prop[BATT_CHG_CTRL_LIM_MAX]; + + rc = of_property_read_u32_array(node, "qcom,thermal-mitigation", + &bcdev->thermal_levels[1], len); + if (rc < 0) { + pr_err("Error in reading qcom,thermal-mitigation, rc=%d\n", rc); + return rc; + } + + bcdev->num_thermal_levels = len; + bcdev->thermal_fcc_ua = pst->prop[BATT_CHG_CTRL_LIM_MAX]; + + return 0; +} + +static int battery_chg_ship_mode(struct notifier_block *nb, unsigned long code, + void *unused) +{ + struct battery_charger_notify_msg msg_notify = { { 0 } }; + struct battery_charger_ship_mode_req_msg msg = { { 0 } }; + struct battery_chg_dev *bcdev = container_of(nb, struct battery_chg_dev, + reboot_notifier); + int rc; + + msg_notify.hdr.owner = MSG_OWNER_BC; + msg_notify.hdr.type = MSG_TYPE_NOTIFY; + msg_notify.hdr.opcode = BC_SHUTDOWN_NOTIFY; + + rc = battery_chg_write(bcdev, &msg_notify, sizeof(msg_notify)); + if (rc < 0) + pr_err("Failed to send shutdown notification rc=%d\n", rc); + + if (!bcdev->ship_mode_en) + return NOTIFY_DONE; + + msg.hdr.owner = MSG_OWNER_BC; + msg.hdr.type = MSG_TYPE_REQ_RESP; + msg.hdr.opcode = BC_SHIP_MODE_REQ_SET; + msg.ship_mode_type = SHIP_MODE_PMIC; + + if (code == SYS_POWER_OFF) { + rc = battery_chg_write(bcdev, &msg, sizeof(msg)); + if (rc < 0) + pr_emerg("Failed to write ship mode: %d\n", rc); + } + + return NOTIFY_DONE; +} + +static int battery_chg_probe(struct platform_device *pdev) +{ + struct battery_chg_dev *bcdev; + struct device *dev = &pdev->dev; + struct pmic_glink_client_data client_data = { }; + int rc, i; + + bcdev = devm_kzalloc(&pdev->dev, sizeof(*bcdev), GFP_KERNEL); + if (!bcdev) + return -ENOMEM; + + bcdev->psy_list[PSY_TYPE_BATTERY].map = battery_prop_map; + bcdev->psy_list[PSY_TYPE_BATTERY].prop_count = BATT_PROP_MAX; + bcdev->psy_list[PSY_TYPE_BATTERY].opcode_get = BC_BATTERY_STATUS_GET; + bcdev->psy_list[PSY_TYPE_BATTERY].opcode_set = BC_BATTERY_STATUS_SET; + bcdev->psy_list[PSY_TYPE_USB].map = usb_prop_map; + bcdev->psy_list[PSY_TYPE_USB].prop_count = USB_PROP_MAX; + bcdev->psy_list[PSY_TYPE_USB].opcode_get = BC_USB_STATUS_GET; + bcdev->psy_list[PSY_TYPE_USB].opcode_set = BC_USB_STATUS_SET; + + for (i = 0; i < PSY_TYPE_MAX; i++) { + bcdev->psy_list[i].prop = + devm_kcalloc(&pdev->dev, bcdev->psy_list[i].prop_count, + sizeof(u32), GFP_KERNEL); + if (!bcdev->psy_list[i].prop) + return -ENOMEM; + } + + bcdev->psy_list[PSY_TYPE_BATTERY].model = + devm_kzalloc(&pdev->dev, MAX_STR_LEN, GFP_KERNEL); + if (!bcdev->psy_list[PSY_TYPE_BATTERY].model) + return -ENOMEM; + + mutex_init(&bcdev->rw_lock); + init_completion(&bcdev->ack); + INIT_WORK(&bcdev->subsys_up_work, battery_chg_subsys_up_work); + INIT_WORK(&bcdev->usb_type_work, battery_chg_update_usb_type_work); + atomic_set(&bcdev->state, PMIC_GLINK_STATE_UP); + bcdev->dev = dev; + + client_data.id = MSG_OWNER_BC; + client_data.name = "battery_charger"; + client_data.msg_cb = battery_chg_callback; + client_data.priv = bcdev; + client_data.state_cb = battery_chg_state_cb; + + bcdev->client = pmic_glink_register_client(dev, &client_data); + if (IS_ERR(bcdev->client)) { + rc = PTR_ERR(bcdev->client); + if (rc != -EPROBE_DEFER) + dev_err(dev, "Error in registering with pmic_glink %d\n", + rc); + return rc; + } + + bcdev->initialized = true; + bcdev->reboot_notifier.notifier_call = battery_chg_ship_mode; + bcdev->reboot_notifier.priority = 255; + register_reboot_notifier(&bcdev->reboot_notifier); + + rc = battery_chg_parse_dt(bcdev); + if (rc < 0) { + dev_err(dev, "Failed to parse dt rc=%d\n", rc); + goto error; + } + + bcdev->restrict_fcc_ua = DEFAULT_RESTRICT_FCC_UA; + platform_set_drvdata(pdev, bcdev); + bcdev->fake_soc = -EINVAL; + rc = battery_chg_init_psy(bcdev); + if (rc < 0) + goto error; + + bcdev->battery_class.name = "qcom-battery"; + bcdev->battery_class.class_groups = battery_class_groups; + rc = class_register(&bcdev->battery_class); + if (rc < 0) { + dev_err(dev, "Failed to create battery_class rc=%d\n", rc); + goto error; + } + + battery_chg_add_debugfs(bcdev); + battery_chg_notify_enable(bcdev); + device_init_wakeup(bcdev->dev, true); + schedule_work(&bcdev->usb_type_work); + + return 0; +error: + bcdev->initialized = false; + complete(&bcdev->ack); + pmic_glink_unregister_client(bcdev->client); + unregister_reboot_notifier(&bcdev->reboot_notifier); + return rc; +} + +static int battery_chg_remove(struct platform_device *pdev) +{ + struct battery_chg_dev *bcdev = platform_get_drvdata(pdev); + int rc; + + device_init_wakeup(bcdev->dev, false); + debugfs_remove_recursive(bcdev->debugfs_dir); + class_unregister(&bcdev->battery_class); + unregister_reboot_notifier(&bcdev->reboot_notifier); + rc = pmic_glink_unregister_client(bcdev->client); + if (rc < 0) { + pr_err("Error unregistering from pmic_glink, rc=%d\n", rc); + return rc; + } + + return 0; +} + +static const struct of_device_id battery_chg_match_table[] = { + { .compatible = "qcom,battery-charger-mmi" }, + {}, +}; + +static struct platform_driver battery_chg_driver = { + .driver = { + .name = "qti_battery_charger_mmi", + .of_match_table = battery_chg_match_table, + }, + .probe = battery_chg_probe, + .remove = battery_chg_remove, +}; +module_platform_driver(battery_chg_driver); + +MODULE_DESCRIPTION("QTI Glink battery charger driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/qti_glink_charger/Android.mk b/drivers/power/qti_glink_charger/Android.mk new file mode 100755 index 000000000000..a59f66cfd10f --- /dev/null +++ b/drivers/power/qti_glink_charger/Android.mk @@ -0,0 +1,37 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := qti_glink_charger.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_charger.ko +LOCAL_ADDITIONAL_DEPENDENCIES += $(KERNEL_MODULES_OUT)/bm_adsp_ulog.ko + +ifeq ($(WIRELESS_CPS4035B),true) + KERNEL_CFLAGS += CONFIG_WIRELESS_CPS4035B=y + KBUILD_OPTIONS += CONFIG_WIRELESS_CPS4035B=y +endif + +ifeq ($(WIRELESS_CPS4019),true) + KERNEL_CFLAGS += CONFIG_WIRELESS_CPS4019=y + KBUILD_OPTIONS += CONFIG_WIRELESS_CPS4019=y +endif + +ifeq ($(MSB_DEV),true) + KERNEL_CFLAGS += CONFIG_MSB_DEV=y + KBUILD_OPTIONS += CONFIG_MSB_DEV=y +endif + +ifeq ($(SWITCHEDCAP_DUMP),true) + KERNEL_CFLAGS += CONFIG_SWITCHEDCAP_DUMP=y + KBUILD_OPTIONS += CONFIG_SWITCHEDCAP_DUMP=y +endif + +ifeq ($(SKIP_QTI_CHARGER_CONFIRMAION),true) + KERNEL_CFLAGS += CONFIG_SKIP_QTI_CHARGER_CONFIRMAION=y + KBUILD_OPTIONS += CONFIG_SKIP_QTI_CHARGER_CONFIRMAION=y +endif + +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/qti_glink_charger/Kbuild b/drivers/power/qti_glink_charger/Kbuild new file mode 100755 index 000000000000..e3327b9ffc4f --- /dev/null +++ b/drivers/power/qti_glink_charger/Kbuild @@ -0,0 +1,29 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/mmi_charger + +ifneq ($(filter m y,$(CONFIG_WIRELESS_CPS4035B)),) + EXTRA_CFLAGS += -DWIRELESS_CPS4035B +endif + +ifneq ($(filter m y,$(CONFIG_WIRELESS_CPS4019)),) + EXTRA_CFLAGS += -DWIRELESS_CPS4019 +endif + +ifneq ($(filter m y,$(CONFIG_MSB_DEV)),) + EXTRA_CFLAGS += -DMSB_DEV +endif + +ifneq ($(filter m y,$(CONFIG_SWITCHEDCAP_DUMP)),) + EXTRA_CFLAGS += -DSWITCHEDCAP_DUMP +endif + +ifneq ($(filter m y,$(CONFIG_SKIP_QTI_CHARGER_CONFIRMAION)),) + EXTRA_CFLAGS += -DSKIP_QTI_CHARGER_CONFIRMAION +endif + +obj-m += qti_glink_charger.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../bm_adsp_ulog/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/qti_glink_charger/Makefile b/drivers/power/qti_glink_charger/Makefile new file mode 100755 index 000000000000..8a469b378c75 --- /dev/null +++ b/drivers/power/qti_glink_charger/Makefile @@ -0,0 +1,17 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../bm_adsp_ulog/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/qti_glink_charger/qti_glink_charger.c b/drivers/power/qti_glink_charger/qti_glink_charger.c new file mode 100644 index 000000000000..ef2adbae0788 --- /dev/null +++ b/drivers/power/qti_glink_charger/qti_glink_charger.c @@ -0,0 +1,3135 @@ +/* + * Copyright (C) 2020-2021 Motorola Mobility LLC + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mmi_charger.h" +#include "qti_glink_charger.h" + +/* PPM specific definitions */ +#define MSG_OWNER_OEM 32782 +#define MSG_TYPE_REQ_RESP 1 +#define MSG_TYPE_NOTIFY 2 +#define OEM_PROPERTY_DATA_SIZE 16 + +#define OEM_READ_BUF_REQ 0x10000 +#define OEM_WRITE_BUF_REQ 0x10001 +#define OEM_NOTIFY_IND 0x10002 + +#define OEM_WAIT_TIME_MS 5000 + +#define BATT_DEFAULT_ID 107000 +#define BATT_SN_UNKNOWN "unknown-sn" + +#define OEM_BM_ULOG_SIZE 4096 + +#define VBUS_MIN_MV 4000 + +#define FOD_GAIN_MAX_LEN 16 +#define FOD_CURR_MAX_LEN 7 + +#define RADIO_MAX_LEN 33 + +static bool debug_enabled; +module_param(debug_enabled, bool, 0600); +MODULE_PARM_DESC(debug_enabled, "Enable debug for qti glink charger driver"); + +struct battery_info { + int batt_uv; + int batt_ua; + int batt_soc; /* 0 ~ 10000 indicating 0% to 100% */ + int batt_temp; /* hundredth degree */ + int batt_status; + int batt_full_uah; + int batt_design_uah; + int batt_chg_counter; + int batt_fv_uv; + int batt_fcc_ua; +}; + +struct charger_info { + int chrg_uv; + int chrg_ua; + int chrg_type; + int chrg_pmax_mw; + int chrg_present; + bool chrg_otg_enabled; +}; + +struct charger_profile_info { + int fg_iterm; + int chrg_iterm; + int max_fv_uv; + int max_fcc_ua; + int vfloat_comp_uv; + int demo_fv_uv; + int data_bk_size; /* number of byte for each data block */ + int data_size; /* number of byte for while data */ + int profile_id; /* profile id for charging profile selection in ADSP */ +}; + +struct lpd_info { + int lpd_present; + int lpd_rsbu1; + int lpd_rsbu2; + int lpd_cid; +}; + +struct oem_notify_ind_msg { + struct pmic_glink_hdr hdr; + u32 notification; + u32 receiver; + u32 data[MAX_OEM_NOTIFY_DATA_LEN]; +}; + +struct oem_read_buf_req_msg { + struct pmic_glink_hdr hdr; + u32 oem_property_id; + u32 data_size; +}; + +struct oem_read_buf_resp_msg { + struct pmic_glink_hdr hdr; + u32 oem_property_id; + u32 buf[OEM_PROPERTY_DATA_SIZE]; + u32 data_size; +}; + +struct oem_write_buf_req_msg { + struct pmic_glink_hdr hdr; + u32 oem_property_id; + u32 buf[OEM_PROPERTY_DATA_SIZE]; + u32 data_size; +}; + +struct oem_write_buf_resp_msg { + struct pmic_glink_hdr hdr; + u32 ret_code; +}; + + +struct fod_curr { + u32 fod_array_curr[FOD_CURR_MAX_LEN]; +}; + +struct fod_gain { + u32 fod_array_gain[FOD_GAIN_MAX_LEN]; +}; + +#if defined(WIRELESS_CPS4035B) || defined(WIRELESS_CPS4019) +struct wls_dump +{ + u32 chip_id; + u32 mtp_fw_ver; + u32 irq_status; + u16 sys_mode; + u16 op_mode; + u16 rx_fop; + u16 rx_vout_mv; + s16 rx_vrect_mv; + u16 rx_irect_ma; + u16 rx_ept; + u16 rx_ce; + u32 rx_rp; + s16 rx_dietemp; + u16 rx_neg_power; + s16 tx_iin_ma; + u16 tx_vin_mv; + u16 tx_vrect_mv; + u16 tx_det_rx_power; + u16 tx_power; + u16 tx_ept; + s16 power_loss; + u16 usb_otg; + u16 wls_boost; + u16 wls_icl_ma; + u16 wls_icl_therm_ma; +}; +#else +struct wls_dump +{ + u32 chip_id; + u32 mtp_fw_ver; + u32 irq_status; + u16 sys_mode; + u16 op_mode; + u16 rx_fop; + u16 rx_vout_mv; + u16 rx_vrect_mv; + u16 rx_irect_ma; + u16 rx_neg_power; + u16 tx_iin_ma; + u16 tx_vin_mv; + u16 tx_vrect_mv; + u16 tx_fod_I; + u16 tx_fod_II; + u16 tx_fod_rp; + u16 tx_det_rx_power; + u16 tx_power; + s16 power_loss; + u16 folio_mode; + u16 pen_status; + u16 pen_soc; + u16 pen_error; + u16 usb_otg; + u16 wls_boost; + u16 wls_icl_ma; + u16 wls_icl_therm_ma; +}; +#endif + +struct msb_dev_info +{ + u32 usb_iin; + u32 usb_vout; + u32 usb_suspend; + u32 batt_fcc; + u32 batt_fv; + u32 chg_en; + u32 chg_st; + u32 chg_fault; +}; + +struct qti_charger { + char *name; + struct device *dev; + struct pmic_glink_client *client; + struct completion read_ack; + struct completion write_ack; + struct mutex read_lock; + struct mutex write_lock; + struct oem_read_buf_resp_msg rx_buf; + atomic_t rx_valid; + struct work_struct setup_work; + struct work_struct notify_work; + struct oem_notify_ind_msg notify_msg; + atomic_t state; + u32 chrg_taper_cnt; + struct mmi_battery_info batt_info; + struct mmi_charger_info chg_info; + struct mmi_charger_cfg chg_cfg; + struct mmi_charger_constraint constraint; + struct mmi_charger_driver *driver; + struct power_supply *wls_psy; + struct power_supply *partner_charger; + u32 partner_charger_icl; + u32 *profile_data; + struct charger_profile_info profile_info; + struct lpd_info lpd_info; + void *ipc_log; + struct fod_curr rx_fod_curr; + struct fod_gain rx_fod_gain; + u32 tx_mode; + u32 folio_mode; + u32 wlc_light_ctl; + u32 wlc_fan_speed; + u32 wlc_status; + u32 wlc_tx_type; + u32 wlc_tx_power; + u32 wlc_tx_capability; + u32 wlc_tx_id; + u32 wlc_tx_sn; + bool *debug_enabled; + u32 wls_curr_max; + int rx_connected; + u32 switched_nums; + struct notifier_block wls_nb; + struct dentry *debug_root; + struct power_supply *batt_psy; +}; + +static struct qti_charger *this_chip = NULL; +static BLOCKING_NOTIFIER_HEAD(qti_chg_notifier_list); + +static int find_profile_id(struct qti_charger *chg) +{ + int i; + int rc; + int count; + int profile_id = -EINVAL; + struct profile_sn_map { + const char *id; + const char *sn; + } *map_table; + + count = of_property_count_strings(chg->dev->of_node, "profile-ids-map"); + if (count <= 0 || (count % 2)) { + mmi_err(chg, "Invalid profile-ids-map in DT, rc=%d\n", count); + return -EINVAL; + } + + map_table = devm_kmalloc_array(chg->dev, count / 2, + sizeof(struct profile_sn_map), + GFP_KERNEL); + if (!map_table) + return -ENOMEM; + + rc = of_property_read_string_array(chg->dev->of_node, "profile-ids-map", + (const char **)map_table, + count); + if (rc < 0) { + mmi_err(chg, "Failed to get profile-ids-map, rc=%d\n", rc); + profile_id = rc; + goto free_map; + } + + for (i = 0; i < count / 2 && map_table[i].sn; i++) { + mmi_info(chg, "profile_ids_map[%d]: id=%s, sn=%s\n", i, + map_table[i].id, map_table[i].sn); + if (!strcmp(map_table[i].sn, chg->batt_info.batt_sn)) + profile_id = i; + } + + if (profile_id >= 0 && profile_id < count / 2) { + i = profile_id; + profile_id = 0; + rc = kstrtou32(map_table[i].id, 0, &profile_id); + if (rc) { + mmi_err(chg, "Invalid id: %s, sn: %s\n", + map_table[i].id, + map_table[i].sn); + profile_id = rc; + } else { + mmi_info(chg, "profile id: %s(%d), sn: %s\n", + map_table[i].id, + profile_id, + map_table[i].sn); + } + } else { + mmi_warn(chg, "No matched profile id in profile-ids-map\n"); + } + +free_map: + devm_kfree(chg->dev, map_table); + + return profile_id; +} + +static int handle_oem_read_ack(struct qti_charger *chg, void *data, size_t len) +{ + if (len != sizeof(chg->rx_buf)) { + mmi_err(chg, "Incorrect received length %zu expected %lu\n", len, + sizeof(chg->rx_buf)); + atomic_set(&chg->rx_valid, 0); + return -EINVAL; + } + + memcpy(&chg->rx_buf, data, sizeof(chg->rx_buf)); + atomic_set(&chg->rx_valid, 1); + complete(&chg->read_ack); + mmi_dbg(chg, "read ack for property: %u\n", chg->rx_buf.oem_property_id); + + return 0; +} + +static int handle_oem_write_ack(struct qti_charger *chg, void *data, size_t len) +{ + struct oem_write_buf_resp_msg *msg_ptr; + + if (len != sizeof(*msg_ptr)) { + mmi_err(chg, "Incorrect received length %zu expected %lu\n", len, + sizeof(*msg_ptr)); + return -EINVAL; + } + + msg_ptr = data; + if (msg_ptr->ret_code) { + mmi_err(chg, "write ack, ret_code: %u\n", msg_ptr->ret_code); + return -EINVAL; + } + + mmi_dbg(chg, "write ack\n"); + complete(&chg->write_ack); + + return 0; +} + +static int handle_oem_notification(struct qti_charger *chg, void *data, size_t len) +{ + struct oem_notify_ind_msg *notify_msg = data; + if (len != sizeof(*notify_msg)) { + mmi_err(chg, "Incorrect received length %zu expected %lu\n", len, + sizeof(*notify_msg)); + return -EINVAL; + } + + mmi_info(chg, "notification: %#x on receiver: %#x\n", + notify_msg->notification, + notify_msg->receiver); + + pm_stay_awake(chg->dev); + memcpy(&chg->notify_msg, notify_msg, sizeof(*notify_msg)); + schedule_work(&chg->notify_work); + + return 0; +} + +static int oem_callback(void *priv, void *data, size_t len) +{ + struct pmic_glink_hdr *hdr = data; + struct qti_charger *chg = priv; + + mmi_dbg(chg, "owner: %u type: %u opcode: 0x%x len:%zu\n", hdr->owner, + hdr->type, hdr->opcode, len); + + if (hdr->opcode == OEM_READ_BUF_REQ) + handle_oem_read_ack(chg, data, len); + else if (hdr->opcode == OEM_WRITE_BUF_REQ) + handle_oem_write_ack(chg, data, len); + else if (hdr->opcode == OEM_NOTIFY_IND) + handle_oem_notification(chg, data, len); + else + mmi_err(chg, "Unknown message opcode: %d\n", hdr->opcode); + + return 0; +} + +static void oem_state_cb(void *priv, enum pmic_glink_state state) +{ + struct qti_charger *chg = priv; + + mmi_dbg(chg, "state: %d\n", state); + + atomic_set(&chg->state, state); + + switch (state) { + case PMIC_GLINK_STATE_DOWN: + case PMIC_GLINK_STATE_UP: + schedule_work(&chg->setup_work); + break; + default: + break; + } +} + +static int qti_charger_write(struct qti_charger *chg, u32 property, + const void *val, size_t val_len) +{ + struct oem_write_buf_req_msg oem_buf = { { 0 } }; + int rc; + + if (val_len > (OEM_PROPERTY_DATA_SIZE * sizeof(u32))) { + mmi_err(chg, "Incorrect data length %zu for property: %u\n", + val_len, property); + return -EINVAL; + } + + if (atomic_read(&chg->state) == PMIC_GLINK_STATE_DOWN) { + mmi_err(chg, "ADSP glink state is down\n"); + return -ENOTCONN; + } + + memset(&oem_buf, 0, sizeof(oem_buf)); + oem_buf.hdr.owner = MSG_OWNER_OEM; + oem_buf.hdr.type = MSG_TYPE_REQ_RESP; + oem_buf.hdr.opcode = OEM_WRITE_BUF_REQ; + oem_buf.oem_property_id = property; + oem_buf.data_size = val_len; + memcpy(oem_buf.buf, val, val_len); + + mutex_lock(&chg->write_lock); + reinit_completion(&chg->write_ack); + + mmi_dbg(chg, "Start data write for property: %u, len=%zu\n", + property, val_len); + + rc = pmic_glink_write(chg->client, &oem_buf, + sizeof(oem_buf)); + if (rc < 0) { + mmi_err(chg, "Error in sending message rc=%d on property: %u\n", + rc, property); + goto out; + } + + rc = wait_for_completion_timeout(&chg->write_ack, + msecs_to_jiffies(OEM_WAIT_TIME_MS)); + if (!rc) { + mmi_err(chg, "timed out on property: %u\n", property); + rc = -ETIMEDOUT; + goto out; + } else { + rc = 0; + bm_ulog_print_log(OEM_BM_ULOG_SIZE); + } +out: + mmi_dbg(chg, "Complete data write for property: %u\n", property); + mutex_unlock(&chg->write_lock); + return rc; +} + +static int qti_charger_read(struct qti_charger *chg, u32 property, + void *val, size_t val_len) +{ + struct oem_read_buf_req_msg oem_buf = { { 0 } }; + int rc; + + if (val_len > (OEM_PROPERTY_DATA_SIZE * sizeof(u32))) { + mmi_err(chg, "Incorrect data length %zu for property: %u\n", + val_len, property); + return -EINVAL; + } + + if (atomic_read(&chg->state) == PMIC_GLINK_STATE_DOWN) { + mmi_err(chg, "ADSP glink state is down\n"); + return -ENOTCONN; + } + + oem_buf.hdr.owner = MSG_OWNER_OEM; + oem_buf.hdr.type = MSG_TYPE_REQ_RESP; + oem_buf.hdr.opcode = OEM_READ_BUF_REQ; + oem_buf.oem_property_id = property; + oem_buf.data_size = val_len; + + mutex_lock(&chg->read_lock); + reinit_completion(&chg->read_ack); + + mmi_dbg(chg, "Start data read for property: %u, len=%zu\n", + property, val_len); + + rc = pmic_glink_write(chg->client, &oem_buf, + sizeof(oem_buf)); + if (rc < 0) { + mmi_err(chg, "Error in sending message rc=%d on property: %u\n", + rc, property); + goto out; + } + + rc = wait_for_completion_timeout(&chg->read_ack, + msecs_to_jiffies(OEM_WAIT_TIME_MS)); + if (!rc) { + mmi_err(chg, "timed out on property: %u\n", property); + rc = -ETIMEDOUT; + goto out; + } else { + rc = 0; + } + + if (!atomic_read(&chg->rx_valid)) { + rc = -ENODATA; + goto out; + } + + if (chg->rx_buf.data_size != val_len) { + mmi_err(chg, "Invalid data size %u, on property: %u\n", + chg->rx_buf.data_size, property); + rc = -ENODATA; + goto out; + } + + memcpy(val, chg->rx_buf.buf, val_len); + atomic_set(&chg->rx_valid, 0); +out: + mmi_dbg(chg, "Complete data read for property: %u\n", property); + mutex_unlock(&chg->read_lock); + + return rc; +} + +int qti_charger_set_property(u32 property, const void *val, size_t val_len) +{ + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + return qti_charger_write(chg, property, val, val_len); +} +EXPORT_SYMBOL(qti_charger_set_property); + +int qti_charger_get_property(u32 property, void *val, size_t val_len) +{ + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + return qti_charger_read(chg, property, val, val_len); +} +EXPORT_SYMBOL(qti_charger_get_property); + +int qti_charger_register_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_register(&qti_chg_notifier_list, nb); +} +EXPORT_SYMBOL(qti_charger_register_notifier); + +int qti_charger_unregister_notifier(struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(&qti_chg_notifier_list, nb); +} +EXPORT_SYMBOL(qti_charger_unregister_notifier); + +static int qti_charger_get_batt_info(void *data, struct mmi_battery_info *batt_info) +{ + int rc; + struct qti_charger *chg = data; + int batt_status = chg->batt_info.batt_status; + struct battery_info info; + + rc = qti_charger_read(chg, OEM_PROP_BATT_INFO, + &info, + sizeof(struct battery_info)); + if (rc) + return rc; + + if (chg->chg_cfg.full_charged) + chg->batt_info.batt_status = POWER_SUPPLY_STATUS_FULL; + + chg->batt_info.batt_ma = info.batt_ua / 1000; + chg->batt_info.batt_mv = info.batt_uv / 1000; + chg->batt_info.batt_soc = info.batt_soc / 100; + chg->batt_info.batt_temp = info.batt_temp / 100; + chg->batt_info.batt_status = info.batt_status; + chg->batt_info.batt_full_uah = info.batt_full_uah; + chg->batt_info.batt_design_uah = info.batt_design_uah; + chg->batt_info.batt_chg_counter = info.batt_chg_counter; + chg->batt_info.batt_fv_mv = info.batt_fv_uv / 1000; + chg->batt_info.batt_fcc_ma = info.batt_fcc_ua / 1000; + memcpy(batt_info, &chg->batt_info, sizeof(struct mmi_battery_info)); + + if (batt_status != chg->batt_info.batt_status) { + bm_ulog_print_log(OEM_BM_ULOG_SIZE); + } + + return rc; +} +#if defined(WIRELESS_CPS4035B) || defined(WIRELESS_CPS4019) +void qti_wireless_charge_dump_info(struct qti_charger *chg, struct wls_dump wls_info) +{ + mmi_info(chg, "Wireless dump info -1: CHIP_ID: 0x%04x, MTP_FW_VER: 0x%04x, IRQ STATUS: 0x%04x, " + "SYS_MODE: RX/TX %d, OP_MODE: BPP/EPP 0x%x, RX_FOP: %dkHz, RX_VOUT: %dmV, " + "RX_VRECT: %dmV, RX_IRECT: %dmV, RX_NEG_POWER: %dw ", + wls_info.chip_id, + wls_info.mtp_fw_ver, + wls_info.irq_status, + wls_info.sys_mode, + wls_info.op_mode, + wls_info.rx_fop, + wls_info.rx_vout_mv, + wls_info.rx_vrect_mv, + wls_info.rx_irect_ma, + wls_info.rx_neg_power); + + mmi_info(chg, "Wireless dump info -2: TX_IIN: %dmA, TX_VIN: %dmV, TX_VRECT: %dmV, " + "TX_DET_RX_POWER: %dmW, TX_POWER: %dmW, POWER_LOSS: %dmW, TX_FOD: %d, " + "RX_CONNECTED: %d, TX_EPT_RSN: 0x%04x, ", + wls_info.tx_iin_ma, + wls_info.tx_vin_mv, + wls_info.tx_vrect_mv, + wls_info.tx_det_rx_power, + wls_info.tx_power, + wls_info.power_loss, + (wls_info.irq_status & (0x01<<12)) ? 1 : 0, + chg->rx_connected, + wls_info.tx_ept); + + mmi_info(chg, "Wireless dump info -3: rx_ept: %d, rx_ce: %d, " + "rx_rp: %d, rx_dietemp: %d, USB_OTG: %d, WLS_BOOST: %d, WLS_ICL_MA: %dmA, WLS_ICL_THERM_MA: %dmA", + wls_info.rx_ept, + wls_info.rx_ce, + wls_info.rx_rp, + wls_info.rx_dietemp, + wls_info.usb_otg, + wls_info.wls_boost, + wls_info.wls_icl_ma, + wls_info.wls_icl_therm_ma); + + + mmi_info(chg, "Wireless dump info -4: WLC Stand: tx_type %d, tx_power: %d, " + "fan: %d, light: %d, status: %d", + chg->wlc_tx_type, + chg->wlc_tx_power, + chg->wlc_fan_speed, + chg->wlc_light_ctl, + chg->wlc_status); + +} +#else +void qti_wireless_charge_dump_info(struct qti_charger *chg, struct wls_dump wls_info) +{ + mmi_info(chg, "Wireless dump info -1: CHIP_ID: 0x%04x, MTP_FW_VER: 0x%04x, IRQ STATUS: 0x%04x, " + "SYS_MODE: RX/TX %d, OP_MODE: BPP/EPP 0x%x, RX_FOP: %dkHz, RX_VOUT: %dmV, " + "RX_VRECT: %dmV, RX_IRECT: %dmV, RX_NEG_POWER: %dw ", + wls_info.chip_id, + wls_info.mtp_fw_ver, + wls_info.irq_status, + wls_info.sys_mode, + wls_info.op_mode, + wls_info.rx_fop, + wls_info.rx_vout_mv, + wls_info.rx_vrect_mv, + wls_info.rx_irect_ma, + wls_info.rx_neg_power); + + mmi_info(chg, "Wireless dump info -2: TX_IIN: %dmA, TX_VIN: %dmV, TX_VRECT: %dmV, " + "TX_DET_RX_POWER: %dmW, TX_POWER: %dmW, POWER_LOSS: %dmW, TX_FOD: %d, ", + wls_info.tx_iin_ma, + wls_info.tx_vin_mv, + wls_info.tx_vrect_mv, + wls_info.tx_det_rx_power, + wls_info.tx_power, + wls_info.power_loss, + (wls_info.irq_status & (0x01<<12)) ? 1 : 0); + + mmi_info(chg, "Wireless dump info -3: FOLIO_MODE: %d, PEN_STATUS: %d, " + "PEN_SOC: %d, PEN_ERROR: %d, USB_OTG: %d, WLS_BOOST: %d, WLS_ICL_MA: %dmA, WLS_ICL_THERM_MA: %dmA", + wls_info.folio_mode, + wls_info.pen_status, + wls_info.pen_soc, + wls_info.pen_error, + wls_info.usb_otg, + wls_info.wls_boost, + wls_info.wls_icl_ma, + wls_info.wls_icl_therm_ma); +} +#endif + +#if defined(MSB_DEV) +void qti_msb_dev_info(struct qti_charger *chg, struct msb_dev_info msb_dev) +{ + mmi_info(chg, "msb dev info : usb_iin: %dma, usb_vout: %dmv, usb_suspend: %d, " + "batt_fcc: %dma, batt_fv: %dmv, chg_en: %d, chg_fault: 0x%x, chg_st: 0x%x", + msb_dev.usb_iin, + msb_dev.usb_vout, + msb_dev.usb_suspend, + msb_dev.batt_fcc, + msb_dev.batt_fv, + msb_dev.chg_en, + msb_dev.chg_fault, + msb_dev.chg_st); +} +#endif + +#if defined(SWITCHEDCAP_DUMP) +void qti_switched_dump_info(struct qti_charger *chg, struct switched_dev_info switched_info) +{ + mmi_info(chg, "switchedcap dump info [%d]: chg_en %d, work_mode 0x%x, int_stat 0x%x, " + "ibat_ma %d, ibus_ma %d, vbus_mv %d, vout_mv %d, vac_mv %d, vbat_mv %d, " + "vusb_mv %d, vwpc_mv %d, die_temp %d", + switched_info.chg_role, switched_info.chg_en, switched_info.work_mode, + switched_info.int_stat, switched_info.ibat_ma, switched_info.ibus_ma, + switched_info.vbus_mv, switched_info.vout_mv, switched_info.vac_mv, + switched_info.vbat_mv, switched_info.vusb_mv, switched_info.vwpc_mv, switched_info.die_temp); +} +#endif + +static int qti_charger_get_chg_info(void *data, struct mmi_charger_info *chg_info) +{ + int rc; + struct qti_charger *chg = data; + struct charger_info info; + struct wls_dump wls_info; +#if defined(MSB_DEV) + struct msb_dev_info msb_dev; +#endif +#if defined(SWITCHEDCAP_DUMP) + struct switched_dev_info master_switched_info; + int i = 0; +#endif + rc = qti_charger_read(chg, OEM_PROP_CHG_INFO, + &info, + sizeof(struct charger_info)); + if (rc) + return rc; + + chg->lpd_info.lpd_cid = -1; + rc = qti_charger_read(chg, OEM_PROP_LPD_INFO, + &chg->lpd_info, + sizeof(struct lpd_info)); + if (rc) { + rc = 0; + memset(&chg->lpd_info, 0, sizeof(struct lpd_info)); + chg->lpd_info.lpd_cid = -1; + } + mmi_info(chg, "LPD: present=%d, rsbu1=%d, rsbu2=%d, cid=%d\n", + chg->lpd_info.lpd_present, + chg->lpd_info.lpd_rsbu1, + chg->lpd_info.lpd_rsbu2, + chg->lpd_info.lpd_cid); + + chg->chg_info.chrg_mv = info.chrg_uv / 1000; + chg->chg_info.chrg_ma = info.chrg_ua / 1000; + chg->chg_info.chrg_type = info.chrg_type; + chg->chg_info.chrg_pmax_mw = info.chrg_pmax_mw; + chg->chg_info.chrg_present = info.chrg_present; + if (!info.chrg_present && info.chrg_type != 0) + chg->chg_info.chrg_present = 1; + + chg->chg_info.vbus_present = chg->chg_info.chrg_mv > VBUS_MIN_MV; + if (chg->wls_psy) { + union power_supply_propval val; + rc = power_supply_get_property(chg->wls_psy, + POWER_SUPPLY_PROP_ONLINE, &val); + if (!rc && val.intval) + chg->chg_info.vbus_present = false; + } + chg->chg_info.chrg_otg_enabled = info.chrg_otg_enabled; + chg->chg_info.lpd_present = chg->lpd_info.lpd_present; + memcpy(chg_info, &chg->chg_info, sizeof(struct mmi_charger_info)); + + if (chg->wls_psy){ + qti_charger_read(chg, OEM_PROP_WLS_DUMP_INFO, + &wls_info, + sizeof(struct wls_dump)); + + qti_wireless_charge_dump_info(chg, wls_info); + } + +#if defined(MSB_DEV) + qti_charger_read(chg, OEM_PROP_MSB_DEV_INFO, + &msb_dev, + sizeof(struct msb_dev_info)); + qti_msb_dev_info(chg, msb_dev); +#endif + +#if defined(SWITCHEDCAP_DUMP) + for (i = 0; i < chg->switched_nums; i++) { + qti_charger_read(chg, OEM_PROP_MASTER_SWITCHEDCAP_INFO + i, + &master_switched_info, + sizeof(struct switched_dev_info)); + qti_switched_dump_info(chg, master_switched_info); + } +#endif + + bm_ulog_print_log(OEM_BM_ULOG_SIZE); + + return rc; +} + +static int qti_charger_get_partner_icl(struct qti_charger *chg, u32 *icl) +{ + int rc; + union power_supply_propval propval; + static const char *partner_psy_name = NULL; + + if (NULL == partner_psy_name) { + rc = of_property_read_string(chg->dev->of_node, + "mmi,partner_psy_name", &partner_psy_name); + if (rc) { + mmi_err(chg, "Failed get the partner psy name"); + return rc; + } + } + + if (!chg->partner_charger) { + chg->partner_charger = power_supply_get_by_name(partner_psy_name); + if (!chg->partner_charger) + return -ENODEV; + } + + rc = power_supply_get_property(chg->partner_charger, POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &propval); + if (rc < 0) { + mmi_err(chg, "get property failed, rc=%d\n", rc); + return rc; + } + *icl = propval.intval; + return rc; +} + +static int qti_charger_config_charge(void *data, struct mmi_charger_cfg *config) +{ + int rc; + u32 value; + struct qti_charger *chg = data; + + /* configure the charger if changed */ + if (config->target_fv != chg->chg_cfg.target_fv) { + value = config->target_fv * 1000; + rc = qti_charger_write(chg, OEM_PROP_CHG_FV, + &value, + sizeof(value)); + if (!rc) + chg->chg_cfg.target_fv = config->target_fv; + } + if (config->target_fcc != chg->chg_cfg.target_fcc) { + value = config->target_fcc * 1000; + rc = qti_charger_write(chg, OEM_PROP_CHG_FCC, + &value, + sizeof(value)); + if (!rc) + chg->chg_cfg.target_fcc = config->target_fcc; + } + if (config->charger_suspend != chg->chg_cfg.charger_suspend) { + value = config->charger_suspend; + rc = qti_charger_write(chg, OEM_PROP_CHG_SUSPEND, + &value, + sizeof(value)); + if (!rc) + chg->chg_cfg.charger_suspend = config->charger_suspend; + } + if (config->charging_disable != chg->chg_cfg.charging_disable) { + value = config->charging_disable; + rc = qti_charger_write(chg, OEM_PROP_CHG_DISABLE, + &value, + sizeof(value)); + if (!rc) + chg->chg_cfg.charging_disable = config->charging_disable; + } + + if (config->taper_kickoff != chg->chg_cfg.taper_kickoff) { + chg->chg_cfg.taper_kickoff = config->taper_kickoff; + chg->chrg_taper_cnt = 0; + } + + if (config->full_charged != chg->chg_cfg.full_charged) { + chg->chg_cfg.full_charged = config->full_charged; + } + + if (config->chrg_iterm != chg->chg_cfg.chrg_iterm) { + value = config->chrg_iterm; + rc = qti_charger_write(chg, OEM_PROP_CHG_ITERM, + &value, + sizeof(value)); + if (!rc) + chg->chg_cfg.chrg_iterm = config->chrg_iterm; + } + if (config->fg_iterm != chg->chg_cfg.fg_iterm) { + value = config->fg_iterm; + rc = qti_charger_write(chg, OEM_PROP_CHG_FG_ITERM, + &value, + sizeof(value)); + if (!rc) + chg->chg_cfg.fg_iterm = config->fg_iterm; + } + + if (config->charging_reset != chg->chg_cfg.charging_reset) { + if (config->charging_reset) { + value = 1; + rc = qti_charger_write(chg, OEM_PROP_CHG_DISABLE, + &value, + sizeof(value)); + msleep(200); + value = 0; + rc = qti_charger_write(chg, OEM_PROP_CHG_DISABLE, + &value, + sizeof(value)); + } + chg->chg_cfg.charging_reset = config->charging_reset; + } + + rc = qti_charger_get_partner_icl(chg, &value); + if (!rc && chg->partner_charger_icl != value) { + rc = qti_charger_write(chg, OEM_PROP_CHG_PARTNER_ICL, + &value, + sizeof(value)); + if (!rc) + chg->partner_charger_icl = value; + } + + return 0; +} + +#define TAPER_COUNT 2 +static bool qti_charger_is_charge_tapered(void *data, int tapered_ma) +{ + bool is_tapered = false; + struct qti_charger *chg = data; + + if (abs(chg->batt_info.batt_ma) <= tapered_ma) { + if (chg->chrg_taper_cnt >= TAPER_COUNT) { + is_tapered = true; + chg->chrg_taper_cnt = 0; + } else + chg->chrg_taper_cnt++; + } else + chg->chrg_taper_cnt = 0; + + return is_tapered; +} + +static bool qti_charger_is_charge_halt(void *data) +{ + struct qti_charger *chg = data; + + if (chg->batt_info.batt_status == POWER_SUPPLY_STATUS_NOT_CHARGING || + chg->batt_info.batt_status == POWER_SUPPLY_STATUS_FULL) + return true; + + return false; +} + +static void qti_charger_set_constraint(void *data, + struct mmi_charger_constraint *constraint) +{ + int rc; + u32 value; + struct qti_charger *chg = data; + + if (constraint->demo_mode != chg->constraint.demo_mode) { + value = constraint->demo_mode; + rc = qti_charger_write(chg, OEM_PROP_DEMO_MODE, + &value, + sizeof(value)); + if (!rc) + chg->constraint.demo_mode = constraint->demo_mode; + } + + if (constraint->factory_version != chg->constraint.factory_version) { + value = constraint->factory_version; + rc = qti_charger_write(chg, OEM_PROP_FACTORY_VERSION, + &value, + sizeof(value)); + if (!rc) + chg->constraint.factory_version = constraint->factory_version; + } + + if (constraint->factory_mode != chg->constraint.factory_mode) { + value = constraint->factory_mode; + rc = qti_charger_write(chg, OEM_PROP_FACTORY_MODE, + &value, + sizeof(value)); + if (!rc) + chg->constraint.factory_mode = constraint->factory_mode; + } + + if (constraint->dcp_pmax != chg->constraint.dcp_pmax) { + value = constraint->dcp_pmax; + rc = qti_charger_write(chg, OEM_PROP_CHG_BC_PMAX, + &value, + sizeof(value)); + if (!rc) + chg->constraint.dcp_pmax = constraint->dcp_pmax; + } + + if (constraint->hvdcp_pmax != chg->constraint.hvdcp_pmax) { + value = constraint->hvdcp_pmax; + rc = qti_charger_write(chg, OEM_PROP_CHG_QC_PMAX, + &value, + sizeof(value)); + if (!rc) + chg->constraint.hvdcp_pmax = constraint->hvdcp_pmax; + } + + if (constraint->pd_pmax != chg->constraint.pd_pmax) { + value = constraint->pd_pmax; + rc = qti_charger_write(chg, OEM_PROP_CHG_PD_PMAX, + &value, + sizeof(value)); + if (!rc) + chg->constraint.pd_pmax = constraint->pd_pmax; + } + + if (chg->wls_psy){ + if (constraint->wls_pmax != chg->constraint.wls_pmax) { + value = constraint->wls_pmax; + rc = qti_charger_write(chg, OEM_PROP_CHG_WLS_PMAX, + &value, + sizeof(value)); + if (!rc) + chg->constraint.wls_pmax = constraint->wls_pmax; + } + } +} + +static int qti_charger_write_profile(struct qti_charger *chg) +{ + int rc; + char *data; + int offset; + int max_size = OEM_PROPERTY_DATA_SIZE * sizeof(u32); + + rc = qti_charger_write(chg, OEM_PROP_CHG_PROFILE_INFO, + &chg->profile_info, + sizeof(struct charger_profile_info)); + if (rc) { + mmi_err(chg, "qti charger write profile info failed, rc=%d\n", rc); + return rc; + } + + if (!chg->profile_data) + return 0; + + offset = 0; + data = (char *)chg->profile_data; + while (offset < chg->profile_info.data_size) { + if ((chg->profile_info.data_size - offset) > max_size) { + rc = qti_charger_write(chg, + OEM_PROP_CHG_PROFILE_DATA, + data + offset, + max_size); + offset += max_size; + } else { + rc = qti_charger_write(chg, + OEM_PROP_CHG_PROFILE_DATA, + data + offset, + chg->profile_info.data_size - offset); + offset += chg->profile_info.data_size - offset; + } + if (rc) { + mmi_err(chg, "qti charger write profile data failed, rc=%d\n", rc); + break; + } + } + + return rc; +} + +static ssize_t tcmd_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long mode; + struct qti_charger *chg = dev_get_drvdata(dev); + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &mode); + if (r) { + mmi_err(chg, "Invalid TCMD = %lu\n", mode); + return -EINVAL; + } + + r = qti_charger_write(chg, OEM_PROP_TCMD, + &mode, + sizeof(mode)); + + return r ? r : count; +} + +static ssize_t tcmd_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int data; + struct qti_charger *chg = dev_get_drvdata(dev); + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + qti_charger_read(chg, OEM_PROP_TCMD, + &data, + sizeof(int)); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", data); +} + + +static DEVICE_ATTR(tcmd, 0664, + tcmd_show, + tcmd_store); + +static ssize_t force_pmic_icl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long pmic_icl; + struct qti_charger *chg = dev_get_drvdata(dev); + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &pmic_icl); + if (r) { + mmi_err(chg, "Invalid TCMD = %lu\n", pmic_icl); + return -EINVAL; + } + + r = qti_charger_write(chg, OEM_PROP_PMIC_ICL, + &pmic_icl, + sizeof(pmic_icl)); + + return r ? r : count; +} + +static ssize_t force_pmic_icl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int data; + struct qti_charger *chg = dev_get_drvdata(dev); + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + qti_charger_read(chg, OEM_PROP_PMIC_ICL, + &data, + sizeof(int)); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", data); +} + + +static DEVICE_ATTR(force_pmic_icl, 0664, + force_pmic_icl_show, + force_pmic_icl_store); + +static ssize_t force_wls_en_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long wls_en; + struct qti_charger *chg = dev_get_drvdata(dev); + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &wls_en); + if (r) { + mmi_err(chg, "Invalid TCMD = %lu\n", wls_en); + return -EINVAL; + } + + r = qti_charger_write(chg, OEM_PROP_WLS_EN, + &wls_en, + sizeof(wls_en)); + + return r ? r : count; +} + +static ssize_t force_wls_en_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int data; + struct qti_charger *chg = dev_get_drvdata(dev); + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + qti_charger_read(chg, OEM_PROP_WLS_EN, + &data, + sizeof(int)); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", data); +} + +static DEVICE_ATTR(force_wls_en, 0664, + force_wls_en_show, + force_wls_en_store); + +static ssize_t force_usb_suspend_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long usb_suspend; + struct qti_charger *chg = dev_get_drvdata(dev); + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &usb_suspend); + if (r) { + mmi_err(chg, "Invalid TCMD = %lu\n", usb_suspend); + return -EINVAL; + } + + r = qti_charger_write(chg, OEM_PROP_USB_SUSPEND, + &usb_suspend, + sizeof(usb_suspend)); + + return r ? r : count; +} + +static ssize_t force_usb_suspend_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int data; + struct qti_charger *chg = dev_get_drvdata(dev); + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + qti_charger_read(chg, OEM_PROP_USB_SUSPEND, + &data, + sizeof(int)); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", data); +} + +static DEVICE_ATTR(force_usb_suspend, 0664, + force_usb_suspend_show, + force_usb_suspend_store); + +static ssize_t force_wls_volt_max_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long wls_volt_max; + struct qti_charger *chg = dev_get_drvdata(dev); + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &wls_volt_max); + if (r) { + mmi_err(chg, "Invalid TCMD = %lu\n", wls_volt_max); + return -EINVAL; + } + + r = qti_charger_write(chg, OEM_PROP_WLS_VOLT_MAX, + &wls_volt_max, + sizeof(wls_volt_max)); + + return r ? r : count; +} + +static ssize_t force_wls_volt_max_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int data; + struct qti_charger *chg = dev_get_drvdata(dev); + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + qti_charger_read(chg, OEM_PROP_WLS_VOLT_MAX, + &data, + sizeof(int)); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", data); +} + +static DEVICE_ATTR(force_wls_volt_max, 0664, + force_wls_volt_max_show, + force_wls_volt_max_store); + +static ssize_t force_wls_curr_max_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long wls_curr_max; + struct qti_charger *chg = dev_get_drvdata(dev); + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &wls_curr_max); + if (r) { + mmi_err(chg, "Invalid TCMD = %lu\n", wls_curr_max); + return -EINVAL; + } + + r = qti_charger_write(chg, OEM_PROP_WLS_CURR_MAX, + &wls_curr_max, + sizeof(wls_curr_max)); + + return r ? r : count; +} + +static ssize_t force_wls_curr_max_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int data; + struct qti_charger *chg = dev_get_drvdata(dev); + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + qti_charger_read(chg, OEM_PROP_WLS_CURR_MAX, + &data, + sizeof(int)); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", data); +} + +static DEVICE_ATTR(force_wls_curr_max, 0664, + force_wls_curr_max_show, + force_wls_curr_max_store); + +static ssize_t wireless_chip_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int data; + struct qti_charger *chg = dev_get_drvdata(dev); + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + qti_charger_read(chg, OEM_PROP_WLS_CHIP_ID, + &data, + sizeof(int)); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "0x%04x\n", data); +} + +static DEVICE_ATTR(wireless_chip_id, S_IRUGO, + wireless_chip_id_show, + NULL); + +static int fod_gain_store(struct qti_charger *chip, const char *buf, + u32 *fod_array) +{ + int i = 0, ret = 0, sum = 0; + char *buffer; + unsigned int temp; + + buffer = (char *)buf; + + for (i = 0; i < FOD_GAIN_MAX_LEN; i++) { + ret = sscanf((const char *)buffer, "%x,%s", &temp, buffer); + fod_array[i] = temp; + sum++; + if (ret != 2) + break; + } + + if (sum != FOD_GAIN_MAX_LEN) { + pr_err("QTI: fod_gain array len err %d\n", sum); + return -ENODEV; + } + + ret = qti_charger_write(chip, OEM_PROP_WLS_RX_FOD_GAIN, + fod_array, + sizeof(struct fod_gain)); + if (ret) { + mmi_err(chip, "qti charger write wls rx fod gain failed, rc=%d\n", ret); + return ret; + } + + + return sum; +} + +static ssize_t wls_fod_gain_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct qti_charger *chg = dev_get_drvdata(dev); + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + fod_gain_store(chg, buf, chg->rx_fod_gain.fod_array_gain); + return count; +} + + +static ssize_t wls_fod_gain_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int count = 0, i = 0; + struct qti_charger *chg = dev_get_drvdata(dev); + + for (i = 0; i < FOD_GAIN_MAX_LEN; i++) { + count += scnprintf(buf+count, CHG_SHOW_MAX_SIZE, + "0x%02x ", chg->rx_fod_gain.fod_array_gain[i]); + if (i == FOD_GAIN_MAX_LEN - 1) + count += scnprintf(buf+count, CHG_SHOW_MAX_SIZE, "\n"); + } + + return count; +} + +static DEVICE_ATTR(wls_fod_gain, 0664, + wls_fod_gain_show, + wls_fod_gain_store); + +static int fod_curr_store(struct qti_charger *chip, const char *buf, + u32 *fod_array) +{ + int i = 0, ret = 0, sum = 0; + char *buffer; + unsigned int temp; + + buffer = (char *)buf; + + for (i = 0; i < FOD_CURR_MAX_LEN; i++) { + ret = sscanf((const char *)buffer, "%x,%s", &temp, buffer); + fod_array[i] = temp; + sum++; + if (ret != 2) + break; + } + + if (sum != FOD_CURR_MAX_LEN) { + pr_err("QTI: fod_curr array len err %d\n", sum); + return -ENODEV; + } + + ret = qti_charger_write(chip, OEM_PROP_WLS_RX_FOD_CURR, + fod_array, + sizeof(struct fod_curr)); + if (ret) { + mmi_err(chip, "qti charger write wls rx fod curr failed, rc=%d\n", ret); + return ret; + } + + return sum; +} + +static ssize_t wls_fod_curr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct qti_charger *chg = dev_get_drvdata(dev); + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + fod_curr_store(chg, buf, chg->rx_fod_curr.fod_array_curr); + return count; +} + +static ssize_t wls_fod_curr_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int count = 0, i = 0; + struct qti_charger *chg = dev_get_drvdata(dev); + + for (i = 0; i < FOD_CURR_MAX_LEN; i++) { + count += scnprintf(buf+count, CHG_SHOW_MAX_SIZE, + "0x%02x ", chg->rx_fod_curr.fod_array_curr[i]); + if (i == FOD_CURR_MAX_LEN - 1) + count += scnprintf(buf+count, CHG_SHOW_MAX_SIZE, "\n"); + } + + return count; +} + +static DEVICE_ATTR(wls_fod_curr, 0664, + wls_fod_curr_show, + wls_fod_curr_store); + +static ssize_t addr_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + u32 addr; + struct qti_charger *chg = dev_get_drvdata(dev); + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtou32(buf, 0, &addr); + if (r) { + mmi_err(chg, "Invalid reg_address = 0x%x\n", addr); + return -EINVAL; + } + + r = qti_charger_write(chg, OEM_PROP_REG_ADDRESS, + &addr, + sizeof(addr)); + + return r ? r : count; +} + +static DEVICE_ATTR_WO(addr); + +static ssize_t data_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + u32 data; + struct qti_charger *chg = dev_get_drvdata(dev); + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtou32(buf, 0, &data); + if (r) { + mmi_err(chg, "Invalid reg_data = 0x%x\n", data); + return -EINVAL; + } + + r = qti_charger_write(chg, OEM_PROP_REG_DATA, + &data, + sizeof(data)); + + return r ? r : count; +} + +static ssize_t data_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 data; + struct qti_charger *chg = dev_get_drvdata(dev); + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + qti_charger_read(chg, OEM_PROP_REG_DATA, + &data, + sizeof(data)); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%x\n", data); +} + +static DEVICE_ATTR(data, 0664, + data_show, + data_store); + +static ssize_t tx_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long tx_mode; + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &tx_mode); + if (r) { + pr_err("Invalid tx_mode = %lu\n", tx_mode); + return -EINVAL; + } + + r = qti_charger_write(chg, OEM_PROP_WLS_TX_MODE, + &tx_mode, + sizeof(tx_mode)); + chg->tx_mode = tx_mode; + if (chg->wls_psy) + sysfs_notify(&chg->wls_psy->dev.parent->kobj, NULL, "tx_mode"); + + return r ? r : count; +} + +static ssize_t tx_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ +#ifndef SKIP_QTI_CHARGER_CONFIRMAION + u32 tx_mode = 0; +#endif + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } +#ifdef SKIP_QTI_CHARGER_CONFIRMAION + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", chg->tx_mode); +#else + qti_charger_read(chg, OEM_PROP_WLS_TX_MODE, + &tx_mode, + sizeof(tx_mode)); + + chg->tx_mode = tx_mode; + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", tx_mode); +#endif +} + +static DEVICE_ATTR(tx_mode, S_IRUGO|S_IWUSR, tx_mode_show, tx_mode_store); + +static ssize_t tx_mode_vout_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct qti_charger *chg = this_chip; + struct wls_dump wls_info; + u32 tx_vout = 0; + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + qti_charger_read(chg, OEM_PROP_WLS_DUMP_INFO, + &wls_info, + sizeof(struct wls_dump)); + + tx_vout = wls_info.tx_vrect_mv; + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", tx_vout); +} + +static DEVICE_ATTR(tx_mode_vout, S_IRUGO, + tx_mode_vout_show, + NULL); + +static ssize_t wlc_light_ctl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long wlc_light_ctl; + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &wlc_light_ctl); + if (r) { + pr_err("Invalid wlc_light_ctl = %lu\n", wlc_light_ctl); + return -EINVAL; + } + + r = qti_charger_write(chg, OEM_PROP_WLS_WLC_LIGHT_CTL, + &wlc_light_ctl, + sizeof(wlc_light_ctl)); + chg->wlc_light_ctl = wlc_light_ctl; + if (chg->wls_psy) + sysfs_notify(&chg->wls_psy->dev.parent->kobj, NULL, "wlc_light_ctl"); + + return r ? r : count; +} + +static ssize_t wlc_light_ctl_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("PEN: chip not valid\n"); + return -ENODEV; + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", chg->wlc_light_ctl); +} +static DEVICE_ATTR(wlc_light_ctl, S_IRUGO|S_IWUSR, wlc_light_ctl_show, wlc_light_ctl_store); + + +static ssize_t wlc_fan_speed_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long wlc_fan_speed; + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &wlc_fan_speed); + if (r) { + pr_err("Invalid wlc_fan_speed = %lu\n", wlc_fan_speed); + return -EINVAL; + } + + r = qti_charger_write(chg, OEM_PROP_WLS_WLC_FAN_SPEED, + &wlc_fan_speed, + sizeof(wlc_fan_speed)); + chg->wlc_fan_speed = wlc_fan_speed; + if (chg->wls_psy) + sysfs_notify(&chg->wls_psy->dev.parent->kobj, NULL, "wlc_fan_speed"); + + return r ? r : count; +} + +static ssize_t wlc_fan_speed_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("PEN: chip not valid\n"); + return -ENODEV; + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", chg->wlc_fan_speed); +} +static DEVICE_ATTR(wlc_fan_speed, S_IRUGO|S_IWUSR, wlc_fan_speed_show, wlc_fan_speed_store); + +static ssize_t wlc_tx_type_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 type = 0; + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + qti_charger_read(chg, OEM_PROP_WLS_WLC_TX_TYPE, + &type, + sizeof(type)); + + chg->wlc_tx_type = type; + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", type); +} + +static DEVICE_ATTR(wlc_tx_type, S_IRUGO, + wlc_tx_type_show, + NULL); + +static ssize_t wlc_tx_power_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 power = 0; + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + qti_charger_read(chg, OEM_PROP_WLS_WLC_TX_POWER, + &power, + sizeof(power)); + + chg->wlc_tx_power = power; + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", power); +} + +static DEVICE_ATTR(wlc_tx_power, S_IRUGO, + wlc_tx_power_show, + NULL); + +static ssize_t wlc_tx_capability_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 capability = 0; + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + qti_charger_read(chg, OEM_PROP_WLS_WLC_TX_CAPABILITY, + &capability, + sizeof(capability)); + + chg->wlc_tx_capability = capability; + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", capability); +} + +static DEVICE_ATTR(wlc_tx_capability, S_IRUGO, + wlc_tx_capability_show, + NULL); + +static ssize_t wlc_tx_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 id = 0; + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + qti_charger_read(chg, OEM_PROP_WLS_WLC_TX_ID, + &id, + sizeof(id)); + + chg->wlc_tx_id = id; + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", id); +} + +static DEVICE_ATTR(wlc_tx_id, S_IRUGO, + wlc_tx_id_show, + NULL); + +static ssize_t wlc_tx_sn_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + u32 sn = 0; + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + qti_charger_read(chg, OEM_PROP_WLS_WLC_TX_SN, + &sn, + sizeof(sn)); + + chg->wlc_tx_sn = sn; + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", sn); +} + +static DEVICE_ATTR(wlc_tx_sn, S_IRUGO, + wlc_tx_sn_show, + NULL); + +static ssize_t rx_connected_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", chg->rx_connected); +} + +static DEVICE_ATTR(rx_connected, S_IRUGO, + rx_connected_show, + NULL); + +static ssize_t wlc_st_changed_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", chg->wlc_status); +} + +static DEVICE_ATTR(wlc_st_changed, S_IRUGO, + wlc_st_changed_show, + NULL); + +static ssize_t wls_input_current_limit_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long wls_curr_max; + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &wls_curr_max); + if (r) { + mmi_err(chg, "Invalid TCMD = %lu\n", wls_curr_max); + return -EINVAL; + } + + r = qti_charger_write(chg, OEM_PROP_WLS_CURR_MAX, + &wls_curr_max, + sizeof(wls_curr_max)); + + chg->wls_curr_max = wls_curr_max; + return r ? r : count; +} + +static ssize_t wls_input_current_limit_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", chg->wls_curr_max); +} +static DEVICE_ATTR(wls_input_current_limit, S_IRUGO|S_IWUSR, wls_input_current_limit_show, wls_input_current_limit_store); + +static ssize_t folio_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long folio_mode; + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + r = kstrtoul(buf, 0, &folio_mode); + if (r) { + pr_err("Invalid folio_mode = %lu\n", folio_mode); + return -EINVAL; + } + + r = qti_charger_write(chg, OEM_PROP_WLS_FOLIO_MODE, + &folio_mode, + sizeof(folio_mode)); + chg->folio_mode = folio_mode; + + return r ? r : count; +} + +static ssize_t folio_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("PEN: chip not valid\n"); + return -ENODEV; + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", chg->folio_mode); +} +static DEVICE_ATTR(folio_mode, S_IRUGO|S_IWUSR, folio_mode_show, folio_mode_store); + +static ssize_t cid_status_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct qti_charger *chg = this_chip; + + if (!chg) { + pr_err("QTI: chip not valid\n"); + return -ENODEV; + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", chg->lpd_info.lpd_cid); +} +static DEVICE_ATTR(cid_status, S_IRUGO, cid_status_show, NULL); + +//ATTRIBUTE_GROUPS(qti_charger); +#define TX_INT_FOD (0x01<<12) +#if defined(WIRELESS_CPS4035B) || defined(WIRELESS_CPS4019) +static int show_wls_dump_info(struct seq_file *m, void *data) +{ + struct qti_charger *chip = m->private; + struct wls_dump wls_info; + + qti_charger_read(chip, OEM_PROP_WLS_DUMP_INFO, + &wls_info, + sizeof(struct wls_dump)); + + seq_printf(m, "CHIP_ID: 0x%04x\n", wls_info.chip_id); + + seq_printf(m, "MTP_FW_VER: 0x%04x\n", wls_info.mtp_fw_ver); + + seq_printf(m, "IRQ STATUS: 0x%04x\n", wls_info.irq_status); + + seq_printf(m, "SYS_MODE: RX/TX %d\n", wls_info.sys_mode); + + seq_printf(m, "OP_MODE: BPP/EPP/Moto50W 0x%x\n", wls_info.op_mode); + + seq_printf(m, "RX_FOP: %dkHz\n", wls_info.rx_fop); + + seq_printf(m, "RX_VOUT: %dmV\n", wls_info.rx_vout_mv); + + seq_printf(m, "RX_VRECT: %dmV\n", wls_info.rx_vrect_mv); + + seq_printf(m, "RX_IRECT: %dmA\n", wls_info.rx_irect_ma); + + seq_printf(m, "RX_EPT: 0x%04x\n", wls_info.rx_ept); + + seq_printf(m, "RX_CE: %d\n", wls_info.rx_ce); + + seq_printf(m, "RX_RP: %d\n", wls_info.rx_rp); + + seq_printf(m, "RX_DieTemp: %dC\n", wls_info.rx_dietemp); + + seq_printf(m, "RX_NEG_POWER: %dw\n", wls_info.rx_neg_power); + + seq_printf(m, "TX_IIN: %dmA\n", wls_info.tx_iin_ma); + + seq_printf(m, "TX_VIN: %dmV\n", wls_info.tx_vin_mv); + + seq_printf(m, "TX_VRECT: %dmV\n", wls_info.tx_vrect_mv); + + seq_printf(m, "TX_DET_RX_POWER: %dmW\n", wls_info.tx_det_rx_power); + + seq_printf(m, "TX_POWER: %dmW\n", wls_info.tx_power); + + seq_printf(m, "TX_EPT_RSN: 0x%04x\n", wls_info.tx_ept); + + seq_printf(m, "POWER_LOSS: %dmW\n", wls_info.power_loss); + + seq_printf(m, "TX_FOD: %d\n", (wls_info.irq_status & TX_INT_FOD) ? 1 : 0); + + seq_printf(m, "USB_OTG: %d\n", wls_info.usb_otg); + + seq_printf(m, "WLS_BOOST: %d\n", wls_info.wls_boost); + + seq_printf(m, "WLS_ICL_MA: %d\n", wls_info.wls_icl_ma); + + seq_printf(m, "WLS_ICL_THERM_MA: %d\n", wls_info.wls_icl_therm_ma); + + return 0; +} +#else +static int show_wls_dump_info(struct seq_file *m, void *data) +{ + struct qti_charger *chip = m->private; + struct wls_dump wls_info; + + qti_charger_read(chip, OEM_PROP_WLS_DUMP_INFO, + &wls_info, + sizeof(struct wls_dump)); + + seq_printf(m, "CHIP_ID: 0x%04x\n", wls_info.chip_id); + + seq_printf(m, "MTP_FW_VER: 0x%04x\n", wls_info.mtp_fw_ver); + + seq_printf(m, "IRQ STATUS: 0x%04x\n", wls_info.irq_status); + + seq_printf(m, "SYS_MODE: RX/TX %d\n", wls_info.sys_mode); + + seq_printf(m, "OP_MODE: BPP/EPP 0x%x\n", wls_info.op_mode); + + seq_printf(m, "RX_FOP: %dkHz\n", wls_info.rx_fop); + + seq_printf(m, "RX_VOUT: %dmV\n", wls_info.rx_vout_mv); + + seq_printf(m, "RX_VRECT: %dmV\n", wls_info.rx_vrect_mv); + + seq_printf(m, "RX_IRECT: %dmV\n", wls_info.rx_irect_ma); + + seq_printf(m, "RX_NEG_POWER: %dw\n", wls_info.rx_neg_power); + + seq_printf(m, "TX_IIN: %dmA\n", wls_info.tx_iin_ma); + + seq_printf(m, "TX_VIN: %dmV\n", wls_info.tx_vin_mv); + + seq_printf(m, "TX_VRECT: %dmV\n", wls_info.tx_vrect_mv); + + seq_printf(m, "TX_FOD_I: %d\n", wls_info.tx_fod_I); + + seq_printf(m, "TX_FOD_II: %d\n", wls_info.tx_fod_II); + + seq_printf(m, "TX_FOD_RP: %d\n", wls_info.tx_fod_rp); + + seq_printf(m, "TX_DET_RX_POWER: %dmW\n", wls_info.tx_det_rx_power); + + seq_printf(m, "TX_POWER: %dmW\n", wls_info.tx_power); + + seq_printf(m, "POWER_LOSS: %dmW\n", wls_info.power_loss); + + seq_printf(m, "TX_FOD: %d\n", (wls_info.irq_status & TX_INT_FOD) ? 1 : 0); + + seq_printf(m, "FOLIO_MODE: %d\n", wls_info.folio_mode); + + seq_printf(m, "PEN_STATUS: %d\n", wls_info.pen_status); + + seq_printf(m, "PEN_SOC: %d\n", wls_info.pen_soc); + + seq_printf(m, "PEN_ERROR: %d\n", wls_info.pen_error); + + seq_printf(m, "USB_OTG: %d\n", wls_info.usb_otg); + + seq_printf(m, "WLS_BOOST: %d\n", wls_info.wls_boost); + + seq_printf(m, "WLS_ICL_MA: %d\n", wls_info.wls_icl_ma); + + seq_printf(m, "WLS_ICL_THERM_MA: %d\n", wls_info.wls_icl_therm_ma); + + return 0; +} +#endif + +static int wls_dump_info_debugfs_open(struct inode *inode, struct file *file) +{ + struct qti_charger *chip = inode->i_private; + + return single_open(file, show_wls_dump_info, chip); +} + +static const struct file_operations wls_dump_info_debugfs_ops = { + .owner = THIS_MODULE, + .open = wls_dump_info_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void create_debugfs_entries(struct qti_charger *chip) +{ + struct dentry *ent; + + chip->debug_root = debugfs_create_dir("qti_glink_charger", NULL); + if (!chip->debug_root) { + mmi_err(chip, "Couldn't create debug dir\n"); + return; + } + + ent = debugfs_create_file("wls_dump_info", S_IFREG | S_IRUGO, + chip->debug_root, chip, + &wls_dump_info_debugfs_ops); + if (!ent) + mmi_err(chip, "Couldn't create wls_dump_info debug file\n"); +} + + +static int wireless_charger_notify_callback(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct qti_charger_notify_data *notify_data = data; + struct qti_charger *chg = container_of(nb, struct qti_charger, wls_nb); + + if (notify_data->receiver != OEM_NOTIFY_RECEIVER_WLS_CHG) { + pr_err("Skip mis-matched receiver: %#x\n", notify_data->receiver); + return 0; + } + + switch (event) { + case NOTIFY_EVENT_WLS_RX_CONNECTED: + /* RX connected update */ + if (notify_data->data[0] != chg->rx_connected) { + if (chg->wls_psy) { + pr_info("report rx_connected %d\n", notify_data->data[0]); + sysfs_notify(&chg->wls_psy->dev.parent->kobj, NULL, "rx_connected"); + } + } + chg->rx_connected = notify_data->data[0]; + break; + case NOTIFY_EVENT_WLS_RX_OVERTEMP: + break; + case NOTIFY_EVENT_WLS_CHANGE: + if (notify_data->data[0] != chg->tx_mode) { + if (chg->wls_psy) { + pr_info("report tx_mode %d\n", notify_data->data[0]); + sysfs_notify(&chg->wls_psy->dev.parent->kobj, NULL, "tx_mode"); + } + } + break; + case NOTIFY_EVENT_WLS_WLC_CHANGE: + /* WLC status update */ + if (notify_data->data[0] != chg->wlc_status) { + chg->wlc_status = notify_data->data[0]; + if (chg->wls_psy) { + pr_info("report wlc_st_changed %d\n", notify_data->data[0]); + sysfs_notify(&chg->wls_psy->dev.parent->kobj, NULL, "wlc_st_changed"); + } + } + break; + default: + pr_err("Unknown wireless event: %#lx\n", event); + break; + } + + if (chg->wls_psy) { + pr_info("wireless charger notify, event %lu\n", event); + power_supply_changed(chg->wls_psy); + } + + return 0; +} + +static void wireless_psy_init(struct qti_charger *chg) +{ + int rc; + + if (chg->wls_psy) + return; + + chg->wls_psy = power_supply_get_by_name("wireless"); + if (!chg->wls_psy) { + pr_err("No pen power supply found\n"); + return; + } + pr_info("wireless power supply is found\n"); + + rc = device_create_file(chg->wls_psy->dev.parent, + &dev_attr_tx_mode); + if (rc) + pr_err("couldn't create wireless tx mode\n"); + + rc = device_create_file(chg->wls_psy->dev.parent, + &dev_attr_tx_mode_vout); + if (rc) + pr_err("couldn't create wireless tx mode vout\n"); + + rc = device_create_file(chg->wls_psy->dev.parent, + &dev_attr_rx_connected); + if (rc) + pr_err("couldn't create wireless rx_connected\n"); + + rc = device_create_file(chg->wls_psy->dev.parent, + &dev_attr_wls_input_current_limit); + if (rc) + pr_err("couldn't create wireless input current limit error\n"); + + rc = device_create_file(chg->wls_psy->dev.parent, + &dev_attr_folio_mode); + if (rc) + pr_err("couldn't create wireless folio mode error\n"); + + rc = device_create_file(chg->wls_psy->dev.parent, + &dev_attr_wlc_light_ctl); + if (rc) + pr_err("couldn't create wireless wlc light control error\n"); + + rc = device_create_file(chg->wls_psy->dev.parent, + &dev_attr_wlc_fan_speed); + if (rc) + pr_err("couldn't create wireless wlc fan speed error\n"); + + rc = device_create_file(chg->wls_psy->dev.parent, + &dev_attr_wlc_tx_type); + if (rc) + pr_err("couldn't create wireless wlc tx type error\n"); + + rc = device_create_file(chg->wls_psy->dev.parent, + &dev_attr_wlc_tx_power); + if (rc) + pr_err("couldn't create wireless wlc tx power capacity error\n"); + + rc = device_create_file(chg->wls_psy->dev.parent, + &dev_attr_wlc_tx_capability); + if (rc) + pr_err("couldn't create wireless wlc tx capability error\n"); + + rc = device_create_file(chg->wls_psy->dev.parent, + &dev_attr_wlc_tx_id); + if (rc) + pr_err("couldn't create wireless wlc tx id error\n"); + + rc = device_create_file(chg->wls_psy->dev.parent, + &dev_attr_wlc_tx_sn); + if (rc) + pr_err("couldn't create wireless wlc tx sn error\n"); + + rc = device_create_file(chg->wls_psy->dev.parent, + &dev_attr_wlc_st_changed); + if (rc) + pr_err("couldn't create wireless wlc status changed error\n"); + + chg->wls_nb.notifier_call = wireless_charger_notify_callback; + rc = qti_charger_register_notifier(&chg->wls_nb); + if (rc) + pr_err("Failed to register notifier, rc=%d\n", rc); +} + +static void wireless_psy_deinit(struct qti_charger *chg) +{ + if (!chg->wls_psy) + return; + + device_remove_file(chg->wls_psy->dev.parent, + &dev_attr_tx_mode); + + device_remove_file(chg->wls_psy->dev.parent, + &dev_attr_tx_mode_vout); + + device_remove_file(chg->wls_psy->dev.parent, + &dev_attr_rx_connected); + + device_remove_file(chg->wls_psy->dev.parent, + &dev_attr_wls_input_current_limit); + + device_remove_file(chg->wls_psy->dev.parent, + &dev_attr_folio_mode); + + device_remove_file(chg->wls_psy->dev.parent, + &dev_attr_wlc_light_ctl); + + device_remove_file(chg->wls_psy->dev.parent, + &dev_attr_wlc_fan_speed); + + device_remove_file(chg->wls_psy->dev.parent, + &dev_attr_wlc_tx_type); + + device_remove_file(chg->wls_psy->dev.parent, + &dev_attr_wlc_tx_power); + + device_remove_file(chg->wls_psy->dev.parent, + &dev_attr_wlc_tx_capability); + + device_remove_file(chg->wls_psy->dev.parent, + &dev_attr_wlc_tx_id); + + device_remove_file(chg->wls_psy->dev.parent, + &dev_attr_wlc_tx_sn); + + device_remove_file(chg->wls_psy->dev.parent, + &dev_attr_wlc_st_changed); + + qti_charger_unregister_notifier(&chg->wls_nb); + + power_supply_put(chg->wls_psy); + chg->wls_psy = NULL; +} + +/* Battery presence detection threshold on battery temperature */ +#define BPD_TEMP_THRE -3000 +static int battery_psy_get_prop(struct power_supply *psy, + enum power_supply_property prop, + union power_supply_propval *pval) +{ + int rc; + struct battery_info info = {0}; + struct qti_charger *chg = power_supply_get_drvdata(psy); + + pval->intval = -ENODATA; + + rc = qti_charger_read(chg, OEM_PROP_BATT_INFO, &info, + sizeof(struct battery_info)); + if (rc) + return rc; + + switch (prop) { + case POWER_SUPPLY_PROP_STATUS: + pval->intval = info.batt_status; + break; + case POWER_SUPPLY_PROP_PRESENT: + pval->intval = info.batt_temp > BPD_TEMP_THRE? 1 : 0; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + pval->intval = info.batt_uv; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + pval->intval = info.batt_ua; + break; + case POWER_SUPPLY_PROP_CAPACITY: + pval->intval = info.batt_soc / 100; + break; + case POWER_SUPPLY_PROP_TEMP: + pval->intval = info.batt_temp / 10; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + pval->intval = info.batt_full_uah; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + pval->intval = info.batt_design_uah; + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + pval->intval = info.batt_chg_counter; + break; + default: + break; + } + + return rc; +} + +static int battery_psy_set_prop(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *pval) +{ + struct qti_charger *chg = power_supply_get_drvdata(psy); + + switch (prop) { + default: + mmi_err(chg, "Not supported property: %d\n", prop); + return -EINVAL; + } + + return 0; +} + +static enum power_supply_property battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_COUNTER, +}; + +static const struct power_supply_desc batt_psy_desc = { + .name = "main_battery", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = battery_props, + .num_properties = ARRAY_SIZE(battery_props), + .get_property = battery_psy_get_prop, + .set_property = battery_psy_set_prop, +}; + +static int mmi_get_bootarg_dt(char *key, char **value, char *prop, char *spl_flag) +{ + const char *bootargs_tmp = NULL; + char *idx = NULL; + char *kvpair = NULL; + int err = 1; + struct device_node *n = of_find_node_by_path("/chosen"); + size_t bootargs_tmp_len = 0; + char *bootargs_str = NULL; + + if (n == NULL) + goto err; + + if (of_property_read_string(n, prop, &bootargs_tmp) != 0) + goto putnode; + + bootargs_tmp_len = strlen(bootargs_tmp); + if (!bootargs_str) { + /* The following operations need a non-const + * version of bootargs + */ + bootargs_str = kzalloc(bootargs_tmp_len + 1, GFP_KERNEL); + if (!bootargs_str) + goto putnode; + } + strlcpy(bootargs_str, bootargs_tmp, bootargs_tmp_len + 1); + + idx = strnstr(bootargs_str, key, strlen(bootargs_str)); + if (idx) { + kvpair = strsep(&idx, " "); + if (kvpair) + if (strsep(&kvpair, "=")) { + *value = strsep(&kvpair, spl_flag); + if (*value) + err = 0; + } + } + +putnode: + of_node_put(n); +err: + return err; +} + +static int mmi_get_bootarg(char *key, char **value) +{ +#ifdef CONFIG_BOOT_CONFIG + return mmi_get_bootarg_dt(key, value, "mmi,bootconfig", "\n"); +#else + return mmi_get_bootarg_dt(key, value, "bootargs", " "); +#endif +} + +static int mmi_get_sku_type(struct qti_charger *chg, u8 *sku_type) +{ + char *s = NULL; + char androidboot_radio_str[RADIO_MAX_LEN]; + + if (mmi_get_bootarg("androidboot.radio=", &s) == 0) { + if (s != NULL) { + strlcpy(androidboot_radio_str, s, RADIO_MAX_LEN); + if (!strncmp("PRC", androidboot_radio_str, 3)) { + *sku_type = MMI_CHARGER_SKU_PRC; + } else if (!strncmp("ROW", androidboot_radio_str, 3)) { + *sku_type = MMI_CHARGER_SKU_ROW; + } else if (!strncmp("NA", androidboot_radio_str, 2)) { + *sku_type = MMI_CHARGER_SKU_NA; + } else if (!strncmp("VZW", androidboot_radio_str, 3)) { + *sku_type = MMI_CHARGER_SKU_VZW; + } else if (!strncmp("JPN", androidboot_radio_str, 3)) { + *sku_type = MMI_CHARGER_SKU_JPN; + } else if (!strncmp("ITA", androidboot_radio_str, 3)) { + *sku_type = MMI_CHARGER_SKU_ITA; + } else if (!strncmp("NAE", androidboot_radio_str, 3)) { + *sku_type = MMI_CHARGER_SKU_NAE; + } else if (!strncmp("SUPERSET", androidboot_radio_str, 8)) { + *sku_type = MMI_CHARGER_SKU_SUPERSET; + } else { + *sku_type = 0; + } + mmi_info(chg, "SKU type: %s, 0x%02x\n", androidboot_radio_str, *sku_type); + return 0; + } else { + mmi_err(chg, "Could not get SKU type\n"); + return -1; + } + } else { + mmi_err(chg, "Could not get radio bootarg\n"); + return -1; + } +} + +static int mmi_get_hw_revision(struct qti_charger *chg, u16 *hw_rev) +{ + char *s = NULL; + char androidboot_hwrev_str[RADIO_MAX_LEN]; + int ret; + + if (mmi_get_bootarg("androidboot.hwrev=", &s) == 0) { + if (s != NULL) { + strlcpy(androidboot_hwrev_str, s, RADIO_MAX_LEN); + ret = kstrtou16(androidboot_hwrev_str, 16, hw_rev); + if (ret < 0) { + mmi_info(chg, "kstrtou16 error: %d \n", ret); + return -1; + } + mmi_info(chg, "HW revision: 0x%x\n", *hw_rev); + return 0; + } else { + mmi_err(chg, "Could not get HW revision\n"); + return -1; + } + } else { + mmi_err(chg, "Could not get hwrev bootarg\n"); + return -1; + } +} + +static int qti_charger_init(struct qti_charger *chg) +{ + int rc; + u32 value; + struct mmi_charger_driver *driver; + u8 sku_type = 0; + u16 hw_rev = 0; + + if (chg->driver) { + mmi_warn(chg, "qti charger has already inited\n"); + return 0; + } + + value = mmi_is_factory_mode(); + rc = qti_charger_write(chg, OEM_PROP_FACTORY_MODE, + &value, + sizeof(value)); + if (rc) { + mmi_err(chg, "qti charger set factory mode failed, rc=%d\n", rc); + return rc; + } + chg->constraint.factory_mode = value; + + value = mmi_is_factory_version(); + rc = qti_charger_write(chg, OEM_PROP_FACTORY_VERSION, + &value, + sizeof(value)); + if (rc) { + mmi_err(chg, "qti charger set factory ver failed, rc=%d\n", rc); + return rc; + } + chg->constraint.factory_version = value; + + //set SKU type + if ((rc = mmi_get_sku_type(chg, &sku_type)) == 0) { + rc = qti_charger_write(chg, OEM_PROP_SKU_TYPE, + &sku_type, + sizeof(sku_type)); + if (rc) { + mmi_err(chg, "qti charger set SKU type failed, rc=%d\n", rc); + } + } else { + mmi_err(chg, "Fail to get sku type\n"); + return rc; + } + //set HW revision + if ((rc = mmi_get_hw_revision(chg, &hw_rev)) == 0) { + rc = qti_charger_write(chg, OEM_PROP_HW_REVISION, + &hw_rev, + sizeof(hw_rev)); + if (rc) { + mmi_err(chg, "qti charger set HW revision failed, rc=%d\n", rc); + } + } else { + mmi_err(chg, "Fail to get HW revision\n"); + return rc; + } + + rc = qti_charger_write_profile(chg); + if (rc) { + mmi_err(chg, "qti charger set profile data failed, rc=%d\n", rc); + return rc; + } + + if (of_property_read_bool(chg->dev->of_node, + "mmi,main-battery-enabled")) { + struct power_supply_config psy_cfg = {}; + psy_cfg.drv_data = chg; + psy_cfg.of_node = chg->dev->of_node; + chg->batt_psy = devm_power_supply_register(chg->dev, + &batt_psy_desc, + &psy_cfg); + if (IS_ERR(chg->batt_psy)) { + rc = PTR_ERR(chg->batt_psy); + chg->batt_psy = NULL; + mmi_err(chg, "Failed to register main psy, rc=%d\n", rc); + return rc; + } + } + + driver = devm_kzalloc(chg->dev, + sizeof(struct mmi_charger_driver), + GFP_KERNEL); + if (!driver) + return -ENOMEM; + + /* init driver */ + driver->name = chg->name; + driver->dev = chg->dev; + driver->data = chg; + driver->get_batt_info = qti_charger_get_batt_info; + driver->get_chg_info = qti_charger_get_chg_info; + driver->config_charge = qti_charger_config_charge; + driver->is_charge_tapered = qti_charger_is_charge_tapered; + driver->is_charge_halt = qti_charger_is_charge_halt; + driver->set_constraint = qti_charger_set_constraint; + chg->driver = driver; + + /* register driver to mmi charger */ + rc = mmi_register_charger_driver(driver); + if (rc) { + mmi_err(chg, "qti charger init failed, rc=%d\n", rc); + } else { + mmi_info(chg, "qti charger init successfully\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_tcmd); + if (rc) { + mmi_err(chg, + "Couldn't create tcmd\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_force_pmic_icl); + if (rc) { + mmi_err(chg, + "Couldn't create force_pmic_icl\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_force_wls_en); + if (rc) { + mmi_err(chg, + "Couldn't create force_wls_en\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_force_usb_suspend); + if (rc) { + mmi_err(chg, + "Couldn't create force_usb_suspend\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_force_wls_volt_max); + if (rc) { + mmi_err(chg, + "Couldn't create force_wls_volt_max\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_force_wls_curr_max); + if (rc) { + mmi_err(chg, + "Couldn't create force_wls_curr_max\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_wireless_chip_id); + if (rc) { + mmi_err(chg, + "Couldn't create wireless_chip_id\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_addr); + if (rc) { + mmi_err(chg, + "Couldn't create addr\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_data); + if (rc) { + mmi_err(chg, + "Couldn't create data\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_wls_fod_curr); + if (rc) { + mmi_err(chg, + "Couldn't create wls_fod_curr\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_wls_fod_gain); + if (rc) { + mmi_err(chg, + "Couldn't create wls_fod_gain\n"); + } + + rc = device_create_file(chg->dev, + &dev_attr_cid_status); + if (rc) { + mmi_err(chg, + "Couldn't create cid_status\n"); + } + + bm_ulog_print_mask_log(BM_ALL, BM_LOG_LEVEL_INFO, OEM_BM_ULOG_SIZE); + + wireless_psy_init(chg); + + create_debugfs_entries(chg); + return 0; +} + +bool qti_charger_reset_chargepump(struct qti_charger *chg) +{ + unsigned long reset = 0x1; + int rc ; + + if(!chg) { + pr_err("QTI: chip not valid"); + return false; + } + + rc = qti_charger_write(chg, OEM_PROP_MASTER_SWITCHEDCAP_RESET, &reset, sizeof(reset)); + if(rc) { + mmi_err(chg, "qit charger reset charger pump register fail:%d\n", rc); + } + + return true; +} + +static void qti_charger_shutdown(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct qti_charger *chg= dev_get_drvdata(dev); + + //reset charger pump register before shutdown. + qti_charger_reset_chargepump(chg); + mmi_info(chg, "qti_charger_shutdown\n"); + + return; +} + +static void qti_charger_deinit(struct qti_charger *chg) +{ + int rc; + + if (!chg->driver) { + mmi_info(chg, "qti charger has not inited yet\n"); + return; + } + + device_remove_file(chg->dev, &dev_attr_cid_status); + device_remove_file(chg->dev, &dev_attr_tcmd); + device_remove_file(chg->dev, &dev_attr_force_pmic_icl); + device_remove_file(chg->dev, &dev_attr_force_wls_en); + device_remove_file(chg->dev, &dev_attr_force_usb_suspend); + device_remove_file(chg->dev, &dev_attr_force_wls_volt_max); + device_remove_file(chg->dev, &dev_attr_force_wls_curr_max); + device_remove_file(chg->dev, &dev_attr_wireless_chip_id); + device_remove_file(chg->dev, &dev_attr_wls_fod_curr); + device_remove_file(chg->dev, &dev_attr_wls_fod_gain); + device_remove_file(chg->dev, &dev_attr_addr); + device_remove_file(chg->dev, &dev_attr_data); + + wireless_psy_deinit(chg); + + if (chg->debug_root) + debugfs_remove_recursive(chg->debug_root); + + /* unregister driver from mmi charger */ + rc = mmi_unregister_charger_driver(chg->driver); + if (rc) { + mmi_err(chg, "qti charger deinit failed, rc=%d\n", rc); + } else { + devm_kfree(chg->dev, chg->driver); + chg->driver = NULL; + } +} + +static void qti_charger_setup_work(struct work_struct *work) +{ + struct qti_charger *chg = container_of(work, + struct qti_charger, setup_work); + enum pmic_glink_state state; + + state = atomic_read(&chg->state); + if (state == PMIC_GLINK_STATE_UP) { + mmi_info(chg, "ADSP glink state is up\n"); + qti_charger_init(chg); + } else if (state == PMIC_GLINK_STATE_DOWN) { + mmi_err(chg, "ADSP glink state is down\n"); + memset(&chg->chg_cfg, 0, sizeof(struct mmi_charger_cfg)); + memset(&chg->constraint, 0, sizeof(struct mmi_charger_constraint)); + } +} + +static void qti_charger_notify_work(struct work_struct *work) +{ + unsigned long notification; + struct qti_charger_notify_data notify_data; + struct qti_charger *chg = container_of(work, + struct qti_charger, notify_work); + + notification = chg->notify_msg.notification; + notify_data.receiver = chg->notify_msg.receiver; + memcpy(notify_data.data, chg->notify_msg.data, + sizeof(u32) * MAX_OEM_NOTIFY_DATA_LEN); + blocking_notifier_call_chain(&qti_chg_notifier_list, + notification, + ¬ify_data); + pm_relax(chg->dev); +} + +static int qti_charger_parse_dt(struct qti_charger *chg) +{ + int rc; + int i, j; + int n = 0; + int bk_num; + int bk_size; + char bk_buf[128]; + int byte_len; + const char *df_sn = NULL, *dev_sn = NULL; + struct device_node *node; + + node = chg->dev->of_node; + dev_sn = mmi_get_battery_serialnumber(); + if (!dev_sn) { + rc = of_property_read_string(node, "mmi,df-serialnum", + &df_sn); + if (!rc && df_sn) { + mmi_info(chg, "Default Serial Number %s\n", df_sn); + } else { + mmi_err(chg, "No Default Serial Number defined\n"); + df_sn = BATT_SN_UNKNOWN; + } + strcpy(chg->batt_info.batt_sn, df_sn); + } else { + strcpy(chg->batt_info.batt_sn, dev_sn); + } + + chg->profile_info.profile_id = find_profile_id(chg); + if (chg->profile_info.profile_id < 0) + chg->profile_info.profile_id = BATT_DEFAULT_ID; + + rc = of_property_read_u32(node, "mmi,chrg-iterm-ma", + &chg->profile_info.chrg_iterm); + if (rc) { + chg->profile_info.chrg_iterm = 300000; + } else { + chg->profile_info.chrg_iterm *= 1000; + } + + rc = of_property_read_u32(node, "mmi,fg-iterm-ma", + &chg->profile_info.fg_iterm); + if (rc) { + chg->profile_info.fg_iterm = + chg->profile_info.chrg_iterm + 50000; + } else { + chg->profile_info.fg_iterm *= 1000; + } + + rc = of_property_read_u32(node, "mmi,vfloat-comp-uv", + &chg->profile_info.vfloat_comp_uv); + if (rc) + chg->profile_info.vfloat_comp_uv = 0; + + rc = of_property_read_u32(node, "mmi,max-fv-mv", + &chg->profile_info.max_fv_uv); + if (rc) + chg->profile_info.max_fv_uv = 4400; + chg->profile_info.max_fv_uv *= 1000; + + rc = of_property_read_u32(node, "mmi,max-fcc-ma", + &chg->profile_info.max_fcc_ua); + if (rc) + chg->profile_info.max_fcc_ua = 4000; + chg->profile_info.max_fcc_ua *= 1000; + + rc = of_property_read_u32(node, "mmi,demo-fv-mv", + &chg->profile_info.demo_fv_uv); + if (rc) + chg->profile_info.demo_fv_uv = 4000; + chg->profile_info.demo_fv_uv *= 1000; + + rc = of_property_read_u32(node, "mmi,profile-data-block-size", + &chg->profile_info.data_bk_size); + if (rc) + chg->profile_info.data_bk_size = 4; + chg->profile_info.data_bk_size *= 4; + + chg->profile_info.data_size = 0; + if (of_find_property(node, "mmi,profile-data", &byte_len)) { + if (byte_len % chg->profile_info.data_bk_size) { + mmi_err(chg, "DT error wrong profile data\n"); + chg->profile_info.data_bk_size = 0; + return -ENODEV; + } + bk_num = byte_len / chg->profile_info.data_bk_size; + chg->profile_data = (u32 *)devm_kzalloc(chg->dev, byte_len, + GFP_KERNEL); + if (chg->profile_data == NULL) { + chg->profile_info.data_bk_size = 0; + return -ENOMEM; + } + + rc = of_property_read_u32_array(node, + "mmi,profile-data", + chg->profile_data, + byte_len / sizeof(u32)); + if (rc < 0) { + mmi_err(chg, "Couldn't read profile data, rc = %d\n", rc); + devm_kfree(chg->dev, chg->profile_data); + chg->profile_data = NULL; + chg->profile_info.data_bk_size = 0; + return rc; + } + + chg->profile_info.data_size = byte_len; + mmi_info(chg, "profile data: block size: %d, num: %d\n", + chg->profile_info.data_bk_size, bk_num); + bk_size = chg->profile_info.data_bk_size / 4; + for (i = 0; i < bk_num; i++) { + memset(bk_buf, '\0', sizeof(bk_buf)); + n = sprintf(bk_buf, "block%d:", i); + for (j = 0; j < bk_size; j++) { + n += sprintf(bk_buf + n, " %d", + chg->profile_data[i * bk_size + j]); + } + mmi_info(chg, "%s\n", bk_buf); + } + } + + rc = of_property_read_u32(node, "mmi,switched-nums", + &chg->switched_nums); + if (rc) { + chg->switched_nums = 1; + } + + return 0; +} + +static int qti_charger_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pmic_glink_client_data client_data; + struct qti_charger *chg; + int rc; + + chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL); + if (!chg) + return -ENOMEM; + + INIT_WORK(&chg->setup_work, qti_charger_setup_work); + INIT_WORK(&chg->notify_work, qti_charger_notify_work); + mutex_init(&chg->read_lock); + mutex_init(&chg->write_lock); + init_completion(&chg->read_ack); + init_completion(&chg->write_ack); + atomic_set(&chg->rx_valid, 0); + atomic_set(&chg->state, PMIC_GLINK_STATE_UP); + platform_set_drvdata(pdev, chg); + chg->dev = dev; + chg->name = "qti_glink_charger"; + + chg->debug_enabled = &debug_enabled; + chg->ipc_log = ipc_log_context_create(MMI_LOG_PAGES, MMI_LOG_DIR, 0); + if (!chg->ipc_log) + mmi_warn(chg, "Error in creating ipc_log_context\n"); + + rc = qti_charger_parse_dt(chg); + if (rc) { + mmi_err(chg, "dt paser failed, rc=%d\n", rc); + return rc; + } + + client_data.id = MSG_OWNER_OEM; + client_data.name = "oem"; + client_data.msg_cb = oem_callback; + client_data.priv = chg; + client_data.state_cb = oem_state_cb; + + chg->client = pmic_glink_register_client(dev, &client_data); + if (IS_ERR(chg->client)) { + rc = PTR_ERR(chg->client); + if (rc != -EPROBE_DEFER) + mmi_err(chg, "Error in registering with pmic_glink rc=%d\n", + rc); + return rc; + } + + this_chip = chg; + device_init_wakeup(chg->dev, true); + qti_charger_init(chg); + return 0; +} + +static int qti_charger_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct qti_charger *chg= dev_get_drvdata(dev); + int rc; + + qti_charger_deinit(chg); + rc = pmic_glink_unregister_client(chg->client); + if (rc < 0) + mmi_err(chg, "pmic_glink_unregister_client failed rc=%d\n", + rc); + + return rc; +} + +static const struct of_device_id qti_charger_match_table[] = { + {.compatible = "mmi,qti-glink-charger"}, + {}, +}; + +static struct platform_driver qti_charger_driver = { + .driver = { + .name = "qti_glink_charger", + .of_match_table = qti_charger_match_table, + }, + .probe = qti_charger_probe, + .remove = qti_charger_remove, + .shutdown = qti_charger_shutdown, +}; + +module_platform_driver(qti_charger_driver); + +MODULE_DESCRIPTION("QTI Glink Charger Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/qti_glink_charger/qti_glink_charger.h b/drivers/power/qti_glink_charger/qti_glink_charger.h new file mode 100644 index 000000000000..e2ed4eb662e8 --- /dev/null +++ b/drivers/power/qti_glink_charger/qti_glink_charger.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2021 Motorola Mobility LLC + * + * 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. + */ + +#ifndef __QTI_GLINK_CHARGER_H__ +#define __QTI_GLINK_CHARGER_H__ + +/*OEM receivers maps*/ +#define OEM_NOTIFY_RECEIVER_PEN_CHG 0x0 +#define OEM_NOTIFY_RECEIVER_WLS_CHG 0x1 +#define OEM_NOTIFY_RECEIVER_EXT_CHG 0x2 + +#define MAX_OEM_NOTIFY_DATA_LEN 8 + +enum oem_property_type { + OEM_PROP_BATT_INFO, + OEM_PROP_CHG_INFO, + OEM_PROP_CHG_PROFILE_INFO, + OEM_PROP_CHG_PROFILE_DATA, + OEM_PROP_CHG_FV, + OEM_PROP_CHG_FCC, + OEM_PROP_CHG_ITERM, + OEM_PROP_CHG_FG_ITERM, + OEM_PROP_CHG_BC_PMAX, + OEM_PROP_CHG_QC_PMAX, + OEM_PROP_CHG_PD_PMAX, + OEM_PROP_CHG_WLS_PMAX, + OEM_PROP_CHG_SUSPEND, + OEM_PROP_CHG_DISABLE, + OEM_PROP_DEMO_MODE, + OEM_PROP_FACTORY_MODE, + OEM_PROP_FACTORY_VERSION, + OEM_PROP_TCMD, + OEM_PROP_PMIC_ICL, + OEM_PROP_REG_ADDRESS, + OEM_PROP_REG_DATA, + OEM_PROP_LPD_INFO, + OEM_PROP_USB_SUSPEND, + OEM_PROP_WLS_EN, + OEM_PROP_WLS_VOLT_MAX, + OEM_PROP_WLS_CURR_MAX, + OEM_PROP_WLS_CHIP_ID, + OEM_PROP_PEN_CTRL, + OEM_PROP_PEN_ID, + OEM_PROP_PEN_SOC, + OEM_PROP_PEN_MAC, + OEM_PROP_PEN_STATUS, + OEM_PROP_WLS_RX_FOD_CURR, + OEM_PROP_WLS_RX_FOD_GAIN, + OEM_PROP_WLS_TX_MODE, + OEM_PROP_WLS_FOLIO_MODE, + OEM_PROP_WLS_DUMP_INFO, + OEM_PROP_WLS_WLC_LIGHT_CTL, + OEM_PROP_WLS_WLC_FAN_SPEED, + OEM_PROP_WLS_WLC_TX_TYPE, + OEM_PROP_WLS_WLC_TX_POWER, + OEM_PROP_SKU_TYPE, + OEM_PROP_HW_REVISION, + OEM_PROP_WLS_WLC_TX_CAPABILITY, + OEM_PROP_WLS_WLC_TX_ID, + OEM_PROP_WLS_WLC_TX_SN, + OEM_PROP_LPD_MITIGATE_MODE, + OEM_PROP_CHG_PARTNER_ICL, + OEM_PROP_MSB_DEV_INFO, + OEM_PROP_MASTER_SWITCHEDCAP_INFO, + OEM_PROP_SLAVE_SWITCHEDCAP_INFO, + OEM_PROP_MASTER_SWITCHEDCAP_RESET, + OEM_PROP_MAX, +}; + +enum wls_notify_event { + NOTIFY_EVENT_WLS_RX_CONNECTED, + NOTIFY_EVENT_WLS_RX_OVERTEMP, + NOTIFY_EVENT_WLS_CHANGE, + NOTIFY_EVENT_WLS_ERROR, + NOTIFY_EVENT_WLS_WLC_CHANGE, +}; + +enum mmi_charger_sku_type +{ + MMI_CHARGER_SKU_PRC = 0x01, + MMI_CHARGER_SKU_ROW, + MMI_CHARGER_SKU_NA, + MMI_CHARGER_SKU_VZW, + MMI_CHARGER_SKU_JPN, + MMI_CHARGER_SKU_ITA, + MMI_CHARGER_SKU_NAE, + MMI_CHARGER_SKU_SUPERSET, +}; + +struct switched_dev_info +{ + bool chg_en; + u8 chg_role; + u8 work_mode; + u8 int_stat; + s32 ibat_ma; + s32 ibus_ma; + s32 vbus_mv; + s32 vout_mv; + s32 vac_mv; + s32 vbat_mv; + s32 vusb_mv; + s32 vwpc_mv; + s32 die_temp; +}; + +struct qti_charger_notify_data { + u32 receiver; + u32 data[MAX_OEM_NOTIFY_DATA_LEN]; +}; + +extern int qti_charger_set_property(u32 property, const void *val, size_t val_len); +extern int qti_charger_get_property(u32 property, void *val, size_t val_len); +extern int qti_charger_register_notifier(struct notifier_block *nb); +extern int qti_charger_unregister_notifier(struct notifier_block *nb); + +#endif diff --git a/drivers/power/rt9426a_fg_mmi/Android.mk b/drivers/power/rt9426a_fg_mmi/Android.mk new file mode 100644 index 000000000000..ea1f8bb4cefe --- /dev/null +++ b/drivers/power/rt9426a_fg_mmi/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := rt9426a_fg_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/rt9426a_fg_mmi/Kbuild b/drivers/power/rt9426a_fg_mmi/Kbuild new file mode 100644 index 000000000000..0c0aa788ddb6 --- /dev/null +++ b/drivers/power/rt9426a_fg_mmi/Kbuild @@ -0,0 +1,7 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/drivers/power/rt9426a_fg_mmi + +rt9426a_fg_mmi-objs += rt9426a_battery.o +obj-m += rt9426a_fg_mmi.o diff --git a/drivers/power/rt9426a_fg_mmi/Makefile b/drivers/power/rt9426a_fg_mmi/Makefile new file mode 100644 index 000000000000..90dc363524bc --- /dev/null +++ b/drivers/power/rt9426a_fg_mmi/Makefile @@ -0,0 +1,31 @@ +ifeq ($(strip $(MODULE_KERNEL_VERSION)), 5.15) + +all: modules + +modules: + $(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 + +else + +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + +endif diff --git a/drivers/power/rt9426a_fg_mmi/rt9426a_battery.c b/drivers/power/rt9426a_fg_mmi/rt9426a_battery.c new file mode 100644 index 000000000000..ae3ea1e2c1d3 --- /dev/null +++ b/drivers/power/rt9426a_fg_mmi/rt9426a_battery.c @@ -0,0 +1,3121 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rt9426a_battery.h" + +#define PRECISION_ENHANCE 5 + +/* Global Variable for RT9426A */ +u16 g_PAGE_CHKSUM[14] = {0}; + +struct rt9426a_chip { + struct i2c_client *i2c; + struct device *dev; + struct rt9426a_platform_data *pdata; + struct power_supply *fg_psy; + struct regmap *regmap; + struct mutex var_lock; + struct mutex update_lock; + struct delayed_work update_work; + int alert_irq; + int capacity; + int soc_offset; + u8 online:1; + int btemp; + int bvolt; + int bcurr; + unsigned cyccnt; + int soh; + u16 ic_ver; + int design_capacity; + int full_capacity; + u16 ocv_checksum_ic; + u16 ocv_checksum_dtsi; + bool calib_unlock; + /* for update ieoc setting by api */ + int ocv_index; + /* for force temperature set */ + u32 op_config_1_backup; +}; + +static const struct rt9426a_platform_data def_platform_data = { + .dtsi_version = { 0, 0 }, + .para_version = 0, + .soc_offset_size = { 2, 1 }, + .offset_interpolation_order = { 2, 2 }, + .battery_type = 4352, + .temp_source = 0, + .volt_source = 0, + .curr_source = 0, + .rs_ic_setting = 1000, /* unit:0.01mR ; 1000x0.01 = 10mR(default)*/ + .rs_schematic = 1000, /* unit:0.01mR ; 1000x0.01 = 10mR(default)*/ + /* add for aging cv */ + .fcc = { 2000, 2000, 2000, 2000, 2000 }, + .fc_vth = { 0x78, 0x78, 0x78, 0x78, 0x78 }, + /* add for smooth_soc */ + .smooth_soc_en = 0, /* default: disable */ + .init_ocv_table = { 3400, 3485, 3571, 3614, 3657, 3683, 3683, 3686, 3690, 3695, + 3699, 3704, 3708, 3712, 3717, 3721, 3726, 3731, 3736, 3740, + 3745, 3750, 3754, 3759, 3764, 3767, 3771, 3775, 3778, 3782, + 3786, 3789, 3793, 3797, 3800, 3804, 3808, 3811, 3815, 3819, + 3822, 3827, 3831, 3835, 3840, 3844, 3849, 3853, 3857, 3863, + 3869, 3875, 3882, 3888, 3895, 3905, 3915, 3925, 3935, 3946, + 3956, 3966, 3976, 3985, 3995, 4005, 4014, 4024, 4033, 4043, + 4053, 4062, 4073, 4083, 4094, 4105, 4116, 4127, 4138, 4149, + 4160, 4171, 4182, 4193, 4204, 4215, 4227, 4238, 4249, 4260, + 4271, 4283, 4294, 4305, 4312, 4318, 4325, 4331, 4338, 4344, 4351}, +}; + +static int rt9426a_block_read(struct i2c_client *i2c, u8 reg, int len, void *dst) +{ + struct rt9426a_chip *chip = i2c_get_clientdata(i2c); + int ret; + + ret = regmap_raw_read(chip->regmap, reg, dst, len); + if (ret < 0) + dev_notice(chip->dev, "RT9426A block read 0x%02x fail\n", reg); + return ret; +} + +static int rt9426a_block_write(struct i2c_client *i2c, + u8 reg, int len, const void *src) +{ + struct rt9426a_chip *chip = i2c_get_clientdata(i2c); + int ret; + + ret = regmap_raw_write(chip->regmap, reg, src, len); + if (ret < 0) + dev_notice(chip->dev, "RT9426A block write 0x%02x fail\n", reg); + return ret; +} + + +static int rt9426a_reg_read_word(struct i2c_client *i2c, u8 reg) +{ + u16 data = 0; + int ret; + + ret = rt9426a_block_read(i2c, reg, 2, &data); + return (ret < 0) ? ret : (s32)le16_to_cpu(data); +} + +static int rt9426a_reg_write_word(struct i2c_client *i2c, u8 reg, u16 data) +{ + data = cpu_to_le16(data); + return rt9426a_block_write(i2c, reg, 2, (uint8_t *)&data); +} + +static int __maybe_unused rt9426a_reg_write_word_with_check + (struct rt9426a_chip *chip, u8 reg, u16 data) +{ + int retry_times = 2, r_data = 0; + + while (retry_times) { + rt9426a_reg_write_word(chip->i2c, reg, data); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(5); + r_data = rt9426a_reg_read_word(chip->i2c, reg); + if (data == r_data) { + dev_info(chip->dev, "Write REG_0x%.2x Successful\n", reg); + break; + } + retry_times--; + if (retry_times == 0) + dev_notice(chip->dev, "Write REG_0x%.2x fail\n", reg); + } + return r_data; +} + +/* caculates page checksum & total checksum */ +static int rt9426a_calculate_checksum_crc(struct rt9426a_chip *chip) +{ + u8 array_idx,i,j; + u16 page_sum, crc_result; + + /* Calculate Page Checksum & Save to Global Array */ + for (array_idx = 0; array_idx < 14; array_idx++) { + page_sum = 0; + for (i = 0; i < 8; i++) { + page_sum += chip->pdata->extreg_table[array_idx].data[2*i] + + (chip->pdata->extreg_table[array_idx].data[2*i+1] << 8); + } + g_PAGE_CHKSUM[array_idx] = 0xFFFF - page_sum; + dev_dbg(chip->dev, "RT9426A Page Checksum: Page=%d, Ckecksum=0x%x\n", array_idx, + g_PAGE_CHKSUM[array_idx]); + } + + /* Calculate Extend Register CRC16 & return */ + crc_result = 0xFFFF; + for (array_idx = 0; array_idx < 14; array_idx++) { + for (i = 0; i < 16;i++) { + crc_result = crc_result ^ chip->pdata->extreg_table[array_idx].data[i]; + + for (j = 0; j < 8; j++) { + if(crc_result & 0x01) { + crc_result = (crc_result >> 1) ^ 0xA001; + } else + crc_result = crc_result >> 1; + } + } + } + dev_dbg(chip->dev,"RT9426A Ext Reg CRC16=0x%x\n",crc_result); + return crc_result; +} + +/* get gauge total checksum value */ +static int rt9426a_get_checksum(struct rt9426a_chip *chip) +{ + u8 retry_times=3,i; + u16 regval,checksum_result; + + while (retry_times) { + /* Send Command to get Total Checksum */ + if(rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, + RT9426A_TOTAL_CHKSUM_CMD) >= 0) { + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(5); + /* Polling [BUSY] flag */ + for (i = 0; i < 10; i++) { + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG3); + if (regval & RT9426A_GAUGE_BUSY_MASK) + mdelay(1); + else + break; + } + /* Get Total Checksum */ + checksum_result = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_TOTAL_CHKSUM); + if((regval & RT9426A_GAUGE_BUSY_MASK)==0) + break; + } + retry_times--; + if (retry_times == 0) { + dev_dbg(chip->dev,"RT9426A Sent Total Checksum Command Fail\n"); + return 0xFFFF; + } + else + dev_dbg(chip->dev,"RT9426A Sent Total Checksum Command Retry\n"); + } + + dev_dbg(chip->dev,"Get RT9426A Total Checksum = 0x%x\n",checksum_result); + return checksum_result; +} + +static void rt9426a_read_page_cmd(struct rt9426a_chip *chip, uint8_t page) +{ + uint16_t read_page_cmd = 0x6500; + int i, regval, retry_times = 2; + + read_page_cmd += page; + + /* confirm sending read page cmd successfully ; 2021-10-12 */ + for (i = 0; i < retry_times; i++) { + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_BDCNTL, read_page_cmd); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_BDCNTL, read_page_cmd); + mdelay(5); + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW1); + if (regval != 0xFFFF) + break; + } +} + +static void rt9426a_write_page_cmd(struct rt9426a_chip *chip, uint8_t page) +{ + uint16_t write_page_cmd = 0x6550; + + write_page_cmd += page; + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_BDCNTL, write_page_cmd); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_BDCNTL, write_page_cmd); + mdelay(5); +} + +static int rt9426a_unseal_wi_retry(struct rt9426a_chip *chip) +{ + int i, regval, retry_times, ret; + + retry_times = 3; + for (i = 0; i < retry_times; i++) { + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG3); + + if ((regval & RT9426A_UNSEAL_MASK) == RT9426A_UNSEAL_STATUS) { + dev_info(chip->dev, "RT9426A Unseal Pass\n"); + ret = RT9426A_UNSEAL_PASS; + goto out; + } else { + + if (i >= 2) { + dev_info(chip->dev, + "RT9426A Unseal Fail after 3 retries\n"); + ret = RT9426A_UNSEAL_FAIL; + goto out; + } else if (i > 0) { + /* print error msg instead of delay */ + dev_info(chip->dev, "RT9426A Unseal Fail Cnt = %d\n", i+1); + } + + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, + RT9426A_Unseal_Key & 0xffff); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, + RT9426A_Unseal_Key >> 16); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(5); + } + } + ret = RT9426A_UNSEAL_FAIL; +out: + return ret; +} + +static void rt9426a_sleep_duty_set(struct rt9426a_chip *chip, uint16_t data) +{ + int regval; + + if (rt9426a_unseal_wi_retry(chip) == RT9426A_UNSEAL_PASS) { + rt9426a_read_page_cmd(chip, RT9426A_PAGE_1); + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW7); + regval = ((regval & 0xfff8) | (data & 0x0007)); + rt9426a_write_page_cmd(chip, RT9426A_PAGE_1); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW7, regval); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(10); + } +} + +static void rt9426a_sleep_duty_read(struct rt9426a_chip *chip) +{ + int regval; + + if (rt9426a_unseal_wi_retry(chip) == RT9426A_UNSEAL_PASS) { + rt9426a_read_page_cmd(chip, RT9426A_PAGE_1); + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW7); + regval = (regval & 0x0007); + dev_info(chip->dev, "Sleep_Dutty = 2^%d sec)\n", regval); + } +} + +static void rt9426a_enter_sleep(struct rt9426a_chip *chip) +{ + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, 0x74AA); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); +} + +static void rt9426a_exit_sleep(struct rt9426a_chip *chip) +{ + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, 0x7400); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); +} + +/* "data" is input temperature with unit = .1'C */ +static void rt9426a_temperature_set(struct rt9426a_chip *chip, int data) +{ + dev_info(chip->dev, "%s: temp = %d .1'C\n", __func__, data); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_TEMP, data + 2732); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); +} + +/* add force temperature set */ +static void __maybe_unused rt9426a_force_temp_set(bool force_en, struct rt9426a_chip *chip, int data) +{ + int regval = 0,retry_times = 3, i =0; + + /* enable force temperature set */ + if(force_en) { + /* 1. set temperature source as host input mode */ + rt9426a_unseal_wi_retry(chip); + for (i = 0 ; i < retry_times ; i++) { + rt9426a_read_page_cmd(chip, RT9426A_PAGE_1); + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW1); + if((regval & RT9426A_OPCFG1_TEMP_SRC_MASK) != RT9426A_OPCFG1_TEMP_SRC_HOST) { + rt9426a_write_page_cmd(chip, RT9426A_PAGE_1); + regval = ((regval & ~(RT9426A_OPCFG1_TEMP_SRC_MASK)) + |RT9426A_OPCFG1_TEMP_SRC_HOST); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW1, regval); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(5); + } + else + break; + } + /* 2. set temperature as input value */ + dev_info(chip->dev, "%s: temp = %d .1'C\n", __func__, data); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_TEMP, data + 2732); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + } + /* disable force temperature set */ + else { + /* 1. check temp source is recovered or not */ + rt9426a_unseal_wi_retry(chip); + for (i = 0 ; i < retry_times ; i++) { + rt9426a_read_page_cmd(chip, RT9426A_PAGE_1); + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW1); + if(regval == chip->op_config_1_backup) + break; + else { + rt9426a_write_page_cmd(chip, RT9426A_PAGE_1); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW1, chip->op_config_1_backup); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(5); + } + } + } +} +static void rt9426a_reset(struct rt9426a_chip *chip) +{ + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, 0x0041); + mdelay(1000); +} + +#if 0 +static int rt9426a_get_avg_vbat(struct rt9426a_chip *chip) +{ + int regval = 0; + + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_AV); + if (regval < 0) + return -EIO; + return regval; +} +#endif + +static int rt9426a_get_volt(struct rt9426a_chip *chip) +{ + if (chip->pdata->volt_source) + chip->bvolt = rt9426a_reg_read_word(chip->i2c, chip->pdata->volt_source); + + return chip->bvolt; +} + +static int rt9426a_get_temp(struct rt9426a_chip *chip) +{ + if (chip->pdata->temp_source) { + chip->btemp = rt9426a_reg_read_word(chip->i2c, chip->pdata->temp_source); + chip->btemp -= 2732; + } + + return chip->btemp; +} + +static unsigned int rt9426a_get_cyccnt(struct rt9426a_chip *chip) +{ + int ret; + + ret = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_CYC); + if (ret < 0) + dev_notice(chip->dev, "%s: read cycle count fail\n", __func__); + else + chip->cyccnt = ret; + + return chip->cyccnt; +} +static int rt9426a_get_soh(struct rt9426a_chip *chip) +{ + int ret; + + ret = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SOH); + if (ret <= 0) + dev_notice(chip->dev, "%s: read soh fail\n", __func__); + else + chip->soh = ret; + + return chip->soh; +} + +static int rt9426a_get_current(struct rt9426a_chip *chip) +{ + if (chip->pdata->curr_source) { + chip->bcurr = rt9426a_reg_read_word(chip->i2c, chip->pdata->curr_source); + if (chip->bcurr < 0) + return -EIO; + if (chip->bcurr > 0x7FFF) { + chip->bcurr = 0x10000 - chip->bcurr; + chip->bcurr = 0 - chip->bcurr; + } + } + + return chip->bcurr; +} +/* add pseudo sub-routine to get is_adpater_plugged */ +static int rt9426a_get_is_adapter_plugged(struct rt9426a_chip *chip) +{ + /* To-Do: + * return 1, if charging adapter is plugged. + * return 0, if charging adapter is unplugged. + */ + return 0; +} +static int rt9426a_fg_get_offset(struct rt9426a_chip *chip, int soc, int temp); +static int rt9426a_fg_get_soc(struct rt9426a_chip *chip, int to_do_smooth_soc) +{ + int regval, capacity = 0, btemp; + + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SOC); + if (regval < 0) { + dev_notice(chip->dev, "read soc value fail\n"); + return -EIO; + } + capacity = (regval * 10); + dev_dbg(chip->dev, "capacity before offset = %d\n", capacity); + btemp = rt9426a_get_temp(chip); + dev_dbg(chip->dev, "TEMP = %d\n", btemp); + chip->soc_offset = rt9426a_fg_get_offset(chip, capacity, btemp); + dev_dbg(chip->dev, "SOC_OFFSET = %d\n", chip->soc_offset); + capacity += chip->soc_offset; + dev_dbg(chip->dev, "capacity after offset = %d\n", capacity); + if (capacity > 0) + capacity = DIV_ROUND_UP(capacity, 10); + else + capacity = 0; + if (capacity > 100) + capacity = 100; + dev_dbg(chip->dev, "SYS_SOC = %d\n", capacity); + + /* add for smooth_soc */ + if (to_do_smooth_soc == 1) { + dev_info(chip->dev, "smooth soc [st, ic] = [%d, %d]\n", chip->capacity, capacity); + dev_info(chip->dev, "smooth soc ta_sts [%d]\n", rt9426a_get_is_adapter_plugged(chip)); + if (abs(chip->capacity - capacity) >= 1) { + if (capacity > chip->capacity) { + if (rt9426a_get_is_adapter_plugged(chip) == 1) + capacity = chip->capacity + 1; + else + capacity = chip->capacity; + } else { + if (rt9426a_get_current(chip) <= 0) + capacity = chip->capacity - 1; + else + capacity = chip->capacity; + } + } + dev_dbg(chip->dev, "SYS_SOC = %d (after smooth)\n", capacity); + } + return capacity; +} + +static unsigned int rt9426a_get_design_capacity(struct rt9426a_chip *chip) +{ + chip->design_capacity = chip->pdata->fcc_design; + + return chip->design_capacity; +} + +static unsigned int rt9426a_get_full_capacity(struct rt9426a_chip *chip) +{ + int curr_soh = chip->soh; + + chip->full_capacity = chip->design_capacity * curr_soh / 100; + if (chip->full_capacity < 0) { + return -EIO; + } + + return chip->full_capacity; +} + +static unsigned int rt9426a_get_charge_counter(struct rt9426a_chip *chip) +{ + int charge_counter; + int full_capacity; + + full_capacity = chip->full_capacity * 1000; + charge_counter = div_s64(full_capacity * chip->capacity, 100); + + return charge_counter; +} + +/* checking cycle_cnt & bccomp */ +static int rt9426a_set_cyccnt(struct rt9426a_chip *chip, unsigned int cyc_new) +{ + int ret; + unsigned int cyccnt = 0; + + ret = rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, RT9426A_SET_CYCCNT_KEY); + if (ret < 0) + dev_notice(chip->dev, "%s: send keyword to set cycle count fail\n", __func__); + else { + mdelay(1); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CYC, cyc_new); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(5); + /* read back check */ + cyccnt = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_CYC); + if (cyccnt == cyc_new) { + ret = cyccnt; + dev_notice(chip->dev, "%s: set cycle count to %d successfully \n", __func__, cyccnt); + } else { + ret = -1; + dev_notice(chip->dev, "%s: set cycle count failed, target = %d, now = %d \n", __func__, cyc_new, cyccnt); + } + } + + return ret; +} + +static int rt9426a_get_cycle_cnt_from_nvm(struct rt9426a_chip *chip, unsigned int *cyccnt_nvm) +{ + /* To-Do: Get backup Cycle Count from NVM and save to "cyc_nvm" */ + return 0; +} + +static int rt9426a_get_bccomp(struct rt9426a_chip *chip) +{ + int regval = 0, retry_times = 2; + + while (retry_times-- > 0) { + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_BCCOMP); + dev_notice(chip->dev, "%s: BCCOMP now is 0x%04X\n", __func__, regval); + if ((regval > 0xCCD) && (regval < 0x7999)) + return regval; + } + + return -EINVAL; +} + +static int rt9426a_set_bccomp(struct rt9426a_chip *chip, unsigned int bccomp_new) +{ + int opcfg4 = 0, opcfg4_new = 0; + + if (rt9426a_unseal_wi_retry(chip) == RT9426A_UNSEAL_FAIL) + return -EINVAL; + /* Get present OPCFG4 */ + rt9426a_read_page_cmd(chip, RT9426A_PAGE_1); + opcfg4 = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW4); + opcfg4_new = opcfg4 | 0x0040; + /* Enable SET_BCOMP by set OPCFG4 */ + rt9426a_write_page_cmd(chip, RT9426A_PAGE_1); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW4, opcfg4_new); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(1); + /* Set new BCCOMP */ + rt9426a_write_page_cmd(chip, RT9426A_PAGE_2); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW5, bccomp_new); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(1); + dev_notice(chip->dev, "%s: Set BCCOMP as 0x%04X\n", __func__, bccomp_new); + /* Recover OPCFG4 */ + rt9426a_write_page_cmd(chip, RT9426A_PAGE_1); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW4, opcfg4); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(1); + /* Seal after reading */ + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, RT9426A_SEAL_CMD); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(1); + + return 0; +} + +static int rt9426a_get_bccomp_from_nvm(struct rt9426a_chip *chip, unsigned int *bccomp_nvm) +{ + /* To-Do: Get backup "BCCOMP" from NVM and save to "bccomp_nvm" */ + return 0; +} + +static void __maybe_unused rt9426a_check_cycle_cnt_for_fg_ini(struct rt9426a_chip *chip) +{ + int cyccnt_fg = 0, cyccnt_nvm = 0; + int bccomp_nvm = 0x4000; + + /* read cyccnt from fg & nvm */ + cyccnt_fg = rt9426a_get_cyccnt(chip); + rt9426a_get_cycle_cnt_from_nvm(chip, &cyccnt_nvm); + + if (cyccnt_fg < cyccnt_nvm) { + /* read bccomp from nvm */ + rt9426a_get_bccomp_from_nvm(chip, &bccomp_nvm); + /* set cyccnt to fg by backup value*/ + rt9426a_set_cyccnt(chip, cyccnt_nvm); + /* set bccomp to fg by backup value*/ + rt9426a_set_bccomp(chip, bccomp_nvm); + dev_notice(chip->dev, "%s: recover fg cycle count from nvm, target = %d, now = %d \n", __func__, cyccnt_nvm, cyccnt_fg); + dev_notice(chip->dev, "%s: recover fg bccomp from nvm, target = %d\n", __func__, bccomp_nvm); + } else + dev_notice(chip->dev, "%s: cycle counts are the same(%d)\n", __func__, cyccnt_fg); +} + +static unsigned int rt9426a_get_ocv_checksum(struct rt9426a_chip *chip) +{ + int i, regval = 0, retry_times = 2; + + if (rt9426a_unseal_wi_retry(chip) == RT9426A_UNSEAL_FAIL) + chip->ocv_checksum_ic = 0xFFFF; + else{ + /* confirm sending read page cmd successfully ; 2021-10-29 */ + for (i = 0; i < retry_times; i++) { + /* get ocv checksum from ic */ + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_BDCNTL, 0xCA09); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_BDCNTL, 0xCA09); + mdelay(5); + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW4); + chip->ocv_checksum_ic = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW5); + if ((regval != 0xFFFF)||(chip->ocv_checksum_ic != 0xFFFF)) + break; + } + } + return chip->ocv_checksum_ic; +} +/* add for updating ieoc setting by api */ +/* unit of ieoc_curr: mA ; unsigned int */ +/* unit of ieoc_buff: mA ; unsigned int */ +static int __maybe_unused rt9426a_write_ieoc_setting_api(struct rt9426a_chip *chip, + unsigned int ieoc_curr, + unsigned int ieoc_buff) +{ + int fc_setting, regval = 0, fc_ith_setting = 0; + + dev_info(chip->dev, "%s\n" ,__func__); + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG1); + if (regval & BIT(0)) + return 0; + + if (rt9426a_unseal_wi_retry(chip) == RT9426A_UNSEAL_FAIL) + return -EINVAL; + + /* read fc_setting for bit operation */ + rt9426a_read_page_cmd(chip, RT9426A_PAGE_5); + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW3); + + /* calculate fc_ith by input parameter */ + /* fc_ith_setting = ((curr+buff)*rs_schematic)/(rs_ic_setting*4) */ + /* e.g. rs_ic_setting = 2.5mR ; rs_ic_setting = 1mR */ + /* fc_ith_setting = (ieoc_setting * rs_schematic)/(rs_ic_setting*4) */ + /* = (ieoc_setting * 1)/(2.5*4) */ + fc_ith_setting = ((ieoc_curr + ieoc_buff) * (int)chip->pdata->rs_schematic); + fc_ith_setting = DIV_ROUND_UP(fc_ith_setting, (int)chip->pdata->rs_ic_setting * 4); + + /* recombine to fc_setting by bit operation */ + fc_setting = (regval & 0xFF) | ((fc_ith_setting & 0xFF) << 8); + + dev_info(chip->dev, "fc_setting was 0x%04X ; is 0x%04X\n" ,regval, fc_setting); + rt9426a_write_page_cmd(chip, RT9426A_PAGE_5); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW3, fc_setting); + + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, 0x0020); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(5); + + return 0; +} + +static int rt9426a_write_ocv_table(struct rt9426a_chip *chip) +{ + int retry_times, i, j, regval, rtn = RT9426A_WRITE_OCV_FAIL; + const u32 *pval = (u32 *)chip->pdata->ocv_table + chip->ocv_index * 80; + + /* set OCV Table */ + if (*pval == 0x13) { + retry_times = 3; + dev_info(chip->dev, "Write NEW OCV Table\n"); + while (retry_times) { + for (i = 0; i < 9; i++) { + rt9426a_reg_write_word(chip->i2c, + RT9426A_REG_BDCNTL, 0xCB00 + i); + rt9426a_reg_write_word(chip->i2c, + RT9426A_REG_BDCNTL, 0xCB00 + i); + for (j = 0; j < 8; j++) { + rt9426a_reg_write_word(chip->i2c, + 0x40 + j * 2, + *(pval + i * 8 + j)); + mdelay(1); + } + } + rt9426a_reg_write_word(chip->i2c, + RT9426A_REG_BDCNTL, 0xCB09); + rt9426a_reg_write_word(chip->i2c, + RT9426A_REG_BDCNTL, 0xCB09); + for (i = 0; i < 5; i++) { + rt9426a_reg_write_word(chip->i2c, 0x40 + i * 2, + *(pval + 9 * 8 + i)); + mdelay(1); + } + rt9426a_reg_write_word(chip->i2c, + RT9426A_REG_DUMMY, 0x0000); + mdelay(10); + regval = rt9426a_reg_read_word(chip->i2c, + RT9426A_REG_FLAG2); + if (regval & RT9426A_USR_TBL_USED_MASK) { + dev_info(chip->dev, "OCV Table Write Successful\n"); + rtn = RT9426A_WRITE_OCV_PASS; + break; + } + retry_times--; + if (retry_times == 0) + dev_notice(chip->dev, "Set OCV Table fail\n"); + } + } + return rtn; +} + +static int rt9426a_apply_pdata(struct rt9426a_chip *); +static void rt9426a_update_info(struct rt9426a_chip *chip) +{ + int regval = 0, ret = 0, retry_times = 0, i; + struct power_supply *batt_psy; + + dev_dbg(chip->dev, "%s\n", __func__); + mutex_lock(&chip->update_lock); + + /* get battery temp from battery power supply */ + batt_psy = power_supply_get_by_name("battery"); + if (!batt_psy) + dev_err(chip->dev, "%s: get batt_psy fail\n", __func__); + + if (rt9426a_unseal_wi_retry(chip) == RT9426A_UNSEAL_FAIL) + goto end_update_info; + + /* check if re-init is necessary */ + retry_times = 3; + for (i = 0 ; i < retry_times ; i++) { + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG3); + if((regval&RT9426A_RI_MASK)&&(regval!=0xFFFF)){ + dev_info(chip->dev, "Init RT9426A by [RI]\n"); + rt9426a_apply_pdata(chip); + break; + } + else if(regval!=0xFFFF) + break; + mdelay(5); + } + + /* read OPCFG1~7 to check */ + rt9426a_read_page_cmd(chip, RT9426A_PAGE_1); + + dev_info(chip->dev, "OPCFG1~5(0x%x 0x%x 0x%x 0x%x 0x%x)\n", + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW1), + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW2), + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW3), + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW4), + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW5)); + + rt9426a_read_page_cmd(chip, RT9426A_PAGE_10); + + dev_info(chip->dev, "OPCFG6~7(0x%x 0x%x)\n", + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW1), + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW2)); + + ret = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG2); + + rt9426a_read_page_cmd(chip, RT9426A_PAGE_2); + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW1); + regval = (regval & 0x0300) >> 8; + if (((ret & 0x0800) >> 11) == 1) { + dev_info(chip->dev, "OCV table define by User\n"); + } else { + if (regval == 0) + dev_info(chip->dev, "OCV(4200) EDV(3200)\n"); + else if (regval == 1) + dev_info(chip->dev, "OCV(4350) EDV(3200)\n"); + else if (regval == 2) + dev_info(chip->dev, "OCV(4400) EDV(3200)\n"); + else + dev_info(chip->dev, "OCV(4350) EDV(3400)\n"); + } + + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW5); + dev_info(chip->dev, "CSCOMP4(%d)\n", regval); + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW4); + dev_info(chip->dev, "CSCOMP5(%d)\n", regval); + + dev_info(chip->dev, "DSNCAP(%d) FCC(%d)\n", + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_DC), + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FCC)); + + dev_info(chip->dev, "VOLT_SOURCE(0x%x) INPUT_VOLT(%d) FG_VBAT(%d) FG_OCV(%d) FG_AV(%d)\n", + chip->pdata->volt_source, rt9426a_get_volt(chip), + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_VBAT), + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_OCV), + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_AV)); + dev_info(chip->dev, "CURR_SOURCE(0x%x) INPUT_CURR(%d) FG_CURR(%d) FG_AI(%d)\n", + chip->pdata->curr_source, rt9426a_get_current(chip), + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_CURR), + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_AI)); + dev_info(chip->dev, "TEMP_SOURCE(0x%x) INPUT_TEMP(%d) FG_TEMP(%d)\n", + chip->pdata->temp_source, rt9426a_get_temp(chip), + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_TEMP)); + dev_info(chip->dev, "FG_FG_INTT(%d) FG_AT(%d)\n", + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_INTT), + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_AT)); + + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG1); + dev_info(chip->dev, "FLAG1(0x%x)\n", regval); + if (((regval & 0x0200) >> 9) == 1) + dev_info(chip->dev, "FC = 1\n"); + else + dev_info(chip->dev, "FC = 0\n"); + + if (((regval & 0x0004) >> 2) == 1) + dev_info(chip->dev, "FD = 1\n"); + else + dev_info(chip->dev, "FD = 0\n"); + + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG2); + dev_info(chip->dev, "FLAG2(0x%x)\n", regval); + + if (((regval & 0xE000) >> 13) == 0) + dev_info(chip->dev, "Power_Mode (Active)\n"); + else if (((regval & 0xE000) >> 13) == 1) + dev_info(chip->dev, "Power_Mode (FST_RSP_ACT)\n"); + else if (((regval & 0xE000) >> 13) == 2) + dev_info(chip->dev, "Power_Mode (Shutdown)\n"); + else + dev_info(chip->dev, "Power_Mode (Sleep)\n"); + + if (((regval & 0x0800) >> 11) == 1) + dev_info(chip->dev, "User_Define_Table (IN USE)\n"); + else + dev_info(chip->dev, "User_Define_Table (NOT IN USE)\n"); + if (((regval & 0x0040) >> 6) == 1) + dev_info(chip->dev, "Battery_Status (Inserted)\n"); + else + dev_info(chip->dev, "Battery_Status (Removed)\n"); + + if (((regval & 0x0001)) == 1) + dev_info(chip->dev, "RLX = 1\n"); + else + dev_info(chip->dev, "RLX = 0\n"); + + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG3); + dev_info(chip->dev, "FLAG3(0x%x)\n", regval); + if (((regval & 0x0100) >> 8) == 1) + dev_info(chip->dev, "RI = 1\n"); + else + dev_info(chip->dev, "RI = 0\n"); + + if (((regval & 0x0001)) == 1) + dev_info(chip->dev, "RT9426A (Unseal)\n"); + else + dev_info(chip->dev, "RT9426A (Seal)\n"); + + dev_info(chip->dev, "CYCCNT(%d)\n", rt9426a_get_cyccnt(chip)); + + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SOC); + dev_info(chip->dev, "SOC(%d)\n", regval); + /* add for smooth_soc */ + /*chip->capacity = rt9426a_fg_get_soc(chip);*/ + chip->capacity = rt9426a_fg_get_soc(chip, chip->pdata->smooth_soc_en); + + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_RM); + dev_info(chip->dev, "RM(%d)\n", regval); + + //regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SOH); + regval = rt9426a_get_soh(chip); + dev_info(chip->dev, "SOH(%d)\n", regval); + + rt9426a_write_page_cmd(chip, RT9426A_PAGE_5); + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW3); + dev_info(chip->dev, "FC_ITH,FC_VTH =(0x%04x)\n", regval); + /* rt9426a_update_ieoc_setting(chip); */ + + if (batt_psy) { + power_supply_changed(batt_psy); + power_supply_put(batt_psy); + } +end_update_info: + mutex_unlock(&chip->update_lock); + return; +} + +static irqreturn_t rt9426a_irq_handler(int irqno, void *param) +{ + struct rt9426a_chip *chip = (struct rt9426a_chip *)param; + + dev_info(chip->dev, "%s\n", __func__); + dev_info(chip->dev, "FG_IRQ(0x%x) FG_FLAG1(0x%x) FG_FLAG2(0x%x) FG_FLAG3(0x%x)\n", + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_IRQ), + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG1), + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG2), + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG3)); + + rt9426a_update_info(chip); + return IRQ_HANDLED; +} + +enum comp_offset_typs { + FG_COMP = 0, + SOC_OFFSET, + EXTREG_UPDATE, +}; + +static void new_vgcomp_soc_offset_datas(struct device *dev, int type, + struct rt9426a_platform_data *pdata, + int size_x, int size_y, int size_z) +{ + switch (type) { + case SOC_OFFSET: + if (pdata->soc_offset.soc_offset_data) { + devm_kfree(dev, + pdata->soc_offset.soc_offset_data); + pdata->soc_offset.soc_offset_data = NULL; + } + if (size_x != 0 && size_y != 0) + pdata->soc_offset.soc_offset_data = + devm_kzalloc(dev, + size_x * size_y * sizeof(struct data_point), + GFP_KERNEL); + if (pdata->soc_offset.soc_offset_data) { + pdata->soc_offset.soc_voltnr = size_x; + pdata->soc_offset.tempnr = size_y; + + } else { + pdata->soc_offset.soc_voltnr = 0; + pdata->soc_offset.tempnr = 0; + } + break; + default: + WARN_ON(1); + } +} + +struct submask_condition { + int x, y, z; + int order_x, order_y, order_z; + int xnr, ynr, znr; + const struct data_point *mesh_src; +}; + +static inline const struct data_point *get_mesh_data( + int i, int j, int k, + const struct data_point *mesh, int xnr, int ynr) +{ + return mesh + k * ynr * xnr + j * xnr + i; +} + +static int get_sub_mesh(int state, struct data_point *mesh_buffer, + struct submask_condition *condition) +{ + int i = 0, j = 0, k = 0, x = 0, y = 0, z = 0; + + x = condition->x; + y = condition->y; + z = condition->z; + for (i = 0; i < condition->xnr; ++i) { + if (get_mesh_data(i, 0, 0, condition->mesh_src, + condition->xnr, condition->ynr)->x >= x) + break; + } + for (; i >= 0 && i < condition->xnr; --i) { + if (get_mesh_data(i, 0, 0, condition->mesh_src, + condition->xnr, condition->ynr)->x <= x) + break; + } + + for (j = 0; j < condition->ynr; ++j) { + if (get_mesh_data(0, j, 0, condition->mesh_src, + condition->xnr, condition->ynr)->y >= y) + break; + } + for (; j >= 0 && j < condition->ynr; --j) { + if (get_mesh_data(0, j, 0, condition->mesh_src, + condition->xnr, condition->ynr)->y <= y) + break; + } + + if (state == FG_COMP) { + for (k = 0; k < condition->znr; ++k) { + if (get_mesh_data(0, 0, k, condition->mesh_src, + condition->xnr, condition->ynr)->z >= z) + break; + } + for (; k >= 0 && k < condition->znr; --k) { + if (get_mesh_data(0, 0, k, condition->mesh_src, + condition->xnr, condition->ynr)->z <= z) + break; + } + } + + i -= ((condition->order_x - 1) / 2); + j -= ((condition->order_y - 1) / 2); + k -= ((condition->order_z - 1) / 2); + + if (i <= 0) + i = 0; + if (j <= 0) + j = 0; + if (k <= 0) + k = 0; + if ((i + condition->order_x) > condition->xnr) + i = condition->xnr - condition->order_x; + if ((j + condition->order_y) > condition->ynr) + j = condition->ynr - condition->order_y; + if ((k + condition->order_z) > condition->znr) + k = condition->znr - condition->order_z; + + if (state == FG_COMP) { + for (z = 0; z < condition->order_z; ++z) { + for (y = 0; y < condition->order_y; ++y) { + for (x = 0; x < condition->order_x; ++x) { + *(mesh_buffer + z * condition->order_y * + condition->order_z + + y * condition->order_x + x) + = *get_mesh_data(i + x, j + y, + k + z, + condition->mesh_src, + condition->xnr, + condition->ynr); + } + } + } + } else { + for (y = 0; y < condition->order_y; ++y) { + for (x = 0; x < condition->order_x; ++x) { + *(mesh_buffer + y * condition->order_x + x) + = *get_mesh_data(i + x, j + y, 0, + condition->mesh_src, + condition->xnr, + condition->ynr); + } + } + } + return 0; +} + +static int offset_li(int xnr, int ynr, + const struct data_point *mesh, int x, int y) +{ + long long retval = 0; + int i, j, k; + long long wm, wd; + const struct data_point *cache; + + for (i = 0; i < xnr; ++i) { + for (j = 0; j < ynr; ++j) { + wm = wd = 1; + cache = get_mesh_data(i, j, 0, mesh, xnr, ynr); + for (k = 0; k < xnr; ++k) { + if (i != k) { + wm *= (x - get_mesh_data(k, j, 0, + mesh, xnr, ynr)->x); + wd *= (cache->x - get_mesh_data(k, j, 0, + mesh, xnr, ynr)->x); + } + } + for (k = 0; k < ynr; ++k) { + if (j != k) { + wm *= (y - get_mesh_data(i, k, 0, + mesh, xnr, ynr)->y); + wd *= (cache->y - get_mesh_data(i, k, 0, + mesh, xnr, ynr)->y); + } + } + retval += div64_s64( + ((cache->w * wm) << PRECISION_ENHANCE), wd); + } + } + return (int)((retval + (1 << (PRECISION_ENHANCE - 1))) + >> PRECISION_ENHANCE); +} + +/* add for init. soc ; 2022-01-18 */ +static int rt9426a_fg_set_soc(struct rt9426a_chip *chip, int soc_target) +{ + int i, regval, retry_times = 3, ret = RT9426A_WRITE_SOC_FAIL; + + for (i = 0; i < retry_times; i++) { + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SOC); + if (regval == soc_target) { + ret = soc_target; + dev_info(chip->dev, "%s: Set SOC PASS.\n", __func__); + break; + } else { + regval = RT9426A_SET_SOC_KEY | ((soc_target * 2) & 0xFF); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, regval); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(5); + if (i > 0) + dev_err(chip->dev, "%s: Set SOC FAIL Cnt = %d.\n", __func__, i); + } + } + + return ret; +} + +static int rt9426a_fg_get_offset(struct rt9426a_chip *chip, int soc_val, int temp) +{ + const int ip_x = chip->pdata->offset_interpolation_order[0]; + const int ip_y = chip->pdata->offset_interpolation_order[1]; + struct data_point *sub_mesh; + int xnr, ynr; + int offset = 0; + struct soc_offset_table *offset_table = NULL; + struct submask_condition condition = { + .x = soc_val, + .y = temp, + }; + + mutex_lock(&chip->var_lock); + offset_table = &chip->pdata->soc_offset; + xnr = offset_table->soc_voltnr; + ynr = offset_table->tempnr; + if (xnr == 0 || ynr == 0) + goto out; + condition.order_x = min(ip_x, xnr); + condition.order_y = min(ip_y, ynr); + condition.xnr = xnr; + condition.ynr = ynr; + condition.mesh_src = offset_table->soc_offset_data; + sub_mesh = kzalloc(ip_x * ip_y * sizeof(struct data_point), GFP_KERNEL); + if (!sub_mesh) + goto out; + get_sub_mesh(SOC_OFFSET, sub_mesh, &condition); + offset = offset_li(condition.order_x, condition.order_y, sub_mesh, + soc_val, temp); + kfree(sub_mesh); +out: + mutex_unlock(&chip->var_lock); + return offset; +} + +static int rt9426a_set_ffc_mode(struct rt9426a_chip *chip,u8 mode) +{ + int i = 0; + int ret = 0; + if (mode == 0) { + if (chip->btemp < 250 && chip->btemp >= 0) + i = RT9426A_CV4500_IEOC243; + else if(chip->btemp < 250) + i = RT9426A_CV4530_IEOC506; + else if (chip->btemp < 350) + i = RT9426A_CV4530_IEOC560; + else if (chip->btemp < 450) + i = RT9426A_CV4530_IEOC965; + else + i = RT9426A_CV4200_IEOC243; + } else { + if (chip->btemp < 350 && chip->btemp >= 0) + i = RT9426A_CV4500_IEOC243; + else if (chip->btemp < 450) + i = RT9426A_CV4500_IEOC670; + else + i = RT9426A_CV4200_IEOC243; + } + mutex_lock(&chip->update_lock); + if (rt9426a_unseal_wi_retry(chip) == RT9426A_UNSEAL_FAIL) { + ret = -EINVAL; + goto ccv_out; + } + rt9426a_write_page_cmd(chip, RT9426A_PAGE_5); + ret = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW3); + if (ret < 0) + goto ccv_out; + if (ret != chip->pdata->fc_vth_ith[i]) + ret = rt9426a_reg_write_word(chip->i2c, + RT9426A_REG_SWINDOW3, + chip->pdata->fc_vth_ith[i]); + else + ret = 0; +ccv_out: + mutex_unlock(&chip->update_lock); + dev_notice(chip->dev, "RT9426A ffc_mode:%d chip->btemp:%d fc_vth_ith %x\n", + mode,chip->btemp,chip->pdata->fc_vth_ith[i]); + + return ret; +} + +static int rt_fg_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct rt9426a_chip *chip = power_supply_get_drvdata(psy); + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = chip->online; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = chip->bvolt * 1000; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = chip->pdata->battery_type; + break; + case POWER_SUPPLY_PROP_CAPACITY: + /* add for smooth_soc */ + /*chip->capacity = rt9426a_fg_get_soc(chip); */ + val->intval = chip->capacity; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + val->intval = rt9426a_get_design_capacity(chip) * 1000; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + rt9426a_get_current(chip); + val->intval = chip->bcurr * 1000 * (-1); + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = chip->btemp; + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + val->intval = chip->cyccnt; + break; + /* add for aging cv */ + case POWER_SUPPLY_PROP_VOLTAGE_OCV: + val->intval = chip->ocv_index; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + val->intval = rt9426a_get_full_capacity(chip) * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + val->intval = rt9426a_get_charge_counter(chip); + break; + default: + rc = -EINVAL; + break; + } + + return rc; +} + +static int rt_fg_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct rt9426a_chip *chip = power_supply_get_drvdata(psy); + int rc = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_TEMP: + rt9426a_temperature_set(chip, val->intval); + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + if (val->intval < 0) + return -EINVAL; + rt9426a_set_cyccnt(chip, val->intval); + break; + /* add for aging cv */ + case POWER_SUPPLY_PROP_VOLTAGE_OCV: + if (val->intval < 0 || val->intval > 4) + return -EINVAL; + chip->ocv_index = val->intval; + /* save to RSVD2 after ocv_index assigned */ + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_RSVD2, chip->ocv_index); + if (rt9426a_unseal_wi_retry(chip) == RT9426A_UNSEAL_FAIL) + return -EIO; + mutex_lock(&chip->update_lock); + /* write aging ocv table */ + rt9426a_write_ocv_table(chip); + /* write aging fcc */ + rt9426a_write_page_cmd(chip, RT9426A_PAGE_2); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW7, chip->pdata->fcc[chip->ocv_index]); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(5); + /* write aging fc_vth */ + rt9426a_write_page_cmd(chip, RT9426A_PAGE_5); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW3, + (chip->pdata->fc_vth[chip->ocv_index]) | (chip->pdata->extreg_table[4].data[5] << 8)); + mutex_unlock(&chip->update_lock); + /* update ocv checksum */ + chip->ocv_checksum_dtsi = *((u32 *)chip->pdata->ocv_table + + (chip->ocv_index * 80) + RT9426A_IDX_OF_OCV_CKSUM); + break; + case POWER_SUPPLY_PROP_TYPE: + if (chip->pdata->fc_vth_ith[0] != 0x0) + rc = rt9426a_set_ffc_mode(chip,val->intval); + break; + default: + rc = -EINVAL; + break; + } + return rc; +} + +static int rt_fg_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) +{ + switch (psp) { + case POWER_SUPPLY_PROP_TEMP: + case POWER_SUPPLY_PROP_CYCLE_COUNT: + /* add for aging cv */ + case POWER_SUPPLY_PROP_VOLTAGE_OCV: + case POWER_SUPPLY_PROP_TYPE: + return true; + default: + return false; + } +} + +static enum power_supply_property rt_fg_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_VOLTAGE_OCV, + POWER_SUPPLY_PROP_TYPE, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_COUNTER, +}; + +static struct power_supply_desc fg_psy_desc = { + .name = "bms", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = rt_fg_props, + .num_properties = ARRAY_SIZE(rt_fg_props), + .get_property = rt_fg_get_property, + .set_property = rt_fg_set_property, + .property_is_writeable = rt_fg_property_is_writeable, +}; + +static int __maybe_unused rt9426a_get_calibration_para(struct rt9426a_chip *chip, u8 *curr_offs, u8 *curr_gain, u8 *volt_gain) +{ + /* To-Do: get calibration parameter(curr_offs/curr_gain/volt_gain) + from platform NVM */ + return 0; +} + +static int rt9426a_enter_calibration_mode(struct rt9426a_chip *chip) +{ + int i, regval, retry_times, ret; + int j, tick_old = 0, tick_new = 0; + + retry_times = 3; + for (i = 0; i < retry_times; i++) { + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG2); + + if ((regval & RT9426A_CALI_MODE_MASK) == RT9426A_CALI_MODE_MASK) { + dev_info(chip->dev, "RT9426 is in Calibration Mode\n"); + ret = RT9426A_CALI_MODE_PASS; + goto out; + } else { + if (rt9426a_unseal_wi_retry(chip) == RT9426A_UNSEAL_PASS){ + /* Check System Tick before entering Calibration */ + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, RT9426A_SYS_TICK_ON_CMD); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(5); + tick_old = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_ADV); + for (j = 0; j < 1000; j++) { + tick_new = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_ADV); + if (tick_old != tick_new) { + mdelay(300); + break; + } + mdelay(5); + } + /* Send Cmd to Enter Calibration */ + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, RT9426A_CALI_ENTR_CMD); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + /* Disable System Tick */ + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, RT9426A_SYS_TICK_OFF_CMD); + if (i >= 2) { + dev_info(chip->dev, + "RT9426 Enter Calibration Mode Fail after 3 retries\n"); + ret = RT9426A_CALI_MODE_FAIL; + goto out; + } + mdelay(5); + } + } + } + ret = RT9426A_CALI_MODE_FAIL; +out: + return ret; +} + +static void rt9426a_exit_calibration_mode(struct rt9426a_chip *chip) +{ + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, RT9426A_CALI_EXIT_CMD); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); +} + +static void rt9426a_apply_sense_resistor(struct rt9426a_chip *chip) +{ + int op_config10 = 0, rsense = 0; + rsense = chip->pdata->rs_ic_setting / RT9426A_NEW_RS_UNIT; + + if (rt9426a_unseal_wi_retry(chip) == RT9426A_UNSEAL_PASS){ + /* get op_config10 */ + rt9426a_read_page_cmd(chip, RT9426A_PAGE_10); + op_config10 = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW3); + /* update rsense to op_config10 */ + op_config10 = (RT9426A_PAGE_10&0xFF00) | (rsense&0xFF); + /* apply op_config10 */ + rt9426a_write_page_cmd(chip, RT9426A_PAGE_10); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW3, op_config10); + mdelay(5); + } +} + +static void rt9426a_apply_calibration_para(struct rt9426a_chip *chip, u8 curr_offs, u8 curr_gain, u8 volt_gain) +{ + if (rt9426a_unseal_wi_retry(chip) == RT9426A_UNSEAL_PASS){ + rt9426a_write_page_cmd(chip, RT9426A_PAGE_0); + /* set Current system gain & offset */ + if((curr_gain != 0x00)&&(curr_gain != 0xFF)&& + (curr_offs != 0x00)&&(curr_offs != 0xFF)){ + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW1, + curr_gain | (curr_offs<<8)); + } + else{ + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW1, 0x8080); + } + /* set Voltage system gain */ + if((volt_gain != 0x00)&&(volt_gain != 0xFF)){ + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW7, + 0x88 | (volt_gain<<8)); + } + else{ + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW7, 0x8088); + } + mdelay(5); + } +} +static void __maybe_unused rt9426a_assign_calibration_para(struct rt9426a_chip *chip, u8 curr_offs, u8 curr_gain, u8 volt_gain) +{ + /* Check and assign Current system gain & offset */ + if((curr_gain != 0x00)&&(curr_gain != 0xFF)&& + (curr_offs != 0x00)&&(curr_offs != 0xFF)){ + chip->pdata->extreg_table[0].data[0] = curr_gain; + chip->pdata->extreg_table[0].data[1] = curr_offs; + dev_info(chip->dev, + "RT9426A assign calibrated current parameter before application\n"); + } + else{ + chip->pdata->extreg_table[0].data[0] = 0x80; + chip->pdata->extreg_table[0].data[1] = 0x80; + dev_info(chip->dev, + "RT9426A assign default current parameter before application\n"); + } + /* Check and assign Voltage system gain */ + if((volt_gain != 0x00)&&(volt_gain != 0xFF)){ + chip->pdata->extreg_table[0].data[13] = volt_gain; + dev_info(chip->dev, + "RT9426A assign calibrated voltage parameter before application\n"); + } + else{ + chip->pdata->extreg_table[0].data[13] = 0x80; + dev_info(chip->dev, + "RT9426A assign default voltage parameter before application\n"); + } + +} + +static int rt9426a_get_curr_by_conversion(struct rt9426a_chip *chip) +{ + int regval=0; + + if(rt9426a_enter_calibration_mode(chip)==RT9426A_CALI_MODE_PASS){ + /* Start current convertion */ + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, RT9426A_CURR_CONVERT_CMD); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(50); + + /* Get convert result */ + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_CURR); + if (regval < 0) + return -EIO; + if (regval > 0x7FFF) { + regval = 0x10000 - regval; + regval = 0 - regval; + } + } + + /* scaling for the current */ + regval = regval * (int)chip->pdata->rs_ic_setting / (int)chip->pdata->rs_schematic; + dev_info(chip->dev, "CALIB_CURRENT = %d mA\n",regval); + + return regval; +} + +static int rt9426a_get_volt_by_conversion(struct rt9426a_chip *chip) +{ + int regval=0; + if(rt9426a_enter_calibration_mode(chip)==RT9426A_CALI_MODE_PASS){ + /* Start voltage convertion */ + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, RT9426A_VOLT_CONVERT_CMD); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(50); + + /* Get voltage result */ + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_VBAT); + } + return regval; +} + +/* To Disable power path */ +static int rt9426a_request_charging_inhibit(bool needInhibit) +{ + if(needInhibit){ + /* To-Do: Turn OFF charging power path to battery */ + } + else{ + /* To-Do: Turn ON charging power path to battery */ + } + return 0; +} + +static int rt9426a_enter_shutdown_mode(struct rt9426a_chip *chip) +{ + int regval,loop; + for(loop=0;loop<5;loop++){ + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG2); + if(regval&RT9426A_SHDN_MASK) + break; + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, RT9426A_SHDN_ENTR_CMD); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(20); + } + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG2); + if(!(regval&RT9426A_SHDN_MASK)){ + dev_info(chip->dev, "RT9426A Enter Shutdown Fail\n"); + return -1; + } + dev_info(chip->dev, "RT9426A Enter Shutdown Success\n"); + return 0; +} + +static int rt9426a_exit_shutdown_mode(struct rt9426a_chip *chip) +{ + int regval,loop; + int cmd_cnt = 0; + for(loop=0;loop<5;loop++){ + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG2); + if(!(regval&RT9426A_SHDN_MASK)) + break; + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, RT9426A_SHDN_EXIT_CMD); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(250); + dev_info(chip->dev, "RT9426A Send Exit Shutdown Cmd Count = %d\n",++cmd_cnt); + } + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG2); + if(regval&RT9426A_SHDN_MASK){ + dev_info(chip->dev, "RT9426A is in Shutdown\n"); + return -1; + } + dev_info(chip->dev, "RT9426A is not in Shutdown\n"); + + if (cmd_cnt == 0) + goto out; + + /* Power path control check */ + regval = rt9426a_get_current(chip); + if(regval>0){ + /* Disable power path */ + rt9426a_request_charging_inhibit(true); + dev_info(chip->dev, "RT9426A request to enable charging inhibit\n"); + mdelay(1250); + } + /* Send QS Command to get INI SOC */ + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, 0x4000); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + dev_info(chip->dev, "Send QS after exiting Shutdown\n"); + mdelay(5); + /* Power path recover check */ + if(regval>0){ + /* To Enable power path */ + rt9426a_request_charging_inhibit(false); + dev_info(chip->dev, "RT9426A request to disable charging inhibit\n"); + } +out: + return 0; +} + +static ssize_t rt_fg_show_attrs(struct device *, struct device_attribute *, char *); +static ssize_t rt_fg_store_attrs(struct device *, struct device_attribute *, const char *, + size_t count); + +#define RT_FG_ATTR(_name) \ +{ \ + .attr = {.name = #_name, .mode = 0664}, \ + .show = rt_fg_show_attrs, \ + .store = rt_fg_store_attrs, \ +} + +static struct device_attribute rt_fuelgauge_attrs[] = { + RT_FG_ATTR(fg_temp), + RT_FG_ATTR(volt), + RT_FG_ATTR(curr), + RT_FG_ATTR(update), + RT_FG_ATTR(ocv_table), + RT_FG_ATTR(enter_sleep), + RT_FG_ATTR(exit_sleep), + RT_FG_ATTR(set_sleep_duty), + RT_FG_ATTR(DBP0), + RT_FG_ATTR(DBP1), + RT_FG_ATTR(DBP2), + RT_FG_ATTR(DBP3), + RT_FG_ATTR(DBP4), + RT_FG_ATTR(DBP5), + RT_FG_ATTR(DBP6), + RT_FG_ATTR(DBP7), + RT_FG_ATTR(DBP8), + RT_FG_ATTR(DBP9), + RT_FG_ATTR(WCNTL), + RT_FG_ATTR(WEXTCNTL), + RT_FG_ATTR(WSW1), + RT_FG_ATTR(WSW2), + RT_FG_ATTR(WSW3), + RT_FG_ATTR(WSW4), + RT_FG_ATTR(WSW5), + RT_FG_ATTR(WSW6), + RT_FG_ATTR(WSW7), + RT_FG_ATTR(WSW8), + RT_FG_ATTR(WTEMP), + RT_FG_ATTR(UNSEAL), + RT_FG_ATTR(FG_SET_TEMP), + RT_FG_ATTR(FG_RESET), + RT_FG_ATTR(CALIB_SECURE), + RT_FG_ATTR(CALIB_RSENSE), + RT_FG_ATTR(CALIB_CURRENT), + RT_FG_ATTR(CALIB_VOLTAGE), + RT_FG_ATTR(CALIB_FACTOR), + RT_FG_ATTR(FORCE_SHUTDOWN), + RT_FG_ATTR(BCCOMP), +}; + +enum { + FG_TEMP = 0, + FG_VOLT, + FG_CURR, + FG_UPDATE, + OCV_TABLE, + ENTER_sleep, + EXIT_sleep, + SET_sleep_DUTY, + DBP0, + DBP1, + DBP2, + DBP3, + DBP4, + DBP5, + DBP6, + DBP7, + DBP8, + DBP9, + WCNTL, + WEXTCNTL, + WSW1, + WSW2, + WSW3, + WSW4, + WSW5, + WSW6, + WSW7, + WSW8, + WTEMP, + UNSEAL, + FG_SET_TEMP, + FG_RESET, + CALIB_SECURE, + CALIB_RSENSE, + CALIB_CURRENT, + CALIB_VOLTAGE, + CALIB_FACTOR, + FORCE_SHUTDOWN, + BCCOMP, +}; + +static ssize_t rt_fg_show_attrs(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct rt9426a_chip *chip = dev_get_drvdata(dev->parent); + const ptrdiff_t offset = attr - rt_fuelgauge_attrs; + int i = 0; + + switch (offset) { + case FG_TEMP: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", chip->btemp); + break; + case FG_VOLT: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", chip->bvolt); + break; + case FG_CURR: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", chip->bcurr); + break; + case CALIB_SECURE: + i = scnprintf(buf, PAGE_SIZE, chip->calib_unlock ? "Unlocked\n" : "Locked\n"); + break; + case CALIB_CURRENT: + if (!chip->calib_unlock) + return -EACCES; + rt9426a_enter_calibration_mode(chip); + i = scnprintf(buf, PAGE_SIZE, "%d\n", rt9426a_get_curr_by_conversion(chip)); + rt9426a_exit_calibration_mode(chip); + break; + case CALIB_VOLTAGE: + if (!chip->calib_unlock) + return -EACCES; + rt9426a_enter_calibration_mode(chip); + i = scnprintf(buf, PAGE_SIZE, "%d\n", rt9426a_get_volt_by_conversion(chip)); + rt9426a_exit_calibration_mode(chip); + break; + case BCCOMP: + i = scnprintf(buf, PAGE_SIZE, "%d\n", rt9426a_get_bccomp(chip)); + break; + case FG_UPDATE: + case OCV_TABLE: + case ENTER_sleep: + case EXIT_sleep: + case SET_sleep_DUTY: + case DBP0: + case DBP1: + case DBP2: + case DBP3: + case DBP4: + case DBP5: + case DBP6: + case DBP7: + case DBP8: + case DBP9: + case WCNTL: + case WEXTCNTL: + case WSW1: + case WSW2: + case WSW3: + case WSW4: + case WSW5: + case WSW6: + case WSW7: + case WSW8: + case WTEMP: + case UNSEAL: + case FG_SET_TEMP: + case FG_RESET: + default: + i = -EINVAL; + break; + } + return i; +} + +static ssize_t rt_fg_store_attrs(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct rt9426a_chip *chip = dev_get_drvdata(dev->parent); + const ptrdiff_t offset = attr - rt_fuelgauge_attrs; + int ret = 0; + int x, y = 0; + char *org, *token, *cur; + int temp[8]; + int val; + + switch (offset) { + case FG_TEMP: + ret = kstrtoint(buf, 10, &val); + if (ret < 0) { + dev_notice(dev, "get paremters fail\n"); + ret = -EINVAL; + } + chip->btemp = val; + ret = count; + break; + case FG_VOLT: + ret = kstrtoint(buf, 10, &val); + if (ret < 0) { + dev_notice(dev, "get paremters fail\n"); + ret = -EINVAL; + } + chip->bvolt = val; + ret = count; + break; + case FG_CURR: + ret = kstrtoint(buf, 10, &val); + if (ret < 0) { + dev_notice(dev, "get paremters fail\n"); + ret = -EINVAL; + } + chip->bcurr = val; + ret = count; + break; + case FG_UPDATE: + if (sscanf(buf, "%x\n", &x) == 1 && x == 1) { + rt9426a_update_info(chip); + ret = count; + } else + ret = -EINVAL; + break; + case OCV_TABLE: + if (sscanf(buf, "%x\n", &x) == 1 && x == 1) { + for (x = 0; x < 10; x++) { + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_BDCNTL, 0xCA00 + x); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_BDCNTL, 0xCA00 + x); + for (y = 0; y < 8; y++) + temp[y] = + rt9426a_reg_read_word(chip->i2c, + RT9426A_REG_SWINDOW1 + y * 2); + + dev_info(dev, "fg_ocv_table_%d\n", x); + dev_info(dev, "<0x%x 0x%x 0x%x 0x%x>\n", + temp[0], temp[1], temp[2], temp[3]); + dev_info(dev, "<0x%x 0x%x 0x%x 0x%x>\n", + temp[4], temp[5], temp[6], temp[7]); + } + ret = count; + } else + ret = -EINVAL; + break; + case ENTER_sleep: + if (sscanf(buf, "%x\n", &x) == 1 && x == 1) { + rt9426a_enter_sleep(chip); + dev_info(dev, "SLP_STS = %d\n", + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG2) >> 15); + ret = count; + } else + ret = -EINVAL; + break; + case EXIT_sleep: + if (sscanf(buf, "%x\n", &x) == 1 && x == 1) { + rt9426a_exit_sleep(chip); + dev_info(dev, "SLP_STS = %d\n", + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG2) >> 15); + ret = count; + } else + ret = -EINVAL; + break; + case SET_sleep_DUTY: + ret = kstrtoint(buf, 10, &val); + if (ret < 0) { + dev_notice(dev, "get paremters fail\n"); + ret = -EINVAL; + } + rt9426a_sleep_duty_set(chip, val); + rt9426a_sleep_duty_read(chip); + ret = count; + break; + case DBP0: + if (sscanf(buf, "%x\n", &x) == 1 && x == 1) { + rt9426a_read_page_cmd(chip, RT9426A_PAGE_0); + for (x = 0; x < 8; x++) + temp[x] = rt9426a_reg_read_word(chip->i2c, + RT9426A_REG_SWINDOW1 + x * 2); + + dev_info(dev, "fg_data_block_page0"); + dev_info(dev, "0x41:<0x%x>\n", temp[0]); + dev_info(dev, "0x43:<0x%x>\n", temp[1]); + dev_info(dev, "0x45:<0x%x>\n", temp[2]); + dev_info(dev, "0x47:<0x%x>\n", temp[3]); + dev_info(dev, "0x49:<0x%x>\n", temp[4]); + dev_info(dev, "0x4B:<0x%x>\n", temp[5]); + dev_info(dev, "0x4D:<0x%x>\n", temp[6]); + dev_info(dev, "0x4F:<0x%x>\n", temp[7]); + ret = count; + } else + ret = -EINVAL; + break; + case DBP1: + if (sscanf(buf, "%x\n", &x) == 1 && x == 1) { + rt9426a_read_page_cmd(chip, RT9426A_PAGE_1); + for (x = 0; x < 8; x++) + temp[x] = rt9426a_reg_read_word(chip->i2c, + RT9426A_REG_SWINDOW1 + x * 2); + + dev_info(dev, "fg_data_block_page1"); + dev_info(dev, "0x41:<0x%x>\n", temp[0]); + dev_info(dev, "0x43:<0x%x>\n", temp[1]); + dev_info(dev, "0x45:<0x%x>\n", temp[2]); + dev_info(dev, "0x47:<0x%x>\n", temp[3]); + dev_info(dev, "0x49:<0x%x>\n", temp[4]); + dev_info(dev, "0x4B:<0x%x>\n", temp[5]); + dev_info(dev, "0x4D:<0x%x>\n", temp[6]); + dev_info(dev, "0x4F:<0x%x>\n", temp[7]); + ret = count; + } else + ret = -EINVAL; + break; + case DBP2: + if (sscanf(buf, "%x\n", &x) == 1 && x == 1) { + rt9426a_read_page_cmd(chip, RT9426A_PAGE_2); + for (x = 0; x < 8; x++) + temp[x] = rt9426a_reg_read_word(chip->i2c, + RT9426A_REG_SWINDOW1 + x * 2); + + dev_info(dev, "fg_data_block_page2"); + dev_info(dev, "0x41:<0x%x>\n", temp[0]); + dev_info(dev, "0x43:<0x%x>\n", temp[1]); + dev_info(dev, "0x45:<0x%x>\n", temp[2]); + dev_info(dev, "0x47:<0x%x>\n", temp[3]); + dev_info(dev, "0x49:<0x%x>\n", temp[4]); + dev_info(dev, "0x4B:<0x%x>\n", temp[5]); + dev_info(dev, "0x4D:<0x%x>\n", temp[6]); + dev_info(dev, "0x4F:<0x%x>\n", temp[7]); + ret = count; + } else + ret = -EINVAL; + break; + case DBP3: + if (sscanf(buf, "%x\n", &x) == 1 && x == 1) { + rt9426a_read_page_cmd(chip, RT9426A_PAGE_3); + for (x = 0; x < 8; x++) + temp[x] = rt9426a_reg_read_word(chip->i2c, + RT9426A_REG_SWINDOW1 + x * 2); + + dev_info(dev, "fg_data_block_page3"); + dev_info(dev, "0x41:<0x%x>\n", temp[0]); + dev_info(dev, "0x43:<0x%x>\n", temp[1]); + dev_info(dev, "0x45:<0x%x>\n", temp[2]); + dev_info(dev, "0x47:<0x%x>\n", temp[3]); + dev_info(dev, "0x49:<0x%x>\n", temp[4]); + dev_info(dev, "0x4B:<0x%x>\n", temp[5]); + dev_info(dev, "0x4D:<0x%x>\n", temp[6]); + dev_info(dev, "0x4F:<0x%x>\n", temp[7]); + ret = count; + } else + ret = -EINVAL; + break; + case DBP4: + if (sscanf(buf, "%x\n", &x) == 1 && x == 1) { + rt9426a_read_page_cmd(chip, RT9426A_PAGE_4); + for (x = 0; x < 8; x++) + temp[x] = rt9426a_reg_read_word(chip->i2c, + RT9426A_REG_SWINDOW1 + x * 2); + + dev_info(dev, "fg_data_block_page4"); + dev_info(dev, "0x41:<0x%x>\n", temp[0]); + dev_info(dev, "0x43:<0x%x>\n", temp[1]); + dev_info(dev, "0x45:<0x%x>\n", temp[2]); + dev_info(dev, "0x47:<0x%x>\n", temp[3]); + dev_info(dev, "0x49:<0x%x>\n", temp[4]); + dev_info(dev, "0x4B:<0x%x>\n", temp[5]); + dev_info(dev, "0x4D:<0x%x>\n", temp[6]); + dev_info(dev, "0x4F:<0x%x>\n", temp[7]); + ret = count; + } else + ret = -EINVAL; + break; + case DBP5: + if (sscanf(buf, "%x\n", &x) == 1 && x == 1) { + rt9426a_read_page_cmd(chip, RT9426A_PAGE_5); + for (x = 0; x < 8; x++) + temp[x] = rt9426a_reg_read_word(chip->i2c, + RT9426A_REG_SWINDOW1 + x * 2); + + dev_info(dev, "fg_data_block_page5"); + dev_info(dev, "0x41:<0x%x>\n", temp[0]); + dev_info(dev, "0x43:<0x%x>\n", temp[1]); + dev_info(dev, "0x45:<0x%x>\n", temp[2]); + dev_info(dev, "0x47:<0x%x>\n", temp[3]); + dev_info(dev, "0x49:<0x%x>\n", temp[4]); + dev_info(dev, "0x4B:<0x%x>\n", temp[5]); + dev_info(dev, "0x4D:<0x%x>\n", temp[6]); + dev_info(dev, "0x4F:<0x%x>\n", temp[7]); + ret = count; + } else + ret = -EINVAL; + break; + case DBP6: + if (sscanf(buf, "%x\n", &x) == 1 && x == 1) { + rt9426a_read_page_cmd(chip, RT9426A_PAGE_6); + for (x = 0; x < 8; x++) + temp[x] = rt9426a_reg_read_word(chip->i2c, + RT9426A_REG_SWINDOW1 + x * 2); + + dev_info(dev, "fg_data_block_page6"); + dev_info(dev, "0x41:<0x%x>\n", temp[0]); + dev_info(dev, "0x43:<0x%x>\n", temp[1]); + dev_info(dev, "0x45:<0x%x>\n", temp[2]); + dev_info(dev, "0x47:<0x%x>\n", temp[3]); + dev_info(dev, "0x49:<0x%x>\n", temp[4]); + dev_info(dev, "0x4B:<0x%x>\n", temp[5]); + dev_info(dev, "0x4D:<0x%x>\n", temp[6]); + dev_info(dev, "0x4F:<0x%x>\n", temp[7]); + ret = count; + } else + ret = -EINVAL; + break; + case DBP7: + if (sscanf(buf, "%x\n", &x) == 1 && x == 1) { + rt9426a_read_page_cmd(chip, RT9426A_PAGE_7); + for (x = 0; x < 8; x++) + temp[x] = rt9426a_reg_read_word(chip->i2c, + RT9426A_REG_SWINDOW1 + x * 2); + + dev_info(dev, "fg_data_block_page7"); + dev_info(dev, "0x41:<0x%x>\n", temp[0]); + dev_info(dev, "0x43:<0x%x>\n", temp[1]); + dev_info(dev, "0x45:<0x%x>\n", temp[2]); + dev_info(dev, "0x47:<0x%x>\n", temp[3]); + dev_info(dev, "0x49:<0x%x>\n", temp[4]); + dev_info(dev, "0x4B:<0x%x>\n", temp[5]); + dev_info(dev, "0x4D:<0x%x>\n", temp[6]); + dev_info(dev, "0x4F:<0x%x>\n", temp[7]); + ret = count; + } else + ret = -EINVAL; + break; + case DBP8: + if (sscanf(buf, "%x\n", &x) == 1 && x == 1) { + rt9426a_read_page_cmd(chip, RT9426A_PAGE_8); + for (x = 0; x < 8; x++) + temp[x] = rt9426a_reg_read_word(chip->i2c, + RT9426A_REG_SWINDOW1 + x * 2); + + dev_info(dev, "fg_data_block_page8"); + dev_info(dev, "0x41:<0x%x>\n", temp[0]); + dev_info(dev, "0x43:<0x%x>\n", temp[1]); + dev_info(dev, "0x45:<0x%x>\n", temp[2]); + dev_info(dev, "0x47:<0x%x>\n", temp[3]); + dev_info(dev, "0x49:<0x%x>\n", temp[4]); + dev_info(dev, "0x4B:<0x%x>\n", temp[5]); + dev_info(dev, "0x4D:<0x%x>\n", temp[6]); + dev_info(dev, "0x4F:<0x%x>\n", temp[7]); + ret = count; + } else + ret = -EINVAL; + break; + case DBP9: + if (sscanf(buf, "%x\n", &x) == 1 && x == 1) { + rt9426a_read_page_cmd(chip, RT9426A_PAGE_9); + mdelay(5); + for (x = 0; x < 8; x++) + temp[x] = rt9426a_reg_read_word(chip->i2c, + RT9426A_REG_SWINDOW1 + x*2); + + dev_info(dev, "fg_data_block_page9"); + dev_info(dev, "0x41:<0x%x>\n", temp[0]); + dev_info(dev, "0x43:<0x%x>\n", temp[1]); + dev_info(dev, "0x45:<0x%x>\n", temp[2]); + dev_info(dev, "0x47:<0x%x>\n", temp[3]); + dev_info(dev, "0x49:<0x%x>\n", temp[4]); + dev_info(dev, "0x4B:<0x%x>\n", temp[5]); + dev_info(dev, "0x4D:<0x%x>\n", temp[6]); + dev_info(dev, "0x4F:<0x%x>\n", temp[7]); + ret = count; + } else + ret = -EINVAL; + break; + case WCNTL: + ret = kstrtoint(buf, 10, &val); + if (ret < 0) { + dev_notice(dev, "get paremters fail\n"); + ret = -EINVAL; + } + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, val); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + ret = count; + break; + case WEXTCNTL: + ret = kstrtoint(buf, 10, &val); + if (ret < 0) { + dev_notice(dev, "get paremters fail\n"); + ret = -EINVAL; + } + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_BDCNTL, val); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_BDCNTL, val); + ret = count; + break; + case WSW1: + ret = kstrtoint(buf, 10, &val); + if (ret < 0) { + dev_notice(dev, "get paremters fail\n"); + ret = -EINVAL; + } + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW1, val); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + ret = count; + break; + case WSW2: + ret = kstrtoint(buf, 10, &val); + if (ret < 0) { + dev_notice(dev, "get paremters fail\n"); + ret = -EINVAL; + } + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW2, val); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + ret = count; + break; + case WSW3: + ret = kstrtoint(buf, 10, &val); + if (ret < 0) { + dev_notice(dev, "get paremters fail\n"); + ret = -EINVAL; + } + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW3, val); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + ret = count; + break; + case WSW4: + ret = kstrtoint(buf, 10, &val); + if (ret < 0) { + dev_notice(dev, "get paremters fail\n"); + ret = -EINVAL; + } + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW4, val); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + ret = count; + break; + case WSW5: + ret = kstrtoint(buf, 10, &val); + if (ret < 0) { + dev_notice(dev, "get paremters fail\n"); + ret = -EINVAL; + } + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW5, val); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + ret = count; + break; + case WSW6: + ret = kstrtoint(buf, 10, &val); + if (ret < 0) { + dev_notice(dev, "get paremters fail\n"); + ret = -EINVAL; + } + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW6, val); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + ret = count; + break; + case WSW7: + ret = kstrtoint(buf, 10, &val); + if (ret < 0) { + dev_notice(dev, "get paremters fail\n"); + ret = -EINVAL; + } + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW7, val); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + ret = count; + break; + case WSW8: + ret = kstrtoint(buf, 10, &val); + if (ret < 0) { + dev_notice(dev, "get paremters fail\n"); + ret = -EINVAL; + } + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW8, val); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + ret = count; + break; + case WTEMP: + ret = kstrtoint(buf, 10, &val); + if (ret < 0) { + dev_notice(dev, "get paremters fail\n"); + ret = -EINVAL; + } + rt9426a_reg_write_word(chip->i2c, + RT9426A_REG_TEMP, ((val*10)+2732)); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + ret = count; + break; + case UNSEAL: + ret = kstrtoint(buf, 10, &val); + if (ret < 0) { + dev_notice(dev, "get paremters fail\n"); + ret = -EINVAL; + } + ret = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG3); + if ((ret & 0x0001) == 0) { + /* Unseal RT9426A */ + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, + (RT9426A_Unseal_Key & 0xffff)); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, + (RT9426A_Unseal_Key >> 16)); + rt9426a_reg_write_word(chip->i2c, + RT9426A_REG_DUMMY, 0x0000); + mdelay(10); + + ret = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG3); + if ((ret & 0x0001) == 0) + dev_info(dev, "RT9426A Unseal Fail\n"); + else + dev_info(dev, "RT9426A Unseal Pass\n"); + } else + dev_info(dev, "RT9426A Unseal Pass\n"); + + /* Unseal RT9426A */ + ret = count; + break; + case FG_SET_TEMP: + ret = kstrtoint(buf, 10, &val); + if (ret < 0) { + dev_notice(dev, "get paremters fail\n"); + ret = -EINVAL; + } + rt9426a_temperature_set(chip, val); + ret = count; + break; + case FG_RESET: + rt9426a_reset(chip); + ret = count; + break; + case CALIB_SECURE: + if (kstrtoint(buf, 0, &x)) + return -EINVAL; + chip->calib_unlock = (x == 5526789); + ret = count; + break; + case CALIB_RSENSE: + if (!chip->calib_unlock) + return -EACCES; + /* set rsense according to dtsi */ + rt9426a_apply_sense_resistor(chip); + rt9426a_apply_calibration_para(chip, 0x80, 0x80, 0x80); + ret = count; + break; + case CALIB_FACTOR: + if (!chip->calib_unlock) + return -EACCES; + org = kstrdup(buf, GFP_KERNEL); + if (!org) + return -ENOMEM; + cur = org; + x = 0; + while ((token = strsep(&cur, ",")) != NULL) { + if (kstrtoint(token, 0, &temp[x])) + break; + if (++x >= 3) + break; + } + kfree(org); + if (x != 3) + return -EINVAL; + rt9426a_apply_calibration_para(chip, temp[0], temp[1], temp[2]); + ret = count; + break; + case FORCE_SHUTDOWN: + if (kstrtoint(buf, 0, &x) || x < 0 || x > 1) + return -EINVAL; + + ret = x ? rt9426a_enter_shutdown_mode(chip) : rt9426a_exit_shutdown_mode(chip); + if (ret) + return ret; + ret = count; + break; + case BCCOMP: + if (kstrtoint(buf, 0, &x) || x < 0xCCD || x > 0x7999) + return -EINVAL; + + ret = rt9426a_set_bccomp(chip, x); + if (ret) + return ret; + ret = count; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int rt_fg_create_attrs(struct device *dev) +{ + int i, rc; + + for (i = 0; i < ARRAY_SIZE(rt_fuelgauge_attrs); i++) { + rc = device_create_file(dev, &rt_fuelgauge_attrs[i]); + if (rc) + goto create_attrs_failed; + } + goto create_attrs_succeed; + +create_attrs_failed: + dev_notice(dev, "%s: failed (%d)\n", __func__, rc); + while (i--) + device_remove_file(dev, &rt_fuelgauge_attrs[i]); +create_attrs_succeed: + return rc; +} + +static int rt9426a_irq_enable(struct rt9426a_chip *chip, bool enable) +{ + int regval; + int opcfg2 = 0; + + opcfg2 = chip->pdata->extreg_table[1].data[3] * 256 + + chip->pdata->extreg_table[1].data[2]; + dev_info(chip->dev, "OPCFG2 = 0x%04x\n", opcfg2); + + if (rt9426a_unseal_wi_retry(chip) == RT9426A_UNSEAL_PASS) { + rt9426a_write_page_cmd(chip, RT9426A_PAGE_1); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_SWINDOW2, + enable ? opcfg2 : 0); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(5); + /* if disable, force clear irq status */ + if (!enable) { + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_IRQ); + dev_info(chip->dev, "previous irq status 0x%04x\n", regval); + } + } + + return 0; +} + +static int rt9426a_irq_init(struct rt9426a_chip *chip) +{ + int rc = 0; + + dev_info(chip->dev, "%s\n", __func__); + chip->alert_irq = chip->i2c->irq; + rc = devm_request_threaded_irq(chip->dev, chip->alert_irq, NULL, + rt9426a_irq_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "rt9426a_fg_irq", chip); + if (rc < 0) { + dev_notice(chip->dev, "irq register failed\n"); + return rc; + } + device_init_wakeup(chip->dev, true); + + return 0; +} + +static int rt9426a_irq_deinit(struct rt9426a_chip *chip) +{ + device_init_wakeup(chip->dev, false); + return 0; +} + +static int rt9426a_apply_pdata(struct rt9426a_chip *chip) +{ + int regval = 0, i, retry_times, ret = RT9426A_INIT_PASS, i2c_retry_times; + int reg_flag2 = 0, reg_flag3 = 0, total_checksum_retry = 0; + u16 total_checksum_target=0,fg_total_checksum=0; + u16 page_checksum=0; + u16 page_idx_cmd=0, extend_reg_data=0; + u8 page_idx=0,extend_reg_cmd_addr=0, array_idx=0; + int volt_now = 0, curr_now = 0, volt_comp = 0; + int wt_ocv_result = RT9426A_WRITE_OCV_FAIL; + int ocv_index = 0; + + dev_info(chip->dev, "%s\n", __func__); + + /* read ocv_index before using it */ + ocv_index = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_RSVD2); + /* ocv_index only 0~4 is valid, if read a invalid ocv_index, using the default ocv_index */ + if (ocv_index < 0 || ocv_index > 4) + chip->ocv_index = 0; + else + chip->ocv_index = ocv_index; + dev_info(chip->dev, "%s: ocv_index = %d\n", + __func__, chip->ocv_index); + /* update ocv_checksum_dtsi after ocv_index updated */ + chip->ocv_checksum_dtsi = *((u32 *)chip->pdata->ocv_table + + (chip->ocv_index * 80) + RT9426A_IDX_OF_OCV_CKSUM); + /* add for aging cv ; udpate fcc before writing */ + chip->pdata->extreg_table[2].data[12] = chip->pdata->fcc[chip->ocv_index] & 0xff; + chip->pdata->extreg_table[2].data[13] = (chip->pdata->fcc[chip->ocv_index] >> 8) & 0xff; + /* add for aging cv ; udpate fc_vth before writing */ + chip->pdata->extreg_table[4].data[4]= chip->pdata->fc_vth[chip->ocv_index]; + + /* backup opcfg1 */ + chip->op_config_1_backup = (chip->pdata->extreg_table[1].data[1] * 256) + + chip->pdata->extreg_table[1].data[0]; + retry_times = 30; + for (i = 0 ; i < retry_times ; i++) { + /* Check [RDY]@Reg-Flag2 & [RI]@Reg-Flag3 */ + reg_flag2 = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG2); + reg_flag3 = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG3); + if(((reg_flag2&RT9426A_RDY_MASK)&&(reg_flag2!=0xFFFF))|| + ((reg_flag3&RT9426A_RI_MASK)&&(reg_flag3!=0xFFFF))) { + /* Exit Shutdown Mode before any action */ + /* rt9426a_exit_shutdown_mode(chip); */ + /* Check [RI]@Reg-Flag3 & fg total checksum */ + regval = rt9426a_get_checksum(chip); + total_checksum_target = rt9426a_calculate_checksum_crc(chip); + /* get ocv checksum from fg ic before applying ocv table */ + rt9426a_get_ocv_checksum(chip); + if(reg_flag3&RT9426A_RI_MASK){ + dev_info(chip->dev, "Init RT9426A by [RI]\n"); + break; + } + else if(regval != total_checksum_target){ + dev_info(chip->dev, "Force Init RT9426A by total checksum\n"); + dev_info(chip->dev, "IC: 0x%04X ; DTSI: 0x%04X \n", regval, total_checksum_target); + break; + } + else if (chip->ocv_checksum_ic != chip->ocv_checksum_dtsi){ + dev_info(chip->dev, "Force Init RT9426A by OCV checksum\n"); + dev_info(chip->dev, "IC: 0x%04X ; DTSI: 0x%04X \n", chip->ocv_checksum_ic , chip->ocv_checksum_dtsi); + break; + } + else{ + dev_info(chip->dev, "No need to Init RT9426A\n"); + ret = RT9426A_INIT_BYPASS; + break; + } + } + else if(i == retry_times-1){ + dev_info(chip->dev, "RT9426A Check [RDY] fail.\n"); + } + mdelay(5); + } + /* Bypass to apply RT9426A parameter */ + if(ret < RT9426A_INIT_PASS) + goto BYPASS_EXT_REG; + + if (rt9426a_unseal_wi_retry(chip) == RT9426A_UNSEAL_FAIL){ + ret = RT9426A_INIT_UNSEAL_ERR; + goto END_INIT; + } + + /*NOTICE!! Please follow below setting sequence & order */ + /* write ocv table only when checksum matched */ + /* add for aging cv */ + if(chip->ocv_checksum_ic != chip->ocv_checksum_dtsi) + wt_ocv_result = rt9426a_write_ocv_table(chip); + + /* Read Reg IRQ to reinital Alert pin state */ + rt9426a_reg_read_word(chip->i2c, RT9426A_REG_IRQ); + + /* get calibration parameter */ + /* rt9426a_get_calibration_para(chip, &target_curr_offs, &target_curr_gain, &target_volt_gain); */ + /* assign calibration parameter */ + /* rt9426a_assign_calibration_para(chip, target_curr_offs, target_curr_gain, target_volt_gain); */ + +TOTAL_CHECKSUM_CHECK: + + /* Calculate Checksum & CRC */ + total_checksum_target = rt9426a_calculate_checksum_crc(chip); + + /* Get Total Checksum from RT9426A */ + fg_total_checksum = rt9426a_get_checksum(chip); + + /* Compare Total Checksum with Target */ + dev_info(chip->dev, "Total Checksum: Target = 0x%x, Result = 0x%x\n", + total_checksum_target, fg_total_checksum); + + if (fg_total_checksum == total_checksum_target) { + ret = RT9426A_INIT_BYPASS; + goto BYPASS_EXT_REG; + } + else if (total_checksum_retry > 5) { + dev_info(chip->dev, "Write RT9426A Extend Register Fail\n"); + ret = RT9426A_INIT_CKSUM_ERR; + retry_times = 0; + goto SET_SEAL_CMD; + } + total_checksum_retry++; + + /* Set Ext Register */ + for (page_idx = 0; page_idx < 16; page_idx++) { + if((page_idx != 4) && (page_idx != 9)) { + /* retry count for i2c fail only */ + i2c_retry_times = 0; +PAGE_CHECKSUM1: + /* Send Write Page Command */ + page_idx_cmd = RT9426A_WPAGE_CMD | page_idx; + if (rt9426a_reg_write_word(chip->i2c, RT9426A_REG_BDCNTL, page_idx_cmd) >= 0) { + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_BDCNTL, page_idx_cmd); + retry_times = 0; +PAGE_CHECKSUM2: + mdelay(5); + /* Get Page Checksum from RT9426A */ + page_checksum = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_SWINDOW9); + if (page_idx < 4) + array_idx = page_idx; + else if ((page_idx > 4) && (page_idx < 9)) + array_idx = page_idx - 1; + else if (page_idx > 9) + array_idx = page_idx - 2; + + /* resend write page cmd again if failed */ + if (retry_times > 0) { + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_BDCNTL, page_idx_cmd); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_BDCNTL, page_idx_cmd); + mdelay(1); + } + + if ((page_checksum != g_PAGE_CHKSUM[array_idx]) || (total_checksum_retry>3)) { + /* Update Setting to Extend Page */ + for (i = 0; i < 8; i++){ + extend_reg_cmd_addr = 0x40 + (2 * i); + extend_reg_data = chip->pdata->extreg_table[array_idx].data[2 * i] + + (chip->pdata->extreg_table[array_idx].data[2 * i + 1] << 8); + + rt9426a_reg_write_word(chip->i2c,extend_reg_cmd_addr, extend_reg_data); + } + rt9426a_reg_write_word(chip->i2c,RT9426A_REG_DUMMY, 0x0000); + + retry_times++; + if (retry_times < 5) { + dev_info(chip->dev, "Page Checksum:%d\n",array_idx); + goto PAGE_CHECKSUM2; + } + } + } else { + /* prepare retry for i2c fail */ + i2c_retry_times++; + if(i2c_retry_times<3) + goto PAGE_CHECKSUM1; + } + } + } + goto TOTAL_CHECKSUM_CHECK; + +BYPASS_EXT_REG: + if ((chip->ocv_checksum_ic != chip->ocv_checksum_dtsi)&& + (wt_ocv_result == RT9426A_WRITE_OCV_PASS)) { + retry_times = 30; + for (i = 0 ; i < retry_times ; i++) { + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG2); + if ((reg_flag2&RT9426A_RDY_MASK)&&(reg_flag2!=0xFFFF)) { + /* get current & vbat for vbat compensation ; 2022-01-18 */ + /* step-1. volt_comp = volt_now - ((curr_now * RT9426A_BATTERY_RESISTANCE) / 1000) */ + curr_now = rt9426a_get_current(chip); + volt_now = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_VBAT); + volt_comp = volt_now - + DIV_ROUND_UP(curr_now * RT9426A_BATTERY_RESISTANCE, 1000); + dev_info(chip->dev, "curr_now = %d, volt_now = %d, volt_comp = %d\n", + curr_now, volt_now, volt_comp); + + /* step-2. use volt_comp to lookup ocv table and get init. soc */ + chip->capacity = 100; + for (i = 0; i <= 100; i++) { + if (volt_comp <= chip->pdata->init_ocv_table[i]) { + chip->capacity = i; /* got soc by volt_comp ; index is soc */ + break; + } + } + dev_info(chip->dev, "init. soc = %d\n", chip->capacity); + rt9426a_fg_set_soc(chip, chip->capacity); + break; + } + mdelay(10); + } + + dev_info(chip->dev, "OCV checksum are different, QS is done.\n"); + + } + else + dev_info(chip->dev, "OCV checksum are the same, bypass QS.\n"); + + /* clear RI, set 0 to RI bits */ + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG3); + regval = (regval & ~RT9426A_RI_MASK); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_FLAG3, regval); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG3); + if (((regval & RT9426A_RI_MASK) >> 8) == 0) + dev_info(chip->dev, "RT9426A RI=0\n"); + else + dev_info(chip->dev, "RT9426A RI=1\n"); + + /* Seal RT9426A */ + retry_times = 0; +SET_SEAL_CMD: + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_CNTL, RT9426A_SEAL_CMD); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(1); + regval = rt9426a_reg_read_word(chip->i2c, RT9426A_REG_FLAG3); + if (regval & RT9426A_UNSEAL_STATUS) { + retry_times++; + if (retry_times < 3) { + dev_dbg(chip->dev,"RT9426A Seal Retry-%d\n",retry_times); + goto SET_SEAL_CMD; + } else { + dev_dbg(chip->dev,"RT9426A Seal Fail\n"); + ret = RT9426A_INIT_SEAL_ERR; + } + } else { + dev_dbg(chip->dev,"RT9426A Seal Pass\n"); + if(ret != RT9426A_INIT_CKSUM_ERR) + ret = RT9426A_INIT_PASS; + } +END_INIT: + /* check cyccnt & bccomp */ + /* rt9426a_check_cycle_cnt_for_fg_ini(chip); */ + + /* get initial soc for driver for smooth soc */ + chip->capacity = rt9426a_fg_get_soc(chip,0); + + if (ret == RT9426A_INIT_PASS) { + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_RSVD, chip->pdata->para_version); + rt9426a_reg_write_word(chip->i2c, RT9426A_REG_DUMMY, 0x0000); + mdelay(1); + dev_info(chip->dev, "RT9426A Initial Successful\n"); + chip->online = 1; + } else + dev_info(chip->dev, "RT9426A Initial Fail\n"); + + return ret; +} +static const char *rt_get_battery_serialnumber(struct device *dev) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + const char *battsn_buf; + int retval; + + battsn_buf = NULL; + + if (np) + retval = of_property_read_string(np, "mmi,battid", + &battsn_buf); + else + return NULL; + + if ((retval == -EINVAL) || !battsn_buf) { + dev_info(dev, "Battsn unused\n"); + of_node_put(np); + return NULL; + + } else + dev_info(dev, "Battsn = %s\n", battsn_buf); + + of_node_put(np); + + return battsn_buf; +} + +static struct device_node *rt_get_profile_by_serialnumber(struct device *dev) +{ + struct device_node *node, *df_node, *sn_node; + struct device_node *np = dev->of_node; + const char *sn_buf, *df_sn, *dev_sn; + int rc; + + if (!np) + return NULL; + + dev_sn = NULL; + df_sn = NULL; + sn_buf = NULL; + df_node = NULL; + sn_node = NULL; + + dev_sn = rt_get_battery_serialnumber(dev); + + rc = of_property_read_string(np, "df-serialnum", + &df_sn); + if (rc) + dev_info(dev,"No Default Serial Number defined\n"); + else if (df_sn) + dev_info(dev,"Default Serial Number %s\n", df_sn); + + for_each_child_of_node(np, node) { + rc = of_property_read_string(node, "serialnum", + &sn_buf); + if (!rc && sn_buf) { + if (dev_sn) + if (strnstr(dev_sn, sn_buf, 32)) + sn_node = node; + if (df_sn) + if (strnstr(df_sn, sn_buf, 32)) + df_node = node; + } + } + + if (sn_node) { + node = sn_node; + df_node = NULL; + dev_info(dev,"Battery Match Found using %s\n", sn_node->name); + } else if (df_node) { + node = df_node; + sn_node = NULL; + dev_info(dev,"Battery Match Found using default %s\n", + df_node->name); + } else { + dev_info(dev,"No Battery Match Found!\n"); + return NULL; + } + + return node; +} + +struct dt_offset_params { + int data[3]; +}; + +struct dt_extreg_params { + int edata[3]; +}; + +static int rt_parse_dt(struct device *dev, struct rt9426a_platform_data *pdata) +{ + struct device_node *np = dev->of_node; + struct device_node *batt_profile_node = NULL; + int sizes[3] = {0}, j, ret, i; + struct dt_offset_params *offset_params; + char prop_name[64] = {0}; + + dev_info(dev, "%s\n", __func__); + + ret = of_property_read_u32_array(np, "rt,dtsi_version", pdata->dtsi_version, 2); + if (ret < 0) + pdata->dtsi_version[0] = pdata->dtsi_version[1] = 0; + + ret = of_property_read_u32(np, "rt,para_version", &pdata->para_version); + if (ret < 0) + pdata->para_version = 0; + ret = of_property_read_u32_array(np, "rt,fc_vth-fc_ith", pdata->fc_vth_ith, 6); + if (ret < 0) { + dev_notice(dev, "no fc_vth-fc_ith, use defaut 0x4CAC\n"); + for (i = 0; i < 6; i++) + pdata->fc_vth_ith[i] = 0x0; + } + ret = of_property_read_string(np, "rt,bat_name", + (const char **)&pdata->bat_name); + if (ret < 0) + pdata->bat_name = NULL; + + ret = of_property_read_u32_array(np, "rt,offset_interpolation_order", + pdata->offset_interpolation_order, 2); + if (ret < 0) + pdata->offset_interpolation_order[0] = pdata->offset_interpolation_order[1] = 2; + + sizes[0] = sizes[1] = 0; + ret = of_property_read_u32_array(np, "rt,soc_offset_size", sizes, 2); + if (ret < 0) + dev_notice(dev, "Can't get prop soc_offset_size(%d)\n", ret); + + new_vgcomp_soc_offset_datas(dev, SOC_OFFSET, pdata, sizes[0], sizes[1], 0); + if (pdata->soc_offset.soc_offset_data) { + offset_params = devm_kzalloc(dev, + sizes[0] * sizes[1] * sizeof(struct dt_offset_params), + GFP_KERNEL); + + of_property_read_u32_array(np, "rt,soc_offset_data", + (u32 *)offset_params, sizes[0] * sizes[1] * 3); + for (j = 0; j < sizes[0] * sizes[1]; j++) { + pdata->soc_offset.soc_offset_data[j].x = offset_params[j].data[0]; + pdata->soc_offset.soc_offset_data[j].y = offset_params[j].data[1]; + pdata->soc_offset.soc_offset_data[j].offset = offset_params[j].data[2]; + } + devm_kfree(dev, offset_params); + } + + ret = of_property_read_u32(np, "rt,battery_type", &pdata->battery_type); + if (ret < 0) { + dev_info(dev, "uset default battery_type 4350mV, EDV=3200mV\n"); + pdata->battery_type = 4352; + } + + ret = of_property_read_u32(np, "rt,volt_source", &pdata->volt_source); + if (ret < 0) + pdata->volt_source = RT9426A_REG_AV; + + if (pdata->volt_source == 0) + pdata->volt_source = 0; + else if (pdata->volt_source == 1) + pdata->volt_source = RT9426A_REG_VBAT; + else if (pdata->volt_source == 2) + pdata->volt_source = RT9426A_REG_OCV; + else if (pdata->volt_source == 3) + pdata->volt_source = RT9426A_REG_AV; + else { + dev_notice(dev, "pdata->volt_source is out of range, use 3\n"); + pdata->volt_source = RT9426A_REG_AV; + } + + ret = of_property_read_u32(np, "rt,temp_source", &pdata->temp_source); + if (ret < 0) + pdata->temp_source = 0; + + if (pdata->temp_source == 0) + pdata->temp_source = 0; + else if (pdata->temp_source == 1) + pdata->temp_source = RT9426A_REG_TEMP; + else if (pdata->temp_source == 2) + pdata->temp_source = RT9426A_REG_INTT; + else if (pdata->temp_source == 3) + pdata->temp_source = RT9426A_REG_AT; + else { + dev_notice(dev, "pdata->temp_source is out of range, use 0\n"); + pdata->temp_source = 0; + } + ret = of_property_read_u32(np, "rt,curr_source", &pdata->curr_source); + if (ret < 0) + pdata->curr_source = 0; + if (pdata->curr_source == 0) + pdata->curr_source = 0; + else if (pdata->curr_source == 1) + pdata->curr_source = RT9426A_REG_CURR; + else if (pdata->curr_source == 2) + pdata->curr_source = RT9426A_REG_AI; + else { + dev_notice(dev, "pdata->curr_source is out of range, use 2\n"); + pdata->curr_source = RT9426A_REG_AI; + } + + of_property_read_u32(np, "rt,rs_ic_setting",&pdata->rs_ic_setting); + of_property_read_u32(np, "rt,rs_schematic",&pdata->rs_schematic); + + dev_info(dev, "rs_ic_setting = %d\n", pdata->rs_ic_setting); + dev_info(dev, "rs_schematic = %d\n", pdata->rs_schematic); + + /* add for smooth_soc */ + of_property_read_u32(np, "rt,smooth_soc_en",&pdata->smooth_soc_en); + dev_info(dev, "smooth_soc_en = %d\n", pdata->smooth_soc_en); + + /* add for aging cv */ + /* parse fcc array by 5 element */ + batt_profile_node = rt_get_profile_by_serialnumber(dev); + if (!batt_profile_node) + return -1; + of_property_read_u32(batt_profile_node, "rt,fcc_design",&pdata->fcc_design); + dev_info(dev,"fcc_design = %d\n", pdata->fcc_design); + + ret = of_property_read_u32_array(batt_profile_node, "rt,fcc", pdata->fcc, 5); + if (ret < 0) { + dev_notice(dev, "no FCC property, use defaut 2000\n"); + for (i = 0; i < 5; i++) + pdata->fcc[i] = 2000; + } + /* parse fc_vth array by 5 element */ + ret = of_property_read_u32_array(batt_profile_node, "rt,fg_fc_vth", pdata->fc_vth, 5); + if (ret < 0) { + dev_notice(dev, "no fc_vth property, use default 4200mV\n"); + for (i = 0; i < 5; i++) + pdata->fc_vth[i] = 0x0078; + } + /* parse init_ocv_table array for init soc ; 2022-01-18 */ + ret = of_property_read_u32_array(batt_profile_node, "rt,init_ocv_table", pdata->init_ocv_table, 101); + if (ret < 0) { + dev_notice(dev, "no init ocv table property, use defaut\n"); + } + /* Read Ext. Reg Table for RT9426A */ + ret = of_property_read_u8_array(batt_profile_node, "rt,fg_extreg_table", (u8 *)pdata->extreg_table, 224); + if (ret < 0) { + dev_notice(dev, "no ocv table property\n"); + for (j = 0; j < 15; j++) + for (i = 0; i < 16; i++) + pdata->extreg_table[j].data[i] = 0; + } + /* parse ocv_table array by 80x5 element */ + for (i = 0; i < 5; i++) { + snprintf(prop_name, 64, "rt,fg_ocv_table%d", i); + ret = of_property_read_u32_array(batt_profile_node, prop_name, + (u32 *)pdata->ocv_table + i * 80, 80); + if (ret < 0) + memset32((u32 *)pdata->ocv_table + i * 80, 0, 80); + } + + return 0; +} + +static int rt9426a_i2c_chipid_check(struct rt9426a_chip *chip) +{ + unsigned int val = 0; + int ret; + + ret = regmap_read(chip->regmap, RT9426A_REG_DEVICE_ID, &val); + if (ret < 0) + return ret; + + if (val != RT9426A_DEVICE_ID) { + dev_err(chip->dev, "dev_id not match\n"); + return -ENODEV; + } + + chip->ic_ver = val; + return 0; +} + +static bool rt9426a_is_writeable_reg(struct device *dev, unsigned int reg) +{ + return (reg % 2) ? false : true; +} + +static bool rt9426a_is_readable_reg(struct device *dev, unsigned int reg) +{ + return (reg % 2) ? false : true; +} + +static const struct regmap_config rt9426a_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .writeable_reg = rt9426a_is_writeable_reg, + .readable_reg = rt9426a_is_readable_reg, + .max_register = RT9426A_REG_TOTAL_CHKSUM, + .val_format_endian = REGMAP_ENDIAN_LITTLE, +}; + +static void fg_update_work_func(struct work_struct *work) +{ + struct rt9426a_chip *chip = container_of(work, struct rt9426a_chip, update_work.work); + + dev_info(chip->dev, "%s++\n", __func__); + rt9426a_update_info(chip); + queue_delayed_work(system_power_efficient_wq, &chip->update_work, 20 * HZ); + dev_info(chip->dev, "%s--\n", __func__); +} + +static int rt9426a_i2c_probe(struct i2c_client *i2c) +{ + struct rt9426a_platform_data *pdata = i2c->dev.platform_data; + struct rt9426a_chip *chip; + struct power_supply_config psy_config = {}; + bool use_dt = i2c->dev.of_node; + int ret = 0, i = 0; + + /* alloc memory */ + chip = devm_kzalloc(&i2c->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + if (use_dt) { + pdata = devm_kzalloc(&i2c->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + memcpy(pdata, &def_platform_data, sizeof(*pdata)); + ret = rt_parse_dt(&i2c->dev, pdata); + if (ret < 0) { + dev_notice(&i2c->dev, " rt_parse_dt failed\n"); + return -EINVAL; + } + chip->pdata = i2c->dev.platform_data = pdata; + } else { + if (!pdata) { + dev_notice(&i2c->dev, " no platdata specified\n"); + return -EINVAL; + } + } + + chip->i2c = i2c; + chip->dev = &i2c->dev; + chip->alert_irq = -1; + chip->btemp = 250; + chip->bvolt = 3800; + chip->bcurr = 1000; + chip->design_capacity = 2000; + chip->full_capacity = 2000; + chip->soh = 100; + chip->cyccnt = 0; + chip->ocv_checksum_ic = 0; + chip->ocv_index = 0; /* init as 0 */ + /* add for aging cv */ + chip->ocv_checksum_dtsi = *((u32 *)chip->pdata->ocv_table + + (chip->ocv_index * 80) + RT9426A_IDX_OF_OCV_CKSUM); + + mutex_init(&chip->var_lock); + mutex_init(&chip->update_lock); + INIT_DELAYED_WORK(&chip->update_work, fg_update_work_func); + i2c_set_clientdata(i2c, chip); + + /* rt regmap init */ + chip->regmap = devm_regmap_init_i2c(i2c, &rt9426a_regmap_config); + if (IS_ERR(chip->regmap)) { + dev_notice(chip->dev, "regmap init fail\n"); + ret = PTR_ERR(chip->regmap); + goto destroy_mutex; + } + + /* check chip id first */ + ret = rt9426a_i2c_chipid_check(chip); + if (ret < 0) { + dev_notice(chip->dev, "chip id check fail\n"); + goto destroy_mutex; + } + + /* apply platform data */ + ret = rt9426a_apply_pdata(chip); + if (ret < 0) { + dev_notice(chip->dev, "apply pdata fail\n"); + goto destroy_mutex; + } + + /* fg psy register */ + psy_config.of_node = i2c->dev.of_node; + psy_config.drv_data = chip; + if (pdata->bat_name) + fg_psy_desc.name = pdata->bat_name; + chip->fg_psy = devm_power_supply_register(chip->dev, &fg_psy_desc, &psy_config); + if (IS_ERR(chip->fg_psy)) { + dev_notice(chip->dev, "register batt psy fail\n"); + ret = PTR_ERR(chip->fg_psy); + goto destroy_mutex; + } + + ret = rt_fg_create_attrs(&chip->fg_psy->dev); + if (ret) { + dev_notice(chip->dev, "create attrs fail\n"); + goto destroy_mutex; + } + + /* mask irq before irq register */ + ret = rt9426a_irq_enable(chip, false); + if (ret < 0) { + dev_notice(chip->dev, "scirq mask fail\n"); + goto fail_irq_enable; + } + + ret = rt9426a_irq_init(chip); + if (ret < 0) { + dev_notice(chip->dev, "irq init fail\n"); + goto fail_irq_enable; + } + + ret = rt9426a_irq_enable(chip, true); + if (ret < 0) { + dev_notice(chip->dev, "scirq mask fail\n"); + goto fail_irq_enable; + } + + dev_info(chip->dev, "chip ver = 0x%04x\n", chip->ic_ver); + queue_delayed_work(system_power_efficient_wq, &chip->update_work, 5 * HZ); + + return 0; + +fail_irq_enable: + for (i = 0; i < ARRAY_SIZE(rt_fuelgauge_attrs); i++) + device_remove_file(&chip->fg_psy->dev, &rt_fuelgauge_attrs[i]); +destroy_mutex: + mutex_destroy(&chip->update_lock); + mutex_destroy(&chip->var_lock); + + return ret; +} + +static int rt9426a_i2c_remove(struct i2c_client *i2c) +{ + struct rt9426a_chip *chip = i2c_get_clientdata(i2c); + int i = 0; + + dev_info(chip->dev, "%s\n", __func__); + rt9426a_irq_enable(chip, false); + rt9426a_irq_deinit(chip); + for (i = 0; i < ARRAY_SIZE(rt_fuelgauge_attrs); i++) + device_remove_file(&chip->fg_psy->dev, &rt_fuelgauge_attrs[i]); + mutex_destroy(&chip->update_lock); + mutex_destroy(&chip->var_lock); + + return 0; +} + +static int rt9426a_i2c_suspend(struct device *dev) +{ + struct rt9426a_chip *chip = dev_get_drvdata(dev); + + dev_info(chip->dev, "%s\n", __func__); + if (device_may_wakeup(dev)) + enable_irq_wake(chip->alert_irq); + disable_irq(chip->alert_irq); + + cancel_delayed_work(&chip->update_work); + + return 0; +} + +static int rt9426a_i2c_resume(struct device *dev) +{ + struct rt9426a_chip *chip = dev_get_drvdata(dev); + + dev_info(chip->dev, "%s\n", __func__); + enable_irq(chip->alert_irq); + if (device_may_wakeup(dev)) + disable_irq_wake(chip->alert_irq); + + queue_delayed_work(system_power_efficient_wq, &chip->update_work, msecs_to_jiffies(20)); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(rt9426a_pm_ops, rt9426a_i2c_suspend, rt9426a_i2c_resume); + +static const struct of_device_id rt_match_table[] = { + { .compatible = "richtek,rt9426a", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt_match_table); + +static struct i2c_driver rt9426a_i2c_driver = { + .driver = { + .name = "rt9426a", + .of_match_table = of_match_ptr(rt_match_table), + .pm = &rt9426a_pm_ops, + }, + .probe_new = rt9426a_i2c_probe, + .remove = rt9426a_i2c_remove, +}; +module_i2c_driver(rt9426a_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jeff Chang "); +MODULE_DESCRIPTION("RT9426A Fuelgauge Driver"); +MODULE_VERSION("1.0.1_G"); diff --git a/drivers/power/rt9426a_fg_mmi/rt9426a_battery.h b/drivers/power/rt9426a_fg_mmi/rt9426a_battery.h new file mode 100644 index 000000000000..9341372ccb8c --- /dev/null +++ b/drivers/power/rt9426a_fg_mmi/rt9426a_battery.h @@ -0,0 +1,221 @@ +#ifndef __LINUX_POWER_RT9426A_BATTERY_H +#define __LINUX_POWER_RT9426A_BATTERY_H + +#define RT9426A_DRIVER_VER 0x0001 + +#define RT9426A_Unseal_Key 0x12345678 +#define RT9426A_DEVICE_ID 0x426A +#define RT9426A_EXTREG_SIZE 224 + +#define RT9426A_REG_CNTL 0x00 +#define RT9426A_REG_RSVD 0x02 +#define RT9426A_REG_CURR 0x04 +#define RT9426A_REG_TEMP 0x06 +#define RT9426A_REG_VBAT 0x08 +#define RT9426A_REG_FLAG1 0x0A +#define RT9426A_REG_FLAG2 0x0C +#define RT9426A_REG_DEVICE_ID 0x0E +#define RT9426A_REG_RM 0x10 +#define RT9426A_REG_FCC 0x12 +#define RT9426A_REG_AI 0x14 +#define RT9426A_REG_BCCOMP 0x1A +#define RT9426A_REG_DUMMY 0x1E +#define RT9426A_REG_INTT 0x28 +#define RT9426A_REG_CYC 0x2A +#define RT9426A_REG_SOC 0x2C +#define RT9426A_REG_SOH 0x2E +#define RT9426A_REG_FLAG3 0x30 +#define RT9426A_REG_IRQ 0x36 +#define RT9426A_REG_ADV 0x3A +#define RT9426A_REG_DC 0x3C +#define RT9426A_REG_BDCNTL 0x3E +#define RT9426A_REG_SWINDOW1 0x40 +#define RT9426A_REG_SWINDOW2 0x42 +#define RT9426A_REG_SWINDOW3 0x44 +#define RT9426A_REG_SWINDOW4 0x46 +#define RT9426A_REG_SWINDOW5 0x48 +#define RT9426A_REG_SWINDOW6 0x4A +#define RT9426A_REG_SWINDOW7 0x4C +#define RT9426A_REG_SWINDOW8 0x4E +#define RT9426A_REG_SWINDOW9 0x50 +#define RT9426A_REG_OCV 0x62 +#define RT9426A_REG_AV 0x64 +#define RT9426A_REG_AT 0x66 +#define RT9426A_REG_TOTAL_CHKSUM 0x68 +#define RT9426A_REG_RSVD2 0x7C + +#define RT9426A_BATPRES_MASK 0x0040 +#define RT9426A_RI_MASK 0x0100 +#define RT9426A_BATEXIST_FLAG_MASK 0x8000 +#define RT9426A_USR_TBL_USED_MASK 0x0800 +#define RT9426A_CSCOMP1_OCV_MASK 0x0300 +#define RT9426A_UNSEAL_MASK 0x0001 +#define RT9426A_UNSEAL_STATUS 0x0001 +#define RT9426A_GAUGE_BUSY_MASK 0x0008 + +#define RT9426A_SMOOTH_POLL 20 +#define RT9426A_NORMAL_POLL 30 +#define RT9426A_SOCALRT_MASK 0x20 +#define RT9426A_SOCL_SHFT 0 +#define RT9426A_SOCL_MASK 0x1F +#define RT9426A_SOCL_MAX 32 +#define RT9426A_SOCL_MIN 1 + +#define RT9426A_RDY_MASK 0x0080 + +#define RT9426A_UNSEAL_PASS 0 +#define RT9426A_UNSEAL_FAIL 1 +#define RT9426A_PAGE_0 0 +#define RT9426A_PAGE_1 1 +#define RT9426A_PAGE_2 2 +#define RT9426A_PAGE_3 3 +#define RT9426A_PAGE_4 4 +#define RT9426A_PAGE_5 5 +#define RT9426A_PAGE_6 6 +#define RT9426A_PAGE_7 7 +#define RT9426A_PAGE_8 8 +#define RT9426A_PAGE_9 9 +#define RT9426A_PAGE_10 10 +#define RT9426A_PAGE_11 11 +#define RT9426A_PAGE_12 12 +#define RT9426A_PAGE_13 13 +#define RT9426A_PAGE_14 14 +#define RT9426A_PAGE_15 15 + +#define RT9426A_TOTAL_CHKSUM_CMD 0x9A12 +#define RT9426A_WPAGE_CMD 0x6550 +#define RT9426A_SEAL_CMD 0x0020 + +/* for calibration */ +#define RT9426A_CALI_ENTR_CMD 0x0081 +#define RT9426A_CALI_EXIT_CMD 0x0080 +#define RT9426A_CURR_CONVERT_CMD 0x0009 +#define RT9426A_VOLT_CONVERT_CMD 0x008C +#define RT9426A_CALI_MODE_MASK 0x1000 +#define RT9426A_SYS_TICK_ON_CMD 0xBBA1 +#define RT9426A_SYS_TICK_OFF_CMD 0xBBA0 + +#define RT9426A_CALI_MODE_PASS 0 +#define RT9426A_CALI_MODE_FAIL 1 + +/* for Enter/Exit Shutdown */ +#define RT9426A_SHDN_MASK 0x4000 +#define RT9426A_SHDN_ENTR_CMD 0x64AA +#define RT9426A_SHDN_EXIT_CMD 0x6400 + +#define TA_IS_CONNECTED 1 +#define TA_IS_DISCONNECTED 0 +#define RT9426A_FD_TBL_IDX 4 +#define RT9426A_FD_DATA_IDX 10 +#define RT9426A_FD_BASE 2500 + +#define RT9426A_NEW_RS_UNIT 50 /* unit:0.01mR ; 50 x 0.01 = 0.5mR */ + +/* for Handling of Cycle Cnt & BCCOMP */ +#define RT9426A_SET_CYCCNT_KEY 0xCC01 +/* for checking result of writing ocv */ +#define RT9426A_WRITE_OCV_PASS 0 +#define RT9426A_WRITE_OCV_FAIL (-1) +#define RT9426A_IDX_OF_OCV_CKSUM 76 + +/* for force temp set */ +#define RT9426A_OPCFG1_TEMP_SRC_MASK 0xC000 +#define RT9426A_OPCFG1_TEMP_SRC_HOST 0x4000 + +/* for current compensation to get ocv */ +#define RT9426A_BATTERY_RESISTANCE 100 /* unit: mOhm */ +#define RT9426A_SET_SOC_KEY 0x8600 +#define RT9426A_WRITE_SOC_FAIL (-1) + +struct data_point { + union { + int x; + int voltage; + int soc; + }; + union { + int y; + int temperature; + }; + union { + int z; + int curr; + }; + union { + int w; + int offset; + }; +}; + +struct soc_offset_table { + int soc_voltnr; + int tempnr; + struct data_point *soc_offset_data; +}; + +/* temperature source table */ +enum { + RT9426A_TEMP_FROM_AP, + RT9426A_TEMP_FROM_IC, +}; + +enum { + RT9426A_CV4500_IEOC243 = 0, + RT9426A_CV4500_IEOC670, + RT9426A_CV4200_IEOC243, + RT9426A_CV4530_IEOC506, + RT9426A_CV4530_IEOC560, + RT9426A_CV4530_IEOC965, +}; + +struct fg_ocv_table { + int data[8]; +}; + +struct fg_extreg_table { + u8 data[16]; +}; + +struct rt9426a_platform_data { + u32 dtsi_version[2]; + u32 para_version; + u32 fc_vth_ith[6]; + int soc_offset_size[2]; + struct soc_offset_table soc_offset; + int offset_interpolation_order[2]; + struct fg_extreg_table extreg_table[14]; + int battery_type; + char *bat_name; + int boot_gpio; + int chg_sts_gpio; + int chg_inh_gpio; + int chg_done_gpio; + u32 temp_source; + u32 volt_source; + u32 curr_source; + + /* for current scaling */ + u32 rs_ic_setting; // in unit of 0.01 Ohm + u32 rs_schematic; // in unit of 0.01 Ohm + /* add for smooth soc */ + int smooth_soc_en; + /* add for aging cv */ + u32 fcc[5]; /* for aging_sts=0~4 */ + u32 fc_vth[5]; /* for aging_sts=0~4 */ + u32 ocv_table[5][10][8]; /* for aging_sts=0~4 */ + u32 fcc_design; + /* add for init. soc by volt_comp */ + u32 init_ocv_table[101]; +}; + +enum { + RT9426A_INIT_SEAL_ERR = -6, + RT9426A_INIT_DEV_ID_ERR = -5, + RT9426A_INIT_CKSUM_ERR = -4, + RT9426A_INIT_UNSEAL_ERR = -3, + RT9426A_INIT_BYPASS = -2, + RT9426A_INIT_FAIL = -1, + RT9426A_INIT_PASS = 0, +}; + +#endif /* __LINUX_POWER_RT9426A_BATTERY_H */ diff --git a/drivers/power/rt9467_chg_mmi/Android.mk b/drivers/power/rt9467_chg_mmi/Android.mk new file mode 100644 index 000000000000..08c5860544df --- /dev/null +++ b/drivers/power/rt9467_chg_mmi/Android.mk @@ -0,0 +1,13 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := rt9467_charger.ko + + +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + + + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/rt9467_chg_mmi/Kbuild b/drivers/power/rt9467_chg_mmi/Kbuild new file mode 100644 index 000000000000..07f61b4bfc10 --- /dev/null +++ b/drivers/power/rt9467_chg_mmi/Kbuild @@ -0,0 +1,8 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/rt9467_chg_mmi + +obj-m += rt9467_charger.o + + diff --git a/drivers/power/rt9467_chg_mmi/Makefile b/drivers/power/rt9467_chg_mmi/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/power/rt9467_chg_mmi/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/power/rt9467_chg_mmi/rt9467_charger.c b/drivers/power/rt9467_chg_mmi/rt9467_charger.c new file mode 100644 index 000000000000..6c4e04345e6c --- /dev/null +++ b/drivers/power/rt9467_chg_mmi/rt9467_charger.c @@ -0,0 +1,3345 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Richtek RT9467 Charger Driver + * + * Copyright (C) 2021 Richtek Technology Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_RT_REGMAP +#include +#endif +#include "rt9467_charger.h" + +#define I2C_ACCESS_MAX_RETRY 5 +#define RT9467_DRV_VERSION "1.0.0_G" + +/* ======================= */ +/* RT9467 Parameter */ +/* ======================= */ + +static const u32 rt9467_boost_oc_threshold[] = { + 500000, 700000, 1100000, 1300000, 1800000, 2100000, 2400000, 3000000, +}; /* uA */ + +static const u32 rt9467_safety_timer[] = { + 4, 6, 8, 10, 12, 14, 16, 20, +}; /* hour */ + +enum rt9467_irq_idx { + RT9467_IRQIDX_CHG_STATC = 0, + RT9467_IRQIDX_CHG_FAULT, + RT9467_IRQIDX_TS_STATC, + RT9467_IRQIDX_CHG_IRQ1, + RT9467_IRQIDX_CHG_IRQ2, + RT9467_IRQIDX_CHG_IRQ3, + RT9467_IRQIDX_DPDM_IRQ, + RT9467_IRQIDX_MAX, +}; + +enum rt9467_irq_stat { + RT9467_IRQSTAT_CHG_STATC = 0, + RT9467_IRQSTAT_CHG_FAULT, + RT9467_IRQSTAT_TS_STATC, + RT9467_IRQSTAT_MAX, +}; + +enum rt9467_chg_type { + RT9467_CHG_TYPE_NOVBUS = 0, + RT9467_CHG_TYPE_UNDER_GOING, + RT9467_CHG_TYPE_SDP, + RT9467_CHG_TYPE_SDPNSTD, + RT9467_CHG_TYPE_DCP, + RT9467_CHG_TYPE_CDP, + RT9467_CHG_TYPE_MAX, +}; + +enum rt9467_usbsw_state { + RT9467_USBSW_CHG = 0, + RT9467_USBSW_USB, +}; + +static const u8 rt9467_irq_maskall[RT9467_IRQIDX_MAX] = { + 0xF0, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +}; + +struct irq_mapping_tbl { + const char *name; + const int id; +}; + +#define RT9467_IRQ_MAPPING(_name, _id) {.name = #_name, .id = _id} +static const struct irq_mapping_tbl rt9467_irq_mapping_tbl[] = { + RT9467_IRQ_MAPPING(chg_treg, 4), + RT9467_IRQ_MAPPING(chg_aicr, 5), + RT9467_IRQ_MAPPING(chg_mivr, 6), + RT9467_IRQ_MAPPING(pwr_rdy, 7), + RT9467_IRQ_MAPPING(chg_vsysuv, 12), + RT9467_IRQ_MAPPING(chg_vsysov, 13), + RT9467_IRQ_MAPPING(chg_vbatov, 14), + RT9467_IRQ_MAPPING(chg_vbusov, 15), + RT9467_IRQ_MAPPING(ts_batcold, 20), + RT9467_IRQ_MAPPING(ts_batcool, 21), + RT9467_IRQ_MAPPING(ts_batwarm, 22), + RT9467_IRQ_MAPPING(ts_bathot, 23), + RT9467_IRQ_MAPPING(ts_statci, 24), + RT9467_IRQ_MAPPING(chg_faulti, 25), + RT9467_IRQ_MAPPING(chg_statci, 26), + RT9467_IRQ_MAPPING(chg_tmri, 27), + RT9467_IRQ_MAPPING(chg_batabsi, 28), + RT9467_IRQ_MAPPING(chg_adpbadi, 29), + RT9467_IRQ_MAPPING(chg_rvpi, 30), + RT9467_IRQ_MAPPING(otpi, 31), + RT9467_IRQ_MAPPING(chg_aiclmeasi, 32), + RT9467_IRQ_MAPPING(chg_ichgmeasi, 33), + RT9467_IRQ_MAPPING(chgdet_donei, 34), + RT9467_IRQ_MAPPING(wdtmri, 35), + RT9467_IRQ_MAPPING(ssfinishi, 36), + RT9467_IRQ_MAPPING(chg_rechgi, 37), + RT9467_IRQ_MAPPING(chg_termi, 38), + RT9467_IRQ_MAPPING(chg_ieoci, 39), + RT9467_IRQ_MAPPING(adc_donei, 40), + RT9467_IRQ_MAPPING(pumpx_donei, 41), + RT9467_IRQ_MAPPING(bst_batuvi, 45), + RT9467_IRQ_MAPPING(bst_midovi, 46), + RT9467_IRQ_MAPPING(bst_olpi, 47), + RT9467_IRQ_MAPPING(attachi, 48), + RT9467_IRQ_MAPPING(detachi, 49), + RT9467_IRQ_MAPPING(chgdeti, 54), + RT9467_IRQ_MAPPING(dcdti, 55), +}; + +enum rt9467_charging_status { + RT9467_CHG_STATUS_READY = 0, + RT9467_CHG_STATUS_PROGRESS, + RT9467_CHG_STATUS_DONE, + RT9467_CHG_STATUS_FAULT, + RT9467_CHG_STATUS_MAX, +}; + +static const char *rt9467_chg_status_name[RT9467_CHG_STATUS_MAX] = { + "ready", "progress", "done", "fault", +}; + +static const u8 rt9467_val_en_hidden_mode[] = { + 0x49, 0x32, 0xB6, 0x27, 0x48, 0x18, 0x03, 0xE2, +}; + +enum rt9467_iin_limit_sel { + RT9467_IINLMTSEL_3_2A = 0, + RT9467_IINLMTSEL_CHG_TYP, + RT9467_IINLMTSEL_AICR, + RT9467_IINLMTSEL_LOWER_LEVEL, /* lower of above three */ +}; + +enum rt9467_adc_sel { + RT9467_ADC_VBUS_DIV5 = 1, + RT9467_ADC_VBUS_DIV2, + RT9467_ADC_VSYS, + RT9467_ADC_VBAT, + RT9467_ADC_TS_BAT = 6, + RT9467_ADC_IBUS = 8, + RT9467_ADC_IBAT, + RT9467_ADC_REGN = 11, + RT9467_ADC_TEMP_JC, + RT9467_ADC_MAX, +}; + +/* + * Unit for each ADC parameter + * 0 stands for reserved + * For TS_BAT, the real unit is 0.25. + * Here we use 25, please remember to divide 100 while showing the value + */ +static const int rt9467_adc_unit[RT9467_ADC_MAX] = { + 0, + RT9467_ADC_UNIT_VBUS_DIV5, + RT9467_ADC_UNIT_VBUS_DIV2, + RT9467_ADC_UNIT_VSYS, + RT9467_ADC_UNIT_VBAT, + 0, + RT9467_ADC_UNIT_TS_BAT, + 0, + RT9467_ADC_UNIT_IBUS, + RT9467_ADC_UNIT_IBAT, + 0, + RT9467_ADC_UNIT_REGN, + RT9467_ADC_UNIT_TEMP_JC, +}; + +static const int rt9467_adc_offset[RT9467_ADC_MAX] = { + 0, + RT9467_ADC_OFFSET_VBUS_DIV5, + RT9467_ADC_OFFSET_VBUS_DIV2, + RT9467_ADC_OFFSET_VSYS, + RT9467_ADC_OFFSET_VBAT, + 0, + RT9467_ADC_OFFSET_TS_BAT, + 0, + RT9467_ADC_OFFSET_IBUS, + RT9467_ADC_OFFSET_IBAT, + 0, + RT9467_ADC_OFFSET_REGN, + RT9467_ADC_OFFSET_TEMP_JC, +}; + +struct rt9467_desc { + u32 ichg; /* uA */ + u32 aicr; /* uA */ + u32 mivr; /* uV */ + u32 cv; /* uV */ + u32 ieoc; /* uA */ + u32 safety_timer; /* hour */ + u32 ircmp_resistor; /* uohm */ + u32 ircmp_vclamp; /* uV */ + bool en_te; + bool en_wdt; + bool en_irq_pulse; + bool en_jeita; + bool en_chgdet; + int regmap_represent_slave_addr; + const char *regmap_name; + const char *chg_dev_name; +}; + +/* These default values will be applied if there's no property in dts */ +static struct rt9467_desc rt9467_default_desc = { + .ichg = 2000000, /* uA */ + .aicr = 500000, /* uA */ + .mivr = 4400000, /* uV */ + .cv = 4350000, /* uA */ + .ieoc = 250000, /* uA */ + .safety_timer = 12, + .ircmp_resistor = 25000, /* uohm */ + .ircmp_vclamp = 32000, /* uV */ + .en_te = true, + .en_wdt = true, + .en_irq_pulse = false, + .en_jeita = false, + .en_chgdet = true, + .regmap_represent_slave_addr = RT9467_SLAVE_ADDR, + .regmap_name = "rt9467", + .chg_dev_name = "primary_chg", +}; + +struct rt9467_info { + struct i2c_client *client; + struct mutex i2c_access_lock; + struct mutex adc_access_lock; + struct mutex irq_access_lock; + struct mutex aicr_access_lock; + struct mutex ichg_access_lock; + struct mutex hidden_mode_lock; + struct mutex bc12_access_lock; + struct mutex ieoc_lock; + struct mutex tchg_lock; + struct mutex pp_lock; + struct device *dev; + struct rt9467_info *info; + struct rt9467_desc *desc; + struct power_supply *chg_psy; + struct power_supply_desc chg_psy_desc; + wait_queue_head_t wait_queue; + int irq; + u32 power_limit; + u8 chip_rev; + u8 irq_flag[RT9467_IRQIDX_MAX]; + u8 irq_stat[RT9467_IRQSTAT_MAX]; + u8 irq_mask[RT9467_IRQIDX_MAX]; + u32 hidden_mode_cnt; + bool pp_en; + bool bc12_en; + bool pwr_rdy; + u32 mivr; + u32 ieoc; + u32 ichg; + bool ieoc_wkard; + struct work_struct init_work; + struct delayed_work kick_wdt_dwork; + atomic_t bc12_sdp_cnt; + atomic_t bc12_wkard; + int tchg; + +#if IS_ENABLED(CONFIG_TCPC_CLASS) + atomic_t tcpc_usb_connected; +#else + struct work_struct chgdet_work; +#endif /* CONFIG_TCPC_CLASS */ + +#ifdef CONFIG_RT_REGMAP + struct rt_regmap_device *regmap_dev; + struct rt_regmap_properties *regmap_prop; +#endif /* CONFIG_RT_REGMAP */ +}; + +/* ======================= */ +/* Register Address */ +/* ======================= */ + +static const unsigned char rt9467_reg_addr[] = { + RT9467_REG_CORE_CTRL0, + RT9467_REG_CHG_CTRL1, + RT9467_REG_CHG_CTRL2, + RT9467_REG_CHG_CTRL3, + RT9467_REG_CHG_CTRL4, + RT9467_REG_CHG_CTRL5, + RT9467_REG_CHG_CTRL6, + RT9467_REG_CHG_CTRL7, + RT9467_REG_CHG_CTRL8, + RT9467_REG_CHG_CTRL9, + RT9467_REG_CHG_CTRL10, + RT9467_REG_CHG_CTRL11, + RT9467_REG_CHG_CTRL12, + RT9467_REG_CHG_CTRL13, + RT9467_REG_CHG_CTRL14, + RT9467_REG_CHG_CTRL15, + RT9467_REG_CHG_CTRL16, + RT9467_REG_CHG_ADC, + RT9467_REG_CHG_DPDM1, + RT9467_REG_CHG_DPDM2, + RT9467_REG_CHG_DPDM3, + RT9467_REG_CHG_CTRL19, + RT9467_REG_CHG_CTRL17, + RT9467_REG_CHG_CTRL18, + RT9467_REG_DEVICE_ID, + RT9467_REG_CHG_STAT, + RT9467_REG_CHG_NTC, + RT9467_REG_ADC_DATA_H, + RT9467_REG_ADC_DATA_L, + RT9467_REG_ADC_DATA_TUNE_H, + RT9467_REG_ADC_DATA_TUNE_L, + RT9467_REG_ADC_DATA_ORG_H, + RT9467_REG_ADC_DATA_ORG_L, + RT9467_REG_CHG_STATC, + RT9467_REG_CHG_FAULT, + RT9467_REG_TS_STATC, + /* Skip IRQ evt to prevent reading clear while dumping registers */ + RT9467_REG_CHG_STATC_CTRL, + RT9467_REG_CHG_FAULT_CTRL, + RT9467_REG_TS_STATC_CTRL, + RT9467_REG_CHG_IRQ1_CTRL, + RT9467_REG_CHG_IRQ2_CTRL, + RT9467_REG_CHG_IRQ3_CTRL, + RT9467_REG_DPDM_IRQ_CTRL, +}; + +/* ========= */ +/* RT Regmap */ +/* ========= */ + +#ifdef CONFIG_RT_REGMAP +RT_REG_DECL(RT9467_REG_CORE_CTRL0, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL1, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL2, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL3, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL4, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL5, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL6, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL7, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL8, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL9, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL10, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL11, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL12, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL13, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL14, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL15, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL16, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_ADC, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_DPDM1, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_DPDM2, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_DPDM3, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL19, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL17, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_CTRL18, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_HIDDEN_CTRL1, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_HIDDEN_CTRL2, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_HIDDEN_CTRL4, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_HIDDEN_CTRL6, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_HIDDEN_CTRL7, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_HIDDEN_CTRL8, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_HIDDEN_CTRL9, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_HIDDEN_CTRL15, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_DEVICE_ID, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_STAT, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_NTC, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_ADC_DATA_H, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_ADC_DATA_L, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_ADC_DATA_TUNE_H, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_ADC_DATA_TUNE_L, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_ADC_DATA_ORG_H, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_ADC_DATA_ORG_L, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_STATC, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_FAULT, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_TS_STATC, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_IRQ1, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_IRQ2, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_IRQ3, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_DPDM_IRQ, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_STATC_CTRL, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_FAULT_CTRL, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_TS_STATC_CTRL, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_IRQ1_CTRL, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_IRQ2_CTRL, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_CHG_IRQ3_CTRL, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9467_REG_DPDM_IRQ_CTRL, 1, RT_VOLATILE, {}); + +static const rt_register_map_t rt9467_regmap_map[] = { + RT_REG(RT9467_REG_CORE_CTRL0), + RT_REG(RT9467_REG_CHG_CTRL1), + RT_REG(RT9467_REG_CHG_CTRL2), + RT_REG(RT9467_REG_CHG_CTRL3), + RT_REG(RT9467_REG_CHG_CTRL4), + RT_REG(RT9467_REG_CHG_CTRL5), + RT_REG(RT9467_REG_CHG_CTRL6), + RT_REG(RT9467_REG_CHG_CTRL7), + RT_REG(RT9467_REG_CHG_CTRL8), + RT_REG(RT9467_REG_CHG_CTRL9), + RT_REG(RT9467_REG_CHG_CTRL10), + RT_REG(RT9467_REG_CHG_CTRL11), + RT_REG(RT9467_REG_CHG_CTRL12), + RT_REG(RT9467_REG_CHG_CTRL13), + RT_REG(RT9467_REG_CHG_CTRL14), + RT_REG(RT9467_REG_CHG_CTRL15), + RT_REG(RT9467_REG_CHG_CTRL16), + RT_REG(RT9467_REG_CHG_ADC), + RT_REG(RT9467_REG_CHG_DPDM1), + RT_REG(RT9467_REG_CHG_DPDM2), + RT_REG(RT9467_REG_CHG_DPDM3), + RT_REG(RT9467_REG_CHG_CTRL19), + RT_REG(RT9467_REG_CHG_CTRL17), + RT_REG(RT9467_REG_CHG_CTRL18), + RT_REG(RT9467_REG_CHG_HIDDEN_CTRL1), + RT_REG(RT9467_REG_CHG_HIDDEN_CTRL2), + RT_REG(RT9467_REG_CHG_HIDDEN_CTRL4), + RT_REG(RT9467_REG_CHG_HIDDEN_CTRL6), + RT_REG(RT9467_REG_CHG_HIDDEN_CTRL7), + RT_REG(RT9467_REG_CHG_HIDDEN_CTRL8), + RT_REG(RT9467_REG_CHG_HIDDEN_CTRL9), + RT_REG(RT9467_REG_CHG_HIDDEN_CTRL15), + RT_REG(RT9467_REG_DEVICE_ID), + RT_REG(RT9467_REG_CHG_STAT), + RT_REG(RT9467_REG_CHG_NTC), + RT_REG(RT9467_REG_ADC_DATA_H), + RT_REG(RT9467_REG_ADC_DATA_L), + RT_REG(RT9467_REG_ADC_DATA_TUNE_H), + RT_REG(RT9467_REG_ADC_DATA_TUNE_L), + RT_REG(RT9467_REG_ADC_DATA_ORG_H), + RT_REG(RT9467_REG_ADC_DATA_ORG_L), + RT_REG(RT9467_REG_CHG_STATC), + RT_REG(RT9467_REG_CHG_FAULT), + RT_REG(RT9467_REG_TS_STATC), + RT_REG(RT9467_REG_CHG_IRQ1), + RT_REG(RT9467_REG_CHG_IRQ2), + RT_REG(RT9467_REG_CHG_IRQ3), + RT_REG(RT9467_REG_DPDM_IRQ), + RT_REG(RT9467_REG_CHG_STATC_CTRL), + RT_REG(RT9467_REG_CHG_FAULT_CTRL), + RT_REG(RT9467_REG_TS_STATC_CTRL), + RT_REG(RT9467_REG_CHG_IRQ1_CTRL), + RT_REG(RT9467_REG_CHG_IRQ2_CTRL), + RT_REG(RT9467_REG_CHG_IRQ3_CTRL), + RT_REG(RT9467_REG_DPDM_IRQ_CTRL), +}; +#endif /* CONFIG_RT_REGMAP */ + +/* ========================= */ +/* I2C operations */ +/* ========================= */ + +static int rt9467_device_read(void *client, u32 addr, int leng, void *dst) +{ + struct i2c_client *i2c = (struct i2c_client *)client; + + return i2c_smbus_read_i2c_block_data(i2c, addr, leng, dst); +} + +static int rt9467_device_write(void *client, u32 addr, int leng, + const void *src) +{ + struct i2c_client *i2c = (struct i2c_client *)client; + + return i2c_smbus_write_i2c_block_data(i2c, addr, leng, src); +} + +#ifdef CONFIG_RT_REGMAP +static struct rt_regmap_fops rt9467_regmap_fops = { + .read_device = rt9467_device_read, + .write_device = rt9467_device_write, +}; + +static int rt9467_register_rt_regmap(struct rt9467_info *info) +{ + int ret = 0; + struct i2c_client *client = info->client; + struct rt_regmap_properties *prop = NULL; + + dev_info(info->dev, "%s\n", __func__); + + prop = devm_kzalloc(&client->dev, sizeof(struct rt_regmap_properties), + GFP_KERNEL); + if (!prop) + return -ENOMEM; + + prop->name = info->desc->regmap_name; + prop->aliases = info->desc->regmap_name; + prop->register_num = ARRAY_SIZE(rt9467_regmap_map); + prop->rm = rt9467_regmap_map; + prop->rt_regmap_mode = RT_SINGLE_BYTE | RT_CACHE_DISABLE | + RT_IO_PASS_THROUGH; + prop->io_log_en = 0; + + info->regmap_prop = prop; + info->regmap_dev = rt_regmap_device_register_ex(info->regmap_prop, + &rt9467_regmap_fops, &client->dev, client, + info->desc->regmap_represent_slave_addr, info); + if (!info->regmap_dev) { + dev_notice(info->dev, "%s: register regmap dev fail\n", + __func__); + return -EIO; + } + + return ret; +} +#endif /* CONFIG_RT_REGMAP */ + +static inline int __rt9467_i2c_write_byte(struct rt9467_info *info, u8 cmd, + u8 data) +{ + int ret = 0, retry = 0; + + do { +#ifdef CONFIG_RT_REGMAP + ret = rt_regmap_block_write(info->regmap_dev, cmd, 1, &data); +#else + ret = rt9467_device_write(info->client, cmd, 1, &data); +#endif + retry++; + if (ret < 0) + udelay(10); + } while (ret < 0 && retry < I2C_ACCESS_MAX_RETRY); + + if (ret < 0) + dev_notice(info->dev, "%s: I2CW[0x%02X] = 0x%02X fail\n", + __func__, cmd, data); + else + dev_dbg(info->dev, "%s: I2CW[0x%02X] = 0x%02X\n", __func__, + cmd, data); + + return ret; +} + +static int rt9467_i2c_write_byte(struct rt9467_info *info, u8 cmd, u8 data) +{ + int ret = 0; + + mutex_lock(&info->i2c_access_lock); + ret = __rt9467_i2c_write_byte(info, cmd, data); + mutex_unlock(&info->i2c_access_lock); + + return ret; +} + +static inline int __rt9467_i2c_read_byte(struct rt9467_info *info, u8 cmd) +{ + int ret = 0, ret_val = 0, retry = 0; + + do { +#ifdef CONFIG_RT_REGMAP + ret = rt_regmap_block_read(info->regmap_dev, cmd, 1, &ret_val); +#else + ret = rt9467_device_read(info->client, cmd, 1, &ret_val); +#endif + retry++; + if (ret < 0) + udelay(10); + } while (ret < 0 && retry < I2C_ACCESS_MAX_RETRY); + + if (ret < 0) { + dev_notice(info->dev, "%s: I2CR[0x%02X] fail\n", __func__, cmd); + return ret; + } + + ret_val = ret_val & 0xFF; + + dev_dbg(info->dev, "%s: I2CR[0x%02X] = 0x%02X\n", __func__, cmd, + ret_val); + + return ret_val; +} + +static int rt9467_i2c_read_byte(struct rt9467_info *info, u8 cmd) +{ + int ret = 0; + + mutex_lock(&info->i2c_access_lock); + ret = __rt9467_i2c_read_byte(info, cmd); + mutex_unlock(&info->i2c_access_lock); + + if (ret < 0) + return ret; + + return (ret & 0xFF); +} + +static inline int __rt9467_i2c_block_write(struct rt9467_info *info, u8 cmd, + u32 leng, const u8 *data) +{ + int ret = 0; + +#ifdef CONFIG_RT_REGMAP + ret = rt_regmap_block_write(info->regmap_dev, cmd, leng, data); +#else + ret = rt9467_device_write(info->client, cmd, leng, data); +#endif + + return ret; +} + + +static int rt9467_i2c_block_write(struct rt9467_info *info, u8 cmd, u32 leng, + const u8 *data) +{ + int ret = 0; + + mutex_lock(&info->i2c_access_lock); + ret = __rt9467_i2c_block_write(info, cmd, leng, data); + mutex_unlock(&info->i2c_access_lock); + + return ret; +} + +static inline int __rt9467_i2c_block_read(struct rt9467_info *info, u8 cmd, + u32 leng, u8 *data) +{ + int ret = 0; + +#ifdef CONFIG_RT_REGMAP + ret = rt_regmap_block_read(info->regmap_dev, cmd, leng, data); +#else + ret = rt9467_device_read(info->client, cmd, leng, data); +#endif + + return ret; +} + + +static int rt9467_i2c_block_read(struct rt9467_info *info, u8 cmd, u32 leng, + u8 *data) +{ + int ret = 0; + + mutex_lock(&info->i2c_access_lock); + ret = __rt9467_i2c_block_read(info, cmd, leng, data); + mutex_unlock(&info->i2c_access_lock); + + return ret; +} + + +static int rt9467_i2c_test_bit(struct rt9467_info *info, u8 cmd, u8 shift, + bool *is_one) +{ + int ret = 0; + u8 data = 0; + + ret = rt9467_i2c_read_byte(info, cmd); + if (ret < 0) { + *is_one = false; + return ret; + } + + data = ret & (1 << shift); + *is_one = (data == 0 ? false : true); + + return ret; +} + +static int rt9467_i2c_update_bits(struct rt9467_info *info, u8 cmd, u8 data, + u8 mask) +{ + int ret = 0; + u8 reg_data = 0; + + mutex_lock(&info->i2c_access_lock); + ret = __rt9467_i2c_read_byte(info, cmd); + if (ret < 0) { + mutex_unlock(&info->i2c_access_lock); + return ret; + } + + reg_data = ret & 0xFF; + reg_data &= ~mask; + reg_data |= (data & mask); + + ret = __rt9467_i2c_write_byte(info, cmd, reg_data); + mutex_unlock(&info->i2c_access_lock); + + return ret; +} + +static inline int rt9467_set_bit(struct rt9467_info *info, u8 reg, u8 mask) +{ + return rt9467_i2c_update_bits(info, reg, mask, mask); +} + +static inline int rt9467_clr_bit(struct rt9467_info *info, u8 reg, u8 mask) +{ + return rt9467_i2c_update_bits(info, reg, 0x00, mask); +} + +/* ================== */ +/* Internal Functions */ +/* ================== */ +static int rt9467_get_mivr(struct rt9467_info *info, u32 *mivr); +static int rt9467_get_aicr(struct rt9467_info *info, u32 *aicr); +static int rt9467_get_ichg(struct rt9467_info *info, u32 *ichg); +static int rt9467_kick_wdt(struct rt9467_info *info); +static int rt9467_get_ieoc(struct rt9467_info *info, u32 *ieoc); +static int rt9467_enable_hidden_mode(struct rt9467_info *info, bool en); + +static inline void rt9467_irq_set_flag(struct rt9467_info *info, u8 *irq, + u8 mask) +{ + mutex_lock(&info->irq_access_lock); + *irq |= mask; + mutex_unlock(&info->irq_access_lock); +} + +static inline void rt9467_irq_clr_flag(struct rt9467_info *info, u8 *irq, + u8 mask) +{ + mutex_lock(&info->irq_access_lock); + *irq &= ~mask; + mutex_unlock(&info->irq_access_lock); +} + +static inline const char *rt9467_get_irq_name(struct rt9467_info *info, + int irqnum) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(rt9467_irq_mapping_tbl); i++) { + if (rt9467_irq_mapping_tbl[i].id == irqnum) + return rt9467_irq_mapping_tbl[i].name; + } + + return "not found"; +} + +static inline void rt9467_irq_mask(struct rt9467_info *info, int irqnum) +{ + dev_dbg(info->dev, "%s: irq = %d, %s\n", __func__, irqnum, + rt9467_get_irq_name(info, irqnum)); + info->irq_mask[irqnum / 8] |= (1 << (irqnum % 8)); +} + +static inline void rt9467_irq_unmask(struct rt9467_info *info, int irqnum) +{ + dev_dbg(info->dev, "%s: irq = %d, %s\n", __func__, irqnum, + rt9467_get_irq_name(info, irqnum)); + info->irq_mask[irqnum / 8] &= ~(1 << (irqnum % 8)); +} + +static inline u8 rt9467_closest_reg(u32 min, u32 max, u32 step, u32 target) +{ + /* Smaller than minimum supported value, use minimum one */ + if (target < min) + return 0; + + /* Greater than maximum supported value, use maximum one */ + if (target >= max) + return (max - min) / step; + + return (target - min) / step; +} + +static inline u8 rt9467_closest_reg_via_tbl(const u32 *tbl, u32 tbl_size, + u32 target) +{ + u32 i = 0; + + /* Smaller than minimum supported value, use minimum one */ + if (target < tbl[0]) + return 0; + + for (i = 0; i < tbl_size - 1; i++) { + if (target >= tbl[i] && target < tbl[i + 1]) + return i; + } + + /* Greater than maximum supported value, use maximum one */ + return tbl_size - 1; +} + +static inline u32 rt9467_closest_value(u32 min, u32 max, u32 step, u8 reg_val) +{ + u32 ret_val = 0; + + ret_val = min + reg_val * step; + if (ret_val > max) + ret_val = max; + + return ret_val; +} + +static int rt9467_get_adc(struct rt9467_info *info, + enum rt9467_adc_sel adc_sel, int *adc_val) +{ + int ret = 0, i = 0; + const int max_wait_times = 6; + u8 adc_data[6] = {0}; + u32 aicr = 0, ichg = 0; + bool adc_start = false; + + mutex_lock(&info->adc_access_lock); + + rt9467_enable_hidden_mode(info, true); + + /* Select ADC to desired channel */ + ret = rt9467_i2c_update_bits(info, RT9467_REG_CHG_ADC, + adc_sel << RT9467_SHIFT_ADC_IN_SEL, RT9467_MASK_ADC_IN_SEL); + if (ret < 0) { + dev_notice(info->dev, "%s: select ch to %d fail(%d)\n", + __func__, adc_sel, ret); + goto out; + } + + /* Workaround for IBUS & IBAT */ + if (adc_sel == RT9467_ADC_IBUS) { + mutex_lock(&info->aicr_access_lock); + ret = rt9467_get_aicr(info, &aicr); + if (ret < 0) { + dev_notice(info->dev, "%s: get aicr fail\n", __func__); + goto out_unlock_all; + } + } else if (adc_sel == RT9467_ADC_IBAT) { + mutex_lock(&info->ichg_access_lock); + ret = rt9467_get_ichg(info, &ichg); + if (ret < 0) { + dev_notice(info->dev, "%s: get ichg fail\n", __func__); + goto out_unlock_all; + } + } + + /* Start ADC conversation */ + ret = rt9467_set_bit(info, RT9467_REG_CHG_ADC, RT9467_MASK_ADC_START); + if (ret < 0) { + dev_notice(info->dev, "%s: start con fail(%d), sel = %d\n", + __func__, ret, adc_sel); + goto out_unlock_all; + } + + for (i = 0; i < max_wait_times; i++) { + msleep(35); + ret = rt9467_i2c_test_bit(info, RT9467_REG_CHG_ADC, + RT9467_SHIFT_ADC_START, &adc_start); + if (ret >= 0 && !adc_start) + break; + } + if (i == max_wait_times) { + dev_notice(info->dev, "%s: wait con fail(%d), sel = %d\n", + __func__, ret, adc_sel); + ret = -EINVAL; + goto out_unlock_all; + } + + mdelay(1); + + /* Read ADC data high/low byte */ + ret = rt9467_i2c_block_read(info, RT9467_REG_ADC_DATA_H, 6, adc_data); + if (ret < 0) { + dev_notice(info->dev, "%s: read ADC data fail\n", __func__); + goto out_unlock_all; + } + dev_dbg(info->dev, + "%s: adc_tune = (0x%02X, 0x%02X), adc_org = (0x%02X, 0x%02X)\n", + __func__, adc_data[2], adc_data[3], adc_data[4], adc_data[5]); + + /* Calculate ADC value */ + *adc_val = ((adc_data[0] << 8) + adc_data[1]) * rt9467_adc_unit[adc_sel] + + rt9467_adc_offset[adc_sel]; + + dev_dbg(info->dev, + "%s: adc_sel = %d, adc_h = 0x%02X, adc_l = 0x%02X, val = %d\n", + __func__, adc_sel, adc_data[0], adc_data[1], *adc_val); + + ret = 0; + +out_unlock_all: + /* Coefficient of IBUS & IBAT */ + if (adc_sel == RT9467_ADC_IBUS) { + if (aicr < 400000) /* 400mA */ + *adc_val = *adc_val * 67 / 100; + mutex_unlock(&info->aicr_access_lock); + } else if (adc_sel == RT9467_ADC_IBAT) { + if (ichg >= 100000 && ichg <= 450000) /* 100~450mA */ + *adc_val = *adc_val * 57 / 100; + else if (ichg >= 500000 && ichg <= 850000) /* 500~850mA */ + *adc_val = *adc_val * 63 / 100; + mutex_unlock(&info->ichg_access_lock); + } + +out: + rt9467_enable_hidden_mode(info, false); + mutex_unlock(&info->adc_access_lock); + return ret; +} + +static inline int __rt9467_enable_chgdet_flow(struct rt9467_info *info, bool en) +{ + int ret = 0; + + dev_info(info->dev, "%s: en = %d\n", __func__, en); + + ret = (en ? rt9467_set_bit : rt9467_clr_bit) + (info, RT9467_REG_CHG_DPDM1, en ? RT9467_MASK_USBCHGEN : + RT9467_MASK_USBCHGEN | RT9467_MASK_HVDCP_EN); + if (ret >= 0) + info->bc12_en = en; + + return ret; +} + +static int rt9467_enable_chgdet_flow(struct rt9467_info *info, bool en) +{ + int ret = 0; + + dev_info(info->dev, "%s: en = %d\n", __func__, en); + + mutex_lock(&info->bc12_access_lock); + ret = __rt9467_enable_chgdet_flow(info, en); + mutex_unlock(&info->bc12_access_lock); + + return ret; +} + +static inline int rt9467_enable_ilim(struct rt9467_info *info, bool en) +{ + dev_info(info->dev, "%s: en = %d\n", __func__, en); + return (en ? rt9467_set_bit : rt9467_clr_bit) + (info, RT9467_REG_CHG_CTRL3, RT9467_MASK_ILIM_EN); +} + +static inline int rt9467_toggle_chgdet_flow(struct rt9467_info *info) +{ + int ret = 0; + u8 data = 0, usbd_off[2] = {0}, usbd_on[2] = {0}; + struct i2c_client *client = info->client; + struct i2c_msg msgs[2] = { + { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = usbd_off, + }, + { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = usbd_on, + }, + }; + + /* read data */ + ret = i2c_smbus_read_i2c_block_data(client, RT9467_REG_CHG_DPDM1, + 1, &data); + if (ret < 0) { + dev_notice(info->dev, "%s: read usbd fail\n", __func__); + goto out; + } + + /* usbd off and then on */ + usbd_off[0] = usbd_on[0] = RT9467_REG_CHG_DPDM1; + usbd_off[1] = data & ~RT9467_MASK_USBCHGEN; + usbd_on[1] = data | RT9467_MASK_USBCHGEN; + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) + dev_notice(info->dev, "%s: usbd off/on fail(%d)\n", + __func__, ret); +out: + + return ret < 0 ? ret : 0; +} + +static int rt9467_bc12_sdp_workaround(struct rt9467_info *info) +{ + int ret = 0; + + dev_info(info->dev, "%s\n", __func__); + + mutex_lock(&info->i2c_access_lock); + + ret = rt9467_toggle_chgdet_flow(info); + if (ret < 0) + goto err; + + mdelay(10); + + ret = rt9467_toggle_chgdet_flow(info); + if (ret < 0) + goto err; + + goto out; +err: + dev_notice(info->dev, "%s: fail\n", __func__); +out: + mutex_unlock(&info->i2c_access_lock); + return ret; +} + +static int __rt9467_chgdet_handler(struct rt9467_info *info) +{ + int ret = 0; + bool pwr_rdy = false, inform_psy = true; + bool hvdcp = false, hvdcp_en = false; + u8 usb_status = 0; + + dev_info(info->dev, "%s\n", __func__); + + /* disabled by user, do nothing */ + if (!info->desc->en_chgdet) { + dev_info(info->dev, "%s: bc12 is disabled by dts\n", __func__); + return 0; + } + +#if IS_ENABLED(CONFIG_TCPC_CLASS) + pwr_rdy = atomic_read(&info->tcpc_usb_connected); +#else + /* check power ready */ + ret = rt9467_i2c_test_bit(info, RT9467_REG_CHG_STATC, + RT9467_SHIFT_PWR_RDY, &pwr_rdy); + if (ret < 0) { + dev_notice(info->dev, "%s: read pwr rdy state fail\n", + __func__); + return ret; + } +#endif + + /* no change in pwr_rdy state */ + if (info->pwr_rdy == pwr_rdy && + atomic_read(&info->bc12_wkard) == 0) { + dev_info(info->dev, "%s: pwr_rdy(%d) state is the same\n", + __func__, pwr_rdy); + inform_psy = false; + goto out; + } + info->pwr_rdy = pwr_rdy; + + /* plug out */ + if (!pwr_rdy) { + info->chg_psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN; + atomic_set(&info->bc12_sdp_cnt, 0); + goto out; + } + + /* plug in */ + ret = rt9467_i2c_read_byte(info, RT9467_REG_CHG_DPDM2); + if (ret < 0) { + dev_notice(info->dev, "%s: read type fail\n", __func__); + return ret; + } + usb_status = (ret & RT9467_MASK_USB_STATUS) >> RT9467_SHIFT_USB_STATUS; + + switch (usb_status) { + case RT9467_CHG_TYPE_NOVBUS: + dev_info(info->dev, "%s: No VBUS\n", __func__); + return 0; + case RT9467_CHG_TYPE_UNDER_GOING: + dev_info(info->dev, "%s: under going...\n", __func__); + return 0; + case RT9467_CHG_TYPE_SDP: + info->chg_psy_desc.type = POWER_SUPPLY_TYPE_USB; + break; + case RT9467_CHG_TYPE_SDPNSTD: + info->chg_psy_desc.type = POWER_SUPPLY_TYPE_USB_FLOAT; + break; + case RT9467_CHG_TYPE_DCP: + ret = rt9467_i2c_read_byte(info, RT9467_REG_CHG_DPDM1); + if (ret < 0) + dev_notice(info->dev, "%s: check HVDCP fail(%d)\n", + __func__, ret); + hvdcp_en = !!(ret & RT9467_MASK_HVDCP_EN); + hvdcp = !!(ret & RT9467_MASK_HVDCP); + info->chg_psy_desc.type = hvdcp ? POWER_SUPPLY_TYPE_USB_HVDCP : + POWER_SUPPLY_TYPE_USB_DCP; + if (!hvdcp && !hvdcp_en) { + ret = rt9467_set_bit(info, RT9467_REG_CHG_DPDM1, + RT9467_MASK_HVDCP_EN); + if (ret < 0) { + dev_notice(info->dev, + "%s: HVDCP EN fail(%d)\n", + __func__, ret); + break; + } + ret = rt9467_toggle_chgdet_flow(info); + if (ret < 0) { + dev_notice(info->dev, + "%s: toggle chgdet flow fail(%d)\n", + __func__, ret); + break; + } + atomic_set(&info->bc12_wkard, 1); + } + break; + case RT9467_CHG_TYPE_CDP: + info->chg_psy_desc.type = POWER_SUPPLY_TYPE_USB_CDP; + break; + default: + info->chg_psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN; + break; + } + + /* BC12 workaround (NONSTD -> STP) */ + if (atomic_read(&info->bc12_sdp_cnt) < 2 && + info->chg_psy_desc.type == POWER_SUPPLY_TYPE_USB) { + ret = rt9467_bc12_sdp_workaround(info); + /* Workaround success, wait for next event */ + if (ret >= 0) { + atomic_inc(&info->bc12_sdp_cnt); + atomic_set(&info->bc12_wkard, 1); + return 0; + } + goto out; + } +out: + atomic_set(&info->bc12_wkard, 0); + + if (info->chg_psy_desc.type != POWER_SUPPLY_TYPE_USB_DCP) { + /* turn off USB charger detection */ + ret = __rt9467_enable_chgdet_flow(info, false); + if (ret < 0) + dev_notice(info->dev, "%s: disable chgdet fail\n", + __func__); + } + + if (inform_psy) + power_supply_changed(info->chg_psy); + + return ret; +} + +static int rt9467_chgdet_handler(struct rt9467_info *info) +{ + int ret = 0; + + mutex_lock(&info->bc12_access_lock); + ret = __rt9467_chgdet_handler(info); + mutex_unlock(&info->bc12_access_lock); + + return ret; +} + +static int rt9467_set_aicl_vth(struct rt9467_info *info, u32 aicl_vth) +{ + u8 reg_aicl_vth = 0; + + reg_aicl_vth = rt9467_closest_reg(RT9467_AICL_VTH_MIN, + RT9467_AICL_VTH_MAX, RT9467_AICL_VTH_STEP, aicl_vth); + + dev_info(info->dev, "%s: vth = %d(0x%02X)\n", __func__, aicl_vth, + reg_aicl_vth); + + return rt9467_i2c_update_bits(info, RT9467_REG_CHG_CTRL14, + reg_aicl_vth << RT9467_SHIFT_AICL_VTH, RT9467_MASK_AICL_VTH); +} + +static int __rt9467_set_aicr(struct rt9467_info *info, u32 aicr) +{ + u8 reg_aicr = 0; + + reg_aicr = rt9467_closest_reg(RT9467_AICR_MIN, RT9467_AICR_MAX, + RT9467_AICR_STEP, aicr); + + dev_info(info->dev, "%s: aicr = %d(0x%02X)\n", __func__, aicr, + reg_aicr); + + return rt9467_i2c_update_bits(info, RT9467_REG_CHG_CTRL3, + reg_aicr << RT9467_SHIFT_AICR, RT9467_MASK_AICR); +} + +static int __rt9467_run_aicl(struct rt9467_info *info) +{ + int ret = 0; + u32 mivr = 0, aicl_vth = 0, aicr = 0; + bool mivr_act = false; + + /* Check whether MIVR loop is active */ + ret = rt9467_i2c_test_bit(info, RT9467_REG_CHG_STATC, + RT9467_SHIFT_CHG_MIVR, &mivr_act); + if (ret < 0) { + dev_notice(info->dev, "%s: read mivr stat fail\n", __func__); + goto out; + } + + if (!mivr_act) { + dev_info(info->dev, "%s: mivr loop is not active\n", __func__); + ret = -EAGAIN; + goto out; + } + + ret = rt9467_get_mivr(info, &mivr); + if (ret < 0) + goto out; + + /* Check if there's a suitable AICL_VTH */ + aicl_vth = mivr + 200000; + if (aicl_vth > RT9467_AICL_VTH_MAX) { + dev_notice(info->dev, "%s: no suitable vth, vth = %d\n", + __func__, aicl_vth); + ret = -EINVAL; + goto out; + } + + ret = rt9467_set_aicl_vth(info, aicl_vth); + if (ret < 0) + goto out; + + /* Clear AICL measurement IRQ */ + rt9467_irq_clr_flag(info, &info->irq_flag[RT9467_IRQIDX_CHG_IRQ2], + RT9467_MASK_CHG_AICLMEASI); + + mutex_lock(&info->aicr_access_lock); + + ret = rt9467_set_bit(info, RT9467_REG_CHG_CTRL14, + RT9467_MASK_AICL_MEAS); + if (ret < 0) + goto unlock_out; + + ret = wait_event_interruptible_timeout(info->wait_queue, + info->irq_flag[RT9467_IRQIDX_CHG_IRQ2] & + RT9467_MASK_CHG_AICLMEASI, + msecs_to_jiffies(3500)); + if (ret <= 0) { + dev_notice(info->dev, "%s: wait AICL time out\n", __func__); + ret = -EIO; + goto unlock_out; + } + + ret = rt9467_get_aicr(info, &aicr); + if (ret < 0) + goto unlock_out; + + dev_info(info->dev, "%s: OK, aicr upper bound = %dmA\n", __func__, + aicr / 1000); + info->power_limit = aicl_vth / 1000000 * aicr; + dev_info(info->dev, "%s: power limit = %dmW\n", __func__, + info->power_limit / 1000); + +unlock_out: + mutex_unlock(&info->aicr_access_lock); +out: + return ret; +} + +#if !IS_ENABLED(CONFIG_TCPC_CLASS) +static void rt9467_chgdet_work_handler(struct work_struct *work) +{ + int ret = 0; + bool pwr_rdy = false; + struct rt9467_info *info = (struct rt9467_info *)container_of(work, + struct rt9467_info, chgdet_work); + + /* Check power ready */ + ret = rt9467_i2c_test_bit(info, RT9467_REG_CHG_STATC, + RT9467_SHIFT_PWR_RDY, &pwr_rdy); + + if (!pwr_rdy) + return; + + /* Enable USB charger type detection */ + ret = rt9467_enable_chgdet_flow(info, true); + if (ret < 0) + dev_notice(info->dev, "%s: enable chgdet fail\n", __func__); + +} +#endif /* CONFIG_TCPC_CLASS */ + + +/* Prevent back boost */ +static int rt9467_toggle_cfo(struct rt9467_info *info) +{ + int ret = 0; + u8 data = 0; + + dev_info(info->dev, "%s\n", __func__); + mutex_lock(&info->i2c_access_lock); + ret = rt9467_device_read(info->client, RT9467_REG_CHG_CTRL2, 1, &data); + if (ret < 0) { + dev_notice(info->dev, "%s read cfo fail(%d)\n", __func__, ret); + goto out; + } + + /* CFO off */ + data &= ~RT9467_MASK_CFO_EN; + ret = rt9467_device_write(info->client, RT9467_REG_CHG_CTRL2, 1, &data); + if (ret < 0) { + dev_notice(info->dev, "%s cfo off fail(%d)\n", __func__, ret); + goto out; + } + + /* CFO on */ + data |= RT9467_MASK_CFO_EN; + ret = rt9467_device_write(info->client, RT9467_REG_CHG_CTRL2, 1, &data); + if (ret < 0) + dev_notice(info->dev, "%s cfo on fail(%d)\n", __func__, ret); + +out: + mutex_unlock(&info->i2c_access_lock); + return ret; +} + +/* IRQ handlers */ +static int rt9467_pwr_rdy_irq_handler(struct rt9467_info *info) +{ +#if !IS_ENABLED(CONFIG_TCPC_CLASS) + int ret = 0; + bool pwr_rdy = false; +#endif /* CONFIG_TCPC_CLASS */ + + dev_notice(info->dev, "%s\n", __func__); + +#if !IS_ENABLED(CONFIG_TCPC_CLASS) + ret = rt9467_i2c_test_bit(info, RT9467_REG_CHG_STATC, + RT9467_SHIFT_PWR_RDY, &pwr_rdy); + if (ret < 0) { + dev_notice(info->dev, "%s: read pwr rdy fail\n", __func__); + goto out; + } + + if (!pwr_rdy) { + dev_info(info->dev, "%s: pwr rdy = 0\n", __func__); + goto out; + } + + ret = rt9467_enable_chgdet_flow(info, true); + if (ret < 0) + dev_notice(info->dev, "%s: en chgdet fail(%d)\n", __func__, + ret); + +out: +#endif /* CONFIG_TCPC_CLASS */ + + return 0; +} + +static int rt9467_chg_mivr_irq_handler(struct rt9467_info *info) +{ + int ret = 0; + bool mivr_act = false; + int adc_ibus = 0; + + dev_notice(info->dev, "%s\n", __func__); + + /* Check whether MIVR loop is active */ + ret = rt9467_i2c_test_bit(info, RT9467_REG_CHG_STATC, + RT9467_SHIFT_CHG_MIVR, &mivr_act); + if (ret < 0) { + dev_notice(info->dev, "%s: read mivr stat fail\n", __func__); + goto out; + } + + if (!mivr_act) { + dev_info(info->dev, "%s: mivr loop is not active\n", __func__); + goto out; + } + + /* Check IBUS ADC */ + ret = rt9467_get_adc(info, RT9467_ADC_IBUS, &adc_ibus); + if (ret < 0) { + dev_notice(info->dev, "%s: get ibus fail\n", __func__); + return ret; + } + if (adc_ibus < 100000) { /* 100mA */ + ret = rt9467_toggle_cfo(info); + return ret; + } +out: + return 0; +} + +static int rt9467_chg_aicr_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_chg_treg_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_chg_vsysuv_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_chg_vsysov_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_chg_vbatov_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_chg_vbusov_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_ts_bat_cold_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_ts_bat_cool_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_ts_bat_warm_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_ts_bat_hot_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_ts_statci_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_chg_faulti_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_chg_statci_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_chg_tmri_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_chg_batabsi_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_chg_adpbadi_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_chg_rvpi_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_chg_otpi_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_chg_aiclmeasi_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + rt9467_irq_set_flag(info, &info->irq_flag[RT9467_IRQIDX_CHG_IRQ2], + RT9467_MASK_CHG_AICLMEASI); + wake_up_interruptible(&info->wait_queue); + return 0; +} + +static int rt9467_chg_ichgmeasi_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_chgdet_donei_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_wdtmri_irq_handler(struct rt9467_info *info) +{ + int ret = 0; + + dev_notice(info->dev, "%s\n", __func__); + ret = rt9467_kick_wdt(info); + if (ret < 0) + dev_notice(info->dev, "%s: kick wdt fail\n", __func__); + + return ret; +} + +static int rt9467_ssfinishi_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_chg_rechgi_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_chg_termi_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_chg_ieoci_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_adc_donei_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_pumpx_donei_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_bst_batuvi_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_bst_midovi_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_bst_olpi_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_attachi_irq_handler(struct rt9467_info *info) +{ + int ret = 0; + + dev_notice(info->dev, "%s\n", __func__); + + /* check bc12_en state */ + mutex_lock(&info->bc12_access_lock); + if (!info->bc12_en) { + dev_notice(info->dev, "%s: bc12 disabled, ignore irq\n", + __func__); + goto out; + } + ret = __rt9467_chgdet_handler(info); +out: + mutex_unlock(&info->bc12_access_lock); + return ret; +} + +static int rt9467_detachi_irq_handler(struct rt9467_info *info) +{ + int ret = 0; + + dev_notice(info->dev, "%s\n", __func__); +#if !IS_ENABLED(CONFIG_TCPC_CLASS) + ret = rt9467_chgdet_handler(info); +#endif /* CONFIG_TCPC_CLASS */ + return ret; +} + +static int rt9467_chgdeti_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +static int rt9467_dcdti_irq_handler(struct rt9467_info *info) +{ + dev_notice(info->dev, "%s\n", __func__); + return 0; +} + +typedef int (*rt9467_irq_fptr)(struct rt9467_info *); +static rt9467_irq_fptr rt9467_irq_handler_tbl[56] = { + NULL, + NULL, + NULL, + NULL, + rt9467_chg_treg_irq_handler, + rt9467_chg_aicr_irq_handler, + rt9467_chg_mivr_irq_handler, + rt9467_pwr_rdy_irq_handler, + NULL, + NULL, + NULL, + NULL, + rt9467_chg_vsysuv_irq_handler, + rt9467_chg_vsysov_irq_handler, + rt9467_chg_vbatov_irq_handler, + rt9467_chg_vbusov_irq_handler, + NULL, + NULL, + NULL, + NULL, + rt9467_ts_bat_cold_irq_handler, + rt9467_ts_bat_cool_irq_handler, + rt9467_ts_bat_warm_irq_handler, + rt9467_ts_bat_hot_irq_handler, + rt9467_ts_statci_irq_handler, + rt9467_chg_faulti_irq_handler, + rt9467_chg_statci_irq_handler, + rt9467_chg_tmri_irq_handler, + rt9467_chg_batabsi_irq_handler, + rt9467_chg_adpbadi_irq_handler, + rt9467_chg_rvpi_irq_handler, + rt9467_chg_otpi_irq_handler, + rt9467_chg_aiclmeasi_irq_handler, + rt9467_chg_ichgmeasi_irq_handler, + rt9467_chgdet_donei_irq_handler, + rt9467_wdtmri_irq_handler, + rt9467_ssfinishi_irq_handler, + rt9467_chg_rechgi_irq_handler, + rt9467_chg_termi_irq_handler, + rt9467_chg_ieoci_irq_handler, + rt9467_adc_donei_irq_handler, + rt9467_pumpx_donei_irq_handler, + NULL, + NULL, + NULL, + rt9467_bst_batuvi_irq_handler, + rt9467_bst_midovi_irq_handler, + rt9467_bst_olpi_irq_handler, + rt9467_attachi_irq_handler, + rt9467_detachi_irq_handler, + NULL, + NULL, + NULL, + rt9467_chgdeti_irq_handler, + rt9467_dcdti_irq_handler, +}; + +static irqreturn_t rt9467_irq_handler(int irq, void *data) +{ + int ret = 0, i = 0, j = 0; + struct rt9467_info *info = data; + u8 evt[RT9467_IRQIDX_MAX] = {0}; + u8 mask[RT9467_IRQIDX_MAX] = {0}; + u8 stat[RT9467_IRQSTAT_MAX] = {0}; + u8 usb_status_old = 0, usb_status_new = 0; + + dev_info(info->dev, "%s\n", __func__); + + /* Read DPDM status before reading evts */ + ret = rt9467_i2c_read_byte(info, RT9467_REG_CHG_DPDM2); + if (ret < 0) { + dev_notice(info->dev, "%s: read type fail\n", __func__); + goto err_read_irq; + } + usb_status_old = (ret & RT9467_MASK_USB_STATUS) >> + RT9467_SHIFT_USB_STATUS; + + /* Read event and skip CHG_IRQ3 */ + ret = rt9467_i2c_block_read(info, RT9467_REG_CHG_IRQ1, 2, &evt[3]); + if (ret < 0) { + dev_notice(info->dev, "%s: read evt1 fail(%d)\n", __func__, + ret); + goto err_read_irq; + } + + ret = rt9467_i2c_block_read(info, RT9467_REG_DPDM_IRQ, 1, &evt[6]); + if (ret < 0) { + dev_notice(info->dev, "%s: read evt2 fail(%d)\n", __func__, + ret); + goto err_read_irq; + } + + ret = rt9467_i2c_block_read(info, RT9467_REG_CHG_STATC, 3, evt); + if (ret < 0) { + dev_notice(info->dev, "%s: read stat fail(%d)\n", __func__, + ret); + goto err_read_irq; + } + + /* Read DPDM status after reading evts */ + ret = rt9467_i2c_read_byte(info, RT9467_REG_CHG_DPDM2); + if (ret < 0) { + dev_notice(info->dev, "%s: read type fail\n", __func__); + goto err_read_irq; + } + usb_status_new = (ret & RT9467_MASK_USB_STATUS) >> + RT9467_SHIFT_USB_STATUS; + + /* Read mask */ + ret = rt9467_i2c_block_read(info, RT9467_REG_CHG_STATC_CTRL, + ARRAY_SIZE(mask), mask); + if (ret < 0) { + dev_notice(info->dev, "%s: read mask fail(%d)\n", __func__, + ret); + goto err_read_irq; + } + + /* Detach */ + if (usb_status_old != RT9467_CHG_TYPE_NOVBUS && + usb_status_new == RT9467_CHG_TYPE_NOVBUS) + evt[RT9467_IRQIDX_DPDM_IRQ] |= 0x02; + + /* Attach */ + if (usb_status_new >= RT9467_CHG_TYPE_SDP && + usb_status_new <= RT9467_CHG_TYPE_CDP && + usb_status_old != usb_status_new) + evt[RT9467_IRQIDX_DPDM_IRQ] |= 0x01; + + /* Store/Update stat */ + memcpy(stat, info->irq_stat, RT9467_IRQSTAT_MAX); + + for (i = 0; i < RT9467_IRQIDX_MAX; i++) { + evt[i] &= ~mask[i]; + if (i < RT9467_IRQSTAT_MAX) { + info->irq_stat[i] = evt[i]; + evt[i] ^= stat[i]; + } + for (j = 0; j < 8; j++) { + if (!(evt[i] & (1 << j))) + continue; + if (rt9467_irq_handler_tbl[i * 8 + j]) + rt9467_irq_handler_tbl[i * 8 + j](info); + } + } + +err_read_irq: + return IRQ_HANDLED; +} + +static int rt9467_irq_register(struct rt9467_info *info) +{ + int ret = 0, len = 0; + char *name = NULL; + + dev_info(info->dev, "%s\n", __func__); + + info->irq = info->client->irq; + dev_info(info->dev, "%s: irq = %d\n", __func__, info->irq); + + /* Request threaded IRQ */ + name = devm_kzalloc(info->dev, len + 5, GFP_KERNEL); + snprintf(name, len + 5, "%s_irq", info->desc->chg_dev_name); + ret = devm_request_threaded_irq(info->dev, info->irq, NULL, + rt9467_irq_handler, IRQF_TRIGGER_LOW | IRQF_ONESHOT, name, + info); + if (ret < 0) { + dev_notice(info->dev, "%s: request thread irq fail\n", + __func__); + return ret; + } + device_init_wakeup(info->dev, true); + + return 0; +} + +static inline int rt9467_maskall_irq(struct rt9467_info *info) +{ + dev_info(info->dev, "%s\n", __func__); + return rt9467_i2c_block_write(info, RT9467_REG_CHG_STATC_CTRL, + ARRAY_SIZE(rt9467_irq_maskall), rt9467_irq_maskall); +} + +static inline int rt9467_irq_init(struct rt9467_info *info) +{ + dev_info(info->dev, "%s\n", __func__); + return rt9467_i2c_block_write(info, RT9467_REG_CHG_STATC_CTRL, + ARRAY_SIZE(info->irq_mask), info->irq_mask); +} + +static bool rt9467_is_hw_exist(struct rt9467_info *info) +{ + int ret = 0; + u8 vendor_id = 0, chip_rev = 0; + + ret = i2c_smbus_read_byte_data(info->client, RT9467_REG_DEVICE_ID); + if (ret < 0) + return false; + + vendor_id = ret & 0xF0; + chip_rev = ret & 0x0F; + if (vendor_id != RT9467_VENDOR_ID) { + dev_notice(info->dev, "%s: vendor id is incorrect (0x%02X)\n", + __func__, vendor_id); + return false; + } + + dev_info(info->dev, "%s: 0x%02X\n", __func__, chip_rev); + info->chip_rev = chip_rev; + + return true; +} + +static int rt9467_set_safety_timer(struct rt9467_info *info, u32 hr) +{ + u8 reg_st = 0; + + reg_st = rt9467_closest_reg_via_tbl(rt9467_safety_timer, + ARRAY_SIZE(rt9467_safety_timer), hr); + + dev_info(info->dev, "%s: time = %d(0x%02X)\n", __func__, hr, reg_st); + + return rt9467_i2c_update_bits(info, RT9467_REG_CHG_CTRL12, + reg_st << RT9467_SHIFT_WT_FC, RT9467_MASK_WT_FC); +} + +static inline int rt9467_enable_wdt(struct rt9467_info *info, bool en) +{ + int ret = 0; + + dev_info(info->dev, "%s: en = %d\n", __func__, en); + ret = (en ? rt9467_set_bit : rt9467_clr_bit) + (info, RT9467_REG_CHG_CTRL13, RT9467_MASK_WDT_EN); + if (ret < 0) + return ret; + + if (en) + schedule_delayed_work(&info->kick_wdt_dwork, + msecs_to_jiffies(30000)); + else + cancel_delayed_work_sync(&info->kick_wdt_dwork); + + return 0; +} + +static inline int rt9467_select_input_current_limit(struct rt9467_info *info, + enum rt9467_iin_limit_sel sel) +{ + dev_info(info->dev, "%s: sel = %d\n", __func__, sel); + return rt9467_i2c_update_bits(info, RT9467_REG_CHG_CTRL2, + sel << RT9467_SHIFT_IINLMTSEL, RT9467_MASK_IINLMTSEL); +} + +static int rt9467_enable_hidden_mode(struct rt9467_info *info, bool en) +{ + int ret = 0; + + mutex_lock(&info->hidden_mode_lock); + + if (en) { + if (info->hidden_mode_cnt == 0) { + ret = rt9467_i2c_block_write(info, 0x70, + ARRAY_SIZE(rt9467_val_en_hidden_mode), + rt9467_val_en_hidden_mode); + if (ret < 0) + goto err; + } + info->hidden_mode_cnt++; + } else { + if (info->hidden_mode_cnt == 1) /* last one */ + ret = rt9467_i2c_write_byte(info, 0x70, 0x00); + info->hidden_mode_cnt--; + if (ret < 0) + goto err; + } + dev_dbg(info->dev, "%s: en = %d\n", __func__, en); + goto out; + +err: + dev_notice(info->dev, "%s: en = %d fail(%d)\n", __func__, en, ret); +out: + mutex_unlock(&info->hidden_mode_lock); + return ret; +} + +static int rt9467_sw_workaround(struct rt9467_info *info) +{ + int ret = 0; + + dev_info(info->dev, "%s\n", __func__); + + rt9467_enable_hidden_mode(info, true); + + /* Modify UG driver */ + ret = rt9467_i2c_update_bits(info, RT9467_REG_CHG_HIDDEN_CTRL4, 0xC0, + 0xF0); + if (ret < 0) + dev_notice(info->dev, "%s: set UG driver fail\n", __func__); + + ret = rt9467_i2c_read_byte(info, RT9467_REG_CHG_HIDDEN_CTRL4); + dev_info(info->dev, "%s: reg0x23 = 0x%02X\n", __func__, ret); + + /* Disable TS auto sensing */ + ret = rt9467_clr_bit(info, RT9467_REG_CHG_HIDDEN_CTRL15, 0x01); + if (ret < 0) + goto out; + + /* Only revision <= E1 needs the following workaround */ + if (info->chip_rev > RT9467_CHIP_REV_E1) + goto out; + + /* ICC: modify sensing node, make it more accurate */ + ret = rt9467_i2c_write_byte(info, RT9467_REG_CHG_HIDDEN_CTRL8, 0x00); + if (ret < 0) + goto out; + + /* DIMIN level */ + ret = rt9467_i2c_write_byte(info, RT9467_REG_CHG_HIDDEN_CTRL9, 0x86); + +out: + rt9467_enable_hidden_mode(info, false); + return ret; +} + +static inline int rt9467_enable_hz(struct rt9467_info *info, bool en) +{ + dev_info(info->dev, "%s: en = %d\n", __func__, en); + return (en ? rt9467_set_bit : rt9467_clr_bit) + (info, RT9467_REG_CHG_CTRL1, RT9467_MASK_HZ_EN); +} + +/* Reset all registers' value to default */ +static int rt9467_reset_chip(struct rt9467_info *info) +{ + int ret = 0; + + dev_info(info->dev, "%s\n", __func__); + + /* disable hz before reset chip */ + ret = rt9467_enable_hz(info, false); + if (ret < 0) { + dev_notice(info->dev, "%s: disable hz fail\n", __func__); + return ret; + } + + return rt9467_set_bit(info, RT9467_REG_CORE_CTRL0, RT9467_MASK_RST); +} + +static inline int rt9467_enable_te(struct rt9467_info *info, bool en) +{ + dev_info(info->dev, "%s: en = %d\n", __func__, en); + return (en ? rt9467_set_bit : rt9467_clr_bit) + (info, RT9467_REG_CHG_CTRL2, RT9467_MASK_TE_EN); +} + +static inline int rt9467_enable_safety_timer(struct rt9467_info *info, + bool en) +{ + dev_info(info->dev, "%s: en = %d\n", __func__, en); + return (en ? rt9467_set_bit : rt9467_clr_bit) + (info, RT9467_REG_CHG_CTRL12, RT9467_MASK_TMR_EN); +} + +static int __rt9467_set_ieoc(struct rt9467_info *info, u32 ieoc) +{ + int ret = 0; + u8 reg_ieoc = 0; + + /* IEOC workaround */ + if (info->ieoc_wkard) + ieoc += 100000; /* 100mA */ + + reg_ieoc = rt9467_closest_reg(RT9467_IEOC_MIN, RT9467_IEOC_MAX, + RT9467_IEOC_STEP, ieoc); + + dev_info(info->dev, "%s: ieoc = %d(0x%02X)\n", __func__, ieoc, + reg_ieoc); + + ret = rt9467_i2c_update_bits(info, RT9467_REG_CHG_CTRL9, + reg_ieoc << RT9467_SHIFT_IEOC, RT9467_MASK_IEOC); + + /* Store IEOC */ + return rt9467_get_ieoc(info, &info->ieoc); +} + +static int rt9467_get_mivr(struct rt9467_info *info, u32 *mivr) +{ + int ret = 0; + u8 reg_mivr = 0; + + ret = rt9467_i2c_read_byte(info, RT9467_REG_CHG_CTRL6); + if (ret < 0) + return ret; + reg_mivr = ((ret & RT9467_MASK_MIVR) >> RT9467_SHIFT_MIVR) & 0xFF; + + *mivr = rt9467_closest_value(RT9467_MIVR_MIN, RT9467_MIVR_MAX, + RT9467_MIVR_STEP, reg_mivr); + + return ret; +} + +static int __rt9467_set_mivr(struct rt9467_info *info, u32 mivr) +{ + u8 reg_mivr = 0; + + reg_mivr = rt9467_closest_reg(RT9467_MIVR_MIN, RT9467_MIVR_MAX, + RT9467_MIVR_STEP, mivr); + + dev_info(info->dev, "%s: mivr = %d(0x%02X)\n", __func__, mivr, + reg_mivr); + + return rt9467_i2c_update_bits(info, RT9467_REG_CHG_CTRL6, + reg_mivr << RT9467_SHIFT_MIVR, RT9467_MASK_MIVR); +} + +static inline int rt9467_enable_jeita(struct rt9467_info *info, bool en) +{ + dev_info(info->dev, "%s: en = %d\n", __func__, en); + return (en ? rt9467_set_bit : rt9467_clr_bit) + (info, RT9467_REG_CHG_CTRL16, RT9467_MASK_JEITA_EN); +} + + +static int rt9467_get_charging_status(struct rt9467_info *info, + enum rt9467_charging_status *chg_stat) +{ + int ret = 0; + + ret = rt9467_i2c_read_byte(info, RT9467_REG_CHG_STAT); + if (ret < 0) + return ret; + + *chg_stat = (ret & RT9467_MASK_CHG_STAT) >> RT9467_SHIFT_CHG_STAT; + + return ret; +} + +static int rt9467_get_ieoc(struct rt9467_info *info, u32 *ieoc) +{ + int ret = 0; + u8 reg_ieoc = 0; + + ret = rt9467_i2c_read_byte(info, RT9467_REG_CHG_CTRL9); + if (ret < 0) + return ret; + + reg_ieoc = (ret & RT9467_MASK_IEOC) >> RT9467_SHIFT_IEOC; + *ieoc = rt9467_closest_value(RT9467_IEOC_MIN, RT9467_IEOC_MAX, + RT9467_IEOC_STEP, reg_ieoc); + + return ret; +} + +static inline int rt9467_is_charging_enable(struct rt9467_info *info, bool *en) +{ + return rt9467_i2c_test_bit(info, RT9467_REG_CHG_CTRL2, + RT9467_SHIFT_CHG_EN, en); +} + +static inline int rt9467_is_otg_enable(struct rt9467_info *info, bool *en) +{ + return rt9467_i2c_test_bit(info, RT9467_REG_CHG_CTRL1, + RT9467_SHIFT_OPA_MODE, en); +} + +static int rt9467_get_ichg(struct rt9467_info *info, u32 *ichg) +{ + int ret = 0; + u8 reg_ichg = 0; + + ret = rt9467_i2c_read_byte(info, RT9467_REG_CHG_CTRL7); + if (ret < 0) + return ret; + + reg_ichg = (ret & RT9467_MASK_ICHG) >> RT9467_SHIFT_ICHG; + *ichg = rt9467_closest_value(RT9467_ICHG_MIN, RT9467_ICHG_MAX, + RT9467_ICHG_STEP, reg_ichg); + + return ret; +} + +static inline int rt9467_ichg_workaround(struct rt9467_info *info, u32 uA) +{ + int ret = 0; + + /* Vsys short protection */ + rt9467_enable_hidden_mode(info, true); + + if (info->ichg >= 900000 && uA < 900000) + ret = rt9467_i2c_update_bits(info, RT9467_REG_CHG_HIDDEN_CTRL7, + 0x00, 0x60); + else if (uA >= 900000 && info->ichg < 900000) + ret = rt9467_i2c_update_bits(info, RT9467_REG_CHG_HIDDEN_CTRL7, + 0x40, 0x60); + + rt9467_enable_hidden_mode(info, false); + return ret; +} + +static int __rt9467_set_ichg(struct rt9467_info *info, u32 ichg) +{ + int ret = 0; + u8 reg_ichg = 0; + + ichg = (ichg < 500000) ? 500000 : ichg; + rt9467_ichg_workaround(info, ichg); + + reg_ichg = rt9467_closest_reg(RT9467_ICHG_MIN, RT9467_ICHG_MAX, + RT9467_ICHG_STEP, ichg); + + dev_info(info->dev, "%s: ichg = %d(0x%02X)\n", __func__, ichg, + reg_ichg); + + ret = rt9467_i2c_update_bits(info, RT9467_REG_CHG_CTRL7, + reg_ichg << RT9467_SHIFT_ICHG, RT9467_MASK_ICHG); + if (ret < 0) + return ret; + + /* Store Ichg setting */ + rt9467_get_ichg(info, &info->ichg); + + /* Workaround to make IEOC accurate */ + if (ichg < 900000 && !info->ieoc_wkard) { /* 900mA */ + ret = __rt9467_set_ieoc(info, info->ieoc + 100000); + info->ieoc_wkard = true; + } else if (ichg >= 900000 && info->ieoc_wkard) { + info->ieoc_wkard = false; + ret = __rt9467_set_ieoc(info, info->ieoc - 100000); + } + + return ret; +} + +static int rt9467_set_cv(struct rt9467_info *info, u32 cv) +{ + u8 reg_cv = 0; + + reg_cv = rt9467_closest_reg(RT9467_CV_MIN, RT9467_CV_MAX, + RT9467_CV_STEP, cv); + + dev_info(info->dev, "%s: cv = %d(0x%02X)\n", __func__, cv, reg_cv); + + return rt9467_i2c_update_bits(info, RT9467_REG_CHG_CTRL4, + reg_cv << RT9467_SHIFT_CV, RT9467_MASK_CV); +} + +static int rt9467_set_ircmp_resistor(struct rt9467_info *info, u32 uohm) +{ + u8 reg_resistor = 0; + + reg_resistor = rt9467_closest_reg(RT9467_IRCMP_RES_MIN, + RT9467_IRCMP_RES_MAX, RT9467_IRCMP_RES_STEP, uohm); + + dev_info(info->dev, "%s: resistor = %d(0x%02X)\n", __func__, uohm, + reg_resistor); + + return rt9467_i2c_update_bits(info, RT9467_REG_CHG_CTRL18, + reg_resistor << RT9467_SHIFT_IRCMP_RES, RT9467_MASK_IRCMP_RES); +} + +static int rt9467_set_ircmp_vclamp(struct rt9467_info *info, u32 uV) +{ + u8 reg_vclamp = 0; + + reg_vclamp = rt9467_closest_reg(RT9467_IRCMP_VCLAMP_MIN, + RT9467_IRCMP_VCLAMP_MAX, RT9467_IRCMP_VCLAMP_STEP, uV); + + dev_info(info->dev, "%s: vclamp = %d(0x%02X)\n", __func__, uV, + reg_vclamp); + + return rt9467_i2c_update_bits(info, RT9467_REG_CHG_CTRL18, + reg_vclamp << RT9467_SHIFT_IRCMP_VCLAMP, + RT9467_MASK_IRCMP_VCLAMP); +} + +static inline int rt9467_enable_irq_pulse(struct rt9467_info *info, bool en) +{ + dev_info(info->dev, "%s: en = %d\n", __func__, en); + return (en ? rt9467_set_bit : rt9467_clr_bit) + (info, RT9467_REG_CHG_CTRL1, RT9467_MASK_IRQ_PULSE); +} + +static inline int rt9467_get_irq_number(struct rt9467_info *info, + const char *name) +{ + int i = 0; + + if (!name) { + dev_notice(info->dev, "%s: null name\n", __func__); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(rt9467_irq_mapping_tbl); i++) { + if (!strcmp(name, rt9467_irq_mapping_tbl[i].name)) + return rt9467_irq_mapping_tbl[i].id; + } + + return -EINVAL; +} + +static int rt9467_parse_dt(struct rt9467_info *info, struct device *dev) +{ + int ret = 0, irq_cnt = 0; + struct rt9467_desc *desc = NULL; + struct device_node *np = dev->of_node; + const char *name = NULL; + int irqnum = 0; + + dev_info(info->dev, "%s\n", __func__); + + if (!np) { + dev_notice(info->dev, "%s: no device node\n", __func__); + return -EINVAL; + } + + info->desc = &rt9467_default_desc; + + desc = devm_kzalloc(dev, sizeof(struct rt9467_desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + memcpy(desc, &rt9467_default_desc, sizeof(struct rt9467_desc)); + + if (of_property_read_string(np, "charger_name", + &desc->chg_dev_name) < 0) + dev_notice(info->dev, "%s: no charger name\n", __func__); + + if (of_property_read_u32(np, "regmap_represent_slave_addr", + &desc->regmap_represent_slave_addr) < 0) + dev_notice(info->dev, "%s: no regmap slave addr\n", __func__); + + if (of_property_read_string(np, "regmap_name", + &(desc->regmap_name)) < 0) + dev_notice(info->dev, "%s: no regmap name\n", __func__); + + if (of_property_read_u32(np, "ichg", &desc->ichg) < 0) + dev_notice(info->dev, "%s: no ichg\n", __func__); + + if (of_property_read_u32(np, "aicr", &desc->aicr) < 0) + dev_notice(info->dev, "%s: no aicr\n", __func__); + + if (of_property_read_u32(np, "mivr", &desc->mivr) < 0) + dev_notice(info->dev, "%s: no mivr\n", __func__); + + if (of_property_read_u32(np, "cv", &desc->cv) < 0) + dev_notice(info->dev, "%s: no cv\n", __func__); + + if (of_property_read_u32(np, "ieoc", &desc->ieoc) < 0) + dev_notice(info->dev, "%s: no ieoc\n", __func__); + + if (of_property_read_u32(np, "safety_timer", &desc->safety_timer) < 0) + dev_notice(info->dev, "%s: no safety timer\n", __func__); + + if (of_property_read_u32(np, "ircmp_resistor", + &desc->ircmp_resistor) < 0) + dev_notice(info->dev, "%s: no ircmp resistor\n", __func__); + + if (of_property_read_u32(np, "ircmp_vclamp", &desc->ircmp_vclamp) < 0) + dev_notice(info->dev, "%s: no ircmp vclamp\n", __func__); + + desc->en_te = of_property_read_bool(np, "en_te"); + desc->en_wdt = of_property_read_bool(np, "en_wdt"); + desc->en_irq_pulse = of_property_read_bool(np, "en_irq_pulse"); + desc->en_jeita = of_property_read_bool(np, "en_jeita"); + desc->en_chgdet = of_property_read_bool(np, "en_chgdet"); + + while (true) { + ret = of_property_read_string_index(np, "interrupt-names", + irq_cnt, &name); + if (ret < 0) + break; + irq_cnt++; + irqnum = rt9467_get_irq_number(info, name); + if (irqnum >= 0) + rt9467_irq_unmask(info, irqnum); + } + + info->desc = desc; + + return 0; +} + +/* =========================================================== */ +/* Released interfaces */ +/* =========================================================== */ + +static int rt9467_enable_charging(struct rt9467_info *info, bool en) +{ + dev_info(info->dev, "%s: en = %d\n", __func__, en); + + return (en ? rt9467_set_bit : rt9467_clr_bit) + (info, RT9467_REG_CHG_CTRL2, RT9467_MASK_CHG_EN); +} + +static int rt9467_set_boost_current_limit(struct rt9467_info *info, + u32 current_limit) +{ + u8 reg_ilimit = 0; + + reg_ilimit = rt9467_closest_reg_via_tbl(rt9467_boost_oc_threshold, + ARRAY_SIZE(rt9467_boost_oc_threshold), current_limit); + + dev_info(info->dev, "%s: boost ilimit = %d(0x%02X)\n", __func__, + current_limit, reg_ilimit); + + return rt9467_i2c_update_bits(info, RT9467_REG_CHG_CTRL10, + reg_ilimit << RT9467_SHIFT_BOOST_OC, RT9467_MASK_BOOST_OC); +} + +static int rt9467_enable_otg(struct rt9467_info *info, bool en) +{ + int ret = 0; + bool en_otg = false; + u8 hidden_val = en ? 0x00 : 0x0F; + u8 lg_slew_rate = en ? 0xCC : 0xC3; + + dev_info(info->dev, "%s: en = %d\n", __func__, en); + + rt9467_enable_hidden_mode(info, true); + + /* Set OTG_OC to 1.8A */ + ret = rt9467_set_boost_current_limit(info, 1800000); + if (ret < 0) { + dev_notice(info->dev, "%s: set current limit fail\n", __func__); + return ret; + } + + /* + * Workaround : slow Low side mos Gate driver slew rate + * for decline VBUS noise + * reg[0x23] = 0xCC after entering OTG mode + * reg[0x23] = 0xC3 after leaving OTG mode + */ + ret = rt9467_i2c_write_byte(info, RT9467_REG_CHG_HIDDEN_CTRL4, + lg_slew_rate); + if (ret < 0) { + dev_notice(info->dev, + "%s: set Low side mos Gate drive speed fail(%d)\n", + __func__, ret); + goto out; + } + + /* Switch OPA mode */ + ret = (en ? rt9467_set_bit : rt9467_clr_bit) + (info, RT9467_REG_CHG_CTRL1, RT9467_MASK_OPA_MODE); + + msleep(20); + + if (en) { + ret = rt9467_i2c_test_bit(info, RT9467_REG_CHG_CTRL1, + RT9467_SHIFT_OPA_MODE, &en_otg); + if (ret < 0 || !en_otg) { + dev_notice(info->dev, "%s: otg fail(%d)\n", __func__, + ret); + goto err_en_otg; + } + } + + /* + * Workaround reg[0x25] = 0x00 after entering OTG mode + * reg[0x25] = 0x0F after leaving OTG mode + */ + ret = rt9467_i2c_write_byte(info, RT9467_REG_CHG_HIDDEN_CTRL6, + hidden_val); + if (ret < 0) + dev_notice(info->dev, "%s: workaroud fail(%d)\n", __func__, + ret); + + goto out; + +err_en_otg: + /* Recover Low side mos Gate slew rate */ + ret = rt9467_i2c_write_byte(info, RT9467_REG_CHG_HIDDEN_CTRL4, 0x73); + if (ret < 0) + dev_notice(info->dev, + "%s: recover Low side mos Gate drive speed fail(%d)\n", + __func__, ret); + ret = -EIO; +out: + rt9467_enable_hidden_mode(info, false); + return ret; +} + +static int rt9467_enable_discharge(struct rt9467_info *info, bool en) +{ + int ret = 0, i = 0; + const int check_dischg_max = 3; + bool is_dischg = true; + + dev_info(info->dev, "%s: en = %d\n", __func__, en); + + ret = rt9467_enable_hidden_mode(info, true); + if (ret < 0) + return ret; + + /* Set bit2 of reg[0x21] to 1 to enable discharging */ + ret = (en ? rt9467_set_bit : rt9467_clr_bit)(info, + RT9467_REG_CHG_HIDDEN_CTRL2, 0x04); + if (ret < 0) { + dev_notice(info->dev, "%s: en = %d, fail\n", __func__, en); + return ret; + } + + if (!en) { + for (i = 0; i < check_dischg_max; i++) { + ret = rt9467_i2c_test_bit(info, + RT9467_REG_CHG_HIDDEN_CTRL2, 2, &is_dischg); + if (ret >= 0 && !is_dischg) + break; + /* Disable discharging */ + ret = rt9467_clr_bit(info, RT9467_REG_CHG_HIDDEN_CTRL2, + 0x04); + } + if (i == check_dischg_max) + dev_notice(info->dev, "%s: disable dischg fail(%d)\n", + __func__, ret); + } + + rt9467_enable_hidden_mode(info, false); + return ret; +} + +static int rt9467_enable_power_path(struct rt9467_info *info, bool en) +{ + int ret = 0; + + mutex_lock(&info->pp_lock); + + dev_info(info->dev, "%s: en = %d, pp_en = %d\n", + __func__, en, info->pp_en); + if (en == info->pp_en) + goto out; + + ret = __rt9467_set_mivr(info, en ? info->mivr : RT9467_MIVR_MAX); + if (ret >= 0) + info->pp_en = en; +out: + mutex_unlock(&info->pp_lock); + + return ret; +} + +#if IS_ENABLED(CONFIG_TCPC_CLASS) +static int rt9467_set_chg_type(struct rt9467_info *info, + enum power_supply_type chg_type) +{ + int ret = 0; + bool en = chg_type != POWER_SUPPLY_TYPE_UNKNOWN; + + if (!info->desc->en_chgdet) { + dev_info(info->dev, "%s: bc12 is disabled by dts\n", __func__); + return 0; + } + + dev_info(info->dev, "%s: chg_type = %d\n", __func__, chg_type); + + atomic_set(&info->tcpc_usb_connected, en); + + mutex_lock(&info->bc12_access_lock); + info->pwr_rdy = en; + info->chg_psy_desc.type = chg_type; + ret = __rt9467_enable_chgdet_flow(info, false); + if (ret < 0) + dev_notice(info->dev, "%s: disable chgdet fail\n", __func__); + mutex_unlock(&info->bc12_access_lock); + power_supply_changed(info->chg_psy); + + return 0; +} + +static int rt9467_enable_chg_type_det(struct rt9467_info *info, bool en) +{ + int ret = 0; + + if (!info->desc->en_chgdet) { + dev_info(info->dev, "%s: bc12 is disabled by dts\n", __func__); + return 0; + } + + dev_info(info->dev, "%s: en = %d\n", __func__, en); + + atomic_set(&info->tcpc_usb_connected, en); + + /* TypeC detach */ + if (!en) { + ret = rt9467_chgdet_handler(info); + return ret; + } + + /* plug in, make usb switch to RT9467 */ + ret = rt9467_enable_chgdet_flow(info, true); + if (ret < 0) + dev_notice(info->dev, "%s: en chgdet fail(%d)\n", __func__, + ret); + + return ret; +} +#endif /* CONFIG_TCPC_CLASS */ + +static int rt9467_set_ichg(struct rt9467_info *info, u32 ichg) +{ + int ret = 0; + + mutex_lock(&info->ichg_access_lock); + mutex_lock(&info->ieoc_lock); + ret = __rt9467_set_ichg(info, ichg); + mutex_unlock(&info->ieoc_lock); + mutex_unlock(&info->ichg_access_lock); + + return ret; +} + +static int rt9467_set_ieoc(struct rt9467_info *info, u32 ieoc) +{ + int ret = 0; + + mutex_lock(&info->ichg_access_lock); + mutex_lock(&info->ieoc_lock); + ret = __rt9467_set_ieoc(info, ieoc); + mutex_unlock(&info->ieoc_lock); + mutex_unlock(&info->ichg_access_lock); + + return ret; +} + +static int rt9467_set_aicr(struct rt9467_info *info, u32 aicr) +{ + int ret = 0; + + mutex_lock(&info->aicr_access_lock); + ret = __rt9467_set_aicr(info, aicr); + mutex_unlock(&info->aicr_access_lock); + + return ret; +} + +static int rt9467_set_mivr(struct rt9467_info *info, u32 mivr) +{ + int ret = 0; + + mutex_lock(&info->pp_lock); + + if (!info->pp_en) { + dev_notice(info->dev, "%s: power path is disabled\n", __func__); + goto out; + } + + ret = __rt9467_set_mivr(info, mivr); +out: + if (ret >= 0) + info->mivr = mivr; + mutex_unlock(&info->pp_lock); + + return ret; +} + +static int rt9467_get_aicr(struct rt9467_info *info, u32 *aicr) +{ + int ret = 0; + u8 reg_aicr = 0; + + ret = rt9467_i2c_read_byte(info, RT9467_REG_CHG_CTRL3); + if (ret < 0) + return ret; + + reg_aicr = (ret & RT9467_MASK_AICR) >> RT9467_SHIFT_AICR; + *aicr = rt9467_closest_value(RT9467_AICR_MIN, RT9467_AICR_MAX, + RT9467_AICR_STEP, reg_aicr); + + return ret; +} + +static int rt9467_get_cv(struct rt9467_info *info, u32 *cv) +{ + int ret = 0; + u8 reg_cv = 0; + + ret = rt9467_i2c_read_byte(info, RT9467_REG_CHG_CTRL4); + if (ret < 0) + return ret; + + reg_cv = (ret & RT9467_MASK_CV) >> RT9467_SHIFT_CV; + *cv = rt9467_closest_value(RT9467_CV_MIN, RT9467_CV_MAX, + RT9467_CV_STEP, reg_cv); + + return ret; +} + +static int rt9467_get_tchg(struct rt9467_info *info, int *tchg) +{ + int ret = 0, adc_temp = 0; + u32 retry_cnt = 3; + + /* Get value from ADC */ + ret = rt9467_get_adc(info, RT9467_ADC_TEMP_JC, &adc_temp); + if (ret < 0) + return ret; + + /* Check unusual temperature */ + while (adc_temp >= 120 && retry_cnt > 0) { + dev_notice(info->dev, + "%s: [WARNING] t = %d\n", __func__, adc_temp); + rt9467_get_adc(info, RT9467_ADC_VBAT, &adc_temp); + ret = rt9467_get_adc(info, RT9467_ADC_TEMP_JC, &adc_temp); + retry_cnt--; + } + if (ret < 0) + return ret; + + mutex_lock(&info->tchg_lock); + /* Use previous one to prevent system from rebooting */ + if (adc_temp >= 120) + adc_temp = info->tchg; + else + info->tchg = adc_temp; + mutex_unlock(&info->tchg_lock); + + *tchg = adc_temp; + + dev_info(info->dev, "%s: temperature = %d\n", __func__, adc_temp); + return ret; +} + +static int rt9467_kick_wdt(struct rt9467_info *info) +{ + enum rt9467_charging_status chg_status = RT9467_CHG_STATUS_READY; + + /* Any I2C communication can reset watchdog timer */ + return rt9467_get_charging_status(info, &chg_status); +} + +/* + * This function is used in shutdown function + * Use i2c smbus directly + */ +static int rt9467_sw_reset(struct rt9467_info *info) +{ + int ret = 0; + u8 evt[RT9467_IRQIDX_MAX] = {0}; + + /* Register 0x01 ~ 0x10 */ + u8 reg_data[] = { + 0x10, 0x03, 0x23, 0x3C, 0x67, 0x0B, 0x4C, 0xA1, + 0x3C, 0x58, 0x2C, 0x02, 0x52, 0x05, 0x00, 0x10 + }; + + dev_info(info->dev, "%s\n", __func__); + + /* Mask all irq */ + mutex_lock(&info->i2c_access_lock); + ret = rt9467_device_write(info->client, RT9467_REG_CHG_STATC_CTRL, + ARRAY_SIZE(rt9467_irq_maskall), rt9467_irq_maskall); + if (ret < 0) + dev_notice(info->dev, "%s: mask all irq fail\n", __func__); + + /* Read all irq */ + ret = rt9467_device_read(info->client, RT9467_REG_CHG_STATC, 5, evt); + if (ret < 0) + dev_notice(info->dev, "%s: read evt1 fail(%d)\n", __func__, + ret); + + ret = rt9467_device_read(info->client, RT9467_REG_DPDM_IRQ, 1, &evt[6]); + if (ret < 0) + dev_notice(info->dev, "%s: read evt2 fail(%d)\n", __func__, + ret); + + /* Reset necessary registers */ + ret = rt9467_device_write(info->client, RT9467_REG_CHG_CTRL1, + ARRAY_SIZE(reg_data), reg_data); + if (ret < 0) + dev_notice(info->dev, "%s: reset registers fail\n", __func__); + mutex_unlock(&info->i2c_access_lock); + + return ret; +} + +static int rt9467_init_setting(struct rt9467_info *info) +{ + int ret = 0; + struct rt9467_desc *desc = info->desc; + u8 evt[RT9467_IRQIDX_MAX] = {0}; + + dev_info(info->dev, "%s\n", __func__); + + /* disable USB charger type detection before reset IRQ */ + ret = rt9467_enable_chgdet_flow(info, false); + if (ret < 0) { + dev_notice(info->dev, "%s: disable chgdet fail\n", __func__); + goto err; + } + + ret = rt9467_set_bit(info, RT9467_REG_CHG_DPDM6, + RT9467_MASK_TIMEOUT_BCDONE); + if (ret < 0) { + dev_notice(info->dev, + "%s: extend HVDCP detection time fail(%d)\n", + __func__, ret); + goto err; + } + + /* mask all irq */ + ret = rt9467_maskall_irq(info); + if (ret < 0) { + dev_notice(info->dev, "%s: mask all irq fail\n", __func__); + goto err; + } + + /* clear event */ + ret = rt9467_i2c_block_read(info, RT9467_REG_CHG_STATC, ARRAY_SIZE(evt), + evt); + if (ret < 0) { + dev_notice(info->dev, "%s: clr evt fail(%d)\n", __func__, ret); + goto err; + } + + ret = __rt9467_set_ichg(info, desc->ichg); + if (ret < 0) + dev_notice(info->dev, "%s: set ichg fail\n", __func__); + + ret = __rt9467_set_aicr(info, desc->aicr); + if (ret < 0) + dev_notice(info->dev, "%s: set aicr fail\n", __func__); + + ret = rt9467_set_mivr(info, desc->mivr); + if (ret < 0) + dev_notice(info->dev, "%s: set mivr fail\n", __func__); + + ret = rt9467_set_cv(info, desc->cv); + if (ret < 0) + dev_notice(info->dev, "%s: set cv fail\n", __func__); + + ret = __rt9467_set_ieoc(info, desc->ieoc); + if (ret < 0) + dev_notice(info->dev, "%s: set ieoc fail\n", __func__); + + ret = rt9467_enable_te(info, desc->en_te); + if (ret < 0) + dev_notice(info->dev, "%s: set te fail\n", __func__); + + ret = rt9467_set_safety_timer(info, desc->safety_timer); + if (ret < 0) + dev_notice(info->dev, "%s: set fast timer fail\n", __func__); + + ret = rt9467_enable_safety_timer(info, true); + if (ret < 0) + dev_notice(info->dev, "%s: enable chg timer fail\n", __func__); + + ret = rt9467_enable_wdt(info, desc->en_wdt); + if (ret < 0) + dev_notice(info->dev, "%s: set wdt fail\n", __func__); + + ret = rt9467_enable_jeita(info, desc->en_jeita); + if (ret < 0) + dev_notice(info->dev, "%s: disable jeita fail\n", __func__); + + ret = rt9467_enable_irq_pulse(info, desc->en_irq_pulse); + if (ret < 0) + dev_notice(info->dev, "%s: set irq pulse fail\n", __func__); + + /* set ircomp according to BIF */ + ret = rt9467_set_ircmp_resistor(info, desc->ircmp_resistor); + if (ret < 0) + dev_notice(info->dev, "%s: set ircmp resistor fail\n", + __func__); + + ret = rt9467_set_ircmp_vclamp(info, desc->ircmp_vclamp); + if (ret < 0) + dev_notice(info->dev, "%s: set ircmp clamp fail\n", __func__); + + ret = rt9467_sw_workaround(info); + if (ret < 0) { + dev_notice(info->dev, "%s: workaround fail\n", __func__); + return ret; + } +err: + return ret; +} + +static int rt9467_run_aicl(struct rt9467_info *info, u32 *uW) +{ + int ret = 0; + + ret = __rt9467_run_aicl(info); + if (ret >= 0) + *uW = info->power_limit; + + return ret; +} + +static int rt9467_dump_register(struct rt9467_info *info) +{ + int i = 0, ret = 0; + u32 ichg = 0, aicr = 0, mivr = 0, ieoc = 0, cv = 0; + bool chg_en = 0; + int adc_vsys = 0, adc_vbat = 0, adc_ibat = 0; + int adc_ibus = 0, adc_vbus = 0; + enum rt9467_charging_status chg_status = RT9467_CHG_STATUS_READY; + u8 chg_stat = 0, chg_ctrl[2] = {0}; + + ret = rt9467_get_ichg(info, &ichg); + ret = rt9467_get_aicr(info, &aicr); + ret = rt9467_get_mivr(info, &mivr); + ret = rt9467_is_charging_enable(info, &chg_en); + ret = rt9467_get_ieoc(info, &ieoc); + ret = rt9467_get_cv(info, &cv); + ret = rt9467_get_charging_status(info, &chg_status); + ret = rt9467_get_adc(info, RT9467_ADC_VSYS, &adc_vsys); + ret = rt9467_get_adc(info, RT9467_ADC_VBAT, &adc_vbat); + ret = rt9467_get_adc(info, RT9467_ADC_IBAT, &adc_ibat); + ret = rt9467_get_adc(info, RT9467_ADC_IBUS, &adc_ibus); + ret = rt9467_get_adc(info, RT9467_ADC_VBUS_DIV5, &adc_vbus); + chg_stat = rt9467_i2c_read_byte(info, RT9467_REG_CHG_STATC); + ret = rt9467_i2c_block_read(info, RT9467_REG_CHG_CTRL1, 2, chg_ctrl); + + /* Charging fault, dump all registers' value */ + if (chg_status == RT9467_CHG_STATUS_FAULT) { + for (i = 0; i < ARRAY_SIZE(rt9467_reg_addr); i++) + ret = rt9467_i2c_read_byte(info, rt9467_reg_addr[i]); + } + + dev_info(info->dev, + "%s: ICHG = %dmA, AICR = %dmA, MIVR = %dmV, IEOC = %dmA, CV = %dmV\n", + __func__, ichg / 1000, aicr / 1000, mivr / 1000, + ieoc / 1000, cv / 1000); + + dev_info(info->dev, + "%s: VSYS = %dmV, VBAT = %dmV, IBAT = %dmA, IBUS = %dmA, VBUS = %dmV\n", + __func__, adc_vsys / 1000, adc_vbat / 1000, adc_ibat / 1000, + adc_ibus / 1000, adc_vbus / 1000); + + dev_info(info->dev, + "%s: CHG_EN = %d, CHG_STATUS = %s, CHG_STAT = 0x%02X\n", + __func__, chg_en, rt9467_chg_status_name[chg_status], chg_stat); + + dev_info(info->dev, "%s: CHG_CTRL1 = 0x%02X, CHG_CTRL2 = 0x%02X\n", + __func__, chg_ctrl[0], chg_ctrl[1]); + + return 0; +} + +static enum power_supply_property rt9467_properties[] = { + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_TYPE, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_CHARGE_EMPTY, + POWER_SUPPLY_PROP_INPUT_POWER_LIMIT, +}; + +static int rt9467_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + struct rt9467_info *info = power_supply_get_drvdata(psy); + bool en = false; + enum rt9467_charging_status chg_status = RT9467_CHG_STATUS_READY; + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = info->pwr_rdy; + break; + case POWER_SUPPLY_PROP_TYPE: + val->intval = info->chg_psy_desc.type; + break; + case POWER_SUPPLY_PROP_STATUS: + if (!info->pwr_rdy) { + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + } + ret = rt9467_get_charging_status(info, &chg_status); + if (ret < 0) { + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } + switch (chg_status) { + case RT9467_CHG_STATUS_READY: + case RT9467_CHG_STATUS_FAULT: + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case RT9467_CHG_STATUS_PROGRESS: + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + case RT9467_CHG_STATUS_DONE: + val->intval = POWER_SUPPLY_STATUS_FULL; + break; + default: + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + break; + } + break; + case POWER_SUPPLY_PROP_TEMP: + ret = rt9467_get_tchg(info, &val->intval); + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = rt9467_get_mivr(info, &val->intval); + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = rt9467_get_aicr(info, &val->intval); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + ret = rt9467_get_cv(info, &val->intval); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + ret = rt9467_get_ichg(info, &val->intval); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + ret = rt9467_get_ieoc(info, &val->intval); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + ret = rt9467_is_otg_enable(info, &en); + if (ret < 0) { + val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + break; + } + if (en) { + val->intval = POWER_SUPPLY_CHARGE_TYPE_CUSTOM; + break; + } + ret = rt9467_is_charging_enable(info, &en); + if (ret < 0) { + val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + break; + } + val->intval = en ? POWER_SUPPLY_CHARGE_TYPE_STANDARD : + POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + case POWER_SUPPLY_PROP_INPUT_POWER_LIMIT: + ret = rt9467_run_aicl(info, &val->intval); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int rt9467_set_property(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + int ret = 0; + struct rt9467_info *info = power_supply_get_drvdata(psy); + + switch (psp) { +#if IS_ENABLED(CONFIG_TCPC_CLASS) + case POWER_SUPPLY_PROP_ONLINE: + ret = rt9467_enable_chg_type_det(info, !!val->intval); + break; + case POWER_SUPPLY_PROP_TYPE: + ret = rt9467_set_chg_type(info, val->intval); + break; +#endif /* CONFIG_TCPC_CLASS */ + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = rt9467_set_mivr(info, val->intval); + break; + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = rt9467_set_aicr(info, val->intval); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + ret = rt9467_set_cv(info, val->intval); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + ret = rt9467_set_ichg(info, val->intval); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + ret = rt9467_set_ieoc(info, val->intval); + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + switch (val->intval) { + case POWER_SUPPLY_CHARGE_TYPE_CUSTOM: + ret = rt9467_enable_otg(info, true); + break; + case POWER_SUPPLY_CHARGE_TYPE_STANDARD: + ret = rt9467_enable_otg(info, false); + if (ret < 0) + break; + ret = rt9467_enable_charging(info, true); + break; + default: + ret = rt9467_enable_otg(info, false); + if (ret < 0) + break; + ret = rt9467_enable_charging(info, false); + break; + } + break; + case POWER_SUPPLY_PROP_CHARGE_EMPTY: + ret = rt9467_enable_discharge(info, !!val->intval); + break; + case POWER_SUPPLY_PROP_INPUT_POWER_LIMIT: + ret = rt9467_enable_power_path(info, !!val->intval); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int rt9467_property_is_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + switch (psp) { +#if IS_ENABLED(CONFIG_TCPC_CLASS) + case POWER_SUPPLY_PROP_ONLINE: + case POWER_SUPPLY_PROP_TYPE: +#endif /* CONFIG_TCPC_CLASS */ + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: + case POWER_SUPPLY_PROP_CHARGE_FULL: + case POWER_SUPPLY_PROP_CHARGE_TYPE: + case POWER_SUPPLY_PROP_CHARGE_EMPTY: + case POWER_SUPPLY_PROP_INPUT_POWER_LIMIT: + return true; + default: + return false; + } +} + +static struct power_supply_desc rt9467_chg_psy_desc = { + .name = "charger", + .type = POWER_SUPPLY_TYPE_USB, + .properties = rt9467_properties, + .num_properties = ARRAY_SIZE(rt9467_properties), + .get_property = rt9467_get_property, + .set_property = rt9467_set_property, + .property_is_writeable = rt9467_property_is_writeable, +}; + +static void rt9467_init_setting_work_handler(struct work_struct *work) +{ + int ret = 0, retry_cnt = 0; + struct rt9467_info *info = container_of(work, + struct rt9467_info, init_work); + + do { + /* Select IINLMTSEL to use AICR */ + ret = rt9467_select_input_current_limit(info, + RT9467_IINLMTSEL_AICR); + if (ret < 0) { + dev_notice(info->dev, "%s: sel ilmtsel fail\n", + __func__); + retry_cnt++; + } + } while (retry_cnt < 5 && ret < 0); + + msleep(150); + + retry_cnt = 0; + do { + /* Disable hardware ILIM */ + ret = rt9467_enable_ilim(info, false); + if (ret < 0) { + dev_notice(info->dev, "%s: disable ilim fail\n", + __func__); + retry_cnt++; + } + } while (retry_cnt < 5 && ret < 0); + + rt9467_dump_register(info); + + /* Schedule work for microB's BC1.2 */ +#if !IS_ENABLED(CONFIG_TCPC_CLASS) + if (info->desc->en_chgdet) + schedule_work(&info->chgdet_work); +#endif /* CONFIG_TCPC_CLASS */ +} + +static void rt9467_kick_wdt_dwork_handler(struct work_struct *work) +{ + int ret = 0; + struct rt9467_info *info = container_of(work, + struct rt9467_info, kick_wdt_dwork.work); + + ret = rt9467_wdtmri_irq_handler(info); + schedule_delayed_work(&info->kick_wdt_dwork, + msecs_to_jiffies(ret < 0 ? 1000 : 30000)); +} + +/* ========================= */ +/* I2C driver function */ +/* ========================= */ + +static int rt9467_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int ret = 0; + struct rt9467_info *info = NULL; + struct power_supply_config psy_cfg = {}; + + pr_info("%s(%s)\n", __func__, RT9467_DRV_VERSION); + + info = devm_kzalloc(&client->dev, sizeof(struct rt9467_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + mutex_init(&info->i2c_access_lock); + mutex_init(&info->adc_access_lock); + mutex_init(&info->irq_access_lock); + mutex_init(&info->aicr_access_lock); + mutex_init(&info->ichg_access_lock); + mutex_init(&info->hidden_mode_lock); + mutex_init(&info->bc12_access_lock); + mutex_init(&info->ieoc_lock); + mutex_init(&info->tchg_lock); + mutex_init(&info->pp_lock); + atomic_set(&info->bc12_sdp_cnt, 0); + atomic_set(&info->bc12_wkard, 0); +#if IS_ENABLED(CONFIG_TCPC_CLASS) + atomic_set(&info->tcpc_usb_connected, 0); +#endif /* CONFIG_TCPC_CLASS */ + + info->client = client; + info->dev = &client->dev; + info->power_limit = 0; + info->hidden_mode_cnt = 0; + info->pp_en = true; + info->bc12_en = true; + info->ieoc_wkard = false; + info->ieoc = 250000; /* register default value 250mA */ + info->ichg = 2000000; /* register default value 2000mA */ + info->tchg = 25; + memcpy(info->irq_mask, rt9467_irq_maskall, RT9467_IRQIDX_MAX); + + /* Init wait queue head */ + init_waitqueue_head(&info->wait_queue); + + INIT_WORK(&info->init_work, rt9467_init_setting_work_handler); + INIT_DELAYED_WORK(&info->kick_wdt_dwork, + rt9467_kick_wdt_dwork_handler); +#if !IS_ENABLED(CONFIG_TCPC_CLASS) + INIT_WORK(&info->chgdet_work, rt9467_chgdet_work_handler); +#endif /* CONFIG_TCPC_CLASS */ + + /* Is HW exist */ + if (!rt9467_is_hw_exist(info)) { + dev_notice(info->dev, "%s: no rt9467 exists\n", __func__); + ret = -ENODEV; + goto err_no_dev; + } + i2c_set_clientdata(client, info); + + ret = rt9467_parse_dt(info, &client->dev); + if (ret < 0) { + dev_notice(info->dev, "%s: parse dt fail\n", __func__); + goto err_parse_dt; + } + +#ifdef CONFIG_RT_REGMAP + ret = rt9467_register_rt_regmap(info); + if (ret < 0) + goto err_register_regmap; +#endif /* CONFIG_RT_REGMAP */ + + ret = rt9467_reset_chip(info); + if (ret < 0) { + dev_notice(info->dev, "%s: reset chip fail\n", __func__); + goto err_reset_chip; + } + + ret = rt9467_init_setting(info); + if (ret < 0) { + dev_notice(info->dev, "%s: init setting fail\n", __func__); + goto err_init_setting; + } + + memcpy(&info->chg_psy_desc, &rt9467_chg_psy_desc, + sizeof(info->chg_psy_desc)); + psy_cfg.of_node = info->dev->of_node; + psy_cfg.drv_data = info; + info->chg_psy = devm_power_supply_register(info->dev, + &info->chg_psy_desc, + &psy_cfg); + if (IS_ERR(info->chg_psy)) { + dev_notice(info->dev, "register chg psy fail\n"); + ret = PTR_ERR(info->chg_psy); + goto err_register_chg_psy; + } + info->chg_psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN; + + ret = rt9467_irq_register(info); + if (ret < 0) { + dev_notice(info->dev, "%s: irq register fail\n", __func__); + goto err_irq_register; + } + + ret = rt9467_irq_init(info); + if (ret < 0) { + dev_notice(info->dev, "%s: irq init fail\n", __func__); + goto err_irq_init; + } + + schedule_work(&info->init_work); + dev_info(info->dev, "%s: successfully\n", __func__); + return ret; + +err_irq_init: +err_irq_register: +err_register_chg_psy: +err_init_setting: +err_reset_chip: +#ifdef CONFIG_RT_REGMAP + rt_regmap_device_unregister(info->regmap_dev); +err_register_regmap: +#endif +err_parse_dt: +err_no_dev: + mutex_destroy(&info->i2c_access_lock); + mutex_destroy(&info->adc_access_lock); + mutex_destroy(&info->irq_access_lock); + mutex_destroy(&info->aicr_access_lock); + mutex_destroy(&info->ichg_access_lock); + mutex_destroy(&info->hidden_mode_lock); + mutex_destroy(&info->bc12_access_lock); + mutex_destroy(&info->ieoc_lock); + mutex_destroy(&info->tchg_lock); + mutex_destroy(&info->pp_lock); + return ret; +} + + +static int rt9467_remove(struct i2c_client *client) +{ + int ret = 0; + struct rt9467_info *info = i2c_get_clientdata(client); + + pr_info("%s\n", __func__); + + if (info) { +#ifdef CONFIG_RT_REGMAP + rt_regmap_device_unregister(info->regmap_dev); +#endif + mutex_destroy(&info->i2c_access_lock); + mutex_destroy(&info->adc_access_lock); + mutex_destroy(&info->irq_access_lock); + mutex_destroy(&info->aicr_access_lock); + mutex_destroy(&info->ichg_access_lock); + mutex_destroy(&info->hidden_mode_lock); + mutex_destroy(&info->bc12_access_lock); + mutex_destroy(&info->ieoc_lock); + mutex_destroy(&info->tchg_lock); + mutex_destroy(&info->pp_lock); + } + + return ret; +} + +static void rt9467_shutdown(struct i2c_client *client) +{ + int ret = 0; + struct rt9467_info *info = i2c_get_clientdata(client); + + pr_info("%s\n", __func__); + if (info) { + devm_free_irq(&client->dev, client->irq, info); + + ret = rt9467_sw_reset(info); + if (ret < 0) + pr_notice("%s: sw reset fail\n", __func__); + /* Warnning! This is just a walkaround solution for reboot issue! + * Qualcomm project use REGNN pin connect to CBL_PWR_N will cause this bug*/ + //msleep(500); + } +} + +static int rt9467_suspend(struct device *dev) +{ + struct rt9467_info *info = dev_get_drvdata(dev); + + dev_info(dev, "%s\n", __func__); + if (device_may_wakeup(dev)) + enable_irq_wake(info->irq); + disable_irq(info->irq); + + return 0; +} + +static int rt9467_resume(struct device *dev) +{ + struct rt9467_info *info = dev_get_drvdata(dev); + + dev_info(dev, "%s\n", __func__); + enable_irq(info->irq); + if (device_may_wakeup(dev)) + disable_irq_wake(info->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(rt9467_pm_ops, rt9467_suspend, rt9467_resume); + +static const struct i2c_device_id rt9467_i2c_id[] = { + {"rt9467", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, rt9467_i2c_id); + +static const struct of_device_id rt9467_of_match[] = { + { .compatible = "richtek,rt9467", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rt9467_of_match); + +static struct i2c_driver rt9467_i2c_driver = { + .driver = { + .name = "rt9467", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rt9467_of_match), + .pm = &rt9467_pm_ops, + }, + .probe = rt9467_probe, + .remove = rt9467_remove, + .shutdown = rt9467_shutdown, + .id_table = rt9467_i2c_id, +}; + +module_i2c_driver(rt9467_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lucas Tsai "); +MODULE_DESCRIPTION("RT9467 Charger Driver"); +MODULE_VERSION(RT9467_DRV_VERSION); + +/* + * Release Note + * 1.0.0 + * (1) Porting from 1.0.19_MTK + * (2) Add HVDCP detection + */ diff --git a/drivers/power/rt9467_chg_mmi/rt9467_charger.h b/drivers/power/rt9467_chg_mmi/rt9467_charger.h new file mode 100644 index 000000000000..6ea62cca91c5 --- /dev/null +++ b/drivers/power/rt9467_chg_mmi/rt9467_charger.h @@ -0,0 +1,443 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Richtek RT9467 Charger Driver + * + * Copyright (C) 2021 Richtek Technology Corp. + */ + +#ifndef __RT9467_CHARGER_H +#define __RT9467_CHARGER_H + +#define RT9467_SLAVE_ADDR 0x5B +#define RT9467_VENDOR_ID 0x90 +#define RT9467_CHIP_REV_E1 0x01 +#define RT9467_CHIP_REV_E2 0x02 +#define RT9467_CHIP_REV_E3 0x03 +#define RT9467_CHIP_REV_E4 0x04 + +enum rt9467_reg_addr { + RT9467_REG_CORE_CTRL0, + RT9467_REG_CHG_CTRL1, + RT9467_REG_CHG_CTRL2, + RT9467_REG_CHG_CTRL3, + RT9467_REG_CHG_CTRL4, + RT9467_REG_CHG_CTRL5, + RT9467_REG_CHG_CTRL6, + RT9467_REG_CHG_CTRL7, + RT9467_REG_CHG_CTRL8, + RT9467_REG_CHG_CTRL9, + RT9467_REG_CHG_CTRL10, + RT9467_REG_CHG_CTRL11, + RT9467_REG_CHG_CTRL12, + RT9467_REG_CHG_CTRL13, + RT9467_REG_CHG_CTRL14, + RT9467_REG_CHG_CTRL15, + RT9467_REG_CHG_CTRL16, + RT9467_REG_CHG_ADC, + RT9467_REG_CHG_DPDM1, + RT9467_REG_CHG_DPDM2, + RT9467_REG_CHG_DPDM3, + RT9467_REG_CHG_DPDM6 = 0x17, + RT9467_REG_CHG_CTRL19, + RT9467_REG_CHG_CTRL17, + RT9467_REG_CHG_CTRL18, + RT9467_REG_CHG_HIDDEN_CTRL1 = 0x20, + RT9467_REG_CHG_HIDDEN_CTRL2, + RT9467_REG_CHG_HIDDEN_CTRL4 = 0x23, + RT9467_REG_CHG_HIDDEN_CTRL6 = 0x25, + RT9467_REG_CHG_HIDDEN_CTRL7, + RT9467_REG_CHG_HIDDEN_CTRL8, + RT9467_REG_CHG_HIDDEN_CTRL9, + RT9467_REG_CHG_HIDDEN_CTRL15 = 0x2E, + RT9467_REG_DEVICE_ID = 0x40, + RT9467_REG_CHG_STAT = 0x42, + RT9467_REG_CHG_NTC, + RT9467_REG_ADC_DATA_H, + RT9467_REG_ADC_DATA_L, + RT9467_REG_ADC_DATA_TUNE_H, + RT9467_REG_ADC_DATA_TUNE_L, + RT9467_REG_ADC_DATA_ORG_H, + RT9467_REG_ADC_DATA_ORG_L, + RT9467_REG_CHG_STATC = 0x50, + RT9467_REG_CHG_FAULT, + RT9467_REG_TS_STATC, + RT9467_REG_CHG_IRQ1, + RT9467_REG_CHG_IRQ2, + RT9467_REG_CHG_IRQ3, + RT9467_REG_DPDM_IRQ, + RT9467_REG_CHG_STATC_CTRL = 0x60, + RT9467_REG_CHG_FAULT_CTRL, + RT9467_REG_TS_STATC_CTRL, + RT9467_REG_CHG_IRQ1_CTRL, + RT9467_REG_CHG_IRQ2_CTRL, + RT9467_REG_CHG_IRQ3_CTRL, + RT9467_REG_DPDM_IRQ_CTRL, + RT9467_REG_MAX, +}; + +/* =========================== */ +/* RT9467 Parameter */ +/* =========================== */ + +/* uA */ +#define RT9467_ICHG_NUM 64 +#define RT9467_ICHG_MIN 100000 +#define RT9467_ICHG_MAX 5000000 +#define RT9467_ICHG_STEP 100000 + +/* uA */ +#define RT9467_IEOC_NUM 16 +#define RT9467_IEOC_MIN 100000 +#define RT9467_IEOC_MAX 850000 +#define RT9467_IEOC_STEP 50000 + +/* uV */ +#define RT9467_MIVR_NUM 128 +#define RT9467_MIVR_MIN 3900000 +#define RT9467_MIVR_MAX 13400000 +#define RT9467_MIVR_STEP 100000 + +/* uA */ +#define RT9467_AICR_NUM 64 +#define RT9467_AICR_MIN 100000 +#define RT9467_AICR_MAX 3250000 +#define RT9467_AICR_STEP 50000 + +/* uV */ +#define RT9467_CV_NUM 128 +#define RT9467_CV_MIN 3900000 +#define RT9467_CV_MAX 4710000 +#define RT9467_CV_STEP 10000 + +/* uV */ +#define RT9467_BOOST_VOREG_NUM 64 +#define RT9467_BOOST_VOREG_MIN 4425000 +#define RT9467_BOOST_VOREG_MAX 5825000 +#define RT9467_BOOST_VOREG_STEP 25000 + +/* uV */ +#define RT9467_VPREC_NUM 16 +#define RT9467_VPREC_MIN 2000000 +#define RT9467_VPREC_MAX 3500000 +#define RT9467_VPREC_STEP 100000 + +/* uA */ +#define RT9467_IPREC_NUM 16 +#define RT9467_IPREC_MIN 100000 +#define RT9467_IPREC_MAX 850000 +#define RT9467_IPREC_STEP 50000 + +/* IR compensation */ +/* uohm */ +#define RT9467_IRCMP_RES_NUM 8 +#define RT9467_IRCMP_RES_MIN 0 +#define RT9467_IRCMP_RES_MAX 175000 +#define RT9467_IRCMP_RES_STEP 25000 + +/* IR compensation maximum voltage clamp */ +/* uV */ +#define RT9467_IRCMP_VCLAMP_NUM 8 +#define RT9467_IRCMP_VCLAMP_MIN 0 +#define RT9467_IRCMP_VCLAMP_MAX 224000 +#define RT9467_IRCMP_VCLAMP_STEP 32000 + +/* PE+20 voltage */ +/* uV */ +#define RT9467_PEP20_VOLT_NUM 19 +#define RT9467_PEP20_VOLT_MIN 5500000 +#define RT9467_PEP20_VOLT_MAX 14500000 +#define RT9467_PEP20_VOLT_STEP 500000 + +/* IIN VTH */ +/* uV */ +#define RT9467_AICL_VTH_NUM 8 +#define RT9467_AICL_VTH_MIN 4100000 +#define RT9467_AICL_VTH_MAX 4800000 +#define RT9467_AICL_VTH_STEP 100000 + +/* ADC unit/offset */ +#define RT9467_ADC_UNIT_VBUS_DIV5 25000 /* uV */ +#define RT9467_ADC_UNIT_VBUS_DIV2 10000 /* uV */ +#define RT9467_ADC_UNIT_VBAT 5000 /* uV */ +#define RT9467_ADC_UNIT_VSYS 5000 /* uV */ +#define RT9467_ADC_UNIT_REGN 5000 /* uV */ +#define RT9467_ADC_UNIT_TS_BAT 25 /* 0.01% */ +#define RT9467_ADC_UNIT_IBUS 50000 /* uA */ +#define RT9467_ADC_UNIT_IBAT 50000 /* uA */ +#define RT9467_ADC_UNIT_TEMP_JC 2 /* degree */ + +#define RT9467_ADC_OFFSET_VBUS_DIV5 0 /* uV */ +#define RT9467_ADC_OFFSET_VBUS_DIV2 0 /* uV */ +#define RT9467_ADC_OFFSET_VBAT 0 /* uV */ +#define RT9467_ADC_OFFSET_VSYS 0 /* uV */ +#define RT9467_ADC_OFFSET_REGN 0 /* uV */ +#define RT9467_ADC_OFFSET_TS_BAT 0 /* % */ +#define RT9467_ADC_OFFSET_IBUS 0 /* uA */ +#define RT9467_ADC_OFFSET_IBAT 0 /* uA */ +#define RT9467_ADC_OFFSET_TEMP_JC (-40) /* degree */ + + +/* ========== CORE_CTRL0 0x00 ============ */ +#define RT9467_SHIFT_RST 7 +#define RT9467_MASK_RST (1 << RT9467_SHIFT_RST) + +/* ========== CHG_CTRL1 0x01 ============ */ +#define RT9467_SHIFT_OPA_MODE 0 +#define RT9467_SHIFT_HZ_EN 2 +#define RT9467_SHIFT_IRQ_PULSE 3 + +#define RT9467_MASK_OPA_MODE (1 << RT9467_SHIFT_OPA_MODE) +#define RT9467_MASK_HZ_EN (1 << RT9467_SHIFT_HZ_EN) +#define RT9467_MASK_IRQ_PULSE (1 << RT9467_SHIFT_IRQ_PULSE) + +/* ========== CHG_CTRL2 0x02 ============ */ +#define RT9467_SHIFT_CHG_EN 0 +#define RT9467_SHIFT_CFO_EN 1 +#define RT9467_SHIFT_IINLMTSEL 2 +#define RT9467_SHIFT_TE_EN 4 + +#define RT9467_MASK_CHG_EN (1 << RT9467_SHIFT_CHG_EN) +#define RT9467_MASK_CFO_EN (1 << RT9467_SHIFT_CFO_EN) +#define RT9467_MASK_IINLMTSEL 0x0C +#define RT9467_MASK_TE_EN (1 << RT9467_SHIFT_TE_EN) + + +/* ========== CHG_CTRL3 0x03 ============ */ +#define RT9467_SHIFT_AICR 2 +#define RT9467_SHIFT_AICR_EN 1 +#define RT9467_SHIFT_ILIM_EN 0 + +#define RT9467_MASK_AICR 0xFC +#define RT9467_MASK_AICR_EN (1 << RT9467_SHIFT_AICR_EN) +#define RT9467_MASK_ILIM_EN (1 << RT9467_SHIFT_ILIM_EN) + +/* ========== CHG_CTRL4 0x04 ============ */ +#define RT9467_SHIFT_CV 1 + +#define RT9467_MASK_CV 0xFE + +/* ========== CHG_CTRL5 0x05 ============ */ +#define RT9467_SHIFT_BOOST_VOREG 2 + +#define RT9467_MASK_BOOST_VOREG 0xFC + +/* ========== CHG_CTRL6 0x06 ============ */ +#define RT9467_SHIFT_MIVR 1 +#define RT9467_SHIFT_MIVR_EN 0 + +#define RT9467_MASK_MIVR 0xFE +#define RT9467_MASK_MIVR_EN (1 << RT9467_SHIFT_MIVR_EN) + +/* ========== CHG_CTRL7 0x07 ============ */ +#define RT9467_SHIFT_ICHG 2 + +#define RT9467_MASK_ICHG 0xFC + +/* ========== CHG_CTRL8 0x08 ============ */ +#define RT9467_SHIFT_VPREC 4 +#define RT9467_SHIFT_IPREC 0 + +#define RT9467_MASK_VPREC 0xF0 +#define RT9467_MASK_IPREC 0x0F + +/* ========== CHG_CTRL9 0x09 ============ */ +#define RT9467_SHIFT_IEOC 4 + +#define RT9467_MASK_IEOC 0xF0 + +/* ========== CHG_CTRL10 0x0A ============ */ +#define RT9467_SHIFT_BOOST_OC 0 + +#define RT9467_MASK_BOOST_OC 0x07 + +/* ========== CHG_CTRL12 0x0C ============ */ +#define RT9467_SHIFT_TMR_EN 1 +#define RT9467_SHIFT_WT_FC 5 + +#define RT9467_MASK_TMR_EN (1 << RT9467_SHIFT_TMR_EN) +#define RT9467_MASK_WT_FC 0xE0 + +/* ========== CHG_CTRL13 0x0D ============ */ +#define RT9467_SHIFT_WDT_EN 7 +#define RT9467_SHIFT_IRQ_REZ 0 + +#define RT9467_MASK_WDT_EN (1 << RT9467_SHIFT_WDT_EN) +#define RT9467_MASK_IRQ_REZ (1 << RT9467_SHIFT_IRQ_REZ) + +/* ========== CHG_CTRL14 0x0E ============ */ +#define RT9467_SHIFT_AICL_MEAS 7 +#define RT9467_SHIFT_AICL_VTH 0 + +#define RT9467_MASK_AICL_MEAS (1 << RT9467_SHIFT_AICL_MEAS) +#define RT9467_MASK_AICL_VTH 0x07 + +/* ========== CHG_CTRL16 0x10 ============ */ +#define RT9467_SHIFT_JEITA_EN 4 + +#define RT9467_MASK_JEITA_EN (1 << RT9467_SHIFT_JEITA_EN) + +/* ========== CHG_ADC 0x11 ============ */ +#define RT9467_SHIFT_ADC_IN_SEL 4 +#define RT9467_SHIFT_ADC_START 0 + +#define RT9467_MASK_ADC_IN_SEL 0xF0 +#define RT9467_MASK_ADC_START (1 << RT9467_SHIFT_ADC_START) + +/* ========== CHG_DPDM1 0x12 ============ */ +#define RT9467_SHIFT_USBCHGEN 7 +#define RT9467_SHIFT_HVDCP_EN 6 +#define RT9467_SHIFT_HVDCP 3 + +#define RT9467_MASK_USBCHGEN (1 << RT9467_SHIFT_USBCHGEN) +#define RT9467_MASK_HVDCP_EN (1 << RT9467_SHIFT_HVDCP_EN) +#define RT9467_MASK_HVDCP (1 << RT9467_SHIFT_HVDCP) + +/* ========== CHG_DPDM2 0x13 ============ */ +#define RT9467_SHIFT_USB_STATUS 0 + +#define RT9467_MASK_USB_STATUS 0x07 + +/* ========== CHG_DPDM6 0x17 ============ */ +#define RT9467_SHIFT_TIMEOUT_BCDONE 2 + +#define RT9467_MASK_TIMEOUT_BCDONE (1 << RT9467_SHIFT_TIMEOUT_BCDONE) + +/* ========== CHG_CTRL17 0x19 ============ */ +#define RT9467_SHIFT_PUMPX_EN 7 +#define RT9467_SHIFT_PUMPX_20_10 6 /* Version of PE */ +#define RT9467_SHIFT_PUMPX_UP_DN 5 +#define RT9467_SHIFT_PUMPX_DEC 0 + +#define RT9467_MASK_PUMPX_EN (1 << RT9467_SHIFT_PUMPX_EN) +#define RT9467_MASK_PUMPX_20_10 (1 << RT9467_SHIFT_PUMPX_20_10) +#define RT9467_MASK_PUMPX_UP_DN (1 << RT9467_SHIFT_PUMPX_UP_DN) +#define RT9467_MASK_PUMPX_DEC 0x1F + +/* ========== CHG_CTRL18 0x1A ============ */ +#define RT9467_SHIFT_IRCMP_RES 3 +#define RT9467_SHIFT_IRCMP_VCLAMP 0 + +#define RT9467_MASK_IRCMP_RES 0x38 +#define RT9467_MASK_IRCMP_VCLAMP 0x07 + +/* ========== CHG_STAT 0x42 ============ */ +#define RT9467_SHIFT_ADC_STAT 0 +#define RT9467_SHIFT_CHG_STAT 6 + +#define RT9467_MASK_ADC_STAT (1 << RT9467_SHIFT_ADC_STAT) +#define RT9467_MASK_CHG_STAT 0xC0 + +/* ========== CHG_STATC 0x50 ============ */ +#define RT9467_SHIFT_PWR_RDY 7 +#define RT9467_SHIFT_CHG_MIVR 6 +#define RT9467_SHIFT_CHG_AICR 5 + +#define RT9467_MASK_PWR_RDY (1 << RT9467_SHIFT_PWR_RDY) +#define RT9467_MASK_CHG_MIVR (1 << RT9467_SHIFT_CHG_MIVR) +#define RT9467_MASK_CHG_AICR (1 << RT9467_SHIFT_CHG_AICR) + +/* ========== CHG_FAULT 0x51 ============ */ +#define RT9467_SHIFT_VBUSOV 7 + +#define RT9467_MASK_VBUSOV (1 << RT9467_SHIFT_VBUSOV) + +/* ========== CHG_IRQ2 0x54 ============ */ +#define RT9467_SHIFT_CHG_AICLMEASI 0 +#define RT9467_SHIFT_WDTMRI 3 +#define RT9467_SHIFT_SSFINISHI 4 + +#define RT9467_MASK_CHG_AICLMEASI (1 << RT9467_SHIFT_CHG_AICLMEASI) +#define RT9467_MASK_WDTMRI (1 << RT9467_SHIFT_WDTMRI) +#define RT9467_MASK_SSFINISHI (1 << RT9467_SHIFT_SSFINISHI) + +/* ========== CHG_IRQ3 0x55 ============ */ +#define RT9467_SHIFT_ADC_DONEI 0 +#define RT9467_SHIFT_PUMPX_DONEI 1 + +#define RT9467_MASK_ADC_DONEI (1 << RT9467_SHIFT_ADC_DONEI) +#define RT9467_MASK_PUMPX_DONEI (1 << RT9467_SHIFT_PUMPX_DONEI) + +/* ========== DPDM_IRQ 0x56 ============ */ +#define RT9467_SHIFT_ATTACHI 0 + +#define RT9467_MASK_ATTACHI (1 << RT9467_SHIFT_ATTACHI) + +/* ========== CHG_STATC_CTRL 0x60 ============ */ +#define RT9467_SHIFT_PWR_RDYM 7 +#define RT9467_SHIFT_CHG_MIVRM 6 +#define RT9467_SHIFT_CHG_AICRM 5 + +#define RT9467_MASK_PWR_RDYM (1 << RT9467_SHIFT_PWR_RDYM) +#define RT9467_MASK_CHG_MIVRM (1 << RT9467_SHIFT_CHG_MIVRM) +#define RT9467_MASK_CHG_AICRM (1 << RT9467_SHIFT_CHG_AICRM) + +/* ========== CHG_FAULT_CTRL 0x61 ============ */ +#define RT9467_SHIFT_VBUSOVM 7 + +#define RT9467_MASK_VBUSOVM (1 << RT9467_SHIFT_VBUSOVM) + +/* ========== CHG_TS_STATC_CTRL 0x62 ============ */ +#define RT9467_SHIFT_TS_BAT_HOTM 7 +#define RT9467_SHIFT_TS_BAT_WARMM 6 +#define RT9467_SHIFT_TS_BAT_COOLM 5 +#define RT9467_SHIFT_TS_BAT_COLDM 4 + +#define RT9467_MASK_TS_BAT_HOTM (1 << RT9467_SHIFT_TS_BAT_HOTM) +#define RT9467_MASK_TS_BAT_WARMM (1 << RT9467_SHIFT_TS_BAT_WARMM) +#define RT9467_MASK_TS_BAT_COOLM (1 << RT9467_SHIFT_TS_BAT_COOLM) +#define RT9467_MASK_TS_BAT_COLDM (1 << RT9467_SHIFT_TS_BAT_COLDM) +#define RT9467_MASK_TS_STATC_RESERVED 0x0F + +/* ========== CHG_IRQ1_CTRL 0x63 ============ */ +#define RT9467_SHIFT_CHG_OTPM 7 +#define RT9467_SHIFT_CHG_RVPM 6 +#define RT9467_SHIFT_CHG_ADPBADM 5 +#define RT9467_SHIFT_CHG_STATCM 2 +#define RT9467_SHIFT_CHG_FAULTM 1 +#define RT9467_SHIFT_TS_STATCM 0 + +#define RT9467_MASK_CHG_OTPM (1 << RT9467_SHIFT_CHG_OTPM) +#define RT9467_MASK_CHG_RVPM (1 << RT9467_SHIFT_CHG_RVPM) +#define RT9467_MASK_CHG_ADPBADM (1 << RT9467_SHIFT_CHG_ADPBADM) +#define RT9467_MASK_CHG_STATCM (1 << RT9467_SHIFT_CHG_STATCM) +#define RT9467_MASK_CHG_FAULTM (1 << RT9467_SHIFT_CHG_FAULTM) +#define RT9467_MASK_TS_STATCM (1 << RT9467_SHIFT_TS_STATCM) + +/* ========== CHG_IRQ2_CTRL 0x64 ============ */ +#define RT9467_SHIFT_IEOCM 7 +#define RT9467_SHIFT_TERMM 6 +#define RT9467_SHIFT_SSFINISHM 4 +#define RT9467_SHIFT_CHG_AICLMEASM 0 + +#define RT9467_MASK_IEOCM (1 << RT9467_SHIFT_IEOCM) +#define RT9467_MASK_TERMM (1 << RT9467_SHIFT_TERMM) +#define RT9467_MASK_SSFINISHM (1 << RT9467_SHIFT_SSFINISHM) +#define RT9467_MASK_CHG_AICLMEASM (1 << RT9467_SHIFT_CHG_AICLMEASM) +#define RT9467_MASK_IRQ2_RESERVED 0x04 + +/* ========== CHG_IRQ3_CTRL 0x65 ============ */ +#define RT9467_SHIFT_BST_BATUVM 5 +#define RT9467_SHIFT_PUMPX_DONEM 1 +#define RT9467_SHIFT_ADC_DONEM 0 + +#define RT9467_MASK_BST_BATUVM (1 << RT9467_SHIFT_BST_BATUVM) +#define RT9467_MASK_PUMPX_DONEM (1 << RT9467_SHIFT_PUMPX_DONEM) +#define RT9467_MASK_ADC_DONEM (1 << RT9467_SHIFT_ADC_DONEM) +#define RT9467_MASK_IRQ3_RESERVED 0x1C + +/* ========== DPDM_IRQ_CTRL 0x66 ============ */ +#define RT9467_SHIFT_DCDTM 7 +#define RT9467_SHIFT_CHGDETM 6 +#define RT9467_SHIFT_HVDCP_DETM 5 +#define RT9467_SHIFT_DETACHM 1 +#define RT9467_SHIFT_ATTACHM 0 + +#define RT9467_MASK_DCDTM (1 << RT9467_SHIFT_DCDTM) +#define RT9467_MASK_CHGDETM (1 << RT9467_SHIFT_CHGDETM) +#define RT9467_MASK_HVDCP_DETM (1 << RT9467_SHIFT_HVDCP_DETM) +#define RT9467_MASK_DETACHM (1 << RT9467_SHIFT_DETACHM) +#define RT9467_MASK_ATTACHM (1 << RT9467_SHIFT_ATTACHM) +#define RT9467_MASK_DPDMIRQ_RESERVED 0x1C + + +#endif /* __RT9467_CHARGER_H */ diff --git a/drivers/power/rt9471_chg_mmi/Android.mk b/drivers/power/rt9471_chg_mmi/Android.mk new file mode 100644 index 000000000000..af355151bfc9 --- /dev/null +++ b/drivers/power/rt9471_chg_mmi/Android.mk @@ -0,0 +1,21 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := rt9471_charger.ko + +ifeq ($(MMI_QC3P_TURBO_CHARGER),true) + KERNEL_CFLAGS += CONFIG_MMI_QC3P_TURBO_CHARGER=y +endif + +ifeq ($(MMI_QC3P_WT6670_DETECTED),true) + KERNEL_CFLAGS += CONFIG_MMI_QC3P_WT6670_DETECTED=y +endif + +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_discrete_charger_class.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki + +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/rt9471_chg_mmi/Kbuild b/drivers/power/rt9471_chg_mmi/Kbuild new file mode 100644 index 000000000000..dbe3b256ecd7 --- /dev/null +++ b/drivers/power/rt9471_chg_mmi/Kbuild @@ -0,0 +1,16 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/rt9471_chg_mmi + +ifneq ($(filter m y,$(CONFIG_MMI_QC3P_TURBO_CHARGER)),) + EXTRA_CFLAGS += -DCONFIG_MMI_QC3P_TURBO_CHARGER +endif + +ifneq ($(filter m y,$(CONFIG_MMI_QC3P_WT6670_DETECTED)),) + EXTRA_CFLAGS += -DCONFIG_MMI_QC3P_WT6670_DETECTED +endif + +obj-m += rt9471_charger.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_discrete_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/rt9471_chg_mmi/Makefile b/drivers/power/rt9471_chg_mmi/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/power/rt9471_chg_mmi/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/power/rt9471_chg_mmi/rt9471_charger.c b/drivers/power/rt9471_chg_mmi/rt9471_charger.c new file mode 100644 index 000000000000..7405b509c463 --- /dev/null +++ b/drivers/power/rt9471_chg_mmi/rt9471_charger.c @@ -0,0 +1,3389 @@ +/* + * Copyright (C) 2021 MediaTek Inc. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_RT_REGMAP +#include +#endif /* CONFIG_RT_REGMAP */ + + +#include "rt9471_charger.h" +#define RT9471_DRV_VERSION "1.0.17_MTK" + +#define RT9471_STATUS_PLUGIN 0x0001 +#define RT9471_STATUS_PG 0x0002 +#define RT9471_STATUS_CHARGE_ENABLE 0x0004 +#define RT9471_STATUS_FAULT 0x0008 +#define RT9471_STATUS_EXIST 0x0100 + +#define MMI_HVDCP2_VOLTAGE_STANDARD 8000000 +#define MMI_HVDCP3_VOLTAGE_STANDARD 7500000 +#define MMI_HVDCP_DETECT_ICL_LIMIT 500000 + +enum rt9471_stat_idx { + RT9471_STATIDX_STAT0 = 0, + RT9471_STATIDX_STAT1, + RT9471_STATIDX_STAT2, + RT9471_STATIDX_STAT3, + RT9471_STATIDX_MAX, +}; + +enum rt9471_irq_idx { + RT9471_IRQIDX_IRQ0 = 0, + RT9471_IRQIDX_IRQ1, + RT9471_IRQIDX_IRQ2, + RT9471_IRQIDX_IRQ3, + RT9471_IRQIDX_MAX, +}; + +enum rt9471_ic_stat { + RT9471_ICSTAT_SLEEP = 0, + RT9471_ICSTAT_VBUSRDY, + RT9471_ICSTAT_TRICKLECHG, + RT9471_ICSTAT_PRECHG, + RT9471_ICSTAT_FASTCHG, + RT9471_ICSTAT_IEOC, + RT9471_ICSTAT_BGCHG, + RT9471_ICSTAT_CHGDONE, + RT9471_ICSTAT_CHGFAULT, + RT9471_ICSTAT_OTG = 15, + RT9471_ICSTAT_MAX, +}; + +static const char * const rt9471_ic_stat_names[RT9471_ICSTAT_MAX] = { + "hz/sleep", "ready", "trickle-charge", "pre-charge", + "fast-charge", "ieoc-charge", "background-charge", + "done", "fault", "RESERVED", "RESERVED", "RESERVED", + "RESERVED", "RESERVED", "RESERVED", "OTG", +}; + +enum rt9471_mivr_track { + RT9471_MIVRTRACK_REG = 0, + RT9471_MIVRTRACK_VBAT_200MV, + RT9471_MIVRTRACK_VBAT_250MV, + RT9471_MIVRTRACK_VBAT_300MV, + RT9471_MIVRTRACK_MAX, +}; + +enum rt9471_port_stat { + RT9471_PORTSTAT_NOINFO = 0, + RT9471_PORTSTAT_APPLE_10W = 8, + RT9471_PORTSTAT_SAMSUNG_10W, + RT9471_PORTSTAT_APPLE_5W, + RT9471_PORTSTAT_APPLE_12W, + RT9471_PORTSTAT_NSDP, + RT9471_PORTSTAT_SDP, + RT9471_PORTSTAT_CDP, + RT9471_PORTSTAT_DCP, + RT9471_PORTSTAT_MAX, +}; + +enum rt9471_usbsw_state { + RT9471_USBSW_CHG = 0, + RT9471_USBSW_USB, +}; + +enum rt9471_hz_user { + RT9471_HZU_PP, + RT9471_HZU_BC12, + RT9471_HZU_OTG, + RT9471_HZU_VBUS_GD, + RT9471_HZU_MAX, +}; + +static const char * const rt9471_hz_user_names[RT9471_HZU_MAX] = { + "PP", "BC12", "OTG", "VBUS_GD", +}; + +struct rt9471_desc { + const char *chg_name; + const char *rm_name; + u8 rm_dev_addr; + u32 vac_ovp; + u32 mivr; + u32 aicr; + u32 cv; + u32 ichg; + u32 ieoc; + u32 safe_tmr; + u32 wdt; + u32 mivr_track; + bool en_safe_tmr; + bool en_te; + bool en_jeita; + bool ceb_invert; + bool dis_i2c_tout; + bool en_qon_rst; + bool auto_aicr; +}; + +/* These default values will be applied if there's no property in dts */ +static struct rt9471_desc rt9471_default_desc = { + .chg_name = "primary_chg", + .rm_name = "rt9471", + .rm_dev_addr = RT9471_DEVICE_ADDR, + .vac_ovp = 6500000, + .mivr = 4500000, + .aicr = 500000, + .cv = 4200000, + .ichg = 2000000, + .ieoc = 200000, + .safe_tmr = 10, + .wdt = 40, + .mivr_track = RT9471_MIVRTRACK_REG, + .en_safe_tmr = true, + .en_te = true, + .en_jeita = true, + .ceb_invert = false, + .dis_i2c_tout = false, + .en_qon_rst = true, + .auto_aicr = true, +}; + +static const u8 rt9471_irq_maskall[RT9471_IRQIDX_MAX] = { + 0xFF, 0xFF, 0xFF, 0xFF, +}; + +static const u32 rt9471_vac_ovp[] = { + 5800000, 6500000, 10900000, 14000000, +}; + +static const u32 rt9471_wdt[] = { + 0, 40, 80, 160, +}; + +static const u32 rt9471_otgcc[] = { + 500000, 1200000, +}; + +static const u8 rt9471_val_en_hidden_mode[] = { + 0x69, 0x96, +}; + +static enum power_supply_usb_type rt9471_usb_type[] = { + POWER_SUPPLY_USB_TYPE_UNKNOWN, + POWER_SUPPLY_USB_TYPE_SDP, + POWER_SUPPLY_USB_TYPE_DCP, + POWER_SUPPLY_USB_TYPE_CDP, +}; + +struct rt9471_state { + bool online; + bool vbus_gd; +}; + +struct rt9471_iio { + struct iio_channel *usbin_v_chan; +}; + +struct rt9471_chip { + struct i2c_client *client; + struct device *dev; + struct charger_device *chg_dev; + struct charger_properties chg_props; + struct mutex io_lock; + struct mutex hidden_mode_lock; + struct mutex hz_lock; + int hidden_mode_cnt; + u8 dev_id; + u8 dev_rev; + u8 chip_rev; + struct rt9471_desc *desc; + u32 intr_gpio; + u32 ceb_gpio; + int irq; + u8 irq_mask[RT9471_IRQIDX_MAX]; + bool chg_done_once; + struct wakeup_source *buck_dwork_ws; + struct delayed_work buck_dwork; +#ifdef CONFIG_RT_REGMAP + struct rt_regmap_device *rm_dev; + struct rt_regmap_properties *rm_prop; +#endif /* CONFIG_RT_REGMAP */ + bool enter_shipping_mode; + struct completion aicc_done; + struct completion pe_done; + bool is_primary; + bool hz_users[RT9471_HZU_MAX]; + + int real_charger_type; + const char *mmi_chg_dev_name; + struct power_supply *charger; + struct regulator *dpdm_reg; + struct mutex regulator_lock; + bool dpdm_enabled; + u32 input_current_cache; + struct delayed_work monitor_work; + struct delayed_work recheck_charger_work; + struct work_struct charge_detect_work; + unsigned int status; + struct rt9471_state state; + struct rt9471_iio iio; + + bool mmi_qc3_support; + wait_queue_head_t mmi_qc3_wait_que; + bool mmi_qc3_trig_flag; + struct task_struct *mmi_qc3_authen_task; + bool mmi_is_qc3_authen; + +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED + struct iio_channel **ext_iio_chans; +#endif +}; + +static struct power_supply_desc rt9471_power_supply_desc; +static int rt9471_set_aicr(struct charger_device *chg_dev, u32 uA); + +static const u8 rt9471_reg_addr[] = { + RT9471_REG_OTGCFG, + RT9471_REG_TOP, + RT9471_REG_FUNCTION, + RT9471_REG_IBUS, + RT9471_REG_VBUS, + RT9471_REG_PRECHG, + RT9471_REG_REGU, + RT9471_REG_VCHG, + RT9471_REG_ICHG, + RT9471_REG_CHGTIMER, + RT9471_REG_EOC, + RT9471_REG_INFO, + RT9471_REG_JEITA, + RT9471_REG_PUMPEXP, + RT9471_REG_DPDMDET, + RT9471_REG_STATUS, + RT9471_REG_STAT0, + RT9471_REG_STAT1, + RT9471_REG_STAT2, + RT9471_REG_STAT3, + /* Skip IRQs to prevent reading clear while dumping registers */ + RT9471_REG_MASK0, + RT9471_REG_MASK1, + RT9471_REG_MASK2, + RT9471_REG_MASK3, +}; + +static int rt9471_read_device(void *client, u32 addr, int len, void *dst) +{ + int ret = 0; + + ret = i2c_smbus_read_i2c_block_data(client, addr, len, dst); + + return (ret < 0) ? ret : 0; +} + +static int rt9471_write_device(void *client, u32 addr, int len, + const void *src) +{ + return i2c_smbus_write_i2c_block_data(client, addr, len, src); +} + +#ifdef CONFIG_RT_REGMAP +RT_REG_DECL(RT9471_REG_OTGCFG, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_TOP, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_FUNCTION, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_IBUS, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_VBUS, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_PRECHG, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_REGU, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_VCHG, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_ICHG, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_CHGTIMER, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_EOC, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_INFO, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_JEITA, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_PUMPEXP, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_DPDMDET, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_STATUS, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_STAT0, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_STAT1, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_STAT2, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_STAT3, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_IRQ0, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_IRQ1, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_IRQ2, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_IRQ3, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_MASK0, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_MASK1, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_MASK2, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT9471_REG_MASK3, 1, RT_VOLATILE, {}); + +static const rt_register_map_t rt9471_rm_map[] = { + RT_REG(RT9471_REG_OTGCFG), + RT_REG(RT9471_REG_TOP), + RT_REG(RT9471_REG_FUNCTION), + RT_REG(RT9471_REG_IBUS), + RT_REG(RT9471_REG_VBUS), + RT_REG(RT9471_REG_PRECHG), + RT_REG(RT9471_REG_REGU), + RT_REG(RT9471_REG_VCHG), + RT_REG(RT9471_REG_ICHG), + RT_REG(RT9471_REG_CHGTIMER), + RT_REG(RT9471_REG_EOC), + RT_REG(RT9471_REG_INFO), + RT_REG(RT9471_REG_JEITA), + RT_REG(RT9471_REG_PUMPEXP), + RT_REG(RT9471_REG_DPDMDET), + RT_REG(RT9471_REG_STATUS), + RT_REG(RT9471_REG_STAT0), + RT_REG(RT9471_REG_STAT1), + RT_REG(RT9471_REG_STAT2), + RT_REG(RT9471_REG_STAT3), + RT_REG(RT9471_REG_IRQ0), + RT_REG(RT9471_REG_IRQ1), + RT_REG(RT9471_REG_IRQ2), + RT_REG(RT9471_REG_IRQ3), + RT_REG(RT9471_REG_MASK0), + RT_REG(RT9471_REG_MASK1), + RT_REG(RT9471_REG_MASK2), + RT_REG(RT9471_REG_MASK3), +}; + +static struct rt_regmap_fops rt9471_rm_fops = { + .read_device = rt9471_read_device, + .write_device = rt9471_write_device, +}; + +static int rt9471_register_rt_regmap(struct rt9471_chip *chip) +{ + struct rt_regmap_properties *prop = NULL; + + dev_info(chip->dev, "%s\n", __func__); + + prop = devm_kzalloc(chip->dev, sizeof(*prop), GFP_KERNEL); + if (!prop) + return -ENOMEM; + + prop->name = chip->desc->rm_name; + prop->aliases = chip->desc->rm_name; + prop->register_num = ARRAY_SIZE(rt9471_rm_map); + prop->rm = rt9471_rm_map; + prop->rt_regmap_mode = RT_SINGLE_BYTE | RT_CACHE_DISABLE | + RT_IO_PASS_THROUGH | RT_DBG_SPECIAL; + prop->io_log_en = 0; + + chip->rm_prop = prop; + chip->rm_dev = rt_regmap_device_register_ex(chip->rm_prop, + &rt9471_rm_fops, chip->dev, + chip->client, + chip->desc->rm_dev_addr, + chip); + if (!chip->rm_dev) { + dev_notice(chip->dev, "%s fail\n", __func__); + return -EIO; + } + + return 0; +} +#endif /* CONFIG_RT_REGMAP */ + +static inline int __rt9471_i2c_read_byte(struct rt9471_chip *chip, u8 cmd, + u8 *data) +{ + int ret = 0; + u8 regval = 0; + +#ifdef CONFIG_RT_REGMAP + ret = rt_regmap_block_read(chip->rm_dev, cmd, 1, ®val); +#else + ret = rt9471_read_device(chip->client, cmd, 1, ®val); +#endif /* CONFIG_RT_REGMAP */ + + if (ret < 0) + dev_notice(chip->dev, "%s reg0x%02X fail(%d)\n", + __func__, cmd, ret); + else { + dev_dbg(chip->dev, "%s reg0x%02X = 0x%02X\n", + __func__, cmd, regval); + *data = regval; + } + + return ret; +} + +static int rt9471_i2c_read_byte(struct rt9471_chip *chip, u8 cmd, u8 *data) +{ + int ret = 0; + + mutex_lock(&chip->io_lock); + ret = __rt9471_i2c_read_byte(chip, cmd, data); + mutex_unlock(&chip->io_lock); + + return ret; +} + +static inline int __rt9471_i2c_write_byte(struct rt9471_chip *chip, u8 cmd, + u8 data) +{ + int ret = 0; + +#ifdef CONFIG_RT_REGMAP + ret = rt_regmap_block_write(chip->rm_dev, cmd, 1, &data); +#else + ret = rt9471_write_device(chip->client, cmd, 1, &data); +#endif /* CONFIG_RT_REGMAP */ + + if (ret < 0) + dev_notice(chip->dev, "%s reg0x%02X = 0x%02X fail(%d)\n", + __func__, cmd, data, ret); + else + dev_dbg(chip->dev, "%s reg0x%02X = 0x%02X\n", + __func__, cmd, data); + + return ret; +} + +static int rt9471_i2c_write_byte(struct rt9471_chip *chip, u8 cmd, u8 data) +{ + int ret = 0; + + mutex_lock(&chip->io_lock); + ret = __rt9471_i2c_write_byte(chip, cmd, data); + mutex_unlock(&chip->io_lock); + + return ret; +} + +static inline int __rt9471_i2c_block_read(struct rt9471_chip *chip, u8 cmd, + u32 len, u8 *data) +{ + int ret = 0, i = 0; + +#ifdef CONFIG_RT_REGMAP + ret = rt_regmap_block_read(chip->rm_dev, cmd, len, data); +#else + ret = rt9471_read_device(chip->client, cmd, len, data); +#endif /* CONFIG_RT_REGMAP */ + + if (ret < 0) + dev_notice(chip->dev, "%s reg0x%02X..reg0x%02X fail(%d)\n", + __func__, cmd, cmd + len - 1, ret); + else + for (i = 0; i <= len - 1; i++) + dev_dbg(chip->dev, "%s reg0x%02X = 0x%02X\n", + __func__, cmd + i, data[i]); + + return ret; +} + +static int rt9471_i2c_block_read(struct rt9471_chip *chip, u8 cmd, u32 len, + u8 *data) +{ + int ret = 0; + + mutex_lock(&chip->io_lock); + ret = __rt9471_i2c_block_read(chip, cmd, len, data); + mutex_unlock(&chip->io_lock); + + return ret; +} + +static inline int __rt9471_i2c_block_write(struct rt9471_chip *chip, u8 cmd, + u32 len, const u8 *data) +{ + int ret = 0, i = 0; + +#ifdef CONFIG_RT_REGMAP + ret = rt_regmap_block_write(chip->rm_dev, cmd, len, data); +#else + ret = rt9471_write_device(chip->client, cmd, len, data); +#endif /* CONFIG_RT_REGMAP */ + + if (ret < 0) { + dev_notice(chip->dev, "%s fail(%d)\n", __func__, ret); + for (i = 0; i <= len - 1; i++) + dev_notice(chip->dev, "%s reg0x%02X = 0x%02X\n", + __func__, cmd + i, data[i]); + } else + for (i = 0; i <= len - 1; i++) + dev_dbg(chip->dev, "%s reg0x%02X = 0x%02X\n", + __func__, cmd + i, data[i]); + + return ret; +} + +static int rt9471_i2c_block_write(struct rt9471_chip *chip, u8 cmd, u32 len, + const u8 *data) +{ + int ret = 0; + + mutex_lock(&chip->io_lock); + ret = __rt9471_i2c_block_write(chip, cmd, len, data); + mutex_unlock(&chip->io_lock); + + return ret; +} + +static int rt9471_i2c_test_bit(struct rt9471_chip *chip, u8 cmd, u8 shift, + bool *is_one) +{ + int ret = 0; + u8 regval = 0; + + ret = rt9471_i2c_read_byte(chip, cmd, ®val); + if (ret < 0) { + *is_one = false; + return ret; + } + + regval &= 1 << shift; + *is_one = (regval ? true : false); + + return ret; +} + +static int rt9471_i2c_update_bits(struct rt9471_chip *chip, u8 cmd, u8 data, + u8 mask) +{ + int ret = 0; + u8 regval = 0; + + mutex_lock(&chip->io_lock); + ret = __rt9471_i2c_read_byte(chip, cmd, ®val); + if (ret < 0) + goto out; + + regval &= ~mask; + regval |= (data & mask); + + ret = __rt9471_i2c_write_byte(chip, cmd, regval); +out: + mutex_unlock(&chip->io_lock); + return ret; +} + +static inline int rt9471_set_bit(struct rt9471_chip *chip, u8 cmd, u8 mask) +{ + return rt9471_i2c_update_bits(chip, cmd, mask, mask); +} + +static inline int rt9471_clr_bit(struct rt9471_chip *chip, u8 cmd, u8 mask) +{ + return rt9471_i2c_update_bits(chip, cmd, 0x00, mask); +} + +static inline u8 rt9471_closest_reg(u32 min, u32 max, u32 step, u32 target) +{ + if (target < min) + return 0; + + if (target >= max) + target = max; + + return (target - min) / step; +} + +static inline u8 rt9471_closest_reg_via_tbl(const u32 *tbl, u32 tbl_size, + u32 target) +{ + u32 i = 0; + + if (target < tbl[0]) + return 0; + + for (i = 0; i < tbl_size - 1; i++) { + if (target >= tbl[i] && target < tbl[i + 1]) + return i; + } + + return tbl_size - 1; +} + +static inline u32 rt9471_closest_value(u32 min, u32 max, u32 step, u8 regval) +{ + u32 val = 0; + + val = min + regval * step; + if (val > max) + val = max; + + return val; +} + +static int rt9471_enable_hidden_mode(struct rt9471_chip *chip, bool en) +{ + int ret = 0; + + mutex_lock(&chip->hidden_mode_lock); + + if (en) { + if (chip->hidden_mode_cnt == 0) { + ret = rt9471_i2c_block_write(chip, RT9471_REG_PASSCODE1, + ARRAY_SIZE(rt9471_val_en_hidden_mode), + rt9471_val_en_hidden_mode); + if (ret < 0) + goto err; + } + chip->hidden_mode_cnt++; + } else { + if (chip->hidden_mode_cnt == 1) { /* last one */ + ret = rt9471_i2c_write_byte(chip, RT9471_REG_PASSCODE1, + 0x00); + if (ret < 0) + goto err; + } + chip->hidden_mode_cnt--; + } + dev_info(chip->dev, "%s en = %d, cnt = %d\n", + __func__, en, chip->hidden_mode_cnt); + goto out; + +err: + dev_notice(chip->dev, "%s en = %d fail(%d)\n", __func__, en, ret); +out: + mutex_unlock(&chip->hidden_mode_lock); + return ret; +} + +static int __rt9471_get_ic_stat(struct rt9471_chip *chip, + enum rt9471_ic_stat *stat) +{ + int ret = 0; + u8 regval = 0; + +#ifdef RT9471_I2C_NO_MUTEX + ret = __rt9471_i2c_read_byte(chip, RT9471_REG_STATUS, ®val); +#else + ret = rt9471_i2c_read_byte(chip, RT9471_REG_STATUS, ®val); +#endif + if (ret < 0) + return ret; + *stat = (regval & RT9471_ICSTAT_MASK) >> RT9471_ICSTAT_SHIFT; + + return ret; +} + +static int __rt9471_get_mivr(struct rt9471_chip *chip, u32 *mivr) +{ + int ret = 0; + u8 regval = 0; + +#ifdef RT9471_I2C_NO_MUTEX + ret = __rt9471_i2c_read_byte(chip, RT9471_REG_VBUS, ®val); +#else + ret = rt9471_i2c_read_byte(chip, RT9471_REG_VBUS, ®val); +#endif + if (ret < 0) + return ret; + + regval = (regval & RT9471_MIVR_MASK) >> RT9471_MIVR_SHIFT; + *mivr = rt9471_closest_value(RT9471_MIVR_MIN, RT9471_MIVR_MAX, + RT9471_MIVR_STEP, regval); + + return ret; +} + +static int __rt9471_get_aicr(struct rt9471_chip *chip, u32 *aicr) +{ + int ret = 0; + u8 regval = 0; + +#ifdef RT9471_I2C_NO_MUTEX + ret = __rt9471_i2c_read_byte(chip, RT9471_REG_IBUS, ®val); +#else + ret = rt9471_i2c_read_byte(chip, RT9471_REG_IBUS, ®val); +#endif + if (ret < 0) + return ret; + + regval = (regval & RT9471_AICR_MASK) >> RT9471_AICR_SHIFT; + *aicr = rt9471_closest_value(RT9471_AICR_MIN, RT9471_AICR_MAX, + RT9471_AICR_STEP, regval); + if (*aicr > RT9471_AICR_MIN && *aicr < RT9471_AICR_MAX) + *aicr -= RT9471_AICR_STEP; + + return ret; +} + +static int __rt9471_get_cv(struct rt9471_chip *chip, u32 *cv) +{ + int ret = 0; + u8 regval = 0; + + ret = rt9471_i2c_read_byte(chip, RT9471_REG_VCHG, ®val); + if (ret < 0) + return ret; + + regval = (regval & RT9471_CV_MASK) >> RT9471_CV_SHIFT; + *cv = rt9471_closest_value(RT9471_CV_MIN, RT9471_CV_MAX, RT9471_CV_STEP, + regval); + + return ret; +} + +static int __rt9471_get_ichg(struct rt9471_chip *chip, u32 *ichg) +{ + int ret = 0; + u8 regval = 0; + + ret = rt9471_i2c_read_byte(chip, RT9471_REG_ICHG, ®val); + if (ret < 0) + return ret; + + regval = (regval & RT9471_ICHG_MASK) >> RT9471_ICHG_SHIFT; + *ichg = rt9471_closest_value(RT9471_ICHG_MIN, RT9471_ICHG_MAX, + RT9471_ICHG_STEP, regval); + + return ret; +} + +static int __rt9471_get_ieoc(struct rt9471_chip *chip, u32 *ieoc) +{ + int ret = 0; + u8 regval = 0; + + ret = rt9471_i2c_read_byte(chip, RT9471_REG_EOC, ®val); + if (ret < 0) + return ret; + + regval = (regval & RT9471_IEOC_MASK) >> RT9471_IEOC_SHIFT; + *ieoc = rt9471_closest_value(RT9471_IEOC_MIN, RT9471_IEOC_MAX, + RT9471_IEOC_STEP, regval); + + return ret; +} + +static inline int __rt9471_is_hz_enabled(struct rt9471_chip *chip, bool *en) +{ + if (chip->is_primary) + return rt9471_i2c_test_bit(chip, RT9471_REG_FUNCTION, + RT9471_HZ_SHIFT, en); + else + return rt9471_i2c_test_bit(chip, RT9471_REG_HIDDEN_2, + RT9471_FORCE_HZ_SHIFT, en); +} + +static inline int __rt9471_is_chg_enabled(struct rt9471_chip *chip, bool *en) +{ + return rt9471_i2c_test_bit(chip, RT9471_REG_FUNCTION, + RT9471_CHG_EN_SHIFT, en); +} + +static int __rt9471_enable_shipmode(struct rt9471_chip *chip) +{ + const u8 mask = RT9471_BATFETDIS_MASK | RT9471_HZ_MASK; + + dev_info(chip->dev, "%s\n", __func__); + + return rt9471_i2c_update_bits(chip, RT9471_REG_FUNCTION, mask, mask); +} + +static int __rt9471_enable_safe_tmr(struct rt9471_chip *chip, bool en) +{ + dev_info(chip->dev, "%s en = %d\n", __func__, en); + return (en ? rt9471_set_bit : rt9471_clr_bit) + (chip, RT9471_REG_CHGTIMER, RT9471_SAFETMR_EN_MASK); +} + +static int __rt9471_eoc_rst(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return rt9471_set_bit(chip, RT9471_REG_EOC, RT9471_EOC_RST_MASK); +} + +static int __rt9471_enable_te(struct rt9471_chip *chip, bool en) +{ + dev_info(chip->dev, "%s en = %d\n", __func__, en); + return (en ? rt9471_set_bit : rt9471_clr_bit) + (chip, RT9471_REG_EOC, RT9471_TE_MASK); +} + +static int __rt9471_enable_jeita(struct rt9471_chip *chip, bool en) +{ + dev_info(chip->dev, "%s en = %d\n", __func__, en); + return (en ? rt9471_set_bit : rt9471_clr_bit) + (chip, RT9471_REG_JEITA, RT9471_JEITA_EN_MASK); +} + +static int __rt9471_disable_i2c_tout(struct rt9471_chip *chip, bool en) +{ + dev_info(chip->dev, "%s en = %d\n", __func__, en); + return (en ? rt9471_set_bit : rt9471_clr_bit) + (chip, RT9471_REG_TOP, RT9471_DISI2CTO_MASK); +} + +static int __rt9471_enable_qon_rst(struct rt9471_chip *chip, bool en) +{ + dev_info(chip->dev, "%s en = %d\n", __func__, en); + return (en ? rt9471_set_bit : rt9471_clr_bit) + (chip, RT9471_REG_TOP, RT9471_QONRST_MASK); +} + +static int __rt9471_enable_autoaicr(struct rt9471_chip *chip, bool en) +{ + dev_info(chip->dev, "%s en = %d\n", __func__, en); + return (en ? rt9471_set_bit : rt9471_clr_bit) + (chip, RT9471_REG_IBUS, RT9471_AUTOAICR_MASK); +} + +static int __rt9471_enable_hz(struct rt9471_chip *chip, bool en) +{ + int ret = 0; + + dev_info(chip->dev, "%s en = %d\n", __func__, en); + + if (chip->is_primary) { + ret = (en ? rt9471_set_bit : rt9471_clr_bit) + (chip, RT9471_REG_FUNCTION, RT9471_HZ_MASK); + } else { + ret = rt9471_enable_hidden_mode(chip, true); + if (ret < 0) + goto out; + + ret = (en ? rt9471_set_bit : rt9471_clr_bit) + (chip, RT9471_REG_HIDDEN_2, RT9471_FORCE_HZ_MASK); + + rt9471_enable_hidden_mode(chip, false); + } +out: + return ret; +} + +static int rt9471_enable_hz(struct rt9471_chip *chip, bool en, u32 user) +{ + int ret = 0, i = 0; + + if (user >= RT9471_HZU_MAX) + return -EINVAL; + + dev_info(chip->dev, "%s en = %d, user = %s\n", + __func__, en, rt9471_hz_user_names[user]); + + mutex_lock(&chip->hz_lock); + chip->hz_users[user] = en; + for (i = 0, en = true; i < RT9471_HZU_MAX; i++) + en &= chip->hz_users[i]; + ret = __rt9471_enable_hz(chip, en); + mutex_unlock(&chip->hz_lock); + + return ret; +} + +static int __rt9471_enable_otg(struct rt9471_chip *chip, bool en) +{ + int ret = 0; + + dev_info(chip->dev, "%s en = %d\n", __func__, en); + + ret = rt9471_enable_hz(chip, !en, RT9471_HZU_OTG); + if (ret < 0) + dev_notice(chip->dev, "%s %s hz fail(%d)\n", + __func__, en ? "dis" : "en", ret); + + return (en ? rt9471_set_bit : rt9471_clr_bit) + (chip, RT9471_REG_FUNCTION, RT9471_OTG_EN_MASK); +} + +static int __rt9471_set_otgcc(struct rt9471_chip *chip, u32 cc) +{ + dev_info(chip->dev, "%s cc = %d\n", __func__, cc); + return (cc <= rt9471_otgcc[0] ? rt9471_clr_bit : rt9471_set_bit) + (chip, RT9471_REG_OTGCFG, RT9471_OTGCC_MASK); +} + +static int __rt9471_enable_chg(struct rt9471_chip *chip, bool en) +{ + int ret = 0; + struct rt9471_desc *desc = chip->desc; + + dev_info(chip->dev, "%s en = %d, chip_rev = %d\n", + __func__, en, chip->chip_rev); + + if (chip->ceb_gpio != U32_MAX) + gpio_set_value(chip->ceb_gpio, desc->ceb_invert ? en : !en); + + ret = (en ? rt9471_set_bit : rt9471_clr_bit) + (chip, RT9471_REG_FUNCTION, RT9471_CHG_EN_MASK); + if (ret >= 0 && chip->chip_rev <= 4) + mod_delayed_work(system_wq, &chip->buck_dwork, + msecs_to_jiffies(100)); + + return ret; +} + +static int __rt9471_set_vac_ovp(struct rt9471_chip *chip, u32 vac_ovp) +{ + u8 regval = 0; + + regval = rt9471_closest_reg_via_tbl(rt9471_vac_ovp, + ARRAY_SIZE(rt9471_vac_ovp), + vac_ovp); + + dev_info(chip->dev, "%s vac_ovp = %d(0x%02X)\n", + __func__, vac_ovp, regval); + + return rt9471_i2c_update_bits(chip, RT9471_REG_VBUS, + regval << RT9471_VAC_OVP_SHIFT, + RT9471_VAC_OVP_MASK); +} + +static int __rt9471_set_mivr(struct rt9471_chip *chip, u32 mivr) +{ + u8 regval = 0; + + regval = rt9471_closest_reg(RT9471_MIVR_MIN, RT9471_MIVR_MAX, + RT9471_MIVR_STEP, mivr); + + dev_info(chip->dev, "%s mivr = %d(0x%02X)\n", __func__, mivr, regval); + + return rt9471_i2c_update_bits(chip, RT9471_REG_VBUS, + regval << RT9471_MIVR_SHIFT, + RT9471_MIVR_MASK); +} + +static int __rt9471_set_aicr(struct rt9471_chip *chip, u32 aicr) +{ + u8 regval = 0; + + regval = rt9471_closest_reg(RT9471_AICR_MIN, RT9471_AICR_MAX, + RT9471_AICR_STEP, aicr); + /* 0 & 1 are both 50mA */ + if (aicr < RT9471_AICR_MAX) + regval += 1; + + dev_info(chip->dev, "%s aicr = %d(0x%02X)\n", __func__, aicr, regval); + + return rt9471_i2c_update_bits(chip, RT9471_REG_IBUS, + regval << RT9471_AICR_SHIFT, + RT9471_AICR_MASK); +} + +static int __rt9471_set_cv(struct rt9471_chip *chip, u32 cv) +{ + u8 regval = 0; + + regval = rt9471_closest_reg(RT9471_CV_MIN, RT9471_CV_MAX, + RT9471_CV_STEP, cv); + + dev_info(chip->dev, "%s cv = %d(0x%02X)\n", __func__, cv, regval); + + return rt9471_i2c_update_bits(chip, RT9471_REG_VCHG, + regval << RT9471_CV_SHIFT, + RT9471_CV_MASK); +} + +static int __rt9471_set_ichg(struct rt9471_chip *chip, u32 ichg) +{ + u8 regval = 0; + + regval = rt9471_closest_reg(RT9471_ICHG_MIN, RT9471_ICHG_MAX, + RT9471_ICHG_STEP, ichg); + + dev_info(chip->dev, "%s ichg = %d(0x%02X)\n", __func__, ichg, regval); + + return rt9471_i2c_update_bits(chip, RT9471_REG_ICHG, + regval << RT9471_ICHG_SHIFT, + RT9471_ICHG_MASK); +} + +static int __rt9471_set_ieoc(struct rt9471_chip *chip, u32 ieoc) +{ + u8 regval = 0; + + regval = rt9471_closest_reg(RT9471_IEOC_MIN, RT9471_IEOC_MAX, + RT9471_IEOC_STEP, ieoc); + + dev_info(chip->dev, "%s ieoc = %d(0x%02X)\n", __func__, ieoc, regval); + + return rt9471_i2c_update_bits(chip, RT9471_REG_EOC, + regval << RT9471_IEOC_SHIFT, + RT9471_IEOC_MASK); +} + +static int __rt9471_reset_eoc_state(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + + return rt9471_set_bit(chip, RT9471_REG_EOC, RT9471_EOC_RST_MASK); +} + +static int __rt9471_set_safe_tmr(struct rt9471_chip *chip, u32 hr) +{ + u8 regval = 0; + + regval = rt9471_closest_reg(RT9471_SAFETMR_MIN, RT9471_SAFETMR_MAX, + RT9471_SAFETMR_STEP, hr); + + dev_info(chip->dev, "%s time = %d(0x%02X)\n", __func__, hr, regval); + + return rt9471_i2c_update_bits(chip, RT9471_REG_CHGTIMER, + regval << RT9471_SAFETMR_SHIFT, + RT9471_SAFETMR_MASK); +} + +static int __rt9471_set_wdt(struct rt9471_chip *chip, u32 sec) +{ + u8 regval = 0; + + /* 40s is the minimum, set to 40 except sec == 0 */ + if (sec <= 40 && sec > 0) + sec = 40; + regval = rt9471_closest_reg_via_tbl(rt9471_wdt, ARRAY_SIZE(rt9471_wdt), + sec); + + dev_info(chip->dev, "%s time = %d(0x%02X)\n", __func__, sec, regval); + + return rt9471_i2c_update_bits(chip, RT9471_REG_TOP, + regval << RT9471_WDT_SHIFT, + RT9471_WDT_MASK); +} + +static int __rt9471_set_mivrtrack(struct rt9471_chip *chip, u32 mivr_track) +{ + if (mivr_track >= RT9471_MIVRTRACK_MAX) + mivr_track = RT9471_MIVRTRACK_VBAT_300MV; + + dev_info(chip->dev, "%s mivrtrack = %d\n", __func__, mivr_track); + + return rt9471_i2c_update_bits(chip, RT9471_REG_VBUS, + mivr_track << RT9471_MIVRTRACK_SHIFT, + RT9471_MIVRTRACK_MASK); +} + +static int __rt9471_kick_wdt(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return rt9471_set_bit(chip, RT9471_REG_TOP, RT9471_WDTCNTRST_MASK); +} + +static inline int rt9471_toggle_bc12(struct rt9471_chip *chip) +{ + int ret = 0; + u8 regval = 0, bc12_dis[2] = {0}, bc12_en[2] = {0}; + struct i2c_client *client = chip->client; + struct i2c_msg msgs[2] = { + { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = bc12_dis, + }, + { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = bc12_en, + }, + }; + + mutex_lock(&chip->io_lock); + + ret = i2c_smbus_read_i2c_block_data(client, RT9471_REG_DPDMDET, + 1, ®val); + if (ret < 0) { + dev_notice(chip->dev, "%s read reg fail(%d)\n", __func__, ret); + goto out; + } + + /* bc12 disable and then enable */ + bc12_dis[0] = bc12_en[0] = RT9471_REG_DPDMDET; + bc12_dis[1] = regval & ~RT9471_BC12_EN_MASK; + bc12_en[1] = regval | RT9471_BC12_EN_MASK; + ret = i2c_transfer(client->adapter, msgs, 2); + if (ret < 0) + dev_notice(chip->dev, "%s bc12 dis/en fail(%d)\n", + __func__, ret); +#ifdef CONFIG_RT_REGMAP + rt_regmap_cache_reload(chip->rm_dev); +#endif /* CONFIG_RT_REGMAP */ +out: + mutex_unlock(&chip->io_lock); + return ret < 0 ? ret : 0; +} + +static int __rt9471_enable_bc12(struct rt9471_chip *chip, bool en) +{ + int ret = 0; + + dev_info(chip->dev, "%s en = %d\n", __func__, en); + + ret = rt9471_enable_hz(chip, !en, RT9471_HZU_BC12); + if (ret < 0) + dev_notice(chip->dev, "%s %s hz fail(%d)\n", + __func__, en ? "dis" : "en", ret); + + if (en) + return rt9471_toggle_bc12(chip); + else + return rt9471_clr_bit(chip, RT9471_REG_DPDMDET, + RT9471_BC12_EN_MASK); +} + +static int __rt9471_dump_registers(struct rt9471_chip *chip) +{ + int ret = 0, i = 0; + u32 mivr = 0, aicr = 0, cv = 0, ichg = 0, ieoc = 0; + bool chg_en = 0; + enum rt9471_ic_stat ic_stat = RT9471_ICSTAT_SLEEP; + u8 stats[RT9471_STATIDX_MAX] = {0}, regval = 0, hidden_2 = 0; + + ret = __rt9471_kick_wdt(chip); + + ret = __rt9471_get_mivr(chip, &mivr); + ret = __rt9471_get_aicr(chip, &aicr); + ret = __rt9471_get_cv(chip, &cv); + ret = __rt9471_get_ichg(chip, &ichg); + ret = __rt9471_get_ieoc(chip, &ieoc); + ret = __rt9471_is_chg_enabled(chip, &chg_en); + ret = __rt9471_get_ic_stat(chip, &ic_stat); + ret = rt9471_i2c_block_read(chip, RT9471_REG_STAT0, RT9471_STATIDX_MAX, + stats); + ret = rt9471_i2c_read_byte(chip, RT9471_REG_HIDDEN_2, &hidden_2); + + if (ic_stat == RT9471_ICSTAT_CHGFAULT) { + for (i = 0; i < ARRAY_SIZE(rt9471_reg_addr); i++) { + ret = rt9471_i2c_read_byte(chip, rt9471_reg_addr[i], + ®val); + if (ret < 0) + continue; + dev_notice(chip->dev, "%s reg0x%02X = 0x%02X\n", + __func__, rt9471_reg_addr[i], + regval); + } + } + + dev_info(chip->dev, "%s MIVR = %dmV, AICR = %dmA\n", + __func__, mivr / 1000, aicr / 1000); + + dev_info(chip->dev, "%s CV = %dmV, ICHG = %dmA, IEOC = %dmA\n", + __func__, cv / 1000, ichg / 1000, ieoc / 1000); + + dev_info(chip->dev, "%s CHG_EN = %d, IC_STAT = %s\n", + __func__, chg_en, rt9471_ic_stat_names[ic_stat]); + + dev_info(chip->dev, "%s STAT0 = 0x%02X, STAT1 = 0x%02X\n", __func__, + stats[RT9471_STATIDX_STAT0], stats[RT9471_STATIDX_STAT1]); + + dev_info(chip->dev, "%s STAT2 = 0x%02X, STAT3 = 0x%02X\n", __func__, + stats[RT9471_STATIDX_STAT2], stats[RT9471_STATIDX_STAT3]); + + dev_info(chip->dev, "%s HIDDEN_2 = 0x%02X\n", __func__, hidden_2); + + return 0; +} + +static void rt9471_buck_dwork_handler(struct work_struct *work) +{ + int ret = 0, i = 0; + struct rt9471_chip *chip = + container_of(work, struct rt9471_chip, buck_dwork.work); + bool chg_rdy = false, chg_done = false, sys_min = false; + u8 regval = 0; + u8 reg_addrs[] = {RT9471_REG_BUCK_HDEN4, RT9471_REG_BUCK_HDEN1, + RT9471_REG_BUCK_HDEN2, RT9471_REG_BUCK_HDEN4, + RT9471_REG_BUCK_HDEN2, RT9471_REG_BUCK_HDEN1}; + u8 reg_vals[] = {0x77, 0x2F, 0xA2, 0x71, 0x22, 0x2D}; + + dev_info(chip->dev, "%s\n", __func__); + + __pm_stay_awake(chip->buck_dwork_ws); + + ret = rt9471_i2c_read_byte(chip, RT9471_REG_STAT0, ®val); + if (ret < 0) + goto out; + chg_rdy = (regval & RT9471_ST_CHGRDY_MASK ? true : false); + chg_done = (regval & RT9471_ST_CHGDONE_MASK ? true : false); + dev_info(chip->dev, "%s chg_rdy = %d\n", __func__, chg_rdy); + dev_info(chip->dev, "%s chg_done = %d, chg_done_once = %d\n", + __func__, chg_done, chip->chg_done_once); + if (!chg_rdy) + goto out; + + ret = rt9471_i2c_test_bit(chip, RT9471_REG_STAT2, + RT9471_ST_SYSMIN_SHIFT, &sys_min); + if (ret < 0) + goto out; + dev_info(chip->dev, "%s sys_min = %d\n", __func__, sys_min); + /* Should not enter CV tracking in sys_min */ + if (sys_min) + reg_vals[1] = 0x2D; + + ret = rt9471_enable_hidden_mode(chip, true); + if (ret < 0) + goto out; + + for (i = 0; i < ARRAY_SIZE(reg_addrs); i++) { + ret = rt9471_i2c_write_byte(chip, reg_addrs[i], reg_vals[i]); + if (ret < 0) + dev_notice(chip->dev, + "%s reg0x%02X = 0x%02X fail(%d)\n", + __func__, reg_addrs[i], reg_vals[i], ret); + if (i == 1) + udelay(1000); + } + + rt9471_enable_hidden_mode(chip, false); + + if (chg_done && !chip->chg_done_once) { + chip->chg_done_once = true; + mod_delayed_work(system_wq, &chip->buck_dwork, + msecs_to_jiffies(100)); + } +out: + __pm_relax(chip->buck_dwork_ws); +} + +static bool rt9471_is_vbus_ready_for_chg(struct rt9471_chip *chip) +{ + int ret = 0; + bool chg_rdy = false; + + ret = rt9471_i2c_test_bit(chip, RT9471_REG_STAT0, + RT9471_ST_CHGRDY_SHIFT, &chg_rdy); + if (ret < 0) + dev_notice(chip->dev, "%s check stat fail(%d)\n", + __func__, ret); + dev_info(chip->dev, "%s chg_rdy = %d, chip_rev = %d\n", + __func__, chg_rdy, chip->chip_rev); + + return chg_rdy; +} + +static bool rt9471_is_vbus_gd(struct rt9471_chip *chip) +{ + int ret = 0; + bool vbus_gd = false; + + ret = rt9471_i2c_test_bit(chip, RT9471_REG_STAT0, + RT9471_ST_VBUSGD_SHIFT, &vbus_gd); + if (ret < 0) + dev_notice(chip->dev, "%s check stat fail(%d)\n", + __func__, ret); + dev_dbg(chip->dev, "%s vbus_gd = %d\n", __func__, vbus_gd); + + return vbus_gd; +} + +static int rt9471_detach_irq_handler(struct rt9471_chip *chip) +{ + bool vbus_gd = rt9471_is_vbus_gd(chip); + + dev_info(chip->dev, "%s vbus_gd = %d\n", __func__, vbus_gd); + if (vbus_gd) + goto out; + + complete(&chip->aicc_done); + complete(&chip->pe_done); +out: + return 0; +} + +static int rt9471_rechg_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return 0; +} + +static void rt9471_bc12_done_handler(struct rt9471_chip *chip) +{ + +} + +static int rt9471_bc12_done_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + rt9471_bc12_done_handler(chip); + return 0; +} + +static int rt9471_chg_done_irq_handler(struct rt9471_chip *chip) +{ + int ret = 0; + bool chg_done = false; + + ret = rt9471_i2c_test_bit(chip, RT9471_REG_STAT0, + RT9471_ST_CHGDONE_SHIFT, &chg_done); + if (ret < 0) + dev_notice(chip->dev, "%s check stat fail(%d)\n", + __func__, ret); + dev_info(chip->dev, "%s chg_done = %d, chip_rev = %d\n", + __func__, chg_done, chip->chip_rev); + if (!chg_done || chip->chip_rev > 4) + goto out; + + cancel_delayed_work_sync(&chip->buck_dwork); + chip->chg_done_once = false; + mod_delayed_work(system_wq, &chip->buck_dwork, msecs_to_jiffies(100)); +out: + return 0; +} + +static int rt9471_bg_chg_irq_handler(struct rt9471_chip *chip) +{ + int ret = 0; + bool bg_chg = false; + + ret = rt9471_i2c_test_bit(chip, RT9471_REG_STAT0, + RT9471_ST_BGCHG_SHIFT, &bg_chg); + if (ret < 0) + dev_notice(chip->dev, "%s check stat fail(%d)\n", + __func__, ret); + dev_info(chip->dev, "%s bg_chg = %d\n", __func__, bg_chg); + + return 0; +} + +static int rt9471_ieoc_irq_handler(struct rt9471_chip *chip) +{ + int ret = 0; + bool ieoc = false; + + ret = rt9471_i2c_test_bit(chip, RT9471_REG_STAT0, + RT9471_ST_IEOC_SHIFT, &ieoc); + if (ret < 0) + dev_notice(chip->dev, "%s check stat fail(%d)\n", + __func__, ret); + dev_info(chip->dev, "%s ieoc = %d\n", __func__, ieoc); + if (!ieoc) + goto out; + +out: + return 0; +} + +static int rt9471_chg_rdy_irq_handler(struct rt9471_chip *chip) +{ + int ret = 0; + bool chg_rdy = false; + + ret = rt9471_i2c_test_bit(chip, RT9471_REG_STAT0, + RT9471_ST_CHGRDY_SHIFT, &chg_rdy); + if (ret < 0) + dev_notice(chip->dev, "%s check stat fail(%d)\n", + __func__, ret); + dev_info(chip->dev, "%s chg_rdy = %d, chip_rev = %d\n", + __func__, chg_rdy, chip->chip_rev); + if (!chg_rdy || chip->chip_rev > 4) + goto out; + + if (chip->chip_rev <= 3) + rt9471_bc12_done_handler(chip); + mod_delayed_work(system_wq, &chip->buck_dwork, msecs_to_jiffies(100)); +out: + return 0; +} + +static int rt9471_vbus_gd_irq_handler(struct rt9471_chip *chip) +{ + int ret = 0; + bool vbus_gd = false; + + ret = rt9471_enable_hz(chip, true, RT9471_HZU_VBUS_GD); + if (ret < 0) + dev_notice(chip->dev, "%s en hz fail(%d)\n", __func__, ret); + + vbus_gd = rt9471_is_vbus_gd(chip); + dev_info(chip->dev, "%s vbus_gd = %d\n", __func__, vbus_gd); + if (!vbus_gd) + goto out; + +out: + return 0; +} + +static int rt9471_chg_batov_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return 0; +} + +static int rt9471_chg_sysov_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return 0; +} + +static int rt9471_chg_tout_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return 0; +} + +static int rt9471_chg_busuv_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return 0; +} + +static int rt9471_chg_threg_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return 0; +} + +static int rt9471_chg_aicr_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return 0; +} + +static int rt9471_chg_mivr_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return 0; +} + +static int rt9471_sys_short_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return 0; +} + +static int rt9471_sys_min_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return 0; +} + +static int rt9471_aicc_done_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + complete(&chip->aicc_done); + return 0; +} + +static int rt9471_pe_done_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + complete(&chip->pe_done); + return 0; +} + +static int rt9471_jeita_cold_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return 0; +} + +static int rt9471_jeita_cool_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return 0; +} + +static int rt9471_jeita_warm_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return 0; +} + +static int rt9471_jeita_hot_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return 0; +} + +static int rt9471_otg_fault_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return 0; +} + +static int rt9471_otg_lbp_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return 0; +} + +static int rt9471_otg_cc_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return 0; +} + +static int rt9471_wdt_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return __rt9471_kick_wdt(chip); +} + +static int rt9471_vac_ov_irq_handler(struct rt9471_chip *chip) +{ + int ret = 0; + bool vac_ov = false; + + ret = rt9471_i2c_test_bit(chip, RT9471_REG_STAT3, RT9471_ST_VACOV_SHIFT, + &vac_ov); + if (ret < 0) + dev_notice(chip->dev, "%s check stat fail(%d)\n", + __func__, ret); + dev_info(chip->dev, "%s vac_ov = %d\n", __func__, vac_ov); + + return 0; +} + +static int rt9471_otp_irq_handler(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return 0; +} + +struct irq_mapping_tbl { + const char *name; + int (*hdlr)(struct rt9471_chip *chip); + int num; +}; + +#define RT9471_IRQ_MAPPING(_name, _num) \ + {.name = #_name, .hdlr = rt9471_##_name##_irq_handler, .num = _num} + +static const struct irq_mapping_tbl rt9471_irq_mapping_tbl[] = { + RT9471_IRQ_MAPPING(wdt, 29), + RT9471_IRQ_MAPPING(vbus_gd, 7), + RT9471_IRQ_MAPPING(chg_rdy, 6), + RT9471_IRQ_MAPPING(bc12_done, 0), + RT9471_IRQ_MAPPING(detach, 1), + RT9471_IRQ_MAPPING(rechg, 2), + RT9471_IRQ_MAPPING(chg_done, 3), + RT9471_IRQ_MAPPING(bg_chg, 4), + RT9471_IRQ_MAPPING(ieoc, 5), + RT9471_IRQ_MAPPING(chg_batov, 9), + RT9471_IRQ_MAPPING(chg_sysov, 10), + RT9471_IRQ_MAPPING(chg_tout, 11), + RT9471_IRQ_MAPPING(chg_busuv, 12), + RT9471_IRQ_MAPPING(chg_threg, 13), + RT9471_IRQ_MAPPING(chg_aicr, 14), + RT9471_IRQ_MAPPING(chg_mivr, 15), + RT9471_IRQ_MAPPING(sys_short, 16), + RT9471_IRQ_MAPPING(sys_min, 17), + RT9471_IRQ_MAPPING(aicc_done, 18), + RT9471_IRQ_MAPPING(pe_done, 19), + RT9471_IRQ_MAPPING(jeita_cold, 20), + RT9471_IRQ_MAPPING(jeita_cool, 21), + RT9471_IRQ_MAPPING(jeita_warm, 22), + RT9471_IRQ_MAPPING(jeita_hot, 23), + RT9471_IRQ_MAPPING(otg_fault, 24), + RT9471_IRQ_MAPPING(otg_lbp, 25), + RT9471_IRQ_MAPPING(otg_cc, 26), + RT9471_IRQ_MAPPING(vac_ov, 30), + RT9471_IRQ_MAPPING(otp, 31), +}; + +static irqreturn_t rt9471_irq_handler(int irq, void *data) +{ + int ret = 0, i = 0, irqnum = 0, irqbit = 0; + u8 evt[RT9471_IRQIDX_MAX] = {0}; + u8 mask[RT9471_IRQIDX_MAX] = {0}; + struct rt9471_chip *chip = (struct rt9471_chip *)data; + + dev_info(chip->dev, "%s\n", __func__); + + pm_stay_awake(chip->dev); + + schedule_work(&chip->charge_detect_work); + + ret = rt9471_i2c_block_read(chip, RT9471_REG_IRQ0, RT9471_IRQIDX_MAX, + evt); + if (ret < 0) { + dev_notice(chip->dev, "%s read evt fail(%d)\n", __func__, ret); + goto out; + } + + ret = rt9471_i2c_block_read(chip, RT9471_REG_MASK0, RT9471_IRQIDX_MAX, + mask); + if (ret < 0) { + dev_notice(chip->dev, "%s read mask fail(%d)\n", __func__, ret); + goto out; + } + + for (i = 0; i < RT9471_IRQIDX_MAX; i++) + evt[i] &= ~mask[i]; + for (i = 0; i < ARRAY_SIZE(rt9471_irq_mapping_tbl); i++) { + irqnum = rt9471_irq_mapping_tbl[i].num / 8; + if (irqnum >= RT9471_IRQIDX_MAX) + continue; + irqbit = rt9471_irq_mapping_tbl[i].num % 8; + if (evt[irqnum] & (1 << irqbit)) + rt9471_irq_mapping_tbl[i].hdlr(chip); + } +out: + pm_relax(chip->dev); + return IRQ_HANDLED; +} + +static int rt9471_register_irq(struct rt9471_chip *chip) +{ + int ret = 0; + + dev_info(chip->dev, "%s\n", __func__); + + ret = devm_gpio_request_one(chip->dev, chip->intr_gpio, GPIOF_DIR_IN, + devm_kasprintf(chip->dev, GFP_KERNEL, + "rt9471_intr_gpio.%s", dev_name(chip->dev))); + if (ret < 0) { + dev_notice(chip->dev, "%s gpio request fail(%d)\n", + __func__, ret); + return ret; + } + chip->irq = gpio_to_irq(chip->intr_gpio); + if (chip->irq < 0) { + dev_notice(chip->dev, "%s gpio2irq fail(%d)\n", + __func__, chip->irq); + return chip->irq; + } + dev_info(chip->dev, "%s irq = %d\n", __func__, chip->irq); + + /* Request threaded IRQ */ + ret = devm_request_threaded_irq(chip->dev, chip->irq, NULL, + rt9471_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + devm_kasprintf(chip->dev, GFP_KERNEL, + "rt9471_irq.%s", dev_name(chip->dev)), + chip); + if (ret < 0) { + dev_notice(chip->dev, "%s request threaded irq fail(%d)\n", + __func__, ret); + return ret; + } + device_init_wakeup(chip->dev, true); + + return ret; +} + +static int rt9471_init_irq(struct rt9471_chip *chip) +{ + dev_info(chip->dev, "%s\n", __func__); + return rt9471_i2c_block_write(chip, RT9471_REG_MASK0, + ARRAY_SIZE(chip->irq_mask), + chip->irq_mask); +} + +static inline int rt9471_get_irq_number(struct rt9471_chip *chip, + const char *name) +{ + int i = 0; + + if (!name) { + dev_notice(chip->dev, "%s null name\n", __func__); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(rt9471_irq_mapping_tbl); i++) { + if (!strcmp(name, rt9471_irq_mapping_tbl[i].name)) + return rt9471_irq_mapping_tbl[i].num; + } + + return -EINVAL; +} + +static inline const char *rt9471_get_irq_name(int irqnum) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(rt9471_irq_mapping_tbl); i++) { + if (rt9471_irq_mapping_tbl[i].num == irqnum) + return rt9471_irq_mapping_tbl[i].name; + } + + return "not found"; +} + +static inline void rt9471_irq_mask(struct rt9471_chip *chip, int irqnum) +{ + dev_dbg(chip->dev, "%s irq(%d, %s)\n", __func__, irqnum, + rt9471_get_irq_name(irqnum)); + chip->irq_mask[irqnum / 8] |= (1 << (irqnum % 8)); +} + +static inline void rt9471_irq_unmask(struct rt9471_chip *chip, int irqnum) +{ + dev_info(chip->dev, "%s irq(%d, %s)\n", __func__, irqnum, + rt9471_get_irq_name(irqnum)); + chip->irq_mask[irqnum / 8] &= ~(1 << (irqnum % 8)); +} + +static int rt9471_parse_dt(struct rt9471_chip *chip) +{ + int ret = 0, irqcnt = 0, irqnum = 0; + struct device_node *parent_np = chip->dev->of_node, *np = NULL; + struct rt9471_desc *desc = NULL; + const char *name = NULL; + + dev_info(chip->dev, "%s\n", __func__); + + chip->desc = &rt9471_default_desc; + + if (!parent_np) { + dev_notice(chip->dev, "%s no device node\n", __func__); + return -EINVAL; + } + np = of_get_child_by_name(parent_np, "rt9471"); + if (!np) { + dev_info(chip->dev, "%s no rt9471 device node\n", __func__); + np = parent_np; + } + + desc = devm_kmemdup(chip->dev, &rt9471_default_desc, + sizeof(rt9471_default_desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + chip->desc = desc; + + ret = of_property_read_string(np, "mmi,chg_dev_name", &chip->mmi_chg_dev_name); + if (ret < 0) { + chip->mmi_chg_dev_name = "master_chg"; + dev_info(chip->dev, "%s no chg_name(%d)\n", __func__, ret); + } + + chip->mmi_qc3_support = of_property_read_bool(chip->dev->of_node, "mmi,qc3-support"); + + ret = of_property_read_string(np, "chg_name", &desc->chg_name); + if (ret < 0) + dev_info(chip->dev, "%s no chg_name(%d)\n", __func__, ret); + + ret = of_property_read_string(np, "chg_alias_name", + &chip->chg_props.alias_name); + if (ret < 0) { + dev_info(chip->dev, "%s no chg_alias_name(%d)\n", + __func__, ret); + chip->chg_props.alias_name = "rt9471_chg"; + } + dev_info(chip->dev, "%s name = %s, alias name = %s\n", __func__, + desc->chg_name, chip->chg_props.alias_name); + + if (strcmp(desc->chg_name, "primary_chg") == 0) + chip->is_primary = true; + +#if !defined(CONFIG_MTK_GPIO) || defined(CONFIG_MTK_GPIOLIB_STAND) + ret = of_get_named_gpio(parent_np, "rt,intr_gpio", 0); + if (ret < 0) { + dev_notice(chip->dev, "%s no rt,intr_gpio(%d)\n", + __func__, ret); + return ret; + } else + chip->intr_gpio = ret; + + ret = of_get_named_gpio(parent_np, "rt,ceb_gpio", 0); + if (ret < 0) { + dev_info(chip->dev, "%s no rt,ceb_gpio(%d)\n", + __func__, ret); + chip->ceb_gpio = U32_MAX; + } else + chip->ceb_gpio = ret; +#else + ret = of_property_read_u32(parent_np, "rt,intr_gpio_num", + &chip->intr_gpio); + if (ret < 0) { + dev_notice(chip->dev, "%s no rt,intr_gpio_num(%d)\n", + __func__, ret); + return ret; + } + + ret = of_property_read_u32(parent_np, "rt,ceb_gpio_num", + &chip->ceb_gpio); + if (ret < 0) { + dev_info(chip->dev, "%s no rt,ceb_gpio_num(%d)\n", + __func__, ret); + chip->ceb_gpio = U32_MAX; + } +#endif + dev_info(chip->dev, "%s intr_gpio = %u, ceb_gpio = %u\n", + __func__, chip->intr_gpio, chip->ceb_gpio); + + if (chip->ceb_gpio != U32_MAX) { + ret = devm_gpio_request_one( + chip->dev, chip->ceb_gpio, GPIOF_DIR_OUT, + devm_kasprintf(chip->dev, GFP_KERNEL, + "rt9471_ceb_gpio.%s", dev_name(chip->dev))); + if (ret < 0) { + dev_notice(chip->dev, "%s gpio request fail(%d)\n", + __func__, ret); + return ret; + } + } + + /* Register map */ + ret = of_property_read_u8(np, "rm-dev-addr", &desc->rm_dev_addr); + if (ret < 0) + dev_info(chip->dev, "%s no rm-dev-addr(%d)\n", __func__, ret); + ret = of_property_read_string(np, "rm-name", &desc->rm_name); + if (ret < 0) + dev_info(chip->dev, "%s no rm-name(%d)\n", __func__, ret); + + /* Charger parameter */ + ret = of_property_read_u32(np, "vac_ovp", &desc->vac_ovp); + if (ret < 0) + dev_info(chip->dev, "%s no vac_ovp(%d)\n", __func__, ret); + + ret = of_property_read_u32(np, "mivr", &desc->mivr); + if (ret < 0) + dev_info(chip->dev, "%s no mivr(%d)\n", __func__, ret); + + ret = of_property_read_u32(np, "aicr", &desc->aicr); + if (ret < 0) + dev_info(chip->dev, "%s no aicr(%d)\n", __func__, ret); + + ret = of_property_read_u32(np, "cv", &desc->cv); + if (ret < 0) + dev_info(chip->dev, "%s no cv(%d)\n", __func__, ret); + + ret = of_property_read_u32(np, "ichg", &desc->ichg); + if (ret < 0) + dev_info(chip->dev, "%s no ichg(%d)\n", __func__, ret); + + ret = of_property_read_u32(np, "ieoc", &desc->ieoc) < 0; + if (ret < 0) + dev_info(chip->dev, "%s no ieoc(%d)\n", __func__, ret); + + ret = of_property_read_u32(np, "safe_tmr", &desc->safe_tmr); + if (ret < 0) + dev_info(chip->dev, "%s no safe_tmr(%d)\n", __func__, ret); + + ret = of_property_read_u32(np, "wdt", &desc->wdt); + if (ret < 0) + dev_info(chip->dev, "%s no wdt(%d)\n", __func__, ret); + + ret = of_property_read_u32(np, "mivr_track", &desc->mivr_track); + if (ret < 0) + dev_info(chip->dev, "%s no mivr_track(%d)\n", __func__, ret); + if (desc->mivr_track >= RT9471_MIVRTRACK_MAX) + desc->mivr_track = RT9471_MIVRTRACK_VBAT_300MV; + + desc->en_safe_tmr = of_property_read_bool(np, "en_safe_tmr"); + desc->en_te = of_property_read_bool(np, "en_te"); + desc->en_jeita = of_property_read_bool(np, "en_jeita"); + desc->ceb_invert = of_property_read_bool(np, "ceb_invert"); + desc->dis_i2c_tout = of_property_read_bool(np, "dis_i2c_tout"); + desc->en_qon_rst = of_property_read_bool(np, "en_qon_rst"); + desc->auto_aicr = of_property_read_bool(np, "auto_aicr"); + + memcpy(chip->irq_mask, rt9471_irq_maskall, RT9471_IRQIDX_MAX); + while (true) { + ret = of_property_read_string_index(np, "interrupt-names", + irqcnt, &name); + if (ret < 0) + break; + irqcnt++; + irqnum = rt9471_get_irq_number(chip, name); + if (irqnum >= 0) + rt9471_irq_unmask(chip, irqnum); + } + + return 0; +} + +static int rt9471_check_chg(struct rt9471_chip *chip) +{ + int ret = 0; + u8 regval = 0; + + dev_info(chip->dev, "%s\n", __func__); + + ret = rt9471_i2c_read_byte(chip, RT9471_REG_STAT0, ®val); + if (ret < 0) + return ret; + + if (regval & RT9471_ST_VBUSGD_MASK) + rt9471_vbus_gd_irq_handler(chip); + if (regval & RT9471_ST_CHGDONE_MASK) + rt9471_chg_done_irq_handler(chip); + else if (regval & RT9471_ST_CHGRDY_MASK) + rt9471_chg_rdy_irq_handler(chip); + + return ret; +} + +static int rt9471_sw_workaround(struct rt9471_chip *chip) +{ + int ret = 0; + u8 regval = 0; + + dev_info(chip->dev, "%s\n", __func__); + + ret = rt9471_enable_hidden_mode(chip, true); + if (ret < 0) + return ret; + + ret = rt9471_i2c_read_byte(chip, RT9471_REG_HIDDEN_0, ®val); + if (ret < 0) + goto out; + + chip->chip_rev = (regval & RT9471_CHIP_REV_MASK) >> + RT9471_CHIP_REV_SHIFT; + dev_info(chip->dev, "%s chip_rev = %d\n", __func__, chip->chip_rev); + + /* OTG load transient improvement */ + if (chip->chip_rev <= 3) + ret = rt9471_i2c_update_bits(chip, RT9471_REG_OTG_HDEN2, 0x10, + RT9471_REG_OTG_RES_COMP_MASK); + +out: + rt9471_enable_hidden_mode(chip, false); + return ret; +} + +static int rt9471_init_setting(struct rt9471_chip *chip) +{ + int ret = 0; + struct rt9471_desc *desc = chip->desc; + u8 evt[RT9471_IRQIDX_MAX] = {0}; + + dev_info(chip->dev, "%s\n", __func__); + + /* Mask all IRQs */ + ret = rt9471_i2c_block_write(chip, RT9471_REG_MASK0, + ARRAY_SIZE(rt9471_irq_maskall), + rt9471_irq_maskall); + if (ret < 0) + dev_notice(chip->dev, "%s mask irq fail(%d)\n", __func__, ret); + + /* Clear all IRQs */ + ret = rt9471_i2c_block_read(chip, RT9471_REG_IRQ0, RT9471_IRQIDX_MAX, + evt); + if (ret < 0) + dev_notice(chip->dev, "%s clear irq fail(%d)\n", __func__, ret); + + ret = __rt9471_set_vac_ovp(chip, desc->vac_ovp); + if (ret < 0) + dev_notice(chip->dev, "%s set vac ovp fail(%d)\n", + __func__, ret); + + ret = __rt9471_set_mivr(chip, desc->mivr); + if (ret < 0) + dev_notice(chip->dev, "%s set mivr fail(%d)\n", __func__, ret); + + ret = __rt9471_set_aicr(chip, desc->aicr); + if (ret < 0) + dev_notice(chip->dev, "%s set aicr fail(%d)\n", __func__, ret); + + ret = __rt9471_set_cv(chip, desc->cv); + if (ret < 0) + dev_notice(chip->dev, "%s set cv fail(%d)\n", __func__, ret); + + ret = __rt9471_set_ichg(chip, desc->ichg); + if (ret < 0) + dev_notice(chip->dev, "%s set ichg fail(%d)\n", __func__, ret); + + ret = __rt9471_set_ieoc(chip, desc->ieoc); + if (ret < 0) + dev_notice(chip->dev, "%s set ieoc fail(%d)\n", __func__, ret); + + ret = __rt9471_reset_eoc_state(chip); + if (ret < 0) + dev_notice(chip->dev, "%s reset eoc state fail(%d)\n", + __func__, ret); + + ret = __rt9471_set_safe_tmr(chip, desc->safe_tmr); + if (ret < 0) + dev_notice(chip->dev, "%s set safe tmr fail(%d)\n", + __func__, ret); + + ret = __rt9471_set_mivrtrack(chip, desc->mivr_track); + if (ret < 0) + dev_notice(chip->dev, "%s set mivrtrack fail(%d)\n", + __func__, ret); + + ret = __rt9471_enable_safe_tmr(chip, desc->en_safe_tmr); + if (ret < 0) + dev_notice(chip->dev, "%s en safe tmr fail(%d)\n", + __func__, ret); + + ret = __rt9471_enable_te(chip, desc->en_te); + if (ret < 0) + dev_notice(chip->dev, "%s en te fail(%d)\n", __func__, ret); + + ret = __rt9471_enable_jeita(chip, desc->en_jeita); + if (ret < 0) + dev_notice(chip->dev, "%s en jeita fail(%d)\n", __func__, ret); + + ret = __rt9471_disable_i2c_tout(chip, desc->dis_i2c_tout); + if (ret < 0) + dev_notice(chip->dev, "%s dis i2c tout fail(%d)\n", + __func__, ret); + + ret = __rt9471_enable_qon_rst(chip, desc->en_qon_rst); + if (ret < 0) + dev_notice(chip->dev, "%s en qon rst fail(%d)\n", + __func__, ret); + + ret = __rt9471_enable_autoaicr(chip, desc->auto_aicr); + if (ret < 0) + dev_notice(chip->dev, "%s en autoaicr fail(%d)\n", + __func__, ret); + + ret = rt9471_sw_workaround(chip); + if (ret < 0) + dev_notice(chip->dev, "%s sw workaround fail(%d)\n", + __func__, ret); + + ret = __rt9471_enable_bc12(chip, false); + if (ret < 0) + dev_notice(chip->dev, "%s dis bc12 fail(%d)\n", __func__, ret); + + /* + * Customization for MTK platform + * Primary charger: HZ controlled by sink vbus with TCPC enabled, + * CHG_EN controlled by charging algorithm + * Secondary charger: HZ=0 and CHG_EN=1 at needed, + * e.x.: PE10, PE20, etc... + */ + if (!chip->is_primary) { + ret = rt9471_enable_hz(chip, true, RT9471_HZU_PP); + if (ret < 0) + dev_notice(chip->dev, "%s en hz fail(%d)\n", + __func__, ret); + ret = __rt9471_enable_chg(chip, false); + if (ret < 0) + dev_notice(chip->dev, "%s dis chg fail(%d)\n", + __func__, ret); + chip->enter_shipping_mode = true; + } + + return 0; +} + +static int rt9471_reset_register(struct rt9471_chip *chip) +{ + int ret = 0; + + dev_info(chip->dev, "%s\n", __func__); + + mutex_lock(&chip->hz_lock); + chip->hz_users[RT9471_HZU_PP] = false; + chip->hz_users[RT9471_HZU_BC12] = false; + chip->hz_users[RT9471_HZU_OTG] = true; + chip->hz_users[RT9471_HZU_VBUS_GD] = true; + ret = rt9471_set_bit(chip, RT9471_REG_INFO, RT9471_REGRST_MASK); + mutex_unlock(&chip->hz_lock); + if (ret < 0) + return ret; +#ifdef CONFIG_RT_REGMAP + rt_regmap_cache_reload(chip->rm_dev); +#endif /* CONFIG_RT_REGMAP */ + ret = __rt9471_enable_autoaicr(chip, false); + if (ret < 0) + return ret; + + return __rt9471_set_wdt(chip, 0); +} + +static bool rt9471_check_devinfo(struct rt9471_chip *chip) +{ + int ret = 0; + + ret = i2c_smbus_read_byte_data(chip->client, RT9471_REG_INFO); + if (ret < 0) { + dev_notice(chip->dev, "%s get devinfo fail(%d)\n", + __func__, ret); + return false; + } + chip->dev_id = (ret & RT9471_DEVID_MASK) >> RT9471_DEVID_SHIFT; + switch (chip->dev_id) { + case RT9470_DEVID: + case RT9470D_DEVID: + case RT9471_DEVID: + case RT9471D_DEVID: + break; + default: + dev_notice(chip->dev, "%s incorrect devid 0x%02X\n", + __func__, chip->dev_id); + return false; + } + chip->dev_rev = (ret & RT9471_DEVREV_MASK) >> RT9471_DEVREV_SHIFT; + dev_info(chip->dev, "%s id = 0x%02X, rev = 0x%02X\n", + __func__, chip->dev_id, chip->dev_rev); + + return true; +} + +static int get_charger_type(struct rt9471_chip *chip) +{ + enum power_supply_usb_type usb_type; + + switch(chip->real_charger_type) { + case POWER_SUPPLY_TYPE_USB: + usb_type = POWER_SUPPLY_USB_TYPE_SDP; + break; + + case POWER_SUPPLY_TYPE_USB_CDP: + usb_type = POWER_SUPPLY_USB_TYPE_CDP; + break; + + case POWER_SUPPLY_TYPE_USB_DCP: + case POWER_SUPPLY_TYPE_USB_HVDCP: + case POWER_SUPPLY_TYPE_USB_HVDCP_3: + case POWER_SUPPLY_TYPE_USB_HVDCP_3P5: + usb_type = POWER_SUPPLY_USB_TYPE_DCP; + break; + + default: + usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + break; + } + pr_err("%s usb_type:%d\n",__func__,usb_type); + + return usb_type; +} + +static int rt9471_read_usbin_voltage_chan(struct rt9471_chip *chip, int *val) +{ + int rc; + + if (!chip->iio.usbin_v_chan) + return -ENODATA; + + rc = iio_read_channel_processed(chip->iio.usbin_v_chan, val); + if (rc < 0) { + dev_err(chip->dev, "Couldn't read USBIN channel rc=%d\n", rc); + return rc; + } + + return 0; +} + +static int rt9471_get_usb_voltage_now(struct rt9471_chip *chip, int *val) +{ + int rc; + int raw_date; + + rc = rt9471_read_usbin_voltage_chan(chip, &raw_date); + if (rc < 0) { + dev_err(chip->dev, "Couldn't read USBIN over vadc rc=%d\n", rc); + return rc; + } + + *val = raw_date * 1000; + + return 0; +} + +static int rt9471_get_iio_channel(struct rt9471_chip *chip, const char *propname, + struct iio_channel **chan) +{ + int rc = 0; + + rc = of_property_match_string(chip->dev->of_node, + "io-channel-names", propname); + if (rc < 0) + return 0; + + *chan = iio_channel_get(chip->dev, propname); + if (IS_ERR(*chan)) { + rc = PTR_ERR(*chan); + if (rc != -EPROBE_DEFER) + dev_err(chip->dev, "%s channel unavailable, %d\n", + propname, rc); + *chan = NULL; + } + + return rc; +} + +static int rt9471_parse_dt_adc_channels(struct rt9471_chip *chip) +{ + int rc = 0; + + rc = rt9471_get_iio_channel(chip, "read_usbin_voltage", + &chip->iio.usbin_v_chan); + + if (rc < 0) + return rc; + + return 0; +} + +static int rt9471_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct rt9471_chip *chip = power_supply_get_drvdata(psy); + int ret = 0; + enum rt9471_ic_stat ic_stat = RT9471_ICSTAT_SLEEP; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + __rt9471_get_ic_stat(chip, &ic_stat); + if (ic_stat == RT9471_ICSTAT_TRICKLECHG || + ic_stat == RT9471_ICSTAT_PRECHG || + ic_stat == RT9471_ICSTAT_FASTCHG || + ic_stat == RT9471_ICSTAT_IEOC || + ic_stat == RT9471_ICSTAT_BGCHG) { + val->intval = POWER_SUPPLY_STATUS_CHARGING; + } else if (ic_stat == RT9471_ICSTAT_CHGFAULT || + ic_stat == RT9471_ICSTAT_VBUSRDY) { + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + } else if (ic_stat == RT9471_ICSTAT_SLEEP || + ic_stat == RT9471_ICSTAT_OTG){ + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + } else if (ic_stat == RT9471_ICSTAT_CHGDONE){ + val->intval = POWER_SUPPLY_STATUS_FULL; + } else { + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + } + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + __rt9471_get_ic_stat(chip, &ic_stat); + switch(ic_stat) { + case RT9471_ICSTAT_TRICKLECHG: + case RT9471_ICSTAT_PRECHG: + val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case RT9471_ICSTAT_FASTCHG: + case RT9471_ICSTAT_IEOC: + case RT9471_ICSTAT_BGCHG: + val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; + break; + case RT9471_ICSTAT_SLEEP: + case RT9471_ICSTAT_VBUSRDY: + case RT9471_ICSTAT_CHGDONE: + case RT9471_ICSTAT_CHGFAULT: + case RT9471_ICSTAT_OTG: + val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + default: + val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + } + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = "richtek"; + break; + + case POWER_SUPPLY_PROP_MODEL_NAME: + switch (chip->dev_id) { + case RT9470_DEVID: + val->strval = "RT9470"; + break; + case RT9470D_DEVID: + val->strval = "RT9470D"; + break; + case RT9471_DEVID: + val->strval = "RT9471"; + break; + case RT9471D_DEVID: + val->strval = "RT9471D"; + break; + default: + val->strval = "UNKNOWN"; + } + break; + + case POWER_SUPPLY_PROP_ONLINE: + val->intval = chip->state.online; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = chip->state.vbus_gd; + break; + case POWER_SUPPLY_PROP_TYPE: + val->intval = rt9471_power_supply_desc.type; + break; + case POWER_SUPPLY_PROP_USB_TYPE: + val->intval = get_charger_type(chip); + break; + + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + rt9471_get_usb_voltage_now(chip, &val->intval); + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + //val->intval = state.ibus_adc; + break; + + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + __rt9471_get_mivr(chip, &val->intval); + break; + + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + __rt9471_get_aicr(chip, &val->intval); + break; + default: + return -EINVAL; + } + + return ret; +} + +static int rt9471_charger_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct rt9471_chip *chip = power_supply_get_drvdata(psy); + int ret = -EINVAL; + + switch (prop) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = rt9471_set_aicr(chip->chg_dev, val->intval); + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = __rt9471_set_mivr(chip, val->intval); + break; + default: + return -EINVAL; + } + + return ret; +} + +static int rt9471_property_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + switch (prop) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + return true; + default: + return false; + } +} + +static enum power_supply_property rt9471_power_supply_props[] = { + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_PRESENT +}; + +static char *rt9471_charger_supplied_to[] = { + "battery", +}; + +static struct power_supply_desc rt9471_power_supply_desc = { + .name = "charger", + .type = POWER_SUPPLY_TYPE_UNKNOWN, + .usb_types = rt9471_usb_type, + .num_usb_types = ARRAY_SIZE(rt9471_usb_type), + .properties = rt9471_power_supply_props, + .num_properties = ARRAY_SIZE(rt9471_power_supply_props), + .get_property = rt9471_charger_get_property, + .set_property = rt9471_charger_set_property, + .property_is_writeable = rt9471_property_is_writeable, +}; + +static int rt9471_power_supply_init(struct rt9471_chip *chip, struct device *dev) +{ + struct power_supply_config psy_cfg = { .drv_data = chip, + .of_node = dev->of_node, }; + + psy_cfg.supplied_to = rt9471_charger_supplied_to; + psy_cfg.num_supplicants = ARRAY_SIZE(rt9471_charger_supplied_to); + + chip->charger = devm_power_supply_register(chip->dev, + &rt9471_power_supply_desc, + &psy_cfg); + if (IS_ERR(chip->charger)) + return -EINVAL; + + return 0; +} + +static int rt9471_enable_charging(struct charger_device *chg_dev, bool en) +{ + int ret = 0; + struct rt9471_chip *chip = dev_get_drvdata(&chg_dev->dev); + + dev_info(chip->dev, "%s en = %d\n", __func__, en); + + ret = __rt9471_enable_chg(chip, en); + if (ret < 0) + dev_notice(chip->dev, "%s en chg fail(%d)\n", __func__, ret); + + return ret; +} + +static int rt9471_is_charging_enabled(struct charger_device *chg_dev, bool *en) +{ + struct rt9471_chip *chip = dev_get_drvdata(&chg_dev->dev); + + return __rt9471_is_chg_enabled(chip, en); +} + +static int rt9471_get_aicr(struct charger_device *chg_dev, u32 *uA) +{ + struct rt9471_chip *chip = dev_get_drvdata(&chg_dev->dev); + + return __rt9471_get_aicr(chip, uA); +} + +static int rt9471_enable_hw_jeita(struct charger_device *chg_dev, bool en) +{ + struct rt9471_chip *chip = dev_get_drvdata(&chg_dev->dev); + + return __rt9471_enable_jeita(chip, en); +} + +static int rt9471_set_aicr(struct charger_device *chg_dev, u32 uA) +{ + struct rt9471_chip *chip = dev_get_drvdata(&chg_dev->dev); + + dev_info(chip->dev, "%s uA = %d\n", __func__, uA); + + chip->input_current_cache = uA; + + if (chip->mmi_is_qc3_authen) { + dev_info(chip->dev, "hvdcp detecting now\n"); + return 0; + } + + return __rt9471_set_aicr(chip, uA); +} + +static int rt9471_set_cv(struct charger_device *chg_dev, u32 uV) +{ + struct rt9471_chip *chip = dev_get_drvdata(&chg_dev->dev); + + return __rt9471_set_cv(chip, uV); +} + +static int rt9471_set_ichg(struct charger_device *chg_dev, u32 uA) +{ + struct rt9471_chip *chip = dev_get_drvdata(&chg_dev->dev); + + return __rt9471_set_ichg(chip, uA); +} + +static int rt9471_enable_te(struct charger_device *chg_dev, bool en) +{ + struct rt9471_chip *chip = dev_get_drvdata(&chg_dev->dev); + int ret = 0; + + if (en) { + ret = __rt9471_eoc_rst(chip); + if (ret < 0) { + dev_notice(chip->dev, "%s __rt9471_eoc_rst fail(%d)\n", __func__, ret); + return ret; + } + + ret = __rt9471_enable_te(chip, en); + if (ret < 0) { + dev_notice(chip->dev, "%s __rt9471_enable_te fail(%d)\n", __func__, ret); + return ret; + } + } else { + ret = __rt9471_enable_te(chip, en); + if (ret < 0) { + dev_notice(chip->dev, "%s __rt9471_enable_te fail(%d)\n", __func__, ret); + return ret; + } + + ret = __rt9471_eoc_rst(chip); + if (ret < 0) { + dev_notice(chip->dev, "%s __rt9471_eoc_rst fail(%d)\n", __func__, ret); + return ret; + } + } + + return ret; +} + +static int rt9471_enable_otg(struct charger_device *chg_dev, bool en) +{ + int ret = 0; + struct rt9471_chip *chip = dev_get_drvdata(&chg_dev->dev); + + dev_info(chip->dev, "%s en = %d\n", __func__, en); + + if (en) { + ret = __rt9471_set_wdt(chip, chip->desc->wdt); + if (ret < 0) { + dev_notice(chip->dev, "%s set wdt fail(%d)\n", + __func__, ret); + return ret; + } + } + + ret = __rt9471_enable_otg(chip, en); + if (ret < 0) { + dev_notice(chip->dev, "%s en otg fail(%d)\n", __func__, ret); + return ret; + } + + if (!en) { + ret = __rt9471_set_wdt(chip, 0); + if (ret < 0) + dev_notice(chip->dev, "%s set wdt fail(%d)\n", + __func__, ret); + } + + return ret; +} + +#if 0 +static int rt9471_enable_discharge(struct charger_device *chg_dev, bool en) //disable otg +{ + int ret = 0; + struct rt9471_chip *chip = dev_get_drvdata(&chg_dev->dev); + + dev_info(chip->dev, "%s en = %d\n", __func__, en); + + ret = rt9471_enable_hidden_mode(chip, true); + if (ret < 0) + return ret; + + ret = (en ? rt9471_set_bit : rt9471_clr_bit)(chip, + RT9471_REG_TOP_HDEN, RT9471_FORCE_EN_VBUS_SINK_MASK); + if (ret < 0) + dev_notice(chip->dev, "%s en = %d fail(%d)\n", + __func__, en, ret); + + rt9471_enable_hidden_mode(chip, false); + + return ret; +} +#endif + +static int rt9471_set_boost_current_limit(struct charger_device *chg_dev, + u32 uA) +{ + struct rt9471_chip *chip = dev_get_drvdata(&chg_dev->dev); + + return __rt9471_set_otgcc(chip, uA); +} + +static int rt9471_dump_registers(struct charger_device *chg_dev, struct seq_file *m) +{ + struct rt9471_chip *chip = dev_get_drvdata(&chg_dev->dev); + u8 addr; + u8 val; + int ret; + + for(addr = 0x0; addr < 0x14; addr++) { + ret = rt9471_i2c_read_byte(chip, addr, &val); + if (ret) + continue; + seq_printf(m, "%s Reg[0x%.2x] = 0x%.2x\n", __func__, addr, val); + + } + + return ret; +} + +static int rt9471_request_dpdm(struct rt9471_chip *chip, bool enable) +{ + int rc = 0; + + mutex_lock(&chip->regulator_lock); + /* fetch the DPDM regulator */ + if (!chip->dpdm_reg && of_get_property(chip->dev->of_node, + "dpdm-supply", NULL)) { + chip->dpdm_reg = devm_regulator_get(chip->dev, "dpdm"); + if (IS_ERR(chip->dpdm_reg)) { + rc = PTR_ERR(chip->dpdm_reg); + dev_err(chip->dev, "Couldn't get dpdm regulator rc=%d\n", rc); + chip->dpdm_reg = NULL; + mutex_unlock(&chip->regulator_lock); + return rc; + } + } + + if (enable) { + if (chip->dpdm_reg && !chip->dpdm_enabled) { + dev_err(chip->dev, "enabling DPDM regulator\n"); + rc = regulator_enable(chip->dpdm_reg); + if (rc < 0) + dev_err(chip->dev, + "Couldn't enable dpdm regulator rc=%d\n", + rc); + else + chip->dpdm_enabled = true; + } + } else { + if (chip->dpdm_reg && chip->dpdm_enabled) { + dev_err(chip->dev, "disabling DPDM regulator\n"); + rc = regulator_disable(chip->dpdm_reg); + if (rc < 0) + dev_err(chip->dev, + "Couldn't disable dpdm regulator rc=%d\n", + rc); + else + chip->dpdm_enabled = false; + } + } + mutex_unlock(&chip->regulator_lock); + + return rc; +} + +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED +bool is_chan_valid(struct rt9471_chip *chip, + enum mmi_qc3p_ext_iio_channels chan) +{ + int rc; + + if (IS_ERR(chip->ext_iio_chans[chan])) + return false; + + if (!chip->ext_iio_chans[chan]) { + chip->ext_iio_chans[chan] = iio_channel_get(chip->dev, + mmi_qc3p_ext_iio_chan_name[chan]); + if (IS_ERR(chip->ext_iio_chans[chan])) { + rc = PTR_ERR(chip->ext_iio_chans[chan]); + if (rc == -EPROBE_DEFER) + chip->ext_iio_chans[chan] = NULL; + + dev_info(chip->dev, "Failed to get IIO channel %s, rc=%d\n", + mmi_qc3p_ext_iio_chan_name[chan], rc); + return false; + } + } + + return true; +} + +int mmi_charger_read_iio_chan(struct rt9471_chip *chip, + enum mmi_qc3p_ext_iio_channels chan, int *val) +{ + int rc; + + if (is_chan_valid(chip, chan)) { + rc = iio_read_channel_processed( + chip->ext_iio_chans[chan], val); + return (rc < 0) ? rc : 0; + } + + return -EINVAL; +} + +int mmi_charger_write_iio_chan(struct rt9471_chip *chip, + enum mmi_qc3p_ext_iio_channels chan, int val) +{ + if (is_chan_valid(chip, chan)) + return iio_write_channel_raw(chip->ext_iio_chans[chan], + val); + + return -EINVAL; +} + +static int mmi_init_iio_psy(struct rt9471_chip *chip, + struct device *dev) +{ + chip->ext_iio_chans = devm_kcalloc(chip->dev, + ARRAY_SIZE(mmi_qc3p_ext_iio_chan_name), + sizeof(*chip->ext_iio_chans), + GFP_KERNEL); + if (!chip->ext_iio_chans) + return -ENOMEM; + + return 0; +} + +int qc3p_start_detection(struct rt9471_chip *chip) +{ + int ret = 0; + ret = mmi_charger_write_iio_chan(chip, SMB5_QC3P_START_DETECT, true); + if(ret ) + dev_err(chip->dev, "Cann't write SMB5_QC3P_START_DETECT IIO\n"); + + dev_info(chip->dev, "write SMB5_QC3P_START_DETECT IIO success\n"); + return 0; +} + +bool qc3p_detection_done(struct rt9471_chip *chip) +{ + int ret = 0; + int val = 0; + int delay_count =0; + + do { + ret = mmi_charger_read_iio_chan(chip, SMB5_QC3P_DETECTION_READY, &val); + if(ret ) + dev_err(chip->dev, "Cann't read SMB5_QC3P_DETECTION_READY IIO\n"); + + dev_info(chip->dev, "read SMB5_QC3P_DETECTION_READY IIO :%d\n",val); + + msleep(100); + delay_count ++; + }while(val == false && delay_count <= 35); + + dev_info(chip->dev, "read SMB5_QC3P_DETECTION_READY IIO :%d\n",val); + return val; +} + +int qc3p_read_charger_type(struct rt9471_chip *chip) +{ + int ret = 0; + int val = 0; + + ret = mmi_charger_read_iio_chan(chip, SMB5_USB_REAL_TYPE, &val); + if(ret ) + dev_err(chip->dev, "Cann't read SMB5_USB_REAL_TYPE IIO\n"); + + dev_info(chip->dev, "read SMB5_USB_REAL_TYPE IIO :%d\n",val); + return val; +} + +bool qc3p_update_policy(struct rt9471_chip *chip) +{ + int ret = 0; + int val = 0; + + ret = mmi_charger_write_iio_chan(chip, SMB5_QC3P_START_POLICY, val); + if(ret ) + dev_err(chip->dev, "Cann't write SMB5_QC3P_START_POLICY IIO\n"); + + dev_info(chip->dev, "write SMB5_QC3P_START_POLICY IIO :%d\n",val); + return val; +} + +int bc12_start_detection(struct rt9471_chip *chip) +{ + int ret = 0; + ret = mmi_charger_write_iio_chan(chip, SMB5_BC12_START_DETECT, true); + if(ret ) + dev_err(chip->dev, "Cann't write SMB5_BC12_START_DETECT IIO\n"); + + dev_info(chip->dev, "write SMB5_BC12_START_DETECT IIO success\n"); + return 0; +} + +bool bc12_detection_done(struct rt9471_chip *chip) +{ + int ret = 0; + int val = 0; + int delay_count =0; + + do { + ret = mmi_charger_read_iio_chan(chip, SMB5_BC12_DETECTION_READY, &val); + if(ret ) + dev_err(chip->dev, "Cann't read SMB5_BC12_DETECTION_READY IIO\n"); + + dev_info(chip->dev, "read SMB5_BC12_DETECTION_READY IIO :%d\n",val); + if (val) + break; + + msleep(50); + delay_count ++; + }while(val == false && delay_count <= 25); + + dev_info(chip->dev, "read SMB5_BC12_DETECTION_READY IIO :%d\n",val); + return val; +} + +int bc12_read_charger_type(struct rt9471_chip *chip) +{ + int ret = 0; + int val = 0; + + ret = mmi_charger_read_iio_chan(chip, SMB5_READ_BC12_CHG_TYPE, &val); + if(ret ) + dev_err(chip->dev, "Cann't read SMB5_READ_BC12_CHG_TYPE IIO\n"); + + dev_info(chip->dev, "read SMB5_READ_BC12_CHG_TYPE IIO :%d\n",val); + return val; +} +#endif + +static void rt9471_monitor_workfunc(struct work_struct *work) +{ + struct rt9471_chip *chip = container_of(work, struct rt9471_chip, monitor_work.work); + + dev_info(chip->dev, "%s\n", __func__); + + __rt9471_dump_registers(chip); + + schedule_delayed_work(&chip->monitor_work, 10*HZ); +} + +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED +static int mmi_hvdcp_detect_kthread(void *param) +{ + struct rt9471_chip *chip = param; + int ret; + int charger_type = POWER_SUPPLY_TYPE_UNKNOWN; + struct sched_param sch_param = {.sched_priority = MAX_RT_PRIO-1}; + + sched_setscheduler(current, SCHED_FIFO, &sch_param); + + do { + + wait_event_interruptible(chip->mmi_qc3_wait_que, chip->mmi_qc3_trig_flag || kthread_should_stop()); + if (kthread_should_stop()) + break; + + dev_info(chip->dev, "mmi_hvdcp_detect_kthread begin\n"); + chip->mmi_qc3_trig_flag = false; + chip->mmi_is_qc3_authen = true; + __rt9471_set_aicr(chip, MMI_HVDCP_DETECT_ICL_LIMIT); + + rt9471_enable_te(chip->chg_dev, true); + + qc3p_start_detection(chip); + qc3p_detection_done(chip); + ret = qc3p_read_charger_type(chip); + + switch(ret) { + case WT_CHG_TYPE_QC2: + charger_type = POWER_SUPPLY_TYPE_USB_HVDCP; + dev_info(chip->dev, "HDVCP 2.0 detected\n"); + break; + case WT_CHG_TYPE_QC3: + case WT_CHG_TYPE_QC3P_18W: + charger_type = POWER_SUPPLY_TYPE_USB_HVDCP_3; + dev_info(chip->dev, "HDVCP 3.0 or qc3p 18W detected\n"); + break; + case WT_CHG_TYPE_QC3P_27W: + dev_err(chip->dev, "QC3P 27W have been detected !\n"); + charger_type = POWER_SUPPLY_TYPE_USB_HVDCP_3P5; +#ifdef CONFIG_MMI_QC3P_TURBO_CHARGER + qc3p_update_policy(chip); +#endif + break; + default: + dev_info(chip->dev, "No HDVCP detected\n"); + goto out; + } + + if (!rt9471_is_vbus_gd(chip)) + goto out; + + chip->real_charger_type = charger_type; + //notify charging policy to update charger type + chip->chg_dev->noti.hvdcp_done = true; + charger_dev_notify(chip->chg_dev); + +out: + __rt9471_set_aicr(chip, chip->input_current_cache); + chip->mmi_is_qc3_authen = false; + charger_type = POWER_SUPPLY_TYPE_UNKNOWN; + dev_info(chip->dev, "mmi_hvdcp_detect_kthread end\n"); + }while(!kthread_should_stop()); + + dev_dbg(chip->dev, "qc3 kthread stop\n"); + return 0; +} + +static void mmi_start_hvdcp_detect(struct rt9471_chip *chip) +{ + + if (chip->mmi_qc3_support + && chip->real_charger_type == POWER_SUPPLY_TYPE_USB_DCP) { + dev_info(chip->dev, "start hvdcp detect\n"); + chip->mmi_qc3_trig_flag = true; + wake_up_interruptible(&chip->mmi_qc3_wait_que); + } +} +#endif + +static void rt9471_recheck_charger_type_workfunc(struct work_struct *work) +{ + int ret; + struct rt9471_chip *chip = container_of(work, struct rt9471_chip, recheck_charger_work.work); + + bc12_start_detection(chip); + bc12_detection_done(chip); + ret = bc12_read_charger_type(chip); + + switch(ret) { + case WT_CHG_TYPE_FC: + case WT_CHG_TYPE_OCP: + dev_err(chip->dev, "rerun, float type have been detected !\n"); + chip->real_charger_type = POWER_SUPPLY_TYPE_USB_FLOAT; + break; + case WT_CHG_TYPE_SDP: + dev_err(chip->dev, "rerun, SDP have been detected !\n"); + chip->real_charger_type = POWER_SUPPLY_TYPE_USB; + break; + case WT_CHG_TYPE_CDP: + dev_err(chip->dev, "rerun, CDP have been detected !\n"); + chip->real_charger_type = POWER_SUPPLY_TYPE_USB_CDP; + break; + case WT_CHG_TYPE_DCP: + case WT_CHG_TYPE_QC2: + case WT_CHG_TYPE_QC3: + case WT_CHG_TYPE_QC3P_18W: + case WT_CHG_TYPE_QC3P_27W: + case Z350_CHG_TYPE_HVDCP: + dev_err(chip->dev, "rerun, DCP have been detected !\n"); + chip->real_charger_type = POWER_SUPPLY_TYPE_USB_DCP; + mmi_start_hvdcp_detect(chip); + break; + default: + pr_err("rerun, bc12: no charge type detected\n"); + if (chip->state.online) { + rt9471_request_dpdm(chip, false); + msleep(10); + rt9471_request_dpdm(chip, true); + schedule_delayed_work(&chip->recheck_charger_work, 3*HZ); + } + break; + } + + chip->chg_dev->noti.apsd_done = true; + charger_dev_notify(chip->chg_dev); +} + +static void rt9471_plug_in_func(struct rt9471_chip *chip) +{ +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED + int ret; + + rt9471_request_dpdm(chip, true); + bc12_start_detection(chip); + bc12_detection_done(chip); + ret = bc12_read_charger_type(chip); + + switch(ret) { + case WT_CHG_TYPE_FC: + case WT_CHG_TYPE_OCP: + dev_err(chip->dev, "float type have been detected !\n"); + chip->real_charger_type = POWER_SUPPLY_TYPE_USB_FLOAT; + break; + case WT_CHG_TYPE_SDP: + dev_err(chip->dev, "SDP have been detected !\n"); + chip->real_charger_type = POWER_SUPPLY_TYPE_USB; + break; + case WT_CHG_TYPE_CDP: + dev_err(chip->dev, "CDP have been detected !\n"); + chip->real_charger_type = POWER_SUPPLY_TYPE_USB_CDP; + break; + case WT_CHG_TYPE_DCP: + case WT_CHG_TYPE_QC2: + case WT_CHG_TYPE_QC3: + case WT_CHG_TYPE_QC3P_18W: + case WT_CHG_TYPE_QC3P_27W: + case Z350_CHG_TYPE_HVDCP: + dev_err(chip->dev, "DCP have been detected !\n"); + chip->real_charger_type = POWER_SUPPLY_TYPE_USB_DCP; + mmi_start_hvdcp_detect(chip); + break; + default: + pr_err("bc12: no charge type detected\n"); + break; + } +#endif + schedule_delayed_work(&chip->monitor_work, 0); + chip->chg_dev->noti.apsd_done = true; + charger_dev_notify(chip->chg_dev); + + if (chip->real_charger_type == POWER_SUPPLY_TYPE_USB || + chip->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT || + (chip->real_charger_type == POWER_SUPPLY_TYPE_UNKNOWN && chip->state.online == true)) { + schedule_delayed_work(&chip->recheck_charger_work, 3*HZ); + } +} + +static void rt9471_plug_out_func(struct rt9471_chip *chip) +{ + cancel_delayed_work_sync(&chip->monitor_work); +#ifdef CONFIG_MMI_QC3P_TURBO_CHARGER + rt9471_enable_te(chip->chg_dev, true); + qc3p_update_policy(chip); +#endif + chip->chg_dev->noti.apsd_done = false; + chip->chg_dev->noti.hvdcp_done = false; + chip->real_charger_type = POWER_SUPPLY_TYPE_UNKNOWN; + rt9471_request_dpdm(chip, false); + charger_dev_notify(chip->chg_dev); +} + +static void charger_detect_work_func(struct work_struct *work) +{ + struct rt9471_chip *chip = container_of(work, struct rt9471_chip, charge_detect_work); + bool vbus_gd = false; + bool chg_ready = false; + + vbus_gd = rt9471_is_vbus_gd(chip); + chg_ready = rt9471_is_vbus_ready_for_chg(chip); + dev_info(chip->dev, "%s: vbus_gd:%d, chg_ready:%d\n", __func__, vbus_gd, chg_ready); + chip->state.online = chg_ready; + chip->state.vbus_gd = vbus_gd; + + if ((!vbus_gd) && (chip->status & RT9471_STATUS_PLUGIN)) { + dev_info(chip->dev, "%s:adapter removed\n", __func__); + chip->status &= ~RT9471_STATUS_PLUGIN; + rt9471_plug_out_func(chip); + } else if (chg_ready && !(chip->status & RT9471_STATUS_PLUGIN)) { + dev_info(chip->dev, "%s:adapter plugged in\n", __func__); + chip->status |= RT9471_STATUS_PLUGIN; + rt9471_plug_in_func(chip); + } +} + +static int rt9471_get_real_charger_type(struct charger_device *chg_dev, int *chg_type) +{ + struct rt9471_chip *chip = dev_get_drvdata(&chg_dev->dev); + + *chg_type = chip->real_charger_type; + + return 0; +} + +static int rt9471_is_charging_halted(struct charger_device *chg_dev, bool *en) +{ + struct rt9471_chip *chip = dev_get_drvdata(&chg_dev->dev); + enum rt9471_ic_stat ic_stat = RT9471_ICSTAT_SLEEP; + int ret = 0; + + ret = __rt9471_get_ic_stat(chip, &ic_stat); + if (ret < 0) + return ret; + + dev_info(chip->dev, "%s ic_stat = %d\n", __func__, ic_stat); + + switch(ic_stat) { + case RT9471_ICSTAT_IEOC: + case RT9471_ICSTAT_BGCHG: + case RT9471_ICSTAT_CHGDONE: + *en = true; + break; + + case RT9471_ICSTAT_TRICKLECHG: + case RT9471_ICSTAT_PRECHG: + case RT9471_ICSTAT_FASTCHG: + *en = false; + break; + + default: + break; + } + + return ret; +} + +static struct charger_ops rt9471_chg_ops = { + //.get_pulse_cnt = mmi_get_pulse_cnt, + //.set_dp_dm = mmi_set_dp_dm, + .get_real_charger_type = rt9471_get_real_charger_type, + .set_input_current = rt9471_set_aicr, + .get_input_current = rt9471_get_aicr, + .enable_hw_jeita = rt9471_enable_hw_jeita, + .enable_otg = rt9471_enable_otg, + .set_boost_current_limit = rt9471_set_boost_current_limit, + .enable_charging = rt9471_enable_charging, + .set_charging_current = rt9471_set_ichg, + .set_constant_voltage = rt9471_set_cv, + .is_charge_halted = rt9471_is_charging_halted, + + .dump_registers = rt9471_dump_registers, + .is_enabled_charging = rt9471_is_charging_enabled, + .enable_termination = rt9471_enable_te, +}; + +static ssize_t shipping_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret = 0, tmp = 0; + struct rt9471_chip *chip = dev_get_drvdata(dev); + + ret = kstrtoint(buf, 10, &tmp); + if (ret < 0) { + dev_notice(dev, "%s parsing number fail(%d)\n", __func__, ret); + return -EINVAL; + } + if (tmp != 5526789) + return -EINVAL; + chip->enter_shipping_mode = true; + /* + * Use kernel_halt() instead of kernel_power_off() to prevent + * the system from booting again while cable still plugged-in. + */ + kernel_halt(); + + return count; +} + +static const DEVICE_ATTR_WO(shipping_mode); + +static int rt9471_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret = 0; + struct rt9471_chip *chip = NULL; + + dev_info(&client->dev, "%s (%s)\n", __func__, RT9471_DRV_VERSION); + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + chip->client = client; + chip->dev = &client->dev; + mutex_init(&chip->io_lock); + mutex_init(&chip->hidden_mode_lock); + mutex_init(&chip->hz_lock); + chip->hidden_mode_cnt = 0; + chip->chg_done_once = false; + chip->buck_dwork_ws = + wakeup_source_register(chip->dev, + devm_kasprintf(chip->dev, GFP_KERNEL, + "rt9471_buck_dwork_ws.%s", + dev_name(chip->dev))); + INIT_DELAYED_WORK(&chip->buck_dwork, rt9471_buck_dwork_handler); + chip->enter_shipping_mode = false; + init_completion(&chip->aicc_done); + init_completion(&chip->pe_done); + chip->is_primary = false; + + if (!rt9471_check_devinfo(chip)) { + ret = -ENODEV; + goto err_nodev; + } + + ret = rt9471_parse_dt(chip); + if (ret < 0) + dev_notice(chip->dev, "%s parse dt fail(%d)\n", __func__, ret); + +#ifdef CONFIG_RT_REGMAP + ret = rt9471_register_rt_regmap(chip); + if (ret < 0) { + dev_notice(chip->dev, "%s register rt regmap fail(%d)\n", + __func__, ret); + goto err_register_rm; + } +#endif /* CONFIG_RT_REGMAP */ + + ret = rt9471_reset_register(chip); + if (ret < 0) + dev_notice(chip->dev, "%s reset register fail(%d)\n", + __func__, ret); + + ret = rt9471_init_setting(chip); + if (ret < 0) { + dev_notice(chip->dev, "%s init fail(%d)\n", __func__, ret); + goto err_init; + } + + i2c_set_clientdata(client, chip); + + chip->chg_dev = charger_device_register(chip->mmi_chg_dev_name, + chip->dev, chip, &rt9471_chg_ops, &chip->chg_props); + if (IS_ERR_OR_NULL(chip->chg_dev)) { + ret = PTR_ERR(chip->chg_dev); + dev_notice(chip->dev, "%s register chg dev fail(%d)\n", + __func__, ret); + goto err_register_chg_dev; + } + + ret = rt9471_register_irq(chip); + if (ret < 0) { + dev_notice(chip->dev, "%s register irq fail(%d)\n", + __func__, ret); + goto err_register_irq; + } + + INIT_DELAYED_WORK(&chip->monitor_work, rt9471_monitor_workfunc); + INIT_DELAYED_WORK(&chip->recheck_charger_work, rt9471_recheck_charger_type_workfunc); + INIT_WORK(&chip->charge_detect_work, charger_detect_work_func); + +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED + if (chip->mmi_qc3_support) { + chip->mmi_qc3_authen_task = kthread_create(mmi_hvdcp_detect_kthread, chip, "mmi_qc3_authen"); + if (IS_ERR(chip->mmi_qc3_authen_task)) { + ret = PTR_ERR(chip->mmi_qc3_authen_task); + dev_err(chip->dev, "Failed to create mmi_qc3_authen_task ret = %d\n", ret); + goto err_check_chg; + } + init_waitqueue_head(&chip->mmi_qc3_wait_que); + wake_up_process(chip->mmi_qc3_authen_task); + } +#endif + + ret = rt9471_power_supply_init(chip, chip->dev); + if (ret) { + dev_notice(chip->dev, "Failed to register power supply\n"); + goto err_check_chg; + } + + ret = rt9471_parse_dt_adc_channels(chip); + if (ret) { + dev_err(chip->dev, "Failed to get adc channels%d\n", ret); + goto err_check_chg; + } + +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED + ret = mmi_init_iio_psy(chip, chip->dev); + if (ret) { + dev_notice(chip->dev, "mmi iio psy init failed\n"); + } +#endif + + ret = rt9471_check_chg(chip); + if (ret < 0) { + dev_notice(chip->dev, "%s check chg(%d)\n", __func__, ret); + goto err_check_chg; + } + + ret = rt9471_init_irq(chip); + if (ret < 0) { + dev_notice(chip->dev, "%s init irq fail(%d)\n", __func__, ret); + goto err_init_irq; + } + + ret = device_create_file(chip->dev, &dev_attr_shipping_mode); + if (ret < 0) { + dev_notice(chip->dev, "%s create file fail(%d)\n", + __func__, ret); + goto err_create_file; + } + + schedule_work(&chip->charge_detect_work); + + __rt9471_dump_registers(chip); + dev_info(chip->dev, "%s successfully\n", __func__); + return 0; + +err_create_file: + disable_irq(chip->irq); +err_init_irq: +err_check_chg: +err_register_irq: +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED + if (chip->mmi_qc3_support && chip->mmi_qc3_authen_task) + kthread_stop(chip->mmi_qc3_authen_task); +#endif + charger_device_unregister(chip->chg_dev); +err_register_chg_dev: + cancel_delayed_work_sync(&chip->buck_dwork); +err_init: + rt9471_reset_register(chip); +#ifdef CONFIG_RT_REGMAP + rt_regmap_device_unregister(chip->rm_dev); +err_register_rm: +#endif /* CONFIG_RT_REGMAP */ +err_nodev: + mutex_destroy(&chip->io_lock); + mutex_destroy(&chip->hidden_mode_lock); + mutex_destroy(&chip->hz_lock); + wakeup_source_unregister(chip->buck_dwork_ws); + + return ret; +} + +static int rt9471_remove(struct i2c_client *client) +{ + struct rt9471_chip *chip = i2c_get_clientdata(client); + + dev_info(chip->dev, "%s\n", __func__); +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED + if (chip->mmi_qc3_support && chip->mmi_qc3_authen_task) + kthread_stop(chip->mmi_qc3_authen_task); +#endif + cancel_delayed_work_sync(&chip->monitor_work); + device_remove_file(chip->dev, &dev_attr_shipping_mode); + disable_irq(chip->irq); + charger_device_unregister(chip->chg_dev); + cancel_delayed_work_sync(&chip->buck_dwork); + rt9471_reset_register(chip); +#ifdef CONFIG_RT_REGMAP + rt_regmap_device_unregister(chip->rm_dev); +#endif /* CONFIG_RT_REGMAP */ + mutex_destroy(&chip->io_lock); + mutex_destroy(&chip->hidden_mode_lock); + mutex_destroy(&chip->hz_lock); + wakeup_source_unregister(chip->buck_dwork_ws); + + return 0; +} + +static void rt9471_shutdown(struct i2c_client *client) +{ + struct rt9471_chip *chip = i2c_get_clientdata(client); + + dev_info(chip->dev, "%s\n", __func__); + + disable_irq(chip->irq); + charger_device_unregister(chip->chg_dev); + cancel_delayed_work_sync(&chip->buck_dwork); + rt9471_reset_register(chip); + if (chip->enter_shipping_mode) + __rt9471_enable_shipmode(chip); +} + +static int rt9471_suspend(struct device *dev) +{ + struct rt9471_chip *chip = dev_get_drvdata(dev); + + dev_info(dev, "%s\n", __func__); + if (device_may_wakeup(dev)) + enable_irq_wake(chip->irq); + disable_irq(chip->irq); + + return 0; +} + +static int rt9471_resume(struct device *dev) +{ + struct rt9471_chip *chip = dev_get_drvdata(dev); + + dev_info(dev, "%s\n", __func__); + enable_irq(chip->irq); + if (device_may_wakeup(dev)) + disable_irq_wake(chip->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(rt9471_pm_ops, rt9471_suspend, rt9471_resume); + +static const struct of_device_id rt9471_of_device_id[] = { + { .compatible = "richtek,rt9470", }, + { .compatible = "richtek,rt9471", }, + { .compatible = "richtek,swchg", }, + { }, +}; +MODULE_DEVICE_TABLE(of, rt9471_of_device_id); + +static const struct i2c_device_id rt9471_i2c_device_id[] = { + { "rt9470", 0 }, + { "rt9471", 1 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, rt9471_i2c_device_id); + +static struct i2c_driver rt9471_i2c_driver = { + .driver = { + .name = "rt9471", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rt9471_of_device_id), + .pm = &rt9471_pm_ops, + }, + .probe = rt9471_probe, + .remove = rt9471_remove, + .shutdown = rt9471_shutdown, + .id_table = rt9471_i2c_device_id, +}; +module_i2c_driver(rt9471_i2c_driver); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("ShuFanLee "); +MODULE_DESCRIPTION("RT9471 Charger Driver"); +MODULE_VERSION(RT9471_DRV_VERSION); diff --git a/drivers/power/rt9471_chg_mmi/rt9471_charger.h b/drivers/power/rt9471_chg_mmi/rt9471_charger.h new file mode 100644 index 000000000000..607f9534e61e --- /dev/null +++ b/drivers/power/rt9471_chg_mmi/rt9471_charger.h @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2019 MediaTek Inc. + * + * 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 http://www.gnu.org/licenses/gpl-2.0.html for more details. + */ + +#ifndef __RT9471_CHARGER_H +#define __RT9471_CHARGER_H + +#define RT9471_I2C_NO_MUTEX + +#define RT9471_DEVICE_ADDR 0x53 +#define RT9470_DEVID 0x09 +#define RT9470D_DEVID 0x0A +#define RT9471_DEVID 0x0D +#define RT9471D_DEVID 0x0E + +enum rt9471_reg_addr { + RT9471_REG_OTGCFG = 0x00, + RT9471_REG_TOP, + RT9471_REG_FUNCTION, + RT9471_REG_IBUS, + RT9471_REG_VBUS, + RT9471_REG_PRECHG, + RT9471_REG_REGU, + RT9471_REG_VCHG, + RT9471_REG_ICHG, + RT9471_REG_CHGTIMER, + RT9471_REG_EOC, + RT9471_REG_INFO, + RT9471_REG_JEITA, + RT9471_REG_PUMPEXP, + RT9471_REG_DPDMDET, + RT9471_REG_STATUS, + RT9471_REG_STAT0, + RT9471_REG_STAT1, + RT9471_REG_STAT2, + RT9471_REG_STAT3, + RT9471_REG_IRQ0 = 0x20, + RT9471_REG_IRQ1, + RT9471_REG_IRQ2, + RT9471_REG_IRQ3, + RT9471_REG_MASK0 = 0x30, + RT9471_REG_MASK1, + RT9471_REG_MASK2, + RT9471_REG_MASK3, + RT9471_REG_HIDDEN_0 = 0x40, + RT9471_REG_HIDDEN_2 = 0x42, + RT9471_REG_TOP_HDEN, + RT9471_REG_BUCK_HDEN1 = 0x45, + RT9471_REG_BUCK_HDEN2 = 0x46, + RT9471_REG_BUCK_HDEN3 = 0x54, + RT9471_REG_BUCK_HDEN4, + RT9471_REG_OTG_HDEN2 = 0x58, + RT9471_REG_BUCK_HDEN5, + RT9471_REG_PASSCODE1 = 0xA0, +}; + +/* ========== OTGCFG 0x00 ============ */ +#define RT9471_OTGCC_MASK BIT(0) + +/* ========== TOP 0x01 ============ */ +#define RT9471_QONRST_MASK BIT(7) +#define RT9471_DISI2CTO_MASK BIT(3) +#define RT9471_WDTCNTRST_MASK BIT(2) +#define RT9471_WDT_SHIFT 0 +#define RT9471_WDT_MASK 0x03 + +/* ========== FUNCTION 0x02 ============ */ +#define RT9471_BATFETDIS_SHIFT 7 +#define RT9471_BATFETDIS_MASK BIT(7) +#define RT9471_HZ_SHIFT 5 +#define RT9471_HZ_MASK BIT(5) +#define RT9471_OTG_EN_SHIFT 1 +#define RT9471_OTG_EN_MASK BIT(1) +#define RT9471_CHG_EN_SHIFT 0 +#define RT9471_CHG_EN_MASK BIT(0) + +/* ========== IBUS 0x03 ============ */ +#define RT9471_AICC_EN_SHIFT 7 +#define RT9471_AICC_EN_MASK BIT(7) +#define RT9471_AUTOAICR_MASK BIT(6) +#define RT9471_AICR_SHIFT 0 +#define RT9471_AICR_MASK 0x3F +#define RT9471_AICR_MIN 50000 +#define RT9471_AICR_MAX 3200000 +#define RT9471_AICR_STEP 50000 + +/* ========== VBUS 0x04 ============ */ +#define RT9471_VAC_OVP_SHIFT 6 +#define RT9471_VAC_OVP_MASK 0xC0 +#define RT9471_MIVR_SHIFT 0 +#define RT9471_MIVR_MASK 0x0F +#define RT9471_MIVR_MIN 3900000 +#define RT9471_MIVR_MAX 5400000 +#define RT9471_MIVR_STEP 100000 +#define RT9471_MIVRTRACK_SHIFT 4 +#define RT9471_MIVRTRACK_MASK 0x30 + +/* ========== VCHG 0x07 ============ */ +#define RT9471_VRECHG_MASK BIT(7) +#define RT9471_CV_SHIFT 0 +#define RT9471_CV_MASK 0x7F +#define RT9471_CV_MIN 3900000 +#define RT9471_CV_MAX 4700000 +#define RT9471_CV_STEP 10000 + +/* ========== ICHG 0x08 ============ */ +#define RT9471_ICHG_SHIFT 0 +#define RT9471_ICHG_MASK 0x3F +#define RT9471_ICHG_MIN 0 +#define RT9471_ICHG_MAX 3150000 +#define RT9471_ICHG_STEP 50000 + +/* ========== CHGTIMER 0x09 ============ */ +#define RT9471_SAFETMR_EN_SHIFT 7 +#define RT9471_SAFETMR_EN_MASK BIT(7) +#define RT9471_SAFETMR_SHIFT 4 +#define RT9471_SAFETMR_MASK 0x30 +#define RT9471_SAFETMR_MIN 5 +#define RT9471_SAFETMR_MAX 20 +#define RT9471_SAFETMR_STEP 5 + +/* ========== EOC 0x0A ============ */ +#define RT9471_IEOC_SHIFT 4 +#define RT9471_IEOC_MASK 0xF0 +#define RT9471_IEOC_MIN 50000 +#define RT9471_IEOC_MAX 800000 +#define RT9471_IEOC_STEP 50000 +#define RT9471_TE_MASK BIT(1) +#define RT9471_EOC_RST_SHIFT 0 +#define RT9471_EOC_RST_MASK BIT(0) + +/* ========== INFO 0x0B ============ */ +#define RT9471_REGRST_MASK BIT(7) +#define RT9471_DEVID_SHIFT 3 +#define RT9471_DEVID_MASK 0x78 +#define RT9471_DEVREV_SHIFT 0 +#define RT9471_DEVREV_MASK 0x03 + +/* ========== JEITA 0x0C ============ */ +#define RT9471_JEITA_EN_MASK BIT(7) + +/* ========== PUMPEXP 0x0D ============ */ +#define RT9471_PE_EN_MASK BIT(7) +#define RT9471_PE_SEL_MASK BIT(6) +#define RT9471_PE10_INC_MASK BIT(5) +#define RT9471_PE20_CODE_SHIFT 0 +#define RT9471_PE20_CODE_MASK 0x1F +#define RT9471_PE20_CODE_MIN 5500000 +#define RT9471_PE20_CODE_MAX 20000000 +#define RT9471_PE20_CODE_STEP 500000 + +/* ========== DPDMDET 0x0E ============ */ +#define RT9471_BC12_EN_MASK BIT(7) + +/* ========== STATUS 0x0F ============ */ +#define RT9471_PORTSTAT_SHIFT 4 +#define RT9471_PORTSTAT_MASK 0xF0 +#define RT9471_ICSTAT_SHIFT 0 +#define RT9471_ICSTAT_MASK 0x0F + +/* ========== STAT0 0x10 ============ */ +#define RT9471_ST_VBUSGD_SHIFT 7 +#define RT9471_ST_VBUSGD_MASK BIT(7) +#define RT9471_ST_CHGRDY_SHIFT 6 +#define RT9471_ST_CHGRDY_MASK BIT(6) +#define RT9471_ST_IEOC_SHIFT 5 +#define RT9471_ST_IEOC_MASK BIT(5) +#define RT9471_ST_BGCHG_SHIFT 4 +#define RT9471_ST_BGCHG_MASK BIT(4) +#define RT9471_ST_CHGDONE_SHIFT 3 +#define RT9471_ST_CHGDONE_MASK BIT(3) +#define RT9471_ST_BC12_DONE_SHIFT 0 +#define RT9471_ST_BC12_DONE_MASK BIT(0) + +/* ========== STAT1 0x11 ============ */ +#define RT9471_ST_MIVR_SHIFT 7 +#define RT9471_ST_MIVR_MASK BIT(7) +#define RT9471_ST_AICR_MASK BIT(6) +#define RT9471_ST_BATOV_MASK BIT(1) + +/* ========== STAT2 0x12 ============ */ +#define RT9471_ST_SYSMIN_SHIFT 1 + +/* ========== STAT3 0x13 ============ */ +#define RT9471_ST_VACOV_SHIFT 6 +#define RT9471_ST_VACOV_MASK BIT(6) + +/* ========== HIDDEN_0 0x40 ============ */ +#define RT9471_CHIP_REV_SHIFT 5 +#define RT9471_CHIP_REV_MASK 0xE0 + +/* ========== HIDDEN_2 0x42 ============ */ +#define RT9471_FORCE_HZ_SHIFT 2 +#define RT9471_FORCE_HZ_MASK BIT(2) + +/* ========== TOP_HDEN 0x43 ============ */ +#define RT9471_FORCE_EN_VBUS_SINK_SHIFT 4 +#define RT9471_FORCE_EN_VBUS_SINK_MASK BIT(4) + +/* ========== OTG_HDEN2 0x58 ============ */ +#define RT9471_REG_OTG_RES_COMP_SHIFT 4 +#define RT9471_REG_OTG_RES_COMP_MASK 0x30 + + +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED +enum WT_charger_type{ + WT_CHG_TYPE_BEGIN = 0, + WT_CHG_TYPE_FC, + WT_CHG_TYPE_SDP, + WT_CHG_TYPE_CDP, + WT_CHG_TYPE_DCP, + WT_CHG_TYPE_QC2, + WT_CHG_TYPE_QC3, + WT_CHG_TYPE_OCP, + WT_CHG_TYPE_QC3P_18W,//0x8 + WT_CHG_TYPE_QC3P_27W, + WT6670_CHG_TYPE_UNKNOWN, + Z350_CHG_TYPE_HVDCP = 0x10, +}; + +enum mmi_qc3p_ext_iio_channels { + /*qc3p*/ + SMB5_USB_REAL_TYPE, + SMB5_QC3P_POWER, + SMB5_QC3P_START_DETECT, + SMB5_QC3P_DETECTION_READY, + SMB5_QC3P_START_POLICY, + SMB5_BC12_START_DETECT, + SMB5_BC12_DETECTION_READY, + SMB5_READ_USBIN_VOLTAGE, + SMB5_READ_BC12_CHG_TYPE, +}; + +static const char * const mmi_qc3p_ext_iio_chan_name[] = { + /*qc3p*/ + [SMB5_USB_REAL_TYPE] = "wt6670_usb_real_type", + [SMB5_QC3P_POWER] = "wt6670_usb_qc3p_power", + [SMB5_QC3P_START_DETECT] = "wt6670_start_detection", + [SMB5_QC3P_DETECTION_READY] "wt6670_detection_ready", + [SMB5_QC3P_START_POLICY] "qc3p_start_policy", + [SMB5_BC12_START_DETECT] = "wt6670_start_bc12_detection", + [SMB5_BC12_DETECTION_READY] = "wt6670_detection_bc12_ready", + [SMB5_READ_USBIN_VOLTAGE] = "read_usbin_voltage", + [SMB5_READ_BC12_CHG_TYPE] = "wt6670_bc12_chg_type", +}; + +#endif +#endif /* __RT9471_CHARGER_H */ diff --git a/drivers/power/sc760x_charger_mmi/Android.mk b/drivers/power/sc760x_charger_mmi/Android.mk new file mode 100644 index 000000000000..30467a26e4f1 --- /dev/null +++ b/drivers/power/sc760x_charger_mmi/Android.mk @@ -0,0 +1,16 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := sc760x_charger_mmi.ko + +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_charger.ko + +ifeq ($(THERMAL_RATIO_CONTROL),true) + KERNEL_CFLAGS += CONFIG_THERMAL_RATIO_CONTROL=y + KBUILD_OPTIONS += CONFIG_THERMAL_RATIO_CONTROL=y +endif +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/sc760x_charger_mmi/Kbuild b/drivers/power/sc760x_charger_mmi/Kbuild new file mode 100644 index 000000000000..60e156bdfd25 --- /dev/null +++ b/drivers/power/sc760x_charger_mmi/Kbuild @@ -0,0 +1,13 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/sc760x_charger_mmi \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/mmi_charger + +ifneq ($(filter m y,$(CONFIG_THERMAL_RATIO_CONTROL)),) + EXTRA_CFLAGS += -DTHERMAL_RATIO_CONTROL +endif + +obj-m += sc760x_charger_mmi.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/sc760x_charger_mmi/Makefile b/drivers/power/sc760x_charger_mmi/Makefile new file mode 100644 index 000000000000..a040b9a0e518 --- /dev/null +++ b/drivers/power/sc760x_charger_mmi/Makefile @@ -0,0 +1,16 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/sc760x_charger_mmi/sc760x_charger_mmi.c b/drivers/power/sc760x_charger_mmi/sc760x_charger_mmi.c new file mode 100644 index 000000000000..c3b6da56e971 --- /dev/null +++ b/drivers/power/sc760x_charger_mmi/sc760x_charger_mmi.c @@ -0,0 +1,2185 @@ +/* + * Copyright (c) 2022 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ +#define pr_fmt(fmt) "sc760x-charger: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "sc760x_charger_mmi.h" + + +static const char* sc760x_irq_name[] = { + [SC760X_MASTER] = "sc760x_master_irq", + [SC760X_SLAVE] = "sc760x_slave_irq", +}; + +static int sc760x_role_data[] = { + [SC760X_MASTER] = SC760X_MASTER, + [SC760X_SLAVE] = SC760X_SLAVE, +}; + +static struct sc760x_cfg_e default_cfg = { + .bat_chg_lim_disable = 0, + .bat_chg_lim = 39, + .pow_lim_disable = 0, + .ilim_disable = 0, + .load_switch_disable = 0, + .lp_mode_enable = 0, + .itrichg = 3, + .iprechg = 2, + .vfc_chg = 2, + .chg_ovp_disable = 0, + .chg_ovp = 0, + .bat_ovp_disable = 0, + .bat_ovp = 10, + .chg_ocp_disable = 0, + .chg_ocp = 2, + .dsg_ocp_disable = 0, + .dsg_ocp = 2, + .tdie_flt_disable = 0, + .tdie_alm_disable = 0, + .tdie_alm = 9, +}; + +static const int sc760x_adc_m[] = + {3125, 125, 125, 3125, 5}; + +static const int sc760x_adc_l[] = + {1000, 100, 100, 10000, 10}; + +//REGISTER +static const struct reg_field sc760x_reg_fields[] = { + /*reg00*/ + [DEVICE_REV] = REG_FIELD(0x00, 4, 7), + [DEVICE_ID] = REG_FIELD(0x00, 0, 3), + /*reg01*/ + [IBAT_CHG_LIM] = REG_FIELD(0x01, 0, 7), + /*reg02*/ + [POW_LIM_DIS] = REG_FIELD(0x02, 7, 7), + [VBALANCE_DIS] = REG_FIELD(0x02, 5, 5), + [POW_LIM] = REG_FIELD(0x02, 0, 3), + /*reg03*/ + [ILIM_DIS] = REG_FIELD(0x03, 7, 7), + [IBAT_CHG_LIM_DIS] = REG_FIELD(0x03, 6, 6), + [BAT_DET_DIS] = REG_FIELD(0x03, 5, 5), + [VDIFF_CHECK_DIS] = REG_FIELD(0x03, 4, 4), + [LS_OFF] = REG_FIELD(0x03, 3, 3), + [SHIP_EN] = REG_FIELD(0x03, 0, 2), + /*reg04*/ + [REG_RST] = REG_FIELD(0x04, 7, 7), + [EN_LOWPOWER] = REG_FIELD(0x04, 6, 6), + [VDIFF_OPEN_TH] = REG_FIELD(0x04, 4, 5), + [AUTO_BSM_DIS] = REG_FIELD(0x04, 3, 3), + [AUTO_BSM_TH] = REG_FIELD(0x04, 2, 2), + [SHIP_WT] = REG_FIELD(0x04, 0, 0), + /*reg05*/ + [ITRICHG] = REG_FIELD(0x05, 5, 7), + [VPRE_CHG] = REG_FIELD(0x05, 0, 2), + /*reg06*/ + [IPRECHG] = REG_FIELD(0x06, 4, 7), + [VFC_CHG] = REG_FIELD(0x06, 0, 3), + /*reg07*/ + [CHG_OVP_DIS] = REG_FIELD(0x07, 7, 7), + [CHG_OVP] = REG_FIELD(0x07, 6, 6), + /*reg08*/ + [BAT_OVP_DIS] = REG_FIELD(0x08, 7, 7), + [BAT_OVP] = REG_FIELD(0x08, 2, 6), + /*reg09*/ + [CHG_OCP_DIS] = REG_FIELD(0x09, 7, 7), + [CHG_OCP] = REG_FIELD(0x09, 4, 6), + /*reg0A*/ + [DSG_OCP_DIS] = REG_FIELD(0x0A, 7, 7), + [DSG_OCP] = REG_FIELD(0x0A, 4, 6), + /*reg0B*/ + [TDIE_FLT_DIS] = REG_FIELD(0x0B, 7, 7), + [TDIE_FLT] = REG_FIELD(0x0B, 0, 3), + /*reg0C*/ + [TDIE_ALRM_DIS] = REG_FIELD(0x0C, 7, 7), + [TDIE_ALRM] = REG_FIELD(0x0C, 0, 3), + /*reg0D*/ + [CHG_OVP_DEG] = REG_FIELD(0x0D, 6, 7), + [BAT_OVP_DEG] = REG_FIELD(0x0D, 4, 5), + [CHG_OCP_DEG] = REG_FIELD(0x0D, 2, 3), + [DSG_OCP_DEG] = REG_FIELD(0x0D, 0, 1), + /*reg0E*/ + [AUTO_BSM_DEG] = REG_FIELD(0x0E, 6, 7), + /*reg0F*/ + [WORK_MODE] = REG_FIELD(0x0F, 5, 7), + /*reg15*/ + [ADC_EN] = REG_FIELD(0x15, 7, 7), + [ADC_RATE] = REG_FIELD(0x15, 6, 6), + [ADC_FREEZE] = REG_FIELD(0x15, 5, 5), + [ADC_DONE_MASK] = REG_FIELD(0x15, 0, 0), +}; + +static const struct regmap_config sc760x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +#ifndef MIN_VAL + #define MIN_VAL( x, y ) ( ((x) < (y)) ? (x) : (y) ) +#endif + +__attribute__((unused)) static int sc760x_set_ibat_dis(struct sc760x_chip *sc, bool en); +__attribute__((unused)) static int sc760x_set_load_switch(struct sc760x_chip *sc, bool en); +__attribute__((unused)) static int sc760x_set_lowpower_mode(struct sc760x_chip *sc, bool en); +__attribute__((unused)) static int sc760x_set_itrickle(struct sc760x_chip *sc, int curr); +__attribute__((unused)) static int sc760x_set_iprechg(struct sc760x_chip *sc, int curr); +__attribute__((unused)) static int sc760x_set_vfcchg(struct sc760x_chip *sc, int volt); +__attribute__((unused)) static int sc760x_set_batovp(struct sc760x_chip *sc, int volt); +__attribute__((unused)) static int sc760x_set_adc_enable(struct sc760x_chip *sc, bool en); +__attribute__((unused)) static void sc760x_mmi_charger_deinit(struct sc760x_mmi_charger *chg); +__attribute__((unused)) static int sc760x_set_power_limit_dis(struct sc760x_chip *sc, bool en); + +static int sc760x_mmi_charger_init(struct sc760x_mmi_charger *chg); +static int sc760x_get_state(struct sc760x_chip *sc, + struct sc760x_state *state); +static int sc760x_init_device(struct sc760x_chip *sc); +static struct power_supply_desc sc760x_power_supply_desc; + +/*********************************************************/ + +static int sc760x_enable_chip(struct sc760x_chip *sc, bool en) +{ + struct pinctrl *pinctrl; + struct pinctrl_state *state; + const char *pinctrl_name; + int ret; + + pinctrl = pinctrl_get(sc->dev); + if (IS_ERR_OR_NULL(pinctrl)) { + dev_err(sc->dev, "Failed to get pinctrl\n"); + return -EINVAL; + } + + if (en) + pinctrl_name = "sc760x_enable"; + else + pinctrl_name = "sc760x_disable"; + + + state = pinctrl_lookup_state(pinctrl, pinctrl_name); + if (IS_ERR_OR_NULL(state)) { + dev_err(sc->dev, "fail to get %s state\n", pinctrl_name); + ret = -ENODEV; + goto put_pinctrl; + } + + ret = pinctrl_select_state(pinctrl, state); + if (ret) { + dev_err(sc->dev, "fail to set %s state\n", pinctrl_name); + ret = -EINVAL; + goto put_pinctrl; + } + + if (en) + msleep(2000); + sc->sc760x_enable = en; + + if (sc->sc760x_enable) { + + if (!sc->irq_enabled) { + enable_irq_wake(sc->irq); + enable_irq(sc->irq); + sc->irq_enabled = true; + } + ret = sc760x_init_device(sc); + if (ret < 0) { + pr_info("init device failed(%d)\n", ret); + } + } else { + if (sc->irq_enabled) { + disable_irq_wake(sc->irq); + disable_irq(sc->irq); + sc->irq_enabled = false; + } + } + dev_err(sc->dev, "success to set %s state, sleep 2s\n", pinctrl_name); + + ret = 0; +put_pinctrl: + pinctrl_put(pinctrl); + + return ret; +} + +static int sc760x_field_read(struct sc760x_chip *sc, + enum sc760x_fields field_id, int *val) +{ + int ret; + if (!sc->sc760x_enable) + return -1; + + ret = regmap_field_read(sc->rmap_fields[field_id], val); + if (ret < 0) { + dev_err(sc->dev, "sc760x read field %d fail: %d\n", field_id, ret); + } + + return ret; +} + +static int sc760x_field_write(struct sc760x_chip *sc, + enum sc760x_fields field_id, int val) +{ + int ret; + if (!sc->sc760x_enable) + return -1; + + ret = regmap_field_write(sc->rmap_fields[field_id], val); + if (ret < 0) { + dev_err(sc->dev, "sc760x read field %d fail: %d\n", field_id, ret); + } + + return ret; +} + +static int sc760x_read_block(struct sc760x_chip *sc, + int reg, uint8_t *val, int len) +{ + int ret; + if (!sc->sc760x_enable) + return -1; + + ret = regmap_bulk_read(sc->regmap, reg, val, len); + if (ret < 0) { + dev_err(sc->dev, "sc760x read %02x block failed %d\n", reg, ret); + } + + return ret; +} + +/*******************************************************/ +static int sc760x_reg_reset(struct sc760x_chip *sc) +{ + return sc760x_field_write(sc, REG_RST, 1); +} + +static int sc760x_set_auto_bsm_dis(struct sc760x_chip *sc, bool en) +{ + return sc760x_field_write(sc, AUTO_BSM_DIS, !!en); +} + +static int sc760x_set_ibat_limit(struct sc760x_chip *sc, int curr) +{ + int user_val = INT_MAX; + + if (sc->user_ichg >= 0) { + user_val = sc->user_ichg; + } + + if (sc->user_ilim >= 0) { + user_val = MIN_VAL(user_val, sc->user_ilim); + } + + if (user_val != INT_MAX) { + curr = user_val/1000; + dev_info(sc->dev, "%s : User request overide ibat = %dmA\n", __func__, curr); + } + + dev_info(sc->dev, "%s : %dmA\n", __func__, curr); + + return sc760x_field_write(sc, IBAT_CHG_LIM, + (curr - IBAT_CHG_LIM_BASE) / IBAT_CHG_LIM_LSB); +} + +static int sc760x_set_ibat_dis(struct sc760x_chip *sc, bool en) +{ + dev_info(sc->dev, "%s : ibat_chrg %d\n", __func__, en); + return sc760x_field_write(sc, IBAT_CHG_LIM_DIS, !!en); +} + +static int sc760x_get_ibat_limit(struct sc760x_chip *sc, int *curr) +{ + int data = 0, ret = 0; + ret = sc760x_field_read(sc, IBAT_CHG_LIM, &data); + *curr = data *50 + 50; + return ret; +} + +static int sc760x_set_power_limit_dis(struct sc760x_chip *sc, bool en) +{ + return sc760x_field_write(sc, POW_LIM_DIS, !!en); +} + +static int sc760x_set_load_switch(struct sc760x_chip *sc, bool en) +{ + return sc760x_field_write(sc, LS_OFF, !!en); +} + +static int sc760x_get_load_switch(struct sc760x_chip *sc, int *ls_en) +{ + int ret = 0; + ret = sc760x_field_read(sc, LS_OFF, ls_en); + return ret; +} + +static int sc760x_set_lowpower_mode(struct sc760x_chip *sc, bool en) +{ + return sc760x_field_write(sc, EN_LOWPOWER, !!en); +} + +static int sc760x_set_itrickle(struct sc760x_chip *sc, int curr) +{ + dev_info(sc->dev, "%s : %dmA\n", __func__, curr); + + return sc760x_field_write(sc, ITRICHG, + (curr * 1000 - ITRICHG_BASE) / ITRICHG_LSB); +} + +static int sc760x_set_iprechg(struct sc760x_chip *sc, int curr) +{ + dev_info(sc->dev, "%s : %dmA\n", __func__, curr); + + return sc760x_field_write(sc, IPRECHG, + (curr - IPRECHG_BASE) / IPRECHG_LSB); +} + +static int sc760x_set_vfcchg(struct sc760x_chip *sc, int volt) +{ + dev_info(sc->dev, "%s : %dmV\n", __func__, volt); + + return sc760x_field_write(sc, VFC_CHG, + (volt - VFC_CHG_BASE) / VFC_CHG_LSB); +} + +static int sc760x_set_batovp(struct sc760x_chip *sc, int volt) +{ + dev_info(sc->dev, "%s : %dmV\n", __func__, volt); + + return sc760x_field_write(sc, BAT_OVP, + (volt - BAT_OVP_BASE) / BAT_OVP_LSB); +} + +static int sc760x_get_work_mode(struct sc760x_chip *sc, int *mode) +{ + return sc760x_field_read(sc, WORK_MODE, mode); +} + +static int sc760x_set_adc_enable(struct sc760x_chip *sc, bool en) +{ + dev_err(sc->dev, "%s set adc %d\n", __func__, en); + return sc760x_field_write(sc, ADC_EN, !!en); +} + +static int sc760x_set_adc_done_mask(struct sc760x_chip *sc, bool en) +{ + dev_info(sc->dev, "%s : set adc done mask %d\n", __func__, en); + return sc760x_field_write(sc, ADC_DONE_MASK, !!en); +} + +static int sc760x_get_adc(struct sc760x_chip *sc, + ADC_CH channel, int *result) +{ + int reg = 0x17 + channel * 2; + u8 val[2] = {0}; + int ret; + + ret = sc760x_read_block(sc, reg, val, 2); + if (ret) { + return ret; + } + + *result = (val[1] | (val[0] << 8)) * + sc760x_adc_m[channel] / sc760x_adc_l[channel]; + + return ret; +} + +static int sc760x_dump_reg(struct sc760x_chip *sc) +{ + int ret = 0, i = 0, val = 0, desc = 0; + char buf[1024]; + if (!sc->sc760x_enable) + return -1; + + for (i = 0; i <= 0x15; i++) { + ret = regmap_read(sc->regmap, i, &val); + if (!ret) { + desc += + sprintf(buf + desc, "[0x%02x]:0x%02x, ", i, val); + } + } + + dev_err(sc->dev, "Reg %s\n ", buf); + return ret; +} + +static irqreturn_t sc760x_irq_handler(int irq, void *data) +{ + struct sc760x_chip *sc = data; + + dev_dbg(sc->dev, "%s\n", __func__); + //sc760x_dump_reg(sc); + return IRQ_HANDLED; +} + +static ssize_t sc760x_show_registers(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sc760x_chip *sc = dev_get_drvdata(dev); + u8 addr; + int val; + u8 tmpbuf[300]; + int len; + int idx = 0; + int ret; + + idx = snprintf(buf, PAGE_SIZE, "%s:\n", "sc7603"); + for (addr = 0x0; addr <= 0x20; addr++) { + ret = regmap_read(sc->regmap, addr, &val); + if (ret == 0) { + len = snprintf(tmpbuf, PAGE_SIZE - idx, + "Reg[%.2X] = 0x%.2x\n", addr, val); + memcpy(&buf[idx], tmpbuf, len); + idx += len; + } + } + + return idx; +} + +static ssize_t sc760x_store_register(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sc760x_chip *sc = dev_get_drvdata(dev); + int ret; + unsigned int reg; + unsigned int val; + + ret = sscanf(buf, "%x %x", ®, &val); + if (ret == 2 && reg <= 0x20) + regmap_write(sc->regmap, (unsigned char)reg, (unsigned char)val); + + return count; +} + +static DEVICE_ATTR(registers, 0660, sc760x_show_registers, sc760x_store_register); + +static ssize_t sc760x_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned long enable; + struct sc760x_chip *sc = dev_get_drvdata(dev); + + + r = kstrtoul(buf, 0, &enable); + if (r) { + pr_err("Invalid tx_mode = %lu\n", enable); + return -EINVAL; + } + + if (enable < 0) { + sc->user_gpio_en = -EINVAL; + pr_info("Clear user gpio en setting\n"); + } else { + sc->user_gpio_en = !!enable; + pr_info("Set user gpio en: %d\n", sc->user_gpio_en); + sc760x_enable_chip(sc, enable); + sc->sc760x_enable = enable; + } + + return r ? r : count; +} + +static ssize_t sc760x_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sc760x_chip *sc = dev_get_drvdata(dev); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", sc->sc760x_enable); +} + +static DEVICE_ATTR(sc760x_enable, S_IRUGO|S_IWUSR, sc760x_enable_show, sc760x_enable_store); + +static ssize_t charger_suspend_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + int chg_suspend; + struct sc760x_chip *sc = dev_get_drvdata(dev); + + if (!sc) { + pr_err("sc chip not valid\n"); + return -ENODEV; + } + + ret = kstrtoint(buf, 0, &chg_suspend); + if (ret) { + pr_err("Invalid chg_suspend value, ret = %d\n", ret); + return ret; + } + + if (chg_suspend < 0) { + sc->user_chg_susp = -EINVAL; + pr_info("Clear user suspend charger setting\n"); + } else { + sc->user_chg_susp = !!chg_suspend; + pr_info("Set user suspend charger: %d\n", sc->user_chg_susp); + sc760x_set_load_switch(sc, sc->user_chg_susp); + } + cancel_delayed_work(&sc->charge_monitor_work); + schedule_delayed_work(&sc->charge_monitor_work, + msecs_to_jiffies(200)); + + return count; +} + +static ssize_t charger_suspend_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sc760x_chip *sc = dev_get_drvdata(dev); + + if (!sc) { + pr_err("sc chip not valid\n"); + return -ENODEV; + } + + return sprintf(buf, "%d\n", sc->user_chg_susp); +} +DEVICE_ATTR(charger_suspend, S_IRUGO|S_IWUSR, charger_suspend_show, charger_suspend_store); + +static bool sc760x_is_enabled_charging(struct sc760x_chip *sc) +{ + int ret = 0, ls_off = 0; + bool enabled = false; + + ret = sc760x_get_load_switch(sc, &ls_off); + if(ret) + return false; + + enabled = ls_off ? false : true; + return enabled; +} + +int sc760x_enable_charger(struct sc760x_chip *sc) +{ + int ret; + + if (sc->user_chg_en == 0) { + pr_info("Skip enable charging for user request override\n"); + return 0; + } + + if (sc->user_chg_susp > 0) { + pr_info("Skip enable charging for user chg suspend\n"); + return 0; + } + + ret = sc760x_set_load_switch(sc, false); + pr_info("sc760x_enable_charger\n"); + return ret; +} + +int sc760x_disable_charger(struct sc760x_chip *sc) +{ + int ret; + + if (sc->user_chg_en > 0) { + pr_info("Skip disable charging for user request override\n"); + return 0; + } + + ret = sc760x_set_load_switch(sc, true); + + pr_info("sc760x_disable_charger\n"); + return ret; +} + +static ssize_t chg_en_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + int enable; + struct sc760x_chip *sc = dev_get_drvdata(dev); + + if (!sc) { + pr_err("sc chip not valid\n"); + return -ENODEV; + } + + ret = kstrtoint(buf, 0, &enable); + if (ret) { + pr_err("Invalid chg_en value, ret = %d\n", ret); + return ret; + } + + if (enable < 0) { + sc->user_chg_en = -EINVAL; + pr_info("Clear user enable charging setting\n"); + } else { + sc->user_chg_en = !!enable; + pr_info("Set user enable charging: %d\n", sc->user_chg_en); + if (sc->user_chg_en) + sc760x_enable_charger(sc); + else + sc760x_disable_charger(sc); + } + cancel_delayed_work(&sc->charge_monitor_work); + schedule_delayed_work(&sc->charge_monitor_work, + msecs_to_jiffies(200)); + + return count; +} + +static ssize_t chg_en_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + bool enable; + struct sc760x_chip *sc = dev_get_drvdata(dev); + + if (!sc) { + pr_err("sc chip not valid\n"); + return -ENODEV; + } + + enable = sc760x_is_enabled_charging(sc); + + return sprintf(buf, "%d\n", enable); +} +DEVICE_ATTR(chg_en , S_IRUGO|S_IWUSR, chg_en_show, chg_en_store); + +static void sc760x_create_device_node(struct device *dev) +{ + device_create_file(dev, &dev_attr_registers); + device_create_file(dev, &dev_attr_sc760x_enable); + device_create_file(dev, &dev_attr_charger_suspend); + device_create_file(dev, &dev_attr_chg_en); +} + +static void sc760x_remove_device_node(struct device *dev) +{ + device_remove_file(dev, &dev_attr_registers); + device_remove_file(dev, &dev_attr_sc760x_enable); + device_remove_file(dev, &dev_attr_charger_suspend); + device_remove_file(dev, &dev_attr_chg_en); +} + +static int sc760x_set_thermal_mitigation(struct sc760x_chip *sc, int val) +{ + + if (!sc->num_thermal_levels) + return 0; + + if (sc->num_thermal_levels < 0) { + pr_err("Incorrect num_thermal_levels\n"); + return -EINVAL; + } + + if (val < 0 || val > sc->num_thermal_levels) { + pr_err("Invalid thermal level: %d\n", val); + return -EINVAL; + } + + sc->thermal_fcc_ua = sc->thermal_levels[val]; + sc->curr_thermal_level = val; + + pr_info("thermal level: %d, thermal fcc: %d\n", sc->curr_thermal_level, sc->thermal_fcc_ua); + + return 0; +} + + +static int get_charger_type(struct sc760x_chip * sc) +{ + enum power_supply_usb_type usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + + if (sc->use_ext_usb_psy && sc->usb_psy) { + int ret = 0; + union power_supply_propval val = {0}; + ret = power_supply_get_property(sc->usb_psy, + POWER_SUPPLY_PROP_USB_TYPE, &val); + if (!ret) { + usb_type = val.intval; + } + } + pr_debug("usb_type:%d\n", usb_type); + return usb_type; +} + +static enum power_supply_property sc760x_power_supply_props[] = { + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_PRESENT +}; + + +static enum power_supply_usb_type sc760x_usb_type[] = { + POWER_SUPPLY_USB_TYPE_UNKNOWN, + POWER_SUPPLY_USB_TYPE_SDP, /* Standard Downstream Port */ + POWER_SUPPLY_USB_TYPE_DCP, /* Dedicated Charging Port */ + POWER_SUPPLY_USB_TYPE_CDP, /* Charging Downstream Port */ + POWER_SUPPLY_USB_TYPE_ACA, /* Accessory Charger Adapters */ + POWER_SUPPLY_USB_TYPE_C, /* Type C Port */ + POWER_SUPPLY_USB_TYPE_PD, /* Power Delivery Port */ + POWER_SUPPLY_USB_TYPE_PD_DRP, /* PD Dual Role Port */ + POWER_SUPPLY_USB_TYPE_PD_PPS, /* PD Programmable Power Supply */ + POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID, /* Apple Charging Method */ +}; + +static char *sc760x_charger_supplied_to[] = { + "battery", +}; + +static int sc760x_property_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + switch (prop) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + return true; + default: + return false; + } +} + +static int sc760x_charger_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct sc760x_chip *sc = power_supply_get_drvdata(psy); + int ret = -EINVAL; + + switch (prop) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + if (val->intval < 0) { + sc->user_ilim = -EINVAL; + ret = 0; + pr_info("Clear user input limit\n"); + } else { + sc->user_ilim = val->intval; + ret = sc760x_set_ibat_limit(sc, val->intval / 1000); + pr_info("Set user input limit: %duA\n", sc->user_ilim); + } + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + if (val->intval < 0) { + sc->user_ichg = -EINVAL; + ret = 0; + pr_info("Clear user charging current limit\n"); + } else { + sc->user_ichg = val->intval; + ret = sc760x_set_ibat_limit(sc, val->intval / 1000); + pr_info("Set user charging current limit: %duA\n", sc->user_ichg); + } + break; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + ret = sc760x_set_thermal_mitigation(sc, val->intval); + break; + default: + return -EINVAL; + } + + return ret; +} + +static int sc760x_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct sc760x_chip *sc = power_supply_get_drvdata(psy); + struct sc760x_state state; + int chrg_status = 0; + int ret = 0; + + mutex_lock(&sc->lock); + ret = sc760x_get_state(sc, &state); + mutex_unlock(&sc->lock); + if (ret) + return ret; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + if (state.chrg_stat == CHARGING) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (state.chrg_stat == DISCHARGING) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else if (state.chrg_stat == NOT_CHARGING) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + sc760x_get_work_mode(sc, &chrg_status); + switch (chrg_status) { + case WORK_TRICKLE_CHARGE: + case WORK_PRE_CHARGE: + val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case WORK_FULLY_ON: + case WORK_CURRENT_REGULATION: + case WORK_POWER_REGULATION: + val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; + break; + case WORK_FULLY_OFF: + val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + default: + val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + } + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = SC760X_MANUFACTURER; + break; + + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = SC760X_NAME; + break; + + case POWER_SUPPLY_PROP_ONLINE: + val->intval = sc760x_is_enabled_charging(sc); + break; + + case POWER_SUPPLY_PROP_PRESENT: + val->intval = sc->sc760x_enable; + break; + + case POWER_SUPPLY_PROP_TYPE: + val->intval = sc760x_power_supply_desc.type; + break; + + case POWER_SUPPLY_PROP_USB_TYPE: + val->intval = get_charger_type(sc); + break; + + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = state.vbat_adc; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + val->intval = state.vchg_adc; + break; + + case POWER_SUPPLY_PROP_TEMP: + val->intval = state.tdie_adc; + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = state.ibat_adc; + break; + + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + val->intval = sc->curr_thermal_level; + break; + + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: + val->intval = sc->num_thermal_levels; + break; + + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = sc760x_get_ibat_limit(sc, &val->intval); + val->intval = val->intval * 1000; + if (ret < 0) + return ret; + ret = 0; + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = sc760x_get_ibat_limit(sc, &val->intval); + val->intval = val->intval * 1000; + if (ret < 0) + return ret; + ret = 0; + break; + default: + return -EINVAL; + } + + return ret; +} + +static struct power_supply_desc sc760x_power_supply_desc = { + .name = "charger", + .type = POWER_SUPPLY_TYPE_UNKNOWN, + .usb_types = sc760x_usb_type, + .num_usb_types = ARRAY_SIZE(sc760x_usb_type), + .properties = sc760x_power_supply_props, + .num_properties = ARRAY_SIZE(sc760x_power_supply_props), + .get_property = sc760x_charger_get_property, + .set_property = sc760x_charger_set_property, + .property_is_writeable = sc760x_property_is_writeable, +}; + +static int sc760x_power_supply_init(struct sc760x_chip *sc, + struct device *dev) +{ + struct power_supply_config psy_cfg = { .drv_data = sc, + .of_node = dev->of_node, }; + + psy_cfg.supplied_to = sc760x_charger_supplied_to; + psy_cfg.num_supplicants = ARRAY_SIZE(sc760x_charger_supplied_to); + + dev_err(dev, "regiser master power supply\n"); + sc->charger_psy = devm_power_supply_register(sc->dev, + &sc760x_power_supply_desc, + &psy_cfg); + + if (IS_ERR(sc->charger_psy)) + return PTR_ERR(sc->charger_psy); + + return 0; +} + +static int sc760x_parse_dt(struct sc760x_chip *sc, struct device *dev) +{ + int i, len, ret; + u32 prev, val; + const char *usb_psy_name = NULL; + const char *wls_psy_name = NULL; + + struct device_node *node = dev->of_node; + + struct { + char *name; + int *conv_data; + } props[] = { + {"sc,sc760x,bat-chg-lim-disable", &(sc->cfg->bat_chg_lim_disable)}, + {"sc,sc760x,bat-chg-lim", &(sc->cfg->bat_chg_lim)}, + {"sc,sc760x,pow-lim-disable", &(sc->cfg->pow_lim_disable)}, + {"sc,sc760x,ilim-disable", &(sc->cfg->ilim_disable)}, + {"sc,sc760x,load-switch-disable", &(sc->cfg->load_switch_disable)}, + {"sc,sc760x,low-power-mode-enable", &(sc->cfg->lp_mode_enable)}, + {"sc,sc760x,itrichg", &(sc->cfg->itrichg)}, + {"sc,sc760x,iprechg", &(sc->cfg->iprechg)}, + {"sc,sc760x,vfc-chg", &(sc->cfg->vfc_chg)}, + {"sc,sc760x,chg-ovp-disable", &(sc->cfg->chg_ovp_disable)}, + {"sc,sc760x,chg-ovp", &(sc->cfg->chg_ovp)}, + {"sc,sc760x,bat-ovp-disable", &(sc->cfg->bat_ovp_disable)}, + {"sc,sc760x,bat-ovp", &(sc->cfg->bat_ovp)}, + {"sc,sc760x,chg-ocp-disable", &(sc->cfg->chg_ocp_disable)}, + {"sc,sc760x,chg-ocp", &(sc->cfg->chg_ocp)}, + {"sc,sc760x,dsg-ocp-disable", &(sc->cfg->dsg_ocp_disable)}, + {"sc,sc760x,dsg-ocp", &(sc->cfg->dsg_ocp)}, + {"sc,sc760x,tdie-flt-disable", &(sc->cfg->tdie_flt_disable)}, + {"sc,sc760x,tdie-alm-disable", &(sc->cfg->tdie_alm_disable)}, + {"sc,sc760x,tdie-alm", &(sc->cfg->tdie_alm)}, + }; + + /* initialize data for optional properties */ + for (i = 0; i < ARRAY_SIZE(props); i++) { + ret = of_property_read_u32(node, props[i].name, + props[i].conv_data); + if (ret < 0) { + dev_err(sc->dev, "can not read %s \n", props[i].name); + continue; + } + } + + ret = device_property_read_string(sc->dev, + "mmi,ext-usb-psy-name", + &usb_psy_name); + if (!ret && usb_psy_name) + sc->use_ext_usb_psy = true; + else + sc->use_ext_usb_psy = false; + + ret = device_property_read_string(sc->dev, + "mmi,ext-wls-psy-name", + &wls_psy_name); + if (!ret && wls_psy_name) + sc->use_ext_wls_psy = true; + else + sc->use_ext_wls_psy = false; + + + sc->init_data.charger_disabled = of_property_read_bool(node, + "init-charger-disabled"); + + ret = device_property_read_u32(sc->dev, + "iterm-microamp", + &sc->init_data.iterm); + if (ret) + sc->init_data.iterm = SC760x_TERMCHRG_I_DEF_uA; + + ret = device_property_read_u32(sc->dev, + "ichg-max-microamp", + &sc->init_data.max_ichg); + if (ret) + sc->init_data.max_ichg = SC760x_ICHRG_I_MAX_uA; + + ret = device_property_read_u32(sc->dev, + "vchg-max-microvolt", + &sc->init_data.max_vreg); + if (ret) + sc->init_data.max_vreg = SC760x_VREG_V_MAX_uV; + + ret = device_property_read_u32(sc->dev, + "ichg-microamp", + &sc->init_data.ichg); + if (ret) + sc->init_data.ichg = SC760x_ICHRG_I_DEF_uA; + + ret = of_property_count_elems_of_size(node, "mmi,thermal-mitigation", + sizeof(u32)); + if (ret <= 0) { + return 0; + } + + len = ret; + prev = sc->init_data.max_ichg; + for (i = 0; i < len; i++) { + ret = of_property_read_u32_index(node, + "mmi,thermal-mitigation", + i, &val); + if (ret < 0) { + pr_err("failed to get thermal-mitigation[%d], ret=%d\n", i, ret); + return ret; + } + pr_info("thermal-mitigation[%d], val=%d, prev=%d\n", i, val, prev); + if (val > prev) { + pr_err("Thermal levels should be in descending order\n"); + sc->num_thermal_levels = -EINVAL; + return 0; + } + prev = val; + } + + sc->thermal_levels = devm_kcalloc(sc->dev, len + 1, + sizeof(*sc->thermal_levels), + GFP_KERNEL); + if (!sc->thermal_levels) + return -ENOMEM; + + sc->thermal_levels[0] = sc->init_data.max_ichg; + ret = of_property_read_u32_array(node, "mmi,thermal-mitigation", + &sc->thermal_levels[1], len); + if (ret < 0) { + pr_err("Error in reading mmi,thermal-mitigation, rc=%d\n", ret); + return ret; + } + sc->num_thermal_levels = len; + sc->thermal_fcc_ua = sc->init_data.max_ichg; + + ret = device_property_read_u32(sc->dev, + "mmi,thermal-ratio-nums", + &sc->thermal_ratio_nums); + if (ret) + sc->thermal_ratio_nums = 1; + + return 0; +} + +static int sc760x_init_device(struct sc760x_chip *sc) +{ + int ret = 0; + int i; + struct { + enum sc760x_fields field_id; + int conv_data; + } props[] = { + {IBAT_CHG_LIM_DIS, sc->cfg->bat_chg_lim_disable}, + {IBAT_CHG_LIM, sc->cfg->bat_chg_lim}, + {POW_LIM_DIS, sc->cfg->pow_lim_disable}, + {ILIM_DIS, sc->cfg->ilim_disable}, + {LS_OFF, sc->cfg->load_switch_disable}, + {EN_LOWPOWER, sc->cfg->lp_mode_enable}, + {ITRICHG, sc->cfg->itrichg}, + {IPRECHG, sc->cfg->iprechg}, + {VFC_CHG, sc->cfg->vfc_chg}, + {CHG_OVP_DIS, sc->cfg->chg_ovp_disable}, + {CHG_OVP, sc->cfg->chg_ovp}, + {BAT_OVP_DIS, sc->cfg->bat_ovp_disable}, + {BAT_OVP, sc->cfg->bat_ovp}, + {CHG_OCP_DIS, sc->cfg->chg_ocp_disable}, + {CHG_OCP, sc->cfg->chg_ocp}, + {DSG_OCP_DIS, sc->cfg->dsg_ocp_disable}, + {DSG_OCP, sc->cfg->dsg_ocp}, + {TDIE_FLT_DIS, sc->cfg->tdie_flt_disable}, + {TDIE_ALRM_DIS, sc->cfg->tdie_alm_disable}, + {TDIE_ALRM, sc->cfg->tdie_alm}, + }; + + ret = sc760x_reg_reset(sc); + if (ret < 0) { + dev_err(sc->dev, "%s Failed to reset registers(%d)\n", __func__, ret); + } + + for (i = 0; i < ARRAY_SIZE(props); i++) { + ret = sc760x_field_write(sc, props[i].field_id, props[i].conv_data); + } + + ret = sc760x_set_adc_enable(sc, true); + if (ret < 0) { + dev_err(sc->dev, "%s Failed to enable adc(%d)\n", __func__, ret); + } + + ret = sc760x_set_adc_done_mask(sc, true); + if (ret < 0) { + dev_err(sc->dev, "%s Failed to set adc mask (%d)\n", __func__, ret); + } + + return sc760x_dump_reg(sc); +} + +static int sc760x_register_interrupt(struct sc760x_chip *sc, struct i2c_client *client) +{ + int ret = 0; + + ret = devm_request_threaded_irq(sc->dev, client->irq, NULL, + sc760x_irq_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + sc760x_irq_name[sc->role], sc); + if (ret < 0) { + dev_err(sc->dev, "request thread irq failed:%d\n", ret); + return ret; + } + + sc->irq_enabled = false; + sc->irq = client->irq; + dev_err(sc->dev, "request thread irq success\n"); + return 0; +} + +static int is_wls_online(struct sc760x_chip *sc) +{ + int rc; + union power_supply_propval val; + + if (!sc->use_ext_wls_psy || !sc->wls_psy) + return 0; + + rc = power_supply_get_property(sc->wls_psy, + POWER_SUPPLY_PROP_ONLINE, &val); + if (rc < 0) { + pr_err("Error wls online rc = %d\n", rc); + return 0; + } + + pr_debug("wireless online is %d", val.intval); + return val.intval; +} + +static int is_usb_online(struct sc760x_chip *sc) +{ + int rc; + union power_supply_propval val; + + if (!sc->use_ext_usb_psy || !sc->usb_psy) + return 0; + + rc = power_supply_get_property(sc->usb_psy, + POWER_SUPPLY_PROP_ONLINE, &val); + if (rc < 0) { + pr_err("Error usb online rc = %d\n", rc); + return 0; + } + + pr_debug("usb online is %d", val.intval); + return val.intval; +} + +static int sc760x_get_state(struct sc760x_chip *sc, + struct sc760x_state *state) +{ + int ret = 0, chrg_status = 0; + union power_supply_propval val = {0}; + + if (sc->use_ext_usb_psy && sc->usb_psy) { + + ret = power_supply_get_property(sc->usb_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); + if (!ret) + state->vbus_adc = val.intval; + ret = power_supply_get_property(sc->usb_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &val); + if (!ret) + state->ibus_adc = val.intval; + ret = power_supply_get_property(sc->usb_psy, + POWER_SUPPLY_PROP_ONLINE, &val); + if (!ret) { + state->usb_online = val.intval; + state->online = val.intval; + } + ret = power_supply_get_property(sc->usb_psy, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, &val); + if (!ret) + state->ibus_limit = val.intval; + + ret = power_supply_get_property(sc->usb_psy, + POWER_SUPPLY_PROP_USB_TYPE, &val); + if (!ret) + state->chrg_type = val.intval; + } else if (sc->use_ext_usb_psy) { + state->chrg_stat = DISCHARGING; + state->online = 0; + state->ibus_limit = 0; + state->chrg_type = 0; + } + + if (!state->online && is_wls_online(sc)) { + state->online = 1; + state->chrg_type = 3; + state->chrg_stat = CHARGING; + } + + sc760x_get_adc(sc, ADC_IBAT, &state->ibat_adc); + sc760x_get_work_mode(sc, &chrg_status); + + if (state->ibat_adc > 10 && chrg_status) + state->chrg_stat = CHARGING; + else if (state->online) + state->chrg_stat = NOT_CHARGING; + else + state->chrg_stat = DISCHARGING; + + sc760x_get_adc(sc, ADC_IBAT, &state->ibat_adc); + sc760x_get_adc(sc, ADC_VBAT, &state->vbat_adc); + sc760x_get_adc(sc, ADC_VCHG, &state->vchg_adc); + sc760x_get_adc(sc, ADC_TBAT, &state->tbat_adc); + sc760x_get_adc(sc, ADC_TDIE, &state->tdie_adc); + state->ibat_adc = state->ibat_adc * 1000; + state->vbat_adc = state->vbat_adc * 1000; + state->vchg_adc = state->vchg_adc * 1000; + return 0; +} + +static bool sc760x_state_changed(struct sc760x_chip *sc, + struct sc760x_state *new_state) +{ + struct sc760x_state old_state; + + mutex_lock(&sc->lock); + old_state = sc->state; + mutex_unlock(&sc->lock); + + return (old_state.chrg_stat != new_state->chrg_stat || + old_state.online != new_state->online || + old_state.chrg_type != new_state->chrg_type || + old_state.ibus_limit != new_state->ibus_limit + ); +} + +static void charger_monitor_work_func(struct work_struct *work) +{ + bool state_changed = false; + struct sc760x_state state; + struct sc760x_chip *sc = container_of(work, + struct sc760x_chip, + charge_monitor_work.work); + + sc760x_get_state(sc, &state); + state_changed = sc760x_state_changed(sc, &state); + mutex_lock(&sc->lock); + sc->state = state; + mutex_unlock(&sc->lock); + + if (state_changed) { + if (!sc->state.chrg_type) + pr_info("No charger is present\n"); + sc760x_dump_reg(sc); + schedule_delayed_work(&sc->charge_detect_delayed_work, 0); + power_supply_changed(sc->charger_psy); + } + + schedule_delayed_work(&sc->charge_monitor_work, 10*HZ); +} + +static void charger_detect_work_func(struct work_struct *work) +{ + int ret; + bool state_changed = false; + struct sc760x_state state; + struct sc760x_chip *sc = container_of(work, + struct sc760x_chip, + charge_detect_delayed_work.work); + + if (!sc->charger_wakelock->active) + __pm_stay_awake(sc->charger_wakelock); + + if (sc->use_ext_usb_psy) { + if (!sc->usb_psy) { + const char *usb_psy_name = NULL; + ret = device_property_read_string(sc->dev, + "mmi,ext-usb-psy-name", + &usb_psy_name); + if (!ret && usb_psy_name) + sc->usb_psy = power_supply_get_by_name(usb_psy_name); + if (!sc->usb_psy) { + pr_info("No USB power supply found, redetecting...\n"); + cancel_delayed_work(&sc->charge_detect_delayed_work); + schedule_delayed_work(&sc->charge_detect_delayed_work, + msecs_to_jiffies(1000)); + goto err; + } else { + pr_info("USB power supply is found\n"); + } + } + } + + if (sc->use_ext_wls_psy) { + if (!sc->wls_psy) { + const char *wls_psy_name = NULL; + ret = device_property_read_string(sc->dev, + "mmi,ext-wls-psy-name", + &wls_psy_name); + if (!ret && wls_psy_name) + sc->wls_psy = power_supply_get_by_name(wls_psy_name); + if (!sc->wls_psy) { + pr_info("No Wireless power supply found, redetecting...\n"); + cancel_delayed_work(&sc->charge_detect_delayed_work); + schedule_delayed_work(&sc->charge_detect_delayed_work, + msecs_to_jiffies(1000)); + goto err; + } else { + pr_info("WLS power supply is found\n"); + } + } + } + + if (sc->mmi_charger && + (!sc->mmi_charger->fg_psy || !sc->mmi_charger->driver)) { + sc760x_mmi_charger_init(sc->mmi_charger); + } + + ret = sc760x_get_state(sc, &state); + state_changed = sc760x_state_changed(sc, &state); + mutex_lock(&sc->lock); + sc->state = state; + mutex_unlock(&sc->lock); + + if(!state.online) + { + dev_err(sc->dev, "Vbus not online\n"); + goto err; + } + + if (state_changed) { + sc760x_dump_reg(sc); + power_supply_changed(sc->charger_psy); + } + return; +err: + //release wakelock + if (state_changed) + power_supply_changed(sc->charger_psy); + dev_info(sc->dev, "Relax wakelock\n"); + __pm_relax(sc->charger_wakelock); + return; +} + + +static int sc760x_psy_notifier_call(struct notifier_block *nb, + unsigned long val, void *v) +{ + struct sc760x_chip *sc = container_of(nb, + struct sc760x_chip, psy_nb); + struct power_supply *psy = v; + struct sc760x_mmi_charger *chg = NULL; + + if (!sc) { + pr_err("called before sc760x valid!\n"); + return NOTIFY_DONE; + } + + pr_err("psy notifier call , val %d\n", (int)val); + if (!psy || val != PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + chg = sc->mmi_charger; + if ((sc->usb_psy && !strcmp(psy->desc->name, sc->usb_psy->desc->name)) || + (chg && chg->fg_psy_name && !strcmp(psy->desc->name, chg->fg_psy_name))) { + cancel_delayed_work(&sc->charge_detect_delayed_work); + schedule_delayed_work(&sc->charge_detect_delayed_work, + msecs_to_jiffies(0)); + } + + return NOTIFY_OK; +} + +#define EMPTY_BATTERY_VBAT 3200 +static int sc760x_charger_get_batt_info(void *data, struct mmi_battery_info *batt_info) +{ + int rc; + struct sc760x_mmi_charger *chg = data; + union power_supply_propval val = {0}; + + rc = power_supply_get_property(chg->fg_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); + if (!rc) + chg->batt_info.batt_mv = val.intval / 1000; + rc = power_supply_get_property(chg->fg_psy, POWER_SUPPLY_PROP_CURRENT_NOW, &val); + if (!rc) + chg->batt_info.batt_ma = val.intval / 1000 * chg->ichg_polority; + rc = power_supply_get_property(chg->fg_psy, POWER_SUPPLY_PROP_CAPACITY, &val); + if (!rc) + chg->batt_info.batt_soc = val.intval; + rc = power_supply_get_property(chg->fg_psy, POWER_SUPPLY_PROP_TEMP, &val); + if (!rc) + chg->batt_info.batt_temp = val.intval / 10; + rc = power_supply_get_property(chg->fg_psy, POWER_SUPPLY_PROP_STATUS, &val); + if (!rc) + chg->batt_info.batt_status = val.intval; + rc = power_supply_get_property(chg->fg_psy, POWER_SUPPLY_PROP_CHARGE_FULL, &val); + if (!rc) + chg->batt_info.batt_full_uah = val.intval; + rc = power_supply_get_property(chg->fg_psy, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, &val); + if (!rc) + chg->batt_info.batt_design_uah = val.intval; + rc = power_supply_get_property(chg->fg_psy, POWER_SUPPLY_PROP_CHARGE_COUNTER, &val); + if (!rc) + chg->batt_info.batt_chg_counter = val.intval; + + if (chg->chg_cfg.full_charged) + chg->batt_info.batt_status = POWER_SUPPLY_STATUS_FULL; + + if (!chg->sc->state.online) { + chg->batt_info.batt_status = POWER_SUPPLY_STATUS_DISCHARGING; + } + + memcpy(batt_info, &chg->batt_info, sizeof(struct mmi_battery_info)); + if (chg->paired_batt_info.batt_soc == 0 && + chg->paired_batt_info.batt_mv < EMPTY_BATTERY_VBAT) { + batt_info->batt_soc = 0; + pr_warn("Force %s to empty from %d for empty paired battery\n", + chg->fg_psy->desc->name, + chg->batt_info.batt_soc); + } + + return rc; +} + +static int sc760x_charger_get_chg_info(void *data, struct mmi_charger_info *chg_info) +{ + int work_mode = 0; + struct sc760x_mmi_charger *chg = data; + struct sc760x_state state = chg->sc->state; + + sc760x_get_state(chg->sc, &state); + sc760x_get_work_mode(chg->sc, &work_mode); + chg->chg_info.chrg_mv = state.vbus_adc / 1000; + chg->chg_info.chrg_ma = state.ibus_adc / 1000; + chg->chg_info.chrg_type = get_charger_type(chg->sc); + chg->chg_info.vbus_present = state.online; + if (chg->chg_info.chrg_type == POWER_SUPPLY_USB_TYPE_UNKNOWN && + is_wls_online(chg->sc)) { + chg->chg_info.chrg_type = 0xFF; + chg->chg_info.vbus_present = false; + } + + chg->chg_info.chrg_present = state.online; + chg->chg_info.chrg_pmax_mw = 0; + + memcpy(chg_info, &chg->chg_info, sizeof(struct mmi_charger_info)); + + pr_info("sc760x_chg_info : gpio_en %d, workmode %d, chrg_stat =%d, usb_online = %d, wls_online %d, vbus = %d, ibus = %d, ibus_limit = %d, ibat = %d, vbat = %d, vchg = %d, tbat = %d\n", + chg->sc->sc760x_enable, work_mode, state.chrg_stat, state.usb_online, state.wls_online, state.vbus_adc, state.ibus_adc, state.ibus_limit, state.ibat_adc, state.vbat_adc, state.vchg_adc, + state.tbat_adc); + sc760x_dump_reg(chg->sc); + return 0; +} + +static int sc760x_charger_config_charge(void *data, struct mmi_charger_cfg *config) +{ + int rc = 0, sc760x_ibat_limit_set = 0, ibat_limit_vote = 0; + u32 value; + bool chg_en; + bool cfg_changed = false; + + struct sc760x_mmi_charger *chg = data; + struct sc760x_state state = chg->sc->state; + + sc760x_get_state(chg->sc, &state); + /* Monitor vbat and ibat for OVP and OCP */ +/* + if (chg->batt_info.batt_mv >= chg->sc->vbat_ovp_threshold) { + pr_err("ERROR: vbat OVP is triggered, batt_mv=%dmV, thre=%d\n", + chg->batt_info.batt_mv, + chg->sc->vbat_ovp_threshold); + } +*/ + + rc = sc760x_get_ibat_limit(chg->sc, &sc760x_ibat_limit_set); + if (rc < 0) + sc760x_ibat_limit_set = ibat_limit_vote; + + if (!config->charging_disable && + !config->charger_suspend && + config->target_fcc != chg->chg_cfg.target_fcc) { + cfg_changed = true; + chg->chg_cfg.target_fcc = config->target_fcc; + } + + /* configure the charger if changed */ + if (config->target_fv != chg->chg_cfg.target_fv) { + cfg_changed = true; + chg->chg_cfg.target_fv = config->target_fv; + } + + if (chg->chg_cfg.target_fv > 0 && (chg->batt_info.batt_mv > chg->chg_cfg.target_fv)) { + sc760x_ibat_limit_set -= IBAT_CHG_LIM_BASE; + sc760x_set_ibat_limit(chg->sc, sc760x_ibat_limit_set); + pr_info("update new sc760x_ibat_limit_set %d, state.vbat_adc %d, batt_mv %d > chg->chg_cfg.target_fv %d\n", + sc760x_ibat_limit_set, state.vbat_adc / 1000, chg->batt_info.batt_mv, chg->chg_cfg.target_fv); + } else { + + ibat_limit_vote = MIN_VAL(chg->chg_cfg.target_fcc, chg->sc->thermal_fcc_ua / 1000); + +#ifdef THERMAL_RATIO_CONTROL + /* The thermal_radio step is 0.5, this is equivalent to (batt_fcc_ma / (0.5 * thermal_ratio_nums)) */ + chg->sc->thermal_fcc_ua = (chg->paired_batt_info.batt_fcc_ma * 2 / chg->sc->thermal_ratio_nums) * 1000; + ibat_limit_vote = MIN_VAL(chg->chg_cfg.target_fcc, chg->sc->thermal_fcc_ua / 1000); +#endif + + if (sc760x_ibat_limit_set > (ibat_limit_vote + IBAT_CHG_LIM_BASE)) { + sc760x_ibat_limit_set -= IBAT_CHG_LIM_BASE; + sc760x_set_ibat_limit(chg->sc, sc760x_ibat_limit_set); + pr_info("Devide to decrease ichg, update new sc760x_ibat_limit_set %d\n", + sc760x_ibat_limit_set); + } else if (sc760x_ibat_limit_set < (ibat_limit_vote - IBAT_CHG_LIM_BASE)) { + sc760x_ibat_limit_set += IBAT_CHG_LIM_BASE; + sc760x_set_ibat_limit(chg->sc, sc760x_ibat_limit_set); + pr_info("Devide to increase ichg, update new sc760x_ibat_limit_set %d\n", + sc760x_ibat_limit_set); + } + } + + if (config->charger_suspend != chg->chg_cfg.charger_suspend) { + rc = sc760x_set_load_switch(chg->sc, config->charger_suspend); + if (!rc) { + cfg_changed = true; + chg->chg_cfg.charger_suspend = config->charger_suspend; + } + } + + chg_en = sc760x_is_enabled_charging(chg->sc); + if (chg->sc->user_chg_en >= 0 && chg_en != !!chg->sc->user_chg_en) { + if (!!chg->sc->user_chg_en) + rc = sc760x_enable_charger(chg->sc); + else + rc = sc760x_disable_charger(chg->sc); + if (!rc) { + cfg_changed = true; + chg_en = !!chg->sc->user_chg_en; + } + } + + if (config->charging_disable != chg->chg_cfg.charging_disable || + (chg_en == config->charging_disable && chg->sc->user_chg_en < 0)) { + value = config->charging_disable; + if (!value) + rc = sc760x_enable_charger(chg->sc); + else + rc = sc760x_disable_charger(chg->sc); + if (!rc) { + cfg_changed = true; + chg->chg_cfg.charging_disable = config->charging_disable; + chg_en = !value; + } + } + + if (config->taper_kickoff != chg->chg_cfg.taper_kickoff) { + chg->chg_cfg.taper_kickoff = config->taper_kickoff; + chg->chrg_taper_cnt = 0; + } + + if (config->full_charged != chg->chg_cfg.full_charged) { + chg->chg_cfg.full_charged = config->full_charged; + } + + if (!config->charging_disable && + !config->charger_suspend && + config->chrg_iterm != chg->chg_cfg.chrg_iterm) { + value = config->chrg_iterm * 1000; + if (!rc) { + cfg_changed = true; + chg->chg_cfg.chrg_iterm = config->chrg_iterm; + } + } + + if (!config->charging_disable && + !config->charger_suspend && + config->fg_iterm != chg->chg_cfg.fg_iterm) { + if (chg->fg_psy) { + union power_supply_propval val = {0}; + val.intval = config->fg_iterm * 1000; + rc = power_supply_set_property(chg->fg_psy, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, + &val); + if (!rc) { + cfg_changed = true; + chg->chg_cfg.fg_iterm = config->fg_iterm; + } + } else { + chg->chg_cfg.fg_iterm = config->fg_iterm; + } + } + + if (!config->charging_disable && + !config->charger_suspend && + config->charging_reset != chg->chg_cfg.charging_reset) { + if (config->charging_reset) { + chg->chg_cfg.target_fv = -EINVAL; + chg->chg_cfg.target_fcc = -EINVAL; + chg->chg_cfg.charging_disable = true; + pr_info("Battery charging reset triggered\n"); + } + cfg_changed = true; + chg->chg_cfg.charging_reset = config->charging_reset; + } + + if (cfg_changed) { + cancel_delayed_work(&chg->sc->charge_monitor_work); + schedule_delayed_work(&chg->sc->charge_monitor_work, + msecs_to_jiffies(200)); + } + + if (!is_usb_online(chg->sc) && + !is_wls_online(chg->sc)) { + + rc = sc760x_set_auto_bsm_dis(chg->sc, false); + if (rc < 0) { + pr_err("Failed to enable audo bsm(%d)\n", rc); + } + } else { + + rc = sc760x_set_auto_bsm_dis(chg->sc, true); + if (rc < 0) { + pr_err("Failed to disable audo bsm(%d)\n", rc); + } + } + + pr_info("chg_en:%d, online %d, chg_st:%d\n", + chg_en, state.online, state.chrg_stat); + sc760x_get_ibat_limit(chg->sc, &sc760x_ibat_limit_set); + pr_info("sc760x_ibat_limit_set %d, ibat_limit_vote %d, target_fcc %d, target_fv %d, thermal_fcc_ua %d\n", + sc760x_ibat_limit_set, ibat_limit_vote, chg->chg_cfg.target_fcc, chg->chg_cfg.target_fv, chg->sc->thermal_fcc_ua); + return 0; +} + +#define TAPER_COUNT 3 +static bool sc760x_charger_is_charge_tapered(void *data, int tapered_ma) +{ + bool is_tapered = false; + struct sc760x_mmi_charger *chg = data; + + if (abs(chg->batt_info.batt_ma) <= tapered_ma) { + if (chg->chrg_taper_cnt >= TAPER_COUNT) { + is_tapered = true; + chg->chrg_taper_cnt = 0; + } else + chg->chrg_taper_cnt++; + } else + chg->chrg_taper_cnt = 0; + + return is_tapered; +} + +static bool sc760x_charger_is_charge_halt(void *data) +{ +// int rc = 0; +// int chrg_stat = 0; + bool chrg_halt = false; + struct sc760x_mmi_charger *chg = data; + + if (chg->batt_info.batt_status == POWER_SUPPLY_STATUS_NOT_CHARGING || + chg->batt_info.batt_status == POWER_SUPPLY_STATUS_FULL) + chrg_halt = true; + + return chrg_halt; +} + +static void sc760x_charger_set_constraint(void *data, + struct mmi_charger_constraint *constraint) +{ + struct sc760x_mmi_charger *chg = data; + + if (constraint->demo_mode != chg->constraint.demo_mode) { + chg->constraint.demo_mode = constraint->demo_mode; + } + + if (constraint->factory_version != chg->constraint.factory_version) { + chg->constraint.factory_version = constraint->factory_version; + } + + if (constraint->factory_mode != chg->constraint.factory_mode) { + chg->constraint.factory_mode = constraint->factory_mode; + } + + if (constraint->dcp_pmax != chg->constraint.dcp_pmax) { + chg->constraint.dcp_pmax = constraint->dcp_pmax; + } + + if (constraint->hvdcp_pmax != chg->constraint.hvdcp_pmax) { + chg->constraint.hvdcp_pmax = constraint->hvdcp_pmax; + } + + if (constraint->pd_pmax != chg->constraint.pd_pmax) { + chg->constraint.pd_pmax = constraint->pd_pmax; + } + + if (constraint->wls_pmax != chg->constraint.wls_pmax) { + chg->constraint.wls_pmax = constraint->wls_pmax; + } +} + +#define DUAL_VBATT_DELTA_MV 200 +static void sc760x_paired_battery_notify(void *data, + struct mmi_battery_info *batt_info) +{ + int partner_fg_vbatt = 0, fg_vbatt = 0; + struct sc760x_mmi_charger *chg = data; + + if (!chg) { + pr_err("sc mmi_charger not valid\n"); + return; + } + + if (!chg->sc) { + pr_err("sc chip not valid\n"); + return; + } + + memcpy(&chg->paired_batt_info, batt_info, sizeof(struct mmi_battery_info)); + if (chg->sc->sc760x_enable) { + pr_info("sc760x has been power on\n"); + return; + } + + pr_info("parnter battery : batt_mv %d, batt_ma %d, batt_soc %d," + " batt_temp %d, batt_status %d, batt_sn %s, batt_fv_mv %d," + " batt_fcc_ma %d\n", + batt_info->batt_mv, + batt_info->batt_ma, + batt_info->batt_soc, + batt_info->batt_temp, + batt_info->batt_status, + batt_info->batt_sn, + batt_info->batt_fv_mv, + batt_info->batt_fcc_ma); + + partner_fg_vbatt = batt_info->batt_mv; + fg_vbatt = chg->batt_info.batt_mv; + + pr_info("sc760x checking, partner_fg_vbatt %d, fg_vbatt %d\n", partner_fg_vbatt, fg_vbatt); + if (!is_usb_online(chg->sc) && + !is_wls_online(chg->sc) && + (DUAL_VBATT_DELTA_MV < (partner_fg_vbatt - fg_vbatt))) { + pr_err("partner_fg_vbatt > fg_vbatt, and delta value > %d, Does not turn on sc760x\n", DUAL_VBATT_DELTA_MV); + return; + } else { + if (chg->sc->user_gpio_en < 0) + sc760x_enable_chip(chg->sc, true); + return; + } + + return; +} + +static int sc760x_mmi_charger_init(struct sc760x_mmi_charger *chg) +{ + int rc; + const char *df_sn = NULL; + struct mmi_charger_driver *driver; + + if (!chg->fg_psy && !chg->fg_psy_name) { + chg->fg_psy_name = "fg_battery"; + device_property_read_string(chg->sc->dev, + "mmi,fg-psy-name", + &chg->fg_psy_name); + } + + if (!chg->fg_psy) { + chg->fg_psy = power_supply_get_by_name(chg->fg_psy_name); + if (!chg->fg_psy) { + pr_err("No %s power supply found\n", chg->fg_psy_name); + return -ENODEV; + } + pr_info("%s power supply is found\n", chg->fg_psy_name); + } + + if (chg->driver) { + pr_info("sc760x_mmi_charger has already registered\n"); + return 0; + } + + rc = device_property_read_string(chg->sc->dev, + "mmi,df-serialnum", + &df_sn); + if (!rc && df_sn) { + pr_info("Default Serial Number %s\n", df_sn); + } else { + pr_err("No Default Serial Number defined\n"); + df_sn = "unknown-sn"; + } + strcpy(chg->batt_info.batt_sn, df_sn); + chg->paired_batt_info.batt_soc = -EINVAL; + chg->chg_cfg.target_fcc = chg->sc->init_data.ichg / 1000; + chg->chg_cfg.charging_disable = chg->sc->init_data.charger_disabled; + chg->chg_cfg.charger_suspend = chg->sc->init_data.charger_disabled; + + if (of_property_read_bool(chg->sc->dev->of_node, "mmi,ichg-invert-polority")) + chg->ichg_polority = -1; + else + chg->ichg_polority = 1; + pr_info("ichg_polority %d\n", chg->ichg_polority); + + driver = devm_kzalloc(chg->sc->dev, + sizeof(struct mmi_charger_driver), + GFP_KERNEL); + if (!driver) + return -ENOMEM; + + /* init driver */ + driver->name = SC760X_NAME; + driver->dev = chg->sc->dev; + driver->data = chg; + driver->get_batt_info = sc760x_charger_get_batt_info; + driver->get_chg_info = sc760x_charger_get_chg_info; + driver->config_charge = sc760x_charger_config_charge; + driver->is_charge_tapered = sc760x_charger_is_charge_tapered; + driver->is_charge_halt = sc760x_charger_is_charge_halt; + driver->set_constraint = sc760x_charger_set_constraint; + driver->notify_paired_battery = sc760x_paired_battery_notify; + chg->driver = driver; + + /* register driver to mmi charger */ + rc = mmi_register_charger_driver(driver); + if (rc) { + pr_err("sc760x_mmi_charger init failed, rc=%d\n", rc); + } else { + pr_info("sc760x_mmi_charger init successfully\n"); + } + + return rc; +} + +static void sc760x_mmi_charger_deinit(struct sc760x_mmi_charger *chg) +{ + int rc; + + if (!chg->driver) { + pr_info("sc760x_mmi_charger has not inited yet\n"); + return; + } + + /* unregister driver from mmi charger */ + rc = mmi_unregister_charger_driver(chg->driver); + if (rc) { + pr_err("sc760x_mmi_charger deinit failed, rc=%d\n", rc); + } else { + devm_kfree(chg->sc->dev, chg->driver); + chg->driver = NULL; + } + + if (chg->fg_psy) { + power_supply_put(chg->fg_psy); + chg->fg_psy = NULL; + } +} + +static struct of_device_id sc760x_charger_match_table[] = { + { .compatible = "southchip,sc7603_master", + .data = &sc760x_role_data[SC760X_MASTER], }, + { .compatible = "southchip,sc7603_slave", + .data = &sc760x_role_data[SC760X_SLAVE], }, + { }, +}; +MODULE_DEVICE_TABLE(of, sc760x_charger_match_table); + +static int sc760x_charger_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct sc760x_chip *sc; + struct device *dev = &client->dev; + const struct of_device_id *match; + struct device_node *node = client->dev.of_node; + char *name = NULL; + int ret = 0, i =0; + + pr_err("%s (%s)\n", __func__, SC760X_DRV_VERSION); + + sc = devm_kzalloc(&client->dev, sizeof(struct sc760x_chip), GFP_KERNEL); + if (!sc) + return -ENOMEM; + + sc->dev = dev; + sc->client = client; + mutex_init(&sc->lock); + + sc->user_ilim = -EINVAL; + sc->user_ichg = -EINVAL; + sc->user_chg_en = -EINVAL; + sc->user_chg_susp = -EINVAL; + sc->user_gpio_en = -EINVAL; + sc->sc760x_enable = 0; + + + sc->regmap = devm_regmap_init_i2c(client, + &sc760x_regmap_config); + if (IS_ERR(sc->regmap)) { + dev_err(sc->dev, "Failed to initialize regmap\n"); + return -EINVAL; + } + + for (i = 0; i < ARRAY_SIZE(sc760x_reg_fields); i++) { + const struct reg_field *reg_fields = sc760x_reg_fields; + + sc->rmap_fields[i] = + devm_regmap_field_alloc(sc->dev, + sc->regmap, + reg_fields[i]); + if (IS_ERR(sc->rmap_fields[i])) { + dev_err(sc->dev, "cannot allocate regmap field\n"); + return PTR_ERR(sc->rmap_fields[i]); + } + } + + i2c_set_clientdata(client, sc); + sc760x_create_device_node(&(client->dev)); + + match = of_match_node(sc760x_charger_match_table, node); + if (match == NULL) { + dev_err(sc->dev, "device tree match not found!\n"); + goto err_get_match; + } + + sc->role = *(int *)match->data; + + dev_err(sc->dev, "sc760x[%s] probe running!!!\n", + sc->role == SC760X_MASTER ? "master" : "slave"); + + sc->cfg = &default_cfg; + ret = sc760x_parse_dt(sc, &client->dev); + if (ret < 0) { + dev_err(sc->dev, "%s parse dt failed(%d)\n", __func__, ret); + goto err_parse_dt; + } + + ret = sc760x_register_interrupt(sc, client); + if (ret < 0) { + dev_err(sc->dev, "%s register irq fail(%d)\n", + __func__, ret); + goto err_register_irq; + } + + name = devm_kasprintf(sc->dev, GFP_KERNEL, "%s", + "sc760x suspend wakelock"); + sc->charger_wakelock = + wakeup_source_register(NULL, name); + + ret = sc760x_power_supply_init(sc, dev); + if (ret) { + dev_err(dev, "Failed to register power supply, ret=%d\n", ret); + goto err_init_device; + } + + INIT_DELAYED_WORK(&sc->charge_detect_delayed_work, charger_detect_work_func); + INIT_DELAYED_WORK(&sc->charge_monitor_work, charger_monitor_work_func); + sc->psy_nb.notifier_call = sc760x_psy_notifier_call; + ret = power_supply_reg_notifier(&sc->psy_nb); + if (ret) + pr_err("Failed to reg power supply notifier: %d\n", ret); + + sc->mmi_charger = devm_kzalloc(dev, sizeof(*sc->mmi_charger), GFP_KERNEL); + if (sc->mmi_charger) { + sc->mmi_charger->sc = sc; + sc760x_mmi_charger_init(sc->mmi_charger); + + schedule_delayed_work(&sc->charge_detect_delayed_work, + msecs_to_jiffies(0)); + } + + dev_err(sc->dev, "sc760x[%s] probe successfully!!!\n", + sc->role == SC760X_MASTER ? "master" : "slave"); + return 0; + +err_register_irq: +err_init_device: +err_parse_dt: +err_get_match: + dev_err(sc->dev, "sc760x probe failed!\n"); + devm_kfree(sc->dev, sc); + return ret; +} + +#if (KERNEL_VERSION(6, 1, 25) > LINUX_VERSION_CODE) +static int sc760x_charger_remove(struct i2c_client *client) +#else +static void sc760x_charger_remove(struct i2c_client *client) +#endif +{ + struct sc760x_chip *sc = i2c_get_clientdata(client); + sc760x_remove_device_node(&(client->dev)); + + cancel_delayed_work_sync(&sc->charge_monitor_work); + + if (sc->mmi_charger) + sc760x_mmi_charger_deinit(sc->mmi_charger); + + power_supply_unreg_notifier(&sc->psy_nb); + power_supply_unregister(sc->charger_psy); + + mutex_destroy(&sc->lock); + +#if (KERNEL_VERSION(6, 1, 25) > LINUX_VERSION_CODE) + return 0; +#endif +} + +#ifdef CONFIG_PM_SLEEP +static int sc760x_suspend(struct device *dev) +{ + struct sc760x_chip *sc = dev_get_drvdata(dev); + int ret = 0; + dev_info(sc->dev, "Suspend successfully!"); + if (device_may_wakeup(dev)) + enable_irq_wake(sc->irq); + disable_irq(sc->irq); + ret = sc760x_set_adc_enable(sc, false); + if (ret < 0) { + dev_err(sc->dev, "%s Failed to disable adc(%d)\n", __func__, ret); + } + + ret = sc760x_set_lowpower_mode(sc, true); + if (ret < 0) { + dev_err(sc->dev, "%s Failed to enter lowpower(%d)\n", __func__, ret); + } + + return 0; +} +static int sc760x_resume(struct device *dev) +{ + struct sc760x_chip *sc = dev_get_drvdata(dev); + int ret = 0; + dev_info(sc->dev, "Resume successfully!"); + if (device_may_wakeup(dev)) + disable_irq_wake(sc->irq); + enable_irq(sc->irq); + + ret = sc760x_set_lowpower_mode(sc, false); + if (ret < 0) { + dev_err(sc->dev, "%s Failed to exit lowpower(%d)\n", __func__, ret); + } + + ret = sc760x_set_adc_enable(sc, true); + if (ret < 0) { + dev_err(sc->dev, "%s Failed to enable adc(%d)\n", __func__, ret); + } + + return 0; +} + +static const struct dev_pm_ops sc760x_pm = { + SET_SYSTEM_SLEEP_PM_OPS(sc760x_suspend, sc760x_resume) +}; +#endif + +static void sc760x_charger_shutdown(struct i2c_client *client) +{ + int ret = 0; + struct sc760x_chip *sc = i2c_get_clientdata(client); + ret = sc760x_set_adc_enable(sc, false); + if (ret < 0) { + dev_err(sc->dev, "%s Failed to enable adc(%d)\n", __func__, ret); + } + + if (ret) { + pr_err("Failed to disable charger, ret = %d\n", ret); + } + pr_info("sc760x_charger_shutdown\n"); +} + +static const struct i2c_device_id sc760x_i2c_ids[] = { + { "sc7603_master", SC760X_MASTER }, + { "sc7603_slave", SC760X_SLAVE }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, sc760x_i2c_ids); + +static struct i2c_driver sc760x_charger_driver = { + .driver = { + .name = "sc760x-charger", + .owner = THIS_MODULE, + .of_match_table = sc760x_charger_match_table, +#ifdef CONFIG_PM_SLEEP + .pm = &sc760x_pm, +#endif + }, + .probe = sc760x_charger_probe, + .remove = sc760x_charger_remove, + .shutdown = sc760x_charger_shutdown, + .id_table = sc760x_i2c_ids, +}; + +module_i2c_driver(sc760x_charger_driver); + +MODULE_DESCRIPTION("SC SC760X Driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/power/sc760x_charger_mmi/sc760x_charger_mmi.h b/drivers/power/sc760x_charger_mmi/sc760x_charger_mmi.h new file mode 100644 index 000000000000..dc13dd7902ee --- /dev/null +++ b/drivers/power/sc760x_charger_mmi/sc760x_charger_mmi.h @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2022 Motorola Mobility, LLC. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. 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. + * 3. Neither the name of the copyright holder 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 HOLDER 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. + */ + +#ifndef _SC760X_CHARGER_H +#define _SC760X_CHARGER_H + +#include +#include "mmi_charger.h" + +#define SC760X_MANUFACTURER "South Chip" +#define SC760X_NAME "sc7603" + +#define SC760X_DRV_VERSION "1.0.0_G" + +enum { + SC760X_MASTER = 0, + SC760X_SLAVE, +}; + +#define IBAT_CHG_LIM_BASE 50 +#define IBAT_CHG_LIM_LSB 50 + +#define ITRICHG_BASE 12500//uA +#define ITRICHG_LSB 12500//uA + +#define IPRECHG_BASE 50 +#define IPRECHG_LSB 50 + +#define VFC_CHG_BASE 2800 +#define VFC_CHG_LSB 50 + +#define BAT_OVP_BASE 4000 +#define BAT_OVP_LSB 50 + +#define SC760x_TERMCHRG_I_DEF_uA 180000 +#define SC760x_ICHRG_I_MAX_uA 3780000 +#define SC760x_ICHRG_I_DEF_uA 2000000 +#define SC760x_VREG_V_MAX_uV 4600000 + +typedef enum { + ADC_IBAT, + ADC_VBAT, + ADC_VCHG, + ADC_TBAT, + ADC_TDIE, + ADC_MAX_NUM, +}ADC_CH; + +//work mode +typedef enum { + WORK_FULLY_OFF = 0, + WORK_TRICKLE_CHARGE, + WORK_PRE_CHARGE, + WORK_FULLY_ON, + WORK_CURRENT_REGULATION, + WORK_POWER_REGULATION, +}WORK_MODE_T; + +//work mode +typedef enum { + DISCHARGING = 0, + CHARGING, + NOT_CHARGING, +}CHG_STAT_T; + +enum sc760x_fields { + DEVICE_REV, DEVICE_ID, + IBAT_CHG_LIM, + POW_LIM_DIS, VBALANCE_DIS, POW_LIM, + ILIM_DIS, IBAT_CHG_LIM_DIS, BAT_DET_DIS, VDIFF_CHECK_DIS, LS_OFF, SHIP_EN, + REG_RST, EN_LOWPOWER, VDIFF_OPEN_TH, AUTO_BSM_DIS, AUTO_BSM_TH, SHIP_WT, + ITRICHG, VPRE_CHG, + IPRECHG, VFC_CHG, + CHG_OVP_DIS, CHG_OVP, + BAT_OVP_DIS, BAT_OVP, + CHG_OCP_DIS, CHG_OCP, + DSG_OCP_DIS, DSG_OCP, + TDIE_FLT_DIS, TDIE_FLT, + TDIE_ALRM_DIS, TDIE_ALRM, + CHG_OVP_DEG, BAT_OVP_DEG, CHG_OCP_DEG, DSG_OCP_DEG, + AUTO_BSM_DEG, + WORK_MODE, BAT_ABSENT_STAT, VDUFF_STAT, + ADC_EN, ADC_RATE, ADC_FREEZE, ADC_DONE_MASK, + F_MAX_FIELDS, +}; + +struct sc760x_cfg_e { + int bat_chg_lim_disable; + int bat_chg_lim; + int pow_lim_disable; + int ilim_disable; + int load_switch_disable; + int lp_mode_enable; + int itrichg; + int iprechg; + int vfc_chg; + int chg_ovp_disable; + int chg_ovp; + int bat_ovp_disable; + int bat_ovp; + int chg_ocp_disable; + int chg_ocp; + int dsg_ocp_disable; + int dsg_ocp; + int tdie_flt_disable; + int tdie_alm_disable; + int tdie_alm; +}; + +struct sc760x_init_data { + u32 ichg; /* charge current */ + u32 iterm; /* termination current */ + u32 max_ichg; + u32 max_vreg; + u32 vrechg; + bool charger_disabled; +}; + +struct sc760x_state { + bool therm_limit; + bool online; + bool usb_online; + bool wls_online; + + CHG_STAT_T chrg_stat; + int chrg_type; + int ibat_limit; + int ibus_limit; + + int vbus_adc; + int ibus_adc; + int ibat_adc; + int vbat_adc; + int vchg_adc; + int tbat_adc; + int tdie_adc; + WORK_MODE_T work_mode; +}; + +struct sc760x_mmi_charger { + struct sc760x_chip *sc; + struct mmi_battery_info batt_info; + struct mmi_charger_info chg_info; + struct mmi_charger_cfg chg_cfg; + struct mmi_charger_constraint constraint; + struct mmi_charger_driver *driver; + u32 chrg_taper_cnt; + const char *fg_psy_name; + struct power_supply *fg_psy; + int ichg_polority; + + struct mmi_battery_info paired_batt_info; +}; + +struct sc760x_chip { + struct device *dev; + struct i2c_client *client; + struct regmap *regmap; + struct regmap_field *rmap_fields[F_MAX_FIELDS]; + struct mutex lock; + struct mutex i2c_rw_lock; + struct dentry *debug_root; + struct power_supply *wls_psy; + struct power_supply *usb_psy; + struct power_supply *charger_psy; + struct wakeup_source *charger_wakelock; + + struct sc760x_cfg_e *cfg; + struct sc760x_init_data init_data; + struct sc760x_state state; + int user_ichg; /* user charge current */ + int user_ilim; /* user input current */ + int user_chg_en; /* user charging enable */ + int user_chg_susp; /* user charger suspend */ + int user_gpio_en; /* user gpio en */ + + int irq; + bool irq_enabled; + int role; + int sc760x_enable; + u32 *thermal_levels; + u32 thermal_fcc_ua; + int curr_thermal_level; + int num_thermal_levels; + int thermal_ratio_nums; + + bool use_ext_usb_psy; + bool use_ext_wls_psy; + struct delayed_work charge_detect_delayed_work; + struct delayed_work charge_monitor_work; + struct notifier_block psy_nb; + struct sc760x_mmi_charger *mmi_charger; +}; + +#endif /* _SC760X_CHARGER_H */ diff --git a/drivers/power/sd77426_fg_mmi/Android.mk b/drivers/power/sd77426_fg_mmi/Android.mk new file mode 100644 index 000000000000..b115b9f10768 --- /dev/null +++ b/drivers/power/sd77426_fg_mmi/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := sd77426_fg_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/sd77426_fg_mmi/Kbuild b/drivers/power/sd77426_fg_mmi/Kbuild new file mode 100644 index 000000000000..f2f6f48cae74 --- /dev/null +++ b/drivers/power/sd77426_fg_mmi/Kbuild @@ -0,0 +1,11 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/drivers/power/sd77426_fg_mmi + +sd77426_fg_mmi-objs += oz8806_battery.o +sd77426_fg_mmi-objs += parameter.o +sd77426_fg_mmi-objs += table.o +sd77426_fg_mmi-objs += bmulib.o +sd77426_fg_mmi-objs += calculate.o +obj-m += sd77426_fg_mmi.o diff --git a/drivers/power/sd77426_fg_mmi/Makefile b/drivers/power/sd77426_fg_mmi/Makefile new file mode 100644 index 000000000000..62a13fd28500 --- /dev/null +++ b/drivers/power/sd77426_fg_mmi/Makefile @@ -0,0 +1,11 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + diff --git a/drivers/power/sd77426_fg_mmi/battery_config.h b/drivers/power/sd77426_fg_mmi/battery_config.h new file mode 100644 index 000000000000..08691e1b05c0 --- /dev/null +++ b/drivers/power/sd77426_fg_mmi/battery_config.h @@ -0,0 +1,42 @@ +/***************************************************************************** +* Copyright(c) BMT, 2021. All rights reserved +* +* BMT [oz8806] Source Code Reference Design +* File:[bmulib.c] +* +* This Source Code Reference Design for BMT [oz8806] access +* ("Reference Design") is solely for the use of PRODUCT INTEGRATION REFERENCE ONLY, +* and contains confidential and privileged information of BMT International +* Limited. BMT shall have no liability to any PARTY FOR THE RELIABILITY, +* SERVICEABILITY FOR THE RESULT OF PRODUCT INTEGRATION, or results from: (i) any +* modification or attempted modification of the Reference Design by any party, or +* (ii) the combination, operation or use of the Reference Design with non-BMT +* Reference Design. Use of the Reference Design is at user's discretion to qualify +* the final work result. +*****************************************************************************/ + +#ifndef __O2_BATTERY_CONFIG_H +#define __O2_BATTERY_CONFIG_H __FILE__ + + +#define OZ88106_I2C_NUM 2 +//#define EXT_THERMAL_READ + +#define OZ8806_VOLTAGE 4350 +#define O2_CONFIG_CAPACITY 5000 +#define O2_CONFIG_RSENSE 10000//Expanded 1000 times +#define O2_CONFIG_EOC 200 +#define OZ8806_EOD 3400 +#define O2_CONFIG_BOARD_OFFSET 12 +#define O2_SOC_START_THRESHOLD_VOL 3700 +#define O2_CONFIG_RECHARGE 100 +#define O2_TEMP_PULL_UP_R 100000 //100K +#define O2_TEMP_REF_VOLTAGE 1800 //mv +#define BATTERY_WORK_INTERVAL 10 + +#define RPULL (68000) +#define RDOWN (5100) + +#define ENABLE_10MIN_END_CHARGE_FUN + +#endif diff --git a/drivers/power/sd77426_fg_mmi/bmulib.c b/drivers/power/sd77426_fg_mmi/bmulib.c new file mode 100644 index 000000000000..c9b47ccd48ec --- /dev/null +++ b/drivers/power/sd77426_fg_mmi/bmulib.c @@ -0,0 +1,3236 @@ +/***************************************************************************** +* Copyright(c) BMT, 2021. All rights reserved. +* +* BMT [oz8806] Source Code Reference Design +* File:[bmulib.c] +* +* This Source Code Reference Design for BMT [oz8806] access +* ("Reference Design") is solely for the use of PRODUCT INTEGRATION REFERENCE ONLY, +* and contains confidential and privileged information of BMT International +* Limited. BMT shall have no liability to any PARTY FOR THE RELIABILITY, +* SERVICEABILITY FOR THE RESULT OF PRODUCT INTEGRATION, or results from: (i) any +* modification or attempted modification of the Reference Design by any party, or +* (ii) the combination, operation or use of the Reference Design with non-BMT +* Reference Design. Use of the Reference Design is at user's discretion to qualify +* the final work result. +*****************************************************************************/ + +/**************************************************************************************** + +****************************************************************************************/ +#include +#include +#include +#include +#include +#include + +#include "parameter.h" +#include "oz8806_regdef.h" +#include "table.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/***************************************************************************** +* Define section +* add all #define here +*****************************************************************************/ + +//#define FCC_UPDATA_CHARGE + +#define VERSION "2021.08.12/7.00.06 disable otp and expand rsense" +#define charge_step parameter->charge_pursue_step +#define discharge_step parameter->discharge_pursue_step +#define discharge_th parameter->discharge_pursue_th +#define config_data parameter->config + +#define RETRY_CNT 8 +#define FORCE_OTP_MAPPING 0 + +#define SHUTDOWN_HI 50 +#define SHUTDOWN_TH1 100 +#define SHUTDOWN_TH2 300 + +#define CHARGE_END_CURRENT2 (config_data->charge_end_current +2) + +#define FCC_UPPER_LIMIT 100 +//#define FCC_LOWER_LIMIT 80 +#define FCC_LOWER_LIMIT 70 //for lianxiang + +#define charge_full_voltage (config_data->charge_cv_voltage - 26) +//#define full_charge_data (gas_gauge->fcc_data + config_data->design_capacity / 100 - 1) +#define full_charge_data (gas_gauge->fcc_data + gas_gauge->fcc_data / 100 - 1) +//#define full_charge_data gas_gauge->fcc_data + 2 + +#define MAX_EOD_TIME (60 * 9) //H7 add timer full +#define MAX_SUSPEND_CURRENT (-45) //H7 add wakup CAR range check, max suspend current +#define MAX_SUSPEND_CONSUME (MAX_SUSPEND_CURRENT*10/36) //H7 add wakup CAR range check, max suspend consumption(1000*mah) +#define MAX_SUSPEND_CHARGE (1800*10/36)//H7 add wakup CAR range check, max suspend charge(1000*mah) + +#define ABS(a, b) ((a>b)?(a-b):(b-a)) + + +/***************************************************************************** +* static variables section +****************************************************************************/ +static unsigned long bmu_kernel_memaddr; +bmu_data_t *batt_info; +gas_gauge_t *gas_gauge; +parameter_data_t parameter_customer; +parameter_data_t *parameter; + +int *rc_table; +int *xaxis_table; +int *yaxis_table; +int *zaxis_table; + +int32_t calculate_mah = 0; +int32_t calculate_soc = 0; +static int car_error = 0; + +static char * BATT_CAPACITY = NULL;//"/data/sCaMAH.dat"; +static char * BATT_FCC = NULL;//"/data/fcc.dat"; +static char * OCV_FLAG = NULL;//"/data/ocv_flag.dat"; +static char * BATT_OFFSET = NULL;//"/data/offset.dat"; + +static uint8_t oz8806_pec_check = 0; +static uint8_t oz8806_cell_num = 1; +static int32_t res_divider_ratio = 2000; +static uint8_t charge_end_flag = 0; + +int32_t one_percent_rc = 0; +//static uint8_t wait_dc_charger= 0; +//static uint8_t wait_voltage_end = 0; + +static uint8_t wait_ocv_flag = 0; +//static uint8_t wait_ocv_times = 0; + +//uint8_t enable_fast_catch_oinom = 0; +//static uint8_t oz8806_in_suspend = 0; + +//static uint8_t start_chg_count_flag = 0; +uint8_t battery_ri = 100; + +//static uint8_t charge_end = 0; +//static uint8_t charge_fcc_update = 0; +//static uint8_t discharge_fcc_update = 0; + +static uint8_t power_on_flag = 0; +//static uint8_t write_offset = 0; +//static uint8_t check_offset_flag = 0; + +static uint8_t bmu_sleep_flag = 0; +static int32_t fRSOC_PRE; + +static uint8_t discharge_end_flag = 0; +static uint8_t sleep_ocv_flag = 0; + +//static struct rtc_time rtc_time; +static uint32_t previous_loop_timex = 0; + +static uint8_t set_soc_from_ext = 0; +static int32_t soc_external = -1; +static uint8_t file_not_ok_cap = 30; +static uint8_t fix_car_init = 0; +static uint8_t bmu_sys_rw_kernel = 1; +static int8_t power_on_retry_times = 4; + +static uint8_t retry_times = 0; +static uint8_t calculate_times = 0; +static uint8_t times = 0; +static uint32_t time_sec = 0; +uint32_t accmulate_dsg_data = 0; +uint8_t check_chip_function = 1; +/****************************************************************************** +* Function prototype section +* add prototypes for all functions called by this file,execepting those +* declared in header file +*****************************************************************************/ + +static void oz8806_over_flow_prevent(void); +//static void check_pec_control(void); + +static int32_t afe_register_read_byte(uint8_t index, uint8_t *dat); +static int32_t afe_register_write_byte(uint8_t index, uint8_t dat); +static int32_t afe_register_read_word(uint8_t index, uint16_t *dat); +//static int32_t afe_register_write_word(uint8_t index, uint16_t dat); + +//static int32_t afe_read_cell_volt(int32_t *voltage); +static int32_t afe_read_ocv_volt(int32_t *voltage); +//static int32_t afe_read_current(int32_t *dat); +static int32_t afe_read_car(int32_t *dat); +static int32_t afe_write_car(int32_t dat); +static int32_t afe_read_cell_temp(int32_t *date); +static int32_t afe_read_board_offset(int32_t *dat); +static int32_t afe_write_board_offset(int32_t date); + +static int32_t oz8806_read_byte( uint8_t index); +static int32_t oz8806_write_byte( uint8_t index, uint8_t data); +static int32_t oz8806_read_word(uint8_t index); +static int32_t oz8806_write_word( uint8_t index, uint16_t data); +static int32_t oz8806_cell_voltage_read(int32_t *voltage); +static int32_t oz8806_ocv_voltage_read(int32_t *voltage); +//static int32_t oz8806_temp_read(int32_t *voltage); +static int32_t oz8806_current_read(int32_t *data); +static int32_t oz8806_car_read(int32_t *car); +static int32_t oz8806_car_write(int32_t data); +static uint8_t pec_calculate (uint8_t ucCrc, uint8_t ucData); +//static int32_t oz8806_write_byte_pec(uint8_t index, uint8_t data); +//static int bmu_check_file(char * address); +static int bmu_write_data(char * address,int data); +static int bmu_read_data(char * address); +//static int bmu_write_string(char * address,char * data); +static void bmu_wait_ready(void); +//static int32_t i2c_read_byte(uint8_t addr,uint8_t index,uint8_t *data); +//static int32_t i2c_write_byte(uint8_t addr,uint8_t index,uint8_t data); +static void check_oz8806_staus(void); + +//static void discharge_end_process(void); +//static void charge_end_process(void); +static void check_board_offset(void); +static void check_shutdwon_voltage(void); + +static void trim_bmu_VD23(void); +//static int32_t oz8806_write_byte_pec(uint8_t addr,uint8_t index, uint8_t data); + +void bmu_init_parameter_more(parameter_data_t *paramter_temp) +{ + if (paramter_temp->BATT_CAPACITY) + BATT_CAPACITY = paramter_temp->BATT_CAPACITY; + + if (paramter_temp->BATT_FCC) + BATT_FCC = paramter_temp->BATT_FCC; + + if (paramter_temp->OCV_FLAG) + OCV_FLAG = paramter_temp->OCV_FLAG; + + if (paramter_temp->BATT_OFFSET) + BATT_OFFSET = paramter_temp->BATT_OFFSET; + + if (paramter_temp->oz8806_cell_num) + oz8806_cell_num = paramter_temp->oz8806_cell_num; + + if (paramter_temp->res_divider_ratio) + res_divider_ratio = paramter_temp->res_divider_ratio; + + if (paramter_temp->file_not_ok_cap) + file_not_ok_cap = paramter_temp->file_not_ok_cap; + + if (paramter_temp->set_soc_from_ext) + set_soc_from_ext = paramter_temp->set_soc_from_ext; + + if (paramter_temp->fix_car_init) + fix_car_init = paramter_temp->fix_car_init; + + //if (paramter_temp->power_on_retry_times) + power_on_retry_times = paramter_temp->power_on_retry_times; + + if (!paramter_temp->bmu_sys_rw_kernel) + bmu_sys_rw_kernel = paramter_temp->bmu_sys_rw_kernel; + + bmt_dbg("oz8806_cell_num:%d, res_divider_ratio: %d,file_not_ok_cap:%d, set_soc_from_ext: %d, fix_car_init:%d,power_on_retry_times:%d, bmu_sys_rw_kernel:%d\n", + oz8806_cell_num, res_divider_ratio,file_not_ok_cap, set_soc_from_ext, fix_car_init,power_on_retry_times, bmu_sys_rw_kernel); +} +/***************************************************************************** + * Description: + * bmu init chip + * Parameters: + * used to init bmu + * Return: + * None + *****************************************************************************/ + void bmu_init_chip(parameter_data_t *paramter_temp) +{ + + int32_t ret; + //uint16_t value; + uint8_t i; + int32_t data; + uint8_t * addr; + config_data_t * config_test; + uint32_t byte_num = num_0; + + byte_num = sizeof(config_data_t) + sizeof(bmu_data_t) + sizeof(gas_gauge_t); + + memset((uint8_t *)bmu_kernel_memaddr,num_0,byte_num); + + parameter = paramter_temp; + addr = (uint8_t *)(parameter->config); + for(byte_num = num_0;byte_num < sizeof(config_data_t);byte_num++) + { + *((uint8_t *)bmu_kernel_memaddr + byte_num) = *addr++; + } + + config_test = (config_data_t *)bmu_kernel_memaddr; + + batt_info = (bmu_data_t *)((uint8_t *)bmu_kernel_memaddr + byte_num); + + //-------------------------------------------------------------------------------------------------- + batt_info->Battery_ok = 1; + batt_info->i2c_error_times = num_0; + + byte_num += sizeof(bmu_data_t); + + gas_gauge = (gas_gauge_t *)((uint8_t *)bmu_kernel_memaddr + byte_num); + byte_num += sizeof(gas_gauge_t); + //-------------------------------------------------------------------------------------------------- + gas_gauge->bmu_init_ok = 0; + gas_gauge->charge_end = num_0; + gas_gauge->charge_end_current_th2 = CHARGE_END_CURRENT2; + gas_gauge->charge_strategy = 1; + gas_gauge->charge_max_ratio = 3500; + //gas_gauge->charge_max_ratio = 10000; // for lianxiang + + gas_gauge->discharge_end = num_0; + gas_gauge->discharge_current_th = DISCH_CURRENT_TH; + gas_gauge->discharge_strategy = 1; + gas_gauge->discharge_max_ratio = 8000; // for lianxiang + gas_gauge->dsg_end_voltage_hi = SHUTDOWN_HI; + + gas_gauge->batt_ri = 120; // charging. It is related to voltage when full charging condition. + gas_gauge->line_impedance = 0; //charging + gas_gauge->max_chg_reserve_percentage = 1000; + gas_gauge->fix_chg_reserve_percentage = 0; + gas_gauge->fast_charge_step = 2; + gas_gauge->start_fast_charge_ratio = 1500; + gas_gauge->charge_method_select = 0; + gas_gauge->max_charge_current_fix = 2000; + + //add this for oinom, start + gas_gauge->lower_capacity_reserve = 0; + gas_gauge->lower_capacity_soc_start =30; + + gas_gauge->percent_10_reserve = 0; + //add this for oinom, end + + //this for test gas gaugue + if(parameter->config->debug) + gas_gauge->dsg_end_voltage_hi = 0; + gas_gauge->dsg_end_voltage_th1 = SHUTDOWN_TH1; + gas_gauge->dsg_end_voltage_th2 = SHUTDOWN_TH2; + gas_gauge->dsg_count_2 = num_0; + + gas_gauge->overflow_data = num_32768 * num_5 * 1000 / config_data->fRsense; + batt_dbg("yyyy gas_gauge->overflow_data is %d\n",gas_gauge->overflow_data); + + bmu_init_gg(gas_gauge); + + //-------------------------------------------------------------------------------------------------- + + bmu_init_table(&xaxis_table, &yaxis_table, &zaxis_table, &rc_table); + + batt_dbg("byte_num is %d\n",byte_num); + power_on_flag = num_0; + bmu_sleep_flag = num_0; + one_percent_rc = config_data->design_capacity / num_100; + + batt_dbg("AAAA BMT fgu DRIVER VERSION is %s\n",VERSION); + + batt_dbg("BMT fgu test parameter:Rsense:%d, RTempPullup:%d, VTempRef:%d, CarLSB:%d, CurLSB:%d, VolLSB:%d, FCC:%d, CV:%d, EOC:%d, EOD:%d, BoardOffset:%d, Debug:%d\n", + config_test->fRsense, + config_test->temp_pull_up, + config_test->temp_ref_voltage, + config_test->dbCARLSB, + config_test->dbCurrLSB, + config_test->fVoltLSB, + config_test->design_capacity, + config_test->charge_cv_voltage, + config_test->charge_end_current, + config_test->discharge_end_voltage, + config_test->board_offset, + config_test->debug + + ); + + //check_pec_control(); + //move check_oz8806_status here to avoid iic-commu-issue + check_oz8806_staus(); + trim_bmu_VD23(); + /* + * board offset current is measured and reg0x18 value is updated only + * after reset(power on reset or software reset), + */ + if (oz8806_get_boot_up_time() < 625) + { + batt_dbg("AAAA wait for current offset detection\n"); + msleep(625 - oz8806_get_boot_up_time()); + afe_register_read_word(OZ8806_OP_BOARD_OFFSET,(uint16_t *)&data); + } + + batt_dbg("AAAAAAAAAAAAA Register BOARD_OFFSET: 0x%02x\n",data); + + ret = afe_read_board_offset(&data); + batt_dbg("AAAA board_offset is %d mA\n",data); + + + if(ret >= num_0) + { + + if(config_data->board_offset != num_0) + { + afe_write_board_offset(config_data->board_offset); + } + else if((data > num_10) || (data <= num_0)) + afe_write_board_offset(num_7); + } + + //wake up OZ8806 into FullPower mode + + batt_dbg("BMT fgu cell_num is %d\n",oz8806_cell_num); + + if(oz8806_cell_num > num_1) + afe_register_write_byte(OZ8806_OP_CTRL,num_0x2c); + else + afe_register_write_byte(OZ8806_OP_CTRL,num_0x20); + + ret = afe_register_read_byte(OZ8806_OP_CTRL,&i); + batt_dbg("first read reg[0x09]= 0x%02x, ret = %d\n", i, ret); + + if((i & num_0x40) || (ret < num_0)) + { + if(oz8806_cell_num > num_1) + { + afe_register_write_byte(OZ8806_OP_CTRL,num_0x2c); + } + else + afe_register_write_byte(OZ8806_OP_CTRL,0x20); + batt_dbg("Wake up BMT fgu write op_ctrl 0x20 \n"); + + ret = afe_register_read_byte(OZ8806_OP_CTRL,&i); + batt_dbg("second read reg[0x09]= 0x%02x, ret = %d\n", i, ret); + } + + batt_dbg("BMT fgu parameter %d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,\n", + config_data->fRsense, + config_data->temp_pull_up, + config_data->temp_ref_voltage, + config_data->dbCARLSB, + config_data->dbCurrLSB, + config_data->fVoltLSB, + config_data->design_capacity, + config_data->charge_cv_voltage, + config_data->charge_end_current, + config_data->discharge_end_voltage, + config_data->board_offset, + config_data->debug + ); + + //check_oz8806_staus(); + + //trim_bmu_VD23(); +} + + +/***************************************************************************** + * Description: + * wait ocv flag + * Parameters: + * None + * Return: + * None + *****************************************************************************/ + void wait_ocv_flag_fun(void) +{ + + int32_t ret; + uint16_t value; + uint8_t i; + int32_t data; +#if 1 + if (oz8806_get_boot_up_time() < 1500)// ocv can be stable after oz8806 is wakeuped for 1.375 s + { + batt_dbg("wait ocv flag register ready 1\n"); + msleep(1500 - oz8806_get_boot_up_time()); + oz8806_get_boot_up_time(); + //return; + } +/* + if (oz8806_get_power_on_time() < 2250) + { + batt_dbg("wait ocv flag register ready 2\n"); + msleep(2250 - oz8806_get_power_on_time()); + oz8806_get_power_on_time(); + } +*/ +#endif + + wait_ocv_flag = 1; + + for(i = 0;i < 3;i++) + { + ret = afe_register_read_word(OZ8806_OP_OCV_LOW,&value); + if(ret >= 0) + { + if ((value & POWER_OCV_FLAG) || (value & SLEEP_OCV_FLAG)) + { + if (oz8806_get_power_on_time() < 2250)//2250+2000 + { + batt_dbg("ocv or sleep_ocv: wait for normal scan cycle\n"); + oz8806_get_power_on_time(); + } + } + else if (oz8806_get_boot_up_time() < 2250) + { + batt_dbg("normal ok: wait for normal scan cycle\n"); + } + break; + } + } + + //read data + afe_read_cell_volt(&batt_info->fVolt); + afe_read_current(&batt_info->fCurr); + afe_read_cell_temp(&batt_info->fCellTemp); + afe_read_car(&batt_info->fRC); + + batt_info->fRSOC = batt_info->fRC * num_100; + batt_info->fRSOC = batt_info->fRSOC / config_data->design_capacity ; + + batt_dbg("read batt_info.fVolt is %d\n",(batt_info->fVolt * oz8806_cell_num)); + batt_dbg("read batt_info.fRC is %d\n",batt_info->fRC); + batt_dbg("read batt_info.fCurr is %d\n",batt_info->fCurr); + batt_dbg("read ocv flag ret is 0x%02x,value is 0x%02x\n",ret,value); + batt_dbg("read fCellTemp is %d\n",batt_info->fCellTemp); + if (ret >= num_0 ) + { + // OZ8806 First power on + if (value & POWER_OCV_FLAG) + { + power_on_flag = num_1; + afe_read_ocv_volt(&batt_info->fOCVVolt); + + afe_read_cell_volt(&batt_info->fVolt); + if(oz8806_cell_num > num_1) + batt_info->fOCVVolt = batt_info->fVolt; + batt_dbg("AAAA ocv volt is %d\n",(batt_info->fOCVVolt * oz8806_cell_num)); + batt_dbg("AAAA volt is %d\n",(batt_info->fVolt * oz8806_cell_num)); + if(batt_info->fOCVVolt > (config_data->charge_cv_voltage + 150)){ + if (oz8806_get_power_on_time() < 2250)//2250+2000 + { + batt_dbg("ocv wait for normal scan cycle\n"); + msleep(2250 - oz8806_get_power_on_time()); + oz8806_get_power_on_time(); + } + + afe_read_cell_volt(&batt_info->fVolt); + batt_info->fOCVVolt = batt_info->fVolt; + batt_dbg("AAAAA ocv data errror ,so batt_info->fVolt is %d\n",batt_info->fVolt); + } + + afe_read_current(&batt_info->fCurr); + batt_dbg("AAAA batt_info.fCurr is %d\n",batt_info->fCurr); + + if(batt_info->fCurr > num_50) + data = batt_info->fOCVVolt - num_100; + else + data = batt_info->fOCVVolt; + batt_info->fRSOC = one_latitude_table(parameter->ocv_data_num,parameter->ocv,data); + batt_dbg("find ocv table batt_info.fRSOC is %d\n",batt_info->fRSOC); + if((batt_info->fRSOC >num_100) || (batt_info->fRSOC < num_0)) + batt_info->fRSOC = num_50; + + batt_info->fRC = batt_info->fRSOC * config_data->design_capacity / num_100; + afe_read_current(&batt_info->fCurr); + if(batt_info->fRC >= (gas_gauge->overflow_data -num_10)) + { + batt_info->fRC = gas_gauge->overflow_data - gas_gauge->overflow_data /num_100; + batt_info->fRCPrev = batt_info->fRC; + } + afe_write_car(batt_info->fRC); + batt_info->fRCPrev = batt_info->fRC; + gas_gauge->fcc_data= config_data->design_capacity * 100/100; + + batt_dbg("Power on mode is activated \n"); + + batt_info->sCaMAH = batt_info->fRSOC * config_data->design_capacity / num_100; + gas_gauge->sCtMAH = batt_info->sCaMAH; + gas_gauge->discharge_sCtMAH = config_data->design_capacity - batt_info->sCaMAH; + } + else if(value & SLEEP_OCV_FLAG) + { + afe_read_ocv_volt(&batt_info->fOCVVolt); + sleep_ocv_flag = 1; + batt_dbg("Sleep ocv mode is activated \n"); + } + else + batt_dbg("Normal mode is activated \n"); + } + else + { + batt_dbg("AAAA BMT fgu DRIVER \n"); + batt_dbg("AAAA BMT fgu read BMT_OP_OCV_LOW\n"); + + } + + //for ifive +#if 0 + afe_read_car(&batt_info->fRC); + if((batt_info->fRC < (one_percent_rc)) && (batt_info->fRC > -3000)) + { + batt_dbg("yyyy fRC will over fRC is %d\n",batt_info->fRC); + batt_info->fRC = one_percent_rc - num_1; + afe_write_car(batt_info->fRC); + batt_info->fRCPrev = batt_info->fRC; + } + afe_read_car(&batt_info->fRC); + batt_dbg("fRC is %d\n",batt_info->fRC); +#endif + afe_register_read_byte(num_0,&i); + batt_dbg("BMT fgu regeidter 0x00 is 0x%02x\n",i); + + afe_register_read_byte(num_9,&i); + batt_dbg("BMT fgu regeidter 0x09 is 0x%02x\n",i); +} + +/***************************************************************************** + * Description: + * trim_bmu_VD23 + * Parameters: + * None + * Return: + * None + *****************************************************************************/ +void trim_bmu_VD23(void) +{ + int32_t ret; + uint8_t i; + uint16_t value; + uint8_t data; + uint8_t temp; + + for(i = 0;i < 3;i++) + { + ret = afe_register_read_word(OZ8806_OP_VD23,&value); + if(ret >= 0)break; + } + if(ret < 0) + { + batt_dbg("READ BMT_OP_VD23 fail and return:%d\n",ret); + return; + } + batt_dbg("READ BMT REG 0x03 is 0x%02x\n",value); + data = ((value >> 6) & 0x02) | ((value >> 8) & 0x01); + data &= 0x03; + batt_dbg("So trim is 0x%02x\n",data); + + for(i = 0;i < 3;i++) + { + ret = afe_register_read_byte(OZ8806_OP_PEC_CTRL,&temp); + if(ret >= 0)break; + } + if(ret < 0) + { + batt_dbg("READ BMT_OP_PEC_CTRL fail and return:%d\n",ret); + return; + } + batt_dbg("READ BMT fgu REG 0x08 is 0x%02x\n",temp); + + temp &= (~0x03); + if(data != 0x02) + { + temp |= data; + for(i = 0;i < 3;i++) + { + ret = afe_register_write_byte(OZ8806_OP_PEC_CTRL,temp); + batt_dbg("write1 BMT fgu REG 0x08 is 0x%02x\n",temp); + if(ret >= 0)break; + } + } + else + { + temp |= 0x03; + for(i = 0;i < 3;i++) + { + ret = afe_register_write_byte(OZ8806_OP_PEC_CTRL,temp); + batt_dbg("write2 BMT fgu REG 0x08 is 0x%02x\n",temp); + if(ret >= 0)break; + } + } + ret = afe_register_read_byte(OZ8806_OP_PEC_CTRL,&temp); + batt_dbg("BMT fgu REG 0x08 is 0x%02x\n",temp); + +} + +/***************************************************************************** +* Description: +* check shudown voltage control +* Parameters: +* None +* Return: +* None +*****************************************************************************/ +void check_shutdwon_voltage(void) +{ + //int32_t ret; + //uint8_t i; + int32_t temp; + //int32_t infVolt; + afe_read_cell_volt(&batt_info->fVolt); + + if(!sleep_ocv_flag) return; + + temp = one_latitude_table(parameter->ocv_data_num,parameter->ocv,batt_info->fOCVVolt); + batt_dbg("Sleep data is %d \n",temp); + + // select higher data + if(calculate_soc > temp) + temp = calculate_soc; + + //Very dangerous + if((batt_info->fRSOC - temp) > 20 + || (batt_info->fVolt < (config_data->discharge_end_voltage - 50) && temp <= 5)) + { + //batt_info->fRSOC -= 5; + batt_info->fRSOC = temp; + if(batt_info->fRSOC < 0) batt_info->fRSOC = 0; + + batt_info->sCaMAH = batt_info->fRSOC * gas_gauge->fcc_data/ 100; + + batt_info->fRC= batt_info->sCaMAH; + if(batt_info->fRC > (gas_gauge->overflow_data - 20)) + batt_info->fRC = gas_gauge->overflow_data - 20; + afe_write_car(batt_info->fRC); + + batt_info->fRCPrev = batt_info->fRC; + + gas_gauge->sCtMAH = batt_info->sCaMAH; + if(gas_gauge->fcc_data > batt_info->sCaMAH) + gas_gauge->discharge_sCtMAH = gas_gauge->fcc_data- batt_info->sCaMAH; + else + gas_gauge->discharge_sCtMAH = 0; + } +} + +/***************************************************************************** +* Description: +* check_oz8806_staus +* Parameters: +* None +* Return: +* None +*****************************************************************************/ +static void check_oz8806_staus(void) +{ + +} +/***************************************************************************** +* Description: +* bmu_wait_ready +* Parameters: +* None +* Return: +* None +*****************************************************************************/ +static void bmu_wait_ready(void) +{ + int32_t store_rc = INIT_CAP; + //int32_t fcc; + uint8_t i; + int32_t ret; + uint16_t value; + int32_t calculate_soc_temp; + int32_t file_soc_temp; + + batt_dbg("AAAA bmu wait times %d \n",times); + times ++; + //wake up OZ8806 into FullPower mode + oz8806_wakeup_full_power(); + + ret = afe_register_read_byte(OZ8806_OP_CTRL,&i); + batt_info->Battery_ok = i & BI_STATUS; + batt_dbg("bmu_wait_ready read 0x09 ret is %d,i is 0x%02x\n",ret,i); + + if((i & 0x40) || (ret < 0)) + { + if(oz8806_cell_num > 1) + { + afe_register_write_byte(OZ8806_OP_CTRL,0x2c); + } + else + afe_register_write_byte(OZ8806_OP_CTRL,0x20); + batt_dbg("BMT fgu wake up function\n"); + + ret = afe_register_read_byte(OZ8806_OP_CTRL,&i); + batt_dbg("bmu_wait_ready read 0x09 ret is %d,i is 0x%02x\n",ret,i); + } + + //do we need add check ocv flag now? + ret = afe_register_read_word(OZ8806_OP_OCV_LOW,&value); + if ((ret >= 0) && (value & POWER_OCV_FLAG)) + { + batt_dbg("read flag too late\n"); + wait_ocv_flag_fun(); + } + + afe_read_car(&batt_info->fRC); + if((batt_info->fRC < 5)|| (power_on_flag)) + { + + if (oz8806_get_power_on_time() < 2250) + { + batt_dbg("wait for normal scan cycle and return \n"); + return; + //msleep(2250 - oz8806_get_power_on_time()); + } + else + batt_dbg("normal scan cycle delay ok\n"); + } + else + batt_dbg("means normal boot,no need delay\n"); + + afe_read_cell_temp(&batt_info->fCellTemp); + afe_read_cell_volt(&batt_info->fVolt); + afe_read_current(&batt_info->fCurr); + + batt_dbg("fVolt is %d\n",(batt_info->fVolt * oz8806_cell_num)); + batt_dbg("fCurr is %d\n",batt_info->fCurr); + batt_dbg("ftemp is %d\n",batt_info->fCellTemp); + batt_dbg("fRC is %d\n",batt_info->fRC); + + // every time we check this and print to see if the voltage and temp and current is abnormal + // how about if rc_result error + + calculate_soc_temp = calculate_soc_result(); + + calculate_soc = calculate_soc_temp; + calculate_mah = calculate_soc * config_data->design_capacity / num_100; + + calculate_times++; +#if 0 + if (calculate_times <= calculate_retry_times) + return; +#endif + batt_dbg("calculate_times: %d\n",calculate_times); + + gas_gauge->fcc_data = config_data->design_capacity; + + if((batt_info->fRC > 5)&& (!power_on_flag)) + { + store_rc = batt_info->fRC; + //calculate_retry_times = 0; + batt_dbg("Success to get soc from car :%d\n", batt_info->fRC); + goto file_ok; + } +/* + if (oz8806_get_boot_up_time() < 5000) // voltage can be stable after oz8806 is powered for 5 s + { + batt_dbg("wait oz8806 ADC ready\n"); + return; + } + */ + if((set_soc_from_ext)) + { + soc_external = oz8806_get_soc_from_ext(); + + batt_dbg("soc from external: %d\n", soc_external); + + if (soc_external == -1) + { + batt_dbg("wait for external soc:%d, retry:%d\n", soc_external, retry_times); + + if(retry_times >= 0) + { + batt_dbg("fail to get externl soc, retry_times:%d \n",retry_times); + retry_times = 0; + set_soc_from_ext = 0; + batt_dbg("can't get external soc,so make set_soc_from_ext 0\n"); + } + else + { + retry_times++; + return; + } + } + else if ((soc_external <= 100) && (soc_external >= 0)) + { + batt_dbg("Success to get soc from external :%d\n", soc_external); + + store_rc = soc_external * gas_gauge->fcc_data / 100; + store_rc += gas_gauge->fcc_data / 200; // + fcc / 100 * / 2 + batt_dbg("stored RC:%d\n", store_rc); + + goto file_ok; + } + else + { + batt_dbg("Error to get wrong soc from external :%d\n", soc_external); + retry_times = 0; + set_soc_from_ext = 0; + batt_dbg("can't get external soc,so make set_soc_from_ext 0\n"); + } + } + + if (bmu_sys_rw_kernel)// this ok + { + /* wait for sysfs to capacity from file*/ + store_rc = bmu_read_data(BATT_CAPACITY); + + //check file ,if file not exit, goto file fail + if(store_rc < 0) + { + batt_dbg("open BATT_CAPACITY fail, retry_times:%d \n", retry_times); + if(retry_times >= power_on_retry_times) + { + batt_dbg("BATT_CAPACITY file fail\n"); + batt_info->sCaMAH = calculate_mah; + goto file_fail; + } + else + { + retry_times++; + return; + } + } + batt_dbg("open BATT_CAPACITY success, retry_times:%d \n", retry_times); + batt_dbg("AAAA read battery capacity data is %d\n", store_rc); + } + else + { + store_rc = oz8806_get_save_capacity(); + batt_dbg("RC from file:%d\n", store_rc); + if (store_rc == INIT_CAP) + { + batt_dbg("wait for reading file:%s, retry:%d\n", BATT_CAPACITY, retry_times); + if(retry_times > 10) + { + batt_dbg("fail to get save_capacity, retry_times:%d \n",retry_times); + goto file_fail; + } + retry_times++; + return; + } + else if (store_rc == NO_FILE) + { + batt_dbg("no file %s, retry_times:%d \n", BATT_CAPACITY, retry_times); + goto file_fail; + } + } + +file_ok: + + //file_soc_temp: soc read from file + if (soc_external != -1) + file_soc_temp = soc_external; + else + file_soc_temp = store_rc * 100 / gas_gauge->fcc_data; + + //file ok, if ocv power on + if(power_on_flag) + { + batt_dbg("bmu_wait_ready, power on ok \n"); + gas_gauge->ocv_flag = 1; + + //file_soc_temp: soc read from file + //batt_info->fRSOC: calculated using OCV + //calculated_soc: calculated using VI + batt_dbg("soc from file:%d, vi soc:%d, ocv soc:%d \n", + file_soc_temp, calculate_soc, batt_info->fRSOC); + + batt_dbg("RC from file:%d, vi RC:%d, ocv RC:%d \n", + store_rc, calculate_mah, batt_info->sCaMAH); + + if((batt_info->fRSOC > 90)&& (file_soc_temp > 90)) + { + batt_info->sCaMAH = store_rc; + batt_dbg("start sCaMAH high USE file\n"); + } + else if(batt_info->fCurr >= 0) + { + if( ABS(calculate_soc,file_soc_temp) > 30) + { + batt_info->sCaMAH = calculate_mah; + batt_dbg("start sCaMAH chg USE vi\n"); + } + else // file + { + batt_info->sCaMAH = store_rc; + batt_dbg("start sCaMAH chg USE file\n"); + } + } + else //discharging + { + if( ABS(file_soc_temp,calculate_soc) > 20 + || (batt_info->fVolt < (config_data->discharge_end_voltage - 50) && file_soc_temp<=5)) + { + batt_info->sCaMAH = calculate_mah; + batt_dbg("start sCaMAH USE vi\n"); + } + else + { + batt_info->sCaMAH = store_rc; + batt_dbg("start sCaMAH USE file\n"); + } + } + } + //if not ocv power on + else + { batt_dbg("bmu_wait_ready, normal ok \n"); + gas_gauge->ocv_flag = 0; + afe_read_car(&batt_info->fRC); + + batt_dbg("RC from file: %d ,RC from register: %d\n", store_rc, batt_info->fRC); + + if ((batt_info->fCurr > 0 && batt_info->fRC <= 2) + || (batt_info->fCurr <= 0 && batt_info->fRC <= 0))//power on, no ocv flag + { + if((ABS(file_soc_temp,calculate_soc) > 30) + || (batt_info->fVolt < (config_data->discharge_end_voltage - 50) && file_soc_temp<=5)) + { + batt_info->sCaMAH = calculate_mah; + batt_dbg("file ok but USE vi,fRC<=0\n"); + } + else + { + batt_info->sCaMAH = store_rc; + batt_dbg("file ok USE file\n"); + } + } + else + { + // car and file changed more than 1% + if(( ABS(batt_info->fRC,store_rc) > (one_percent_rc))) + { + batt_info->sCaMAH = batt_info->fRC; + batt_dbg("file ok USE RC\n"); + } + else + { + //prevent issue: when reboot frequently, soc -= 1 every reboot + if (((soc_external <= 100) && (soc_external >= 0)) + &&(batt_info->fRC > store_rc) + &&(batt_info->fRC - store_rc <= one_percent_rc)) + { + batt_dbg("file ok USE RC\n"); + batt_info->sCaMAH = batt_info->fRC; + } + else + { + batt_info->sCaMAH = store_rc; + batt_dbg("file ok USE file\n"); + } + } + } + } + +file_fail: + if(store_rc<0) //fail to red fil or no file exist + { + if(power_on_flag) + { + batt_dbg("fVolt %dmv,cell_num:%d, power_on_100_vol:%dmv\n",batt_info->fVolt,oz8806_cell_num,gas_gauge->power_on_100_vol); + if((batt_info->fVolt * oz8806_cell_num)>= gas_gauge->power_on_100_vol) + batt_info->sCaMAH = full_charge_data; + else + batt_info->sCaMAH = calculate_mah; + batt_dbg("bmu_wait_ready, power on ok and file fail \n"); + batt_dbg("file fail USE vi\n"); + } + else + { + batt_dbg("bmu_wait_ready, normal on ok and file fail \n"); + afe_read_car(&batt_info->fRC); + + batt_dbg("RC from register: %d, RC from vi: %d\n", batt_info->fRC, calculate_mah); + + if ((batt_info->fCurr > 0 && batt_info->fRC <= 2) + || (batt_info->fCurr <= 0 && batt_info->fRC <= 0))//power on, no ocv flag + { + batt_dbg("big error first power on and ocv flag not set: %d ,%d\n", batt_info->fRC, calculate_mah); + batt_info->sCaMAH = calculate_mah; + batt_dbg("file fail USE vi\n"); + } + else + { +#if 0 + batt_info->sCaMAH = batt_info->fRC; + batt_dbg("file fail USE RC\n"); +#else + + if(batt_info->fRC < one_percent_rc ) //if CAR < 1% + file_not_ok_cap = 30; + + if( ABS(batt_info->fRC,calculate_mah) > (file_not_ok_cap * config_data->design_capacity / num_100)) + { + batt_info->sCaMAH = calculate_mah; + batt_dbg("file fail USE vi\n"); + + } + else + { + batt_info->sCaMAH = batt_info->fRC; + batt_dbg("file fail USE RC\n"); + } + + if(fix_car_init) + { + batt_info->sCaMAH = batt_info->fRC; + batt_dbg("file fail fix fRC: %d,%d \n",batt_info->sCaMAH,batt_info->fRC); + } +#endif + } + } + } + + if(batt_info->sCaMAH <= num_0) + { + batt_dbg("calculate_soc is %d\n",calculate_soc); + afe_read_cell_volt(&batt_info->fVolt); + batt_info->fRSOC = calculate_soc; + if((batt_info->fRSOC >num_100) || (batt_info->fRSOC < num_0)) + batt_info->fRSOC = num_50; + + batt_dbg("batt_info->fRSOC is %d\n",batt_info->fRSOC); + batt_info->fRC = batt_info->fRSOC * config_data->design_capacity / num_100; + batt_dbg("batt_info->fRC is %d\n",batt_info->fRC); + if(batt_info->fRC >= (gas_gauge->overflow_data -num_10)) + { + batt_info->fRC = gas_gauge->overflow_data - gas_gauge->overflow_data/num_100; + batt_info->fRCPrev = batt_info->fRC; + } + batt_dbg("batt_info->fRC is %d\n",batt_info->fRC); + + afe_write_car(batt_info->fRC); + batt_info->fRCPrev = batt_info->fRC; + + batt_info->sCaMAH = batt_info->fRC; + } + if(batt_info->sCaMAH > full_charge_data) + { + batt_info->sCaMAH = full_charge_data; + } + batt_dbg("batt_info->sCaMAH is %d\n",batt_info->sCaMAH); + + batt_info->fRSOC = batt_info->sCaMAH * num_100; + batt_info->fRSOC = batt_info->fRSOC / gas_gauge->fcc_data ; + + //check if batt_info->sCaMAH == soc_external * gas_gauge->fcc_data / 100; + if ((soc_external <= 100) && (soc_external >= 0)) + if (batt_info->sCaMAH == soc_external * gas_gauge->fcc_data / 100) + batt_info->fRSOC = soc_external; + + //batt_info->fRC = batt_info->fRSOC * gas_gauge->fcc_data / num_100;//this make result smaller + batt_info->fRC = batt_info->sCaMAH; + if(batt_info->fRC >= (gas_gauge->overflow_data -num_10)) + { + batt_info->fRC = gas_gauge->overflow_data - gas_gauge->overflow_data/num_100; + batt_info->fRCPrev = batt_info->fRC; + } + gas_gauge->sCtMAH = batt_info->sCaMAH; + + batt_dbg("AAAA batt_info->fVolt is %d\n",(batt_info->fVolt * oz8806_cell_num)); + batt_dbg("AAAA batt_info->fRSOC is %d\n",batt_info->fRSOC); + batt_dbg("AAAA batt_info->sCaMAH is %d\n",batt_info->sCaMAH); + batt_dbg("AAAA gas_gauge->sCtMAH is %d\n",gas_gauge->sCtMAH); + batt_dbg("AAAA fcc is %d\n",gas_gauge->fcc_data); + batt_dbg("AAAA batt_info->fRC is %d\n",batt_info->fRC); + batt_dbg("AAAA batt_info->fCurr is %d\n",batt_info->fCurr); + + afe_write_car(batt_info->fRC); + batt_info->fRCPrev = batt_info->fRC; + + if(gas_gauge->fcc_data > batt_info->sCaMAH) + gas_gauge->discharge_sCtMAH = gas_gauge->fcc_data- batt_info->sCaMAH; + else + gas_gauge->discharge_sCtMAH = 0; + + if(batt_info->fRSOC <= num_0) + { + gas_gauge->discharge_end = num_1; + batt_info->fRSOC = num_0; + gas_gauge->sCtMAH = num_0; + gas_gauge->ocv_flag = 0; + bmu_write_data(OCV_FLAG,0); + } + if(batt_info->fRSOC >= num_100) + { + gas_gauge->charge_end = num_1; + charge_end_flag = num_1; + batt_info->fRSOC = num_100; + gas_gauge->discharge_sCtMAH = num_0; + gas_gauge->discharge_fcc_update = num_1; + gas_gauge->ocv_flag = 0; + bmu_write_data(OCV_FLAG,0); + } + + if(sleep_ocv_flag) + check_shutdwon_voltage(); + + fRSOC_PRE = batt_info->fRSOC; + + // double check + if (bmu_sys_rw_kernel) + { + store_rc = bmu_read_data(BATT_CAPACITY); + if(store_rc != batt_info->sCaMAH) + { + bmu_write_data(BATT_CAPACITY,batt_info->sCaMAH); + bmu_write_data(BATT_FCC, gas_gauge->fcc_data); + //batt_dbg("init write sCaMAH %d,%d\n",batt_info->sCaMAH,store_rc); + } + } + gas_gauge->bmu_init_ok = num_1; +} + +/***************************************************************************** +* Description: +* power down 8806 chip +* Parameters: +* None +* Return: +* None +*****************************************************************************/ +void bmu_power_down_chip(void) +{ + afe_read_cell_temp(&batt_info->fCellTemp); + afe_read_cell_volt(&batt_info->fVolt); + afe_read_current(&batt_info->fCurr); + afe_read_car(&batt_info->fRC); + + if(oz8806_cell_num > num_1) + afe_register_write_byte(OZ8806_OP_CTRL,num_0x4c |SLEEP_OCV_EN); + else + afe_register_write_byte(OZ8806_OP_CTRL,SLEEP_MODE | SLEEP_OCV_EN); + + if(parameter->config->debug){ + batt_dbg("eeee power down BMT fgu \n"); + batt_dbg("eeee batt_info->fVolt is %d\n",(batt_info->fVolt * oz8806_cell_num)); + batt_dbg("eeee batt_info->fRSOC is %d\n",batt_info->fRSOC); + batt_dbg("eeee batt_info->sCaMAH is %d\n",batt_info->sCaMAH); + batt_dbg("eeee batt_info->fRC is %d\n",batt_info->fRC); + batt_dbg("eeee batt_info->fCurr is %d\n",batt_info->fCurr); + } +} + +/***************************************************************************** +* Description: +* power up 8806 chip +* Parameters: +* None +* Return: +* None +*****************************************************************************/ +void bmu_wake_up_chip(void) +{ + uint8_t data; + int32_t ret; + int32_t value; + uint8_t discharge_flag = num_0; + uint8_t i; + int32_t sleep_time = 0; + int32_t car_wakeup = 0; + int8_t adapter_status = 0; + + batt_dbg("fCurr is %d\n",batt_info->fCurr); + + adapter_status = get_adapter_status(); + if ((batt_info->fCurr <= gas_gauge->discharge_current_th) || + (adapter_status == O2_CHARGER_BATTERY)) + { + discharge_flag = 1; + } + + batt_dbg("adapter_status: %d\n", adapter_status); + + bmu_sleep_flag = 1; + + time_sec = oz8806_get_system_boot_time(); + sleep_time = time_sec - previous_loop_timex; + batt_dbg("sleep time: %d s\n",sleep_time); + + oz8806_wakeup_full_power(); + + ret = afe_register_read_byte(OZ8806_OP_CTRL,&data);//wake up OZ8806 into FullPower mode + if((data & 0x40) || (ret < 0)) + { + batt_dbg("bmu_wake_up_chip read 0x09 ret is %d,i is %d \n",ret,data); + if(oz8806_cell_num > 1) + { + afe_register_write_byte(OZ8806_OP_CTRL,0x2c); + } + else + afe_register_write_byte(OZ8806_OP_CTRL,0x20); + batt_dbg("BMT fgu wake up function\n"); + + } + + if(batt_info->fRSOC >=num_100) + { + if(batt_info->fRSOC >= num_100) batt_info->fRSOC = num_100; + if(batt_info->sCaMAH > (full_charge_data)) + { + batt_dbg("BMT fgu wake up batt_info.sCaMAH big error is %d\n",batt_info->sCaMAH); + batt_info->sCaMAH = full_charge_data; + } + } + + afe_read_current(&batt_info->fCurr); + afe_read_cell_volt(&batt_info->fVolt); + + batt_dbg("sCaMAH:%d,fRC:%d,fcurr:%d,volt:%d,sCtMAH:%d\n", + batt_info->sCaMAH,batt_info->fRC,batt_info->fCurr, + (batt_info->fVolt * oz8806_cell_num), + gas_gauge->sCtMAH); + + for(i = num_0;i< num_3;i++) + { + ret = afe_read_car(&car_wakeup); + if (ret >= 0) + { + if ((car_wakeup == 0) || ((!discharge_flag) && (car_wakeup < 0))) + { + car_error += 1; + batt_dbg("test fRC read: %d, %d\n",car_error, car_wakeup); //H7 Test Version + } + else + break; + } + } + + value = car_wakeup - batt_info->fRC; + //*******CAR zero reading Workaround****************************** + if (sleep_time < 0) sleep_time = 60; //force 60s + if (discharge_flag == 1) + sleep_time *= MAX_SUSPEND_CONSUME; //time * current / 3600 * 1000 + else + sleep_time *= MAX_SUSPEND_CHARGE; //time * current / 3600 * 1000 + + // 10% + if (ABS(car_wakeup, batt_info->fRC) > (10 * config_data->design_capacity / num_100)) //delta over 10% + { + if (((value * 1000) - sleep_time) < 0) //over max car range + { + value = (sleep_time / 1000); + batt_dbg("Ab CAR:%d,mod:%d\n",car_wakeup,value); + car_wakeup = value + batt_info->fRC; + afe_write_car(car_wakeup); + } + } + batt_dbg("CAR Prev:%d,CAR Now:%d\n",batt_info->fRC,car_wakeup); + //*******CAR zero reading Workaround****************************** + if(car_wakeup <= 0) + { + batt_dbg("fRC is %d\n",car_wakeup); + //oz8806_over_flow_prevent(); + gas_gauge->charge_fcc_update = num_0; + gas_gauge->discharge_fcc_update = num_0; + + if (car_wakeup < 0) //overflow check + { + if (batt_info->fVolt >= (config_data->charge_cv_voltage - 50)) + { + car_wakeup = gas_gauge->fcc_data -num_1; + } + else + { + if (batt_info->fRSOC > 0) + car_wakeup = batt_info->fRSOC * gas_gauge->fcc_data / num_100 - num_1; + else + car_wakeup = batt_info->sCaMAH; + } + afe_write_car(car_wakeup); + batt_info->fRC = car_wakeup; + batt_info->fRCPrev = batt_info->fRC; + } + + if(discharge_flag) + { + check_shutdwon_voltage(); + value = calculate_soc_result(); + if(value < batt_info->fRSOC) + { + batt_info->fRSOC = value; + batt_info->sCaMAH = batt_info->fRSOC * gas_gauge->fcc_data / 100; + batt_info->fRC = batt_info->sCaMAH; + batt_info->fRCPrev = batt_info->fRC; + afe_write_car(batt_info->fRC); + } + + if(batt_info->fRSOC <= 0) + { + if((batt_info->fVolt <= config_data->discharge_end_voltage)) + { + discharge_end_process(); + } + //wait voltage + else + { + batt_info->fRSOC = 1; + batt_info->sCaMAH = gas_gauge->fcc_data / num_100 ; + gas_gauge->discharge_end = num_0; + } + } + return; + } + } + + if ((discharge_flag) && (car_wakeup > batt_info->fRC)) + { + batt_dbg("it seems error 1\n"); + value = car_wakeup - batt_info->fRC; + car_wakeup = batt_info->fRC - value; + afe_write_car(car_wakeup); + } + if ((!discharge_flag) && (car_wakeup < batt_info->fRC)) + { + batt_dbg("it seems error 2\n"); + value = batt_info->fRC - car_wakeup; + car_wakeup = batt_info->fRC + value; + afe_write_car(car_wakeup); + } + + batt_dbg("tttt final wakeup car is %d\n",car_wakeup); +} + +/***************************************************************************** +* Description: +* check_board_offset +* Parameters: +* None +* Return: +* None +*****************************************************************************/ +static void check_board_offset(void) +{ + int32_t data; + int32_t ret; + int32_t offset; + int32_t ret2; + + ret2 = afe_read_board_offset(&data); + + if(config_data->board_offset != data && config_data->board_offset != num_0) + { + afe_write_board_offset(config_data->board_offset); + return; + } + + ret = bmu_read_data(BATT_OFFSET); + //batt_dbg("AAAA board_offset is %d\n",data); + if(ret < num_0) + { + + if((data > num_10) || (data <= num_0)) + afe_write_board_offset(7); + + if(ret2 >= num_0) + { + if((data < num_10) && (data > num_0) && (data != num_0)) + { + ret = bmu_write_data(BATT_OFFSET,data); + + if(ret num_2) || ((offset - data) < -num_2)) + afe_write_board_offset(offset); + } + + afe_read_board_offset(&data); + if((data > num_10) || (data <= num_0)) + afe_write_board_offset(num_7); + + afe_read_board_offset(&data); + batt_dbg("AAAA board_offset is %dmA\n",data); +} + +/***************************************************************************** +* Description: +* bmu polling loop +* Parameters: +* None +* Return: +* None +*****************************************************************************/ +void bmu_polling_loop(void) +{ + int32_t data; + + int32_t ret; + uint8_t i; + static uint8_t error_times = 0; + + int8_t adapter_status = 0; + uint8_t retry = 0; + uint8_t val = 0; + if(is_battery_exchanged()){ //battery was exchanged, oz8806 in start-up mode + gas_gauge->bmu_init_ok = 0; + batt_info->i2c_error_times = 0; + batt_dbg("battery was exchanged, init SOC again\n"); + +retry_wakeup: + //wake up this stupid chip + oz8806_wakeup_full_power(); + msleep(10) ; + ret = afe_register_read_byte(OZ8806_OP_CTRL,&val); + if((val & 0x40) || (ret < 0)) + { + batt_dbg("bmu_wake_up_chip read 0x09 ret is %d,i is %d \n",ret,val); + retry++; + + if(retry>10){ + batt_dbg("fail wakeup ic"); + } + goto retry_wakeup; + } + + retry_times = 0; + calculate_times = 0; + times = 0; + wait_ocv_flag = 0; + wait_ocv_flag_fun(); + } + + batt_dbg("-----------------------bmu_polling_loop start------------------\n"); + + if(!(gas_gauge->bmu_init_ok)) + { + if(!wait_ocv_flag) + wait_ocv_flag_fun(); + else + bmu_wait_ready(); + + if(gas_gauge->bmu_init_ok) + check_board_offset(); + + goto end; + } + + batt_info->fRCPrev = batt_info->fRC; + + afe_read_cell_temp(&batt_info->fCellTemp); + afe_read_cell_volt(&batt_info->fVolt); + afe_read_current(&batt_info->fCurr); + + //be careful for large current + //and wake up condition + ret = afe_read_car(&data); + if(parameter->config->debug) { + if (data == 0) car_error++; + batt_dbg("CAR is %d, %d\n",data, car_error); + } + + if((ret >= 0) && (data > 0)) + { + // for big current charge + // 10% + if (ABS(batt_info->fRCPrev, data) < (10 * config_data->design_capacity / num_100)) + { + batt_info->fRC = data; + error_times = 0; + } + else + { + error_times++; + if(error_times > 3) + { + batt_dbg("CAR error_times is %d\n",error_times); + error_times = 0; + afe_write_car(batt_info->sCaMAH); + batt_info->fRCPrev = batt_info->sCaMAH; + batt_info->fRC = batt_info->sCaMAH; + } + } + } + + batt_dbg("first sCaMAH:%d,fRC:%d,fRCPrev:%d,fcurr:%d,volt:%d\n", + batt_info->sCaMAH,batt_info->fRC,batt_info->fRCPrev, + batt_info->fCurr,(batt_info->fVolt * oz8806_cell_num)); + + //back sCaMAH + //H7====LOW VOLTAGE CHARGE PREVENT====== + if ((batt_info->fVolt < config_data->discharge_end_voltage) && (batt_info->fCurr > 0)) //charge and voltage < 3300 + { + batt_dbg("Low voltage %d charge current %d detected\n",batt_info->fVolt,batt_info->fCurr); + if (batt_info->fRC > batt_info->fRCPrev) //CAR increased + { + batt_dbg("Low voltage fRC limit triggered %d\n",batt_info->fRCPrev); + batt_info->fRC = batt_info->fRCPrev; //Limit CAR as previous + afe_write_car(batt_info->fRC); //write back CAR + } + } + //H7====LOW VOLTAGE CHARGE PREVENT====== + + gas_gauge->sCtMAH += batt_info->fRC - batt_info->fRCPrev; + gas_gauge->discharge_sCtMAH += batt_info->fRCPrev - batt_info->fRC; + age_update(); + + if(gas_gauge->sCtMAH < num_0) gas_gauge->sCtMAH = num_0; + if(gas_gauge->discharge_sCtMAH < num_0) gas_gauge->discharge_sCtMAH = num_0; + + //bmu_call(); + + adapter_status = get_adapter_status(); + if ((batt_info->fCurr <= gas_gauge->discharge_current_th) || + (adapter_status == O2_CHARGER_BATTERY)) + discharge_process(); + else + charge_process(); + + batt_dbg("second sCaMAH:%d,fRC:%d,fRCPrev:%d,fcurr:%d,volt:%d\n", + batt_info->sCaMAH,batt_info->fRC,batt_info->fRCPrev, + batt_info->fCurr,(batt_info->fVolt * oz8806_cell_num)); + + if(gas_gauge->charge_end) + { + if(!charge_end_flag) + { + batt_dbg("enter BMT fgu charge end\n"); + charge_end_flag = num_1; + charge_end_process(); + } + } + else + { + charge_end_flag = num_0; + } + + if(gas_gauge->discharge_end) + { + if(!discharge_end_flag) + { + discharge_end_flag = num_1; + discharge_end_process(); + } + } + else + { + discharge_end_flag = num_0; + } + + oz8806_over_flow_prevent(); + + /* + //very dangerous + if(batt_info->fVolt <= (config_data->discharge_end_voltage - num_100)) + { + ret = afe_read_cell_volt(&batt_info->fVolt); + if((ret >=0) && (batt_info->fVolt > 2500)) + discharge_end_process(); + } + */ + //gas_gauge->bmu_tick++; + previous_loop_timex = time_sec; + time_sec = oz8806_get_system_boot_time(); + batt_dbg("----------------------------------------------------\n" + "[bmt]:VERSION: %s-%x\n" + "[bmt]:TABLEVERSION: %s\n" + "[bmt]:battery_ok: %d,time_x: %d\n" + "[bmt]:chg_fcc_update: %d, disg_fcc_update: %d\n" + "[bmt]:fVolt: %d fCurr: %d fCellTemp: %d fRSOC: %d\n" + "[bmt]:sCaMAH: %d, sCtMAH1: %d, sCtMAH2: %d\n" + "[bmt]:fcc: %d, fRC: %d\n" + "[bmt]:i2c_error_times: %d, acc_data:%d\n" + "[bmt]:charge_end: %d, discharge_end: %d\n" + "[bmt]:adapter_status: %d, soh: %d, cycle: %d\n" + "[bmt]:----------------------------------------------------\n", + VERSION,calculate_version,get_table_version(),batt_info->Battery_ok,time_sec, + gas_gauge->charge_fcc_update,gas_gauge->discharge_fcc_update, + (batt_info->fVolt * oz8806_cell_num),batt_info->fCurr,batt_info->fCellTemp,batt_info->fRSOC, + batt_info->sCaMAH,gas_gauge->sCtMAH,gas_gauge->discharge_sCtMAH, + gas_gauge->fcc_data,batt_info->fRC, + batt_info->i2c_error_times, accmulate_dsg_data, + gas_gauge->charge_end,gas_gauge->discharge_end, + adapter_status,age_soh,cycle_soh); + + if(batt_info->sCaMAH > (full_charge_data)) + { + batt_dbg("big error sCaMAH is %d\n",batt_info->sCaMAH); + batt_info->sCaMAH = full_charge_data; + } + + if(batt_info->sCaMAH < (gas_gauge->fcc_data / 100 - 1)) + { + //batt_dbg("big error sCaMAH is %d\n",batt_info->sCaMAH); + batt_info->sCaMAH = gas_gauge->fcc_data / 100 - 1; + } + + /* + if(gas_gauge->fcc_data > (config_data->design_capacity *FCC_UPPER_LIMIT / 100)) + { + gas_gauge->fcc_data= config_data->design_capacity * FCC_UPPER_LIMIT / 100 ; + bmu_write_data(BATT_FCC,gas_gauge->fcc_data); + + batt_dbg("fcc error is %d\n",gas_gauge->fcc_data); + + } + if((gas_gauge->fcc_data <= 0) || (gas_gauge->fcc_data < (config_data->design_capacity * FCC_LOWER_LIMIT / 100))) + { + gas_gauge->fcc_data = config_data->design_capacity * FCC_LOWER_LIMIT /100; + bmu_write_data(BATT_FCC,gas_gauge->fcc_data); + + batt_dbg("fcc error is %d\n",gas_gauge->fcc_data); + + } + */ + + gas_gauge->fcc_data= config_data->design_capacity; + + if (bmu_sys_rw_kernel) + { + data = bmu_read_data(BATT_CAPACITY); + //batt_dbg("read from RAM batt_info->sCaMAH is %d\n",data); + if(data >= num_0) + { + if(fRSOC_PRE != batt_info->fRSOC) + { + fRSOC_PRE = batt_info->fRSOC; + bmu_write_data(BATT_CAPACITY,batt_info->sCaMAH); + // batt_dbg("o2 back batt_info->sCaMAH num_1 is %d\n",batt_info->sCaMAH); + //return; + goto end; + } + + if(((batt_info->sCaMAH - data)> (gas_gauge->fcc_data/200))||((data - batt_info->sCaMAH)> (gas_gauge->fcc_data/200))){ + bmu_write_data(BATT_CAPACITY,batt_info->sCaMAH); + // batt_dbg("o2 back batt_info->sCaMAH 2 is %d\n",batt_info->sCaMAH); + } + } + else bmu_write_data(BATT_CAPACITY,batt_info->sCaMAH); + } + else + { + if(fRSOC_PRE != batt_info->fRSOC) + { + fRSOC_PRE = batt_info->fRSOC; + //batt_dbg("o2 back batt_info->sCaMAH num_1 is %d\n",batt_info->sCaMAH); + goto end; + } + } + + //wake up OZ8806 into FullPower mode + ret = afe_register_read_byte(OZ8806_OP_CTRL,&i); + batt_info->Battery_ok = i & BI_STATUS; + if(ret < 0) + batt_info->Battery_ok = 0; + + if((i & num_0x40) || (ret < num_0)) + { + batt_dbg("bmu_polling_loop read 0x09 ret is %d,i is %d \n",ret,i); + if(oz8806_cell_num > 1) + { + afe_register_write_byte(OZ8806_OP_CTRL,num_0x2c); + } + else + afe_register_write_byte(OZ8806_OP_CTRL,num_0x20); + //batt_dbg("oz8806 wake up function\n"); + + } + + ret = afe_read_board_offset(&data); + if (ret >= 0) + { + if (config_data->board_offset != 0) + { + //if (data != config_data->board_offset) + if(ABS(data, config_data->board_offset) > 3) + { + afe_write_board_offset(config_data->board_offset); + batt_dbg("BMT fgu board offset error1 is %d \n",data); + } + } + else + { + if((data > 10) || (data <= 0)) + { + batt_dbg("BMT fgu board offset error2 is %d \n",data); + afe_write_board_offset(7); + } + } + } + batt_dbg("test data is %d\n",batt_info->sCaMAH); + /* + + data = bmu_read_data(BATT_FCC); + + if(data != gas_gauge->fcc_data) + { + bmu_write_data(BATT_FCC,gas_gauge->fcc_data); + batt_dbg("test %d\n",gas_gauge->fcc_data); + } + */ + +end: + batt_dbg("-----------------------bmu_polling_loop end--------------------\n"); +} + +/***************************************************************************** +* Description: +* prevent oz8806 over flow +* Parameters: +* None +* Return: +* None +*****************************************************************************/ +static void oz8806_over_flow_prevent(void) +{ + int32_t ret; + int32_t data; + + if((batt_info->fRSOC > num_0) && gas_gauge->discharge_end) + gas_gauge->discharge_end = num_0; + + if((batt_info->fRSOC < num_100) && gas_gauge->charge_end) + gas_gauge->charge_end = num_0; + + if(batt_info->fRC< num_0) + { + if(batt_info->fVolt >= (config_data->charge_cv_voltage - 200)) + { + batt_info->fRC = gas_gauge->fcc_data -num_1; + } + else + { + if(batt_info->fRSOC > 0) + batt_info->fRC = batt_info->fRSOC * gas_gauge->fcc_data / num_100 - num_1; + else + batt_info->fRC = batt_info->sCaMAH; + } + + afe_write_car(batt_info->fRC); + batt_info->fRCPrev = batt_info->fRC; + } + + /* + ret = oz8806_read_word(OZ8806_OP_CAR); + + if(ret >= num_0) + { + ret = (int16_t)ret; + if(ret >= (num_32768 - num_10 * config_data->fRsense)) + { + if(parameter->config->debug)batt_dbg("yyyy CAR WILL UP OVER %d\n",ret); + ret = 32768 - 15 * config_data->fRsense; + oz8806_write_word(OZ8806_OP_CAR,(int16_t)ret); + afe_read_car(&batt_info->fRC); + batt_info->fRCPrev = batt_info->fRC; + } + else if(ret <= (num_10 * config_data->fRsense)) + { + if(parameter->config->debug)batt_dbg("yyyy CAR WILL DOWN OVER %d\n",ret); + ret = num_15 * config_data->fRsense; + oz8806_write_word(OZ8806_OP_CAR,(int16_t)ret); + afe_read_car(&batt_info->fRC); + batt_info->fRCPrev = batt_info->fRC; + } + + } + */ + + ret = afe_read_car(&data); + if(ret<0) + return; + + if(data < 5) + { + afe_write_car(batt_info->sCaMAH); + batt_info->fRCPrev = batt_info->sCaMAH; + batt_info->fRC = batt_info->sCaMAH; + batt_dbg("dddd car down is %d\n",data); + } + else if(data > (32768 * parameter->config->dbCARLSB * 1000 / config_data->fRsense -5)) + { + afe_write_car(batt_info->sCaMAH); + batt_info->fRCPrev = batt_info->sCaMAH; + batt_info->fRC = batt_info->sCaMAH; + batt_dbg("dddd car up is %d\n",data); + } + + if(ABS(batt_info->sCaMAH, data) > one_percent_rc) + { + if((batt_info->sCaMAH < gas_gauge->overflow_data ) && (batt_info->sCaMAH > 0)) + { + afe_write_car(batt_info->sCaMAH); + batt_info->fRCPrev = batt_info->sCaMAH; + batt_info->fRC = batt_info->sCaMAH; + if(parameter->config->debug){ + batt_dbg("dddd write car batt_info->fRCPrev is %d\n",batt_info->fRCPrev); + batt_dbg("dddd write car batt_info->fRC is %d\n",batt_info->fRC); + batt_dbg("dddd write car batt_info->sCaMAH is %d\n",batt_info->sCaMAH); + } + } + } + + if(batt_info->fRC > (full_charge_data)) + { + batt_dbg("more fRC %d\n",batt_info->fRC); + batt_info->fRC = full_charge_data; + batt_info->fRCPrev = batt_info->fRC; + afe_write_car(batt_info->fRC); + } +} + + + +/***************************************************************************** +* Description: +* charge fcc update process +* Parameters: +* None +* Return: +* None +*****************************************************************************/ +void charge_end_process(void) +{ + //FCC UPdate + + /* + if(gas_gauge->charge_fcc_update) + { + if(batt_info->fCurr < config_data->charge_end_current) + gas_gauge->fcc_data = gas_gauge->sCtMAH; + bmu_write_data(BATT_FCC,gas_gauge->fcc_data); + + batt_dbg("charge1 fcc update is %d\n",gas_gauge->fcc_data); + + } + + if(gas_gauge->fcc_data > (config_data->design_capacity *FCC_UPPER_LIMIT / 100)) + { + gas_gauge->fcc_data= config_data->design_capacity * FCC_UPPER_LIMIT / 100 ; + bmu_write_data(BATT_FCC,gas_gauge->fcc_data); + + batt_dbg("charge2 fcc update is %d\n",gas_gauge->fcc_data); + + } + if((gas_gauge->fcc_data <= 0) || (gas_gauge->fcc_data < (config_data->design_capacity * FCC_LOWER_LIMIT / 100))) + { + gas_gauge->fcc_data = config_data->design_capacity * FCC_LOWER_LIMIT / 100; + bmu_write_data(BATT_FCC,gas_gauge->fcc_data); + + batt_dbg("charge3 fcc update is %d\n",gas_gauge->fcc_data); + + } + */ + + gas_gauge->fcc_data = config_data->design_capacity; + batt_info->sCaMAH = full_charge_data;; + + if (bmu_sys_rw_kernel) + bmu_write_data(BATT_CAPACITY,batt_info->sCaMAH); + + afe_write_car(batt_info->sCaMAH); + + if(gas_gauge->ocv_flag) + bmu_write_data(OCV_FLAG,0); + + batt_dbg("yyyy end charge \n"); + batt_info->fRSOC = num_100; + gas_gauge->charge_end = num_1; + charge_end_flag = num_1; + //charger_finish = 0; + gas_gauge->charge_fcc_update = 1; + gas_gauge->discharge_fcc_update = 1; + gas_gauge->discharge_sCtMAH = 0; + power_on_flag = 0; +} + +/***************************************************************************** +* Description: +* discharge fcc update process +* Parameters: +* None +* Return: +* None +*****************************************************************************/ +void discharge_end_process(void) +{ + //int32_t voltage_end = config_data->discharge_end_voltage; + + //FCC UPdate + /* + if(gas_gauge->discharge_fcc_update) + { + gas_gauge->fcc_data = gas_gauge->discharge_sCtMAH; + bmu_write_data(BATT_FCC,gas_gauge->fcc_data); + batt_dbg("discharge1 fcc update is %d\n",gas_gauge->fcc_data); + + } + + if(gas_gauge->fcc_data > (config_data->design_capacity *FCC_UPPER_LIMIT / 100)) + { + gas_gauge->fcc_data= config_data->design_capacity * FCC_UPPER_LIMIT / 100 ; + bmu_write_data(BATT_FCC,gas_gauge->fcc_data); + batt_dbg("discharge2 fcc update is %d\n",gas_gauge->fcc_data); + } + if((gas_gauge->fcc_data <= 0) || (gas_gauge->fcc_data < (config_data->design_capacity * FCC_LOWER_LIMIT / 100))) + { + gas_gauge->fcc_data = config_data->design_capacity * FCC_LOWER_LIMIT / 100; + bmu_write_data(BATT_FCC,gas_gauge->fcc_data); + batt_dbg("discharge3 fcc update is %d\n",gas_gauge->fcc_data); + } + */ + gas_gauge->fcc_data = config_data->design_capacity; + if(gas_gauge->ocv_flag) + bmu_write_data(OCV_FLAG,0); + + batt_info->sCaMAH = gas_gauge->fcc_data / 100 - 1; + + if (bmu_sys_rw_kernel) + bmu_write_data(BATT_CAPACITY,batt_info->sCaMAH); + + afe_write_car(batt_info->sCaMAH); + batt_info->fRCPrev = batt_info->sCaMAH; + + + batt_dbg("yyyy end discharge \n"); + batt_info->fRSOC = num_0; + gas_gauge->discharge_end = num_1; + gas_gauge->discharge_fcc_update = 1; + gas_gauge->charge_fcc_update = 1; + gas_gauge->sCtMAH = num_0; + +} + + + +/***************************************************************************** +* Description: +* below is linux file operation +* Parameters: +* description for each argument, new argument starts at new line +* Return: +* what does this function returned? +*****************************************************************************/ +#if 0 +static int bmu_check_file(char * address) +{ + long fd = sys_open(address,O_RDONLY,0644); + + if(fd < num_0) + { + //batt_dbg(" bmu check file fail %s\n",address); + return -num_1; + } + sys_close(fd); + return (num_0); +} + + +static int bmu_read_data(char * address) +{ + char value[4]; + int* p = (int *)value; + long fd = sys_open(address,O_RDONLY,0644); + + if(fd < num_0) + { + batt_dbg(" bmu read open file fail %s, return %ld\n",address, fd); + return fd; + } + + sys_read(fd,(char __user *)value,4); + + sys_close(fd); + + return (*p); +} + +static int bmu_write_data(char * address,int data) +{ + char value[4]; + int* p = (int *)value; + long fd = sys_open(address,O_CREAT | O_RDWR, 0644); + + if(fd < num_0) + { + batt_dbg("bmu wirte open file fail %s failed, return %ld\n",address, fd); + return fd; + } + *p = data; + sys_write(fd, (const char __user *)value, 4); + + sys_close(fd); + return num_0; +} + +#else +static int bmu_read_data(char * address) +{ + return -1; +#if 0 + struct file *fp = NULL; + mm_segment_t fs; + loff_t pos = 0; + char buf_r[5]; + int ret = 0; + int val = 0; + + //batt_dbg("%s\n", __func__); + + fp = filp_open(address, O_RDONLY, 0644); + if (IS_ERR(fp)) + { + batt_dbg(" %s: open file fail %s\n", __func__, address); + return -1; + } + + fs = get_fs(); + set_fs(KERNEL_DS); + + pos = 0; + ret = vfs_read(fp, buf_r, sizeof(buf_r), &pos); + if (ret < 0) + batt_dbg(" %s: read file fail %s\n", __func__, address); + + buf_r[4] = '\0'; + batt_dbg("%s: read: %s\n", __func__, buf_r); + + ret = kstrtoint(buf_r, 10, &val); + if (ret < 0) + batt_dbg(" %s: fail to get int from %s\n", __func__, address); + + set_fs(fs); + + filp_close(fp, NULL); + + if (ret < 0) + return ret; + + return val; +#endif +} + +static int bmu_write_data(char * address,int data) +{ + return -1; +#if 0 + struct file *fp = NULL; + mm_segment_t fs; + loff_t pos = 0; + char buf[5]; + char buf_r[5]; + int ret = 0; + int val = 0; + + ret = sprintf(buf, "%d", data); + + //batt_dbg("%s\n", __func__); + + fp = filp_open(address, O_RDWR | O_CREAT, 0644); + if (IS_ERR(fp)) + { + batt_dbg(" %s: open file fail %s\n", __func__, address); + return -1; + } + + fs = get_fs(); + set_fs(KERNEL_DS); + + pos = 0; + ret = vfs_write(fp, buf, sizeof(buf), &pos); + if (ret < 0) + batt_dbg(" %s: write file fail %s\n", __func__, address); + + else if (!strcmp(address, BATT_CAPACITY)) + gas_gauge->stored_capacity = data; + + if(config_data->debug) + { + pos = 0; + ret = vfs_read(fp, buf_r, sizeof(buf_r), &pos); + if (ret < 0) + batt_dbg(" %s: read file fail %s\n", __func__, address); + + batt_dbg("%s: read: %s\n", __func__, buf_r); + + ret = kstrtoint(buf_r, 10, &val); + if (ret < 0) + batt_dbg(" %s: fail to get int from %s\n", __func__, address); + } + + set_fs(fs); + + filp_close(fp, NULL); + + return ret; +#endif +} +#if 0 +static int bmu_check_file(char * address) +{ + struct file *fp = NULL; + + fp = filp_open(address, O_RDONLY, 0644); + if (IS_ERR(fp)) + { + batt_dbg(" %s: open file fail %s\n", __func__, address); + return -1; + } + return 0; +} +#endif +#endif + +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- +//---------------------------------------------------------------------------- + +/**************************************************************************** + * Description: + * oz8806_write_byte_pec + * Parameters: + * //addr: oz8806 slave address + * index: oz8806 operation register + * data: write data + * Return: + * see I2C_STATUS_XXX define + ****************************************************************************/ +#if 0 +static int32_t oz8806_write_byte_pec(uint8_t addr,uint8_t index, uint8_t data) +{ + int32_t ret; + + uint8_t pec_data = num_0; + uint16_t temp = num_0; + pec_data =pec_calculate(pec_data,addr); + pec_data =pec_calculate(pec_data,index); + pec_data =pec_calculate(pec_data,data); + temp = pec_data; + temp <<= 8; + temp |= data; + ret = i2c_smbus_write_word_data(parameter->client, index,temp); + return ret; +} +#endif + +/**************************************************************************** + * Description: + * read oz8806 operation register + * Parameters: + * index: oz8806 operation register + * buf: pointer to buffer to store read data + * Return: + * see I2C_STATUS_XXX define + ****************************************************************************/ +static int32_t oz8806_read_byte(uint8_t index) +{ + int32_t ret; + uint8_t pec_data = num_0; + uint8_t temp; + + + if(num_0) + { + ret = i2c_smbus_read_word_data(parameter->client, index); + if(ret < num_0 ) return ret; + pec_data =pec_calculate(pec_data,0x5e); + pec_data =pec_calculate(pec_data,index); + pec_data =pec_calculate(pec_data,0x5f); + temp = (uint8_t)(ret & 0xff); + pec_data =pec_calculate(pec_data,temp); + temp = (uint8_t)(ret >> 8); + + if(pec_data != temp) + { + batt_dbg("33333333333333333 BMT_fgu_read_byte\n"); + batt_dbg("33333333333333333 pec_data is %d\n",pec_data); + batt_dbg("33333333333333333 temp is %d\n",temp); + return -num_1; + } + + return ret; + } + else + { + ret = i2c_smbus_read_byte_data(parameter->client, index); + return ret; + } +} + +/**************************************************************************** + * Description: + * write oz8806 operation register + * Parameters: + * index: oz8806 operation register + * data: write data + * Return: + * see I2C_STATUS_XXX define + ****************************************************************************/ +static int32_t oz8806_write_byte(uint8_t index, uint8_t data) +{ + int32_t ret; + uint8_t pec_data = num_0; + uint16_t temp = num_0; + + if(oz8806_pec_check) + { + pec_data =pec_calculate(pec_data,0x5e); + pec_data =pec_calculate(pec_data,index); + pec_data =pec_calculate(pec_data,data); + temp = pec_data; + temp <<= 8; + temp |= data; + ret = i2c_smbus_write_word_data(parameter->client, index,temp); + return ret; + } + else + { + ret = i2c_smbus_write_byte_data(parameter->client, index,data); + return ret; + } +} + +/**************************************************************************** + * Description: + * read oz8806 operation register word + * Parameters: + * index: oz8806 operation register + * Return: + * see I2C_STATUS_XXX define + ****************************************************************************/ +static int32_t oz8806_read_word(uint8_t index) +{ + int32_t ret = num_0; + int32_t ret1 = num_0; + uint8_t temp; + uint8_t pec_data = num_0; + + if(num_0) + { + ret = i2c_smbus_read_word_data(parameter->client, index); + if(ret < num_0) return ret; + pec_data =pec_calculate(pec_data,0x5e); + pec_data =pec_calculate(pec_data,index); + pec_data =pec_calculate(pec_data,0x5f); + temp = (uint8_t)(ret & 0xff); + pec_data =pec_calculate(pec_data,temp); + temp = (uint8_t)(ret >> 8); + + if(pec_data != temp) + { + batt_dbg("index 0x%02x\n",index); + batt_dbg("0x%02x\n",(uint8_t)(ret & 0xff)); + batt_dbg("33333333333333333 BMT_fgu_read_word num_1\n"); + batt_dbg("33333333333333333 pec_data is 0x%02x\n",pec_data); + batt_dbg("33333333333333333 temp is 0x%02x\n",temp); + + batt_dbg("33333333333333333 ret is 0x%02x\n",ret); + return -num_1; + } + + ret1 = i2c_smbus_read_word_data(parameter->client, (index + num_1)); + if(ret1 < num_0) return ret1; + + pec_data = num_0; + pec_data =pec_calculate(pec_data,0x5e); + pec_data =pec_calculate(pec_data,(index +num_1)); + pec_data =pec_calculate(pec_data,0x5f); + temp = (uint8_t)(ret1 & 0xff); + pec_data =pec_calculate(pec_data,temp); + temp = (uint8_t)(ret1 >> 8); + + if(pec_data != temp) + { + batt_dbg("33333333333333333 BMT_fgu_read_word 2\n"); + batt_dbg("33333333333333333 pec_data is %d\n",pec_data); + batt_dbg("33333333333333333 temp is %d\n",temp); + return -num_1; + } + + ret1 <<= 8; + ret1 |= (uint8_t)(0xff & ret); + return ret1; + } + else + { + ret = i2c_smbus_read_word_data(parameter->client, index); + return ret; + } +} + +/**************************************************************************** + * Description: + * write oz8806 operation register + * Parameters: + * index: oz8806 operation register + * data: write data + * Return: + * see I2C_STATUS_XXX define + ****************************************************************************/ +static int32_t oz8806_write_word( uint8_t index, uint16_t data) +{ + int32_t ret; + uint8_t pec_data = num_0; + uint16_t temp = num_0; + + + if(oz8806_pec_check) + { + + pec_data =pec_calculate(pec_data,0x5e); + pec_data =pec_calculate(pec_data,index); + pec_data =pec_calculate(pec_data,(uint8_t)data); + temp = pec_data; + temp <<= 8; + temp |= (uint8_t)data; + ret = i2c_smbus_write_word_data(parameter->client, index,temp); + + if(ret < num_0) return ret; + pec_data = num_0; + + pec_data =pec_calculate(pec_data,0x5e); + pec_data =pec_calculate(pec_data,(index +num_1)); + pec_data =pec_calculate(pec_data,(uint8_t)(data>>8)); + temp = pec_data; + temp <<= 8; + temp |= (uint8_t)(data >> 8); + ret = i2c_smbus_write_word_data(parameter->client, (index +num_1),temp); + + return ret; + } + else + { + ret = i2c_smbus_write_word_data(parameter->client, index,data); + return ret; + } +} + +/**************************************************************************** + * Description: + * read oz8806 cell voltage + * Parameters: + * voltage: cell voltage in mV, range from -5120mv to +5120mv + * Return: + * see I2C_STATUS_XXX define + ****************************************************************************/ +static int32_t oz8806_cell_voltage_read(int32_t *voltage) +{ + int32_t ret; + int32_t data; + + ret = oz8806_read_word(OZ8806_OP_CELL_VOLT); + + if (ret < num_0) return ret; + + data = ret & 0xFFFF; + data = (int16_t)data / 16; // >> 4 and keep sign bit + + data = data * parameter->config->fVoltLSB; //fVoltLSB = 250 (2.5 mV) + data = data / num_100; //fVoltLSB = 250 (2.5 mV) + + if (data > 5117 || data < 0) + { + batt_dbg("%s:data: %d, reg:%x\n", __func__, data, ret); + return -1; + } + batt_info->i2c_error_times = 0; + *voltage = data; + + return ret; +} + +/**************************************************************************** + * Description: + * read oz8806 cell OCV(open circuit voltage) + * Parameters: + * voltage: voltage in mV, range from -5120mv to +5120mv with 2.5mV LSB + * Return: + * see I2C_STATUS_XXX define + ****************************************************************************/ +static int32_t oz8806_ocv_voltage_read(int32_t *voltage) +{ + int32_t ret; + int32_t data; + + ret = oz8806_read_word(OZ8806_OP_OCV_LOW); + if (ret < num_0) return ret; + + data = ret & 0xFFFF; + data = (int16_t)data / 16; // >> 4 and keep sign bit + + data = data * parameter->config->fVoltLSB; //fVoltLSB = 250 (2.5 mV) + data = data / num_100; //fVoltLSB = 250 (2.5 mV) + + if (data > 5117 || data < 0) + { + batt_dbg("%s:data: %d, reg:%x\n", __func__, data, ret); + return -1; + } + + *voltage = data; + + return ret; +} + +/**************************************************************************** + * Description: + * read oz8806 current + * Parameters: + * current: in mA, range is +/-64mv / Rsense with 7.8uv LSB / Rsense + * Return: + * see I2C_STATUS_XXX define + ****************************************************************************/ +static int32_t oz8806_current_read(int32_t *data) + +{ + int32_t ret; + int32_t temp; + + ret = oz8806_read_word(OZ8806_OP_CURRENT); + + if (ret < num_0) return ret; + + temp = ret & 0xFFFF; + temp = (int16_t)temp * parameter->config->dbCurrLSB; //dbCurrLSB = 391 (3.90625 mA) + temp = (temp * 10 / parameter->config->fRsense); + temp /= 4; + + batt_info->i2c_error_times = 0; + *data = temp; + return ret; +} + +/**************************************************************************** + * Description: + * read oz8806 temp + * Parameters: + * voltage: voltage value in mV, range is +/- 5120mV with 2.5mV LSB + * Return: + * see I2C_STATUS_XXX define + ****************************************************************************/ +int32_t oz8806_temp_read(int32_t *voltage) +{ + int32_t ret; + int32_t data; + + ret = oz8806_read_word(OZ8806_OP_CELL_TEMP); + if (ret < num_0) return ret; + + //batt_dbg("1111111111111 regeister is 0x%02x\n",ret); + + data = ret & 0xFFFF; + data = (int16_t)data / 16; // >> 4 and keep sign bit + + data = data * parameter->config->fVoltLSB; //fVoltLSB = 250 (2.5 mV) + data = data / num_100; //fVoltLSB = 250 (2.5 mV) + + if (data > 5117 || data < 0) + { + batt_dbg("%s:data: %d, reg:%x\n", __func__, data, ret); + return -1; + } + + *voltage = data; + + //batt_dbg("1111111111111 voltage is %d\n",*voltage); + + return ret; +} + +/**************************************************************************** + * Description: + * read oz8806 CAR + * Parameters: + * car: in mAHr, range is +/- 163840 uvHr/ Rsense with 5 uvHr LSB / Rsense + * Return: + * see I2C_STATUS_XXX define + ****************************************************************************/ +static int32_t oz8806_car_read(int32_t *car) +{ + int32_t ret; + int32_t data; + + + ret = oz8806_read_word(OZ8806_OP_CAR); + if (ret < num_0) return ret; + + data = (int16_t)ret; + data = data * parameter->config->dbCARLSB; + data = data * 1000 / parameter->config->fRsense; + + batt_info->i2c_error_times = 0; + *car = (int16_t)data; + return ret; +} + +/**************************************************************************** + * Description: + * write oz8806 CAR + * Parameters: + * data: in mAHr, range is +/- 163840 uvHr/ Rsense with 5 uvHr LSB / Rsense + * Return: + * see I2C_STATUS_XXX define + ****************************************************************************/ +static int32_t oz8806_car_write(int32_t data) +{ + int32_t temp; + int32_t ret; + + temp = data; + temp = (temp * parameter->config->fRsense) / parameter->config->dbCARLSB; //transfer to CAR + temp /= 1000; + ret = oz8806_write_word(OZ8806_OP_CAR,(uint16_t)temp); + + return ret; +} + +/**************************************************************************** + * Description: + * read oz8806 board offset + * Parameters: + * data: in mA, +/- 8mV / Rsense with 7.8uV / Rsense + * Return: + * see I2C_STATUS_XXX define + ****************************************************************************/ +static int32_t oz8806_board_offset_read(int32_t *data) +{ + int32_t ret; + int32_t temp; + + + ret = oz8806_read_word(OZ8806_OP_BOARD_OFFSET); + if (ret < num_0) return ret; + + + temp = (int16_t)ret; + temp = temp & 0x7ff; + if(temp & 0x0400)// if bit[10] is 1, it's negative + temp = temp |0xfffffc00; + temp = temp * 78; + temp = temp * 100/ config_data->fRsense; + + *data = (int16_t)temp; + return ret; +} + +/**************************************************************************** + * Description: + * write oz8806 board offset + * Parameters: + * data: the data will be wrriten + * Return: + * see I2C_STATUS_XXX define + ****************************************************************************/ +static int32_t oz8806_board_offset_write(int32_t data) +{ + int32_t ret; + int32_t over_data; + + if(data > num_0){ + data = data * config_data->fRsense * num_10 / 78; + data /= 1000; + if(data > 0x3ff)data = 0x3ff; + ret = oz8806_write_word(OZ8806_OP_BOARD_OFFSET,(uint16_t)data); + } + else if(data < num_0){ + over_data = (-1024 * 78 * 1000) / (config_data->fRsense * num_10); + if(data < over_data) + data = over_data; + data = data * config_data->fRsense * num_10 / 78; + data /= 1000; + data = (data & 0x07ff) | 0x0400; + ret = oz8806_write_word(OZ8806_OP_BOARD_OFFSET,(uint16_t)data); + } + else + ret = oz8806_write_word(OZ8806_OP_BOARD_OFFSET,num_0); + + return ret; +} + +/***************************************************************************** + * Description: + * wrapper function for operation register byte reading + * Parameters: + * index: register index to be read + * *dat: buffer to store data read back + * Return: + * I2C_STATUS_OK if read success, else inidcate error + *****************************************************************************/ +static int32_t afe_register_read_byte(uint8_t index, uint8_t *dat) +{ + int32_t ret; + uint8_t i; + + for(i = num_0; i < RETRY_CNT; i++){ + ret = oz8806_read_byte(index); + if(ret >=num_0) break; + } + if(i >= RETRY_CNT){ + batt_info->i2c_error_times++; + batt_dbg("yyyy. %s failed, ret %d\n", __func__, ret); + return ret; + } + + *dat = (uint8_t)ret; + + return ret; +} + +/***************************************************************************** + * Description: + * wrapper function for operation register byte writing + * Parameters: + * index: register index to be read + * dat: write data + * Return: + * see I2C_STATUS_XXX define + *****************************************************************************/ +static int32_t afe_register_write_byte(uint8_t index, uint8_t dat) +{ + int32_t ret; + uint8_t i; + + for(i = num_0; i < RETRY_CNT; i++){ + ret = oz8806_write_byte(index, dat); + if(ret >= num_0) break; + } + if(i >= RETRY_CNT){ + batt_info->i2c_error_times++; + batt_dbg("yyyy. %s failed, ret %d\n", __func__, ret); + return ret; + } + + return ret; +} + +/***************************************************************************** + * Description: + * wrapper function for operation register word reading + * Parameters: + * index: register index to be read + * *dat: buffer to store data read back + * Return: + * I2C_STATUS_OK if read success, else inidcate error + *****************************************************************************/ +static int32_t afe_register_read_word(uint8_t index, uint16_t *dat) +{ + int32_t ret; + uint8_t i; + + for(i = num_0; i < RETRY_CNT; i++){ + ret = oz8806_read_word(index); + if(ret >= num_0) break; + } + if(i >= RETRY_CNT){ + batt_info->i2c_error_times++; + batt_dbg("yyyy. %s failed, ret %d\n", __func__, ret); + return ret; + } + + *dat = (uint16_t)ret; + + return ret; +} + +/***************************************************************************** + * Description: + * wrapper function for operation register word writing + * Parameters: + * index: register index to be read + * dat: write data + * Return: + * see I2C_STATUS_XXX define + *****************************************************************************/ +#if 0 +static int32_t afe_register_write_word(uint8_t index, uint16_t dat) +{ + int32_t ret; + uint8_t i; + + for(i = num_0; i < RETRY_CNT; i++){ + ret = oz8806_write_word(index, dat); + if(ret >= num_0) break; + } + if(i >= RETRY_CNT){ + batt_info->i2c_error_times++; + batt_dbg("yyyy. %s failed, ret %d\n", __func__, ret); + return ret; + } + + return ret; +} +#endif + +/***************************************************************************** + * Description: + * wrapper function to read cell voltage + * Parameters: + * vol: buffer to store voltage read back + * Return: + * see I2C_STATUS_XXX define + * Note: + * it's acceptable if one or more times reading fail + *****************************************************************************/ +int32_t afe_read_cell_volt(int32_t *voltage) +{ + int32_t ret; + uint8_t i; + int32_t buf; + + for(i = num_0; i < RETRY_CNT; i++){ + ret = oz8806_cell_voltage_read(&buf); + if(ret >= num_0) + break; + msleep(5); + } + if(i >= RETRY_CNT){ + batt_info->i2c_error_times++; + batt_dbg("yyyy. %s failed, ret %d\n", __func__, ret); + return ret; + } + if(oz8806_cell_num > num_1) + { + //*voltage = buf * num_1000 / res_divider_ratio; + batt_dbg("read from register %d mv \n",buf); + *voltage = buf * res_divider_ratio / num_1000; // res_divider_ratio = ((Rpull + Rdown) / Rdown) * 1000 + *voltage /= oz8806_cell_num; + } + else + *voltage = buf; + return ret; +} + +/***************************************************************************** + * Description: + * wrapper function to read cell ocv voltage + * Parameters: + * vol: buffer to store voltage read back + + * Return: + * see I2C_STATUS_XXX define + * Note: + * it's acceptable if one or more times reading fail + *****************************************************************************/ +static int32_t afe_read_ocv_volt(int32_t *voltage) +{ + int32_t ret; + uint8_t i; + int32_t buf; + + for(i = num_0; i < RETRY_CNT; i++){ + ret = oz8806_ocv_voltage_read(&buf); + if(ret >= num_0) + break; + } + if(i >= RETRY_CNT){ + batt_info->i2c_error_times++; + batt_dbg("yyyy. %s failed, ret %d\n", __func__, ret); + return ret; + } + + if(oz8806_cell_num > num_1) + { + *voltage = buf * num_1000 / res_divider_ratio; + *voltage /= oz8806_cell_num; + } + else + *voltage = buf; + + return ret; +} + +/***************************************************************************** + * Description: + * wrapper function to read current + * Parameters: + * dat: buffer to store current value read back + * Return: + * see I2C_STATUS_XXX define + * Note: + * it's acceptable if one or more times reading fail + *****************************************************************************/ +int32_t afe_read_current(int32_t *dat) +{ + int32_t ret; + uint8_t i; + int32_t buf; + + for(i = num_0; i < RETRY_CNT; i++){ + ret = oz8806_current_read(&buf); + if(ret >= num_0) + break; + msleep(5); + } + if(i >= RETRY_CNT){ + batt_info->i2c_error_times++; + batt_dbg("yyyy. %s failed, ret %d\n", __func__, ret); + return ret; + } + *dat = buf; + return ret; +} + +/***************************************************************************** + * Description: + * wrapper function to read CAR(accummulated current calculation) + * Parameters: + * dat: buffer to store current value read back + * Return: + * see I2C_STATUS_XXX define + * Note: + * it's acceptable if one or more times reading fail + *****************************************************************************/ +static int32_t afe_read_car(int32_t *dat) +{ + int32_t ret; + uint8_t i; + int32_t buf; + + for(i = num_0; i < RETRY_CNT; i++){ + ret = oz8806_car_read(&buf); + if(ret >= num_0) + break; + msleep(5); + } + if(i >= RETRY_CNT){ + batt_info->i2c_error_times++; + batt_dbg("yyyy. %s failed, ret %d\n", __func__, ret); + return ret; + } + *dat = buf; + return ret; +} + +/***************************************************************************** + * Description: + * wrapper function to write car + * Parameters: + * dat: buffer to store current value read back + * Return: + * see I2C_STATUS_XXX define + * Note: + * it's acceptable if one or more times reading fail + *****************************************************************************/ +static int32_t afe_write_car(int32_t date) +{ + int32_t ret; + uint8_t i; + + + for(i = num_0; i < RETRY_CNT; i++){ + ret = oz8806_car_write(date); + if(ret >= num_0) break; + msleep(5); + } + if(i >= RETRY_CNT){ + batt_info->i2c_error_times++; + batt_dbg("yyyy. %s failed, ret %d\n", __func__, ret); + return ret; + } + return ret; +} + +/***************************************************************************** + * Description: + * wrapper function to write board offset + * Parameters: + * dat: buffer to store current value read back + * Return: + * see I2C_STATUS_XXX define + * Note: + * it's acceptable if one or more times reading fail + *****************************************************************************/ +static int32_t afe_write_board_offset(int32_t date) +{ + int32_t ret; + uint8_t i; + + + for(i = num_0; i < RETRY_CNT; i++){ + ret = oz8806_board_offset_write(date); + if(ret >= num_0) break; + } + if(i >= RETRY_CNT){ + batt_info->i2c_error_times++; + batt_dbg("yyyy. %s failed, ret %d\n", __func__, ret); + return ret; + } + return ret; +} + +/***************************************************************************** + * Description: + * wrapper function to read board offset + * Parameters: + * dat: buffer to store current value read back + * Return: + * see I2C_STATUS_XXX define + * Note: + * it's acceptable if one or more times reading fail + *****************************************************************************/ +static int32_t afe_read_board_offset(int32_t *dat) +{ + int32_t ret; + uint8_t i; + int32_t buf; + + for(i = num_0; i < RETRY_CNT; i++){ + ret = oz8806_board_offset_read(&buf); + if(ret >= num_0) + break; + } + if(i >= RETRY_CNT){ + batt_info->i2c_error_times++; + batt_dbg("yyyy. %s failed, ret %d\n", __func__, ret); + return ret; + } + *dat = buf; + return ret; +} + +/***************************************************************************** + * Description: + * wrapper function to read temp + * Parameters: + * dat: buffer to store current value read back + * Return: + * see I2C_STATUS_XXX define + * Note: + * it's acceptable if one or more times reading fail + *****************************************************************************/ +static int32_t afe_read_cell_temp(int32_t *data) +{ + int32_t ret; + uint8_t i; + int32_t temp; + int32_t buf; + + if (gas_gauge->ext_temp_measure) + return num_0; + + for(i = num_0; i < RETRY_CNT; i++){ + ret = oz8806_temp_read(&buf); + if(ret >= num_0) break; + msleep(5); + } + if(i >= RETRY_CNT){ + batt_info->i2c_error_times++; + batt_dbg("yyyy. %s failed, ret %d\n", __func__, ret); + return ret; + } + + if((buf >= config_data->temp_ref_voltage)|| (buf <= num_0)){ + *data = num_25; + } + else{ + temp = buf * config_data->temp_pull_up; + temp = temp / (config_data->temp_ref_voltage - buf); + *data = one_latitude_table(parameter->cell_temp_num,parameter->temperature,temp); + } + //batt_dbg("1111111111111 r is %d\n",temp); + return ret; +} + +/***************************************************************************** + * Description: + * pec_calculate + * Parameters: + * ucCrc: source + * ucData: data + * Return: + * crc data + * Note: + * + *****************************************************************************/ +static uint8_t pec_calculate (uint8_t ucCrc, uint8_t ucData) +{ + uint8_t j; + for (j = num_0x80; j != num_0; j >>= num_1) + { + if((ucCrc & num_0x80) != num_0) + { + ucCrc <<= num_1; + ucCrc ^= num_0x07; + } + else + ucCrc <<= num_1; + if (ucData & j) + ucCrc ^= num_0x07; + } + return ucCrc; +} + +#if 0 +/***************************************************************************** + * Description: + * i2c_read_byte + * Parameters: + * Return: + * Note: + * + *****************************************************************************/ +static int32_t i2c_read_byte(uint8_t addr,uint8_t index,uint8_t *data) +{ + int32_t ret = 0; + #if 0 + struct i2c_adapter *adap=parameter->client->adapter; + struct i2c_msg msgs[num_2]; + int scl_rate= num_100 * num_1000; + msgs[num_0].addr = addr; + msgs[num_0].flags = parameter->client->flags; + msgs[num_0].len = num_1; + msgs[num_0].buf = &index; + msgs[num_0].scl_rate = scl_rate; + + msgs[num_1].addr = addr; + msgs[num_1].flags = parameter->client->flags | I2C_M_RD; + msgs[num_1].len = num_1; + msgs[num_1].buf = (char *)data; + msgs[num_1].scl_rate = scl_rate; + + ret = i2c_transfer(adap, msgs, num_2); + + #endif + return (ret == num_2)? num_0 : ret; +} + +/***************************************************************************** + * Description: + * i2c_write_byte + * Parameters: + * Return: + * Note: + * + *****************************************************************************/ +static int32_t i2c_write_byte(uint8_t addr,uint8_t index,uint8_t data) +{ + int32_t ret = 0; + #if 0 + struct i2c_adapter *adap=parameter->client->adapter; + struct i2c_msg msg; + int32_t scl_rate = num_100 * num_1000; + + char tx_buf[num_3]; + tx_buf[num_0] = index; + tx_buf[num_1] = data; + + msg.addr = addr; + msg.flags = parameter->client->flags; + msg.len = num_2; + msg.buf = (char *)tx_buf; + msg.scl_rate = scl_rate; + + if(oz8806_pec_check) + { + msg.len = num_3; + tx_buf[num_2] = num_0; + tx_buf[num_2] =pec_calculate(tx_buf[num_2],(addr<sCaMAH); + batt_info->fRCPrev = batt_info->sCaMAH; + batt_info->fRC = batt_info->sCaMAH; + } +} +EXPORT_SYMBOL(bmu_reinit); + +int bmulib_init(void) +{ + int ret = 0; +#if !defined(OZ8806_API) + uint32_t byte_num = 0; + uint8_t * p = NULL; +#endif + + bmt_dbg("%s\n", __func__); + + //NOTICE: Don't change the sequence of calling below functions + + byte_num = sizeof(config_data_t) + sizeof(bmu_data_t) + sizeof(gas_gauge_t); + p = kzalloc(byte_num, GFP_KERNEL); + bmu_kernel_memaddr = (unsigned long)p; + + if (!bmu_kernel_memaddr) + goto fail_mem; + + parameter_customer.client = oz8806_get_client(); + if (!parameter_customer.client) + goto fail_i2c; + bmt_dbg("i2c address:%x\n", parameter_customer.client->addr); + //struct oz8806_data *data = i2c_get_clientdata(parameter_customer.client); + + bmu_init_parameter(¶meter_customer); + + bmu_init_parameter_more(¶meter_customer); + + bmu_init_chip(¶meter_customer); + + oz8806_set_batt_info_ptr(batt_info); + + oz8806_set_gas_gauge(gas_gauge); + + oz8806_register_bmu_callback(&bmu_polling_loop, &bmu_wake_up_chip, &bmu_power_down_chip, + &charge_end_process, &discharge_end_process, + &oz8806_temp_read, &afe_read_current, &afe_read_cell_volt); + + return ret; +fail_mem: + bmt_dbg("%s: bmu_kernel_memaddr error\n", __func__); + +fail_i2c: + bmt_dbg("%s: oz8806 i2c register error\n", __func__); + return -1; +} + +void bmulib_exit(void) +{ + batt_dbg("%s\n", __func__); + unregister_bmu_callback(); + +#if !defined(OZ8806_API) + if (bmu_kernel_memaddr) + kfree((int8_t *)bmu_kernel_memaddr); + bmu_kernel_memaddr = 0; +#endif +} + diff --git a/drivers/power/sd77426_fg_mmi/calculate.o b/drivers/power/sd77426_fg_mmi/calculate.o new file mode 100644 index 000000000000..b7632350c5cf Binary files /dev/null and b/drivers/power/sd77426_fg_mmi/calculate.o differ diff --git a/drivers/power/sd77426_fg_mmi/oz8806_battery.c b/drivers/power/sd77426_fg_mmi/oz8806_battery.c new file mode 100644 index 000000000000..01707909d59c --- /dev/null +++ b/drivers/power/sd77426_fg_mmi/oz8806_battery.c @@ -0,0 +1,1658 @@ +/***************************************************************************** +* Copyright(c) BMT, 2021. All rights reserved. +* +* BMT [oz8806] Source Code Reference Design +* File:[bmulib.c] +* +* This Source Code Reference Design for BMT [oz8806] access +* ("Reference Design") is solely for the use of PRODUCT INTEGRATION REFERENCE ONLY, +* and contains confidential and privileged information of BMT International +* Limited. BMT shall have no liability to any PARTY FOR THE RELIABILITY, +* SERVICEABILITY FOR THE RESULT OF PRODUCT INTEGRATION, or results from: (i) any +* modification or attempted modification of the Reference Design by any party, or +* (ii) the combination, operation or use of the Reference Design with non-BMT +* Reference Design. Use of the Reference Design is at user's discretion to qualify +* the final work result. +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//you must add these here for BMT +#include "parameter.h" +#include "table.h" +#include "battery_config.h" +#include "oz8806_regdef.h" + +/***************************************************************************** +* static variables/functions section +****************************************************************************/ +static int fg_hw_init_done = 0; +static uint8_t bmu_init_done = 0; +static int oz8806_suspend = 0; +static DEFINE_MUTEX(update_mutex); +static bmu_data_t *batt_info_ptr = NULL; +static gas_gauge_t *gas_gauge_ptr = NULL; +static uint8_t charger_finish = 0; +static unsigned long ic_wakeup_time = 0; +static int init_soc = 0; +static void (*bmu_polling_loop_callback)(void) = NULL; +static void (*bmu_wake_up_chip_callback)(void) = NULL; +static void (*bmu_power_down_chip_callback)(void) = NULL; +static void (*bmu_charge_end_process_callback)(void) = NULL; +static void (*bmu_discharge_end_process_callback)(void) = NULL; +static int32_t (*oz8806_temp_read_callback)(int32_t *voltage) = NULL; +static int32_t (*oz8806_current_read_callback)(int32_t *voltage) = NULL; +static int32_t (*oz8806_voltage_read_callback)(int32_t *voltage) = NULL; + +static int oz8806_update_batt_info(struct oz8806_data *data); +static int oz8806_update_batt_temp(struct oz8806_data *data); +static int32_t oz8806_write_byte(struct oz8806_data *data, uint8_t index, uint8_t dat); +static int32_t oz8806_read_byte(struct oz8806_data *data, uint8_t index, uint8_t *dat); + +static struct oz8806_data *the_oz8806 = NULL; +static int8_t adapter_status = O2_CHARGER_BATTERY; +static int32_t rsoc_pre = 0; +#ifdef EXT_THERMAL_READ +static uint8_t ext_thermal_read = 1; +#else +static uint8_t ext_thermal_read = 0; +#endif +static int capacity_init_ok = 0; + +static int save_capacity = INIT_CAP; + +//-1 means not ready +int32_t age_soh = -1; +int32_t cycle_soh = -1; +#define NAME "oz8806" +#define BATT_PHY "bms" + +static enum power_supply_property oz8806_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_NOW, + POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, + POWER_SUPPLY_PROP_ENERGY_NOW, + //POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW, + //POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, + //POWER_SUPPLY_PROP_TIME_TO_FULL_NOW, +}; + +/*-------------------------------------------------------------------------*/ +/***************************************************************************** +* Description: +* below function is linux power section +* Parameters: +* description for each argument, new argument starts at new line +* Return: +* what does this function returned? +*****************************************************************************/ +/*-------------------------------------------------------------------------*/ + +static int oz8806_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct oz8806_data *data = (struct oz8806_data *)power_supply_get_drvdata(psy); + + switch (psp) { + + case POWER_SUPPLY_PROP_STATUS: + + if (adapter_status == O2_CHARGER_BATTERY) + { + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; /*discharging*/ + } + else if(adapter_status == O2_CHARGER_USB || + adapter_status == O2_CHARGER_AC ) + { + if (data->batt_info.batt_soc == 100) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_CHARGING; /*charging*/ + + } + else + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = data->batt_info.batt_voltage * 1000; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = bmu_init_done; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = data->batt_info.batt_current * 1000; + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = data->batt_info.batt_soc; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + break; + case POWER_SUPPLY_PROP_TEMP: + val->intval = data->batt_info.batt_temp * 10; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + val->intval = data->batt_info.batt_fcc_data; + break; + case POWER_SUPPLY_PROP_CHARGE_NOW: + val->intval = data->batt_info.batt_capacity; + break; + case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: + val->intval = data->batt_info.batt_fcc_data; + break; + case POWER_SUPPLY_PROP_ENERGY_NOW: + val->intval = data->batt_info.batt_capacity; + break; + default: + return -EINVAL; + } + + return 0; +} + +static void oz8806_external_power_changed(struct power_supply *psy) +{ + struct oz8806_data *data = (struct oz8806_data *)power_supply_get_drvdata(psy); + + power_supply_changed(data->bat); + cancel_delayed_work(&data->work); + schedule_delayed_work(&data->work, 0); +} + +static void oz8806_powersupply_init(struct oz8806_data *data) +{ + data->bat_desc.name = BATT_PHY; + data->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; + data->bat_desc.properties = oz8806_battery_props; + data->bat_desc.num_properties = ARRAY_SIZE(oz8806_battery_props); + data->bat_desc.get_property = oz8806_battery_get_property; + data->bat_desc.external_power_changed = oz8806_external_power_changed; + data->bat_desc.no_thermal = 1; +} + +/***************************************************************************** + *write 0x20 into register 0x09 + *example:echo 0920 > /sys/class/i2c-dev/i2c-2/device/2-002f/registers + *****************************************************************************/ +static ssize_t oz8806_register_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t _count) +{ + char data[num_3]; + uint8_t address; + uint8_t value = 0; + char *endp; + int len; + struct oz8806_data *oz8806; + + len = strlen(buf); + if(len < 5) + return -EINVAL; + + if (&(the_oz8806->myclient->dev) == dev) + oz8806 = dev_get_drvdata(dev); + else //this device is the private device of power supply + oz8806 = dev_get_drvdata(dev->parent); + + data[num_0] = buf[num_0]; + data[num_1] = buf[num_1]; + data[num_2] = num_0; + + address = simple_strtoul(data, &endp, 16); + + if (address != OZ8806_OP_PEC_CTRL + && address != OZ8806_OP_CTRL + && address != OZ8806_OP_CAR + && address != OZ8806_OP_CAR+1 + && address != OZ8806_OP_BOARD_OFFSET + && address != OZ8806_OP_BOARD_OFFSET+1) + { + bmt_dbg("register[0x%.2x] is read-only\n", address); + return _count; + } + + data[num_0] = buf[num_2]; + data[num_1] = buf[num_3]; + data[num_2] = num_0; + + value = simple_strtoul(data, &endp, 16); + + oz8806_write_byte(oz8806, address, value); + + bmt_dbg("write 0x%.2x into register[0x%.2x]\n", value, address); + + return _count; +} + +/* + example:cat /sys/class/i2c-dev/i2c-2/device/2-002f/registers +*/ +static ssize_t oz8806_register_show(struct device *dev, struct device_attribute *attr,char *buf) +{ + struct oz8806_data *oz8806; + u8 i = 0; + u8 data = 0; + int result = 0; + + if (&(the_oz8806->myclient->dev) == dev) + oz8806 = dev_get_drvdata(dev); + else //this device is the private device of power supply + oz8806 = dev_get_drvdata(dev->parent); + + //CHIP ID and Reversion + oz8806_read_byte(oz8806, OZ8806_OP_IDREV, &data); + result += sprintf(buf + result, "[0x%.2x] = 0x%.2x\n", OZ8806_OP_IDREV, data); + + //I2C config register + oz8806_read_byte(oz8806, OZ8806_OP_I2CCONFIG, &data); + result += sprintf(buf + result, "[0x%.2x] = 0x%.2x\n", OZ8806_OP_I2CCONFIG, data); + + for (i=0x00; i<=0x1a; i++) + { + oz8806_read_byte(oz8806, i, &data); + result += sprintf(buf + result, "[0x%.2x] = 0x%.2x\n", i, data); + } + + return result; +} + +// chip id: 0x38 +static ssize_t oz8806_chip_id_show(struct device *dev, struct device_attribute *attr,char *buf) +{ + struct oz8806_data *oz8806; + u8 data = 0; + int result = 0; + + if (&(the_oz8806->myclient->dev) == dev) + oz8806 = dev_get_drvdata(dev); + else //this device is the private device of power supply + oz8806 = dev_get_drvdata(dev->parent); + + //CHIP ID and Reversion + oz8806_read_byte(oz8806, OZ8806_OP_IDREV, &data); + result = sprintf(buf, "0x%.2x = 0x%.2x\n", OZ8806_OP_IDREV, data); + return result; +} + +static ssize_t oz8806_debug_show(struct device *dev, struct device_attribute *attr,char *buf) +{ + return sprintf(buf,"%d\n", config_data.debug); +} +/***************************************************************************** + * Description: + * oz8806_debug_store + * Parameters: + * write example: echo 1 > debug ---open debug + *****************************************************************************/ +static ssize_t oz8806_debug_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t _count) +{ + int val; + if (kstrtoint(buf, 10, &val)) + return -EINVAL; + + if(val == 1) + { + config_data.debug = 1; + bmt_dbg("DEBUG ON \n"); + } + else if (val == 0) + { + config_data.debug = 0; + bmt_dbg("DEBUG CLOSE \n"); + } + else + { + bmt_dbg("invalid command\n"); + return -EINVAL; + } + + return _count; +} +static ssize_t oz8806_save_capacity_show(struct device *dev, struct device_attribute *attr,char *buf) +{ + if (capacity_init_ok && gas_gauge_ptr && gas_gauge_ptr->stored_capacity) + return sprintf(buf,"%d\n", gas_gauge_ptr->stored_capacity); + + return sprintf(buf,"%d\n", save_capacity); +} + +static ssize_t oz8806_bmu_init_done_show(struct device *dev, struct device_attribute *attr,char *buf) +{ + return sprintf(buf,"%d\n", bmu_init_done); +} +static ssize_t oz8806_bmu_init_done_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t _count) +{ + int val = 0; + if (kstrtoint(buf, 10, &val)) + return -EINVAL; + + bmu_init_done = 0; + ic_wakeup_time = jiffies; + + cancel_delayed_work(&the_oz8806->work); + msleep(1000); + + if(val == 1) + { + bmt_dbg("reinit BMT from ocv\n"); + bmu_reinit(1);//bmu_init_done will be setted + } + else if (val == 0) + { + bmt_dbg("reinit BMT,just wakeup ic\n"); + bmu_reinit(0); + bmu_init_done = 1; + } + else + { + bmt_dbg("invalid command\n"); + return -EINVAL; + } + schedule_delayed_work(&the_oz8806->work, 0); + return _count; +} + +static ssize_t batt_cycle_soh_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int val; + + if (kstrtoint(buf, 10, &val)) + return -EINVAL; + + cycle_soh = val; + accmulate_data_update(); + + batt_dbg("batt_cycle_count: %d ", cycle_soh); + return count; +} + +static ssize_t batt_cycle_soh_show(struct device *dev, struct device_attribute *attr,char *buf) +{ + return sprintf(buf, "%d\n", cycle_soh); +} + +static ssize_t batt_age_soh_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + int val; + + if (kstrtoint(buf, 10, &val)) + return -EINVAL; + + age_soh = val; + + batt_dbg("batt_age: %d ", age_soh); + return count; +} + +static ssize_t batt_age_soh_show(struct device *dev, struct device_attribute *attr,char *buf) +{ + return sprintf(buf, "%d\n", age_soh); +} + +static ssize_t oz8806_name_show(struct device *dev, struct device_attribute *attr,char *buf) +{ + return sprintf(buf, "%s\n", NAME); +} + +static DEVICE_ATTR(registers, S_IRUGO | (S_IWUSR|S_IWGRP), oz8806_register_show, oz8806_register_store); +static DEVICE_ATTR(chip_id, S_IRUGO, oz8806_chip_id_show, NULL); +static DEVICE_ATTR(bmt_debug, S_IRUGO | (S_IWUSR|S_IWGRP), oz8806_debug_show, oz8806_debug_store); +static DEVICE_ATTR(save_capacity, S_IRUGO, oz8806_save_capacity_show, NULL); +static DEVICE_ATTR(bmu_init_done, S_IRUGO| (S_IWUSR|S_IWGRP), oz8806_bmu_init_done_show, oz8806_bmu_init_done_store); +static DEVICE_ATTR(cycle_count, S_IRUGO| (S_IWUSR|S_IWGRP), batt_cycle_soh_show, batt_cycle_soh_store); +static DEVICE_ATTR(age, S_IRUGO| (S_IWUSR|S_IWGRP), batt_age_soh_show, batt_age_soh_store); +static DEVICE_ATTR(name, S_IRUGO, oz8806_name_show, NULL); +static struct attribute *oz8806_attributes[] = { + &dev_attr_registers.attr, + &dev_attr_chip_id.attr, + &dev_attr_bmt_debug.attr, + &dev_attr_bmu_init_done.attr, + &dev_attr_save_capacity.attr, + &dev_attr_cycle_count.attr, + &dev_attr_age.attr, + &dev_attr_name.attr, + NULL, +}; + +static struct attribute_group oz8806_attribute_group = { + .attrs = oz8806_attributes, +}; + +static int oz8806_create_sys(struct device *dev, const struct attribute_group * grp) +{ + int err = 0; + struct power_supply *ply; + + bmt_dbg("BMT_create_sysfs\n"); + ply = power_supply_get_by_name(BATT_PHY); + + if(NULL == dev){ + bmt_dbg("[BATT]: failed to register battery\n"); + return -EINVAL; + } + + if(ply) + { + err = sysfs_create_group(&ply->dev.kobj, grp); + + if (!err) + { + bmt_dbg("creat BMT sysfs group ok\n"); + } + else + { + bmt_dbg("creat BMT sysfs group fail\n"); + err = -EIO; + } + power_supply_put(ply); + } + else + { + bmt_dbg("don't have /sys/class/power_supply/battery dir\n"); + } + return err; +} + +static int32_t oz8806_read_byte(struct oz8806_data *data, uint8_t index, uint8_t *dat) +{ + int32_t ret; + uint8_t i; + struct i2c_client *client = data->myclient; + + for(i = 0; i < 4; i++){ + ret = i2c_smbus_read_byte_data(client, index); + + if(ret >= 0) break; + else + dev_err(&client->dev, "%s: err %d, %d times\n", __func__, ret, i); + } + if(i >= 4) + { + return ret; + } + *dat = (uint8_t)ret; + + return ret; +} + +static int32_t oz8806_write_byte(struct oz8806_data *data, uint8_t index, uint8_t dat) +{ + int32_t ret; + uint8_t i; + struct i2c_client *client = data->myclient; + + for(i = 0; i < 4; i++){ + ret = i2c_smbus_write_byte_data(client, index, dat); + if(ret >= 0) break; + else + dev_err(&client->dev, "%s: err %d, %d times\n", __func__, ret, i); + } + if(i >= 4) + { + return ret; + } + + return ret; +} + +static int32_t oz8806_update_bits(struct oz8806_data *data, uint8_t reg, uint8_t mask, uint8_t dat) +{ + int32_t ret; + uint8_t tmp; + + ret = oz8806_read_byte(data, reg, &tmp); + if (ret < 0) + return ret; + + if ((tmp & mask) != dat) + { + tmp &= ~mask; + tmp |= dat & mask; + return oz8806_write_byte(data, reg, tmp); + + } + else + return 0; +} + +void oz8806_reset_wkuptime(void) +{ + ic_wakeup_time = 0; +} +int oz8806_wakeup_full_power(void) +{ + uint8_t val = 0; + int32_t ret = 0; + struct oz8806_data *data = the_oz8806; + + ret = oz8806_read_byte(data, OZ8806_OP_CTRL, &val); + bmt_dbg("BMT_OP_CTRL: 0x%02x\n", val); + + if ((val & SLEEP_MODE) != 0<<6) + { + ret = oz8806_update_bits(data, OZ8806_OP_CTRL, SLEEP_MODE, 0 << 6); + + ret = oz8806_read_byte(data, OZ8806_OP_CTRL, &val); + + bmt_dbg("BMT_OP_CTRL: 0x%02x after writing\n", val); + + if ((val & SLEEP_MODE) != (0 << 6)) + bmt_dbg("fail to wake up BMT to full power mode\n"); + else { + if (oz8806_get_boot_up_time() < 2000) + ic_wakeup_time = jiffies + msecs_to_jiffies(2000 - oz8806_get_boot_up_time()); + else + ic_wakeup_time = jiffies; + bmt_dbg("wake up BMT to full power mode\n"); + } + } + bmt_dbg("ic_wakeup_time %lu\n",ic_wakeup_time); + + return ret; +} +EXPORT_SYMBOL(oz8806_wakeup_full_power); + +static void discharge_end_fun(struct oz8806_data *data) +{ + //End discharge + //this may jump 2% + if (!gas_gauge_ptr || !batt_info_ptr) + return; + /* + if(batt_info_ptr->fCellTemp < -10) + config_data.discharge_end_voltage = OZ8806_EOD + 350; + else if (batt_info_ptr->fCellTemp < 0) + config_data.discharge_end_voltage = OZ8806_EOD + 300; + else if (batt_info_ptr->fCellTemp < 5) + config_data.discharge_end_voltage = OZ8806_EOD + 200; + else if (batt_info_ptr->fCellTemp < 10) + config_data.discharge_end_voltage = OZ8806_EOD + 100; + else + config_data.discharge_end_voltage = OZ8806_EOD; + +*/ + + if(batt_info_ptr->fVolt < (config_data.discharge_end_voltage - 100)) + { + if(batt_info_ptr->fRSOC == 1) + { + batt_info_ptr->fRSOC = 0; + batt_info_ptr->sCaMAH = data->batt_info.batt_fcc_data / num_100 -1; + discharge_end_process(); + } + else if(batt_info_ptr->fRSOC > 0){ + batt_info_ptr->sCaMAH = batt_info_ptr->fRSOC * data->batt_info.batt_fcc_data / num_100 - 1; + batt_info_ptr->fRSOC--; + } + } +} + +static int check_charger_full(void) +{ + int ret = 0; + + //from charge ic + return ret; +} + +static void charge_end_fun(void) +{ +#define CHG_END_PERIOD_MS (MSEC_PER_SEC * 60) //60 s = 1 minutes +#define FORCE_FULL_MS (MSEC_PER_SEC * 120) //120s = 2 minutes +#define CHG_END_PURSUE_STEP (MSEC_PER_SEC * 30) //30s + + static unsigned int time_accumulation; + static unsigned long start_jiffies; + int oz8806_eoc = 0; + static unsigned long chgr_full_soc_pursue_start; + static unsigned int chgr_full_soc_pursue_accumulation; + +#ifdef ENABLE_10MIN_END_CHARGE_FUN + static unsigned long start_record_jiffies; + static uint8_t start_record_flag = 0; +#endif + + if (!gas_gauge_ptr || !batt_info_ptr) + return; + + if (adapter_status != O2_CHARGER_BATTERY && check_charger_full()) + { + charger_finish = 1; + bmt_dbg("charger is full, enter external charger finish\n"); + goto charger_full; + } + + if(adapter_status == O2_CHARGER_USB) + oz8806_eoc = 100; + else if(adapter_status == O2_CHARGER_AC) + oz8806_eoc = config_data.charge_end_current; + + if((adapter_status == O2_CHARGER_BATTERY) || + (batt_info_ptr->fCurr < DISCH_CURRENT_TH) || + (batt_info_ptr->fCurr > oz8806_eoc)) + { + charger_finish = 0; + time_accumulation = 0; + start_jiffies = 0; + chgr_full_soc_pursue_start = 0; +#ifdef ENABLE_10MIN_END_CHARGE_FUN + start_record_jiffies = 0; + start_record_flag = 0; +#endif + return; + } + + if((batt_info_ptr->fVolt >= (config_data.charge_cv_voltage - 50))&&(batt_info_ptr->fCurr >= DISCH_CURRENT_TH) && + (batt_info_ptr->fCurr < oz8806_eoc)&& (!gas_gauge_ptr->charge_end)) + { + if (!start_jiffies) + start_jiffies = jiffies; + + time_accumulation = jiffies_to_msecs(jiffies - start_jiffies); + + //time accumulation is over 5 minutes + if (time_accumulation >= CHG_END_PERIOD_MS) + { + charger_finish = 1; + bmt_dbg("enter external charger finish\n"); + } + else + { + charger_finish = 0; + } + } + else + { + time_accumulation = 0; + start_jiffies = 0; + charger_finish = 0; + } + +charger_full: + bmt_dbg("%s, time_accumulation:%d, charger_finish:%d\n", + __func__, time_accumulation, charger_finish); + + bmt_dbg("voltage:%d, cv:%d, fcurr:%d, BMT eoc:%d, gas_gauge_ptr->charge_end:%d\n", + batt_info_ptr->fVolt, config_data.charge_cv_voltage, + batt_info_ptr->fCurr, oz8806_eoc, gas_gauge_ptr->charge_end); + +#ifdef ENABLE_10MIN_END_CHARGE_FUN + if((batt_info_ptr->fRSOC == 99) &&(!start_record_flag) &&(batt_info_ptr->fCurr > oz8806_eoc)) + { + start_record_jiffies = jiffies; + start_record_flag = 1; + bmt_dbg("start_record_flag: %d, at %d ms\n",start_record_flag, jiffies_to_msecs(jiffies)); + } + if((batt_info_ptr->fRSOC != 99) ||(batt_info_ptr->fCurr < oz8806_eoc)) + { + start_record_flag = 0; + } + + if((start_record_flag) && (batt_info_ptr->fCurr > oz8806_eoc)) + { + if(jiffies_to_msecs(jiffies - start_record_jiffies) > FORCE_FULL_MS) + { + bmt_dbg("start_record_flag: %d, at %d ms\n",start_record_flag, jiffies_to_msecs(jiffies)); + charger_finish = 1; + start_record_flag = 0; + bmt_dbg("enter charge timer finish\n"); + } + } +#endif + if(charger_finish) + { + if(!gas_gauge_ptr->charge_end) + { + if(batt_info_ptr->fRSOC < 100) + { + static int32_t fRSOC_extern = 0; + + if(batt_info_ptr->fRSOC <= rsoc_pre){ + bmt_dbg("fRSOC_extern:%d, soc:%d \n",fRSOC_extern,batt_info_ptr->fRSOC); + + if (!chgr_full_soc_pursue_start || fRSOC_extern != batt_info_ptr->fRSOC) { + chgr_full_soc_pursue_start = jiffies; + fRSOC_extern = batt_info_ptr->fRSOC ; + } + + chgr_full_soc_pursue_accumulation = + jiffies_to_msecs(jiffies - chgr_full_soc_pursue_start); + + if (chgr_full_soc_pursue_accumulation >= CHG_END_PURSUE_STEP){ + + chgr_full_soc_pursue_start = 0; + batt_info_ptr->fRSOC++; + batt_info_ptr->sCaMAH = batt_info_ptr->fRSOC * the_oz8806->batt_info.batt_fcc_data / num_100; + batt_info_ptr->sCaMAH += the_oz8806->batt_info.batt_fcc_data / 200; // + 0.5% + } + } + + if(batt_info_ptr->fRSOC > 100) { + batt_info_ptr->fRSOC = 100; + batt_info_ptr->sCaMAH = batt_info_ptr->fRSOC * the_oz8806->batt_info.batt_fcc_data / num_100; + batt_info_ptr->sCaMAH ++; + } + //update fRSOC_extern + fRSOC_extern = batt_info_ptr->fRSOC ; + bmt_dbg("enter charger finsh update soc:%d\n",batt_info_ptr->fRSOC); + } + else + { + bmt_dbg("enter charger charge end\n"); + gas_gauge_ptr->charge_end = 1; + charge_end_process(); + + charger_finish = 0; + chgr_full_soc_pursue_start = 0; + } + } + else { + charger_finish = 0; + chgr_full_soc_pursue_start = 0; + } + } else chgr_full_soc_pursue_start = 0; + +} +static void oz8806_wakeup_event(struct oz8806_data *data) +{ + static int ws_active = 0; + + if (adapter_status == O2_CHARGER_BATTERY && ws_active) { + pm_relax(&data->myclient->dev); + ws_active = 0; + } + + if (adapter_status != O2_CHARGER_BATTERY && !ws_active) { + pm_stay_awake(&data->myclient->dev); + ws_active = 1; + } +} +//this is very important code customer need change +//customer should change charge discharge status according to your system +static void system_charge_discharge_status(struct oz8806_data *data) +{ + int8_t adapter_status_temp = O2_CHARGER_BATTERY; + + union power_supply_propval val_ac = {0}; + union power_supply_propval val_usb = {0}; + + if (!data->ac_psy) + data->ac_psy = power_supply_get_by_name ("ac"); + + if (!data->usb_psy) + data->usb_psy= power_supply_get_by_name ("usb"); + +//--------------------------------------------- +#ifdef QUALCOMM_MACH_SUPPORT + if (data->usb_psy) + { + if(0 == power_supply_get_property(data->usb_psy, POWER_SUPPLY_PROP_REAL_TYPE, &val_usb)) + { + if(POWER_SUPPLY_TYPE_USB_ACA == val_usb.intval|| POWER_SUPPLY_TYPE_USB_CDP == val_usb.intval|| POWER_SUPPLY_TYPE_USB_DCP == val_usb.intval) + adapter_status_temp = O2_CHARGER_AC; + else if(POWER_SUPPLY_TYPE_USB == val_usb.intval) + adapter_status_temp = O2_CHARGER_USB; + else + adapter_status_temp = O2_CHARGER_BATTERY; + } + } + +#else + if (data->usb_psy) + { + if(0 == power_supply_get_property(data->usb_psy, POWER_SUPPLY_PROP_ONLINE, &val_usb)) + { + if (val_usb.intval) + adapter_status_temp = O2_CHARGER_USB; + } + } + + if (data->ac_psy) + { + if(0 == power_supply_get_property(data->ac_psy, POWER_SUPPLY_PROP_ONLINE, &val_ac)) + { + if (val_ac.intval) + adapter_status_temp = O2_CHARGER_AC; + } + } + batt_dbg("val_ac.intval %d, val_usb.intval %d,adapter_status_temp %d\n",val_ac.intval,val_usb.intval,adapter_status_temp); +#endif + + adapter_status = adapter_status_temp; + batt_dbg("adapter_status:%d\n", adapter_status); +} + +static void oz8806_lock_soc(struct oz8806_data *data) +{ + unsigned long car = 0; + unsigned short temp = 0; + + //bmu ok + if(bmu_init_done) + { + if(rsoc_pre < 0) rsoc_pre = 0; + if(rsoc_pre > 100) rsoc_pre = 100; + + //adapter is online,and current > 0, lock to 0 or pre soc + if( (data->batt_info.batt_current > 0) && (O2_CHARGER_USB == adapter_status || O2_CHARGER_AC == adapter_status) ) + { + if(data->batt_info.batt_voltage <= O2_SOC_START_THRESHOLD_VOL) + { + bmt_dbg("charge 1,lock soc to pre_soc,pre_soc=%d\n",rsoc_pre); + if(0 == rsoc_pre) + car = data->batt_info.batt_fcc_data / 100 -1; + else + car = rsoc_pre * data->batt_info.batt_fcc_data / 100; + + temp = (car * config_data.fRsense) / config_data.dbCARLSB; //transfer to CAR + temp /= 1000; + + i2c_smbus_write_word_data(data->myclient, OZ8806_OP_CAR,(unsigned short)temp); + } + } + + //adapter is online,do not consider current, lock soc to 100 if voltage >= cv - recharge + if(O2_CHARGER_USB == adapter_status || O2_CHARGER_AC == adapter_status) + { + if( (100 == rsoc_pre) && (data->batt_info.batt_voltage >= (config_data.charge_cv_voltage - O2_CONFIG_RECHARGE)) ) + { + bmt_dbg("charge 2,lock soc to pre_soc,pre_soc=%d\n",rsoc_pre); + car = 101 * data->batt_info.batt_fcc_data / 100 - 2; + batt_info_ptr->sCaMAH = car; + batt_info_ptr->fRC = car; + batt_info_ptr->fRCPrev = car; + temp = (car * config_data.fRsense) / config_data.dbCARLSB; //transfer to CAR + temp /= 1000; + + i2c_smbus_write_word_data(data->myclient, OZ8806_OP_CAR,(unsigned short)temp); + } + } + } +} + +static void oz8806_battery_func(struct oz8806_data *data) +{ + unsigned long time_since_last_update_ms = 0; + static unsigned long cur_jiffies = 0; + + if(0 == cur_jiffies) + cur_jiffies = jiffies; + + batt_dbg("BMT battery func: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"); + time_since_last_update_ms = jiffies_to_msecs(jiffies - cur_jiffies); + cur_jiffies = jiffies; + + //get charging type: battery, AC, or USB + system_charge_discharge_status(data); + + rsoc_pre = data->batt_info.batt_soc; + + //you must add this code here for BMT + //Notice: don't nest mutex +#ifdef EXT_THERMAL_READ + oz8806_update_batt_temp(data); +#endif + oz8806_lock_soc(data); + + /**************mutex_lock*********************/ + mutex_lock(&update_mutex); + if (!oz8806_suspend) + bmu_polling_loop(); + + oz8806_update_batt_info(data); + + charge_end_fun(); + + if(adapter_status == O2_CHARGER_BATTERY) + discharge_end_fun(data); + + mutex_unlock(&update_mutex); + /**************mutex_unlock*********************/ + + + oz8806_wakeup_event(data); + + batt_dbg("l=%d v=%d t=%d c=%d ch=%d\n", + data->batt_info.batt_soc, data->batt_info.batt_voltage, + data->batt_info.batt_temp, data->batt_info.batt_current, adapter_status); + + power_supply_changed(data->bat); + batt_dbg("since last batt update = %lu ms\n", time_since_last_update_ms); +} + +static void oz8806_battery_work(struct work_struct *work) +{ + struct oz8806_data *data = container_of(work, struct oz8806_data, work.work); + + oz8806_battery_func(data); + + if (!bmu_init_done) + { + // data->interval = 2000; + schedule_delayed_work(&data->work, msecs_to_jiffies(200)); + } + else + { + data->interval = BATTERY_WORK_INTERVAL * 1000; + schedule_delayed_work(&data->work, msecs_to_jiffies(data->interval)); + + } + batt_dbg("interval:%d ms\n", data->interval); +} + +int32_t is_battery_exchanged(void) +{ + return 0; +} +EXPORT_SYMBOL(is_battery_exchanged); + +static int oz8806_suspend_notifier(struct notifier_block *nb, + unsigned long event, + void *dummy) +{ + struct oz8806_data *data = container_of(nb, struct oz8806_data, pm_nb); + + switch (event) { + + case PM_SUSPEND_PREPARE: + bmt_dbg("BMT PM_SUSPEND_PREPARE \n"); + cancel_delayed_work_sync(&data->work); + system_charge_discharge_status(data); + oz8806_suspend = 1; + return NOTIFY_OK; + case PM_POST_SUSPEND: + bmt_dbg("BMT PM_POST_SUSPEND \n"); + system_charge_discharge_status(data); + mutex_lock(&update_mutex); + // if AC charge can't wake up every 1 min,you must remove the if. + if(adapter_status == O2_CHARGER_BATTERY) + bmu_wake_up_chip(); + + oz8806_update_batt_info(data); + mutex_unlock(&update_mutex); + + schedule_delayed_work(&data->work, 0); + //this code must be here,very carefull. + if(adapter_status == O2_CHARGER_BATTERY) + { + if(data->batt_info.batt_current >= data->batt_info.discharge_current_th) + { + if (batt_info_ptr) batt_info_ptr->fCurr = -20; + + data->batt_info.batt_current = -20; + bmt_dbg("drop current\n"); + } + } + oz8806_suspend = 0; + return NOTIFY_OK; + + default: + return NOTIFY_DONE; + } +} + +static int32_t oz8806_hw_ic_detect(struct oz8806_data *data) +{ + + uint8_t chip_id = 0; + int32_t ret = 0; + int32_t i = 0; + + for(i=0; i<5; i++) + { + ret = i2c_smbus_read_word_data(data->myclient, OZ8806_OP_IDREV); + if(ret >= 0) + { + bmt_dbg("BMT fgu read chip id value 0x%.2x\n",ret); + break; + } + msleep(5); + } + if(i >= 5) + { + bmt_dbg("BMT fgu hw ic not detect, reg value 0x%.2x\n",ret); + return -1; + } + chip_id = (uint8_t)ret; + if(OZ8806_CHIP_ID != chip_id) + return -1; + + return 0; +} + +static void oz8806_init_soc(struct oz8806_data *data) +{ + int32_t ret = 0; + int32_t temp = 0; + uint32_t i = 0; + + for(i=0; i<5; i++) + { + ret = i2c_smbus_read_word_data(data->myclient, OZ8806_OP_CAR); + if(ret >= 0) + break; + msleep(5); + } + if(i >= 5) + { + bmt_dbg("BMT fgu init soc failed, reg value %x\n",ret); + init_soc = 50; + return; + } + + temp = (int16_t)ret; + temp = temp * config_data.dbCARLSB; + temp = temp * 1000 / config_data.fRsense; + if(temp > 5) + { + init_soc = temp * 100 / config_data.design_capacity - 1; + if(init_soc >= 100) + init_soc = 100; + if(init_soc <= 0) + init_soc = 0; + } + else + init_soc = 0; + + bmt_dbg("BMT fgu init soc succeed, init soc is %d\n",init_soc); +} +static int oz8806_init_batt_info(struct oz8806_data *data) +{ + data->batt_info.batt_soc = 50; + data->batt_info.batt_voltage = 3999; + data->batt_info.batt_current = -300; + data->batt_info.batt_temp = 27; + data->batt_info.batt_capacity = 4000; + + data->batt_info.batt_fcc_data = config_data.design_capacity; + data->batt_info.discharge_current_th = DISCH_CURRENT_TH; + data->batt_info.charge_end = 0; + oz8806_init_soc(data); + return 0; +} +static int oz8806_update_batt_info(struct oz8806_data *data) +{ + int ret = 0; + + //Notice: before call this function, use mutex_lock(&update_mutex) + //Notice: don't nest mutex + if (!batt_info_ptr || !gas_gauge_ptr) + { + ret = -EINVAL; + goto end; + } + + data->batt_info.batt_soc = batt_info_ptr->fRSOC; + if (data->batt_info.batt_soc > 100) + data->batt_info.batt_soc = 100; + + data->batt_info.batt_voltage = batt_info_ptr->fVolt; + data->batt_info.batt_current = batt_info_ptr->fCurr; + data->batt_info.batt_capacity = batt_info_ptr->sCaMAH; + + data->batt_info.charge_end = gas_gauge_ptr->charge_end; + bmu_init_done = gas_gauge_ptr->bmu_init_ok; +#ifndef EXT_THERMAL_READ + oz8806_update_batt_temp(data); +#endif +end: + return ret; +} + +static int oz8806_update_batt_temp(struct oz8806_data *data) +{ + int ret = 0; + + if (!batt_info_ptr || !gas_gauge_ptr) + { + ret = -EINVAL; + goto end; + } + + data->batt_info.batt_temp = batt_info_ptr->fCellTemp; + if (batt_info_ptr->fCellTemp != data->batt_info.batt_temp) + batt_info_ptr->fCellTemp = data->batt_info.batt_temp; +end: + return ret; +} + +int oz8806_get_remaincap(void) +{ + int ret = -1; + + mutex_lock(&update_mutex); + + if (batt_info_ptr) + ret = batt_info_ptr->sCaMAH; + + mutex_unlock(&update_mutex); + + return ret; +} +EXPORT_SYMBOL(oz8806_get_remaincap); + +int oz8806_get_soc_from_ext(void) +{ + int ret = -1; + + return ret; +} +EXPORT_SYMBOL(oz8806_get_soc_from_ext); + +int oz8806_get_soc(void) +{ + int ret = -1; + + mutex_lock(&update_mutex); + + if (!bmu_init_done) + { + ret = init_soc; + } + else if (batt_info_ptr) + ret = batt_info_ptr->fRSOC; + + mutex_unlock(&update_mutex); + + return ret; +} +EXPORT_SYMBOL(oz8806_get_soc); + +int oz8806_get_battry_current(void) +{ + int ret = -1; + + if (!bmu_init_done) return -1; + + mutex_lock(&update_mutex); + + if (batt_info_ptr) + ret = afe_read_current(&batt_info_ptr->fCurr); + + if (ret < 0) + { + ret = -1; + bmt_dbg("BMT current adc error\n"); + } + + if (batt_info_ptr && ret >= 0) + ret = batt_info_ptr->fCurr; + + mutex_unlock(&update_mutex); + + return ret; +} +EXPORT_SYMBOL(oz8806_get_battry_current); + +int oz8806_get_battery_voltage(void) +{ + int ret = -1; + + if (!bmu_init_done) return -1; + + mutex_lock(&update_mutex); + + if (batt_info_ptr) + ret = afe_read_cell_volt(&batt_info_ptr->fVolt); + + if (ret < 0) + { + ret = -1; + bmt_dbg("BMT voltage adc error\n"); + } + + if (batt_info_ptr && ret >= 0) + ret = batt_info_ptr->fVolt; + + mutex_unlock(&update_mutex); + + return ret; +} +EXPORT_SYMBOL(oz8806_get_battery_voltage); + +int oz8806_get_battery_temp(void) +{ + int ret = -1; + + if (!bmu_init_done) return -1; + + mutex_lock(&update_mutex); + + if (batt_info_ptr) + ret = batt_info_ptr->fCellTemp; + + mutex_unlock(&update_mutex); + + return ret; +} +EXPORT_SYMBOL(oz8806_get_battery_temp); + +void oz8806_battery_update_data(void) +{ + bmt_dbg("enter BMT fgu battery update data"); + if (fg_hw_init_done && the_oz8806 && bmu_init_done) + oz8806_battery_func(the_oz8806); +} +EXPORT_SYMBOL(oz8806_battery_update_data); + +int32_t oz8806_vbus_voltage(void) +{ + int32_t vbus_voltage = 0; + int ret = -1; + + ret =oz8806_temp_read(&vbus_voltage); + + if (ret < 0) + { + bmt_dbg("BMT temp adc error\n"); + return ret; + } + + bmt_dbg("voltage from BMT:%d\n", vbus_voltage); + vbus_voltage = vbus_voltage * (RPULL + RDOWN) / RDOWN; + + return vbus_voltage; +} +EXPORT_SYMBOL(oz8806_vbus_voltage); + +int32_t oz8806_get_simulated_temp(void) +{ + return 25; + +} +EXPORT_SYMBOL(oz8806_get_simulated_temp); + +int32_t oz8806_get_init_status(void) +{ + return bmu_init_done; +} +EXPORT_SYMBOL(oz8806_get_init_status); + +int32_t gauge_int_done(void) +{ + return fg_hw_init_done; +} +EXPORT_SYMBOL(gauge_int_done); + +struct i2c_client * oz8806_get_client(void) +{ + if (the_oz8806) + return the_oz8806->myclient; + else + { + bmt_dbg("BMT is NULL, BMT_probe didn't call\n"); + return NULL; + } +} +EXPORT_SYMBOL(oz8806_get_client); + +int8_t get_adapter_status(void) +{ + return adapter_status; +} +EXPORT_SYMBOL(get_adapter_status); + +void oz8806_register_bmu_callback(void *bmu_polling_loop_func, + void *bmu_wake_up_chip_func, + void *bmu_power_down_chip_func, + void *charge_end_process_func, + void *discharge_end_process_func, + void *oz8806_temp_read_func, + void *oz8806_current_read_func, + void *oz8806_voltage_read_func) +{ + mutex_lock(&update_mutex); + + bmu_polling_loop_callback = bmu_polling_loop_func; + bmu_wake_up_chip_callback = bmu_wake_up_chip_func; + bmu_power_down_chip_callback = bmu_power_down_chip_func; + bmu_charge_end_process_callback = charge_end_process_func; + bmu_discharge_end_process_callback = discharge_end_process_func; + + oz8806_temp_read_callback = oz8806_temp_read_func; + oz8806_current_read_callback = oz8806_current_read_func; + oz8806_voltage_read_callback = oz8806_voltage_read_func; + + bmu_polling_loop(); + + mutex_unlock(&update_mutex); +} +EXPORT_SYMBOL(oz8806_register_bmu_callback); + +void unregister_bmu_callback(void) +{ + mutex_lock(&update_mutex); + + bmu_polling_loop_callback = NULL; + bmu_wake_up_chip_callback = NULL; + bmu_power_down_chip_callback = NULL; + bmu_charge_end_process_callback = NULL; + bmu_discharge_end_process_callback = NULL; + + oz8806_temp_read_callback = NULL; + oz8806_current_read_callback = NULL; + oz8806_voltage_read_callback = NULL; + + oz8806_init_batt_info(the_oz8806); + + batt_info_ptr = NULL; + gas_gauge_ptr = NULL; + + mutex_unlock(&update_mutex); +} +EXPORT_SYMBOL(unregister_bmu_callback); + +void oz8806_set_batt_info_ptr(bmu_data_t *batt_info) +{ + mutex_lock(&update_mutex); + + if (!batt_info) + { + bmt_dbg("BMT fgu batt_info NULL\n"); + mutex_unlock(&update_mutex); + return; + } + + batt_info_ptr = batt_info; + + mutex_unlock(&update_mutex); +} +EXPORT_SYMBOL(oz8806_set_batt_info_ptr); + +void oz8806_set_gas_gauge(gas_gauge_t *gas_gauge) +{ + mutex_lock(&update_mutex); + + if (!gas_gauge) + { + bmt_dbg("BMT fgu gas_gauge NULL\n"); + mutex_unlock(&update_mutex); + return; + } + + gas_gauge_ptr = gas_gauge; + + if (gas_gauge_ptr->fcc_data != 0) + the_oz8806->batt_info.batt_fcc_data = gas_gauge_ptr->fcc_data; + + if (gas_gauge_ptr->discharge_current_th != 0) + the_oz8806->batt_info.discharge_current_th = gas_gauge_ptr->discharge_current_th; + + bmt_dbg("batt_fcc_data:%d, discharge_current_th:%d\n", + the_oz8806->batt_info.batt_fcc_data, the_oz8806->batt_info.discharge_current_th); + + gas_gauge_ptr->ext_temp_measure = ext_thermal_read; + + mutex_unlock(&update_mutex); +} +EXPORT_SYMBOL(oz8806_set_gas_gauge); + +int oz8806_get_save_capacity(void) +{ + return save_capacity; +} +EXPORT_SYMBOL(oz8806_get_save_capacity); + +unsigned long oz8806_get_system_boot_time(void) +{ + unsigned long long time_sec; + unsigned long mod; + time_sec = ktime_to_ns(ktime_get_boottime()); + mod = do_div(time_sec,1000000); + batt_dbg("system boottime: %lu ms\n", (unsigned long)time_sec); + + return time_sec; +} +EXPORT_SYMBOL(oz8806_get_system_boot_time); + +unsigned long oz8806_get_boot_up_time(void) +{ + unsigned long long t; + struct timespec time; + ktime_get_ts(&time); + t = time.tv_sec * 1000 + time.tv_nsec / 1000000; + bmt_dbg("boot up time: %lu ms\n", (unsigned long) t); + + return t; +} +EXPORT_SYMBOL(oz8806_get_boot_up_time); + +unsigned long oz8806_get_power_on_time(void) +{ + unsigned int t = 0; + bmt_dbg("jiffies %lu,ic_wakeup_time %lu\n",jiffies,ic_wakeup_time); + t = jiffies_to_msecs(jiffies - ic_wakeup_time); + + bmt_dbg("IC wakeup time: %u ms\n", t); + + return (unsigned long)t; +} +EXPORT_SYMBOL(oz8806_get_power_on_time); + +static int oz8806_probe(struct i2c_client *client,const struct i2c_device_id *id) +{ + int32_t ret = 0; + struct oz8806_data* data = NULL; + + struct power_supply_config psy_cfg = { + .of_node = client->dev.of_node, + }; + + bmt_dbg("BMT fgu probe start\n"); + data = devm_kzalloc(&client->dev, sizeof(struct oz8806_data), GFP_KERNEL); + if (!data) { + dev_err(&client->dev, "Can't alloc BMT_data struct\n"); + return -ENOMEM; + } + + // Init real i2c_client + i2c_set_clientdata(client, data); + + the_oz8806 = data; + data->myclient = client; + + ret = oz8806_hw_ic_detect(data); + if(ret < 0) + { + pr_err("BMT fgu do not detect ic, exit\n"); + goto register_fail; + } + + //init battery information as soon as possible + oz8806_wakeup_full_power(); + + oz8806_init_batt_info(data); + + INIT_DELAYED_WORK(&data->work, oz8806_battery_work); + /***************************************************************************** + * BMT Warning: This is a very important code here(bmulib_init). + * The chip needs i2c client processing opt mapping and self init first in bmulib_init. + * You must not return before this code even if you read i2c error. + * Remind you again that it will make serious mistake if you return before bmulib_init. + *****************************************************************************/ + if (oz8806_get_boot_up_time() < 2000) + ic_wakeup_time = jiffies + msecs_to_jiffies(2000 - oz8806_get_boot_up_time()); + else + ic_wakeup_time = jiffies;//msecs_to_jiffies(2000); + +#ifndef CONFIG_OZ8806_M + bmulib_init(); +#else + /* + * Wake up oz8806 to full power mode here. + * Then, after at least 2.5s, detection of current, voltage, and temperature can be stable + * + * when IC start with start-up mode, after at least 2s, IC can enter Full Power Mode. + */ + oz8806_wakeup_full_power(); +#endif + + data->interval = BATTERY_WORK_INTERVAL * 1000; + + oz8806_powersupply_init(data); + psy_cfg.drv_data = data, + data->bat = power_supply_register(&client->dev, &the_oz8806->bat_desc,&psy_cfg); + if (!data->bat) { + bmt_dbg("failed to register power_supply battery\n"); + goto register_fail; + } + /* + * /sys/class/i2c-dev/i2c-2/device/2-002f/ + */ + ret = oz8806_create_sys(&(client->dev), &oz8806_attribute_group); + if(ret){ + bmt_dbg("[BATT]: Err failed to creat BMT attributes\n"); + goto bat_failed; + } + + //alternative suspend/resume method + data->pm_nb.notifier_call = oz8806_suspend_notifier; + register_pm_notifier(&data->pm_nb); + + ret = device_init_wakeup(&client->dev, true); + if (ret) + dev_err(&client->dev, "wakeup source init failed.\n"); + + fg_hw_init_done = 1; + + schedule_delayed_work(&data->work, 0); + + bmt_dbg("BMT fgu probe end\n"); + return 0; +bat_failed: + power_supply_unregister(the_oz8806->bat); +register_fail: + bmt_dbg("BMT fgu probe () fail: return %d\n",ret); + the_oz8806 = NULL; + return ret; +} + + +static int oz8806_remove(struct i2c_client *client) +{ + struct oz8806_data *data = i2c_get_clientdata(client); + + cancel_delayed_work(&data->work); + + sysfs_remove_group(&(data->myclient->dev.kobj), &oz8806_attribute_group); + + power_supply_unregister(data->bat); + + mutex_lock(&update_mutex); + + /* + * It should be noted here that during the shutdown process, the shutdown callback must be called, + * otherwise the chip may not sleep normally, resulting in abnormal capacity at the next startup. + * + * And be careful after the callback, if there is work or thread running, + * it may wake up the chip again, to avoid this situation. + */ + fg_hw_init_done = 0;//be carefull other thread call bmu_polling_loop_func and wake up chip again + bmu_power_down_chip(); + + oz8806_update_batt_info(data); + + mutex_unlock(&update_mutex); +#ifndef CONFIG_OZ8806_M + bmulib_exit(); +#endif + + return 0; +} + +static void oz8806_shutdown(struct i2c_client *client) +{ + struct oz8806_data *data = i2c_get_clientdata(client); + bmt_dbg("BMT fgu shutdown\n"); + + cancel_delayed_work(&data->work); + + sysfs_remove_group(&(data->myclient->dev.kobj), &oz8806_attribute_group); + + mutex_lock(&update_mutex); + + /* + * It should be noted here that during the shutdown process, the shutdown callback must be called, + * otherwise the chip may not sleep normally, resulting in abnormal capacity at the next startup. + * + * And be careful after the callback, if there is work or thread running, + * it may wake up the chip again, to avoid this situation. + */ + fg_hw_init_done = 0; + bmu_power_down_chip(); + + oz8806_update_batt_info(data); + mutex_unlock(&update_mutex); +} +/*-------------------------------------------------------------------------*/ + +static const struct i2c_device_id oz8806_id[] = { + { MYDRIVER, 0 }, //string, id?? + { } +}; +MODULE_DEVICE_TABLE(i2c, oz8806_id); + +static const struct of_device_id oz8806_of_match[] = { + {.compatible = "o2micro,oz8806",}, + {}, +}; +MODULE_DEVICE_TABLE(of, oz8806_of_match); + + +static struct i2c_driver oz8806_driver = { + .driver = { + .name = MYDRIVER, + .of_match_table = oz8806_of_match, + }, + .probe = oz8806_probe, + .remove = oz8806_remove, + .shutdown = oz8806_shutdown, + .id_table = oz8806_id, +}; + + +static int __init oz8806_init(void) +{ + int ret = 0; + + ret = i2c_add_driver(&oz8806_driver); + + if(ret != 0) + bmt_dbg("failed to register BMT i2c driver.\n"); + else + bmt_dbg("Success to register BMT i2c driver.\n"); + + return ret; +} + +static void __exit oz8806_exit(void) +{ + bmt_dbg("BMT fgu exit\n"); + i2c_del_driver(&oz8806_driver); +} + +MODULE_DESCRIPTION("oz8806 Battery Monitor IC Driver"); +MODULE_LICENSE("GPL"); + +//subsys_initcall_sync(oz8806_init); +module_init(oz8806_init); +module_exit(oz8806_exit); diff --git a/drivers/power/sd77426_fg_mmi/oz8806_regdef.h b/drivers/power/sd77426_fg_mmi/oz8806_regdef.h new file mode 100644 index 000000000000..716903f0265e --- /dev/null +++ b/drivers/power/sd77426_fg_mmi/oz8806_regdef.h @@ -0,0 +1,87 @@ +/***************************************************************************** +* Copyright(c) BMT, 2021. All rights reserved. +* +* BMT [oz8806] Source Code Reference Design +* File:[bmulib.c] +* +* This Source Code Reference Design for BMT [oz8806] access +* ("Reference Design") is solely for the use of PRODUCT INTEGRATION REFERENCE ONLY, +* and contains confidential and privileged information of BMT International +* Limited. BMT shall have no liability to any PARTY FOR THE RELIABILITY, +* SERVICEABILITY FOR THE RESULT OF PRODUCT INTEGRATION, or results from: (i) any +* modification or attempted modification of the Reference Design by any party, or +* (ii) the combination, operation or use of the Reference Design with non-BMT +* Reference Design. Use of the Reference Design is at user's discretion to qualify +* the final work result. +*****************************************************************************/ + + #ifndef _OZ8806_REGDEF_H_ + #define _OZ8806_REGDEF_H_ + +/***************************************************************************** + * #include section + * add #include here + *****************************************************************************/ + + +/***************************************************************************** + * const section + * add const #define here + *****************************************************************************/ + +/* Operation register map define */ +#define OZ8806_OP_IDREV 0X00 //CHIP ID and Reversion +#define CHIP_ID_MASK 0xF8 //default CHIP_ID[7:3]=0x07 +#define CHIP_VER_MASK 0x07 //default CHIP_VER[2:0]=0x00 for 8806 + +#define OZ8806_OP_VD23 0x03 // + +#define OZ8806_OP_I2CCONFIG 0x04 //I2C config register +#define I2C_ADDRESS_MASK 0x0E //default 0X07 + +#define OZ8806_OP_PEC_CTRL 0x08 //PEC control register +#define PEC_EN 0x04 //PEC control register[2]: 1: PEC enable 0: PEC disable + +#define OZ8806_OP_CTRL 0x09 //Status control register +#define SOFT_RESET 0x80 //Status control register[7]: 1: reset +#define SLEEP_MODE 0x40 //Status control register[6]: 1: sleep 0:active +#define SLEEP_OCV_EN 0x20 +#define CHARGE_ON 0x10 +#define VME_SEL 0x0c +#define CHG_SEL 0x02 +#define BI_STATUS 0x01 + + +#define OZ8806_OP_VOLT_OFFSET 0X0A +#define VOLT_OFFSET_MASK 0xFC +#define OZ8806_OP_BATTERY_ID 0x0B +#define OZ8806_OP_CELL_TEMP 0x0C //cell temperature low byte,[7:4]->[3:0]of cell temp +#define CELL_TEMP_MASK 0x0FFF + +#define OZ8806_OP_CELL_TEMP_HIGH 0x0D //cell temperature high byte,[7:0]->[11:4]of cell temp + +#define OZ8806_OP_CELL_VOLT 0x0E //cell voltage low byte,[7:4]->[3:0]of cell volt +#define CELL_VOLT_MASK 0x0FFF + +#define OZ8806_OP_CELL_VOLT_HIGH 0x0f //cell voltage high byte,[7:0]->[11:4]of cell volt + +#define OZ8806_OP_OCV_LOW 0x10 //OCV low byte,[7:4]->[3:0]of OCV +#define OCV_LOW_MASK 0xF0 +#define SLEEP_OCV_FLAG 0X02 +#define POWER_OCV_FLAG 0X01 + +#define OZ8806_OP_OCV_HIGH 0x11 //OCV high byte,[7:0]->[11:4]of OCV +#define OZ8806_OP_CURRENT 0x12 //CURRENT low byte,[7:1]->[5:0]of CURRENT +#define OZ8806_OP_CAR 0x14 //CAR low byte + +#define OZ8806_OP_BOARD_OFFSET 0x18 + + +/***************************************************************************** + * function prototype section + * add function prototype here + *****************************************************************************/ + + +#endif + diff --git a/drivers/power/sd77426_fg_mmi/parameter.c b/drivers/power/sd77426_fg_mmi/parameter.c new file mode 100644 index 000000000000..5ebcb380f96d --- /dev/null +++ b/drivers/power/sd77426_fg_mmi/parameter.c @@ -0,0 +1,135 @@ +/***************************************************************************** +* Copyright(c) BMT, 2021. All rights reserved +* +* BMT [oz8806] Source Code Reference Design +* File:[bmulib.c] +* +* This Source Code Reference Design for BMT [oz8806] access +* ("Reference Design") is solely for the use of PRODUCT INTEGRATION REFERENCE ONLY, +* and contains confidential and privileged information of BMT International +* Limited. BMT shall have no liability to any PARTY FOR THE RELIABILITY, +* SERVICEABILITY FOR THE RESULT OF PRODUCT INTEGRATION, or results from: (i) any +* modification or attempted modification of the Reference Design by any party, or +* (ii) the combination, operation or use of the Reference Design with non-BMT +* Reference Design. Use of the Reference Design is at user's discretion to qualify +* the final work result. +*****************************************************************************/ + +#include +#include +#include +#include "parameter.h" +#include "table.h" +#include "battery_config.h" + +one_latitude_data_t cell_temp_data[TEMPERATURE_DATA_NUM] = { + {3380, 115}, {3590, 113}, {4594, 105}, + {5383, 100}, {6336, 95}, {7481, 90}, + {8872, 85}, {10565, 80}, {12635, 75}, + {15184, 70}, {18322, 65}, {22224, 60}, + {27090, 55}, {33194, 50}, {40903, 45}, + {50676, 40}, {63167, 35}, {79221, 30}, + {100000, 25}, {127080, 20}, {162650, 15}, + {209709, 10}, {272499, 5}, {357011, 0}, + {471632, -5}, {628988, -10}, {846578, -15}, + {1151036, -20}, +}; + + +config_data_t config_data = { + .fRsense = O2_CONFIG_RSENSE, + .temp_pull_up = O2_TEMP_PULL_UP_R, + .temp_ref_voltage = O2_TEMP_REF_VOLTAGE, + .dbCARLSB = 5, + .dbCurrLSB = 781 , + .fVoltLSB = 250, + + .design_capacity = O2_CONFIG_CAPACITY, + .charge_cv_voltage = OZ8806_VOLTAGE, + .charge_end_current = O2_CONFIG_EOC, + .discharge_end_voltage = OZ8806_EOD, + .board_offset = O2_CONFIG_BOARD_OFFSET, + .debug = 1, +}; + +/***************************************************************************** + * bmu_init_parameter: + * this implements a interface for customer initiating bmu parameters + * Return: NULL + *****************************************************************************/ +void bmu_init_parameter(parameter_data_t *parameter_customer) +{ + check_chip_function = 0; + parameter_customer->config = &config_data; + parameter_customer->ocv = ocv_data; + parameter_customer->temperature = cell_temp_data; + parameter_customer->ocv_data_num = OCV_DATA_NUM; + parameter_customer->cell_temp_num = TEMPERATURE_DATA_NUM; + parameter_customer->charge_pursue_step = 10; + parameter_customer->discharge_pursue_step = 6; + parameter_customer->discharge_pursue_th = 10; + parameter_customer->wait_method = 2; + + //parameter_customer->BATT_CAPACITY = "/data/sCaMAH.dat"; + //parameter_customer->BATT_FCC = "/data/fcc.dat"; + //parameter_customer->OCV_FLAG = "/data/ocv_flag.dat"; + //parameter_customer->BATT_OFFSET = "/data/offset.dat"; + + parameter_customer->fix_car_init = 0; + parameter_customer->power_on_retry_times = 0; + + //if read/write file /data/sCaMAH.dat in userspace + //please enable "bmu_sys_rw_kernel = 0" +#ifdef USERSPACE_READ_SAVED_CAP + parameter_customer->bmu_sys_rw_kernel = 0; +#else + parameter_customer->bmu_sys_rw_kernel = 1; +#endif +} +EXPORT_SYMBOL(bmu_init_parameter); + +/***************************************************************************** + * Description: + * bmu_init_gg + * Return: NULL + *****************************************************************************/ +void bmu_init_gg(gas_gauge_t *gas_gauge) +{ + //gas_gauge->charge_table_num = CHARGE_DATA_NUM; + gas_gauge->rc_x_num = XAxis; + gas_gauge->rc_y_num = YAxis; + gas_gauge->rc_z_num = ZAxis; + + gas_gauge->discharge_current_th = DISCH_CURRENT_TH; + gas_gauge->fcc_data = config_data.design_capacity; + + bmt_dbg("AAAA battery_id is %s\n", battery_id[0]); + + gas_gauge->ri = 18; + gas_gauge->line_impedance = 10; + gas_gauge->power_on_100_vol = O2_OCV_100_VOLTAGE; + +#if 0 + //add this for oinom + gas_gauge->lower_capacity_reserve = 5; + gas_gauge->lower_capacity_soc_start =30; + //gas_gauge->percent_10_reserve = 66; +#endif +} +EXPORT_SYMBOL(bmu_init_gg); + +void bmu_init_table(int **x, int **y, int **z, int **rc) +{ + *x = (int *)((uint8_t *)XAxisElement); + *y = (int *)((uint8_t *)YAxisElement); + *z = (int *)((uint8_t *)ZAxisElement); + *rc = (int *)((uint8_t *)RCtable); +} +EXPORT_SYMBOL(bmu_init_table); + +const char * get_table_version(void) +{ + return table_version; +} +EXPORT_SYMBOL(get_table_version); + diff --git a/drivers/power/sd77426_fg_mmi/parameter.h b/drivers/power/sd77426_fg_mmi/parameter.h new file mode 100644 index 000000000000..a9f3caf0079c --- /dev/null +++ b/drivers/power/sd77426_fg_mmi/parameter.h @@ -0,0 +1,348 @@ +/***************************************************************************** +* Copyright(c) BMT, 2021. All rights reserved. +* +* BMT [oz8806] Source Code Reference Design +* File:[bmulib.c] +* +* This Source Code Reference Design for BMT [oz8806] access +* ("Reference Design") is solely for the use of PRODUCT INTEGRATION REFERENCE ONLY, +* and contains confidential and privileged information of BMT International +* Limited. BMT shall have no liability to any PARTY FOR THE RELIABILITY, +* SERVICEABILITY FOR THE RESULT OF PRODUCT INTEGRATION, or results from: (i) any +* modification or attempted modification of the Reference Design by any party, or +* (ii) the combination, operation or use of the Reference Design with non-BMT +* Reference Design. Use of the Reference Design is at user's discretion to qualify +* the final work result. +*****************************************************************************/ + +#ifndef _PARAMETER_H_ +#define _PARAMETER_H_ + +/**************************************************************************** +* #include section +* add #include here if any +***************************************************************************/ +#include "table.h" +#include +#include + +/**************************************************************************** +* #define section +* add constant #define here if any +***************************************************************************/ +#define MYDRIVER "oz8806" //"oz8806" + +#define DISCH_CURRENT_TH -10 +#define O2_OCV_100_VOLTAGE 8700 +#define TEMPERATURE_DATA_NUM 28 +#define OZ8806_CHIP_ID 0x38 + +#define num_0 0 +#define num_1 1 +#define num_32768 32768 +#define num_5 5 +#define num_0x2c 0x2c +#define num_0x40 0x40 +#define num_0x20 0x20 +#define num_1000 1000 +#define num_50 50 +#define num_100 100 +#define num_10 10 +#define num_7 7 +#define num_2 2 +#define num_1000 1000 +#define num_0x2f 0x2f +#define num_0x28 0x28 +#define num_0x07 0x07 +#define num_6 6 +#define num_10 10 +#define num_20 20 +#define num_9 9 +#define num_95 95 +#define num_0x4c 0x4c +#define num_0x40 0x40 +#define num_3 3 +#define num_99 99 +#define num_15 15 +#define num_25 25 +#define num_0x80 0x80 + +//#define PEC_CHECK + +#define INIT_CAP (-2) +#define NO_FILE (-1) + +/**************************************************************************** +* Struct section +* add struct #define here if any +***************************************************************************/ + +enum charger_type_t { + O2_CHARGER_UNKNOWN = -1, + O2_CHARGER_BATTERY = 0, + O2_CHARGER_USB = 1, + O2_CHARGER_AC = 4, +}; + +struct batt_data { + int32_t batt_soc;//Relative State Of Charged, present percentage of battery capacity + int32_t batt_voltage;//Voltage of battery, in mV + int32_t batt_current;//Current of battery, in mA; plus value means charging, minus value means discharging + int32_t batt_temp;//Temperature of battery + int32_t batt_capacity;//adjusted residual capacity + int32_t batt_fcc_data; + int32_t discharge_current_th; + uint8_t charge_end; +}; + +struct oz8806_data +{ + struct power_supply *bat; + struct power_supply_desc bat_desc; + struct power_supply *ac_psy; + struct power_supply *usb_psy; + struct delayed_work work; + unsigned int interval; + struct i2c_client *myclient; + struct notifier_block pm_nb; //alternative suspend/resume method + struct batt_data batt_info; + int status; +}; + +typedef struct tag_config_data { + int32_t fRsense; //= 20 * 1000; //Rsense value of chip, in mini ohm expand 1000 times + int32_t temp_pull_up; //230000; + int32_t temp_ref_voltage; //1800;1.8v + int32_t dbCARLSB; //= 5.0; //LSB of CAR, comes from spec + int32_t dbCurrLSB; //781 (7.8 *100 uV); //LSB of Current, comes from spec + int32_t fVoltLSB; //250 (2.5*100 mV); //LSB of Voltage, comes from spec + int32_t design_capacity; //= 7000; //design capacity of the battery + int32_t charge_cv_voltage; //= 4200; //CV Voltage at fully charged + int32_t charge_end_current; //= 100; //the current threshold of End of Charged + int32_t discharge_end_voltage; //= 3550; //mV + int32_t board_offset; //0; //mA, not more than caculate data + uint8_t debug; // enable or disable O2MICRO debug information + +}config_data_t; + +typedef struct tag_parameter_data { + int32_t ocv_data_num; + int32_t cell_temp_num; + one_latitude_data_t *ocv; + one_latitude_data_t *temperature; + config_data_t *config; + struct i2c_client *client; + uint8_t charge_pursue_step; + uint8_t discharge_pursue_step; + uint8_t discharge_pursue_th; + uint8_t wait_method; + char * BATT_CAPACITY ; + char * BATT_FCC; + char * OCV_FLAG; + char * BATT_OFFSET; + uint8_t oz8806_cell_num; + int32_t res_divider_ratio; + uint8_t set_soc_from_ext; + int32_t soc_external; + uint8_t file_not_ok_cap; + uint8_t fix_car_init; + uint8_t bmu_sys_rw_kernel; + int8_t power_on_retry_times; +}parameter_data_t; + +typedef struct tag_bmu { + int32_t Battery_ok; + int32_t fRC; //= 0; //Remaining Capacity, indicates how many mAhr in battery + int32_t fRSOC; //50 = 50%; //Relative State Of Charged, present percentage of battery capacity + int32_t fVolt; //= 0; //Voltage of battery, in mV + int32_t fCurr; //= 0; //Current of battery, in mA; plus value means charging, minus value means discharging + int32_t fPrevCurr; //= 0; //last one current reading + int32_t fOCVVolt; //= 0; //Open Circuit Voltage + int32_t fCellTemp; //= 0; //Temperature of battery + int32_t fRCPrev; + int32_t sCaMAH; //= 0; //adjusted residual capacity + int32_t i2c_error_times; +}bmu_data_t; + +typedef struct tag_gas_gauge { + int32_t overflow_data; //unit: mAh, maximum capacity that the IC can measure + uint8_t discharge_end; + uint8_t charge_end; + uint8_t charge_fcc_update; + uint8_t discharge_fcc_update; + int32_t sCtMAH ; //be carefull, this must be int32_t + int32_t fcc_data; + int32_t discharge_sCtMAH ;//be carefull, this must be int32_t + //uint8_t charge_wait_times; + //uint8_t discharge_wait_times; + //uint8_t charge_count; + //uint8_t discharge_count; + //uint32_t bmu_tick; + //uint32_t charge_tick; + //uint8_t charge_table_num; + uint8_t rc_x_num; + uint8_t rc_y_num; + uint8_t rc_z_num; + uint8_t charge_strategy; + int32_t charge_sCaUAH; + int32_t charge_ratio; //this must be static + uint8_t charge_table_flag; + int32_t charge_end_current_th2; + int32_t charge_max_ratio; + uint8_t discharge_strategy; + int32_t discharge_sCaUAH; + int32_t discharge_ratio; //this must be static + uint8_t discharge_table_flag; + int32_t discharge_current_th; + uint32_t discharge_max_ratio; + int32_t dsg_end_voltage_hi; + int32_t dsg_end_voltage_th1; + int32_t dsg_end_voltage_th2; + uint8_t dsg_count_2; + uint8_t ocv_flag; + uint8_t vbus_ok; + uint8_t charge_full; + int32_t ri; + int32_t batt_ri; + int32_t line_impedance; + int32_t max_chg_reserve_percentage; + int32_t fix_chg_reserve_percentage; + uint8_t fast_charge_step; + int32_t start_fast_charge_ratio; + uint8_t charge_method_select; + int32_t max_charge_current_fix; + uint8_t ext_temp_measure; + uint8_t bmu_init_ok; + int32_t stored_capacity; + uint8_t lower_capacity_reserve; + uint8_t lower_capacity_soc_start; + uint8_t percent_10_reserve; + uint32_t power_on_100_vol; +}gas_gauge_t; + +/**************************************************************************** +* debug func * +***************************************************************************/ +#define bmt_dbg(fmt, args...) printk(KERN_ERR"[bmt]:"pr_fmt(fmt)"", ## args) + +#define batt_dbg(fmt, args...)\ +do {\ + if(parameter->config->debug)\ + printk(KERN_ERR"[bmt]:"pr_fmt(fmt)"", ## args);\ +} while(0) + +/**************************************************************************** +* extern variable/function declaration of table.c +***************************************************************************/ + +extern one_latitude_data_t ocv_data[OCV_DATA_NUM]; +extern int XAxisElement[XAxis]; +extern int YAxisElement[YAxis]; +extern int ZAxisElement[ZAxis]; +extern int RCtable[YAxis*ZAxis][XAxis]; +//extern one_latitude_data_t charge_data[CHARGE_DATA_NUM]; + +extern uint8_t battery_ri; +extern int32_t one_percent_rc; + +/**************************************************************************** +* extern variable/function defined by parameter.c +***************************************************************************/ +extern config_data_t config_data; +extern uint8_t check_chip_function; +//****************************************************************************** + +extern void bmu_init_parameter(parameter_data_t *parameter_customer); +extern void bmu_init_gg(gas_gauge_t *gas_gauge); +extern void bmu_reinit(int32_t mode); //mode = 0,wakeup ic,rewrite car; mode = 1, reinit from ocv +#ifndef CONFIG_OZ8806_M +extern int bmulib_init(void); +extern void bmulib_exit(void); +#endif + +extern void bmu_polling_loop(void); +extern void bmu_wake_up_chip(void); +extern void bmu_power_down_chip(void); +extern void charge_end_process(void); +extern void discharge_end_process(void); +extern int32_t oz8806_temp_read(int32_t *voltage); +extern int32_t afe_read_current(int32_t *dat); +extern int32_t afe_read_cell_volt(int32_t *voltage); + +extern void bmu_init_table(int **x, int **y, int **z, int **rc); + +extern const char * get_table_version(void); + +/**************************************************************************** +* extern variable/function defined by oz8806_battery.c +***************************************************************************/ +extern int32_t oz8806_vbus_voltage(void); +extern int32_t oz8806_get_simulated_temp(void); +extern int32_t oz8806_get_init_status(void); //if return 1, battery capacity is initiated successfully +extern void oz8806_battery_update_data(void); +extern int oz8806_get_soc(void); +extern int oz8806_get_remaincap(void); +extern int oz8806_get_battry_current(void); +extern int oz8806_get_battery_voltage(void); +extern int oz8806_get_battery_temp(void); + +//export symbol for bmulib +extern struct i2c_client * oz8806_get_client(void); +extern int8_t get_adapter_status(void); + +extern void oz8806_register_bmu_callback(void *bmu_polling_loop_func, + void *bmu_wake_up_chip_func, + void *bmu_power_down_chip_func, + void *charge_end_process_func, + void *discharge_end_process_func, + void *oz8806_temp_read_func, + void *oz8806_current_read_func, + void *oz8806_voltage_read_func); + +extern void unregister_bmu_callback(void); +extern void oz8806_set_batt_info_ptr(bmu_data_t *batt_info); +extern void oz8806_set_gas_gauge(gas_gauge_t *gas_gauge); +extern int oz8806_get_save_capacity(void); +extern int oz8806_get_soc_from_ext(void); +extern unsigned long oz8806_get_system_boot_time(void); +extern unsigned long oz8806_get_power_on_time(void); +extern unsigned long oz8806_get_boot_up_time(void); +extern int oz8806_wakeup_full_power(void); +extern void oz8806_reset_wkuptime(void); +/**************************************************************************** +* extern variable/function defined by oz8806_api_dev.c. +* This file is deprecated in Linux system. +***************************************************************************/ + +extern int is_battery_exchanged(void); +extern int32_t calculate_soc_result(void); +extern int32_t one_latitude_table(int32_t number,one_latitude_data_t *data,int32_t value); +extern uint8_t OZ8806_LookUpRCTable(int infVolt,int infCurr, int infTemp, int *infCal); +void charge_process(void); +void discharge_process(void); +void age_update(void); +void accmulate_data_update(void); + +extern bmu_data_t *batt_info; +extern gas_gauge_t *gas_gauge; +extern parameter_data_t parameter_customer; +extern parameter_data_t *parameter; + +extern int *rc_table; +extern int *xaxis_table; +extern int *yaxis_table; +extern int *zaxis_table; + +extern int32_t calculate_mah; +extern int32_t calculate_soc ; +extern uint32_t calculate_version; +extern int32_t age_soh; +extern int32_t cycle_soh; +extern uint32_t accmulate_dsg_data; + +#define X_AXIS gas_gauge->rc_x_num +#define Y_AXIS gas_gauge->rc_y_num +#define Z_AXIS gas_gauge->rc_z_num + +#endif //end _PARAMETER_H_ + diff --git a/drivers/power/sd77426_fg_mmi/table.c b/drivers/power/sd77426_fg_mmi/table.c new file mode 100644 index 000000000000..a17eee99b121 --- /dev/null +++ b/drivers/power/sd77426_fg_mmi/table.c @@ -0,0 +1,174 @@ +/***************************************************************************** +* Copyright(c) O2Micro, 2014. All rights reserved. +* +* O2Micro battery gauge driver +* File: table.c +* +* $Source: /data/code/CVS +* $Revision: 4.00.01 $ +* +* This program is free software and can be edistributed and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This Source Code Reference Design for O2MICRO Battery Gauge access (\u201cReference Design\u201d) +* is sole for the use of PRODUCT INTEGRATION REFERENCE ONLY, and contains confidential +* and privileged information of O2Micro International Limited. O2Micro shall have no +* liability to any PARTY FOR THE RELIABILITY, SERVICEABILITY FOR THE RESULT OF PRODUCT +* INTEGRATION, or results from: (i) any modification or attempted modification of the +* Reference Design by any party, or (ii) the combination, operation or use of the +* Reference Design with non-O2Micro Reference Design. +* +* Battery Manufacture: ATL +* Battery Model: M1892 +* Absolute Max Capacity(mAhr): 3661 +* Limited Charge Voltage(mV): 4400 +* Cutoff Discharge Voltage(mV): 3200 +* Equipment: "JY1-5","JY1-5","JY1-5","JY1-5","JY1-5","JY1-5","JY1-5","JY1-5","JY1-5","JY1-5","JY1-5","JY1-5","JY2-5","JY2-5","JY2-5","JY2-6","JY2-6","JY2-6","JY2-6","JY2-6","JY2-6","JY2-6","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-6","JY2-6","JY2-6","JY2-6","JY2-6","JY2-6","JY2-6","JY2-6","JY2-6","JY2-5","JY1-6","JY1-6","JY1-6","JY1-6","JY1-6","JY1-6","JY1-8","JY1-8","JY1-8","JY1-8","JY1-6","JY1-6","JY1-6","JY1-6","JY1-6","JY1-6","JY1-6","JY1-6","JY1-6","JY1-6","JY1-6","JY1-8","JY1-7","JY1-7","JY1-7","JY1-7","JY1-7","JY1-7","JY1-3","JY1-3","JY1-3","JY1-5","JY1-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY1-8","JY1-8","JY1-2","JY1-2","JY1-2","JY2-6","JY2-6","JY2-6" +* Tester: "SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ" +* Battery ID: "M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_4","M1892_4","M1892_4","M1892_2","M1892_2","M1892_2" +* Version = V00.8D.00 +* Date = 2018.3.26 +* Comment = +* +*****************************************************************************/ + +#include +#include "table.h" + +/***************************************************************************** +* Global variables section - Exported +* add declaration of global variables that will be exported here +* e.g. +* int8_t foo; +****************************************************************************/ +const char * table_version = "V00.8D.00-2018.3.26-M1892"; + +const char *battery_id[BATTERY_ID_NUM] = {"M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_4","M1892_4","M1892_4","M1892_2","M1892_2","M1892_2"}; + +one_latitude_data_t ocv_data[OCV_DATA_NUM] = {{3200, 0},{3511, 2},{3607, 3},{3672, 5},{3687, 6},{3689, 8},{3690, 9},{3692, 11},{3701, 13},{3713, 14},{3721, 16},{3730, 17},{3737, 19},{3743, 20},{3748, 22},{3754, 23},{3761, 25},{3768, 27},{3774, 28},{3780, 30},{3785, 31},{3790, 33},{3795, 34},{3799, 36},{3804, 38},{3810, 39},{3815, 41},{3822, 42},{3828, 44},{3835, 45},{3843, 47},{3852, 48},{3860, 50},{3869, 52},{3880, 53},{3893, 55},{3910, 56},{3929, 58},{3946, 59},{3961, 61},{3973, 63},{3985, 64},{3998, 66},{4014, 67},{4030, 69},{4046, 70},{4062, 72},{4076, 73},{4092, 75},{4109, 77},{4126, 78},{4143, 80},{4161, 81},{4178, 83},{4196, 84},{4214, 86},{4233, 88},{4251, 89},{4269, 91},{4287, 92},{4306, 94},{4325, 95},{4344, 97},{4363, 98},{4390, 100}}; + +//real current to soc +one_latitude_data_t charge_data[CHARGE_DATA_NUM] = {{73, 9999},{74, 9998},{76, 9995},{78, 9993},{80, 9991},{82, 9988},{84, 9986},{86, 9983},{88, 9982},{90, 9979},{92, 9978},{94, 9974},{96, 9972},{98, 9970},{100, 9967},{105, 9961},{110, 9956},{115, 9949},{120, 9944},{125, 9937},{130, 9931},{135, 9926},{140, 9919},{145, 9913},{150, 9908},{155, 9901},{160, 9895},{165, 9890},{170, 9883},{175, 9877},{180, 9872},{185, 9865},{190, 9859},{195, 9853},{200, 9847},{210, 9834},{220, 9822},{230, 9811},{240, 9798},{250, 9786},{260, 9774},{270, 9763},{280, 9750},{290, 9738},{300, 9727},{310, 9715},{320, 9702},{330, 9691},{340, 9679},{350, 9668},{360, 9657},{370, 9645},{380, 9633},{390, 9623},{400, 9611},{425, 9583},{450, 9556},{475, 9529},{500, 9503},{525, 9477},{550, 9452},{575, 9427},{600, 9403},{650, 9356},{727, 9297}}; + +//RC table X Axis value, in mV format +int32_t XAxisElement[XAxis] = {3200, 3220, 3260, 3280, 3300, 3320, 3340, 3360, 3380, 3400, 3420, 3440, 3460, 3480, 3500, 3511, 3525, 3538, 3551, 3557, 3563, 3574, 3584, 3595, 3608, 3621, 3636, 3643, 3650, 3662, 3667, 3678, 3695, 3709, 3720, 3729, 3739, 3748, 3757, 3765, 3773, 3782, 3791, 3802, 3813, 3826, 3840, 3855, 3875, 3884, 3900, 3916, 3925, 3939, 3947, 3959, 3968, 3977, 3992, 4019, 4030, 4044, 4060, 4070, 4081, 4097, 4114, 4125, 4142, 4154, 4171, 4183, 4194, 4212, 4230, 4242, 4254, 4271, 4289, 4301, 4313, 4331, 4345, 4352, 4369, 4390}; + +//RC table Y Axis value, in mA format +int32_t YAxisElement[YAxis] = {200, 500, 800, 1000, 1200, 1500, 1800, 2000, 2500, 3000}; + +// RC table Z Axis value, in 10*'C format +int32_t ZAxisElement[ZAxis] = {-199, -99, 0, 100, 200, 300, 400, 500, 600}; + +// contents of RC table, its unit is 10000C, 1C = DesignCapacity +int32_t RCtable[YAxis*ZAxis][XAxis]={ + + +//temp = -20.0 ^C +{1599, 1756, 2111, 2306, 2524, 2745, 2986, 3235, 3508, 3782, 4073, 4368, 4683, 4988, 5320, 5539, 5725, 5942, 6159, 6270, 6368, 6584, 6772, 6969, 7218, 7481, 7787, 7937, 8096, 8470, 8681, 9096, 9474, 9615, 9683, 9726, 9765, 9793, 9816, 9836, 9853, 9867, 9881, 9895, 9908, 9922, 9935, 9946, 9960, 9965, 9972, 9979, 9982, 9986, 9988, 9990, 9992, 9994, 9996, 9998, 9998, 9999, 9999, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{3866, 4135, 4697, 4997, 5280, 5582, 5862, 6170, 6458, 6735, 7040, 7343, 7658, 8008, 8441, 8713, 9160, 9455, 9563, 9610, 9645, 9697, 9737, 9773, 9806, 9832, 9857, 9868, 9878, 9893, 9898, 9910, 9926, 9937, 9946, 9952, 9958, 9963, 9968, 9971, 9975, 9979, 9982, 9986, 9989, 9992, 9994, 9997, 9998, 9999, 9999, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{3866, 4135, 4697, 4997, 5280, 5582, 5862, 6170, 6458, 6735, 7040, 7343, 7658, 8008, 8441, 8713, 9160, 9455, 9563, 9610, 9645, 9697, 9737, 9773, 9806, 9832, 9857, 9868, 9878, 9893, 9898, 9910, 9926, 9937, 9946, 9952, 9958, 9963, 9968, 9971, 9975, 9979, 9982, 9986, 9989, 9992, 9994, 9997, 9998, 9999, 9999, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{8577, 8760, 9134, 9311, 9458, 9576, 9659, 9723, 9771, 9809, 9841, 9869, 9894, 9914, 9936, 9944, 9956, 9965, 9975, 9977, 9978, 9986, 9988, 9989, 9995, 9997, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{8577, 8760, 9134, 9311, 9458, 9576, 9659, 9723, 9771, 9809, 9841, 9869, 9894, 9914, 9936, 9944, 9956, 9965, 9975, 9977, 9978, 9986, 9988, 9989, 9995, 9997, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{8577, 8760, 9134, 9311, 9458, 9576, 9659, 9723, 9771, 9809, 9841, 9869, 9894, 9914, 9936, 9944, 9956, 9965, 9975, 9977, 9978, 9986, 9988, 9989, 9995, 9997, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{8577, 8760, 9134, 9311, 9458, 9576, 9659, 9723, 9771, 9809, 9841, 9869, 9894, 9914, 9936, 9944, 9956, 9965, 9975, 9977, 9978, 9986, 9988, 9989, 9995, 9997, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{9936, 9947, 9976, 9985, 9987, 9992, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{9936, 9947, 9976, 9985, 9987, 9992, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{9936, 9947, 9976, 9985, 9987, 9992, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, + +//temp = -10.0 ^C +{595, 609, 640, 657, 676, 695, 717, 740, 765, 794, 826, 862, 907, 959, 1026, 1069, 1136, 1205, 1288, 1335, 1382, 1478, 1559, 1656, 1768, 1892, 2052, 2131, 2216, 2372, 2447, 2629, 2977, 3362, 3710, 4021, 4320, 4546, 4746, 4908, 5066, 5232, 5384, 5560, 5724, 5902, 6084, 6269, 6507, 6611, 6796, 6972, 7059, 7179, 7242, 7330, 7399, 7465, 7576, 7818, 7933, 8066, 8208, 8297, 8393, 8529, 8673, 8766, 8910, 9009, 9150, 9247, 9333, 9470, 9602, 9683, 9758, 9852, 9932, 9968, 9990, 10000, 10000, 10000, 10000, 10000}, +{1574, 1690, 1951, 2101, 2259, 2435, 2621, 2825, 3038, 3269, 3525, 3776, 4045, 4344, 4643, 4815, 5032, 5226, 5431, 5540, 5623, 5796, 5944, 6115, 6308, 6539, 6711, 6812, 6899, 7069, 7130, 7270, 7476, 7638, 7763, 7869, 7979, 8074, 8177, 8257, 8340, 8433, 8520, 8630, 8732, 8853, 8980, 9100, 9266, 9330, 9448, 9545, 9599, 9678, 9715, 9765, 9800, 9829, 9870, 9928, 9946, 9962, 9977, 9985, 9991, 9997, 9999, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{2254, 2418, 2794, 3019, 3247, 3485, 3755, 4044, 4328, 4630, 4934, 5245, 5552, 5848, 6142, 6308, 6492, 6688, 6859, 6942, 7022, 7160, 7294, 7429, 7595, 7743, 7916, 8008, 8093, 8223, 8275, 8389, 8567, 8706, 8804, 8893, 8987, 9063, 9134, 9196, 9250, 9315, 9381, 9446, 9509, 9581, 9645, 9703, 9769, 9795, 9832, 9866, 9883, 9906, 9919, 9932, 9943, 9953, 9966, 9986, 9991, 9996, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{2767, 2964, 3411, 3665, 3926, 4219, 4510, 4802, 5097, 5402, 5704, 5990, 6248, 6527, 6786, 6935, 7102, 7258, 7414, 7486, 7562, 7691, 7804, 7924, 8067, 8213, 8378, 8445, 8505, 8609, 8656, 8771, 8958, 9074, 9157, 9226, 9295, 9357, 9407, 9454, 9502, 9547, 9588, 9637, 9679, 9726, 9770, 9812, 9855, 9874, 9899, 9922, 9935, 9950, 9957, 9967, 9976, 9981, 9988, 9998, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{3085, 3290, 3756, 4013, 4286, 4554, 4846, 5119, 5403, 5695, 5958, 6223, 6474, 6710, 6951, 7067, 7225, 7361, 7513, 7578, 7647, 7755, 7862, 7972, 8102, 8232, 8382, 8449, 8512, 8622, 8674, 8755, 8897, 9019, 9110, 9178, 9245, 9298, 9347, 9392, 9432, 9478, 9525, 9580, 9632, 9687, 9737, 9784, 9841, 9863, 9895, 9925, 9938, 9953, 9963, 9976, 9981, 9986, 9996, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{4092, 4327, 4839, 5128, 5402, 5676, 5959, 6229, 6493, 6768, 7024, 7319, 7573, 7817, 8126, 8269, 8449, 8621, 8790, 8862, 8922, 9041, 9137, 9238, 9353, 9447, 9541, 9577, 9612, 9666, 9685, 9724, 9774, 9805, 9831, 9850, 9864, 9881, 9895, 9906, 9914, 9926, 9935, 9946, 9959, 9967, 9976, 9985, 9995, 9997, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{4926, 5178, 5688, 5983, 6210, 6491, 6730, 6965, 7197, 7427, 7654, 7858, 8087, 8313, 8552, 8674, 8833, 8966, 9099, 9154, 9210, 9302, 9377, 9449, 9527, 9585, 9653, 9676, 9702, 9737, 9750, 9776, 9815, 9843, 9863, 9875, 9893, 9903, 9913, 9925, 9932, 9945, 9953, 9959, 9972, 9979, 9988, 9992, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{5489, 5739, 6227, 6469, 6706, 6937, 7160, 7386, 7607, 7824, 8043, 8251, 8456, 8653, 8864, 8974, 9097, 9205, 9307, 9348, 9392, 9461, 9519, 9573, 9632, 9676, 9725, 9747, 9765, 9792, 9806, 9827, 9855, 9882, 9896, 9908, 9921, 9928, 9942, 9946, 9957, 9967, 9971, 9976, 9987, 9993, 9995, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{8331, 8508, 8860, 9036, 9202, 9363, 9514, 9621, 9684, 9729, 9768, 9799, 9819, 9848, 9864, 9882, 9892, 9902, 9920, 9923, 9925, 9938, 9945, 9952, 9965, 9971, 9982, 9985, 9987, 9989, 9991, 9994, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{9172, 9288, 9489, 9567, 9625, 9670, 9705, 9737, 9764, 9794, 9816, 9841, 9856, 9873, 9899, 9906, 9922, 9928, 9943, 9948, 9952, 9962, 9972, 9974, 9985, 9988, 9992, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, + +//temp = 0.0 ^C +{595, 609, 640, 657, 676, 695, 717, 740, 765, 794, 826, 862, 907, 959, 1026, 1069, 1136, 1205, 1288, 1335, 1382, 1478, 1559, 1656, 1768, 1892, 2052, 2131, 2216, 2372, 2447, 2629, 2977, 3362, 3710, 4021, 4320, 4546, 4746, 4908, 5066, 5232, 5384, 5560, 5724, 5902, 6084, 6269, 6507, 6611, 6796, 6972, 7059, 7179, 7242, 7330, 7399, 7465, 7576, 7818, 7933, 8066, 8208, 8297, 8393, 8529, 8673, 8766, 8910, 9009, 9150, 9247, 9333, 9470, 9602, 9683, 9758, 9852, 9932, 9968, 9990, 10000, 10000, 10000, 10000, 10000}, +{1109, 1163, 1279, 1346, 1420, 1503, 1592, 1689, 1801, 1913, 2040, 2178, 2328, 2498, 2685, 2805, 2951, 3104, 3271, 3356, 3432, 3600, 3746, 3925, 4142, 4379, 4657, 4805, 4931, 5162, 5250, 5454, 5753, 5932, 6085, 6206, 6332, 6447, 6556, 6649, 6745, 6845, 6947, 7068, 7185, 7321, 7462, 7604, 7781, 7860, 7985, 8105, 8175, 8286, 8348, 8443, 8525, 8597, 8712, 8920, 9004, 9105, 9220, 9288, 9360, 9461, 9567, 9628, 9716, 9772, 9841, 9883, 9914, 9955, 9980, 9991, 9998, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{1431, 1527, 1757, 1891, 2063, 2207, 2362, 2544, 2737, 2937, 3160, 3390, 3634, 3911, 4190, 4350, 4570, 4766, 4972, 5070, 5159, 5339, 5501, 5667, 5854, 6049, 6274, 6369, 6454, 6619, 6682, 6819, 7024, 7182, 7294, 7394, 7506, 7595, 7683, 7763, 7834, 7903, 7980, 8097, 8206, 8312, 8424, 8539, 8686, 8751, 8869, 8987, 9047, 9146, 9197, 9271, 9324, 9377, 9462, 9598, 9649, 9709, 9768, 9802, 9836, 9877, 9915, 9936, 9958, 9972, 9986, 9994, 9999, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{1573, 1682, 1927, 2086, 2254, 2433, 2641, 2852, 3084, 3323, 3580, 3849, 4132, 4426, 4727, 4890, 5110, 5316, 5515, 5607, 5702, 5864, 6013, 6166, 6344, 6530, 6718, 6804, 6891, 7036, 7098, 7224, 7402, 7555, 7661, 7757, 7855, 7937, 8024, 8095, 8167, 8245, 8325, 8415, 8502, 8606, 8714, 8818, 8962, 9025, 9140, 9238, 9295, 9377, 9423, 9484, 9530, 9574, 9642, 9747, 9785, 9828, 9870, 9891, 9912, 9940, 9961, 9973, 9986, 9994, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{1805, 1928, 2228, 2399, 2592, 2800, 3030, 3264, 3530, 3800, 4078, 4370, 4661, 4965, 5276, 5441, 5651, 5844, 6028, 6118, 6197, 6350, 6481, 6627, 6791, 6947, 7123, 7205, 7281, 7409, 7465, 7583, 7751, 7890, 7996, 8079, 8173, 8253, 8331, 8397, 8459, 8534, 8606, 8692, 8771, 8868, 8969, 9068, 9203, 9259, 9356, 9441, 9486, 9559, 9594, 9647, 9681, 9716, 9769, 9847, 9873, 9899, 9927, 9942, 9954, 9974, 9985, 9993, 9998, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{2254, 2413, 2783, 2992, 3220, 3468, 3726, 3988, 4271, 4562, 4861, 5165, 5461, 5754, 6043, 6188, 6382, 6552, 6711, 6787, 6861, 7001, 7118, 7246, 7387, 7530, 7688, 7763, 7835, 7962, 8007, 8111, 8268, 8394, 8487, 8563, 8642, 8717, 8784, 8846, 8906, 8966, 9034, 9103, 9174, 9253, 9338, 9417, 9518, 9556, 9624, 9689, 9720, 9769, 9791, 9823, 9843, 9864, 9890, 9935, 9948, 9960, 9978, 9984, 9990, 9998, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{2760, 2951, 3358, 3587, 3835, 4097, 4388, 4666, 4953, 5253, 5544, 5829, 6109, 6369, 6638, 6786, 6978, 7128, 7257, 7315, 7378, 7491, 7600, 7719, 7849, 7977, 8123, 8192, 8247, 8364, 8408, 8505, 8646, 8761, 8840, 8910, 8981, 9040, 9104, 9156, 9203, 9262, 9317, 9377, 9430, 9497, 9563, 9622, 9699, 9724, 9774, 9820, 9837, 9868, 9880, 9904, 9918, 9927, 9947, 9974, 9982, 9990, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{3087, 3276, 3707, 3953, 4207, 4474, 4758, 5036, 5338, 5616, 5899, 6164, 6425, 6676, 6908, 7037, 7190, 7336, 7483, 7547, 7607, 7715, 7819, 7925, 8053, 8172, 8314, 8370, 8435, 8538, 8582, 8664, 8797, 8906, 8979, 9049, 9110, 9168, 9227, 9271, 9321, 9368, 9418, 9471, 9528, 9580, 9644, 9697, 9760, 9784, 9823, 9861, 9875, 9899, 9912, 9932, 9943, 9952, 9966, 9988, 9994, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{4088, 4307, 4809, 5076, 5338, 5611, 5885, 6145, 6403, 6647, 6895, 7117, 7342, 7570, 7788, 7897, 8036, 8167, 8286, 8349, 8402, 8500, 8587, 8681, 8788, 8887, 8991, 9043, 9086, 9164, 9200, 9257, 9358, 9429, 9481, 9523, 9571, 9603, 9637, 9670, 9690, 9722, 9743, 9775, 9803, 9827, 9854, 9881, 9912, 9923, 9941, 9956, 9969, 9981, 9983, 9990, 9993, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{5060, 5300, 5799, 6049, 6298, 6540, 6777, 7017, 7238, 7464, 7673, 7875, 8082, 8278, 8452, 8554, 8679, 8776, 8876, 8923, 8970, 9042, 9110, 9176, 9261, 9331, 9415, 9445, 9484, 9540, 9557, 9596, 9655, 9696, 9730, 9752, 9774, 9791, 9810, 9831, 9844, 9856, 9877, 9889, 9909, 9920, 9940, 9951, 9970, 9980, 9988, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, + +//temp = 10.0 ^C +{255, 263, 284, 295, 307, 320, 334, 349, 366, 384, 403, 424, 446, 471, 499, 514, 535, 557, 579, 591, 602, 625, 650, 681, 732, 811, 978, 1121, 1288, 1460, 1522, 1659, 1909, 2162, 2425, 2687, 3063, 3423, 3713, 3937, 4146, 4361, 4566, 4789, 4998, 5218, 5437, 5638, 5871, 5965, 6143, 6323, 6424, 6583, 6674, 6795, 6874, 6943, 7052, 7271, 7376, 7525, 7680, 7769, 7868, 8010, 8159, 8257, 8404, 8508, 8654, 8760, 8852, 9003, 9156, 9257, 9357, 9496, 9637, 9728, 9815, 9925, 9982, 9996, 10000, 10000}, +{451, 466, 498, 518, 536, 558, 582, 607, 634, 667, 704, 749, 803, 870, 955, 1012, 1098, 1199, 1311, 1368, 1421, 1527, 1620, 1732, 1876, 2035, 2250, 2368, 2492, 2753, 2878, 3178, 3688, 4087, 4348, 4543, 4741, 4906, 5066, 5200, 5326, 5461, 5593, 5742, 5886, 6047, 6219, 6403, 6641, 6739, 6899, 7036, 7107, 7214, 7273, 7365, 7440, 7521, 7660, 7911, 8008, 8126, 8262, 8347, 8440, 8576, 8718, 8812, 8950, 9051, 9187, 9286, 9372, 9509, 9640, 9722, 9800, 9891, 9960, 9985, 9997, 10000, 10000, 10000, 10000, 10000}, +{653, 678, 739, 773, 812, 858, 910, 970, 1040, 1123, 1226, 1334, 1471, 1617, 1784, 1886, 2013, 2152, 2310, 2379, 2459, 2619, 2770, 2963, 3207, 3476, 3816, 3979, 4145, 4421, 4529, 4749, 5057, 5288, 5457, 5591, 5729, 5850, 5966, 6071, 6170, 6283, 6395, 6531, 6659, 6808, 6960, 7103, 7279, 7352, 7476, 7603, 7677, 7799, 7872, 7977, 8061, 8138, 8262, 8485, 8575, 8690, 8816, 8896, 8985, 9112, 9239, 9321, 9448, 9532, 9646, 9720, 9780, 9869, 9935, 9963, 9980, 9996, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{819, 857, 948, 1003, 1059, 1131, 1210, 1306, 1411, 1529, 1670, 1817, 1989, 2180, 2393, 2524, 2702, 2883, 3081, 3175, 3272, 3467, 3653, 3873, 4128, 4402, 4701, 4837, 4961, 5169, 5253, 5426, 5673, 5869, 6013, 6131, 6254, 6366, 6472, 6569, 6666, 6767, 6872, 6991, 7103, 7229, 7354, 7478, 7639, 7708, 7841, 7978, 8058, 8183, 8250, 8345, 8419, 8490, 8611, 8824, 8911, 9020, 9140, 9213, 9290, 9408, 9524, 9590, 9696, 9763, 9845, 9892, 9924, 9962, 9984, 9995, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{992, 1048, 1177, 1251, 1337, 1437, 1548, 1676, 1822, 1980, 2161, 2352, 2564, 2800, 3067, 3224, 3431, 3644, 3859, 3968, 4073, 4273, 4460, 4668, 4903, 5128, 5374, 5485, 5587, 5758, 5832, 5979, 6205, 6378, 6506, 6616, 6734, 6832, 6930, 7018, 7103, 7194, 7282, 7387, 7485, 7595, 7709, 7831, 7993, 8067, 8200, 8338, 8414, 8526, 8589, 8679, 8750, 8820, 8933, 9131, 9212, 9309, 9419, 9480, 9554, 9648, 9741, 9794, 9863, 9901, 9941, 9960, 9975, 9992, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{1247, 1333, 1529, 1645, 1773, 1921, 2085, 2267, 2469, 2685, 2917, 3161, 3433, 3717, 4022, 4205, 4434, 4650, 4872, 4974, 5078, 5258, 5421, 5589, 5785, 5967, 6172, 6267, 6354, 6501, 6562, 6686, 6885, 7038, 7152, 7250, 7346, 7441, 7523, 7596, 7664, 7742, 7822, 7916, 8004, 8104, 8219, 8338, 8500, 8570, 8697, 8817, 8882, 8988, 9043, 9127, 9188, 9250, 9347, 9513, 9576, 9655, 9731, 9777, 9823, 9872, 9916, 9936, 9961, 9975, 9992, 9998, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{1497, 1609, 1871, 2022, 2192, 2385, 2583, 2812, 3057, 3316, 3592, 3870, 4171, 4481, 4796, 4971, 5188, 5395, 5597, 5691, 5774, 5943, 6076, 6222, 6396, 6559, 6739, 6825, 6902, 7043, 7098, 7215, 7388, 7528, 7634, 7724, 7806, 7889, 7964, 8034, 8099, 8169, 8243, 8334, 8419, 8521, 8626, 8742, 8889, 8950, 9067, 9173, 9232, 9324, 9376, 9442, 9495, 9547, 9626, 9752, 9792, 9842, 9886, 9908, 9925, 9951, 9969, 9981, 9996, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{1649, 1775, 2067, 2232, 2420, 2631, 2856, 3088, 3352, 3645, 3926, 4210, 4514, 4836, 5142, 5323, 5537, 5734, 5916, 6002, 6077, 6225, 6364, 6499, 6663, 6819, 6983, 7061, 7136, 7264, 7318, 7435, 7600, 7739, 7835, 7915, 7999, 8074, 8149, 8216, 8284, 8356, 8431, 8514, 8601, 8693, 8801, 8910, 9053, 9113, 9218, 9321, 9380, 9461, 9511, 9578, 9624, 9664, 9727, 9833, 9862, 9896, 9926, 9941, 9957, 9976, 9985, 9996, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{2271, 2448, 2837, 3058, 3294, 3543, 3809, 4094, 4399, 4687, 5000, 5296, 5595, 5886, 6160, 6312, 6497, 6660, 6816, 6890, 6957, 7083, 7195, 7316, 7463, 7591, 7737, 7800, 7863, 7979, 8025, 8124, 8261, 8387, 8473, 8546, 8619, 8694, 8756, 8818, 8873, 8943, 9003, 9085, 9150, 9234, 9328, 9412, 9526, 9578, 9654, 9723, 9757, 9796, 9826, 9850, 9875, 9887, 9914, 9954, 9965, 9981, 9992, 9994, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{2933, 3136, 3591, 3842, 4099, 4389, 4676, 4957, 5259, 5549, 5831, 6098, 6372, 6629, 6871, 7004, 7157, 7308, 7441, 7507, 7577, 7686, 7781, 7891, 8012, 8127, 8257, 8323, 8375, 8475, 8518, 8603, 8735, 8848, 8921, 8988, 9055, 9121, 9179, 9226, 9278, 9333, 9389, 9453, 9511, 9574, 9643, 9708, 9776, 9804, 9837, 9877, 9888, 9911, 9926, 9938, 9955, 9963, 9976, 9993, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, + +//temp = 20.0 ^C +{101, 108, 125, 134, 144, 156, 167, 182, 197, 212, 230, 249, 269, 292, 316, 331, 351, 370, 390, 401, 411, 431, 449, 471, 498, 529, 579, 616, 676, 946, 1172, 1351, 1598, 1827, 2066, 2309, 2578, 2888, 3195, 3447, 3686, 3931, 4159, 4409, 4644, 4892, 5137, 5361, 5611, 5706, 5866, 6025, 6124, 6293, 6382, 6509, 6605, 6692, 6829, 7064, 7163, 7308, 7469, 7561, 7662, 7806, 7957, 8057, 8205, 8309, 8457, 8562, 8653, 8805, 8958, 9059, 9161, 9303, 9455, 9553, 9652, 9800, 9900, 9942, 10000, 10000}, +{212, 221, 244, 256, 269, 284, 300, 317, 335, 355, 377, 401, 425, 454, 486, 505, 529, 557, 588, 606, 625, 668, 718, 795, 940, 1196, 1437, 1528, 1621, 1803, 1890, 2094, 2542, 3032, 3387, 3649, 3905, 4116, 4317, 4480, 4642, 4810, 4968, 5153, 5325, 5512, 5698, 5877, 6101, 6212, 6394, 6563, 6651, 6781, 6852, 6950, 7025, 7097, 7224, 7490, 7589, 7712, 7853, 7940, 8033, 8173, 8316, 8411, 8553, 8657, 8800, 8900, 8990, 9142, 9292, 9390, 9490, 9626, 9767, 9851, 9925, 9988, 10000, 10000, 10000, 10000}, +{295, 309, 338, 353, 370, 390, 409, 433, 457, 483, 512, 546, 586, 638, 702, 745, 808, 886, 988, 1049, 1115, 1259, 1388, 1530, 1705, 1898, 2164, 2316, 2486, 2843, 3016, 3367, 3823, 4146, 4371, 4552, 4740, 4891, 5046, 5173, 5295, 5425, 5557, 5701, 5845, 6005, 6178, 6357, 6584, 6675, 6826, 6965, 7038, 7152, 7220, 7320, 7407, 7493, 7626, 7864, 7958, 8077, 8211, 8296, 8386, 8522, 8664, 8755, 8896, 8997, 9134, 9235, 9323, 9468, 9608, 9700, 9785, 9891, 9966, 9987, 10000, 10000, 10000, 10000, 10000, 10000}, +{357, 374, 406, 426, 448, 470, 496, 525, 559, 597, 643, 696, 758, 840, 943, 1021, 1136, 1268, 1414, 1489, 1559, 1699, 1837, 2006, 2239, 2522, 2924, 3137, 3340, 3677, 3806, 4058, 4409, 4665, 4852, 5009, 5167, 5300, 5436, 5551, 5659, 5775, 5894, 6029, 6167, 6327, 6498, 6661, 6860, 6941, 7073, 7203, 7276, 7404, 7478, 7586, 7671, 7750, 7874, 8102, 8193, 8314, 8447, 8527, 8622, 8752, 8890, 8981, 9117, 9218, 9354, 9448, 9531, 9670, 9792, 9866, 9922, 9973, 9995, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{425, 443, 484, 510, 534, 568, 602, 642, 692, 749, 814, 889, 989, 1115, 1277, 1391, 1536, 1690, 1856, 1948, 2030, 2219, 2402, 2636, 2958, 3304, 3705, 3874, 4037, 4285, 4390, 4593, 4887, 5111, 5275, 5413, 5548, 5672, 5790, 5892, 5996, 6107, 6221, 6356, 6493, 6636, 6790, 6929, 7105, 7175, 7308, 7450, 7532, 7660, 7733, 7832, 7915, 7989, 8114, 8337, 8432, 8542, 8673, 8755, 8846, 8976, 9112, 9202, 9335, 9427, 9556, 9643, 9719, 9835, 9922, 9956, 9981, 9996, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{538, 566, 629, 668, 711, 761, 814, 884, 961, 1051, 1163, 1298, 1463, 1667, 1901, 2047, 2256, 2475, 2717, 2843, 2967, 3214, 3454, 3714, 4008, 4278, 4565, 4694, 4806, 4997, 5085, 5251, 5492, 5684, 5825, 5946, 6067, 6181, 6293, 6391, 6483, 6596, 6697, 6811, 6921, 7044, 7166, 7287, 7460, 7540, 7683, 7827, 7902, 8023, 8089, 8186, 8262, 8334, 8460, 8678, 8762, 8875, 9006, 9080, 9168, 9290, 9420, 9499, 9622, 9705, 9806, 9871, 9913, 9958, 9987, 9995, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{453, 478, 535, 571, 608, 655, 711, 769, 847, 938, 1050, 1181, 1364, 1616, 1950, 2166, 2463, 2763, 3071, 3226, 3365, 3623, 3838, 4064, 4310, 4535, 4788, 4894, 5000, 5179, 5255, 5403, 5632, 5818, 5956, 6072, 6193, 6303, 6411, 6501, 6588, 6686, 6782, 6880, 6981, 7092, 7211, 7349, 7531, 7613, 7755, 7898, 7971, 8092, 8158, 8255, 8330, 8402, 8529, 8752, 8839, 8955, 9082, 9160, 9252, 9385, 9526, 9609, 9735, 9817, 9903, 9940, 9965, 9990, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{578, 610, 685, 736, 785, 846, 919, 1005, 1104, 1230, 1389, 1589, 1859, 2197, 2604, 2848, 3159, 3456, 3744, 3873, 4001, 4206, 4389, 4580, 4792, 4996, 5218, 5315, 5410, 5579, 5647, 5784, 5999, 6172, 6305, 6419, 6531, 6631, 6727, 6811, 6887, 6968, 7052, 7148, 7237, 7354, 7483, 7615, 7792, 7867, 7999, 8137, 8209, 8326, 8388, 8484, 8558, 8631, 8753, 8966, 9051, 9160, 9280, 9360, 9445, 9567, 9686, 9764, 9861, 9909, 9955, 9972, 9989, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{857, 915, 1051, 1132, 1230, 1348, 1489, 1669, 1879, 2137, 2461, 2825, 3224, 3644, 4025, 4224, 4474, 4682, 4892, 4985, 5065, 5230, 5369, 5513, 5684, 5851, 6039, 6125, 6205, 6350, 6406, 6539, 6721, 6872, 6972, 7056, 7148, 7227, 7301, 7375, 7443, 7528, 7602, 7702, 7796, 7905, 8028, 8152, 8318, 8388, 8517, 8648, 8715, 8827, 8892, 8982, 9051, 9119, 9226, 9428, 9509, 9607, 9718, 9776, 9834, 9895, 9936, 9961, 9985, 9996, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{1262, 1361, 1608, 1758, 1932, 2149, 2400, 2685, 3013, 3375, 3739, 4090, 4438, 4759, 5062, 5217, 5408, 5581, 5750, 5827, 5893, 6031, 6155, 6284, 6434, 6572, 6738, 6810, 6885, 6996, 7047, 7147, 7296, 7420, 7522, 7599, 7685, 7769, 7840, 7906, 7975, 8051, 8132, 8217, 8314, 8418, 8534, 8644, 8799, 8875, 8991, 9110, 9172, 9279, 9340, 9417, 9490, 9547, 9645, 9803, 9847, 9893, 9928, 9947, 9967, 9987, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, + +//temp = 30.0 ^C +{1, 8, 23, 32, 43, 53, 66, 79, 94, 110, 127, 146, 167, 190, 215, 230, 250, 269, 290, 300, 310, 330, 349, 371, 399, 428, 472, 502, 547, 765, 992, 1251, 1505, 1749, 2011, 2263, 2510, 2748, 3003, 3255, 3501, 3757, 4000, 4261, 4502, 4758, 5014, 5249, 5507, 5598, 5750, 5907, 6000, 6156, 6256, 6403, 6506, 6600, 6747, 6997, 7100, 7252, 7406, 7501, 7600, 7749, 7900, 7999, 8147, 8253, 8400, 8504, 8598, 8750, 8902, 9004, 9105, 9249, 9400, 9499, 9599, 9749, 9856, 9904, 9991, 10000}, +{64, 72, 91, 103, 113, 126, 141, 157, 174, 192, 212, 233, 257, 284, 313, 331, 353, 376, 402, 415, 427, 453, 483, 526, 604, 737, 1087, 1243, 1353, 1530, 1614, 1813, 2211, 2629, 2971, 3256, 3537, 3768, 3983, 4157, 4329, 4510, 4688, 4885, 5073, 5275, 5474, 5666, 5892, 5991, 6164, 6350, 6447, 6593, 6671, 6781, 6864, 6944, 7073, 7338, 7442, 7569, 7709, 7797, 7895, 8034, 8179, 8274, 8420, 8520, 8662, 8762, 8854, 9006, 9157, 9257, 9359, 9501, 9652, 9747, 9839, 9950, 9993, 10000, 10000, 10000}, +{106, 117, 140, 152, 166, 182, 199, 217, 238, 260, 283, 310, 337, 370, 408, 433, 467, 510, 560, 594, 626, 705, 798, 958, 1203, 1420, 1674, 1798, 1934, 2226, 2368, 2704, 3241, 3621, 3887, 4083, 4283, 4460, 4623, 4765, 4905, 5055, 5198, 5361, 5518, 5688, 5869, 6045, 6280, 6383, 6556, 6712, 6795, 6918, 6987, 7087, 7176, 7260, 7398, 7640, 7736, 7856, 7992, 8078, 8169, 8304, 8446, 8539, 8681, 8781, 8920, 9018, 9110, 9262, 9410, 9508, 9608, 9744, 9874, 9939, 9981, 10000, 10000, 10000, 10000, 10000}, +{133, 145, 171, 186, 201, 219, 240, 259, 282, 308, 335, 365, 401, 445, 504, 541, 599, 672, 761, 817, 877, 1016, 1180, 1365, 1586, 1817, 2141, 2322, 2518, 2909, 3068, 3380, 3802, 4107, 4323, 4498, 4674, 4824, 4973, 5100, 5223, 5357, 5485, 5633, 5779, 5938, 6112, 6294, 6524, 6615, 6771, 6911, 6986, 7115, 7185, 7294, 7382, 7460, 7592, 7822, 7917, 8035, 8169, 8250, 8345, 8476, 8617, 8707, 8847, 8947, 9086, 9185, 9278, 9424, 9572, 9669, 9765, 9878, 9963, 9988, 10000, 10000, 10000, 10000, 10000, 10000}, +{165, 180, 209, 225, 243, 263, 285, 312, 336, 368, 402, 444, 495, 561, 644, 705, 798, 904, 1055, 1140, 1226, 1396, 1565, 1766, 2029, 2337, 2783, 3002, 3210, 3530, 3661, 3906, 4251, 4511, 4704, 4860, 5014, 5153, 5289, 5399, 5513, 5633, 5753, 5893, 6030, 6186, 6357, 6528, 6737, 6816, 6961, 7099, 7181, 7313, 7385, 7491, 7573, 7651, 7775, 8006, 8099, 8216, 8344, 8429, 8517, 8649, 8788, 8877, 9017, 9114, 9253, 9352, 9440, 9589, 9729, 9816, 9891, 9959, 9993, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{219, 234, 270, 294, 316, 341, 370, 405, 443, 492, 546, 611, 699, 805, 952, 1057, 1221, 1399, 1613, 1719, 1835, 2063, 2287, 2578, 2949, 3309, 3688, 3853, 3997, 4232, 4331, 4525, 4807, 5034, 5192, 5328, 5466, 5586, 5704, 5809, 5913, 6021, 6134, 6269, 6397, 6548, 6698, 6839, 7018, 7100, 7247, 7395, 7475, 7599, 7670, 7775, 7851, 7923, 8044, 8268, 8361, 8474, 8603, 8685, 8771, 8902, 9041, 9130, 9267, 9361, 9501, 9594, 9681, 9807, 9911, 9954, 9977, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{282, 304, 353, 380, 414, 448, 493, 542, 600, 667, 748, 851, 984, 1158, 1401, 1566, 1803, 2062, 2350, 2493, 2642, 2931, 3199, 3490, 3788, 4054, 4347, 4474, 4589, 4786, 4870, 5032, 5280, 5482, 5623, 5740, 5869, 5981, 6086, 6188, 6284, 6394, 6499, 6618, 6729, 6856, 6981, 7121, 7301, 7382, 7530, 7669, 7749, 7870, 7939, 8032, 8111, 8184, 8305, 8524, 8615, 8728, 8852, 8931, 9022, 9147, 9281, 9369, 9507, 9600, 9724, 9807, 9869, 9943, 9982, 9996, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{339, 363, 419, 458, 496, 541, 597, 657, 731, 818, 925, 1056, 1237, 1481, 1801, 2004, 2300, 2602, 2931, 3089, 3237, 3500, 3727, 3962, 4217, 4453, 4710, 4822, 4930, 5106, 5184, 5338, 5564, 5751, 5888, 5996, 6117, 6226, 6334, 6426, 6519, 6614, 6714, 6818, 6929, 7041, 7172, 7304, 7490, 7568, 7707, 7848, 7928, 8044, 8110, 8205, 8279, 8351, 8472, 8688, 8776, 8889, 9014, 9091, 9183, 9309, 9442, 9528, 9654, 9738, 9844, 9907, 9943, 9980, 9998, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{534, 574, 668, 724, 793, 869, 965, 1070, 1202, 1369, 1597, 1882, 2239, 2660, 3116, 3361, 3668, 3942, 4188, 4303, 4403, 4586, 4750, 4920, 5108, 5294, 5501, 5592, 5677, 5830, 5896, 6026, 6227, 6392, 6511, 6611, 6723, 6803, 6896, 6972, 7043, 7122, 7213, 7309, 7408, 7520, 7646, 7779, 7949, 8025, 8158, 8292, 8364, 8478, 8543, 8632, 8711, 8777, 8890, 9102, 9192, 9291, 9419, 9489, 9573, 9688, 9799, 9853, 9924, 9955, 9980, 9990, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{1374, 1489, 1778, 1949, 2150, 2372, 2625, 2887, 3189, 3511, 3858, 4209, 4559, 4915, 5241, 5401, 5589, 5732, 5864, 5919, 5973, 6079, 6203, 6334, 6494, 6642, 6819, 6901, 6970, 7105, 7149, 7269, 7430, 7569, 7674, 7762, 7851, 7927, 8016, 8082, 8156, 8231, 8316, 8405, 8501, 8612, 8725, 8848, 9014, 9081, 9202, 9333, 9393, 9506, 9557, 9641, 9700, 9748, 9824, 9906, 9928, 9952, 9969, 9979, 9987, 9996, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, + +//temp = 40.0 ^C +{48, 55, 71, 81, 91, 102, 114, 128, 143, 159, 176, 196, 216, 240, 265, 280, 300, 319, 341, 351, 361, 381, 400, 422, 450, 480, 524, 554, 603, 883, 1161, 1327, 1602, 1855, 2146, 2368, 2587, 2762, 2949, 3177, 3423, 3685, 3926, 4191, 4436, 4703, 4957, 5201, 5458, 5546, 5688, 5833, 5927, 6082, 6173, 6316, 6428, 6532, 6683, 6940, 7050, 7198, 7352, 7446, 7547, 7694, 7848, 7946, 8097, 8200, 8349, 8452, 8545, 8700, 8854, 8954, 9054, 9199, 9354, 9454, 9557, 9712, 9825, 9878, 9984, 10000}, +{95, 103, 120, 131, 142, 157, 170, 185, 202, 219, 240, 261, 284, 310, 339, 356, 379, 400, 425, 437, 449, 474, 497, 533, 594, 708, 1042, 1250, 1366, 1550, 1632, 1833, 2228, 2571, 2846, 3091, 3363, 3602, 3828, 4015, 4194, 4380, 4565, 4771, 4965, 5174, 5380, 5567, 5782, 5874, 6037, 6209, 6312, 6474, 6557, 6677, 6762, 6846, 6982, 7248, 7352, 7481, 7625, 7712, 7810, 7950, 8097, 8192, 8339, 8442, 8585, 8685, 8777, 8930, 9081, 9183, 9286, 9429, 9586, 9688, 9788, 9921, 9986, 9998, 10000, 10000}, +{123, 133, 153, 167, 178, 195, 211, 227, 247, 268, 291, 315, 341, 372, 406, 427, 454, 486, 526, 549, 575, 637, 715, 835, 1089, 1358, 1615, 1736, 1870, 2127, 2246, 2534, 2999, 3401, 3667, 3880, 4098, 4279, 4449, 4599, 4744, 4902, 5048, 5220, 5382, 5557, 5735, 5906, 6125, 6221, 6397, 6568, 6655, 6791, 6864, 6968, 7054, 7138, 7283, 7525, 7624, 7743, 7885, 7968, 8060, 8200, 8344, 8436, 8579, 8679, 8818, 8919, 9012, 9162, 9311, 9417, 9520, 9665, 9814, 9898, 9961, 9999, 10000, 10000, 10000, 10000}, +{147, 155, 180, 194, 209, 223, 242, 259, 281, 303, 330, 357, 387, 422, 464, 493, 537, 593, 668, 710, 759, 876, 1026, 1237, 1470, 1702, 1996, 2150, 2316, 2637, 2786, 3093, 3549, 3878, 4106, 4288, 4480, 4637, 4788, 4922, 5055, 5192, 5332, 5480, 5629, 5793, 5964, 6134, 6357, 6454, 6618, 6768, 6853, 6983, 7056, 7167, 7252, 7336, 7466, 7702, 7795, 7918, 8053, 8137, 8232, 8360, 8502, 8596, 8733, 8834, 8974, 9076, 9165, 9318, 9470, 9569, 9674, 9811, 9933, 9975, 9998, 10000, 10000, 10000, 10000, 10000}, +{170, 179, 208, 222, 238, 257, 277, 296, 320, 348, 374, 407, 445, 494, 561, 608, 680, 769, 891, 965, 1040, 1222, 1397, 1597, 1853, 2106, 2486, 2681, 2876, 3216, 3354, 3627, 4000, 4288, 4479, 4645, 4810, 4953, 5092, 5214, 5333, 5462, 5589, 5726, 5867, 6023, 6189, 6359, 6578, 6666, 6814, 6959, 7042, 7172, 7249, 7352, 7438, 7515, 7645, 7874, 7965, 8081, 8215, 8299, 8393, 8523, 8661, 8755, 8893, 8992, 9134, 9232, 9320, 9473, 9626, 9726, 9819, 9926, 9987, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{200, 213, 242, 261, 278, 302, 324, 352, 378, 412, 457, 506, 573, 655, 775, 860, 993, 1152, 1345, 1448, 1554, 1768, 1967, 2210, 2535, 2899, 3313, 3493, 3655, 3919, 4028, 4241, 4545, 4780, 4955, 5092, 5237, 5367, 5489, 5598, 5706, 5822, 5931, 6065, 6195, 6345, 6508, 6664, 6855, 6936, 7088, 7234, 7315, 7438, 7509, 7611, 7686, 7763, 7885, 8114, 8204, 8321, 8450, 8535, 8620, 8750, 8887, 8978, 9114, 9213, 9355, 9454, 9544, 9696, 9835, 9906, 9960, 9994, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{303, 322, 366, 391, 418, 451, 485, 529, 582, 646, 722, 817, 944, 1114, 1338, 1496, 1705, 1943, 2199, 2324, 2461, 2740, 2997, 3287, 3599, 3876, 4169, 4304, 4414, 4614, 4700, 4865, 5126, 5323, 5472, 5597, 5719, 5831, 5930, 6026, 6119, 6220, 6326, 6439, 6554, 6683, 6821, 6957, 7136, 7222, 7357, 7495, 7567, 7690, 7755, 7851, 7926, 8001, 8121, 8336, 8425, 8532, 8661, 8737, 8831, 8956, 9092, 9183, 9322, 9424, 9566, 9665, 9752, 9881, 9955, 9983, 9999, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{368, 393, 440, 470, 508, 546, 591, 651, 713, 796, 901, 1027, 1200, 1408, 1688, 1877, 2140, 2421, 2721, 2876, 3030, 3306, 3546, 3796, 4054, 4289, 4555, 4668, 4773, 4958, 5035, 5187, 5429, 5611, 5746, 5867, 5980, 6094, 6196, 6288, 6378, 6477, 6576, 6688, 6801, 6929, 7055, 7196, 7373, 7457, 7589, 7729, 7809, 7928, 7990, 8087, 8164, 8236, 8356, 8580, 8669, 8773, 8904, 8982, 9072, 9202, 9338, 9429, 9568, 9670, 9796, 9872, 9923, 9971, 9993, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{517, 551, 635, 681, 744, 811, 889, 994, 1116, 1268, 1458, 1697, 2009, 2396, 2837, 3098, 3421, 3709, 3977, 4092, 4205, 4403, 4566, 4740, 4938, 5127, 5332, 5433, 5527, 5679, 5745, 5875, 6075, 6240, 6366, 6465, 6574, 6664, 6755, 6834, 6911, 6997, 7086, 7188, 7290, 7410, 7536, 7663, 7833, 7906, 8044, 8172, 8249, 8358, 8423, 8517, 8585, 8657, 8775, 8987, 9075, 9184, 9315, 9388, 9481, 9606, 9735, 9812, 9903, 9938, 9972, 9989, 9995, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{709, 761, 894, 974, 1077, 1188, 1331, 1503, 1722, 1999, 2352, 2751, 3182, 3610, 4007, 4214, 4454, 4666, 4873, 4958, 5043, 5210, 5339, 5493, 5653, 5817, 5995, 6082, 6160, 6290, 6358, 6470, 6646, 6790, 6899, 6985, 7074, 7163, 7244, 7315, 7390, 7465, 7549, 7638, 7734, 7843, 7963, 8092, 8248, 8322, 8449, 8576, 8644, 8762, 8819, 8916, 8983, 9057, 9170, 9385, 9465, 9575, 9693, 9761, 9831, 9904, 9948, 9965, 9990, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, + +//temp = 50.0 ^C +{75, 82, 99, 109, 120, 131, 144, 158, 174, 191, 209, 229, 250, 274, 300, 315, 336, 356, 377, 389, 399, 421, 440, 463, 492, 524, 573, 614, 692, 1166, 1283, 1426, 1708, 2004, 2282, 2471, 2683, 2810, 2958, 3162, 3408, 3661, 3900, 4159, 4404, 4664, 4921, 5163, 5435, 5526, 5657, 5794, 5887, 6042, 6131, 6267, 6370, 6474, 6641, 6910, 7025, 7162, 7316, 7411, 7513, 7664, 7815, 7915, 8066, 8174, 8319, 8423, 8517, 8671, 8824, 8927, 9030, 9176, 9330, 9433, 9537, 9695, 9813, 9867, 9979, 10000}, +{120, 129, 146, 158, 170, 183, 198, 213, 231, 248, 269, 291, 315, 342, 369, 387, 410, 432, 458, 470, 481, 506, 531, 566, 633, 764, 1161, 1329, 1437, 1626, 1713, 1924, 2323, 2623, 2844, 3041, 3295, 3528, 3754, 3949, 4126, 4314, 4505, 4703, 4900, 5115, 5328, 5517, 5721, 5807, 5969, 6140, 6234, 6388, 6479, 6603, 6696, 6785, 6932, 7196, 7299, 7429, 7575, 7662, 7764, 7906, 8053, 8150, 8296, 8399, 8542, 8646, 8736, 8890, 9042, 9144, 9247, 9396, 9553, 9656, 9762, 9901, 9980, 9996, 10000, 10000}, +{146, 156, 178, 190, 203, 218, 235, 252, 271, 292, 315, 339, 367, 398, 430, 451, 479, 507, 545, 568, 591, 654, 735, 865, 1127, 1403, 1660, 1786, 1919, 2176, 2293, 2543, 2935, 3286, 3556, 3770, 3987, 4179, 4360, 4509, 4656, 4814, 4968, 5139, 5305, 5481, 5656, 5817, 6032, 6131, 6294, 6466, 6560, 6703, 6780, 6894, 6983, 7067, 7206, 7456, 7556, 7678, 7816, 7905, 8000, 8138, 8282, 8373, 8518, 8619, 8761, 8861, 8956, 9108, 9259, 9364, 9470, 9617, 9774, 9866, 9945, 9997, 10000, 10000, 10000, 10000}, +{166, 177, 199, 213, 229, 243, 262, 282, 301, 324, 350, 377, 408, 440, 479, 506, 547, 598, 666, 711, 760, 874, 1023, 1243, 1498, 1731, 2018, 2167, 2320, 2600, 2724, 2991, 3415, 3752, 3983, 4168, 4358, 4528, 4687, 4822, 4951, 5092, 5232, 5390, 5543, 5700, 5865, 6028, 6243, 6342, 6507, 6666, 6754, 6892, 6964, 7078, 7166, 7247, 7382, 7620, 7719, 7840, 7977, 8062, 8154, 8287, 8428, 8523, 8664, 8766, 8907, 9007, 9099, 9251, 9407, 9510, 9618, 9762, 9902, 9961, 9992, 10000, 10000, 10000, 10000, 10000}, +{186, 196, 222, 236, 253, 268, 289, 308, 333, 358, 385, 416, 450, 494, 550, 595, 663, 749, 871, 940, 1025, 1203, 1385, 1594, 1842, 2092, 2426, 2594, 2764, 3074, 3204, 3470, 3850, 4144, 4347, 4510, 4680, 4832, 4975, 5099, 5219, 5353, 5474, 5620, 5758, 5913, 6075, 6239, 6450, 6543, 6699, 6856, 6939, 7075, 7150, 7254, 7336, 7416, 7550, 7782, 7878, 7996, 8128, 8214, 8306, 8437, 8578, 8671, 8806, 8909, 9050, 9151, 9246, 9399, 9551, 9657, 9759, 9888, 9976, 9995, 10000, 10000, 10000, 10000, 10000, 10000}, +{207, 220, 250, 265, 283, 303, 327, 352, 380, 408, 445, 487, 547, 626, 738, 824, 955, 1109, 1301, 1402, 1511, 1722, 1921, 2140, 2431, 2742, 3132, 3320, 3485, 3755, 3864, 4076, 4392, 4635, 4811, 4958, 5103, 5235, 5361, 5471, 5578, 5696, 5816, 5946, 6080, 6225, 6377, 6536, 6740, 6826, 6975, 7127, 7205, 7331, 7401, 7506, 7586, 7664, 7785, 8013, 8110, 8223, 8355, 8437, 8528, 8654, 8796, 8887, 9026, 9127, 9270, 9370, 9460, 9618, 9765, 9860, 9930, 9984, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{240, 252, 289, 308, 329, 354, 382, 410, 450, 497, 554, 627, 721, 855, 1044, 1179, 1362, 1568, 1817, 1943, 2065, 2312, 2540, 2814, 3142, 3455, 3797, 3945, 4080, 4305, 4399, 4578, 4842, 5058, 5216, 5352, 5484, 5604, 5726, 5824, 5918, 6028, 6136, 6254, 6377, 6523, 6664, 6815, 7008, 7090, 7238, 7380, 7455, 7583, 7647, 7745, 7822, 7899, 8022, 8242, 8332, 8447, 8579, 8657, 8744, 8877, 9013, 9107, 9242, 9340, 9485, 9586, 9682, 9825, 9930, 9971, 9989, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{263, 283, 317, 342, 365, 396, 431, 469, 517, 582, 662, 756, 892, 1077, 1308, 1457, 1700, 1948, 2227, 2360, 2496, 2770, 3017, 3298, 3597, 3873, 4170, 4298, 4414, 4621, 4707, 4872, 5127, 5328, 5473, 5597, 5720, 5836, 5940, 6034, 6133, 6231, 6335, 6459, 6580, 6708, 6852, 6993, 7179, 7262, 7400, 7536, 7620, 7738, 7806, 7900, 7977, 8053, 8173, 8397, 8484, 8593, 8728, 8802, 8893, 9021, 9157, 9250, 9393, 9493, 9631, 9728, 9812, 9922, 9981, 9996, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{346, 372, 428, 466, 507, 562, 622, 698, 794, 906, 1058, 1255, 1488, 1791, 2194, 2445, 2768, 3084, 3386, 3540, 3663, 3901, 4092, 4299, 4520, 4724, 4951, 5055, 5153, 5319, 5388, 5535, 5753, 5928, 6051, 6169, 6274, 6372, 6471, 6549, 6629, 6729, 6816, 6927, 7028, 7155, 7279, 7415, 7595, 7669, 7813, 7945, 8017, 8129, 8194, 8288, 8367, 8435, 8552, 8764, 8853, 8956, 9082, 9167, 9259, 9385, 9518, 9611, 9750, 9832, 9917, 9961, 9978, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{463, 505, 599, 657, 730, 817, 922, 1056, 1223, 1423, 1671, 1977, 2392, 2862, 3325, 3566, 3852, 4101, 4343, 4450, 4543, 4728, 4877, 5045, 5227, 5399, 5598, 5684, 5768, 5909, 5970, 6098, 6290, 6441, 6554, 6651, 6750, 6842, 6922, 6998, 7076, 7163, 7246, 7352, 7446, 7566, 7689, 7809, 7979, 8052, 8185, 8313, 8379, 8497, 8553, 8650, 8720, 8786, 8898, 9115, 9203, 9304, 9432, 9516, 9596, 9726, 9832, 9899, 9951, 9974, 9991, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, + +//temp = 60.0 ^C +{159, 167, 185, 196, 207, 220, 233, 248, 264, 281, 300, 320, 344, 368, 396, 412, 432, 454, 476, 488, 499, 521, 543, 566, 596, 631, 696, 761, 925, 1385, 1446, 1616, 1902, 2238, 2465, 2651, 2811, 2916, 3044, 3224, 3448, 3690, 3917, 4175, 4410, 4664, 4914, 5160, 5442, 5538, 5676, 5799, 5883, 6032, 6124, 6256, 6357, 6460, 6617, 6899, 7010, 7150, 7303, 7397, 7502, 7648, 7804, 7903, 8054, 8160, 8308, 8411, 8505, 8661, 8816, 8919, 9021, 9168, 9327, 9433, 9540, 9703, 9826, 9888, 9996, 10000}, +{264, 273, 293, 304, 317, 331, 345, 361, 379, 398, 419, 442, 468, 496, 525, 542, 567, 590, 617, 629, 642, 668, 694, 734, 821, 1011, 1452, 1570, 1678, 1872, 1968, 2201, 2570, 2831, 3006, 3165, 3387, 3623, 3845, 4030, 4208, 4396, 4579, 4780, 4972, 5185, 5398, 5589, 5790, 5871, 6022, 6190, 6287, 6442, 6528, 6650, 6748, 6840, 6989, 7255, 7359, 7488, 7633, 7725, 7824, 7966, 8118, 8212, 8359, 8465, 8610, 8713, 8804, 8959, 9112, 9214, 9321, 9469, 9633, 9736, 9842, 9970, 10000, 10000, 10000, 10000}, +{328, 338, 361, 375, 387, 403, 418, 439, 458, 480, 504, 530, 559, 589, 624, 644, 673, 704, 744, 768, 796, 873, 969, 1137, 1433, 1688, 1942, 2076, 2214, 2467, 2571, 2780, 3088, 3369, 3621, 3830, 4049, 4240, 4414, 4558, 4707, 4859, 5014, 5184, 5347, 5525, 5699, 5852, 6055, 6145, 6315, 6481, 6578, 6723, 6802, 6921, 7010, 7096, 7236, 7485, 7587, 7710, 7852, 7937, 8035, 8173, 8316, 8411, 8556, 8657, 8801, 8901, 8994, 9149, 9307, 9412, 9517, 9672, 9833, 9924, 9988, 10000, 10000, 10000, 10000, 10000}, +{370, 381, 405, 418, 436, 450, 471, 491, 509, 534, 560, 587, 619, 654, 696, 721, 761, 820, 899, 950, 1007, 1154, 1321, 1548, 1797, 2025, 2329, 2474, 2617, 2850, 2954, 3169, 3540, 3861, 4097, 4286, 4475, 4642, 4798, 4933, 5066, 5205, 5346, 5501, 5649, 5805, 5960, 6119, 6327, 6416, 6586, 6747, 6841, 6978, 7055, 7172, 7256, 7339, 7476, 7717, 7816, 7935, 8075, 8159, 8252, 8391, 8530, 8628, 8769, 8869, 9013, 9115, 9207, 9359, 9517, 9623, 9724, 9869, 9979, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{383, 396, 421, 438, 454, 473, 493, 515, 539, 564, 593, 624, 657, 702, 762, 807, 885, 985, 1124, 1208, 1293, 1488, 1691, 1892, 2129, 2390, 2694, 2835, 2978, 3229, 3339, 3579, 3950, 4238, 4446, 4615, 4787, 4933, 5079, 5199, 5319, 5448, 5577, 5716, 5853, 6002, 6155, 6314, 6518, 6611, 6774, 6935, 7024, 7158, 7232, 7341, 7421, 7501, 7631, 7865, 7963, 8078, 8213, 8296, 8391, 8527, 8666, 8759, 8902, 9004, 9148, 9250, 9339, 9496, 9657, 9766, 9872, 9980, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{401, 413, 442, 461, 478, 499, 525, 550, 577, 610, 646, 685, 745, 829, 949, 1044, 1189, 1355, 1558, 1667, 1782, 2004, 2191, 2399, 2657, 2927, 3260, 3417, 3566, 3826, 3936, 4155, 4472, 4719, 4896, 5040, 5188, 5323, 5446, 5559, 5668, 5779, 5899, 6030, 6158, 6298, 6444, 6598, 6797, 6886, 7041, 7186, 7265, 7399, 7469, 7572, 7658, 7735, 7863, 8100, 8197, 8315, 8447, 8529, 8615, 8745, 8889, 8980, 9120, 9221, 9366, 9474, 9567, 9722, 9867, 9941, 9986, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{401, 413, 442, 461, 478, 499, 525, 550, 577, 610, 646, 685, 745, 829, 949, 1044, 1189, 1355, 1558, 1667, 1782, 2004, 2191, 2399, 2657, 2927, 3260, 3417, 3566, 3826, 3936, 4155, 4472, 4719, 4896, 5040, 5188, 5323, 5446, 5559, 5668, 5779, 5899, 6030, 6158, 6298, 6444, 6598, 6797, 6886, 7041, 7186, 7265, 7399, 7469, 7572, 7658, 7735, 7863, 8100, 8197, 8315, 8447, 8529, 8615, 8745, 8889, 8980, 9120, 9221, 9366, 9474, 9567, 9722, 9867, 9941, 9986, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{432, 445, 485, 511, 534, 560, 595, 635, 678, 744, 826, 932, 1067, 1256, 1486, 1652, 1893, 2142, 2399, 2526, 2642, 2877, 3085, 3335, 3610, 3877, 4177, 4314, 4432, 4638, 4721, 4893, 5138, 5334, 5474, 5597, 5717, 5837, 5946, 6039, 6133, 6239, 6351, 6472, 6590, 6722, 6867, 7008, 7195, 7273, 7412, 7553, 7625, 7748, 7809, 7910, 7987, 8058, 8183, 8407, 8499, 8614, 8746, 8826, 8915, 9044, 9186, 9277, 9415, 9519, 9666, 9762, 9842, 9954, 9992, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{420, 437, 487, 517, 550, 597, 656, 724, 806, 918, 1057, 1230, 1451, 1721, 2072, 2310, 2608, 2897, 3189, 3330, 3464, 3700, 3908, 4122, 4358, 4572, 4807, 4920, 5019, 5195, 5268, 5422, 5654, 5836, 5971, 6087, 6199, 6306, 6410, 6493, 6579, 6670, 6768, 6872, 6983, 7099, 7228, 7365, 7543, 7619, 7757, 7894, 7964, 8084, 8147, 8246, 8318, 8391, 8512, 8724, 8811, 8920, 9049, 9124, 9217, 9348, 9479, 9565, 9698, 9790, 9895, 9949, 9975, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +{477, 511, 585, 641, 703, 780, 875, 1001, 1144, 1322, 1542, 1802, 2123, 2533, 2971, 3223, 3532, 3813, 4075, 4194, 4314, 4506, 4669, 4843, 5033, 5213, 5419, 5515, 5600, 5750, 5822, 5952, 6153, 6311, 6436, 6537, 6638, 6735, 6825, 6907, 6985, 7077, 7163, 7268, 7367, 7487, 7609, 7742, 7911, 7976, 8111, 8238, 8311, 8413, 8477, 8567, 8643, 8709, 8830, 9040, 9126, 9229, 9351, 9427, 9516, 9632, 9761, 9834, 9919, 9962, 9987, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000}, +}; diff --git a/drivers/power/sd77426_fg_mmi/table.h b/drivers/power/sd77426_fg_mmi/table.h new file mode 100644 index 000000000000..50fcf8f332e3 --- /dev/null +++ b/drivers/power/sd77426_fg_mmi/table.h @@ -0,0 +1,69 @@ +/***************************************************************************** +* Copyright(c) O2Micro, 2014. All rights reserved. +* +* O2Micro battery gauge driver +* File: table.h +* +* $Source: /data/code/CVS +* $Revision: 4.00.01 $ +* +* This program is free software and can be edistributed and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This Source Code Reference Design for O2MICRO Battery Gauge access (\u201cReference Design\u201d) +* is sole for the use of PRODUCT INTEGRATION REFERENCE ONLY, and contains confidential +* and privileged information of O2Micro International Limited. O2Micro shall have no +* liability to any PARTY FOR THE RELIABILITY, SERVICEABILITY FOR THE RESULT OF PRODUCT +* INTEGRATION, or results from: (i) any modification or attempted modification of the +* Reference Design by any party, or (ii) the combination, operation or use of the +* Reference Design with non-O2Micro Reference Design. +* +* Battery Manufacture: ATL +* Battery Model: M1892 +* Absolute Max Capacity(mAhr): 3661 +* Limited Charge Voltage(mV): 4400 +* Cutoff Discharge Voltage(mV): 3200 +* Equipment: "JY1-5","JY1-5","JY1-5","JY1-5","JY1-5","JY1-5","JY1-5","JY1-5","JY1-5","JY1-5","JY1-5","JY1-5","JY2-5","JY2-5","JY2-5","JY2-6","JY2-6","JY2-6","JY2-6","JY2-6","JY2-6","JY2-6","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-6","JY2-6","JY2-6","JY2-6","JY2-6","JY2-6","JY2-6","JY2-6","JY2-6","JY2-5","JY1-6","JY1-6","JY1-6","JY1-6","JY1-6","JY1-6","JY1-8","JY1-8","JY1-8","JY1-8","JY1-6","JY1-6","JY1-6","JY1-6","JY1-6","JY1-6","JY1-6","JY1-6","JY1-6","JY1-6","JY1-6","JY1-8","JY1-7","JY1-7","JY1-7","JY1-7","JY1-7","JY1-7","JY1-3","JY1-3","JY1-3","JY1-5","JY1-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY2-5","JY1-8","JY1-8","JY1-2","JY1-2","JY1-2","JY2-6","JY2-6","JY2-6" +* Tester: "SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ","SZ" +* Battery ID: "M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_1","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_2","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_3","M1892_4","M1892_4","M1892_4","M1892_2","M1892_2","M1892_2" +* Version = V00.8D.00 +* Date = 2018.3.26 +* Comment = +* +*****************************************************************************/ + +#ifndef _TABLE_H_ +#define _TABLE_H_ + + +#define OCV_DATA_NUM 65 +#define CHARGE_DATA_NUM 65 + + +#define XAxis 86 +#define YAxis 10 +#define ZAxis 9 + +#define BATTERY_ID_NUM 93 + + +/**************************************************************************** +* Struct section +* add struct #define here if any +***************************************************************************/ +typedef struct tag_one_latitude_data { + int32_t x;// + int32_t y;// +} one_latitude_data_t; + + +/**************************************************************************** +* extern variable declaration section +***************************************************************************/ +extern const char *battery_id[] ; + +extern const char *table_version; + +#endif + diff --git a/drivers/power/sgm4154x_charger_lite/Android.mk b/drivers/power/sgm4154x_charger_lite/Android.mk new file mode 100644 index 000000000000..3b56d7c9808a --- /dev/null +++ b/drivers/power/sgm4154x_charger_lite/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := sgm4154x_charger_lite.ko + +KERNEL_CFLAGS += CONFIG_SGM4154X_CHARGER_NAME=$(SGM4154X_CHARGER_NAME) +KBUILD_OPTIONS += CONFIG_SGM4154X_CHARGER_NAME=$(SGM4154X_CHARGER_NAME) + +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_charger.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/sgm4154x_charger_lite/Kbuild b/drivers/power/sgm4154x_charger_lite/Kbuild new file mode 100644 index 000000000000..3b551c3eacd9 --- /dev/null +++ b/drivers/power/sgm4154x_charger_lite/Kbuild @@ -0,0 +1,25 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/sgm4154x_charger_lite \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/mmi_charger + +ifeq ($(CONFIG_SGM4154X_CHARGER_NAME),) + EXTRA_CFLAGS += -D__SGM41516D_CHIP_ID__ +endif +ifeq ($(CONFIG_SGM4154X_CHARGER_NAME),sgm41541) + EXTRA_CFLAGS += -D__SGM41541_CHIP_ID__ +endif +ifeq ($(CONFIG_SGM4154X_CHARGER_NAME),sgm41542) + EXTRA_CFLAGS += -D__SGM41542_CHIP_ID__ +endif +ifeq ($(CONFIG_SGM4154X_CHARGER_NAME),sgm41516) + EXTRA_CFLAGS += -D__SGM41516_CHIP_ID__ +endif +ifeq ($(CONFIG_SGM4154X_CHARGER_NAME),sgm41516D) + EXTRA_CFLAGS += -D__SGM41516D_CHIP_ID__ +endif + +obj-m += sgm4154x_charger_lite.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/sgm4154x_charger_lite/Makefile b/drivers/power/sgm4154x_charger_lite/Makefile new file mode 100644 index 000000000000..a040b9a0e518 --- /dev/null +++ b/drivers/power/sgm4154x_charger_lite/Makefile @@ -0,0 +1,16 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/sgm4154x_charger_lite/README b/drivers/power/sgm4154x_charger_lite/README new file mode 100644 index 000000000000..72d15d7d3415 --- /dev/null +++ b/drivers/power/sgm4154x_charger_lite/README @@ -0,0 +1,215 @@ +Motorola Mobility. sgm4154x_charger_lite Specific Bindings + +This sgm4154x charger driver is a lite version to support battery +charging without BC1.2 charger type detection over DP/DM or OTG +function. +It utilizes an external USB power supply to query the input status +and charger type. + +This charger driver can work independently or cooperate with Motorola +mmi_charger under the new Motorola charger driver architecture, so as +to support multiple batteries system design. + +Motorola charger driver architecture: +----------------------------------------------------------------------------------------------------------- +Userspace Common Interfaces for Charging Features and Factory Test +----------------------------------------------------------------------------------------------------------- +Kernel mmi_charger (Motorola demo, step, flash-fast charging, + / \ factory and debugging interfaces) + / \ + qti_glink_charger sgm4154x_charger_lite +(base battery charging) (flip battery charging) + | | + | | +------------glink-------------i2c-------------------------------------------------------------------------- + | | + | | + ADSP sgm4154x-chip + + +devicetree properties description: +- vdd-i2c-supply + Usage: optional + Value Type: pointer to power regulator + Definition: power regulator to supply pullup power for i2c data lines + +- sgm,irq-gpio + Usage: mandatory + Value Type: gpio + Definition: input gpio used as chip irq line + +- sgm,chg-en-gpio + Usage: optional + Value Type: gpio + Definition: output gpio used as chip nCE pin controlling + +- init-charger-disabled + Usage: optional + Value Type: bool + Definition: Disable charger IC in initial state + +- vrechg-millivolt + Usage: optional + Value Type: u32 + Definition: voltage diff with full voltage to trigger battery recharging + +- iprechg-microamp + Usage: optional + Value Type: u32 + Definition: charging current in uA in precharging stage + +- iterm-microamp + Usage: optional + Value Type: u32 + Definition: charging termination current in uA + +- ichg-max-microamp + Usage: optional + Value Type: u32 + Definition: maximum charging current in uA + +- vchg-max-microvolt + Usage: optional + Value Type: u32 + Definition: maximum charging voltage in uV + +- ichg-microamp + Usage: optional + Value Type: u32 + Definition: initial charging current in uA + +- vchg-microvolt + Usage: optional + Value Type: u32 + Definition: initial charging voltage in uV + +- input-current-limit-microamp + Usage: optional + Value Type: u32 + Definition: USB input current limitation in uA + +- mmi,ext-usb-psy-name + Usage: optional + Value Type: string + Definition: Name of the external USB power supply which provides +USB input status and information + +- mmi,fg-psy-name + Usage: optional + Value Type: string + Definition: Name of the battery FG power supply which provides +battery status and information + +- mmi,mmi-temp-zones + Usage: optional + Value Type: Array of tuples of + Definition: Unlimited array of arrays. Each internal array needs to have a set +of 4 bytes, each one representing this sequence: Temperature (in degC), Step +"Normalized" Voltage (in millivolts), Full Rate "FCC" Current (in milliamps), +and Taper Rate "FCC Normalized" Current (in milliamps). These values are +related to charging limitations based on temperature specifications. Note that +setting a voltage to '0' means that it doesn't matter the battery voltage on +that defined temperature range to perform necessary tapering. To understand how +Those values are used, please check the "IEEE Charging Compliance" section below. +In order to support charging current limit based on battery voltage in the same +temperature zone, one zone allows to have multiple lines with ascending sequence +by float voltage. First find the temperature zone, and then find the right one +among the same zone according to current battery voltage. + +- mmi,df-serialnum + Usage: optional + Value Type: + Definition: The default battery serial number for charging profile selection +and battery identification. + +- mmi,chrg-iterm-ma + Usage: optional + Value Type: + Definition: It will define taper current on battery (in milliamps). If charging +current becomes below this value, it indicates FULL charging. If not defined, +defaults to 300. + +- mmi,fg-iterm-ma + Usage: optional + Value Type: + Definition: It will define charging current on battery (in milliamps) as the +threshold for the fuel gauge to decide reporting FULL capacity. If not defined, +defaults to 350. + +- mmi,vfloat-comp-uv + Usage: optional + Value Type: + Definition: This is a tolerance, in uV, for battery floating voltage when +charging is being limited. Such situation can happen when either battery is +close to full or when there's thermal mitigation on charging. If not set, +defaults to 0. + +- mmi,max-fv-mv + Usage: optional + Value Type: + Definition: It will define the maximum battery charging voltage (in milliamps). +This value is used as the max float voltage for battery charging. If not +defined, defaults to 4400. + +- mmi,max-fcc-ma + Usage: optional + Value Type: + Definition: It will define maximum charging current on battery (in milliamps) +If not defined, defaults to 4000. + +- mmi,demo-fv-mv + Usage: optional + Value Type: + Definition: It will define the maximum battery charging voltage (in milliamps) +in demo mode. This value is used as the max float voltage for battery charging in +demo mode. If not defined, defaults to 4000. + +- mmi,mmi-ffc-zones + Usage: optional + Value Type: Array of tuples of + Definition: Unlimited array of arrays. Each internal array needs to have a set +of 4 data members, each one representing this sequence: Temperature (in degC), +maximum Float Voltage (in millivolts), charging terminated current(in milliamps), +and Fuel Gauge FULL terminated current (in milliamps). These values are +related to charging limitations based on temperature specifications when the +battery supports Flash Fast Charging. + +Example: + sgm4154x_charger: sgm4154x@6B { + compatible = "sgm,sgm41516D"; + reg = <0x6B>; + + vdd-i2c-supply = <&S10B>; + pinctrl-names = "default"; + pinctrl-0 = <&sgm4154x_irq_default &sgm4154x_ce_default>; + + interrupt-parent = <&tlmm>; + interrupts = <51 0>; //irq num base on hardware design + sgm,irq-gpio = <&tlmm 51 0>; //irq num base on hardware design + sgm,chg-en-gpio = <&pm8350c_gpios 2 GPIO_ACTIVE_HIGH>; //gpio num base on hardware design + + init-charger-disabled; + vrechg-millivolt = <200>; + iprechg-microamp = <180000>; + iterm-microamp = <200000>; + ichg-max-microamp = <1200000>; + vchg-max-microvolt = <4600000>; + ichg-microamp = <700000>; + vchg-microvolt = <4400000>; + input-current-limit-microamp = <1200000>; + + mmi,ext-usb-psy-name = "usb"; + mmi,fg-psy-name = "fg_flip_battery"; + mmi,chrg-iterm-ma = <50>; + mmi,fg-iterm-ma = <100>; + mmi,max-fv-mv = <4480>; + mmi,max-fcc-ma = <1214>; + mmi,demo-fv-mv = <4000>; + mmi,df-serialnum = "SB18D44720"; + mmi,mmi-temp-zones = <0 4200 136 0>, + <10 4250 340 204>, + <15 0 679 476>, + <45 4300 1214 883>, + <45 4350 883 679>, + <60 4200 679 0>; + }; diff --git a/drivers/power/sgm4154x_charger_lite/sgm4154x_charger_lite.c b/drivers/power/sgm4154x_charger_lite/sgm4154x_charger_lite.c new file mode 100644 index 000000000000..d05cebaa74f9 --- /dev/null +++ b/drivers/power/sgm4154x_charger_lite/sgm4154x_charger_lite.c @@ -0,0 +1,3635 @@ +// SPDX-License-Identifier: GPL-2.0 +// SGM4154x driver version 2021-08-05-002 +// Copyright (C) 2021 Texas Instruments Incorporated - http://www.sg-micro.com + +#define pr_fmt(fmt) "sgm4154x-charger: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sgm4154x_charger_lite.h" + +#define DEF_VBAT_OVP_MV (4600) +#define DEF_IBAT_OCP_MA (1500) +#define DEF_IBAT_OCCP_MA (1500) +#define DEF_PROTECT_DURATION_MS (60000) +#define DEF_USB_CURRENT_MA (500000) +#define DEF_WLS_8W_VOLTAGE_UV (7000000) + +#ifndef MIN_VAL + #define MIN_VAL( x, y ) ( ((x) < (y)) ? (x) : (y) ) +#endif + +static bool paired_vbat_panic_enabled = false; +module_param(paired_vbat_panic_enabled, bool, 0644); +MODULE_PARM_DESC(paired_vbat_panic_enabled, + "Enable panic on abnormal paired vbat delta"); + +static struct power_supply_desc sgm4154x_power_supply_desc; +#ifdef SGM_BASE +static struct reg_default sgm4154x_reg_defs[] = { + {SGM4154x_CHRG_CTRL_0, 0x0a}, + {SGM4154x_CHRG_CTRL_1, 0x1a}, + {SGM4154x_CHRG_CTRL_2, 0x88}, + {SGM4154x_CHRG_CTRL_3, 0x22}, + {SGM4154x_CHRG_CTRL_4, 0x58}, + {SGM4154x_CHRG_CTRL_5, 0x9f}, + {SGM4154x_CHRG_CTRL_6, 0xe6}, + {SGM4154x_CHRG_CTRL_7, 0x4c}, + {SGM4154x_CHRG_STAT, 0x00}, + {SGM4154x_CHRG_FAULT, 0x00}, + {SGM4154x_CHRG_CTRL_a, 0x00},// + {SGM4154x_CHRG_CTRL_b, 0x64}, + {SGM4154x_CHRG_CTRL_c, 0x75}, + {SGM4154x_CHRG_CTRL_d, 0x00}, + {SGM4154x_INPUT_DET, 0x00}, + {SGM4154x_CHRG_CTRL_f, 0x00}, +}; +#endif + +/* SGM4154x REG06 BOOST_LIM[5:4], uV */ +static const unsigned int BOOST_VOLT_LIMIT[] = { + 4850000, 5000000, 5150000, 5300000 +}; + /* SGM4154x REG02 BOOST_LIM[7:7], uA */ +#if defined(__SGM41542_CHIP_ID__)|| defined(__SGM41516D_CHIP_ID__) +static const unsigned int BOOST_CURRENT_LIMIT[] = { + 1200000, 2000000 +}; +#else +static const unsigned int BOOST_CURRENT_LIMIT[] = { + 500000, 1200000 +}; +#endif + +enum SGM4154x_VREG_FT { + VREG_FT_DISABLE, + VREG_FT_UP_8mV, + VREG_FT_DN_8mV, + VREG_FT_DN_16mV, +}; + +enum SGM4154x_VINDPM_OS { + VINDPM_OS_3900mV, + VINDPM_OS_5900mV, + VINDPM_OS_7500mV, + VINDPM_OS_10500mV, +}; + +enum { + PAIRED_LOAD_OFF, + PAIRED_LOAD_LOW, + PAIRED_LOAD_HIGH, +}; + +enum { + BATT_PROTECT_TYPE_NONE, + BATT_PROTECT_TYPE_OVP, + BATT_PROTECT_TYPE_OCP, + BATT_PROTECT_TYPE_OCCP, +}; + +static enum power_supply_usb_type sgm4154x_usb_type[] = { + POWER_SUPPLY_USB_TYPE_UNKNOWN, + POWER_SUPPLY_USB_TYPE_SDP, /* Standard Downstream Port */ + POWER_SUPPLY_USB_TYPE_DCP, /* Dedicated Charging Port */ + POWER_SUPPLY_USB_TYPE_CDP, /* Charging Downstream Port */ + POWER_SUPPLY_USB_TYPE_ACA, /* Accessory Charger Adapters */ + POWER_SUPPLY_USB_TYPE_C, /* Type C Port */ + POWER_SUPPLY_USB_TYPE_PD, /* Power Delivery Port */ + POWER_SUPPLY_USB_TYPE_PD_DRP, /* PD Dual Role Port */ + POWER_SUPPLY_USB_TYPE_PD_PPS, /* PD Programmable Power Supply */ + POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID, /* Apple Charging Method */ +}; + +static int sgm4154x_usb_notifier(struct notifier_block *nb, unsigned long val, + void *priv) +{ + struct sgm4154x_device *sgm = + container_of(nb, struct sgm4154x_device, usb_nb); + + sgm->usb_event = val; + + queue_work(system_power_efficient_wq, &sgm->usb_work); + + return NOTIFY_OK; +} + +static void sgm4154x_usb_work(struct work_struct *data) +{ + struct sgm4154x_device *sgm = + container_of(data, struct sgm4154x_device, usb_work); + + switch (sgm->usb_event) { + case USB_EVENT_ID: + break; + + case USB_EVENT_NONE: + power_supply_changed(sgm->charger); + break; + } + + return; + + dev_err(sgm->dev, "Error switching to charger mode.\n"); +} + +#ifdef SGM_BASE +static int sgm4154x_get_term_curr(struct sgm4154x_device *sgm) +{ + int ret; + int reg_val; + int offset = SGM4154x_TERMCHRG_I_MIN_uA; + + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_3, ®_val); + if (ret) + return ret; + + reg_val &= SGM4154x_TERMCHRG_CUR_MASK; + reg_val = reg_val * SGM4154x_TERMCHRG_CURRENT_STEP_uA + offset; + return reg_val; +} + +static int sgm4154x_get_prechrg_curr(struct sgm4154x_device *sgm) +{ + int ret; + int reg_val; + int offset = SGM4154x_PRECHRG_I_MIN_uA; + + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_3, ®_val); + if (ret) + return ret; + + reg_val = (reg_val&SGM4154x_PRECHRG_CUR_MASK)>>4; + reg_val = reg_val * SGM4154x_PRECHRG_CURRENT_STEP_uA + offset; + return reg_val; +} +#endif + +static int sgm4154x_get_ichg_curr(struct sgm4154x_device *sgm) +{ + int ret; + int ichg; + + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_2, &ichg); + if (ret) + return ret; + + ichg &= SGM4154x_ICHRG_CUR_MASK; + + return ichg * SGM4154x_ICHRG_CURRENT_STEP_uA; +} + +static int sgm4154x_set_term_curr(struct sgm4154x_device *sgm, int term_current) +{ + int reg_val; + int offset = SGM4154x_TERMCHRG_I_MIN_uA; + + if (term_current < SGM4154x_TERMCHRG_I_MIN_uA) + term_current = SGM4154x_TERMCHRG_I_MIN_uA; + else if (term_current > SGM4154x_TERMCHRG_I_MAX_uA) + term_current = SGM4154x_TERMCHRG_I_MAX_uA; + + reg_val = (term_current - offset) / SGM4154x_TERMCHRG_CURRENT_STEP_uA; + + return regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_3, + SGM4154x_TERMCHRG_CUR_MASK, reg_val); +} + +static int sgm4154x_set_prechrg_curr(struct sgm4154x_device *sgm, int pre_current) +{ + int reg_val; + int offset = SGM4154x_PRECHRG_I_MIN_uA; + + if (pre_current < SGM4154x_PRECHRG_I_MIN_uA) + pre_current = SGM4154x_PRECHRG_I_MIN_uA; + else if (pre_current > SGM4154x_PRECHRG_I_MAX_uA) + pre_current = SGM4154x_PRECHRG_I_MAX_uA; + + reg_val = (pre_current - offset) / SGM4154x_PRECHRG_CURRENT_STEP_uA; + reg_val = reg_val << 4; + return regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_3, + SGM4154x_PRECHRG_CUR_MASK, reg_val); +} + + + +static int sgm4154x_set_ichrg_curr(struct sgm4154x_device *sgm, int chrg_curr) +{ + int ret; + int reg_val; + + if (chrg_curr < SGM4154x_ICHRG_I_MIN_uA) + chrg_curr = SGM4154x_ICHRG_I_MIN_uA; + else if ( chrg_curr > sgm->init_data.max_ichg) + chrg_curr = sgm->init_data.max_ichg; + + reg_val = sgm->thermal_fcc_ua; + chrg_curr = min(chrg_curr, reg_val); + if (sgm->mmi_charger) { + reg_val = sgm->mmi_charger->paired_ichg; + chrg_curr = min(chrg_curr, reg_val); + } + + if (sgm->user_ichg >= 0 && sgm->user_ichg != chrg_curr) { + chrg_curr = sgm->user_ichg; + pr_info("User request override ichg = %duA\n", chrg_curr); + } + + sgm->ichg = chrg_curr; + pr_info("set ichg = %duA\n", chrg_curr); + reg_val = chrg_curr / SGM4154x_ICHRG_CURRENT_STEP_uA; + + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_2, + SGM4154x_ICHRG_CUR_MASK, reg_val); + + return ret; +} + + +static int sgm4154x_set_thermal_mitigation(struct sgm4154x_device *sgm, int val) +{ + int ret; + u32 prev_fcc_ua; + + if (!sgm->num_thermal_levels) + return 0; + + if (sgm->num_thermal_levels < 0) { + pr_err("Incorrect num_thermal_levels\n"); + return -EINVAL; + } + + if (val < 0 || val > sgm->num_thermal_levels) { + pr_err("Invalid thermal level: %d\n", val); + return -EINVAL; + } + + prev_fcc_ua = sgm->thermal_fcc_ua; + sgm->thermal_fcc_ua = sgm->thermal_levels[val]; + ret = sgm4154x_set_ichrg_curr(sgm, sgm->thermal_levels[val]); + if (ret) { + pr_err("Failed to set thermal mitigation val=%d, ret=%d\n", + sgm->thermal_levels[val], ret); + sgm->thermal_fcc_ua = prev_fcc_ua; + } else { + sgm->curr_thermal_level = val; + } + pr_info("thermal level: %d, thermal fcc: %d\n", sgm->curr_thermal_level, sgm->thermal_fcc_ua); + + return ret; +} + +static int sgm4154x_vreg_fine_tuning(struct sgm4154x_device *sgm, enum SGM4154x_VREG_FT ft); +static int sgm4154x_set_chrg_volt(struct sgm4154x_device *sgm, int chrg_volt) +{ + int ret; + int reg_val; + enum SGM4154x_VREG_FT ft = VREG_FT_DISABLE; + + if (chrg_volt < SGM4154x_VREG_V_MIN_uV) + chrg_volt = SGM4154x_VREG_V_MIN_uV; + else if (chrg_volt > sgm->init_data.max_vreg) + chrg_volt = sgm->init_data.max_vreg; + + sgm->vreg = chrg_volt; + + reg_val = (chrg_volt-SGM4154x_VREG_V_MIN_uV) / SGM4154x_VREG_V_STEP_uV; + + switch(chrg_volt) { + case 4480000: + case 4450000: + reg_val++; + ft = VREG_FT_DN_16mV; + break; + case 4200000: + reg_val++; + ft = VREG_FT_DN_8mV; + break; + default: + break; + } + + ret = sgm4154x_vreg_fine_tuning(sgm, ft); + if (ret) { + pr_err("can't set vreg fine tunning ret=%d\n", ret); + return ret; + } + + reg_val = reg_val<<3; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_4, + SGM4154x_VREG_V_MASK, reg_val); + + return ret; +} + +#ifdef SGM_BASE +static int sgm4154x_get_chrg_volt(struct sgm4154x_device *sgm) +{ + int ret; + int vreg_val, chrg_volt; + + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_4, &vreg_val); + if (ret) + return ret; + + vreg_val = (vreg_val & SGM4154x_VREG_V_MASK)>>3; + + if (15 == vreg_val) + chrg_volt = 4352000; //default + else if (vreg_val < 25) + chrg_volt = vreg_val*SGM4154x_VREG_V_STEP_uV + SGM4154x_VREG_V_MIN_uV; + + return chrg_volt; +} + +#if defined(__SGM41542_CHIP_ID__)|| defined(__SGM41516D_CHIP_ID__) +static int sgm4154x_enable_qc20_hvdcp_9v(struct sgm4154x_device *sgm) +{ + int ret; + int dp_val, dm_val; + + /*dp and dm connected,dp 0.6V dm 0.6V*/ + dp_val = 0x2<<3; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, + SGM4154x_DP_VSEL_MASK, dp_val); //dp 0.6V + if (ret) + return ret; + + dm_val = 0x2<<1; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0.6V + if (ret) + return ret; + mdelay(1000); + + dm_val = 0x2; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0V + mdelay(1); + /* dp 3.3v and dm 0.6v out 9V */ + dp_val = SGM4154x_DP_VSEL_MASK; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, + SGM4154x_DP_VSEL_MASK, dp_val); //dp 3.3v + if (ret) + return ret; + //mdelay(1250); + + dm_val = 0x2<<1; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0.6v + + return ret; +} + +static int sgm4154x_enable_qc20_hvdcp_12v(struct sgm4154x_device *sgm) +{ + int ret; + int dp_val, dm_val; + + /*dp and dm connected,dp 0.6V dm 0.6V*/ + dp_val = 0x2<<3; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, + SGM4154x_DP_VSEL_MASK, dp_val); //dp 0.6V + if (ret) + return ret; + + dm_val = 0x2<<1; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0.6V + if (ret) + return ret; + mdelay(1000); + + dm_val = 0x2; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0V + mdelay(1); + /* dp 0.6v and dm 0.6v out 12V */ + dp_val = 0x2<<3; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, + SGM4154x_DP_VSEL_MASK, dp_val); //dp 0.6v + if (ret) + return ret; + //mdelay(1250); + + dm_val = 0x2<<1; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0.6v + + return ret; +} + +/* step 1. entry QC3.0 mode + step 2. up or down 200mv + step 3. retry step 2 */ +static int sgm4154x_enable_qc30_hvdcp(struct sgm4154x_device *sgm) +{ + int ret; + int dp_val, dm_val; + + dp_val = 0x2<<3; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, + SGM4154x_DP_VSEL_MASK, dp_val); //dp 0.6v + if (ret) + return ret; + + dm_val = 0x2<<1; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0.6V + if (ret) + return ret; + mdelay(1000); + + dm_val = 0x2; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0V + mdelay(1); + + dm_val = SGM4154x_DM_VSEL_MASK; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 3.3v + + return ret; +} + +// Must enter 3.0 mode to call ,otherwise cannot step correctly. +static int sgm4154x_qc30_step_up_vbus(struct sgm4154x_device *sgm) +{ + int ret; + int dp_val; + + /* dm 3.3v to dm 0.6v step up 200mV when IC is QC3.0 mode*/ + dp_val = SGM4154x_DP_VSEL_MASK; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, + SGM4154x_DP_VSEL_MASK, dp_val); //dp 3.3v + if (ret) + return ret; + + dp_val = 0x2<<3; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, + SGM4154x_DP_VSEL_MASK, dp_val); //dp 0.6v + if (ret) + return ret; + + udelay(100); + return ret; +} +// Must enter 3.0 mode to call ,otherwise cannot step correctly. +static int sgm4154x_qc30_step_down_vbus(struct sgm4154x_device *sgm) +{ + int ret; + int dm_val; + + /* dp 0.6v and dm 0.6v step down 200mV when IC is QC3.0 mode*/ + dm_val = 0x2<<1; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0.6V + if (ret) + return ret; + + dm_val = SGM4154x_DM_VSEL_MASK; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 3.3v + udelay(100); + + return ret; +} +#endif +#endif + +// fine tuning termination voltage,to Improve accuracy +static int sgm4154x_vreg_fine_tuning(struct sgm4154x_device *sgm,enum SGM4154x_VREG_FT ft) +{ + int ret; + int reg_val; + + switch(ft) { + case VREG_FT_DISABLE: + reg_val = 0; + break; + + case VREG_FT_UP_8mV: + reg_val = SGM4154x_VREG_FT_UP_8mV; + break; + + case VREG_FT_DN_8mV: + reg_val = SGM4154x_VREG_FT_DN_8mV; + break; + + case VREG_FT_DN_16mV: + reg_val = SGM4154x_VREG_FT_DN_16mV; + break; + + default: + reg_val = 0; + break; + } + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_f, + SGM4154x_VREG_FT_MASK, reg_val); + pr_info("reg_val:%d\n", reg_val); + + return ret; +} + +static int sgm4154x_get_vindpm_offset_os(struct sgm4154x_device *sgm) +{ + int ret; + int reg_val; + + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_f, ®_val); + if (ret) + return ret; + + reg_val = reg_val & SGM4154x_VINDPM_OS_MASK; + + return reg_val; +} + +#ifdef SGM_BASE +static int sgm4154x_set_vindpm_offset_os(struct sgm4154x_device *sgm,enum SGM4154x_VINDPM_OS offset_os) +{ + int ret; + + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_f, + SGM4154x_VINDPM_OS_MASK, offset_os); + + if (ret){ + pr_err("fail\n"); + return ret; + } + + return ret; +} +#endif + +static int sgm4154x_set_input_volt_lim(struct sgm4154x_device *sgm, int vindpm) +{ + int ret; + int offset = 0; + int vlim; + int temp; + + if (vindpm < SGM4154x_VINDPM_V_MIN_uV || + vindpm > SGM4154x_VINDPM_V_MAX_uV) + return -EINVAL; + + sgm->vlim = vindpm; + + temp = sgm4154x_get_vindpm_offset_os(sgm); + if (0 == temp) + offset = 3900000; //uv + else if (1 == temp) + offset = 5900000; //uv + else if (2 == temp) + offset = 7500000; //uv + else if (3 == temp) + offset = 10500000; //uv + + vlim = (vindpm - offset) / SGM4154x_VINDPM_STEP_uV; + + vlim &= 0x0F; + + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_6, + SGM4154x_VINDPM_V_MASK, vlim); + + return ret; +} + +static int sgm4154x_get_input_volt_lim(struct sgm4154x_device *sgm) +{ + int ret; + int offset = 0; + int vlim; + int temp; + + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_6, &vlim); + if (ret) + return ret; + + temp = sgm4154x_get_vindpm_offset_os(sgm); + if (0 == temp) + offset = 3900000; //uv + else if (1 == temp) + offset = 5900000; + else if (2 == temp) + offset = 7500000; + else if (3 == temp) + offset = 10500000; + + vlim = offset + (vlim & 0x0F) * SGM4154x_VINDPM_STEP_uV; + return vlim; +} + +static int sgm4154x_set_input_curr_lim(struct sgm4154x_device *sgm, int iindpm) +{ + int ret; + int reg_val = 0; + + if (iindpm > sgm->init_data.ilim) + iindpm = sgm->init_data.ilim; + + if (sgm->user_ilim >= 0 && sgm->user_ilim != iindpm) { + iindpm = sgm->user_ilim; + pr_info("User request override ilim = %duA\n", iindpm); + } + + sgm->ilim = iindpm; + + if (iindpm < SGM4154x_IINDPM_I_MIN_uA) + reg_val = 0; + else if (iindpm >= SGM4154x_IINDPM_I_MAX_uA) + reg_val = 0x1F; + else if (iindpm >= SGM4154x_IINDPM_I_MIN_uA && iindpm <= 3100000)//default + reg_val = (iindpm-SGM4154x_IINDPM_I_MIN_uA) / SGM4154x_IINDPM_STEP_uA; + else if (iindpm > 3100000 && iindpm < SGM4154x_IINDPM_I_MAX_uA) + reg_val = 0x1E; + + return ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_0, + SGM4154x_IINDPM_I_MASK, reg_val); +} + +static int sgm4154x_get_input_curr_lim(struct sgm4154x_device *sgm) +{ + int ret; + int ilim; + + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_0, &ilim); + if (ret) + return ret; + if (SGM4154x_IINDPM_I_MASK == (ilim & SGM4154x_IINDPM_I_MASK)) + return SGM4154x_IINDPM_I_MAX_uA; + + ilim = (ilim & SGM4154x_IINDPM_I_MASK)*SGM4154x_IINDPM_STEP_uA + SGM4154x_IINDPM_I_MIN_uA; + + return ilim; +} + +#ifdef SGM_BASE +static int sgm4154x_set_watchdog_timer(struct sgm4154x_device *sgm, int time) +{ + int ret; + u8 reg_val; + + if (time == 0) + reg_val = SGM4154x_WDT_TIMER_DISABLE; + else if (time == 40) + reg_val = SGM4154x_WDT_TIMER_40S; + else if (time == 80) + reg_val = SGM4154x_WDT_TIMER_80S; + else + reg_val = SGM4154x_WDT_TIMER_160S; + + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_5, + SGM4154x_WDT_TIMER_MASK, reg_val); + + return ret; +} + +static int sgm4154x_set_wdt_rst(struct sgm4154x_device *sgm, bool is_rst) +{ + int val = 0; + + if (is_ rst) + val = SGM4154x_WDT_RST_MASK; + else + val = 0; + return regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_1, + SGM4154x_WDT_RST_MASK, val); +} +#endif + +static int is_wls_online(struct sgm4154x_device *sgm) +{ + int rc; + union power_supply_propval val; + + if (!sgm->use_ext_wls_psy || !sgm->wls_psy) + return 0; + + rc = power_supply_get_property(sgm->wls_psy, + POWER_SUPPLY_PROP_ONLINE, &val); + if (rc < 0) { + pr_err("Error wls online rc = %d\n", rc); + return 0; + } + + pr_debug("wireless online is %d", val.intval); + return val.intval; +} + +static int sgm4154x_get_state(struct sgm4154x_device *sgm, + struct sgm4154x_state *state) +{ + int chrg_stat; + int fault; + int chrg_param_0,chrg_param_1,chrg_param_2,chrg_param_3; + int ret; + union power_supply_propval val = {0}; + + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_STAT, &chrg_stat); + if (ret){ + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_STAT, &chrg_stat); + if (ret){ + pr_err("read SGM4154x_CHRG_STAT fail\n"); + return ret; + } + } + + if (is_wls_online(sgm)) { + state->online = 1; + state->chrg_type = SGM4154x_WLS_TYPE; + ret = power_supply_get_property(sgm->wls_psy, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); + if (!ret) + state->vbus_adc = val.intval; + ret = power_supply_get_property(sgm->wls_psy, + POWER_SUPPLY_PROP_CURRENT_NOW, &val); + if (!ret) + state->ibus_adc = val.intval; + ret = power_supply_get_property(sgm->wls_psy, + POWER_SUPPLY_PROP_CURRENT_MAX, &val); + if (!ret) + state->ibus_limit = val.intval; + state->chrg_stat = chrg_stat & SGM4154x_CHG_STAT_MASK; + } else if (sgm->use_ext_usb_psy && sgm->usb) { + ret = power_supply_get_property(sgm->usb, + POWER_SUPPLY_PROP_USB_TYPE, &val); + switch (val.intval) { + case POWER_SUPPLY_USB_TYPE_UNKNOWN: + state->chrg_type = 0; + break; + case POWER_SUPPLY_USB_TYPE_SDP: + state->chrg_type = SGM4154x_USB_SDP; + break; + case POWER_SUPPLY_USB_TYPE_CDP: + state->chrg_type = SGM4154x_USB_CDP; + break; + case POWER_SUPPLY_USB_TYPE_DCP: + state->chrg_type = SGM4154x_USB_DCP; + break; + default: + state->chrg_type = SGM4154x_UNKNOWN; + break; + } + ret = power_supply_get_property(sgm->usb, + POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); + if (!ret) + state->vbus_adc = val.intval; + ret = power_supply_get_property(sgm->usb, + POWER_SUPPLY_PROP_CURRENT_NOW, &val); + if (!ret) + state->ibus_adc = val.intval; + ret = power_supply_get_property(sgm->usb, + POWER_SUPPLY_PROP_ONLINE, &val); + if (!ret) + state->online = val.intval; + ret = power_supply_get_property(sgm->usb, + POWER_SUPPLY_PROP_CURRENT_MAX, &val); + if (!ret) + state->ibus_limit = val.intval; + state->chrg_stat = chrg_stat & SGM4154x_CHG_STAT_MASK; + } else if (sgm->use_ext_usb_psy) { + state->chrg_type = 0; + state->chrg_stat = 0; + state->online = 0; + state->ibus_limit = 0; + } else { + state->chrg_type = chrg_stat & SGM4154x_VBUS_STAT_MASK; + state->chrg_stat = chrg_stat & SGM4154x_CHG_STAT_MASK; + state->online = !!(chrg_stat & SGM4154x_PG_STAT); + }; + + state->therm_stat = !!(chrg_stat & SGM4154x_THERM_STAT); + state->vsys_stat = !!(chrg_stat & SGM4154x_VSYS_STAT); + + pr_debug("chrg_stat =%d chrg_type =%d online = %d\n", state->chrg_stat, state->chrg_type, state->online); + + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_FAULT, &fault); + if (ret){ + pr_err("read SGM4154x_CHRG_FAULT fail\n"); + return ret; + } + state->chrg_fault = fault; + state->ntc_fault = fault & SGM4154x_TEMP_MASK; + state->health = state->ntc_fault; + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_0, &chrg_param_0); + if (ret){ + pr_err("read SGM4154x_CHRG_CTRL_0 fail\n"); + return ret; + } + state->hiz_en = !!(chrg_param_0 & SGM4154x_HIZ_EN); + + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_5, &chrg_param_1); + if (ret){ + pr_err("read SGM4154x_CHRG_CTRL_5 fail\n"); + return ret; + } + state->term_en = !!(chrg_param_1 & SGM4154x_TERM_EN); + + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_a, &chrg_param_2); + if (ret){ + pr_err("read SGM4154x_CHRG_CTRL_a fail\n"); + return ret; + } + state->vbus_gd = !!(chrg_param_2 & SGM4154x_VBUS_GOOD); + + ret = regmap_read(sgm->regmap, SGM4154x_INPUT_DET, &chrg_param_3); + if (ret){ + pr_err("read SGM4154x_INPT_DET fail\n"); + return ret; + } + state->detect_done = !!(chrg_param_3 & SGM4154x_DPDM_ONGOING); + + state->input_ready = state->online && state->vbus_gd && state->detect_done; + return 0; +} + +static int sgm4154x_set_hiz_en(struct sgm4154x_device *sgm, bool hiz_en) +{ + int reg_val; + + if (sgm->user_chg_susp >= 0 && sgm->user_chg_susp != hiz_en) { + hiz_en = !!sgm->user_chg_susp; + pr_info("User request override hiz_en = %d\n", hiz_en); + } + + dev_notice(sgm->dev, "%s:%d", __func__, hiz_en); + reg_val = hiz_en ? SGM4154x_HIZ_EN : 0; + + return regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_0, + SGM4154x_HIZ_EN, reg_val); +} + +static bool sgm4154x_is_enabled_charging(struct sgm4154x_device *sgm) +{ + int ret = 0; + int status = 0; + bool enabled = false; + + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_1, &status); + if(ret) + return false; + + enabled = (status & SGM4154x_CHRG_EN) ? true:false; + + if (gpio_is_valid(sgm->chg_en_gpio) && + gpio_get_value(sgm->chg_en_gpio)) + enabled = false; + + return enabled; +} + +int sgm4154x_enable_charger(struct sgm4154x_device *sgm) +{ + int ret; + + if (sgm->user_chg_en == 0) { + pr_info("Skip enable charging for user request override\n"); + return 0; + } + + if (sgm->state.input_ready == 0) { + pr_info("Skip enable charging before input ready\n"); + return 0; + } + + pr_info("sgm4154x_enable_charger\n"); + if (gpio_is_valid(sgm->chg_en_gpio)) { + gpio_set_value(sgm->chg_en_gpio, 0); + } + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_1, + SGM4154x_CHRG_EN, + SGM4154x_CHRG_EN); + + return ret; +} + +int sgm4154x_disable_charger(struct sgm4154x_device *sgm) +{ + int ret; + + if (sgm->user_chg_en > 0) { + pr_info("Skip disable charging for user request override\n"); + return 0; + } + + pr_info("sgm4154x_disable_charger\n"); + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_1, + SGM4154x_CHRG_EN, + 0); + return ret; +} + +int sgm4154x_disable_watchdog(struct sgm4154x_device *sgm) +{ + int ret; + pr_info("sgm4154x_disable_watchdog\n"); + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_5, SGM4154x_WDT_TIMER_MASK, + SGM4154x_WDT_TIMER_DISABLE); + return ret; +} + +int sgm4154x_enable_hw_jeita(struct sgm4154x_device *sgm, bool en) +{ + int rc = 0; + + rc = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_d, SGM4154x_JEITA_ENABLE_MASK, + en ? SGM4154x_JEITA_ENABLE : SGM4154x_JEITA_DISABLE); + + pr_info("%s hw jeita %s\n", + en ? "enable" : "disable", + rc ? "failed" : "success"); + + return rc; +} + +int sgm4154x_disable_vindpm_int_pulse(struct sgm4154x_device *sgm) +{ + int ret; + pr_info("sgm4154x_disable_vindpm_pulse\n"); + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_a, SGM4154x_VINDPM_INT_MASK, + SGM4154x_VINDPM_INT_DISABLE); + return ret; +} + +int sgm4154x_disable_iindpm_int_pulse(struct sgm4154x_device *sgm) +{ + int ret; + pr_info("sgm4154x_disable_iindpm_pulse\n"); + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_a, SGM4154x_IINDPM_INT_MASK, + SGM4154x_IINDPM_INT_DISABLE); + return ret; +} + +#ifdef SGM_BASE +float sgm4154x_get_charger_output_power(struct sgm4154x_device *sgm) +{ + int ret; + int i = 0x1F; + int j = 0; + int vlim; + int ilim; + int temp; + int offset; + int output_volt; + int output_curr; + float o_i,o_v; + + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_6, &vlim); //read default setting to save + if (ret){ + pr_err("read SGM4154x_CHRG_CTRL_6 fail\n"); + return ret; + } + + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_0, &ilim); //read default setting to save + if (ret){ + pr_err("read SGM4154x_CHRG_CTRL_0 fail\n"); + return ret; + } + + regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_6, + SGM4154x_VINDPM_V_MASK, 0); + while(i--){ + + regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_0, + SGM4154x_IINDPM_I_MASK, i); + mdelay(50); + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_a, &temp); + if (ret){ + pr_err("read SGM4154x_CHRG_CTRL_a fail\n"); + return ret; + } + if (1 == (!!(temp&0x20))){ + output_curr = 100 + i*100; //mA + if (0x1F == i) + output_curr = 3800; //mA + } + } + + regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_0, + SGM4154x_IINDPM_I_MASK, SGM4154x_IINDPM_I_MASK); + for(j = 0;j <= 0xF;j ++) + { + regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_6, + SGM4154x_VINDPM_V_MASK, j); + + mdelay(10); + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_a, &temp); + if (ret){ + pr_err("read SGM4154x_CHRG_CTRL_a fail\n"); + return ret; + } + + if (1 == (!!(temp&0x40))){ + + temp = sgm4154x_get_vindpm_offset_os(sgm); + if (0 == temp) + offset = 3900; //mv + else if (1 == temp) + offset = 5900; //mv + else if (2 == temp) + offset = 7500; //mv + else if (3 == temp) + offset = 10500; //mv + output_volt = offset + j*100; //mv + } + } + o_i = (float)output_curr/1000; + o_v = (float)output_volt/1000; + return o_i * o_v; +} +#endif + +static int sgm4154x_set_vac_ovp(struct sgm4154x_device *sgm) +{ + int reg_val; + + dev_notice(sgm->dev, "%s", __func__); + reg_val = 0xFF & SGM4154x_VAC_OVP_MASK; + + return regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_6, + SGM4154x_VAC_OVP_MASK, reg_val); +} + +static int sgm4154x_set_recharge_volt(struct sgm4154x_device *sgm, int recharge_volt) +{ + int reg_val; + dev_notice(sgm->dev, "%s:%d", __func__, recharge_volt); + reg_val = (recharge_volt - SGM4154x_VRECHRG_OFFSET_mV) / SGM4154x_VRECHRG_STEP_mV; + + return regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_4, + SGM4154x_VRECHARGE, reg_val); +} + +#if defined(__SGM41542_CHIP_ID__)|| defined(__SGM41516D_CHIP_ID__) +static int get_charger_type(struct sgm4154x_device * sgm) +{ + enum power_supply_usb_type usb_type; + + switch(sgm->state.chrg_type) { + case SGM4154x_USB_SDP: + usb_type = POWER_SUPPLY_USB_TYPE_SDP; + break; + + case SGM4154x_USB_CDP: + usb_type = POWER_SUPPLY_USB_TYPE_CDP; + break; + + case SGM4154x_USB_DCP: + usb_type = POWER_SUPPLY_USB_TYPE_DCP; + break; + + case SGM4154x_NON_STANDARD: + usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + break; + + default: + usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + break; + } + + if (sgm->use_ext_usb_psy && sgm->usb) { + int ret; + union power_supply_propval val = {0}; + ret = power_supply_get_property(sgm->usb, + POWER_SUPPLY_PROP_USB_TYPE, &val); + if (!ret) { + usb_type = val.intval; + } + } + + pr_debug("usb_type:%d\n", usb_type); + return usb_type; +} +#endif +static int sgm4154x_property_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + switch (prop) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + return true; + default: + return false; + } +} +static int sgm4154x_charger_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct sgm4154x_device *sgm = power_supply_get_drvdata(psy); + int ret = -EINVAL; + + switch (prop) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + if (val->intval < 0) { + sgm->update_pending = true; + sgm->user_ilim = -EINVAL; + ret = 0; + pr_info("Clear user input limit\n"); + } else { + sgm->user_ilim = val->intval; + ret = sgm4154x_set_input_curr_lim(sgm, val->intval); + pr_info("Set user input limit: %duA\n", sgm->user_ilim); + } + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = sgm4154x_set_input_volt_lim(sgm, val->intval); + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + if (val->intval < 0) { + sgm->update_pending = true; + sgm->user_ichg = -EINVAL; + ret = 0; + pr_info("Clear user charging current limit\n"); + } else { + sgm->user_ichg = val->intval; + ret = sgm4154x_set_ichrg_curr(sgm, val->intval); + pr_info("Set user charging current limit: %duA\n", sgm->user_ichg); + } + break; + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + ret = sgm4154x_set_thermal_mitigation(sgm, val->intval); + break; + default: + return -EINVAL; + } + + return ret; +} + +int sgm4154x_get_charging_status(struct sgm4154x_device *sgm) +{ + int ret = 0; + int chrg_stat; + + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_STAT, &chrg_stat); + if (ret) { + pr_err("read SGM4154x_CHRG_STAT fail\n"); + return ret; + } + + chrg_stat &= SGM4154x_CHG_STAT_MASK; + + return chrg_stat; +} + +static int sgm4154x_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct sgm4154x_device *sgm = power_supply_get_drvdata(psy); + struct sgm4154x_state state; + int chrg_status = 0; + int ibus_ua = 0; + int ret = 0; + + mutex_lock(&sgm->lock); + //ret = sgm4154x_get_state(sgm, &state); + state = sgm->state; + mutex_unlock(&sgm->lock); + if (ret) + return ret; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + chrg_status = sgm4154x_get_charging_status(sgm); + if (!state.chrg_type || (state.chrg_type == SGM4154x_OTG_MODE)) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else if (!chrg_status || (chrg_status == SGM4154x_TERM_CHRG)) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + chrg_status = sgm4154x_get_charging_status(sgm); + switch (chrg_status) { + case SGM4154x_PRECHRG: + val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case SGM4154x_FAST_CHRG: + val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; + break; + case SGM4154x_TERM_CHRG: + case SGM4154x_NOT_CHRGING: + val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + default: + val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + } + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = SGM4154x_MANUFACTURER; + break; + + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = SGM4154x_NAME; + break; + + case POWER_SUPPLY_PROP_ONLINE: + val->intval = state.online; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = state.vbus_gd; + break; + case POWER_SUPPLY_PROP_TYPE: + val->intval = sgm4154x_power_supply_desc.type; + break; + case POWER_SUPPLY_PROP_USB_TYPE: + val->intval = get_charger_type(sgm); + break; + + case POWER_SUPPLY_PROP_HEALTH: + if (state.chrg_fault & 0xF8) + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + else + val->intval = POWER_SUPPLY_HEALTH_GOOD; + + switch (state.health) { + case SGM4154x_TEMP_HOT: + val->intval = POWER_SUPPLY_HEALTH_HOT; + break; + case SGM4154x_TEMP_WARM: + val->intval = POWER_SUPPLY_HEALTH_WARM; + break; + case SGM4154x_TEMP_COOL: + val->intval = POWER_SUPPLY_HEALTH_COOL; + break; + case SGM4154x_TEMP_COLD: + val->intval = POWER_SUPPLY_HEALTH_COLD; + break; + } + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = state.vbus_adc; + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = state.ibus_adc; + break; + + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = sgm4154x_get_input_volt_lim(sgm); + if (ret < 0) + return ret; + + val->intval = ret; + ret = 0; + break; + + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: + val->intval = sgm->curr_thermal_level; + break; + + case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: + val->intval = sgm->num_thermal_levels; + break; + + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + val->intval = sgm->ilim; + if (sgm->init_data.vlim > 0 && state.vbus_adc >= sgm->init_data.vlim) { + ibus_ua = (sgm->ichg / 1000) * (sgm->vreg / 1000) / (state.vbus_adc / 1000) * 1000; + if (ibus_ua < sgm->ilim) + val->intval = ibus_ua; + } + ret = 0; + break; + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + ret = sgm4154x_get_ichg_curr(sgm); + if (ret < 0) + return ret; + + val->intval = ret; + ret = 0; + break; + default: + return -EINVAL; + } + + return ret; +} + +static bool sgm4154x_state_changed(struct sgm4154x_device *sgm, + struct sgm4154x_state *new_state) +{ + struct sgm4154x_state old_state; + + mutex_lock(&sgm->lock); + old_state = sgm->state; + mutex_unlock(&sgm->lock); + + return (old_state.chrg_stat != new_state->chrg_stat || + old_state.chrg_fault != new_state->chrg_fault || + old_state.online != new_state->online || + old_state.health != new_state->health || + old_state.chrg_type != new_state->chrg_type || + old_state.vbus_gd != new_state->vbus_gd || + old_state.ibus_limit != new_state->ibus_limit || + old_state.detect_done != new_state->detect_done + ); +} + +static void sgm4154x_dump_register(struct sgm4154x_device * sgm) +{ + int i = 0; + u32 reg = 0; + + for(i=0; i<=SGM4154x_CHRG_CTRL_f; i++) { + regmap_read(sgm->regmap, i, ®); + pr_info("REG[0x%x]=0x%x\n", i, reg); + } +} + +#if 0 +static bool sgm4154x_dpdm_detect_is_done(struct sgm4154x_device * sgm) +{ + int chrg_stat; + int ret; + + ret = regmap_read(sgm->regmap, SGM4154x_INPUT_DET, &chrg_stat); + if(ret) { + dev_err(sgm->dev, "Check DPDM detecte error\n"); + } + + return (chrg_stat&SGM4154x_DPDM_ONGOING)?true:false; +} +#endif + +static void charger_monitor_work_func(struct work_struct *work) +{ + int ret = 0; + //static u8 last_chg_method = 0; + bool state_changed = false; + struct sgm4154x_state state; + struct sgm4154x_device *sgm = container_of(work, + struct sgm4154x_device, + charge_monitor_work.work); + + ret = sgm4154x_get_state(sgm, &state); + state_changed = sgm4154x_state_changed(sgm, &state); + mutex_lock(&sgm->lock); + sgm->state = state; + mutex_unlock(&sgm->lock); + + if (state_changed) { + if (!sgm->state.chrg_type) + pr_info("No charger is present\n"); + sgm4154x_dump_register(sgm); + cancel_delayed_work(&sgm->charge_detect_delayed_work); + schedule_delayed_work(&sgm->charge_detect_delayed_work, 0); + power_supply_changed(sgm->charger); + } + + schedule_delayed_work(&sgm->charge_monitor_work, 10*HZ); +} + +static int sgm4154x_mmi_charger_init(struct sgm_mmi_charger *chg); +static void charger_detect_work_func(struct work_struct *work) +{ + int ret; + //static int charge_type_old = 0; + int curr_in_limit = 0; + u32 volt_in_limit = 0; + static int prev_in_limit = 0; + bool state_changed = false; + struct sgm4154x_state state; + struct sgm4154x_device *sgm = container_of(work, + struct sgm4154x_device, + charge_detect_delayed_work.work); + + if (!sgm->charger_wakelock->active) + __pm_stay_awake(sgm->charger_wakelock); + + if (sgm->use_ext_usb_psy) { + if (!sgm->usb) { + const char *usb_psy_name = NULL; + ret = device_property_read_string(sgm->dev, + "mmi,ext-usb-psy-name", + &usb_psy_name); + if (!ret && usb_psy_name) + sgm->usb = power_supply_get_by_name(usb_psy_name); + if (!sgm->usb) { + pr_info("No USB power supply found, redetecting...\n"); + cancel_delayed_work(&sgm->charge_detect_delayed_work); + schedule_delayed_work(&sgm->charge_detect_delayed_work, + msecs_to_jiffies(1000)); + goto err; + } else { + pr_info("USB power supply is found\n"); + } + } + } + + if (sgm->use_ext_wls_psy) { + if (!sgm->wls_psy) { + const char *wls_psy_name = NULL; + ret = device_property_read_string(sgm->dev, + "mmi,ext-wls-psy-name", + &wls_psy_name); + if (!ret && wls_psy_name) + sgm->wls_psy = power_supply_get_by_name(wls_psy_name); + if (!sgm->wls_psy) { + pr_info("No Wireless power supply found, redetecting...\n"); + cancel_delayed_work(&sgm->charge_detect_delayed_work); + schedule_delayed_work(&sgm->charge_detect_delayed_work, + msecs_to_jiffies(1000)); + goto err; + } else { + pr_info("WLS power supply is found\n"); + } + } + } + + if (sgm->mmi_charger && + (!sgm->mmi_charger->fg_psy || !sgm->mmi_charger->driver)) { + sgm4154x_mmi_charger_init(sgm->mmi_charger); + } + + ret = sgm4154x_get_state(sgm, &state); + state_changed = sgm4154x_state_changed(sgm, &state); + mutex_lock(&sgm->lock); + sgm->state = state; + mutex_unlock(&sgm->lock); + + if(!sgm->state.vbus_gd) { + if (sgm4154x_is_enabled_charging(sgm) && !state.online) + sgm4154x_disable_charger(sgm); + dev_err(sgm->dev, "Vbus not present, disable charge\n"); + goto err; + } + if(!state.online) + { + dev_err(sgm->dev, "Vbus not online\n"); + goto err; + } + if(!state.detect_done) { + dev_err(sgm->dev, "DPDM detecte not done\n"); + goto err; + } + + volt_in_limit = sgm->init_data.vlim; +#if defined(__SGM41542_CHIP_ID__)|| defined(__SGM41516D_CHIP_ID__) + switch(sgm->state.chrg_type) { + case SGM4154x_USB_SDP: + pr_err("SGM4154x charger type: SDP\n"); + curr_in_limit = 500000; + break; + + case SGM4154x_USB_CDP: + pr_err("SGM4154x charger type: CDP\n"); + curr_in_limit = 1500000; + break; + + case SGM4154x_USB_DCP: + pr_err("SGM4154x charger type: DCP\n"); + curr_in_limit = 1500000; + break; + + case SGM4154x_UNKNOWN: + pr_err("SGM4154x charger type: UNKNOWN\n"); + curr_in_limit = 500000; + break; + case SGM4154x_NON_STANDARD: + pr_err("SGM4154x charger type: NON_STANDARD\n"); + curr_in_limit = 500000; + break; + + case SGM4154x_OTG_MODE: + pr_err("SGM4154x OTG mode do nothing\n"); + goto err; + + default: + if (is_wls_online(sgm)) { + pr_err("SGM4154x charger type: Wireless\n"); + sgm->state.chrg_type = SGM4154x_WLS_TYPE; + curr_in_limit = sgm->init_data.wls_ilim; + volt_in_limit = sgm->init_data.wls_vlim; + break; + } else { + pr_err("SGM4154x charger type: default\n"); + //curr_in_limit = 500000; + //break; + return; + } + } + + //Enable charge IC + if (!sgm->mmi_charger) { + sgm4154x_enable_charger(sgm); + } + + if (volt_in_limit != sgm->vlim) { + dev_info(sgm->dev, "Update: volt_in_limit = %d\n", + volt_in_limit); + sgm4154x_set_input_volt_lim(sgm, volt_in_limit); + } + + if ((!sgm->use_ext_usb_psy || !sgm->usb) && + prev_in_limit != curr_in_limit) { + dev_info(sgm->dev, "Update: curr_in_limit = %d\n", + curr_in_limit); + sgm4154x_set_input_curr_lim(sgm, curr_in_limit); + prev_in_limit = curr_in_limit; + } +#endif + if (state_changed) { + sgm4154x_dump_register(sgm); + power_supply_changed(sgm->charger); + } + return; +err: + //release wakelock + if (state_changed) + power_supply_changed(sgm->charger); + dev_info(sgm->dev, "Relax wakelock\n"); + __pm_relax(sgm->charger_wakelock); + return; +} + +static irqreturn_t sgm4154x_irq_handler_thread(int irq, void *private) +{ + struct sgm4154x_device *sgm = private; + + //lock wakelock + pr_info("entry irq handler\n"); + sgm->update_pending = true; + + #if defined(__SGM41542_CHIP_ID__)|| defined(__SGM41516D_CHIP_ID__) + cancel_delayed_work(&sgm->charge_detect_delayed_work); + schedule_delayed_work(&sgm->charge_detect_delayed_work, 100); + //power_supply_changed(sgm->charger); + #endif + return IRQ_HANDLED; +} + +static enum power_supply_property sgm4154x_power_supply_props[] = { + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, + POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_PRESENT +}; + +static char *sgm4154x_charger_supplied_to[] = { + "battery", +}; + +static struct power_supply_desc sgm4154x_power_supply_desc = { + .name = "charger", + .type = POWER_SUPPLY_TYPE_UNKNOWN, + .usb_types = sgm4154x_usb_type, + .num_usb_types = ARRAY_SIZE(sgm4154x_usb_type), + .properties = sgm4154x_power_supply_props, + .num_properties = ARRAY_SIZE(sgm4154x_power_supply_props), + .get_property = sgm4154x_charger_get_property, + .set_property = sgm4154x_charger_set_property, + .property_is_writeable = sgm4154x_property_is_writeable, +}; + +#ifdef SGM_BASE +static bool sgm4154x_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SGM4154x_CHRG_CTRL_0...SGM4154x_CHRG_CTRL_f: + return true; + default: + return false; + } +} +#endif + +static const struct regmap_config sgm4154x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = SGM4154x_CHRG_CTRL_f, +#ifdef SGM_BASE + .reg_defaults = sgm4154x_reg_defs, + .num_reg_defaults = ARRAY_SIZE(sgm4154x_reg_defs), + .cache_type = REGCACHE_RBTREE, + .volatile_reg = sgm4154x_is_volatile_reg, +#endif +}; + +static int sgm4154x_power_supply_init(struct sgm4154x_device *sgm, + struct device *dev) +{ + struct power_supply_config psy_cfg = { .drv_data = sgm, + .of_node = dev->of_node, }; + + psy_cfg.supplied_to = sgm4154x_charger_supplied_to; + psy_cfg.num_supplicants = ARRAY_SIZE(sgm4154x_charger_supplied_to); + + sgm->charger = devm_power_supply_register(sgm->dev, + &sgm4154x_power_supply_desc, + &psy_cfg); + if (IS_ERR(sgm->charger)) + return PTR_ERR(sgm->charger); + + return 0; +} + +static int sgm4154x_hw_init(struct sgm4154x_device *sgm) +{ + int ret = 0; + + ret = sgm4154x_set_ichrg_curr(sgm, sgm->init_data.ichg); + if (ret) + goto err_out; + + ret = sgm4154x_set_prechrg_curr(sgm, sgm->init_data.iprechg); + if (ret) + goto err_out; + + ret = sgm4154x_set_chrg_volt(sgm, sgm->init_data.vreg); + if (ret) + goto err_out; + + ret = sgm4154x_set_term_curr(sgm, sgm->init_data.iterm); + if (ret) + goto err_out; + + ret = sgm4154x_set_input_volt_lim(sgm, sgm->init_data.vlim); + if (ret) + goto err_out; + + ret = sgm4154x_set_input_curr_lim(sgm, sgm->init_data.ilim); + if (ret) + goto err_out; + + ret = sgm4154x_set_vac_ovp(sgm);//14V + if (ret) + goto err_out; + + ret = sgm4154x_set_recharge_volt(sgm, sgm->init_data.vrechg);//100~200mv + if (ret) + goto err_out; + + ret = sgm4154x_enable_hw_jeita(sgm, sgm->enable_hw_jeita); + if (ret) + goto err_out; + + ret = sgm4154x_disable_watchdog(sgm); + if (ret) + goto err_out; + + ret = sgm4154x_disable_vindpm_int_pulse(sgm); + if (ret) + goto err_out; + + ret = sgm4154x_disable_iindpm_int_pulse(sgm); + if (ret) + goto err_out; + + if (sgm->init_data.charger_disabled) { + ret = sgm4154x_disable_charger(sgm); + ret |= sgm4154x_set_hiz_en(sgm, true); + } else { + ret |= sgm4154x_set_hiz_en(sgm, false); + ret = sgm4154x_enable_charger(sgm); + } + if (ret) + goto err_out; + + dev_notice(sgm->dev, "ichrg:%d vchrg:%d iprechrg:%d iterm:%d" + " ilim:%d vlim:%d vrechg:%d chg_dis:%d hw_jeita_en:%d\n", + sgm->init_data.ichg, sgm->init_data.vreg, + sgm->init_data.iprechg, sgm->init_data.iterm, + sgm->init_data.ilim, sgm->init_data.vlim, + sgm->init_data.vrechg, sgm->init_data.charger_disabled, + sgm->enable_hw_jeita); + + return 0; + +err_out: + return ret; +} + +static int sgm4154x_parse_dt(struct sgm4154x_device *sgm) +{ + int ret; + //u32 val = 0; + int irq_gpio = 0, irqn = 0; + enum of_gpio_flags flags; + const char *usb_psy_name = NULL; + const char *wls_psy_name = NULL; + int i, len; + u32 prev, val; + struct device_node *node = sgm->dev->of_node; + + #if 0 + ret = device_property_read_u32(sgm->dev, "watchdog-timer", + &sgm->watchdog_timer); + if (ret) + sgm->watchdog_timer = SGM4154x_WATCHDOG_DIS; + + if (sgm->watchdog_timer > SGM4154x_WATCHDOG_MAX || + sgm->watchdog_timer < SGM4154x_WATCHDOG_DIS) + return -EINVAL; + #endif + ret = device_property_read_u32(sgm->dev, + "mmi,batt-protect-duration", + &sgm->batt_protect_duration); + if (ret) + sgm->batt_protect_duration = DEF_PROTECT_DURATION_MS; + + ret = device_property_read_u32(sgm->dev, + "mmi,vbat-ovp-threshold", + &sgm->vbat_ovp_threshold); + if (ret) + sgm->vbat_ovp_threshold = DEF_VBAT_OVP_MV; + + ret = device_property_read_u32(sgm->dev, + "mmi,ibat-ocp-threshold", + &sgm->ibat_ocp_threshold); + if (ret) + sgm->ibat_ocp_threshold = DEF_IBAT_OCP_MA; + + ret = device_property_read_u32(sgm->dev, + "mmi,ibat-occp-threshold", + &sgm->ibat_occp_threshold); + if (ret) + sgm->ibat_occp_threshold = DEF_IBAT_OCCP_MA; + + sgm->high_load_en_gpio = of_get_named_gpio_flags(sgm->dev->of_node, + "mmi,high-load-en-gpio", 0, &flags); + if (gpio_is_valid(sgm->high_load_en_gpio)) { + ret = gpio_request(sgm->high_load_en_gpio, + "high-load-en-gpio"); + if (ret) { + dev_err(sgm->dev, "%s: %d gpio request failed\n", + __func__, sgm->high_load_en_gpio); + sgm->high_load_en_gpio = -EINVAL; + } else { + sgm->high_load_active_low = !!(flags & OF_GPIO_ACTIVE_LOW); + /* Disable batt high load switch by default */ + gpio_direction_output(sgm->high_load_en_gpio, + sgm->high_load_active_low); + } + } + + sgm->low_load_en_gpio = of_get_named_gpio_flags(sgm->dev->of_node, + "mmi,low-load-en-gpio", 0, &flags); + if (gpio_is_valid(sgm->low_load_en_gpio)) { + ret = gpio_request(sgm->low_load_en_gpio, + "low-load-en-gpio"); + if (ret) { + dev_err(sgm->dev, "%s: %d gpio request failed\n", + __func__, sgm->low_load_en_gpio); + sgm->low_load_en_gpio = -EINVAL; + } else { + sgm->low_load_active_low = !!(flags & OF_GPIO_ACTIVE_LOW); + /* Disable batt low load switch by default */ + gpio_direction_output(sgm->low_load_en_gpio, + sgm->low_load_active_low); + } + } + + ret = device_property_read_string(sgm->dev, + "mmi,ext-usb-psy-name", + &usb_psy_name); + if (!ret && usb_psy_name) + sgm->use_ext_usb_psy = true; + else + sgm->use_ext_usb_psy = false; + + ret = device_property_read_string(sgm->dev, + "mmi,ext-wls-psy-name", + &wls_psy_name); + if (!ret && wls_psy_name) + sgm->use_ext_wls_psy = true; + else + sgm->use_ext_wls_psy = false; + + sgm->vdd_i2c_vreg = devm_regulator_get_optional(sgm->dev, "vdd-i2c"); + + sgm->enable_hw_jeita = of_property_read_bool(sgm->dev->of_node, + "enable_hw_jeita"); + + sgm->init_data.charger_disabled = of_property_read_bool(sgm->dev->of_node, + "init-charger-disabled"); + + ret = device_property_read_u32(sgm->dev, + "vrechg-millivolt", + &sgm->init_data.vrechg); + if (ret) + sgm->init_data.vrechg = 200; + + if (sgm->init_data.vrechg > SGM4154x_VRECHRG_OFFSET_mV) + sgm->init_data.vrechg = 200; + else + sgm->init_data.vrechg = 100; + + ret = device_property_read_u32(sgm->dev, + "iprechg-microamp", + &sgm->init_data.iprechg); + if (ret) + sgm->init_data.iprechg = SGM4154x_PRECHRG_I_DEF_uA; + + if (sgm->init_data.iprechg > SGM4154x_PRECHRG_I_MAX_uA || + sgm->init_data.iprechg < SGM4154x_PRECHRG_I_MIN_uA) + return -EINVAL; + + ret = device_property_read_u32(sgm->dev, + "iterm-microamp", + &sgm->init_data.iterm); + if (ret) + sgm->init_data.iterm = SGM4154x_TERMCHRG_I_DEF_uA; + + if (sgm->init_data.iterm > SGM4154x_TERMCHRG_I_MAX_uA || + sgm->init_data.iterm < SGM4154x_TERMCHRG_I_MIN_uA) + return -EINVAL; + + ret = device_property_read_u32(sgm->dev, + "ichg-max-microamp", + &sgm->init_data.max_ichg); + if (ret) + sgm->init_data.max_ichg = SGM4154x_ICHRG_I_MAX_uA; + + if (sgm->init_data.max_ichg > SGM4154x_ICHRG_I_MAX_uA || + sgm->init_data.max_ichg < SGM4154x_ICHRG_I_MIN_uA) + return -EINVAL; + + ret = device_property_read_u32(sgm->dev, + "vchg-max-microvolt", + &sgm->init_data.max_vreg); + if (ret) + sgm->init_data.max_vreg = SGM4154x_VREG_V_MAX_uV; + + if (sgm->init_data.max_vreg > SGM4154x_VREG_V_MAX_uV || + sgm->init_data.max_vreg < SGM4154x_VREG_V_MIN_uV) + return -EINVAL; + + ret = device_property_read_u32(sgm->dev, + "ichg-microamp", + &sgm->init_data.ichg); + if (ret) + sgm->init_data.ichg = SGM4154x_ICHRG_I_DEF_uA; + + if (sgm->init_data.ichg > SGM4154x_ICHRG_I_MAX_uA || + sgm->init_data.ichg < SGM4154x_ICHRG_I_MIN_uA) + return -EINVAL; + + ret = device_property_read_u32(sgm->dev, + "vchg-microvolt", + &sgm->init_data.vreg); + if (ret) + sgm->init_data.vreg = SGM4154x_VREG_V_DEF_uV; + + if (sgm->init_data.vreg > SGM4154x_VREG_V_MAX_uV || + sgm->init_data.vreg < SGM4154x_VREG_V_MIN_uV) + return -EINVAL; + + ret = device_property_read_u32(sgm->dev, + "input-voltage-limit-microvolt", + &sgm->init_data.vlim); + if (ret) + sgm->init_data.vlim = SGM4154x_VINDPM_DEF_uV; + + if (sgm->init_data.vlim > SGM4154x_VINDPM_V_MAX_uV || + sgm->init_data.vlim < SGM4154x_VINDPM_V_MIN_uV) + return -EINVAL; + + ret = device_property_read_u32(sgm->dev, + "wls-input-voltage-limit-microvolt", + &sgm->init_data.wls_vlim); + if (ret) + sgm->init_data.wls_vlim = SGM4154x_VINDPM_DEF_uV; + + if (sgm->init_data.wls_vlim > SGM4154x_VINDPM_V_MAX_uV || + sgm->init_data.wls_vlim < SGM4154x_VINDPM_V_MIN_uV) + return -EINVAL; + + ret = device_property_read_u32(sgm->dev, + "input-current-limit-microamp", + &sgm->init_data.ilim); + if (ret) + sgm->init_data.ilim = SGM4154x_IINDPM_DEF_uA; + + if (sgm->init_data.ilim > SGM4154x_IINDPM_I_MAX_uA || + sgm->init_data.ilim < SGM4154x_IINDPM_I_MIN_uA) + return -EINVAL; + + ret = device_property_read_u32(sgm->dev, + "wls-input-current-limit-microamp", + &sgm->init_data.wls_ilim); + if (ret) + sgm->init_data.wls_ilim = SGM4154x_IINDPM_I_MIN_uA; + + pr_info("wireless input current limit: %duA\n", sgm->init_data.wls_ilim); + + ret = device_property_read_u32(sgm->dev, + "wls-ichg-limit-microamp", + &sgm->init_data.wls_ichg_ilim); + if (ret) + sgm->init_data.wls_ichg_ilim = SGM4154x_ICHRG_I_MIN_uA; + + pr_info("wireless ichg current limit: %duA\n", sgm->init_data.wls_ichg_ilim); + + ret = device_property_read_u32(sgm->dev, + "wls-8w-ichg-limit-microamp", + &sgm->init_data.wls_8w_ichg_ilim); + if (ret) + sgm->init_data.wls_8w_ichg_ilim = SGM4154x_ICHRG_I_MIN_uA; + + pr_info("wireless 8w ichg current limit: %duA\n", sgm->init_data.wls_8w_ichg_ilim); + + + irq_gpio = of_get_named_gpio(sgm->dev->of_node, "sgm,irq-gpio", 0); + if (!gpio_is_valid(irq_gpio)) + { + dev_err(sgm->dev, "%s: %d gpio get failed\n", __func__, irq_gpio); + return -EINVAL; + } + ret = gpio_request(irq_gpio, "sgm4154x irq pin"); + if (ret) { + dev_err(sgm->dev, "%s: %d gpio request failed\n", __func__, irq_gpio); + return ret; + } + gpio_direction_input(irq_gpio); + irqn = gpio_to_irq(irq_gpio); + if (irqn < 0) { + dev_err(sgm->dev, "%s:%d gpio_to_irq failed\n", __func__, irqn); + return irqn; + } + sgm->client->irq = irqn; + + sgm->chg_en_gpio = of_get_named_gpio(sgm->dev->of_node, "sgm,chg-en-gpio", 0); + if (gpio_is_valid(sgm->chg_en_gpio)) + { + ret = gpio_request(sgm->chg_en_gpio, "sgm4154x chg en pin"); + if (ret) { + dev_err(sgm->dev, "%s: %d gpio request failed\n", + __func__, + sgm->chg_en_gpio); + sgm->chg_en_gpio = -EINVAL; + } else { + gpio_direction_output(sgm->chg_en_gpio, + sgm->init_data.charger_disabled); + } + } +#ifdef SGM_BASE + /* sw jeita */ + sgm->enable_sw_jeita = of_property_read_bool(sgm->dev->of_node, "enable_sw_jeita"); + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_above_t4_cv", &val) >= 0) + sgm->data.jeita_temp_above_t4_cv = val; + else { + dev_err(sgm->dev, "use default JEITA_TEMP_ABOVE_T4_CV:%d\n",JEITA_TEMP_ABOVE_T4_CV); + sgm->data.jeita_temp_above_t4_cv = JEITA_TEMP_ABOVE_T4_CV; + } + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_t3_to_t4_cv", &val) >= 0) + sgm->data.jeita_temp_t3_to_t4_cv = val; + else { + dev_err(sgm->dev, "use default JEITA_TEMP_T3_TO_T4_CV:%d\n",JEITA_TEMP_T3_TO_T4_CV); + sgm->data.jeita_temp_t3_to_t4_cv = JEITA_TEMP_T3_TO_T4_CV; + } + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_t2_to_t3_cv", &val) >= 0) + sgm->data.jeita_temp_t2_to_t3_cv = val; + else { + dev_err(sgm->dev, "use default JEITA_TEMP_T2_TO_T3_CV:%d\n",JEITA_TEMP_T2_TO_T3_CV); + sgm->data.jeita_temp_t2_to_t3_cv = JEITA_TEMP_T2_TO_T3_CV; + } + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_t1_to_t2_cv", &val) >= 0) + sgm->data.jeita_temp_t1_to_t2_cv = val; + else { + dev_err(sgm->dev, "use default JEITA_TEMP_T1_TO_T2_CV:%d\n",JEITA_TEMP_T1_TO_T2_CV); + sgm->data.jeita_temp_t1_to_t2_cv = JEITA_TEMP_T1_TO_T2_CV; + } + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_t0_to_t1_cv", &val) >= 0) + sgm->data.jeita_temp_t0_to_t1_cv = val; + else { + dev_err(sgm->dev, "use default JEITA_TEMP_T0_TO_T1_CV:%d\n",JEITA_TEMP_T0_TO_T1_CV); + sgm->data.jeita_temp_t0_to_t1_cv = JEITA_TEMP_T0_TO_T1_CV; + } + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_below_t0_cv", &val) >= 0) + sgm->data.jeita_temp_below_t0_cv = val; + else { + dev_err(sgm->dev, "use default JEITA_TEMP_BELOW_T0_CV:%d\n",JEITA_TEMP_BELOW_T0_CV); + sgm->data.jeita_temp_below_t0_cv = JEITA_TEMP_BELOW_T0_CV; + } + pr_info("enable_sw_jeita = %d,CV1 = %d,CV2 = %d,CV3 = %d,CV4 = %d,CV5 = %d,CV6 = %d\n", + sgm->enable_sw_jeita,sgm->data.jeita_temp_above_t4_cv,sgm->data.jeita_temp_t3_to_t4_cv, + sgm->data.jeita_temp_t2_to_t3_cv,sgm->data.jeita_temp_t1_to_t2_cv, + sgm->data.jeita_temp_t0_to_t1_cv,sgm->data.jeita_temp_below_t0_cv); + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_above_t4_cc_current", &val) >= 0) + sgm->data.jeita_temp_above_t4_cc_current = val; + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_t3_to_t4_cc_current", &val) >= 0) + sgm->data.jeita_temp_t3_to_t4_cc_current = val; + else + sgm->data.jeita_temp_t3_to_t4_cc_current = JEITA_TEMP_T3_TO_T4_CC_CURRENT; + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_t2_to_t3_cc_current", &val) >= 0) + sgm->data.jeita_temp_t2_to_t3_cc_current = val; + else + sgm->data.jeita_temp_t2_to_t3_cc_current = JEITA_TEMP_T2_TO_T3_CC_CURRENT; + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_t1_to_t2_cc_current", &val) >= 0) + sgm->data.jeita_temp_t1_to_t2_cc_current = val; + else + sgm->data.jeita_temp_t1_to_t2_cc_current = JEITA_TEMP_T1_TO_T2_CC_CURRENT; + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_below_t0_cc_current", &val) >= 0) + sgm->data.jeita_temp_below_t0_cc_current = val; + else + sgm->data.jeita_temp_below_t0_cc_current = JEITA_TEMP_BELOW_T0_CC_CURRENT; + + pr_err("CC1 = %d,CC2 = %d,CC3 = %d,CC4 = %d,CC5 = %d\n", + sgm->data.jeita_temp_above_t4_cc_current,sgm->data.jeita_temp_t3_to_t4_cc_current, + sgm->data.jeita_temp_t2_to_t3_cc_current,sgm->data.jeita_temp_t1_to_t2_cc_current, + sgm->data.jeita_temp_below_t0_cc_current); + + if (of_property_read_u32(sgm->dev->of_node, "temp_t4_thres", &val) >= 0) + sgm->data.temp_t4_thres = val; + else { + dev_err(sgm->dev, "use default TEMP_T4_THRES:%d\n",TEMP_T4_THRES); + sgm->data.temp_t4_thres = TEMP_T4_THRES; + } + if (of_property_read_u32(sgm->dev->of_node, "temp_t4_thres_minus_x_degree", &val) >= 0) + sgm->data.temp_t4_thres_minus_x_degree = val; + else { + dev_err(sgm->dev,"use default TEMP_T4_THRES_MINUS_X_DEGREE:%d\n",TEMP_T4_THRES_MINUS_X_DEGREE); + sgm->data.temp_t4_thres_minus_x_degree = TEMP_T4_THRES_MINUS_X_DEGREE; + } + + if (of_property_read_u32(sgm->dev->of_node, "temp_t3_thres", &val) >= 0) + sgm->data.temp_t3_thres = val; + else { + dev_err(sgm->dev,"use default TEMP_T3_THRES:%d\n",TEMP_T3_THRES); + sgm->data.temp_t3_thres = TEMP_T3_THRES; + } + + if (of_property_read_u32(sgm->dev->of_node, "temp_t3_thres_minus_x_degree", &val) >= 0) + sgm->data.temp_t3_thres_minus_x_degree = val; + else { + dev_err(sgm->dev,"use default TEMP_T3_THRES_MINUS_X_DEGREE:%d\n",TEMP_T3_THRES_MINUS_X_DEGREE); + sgm->data.temp_t3_thres_minus_x_degree = TEMP_T3_THRES_MINUS_X_DEGREE; + } + + if (of_property_read_u32(sgm->dev->of_node, "temp_t2_thres", &val) >= 0) + sgm->data.temp_t2_thres = val; + else { + dev_err(sgm->dev,"use default TEMP_T2_THRES:%d\n",TEMP_T2_THRES); + sgm->data.temp_t2_thres = TEMP_T2_THRES; + } + + if (of_property_read_u32(sgm->dev->of_node, "temp_t2_thres_plus_x_degree", &val) >= 0) + sgm->data.temp_t2_thres_plus_x_degree = val; + else { + dev_err(sgm->dev,"use default TEMP_T2_THRES_PLUS_X_DEGREE:%d\n",TEMP_T2_THRES_PLUS_X_DEGREE); + sgm->data.temp_t2_thres_plus_x_degree = TEMP_T2_THRES_PLUS_X_DEGREE; + } + + if (of_property_read_u32(sgm->dev->of_node, "temp_t1_thres", &val) >= 0) + sgm->data.temp_t1_thres = val; + else { + dev_err(sgm->dev,"use default TEMP_T1_THRES:%d\n",TEMP_T1_THRES); + sgm->data.temp_t1_thres = TEMP_T1_THRES; + } + + if (of_property_read_u32(sgm->dev->of_node, "temp_t1_thres_plus_x_degree", &val) >= 0) + sgm->data.temp_t1_thres_plus_x_degree = val; + else { + dev_err(sgm->dev,"use default TEMP_T1_THRES_PLUS_X_DEGREE:%d\n",TEMP_T1_THRES_PLUS_X_DEGREE); + sgm->data.temp_t1_thres_plus_x_degree = TEMP_T1_THRES_PLUS_X_DEGREE; + } + + if (of_property_read_u32(sgm->dev->of_node, "temp_t0_thres", &val) >= 0) + sgm->data.temp_t0_thres = val; + else { + dev_err(sgm->dev,"use default TEMP_T0_THRES:%d\n",TEMP_T0_THRES); + sgm->data.temp_t0_thres = TEMP_T0_THRES; + } + + if (of_property_read_u32(sgm->dev->of_node, "temp_t0_thres_plus_x_degree", &val) >= 0) + sgm->data.temp_t0_thres_plus_x_degree = val; + else { + dev_err(sgm->dev,"use default TEMP_T0_THRES_PLUS_X_DEGREE:%d\n",TEMP_T0_THRES_PLUS_X_DEGREE); + sgm->data.temp_t0_thres_plus_x_degree = TEMP_T0_THRES_PLUS_X_DEGREE; + } + + if (of_property_read_u32(sgm->dev->of_node, "temp_neg_10_thres", &val) >= 0) + sgm->data.temp_neg_10_thres = val; + else { + dev_err(sgm->dev,"use default TEMP_NEG_10_THRES:%d\n",TEMP_NEG_10_THRES); + sgm->data.temp_neg_10_thres = TEMP_NEG_10_THRES; + } + pr_info("%s,thres4 = %d,thres3 = %d,thres2 = %d,thres1 = %d,thres0 = %d\n", + sgm->data.temp_t4_thres,sgm->data.temp_t3_thres, + sgm->data.temp_t2_thres,sgm->data.temp_t1_thres, + sgm->data.temp_t0_thres); +#endif + ret = of_property_count_elems_of_size(node, "mmi,thermal-mitigation", + sizeof(u32)); + if (ret <= 0) { + return 0; + } + + len = ret; + prev = sgm->init_data.max_ichg; + for (i = 0; i < len; i++) { + ret = of_property_read_u32_index(node, + "mmi,thermal-mitigation", + i, &val); + if (ret < 0) { + pr_err("failed to get thermal-mitigation[%d], ret=%d\n", i, ret); + return ret; + } + pr_info("thermal-mitigation[%d], val=%d, prev=%d\n", i, val, prev); + if (val > prev) { + pr_err("Thermal levels should be in descending order\n"); + sgm->num_thermal_levels = -EINVAL; + return 0; + } + prev = val; + } + + sgm->thermal_levels = devm_kcalloc(sgm->dev, len + 1, + sizeof(*sgm->thermal_levels), + GFP_KERNEL); + if (!sgm->thermal_levels) + return -ENOMEM; + + sgm->thermal_levels[0] = sgm->init_data.max_ichg; + ret = of_property_read_u32_array(node, "mmi,thermal-mitigation", + &sgm->thermal_levels[1], len); + if (ret < 0) { + pr_err("Error in reading mmi,thermal-mitigation, rc=%d\n", ret); + return ret; + } + sgm->num_thermal_levels = len; + sgm->thermal_fcc_ua = sgm->init_data.max_ichg; + + return 0; +} + +static int sgm4154x_set_otg_voltage(struct sgm4154x_device *sgm, int uv) +{ + int ret = 0; + int reg_val = -1; + int i = 0; + while(i<4){ + if (uv == BOOST_VOLT_LIMIT[i]){ + reg_val = i; + break; + } + i++; + } + if (reg_val < 0) + return reg_val; + reg_val = reg_val << 4; + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_6, + SGM4154x_OTG_MASK, reg_val); + + return ret; +} + +static int sgm4154x_set_otg_current(struct sgm4154x_device *sgm, int ua) +{ + int ret = 0; + + if (ua == BOOST_CURRENT_LIMIT[0]){ + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_2, SGM4154x_OTG_EN, + 0); + } + else if (ua == BOOST_CURRENT_LIMIT[1]){ + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_2, SGM4154x_OTG_EN, + BIT(7)); + } + return ret; +} + +static int sgm4154x_enable_vbus(struct regulator_dev *rdev) +{ + struct sgm4154x_device *sgm = rdev_get_drvdata(rdev); + int ret = 0; + + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_1, SGM4154x_OTG_EN, + SGM4154x_OTG_EN); + return ret; +} + +static int sgm4154x_disable_vbus(struct regulator_dev *rdev) +{ + struct sgm4154x_device *sgm = rdev_get_drvdata(rdev); + int ret = 0; + + ret = regmap_update_bits(sgm->regmap, SGM4154x_CHRG_CTRL_1, SGM4154x_OTG_EN, + 0); + + return ret; +} + +static int sgm4154x_is_enabled_vbus(struct regulator_dev *rdev) +{ + struct sgm4154x_device *sgm = rdev_get_drvdata(rdev); + int temp = 0; + int ret = 0; + + ret = regmap_read(sgm->regmap, SGM4154x_CHRG_CTRL_1, &temp); + return (temp&SGM4154x_OTG_EN)? 1 : 0; +} + +static struct regulator_ops sgm4154x_vbus_ops = { + .enable = sgm4154x_enable_vbus, + .disable = sgm4154x_disable_vbus, + .is_enabled = sgm4154x_is_enabled_vbus, +}; + +static struct regulator_desc sgm4154x_otg_rdesc = { + .of_match = "usb-otg-vbus", + .name = "usb-otg-vbus", + .ops = &sgm4154x_vbus_ops, + .owner = THIS_MODULE, + .type = REGULATOR_VOLTAGE, + .fixed_uV = 5000000, + .n_voltages = 1, +}; + +static int sgm4154x_vbus_regulator_register(struct sgm4154x_device *sgm) +{ + struct regulator_config config = {}; + int ret = 0; + /* otg regulator */ + config.dev = sgm->dev; + config.driver_data = sgm; + sgm->otg_rdev = devm_regulator_register(sgm->dev, + &sgm4154x_otg_rdesc, &config); + sgm->otg_rdev->constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS; + if (IS_ERR(sgm->otg_rdev)) { + ret = PTR_ERR(sgm->otg_rdev); + pr_info("register otg regulator failed (%d)\n", ret); + } + return ret; +} + +static int sgm4154x_suspend_notifier(struct notifier_block *nb, + unsigned long event, + void *dummy) +{ + struct sgm4154x_device *sgm = container_of(nb, struct sgm4154x_device, pm_nb); + + switch (event) { + case PM_SUSPEND_PREPARE: + pr_err("sgm4154x PM_SUSPEND \n"); + cancel_delayed_work_sync(&sgm->charge_monitor_work); + sgm->sgm4154x_suspend_flag = 1; + return NOTIFY_OK; + case PM_POST_SUSPEND: + pr_err("sgm4154x PM_RESUME \n"); + schedule_delayed_work(&sgm->charge_monitor_work, 0); + sgm->sgm4154x_suspend_flag = 0; + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } +} + +static int sgm4154x_hw_chipid_detect(struct sgm4154x_device *sgm) +{ + int ret = 0; + int val = 0; + + ret = regmap_read(sgm->regmap,SGM4154x_CHRG_CTRL_b,&val); + if (ret < 0) + { + pr_info("read SGM4154x_CHRG_CTRL_b fail\n"); + return ret; + } + + pr_info("Reg[0x0B]=0x%x\n", val); + + return val; +} + +#define EMPTY_BATTERY_VBAT 3200 +static int sgm4154x_charger_get_batt_info(void *data, struct mmi_battery_info *batt_info) +{ + int rc; + struct sgm_mmi_charger *chg = data; + union power_supply_propval val = {0}; + + rc = power_supply_get_property(chg->fg_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); + if (!rc) + chg->batt_info.batt_mv = val.intval / 1000; + rc = power_supply_get_property(chg->fg_psy, POWER_SUPPLY_PROP_CURRENT_NOW, &val); + if (!rc) + chg->batt_info.batt_ma = val.intval / 1000 * chg->ichg_polority; + rc = power_supply_get_property(chg->fg_psy, POWER_SUPPLY_PROP_CAPACITY, &val); + if (!rc) + chg->batt_info.batt_soc = val.intval; + rc = power_supply_get_property(chg->fg_psy, POWER_SUPPLY_PROP_TEMP, &val); + if (!rc) + chg->batt_info.batt_temp = val.intval / 10; + rc = power_supply_get_property(chg->fg_psy, POWER_SUPPLY_PROP_STATUS, &val); + if (!rc) + chg->batt_info.batt_status = val.intval; + rc = power_supply_get_property(chg->fg_psy, POWER_SUPPLY_PROP_CHARGE_FULL, &val); + if (!rc) + chg->batt_info.batt_full_uah = val.intval; + rc = power_supply_get_property(chg->fg_psy, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, &val); + if (!rc) + chg->batt_info.batt_design_uah = val.intval; + rc = power_supply_get_property(chg->fg_psy, POWER_SUPPLY_PROP_CHARGE_COUNTER, &val); + if (!rc) + chg->batt_info.batt_chg_counter = val.intval; + + if (chg->chg_cfg.full_charged) + chg->batt_info.batt_status = POWER_SUPPLY_STATUS_FULL; + + if (!chg->sgm->state.online) { + chg->batt_info.batt_status = POWER_SUPPLY_STATUS_DISCHARGING; + } + + memcpy(batt_info, &chg->batt_info, sizeof(struct mmi_battery_info)); + if (chg->paired_batt_info.batt_soc == 0 && + chg->paired_batt_info.batt_mv < EMPTY_BATTERY_VBAT) { + batt_info->batt_soc = 0; + pr_warn("Force %s to empty from %d for empty paired battery\n", + chg->fg_psy->desc->name, + chg->batt_info.batt_soc); + } + + return rc; +} + +static int sgm4154x_charger_get_chg_info(void *data, struct mmi_charger_info *chg_info) +{ + int ret; + struct sgm_mmi_charger *chg = data; + struct sgm4154x_state state = chg->sgm->state; + + sgm4154x_get_state(chg->sgm, &state); + + chg->chg_info.chrg_mv = state.vbus_adc / 1000; + chg->chg_info.chrg_ma = state.ibus_adc / 1000; + chg->chg_info.chrg_type = get_charger_type(chg->sgm); + chg->chg_info.vbus_present = state.vbus_gd; + if (chg->chg_info.chrg_type == POWER_SUPPLY_USB_TYPE_UNKNOWN && + is_wls_online(chg->sgm)) { + chg->chg_info.chrg_type = SGM4154x_WLS_TYPE; + chg->chg_info.vbus_present = false; + } + chg->chg_info.chrg_present = state.online; + + if (state.ibus_limit > 0) { + ret = sgm4154x_get_input_curr_lim(chg->sgm); + if (ret < 0) { + chg->chg_info.chrg_pmax_mw = 0; + } else { + chg->chg_info.chrg_pmax_mw = (ret / 1000) * + (state.vbus_adc / 1000) / 1000; + } + } else { + chg->chg_info.chrg_pmax_mw = 0; + } + + memcpy(chg_info, &chg->chg_info, sizeof(struct mmi_charger_info)); + + return 0; +} + +static int sgm4154x_charger_config_charge(void *data, struct mmi_charger_cfg *config) +{ + int rc; + u32 value; + bool chg_en; + static bool prev_chg_en = false; + bool cfg_changed = false; + struct sgm_mmi_charger *chg = data; + struct sgm4154x_state state = chg->sgm->state; + struct timespec64 now; + static struct timespec64 start = {0}; + uint32_t elapsed_ms; + bool high_load_en; + bool low_load_en; + static int prev_paired_load = PAIRED_LOAD_OFF; + int curr_in_limit = chg->sgm->ilim; + int volt_in_limit = chg->sgm->init_data.vlim; + int ichg_in_limit = chg->sgm->init_data.ichg; + + /* Monitor vbat and ibat for OVP and OCP */ + if (chg->batt_info.batt_mv >= chg->sgm->vbat_ovp_threshold) { + chg->paired_load = PAIRED_LOAD_HIGH; + chg->batt_protect_type = BATT_PROTECT_TYPE_OVP; + ktime_get_real_ts64(&start); + pr_err("ERROR: vbat OVP is triggered, batt_mv=%dmV, thre=%d\n", + chg->batt_info.batt_mv, + chg->sgm->vbat_ovp_threshold); + } else if (chg->batt_info.batt_ma >= chg->sgm->ibat_occp_threshold) { + chg->paired_load = PAIRED_LOAD_HIGH; + chg->batt_protect_type = BATT_PROTECT_TYPE_OCCP; + ktime_get_real_ts64(&start); + pr_err("ERROR: ibat OCCP is triggered, batt_ma=%dmA, thre=%d\n", + chg->batt_info.batt_ma, + chg->sgm->ibat_occp_threshold); + } else if (chg->batt_info.batt_ma <= -chg->sgm->ibat_ocp_threshold) { + chg->paired_load = PAIRED_LOAD_LOW; + chg->batt_protect_type = BATT_PROTECT_TYPE_OCP; + ktime_get_real_ts64(&start); + pr_err("ERROR: ibat OCP is triggered, batt_ma=%dmA, thre=%d\n", + chg->batt_info.batt_ma, + chg->sgm->ibat_ocp_threshold); + } else if (chg->batt_protect_type != BATT_PROTECT_TYPE_NONE) { + ktime_get_real_ts64(&now); + elapsed_ms = (now.tv_sec - start.tv_sec) * 1000; + elapsed_ms += (now.tv_nsec - start.tv_nsec) / 1000000; + if (elapsed_ms < chg->sgm->batt_protect_duration) { + if (!chg->chg_info.chrg_present && + chg->batt_protect_type == BATT_PROTECT_TYPE_OCP) { + chg->paired_load = PAIRED_LOAD_OFF; + } else if (chg->chg_info.chrg_present && + (chg->batt_protect_type == BATT_PROTECT_TYPE_OVP || + chg->batt_protect_type == BATT_PROTECT_TYPE_OCCP)) { + chg->paired_load = PAIRED_LOAD_HIGH; + sgm4154x_set_hiz_en(chg->sgm, true); + } else { + chg->batt_protect_type = BATT_PROTECT_TYPE_NONE; + } + } else { + chg->batt_protect_type = BATT_PROTECT_TYPE_NONE; + } + if (chg->batt_protect_type != BATT_PROTECT_TYPE_NONE) { + pr_warn("keep batt protection:%d, elapsed=%dms\n", + chg->batt_protect_type, elapsed_ms); + } else { + pr_warn("recover battery from protection\n"); + } + } + + switch (chg->paired_load) { + case PAIRED_LOAD_LOW: + high_load_en = false; + low_load_en = true; + break; + case PAIRED_LOAD_HIGH: + high_load_en = true; + low_load_en = true; + break; + case PAIRED_LOAD_OFF: + default: + high_load_en = false; + low_load_en = false; + } + if (chg->paired_load != prev_paired_load) { + if (gpio_is_valid(chg->sgm->high_load_en_gpio)) { + gpio_set_value(chg->sgm->high_load_en_gpio, + high_load_en ^ chg->sgm->high_load_active_low); + } + if (gpio_is_valid(chg->sgm->low_load_en_gpio)) { + gpio_set_value(chg->sgm->low_load_en_gpio, + low_load_en ^ chg->sgm->low_load_active_low); + } + prev_paired_load = chg->paired_load; + } + + if (!chg->sgm->batt_present || chg->sgm->otg_enabled || + chg->batt_protect_type == BATT_PROTECT_TYPE_OVP || + chg->batt_protect_type == BATT_PROTECT_TYPE_OCCP) { + if (chg->sgm->client->irq && chg->sgm->irq_enabled) { + cfg_changed = true; + disable_irq_wake(chg->sgm->client->irq); + disable_irq(chg->sgm->client->irq); + chg->sgm->irq_enabled = false; + pr_warn("irq is disabled, bpd=%d, otg=%d, protect_type=%d\n", + chg->sgm->batt_present, chg->sgm->otg_enabled, + chg->batt_protect_type); + } + config->charging_disable = true; + config->charger_suspend = true; + chg->chg_cfg.charger_suspend = false; + } else { + if (!chg->sgm->irq_enabled && chg->sgm->client->irq) { + cfg_changed = true; + enable_irq_wake(chg->sgm->client->irq); + enable_irq(chg->sgm->client->irq); + chg->sgm->irq_enabled = true; + pr_warn("irq is enabled, bpd=%d, otg=%d, protect_type=%d\n", + chg->sgm->batt_present, chg->sgm->otg_enabled, + chg->batt_protect_type); + } + } + + /* force to update charger configuration for user input or chip reset */ + if (chg->sgm->update_pending && state.input_ready) { + chg->sgm->update_pending = false; + chg->sgm->ilim = UINT_MAX; + chg->chg_cfg.target_fv = -EINVAL; + chg->chg_cfg.target_fcc = -EINVAL; + chg->chg_cfg.chrg_iterm = -EINVAL; + chg->chg_cfg.charging_disable = !config->charging_disable; + chg->chg_cfg.charger_suspend = !config->charger_suspend; + } + + /* configure the charger if changed */ + if (!config->charging_disable && + !config->charger_suspend && + config->target_fv != chg->chg_cfg.target_fv) { + value = config->target_fv * 1000; + rc = sgm4154x_set_chrg_volt(chg->sgm, value); + if (!rc) { + cfg_changed = true; + chg->chg_cfg.target_fv = config->target_fv; + } + } + if (!config->charging_disable && + !config->charger_suspend && + config->target_fcc != chg->chg_cfg.target_fcc) { + value = config->target_fcc * 1000; + rc = sgm4154x_set_ichrg_curr(chg->sgm, value); + if (!rc) { + cfg_changed = true; + chg->chg_cfg.target_fcc = config->target_fcc; + } + } + if (config->charger_suspend != chg->chg_cfg.charger_suspend) { + rc = sgm4154x_set_hiz_en(chg->sgm, config->charger_suspend); + if (!rc) { + cfg_changed = true; + chg->chg_cfg.charger_suspend = config->charger_suspend; + } + } + + sgm4154x_get_state(chg->sgm, &state); + + if (chg->chg_info.chrg_present && !state.hiz_en && + chg->sgm->use_ext_usb_psy && chg->sgm->usb && + chg->chg_info.chrg_type != SGM4154x_WLS_TYPE) { + if (state.ibus_limit < DEF_USB_CURRENT_MA && + state.ibus_adc < DEF_USB_CURRENT_MA) { + curr_in_limit = DEF_USB_CURRENT_MA - state.ibus_adc; + } else if (state.ibus_limit > state.ibus_adc) { + curr_in_limit = state.ibus_limit - state.ibus_adc; + } else { + curr_in_limit = 0; + } + if (curr_in_limit > chg->sgm->init_data.ilim) + curr_in_limit = chg->sgm->init_data.ilim; + } else if (chg->chg_info.chrg_present && chg->chg_info.chrg_type == SGM4154x_WLS_TYPE) { + if (state.vbus_adc < DEF_WLS_8W_VOLTAGE_UV) { + ichg_in_limit = MIN_VAL(chg->sgm->init_data.wls_ichg_ilim, chg->chg_cfg.target_fcc * 1000); + } else { + ichg_in_limit = MIN_VAL(chg->sgm->init_data.wls_8w_ichg_ilim, chg->chg_cfg.target_fcc * 1000); + } + if (chg->sgm->ichg != ichg_in_limit) { + rc = sgm4154x_set_ichrg_curr(chg->sgm, ichg_in_limit); + } + curr_in_limit = chg->sgm->init_data.wls_ilim; + volt_in_limit = chg->sgm->init_data.wls_vlim; + pr_debug("wireless is on, curr_in_limit=%d, volt_in_limit=%d, ichg_in_limit=%d, vbus_adc = %d\n", + curr_in_limit, volt_in_limit, ichg_in_limit, state.vbus_adc); + } else { + curr_in_limit = 0; + } + + if (chg->sgm->vlim != volt_in_limit) { + pr_info("volt_in_limit=%d\n", volt_in_limit); + sgm4154x_set_input_volt_lim(chg->sgm, volt_in_limit); + } + + if (chg->sgm->ilim != curr_in_limit) { + sgm4154x_set_input_curr_lim(chg->sgm, curr_in_limit); + pr_info("ibus_limit=%d, ibus_adc=%d, curr=%d\n", + state.ibus_limit, state.ibus_adc, curr_in_limit); + } + + if (config->charging_disable != chg->chg_cfg.charging_disable) { + value = config->charging_disable; + if (!value) + rc = sgm4154x_enable_charger(chg->sgm); + else + rc = sgm4154x_disable_charger(chg->sgm); + if (!rc) { + cfg_changed = true; + chg->chg_cfg.charging_disable = config->charging_disable; + } + } + + if (config->taper_kickoff != chg->chg_cfg.taper_kickoff) { + chg->chg_cfg.taper_kickoff = config->taper_kickoff; + chg->chrg_taper_cnt = 0; + } + + if (config->full_charged != chg->chg_cfg.full_charged) { + chg->chg_cfg.full_charged = config->full_charged; + } + + if (!config->charging_disable && + !config->charger_suspend && + config->chrg_iterm != chg->chg_cfg.chrg_iterm) { + value = config->chrg_iterm * 1000; + rc = sgm4154x_set_term_curr(chg->sgm, value); + if (!rc) { + cfg_changed = true; + chg->chg_cfg.chrg_iterm = config->chrg_iterm; + } + } + + if (!config->charging_disable && + !config->charger_suspend && + config->fg_iterm != chg->chg_cfg.fg_iterm) { + if (chg->fg_psy) { + union power_supply_propval val = {0}; + val.intval = config->fg_iterm * 1000; + rc = power_supply_set_property(chg->fg_psy, + POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, + &val); + if (!rc) { + cfg_changed = true; + chg->chg_cfg.fg_iterm = config->fg_iterm; + } + } else { + chg->chg_cfg.fg_iterm = config->fg_iterm; + } + } + + if (!config->charging_disable && + !config->charger_suspend && + config->charging_reset != chg->chg_cfg.charging_reset) { + if (config->charging_reset) { + sgm4154x_disable_charger(chg->sgm); + chg->chg_cfg.target_fv = -EINVAL; + chg->chg_cfg.target_fcc = -EINVAL; + chg->chg_cfg.charging_disable = true; + pr_info("Battery charging reset triggered\n"); + } + cfg_changed = true; + chg->chg_cfg.charging_reset = config->charging_reset; + } + + chg_en = sgm4154x_is_enabled_charging(chg->sgm); + if (!cfg_changed && + chg->chg_info.chrg_present && !state.hiz_en && + chg_en && prev_chg_en && chg->sgm->ichg > 0 && + ((chg->batt_info.batt_mv + 50) * 1000) <= chg->sgm->vreg && + state.chrg_stat != SGM4154x_FAST_CHRG && + state.chrg_stat != SGM4154x_PRECHRG) { + sgm4154x_disable_charger(chg->sgm); + chg->chg_cfg.target_fv = -EINVAL; + chg->chg_cfg.target_fcc = -EINVAL; + chg->chg_cfg.charging_disable = true; + pr_info("Battery charging reconfigure triggered\n"); + } + prev_chg_en = chg_en; + + if (cfg_changed) { + cancel_delayed_work(&chg->sgm->charge_monitor_work); + schedule_delayed_work(&chg->sgm->charge_monitor_work, + msecs_to_jiffies(200)); + } + + pr_info("cpd:%d, bpd:%d, otg:%d, protect:%d, load:%d, paired_ichg:%duA\n", + chg->chg_info.chrg_present, chg->sgm->batt_present, + chg->sgm->otg_enabled, chg->batt_protect_type, + chg->paired_load, chg->paired_ichg); + pr_info("chg_en:%d, ichg:%d, vchg:%d, ilim:%d, chg_st:0x%x, therm:%d, suspend:%d\n", + chg_en, chg->sgm->ichg, chg->sgm->vreg, chg->sgm->ilim, + state.chrg_stat, state.therm_stat, state.hiz_en); + + return 0; +} + +#define TAPER_COUNT 3 +static bool sgm4154x_charger_is_charge_tapered(void *data, int tapered_ma) +{ + bool is_tapered = false; + struct sgm_mmi_charger *chg = data; + + if (abs(chg->batt_info.batt_ma) <= tapered_ma) { + if (chg->chrg_taper_cnt >= TAPER_COUNT) { + is_tapered = true; + chg->chrg_taper_cnt = 0; + } else + chg->chrg_taper_cnt++; + } else + chg->chrg_taper_cnt = 0; + + return is_tapered; +} + +static bool sgm4154x_charger_is_charge_halt(void *data) +{ + int rc = 0; + int chrg_stat = 0; + bool chrg_halt = false; + struct sgm_mmi_charger *chg = data; + + rc = regmap_read(chg->sgm->regmap, SGM4154x_CHRG_STAT, &chrg_stat); + if (rc){ + pr_err("read SGM4154x_CHRG_STAT fail, rc=%d\n", rc); + return chrg_halt; + } + + chrg_stat = chrg_stat & SGM4154x_CHG_STAT_MASK; + switch(chrg_stat) { + case SGM4154x_NOT_CHRGING: + case SGM4154x_TERM_CHRG: + chrg_halt = true; + break; + case SGM4154x_PRECHRG: + case SGM4154x_FAST_CHRG: + default: + break; + } + + if (chg->batt_info.batt_status == POWER_SUPPLY_STATUS_NOT_CHARGING || + chg->batt_info.batt_status == POWER_SUPPLY_STATUS_FULL) + chrg_halt = true; + + return chrg_halt; +} + +static void sgm4154x_charger_set_constraint(void *data, + struct mmi_charger_constraint *constraint) +{ + struct sgm_mmi_charger *chg = data; + + if (constraint->demo_mode != chg->constraint.demo_mode) { + chg->constraint.demo_mode = constraint->demo_mode; + } + + if (constraint->factory_version != chg->constraint.factory_version) { + chg->constraint.factory_version = constraint->factory_version; + } + + if (constraint->factory_mode != chg->constraint.factory_mode) { + chg->constraint.factory_mode = constraint->factory_mode; + } + + if (constraint->dcp_pmax != chg->constraint.dcp_pmax) { + chg->constraint.dcp_pmax = constraint->dcp_pmax; + } + + if (constraint->hvdcp_pmax != chg->constraint.hvdcp_pmax) { + chg->constraint.hvdcp_pmax = constraint->hvdcp_pmax; + } + + if (constraint->pd_pmax != chg->constraint.pd_pmax) { + chg->constraint.pd_pmax = constraint->pd_pmax; + } + + if (constraint->wls_pmax != chg->constraint.wls_pmax) { + chg->constraint.wls_pmax = constraint->wls_pmax; + } +} + +/* Battery presence detection threshold on battery temperature */ +#define BPD_TEMP_THRE (-30) +#define PANIC_DEB_TIME_MAX (60000) +#define OTG_VBUS_MIN_MV (4600) +#define OTG_VBUS_MAX_MV (5600) +static void sgm4154x_paired_battery_notify(void *data, + struct mmi_battery_info *batt_info) +{ + int i; + int rc; + int batt_ocv; + int paired_ocv; + int delta_ocv; + int delta_soc; + bool batt_present; + union power_supply_propval val = {0}; + struct sgm_mmi_charger *chg = data; + int paired_ichg = chg->paired_ichg; + int paired_load = chg->paired_load; + struct timespec64 now = {0}; + static struct timespec64 start = {0}; + uint32_t elapsed_ms; + + if (!batt_info || batt_info->batt_mv <= 0) { + pr_warn("Invalid paired battery info\n"); + return; + } + + if (!chg->chg_info.chrg_present && + chg->chg_info.chrg_ma == 0 && + chg->chg_info.chrg_mv > OTG_VBUS_MIN_MV && + chg->chg_info.chrg_mv < OTG_VBUS_MAX_MV && + (chg->batt_info.batt_ma < 0 || batt_info->batt_ma < 0)) { + chg->sgm->otg_enabled = true; + } else { + chg->sgm->otg_enabled = false; + } + + memcpy(&chg->paired_batt_info, batt_info, sizeof(struct mmi_battery_info)); + batt_present = (chg->batt_info.batt_temp >= BPD_TEMP_THRE)? true : false; + if (batt_present) { + rc = power_supply_get_property(chg->fg_psy, + POWER_SUPPLY_PROP_PRESENT, &val); + if (!rc && !val.intval) + batt_present = false; + } + if (!batt_present) { + chg->paired_ichg = 0; + chg->paired_load = PAIRED_LOAD_OFF; + return; + } else if (!chg->sgm->batt_present) { + chg->paired_ichg = chg->sgm->init_data.max_ichg; + chg->sgm->batt_present = batt_present; + pr_info("Battery present!\n"); + } + + batt_ocv = chg->batt_info.batt_mv; + batt_ocv -= (chg->batt_info.batt_ma * chg->batt_esr) / 1000; + paired_ocv = batt_info->batt_mv; + paired_ocv -= (batt_info->batt_ma * chg->paired_esr) / 1000; + + delta_ocv = batt_ocv - paired_ocv; + delta_soc = chg->batt_info.batt_soc - batt_info->batt_soc; + + if (chg->paired_ichg_table && chg->paired_ichg_table_len) { + paired_ichg = 0; + for (i = 0; i < chg->paired_ichg_table_len; i++) { + if (delta_ocv <= chg->paired_ichg_table[i * 2]) { + paired_ichg = chg->paired_ichg_table[i * 2 + 1]; + break; + } + } + paired_ichg *= 1000; + if (!chg->constraint.factory_mode && + chg->paired_ichg != paired_ichg) { + chg->paired_ichg = paired_ichg; + chg->chg_cfg.target_fcc = -EINVAL; + } + } + + if (chg->paired_load_thres && chg->paired_load_thres_len) { + paired_load = PAIRED_LOAD_HIGH; + for (i = 0; i < chg->paired_load_thres_len; i++) { + if (delta_ocv > chg->paired_load_thres[i]) { + paired_load = i; + break; + } + } + if (paired_load == PAIRED_LOAD_OFF) { + if (start.tv_sec == 0 && start.tv_nsec == 0) { + ktime_get_real_ts64(&start); + elapsed_ms = 0; + } else { + ktime_get_real_ts64(&now); + if (now.tv_sec <= start.tv_sec) + start = now; + elapsed_ms = (now.tv_sec - start.tv_sec) * 1000; + elapsed_ms += (now.tv_nsec - start.tv_nsec) / 1000000; + } + if (paired_vbat_panic_enabled && + elapsed_ms >= PANIC_DEB_TIME_MAX) { + panic("ERROR: delta_ocv=%dmV, delta_soc=%d", + delta_ocv, delta_soc); + } else if (elapsed_ms >= PANIC_DEB_TIME_MAX) { + pr_err("ERROR: delta_ocv=%dmV, delta_soc=%d", + delta_ocv, delta_soc); + } + } else { + start.tv_sec = 0; + start.tv_nsec = 0; + } + chg->paired_load = paired_load; + } + + /* Turn off load switch to stop charging by paired battery */ + if (delta_ocv < 0 || + (chg->chg_info.chrg_present && + chg->chg_info.chrg_ma > 0 && + batt_info->batt_ma >= 0)) { + chg->paired_load = PAIRED_LOAD_OFF; + } + + pr_info("paired_ocv:%dmV, batt_ocv:%dmV, delta_ocv:%dmV, delta_soc:%d\n", + paired_ocv, batt_ocv, delta_ocv, delta_soc); +} + +static int sgm4154x_mmi_charger_init(struct sgm_mmi_charger *chg) +{ + int rc; + int i; + int byte_len; + const char *df_sn = NULL; + struct mmi_charger_driver *driver; + + if (!chg->fg_psy && !chg->fg_psy_name) { + chg->fg_psy_name = "fg_battery"; + device_property_read_string(chg->sgm->dev, + "mmi,fg-psy-name", + &chg->fg_psy_name); + } + + if (!chg->fg_psy) { + chg->fg_psy = power_supply_get_by_name(chg->fg_psy_name); + if (!chg->fg_psy) { + pr_err("No %s power supply found\n", chg->fg_psy_name); + return -ENODEV; + } + pr_info("%s power supply is found\n", chg->fg_psy_name); + } + + if (chg->driver) { + pr_info("sgm4154x_mmi_charger has already registered\n"); + return 0; + } + + rc = device_property_read_string(chg->sgm->dev, + "mmi,df-serialnum", + &df_sn); + if (!rc && df_sn) { + pr_info("Default Serial Number %s\n", df_sn); + } else { + pr_err("No Default Serial Number defined\n"); + df_sn = "unknown-sn"; + } + strcpy(chg->batt_info.batt_sn, df_sn); + chg->paired_batt_info.batt_soc = -EINVAL; + chg->chg_cfg.target_fcc = chg->sgm->init_data.ichg / 1000; + chg->chg_cfg.charging_disable = chg->sgm->init_data.charger_disabled; + chg->chg_cfg.charger_suspend = chg->sgm->init_data.charger_disabled; + + if (of_property_read_bool(chg->sgm->dev->of_node, "mmi,ichg-invert-polority")) + chg->ichg_polority = -1; + else + chg->ichg_polority = 1; + chg->paired_ichg = chg->sgm->init_data.max_ichg; + chg->paired_load = PAIRED_LOAD_OFF; + if (!chg->paired_ichg_table && + of_find_property(chg->sgm->dev->of_node, "mmi,paired-ichg-table", &byte_len)) { + if ((byte_len / sizeof(u32)) % 2) { + pr_err("DT error wrong paired-ichg-table\n"); + return -ENODEV; + } + + chg->paired_ichg_table = (int *)devm_kzalloc(chg->sgm->dev, byte_len, GFP_KERNEL); + if (chg->paired_ichg_table == NULL) + return -ENOMEM; + + chg->paired_ichg_table_len = byte_len / sizeof(u32) / 2; + rc = of_property_read_u32_array(chg->sgm->dev->of_node, + "mmi,paired-ichg-table", + (u32 *)chg->paired_ichg_table, + byte_len / sizeof(u32)); + if (rc < 0) { + pr_err("Couldn't read paired-ichg-table rc = %d\n", rc); + devm_kfree(chg->sgm->dev, chg->paired_ichg_table); + chg->paired_ichg_table = NULL; + return rc; + } + + pr_info("paired-ichg-table: len: %d\n", chg->paired_ichg_table_len); + for (i = 0; i < chg->paired_ichg_table_len; i++) { + pr_info("paired-ichg-table[%d]: delta_soc %d, fcc %dmA\n", i, + chg->paired_ichg_table[i * 2], + chg->paired_ichg_table[i * 2 + 1]); + } + } + + if (!chg->paired_load_thres && + of_find_property(chg->sgm->dev->of_node, "mmi,paired-load-thres", &byte_len)) { + chg->paired_load_thres = (int *)devm_kzalloc(chg->sgm->dev, byte_len, GFP_KERNEL); + if (chg->paired_load_thres == NULL) + return -ENOMEM; + + chg->paired_load_thres_len = byte_len / sizeof(u32); + rc = of_property_read_u32_array(chg->sgm->dev->of_node, + "mmi,paired-load-thres", + (u32 *)chg->paired_load_thres, + byte_len / sizeof(u32)); + if (rc < 0) { + pr_err("Couldn't read paired-load-thres rc = %d\n", rc); + devm_kfree(chg->sgm->dev, chg->paired_load_thres); + chg->paired_load_thres = NULL; + return rc; + } + + pr_info("paired-load-thres: len: %d\n", chg->paired_load_thres_len); + for (i = 0; i < chg->paired_load_thres_len; i++) { + pr_info("paired-load-thres[%d]: %d\n", + i, chg->paired_load_thres[i]); + } + } + + rc = of_property_read_u32(chg->sgm->dev->of_node, + "mmi,paired-esr", + &chg->paired_esr); + if (rc) + chg->paired_esr = 60; + pr_info("paired battery ESR: %d\n", chg->paired_esr); + + rc = of_property_read_u32(chg->sgm->dev->of_node, + "mmi,batt-esr", + &chg->batt_esr); + if (rc) + chg->batt_esr = 40; + pr_info("battery ESR: %d\n", chg->batt_esr); + + driver = devm_kzalloc(chg->sgm->dev, + sizeof(struct mmi_charger_driver), + GFP_KERNEL); + if (!driver) + return -ENOMEM; + + /* init driver */ + driver->name = SGM4154x_NAME; + driver->dev = chg->sgm->dev; + driver->data = chg; + driver->get_batt_info = sgm4154x_charger_get_batt_info; + driver->get_chg_info = sgm4154x_charger_get_chg_info; + driver->config_charge = sgm4154x_charger_config_charge; + driver->is_charge_tapered = sgm4154x_charger_is_charge_tapered; + driver->is_charge_halt = sgm4154x_charger_is_charge_halt; + driver->set_constraint = sgm4154x_charger_set_constraint; + driver->notify_paired_battery = sgm4154x_paired_battery_notify; + chg->driver = driver; + + /* register driver to mmi charger */ + rc = mmi_register_charger_driver(driver); + if (rc) { + pr_err("sgm4154x_mmi_charger init failed, rc=%d\n", rc); + } else { + pr_info("sgm4154x_mmi_charger init successfully\n"); + } + + return rc; +} + +static void sgm4154x_mmi_charger_deinit(struct sgm_mmi_charger *chg) +{ + int rc; + + if (!chg->driver) { + pr_info("sgm4154x_mmi_charger has not inited yet\n"); + return; + } + + /* unregister driver from mmi charger */ + rc = mmi_unregister_charger_driver(chg->driver); + if (rc) { + pr_err("sgm4154x_mmi_charger deinit failed, rc=%d\n", rc); + } else { + devm_kfree(chg->sgm->dev, chg->driver); + chg->driver = NULL; + } + + if (chg->fg_psy) { + power_supply_put(chg->fg_psy); + chg->fg_psy = NULL; + } +} + +static int sgm4154x_psy_notifier_call(struct notifier_block *nb, + unsigned long val, void *v) +{ + struct sgm4154x_device *sgm = container_of(nb, + struct sgm4154x_device, psy_nb); + struct power_supply *psy = v; + struct sgm_mmi_charger *chg = NULL; + + if (!sgm) { + pr_err("called before sgm valid!\n"); + return NOTIFY_DONE; + } + + if (!psy || val != PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + chg = sgm->mmi_charger; + if ((sgm->usb && !strcmp(psy->desc->name, sgm->usb->desc->name)) || + (chg && chg->fg_psy_name && !strcmp(psy->desc->name, chg->fg_psy_name)) || + (sgm->wls_psy && !strcmp(psy->desc->name, sgm->wls_psy->desc->name))) { + cancel_delayed_work(&sgm->charge_detect_delayed_work); + schedule_delayed_work(&sgm->charge_detect_delayed_work, + msecs_to_jiffies(0)); + } + + return NOTIFY_OK; +} + +static int sgm4154x_show_registers(struct seq_file *m, void *data) +{ + int i; + int ret; + u32 reg = 0; + struct sgm4154x_device *sgm = m->private; + + for (i = 0; i <= SGM4154x_CHRG_CTRL_f; i++) { + ret = regmap_read(sgm->regmap, i, ®); + if (!ret) + seq_printf(m, "Reg[%02X] = 0x%02X\n", i, reg); + } + + return 0; +} + +static int sgm4154x_reg_debugfs_open(struct inode *inode, struct file *file) +{ + struct sgm4154x_device *sgm = inode->i_private; + + return single_open(file, sgm4154x_show_registers, sgm); +} + +static const struct file_operations sgm4154x_reg_debugfs_ops = { + .owner = THIS_MODULE, + .open = sgm4154x_reg_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void create_debugfs_entry(struct sgm4154x_device *sgm) +{ + sgm->debug_root = debugfs_create_dir("sgm4154x", NULL); + if (!sgm->debug_root) { + pr_err("Failed to create debug dir\n"); + return; + } + + debugfs_create_file("registers", S_IFREG | S_IRUGO, + sgm->debug_root, sgm, + &sgm4154x_reg_debugfs_ops); +} + +static ssize_t chg_en_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + int enable; + struct sgm4154x_device *sgm = dev_get_drvdata(dev); + + if (!sgm) { + pr_err("sgm chip not valid\n"); + return -ENODEV; + } + + ret = kstrtoint(buf, 0, &enable); + if (ret) { + pr_err("Invalid chg_en value, ret = %d\n", ret); + return ret; + } + + if (enable < 0) { + sgm->update_pending = true; + sgm->user_chg_en = -EINVAL; + pr_info("Clear user enable charging setting\n"); + } else { + sgm->user_chg_en = !!enable; + pr_info("Set user enable charging: %d\n", sgm->user_chg_en); + if (sgm->user_chg_en) + sgm4154x_enable_charger(sgm); + else + sgm4154x_disable_charger(sgm); + } + cancel_delayed_work(&sgm->charge_monitor_work); + schedule_delayed_work(&sgm->charge_monitor_work, + msecs_to_jiffies(200)); + + return count; +} + +static ssize_t chg_en_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + bool enable; + struct sgm4154x_device *sgm = dev_get_drvdata(dev); + + if (!sgm) { + pr_err("sgm chip not valid\n"); + return -ENODEV; + } + + enable = sgm4154x_is_enabled_charging(sgm); + + return sprintf(buf, "%d\n", enable); +} +DEVICE_ATTR_RW(chg_en); + +static ssize_t high_load_en_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + bool enable; + struct sgm4154x_device *sgm = dev_get_drvdata(dev); + + if (!sgm) { + pr_err("sgm chip not valid\n"); + return -ENODEV; + } + + ret = kstrtobool(buf, &enable); + if (ret) { + pr_err("Invalid high_load_en value, ret = %d\n", ret); + return ret; + } + + if (gpio_is_valid(sgm->high_load_en_gpio)) { + gpio_set_value(sgm->high_load_en_gpio, + enable ^ sgm->high_load_active_low); + cancel_delayed_work(&sgm->charge_monitor_work); + schedule_delayed_work(&sgm->charge_monitor_work, + msecs_to_jiffies(200)); + } + + return count; +} + +static ssize_t high_load_en_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + bool enable; + struct sgm4154x_device *sgm = dev_get_drvdata(dev); + + if (!sgm) { + pr_err("sgm chip not valid\n"); + return -ENODEV; + } + + if (gpio_is_valid(sgm->high_load_en_gpio)) + enable = sgm->high_load_active_low + ^ gpio_get_value(sgm->high_load_en_gpio); + else + enable = false; + + return sprintf(buf, "%d\n", enable); +} +DEVICE_ATTR_RW(high_load_en); + +static ssize_t low_load_en_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + bool enable; + struct sgm4154x_device *sgm = dev_get_drvdata(dev); + + if (!sgm) { + pr_err("sgm chip not valid\n"); + return -ENODEV; + } + + ret = kstrtobool(buf, &enable); + if (ret) { + pr_err("Invalid low_load_en value, ret = %d\n", ret); + return ret; + } + + if (gpio_is_valid(sgm->low_load_en_gpio)) { + gpio_set_value(sgm->low_load_en_gpio, + enable ^ sgm->low_load_active_low); + cancel_delayed_work(&sgm->charge_monitor_work); + schedule_delayed_work(&sgm->charge_monitor_work, + msecs_to_jiffies(200)); + } + + return count; +} + +static ssize_t low_load_en_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + bool enable; + struct sgm4154x_device *sgm = dev_get_drvdata(dev); + + if (!sgm) { + pr_err("sgm chip not valid\n"); + return -ENODEV; + } + + if (gpio_is_valid(sgm->low_load_en_gpio)) + enable = gpio_get_value(sgm->low_load_en_gpio) + ^ sgm->low_load_active_low; + else + enable = false; + + return sprintf(buf, "%d\n", enable); +} +DEVICE_ATTR_RW(low_load_en); + +static ssize_t charger_suspend_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + int chg_suspend; + struct sgm4154x_device *sgm = dev_get_drvdata(dev); + + if (!sgm) { + pr_err("sgm chip not valid\n"); + return -ENODEV; + } + + ret = kstrtoint(buf, 0, &chg_suspend); + if (ret) { + pr_err("Invalid chg_suspend value, ret = %d\n", ret); + return ret; + } + + if (chg_suspend < 0) { + sgm->update_pending = true; + sgm->user_chg_susp = -EINVAL; + pr_info("Clear user suspend charger setting\n"); + } else { + sgm->user_chg_susp = !!chg_suspend; + pr_info("Set user suspend charger: %d\n", sgm->user_chg_susp); + sgm4154x_set_hiz_en(sgm, sgm->user_chg_susp); + } + cancel_delayed_work(&sgm->charge_monitor_work); + schedule_delayed_work(&sgm->charge_monitor_work, + msecs_to_jiffies(200)); + + return count; +} + +static ssize_t charger_suspend_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sgm4154x_device *sgm = dev_get_drvdata(dev); + + if (!sgm) { + pr_err("sgm chip not valid\n"); + return -ENODEV; + } + + return sprintf(buf, "%d\n", sgm->state.hiz_en); +} +DEVICE_ATTR_RW(charger_suspend); + +static int sgm4154x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct sgm4154x_device *sgm; + int ret; + int otg_notify; + char *name = NULL; + + sgm = devm_kzalloc(dev, sizeof(*sgm), GFP_KERNEL); + if (!sgm) + return -ENOMEM; + + sgm->client = client; + sgm->dev = dev; + sgm->user_ilim = -EINVAL; + sgm->user_ichg = -EINVAL; + sgm->user_chg_en = -EINVAL; + sgm->user_chg_susp = -EINVAL; + + mutex_init(&sgm->lock); + + strncpy(sgm->model_name, id->name, I2C_NAME_SIZE); + + sgm->regmap = devm_regmap_init_i2c(client, &sgm4154x_regmap_config); + if (IS_ERR(sgm->regmap)) { + dev_err(dev, "Failed to allocate register map\n"); + return PTR_ERR(sgm->regmap); + } + + i2c_set_clientdata(client, sgm); + + // Customer customization + ret = sgm4154x_parse_dt(sgm); + if (ret) { + dev_err(dev, "Failed to read device tree properties%d\n", ret); + return ret; + } + + if (IS_ERR_OR_NULL(sgm->vdd_i2c_vreg)) { + dev_err(dev, "Could not get vdd-i2c power regulator\n"); + } else { + ret = regulator_enable(sgm->vdd_i2c_vreg); + if (ret) + dev_err(dev, "Could not enable vdd-i2c power regulator\n"); + } + + ret = sgm4154x_hw_chipid_detect(sgm); + if ((ret & SGM4154x_PN_MASK) != SGM4154x_PN_41542_ID){ + pr_info("device not found !!!\n"); + return ret; + } + + name = devm_kasprintf(sgm->dev, GFP_KERNEL, "%s", + "sgm4154x suspend wakelock"); + sgm->charger_wakelock = + wakeup_source_register(NULL, name); + + /* OTG reporting */ + sgm->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + if (!IS_ERR_OR_NULL(sgm->usb2_phy)) { + INIT_WORK(&sgm->usb_work, sgm4154x_usb_work); + sgm->usb_nb.notifier_call = sgm4154x_usb_notifier; + otg_notify = usb_register_notifier(sgm->usb2_phy, &sgm->usb_nb); + } + + sgm->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3); + if (!IS_ERR_OR_NULL(sgm->usb3_phy)) { + INIT_WORK(&sgm->usb_work, sgm4154x_usb_work); + sgm->usb_nb.notifier_call = sgm4154x_usb_notifier; + otg_notify = usb_register_notifier(sgm->usb3_phy, &sgm->usb_nb); + } + + INIT_DELAYED_WORK(&sgm->charge_detect_delayed_work, charger_detect_work_func); + INIT_DELAYED_WORK(&sgm->charge_monitor_work, charger_monitor_work_func); + + if (client->irq) { + ret = devm_request_threaded_irq(dev, client->irq, NULL, + sgm4154x_irq_handler_thread, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + dev_name(&client->dev), sgm); + if (ret) + goto error_out; + enable_irq_wake(client->irq); + sgm->irq_enabled = true; + } + + sgm->pm_nb.notifier_call = sgm4154x_suspend_notifier; + register_pm_notifier(&sgm->pm_nb); + sgm->psy_nb.notifier_call = sgm4154x_psy_notifier_call; + ret = power_supply_reg_notifier(&sgm->psy_nb); + if (ret) + pr_err("Failed to reg power supply notifier: %d\n", ret); + + ret = sgm4154x_power_supply_init(sgm, dev); + if (ret) { + dev_err(dev, "Failed to register power supply, ret=%d\n", ret); + goto error_out; + } + + ret = sgm4154x_hw_init(sgm); + if (ret) { + dev_err(dev, "Cannot initialize the chip.\n"); + goto error_out; + } + + //OTG setting + sgm4154x_set_otg_voltage(sgm, 5000000); //5V + sgm4154x_set_otg_current(sgm, 1200000); //1.2A + + ret = sgm4154x_vbus_regulator_register(sgm); + + schedule_delayed_work(&sgm->charge_monitor_work,100); + + sgm->mmi_charger = devm_kzalloc(dev, sizeof(*sgm->mmi_charger), GFP_KERNEL); + if (sgm->mmi_charger) { + sgm->mmi_charger->sgm = sgm; + sgm4154x_mmi_charger_init(sgm->mmi_charger); + } + + ret = device_create_file(sgm->dev, &dev_attr_charger_suspend); + if (ret) + pr_err("Couldn't create charger_suspend\n"); + + ret = device_create_file(sgm->dev, &dev_attr_chg_en); + if (ret) + pr_err("Couldn't create chg_en\n"); + + ret = device_create_file(sgm->dev, &dev_attr_high_load_en); + if (ret) + pr_err("Couldn't create high_load_en\n"); + + ret = device_create_file(sgm->dev, &dev_attr_low_load_en); + if (ret) + pr_err("Couldn't create low_load_en\n"); + + create_debugfs_entry(sgm); + + dev_info(dev, "%s probe successfully.\n", SGM4154x_NAME); + return 0; +error_out: + if (!IS_ERR_OR_NULL(sgm->usb2_phy)) + usb_unregister_notifier(sgm->usb2_phy, &sgm->usb_nb); + + if (!IS_ERR_OR_NULL(sgm->usb3_phy)) + usb_unregister_notifier(sgm->usb3_phy, &sgm->usb_nb); + return ret; +} + +static int sgm4154x_charger_remove(struct i2c_client *client) +{ + struct sgm4154x_device *sgm = i2c_get_clientdata(client); + + device_remove_file(sgm->dev, &dev_attr_high_load_en); + device_remove_file(sgm->dev, &dev_attr_low_load_en); + device_remove_file(sgm->dev, &dev_attr_chg_en); + device_remove_file(sgm->dev, &dev_attr_charger_suspend); + debugfs_remove_recursive(sgm->debug_root); + + cancel_delayed_work_sync(&sgm->charge_monitor_work); + + if (sgm->mmi_charger) + sgm4154x_mmi_charger_deinit(sgm->mmi_charger); + if (sgm->usb) { + power_supply_put(sgm->usb); + sgm->usb = NULL; + } + + if (!IS_ERR_OR_NULL(sgm->usb2_phy)) + usb_unregister_notifier(sgm->usb2_phy, &sgm->usb_nb); + + if (!IS_ERR_OR_NULL(sgm->usb3_phy)) + usb_unregister_notifier(sgm->usb3_phy, &sgm->usb_nb); + + power_supply_unreg_notifier(&sgm->psy_nb); + regulator_unregister(sgm->otg_rdev); + + power_supply_unregister(sgm->charger); + + if (!IS_ERR_OR_NULL(sgm->vdd_i2c_vreg)) { + if (regulator_is_enabled(sgm->vdd_i2c_vreg)) + regulator_disable(sgm->vdd_i2c_vreg); + devm_regulator_put(sgm->vdd_i2c_vreg); + } + + if (gpio_is_valid(sgm->high_load_en_gpio)) + gpio_free(sgm->high_load_en_gpio); + if (gpio_is_valid(sgm->low_load_en_gpio)) + gpio_free(sgm->low_load_en_gpio); + if (gpio_is_valid(sgm->chg_en_gpio)) + gpio_free(sgm->chg_en_gpio); + + mutex_destroy(&sgm->lock); + + return 0; +} + +static void sgm4154x_charger_shutdown(struct i2c_client *client) +{ + int ret = 0; + struct sgm4154x_device *sgm = i2c_get_clientdata(client); + ret = sgm4154x_disable_charger(sgm); + if (ret) { + pr_err("Failed to disable charger, ret = %d\n", ret); + } + pr_info("sgm4154x_charger_shutdown\n"); +} + +static const struct i2c_device_id sgm4154x_i2c_ids[] = { + { "sgm41541", 0 }, + { "sgm41542", 0 }, + { "sgm41516", 0 }, + { "sgm41516D", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, sgm4154x_i2c_ids); + +static const struct of_device_id sgm4154x_of_match[] = { + { .compatible = "sgm,sgm41541" }, + { .compatible = "sgm,sgm41542" }, + { .compatible = "sgm,sgm41516" }, + { .compatible = "sgm,sgm41516D" }, + { }, +}; +MODULE_DEVICE_TABLE(of, sgm4154x_of_match); + +static struct i2c_driver sgm4154x_driver = { + .driver = { + .name = "sgm4154x-charger", + .of_match_table = sgm4154x_of_match, + }, + .probe = sgm4154x_probe, + .remove = sgm4154x_charger_remove, + .shutdown = sgm4154x_charger_shutdown, + .id_table = sgm4154x_i2c_ids, +}; +module_i2c_driver(sgm4154x_driver); + +MODULE_AUTHOR(" qhq "); +MODULE_DESCRIPTION("sgm4154x charger driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/sgm4154x_charger_lite/sgm4154x_charger_lite.h b/drivers/power/sgm4154x_charger_lite/sgm4154x_charger_lite.h new file mode 100644 index 000000000000..f9e84e62bf2a --- /dev/null +++ b/drivers/power/sgm4154x_charger_lite/sgm4154x_charger_lite.h @@ -0,0 +1,387 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +// sgm4154x Charger Driver +// Copyright (C) 2021 Texas Instruments Incorporated - http://www.sg-micro.com + +#ifndef _SGM4154x_CHARGER_H +#define _SGM4154x_CHARGER_H + +#include + +#include "mmi_charger.h" + +#define SGM4154x_MANUFACTURER "Texas Instruments" + +//#define __SGM41541_CHIP_ID__ +//#define __SGM41542_CHIP_ID__ +//#define __SGM41516_CHIP_ID__ +//#define __SGM41516D_CHIP_ID__ + +#ifdef __SGM41541_CHIP_ID__ +#define SGM4154x_NAME "sgm41541" +#endif + +#ifdef __SGM41542_CHIP_ID__ +#define SGM4154x_NAME "sgm41542" +#endif + +#ifdef __SGM41516_CHIP_ID__ +#define SGM4154x_NAME "sgm41516" +#endif + +#ifdef __SGM41516D_CHIP_ID__ +#define SGM4154x_NAME "sgm41516D" +#endif + +/*define register*/ +#define SGM4154x_CHRG_CTRL_0 0x00 +#define SGM4154x_CHRG_CTRL_1 0x01 +#define SGM4154x_CHRG_CTRL_2 0x02 +#define SGM4154x_CHRG_CTRL_3 0x03 +#define SGM4154x_CHRG_CTRL_4 0x04 +#define SGM4154x_CHRG_CTRL_5 0x05 +#define SGM4154x_CHRG_CTRL_6 0x06 +#define SGM4154x_CHRG_CTRL_7 0x07 +#define SGM4154x_CHRG_STAT 0x08 +#define SGM4154x_CHRG_FAULT 0x09 +#define SGM4154x_CHRG_CTRL_a 0x0a +#define SGM4154x_CHRG_CTRL_b 0x0b +#define SGM4154x_CHRG_CTRL_c 0x0c +#define SGM4154x_CHRG_CTRL_d 0x0d +#define SGM4154x_INPUT_DET 0x0e +#define SGM4154x_CHRG_CTRL_f 0x0f + +/*rerun apsd*/ +#define SGM4154x_IINDET_EN_MASK GENMASK(7, 7) +#define SGM4154x_IINDET_EN BIT(7) + +/* charge status flags */ +#define SGM4154x_IINDPM_INT_MASK GENMASK(0, 0) +#define SGM4154x_IINDPM_INT_DISABLE BIT(0) +#define SGM4154x_VINDPM_INT_MASK GENMASK(1, 1) +#define SGM4154x_VINDPM_INT_DISABLE BIT(1) +#define SGM4154x_CHRG_EN BIT(4) +#define SGM4154x_HIZ_EN BIT(7) +#define SGM4154x_TERM_EN BIT(7) +#define SGM4154x_VAC_OVP_MASK GENMASK(7, 6) +#define SGM4154x_DPDM_ONGOING BIT(7) +#define SGM4154x_VBUS_GOOD BIT(7) + +#define SGM4154X_BOOST_LIM_MASK GENMASK(7, 7) +#define SGM4154x_OTG_MASK GENMASK(5, 4) +#define SGM4154x_OTG_EN BIT(5) + +/* Part ID */ +#define SGM4154x_PN_MASK GENMASK(6, 3) +#define SGM4154x_PN_41541_ID (BIT(6)| BIT(5)) +#define SGM4154x_PN_41516_ID (BIT(6)| BIT(5)) +#define SGM4154x_PN_41542_ID (BIT(6)| BIT(5)| BIT(3)) +#define SGM4154x_PN_41516D_ID (BIT(6)| BIT(5)| BIT(3)) + +/* WDT TIMER SET */ +#define SGM4154x_WDT_TIMER_MASK GENMASK(5, 4) +#define SGM4154x_WDT_TIMER_DISABLE 0 +#define SGM4154x_WDT_TIMER_40S BIT(4) +#define SGM4154x_WDT_TIMER_80S BIT(5) +#define SGM4154x_WDT_TIMER_160S (BIT(4)| BIT(5)) + +#define SGM4154x_WDT_RST_MASK BIT(6) + +/*En termniataion*/ +#define SGM4154x_EN_TERM_MASK GENMASK(7,7) +#define SGM4154x_EN_TERM_DISABLE 0 +#define SGM4154x_EN_TERM_ENABLE BIT(7) + +/* SAFETY TIMER SET */ +#define SGM4154x_SAFETY_TIMER_MASK GENMASK(3, 3) +#define SGM4154x_SAFETY_TIMER_DISABLE 0 +#define SGM4154x_SAFETY_TIMER_EN BIT(3) +#define SGM4154x_SAFETY_TIMER_5H 0 +#define SGM4154x_SAFETY_TIMER_10H BIT(2) + +/* recharge voltage */ +#define SGM4154x_VRECHARGE BIT(0) +#define SGM4154x_VRECHRG_STEP_mV 100 +#define SGM4154x_VRECHRG_OFFSET_mV 100 + +/* charge status */ +#define SGM4154x_VSYS_STAT BIT(0) +#define SGM4154x_THERM_STAT BIT(1) +#define SGM4154x_PG_STAT BIT(2) +#define SGM4154x_CHG_STAT_MASK GENMASK(4, 3) +#define SGM4154x_PRECHRG BIT(3) +#define SGM4154x_FAST_CHRG BIT(4) +#define SGM4154x_TERM_CHRG (BIT(3)| BIT(4)) + +/* charge type */ +#define SGM4154x_VBUS_STAT_MASK GENMASK(7, 5) +#define SGM4154x_NOT_CHRGING 0 +#define SGM4154x_USB_SDP BIT(5) +#define SGM4154x_USB_CDP BIT(6) +#define SGM4154x_USB_DCP (BIT(5) | BIT(6)) +#define SGM4154x_UNKNOWN (BIT(7) | BIT(5)) +#define SGM4154x_NON_STANDARD (BIT(7) | BIT(6)) +#define SGM4154x_OTG_MODE (BIT(7) | BIT(6) | BIT(5)) + +#define SGM4154x_WLS_TYPE 0xFF + +/* TEMP Status */ +#define SGM4154x_TEMP_MASK GENMASK(2, 0) +#define SGM4154x_TEMP_NORMAL BIT(0) +#define SGM4154x_TEMP_WARM BIT(1) +#define SGM4154x_TEMP_COOL (BIT(0) | BIT(1)) +#define SGM4154x_TEMP_COLD (BIT(0) | BIT(3)) +#define SGM4154x_TEMP_HOT (BIT(2) | BIT(3)) + +/* precharge current */ +#define SGM4154x_PRECHRG_CUR_MASK GENMASK(7, 4) +#define SGM4154x_PRECHRG_CURRENT_STEP_uA 60000 +#define SGM4154x_PRECHRG_I_MIN_uA 60000 +#define SGM4154x_PRECHRG_I_MAX_uA 780000 +#define SGM4154x_PRECHRG_I_DEF_uA 180000 + +/* termination current */ +#define SGM4154x_TERMCHRG_CUR_MASK GENMASK(3, 0) +#define SGM4154x_TERMCHRG_CURRENT_STEP_uA 60000 +#define SGM4154x_TERMCHRG_I_MIN_uA 60000 +#define SGM4154x_TERMCHRG_I_MAX_uA 960000 +#define SGM4154x_TERMCHRG_I_DEF_uA 180000 + +/* charge current */ +#define SGM4154x_ICHRG_CUR_MASK GENMASK(5, 0) +#define SGM4154x_ICHRG_CURRENT_STEP_uA 60000 +#define SGM4154x_ICHRG_I_MIN_uA 0 +#define SGM4154x_ICHRG_I_MAX_uA 3780000 +#define SGM4154x_ICHRG_I_DEF_uA 2040000 + +/* charge voltage */ +#define SGM4154x_VREG_V_MASK GENMASK(7, 3) +#define SGM4154x_VREG_V_MAX_uV 4624000 +#define SGM4154x_VREG_V_MIN_uV 3856000 +#define SGM4154x_VREG_V_DEF_uV 4208000 +#define SGM4154x_VREG_V_STEP_uV 32000 + +/* VREG Fine Tuning */ +#define SGM4154x_VREG_FT_MASK GENMASK(7, 6) +#define SGM4154x_VREG_FT_UP_8mV BIT(6) +#define SGM4154x_VREG_FT_DN_8mV BIT(7) +#define SGM4154x_VREG_FT_DN_16mV (BIT(7) | BIT(6)) + +/* iindpm current */ +#define SGM4154x_IINDPM_I_MASK GENMASK(4, 0) +#define SGM4154x_IINDPM_I_MIN_uA 100000 +#define SGM4154x_IINDPM_I_MAX_uA 3800000 +#define SGM4154x_IINDPM_STEP_uA 100000 +#define SGM4154x_IINDPM_DEF_uA 2400000 + +/* vindpm voltage */ +#define SGM4154x_VINDPM_V_MASK GENMASK(3, 0) +#define SGM4154x_VINDPM_V_MIN_uV 3900000 +#define SGM4154x_VINDPM_V_MAX_uV 12000000 +#define SGM4154x_VINDPM_STEP_uV 100000 +#define SGM4154x_VINDPM_DEF_uV 4500000 +#define SGM4154x_VINDPM_OS_MASK GENMASK(1, 0) + +/* DP DM SEL */ +#define SGM4154x_DP_VSEL_MASK GENMASK(4, 3) +#define SGM4154x_DM_VSEL_MASK GENMASK(2, 1) + +/* PUMPX SET */ +#define SGM4154x_EN_PUMPX BIT(7) +#define SGM4154x_PUMPX_UP BIT(6) +#define SGM4154x_PUMPX_DN BIT(5) + +/* customer define jeita paramter */ +#define SGM4154x_JEITA_ENABLE_MASK GENMASK(0, 0) +#define SGM4154x_JEITA_ENABLE BIT(0) +#define SGM4154x_JEITA_DISABLE 0 +#define JEITA_TEMP_ABOVE_T4_CV 0 +#define JEITA_TEMP_T3_TO_T4_CV 4100000 +#define JEITA_TEMP_T2_TO_T3_CV 4350000 +#define JEITA_TEMP_T1_TO_T2_CV 4350000 +#define JEITA_TEMP_T0_TO_T1_CV 0 +#define JEITA_TEMP_BELOW_T0_CV 0 + +#define JEITA_TEMP_ABOVE_T4_CC_CURRENT 0 +#define JEITA_TEMP_T3_TO_T4_CC_CURRENT 1000000 +#define JEITA_TEMP_T2_TO_T3_CC_CURRENT 2400000 +#define JEITA_TEMP_T1_TO_T2_CC_CURRENT 2000000 +#define JEITA_TEMP_T0_TO_T1_CC_CURRENT 0 +#define JEITA_TEMP_BELOW_T0_CC_CURRENT 0 + +#define TEMP_T4_THRES 50 +#define TEMP_T4_THRES_MINUS_X_DEGREE 47 +#define TEMP_T3_THRES 45 +#define TEMP_T3_THRES_MINUS_X_DEGREE 39 +#define TEMP_T2_THRES 20 +#define TEMP_T2_THRES_PLUS_X_DEGREE 16 +#define TEMP_T1_THRES 0 +#define TEMP_T1_THRES_PLUS_X_DEGREE 6 +#define TEMP_T0_THRES 0 +#define TEMP_T0_THRES_PLUS_X_DEGREE 0 +#define TEMP_NEG_10_THRES 0 + +struct sgm4154x_init_data { + u32 ichg; /* charge current */ + u32 ilim; /* input current */ + u32 wls_ilim; /* wireless input current */ + u32 wls_ichg_ilim; /* wireless charge current(FCC) */ + u32 wls_8w_ichg_ilim; /* wireless 8W charge current(FCC) */ + u32 vreg; /* regulation voltage */ + u32 iterm; /* termination current */ + u32 iprechg; /* precharge current */ + u32 vlim; /* minimum input voltage limit for wired charger*/ + u32 wls_vlim; /* minimum input voltage limit for wireless charger*/ + u32 max_ichg; + u32 max_vreg; + u32 vrechg; + bool charger_disabled; +}; + +struct sgm4154x_state { + bool input_ready; + bool vsys_stat; + bool therm_stat; + bool online; + u8 chrg_stat; + u8 vbus_status; + + bool chrg_en; + bool hiz_en; + bool term_en; + bool vbus_gd; + bool detect_done; + u8 chrg_type; + u8 health; + u8 chrg_fault; + u8 ntc_fault; + + u32 ibus_limit; + u32 vbus_adc; + u32 ibus_adc; +}; + +struct sgm4154x_jeita { + int jeita_temp_above_t4_cv; + int jeita_temp_t3_to_t4_cv; + int jeita_temp_t2_to_t3_cv; + int jeita_temp_t1_to_t2_cv; + int jeita_temp_t0_to_t1_cv; + int jeita_temp_below_t0_cv; + int jeita_temp_above_t4_cc_current; + int jeita_temp_t3_to_t4_cc_current; + int jeita_temp_t2_to_t3_cc_current; + int jeita_temp_t1_to_t2_cc_current; + int jeita_temp_below_t0_cc_current; + int temp_t4_thres; + int temp_t4_thres_minus_x_degree; + int temp_t3_thres; + int temp_t3_thres_minus_x_degree; + int temp_t2_thres; + int temp_t2_thres_plus_x_degree; + int temp_t1_thres; + int temp_t1_thres_plus_x_degree; + int temp_t0_thres; + int temp_t0_thres_plus_x_degree; + int temp_neg_10_thres; +}; + +struct sgm_mmi_charger { + struct sgm4154x_device *sgm; + struct mmi_battery_info batt_info; + struct mmi_charger_info chg_info; + struct mmi_charger_cfg chg_cfg; + struct mmi_charger_constraint constraint; + struct mmi_charger_driver *driver; + u32 chrg_taper_cnt; + const char *fg_psy_name; + struct power_supply *fg_psy; + int ichg_polority; + + int paired_ichg; + int paired_ichg_table_len; + int *paired_ichg_table; + int paired_load; + int paired_load_thres_len; + int *paired_load_thres; + int paired_esr; + int batt_esr; + int batt_protect_type; + struct mmi_battery_info paired_batt_info; +}; + +struct sgm4154x_device { + struct i2c_client *client; + struct device *dev; + struct power_supply *charger; + struct power_supply *wls_psy; + struct power_supply *usb; + struct power_supply *ac; + struct mutex lock; + struct mutex i2c_rw_lock; + + struct usb_phy *usb2_phy; + struct usb_phy *usb3_phy; + struct notifier_block usb_nb; + struct work_struct usb_work; + unsigned long usb_event; + struct regmap *regmap; + + char model_name[I2C_NAME_SIZE]; + int device_id; + + struct sgm4154x_init_data init_data; + struct sgm4154x_state state; + u32 watchdog_timer; + #if defined(CONFIG_MTK_GAUGE_VERSION) && (CONFIG_MTK_GAUGE_VERSION == 30) + struct charger_device *chg_dev; + #endif + struct regulator_dev *otg_rdev; + + struct delayed_work charge_detect_delayed_work; + struct delayed_work charge_monitor_work; + struct notifier_block pm_nb; + bool sgm4154x_suspend_flag; + + struct wakeup_source *charger_wakelock; + bool enable_sw_jeita; + struct sgm4154x_jeita data; + bool enable_hw_jeita; + + struct dentry *debug_root; + + u32 *thermal_levels; + u32 thermal_fcc_ua; + int curr_thermal_level; + int num_thermal_levels; + + u32 ichg; /* charge current */ + u32 ilim; /* input current */ + u32 vlim; /* input voltage */ + u32 vreg; /* regulation voltage */ + int user_ichg; /* user charge current */ + int user_ilim; /* user input current */ + int user_chg_en; /* user charging enable */ + int user_chg_susp; /* user charger suspend */ + + bool batt_present; + bool otg_enabled; + bool irq_enabled; + u32 batt_protect_duration; + int vbat_ovp_threshold; + int ibat_ocp_threshold; + int ibat_occp_threshold; + int high_load_en_gpio; + int low_load_en_gpio; + bool high_load_active_low; + bool low_load_active_low; + bool update_pending; + + int chg_en_gpio; + bool use_ext_usb_psy; + bool use_ext_wls_psy; + struct regulator *vdd_i2c_vreg; + struct notifier_block psy_nb; + struct sgm_mmi_charger *mmi_charger; +}; + +#endif /* _SGM4154x_CHARGER_H */ diff --git a/drivers/power/sgm4154x_chg_mmi/Android.mk b/drivers/power/sgm4154x_chg_mmi/Android.mk new file mode 100644 index 000000000000..e58a5e73d125 --- /dev/null +++ b/drivers/power/sgm4154x_chg_mmi/Android.mk @@ -0,0 +1,34 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := sgm4154x_charger.ko + +ifeq ($(MMI_QC3P_TURBO_CHARGER),true) + KERNEL_CFLAGS += CONFIG_MMI_QC3P_TURBO_CHARGER=y +endif + +ifeq ($(MMI_QC3P_WT6670_DETECTED),true) + KERNEL_CFLAGS += CONFIG_MMI_QC3P_WT6670_DETECTED=y +endif + +ifeq ($(MMI_SGM41513_CHARGER),true) + KERNEL_CFLAGS += CONFIG_MMI_SGM41513_CHARGER=y +endif + +ifeq ($(MMI_QC3P_Z350_DETECTED),true) + KERNEL_CFLAGS += CONFIG_MMI_QC3P_Z350_DETECTED=y +endif + +ifeq ($(MMI_EXT_CHG_LED),true) + KERNEL_CFLAGS += CONFIG_MMI_EXT_CHG_LED=y + KBUILD_OPTIONS += CONFIG_MMI_EXT_CHG_LED=y +endif + +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_discrete_charger_class.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +KBUILD_OPTIONS += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/sgm4154x_chg_mmi/Kbuild b/drivers/power/sgm4154x_chg_mmi/Kbuild new file mode 100644 index 000000000000..b9fd084d799a --- /dev/null +++ b/drivers/power/sgm4154x_chg_mmi/Kbuild @@ -0,0 +1,27 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/sgm4154x_chg_mmi + +ifneq ($(filter m y,$(CONFIG_MMI_QC3P_TURBO_CHARGER)),) + EXTRA_CFLAGS += -DCONFIG_MMI_QC3P_TURBO_CHARGER +endif + +ifneq ($(filter m y,$(CONFIG_MMI_QC3P_WT6670_DETECTED)),) + EXTRA_CFLAGS += -DCONFIG_MMI_QC3P_WT6670_DETECTED +endif + +ifneq ($(filter m y,$(CONFIG_MMI_SGM41513_CHARGER)),) + EXTRA_CFLAGS += -DCONFIG_MMI_SGM41513_CHARGER +endif + +ifneq ($(filter m y,$(CONFIG_MMI_QC3P_Z350_DETECTED)),) + EXTRA_CFLAGS += -DCONFIG_MMI_QC3P_Z350_DETECTED +endif + +ifneq ($(filter m y,$(CONFIG_MMI_EXT_CHG_LED)),) + EXTRA_CFLAGS += -DCONFIG_MMI_EXT_CHG_LED +endif + +obj-m += sgm4154x_charger.o +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_discrete_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/sgm4154x_chg_mmi/Makefile b/drivers/power/sgm4154x_chg_mmi/Makefile new file mode 100644 index 000000000000..a98d6ad2c0d8 --- /dev/null +++ b/drivers/power/sgm4154x_chg_mmi/Makefile @@ -0,0 +1,33 @@ +ifeq ($(strip $(MODULE_KERNEL_VERSION)), 5.15) + +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_discrete_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers + +else + +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + +endif diff --git a/drivers/power/sgm4154x_chg_mmi/sgm4154x_charger.c b/drivers/power/sgm4154x_chg_mmi/sgm4154x_charger.c new file mode 100644 index 000000000000..9d8911ff4c09 --- /dev/null +++ b/drivers/power/sgm4154x_chg_mmi/sgm4154x_charger.c @@ -0,0 +1,3746 @@ +// SPDX-License-Identifier: GPL-2.0 +// SGM4154x driver version 2021-08-05-002 +// Copyright (C) 2021 Texas Instruments Incorporated - http://www.sg-micro.com + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sgm4154x_charger.h" +#include +#include +#include +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +#include +#endif + +#ifdef __indicator_led_en__ +static struct sgm4154x_device *sgm_g; +#define TRILED_NUM_MAX 3 + +struct pwm_setting { + u64 pre_period_ns; + u64 period_ns; + u64 duty_ns; +}; + +struct led_setting { + u64 on_ms; + u64 off_ms; + enum led_brightness brightness; + bool blink; + bool breath; +}; + +struct qpnp_led_dev { + struct led_classdev cdev; + struct pwm_device *pwm_dev; + struct pwm_setting pwm_setting; + struct led_setting led_setting; + struct indicator_led_chip *chip; + struct mutex lock; + const char *label; + const char *default_trigger; + u8 id; + bool blinking; + bool breathing; +}; + +struct indicator_led_chip { + struct device *dev; + struct qpnp_led_dev *leds; + struct nvmem_device *pbs_nvmem; + struct mutex bus_lock; + int num_leds; + u16 reg_base; + u8 subtype; + u8 bitmap; + struct pinctrl *pinctrl; + struct pinctrl_state *pin_sta_default; +}; +#endif +static struct power_supply_desc sgm4154x_power_supply_desc; + +/* SGM4154x REG06 BOOST_LIM[5:4], uV */ +static const unsigned int BOOST_VOLT_LIMIT[] = { + 4850000, 5000000, 5150000, 5300000 +}; + /* SGM4154x REG02 BOOST_LIM[7:7], uA */ +#if defined(__SGM41542_CHIP_ID__)|| defined(__SGM41516D_CHIP_ID__) +static const unsigned int BOOST_CURRENT_LIMIT[] = { + 1200000, 2000000 +}; +#else +static const unsigned int BOOST_CURRENT_LIMIT[] = { + 500000, 1200000 +}; +#endif + +#if (defined(__SGM41513_CHIP_ID__) || defined(__SGM41513A_CHIP_ID__) || defined(__SGM41513D_CHIP_ID__)) + +static const unsigned int IPRECHG_CURRENT_STABLE[] = { + 5000, 10000, 15000, 20000, 30000, 40000, 50000, 60000, + 80000, 100000, 120000, 140000, 160000, 180000, 200000, 240000 +}; + +static const unsigned int ITERM_CURRENT_STABLE[] = { + 5000, 10000, 15000, 20000, 30000, 40000, 50000, 60000, + 80000, 100000, 120000, 140000, 160000, 180000, 200000, 240000 +}; + +static const unsigned int DYNAMIC_VINDPM_TRACK[] = { + 0, 200000, 250000, 300000 +}; +#endif + +enum SGM4154x_VREG_FT { + VREG_FT_DISABLE, + VREG_FT_UP_8mV, + VREG_FT_DN_8mV, + VREG_FT_DN_16mV, +}; + +enum SGM4154x_VINDPM_OS { + VINDPM_OS_3900mV, + VINDPM_OS_5900mV, + VINDPM_OS_7500mV, + VINDPM_OS_10500mV, +}; + +static enum power_supply_usb_type sgm4154x_usb_type[] = { + POWER_SUPPLY_USB_TYPE_UNKNOWN, + POWER_SUPPLY_USB_TYPE_SDP, + POWER_SUPPLY_USB_TYPE_DCP, + POWER_SUPPLY_USB_TYPE_CDP, +}; + +#define WAIT_I2C_COUNT 50 +#define WAIT_I2C_TIME 10 +int mmi_regmap_update_bits(struct sgm4154x_device *sgm, unsigned int reg, + unsigned int mask, unsigned int val) +{ + int retry_count = 0; + + if (!sgm->i2c_err_wa_dis) { + while (sgm->sgm4154x_suspend_flag && retry_count < WAIT_I2C_COUNT) { + retry_count ++; + dev_err(sgm->dev, "wait system resume when I2C write, count %d\n", retry_count); + msleep(WAIT_I2C_TIME); + } + + if (retry_count >= WAIT_I2C_COUNT) + return -EBUSY; + } + + return regmap_update_bits(sgm->regmap, reg, mask, val); +} + +int mmi_regmap_read(struct sgm4154x_device *sgm, unsigned int reg, unsigned int *val) +{ + int retry_count = 0; + + if (!sgm->i2c_err_wa_dis) { + while (sgm->sgm4154x_suspend_flag && retry_count < WAIT_I2C_COUNT) { + retry_count ++; + dev_err(sgm->dev, "wait system resume when I2C read, count %d\n", retry_count); + msleep(WAIT_I2C_TIME); + } + + if (retry_count >= WAIT_I2C_COUNT) + return -EBUSY; + } + + return regmap_read(sgm->regmap, reg, val); +} + +static int sgm4154x_usb_notifier(struct notifier_block *nb, unsigned long val, + void *priv) +{ + struct sgm4154x_device *sgm = + container_of(nb, struct sgm4154x_device, usb_nb); + + sgm->usb_event = val; + + queue_work(system_power_efficient_wq, &sgm->usb_work); + + return NOTIFY_OK; +} + +static void sgm4154x_usb_work(struct work_struct *data) +{ + struct sgm4154x_device *sgm = + container_of(data, struct sgm4154x_device, usb_work); + + switch (sgm->usb_event) { + case USB_EVENT_ID: + break; + + case USB_EVENT_NONE: + power_supply_changed(sgm->charger); + break; + } + + return; + + dev_err(sgm->dev, "Error switching to charger mode.\n"); +} +/*mahj:for build +static int sgm4154x_get_term_curr(struct sgm4154x_device *sgm) +{ + int ret; + int reg_val; + int offset = SGM4154x_TERMCHRG_I_MIN_uA; + + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_3, ®_val); + if (ret) + return ret; + + reg_val &= SGM4154x_TERMCHRG_CUR_MASK; + reg_val = reg_val * SGM4154x_TERMCHRG_CURRENT_STEP_uA + offset; + return reg_val; +} + +static int sgm4154x_get_prechrg_curr(struct sgm4154x_device *sgm) +{ + int ret; + int reg_val; + int offset = SGM4154x_PRECHRG_I_MIN_uA; + + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_3, ®_val); + if (ret) + return ret; + + reg_val = (reg_val&SGM4154x_PRECHRG_CUR_MASK)>>4; + reg_val = reg_val * SGM4154x_PRECHRG_CURRENT_STEP_uA + offset; + return reg_val; +} + +static int sgm4154x_get_ichg_curr(struct sgm4154x_device *sgm) +{ + int ret; + int ichg; + + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_2, &ichg); + if (ret) + return ret; + + ichg &= SGM4154x_ICHRG_CUR_MASK; + + return ichg * SGM4154x_ICHRG_CURRENT_STEP_uA; +} +*/ +static int sgm4154x_set_term_curr(struct sgm4154x_device *sgm, int term_current) +{ + int reg_val; + +#if (defined(__SGM41513_CHIP_ID__) || defined(__SGM41513A_CHIP_ID__) || defined(__SGM41513D_CHIP_ID__)) + for(reg_val = 1; reg_val < 16 && term_current >= ITERM_CURRENT_STABLE[reg_val]; reg_val++) + ; + reg_val--; +#else + int offset = SGM4154x_TERMCHRG_I_MIN_uA; + + if (term_current < SGM4154x_TERMCHRG_I_MIN_uA) + term_current = SGM4154x_TERMCHRG_I_MIN_uA; + else if (term_current > SGM4154x_TERMCHRG_I_MAX_uA) + term_current = SGM4154x_TERMCHRG_I_MAX_uA; + + reg_val = (term_current - offset) / SGM4154x_TERMCHRG_CURRENT_STEP_uA; +#endif + return mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_3, + SGM4154x_TERMCHRG_CUR_MASK, reg_val); +} + +static int sgm4154x_set_prechrg_curr(struct sgm4154x_device *sgm, int pre_current) +{ + int reg_val; + +#if (defined(__SGM41513_CHIP_ID__) || defined(__SGM41513A_CHIP_ID__) || defined(__SGM41513D_CHIP_ID__)) + for(reg_val = 1; reg_val < 16 && pre_current >= IPRECHG_CURRENT_STABLE[reg_val]; reg_val++) + ; + reg_val--; +#else + int offset = SGM4154x_PRECHRG_I_MIN_uA; + + if (pre_current < SGM4154x_PRECHRG_I_MIN_uA) + pre_current = SGM4154x_PRECHRG_I_MIN_uA; + else if (pre_current > SGM4154x_PRECHRG_I_MAX_uA) + pre_current = SGM4154x_PRECHRG_I_MAX_uA; + + reg_val = (pre_current - offset) / SGM4154x_PRECHRG_CURRENT_STEP_uA; +#endif + reg_val = reg_val << 4; + return mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_3, + SGM4154x_PRECHRG_CUR_MASK, reg_val); +} + + + +static int sgm4154x_set_ichrg_curr(struct sgm4154x_device *sgm, int chrg_curr) +{ + int ret; + int reg_val; + +#if (defined(__SGM41513_CHIP_ID__) || defined(__SGM41513A_CHIP_ID__) || defined(__SGM41513D_CHIP_ID__)) + if (chrg_curr <= 40000) + reg_val = chrg_curr / 5000; + else if (chrg_curr <= 110000) + reg_val = 0x08 + (chrg_curr -40000) / 10000; + else if (chrg_curr <= 270000) + reg_val = 0x0F + (chrg_curr -110000) / 20000; + else if (chrg_curr <= 540000) + reg_val = 0x17 + (chrg_curr -270000) / 30000; + else if (chrg_curr <= 1500000) + reg_val = 0x20 + (chrg_curr -540000) / 60000; + else if (chrg_curr <= 2940000) + reg_val = 0x30 + (chrg_curr -1500000) / 120000; + else + reg_val = 0x3d; +#else + if (chrg_curr < SGM4154x_ICHRG_I_MIN_uA) + chrg_curr = SGM4154x_ICHRG_I_MIN_uA; + else if ( chrg_curr > sgm->init_data.max_ichg) + chrg_curr = sgm->init_data.max_ichg; + + /* enable dynamic adjust battery voltage */ + if(sgm->enable_dynamic_adjust_batvol) + sgm->final_cc = chrg_curr; + + reg_val = chrg_curr / SGM4154x_ICHRG_CURRENT_STEP_uA; +#endif + + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_2, + SGM4154x_ICHRG_CUR_MASK, reg_val); + + return ret; +} + +/* enable dynamic adjust battery voltage */ +static int sgm4154x_set_chrg_adjust_volt(struct sgm4154x_device *sgm, int chrg_volt, int adjust) +{ + int ret; + int reg_val; + + if (chrg_volt < SGM4154x_VREG_V_MIN_uV) + chrg_volt = SGM4154x_VREG_V_MIN_uV; + else if (chrg_volt > sgm->init_data.max_vreg) + chrg_volt = sgm->init_data.max_vreg; + + sgm->final_cv = chrg_volt; + reg_val = (chrg_volt-SGM4154x_VREG_V_MIN_uV) / SGM4154x_VREG_V_STEP_uV + adjust; + reg_val = reg_val<<3; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_4, + SGM4154x_VREG_V_MASK, reg_val); + + return ret; +} + +// fine tuning termination voltage,to Improve accuracy +static int sgm4154x_vreg_fine_tuning(struct sgm4154x_device *sgm, enum SGM4154x_VREG_FT ft) +{ + int ret; + int reg_val; + + switch(ft) { + case VREG_FT_DISABLE: + reg_val = 0; + break; + + case VREG_FT_UP_8mV: + reg_val = SGM4154x_VREG_FT_UP_8mV; + break; + + case VREG_FT_DN_8mV: + reg_val = SGM4154x_VREG_FT_DN_8mV; + break; + + case VREG_FT_DN_16mV: + reg_val = SGM4154x_VREG_FT_DN_16mV; + break; + + default: + reg_val = 0; + break; + } + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_f, + SGM4154x_VREG_FT_MASK, reg_val); + pr_info("%s reg_val:%d\n",__func__,reg_val); + + return ret; +} + +static int sgm4154x_set_chrg_volt(struct sgm4154x_device *sgm, int chrg_volt) +{ + int ret; + int reg_val; + enum SGM4154x_VREG_FT ft = VREG_FT_DISABLE; + + if (chrg_volt < SGM4154x_VREG_V_MIN_uV) + chrg_volt = SGM4154x_VREG_V_MIN_uV; + else if (chrg_volt > sgm->init_data.max_vreg) + chrg_volt = sgm->init_data.max_vreg; + + + reg_val = (chrg_volt-SGM4154x_VREG_V_MIN_uV) / SGM4154x_VREG_V_STEP_uV; + + switch(chrg_volt) { + case 4512000: + case 4480000: + case 4450000: + reg_val++; + ft = VREG_FT_DN_16mV; + break; + case 4200000: + reg_val++; + ft = VREG_FT_DN_8mV; + break; + default: + break; + } + + ret = sgm4154x_vreg_fine_tuning(sgm, ft); + if (ret) { + pr_err("%s can't set vreg fine tunning ret=%d\n", __func__, ret); + return ret; + } + + reg_val = reg_val<<3; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_4, + SGM4154x_VREG_V_MASK, reg_val); + + return ret; +} +/*mahj:for build +static int sgm4154x_get_chrg_volt(struct sgm4154x_device *sgm) +{ + int ret = 0; + int vreg_val = 0; + int chrg_volt = 0; + + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_4, &vreg_val); + if (ret) + return ret; + + vreg_val = (vreg_val & SGM4154x_VREG_V_MASK)>>3; + + if (15 == vreg_val) + chrg_volt = 4352000; //default + else if (vreg_val < 25) + chrg_volt = vreg_val*SGM4154x_VREG_V_STEP_uV + SGM4154x_VREG_V_MIN_uV; + + return chrg_volt; +} +*/ +//#if defined(__SGM41542_CHIP_ID__)|| defined(__SGM41516D_CHIP_ID__) +static int sgm4154x_read_usbin_voltage_chan(struct sgm4154x_device *sgm, int *val) +{ + int rc; + + if (!sgm->iio.usbin_v_chan) + return -ENODATA; + + rc = iio_read_channel_processed(sgm->iio.usbin_v_chan, val); + if (rc < 0) { + dev_err(sgm->dev, "Couldn't read USBIN channel rc=%d\n", rc); + return rc; + } + + return 0; +} + +static int sgm4154x_get_usb_voltage_now(struct sgm4154x_device *sgm, int *val) +{ + int rc; + int raw_date; + + rc = sgm4154x_read_usbin_voltage_chan(sgm, &raw_date); + if (rc < 0) { + dev_err(sgm->dev, "Couldn't read USBIN over vadc rc=%d\n", rc); + return rc; + } +#ifdef CONFIG_MMI_SGM41513_CHARGER + *val = raw_date * 1000; +#else + *val = raw_date * 3; +#endif + + return 0; +} + +#ifndef CONFIG_MMI_QC3P_WT6670_DETECTED +static int sgm4154x_adjust_qc20_hvdcp_5v(struct sgm4154x_device *sgm) +{ + int ret; + int dp_val, dm_val; + + /* dp 0.6v and dm 0v out 5V */ + dm_val = 0x1<<1; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0v + if (ret) + return ret; + + dp_val = 0x2<<3; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DP_VSEL_MASK, dp_val); //dp 0.6v + return ret; +} +static int sgm4154x_detected_qc20_hvdcp(struct sgm4154x_device *sgm, int *charger_type) +{ + int ret; + int dp_val, dm_val; + int vbus_voltage; + + /*dp and dm connected,dp 0.6V dm 0.6V*/ + dp_val = 0x2<<3; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DP_VSEL_MASK, dp_val); //dp 0.6V + if (ret) + return ret; + + dm_val = 0x2<<1; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0.6V + if (ret) + return ret; + + msleep(1300); + + dm_val = 0x1<<1; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0V + if (ret) + return ret; + + mdelay(500); + + /* dp 3.3v and dm 0.6v out 9V */ + dp_val = SGM4154x_DP_VSEL_MASK; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DP_VSEL_MASK, dp_val); //dp 3.3v + if (ret) + return ret; + + dm_val = 0x2<<1; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0.6v + if (ret) + return ret; + + msleep(200);//need tunning + + sgm4154x_get_usb_voltage_now(sgm, &vbus_voltage); + dev_info(sgm->dev, "vbus voltage now = %d\n", vbus_voltage); + + if (vbus_voltage > MMI_HVDCP2_VOLTAGE_STANDARD) { + dev_info(sgm->dev, "QC20 charger detected\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + *charger_type = QTI_POWER_SUPPLY_TYPE_USB_HVDCP; +#else + *charger_type = POWER_SUPPLY_TYPE_USB_HVDCP; +#endif + } else { + dev_info(sgm->dev, "charger type is not HVDCP\n"); + } + + ret = sgm4154x_adjust_qc20_hvdcp_5v(sgm); + if (ret) { + dev_err(sgm->dev, "Cann't adjust qc20 hvdcp 5V\n"); + } + + msleep(300);//need tunning + + sgm4154x_get_usb_voltage_now(sgm, &vbus_voltage); + dev_info(sgm->dev, "vbus voltage now = %d after qc20 detected\n", vbus_voltage); + + return ret; +} +#endif +#if 0 //mahj:for build +static int sgm4154x_enable_qc20_hvdcp_12v(struct sgm4154x_device *sgm) +{ + int ret; + int dp_val, dm_val; + + /*dp and dm connected,dp 0.6V dm 0.6V*/ + dp_val = 0x2<<3; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DP_VSEL_MASK, dp_val); //dp 0.6V + if (ret) + return ret; + + dm_val = 0x2<<1; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0.6V + if (ret) + return ret; + mdelay(1000); + + dm_val = 0x2; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0V + mdelay(1); + /* dp 0.6v and dm 0.6v out 12V */ + dp_val = 0x2<<3; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DP_VSEL_MASK, dp_val); //dp 0.6v + if (ret) + return ret; + //mdelay(1250); + + dm_val = 0x2<<1; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0.6v + + return ret; +} + +/* step 1. entry QC3.0 mode + step 2. up or down 200mv + step 3. retry step 2 */ +static int sgm4154x_enable_qc30_hvdcp(struct sgm4154x_device *sgm) +{ + int ret; + int dp_val, dm_val; + + dp_val = 0x2<<3; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DP_VSEL_MASK, dp_val); //dp 0.6v + if (ret) + return ret; + + dm_val = 0x2<<1; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0.6V + if (ret) + return ret; + mdelay(1000); + + dm_val = 0x2; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0V + mdelay(1); + + dm_val = SGM4154x_DM_VSEL_MASK; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 3.3v + + return ret; +} +#endif + +// Must enter 3.0 mode to call ,otherwise cannot step correctly. +static int sgm4154x_qc30_step_up_vbus(struct sgm4154x_device *sgm) +{ + int ret; + int dp_val; + + /* dm 3.3v to dm 0.6v step up 200mV when IC is QC3.0 mode*/ + dp_val = SGM4154x_DP_VSEL_MASK; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DP_VSEL_MASK, dp_val); //dp 3.3v + if (ret) + return ret; + + udelay(2500); + dp_val = 0x2<<3; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DP_VSEL_MASK, dp_val); //dp 0.6v + if (ret) + return ret; + + udelay(2500); + return ret; +} +// Must enter 3.0 mode to call ,otherwise cannot step correctly. +static int sgm4154x_qc30_step_down_vbus(struct sgm4154x_device *sgm) +{ + int ret; + int dm_val; + + /* dp 0.6v and dm 0.6v step down 200mV when IC is QC3.0 mode*/ + dm_val = 0x2<<1; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 0.6V + if (ret) + return ret; + + udelay(2500); + dm_val = SGM4154x_DM_VSEL_MASK; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 3.3v + udelay(2500); + + return ret; +} + +static int sgm4154x_get_vindpm_offset_os(struct sgm4154x_device *sgm) +{ + int ret; + int reg_val; + + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_f, ®_val); + if (ret) + return ret; + + reg_val = reg_val & SGM4154x_VINDPM_OS_MASK; + + return reg_val; +} +/*mahj:for build +static int sgm4154x_set_vindpm_offset_os(struct sgm4154x_device *sgm,enum SGM4154x_VINDPM_OS offset_os) +{ + int ret; + + + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_f, + SGM4154x_VINDPM_OS_MASK, offset_os); + + if (ret){ + pr_err("%s fail\n",__func__); + return ret; + } + + return ret; +} +*/ +static int sgm4154x_set_input_volt_lim(struct sgm4154x_device *sgm, int vindpm) +{ + int ret; + int offset = 0; + int vlim; + int temp; + + if (vindpm < SGM4154x_VINDPM_V_MIN_uV || + vindpm > SGM4154x_VINDPM_V_MAX_uV) + return -EINVAL; + + temp = sgm4154x_get_vindpm_offset_os(sgm); + if (0 == temp) + offset = 3900000; //uv + else if (1 == temp) + offset = 5900000; //uv + else if (2 == temp) + offset = 7500000; //uv + else if (3 == temp) + offset = 10500000; //uv + + vlim = (vindpm - offset) / SGM4154x_VINDPM_STEP_uV; + + vlim &= 0x0F; + + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_6, + SGM4154x_VINDPM_V_MASK, vlim); + + return ret; +} + +static int sgm4154x_get_input_volt_lim(struct sgm4154x_device *sgm) +{ + int ret; + int offset = 0; + int vlim; + int temp; + + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_6, &vlim); + if (ret) + return ret; + + temp = sgm4154x_get_vindpm_offset_os(sgm); + if (0 == temp) + offset = 3900000; //uv + else if (1 == temp) + offset = 5900000; + else if (2 == temp) + offset = 7500000; + else if (3 == temp) + offset = 10500000; + + vlim = offset + (vlim & 0x0F) * SGM4154x_VINDPM_STEP_uV; + return vlim; +} + +#ifdef __SGM41513_CHIP_ID__ +static int sgm4154x_set_dynamic_vindpm_track(struct sgm4154x_device *sgm, int track_uv) +{ + int reg_val = 0; + int ret = 0; + + for(reg_val = 1; reg_val < 4 && track_uv >= DYNAMIC_VINDPM_TRACK[reg_val]; reg_val++); + reg_val--; + + pr_info("%s track_uv: %d, reg_val: %d\n", __func__, track_uv, reg_val); + + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_7, + SGM4154x_DYNAMIC_VINDPM_TRACK_MASK, reg_val); + + return ret; +} +#endif + +static int sgm4154x_set_input_curr_lim(struct sgm4154x_device *sgm, int iindpm) +{ + int ret; + int reg_val = 0; + + /*As sgm41542 ic input current positive error, so limit 100ma for wls attestation*/ + if (sgm->wls_max_icl && iindpm == sgm->wls_max_icl) { + iindpm -= SGM4154x_IINDPM_I_MIN_uA; + pr_info("wls_max_icl = %d, iindpm = %d\n",sgm->wls_max_icl, iindpm); + } + + if (iindpm < SGM4154x_IINDPM_I_MIN_uA) + reg_val = 0; + else if (iindpm >= SGM4154x_IINDPM_I_MAX_uA) + reg_val = 0x1F; +#if (defined(__SGM41513_CHIP_ID__) || defined(__SGM41513A_CHIP_ID__) || defined(__SGM41513D_CHIP_ID__)) + reg_val = (iindpm-SGM4154x_IINDPM_I_MIN_uA) / SGM4154x_IINDPM_STEP_uA; +#else + else if (iindpm >= SGM4154x_IINDPM_I_MIN_uA && iindpm <= 3100000)//default + reg_val = (iindpm-SGM4154x_IINDPM_I_MIN_uA) / SGM4154x_IINDPM_STEP_uA; + else if (iindpm > 3100000 && iindpm < SGM4154x_IINDPM_I_MAX_uA) + reg_val = 0x1E; + if(sgm->sgm_18W_iindpm_comp && iindpm == 3000000) + reg_val = 0x1E; + +#endif + + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_0, + SGM4154x_IINDPM_I_MASK, reg_val); + return ret; +} + +static int sgm4154x_get_input_curr_lim(struct sgm4154x_device *sgm) +{ + int ret; + int ilim; + + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_0, &ilim); + if (ret) + return ret; + if (SGM4154x_IINDPM_I_MASK == (ilim & SGM4154x_IINDPM_I_MASK)) + return SGM4154x_IINDPM_I_MAX_uA; + + ilim = (ilim & SGM4154x_IINDPM_I_MASK)*SGM4154x_IINDPM_STEP_uA + SGM4154x_IINDPM_I_MIN_uA; + + return ilim; +} + +static int sgm4154x_set_icl(struct charger_device *chg_dev, u32 uA) +{ + struct sgm4154x_device *sgm = dev_get_drvdata(&chg_dev->dev); + + pr_info("%s set icl curr = %d\n", __func__, uA); + + sgm->input_current_cache = uA; + + if (sgm->mmi_is_qc3_authen) { + dev_info(sgm->dev, "hvdcp detecting now\n"); + return 0; + } + + return sgm4154x_set_input_curr_lim(sgm, uA); +} + +static int sgm4154x_get_icl(struct charger_device *chg_dev, u32 *uA) +{ + struct sgm4154x_device *sgm = dev_get_drvdata(&chg_dev->dev); + + *uA = sgm4154x_get_input_curr_lim(sgm); + + pr_info("%s get icl curr = %d\n", __func__, *uA); + + return 0; +} + +static int sgm4154x_is_enabled_charging(struct charger_device *chg_dev, bool *en) +{ + struct sgm4154x_device *sgm = dev_get_drvdata(&chg_dev->dev); + int ret = 0; + int status = 0; + + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_1, &status); + if(ret) + return false; + + *en = (status & SGM4154x_CHRG_EN) ? true:false; + + pr_info("%s status:%d\n", __func__, *en); + + return 0; +} + +/*mahj:for build +static int sgm4154x_set_watchdog_timer(struct sgm4154x_device *sgm, int time) +{ + int ret; + u8 reg_val = 0; + + if (time == 0) + reg_val = SGM4154x_WDT_TIMER_DISABLE; + else if (time == 40) + reg_val = SGM4154x_WDT_TIMER_40S; + else if (time == 80) + reg_val = SGM4154x_WDT_TIMER_80S; + else + reg_val = SGM4154x_WDT_TIMER_160S; + + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_5, + SGM4154x_WDT_TIMER_MASK, reg_val); + + return ret; +} + +static int sgm4154x_set_wdt_rst(struct sgm4154x_device *sgm, bool is_rst) +{ + int val = 0; + + if (is_rst) + val = SGM4154x_WDT_RST_MASK; + else + val = 0; + return mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_1, + SGM4154x_WDT_RST_MASK, val); +} +*/ +static int sgm4154x_get_state(struct sgm4154x_device *sgm, + struct sgm4154x_state *state) +{ + int chrg_stat; + int fault; + int chrg_param_0,chrg_param_1,chrg_param_2; + int ret; + + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_STAT, &chrg_stat); + if (ret){ + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_STAT, &chrg_stat); + if (ret){ + pr_err("%s read SGM4154x_CHRG_STAT fail: %d\n",__func__, ret); + return ret; + } + } + + state->chrg_type = chrg_stat & SGM4154x_VBUS_STAT_MASK; + state->chrg_stat = chrg_stat & SGM4154x_CHG_STAT_MASK; + state->online = !!(chrg_stat & SGM4154x_PG_STAT); + state->therm_stat = !!(chrg_stat & SGM4154x_THERM_STAT); + state->vsys_stat = !!(chrg_stat & SGM4154x_VSYS_STAT); + + pr_err("%s chrg_stat =%d,chrg_type =%d online = %d\n",__func__,state->chrg_stat,state->chrg_type,state->online); + + + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_FAULT, &fault); + if (ret){ + pr_err("%s read SGM4154x_CHRG_FAULT fail\n",__func__); + return ret; + } + state->chrg_fault = fault; + state->ntc_fault = fault & SGM4154x_TEMP_MASK; + state->health = state->ntc_fault; + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_0, &chrg_param_0); + if (ret){ + pr_err("%s read SGM4154x_CHRG_CTRL_0 fail\n",__func__); + return ret; + } + state->hiz_en = !!(chrg_param_0 & SGM4154x_HIZ_EN); + + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_5, &chrg_param_1); + if (ret){ + pr_err("%s read SGM4154x_CHRG_CTRL_5 fail\n",__func__); + return ret; + } + state->term_en = !!(chrg_param_1 & SGM4154x_TERM_EN); + + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_a, &chrg_param_2); + if (ret){ + pr_err("%s read SGM4154x_CHRG_CTRL_a fail\n",__func__); + return ret; + } + state->vbus_gd = !!(chrg_param_2 & SGM4154x_VBUS_GOOD); + + return 0; +} + +int sgm4154x_enable_charger(struct sgm4154x_device *sgm) +{ + int ret; + printk("sgm4154x_enable_charger\n"); + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_1, SGM4154x_CHRG_EN, + SGM4154x_CHRG_EN); + + return ret; +} + +#ifdef __indicator_led_en__ +int sgm4154x_disable_indicator_led(struct sgm4154x_device *sgm) +{ + int ret; + printk("sgm4154x_disable_indicator_led\n"); + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_0, SGM4154x_VREG_ICHG_MON_MASK, 0x1<<5);//follow stat_set + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_f, SGM4154x_VREG_STAT_SET_MASK, 0x0<<2);//led off + return ret; +} +#endif + +int sgm4154x_disable_charger(struct sgm4154x_device *sgm) +{ + int ret; + printk("sgm4154x_disable_charger\n"); + ret = + mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_1, SGM4154x_CHRG_EN, + 0); + return ret; +} + +int sgm4154x_disable_watchdog(struct sgm4154x_device *sgm) +{ + int ret; + printk("sgm4154x_disable_watchdog\n"); + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_5, SGM4154x_WDT_TIMER_MASK, + SGM4154x_WDT_TIMER_DISABLE); + return ret; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +int sgm4154x_enable_termination(struct charger_device *chg_dev, bool enable) +#else +static int sgm4154x_enable_termination(struct charger_device *chg_dev, bool enable) +#endif +{ + struct sgm4154x_device *sgm = dev_get_drvdata(&chg_dev->dev); + int ret = 0; + + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_5, SGM4154x_EN_TERM_MASK, + enable ? SGM4154x_EN_TERM_ENABLE : SGM4154x_EN_TERM_DISABLE ); + + pr_info("%s, %s enable term %s\n", __func__, + enable ? "enable" : "disable", + ret ? "failed" : "success"); + + return ret; +} +EXPORT_SYMBOL_GPL(sgm4154x_enable_termination); +int sgm4154x_enable_hw_jeita(struct charger_device *chg_dev, bool en) +{ + int rc = 0; + struct sgm4154x_device *sgm = dev_get_drvdata(&chg_dev->dev); + + rc = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, SGM4154x_JEITA_ENABLE_MASK, + en ? SGM4154x_JEITA_ENABLE : SGM4154x_JEITA_DISABLE); + + pr_info("%s, %s hw jeita %s\n", __func__, + en ? "enable" : "disable", + rc ? "failed" : "success"); + + return rc; +} + +int sgm4154x_disable_vindpm_int_pulse(struct sgm4154x_device *sgm) +{ + int ret; + printk("sgm4154x_disable_vindpm_pulse\n"); + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_a, SGM4154x_VINDPM_INT_MASK, + SGM4154x_VINDPM_INT_DISABLE); + return ret; +} + +int sgm4154x_disable_iindpm_int_pulse(struct sgm4154x_device *sgm) +{ + int ret; + printk("sgm4154x_disable_iindpm_pulse\n"); + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_a, SGM4154x_IINDPM_INT_MASK, + SGM4154x_IINDPM_INT_DISABLE); + return ret; +} + +/*mahj:for build +float sgm4154x_get_charger_output_power(struct sgm4154x_device *sgm) +{ + int ret; + int i = 0x1F; + int j = 0; + int vlim; + int ilim; + int temp; + int offset; + int output_volt; + int output_curr; + float o_i,o_v; + + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_6, &vlim); //read default setting to save + if (ret){ + pr_err("%s read SGM4154x_CHRG_CTRL_6 fail\n",__func__); + return ret; + } + + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_0, &ilim); //read default setting to save + if (ret){ + pr_err("%s read SGM4154x_CHRG_CTRL_0 fail\n",__func__); + return ret; + } + + + mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_6, + SGM4154x_VINDPM_V_MASK, 0); + while(i--){ + + mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_0, + SGM4154x_IINDPM_I_MASK, i); + mdelay(50); + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_a, &temp); + if (ret){ + pr_err("%s read SGM4154x_CHRG_CTRL_a fail\n",__func__); + return ret; + } + if (1 == (!!(temp&0x20))){ + output_curr = 100 + i*100; //mA + if (0x1F == i) + output_curr = 3800; //mA + } + } + + mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_0, + SGM4154x_IINDPM_I_MASK, SGM4154x_IINDPM_I_MASK); + for(j = 0;j <= 0xF;j ++) + { + mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_6, + SGM4154x_VINDPM_V_MASK, j); + + mdelay(10); + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_a, &temp); + if (ret){ + pr_err("%s read SGM4154x_CHRG_CTRL_a fail\n",__func__); + return ret; + } + + if (1 == (!!(temp&0x40))){ + + temp = sgm4154x_get_vindpm_offset_os(sgm); + if (0 == temp) + offset = 3900; //mv + else if (1 == temp) + offset = 5900; //mv + else if (2 == temp) + offset = 7500; //mv + else if (3 == temp) + offset = 10500; //mv + output_volt = offset + j*100; //mv + } + + } + o_i = (float)output_curr/1000; + o_v = (float)output_volt/1000; + return o_i * o_v; +} +*/ +static int sgm4154x_set_vac_ovp(struct sgm4154x_device *sgm) +{ + int reg_val; + + dev_notice(sgm->dev, "%s", __func__); + reg_val = 0xFF & SGM4154x_VAC_OVP_MASK; + + return mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_6, + SGM4154x_VAC_OVP_MASK, reg_val); +} + +static int sgm4154x_set_recharge_volt(struct sgm4154x_device *sgm, int recharge_volt) +{ + int reg_val; + dev_notice(sgm->dev, "%s:%d", __func__, recharge_volt); + reg_val = (recharge_volt - SGM4154x_VRECHRG_OFFSET_mV) / SGM4154x_VRECHRG_STEP_mV; + + return mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_4, + SGM4154x_VRECHARGE, reg_val); +} + +static int sgm4154x_set_otg_voltage(struct sgm4154x_device *sgm, int uv) +{ + int ret = 0; + int reg_val = -1; + int i = 0; + while(i<4){ + if (uv == BOOST_VOLT_LIMIT[i]){ + reg_val = i; + break; + } + i++; + } + if (reg_val < 0) + return reg_val; + reg_val = reg_val << 4; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_6, + SGM4154x_OTG_MASK, reg_val); + + return ret; +} + +static int sgm4154x_set_otg_current(struct sgm4154x_device *sgm, int ua) +{ + int ret = 0; + + if (ua >= BOOST_CURRENT_LIMIT[1]) { + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_2, + SGM4154X_BOOST_LIM_MASK, BIT(7)); + } else { + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_2, + SGM4154X_BOOST_LIM_MASK, 0); + } + return ret; +} + +static int get_charger_type(struct sgm4154x_device * sgm) +{ + enum power_supply_usb_type usb_type; + +#if defined(__SGM41542_CHIP_ID__)|| defined(__SGM41516D_CHIP_ID__) + switch(sgm->state.chrg_type) { + case SGM4154x_USB_SDP: + usb_type = POWER_SUPPLY_USB_TYPE_SDP; + break; + + case SGM4154x_USB_CDP: + usb_type = POWER_SUPPLY_USB_TYPE_CDP; + break; + + case SGM4154x_USB_DCP: + usb_type = POWER_SUPPLY_USB_TYPE_DCP; + break; + + case SGM4154x_NON_STANDARD: + usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + break; + + default: + usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + break; + } +#else + switch(sgm->real_charger_type) { + case POWER_SUPPLY_TYPE_USB: + usb_type = POWER_SUPPLY_USB_TYPE_SDP; + break; + + case POWER_SUPPLY_TYPE_USB_CDP: + usb_type = POWER_SUPPLY_USB_TYPE_CDP; + break; + + case POWER_SUPPLY_TYPE_USB_DCP: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + case QTI_POWER_SUPPLY_TYPE_USB_HVDCP: + case QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3: + case QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5: +#else + case POWER_SUPPLY_TYPE_USB_HVDCP: + case POWER_SUPPLY_TYPE_USB_HVDCP_3: + case POWER_SUPPLY_TYPE_USB_HVDCP_3P5: +#endif + usb_type = POWER_SUPPLY_USB_TYPE_DCP; + break; + + default: + usb_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; + break; + } +#endif + + pr_err("%s usb_type:%d\n",__func__,usb_type); + return usb_type; +} + +static int sgm4154x_property_is_writeable(struct power_supply *psy, + enum power_supply_property prop) +{ + switch (prop) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: + case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: + case POWER_SUPPLY_PROP_PRECHARGE_CURRENT: + case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: + return true; + default: + return false; + } +} +static int sgm4154x_charger_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + struct sgm4154x_device *sgm = power_supply_get_drvdata(psy); + int ret = -EINVAL; + + switch (prop) { + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = sgm4154x_set_icl(sgm->chg_dev, val->intval); + break; + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = sgm4154x_set_input_volt_lim(sgm, val->intval); + break; + default: + return -EINVAL; + } + + return ret; +} + +int sgm4154x_get_charging_status(struct sgm4154x_device *sgm) +{ + int ret = 0; + int chrg_stat; + + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_STAT, &chrg_stat); + if (ret) { + pr_err("%s read SGM4154x_CHRG_STAT fail\n",__func__); + return ret; + } + + chrg_stat &= SGM4154x_CHG_STAT_MASK; + + return chrg_stat; +} + +static int sgm4154x_charger_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct sgm4154x_device *sgm = power_supply_get_drvdata(psy); + struct sgm4154x_state state = sgm->state; + int chrg_status = 0; + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + chrg_status = sgm4154x_get_charging_status(sgm); + if (!state.chrg_type || (state.chrg_type == SGM4154x_OTG_MODE)) + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + else if (!chrg_status || (chrg_status == SGM4154x_TERM_CHRG)) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + case POWER_SUPPLY_PROP_CHARGE_TYPE: + chrg_status = sgm4154x_get_charging_status(sgm); + switch (chrg_status) { + case SGM4154x_PRECHRG: + val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; + break; + case SGM4154x_FAST_CHRG: + val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; + break; + case SGM4154x_TERM_CHRG: + case SGM4154x_NOT_CHRGING: + val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; + break; + default: + val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; + } + break; + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = SGM4154x_MANUFACTURER; + break; + + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = SGM4154x_NAME; + break; + + case POWER_SUPPLY_PROP_ONLINE: + val->intval = state.online; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = state.vbus_gd; + break; + case POWER_SUPPLY_PROP_TYPE: + val->intval = sgm4154x_power_supply_desc.type; + break; + case POWER_SUPPLY_PROP_USB_TYPE: + val->intval = get_charger_type(sgm); + break; + + case POWER_SUPPLY_PROP_HEALTH: + if (state.chrg_fault & 0xF8) + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + else + val->intval = POWER_SUPPLY_HEALTH_GOOD; + + switch (state.health) { + case SGM4154x_TEMP_HOT: + val->intval = POWER_SUPPLY_HEALTH_HOT; + break; + case SGM4154x_TEMP_WARM: + val->intval = POWER_SUPPLY_HEALTH_WARM; + break; + case SGM4154x_TEMP_COOL: + val->intval = POWER_SUPPLY_HEALTH_COOL; + break; + case SGM4154x_TEMP_COLD: + val->intval = POWER_SUPPLY_HEALTH_COLD; + break; + } + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + sgm4154x_get_usb_voltage_now(sgm, &val->intval); + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + //val->intval = state.ibus_adc; + break; + + case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT: + ret = sgm4154x_get_input_volt_lim(sgm); + if (ret < 0) + return ret; + + val->intval = ret; + ret = 0; + break; + + case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: + ret = sgm4154x_get_input_curr_lim(sgm); + if (ret < 0) + return ret; + + val->intval = ret; + ret = 0; + break; + default: + return -EINVAL; + } + + return ret; +} + + +#if 0 +static bool sgm4154x_state_changed(struct sgm4154x_device *sgm, + struct sgm4154x_state *new_state) +{ + struct sgm4154x_state old_state; + + mutex_lock(&sgm->lock); + old_state = sgm->state; + mutex_unlock(&sgm->lock); + + return (old_state.chrg_status != new_state->chrg_status || + old_state.chrg_fault != new_state->chrg_fault || + old_state.online != new_state->online || + old_state.health != new_state->health || + old_state.chrg_type != new_state->chrg_type + ); +} +#endif +static void sgm4154x_dump_register(struct sgm4154x_device * sgm) +{ + int i = 0; + u32 reg = 0; + + for(i=0; i<=SGM4154x_CHRG_CTRL_f; i++) { + mmi_regmap_read(sgm, i, ®); + pr_err("%s REG[0x%x]=0x%x\n", __func__, i, reg); + } +} + +int sgm4154x_get_usb_present(struct sgm4154x_device *sgm) +{ + int ret; + u32 stat; + + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_a, &stat); + if (ret){ + pr_err("%s read SGM4154x_CHRG_CTRL_a fail\n",__func__); + return ret; + } + sgm->state.vbus_gd = !!(stat & SGM4154x_VBUS_GOOD); + return 0; +} + +static int sgm4154x_request_dpdm(struct sgm4154x_device *sgm, bool enable) +{ + int rc = 0; + + if(sgm->ignore_request_dpdm) { + dev_err(sgm->dev, "%s ignore_request_dpdm\n", __func__); + return rc; + } + mutex_lock(&sgm->regulator_lock); + /* fetch the DPDM regulator */ + if (!sgm->dpdm_reg && of_get_property(sgm->dev->of_node, + "dpdm-supply", NULL)) { + sgm->dpdm_reg = devm_regulator_get(sgm->dev, "dpdm"); + if (IS_ERR(sgm->dpdm_reg)) { + rc = PTR_ERR(sgm->dpdm_reg); + dev_err(sgm->dev, "Couldn't get dpdm regulator rc=%d\n", rc); + sgm->dpdm_reg = NULL; + mutex_unlock(&sgm->regulator_lock); + return rc; + } + } + + + if (enable) { + if (sgm->dpdm_reg && !sgm->dpdm_enabled) { + dev_err(sgm->dev, "enabling DPDM regulator\n"); + rc = regulator_enable(sgm->dpdm_reg); + if (rc < 0) + dev_err(sgm->dev, + "Couldn't enable dpdm regulator rc=%d\n", + rc); + else + sgm->dpdm_enabled = true; + } + } else { + if (sgm->dpdm_reg && sgm->dpdm_enabled) { + dev_err(sgm->dev, "disabling DPDM regulator\n"); + rc = regulator_disable(sgm->dpdm_reg); + if (rc < 0) + dev_err(sgm->dev, + "Couldn't disable dpdm regulator rc=%d\n", + rc); + else + sgm->dpdm_enabled = false; + } + } + mutex_unlock(&sgm->regulator_lock); + + return rc; +} + +void sgm4154x_rerun_apsd(struct sgm4154x_device * sgm) +{ + int rc; + + dev_info(sgm->dev, "re-running APSD\n"); + + rc = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_7, SGM4154x_IINDET_EN_MASK, + SGM4154x_IINDET_EN); + if (rc < 0) + dev_err(sgm->dev, "Couldn't re-run APSD rc=%d\n", rc); + else + sgm->real_charger_type = POWER_SUPPLY_TYPE_UNKNOWN; +} + +static bool sgm4154x_is_rerun_apsd_done(struct sgm4154x_device * sgm) +{ + int rc = 0; + int val = 0; + bool result = false; + rc = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_7, &val); + if (rc) + return false; + + result = !(val & SGM4154x_IINDET_EN_MASK); + pr_info("%s:rerun apsd %s", __func__, result ? "done" : "not complete"); + return result; +} + +static void sgm4154x_rerun_apsd_work_func(struct work_struct *work) +{ + struct sgm4154x_device * sgm = NULL; + int rc = 0; + bool apsd_done = false; + int check_count = 0; + + sgm = container_of(work, struct sgm4154x_device, rerun_apsd_work); + if(sgm == NULL) { + pr_err("Cann't get sgm4154x_device\n"); + return; + } + + rc = sgm4154x_get_usb_present(sgm); + if (rc) + return; + + if (!sgm->state.vbus_gd) + return; + + down(&sgm->sem_dpdm); + + rc = sgm4154x_request_dpdm(sgm, true); + if (rc < 0) + dev_err(sgm->dev, "Couldn't to enable DPDM rc=%d\n", rc); + + sgm->typec_apsd_rerun_done = true; + + sgm4154x_rerun_apsd(sgm); + + while(check_count < 10) { + apsd_done = sgm4154x_is_rerun_apsd_done(sgm); + if (apsd_done) { + break; + } + + msleep(100); + check_count ++; + } + + sgm->pulse_cnt = 0; + + up(&sgm->sem_dpdm); + + schedule_work(&sgm->charge_detect_work); +} + +/* for build +static bool sgm4154x_dpdm_detect_is_done(struct sgm4154x_device * sgm) +{ + int chrg_stat; + int ret; + + ret = mmi_regmap_read(sgm, SGM4154x_INPUT_DET, &chrg_stat); + if(ret) { + dev_err(sgm->dev, "Check DPDM detecte error\n"); + } + + return (chrg_stat&SGM4154x_DPDM_ONGOING)?true:false; +} +*/ + +/* enable dynamic adjust battery voltage */ +static void sgm4154x_adjust_constant_voltage(struct sgm4154x_device *sgm, int vbat) +{ + int ret, t1, t2; + int ft[4] = {0, 0x40, 0xC0, 0x80}; + int reg = 0; + + if (sgm->final_cv > vbat && (sgm->final_cv - vbat) < 8000) + return; + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_a, ®); + if ((ret || (reg & 0x60)) && sgm->final_cv > vbat) + return; + if (sgm->final_cv > vbat) { + sgm->cv_tune++; + } else if (sgm->cv_tune > 0) { + sgm->cv_tune--; + } + sgm->cv_tune = min(sgm->cv_tune, 12); //limit to max 12 - 96mV + dev_err(sgm->dev, "cv_tune=%duV\n", sgm->cv_tune*8000); + t1 = sgm->cv_tune / 4; + t2 = sgm->cv_tune - t1*4; + if (t2 == 2 || t2 == 3) + t1++; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_f, SGM4154x_VREG_FT_MASK, ft[t2]); + ret |= sgm4154x_set_chrg_adjust_volt(sgm, sgm->final_cv, t1); + if (!ret) + return; + dev_err(sgm->dev, "failed to tune cv, reset the tuning\n"); + sgm->cv_tune = 0; + sgm4154x_set_chrg_adjust_volt(sgm, sgm->final_cv, 0); + mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_f, SGM4154x_VREG_FT_MASK, 0); + return; +} + +static void charger_monitor_work_func(struct work_struct *work) +{ + int ret = 0; + struct sgm4154x_device * sgm = NULL; + struct delayed_work *charge_monitor_work = NULL; + //static u8 last_chg_method = 0; + struct sgm4154x_state state; + + /* enable dynamic adjust battery voltage */ + union power_supply_propval val_battery = {0}; + int vbat_uv, ibat_ua; + /* enable dynamic adjust battery voltage */ + int soc; + + charge_monitor_work = container_of(work, struct delayed_work, work); + if(charge_monitor_work == NULL) { + pr_err("Cann't get charge_monitor_work\n"); + return ; + } + sgm = container_of(charge_monitor_work, struct sgm4154x_device, charge_monitor_work); + if(sgm == NULL) { + pr_err("Cann't get sgm \n"); + return ; + } + + ret = sgm4154x_get_state(sgm, &state); + mutex_lock(&sgm->lock); + sgm->state = state; + mutex_unlock(&sgm->lock); + + if (!sgm->state.chrg_type) { + pr_err("%s not present vbus_status \n",__func__); + goto OUT; + } + + /* enable dynamic adjust battery voltage */ + if(sgm->enable_dynamic_adjust_batvol){ + if (!sgm->battery) + sgm->battery = power_supply_get_by_name ("battery"); + if (sgm->battery) { + power_supply_get_property(sgm->battery, POWER_SUPPLY_PROP_CURRENT_NOW, &val_battery); + if(val_battery.intval < 10000 && state.chrg_stat != SGM4154x_TERM_CHRG) { + pr_err("VBUS present but no charging current \n"); + //sgm4154x_dump_register(sgm); + } + + ibat_ua = val_battery.intval; + power_supply_get_property(sgm->battery, POWER_SUPPLY_PROP_VOLTAGE_NOW, &val_battery); + vbat_uv = val_battery.intval; + dev_err(sgm->dev, "vbat=%duV, cv=%duV\n", vbat_uv, sgm->final_cv); + dev_err(sgm->dev, "ibat=%duA, cc=%duA,tune=%d\n", ibat_ua, sgm->final_cc,sgm->cv_tune); + if ((ibat_ua > 10000 && ibat_ua < (sgm->final_cc - 100000)) || vbat_uv > sgm->final_cv) { + sgm4154x_adjust_constant_voltage(sgm, vbat_uv); + } + } + } + + /* enable dynamic adjust vindpm */ + if(sgm->enable_dynamic_adjust_vindpm){ + if (!sgm->battery) + sgm->battery = power_supply_get_by_name ("battery"); + if (sgm->battery) { + power_supply_get_property(sgm->battery, POWER_SUPPLY_PROP_CAPACITY, &val_battery); + soc = val_battery.intval; + dev_err(sgm->dev, "soc = %d\n", soc); + if(soc > 80 && sgm->vindpm_flag == false){ + ret = sgm4154x_set_input_volt_lim(sgm, 4700000); + sgm->vindpm_flag = true; + } + if(soc <= 80 && sgm->vindpm_flag){ + ret = sgm4154x_set_input_volt_lim(sgm, sgm->init_data.vlim); + sgm->vindpm_flag = false; + } + } + } + + sgm4154x_dump_register(sgm); + pr_err("%s ret:%d\n",__func__,ret); +OUT: + schedule_delayed_work(&sgm->charge_monitor_work, 10*HZ); +} + +#ifndef CONFIG_MMI_QC3P_WT6670_DETECTED +#define QC3P_AUTHEN_LOW_THR_MV 5500000 +#define QC3P_AUTHEN_HIGH_THR_MV 6500000 +#define QC3P_AUTHEN_NONE_THR_MV 9200000 +#define QC3P_AUTHEN_45W_THR_MV 8200000 +#define QC3P_AUTHEN_27W_THR_MV 7200000 +#define QC3P_AUTHEN_18W_THR_MV 6200000 +static int sgm4154x_detected_qc30_hvdcp(struct sgm4154x_device *sgm, int *charger_type) +{ + int ret = 0; + int dp_val, dm_val; + int i=0, vbus_voltage; + + /* dp 0.6v and dm 3.3v entry QC3.0 mode */ + dp_val = 0x2<<3; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DP_VSEL_MASK, dp_val); //dp 0.6v + if (ret) + return ret; + + dm_val = 0x3<<1; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm 3.3v + if (ret) + return ret; + + mdelay(100); + + for (i = 0; i < 16; i++) { + ret = sgm4154x_qc30_step_up_vbus(sgm); + if (ret) + dev_err(sgm->dev, "%s qc30 step up vbus error\n", __func__); + } + + mdelay(100);//need tunning + + sgm4154x_get_usb_voltage_now(sgm, &vbus_voltage); + dev_info(sgm->dev, "%s vbus voltage now = %d in detected qc30\n", __func__,vbus_voltage); + + if (vbus_voltage > MMI_HVDCP3_VOLTAGE_STANDARD) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + *charger_type = QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3; +#else + *charger_type = POWER_SUPPLY_TYPE_USB_HVDCP_3; +#endif + dev_info(sgm->dev, "%s QC3.0 charger detected\n", __func__); + } + + for (i = 0; i < 16; i++) { + ret = sgm4154x_qc30_step_down_vbus(sgm); + if (ret) + dev_err(sgm->dev, "%s qc30 step down vbus error\n", __func__); + } + +#ifndef CONFIG_MMI_QC3P_TURBO_CHARGER + sgm4154x_get_usb_voltage_now(sgm, &vbus_voltage); + dev_info(sgm->dev, "%s vbus voltage now = %d after detected qc30\n", __func__,vbus_voltage); +#endif + + return ret; +} + +#ifdef CONFIG_MMI_QC3P_TURBO_CHARGER +static int sgm4154x_detected_qc3p_hvdcp(struct sgm4154x_device *sgm, int *charger_type) +{ + int ret = 0; + int dp_val, dm_val; + int i=0, vbus_voltage; + + mdelay(100);//need tunning + + sgm4154x_get_usb_voltage_now(sgm, &vbus_voltage); + + if (vbus_voltage < QC3P_AUTHEN_LOW_THR_MV + || vbus_voltage > QC3P_AUTHEN_HIGH_THR_MV) { + sgm->mmi_qc3p_power = MMI_POWER_SUPPLY_QC3P_NONE; + /*do qc3p rerun*/ + dev_err(sgm->dev, "qc3p voltage is invalid, rerun qc3p detect\n"); + if (!sgm->mmi_qc3p_rerun_done && !sgm->pd_active) { + sgm->mmi_qc3p_rerun_done = true; + sgm->mmi_qc3p_wa = true; + dp_val = 0x0<<3; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DP_VSEL_MASK, dp_val); //dp HIZ + if (ret) + return ret; + + dm_val = 0x0<<1; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm HIZ + if (ret) + return ret; + + msleep(30);//need tunning + + schedule_work(&sgm->rerun_apsd_work); + return -1; + } + + return 0; + } + + for (i = 0;i < 3; i++) { + ret = sgm4154x_qc30_step_up_vbus(sgm); + if (ret) + dev_err(sgm->dev, "%s qc3p step up vbus error\n", __func__); + + ret = sgm4154x_qc30_step_down_vbus(sgm); + if (ret) + dev_err(sgm->dev, "%s qc3p step down vbus error\n", __func__); + + } + + mdelay(30);//need tuning + + sgm4154x_get_usb_voltage_now(sgm, &vbus_voltage); + + for (i = 0; i < 2; i++) { + ret = sgm4154x_qc30_step_up_vbus(sgm); + if (ret) + dev_err(sgm->dev, "%s qc3p step up vbus error\n", __func__); + } + + for (i = 0; i < 2; i++) { + ret = sgm4154x_qc30_step_down_vbus(sgm); + if (ret) + dev_err(sgm->dev, "%s qc3p step down vbus error\n", __func__); + } + + msleep(30); + + dev_info(sgm->dev, "%s vbus voltage now = %d after detected qc3p\n", __func__,vbus_voltage); + + if (vbus_voltage > QC3P_AUTHEN_NONE_THR_MV) + sgm->mmi_qc3p_power = MMI_POWER_SUPPLY_QC3P_NONE; + else if (vbus_voltage > QC3P_AUTHEN_45W_THR_MV) + sgm->mmi_qc3p_power = MMI_POWER_SUPPLY_QC3P_45W; + else if (vbus_voltage > QC3P_AUTHEN_27W_THR_MV) + sgm->mmi_qc3p_power = MMI_POWER_SUPPLY_QC3P_27W; + else if (vbus_voltage > QC3P_AUTHEN_18W_THR_MV) + sgm->mmi_qc3p_power = MMI_POWER_SUPPLY_QC3P_18W; + else + sgm->mmi_qc3p_power = MMI_POWER_SUPPLY_QC3P_NONE; + + if (sgm->mmi_qc3p_power != MMI_POWER_SUPPLY_QC3P_NONE) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + *charger_type = QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5; +#else + *charger_type = POWER_SUPPLY_TYPE_USB_HVDCP_3P5; +#endif + dev_info(sgm->dev, "%s detected qc3p, qc3p power = %d\n", + __func__,sgm->mmi_qc3p_power); + } else { + dev_err(sgm->dev, "qc3p power is invalid, rerun qc3p detect\n"); + //do rerun qc3p + if (!sgm->mmi_qc3p_rerun_done && !sgm->pd_active) { + sgm->mmi_qc3p_rerun_done = true; + sgm->mmi_qc3p_wa = true; + dp_val = 0x0<<3; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DP_VSEL_MASK, dp_val); //dp HIZ + if (ret) + return ret; + + dm_val = 0x0<<1; + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_d, + SGM4154x_DM_VSEL_MASK, dm_val); //dm HIZ + if (ret) + return ret; + + msleep(30);//need tunning + + schedule_work(&sgm->rerun_apsd_work); + return -1; + } + + } + + return ret; +} +#endif +#endif + +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED +bool is_chan_valid(struct sgm4154x_device *chip, + enum mmi_qc3p_ext_iio_channels chan) +{ + int rc; + + if (IS_ERR(chip->ext_iio_chans[chan])) + return false; + + if (!chip->ext_iio_chans[chan]) { + chip->ext_iio_chans[chan] = iio_channel_get(chip->dev, + mmi_qc3p_ext_iio_chan_name[chan]); + if (IS_ERR(chip->ext_iio_chans[chan])) { + rc = PTR_ERR(chip->ext_iio_chans[chan]); + if (rc == -EPROBE_DEFER) + chip->ext_iio_chans[chan] = NULL; + + dev_info(chip->dev, "Failed to get IIO channel %s, rc=%d\n", + mmi_qc3p_ext_iio_chan_name[chan], rc); + return false; + } + } + + return true; +} + +int mmi_charger_read_iio_chan(struct sgm4154x_device *chip, + enum mmi_qc3p_ext_iio_channels chan, int *val) +{ + int rc; + + if (is_chan_valid(chip, chan)) { + rc = iio_read_channel_processed( + chip->ext_iio_chans[chan], val); + return (rc < 0) ? rc : 0; + } + + return -EINVAL; +} + +int mmi_charger_write_iio_chan(struct sgm4154x_device *chip, + enum mmi_qc3p_ext_iio_channels chan, int val) +{ + if (is_chan_valid(chip, chan)) + return iio_write_channel_raw(chip->ext_iio_chans[chan], + val); + + return -EINVAL; +} + +static int mmi_init_iio_psy(struct sgm4154x_device *chip, + struct device *dev) +{ + chip->ext_iio_chans = devm_kcalloc(chip->dev, + ARRAY_SIZE(mmi_qc3p_ext_iio_chan_name), + sizeof(*chip->ext_iio_chans), + GFP_KERNEL); + if (!chip->ext_iio_chans) + return -ENOMEM; + + return 0; +} + +int qc3p_start_detection(struct sgm4154x_device *chip) +{ + int ret = 0; + ret = mmi_charger_write_iio_chan(chip, SMB5_QC3P_START_DETECT, true); + if(ret ) + dev_err(chip->dev, "Cann't write SMB5_QC3P_START_DETECT IIO\n"); + + dev_info(chip->dev, "write SMB5_QC3P_START_DETECT IIO success\n"); + return 0; +} + +bool qc3p_detection_done(struct sgm4154x_device *chip) +{ + int ret = 0; + int val = 0; + int delay_count =0; + +#ifdef CONFIG_MMI_SGM41513_CHARGER +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + if(chip->real_charger_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP) { +#else + if(chip->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP) { +#endif + dev_info(chip->dev, "qc3p hvdcp detection already done, do not wait\n"); + return true; + } +#endif + + do { + ret = mmi_charger_read_iio_chan(chip, SMB5_QC3P_DETECTION_READY, &val); + if(ret ) + dev_err(chip->dev, "Cann't read SMB5_QC3P_DETECTION_READY IIO\n"); + + dev_info(chip->dev, "read SMB5_QC3P_DETECTION_READY IIO :%d\n",val); + + msleep(100); + delay_count ++; + }while(val == false && delay_count <= 35); + + dev_info(chip->dev, "read SMB5_QC3P_DETECTION_READY IIO :%d\n",val); + return val; +} + +int qc3p_read_charger_type(struct sgm4154x_device *chip) +{ + int ret = 0; + int val = 0; + + ret = mmi_charger_read_iio_chan(chip, SMB5_USB_REAL_TYPE, &val); + if(ret ) + dev_err(chip->dev, "Cann't read SMB5_USB_REAL_TYPE IIO\n"); + + dev_info(chip->dev, "read SMB5_USB_REAL_TYPE IIO :%d\n",val); + return val; +} + +bool qc3p_update_policy(struct sgm4154x_device *chip ) +{ + int ret = 0; + int val = 0; + + ret = mmi_charger_write_iio_chan(chip, SMB5_QC3P_START_POLICY, val); + if(ret ) + dev_err(chip->dev, "Cann't write SMB5_QC3P_START_POLICY IIO\n"); + + dev_info(chip->dev, "write SMB5_QC3P_START_POLICY IIO :%d\n",val); + return val; +} + +#ifdef CONFIG_MMI_SGM41513_CHARGER +int bc12_start_detection(struct sgm4154x_device *chip) +{ + int ret = 0; + ret = mmi_charger_write_iio_chan(chip, SMB5_BC12_START_DETECT, true); + if(ret ) + dev_err(chip->dev, "Cann't write SMB5_BC12_START_DETECT IIO\n"); + + dev_info(chip->dev, "write SMB5_BC12_START_DETECT IIO success\n"); + return 0; +} + +bool bc12_detection_done(struct sgm4154x_device *chip) +{ + int ret = 0; + int val = 0; + int delay_count =0; + + do { + ret = mmi_charger_read_iio_chan(chip, SMB5_BC12_DETECTION_READY, &val); + if(ret ) + dev_err(chip->dev, "Cann't read SMB5_BC12_DETECTION_READY IIO\n"); + + dev_info(chip->dev, "read SMB5_BC12_DETECTION_READY IIO :%d\n",val); + if (val) + break; + + msleep(50); + delay_count ++; + }while(delay_count <= 20); + + dev_info(chip->dev, "read SMB5_BC12_DETECTION_READY IIO :%d\n",val); + return val; +} + +int bc12_read_charger_type(struct sgm4154x_device *chip) +{ + int ret = 0; + int val = 0; + + ret = mmi_charger_read_iio_chan(chip, SMB5_READ_BC12_CHG_TYPE, &val); + if(ret ) + dev_err(chip->dev, "Cann't read SMB5_READ_BC12_CHG_TYPE IIO\n"); + + dev_info(chip->dev, "read SMB5_READ_BC12_CHG_TYPE IIO :%d\n",val); + return val; +} +#endif +#endif + +static int mmi_hvdcp_detect_kthread(void *param) +{ + struct sgm4154x_device * sgm = param; + int ret; + int charger_type = POWER_SUPPLY_TYPE_UNKNOWN; + struct sched_param sch_param = {.sched_priority = MAX_RT_PRIO-1}; + + sched_setscheduler(current, SCHED_FIFO, &sch_param); + + do { + + wait_event_interruptible(sgm->mmi_qc3_wait_que, sgm->mmi_qc3_trig_flag || kthread_should_stop()); + if (kthread_should_stop()) + break; + + down(&sgm->sem_dpdm); + dev_info(sgm->dev, "mmi_hvdcp_detect_kthread begin\n"); + sgm->mmi_qc3_trig_flag = false; + sgm->mmi_is_qc3_authen = true; + sgm4154x_set_input_curr_lim(sgm, MMI_HVDCP_DETECT_ICL_LIMIT); + +#ifndef CONFIG_MMI_QC3P_WT6670_DETECTED + //do qc2.0 detected + ret = sgm4154x_detected_qc20_hvdcp(sgm, &charger_type); + if (ret) { + dev_err(sgm->dev, "Cann't detected qc20 hvdcp\n"); + goto out; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + if (charger_type != QTI_POWER_SUPPLY_TYPE_USB_HVDCP || sgm->pd_active) +#else + if (charger_type != POWER_SUPPLY_TYPE_USB_HVDCP || sgm->pd_active) +#endif + goto out; + + //do qc3.0 detected + ret = sgm4154x_detected_qc30_hvdcp(sgm, &charger_type); + if (ret) { + dev_err(sgm->dev, "Cann't detected qc30 hvdcp\n"); + goto out; + } + +#ifdef CONFIG_MMI_QC3P_TURBO_CHARGER + //do qc3p detected +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + if (charger_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3 && !sgm->pd_active) { +#else + if (charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3 && !sgm->pd_active) { +#endif + ret = sgm4154x_detected_qc3p_hvdcp(sgm, &charger_type); + if (ret) { + dev_err(sgm->dev, "Cann't detected qc3p hvdcp\n"); + goto out; + } + } +#endif +#else + sgm4154x_enable_termination(sgm->chg_dev, true); + qc3p_start_detection(sgm); + qc3p_detection_done(sgm); + ret = qc3p_read_charger_type(sgm); + + switch(ret) { + case WT_CHG_TYPE_QC2: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + charger_type = QTI_POWER_SUPPLY_TYPE_USB_HVDCP; +#else + charger_type = POWER_SUPPLY_TYPE_USB_HVDCP; +#endif + dev_info(sgm->dev, "HDVCP 2.0 detected\n"); + break; + case WT_CHG_TYPE_QC3: + case WT_CHG_TYPE_QC3P_18W: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + charger_type = QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3; +#else + charger_type = POWER_SUPPLY_TYPE_USB_HVDCP_3; +#endif + dev_info(sgm->dev, "HDVCP 3.0 or qc3p 18W detected\n"); + break; + case WT_CHG_TYPE_QC3P_27W: + dev_err(sgm->dev, "QC3P 27W have been detected !\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + charger_type = QTI_POWER_SUPPLY_TYPE_USB_HVDCP_3P5; +#else + charger_type = POWER_SUPPLY_TYPE_USB_HVDCP_3P5; +#endif + qc3p_update_policy(sgm); + break; + default: + dev_info(sgm->dev, "No HDVCP detected\n"); + goto out; + } +#endif + sgm4154x_get_usb_present(sgm); + if (!sgm->state.vbus_gd || sgm->pd_active) + goto out; + + sgm->real_charger_type = charger_type; + //notify charging policy to update charger type + sgm->chg_dev->noti.hvdcp_done = true; + charger_dev_notify(sgm->chg_dev); + + out: + sgm4154x_set_input_curr_lim(sgm, sgm->input_current_cache); + sgm->mmi_is_qc3_authen = false; + charger_type = POWER_SUPPLY_TYPE_UNKNOWN; + up(&sgm->sem_dpdm); + dev_info(sgm->dev, "mmi_hvdcp_detect_kthread end\n"); + }while(!kthread_should_stop()); + + dev_dbg(sgm->dev, "qc3 kthread stop\n"); + return 0; +} + +static void mmi_start_hvdcp_detect(struct sgm4154x_device *sgm) +{ + + if (sgm->mmi_qc3_support + && (!sgm->pd_active) + && (sgm->real_charger_type == POWER_SUPPLY_TYPE_USB_DCP +#ifdef CONFIG_MMI_SGM41513_CHARGER +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + || sgm->real_charger_type == QTI_POWER_SUPPLY_TYPE_USB_HVDCP +#else + || sgm->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP +#endif +#endif + )) { + dev_info(sgm->dev, "start hvdcp detect\n"); + sgm->mmi_qc3_trig_flag = true; + wake_up_interruptible(&sgm->mmi_qc3_wait_que); + } +} + +#ifdef CONFIG_MMI_SGM41513_CHARGER +static bool mmi_start_bc12_charger_type_detect(struct sgm4154x_device *sgm, int *real_charger_type) +{ + bool result; + int ret; + + bc12_start_detection(sgm); + bc12_detection_done(sgm); + ret = bc12_read_charger_type(sgm); + + switch(ret) { + case WT6670_CHG_TYPE_UNKNOWN: + case WT_CHG_TYPE_FC: + case WT_CHG_TYPE_OCP: + dev_err(sgm->dev, "unkown type have been detected !\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + *real_charger_type = QTI_POWER_SUPPLY_TYPE_USB_FLOAT; +#else + *real_charger_type = POWER_SUPPLY_TYPE_USB_FLOAT; +#endif + result = true; + break; + case WT_CHG_TYPE_SDP: + dev_err(sgm->dev, "SDP have been detected !\n"); + *real_charger_type = POWER_SUPPLY_TYPE_USB; + result = true; + break; + case WT_CHG_TYPE_CDP: + dev_err(sgm->dev, "CDP have been detected !\n"); + *real_charger_type = POWER_SUPPLY_TYPE_USB_CDP; + result = true; + break; + case WT_CHG_TYPE_DCP: + dev_err(sgm->dev, "DCP have been detected !\n"); + *real_charger_type = POWER_SUPPLY_TYPE_USB_DCP; + mmi_start_hvdcp_detect(sgm); + result = true; + break; + case WT_CHG_TYPE_HVDCP: + dev_err(sgm->dev, "quick plug out/in, HVDCP have been detected already !\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + *real_charger_type = QTI_POWER_SUPPLY_TYPE_USB_HVDCP; +#else + *real_charger_type = POWER_SUPPLY_TYPE_USB_HVDCP; +#endif + mmi_start_hvdcp_detect(sgm); + result = true; + break; + default: + pr_err("bc12 charger type: default\n"); + result = false; + break; + } + +#ifdef __SGM41513_CHIP_ID__ + if (*real_charger_type == POWER_SUPPLY_TYPE_USB_CDP) { + ret = sgm4154x_set_dynamic_vindpm_track(sgm, sgm->init_data.vdpm_bat_track); + if (ret) { + dev_err(sgm->dev, "%s set dynamic vindpm track:%d failed\n", __func__, sgm->init_data.vdpm_bat_track); + } + } else { + ret = sgm4154x_set_dynamic_vindpm_track(sgm, 0); + if (ret) { + dev_err(sgm->dev, "%s disable dynamic vindpm track failed\n", __func__); + } + } +#endif + + return result; +} +#endif + +static void sgm4154x_vbus_remove(struct sgm4154x_device * sgm) +{ + /* enable dynamic adjust vindpm */ + int ret = 0; + dev_err(sgm->dev, "Vbus removed, disable charge\n"); + +#ifdef CONFIG_MMI_QC3P_TURBO_CHARGER + sgm4154x_enable_termination(sgm->chg_dev, true); +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED + qc3p_update_policy(sgm); +#endif +#endif + cancel_delayed_work_sync(&sgm->charge_monitor_work); + /* enable dynamic adjust battery voltage */ + if(sgm->enable_dynamic_adjust_batvol){ + sgm->cv_tune = 0; + sgm4154x_set_chrg_adjust_volt(sgm, sgm->final_cv, 0); + mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_f, SGM4154x_VREG_FT_MASK, 0); + } + + /* enable dynamic adjust vindpm */ + if(sgm->enable_dynamic_adjust_vindpm){ + ret = sgm4154x_set_input_volt_lim(sgm, sgm->init_data.vlim); + sgm->vindpm_flag = false; + } + + sgm->pulse_cnt = 0; + sgm->mmi_qc3p_rerun_done = false; + sgm->mmi_qc3p_wa = false; + sgm->typec_apsd_rerun_done = false; + sgm->chg_dev->noti.apsd_done = false; + sgm->chg_dev->noti.hvdcp_done = false; + sgm->real_charger_type = POWER_SUPPLY_TYPE_UNKNOWN; + sgm->mmi_qc3p_power = MMI_POWER_SUPPLY_QC3P_NONE; + sgm4154x_request_dpdm(sgm, false); + charger_dev_notify(sgm->chg_dev); + +#ifdef __SGM41513_CHIP_ID__ + sgm4154x_set_dynamic_vindpm_track(sgm, 0); +#endif + pr_info("%s ret:%d\n",__func__,ret); +} + +static void sgm4154x_vbus_plugin(struct sgm4154x_device * sgm) +{ +#ifdef CONFIG_MMI_SGM41513_CHARGER + bool ret; +#endif +#if defined(__SGM41542_CHIP_ID__)|| defined(__SGM41516D_CHIP_ID__) || defined(__SGM41513_CHIP_ID__) + if (((sgm->state.chrg_type == SGM4154x_USB_SDP) || + (sgm->state.chrg_type == SGM4154x_NON_STANDARD) || + (sgm->state.chrg_type == SGM4154x_UNKNOWN)) + && (!sgm->typec_apsd_rerun_done)) { + dev_err(sgm->dev, "rerun apsd for 0x%x\n", sgm->state.chrg_type); + schedule_work(&sgm->rerun_apsd_work); + return; + } +#ifdef CONFIG_MMI_QC3P_TURBO_CHARGER + if (true == sgm->mmi_qc3p_wa) { + if (sgm->state.chrg_type != SGM4154x_USB_DCP) { + pr_err("SGM4154x charger type fix to DCP to workaroud BC1.2 rerun fail \n"); + sgm->state.chrg_type = SGM4154x_USB_DCP; + } + } +#endif + + switch(sgm->state.chrg_type) { + case SGM4154x_USB_SDP: + pr_err("SGM4154x charger type: SDP\n"); +#ifndef CONFIG_MMI_SGM41513_CHARGER + sgm->real_charger_type = POWER_SUPPLY_TYPE_USB; +#else + ret = mmi_start_bc12_charger_type_detect(sgm, &sgm->real_charger_type); + if (!ret) + return; +#endif + break; + + case SGM4154x_USB_CDP: + pr_err("SGM4154x charger type: CDP\n"); + sgm->real_charger_type = POWER_SUPPLY_TYPE_USB_CDP; + break; + + case SGM4154x_USB_DCP: + pr_err("SGM4154x charger type: DCP\n"); + sgm->real_charger_type = POWER_SUPPLY_TYPE_USB_DCP; + mmi_start_hvdcp_detect(sgm); + break; + + case SGM4154x_UNKNOWN: + case SGM4154x_NON_STANDARD: + pr_err("SGM4154x charger type: UNKNOWN\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + sgm->real_charger_type = QTI_POWER_SUPPLY_TYPE_USB_FLOAT; +#else + sgm->real_charger_type = POWER_SUPPLY_TYPE_USB_FLOAT; +#endif + break; + + case SGM4154x_OTG_MODE: + pr_err("SGM4154x OTG mode do nothing\n"); + return; + + default: + pr_err("SGM4154x charger type: default\n"); + return; + } +#endif + + schedule_delayed_work(&sgm->charge_monitor_work, 0); + + //notify charging policy to update charger type + sgm->chg_dev->noti.apsd_done = true; + sgm->typec_apsd_rerun_done = false; + charger_dev_notify(sgm->chg_dev); +} + +static void charger_detect_work_func(struct work_struct *work) +{ + struct sgm4154x_device * sgm = NULL; + struct sgm4154x_state state; + bool reapsd_complete = false; + int ret; + + sgm = container_of(work, struct sgm4154x_device, charge_detect_work); + if(sgm == NULL) { + pr_err("Cann't get sgm4154x_device\n"); + goto err; + } + + if (!sgm->charger_wakelock->active) + __pm_stay_awake(sgm->charger_wakelock); + + if (!sgm->dpdm_enabled) { + sgm4154x_request_dpdm(sgm, true); + } + + ret = sgm4154x_get_state(sgm, &state); + if (ret) + goto err; + + mutex_lock(&sgm->lock); + sgm->state = state; + mutex_unlock(&sgm->lock); + + if(sgm->state.vbus_gd && sgm->state.online + && (sgm->typec_apsd_rerun_done == true) + && (sgm->state.chrg_type != SGM4154x_USB_NONE) + && (sgm->state.chrg_type != SGM4154x_OTG_MODE)) { + reapsd_complete = sgm4154x_is_rerun_apsd_done(sgm); + } + + if (((!sgm->state.vbus_gd) || (sgm->state.chrg_type == SGM4154x_OTG_MODE)) + && (sgm->status & SGM4154X_STATUS_PLUGIN)) { + + dev_info(sgm->dev, "%s:adapter removed\n", __func__); + sgm->status &= ~SGM4154X_STATUS_PLUGIN; + sgm4154x_vbus_remove(sgm); + } else if ((sgm->state.chrg_type != SGM4154x_USB_NONE) + && (sgm->state.chrg_type != SGM4154x_OTG_MODE) + && (!(sgm->status & SGM4154X_STATUS_PLUGIN) || (reapsd_complete == true)) + && sgm->state.online) { + + dev_info(sgm->dev, "%s:adapter plugged in\n", __func__); + sgm->status |= SGM4154X_STATUS_PLUGIN; + sgm4154x_vbus_plugin(sgm); + } + +err: + //release wakelock + dev_err(sgm->dev, "Relax wakelock\n"); + __pm_relax(sgm->charger_wakelock); + return; +} + +static irqreturn_t sgm4154x_irq_handler_thread(int irq, void *private) +{ + struct sgm4154x_device *sgm = private; + + //lock wakelock + pr_err("%s entry\n",__func__); + #if defined(__SGM41542_CHIP_ID__)|| defined(__SGM41516D_CHIP_ID__) || defined(__SGM41513_CHIP_ID__) + schedule_work(&sgm->charge_detect_work); + //power_supply_changed(sgm->charger); + #endif + return IRQ_HANDLED; +} + +static enum power_supply_property sgm4154x_power_supply_props[] = { + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, + POWER_SUPPLY_PROP_CHARGE_TYPE, + POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_PRESENT +}; + +static char *sgm4154x_charger_supplied_to[] = { + "battery", +}; + +static struct power_supply_desc sgm4154x_power_supply_desc = { + .name = "charger", + .type = POWER_SUPPLY_TYPE_UNKNOWN, + .usb_types = sgm4154x_usb_type, + .num_usb_types = ARRAY_SIZE(sgm4154x_usb_type), + .properties = sgm4154x_power_supply_props, + .num_properties = ARRAY_SIZE(sgm4154x_power_supply_props), + .get_property = sgm4154x_charger_get_property, + .set_property = sgm4154x_charger_set_property, + .property_is_writeable = sgm4154x_property_is_writeable, +}; + +static const struct regmap_config sgm4154x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = SGM4154x_CHRG_CTRL_f, +}; + +static int sgm4154x_power_supply_init(struct sgm4154x_device *sgm, + struct device *dev) +{ + struct power_supply_config psy_cfg = { .drv_data = sgm, + .of_node = dev->of_node, }; + + psy_cfg.supplied_to = sgm4154x_charger_supplied_to; + psy_cfg.num_supplicants = ARRAY_SIZE(sgm4154x_charger_supplied_to); + + sgm->charger = devm_power_supply_register(sgm->dev, + &sgm4154x_power_supply_desc, + &psy_cfg); + if (IS_ERR(sgm->charger)) + return -EINVAL; + + return 0; +} + +static int sgm4154x_hw_init(struct sgm4154x_device *sgm) +{ + int ret = 0; + struct power_supply_battery_info bat_info = { }; + + bat_info.constant_charge_current_max_ua = + SGM4154x_ICHRG_I_DEF_uA; + + bat_info.constant_charge_voltage_max_uv = + SGM4154x_VREG_V_DEF_uV; + + bat_info.precharge_current_ua = + SGM4154x_PRECHRG_I_DEF_uA; + + bat_info.charge_term_current_ua = + SGM4154x_TERMCHRG_I_DEF_uA; + + sgm->init_data.max_ichg = + SGM4154x_ICHRG_I_MAX_uA; + + sgm->init_data.max_vreg = + SGM4154x_VREG_V_MAX_uV; + + //OTG setting + sgm4154x_set_otg_voltage(sgm, 5000000); //5V + sgm4154x_set_otg_current(sgm, 2000000); //2A + + ret = sgm4154x_set_ichrg_curr(sgm, + bat_info.constant_charge_current_max_ua); + if (ret) + goto err_out; + + ret = sgm4154x_set_prechrg_curr(sgm, bat_info.precharge_current_ua); + if (ret) + goto err_out; + + /* enable dynamic adjust battery voltage */ + if(sgm->enable_dynamic_adjust_batvol) + ret = sgm4154x_set_chrg_adjust_volt(sgm, + bat_info.constant_charge_voltage_max_uv, 0); + else + ret = sgm4154x_set_chrg_volt(sgm, + bat_info.constant_charge_voltage_max_uv); + if (ret) + goto err_out; + + ret = sgm4154x_set_term_curr(sgm, bat_info.charge_term_current_ua); + if (ret) + goto err_out; + + ret = sgm4154x_set_input_volt_lim(sgm, sgm->init_data.vlim); + if (ret) + goto err_out; + + ret = sgm4154x_set_input_curr_lim(sgm, sgm->init_data.ilim); + if (ret) + goto err_out; + + ret = sgm4154x_set_vac_ovp(sgm);//14V + if (ret) + goto err_out; + +#ifdef __SGM41513_CHIP_ID__ + ret = sgm4154x_set_dynamic_vindpm_track(sgm, 0); + if (ret) + goto err_out; +#endif + +#ifdef CONFIG_MMI_QC3P_TURBO_CHARGER + ret = sgm4154x_set_recharge_volt(sgm, 100);//100~200mv + if (ret) + goto err_out; +#else + ret = sgm4154x_set_recharge_volt(sgm, 200);//100~200mv + if (ret) + goto err_out; +#endif + ret = sgm4154x_enable_hw_jeita(sgm->chg_dev, false); + if (ret) + goto err_out; + + ret = sgm4154x_disable_watchdog(sgm); + if (ret) + goto err_out; + + ret = sgm4154x_disable_vindpm_int_pulse(sgm); + if (ret) + goto err_out; + + ret = sgm4154x_disable_iindpm_int_pulse(sgm); + if (ret) + goto err_out; + + ret = sgm4154x_enable_charger(sgm); + if (ret) + goto err_out; + +#ifdef __indicator_led_en__ + ret = sgm4154x_disable_indicator_led(sgm); + if (ret) + goto err_out; +#endif + dev_notice(sgm->dev, "ichrg_curr:%d prechrg_curr:%d chrg_vol:%d" + " term_curr:%d input_curr_lim:%d", + bat_info.constant_charge_current_max_ua, + bat_info.precharge_current_ua, + bat_info.constant_charge_voltage_max_uv, + bat_info.charge_term_current_ua, + sgm->init_data.ilim); + + return 0; + +err_out: + return ret; +} + +static int sgm4154x_parse_dt(struct sgm4154x_device *sgm) +{ + int ret; + u32 val = 0; + int irq_gpio = 0, irqn = 0; + int chg_en_gpio = 0; + + if (of_property_read_string(sgm->dev->of_node, "charger_name", + &sgm->chg_dev_name) < 0) { + sgm->chg_dev_name = "master_chg"; + pr_warn("no charger name\n"); + } + + sgm->mmi_qc3_support = of_property_read_bool(sgm->dev->of_node, "mmi,qc3-support"); + + ret = device_property_read_u32(sgm->dev, + "mmi,wls-max-icl", + &sgm->wls_max_icl); + if (ret) + sgm->wls_max_icl = 0; + + dev_info(sgm->dev, "mmi,wls-max-icl = %d\n",sgm->wls_max_icl); + + sgm->i2c_err_wa_dis = of_property_read_bool(sgm->dev->of_node, "mmi,i2c-err-wa-dis"); + + #if 0 + ret = device_property_read_u32(sgm->dev, "watchdog-timer", + &sgm->watchdog_timer); + if (ret) + sgm->watchdog_timer = SGM4154x_WATCHDOG_DIS; + + if (sgm->watchdog_timer > SGM4154x_WATCHDOG_MAX || + sgm->watchdog_timer < SGM4154x_WATCHDOG_DIS) + return -EINVAL; + #endif + ret = device_property_read_u32(sgm->dev, + "input-voltage-limit-microvolt", + &sgm->init_data.vlim); + if (ret) + sgm->init_data.vlim = SGM4154x_VINDPM_DEF_uV; + + if (sgm->init_data.vlim > SGM4154x_VINDPM_V_MAX_uV || + sgm->init_data.vlim < SGM4154x_VINDPM_V_MIN_uV) + return -EINVAL; + + ret = device_property_read_u32(sgm->dev, + "input-current-limit-microamp", + &sgm->init_data.ilim); + if (ret) + sgm->init_data.ilim = SGM4154x_IINDPM_DEF_uA; + + if (sgm->init_data.ilim > SGM4154x_IINDPM_I_MAX_uA || + sgm->init_data.ilim < SGM4154x_IINDPM_I_MIN_uA) + return -EINVAL; + +#ifdef __SGM41513_CHIP_ID__ + if (of_property_read_u32(sgm->dev->of_node, "dynamic-vindpm-tracking-uv", &val) >= 0) { + sgm->init_data.vdpm_bat_track = val; + dev_err(sgm->dev, "dynamic-vindpm-tracking-uv: %d\n", sgm->init_data.vdpm_bat_track); + } else { + dev_err(sgm->dev, "no dynamic-vindpm-tracking-uv\n"); + sgm->init_data.vdpm_bat_track = 0; + } +#endif + + irq_gpio = of_get_named_gpio(sgm->dev->of_node, "sgm,irq-gpio", 0); + if (!gpio_is_valid(irq_gpio)) + { + dev_err(sgm->dev, "%s: %d gpio get failed\n", __func__, irq_gpio); + return -EINVAL; + } + ret = gpio_request(irq_gpio, "sgm4154x irq pin"); + if (ret) { + dev_err(sgm->dev, "%s: %d gpio request failed\n", __func__, irq_gpio); + return ret; + } + gpio_direction_input(irq_gpio); + irqn = gpio_to_irq(irq_gpio); + if (irqn < 0) { + dev_err(sgm->dev, "%s:%d gpio_to_irq failed\n", __func__, irqn); + return irqn; + } + sgm->client->irq = irqn; + + chg_en_gpio = of_get_named_gpio(sgm->dev->of_node, "sgm,chg-en-gpio", 0); + if (gpio_is_valid(chg_en_gpio)) + { + ret = gpio_request(chg_en_gpio, "sgm4154x chg en pin"); + if (ret) { + dev_err(sgm->dev, "%s: %d gpio request chg_en failed\n", __func__, chg_en_gpio); + return ret; + } + gpio_direction_output(chg_en_gpio,0);//default enable charge + } + + /*wls outout en control*/ + sgm->wls_en_gpio = of_get_named_gpio(sgm->dev->of_node, "mmi,wls-en-gpio", 0); + if (gpio_is_valid(sgm->wls_en_gpio)) + { + ret = gpio_request(sgm->wls_en_gpio, "mmi wls en pin"); + if (ret) { + dev_err(sgm->dev, "%s: %d gpio(wls en) request failed\n", __func__, sgm->wls_en_gpio); + return ret; + } + + gpio_direction_output(sgm->wls_en_gpio, 0);//default enable wls charge + } + + /* 18W iindpm comp */ + sgm->sgm_18W_iindpm_comp = of_property_read_bool(sgm->dev->of_node, "sgm,18w_iindpm_comp"); + /* sw jeita */ + sgm->enable_sw_jeita = of_property_read_bool(sgm->dev->of_node, "enable_sw_jeita"); + /* enable dynamic adjust battery voltage */ + sgm->enable_dynamic_adjust_batvol = of_property_read_bool(sgm->dev->of_node, "enable_dynamic_adjust_batvol"); + dev_err(sgm->dev, "%s: enable_dynamic_adjust_batvol = %d \n", __func__, sgm->enable_dynamic_adjust_batvol); + + /* enable dynamic adjust vindpm */ + sgm->enable_dynamic_adjust_vindpm = of_property_read_bool(sgm->dev->of_node, "enable_dynamic_adjust_vindpm"); + dev_err(sgm->dev, "%s: enable_dynamic_adjust_vindpm = %d \n", __func__, sgm->enable_dynamic_adjust_vindpm); + if(sgm->enable_dynamic_adjust_vindpm){ + sgm->vindpm_flag = false; + } + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_above_t4_cv", &val) >= 0) + sgm->data.jeita_temp_above_t4_cv = val; + else { + dev_err(sgm->dev, "use default JEITA_TEMP_ABOVE_T4_CV:%d\n",JEITA_TEMP_ABOVE_T4_CV); + sgm->data.jeita_temp_above_t4_cv = JEITA_TEMP_ABOVE_T4_CV; + } + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_t3_to_t4_cv", &val) >= 0) + sgm->data.jeita_temp_t3_to_t4_cv = val; + else { + dev_err(sgm->dev, "use default JEITA_TEMP_T3_TO_T4_CV:%d\n",JEITA_TEMP_T3_TO_T4_CV); + sgm->data.jeita_temp_t3_to_t4_cv = JEITA_TEMP_T3_TO_T4_CV; + } + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_t2_to_t3_cv", &val) >= 0) + sgm->data.jeita_temp_t2_to_t3_cv = val; + else { + dev_err(sgm->dev, "use default JEITA_TEMP_T2_TO_T3_CV:%d\n",JEITA_TEMP_T2_TO_T3_CV); + sgm->data.jeita_temp_t2_to_t3_cv = JEITA_TEMP_T2_TO_T3_CV; + } + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_t1_to_t2_cv", &val) >= 0) + sgm->data.jeita_temp_t1_to_t2_cv = val; + else { + dev_err(sgm->dev, "use default JEITA_TEMP_T1_TO_T2_CV:%d\n",JEITA_TEMP_T1_TO_T2_CV); + sgm->data.jeita_temp_t1_to_t2_cv = JEITA_TEMP_T1_TO_T2_CV; + } + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_t0_to_t1_cv", &val) >= 0) + sgm->data.jeita_temp_t0_to_t1_cv = val; + else { + dev_err(sgm->dev, "use default JEITA_TEMP_T0_TO_T1_CV:%d\n",JEITA_TEMP_T0_TO_T1_CV); + sgm->data.jeita_temp_t0_to_t1_cv = JEITA_TEMP_T0_TO_T1_CV; + } + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_below_t0_cv", &val) >= 0) + sgm->data.jeita_temp_below_t0_cv = val; + else { + dev_err(sgm->dev, "use default JEITA_TEMP_BELOW_T0_CV:%d\n",JEITA_TEMP_BELOW_T0_CV); + sgm->data.jeita_temp_below_t0_cv = JEITA_TEMP_BELOW_T0_CV; + } + pr_err("%s,enable_sw_jeita = %d,CV1 = %d,CV2 = %d,CV3 = %d,CV4 = %d,CV5 = %d,CV6 = %d\n",__func__, + sgm->enable_sw_jeita,sgm->data.jeita_temp_above_t4_cv,sgm->data.jeita_temp_t3_to_t4_cv, + sgm->data.jeita_temp_t2_to_t3_cv,sgm->data.jeita_temp_t1_to_t2_cv, + sgm->data.jeita_temp_t0_to_t1_cv,sgm->data.jeita_temp_below_t0_cv); + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_above_t4_cc_current", &val) >= 0) + sgm->data.jeita_temp_above_t4_cc_current = val; + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_t3_to_t4_cc_current", &val) >= 0) + sgm->data.jeita_temp_t3_to_t4_cc_current = val; + else + sgm->data.jeita_temp_t3_to_t4_cc_current = JEITA_TEMP_T3_TO_T4_CC_CURRENT; + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_t2_to_t3_cc_current", &val) >= 0) + sgm->data.jeita_temp_t2_to_t3_cc_current = val; + else + sgm->data.jeita_temp_t2_to_t3_cc_current = JEITA_TEMP_T2_TO_T3_CC_CURRENT; + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_t1_to_t2_cc_current", &val) >= 0) + sgm->data.jeita_temp_t1_to_t2_cc_current = val; + else + sgm->data.jeita_temp_t1_to_t2_cc_current = JEITA_TEMP_T1_TO_T2_CC_CURRENT; + + if (of_property_read_u32(sgm->dev->of_node, "jeita_temp_below_t0_cc_current", &val) >= 0) + sgm->data.jeita_temp_below_t0_cc_current = val; + else + sgm->data.jeita_temp_below_t0_cc_current = JEITA_TEMP_BELOW_T0_CC_CURRENT; + + pr_err("%s,CC1 = %d,CC2 = %d,CC3 = %d,CC4 = %d,CC5 = %d\n",__func__, + sgm->data.jeita_temp_above_t4_cc_current,sgm->data.jeita_temp_t3_to_t4_cc_current, + sgm->data.jeita_temp_t2_to_t3_cc_current,sgm->data.jeita_temp_t1_to_t2_cc_current, + sgm->data.jeita_temp_below_t0_cc_current); + + if (of_property_read_u32(sgm->dev->of_node, "temp_t4_thres", &val) >= 0) + sgm->data.temp_t4_thres = val; + else { + dev_err(sgm->dev, "use default TEMP_T4_THRES:%d\n",TEMP_T4_THRES); + sgm->data.temp_t4_thres = TEMP_T4_THRES; + } + if (of_property_read_u32(sgm->dev->of_node, "temp_t4_thres_minus_x_degree", &val) >= 0) + sgm->data.temp_t4_thres_minus_x_degree = val; + else { + dev_err(sgm->dev,"use default TEMP_T4_THRES_MINUS_X_DEGREE:%d\n",TEMP_T4_THRES_MINUS_X_DEGREE); + sgm->data.temp_t4_thres_minus_x_degree = TEMP_T4_THRES_MINUS_X_DEGREE; + } + + if (of_property_read_u32(sgm->dev->of_node, "temp_t3_thres", &val) >= 0) + sgm->data.temp_t3_thres = val; + else { + dev_err(sgm->dev,"use default TEMP_T3_THRES:%d\n",TEMP_T3_THRES); + sgm->data.temp_t3_thres = TEMP_T3_THRES; + } + + if (of_property_read_u32(sgm->dev->of_node, "temp_t3_thres_minus_x_degree", &val) >= 0) + sgm->data.temp_t3_thres_minus_x_degree = val; + else { + dev_err(sgm->dev,"use default TEMP_T3_THRES_MINUS_X_DEGREE:%d\n",TEMP_T3_THRES_MINUS_X_DEGREE); + sgm->data.temp_t3_thres_minus_x_degree = TEMP_T3_THRES_MINUS_X_DEGREE; + } + + if (of_property_read_u32(sgm->dev->of_node, "temp_t2_thres", &val) >= 0) + sgm->data.temp_t2_thres = val; + else { + dev_err(sgm->dev,"use default TEMP_T2_THRES:%d\n",TEMP_T2_THRES); + sgm->data.temp_t2_thres = TEMP_T2_THRES; + } + + if (of_property_read_u32(sgm->dev->of_node, "temp_t2_thres_plus_x_degree", &val) >= 0) + sgm->data.temp_t2_thres_plus_x_degree = val; + else { + dev_err(sgm->dev,"use default TEMP_T2_THRES_PLUS_X_DEGREE:%d\n",TEMP_T2_THRES_PLUS_X_DEGREE); + sgm->data.temp_t2_thres_plus_x_degree = TEMP_T2_THRES_PLUS_X_DEGREE; + } + + if (of_property_read_u32(sgm->dev->of_node, "temp_t1_thres", &val) >= 0) + sgm->data.temp_t1_thres = val; + else { + dev_err(sgm->dev,"use default TEMP_T1_THRES:%d\n",TEMP_T1_THRES); + sgm->data.temp_t1_thres = TEMP_T1_THRES; + } + + if (of_property_read_u32(sgm->dev->of_node, "temp_t1_thres_plus_x_degree", &val) >= 0) + sgm->data.temp_t1_thres_plus_x_degree = val; + else { + dev_err(sgm->dev,"use default TEMP_T1_THRES_PLUS_X_DEGREE:%d\n",TEMP_T1_THRES_PLUS_X_DEGREE); + sgm->data.temp_t1_thres_plus_x_degree = TEMP_T1_THRES_PLUS_X_DEGREE; + } + + if (of_property_read_u32(sgm->dev->of_node, "temp_t0_thres", &val) >= 0) + sgm->data.temp_t0_thres = val; + else { + dev_err(sgm->dev,"use default TEMP_T0_THRES:%d\n",TEMP_T0_THRES); + sgm->data.temp_t0_thres = TEMP_T0_THRES; + } + + if (of_property_read_u32(sgm->dev->of_node, "temp_t0_thres_plus_x_degree", &val) >= 0) + sgm->data.temp_t0_thres_plus_x_degree = val; + else { + dev_err(sgm->dev,"use default TEMP_T0_THRES_PLUS_X_DEGREE:%d\n",TEMP_T0_THRES_PLUS_X_DEGREE); + sgm->data.temp_t0_thres_plus_x_degree = TEMP_T0_THRES_PLUS_X_DEGREE; + } + + if (of_property_read_u32(sgm->dev->of_node, "temp_neg_10_thres", &val) >= 0) + sgm->data.temp_neg_10_thres = val; + else { + dev_err(sgm->dev,"use default TEMP_NEG_10_THRES:%d\n",TEMP_NEG_10_THRES); + sgm->data.temp_neg_10_thres = TEMP_NEG_10_THRES; + } + pr_err("%s,thres4 = %d,thres3 = %d,thres2 = %d,thres1 = %d,thres0 = %d\n",__func__, + sgm->data.temp_t4_thres,sgm->data.temp_t3_thres, + sgm->data.temp_t2_thres,sgm->data.temp_t1_thres, + sgm->data.temp_t0_thres); + return 0; +} + +static int sgm4154x_enable_vbus(struct regulator_dev *rdev) +{ + struct sgm4154x_device *sgm = rdev_get_drvdata(rdev); + int ret = 0; + + /*disable wls output*/ + if (gpio_is_valid(sgm->wls_en_gpio)) { + pr_err("%s,set wls en\n",__func__); + gpio_direction_output(sgm->wls_en_gpio, 1); + } + + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_1, SGM4154x_OTG_EN, + SGM4154x_OTG_EN); + return ret; +} + +static int sgm4154x_disable_vbus(struct regulator_dev *rdev) +{ + struct sgm4154x_device *sgm = rdev_get_drvdata(rdev); + int ret = 0; + + ret = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_1, SGM4154x_OTG_EN, + 0); + + /*resume wls output*/ + if (gpio_is_valid(sgm->wls_en_gpio)) { + pr_err("%s,set wls dis\n",__func__); + gpio_direction_output(sgm->wls_en_gpio, 0); + } + + return ret; +} + +static int sgm4154x_is_enabled_vbus(struct regulator_dev *rdev) +{ + struct sgm4154x_device *sgm = rdev_get_drvdata(rdev); + int temp = 0; + int ret = 0; + + ret = mmi_regmap_read(sgm, SGM4154x_CHRG_CTRL_1, &temp); + pr_info("%s ret:%d\n",__func__,ret); + return (temp&SGM4154x_OTG_EN)? 1 : 0; +} +static struct regulator_ops sgm4154x_vbus_ops = { + .enable = sgm4154x_enable_vbus, + .disable = sgm4154x_disable_vbus, + .is_enabled = sgm4154x_is_enabled_vbus, +}; + +static struct regulator_desc sgm4154x_otg_rdesc = { + .of_match = "usb-otg-vbus", + .name = "usb-otg-vbus", + .ops = &sgm4154x_vbus_ops, + .owner = THIS_MODULE, + .type = REGULATOR_VOLTAGE, + .fixed_uV = 5000000, + .n_voltages = 1, +}; + +static int sgm4154x_vbus_regulator_register(struct sgm4154x_device *sgm) +{ + struct regulator_config config = {}; + int ret = 0; + /* otg regulator */ + config.dev = sgm->dev; + config.driver_data = sgm; + sgm->otg_rdev = devm_regulator_register(sgm->dev, + &sgm4154x_otg_rdesc, &config); + sgm->otg_rdev->constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS; + if (IS_ERR(sgm->otg_rdev)) { + ret = PTR_ERR(sgm->otg_rdev); + pr_info("%s: register otg regulator failed (%d)\n", __func__, ret); + } + return ret; +} + +static int sgm4154x_hw_chipid_detect(struct sgm4154x_device *sgm) +{ + int ret = 0; + int val = 0; + + ret = mmi_regmap_read(sgm,SGM4154x_CHRG_CTRL_b,&val); + if (ret < 0) + { + pr_info("[%s] read SGM4154x_CHRG_CTRL_b fail\n", __func__); + return ret; + } + + pr_info("[%s] Reg[0x0B]=0x%x\n", __func__,val); + + return val; +} + +static int sgm4154x_get_iio_channel(struct sgm4154x_device *sgm, const char *propname, + struct iio_channel **chan) +{ + int rc = 0; + + rc = of_property_match_string(sgm->dev->of_node, + "io-channel-names", propname); + if (rc < 0) + return 0; + + *chan = iio_channel_get(sgm->dev, propname); + if (IS_ERR(*chan)) { + rc = PTR_ERR(*chan); + if (rc != -EPROBE_DEFER) + dev_err(sgm->dev, "%s channel unavailable, %d\n", + propname, rc); + *chan = NULL; + } + + return rc; +} + +static int sgm4154x_parse_dt_adc_channels(struct sgm4154x_device *sgm) +{ + int rc = 0; + +#ifdef CONFIG_MMI_SGM41513_CHARGER + rc = sgm4154x_get_iio_channel(sgm, "read_usbin_voltage", + &sgm->iio.usbin_v_chan); +#else + rc = sgm4154x_get_iio_channel(sgm, "gpio3_div3", + &sgm->iio.usbin_v_chan); +#endif + + if (rc < 0) + return rc; + + return 0; +} + +static int sgm4154x_enable_otg(struct charger_device *chg_dev, bool enable) +{ + struct sgm4154x_device *sgm = dev_get_drvdata(&chg_dev->dev); + static struct regulator *vbus_reg = NULL; + int rc = -EINVAL; + + if (!sgm) { + return rc; + } + + mutex_lock(&sgm->regulator_lock); + if (!sgm->otg_vbus_reg) { + sgm->otg_vbus_reg = devm_regulator_get(sgm->dev, "usb-otg-vbus"); + if (IS_ERR(vbus_reg)) { + rc = PTR_ERR(vbus_reg); + dev_err(sgm->dev, "Couldn't get vbus regulator rc=%d\n", rc); + sgm->otg_vbus_reg = NULL; + mutex_unlock(&sgm->regulator_lock); + return rc; + } + } + + if (sgm->otg_vbus_reg) { + rc = (enable) ? regulator_enable(sgm->otg_vbus_reg) : regulator_disable(sgm->otg_vbus_reg); + } + + pr_info("%s, %s otg %s\n", __func__, + enable ? "enable" : "disable", + rc ? "failed" : "success"); + + mutex_unlock(&sgm->regulator_lock); + + return rc; +} + +static int sgm4154x_enable_charging(struct charger_device *chg_dev, bool enable) +{ + struct sgm4154x_device *sgm = dev_get_drvdata(&chg_dev->dev); + int rc = 0; + + rc = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_1, SGM4154x_CHRG_EN, + enable ? SGM4154x_CHRG_EN : 0); + + pr_info("%s, %s charging %s\n", __func__, + enable ? "enable" : "disable", + rc ? "failed" : "success"); + + return rc; +} + +static int sgm4154x_enable_hz(struct charger_device *chg_dev, bool enable) +{ + struct sgm4154x_device *sgm = dev_get_drvdata(&chg_dev->dev); + int rc = 0; + + rc = mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_0, SGM4154x_HIZ_EN, + enable ? SGM4154x_HIZ_EN : 0); + + pr_info("%s, %s hz %s\n", __func__, + enable ? "enable" : "disable", + rc ? "failed" : "success"); + + return rc; +} + +static int sgm4154x_set_charging_current(struct charger_device *chg_dev, u32 uA) +{ + struct sgm4154x_device *sgm = dev_get_drvdata(&chg_dev->dev); + int rc = 0; + + rc = sgm4154x_set_ichrg_curr(sgm, uA); + + pr_info("%s set charging curr = %d, %s\n", __func__, uA, rc ? "failed" : "success"); + + return rc; +} + +static int sgm4154x_set_charging_voltage(struct charger_device *chg_dev, u32 uV) +{ + struct sgm4154x_device *sgm = dev_get_drvdata(&chg_dev->dev); + int rc = 0; + + /* enable dynamic adjust battery voltage */ + if(sgm->enable_dynamic_adjust_batvol) + rc = sgm4154x_set_chrg_adjust_volt(sgm, uV, 0); + else + rc = sgm4154x_set_chrg_volt(sgm, uV); + + pr_info("%s set charging volt = %d, %s\n", __func__, uV, rc ? "failed" : "success"); + + return rc; +} + +static int sgm4154x_is_charging_halted(struct charger_device *chg_dev, bool *en) +{ + int rc = 0; + int chrg_stat = 0; + struct sgm4154x_device *sgm = dev_get_drvdata(&chg_dev->dev); + + rc = mmi_regmap_read(sgm, SGM4154x_CHRG_STAT, &chrg_stat); + if (rc){ + pr_err("%s read SGM4154x_CHRG_STAT fail\n",__func__); + return rc; + } + + chrg_stat = chrg_stat & SGM4154x_CHG_STAT_MASK; + switch(chrg_stat) { + case SGM4154x_NOT_CHRGING: + case SGM4154x_TERM_CHRG: + *en = true; + break; + case SGM4154x_PRECHRG: + case SGM4154x_FAST_CHRG: + *en = false; + break; + default: + break; + } + + return rc; +} + +static int sgm4154x_set_boost_current_limit(struct charger_device *chg_dev, u32 uA) +{ + struct sgm4154x_device *sgm = dev_get_drvdata(&chg_dev->dev); + int rc = 0; + + rc = sgm4154x_set_otg_current(sgm, uA); + + pr_info("%s set boost current limit = %d, %s\n", __func__, uA, rc ? "failed" : "success"); + + return rc; +} + +static int sgm4154x_get_qc3p_power(struct charger_device *chg_dev, int *qc3p_power) +{ + struct sgm4154x_device *sgm = dev_get_drvdata(&chg_dev->dev); + + *qc3p_power = sgm->mmi_qc3p_power; + + return 0; +} + +static int sgm4154x_get_pulse_cnt(struct charger_device *chg_dev, int *count) +{ + struct sgm4154x_device *sgm = dev_get_drvdata(&chg_dev->dev); + + *count = sgm->pulse_cnt; + + return 0; +} + +static int sgm4154x_set_dp_dm(struct charger_device *chg_dev, int val) +{ + int rc = 0; + struct sgm4154x_device *sgm = dev_get_drvdata(&chg_dev->dev); + + down(&sgm->sem_dpdm); + + switch(val) { + case MMI_POWER_SUPPLY_DP_DM_DP_PULSE: + sgm->pulse_cnt++; + rc = sgm4154x_qc30_step_up_vbus(sgm); + if (rc) { + dev_err(sgm->dev, "Couldn't increase pulse count rc=%d\n", + rc); + sgm->pulse_cnt--; + } + dev_dbg(sgm->dev,"DP_DM_DP_PULSE rc=%d cnt=%d\n", + rc, sgm->pulse_cnt); + break; + case MMI_POWER_SUPPLY_DP_DM_DM_PULSE: + rc = sgm4154x_qc30_step_down_vbus(sgm); + if (!rc && sgm->pulse_cnt) + sgm->pulse_cnt--; + dev_dbg(sgm->dev, "DP_DM_DM_PULSE rc=%d cnt=%d\n", + rc, sgm->pulse_cnt); + break; + case MMI_POWER_SUPPLY_IGNORE_REQUEST_DPDM: + sgm->ignore_request_dpdm = true; + dev_dbg(sgm->dev, "MMI_POWER_SUPPLY_IGNORE_REQUEST_DPDM\n"); + break; + case MMI_POWER_SUPPLY_DONOT_IGNORE_REQUEST_DPDM: + sgm->ignore_request_dpdm = false; + dev_dbg(sgm->dev, "MMI_POWER_SUPPLY_DONOT_IGNORE_REQUEST_DPDM\n"); + break; + default: + break; + } + + up(&sgm->sem_dpdm); + + return rc; +} + +static int sgm4154x_get_real_charger_type(struct charger_device *chg_dev, int *chg_type) +{ + struct sgm4154x_device *sgm = dev_get_drvdata(&chg_dev->dev); + + *chg_type = sgm->real_charger_type; + + return 0; +} + +static int sgm4154x_config_pd_active(struct charger_device *chg_dev, int val) +{ + struct sgm4154x_device *sgm = dev_get_drvdata(&chg_dev->dev); + + sgm->pd_active = val; + + return 0; +} + +static int sgm4154x_dump_registers(struct charger_device *chg_dev, struct seq_file *m) +{ + struct sgm4154x_device *sgm = dev_get_drvdata(&chg_dev->dev); + int i = 0; + u32 reg = 0; + int rc = 0; + + for(i=0; i<=SGM4154x_CHRG_CTRL_f; i++) { + rc = mmi_regmap_read(sgm, i, ®); + if (rc) + continue; + seq_printf(m, "%s REG[0x%x]=0x%x\n", __func__, i, reg); + } + + return rc; +} + +static const struct charger_properties sgm4154x_chg_props = { + .alias_name = "sgm4154x", +}; + +static struct charger_ops sgm4154x_chg_ops = { + .get_pulse_cnt = sgm4154x_get_pulse_cnt, + .set_dp_dm = sgm4154x_set_dp_dm, + .get_real_charger_type = sgm4154x_get_real_charger_type, + .set_input_current = sgm4154x_set_icl, + .get_input_current = sgm4154x_get_icl, + .enable_hw_jeita = sgm4154x_enable_hw_jeita, + .enable_otg = sgm4154x_enable_otg, + .set_boost_current_limit = sgm4154x_set_boost_current_limit, + .enable_charging = sgm4154x_enable_charging, + .enable_hz = sgm4154x_enable_hz, + .set_charging_current = sgm4154x_set_charging_current, + .set_constant_voltage = sgm4154x_set_charging_voltage, + .is_charge_halted = sgm4154x_is_charging_halted, + + .dump_registers = sgm4154x_dump_registers, + .is_enabled_charging = sgm4154x_is_enabled_charging, + .enable_termination = sgm4154x_enable_termination, + .get_qc3p_power = sgm4154x_get_qc3p_power, + .config_pd_active = sgm4154x_config_pd_active, +}; +#ifdef __indicator_led_en__ +static int indicator_led_set_brightness(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct qpnp_led_dev *led = + container_of(led_cdev, struct qpnp_led_dev, cdev); + int rc = 0; + + mutex_lock(&led->lock); + if (brightness > LED_FULL) + brightness = LED_FULL; + + if (brightness == led->led_setting.brightness && + !led->blinking && !led->breathing) { + mutex_unlock(&led->lock); + return 0; + } + + led->led_setting.brightness = brightness; + if (!!brightness) + mmi_regmap_update_bits(sgm_g, SGM4154x_CHRG_CTRL_f, SGM4154x_VREG_STAT_SET_MASK, 0x1<<2); + else + mmi_regmap_update_bits(sgm_g, SGM4154x_CHRG_CTRL_f, SGM4154x_VREG_STAT_SET_MASK, 0x0<<2); + led->led_setting.blink = false; + led->led_setting.breath = false; + + mutex_unlock(&led->lock); + + return rc; +} + +static enum led_brightness indicator_led_get_brightness( + struct led_classdev *led_cdev) +{ + return led_cdev->brightness; +} + +static int indicator_led_set_blink(struct led_classdev *led_cdev, + unsigned long *on_ms, unsigned long *off_ms) +{ + int rc = 0; + return rc; +} + +static int indicator_led_register(struct indicator_led_chip *chip) +{ + struct qpnp_led_dev *led; + int rc, i, j; + + + for (i = 0; i < chip->num_leds; i++) { + led = &chip->leds[i]; + mutex_init(&led->lock); + led->cdev.name = led->label; + led->cdev.max_brightness = LED_FULL; + led->cdev.brightness_set_blocking = indicator_led_set_brightness; + led->cdev.brightness_get = indicator_led_get_brightness; + led->cdev.blink_set = indicator_led_set_blink; + led->cdev.default_trigger = led->default_trigger; + led->cdev.brightness = LED_OFF; + + rc = devm_led_classdev_register(chip->dev, &led->cdev); + if (rc < 0) { + dev_err(chip->dev, "%s led class device registering failed, rc=%d\n", + led->label, rc); + goto err_out; + } + } + + return 0; + +err_out: + for (j = 0; j <= i; j++) { + mutex_destroy(&chip->leds[j].lock); + } + return rc; +} + +static int indicator_led_parse_dt(struct indicator_led_chip *chip) +{ + struct device_node *node = chip->dev->of_node, *child_node; + struct qpnp_led_dev *led; + int rc = 0, id, i = 0; + + chip->num_leds = of_get_available_child_count(node); + if (chip->num_leds == 0) { + dev_err(chip->dev, "No led child node defined\n"); + return -ENODEV; + } + + if (chip->num_leds > TRILED_NUM_MAX) { + dev_err(chip->dev, "can't support %d leds(max %d)\n", + chip->num_leds, TRILED_NUM_MAX); + return -EINVAL; + } + + chip->leds = devm_kcalloc(chip->dev, chip->num_leds, + sizeof(struct qpnp_led_dev), GFP_KERNEL); + if (!chip->leds) + return -ENOMEM; + + for_each_available_child_of_node(node, child_node) { + rc = of_property_read_u32(child_node, "led-sources", &id); + if (rc) { + dev_err(chip->dev, "Get led-sources failed, rc=%d\n", + rc); + return rc; + } + + if (id >= TRILED_NUM_MAX) { + dev_err(chip->dev, "only support 0~%d current source\n", + TRILED_NUM_MAX - 1); + return -EINVAL; + } + + led = &chip->leds[i++]; + led->chip = chip; + led->id = id; + led->label = + of_get_property(child_node, "label", NULL) ? : + child_node->name; + led->default_trigger = of_get_property(child_node, + "linux,default-trigger", NULL); + } + + return rc; +} + +static int indicator_led_probe(struct i2c_client *client)//(struct platform_device *pdev) +{ + struct indicator_led_chip *chip; + int rc = 0; + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->dev = &client->dev; + + rc = indicator_led_parse_dt(chip); + if (rc < 0) { + if (rc != -EPROBE_DEFER) + dev_err(chip->dev, "Devicetree properties parsing failed, rc=%d\n", + rc); + return rc; + } + rc = indicator_led_register(chip); + if (rc < 0) { + dev_err(chip->dev, "Registering LED class devices failed, rc=%d\n", + rc); + goto destroy; + } + + dev_err(chip->dev, "%s has been finished\n", __func__); + return 0; +destroy: + return rc; +} +#endif + +static int sgm4154x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct sgm4154x_device *sgm; + int ret; + int otg_notify; + char *name = NULL; + + sgm = devm_kzalloc(dev, sizeof(*sgm), GFP_KERNEL); + if (!sgm) + return -ENOMEM; +#ifdef __indicator_led_en__ + sgm_g = sgm; +#endif + sgm->client = client; + sgm->dev = dev; + + sgm->ignore_request_dpdm = false; + + mutex_init(&sgm->lock); + mutex_init(&sgm->regulator_lock); + + strncpy(sgm->model_name, id->name, I2C_NAME_SIZE); + + sgm->regmap = devm_regmap_init_i2c(client, &sgm4154x_regmap_config); + if (IS_ERR(sgm->regmap)) { + dev_err(dev, "Failed to allocate register map\n"); + return PTR_ERR(sgm->regmap); + } + + i2c_set_clientdata(client, sgm); + + ret = sgm4154x_hw_chipid_detect(sgm); + if (ret < 0) { + pr_info("[%s] device not found, ret = %d!!!\n", __func__, ret); + return ret; + } +#ifdef __SGM41513_CHIP_ID__ + if ((ret & SGM4154x_PN_MASK) != SGM4154x_PN_41513_ID){ + +#else + if (((ret & SGM4154x_PN_MASK) != SGM4154x_PN_41542_ID) && ((ret & SGM4154x_PN_MASK) != SGM4154x_PN_41543D_ID)){ +#endif + pr_info("[%s] device not found !!!\n", __func__); + return ret; + } + + sema_init(&sgm->sem_dpdm, 1); + + // Customer customization + ret = sgm4154x_parse_dt(sgm); + if (ret) { + dev_err(dev, "Failed to read device tree properties%d\n", ret); + return ret; + } + + ret = sgm4154x_parse_dt_adc_channels(sgm); + if (ret) { + dev_err(dev, "Failed to get adc channels%d\n", ret); + return ret; + } + + sgm->chg_dev = charger_device_register(sgm->chg_dev_name, + &client->dev, sgm, + &sgm4154x_chg_ops, + &sgm4154x_chg_props); + if (IS_ERR_OR_NULL(sgm->chg_dev)) { + ret = PTR_ERR(sgm->chg_dev); + return ret; + } + + ret = sgm4154x_hw_init(sgm); + if (ret) { + dev_err(dev, "Cannot initialize the chip.\n"); + return ret; + } + + name = devm_kasprintf(sgm->dev, GFP_KERNEL, "%s", + "sgm4154x suspend wakelock"); + sgm->charger_wakelock = + wakeup_source_register(NULL, name); + + /* OTG reporting */ + sgm->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + if (!IS_ERR_OR_NULL(sgm->usb2_phy)) { + INIT_WORK(&sgm->usb_work, sgm4154x_usb_work); + sgm->usb_nb.notifier_call = sgm4154x_usb_notifier; + otg_notify = usb_register_notifier(sgm->usb2_phy, &sgm->usb_nb); + } + + sgm->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3); + if (!IS_ERR_OR_NULL(sgm->usb3_phy)) { + INIT_WORK(&sgm->usb_work, sgm4154x_usb_work); + sgm->usb_nb.notifier_call = sgm4154x_usb_notifier; + otg_notify = usb_register_notifier(sgm->usb3_phy, &sgm->usb_nb); + } + + INIT_WORK(&sgm->charge_detect_work, charger_detect_work_func); + INIT_WORK(&sgm->rerun_apsd_work, sgm4154x_rerun_apsd_work_func); + INIT_DELAYED_WORK(&sgm->charge_monitor_work, charger_monitor_work_func); + + if (sgm->mmi_qc3_support) { + sgm->mmi_qc3_authen_task = kthread_create(mmi_hvdcp_detect_kthread, sgm, "mmi_qc3_authen"); + if (IS_ERR(sgm->mmi_qc3_authen_task)) { + ret = PTR_ERR(sgm->mmi_qc3_authen_task); + dev_err(dev, "Failed to create mmi_qc3_authen_task ret = %d\n", ret); + goto error_out; + } + init_waitqueue_head(&sgm->mmi_qc3_wait_que); + wake_up_process(sgm->mmi_qc3_authen_task); + } + + //rerun apsd and trigger charger detect when boot with charger + schedule_work(&sgm->rerun_apsd_work); + + ret = sgm4154x_power_supply_init(sgm, dev); + if (ret) { + dev_err(dev, "Failed to register power supply\n"); + goto error_out; + } + + ret = sgm4154x_vbus_regulator_register(sgm); + + if (client->irq) { + ret = devm_request_threaded_irq(dev, client->irq, NULL, + sgm4154x_irq_handler_thread, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + dev_name(&client->dev), sgm); + if (ret) + goto error_out; + enable_irq_wake(client->irq); + } + +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED + ret = mmi_init_iio_psy(sgm, dev); + if (ret) { + dev_err(dev, + "mmi iio psy init failed\n"); + } +#endif + pr_info("%s otg_notify:%d\n",__func__, otg_notify); + schedule_delayed_work(&sgm->charge_monitor_work,100); +#ifdef __indicator_led_en__ + indicator_led_probe(client); +#endif + dev_info(dev, "SGM4154x prob successfully.\n"); + return ret; +error_out: + if (sgm->mmi_qc3_support && sgm->mmi_qc3_authen_task) + kthread_stop(sgm->mmi_qc3_authen_task); + + if (!IS_ERR_OR_NULL(sgm->usb2_phy)) + usb_unregister_notifier(sgm->usb2_phy, &sgm->usb_nb); + + if (!IS_ERR_OR_NULL(sgm->usb3_phy)) + usb_unregister_notifier(sgm->usb3_phy, &sgm->usb_nb); + return ret; +} + +static int sgm4154x_charger_remove(struct i2c_client *client) +{ + struct sgm4154x_device *sgm= i2c_get_clientdata(client); + + if (sgm->mmi_qc3_support && sgm->mmi_qc3_authen_task) + kthread_stop(sgm->mmi_qc3_authen_task); + + cancel_delayed_work_sync(&sgm->charge_monitor_work); + + regulator_unregister(sgm->otg_rdev); + + power_supply_unregister(sgm->charger); + + mutex_destroy(&sgm->lock); + + return 0; +} + +static void sgm4154x_charger_shutdown(struct i2c_client *client) +{ + int ret = 0; + + struct sgm4154x_device *sgm = i2c_get_clientdata(client); + ret = sgm4154x_disable_charger(sgm); + if (ret) { + pr_err("Failed to disable charger, ret = %d\n", ret); + } + /* enable dynamic adjust battery voltage */ + if(sgm->enable_dynamic_adjust_batvol){ + sgm4154x_set_chrg_adjust_volt(sgm, sgm->final_cv, 0); + mmi_regmap_update_bits(sgm, SGM4154x_CHRG_CTRL_f, SGM4154x_VREG_FT_MASK, 0); + } + pr_info("sgm4154x_charger_shutdown\n"); +} + +static int sgm4154x_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sgm4154x_device *sgm = i2c_get_clientdata(client); + + sgm->sgm4154x_suspend_flag = 1; + pr_info("sgm4154x PM_SUSPEND \n"); + + return 0; +} + +static int sgm4154x_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sgm4154x_device *sgm = i2c_get_clientdata(client); + + sgm->sgm4154x_suspend_flag = 0; + pr_info("sgm4154x PM_RESUME \n"); + + return 0; +} + +static const struct dev_pm_ops sgm4154x_pm_ops = { + .resume = sgm4154x_resume, + .suspend = sgm4154x_suspend, +}; + +static const struct i2c_device_id sgm4154x_i2c_ids[] = { + { "sgm41541", 0 }, + { "sgm41542", 0 }, + { "sgm41516", 0 }, + { "sgm41516D", 0 }, + { "sgm41513", 0 }, + { "sgm41543D", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, sgm4154x_i2c_ids); + +static const struct of_device_id sgm4154x_of_match[] = { + { .compatible = "sgm,sgm41541" }, + { .compatible = "sgm,sgm41542" }, + { .compatible = "sgm,sgm41516" }, + { .compatible = "sgm,sgm41516D" }, + { .compatible = "sgm,sgm41513" }, + { .compatible = "sgm,sgm41543D"}, + { }, +}; +MODULE_DEVICE_TABLE(of, sgm4154x_of_match); + +static struct i2c_driver sgm4154x_driver = { + .driver = { + .name = "sgm4154x-charger", + .of_match_table = sgm4154x_of_match, + .pm = &sgm4154x_pm_ops, + }, + .probe = sgm4154x_probe, + .remove = sgm4154x_charger_remove, + .shutdown = sgm4154x_charger_shutdown, + .id_table = sgm4154x_i2c_ids, +}; +module_i2c_driver(sgm4154x_driver); + +MODULE_AUTHOR(" qhq "); +MODULE_DESCRIPTION("sgm4154x charger driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/sgm4154x_chg_mmi/sgm4154x_charger.h b/drivers/power/sgm4154x_chg_mmi/sgm4154x_charger.h new file mode 100644 index 000000000000..f230e4f9bfd3 --- /dev/null +++ b/drivers/power/sgm4154x_chg_mmi/sgm4154x_charger.h @@ -0,0 +1,467 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +// sgm4154x Charger Driver +// Copyright (C) 2021 Texas Instruments Incorporated - http://www.sg-micro.com + +#ifndef _SGM4154x_CHARGER_H +#define _SGM4154x_CHARGER_H + +#include +#include +#include +#ifdef CONFIG_MMI_EXT_CHG_LED +#define __indicator_led_en__ +#endif + +#ifdef __indicator_led_en__ +#include +#include +#endif + +#define SGM4154x_MANUFACTURER "Texas Instruments" +#define SGM4154X_STATUS_PLUGIN 0x0001 +#define SGM4154X_STATUS_PG 0x0002 +#define SGM4154X_STATUS_CHARGE_ENABLE 0x0004 +#define SGM4154X_STATUS_FAULT 0x0008 +#define SGM4154X_STATUS_EXIST 0x0100 + +//#define __SGM41541_CHIP_ID__ +#define __SGM41542_CHIP_ID__ +//#define __SGM41516_CHIP_ID__ +//#define __SGM41516D_CHIP_ID__ +#ifdef CONFIG_MMI_SGM41513_CHARGER +#undef __SGM41542_CHIP_ID__ +#define __SGM41513_CHIP_ID__ +#endif + +#ifdef __SGM41541_CHIP_ID__ +#define SGM4154x_NAME "sgm41541" +#endif + +#ifdef __SGM41542_CHIP_ID__ +#define SGM4154x_NAME "sgm41542" +#endif + +#ifdef __SGM41516_CHIP_ID__ +#define SGM4154x_NAME "sgm41516" +#endif + +#ifdef __SGM41516D_CHIP_ID__ +#define SGM4154x_NAME "sgm41516D" +#endif + +#ifdef __SGM41513_CHIP_ID__ +#define SGM4154x_NAME "sgm41513" +#endif + +/*define register*/ +#define SGM4154x_CHRG_CTRL_0 0x00 +#define SGM4154x_CHRG_CTRL_1 0x01 +#define SGM4154x_CHRG_CTRL_2 0x02 +#define SGM4154x_CHRG_CTRL_3 0x03 +#define SGM4154x_CHRG_CTRL_4 0x04 +#define SGM4154x_CHRG_CTRL_5 0x05 +#define SGM4154x_CHRG_CTRL_6 0x06 +#define SGM4154x_CHRG_CTRL_7 0x07 +#define SGM4154x_CHRG_STAT 0x08 +#define SGM4154x_CHRG_FAULT 0x09 +#define SGM4154x_CHRG_CTRL_a 0x0a +#define SGM4154x_CHRG_CTRL_b 0x0b +#define SGM4154x_CHRG_CTRL_c 0x0c +#define SGM4154x_CHRG_CTRL_d 0x0d +#define SGM4154x_INPUT_DET 0x0e +#define SGM4154x_CHRG_CTRL_f 0x0f + +/*rerun apsd*/ +#define SGM4154x_IINDET_EN_MASK GENMASK(7, 7) +#define SGM4154x_IINDET_EN BIT(7) + +/* charge status flags */ +#define SGM4154x_IINDPM_INT_MASK GENMASK(0, 0) +#define SGM4154x_IINDPM_INT_DISABLE BIT(0) +#define SGM4154x_VINDPM_INT_MASK GENMASK(1, 1) +#define SGM4154x_VINDPM_INT_DISABLE BIT(1) +#define SGM4154x_CHRG_EN BIT(4) +#define SGM4154x_HIZ_EN BIT(7) +#define SGM4154x_TERM_EN BIT(7) +#define SGM4154x_VAC_OVP_MASK GENMASK(7, 6) +#define SGM4154x_DPDM_ONGOING BIT(7) +#define SGM4154x_VBUS_GOOD BIT(7) + +#define SGM4154X_BOOST_LIM_MASK GENMASK(7, 7) +#define SGM4154x_OTG_MASK GENMASK(5, 4) +#define SGM4154x_OTG_EN BIT(5) + +/* Part ID */ +#define SGM4154x_PN_MASK GENMASK(6, 3) +#define SGM4154x_PN_41541_ID (BIT(6)| BIT(5)) +#define SGM4154x_PN_41516_ID (BIT(6)| BIT(5)) +#define SGM4154x_PN_41542_ID (BIT(6)| BIT(5)| BIT(3)) +#define SGM4154x_PN_41516D_ID (BIT(6)| BIT(5)| BIT(3)) +#define SGM4154x_PN_41543D_ID (BIT(6)| BIT(3)) +#define SGM4154x_PN_41513_ID 0 + +/* WDT TIMER SET */ +#define SGM4154x_WDT_TIMER_MASK GENMASK(5, 4) +#define SGM4154x_WDT_TIMER_DISABLE 0 +#define SGM4154x_WDT_TIMER_40S BIT(4) +#define SGM4154x_WDT_TIMER_80S BIT(5) +#define SGM4154x_WDT_TIMER_160S (BIT(4)| BIT(5)) + +#define SGM4154x_WDT_RST_MASK BIT(6) + +/*En termniataion*/ +#define SGM4154x_EN_TERM_MASK GENMASK(7,7) +#define SGM4154x_EN_TERM_DISABLE 0 +#define SGM4154x_EN_TERM_ENABLE BIT(7) + +/* SAFETY TIMER SET */ +#define SGM4154x_SAFETY_TIMER_MASK GENMASK(3, 3) +#define SGM4154x_SAFETY_TIMER_DISABLE 0 +#define SGM4154x_SAFETY_TIMER_EN BIT(3) +#define SGM4154x_SAFETY_TIMER_5H 0 +#define SGM4154x_SAFETY_TIMER_10H BIT(2) + +/* recharge voltage */ +#define SGM4154x_VRECHARGE BIT(0) +#define SGM4154x_VRECHRG_STEP_mV 100 +#define SGM4154x_VRECHRG_OFFSET_mV 100 + +/* charge status */ +#define SGM4154x_VSYS_STAT BIT(0) +#define SGM4154x_THERM_STAT BIT(1) +#define SGM4154x_PG_STAT BIT(2) +#define SGM4154x_CHG_STAT_MASK GENMASK(4, 3) +#define SGM4154x_PRECHRG BIT(3) +#define SGM4154x_FAST_CHRG BIT(4) +#define SGM4154x_TERM_CHRG (BIT(3)| BIT(4)) +#define SGM4154x_NOT_CHRGING 0 + +/* charge type */ +#define SGM4154x_VBUS_STAT_MASK GENMASK(7, 5) +#define SGM4154x_USB_NONE 0 +#define SGM4154x_USB_SDP BIT(5) +#define SGM4154x_USB_CDP BIT(6) +#define SGM4154x_USB_DCP (BIT(5) | BIT(6)) +#define SGM4154x_UNKNOWN (BIT(7) | BIT(5)) +#define SGM4154x_NON_STANDARD (BIT(7) | BIT(6)) +#define SGM4154x_OTG_MODE (BIT(7) | BIT(6) | BIT(5)) + +/* TEMP Status */ +#define SGM4154x_TEMP_MASK GENMASK(2, 0) +#define SGM4154x_TEMP_NORMAL BIT(0) +#define SGM4154x_TEMP_WARM BIT(1) +#define SGM4154x_TEMP_COOL (BIT(0) | BIT(1)) +#define SGM4154x_TEMP_COLD (BIT(0) | BIT(3)) +#define SGM4154x_TEMP_HOT (BIT(2) | BIT(3)) + +/* precharge current */ +#define SGM4154x_PRECHRG_CUR_MASK GENMASK(7, 4) +#define SGM4154x_PRECHRG_CURRENT_STEP_uA 60000 +#define SGM4154x_PRECHRG_I_MIN_uA 60000 +#define SGM4154x_PRECHRG_I_MAX_uA 780000 +#define SGM4154x_PRECHRG_I_DEF_uA 180000 + +/* termination current */ +#define SGM4154x_TERMCHRG_CUR_MASK GENMASK(3, 0) +#define SGM4154x_TERMCHRG_CURRENT_STEP_uA 60000 +#define SGM4154x_TERMCHRG_I_MIN_uA 60000 +#define SGM4154x_TERMCHRG_I_MAX_uA 960000 +#define SGM4154x_TERMCHRG_I_DEF_uA 120000 + +/* charge current */ +#define SGM4154x_ICHRG_CUR_MASK GENMASK(5, 0) +#define SGM4154x_ICHRG_CURRENT_STEP_uA 60000 +#define SGM4154x_ICHRG_I_MIN_uA 0 +#define SGM4154x_ICHRG_I_MAX_uA 3780000 +#define SGM4154x_ICHRG_I_DEF_uA 2040000 + +/* charge voltage */ +#define SGM4154x_VREG_V_MASK GENMASK(7, 3) +#define SGM4154x_VREG_V_MAX_uV 4624000 +#define SGM4154x_VREG_V_MIN_uV 3856000 +#define SGM4154x_VREG_V_DEF_uV 4208000 +#define SGM4154x_VREG_V_STEP_uV 32000 + +#ifdef __indicator_led_en__ +/* REG00 */ +#define SGM4154x_VREG_ICHG_MON_MASK GENMASK(6, 5) +/* REG0F */ +#define SGM4154x_VREG_STAT_SET_MASK GENMASK(3, 2) +#endif + +/* VREG Fine Tuning */ +#define SGM4154x_VREG_FT_MASK GENMASK(7, 6) +#define SGM4154x_VREG_FT_UP_8mV BIT(6) +#define SGM4154x_VREG_FT_DN_8mV BIT(7) +#define SGM4154x_VREG_FT_DN_16mV (BIT(7) | BIT(6)) + +/* iindpm current */ +#define SGM4154x_IINDPM_I_MASK GENMASK(4, 0) +#define SGM4154x_IINDPM_I_MIN_uA 100000 +#if (defined(__SGM41513_CHIP_ID__) || defined(__SGM41513A_CHIP_ID__) || defined(__SGM41513D_CHIP_ID__)) +#define SGM4154x_IINDPM_I_MAX_uA 3200000 +#else +#define SGM4154x_IINDPM_I_MAX_uA 3800000 +#endif +#define SGM4154x_IINDPM_STEP_uA 100000 +#define SGM4154x_IINDPM_DEF_uA 2400000 + +/* vindpm voltage */ +#define SGM4154x_VINDPM_V_MASK GENMASK(3, 0) +#define SGM4154x_VINDPM_V_MIN_uV 3900000 +#define SGM4154x_VINDPM_V_MAX_uV 12000000 +#define SGM4154x_VINDPM_STEP_uV 100000 +#define SGM4154x_VINDPM_DEF_uV 4600000 +#define SGM4154x_VINDPM_OS_MASK GENMASK(1, 0) + +/* dynamic vindpm track */ +#define SGM4154x_DYNAMIC_VINDPM_TRACK_MASK GENMASK(1, 0) + +/* DP DM SEL */ +#define SGM4154x_DP_VSEL_MASK GENMASK(4, 3) +#define SGM4154x_DM_VSEL_MASK GENMASK(2, 1) + +/* PUMPX SET */ +#define SGM4154x_EN_PUMPX BIT(7) +#define SGM4154x_PUMPX_UP BIT(6) +#define SGM4154x_PUMPX_DN BIT(5) + +/* customer define jeita paramter */ +#define SGM4154x_JEITA_ENABLE_MASK GENMASK(0, 0) +#define SGM4154x_JEITA_ENABLE BIT(0) +#define SGM4154x_JEITA_DISABLE 0 +#define JEITA_TEMP_ABOVE_T4_CV 0 +#define JEITA_TEMP_T3_TO_T4_CV 4100000 +#define JEITA_TEMP_T2_TO_T3_CV 4350000 +#define JEITA_TEMP_T1_TO_T2_CV 4350000 +#define JEITA_TEMP_T0_TO_T1_CV 0 +#define JEITA_TEMP_BELOW_T0_CV 0 + +#define JEITA_TEMP_ABOVE_T4_CC_CURRENT 0 +#define JEITA_TEMP_T3_TO_T4_CC_CURRENT 1000000 +#define JEITA_TEMP_T2_TO_T3_CC_CURRENT 2400000 +#define JEITA_TEMP_T1_TO_T2_CC_CURRENT 2000000 +#define JEITA_TEMP_T0_TO_T1_CC_CURRENT 0 +#define JEITA_TEMP_BELOW_T0_CC_CURRENT 0 + +#define TEMP_T4_THRES 50 +#define TEMP_T4_THRES_MINUS_X_DEGREE 47 +#define TEMP_T3_THRES 45 +#define TEMP_T3_THRES_MINUS_X_DEGREE 39 +#define TEMP_T2_THRES 20 +#define TEMP_T2_THRES_PLUS_X_DEGREE 16 +#define TEMP_T1_THRES 0 +#define TEMP_T1_THRES_PLUS_X_DEGREE 6 +#define TEMP_T0_THRES 0 +#define TEMP_T0_THRES_PLUS_X_DEGREE 0 +#define TEMP_NEG_10_THRES 0 + +#define MMI_HVDCP2_VOLTAGE_STANDARD 8000000 +#define MMI_HVDCP3_VOLTAGE_STANDARD 7500000 +#define MMI_HVDCP_DETECT_ICL_LIMIT 500000 + +struct sgm4154x_iio { + struct iio_channel *usbin_v_chan; +}; + +struct sgm4154x_init_data { + u32 ichg; /* charge current */ + u32 ilim; /* input current */ + u32 vreg; /* regulation voltage */ + u32 iterm; /* termination current */ + u32 iprechg; /* precharge current */ + u32 vlim; /* minimum system voltage limit */ + u32 max_ichg; + u32 max_vreg; + u32 vdpm_bat_track; +}; + +struct sgm4154x_state { + bool vsys_stat; + bool therm_stat; + bool online; + u8 chrg_stat; + u8 vbus_status; + + bool chrg_en; + bool hiz_en; + bool term_en; + bool vbus_gd; + u8 chrg_type; + u8 health; + u8 chrg_fault; + u8 ntc_fault; +}; + +struct sgm4154x_jeita { + int jeita_temp_above_t4_cv; + int jeita_temp_t3_to_t4_cv; + int jeita_temp_t2_to_t3_cv; + int jeita_temp_t1_to_t2_cv; + int jeita_temp_t0_to_t1_cv; + int jeita_temp_below_t0_cv; + int jeita_temp_above_t4_cc_current; + int jeita_temp_t3_to_t4_cc_current; + int jeita_temp_t2_to_t3_cc_current; + int jeita_temp_t1_to_t2_cc_current; + int jeita_temp_below_t0_cc_current; + int temp_t4_thres; + int temp_t4_thres_minus_x_degree; + int temp_t3_thres; + int temp_t3_thres_minus_x_degree; + int temp_t2_thres; + int temp_t2_thres_plus_x_degree; + int temp_t1_thres; + int temp_t1_thres_plus_x_degree; + int temp_t0_thres; + int temp_t0_thres_plus_x_degree; + int temp_neg_10_thres; +}; + +struct sgm4154x_device { + struct i2c_client *client; + struct device *dev; + struct power_supply *charger; + struct power_supply *usb; + struct power_supply *ac; + struct power_supply *battery; /* enable dynamic adjust battery voltage */ + struct mutex lock; + struct mutex i2c_rw_lock; + + struct usb_phy *usb2_phy; + struct usb_phy *usb3_phy; + struct notifier_block usb_nb; + struct work_struct usb_work; + unsigned long usb_event; + struct regmap *regmap; + + char model_name[I2C_NAME_SIZE]; + int device_id; + + struct sgm4154x_init_data init_data; + struct sgm4154x_state state; + + unsigned int status; + bool ignore_request_dpdm; + + u32 watchdog_timer; + const char *chg_dev_name; + struct charger_device *chg_dev; + struct regulator_dev *otg_rdev; + + struct work_struct charge_detect_work; + struct delayed_work charge_monitor_work; + + bool sgm4154x_suspend_flag; + + struct wakeup_source *charger_wakelock; + bool enable_sw_jeita; + struct sgm4154x_jeita data; + + /* enable dynamic adjust battery voltage */ + bool enable_dynamic_adjust_batvol; + int final_cc; + int final_cv; + int cv_tune; + + /* enable dynamic adjust vindpm */ + bool enable_dynamic_adjust_vindpm; + bool vindpm_flag; + + struct regulator *dpdm_reg; + struct regulator *otg_vbus_reg; + struct mutex regulator_lock; + bool dpdm_enabled; + int typec_apsd_rerun_done; + int real_charger_type; + + struct work_struct rerun_apsd_work; + + /*mmi qc3*/ + bool mmi_qc3_support; + struct semaphore sem_dpdm; + struct task_struct *mmi_qc3_authen_task; + wait_queue_head_t mmi_qc3_wait_que; + bool mmi_qc3_trig_flag; + bool mmi_is_qc3_authen; + u32 input_current_cache; + int pulse_cnt; + + /*mmi qc3p*/ + bool mmi_qc3p_rerun_done; + bool mmi_qc3p_wa; + int mmi_qc3p_power; + + /*mmi PD*/ + int pd_active; + + struct sgm4154x_iio iio; +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED + struct iio_channel **ext_iio_chans; +#endif + + /*wls output en/dis control*/ + int wls_en_gpio; + int wls_max_icl; + + bool i2c_err_wa_dis; + bool sgm_18W_iindpm_comp; +}; + +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED +enum WT_charger_type{ +#ifdef CONFIG_MMI_QC3P_Z350_DETECTED + WT_CHG_TYPE_OCP = 0, + WT_CHG_TYPE_FC = 0x1, + WT_CHG_TYPE_SDP = 0x2, + WT_CHG_TYPE_CDP = 0x3, + WT_CHG_TYPE_DCP = 0x4, + WT_CHG_TYPE_QC2 = 0x5, + WT_CHG_TYPE_QC3 = 0x6, + WT_CHG_TYPE_QC3P_18W = 0x8,//0x8 + WT_CHG_TYPE_QC3P_27W = 0x9, + WT_CHG_TYPE_HVDCP = 0x10, + WT6670_CHG_TYPE_UNKNOWN = 0x11, +#else + WT_CHG_TYPE_BEGIN = 0, + WT_CHG_TYPE_FC, + WT_CHG_TYPE_SDP, + WT_CHG_TYPE_CDP, + WT_CHG_TYPE_DCP, + WT_CHG_TYPE_QC2, + WT_CHG_TYPE_QC3, + WT_CHG_TYPE_OCP, + WT_CHG_TYPE_QC3P_18W,//0x8 + WT_CHG_TYPE_QC3P_27W, + WT6670_CHG_TYPE_UNKNOWN, +#endif +}; + +enum mmi_qc3p_ext_iio_channels { + /*qc3p*/ + SMB5_USB_REAL_TYPE, + SMB5_QC3P_POWER, + SMB5_QC3P_START_DETECT, + SMB5_QC3P_DETECTION_READY, + SMB5_QC3P_START_POLICY, + SMB5_BC12_START_DETECT, + SMB5_BC12_DETECTION_READY, + SMB5_READ_USBIN_VOLTAGE, + SMB5_READ_BC12_CHG_TYPE, +}; + +static const char * const mmi_qc3p_ext_iio_chan_name[] = { + /*qc3p*/ + [SMB5_USB_REAL_TYPE] = "wt6670_usb_real_type", + [SMB5_QC3P_POWER] = "wt6670_usb_qc3p_power", + [SMB5_QC3P_START_DETECT] = "wt6670_start_detection", + [SMB5_QC3P_DETECTION_READY] "wt6670_detection_ready", + [SMB5_QC3P_START_POLICY] "qc3p_start_policy", + [SMB5_BC12_START_DETECT] = "wt6670_start_bc12_detection", + [SMB5_BC12_DETECTION_READY] = "wt6670_detection_bc12_ready", + [SMB5_READ_USBIN_VOLTAGE] = "read_usbin_voltage", + [SMB5_READ_BC12_CHG_TYPE] = "wt6670_bc12_chg_type", +}; +bool qc3p_update_policy(struct sgm4154x_device *chip); +#endif +#endif /* _SGM4154x_CHARGER_H */ diff --git a/drivers/power/sm5602_fg_mmi/Android.mk b/drivers/power/sm5602_fg_mmi/Android.mk new file mode 100644 index 000000000000..be2b1df8c65a --- /dev/null +++ b/drivers/power/sm5602_fg_mmi/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := sm5602_fg_mmi.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/sm5602_fg_mmi/Kbuild b/drivers/power/sm5602_fg_mmi/Kbuild new file mode 100644 index 000000000000..de7af59dabd3 --- /dev/null +++ b/drivers/power/sm5602_fg_mmi/Kbuild @@ -0,0 +1,7 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/drivers/power/sm5602_fg_mmi + +sm5602_fg_mmi-objs += sm5602_fg.o +obj-m += sm5602_fg_mmi.o diff --git a/drivers/power/sm5602_fg_mmi/Makefile b/drivers/power/sm5602_fg_mmi/Makefile new file mode 100644 index 000000000000..90dc363524bc --- /dev/null +++ b/drivers/power/sm5602_fg_mmi/Makefile @@ -0,0 +1,31 @@ +ifeq ($(strip $(MODULE_KERNEL_VERSION)), 5.15) + +all: modules + +modules: + $(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 + +else + +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + +endif diff --git a/drivers/power/sm5602_fg_mmi/sm5602_fg.c b/drivers/power/sm5602_fg_mmi/sm5602_fg.c new file mode 100644 index 000000000000..4289d3515d9c --- /dev/null +++ b/drivers/power/sm5602_fg_mmi/sm5602_fg.c @@ -0,0 +1,2994 @@ +/* + * Fuelgauge battery driver + * + * Copyright (C) 2018 Siliconmitus + * + * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ + +#define pr_fmt(fmt) "[sm5602] %s(%d): " fmt, __func__, __LINE__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sm5602_fg.h" + +#undef pr_debug +#define pr_debug pr_err +#undef pr_info +#define pr_info pr_err +#undef dev_dbg +#define dev_dbg dev_err + +#define INVALID_REG_ADDR 0xFF +#define RET_ERR -1 +#define queue_delayed_work_time 8000//8000 +#define queue_start_work_time 50 +#define SM_CUR_UNIT 1000 +#define ENABLE_MAP_SOC + +#ifdef ENABLE_MAP_SOC +#define MAP_MAX_SOC 98 +#define MAP_RATE_SOC 985 +#define MAP_MIN_SOC 4 +#endif + +enum sm_fg_reg_idx { + SM_FG_REG_DEVICE_ID = 0, + SM_FG_REG_CNTL, + SM_FG_REG_INT, + SM_FG_REG_INT_MASK, + SM_FG_REG_STATUS, + SM_FG_REG_SOC, + SM_FG_REG_OCV, + SM_FG_REG_VOLTAGE, + SM_FG_REG_CURRENT, + SM_FG_REG_TEMPERATURE_IN, + SM_FG_REG_TEMPERATURE_EX, + SM_FG_REG_V_L_ALARM, + SM_FG_REG_V_H_ALARM, + SM_FG_REG_A_H_ALARM, + SM_FG_REG_T_IN_H_ALARM, + SM_FG_REG_SOC_L_ALARM, + SM_FG_REG_FG_OP_STATUS, + SM_FG_REG_TOPOFFSOC, + SM_FG_REG_PARAM_CTRL, + SM_FG_REG_SHUTDOWN, + SM_FG_REG_VIT_PERIOD, + SM_FG_REG_CURRENT_RATE, + SM_FG_REG_BAT_CAP, + SM_FG_REG_CURR_OFFSET, + SM_FG_REG_CURR_SLOPE, + SM_FG_REG_MISC, + SM_FG_REG_RESET, + SM_FG_REG_RSNS_SEL, + SM_FG_REG_VOL_COMP, + NUM_REGS, +}; + +static u8 sm5602_regs[NUM_REGS] = { + 0x00, /* DEVICE_ID */ + 0x01, /* CNTL */ + 0x02, /* INT */ + 0x03, /* INT_MASK */ + 0x04, /* STATUS */ + 0x05, /* SOC */ + 0x06, /* OCV */ + 0x07, /* VOLTAGE */ + 0x08, /* CURRENT */ + 0x09, /* TEMPERATURE_IN */ + 0x0A, /* TEMPERATURE_EX */ + 0x0C, /* V_L_ALARM */ + 0x0D, /* V_H_ALARM */ + 0x0E, /* A_H_ALARM */ + 0x0F, /* T_IN_H_ALARM */ + 0x10, /* SOC_L_ALARM */ + 0x11, /* FG_OP_STATUS */ + 0x12, /* TOPOFFSOC */ + 0x13, /* PARAM_CTRL */ + 0x14, /* SHUTDOWN */ + 0x1A, /* VIT_PERIOD */ + 0x1B, /* CURRENT_RATE */ + 0x62, /* BAT_CAP */ + 0x73, /* CURR_OFFSET */ + 0x74, /* CURR_SLOPE */ + 0x90, /* MISC */ + 0x91, /* RESET */ + 0x95, /* RSNS_SEL */ + 0x96, /* VOL_COMP */ +}; + +enum sm_fg_device { + SM5602, +}; + +enum sm_fg_temperature_type { + TEMPERATURE_IN = 0, + TEMPERATURE_EX, +}; + +const unsigned char *device2str[] = { + "sm5602", +}; + +enum battery_table_type { + BATTERY_TABLE0 = 0, + BATTERY_TABLE1, + BATTERY_TABLE2, + BATTERY_TABLE_MAX, +}; + +//struct sm_fg_chip; + +struct sm_fg_chip { + struct device *dev; + struct i2c_client *client; + struct mutex i2c_rw_lock; /* I2C Read/Write Lock */ + struct mutex data_lock; /* Data Lock */ + u8 chip; + u8 regs[NUM_REGS]; + int batt_id; + //int gpio_int; + /* Status Tracking */ + bool batt_present; + bool batt_fc; /* Battery Full Condition */ + bool batt_ot; /* Battery Over Temperature */ + bool batt_ut; /* Battery Under Temperature */ + bool batt_soc1; /* SOC Low */ + bool batt_socp; /* SOC Poor */ + bool batt_dsg; /* Discharge Condition*/ + int batt_soc; + int batt_ocv; + int batt_fcc; /* Full charge capacity */ + int batt_fcc_design; /* Design Full charge capacity */ + int batt_volt; + int aver_batt_volt; + int batt_temp; + int batt_curr; + int is_charging; /* Charging informaion from charger IC */ + int batt_soc_cycle; /* Battery SOC cycle */ + int topoff_soc; + int top_off; + int iocv_error_count; + + /* previous battery voltage current*/ + int p_batt_voltage; + int p_batt_current; + + /* DT */ + bool en_temp_ex; + bool en_temp_in; + bool en_batt_det; + bool iocv_man_mode; + int aging_ctrl; + int batt_rsns; /* Sensing resistor value */ + int ntc_exist; + bool factory_mode; + int cycle_cfg; + int fg_irq_set; + int low_soc1; + int low_soc2; + int v_l_alarm; + int v_h_alarm; + int battery_table_num; + int misc; + int batt_v_max; + int min_cap; + u32 common_param_version; + int t_l_alarm_in; + int t_h_alarm_in; + u32 t_l_alarm_ex; + u32 t_h_alarm_ex; + + /* Battery Data */ + int battery_table[BATTERY_TABLE_MAX][FG_TABLE_LEN]; + signed short battery_temp_table[FG_TEMP_TABLE_CNT_MAX]; /* -20~80 Degree */ + int alpha; + int beta; + int rs; + int rs_value[4]; + int vit_period; + int mix_value; + const char *battery_type; + int volt_cal; + int curr_offset; + int curr_slope; + int cap; + int n_tem_poff; + int n_tem_poff_offset; + int batt_max_voltage_uv; + int temp_std; + int en_high_fg_temp_offset; + int high_fg_temp_offset_denom; + int high_fg_temp_offset_fact; + int en_low_fg_temp_offset; + int low_fg_temp_offset_denom; + int low_fg_temp_offset_fact; + int en_high_fg_temp_cal; + int high_fg_temp_p_cal_denom; + int high_fg_temp_p_cal_fact; + int high_fg_temp_n_cal_denom; + int high_fg_temp_n_cal_fact; + int en_low_fg_temp_cal; + int low_fg_temp_p_cal_denom; + int low_fg_temp_p_cal_fact; + int low_fg_temp_n_cal_denom; + int low_fg_temp_n_cal_fact; + int en_high_temp_cal; + int high_temp_p_cal_denom; + int high_temp_p_cal_fact; + int high_temp_n_cal_denom; + int high_temp_n_cal_fact; + int en_low_temp_cal; + int low_temp_p_cal_denom; + int low_temp_p_cal_fact; + int low_temp_n_cal_denom; + int low_temp_n_cal_fact; + u32 battery_param_version; + + struct delayed_work monitor_work; + struct workqueue_struct *smfg_workqueue; +// unsigned long last_update; + + /* Debug */ + int skip_reads; + int skip_writes; + int fake_soc; + int fake_temp; + struct dentry *debug_root; + struct power_supply *batt_psy; + struct power_supply *bms_psy; + //struct power_supply fg_psy; +}; + +static int show_registers(struct seq_file *m, void *data); +static bool fg_init(struct i2c_client *client); + +static int __fg_read_word(struct i2c_client *client, u8 reg, u16 *val) +{ + s32 ret; + + ret = i2c_smbus_read_word_data(client, reg); + if (ret < 0) { + pr_err("i2c read word fail: can't read from reg 0x%02X\n", reg); + return ret; + } + + *val = (u16)ret; + + return 0; +} + +static int __fg_write_word(struct i2c_client *client, u8 reg, u16 val) +{ + s32 ret; + + ret = i2c_smbus_write_word_data(client, reg, val); + if (ret < 0) { + pr_err("i2c write word fail: can't write 0x%02X to reg 0x%02X\n", + val, reg); + return ret; + } + + return 0; +} + +static int fg_read_word(struct sm_fg_chip *sm, u8 reg, u16 *val) +{ + int ret; + + if (sm->skip_reads) { + *val = 0; + return 0; + } + /* TODO:check little endian */ + mutex_lock(&sm->i2c_rw_lock); + ret = __fg_read_word(sm->client, reg, val); + mutex_unlock(&sm->i2c_rw_lock); + + return ret; +} + +static int fg_write_word(struct sm_fg_chip *sm, u8 reg, u16 val) +{ + int ret; + + if (sm->skip_writes) + return 0; + + /* TODO:check little endian */ + mutex_lock(&sm->i2c_rw_lock); + ret = __fg_write_word(sm->client, reg, val); + mutex_unlock(&sm->i2c_rw_lock); + + return ret; +} + +#define FG_STATUS_SLEEP BIT(10) +#define FG_STATUS_BATT_PRESENT BIT(9) +#define FG_STATUS_SOC_UPDATE BIT(8) +#define FG_STATUS_TOPOFF BIT(7) +#define FG_STATUS_LOW_SOC2 BIT(6) +#define FG_STATUS_LOW_SOC1 BIT(5) +#define FG_STATUS_HIGH_CURRENT BIT(4) +#define FG_STATUS_HIGH_TEMPERATURE BIT(3) +#define FG_STATUS_LOW_TEMPERATURE BIT(2) +#define FG_STATUS_HIGH_VOLTAGE BIT(1) +#define FG_STATUS_LOW_VOLTAGE BIT(0) + +#define FG_OP_STATUS_CHG_DISCHG BIT(15) //if can use the charger information, plz use the charger information for CHG/DISCHG condition. + +static int fg_read_status(struct sm_fg_chip *sm) +{ + int ret; + u16 flags1, flags2; + + ret = fg_read_word(sm, sm->regs[SM_FG_REG_STATUS], &flags1); + if (ret < 0) + return ret; + + ret = fg_read_word(sm, sm->regs[SM_FG_REG_FG_OP_STATUS], &flags2); + if (ret < 0) + return ret; + + mutex_lock(&sm->data_lock); + if(sm->factory_mode && !sm->ntc_exist) + sm->batt_present = true; + else + sm->batt_present = !!(flags1 & FG_STATUS_BATT_PRESENT); + sm->batt_ot = !!(flags1 & FG_STATUS_HIGH_TEMPERATURE); + sm->batt_ut = !!(flags1 & FG_STATUS_LOW_TEMPERATURE); + sm->batt_fc = !!(flags1 & FG_STATUS_TOPOFF); + sm->batt_soc1 = !!(flags1 & FG_STATUS_LOW_SOC2); + sm->batt_socp = !!(flags1 & FG_STATUS_LOW_SOC1); + sm->batt_dsg = !!!(flags2 & FG_OP_STATUS_CHG_DISCHG); + mutex_unlock(&sm->data_lock); + + return 0; +} +#if 0 +static int fg_status_changed(struct sm_fg_chip *sm) +{ + //cancel_delayed_work(&sm->monitor_work); + //schedule_delayed_work(&sm->monitor_work, 0); + //power_supply_changed(sm->batt_psy); + printk("wangtao fg_status_changed\n"); + return IRQ_HANDLED; +} + +static irqreturn_t fg_irq_thread(int irq, void *dev_id) +{ + struct sm_fg_chip *sm = dev_id; + int ret; + u16 data_int, data_int_mask; + + /* Read INT */ + ret = fg_read_word(sm, sm->regs[SM_FG_REG_INT_MASK], &data_int_mask); + if (ret < 0){ + pr_err("Failed to read INT_MASK, ret = %d\n", ret); + return ret; + } + + ret = fg_write_word(sm, sm->regs[SM_FG_REG_INT_MASK], 0x8000 | data_int_mask); + if (ret < 0) { + pr_err("Failed to write 0x8000 | INIT_MARK, ret = %d\n", ret); + return ret; + } + + ret = fg_read_word(sm, sm->regs[SM_FG_REG_INT], &data_int); + if (ret < 0) { + pr_err("Failed to write REG_INT, ret = %d\n", ret); + return ret; + } + + ret = fg_write_word(sm, sm->regs[SM_FG_REG_INT_MASK], 0x03FF & data_int_mask); + if (ret < 0) { + pr_err("Failed to write INIT_MARK, ret = %d\n", ret); + return ret; + } + + fg_status_changed(sm); + + pr_info("fg_read_int = 0x%x\n", data_int); + + return 0; +} +#endif + +static int fg_read_soc(struct sm_fg_chip *sm) +{ + int ret; + unsigned int soc = 0; + u16 data = 0; + + ret = fg_read_word(sm, sm->regs[SM_FG_REG_SOC], &data); + if (ret < 0) { + pr_err("could not read SOC, ret = %d\n", ret); + return ret; + } else { + /*integer bit;*/ + soc = ((data&0x7f00)>>8) * 10; + /* integer + fractional bit*/ + soc = soc + (((data&0x00ff)*10)/256); + + if (data & 0x8000) { + soc *= -1; + } + } + + pr_info("fg_read_soc soc=%d\n",soc); + return soc; +} + +static unsigned int fg_read_ocv(struct sm_fg_chip *sm) +{ + int ret; + u16 data = 0; + unsigned int ocv;// = 3500; /*3500 means 3500mV*/ + + ret = fg_read_word(sm, sm->regs[SM_FG_REG_OCV], &data); + if (ret<0) { + pr_err("could not read OCV, ret = %d\n", ret); + ocv = 4000; + } else { + ocv = (((data&0x0fff)*1000)/2048) + (((data&0xf000)>>11)*1000); + } + + return ocv; //mV +} + +bool is_factory_mode(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + bool factory_mode = false; + const char *bootargs = NULL; + char *bootmode = NULL; + char *end = NULL; + + if (!np) + return factory_mode; + + if (!of_property_read_string(np, "bootargs", &bootargs)) { + bootmode = strstr(bootargs, "androidboot.mode="); + if (bootmode) { + end = strpbrk(bootmode, " "); + bootmode = strpbrk(bootmode, "="); + } + if (bootmode && + end > bootmode && + strnstr(bootmode, "mot-factory", end - bootmode)) { + factory_mode = true; + } + } + of_node_put(np); + + return factory_mode; +} + + +static int _calculate_battery_temp_ex(struct sm_fg_chip *sm, u16 uval) +{ + int i = 0, temp = 0; + signed short val = 0; + + if ((uval >= 0x8001) && (uval <= 0x823B)) { + pr_info("sp_range uval = 0x%x\n",uval); + uval = 0x0000; + } + + val = uval; + + if (val >= sm->battery_temp_table[0]) { + temp = -20; //Min : -20 + } else if (val <= sm->battery_temp_table[FG_TEMP_TABLE_CNT_MAX-1]) { + temp = 80; //Max : 80 + } else { + for (i = 0; i < FG_TEMP_TABLE_CNT_MAX; i++) { + if (val >= sm->battery_temp_table[i]) { + temp = -20 + i; //[ex] ~-20 : -20(skip), -19.9~-19.0 : 19, -18.9~-18 : 18, .., 0.9~0 : 0 + if ((temp >= 1) && (val != sm->battery_temp_table[i]))//+ range 0~79 degree. In same value case, no needed (temp-1) + temp = temp -1; //[ex] 0.1~0.9 : 0, 1.1~1.9 : 1, .., 79.1~79.9 : 79 + break; + } + } + } + + pr_info("uval = 0x%x, val = 0x%x, temp = %d\n",uval, val, temp); + + if(sm->factory_mode && !sm->ntc_exist && ((-20 == temp) || (80 == temp))) + temp = 25; + return temp; +} + +static int fg_read_temperature(struct sm_fg_chip *sm, enum sm_fg_temperature_type temperature_type) +{ + int ret, temp = 0; + u16 data = 0; + + switch (temperature_type) { + case TEMPERATURE_IN: + ret = fg_read_word(sm, sm->regs[SM_FG_REG_TEMPERATURE_IN], &data); + if (ret < 0) { + pr_err("could not read temperature in , ret = %d\n", ret); + return ret; + } else { + /*integer bit*/ + temp = ((data & 0x00FF)); + if (data & 0x8000) + temp *= -1; + } + pr_info("fg_read_temperature_in temp_in=%d\n", temp); + break; + case TEMPERATURE_EX: + ret = fg_read_word(sm, sm->regs[SM_FG_REG_TEMPERATURE_EX], &data); + if (ret < 0) { + pr_err("could not read temperature ex , ret = %d\n", ret); + return ret; + } else { + temp = _calculate_battery_temp_ex(sm, data); + } + temp = temp * 10; + pr_info("fg_read_temperature_ex temp_ex=%d\n", temp); + break; + + default: + return -EINVAL; + } + + return temp; +} + +/* + * Return : mV + */ +static int fg_read_volt(struct sm_fg_chip *sm) +{ + int ret = 0; + int volt = 0; + u16 data = 0; + + ret = fg_read_word(sm, sm->regs[SM_FG_REG_VOLTAGE], &data); + if (ret < 0) { + pr_err("could not read voltage, ret = %d\n", ret); + return ret; + } else { + /* */ + volt = 1800 * (data & 0x7FFF) / 19622; + if (data&0x8000) + volt *= -1; + volt += 2700; + } + + /*cal avgvoltage*/ + sm->aver_batt_volt = (((sm->aver_batt_volt)*4) + volt)/5; + volt = volt * 1000; + return volt; +} + +static int fg_get_cycle(struct sm_fg_chip *sm) +{ + int ret; + int cycle; + u16 data = 0; + + ret = fg_read_word(sm, FG_REG_SOC_CYCLE, &data); + if (ret<0) { + pr_err("read cycle reg fail ret = %d\n", ret); + cycle = 0; + } else { + cycle = data&0x01FF; + } + + return cycle; +} + +static int fg_read_current(struct sm_fg_chip *sm) +{ + int ret = 0, rsns = 0; + u16 data = 0; + int curr = 0; + + ret = fg_read_word(sm, sm->regs[SM_FG_REG_CURRENT], &data); + if (ret < 0) { + pr_err("could not read current, ret = %d\n", ret); + return ret; + } else { + if (sm->batt_rsns == -EINVAL) { + pr_err("could not read sm->batt_rsns, rsns = 10mohm\n"); + rsns = 10; + } else { + sm->batt_rsns == 0 ? rsns = 5 : (rsns = sm->batt_rsns*10); + } + + curr = ((data & 0x7FFF) * 1250 / 511 / rsns ); + + if (data & 0x8000) + curr *= -1; + } + + curr *= SM_CUR_UNIT * (-1); + pr_err("curr = %d,data=%d\n",curr,data); + + return curr; +} + +static int fg_read_fcc(struct sm_fg_chip *sm) +{ + int ret = 0; + int fcc = 0; + u16 data = 0; + int64_t temp = 0; + + ret = fg_read_word(sm, sm->regs[SM_FG_REG_BAT_CAP], &data); + if (ret < 0) { + pr_err("could not read FCC, ret=%d\n", ret); + return ret; + } else { + /* */ + temp = div_s64((data & 0x7FFF) * 1000, 2048); + fcc = temp; + } + + return fcc; +} + +#define FG_SOFT_RESET 0xA6 +static int fg_reset(struct sm_fg_chip *sm) +{ + int ret; + + ret = fg_write_word(sm, sm->regs[SM_FG_REG_RESET], FG_SOFT_RESET); + if (ret < 0) { + pr_err("could not reset, ret=%d\n", ret); + return ret; + } + + msleep(600); + + return 0; +} + +static int get_battery_status(struct sm_fg_chip *sm) +{ + union power_supply_propval ret = {0,}; + int rc; + + if (sm->batt_psy == NULL) + sm->batt_psy = power_supply_get_by_name("battery"); + if (sm->batt_psy) { + /* if battery has been registered, use the status property */ + rc = power_supply_get_property(sm->batt_psy, + POWER_SUPPLY_PROP_STATUS, &ret); + if (rc) { + pr_err("Battery does not export status: %d\n", rc); + return POWER_SUPPLY_STATUS_UNKNOWN; + } + return ret.intval; + } + + /* Default to false if the battery power supply is not registered. */ + pr_err("battery power supply is not registered\n"); + return POWER_SUPPLY_STATUS_UNKNOWN; +} + +static bool is_battery_charging(struct sm_fg_chip *sm) +{ + return get_battery_status(sm) == POWER_SUPPLY_STATUS_CHARGING; +} + +static void fg_vbatocv_check(struct sm_fg_chip *sm) +{ + pr_info("%s: sm->batt_curr (%d), sm->is_charging (%d), sm->top_off (%d), sm->batt_soc (%d)\n", + __func__, sm->batt_curr, sm->is_charging, sm->top_off, sm->batt_soc); + + if((abs(sm->batt_curr)<50) || + ((sm->is_charging) && (sm->batt_curr<(sm->top_off)) && + (sm->batt_curr>(sm->top_off/3)) && (sm->batt_soc>=900))) + { + if(abs(sm->batt_ocv-sm->batt_volt)>30) + { + sm->iocv_error_count ++; + } + + pr_info("%s: sm5602 FG iocv_error_count (%d)\n", __func__, sm->iocv_error_count); + + if(sm->iocv_error_count > 5) + sm->iocv_error_count = 6; + } + else + { + sm->iocv_error_count = 0; + } + + if(sm->iocv_error_count > 5) + { + pr_info("%s: p_v - v = (%d)\n", __func__, sm->p_batt_voltage - sm->batt_volt); + if(abs(sm->p_batt_voltage - sm->batt_volt)>15) + { + sm->iocv_error_count = 0; + } + else + { + pr_info("%s: mode change to RS m mode\n", __func__); + fg_write_word(sm, FG_REG_RS_2, sm->rs_value[0]); + } + } + else + { + if((sm->p_batt_voltage < sm->n_tem_poff) && + (sm->batt_volt < sm->n_tem_poff) && (!sm->is_charging)) + { + pr_info("%s: mode change to normal tem RS m mode\n", __func__); + if((sm->p_batt_voltage < + (sm->n_tem_poff - sm->n_tem_poff_offset)) && + (sm->batt_volt < + (sm->n_tem_poff - sm->n_tem_poff_offset))) + { + fg_write_word(sm, FG_REG_RS_2, sm->rs_value[0]>>1); + } + else + { + fg_write_word(sm, FG_REG_RS_2, sm->rs_value[0]); + } + } + else + { + pr_info("%s: mode change to RS a mode\n", __func__); + + fg_write_word(sm, FG_REG_RS_2, sm->rs_value[2]); + } + } + sm->p_batt_voltage = sm->batt_volt; + sm->p_batt_current = sm->batt_curr; + // iocv error case cover end +} + + +static int fg_cal_carc (struct sm_fg_chip *sm) +{ + int curr_cal = 0, p_curr_cal=0, n_curr_cal=0, p_delta_cal=0, n_delta_cal=0, p_fg_delta_cal=0, n_fg_delta_cal=0, temp_curr_offset=0; + int temp_gap, fg_temp_gap = 0; + int ret = 0; + u16 data[3] = {0,}; + + fg_vbatocv_check(sm); + + sm->is_charging = is_battery_charging(sm); //From Charger Driver + + //fg_temp_gap = (sm->batt_temp/10) - sm->temp_std; + fg_temp_gap = sm->batt_temp - sm->temp_std; + + temp_curr_offset = sm->curr_offset; + if(sm->en_high_fg_temp_offset && (fg_temp_gap > 0)) + { + if(temp_curr_offset & 0x0080) + { + temp_curr_offset = -(temp_curr_offset & 0x007F); + } + temp_curr_offset = temp_curr_offset + (fg_temp_gap / sm->high_fg_temp_offset_denom)*sm->high_fg_temp_offset_fact; + if(temp_curr_offset < 0) + { + temp_curr_offset = -temp_curr_offset; + temp_curr_offset = temp_curr_offset|0x0080; + } + } + else if (sm->en_low_fg_temp_offset && (fg_temp_gap < 0)) + { + if(temp_curr_offset & 0x0080) + { + temp_curr_offset = -(temp_curr_offset & 0x007F); + } + temp_curr_offset = temp_curr_offset + ((-fg_temp_gap) / sm->low_fg_temp_offset_denom)*sm->low_fg_temp_offset_fact; + if(temp_curr_offset < 0) + { + temp_curr_offset = -temp_curr_offset; + temp_curr_offset = temp_curr_offset|0x0080; + } + } + temp_curr_offset = temp_curr_offset | (temp_curr_offset<<8); + ret = fg_write_word(sm, FG_REG_CURR_IN_OFFSET, temp_curr_offset); + if (ret < 0) { + pr_err("Failed to write CURR_IN_OFFSET, ret = %d\n", ret); + return ret; + } else { + pr_err("CURR_IN_OFFSET [0x%x] = 0x%x\n", FG_REG_CURR_IN_OFFSET, temp_curr_offset); + } + + n_curr_cal = (sm->curr_slope & 0xFF00)>>8; + p_curr_cal = (sm->curr_slope & 0x00FF); + + if (sm->en_high_fg_temp_cal && (fg_temp_gap > 0)) + { + p_fg_delta_cal = (fg_temp_gap / sm->high_fg_temp_p_cal_denom)*sm->high_fg_temp_p_cal_fact; + n_fg_delta_cal = (fg_temp_gap / sm->high_fg_temp_n_cal_denom)*sm->high_fg_temp_n_cal_fact; + } + else if (sm->en_low_fg_temp_cal && (fg_temp_gap < 0)) + { + fg_temp_gap = -fg_temp_gap; + p_fg_delta_cal = (fg_temp_gap / sm->low_fg_temp_p_cal_denom)*sm->low_fg_temp_p_cal_fact; + n_fg_delta_cal = (fg_temp_gap / sm->low_fg_temp_n_cal_denom)*sm->low_fg_temp_n_cal_fact; + } + p_curr_cal = p_curr_cal + (p_fg_delta_cal); + n_curr_cal = n_curr_cal + (n_fg_delta_cal); + + pr_info("%s: <%d %d %d %d %d %d %d %d %d %d>, temp_fg = %d ,p_curr_cal = 0x%x, n_curr_cal = 0x%x, " + "batt_temp = %d\n", + __func__, + sm->en_high_fg_temp_cal, + sm->high_fg_temp_p_cal_denom, sm->high_fg_temp_p_cal_fact, + sm->high_fg_temp_n_cal_denom, sm->high_fg_temp_n_cal_fact, + sm->en_low_fg_temp_cal, + sm->low_fg_temp_p_cal_denom, sm->low_fg_temp_p_cal_fact, + sm->low_fg_temp_n_cal_denom, sm->low_fg_temp_n_cal_fact, + sm->batt_temp, p_curr_cal, n_curr_cal, sm->batt_temp); + + //temp_gap = (sm->batt_temp/10) - sm->temp_std; + temp_gap = sm->batt_temp - sm->temp_std; + if (sm->en_high_temp_cal && (temp_gap > 0)) + { + p_delta_cal = (temp_gap / sm->high_temp_p_cal_denom)*sm->high_temp_p_cal_fact; + n_delta_cal = (temp_gap / sm->high_temp_n_cal_denom)*sm->high_temp_n_cal_fact; + } + else if (sm->en_low_temp_cal && (temp_gap < 0)) + { + temp_gap = -temp_gap; + p_delta_cal = (temp_gap / sm->low_temp_p_cal_denom)*sm->low_temp_p_cal_fact; + n_delta_cal = (temp_gap / sm->low_temp_n_cal_denom)*sm->low_temp_n_cal_fact; + } + p_curr_cal = p_curr_cal + (p_delta_cal); + n_curr_cal = n_curr_cal + (n_delta_cal); + + curr_cal = (n_curr_cal << 8) | p_curr_cal; + + ret = fg_write_word(sm, FG_REG_CURR_IN_SLOPE, curr_cal); + if (ret < 0) { + pr_err("Failed to write CURR_IN_SLOPE, ret = %d\n", ret); + return ret; + } else { + pr_err("write CURR_IN_SLOPE [0x%x] = 0x%x\n", FG_REG_CURR_IN_SLOPE, curr_cal); + } + + pr_info("%s: <%d %d %d %d %d %d %d %d %d %d>, " + "p_curr_cal = 0x%x, n_curr_cal = 0x%x, curr_cal = 0x%x\n", + __func__, + sm->en_high_temp_cal, + sm->high_temp_p_cal_denom, sm->high_temp_p_cal_fact, + sm->high_temp_n_cal_denom, sm->high_temp_n_cal_fact, + sm->en_low_temp_cal, + sm->low_temp_p_cal_denom, sm->low_temp_p_cal_fact, + sm->low_temp_n_cal_denom, sm->low_temp_n_cal_fact, + p_curr_cal, n_curr_cal, curr_cal); + + ret = fg_read_word(sm, 0x28, &data[0]); + ret |= fg_read_word(sm, 0x82, &data[1]); + ret |= fg_read_word(sm, 0x83, &data[2]); + if (ret < 0) { + pr_err("could not read , ret = %d\n", ret); + return ret; + } else + pr_info("0x28=0x%x, 0x82=0x%x, 0x83=0x%x\n", data[0],data[1],data[2]); + + return 1; +} + +#if 0 +static int fg_get_batt_status(struct sm_fg_chip *sm) +{ + if (!sm->batt_present) + return POWER_SUPPLY_STATUS_UNKNOWN; + //else if (sm->batt_fc) + // return POWER_SUPPLY_STATUS_FULL; + else if (sm->batt_dsg) + return POWER_SUPPLY_STATUS_DISCHARGING; + else if (sm->batt_curr > 0) + return POWER_SUPPLY_STATUS_CHARGING; + else + return POWER_SUPPLY_STATUS_NOT_CHARGING; + +} +#endif + +static int fg_get_batt_capacity_level(struct sm_fg_chip *sm) +{ + if (!sm->batt_present) + return POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; + else if (sm->batt_fc) + return POWER_SUPPLY_CAPACITY_LEVEL_FULL; + else if (sm->batt_soc1) + return POWER_SUPPLY_CAPACITY_LEVEL_LOW; + else if (sm->batt_socp) + return POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + else + return POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + +} + +static int fg_get_charge_counter(struct sm_fg_chip *sm) +{ + int charge_counter; + int full_capacity; + int ui_soc; + + full_capacity = fg_read_fcc(sm) * 1000; +#ifdef ENABLE_MAP_SOC + ui_soc = (((100*(sm->batt_soc*10+MAP_MAX_SOC))/MAP_RATE_SOC)-MAP_MIN_SOC)/10; +#else + ui_soc = sm->batt_soc/10; +#endif + charge_counter = div_s64(full_capacity * ui_soc, 100); + + return charge_counter; +} + +static int fg_get_batt_health(struct sm_fg_chip *sm) +{ + if (!sm->batt_present) + return POWER_SUPPLY_HEALTH_UNKNOWN; + else if (sm->batt_ot) + return POWER_SUPPLY_HEALTH_OVERHEAT; + else if (sm->batt_ut) + return POWER_SUPPLY_HEALTH_COLD; + else + return POWER_SUPPLY_HEALTH_GOOD; +} + +static enum power_supply_property fg_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CHARGE_FULL, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CYCLE_COUNT, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, + POWER_SUPPLY_PROP_CHARGE_COUNTER, +}; + +static void fg_monitor_workfunc(struct work_struct *work); + +static int fg_get_property(struct power_supply *psy, enum power_supply_property psp, + union power_supply_propval *val) +{ + + struct sm_fg_chip *sm = power_supply_get_drvdata(psy); + int ret; + /* pr_debug("fg_get_property\n"); */ + switch (psp) { +#if 0 + case POWER_SUPPLY_PROP_STATUS: + val->intval = fg_get_batt_status(sm); +/* pr_info("fg POWER_SUPPLY_PROP_STATUS:%d\n", val->intval); */ + break; +#endif + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = fg_read_volt(sm); + mutex_lock(&sm->data_lock); + if (ret >= 0) + sm->batt_volt = ret; + val->intval = sm->batt_volt; + mutex_unlock(&sm->data_lock); + break; + + case POWER_SUPPLY_PROP_PRESENT: + val->intval = sm->batt_present; + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + mutex_lock(&sm->data_lock); + sm->batt_curr = fg_read_current(sm); + val->intval = sm->batt_curr; + mutex_unlock(&sm->data_lock); + break; + + case POWER_SUPPLY_PROP_CAPACITY: + if (sm->fake_soc >= 0) { + val->intval = sm->fake_soc; + break; + } + ret = fg_read_soc(sm); + mutex_lock(&sm->data_lock); + if (ret >= 0) + sm->batt_soc = ret; + //val->intval = sm->batt_soc; +#ifdef ENABLE_MAP_SOC + val->intval = (((100*(sm->batt_soc*10+MAP_MAX_SOC))/MAP_RATE_SOC)-MAP_MIN_SOC)/10; +#else + val->intval = sm->batt_soc/10; +#endif + pr_info("fg POWER_SUPPLY_PROP_STATUS:%d\n", val->intval); + mutex_unlock(&sm->data_lock); + break; + + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + val->intval = fg_get_batt_capacity_level(sm); + break; + + case POWER_SUPPLY_PROP_TEMP: + if (sm->fake_temp != -EINVAL) { + val->intval = sm->fake_temp; + break; + } + if (sm->en_temp_in) + ret = fg_read_temperature(sm, TEMPERATURE_IN); + else if (sm->en_temp_ex) + ret = fg_read_temperature(sm, TEMPERATURE_EX); + else + ret = -ENODATA; + mutex_lock(&sm->data_lock); + if (ret > 0) + sm->batt_temp = ret; + val->intval = sm->batt_temp; + mutex_unlock(&sm->data_lock); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL: + ret = fg_read_fcc(sm); + mutex_lock(&sm->data_lock); + if (ret > 0) + sm->batt_fcc = ret; + val->intval = sm->batt_fcc * 1000; + mutex_unlock(&sm->data_lock); + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = fg_get_batt_health(sm); + break; + + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + case POWER_SUPPLY_PROP_CYCLE_COUNT: + val->intval = fg_get_cycle(sm); + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + val->intval = sm->batt_fcc_design * 1000; + break; + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + val->intval = fg_get_charge_counter(sm); + break; + + default: + return -EINVAL; + } + return 0; +} + +static int fg_set_property(struct power_supply *psy, + enum power_supply_property prop, + const union power_supply_propval *val) +{ + int ret = 0; + + switch(prop) { + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/* +static int fg_prop_is_writeable(struct power_supply *psy,enum power_supply_property prop) +{ + int ret; + + switch (prop) { + case POWER_SUPPLY_PROP_TEMP: + case POWER_SUPPLY_PROP_CAPACITY: + ret = 1; + break; + default: + ret = 0; + break; + } + return ret; +} + + +static void fg_external_power_changed(struct power_supply *psy) +{ + struct sm_fg_chip *sm = container_of(psy, struct sm_fg_chip, fg_psy); + + cancel_delayed_work(&sm->monitor_work); + schedule_delayed_work(&sm->monitor_work, 0); +} +*/ + +static const u8 fg_dump_regs[] = { + 0x00, 0x01, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x1A, + 0x1B, 0x1C, 0x62, 0x73, + 0x74, 0x90, 0x91, 0x95, + 0x96 +}; + +static int fg_dump_debug(struct sm_fg_chip *sm) +{ + int i; + int ret; + u16 val = 0; + + for (i = 0; i < ARRAY_SIZE(fg_dump_regs); i++) { + ret = fg_read_word(sm, fg_dump_regs[i], &val); + if (!ret) + pr_info("Reg[0x%02X] = 0x%02X\n", + fg_dump_regs[i], val); + } + return 0; +} + + +static int reg_debugfs_open(struct inode *inode, struct file *file) +{ + struct sm_fg_chip *sm = inode->i_private; + + return single_open(file, show_registers, sm); +} + +static const struct file_operations reg_debugfs_ops = { + .owner = THIS_MODULE, + .open = reg_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void create_debugfs_entry(struct sm_fg_chip *sm) +{ + sm->debug_root = debugfs_create_dir("sm_fg", NULL); + if (!sm->debug_root) + pr_err("Failed to create debug dir\n"); + + if (sm->debug_root) { + + debugfs_create_file("registers", S_IFREG | S_IRUGO, + sm->debug_root, sm, ®_debugfs_ops); + + debugfs_create_x32("fake_soc", + S_IFREG | S_IWUSR | S_IRUGO, + sm->debug_root, + &(sm->fake_soc)); + + debugfs_create_x32("fake_temp", + S_IFREG | S_IWUSR | S_IRUGO, + sm->debug_root, + &(sm->fake_temp)); + + debugfs_create_x32("skip_reads", + S_IFREG | S_IWUSR | S_IRUGO, + sm->debug_root, + &(sm->skip_reads)); + debugfs_create_x32("skip_writes", + S_IFREG | S_IWUSR | S_IRUGO, + sm->debug_root, + &(sm->skip_writes)); + } +} + +static int show_registers(struct seq_file *m, void *data) +{ + struct sm_fg_chip *sm = m->private; + int i; + int ret; + u16 val = 0; + + for (i = 0; i < ARRAY_SIZE(fg_dump_regs); i++) { + ret = fg_read_word(sm, fg_dump_regs[i], &val); + if (!ret) + seq_printf(m, "Reg[0x%02X] = 0x%02X\n", + fg_dump_regs[i], val); + } + return 0; +} +#if 0 +static void fg_refresh_status(struct sm_fg_chip *sm) +{ + bool last_batt_inserted; + bool last_batt_fc; + bool last_batt_ot; + bool last_batt_ut; + + last_batt_inserted = sm->batt_present; + last_batt_fc = sm->batt_fc; + last_batt_ot = sm->batt_ot; + last_batt_ut = sm->batt_ut; + + fg_read_status(sm); + + pr_info("batt_present=%d", sm->batt_present); + + if (!last_batt_inserted && sm->batt_present) {/* battery inserted */ + pr_info("Battery inserted\n"); + } else if (last_batt_inserted && !sm->batt_present) {/* battery removed */ + pr_info("Battery removed\n"); + sm->batt_soc = -ENODATA; + sm->batt_fcc = -ENODATA; + sm->batt_volt = -ENODATA; + sm->batt_curr = -ENODATA; + sm->batt_temp = -ENODATA; + } + + if ((last_batt_inserted != sm->batt_present) + || (last_batt_fc != sm->batt_fc) + || (last_batt_ot != sm->batt_ot) + || (last_batt_ut != sm->batt_ut)) + power_supply_changed(sm->batt_psy); + + if (sm->batt_present) { + sm->batt_soc = fg_read_soc(sm); + sm->batt_ocv = fg_read_ocv(sm); + sm->batt_volt = fg_read_volt(sm); + sm->batt_curr = fg_read_current(sm); + sm->batt_soc_cycle = fg_get_cycle(sm); + if (sm->en_temp_in) + sm->batt_temp = fg_read_temperature(sm, TEMPERATURE_IN); + else if (sm->en_temp_ex) + sm->batt_temp = fg_read_temperature(sm, TEMPERATURE_EX); + else + sm->batt_temp = -ENODATA; + fg_cal_carc(sm); + + pr_info("RSOC:%d, Volt:%d, Current:%d, Temperature:%d\n", + sm->batt_soc, sm->batt_volt, sm->batt_curr, sm->batt_temp); + } + +// sm->last_update = jiffies; +} +#endif + +static int sm_update_data(struct sm_fg_chip *sm) +{ + int ret = 0; + bool last_batt_inserted; + bool last_batt_fc; + bool last_batt_ot; + bool last_batt_ut; + + last_batt_inserted = sm->batt_present; + last_batt_fc = sm->batt_fc; + last_batt_ot = sm->batt_ot; + last_batt_ut = sm->batt_ut; + + fg_read_status(sm); + + pr_info("batt_present=%d", sm->batt_present); + + if (!last_batt_inserted && sm->batt_present) {/* battery inserted */ + pr_info("Battery inserted\n"); + } else if (last_batt_inserted && !sm->batt_present) {/* battery removed */ + pr_info("Battery removed\n"); + sm->batt_soc = -ENODATA; + sm->batt_fcc = -ENODATA; + sm->batt_volt = -ENODATA; + sm->batt_curr = -ENODATA; + sm->batt_temp = -ENODATA; + } + + if ((last_batt_inserted != sm->batt_present) + || (last_batt_fc != sm->batt_fc) + || (last_batt_ot != sm->batt_ot) + || (last_batt_ut != sm->batt_ut)) { + if (sm->batt_psy) + power_supply_changed(sm->batt_psy); + } + + if (sm->batt_present) { + sm->batt_soc = fg_read_soc(sm); + sm->batt_ocv = fg_read_ocv(sm); + sm->batt_volt = fg_read_volt(sm); + sm->batt_curr = fg_read_current(sm); + sm->batt_soc_cycle = fg_get_cycle(sm); + if (sm->en_temp_in) + + sm->batt_temp = fg_read_temperature(sm, TEMPERATURE_IN); + + else if (sm->en_temp_ex) + + sm->batt_temp = fg_read_temperature(sm, TEMPERATURE_EX); + + else + sm->batt_temp = -ENODATA; + + fg_cal_carc(sm); + + pr_info("RSOC:%d, Volt:%d, Current:%d, Temperature:%d, OCV:%d\n", + sm->batt_soc, sm->batt_volt, sm->batt_curr, sm->batt_temp, sm->batt_ocv); + } + + return ret; +} + +//static unsigned int poll_interval = 60; +static void fg_monitor_workfunc(struct work_struct *work) +{ + struct delayed_work *delay_work; + struct sm_fg_chip *sm_bat; + int ret; + + delay_work = container_of(work, struct delayed_work, work); + sm_bat = container_of(delay_work, struct sm_fg_chip, monitor_work); + + /* get battery power supply */ + if (!sm_bat->batt_psy) { + sm_bat->batt_psy = power_supply_get_by_name("battery"); + if (!sm_bat->batt_psy) + printk("%s: get batt_psy fail\n", __func__); + } + + ret = sm_update_data(sm_bat); + if (ret < 0) + printk(KERN_ERR "iic read error when update data"); + + if (sm_bat->batt_psy) { + power_supply_changed(sm_bat->batt_psy); + } + + queue_delayed_work(sm_bat->smfg_workqueue, &sm_bat->monitor_work, msecs_to_jiffies(queue_delayed_work_time)); + +} + +#define COMMON_PARAM_MASK 0xFF00 +#define COMMON_PARAM_SHIFT 8 +#define BATTERY_PARAM_MASK 0x00FF +static bool fg_check_reg_init_need(struct i2c_client *client) +{ + struct sm_fg_chip *sm = i2c_get_clientdata(client); + int ret = 0; + u16 data = 0; + u16 param_ver = 0; + ret = fg_read_word(sm, sm->regs[SM_FG_REG_FG_OP_STATUS], &data); + if (ret < 0) { + pr_err("Failed to read param_ctrl unlock, ret = %d\n", ret); + return ret; + } else { + pr_info("FG_OP_STATUS = 0x%x\n", data); + + ret = fg_read_word(sm, FG_PARAM_VERION, ¶m_ver); + if (ret < 0) { + pr_err("Failed to read FG_PARAM_VERION, ret = %d\n", ret); + return ret; + } + + pr_info("param_ver = 0x%x, common_param_version = 0x%x, battery_param_version = 0x%x\n", param_ver, sm->common_param_version, sm->battery_param_version); + + if(((data & INIT_CHECK_MASK) == DISABLE_RE_INIT) + && (((param_ver & COMMON_PARAM_MASK) >> COMMON_PARAM_SHIFT) >= sm->common_param_version) + && ((param_ver & BATTERY_PARAM_MASK) >= sm->battery_param_version)) + { + pr_info("%s: SM_FG_REG_FG_OP_STATUS : 0x%x , return FALSE NO init need\n", __func__, data); + return 0; + } + else + { + pr_info("%s: SM_FG_REG_FG_OP_STATUS : 0x%x , return TRUE init need!!!!\n", __func__, data); + return 1; + } + } +} + +#define MINVAL(a, b) ((a <= b) ? a : b) +#define MAXVAL(a, b) ((a > b) ? a : b) +static int fg_calculate_iocv(struct sm_fg_chip *sm) +{ + bool only_lb=false, sign_i_offset=0; //valid_cb=false, + int roop_start=0, roop_max=0, i=0, cb_last_index = 0, cb_pre_last_index =0; + int lb_v_buffer[FG_INIT_B_LEN+1] = {0, 0, 0, 0, 0, 0, 0, 0}; + int lb_i_buffer[FG_INIT_B_LEN+1] = {0, 0, 0, 0, 0, 0, 0, 0}; + int cb_v_buffer[FG_INIT_B_LEN+1] = {0, 0, 0, 0, 0, 0, 0, 0}; + int cb_i_buffer[FG_INIT_B_LEN+1] = {0, 0, 0, 0, 0, 0, 0, 0}; + int i_offset_margin = 0x14, i_vset_margin = 0x67; + int v_max=0, v_min=0, v_sum=0, lb_v_avg=0, cb_v_avg=0, lb_v_set=0, lb_i_set=0, i_offset=0; + int i_max=0, i_min=0, i_sum=0, lb_i_avg=0, cb_i_avg=0, cb_v_set=0, cb_i_set=0; + int lb_i_p_v_min=0, lb_i_n_v_max=0, cb_i_p_v_min=0, cb_i_n_v_max=0; + + u16 v_ret, i_ret = 0; + int ret=0; + + u16 data = 0; + + ret = fg_read_word(sm, FG_REG_END_V_IDX, &data); + if (ret < 0) { + pr_err("Failed to read FG_REG_END_V_IDX, ret = %d\n", ret); + return ret; + } else { + pr_info("iocv_status_read = addr : 0x%x , data : 0x%x\n", FG_REG_END_V_IDX, data); + } + + if((data & 0x0010) == 0x0000) + { + only_lb = true; + } + + roop_max = (data & 0x000F); + if(roop_max > FG_INIT_B_LEN) + roop_max = FG_INIT_B_LEN; + + roop_start = FG_REG_START_LB_V; + for (i = roop_start; i < roop_start + roop_max; i++) + { + ret = fg_read_word(sm, i, &v_ret); + if (ret < 0) { + pr_err("Failed to read 0x%x, ret = %d\n",i, ret); + return ret; + } + ret = fg_read_word(sm, i+0x20, &i_ret); + if (ret < 0) { + pr_err("Failed to read 0x%x, ret = %d\n",i, ret); + return ret; + } + + if((i_ret&0x4000) == 0x4000) + { + i_ret = -(i_ret&0x3FFF); + } + + lb_v_buffer[i-roop_start] = v_ret; + lb_i_buffer[i-roop_start] = i_ret; + + if (i == roop_start) + { + v_max = v_ret; + v_min = v_ret; + v_sum = v_ret; + i_max = i_ret; + i_min = i_ret; + i_sum = i_ret; + } + else + { + if(v_ret > v_max) + v_max = v_ret; + else if(v_ret < v_min) + v_min = v_ret; + v_sum = v_sum + v_ret; + + if(i_ret > i_max) + i_max = i_ret; + else if(i_ret < i_min) + i_min = i_ret; + i_sum = i_sum + i_ret; + } + + if(abs(i_ret) > i_vset_margin) + { + if(i_ret > 0) + { + if(lb_i_p_v_min == 0) + { + lb_i_p_v_min = v_ret; + } + else + { + if(v_ret < lb_i_p_v_min) + lb_i_p_v_min = v_ret; + } + } + else + { + if(lb_i_n_v_max == 0) + { + lb_i_n_v_max = v_ret; + } + else + { + if(v_ret > lb_i_n_v_max) + lb_i_n_v_max = v_ret; + } + } + } + } + v_sum = v_sum - v_max - v_min; + i_sum = i_sum - i_max - i_min; + + lb_v_avg = v_sum / (roop_max-2); + lb_i_avg = i_sum / (roop_max-2); + + if(abs(lb_i_buffer[roop_max-1]) < i_vset_margin) + { + if(abs(lb_i_buffer[roop_max-2]) < i_vset_margin) + { + lb_v_set = MAXVAL(lb_v_buffer[roop_max-2], lb_v_buffer[roop_max-1]); + if(abs(lb_i_buffer[roop_max-3]) < i_vset_margin) + { + lb_v_set = MAXVAL(lb_v_buffer[roop_max-3], lb_v_set); + } + } + else + { + lb_v_set = lb_v_buffer[roop_max-1]; + } + } + else + { + lb_v_set = lb_v_avg; + } + + if(lb_i_n_v_max > 0) + { + lb_v_set = MAXVAL(lb_i_n_v_max, lb_v_set); + } + + if(roop_max > 3) + { + lb_i_set = (lb_i_buffer[2] + lb_i_buffer[3]) / 2; + } + + if((abs(lb_i_buffer[roop_max-1]) < i_offset_margin) && (abs(lb_i_set) < i_offset_margin)) + { + lb_i_set = MAXVAL(lb_i_buffer[roop_max-1], lb_i_set); + } + else if(abs(lb_i_buffer[roop_max-1]) < i_offset_margin) + { + lb_i_set = lb_i_buffer[roop_max-1]; + } + else if(abs(lb_i_set) < i_offset_margin) + { + //lb_i_set = lb_i_set; + } + else + { + lb_i_set = 0; + } + + i_offset = lb_i_set; + + i_offset = i_offset + 4; + + if(i_offset <= 0) + { + sign_i_offset = 1; +#ifdef IGNORE_N_I_OFFSET + i_offset = 0; +#else + i_offset = -i_offset; +#endif + } + + i_offset = i_offset>>1; + + if(sign_i_offset == 0) + { + i_offset = i_offset|0x0080; + } + i_offset = i_offset | i_offset<<8; + + pr_info("%s: iocv_l_max=0x%x, iocv_l_min=0x%x, iocv_l_avg=0x%x, lb_v_set=0x%x, roop_max=%d \n", + __func__, v_max, v_min, lb_v_avg, lb_v_set, roop_max); + pr_info("%s: ioci_l_max=0x%x, ioci_l_min=0x%x, ioci_l_avg=0x%x, lb_i_set=0x%x, i_offset=0x%x, sign_i_offset=%d\n", + __func__, i_max, i_min, lb_i_avg, lb_i_set, i_offset, sign_i_offset); + + if(!only_lb) + { + roop_start = FG_REG_START_CB_V; + roop_max = 6; + for (i = roop_start; i < roop_start + roop_max; i++) + { + ret = fg_read_word(sm, i, &v_ret); + if (ret < 0) { + pr_err("Failed to read 0x%x, ret = %d\n",i, ret); + return ret; + } + ret = fg_read_word(sm, i+0x20, &i_ret); + if (ret < 0) { + pr_err("Failed to read 0x%x, ret = %d\n",i, ret); + return ret; + } + + if((i_ret&0x4000) == 0x4000) + { + i_ret = -(i_ret&0x3FFF); + } + + cb_v_buffer[i-roop_start] = v_ret; + cb_i_buffer[i-roop_start] = i_ret; + + if (i == roop_start) + { + v_max = v_ret; + v_min = v_ret; + v_sum = v_ret; + i_max = i_ret; + i_min = i_ret; + i_sum = i_ret; + } + else + { + if(v_ret > v_max) + v_max = v_ret; + else if(v_ret < v_min) + v_min = v_ret; + v_sum = v_sum + v_ret; + + if(i_ret > i_max) + i_max = i_ret; + else if(i_ret < i_min) + i_min = i_ret; + i_sum = i_sum + i_ret; + } + + if(abs(i_ret) > i_vset_margin) + { + if(i_ret > 0) + { + if(cb_i_p_v_min == 0) + { + cb_i_p_v_min = v_ret; + } + else + { + if(v_ret < cb_i_p_v_min) + cb_i_p_v_min = v_ret; + } + } + else + { + if(cb_i_n_v_max == 0) + { + cb_i_n_v_max = v_ret; + } + else + { + if(v_ret > cb_i_n_v_max) + cb_i_n_v_max = v_ret; + } + } + } + } + v_sum = v_sum - v_max - v_min; + i_sum = i_sum - i_max - i_min; + + cb_v_avg = v_sum / (roop_max-2); + cb_i_avg = i_sum / (roop_max-2); + + cb_last_index = (data & 0x000F)-7; //-6-1 + if(cb_last_index < 0) + { + cb_last_index = 5; + } + + for (i = roop_max; i > 0; i--) + { + if(abs(cb_i_buffer[cb_last_index]) < i_vset_margin) + { + cb_v_set = cb_v_buffer[cb_last_index]; + if(abs(cb_i_buffer[cb_last_index]) < i_offset_margin) + { + cb_i_set = cb_i_buffer[cb_last_index]; + } + + cb_pre_last_index = cb_last_index - 1; + if(cb_pre_last_index < 0) + { + cb_pre_last_index = 5; + } + + if(abs(cb_i_buffer[cb_pre_last_index]) < i_vset_margin) + { + cb_v_set = MAXVAL(cb_v_buffer[cb_pre_last_index], cb_v_set); + if(abs(cb_i_buffer[cb_pre_last_index]) < i_offset_margin) + { + cb_i_set = MAXVAL(cb_i_buffer[cb_pre_last_index], cb_i_set); + } + } + } + else + { + cb_last_index--; + if(cb_last_index < 0) + { + cb_last_index = 5; + } + } + } + + if(cb_v_set == 0) + { + cb_v_set = cb_v_avg; + if(cb_i_set == 0) + { + cb_i_set = cb_i_avg; + } + } + + if(cb_i_n_v_max > 0) + { + cb_v_set = MAXVAL(cb_i_n_v_max, cb_v_set); + } + + if(abs(cb_i_set) < i_offset_margin) + { + if(cb_i_set > lb_i_set) + { + i_offset = cb_i_set; + i_offset = i_offset + 4; + + if(i_offset <= 0) + { + sign_i_offset = 1; +#ifdef IGNORE_N_I_OFFSET + i_offset = 0; +#else + i_offset = -i_offset; +#endif + } + + i_offset = i_offset>>1; + + if(sign_i_offset == 0) + { + i_offset = i_offset|0x0080; + } + i_offset = i_offset | i_offset<<8; + + } + } + + pr_info("%s: iocv_c_max=0x%x, iocv_c_min=0x%x, iocv_c_avg=0x%x, cb_v_set=0x%x, cb_last_index=%d\n", + __func__, v_max, v_min, cb_v_avg, cb_v_set, cb_last_index); + pr_info("%s: ioci_c_max=0x%x, ioci_c_min=0x%x, ioci_c_avg=0x%x, cb_i_set=0x%x, i_offset=0x%x, sign_i_offset=%d\n", + __func__, i_max, i_min, cb_i_avg, cb_i_set, i_offset, sign_i_offset); + + } + + if((abs(cb_i_set) > i_vset_margin) || only_lb) + { + ret = MAXVAL(lb_v_set, cb_i_n_v_max); + } + else + { + ret = cb_v_set; + } + + if(ret > sm->battery_table[BATTERY_TABLE0][FG_TABLE_LEN-1]) + { + pr_info("iocv ret change 0x%x -> 0x%x \n",ret, sm->battery_table[BATTERY_TABLE0][FG_TABLE_LEN-1]); + ret = sm->battery_table[BATTERY_TABLE0][FG_TABLE_LEN-1]; + } + else if(ret < sm->battery_table[BATTERY_TABLE0][0]) + { + pr_info("iocv ret change 0x%x -> 0x%x \n", ret, (sm->battery_table[BATTERY_TABLE0][0] + 0x10)); + ret = sm->battery_table[BATTERY_TABLE0][0] + 0x10; + } + + return ret; +} + +static bool fg_reg_init(struct i2c_client *client) +{ + struct sm_fg_chip *sm = i2c_get_clientdata(client); + int i, j, value, ret, cnt = 0; + uint8_t table_reg; + u16 data, data_int_mask = 0; + + pr_info("sm5602_fg_reg_init START!!\n"); + + /* Init mark */ + if (sm->fg_irq_set == -EINVAL) { + pr_err("sm->fg_irq_set is invalid"); + } else { + ret = fg_read_word(sm, sm->regs[SM_FG_REG_INT_MASK], &data_int_mask); + if (ret < 0){ + pr_err("Failed to read INT_MASK, ret = %d\n", ret); + return ret; + } + ret = fg_write_word(sm, sm->regs[SM_FG_REG_INT_MASK], 0x4000 | (data_int_mask | sm->fg_irq_set)); + if (ret < 0) { + pr_err("Failed to write 0x4000 | INIT_MASK, ret = %d\n", ret); + return ret; + } + ret = fg_write_word(sm, sm->regs[SM_FG_REG_INT_MASK], 0x07FF & (data_int_mask | sm->fg_irq_set)); + if (ret < 0) { + pr_err("Failed to write INIT_MASK, ret = %d\n", ret); + return ret; + } + } + + /* Low SOC1 */ + if (sm->low_soc1 == -EINVAL) { + pr_err("sm->low_soc1 is invalid"); + } else { + ret = fg_read_word(sm, sm->regs[SM_FG_REG_SOC_L_ALARM], &data); + if (ret < 0){ + pr_err("Failed to read SOC_L_ALARM (LOW_SOC1), ret = %d\n", ret); + return ret; + } + ret = fg_write_word(sm, sm->regs[SM_FG_REG_SOC_L_ALARM], ((data & 0xFFE0) | sm->low_soc1)); + if (ret < 0) { + pr_err("Failed to write SOC_L_ALARM (LOW_SOC1), ret = %d\n", ret); + return ret; + } + } + + /* Low SOC2 */ + if (sm->low_soc2 == -EINVAL) { + pr_err("sm->low_soc2 is invalid"); + } else { + ret = fg_read_word(sm, sm->regs[SM_FG_REG_SOC_L_ALARM], &data); + if (ret < 0){ + pr_err("Failed to read SOC_L_ALARM (LOW_SOC2), ret = %d\n", ret); + return ret; + } + ret = fg_write_word(sm, sm->regs[SM_FG_REG_SOC_L_ALARM],((data & 0xE0FF) | (sm->low_soc2 << 8))); + if (ret < 0) { + pr_err("Failed to write LOW_SOC2, ret = %d\n", ret); + return ret; + } + } + + /* V L ALARM */ + if (sm->v_l_alarm == -EINVAL) { + pr_err("sm->v_l_alarm is invalid"); + } else { + if (sm->v_l_alarm >= 2000 && sm->v_l_alarm < 3000) + data = (0xFEFF & (sm->v_l_alarm/10 * 256)); + else if (sm->v_l_alarm >= 3000 && sm->v_l_alarm < 4000) + data = (0x0100 | (sm->v_l_alarm/10 * 256)); + else { + ret = -EINVAL; + pr_err("Failed to calculate V_L_ALARM, ret = %d\n", ret); + return ret; + } + + ret = fg_write_word(sm, sm->regs[SM_FG_REG_V_L_ALARM], data); + if (ret < 0) { + pr_err("Failed to write V_L_ALARM, ret = %d\n", ret); + return ret; + } + } + + /* V H ALARM */ + if (sm->v_h_alarm == -EINVAL) { + pr_err("sm->v_h_alarm is invalid"); + } else { + if (sm->v_h_alarm >= 3000 && sm->v_h_alarm < 4000) + data = (0xFEFF & (sm->v_h_alarm/10 * 256)); + else if (sm->v_h_alarm >= 4000 && sm->v_h_alarm < 5000) + data = (0x0100 | (sm->v_h_alarm/10 * 256)); + else { + ret = -EINVAL; + pr_err("Failed to calculate V_H_ALARM, ret = %d\n", ret); + return ret; + } + + ret = fg_write_word(sm, sm->regs[SM_FG_REG_V_H_ALARM], data); + if (ret < 0) { + pr_err("Failed to write V_H_ALARM, ret = %d\n", ret); + return ret; + } + } + + /* T IN H/L ALARM */ + if (sm->t_h_alarm_in == -EINVAL + || sm->t_l_alarm_in == -EINVAL) { + pr_err("sm->t_h_alarm_in || sm->t_l_alarm_in is invalid"); + } else { + data = 0; //clear value + //T IN H ALARM + if (sm->t_h_alarm_in < 0) { + data |= 0x8000; + data |= ((((-1)*sm->t_h_alarm_in) & 0x7F) << 8); + } else { + data |= (((sm->t_h_alarm_in) & 0x7F) << 8); + } + //T IN L ALARM + if (sm->t_l_alarm_in < 0) { + data |= 0x0080; + data |= ((((-1)*sm->t_l_alarm_in) & 0x7F)); + } else { + data |= (((sm->t_l_alarm_in) & 0x7F)); + } + + ret = fg_write_word(sm, sm->regs[SM_FG_REG_T_IN_H_ALARM], data); + if (ret < 0) { + pr_err("Failed to write SM_FG_REG_T_IN_H_ALARM, ret = %d\n", ret); + return ret; + } + } + + do { + ret = fg_write_word(sm, sm->regs[SM_FG_REG_PARAM_CTRL], (FG_PARAM_UNLOCK_CODE | ((sm->battery_table_num & 0x0003) << 6) | (FG_TABLE_LEN-1))); + if (ret < 0) { + pr_err("Failed to write param_ctrl unlock, ret = %d\n", ret); + return ret; + } else { + pr_info("Param Unlock\n"); + } + //msleep(3); + msleep(60); + ret = fg_read_word(sm, sm->regs[SM_FG_REG_FG_OP_STATUS], &data); + if (ret < 0){ + pr_err("Failed to read FG_OP_STATUS, ret = %d\n", ret); + } else { + pr_info(" FG_OP_STATUS = 0x%x\n", data); + } + cnt++; + + } while(((data & 0x03)!=0x03) && cnt <= 3); + + /* VIT_PERIOD write */ + ret = fg_write_word(sm, sm->regs[SM_FG_REG_VIT_PERIOD], sm->vit_period); + if (ret < 0) { + pr_err("Failed to write VIT PERIOD, ret = %d\n", ret); + return ret; + } else { + pr_info("Write VIT_PERIOD = 0x%x : 0x%x\n", sm->regs[SM_FG_REG_VIT_PERIOD], sm->vit_period); + } + + /* Aging ctrl write */ + ret = fg_write_word(sm, FG_REG_AGING_CTRL, sm->aging_ctrl); + if (ret < 0) { + pr_err("Failed to write FG_REG_AGING_CTRL, ret = %d\n", ret); + return ret; + } else { + pr_info("Write FG_REG_AGING_CTRL = 0x%x : 0x%x\n", FG_REG_AGING_CTRL, sm->aging_ctrl); + } + + /* SOC Cycle ctrl write */ + ret = fg_write_word(sm, FG_REG_SOC_CYCLE_CFG, sm->cycle_cfg); + if (ret < 0) { + pr_err("Failed to write FG_REG_SOC_CYCLE_CFG, ret = %d\n", ret); + return ret; + } else { + pr_info("Write FG_REG_SOC_CYCLE_CFG = 0x%x : 0x%x\n", FG_REG_SOC_CYCLE_CFG, sm->cycle_cfg); + } + + /*RSNS write */ + ret = fg_write_word(sm, sm->regs[SM_FG_REG_RSNS_SEL], sm->batt_rsns); + if (ret < 0) { + pr_err("Failed to write SM_FG_REG_RSNS_SEL, ret = %d\n", ret); + return ret; + } else { + pr_info("Write SM_FG_REG_RSNS_SEL = 0x%x : 0x%x\n", sm->regs[SM_FG_REG_RSNS_SEL], sm->batt_rsns); + } + + /* Battery_Table write */ + for (i = BATTERY_TABLE0; i < BATTERY_TABLE2; i++) { + table_reg = 0xA0 + (i*FG_TABLE_LEN); + for (j = 0; j < FG_TABLE_LEN; j++) { + ret = fg_write_word(sm, (table_reg + j), sm->battery_table[i][j]); + if (ret < 0) { + pr_err("Failed to write Battery Table, ret = %d\n", ret); + return ret; + } else { + pr_info("TABLE write OK [%d][%d] = 0x%x : 0x%x\n", + i, j, (table_reg + j), sm->battery_table[i][j]); + } + } + } + + for(j=0; j < FG_ADD_TABLE_LEN; j++) + { + table_reg = 0xD0 + j; + ret = fg_write_word(sm, table_reg, sm->battery_table[i][j]); + if (ret < 0) { + pr_err("Failed to write Battery Table, ret = %d\n", ret); + return ret; + } else { + pr_info("TABLE write OK [%d][%d] = 0x%x : 0x%x\n", + i, j, table_reg, sm->battery_table[i][j]); + } + } + + /* RS write */ + ret = fg_write_word(sm, FG_REG_RS, sm->rs); + if (ret < 0) { + pr_err("Failed to write RS, ret = %d\n", ret); + return ret; + } else { + pr_info("RS = 0x%x : 0x%x\n",FG_REG_RS, sm->rs); + } + + /* alpha write */ + ret = fg_write_word(sm, FG_REG_ALPHA, sm->alpha); + if (ret < 0) { + pr_err("Failed to write FG_REG_ALPHA, ret = %d\n", ret); + return ret; + } else { + pr_info("ALPHA = 0x%x : 0x%x\n",FG_REG_ALPHA, sm->alpha); + } + + /* beta write */ + ret = fg_write_word(sm, FG_REG_BETA, sm->beta); + if (ret < 0) { + pr_err("Failed to write FG_REG_BETA, ret = %d\n", ret); + return ret; + } else { + pr_info("BETA = 0x%x : 0x%x\n",FG_REG_BETA, sm->beta); + } + + /* RS write */ + ret = fg_write_word(sm, FG_REG_RS_0, sm->rs_value[0]); + if (ret < 0) { + pr_err("Failed to write RS_0, ret = %d\n", ret); + return ret; + } else { + pr_info("RS = 0x%x : 0x%x\n",FG_REG_RS_0, sm->rs_value[0]); + } + + ret = fg_write_word(sm, FG_REG_RS_1, sm->rs_value[1]); + if (ret < 0) { + pr_err("Failed to write RS_1, ret = %d\n", ret); + return ret; + } else { + pr_info("RS_1 = 0x%x : 0x%x\n", FG_REG_RS_1, sm->rs_value[1]); + } + + ret = fg_write_word(sm, FG_REG_RS_2, sm->rs_value[2]); + if (ret < 0) { + pr_err("Failed to write RS_2, ret = %d\n", ret); + return ret; + } else { + pr_info("RS_2 = 0x%x : 0x%x\n", FG_REG_RS_2, sm->rs_value[2]); + } + + ret = fg_write_word(sm, FG_REG_RS_3, sm->rs_value[3]); + if (ret < 0) { + pr_err("Failed to write RS_3, ret = %d\n", ret); + return ret; + } else { + pr_info("RS_3 = 0x%x : 0x%x\n", FG_REG_RS_3, sm->rs_value[3]); + } + + ret = fg_write_word(sm, sm->regs[SM_FG_REG_CURRENT_RATE], sm->mix_value); + if (ret < 0) { + pr_err("Failed to write CURRENT_RATE, ret = %d\n", ret); + return ret; + } else { + pr_info("CURRENT_RATE = 0x%x : 0x%x\n", sm->regs[SM_FG_REG_CURRENT_RATE], sm->mix_value); + } + + pr_info("RS_0 = 0x%x, RS_1 = 0x%x, RS_2 = 0x%x, RS_3 = 0x%x, CURRENT_RATE = 0x%x\n", + sm->rs_value[0], sm->rs_value[1], sm->rs_value[2], sm->rs_value[3], sm->mix_value); + + /* VOLT_CAL write*/ + ret = fg_write_word(sm, FG_REG_VOLT_CAL, sm->volt_cal); + if (ret < 0) { + pr_err("Failed to write FG_REG_VOLT_CAL, ret = %d\n", ret); + return ret; + } else { + pr_info("FG_REG_VOLT_CAL = 0x%x : 0x%x\n", FG_REG_VOLT_CAL, sm->volt_cal); + } + + /* CAL write*/ + ret = fg_write_word(sm, FG_REG_CURR_IN_OFFSET, sm->curr_offset); + if (ret < 0) { + pr_err("Failed to write CURR_IN_OFFSET, ret = %d\n", ret); + return ret; + } else { + pr_info("CURR_IN_OFFSET = 0x%x : 0x%x\n", FG_REG_CURR_IN_OFFSET, sm->curr_offset); + } + ret = fg_write_word(sm, FG_REG_CURR_IN_SLOPE, sm->curr_slope); + if (ret < 0) { + pr_err("Failed to write CURR_IN_SLOPE, ret = %d\n", ret); + return ret; + } else { + pr_info("CURR_IN_SLOPE = 0x%x : 0x%x\n", FG_REG_CURR_IN_SLOPE, sm->curr_slope); + } + + /* BAT CAP write */ + ret = fg_write_word(sm, sm->regs[SM_FG_REG_BAT_CAP], sm->cap); + if (ret < 0) { + pr_err("Failed to write BAT_CAP, ret = %d\n", ret); + return ret; + } else { + pr_info("BAT_CAP = 0x%x : 0x%x\n", sm->regs[SM_FG_REG_BAT_CAP], sm->cap); + } + + /* MISC write */ + ret = fg_write_word(sm, sm->regs[SM_FG_REG_MISC], sm->misc); + if (ret < 0) { + pr_err("Failed to write REG_MISC, ret = %d\n", ret); + return ret; + } else { + pr_info("REG_MISC 0x%x : 0x%x\n", sm->regs[SM_FG_REG_MISC], sm->misc); + } + + /* TOPOFF SOC */ + ret = fg_write_word(sm, sm->regs[SM_FG_REG_TOPOFFSOC], sm->topoff_soc); + if (ret < 0) { + pr_err("Failed to write SM_FG_REG_TOPOFFSOC, ret = %d\n", ret); + return ret; + } else { + pr_info("SM_REG_TOPOFFSOC 0x%x : 0x%x\n", sm->regs[SM_FG_REG_TOPOFFSOC], sm->topoff_soc); + } + + /*INIT_last - control register set*/ + ret = fg_read_word(sm, sm->regs[SM_FG_REG_CNTL], &data); + if (ret < 0) { + pr_err("Failed to read CNTL, ret = %d\n", ret); + return ret; + } + + if (sm->en_temp_in) + data |= ENABLE_EN_TEMP_IN; + if (sm->en_temp_ex) + data |= ENABLE_EN_TEMP_EX; + if (sm->en_batt_det) + data |= ENABLE_EN_BATT_DET; + if (sm->iocv_man_mode) + data |= ENABLE_IOCV_MAN_MODE; + + ret = fg_write_word(sm, sm->regs[SM_FG_REG_CNTL], data); + if (ret < 0) { + pr_err("Failed to write CNTL, ret = %d\n", ret); + return ret; + } else { + pr_info("CNTL = 0x%x : 0x%x\n", sm->regs[SM_FG_REG_CNTL], data); + } + + /* Parameter Version [COMMON(0~255) | BATTERY(0~255)] */ + ret = fg_write_word(sm, FG_PARAM_VERION, ((sm->common_param_version << 8) | sm->battery_param_version)); + if (ret < 0) { + pr_err("Failed to write FG_PARAM_VERION, ret = %d\n", ret); + return ret; + } + + /* T EX L ALARM */ + if (sm->t_l_alarm_ex == -EINVAL) { + pr_err("sm->t_l_alarm_ex is invalid"); + } else { + data = (sm->t_l_alarm_ex) >> 1; //NTC Value/2 + + ret = fg_write_word(sm, FG_REG_SWADDR, 0x6A); + if (ret < 0) { + pr_err("Failed to write FG_REG_SWADDR, ret = %d\n", ret); + return ret; + } + ret = fg_write_word(sm, FG_REG_SWDATA, data); + if (ret < 0) { + pr_err("Failed to write FG_REG_SWADDR, ret = %d\n", ret); + return ret; + } + + pr_info("write to T_EX_H_ALARM = 0x%x\n", data); + } + + /* T EX H ALARM */ + if (sm->t_h_alarm_ex == -EINVAL) { + pr_err("sm->t_h_alarm_ex is invalid"); + } else { + data = (sm->t_h_alarm_ex) >> 1; //NTC Value/2 + + ret = fg_write_word(sm, FG_REG_SWADDR, 0x6B); + if (ret < 0) { + pr_err("Failed to write FG_REG_SWADDR, ret = %d\n", ret); + return ret; + } + ret = fg_write_word(sm, FG_REG_SWDATA, data); + if (ret < 0) { + pr_err("Failed to write FG_REG_SWADDR, ret = %d\n", ret); + return ret; + } + + pr_info("write to T_EX_L_ALARM = 0x%x\n", data); + } + + if (sm->iocv_man_mode) { + value = fg_calculate_iocv(sm); + + msleep(10); + ret = fg_write_word(sm, FG_REG_SWADDR, 0x75); + if (ret < 0) { + pr_err("Failed to write FG_REG_SWADDR, ret = %d\n", ret); + return ret; + } + ret = fg_write_word(sm, FG_REG_SWDATA, value); + if (ret < 0) { + pr_err("Failed to write FG_REG_SWADDR, ret = %d\n", ret); + return ret; + } + pr_info("IOCV_MAN : 0x%x\n", value); + } + + msleep(20); + + ret = fg_write_word(sm, sm->regs[SM_FG_REG_PARAM_CTRL], ((FG_PARAM_LOCK_CODE | (sm->battery_table_num & 0x0003) << 6) | (FG_TABLE_LEN-1))); + if (ret < 0) { + pr_err("Failed to write param_ctrl lock, ret = %d\n", ret); + return ret; + } else { + pr_info("Param Lock\n"); + } + + msleep(160); + + return 1; +} + +static unsigned int fg_get_device_id(struct i2c_client *client) +{ + struct sm_fg_chip *sm = i2c_get_clientdata(client); + int ret; + u16 data; + + ret = fg_read_word(sm, sm->regs[SM_FG_REG_DEVICE_ID], &data); + if (ret < 0) { + pr_err("Failed to read DEVICE_ID, ret = %d\n", ret); + return ret; + } + + pr_info("revision_id = 0x%x\n",(data & 0x000f)); + pr_info("device_id = 0x%x\n",(data & 0x00f0)>>4); + + return ret; +} +static bool fg_init(struct i2c_client *client) +{ + int ret; + struct sm_fg_chip *sm = i2c_get_clientdata(client); + + /*sm5602 i2c read check*/ + + if (fg_check_reg_init_need(client)) { + ret = fg_reset(sm); + if (ret < 0) { + pr_err("%s: fail to do reset(%d)\n", __func__, ret); + return false; + } + fg_reg_init(client); + } + + //sm->is_charging = (sm->batt_current > 9) ? true : false; + //pr_err("is_charging = %dd\n",sm->is_charging); + + return true; +} + +#define PROPERTY_NAME_SIZE 128 +static int fg_common_parse_dt(struct sm_fg_chip *sm) +{ + struct device *dev = &sm->client->dev; + struct device_node *np = dev->of_node; + int rc; + + BUG_ON(dev == 0); + BUG_ON(np == 0); + + /* + sm->gpio_int = of_get_named_gpio(np, "qcom,irq-gpio", 0); + pr_info("gpio_int=%d\n", sm->gpio_int); + + if (!gpio_is_valid(sm->gpio_int)) { + pr_info("gpio_int is not valid\n"); + sm->gpio_int = -EINVAL; + } + */ + rc = of_property_read_u32(np, "sm,fcc_design", + &sm->batt_fcc_design); + if (rc < 0) + sm->batt_fcc_design = 5000; + pr_info("sm,fcc_design = %d\n", sm->batt_fcc_design); + + /* EN TEMP EX/IN */ + if (of_property_read_bool(np, "sm,en_temp_ex")) + sm->en_temp_ex = true; + else + sm->en_temp_ex = 0; + pr_info("Temperature EX enabled = %d\n", sm->en_temp_ex); + + if (of_property_read_bool(np, "sm,en_temp_in")) + sm->en_temp_in = true; + else + sm->en_temp_in = 0; + pr_info("Temperature IN enabled = %d\n", sm->en_temp_in); + + /* EN BATT DET */ + if (of_property_read_bool(np, "sm,en_batt_det")) + sm->en_batt_det = true; + else + sm->en_batt_det = 0; + pr_info("Batt Det enabled = %d\n", sm->en_batt_det); + + /* MISC */ + rc = of_property_read_u32(np, "sm,misc", + &sm->misc); + if (rc < 0) + sm->misc = 0x0800; + + /* IOCV MAN MODE */ + if (of_property_read_bool(np, "sm,iocv_man_mode")) + sm->iocv_man_mode = true; + else + sm->iocv_man_mode = 0; + pr_info("IOCV_MAN_MODE = %d\n", sm->iocv_man_mode); + + /* Aging */ + rc = of_property_read_u32(np, "sm,aging_ctrl",&sm->aging_ctrl); + if (rc < 0) + sm->aging_ctrl = -EINVAL; + + /* SOC Cycle cfg */ + rc = of_property_read_u32(np, "sm,cycle_cfg", + &sm->cycle_cfg); + if (rc < 0) + sm->cycle_cfg = -EINVAL; + + /* RSNS */ + rc = of_property_read_u32(np, "sm,rsns", + &sm->batt_rsns); + if (rc < 0) + sm->batt_rsns = -EINVAL; + + rc = of_property_read_u32(np, "factory_mode_ntc_exist", + &sm->ntc_exist); + if (rc < 0) + sm->ntc_exist = true; + + /* IRQ Mask */ + rc = of_property_read_u32(np, "sm,fg_irq_set", + &sm->fg_irq_set); + if (rc < 0) + sm->fg_irq_set = -EINVAL; + + /* LOW SOC1/2 */ + rc = of_property_read_u32(np, "sm,low_soc1", + &sm->low_soc1); + if (rc < 0) + sm->low_soc1 = -EINVAL; + pr_info("low_soc1 = %d\n", sm->low_soc1); + + rc = of_property_read_u32(np, "sm,low_soc2", + &sm->low_soc2); + if (rc < 0) + sm->low_soc2 = -EINVAL; + pr_info("low_soc2 = %d\n", sm->low_soc2); + + /* V_L/H_ALARM */ + rc = of_property_read_u32(np, "sm,v_l_alarm", + &sm->v_l_alarm); + if (rc < 0) + sm->v_l_alarm = -EINVAL; + pr_info("v_l_alarm = %d\n", sm->v_l_alarm); + + rc = of_property_read_u32(np, "sm,v_h_alarm", + &sm->v_h_alarm); + if (rc < 0) + sm->v_h_alarm = -EINVAL; + pr_info("v_h_alarm = %d\n", sm->v_h_alarm); + + /* T_IN_H/L_ALARM */ + rc = of_property_read_u32(np, "sm,t_l_alarm_in", + &sm->t_l_alarm_in); + if (rc < 0) + sm->t_l_alarm_in = -EINVAL; + pr_info("t_l_alarm_in = %d\n", sm->t_l_alarm_in); + + rc = of_property_read_u32(np, "sm,t_h_alarm_in", + &sm->t_h_alarm_in); + if (rc < 0) + sm->t_h_alarm_in = -EINVAL; + pr_info("t_h_alarm_in = %d\n", sm->t_h_alarm_in); + + /* T_EX_H/L_ALARM */ + rc = of_property_read_u32(np, "sm,t_l_alarm_ex", + &sm->t_l_alarm_ex); + if (rc < 0) + sm->t_l_alarm_ex = -EINVAL; + pr_info("t_l_alarm_ex = %d\n", sm->t_l_alarm_ex); + + rc = of_property_read_u32(np, "sm,t_h_alarm_ex", + &sm->t_h_alarm_ex); + if (rc < 0) + sm->t_h_alarm_ex = -EINVAL; + pr_info("t_h_alarm_ex = %d\n", sm->t_h_alarm_ex); + + /* Battery Table Number */ + rc = of_property_read_u32(np, "sm,battery_table_num", + &sm->battery_table_num); + if (rc < 0) + sm->battery_table_num = -EINVAL; + + /* Paramater Number */ + rc = of_property_read_u32(np, "sm,param_version", + &sm->common_param_version); + if (rc < 0) + sm->common_param_version = -EINVAL; + pr_info("common_param_version = %d\n", sm->common_param_version); + + return 0; +} + +static const char *get_battery_serialnumber(void) +{ + struct device_node *np = of_find_node_by_path("/chosen"); + const char *battsn_buf; + int retval; + + battsn_buf = NULL; + + if (np) + retval = of_property_read_string(np, "mmi,battid", + &battsn_buf); + else + return NULL; + + if ((retval == -EINVAL) || !battsn_buf) { + pr_info(" Battsn unused\n"); + of_node_put(np); + return NULL; + + } else + pr_info("Battsn = %s\n", battsn_buf); + + of_node_put(np); + + return battsn_buf; +} + +static struct device_node *get_profile_by_serialnumber( + const struct device_node *np) +{ + struct device_node *node, *df_node, *sn_node; + const char *sn_buf, *df_sn, *dev_sn; + int rc; + + if (!np) + return NULL; + + dev_sn = NULL; + df_sn = NULL; + sn_buf = NULL; + df_node = NULL; + sn_node = NULL; + + dev_sn = get_battery_serialnumber(); + + rc = of_property_read_string(np, "df-serialnum", + &df_sn); + if (rc) + pr_info("No Default Serial Number defined\n"); + else if (df_sn) + pr_info("Default Serial Number %s\n", df_sn); + + for_each_child_of_node(np, node) { + rc = of_property_read_string(node, "serialnum", + &sn_buf); + if (!rc && sn_buf) { + if (dev_sn) + if (strnstr(dev_sn, sn_buf, 32)) + sn_node = node; + if (df_sn) + if (strnstr(df_sn, sn_buf, 32)) + df_node = node; + } + } + + if (sn_node) { + node = sn_node; + df_node = NULL; + pr_info("Battery Match Found using %s\n", sn_node->name); + } else if (df_node) { + node = df_node; + sn_node = NULL; + pr_info("Battery Match Found using default %s\n", + df_node->name); + } else { + pr_info("No Battery Match Found!\n"); + return NULL; + } + + return node; +} + +static int fg_battery_parse_dt(struct sm_fg_chip *sm) +{ + struct device *dev = &sm->client->dev; + struct device_node *np = dev->of_node; + struct device_node *batt_profile_node = NULL; + char prop_name[PROPERTY_NAME_SIZE]; + int battery_temp_table[FG_TEMP_TABLE_CNT_MAX]; + int table[FG_TABLE_LEN]; + int rs_value[4]; + int topoff_soc[3]; + int temp_offset[6]; + int temp_cal[10]; + int ext_temp_cal[10]; + int battery_type[3]; + int set_temp_poff[4]; + int ret; + int i, j; + + BUG_ON(dev == 0); + BUG_ON(np == 0); + batt_profile_node = get_profile_by_serialnumber(np); + if (!batt_profile_node) { + pr_err("sm5602 %s get battery parameter dts fail.\n", __func__); + return -1; + } + /* battery_table*/ + for (i = BATTERY_TABLE0; i < BATTERY_TABLE2; i++) { + snprintf(prop_name, PROPERTY_NAME_SIZE, + "battery,%s%d", "battery_table", i); + + ret = of_property_read_u32_array(batt_profile_node, prop_name, table, FG_TABLE_LEN); + if (ret < 0) + pr_info("Can get prop %s (%d)\n", prop_name, ret); + for (j = 0; j < FG_TABLE_LEN; j++) { + sm->battery_table[i][j] = table[j]; + pr_info("%s = \n", + prop_name, i, j, table[j]); + } + } + + i = BATTERY_TABLE2; + snprintf(prop_name, PROPERTY_NAME_SIZE, + "battery,%s%d", "battery_table", i); + ret = of_property_read_u32_array(batt_profile_node, prop_name, table, FG_ADD_TABLE_LEN); + if (ret < 0) + pr_info("Can get prop %s (%d)\n", prop_name, ret); + else { + for(j=0; j < FG_ADD_TABLE_LEN; j++) + { + sm->battery_table[i][j] = table[j]; + pr_info("%s = \n", + prop_name, i, j, table[j]); + } + } + + /* rs */ + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery,%s", "rs"); + ret = of_property_read_u32_array(batt_profile_node, prop_name, &sm->rs, 1); + if (ret < 0) + pr_err("Can get prop %s (%d)\n", prop_name, ret); + pr_info("%s = <0x%x>\n", prop_name, sm->rs); + + /* alpha */ + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery,%s", "alpha"); + ret = of_property_read_u32_array(batt_profile_node, prop_name, &sm->alpha, 1); + if (ret < 0) + pr_err("Can get prop %s (%d)\n", prop_name, ret); + pr_info("%s = <0x%x>\n", prop_name, sm->alpha); + + /* beta */ + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery,%s", "beta"); + ret = of_property_read_u32_array(batt_profile_node, prop_name, &sm->beta, 1); + if (ret < 0) + pr_err("Can get prop %s (%d)\n", prop_name, ret); + pr_info("%s = <0x%x>\n", prop_name, sm->beta); + + /* rs_value*/ + for (i = 0; i < 4; i++) { + snprintf(prop_name, + PROPERTY_NAME_SIZE, "battery,%s", "rs_value"); + ret = of_property_read_u32_array(batt_profile_node, prop_name, rs_value, 4); + if (ret < 0) + pr_err("Can get prop %s (%d)\n", prop_name, ret); + sm->rs_value[i] = rs_value[i]; + } + pr_info("%s = <0x%x 0x%x 0x%x 0x%x>\n", + prop_name, rs_value[0], rs_value[1], rs_value[2], rs_value[3]); + + /* vit_period*/ + snprintf(prop_name, + PROPERTY_NAME_SIZE, "battery,%s", "vit_period"); + ret = of_property_read_u32_array(batt_profile_node, + prop_name, &sm->vit_period, 1); + if (ret < 0) + pr_info("Can get prop %s (%d)\n", prop_name, ret); + pr_info("%s = <0x%x>\n", prop_name, sm->vit_period); + + /* battery_type*/ + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery,%s", "battery_type"); + ret = of_property_read_u32_array(batt_profile_node, prop_name, battery_type, 3); + if (ret < 0) + pr_err("Can get prop %s (%d)\n", prop_name, ret); + sm->batt_v_max = battery_type[0]; + sm->min_cap = battery_type[1]; + sm->cap = battery_type[2]; + + pr_info("%s = <%d %d %d>\n", prop_name, + sm->batt_v_max, sm->min_cap, sm->cap); + + /* tem poff level */ + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery,%s", "tem_poff"); + ret = of_property_read_u32_array(batt_profile_node, prop_name, set_temp_poff, 2); + if (ret < 0) + pr_err("Can get prop %s (%d)\n", prop_name, ret); + sm->n_tem_poff = set_temp_poff[0]; + sm->n_tem_poff_offset = set_temp_poff[1]; + + pr_info("%s = <%d, %d>\n", + prop_name, + sm->n_tem_poff, sm->n_tem_poff_offset); + + /* max-voltage -mv*/ + snprintf(prop_name, + PROPERTY_NAME_SIZE, "battery,%s", "max_voltage_uv"); + ret = of_property_read_u32(batt_profile_node, prop_name, + &sm->batt_max_voltage_uv); + if (ret < 0) + pr_err("couldn't find battery max voltage\n"); + + // TOPOFF SOC + snprintf(prop_name, + PROPERTY_NAME_SIZE, "battery,%s", "topoff_soc"); + ret = of_property_read_u32_array(batt_profile_node, prop_name, topoff_soc, 2); + if (ret < 0) + pr_err("Can get prop %s (%d)\n", prop_name, ret); + sm->topoff_soc = topoff_soc[0]; + sm->top_off = topoff_soc[1]; + + pr_info("%s = <%d %d>\n", prop_name, + sm->topoff_soc, sm->top_off); + + // Mix + snprintf(prop_name, + PROPERTY_NAME_SIZE, "battery,%s", "mix_value"); + ret = of_property_read_u32_array(batt_profile_node, prop_name, &sm->mix_value, 1); + if (ret < 0) + pr_err("Can get prop %s (%d)\n", prop_name, ret); + + pr_info("%s = <%d>\n", prop_name, + sm->mix_value); + + /* VOLT CAL */ + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery,%s", "volt_cal"); + ret = of_property_read_u32_array(batt_profile_node, prop_name, &sm->volt_cal, 1); + if (ret < 0) + pr_err("Can get prop %s (%d)\n", prop_name, ret); + pr_info("%s = <0x%x>\n", prop_name, sm->volt_cal); + + /* CURR OFFSET */ + snprintf(prop_name, + PROPERTY_NAME_SIZE, "battery,%s", "curr_offset"); + ret = of_property_read_u32_array(batt_profile_node, + prop_name, &sm->curr_offset, 1); + if (ret < 0) + pr_err("Can get prop %s (%d)\n", prop_name, ret); + pr_info("%s = <0x%x>\n", prop_name, sm->curr_offset); + + /* CURR SLOPE */ + snprintf(prop_name, + PROPERTY_NAME_SIZE, "battery,%s", "curr_slope"); + ret = of_property_read_u32_array(batt_profile_node, + prop_name, &sm->curr_slope, 1); + if (ret < 0) + pr_err("Can get prop %s (%d)\n", prop_name, ret); + pr_info("%s = <0x%x>\n", prop_name, sm->curr_slope); + + /* temp_std */ + snprintf(prop_name, + PROPERTY_NAME_SIZE, "battery,%s", "temp_std"); + ret = of_property_read_u32_array(batt_profile_node, prop_name, &sm->temp_std, 1); + if (ret < 0) + pr_err("Can get prop %s (%d)\n", prop_name, ret); + pr_info("%s = <%d>\n", prop_name, sm->temp_std); + + /* temp_offset */ + snprintf(prop_name, + PROPERTY_NAME_SIZE, "battery,%s", "temp_offset"); + ret = of_property_read_u32_array(batt_profile_node, prop_name, temp_offset, 6); + if (ret < 0) + pr_err("Can get prop %s (%d)\n", prop_name, ret); + sm->en_high_fg_temp_offset = temp_offset[0]; + sm->high_fg_temp_offset_denom = temp_offset[1]; + sm->high_fg_temp_offset_fact = temp_offset[2]; + sm->en_low_fg_temp_offset = temp_offset[3]; + sm->low_fg_temp_offset_denom = temp_offset[4]; + sm->low_fg_temp_offset_fact = temp_offset[5]; + pr_info("%s = <%d, %d, %d, %d, %d, %d>\n", prop_name, + sm->en_high_fg_temp_offset, + sm->high_fg_temp_offset_denom, sm->high_fg_temp_offset_fact, + sm->en_low_fg_temp_offset, + sm->low_fg_temp_offset_denom, sm->low_fg_temp_offset_fact); + + /* temp_calc */ + snprintf(prop_name, + PROPERTY_NAME_SIZE, "battery,%s", "temp_cal"); + ret = of_property_read_u32_array(batt_profile_node, prop_name, temp_cal, 10); + if (ret < 0) + pr_err("Can get prop %s (%d)\n", prop_name, ret); + sm->en_high_fg_temp_cal = temp_cal[0]; + sm->high_fg_temp_p_cal_denom = temp_cal[1]; + sm->high_fg_temp_p_cal_fact = temp_cal[2]; + sm->high_fg_temp_n_cal_denom = temp_cal[3]; + sm->high_fg_temp_n_cal_fact = temp_cal[4]; + sm->en_low_fg_temp_cal = temp_cal[5]; + sm->low_fg_temp_p_cal_denom = temp_cal[6]; + sm->low_fg_temp_p_cal_fact = temp_cal[7]; + sm->low_fg_temp_n_cal_denom = temp_cal[8]; + sm->low_fg_temp_n_cal_fact = temp_cal[9]; + pr_info("%s = <%d, %d, %d, %d, %d, %d, %d, %d, %d, %d>\n", prop_name, + sm->en_high_fg_temp_cal, + sm->high_fg_temp_p_cal_denom, sm->high_fg_temp_p_cal_fact, + sm->high_fg_temp_n_cal_denom, sm->high_fg_temp_n_cal_fact, + sm->en_low_fg_temp_cal, + sm->low_fg_temp_p_cal_denom, sm->low_fg_temp_p_cal_fact, + sm->low_fg_temp_n_cal_denom, sm->low_fg_temp_n_cal_fact); + + /* ext_temp_calc */ + snprintf(prop_name, + PROPERTY_NAME_SIZE, "battery,%s", "ext_temp_cal"); + ret = of_property_read_u32_array(batt_profile_node, prop_name, ext_temp_cal, 10); + if (ret < 0) + pr_err("Can get prop %s (%d)\n", prop_name, ret); + sm->en_high_temp_cal = ext_temp_cal[0]; + sm->high_temp_p_cal_denom = ext_temp_cal[1]; + sm->high_temp_p_cal_fact = ext_temp_cal[2]; + sm->high_temp_n_cal_denom = ext_temp_cal[3]; + sm->high_temp_n_cal_fact = ext_temp_cal[4]; + sm->en_low_temp_cal = ext_temp_cal[5]; + sm->low_temp_p_cal_denom = ext_temp_cal[6]; + sm->low_temp_p_cal_fact = ext_temp_cal[7]; + sm->low_temp_n_cal_denom = ext_temp_cal[8]; + sm->low_temp_n_cal_fact = ext_temp_cal[9]; + pr_info("%s = <%d, %d, %d, %d, %d, %d, %d, %d, %d, %d>\n", prop_name, + sm->en_high_temp_cal, + sm->high_temp_p_cal_denom, sm->high_temp_p_cal_fact, + sm->high_temp_n_cal_denom, sm->high_temp_n_cal_fact, + sm->en_low_temp_cal, + sm->low_temp_p_cal_denom, sm->low_temp_p_cal_fact, + sm->low_temp_n_cal_denom, sm->low_temp_n_cal_fact); + + /* get battery_temp_table*/ + snprintf(prop_name, PROPERTY_NAME_SIZE, + "battery,%s", "thermal_table"); + + ret = of_property_read_u32_array(batt_profile_node, prop_name, battery_temp_table, FG_TEMP_TABLE_CNT_MAX); + if (ret < 0) + pr_err("Can get prop %s (%d)\n", prop_name, ret); + for (i = 0; i < FG_TEMP_TABLE_CNT_MAX; i++) { + sm->battery_temp_table[i] = battery_temp_table[i]; + pr_err("%s = \n", + prop_name, i, battery_temp_table[i]); + } + + /* Battery Paramter */ + snprintf(prop_name, PROPERTY_NAME_SIZE, "battery,%s", "param_version"); + ret = of_property_read_u32_array(batt_profile_node, prop_name, &sm->battery_param_version, 1); + if (ret < 0) + pr_err("Can get prop %s (%d)\n", prop_name, ret); + pr_info("%s = <0x%x>\n", prop_name, sm->battery_param_version); + + return 0; +} + +bool hal_fg_init(struct i2c_client *client) +{ + struct sm_fg_chip *sm = i2c_get_clientdata(client); + int ret = 0; + + pr_info("sm5602 hal_fg_init...\n"); + + ret = fg_get_device_id(client); + if (ret < 0) { + pr_err("%s: fail to do i2c read(%d)\n", __func__, ret); + return false; + } + + mutex_lock(&sm->data_lock); + if (client->dev.of_node) { + /* Load common data from DTS*/ + fg_common_parse_dt(sm); + /* Load battery data from DTS*/ + ret = fg_battery_parse_dt(sm); + if (ret < 0) { + pr_err("%s: Load battery data fail,(%d)\n", __func__, ret); + return false; + } + } + + if(!fg_init(client)) + return false; + //sm->batt_temp = 250; + + mutex_unlock(&sm->data_lock); + pr_info("hal fg init OK\n"); + return true; +} + +static int sm_fg_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + + int ret = 0; + struct sm_fg_chip *sm; + u8 *regs; + struct power_supply_desc *psy_desc; + struct power_supply_config psy_cfg = {0}; + + pr_info("enter\n"); + sm = devm_kzalloc(&client->dev, sizeof(*sm), GFP_KERNEL); + + if (!sm) + return -ENOMEM; + + sm->dev = &client->dev; + sm->client = client; + sm->chip = id->driver_data; + + sm->batt_soc = -ENODATA; + sm->batt_fcc = -ENODATA; + //sm->batt_dc = -ENODATA; + sm->batt_volt = -ENODATA; + sm->batt_temp = -ENODATA; + sm->batt_curr = -ENODATA; + sm->fake_soc = -EINVAL; + sm->fake_temp = -EINVAL; + + if (sm->chip == SM5602) { + regs = sm5602_regs; + } else { + pr_err("unexpected fuel gauge: %d\n", sm->chip); + regs = sm5602_regs; + } + + memcpy(sm->regs, regs, NUM_REGS); + + i2c_set_clientdata(client, sm); + + mutex_init(&sm->i2c_rw_lock); + mutex_init(&sm->data_lock); + + if (!hal_fg_init(client)) { + pr_err("Failed to Initialize Fuelgauge\n"); + ret = -EIO; + goto err_0; + } + + psy_desc = devm_kzalloc(&client->dev, sizeof(*psy_desc), GFP_KERNEL); + if (!psy_desc) + return -ENOMEM; + + psy_cfg.drv_data = sm; + psy_desc->name = "bms"; + psy_desc->type = POWER_SUPPLY_TYPE_MAINS; + psy_desc->properties = fg_props; + psy_desc->num_properties = ARRAY_SIZE(fg_props); + psy_desc->get_property = fg_get_property; + psy_desc->set_property = fg_set_property; + sm->bms_psy = power_supply_register(&client->dev, psy_desc, &psy_cfg); + if (IS_ERR(sm->bms_psy)) { + ret = PTR_ERR(sm->bms_psy); + printk(KERN_ERR"failed to register battery: %d\n", ret); + return ret; + } + + sm->smfg_workqueue = create_singlethread_workqueue("smfg_gauge"); + INIT_DELAYED_WORK(&sm->monitor_work, fg_monitor_workfunc); + queue_delayed_work(sm->smfg_workqueue, &sm->monitor_work, msecs_to_jiffies(queue_start_work_time)); + + /* + if (sm->gpio_int != -EINVAL) + client->irq = sm->gpio_int; + else { + pr_err("Failed to registe gpio interrupt\n"); + goto err_0; + } + + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, + fg_irq_thread, + IRQF_TRIGGER_LOW | IRQF_ONESHOT | IRQF_NO_SUSPEND, + "sm fuel gauge irq", sm); + if (ret < 0) { + pr_err("request irq for irq=%d failed, ret = %d\n", client->irq, ret); + goto err_0; + } + } + */ + //fg_irq_thread(client->irq, sm); // if IRQF_TRIGGER_FALLING or IRQF_TRIGGER_RISING is needed, enable initial irq. + + create_debugfs_entry(sm); + + fg_dump_debug(sm); + + if(is_factory_mode()) + sm->factory_mode = true; + + //schedule_delayed_work(&sm->monitor_work, 10 * HZ); + pr_info("sm fuel gauge probe successfully, %s\n",device2str[sm->chip]); + + return 0; + +err_0: + return ret; +} + + +static int sm_fg_remove(struct i2c_client *client) +{ + struct sm_fg_chip *sm = i2c_get_clientdata(client); + + cancel_delayed_work_sync(&sm->monitor_work); + + mutex_destroy(&sm->data_lock); + mutex_destroy(&sm->i2c_rw_lock); + + debugfs_remove_recursive(sm->debug_root); + + return 0; + +} + +#ifdef CONFIG_PM +static int sm_bat_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sm_fg_chip *sm_bat = i2c_get_clientdata(client); + + cancel_delayed_work(&sm_bat->monitor_work); + return 0; +} + +static int sm_bat_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sm_fg_chip *sm_bat = i2c_get_clientdata(client); + + queue_delayed_work(sm_bat->smfg_workqueue, &sm_bat->monitor_work, msecs_to_jiffies(20)); + return 0; +} + +static const struct dev_pm_ops sm_bat_pm_ops = { + .suspend = sm_bat_suspend, + .resume = sm_bat_resume, +}; +#endif + +static void sm_fg_shutdown(struct i2c_client *client) +{ + pr_info("sm fuel gauge driver shutdown!\n"); +} + +static const struct of_device_id sm_fg_match_table[] = { + {.compatible = "sm,sm5602",}, + {}, +}; +MODULE_DEVICE_TABLE(of, sm_fg_match_table); + +static const struct i2c_device_id sm_fg_id[] = { + { "sm5602", SM5602 }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, sm_fg_id); + +static struct i2c_driver sm_fg_driver = { + .driver = { + .name = "sm5602", +#ifdef CONFIG_PM + .pm = &sm_bat_pm_ops, +#endif + .owner = THIS_MODULE, + .of_match_table = sm_fg_match_table, + }, + .id_table = sm_fg_id, + .probe = sm_fg_probe, + .remove = sm_fg_remove, + .shutdown = sm_fg_shutdown, +}; + + +module_i2c_driver(sm_fg_driver); + +MODULE_DESCRIPTION("SM SM5602 Gauge Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Siliconmitus"); + diff --git a/drivers/power/sm5602_fg_mmi/sm5602_fg.h b/drivers/power/sm5602_fg_mmi/sm5602_fg.h new file mode 100644 index 000000000000..27820837f543 --- /dev/null +++ b/drivers/power/sm5602_fg_mmi/sm5602_fg.h @@ -0,0 +1,64 @@ +/* + * drivers/battery/sm5602_fg.h + * + * Copyright (C) 2018 SiliconMitus + * + * 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; either version 2 + * of the License, or (at your option) any later version. + */ + +#ifndef SM5602_FG_H +#define SM5602_FG_H + +#define FG_INIT_MARK 0xA000 + +#define FG_PARAM_UNLOCK_CODE 0x3700 +#define FG_PARAM_LOCK_CODE 0x0000 +#define FG_TABLE_LEN 0x18//real table length -1 +#define FG_ADD_TABLE_LEN 0x8//real table length -1 +#define FG_INIT_B_LEN 0x7//real table length -1 + +#define ENABLE_EN_TEMP_IN 0x0200 +#define ENABLE_EN_TEMP_EX 0x0400 +#define ENABLE_EN_BATT_DET 0x0800 +#define ENABLE_IOCV_MAN_MODE 0x1000 +#define ENABLE_FORCED_SLEEP 0x2000 +#define ENABLE_SLEEPMODE_EN 0x4000 +#define ENABLE_SHUTDOWN 0x8000 + +/* REG */ +#define FG_REG_SOC_CYCLE 0x0B +#define FG_REG_SOC_CYCLE_CFG 0x15 +#define FG_REG_ALPHA 0x20 +#define FG_REG_BETA 0x21 +#define FG_REG_RS 0x24 +#define FG_REG_RS_1 0x25 +#define FG_REG_RS_2 0x26 +#define FG_REG_RS_3 0x27 +#define FG_REG_RS_0 0x29 +#define FG_REG_END_V_IDX 0x2F +#define FG_REG_START_LB_V 0x30 +#define FG_REG_START_CB_V 0x38 +#define FG_REG_START_LB_I 0x40 +#define FG_REG_START_CB_I 0x48 +#define FG_REG_VOLT_CAL 0x70 +#define FG_REG_CURR_IN_OFFSET 0x75 +#define FG_REG_CURR_IN_SLOPE 0x76 + +#define FG_REG_SRADDR 0x8C +#define FG_REG_SRDATA 0x8D +#define FG_REG_SWADDR 0x8E +#define FG_REG_SWDATA 0x8F + +#define FG_REG_AGING_CTRL 0x9C + +#define FG_TEMP_TABLE_CNT_MAX 0x65 + +#define FG_PARAM_VERION 0x1E + +#define INIT_CHECK_MASK 0x0010 +#define DISABLE_RE_INIT 0x0010 + +#endif /* SM5602_FG_H */ diff --git a/drivers/power/smart_pen_charger/Android.mk b/drivers/power/smart_pen_charger/Android.mk new file mode 100755 index 000000000000..20421ad4a8d4 --- /dev/null +++ b/drivers/power/smart_pen_charger/Android.mk @@ -0,0 +1,10 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := smart_pen_charger.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/qti_glink_charger.ko +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/smart_pen_charger/Kbuild b/drivers/power/smart_pen_charger/Kbuild new file mode 100755 index 000000000000..7b5f19fd0f1b --- /dev/null +++ b/drivers/power/smart_pen_charger/Kbuild @@ -0,0 +1,8 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/power/qti_glink_charger + +obj-m += smart_pen_charger.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../qti_glink_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/smart_pen_charger/Makefile b/drivers/power/smart_pen_charger/Makefile new file mode 100755 index 000000000000..0fc056a6f7da --- /dev/null +++ b/drivers/power/smart_pen_charger/Makefile @@ -0,0 +1,16 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../qti_glink_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/power/smart_pen_charger/smart_pen_charger.c b/drivers/power/smart_pen_charger/smart_pen_charger.c new file mode 100644 index 000000000000..60974e36b066 --- /dev/null +++ b/drivers/power/smart_pen_charger/smart_pen_charger.c @@ -0,0 +1,751 @@ +/* + * Copyright (C) 2021 Motorola Mobility LLC + * + * 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. + */ + +#define pr_fmt(fmt) "SMART_PEN_CHG: %s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qti_glink_charger.h" + +#define MAC_ADDR_LEN 6 +#define MAX_PSY_NAME_LEN 16 +#define CHG_SHOW_MAX_SIZE 50 + +static bool simulator_enabled; +module_param(simulator_enabled, bool, 0600); +MODULE_PARM_DESC(simulator_enabled, "Enable Pen Simulator"); + +enum pen_notify_event { + NOTIFY_EVENT_PEN_ID, + NOTIFY_EVENT_PEN_STATUS, + NOTIFY_EVENT_PEN_SOC, + NOTIFY_EVENT_PEN_MAC, + NOTIFY_EVENT_PEN_ERROR, +}; + +enum pen_status { + PEN_STAT_DETACHED, + PEN_STAT_ATTACHED, + PEN_STAT_READY, + PEN_STAT_CHARGING, + PEN_STAT_DISCHARGING, + PEN_STAT_CHARGE_FULL, + PEN_STAT_CHARGE_DISABLED, + PEN_STAT_MAX, +}; + +enum pen_error { + PEN_OK, + PEN_ERR_UNKNOWN, + PEN_ERR_NOT_PLUGIN, + PEN_ERR_TRANSTER_FAILED, + PEN_ERR_OVERTEMP, + PEN_ERR_OVERVOLT, + PEN_ERR_OVERCURR, + PEN_ERR_UNDERVOLT, + PEN_ERR_MAX, +}; + +static char *pen_status_maps[] = { + "detached", + "attached", + "ready", + "charging", + "discharging", + "charge_full", + "charge_disabled", +}; + +struct pen_mac { + u32 addr[MAC_ADDR_LEN]; +}; + +struct pen_charger_data { + u32 id; + u32 soc; + int32_t error; + enum pen_status status; + struct pen_mac mac; +}; + +struct pen_charger { + struct device *dev; + struct pen_charger_data pen_data; + struct pen_charger_data simulator_data; + struct power_supply *pen_psy; + char *pen_uenvp[2]; + char pen_psy_name[MAX_PSY_NAME_LEN]; + struct notifier_block pen_nb; + struct notifier_block pen_psy_nb; +}; + +static struct pen_charger *this_chip = NULL; +static void pen_charger_handle_event(struct pen_charger *chg, int event); + +static ssize_t pen_control_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + u32 cmd; + unsigned long r; + struct pen_charger *chg = this_chip; + + if (!chg) { + pr_err("PEN: chip not valid\n"); + return -ENODEV; + } + + r = kstrtouint(buf, 0, &cmd); + if (r) { + pr_err("Invalid pen control cmd = 0x%x\n", cmd); + return -EINVAL; + } + + pr_info("Send pen cmd = 0x%x\n", cmd); + rc = qti_charger_set_property(OEM_PROP_PEN_CTRL, + &cmd, sizeof(cmd)); + if (rc) { + pr_err("Failed to set control cmd=0x%x, rc=%d\n", cmd, rc); + return rc; + } + + return count; +} +static DEVICE_ATTR_WO(pen_control); + +static ssize_t pen_id_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned int id; + struct pen_charger *chg = this_chip; + + if (!chg) { + pr_err("PEN: chip not valid\n"); + return -ENODEV; + } + + r = kstrtouint(buf, 0, &id); + if (r) { + pr_err("Invalid pen id = 0x%x\n", id); + return -EINVAL; + } + + if (chg->simulator_data.id != id && id != 0) { + pr_info("Pen id changed by user 0x%x -> 0x%x\n", + chg->simulator_data.id, id); + chg->simulator_data.id = id; + if (simulator_enabled) + pen_charger_handle_event(chg, NOTIFY_EVENT_PEN_ID); + } + + return count; +} + +static ssize_t pen_id_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int rc; + u32 id; + struct pen_charger *chg = this_chip; + + if (!chg) { + pr_err("PEN: chip not valid\n"); + return -ENODEV; + } + + if (simulator_enabled) + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "0x%x\n", + chg->simulator_data.id); + + rc = qti_charger_get_property(OEM_PROP_PEN_ID, + &id, sizeof(id)); + if (rc) { + pr_err("Failed to read pen id, rc=%d\n", rc); + return rc; + } + + if (chg->pen_data.id != id) { + pr_info("Pen id updated 0x%x -> 0x%x\n", chg->pen_data.id, id); + chg->pen_data.id = id; + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "0x%x\n", chg->pen_data.id); +} +static DEVICE_ATTR(pen_id, 0664, pen_id_show, pen_id_store); + +static ssize_t pen_soc_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + unsigned long r; + unsigned int soc; + struct pen_charger *chg = this_chip; + + if (!chg) { + pr_err("PEN: chip not valid\n"); + return -ENODEV; + } + + r = kstrtouint(buf, 0, &soc); + if (r) { + pr_err("Invalid pen soc = %d\n", soc); + return -EINVAL; + } + + if (soc >= 0 && soc <= 100 && chg->pen_data.soc != soc) { + pr_info("Pen soc changed by user %d -> %d\n", chg->pen_data.soc, soc); + chg->pen_data.soc = soc; + rc = qti_charger_set_property(OEM_PROP_PEN_SOC, + &soc, sizeof(soc)); + if (rc) { + pr_err("Failed to Update pen soc, rc=%d\n", rc); + return rc; + } + } + + if (soc > 0 && soc <= 100 && chg->simulator_data.soc != soc) { + chg->simulator_data.soc = soc; + if (simulator_enabled) + pen_charger_handle_event(chg, NOTIFY_EVENT_PEN_SOC); + } + + return count; +} + +static ssize_t pen_soc_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int rc; + u32 soc; + struct pen_charger *chg = this_chip; + + if (!chg) { + pr_err("PEN: chip not valid\n"); + return -ENODEV; + } + + if (simulator_enabled) + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", + chg->simulator_data.soc); + + rc = qti_charger_get_property(OEM_PROP_PEN_SOC, + &soc, sizeof(soc)); + if (rc) { + pr_err("Failed to read pen soc, rc=%d\n", rc); + return rc; + } + + if (soc >= 0 && soc <= 100 && chg->pen_data.soc != soc) { + pr_info("Pen soc updated %d -> %d\n", chg->pen_data.soc, soc); + chg->pen_data.soc = soc; + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", chg->pen_data.soc); +} +static DEVICE_ATTR(pen_soc, 0664, pen_soc_show, pen_soc_store); + +static ssize_t pen_status_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int i; + u32 status = PEN_STAT_MAX; + struct pen_charger *chg = this_chip; + + if (!chg) { + pr_err("PEN: chip not valid\n"); + return -ENODEV; + } + + for (i = 0; i < PEN_STAT_MAX; i++) { + if (strstr(buf, pen_status_maps[i]) == buf) { + status = i; + break; + } + } + + if (status >= PEN_STAT_MAX) { + pr_err("Invalid pen status = %d\n", status); + return -EINVAL; + } + + if (chg->simulator_data.status != status) { + pr_info("Pen status changed by user %s -> %s\n", + pen_status_maps[chg->simulator_data.status], + pen_status_maps[status]); + chg->simulator_data.status = status; + if (simulator_enabled) + pen_charger_handle_event(chg, NOTIFY_EVENT_PEN_STATUS); + } + + return count; +} + +static ssize_t pen_status_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct pen_charger *chg = this_chip; + + if (!chg) { + pr_err("PEN: chip not valid\n"); + return -ENODEV; + } + + if (simulator_enabled) + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%s\n", + pen_status_maps[chg->simulator_data.status]); + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", + chg->pen_data.status); +} +static DEVICE_ATTR(pen_status, 0664, pen_status_show, pen_status_store); + +static ssize_t pen_mac_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc; + struct pen_mac mac; + struct pen_charger *chg = this_chip; + + if (!chg) { + pr_err("PEN: chip not valid\n"); + return -ENODEV; + } + + rc = sscanf(buf, "%2x:%2x:%2x:%2x:%2x:%2x", + &mac.addr[0], &mac.addr[1], &mac.addr[2], + &mac.addr[3], &mac.addr[4], &mac.addr[5]); + if (rc != MAC_ADDR_LEN) { + pr_err("Invalid pen mac, rc = %d\n", rc); + return -EINVAL; + } + + if (memcmp(&chg->simulator_data.mac, &mac, sizeof(mac))) { + pr_info("Pen mac changed by user, mac=%2x:%2x:%2x:%2x:%2x:%2x\n", + mac.addr[0], mac.addr[1], mac.addr[2], + mac.addr[3], mac.addr[4], mac.addr[5]); + memcpy(&chg->simulator_data.mac, &mac, sizeof(mac)); + if (simulator_enabled) + pen_charger_handle_event(chg, NOTIFY_EVENT_PEN_MAC); + } + + return count; +} + +static ssize_t pen_mac_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int i; + int rc; + struct pen_mac mac; + struct pen_charger *chg = this_chip; + + if (!chg) { + pr_err("PEN: chip not valid\n"); + return -ENODEV; + } + + if (simulator_enabled) { + memcpy(&mac, &chg->simulator_data.mac, sizeof(mac)); + return scnprintf(buf, CHG_SHOW_MAX_SIZE, + "%2x:%2x:%2x:%2x:%2x:%2x\n", + mac.addr[0], mac.addr[1], mac.addr[2], + mac.addr[3], mac.addr[4], mac.addr[5]); + } + + rc = qti_charger_get_property(OEM_PROP_PEN_MAC, + &mac, sizeof(mac)); + if (rc) { + pr_err("Failed to read pen mac, rc=%d\n", rc); + return rc; + } + + for (i = 0; i < MAC_ADDR_LEN; i++) + mac.addr[i] &= 0xFF; + + if (memcmp(&chg->pen_data.mac, &mac, sizeof(mac))) { + pr_info("Pen mac updated, mac=%2x:%2x:%2x:%2x:%2x:%2x\n", + mac.addr[0], mac.addr[1], mac.addr[2], + mac.addr[3], mac.addr[4], mac.addr[5]); + memcpy(&chg->pen_data.mac, &mac, sizeof(mac)); + } + + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%2x:%2x:%2x:%2x:%2x:%2x\n", + mac.addr[0], mac.addr[1], mac.addr[2], + mac.addr[3], mac.addr[4], mac.addr[5]); +} +static DEVICE_ATTR(pen_mac, 0664, pen_mac_show, pen_mac_store); + +static ssize_t pen_error_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long r; + unsigned int error; + struct pen_charger *chg = this_chip; + + if (!chg) { + pr_err("PEN: chip not valid\n"); + return -ENODEV; + } + + r = kstrtouint(buf, 0, &error); + if (r) { + pr_err("Invalid pen error = %d\n", error); + return -EINVAL; + } + + if (chg->simulator_data.error != error) { + pr_info("Pen error changed by user %d -> %d\n", + chg->simulator_data.error, error); + chg->simulator_data.error = error; + if (simulator_enabled) + pen_charger_handle_event(chg, NOTIFY_EVENT_PEN_ERROR); + } + + return count; +} + +static ssize_t pen_error_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct pen_charger *chg = this_chip; + + if (!chg) { + pr_err("PEN: chip not valid\n"); + return -ENODEV; + } + + if (simulator_enabled) + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", + chg->simulator_data.error); + else + return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", + chg->pen_data.error); +} +static DEVICE_ATTR(pen_error, 0664, pen_error_show, pen_error_store); + +static void pen_charger_psy_init(struct pen_charger *chg) +{ + int rc; + + if (chg->pen_psy) + return; + + chg->pen_psy = power_supply_get_by_name(chg->pen_psy_name); + if (!chg->pen_psy) { + pr_err("No pen power supply found\n"); + return; + } + pr_info("Pen power supply %s is found\n", chg->pen_psy_name); + + rc = device_create_file(chg->pen_psy->dev.parent, + &dev_attr_pen_control); + if (rc) + pr_err("couldn't create pen control\n"); + rc = device_create_file(chg->pen_psy->dev.parent, + &dev_attr_pen_id); + if (rc) + pr_err("couldn't create pen id\n"); + rc = device_create_file(chg->pen_psy->dev.parent, + &dev_attr_pen_soc); + if (rc) + pr_err("couldn't create pen soc\n"); + rc = device_create_file(chg->pen_psy->dev.parent, + &dev_attr_pen_status); + if (rc) + pr_err("couldn't create pen status\n"); + rc = device_create_file(chg->pen_psy->dev.parent, + &dev_attr_pen_mac); + if (rc) + pr_err("couldn't create pen mac\n"); + rc = device_create_file(chg->pen_psy->dev.parent, + &dev_attr_pen_error); + if (rc) + pr_err("couldn't create pen error\n"); +} + +static void pen_charger_handle_event(struct pen_charger *chg, int event) +{ + char *event_string = NULL; + struct pen_charger_data *data = NULL; + + event_string = kmalloc(CHG_SHOW_MAX_SIZE, GFP_KERNEL); + if (!event_string) + return; + + if (!simulator_enabled) + data = &chg->pen_data; + else + data = &chg->simulator_data; + + switch (event) { + case NOTIFY_EVENT_PEN_ID: + scnprintf(event_string, CHG_SHOW_MAX_SIZE, + "POWER_SUPPLY_PEN_ID=0x%x", + data->id); + break; + case NOTIFY_EVENT_PEN_STATUS: + if (data->status == PEN_STAT_DETACHED) { + pr_warn("Pen has been deteched, clean pen data!\n"); + memset(data, 0, sizeof(struct pen_charger_data)); + } + scnprintf(event_string, CHG_SHOW_MAX_SIZE, + "POWER_SUPPLY_PEN_STATUS=%s", + pen_status_maps[data->status]); + chg->pen_data.status = data->status; + if (chg->pen_psy) + sysfs_notify(&chg->pen_psy->dev.parent->kobj, NULL, "pen_status"); + break; + case NOTIFY_EVENT_PEN_SOC: + scnprintf(event_string, CHG_SHOW_MAX_SIZE, + "POWER_SUPPLY_PEN_SOC=%d", + data->soc); + if (chg->pen_psy) + sysfs_notify(&chg->pen_psy->dev.parent->kobj, NULL, "pen_soc"); + break; + case NOTIFY_EVENT_PEN_MAC: + scnprintf(event_string, CHG_SHOW_MAX_SIZE, + "POWER_SUPPLY_PEN_MAC=%2x:%2x:%2x:%2x:%2x:%2x", + data->mac.addr[0], data->mac.addr[1], + data->mac.addr[2], data->mac.addr[3], + data->mac.addr[4], data->mac.addr[5]); + break; + case NOTIFY_EVENT_PEN_ERROR: + scnprintf(event_string, CHG_SHOW_MAX_SIZE, + "POWER_SUPPLY_PEN_ERROR=%d", + data->error); + chg->pen_data.error = data->error; + if (chg->pen_psy) + sysfs_notify(&chg->pen_psy->dev.parent->kobj, NULL, "pen_error"); + break; + default: + pr_err("Invalid notify event %d\n", event); + kfree(event_string); + return; + } + + if (!chg->pen_psy) { + kfree(event_string); + pr_warn("Pen power supply is unavailable\n"); + return; + } + + if (chg->pen_uenvp[0]) + kfree(chg->pen_uenvp[0]); + chg->pen_uenvp[0] = event_string; + chg->pen_uenvp[1] = NULL; + kobject_uevent_env(&chg->pen_psy->dev.kobj, + KOBJ_CHANGE, + chg->pen_uenvp); + pr_info("Send pen event: %s\n", event_string); +} + +static int pen_charger_notify_callback(struct notifier_block *nb, + unsigned long event, void *data) +{ + int i; + bool pen_changed = 0; + struct qti_charger_notify_data *notify_data = data; + struct pen_charger *chg = container_of(nb, struct pen_charger, pen_nb); + + if (notify_data->receiver != OEM_NOTIFY_RECEIVER_PEN_CHG) { + pr_err("Skip mis-matched receiver: %#x\n", notify_data->receiver); + return 0; + } + + switch (event) { + case NOTIFY_EVENT_PEN_ID: + /* PEN ID update */ + pen_changed = 1; + chg->pen_data.id = notify_data->data[0]; + break; + case NOTIFY_EVENT_PEN_STATUS: + /* + * PEN status changes: attached, ready, charging, + * discharging, charged, detached + */ + pen_changed = 1; + chg->pen_data.status = notify_data->data[0]; + break; + case NOTIFY_EVENT_PEN_SOC: + /* PEN status of charge changes */ + pen_changed = 1; + chg->pen_data.soc = notify_data->data[0]; + break; + case NOTIFY_EVENT_PEN_MAC: + /* PEN MAC update */ + pen_changed = 1; + for (i = 0; i < MAC_ADDR_LEN; i++) + chg->pen_data.mac.addr[i] = notify_data->data[i] & 0xFF; + break; + case NOTIFY_EVENT_PEN_ERROR: + /* PEN occurs error */ + pen_changed = 1; + chg->pen_data.error = notify_data->data[0]; + break; + default: + pr_err("Unknown pen event: %#lx\n", event); + break; + } + + if (pen_changed) { + pr_err("pen event: %#lx\n", event); + power_supply_changed(chg->pen_psy); + pen_charger_handle_event(chg, (int)event); + } + + return 0; +} + +static int pen_charger_psy_notify_callback(struct notifier_block *nb, + unsigned long val, void *v) +{ + struct pen_charger *chg = container_of(nb, + struct pen_charger, pen_psy_nb); + struct power_supply *psy = v; + + if (!chg) { + pr_err("called before pen charger valid!\n"); + return NOTIFY_DONE; + } + + if (val != PSY_EVENT_PROP_CHANGED) + return NOTIFY_OK; + + if (psy && + !chg->pen_psy && + !strcmp(psy->desc->name, chg->pen_psy_name)) { + pen_charger_psy_init(chg); + } + + return NOTIFY_OK; +} + +static int pen_charger_parse_dt(struct pen_charger *chg) +{ + int rc; + const char *psy_name = NULL; + struct device_node *node = chg->dev->of_node; + + rc = of_property_read_string(node, "mmi,pen-psy-name", &psy_name); + if (rc || !psy_name) + psy_name = "wireless"; + strncpy(chg->pen_psy_name, psy_name, MAX_PSY_NAME_LEN); + + return 0; +} + +static int pen_charger_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pen_charger *chg; + int rc; + + chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL); + if (!chg) + return -ENOMEM; + + platform_set_drvdata(pdev, chg); + chg->dev = dev; + memset(&chg->pen_data, 0, sizeof(struct pen_charger_data)); + memset(&chg->simulator_data, 0, sizeof(struct pen_charger_data)); + chg->pen_nb.notifier_call = pen_charger_notify_callback; + rc = qti_charger_register_notifier(&chg->pen_nb); + if (rc) { + pr_err("Failed to register notifier, rc=%d\n", rc); + return rc; + } + chg->pen_psy_nb.notifier_call = pen_charger_psy_notify_callback; + rc = power_supply_reg_notifier(&chg->pen_psy_nb); + if (rc) { + pr_err("Failed to register psy notifier, rc=%d\n", rc); + return rc; + } + + rc = pen_charger_parse_dt(chg); + if (rc) { + pr_err("Failed to parse devicetree, rc=%d\n", rc); + return rc; + } + + this_chip = chg; + pen_charger_psy_init(chg); + + return 0; +} + +static int pen_charger_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pen_charger *chg= dev_get_drvdata(dev); + + if (chg->pen_psy) { + if (chg->pen_uenvp[0]) { + kfree(chg->pen_uenvp[0]); + chg->pen_uenvp[0] = NULL; + } + device_remove_file(chg->pen_psy->dev.parent, &dev_attr_pen_control); + device_remove_file(chg->pen_psy->dev.parent, &dev_attr_pen_id); + device_remove_file(chg->pen_psy->dev.parent, &dev_attr_pen_soc); + device_remove_file(chg->pen_psy->dev.parent, &dev_attr_pen_status); + device_remove_file(chg->pen_psy->dev.parent, &dev_attr_pen_mac); + device_remove_file(chg->pen_psy->dev.parent, &dev_attr_pen_error); + power_supply_put(chg->pen_psy); + } + + power_supply_unreg_notifier(&chg->pen_psy_nb); + qti_charger_unregister_notifier(&chg->pen_nb); + + return 0; +} + +static const struct of_device_id pen_charger_match_table[] = { + {.compatible = "mmi,pen-charger"}, + {}, +}; + +static struct platform_driver pen_charger_driver = { + .driver = { + .name = "pen_charger", + .of_match_table = pen_charger_match_table, + }, + .probe = pen_charger_probe, + .remove = pen_charger_remove, +}; + +module_platform_driver(pen_charger_driver); + +MODULE_DESCRIPTION("Smart Pen Charger Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/wakeup_sources/Android.mk b/drivers/power/wakeup_sources/Android.mk new file mode 100644 index 000000000000..97a0a6aebbed --- /dev/null +++ b/drivers/power/wakeup_sources/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := wakeup_sources.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/power/wakeup_sources/Kbuild b/drivers/power/wakeup_sources/Kbuild new file mode 100644 index 000000000000..95618af7359e --- /dev/null +++ b/drivers/power/wakeup_sources/Kbuild @@ -0,0 +1,6 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += wakeup_sources.o + diff --git a/drivers/power/wakeup_sources/Makefile b/drivers/power/wakeup_sources/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/power/wakeup_sources/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/power/wakeup_sources/wakeup_sources.c b/drivers/power/wakeup_sources/wakeup_sources.c new file mode 100644 index 000000000000..237c67764e78 --- /dev/null +++ b/drivers/power/wakeup_sources/wakeup_sources.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * /proc/wakeup_sources + */ + +struct proc_dir_entry *g_proc_wakeup_sources; +int g_lock_wakeup_sources; + +/** + * print_wakeup_source_stats - Print wakeup source statistics information. + * @m: seq_file to print the statistics into. + * @ws: Wakeup source object to print the statistics for. + */ +static int print_wakeup_source_stats(struct seq_file *m, + struct wakeup_source *ws) +{ + unsigned long flags; + ktime_t total_time; + ktime_t max_time; + unsigned long active_count; + ktime_t active_time; + ktime_t prevent_sleep_time; + + spin_lock_irqsave(&ws->lock, flags); + + total_time = ws->total_time; + max_time = ws->max_time; + prevent_sleep_time = ws->prevent_sleep_time; + active_count = ws->active_count; + if (ws->active) { + ktime_t now = ktime_get(); + + active_time = ktime_sub(now, ws->last_time); + total_time = ktime_add(total_time, active_time); + if (active_time > max_time) + max_time = active_time; + + if (ws->autosleep_enabled) + prevent_sleep_time = ktime_add(prevent_sleep_time, + ktime_sub(now, ws->start_prevent_time)); + } else { + active_time = 0; + } + + seq_printf(m, "%-12s\t%lu\t\t%lu\t\t%lu\t\t%lu\t\t%lld\t\t%lld\t\t%lld\t\t%lld\t\t%lld\n", + ws->name, active_count, ws->event_count, + ws->wakeup_count, ws->expire_count, + ktime_to_ms(active_time), ktime_to_ms(total_time), + ktime_to_ms(max_time), ktime_to_ms(ws->last_time), + ktime_to_ms(prevent_sleep_time)); + + spin_unlock_irqrestore(&ws->lock, flags); + + return 0; +} + +static void *wakeup_sources_stats_seq_start(struct seq_file *m, + loff_t *pos) +{ + struct wakeup_source *ws; + loff_t n = *pos; + + if (n == 0) { + seq_puts(m, "name\t\tactive_count\tevent_count\twakeup_count\t" + "expire_count\tactive_since\ttotal_time\tmax_time\t" + "last_change\tprevent_suspend_time\n"); + } + + g_lock_wakeup_sources = wakeup_sources_read_lock(); + for_each_wakeup_source(ws) { + if (n-- <= 0) + return ws; + } + + return NULL; +} + +static void *wakeup_sources_stats_seq_next(struct seq_file *m, + void *v, loff_t *pos) +{ + struct wakeup_source *ws = v; + struct wakeup_source *next_ws = NULL; + + ++(*pos); + next_ws = wakeup_sources_walk_next(ws); + + return next_ws; +} + +static void wakeup_sources_stats_seq_stop(struct seq_file *m, void *v) +{ + wakeup_sources_read_unlock(g_lock_wakeup_sources); +} + +/** + * wakeup_sources_stats_seq_show - Print wakeup sources statistics information. + * @m: seq_file to print the statistics into. + * @v: wakeup_source of each iteration + */ +static int wakeup_sources_stats_seq_show(struct seq_file *m, void *v) +{ + struct wakeup_source *ws = v; + + print_wakeup_source_stats(m, ws); + + return 0; +} + +static const struct seq_operations wakeup_sources_stats_seq_ops = { + .start = wakeup_sources_stats_seq_start, + .next = wakeup_sources_stats_seq_next, + .stop = wakeup_sources_stats_seq_stop, + .show = wakeup_sources_stats_seq_show, +}; + +static int __init proc_wakeup_sources_init(void) +{ + pr_debug("%s(), %d\n", __func__, __LINE__); + g_proc_wakeup_sources = proc_create_seq("wakeup_sources", + S_IRUGO, NULL, &wakeup_sources_stats_seq_ops); + return 0; +} + +static void __exit proc_wakeup_sources_exit(void) +{ + pr_debug("%s(), %d\n", __func__, __LINE__); + proc_remove(g_proc_wakeup_sources); +} + +module_init(proc_wakeup_sources_init); +module_exit(proc_wakeup_sources_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/Android.mk b/drivers/regulator/Android.mk new file mode 100644 index 000000000000..9c82a6b1f912 --- /dev/null +++ b/drivers/regulator/Android.mk @@ -0,0 +1,4 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/drivers/regulator/aw37501/Android.mk b/drivers/regulator/aw37501/Android.mk new file mode 100644 index 000000000000..767021ac2c6f --- /dev/null +++ b/drivers/regulator/aw37501/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := aw37501.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/regulator/aw37501/Kbuild b/drivers/regulator/aw37501/Kbuild new file mode 100644 index 000000000000..bd4323aabcc4 --- /dev/null +++ b/drivers/regulator/aw37501/Kbuild @@ -0,0 +1,4 @@ +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +obj-m += aw37501.o diff --git a/drivers/regulator/aw37501/Makefile b/drivers/regulator/aw37501/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/regulator/aw37501/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/regulator/aw37501/aw37501.c b/drivers/regulator/aw37501/aw37501.c new file mode 100644 index 000000000000..4c410bceaf0e --- /dev/null +++ b/drivers/regulator/aw37501/aw37501.c @@ -0,0 +1,1802 @@ +/* +* aw37501.c +* +* Version: v0.1.2 +* +* Copyright (c) 2021 AWINIC Technology CO., LTD +* +* Author: Alex +* +* 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "aw37501.h" + +#define AW37501_DRIVER_VERSION "V0.1.2" + +static int aw37501_write(struct aw37501_power *aw37501, u8 reg, u8 val) +{ + int ret = -1; + unsigned char cnt = 0; + + pr_info("%s: reg[%x] = %x\n", __func__, reg, val); + while (cnt < AW_I2C_RETRIES) { + ret = + i2c_smbus_write_byte_data(aw37501->client, reg, val); + if (ret < 0) { + pr_err("%s: i2c_write cnt=%d error=%d\n", + __func__, cnt, ret); + } else { + break; + } + cnt++; + usleep_range(2000, 3000); + } + + return ret; +} + +static int aw37501_read(struct aw37501_power *aw37501, u8 reg, u8 *val) +{ + int ret = -1; + unsigned char cnt = 0; + + while (cnt < AW_I2C_RETRIES) { + ret = i2c_smbus_read_byte_data(aw37501->client, reg); + if (ret < 0) { + pr_err("%s: i2c_read cnt=%d error=%d\n", + __func__, cnt, ret); + } else { + *val = ret; + break; + } + cnt++; + usleep_range(2000, 3000); + } + return ret; +} + +static int aw37501_soft_reset(struct aw37501_power *aw37501) +{ + u8 reg_val; + int ret = 0; + + pr_info("%s\n", __func__); + + ret = aw37501_write(aw37501, AW37501_REG_PRO, AW37501_OPEN_CMD); + if (ret < 0) + return ret; + + ret = aw37501_read(aw37501, AW37501_REG_APPS, ®_val); + if (ret < 0) + return ret; + + reg_val = reg_val | AW37501_RESET; + ret = aw37501_write(aw37501, AW37501_REG_APPS, reg_val); + if (ret < 0) + return ret; + + ret = aw37501_write(aw37501, AW37501_REG_PRO, AW37501_CLOSE_CMD); + if (ret < 0) + return ret; + + return 0; +} + +static int aw37501_disn_enable(struct aw37501_power *aw37501) +{ + u8 reg_val; + int ret; + + pr_info("%s\n", __func__); + + ret = aw37501_read(aw37501, AW37501_REG_APPS, ®_val); + if (ret < 0) + return ret; + + reg_val = reg_val | AW37501_DISN_ENABLE; + ret = aw37501_write(aw37501, AW37501_REG_APPS, reg_val); + if (ret < 0) + return ret; + return 0; +} + +static int aw37501_disn_disable(struct aw37501_power *aw37501) +{ + u8 reg_val; + int ret; + + pr_info("%s\n", __func__); + + ret = aw37501_read(aw37501, AW37501_REG_APPS, ®_val); + if (ret < 0) + return ret; + + reg_val = reg_val & AW37501_DISN_DISABLE; + ret = aw37501_write(aw37501, AW37501_REG_APPS, reg_val); + if (ret < 0) + return ret; + return 0; +} +static int aw37501_disp_enable(struct aw37501_power *aw37501) +{ + u8 reg_val; + int ret; + + pr_info("%s\n", __func__); + + ret = aw37501_read(aw37501, AW37501_REG_APPS, ®_val); + if (ret < 0) + return ret; + + reg_val = reg_val | AW37501_DISP_ENABLE; + ret = aw37501_write(aw37501, AW37501_REG_APPS, reg_val); + if (ret < 0) + return ret; + return 0; +} + +static int aw37501_disp_disable(struct aw37501_power *aw37501) +{ + u8 reg_val; + int ret; + + pr_info("%s\n", __func__); + + ret = aw37501_read(aw37501, AW37501_REG_APPS, ®_val); + if (ret < 0) + return ret; + + reg_val = reg_val & AW37501_DISP_DISABLE; + ret = aw37501_write(aw37501, AW37501_REG_APPS, reg_val); + if (ret < 0) + return ret; + return 0; +} + +static int aw37501_offset_set(struct aw37501_power *aw37501) +{ + u8 reg_val; + u8 set_val; + int ret; + + pr_info("%s\n", __func__); + + if (aw37501->offset == AW_OFFSET_0MA) { + set_val = AW_OFFSET_0MA_VAL; + } else if (aw37501->offset == AW_OFFSET_50MA) { + set_val = AW_OFFSET_50MA_VAL; + } else if (aw37501->offset == AW_OFFSET_100MA) { + set_val = AW_OFFSET_100MA_VAL; + } else if (aw37501->offset == AW_OFFSET_150MA) { + set_val = AW_OFFSET_150MA_VAL; + } else { + pr_err("%s, offset input val error.\n", __func__); + return -EINVAL; + } + + ret = aw37501_read(aw37501, AW37501_REG_CTRL, ®_val); + if (ret < 0) { + pr_info("%s, aw37501_read error...\n", __func__); + return ret; + } + + reg_val = (reg_val & AW37501_OFFSET_CLEAR) | set_val; + ret = aw37501_write(aw37501, AW37501_REG_CTRL, reg_val); + if (ret < 0) { + pr_err("%s, aw37501_write failed\n", __func__); + return ret; + } + return ret; +} + +static int aw37501_offset_read(struct aw37501_power *aw37501) +{ + u8 reg_val; + int ret; + + pr_info("%s\n", __func__); + + ret = aw37501_read(aw37501, AW37501_REG_CTRL, ®_val); + if (ret < 0) { + pr_err("%s, aw37501_read error...\n", __func__); + return ret; + } + pr_info("%s val = %#x\n", __func__, reg_val); + reg_val = (reg_val & AW37501_OFFSET_GET); + if (reg_val == AW_OFFSET_0MA_VAL) { + aw37501->offset = AW_OFFSET_0MA; + } else if (reg_val == AW_OFFSET_50MA_VAL) { + aw37501->offset = AW_OFFSET_50MA; + } else if (reg_val == AW_OFFSET_100MA_VAL) { + aw37501->offset = AW_OFFSET_100MA; + } else if (reg_val == AW_OFFSET_150MA_VAL) { + aw37501->offset = AW_OFFSET_150MA; + } else { + pr_err("%s, no match offset data val\n", __func__); + return ret; + } + return 0; +} + +static int aw37501_enn_enable(struct aw37501_power *aw37501) +{ + u8 reg_val; + int ret = 0; + + if (aw37501->bool_gpio_ctrl == true) { + ret = pinctrl_select_state(aw37501->pinctrl.pinctrl, + aw37501->pinctrl.aw_enn_high); + if (ret < 0) { + pr_info("%s pinctrl_select_state failed for aw_enn_high\n", + __func__); + return ret; + } + } else { + ret = aw37501_write(aw37501, AW37501_REG_PRO, AW37501_OPEN_CMD); + if (ret < 0) + return ret; + + ret = aw37501_read(aw37501, AW37501_REG_APPS, ®_val); + if (ret < 0) + return ret; + + reg_val = reg_val | AW37501_ENN_ENABLE; + ret = aw37501_write(aw37501, AW37501_REG_APPS, reg_val); + if (ret < 0) + return ret; + + ret = aw37501_write(aw37501, AW37501_REG_PRO, AW37501_CLOSE_CMD); + if (ret < 0) + return ret; + } + return 0; +} + +static int aw37501_enn_disable(struct aw37501_power *aw37501) +{ + u8 reg_val; + int ret = 0; + + if (aw37501->bool_gpio_ctrl == true) { + ret = pinctrl_select_state(aw37501->pinctrl.pinctrl, + aw37501->pinctrl.aw_enn_low); + if (ret < 0) { + pr_info("%s pinctrl_select_state failed for aw_enn_low\n", + __func__); + return ret; + } + } else { + ret = aw37501_write(aw37501, AW37501_REG_PRO, AW37501_OPEN_CMD); + if (ret < 0) + return ret; + + ret = aw37501_read(aw37501, AW37501_REG_APPS, ®_val); + if (ret < 0) + return ret; + + reg_val = reg_val & AW37501_ENN_DISABLE; + ret = aw37501_write(aw37501, AW37501_REG_APPS, reg_val); + if (ret < 0) + return ret; + + ret = aw37501_write(aw37501, AW37501_REG_PRO, AW37501_CLOSE_CMD); + if (ret < 0) + return ret; + } + return 0; +} + +static int aw37501_enp_enable(struct aw37501_power *aw37501) +{ + u8 reg_val; + int ret = 0; + + if (aw37501->bool_gpio_ctrl == true) { + ret = pinctrl_select_state(aw37501->pinctrl.pinctrl, + aw37501->pinctrl.aw_enp_high); + if (ret < 0) { + pr_info("%s pinctrl_select_state failed for aw_enp_high\n", + __func__); + return ret; + } + } else { + ret = aw37501_write(aw37501, AW37501_REG_PRO, AW37501_OPEN_CMD); + if (ret < 0) + return ret; + + ret = aw37501_read(aw37501, AW37501_REG_APPS, ®_val); + if (ret < 0) + return ret; + + reg_val = reg_val | AW37501_ENP_ENABLE; + pr_info("%s write reg_val = %#x\n", __func__, reg_val); + + ret = aw37501_write(aw37501, AW37501_REG_APPS, reg_val); + if (ret < 0) + return ret; + + ret = aw37501_write(aw37501, AW37501_REG_PRO, AW37501_CLOSE_CMD); + if (ret < 0) + return ret; + } + return 0; +} + +static int aw37501_enp_disable(struct aw37501_power *aw37501) +{ + u8 reg_val; + int ret = 0; + + if (aw37501->bool_gpio_ctrl == true) { + ret = pinctrl_select_state(aw37501->pinctrl.pinctrl, + aw37501->pinctrl.aw_enp_low); + if (ret < 0) { + pr_info("%s pinctrl_select_state failed for aw_enp_low\n", + __func__); + return ret; + } + } else { + ret = aw37501_write(aw37501, AW37501_REG_PRO, AW37501_OPEN_CMD); + if (ret < 0) + return ret; + + ret = aw37501_read(aw37501, AW37501_REG_APPS, ®_val); + if (ret < 0) + return ret; + + reg_val = reg_val & AW37501_ENP_DISABLE; + ret = aw37501_write(aw37501, AW37501_REG_APPS, reg_val); + if (ret < 0) + return ret; + + ret = aw37501_write(aw37501, AW37501_REG_PRO, + AW37501_CLOSE_CMD); + if (ret < 0) + return ret; + } + return 0; +} + +static int aw37501_current_limit_set(struct aw37501_power *aw37501) +{ + u8 reg_val; + int ret; + + pr_info("%s\n", __func__); + + ret = aw37501_read(aw37501, AW37501_REG_APPS, ®_val); + if (ret < 0) + return ret; + + reg_val = (reg_val & AW37501_CURRENT_CLEAR) | aw37501->power_mode; + ret = aw37501_write(aw37501, AW37501_REG_APPS, reg_val); + if (ret < 0) + return ret; + return ret; +} + +static int aw37501_current_limit_get(struct aw37501_power *aw37501) +{ + u8 reg_val; + int ret; + + pr_info("%s\n", __func__); + + ret = aw37501_read(aw37501, AW37501_REG_APPS, ®_val); + if (ret < 0) + return ret; + aw37501->read_power_mode = reg_val; + + return 0; +} + + +static int aw37501_voltage_outp_set(struct aw37501_power *aw37501) +{ + u8 reg_val; + int ret; + + pr_info("%s out_val = %#x\n", __func__, aw37501->outp); + + ret = aw37501_read(aw37501, AW37501_REG_VOUTP, ®_val); + if (ret < 0) { + pr_err("%s, aw37501_read error...\n", __func__); + return ret; + } + + reg_val = (reg_val & AW37501_OUT_CLEAR) | aw37501->outp; + ret = aw37501_write(aw37501, AW37501_REG_VOUTP, reg_val); + if (ret < 0) + return ret; + return 0; +} + +static int aw37501_voltage_outn_set(struct aw37501_power *aw37501) +{ + u8 reg_val; + int ret; + + pr_info("%s out_val = %#x\n", __func__, aw37501->outn); + + ret = aw37501_read(aw37501, AW37501_REG_VOUTN, ®_val); + if (ret < 0) { + pr_err("%s, aw37501_read error...\n", __func__); + return ret; + } + + reg_val = (reg_val & AW37501_OUT_CLEAR) | aw37501->outn; + ret = aw37501_write(aw37501, AW37501_REG_VOUTN, reg_val); + if (ret < 0) + return ret; + + return 0; +} +static int aw37501_voltage_outp_get(struct aw37501_power *aw37501) +{ + u8 reg_val; + int ret; + + pr_info("%s\n", __func__); + + ret = aw37501_read(aw37501, AW37501_REG_VOUTP, ®_val); + if (ret < 0) { + pr_err("%s, aw37501_read error...\n", __func__); + return ret; + } + aw37501->read_outp = reg_val; + + return 0; +} + +static int aw37501_voltage_outn_get(struct aw37501_power *aw37501) +{ + u8 reg_val; + int ret; + + pr_info("%s\n", __func__); + + ret = aw37501_read(aw37501, AW37501_REG_VOUTN, ®_val); + if (ret < 0) { + pr_err("%s, aw37501_read error...\n", __func__); + return ret; + } + aw37501->read_outn = reg_val; + return 0; +} + +static int aw37501_ldo_current_set(struct aw37501_power *aw37501) +{ + u8 reg_val; + int ret; + + pr_info("%s\n", __func__); + + ret = aw37501_read(aw37501, AW37501_REG_CTRL, ®_val); + if (ret < 0) { + pr_err("%s, aw37501_read error...\n", __func__); + return ret; + } + if (aw37501->limit == MAX_LOO_CURRENT) + reg_val = reg_val & MAX_LOO_CURRENT_VAL; + else if (aw37501->limit == MIN_LOD_CURRENT) + reg_val = reg_val & MIN_LOD_CURRENT_VAL; + + ret = aw37501_write(aw37501, AW37501_REG_CTRL, reg_val); + if (ret < 0) + return ret; + + return ret; +} + +static int aw37501_status_read(struct aw37501_power *aw37501, u8 *val) +{ + u8 reg_val; + int ret; + + pr_info("%s\n", __func__); + + ret = aw37501_read(aw37501, AW37501_REG_APPS, ®_val); + if (ret < 0) { + pr_err("%s, aw37501_read error...\n", __func__); + return ret; + } + *val = reg_val; + + return 0; +} + +/* ************************* regulator driver ************************* */ +static int aw37501_list_voltage(struct regulator_dev *rdev, unsigned selector) +{ + pr_info("%s enter test\n", __func__); + + if (selector >= rdev->desc->n_voltages || + selector < rdev->desc->linear_min_sel) + return -EINVAL; + if (strcmp(rdev->desc->name, "outp")) { + pr_info("%s enter outp\n", __func__); + return rdev->desc->min_uV + (rdev->desc->uV_step * selector); + } else if (strcmp(rdev->desc->name, "outn")) { + pr_info("%s enter outn\n", __func__); + return rdev->desc->min_uV + (rdev->desc->uV_step * selector); + } else { + return -ENODEV; + } + return 0; +} + +static int +aw37501_set_voltage_sel(struct regulator_dev *rdev, unsigned selector) +{ + struct aw37501_power *aw37501 = rdev_get_drvdata(rdev); + int ret = 0; + + pr_info("%s enter selector = %#x\n", __func__, selector); + + if (strcmp(rdev->desc->name, "outp")) { + aw37501->outp = selector; + pr_info("%s enter set outp = %#x\n", __func__, aw37501->outp); + + ret = aw37501_voltage_outp_set(aw37501); + if (ret < 0) { + pr_err("%s, aw37501_voltage_outp_set error\n", + __func__); + return ret; + } + } else if (strcmp(rdev->desc->name, "outn")) { + aw37501->outn = selector; + pr_info("%s enter set outn = %#x\n", __func__, aw37501->outn); + + ret = aw37501_voltage_outn_set(aw37501); + if (ret < 0) { + pr_err("%s, aw37501_voltage_outn_set error\n", + __func__); + return ret; + } + } else { + pr_err("%s, regulator outp/outn set failed\n", __func__); + return -ENODEV; + } + + return ret; +} + +static int aw37501_get_voltage_sel(struct regulator_dev *rdev) +{ + int i = 0; + int ret = 0; + struct aw37501_power *aw37501 = rdev_get_drvdata(rdev); + + pr_info("%s enter test\n", __func__); + + if (!strcmp(rdev->desc->name, "outp")) { + ret = aw37501_voltage_outp_get(aw37501); + if (ret < 0) { + return ret; + pr_err("%s, aw37501_voltage_outp_get error\n", + __func__); + } + pr_info("%s get outp = %#x\n", __func__, aw37501->read_outp); + + for (i = 0; i < ARRAY_SIZE(aw_vout_regval); i++) { + if (aw37501->read_outp == aw_vout_regval[i]) { + pr_info("%s, aw_vout_map_outp[%d] = %d\n", + __func__, i, aw_vout_map_outp[i]); + return aw_vout_map_outp[i]; + } + } + } else if (!strcmp(rdev->desc->name, "outn")) { + ret = aw37501_voltage_outn_get(aw37501); + if (ret < 0) { + return ret; + pr_err("%s, aw37501_voltage_outn_get error\n", + __func__); + } + pr_info("%s get outn = %#x\n", __func__, aw37501->read_outn); + + for (i = 0; i < ARRAY_SIZE(aw_vout_regval); i++) { + if (aw37501->read_outn == aw_vout_regval[i]) { + pr_info("%s, aw_vout_map_outn[%d] = %d\n", + __func__, i, aw_vout_map_outn[i]); + return aw_vout_map_outn[i]; + } + } + + } else { + pr_err("%s, regulator dev error\n", __func__); + return -ENODEV; + } + + return 0; +} + +static int aw37501_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + struct aw37501_power *aw37501 = rdev_get_drvdata(rdev); + int ret = 0; + + pr_info("%s enter min = %d, max = %d\n", __func__, min_uA, max_uA); + + if (min_uA < AW_CURRENT_40MA) + aw37501->power_mode = AW_CURRENT_40MA_VAL; + else if (min_uA < AW_CURRENT_80MA) + aw37501->power_mode = AW_CURRENT_80MA_VAL; + else if (min_uA < AW_CURRENT_100MA) + aw37501->power_mode = AW_CURRENT_100MA_VAL; + else + return -EINVAL; + + ret = aw37501_current_limit_set(aw37501); + if (ret < 0) { + pr_err("%s,aw37501_current_limit_set failed\n", __func__); + return ret; + } + + return ret; +} + +static int aw37501_get_current_limit(struct regulator_dev *rdev) +{ + struct aw37501_power *aw37501 = rdev_get_drvdata(rdev); + int ret = 0; + + pr_info("%s enter\n", __func__); + + ret = aw37501_current_limit_get(aw37501); + if (ret < 0) { + pr_err("%s, aw37501_current_limit_get failed\n", __func__); + return ret; + } + if ((aw37501->read_power_mode & AW37501_CURRENT_GET) == AW_CURRENT_40MA_VAL) + ret = AW_CURRENT_40MA; + else if ((aw37501->read_power_mode & AW37501_CURRENT_GET) == AW_CURRENT_80MA_VAL) + ret = AW_CURRENT_80MA; + else if ((aw37501->read_power_mode & AW37501_CURRENT_GET) == AW_CURRENT_100MA_VAL) + ret = AW_CURRENT_100MA; + else { + pr_err("%s, no matched data val\n", __func__); + return ret; + } + + pr_info("%s ret = %d\n", __func__, ret); + return ret; +} + +static int aw37501_enable(struct regulator_dev *rdev) +{ + struct aw37501_power *aw37501 = rdev_get_drvdata(rdev); + int ret = 0; + + pr_info("%s enter\n", __func__); + + if (!strcmp(rdev->desc->name, "outp")) { + ret = aw37501_enp_enable(aw37501); + if (ret < 0) { + pr_err("%s, aw37501_enp_enable failed\n", __func__); + return ret; + } + } else if (!strcmp(rdev->desc->name, "outn")) { + ret = aw37501_enn_enable(aw37501); + if (ret < 0) { + pr_err("%s, aw37501_enn_enable failed\n", __func__); + return ret; + } + } else { + pr_err("%s, regulator enable error\n", __func__); + return -ENODEV; + } + + return 0; +} + +static int aw37501_disable(struct regulator_dev *rdev) +{ + struct aw37501_power *aw37501 = rdev_get_drvdata(rdev); + int ret = 0; + + pr_info("%s enter\n", __func__); + + if (!strcmp(rdev->desc->name, "outp")) { + ret = aw37501_enp_disable(aw37501); + if (ret < 0) { + pr_err("%s, aw37501_enp_disable failed\n", __func__); + return ret; + } + } else if (!strcmp(rdev->desc->name, "outn")) { + ret = aw37501_enn_disable(aw37501); + if (ret < 0) { + pr_err("%s, aw37501_enn_disable failed\n", __func__); + return ret; + } + } else { + pr_err("%s, regulator disable error\n", __func__); + return -ENODEV; + } + + return 0; +} + +static int aw37501_is_enabled(struct regulator_dev *rdev) +{ + struct aw37501_power *aw37501 = rdev_get_drvdata(rdev); + u8 val = 0; + int ret = -1; + + if (aw37501->bool_gpio_ctrl == true) { + if (!strcmp(rdev->desc->name, "outp")) { + val = gpio_get_value(aw37501->enp_gpio); + return val; + } else if (!strcmp(rdev->desc->name, "outn")) { + val = gpio_get_value(aw37501->enn_gpio); + return val; + } + } else { + ret = aw37501_status_read(aw37501, &val); + if (ret < 0) + return ret; + if (!strcmp(rdev->desc->name, "outp")) { + val = (val & (1 << 3)) >> 3; + return val; + } else if (!strcmp(rdev->desc->name, "outn")) { + val = (val & (1 << 4)) >> 4; + return val; + } + } + return ret; +} + +static struct regulator_ops aw37501_outp_ops = { + .list_voltage = aw37501_list_voltage, + .set_voltage_sel = aw37501_set_voltage_sel, + .get_voltage_sel = aw37501_get_voltage_sel, + .set_current_limit = aw37501_set_current_limit, + .get_current_limit = aw37501_get_current_limit, + .enable = aw37501_enable, + .disable = aw37501_disable, + .is_enabled = aw37501_is_enabled, +}; + +static struct regulator_ops aw37501_outn_ops = { + .list_voltage = aw37501_list_voltage, + .set_voltage_sel = aw37501_set_voltage_sel, + .get_voltage_sel = aw37501_get_voltage_sel, + .set_current_limit = aw37501_set_current_limit, + .get_current_limit = aw37501_get_current_limit, + .enable = aw37501_enable, + .disable = aw37501_disable, + .is_enabled = aw37501_is_enabled, +}; + +static struct regulator_desc aw37501_desc[] = { + { + .name = "outp", + .id = 0, + .n_voltages = N_VOLTAGE, + .min_uV = MIN_VOLTAGE, + .uV_step = UV_STEP, + .linear_min_sel = 0, + .ops = &aw37501_outp_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, + { + .name = "outn", + .id = 1, + .n_voltages = N_VOLTAGE, + .min_uV = MIN_VOLTAGE, + .uV_step = UV_STEP, + .linear_min_sel = 0, + .ops = &aw37501_outn_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + }, +}; + +static int aw37501_pinctrl_init(struct aw37501_power *aw37501) +{ + struct aw37501_pinctrl *pinctrl = &aw37501->pinctrl; + + pinctrl->pinctrl = devm_pinctrl_get(aw37501->dev); + if (IS_ERR_OR_NULL(pinctrl->pinctrl)) { + pr_err("%s:No pinctrl found\n", __func__); + pinctrl->pinctrl = NULL; + return 0; + } + /* for enn */ + pinctrl->aw_enn_default = pinctrl_lookup_state(pinctrl->pinctrl, + "aw_enn_default"); + if (IS_ERR_OR_NULL(pinctrl->aw_enn_default)) { + pr_err("%s: Failed get pinctrl state:enn default state\n", + __func__); + goto exit_pinctrl_init; + } + + pinctrl->aw_enn_high = pinctrl_lookup_state(pinctrl->pinctrl, + "aw_enn_output_high"); + if (IS_ERR_OR_NULL(pinctrl->aw_enn_high)) { + pr_err("%s: Failed get pinctrl state:enn_high\n", __func__); + goto exit_pinctrl_init; + } + + pinctrl->aw_enn_low = pinctrl_lookup_state(pinctrl->pinctrl, + "aw_enn_output_low"); + if (IS_ERR_OR_NULL(pinctrl->aw_enn_low)) { + pr_err("%s: Failed get pinctrl state:enn_low\n", __func__); + goto exit_pinctrl_init; + } + /* for enp */ + pinctrl->aw_enp_default = pinctrl_lookup_state(pinctrl->pinctrl, + "aw_enp_default"); + if (IS_ERR_OR_NULL(pinctrl->aw_enp_default)) { + pr_err("%s: Failed get pinctrl state:enp default state\n", + __func__); + goto exit_pinctrl_init; + } + + pinctrl->aw_enp_high = pinctrl_lookup_state(pinctrl->pinctrl, + "aw_enp_output_high"); + if (IS_ERR_OR_NULL(pinctrl->aw_enp_high)) { + pr_err("%s: Failed get pinctrl state:enp_high\n", __func__); + goto exit_pinctrl_init; + } + + pinctrl->aw_enp_low = pinctrl_lookup_state(pinctrl->pinctrl, + "aw_enp_output_low"); + if (IS_ERR_OR_NULL(pinctrl->aw_enp_low)) { + pr_err("%s: Failed get pinctrl state:enp_low\n", __func__); + goto exit_pinctrl_init; + } + pr_info("%s: Success init pinctrl\n", __func__); + return 0; + exit_pinctrl_init: + devm_pinctrl_put(pinctrl->pinctrl); + return -ENODEV; +} + +static void aw37501_pinctrl_deinit(struct aw37501_power *aw37501) +{ + if (aw37501->pinctrl.pinctrl) + devm_pinctrl_put(aw37501->pinctrl.pinctrl); +} + +/***************************sys attribute*********************************/ +static ssize_t aw37501_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + + unsigned char i = 0, reg_val = 0; + ssize_t len = 0; + int ret = 0; + + pr_info("%s\n", __func__); + + for (i = 0; i < AW37501_REG_MAX; i++) { + if (!(aw37501_reg_access[i] & REG_RD_ACCESS)) + continue; + ret = aw37501_read(aw37501, i, ®_val); + if (ret < 0) { + pr_err("aw37501_read failed,ret = %d\n", ret); + break; + } + len += snprintf(buf+len, PAGE_SIZE-len, "reg:0x%02x=0x%02x\n", + i, reg_val); + } + + return len; +} + +static ssize_t aw37501_reg_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0}; + + pr_info("%s\n", __func__); + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) { + aw37501_write(aw37501, (unsigned char)databuf[0], + (unsigned char)databuf[1]); + } + + return len; +} + +static ssize_t aw37501_power_outp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + int ret = 0; + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + + pr_info("%s\n", __func__); + + ret = aw37501->regulators[0] + ->desc->ops->get_voltage_sel(aw37501->regulators[0]); + if (ret < 0) { + pr_err("get_voltage_sel failed,ret = %d\n", ret); + return ret; + } + + len += snprintf(buf + len, PAGE_SIZE - len, "outp : %d\n", ret); + + return len; +} + +static ssize_t aw37501_power_outp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + int ret = 0; + unsigned int databuf[1] = {0}; + + pr_info("%s\n", __func__); + + if (sscanf(buf, "%x", &databuf[0]) == 1) { + if (databuf[0] >= MIN_VOLTAGE_VAL && databuf[0] <= MAX_VOLTAGE_VAL) { + ret = + aw37501->regulators[1]->desc->ops->set_voltage_sel(aw37501->regulators[1], + (unsigned char)databuf[0]); + if (ret < 0) { + dev_err(dev, "%s set_voltage_sel error\n", __func__); + return ret; + } + } + } + + return len; +} + +static ssize_t aw37501_power_outn_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + int ret = 0; + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + + pr_info("%s\n", __func__); + + ret = aw37501->regulators[1] + ->desc->ops->get_voltage_sel(aw37501->regulators[1]); + if (ret < 0 && ret > -MIN_VOLTAGE) { + dev_err(dev, "%s get_voltage_sel failed\n", __func__); + return ret; + } + len += snprintf(buf + len, PAGE_SIZE - len, "outn : %d\n", ret); + + return len; +} + +static ssize_t aw37501_power_outn_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + int ret = 0; + unsigned int databuf[1] = {0}; + + pr_info("%s\n", __func__); + + if (sscanf(buf, "%x", &databuf[0]) == 1) { + if (databuf[0] >= MIN_VOLTAGE_VAL && databuf[0] <= MAX_VOLTAGE_VAL) { + ret = aw37501->regulators[1]->desc->ops->set_voltage_sel(aw37501->regulators[0], + (unsigned char)databuf[0]); + if (ret < 0) { + dev_err(dev, "%s set_voltage_sel error\n", + __func__); + return ret; + } + } + } + + return len; +} + +static ssize_t aw37501_offset_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + int ret = 0; + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + + pr_info("%s\n", __func__); + + ret = aw37501_offset_read(aw37501); + if (ret < 0) { + dev_err(dev, "%s aw37501_offset_read error\n", __func__); + return ret; + } + + len += snprintf(buf + len, PAGE_SIZE - len, "offset: %d\n", + aw37501->offset); + + return len; +} + +static ssize_t aw37501_offset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + int val = 0; + int rc = 0; + + pr_info("%s\n", __func__); + + rc = kstrtouint(buf, 10, &val); + if (rc < 0) + return rc; + + aw37501->offset = val; + rc = aw37501_offset_set(aw37501); + if (rc < 0) { + dev_err(dev, "%s aw37501_offset_set error\n", __func__); + return rc; + } + + return len; +} + +static ssize_t aw37501_enn_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + int ret = 0; + + pr_info("%s\n", __func__); + + ret = aw37501->regulators[1] + ->desc->ops->is_enabled(aw37501->regulators[1]); + if (ret < 0) { + dev_err(dev, "%s is_enabled error\n", __func__); + return ret; + } + + len += snprintf(buf + len, PAGE_SIZE - len, "outn status: %d\n", ret); + + return len; +} + +static ssize_t aw37501_enn_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + int ret = 0; + int val = 0; + + pr_info("%s\n", __func__); + + ret = kstrtouint(buf, 0, &val); + if (ret < 0) { + dev_err(dev, "%s kstrtouint failed\n", __func__); + return ret; + } + + if (val == 1) { + ret = aw37501->regulators[1] + ->desc->ops->enable(aw37501->regulators[1]); + if (ret < 0) { + dev_err(dev, "%s enable error\n", __func__); + return ret; + } + } else if (val == 0) { + ret = aw37501->regulators[1] + ->desc->ops->disable(aw37501->regulators[1]); + if (ret < 0) { + dev_err(dev, "%s disable error\n", __func__); + return ret; + } + } else { + dev_err(dev, "%s input val error\n", __func__); + return -EINVAL; + } + return len; +} + +static ssize_t aw37501_enp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + + ssize_t len = 0; + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + int ret = 0; + + pr_info("%s\n", __func__); + + ret = aw37501->regulators[0] + ->desc->ops->is_enabled(aw37501->regulators[0]); + if (ret < 0) { + dev_err(dev, "%s is_enabled error\n", __func__); + return ret; + } + + len += snprintf(buf + len, PAGE_SIZE - len, "outp status: %d\n", ret); + + return len; +} + +static ssize_t aw37501_enp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + int ret = 0; + int val = 0; + + pr_info("%s\n", __func__); + + ret = kstrtouint(buf, 10, &val); + if (ret < 0) { + dev_err(dev, "%s kstrtouint failed\n", __func__); + return ret; + } + pr_info("%s val = %d\n", __func__, val); + + if (val == 1) { + ret = aw37501->regulators[0] + ->desc->ops->enable(aw37501->regulators[0]); + if (ret < 0) { + dev_err(dev, "%s enable error\n", __func__); + return ret; + } + } else if (val == 0) { + ret = aw37501->regulators[0] + ->desc->ops->disable(aw37501->regulators[0]); + if (ret < 0) { + dev_err(dev, "%s disable error\n", __func__); + return ret; + } + } else { + dev_err(dev, "%s input val error\n", __func__); + return -EINVAL; + } + return len; +} + +static ssize_t aw37501_current_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + int ret = 0; + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + + pr_info("%s\n", __func__); + + ret = aw37501->regulators[0] + ->desc->ops->get_current_limit(aw37501->regulators[0]); + if (ret < 0) { + dev_err(dev, "%s get_current_limit error\n", __func__); + return ret; + } + len += snprintf(buf + len, PAGE_SIZE - len, "modes: %d\n", ret); + + return len; +} + +static ssize_t aw37501_current_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + int ret = 0; + int databuf[2] = {0}; + + pr_info("%s\n", __func__); + + if (sscanf(buf, "%d %d", &databuf[0], &databuf[1]) == 2) { + ret = aw37501->regulators[1]->desc->ops-> + set_current_limit(aw37501->regulators[1], + databuf[0], databuf[1]); + if (ret < 0) { + dev_err(dev, "%s set_current_limit error\n", __func__); + return ret; + } + } + + return len; +} + +static ssize_t aw37501_disn_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + int ret = 0; + int val = 0; + + ret = kstrtouint(buf, 0, &val); + if (ret < 0) { + dev_err(dev, "%s kstrtouint failed\n", __func__); + return ret; + } + if (val > 0) { + ret = aw37501_disn_enable(aw37501); + if (ret < 0) { + dev_err(dev, "%s aw37501_disn_enable error\n", + __func__); + return ret; + } + } else if (val == 0) { + ret = aw37501_disn_disable(aw37501); + if (ret < 0) { + dev_err(dev, "%s aw37501_disn_disable error\n", + __func__); + return ret; + } + } else { + return -EINVAL; + } + return len; +} + +static ssize_t aw37501_disp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + int ret = 0; + int val = 0; + + ret = kstrtouint(buf, 0, &val); + if (ret < 0) { + dev_err(dev, "%s kstrtouint failed\n", __func__); + return ret; + } + if (val > 0) { + ret = aw37501_disp_enable(aw37501); + if (ret < 0) { + dev_err(dev, "%s aw37501_disp_enable error\n", + __func__); + return ret; + } + } else if (val == 0) { + ret = aw37501_disp_disable(aw37501); + if (ret < 0) { + dev_err(dev, "%s aw37501_disp_disable error\n", + __func__); + return ret; + } + } else { + return -EINVAL; + } + return len; +} + +static ssize_t aw37501_listoutp_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + int ret = 0; + int val = 0; + + ret = kstrtouint(buf, 10, &val); + if (ret < 0) { + dev_err(dev, "%s kstrtouint failed\n", __func__); + return ret; + } + if (val < 0 || val >= N_VOLTAGE) { + dev_err(dev, "%s,error n_voltage = %d\n", __func__, val); + return -EINVAL; + } + + ret = + aw37501->regulators[1]->desc->ops->list_voltage(aw37501->regulators[1], + val); + if (ret < 0) { + dev_err(dev, "%s list_voltage error\n", __func__); + return ret; + } + pr_info("%s,voltage = %d\n", __func__, ret); + + return len; +} + +static ssize_t aw37501_listoutn_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + int ret = 0; + int val = 0; + + ret = kstrtouint(buf, 10, &val); + if (ret < 0) { + dev_err(dev, "%s kstrtouint failed\n", __func__); + return ret; + } + if (val < 0 || val >= N_VOLTAGE) + return -EINVAL; + + ret = + aw37501->regulators[0]->desc->ops->list_voltage(aw37501->regulators[0], + val); + if (ret < 0) { + dev_err(dev, "%s list_voltage error\n", __func__); + return ret; + } + pr_info("%s,voltage = %d\n", __func__, ret); + + return len; +} + +static ssize_t aw37501_reset_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct aw37501_power *aw37501 = dev_get_drvdata(dev); + int ret = 0; + int val = 0; + + ret = kstrtouint(buf, 0, &val); + if (ret < 0) { + dev_err(dev, "%s kstrtouint failed\n", __func__); + return ret; + } + + if (val == 1) { + ret = aw37501_soft_reset(aw37501); + if (ret < 0) { + dev_err(dev, "%s aw37501_soft_reset error\n", __func__); + return ret; + } + } else { + return -EINVAL; + } + return len; +} + +static DEVICE_ATTR(reg, 0664, aw37501_reg_show, aw37501_reg_store); +static DEVICE_ATTR(outp, 0664, aw37501_power_outp_show, + aw37501_power_outp_store); +static DEVICE_ATTR(outn, 0664, aw37501_power_outn_show, + aw37501_power_outn_store); +static DEVICE_ATTR(offset, 0664, aw37501_offset_show, aw37501_offset_store); +static DEVICE_ATTR(enn, 0664, aw37501_enn_show, aw37501_enn_store); +static DEVICE_ATTR(enp, 0664, aw37501_enp_show, aw37501_enp_store); +static DEVICE_ATTR(current_mode, 0664, aw37501_current_mode_show, + aw37501_current_mode_store); +static DEVICE_ATTR(disn, 0664, NULL, aw37501_disn_store); +static DEVICE_ATTR(disp, 0664, NULL, aw37501_disp_store); +static DEVICE_ATTR(list_outp, 0664, NULL, aw37501_listoutp_store); +static DEVICE_ATTR(list_outn, 0664, NULL, aw37501_listoutn_store); +static DEVICE_ATTR(reset, 0664, NULL, aw37501_reset_store); + + +static struct attribute *aw37501_power_attributes[] = { + &dev_attr_reg.attr, + &dev_attr_outp.attr, + &dev_attr_outn.attr, + &dev_attr_offset.attr, + &dev_attr_enn.attr, + &dev_attr_enp.attr, + &dev_attr_current_mode.attr, + &dev_attr_disn.attr, + &dev_attr_disp.attr, + &dev_attr_list_outp.attr, + &dev_attr_list_outn.attr, + &dev_attr_reset.attr, + NULL, +}; + +static struct attribute_group aw37501_power_attr_group = { + .attrs = aw37501_power_attributes +}; + +/* **********************************driver********************************** */ +static int aw37501_power_init(struct aw37501_power *aw37501) +{ + int ret = 0; + + pr_info("%s\n", __func__); + + + if(aw37501->chip_id == AW37501_CHIP_ID) { + /* VREG offset voltage configure */ + ret = aw37501_offset_set(aw37501); + if (ret < 0) { + dev_err(aw37501->dev, "%s aw37501_offset_set error\n", + __func__); + return ret; + } + + /* Current mode */ + ret = aw37501_current_limit_set(aw37501); + if (ret < 0) { + dev_err(aw37501->dev, "%s aw37501_current_limit_set error\n", + __func__); + return ret; + } + + /* LDO output current limit value configure*/ + ret = aw37501_ldo_current_set(aw37501); + if (ret < 0) { + dev_err(aw37501->dev, "%s aw37501_ldo_current_set error\n", + __func__); + return ret; + } + + /* Output voltage of LDO */ + ret = aw37501_voltage_outp_set(aw37501); + if (ret < 0) { + dev_err(aw37501->dev, "%s aw37501_voltage_outp_set error\n", + __func__); + return ret; + } + + /* Output voltage of CHARGE PUMP */ + ret = aw37501_voltage_outn_set(aw37501); + if (ret < 0) { + dev_err(aw37501->dev, "%s aw37501_voltage_outn_set error\n", + __func__); + return ret; + } + } else { + + /* Current mode */ + ret = aw37501_current_limit_set(aw37501); + if (ret < 0) { + dev_err(aw37501->dev, "%s aw37501_current_limit_set error\n", + __func__); + return ret; + } + + /* Output voltage of LDO */ + ret = aw37501_voltage_outp_set(aw37501); + if (ret < 0) { + dev_err(aw37501->dev, "%s aw37501_voltage_outp_set error\n", + __func__); + return ret; + } + + /* Output voltage of CHARGE PUMP */ + ret = aw37501_voltage_outn_set(aw37501); + if (ret < 0) { + dev_err(aw37501->dev, "%s aw37501_voltage_outn_set error\n", + __func__); + return ret; + } + } + return 0; +} + +static int +aw37501_gpio_init(struct aw37501_power *aw37501, struct i2c_client *i2c) +{ + int ret; + + pr_info("%s enter\n", __func__); + ret = aw37501_pinctrl_init(aw37501); + if (ret < 0) { + pr_err("%s: Failed get wanted pinctrl state\n", __func__); + return ret; + } + ret = pinctrl_select_state(aw37501->pinctrl.pinctrl, + aw37501->pinctrl.aw_enn_high); + if (ret < 0) { + pr_err("%s: pinctrl_select_state failed for aw_enn_high\n", + __func__); + return ret; + } + ret = pinctrl_select_state(aw37501->pinctrl.pinctrl, + aw37501->pinctrl.aw_enp_high); + if (ret < 0) { + pr_err("%s: pinctrl_select_state failed for aw_enp_high\n", + __func__); + return ret; + } + return 0; +} + +static int +aw37501_parse_dt(struct i2c_client *i2c, struct aw37501_power *aw37501, + struct device_node *np) +{ + int rc = 0; + struct device *dev = &i2c->dev; + + pr_info("%s enter\n", __func__); + + + aw37501->bool_regulator_fixed = of_property_read_bool(np, "bool_regulator_fixed"); + dev_info(dev, "%s: bool_regulator_fixed %d.\n", + __func__, aw37501->bool_regulator_fixed); + + aw37501->bool_gpio_ctrl = of_property_read_bool(np, "bool_gpio_ctrl"); + + if (aw37501->bool_gpio_ctrl == true) { + aw37501->enn_gpio = of_get_named_gpio(np, "enn-gpio", 0); + if (gpio_is_valid(aw37501->enn_gpio)) { + dev_info(dev, "%s: enn gpio provided ok.\n", __func__); + } else { + dev_err(dev, "%s: no enn gpio provided.\n", __func__); + return -EIO; + } + + aw37501->enp_gpio = of_get_named_gpio(np, "enp-gpio", 0); + if (gpio_is_valid(aw37501->enp_gpio)) { + dev_info(dev, "%s: enp gpio provided ok.\n", __func__); + } else { + dev_err(dev, "%s: no enp gpio provided.\n", __func__); + return -EIO; + } + + rc = aw37501_gpio_init(aw37501, i2c); + if (rc < 0) { + dev_err(&aw37501->client->dev, + "aw37501_gpio_init failed, rc = %d\n", rc); + return rc; + } + + pr_info("%s: ctrl by gpio\n", __func__); + + } else { + pr_info("%s: ctrl by IIC\n", __func__); + } + + rc = of_property_read_u32(np, "outp", &aw37501->outp); + if (rc < 0) { + dev_err(&aw37501->client->dev, + "Failure reading outp, rc = %d\n", rc); + goto read_err; + } else { + pr_info("%s,outp = %#x\n", __func__, aw37501->outp); + } + + rc = of_property_read_u32(np, "outn", &aw37501->outn); + if (rc < 0) { + dev_err(&aw37501->client->dev, + "Failure reading outn, rc = %d\n", rc); + goto read_err; + } else { + pr_info("%s,outn = %#x\n", __func__, aw37501->outn); + } + + rc = of_property_read_u32(np, "power_mode", &aw37501->power_mode); + if (rc < 0) { + dev_err(&aw37501->client->dev, + "Failure reading mode, rc = %d\n", rc); + goto read_err; + } else { + pr_info("%s,power mode = %#x\n", __func__, aw37501->power_mode); + } + + rc = of_property_read_u32(np, "offset", &aw37501->offset); + if (rc < 0) { + dev_err(&aw37501->client->dev, + "Failure reading offset, rc = %d\n", rc); + goto read_err; + } else { + pr_info("%s,power offset = %d\n", __func__, aw37501->offset); + } + + rc = of_property_read_u32(np, "limit", &aw37501->limit); + if (rc < 0) { + dev_err(&aw37501->client->dev, + "Failure reading limit, rc = %d\n", rc); + goto read_err; + } else { + pr_info("%s,power limit = %d\n", __func__, aw37501->limit); + } + + return 0; +read_err: + if (aw37501->bool_gpio_ctrl == true) + aw37501_pinctrl_deinit(aw37501); + return rc; +} + +static int aw37501_read_chipid(struct aw37501_power *aw37501) +{ + int ret; + u8 val; + + pr_info("%s enter\n", __func__); + ret = aw37501_read(aw37501, AW37501_REG_CTRL, &val); + if (ret < 0) { + pr_err("aw37501_read failed,ret = %d\n", ret); + return ret; + } + val = val & AW37501_GET_CHIP_ID; + aw37501->chip_id = val; + if (val == AW37501_CHIP_ID) + pr_info("%s,awinic IC, chip id = 0x%02x\n", __func__, val); + else { + pr_info("%s,not awinic IC, chip id = 0x%02x\n", __func__, val); + return ret; + } + + return 0; +} + +static int aw37501_power_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + int ret = 0; + int i; + struct aw37501_power *aw37501 = NULL; + struct device_node *node = i2c->dev.of_node; + + pr_info("%s\n", __func__); + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed\n"); + return -EIO; + } + + aw37501 = devm_kzalloc(&i2c->dev, + (sizeof(struct aw37501_power)), GFP_KERNEL); + if (!aw37501) + return -ENOMEM; + + aw37501->dev = &i2c->dev; + aw37501->client = i2c; + + ret = aw37501_read_chipid(aw37501); + if (ret < 0) + goto chip_error; + + if (node) { + ret = aw37501_parse_dt(i2c, aw37501, node); + if (ret < 0) + goto chip_error; + } else { + pr_info("%s, failed device node if null!\n", __func__); + goto chip_error; + } + i2c_set_clientdata(i2c, aw37501); + + if (aw37501->bool_regulator_fixed != true) { + for (i = 0; i < ARRAY_SIZE(aw37501_desc); i++) { + + struct regulator_desc *desc = &aw37501_desc[i]; + struct regulator_init_data *init = NULL; + struct regulator_config config = { }; + + init = of_get_regulator_init_data(&i2c->dev, node, desc); + + /* set never disable */ + init->constraints.always_on = 1; + + if (init == NULL) { + dev_err(&i2c->dev, " %s Failed to get init_data %s\n", + __func__, desc->name); + of_node_put(node); + goto regulator_error; + } + config.dev = &i2c->dev; + config.init_data = init; + config.driver_data = aw37501; + config.of_node = i2c->dev.of_node; + + pr_info("register regulator %s\n", desc->name); + aw37501->regulators[i] = devm_regulator_register(&i2c->dev, + desc, &config); + if (IS_ERR(aw37501->regulators[i])) { + ret = PTR_ERR(aw37501->regulators[i]); + dev_err(&i2c->dev, "failed to register regulator %s:%d\n", + desc->name, ret); + goto regulator_error; + } + } + } + + i2c_set_clientdata(i2c, aw37501); + + ret = sysfs_create_group(&aw37501->dev->kobj, + &aw37501_power_attr_group); + if (ret < 0) { + dev_err(&aw37501->client->dev, "power sysfs ret: %d\n", ret); + goto regulator_error; + } + + ret = aw37501_power_init(aw37501); + if (ret < 0) { + dev_err(&aw37501->client->dev, "%s,aw37501_power_init failed\n", + __func__); + goto init_error; + } + + return 0; +init_error: + sysfs_remove_group(&aw37501->dev->kobj, + &aw37501_power_attr_group); +regulator_error: + while (--i >= 0) + regulator_unregister(aw37501->regulators[i]); +chip_error: + devm_kfree(&i2c->dev, aw37501); + aw37501 = NULL; + return ret; +} + +static int aw37501_power_remove(struct i2c_client *i2c) +{ + struct aw37501_power *aw37501 = i2c_get_clientdata(i2c); + int i = 2; + + pr_info("%s\n", __func__); + sysfs_remove_group(&aw37501->dev->kobj, + &aw37501_power_attr_group); + while (--i >= 0) + regulator_unregister(aw37501->regulators[i]); + + if (aw37501->bool_gpio_ctrl == true) + aw37501_pinctrl_deinit(aw37501); + + devm_kfree(&i2c->dev, aw37501); + aw37501 = NULL; + + return 0; +} + +static const struct i2c_device_id aw37501_power_id[] = { + {"aw37501_led", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, aw37501_power_id); + +static const struct of_device_id aw37501_match_table[] = { + {.compatible = "awinic,aw37501",}, + { }, +}; + +static struct i2c_driver aw37501_power_driver = { + .driver = { + .name = "aw37501", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw37501_match_table), + }, + .probe = aw37501_power_probe, + .remove = aw37501_power_remove, + .id_table = aw37501_power_id, +}; + +static int __init aw37501_module_init(void) +{ + pr_info("%s: driver version: %s\n", __func__, AW37501_DRIVER_VERSION); + return i2c_add_driver(&aw37501_power_driver); +} +module_init(aw37501_module_init); + +static void __exit aw37501_module_exit(void) +{ + i2c_del_driver(&aw37501_power_driver); +} +module_exit(aw37501_module_exit); + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("AWINIC AW37501 Power driver"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/regulator/aw37501/aw37501.h b/drivers/regulator/aw37501/aw37501.h new file mode 100644 index 000000000000..a2c439388e09 --- /dev/null +++ b/drivers/regulator/aw37501/aw37501.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) aw37501, The Linux Foundation. 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 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. + * + */ + +#ifndef __LINUX_AW37501_LED_H__ +#define __LINUX_AW37501_LED_H__ + +#define AW37501_REG_VOUTP 0x00 +#define AW37501_REG_VOUTN 0x01 +#define AW37501_REG_APPS 0x03 +#define AW37501_REG_CTRL 0x04 +#define AW37501_REG_PRO 0x21 + +#define AW37501_REG_MAX 0x2f +#define AW37501_OPEN_CMD 0x4C +#define AW37501_CLOSE_CMD 0x00 + +#define AW37501_CHIP_ID 0x01 +#define AW37501_GET_CHIP_ID 0x03 + +#define AW37501_OUT_CLEAR 0xE0 + +/*register read/write access*/ +#define REG_NONE_ACCESS 0 +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) + +#define AW37501_RESET (1 << 5) + +#define AW37501_DISN_ENABLE (1 << 0) +#define AW37501_DISN_DISABLE (~(1 << 0)) +#define AW37501_DISP_ENABLE (1 << 1) +#define AW37501_DISP_DISABLE (~(1 << 1)) + +#define AW37501_ENN_ENABLE (1 << 4) +#define AW37501_ENN_DISABLE (~(1 << 4)) +#define AW37501_ENP_ENABLE (1 << 3) +#define AW37501_ENP_DISABLE (~(1 << 3)) +#define AW37501_ENP_IS_ENABLE (1 << 3) +#define AW37501_ENN_IS_ENABLE (~(1 << 3)) + +#define AW_I2C_RETRIES 5 + +#define AW_OFFSET_0MA 0 +#define AW_OFFSET_50MA 50 +#define AW_OFFSET_100MA 100 +#define AW_OFFSET_150MA 150 + +#define AW37501_OFFSET_CLEAR (~(3 << 6)) +#define AW37501_OFFSET_GET (3 << 6) + +#define AW_OFFSET_0MA_VAL 0 +#define AW_OFFSET_50MA_VAL (1 << 6) +#define AW_OFFSET_100MA_VAL (1 << 7) +#define AW_OFFSET_150MA_VAL (3 << 6) + + +#define AW_CURRENT_40MA 40000 +#define AW_CURRENT_80MA 80000 +#define AW_CURRENT_100MA 100000 + +#define AW37501_CURRENT_GET (3 << 6) +#define AW37501_CURRENT_CLEAR (~(3 << 6)) +#define AW_CURRENT_40MA_VAL 0 +#define AW_CURRENT_80MA_VAL (1 << 6) +#define AW_CURRENT_100MA_VAL (1 << 7) + +#define MIN_VOLTAGE 4000000 +#define MAX_VOLTAGE 6000000 +#define MIN_VOLTAGE_VAL 0x00 +#define MAX_VOLTAGE_VAL 0x14 + +#define N_VOLTAGE 21 +#define UV_STEP 100000 + +#define MAX_LOO_CURRENT 370 +#define MIN_LOD_CURRENT 270 +#define MAX_LOO_CURRENT_VAL (~(1 << 4)) +#define MIN_LOD_CURRENT_VAL (1 << 4) + +struct aw37501_pinctrl { + struct pinctrl *pinctrl; + struct pinctrl_state *aw_enn_default; + struct pinctrl_state *aw_enn_high; + struct pinctrl_state *aw_enn_low; + struct pinctrl_state *aw_enp_default; + struct pinctrl_state *aw_enp_high; + struct pinctrl_state *aw_enp_low; +}; + +struct aw37501_power { + struct i2c_client *client; + struct device *dev; + struct mutex lock; + struct aw37501_pinctrl pinctrl; + struct regulator_init_data *init_data; + struct regulator_dev *regulators[2]; + + int enn_gpio; + int enp_gpio; + int offset; + int limit; + int power_mode; + int read_power_mode; + int outp; + int outn; + int read_outp; + int read_outn; + bool bool_gpio_ctrl; + bool bool_regulator_fixed; + + u8 chip_id; +}; + +const unsigned char aw37501_reg_access[AW37501_REG_MAX] = { + [AW37501_REG_VOUTP] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW37501_REG_VOUTN] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW37501_REG_APPS] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW37501_REG_CTRL] = REG_RD_ACCESS|REG_WR_ACCESS, +}; + +static const unsigned int aw_vout_map_outp[] = { + 4000000, 4100000, 4200000, 4300000, 4400000, + 4500000, 4600000, 4700000, 4800000, 4900000, + 5000000, 5100000, 5200000, 5300000, 5400000, + 5500000, 5600000, 5700000, 5800000, 5900000, + 6000000, +}; +static const int aw_vout_map_outn[] = { + -4000000, -4100000, -4200000, -4300000, -4400000, + -4500000, -4600000, -4700000, -4800000, -4900000, + -5000000, -5100000, -5200000, -5300000, -5400000, + -5500000, -5600000, -5700000, -5800000, -5900000, + -6000000, +}; + +static const unsigned int aw_vout_regval[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, 0x13, + 0x14, +}; + +#endif diff --git a/drivers/regulator/et5907/Android.mk b/drivers/regulator/et5907/Android.mk new file mode 100644 index 000000000000..1d93ef510064 --- /dev/null +++ b/drivers/regulator/et5907/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := et5907.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/regulator/et5907/Kbuild b/drivers/regulator/et5907/Kbuild new file mode 100644 index 000000000000..1dcb8c514fdb --- /dev/null +++ b/drivers/regulator/et5907/Kbuild @@ -0,0 +1,5 @@ +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +et5907-objs += et5907-regulator.o +obj-m += et5907.o diff --git a/drivers/regulator/et5907/Makefile b/drivers/regulator/et5907/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/regulator/et5907/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/regulator/et5907/et5907-regulator.c b/drivers/regulator/et5907/et5907-regulator.c new file mode 100644 index 000000000000..6a376427a883 --- /dev/null +++ b/drivers/regulator/et5907/et5907-regulator.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * et5907, Multi-Output Regulators + * Copyright (C) 2019 Motorola Mobility LLC, + * + * Author: ChengLong, Motorola Mobility LLC, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "et5907-regulator.h" + +enum slg51000_regulators { + ET5907_REGULATOR_LDO1 = 0, + ET5907_REGULATOR_LDO2, + ET5907_REGULATOR_LDO3, + ET5907_REGULATOR_LDO4, + ET5907_REGULATOR_LDO5, + ET5907_REGULATOR_LDO6, + ET5907_REGULATOR_LDO7, + ET5907_MAX_REGULATORS, +}; + +struct et5907 { + struct device *dev; + struct regmap *regmap; + struct regulator_desc *rdesc[ET5907_MAX_REGULATORS]; + struct regulator_dev *rdev[ET5907_MAX_REGULATORS]; + // int chip_irq; + int chip_cs_pin; +}; + +struct et5907_evt_sta { + unsigned int sreg; +}; + +static const struct et5907_evt_sta et5907_status_reg = { ET5907_LDO_EN }; + +static const struct regmap_range et5907_writeable_ranges[] = { + regmap_reg_range(ET5907_CURRENT_LIMITSEL, ET5907_SEQ_STATUS), + /* Do not let useless register writeable */ + // regmap_reg_range(ET5907_CURRENT_LIMITSEL, ET5907_CURRENT_LIMITSEL), + // regmap_reg_range(ET5907_LDO1_VOUT, ET5907_LDO7_VOUT), + // regmap_reg_range(ET5907_LDO_EN, ET5907_LDO_EN), +}; + +static const struct regmap_range et5907_readable_ranges[] = { + regmap_reg_range(ET5907_CHIP_REV, ET5907_SEQ_STATUS), +}; + +static const struct regmap_range et5907_volatile_ranges[] = { + regmap_reg_range(ET5907_CURRENT_LIMITSEL, ET5907_SEQ_STATUS), +}; + +static const struct regmap_access_table et5907_writeable_table = { + .yes_ranges = et5907_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(et5907_writeable_ranges), +}; + +static const struct regmap_access_table et5907_readable_table = { + .yes_ranges = et5907_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(et5907_readable_ranges), +}; + +static const struct regmap_access_table et5907_volatile_table = { + .yes_ranges = et5907_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(et5907_volatile_ranges), +}; + +static const struct regmap_config et5907_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x0F, + .wr_table = &et5907_writeable_table, + .rd_table = &et5907_readable_table, + .volatile_table = &et5907_volatile_table, +}; + +static int et5907_get_error_flags(struct regulator_dev *rdev, unsigned int *flags) +{ + struct et5907 *chip = rdev_get_drvdata(rdev); + uint8_t reg_dump[ET5907_REG_NUM]; + uint8_t reg_idx; + unsigned int val = 0; + + dev_err(chip->dev, "************ start dump et5907 register ************\n"); + dev_err(chip->dev, "register 0x00: chip version\n"); + dev_err(chip->dev, "register 0x01: LDO CL\n"); + dev_err(chip->dev, "register 0x03~0x09: LDO1~LDO7 OUT Voltage\n"); + dev_err(chip->dev, "register 0x0e: Bit[6:0] LDO7~LDO1 EN\n"); + + for (reg_idx = 0; reg_idx < ET5907_REG_NUM; reg_idx++) { + regmap_read(chip->regmap, reg_idx, &val); + reg_dump[reg_idx] = val; + dev_err(chip->dev, "Reg[0x%02x] = 0x%x", reg_idx, reg_dump[reg_idx]); + } + dev_err(chip->dev, "************ end dump et5907 register ************\n"); + + if (flags != NULL) { + *flags = 0; + } + + return 0; +} + +static int et5907_get_status(struct regulator_dev *rdev) +{ + struct et5907 *chip = rdev_get_drvdata(rdev); + int ret, id = rdev_get_id(rdev); + unsigned int status = 0; + + ret = regulator_is_enabled_regmap(rdev); + if (ret < 0) { + dev_err(chip->dev, "Failed to read enable register(%d)\n", + ret); + return ret; + } + + if (!ret) + return REGULATOR_STATUS_OFF; + + et5907_get_error_flags(rdev, NULL); + + ret = regmap_read(chip->regmap, et5907_status_reg.sreg, &status); + if (ret < 0) { + dev_err(chip->dev, "Failed to read status register(%d)\n", + ret); + return ret; + } + if (status & (0x01ul << id)) { + return REGULATOR_STATUS_ON; + } else { + return REGULATOR_STATUS_OFF; + } +} + +static const struct regulator_ops et5907_regl_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_status = et5907_get_status, + .get_error_flags = et5907_get_error_flags, +}; + +static int et5907_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + int ena_gpio; + + ena_gpio = of_get_named_gpio(np, "enable-gpios", 0); + if (gpio_is_valid(ena_gpio)) + config->ena_gpiod = gpio_to_desc(ena_gpio); + + return 0; +} + +#define ET5907_REGL_DESC(_id, _name, _s_name, _min, _step) \ + [ET5907_REGULATOR_##_id] = { \ + .name = #_name, \ + .supply_name = _s_name, \ + .id = ET5907_REGULATOR_##_id, \ + .of_match = of_match_ptr(#_name), \ + .of_parse_cb = et5907_of_parse_cb, \ + .ops = &et5907_regl_ops, \ + .regulators_node = of_match_ptr("regulators"), \ + .n_voltages = 256, \ + .min_uV = _min, \ + .uV_step = _step, \ + .linear_min_sel = 0, \ + .vsel_mask = ET5907_VSEL_MASK, \ + .vsel_reg = ET5907_##_id##_VSEL, \ + .enable_reg = ET5907_LDO_EN, \ + .enable_mask = BIT(ET5907_REGULATOR_##_id), \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + } + +static struct regulator_desc et5907_regls_desc[ET5907_MAX_REGULATORS] = { + ET5907_REGL_DESC(LDO1, ldo1, "vin1", 600000, 6000), + ET5907_REGL_DESC(LDO2, ldo2, "vin1", 600000, 6000), + ET5907_REGL_DESC(LDO3, ldo3, "vin2", 1200000, 10000), + ET5907_REGL_DESC(LDO4, ldo4, "vin2", 1200000, 10000), + ET5907_REGL_DESC(LDO5, ldo5, "vin2", 1200000, 10000), + ET5907_REGL_DESC(LDO6, ldo6, "vin2", 1200000, 10000), + ET5907_REGL_DESC(LDO7, ldo7, "vin2", 1200000, 10000), +}; + +static int et5907_regulator_init(struct et5907 *chip) +{ + struct regulator_config config = { }; + struct regulator_desc *rdesc; + u8 vsel_range[1]; + int id, ret = 0; + + const unsigned int ldo_regs[ET5907_MAX_REGULATORS] = { + ET5907_LDO1_VOUT, + ET5907_LDO2_VOUT, + ET5907_LDO3_VOUT, + ET5907_LDO4_VOUT, + ET5907_LDO5_VOUT, + ET5907_LDO6_VOUT, + ET5907_LDO7_VOUT, + }; + + const unsigned int initial_voltage[ET5907_MAX_REGULATORS] = { + 0x4B,//LDO1 1.05V + 0x64,//LDO2 1.2V + 0xa0,//LDO3 2.8V + 0xa0,//LDO4 2.8V + 0xa0,//LDO5 2.8V + 0xa0,//LDO6 2.8V + 0x3C,//LDO7 1.8V + }; + + /*Disable all ldo output by default*/ + ret = regmap_write(chip->regmap, ET5907_LDO_EN, 0); + if (ret < 0) { + dev_err(chip->dev, + "Disable all LDO output failed!!!\n"); + return ret; + } + + for (id = 0; id < ET5907_MAX_REGULATORS; id++) { + chip->rdesc[id] = &et5907_regls_desc[id]; + rdesc = chip->rdesc[id]; + config.regmap = chip->regmap; + config.dev = chip->dev; + config.driver_data = chip; + + ret = regmap_bulk_read(chip->regmap, ldo_regs[id], + vsel_range, 1); + pr_err("et5907_regulator_init: LDO%d, default value:0x%x", (id+1), vsel_range[0]); + if (ret < 0) { + dev_err(chip->dev, + "Failed to read the ldo register\n"); + return ret; + } + + ret = regmap_write(chip->regmap, ldo_regs[id], initial_voltage[id]); + if (ret < 0) { + dev_err(chip->dev, + "Failed to write inital voltage register\n"); + return ret; + } + pr_err("et5907_regulator_init: LDO%d, initial value:0x%x", (id+1), initial_voltage[id]); + chip->rdev[id] = devm_regulator_register(chip->dev, rdesc, + &config); + if (IS_ERR(chip->rdev[id])) { + ret = PTR_ERR(chip->rdev[id]); + dev_err(chip->dev, + "Failed to register regulator(%s):%d\n", + chip->rdesc[id]->name, ret); + return ret; + } + } + + return 0; +} + +static int et5907_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct et5907 *chip; + int error, cs_gpio, ret; + int chip_id = 0; + + chip = devm_kzalloc(dev, sizeof(struct et5907), GFP_KERNEL); + if (!chip) { + dev_err(chip->dev, "et5907_i2c_probe Memory error...\n"); + return -ENOMEM; + } + dev_info(chip->dev, "et5907_i2c_probe Enter...\n"); + + cs_gpio = of_get_named_gpio(dev->of_node, "semi,cs-gpios", 0); + if (cs_gpio > 0) { + if (!gpio_is_valid(cs_gpio)) { + dev_err(dev, "Invalid chip select pin\n"); + return -EPERM; + } + + ret = devm_gpio_request_one(dev, cs_gpio, GPIOF_OUT_INIT_HIGH, + "et5907_cs_pin"); + if (ret) { + dev_err(dev, "GPIO(%d) request failed(%d)\n", + cs_gpio, ret); + return ret; + } + + chip->chip_cs_pin = cs_gpio; + } + + dev_info(chip->dev, "et5907_i2c_probe cs_gpio:%d...\n", cs_gpio); + mdelay(10); + + i2c_set_clientdata(client, chip); + chip->dev = dev; + chip->regmap = devm_regmap_init_i2c(client, &et5907_regmap_config); + if (IS_ERR(chip->regmap)) { + error = PTR_ERR(chip->regmap); + dev_err(dev, "Failed to allocate register map: %d\n", + error); + return error; + } + + ret = regmap_bulk_read(chip->regmap, ET5907_CHIP_ID, &chip_id, 1); + if (ret < 0) { + dev_err(dev, "I2C read failed: %d\n", ret); + return ret; + } + dev_info(chip->dev, "et5907 chip id: %02x\n", chip_id); + + ret = et5907_regulator_init(chip); + if (ret < 0) { + dev_err(chip->dev, "Failed to init regulator(%d)\n", ret); + return ret; + } + dev_info(chip->dev, "et5907_i2c_probe Exit...\n"); + + return ret; +} + +static int et5907_i2c_remove(struct i2c_client *client) +{ + struct et5907 *chip = i2c_get_clientdata(client); + struct gpio_desc *desc; + int ret = 0; + + if (chip->chip_cs_pin > 0) { + desc = gpio_to_desc(chip->chip_cs_pin); + ret = gpiod_direction_output_raw(desc, GPIOF_INIT_LOW); + } + + return ret; +} + +static const struct i2c_device_id et5907_i2c_id[] = { + {"et5907", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, et5907_i2c_id); + +static struct i2c_driver et5907_regulator_driver = { + .driver = { + .name = "et5907-regulator", + }, + .probe = et5907_i2c_probe, + .remove = et5907_i2c_remove, + .id_table = et5907_i2c_id, +}; + +module_i2c_driver(et5907_regulator_driver); + +MODULE_AUTHOR("ChengLong "); +MODULE_DESCRIPTION("et5907 regulator driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/regulator/et5907/et5907-regulator.h b/drivers/regulator/et5907/et5907-regulator.h new file mode 100644 index 000000000000..2e8ff23068ca --- /dev/null +++ b/drivers/regulator/et5907/et5907-regulator.h @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * wl2868c, Multi-Output Regulators + * Copyright (C) 2019 Motorola Mobility LLC, + * + * Author: ChengLong, Motorola Mobility LLC, + */ + +#ifndef __ET5907_REGISTERS_H__ +#define __ET5907_REGISTERS_H__ + +/* Registers */ +#define ET5907_REG_NUM (ET5907_SEQ_STATUS - ET5907_CHIP_REV + 1) + +#define ET5907_CHIP_ID 0x00 +#define ET5907_CHIP_REV 0x01 +#define ET5907_CURRENT_LIMITSEL 0x02 +#define ET5907_LDO_EN 0x03 +#define ET5907_LDO1_VOUT 0x04 +#define ET5907_LDO2_VOUT 0x05 +#define ET5907_LDO3_VOUT 0x06 +#define ET5907_LDO4_VOUT 0x07 +#define ET5907_LDO5_VOUT 0x08 +#define ET5907_LDO6_VOUT 0x09 +#define ET5907_LDO7_VOUT 0x0a +#define ET5907_SEQ_STATUS 0x0f + +#define ET5907_LDO_OFFSET 0x01 + +/* ET5907_LDO1_VSEL ~ ET5907_LDO7_VSEL = + * 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a + */ +#define ET5907_LDO1_VSEL ET5907_LDO1_VOUT +#define ET5907_LDO2_VSEL ET5907_LDO2_VOUT +#define ET5907_LDO3_VSEL ET5907_LDO3_VOUT +#define ET5907_LDO4_VSEL ET5907_LDO4_VOUT +#define ET5907_LDO5_VSEL ET5907_LDO5_VOUT +#define ET5907_LDO6_VSEL ET5907_LDO6_VOUT +#define ET5907_LDO7_VSEL ET5907_LDO7_VOUT + +#define ET5907_VSEL_SHIFT 0 +#define ET5907_VSEL_MASK (0xff << 0) + +#endif /* __ET5907_REGISTERS_H__ */ + diff --git a/drivers/regulator/ocp2138/Android.mk b/drivers/regulator/ocp2138/Android.mk new file mode 100644 index 000000000000..e1317b162247 --- /dev/null +++ b/drivers/regulator/ocp2138/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := ocp2138.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/regulator/ocp2138/Kbuild b/drivers/regulator/ocp2138/Kbuild new file mode 100644 index 000000000000..4d5b3eb7c78e --- /dev/null +++ b/drivers/regulator/ocp2138/Kbuild @@ -0,0 +1,4 @@ +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +obj-m += ocp2138.o diff --git a/drivers/regulator/ocp2138/ocp2138.c b/drivers/regulator/ocp2138/ocp2138.c new file mode 100644 index 000000000000..99251c16ede7 --- /dev/null +++ b/drivers/regulator/ocp2138/ocp2138.c @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2021 Wingtech.Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ocp2138.h" + +#define LOG_TAG "LCM_BIAS_OCP2138" +#define LCM_LOGI(fmt, args...) pr_info("[KERNEL/"LOG_TAG"]"fmt, ##args) +#define LCM_LOGD(fmt, args...) pr_debug("[KERNEL/"LOG_TAG"]"fmt, ##args) +#define LCM_LOGE(fmt, args...) pr_debug("[KERNEL ERROR/"LOG_TAG"]"fmt, ##args) + +#define ocp2138_DEV_NAME "ocp2138" + +#define ocp2138_VPOS_ADDRESS 0x00 +#define ocp2138_VNEG_ADDRESS 0x01 + +static unsigned int GPIO_LCD_BIAS_ENN; +static unsigned int GPIO_LCD_BIAS_ENP; +int ocp2138_BiasPower_enable(LCM_LDO_VOLTAGE_E avdd, LCM_LDO_VOLTAGE_E avee,u32 pwrup_delay); +int ocp2138_BiasPower_disable(u32 pwrdown_delay); + +static const struct i2c_device_id ocp2138_i2c_id[] = {{ocp2138_DEV_NAME,0},{}}; +static struct i2c_client *i2c_client = NULL; +static const struct of_device_id panel_i2c_of_match[] = { + { + .compatible = "ocp,i2c_lcd_bias", + }, + }; + +static int ocp2138_read_chip_info(struct i2c_client *client) +{ + int ret; + u8 id_data[4] = {0}; + + ret = i2c_smbus_read_i2c_block_data(client, 0x03, 1, &id_data[0]); + if(ret < 0) + { + LCM_LOGE("ocp2138_read_chip_info() I2C Error\n"); + return -1; + } + + LCM_LOGD("ocp2138_read_chip_info() id_data = 0x%x.\n", id_data[0]); + return 0; +} + +int ocp2138_set_voltage(LCM_LDO_VOLTAGE_E avdd, LCM_LDO_VOLTAGE_E avee,u32 pwrup_delay) +{ + u8 reg_value_avdd =0x00; + u8 reg_value_avee =0x00; + + reg_value_avdd = avdd; + reg_value_avee = avee; + + if(i2c_client == NULL) + { + LCM_LOGE("ocp2138_set_voltage i2c error\n"); + return -1; + } + + if( reg_value_avdd == LCM_LDO_VOL_DEFAULT && reg_value_avee == LCM_LDO_VOL_DEFAULT) + { + LCM_LOGD("LCD bias IC have not conmfig and use default voltage\n"); + if ( gpio_is_valid(GPIO_LCD_BIAS_ENP) ) + gpio_direction_output(GPIO_LCD_BIAS_ENP, 1); + + if(pwrup_delay) + mdelay(pwrup_delay); + else + mdelay(8); + if ( gpio_is_valid(GPIO_LCD_BIAS_ENN) ) + gpio_direction_output(GPIO_LCD_BIAS_ENN, 1); + return 0; + } + + if( reg_value_avdd >= LCM_LDO_VOL_MAX || reg_value_avee >= LCM_LDO_VOL_MAX) + { + LCM_LOGE("LCD bias IC unsupport voltage avdd = %d, avee = %d\n", reg_value_avdd, reg_value_avee); + return -1; + } + + if ( gpio_is_valid(GPIO_LCD_BIAS_ENP) ) + gpio_direction_output(GPIO_LCD_BIAS_ENP, 1); + if(0 != i2c_smbus_write_i2c_block_data(i2c_client, ocp2138_VPOS_ADDRESS, 1, ®_value_avdd)) + { + LCM_LOGE("LCD bias IC avdd write fail\n"); + if ( gpio_is_valid(GPIO_LCD_BIAS_ENN) ) + gpio_direction_output(GPIO_LCD_BIAS_ENN, 1); + return -1; + } + + if(pwrup_delay) + mdelay(pwrup_delay); + else + mdelay(8); + if ( gpio_is_valid(GPIO_LCD_BIAS_ENN) ) + gpio_direction_output(GPIO_LCD_BIAS_ENN, 1); + + if(0 != i2c_smbus_write_i2c_block_data(i2c_client, ocp2138_VNEG_ADDRESS, 1, ®_value_avee)) + { + LCM_LOGE("LCD bias IC avee write fail\n"); + return -1; + } + if(pwrup_delay) + mdelay(pwrup_delay); + else + mdelay(8); + + return 0; +} + +int ocp2138_BiasPower_enable(LCM_LDO_VOLTAGE_E avdd, LCM_LDO_VOLTAGE_E avee,u32 pwrup_delay) +{ + int rc = 0; + + rc = ocp2138_set_voltage(avdd,avee,pwrup_delay); + if( rc < 0 ) + LCM_LOGE("ocp2138 Bias Power enable fail"); + + return rc; +} +EXPORT_SYMBOL(ocp2138_BiasPower_enable); + +int ocp2138_BiasPower_disable(u32 pwrdown_delay) +{ + if ( gpio_is_valid(GPIO_LCD_BIAS_ENN) ) + gpio_direction_output(GPIO_LCD_BIAS_ENN, 0); + if(pwrdown_delay) + mdelay(pwrdown_delay); + else + mdelay(8); + if ( gpio_is_valid(GPIO_LCD_BIAS_ENP) ) + gpio_direction_output(GPIO_LCD_BIAS_ENP, 0); + if(pwrdown_delay) + mdelay(pwrdown_delay); + else + mdelay(8); + + return 0; +} +EXPORT_SYMBOL(ocp2138_BiasPower_disable); + +static int ocp2138_parse_dt(struct device *dev) +{ + struct device_node *np = dev->of_node; + + /* enn, enp gpio info */ + GPIO_LCD_BIAS_ENN = of_get_named_gpio(np, "ocp,lcd-bias-enn-gpio", 0); + if (!gpio_is_valid(GPIO_LCD_BIAS_ENN)) + { + LCM_LOGD("ocp2138_parse_dt() ennnnn gpio not specified\n"); + } + + GPIO_LCD_BIAS_ENP = of_get_named_gpio(np, "ocp,lcd-bias-enp-gpio", 0); + if (!gpio_is_valid(GPIO_LCD_BIAS_ENP)) + { + LCM_LOGD("ocp2138_parse_dt() enpppp gpio not specified\n"); + } + + return 0; +} + +static int ocp2138_gpio_configure(void) +{ + int ret = 0; + + /* request enn gpio */ + if (gpio_is_valid(GPIO_LCD_BIAS_ENN)) + { + ret = gpio_request(GPIO_LCD_BIAS_ENN, "ocp2138_enn_gpio"); + if (ret) + { + LCM_LOGE("[GPIO] GPIO_LCD_BIAS_ENN gpio request failed"); + goto err_enn_gpio_req; + } + } + + /* request enp gpio */ + if (gpio_is_valid(GPIO_LCD_BIAS_ENP)) + { + ret = gpio_request(GPIO_LCD_BIAS_ENP, "ocp2138_enp_gpio"); + if (ret) + { + LCM_LOGE("[GPIO]GPIO_LCD_BIAS_ENP gpio request failed"); + goto err_enp_gpio_dir; + } + } + + return 0; + +err_enp_gpio_dir: + if (gpio_is_valid(GPIO_LCD_BIAS_ENP)) + gpio_free(GPIO_LCD_BIAS_ENP); + +err_enn_gpio_req: + return ret; +} + +static int ocp2138_i2c_remove(struct i2c_client *client) +{ + return 0; +} + +static int ocp2138_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int rc = 0; + + rc = ocp2138_parse_dt(&client->dev); + if (rc) + { + LCM_LOGE("ocp2138_i2c_probe parse dt error.\n"); + return -ENODEV; + } + + rc = ocp2138_gpio_configure(); + if (rc) + { + LCM_LOGE("ocp2138_i2c_probe() configure gpio error.\n"); + } + + gpio_direction_output(GPIO_LCD_BIAS_ENP, 1); + gpio_direction_output(GPIO_LCD_BIAS_ENN, 1); + + if(ocp2138_read_chip_info(client) != 0) + { + LCM_LOGE("ocp2138_i2c_probe() read chip info error.\n"); + + return -ENODEV; + } + i2c_client = client; + LCM_LOGD("ocp2138_i2c_probe() OKAY.\n"); + return 0; +} + +struct i2c_driver ocp2138_i2c_driver = { + .driver = { + .name = ocp2138_DEV_NAME, + .owner = THIS_MODULE, + .of_match_table = panel_i2c_of_match, + }, + .probe = ocp2138_i2c_probe, + .remove = ocp2138_i2c_remove, + .id_table = ocp2138_i2c_id, +}; + + +/* called when loaded into kernel */ +static int __init ocp2138_driver_init(void) +{ + if(i2c_add_driver(&ocp2138_i2c_driver) != 0) + { + printk("ocp2138_driver_init() Fail\n"); + return -1; + } + return 0; +} + +/* should never be called */ +static void __exit ocp2138_driver_exit(void) +{ + i2c_del_driver(&ocp2138_i2c_driver); +} + +module_init(ocp2138_driver_init); +module_exit(ocp2138_driver_exit); + +MODULE_AUTHOR("WingTech Driver Team"); +MODULE_DESCRIPTION("Panel Power Supply Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/regulator/ocp2138/ocp2138.h b/drivers/regulator/ocp2138/ocp2138.h new file mode 100644 index 000000000000..7d9f181c2dc1 --- /dev/null +++ b/drivers/regulator/ocp2138/ocp2138.h @@ -0,0 +1,38 @@ +#ifndef _LINUX_OCP2138_H +#define _LINUX_OCP2138_H + +typedef enum +{ + LCM_LDO_VOL_4V0 = 0, + LCM_LDO_VOL_4V1, + LCM_LDO_VOL_4V2, + LCM_LDO_VOL_4V3, + LCM_LDO_VOL_4V4, + LCM_LDO_VOL_4V5, + LCM_LDO_VOL_4V6, + LCM_LDO_VOL_4V7, + LCM_LDO_VOL_4V8, + LCM_LDO_VOL_4V9, + LCM_LDO_VOL_5V0, + LCM_LDO_VOL_5V1, + LCM_LDO_VOL_5V2, + LCM_LDO_VOL_5V3, + LCM_LDO_VOL_5V4, + LCM_LDO_VOL_5V5, + LCM_LDO_VOL_5V6, + LCM_LDO_VOL_5V7, + LCM_LDO_VOL_5V8, + LCM_LDO_VOL_5V9, + LCM_LDO_VOL_6V0, + LCM_LDO_VOL_6V1, + LCM_LDO_VOL_6V2, + LCM_LDO_VOL_6V3, + LCM_LDO_VOL_6V4, + LCM_LDO_VOL_6V5, + LCM_LDO_VOL_DEFAULT, + LCM_LDO_VOL_MAX, +}LCM_LDO_VOLTAGE_E; + +extern int ocp2138_BiasPower_enable(LCM_LDO_VOLTAGE_E avdd, LCM_LDO_VOLTAGE_E avee,u32 pwrup_delay); +extern int ocp2138_BiasPower_disable(u32 pwrdown_delay); +#endif /* _LINUX_OCP2138_H */ \ No newline at end of file diff --git a/drivers/regulator/slg51000/Android.mk b/drivers/regulator/slg51000/Android.mk new file mode 100644 index 000000000000..b9fbe2847276 --- /dev/null +++ b/drivers/regulator/slg51000/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := slg51000.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/regulator/slg51000/Kbuild b/drivers/regulator/slg51000/Kbuild new file mode 100644 index 000000000000..90ac9f38b050 --- /dev/null +++ b/drivers/regulator/slg51000/Kbuild @@ -0,0 +1,5 @@ +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +slg51000-objs += slg51000-regulator.o +obj-m += slg51000.o diff --git a/drivers/regulator/slg51000/Makefile b/drivers/regulator/slg51000/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/regulator/slg51000/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/regulator/slg51000/slg51000-regulator.c b/drivers/regulator/slg51000/slg51000-regulator.c new file mode 100644 index 000000000000..aa3d50d99d78 --- /dev/null +++ b/drivers/regulator/slg51000/slg51000-regulator.c @@ -0,0 +1,566 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * SLG51000 High PSRR, Multi-Output Regulators + * Copyright (C) 2019 Dialog Semiconductor + * + * Author: Eric Jeong, Dialog Semiconductor + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "slg51000-regulator.h" + +#define SLG51000_REGULATOR_LDO1 0 +#define SLG51000_REGULATOR_LDO2 1 +#define SLG51000_REGULATOR_LDO3 2 +#define SLG51000_REGULATOR_LDO4 3 +#define SLG51000_REGULATOR_LDO5 4 +#define SLG51000_REGULATOR_LDO6 5 +#define SLG51000_REGULATOR_LDO7 6 +#define SLG51000_MAX_REGULATORS 7 +#define SLG51000_SCTL_EVT 7 +#define SLG51000_MAX_EVT_REGISTER 8 + +#define SLG51000_LDOHP_LV_MIN 1200000 +#define SLG51000_LDOHP_HV_MIN 2400000 + +struct slg51000_pdata { + int ena_gpio; +}; + +struct slg51000 { + struct device *dev; + struct regmap *regmap; + struct slg51000_pdata regl_pdata[SLG51000_MAX_REGULATORS]; + struct regulator_desc *rdesc[SLG51000_MAX_REGULATORS]; + struct regulator_dev *rdev[SLG51000_MAX_REGULATORS]; + int chip_irq; + int chip_cs_pin; + int use_default_voltage; +}; + +struct slg51000_evt_sta { + unsigned int ereg; + unsigned int sreg; +}; + +static const struct slg51000_evt_sta es_reg[SLG51000_MAX_EVT_REGISTER] = { + {SLG51000_LDO1_EVENT, SLG51000_LDO1_STATUS}, + {SLG51000_LDO2_EVENT, SLG51000_LDO2_STATUS}, + {SLG51000_LDO3_EVENT, SLG51000_LDO3_STATUS}, + {SLG51000_LDO4_EVENT, SLG51000_LDO4_STATUS}, + {SLG51000_LDO5_EVENT, SLG51000_LDO5_STATUS}, + {SLG51000_LDO6_EVENT, SLG51000_LDO6_STATUS}, + {SLG51000_LDO7_EVENT, SLG51000_LDO7_STATUS}, + {SLG51000_SYSCTL_EVENT, SLG51000_SYSCTL_STATUS}, +}; + +static const struct regmap_range slg51000_writeable_ranges[] = { + regmap_reg_range(SLG51000_SYSCTL_MATRIX_CONF_A, + SLG51000_SYSCTL_MATRIX_CONF_A), + regmap_reg_range(SLG51000_LDO1_VSEL, SLG51000_LDO1_VSEL), + regmap_reg_range(SLG51000_LDO1_MINV, SLG51000_LDO1_MAXV), + regmap_reg_range(SLG51000_LDO1_IRQ_MASK, SLG51000_LDO1_IRQ_MASK), + regmap_reg_range(SLG51000_LDO2_VSEL, SLG51000_LDO2_VSEL), + regmap_reg_range(SLG51000_LDO2_MINV, SLG51000_LDO2_MAXV), + regmap_reg_range(SLG51000_LDO2_IRQ_MASK, SLG51000_LDO2_IRQ_MASK), + regmap_reg_range(SLG51000_LDO3_VSEL, SLG51000_LDO3_VSEL), + regmap_reg_range(SLG51000_LDO3_MINV, SLG51000_LDO3_MAXV), + regmap_reg_range(SLG51000_LDO3_IRQ_MASK, SLG51000_LDO3_IRQ_MASK), + regmap_reg_range(SLG51000_LDO4_VSEL, SLG51000_LDO4_VSEL), + regmap_reg_range(SLG51000_LDO4_MINV, SLG51000_LDO4_MAXV), + regmap_reg_range(SLG51000_LDO4_IRQ_MASK, SLG51000_LDO4_IRQ_MASK), + regmap_reg_range(SLG51000_LDO5_VSEL, SLG51000_LDO5_VSEL), + regmap_reg_range(SLG51000_LDO5_MINV, SLG51000_LDO5_MAXV), + regmap_reg_range(SLG51000_LDO5_IRQ_MASK, SLG51000_LDO5_IRQ_MASK), + regmap_reg_range(SLG51000_LDO6_VSEL, SLG51000_LDO6_VSEL), + regmap_reg_range(SLG51000_LDO6_MINV, SLG51000_LDO6_MAXV), + regmap_reg_range(SLG51000_LDO6_IRQ_MASK, SLG51000_LDO6_IRQ_MASK), + regmap_reg_range(SLG51000_LDO7_VSEL, SLG51000_LDO7_VSEL), + regmap_reg_range(SLG51000_LDO7_MINV, SLG51000_LDO7_MAXV), + regmap_reg_range(SLG51000_LDO7_IRQ_MASK, SLG51000_LDO7_IRQ_MASK), + regmap_reg_range(SLG51000_OTP_IRQ_MASK, SLG51000_OTP_IRQ_MASK), +}; + +static const struct regmap_range slg51000_readable_ranges[] = { + regmap_reg_range(SLG51000_SYSCTL_PATN_ID_B0, + SLG51000_SYSCTL_PATN_ID_B2), + regmap_reg_range(SLG51000_SYSCTL_SYS_CONF_A, + SLG51000_SYSCTL_SYS_CONF_A), + regmap_reg_range(SLG51000_SYSCTL_SYS_CONF_D, + SLG51000_SYSCTL_MATRIX_CONF_B), + regmap_reg_range(SLG51000_SYSCTL_REFGEN_CONF_C, + SLG51000_SYSCTL_UVLO_CONF_A), + regmap_reg_range(SLG51000_SYSCTL_FAULT_LOG1, SLG51000_SYSCTL_IRQ_MASK), + regmap_reg_range(SLG51000_IO_GPIO1_CONF, SLG51000_IO_GPIO_STATUS), + regmap_reg_range(SLG51000_LUTARRAY_LUT_VAL_0, + SLG51000_LUTARRAY_LUT_VAL_11), + regmap_reg_range(SLG51000_MUXARRAY_INPUT_SEL_0, + SLG51000_MUXARRAY_INPUT_SEL_63), + regmap_reg_range(SLG51000_PWRSEQ_RESOURCE_EN_0, + SLG51000_PWRSEQ_INPUT_SENSE_CONF_B), + regmap_reg_range(SLG51000_LDO1_VSEL, SLG51000_LDO1_VSEL), + regmap_reg_range(SLG51000_LDO1_MINV, SLG51000_LDO1_MAXV), + regmap_reg_range(SLG51000_LDO1_MISC1, SLG51000_LDO1_VSEL_ACTUAL), + regmap_reg_range(SLG51000_LDO1_EVENT, SLG51000_LDO1_IRQ_MASK), + regmap_reg_range(SLG51000_LDO2_VSEL, SLG51000_LDO2_VSEL), + regmap_reg_range(SLG51000_LDO2_MINV, SLG51000_LDO2_MAXV), + regmap_reg_range(SLG51000_LDO2_MISC1, SLG51000_LDO2_VSEL_ACTUAL), + regmap_reg_range(SLG51000_LDO2_EVENT, SLG51000_LDO2_IRQ_MASK), + regmap_reg_range(SLG51000_LDO3_VSEL, SLG51000_LDO3_VSEL), + regmap_reg_range(SLG51000_LDO3_MINV, SLG51000_LDO3_MAXV), + regmap_reg_range(SLG51000_LDO3_CONF1, SLG51000_LDO3_VSEL_ACTUAL), + regmap_reg_range(SLG51000_LDO3_EVENT, SLG51000_LDO3_IRQ_MASK), + regmap_reg_range(SLG51000_LDO4_VSEL, SLG51000_LDO4_VSEL), + regmap_reg_range(SLG51000_LDO4_MINV, SLG51000_LDO4_MAXV), + regmap_reg_range(SLG51000_LDO4_CONF1, SLG51000_LDO4_VSEL_ACTUAL), + regmap_reg_range(SLG51000_LDO4_EVENT, SLG51000_LDO4_IRQ_MASK), + regmap_reg_range(SLG51000_LDO5_VSEL, SLG51000_LDO5_VSEL), + regmap_reg_range(SLG51000_LDO5_MINV, SLG51000_LDO5_MAXV), + regmap_reg_range(SLG51000_LDO5_TRIM2, SLG51000_LDO5_TRIM2), + regmap_reg_range(SLG51000_LDO5_CONF1, SLG51000_LDO5_VSEL_ACTUAL), + regmap_reg_range(SLG51000_LDO5_EVENT, SLG51000_LDO5_IRQ_MASK), + regmap_reg_range(SLG51000_LDO6_VSEL, SLG51000_LDO6_VSEL), + regmap_reg_range(SLG51000_LDO6_MINV, SLG51000_LDO6_MAXV), + regmap_reg_range(SLG51000_LDO6_TRIM2, SLG51000_LDO6_TRIM2), + regmap_reg_range(SLG51000_LDO6_CONF1, SLG51000_LDO6_VSEL_ACTUAL), + regmap_reg_range(SLG51000_LDO6_EVENT, SLG51000_LDO6_IRQ_MASK), + regmap_reg_range(SLG51000_LDO7_VSEL, SLG51000_LDO7_VSEL), + regmap_reg_range(SLG51000_LDO7_MINV, SLG51000_LDO7_MAXV), + regmap_reg_range(SLG51000_LDO7_CONF1, SLG51000_LDO7_VSEL_ACTUAL), + regmap_reg_range(SLG51000_LDO7_EVENT, SLG51000_LDO7_IRQ_MASK), + regmap_reg_range(SLG51000_OTP_EVENT, SLG51000_OTP_EVENT), + regmap_reg_range(SLG51000_OTP_IRQ_MASK, SLG51000_OTP_IRQ_MASK), + regmap_reg_range(SLG51000_OTP_LOCK_OTP_PROG, SLG51000_OTP_LOCK_CTRL), + regmap_reg_range(SLG51000_LOCK_GLOBAL_LOCK_CTRL1, + SLG51000_LOCK_GLOBAL_LOCK_CTRL1), +}; + +static const struct regmap_range slg51000_volatile_ranges[] = { + regmap_reg_range(SLG51000_SYSCTL_FAULT_LOG1, SLG51000_SYSCTL_STATUS), + regmap_reg_range(SLG51000_IO_GPIO_STATUS, SLG51000_IO_GPIO_STATUS), + regmap_reg_range(SLG51000_LDO1_EVENT, SLG51000_LDO1_STATUS), + regmap_reg_range(SLG51000_LDO2_EVENT, SLG51000_LDO2_STATUS), + regmap_reg_range(SLG51000_LDO3_EVENT, SLG51000_LDO3_STATUS), + regmap_reg_range(SLG51000_LDO4_EVENT, SLG51000_LDO4_STATUS), + regmap_reg_range(SLG51000_LDO5_EVENT, SLG51000_LDO5_STATUS), + regmap_reg_range(SLG51000_LDO6_EVENT, SLG51000_LDO6_STATUS), + regmap_reg_range(SLG51000_LDO7_EVENT, SLG51000_LDO7_STATUS), + regmap_reg_range(SLG51000_OTP_EVENT, SLG51000_OTP_EVENT), +}; + +static const struct regmap_access_table slg51000_writeable_table = { + .yes_ranges = slg51000_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(slg51000_writeable_ranges), +}; + +static const struct regmap_access_table slg51000_readable_table = { + .yes_ranges = slg51000_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(slg51000_readable_ranges), +}; + +static const struct regmap_access_table slg51000_volatile_table = { + .yes_ranges = slg51000_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(slg51000_volatile_ranges), +}; + +static const struct regmap_config slg51000_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0x8000, + .wr_table = &slg51000_writeable_table, + .rd_table = &slg51000_readable_table, + .volatile_table = &slg51000_volatile_table, +}; + +static int slg51000_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct slg51000 *chip = rdev_get_drvdata(rdev); + int ret, id = rdev_get_id(rdev); + unsigned int state; + + ret = regmap_read(chip->regmap, es_reg[id].sreg, &state); + if (ret < 0) { + dev_err(chip->dev, "Failed to read status register(%d)\n", + ret); + return ret; + } + + if (!(state & SLG51000_STA_ILIM_FLAG_MASK) + && (state & SLG51000_STA_VOUT_OK_FLAG_MASK)) + return 1; + else + return 0; +} + +static int slg51000_regulator_set_voltage_sel(struct regulator_dev *rdev, unsigned sel) { + struct slg51000 *chip = rdev_get_drvdata(rdev); + + if (chip->use_default_voltage) { + return 0; + } + return regulator_set_voltage_sel_regmap(rdev, sel); +} + +static struct regulator_ops slg51000_regl_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = slg51000_regulator_is_enabled, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = slg51000_regulator_set_voltage_sel, +}; + +static struct regulator_ops slg51000_switch_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = slg51000_regulator_is_enabled, +}; + +static int slg51000_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct slg51000 *chip = config->driver_data; + struct slg51000_pdata *rpdata = &chip->regl_pdata[desc->id]; + + rpdata->ena_gpio = of_get_named_gpio(np, "enable-gpios", 0); + if (rpdata->ena_gpio > 0) + config->ena_gpio = rpdata->ena_gpio; + + return 0; +} + +#define SLG51000_REGL_DESC(_id, _name, _nvolt, _msel, _min, _step) \ + [SLG51000_REGULATOR_##_id] = { \ + .name = #_name, \ + .id = SLG51000_REGULATOR_##_id, \ + .of_match = of_match_ptr(#_name), \ + .of_parse_cb = slg51000_of_parse_cb, \ + .ops = &slg51000_regl_ops, \ + .regulators_node = of_match_ptr("regulators"), \ + .n_voltages = _nvolt, \ + .min_uV = _min, \ + .uV_step = _step, \ + .linear_min_sel = _msel, \ + .vsel_mask = SLG51000_VSEL_MASK, \ + .vsel_reg = SLG51000_##_id##_VSEL, \ + .enable_reg = SLG51000_SYSCTL_MATRIX_CONF_A, \ + .enable_mask = BIT(SLG51000_REGULATOR_##_id), \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + } + +static struct regulator_desc regls_desc[SLG51000_MAX_REGULATORS] = { + SLG51000_REGL_DESC(LDO1, ldo1, 256, 0, 2400000, 5000), + SLG51000_REGL_DESC(LDO2, ldo2, 256, 0, 2400000, 5000), + SLG51000_REGL_DESC(LDO3, ldo3, 256, 0, 1200000, 10000), + SLG51000_REGL_DESC(LDO4, ldo4, 256, 0, 1200000, 10000), + SLG51000_REGL_DESC(LDO5, ldo5, 256, 0, 400000, 5000), + SLG51000_REGL_DESC(LDO6, ldo6, 256, 0, 400000, 5000), + SLG51000_REGL_DESC(LDO7, ldo7, 256, 0, 1200000, 10000), +}; + +static int slg51000_regulator_init(struct slg51000 *chip) +{ + struct regulator_config config = { }; + struct regulator_desc *rdesc; + unsigned int reg, val; + u8 vsel_range[2]; + int id, ret = 0; + const unsigned int min_regs[SLG51000_MAX_REGULATORS] = { + SLG51000_LDO1_MINV, SLG51000_LDO2_MINV, SLG51000_LDO3_MINV, + SLG51000_LDO4_MINV, SLG51000_LDO5_MINV, SLG51000_LDO6_MINV, + SLG51000_LDO7_MINV, + }; + + for (id = 0; id < SLG51000_MAX_REGULATORS; id++) { + rdesc = chip->rdesc[id] = ®ls_desc[id]; + + config.regmap = chip->regmap; + config.dev = chip->dev; + config.driver_data = chip; + + ret = regmap_bulk_read(chip->regmap, min_regs[id], + vsel_range, 2); + if (ret < 0) { + dev_err(chip->dev, + "Failed to read the MIN register\n"); + return ret; + } + + switch (id) { + case SLG51000_REGULATOR_LDO1: + case SLG51000_REGULATOR_LDO2: + if (id == SLG51000_REGULATOR_LDO1) + reg = SLG51000_LDO1_MISC1; + else + reg = SLG51000_LDO2_MISC1; + + ret = regmap_read(chip->regmap, reg, &val); + if (ret < 0) { + dev_err(chip->dev, + "Failed to read voltage range of ldo%d\n", + id + 1); + return ret; + } + + rdesc->linear_min_sel = vsel_range[0]; + rdesc->n_voltages = vsel_range[1] + 1; + if (val & SLG51000_SEL_VRANGE_MASK) + rdesc->min_uV = SLG51000_LDOHP_HV_MIN + + (vsel_range[0] + * rdesc->uV_step); + else + rdesc->min_uV = SLG51000_LDOHP_LV_MIN + + (vsel_range[0] + * rdesc->uV_step); + break; + + case SLG51000_REGULATOR_LDO5: + case SLG51000_REGULATOR_LDO6: + if (id == SLG51000_REGULATOR_LDO5) + reg = SLG51000_LDO5_TRIM2; + else + reg = SLG51000_LDO6_TRIM2; + + ret = regmap_read(chip->regmap, reg, &val); + if (ret < 0) { + dev_err(chip->dev, + "Failed to read LDO mode register\n"); + return ret; + } + + if (val & SLG51000_SEL_BYP_MODE_MASK) { + rdesc->ops = &slg51000_switch_ops; + rdesc->n_voltages = 0; + rdesc->min_uV = 0; + rdesc->uV_step = 0; + rdesc->linear_min_sel = 0; + break; + } + /* Fall through for else case */ + + default: + rdesc->linear_min_sel = vsel_range[0]; + rdesc->n_voltages = vsel_range[1] + 1; + rdesc->min_uV = rdesc->min_uV + + (vsel_range[0] * rdesc->uV_step); + break; + } + + chip->rdev[id] = devm_regulator_register(chip->dev, rdesc, + &config); + if (IS_ERR(chip->rdev[id])) { + ret = PTR_ERR(chip->rdev[id]); + dev_err(chip->dev, + "Failed to register regulator(%s):%d\n", + chip->rdesc[id]->name, ret); + return ret; + } + } + + return 0; +} + +static irqreturn_t slg51000_irq_handler(int irq, void *data) +{ + struct slg51000 *chip = data; + struct regmap *regmap = chip->regmap; + u8 evt[SLG51000_MAX_EVT_REGISTER][3]; + int ret, i, handled = IRQ_NONE; + + /* Read event[0], status[1] and mask[2] register */ + for (i = 0; i < SLG51000_MAX_EVT_REGISTER; i++) { + ret = regmap_bulk_read(regmap, es_reg[i].ereg, evt[i], 3); + if (ret < 0) { + dev_err(chip->dev, + "Failed to read event registers(%d)\n", ret); + return IRQ_NONE; + } + } + + for (i = 0; i < SLG51000_MAX_REGULATORS; i++) { + if (!(evt[i][2] & SLG51000_IRQ_ILIM_FLAG_MASK) + && (evt[i][0] & SLG51000_EVT_ILIM_FLAG_MASK)) { + mutex_lock(&chip->rdev[i]->mutex); + regulator_notifier_call_chain(chip->rdev[i], + REGULATOR_EVENT_OVER_CURRENT, + NULL); + mutex_unlock(&chip->rdev[i]->mutex); + + if (evt[i][1] & SLG51000_STA_ILIM_FLAG_MASK) + dev_warn(chip->dev, + "Over-current limit(ldo%d)\n", i + 1); + handled = IRQ_HANDLED; + } + } + + if (!(evt[SLG51000_SCTL_EVT][2] & SLG51000_IRQ_HIGH_TEMP_WARN_MASK) + && (evt[SLG51000_SCTL_EVT][0] & SLG51000_EVT_HIGH_TEMP_WARN_MASK)) { + for (i = 0; i < SLG51000_MAX_REGULATORS; i++) { + if (!(evt[i][1] & SLG51000_STA_ILIM_FLAG_MASK) + && (evt[i][1] & SLG51000_STA_VOUT_OK_FLAG_MASK)) { + mutex_lock(&chip->rdev[i]->mutex); + regulator_notifier_call_chain(chip->rdev[i], + REGULATOR_EVENT_OVER_TEMP, + NULL); + mutex_unlock(&chip->rdev[i]->mutex); + } + } + handled = IRQ_HANDLED; + if (evt[SLG51000_SCTL_EVT][1] + & SLG51000_STA_HIGH_TEMP_WARN_MASK) + dev_warn(chip->dev, "High temperature warning!\n"); + } + + return handled; +} + +static void slg51000_check_crc_and_clear_fault(struct slg51000 *chip) +{ + unsigned int val = 0; + int ret = 0; + + ret = regmap_read(chip->regmap, SLG51000_OTP_EVENT, &val); + if (ret < 0) { + dev_err(chip->dev, "Failed to read OTP CRC register\n"); + return; + } + + if (val & SLG51000_EVT_CRC_MASK) + dev_dbg(chip->dev, "OTP CRC failure is detected\n"); + + ret = regmap_read(chip->regmap, SLG51000_SYSCTL_FAULT_LOG1, &val); + if (ret < 0) { + dev_err(chip->dev, "Failed to read Fault log register\n"); + return; + } + + if (val & SLG51000_FLT_OVER_TEMP_MASK) + dev_dbg(chip->dev, "Fault log: FLT_OVER_TEMP\n"); + if (val & SLG51000_FLT_POWER_SEQ_CRASH_REQ_MASK) + dev_dbg(chip->dev, "Fault log: FLT_POWER_SEQ_CRASH_REQ\n"); + if (val & SLG51000_FLT_RST_MASK) + dev_dbg(chip->dev, "Fault log: FLT_RST\n"); + if (val & SLG51000_FLT_POR_MASK) + dev_dbg(chip->dev, "Fault log: FLT_POR\n"); +} + +static int slg51000_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct slg51000 *chip; + int error, cs_gpio, ret; + + chip = devm_kzalloc(dev, sizeof(struct slg51000), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + cs_gpio = of_get_named_gpio(dev->of_node, "dlg,cs-gpios", 0); + if (cs_gpio > 0) { + if (!gpio_is_valid(cs_gpio)) { + dev_err(dev, "Invalid chip select pin\n"); + return -EPERM; + } + + ret = devm_gpio_request_one(dev, cs_gpio, GPIOF_OUT_INIT_HIGH, + "slg51000_cs_pin"); + if (ret) { + dev_err(dev, "GPIO(%d) request failed(%d)\n", + cs_gpio, ret); + return ret; + } + + chip->chip_cs_pin = cs_gpio; + } + + chip->use_default_voltage = of_property_read_bool(dev->of_node, "use-default-voltage"); + + i2c_set_clientdata(client, chip); + chip->chip_irq = client->irq; + chip->dev = dev; + chip->regmap = devm_regmap_init_i2c(client, &slg51000_regmap_config); + if (IS_ERR(chip->regmap)) { + error = PTR_ERR(chip->regmap); + dev_err(dev, "Failed to allocate register map: %d\n", + error); + return error; + } + + /* FIXME - Optimize Delay */ + mdelay(10); + ret = slg51000_regulator_init(chip); + if (ret < 0) { + dev_err(chip->dev, "Failed to init regulator(%d)\n", ret); + return ret; + } + + slg51000_check_crc_and_clear_fault(chip); + + if (chip->chip_irq) { + ret = devm_request_threaded_irq(dev, chip->chip_irq, NULL, + slg51000_irq_handler, + (IRQF_TRIGGER_LOW + | IRQF_ONESHOT), + "slg51000-irq", chip); + if (ret != 0) { + dev_err(dev, "Failed to request IRQ: %d\n", + chip->chip_irq); + return ret; + } + } else { + dev_warn(dev, "No IRQ configured\n"); + } + + return ret; +} + +static int slg51000_i2c_remove(struct i2c_client *client) +{ + struct slg51000 *chip = i2c_get_clientdata(client); + struct gpio_desc *desc; + int ret = 0; + + if (chip->chip_cs_pin > 0) { + desc = gpio_to_desc(chip->chip_cs_pin); + ret = gpiod_direction_output_raw(desc, GPIOF_INIT_LOW); + } + + return ret; +} + +static const struct i2c_device_id slg51000_i2c_id[] = { + {"slg51000", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, slg51000_i2c_id); + +static struct i2c_driver slg51000_regulator_driver = { + .driver = { + .name = "slg51000-regulator", + }, + .probe = slg51000_i2c_probe, + .remove = slg51000_i2c_remove, + .id_table = slg51000_i2c_id, +}; + +module_i2c_driver(slg51000_regulator_driver); + +MODULE_AUTHOR("Eric Jeong "); +MODULE_DESCRIPTION("SLG51000 regulator driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/regulator/slg51000/slg51000-regulator.h b/drivers/regulator/slg51000/slg51000-regulator.h new file mode 100644 index 000000000000..45c9d75f809e --- /dev/null +++ b/drivers/regulator/slg51000/slg51000-regulator.h @@ -0,0 +1,505 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * SLG51000 High PSRR, Multi-Output Regulators + * Copyright (C) 2019 Dialog Semiconductor + * + * Author: Eric Jeong, Dialog Semiconductor + */ + +#ifndef __SLG51000_REGISTERS_H__ +#define __SLG51000_REGISTERS_H__ + +/* Registers */ + +#define SLG51000_SYSCTL_PATN_ID_B0 0x1105 +#define SLG51000_SYSCTL_PATN_ID_B1 0x1106 +#define SLG51000_SYSCTL_PATN_ID_B2 0x1107 +#define SLG51000_SYSCTL_SYS_CONF_A 0x1109 +#define SLG51000_SYSCTL_SYS_CONF_D 0x110c +#define SLG51000_SYSCTL_MATRIX_CONF_A 0x110d +#define SLG51000_SYSCTL_MATRIX_CONF_B 0x110e +#define SLG51000_SYSCTL_REFGEN_CONF_C 0x1111 +#define SLG51000_SYSCTL_UVLO_CONF_A 0x1112 +#define SLG51000_SYSCTL_FAULT_LOG1 0x1115 +#define SLG51000_SYSCTL_EVENT 0x1116 +#define SLG51000_SYSCTL_STATUS 0x1117 +#define SLG51000_SYSCTL_IRQ_MASK 0x1118 +#define SLG51000_IO_GPIO1_CONF 0x1500 +#define SLG51000_IO_GPIO2_CONF 0x1501 +#define SLG51000_IO_GPIO3_CONF 0x1502 +#define SLG51000_IO_GPIO4_CONF 0x1503 +#define SLG51000_IO_GPIO5_CONF 0x1504 +#define SLG51000_IO_GPIO6_CONF 0x1505 +#define SLG51000_IO_GPIO_STATUS 0x1506 +#define SLG51000_LUTARRAY_LUT_VAL_0 0x1600 +#define SLG51000_LUTARRAY_LUT_VAL_1 0x1601 +#define SLG51000_LUTARRAY_LUT_VAL_2 0x1602 +#define SLG51000_LUTARRAY_LUT_VAL_3 0x1603 +#define SLG51000_LUTARRAY_LUT_VAL_4 0x1604 +#define SLG51000_LUTARRAY_LUT_VAL_5 0x1605 +#define SLG51000_LUTARRAY_LUT_VAL_6 0x1606 +#define SLG51000_LUTARRAY_LUT_VAL_7 0x1607 +#define SLG51000_LUTARRAY_LUT_VAL_8 0x1608 +#define SLG51000_LUTARRAY_LUT_VAL_9 0x1609 +#define SLG51000_LUTARRAY_LUT_VAL_10 0x160a +#define SLG51000_LUTARRAY_LUT_VAL_11 0x160b +#define SLG51000_MUXARRAY_INPUT_SEL_0 0x1700 +#define SLG51000_MUXARRAY_INPUT_SEL_1 0x1701 +#define SLG51000_MUXARRAY_INPUT_SEL_2 0x1702 +#define SLG51000_MUXARRAY_INPUT_SEL_3 0x1703 +#define SLG51000_MUXARRAY_INPUT_SEL_4 0x1704 +#define SLG51000_MUXARRAY_INPUT_SEL_5 0x1705 +#define SLG51000_MUXARRAY_INPUT_SEL_6 0x1706 +#define SLG51000_MUXARRAY_INPUT_SEL_7 0x1707 +#define SLG51000_MUXARRAY_INPUT_SEL_8 0x1708 +#define SLG51000_MUXARRAY_INPUT_SEL_9 0x1709 +#define SLG51000_MUXARRAY_INPUT_SEL_10 0x170a +#define SLG51000_MUXARRAY_INPUT_SEL_11 0x170b +#define SLG51000_MUXARRAY_INPUT_SEL_12 0x170c +#define SLG51000_MUXARRAY_INPUT_SEL_13 0x170d +#define SLG51000_MUXARRAY_INPUT_SEL_14 0x170e +#define SLG51000_MUXARRAY_INPUT_SEL_15 0x170f +#define SLG51000_MUXARRAY_INPUT_SEL_16 0x1710 +#define SLG51000_MUXARRAY_INPUT_SEL_17 0x1711 +#define SLG51000_MUXARRAY_INPUT_SEL_18 0x1712 +#define SLG51000_MUXARRAY_INPUT_SEL_19 0x1713 +#define SLG51000_MUXARRAY_INPUT_SEL_20 0x1714 +#define SLG51000_MUXARRAY_INPUT_SEL_21 0x1715 +#define SLG51000_MUXARRAY_INPUT_SEL_22 0x1716 +#define SLG51000_MUXARRAY_INPUT_SEL_23 0x1717 +#define SLG51000_MUXARRAY_INPUT_SEL_24 0x1718 +#define SLG51000_MUXARRAY_INPUT_SEL_25 0x1719 +#define SLG51000_MUXARRAY_INPUT_SEL_26 0x171a +#define SLG51000_MUXARRAY_INPUT_SEL_27 0x171b +#define SLG51000_MUXARRAY_INPUT_SEL_28 0x171c +#define SLG51000_MUXARRAY_INPUT_SEL_29 0x171d +#define SLG51000_MUXARRAY_INPUT_SEL_30 0x171e +#define SLG51000_MUXARRAY_INPUT_SEL_31 0x171f +#define SLG51000_MUXARRAY_INPUT_SEL_32 0x1720 +#define SLG51000_MUXARRAY_INPUT_SEL_33 0x1721 +#define SLG51000_MUXARRAY_INPUT_SEL_34 0x1722 +#define SLG51000_MUXARRAY_INPUT_SEL_35 0x1723 +#define SLG51000_MUXARRAY_INPUT_SEL_36 0x1724 +#define SLG51000_MUXARRAY_INPUT_SEL_37 0x1725 +#define SLG51000_MUXARRAY_INPUT_SEL_38 0x1726 +#define SLG51000_MUXARRAY_INPUT_SEL_39 0x1727 +#define SLG51000_MUXARRAY_INPUT_SEL_40 0x1728 +#define SLG51000_MUXARRAY_INPUT_SEL_41 0x1729 +#define SLG51000_MUXARRAY_INPUT_SEL_42 0x172a +#define SLG51000_MUXARRAY_INPUT_SEL_43 0x172b +#define SLG51000_MUXARRAY_INPUT_SEL_44 0x172c +#define SLG51000_MUXARRAY_INPUT_SEL_45 0x172d +#define SLG51000_MUXARRAY_INPUT_SEL_46 0x172e +#define SLG51000_MUXARRAY_INPUT_SEL_47 0x172f +#define SLG51000_MUXARRAY_INPUT_SEL_48 0x1730 +#define SLG51000_MUXARRAY_INPUT_SEL_49 0x1731 +#define SLG51000_MUXARRAY_INPUT_SEL_50 0x1732 +#define SLG51000_MUXARRAY_INPUT_SEL_51 0x1733 +#define SLG51000_MUXARRAY_INPUT_SEL_52 0x1734 +#define SLG51000_MUXARRAY_INPUT_SEL_53 0x1735 +#define SLG51000_MUXARRAY_INPUT_SEL_54 0x1736 +#define SLG51000_MUXARRAY_INPUT_SEL_55 0x1737 +#define SLG51000_MUXARRAY_INPUT_SEL_56 0x1738 +#define SLG51000_MUXARRAY_INPUT_SEL_57 0x1739 +#define SLG51000_MUXARRAY_INPUT_SEL_58 0x173a +#define SLG51000_MUXARRAY_INPUT_SEL_59 0x173b +#define SLG51000_MUXARRAY_INPUT_SEL_60 0x173c +#define SLG51000_MUXARRAY_INPUT_SEL_61 0x173d +#define SLG51000_MUXARRAY_INPUT_SEL_62 0x173e +#define SLG51000_MUXARRAY_INPUT_SEL_63 0x173f +#define SLG51000_PWRSEQ_RESOURCE_EN_0 0x1900 +#define SLG51000_PWRSEQ_RESOURCE_EN_1 0x1901 +#define SLG51000_PWRSEQ_RESOURCE_EN_2 0x1902 +#define SLG51000_PWRSEQ_RESOURCE_EN_3 0x1903 +#define SLG51000_PWRSEQ_RESOURCE_EN_4 0x1904 +#define SLG51000_PWRSEQ_RESOURCE_EN_5 0x1905 +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_UP0 0x1906 +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_DOWN0 0x1907 +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_UP1 0x1908 +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_DOWN1 0x1909 +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_UP2 0x190a +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_DOWN2 0x190b +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_UP3 0x190c +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_DOWN3 0x190d +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_UP4 0x190e +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_DOWN4 0x190f +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_UP5 0x1910 +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_DOWN5 0x1911 +#define SLG51000_PWRSEQ_SLOT_TIME_MAX_CONF_A 0x1912 +#define SLG51000_PWRSEQ_SLOT_TIME_MAX_CONF_B 0x1913 +#define SLG51000_PWRSEQ_SLOT_TIME_MAX_CONF_C 0x1914 +#define SLG51000_PWRSEQ_INPUT_SENSE_CONF_A 0x1915 +#define SLG51000_PWRSEQ_INPUT_SENSE_CONF_B 0x1916 +#define SLG51000_LDO1_VSEL 0x2000 +#define SLG51000_LDO1_MINV 0x2060 +#define SLG51000_LDO1_MAXV 0x2061 +#define SLG51000_LDO1_MISC1 0x2064 +#define SLG51000_LDO1_VSEL_ACTUAL 0x2065 +#define SLG51000_LDO1_EVENT 0x20c0 +#define SLG51000_LDO1_STATUS 0x20c1 +#define SLG51000_LDO1_IRQ_MASK 0x20c2 +#define SLG51000_LDO2_VSEL 0x2200 +#define SLG51000_LDO2_MINV 0x2260 +#define SLG51000_LDO2_MAXV 0x2261 +#define SLG51000_LDO2_MISC1 0x2264 +#define SLG51000_LDO2_VSEL_ACTUAL 0x2265 +#define SLG51000_LDO2_EVENT 0x22c0 +#define SLG51000_LDO2_STATUS 0x22c1 +#define SLG51000_LDO2_IRQ_MASK 0x22c2 +#define SLG51000_LDO3_VSEL 0x2300 +#define SLG51000_LDO3_MINV 0x2360 +#define SLG51000_LDO3_MAXV 0x2361 +#define SLG51000_LDO3_CONF1 0x2364 +#define SLG51000_LDO3_CONF2 0x2365 +#define SLG51000_LDO3_VSEL_ACTUAL 0x2366 +#define SLG51000_LDO3_EVENT 0x23c0 +#define SLG51000_LDO3_STATUS 0x23c1 +#define SLG51000_LDO3_IRQ_MASK 0x23c2 +#define SLG51000_LDO4_VSEL 0x2500 +#define SLG51000_LDO4_MINV 0x2560 +#define SLG51000_LDO4_MAXV 0x2561 +#define SLG51000_LDO4_CONF1 0x2564 +#define SLG51000_LDO4_CONF2 0x2565 +#define SLG51000_LDO4_VSEL_ACTUAL 0x2566 +#define SLG51000_LDO4_EVENT 0x25c0 +#define SLG51000_LDO4_STATUS 0x25c1 +#define SLG51000_LDO4_IRQ_MASK 0x25c2 +#define SLG51000_LDO5_VSEL 0x2700 +#define SLG51000_LDO5_MINV 0x2760 +#define SLG51000_LDO5_MAXV 0x2761 +#define SLG51000_LDO5_TRIM2 0x2763 +#define SLG51000_LDO5_CONF1 0x2765 +#define SLG51000_LDO5_CONF2 0x2766 +#define SLG51000_LDO5_VSEL_ACTUAL 0x2767 +#define SLG51000_LDO5_EVENT 0x27c0 +#define SLG51000_LDO5_STATUS 0x27c1 +#define SLG51000_LDO5_IRQ_MASK 0x27c2 +#define SLG51000_LDO6_VSEL 0x2900 +#define SLG51000_LDO6_MINV 0x2960 +#define SLG51000_LDO6_MAXV 0x2961 +#define SLG51000_LDO6_TRIM2 0x2963 +#define SLG51000_LDO6_CONF1 0x2965 +#define SLG51000_LDO6_CONF2 0x2966 +#define SLG51000_LDO6_VSEL_ACTUAL 0x2967 +#define SLG51000_LDO6_EVENT 0x29c0 +#define SLG51000_LDO6_STATUS 0x29c1 +#define SLG51000_LDO6_IRQ_MASK 0x29c2 +#define SLG51000_LDO7_VSEL 0x3100 +#define SLG51000_LDO7_MINV 0x3160 +#define SLG51000_LDO7_MAXV 0x3161 +#define SLG51000_LDO7_CONF1 0x3164 +#define SLG51000_LDO7_CONF2 0x3165 +#define SLG51000_LDO7_VSEL_ACTUAL 0x3166 +#define SLG51000_LDO7_EVENT 0x31c0 +#define SLG51000_LDO7_STATUS 0x31c1 +#define SLG51000_LDO7_IRQ_MASK 0x31c2 +#define SLG51000_OTP_EVENT 0x782b +#define SLG51000_OTP_IRQ_MASK 0x782d +#define SLG51000_OTP_LOCK_OTP_PROG 0x78fe +#define SLG51000_OTP_LOCK_CTRL 0x78ff +#define SLG51000_LOCK_GLOBAL_LOCK_CTRL1 0x8000 + +/* Register Bit Fields */ + +/* SLG51000_SYSCTL_PATTERN_ID_BYTE0 = 0x1105 */ +#define SLG51000_PATTERN_ID_BYTE0_SHIFT 0 +#define SLG51000_PATTERN_ID_BYTE0_MASK (0xff << 0) + +/* SLG51000_SYSCTL_PATTERN_ID_BYTE1 = 0x1106 */ +#define SLG51000_PATTERN_ID_BYTE1_SHIFT 0 +#define SLG51000_PATTERN_ID_BYTE1_MASK (0xff << 0) + +/* SLG51000_SYSCTL_PATTERN_ID_BYTE2 = 0x1107 */ +#define SLG51000_PATTERN_ID_BYTE2_SHIFT 0 +#define SLG51000_PATTERN_ID_BYTE2_MASK (0xff << 0) + +/* SLG51000_SYSCTL_SYS_CONF_A = 0x1109 */ +#define SLG51000_I2C_ADDRESS_SHIFT 0 +#define SLG51000_I2C_ADDRESS_MASK (0x7f << 0) +#define SLG51000_I2C_DISABLE_SHIFT 7 +#define SLG51000_I2C_DISABLE_MASK (0x01 << 7) + +/* SLG51000_SYSCTL_SYS_CONF_D = 0x110c */ +#define SLG51000_CS_T_DEB_SHIFT 6 +#define SLG51000_CS_T_DEB_MASK (0x03 << 6) +#define SLG51000_I2C_CLR_MODE_SHIFT 5 +#define SLG51000_I2C_CLR_MODE_MASK (0x01 << 5) + +/* SLG51000_SYSCTL_MATRIX_CTRL_CONF_A = 0x110d */ +#define SLG51000_RESOURCE_CTRL_SHIFT 0 +#define SLG51000_RESOURCE_CTRL_MASK (0xff << 0) + +/* SLG51000_SYSCTL_MATRIX_CTRL_CONF_B = 0x110e */ +#define SLG51000_MATRIX_EVENT_SENSE_SHIFT 0 +#define SLG51000_MATRIX_EVENT_SENSE_MASK (0x07 << 0) + +/* SLG51000_SYSCTL_REFGEN_CONF_C = 0x1111 */ +#define SLG51000_REFGEN_SEL_TEMP_WARN_DEBOUNCE_SHIFT 2 +#define SLG51000_REFGEN_SEL_TEMP_WARN_DEBOUNCE_MASK (0x03 << 2) +#define SLG51000_REFGEN_SEL_TEMP_WARN_THR_SHIFT 0 +#define SLG51000_REFGEN_SEL_TEMP_WARN_THR_MASK (0x03 << 0) + +/* SLG51000_SYSCTL_UVLO_CONF_A = 0x1112 */ +#define SLG51000_VMON_UVLO_SEL_THR_SHIFT 0 +#define SLG51000_VMON_UVLO_SEL_THR_MASK (0x1f << 0) + +/* SLG51000_SYSCTL_FAULT_LOG1 = 0x1115 */ +#define SLG51000_FLT_POR_SHIFT 5 +#define SLG51000_FLT_POR_MASK (0x01 << 5) +#define SLG51000_FLT_RST_SHIFT 4 +#define SLG51000_FLT_RST_MASK (0x01 << 4) +#define SLG51000_FLT_POWER_SEQ_CRASH_REQ_SHIFT 2 +#define SLG51000_FLT_POWER_SEQ_CRASH_REQ_MASK (0x01 << 2) +#define SLG51000_FLT_OVER_TEMP_SHIFT 1 +#define SLG51000_FLT_OVER_TEMP_MASK (0x01 << 1) + +/* SLG51000_SYSCTL_EVENT = 0x1116 */ +#define SLG51000_EVT_MATRIX_SHIFT 1 +#define SLG51000_EVT_MATRIX_MASK (0x01 << 1) +#define SLG51000_EVT_HIGH_TEMP_WARN_SHIFT 0 +#define SLG51000_EVT_HIGH_TEMP_WARN_MASK (0x01 << 0) + +/* SLG51000_SYSCTL_STATUS = 0x1117 */ +#define SLG51000_STA_MATRIX_SHIFT 1 +#define SLG51000_STA_MATRIX_MASK (0x01 << 1) +#define SLG51000_STA_HIGH_TEMP_WARN_SHIFT 0 +#define SLG51000_STA_HIGH_TEMP_WARN_MASK (0x01 << 0) + +/* SLG51000_SYSCTL_IRQ_MASK = 0x1118 */ +#define SLG51000_IRQ_MATRIX_SHIFT 1 +#define SLG51000_IRQ_MATRIX_MASK (0x01 << 1) +#define SLG51000_IRQ_HIGH_TEMP_WARN_SHIFT 0 +#define SLG51000_IRQ_HIGH_TEMP_WARN_MASK (0x01 << 0) + +/* SLG51000_IO_GPIO1_CONF ~ SLG51000_IO_GPIO5_CONF = + * 0x1500, 0x1501, 0x1502, 0x1503, 0x1504 + */ +#define SLG51000_GPIO_DIR_SHIFT 7 +#define SLG51000_GPIO_DIR_MASK (0x01 << 7) +#define SLG51000_GPIO_SENS_SHIFT 5 +#define SLG51000_GPIO_SENS_MASK (0x03 << 5) +#define SLG51000_GPIO_INVERT_SHIFT 4 +#define SLG51000_GPIO_INVERT_MASK (0x01 << 4) +#define SLG51000_GPIO_BYP_SHIFT 3 +#define SLG51000_GPIO_BYP_MASK (0x01 << 3) +#define SLG51000_GPIO_T_DEB_SHIFT 1 +#define SLG51000_GPIO_T_DEB_MASK (0x03 << 1) +#define SLG51000_GPIO_LEVEL_SHIFT 0 +#define SLG51000_GPIO_LEVEL_MASK (0x01 << 0) + +/* SLG51000_IO_GPIO6_CONF = 0x1505 */ +#define SLG51000_GPIO6_SENS_SHIFT 5 +#define SLG51000_GPIO6_SENS_MASK (0x03 << 5) +#define SLG51000_GPIO6_INVERT_SHIFT 4 +#define SLG51000_GPIO6_INVERT_MASK (0x01 << 4) +#define SLG51000_GPIO6_T_DEB_SHIFT 1 +#define SLG51000_GPIO6_T_DEB_MASK (0x03 << 1) +#define SLG51000_GPIO6_LEVEL_SHIFT 0 +#define SLG51000_GPIO6_LEVEL_MASK (0x01 << 0) + +/* SLG51000_IO_GPIO_STATUS = 0x1506 */ +#define SLG51000_GPIO6_STATUS_SHIFT 5 +#define SLG51000_GPIO6_STATUS_MASK (0x01 << 5) +#define SLG51000_GPIO5_STATUS_SHIFT 4 +#define SLG51000_GPIO5_STATUS_MASK (0x01 << 4) +#define SLG51000_GPIO4_STATUS_SHIFT 3 +#define SLG51000_GPIO4_STATUS_MASK (0x01 << 3) +#define SLG51000_GPIO3_STATUS_SHIFT 2 +#define SLG51000_GPIO3_STATUS_MASK (0x01 << 2) +#define SLG51000_GPIO2_STATUS_SHIFT 1 +#define SLG51000_GPIO2_STATUS_MASK (0x01 << 1) +#define SLG51000_GPIO1_STATUS_SHIFT 0 +#define SLG51000_GPIO1_STATUS_MASK (0x01 << 0) + +/* SLG51000_LUTARRAY_LUT_VAL_0 ~ SLG51000_LUTARRAY_LUT_VAL_11 + * 0x1600, 0x1601, 0x1602, 0x1603, 0x1604, 0x1605, + * 0x1606, 0x1607, 0x1608, 0x1609, 0x160a, 0x160b + */ +#define SLG51000_LUT_VAL_SHIFT 0 +#define SLG51000_LUT_VAL_MASK (0xff << 0) + +/* SLG51000_MUXARRAY_INPUT_SEL_0 ~ SLG51000_MUXARRAY_INPUT_SEL_63 + * 0x1700, 0x1701, 0x1702, 0x1703, 0x1704, 0x1705, + * 0x1706, 0x1707, 0x1708, 0x1709, 0x170a, 0x170b, + * 0x170c, 0x170d, 0x170e, 0x170f, 0x1710, 0x1711, + * 0x1712, 0x1713, 0x1714, 0x1715, 0x1716, 0x1717, + * 0x1718, 0x1719, 0x171a, 0x171b, 0x171c, 0x171d, + * 0x171e, 0x171f, 0x1720, 0x1721, 0x1722, 0x1723, + * 0x1724, 0x1725, 0x1726, 0x1727, 0x1728, 0x1729, + * 0x173a, 0x173b, 0x173c, 0x173d, 0x173e, 0x173f, + */ +#define SLG51000_INPUT_SEL_SHIFT 0 +#define SLG51000_INPUT_SEL_MASK (0x3f << 0) + +/* SLG51000_PWRSEQ_RESOURCE_EN_0 ~ SLG51000_PWRSEQ_RESOURCE_EN_5 + * 0x1900, 0x1901, 0x1902, 0x1903, 0x1904, 0x1905 + */ +#define SLG51000_RESOURCE_EN_DOWN0_SHIFT 4 +#define SLG51000_RESOURCE_EN_DOWN0_MASK (0x07 << 4) +#define SLG51000_RESOURCE_EN_UP0_SHIFT 0 +#define SLG51000_RESOURCE_EN_UP0_MASK (0x07 << 0) + +/* SLG51000_PWRSEQ_SLOT_TIME_MIN_UP0 ~ SLG51000_PWRSEQ_SLOT_TIME_MIN_UP5 + * 0x1906, 0x1908, 0x190a, 0x190c, 0x190e, 0x1910 + */ +#define SLG51000_SLOT_TIME_MIN_UP_SHIFT 0 +#define SLG51000_SLOT_TIME_MIN_UP_MASK (0xff << 0) + +/* SLG51000_PWRSEQ_SLOT_TIME_MIN_DOWN0 ~ SLG51000_PWRSEQ_SLOT_TIME_MIN_DOWN5 + * 0x1907, 0x1909, 0x190b, 0x190d, 0x190f, 0x1911 + */ +#define SLG51000_SLOT_TIME_MIN_DOWN_SHIFT 0 +#define SLG51000_SLOT_TIME_MIN_DOWN_MASK (0xff << 0) + +/* SLG51000_PWRSEQ_SLOT_TIME_MAX_CONF_A ~ SLG51000_PWRSEQ_SLOT_TIME_MAX_CONF_C + * 0x1912, 0x1913, 0x1914 + */ +#define SLG51000_SLOT_TIME_MAX_DOWN1_SHIFT 6 +#define SLG51000_SLOT_TIME_MAX_DOWN1_MASK (0x03 << 6) +#define SLG51000_SLOT_TIME_MAX_UP1_SHIFT 4 +#define SLG51000_SLOT_TIME_MAX_UP1_MASK (0x03 << 4) +#define SLG51000_SLOT_TIME_MAX_DOWN0_SHIFT 2 +#define SLG51000_SLOT_TIME_MAX_DOWN0_MASK (0x03 << 2) +#define SLG51000_SLOT_TIME_MAX_UP0_SHIFT 0 +#define SLG51000_SLOT_TIME_MAX_UP0_MASK (0x03 << 0) + +/* SLG51000_PWRSEQ_INPUT_SENSE_CONF_A = 0x1915 */ +#define SLG51000_TRIG_UP_SENSE_SHIFT 6 +#define SLG51000_TRIG_UP_SENSE_MASK (0x01 << 6) +#define SLG51000_UP_EN_SENSE5_SHIFT 5 +#define SLG51000_UP_EN_SENSE5_MASK (0x01 << 5) +#define SLG51000_UP_EN_SENSE4_SHIFT 4 +#define SLG51000_UP_EN_SENSE4_MASK (0x01 << 4) +#define SLG51000_UP_EN_SENSE3_SHIFT 3 +#define SLG51000_UP_EN_SENSE3_MASK (0x01 << 3) +#define SLG51000_UP_EN_SENSE2_SHIFT 2 +#define SLG51000_UP_EN_SENSE2_MASK (0x01 << 2) +#define SLG51000_UP_EN_SENSE1_SHIFT 1 +#define SLG51000_UP_EN_SENSE1_MASK (0x01 << 1) +#define SLG51000_UP_EN_SENSE0_SHIFT 0 +#define SLG51000_UP_EN_SENSE0_MASK (0x01 << 0) + +/* SLG51000_PWRSEQ_INPUT_SENSE_CONF_B = 0x1916 */ +#define SLG51000_CRASH_DETECT_SENSE_SHIFT 7 +#define SLG51000_CRASH_DETECT_SENSE_MASK (0x01 << 7) +#define SLG51000_TRIG_DOWN_SENSE_SHIFT 6 +#define SLG51000_TRIG_DOWN_SENSE_MASK (0x01 << 6) +#define SLG51000_DOWN_EN_SENSE5_SHIFT 5 +#define SLG51000_DOWN_EN_SENSE5_MASK (0x01 << 5) +#define SLG51000_DOWN_EN_SENSE4_SHIFT 4 +#define SLG51000_DOWN_EN_SENSE4_MASK (0x01 << 4) +#define SLG51000_DOWN_EN_SENSE3_SHIFT 3 +#define SLG51000_DOWN_EN_SENSE3_MASK (0x01 << 3) +#define SLG51000_DOWN_EN_SENSE2_SHIFT 2 +#define SLG51000_DOWN_EN_SENSE2_MASK (0x01 << 2) +#define SLG51000_DOWN_EN_SENSE1_SHIFT 1 +#define SLG51000_DOWN_EN_SENSE1_MASK (0x01 << 1) +#define SLG51000_DOWN_EN_SENSE0_SHIFT 0 +#define SLG51000_DOWN_EN_SENSE0_MASK (0x01 << 0) + +/* SLG51000_LDO1_VSEL ~ SLG51000_LDO7_VSEL = + * 0x2000, 0x2200, 0x2300, 0x2500, 0x2700, 0x2900, 0x3100 + */ +#define SLG51000_VSEL_SHIFT 0 +#define SLG51000_VSEL_MASK (0xff << 0) + +/* SLG51000_LDO1_MINV ~ SLG51000_LDO7_MINV = + * 0x2060, 0x2260, 0x2360, 0x2560, 0x2760, 0x2960, 0x3160 + */ +#define SLG51000_MINV_SHIFT 0 +#define SLG51000_MINV_MASK (0xff << 0) + +/* SLG51000_LDO1_MAXV ~ SLG51000_LDO7_MAXV = + * 0x2061, 0x2261, 0x2361, 0x2561, 0x2761, 0x2961, 0x3161 + */ +#define SLG51000_MAXV_SHIFT 0 +#define SLG51000_MAXV_MASK (0xff << 0) + +/* SLG51000_LDO1_MISC1 = 0x2064, SLG51000_LDO2_MISC1 = 0x2264 */ +#define SLG51000_SEL_VRANGE_SHIFT 0 +#define SLG51000_SEL_VRANGE_MASK (0x01 << 0) + +/* SLG51000_LDO1_VSEL_ACTUAL ~ SLG51000_LDO7_VSEL_ACTUAL = + * 0x2065, 0x2265, 0x2366, 0x2566, 0x2767, 0x2967, 0x3166 + */ +#define SLG51000_VSEL_ACTUAL_SHIFT 0 +#define SLG51000_VSEL_ACTUAL_MASK (0xff << 0) + +/* SLG51000_LDO1_EVENT ~ SLG51000_LDO7_EVENT = + * 0x20c0, 0x22c0, 0x23c0, 0x25c0, 0x27c0, 0x29c0, 0x31c0 + */ +#define SLG51000_EVT_ILIM_FLAG_SHIFT 0 +#define SLG51000_EVT_ILIM_FLAG_MASK (0x01 << 0) +#define SLG51000_EVT_VOUT_OK_FLAG_SHIFT 1 +#define SLG51000_EVT_VOUT_OK_FLAG_MASK (0x01 << 1) + +/* SLG51000_LDO1_STATUS ~ SLG51000_LDO7_STATUS = + * 0x20c1, 0x22c1, 0x23c1, 0x25c1, 0x27c1, 0x29c1, 0x31c1 + */ +#define SLG51000_STA_ILIM_FLAG_SHIFT 0 +#define SLG51000_STA_ILIM_FLAG_MASK (0x01 << 0) +#define SLG51000_STA_VOUT_OK_FLAG_SHIFT 1 +#define SLG51000_STA_VOUT_OK_FLAG_MASK (0x01 << 1) + +/* SLG51000_LDO1_IRQ_MASK ~ SLG51000_LDO7_IRQ_MASK = + * 0x20c2, 0x22c2, 0x23c2, 0x25c2, 0x27c2, 0x29c2, 0x31c2 + */ +#define SLG51000_IRQ_ILIM_FLAG_SHIFT 0 +#define SLG51000_IRQ_ILIM_FLAG_MASK (0x01 << 0) + +/* SLG51000_LDO3_CONF1 ~ SLG51000_LDO7_CONF1 = + * 0x2364, 0x2564, 0x2765, 0x2965, 0x3164 + */ +#define SLG51000_SEL_START_ILIM_SHIFT 0 +#define SLG51000_SEL_START_ILIM_MASK (0x7f << 0) + +/* SLG51000_LDO3_CONF2 ~ SLG51000_LDO7_CONF2 = + * 0x2365, 0x2565, 0x2766, 0x2966, 0x3165 + */ +#define SLG51000_SEL_FUNC_ILIM_SHIFT 0 +#define SLG51000_SEL_FUNC_ILIM_MASK (0x7f << 0) + +/* SLG51000_LDO5_TRIM2 = 0x2763, SLG51000_LDO6_TRIM2 = 0x2963 */ +#define SLG51000_SEL_BYP_SLEW_RATE_SHIFT 2 +#define SLG51000_SEL_BYP_SLEW_RATE_MASK (0x03 << 2) +#define SLG51000_SEL_BYP_VGATE_SHIFT 1 +#define SLG51000_SEL_BYP_VGATE_MASK (0x01 << 1) +#define SLG51000_SEL_BYP_MODE_SHIFT 0 +#define SLG51000_SEL_BYP_MODE_MASK (0x01 << 0) + +/* SLG51000_OTP_EVENT = 0x782b */ +#define SLG51000_EVT_CRC_SHIFT 0 +#define SLG51000_EVT_CRC_MASK (0x01 << 0) + +/* SLG51000_OTP_IRQ_MASK = 0x782d */ +#define SLG51000_IRQ_CRC_SHIFT 0 +#define SLG51000_IRQ_CRC_MASK (0x01 << 0) + +/* SLG51000_OTP_LOCK_OTP_PROG = 0x78fe */ +#define SLG51000_LOCK_OTP_PROG_SHIFT 0 +#define SLG51000_LOCK_OTP_PROG_MASK (0x01 << 0) + +/* SLG51000_OTP_LOCK_CTRL = 0x78ff */ +#define SLG51000_LOCK_DFT_SHIFT 1 +#define SLG51000_LOCK_DFT_MASK (0x01 << 1) +#define SLG51000_LOCK_RWT_SHIFT 0 +#define SLG51000_LOCK_RWT_MASK (0x01 << 0) + +/* SLG51000_LOCK_GLOBAL_LOCK_CTRL1 = 0x8000 */ +#define SLG51000_LDO7_LOCK_SHIFT 7 +#define SLG51000_LDO7_LOCK_MASK (0x01 << 7) +#define SLG51000_LDO6_LOCK_SHIFT 6 +#define SLG51000_LDO6_LOCK_MASK (0x01 << 6) +#define SLG51000_LDO5_LOCK_SHIFT 5 +#define SLG51000_LDO5_LOCK_MASK (0x01 << 5) +#define SLG51000_LDO4_LOCK_SHIFT 4 +#define SLG51000_LDO4_LOCK_MASK (0x01 << 4) +#define SLG51000_LDO3_LOCK_SHIFT 3 +#define SLG51000_LDO3_LOCK_MASK (0x01 << 3) +#define SLG51000_LDO2_LOCK_SHIFT 2 +#define SLG51000_LDO2_LOCK_MASK (0x01 << 2) +#define SLG51000_LDO1_LOCK_SHIFT 1 +#define SLG51000_LDO1_LOCK_MASK (0x01 << 1) + +#endif /* __SLG51000_REGISTERS_H__ */ + diff --git a/drivers/regulator/slg5bm43670/Android.mk b/drivers/regulator/slg5bm43670/Android.mk new file mode 100644 index 000000000000..9b1d200b3e74 --- /dev/null +++ b/drivers/regulator/slg5bm43670/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := slg5bm43670.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/regulator/slg5bm43670/Kbuild b/drivers/regulator/slg5bm43670/Kbuild new file mode 100644 index 000000000000..e258ce77cbc6 --- /dev/null +++ b/drivers/regulator/slg5bm43670/Kbuild @@ -0,0 +1,5 @@ +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +slg5bm43670-objs += slg5bm43670-regulator.o +obj-m += slg5bm43670.o diff --git a/drivers/regulator/slg5bm43670/Makefile b/drivers/regulator/slg5bm43670/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/regulator/slg5bm43670/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/regulator/slg5bm43670/slg5bm43670-regulator.c b/drivers/regulator/slg5bm43670/slg5bm43670-regulator.c new file mode 100644 index 000000000000..ae717269a139 --- /dev/null +++ b/drivers/regulator/slg5bm43670/slg5bm43670-regulator.c @@ -0,0 +1,610 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * SLG51000 High PSRR, Multi-Output Regulators + * Copyright (C) 2020 Dialog Semiconductor + * + * Author: Eric Jeong, Dialog Semiconductor + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "slg5bm43670-regulator.h" + +#define SLG51000_SCTL_EVT 7 +#define SLG51000_MAX_EVT_REGISTER 8 + +enum slg51000_regulators { + SLG51000_REGULATOR_LDO1 = 0, + SLG51000_REGULATOR_LDO2, + SLG51000_REGULATOR_LDO3, + SLG51000_REGULATOR_LDO4, + SLG51000_REGULATOR_LDO5, + SLG51000_REGULATOR_LDO6, + SLG51000_REGULATOR_LDO7, + SLG51000_MAX_REGULATORS, +}; + +struct slg51000 { + struct device *dev; + struct regmap *regmap; + struct regulator_desc *rdesc[SLG51000_MAX_REGULATORS]; + struct regulator_dev *rdev[SLG51000_MAX_REGULATORS]; + int chip_irq; + int chip_cs_pin; + struct pinctrl *pinctrl; + struct pinctrl_state *gpio_state_default; +}; + +struct slg51000_evt_sta { + unsigned int ereg; + unsigned int sreg; +}; + +static const struct slg51000_evt_sta es_reg[SLG51000_MAX_EVT_REGISTER] = { + {SLG51000_LDO1_EVENT, SLG51000_LDO1_STATUS}, + {SLG51000_LDO2_EVENT, SLG51000_LDO2_STATUS}, + {SLG51000_LDO3_EVENT, SLG51000_LDO3_STATUS}, + {SLG51000_LDO4_EVENT, SLG51000_LDO4_STATUS}, + {SLG51000_LDO5_EVENT, SLG51000_LDO5_STATUS}, + {SLG51000_LDO6_EVENT, SLG51000_LDO6_STATUS}, + {SLG51000_LDO7_EVENT, SLG51000_LDO7_STATUS}, + {SLG51000_SYSCTL_EVENT, SLG51000_SYSCTL_STATUS}, +}; + +static const struct regmap_range slg51000_writeable_ranges[] = { + regmap_reg_range(SLG51000_SYSCTL_MATRIX_CONF_A, + SLG51000_SYSCTL_MATRIX_CONF_A), + regmap_reg_range(0x1119, 0x111D), + regmap_reg_range(SLG51000_IO_GPIO1_CONF, SLG51000_IO_GPIO1_CONF), + regmap_reg_range(SLG51000_MUXARRAY_INPUT_SEL_16, SLG51000_MUXARRAY_INPUT_SEL_16), + regmap_reg_range(SLG51000_LDO1_VSEL, SLG51000_LDO1_VSEL), + regmap_reg_range(SLG51000_LDO1_VSEL_RANGE_MASK_MIN, + SLG51000_LDO1_VSEL_RANGE_MASK_MAX), + regmap_reg_range(SLG51000_LDO1_IRQ_MASK, SLG51000_LDO1_IRQ_MASK), + regmap_reg_range(SLG51000_LDO2_VSEL, SLG51000_LDO2_VSEL), + regmap_reg_range(SLG51000_LDO2_VSEL_RANGE_MASK_MIN, + SLG51000_LDO2_VSEL_RANGE_MASK_MAX), + regmap_reg_range(SLG51000_LDO2_IRQ_MASK, SLG51000_LDO2_IRQ_MASK), + regmap_reg_range(SLG51000_LDO3_VSEL, SLG51000_LDO3_VSEL), + regmap_reg_range(SLG51000_LDO3_VSEL_RANGE_MASK_MIN, + SLG51000_LDO3_VSEL_RANGE_MASK_MAX), + regmap_reg_range(SLG51000_LDO3_IRQ_MASK, SLG51000_LDO3_IRQ_MASK), + regmap_reg_range(SLG51000_LDO4_VSEL, SLG51000_LDO4_VSEL), + regmap_reg_range(SLG51000_LDO4_VSEL_RANGE_MASK_MIN, + SLG51000_LDO4_VSEL_RANGE_MASK_MAX), + regmap_reg_range(SLG51000_LDO4_IRQ_MASK, SLG51000_LDO4_IRQ_MASK), + regmap_reg_range(SLG51000_LDO5_VSEL, SLG51000_LDO5_VSEL), + regmap_reg_range(SLG51000_LDO5_VSEL_RANGE_MASK_MIN, + SLG51000_LDO5_VSEL_RANGE_MASK_MAX), + regmap_reg_range(SLG51000_LDO5_IRQ_MASK, SLG51000_LDO5_IRQ_MASK), + regmap_reg_range(SLG51000_LDO6_VSEL, SLG51000_LDO6_VSEL), + regmap_reg_range(SLG51000_LDO6_VSEL_RANGE_MASK_MIN, + SLG51000_LDO6_VSEL_RANGE_MASK_MAX), + regmap_reg_range(SLG51000_LDO6_IRQ_MASK, SLG51000_LDO6_IRQ_MASK), + regmap_reg_range(SLG51000_LDO7_VSEL, SLG51000_LDO7_VSEL), + regmap_reg_range(SLG51000_LDO7_VSEL_RANGE_MASK_MIN, + SLG51000_LDO7_VSEL_RANGE_MASK_MAX), + regmap_reg_range(SLG51000_LDO7_IRQ_MASK, SLG51000_LDO7_IRQ_MASK), +}; + +static const struct regmap_range slg51000_readable_ranges[] = { + regmap_reg_range(SLG51000_SYSCTL_PATN_ID_B0, + SLG51000_SYSCTL_PATN_ID_B2), + regmap_reg_range(SLG51000_SYSCTL_SYS_CONF_A, + SLG51000_SYSCTL_SYS_CONF_A), + regmap_reg_range(SLG51000_SYSCTL_SYS_CONF_D, + SLG51000_SYSCTL_MATRIX_CONF_B), + regmap_reg_range(SLG51000_SYSCTL_REFGEN_CONF_C, + SLG51000_SYSCTL_UVLO_CONF_A), + regmap_reg_range(SLG51000_SYSCTL_FAULT_LOG1, SLG51000_SYSCTL_IRQ_MASK), + regmap_reg_range(0x1119, 0x111D), + regmap_reg_range(SLG51000_IO_GPIO1_CONF, SLG51000_IO_GPIO_STATUS), + regmap_reg_range(SLG51000_LUTARRAY_LUT_VAL_0, + SLG51000_LUTARRAY_LUT_VAL_11), + regmap_reg_range(SLG51000_MUXARRAY_INPUT_SEL_0, + SLG51000_MUXARRAY_INPUT_SEL_63), + regmap_reg_range(SLG51000_PWRSEQ_RESOURCE_EN_0, + SLG51000_PWRSEQ_INPUT_SENSE_CONF_B), + regmap_reg_range(SLG51000_LDO1_VSEL, SLG51000_LDO1_VSEL), + regmap_reg_range(SLG51000_LDO1_VSEL_RANGE_MASK_MIN, + SLG51000_LDO1_VSEL_RANGE_MASK_MAX), + regmap_reg_range(SLG51000_LDO1_TRIM2, SLG51000_LDO1_TRIM2), + regmap_reg_range(SLG51000_LDO1_VSEL_ACTUAL, SLG51000_LDO1_VSEL_ACTUAL), + regmap_reg_range(SLG51000_LDO1_EVENT, SLG51000_LDO1_IRQ_MASK), + regmap_reg_range(SLG51000_LDO2_VSEL, SLG51000_LDO2_VSEL), + regmap_reg_range(SLG51000_LDO2_VSEL_RANGE_MASK_MIN, + SLG51000_LDO2_VSEL_RANGE_MASK_MAX), + regmap_reg_range(SLG51000_LDO2_TRIM2, SLG51000_LDO2_TRIM2), + regmap_reg_range(SLG51000_LDO2_VSEL_ACTUAL, SLG51000_LDO2_VSEL_ACTUAL), + regmap_reg_range(SLG51000_LDO2_EVENT, SLG51000_LDO2_IRQ_MASK), + regmap_reg_range(SLG51000_LDO3_VSEL, SLG51000_LDO3_VSEL), + regmap_reg_range(SLG51000_LDO3_VSEL_RANGE_MASK_MIN, + SLG51000_LDO3_VSEL_RANGE_MASK_MAX), + regmap_reg_range(SLG51000_LDO3_TRIM2, SLG51000_LDO3_VSEL_ACTUAL), + regmap_reg_range(SLG51000_LDO3_EVENT, SLG51000_LDO3_IRQ_MASK), + regmap_reg_range(SLG51000_LDO4_VSEL, SLG51000_LDO4_VSEL), + regmap_reg_range(SLG51000_LDO4_VSEL_RANGE_MASK_MIN, + SLG51000_LDO4_VSEL_RANGE_MASK_MAX), + regmap_reg_range(SLG51000_LDO4_TRIM2, SLG51000_LDO4_VSEL_ACTUAL), + regmap_reg_range(SLG51000_LDO4_EVENT, SLG51000_LDO4_IRQ_MASK), + regmap_reg_range(SLG51000_LDO5_VSEL, SLG51000_LDO5_VSEL), + regmap_reg_range(SLG51000_LDO5_VSEL_RANGE_MASK_MIN, + SLG51000_LDO5_VSEL_RANGE_MASK_MAX), + regmap_reg_range(SLG51000_LDO5_TRIM2, SLG51000_LDO5_VSEL_ACTUAL), + regmap_reg_range(SLG51000_LDO5_EVENT, SLG51000_LDO5_IRQ_MASK), + regmap_reg_range(SLG51000_LDO6_VSEL, SLG51000_LDO6_VSEL), + regmap_reg_range(SLG51000_LDO6_VSEL_RANGE_MASK_MIN, + SLG51000_LDO6_VSEL_RANGE_MASK_MAX), + regmap_reg_range(SLG51000_LDO6_TRIM2, SLG51000_LDO6_VSEL_ACTUAL), + regmap_reg_range(SLG51000_LDO6_EVENT, SLG51000_LDO6_IRQ_MASK), + regmap_reg_range(SLG51000_LDO7_VSEL, SLG51000_LDO7_VSEL), + regmap_reg_range(SLG51000_LDO7_VSEL_RANGE_MASK_MIN, + SLG51000_LDO7_VSEL_RANGE_MASK_MAX), + regmap_reg_range(SLG51000_LDO7_TRIM2, SLG51000_LDO7_VSEL_ACTUAL), + regmap_reg_range(SLG51000_LDO7_EVENT, SLG51000_LDO7_IRQ_MASK), + regmap_reg_range(SLG51000_LOCK_GLOBAL_LOCK_CTRL1, + SLG51000_LOCK_GLOBAL_LOCK_CTRL1), +}; + +static const struct regmap_range slg51000_volatile_ranges[] = { + regmap_reg_range(SLG51000_SYSCTL_FAULT_LOG1, SLG51000_SYSCTL_STATUS), + regmap_reg_range(SLG51000_IO_GPIO_STATUS, SLG51000_IO_GPIO_STATUS), + regmap_reg_range(SLG51000_LDO1_EVENT, SLG51000_LDO1_STATUS), + regmap_reg_range(SLG51000_LDO2_EVENT, SLG51000_LDO2_STATUS), + regmap_reg_range(SLG51000_LDO3_EVENT, SLG51000_LDO3_STATUS), + regmap_reg_range(SLG51000_LDO4_EVENT, SLG51000_LDO4_STATUS), + regmap_reg_range(SLG51000_LDO5_EVENT, SLG51000_LDO5_STATUS), + regmap_reg_range(SLG51000_LDO6_EVENT, SLG51000_LDO6_STATUS), + regmap_reg_range(SLG51000_LDO7_EVENT, SLG51000_LDO7_STATUS), +}; + +static const struct regmap_access_table slg51000_writeable_table = { + .yes_ranges = slg51000_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(slg51000_writeable_ranges), +}; + +static const struct regmap_access_table slg51000_readable_table = { + .yes_ranges = slg51000_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(slg51000_readable_ranges), +}; + +static const struct regmap_access_table slg51000_volatile_table = { + .yes_ranges = slg51000_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(slg51000_volatile_ranges), +}; + +static const struct regmap_config slg51000_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .max_register = 0x8000, + .wr_table = &slg51000_writeable_table, + .rd_table = &slg51000_readable_table, + .volatile_table = &slg51000_volatile_table, +}; + +static const struct regulator_ops slg51000_regl_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, +}; + +static const struct regulator_ops slg51000_switch_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static int slg51000_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + int ena_gpio; + + ena_gpio = of_get_named_gpio(np, "enable-gpios", 0); + if (gpio_is_valid(ena_gpio)) + config->ena_gpiod = gpio_to_desc(ena_gpio); + + return 0; +} + +#define SLG51000_REGL_DESC(_id, _name, _s_name, _min, _step) \ + [SLG51000_REGULATOR_##_id] = { \ + .name = #_name, \ + .supply_name = _s_name, \ + .id = SLG51000_REGULATOR_##_id, \ + .of_match = of_match_ptr(#_name), \ + .of_parse_cb = slg51000_of_parse_cb, \ + .ops = &slg51000_regl_ops, \ + .regulators_node = of_match_ptr("regulators"), \ + .n_voltages = 256, \ + .min_uV = _min, \ + .uV_step = _step, \ + .linear_min_sel = 0, \ + .vsel_mask = SLG51000_VSEL_MASK, \ + .vsel_reg = SLG51000_##_id##_VSEL, \ + .enable_reg = SLG51000_SYSCTL_MATRIX_CONF_A, \ + .enable_mask = BIT(SLG51000_REGULATOR_##_id), \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + } + +static struct regulator_desc regls_desc[SLG51000_MAX_REGULATORS] = { + SLG51000_REGL_DESC(LDO1, ldo1, NULL, 2200000, 5000), + SLG51000_REGL_DESC(LDO2, ldo2, NULL, 2200000, 5000), + SLG51000_REGL_DESC(LDO3, ldo3, "vin3", 1200000, 10000), + SLG51000_REGL_DESC(LDO4, ldo4, "vin4", 1200000, 10000), + SLG51000_REGL_DESC(LDO5, ldo5, "vin5", 400000, 5000), + SLG51000_REGL_DESC(LDO6, ldo6, "vin6", 400000, 5000), + SLG51000_REGL_DESC(LDO7, ldo7, "vin7", 1200000, 10000), +}; + +//======================================= +// Added by Aaron Wang on Dec 27th 2019 +// Call this function to upgrade the firmware +// to the revision. +// CS must be high and wait 10ms +// Just a reference, need to fully re program this part. +//======================================= +static int upgrade_firmware(struct slg51000 *chip) +{ + unsigned int val; + + if (regmap_write(chip->regmap, 0x111A, 0x45) < 0) + dev_err(chip->dev, "regmap_write val 0x45 to reg 0x111A fail..."); + + if (regmap_write(chip->regmap, 0x111B, 0x53) < 0) + dev_err(chip->dev, "regmap_write val 0x53 to reg 0x111B fail..."); + + if (regmap_write(chip->regmap, 0x111C, 0x54) < 0) + dev_err(chip->dev, "regmap_write val 0x54 to reg 0x111C fail..."); + + if (regmap_write(chip->regmap, 0x111D, 0x4D) < 0) + dev_err(chip->dev, "regmap_write val 0x4D to reg 0x111D fail..."); + + /* If the 3rd bit is set to 1, then SLG51000 device is in test mode. */ + if (regmap_read(chip->regmap, 0x1119, &val) < 0) + dev_err(chip->dev, "regmap_read reg 0x1119 fail..."); + + dev_info(chip->dev, "Is test mode: (0x%X)%s\n", val, (val & 0x4) ? "TRUE" : "FALSE"); + + if (regmap_read(chip->regmap, SLG51000_IO_GPIO1_CONF, &val) < 0) + dev_err(chip->dev, "regmap_read reg SLG51000_IO_GPIO1_CONF fail..."); + + dev_info(chip->dev, "GPIO1 Value: 0x%X\n", val); + + val |= (SLG51000_GPIO_DIR_MASK | SLG51000_GPIO_BYP_MASK); + if (regmap_write(chip->regmap, SLG51000_IO_GPIO1_CONF, val) < 0) + dev_err(chip->dev, "regmap_write val 0x%x to reg SLG51000_IO_GPIO1_CONF fail...", val); + + /* To make sure it has been written successfully */ + if (regmap_read(chip->regmap, SLG51000_IO_GPIO1_CONF, &val) < 0) + dev_err(chip->dev, "regmap_read reg 0x1500 fail..."); + + dev_info(chip->dev, "GPIO1 Config value: 0x%X\n", val); + + if (regmap_write(chip->regmap, SLG51000_MUXARRAY_INPUT_SEL_16, 0x2A) < 0) + dev_err(chip->dev, "regmap_write val 0x%x to reg SLG51000_MUXARRAY_INPUT_SEL_16 fail...", val); + + if (regmap_read(chip->regmap, SLG51000_MUXARRAY_INPUT_SEL_16, &val) < 0) + dev_err(chip->dev, "regmap_read reg SLG51000_MUXARRAY_INPUT_SEL_16 fail..."); + + dev_info(chip->dev, "MUXARRAY_INPUT_SEL_16: 0x%X\n", val); + + if (regmap_write(chip->regmap, 0x1119, 0x0) < 0) + dev_err(chip->dev, "regmap_write val 0x0 to reg 0x1119 fail..."); + + if (regmap_read(chip->regmap, 0x1119, &val) < 0) + dev_err(chip->dev, "regmap_read reg 0x1119 fail..."); + + if (val) { + if (regmap_write(chip->regmap, 0x1119, 0x00) < 0) + dev_err(chip->dev, "regmap_write val 0x00 to reg 0x1119 fail..."); + } + + return 1; // always return 1. But should add false or true return later. +} + +static int slg51000_regulator_init(struct slg51000 *chip) +{ + struct regulator_config config = { }; + struct regulator_desc *rdesc; + unsigned int reg, val; + u8 vsel_range[2]; + int id, ret = 0; + const unsigned int min_regs[SLG51000_MAX_REGULATORS] = { + SLG51000_LDO1_VSEL_RANGE_MASK_MIN, + SLG51000_LDO2_VSEL_RANGE_MASK_MIN, + SLG51000_LDO3_VSEL_RANGE_MASK_MIN, + SLG51000_LDO4_VSEL_RANGE_MASK_MIN, + SLG51000_LDO5_VSEL_RANGE_MASK_MIN, + SLG51000_LDO6_VSEL_RANGE_MASK_MIN, + SLG51000_LDO7_VSEL_RANGE_MASK_MIN, + }; + + upgrade_firmware(chip); // upgrade the firmware during initial + + for (id = 0; id < SLG51000_MAX_REGULATORS; id++) { + chip->rdesc[id] = ®ls_desc[id]; + rdesc = chip->rdesc[id]; + config.regmap = chip->regmap; + config.dev = chip->dev; + config.driver_data = chip; + + ret = regmap_bulk_read(chip->regmap, min_regs[id], + vsel_range, 2); + if (ret < 0) { + dev_err(chip->dev, + "Failed to read the MIN register\n"); + return ret; + } + + switch (id) { + case SLG51000_REGULATOR_LDO5: + case SLG51000_REGULATOR_LDO6: + if (id == SLG51000_REGULATOR_LDO5) + reg = SLG51000_LDO5_TRIM2; + else + reg = SLG51000_LDO6_TRIM2; + + ret = regmap_read(chip->regmap, reg, &val); + if (ret < 0) { + dev_err(chip->dev, + "Failed to read LDO mode register\n"); + return ret; + } + + if (val & SLG51000_SEL_BYP_MODE_MASK) { + rdesc->ops = &slg51000_switch_ops; + rdesc->n_voltages = 0; + rdesc->min_uV = 0; + rdesc->uV_step = 0; + rdesc->linear_min_sel = 0; + break; + } + /* Fall through - to the check below.*/ + + default: + rdesc->linear_min_sel = vsel_range[0]; + rdesc->n_voltages = vsel_range[1] + 1; + rdesc->min_uV = rdesc->min_uV + + (vsel_range[0] * rdesc->uV_step); + break; + } + + chip->rdev[id] = devm_regulator_register(chip->dev, rdesc, + &config); + if (IS_ERR(chip->rdev[id])) { + ret = PTR_ERR(chip->rdev[id]); + dev_err(chip->dev, + "Failed to register regulator(%s):%d\n", + chip->rdesc[id]->name, ret); + return ret; + } + } + + return 0; +} + +static irqreturn_t slg51000_irq_handler(int irq, void *data) +{ + struct slg51000 *chip = data; + struct regmap *regmap = chip->regmap; + enum { R0 = 0, R1, R2, REG_MAX }; + u8 evt[SLG51000_MAX_EVT_REGISTER][REG_MAX]; + int ret, i, handled = IRQ_NONE; + + /* Read event[R0], status[R1] and mask[R2] register */ + for (i = 0; i < SLG51000_MAX_EVT_REGISTER; i++) { + ret = regmap_bulk_read(regmap, es_reg[i].ereg, evt[i], REG_MAX); + if (ret < 0) { + dev_err(chip->dev, + "Failed to read event registers(%d)\n", ret); + return IRQ_NONE; + } + } + + for (i = 0; i < SLG51000_MAX_REGULATORS; i++) { + if (!(evt[i][R2] & SLG51000_IRQ_ILIM_FLAG_MASK) && + (evt[i][R0] & SLG51000_EVT_ILIM_FLAG_MASK)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0) && LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0) + regulator_lock(chip->rdev[i]); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0) + mutex_lock(&chip->rdev[i]->mutex); +#endif + regulator_notifier_call_chain(chip->rdev[i], + REGULATOR_EVENT_OVER_CURRENT, + NULL); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0) && LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0) + regulator_unlock(chip->rdev[i]); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0) + mutex_unlock(&chip->rdev[i]->mutex); +#endif + if (evt[i][R1] & SLG51000_STA_ILIM_FLAG_MASK) + dev_warn(chip->dev, + "Over-current limit(ldo%d)\n", i + 1); + handled = IRQ_HANDLED; + } + } + + if (!(evt[SLG51000_SCTL_EVT][R2] & SLG51000_IRQ_HIGH_TEMP_WARN_MASK) && + (evt[SLG51000_SCTL_EVT][R0] & SLG51000_EVT_HIGH_TEMP_WARN_MASK)) { + for (i = 0; i < SLG51000_MAX_REGULATORS; i++) { + if (!(evt[i][R1] & SLG51000_STA_ILIM_FLAG_MASK) && + (evt[i][R1] & SLG51000_STA_VOUT_OK_FLAG_MASK)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0) && LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0) + regulator_lock(chip->rdev[i]); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0) + mutex_lock(&chip->rdev[i]->mutex); +#endif + regulator_notifier_call_chain(chip->rdev[i], + REGULATOR_EVENT_OVER_TEMP, + NULL); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0) && LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0) + regulator_unlock(chip->rdev[i]); +#elif LINUX_VERSION_CODE < KERNEL_VERSION(5,10,0) + mutex_unlock(&chip->rdev[i]->mutex); +#endif + } + } + handled = IRQ_HANDLED; + if (evt[SLG51000_SCTL_EVT][R1] & + SLG51000_STA_HIGH_TEMP_WARN_MASK) + dev_warn(chip->dev, "High temperature warning!\n"); + } + + return handled; +} + +static void slg51000_clear_fault_log(struct slg51000 *chip) +{ + unsigned int val = 0; + int ret = 0; + + ret = regmap_read(chip->regmap, SLG51000_SYSCTL_FAULT_LOG1, &val); + if (ret < 0) { + dev_err(chip->dev, "Failed to read Fault log register\n"); + return; + } + + dev_info(chip->dev, "SLG51000_SYSCTL_FAULT_LOG1(0x1115)'s val = 0x%X\n", val); + + if (val & SLG51000_FLT_OVER_TEMP_MASK) + dev_dbg(chip->dev, "Fault log: FLT_OVER_TEMP\n"); + if (val & SLG51000_FLT_POWER_SEQ_CRASH_REQ_MASK) + dev_dbg(chip->dev, "Fault log: FLT_POWER_SEQ_CRASH_REQ\n"); + if (val & SLG51000_FLT_RST_MASK) + dev_dbg(chip->dev, "Fault log: FLT_RST\n"); + if (val & SLG51000_FLT_POR_MASK) + dev_dbg(chip->dev, "Fault log: FLT_POR\n"); +} + +static int slg51000_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct slg51000 *chip; + int error, cs_gpio, ret; + + chip = devm_kzalloc(dev, sizeof(struct slg51000), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + cs_gpio = of_get_named_gpio(dev->of_node, "dlg,cs-gpios", 0); + if (cs_gpio > 0) { + if (!gpio_is_valid(cs_gpio)) { + dev_err(dev, "Invalid chip select pin\n"); + return -EPERM; + } + + ret = devm_gpio_request_one(dev, cs_gpio, GPIOF_OUT_INIT_HIGH, + "slg51000_cs_pin"); + if (ret) { + dev_err(dev, "GPIO(%d) request failed(%d)\n", + cs_gpio, ret); + return ret; + } + + chip->chip_cs_pin = cs_gpio; + } + + chip->pinctrl = devm_pinctrl_get(dev); + if (!IS_ERR_OR_NULL(chip->pinctrl)) { + chip->gpio_state_default = pinctrl_lookup_state(chip->pinctrl, "pmic_default"); + } else + dev_err(dev, "Getting pinctrl handle failed\n"); + + if (!IS_ERR_OR_NULL(chip->gpio_state_default)) { + pinctrl_select_state(chip->pinctrl, chip->gpio_state_default); + dev_err(dev, "Setting pinctrl handle successfully\n"); + } + + mdelay(10); //must delay 10ms, wait camera pmic stable. + + i2c_set_clientdata(client, chip); + chip->chip_irq = client->irq; + chip->dev = dev; + chip->regmap = devm_regmap_init_i2c(client, &slg51000_regmap_config); + if (IS_ERR(chip->regmap)) { + error = PTR_ERR(chip->regmap); + dev_err(dev, "Failed to allocate register map: %d\n", + error); + return error; + } + + ret = slg51000_regulator_init(chip); + if (ret < 0) { + dev_err(chip->dev, "Failed to init regulator(%d)\n", ret); + return ret; + } + + slg51000_clear_fault_log(chip); + + if (chip->chip_irq) { + ret = devm_request_threaded_irq(dev, chip->chip_irq, NULL, + slg51000_irq_handler, + (IRQF_TRIGGER_LOW | + IRQF_ONESHOT), + "slg51000-irq", chip); + if (ret != 0) { + dev_err(dev, "Failed to request IRQ: %d\n", + chip->chip_irq); + return ret; + } + } else { + dev_warn(dev, "No IRQ configured\n"); + } + + return ret; +} + +static int slg51000_i2c_remove(struct i2c_client *client) +{ + struct slg51000 *chip = i2c_get_clientdata(client); + struct gpio_desc *desc; + int ret = 0; + + if (chip->chip_cs_pin > 0) { + desc = gpio_to_desc(chip->chip_cs_pin); + ret = gpiod_direction_output_raw(desc, GPIOF_INIT_LOW); + } + + return ret; +} + +static const struct i2c_device_id slg51000_i2c_id[] = { + {"slg51000", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, slg51000_i2c_id); + +static struct i2c_driver slg51000_regulator_driver = { + .driver = { + .name = "slg51000-regulator", + }, + .probe = slg51000_i2c_probe, + .remove = slg51000_i2c_remove, + .id_table = slg51000_i2c_id, +}; + +module_i2c_driver(slg51000_regulator_driver); + +MODULE_AUTHOR("Eric Jeong "); +MODULE_DESCRIPTION("SLG51000 regulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/slg5bm43670/slg5bm43670-regulator.h b/drivers/regulator/slg5bm43670/slg5bm43670-regulator.h new file mode 100644 index 000000000000..733f1d8808ba --- /dev/null +++ b/drivers/regulator/slg5bm43670/slg5bm43670-regulator.h @@ -0,0 +1,493 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * SLG51000 High PSRR, Multi-Output Regulators + * Copyright (C) 2019 Dialog Semiconductor + * + * Author: Eric Jeong, Dialog Semiconductor + */ + +#ifndef __SLG51000_REGISTERS_H__ +#define __SLG51000_REGISTERS_H__ + +/* Registers */ + +#define SLG51000_SYSCTL_PATN_ID_B0 0x1105 +#define SLG51000_SYSCTL_PATN_ID_B1 0x1106 +#define SLG51000_SYSCTL_PATN_ID_B2 0x1107 +#define SLG51000_SYSCTL_SYS_CONF_A 0x1109 +#define SLG51000_SYSCTL_SYS_CONF_D 0x110c +#define SLG51000_SYSCTL_MATRIX_CONF_A 0x110d +#define SLG51000_SYSCTL_MATRIX_CONF_B 0x110e +#define SLG51000_SYSCTL_REFGEN_CONF_C 0x1111 +#define SLG51000_SYSCTL_UVLO_CONF_A 0x1112 +#define SLG51000_SYSCTL_FAULT_LOG1 0x1115 +#define SLG51000_SYSCTL_EVENT 0x1116 +#define SLG51000_SYSCTL_STATUS 0x1117 +#define SLG51000_SYSCTL_IRQ_MASK 0x1118 +#define SLG51000_IO_GPIO1_CONF 0x1500 +#define SLG51000_IO_GPIO2_CONF 0x1501 +#define SLG51000_IO_GPIO3_CONF 0x1502 +#define SLG51000_IO_GPIO4_CONF 0x1503 +#define SLG51000_IO_GPIO5_CONF 0x1504 +#define SLG51000_IO_GPIO6_CONF 0x1505 +#define SLG51000_IO_GPIO_STATUS 0x1506 +#define SLG51000_LUTARRAY_LUT_VAL_0 0x1600 +#define SLG51000_LUTARRAY_LUT_VAL_1 0x1601 +#define SLG51000_LUTARRAY_LUT_VAL_2 0x1602 +#define SLG51000_LUTARRAY_LUT_VAL_3 0x1603 +#define SLG51000_LUTARRAY_LUT_VAL_4 0x1604 +#define SLG51000_LUTARRAY_LUT_VAL_5 0x1605 +#define SLG51000_LUTARRAY_LUT_VAL_6 0x1606 +#define SLG51000_LUTARRAY_LUT_VAL_7 0x1607 +#define SLG51000_LUTARRAY_LUT_VAL_8 0x1608 +#define SLG51000_LUTARRAY_LUT_VAL_9 0x1609 +#define SLG51000_LUTARRAY_LUT_VAL_10 0x160a +#define SLG51000_LUTARRAY_LUT_VAL_11 0x160b +#define SLG51000_MUXARRAY_INPUT_SEL_0 0x1700 +#define SLG51000_MUXARRAY_INPUT_SEL_1 0x1701 +#define SLG51000_MUXARRAY_INPUT_SEL_2 0x1702 +#define SLG51000_MUXARRAY_INPUT_SEL_3 0x1703 +#define SLG51000_MUXARRAY_INPUT_SEL_4 0x1704 +#define SLG51000_MUXARRAY_INPUT_SEL_5 0x1705 +#define SLG51000_MUXARRAY_INPUT_SEL_6 0x1706 +#define SLG51000_MUXARRAY_INPUT_SEL_7 0x1707 +#define SLG51000_MUXARRAY_INPUT_SEL_8 0x1708 +#define SLG51000_MUXARRAY_INPUT_SEL_9 0x1709 +#define SLG51000_MUXARRAY_INPUT_SEL_10 0x170a +#define SLG51000_MUXARRAY_INPUT_SEL_11 0x170b +#define SLG51000_MUXARRAY_INPUT_SEL_12 0x170c +#define SLG51000_MUXARRAY_INPUT_SEL_13 0x170d +#define SLG51000_MUXARRAY_INPUT_SEL_14 0x170e +#define SLG51000_MUXARRAY_INPUT_SEL_15 0x170f +#define SLG51000_MUXARRAY_INPUT_SEL_16 0x1710 +#define SLG51000_MUXARRAY_INPUT_SEL_17 0x1711 +#define SLG51000_MUXARRAY_INPUT_SEL_18 0x1712 +#define SLG51000_MUXARRAY_INPUT_SEL_19 0x1713 +#define SLG51000_MUXARRAY_INPUT_SEL_20 0x1714 +#define SLG51000_MUXARRAY_INPUT_SEL_21 0x1715 +#define SLG51000_MUXARRAY_INPUT_SEL_22 0x1716 +#define SLG51000_MUXARRAY_INPUT_SEL_23 0x1717 +#define SLG51000_MUXARRAY_INPUT_SEL_24 0x1718 +#define SLG51000_MUXARRAY_INPUT_SEL_25 0x1719 +#define SLG51000_MUXARRAY_INPUT_SEL_26 0x171a +#define SLG51000_MUXARRAY_INPUT_SEL_27 0x171b +#define SLG51000_MUXARRAY_INPUT_SEL_28 0x171c +#define SLG51000_MUXARRAY_INPUT_SEL_29 0x171d +#define SLG51000_MUXARRAY_INPUT_SEL_30 0x171e +#define SLG51000_MUXARRAY_INPUT_SEL_31 0x171f +#define SLG51000_MUXARRAY_INPUT_SEL_32 0x1720 +#define SLG51000_MUXARRAY_INPUT_SEL_33 0x1721 +#define SLG51000_MUXARRAY_INPUT_SEL_34 0x1722 +#define SLG51000_MUXARRAY_INPUT_SEL_35 0x1723 +#define SLG51000_MUXARRAY_INPUT_SEL_36 0x1724 +#define SLG51000_MUXARRAY_INPUT_SEL_37 0x1725 +#define SLG51000_MUXARRAY_INPUT_SEL_38 0x1726 +#define SLG51000_MUXARRAY_INPUT_SEL_39 0x1727 +#define SLG51000_MUXARRAY_INPUT_SEL_40 0x1728 +#define SLG51000_MUXARRAY_INPUT_SEL_41 0x1729 +#define SLG51000_MUXARRAY_INPUT_SEL_42 0x172a +#define SLG51000_MUXARRAY_INPUT_SEL_43 0x172b +#define SLG51000_MUXARRAY_INPUT_SEL_44 0x172c +#define SLG51000_MUXARRAY_INPUT_SEL_45 0x172d +#define SLG51000_MUXARRAY_INPUT_SEL_46 0x172e +#define SLG51000_MUXARRAY_INPUT_SEL_47 0x172f +#define SLG51000_MUXARRAY_INPUT_SEL_48 0x1730 +#define SLG51000_MUXARRAY_INPUT_SEL_49 0x1731 +#define SLG51000_MUXARRAY_INPUT_SEL_50 0x1732 +#define SLG51000_MUXARRAY_INPUT_SEL_51 0x1733 +#define SLG51000_MUXARRAY_INPUT_SEL_52 0x1734 +#define SLG51000_MUXARRAY_INPUT_SEL_53 0x1735 +#define SLG51000_MUXARRAY_INPUT_SEL_54 0x1736 +#define SLG51000_MUXARRAY_INPUT_SEL_55 0x1737 +#define SLG51000_MUXARRAY_INPUT_SEL_56 0x1738 +#define SLG51000_MUXARRAY_INPUT_SEL_57 0x1739 +#define SLG51000_MUXARRAY_INPUT_SEL_58 0x173a +#define SLG51000_MUXARRAY_INPUT_SEL_59 0x173b +#define SLG51000_MUXARRAY_INPUT_SEL_60 0x173c +#define SLG51000_MUXARRAY_INPUT_SEL_61 0x173d +#define SLG51000_MUXARRAY_INPUT_SEL_62 0x173e +#define SLG51000_MUXARRAY_INPUT_SEL_63 0x173f +#define SLG51000_PWRSEQ_RESOURCE_EN_0 0x1900 +#define SLG51000_PWRSEQ_RESOURCE_EN_1 0x1901 +#define SLG51000_PWRSEQ_RESOURCE_EN_2 0x1902 +#define SLG51000_PWRSEQ_RESOURCE_EN_3 0x1903 +#define SLG51000_PWRSEQ_RESOURCE_EN_4 0x1904 +#define SLG51000_PWRSEQ_RESOURCE_EN_5 0x1905 +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_UP0 0x1906 +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_DOWN0 0x1907 +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_UP1 0x1908 +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_DOWN1 0x1909 +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_UP2 0x190a +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_DOWN2 0x190b +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_UP3 0x190c +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_DOWN3 0x190d +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_UP4 0x190e +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_DOWN4 0x190f +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_UP5 0x1910 +#define SLG51000_PWRSEQ_SLOT_TIME_MIN_DOWN5 0x1911 +#define SLG51000_PWRSEQ_SLOT_TIME_MAX_CONF_A 0x1912 +#define SLG51000_PWRSEQ_SLOT_TIME_MAX_CONF_B 0x1913 +#define SLG51000_PWRSEQ_SLOT_TIME_MAX_CONF_C 0x1914 +#define SLG51000_PWRSEQ_INPUT_SENSE_CONF_A 0x1915 +#define SLG51000_PWRSEQ_INPUT_SENSE_CONF_B 0x1916 +#define SLG51000_LDO1_VSEL 0x2000 +#define SLG51000_LDO1_VSEL_RANGE_MASK_MIN 0x2060 +#define SLG51000_LDO1_VSEL_RANGE_MASK_MAX 0x2061 +#define SLG51000_LDO1_TRIM2 0x2063 +#define SLG51000_LDO1_VSEL_ACTUAL 0x2065 +#define SLG51000_LDO1_EVENT 0x20c0 +#define SLG51000_LDO1_STATUS 0x20c1 +#define SLG51000_LDO1_IRQ_MASK 0x20c2 +#define SLG51000_LDO2_VSEL 0x2200 +#define SLG51000_LDO2_VSEL_RANGE_MASK_MIN 0x2260 +#define SLG51000_LDO2_VSEL_RANGE_MASK_MAX 0x2261 +#define SLG51000_LDO2_TRIM2 0x2263 +#define SLG51000_LDO2_VSEL_ACTUAL 0x2265 +#define SLG51000_LDO2_EVENT 0x22c0 +#define SLG51000_LDO2_STATUS 0x22c1 +#define SLG51000_LDO2_IRQ_MASK 0x22c2 +#define SLG51000_LDO3_VSEL 0x2300 +#define SLG51000_LDO3_VSEL_RANGE_MASK_MIN 0x2360 +#define SLG51000_LDO3_VSEL_RANGE_MASK_MAX 0x2361 +#define SLG51000_LDO3_TRIM2 0x2363 +#define SLG51000_LDO3_CONF1 0x2364 +#define SLG51000_LDO3_CONF2 0x2365 +#define SLG51000_LDO3_VSEL_ACTUAL 0x2366 +#define SLG51000_LDO3_EVENT 0x23c0 +#define SLG51000_LDO3_STATUS 0x23c1 +#define SLG51000_LDO3_IRQ_MASK 0x23c2 +#define SLG51000_LDO4_VSEL 0x2500 +#define SLG51000_LDO4_VSEL_RANGE_MASK_MIN 0x2560 +#define SLG51000_LDO4_VSEL_RANGE_MASK_MAX 0x2561 +#define SLG51000_LDO4_TRIM2 0x2563 +#define SLG51000_LDO4_CONF1 0x2564 +#define SLG51000_LDO4_CONF2 0x2565 +#define SLG51000_LDO4_VSEL_ACTUAL 0x2566 +#define SLG51000_LDO4_EVENT 0x25c0 +#define SLG51000_LDO4_STATUS 0x25c1 +#define SLG51000_LDO4_IRQ_MASK 0x25c2 +#define SLG51000_LDO5_VSEL 0x2700 +#define SLG51000_LDO5_VSEL_RANGE_MASK_MIN 0x2760 +#define SLG51000_LDO5_VSEL_RANGE_MASK_MAX 0x2761 +#define SLG51000_LDO5_TRIM2 0x2763 +#define SLG51000_LDO5_TRIM3 0x2764 +#define SLG51000_LDO5_CONF1 0x2765 +#define SLG51000_LDO5_CONF2 0x2766 +#define SLG51000_LDO5_VSEL_ACTUAL 0x2767 +#define SLG51000_LDO5_EVENT 0x27c0 +#define SLG51000_LDO5_STATUS 0x27c1 +#define SLG51000_LDO5_IRQ_MASK 0x27c2 +#define SLG51000_LDO6_VSEL 0x2900 +#define SLG51000_LDO6_VSEL_RANGE_MASK_MIN 0x2960 +#define SLG51000_LDO6_VSEL_RANGE_MASK_MAX 0x2961 +#define SLG51000_LDO6_TRIM2 0x2963 +#define SLG51000_LDO6_TRIM3 0x2964 +#define SLG51000_LDO6_CONF1 0x2965 +#define SLG51000_LDO6_CONF2 0x2966 +#define SLG51000_LDO6_VSEL_ACTUAL 0x2967 +#define SLG51000_LDO6_EVENT 0x29c0 +#define SLG51000_LDO6_STATUS 0x29c1 +#define SLG51000_LDO6_IRQ_MASK 0x29c2 +#define SLG51000_LDO7_VSEL 0x3100 +#define SLG51000_LDO7_VSEL_RANGE_MASK_MIN 0x3160 +#define SLG51000_LDO7_VSEL_RANGE_MASK_MAX 0x3161 +#define SLG51000_LDO7_TRIM2 0x3163 +#define SLG51000_LDO7_CONF1 0x3164 +#define SLG51000_LDO7_CONF2 0x3165 +#define SLG51000_LDO7_VSEL_ACTUAL 0x3166 +#define SLG51000_LDO7_EVENT 0x31c0 +#define SLG51000_LDO7_STATUS 0x31c1 +#define SLG51000_LDO7_IRQ_MASK 0x31c2 +#define SLG51000_LOCK_GLOBAL_LOCK_CTRL1 0x8000 + +/* Register Bit Fields */ + +/* SLG51000_SYSCTL_PATN_ID_B0 = 0x1105 */ +#define SLG51000_PROGRAMMING_CODE_NUMBER_SHIFT 0 +#define SLG51000_PROGRAMMING_CODE_NUMBER_MASK (0xff << 0) + +/* SLG51000_SYSCTL_PATN_ID_B1 = 0x1106 */ +#define SLG51000_PART_NUMBER_BYTE0_SHIFT 0 +#define SLG51000_PART_NUMBER_BYTE0_MASK (0xff << 0) + +/* SLG51000_SYSCTL_PATN_ID_B2 = 0x1107 */ +#define SLG51000_PART_NUMBER_BYTE1_SHIFT 0 +#define SLG51000_PART_NUMBER_BYTE1_MASK (0xff << 0) + +/* SLG51000_SYSCTL_SYS_CONF_A = 0x1109 */ +#define SLG51000_I2C_ADDRESS_SHIFT 0 +#define SLG51000_I2C_ADDRESS_MASK (0x7f << 0) +#define SLG51000_I2C_DISABLE_SHIFT 7 +#define SLG51000_I2C_DISABLE_MASK (0x01 << 7) + +/* SLG51000_SYSCTL_SYS_CONF_D = 0x110c */ +#define SLG51000_CS_T_DEB_SHIFT 6 +#define SLG51000_CS_T_DEB_MASK (0x03 << 6) +#define SLG51000_I2C_CLR_MODE_SHIFT 5 +#define SLG51000_I2C_CLR_MODE_MASK (0x01 << 5) + +/* SLG51000_SYSCTL_MATRIX_CONF_A = 0x110d */ +#define SLG51000_RESOURCE_CTRL_SHIFT 0 +#define SLG51000_RESOURCE_CTRL_MASK (0xff << 0) + +/* SLG51000_SYSCTL_MATRIX_CONF_B = 0x110e */ +#define SLG51000_MATRIX_EVENT_SENSE_SHIFT 0 +#define SLG51000_MATRIX_EVENT_SENSE_MASK (0x07 << 0) + +/* SLG51000_SYSCTL_REFGEN_CONF_C = 0x1111 */ +#define SLG51000_REFGEN_SEL_TEMP_WARN_DEBOUNCE_SHIFT 2 +#define SLG51000_REFGEN_SEL_TEMP_WARN_DEBOUNCE_MASK (0x03 << 2) +#define SLG51000_REFGEN_SEL_TEMP_WARN_THR_SHIFT 0 +#define SLG51000_REFGEN_SEL_TEMP_WARN_THR_MASK (0x03 << 0) + +/* SLG51000_SYSCTL_UVLO_CONF_A = 0x1112 */ +#define SLG51000_VMON_UVLO_SEL_THR_SHIFT 0 +#define SLG51000_VMON_UVLO_SEL_THR_MASK (0x1f << 0) + +/* SLG51000_SYSCTL_FAULT_LOG1 = 0x1115 */ +#define SLG51000_FLT_POR_SHIFT 5 +#define SLG51000_FLT_POR_MASK (0x01 << 5) +#define SLG51000_FLT_RST_SHIFT 4 +#define SLG51000_FLT_RST_MASK (0x01 << 4) +#define SLG51000_FLT_POWER_SEQ_CRASH_REQ_SHIFT 2 +#define SLG51000_FLT_POWER_SEQ_CRASH_REQ_MASK (0x01 << 2) +#define SLG51000_FLT_OVER_TEMP_SHIFT 1 +#define SLG51000_FLT_OVER_TEMP_MASK (0x01 << 1) + +/* SLG51000_SYSCTL_EVENT = 0x1116 */ +#define SLG51000_EVT_MATRIX_SHIFT 1 +#define SLG51000_EVT_MATRIX_MASK (0x01 << 1) +#define SLG51000_EVT_HIGH_TEMP_WARN_SHIFT 0 +#define SLG51000_EVT_HIGH_TEMP_WARN_MASK (0x01 << 0) + +/* SLG51000_SYSCTL_STATUS = 0x1117 */ +#define SLG51000_STA_MATRIX_SHIFT 1 +#define SLG51000_STA_MATRIX_MASK (0x01 << 1) +#define SLG51000_STA_HIGH_TEMP_WARN_SHIFT 0 +#define SLG51000_STA_HIGH_TEMP_WARN_MASK (0x01 << 0) + +/* SLG51000_SYSCTL_IRQ_MASK = 0x1118 */ +#define SLG51000_IRQ_MATRIX_SHIFT 1 +#define SLG51000_IRQ_MATRIX_MASK (0x01 << 1) +#define SLG51000_IRQ_HIGH_TEMP_WARN_SHIFT 0 +#define SLG51000_IRQ_HIGH_TEMP_WARN_MASK (0x01 << 0) + +/* SLG51000_IO_GPIO1_CONF ~ SLG51000_IO_GPIO5_CONF = + * 0x1500, 0x1501, 0x1502, 0x1503, 0x1504 + */ +#define SLG51000_GPIO_DIR_SHIFT 7 +#define SLG51000_GPIO_DIR_MASK (0x01 << 7) +#define SLG51000_GPIO_SENS_SHIFT 5 +#define SLG51000_GPIO_SENS_MASK (0x03 << 5) +#define SLG51000_GPIO_INVERT_SHIFT 4 +#define SLG51000_GPIO_INVERT_MASK (0x01 << 4) +#define SLG51000_GPIO_BYP_SHIFT 3 +#define SLG51000_GPIO_BYP_MASK (0x01 << 3) +#define SLG51000_GPIO_T_DEB_SHIFT 1 +#define SLG51000_GPIO_T_DEB_MASK (0x03 << 1) +#define SLG51000_GPIO_LEVEL_SHIFT 0 +#define SLG51000_GPIO_LEVEL_MASK (0x01 << 0) + +/* SLG51000_IO_GPIO6_CONF = 0x1505 */ +#define SLG51000_GPIO6_SENS_SHIFT 5 +#define SLG51000_GPIO6_SENS_MASK (0x03 << 5) +#define SLG51000_GPIO6_INVERT_SHIFT 4 +#define SLG51000_GPIO6_INVERT_MASK (0x01 << 4) +#define SLG51000_GPIO6_T_DEB_SHIFT 1 +#define SLG51000_GPIO6_T_DEB_MASK (0x03 << 1) +#define SLG51000_GPIO6_LEVEL_SHIFT 0 +#define SLG51000_GPIO6_LEVEL_MASK (0x01 << 0) + +/* SLG51000_IO_GPIO_STATUS = 0x1506 */ +#define SLG51000_GPIO6_STATUS_SHIFT 5 +#define SLG51000_GPIO6_STATUS_MASK (0x01 << 5) +#define SLG51000_GPIO5_STATUS_SHIFT 4 +#define SLG51000_GPIO5_STATUS_MASK (0x01 << 4) +#define SLG51000_GPIO4_STATUS_SHIFT 3 +#define SLG51000_GPIO4_STATUS_MASK (0x01 << 3) +#define SLG51000_GPIO3_STATUS_SHIFT 2 +#define SLG51000_GPIO3_STATUS_MASK (0x01 << 2) +#define SLG51000_GPIO2_STATUS_SHIFT 1 +#define SLG51000_GPIO2_STATUS_MASK (0x01 << 1) +#define SLG51000_GPIO1_STATUS_SHIFT 0 +#define SLG51000_GPIO1_STATUS_MASK (0x01 << 0) + +/* SLG51000_LUTARRAY_LUT_VAL_0 ~ SLG51000_LUTARRAY_LUT_VAL_11 + * 0x1600, 0x1601, 0x1602, 0x1603, 0x1604, 0x1605, + * 0x1606, 0x1607, 0x1608, 0x1609, 0x160a, 0x160b + */ +#define SLG51000_LUT_VAL_SHIFT 0 +#define SLG51000_LUT_VAL_MASK (0xff << 0) + +/* SLG51000_MUXARRAY_INPUT_SEL_0 ~ SLG51000_MUXARRAY_INPUT_SEL_63 + * 0x1700, 0x1701, 0x1702, 0x1703, 0x1704, 0x1705, + * 0x1706, 0x1707, 0x1708, 0x1709, 0x170a, 0x170b, + * 0x170c, 0x170d, 0x170e, 0x170f, 0x1710, 0x1711, + * 0x1712, 0x1713, 0x1714, 0x1715, 0x1716, 0x1717, + * 0x1718, 0x1719, 0x171a, 0x171b, 0x171c, 0x171d, + * 0x171e, 0x171f, 0x1720, 0x1721, 0x1722, 0x1723, + * 0x1724, 0x1725, 0x1726, 0x1727, 0x1728, 0x1729, + * 0x173a, 0x173b, 0x173c, 0x173d, 0x173e, 0x173f, + */ +#define SLG51000_INPUT_SEL_SHIFT 0 +#define SLG51000_INPUT_SEL_MASK (0x3f << 0) + +/* SLG51000_PWRSEQ_RESOURCE_EN_0 ~ SLG51000_PWRSEQ_RESOURCE_EN_5 + * 0x1900, 0x1901, 0x1902, 0x1903, 0x1904, 0x1905 + */ +#define SLG51000_RESOURCE_EN_DOWN0_SHIFT 4 +#define SLG51000_RESOURCE_EN_DOWN0_MASK (0x07 << 4) +#define SLG51000_RESOURCE_EN_UP0_SHIFT 0 +#define SLG51000_RESOURCE_EN_UP0_MASK (0x07 << 0) + +/* SLG51000_PWRSEQ_SLOT_TIME_MIN_UP0 ~ SLG51000_PWRSEQ_SLOT_TIME_MIN_UP5 + * 0x1906, 0x1908, 0x190a, 0x190c, 0x190e, 0x1910 + */ +#define SLG51000_SLOT_TIME_MIN_UP_SHIFT 0 +#define SLG51000_SLOT_TIME_MIN_UP_MASK (0xff << 0) + +/* SLG51000_PWRSEQ_SLOT_TIME_MIN_DOWN0 ~ SLG51000_PWRSEQ_SLOT_TIME_MIN_DOWN5 + * 0x1907, 0x1909, 0x190b, 0x190d, 0x190f, 0x1911 + */ +#define SLG51000_SLOT_TIME_MIN_DOWN_SHIFT 0 +#define SLG51000_SLOT_TIME_MIN_DOWN_MASK (0xff << 0) + +/* SLG51000_PWRSEQ_SLOT_TIME_MAX_CONF_A ~ SLG51000_PWRSEQ_SLOT_TIME_MAX_CONF_C + * 0x1912, 0x1913, 0x1914 + */ +#define SLG51000_SLOT_TIME_MAX_DOWN1_SHIFT 6 +#define SLG51000_SLOT_TIME_MAX_DOWN1_MASK (0x03 << 6) +#define SLG51000_SLOT_TIME_MAX_UP1_SHIFT 4 +#define SLG51000_SLOT_TIME_MAX_UP1_MASK (0x03 << 4) +#define SLG51000_SLOT_TIME_MAX_DOWN0_SHIFT 2 +#define SLG51000_SLOT_TIME_MAX_DOWN0_MASK (0x03 << 2) +#define SLG51000_SLOT_TIME_MAX_UP0_SHIFT 0 +#define SLG51000_SLOT_TIME_MAX_UP0_MASK (0x03 << 0) + +/* SLG51000_PWRSEQ_INPUT_SENSE_CONF_A = 0x1915 */ +#define SLG51000_TRIG_UP_SENSE_SHIFT 6 +#define SLG51000_TRIG_UP_SENSE_MASK (0x01 << 6) +#define SLG51000_UP_EN_SENSE5_SHIFT 5 +#define SLG51000_UP_EN_SENSE5_MASK (0x01 << 5) +#define SLG51000_UP_EN_SENSE4_SHIFT 4 +#define SLG51000_UP_EN_SENSE4_MASK (0x01 << 4) +#define SLG51000_UP_EN_SENSE3_SHIFT 3 +#define SLG51000_UP_EN_SENSE3_MASK (0x01 << 3) +#define SLG51000_UP_EN_SENSE2_SHIFT 2 +#define SLG51000_UP_EN_SENSE2_MASK (0x01 << 2) +#define SLG51000_UP_EN_SENSE1_SHIFT 1 +#define SLG51000_UP_EN_SENSE1_MASK (0x01 << 1) +#define SLG51000_UP_EN_SENSE0_SHIFT 0 +#define SLG51000_UP_EN_SENSE0_MASK (0x01 << 0) + +/* SLG51000_PWRSEQ_INPUT_SENSE_CONF_B = 0x1916 */ +#define SLG51000_CRASH_DETECT_SENSE_SHIFT 7 +#define SLG51000_CRASH_DETECT_SENSE_MASK (0x01 << 7) +#define SLG51000_TRIG_DOWN_SENSE_SHIFT 6 +#define SLG51000_TRIG_DOWN_SENSE_MASK (0x01 << 6) +#define SLG51000_DOWN_EN_SENSE5_SHIFT 5 +#define SLG51000_DOWN_EN_SENSE5_MASK (0x01 << 5) +#define SLG51000_DOWN_EN_SENSE4_SHIFT 4 +#define SLG51000_DOWN_EN_SENSE4_MASK (0x01 << 4) +#define SLG51000_DOWN_EN_SENSE3_SHIFT 3 +#define SLG51000_DOWN_EN_SENSE3_MASK (0x01 << 3) +#define SLG51000_DOWN_EN_SENSE2_SHIFT 2 +#define SLG51000_DOWN_EN_SENSE2_MASK (0x01 << 2) +#define SLG51000_DOWN_EN_SENSE1_SHIFT 1 +#define SLG51000_DOWN_EN_SENSE1_MASK (0x01 << 1) +#define SLG51000_DOWN_EN_SENSE0_SHIFT 0 +#define SLG51000_DOWN_EN_SENSE0_MASK (0x01 << 0) + +/* SLG51000_LDO1_VSEL ~ SLG51000_LDO7_VSEL = + * 0x2000, 0x2200, 0x2300, 0x2500, 0x2700, 0x2900, 0x3100 + */ +#define SLG51000_VSEL_SHIFT 0 +#define SLG51000_VSEL_MASK (0xff << 0) + +/* SLG51000_LDO1_VSEL_RANGE_MASK_MIN ~ SLG51000_LDO7_VSEL_RANGE_MASK_MIN = + * 0x2060, 0x2260, 0x2360, 0x2560, 0x2760, 0x2960, 0x3160 + */ +#define SLG51000_VSEL_RANGE_MASK_MIN_SHIFT 0 +#define SLG51000_VSEL_RANGE_MASK_MIN_MASK (0xff << 0) + +/* SLG51000_LDO1_VSEL_RANGE_MASK_MAX ~ SLG51000_LDO7_VSEL_RANGE_MASK_MAX = + * 0x2061, 0x2261, 0x2361, 0x2561, 0x2761, 0x2961, 0x3161 + */ +#define SLG51000_VSEL_RANGE_MASK_MAX_SHIFT 0 +#define SLG51000_VSEL_RANGE_MASK_MAX_MASK (0xff << 0) + +/* SLG51000_LDO1_VSEL_ACTUAL ~ SLG51000_LDO7_VSEL_ACTUAL = + * 0x2065, 0x2265, 0x2366, 0x2566, 0x2767, 0x2967, 0x3166 + */ +#define SLG51000_VSEL_ACTUAL_SHIFT 0 +#define SLG51000_VSEL_ACTUAL_MASK (0xff << 0) + +/* SLG51000_LDO1_EVENT ~ SLG51000_LDO7_EVENT = + * 0x20c0, 0x22c0, 0x23c0, 0x25c0, 0x27c0, 0x29c0, 0x31c0 + */ +#define SLG51000_EVT_ILIM_FLAG_SHIFT 0 +#define SLG51000_EVT_ILIM_FLAG_MASK (0x01 << 0) +#define SLG51000_EVT_VOUT_OK_FLAG_SHIFT 1 +#define SLG51000_EVT_VOUT_OK_FLAG_MASK (0x01 << 1) + +/* SLG51000_LDO1_STATUS ~ SLG51000_LDO7_STATUS = + * 0x20c1, 0x22c1, 0x23c1, 0x25c1, 0x27c1, 0x29c1, 0x31c1 + */ +#define SLG51000_STA_ILIM_FLAG_SHIFT 0 +#define SLG51000_STA_ILIM_FLAG_MASK (0x01 << 0) +#define SLG51000_STA_VOUT_OK_FLAG_SHIFT 1 +#define SLG51000_STA_VOUT_OK_FLAG_MASK (0x01 << 1) + +/* SLG51000_LDO1_IRQ_MASK ~ SLG51000_LDO7_IRQ_MASK = + * 0x20c2, 0x22c2, 0x23c2, 0x25c2, 0x27c2, 0x29c2, 0x31c2 + */ +#define SLG51000_IRQ_ILIM_FLAG_SHIFT 0 +#define SLG51000_IRQ_ILIM_FLAG_MASK (0x01 << 0) + +/* SLG51000_LDO3_CONF1 ~ SLG51000_LDO7_CONF1 = + * 0x2364, 0x2564, 0x2765, 0x2965, 0x3164 + */ +#define SLG51000_SEL_START_ILIM_SHIFT 0 +#define SLG51000_SEL_START_ILIM_MASK (0x7f << 0) + +/* SLG51000_LDO3_CONF2 ~ SLG51000_LDO7_CONF2 = + * 0x2365, 0x2565, 0x2766, 0x2966, 0x3165 + */ +#define SLG51000_SEL_FUNC_ILIM_SHIFT 0 +#define SLG51000_SEL_FUNC_ILIM_MASK (0x7f << 0) + +/* SLG51000_LDO5_TRIM2 = 0x2763, SLG51000_LDO6_TRIM2 = 0x2963 */ +#define SLG51000_SEL_BYP_SLEW_RATE_SHIFT 2 +#define SLG51000_SEL_BYP_SLEW_RATE_MASK (0x03 << 2) +#define SLG51000_SEL_BYP_VGATE_SHIFT 1 +#define SLG51000_SEL_BYP_VGATE_MASK (0x01 << 1) +#define SLG51000_SEL_BYP_MODE_SHIFT 0 +#define SLG51000_SEL_BYP_MODE_MASK (0x01 << 0) + +/* SLG51000_LDO1_TRIM2 = 0x2063, SLG51000_LDO2_TRIM2 = 0x2263, + * SLG51000_LDO3_TRIM2 = 0x2363, SLG51000_LDO4_TRIM2 = 0x2563, + * SLG51000_LDO5_TRIM3 = 0x2764, SLG51000_LDO6_TRIM3 = 0x2964, + * SLG51000_LDO7_TRIM2 = 0x3163 + */ +#define SLG51000_ILIM_FLAG_DEB_SHIFT 3 +#define SLG51000_ILIM_FLAG_DEB_MASK (0x03 << 3) +#define SLG51000_VOUT_OK_DEB_SHIFT 1 +#define SLG51000_VOUT_OK_DEB_MASK (0x03 << 1) + +/* SLG51000_LOCK_GLOBAL_LOCK_CTRL1 = 0x8000 */ +#define SLG51000_LDO7_LOCK_SHIFT 7 +#define SLG51000_LDO7_LOCK_MASK (0x01 << 7) +#define SLG51000_LDO6_LOCK_SHIFT 6 +#define SLG51000_LDO6_LOCK_MASK (0x01 << 6) +#define SLG51000_LDO5_LOCK_SHIFT 5 +#define SLG51000_LDO5_LOCK_MASK (0x01 << 5) +#define SLG51000_LDO4_LOCK_SHIFT 4 +#define SLG51000_LDO4_LOCK_MASK (0x01 << 4) +#define SLG51000_LDO3_LOCK_SHIFT 3 +#define SLG51000_LDO3_LOCK_MASK (0x01 << 3) +#define SLG51000_LDO2_LOCK_SHIFT 2 +#define SLG51000_LDO2_LOCK_MASK (0x01 << 2) +#define SLG51000_LDO1_LOCK_SHIFT 1 +#define SLG51000_LDO1_LOCK_MASK (0x01 << 1) + +#endif /* __SLG51000_REGISTERS_H__ */ diff --git a/drivers/regulator/wl2864c/Android.mk b/drivers/regulator/wl2864c/Android.mk new file mode 100644 index 000000000000..5e565a1cc223 --- /dev/null +++ b/drivers/regulator/wl2864c/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := wl2864c.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/regulator/wl2864c/Kbuild b/drivers/regulator/wl2864c/Kbuild new file mode 100644 index 000000000000..359e291dfdca --- /dev/null +++ b/drivers/regulator/wl2864c/Kbuild @@ -0,0 +1,5 @@ +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +wl2864c-objs += wl2864c-regulator.o +obj-m += wl2864c.o diff --git a/drivers/regulator/wl2864c/Makefile b/drivers/regulator/wl2864c/Makefile new file mode 100644 index 000000000000..365d3fe73a44 --- /dev/null +++ b/drivers/regulator/wl2864c/Makefile @@ -0,0 +1,15 @@ +all: modules + +modules: + $(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 + diff --git a/drivers/regulator/wl2864c/wl2864c-regulator.c b/drivers/regulator/wl2864c/wl2864c-regulator.c new file mode 100644 index 000000000000..2599e5fbeafd --- /dev/null +++ b/drivers/regulator/wl2864c/wl2864c-regulator.c @@ -0,0 +1,397 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * WL2864C, Multi-Output Regulators + * Copyright (C) 2019 Motorola Mobility LLC, + * + * Author: ChengLong, Motorola Mobility LLC, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wl2864c-regulator.h" + +enum slg51000_regulators { + WL2864C_REGULATOR_LDO1 = 0, + WL2864C_REGULATOR_LDO2, + WL2864C_REGULATOR_LDO3, + WL2864C_REGULATOR_LDO4, + WL2864C_REGULATOR_LDO5, + WL2864C_REGULATOR_LDO6, + WL2864C_REGULATOR_LDO7, + WL2864C_MAX_REGULATORS, +}; + +struct wl2864c { + struct device *dev; + struct regmap *regmap; + struct regulator_desc *rdesc[WL2864C_MAX_REGULATORS]; + struct regulator_dev *rdev[WL2864C_MAX_REGULATORS]; + int chip_cs_pin; +}; + +struct wl2864c_evt_sta { + unsigned int sreg; +}; + +static const struct wl2864c_evt_sta wl2864c_status_reg = { WL2864C_LDO_EN }; + +static const struct regmap_range wl2864c_writeable_ranges[] = { + regmap_reg_range(WL2864C_CURRENT_LIMITSEL, WL2864C_SEQ_STATUS), +}; + +static const struct regmap_range wl2864c_readable_ranges[] = { + regmap_reg_range(WL2864C_CHIP_REV, WL2864C_SEQ_STATUS), +}; + +static const struct regmap_range wl2864c_volatile_ranges[] = { + regmap_reg_range(WL2864C_CURRENT_LIMITSEL, WL2864C_SEQ_STATUS), +}; + +static const struct regmap_access_table wl2864c_writeable_table = { + .yes_ranges = wl2864c_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(wl2864c_writeable_ranges), +}; + +static const struct regmap_access_table wl2864c_readable_table = { + .yes_ranges = wl2864c_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(wl2864c_readable_ranges), +}; + +static const struct regmap_access_table wl2864c_volatile_table = { + .yes_ranges = wl2864c_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(wl2864c_volatile_ranges), +}; + +static const struct regmap_config wl2864c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x0F, + .wr_table = &wl2864c_writeable_table, + .rd_table = &wl2864c_readable_table, + .volatile_table = &wl2864c_volatile_table, +}; + +static int wl2864c_get_current_limit(struct regulator_dev *rdev) +{ + struct wl2864c *chip = rdev_get_drvdata(rdev); + uint8_t reg_dump[WL2864C_REG_NUM]; + uint8_t reg_idx; + unsigned int val = 0; + + dev_dbg(chip->dev, "************ start dump wl2864c register ************\n"); + dev_dbg(chip->dev, "regulator name = %s \n", rdev->desc->name); + dev_dbg(chip->dev, "register 0x00: chip version\n"); + dev_dbg(chip->dev, "register 0x01: LDO CL\n"); + dev_dbg(chip->dev, "register 0x03~0x09: LDO1~LDO7 OUT Voltage\n"); + dev_dbg(chip->dev, "register 0x0e: Bit[6:0] LDO7~LDO1 EN\n"); + + for (reg_idx = 0; reg_idx < WL2864C_REG_NUM; reg_idx++) { + regmap_read(chip->regmap, reg_idx, &val); + reg_dump[reg_idx] = val; + dev_dbg(chip->dev, "Reg[0x%02x] = 0x%x", reg_idx, reg_dump[reg_idx]); + } + dev_dbg(chip->dev, "************ end dump wl2864c register ************\n"); + + return 0; +} + +static int wl2864c_get_status(struct regulator_dev *rdev) +{ + struct wl2864c *chip = rdev_get_drvdata(rdev); + int ret, id = rdev_get_id(rdev); + unsigned int status = 0; + + ret = regulator_is_enabled_regmap(rdev); + if (ret < 0) { + dev_err(chip->dev, "Failed to read enable register(%d)\n", + ret); + return ret; + } + + if (!ret) + return REGULATOR_STATUS_OFF; + + wl2864c_get_current_limit(rdev); + + ret = regmap_read(chip->regmap, wl2864c_status_reg.sreg, &status); + if (ret < 0) { + dev_err(chip->dev, "Failed to read status register(%d)\n", + ret); + return ret; + } + + if (status & (0x01ul << id)) { + return REGULATOR_STATUS_ON; + } else { + return REGULATOR_STATUS_OFF; + } +} + +static const struct regulator_ops wl2864c_regl_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_status = wl2864c_get_status, + .get_current_limit = wl2864c_get_current_limit, +}; + +static int wl2864c_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + int ena_gpio; + + ena_gpio = of_get_named_gpio(np, "enable-gpios", 0); + if (gpio_is_valid(ena_gpio)) + config->ena_gpiod = gpio_to_desc(ena_gpio); + + return 0; +} + +#define WL2864C_REGL_DESC(_id, _name, _s_name, _min, _step) \ + [WL2864C_REGULATOR_##_id] = { \ + .name = #_name, \ + .supply_name = _s_name, \ + .id = WL2864C_REGULATOR_##_id, \ + .of_match = of_match_ptr(#_name), \ + .of_parse_cb = wl2864c_of_parse_cb, \ + .ops = &wl2864c_regl_ops, \ + .regulators_node = of_match_ptr("regulators"), \ + .n_voltages = 256, \ + .min_uV = _min, \ + .uV_step = _step, \ + .linear_min_sel = 0, \ + .vsel_mask = WL2864C_VSEL_MASK, \ + .vsel_reg = WL2864C_##_id##_VSEL, \ + .enable_reg = WL2864C_LDO_EN, \ + .enable_mask = BIT(WL2864C_REGULATOR_##_id), \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + } + +static struct regulator_desc wl2864c_regls_desc[WL2864C_MAX_REGULATORS] = { + WL2864C_REGL_DESC(LDO1, ldo1, "vin1", 600000, 12500), + WL2864C_REGL_DESC(LDO2, ldo2, "vin1", 600000, 12500), + WL2864C_REGL_DESC(LDO3, ldo3, "vin2", 1200000, 12500), + WL2864C_REGL_DESC(LDO4, ldo4, "vin2", 1200000, 12500), + WL2864C_REGL_DESC(LDO5, ldo5, "vin2", 1200000, 12500), + WL2864C_REGL_DESC(LDO6, ldo6, "vin2", 1200000, 12500), + WL2864C_REGL_DESC(LDO7, ldo7, "vin2", 1200000, 12500), +}; + +static int wl2864c_regulator_init(struct wl2864c *chip) +{ + struct regulator_config config = { }; + struct regulator_desc *rdesc; + u8 vsel_range[1]; + int id, ret = 0; + const unsigned int ldo_regs[WL2864C_MAX_REGULATORS] = { + WL2864C_LDO1_VOUT, + WL2864C_LDO2_VOUT, + WL2864C_LDO3_VOUT, + WL2864C_LDO4_VOUT, + WL2864C_LDO5_VOUT, + WL2864C_LDO6_VOUT, + WL2864C_LDO7_VOUT, + }; + + const unsigned int initial_voltage[WL2864C_MAX_REGULATORS] = { + 0x24,//LDO1 1.05V + 0x30,//LDO2 1.2V + 0x80,//LDO3 2.8V + 0x80,//LDO4 2.8V + 0x80,//LDO5 2.8V + 0x30,//LDO6 1.8V + 0x30,//LDO7 1.8V + }; + + /*Disable all ldo output by default*/ + ret = regmap_write(chip->regmap, WL2864C_LDO_EN, 0); + if (ret < 0) { + dev_err(chip->dev, + "Disable all LDO output failed!!!\n"); + return ret; + } + + for (id = 0; id < WL2864C_MAX_REGULATORS; id++) { + chip->rdesc[id] = &wl2864c_regls_desc[id]; + rdesc = chip->rdesc[id]; + config.regmap = chip->regmap; + config.dev = chip->dev; + config.driver_data = chip; + + ret = regmap_bulk_read(chip->regmap, ldo_regs[id], + vsel_range, 1); + pr_debug("wl2864c_regulator_init: LDO%d, default value:0x%x", (id+1), vsel_range[0]); + if (ret < 0) { + dev_err(chip->dev, + "Failed to read the ldo register\n"); + return ret; + } + + ret = regmap_write(chip->regmap, ldo_regs[id], initial_voltage[id]); + if (ret < 0) { + dev_err(chip->dev, + "Failed to write inital voltage register\n"); + return ret; + } + pr_debug("wl2864c_regulator_init: LDO%d, initial value:0x%x", (id+1), initial_voltage[id]); + + chip->rdev[id] = devm_regulator_register(chip->dev, rdesc, + &config); + if (IS_ERR(chip->rdev[id])) { + ret = PTR_ERR(chip->rdev[id]); + dev_err(chip->dev, + "Failed to register regulator(%s):%d\n", + chip->rdesc[id]->name, ret); + return ret; + } + } + + return 0; +} + +static int wl2864c_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct wl2864c *chip; + int error, cs_gpio, ret, i; + + /* Set all register to initial value when probe driver to avoid register value was modified. + */ + const unsigned int initial_register[7][2] = { + {WL2864C_CURRENT_LIMITSEL, 0x40}, + {WL2864C_DISCHARGE_RESISTORS, 0x00}, + {WL2864C_LDO1_LDO2_SEQ, 0x00}, + {WL2864C_LDO3_LDO4_SEQ, 0x00}, + {WL2864C_LDO5_LDO6_SEQ, 0x00}, + {WL2864C_LDO7_SEQ, 0x00}, + {WL2864C_SEQ_STATUS, 0x00}, + }; + + chip = devm_kzalloc(dev, sizeof(struct wl2864c), GFP_KERNEL); + if (!chip) { + dev_err(chip->dev, "wl2864c_i2c_probe Memory error...\n"); + return -ENOMEM; + } + + dev_info(chip->dev, "wl2864c_i2c_probe Enter...\n"); + + cs_gpio = of_get_named_gpio(dev->of_node, "semi,cs-gpios", 0); + if (cs_gpio > 0) { + if (!gpio_is_valid(cs_gpio)) { + dev_err(dev, "Invalid chip select pin\n"); + return -EPERM; + } + + ret = devm_gpio_request_one(dev, cs_gpio, GPIOF_OUT_INIT_LOW, + "wl2864c_cs_pin"); + if (ret) { + dev_err(dev, "GPIO(%d) request failed(%d)\n", + cs_gpio, ret); + return ret; + } + + chip->chip_cs_pin = cs_gpio; + } + + dev_info(chip->dev, "wl2864c_i2c_probe cs_gpio:%d...\n", cs_gpio); + + mdelay(10); + + i2c_set_clientdata(client, chip); + chip->dev = dev; + chip->regmap = devm_regmap_init_i2c(client, &wl2864c_regmap_config); + if (IS_ERR(chip->regmap)) { + error = PTR_ERR(chip->regmap); + dev_err(dev, "Failed to allocate register map: %d\n", + error); + return error; + } + + for (i = 0; i < 7; i++) { + ret = regmap_write(chip->regmap, initial_register[i][0], initial_register[i][1]); + if (ret < 0) { + dev_err(chip->dev,"Failed to write register: 0x%x, value: 0x%x \n", + initial_register[i][0], initial_register[i][1]); + } + + dev_dbg(chip->dev,"Success to write register: 0x%x, value: 0x%x \n", + initial_register[i][0], initial_register[i][1]); + } + + ret = wl2864c_regulator_init(chip); + if (ret < 0) { + dev_err(chip->dev, "Failed to init regulator(%d)\n", ret); + return ret; + } + + wl2864c_get_current_limit(chip->rdev[0]); + + dev_info(chip->dev, "wl2864c_i2c_probe Exit...\n"); + + return ret; +} + +static void wl2864c_i2c_shutdown(struct i2c_client *client) +{ + struct wl2864c *chip = i2c_get_clientdata(client); + + if (chip) { + /* Disable all regulator to avoid current leak */ + regmap_write(chip->regmap, WL2864C_LDO_EN, 0x00); + dev_err(chip->dev, "wl2864c_i2c_shutdown"); + } +} + +static int wl2864c_i2c_remove(struct i2c_client *client) +{ + struct wl2864c *chip = i2c_get_clientdata(client); + struct gpio_desc *desc; + int ret = 0; + + if (chip->chip_cs_pin > 0) { + desc = gpio_to_desc(chip->chip_cs_pin); + ret = gpiod_direction_output_raw(desc, GPIOF_INIT_LOW); + } + + return ret; +} + +static const struct i2c_device_id wl2864c_i2c_id[] = { + {"wl2864c", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, wl2864c_i2c_id); + +static struct i2c_driver wl2864c_regulator_driver = { + .driver = { + .name = "wl2864c-regulator", + }, + .probe = wl2864c_i2c_probe, + .remove = wl2864c_i2c_remove, + .shutdown = wl2864c_i2c_shutdown, + .id_table = wl2864c_i2c_id, +}; + +module_i2c_driver(wl2864c_regulator_driver); + +MODULE_AUTHOR("ChengLong "); +MODULE_DESCRIPTION("WL2864C regulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/wl2864c/wl2864c-regulator.h b/drivers/regulator/wl2864c/wl2864c-regulator.h new file mode 100644 index 000000000000..d99469e9de5f --- /dev/null +++ b/drivers/regulator/wl2864c/wl2864c-regulator.h @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * WL2864C, Multi-Output Regulators + * Copyright (C) 2019 Motorola Mobility LLC, + * + * Author: ChengLong, Motorola Mobility LLC, + */ + +#ifndef __WL2864C_REGISTERS_H__ +#define __WL2864C_REGISTERS_H__ + +/* Registers */ +#define WL2864C_REG_NUM (WL2864C_SEQ_STATUS-WL2864C_CHIP_REV+1) + +#define WL2864C_CHIP_REV 0x00 +#define WL2864C_CURRENT_LIMITSEL 0x01 +#define WL2864C_DISCHARGE_RESISTORS 0x02 +#define WL2864C_LDO1_VOUT 0x03 +#define WL2864C_LDO2_VOUT 0x04 +#define WL2864C_LDO3_VOUT 0x05 +#define WL2864C_LDO4_VOUT 0x06 +#define WL2864C_LDO5_VOUT 0x07 +#define WL2864C_LDO6_VOUT 0x08 +#define WL2864C_LDO7_VOUT 0x09 +#define WL2864C_LDO1_LDO2_SEQ 0x0a +#define WL2864C_LDO3_LDO4_SEQ 0x0b +#define WL2864C_LDO5_LDO6_SEQ 0x0c +#define WL2864C_LDO7_SEQ 0x0d +#define WL2864C_LDO_EN 0x0e +#define WL2864C_SEQ_STATUS 0x0f + + +/* WL2864C_LDO1_VSEL ~ WL2864C_LDO7_VSEL = + * 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 + */ +#define WL2864C_LDO1_VSEL WL2864C_LDO1_VOUT +#define WL2864C_LDO2_VSEL WL2864C_LDO2_VOUT +#define WL2864C_LDO3_VSEL WL2864C_LDO3_VOUT +#define WL2864C_LDO4_VSEL WL2864C_LDO4_VOUT +#define WL2864C_LDO5_VSEL WL2864C_LDO5_VOUT +#define WL2864C_LDO6_VSEL WL2864C_LDO6_VOUT +#define WL2864C_LDO7_VSEL WL2864C_LDO7_VOUT + +#define WL2864C_VSEL_SHIFT 0 +#define WL2864C_VSEL_MASK (0xff << 0) + +#endif /* __WL2864C_REGISTERS_H__ */ diff --git a/drivers/regulator/wl2866d/Android.mk b/drivers/regulator/wl2866d/Android.mk new file mode 100644 index 000000000000..6bf595aa3fac --- /dev/null +++ b/drivers/regulator/wl2866d/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := wl2866d.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/regulator/wl2866d/Kbuild b/drivers/regulator/wl2866d/Kbuild new file mode 100644 index 000000000000..279f1df769be --- /dev/null +++ b/drivers/regulator/wl2866d/Kbuild @@ -0,0 +1,5 @@ +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +wl2866d-objs += wl2866d-regulator.o +obj-m += wl2866d.o diff --git a/drivers/regulator/wl2866d/Makefile b/drivers/regulator/wl2866d/Makefile new file mode 100644 index 000000000000..4191dea22679 --- /dev/null +++ b/drivers/regulator/wl2866d/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean diff --git a/drivers/regulator/wl2866d/wl2866d-regulator.c b/drivers/regulator/wl2866d/wl2866d-regulator.c new file mode 100644 index 000000000000..baaa3ad66f69 --- /dev/null +++ b/drivers/regulator/wl2866d/wl2866d-regulator.c @@ -0,0 +1,406 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * wl2866d, Multi-Output Regulators + * Copyright (C) 2019 Motorola Mobility LLC, + * + * Author: ChengLong, Motorola Mobility LLC, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wl2866d-regulator.h" + +enum slg51000_regulators { + WL2866D_REGULATOR_LDO1 = 0, + WL2866D_REGULATOR_LDO2, + WL2866D_REGULATOR_LDO3, + WL2866D_REGULATOR_LDO4, + WL2866D_MAX_REGULATORS, +}; + +struct wl2866d { + struct device *dev; + struct regmap *regmap; + struct regulator_desc *rdesc[WL2866D_MAX_REGULATORS]; + struct regulator_dev *rdev[WL2866D_MAX_REGULATORS]; + int chip_cs_pin; + int init_value; + bool shutdown_ldo; +}; + +struct wl2866d_evt_sta { + unsigned int sreg; +}; + +static const struct wl2866d_evt_sta wl2866d_status_reg = { WL2866D_LDO_EN }; + +static const struct regmap_range wl2866d_writeable_ranges[] = { + /* Do not let useless register writeable */ + regmap_reg_range(WL2866D_CURRENT_LIMITSEL, WL2866D_SEQ_STATUS), +}; + +static const struct regmap_range wl2866d_readable_ranges[] = { + regmap_reg_range(WL2866D_CHIP_REV, WL2866D_SEQ_STATUS), +}; + +static const struct regmap_range wl2866d_volatile_ranges[] = { + regmap_reg_range(WL2866D_CURRENT_LIMITSEL, WL2866D_SEQ_STATUS), +}; + +static const struct regmap_access_table wl2866d_writeable_table = { + .yes_ranges = wl2866d_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(wl2866d_writeable_ranges), +}; + +static const struct regmap_access_table wl2866d_readable_table = { + .yes_ranges = wl2866d_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(wl2866d_readable_ranges), +}; + +static const struct regmap_access_table wl2866d_volatile_table = { + .yes_ranges = wl2866d_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(wl2866d_volatile_ranges), +}; + +static const struct regmap_config wl2866d_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x0F, + .wr_table = &wl2866d_writeable_table, + .rd_table = &wl2866d_readable_table, + .volatile_table = &wl2866d_volatile_table, +}; + +static int wl2866d_get_current_limit(struct regulator_dev *rdev) +{ + struct wl2866d *chip = rdev_get_drvdata(rdev); + uint8_t reg_dump[WL2866D_REG_NUM]; + uint8_t reg_idx; + unsigned int val = 0; + + dev_err(chip->dev, "************ start dump wl2866d register ************\n"); + dev_err(chip->dev, "register name =%s \n",rdev->desc->name); + dev_err(chip->dev, "register 0x00: chip version\n"); + dev_err(chip->dev, "register 0x01: LDO CL\n"); + dev_err(chip->dev, "register 0x03~0x06: LDO1~LDO4 OUT Voltage\n"); + dev_err(chip->dev, "register 0x0e: Bit[3:0] LDO4~LDO1 EN\n"); + + for (reg_idx = 0; reg_idx < WL2866D_REG_NUM; reg_idx++) { + regmap_read(chip->regmap, reg_idx, &val); + reg_dump[reg_idx] = val; + dev_err(chip->dev, "Reg[0x%02x] = 0x%x", reg_idx, reg_dump[reg_idx]); + } + dev_err(chip->dev, "************ end dump wl2866D register ************\n"); + + return 0; +} + +static int wl2866d_get_status(struct regulator_dev * rdev) +{ + struct wl2866d *chip = rdev_get_drvdata(rdev); + int ret, id = rdev_get_id(rdev); + unsigned int status = 0; + + ret = regulator_is_enabled_regmap(rdev); + if (ret < 0) { + dev_err(chip->dev, "Failed to read enable register(%d)\n", + ret); + return ret; + } + + if (!ret) + return REGULATOR_STATUS_OFF; + + wl2866d_get_current_limit(rdev); + + ret = regmap_read(chip->regmap, wl2866d_status_reg.sreg, &status); + if (ret < 0) { + dev_err(chip->dev, "Failed to read status register(%d)\n", + ret); + return ret; + } + + if (status & (0x01ul << id)) { + return REGULATOR_STATUS_ON; + } else { + return REGULATOR_STATUS_OFF; + } +} + +static const struct regulator_ops wl2866d_regl_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_status = wl2866d_get_status, + .get_current_limit = wl2866d_get_current_limit, +}; + +static int wl2866d_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + int ena_gpio; + + ena_gpio = of_get_named_gpio(np, "enable-gpios", 0); + if (gpio_is_valid(ena_gpio)) + config->ena_gpiod = gpio_to_desc(ena_gpio); + + return 0; +} + +#define WL2866D_REGL_DESC(_id, _name, _s_name, _min, _step) \ + [WL2866D_REGULATOR_##_id] = { \ + .name = #_name, \ + .supply_name = _s_name, \ + .id = WL2866D_REGULATOR_##_id, \ + .of_match = of_match_ptr(#_name), \ + .of_parse_cb = wl2866d_of_parse_cb, \ + .ops = &wl2866d_regl_ops, \ + .regulators_node = of_match_ptr("regulators"), \ + .n_voltages = 256, \ + .min_uV = _min, \ + .uV_step = _step, \ + .linear_min_sel = 0, \ + .vsel_mask = WL2866D_VSEL_MASK, \ + .vsel_reg = WL2866D_##_id##_VSEL, \ + .enable_reg = WL2866D_LDO_EN, \ + .enable_mask = BIT(WL2866D_REGULATOR_##_id), \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + } + +static struct regulator_desc wl2866d_regls_desc[WL2866D_MAX_REGULATORS] = { + WL2866D_REGL_DESC(LDO1, ldo1, "vin1", 600000, 6000), + WL2866D_REGL_DESC(LDO2, ldo2, "vin1", 600000, 6000), + WL2866D_REGL_DESC(LDO3, ldo3, "vin2", 1200000, 12500), + WL2866D_REGL_DESC(LDO4, ldo4, "vin2", 1200000, 12500), +}; + +static int wl2866d_regulator_init(struct wl2866d *chip) +{ + struct regulator_config config = { }; + struct regulator_desc *rdesc; + u8 vsel_range[1]; + int id, ret = 0; + const unsigned int ldo_regs[WL2866D_MAX_REGULATORS] = { + WL2866D_LDO1_VOUT, + WL2866D_LDO2_VOUT, + WL2866D_LDO3_VOUT, + WL2866D_LDO4_VOUT, + }; + + const unsigned int initial_voltage[WL2866D_MAX_REGULATORS] = { + 0x4b,//LDO1 DVDD 1.05V + 0x64,//LDO2 DVDD 1.2V + 0x80,//LDO3 AVDD 2.8V + 0x80,//LDO4 AVDD 2.8V + }; + + /*Disable all ldo output by default*/ + ret = regmap_write(chip->regmap, WL2866D_LDO_EN, chip->init_value); + if (ret < 0) { + dev_err(chip->dev, + "Disable all LDO output failed!!!\n"); + return ret; + } + /* Enable all ldo discharge by default */ + ret = regmap_write(chip->regmap, WL2866D_DISCHARGE_RESISTORS, 0x8f); + if (ret < 0) { + dev_err(chip->dev, + "Enable LDO discharge failed!!!\n"); + return ret; + } + for (id = 0; id < WL2866D_MAX_REGULATORS; id++) { + chip->rdesc[id] = &wl2866d_regls_desc[id]; + rdesc = chip->rdesc[id]; + config.regmap = chip->regmap; + config.dev = chip->dev; + config.driver_data = chip; + + ret = regmap_bulk_read(chip->regmap, ldo_regs[id], + vsel_range, 1); + pr_err("wl2866d_regulator_init: LDO%d, default value:0x%x", (id+1), vsel_range[0]); + if (ret < 0) { + dev_err(chip->dev, + "Failed to read the ldo register\n"); + return ret; + } + + ret = regmap_write(chip->regmap, ldo_regs[id], initial_voltage[id]); + if (ret < 0) { + dev_err(chip->dev, + "Failed to write inital voltage register\n"); + return ret; + } + pr_err("wl2866d_regulator_init: LDO%d, initial value:0x%x", (id+1), initial_voltage[id]); + + chip->rdev[id] = devm_regulator_register(chip->dev, rdesc, + &config); + if (IS_ERR(chip->rdev[id])) { + ret = PTR_ERR(chip->rdev[id]); + dev_err(chip->dev, + "Failed to register regulator(%s):%d\n", + chip->rdesc[id]->name, ret); + return ret; + } + } + + return 0; +} + +static int wl2866d_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct wl2866d *chip; + int error, cs_gpio, ret, i, value; + + /* Set all register to initial value when probe driver to avoid register value was modified. + */ + const unsigned int initial_register[5][2] = { + {WL2866D_CURRENT_LIMITSEL, 0x40}, + {WL2866D_DISCHARGE_RESISTORS, 0x00}, + {WL2866D_LDO1_LDO2_SEQ, 0x00}, + {WL2866D_LDO3_LDO4_SEQ, 0x00}, + {WL2866D_SEQ_STATUS, 0x00}, + }; + chip = devm_kzalloc(dev, sizeof(struct wl2866d), GFP_KERNEL); + if (!chip) { + dev_err(chip->dev, "wl2866d_i2c_probe Memory error...\n"); + return -ENOMEM; + } + + dev_info(chip->dev, "wl2866d_i2c_probe Enter...\n"); + + cs_gpio = of_get_named_gpio(dev->of_node, "semi,cs-gpios", 0); + if (cs_gpio > 0) { + if (!gpio_is_valid(cs_gpio)) { + dev_err(dev, "Invalid chip select pin\n"); + return -EPERM; + } + + ret = devm_gpio_request_one(dev, cs_gpio, GPIOF_OUT_INIT_LOW, + "wl2866d_cs_pin"); + if (ret) { + dev_err(dev, "GPIO(%d) request failed(%d)\n", + cs_gpio, ret); + return ret; + } + + chip->chip_cs_pin = cs_gpio; + } + + dev_info(chip->dev, "wl2866d_i2c_probe cs_gpio:%d...\n", cs_gpio); + + if (of_property_read_u32(dev->of_node, "semi,init-value", &value) < 0) { + dev_info(chip->dev, "wl2866d_i2c_probe no init_value, use default 0x0\n"); + value = 0x0; + } + chip->init_value = value; + dev_info(chip->dev, "wl2866d_i2c_probe init_value:%d...\n", value); + chip->shutdown_ldo = of_property_read_bool(dev->of_node, "semi,shutdown-ldo"); + + mdelay(10); + + i2c_set_clientdata(client, chip); + chip->dev = dev; + chip->regmap = devm_regmap_init_i2c(client, &wl2866d_regmap_config); + if (IS_ERR(chip->regmap)) { + error = PTR_ERR(chip->regmap); + dev_err(dev, "Failed to allocate register map: %d\n", + error); + return error; + } + + + for (i = 0; i < 5; i++) { + ret = regmap_write(chip->regmap, initial_register[i][0], initial_register[i][1]); + if (ret < 0) { + dev_err(chip->dev,"Failed to write register: 0x%x, value: 0x%x \n", + initial_register[i][0], initial_register[i][1]); + } + + dev_err(chip->dev,"Success to write register: 0x%x, value: 0x%x \n", + initial_register[i][0], initial_register[i][1]); + } + + ret = wl2866d_regulator_init(chip); + if (ret < 0) { + dev_err(chip->dev, "Failed to init regulator(%d)\n", ret); + return ret; + } + + wl2866d_get_current_limit(chip->rdev[0]); + + dev_info(chip->dev, "wl2866d_i2c_probe Exit...\n"); + + return ret; +} + +static int wl2866d_i2c_remove(struct i2c_client *client) +{ + struct wl2866d *chip = i2c_get_clientdata(client); + struct gpio_desc *desc; + int ret = 0; + + if (chip->chip_cs_pin > 0) { + desc = gpio_to_desc(chip->chip_cs_pin); + ret = gpiod_direction_output_raw(desc, GPIOF_INIT_LOW); + } + + return ret; +} + +static void wl2866d_i2c_shutdown(struct i2c_client *client) +{ + struct wl2866d *chip = i2c_get_clientdata(client); + unsigned int val = 0; + + if (chip->shutdown_ldo) { + regmap_read(chip->regmap, WL2866D_LDO_EN, &val); + /* Disable AVDD1 when shutdown to meet device SPEC and avoid current leak */ + regmap_write(chip->regmap, WL2866D_LDO_EN, val & ~(1<<2)); + dev_info(chip->dev, "wl2866d_i2c_shutdown"); + } + if (chip) { + regmap_write(chip->regmap, WL2866D_LDO_EN, 0); + dev_err(chip->dev, "wl2866d_i2c_shutdown force disable all LDOs"); + } +} + +static const struct i2c_device_id wl2866d_i2c_id[] = { + {"wl2866d", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, wl2866d_i2c_id); + +static struct i2c_driver wl2866d_regulator_driver = { + .driver = { + .name = "wl2866d-regulator", + }, + .probe = wl2866d_i2c_probe, + .remove = wl2866d_i2c_remove, + .shutdown = wl2866d_i2c_shutdown, + .id_table = wl2866d_i2c_id, +}; + +module_i2c_driver(wl2866d_regulator_driver); + +MODULE_AUTHOR("ChengLong "); +MODULE_DESCRIPTION("WL2866D regulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/wl2866d/wl2866d-regulator.h b/drivers/regulator/wl2866d/wl2866d-regulator.h new file mode 100644 index 000000000000..144ebe4b56b1 --- /dev/null +++ b/drivers/regulator/wl2866d/wl2866d-regulator.h @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * WL2866D, Multi-Output Regulators + * Copyright (C) 2019 Motorola Mobility LLC, + * + * Author: ChengLong, Motorola Mobility LLC, + */ + +#ifndef __WL2866D_REGISTERS_H__ +#define __WL2866D_REGISTERS_H__ + +/* Registers */ +#define WL2866D_REG_NUM (WL2866D_SEQ_STATUS-WL2866D_CHIP_REV+1) + +#define WL2866D_CHIP_REV 0x00 +#define WL2866D_CURRENT_LIMITSEL 0x01 +#define WL2866D_DISCHARGE_RESISTORS 0x02 +#define WL2866D_LDO1_VOUT 0x03 +#define WL2866D_LDO2_VOUT 0x04 +#define WL2866D_LDO3_VOUT 0x05 +#define WL2866D_LDO4_VOUT 0x06 +#define WL2866D_LDO1_LDO2_SEQ 0x0a +#define WL2866D_LDO3_LDO4_SEQ 0x0b +#define WL2866D_LDO_EN 0x0e +#define WL2866D_SEQ_STATUS 0x0f + + +/* WL2866D_LDO1_VSEL ~ WL2866D_LDO4_VSEL = + * 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 + */ +#define WL2866D_LDO1_VSEL WL2866D_LDO1_VOUT +#define WL2866D_LDO2_VSEL WL2866D_LDO2_VOUT +#define WL2866D_LDO3_VSEL WL2866D_LDO3_VOUT +#define WL2866D_LDO4_VSEL WL2866D_LDO4_VOUT + + +#define WL2866D_VSEL_SHIFT 0 +#define WL2866D_VSEL_MASK (0xff << 0) + +#endif /* __WL2866D_REGISTERS_H__ */ diff --git a/drivers/regulator/wl2868c/Android.mk b/drivers/regulator/wl2868c/Android.mk new file mode 100644 index 000000000000..17706a9a66a0 --- /dev/null +++ b/drivers/regulator/wl2868c/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := wl2868c.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/regulator/wl2868c/Kbuild b/drivers/regulator/wl2868c/Kbuild new file mode 100644 index 000000000000..c24c715f39f3 --- /dev/null +++ b/drivers/regulator/wl2868c/Kbuild @@ -0,0 +1,5 @@ +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +wl2868c-objs += wl2868c-regulator.o +obj-m += wl2868c.o diff --git a/drivers/regulator/wl2868c/Makefile b/drivers/regulator/wl2868c/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/regulator/wl2868c/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/regulator/wl2868c/wl2868c-regulator.c b/drivers/regulator/wl2868c/wl2868c-regulator.c new file mode 100644 index 000000000000..67709f8226ce --- /dev/null +++ b/drivers/regulator/wl2868c/wl2868c-regulator.c @@ -0,0 +1,504 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * wl2868c, Multi-Output Regulators + * Copyright (C) 2019 Motorola Mobility LLC, + * + * Author: ChengLong, Motorola Mobility LLC, + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wl2868c-regulator.h" +static int ldo_chipid = -1; +enum slg51000_regulators { + WL2868C_REGULATOR_LDO1 = 0, + WL2868C_REGULATOR_LDO2, + WL2868C_REGULATOR_LDO3, + WL2868C_REGULATOR_LDO4, + WL2868C_REGULATOR_LDO5, + WL2868C_REGULATOR_LDO6, + WL2868C_REGULATOR_LDO7, + WL2868C_MAX_REGULATORS, +}; + +struct wl2868c { + struct device *dev; + struct regmap *regmap; + struct regulator_desc *rdesc[WL2868C_MAX_REGULATORS]; + struct regulator_dev *rdev[WL2868C_MAX_REGULATORS]; + struct regulator *vio_reg; + int chip_cs_pin; +}; + +/* + * struct wl2868c_evt_sta { + * unsigned int sreg; + * }; + * + * static const struct wl2868c_evt_sta wl2868c_status_reg = { WL2868C_LDO_EN }; + */ + +static unsigned int wl2868c_status_reg = WL2868C_LDO_EN; +static const struct regmap_range wl2868c_writeable_ranges[] = { + regmap_reg_range(WL2868C_CURRENT_LIMITSEL, WL2868C_SEQ_STATUS), + /* Do not let useless register writeable */ + // regmap_reg_range(WL2868C_CURRENT_LIMITSEL, WL2868C_CURRENT_LIMITSEL), + // regmap_reg_range(WL2868C_LDO1_VOUT, WL2868C_LDO7_VOUT), + // regmap_reg_range(WL2868C_LDO_EN, WL2868C_LDO_EN), +}; + +static const struct regmap_range wl2868c_readable_ranges[] = { + regmap_reg_range(WL2868C_CHIP_REV, WL2868C_SEQ_STATUS), +}; + +static const struct regmap_range wl2868c_volatile_ranges[] = { + regmap_reg_range(WL2868C_CURRENT_LIMITSEL, WL2868C_SEQ_STATUS), +}; + +static const struct regmap_access_table wl2868c_writeable_table = { + .yes_ranges = wl2868c_writeable_ranges, + .n_yes_ranges = ARRAY_SIZE(wl2868c_writeable_ranges), +}; + +static const struct regmap_access_table wl2868c_readable_table = { + .yes_ranges = wl2868c_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(wl2868c_readable_ranges), +}; + +static const struct regmap_access_table wl2868c_volatile_table = { + .yes_ranges = wl2868c_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(wl2868c_volatile_ranges), +}; + +static const struct regmap_config wl2868c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x0F, + .wr_table = &wl2868c_writeable_table, + .rd_table = &wl2868c_readable_table, + .volatile_table = &wl2868c_volatile_table, +}; + +static int wl2868c_get_error_flags(struct regulator_dev *rdev, unsigned int *flags) +{ + struct wl2868c *chip = rdev_get_drvdata(rdev); + uint8_t reg_dump[WL2868C_REG_NUM]; + uint8_t reg_idx; + unsigned int val = 0; + + dev_err(chip->dev, "************ start dump wl2868c register ************\n"); + dev_err(chip->dev, "register 0x00: chip version\n"); + dev_err(chip->dev, "register 0x01: LDO CL\n"); + dev_err(chip->dev, "register 0x03~0x09: LDO1~LDO7 OUT Voltage\n"); + dev_err(chip->dev, "register 0x0e: Bit[6:0] LDO7~LDO1 EN\n"); + + for (reg_idx = 0; reg_idx < WL2868C_REG_NUM; reg_idx++) { + regmap_read(chip->regmap, reg_idx, &val); + reg_dump[reg_idx] = val; + dev_err(chip->dev, "Reg[0x%02x] = 0x%x", reg_idx, reg_dump[reg_idx]); + } + dev_err(chip->dev, "************ end dump wl2868c register ************\n"); + + if (flags != NULL) { + *flags = 0; + } + + return 0; +} + +static int wl2868c_get_status(struct regulator_dev *rdev) +{ + struct wl2868c *chip = rdev_get_drvdata(rdev); + int ret, id = rdev_get_id(rdev); + unsigned int status = 0; + + ret = regulator_is_enabled_regmap(rdev); + if (ret < 0) { + dev_err(chip->dev, "Failed to read enable register(%d)\n", ret); + return ret; + } + + if (!ret) + return REGULATOR_STATUS_OFF; + + wl2868c_get_error_flags(rdev, NULL); + if ((ldo_chipid == 0x01) || (ldo_chipid == 0x00)) + { + wl2868c_status_reg = fan53870_LDO_EN; + } + ret = regmap_read(chip->regmap, wl2868c_status_reg, &status); + if (ret < 0) { + dev_err(chip->dev, "Failed to read status register(%d)\n", ret); + return ret; + } + + if (status & (0x01ul << id)) { + return REGULATOR_STATUS_ON; + } else { + return REGULATOR_STATUS_OFF; + } +} + +static const struct regulator_ops wl2868c_regl_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + + .is_enabled = regulator_is_enabled_regmap, + + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + + .get_status = wl2868c_get_status, + .get_error_flags = wl2868c_get_error_flags, +}; + +static int wl2868c_of_parse_cb(struct device_node *np, const struct regulator_desc *desc, struct regulator_config *config) +{ + int ena_gpio; + + ena_gpio = of_get_named_gpio(np, "enable-gpios", 0); + if (gpio_is_valid(ena_gpio)) + config->ena_gpiod = gpio_to_desc(ena_gpio); + + return 0; +} + +#define WL2868C_REGL_DESC(_id, _name, _s_name, _min, _step) \ + [WL2868C_REGULATOR_##_id] = { \ + .name = #_name, \ + .supply_name = _s_name, \ + .id = WL2868C_REGULATOR_##_id, \ + .of_match = of_match_ptr(#_name), \ + .of_parse_cb = wl2868c_of_parse_cb, \ + .ops = &wl2868c_regl_ops, \ + .regulators_node = of_match_ptr("regulators"), \ + .n_voltages = 256, \ + .min_uV = _min, \ + .uV_step = _step, \ + .linear_min_sel = 0, \ + .vsel_mask = WL2868C_VSEL_MASK, \ + .vsel_reg = WL2868C_##_id##_VSEL, \ + .enable_reg = WL2868C_LDO_EN, \ + .enable_mask = BIT(WL2868C_REGULATOR_##_id), \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + } + +static struct regulator_desc wl2868c_regls_desc[WL2868C_MAX_REGULATORS] = { + WL2868C_REGL_DESC(LDO1, ldo1, "vin1", 496000, 8000), + WL2868C_REGL_DESC(LDO2, ldo2, "vin1", 496000, 8000), + WL2868C_REGL_DESC(LDO3, ldo3, "vin2", 1504000, 8000), + WL2868C_REGL_DESC(LDO4, ldo4, "vin2", 1504000, 8000), + WL2868C_REGL_DESC(LDO5, ldo5, "vin2", 1504000, 8000), + WL2868C_REGL_DESC(LDO6, ldo6, "vin2", 1504000, 8000), + WL2868C_REGL_DESC(LDO7, ldo7, "vin2", 1504000, 8000), +}; + +static int wl2868c_regulator_init(struct wl2868c *chip) +{ + struct regulator_config config = { }; + struct regulator_desc *rdesc; + u8 vsel_range[1]; + int id, ret = 0; + + unsigned int ldo_regs[WL2868C_MAX_REGULATORS] = { + WL2868C_LDO1_VOUT, + WL2868C_LDO2_VOUT, + WL2868C_LDO3_VOUT, + WL2868C_LDO4_VOUT, + WL2868C_LDO5_VOUT, + WL2868C_LDO6_VOUT, + WL2868C_LDO7_VOUT, + }; + + unsigned int initial_voltage[WL2868C_MAX_REGULATORS] = { + 0x24,//LDO1 1.05V + 0x5D,//LDO2 1.24V + 0x80,//LDO3 2.8V + 0x80,//LDO4 2.8V + 0x80,//LDO5 2.8V + 0xA2,//LDO6 2.8V + 0x30,//LDO7 1.8V + }; + + /*Disable all ldo output by default*/ + if (ldo_chipid == 0x33) + { + ret = regmap_write(chip->regmap, WL2868C_LDO_EN, 0x80); + } + else if((ldo_chipid == 0x01) ||(ldo_chipid == 0x00)) + { + ret = regmap_write(chip->regmap, fan53870_LDO_EN, 0); + } + + if (ret < 0) { + dev_err(chip->dev, "Disable all LDO output failed!!!\n"); + return ret; + } + + //ET5907 chipid is 0x00,Fan53870 chipid is 0x01.for change min_uV and step by chip + if (0x01 == ldo_chipid) + { + wl2868c_regls_desc[WL2868C_REGULATOR_LDO1].min_uV = 8000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO1].uV_step = 8000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO2].min_uV = 8000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO2].uV_step = 8000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO3].min_uV = 1372000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO3].uV_step = 8000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO4].min_uV = 1372000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO4].uV_step = 8000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO5].min_uV = 1372000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO5].uV_step = 8000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO6].min_uV = 1372000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO6].uV_step = 8000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO7].min_uV = 1372000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO7].uV_step = 8000; + } + else if (0x00 == ldo_chipid) + { + wl2868c_regls_desc[WL2868C_REGULATOR_LDO1].min_uV = 600000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO1].uV_step = 6000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO2].min_uV = 600000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO2].uV_step = 6000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO3].min_uV = 1200000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO3].uV_step = 10000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO4].min_uV = 1200000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO4].uV_step = 10000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO5].min_uV = 1200000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO5].uV_step = 10000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO6].min_uV = 1200000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO6].uV_step = 10000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO7].min_uV = 1200000; + wl2868c_regls_desc[WL2868C_REGULATOR_LDO7].uV_step = 10000; + } + + for (id = 0; id < WL2868C_MAX_REGULATORS; id++) + { + if ((0x01 == ldo_chipid) || (0x00 == ldo_chipid)) + { + wl2868c_regls_desc[id].enable_reg = fan53870_LDO_EN; + wl2868c_regls_desc[id].enable_mask = BIT(id); + wl2868c_regls_desc[id].vsel_reg += fan53870_LDO_OFFSET; + } + chip->rdesc[id] = &wl2868c_regls_desc[id]; + rdesc = chip->rdesc[id]; + config.regmap = chip->regmap; + config.dev = chip->dev; + config.driver_data = chip; + + ret = regmap_bulk_read(chip->regmap, ldo_regs[id], vsel_range, 1); + pr_err("wl2868c_regulator_init: LDO%d, ldo_regs=0x%x default value:0x%x", (id+1),ldo_regs[id],vsel_range[0]); + if (ret < 0) { + dev_err(chip->dev, "Failed to read the ldo register\n"); + return ret; + } + + pr_err("wl2868c_regulator_init: enable_reg0x%x, enable_mask:0x%x", wl2868c_regls_desc[id].enable_reg, wl2868c_regls_desc[id].enable_mask); + + ret = regmap_write(chip->regmap, ldo_regs[id], initial_voltage[id]); + if (ret < 0) { + dev_err(chip->dev, "Failed to write inital voltage register\n"); + return ret; + } + pr_err("wl2868c_regulator_init: LDO%d, initial value:0x%x", (id+1), initial_voltage[id]); + + chip->rdev[id] = devm_regulator_register(chip->dev, rdesc, &config); + if (IS_ERR(chip->rdev[id])) { + ret = PTR_ERR(chip->rdev[id]); + dev_err(chip->dev, "Failed to register regulator(%s):%d\n", chip->rdesc[id]->name, ret); + return ret; + } + } + + return 0; +} + +static int wl2868c_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct wl2868c *chip; + struct regulator *vio_reg; + int error, cs_gpio, ret; + chip = devm_kzalloc(dev, sizeof(struct wl2868c), GFP_KERNEL); + if (!chip) { + dev_err(chip->dev, "wl2868c_i2c_probe Memory error...\n"); + return -ENOMEM; + } + vio_reg = devm_regulator_get(dev, "vio"); + if (vio_reg != NULL) { + dev_info(chip->dev, "wl2868c vio regulator get successed\n"); + ret = regulator_enable(vio_reg); + chip->vio_reg = vio_reg; + } else { + dev_warn(dev, "VIO regulator get failed or absent!!!\n"); + } + + dev_info(chip->dev, "wl2868c_i2c_probe Enter...\n"); + + cs_gpio = of_get_named_gpio(dev->of_node, "semi,cs-gpios", 0); + if (cs_gpio > 0) { + if (!gpio_is_valid(cs_gpio)) { + dev_err(dev, "Invalid chip select pin\n"); + return -EPERM; + } + + ret = devm_gpio_request_one(dev, cs_gpio, GPIOF_OUT_INIT_HIGH, "wl2868c_cs_pin"); + if (ret) { + dev_err(dev, "GPIO(%d) request failed(%d)\n", cs_gpio, ret); + return ret; + } + + chip->chip_cs_pin = cs_gpio; + } + + dev_info(chip->dev, "wl2868c_i2c_probe cs_gpio:%d...\n", cs_gpio); + + mdelay(10); + + i2c_set_clientdata(client, chip); + chip->dev = dev; + chip->regmap = devm_regmap_init_i2c(client, &wl2868c_regmap_config); + + regmap_read(chip->regmap, WL2868C_CHIP_REV, &ldo_chipid); + dev_info(chip->dev, "IIC addr= 0x%x chipid=:0x%x\n", client->addr,ldo_chipid); + //if regmap fail use a new iic addr try again. + if ((ldo_chipid != 0x33)&&(ldo_chipid != 0x01)&& (ldo_chipid != 0x00)) { + error = PTR_ERR(chip->regmap); + dev_info(chip->dev, "retry iic init\n"); + client->addr = fan53870_ET5907_ADDR; + i2c_set_clientdata(client, chip); + chip->dev = dev; + chip->regmap = devm_regmap_init_i2c(client, &wl2868c_regmap_config); + regmap_read(chip->regmap, WL2868C_CHIP_REV, &ldo_chipid); + if (IS_ERR(chip->regmap)) { + error = PTR_ERR(chip->regmap); + dev_err(dev, "Failed to allocate register map: %d\n", + error); + return error; + } + } + + ret = regmap_read(chip->regmap, WL2868C_CHIP_REV, &ldo_chipid); + if (0x33 == ldo_chipid) + { + dev_info(chip->dev, "wl2868c chipid=:0x%x\n", ldo_chipid); + } + else if (0x01 == ldo_chipid) + { + dev_info(chip->dev, "fan53870 chipid=:0x%x\n", ldo_chipid); + } + else if (0x00 == ldo_chipid) + { + dev_info(chip->dev, "et5907 chipid=:0x%x\n", ldo_chipid); + } + else + { + dev_err(chip->dev, "ldo read chipid error...chipid=:0x%x\n",ldo_chipid); + return -ENOMEM; + } + + { + int chip_rev = 0; + ret = regmap_bulk_read(chip->regmap, 0x00, &chip_rev, 1); + + printk("wl2868c chip rev: %02x\n", chip_rev); + } + + ret = wl2868c_regulator_init(chip); + if (ret < 0) { + dev_err(chip->dev, "Failed to init regulator(%d)\n", ret); + return ret; + } + dev_info(chip->dev, "wl2868c_i2c_probe Exit...\n"); + + return ret; +} + +static void wl2868c_i2c_shutdown(struct i2c_client *client) +{ + struct wl2868c *chip = i2c_get_clientdata(client); + + if (chip) { + /* Since some platforms doesn't have BOB power supply, if no BOB power source, the BOB on schematic is stands for + connecting directly to battery. Most of WL286xC chip selects BOB as input power supply for LDO2~LDO7, when + software doesn't have shutdown handler, LD02~7's input power supply is always there, there will be high risk of + current leak after phone power off, so force disable all ldos before phone power off.*/ + regmap_write(chip->regmap, WL2868C_LDO_EN, 0x00); + dev_err(chip->dev, "wl2868c_i2c_shutdown"); + } +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)) +static void wl2868c_i2c_remove(struct i2c_client *client) +{ + struct wl2868c *chip = i2c_get_clientdata(client); + struct gpio_desc *desc; + + if (chip->chip_cs_pin > 0) { + desc = gpio_to_desc(chip->chip_cs_pin); + gpiod_direction_output_raw(desc, GPIOF_INIT_LOW); + } + + if (NULL != chip->vio_reg) { + regulator_disable(chip->vio_reg); + regulator_put(chip->vio_reg); + } + +} +#else +static int wl2868c_i2c_remove(struct i2c_client *client) +{ + struct wl2868c *chip = i2c_get_clientdata(client); + struct gpio_desc *desc; + int ret = 0; + + if (chip->chip_cs_pin > 0) { + desc = gpio_to_desc(chip->chip_cs_pin); + ret = gpiod_direction_output_raw(desc, GPIOF_INIT_LOW); + } + + if (NULL != chip->vio_reg) { + regulator_disable(chip->vio_reg); + regulator_put(chip->vio_reg); + } + + return ret; +} +#endif + +static const struct i2c_device_id wl2868c_i2c_id[] = { + {"wl2868c", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, wl2868c_i2c_id); + +static struct i2c_driver wl2868c_regulator_driver = { + .driver = { + .name = "wl2868c-regulator", + }, + .probe = wl2868c_i2c_probe, + .remove = wl2868c_i2c_remove, + .shutdown = wl2868c_i2c_shutdown, + .id_table = wl2868c_i2c_id, +}; + +module_i2c_driver(wl2868c_regulator_driver); + +MODULE_AUTHOR("ChengLong "); +MODULE_DESCRIPTION("wl2868c regulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/wl2868c/wl2868c-regulator.h b/drivers/regulator/wl2868c/wl2868c-regulator.h new file mode 100644 index 000000000000..0701091505ef --- /dev/null +++ b/drivers/regulator/wl2868c/wl2868c-regulator.h @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * wl2868c, Multi-Output Regulators + * Copyright (C) 2019 Motorola Mobility LLC, + * + * Author: ChengLong, Motorola Mobility LLC, + */ + +#ifndef __WL2868C_REGISTERS_H__ +#define __WL2868C_REGISTERS_H__ + +/* Registers */ +#define WL2868C_REG_NUM (WL2868C_SEQ_STATUS - WL2868C_CHIP_REV + 1) + +#define WL2868C_CHIP_REV 0x01 +#define WL2868C_CURRENT_LIMITSEL 0x01 +#define WL2868C_DISCHARGE_RESISTORS 0x02 + +#define WL2868C_LDO1_VOUT 0x03 +#define WL2868C_LDO2_VOUT 0x04 +#define WL2868C_LDO3_VOUT 0x05 +#define WL2868C_LDO4_VOUT 0x06 +#define WL2868C_LDO5_VOUT 0x07 +#define WL2868C_LDO6_VOUT 0x08 +#define WL2868C_LDO7_VOUT 0x09 + +#define WL2868C_LDO1_LDO2_SEQ 0x0a +#define WL2868C_LDO3_LDO4_SEQ 0x0b +#define WL2868C_LDO5_LDO6_SEQ 0x0c +#define WL2868C_LDO7_SEQ 0x0d +#define WL2868C_LDO_EN 0x0e + +#define WL2868C_SEQ_STATUS 0x0f + + +#define fan53870_LDO1_VOUT 0x04 +#define fan53870_LDO2_VOUT 0x05 +#define fan53870_LDO3_VOUT 0x06 +#define fan53870_LDO4_VOUT 0x07 +#define fan53870_LDO5_VOUT 0x08 +#define fan53870_LDO6_VOUT 0x09 +#define fan53870_LDO7_VOUT 0x0a + +#define fan53870_LDO_EN 0x03 +#define fan53870_LDO_OFFSET 0x01 +#define fan53870_ET5907_ADDR 0x35 + +/* WL2868C_LDO1_VSEL ~ WL2868C_LDO7_VSEL = + * 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09 + */ +#define WL2868C_LDO1_VSEL WL2868C_LDO1_VOUT +#define WL2868C_LDO2_VSEL WL2868C_LDO2_VOUT +#define WL2868C_LDO3_VSEL WL2868C_LDO3_VOUT +#define WL2868C_LDO4_VSEL WL2868C_LDO4_VOUT +#define WL2868C_LDO5_VSEL WL2868C_LDO5_VOUT +#define WL2868C_LDO6_VSEL WL2868C_LDO6_VOUT +#define WL2868C_LDO7_VSEL WL2868C_LDO7_VOUT + +#define WL2868C_VSEL_SHIFT 0 +#define WL2868C_VSEL_MASK (0xff << 0) + +#endif /* __WL2868C_REGISTERS_H__ */ diff --git a/drivers/security/moto_kern_prot/Android.mk b/drivers/security/moto_kern_prot/Android.mk new file mode 100644 index 000000000000..a3636e0ac5f8 --- /dev/null +++ b/drivers/security/moto_kern_prot/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := moto_kern_prot.ko +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/security/moto_kern_prot/Kbuild b/drivers/security/moto_kern_prot/Kbuild new file mode 100644 index 000000000000..481d1f4c40f2 --- /dev/null +++ b/drivers/security/moto_kern_prot/Kbuild @@ -0,0 +1,9 @@ +ccflags-y += -Wall +ccflags-y += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/misc/moto_kern_prot +ccflags-y += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include +ccflags-y += -I$(ANDROID_BUILD_TOP)/kernel_platform/msm-kernel/include +ccflags-y += -I$(ANDROID_BUILD_TOP)/kernel_platform/msm-kernel/ + +moto_kern_prot-objs := moto_rkp_kickoff.o qcom_hvc.o + +obj-m += moto_kern_prot.o diff --git a/drivers/security/moto_kern_prot/Makefile b/drivers/security/moto_kern_prot/Makefile new file mode 100644 index 000000000000..249a2fc1e8a5 --- /dev/null +++ b/drivers/security/moto_kern_prot/Makefile @@ -0,0 +1,13 @@ +all: modules + +modules: + $(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 modules.order diff --git a/drivers/security/moto_kern_prot/moto_rkp_kickoff.c b/drivers/security/moto_kern_prot/moto_rkp_kickoff.c new file mode 100644 index 000000000000..b120d177ee4b --- /dev/null +++ b/drivers/security/moto_kern_prot/moto_rkp_kickoff.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Motorola Mobility, Inc. + * + * Authors: Maxwell Bland + * Binsheng "Sammy" Que + * + * 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. + * + * Kernel module that initializes motorola's hypervisor-level runtime kernel protections. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "patch_lookup_tree.h" +#include "rkp_hvc_api.h" + +/* + * A critical note on why this is included. After our protections initialize, it + * becomes impossible to register additional kprobes, and thus any kprobe + * initialization for testing purposes must be done before this kernel module + * runs. That means a separate module for testing would involve implementing + * some (likely file-based) IPC, which is decidedly more ugly than bringing + * in the needed test code/adding a compile-time hook during development. + */ +#ifdef ATTACK_TEST +#include "tests/attack_test.h" +#endif + +/* + * For determining the offsets of kernel code, rodata, etc. + * kallsyms_lookup_name is no longer exported due to misuse. In this case, + * however, we want it just to look up very specific constant name strings + */ +static struct kprobe kp_kallsyms_lookup_name = { .symbol_name = "kallsyms_lookup_name", .addr = 0 }; +typedef unsigned long (*kallsyms_lookup_name_t)(const char *name); + +/* + * Used to pin pages of the jump_entry lookup table in memory, so that we + * do not accidentally swap them out + */ +static struct kprobe kp_walk_page_range_novma = { .symbol_name = "walk_page_range_novma", + .addr = 0 }; +typedef int (*walk_page_range_novma_t)(struct mm_struct *mm, + unsigned long start, unsigned long end, + const struct mm_walk_ops *ops, + pgd_t *pgd, void *private); + +static int mem_ready; +module_param(mem_ready, int, 0); +MODULE_PARM_DESC( + mem_ready, + "Parameter for loading the module after boot is complete, indicating that the kernel has allocated needed memory/variables and is ready to be protected."); + +static int pin_pmd_entry(pmd_t *pmd, unsigned long addr, unsigned long next, + struct mm_walk *walk) +{ + pmd_t val; + struct page *page; + + if (!pmd) + return -EACCES; + + val = READ_ONCE(*pmd); + + + if (pmd_leaf(val)) { + page = pmd_page(val); + if (page->flags & PAGE_FLAGS_CHECK_AT_FREE) + get_page(page); + } + + return 0; +} + +static int pin_pte_entry(pte_t *pte, unsigned long addr, unsigned long next, + struct mm_walk *walk) +{ + pte_t val; + struct page *page; + + if (!pte) + return -EACCES; + + val = READ_ONCE(*pte); + page = pte_page(val); + + if (page->flags & PAGE_FLAGS_CHECK_AT_FREE) + get_page(page); + + return 0; +} + +static const struct mm_walk_ops pin_ops = { .pmd_entry = pin_pmd_entry, + .pte_entry = pin_pte_entry }; + +#define pin_range(vsaddr, veaddr, mm) \ + walk_page_range_novma_ind(mm, vsaddr, veaddr, &pin_ops, NULL, NULL) + +/** + * tlb_update_barrier - synchronizes PTE updates on Snapdragon processors + * @info: an array of volatile ints that indicate whether a processor is + * locked. We use volatile here: it is necessary as attempting to access other + * resources (locks, spinlocks) will lead to a fatal page fault on the actively + * modified kernel code page. + * + * Some processors (the 6450) lack a inner-sharable domain for tlb invalidation + * operations and handle TLB updates out-of-band. Updating the PTEs/TLBs needs + * to occur for every single processor. In order to do so, we must gate, in + * order, each processor to preform the update. Once a processor leaves the + * gate, it can commit an operation following break-before-make semantics. Then + * it must return to a spinlock in order to allow the other processors to + * commit their changes. + * + * The linux spinlock is not good enough here: it still tries to access page + * tables, so we use raw arm. + */ +void tlb_update_barrier(void *info) +{ + volatile int *cpuready = (volatile int *)info; + spinlock_t irqoff = INIT_LOCAL_LOCK("irqnever"); + unsigned long irqflags; + int cpu = get_cpu(); + + spin_lock_irqsave(&irqoff, irqflags); + + flush_cache_all(); + + WRITE_ONCE(cpuready[cpu], 1); + while (true) { + asm volatile("sevl\n" + "wfe;\n" + "wfe;\n"); + if (!READ_ONCE(cpuready[cpu])) + break; + } + + /* + * Note we must absolutely not fail here, since if we do, the TLB will + * be invalid for kernel code and we will be unable to recover + */ + commit_hugepage(); + + WRITE_ONCE(cpuready[cpu], 1); + asm volatile("sev\n"); + while (true) { + asm volatile("sevl\n" + "wfe;\n" + "wfe;\n"); + if (!READ_ONCE(cpuready[cpu])) + break; + } + + WRITE_ONCE(cpuready[cpu], 1); + + spin_unlock_irqrestore(&irqoff, irqflags); + put_cpu(); +} + +/** + * on_each_cpu_private - a duplicate of on_each_cpu_mask + * + * Google's GKI does not export the on_each_cpu_mask function but does export + * the equivalent smp function, so this acts as a simple reproduction of + * the on_each_cpu_mask function + */ +void on_each_cpu_private(const struct cpumask *mask, smp_call_func_t func, + void *info, bool wait) +{ + int cpu = get_cpu(); + unsigned long flags; + + smp_call_function_many(mask, func, info, wait); + if (cpumask_test_cpu(cpu, mask)) { + local_irq_save(flags); + func(info); + local_irq_restore(flags); + } + put_cpu(); +} + +/** + * try_prepare_hugepage - splits a hugepage in the hypervisor at a vaddr + * @vaddr: the virtual address of the 2MB page + * + * Used to split 2MB hugepages in the hypervisor to 4KB pages with + * appropriate locking for snapdragon and other processors: puts all + * the other procs in a lock, then wakes them up one by one to do + * TLB updates + * + * Return: 0 on success (should always happen) + */ +int try_prepare_hugepage(uint64_t vaddr) +{ + cpumask_t cpu_mask = { 0 }; + int num_tlb_waiting = 0; + int i = 0; + spinlock_t irqoff = INIT_LOCAL_LOCK("irqnever"); + unsigned long irqflags = 0; + int cpu = -1; + volatile int *cpuready = + kvcalloc((num_present_cpus() + 1), sizeof(int), GFP_KERNEL); + if (!cpuready) { + pr_err("Failed to allocate cpuready array for MotoRKP!\n"); + return -ENOMEM; + } + + cpumask_copy(&cpu_mask, cpu_possible_mask); + cpumask_clear_cpu(smp_processor_id(), &cpu_mask); + on_each_cpu_private(&cpu_mask, tlb_update_barrier, (void *)cpuready, 0); + + cpu = get_cpu(); + spin_lock_irqsave(&irqoff, irqflags); + + flush_cache_all(); + while (true) { + num_tlb_waiting = 0; + for (i = 0; i < num_present_cpus(); i++) { + if (READ_ONCE(cpuready[i])) + num_tlb_waiting++; + } + if (num_tlb_waiting == num_present_cpus() - 1) + break; + } + + /* + * The following call in to the hypervisor and preform + * block demotion on the stage-2 page table if it is + * needed. The first call identifies the block to split + * and allocates a new page table, and the second + * performs the break-before-make to update the PTE + */ + if (prepare_hugepage(vaddr)) { + pr_err("MotoRKP failed to prepare hugepage\n"); + return -EACCES; + } + if (commit_hugepage()) { + pr_err("MotoRKP failed to commit hugepage\n"); + return -EACCES; + } + + /* + * We are OK here since each invalidation is local, so we should keep + * our "good" mappings, and IRQs are disabled, so the only pages being + * accessed here are in the spinlock + */ + for (i = 0; i < num_present_cpus(); i++) { + if (i != cpu) { + WRITE_ONCE(cpuready[i], 0); + asm volatile("sev\n"); + while (true) { + asm volatile("sevl\n" + "wfe;\n" + "wfe;\n"); + if (READ_ONCE(cpuready[i])) + break; + } + } + } + + for (i = 0; i < num_present_cpus(); i++) { + if (i != cpu) { + WRITE_ONCE(cpuready[i], 0); + asm volatile("sev\n"); + while (true) { + if (READ_ONCE(cpuready[i])) + break; + } + } + } + + spin_unlock_irqrestore(&irqoff, irqflags); + put_cpu(); + + if (cpuready) + kvfree((void *)cpuready); + return 0; +} + +/** + * mod_init - Handles the full initialization of Motorola's RKP + * + * Manages the multiprocessor system, allocation of data structures, and + * modification of memory permissions. + */ +static int __init mod_init(void) +{ + uint64_t jel_start, jel_sz, jel_end; /* jump_entry_lookup */ + kallsyms_lookup_name_t kallsyms_lookup_name_ind; + walk_page_range_novma_t walk_page_range_novma_ind; + uint64_t start_jump_table, stop_jump_table, stext, etext, start_rodata, + end_rodata; + struct mm_struct *mm; + +#ifdef ATTACK_TEST + ATTACK_KERNEL_CODE_DECLS; +#endif + + /* + * Ensure that this module is never accidentally insmodded before + * kernel memory is mapped in + */ + if (!mem_ready) { + pr_err("MotoRKP waiting to insmod until kernel memory mapped\n"); + return -EACCES; + } + + pr_info("MotoRKP module loaded!\n"); + + if (register_kprobe(&kp_kallsyms_lookup_name)) { + pr_err("MotoRKP failed to register kallsyms kprobe!\n"); + return -EACCES; + } + kallsyms_lookup_name_ind = (kallsyms_lookup_name_t)kp_kallsyms_lookup_name.addr; + if (register_kprobe(&kp_walk_page_range_novma)) { + pr_err("MotoRKP failed to register kallsyms kprobe!\n"); + return -EACCES; + } + walk_page_range_novma_ind = (walk_page_range_novma_t)kp_walk_page_range_novma.addr; + + start_jump_table = kallsyms_lookup_name_ind("__start___jump_table"); + stop_jump_table = kallsyms_lookup_name_ind("__stop___jump_table"); + stext = kallsyms_lookup_name_ind("_stext"); + etext = kallsyms_lookup_name_ind("_etext"); + start_rodata = kallsyms_lookup_name_ind("__start_rodata"); + end_rodata = kallsyms_lookup_name_ind("__hyp_rodata_end"); + mm = (struct mm_struct *)kallsyms_lookup_name_ind("init_mm"); + +#ifdef ATTACK_TEST + if (tc_num == 1) { + ATTACK_KERNEL_CODE; + return 0; + } +#endif + + /* If we unregister it later, our own protections will create an exception */ + unregister_kprobe(&kp_kallsyms_lookup_name); + + jel_start = jet_alloc((struct jump_entry *)start_jump_table, + (struct jump_entry *)stop_jump_table, + &(jel_sz)); + if (!jel_start) { + pr_err("MotoRKP failed to allocate memory for jump table lookup\n"); + return -EACCES; + } + jel_end = jel_start + jel_sz; + if (pin_range(jel_start, jel_end, mm)) { + pr_err("MotoRKP failed to pin jump table lookup in memory\n"); + return -EACCES; + } + + unregister_kprobe(&kp_walk_page_range_novma); + if (add_jump_entry_lookup(jel_start, jel_end)) { + pr_err("MotoRKP failed to add jump label lookup to hypervisor!\n"); + return -EACCES; + } + + /* + * Check the edges of the protection boundary and ensure that if they + * do correspond to somewhere in the middle of a hugepage, we properly + * demote this block in order to avoid incorrect permissions on + * adjacent memory regions + */ + try_prepare_hugepage(stext); + try_prepare_hugepage(etext); + + if (mark_range_ro_smc(stext, etext)) + return -EACCES; + + try_prepare_hugepage(start_rodata); + try_prepare_hugepage(end_rodata); + + if (mark_range_ro_smc(start_rodata, end_rodata)) + return -EACCES; + + if (lock_rkp()) + return -EACCES; + +#ifdef ATTACK_TEST + if (tc_num == 2) + ATTACK_KERNEL_CODE; + else + attack(); +#endif + + return 0; +} + +static void __exit mod_exit(void) +{ + + pr_info("MotoRKP module exiting. Locking RKP interface just in case.\n"); + lock_rkp(); +} + +module_init(mod_init); +module_exit(mod_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Maxwell Bland "); +MODULE_DESCRIPTION( + "Initializes hypervisor-based Motorola runtime kernel protections."); diff --git a/drivers/security/moto_kern_prot/patch_lookup_tree.h b/drivers/security/moto_kern_prot/patch_lookup_tree.h new file mode 100644 index 000000000000..0e4874990a2b --- /dev/null +++ b/drivers/security/moto_kern_prot/patch_lookup_tree.h @@ -0,0 +1,210 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/** + * Copyright (C) 2023 Motorola Mobility, Inc. + * + * Author: Maxwell Bland + * + * 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. + * + * O(64) time bitwise trie data structure for storing jump label code addresses. + * To perform a lookup, we iterate over the bits of the jump_label from MSB to + * LSB, and use the bit as a tree traversal. + * + * Ideally we would just store this in the hypervisor, but we don't have enough + * memory. At the time of writing there are 56404 jump labels in the kernel, + * and HypX is restricted to a memory footprint of 32 KB. + * + * So instead what we do is allocate this data structure in the kernel module, + * and then lock it down. Finally, we pass the pointer to the hypervisor, which + * can then perform lookups whenever a DABT exception occurs. + * + * We choose not to use the kernel's radix tree for this because it is less + * space efficient and much slower, and jump_label operations relate to + * performance sensitive code (e.g. the networking stack). + */ + +#ifndef JUMP_TABLE_LOOKUP_H +#define JUMP_TABLE_LOOKUP_H + +#include + +/* Used to determine the branch direction */ +#define JUMP_TREE_BRANCH(x) ((x >> ((sizeof(x) * 8) - 1)) & 1) + +union jump_tree_node { + uint32_t je_index[2]; + uint64_t je_ptr; +}; + +static union jump_tree_node *jet; /* Start of jump table lookup */ +static uint64_t jet_ind; /* Current index of jump table lookup */ +static union jump_tree_node *jet_end; /* End of jump table lookup */ + +void init_jtn(union jump_tree_node *jtn) +{ + jtn->je_ptr = 0; +} + +/** + * jet_extend - extends the jump entry lookup table + * + * Scales up the size of the jump entry lookup table by 2 just in case our + * initial estimate was too small. Ensures that the new size is a multiple of + * PAGE_SIZE. + */ +void jet_extend(void) +{ + uint64_t jet_sz = jet_end - jet; + uint64_t new_sz = jet_sz * 2; + union jump_tree_node *new_jet; + + if (new_sz % PAGE_SIZE) + new_sz += PAGE_SIZE - (new_sz % PAGE_SIZE); + + if (jet_ind + 1 >= jet_sz) { + new_jet = kvmalloc(new_sz * sizeof(union jump_tree_node), + GFP_KERNEL); + memcpy(new_jet, jet, jet_sz * sizeof(union jump_tree_node)); + jet_end = new_jet + new_sz; + kvfree(jet); + jet = new_jet; + } + + init_jtn(jet + jet_ind); +} + +/** + * add_branch - adds a branch to the jump entry lookup tree + * + * Adds a branch to the current node corresponding to whether the LSB of the + * cur_code_ptr is 1 or 0. The branch target is the index of the next node in + * the tree or a leaf (jump entry) + */ +int add_branch(unsigned long *cur_code_ptr, union jump_tree_node *cur_node, + uint64_t branch_target) +{ + bool bit; + + if (!cur_code_ptr || !cur_node) + return -EACCES; + + bit = JUMP_TREE_BRANCH(*cur_code_ptr); + *cur_code_ptr <<= 1; + + if (bit) + cur_node->je_index[0] = branch_target; + else + cur_node->je_index[1] = branch_target; + + return 0; +} + +/** + * add_trunk - adds a path of branches to the jump entry lookup tree + * + * Adds the series of branches to the jump entry lookup tree corresponding to + * the bits in the cur_code_ptr value starting at LSB index i, and adds the leaf + * node at the end. + */ +int add_trunk(union jump_tree_node *cur_node, struct jump_entry *je, + unsigned long cur_code_ptr, int i) +{ + if (!cur_node || !je) + return -EACCES; + + for (; i < sizeof(cur_code_ptr) * 8; i++) { + if (add_branch(&cur_code_ptr, cur_node, jet_ind)) + return -EACCES; + jet_extend(); + cur_node = jet + jet_ind; + jet_ind++; + } + cur_node->je_ptr = (uint64_t)je; + + return 0; +} + +/** + * jet_alloc - allocates a new jump entry tree for lookups + * @jet_end_out: output parameter for the size of the jump entry tree (bytes) + * + * Allocates the jump entry tree with 2x the number of jump entries in the + * kernel. This is because we need to store both the jump entry and, in the + * worst case, a pointer to the next index in the tree. Ensures the total memory + * allocated is a multiple of PAGE_SIZE, uses kvmalloc (the memory is + * non-contiguous) + * + * Return: 0 on failure, or the pointer to the allocated jump entry tree + */ +uint64_t jet_alloc(struct jump_entry *je_start, struct jump_entry *je_end, + uint64_t *jet_size_out) +{ + union jump_tree_node *cur_node; + unsigned long cur_code_ptr = -1; + bool bit; + int i = 0; + + if (!je_start || !je_end || !jet_size_out) { + pr_err("%s fail: invalid parameters\n", __func__); + return 0; + } + + if (je_start >= je_end) { + pr_err("%s fail: je_start >= je_end\n", __func__); + return 0; + } + + jet = kvmalloc(2 * (je_end - je_start) * sizeof(union jump_tree_node), + GFP_KERNEL); + jet_ind = 1; + jet_end = jet + 2 * (je_end - je_start); + init_jtn(jet); + init_jtn(jet + 1); + + while (je_start != je_end) { + cur_code_ptr = __virt_to_phys(jump_entry_code(je_start)); + cur_node = jet; + + // iterate over the bits of the jump label + for (i = 0; i < sizeof(cur_code_ptr) * 8; i++) { + bit = JUMP_TREE_BRANCH(cur_code_ptr); + + if (bit) { + if (cur_node->je_index[0]) { + cur_node = jet + cur_node->je_index[0]; + } else { + if (add_trunk(cur_node, je_start, + cur_code_ptr, i)) + return 0; + break; + } + } else { + if (cur_node->je_index[1]) { + cur_node = jet + cur_node->je_index[1]; + } else { + if (add_trunk(cur_node, je_start, + cur_code_ptr, i)) + return 0; + break; + } + } + + cur_code_ptr <<= 1; + } + + je_start++; + } + + *jet_size_out = jet_end - jet; + + return (uint64_t)jet; +} + +#endif diff --git a/drivers/security/moto_kern_prot/qcom_hvc.c b/drivers/security/moto_kern_prot/qcom_hvc.c new file mode 100644 index 000000000000..ef23529abaf3 --- /dev/null +++ b/drivers/security/moto_kern_prot/qcom_hvc.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2023 Motorola Mobility, Inc. + * + * Author: Maxwell Bland + * + * 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. + * + * Implements the Motorola Kernel Integrity Protection Hypervisor API. + */ +#include +#include +#include +#include + +#include "rkp_hvc_api.h" + +int mark_range_ro_smc(uint64_t start, uint64_t end) +{ + struct qseecom_scm_desc desc = { 0 }; + + desc.arginfo = TZ_SYSCALL_CREATE_PARAM_ID_8(0, 0, 0, 0, 0, 0, 0, 0); + desc.args[0] = __virt_to_phys(start); + desc.args[1] = __virt_to_phys(end); + return qcom_scm_qseecom_call(MOTO_RKP_SMCID | MARK_RANGE_RO, &desc, 1); +} + +int add_jump_entry_lookup(uint64_t vaddr, uint64_t size) +{ + struct qseecom_scm_desc desc = { 0 }; + + desc.arginfo = TZ_SYSCALL_CREATE_PARAM_ID_8(0, 0, 0, 0, 0, 0, 0, 0); + desc.args[0] = vaddr; + desc.args[1] = size; + return qcom_scm_qseecom_call(MOTO_RKP_SMCID | ADD_JUMP_LABEL_LOOKUP, + &desc, 1); +} + +int lock_rkp(void) +{ + struct qseecom_scm_desc desc = { 0 }; + + desc.arginfo = TZ_SYSCALL_CREATE_PARAM_ID_8(0, 0, 0, 0, 0, 0, 0, 0); + return qcom_scm_qseecom_call(MOTO_RKP_SMCID | LOCK_RKP, &desc, 1); +} + +int prepare_hugepage(uint64_t addr) +{ + struct qseecom_scm_desc desc = { 0 }; + + desc.arginfo = TZ_SYSCALL_CREATE_PARAM_ID_8(0, 0, 0, 0, 0, 0, 0, 0); + desc.args[0] = __virt_to_phys(addr); + return qcom_scm_qseecom_call(MOTO_RKP_SMCID | PREPARE_BLOCK, &desc, 1); +} + +int commit_hugepage(void) +{ + struct qseecom_scm_desc desc = { 0 }; + + desc.arginfo = TZ_SYSCALL_CREATE_PARAM_ID_8(0, 0, 0, 0, 0, 0, 0, 0); + return qcom_scm_qseecom_call(MOTO_RKP_SMCID | COMMIT_BLOCK, &desc, 1); +} diff --git a/drivers/security/moto_kern_prot/rkp_hvc_api.h b/drivers/security/moto_kern_prot/rkp_hvc_api.h new file mode 100644 index 000000000000..f32252a9b32d --- /dev/null +++ b/drivers/security/moto_kern_prot/rkp_hvc_api.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2023 Motorola Mobility, Inc. + * + * 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. + * + * Defines the kernel-side API for runtime kernel protections as they + * call into the hypervisor. + */ +#ifndef _RKP_HVC_API_H_ +#define _RKP_HVC_API_H_ + +#ifdef MTK_PLATFORM +#define MOTO_RKP_SMCID 0x82000000 +#else +#define MOTO_RKP_SMCID 0x3000000 +#endif + +enum SMC_FUNC_IDS { + MARK_RANGE_RO = 0x1, + LOCK_RKP = 0x2, + ADD_JUMP_LABEL_LOOKUP = 0x3, + ALLOC_IMMUTABLE_BITMAP = 0x4, // Not enabled in this release + MARK_WORD_RO = 0x5, // Not enabled in this release + PREPARE_BLOCK = 0x6, + COMMIT_BLOCK = 0x7 +}; + +/** + * mark_range_ro_smc - Marks a range of memory read-only using the RKP smcID + * + * Callers should ensure the inputs always resolve to a fixed value, e.g. + * _stext. Should be used for large regions of memory spanning multiple pages + */ +int mark_range_ro_smc(uint64_t start, uint64_t end); + +/** + * add_jump_entry_lookup - loads the jet table into the hypervisor + * + * Sends the start of the jump entry struct lookup data structure and the size + * of it to the hypervisor. The hypervisor will use this to determine if a + * jump entry is valid or not, and if so, allow it to patch the jump entry. + */ +int add_jump_entry_lookup(uint64_t vaddr, uint64_t size); + +/** + * lock_rkp - removes all RKP-related SMC calls + * + * Locks down the hypervisor side of RKP so that it may not be called into + */ +int lock_rkp(void); + +/** + * prepare_hugepage - sets up a split hugepage in the hypervisor + * + * Prepares a new page table for splitting a 2MB hugepage into 4KB pages + * in the hypervisor + */ +int prepare_hugepage(uint64_t addr); + +/** + * commit_hugepage - updates TLB for hugepage split by prepare_hugepage + * + * Commits a hugepage split by prepare_hugepage into the calling CPU's + * TLB and page table, must be called from a context where it is impossible + * for the break-before-make semantics to affect other processor's execution + */ +int commit_hugepage(void); + +#endif /* _RKP_HVC_API_H_ */ diff --git a/drivers/sensors/Android.mk b/drivers/sensors/Android.mk new file mode 100644 index 000000000000..98fab6cb103f --- /dev/null +++ b/drivers/sensors/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := sensors_class.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/sensors/Kbuild b/drivers/sensors/Kbuild new file mode 100644 index 000000000000..b50ef6c2c4d8 --- /dev/null +++ b/drivers/sensors/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += sensors_class.o \ No newline at end of file diff --git a/drivers/sensors/Makefile b/drivers/sensors/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/sensors/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/sensors/sensors_class.c b/drivers/sensors/sensors_class.c new file mode 100644 index 000000000000..1e3b61cbfc77 --- /dev/null +++ b/drivers/sensors/sensors_class.c @@ -0,0 +1,567 @@ +/* Copyright (c) 2013-2014, The Linux Foundation. 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 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define APPLY_MASK 0x00000001 + +#define CMD_W_L_MASK 0x00 +#define CMD_W_H_MASK 0x10 +#define CMD_W_H_L 0x10 +#define CMD_MASK 0xF +#define DATA_MASK 0xFFFF0000 +#define DATA_AXIS_SHIFT 17 +#define DATA_APPLY_SHIFT 16 +/* + * CMD_GET_PARAMS(BIT, PARA, DATA) combine high 16 bit and low 16 bit + * as one params + */ + +#define CMD_GET_PARAMS(BIT, PARA, DATA) \ + ((BIT) ? \ + ((DATA) & DATA_MASK) \ + : ((PARA) \ + | (((DATA) & DATA_MASK) >> 16))) + + +/* + * CMD_DO_CAL sensor do calibrate command, when do sensor calibrate must use + * this. + * AXIS_X,AXIS_Y,AXIS_Z write axis params to driver like accelerometer + * magnetometer,gyroscope etc. + * CMD_W_THRESHOLD_H,CMD_W_THRESHOLD_L,CMD_W_BIAS write theshold and bias + * params to proximity driver. + * CMD_W_FACTOR,CMD_W_OFFSET write factor and offset params to light + * sensor driver. + * CMD_COMPLETE when one sensor receive calibrate parameters complete, it + * must use this command to end receive the parameters and send the + * parameters to sensor. + */ + +enum { + CMD_DO_CAL = 0x0, + CMD_W_OFFSET_X, + CMD_W_OFFSET_Y, + CMD_W_OFFSET_Z, + CMD_W_THRESHOLD_H, + CMD_W_THRESHOLD_L, + CMD_W_BIAS, + CMD_W_OFFSET, + CMD_W_FACTOR, + CMD_W_RANGE, + CMD_COMPLETE, + CMD_COUNT +}; + +int cal_map[] = { + 0, + offsetof(struct cal_result_t, offset_x), + offsetof(struct cal_result_t, offset_y), + offsetof(struct cal_result_t, offset_z), + offsetof(struct cal_result_t, threshold_h), + offsetof(struct cal_result_t, threshold_l), + offsetof(struct cal_result_t, bias), + offsetof(struct cal_result_t, offset[0]), + offsetof(struct cal_result_t, offset[1]), + offsetof(struct cal_result_t, offset[2]), + offsetof(struct cal_result_t, factor), + offsetof(struct cal_result_t, range), +}; + +static struct class *sensors_class; + +DECLARE_RWSEM(sensors_list_lock); +LIST_HEAD(sensors_list); + +static ssize_t sensors_name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%s\n", sensors_cdev->name); +} + +static ssize_t sensors_vendor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%s\n", sensors_cdev->vendor); +} + +static ssize_t sensors_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", sensors_cdev->version); +} + +static ssize_t sensors_handle_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", sensors_cdev->handle); +} + +static ssize_t sensors_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", sensors_cdev->type); +} + +static ssize_t sensors_max_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", sensors_cdev->max_delay); +} + +static ssize_t sensors_flags_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", sensors_cdev->flags); +} + +static ssize_t sensors_max_range_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%s\n", sensors_cdev->max_range); +} + +static ssize_t sensors_resolution_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%s\n", sensors_cdev->resolution); +} + +static ssize_t sensors_power_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%s\n", sensors_cdev->sensor_power); +} + +static ssize_t sensors_min_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", sensors_cdev->min_delay); +} + +static ssize_t sensors_fifo_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", + sensors_cdev->fifo_reserved_event_count); +} + +static ssize_t sensors_fifo_max_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", + sensors_cdev->fifo_max_event_count); +} + +static ssize_t sensors_enable_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + unsigned long data = 0; + + ret = kstrtoul(buf, 10, &data); + if (ret) + return ret; + if (data > 1) { + dev_err(dev, "Invalid value of input, input=%ld\n", data); + return -EINVAL; + } + + if (sensors_cdev->sensors_enable == NULL) { + dev_err(dev, "Invalid sensor class enable handle\n"); + return -EINVAL; + } + ret = sensors_cdev->sensors_enable(sensors_cdev, data); + if (ret) + return ret; + + sensors_cdev->enabled = data; + return size; +} + + +static ssize_t sensors_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%u\n", + sensors_cdev->enabled); +} + +static ssize_t sensors_delay_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + unsigned long data = 0; + + ret = kstrtoul(buf, 10, &data); + if (ret) + return ret; + /* The data unit is millisecond, the min_delay unit is microseconds. */ + if ((data * 1000) < sensors_cdev->min_delay) { + dev_err(dev, "Invalid value of delay, delay=%ld\n", data); + return -EINVAL; + } + if (sensors_cdev->sensors_poll_delay == NULL) { + dev_err(dev, "Invalid sensor class delay handle\n"); + return -EINVAL; + } + ret = sensors_cdev->sensors_poll_delay(sensors_cdev, data); + if (ret) + return ret; + + sensors_cdev->delay_msec = data; + return size; +} + +static ssize_t sensors_delay_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%u\n", + sensors_cdev->delay_msec); +} + +static ssize_t sensors_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + int ret; + + if (sensors_cdev->sensors_self_test == NULL) { + dev_err(dev, "Invalid sensor class self test handle\n"); + return -EINVAL; + } + + ret = sensors_cdev->sensors_self_test(sensors_cdev); + if (ret) + dev_warn(dev, "self test failed.(%d)\n", ret); + + return snprintf(buf, PAGE_SIZE, "%s\n", + ret ? "fail" : "pass"); +} + +static ssize_t sensors_max_latency_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + unsigned long latency; + int ret = -EINVAL; + + ret = kstrtoul(buf, 10, &latency); + if (ret) + return ret; + + if (latency > sensors_cdev->max_delay) { + dev_err(dev, "max_latency(%lu) is greater than max_delay(%u)\n", + latency, sensors_cdev->max_delay); + return -EINVAL; + } + + if (sensors_cdev->sensors_set_latency == NULL) { + dev_err(dev, "Invalid sensor calss set latency handle\n"); + return -EINVAL; + } + + /* Disable batching for this sensor */ + if ((latency < sensors_cdev->delay_msec) && (latency != 0)) { + dev_err(dev, "max_latency is less than delay_msec\n"); + return -EINVAL; + } + + ret = sensors_cdev->sensors_set_latency(sensors_cdev, latency); + if (ret) + return ret; + + sensors_cdev->max_latency = latency; + + return size; +} + +static ssize_t sensors_max_latency_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, + "%u\n", sensors_cdev->max_latency); +} + +static ssize_t sensors_flush_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + unsigned long data = 0; + + ret = kstrtoul(buf, 10, &data); + if (ret) + return ret; + if (data != 1) { + dev_err(dev, "Flush: Invalid value of input, input=%ld\n", + data); + return -EINVAL; + } + + if (sensors_cdev->sensors_flush == NULL) { + dev_err(dev, "Invalid sensor class flush handle\n"); + return -EINVAL; + } + ret = sensors_cdev->sensors_flush(sensors_cdev); + if (ret) + return ret; + + return size; +} + +static ssize_t sensors_flush_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, + "Flush handler %s\n", + (sensors_cdev->sensors_flush == NULL) + ? "not exist" : "exist"); +} + +static ssize_t sensors_enable_wakeup_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + ssize_t ret; + unsigned long enable; + + if (sensors_cdev->sensors_enable_wakeup == NULL) { + dev_err(dev, "Invalid sensor class enable_wakeup handle\n"); + return -EINVAL; + } + + ret = kstrtoul(buf, 10, &enable); + if (ret) + return ret; + + enable = enable ? 1 : 0; + ret = sensors_cdev->sensors_enable_wakeup(sensors_cdev, enable); + if (ret) + return ret; + + sensors_cdev->wakeup = enable; + + return size; +} + +static ssize_t sensors_enable_wakeup_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", sensors_cdev->wakeup); +} + + +static ssize_t sensors_calibrate_show(struct device *dev, + struct device_attribute *atte, char *buf) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + if (sensors_cdev->params == NULL) { + dev_err(dev, "Invalid sensor params\n"); + return -EINVAL; + } + return snprintf(buf, PAGE_SIZE, "%s\n", sensors_cdev->params); +} + +static ssize_t sensors_calibrate_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t size) +{ + struct sensors_classdev *sensors_cdev = dev_get_drvdata(dev); + ssize_t ret = -EINVAL; + long data; + int axis, apply_now; + int cmd, bit_h; + + ret = kstrtol(buf, 0, &data); + if (ret) + return ret; + dev_dbg(dev, "data = %lx\n", data); + cmd = data & CMD_MASK; + if (cmd == CMD_DO_CAL) { + if (sensors_cdev->sensors_calibrate == NULL) { + dev_err(dev, "Invalid calibrate handle\n"); + return -EINVAL; + } + /* parse the data to get the axis and apply_now value*/ + apply_now = (int)(data >> DATA_APPLY_SHIFT) & APPLY_MASK; + axis = (int)data >> DATA_AXIS_SHIFT; + dev_dbg(dev, "apply_now = %d, axis = %d\n", apply_now, axis); + ret = sensors_cdev->sensors_calibrate(sensors_cdev, + axis, apply_now); + if (ret) + return ret; + } else { + if (sensors_cdev->sensors_write_cal_params == NULL) { + dev_err(dev, + "Invalid write_cal_params handle\n"); + return -EINVAL; + } + bit_h = (data & CMD_W_H_L) >> 4; + if (cmd > CMD_DO_CAL && cmd < CMD_COMPLETE) { + char *p = (char *)(&sensors_cdev->cal_result) + + cal_map[cmd]; + *(int *)p = CMD_GET_PARAMS(bit_h, *(int *)p, data); + } else if (cmd == CMD_COMPLETE) { + ret = sensors_cdev->sensors_write_cal_params + (sensors_cdev, &sensors_cdev->cal_result); + } else { + dev_err(dev, "Invalid command\n"); + return -EINVAL; + } + } + return size; +} +/*add for sar sensor register zhanghaipeng.wt bengin*/ +static DEVICE_ATTR(name, 0444, sensors_name_show, NULL); +static DEVICE_ATTR(vendor, 0444, sensors_vendor_show, NULL); +static DEVICE_ATTR(version, 0444, sensors_version_show, NULL); +static DEVICE_ATTR(handle, 0444, sensors_handle_show, NULL); +static DEVICE_ATTR(type, 0444, sensors_type_show, NULL); +static DEVICE_ATTR(max_range, 0444, sensors_max_range_show, NULL); +static DEVICE_ATTR(resolution, 0444, sensors_resolution_show, NULL); +static DEVICE_ATTR(sensor_power, 0444, sensors_power_show, NULL); +static DEVICE_ATTR(min_delay, 0444, sensors_min_delay_show, NULL); +static DEVICE_ATTR(fifo_reserved_event_count, 0444, sensors_fifo_event_show, NULL); +static DEVICE_ATTR(fifo_max_event_count, 0444, sensors_fifo_max_show, NULL); +static DEVICE_ATTR(max_delay, 0444, sensors_max_delay_show, NULL); +static DEVICE_ATTR(flags, 0444, sensors_flags_show, NULL); +static DEVICE_ATTR(enable, 0664, sensors_enable_show, sensors_enable_store); +static DEVICE_ATTR(enable_wakeup, 0664, sensors_enable_wakeup_show,sensors_enable_wakeup_store); +static DEVICE_ATTR(poll_delay, 0664, sensors_delay_show, sensors_delay_store); +static DEVICE_ATTR(self_test, 0440, sensors_test_show, NULL); +static DEVICE_ATTR(max_latency, 0660, sensors_max_latency_show,sensors_max_latency_store); +static DEVICE_ATTR(flush, 0660, sensors_flush_show, sensors_flush_store); +static DEVICE_ATTR(calibrate, 0664, sensors_calibrate_show,sensors_calibrate_store); + +static struct device_attribute *sensors_class_attrs[] = { + &dev_attr_name, + &dev_attr_vendor, + &dev_attr_version, + &dev_attr_handle, + &dev_attr_type, + &dev_attr_max_range, + &dev_attr_resolution, + &dev_attr_sensor_power, + &dev_attr_min_delay, + &dev_attr_fifo_reserved_event_count, + &dev_attr_fifo_max_event_count, + &dev_attr_max_delay, + &dev_attr_flags, + &dev_attr_enable, + &dev_attr_enable_wakeup, + &dev_attr_poll_delay, + &dev_attr_self_test, + &dev_attr_max_latency, + &dev_attr_flush, + &dev_attr_calibrate +}; +/*add for sar sensor register zhanghaipeng.wt end*/ + +/** + * sensors_classdev_register - register a new object of sensors_classdev class. + * @parent: The device to register. + * @sensors_cdev: the sensors_classdev structure for this device. +*/ +int sensors_classdev_register(struct device *parent, + struct sensors_classdev *sensors_cdev) +{ + int ret = 0; + int i = 0; + printk("sensors_classdev_register,%s\n",sensors_cdev->name); + sensors_cdev->dev = device_create(sensors_class, parent, 0, + sensors_cdev, "%s", sensors_cdev->name); + if (IS_ERR(sensors_cdev->dev)) + { + printk("sensors_classdev_register fail\n"); + return PTR_ERR(sensors_cdev->dev); + } + down_write(&sensors_list_lock); + list_add_tail(&sensors_cdev->node, &sensors_list); + up_write(&sensors_list_lock); + for (i = 0; i < ARRAY_SIZE(sensors_class_attrs); ++i) { + ret = device_create_file(sensors_cdev->dev, sensors_class_attrs[i]); + if (ret) + printk("i = %d,device_create_file register fail\n",i); + } + printk("Registered sensors device: %s,ret = %d\n", + sensors_cdev->name,ret); + return 0; +} +EXPORT_SYMBOL(sensors_classdev_register); + +/** + * sensors_classdev_unregister - unregister a object of sensors class. + * @sensors_cdev: the sensor device to unregister + * Unregister a previously registered via sensors_classdev_register object. +*/ +void sensors_classdev_unregister(struct sensors_classdev *sensors_cdev) +{ + printk("%s\n",__func__); + device_unregister(sensors_cdev->dev); + down_write(&sensors_list_lock); + list_del(&sensors_cdev->node); + up_write(&sensors_list_lock); +} +EXPORT_SYMBOL(sensors_classdev_unregister); + +static int __init sensors_init(void) +{ + printk("moto sensor init!\n"); + sensors_class = class_create(THIS_MODULE, "sensors"); + if (IS_ERR(sensors_class)) + { + printk("sensor init fail!\n"); + return PTR_ERR(sensors_class); + } + printk("sensor init success!\n"); + //sensors_class->dev_attrs = sensors_class_attrs; + return 0; +} + +static void __exit sensors_exit(void) +{ + class_destroy(sensors_class); +} + +subsys_initcall(sensors_init); +module_exit(sensors_exit); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/tzlog_dump/Android.mk b/drivers/tzlog_dump/Android.mk new file mode 100644 index 000000000000..3a70e6ae6933 --- /dev/null +++ b/drivers/tzlog_dump/Android.mk @@ -0,0 +1,13 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := tzlog_dump.ko +LOCAL_MODULE_TAGS := optional +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_info.ko +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/tzlog_dump/Kbuild b/drivers/tzlog_dump/Kbuild new file mode 100644 index 000000000000..72c4fbf7ce22 --- /dev/null +++ b/drivers/tzlog_dump/Kbuild @@ -0,0 +1,8 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += tzlog_dump.o +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_annotate/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_info/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(KBUILD_OUTPUT)/Module.symver diff --git a/drivers/tzlog_dump/Makefile b/drivers/tzlog_dump/Makefile new file mode 100644 index 000000000000..34b09dd815a7 --- /dev/null +++ b/drivers/tzlog_dump/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean \ No newline at end of file diff --git a/drivers/tzlog_dump/tzlog_dump.c b/drivers/tzlog_dump/tzlog_dump.c new file mode 100644 index 000000000000..c61144c50240 --- /dev/null +++ b/drivers/tzlog_dump/tzlog_dump.c @@ -0,0 +1,343 @@ +/* Copyright (c) 2011-2015,2017 The Linux Foundation. All rights reserved. + * Copyright (C) 2018 Motorola Mobility LLC + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Check memory_dump.h to verify this is not going over the max or + * conflicting with another entry. Also must match BL + */ +#define MSM_DUMP_DATA_TZ_LOG 0x11F + +#define TZBSP_DIAG_SIZE 0x3000 +#define TZBSP_MAGIC_NUMBER 0x747A6461 +#define TZBSP_VERSION 0x90001 +#define TZBSP_MAX_CPU_COUNT 0x08 +#define TZBSP_DIAG_NUM_OF_VMID 16 +#define TZBSP_DIAG_VMID_DESC_LEN 7 +#define TZBSP_DIAG_INT_NUM 64 +#define TZBSP_MAX_INT_DESC 16 +#define TZBSP_AES_256_ENCRYPTED_KEY_SIZE 256 +#define TZBSP_NONCE_LEN 12 +#define TZBSP_TAG_LEN 16 + +struct tzdbg_vmid_t { + uint8_t vmid; + uint8_t desc[TZBSP_DIAG_VMID_DESC_LEN]; +}; + +struct tzdbg_boot_info_t { + uint32_t wb_entry_cnt; + uint32_t wb_exit_cnt; + uint32_t pc_entry_cnt; + uint32_t pc_exit_cnt; + uint32_t psci_entry_cnt; + uint32_t psci_exit_cnt; + uint64_t warm_jmp_addr; + uint32_t warm_jmp_instr; +}; + +struct tzdbg_reset_info_t { + uint32_t reset_type; + uint32_t reset_cnt; +}; + +struct tzdbg_int_t { + uint16_t int_info; + uint8_t avail; + uint8_t spare; + uint32_t int_num; + uint8_t int_desc[TZBSP_MAX_INT_DESC]; + uint64_t int_count[TZBSP_MAX_CPU_COUNT]; +}; + +struct tzbsp_diag_wakeup_info_t { + uint32_t HPPIR; + uint32_t AHPPIR; +}; + +struct tzdbg_log_pos_t { + uint16_t wrap; + uint16_t offset; +}; + +struct tzdbg_log_t { + struct tzdbg_log_pos_t log_pos; + uint8_t log_buf[]; +}; + +struct tzdbg_t { + uint32_t magic_num; + uint32_t version; + uint32_t cpu_count; + uint32_t vmid_info_off; + uint32_t boot_info_off; + uint32_t reset_info_off; + uint32_t int_info_off; + uint32_t ring_off; + uint32_t ring_len; + uint32_t wakeup_info_off; + struct tzdbg_vmid_t vmid_info[TZBSP_DIAG_NUM_OF_VMID]; + struct tzdbg_boot_info_t boot_info[TZBSP_MAX_CPU_COUNT]; + struct tzdbg_reset_info_t reset_info[TZBSP_MAX_CPU_COUNT]; + uint32_t num_interrupts; + struct tzdbg_int_t int_info[TZBSP_DIAG_INT_NUM]; + struct tzbsp_diag_wakeup_info_t wakeup_info[TZBSP_MAX_CPU_COUNT]; + uint8_t key[TZBSP_AES_256_ENCRYPTED_KEY_SIZE]; + uint8_t nonce[TZBSP_NONCE_LEN]; + uint8_t tag[TZBSP_TAG_LEN]; + struct tzdbg_log_t ring_buffer; +}; + +struct tz_dump_platform_data { + phys_addr_t mem_address; + size_t mem_size; +}; + +static struct tzdbg_t *tzdbg_data; + +#define MSMDBG(fmt, args...) mmi_annotate(fmt, ##args) + +#define MSMWDTD_IFWDOG(fmt, args...) do { \ + if (bi_powerup_reason() == PU_REASON_WDOG_AP_RESET) \ + MSMDBG(fmt, ##args); \ +} while (0) + +static void tzlog_dump_show_boot_info(void) +{ + int cpu; + int power_collapsed; + struct tzdbg_boot_info_t *ptr; + + ptr = (struct tzdbg_boot_info_t *) + ((u8 *)tzdbg_data + tzdbg_data->boot_info_off); + + MSMWDTD_IFWDOG("\n--- TZ Power Collapse Counters\n"); + MSMWDTD_IFWDOG(" | WarmEntry : WarmExit : TermEntry :"); + MSMWDTD_IFWDOG(" TermExit : PsciEntry : PsciExit : JumpAddr |\n"); + for (cpu = 0; cpu < tzdbg_data->cpu_count; cpu++) { + power_collapsed = ptr->wb_entry_cnt + + ptr->pc_exit_cnt - ptr->pc_entry_cnt; + if (cpu) + power_collapsed--; + MSMWDTD_IFWDOG("CPU%d | %8x : %8x : %8x : %8x : %8x : %8x : " + "%llx | %sPC\n", + cpu, + ptr->wb_entry_cnt, + ptr->wb_exit_cnt, + ptr->pc_entry_cnt, + ptr->pc_exit_cnt, + ptr->psci_entry_cnt, + ptr->psci_exit_cnt, + ptr->warm_jmp_addr, + power_collapsed ? "IN-" : "NOT-"); + ptr++; + } +} + +static void tzlog_dump_show_log(void) +{ + struct tzdbg_log_t *log_ptr; + const char *log_buf, *p, *start; + + log_buf = (const char *)tzdbg_data + tzdbg_data->ring_off; + log_ptr = (struct tzdbg_log_t *)(log_buf - + offsetof(struct tzdbg_log_t, log_buf)); + + if (log_ptr->log_pos.offset >= tzdbg_data->ring_len) + return; + MSMWDTD_IFWDOG("--- TZ Log start ---\n"); + if (log_ptr->log_pos.wrap) { + for (start = log_buf + log_ptr->log_pos.offset, p = start; + p < (log_buf + tzdbg_data->ring_len); p++) { + if (isprint(*p)) + MSMWDTD_IFWDOG("%c", *p); + else if ((p > start) && isprint(*(p-1))) + MSMWDTD_IFWDOG("\n"); + } + } + for (start = log_buf, p = start; + p < (log_buf + log_ptr->log_pos.offset); p++) { + if (isprint(*p)) + MSMWDTD_IFWDOG("%c", *p); + else if ((p > start) && isprint(*(p-1))) + MSMWDTD_IFWDOG("\n"); + } + MSMWDTD_IFWDOG("\n--- TZ Log end ---\n"); +} + +static int tzlog_dump_annotate(void) +{ + if (!tzdbg_data || + tzdbg_data->magic_num != TZBSP_MAGIC_NUMBER) { + MSMWDTD_IFWDOG("No valid backup\n"); + return 0; + } + + tzlog_dump_show_boot_info(); + tzlog_dump_show_log(); + + return 0; +} + +static void tzlog_dump_table_register(struct device *dev, + struct tz_dump_platform_data *pdata) +{ + int err = 0; + struct msm_dump_entry dump_entry; + struct msm_dump_data *tz_dump_data; + + tz_dump_data = kzalloc(sizeof(struct msm_dump_data), + GFP_KERNEL); + + if (!tz_dump_data) { + dev_err(dev, "Cannot alloc dump data structure.\n"); + goto err; + } + + tz_dump_data->addr = pdata->mem_address; + tz_dump_data->len = pdata->mem_size; + dump_entry.id = MSM_DUMP_DATA_TZ_LOG; + dump_entry.addr = virt_to_phys(tz_dump_data); +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) + err = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry); +#else + //walkaround the compile error in GKI +#ifdef CONFIG_QGKI + err = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry); +#endif +#endif + if (err) { + dev_err(dev, "Registering dump data failed.\n"); + kfree(tz_dump_data); + } +err: + return; +} + +static int tzlog_dump_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct tz_dump_platform_data *pdata; + struct resource res; + struct device_node *node; + int err = 0; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + err = -ENOMEM; + goto err; + } + + /* Get reserved memory region from Device-tree */ + node = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!node) { + dev_err(dev, "No %s specified\n", "memory-region"); + goto err; + } + + err = of_address_to_resource(node, 0, &res); + of_node_put(node); + if (err) { + dev_err(dev, "No memory address assigned to the region\n"); + goto err; + } + + pdata->mem_size = resource_size(&res); + pdata->mem_address = res.start; + + dev_info(dev, "tzlog_dump_size %zx", pdata->mem_size); + dev_info(dev, "tzlog_dump_addr %lx", (unsigned long)pdata->mem_address); + + if (pdata->mem_size < TZBSP_DIAG_SIZE) { + dev_err(dev, "Mem reserve too small %zx/%xu\n", + pdata->mem_size, TZBSP_DIAG_SIZE); + err = -ENOMEM; + goto err; + } + + tzlog_dump_table_register(dev, pdata); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0) + tzdbg_data = ioremap_wc(pdata->mem_address, pdata->mem_size); +#else + tzdbg_data = dma_remap(dev, NULL, pdata->mem_address, + pdata->mem_size, 0); +#endif + + if (!tzdbg_data) { + dev_err(dev, "Cannot remap buffer %pa size %zx\n", + &pdata->mem_address, pdata->mem_size); + err = -ENOMEM; + goto err; + } + + tzlog_dump_annotate(); + memset_io(tzdbg_data, 0, pdata->mem_size); +err: + return err; +} + +static int tzlog_dump_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id tzlog_dump_match[] = { + { .compatible = "mmi,tzlog-dump" }, + {} +}; + +static struct platform_driver tzlog_dump_driver = { + .probe = tzlog_dump_probe, + .remove = tzlog_dump_remove, + .driver = { + .name = "tzlog_dump", + .of_match_table = tzlog_dump_match, + }, +}; + +static int __init tzlog_dump_init(void) +{ + return platform_driver_register(&tzlog_dump_driver); +} + +static void __exit tzlog_dump_exit(void) +{ + platform_driver_unregister(&tzlog_dump_driver); +} + +module_init(tzlog_dump_init); +module_exit(tzlog_dump_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TZ Log dump driver"); diff --git a/drivers/usb/typec/adapter_class/Android.mk b/drivers/usb/typec/adapter_class/Android.mk new file mode 100755 index 000000000000..2f8b28bb0a51 --- /dev/null +++ b/drivers/usb/typec/adapter_class/Android.mk @@ -0,0 +1,10 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := adapter_class.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +KBUILD_OPTIONS += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) +include $(DLKM_DIR)/AndroidKernelModule.mk + diff --git a/drivers/usb/typec/adapter_class/Kbuild b/drivers/usb/typec/adapter_class/Kbuild new file mode 100755 index 000000000000..f003c3c245e9 --- /dev/null +++ b/drivers/usb/typec/adapter_class/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += adapter_class.o diff --git a/drivers/usb/typec/adapter_class/Makefile b/drivers/usb/typec/adapter_class/Makefile new file mode 100755 index 000000000000..da93ae6f853e --- /dev/null +++ b/drivers/usb/typec/adapter_class/Makefile @@ -0,0 +1,30 @@ +ifeq ($(strip $(MODULE_KERNEL_VERSION)), 5.15) +all: modules + +modules: + $(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 + +else + +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + +endif diff --git a/drivers/usb/typec/adapter_class/adapter_class.c b/drivers/usb/typec/adapter_class/adapter_class.c new file mode 100644 index 000000000000..133040f1c2f6 --- /dev/null +++ b/drivers/usb/typec/adapter_class/adapter_class.c @@ -0,0 +1,402 @@ +/* Copyright (c) 2020, 2021 The Linux Foundation. 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 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct class *adapter_class; + +static ssize_t name_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adapter_device *adapter_dev = to_adapter_device(dev); + + return snprintf(buf, 20, "%s\n", + adapter_dev->props.alias_name ? + adapter_dev->props.alias_name : "anonymous"); +} + +static ssize_t pdo_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adapter_device *adapter_dev = to_adapter_device(dev); + struct adapter_power_cap cap; + ssize_t size = 0; + int i; + + cap.nr = 0; + cap.pdp = 0; + + adapter_dev = get_adapter_by_name("pd_adapter"); + if (adapter_dev) + pr_err("Found PD adapter [%s]\n", + adapter_dev->props.alias_name); + else + pr_err("*** Error : can't find PD adapter ***\n"); + + for (i = 0; i < ADAPTER_CAP_MAX_NR; i++) { + cap.max_mv[i] = 0; + cap.min_mv[i] = 0; + cap.ma[i] = 0; + cap.type[i] = 0; + cap.pwr_limit[i] = 0; + } + + adapter_dev_get_cap(adapter_dev, MMI_PD_ALL, &cap); + for (i = 0; i < cap.nr; i++) { + pr_err("%d: mV:%d,%d mA:%d type:%d\n", i, + cap.max_mv[i], cap.min_mv[i], cap.ma[i], cap.type[i]); + + size += snprintf(buf + size, PAGE_SIZE - size, + "%d: mV:%d,%d mA:%d type:%d\n", i, + cap.max_mv[i], cap.min_mv[i], cap.ma[i], cap.type[i]); + } + + return size; +} + +static ssize_t apdo_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adapter_device *adapter_dev = to_adapter_device(dev); + struct adapter_power_cap cap; + ssize_t size = 0; + int i; + + cap.nr = 0; + cap.pdp = 0; + + adapter_dev = get_adapter_by_name("pd_adapter"); + if (adapter_dev) + pr_err("Found PD adapter [%s]\n", + adapter_dev->props.alias_name); + else + pr_err("*** Error : can't find PD adapter ***\n"); + + for (i = 0; i < ADAPTER_CAP_MAX_NR; i++) { + cap.max_mv[i] = 0; + cap.min_mv[i] = 0; + cap.ma[i] = 0; + cap.type[i] = 0; + cap.pwr_limit[i] = 0; + } + + adapter_dev_get_cap(adapter_dev, MMI_PD_APDO, &cap); + for (i = 0; i < cap.nr; i++) { + pr_err("%d: mV:%d,%d mA:%d type:%d pwr_limit:%d pdp:%d\n", i, + cap.max_mv[i], cap.min_mv[i], cap.ma[i], + cap.type[i], cap.pwr_limit[i], cap.pdp); + + size += snprintf(buf + size, PAGE_SIZE - size, + "%d: mV:%d,%d mA:%d type:%d pwr_limit:%d pdp:%d\n", i, + cap.max_mv[i], cap.min_mv[i], cap.ma[i], + cap.type[i], cap.pwr_limit[i], cap.pdp); + } + + return size; +} + +static ssize_t pdo_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adapter_device *adapter_dev = to_adapter_device(dev); + int ret; + + adapter_dev = get_adapter_by_name("pd_adapter"); + if (adapter_dev) + pr_err("Found PD adapter [%s]\n", + adapter_dev->props.alias_name); + else + pr_err("*** Error : can't find PD adapter ***\n"); + + + ret = adapter_dev_set_cap(adapter_dev, MMI_PD_FIXED, + 1, 9000, 3000); + if (ret == MMI_ADAPTER_OK) + return snprintf(buf, 40, "select pdo to 9v3a mode ok\n"); + else + return snprintf(buf, 40, "select pdo to 9v3a mode fail\n"); +} + +static ssize_t apdo_test_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adapter_device *adapter_dev = to_adapter_device(dev); + int ret; + int vol = 5000; + + adapter_dev = get_adapter_by_name("pd_adapter"); + if (adapter_dev) + pr_err("Found PD adapter [%s]\n", + adapter_dev->props.alias_name); + else + pr_err("*** Error : can't find PD adapter ***\n"); + + ret = adapter_dev_set_cap(adapter_dev, MMI_PD_APDO_START, 3, vol, 3000); + if (ret == MMI_ADAPTER_REJECT) + return snprintf(buf, 40, "start apdo reject\n"); + else if (ret != 0) + return snprintf(buf, 40, "start apdo err\n"); + + while (vol <10000) { + adapter_dev_set_cap(adapter_dev, MMI_PD_APDO, + 3, vol, 3000); + vol += 200; + msleep(200); + } + + ret = adapter_dev_set_cap(adapter_dev, MMI_PD_APDO_END, 3, vol, 3000); + if (ret == MMI_ADAPTER_REJECT) + return snprintf(buf, 40, "stop apdo reject\n"); + else if (ret != 0) + return snprintf(buf, 40, "stop apdo err\n"); + + return snprintf(buf, 40, "select apdo mode ok\n"); +} + + +static void adapter_device_release(struct device *dev) +{ + struct adapter_device *adapter_dev = to_adapter_device(dev); + + kfree(adapter_dev); +} + +int adapter_dev_get_property(struct adapter_device *adapter_dev, + enum adapter_property sta) +{ + if (adapter_dev != NULL && adapter_dev->ops != NULL && + adapter_dev->ops->get_property) + return adapter_dev->ops->get_property(adapter_dev, sta); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(adapter_dev_get_property); + +int adapter_dev_get_status(struct adapter_device *adapter_dev, + struct adapter_status *sta) +{ + if (adapter_dev != NULL && adapter_dev->ops != NULL && + adapter_dev->ops->get_status) + return adapter_dev->ops->get_status(adapter_dev, sta); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(adapter_dev_get_status); + +int adapter_dev_get_output(struct adapter_device *adapter_dev, int *mV, int *mA) +{ + if (adapter_dev != NULL && adapter_dev->ops != NULL && + adapter_dev->ops->get_output) + return adapter_dev->ops->get_output(adapter_dev, mV, mA); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(adapter_dev_get_output); + +int adapter_dev_set_cap(struct adapter_device *adapter_dev, + enum adapter_cap_type type, int pdo_idx, + int mV, int mA) +{ + if (adapter_dev != NULL && adapter_dev->ops != NULL && + adapter_dev->ops->set_cap) + return adapter_dev->ops->set_cap(adapter_dev, type, pdo_idx, mV, mA); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(adapter_dev_set_cap); + + +int adapter_dev_get_cap(struct adapter_device *adapter_dev, + enum adapter_cap_type type, + struct adapter_power_cap *cap) +{ + if (adapter_dev != NULL && adapter_dev->ops != NULL && + adapter_dev->ops->get_cap) + return adapter_dev->ops->get_cap(adapter_dev, type, cap); + + return -ENOTSUPP; +} +EXPORT_SYMBOL(adapter_dev_get_cap); + +static DEVICE_ATTR_RO(name); +static DEVICE_ATTR_RO(pdo_info); +static DEVICE_ATTR_RO(pdo_test); +static DEVICE_ATTR_RO(apdo_info); +static DEVICE_ATTR_RO(apdo_test); + +static struct attribute *adapter_class_attrs[] = { + &dev_attr_name.attr, + &dev_attr_pdo_info.attr, + &dev_attr_apdo_info.attr, + &dev_attr_pdo_test.attr, + &dev_attr_apdo_test.attr, + NULL, +}; + +static const struct attribute_group adapter_group = { + .attrs = adapter_class_attrs, +}; + +static const struct attribute_group *adapter_groups[] = { + &adapter_group, + NULL, +}; + +int register_adapter_device_notifier(struct adapter_device *adapter_dev, + struct notifier_block *nb) +{ + int ret = 0; + + if (!adapter_dev) + return -ENODEV; + + ret = srcu_notifier_chain_register(&adapter_dev->evt_nh, nb); + return ret; +} +EXPORT_SYMBOL(register_adapter_device_notifier); + +int unregister_adapter_device_notifier(struct adapter_device *adapter_dev, + struct notifier_block *nb) +{ + if (!adapter_dev) + return -ENODEV; + + return srcu_notifier_chain_unregister(&adapter_dev->evt_nh, nb); +} +EXPORT_SYMBOL(unregister_adapter_device_notifier); + +/** + * adapter_device_register - create and register a new object of + * adapter_device class. + * @name: the name of the new object + * @parent: a pointer to the parent device + * @devdata: an optional pointer to be stored for private driver use. + * The methods may retrieve it by using adapter_get_data(adapter_dev). + * @ops: the charger operations structure. + * + * Creates and registers new charger device. Returns either an + * ERR_PTR() or a pointer to the newly allocated device. + */ +struct adapter_device *adapter_device_register(const char *name, + struct device *parent, void *devdata, + const struct adapter_ops *ops, + const struct adapter_properties *props) +{ + struct adapter_device *adapter_dev; + static struct lock_class_key key; + struct srcu_notifier_head *head; + int rc; + + pr_notice("%s: name=%s\n", __func__, name); + adapter_dev = kzalloc(sizeof(*adapter_dev), GFP_KERNEL); + if (!adapter_dev) + return ERR_PTR(-ENOMEM); + + head = &adapter_dev->evt_nh; + srcu_init_notifier_head(head); + /* Rename srcu's lock to avoid LockProve warning */ + lockdep_init_map(&(&head->srcu)->dep_map, name, &key, 0); + mutex_init(&adapter_dev->ops_lock); + adapter_dev->dev.class = adapter_class; + adapter_dev->dev.parent = parent; + adapter_dev->dev.release = adapter_device_release; + dev_set_name(&adapter_dev->dev, "%s", name); + dev_set_drvdata(&adapter_dev->dev, devdata); + + /* Copy properties */ + if (props) { + memcpy(&adapter_dev->props, props, + sizeof(struct adapter_properties)); + } + rc = device_register(&adapter_dev->dev); + if (rc) { + kfree(adapter_dev); + return ERR_PTR(rc); + } + adapter_dev->ops = ops; + return adapter_dev; +} +EXPORT_SYMBOL(adapter_device_register); + +/** + * adapter_device_unregister - unregisters a switching charger device + * object. + * @adapter_dev: the switching charger device object to be unregistered + * and freed. + * + * Unregisters a previously registered via adapter_device_register object. + */ +void adapter_device_unregister(struct adapter_device *adapter_dev) +{ + if (!adapter_dev) + return; + + mutex_lock(&adapter_dev->ops_lock); + adapter_dev->ops = NULL; + mutex_unlock(&adapter_dev->ops_lock); + device_unregister(&adapter_dev->dev); +} +EXPORT_SYMBOL(adapter_device_unregister); + + +static int adapter_match_device_by_name(struct device *dev, + const void *data) +{ + const char *name = data; + + return strcmp(dev_name(dev), name) == 0; +} + +struct adapter_device *get_adapter_by_name(const char *name) +{ + struct device *dev; + + if (!name) + return (struct adapter_device *)NULL; + dev = class_find_device(adapter_class, NULL, name, + adapter_match_device_by_name); + + return dev ? to_adapter_device(dev) : NULL; + +} +EXPORT_SYMBOL(get_adapter_by_name); + +static void __exit adapter_class_exit(void) +{ + class_destroy(adapter_class); +} + +static int __init adapter_class_init(void) +{ + adapter_class = class_create(THIS_MODULE, "PD_charging_Adapter"); + if (IS_ERR(adapter_class)) { + pr_notice("Unable to create Charging Adapter class; errno = %ld\n", + PTR_ERR(adapter_class)); + return PTR_ERR(adapter_class); + } + adapter_class->dev_groups = adapter_groups; + return 0; +} + +subsys_initcall(adapter_class_init); +module_exit(adapter_class_exit); + +MODULE_DESCRIPTION("Adapter Class Device"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/usb/typec/mmi_tcpc/Android.mk b/drivers/usb/typec/mmi_tcpc/Android.mk new file mode 100644 index 000000000000..58dd4796f3d8 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/Android.mk @@ -0,0 +1,82 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifeq ($(USB_POWER_DELIVERY),true) + LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/adapter_class.ko +endif +KBUILD_OPTIONS_GKI += GKI_OBJ_MODULE_DIR=gki +KBUILD_OPTIONS += MODULE_KERNEL_VERSION=$(TARGET_KERNEL_VERSION) + +ifeq ($(TCPC_CLASS),true) + KERNEL_CFLAGS += CONFIG_TCPC_CLASS=y + KBUILD_OPTIONS += CONFIG_TCPC_CLASS=y +endif + +ifeq ($(TCPC_AW35616),true) +ifeq ($(TCPC_SGM7220),true) + KERNEL_CFLAGS += CONFIG_TCPC_AW35616=y CONFIG_TCPC_SGM7220=y + KBUILD_OPTIONS += CONFIG_TCPC_AW35616=y CONFIG_TCPC_SGM7220=y +endif +endif + +ifeq ($(TCPC_AW35616),true) + KERNEL_CFLAGS += CONFIG_TCPC_AW35616=y + KBUILD_OPTIONS += CONFIG_TCPC_AW35616=y + include $(CLEAR_VARS) + LOCAL_MODULE_TAGS := optional + LOCAL_MODULE := tcpc_aw35616.ko + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/AndroidKernelModule.mk +endif + +ifeq ($(TCPC_SGM7220),true) + KERNEL_CFLAGS += CONFIG_TCPC_SGM7220=y + KBUILD_OPTIONS += CONFIG_TCPC_SGM7220=y + include $(CLEAR_VARS) + LOCAL_MODULE_TAGS := optional + LOCAL_MODULE := tcpc_sgm7220.ko + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/AndroidKernelModule.mk +endif + +ifeq ($(TCPC_RT1711H),true) + KERNEL_CFLAGS += CONFIG_TCPC_RT1711H=y + KBUILD_OPTIONS += CONFIG_TCPC_RT1711H=y + + include $(CLEAR_VARS) + LOCAL_MODULE_TAGS := optional + LOCAL_MODULE := tcpc_rt1711h.ko + LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) + include $(DLKM_DIR)/AndroidKernelModule.mk +endif + +#ifeq ($(TCPC_CLASS),true) +# KERNEL_CFLAGS += CONFIG_TCPC_CLASS=y +# KBUILD_OPTIONS += CONFIG_TCPC_CLASS=y +#endif + +ifeq ($(USB_POWER_DELIVERY),true) + KERNEL_CFLAGS += CONFIG_USB_POWER_DELIVERY=y + KBUILD_OPTIONS += CONFIG_USB_POWER_DELIVERY=y +endif + +TCPC_MAX_POLLING_COUNT := $(shell echo $(TCPC_MAX_POLLING_COUNT) | grep -o "[0-9]\+") +ifneq ($(TCPC_MAX_POLLING_COUNT),) + KERNEL_CFLAGS += CONFIG_TCPC_MAX_POLLING_COUNT=$(TCPC_MAX_POLLING_COUNT) + KBUILD_OPTIONS += CONFIG_TCPC_MAX_POLLING_COUNT=$(TCPC_MAX_POLLING_COUNT) +endif + + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := tcpc_class.ko +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + + +include $(CLEAR_VARS) +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := rt_pd_manager.ko +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_discrete_charger_class.ko +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/usb/typec/mmi_tcpc/Kbuild b/drivers/usb/typec/mmi_tcpc/Kbuild new file mode 100644 index 000000000000..72f7413ba5bc --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/Kbuild @@ -0,0 +1,55 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include \ + -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/drivers/usb/typec/mmi_tcpc + +subdir-ccflags-y += -Wall -Werror -DCONFIG_RT_REGMAP + +EXTRA_CFLAGS += -DCONFIG_PD_DBG_INFO + +ifneq ($(filter m y,$(CONFIG_TCPC_CLASS)),) + EXTRA_CFLAGS += -DCONFIG_TCPC_CLASS +endif + +ifneq ($(filter m y,$(CONFIG_USB_POWER_DELIVERY)),) + EXTRA_CFLAGS += -DCONFIG_USB_POWER_DELIVERY +endif + +ifneq ($(filter m y,$(CONFIG_TCPC_RT1711H)),) + obj-m += tcpc_rt1711h.o +endif + +ifneq ($(filter m y,$(CONFIG_TCPC_AW35616)),) + obj-m += tcpc_aw35616.o +endif + +ifneq ($(filter m y,$(CONFIG_TCPC_SGM7220)),) + obj-m += tcpc_sgm7220.o +endif + +ifneq ($(CONFIG_TCPC_MAX_POLLING_COUNT),) + EXTRA_CFLAGS += -DCONFIG_TCPC_MAX_POLLING_COUNT=$(CONFIG_TCPC_MAX_POLLING_COUNT) +endif + +tcpc_class-objs += tcpci_core.o tcpci_typec.o tcpci_timer.o \ + tcpm.o tcpci.o pd_dbg_info.o tcpci_alert.o rt-regmap.o tcpci_dual_role.o + +ifneq ($(filter m y,$(CONFIG_USB_POWER_DELIVERY)),) +tcpc_class-objs += tcpci_event.o \ + pd_core.o pd_policy_engine.o pd_process_evt.o \ + pd_dpm_core.o pd_dpm_uvdm.o pd_dpm_alt_mode_dp.o pd_dpm_pdo_select.o\ + pd_dpm_reaction.o \ + pd_process_evt_snk.o pd_process_evt_src.o pd_process_evt_vdm.o \ + pd_process_evt_drs.o pd_process_evt_prs.o pd_process_evt_vcs.o \ + pd_process_evt_dbg.o pd_process_evt_tcp.o pd_process_evt_com.o \ + pd_policy_engine_src.o pd_policy_engine_snk.o pd_policy_engine_ufp.o pd_policy_engine_vcs.o \ + pd_policy_engine_dfp.o pd_policy_engine_dr.o pd_policy_engine_drs.o pd_policy_engine_prs.o \ + pd_policy_engine_dbg.o pd_policy_engine_com.o pd_dpm_alt_mode_dc.o pd_adapter.o \ + pd_dpm_mmi_adapter.o +endif + +obj-m += tcpc_class.o +obj-m += rt_pd_manager.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../adapter_class/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../power/mmi_discrete_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers diff --git a/drivers/usb/typec/mmi_tcpc/Makefile b/drivers/usb/typec/mmi_tcpc/Makefile new file mode 100644 index 000000000000..bff880501a3e --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/Makefile @@ -0,0 +1,34 @@ +ifeq ($(strip $(MODULE_KERNEL_VERSION)), 5.15) + +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../adapter_class/$(GKI_OBJ_MODULE_DIR)/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../../power/mmi_discrete_charger/$(GKI_OBJ_MODULE_DIR)/Module.symvers + +else + +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean + +endif diff --git a/drivers/usb/typec/mmi_tcpc/inc/pd_core.h b/drivers/usb/typec/mmi_tcpc/inc/pd_core.h new file mode 100644 index 000000000000..a593e39b5119 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/pd_core.h @@ -0,0 +1,1649 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#ifndef PD_CORE_H_ +#define PD_CORE_H_ + +#include +#include +#include "tcpci_timer.h" +#include "tcpci_event.h" +#include "pd_dbg_info.h" +#include "tcpm.h" + +#define PD_BUG_ON(x) WARN_ON(x) +/*---------------------------------------------------------------------------*/ + +#ifndef CONFIG_TCPC_SOURCE_VCONN +#undef CONFIG_USB_PD_VCONN_SWAP +#undef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID +#undef CONFIG_USB_PD_DFP_READY_DISCOVER_ID +#undef CONFIG_USB_PD_DISCOVER_CABLE_REQUEST_VCONN +#undef CONFIG_USB_PD_DISCOVER_CABLE_RETURN_VCONN +#undef CONFIG_USB_PD_ALT_MODE_SVID +#undef CONFIG_USB_PD_RESET_CABLE +#endif /* CONFIG_TCPC_SOURCE_VCONN */ + +#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID +#define CONFIG_PD_DISCOVER_CABLE_ID +#endif /* CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */ + +#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID +#undef CONFIG_PD_DISCOVER_CABLE_ID +#define CONFIG_PD_DISCOVER_CABLE_ID +#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */ + +#ifdef CONFIG_USB_PD_RESET_CABLE + +#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID +#define CONFIG_PD_SRC_RESET_CABLE +#endif /* CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */ + +#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID +#define CONFIG_PD_DFP_RESET_CABLE +#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */ + +#endif /* CONFIG_USB_PD_RESET_CABLE */ + +/*---------------------------------------------------------------------------*/ + +#define PD_SOP_NR 3 + +/* Default retry count for transmitting */ +/* Set to 0 for TD.PD.LL.E2 Retransmission */ +#define PD_RETRY_COUNT 3 +#define PD30_RETRY_COUNT 2 + +#if PD_RETRY_COUNT > 3 +#error "PD_RETRY_COUNT Max = 3" +#endif + +/* PDO : Power Data Object */ +/* + * 1. The vSafe5V Fixed Supply Object shall always be the first object. + * 2. The remaining Fixed Supply Objects, + * if present, shall be sent in voltage order; lowest to highest. + * 3. The Battery Supply Objects, + * if present shall be sent in Minimum Voltage order; lowest to highest. + * 4. The Variable Supply (non battery) Objects, + * if present, shall be sent in Minimum Voltage order; lowest to highest. + */ +#define PDO_TYPE_FIXED (0 << 30) +#define PDO_TYPE_BATTERY (1 << 30) +#define PDO_TYPE_VARIABLE (2 << 30) +#define PDO_TYPE_APDO (3 << 30) +#define PDO_TYPE_MASK (3 << 30) + +#define PDO_FIXED_DUAL_ROLE (1 << 29) /* Dual role device */ +#define PDO_FIXED_SUSPEND (1 << 28) /* USB Suspend supported (SRC) */ +#define PDO_FIXED_HIGH_CAP (1 << 28) /* Higher Capability (SNK) */ +#define PDO_FIXED_EXTERNAL (1 << 27) /* Externally powered */ +#define PDO_FIXED_COMM_CAP (1 << 26) /* USB Communications Capable */ +#define PDO_FIXED_DATA_SWAP (1 << 25) /* Data role swap command supported */ + +#define PDO_FIXED_PEAK_CURR(i) \ + ((i & 0x03) << 20) /* [21..20] Peak current */ +#define PDO_FIXED_VOLT(mv) \ + ((((mv)/50) & 0x3ff) << 10) /* Voltage in 50mV units */ +#define PDO_FIXED_CURR(ma) \ + ((((ma)/10) & 0x3ff) << 0) /* Max current in 10mA units */ + +#define PDO_TYPE(raw) (raw & PDO_TYPE_MASK) +#define PDO_TYPE_VAL(raw) (PDO_TYPE(raw) >> 30) + +#define PDO_FIXED_EXTRACT_VOLT_RAW(raw) (((raw) >> 10) & 0x3ff) +#define PDO_FIXED_EXTRACT_CURR_RAW(raw) (((raw) >> 0) & 0x3ff) +#define PDO_FIXED_EXTRACT_VOLT(raw) (PDO_FIXED_EXTRACT_VOLT_RAW(raw) * 50) +#define PDO_FIXED_EXTRACT_CURR(raw) (PDO_FIXED_EXTRACT_CURR_RAW(raw) * 10) +#define PDO_FIXED_RESET_CURR(raw, ma) \ + ((raw & ~0x3ff) | PDO_FIXED_CURR(ma)) + +#define PDO_FIXED(mv, ma, flags) (PDO_FIXED_VOLT(mv) |\ + PDO_FIXED_CURR(ma) | (flags)) + +#define PDO_VAR_MAX_VOLT(mv) ((((mv) / 50) & 0x3FF) << 20) +#define PDO_VAR_MIN_VOLT(mv) ((((mv) / 50) & 0x3FF) << 10) +#define PDO_VAR_OP_CURR(ma) ((((ma) / 10) & 0x3FF) << 0) + +#define PDO_VAR_EXTRACT_MAX_VOLT_RAW(raw) (((raw) >> 20) & 0x3ff) +#define PDO_VAR_EXTRACT_MIN_VOLT_RAW(raw) (((raw) >> 10) & 0x3ff) +#define PDO_VAR_EXTRACT_CURR_RAW(raw) (((raw) >> 0) & 0x3ff) + +#define PDO_VAR_EXTRACT_MAX_VOLT(raw) (PDO_VAR_EXTRACT_MAX_VOLT_RAW(raw) * 50) +#define PDO_VAR_EXTRACT_MIN_VOLT(raw) (PDO_VAR_EXTRACT_MIN_VOLT_RAW(raw) * 50) +#define PDO_VAR_EXTRACT_CURR(raw) (PDO_VAR_EXTRACT_CURR_RAW(raw) * 10) + +#define PDO_VAR_RESET_CURR(raw, ma) \ + ((raw & ~0x3ff) | PDO_VAR_OP_CURR(ma)) + +#define PDO_VAR(min_mv, max_mv, op_ma) \ + (PDO_VAR_MIN_VOLT(min_mv) | \ + PDO_VAR_MAX_VOLT(max_mv) | \ + PDO_VAR_OP_CURR(op_ma) | \ + PDO_TYPE_VARIABLE) + +#define PDO_BATT_MAX_VOLT(mv) ((((mv) / 50) & 0x3FF) << 20) +#define PDO_BATT_MIN_VOLT(mv) ((((mv) / 50) & 0x3FF) << 10) +#define PDO_BATT_OP_POWER(mw) ((((mw) / 250) & 0x3FF) << 0) + +#define PDO_BATT_EXTRACT_MAX_VOLT_RAW(raw) (((raw) >> 20) & 0x3ff) +#define PDO_BATT_EXTRACT_MIN_VOLT_RAW(raw) (((raw) >> 10) & 0x3ff) +#define PDO_BATT_EXTRACT_OP_POWER_RAW(raw) (((raw) >> 0) & 0x3ff) + +#define PDO_BATT_EXTRACT_MAX_VOLT(raw) \ + (PDO_BATT_EXTRACT_MAX_VOLT_RAW(raw) * 50) +#define PDO_BATT_EXTRACT_MIN_VOLT(raw) \ + (PDO_BATT_EXTRACT_MIN_VOLT_RAW(raw) * 50) +#define PDO_BATT_EXTRACT_OP_POWER(raw) \ + (PDO_BATT_EXTRACT_OP_POWER_RAW(raw) * 250) + +#define PDO_BATT(min_mv, max_mv, op_mw) \ + (PDO_BATT_MIN_VOLT(min_mv) | \ + PDO_BATT_MAX_VOLT(max_mv) | \ + PDO_BATT_OP_POWER(op_mw) | \ + PDO_TYPE_BATTERY) + +/* APDO : Augmented Power Data Object */ + +#define APDO_TYPE_MASK (3 << 28) +#define APDO_TYPE_PPS (0 << 28) + +#define APDO_TYPE(raw) (raw & APDO_TYPE_MASK) +#define APDO_TYPE_VAL(raw) (APDO_TYPE(raw) >> 28) + +#define APDO_PPS_CURR_FOLDBACK (1<<26) +#define APDO_PPS_MAX_VOLT(mv) ((((mv) / 100) & 0xff) << 17) +#define APDO_PPS_MIN_VOLT(mv) ((((mv) / 100) & 0xff) << 8) +#define APDO_PPS_CURR(ma) ((((ma) / 50) & 0x7f) << 0) + +#define APDO_PPS_EXTRACT_MAX_VOLT_RAW(raw) (((raw) >> 17) & 0xff) +#define APDO_PPS_EXTRACT_MIN_VOLT_RAW(raw) (((raw) >> 8) & 0Xff) +#define APDO_PPS_EXTRACT_CURR_RAW(raw) (((raw) >> 0) & 0x7f) +#define APDO_PPS_EXTRACT_PWR_LIMIT(raw) ((raw >> 27) & 0x1) + +#define APDO_PPS_EXTRACT_MAX_VOLT(raw) \ + (APDO_PPS_EXTRACT_MAX_VOLT_RAW(raw) * 100) +#define APDO_PPS_EXTRACT_MIN_VOLT(raw) \ + (APDO_PPS_EXTRACT_MIN_VOLT_RAW(raw) * 100) +#define APDO_PPS_EXTRACT_CURR(raw) \ + (APDO_PPS_EXTRACT_CURR_RAW(raw) * 50) + +#define APDO_PPS(min_mv, max_mv, ma, flags) \ + (APDO_PPS_MIN_VOLT(min_mv) | \ + APDO_PPS_MAX_VOLT(max_mv) | \ + APDO_PPS_CURR(ma) | \ + flags | PDO_TYPE_APDO | APDO_TYPE_PPS) + +/* RDO : Request Data Object */ +#define RDO_OBJ_POS(n) (((n) & 0x7) << 28) +#define RDO_POS(rdo) (((rdo) >> 28) & 0x7) +#define RDO_GIVE_BACK (1 << 27) +#define RDO_CAP_MISMATCH (1 << 26) +#define RDO_COMM_CAP (1 << 25) +#define RDO_NO_SUSPEND (1 << 24) +#define RDO_EXTEND_MESSAGE (1 << 23) +#define RDO_CURR_FOLDBACK (1 << 22) + +#define RDO_FIXED_VAR_OP_CURR(ma) ((((ma) / 10) & 0x3FF) << 10) +#define RDO_FIXED_VAR_MAX_CURR(ma) ((((ma) / 10) & 0x3FF) << 0) + +#define RDO_FIXED_VAR_EXTRACT_OP_CURR(raw) (((raw >> 10 & 0x3ff)) * 10) +#define RDO_FIXED_VAR_EXTRACT_MAX_CURR(raw) (((raw >> 0 & 0x3ff)) * 10) + +#define RDO_BATT_OP_POWER(mw) ((((mw) / 250) & 0x3FF) << 10) +#define RDO_BATT_MAX_POWER(mw) ((((mw) / 250) & 0x3FF) << 0) + +#define RDO_BATT_EXTRACT_OP_POWER(raw) (((raw >> 10 & 0x3ff)) * 250) +#define RDO_BATT_EXTRACT_MAX_POWER(raw) (((raw >> 0 & 0x3ff)) * 250) + +#define RDO_APDO_OP_MV(mv) ((((mv) / 20) & 0x7FF) << 9) +#define RDO_APDO_OP_MA(ma) ((((ma) / 50) & 0x7F) << 0) + +#define RDO_APDO_EXTRACT_OP_MV(raw) (((raw >> 9 & 0x7FF)) * 20) +#define RDO_APDO_EXTRACT_OP_MA(raw) (((raw >> 0 & 0x7F)) * 50) + +#define RDO_FIXED(n, op_ma, max_ma, flags) \ + (RDO_OBJ_POS(n) | (flags) | \ + RDO_FIXED_VAR_OP_CURR(op_ma) | \ + RDO_FIXED_VAR_MAX_CURR(max_ma)) + +#define RDO_BATT(n, op_mw, max_mw, flags) \ + (RDO_OBJ_POS(n) | (flags) | \ + RDO_BATT_OP_POWER(op_mw) | \ + RDO_BATT_MAX_POWER(max_mw)) + +#define RDO_APDO(n, op_mv, op_ma, flags) \ + (RDO_OBJ_POS(n) | (flags) | \ + RDO_APDO_OP_MV(op_mv) | \ + RDO_APDO_OP_MA(op_ma)) + +/* BDO : BIST Data Object */ +#define BDO_MODE_RECV (0 << 28) +#define BDO_MODE_TRANSMIT (1 << 28) +#define BDO_MODE_COUNTERS (2 << 28) +#define BDO_MODE_CARRIER0 (3 << 28) +#define BDO_MODE_CARRIER1 (4 << 28) +#define BDO_MODE_CARRIER2 (5 << 28) +#define BDO_MODE_CARRIER3 (6 << 28) +#define BDO_MODE_EYE (7 << 28) +#define BDO_MODE_TEST_DATA (8 << 28) + +#define BDO_MODE(obj) (obj & (0xf << 28)) +#define BDO(mode, cnt) ((mode) | ((cnt) & 0xFFFF)) + +#define SVID_DISCOVERY_MAX 16 + +/* Protocol revision */ +#define PD_REV10 0 +#define PD_REV20 1 +#define PD_REV30 2 + +#define MAX_EXTENDED_MSG_CHUNK_LEN 26 +#define MAX_EXTENDED_MSG_LEGACY_LEN 26 + +#define PD_MSG_ID_MAX 8 + +/* build message header */ + +#define PD_HEADER_ROLE(prole, drole) \ + (((drole) << 5) | ((prole) << 8)) + +#define PD_HEADER_COMMON(msg_type, rev, id, cnt, ext, private) \ + ((msg_type) | (rev << 6) | \ + ((id) << 9) | ((cnt) << 12) | ((ext) << 15) | (private)) + +#define PD_HEADER_SOP(msg_type, rev, prole, drole, id, cnt, ext) \ + ((msg_type) | (rev << 6) | \ + ((drole) << 5) | ((prole) << 8) | \ + ((id) << 9) | ((cnt) << 12) | ((ext) << 15)) + +#define PD_HEADER_SOP_PRIME(msg_type, rev, cable_plug, id, cnt, ext) \ + ((msg_type) | (rev << 6) | \ + ((cable_plug) << 8) | \ + ((id) << 9) | ((cnt) << 12) | ((ext) << 15)) + +#define PD_HEADER_EXT(header) (((header) >> 15) & 1) /* pd30 */ +#define PD_HEADER_REV(header) (((header) >> 6) & 3) +#define PD_HEADER_CNT(header) (((header) >> 12) & 7) +#define PD_HEADER_TYPE(header) ((header) & 0x1F) +#define PD_HEADER_ID(header) (((header) >> 9) & 7) +#define PD_HEADER_PR(header) (((header) >> 8) & 1) +#define PD_HEADER_DR(header) (((header) >> 5) & 1) + +#define PD_EXT_HEADER_PAYLOAD_INDEX 2 + +#define PD_EXT_HEADER_CHUNKED(header) (((header) >> 15) & 1) +#define PD_EXT_HEADER_CHUNK_NR(header) (((header) >> 11) & 0xF) +#define PD_EXT_HEADER_REQUEST(header) (((header) >> 10) & 1) +#define PD_EXT_HEADER_DATA_SIZE(header) (((header) >> 0) & 0x1FF) + +#define PD_EXT_HEADER_CK(data_size, req, chunk_nr, chunked) \ + ((data_size) | (req << 10) | \ + ((chunk_nr) << 11) | (chunked << 15)) + +/* + * VDO : Vendor Defined Message Object + * VDM object is minimum of VDM header + 6 additional data objects. + */ + +/* + * VDM header + * ---------- + * <31:16> :: SVID + * <15> :: VDM type ( 1b == structured, 0b == unstructured ) + * <14:13> :: Structured VDM version (can only be 00 == 1.0 currently) + * <12:11> :: reserved + * <10:8> :: object position (1-7 valid ... used for enter/exit mode only) + * <7:6> :: command type (SVDM only?) + * <5> :: reserved (SVDM), command type (UVDM) + * <4:0> :: command + */ + +#define VDO(vid, type, custom) \ + (((vid) << 16) | \ + ((type) << 15) | \ + ((custom) & 0x7FFF)) + +#define VDO_S(svid, ver, cmd_type, cmd, obj) \ + VDO(svid, 1, VDO_SVDM_VERS(ver) | \ + VDO_CMDT(cmd_type) | VDO_OPOS(obj) | cmd) + +#define VDO_REPLY(ver, cmd_type, request_vdo) \ + (VDO_SVDM_VERS(ver) | VDO_CMDT(cmd_type) \ + | ((request_vdo) & (~0x60C0))) + +#define SVDM_REV10 0 +#define SVDM_REV20 1 + +#define VDO_SVDM_TYPE (1 << 15) +#define VDO_SVDM_VERS(x) (x << 13) +#define VDO_OPOS(x) (x << 8) +#define VDO_CMDT(x) (x << 6) + +#define CMDT_INIT 0 +#define CMDT_RSP_ACK 1 +#define CMDT_RSP_NAK 2 +#define CMDT_RSP_BUSY 3 + +#define CMD_DISCOVER_IDENT 1 +#define CMD_DISCOVER_SVID 2 +#define CMD_DISCOVER_MODES 3 +#define CMD_ENTER_MODE 4 +#define CMD_EXIT_MODE 5 +#define CMD_ATTENTION 6 +#define CMD_DP_STATUS 16 +#define CMD_DP_CONFIG 17 + +#define PD_VDO_VID(vdo) ((vdo) >> 16) +#define PD_VDO_SVDM(vdo) (((vdo) >> 15) & 1) +#define PD_VDO_OPOS(vdo) (((vdo) >> 8) & 0x7) +#define PD_VDO_CMD(vdo) ((vdo) & 0x1f) +#define PD_VDO_CMDT(vdo) (((vdo) >> 6) & 0x3) + +/* + * SVDM Identity request -> response + * + * Request is simply properly formatted SVDM header + * + * Response is 4 data objects: + * [0] :: SVDM header + * [1] :: Identitiy header + * [2] :: Cert Stat VDO + * [3] :: Product VDO + * [4] :: Cable / AMA VDO + * + */ + +#define VDO_INDEX_HDR 0 +#define VDO_INDEX_IDH 1 +#define VDO_INDEX_CSTAT 2 +#define VDO_INDEX_PRODUCT 3 +#define VDO_INDEX_CABLE 4 +#define VDO_INDEX_AMA 4 +#define VDO_I(name) VDO_INDEX_##name + +/* + * SVDM Identity Header + * -------------------- + * <31> :: data capable as a USB host + * <30> :: data capable as a USB device + * <29:27> :: product type for UFP/Cable + * <26> :: modal operation supported (1b == yes) + * <25:23> :: product type for DFP + * <22:16> :: SBZ + * <15:0> :: USB-IF assigned VID for this cable vendor + */ + +#define IDH_PTYPE_UNDEF 0 +#define IDH_PTYPE_HUB 1 +#define IDH_PTYPE_PERIPH 2 +#define IDH_PTYPE_PCABLE 3 +#define IDH_PTYPE_ACABLE 4 +#define IDH_PTYPE_AMA 5 + +#define IDH_PTYPE_DFP_UNDEF 0 +#define IDH_PTYPE_DFP_HUB 1 +#define IDH_PTYPE_DFP_HOST 2 +#define IDH_PTYPE_DFP_PB 3 +#define IDH_PTYPE_DFP_AMC 4 + +#define VDO_IDH(usbh, usbd, ptype, ptype_dfp, is_modal, vid) \ + ((usbh) << 31 | (usbd) << 30 | ((ptype) & 0x7) << 27 \ + | ((ptype_dfp) & 0x07) << 23 \ + | (is_modal) << 26 | ((vid) & 0xffff)) + +#define VDO_IDH_PD20(idh) (idh & (~(0x1f << 21))) + +#define PD_IDH_PTYPE(vdo) (((vdo) >> 27) & 0x7) +#define PD_IDH_PTYPE_DFP(vdo) (((vdo) >> 23) & 0x7) +#define PD_IDH_VID(vdo) ((vdo) & 0xffff) + +#define PD_IDH_VID_MASK (0xffff) + +#define PD_IDH_MODAL_SUPPORT (1<<26) + +/* + * Cert Stat VDO + * ------------- + * <31:20> : SBZ + * <19:0> : USB-IF assigned TID for this cable + */ +#define VDO_CSTAT(tid) ((tid) & 0xfffff) +#define PD_CSTAT_TID(vdo) ((vdo) & 0xfffff) + +/* + * Product VDO + * ----------- + * <31:16> : USB Product ID + * <15:0> : USB bcdDevice + */ +#define VDO_PRODUCT(pid, bcd) (((pid) & 0xffff) << 16 | ((bcd) & 0xffff)) +#define PD_PRODUCT_BCD(vdo) ((vdo) & 0xffff) +#define PD_PRODUCT_PID(vdo) (((vdo) >> 16) & 0xffff) + +/* + * Cable VDO + * --------- + * <31:28> :: Cable HW version + * <27:24> :: Cable FW version + * <23:20> :: SBZ + * <19:18> :: type-C to Type-A/B/C (00b == A, 01 == B, 10 == C) + * <17> :: Type-C to Plug/Receptacle (0b == plug, 1b == receptacle) + * <16:13> :: cable latency (0001 == <10ns(~1m length)) + * <12:11> :: cable termination type (11b == both ends active VCONN req) + * <10> :: SSTX1 Directionality support (0b == fixed, 1b == cfgable) + * <9> :: SSTX2 Directionality support + * <8> :: SSRX1 Directionality support + * <7> :: SSRX2 Directionality support + * <6:5> :: Vbus current handling capability + * <4> :: Vbus through cable (0b == no, 1b == yes) + * <3> :: SOP" controller present? (0b == no, 1b == yes) + * <2:0> :: USB SS Signaling support + */ +#define CABLE_ATYPE 0 +#define CABLE_BTYPE 1 +#define CABLE_CTYPE 2 +#define CABLE_PLUG 0 +#define CABLE_RECEPTACLE 1 +#define CABLE_CURR_1A5 0 +#define CABLE_CURR_3A 1 +#define CABLE_CURR_5A 2 +#define CABLE_USBSS_U2_ONLY 0 +#define CABLE_USBSS_U31_GEN1 1 +#define CABLE_USBSS_U31_GEN2 2 +#define VDO_CABLE(hw, fw, cbl, gdr, lat, term, tx1d,\ + tx2d, rx1d, rx2d, cur, vps, sopp, usbss) \ + (((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 | ((cbl) & 0x3) << 18 \ + | (gdr) << 17 | ((lat) & 0x7) << 13 | ((term) & 0x3) << 11 \ + | (tx1d) << 10 | (tx2d) << 9 | (rx1d) << 8 | (rx2d) << 7 \ + | ((cur) & 0x3) << 5 | (vps) << 4 | (sopp) << 3 \ + | ((usbss) & 0x7)) + +#define PD_VDO_CABLE_CURR(x) (((x) >> 5) & 0x03) + +/* + * AMA VDO + * --------- + * <31:28> :: Cable HW version + * <27:24> :: Cable FW version + * <23:12> :: SBZ + * <11> :: SSTX1 Directionality support (0b == fixed, 1b == cfgable) + * <10> :: SSTX2 Directionality support + * <9> :: SSRX1 Directionality support + * <8> :: SSRX2 Directionality support + * <7:5> :: Vconn power + * <4> :: Vconn power required + * <3> :: Vbus power required + * <2:0> :: USB SS Signaling support + */ +#define VDO_AMA(hw, fw, tx1d, tx2d, rx1d, rx2d, vcpwr, vcr, vbr, usbss) \ + (((hw) & 0x7) << 28 | ((fw) & 0x7) << 24 \ + | (tx1d) << 11 | (tx2d) << 10 | (rx1d) << 9 | (rx2d) << 8 \ + | ((vcpwr) & 0x3) << 5 | (vcr) << 4 | (vbr) << 3 \ + | ((usbss) & 0x7)) + +#define PD_VDO_AMA_VCONN_REQ(vdo) (((vdo) >> 4) & 1) +#define PD_VDO_AMA_VBUS_REQ(vdo) (((vdo) >> 3) & 1) + +#define AMA_VCONN_PWR_1W 0 +#define AMA_VCONN_PWR_1W5 1 +#define AMA_VCONN_PWR_2W 2 +#define AMA_VCONN_PWR_3W 3 +#define AMA_VCONN_PWR_4W 4 +#define AMA_VCONN_PWR_5W 5 +#define AMA_VCONN_PWR_6W 6 +#define AMA_USBSS_U2_ONLY 0 +#define AMA_USBSS_U31_GEN1 1 +#define AMA_USBSS_U31_GEN2 2 +#define AMA_USBSS_BBONLY 3 + +/* + * SVDM Discover SVIDs request -> response + * + * Request is properly formatted VDM Header with discover SVIDs command. + * Response is a set of SVIDs of all all supported SVIDs with all zero's to + * mark the end of SVIDs. If more than 12 SVIDs are supported command SHOULD be + * repeated. + */ +#define VDO_SVID(svid0, svid1) (((svid0) & 0xffff) << 16 | ((svid1) & 0xffff)) +#define PD_VDO_SVID_SVID0(vdo) ((vdo) >> 16) +#define PD_VDO_SVID_SVID1(vdo) ((vdo) & 0xffff) + +/* + * DisplayPort modes capabilities + * ------------------------------- + * <31:24> : SBZ + * <23:16> : UFP_D pin assignment supported + * <15:8> : DFP_D pin assignment supported + * <7> : USB 2.0 signaling (0b=yes, 1b=no) + * <6> : Plug | Receptacle (0b == plug, 1b == receptacle) + * <5:2> : xxx1: Supports DPv1.3, xx1x Supports USB Gen 2 signaling + * Other bits are reserved. + * <1:0> : signal direction ( 00b=rsv, 01b=sink, 10b=src 11b=both ) + */ +#define VDO_MODE_DP(snkp, srcp, usb, gdr, sign, sdir) \ + (((snkp) & 0xff) << 16 | ((srcp) & 0xff) << 8 \ + | ((usb) & 1) << 7 | ((gdr) & 1) << 6 | ((sign) & 0xF) << 2 \ + | ((sdir) & 0x3)) +#define PD_DP_PIN_CAPS(x) ((((x) >> 6) & 0x1) ? (((x) >> 16) & 0x3f) \ + : (((x) >> 8) & 0x3f)) + +#define MODE_DP_PIN_A 0x01 +#define MODE_DP_PIN_B 0x02 +#define MODE_DP_PIN_C 0x04 +#define MODE_DP_PIN_D 0x08 +#define MODE_DP_PIN_E 0x10 +#define MODE_DP_PIN_F 0x20 + +/* Pin configs B/D/F support multi-function */ +#define MODE_DP_PIN_MF_MASK 0x2a +/* Pin configs A/B support BR2 signaling levels */ +#define MODE_DP_PIN_BR2_MASK 0x3 +/* Pin configs C/D/E/F support DP signaling levels */ +#define MODE_DP_PIN_DP_MASK 0x3c + +#define MODE_DP_V13 0x1 +#define MODE_DP_GEN2 0x2 + +#define MODE_DP_SNK 0x1 +#define MODE_DP_SRC 0x2 +#define MODE_DP_BOTH 0x3 + +#define MODE_DP_PORT_CAP(raw) (raw & 0x03) +#define MODE_DP_SIGNAL_SUPPORT(raw) ((raw>>2) & 0x0f) +#define MODE_DP_RECEPT(mode) ((mode >> 6) & 0x01) + +#define MODE_DP_PIN_DFP(mode) ((mode >> 8) & 0xff) +#define MODE_DP_PIN_UFP(mode) ((mode >> 16) & 0xff) + +#define PD_DP_DFP_D_PIN_CAPS(x) (MODE_DP_RECEPT(x) ? \ + MODE_DP_PIN_DFP(x) : MODE_DP_PIN_UFP(x)) + +#define PD_DP_UFP_D_PIN_CAPS(x) (MODE_DP_RECEPT(x) ? \ + MODE_DP_PIN_UFP(x) : MODE_DP_PIN_DFP(x)) + +/* + * DisplayPort Status VDO + * ---------------------- + * <31:9> : SBZ + * <8> : IRQ_HPD : 1 == irq arrived since last message otherwise 0. + * <7> : HPD state : 0 = HPD_LOW, 1 == HPD_HIGH + * <6> : Exit DP Alt mode: 0 == maintain, 1 == exit + * <5> : USB config : 0 == maintain current, 1 == switch to USB from DP + * <4> : Multi-function preference : 0 == no pref, 1 == MF preferred. + * <3> : enabled : is DPout on/off. + * <2> : power low : 0 == normal or LPM disabled, 1 == DP disabled for LPM + * <1:0> : connect status : 00b == no (DFP|UFP)_D is connected or disabled. + * 01b == DFP_D connected, 10b == UFP_D connected, 11b == both. + */ + +#define VDO_DP_STATUS(irq, lvl, amode, usbc, mf, en, lp, conn) \ + (((irq) & 1) << 8 | ((lvl) & 1) << 7 | ((amode) & 1) << 6 \ + | ((usbc) & 1) << 5 | ((mf) & 1) << 4 | ((en) & 1) << 3 \ + | ((lp) & 1) << 2 | ((conn & 0x3) << 0)) + +#define PD_VDO_DPSTS_HPD_IRQ(x) (((x) >> 8) & 1) +#define PD_VDO_DPSTS_HPD_LVL(x) (((x) >> 7) & 1) +#define PD_VDO_DPSTS_MF_PREF(x) (((x) >> 4) & 1) + +#define PD_VDO_DPSTS_CONNECT(x) (((x) >> 0) & 0x03) + +#define DPSTS_DISCONNECT 0 + +#define DPSTS_DFP_D_CONNECTED (1 << 0) +#define DPSTS_UFP_D_CONNECTED (1 << 1) +#define DPSTS_BOTH_CONNECTED (DPSTS_DFP_D_CONNECTED | DPSTS_UFP_D_CONNECTED) + +/* UFP_U only */ +#define DPSTS_DP_ENABLED (1<<3) +#define DPSTS_DP_MF_PREF (1<<4) +#define DPSTS_DP_USB_CONFIG (1<<5) +#define DPSTS_DP_EXIT_ALT_MODE (1<<6) + +/* UFP_D only */ +#define DPSTS_DP_HPD_STATUS (1<<7) +#define DPSTS_DP_HPD_IRQ (1<<8) + +/* Per DisplayPort Spec v1.3 Section 3.3 */ +#define HPD_USTREAM_DEBOUNCE_LVL (2*MSEC) +#define HPD_USTREAM_DEBOUNCE_IRQ (250) +#define HPD_DSTREAM_DEBOUNCE_IRQ (750) /* between 500-1000us */ + +/* + * DisplayPort Configure VDO + * ------------------------- + * <31:24> : SBZ + * <23:16> : SBZ + * <15:8> : Pin assignment requested. Choose one from mode caps. + * <7:6> : SBZ + * <5:2> : signalling : 1h == DP v1.3, 2h == Gen 2 + * Oh is only for USB, remaining values are reserved + * <1:0> : cfg : 00 == USB, 01 == DFP_D, 10 == UFP_D, 11 == reserved + */ + +#define DP_CONFIG_USB 0 +#define DP_CONFIG_DFP_D 1 +#define DP_CONFIG_UFP_D 2 + +#define VDO_DP_CFG(pin, sig, cfg) \ + (((pin) & 0xff) << 8 | ((sig) & 0xf) << 2 | ((cfg) & 0x3)) + +#define VDO_DP_DFP_CFG(pin, sig) VDO_DP_CFG(pin, sig, DP_CONFIG_DFP_D) +#define VDO_DP_UFP_CFG(pin, sig) VDO_DP_CFG(pin, sig, DP_CONFIG_UFP_D) + +#define PD_DP_CFG_ROLE(x) (x & 0x3) +#define PD_DP_CFG_USB(x) ((x & 0x3) == DP_CONFIG_USB) +#define PD_DP_CFG_DFP_D(x) ((x & 0x3) == DP_CONFIG_DFP_D) +#define PD_DP_CFG_UFP_D(x) ((x & 0x3) == DP_CONFIG_UFP_D) +#define PD_DP_CFG_DPON(x) (PD_DP_CFG_DFP_D(x) | PD_DP_CFG_UFP_D(x)) + +#define DP_SIG_DPV13 (0x01) +#define DP_SIG_GEN2 (0x02) + +#define DP_PIN_ASSIGN_SUPPORT_A (1 << 0) +#define DP_PIN_ASSIGN_SUPPORT_B (1 << 1) +#define DP_PIN_ASSIGN_SUPPORT_C (1 << 2) +#define DP_PIN_ASSIGN_SUPPORT_D (1 << 3) +#define DP_PIN_ASSIGN_SUPPORT_E (1 << 4) +#define DP_PIN_ASSIGN_SUPPORT_F (1 << 5) + +/* + * Get the pin assignment mask + * for backward compatibility, if it is null, + * get the former sink pin assignment we used to be in <23:16>. + */ + +#define PD_DP_CFG_PIN(x) (((x) >> 8) & 0xff) + +/* USB-IF SVIDs */ +#define USB_SID_PD 0xff00 /* power delivery */ +#define USB_SID_DISPLAYPORT 0xff01 /* display port */ +#define USB_VID_RICHTEK 0x29cf /* demo uvdm */ +#define USB_VID_DIRECTCHARGE 0x29cf /* direct charge */ +#define USB_VID_MQP 0x1748 +#define USB_VID_MMI_ADAPTER 0x22b8 + +/* PD counter definitions */ +#define PD_MESSAGE_ID_COUNT 7 +#define PD_HARD_RESET_COUNT 2 +#define PD_CAPS_COUNT 50 +#define PD_WAIT_RETRY_COUNT 1 +#define PD_DISCOVER_ID_COUNT 3 /* max : 20 */ +#define PD_DISCOVER_ID30_COUNT 2 /* max : 20 */ + +enum { + PD_WAIT_VBUS_DISABLE = 0, + PD_WAIT_VBUS_VALID_ONCE = 1, + PD_WAIT_VBUS_INVALID_ONCE = 2, + PD_WAIT_VBUS_SAFE0V_ONCE = 3, + PD_WAIT_VBUS_STABLE_ONCE = 4, +}; + +struct pd_port_power_caps { + uint8_t nr; + uint32_t pdos[7]; +}; + +struct svdm_mode { + uint8_t mode_cnt; + uint32_t mode_vdo[VDO_MAX_NR]; +}; + +struct svdm_svid_ops; +struct svdm_svid_data { + bool exist; + uint16_t svid; + uint8_t active_mode; + struct svdm_mode local_mode; + struct svdm_mode remote_mode; + const struct svdm_svid_ops *ops; +}; + +struct svdm_svid_list { + uint8_t cnt; + uint16_t svids[VDO_MAX_SVID_NR]; +}; + +struct pd_port; +typedef void (*pe_state_action_fcn_t)(struct pd_port *pd_port); + +struct dp_data { + uint8_t ufp_u_state; + uint8_t dfp_u_state; + + uint32_t local_status; + uint32_t remote_status; + + uint32_t local_config; + uint32_t remote_config; +}; + +struct pe_data { /* reset after detached */ + bool pd_connected; + bool pd_prev_connected; + bool explicit_contract; + bool invalid_contract; + bool modal_operation; + + bool pe_ready; + bool reset_vdm_state; + bool power_cable_present; + bool during_swap; /* pr or dr swap */ + +#ifdef CONFIG_USB_PD_REV30 + bool cable_rev_discovered; +#endif /* CONFIG_USB_PD_REV30 */ + +#ifdef CONFIG_USB_PD_KEEP_PARTNER_ID + bool partner_id_present; +#endif /* CONFIG_USB_PD_KEEP_PARTNER_ID */ + +#ifdef CONFIG_USB_PD_VCONN_SAFE5V_ONLY + bool vconn_highv_prot; + uint8_t vconn_highv_prot_role; +#endif /* CONFIG_USB_PD_VCONN_SAFE5V_ONLY */ + + uint8_t cap_counter; + uint8_t discover_id_counter; + uint8_t hard_reset_counter; + + bool vdm_discard_retry_flag; + uint8_t vdm_discard_retry_count; + + uint8_t msg_id_rx[PD_SOP_NR]; + uint8_t msg_id_tx[PD_SOP_NR]; + + uint8_t dpm_reaction_retry; + uint8_t dpm_svdm_retry_cnt; + uint16_t dpm_flags; + uint32_t dpm_reaction_id; + uint32_t dpm_ready_reactions; + + uint8_t local_selected_cap; + uint8_t remote_selected_cap; + + uint8_t pe_state_flags; + uint8_t pe_state_flags2; + uint8_t vdm_state_flags; + uint32_t pe_state_timer; + uint32_t vdm_state_timer; + +#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER + uint8_t recv_hard_reset_count; +#endif /* CONFIG_USB_PD_RECV_HRESET_COUNTER */ + +#ifdef CONFIG_USB_PD_RENEGOTIATION_COUNTER + uint8_t renegotiation_count; +#endif /* CONFIG_USB_PD_RENEGOTIATION_COUNTER */ + +#ifdef CONFIG_USB_PD_WAIT_AUTO_RETRY + uint8_t wait_retry_cnt; + uint8_t wait_retry_type; +#endif /* CONFIG_USB_PD_WAIT_AUTO_RETRY */ + +#ifdef CONFIG_USB_PD_KEEP_PARTNER_ID + uint32_t partner_vdos[VDO_MAX_NR]; +#endif /* CONFIG_USB_PD_KEEP_PARTNER_ID */ + + uint32_t cable_vdos[VDO_MAX_NR]; + + struct pd_port_power_caps remote_src_cap; + struct pd_port_power_caps remote_snk_cap; + +#ifdef CONFIG_USB_PD_KEEP_SVIDS + struct svdm_svid_list remote_svid_list; +#endif /* CONFIG_USB_PD_KEEP_SVIDS */ + +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE + uint32_t remote_alert; +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL + uint32_t local_alert; + bool get_status_once; +#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + bool pd_traffic_idle; /* source only */ + uint8_t pd_traffic_control; +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ +#endif /* CONFIG_USB_PD_REV30 */ + +#ifdef CONFIG_USB_PD_ALT_MODE_RTDC +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + bool dc_pps_mode; +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ +#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */ + +#ifdef CONFIG_USB_PD_ALT_MODE + struct dp_data dp_data; +#endif /* CONFIG_USB_PD_ALT_MODE */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL + uint8_t pd_status_event; +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ +}; + +#ifdef CONFIG_USB_PD_REV30_BAT_INFO +struct pd_battery_info { + uint32_t bat_status; + struct pd_manufacturer_info mfrs_info; + struct pd_battery_capabilities bat_cap; +}; +#endif /* CONFIG_USB_PD_REV30_BAT_INFO */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_AUTHORITY +struct pd_country_authority { + uint16_t code; + uint8_t len; + uint8_t *data; +}; +#endif /* CONFIG_USB_PD_REV30_COUNTRY_AUTHORITY */ + +struct pd_port { + struct tcpc_device *tcpc; + struct mutex pd_lock; + + /* miss msg */ + bool miss_msg; + uint8_t rx_cap; + +#ifdef CONFIG_SUPPORT_MMI_ADAPTER + uint8_t mmi_adapter_state; +#endif /* CONFIG_SUPPORT_MMI_ADAPTER */ + + /* PD */ + bool msg_output_lock; + + uint8_t state_machine; + uint8_t pd_connect_state; + uint8_t pd_vdm_verify_state; + + uint8_t pe_pd_state; + uint8_t pe_vdm_state; + + uint8_t pe_state_next; + uint8_t pe_state_curr; + + uint8_t data_role; + uint8_t power_role; + uint8_t vconn_role; + + struct pe_data pe_data; + +#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE + uint8_t error_recovery_once; +#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */ + +#ifdef CONFIG_USB_PD_REV30 + struct wakeup_source *pps_request_wake_lock; + wait_queue_head_t pps_request_wait_que; + atomic_t pps_request; + struct task_struct *pps_request_task; +#ifdef CONFIG_USB_PD_REV30_SYNC_SPEC_REV + uint8_t pd_revision[2]; +#endif /* CONFIG_USB_PD_REV30_SYNC_SPEC_REV */ +#endif /* CONFIG_USB_PD_REV30 */ + +#ifdef CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP + uint8_t msg_id_pr_swap_last; +#endif /* CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP */ + + uint32_t last_rdo; + + uint8_t id_vdo_nr; + uint32_t id_vdos[VDO_MAX_NR]; + + uint32_t id_header; + + uint8_t svid_data_cnt; + struct svdm_svid_data *svid_data; + +/* DPM */ + int request_v; + int request_i; + int request_v_new; + int request_i_new; + int request_i_op; + int request_i_max; + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + int request_v_apdo; + int request_i_apdo; + bool request_apdo; + bool request_apdo_new; +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + struct pd_port_power_caps local_src_cap; + struct pd_port_power_caps local_snk_cap; + struct pd_port_power_caps local_src_cap_default; + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + uint8_t local_snk_cap_nr_pd30; + uint8_t local_snk_cap_nr_pd20; +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + uint16_t mode_svid; + uint8_t mode_obj_pos; + + uint32_t dpm_caps; + + uint8_t dpm_charging_policy; + uint8_t dpm_charging_policy_default; + +/* ALT Mode */ +#ifdef CONFIG_USB_PD_ALT_MODE + uint8_t dp_first_connected; + uint8_t dp_second_connected; +#endif /* CONFIG_USB_PD_ALT_MODE */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM + bool uvdm_wait_resp; + uint8_t uvdm_cnt; + uint16_t uvdm_svid; + uint32_t uvdm_data[PD_DATA_OBJ_SIZE]; +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + +#ifdef CONFIG_USB_PD_ALT_MODE_RTDC + uint8_t dc_dfp_state; + uint32_t dc_pass_code; +#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */ + +#ifdef CONFIG_USB_PD_CUSTOM_DBGACC + bool custom_dbgacc; +#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */ + +#ifdef CONFIG_USB_PD_RICHTEK_UVDM + bool richtek_init_done; +#endif /* CONFIG_USB_PD_RICHTEK_UVDM */ + + struct tcp_dpm_event tcp_event; + uint8_t tcp_event_id_1st; + +#ifdef CONFIG_USB_PD_TCPM_CB_2ND + uint8_t tcp_event_id_2nd; + bool tcp_event_drop_reset_once; + + uint8_t *tcpm_bk_cb_data; + uint8_t tcpm_bk_cb_data_max; +#endif /* CONFIG_USB_PD_TCPM_CB_2ND */ + +#ifdef CONFIG_USB_PD_BLOCK_TCPM + int tcpm_bk_ret; + bool tcpm_bk_done; + uint8_t tcpm_bk_event_id; + struct mutex tcpm_bk_lock; + wait_queue_head_t tcpm_bk_wait_que; +#endif /* CONFIG_USB_PD_BLOCK_TCPM */ + + /* curr_event info */ + bool curr_is_vdm_evt; + struct pd_event curr_pd_event; + + uint16_t pd_msg_data_size; + uint8_t pd_msg_data_count; + uint8_t *pd_msg_data_payload; + + uint8_t curr_vdm_ops; + uint16_t curr_vdm_svid; + + uint16_t curr_msg_hdr; + uint32_t curr_vdm_hdr; + + uint8_t curr_ready_state; + uint8_t curr_hreset_state; + uint8_t curr_sreset_state; + + bool curr_unsupported_msg; + +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL + struct pd_source_cap_ext src_cap_ext; +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL + struct pd_manufacturer_info mfrs_info; +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_BAT_INFO + uint32_t pid; + uint32_t vid; + uint8_t bat_nr; + struct pd_battery_info *fix_bat_info; + struct pd_battery_info *swap_bat_info; +#endif /* CONFIG_USB_PD_REV30_BAT_INFO */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL + uint8_t pd_status_bat_in; + uint8_t pd_status_present_in; + +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL_TEMP + uint8_t pd_status_temp; + uint8_t pd_status_temp_status; +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL_TEMP */ +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_AUTHORITY + uint8_t country_nr; + struct pd_country_authority *country_info; +#endif /* CONFIG_USB_PD_REV30_COUNTRY_AUTHORITY */ + +#ifdef CONFIG_RECV_BAT_ABSENT_NOTIFY + /* for MTK only, handle battery plug out */ + struct work_struct fg_bat_work; + struct notifier_block fg_bat_nb; +#endif /* CONFIG_RECV_BAT_ABSENT_NOTIFY */ + + uint8_t cap_miss_match; /* For src_cap miss match */ +}; + +#ifdef CONFIG_USB_PD_ALT_MODE +static inline struct dp_data *pd_get_dp_data(struct pd_port *pd_port) +{ + return &pd_port->pe_data.dp_data; +} +#endif /* CONFIG_USB_PD_ALT_MODE */ + +extern int pd_core_init(struct tcpc_device *tcpc); + +static inline void *pd_get_msg_data_payload(struct pd_port *pd_port) +{ + return pd_port->pd_msg_data_payload; +} + +static inline uint32_t *pd_get_msg_vdm_data_payload(struct pd_port *pd_port) +{ + uint32_t *payload = (uint32_t *) pd_port->pd_msg_data_payload; + + if (payload == NULL) + return NULL; + + return payload+1; +} + +static inline uint8_t pd_get_msg_data_count(struct pd_port *pd_port) +{ + return pd_port->pd_msg_data_count; +} + +static inline uint16_t pd_get_msg_data_size(struct pd_port *pd_port) +{ + return pd_port->pd_msg_data_size; +} + +static inline uint8_t pd_get_msg_vdm_data_count(struct pd_port *pd_port) +{ + return pd_port->pd_msg_data_count-1; +} + +static inline bool pd_curr_is_vdm_evt(struct pd_port *pd_port) +{ + return pd_port->curr_is_vdm_evt; +} + +static inline struct pd_event *pd_get_curr_pd_event(struct pd_port *pd_port) +{ + return &pd_port->curr_pd_event; +} + +static inline uint32_t pd_get_msg_vdm_hdr(struct pd_port *pd_port) +{ + return pd_port->curr_vdm_hdr; +} + +#ifdef CONFIG_USB_PD_REV30 +static inline uint8_t pd_get_msg_hdr_ext(struct pd_port *pd_port) +{ + return PD_HEADER_EXT(pd_port->curr_msg_hdr); +} +#endif /* CONFIG_USB_PD_REV30 */ + +static inline uint8_t pd_get_msg_hdr_rev(struct pd_port *pd_port) +{ + return PD_HEADER_REV(pd_port->curr_msg_hdr); +} + +static inline uint8_t pd_get_msg_hdr_cnt(struct pd_port *pd_port) +{ + return PD_HEADER_CNT(pd_port->curr_msg_hdr); +} + +static inline uint8_t pd_get_msg_hdr_id(struct pd_port *pd_port) +{ + return PD_HEADER_ID(pd_port->curr_msg_hdr); +} + +static inline bool pd_check_ctrl_msg_event( + struct pd_port *pd_port, uint8_t msg) +{ + return pd_event_ctrl_msg_match( + pd_get_curr_pd_event(pd_port), msg); +} + +static inline bool pd_check_data_msg_event( + struct pd_port *pd_port, uint8_t msg) +{ + return pd_event_data_msg_match( + pd_get_curr_pd_event(pd_port), msg); +} + +static inline bool pd_check_timer_msg_event( + struct pd_port *pd_port, uint8_t msg) +{ + return pd_event_timer_msg_match( + pd_get_curr_pd_event(pd_port), msg); +} + +extern bool pd_is_reset_cable(struct pd_port *pd_port); +extern bool pd_is_discover_cable(struct pd_port *pd_port); + +static inline int pd_is_support_modal_operation(struct pd_port *pd_port) +{ + if (!(pd_port->id_vdos[0] & PD_IDH_MODAL_SUPPORT)) + return false; + + return pd_port->svid_data_cnt > 0; +} + +static inline int pd_is_source_support_apdo(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + uint8_t i; + struct pd_port_power_caps *src_cap = &pd_port->pe_data.remote_src_cap; + + /* index0 always be fixed 5V */ + for (i = 1; i < src_cap->nr; i++) { + if (PDO_TYPE(src_cap->pdos[i]) == PDO_TYPE_APDO) + return true; + } +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + return false; +} + +/* new definitions*/ + +#define PD_RX_CAP_PE_IDLE (0) +#define PD_RX_CAP_PE_DISABLE (TCPC_RX_CAP_HARD_RESET) +#define PD_RX_CAP_PE_STARTUP (TCPC_RX_CAP_HARD_RESET) +#define PD_RX_CAP_PE_HARDRESET (0) +#define PD_RX_CAP_PE_SWAP \ + (TCPC_RX_CAP_HARD_RESET|TCPC_RX_CAP_SOP) +#define PD_RX_CAP_PE_SEND_WAIT_CAP \ + (TCPC_RX_CAP_HARD_RESET|TCPC_RX_CAP_SOP) +#define PD_RX_CAP_PE_DISCOVER_CABLE \ + (TCPC_RX_CAP_HARD_RESET|TCPC_RX_CAP_SOP_PRIME) +#define PD_RX_CAP_PE_READY_UFP \ + (TCPC_RX_CAP_HARD_RESET|TCPC_RX_CAP_SOP) + +#ifdef CONFIG_PD_DISCOVER_CABLE_ID +#define PD_RX_CAP_PE_READY_DFP \ + (TCPC_RX_CAP_HARD_RESET|TCPC_RX_CAP_SOP|TCPC_RX_CAP_SOP_PRIME) +#else +#define PD_RX_CAP_PE_READY_DFP (TCPC_RX_CAP_HARD_RESET|TCPC_RX_CAP_SOP) +#endif + +enum { + PD_BIST_MODE_DISABLE = 0, + PD_BIST_MODE_EVENT_PENDING, + PD_BIST_MODE_TEST_DATA, +}; + +void pd_reset_svid_data(struct pd_port *pd_port); +int pd_reset_protocol_layer(struct pd_port *pd_port, bool sop_only); + +int pd_set_rx_enable(struct pd_port *pd_port, uint8_t enable); + +int pd_enable_vbus_valid_detection(struct pd_port *pd_port, bool wait_valid); +int pd_enable_vbus_safe0v_detection(struct pd_port *pd_port); +int pd_enable_vbus_stable_detection(struct pd_port *pd_port); + +uint32_t pd_reset_pdo_power(struct tcpc_device *tcpc, + uint32_t pdo, uint32_t imax); + +void pd_extract_rdo_power( + uint32_t rdo, uint32_t pdo, uint32_t *op_curr, uint32_t *max_curr); + +uint32_t pd_get_cable_curr_lvl(struct pd_port *pd_port); +uint32_t pd_get_cable_current_limit(struct pd_port *pd_port); + + +int pd_set_data_role(struct pd_port *pd_port, uint8_t dr); +int pd_set_power_role(struct pd_port *pd_port, uint8_t pr); +int pd_init_message_hdr(struct pd_port *pd_port, bool act_as_sink); + +int pd_set_cc_res(struct pd_port *pd_port, int pull); +int pd_set_vconn(struct pd_port *pd_port, uint8_t role); +int pd_reset_local_hw(struct pd_port *pd_port); + +int pd_enable_bist_test_mode(struct pd_port *pd_port, bool en); +int pd_schedule_wait_request(struct pd_port *pd_port, uint8_t type); +int pd_cancel_wait_request(struct pd_port *pd_port); + +void pd_lock_msg_output(struct pd_port *pd_port); +void pd_unlock_msg_output(struct pd_port *pd_port); + +int pd_update_connect_state(struct pd_port *pd_port, uint8_t state); +int pd_update_vdm_verify_state(struct pd_port *pd_port, uint8_t state); + +/* ---- PD notify TCPC Policy Engine State Changed ---- */ + +extern void pd_try_put_pe_idle_event(struct pd_port *pd_port); +extern void pd_notify_pe_transit_to_default(struct pd_port *pd_port); +extern void pd_notify_pe_hard_reset_completed(struct pd_port *pd_port); +extern void pd_notify_pe_send_hard_reset(struct pd_port *pd_port); +extern void pd_notify_pe_idle(struct pd_port *pd_port); +extern void pd_notify_pe_wait_vbus_once(struct pd_port *pd_port, int wait_evt); +extern void pd_notify_pe_error_recovery(struct pd_port *pd_port); +extern void pd_notify_pe_execute_pr_swap( + struct pd_port *pd_port, bool start_swap); +extern void pd_notify_pe_cancel_pr_swap(struct pd_port *pd_port); +extern void pd_notify_pe_reset_protocol(struct pd_port *pd_port); +extern void pd_noitfy_pe_bist_mode(struct pd_port *pd_port, uint8_t mode); +extern void pd_notify_pe_pr_changed(struct pd_port *pd_port); +extern void pd_notify_pe_snk_explicit_contract(struct pd_port *pd_port); +extern void pd_notify_pe_src_explicit_contract(struct pd_port *pd_port); +extern void pd_notify_pe_transmit_msg(struct pd_port *pd_port, uint8_t type); + +#ifdef CONFIG_USB_PD_DIRECT_CHARGE +extern void pd_notify_pe_direct_charge(struct pd_port *pd_port, bool en); +#endif /* CONFIG_USB_PD_DIRECT_CHARGE */ + +#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER +extern void pd_notify_pe_over_recv_hreset(struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_RECV_HRESET_COUNTER */ + +extern void pd_notify_tcp_event_buf_reset( + struct pd_port *pd_port, uint8_t reason); +extern void pd_notify_tcp_event_1st_result(struct pd_port *pd_port, int ret); +extern void pd_notify_tcp_event_2nd_result(struct pd_port *pd_port, int ret); +extern void pd_notify_tcp_vdm_event_2nd_result( + struct pd_port *pd_port, uint8_t ret); + +/* ---- pd_timer ---- */ + +static inline void pd_restart_timer(struct pd_port *pd_port, uint32_t timer_id) +{ + return tcpc_restart_timer(pd_port->tcpc, timer_id); +} + +static inline void pd_enable_timer(struct pd_port *pd_port, uint32_t timer_id) +{ + return tcpc_enable_timer(pd_port->tcpc, timer_id); +} + +static inline void pd_enable_pe_state_timer( + struct pd_port *pd_port, uint32_t timer_id) +{ + /* timer_id shoult not be zero */ + PD_BUG_ON(timer_id == 0); + + pd_port->pe_data.pe_state_timer = timer_id; + return pd_enable_timer(pd_port, timer_id); +} + +static inline void pd_enable_vdm_state_timer( + struct pd_port *pd_port, uint32_t timer_id) +{ + pd_port->pe_data.vdm_state_timer = timer_id; +} + +static inline void pd_disable_timer(struct pd_port *pd_port, uint32_t timer_id) +{ + return tcpc_disable_timer(pd_port->tcpc, timer_id); +} + +static inline void pd_disable_pe_state_timer(struct pd_port *pd_port) +{ + struct pe_data *pe_data = &pd_port->pe_data; + + pd_disable_timer(pd_port, pe_data->pe_state_timer); + pe_data->pe_state_timer = 0; +} + +void pd_reset_pe_timer(struct pd_port *pd_port); + +/* ---- pd_event ---- */ + +static inline bool pd_put_pe_event(struct pd_port *pd_port, uint8_t pe_event) +{ + struct pd_event evt = { + .event_type = PD_EVT_PE_MSG, + .msg = pe_event, + .pd_msg = NULL, + }; + + return pd_put_event(pd_port->tcpc, &evt, false); +} + +static inline bool pd_put_dpm_notify_event( + struct pd_port *pd_port, uint8_t notify) +{ + struct pd_event evt = { + .event_type = PD_EVT_DPM_MSG, + .msg = PD_DPM_NOTIFIED, + .msg_sec = notify, + .pd_msg = NULL, + }; + + return pd_put_event(pd_port->tcpc, &evt, false); +} + +static inline bool pd_put_dpm_ack_event(struct pd_port *pd_port) +{ + struct pd_event evt = { + .event_type = PD_EVT_DPM_MSG, + .msg = PD_DPM_ACK, + .pd_msg = NULL, + }; + + return pd_put_event(pd_port->tcpc, &evt, false); +} + +static inline bool pd_put_dpm_nak_event(struct pd_port *pd_port, uint8_t notify) +{ + struct pd_event evt = { + .event_type = PD_EVT_DPM_MSG, + .msg = PD_DPM_NAK, + .msg_sec = notify, + .pd_msg = NULL, + }; + + return pd_put_event(pd_port->tcpc, &evt, false); +} + +static inline bool pd_put_dpm_event(struct pd_port *pd_port, uint8_t msg) +{ + struct pd_event evt = { + .event_type = PD_EVT_DPM_MSG, + .msg = msg, + .pd_msg = NULL, + }; + + return pd_put_event(pd_port->tcpc, &evt, false); +} + +static inline bool pd_put_tcp_pd_event(struct pd_port *pd_port, uint8_t event) +{ + struct pd_event evt = { + .event_type = PD_EVT_TCP_MSG, + .msg = event, + .msg_sec = PD_TCP_FROM_PE, + .pd_msg = NULL, + }; + + return pd_put_event(pd_port->tcpc, &evt, false); +}; + +static inline bool pd_put_tcp_vdm_event(struct pd_port *pd_port, uint8_t event) +{ + struct pd_event evt = { + .event_type = PD_EVT_TCP_MSG, + .msg = event, + .msg_sec = PD_TCP_FROM_PE, + .pd_msg = NULL, + }; + + return pd_put_vdm_event(pd_port->tcpc, &evt, false); +}; + +static inline bool vdm_put_hw_event( + struct tcpc_device *tcpc, uint8_t hw_event) +{ + struct pd_event evt = { + .event_type = PD_EVT_HW_MSG, + .msg = hw_event, + .pd_msg = NULL, + }; + + return pd_put_vdm_event(tcpc, &evt, false); +} + +static inline bool vdm_put_pe_event( + struct tcpc_device *tcpc, uint8_t pe_event) +{ + struct pd_event evt = { + .event_type = PD_EVT_PE_MSG, + .msg = pe_event, + .pd_msg = NULL, + }; + + return pd_put_vdm_event(tcpc, &evt, false); +} + +static inline bool vdm_put_dpm_discover_cable_event(struct pd_port *pd_port) +{ + /* waiting for dpm_ack event */ + return pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_DISCOVER_CABLE); +} + +static inline bool pd_put_hw_event( + struct tcpc_device *tcpc, uint8_t hw_event) +{ + struct pd_event evt = { + .event_type = PD_EVT_HW_MSG, + .msg = hw_event, + .pd_msg = NULL, + }; + + return pd_put_event(tcpc, &evt, false); +} + +static inline bool pd_put_sink_tx_event( + struct tcpc_device *tcpc, uint8_t cc_res) +{ +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + struct pd_event evt = { + .event_type = PD_EVT_HW_MSG, + .msg = PD_HW_SINK_TX_CHANGE, + .pd_msg = NULL, + }; + + evt.msg_sec = cc_res == TYPEC_CC_VOLT_SNK_3_0; + return pd_put_event(tcpc, &evt, false); +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ + + return true; +} + +/* ---- Handle PD Message ----*/ + +void pd_handle_first_pd_command(struct pd_port *pd_port); +int pd_handle_soft_reset(struct pd_port *pd_port); +void pd_handle_hard_reset_recovery(struct pd_port *pd_port); + +/* ---- Send PD Message ----*/ + +enum tcpm_transmit_type { + TCPC_TX_SOP = 0, + TCPC_TX_SOP_PRIME = 1, + TCPC_TX_SOP_PRIME_PRIME = 2, + TCPC_TX_SOP_DEBUG_PRIME = 3, + TCPC_TX_SOP_DEBUG_PRIME_PRIME = 4, + TCPC_TX_HARD_RESET = 5, + TCPC_TX_CABLE_RESET = 6, + TCPC_TX_BIST_MODE_2 = 7 +}; + +int pd_send_message(struct pd_port *pd_port, uint8_t sop_type, + uint8_t msg, bool ext, uint16_t count, const uint32_t *data); + +int pd_send_data_msg(struct pd_port *pd_port, + uint8_t sop_type, uint8_t msg, uint8_t cnt, uint32_t *payload); + +int pd_send_sop_ctrl_msg(struct pd_port *pd_port, uint8_t msg); + +int pd_send_sop_prime_ctrl_msg(struct pd_port *pd_port, uint8_t msg); + +int pd_send_sop_data_msg(struct pd_port *pd_port, + uint8_t msg, uint8_t cnt, const uint32_t *payload); + +int pd_reply_wait_reject_msg(struct pd_port *pd_port); +int pd_reply_wait_reject_msg_no_resp(struct pd_port *pd_port); + +int pd_send_swap_msg(struct pd_port *pd_port, uint8_t msg); + +#ifdef CONFIG_USB_PD_REV30 +int pd_send_ext_msg(struct pd_port *pd_port, + uint8_t sop_type, uint8_t msg, bool request, + uint8_t chunk_nr, uint8_t size, const uint8_t *data); + +static inline int pd_send_sop_ext_msg(struct pd_port *pd_port, + uint8_t msg, uint8_t size, const void *data) +{ + PD_BUG_ON(size > MAX_EXTENDED_MSG_LEGACY_LEN); + + /* TODO: chunking */ + + return pd_send_ext_msg(pd_port, + TCPC_TX_SOP, msg, false, 0, size, data); +} +#endif /* CONFIG_USB_PD_REV30 */ + +#ifdef CONFIG_USB_PD_RESET_CABLE +int pd_send_cable_soft_reset(struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_RESET_CABLE */ + +int pd_send_soft_reset(struct pd_port *pd_port); +int pd_send_hard_reset(struct pd_port *pd_port); + +int pd_send_bist_mode2(struct pd_port *pd_port); +int pd_disable_bist_mode2(struct pd_port *pd_port); + + +/* ---- Send / Reply SVDM Command ----*/ + +/* Auto enable timer if success */ +int pd_send_svdm_request(struct pd_port *pd_port, + uint8_t sop_type, uint16_t svid, uint8_t vdm_cmd, + uint8_t obj_pos, uint8_t cnt, uint32_t *data_obj, + uint32_t timer_id); + +int pd_reply_svdm_request(struct pd_port *pd_port, + uint8_t reply, uint8_t cnt, uint32_t *data_obj); + +#ifdef CONFIG_USB_POWER_DELIVERY + +static inline int pd_send_vdm_discover_id( + struct pd_port *pd_port, uint8_t sop_type) +{ + return pd_send_svdm_request(pd_port, sop_type, USB_SID_PD, + CMD_DISCOVER_IDENT, 0, 0, NULL, PD_TIMER_VDM_RESPONSE); +} + +static inline int pd_send_vdm_discover_svids( + struct pd_port *pd_port, uint8_t sop_type) +{ + return pd_send_svdm_request(pd_port, sop_type, USB_SID_PD, + CMD_DISCOVER_SVID, 0, 0, NULL, PD_TIMER_VDM_RESPONSE); +} + +static inline int pd_send_vdm_discover_modes( + struct pd_port *pd_port, uint8_t sop_type, uint16_t svid) +{ + return pd_send_svdm_request(pd_port, sop_type, svid, + CMD_DISCOVER_MODES, 0, 0, NULL, PD_TIMER_VDM_RESPONSE); +} + +static inline int pd_send_vdm_enter_mode(struct pd_port *pd_port, + uint8_t sop_type, uint16_t svid, uint8_t obj_pos) +{ + return pd_send_svdm_request(pd_port, sop_type, svid, + CMD_ENTER_MODE, obj_pos, 0, NULL, PD_TIMER_VDM_MODE_ENTRY); +} + +static inline int pd_send_vdm_exit_mode(struct pd_port *pd_port, + uint8_t sop_type, uint16_t svid, uint8_t obj_pos) +{ + return pd_send_svdm_request(pd_port, sop_type, svid, + CMD_EXIT_MODE, obj_pos, 0, NULL, PD_TIMER_VDM_MODE_EXIT); +} + +static inline int pd_send_vdm_attention(struct pd_port *pd_port, + uint8_t sop_type, uint16_t svid, uint8_t obj_pos) +{ + return pd_send_svdm_request(pd_port, sop_type, svid, + CMD_ATTENTION, obj_pos, 0, NULL, 0); +} + +static inline int pd_send_vdm_dp_attention(struct pd_port *pd_port, + uint8_t sop_type, uint8_t obj_pos, uint32_t dp_status) +{ + return pd_send_svdm_request(pd_port, sop_type, USB_SID_DISPLAYPORT, + CMD_ATTENTION, obj_pos, 1, &dp_status, 0); +} + +static inline int pd_send_vdm_dp_status(struct pd_port *pd_port, + uint8_t sop_type, uint8_t obj_pos, uint8_t cnt, uint32_t *data_obj) +{ + return pd_send_svdm_request(pd_port, sop_type, USB_SID_DISPLAYPORT, + CMD_DP_STATUS, obj_pos, cnt, data_obj, PD_TIMER_VDM_RESPONSE); +} + +static inline int pd_send_vdm_dp_config(struct pd_port *pd_port, + uint8_t sop_type, uint8_t obj_pos, uint8_t cnt, uint32_t *data_obj) +{ + return pd_send_svdm_request(pd_port, sop_type, USB_SID_DISPLAYPORT, + CMD_DP_CONFIG, obj_pos, cnt, data_obj, PD_TIMER_VDM_RESPONSE); +} + +static inline int pd_reply_svdm_request_simply( + struct pd_port *pd_port, uint8_t reply) +{ + return pd_reply_svdm_request(pd_port, reply, 0, NULL); +} + +#endif /* CONFIG_USB_POWER_DELIVERY */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM +int pd_send_custom_vdm(struct pd_port *pd_port, uint8_t sop_type); +int pd_reply_custom_vdm(struct pd_port *pd_port, uint8_t sop_type, + uint8_t cnt, uint32_t *payload); +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + +#ifdef CONFIG_USB_PD_REV30 + +enum { /* pd_traffic_control */ + PD_SINK_TX_OK = 0, + PD_SINK_TX_NG = 1, + PD_SOURCE_TX_OK = 2, + PD_SOURCE_TX_START = 3, + PD_SINK_TX_START = 4, +}; + +#define PD30_SINK_TX_OK TYPEC_CC_RP_3_0 +#define PD30_SINK_TX_NG TYPEC_CC_RP_1_5 + +void pd_add_miss_msg(struct pd_port *pd_port,struct pd_event *pd_event, + uint8_t msg); + +void pd_set_sink_tx(struct pd_port *pd_port, uint8_t cc); +void pd_sync_sop_spec_revision(struct pd_port *pd_port); +void pd_sync_sop_prime_spec_revision(struct pd_port *pd_port, uint8_t rev); +bool pd_is_multi_chunk_msg(struct pd_port *pd_port); + +static inline uint8_t pd_get_fix_battery_nr(struct pd_port *pd_port) +{ + return PD_SCEDB_FIX_BAT_NR(pd_port->bat_nr); +} + +static inline uint8_t pd_get_swap_battery_nr(struct pd_port *pd_port) +{ + return PD_SCEDB_SWAP_BAT_NR(pd_port->bat_nr); +} + +struct pd_battery_info *pd_get_battery_info( + struct pd_port *pd_port, enum pd_battery_reference ref); +#endif /* CONFIG_USB_PD_REV30 */ + +#endif /* PD_CORE_H_ */ diff --git a/drivers/usb/typec/mmi_tcpc/inc/pd_dbg_info.h b/drivers/usb/typec/mmi_tcpc/inc/pd_dbg_info.h new file mode 100644 index 000000000000..cd32a7852343 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/pd_dbg_info.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#ifndef PD_DBG_INFO_H_INCLUDED +#define PD_DBG_INFO_H_INCLUDED + +#include +#include +#include "tcpci_config.h" + +#ifdef CONFIG_PD_DBG_INFO +extern int pd_dbg_info(const char *fmt, ...); +extern void pd_dbg_info_lock(void); +extern void pd_dbg_info_unlock(void); +extern int pd_dbg_info_init(void); +extern void pd_dbg_info_exit(void); +#else +static inline int pd_dbg_info(const char *fmt, ...) +{ + return 0; +} +static inline void pd_dbg_info_lock(void) {} +static inline void pd_dbg_info_unlock(void) {} +static inline int pd_dbg_info_init(void) +{ + return 0; +} +static inline void pd_dbg_info_exit(void) {} +#endif /* CONFIG_PD_DBG_INFO */ + +#endif /* PD_DBG_INFO_H_INCLUDED */ diff --git a/drivers/usb/typec/mmi_tcpc/inc/pd_dpm_core.h b/drivers/usb/typec/mmi_tcpc/inc/pd_dpm_core.h new file mode 100644 index 000000000000..f38c94df5e9c --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/pd_dpm_core.h @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#ifndef PD_DPM_CORE_H +#define PD_DPM_CORE_H + +#include "tcpci.h" +#include "pd_core.h" +#include "pd_process_evt.h" + +/* ---- MISC ---- */ +int pd_dpm_core_init(struct pd_port *pd_port); +int pd_dpm_enable_vconn(struct pd_port *pd_port, bool en); +int pd_dpm_send_sink_caps(struct pd_port *pd_port); +int pd_dpm_send_source_caps(struct pd_port *pd_port); + +void pd_dpm_inform_cable_id(struct pd_port *pd_port, bool src_startup); + +void pd_dpm_dynamic_enable_vconn(struct pd_port *pd_port); +void pd_dpm_dynamic_disable_vconn(struct pd_port *pd_port); + +/* ---- SNK ---- */ + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK +void pd_dpm_start_pps_request_thread(struct pd_port *pd_port, bool en); +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + +int pd_dpm_update_tcp_request(struct pd_port *pd_port, + struct tcp_dpm_pd_request *pd_req); + +int pd_dpm_update_tcp_request_ex(struct pd_port *pd_port, + struct tcp_dpm_pd_request_ex *pd_req); + +int pd_dpm_update_tcp_request_again(struct pd_port *pd_port); + +void pd_dpm_snk_evaluate_caps(struct pd_port *pd_port); +void pd_dpm_snk_standby_power(struct pd_port *pd_port); +void pd_dpm_snk_transition_power(struct pd_port *pd_port); +void pd_dpm_snk_hard_reset(struct pd_port *pd_port); + +/* ---- SRC ---- */ + +void pd_dpm_src_evaluate_request(struct pd_port *pd_port); +void pd_dpm_src_transition_power(struct pd_port *pd_port); +void pd_dpm_src_hard_reset(struct pd_port *pd_port); + +/* ---- UFP : Handle VDM Request ---- */ + +void pd_dpm_ufp_request_id_info(struct pd_port *pd_port); +void pd_dpm_ufp_request_svid_info(struct pd_port *pd_port); +void pd_dpm_ufp_request_mode_info(struct pd_port *pd_port); +void pd_dpm_ufp_request_enter_mode(struct pd_port *pd_port); +void pd_dpm_ufp_request_exit_mode(struct pd_port *pd_port); + +/* ---- UFP : DP Only ---- */ + +#ifdef CONFIG_USB_PD_ALT_MODE +int pd_dpm_ufp_request_dp_status(struct pd_port *pd_port); +int pd_dpm_ufp_request_dp_config(struct pd_port *pd_port); +void pd_dpm_ufp_send_dp_attention(struct pd_port *pd_port); +#endif + +/* ---- DFP : Inform VDM Result ---- */ + +void pd_dpm_dfp_inform_id(struct pd_port *pd_port, bool ack); +void pd_dpm_dfp_inform_svids(struct pd_port *pd_port, bool ack); +void pd_dpm_dfp_inform_modes(struct pd_port *pd_port, bool ack); +void pd_dpm_dfp_inform_enter_mode(struct pd_port *pd_port, bool ack); +void pd_dpm_dfp_inform_exit_mode(struct pd_port *pd_port); +void pd_dpm_dfp_inform_attention(struct pd_port *pd_port); + +/* ---- DFP : DP Only ---- */ + +#ifdef CONFIG_USB_PD_ALT_MODE_DFP +void pd_dpm_dfp_send_dp_status_update(struct pd_port *pd_port); +void pd_dpm_dfp_inform_dp_status_update(struct pd_port *pd_port, bool ack); + +void pd_dpm_dfp_send_dp_configuration(struct pd_port *pd_port); +void pd_dpm_dfp_inform_dp_configuration(struct pd_port *pd_port, bool ack); +#endif + +/* ---- SVDM/UVDM ---- */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM + +void pd_dpm_ufp_recv_uvdm(struct pd_port *pd_port); +void pd_dpm_dfp_send_uvdm(struct pd_port *pd_port); +void pd_dpm_dfp_inform_uvdm(struct pd_port *pd_port, bool ack); + +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + +void pd_dpm_ufp_send_svdm_nak(struct pd_port *pd_port); + +/* ---- DRP : Inform PowerCap ---- */ + +void pd_dpm_dr_inform_sink_cap(struct pd_port *pd_port); +void pd_dpm_dr_inform_source_cap(struct pd_port *pd_port); + +/* ---- DRP : Data Role Swap ---- */ + +void pd_dpm_drs_evaluate_swap(struct pd_port *pd_port, uint8_t role); +void pd_dpm_drs_change_role(struct pd_port *pd_port, uint8_t role); + +/* ---- DRP : Power Role Swap ---- */ + +void pd_dpm_prs_evaluate_swap(struct pd_port *pd_port, uint8_t role); +void pd_dpm_prs_turn_off_power_sink(struct pd_port *pd_port); +void pd_dpm_prs_enable_power_source(struct pd_port *pd_port, bool en); +void pd_dpm_prs_change_role(struct pd_port *pd_port, uint8_t role); + +/* ---- DRP : Vconn Swap ---- */ + +void pd_dpm_vcs_evaluate_swap(struct pd_port *pd_port); +void pd_dpm_vcs_enable_vconn(struct pd_port *pd_port, uint8_t role); + + +/* + * PE : PD3.0 + */ + +#ifdef CONFIG_USB_PD_REV30 + +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL +int pd_dpm_send_source_cap_ext(struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE +void pd_dpm_inform_source_cap_ext(struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL +int pd_dpm_send_battery_cap(struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE +void pd_dpm_inform_battery_cap(struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL +int pd_dpm_send_battery_status(struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE +void pd_dpm_inform_battery_status(struct pd_port *pd_port); +#endif /* #ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL +int pd_dpm_send_mfrs_info(struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE +void pd_dpm_inform_mfrs_info(struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL +int pd_dpm_send_country_codes(struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE +void pd_dpm_inform_country_codes(struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL +int pd_dpm_send_country_info(struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE +void pd_dpm_inform_country_info(struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL +int pd_dpm_send_alert(struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE +void pd_dpm_inform_alert(struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL +int pd_dpm_send_status(struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE +void pd_dpm_inform_status(struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK +void pd_dpm_inform_pps_status(struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + +void pd_dpm_inform_not_support(struct pd_port *pd_port); + +#endif /* CONFIG_USB_PD_REV30 */ + +/* PE : Notify DPM */ + +int pd_dpm_notify_pe_startup(struct pd_port *pd_port); +int pd_dpm_notify_pe_hardreset(struct pd_port *pd_port); + +/* TCPCI - VBUS Control */ + +static inline int pd_dpm_check_vbus_valid(struct pd_port *pd_port) +{ + return tcpci_check_vbus_valid(pd_port->tcpc); +} + +static inline int pd_dpm_sink_vbus(struct pd_port *pd_port, bool en) +{ + int mv = en ? TCPC_VBUS_SINK_5V : TCPC_VBUS_SINK_0V; + + return tcpci_sink_vbus(pd_port->tcpc, + TCP_VBUS_CTRL_REQUEST, mv, -1); +} + +static inline int pd_dpm_source_vbus(struct pd_port *pd_port, bool en) +{ + int mv = en ? TCPC_VBUS_SOURCE_5V : TCPC_VBUS_SOURCE_0V; + + return tcpci_source_vbus(pd_port->tcpc, + TCP_VBUS_CTRL_REQUEST, mv, -1); +} + +/* Mode Operations */ + +#ifdef CONFIG_USB_PD_ALT_MODE +#ifdef CONFIG_USB_PD_ALT_MODE_DFP + +extern bool dp_dfp_u_notify_discover_id(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack); + +extern bool dp_dfp_u_notify_discover_svid( + struct pd_port *pd_port, struct svdm_svid_data *svid_data, bool ack); + +extern bool dp_dfp_u_notify_discover_modes( + struct pd_port *pd_port, struct svdm_svid_data *svid_data, bool ack); + + +extern bool dp_dfp_u_notify_enter_mode(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, uint8_t ops, bool ack); + +extern bool dp_dfp_u_notify_exit_mode( + struct pd_port *pd_port, struct svdm_svid_data *svid_data, uint8_t ops); + +extern bool dp_dfp_u_notify_attention(struct pd_port *pd_port, + struct svdm_svid_data *svid_data); +#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ + +extern void dp_ufp_u_request_enter_mode( + struct pd_port *pd_port, struct svdm_svid_data *svid_data, uint8_t ops); + +extern void dp_ufp_u_request_exit_mode( + struct pd_port *pd_port, struct svdm_svid_data *svid_data, uint8_t ops); + +#ifdef CONFIG_USB_PD_ALT_MODE_DFP +extern bool dp_dfp_u_notify_pe_startup( + struct pd_port *pd_port, struct svdm_svid_data *svid_data); + +extern int dp_dfp_u_notify_pe_ready(struct pd_port *pd_port, + struct svdm_svid_data *svid_data); +#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ + +extern bool dp_reset_state( + struct pd_port *pd_port, struct svdm_svid_data *svid_data); + +extern bool dp_parse_svid_data( + struct pd_port *pd_port, struct svdm_svid_data *svid_data); +#endif /* CONFIG_USB_PD_ALT_MODE */ + +#ifdef CONFIG_USB_PD_RICHTEK_UVDM +extern bool richtek_dfp_notify_pe_startup( + struct pd_port *pd_port, struct svdm_svid_data *svid_data); + +extern int richtek_dfp_notify_pe_ready(struct pd_port *pd_port, + struct svdm_svid_data *svid_data); + +extern bool richtek_dfp_notify_uvdm(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack); +extern bool richtek_ufp_notify_uvdm(struct pd_port *pd_port, + struct svdm_svid_data *svid_data); +#endif /* CONFIG_USB_PD_RICHTEK_UVDM */ + + +#ifdef CONFIG_USB_PD_ALT_MODE_RTDC +extern bool dc_dfp_notify_discover_id(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack); + +extern bool dc_dfp_notify_discover_svid(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack); + +extern bool dc_dfp_notify_discover_modes(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack); + +extern bool dc_dfp_notify_enter_mode(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, uint8_t ops, bool ack); + +extern bool dc_dfp_notify_exit_mode(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, uint8_t ops); + +extern bool dc_dfp_notify_pe_startup( + struct pd_port *pd_port, struct svdm_svid_data *svid_data); + +extern int dc_dfp_notify_pe_ready(struct pd_port *pd_port, + struct svdm_svid_data *svid_data); + +extern bool dc_dfp_notify_uvdm(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack); +extern bool dc_ufp_notify_uvdm(struct pd_port *pd_port, + struct svdm_svid_data *svid_data); + +extern bool dc_reset_state(struct pd_port *pd_port, + struct svdm_svid_data *svid_data); + +extern bool dc_parse_svid_data(struct pd_port *pd_port, + struct svdm_svid_data *svid_data); +#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */ + +#ifdef CONFIG_SUPPORT_MMI_ADAPTER +extern bool mmi_dfp_u_notify_discover_svid( + struct pd_port *pd_port, struct svdm_svid_data *svid_data, bool ack); +extern bool mmi_dfp_notify_uvdm(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack); +extern int mmi_notify_pe_ready(struct pd_port *pd_port, + struct svdm_svid_data *svid_data); +extern bool mmi_dfp_notify_pe_startup( + struct pd_port *pd_port, struct svdm_svid_data *svid_data); +#endif /* CONFIG_SUPPORT_MMI_ADAPTER */ + +/** + * pd_dpm_get_ready_reaction + * + * Get a pending event from the reaction's table definied by DPM. + * + * Returns TCP_DPM_EVT_ID if succeeded; + * Returns Zero to indicate there is no pending event. + * Returns DPM_READY_REACTION_BUSY to indicate + * waiting for previous reaction is finished. + */ + +#define DPM_READY_REACTION_BUSY (0xff) +extern uint8_t pd_dpm_get_ready_reaction(struct pd_port *pd_port); + +/* ---- DPM reactions ---- */ + + + +/* If receive reject/wait, cancel reaction */ +#define DPM_REACTION_REQUEST_PR_SWAP (1<<0) +#define DPM_REACTION_REQUEST_DR_SWAP (1<<1) +#define DPM_REACTION_GET_SINK_CAP (1<<2) +#define DPM_REACTION_GET_SOURCE_CAP (1<<3) +#define DPM_REACTION_ATTEMPT_GET_FLAG (1<<4) +#define DPM_REACTION_REQUEST_VCONN_SRC (1<<5) +#define DPM_REACTION_RETURN_VCONN_SRC (1<<6) +#define DPM_REACTION_REJECT_CANCEL (1<<7) /* FLAG ONLY */ + +#define DPM_REACTION_DFP_FLOW_DELAY (1<<10) +#define DPM_REACTION_UFP_FLOW_DELAY (1<<11) +#define DPM_REACTION_VCONN_STABLE_DELAY (1<<12) + +#define DPM_REACTION_DISCOVER_ID (1<<16) +#define DPM_REACTION_DISCOVER_SVID (1<<17) +#define DPM_REACTION_DISCOVER_CABLE (1<<18) +#define DPM_REACTION_DYNAMIC_VCONN (1<<19) + +#define DPM_REACTION_DISCOVER_CABLE_FLOW (\ + DPM_REACTION_DISCOVER_CABLE | \ + DPM_REACTION_REQUEST_VCONN_SRC |\ + DPM_REACTION_VCONN_STABLE_DELAY |\ + DPM_REACTION_DYNAMIC_VCONN) + +#define DPM_REACTION_GET_SOURCE_CAP_EXT (1<<24) + +#define DPM_REACTION_CAP_RESET_CABLE (1<<28) +#define DPM_REACTION_CAP_READY_ONCE (1<<29) +#define DPM_REACTION_CAP_DISCOVER_CABLE (1<<30) +#define DPM_REACTION_CAP_ALWAYS (1<<31) + +static inline void dpm_reaction_clear(struct pd_port *pd_port, uint32_t mask) +{ + pd_port->pe_data.dpm_ready_reactions &= ~mask; +} + +static inline void dpm_reaction_set(struct pd_port *pd_port, uint32_t mask) +{ + pd_port->pe_data.dpm_ready_reactions |= mask; +} + +static inline void dpm_reaction_set_ready_once(struct pd_port *pd_port) +{ + if (pd_check_pe_state_ready(pd_port)) + dpm_reaction_set(pd_port, DPM_REACTION_CAP_READY_ONCE); +} + +static inline void dpm_reaction_set_clear( + struct pd_port *pd_port, uint32_t set, uint32_t clear) +{ + uint32_t val = pd_port->pe_data.dpm_ready_reactions | set; + + pd_port->pe_data.dpm_ready_reactions = val & (~clear); +} + +static inline uint32_t dpm_reaction_check( + struct pd_port *pd_port, uint32_t mask) +{ + return pd_port->pe_data.dpm_ready_reactions & mask; +} +#endif /* PD_DPM_CORE_H */ diff --git a/drivers/usb/typec/mmi_tcpc/inc/pd_dpm_pdo_select.h b/drivers/usb/typec/mmi_tcpc/inc/pd_dpm_pdo_select.h new file mode 100644 index 000000000000..818d6a71f016 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/pd_dpm_pdo_select.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#ifndef PD_DPM_PDO_SELECT_H +#define PD_DPM_PDO_SELECT_H + +#include "inc/tcpci.h" + +struct dpm_pdo_info_t { + uint8_t type; + uint8_t apdo_type; + uint8_t pwr_limit; + int vmin; + int vmax; + int uw; + int ma; +}; + +struct dpm_rdo_info_t { + uint8_t pos; + uint8_t type; + bool mismatch; + + int vmin; + int vmax; + + union { + uint32_t max_uw; + uint32_t max_ma; + }; + + union { + uint32_t oper_uw; + uint32_t oper_ma; + }; +}; + +#define DPM_PDO_TYPE_FIXED TCPM_POWER_CAP_VAL_TYPE_FIXED +#define DPM_PDO_TYPE_VAR TCPM_POWER_CAP_VAL_TYPE_VARIABLE +#define DPM_PDO_TYPE_BAT TCPM_POWER_CAP_VAL_TYPE_BATTERY +#define DPM_PDO_TYPE_APDO TCPM_POWER_CAP_VAL_TYPE_AUGMENT + +#define DPM_APDO_TYPE_PPS (TCPM_POWER_CAP_APDO_TYPE_PPS) +#define DPM_APDO_TYPE_PPS_CF (TCPM_POWER_CAP_APDO_TYPE_PPS_CF) + +extern void dpm_extract_pdo_info( + uint32_t pdo, struct dpm_pdo_info_t *info); + +extern bool dpm_find_match_req_info(struct dpm_rdo_info_t *req_info, + struct dpm_pdo_info_t *sink, int cnt, uint32_t *src_pdos, + int min_uw, uint32_t select_rule); + +#endif /* PD_DPM_PDO_SELECT_H */ diff --git a/drivers/usb/typec/mmi_tcpc/inc/pd_policy_engine.h b/drivers/usb/typec/mmi_tcpc/inc/pd_policy_engine.h new file mode 100644 index 000000000000..8626b431be15 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/pd_policy_engine.h @@ -0,0 +1,937 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#ifndef PD_POLICY_ENGINE_H_ +#define PD_POLICY_ENGINE_H_ + +#include "pd_core.h" + +/* ---- Policy Engine State ---- */ + +enum pd_pe_state_machine { + PE_STATE_MACHINE_IDLE = 0, + PE_STATE_MACHINE_NORMAL, + PE_STATE_MACHINE_DR_SWAP, + PE_STATE_MACHINE_PR_SWAP, + PE_STATE_MACHINE_VCONN_SWAP, +}; + +/* ---- Policy Engine Runtime Flags ---- */ + +#define PE_STATE_FLAG_BACK_READY_IF_RECV_WAIT (1<<0) +#define PE_STATE_FLAG_BACK_READY_IF_RECV_REJECT (1<<1) +#define PE_STATE_FLAG_BACK_READY_IF_SR_TIMER_TOUT (1<<2) +#define PE_STATE_FLAG_BACK_READY_IF_TX_FAILED (1<<3) +#define PE_STATE_FLAG_HRESET_IF_SR_TIMEOUT (1<<4) +#define PE_STATE_FLAG_HRESET_IF_TX_FAILED (1<<5) +#define PE_STATE_FLAG_IGNORE_UNKNOWN_EVENT (1<<6) +#define PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER (1<<7) + +#define PE_STATE_WAIT_RESPONSE(pd_port) {\ + pd_port->pe_data.pe_state_flags = \ + PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER; } + +#define PE_STATE_WAIT_MSG(pd_port) {\ + pd_port->pe_data.pe_state_flags = \ + PE_STATE_FLAG_BACK_READY_IF_SR_TIMER_TOUT |\ + PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER; } + +#define PE_STATE_WAIT_MSG_HRESET_IF_TOUT(pd_port) {\ + pd_port->pe_data.pe_state_flags = \ + PE_STATE_FLAG_HRESET_IF_SR_TIMEOUT |\ + PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER; } + +#define PE_STATE_WAIT_MSG_OR_TX_FAILED(pd_port) {\ + pd_port->pe_data.pe_state_flags = \ + PE_STATE_FLAG_BACK_READY_IF_TX_FAILED |\ + PE_STATE_FLAG_BACK_READY_IF_SR_TIMER_TOUT |\ + PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER; } + +#define PE_STATE_WAIT_MSG_OR_RJ(pd_port) {\ + pd_port->pe_data.pe_state_flags = \ + PE_STATE_FLAG_BACK_READY_IF_RECV_REJECT |\ + PE_STATE_FLAG_BACK_READY_IF_SR_TIMER_TOUT |\ + PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER; } + +#define PE_STATE_WAIT_ANSWER_MSG(pd_port) {\ + pd_port->pe_data.pe_state_flags = \ + PE_STATE_FLAG_BACK_READY_IF_RECV_WAIT |\ + PE_STATE_FLAG_BACK_READY_IF_RECV_REJECT |\ + PE_STATE_FLAG_BACK_READY_IF_SR_TIMER_TOUT |\ + PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER; } + +#define PE_STATE_HRESET_IF_TX_FAILED(pd_port) {\ + pd_port->pe_data.pe_state_flags = \ + PE_STATE_FLAG_HRESET_IF_TX_FAILED; } + +#define PE_STATE_IGNORE_UNKNOWN_EVENT(pd_port) {\ + pd_port->pe_data.pe_state_flags = \ + PE_STATE_FLAG_IGNORE_UNKNOWN_EVENT; } + +#define PE_STATE_RECV_SOFT_RESET(pd_port) {\ + pd_port->pe_data.pe_state_flags = \ + PE_STATE_FLAG_HRESET_IF_TX_FAILED | \ + PE_STATE_FLAG_IGNORE_UNKNOWN_EVENT; } + +#define PE_STATE_SEND_SOFT_RESET(pd_port) {\ + pd_port->pe_data.pe_state_flags = \ + PE_STATE_FLAG_HRESET_IF_TX_FAILED |\ + PE_STATE_FLAG_HRESET_IF_SR_TIMEOUT |\ + PE_STATE_FLAG_IGNORE_UNKNOWN_EVENT |\ + PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER; } + +#define PE_STATE_FLAG_BACK_READY_IF_RECV_GOOD_CRC (1<<0) +#define PE_STATE_FLAG_BACK_READY_IF_DPM_ACK (1<<1) +#define PE_STATE_FLAG_DPM_ACK_IMMEDIATELY (1<<7) + +#define PE_STATE_WAIT_TX_SUCCESS(pd_port) {\ + pd_port->pe_data.pe_state_flags2 = \ + PE_STATE_FLAG_BACK_READY_IF_RECV_GOOD_CRC; } + +#define PE_STATE_DPM_INFORMED(pd_port) {\ + pd_port->pe_data.pe_state_flags2 = \ + PE_STATE_FLAG_BACK_READY_IF_DPM_ACK |\ + PE_STATE_FLAG_DPM_ACK_IMMEDIATELY; } + +#define PE_STATE_WAIT_DPM_ACK(pd_port) {\ + pd_port->pe_data.pe_state_flags2 = \ + PE_STATE_FLAG_BACK_READY_IF_DPM_ACK; } + +#define PE_STATE_DPM_ACK_IMMEDIATELY(pd_port) {\ + pd_port->pe_data.pe_state_flags2 |= \ + PE_STATE_FLAG_DPM_ACK_IMMEDIATELY; } + +#define VDM_STATE_FLAG_ENABLE_VDM_RESPONSE_TIMER (1<<0) +#define VDM_STATE_FLAG_DPM_ACK_IMMEDIATELY (1<<4) +#define VDM_STATE_FLAG_BACK_READY_IF_DPM_ACK (1<<6) +#define VDM_STATE_FLAG_BACK_READY_IF_RECV_GOOD_CRC (1<<7) + +#define VDM_STATE_DPM_INFORMED(pd_port) {\ + pd_port->pe_data.vdm_state_flags = \ + VDM_STATE_FLAG_BACK_READY_IF_DPM_ACK |\ + VDM_STATE_FLAG_DPM_ACK_IMMEDIATELY; } + +#define VDM_STATE_REPLY_SVDM_REQUEST(pd_port) {\ + pd_port->pe_data.vdm_state_flags = \ + VDM_STATE_FLAG_BACK_READY_IF_RECV_GOOD_CRC; } + +#define VDM_STATE_NORESP_CMD(pd_port) {\ + pd_port->pe_data.vdm_state_flags = \ + VDM_STATE_FLAG_BACK_READY_IF_RECV_GOOD_CRC; } + +#define VDM_STATE_RESPONSE_CMD(pd_port, timer_id) {\ + pd_port->pe_data.vdm_state_flags = \ + VDM_STATE_FLAG_ENABLE_VDM_RESPONSE_TIMER; \ + pd_port->pe_data.vdm_state_timer = timer_id; } + +static inline bool pd_check_pe_during_hard_reset(struct pd_port *pd_port) +{ + return pd_port->tcpc->pd_wait_hard_reset_complete; +} + +enum pd_pe_state { + PE_STATE_START_ID = -1, +/******************* Source *******************/ +#ifdef CONFIG_USB_PD_PE_SOURCE + PE_SRC_STARTUP, + PE_SRC_DISCOVERY, + PE_SRC_SEND_CAPABILITIES, + PE_SRC_NEGOTIATE_CAPABILITIES, + PE_SRC_TRANSITION_SUPPLY, + PE_SRC_TRANSITION_SUPPLY2, + PE_SRC_READY, + PE_SRC_DISABLED, + PE_SRC_CAPABILITY_RESPONSE, + PE_SRC_HARD_RESET, + PE_SRC_HARD_RESET_RECEIVED, + PE_SRC_TRANSITION_TO_DEFAULT, + PE_SRC_GET_SINK_CAP, + PE_SRC_WAIT_NEW_CAPABILITIES, + PE_SRC_SEND_SOFT_RESET, + PE_SRC_SOFT_RESET, + +/* Source Startup Discover Cable */ +#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID +#ifdef CONFIG_PD_SRC_RESET_CABLE + PE_SRC_CBL_SEND_SOFT_RESET, +#endif /* CONFIG_PD_SRC_RESET_CABLE */ + PE_SRC_VDM_IDENTITY_REQUEST, + PE_SRC_VDM_IDENTITY_ACKED, + PE_SRC_VDM_IDENTITY_NAKED, +#endif /* PD_CAP_PE_SRC_STARTUP_DISCOVER_ID */ + +/* Source for PD30 */ +#ifdef CONFIG_USB_PD_REV30 + PE_SRC_SEND_NOT_SUPPORTED, + PE_SRC_NOT_SUPPORTED_RECEIVED, + PE_SRC_CHUNK_RECEIVED, +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL + PE_SRC_SEND_SOURCE_ALERT, +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE + PE_SRC_SINK_ALERT_RECEIVED, +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL + PE_SRC_GIVE_SOURCE_CAP_EXT, +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL + PE_SRC_GIVE_SOURCE_STATUS, +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE + PE_SRC_GET_SINK_STATUS, +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE + PE_SRC_GIVE_PPS_STATUS, +#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */ +#endif /* CONFIG_USB_PD_REV30 */ +#endif /* CONFIG_USB_PD_PE_SOURCE */ + +/******************* Sink *******************/ +#ifdef CONFIG_USB_PD_PE_SINK +/* Sink Init */ + PE_SNK_STARTUP, + PE_SNK_DISCOVERY, + PE_SNK_WAIT_FOR_CAPABILITIES, + PE_SNK_EVALUATE_CAPABILITY, + PE_SNK_SELECT_CAPABILITY, + PE_SNK_TRANSITION_SINK, + PE_SNK_READY, + PE_SNK_HARD_RESET, + PE_SNK_TRANSITION_TO_DEFAULT, + PE_SNK_GIVE_SINK_CAP, + PE_SNK_GET_SOURCE_CAP, + + PE_SNK_SEND_SOFT_RESET, + PE_SNK_SOFT_RESET, + +/* Sink for PD30 */ +#ifdef CONFIG_USB_PD_REV30 + PE_SNK_SEND_NOT_SUPPORTED, + PE_SNK_NOT_SUPPORTED_RECEIVED, + PE_SNK_CHUNK_RECEIVED, +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE + PE_SNK_SOURCE_ALERT_RECEIVED, +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL + PE_SNK_SEND_SINK_ALERT, +#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE + PE_SNK_GET_SOURCE_CAP_EXT, +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE + PE_SNK_GET_SOURCE_STATUS, +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL + PE_SNK_GIVE_SINK_STATUS, +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + PE_SNK_GET_PPS_STATUS, +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ +#endif /* CONFIG_USB_PD_REV30 */ +#endif /* CONFIG_USB_PD_PE_SINK */ + +/******************* DR_SWAP *******************/ +#ifdef CONFIG_USB_PD_DR_SWAP +/* DR_SWAP_DFP */ + PE_DRS_DFP_UFP_EVALUATE_DR_SWAP, + PE_DRS_DFP_UFP_ACCEPT_DR_SWAP, + PE_DRS_DFP_UFP_CHANGE_TO_UFP, + PE_DRS_DFP_UFP_SEND_DR_SWAP, + PE_DRS_DFP_UFP_REJECT_DR_SWAP, +/* DR_SWAP_UFP */ + PE_DRS_UFP_DFP_EVALUATE_DR_SWAP, + PE_DRS_UFP_DFP_ACCEPT_DR_SWAP, + PE_DRS_UFP_DFP_CHANGE_TO_DFP, + PE_DRS_UFP_DFP_SEND_DR_SWAP, + PE_DRS_UFP_DFP_REJECT_DR_SWAP, +#endif /* CONFIG_USB_PD_DR_SWAP */ + +/******************* PR_SWAP *******************/ +#ifdef CONFIG_USB_PD_PR_SWAP +/* PR_SWAP_SRC */ + PE_PRS_SRC_SNK_EVALUATE_PR_SWAP, + PE_PRS_SRC_SNK_ACCEPT_PR_SWAP, + PE_PRS_SRC_SNK_TRANSITION_TO_OFF, + PE_PRS_SRC_SNK_ASSERT_RD, + PE_PRS_SRC_SNK_WAIT_SOURCE_ON, + PE_PRS_SRC_SNK_SEND_SWAP, + PE_PRS_SRC_SNK_REJECT_PR_SWAP, + +/* PR_SWAP_SNK */ + PE_PRS_SNK_SRC_EVALUATE_PR_SWAP, + PE_PRS_SNK_SRC_ACCEPT_PR_SWAP, + PE_PRS_SNK_SRC_TRANSITION_TO_OFF, + PE_PRS_SNK_SRC_ASSERT_RP, + PE_PRS_SNK_SRC_SOURCE_ON, + PE_PRS_SNK_SRC_SEND_SWAP, + PE_PRS_SNK_SRC_REJECT_SWAP, + +/* get same role cap */ + PE_DR_SRC_GET_SOURCE_CAP, + PE_DR_SRC_GIVE_SINK_CAP, + PE_DR_SNK_GET_SINK_CAP, + PE_DR_SNK_GIVE_SOURCE_CAP, + +/* get same role cap for PD30 */ +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL + PE_DR_SNK_GIVE_SOURCE_CAP_EXT, +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE + PE_DR_SRC_GET_SOURCE_CAP_EXT, +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ +#endif /* CONFIG_USB_PD_REV30 */ +#endif /* CONFIG_USB_PD_PR_SWAP */ + +/******************* VCONN_SWAP *******************/ +#ifdef CONFIG_USB_PD_VCONN_SWAP + PE_VCS_SEND_SWAP, + PE_VCS_EVALUATE_SWAP, + PE_VCS_ACCEPT_SWAP, + PE_VCS_REJECT_VCONN_SWAP, + PE_VCS_WAIT_FOR_VCONN, + PE_VCS_TURN_OFF_VCONN, + PE_VCS_TURN_ON_VCONN, + PE_VCS_SEND_PS_RDY, +#endif /* CONFIG_USB_PD_VCONN_SWAP */ + +/******************* UFP_VDM *******************/ + PE_UFP_VDM_GET_IDENTITY, + PE_UFP_VDM_GET_SVIDS, + PE_UFP_VDM_GET_MODES, + PE_UFP_VDM_EVALUATE_MODE_ENTRY, + PE_UFP_VDM_MODE_EXIT, + PE_UFP_VDM_ATTENTION_REQUEST, + +#ifdef CONFIG_USB_PD_ALT_MODE + PE_UFP_VDM_DP_STATUS_UPDATE, + PE_UFP_VDM_DP_CONFIGURE, +#endif/* CONFIG_USB_PD_ALT_MODE */ + +/******************* DFP_VDM *******************/ + PE_DFP_UFP_VDM_IDENTITY_REQUEST, + PE_DFP_UFP_VDM_IDENTITY_ACKED, + PE_DFP_UFP_VDM_IDENTITY_NAKED, + PE_DFP_CBL_VDM_IDENTITY_REQUEST, + PE_DFP_CBL_VDM_IDENTITY_ACKED, + PE_DFP_CBL_VDM_IDENTITY_NAKED, + + PE_DFP_VDM_SVIDS_REQUEST, + PE_DFP_VDM_SVIDS_ACKED, + PE_DFP_VDM_SVIDS_NAKED, + PE_DFP_VDM_MODES_REQUEST, + PE_DFP_VDM_MODES_ACKED, + PE_DFP_VDM_MODES_NAKED, + PE_DFP_VDM_MODE_ENTRY_REQUEST, + PE_DFP_VDM_MODE_ENTRY_ACKED, + PE_DFP_VDM_MODE_ENTRY_NAKED, + PE_DFP_VDM_MODE_EXIT_REQUEST, + PE_DFP_VDM_MODE_EXIT_ACKED, + PE_DFP_VDM_ATTENTION_REQUEST, + +#ifdef CONFIG_PD_DFP_RESET_CABLE + PE_DFP_CBL_SEND_SOFT_RESET, + PE_DFP_CBL_SEND_CABLE_RESET, +#endif /* CONFIG_PD_DFP_RESET_CABLE */ + +#ifdef CONFIG_USB_PD_ALT_MODE_DFP + PE_DFP_VDM_DP_STATUS_UPDATE_REQUEST, + PE_DFP_VDM_DP_STATUS_UPDATE_ACKED, + PE_DFP_VDM_DP_STATUS_UPDATE_NAKED, + + PE_DFP_VDM_DP_CONFIGURATION_REQUEST, + PE_DFP_VDM_DP_CONFIGURATION_ACKED, + PE_DFP_VDM_DP_CONFIGURATION_NAKED, +#endif/* CONFIG_USB_PD_ALT_MODE_DFP */ + +/******************* UVDM & SVDM *******************/ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM + PE_UFP_UVDM_RECV, + PE_DFP_UVDM_SEND, + PE_DFP_UVDM_ACKED, + PE_DFP_UVDM_NAKED, +#endif/* CONFIG_USB_PD_CUSTOM_VDM */ + + PE_UFP_VDM_SEND_NAK, + +/******************* PD30 Common *******************/ +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE + PE_GET_BATTERY_CAP, +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL + PE_GIVE_BATTERY_CAP, +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE + PE_GET_BATTERY_STATUS, +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL + PE_GIVE_BATTERY_STATUS, +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE + PE_GET_MANUFACTURER_INFO, +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL + PE_GIVE_MANUFACTURER_INFO, +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE + PE_GET_COUNTRY_CODES, +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL + PE_GIVE_COUNTRY_CODES, +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE + PE_GET_COUNTRY_INFO, +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL + PE_GIVE_COUNTRY_INFO, +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL */ + PE_VDM_NOT_SUPPORTED, +#endif /* CONFIG_USB_PD_REV30 */ + +/******************* Others *******************/ +#ifdef CONFIG_USB_PD_CUSTOM_DBGACC + PE_DBG_READY, +#endif/* CONFIG_USB_PD_CUSTOM_DBGACC */ + +#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER + PE_OVER_RECV_HRESET_LIMIT, +#endif/* CONFIG_USB_PD_RECV_HRESET_COUNTER */ + + PE_REJECT, + PE_ERROR_RECOVERY, +#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE + PE_ERROR_RECOVERY_ONCE, +#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */ + PE_BIST_TEST_DATA, + PE_BIST_CARRIER_MODE_2, +/* Wait tx finished */ + PE_IDLE1, + PE_IDLE2, + PD_NR_PE_STATES, +}; + +/** + * pd_policy_engine_run + * + * Driving the PE to get next event, and take the corresponding actions. + * + * + * Returns True if a pending event is processed; + * Returns Zero to indicate there is no pending event. + * Returns Negative Value if an error occurs. + */ + +int pd_policy_engine_run(struct tcpc_device *tcpc); + + +/* ---- Policy Engine (General) ---- */ + +void pe_power_ready_entry(struct pd_port *pd_port); + +static inline void pe_send_swap_request_entry( + struct pd_port *pd_port, uint8_t msg) +{ + PE_STATE_WAIT_ANSWER_MSG(pd_port); + pd_send_sop_ctrl_msg(pd_port, msg); +} + +/******************* Source *******************/ +#ifdef CONFIG_USB_PD_PE_SOURCE +void pe_src_startup_entry( + struct pd_port *pd_port); +void pe_src_discovery_entry( + struct pd_port *pd_port); +void pe_src_send_capabilities_entry( + struct pd_port *pd_port); +void pe_src_negotiate_capabilities_entry( + struct pd_port *pd_port); +void pe_src_transition_supply_entry( + struct pd_port *pd_port); +void pe_src_transition_supply_exit( + struct pd_port *pd_port); +void pe_src_transition_supply2_entry( + struct pd_port *pd_port); +void pe_src_ready_entry( + struct pd_port *pd_port); +void pe_src_disabled_entry( + struct pd_port *pd_port); +void pe_src_capability_response_entry( + struct pd_port *pd_port); +void pe_src_hard_reset_entry( + struct pd_port *pd_port); +void pe_src_hard_reset_received_entry( + struct pd_port *pd_port); +void pe_src_transition_to_default_entry( + struct pd_port *pd_port); +void pe_src_transition_to_default_exit( + struct pd_port *pd_port); +void pe_src_get_sink_cap_entry( + struct pd_port *pd_port); +void pe_src_get_sink_cap_exit( + struct pd_port *pd_port); +void pe_src_wait_new_capabilities_entry( + struct pd_port *pd_port); +void pe_src_send_soft_reset_entry( + struct pd_port *pd_port); +void pe_src_soft_reset_entry( + struct pd_port *pd_port); + +/* Source Startup Discover Cable */ +#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID +#ifdef CONFIG_PD_SRC_RESET_CABLE +void pe_src_cbl_send_soft_reset_entry( + struct pd_port *pd_port); +#endif /* CONFIG_PD_SRC_RESET_CABLE */ +void pe_src_vdm_identity_request_entry( + struct pd_port *pd_port); +void pe_src_vdm_identity_acked_entry( + struct pd_port *pd_port); +void pe_src_vdm_identity_naked_entry( + struct pd_port *pd_port); +#endif /* PD_CAP_PE_SRC_STARTUP_DISCOVER_ID */ + +/* Source for PD30 */ +#ifdef CONFIG_USB_PD_REV30 +void pe_src_send_not_supported_entry( + struct pd_port *pd_port); +void pe_src_not_supported_received_entry( + struct pd_port *pd_port); +void pe_src_chunk_received_entry( + struct pd_port *pd_port); +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL +void pe_src_send_source_alert_entry( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE +void pe_src_sink_alert_received_entry( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL +void pe_src_give_source_cap_ext_entry( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL +void pe_src_give_source_status_entry( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE +void pe_src_get_sink_status_entry( + struct pd_port *pd_port); +void pe_src_get_sink_status_exit( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE +void pe_src_give_pps_status_entry( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */ +#endif /* CONFIG_USB_PD_REV30 */ +#endif /* CONFIG_USB_PD_PE_SOURCE */ + +/******************* Sink *******************/ +#ifdef CONFIG_USB_PD_PE_SINK +/* Sink Init */ +void pe_snk_startup_entry( + struct pd_port *pd_port); +void pe_snk_discovery_entry( + struct pd_port *pd_port); +void pe_snk_wait_for_capabilities_entry( + struct pd_port *pd_port); +void pe_snk_evaluate_capability_entry( + struct pd_port *pd_port); +void pe_snk_select_capability_entry( + struct pd_port *pd_port); +void pe_snk_select_capability_exit( + struct pd_port *pd_port); +void pe_snk_transition_sink_entry( + struct pd_port *pd_port); +void pe_snk_transition_sink_exit( + struct pd_port *pd_port); +void pe_snk_ready_entry( + struct pd_port *pd_port); +void pe_snk_hard_reset_entry( + struct pd_port *pd_port); +void pe_snk_transition_to_default_entry( + struct pd_port *pd_port); +void pe_snk_give_sink_cap_entry( + struct pd_port *pd_port); +void pe_snk_get_source_cap_entry( + struct pd_port *pd_port); + +void pe_snk_send_soft_reset_entry( + struct pd_port *pd_port); +void pe_snk_soft_reset_entry( + struct pd_port *pd_port); + +/* Sink for PD30 */ +#ifdef CONFIG_USB_PD_REV30 +void pe_snk_send_not_supported_entry( + struct pd_port *pd_port); +void pe_snk_not_supported_received_entry( + struct pd_port *pd_port); +void pe_snk_chunk_received_entry( + struct pd_port *pd_port); +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE +void pe_snk_source_alert_received_entry( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL +void pe_snk_send_sink_alert_entry( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE +void pe_snk_get_source_cap_ext_entry( + struct pd_port *pd_port); +void pe_snk_get_source_cap_ext_exit( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE +void pe_snk_get_source_status_entry( + struct pd_port *pd_port); +void pe_snk_get_source_status_exit( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL +void pe_snk_give_sink_status_entry( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_PPS_SINK +void pe_snk_get_pps_status_entry( + struct pd_port *pd_port); +void pe_snk_get_pps_status_exit( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ +#endif /* CONFIG_USB_PD_REV30 */ +#endif /* CONFIG_USB_PD_PE_SINK */ + +/******************* DR_SWAP *******************/ +#ifdef CONFIG_USB_PD_DR_SWAP +/* DR_SWAP_DFP */ +void pe_drs_dfp_ufp_evaluate_dr_swap_entry( + struct pd_port *pd_port); +void pe_drs_dfp_ufp_accept_dr_swap_entry( + struct pd_port *pd_port); +void pe_drs_dfp_ufp_change_to_ufp_entry( + struct pd_port *pd_port); +void pe_drs_dfp_ufp_send_dr_swap_entry( + struct pd_port *pd_port); +void pe_drs_dfp_ufp_reject_dr_swap_entry( + struct pd_port *pd_port); +/* DR_SWAP_UFP */ +void pe_drs_ufp_dfp_evaluate_dr_swap_entry( + struct pd_port *pd_port); +void pe_drs_ufp_dfp_accept_dr_swap_entry( + struct pd_port *pd_port); +void pe_drs_ufp_dfp_change_to_dfp_entry( + struct pd_port *pd_port); +void pe_drs_ufp_dfp_send_dr_swap_entry( + struct pd_port *pd_port); +void pe_drs_ufp_dfp_reject_dr_swap_entry( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_DR_SWAP */ + +/******************* PR_SWAP *******************/ +#ifdef CONFIG_USB_PD_PR_SWAP +/* PR_SWAP_SRC */ +void pe_prs_src_snk_evaluate_pr_swap_entry( + struct pd_port *pd_port); +void pe_prs_src_snk_accept_pr_swap_entry( + struct pd_port *pd_port); +void pe_prs_src_snk_transition_to_off_entry( + struct pd_port *pd_port); +void pe_prs_src_snk_assert_rd_entry( + struct pd_port *pd_port); +void pe_prs_src_snk_wait_source_on_entry( + struct pd_port *pd_port); +void pe_prs_src_snk_wait_source_on_exit( + struct pd_port *pd_port); +void pe_prs_src_snk_send_swap_entry( + struct pd_port *pd_port); +void pe_prs_src_snk_reject_pr_swap_entry( + struct pd_port *pd_port); + +/* PR_SWAP_SNK */ +void pe_prs_snk_src_evaluate_pr_swap_entry( + struct pd_port *pd_port); +void pe_prs_snk_src_accept_pr_swap_entry( + struct pd_port *pd_port); +void pe_prs_snk_src_transition_to_off_entry( + struct pd_port *pd_port); +void pe_prs_snk_src_transition_to_off_exit( + struct pd_port *pd_port); +void pe_prs_snk_src_assert_rp_entry( + struct pd_port *pd_port); +void pe_prs_snk_src_source_on_entry( + struct pd_port *pd_port); +void pe_prs_snk_src_source_on_exit( + struct pd_port *pd_port); +void pe_prs_snk_src_send_swap_entry( + struct pd_port *pd_port); +void pe_prs_snk_src_reject_swap_entry( + struct pd_port *pd_port); + +/* get same role cap */ +void pe_dr_src_get_source_cap_entry( + struct pd_port *pd_port); +void pe_dr_src_get_source_cap_exit( + struct pd_port *pd_port); +void pe_dr_src_give_sink_cap_entry( + struct pd_port *pd_port); +void pe_dr_snk_get_sink_cap_entry( + struct pd_port *pd_port); +void pe_dr_snk_get_sink_cap_exit( + struct pd_port *pd_port); +void pe_dr_snk_give_source_cap_entry( + struct pd_port *pd_port); + +/* get same role cap for PD30 */ +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL +void pe_dr_snk_give_source_cap_ext_entry( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE +void pe_dr_src_get_source_cap_ext_entry( + struct pd_port *pd_port); +void pe_dr_src_get_source_cap_ext_exit( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ +#endif /* CONFIG_USB_PD_REV30 */ +#endif /* CONFIG_USB_PD_PR_SWAP */ + +/******************* VCONN_SWAP *******************/ +#ifdef CONFIG_USB_PD_VCONN_SWAP +void pe_vcs_send_swap_entry( + struct pd_port *pd_port); +void pe_vcs_evaluate_swap_entry( + struct pd_port *pd_port); +void pe_vcs_accept_swap_entry( + struct pd_port *pd_port); +void pe_vcs_reject_vconn_swap_entry( + struct pd_port *pd_port); +void pe_vcs_wait_for_vconn_entry( + struct pd_port *pd_port); +void pe_vcs_wait_for_vconn_exit( + struct pd_port *pd_port); +void pe_vcs_turn_off_vconn_entry( + struct pd_port *pd_port); +void pe_vcs_turn_on_vconn_entry( + struct pd_port *pd_port); +void pe_vcs_send_ps_rdy_entry( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_VCONN_SWAP */ + +/******************* UFP_VDM *******************/ +void pe_ufp_vdm_get_identity_entry( + struct pd_port *pd_port); +void pe_ufp_vdm_send_identity_entry( + struct pd_port *pd_port); +void pe_ufp_vdm_get_identity_nak_entry( + struct pd_port *pd_port); +void pe_ufp_vdm_get_svids_entry( + struct pd_port *pd_port); +void pe_ufp_vdm_send_svids_entry( + struct pd_port *pd_port); +void pe_ufp_vdm_get_svids_nak_entry( + struct pd_port *pd_port); +void pe_ufp_vdm_get_modes_entry( + struct pd_port *pd_port); +void pe_ufp_vdm_send_modes_entry( + struct pd_port *pd_port); +void pe_ufp_vdm_get_modes_nak_entry( + struct pd_port *pd_port); +void pe_ufp_vdm_evaluate_mode_entry_entry( + struct pd_port *pd_port); +void pe_ufp_vdm_mode_entry_ack_entry( + struct pd_port *pd_port); +void pe_ufp_vdm_mode_entry_nak_entry( + struct pd_port *pd_port); +void pe_ufp_vdm_mode_exit_entry( + struct pd_port *pd_port); +void pe_ufp_vdm_mode_exit_ack_entry( + struct pd_port *pd_port); +void pe_ufp_vdm_mode_exit_nak_entry( + struct pd_port *pd_port); + +void pe_ufp_vdm_attention_request_entry( + struct pd_port *pd_port); + +#ifdef CONFIG_USB_PD_ALT_MODE +void pe_ufp_vdm_dp_status_update_entry( + struct pd_port *pd_port); +void pe_ufp_vdm_dp_configure_entry( + struct pd_port *pd_port); +#endif/* CONFIG_USB_PD_ALT_MODE */ +/******************* DFP_VDM *******************/ +void pe_dfp_ufp_vdm_identity_request_entry( + struct pd_port *pd_port); +void pe_dfp_ufp_vdm_identity_acked_entry( + struct pd_port *pd_port); +void pe_dfp_ufp_vdm_identity_naked_entry( + struct pd_port *pd_port); +void pe_dfp_cbl_vdm_identity_request_entry( + struct pd_port *pd_port); +void pe_dfp_cbl_vdm_identity_acked_entry( + struct pd_port *pd_port); +void pe_dfp_cbl_vdm_identity_naked_entry( + struct pd_port *pd_port); + +void pe_dfp_vdm_svids_request_entry( + struct pd_port *pd_port); +void pe_dfp_vdm_svids_acked_entry( + struct pd_port *pd_port); +void pe_dfp_vdm_svids_naked_entry( + struct pd_port *pd_port); +void pe_dfp_vdm_modes_request_entry( + struct pd_port *pd_port); +void pe_dfp_vdm_modes_acked_entry( + struct pd_port *pd_port); +void pe_dfp_vdm_modes_naked_entry( + struct pd_port *pd_port); +void pe_dfp_vdm_mode_entry_request_entry( + struct pd_port *pd_port); +void pe_dfp_vdm_mode_entry_acked_entry( + struct pd_port *pd_port); +void pe_dfp_vdm_mode_entry_naked_entry( + struct pd_port *pd_port); +void pe_dfp_vdm_mode_exit_request_entry( + struct pd_port *pd_port); +void pe_dfp_vdm_mode_exit_acked_entry( + struct pd_port *pd_port); +void pe_dfp_vdm_attention_request_entry( + struct pd_port *pd_port); + +#ifdef CONFIG_PD_DFP_RESET_CABLE +void pe_dfp_cbl_send_soft_reset_entry( + struct pd_port *pd_port); +void pe_dfp_cbl_send_cable_reset_entry( + struct pd_port *pd_port); +#endif /* CONFIG_PD_DFP_RESET_CABLE */ +#ifdef CONFIG_USB_PD_ALT_MODE_DFP +void pe_dfp_vdm_dp_status_update_request_entry( + struct pd_port *pd_port); +void pe_dfp_vdm_dp_status_update_acked_entry( + struct pd_port *pd_port); +void pe_dfp_vdm_dp_status_update_naked_entry( + struct pd_port *pd_port); + +void pe_dfp_vdm_dp_configuration_request_entry( + struct pd_port *pd_port); +void pe_dfp_vdm_dp_configuration_acked_entry( + struct pd_port *pd_port); +void pe_dfp_vdm_dp_configuration_naked_entry( + struct pd_port *pd_port); +#endif/* CONFIG_USB_PD_ALT_MODE_DFP */ +/******************* UVDM & SVDM *******************/ +#ifdef CONFIG_USB_PD_CUSTOM_VDM +void pe_ufp_uvdm_recv_entry( + struct pd_port *pd_port); +void pe_dfp_uvdm_send_entry( + struct pd_port *pd_port); +void pe_dfp_uvdm_acked_entry( + struct pd_port *pd_port); +void pe_dfp_uvdm_naked_entry( + struct pd_port *pd_port); +#endif/* CONFIG_USB_PD_CUSTOM_VDM */ +void pe_ufp_vdm_send_nak_entry( + struct pd_port *pd_port); + +/******************* PD30 Common *******************/ +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE +void pe_get_battery_cap_entry( + struct pd_port *pd_port); +void pe_get_battery_cap_exit( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL +void pe_give_battery_cap_entry( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE +void pe_get_battery_status_entry( + struct pd_port *pd_port); +void pe_get_battery_status_exit( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL +void pe_give_battery_status_entry( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE +void pe_get_manufacturer_info_entry( + struct pd_port *pd_port); +void pe_get_manufacturer_info_exit( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL +void pe_give_manufacturer_info_entry( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE +void pe_get_country_codes_entry( + struct pd_port *pd_port); +void pe_get_country_codes_exit( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL +void pe_give_country_codes_entry( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE +void pe_get_country_info_entry( + struct pd_port *pd_port); +void pe_get_country_info_exit( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL +void pe_give_country_info_entry( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL */ +void pe_vdm_not_supported_entry( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_REV30 */ +/******************* Others *******************/ +#ifdef CONFIG_USB_PD_CUSTOM_DBGACC +void pe_dbg_ready_entry( + struct pd_port *pd_port); +#endif/* CONFIG_USB_PD_CUSTOM_DBGACC */ + +#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER +void pe_over_recv_hreset_limit_entry( + struct pd_port *pd_port); +#endif/* CONFIG_USB_PD_RECV_HRESET_COUNTER */ +void pe_reject_entry( + struct pd_port *pd_port); +void pe_error_recovery_entry( + struct pd_port *pd_port); +#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE +void pe_error_recovery_once_entry( + struct pd_port *pd_port); +#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */ +void pe_bist_test_data_entry( + struct pd_port *pd_port); +void pe_bist_test_data_exit( + struct pd_port *pd_port); +void pe_bist_carrier_mode_2_entry( + struct pd_port *pd_port); +void pe_bist_carrier_mode_2_exit( + struct pd_port *pd_port); +/* Wait tx finished */ +void pe_idle1_entry( + struct pd_port *pd_port); +void pe_idle2_entry( + struct pd_port *pd_port); + +#endif /* PD_POLICY_ENGINE_H_ */ diff --git a/drivers/usb/typec/mmi_tcpc/inc/pd_process_evt.h b/drivers/usb/typec/mmi_tcpc/inc/pd_process_evt.h new file mode 100644 index 000000000000..52aac796f05c --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/pd_process_evt.h @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#ifndef PD_PROCESS_EVT_H_ +#define PD_PROCESS_EVT_H_ + +#include "tcpci.h" +#include "pd_policy_engine.h" + +/*---------------------------------------------------------------------------*/ + +struct pe_state_transition { + uint8_t curr_state; /*state, msg, or cmd */ + uint8_t next_state; +}; + +struct pe_state_reaction { + uint16_t nr_transition; + const struct pe_state_transition *state_transition; +}; + +#define DECL_PE_STATE_TRANSITION(state) \ + static const struct pe_state_transition state##_state_transition[] + +#define DECL_PE_STATE_REACTION(state) \ + static const struct pe_state_reaction state##_reactions = {\ + .nr_transition = ARRAY_SIZE(state##_state_transition),\ + .state_transition = state##_state_transition,\ + } + +/*----------------------------------------------------------------------------- + * Sink & Source Common Event + *--------------------------------------------------------------------------- + */ + +bool pd_process_protocol_error( + struct pd_port *pd_port, struct pd_event *pd_event); + +bool pd_process_tx_failed(struct pd_port *pd_port); + +/*---------------------------------------------------------------------------*/ + +#define PE_TRANSIT_STATE(pd_port, state) \ + (pd_port->pe_state_next = state) + +#define PE_TRANSIT_DATA_STATE(pd_port, ufp, dfp) \ + (pd_port->pe_state_next =\ + ((pd_port->data_role == PD_ROLE_UFP) ? ufp : dfp)) + +static inline uint8_t pe_get_curr_ready_state(struct pd_port *pd_port) +{ + return pd_port->curr_ready_state; +} + +static inline uint8_t pe_get_curr_hard_reset_state(struct pd_port *pd_port) +{ + return pd_port->curr_hreset_state; +} + +static inline uint8_t pe_get_curr_soft_reset_state(struct pd_port *pd_port) +{ + return pd_port->curr_sreset_state; +} + +static inline uint8_t pe_get_curr_evaluate_pr_swap_state( + struct pd_port *pd_port) +{ + if (pd_port->power_role == PD_ROLE_SINK) + return PE_PRS_SNK_SRC_EVALUATE_PR_SWAP; + + return PE_PRS_SRC_SNK_EVALUATE_PR_SWAP; +} + +static inline uint8_t pe_get_curr_send_pr_swap_state( + struct pd_port *pd_port) +{ + if (pd_port->power_role == PD_ROLE_SINK) + return PE_PRS_SNK_SRC_SEND_SWAP; + + return PE_PRS_SRC_SNK_SEND_SWAP; +} + +static inline uint8_t pd_get_curr_hard_reset_recv_state( + struct pd_port *pd_port) +{ + if (pd_port->power_role == PD_ROLE_SINK) + return PE_SNK_TRANSITION_TO_DEFAULT; + + return PE_SRC_HARD_RESET_RECEIVED; +} + +static inline uint8_t pd_get_curr_soft_reset_recv_state( + struct pd_port *pd_port) +{ + if (pd_port->power_role == PD_ROLE_SINK) + return PE_SNK_SOFT_RESET; + + return PE_SRC_SOFT_RESET; +} + +static inline void pe_transit_ready_state(struct pd_port *pd_port) +{ + PE_TRANSIT_STATE(pd_port, pe_get_curr_ready_state(pd_port)); +} + +static inline void pe_transit_hard_reset_state(struct pd_port *pd_port) +{ + PE_TRANSIT_STATE(pd_port, pe_get_curr_hard_reset_state(pd_port)); +} + +static inline void pe_transit_soft_reset_state(struct pd_port *pd_port) +{ + PE_TRANSIT_STATE(pd_port, pe_get_curr_soft_reset_state(pd_port)); +} + +static inline void pe_transit_soft_reset_recv_state(struct pd_port *pd_port) +{ + PE_TRANSIT_STATE(pd_port, pd_get_curr_soft_reset_recv_state(pd_port)); +} + +static inline void pe_transit_evaluate_pr_swap_state(struct pd_port *pd_port) +{ + PE_TRANSIT_STATE(pd_port, + pe_get_curr_evaluate_pr_swap_state(pd_port)); +} + +static inline void pe_transit_send_pr_swap_state(struct pd_port *pd_port) +{ + PE_TRANSIT_STATE(pd_port, + pe_get_curr_send_pr_swap_state(pd_port)); +} + +static inline void pe_transit_hard_reset_recv_state(struct pd_port *pd_port) +{ + PE_TRANSIT_STATE(pd_port, + pd_get_curr_hard_reset_recv_state(pd_port)); +} + +/*---------------------------------------------------------------------------*/ + +static inline bool pd_check_pe_state_ready(struct pd_port *pd_port) +{ + uint8_t ready_state = pe_get_curr_ready_state(pd_port); + + return pd_port->pe_state_curr == ready_state; +} + +/*---------------------------------------------------------------------------*/ + +#define PE_MAKE_STATE_TRANSIT_SINGLE(reaction, next) \ + pd_make_pe_state_transit_single(\ + pd_port, pd_port->pe_state_curr, reaction, next) +/* PE_MAKE_STATE_TRANSIT_SINGLE */ + +#define PE_MAKE_STATE_TRANSIT_TO_HRESET(reaction) \ + PE_MAKE_STATE_TRANSIT_SINGLE(reaction, \ + pe_get_curr_hard_reset_state(pd_port)) +/* PE_MAKE_STATE_TRANSIT_TO_HRESET */ + +#define PE_MAKE_STATE_TRANSIT(state) \ + pd_make_pe_state_transit(\ + pd_port, pd_port->pe_state_curr, &state##_reactions) +/* PE_MAKE_STATE_TRANSIT */ + + +static inline bool pd_make_pe_state_transit_single(struct pd_port *pd_port, + uint8_t curr_state, uint8_t reaction_state, uint8_t next_state) +{ + if (curr_state == reaction_state) { + PE_TRANSIT_STATE(pd_port, next_state); + return true; + } + + return false; +} + +bool pd_make_pe_state_transit(struct pd_port *pd_port, uint8_t curr_state, + const struct pe_state_reaction *state_reaction); + +bool pd_process_event(struct pd_port *pd_port, struct pd_event *pd_event); + +extern bool pd_process_event_snk(struct pd_port *pd_port, struct pd_event *evt); +extern bool pd_process_event_src(struct pd_port *pd_port, struct pd_event *evt); +extern bool pd_process_event_drs(struct pd_port *pd_port, struct pd_event *evt); +extern bool pd_process_event_prs(struct pd_port *pd_port, struct pd_event *evt); +extern bool pd_process_event_vdm(struct pd_port *pd_port, struct pd_event *evt); +extern bool pd_process_event_vcs(struct pd_port *pd_port, struct pd_event *evt); +extern bool pd_process_event_com(struct pd_port *pd_port, struct pd_event *evt); +extern bool pd_process_event_tcp(struct pd_port *pd_port, struct pd_event *evt); + +#ifdef CONFIG_USB_PD_CUSTOM_DBGACC +extern bool pd_process_event_dbg(struct pd_port *pd_port, struct pd_event *evt); +#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */ + +#endif /* PD_PROCESS_EVT_H_ */ diff --git a/drivers/usb/typec/mmi_tcpc/inc/rt-regmap.h b/drivers/usb/typec/mmi_tcpc/inc/rt-regmap.h new file mode 100644 index 000000000000..abe06e34abfb --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/rt-regmap.h @@ -0,0 +1,218 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020 MediaTek Inc. + * Author: cy_huang + */ + +#ifndef LINUX_MISC_RT_REGMAP_H +#define LINUX_MISC_RT_REGMAP_H + +#include + +enum rt_access_mode { + RT_1BYTE_MODE = 1, + RT_2BYTE_MODE = 2, + RT_4BYTE_MODE = 4, + RT_DUMMY_MODE = -1, +}; + +/* start: the start address of group + * end: the end address of group + * mode: access mode (1,2,4 bytes) + */ +struct rt_access_group { + u32 start; + u32 end; + enum rt_access_mode mode; +}; + +/* rt_reg_type + * RT_NORMAL : Write without mask, Read from cache + * RT_WBITS : Write with mask, Read from cache + * RT_VOLATILE : Write to chip directly, Read from chip + * RT_RESERVE : Reserve registers (Write/Read as RT_NORMAL) + */ +#define RT_REG_TYPE_MASK (0x03) +#define RT_NORMAL (0x00) +#define RT_WBITS (0x01) +#define RT_VOLATILE (0x02) +#define RT_RESERVE (0x03) + +/* RT_WR_ONCE: if write data = cache data, data will not be written. + */ +#define RT_WR_ONCE (0x08) +#define RT_NORMAL_WR_ONCE (RT_NORMAL | RT_WR_ONCE) +#define RT_WBITS_WR_ONCE (RT_WBITS | RT_WR_ONCE) + +enum rt_data_format { + RT_LITTLE_ENDIAN, + RT_BIG_ENDIAN, +}; + +/* rt_regmap_mode + * 0 0 0 0 0 0 0 0 + * | | | | | | + * | | | |__| byte_mode + * | |__| || + * | || Cache_mode + * | Block_mode + * Debug_mode + */ + +#define RT_BYTE_MODE_MASK (0x01) +/* 1 byte for each register */ +#define RT_SINGLE_BYTE (0 << 0) +/* multi bytes for each regiseter */ +#define RT_MULTI_BYTE (1 << 0) + +#define RT_CACHE_MODE_MASK (0x06) +/* write to cache and chip synchronously */ +#define RT_CACHE_WR_THROUGH (0 << 1) +/* write to cache and chip asynchronously */ +#define RT_CACHE_WR_BACK (1 << 1) +/* disable cache */ +#define RT_CACHE_DISABLE (2 << 1) + +#define RT_IO_BLK_MODE_MASK (0x18) +/* pass through all write function */ +#define RT_IO_PASS_THROUGH (0 << 3) +/* block all write function */ +#define RT_IO_BLK_ALL (1 << 3) +/* block cache write function */ +#define RT_IO_BLK_CACHE (2 << 3) +/* block chip write function */ +#define RT_IO_BLK_CHIP (3 << 3) + +#define RT_DBG_MODE_MASK (0x20) +/* create general debugfs for register map */ +#define RT_DBG_GENERAL (0 << 5) +/* create node for each regisetr map by register address */ +#define RT_DBG_SPECIAL (1 << 5) + + +/* struct rt_register + * + * Ricktek register map structure for storing mapping data + * @addr: register address. + * @size: register size in byte. + * @reg_type: register R/W type (RT_NORMAL, RT_WBITS, RT_VOLATILE, RT_RESERVE). + * @wbit_mask: register writeable bits mask. + */ +struct rt_register { + u32 addr; + unsigned int size; + unsigned char reg_type; + unsigned char *wbit_mask; +}; + +/* Declare a rt_register by RT_REG_DECL + */ +#define RT_REG_DECL(_addr, _reg_length, _reg_type, _mask_...) \ + static unsigned char rt_writable_mask_##_addr[_reg_length] = _mask_; \ + static struct rt_register rt_register_##_addr = { \ + .addr = _addr, \ + .size = _reg_length, \ + .reg_type = _reg_type, \ + .wbit_mask = rt_writable_mask_##_addr, \ + } + +#define rt_register_map_t struct rt_register * + +#define RT_REG(_addr) (&rt_register_##_addr) + +/* rt_regmap_properties + * @name: the name of debug node. + * @aliases: alisis name of rt_regmap_device. + * @rm: rt_regiseter_map pointer array. + * @register_num: the number of rt_register_map registers. + * @group: register map access group. + * @rt_format: default is little endian. + * @rt_regmap_mode: rt_regmap_device mode. + * @io_log_en: enable/disable io log + */ +struct rt_regmap_properties { + const char *name; + const char *aliases; + const rt_register_map_t *rm; + int register_num; + struct rt_access_group *group; + const enum rt_data_format rt_format; + unsigned char rt_regmap_mode; + unsigned char io_log_en:1; +}; + +/* A passing struct for rt_regmap_reg_read and rt_regmap_reg_write function + * reg: regmap addr. + * mask: mask for update bits. + * rt_data: register value. + */ +struct rt_reg_data { + u32 reg; + u32 mask; + union { + u32 data_u32; + u16 data_u16; + u8 data_u8; + u8 data[4]; + } rt_data; +}; + +struct rt_regmap_device; + +/* basic chip read/write function */ +struct rt_regmap_fops { + int (*read_device)(void *client, u32 addr, int len, void *dst); + int (*write_device)(void *client, u32 addr, int len, const void *src); +}; + +/* with device address */ +extern struct rt_regmap_device* +rt_regmap_device_register_ex(struct rt_regmap_properties *props, + struct rt_regmap_fops *rops, + struct device *parent, + void *client, int dev_addr, void *drvdata); + +static inline struct rt_regmap_device* +rt_regmap_device_register(struct rt_regmap_properties *props, + struct rt_regmap_fops *rops, + struct device *parent, + struct i2c_client *client, void *drvdata) +{ + return rt_regmap_device_register_ex(props, rops, parent, + client, client->addr, drvdata); +} + +extern void rt_regmap_device_unregister(struct rt_regmap_device *rd); + +extern int rt_regmap_cache_reload(struct rt_regmap_device *rd); + +extern int rt_regmap_block_write(struct rt_regmap_device *rd, u32 reg, + int bytes, const void *src); +extern int rt_asyn_regmap_block_write(struct rt_regmap_device *rd, u32 reg, + int bytes, const void *src); +extern int rt_regmap_block_read(struct rt_regmap_device *rd, u32 reg, + int bytes, void *dst); +extern int rt_regmap_reg_read(struct rt_regmap_device *rd, + struct rt_reg_data *rrd, u32 reg); +extern int rt_regmap_reg_write(struct rt_regmap_device *rd, + struct rt_reg_data *rrd, u32 reg, + const u32 data); +extern int rt_asyn_regmap_reg_write(struct rt_regmap_device *rd, + struct rt_reg_data *rrd, u32 reg, + const u32 data); +extern int rt_regmap_update_bits(struct rt_regmap_device *rd, + struct rt_reg_data *rrd, u32 reg, + u32 mask, u32 data); +extern void rt_regmap_cache_sync(struct rt_regmap_device *rd); +extern void rt_regmap_cache_write_back(struct rt_regmap_device *rd, u32 reg); +extern int rt_is_reg_volatile(struct rt_regmap_device *rd, u32 reg); +extern int rt_get_regsize(struct rt_regmap_device *rd, u32 reg); +extern void rt_cache_getlasterror(struct rt_regmap_device *rd, char *buf, + size_t size); +extern void rt_cache_clrlasterror(struct rt_regmap_device *rd); +extern int rt_regmap_add_debugfs(struct rt_regmap_device *rd, const char *name, + umode_t mode, void *data, + const struct file_operations *fops); +extern int regmap_plat_init(void); +extern void regmap_plat_exit(void); +#endif /* LINUX_MISC_RT_REGMAP_H */ diff --git a/drivers/usb/typec/mmi_tcpc/inc/rt1711h.h b/drivers/usb/typec/mmi_tcpc/inc/rt1711h.h new file mode 100644 index 000000000000..c710ef19c96a --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/rt1711h.h @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#ifndef __LINUX_RT1711H_H +#define __LINUX_RT1711H_H + +#include "std_tcpci_v10.h" +#include "pd_dbg_info.h" + +/*show debug message or not */ +#define ENABLE_RT1711_DBG 0 + +/* RT1711H Private RegMap */ + +#define RT1711H_REG_CONFIG_GPIO0 (0x71) + +#define RT1711H_REG_PHY_CTRL1 (0x80) + +#define RT1711H_REG_CLK_CTRL2 (0x87) +#define RT1711H_REG_CLK_CTRL3 (0x88) + +#define RT1711H_REG_PRL_FSM_RESET (0x8D) + +#define RT1711H_REG_BMC_CTRL (0x90) +#define RT1711H_REG_BMCIO_RXDZSEL (0x93) +#define RT1711H_REG_VCONN_CLIMITEN (0x95) + +#define RT1711H_REG_RT_STATUS (0x97) +#define RT1711H_REG_RT_INT (0x98) +#define RT1711H_REG_RT_MASK (0x99) + +#define RT1711H_REG_IDLE_CTRL (0x9B) +#define RT1711H_REG_INTRST_CTRL (0x9C) +#define RT1711H_REG_WATCHDOG_CTRL (0x9D) +#define RT1711H_REG_I2CRST_CTRL (0X9E) + +#define RT1711H_REG_SWRESET (0xA0) +#define RT1711H_REG_TTCPC_FILTER (0xA1) +#define RT1711H_REG_DRP_TOGGLE_CYCLE (0xA2) +#define RT1711H_REG_DRP_DUTY_CTRL (0xA3) +#define RT1711H_REG_BMCIO_RXDZEN (0xAF) + +#define RT1711H_REG_UNLOCK_PW_2 (0xF0) +#define RT1711H_REG_UNLOCK_PW_1 (0xF1) +#define RT1711H_REG_EFUSE5 (0xF6) + +/* + * Device ID + */ + +#define RT1711H_DID_A 0x2170 +#define RT1711H_DID_B 0x2171 +#define RT1711H_DID_C 0x2172 + +#define RT1715_DID_D 0x2173 + +/* + * RT1711H_REG_PHY_CTRL1 (0x80) + */ + +#define RT1711H_REG_PHY_CTRL1_SET( \ + retry_discard, toggle_cnt, bus_idle_cnt, rx_filter) \ + ((retry_discard << 7) | (toggle_cnt << 4) | \ + (bus_idle_cnt << 2) | (rx_filter & 0x03)) + +/* + * RT1711H_REG_CLK_CTRL2 (0x87) + */ + +#define RT1711H_REG_CLK_DIV_600K_EN (1<<7) +#define RT1711H_REG_CLK_BCLK2_EN (1<<6) +#define RT1711H_REG_CLK_BCLK2_TG_EN (1<<5) +#define RT1711H_REG_CLK_DIV_300K_EN (1<<3) +#define RT1711H_REG_CLK_CK_300K_EN (1<<2) +#define RT1711H_REG_CLK_BCLK_EN (1<<1) +#define RT1711H_REG_CLK_BCLK_TH_EN (1<<0) + +/* + * RT1711H_REG_CLK_CTRL3 (0x88) + */ + +#define RT1711H_REG_CLK_OSCMUX_RG_EN (1<<7) +#define RT1711H_REG_CLK_CK_24M_EN (1<<6) +#define RT1711H_REG_CLK_OSC_RG_EN (1<<5) +#define RT1711H_REG_CLK_DIV_2P4M_EN (1<<4) +#define RT1711H_REG_CLK_CK_2P4M_EN (1<<3) +#define RT1711H_REG_CLK_PCLK_EN (1<<2) +#define RT1711H_REG_CLK_PCLK_RG_EN (1<<1) +#define RT1711H_REG_CLK_PCLK_TG_EN (1<<0) + +/* + * RT1711H_REG_BMC_CTRL (0x90) + */ + +#define RT1711H_REG_IDLE_EN (1<<6) +#define RT1711H_REG_DISCHARGE_EN (1<<5) +#define RT1711H_REG_BMCIO_LPRPRD (1<<4) +#define RT1711H_REG_BMCIO_LPEN (1<<3) +#define RT1711H_REG_BMCIO_BG_EN (1<<2) +#define RT1711H_REG_VBUS_DET_EN (1<<1) +#define RT1711H_REG_BMCIO_OSC_EN (1<<0) + +/* + * RT1711H_REG_RT_STATUS (0x97) + */ + +#define RT1711H_REG_RA_DETACH (1<<5) +#define RT1711H_REG_VBUS_80 (1<<1) + +/* + * RT1711H_REG_RT_INT (0x98) + */ + +#define RT1711H_REG_INT_RA_DETACH (1<<5) +#define RT1711H_REG_INT_WATCHDOG (1<<2) +#define RT1711H_REG_INT_VBUS_80 (1<<1) +#define RT1711H_REG_INT_WAKEUP (1<<0) + +/* + * RT1711H_REG_RT_MASK (0x99) + */ + +#define RT1711H_REG_M_RA_DETACH (1<<5) +#define RT1711H_REG_M_WATCHDOG (1<<2) +#define RT1711H_REG_M_VBUS_80 (1<<1) +#define RT1711H_REG_M_WAKEUP (1<<0) + +/* + * RT1711H_REG_IDLE_CTRL (0x9B) + */ + +#define RT1711H_REG_CK_300K_SEL (1<<7) +#define RT1711H_REG_SHIPPING_OFF (1<<5) +#define RT1711H_REG_ENEXTMSG (1<<4) +#define RT1711H_REG_AUTOIDLE_EN (1<<3) + +/* + * RT1711H_REG_EFUSE5 (0xF6) + */ + +#define RT1711H_REG_M_VBUS_CAL GENMASK(7, 5) +#define RT1711H_REG_S_VBUS_CAL 5 +#define RT1711H_REG_MIN_VBUS_CAL -4 + +/* timeout = (tout*2+1) * 6.4ms */ + +#ifdef CONFIG_USB_PD_REV30 +#define RT1711H_REG_IDLE_SET(ck300, ship_dis, auto_idle, tout) \ + ((ck300 << 7) | (ship_dis << 5) | (auto_idle << 3) \ + | (tout & 0x07) | RT1711H_REG_ENEXTMSG) +#else +#define RT1711H_REG_IDLE_SET(ck300, ship_dis, auto_idle, tout) \ + ((ck300 << 7) | (ship_dis << 5) | (auto_idle << 3) | (tout & 0x07)) +#endif + +/* + * RT1711H_REG_INTRST_CTRL (0x9C) + */ + +#define RT1711H_REG_INTRST_EN (1<<7) + +/* timeout = (tout+1) * 0.2sec */ +#define RT1711H_REG_INTRST_SET(en, tout) \ + ((en << 7) | (tout & 0x03)) + +/* + * RT1711H_REG_WATCHDOG_CTRL (0x9D) + */ + +#define RT1711H_REG_WATCHDOG_EN (1<<7) + +/* timeout = (tout+1) * 0.4sec */ +#define RT1711H_REG_WATCHDOG_CTRL_SET(en, tout) \ + ((en << 7) | (tout & 0x07)) + +/* + * RT1711H_REG_I2CRST_CTRL (0x9E) + */ + +#define RT1711H_REG_I2CRST_EN (1<<7) + +/* timeout = (tout+1) * 12.5ms */ +#define RT1711H_REG_I2CRST_SET(en, tout) \ + ((en << 7) | (tout & 0x0f)) + +#if ENABLE_RT1711_DBG +#define RT1711_INFO(format, args...) \ + pd_dbg_info("%s() line-%d: " format,\ + __func__, __LINE__, ##args) +#else +#define RT1711_INFO(foramt, args...) +#endif + +#endif /* #ifndef __LINUX_RT1711H_H */ diff --git a/drivers/usb/typec/mmi_tcpc/inc/sgm7220.h b/drivers/usb/typec/mmi_tcpc/inc/sgm7220.h new file mode 100644 index 000000000000..3a77003871e9 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/sgm7220.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2021 MediaTek Inc. + * + * 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. + */ + +#ifndef __LINUX_SGM7220_H +#define __LINUX_SGM7220_H + +/****************************************************************************** +* Register addresses +******************************************************************************/ +#define REG_BASE 0x00 +/* 0x00 - 0x07 reserved for ID */ +#define REG_MOD 0x08 +#define REG_INT 0x09 +#define REG_SET 0x0A +#define REG_CTL 0X45 +#define REG_REVISION 0XA0 + +/****************************************************************************** +* Register bits +******************************************************************************/ +/* REG_ID (0x00) */ +#define ID_REG_LEN 0X08 + +/* REG_MOD (0x08) */ +#define MOD_ACTIVE_CABLE_DETECTION 0X01 /*RU*/ +#define MOD_ACCESSORY_CONNECTED_SHIFT 1 +#define MOD_ACCESSORY_CONNECTED (0x07 << MOD_ACCESSORY_CONNECTED_SHIFT) /*RU*/ +#define MOD_CURRENT_MODE_DETECT_SHIFT 4 +#define MOD_CURRENT_MODE_DETECT (0x03 << MOD_CURRENT_MODE_DETECT_SHIFT) /*RU*/ +#define MOD_CURRENT_MODE_ADVERTISE_SHIFT 6 +#define MOD_CURRENT_MODE_ADVERTISE (0x03 << MOD_CURRENT_MODE_ADVERTISE_SHIFT) /*RW*/ + +/* REG_INT (0x09) */ +#define INT_DRP_DUTY_CYCLE_SHIFT 1 +#define INT_DRP_DUTY_CYCLE (0x03 << INT_DRP_DUTY_CYCLE_SHIFT) /*RW*/ +#define INT_INTERRUPT_STATUS_SHIFT 4 +#define INT_INTERRUPT_STATUS (0x01 << INT_INTERRUPT_STATUS_SHIFT) /*RCU*/ +#define INT_CABLE_DIR_SHIFT 5 +#define INT_CABLE_DIR (0x01 << INT_CABLE_DIR_SHIFT) /*RU*/ +#define INT_ATTACHED_STATE_SHIFT 6 +#define INT_ATTACHED_STATE (0x03 << INT_ATTACHED_STATE_SHIFT) /*RU*/ + +/* REG_SET (0x0A) */ +#define SET_I2C_DISABLE_TERM 0x01 /*RW*/ +#define SET_I2C_SOURCE_PREF_SHIFT 1 +#define SET_I2C_SOURCE_PREF (0x03 << SET_I2C_SOURCE_PREF_SHIFT) /*RW*/ +#define SET_I2C_SOFT_RESET_SHIFT 3 +#define SET_I2C_SOFT_RESET (0x01 << SET_I2C_SOFT_RESET_SHIFT) /*RSU*/ +#define SET_MODE_SELECT_SHIFT 4 +#define SET_MODE_SELECT (0x03 << SET_MODE_SELECT_SHIFT) /*RW*/ +#define SET_DEBOUNCE_SHIFT 6 +#define SET_DEBOUNCE (0x03 << SET_DEBOUNCE_SHIFT) /*RW*/ + +/* REG_CTR (0x45) */ +#define CTR_DISABLE_RD_RP_SHIFT 2 +#define CTR_DISABLE_RD_RP (0x01 << CTR_DISABLE_RD_RP_SHIFT) /*RW*/ + +/* SET_MODE_SELECT */ +#define SET_MODE_SELECT_DEFAULT 0x00 +#define SET_MODE_SELECT_SNK 0x01 +#define SET_MODE_SELECT_SRC 0x02 +#define SET_MODE_SELECT_DRP 0x03 +#endif /* #ifndef __LINUX_SGM7220_H */ diff --git a/drivers/usb/typec/mmi_tcpc/inc/std_tcpci_v10.h b/drivers/usb/typec/mmi_tcpc/inc/std_tcpci_v10.h new file mode 100644 index 000000000000..f56b56f9c381 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/std_tcpci_v10.h @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#ifndef STD_TCPCI_V10_H_ +#define STD_TCPCI_V10_H_ + +/* Standard TCPC V10 RegMap */ + +#define TCPC_V10_REG_VID (0x00) +#define TCPC_V10_REG_PID (0x02) +#define TCPC_V10_REG_DID (0x04) +#define TCPC_V10_REG_TYPEC_REV (0x06) +#define TCPC_V10_REG_PD_REV (0x08) +#define TCPC_V10_REG_PDIF_REV (0x0A) + +#define TCPC_V10_REG_ALERT (0x10) +#define TCPC_V10_REG_ALERT_MASK (0x12) +#define TCPC_V10_REG_POWER_STATUS_MASK (0x14) +#define TCPC_V10_REG_FAULT_STATUS_MASK (0x15) +#define TCPC_V10_REG_EXT_STATUS_MASK (0x16) + +#define TCPC_V10_REG_TCPC_CTRL (0x19) +#define TCPC_V10_REG_ROLE_CTRL (0x1A) +#define TCPC_V10_REG_FAULT_CTRL (0x1B) +#define TCPC_V10_REG_POWER_CTRL (0x1C) + +#define TCPC_V10_REG_CC_STATUS (0x1D) +#define TCPC_V10_REG_POWER_STATUS (0x1E) +#define TCPC_V10_REG_FAULT_STATUS (0x1F) +#define TCPC_V10_REG_EXT_STATUS (0x20) + +#define TCPC_V10_REG_COMMAND (0x23) + +#define TCPC_V10_REG_MSG_HDR_INFO (0x2e) + +#define TCPC_V10_REG_RX_DETECT (0x2f) + +#define TCPC_V10_REG_RX_BYTE_CNT (0x30) +#define TCPC_V10_REG_RX_BUF_FRAME_TYPE (0x31) +#define TCPC_V10_REG_RX_HDR (0x32) +#define TCPC_V10_REG_RX_DATA (0x34) + +#define TCPC_V10_REG_TRANSMIT (0x50) +#define TCPC_V10_REG_TX_BYTE_CNT (0x51) +#define TCPC_V10_REG_TX_HDR (0x52) +#define TCPC_V10_REG_TX_DATA (0x54)/* through 0x6f */ + +#define TCPC_V10_REG_VBUS_SINK_DISCONNECT_THD (0x72) +#define TCPC_V10_REG_VBUS_STOP_DISCHARGE_THD (0x74) +#define TCPC_V10_REG_VBUS_VOLTAGE_ALARM_HI (0x76) +#define TCPC_V10_REG_VBUS_VOLTAGE_ALARM_LO (0x78) + +/* + * TCPC_V10_REG_ALERT (0x10) + * TCPC_V10_REG_ALERT_MASK (0x12) + */ +#define TCPC_V10_REG_ALERT_VENDOR_DEFINED (1<<15) +#define TCPC_V10_REG_VBUS_SINK_DISCONNECT (1<<11) +#define TCPC_V10_REG_RX_OVERFLOW (1<<10) +#define TCPC_V10_REG_ALERT_FAULT (1<<9) +#define TCPC_V10_REG_ALERT_LO_VOLT (1<<8) +#define TCPC_V10_REG_ALERT_HI_VOLT (1<<7) +#define TCPC_V10_REG_ALERT_TX_SUCCESS (1<<6) +#define TCPC_V10_REG_ALERT_TX_DISCARDED (1<<5) +#define TCPC_V10_REG_ALERT_TX_FAILED (1<<4) +#define TCPC_V10_REG_ALERT_RX_HARD_RST (1<<3) +#define TCPC_V10_REG_ALERT_RX_STATUS (1<<2) +#define TCPC_V10_REG_ALERT_POWER_STATUS (1<<1) +#define TCPC_V10_REG_ALERT_CC_STATUS (1<<0) + +/* + * TCPC_V10_REG_POWER_STATUS_MASK (0x14) + * TCPC_V10_REG_POWER_STATUS (0x1E) + */ + +#define TCPC_V10_REG_POWER_STATUS_TCPC_INITIAL (1<<6) +#define TCPC_V10_REG_POWER_STATUS_SRC_HV (1<<5) +#define TCPC_V10_REG_POWER_STATUS_SRC_VBUS (1<<4) +#define TCPC_V10_REG_POWER_STATUS_VBUS_PRES_DET (1<<3) +#define TCPC_V10_REG_POWER_STATUS_VBUS_PRES (1<<2) +#define TCPC_V10_REG_POWER_STATUS_VCONN_PRES (1<<1) +#define TCPC_V10_REG_POWER_STATUS_SINK_VBUS (1<<0) + +/* + * TCPC_V10_REG_FAULT_STATUS_MASK (0x15) + * TCPC_V10_REG_FAULT_STATUS (0x1F) + */ + +/* VCONN_OV should not be here */ +#define TCPC_V10_REG_FAULT_STATUS_VCONN_OV (1<<7) +#define TCPC_V10_REG_FAULT_STATUS_FORCE_OFF_VBUS (1<<6) +#define TCPC_V10_REG_FAULT_STATUS_AUTO_DISC_FAIL (1<<5) +#define TCPC_V10_REG_FAULT_STATUS_FORCE_DISC_FAIL (1<<4) +#define TCPC_V10_REG_FAULT_STATUS_VBUS_OC (1<<3) +#define TCPC_V10_REG_FAULT_STATUS_VBUS_OV (1<<2) +#define TCPC_V10_REG_FAULT_STATUS_VCONN_OC (1<<1) +#define TCPC_V10_REG_FAULT_STATUS_I2C_ERROR (1<<0) + +/* + * TCPC_V10_REG_ROLE_CTRL (0x1A) + */ + +#define TCPC_V10_REG_ROLE_CTRL_DRP (1<<6) + +#define TCPC_V10_REG_ROLE_CTRL_RES_SET(drp, rp, cc1, cc2) \ + ((drp) << 6 | (rp) << 4 | (cc2) << 2 | (cc1)) + +#define CC_RD 0x02 +#define CC_RP 0x01 +#define CC_OPEN 0x03 +#define CC_RA 0x00 + +/* + * TCPC_V10_REG_TCPC_CTRL (0x19) + */ + +#define TCPC_V10_REG_TCPC_CTRL_EN_LOOK4CONNECTION_ALERT (1<<6) +#define TCPC_V10_REG_TCPC_CTRL_EN_WDT (1<<5) +#define TCPC_V10_REG_TCPC_CTRL_BIST_TEST_MODE (1<<1) +#define TCPC_V10_REG_TCPC_CTRL_PLUG_ORIENT (1<<0) + +/* + * TCPC_V10_REG_FAULT_CTRL (0x1B) + */ + +#define TCPC_V10_REG_FAULT_CTRL_DIS_VCONN_OV (1<<7) +#define TCPC_V10_REG_FAULT_CTRL_DIS_SNK_VBUS_OC (1<<2) +#define TCPC_V10_REG_FAULT_CTRL_DIS_VCONN_OC (1<<0) + +/* + * TCPC_V10_REG_POWER_CTRL (0x1C) + */ + +#define TCPC_V10_REG_VBUS_MONITOR (1<<6) +#define TCPC_V10_REG_BLEED_DISC_EN (1<<3) +#define TCPC_V10_REG_FORCE_DISC_EN (1<<2) +#define TCPC_V10_REG_POWER_CTRL_VCONN (1<<0) + +/* + * TCPC_V10_REG_CC_STATUS (0x1D) + */ + +#define TCPC_V10_REG_CC_STATUS_DRP_TOGGLING (1<<5) +#define TCPC_V10_REG_CC_STATUS_DRP_RESULT(reg) (((reg) & 0x10) >> 4) +#define TCPC_V10_REG_CC_STATUS_CC2(reg) (((reg) & 0xc) >> 2) +#define TCPC_V10_REG_CC_STATUS_CC1(reg) ((reg) & 0x3) + +/* + * TCPC_V10_REG_COMMAND (0x23) + */ + +enum tcpm_v10_command { + TCPM_CMD_WAKE_I2C = 0x11, + TCPM_CMD_DISABLE_VBUS_DETECT = 0x22, + TCPM_CMD_ENABLE_VBUS_DETECT = 0x33, + TCPM_CMD_DISABLE_SINK_VBUS = 0x44, + TCPM_CMD_ENABLE_SINK_VBUS = 0x55, + TCPM_CMD_DISABLE_SOURCE_VBUS = 0x66, + TCPM_CMD_ENABLE_SOURCE_VBUS = 0x77, + TCPM_CMD_SOURCE_VBUS_HV = 0x88, + TCPM_CMD_LOOK_CONNECTION = 0x99, + TCPM_CMD_RX_ONE_MODE = 0xAA, + TCPM_CMD_I2C_IDLE = 0xFF, +}; + +/* + * TCPC_V10_REG_MSG_HDR_INFO (0x2e) + * According to PD30_Rev11 ECR, + * The sender of a GoodCRC Message should set : + * the Specification Revsiion field to 10b. + */ + +#define TCPC_V10_REG_MSG_HDR_INFO_SET(drole, prole) \ + ((drole) << 3 | ((PD_REV20) << 1) | (prole)) +#define TCPC_V10_REG_MSG_HDR_INFO_DROLE(reg) (((reg) & 0x8) >> 3) +#define TCPC_V10_REG_MSG_HDR_INFO_PROLE(reg) ((reg) & 0x1) + + +/* + * TCPC_V10_REG_TRANSMIT (0x50) + */ + +#ifdef CONFIG_USB_PD_REV30 +#define TCPC_V10_REG_TRANSMIT_SET(retry, type) \ + ((retry) << 4 | (type)) +#else +#define TCPC_V10_REG_TRANSMIT_SET(retry, type) \ + (PD_RETRY_COUNT << 4 | (type)) +#endif /* CONFIG_USB_PD_REV30 */ + +#endif /* STD_TCPCI_V10_H_ */ diff --git a/drivers/usb/typec/mmi_tcpc/inc/tcpc_aw35616.h b/drivers/usb/typec/mmi_tcpc/inc/tcpc_aw35616.h new file mode 100644 index 000000000000..ac41c31e3952 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/tcpc_aw35616.h @@ -0,0 +1,261 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/******************************************************************************* + **** Copyright (C), 2022, Shanghai awinic technology Co.,Ltd. + all rights reserved. *********** + ******************************************************************************* + * File Name : tcpc_aw35616.h + * Author : awinic + * Date : 2022-12-16 + * Description : .H file function description + * Version : 1.0 + * Function List : + ******************************************************************************/ +#ifndef __LINUX_AW35616_H +#define __LINUX_AW35616_H + +/* AW35616 I2C Configuration */ +#define AW35616_I2C_RETRIES (5) +#define AW35616_CHECK_RETEY (5) + +/* aw91xxx register read/write access */ +#define REG_NONE_ACCESS (0) +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) +#define AW35616_REG_MAX (0x23) + +/* AW35616 Register Addresses */ +#define AW35616_REG_DEV_ID (0x01) +#define AW35616_REG_CTR (0x02) +#define AW35616_REG_INT (0x03) +#define AW35616_REG_STATUS (0x04) +#define AW35616_REG_STATUS1 (0x05) +#define AW35616_REG_RSTN (0x06) +#define AW35616_REG_USB_VID0 (0x07) +#define AW35616_REG_USB_VID1 (0x08) +#define AW35616_REG_TYPEC_STATUS (0x20) +#define AW35616_REG_CTR2 (0x22) + +/* AW35616 vendor id */ +#define AW35616_VENDOR_ID (0x06) +#define AW35616_reg_val1 (0xf9) +#define AW35616_reg_val2 (0xc2) +#define AW35616_reg_val3 (0x87) +#define AW35616_reg_val4 (0x80) +#define AW35616_reg_val5 (0x00) + +/** + * power role mode control + */ +typedef enum { + SNK = 0, + SRC = 1, + DRP = 2, + WAKE_MODE = 3 +} WKMD; +#define PR_MODE_SET (WAKE_MODE) + +/** + * source current mode set + */ +typedef enum { + SRC_DEF = 0, + SRC_1_5A = 1, + SRC_3_0A = 2, + OR_SRC_DEF = 3 +} SRC_CUR_MD; +#define SRC_CUR_SET (SRC_DEF) + +/** + * try src/snk mode set + */ +typedef enum { + NO_TRY = 0, + TRY_SNK = 1, + TRY_SRC = 2, + OR_NO_TRY = 3 +} TRY_MD; +#define TRY_MODE_SET (TRY_SNK) + +/** + * accessory mode set + */ +#define ACC_MODE_SET (1) + +/** + * Disable pull-up and pull-down after a toggle cycle + */ +typedef enum { + TOGGLE_CYCCLE_DEF = 0, + TOGGLE_CYCCLE_40ms = 1, + TOGGLE_CYCCLE_80ms = 2, + TOGGLE_CYCCLE_160ms = 3 +} TOG_SAVE_MD; +#define TOGGLE_CYCCLE_SET (TOGGLE_CYCCLE_DEF) + +/** + * intb flag + */ +typedef enum { + NO_INTB = 0, + ATTACHED = 1, + DETACHED = 2 +} INTB_FLAG; + +/** + * plag direction + */ +typedef enum { + STANDBY = 0, + CC1 = 1, + CC2 = 2, + CC1_CC2 = 3 +} PLAG_ORI; + +/** + * plag status + */ +typedef enum { + SINK = 1, + SOURCE = 2, + AUD_ACC = 3, + DUG_ACC = 4 +} PLAG_ST; + +/** + * charging current detection as sink + */ +typedef enum { + SNK_DEF = 1, + SNK_1_5A = 2, + SNK_3_0A = 3 +} SINK_CUR; + +/** + * vbus detection as sink + */ +typedef enum { + VBUS_DIS = 0, + VBUS_OK = 1 +} VBUS_ST; + +typedef union { + u8 byte; + struct { + u8 vd_id:3; + u8 ver_id:5; + }; +} reg_dev_id; + +typedef union { + u8 byte; + struct { + u8 intdis:1; + u8 wkmd:2; + u8 src_cur_md:2; + u8 try_md:2; + u8 accdis:1; + }; +} reg_ctr; + +typedef union { + u8 byte; + struct { + u8 intb_flag:2; + u8 wake_flag:1; + u8 reserved:5; + }; +} reg_ints; + +typedef union { + u8 byte[2]; + struct { + /* status */ + u8 plug_ori:2; + u8 plug_st:3; + u8 snk_cur_md:2; + u8 vbusok:1; + /* status1 */ + u8 active_cable:1; + u8 wake_st:1; + u8 snk_det_rp_dbg:1; + u8 reserved:5; + }; +} reg_status; + +typedef union { + u8 byte; + struct { + u8 sft_rstn:1; + u8 typec_rstn:1; + u8 reserved:6; + }; +} reg_rstn; + +typedef union { + u8 byte[2]; + struct { + /* vid0 */ + u8 usb_vid_lsb:8; + /* vid1 */ + u8 usb_vid_msb:8; + }; +} reg_vid; + +typedef union { + u8 byte; + struct { + u8 reserved:1; + u8 tog_save_md:2; + u8 reserved1:5; + }; +} reg_ctr2; + +typedef struct { + reg_dev_id dev_id; + reg_ctr ctr; + reg_ints ints; + reg_status status; + reg_rstn rstn; + reg_vid vid; + reg_ctr2 ctr2; +} aw35616_reg; + +struct aw35616_chip { + struct i2c_client *client; + struct device *dev; + struct semaphore suspend_lock; + struct tcpc_desc *tcpc_desc; + struct tcpc_device *tcpc; + struct kthread_worker irq_worker; + struct kthread_work irq_work; + struct task_struct *irq_worker_task; + struct wakeup_source *for_irq_wake_lock; + struct wakeup_source *for_i2c_wake_lock; + + struct delayed_work first_check_typec_work; + + aw35616_reg reg; + uint8_t toggle_cycle; + bool acc_support; + + int irq_gpio; + int irq; +}; + +enum aw35616_mode { + REVERSE_CHG_DRP, + REVERSE_CHG_SINK, + REVERSE_CHG_SOURCE, +}; + +#define AWINIC_DEBUG +#ifdef AWINIC_DEBUG +#define AWINIC_LOG_NAME "AW35616" +#define AW_LOG(format, arg...) pr_err("[%s] %s %d: " format, AWINIC_LOG_NAME, \ + __func__, __LINE__, ##arg) +#else +#define AW_LOG(format, arg...) +#endif + +#endif + diff --git a/drivers/usb/typec/mmi_tcpc/inc/tcpci.h b/drivers/usb/typec/mmi_tcpc/inc/tcpci.h new file mode 100644 index 000000000000..e9088ab1d0b8 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/tcpci.h @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#ifndef __LINUX_RT_TCPC_H +#define __LINUX_RT_TCPC_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_DUAL_ROLE_USB_INTF +#include +#endif /* CONFIG_DUAL_ROLE_USB_INTF */ + +#include "tcpci_core.h" + +#ifdef CONFIG_PD_DBG_INFO +#include "pd_dbg_info.h" +#endif /* CONFIG_PD_DBG_INFO */ + +#ifdef CONFIG_USB_POWER_DELIVERY +#include "pd_core.h" +#endif /* CONFIG_USB_POWER_DELIVERY */ + +#define SC2150A_DID 0x0000 + +#define PE_STATE_FULL_NAME 0 + +#define TCPC_LOW_RP_DUTY (100) /* 10 % */ +#define TCPC_NORMAL_RP_DUTY (330) /* 33 % */ + +/* provide to TCPC interface */ +extern int tcpci_report_usb_port_changed(struct tcpc_device *tcpc); +extern int tcpci_report_power_control(struct tcpc_device *tcpc, bool en); +extern int tcpc_typec_init(struct tcpc_device *tcpc, uint8_t typec_role); +extern void tcpc_typec_deinit(struct tcpc_device *tcpc); +extern int tcpc_dual_role_phy_init(struct tcpc_device *tcpc); + +extern struct tcpc_device *tcpc_device_register( + struct device *parent, struct tcpc_desc *tcpc_desc, + struct tcpc_ops *ops, void *drv_data); +extern void tcpc_device_unregister( + struct device *dev, struct tcpc_device *tcpc); + +extern int tcpc_schedule_init_work(struct tcpc_device *tcpc); + +extern void *tcpc_get_dev_data(struct tcpc_device *tcpc); +extern void tcpci_lock_typec(struct tcpc_device *tcpc); +extern void tcpci_unlock_typec(struct tcpc_device *tcpc); +extern int tcpci_alert(struct tcpc_device *tcpc); + +extern void tcpci_vbus_level_init( + struct tcpc_device *tcpc, uint16_t power_status); + +static inline int tcpci_check_vbus_valid(struct tcpc_device *tcpc) +{ + return tcpc->vbus_level >= TCPC_VBUS_VALID; +} + +int tcpci_check_vbus_valid_from_ic(struct tcpc_device *tcpc); +int tcpci_check_vsafe0v(struct tcpc_device *tcpc, bool detect_en); +int tcpci_get_chip_id(struct tcpc_device *tcpc, uint32_t *chip_id); +int tcpci_alert_status_clear(struct tcpc_device *tcpc, uint32_t mask); +int tcpci_fault_status_clear(struct tcpc_device *tcpc, uint8_t status); +int tcpci_set_alert_mask(struct tcpc_device *tcpc, uint32_t mask); +int tcpci_get_alert_mask(struct tcpc_device *tcpc, uint32_t *mask); +int tcpci_get_alert_status(struct tcpc_device *tcpc, uint32_t *alert); +int tcpci_get_fault_status(struct tcpc_device *tcpc, uint8_t *fault); +int tcpci_get_power_status(struct tcpc_device *tcpc, uint16_t *pw_status); +int tcpci_init(struct tcpc_device *tcpc, bool sw_reset); +int tcpci_init_alert_mask(struct tcpc_device *tcpc); + +int tcpci_get_cc(struct tcpc_device *tcpc); +int tcpci_set_cc(struct tcpc_device *tcpc, int pull); +static inline int __tcpci_set_cc(struct tcpc_device *tcpc, int pull) +{ + PD_BUG_ON(tcpc->ops->set_cc == NULL); + + if (pull & TYPEC_CC_DRP) { + tcpc->typec_remote_cc[0] = + tcpc->typec_remote_cc[1] = + TYPEC_CC_DRP_TOGGLING; + } + + tcpc->typec_local_cc = pull; + return tcpc->ops->set_cc(tcpc, pull); +} +int tcpci_set_polarity(struct tcpc_device *tcpc, int polarity); +int tcpci_set_port_type(struct tcpc_device *tcpc, int mode); +int tcpci_set_low_rp_duty(struct tcpc_device *tcpc, bool low_rp); +int tcpci_set_vconn(struct tcpc_device *tcpc, int enable); + +int tcpci_is_low_power_mode(struct tcpc_device *tcpc); +int tcpci_set_low_power_mode(struct tcpc_device *tcpc, bool en, int pull); +int tcpci_idle_poll_ctrl(struct tcpc_device *tcpc, bool en, bool lock); +int tcpci_set_watchdog(struct tcpc_device *tcpc, bool en); +int tcpci_alert_vendor_defined_handler(struct tcpc_device *tcpc); +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC +int tcpci_is_vsafe0v(struct tcpc_device *tcpc); +#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */ + +#ifdef CONFIG_WATER_DETECTION +int tcpci_is_water_detected(struct tcpc_device *tcpc); +int tcpci_set_water_protection(struct tcpc_device *tcpc, bool en); +int tcpci_set_usbid_polling(struct tcpc_device *tcpc, bool en); +int tcpci_notify_wd_status(struct tcpc_device *tcpc, bool water_detected); +#endif /* CONFIG_WATER_DETECTION */ + +#ifdef CONFIG_CABLE_TYPE_DETECTION +int tcpci_notify_cable_type(struct tcpc_device *tcpc); +#endif /* CONFIG_CABLE_TYPE_DETECTION */ + +#ifdef CONFIG_USB_POWER_DELIVERY + +int tcpci_set_msg_header(struct tcpc_device *tcpc, + uint8_t power_role, uint8_t data_role); + +int tcpci_set_rx_enable(struct tcpc_device *tcpc, uint8_t enable); + +int tcpci_protocol_reset(struct tcpc_device *tcpc); + +int tcpci_get_message(struct tcpc_device *tcpc, + uint32_t *payload, uint16_t *head, enum tcpm_transmit_type *type); + +int tcpci_transmit(struct tcpc_device *tcpc, + enum tcpm_transmit_type type, uint16_t header, const uint32_t *data); + +int tcpci_set_bist_test_mode(struct tcpc_device *tcpc, bool en); + +int tcpci_set_bist_carrier_mode(struct tcpc_device *tcpc, uint8_t pattern); + +#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD +int tcpci_retransmit(struct tcpc_device *tcpc); +#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */ +#endif /* CONFIG_USB_POWER_DELIVERY */ + +int tcpci_notify_typec_state(struct tcpc_device *tcpc); + +int tcpci_notify_role_swap( + struct tcpc_device *tcpc, uint8_t event, uint8_t role); +int tcpci_notify_pd_state(struct tcpc_device *tcpc, uint8_t connect); +int tcpci_notify_pd_vdm_verify(struct tcpc_device *tcpc, uint8_t vdm_verify); + +int tcpci_set_intrst(struct tcpc_device *tcpc, bool en); +int tcpci_enable_watchdog(struct tcpc_device *tcpc, bool en); + +int tcpci_source_vbus(struct tcpc_device *tcpc, uint8_t type, int mv, int ma); +int tcpci_sink_vbus(struct tcpc_device *tcpc, uint8_t type, int mv, int ma); +int tcpci_disable_vbus_control(struct tcpc_device *tcpc); +int tcpci_notify_attachwait_state(struct tcpc_device *tcpc, bool as_sink); +int tcpci_enable_auto_discharge(struct tcpc_device *tcpc, bool en); +int tcpci_enable_force_discharge(struct tcpc_device *tcpc, bool en, int mv); + +#ifdef CONFIG_USB_POWER_DELIVERY + +int tcpci_notify_hard_reset_state(struct tcpc_device *tcpc, uint8_t state); + +int tcpci_enter_mode(struct tcpc_device *tcpc, + uint16_t svid, uint8_t ops, uint32_t mode); +int tcpci_exit_mode(struct tcpc_device *tcpc, uint16_t svid); + +#ifdef CONFIG_USB_PD_ALT_MODE +int tcpci_report_hpd_state(struct tcpc_device *tcpc, uint32_t dp_status); +int tcpci_dp_status_update(struct tcpc_device *tcpc, uint32_t dp_status); +int tcpci_dp_configure(struct tcpc_device *tcpc, uint32_t dp_config); +int tcpci_dp_attention(struct tcpc_device *tcpc, uint32_t dp_status); + +int tcpci_dp_notify_status_update_done( + struct tcpc_device *tcpc, uint32_t dp_status, bool ack); + +int tcpci_dp_notify_config_start(struct tcpc_device *tcpc); +int tcpci_dp_notify_config_done(struct tcpc_device *tcpc, + uint32_t local_cfg, uint32_t remote_cfg, bool ack); +#endif /* CONFIG_USB_PD_ALT_MODE */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM +int tcpci_notify_uvdm(struct tcpc_device *tcpc, bool ack); +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + +#ifdef CONFIG_USB_PD_ALT_MODE_RTDC +int tcpci_dc_notify_en_unlock(struct tcpc_device *tcpc); +#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */ + +#ifdef CONFIG_USB_PD_REV30 + +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE +int tcpci_notify_alert(struct tcpc_device *tcpc, uint32_t ado); +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE +int tcpci_notify_status(struct tcpc_device *tcpc, struct pd_status *sdb); +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_BAT_INFO +int tcpci_notify_request_bat_info( + struct tcpc_device *tcpc, enum pd_battery_reference ref); +#endif /* CONFIG_USB_PD_REV30_BAT_INFO */ +#endif /* CONFIG_USB_PD_REV30 */ + +#endif /* CONFIG_USB_POWER_DELIVERY */ + +#endif /* #ifndef __LINUX_RT_TCPC_H */ diff --git a/drivers/usb/typec/mmi_tcpc/inc/tcpci_config.h b/drivers/usb/typec/mmi_tcpc/inc/tcpci_config.h new file mode 100644 index 000000000000..2b48bbb020eb --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/tcpci_config.h @@ -0,0 +1,444 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#ifndef __LINUX_TCPC_CONFIG_H +#define __LINUX_TCPC_CONFIG_H + +#if IS_ENABLED(CONFIG_TCPC_CLASS) +/* default config */ + +/* #define CONFIG_TYPEC_USE_DIS_VBUS_CTRL */ +#define CONFIG_TYPEC_POWER_CTRL_INIT + +#define CONFIG_TYPEC_CAP_TRY_SOURCE +#define CONFIG_TYPEC_CAP_TRY_SINK + +#define CONFIG_TYPEC_CAP_DBGACC +#define CONFIG_TYPEC_CAP_DBGACC_SNK +#define CONFIG_TYPEC_CAP_CUSTOM_SRC +#define CONFIG_TYPEC_CAP_NORP_SRC +/* #define CONFIG_COMPATIBLE_APPLE_TA */ + +#define CONFIG_SUPPORT_MMI_ADAPTER +#ifdef CONFIG_SUPPORT_MMI_ADAPTER +//#define CONFIG_SUPPORT_MMI_ADAPTER_GET_TEMP +#endif /* CONFIG_SUPPORT_MMI_ADAPTER */ + +#if 0 /* CONFIG_MTK_GAUGE_VERSION == 30 */ +#define CONFIG_RECV_BAT_ABSENT_NOTIFY +#endif /* CONFIG_MTK_GAUGE_VERSION */ + +/* #define CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_DELAY */ +#define CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT + +#define CONFIG_TYPEC_CHECK_LEGACY_CABLE +#define CONFIG_TYPEC_CHECK_LEGACY_CABLE2 +#define CONFIG_TYPEC_LEGACY2_AUTO_RECYCLE +/* #define CONFIG_TYPEC_CHECK_SRC_UNATTACH_OPEN */ +#define CONFIG_TYPEC_LEGACY3_ALWAYS_LOCAL_RP + +#define CONFIG_TYPEC_CAP_RA_DETACH +#define CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG + +#define CONFIG_TYPEC_CAP_POWER_OFF_CHARGE + +#define CONFIG_TYPEC_CAP_ROLE_SWAP +#define CONFIG_TYPEC_CAP_ROLE_SWAP_TOUT 1200 + +#define CONFIG_TYPEC_CAP_DISCHARGE_TOUT 50 + +#define CONFIG_TYPEC_CAP_AUTO_DISCHARGE +/* #define CONFIG_TCPC_AUTO_DISCHARGE_IC */ + +#define CONFIG_TYPEC_CAP_FORCE_DISCHARGE +/* #define CONFIG_TCPC_FORCE_DISCHARGE_IC */ +#define CONFIG_TCPC_FORCE_DISCHARGE_EXT + +#define CONFIG_TYPEC_CAP_AUDIO_ACC_SINK_VBUS + +#define CONFIG_TYPEC_CAP_LOW_RP_DUTY +#define CONFIG_TYPEC_WAKEUP_ONCE_LOW_DUTY + +/* #define CONFIG_TYPEC_CAP_CUSTOM_HV */ + +#define CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SNK +/* #define CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SRC */ + +#define CONFIG_TCPC_ATTACH_WAKE_LOCK_TOUT 5000 + +/* #define CONFIG_TCPC_LOG_WITH_PORT_NAME */ + +#ifdef CONFIG_TCPC_LOG_WITH_PORT_NAME +#define CONFIG_TCPC_DBG_PRESTR "{%s}TCPC-" +#else +#define CONFIG_TCPC_DBG_PRESTR "TCPC-" +#endif /* CONFIG_TCPC_LOG_WITH_PORT_NAME */ + +/* + * USB 2.0 & 3.0 current + * Unconfigured : 100 / 150 mA + * Configured : 500 / 900 mA + * http://www.testusb.com/power_issue.htm + */ + +#define CONFIG_TYPEC_SNK_CURR_DFT 100 +#define CONFIG_TYPEC_SRC_CURR_DFT 500 +#define CONFIG_TYPEC_SNK_CURR_LIMIT 0 + +#define CONFIG_TCPC_SOURCE_VCONN +#define CONFIG_TCPC_VCONN_SUPPLY_MODE + +#define CONFIG_TCPC_VSAFE0V_DETECT +/* #define CONFIG_TCPC_VSAFE0V_DETECT_EXT */ +#ifdef CONFIG_TCPC_VSAFE0V_DETECT +#define CONFIG_TCPC_VSAFE0V_DETECT_IC +#endif /* CONFIG_TCPC_VSAFE0V_DETECT */ + +#define CONFIG_TCPC_LPM_CONFIRM +#define CONFIG_TCPC_LPM_POSTPONE + +#define CONFIG_TCPC_LOW_POWER_MODE +#define CONFIG_TCPC_CLOCK_GATING + +/* #define CONFIG_TCPC_WATCHDOG_EN */ +/* #define CONFIG_TCPC_INTRST_EN */ +#define CONFIG_TCPC_I2CRST_EN + +#define CONFIG_TCPC_SHUTDOWN_CC_DETACH +#define CONFIG_TCPC_SHUTDOWN_VBUS_DISABLE + +#define CONFIG_TCPC_NOTIFIER_LATE_SYNC + +#define CONFIG_TCPC_NOTIFICATION_NON_BLOCKING + +#ifdef CONFIG_USB_POWER_DELIVERY + +/* Experimental for CodeSize */ +#define CONFIG_USB_PD_DR_SWAP +#define CONFIG_USB_PD_PR_SWAP +#define CONFIG_USB_PD_VCONN_SWAP +#define CONFIG_USB_PD_PE_SINK +#define CONFIG_USB_PD_PE_SOURCE +#define CONFIG_USB_PD_DISABLE_PE + +#define CONFIG_USB_PD_TCPM_CB_RETRY 3 +#define CONFIG_USB_PD_TCPM_CB_2ND +#define CONFIG_USB_PD_BLOCK_TCPM + +/* #define CONFIG_USB_PD_RICHTEK_UVDM */ + +/********************************************************** + * Mode Operation + **********************************************************/ + +#define CONFIG_USB_PD_MODE_OPERATION + +#ifdef CONFIG_USB_PD_MODE_OPERATION + +#define CONFIG_USB_PD_ATTEMP_ENTER_MODE + +#define CONFIG_USB_PD_ALT_MODE +#ifdef CONFIG_USB_PD_ALT_MODE +#define CONFIG_USB_PD_ALT_MODE_DFP +#define CONFIG_USB_PD_ALT_MODE_RTDC +#endif /* CONFIG_USB_PD_ALT_MODE */ + +/* #define CONFIG_USB_PD_DP_CHECK_CABLE */ +/* #define CONFIG_USB_PD_RTDC_CHECK_CABLE */ + +#endif /* CONFIG_USB_PD_MODE_OPERATION */ + +/********************************************************** + * PD revision 3.0 feature + **********************************************************/ + +#define CONFIG_USB_PD_REV30 + +#ifdef CONFIG_USB_PD_REV30 + +#define CONFIG_USB_PD_REV30_SYNC_SPEC_REV +#define CONFIG_USB_PD_REV30_COLLISION_AVOID + +/* + * If DUT send a PD command immediately after Policy Engine is ready, + * it may interrupt the compliance test process and getting a failed result. + * even you make sure the CC state is SinkTxNG or SinkTxOK. + * + * SRC_FLOW_DELAY_STARTUP: For Ellisys + * SNK_FLOW_DELAY_STARTUP: For MQP + */ + +#define CONFIG_USB_PD_REV30_SRC_FLOW_DELAY_STARTUP +#define CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP + +#define CONFIG_USB_PD_REV30_DISCOVER_CABLE_WITH_VCONN + +/* PD30 Common Feature */ + +#define CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL +#define CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE + +#define CONFIG_USB_PD_REV30_BAT_CAP_LOCAL +#define CONFIG_USB_PD_REV30_BAT_CAP_REMOTE + +#define CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL +#define CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE + +#define CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL +#define CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE + +#define CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL +#define CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE + +#define CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL +#define CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE + +#define CONFIG_USB_PD_REV30_ALERT_LOCAL +#define CONFIG_USB_PD_REV30_ALERT_REMOTE + +#define CONFIG_USB_PD_REV30_STATUS_LOCAL +#define CONFIG_USB_PD_REV30_STATUS_REMOTE + +/* #define CONFIG_USB_PD_REV30_CHUNKING_BY_PE */ + +#define CONFIG_USB_PD_REV30_PPS_SINK +/* #define CONFIG_USB_PD_REV30_PPS_SOURCE */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL +#define CONFIG_USB_PD_REV30_STATUS_LOCAL_TEMP +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL +#define CONFIG_USB_PD_REV30_BAT_INFO +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL +#undef CONFIG_USB_PD_REV30_BAT_INFO +#define CONFIG_USB_PD_REV30_BAT_INFO +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL +#define CONFIG_USB_PD_REV30_COUNTRY_AUTHORITY +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL +#undef CONFIG_USB_PD_REV30_COUNTRY_AUTHORITY +#define CONFIG_USB_PD_REV30_COUNTRY_AUTHORITY +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL +#define CONFIG_USB_PD_DPM_AUTO_SEND_ALERT +#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE +#define CONFIG_USB_PD_DPM_AUTO_GET_STATUS +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ + +#define CONFIG_MTK_HANDLE_PPS_TIMEOUT + +#endif /* CONFIG_USB_PD_REV30 */ + +/********************************************************** + * PD direct charge support + **********************************************************/ + +#ifdef CONFIG_USB_PD_ALT_MODE_RTDC +#define CONFIG_USB_PD_DIRECT_CHARGE +#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */ + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK +#undef CONFIG_USB_PD_DIRECT_CHARGE +#define CONFIG_USB_PD_DIRECT_CHARGE +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + +#define CONFIG_USB_PD_KEEP_PARTNER_ID +#define CONFIG_USB_PD_KEEP_SVIDS +#define CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID +#define CONFIG_USB_PD_DFP_READY_DISCOVER_ID +#define CONFIG_USB_PD_RESET_CABLE + +#define CONFIG_USB_PD_RANDOM_FLOW_DELAY + +#define CONFIG_USB_PD_DFP_FLOW_DELAY +#define CONFIG_USB_PD_DFP_FLOW_DELAY_STARTUP +#define CONFIG_USB_PD_DFP_FLOW_DELAY_DRSWAP +#define CONFIG_USB_PD_DFP_FLOW_DELAY_RESET + +/* Only in startup */ +#define CONFIG_USB_PD_UFP_FLOW_DELAY +#define CONFIG_USB_PD_VCONN_STABLE_DELAY +#define CONFIG_USB_PD_VCONN_SAFE5V_ONLY + +#define CONFIG_USB_PD_ATTEMP_DISCOVER_ID +#define CONFIG_USB_PD_ATTEMP_DISCOVER_SVID + +#define CONFIG_USB_PD_DISCOVER_CABLE_REQUEST_VCONN +#define CONFIG_USB_PD_DISCOVER_CABLE_RETURN_VCONN + +#define CONFIG_USB_PD_PR_SWAP_ERROR_RECOVERY + +#define CONFIG_USB_PD_CUSTOM_VDM + +#ifdef CONFIG_USB_PD_CUSTOM_VDM +#define CONFIG_USB_PD_SVDM +#define CONFIG_USB_PD_UVDM +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + +#ifdef CONFIG_TYPEC_CAP_DBGACC_SNK +/* #define CONFIG_USB_PD_CUSTOM_DBGACC */ +#endif /* CONFIG_TYPEC_CAP_DBGACC_SNK */ + +/* S/W patch for ESD issue: repeat HReset Alert */ +/* #define CONFIG_USB_PD_RECV_HRESET_COUNTER */ + +/* S/W patch for NoGoodCRC if SNK_DFT */ +#define CONFIG_USB_PD_SNK_DFT_NO_GOOD_CRC + +/* S/W patch for NoGoodCRC after PR_SWAP (repeat PS_RDY) */ +#define CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP + +/* S/W patch for delayed ps_change related to PS_RDY during PR_SWAP */ +#define CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP + +/* + * S/W patch for INT handler was stuck by other task (system busy) + * + * If the communication fails due to a timeout, + * check the rx buffer is empty or not. + * + * If the rx buffer isn't empty, postpone timer. + */ + +#define CONFIG_USB_PD_CHECK_RX_PENDING_IF_SRTOUT +#define CONFIG_USB_PD_ONLY_PRINT_SYSTEM_BUSY + +/* + * S/W patch for + * If the CC pin is shorted to ground and our receive ability is better, + * then we may receive message however always send failed. + * + * For the source role, it becomes an infinite hard reset loop. + * For the sink role, it becomes an infinite error recovery loop. + * + * RENEGOTIATION_COUNTER: + * Auto error recovery + * if retried hard reset many times still negotiation failed. + * + * ERROR_RECOVERY_ONCE: + * If we have tried an error recovery, + * using pure typec mode during next time we reconnect. + * + */ + +#define CONFIG_USB_PD_RENEGOTIATION_COUNTER +#define CONFIG_USB_PD_ERROR_RECOVERY_ONCE + +#define CONFIG_USB_PD_IGNORE_HRESET_COMPLETE_TIMER +#define CONFIG_USB_PD_DROP_REPEAT_PING +#define CONFIG_USB_PD_CHECK_DATA_ROLE +#define CONFIG_USB_PD_RETRY_CRC_DISCARD +#define CONFIG_USB_PD_PARTNER_CTRL_MSG_FIRST + +#define CONFIG_USB_PD_SNK_HRESET_KEEP_DRAW +/* #define CONFIG_USB_PD_SNK_IGNORE_HRESET_IF_TYPEC_ONLY */ +#define CONFIG_USB_PD_SNK_STANDBY_POWER +#define CONFIG_USB_PD_SNK_GOTOMIN + +/* #define CONFIG_USB_PD_SRC_HIGHCAP_POWER */ +#define CONFIG_USB_PD_SRC_TRY_PR_SWAP_IF_BAD_PW + +#define CONFIG_USB_PD_TRANSMIT_BIST2 +#define CONFIG_USB_PD_POSTPONE_VDM +#define CONFIG_USB_PD_POSTPONE_RETRY_VDM +#define CONFIG_USB_PD_POSTPONE_FIRST_VDM +#define CONFIG_USB_PD_POSTPONE_OTHER_VDM +/* #define CONFIG_USB_PD_STOP_SEND_VDM_IF_RX_BUSY */ +#define CONFIG_USB_PD_STOP_REPLY_VDM_IF_RX_BUSY + +/* #define CONFIG_USB_PD_SAFE0V_DELAY */ +/* #define CONFIG_USB_PD_SAFE0V_TIMEOUT */ + +#ifndef CONFIG_USB_PD_DPM_SVDM_RETRY +#define CONFIG_USB_PD_DPM_SVDM_RETRY 2 +#endif /* CONFIG_USB_PD_DPM_SVDM_RETRY */ + +#ifndef CONFIG_USB_PD_DFP_FLOW_RETRY_MAX +#define CONFIG_USB_PD_DFP_FLOW_RETRY_MAX 2 +#endif /* CONFIG_USB_PD_DFP_FLOW_RETRY_MAX */ + +#ifndef CONFIG_USB_PD_VBUS_STABLE_TOUT +#define CONFIG_USB_PD_VBUS_STABLE_TOUT 125 +#endif /* CONFIG_USB_PD_VBUS_STABLE_TOUT */ + +#ifndef CONFIG_USB_PD_VBUS_PRESENT_TOUT +#define CONFIG_USB_PD_VBUS_PRESENT_TOUT 20 +#endif /* CONFIG_USB_PD_VBUS_PRESENT_TOUT */ + +#ifndef CONFIG_USB_PD_CUSTOM_VDM_TOUT +#define CONFIG_USB_PD_CUSTOM_VDM_TOUT 500 +#endif /* CONFIG_USB_PD_CUSTOM_VDM_TOUT */ + +#ifndef CONFIG_USB_PD_VCONN_READY_TOUT +#define CONFIG_USB_PD_VCONN_READY_TOUT 5 +#endif /* CONFIG_USB_PD_VCONN_READY_TOUT */ + +#ifndef CONFIG_USB_PD_DFP_FLOW_DLY +#define CONFIG_USB_PD_DFP_FLOW_DLY 30 +#endif /* CONFIG_USB_PD_DFP_FLOW_DLY */ + +#ifndef CONFIG_USB_PD_UFP_FLOW_DLY +#define CONFIG_USB_PD_UFP_FLOW_DLY 300 +#endif /* CONFIG_USB_PD_UFP_FLOW_DLY */ + +#ifndef CONFIG_USB_PD_PPS_REQUEST_INTERVAL +#define CONFIG_USB_PD_PPS_REQUEST_INTERVAL 7000 +#endif /* CONFIG_USB_PD_PPS_REQUEST_INTERVAL */ + +#undef CONFIG_USB_PD_WAIT_BC12 +#endif /* CONFIG_USB_POWER_DELIVERY */ + +/* debug config */ +#define CONFIG_USB_PD_DBG_ALERT_STATUS +/* #define CONFIG_USB_PD_DBG_SKIP_ALERT_HANDLER */ +#define CONFIG_USB_PD_DBG_DP_DFP_D_AUTO_UPDATE + +/********************************************************** + * TypeC Shield Protection + **********************************************************/ + +#ifdef CONFIG_MTK_TYPEC_WATER_DETECT +#define CONFIG_WATER_DETECTION +#endif /* CONFIG_MTK_TYPEC_WATER_DETECT */ + +#ifdef CONFIG_WATER_DETECTION +#define CONFIG_WD_SBU_POLLING +/* #define CONFIG_WATER_CALIBRATION */ +#define CONFIG_WD_SBU_CALIB_INIT 1800 /* mV */ +#define CONFIG_WD_SBU_PL_BOUND 200 /* mV */ +#define CONFIG_WD_SBU_PH_AUDDEV 200 /* mV */ +#define CONFIG_WD_SBU_PH_LBOUND 1180 /* mV */ +#define CONFIG_WD_SBU_PH_LBOUND1_C2C 2850 /* mV */ +#define CONFIG_WD_SBU_PH_UBOUND1_C2C 3150 /* mV */ +#define CONFIG_WD_SBU_PH_UBOUND2_C2C 3800 /* mV */ +#define CONFIG_WD_SBU_AUD_UBOUND 1600 /* mV */ +#define CONFIG_WD_PROTECT_RETRY_COUNT 3 +#endif /* CONFIG_WATER_DETECTION */ + +#ifdef CONFIG_WD_SBU_POLLING +#define CONFIG_WD_POLLING_ONLY +#endif /* CONFIG_WD_SBU_POLLING */ + +#define CONFIG_CABLE_TYPE_DETECTION + +#endif /* CONFIG_TCPC_CLASS */ +#endif /* __LINUX_TCPC_CONFIG_H */ diff --git a/drivers/usb/typec/mmi_tcpc/inc/tcpci_core.h b/drivers/usb/typec/mmi_tcpc/inc/tcpci_core.h new file mode 100644 index 000000000000..6f996eb43b17 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/tcpci_core.h @@ -0,0 +1,714 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#ifndef __LINUX_RT_TCPCI_CORE_H +#define __LINUX_RT_TCPCI_CORE_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tcpm.h" +#include "tcpci_timer.h" +#include "tcpci_config.h" + +#ifdef CONFIG_USB_POWER_DELIVERY +#include "pd_core.h" +#ifdef CONFIG_USB_PD_WAIT_BC12 +#include +#endif /* CONFIG_USB_PD_WAIT_BC12 */ +#endif + +/* The switch of log message */ +#define TYPEC_INFO_ENABLE 1 +#define TYPEC_INFO2_ENABLE 1 +#define PE_EVENT_DBG_ENABLE 1 +#define PE_STATE_INFO_ENABLE 1 +#define TCPC_INFO_ENABLE 1 +#define TCPC_TIMER_DBG_EN 0 +#define TCPC_TIMER_INFO_EN 0 +#define PE_INFO_ENABLE 1 +#define TCPC_DBG_ENABLE 0 +#define TCPC_DBG2_ENABLE 0 +#define DPM_INFO_ENABLE 1 +#define DPM_INFO2_ENABLE 1 +#define DPM_DBG_ENABLE 0 +#define PD_ERR_ENABLE 1 +#define PE_DBG_ENABLE 0 +#define TYPEC_DBG_ENABLE 0 + + +#define DP_INFO_ENABLE 1 +#define DP_DBG_ENABLE 1 + +#define UVDM_INFO_ENABLE 1 +#define TCPM_DBG_ENABLE 1 + +#define MMI_DBG_ENABLE 1 + +#ifdef CONFIG_USB_PD_ALT_MODE_RTDC +#define DC_INFO_ENABLE 1 +#define DC_DBG_ENABLE 1 +#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */ + +#define TCPC_ENABLE_ANYMSG \ + ((TCPC_DBG_ENABLE)|(TCPC_DBG2_ENABLE)|\ + (DPM_DBG_ENABLE)|\ + (PD_ERR_ENABLE)|(PE_INFO_ENABLE)|(TCPC_TIMER_INFO_EN)|\ + (PE_DBG_ENABLE)|(PE_EVENT_DBG_ENABLE)|\ + (PE_STATE_INFO_ENABLE)|(TCPC_INFO_ENABLE)|\ + (TCPC_TIMER_DBG_EN)|(TYPEC_DBG_ENABLE)|\ + (TYPEC_INFO_ENABLE)|\ + (DP_INFO_ENABLE)|(DP_DBG_ENABLE)|\ + (UVDM_INFO_ENABLE)|(TCPM_DBG_ENABLE)) + +/* Disable VDM DBG Msg */ +#define PE_STATE_INFO_VDM_DIS 0 +#define PE_EVT_INFO_VDM_DIS 0 +#define PE_DBG_RESET_VDM_DIS 1 + +#define PD_BUG_ON(x) WARN_ON(x) + +extern struct class *tcpc_class; +struct tcpc_device; + +struct tcpc_desc { + uint8_t role_def; + uint8_t rp_lvl; + uint8_t vconn_supply; + int notifier_supply_num; + char *name; +}; + +int tcpc_device_irq_enable(struct tcpc_device *tcpc); + +/*---------------------------------------------------------------------------*/ + +#ifdef CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SNK +#define CONFIG_TYPEC_NOTIFY_ATTACHWAIT +#endif /* CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SNK */ + +#ifdef CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SRC +#undef CONFIG_TYPEC_NOTIFY_ATTACHWAIT +#define CONFIG_TYPEC_NOTIFY_ATTACHWAIT +#endif /* CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SNK */ + + +#ifdef CONFIG_TCPC_FORCE_DISCHARGE_EXT +#define CONFIG_TCPC_EXT_DISCHARGE +#endif /* CONFIG_TCPC_FORCE_DISCHARGE_EXT */ + +/*---------------------------------------------------------------------------*/ + +/* TCPC Power Register Define */ +#define TCPC_REG_POWER_STATUS_EXT_VSAFE0V (1<<15) /* extend */ +#define TCPC_REG_POWER_STATUS_VBUS_PRES (1<<2) + +/* TCPC Alert Register Define */ +#define TCPC_REG_ALERT_EXT_RA_DETACH (1<<(16+5)) +#define TCPC_REG_ALERT_EXT_WATCHDOG (1<<(16+2)) +#define TCPC_REG_ALERT_EXT_VBUS_80 (1<<(16+1)) +#define TCPC_REG_ALERT_EXT_WAKEUP (1<<(16+0)) + +#define TCPC_REG_ALERT_VBUS_DISCNCT (1<<11) +#define TCPC_REG_ALERT_RX_BUF_OVF (1<<10) +#define TCPC_REG_ALERT_FAULT (1<<9) +#define TCPC_REG_ALERT_V_ALARM_LO (1<<8) +#define TCPC_REG_ALERT_V_ALARM_HI (1<<7) +#define TCPC_REG_ALERT_TX_SUCCESS (1<<6) +#define TCPC_REG_ALERT_TX_DISCARDED (1<<5) +#define TCPC_REG_ALERT_TX_FAILED (1<<4) +#define TCPC_REG_ALERT_RX_HARD_RST (1<<3) +#define TCPC_REG_ALERT_RX_STATUS (1<<2) +#define TCPC_REG_ALERT_POWER_STATUS (1<<1) +#define TCPC_REG_ALERT_CC_STATUS (1<<0) + +#define TCPC_REG_ALERT_RX_MASK \ + (TCPC_REG_ALERT_RX_STATUS | TCPC_REG_ALERT_RX_BUF_OVF) + +#define TCPC_REG_ALERT_RX_ALL_MASK \ + (TCPC_REG_ALERT_RX_MASK | TCPC_REG_ALERT_RX_HARD_RST) + +#define TCPC_REG_ALERT_HRESET_SUCCESS \ + (TCPC_REG_ALERT_TX_SUCCESS | TCPC_REG_ALERT_TX_FAILED) + +#define TCPC_REG_ALERT_TX_MASK (TCPC_REG_ALERT_TX_SUCCESS | \ + TCPC_REG_ALERT_TX_FAILED | TCPC_REG_ALERT_TX_DISCARDED) + +#define TCPC_REG_ALERT_TXRX_MASK \ + (TCPC_REG_ALERT_TX_MASK | TCPC_REG_ALERT_RX_MASK) + +/* TCPC Behavior Flags */ +#define TCPC_FLAGS_RETRY_CRC_DISCARD (1<<0) +#define TCPC_FLAGS_WAIT_HRESET_COMPLETE (1<<1) /* Always true */ +#define TCPC_FLAGS_CHECK_CC_STABLE (1<<2) +#define TCPC_FLAGS_LPM_WAKEUP_WATCHDOG (1<<3) +#define TCPC_FLAGS_CHECK_RA_DETACHE (1<<4) +#define TCPC_FLAGS_PREFER_LEGACY2 (1<<5) +#define TCPC_FLAGS_DISABLE_LEGACY (1<<6) + +#define TCPC_FLAGS_PD_REV30 (1<<7) + +#define TCPC_FLAGS_WATCHDOG_EN (1<<8) +#define TCPC_FLAGS_WATER_DETECTION (1<<9) +#define TCPC_FLAGS_CABLE_TYPE_DETECTION (1<<10) +#define TCPC_FLAGS_VCONN_SAFE5V_ONLY (1<<11) +#define TCPC_FLAGS_ALERT_V10 (1<<12) + +#define TYPEC_CC_PULL(rp_lvl, res) ((rp_lvl & 0x03) << 3 | (res & 0x07)) + +enum tcpc_rp_lvl { + TYPEC_RP_DFT, + TYPEC_RP_1_5, + TYPEC_RP_3_0, + TYPEC_RP_RSV, +}; + +enum tcpc_cc_pull { + TYPEC_CC_RA = 0, + TYPEC_CC_RP = 1, + TYPEC_CC_RD = 2, + TYPEC_CC_OPEN = 3, + TYPEC_CC_DRP = 4, /* from Rd */ + + TYPEC_CC_RP_DFT = TYPEC_CC_PULL(TYPEC_RP_DFT, TYPEC_CC_RP), + TYPEC_CC_RP_1_5 = TYPEC_CC_PULL(TYPEC_RP_1_5, TYPEC_CC_RP), + TYPEC_CC_RP_3_0 = TYPEC_CC_PULL(TYPEC_RP_3_0, TYPEC_CC_RP), + + TYPEC_CC_DRP_DFT = TYPEC_CC_PULL(TYPEC_RP_DFT, TYPEC_CC_DRP), + TYPEC_CC_DRP_1_5 = TYPEC_CC_PULL(TYPEC_RP_1_5, TYPEC_CC_DRP), + TYPEC_CC_DRP_3_0 = TYPEC_CC_PULL(TYPEC_RP_3_0, TYPEC_CC_DRP), +}; + +#define TYPEC_CC_PULL_GET_RES(pull) (pull & 0x07) +#define TYPEC_CC_PULL_GET_RP_LVL(pull) ((pull & 0x18) >> 3) + +enum tcpm_rx_cap_type { + TCPC_RX_CAP_SOP = 1 << 0, + TCPC_RX_CAP_SOP_PRIME = 1 << 1, + TCPC_RX_CAP_SOP_PRIME_PRIME = 1 << 2, + TCPC_RX_CAP_SOP_DEBUG_PRIME = 1 << 3, + TCPC_RX_CAP_SOP_DEBUG_PRIME_PRIME = 1 << 4, + TCPC_RX_CAP_HARD_RESET = 1 << 5, + TCPC_RX_CAP_CABLE_RESET = 1 << 6, +}; + +struct tcpc_ops { + int (*init)(struct tcpc_device *tcpc, bool sw_reset); + int (*init_alert_mask)(struct tcpc_device *tcpc); + int (*alert_status_clear)(struct tcpc_device *tcpc, uint32_t mask); + int (*fault_status_clear)(struct tcpc_device *tcpc, uint8_t status); + int (*set_alert_mask)(struct tcpc_device *tcpc, uint32_t mask); + int (*get_chip_id)(struct tcpc_device *tcpc, uint32_t *chip_id); + int (*get_alert_mask)(struct tcpc_device *tcpc, uint32_t *mask); + int (*get_alert_status)(struct tcpc_device *tcpc, uint32_t *alert); + int (*get_power_status)(struct tcpc_device *tcpc, uint16_t *pwr_status); + int (*get_fault_status)(struct tcpc_device *tcpc, uint8_t *status); + int (*get_cc)(struct tcpc_device *tcpc, int *cc1, int *cc2); + int (*set_cc)(struct tcpc_device *tcpc, int pull); + int (*set_polarity)(struct tcpc_device *tcpc, int polarity); + int (*set_low_rp_duty)(struct tcpc_device *tcpc, bool low_rp); + int (*set_vconn)(struct tcpc_device *tcpc, int enable); + int (*deinit)(struct tcpc_device *tcpc); + int (*alert_vendor_defined_handler)(struct tcpc_device *tcpc); + int (*set_port_type)(struct tcpc_device *tcpc, int mode); +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC + int (*is_vsafe0v)(struct tcpc_device *tcpc); +#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */ + +#ifdef CONFIG_WATER_DETECTION + int (*is_water_detected)(struct tcpc_device *tcpc); + int (*set_water_protection)(struct tcpc_device *tcpc, bool en); + int (*set_usbid_polling)(struct tcpc_device *tcpc, bool en); +#endif /* CONFIG_WATER_DETECTION */ + +#ifdef CONFIG_TCPC_LOW_POWER_MODE + int (*is_low_power_mode)(struct tcpc_device *tcpc); + int (*set_low_power_mode)(struct tcpc_device *tcpc, bool en, int pull); +#endif /* CONFIG_TCPC_LOW_POWER_MODE */ + + int (*set_watchdog)(struct tcpc_device *tcpc, bool en); + +#ifdef CONFIG_TCPC_INTRST_EN + int (*set_intrst)(struct tcpc_device *tcpc, bool en); +#endif /* CONFIG_TCPC_INTRST_EN */ + +#ifdef CONFIG_TYPEC_CAP_AUTO_DISCHARGE +#ifdef CONFIG_TCPC_AUTO_DISCHARGE_IC + int (*set_auto_discharge)(struct tcpc_device *tcpc, bool en); +#endif /* CONFIG_TCPC_AUTO_DISCHARGE_IC */ +#endif /* CONFIG_TYPEC_CAP_AUTO_DISCHARGE */ + +#ifdef CONFIG_USB_POWER_DELIVERY + int (*set_msg_header)(struct tcpc_device *tcpc, + uint8_t power_role, uint8_t data_role); + int (*set_rx_enable)(struct tcpc_device *tcpc, uint8_t enable); + int (*get_message)(struct tcpc_device *tcpc, uint32_t *payload, + uint16_t *head, enum tcpm_transmit_type *type); + int (*protocol_reset)(struct tcpc_device *tcpc); + int (*transmit)(struct tcpc_device *tcpc, + enum tcpm_transmit_type type, + uint16_t header, const uint32_t *data); + int (*set_bist_test_mode)(struct tcpc_device *tcpc, bool en); + int (*set_bist_carrier_mode)(struct tcpc_device *tcpc, uint8_t pattern); + +#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD + int (*retransmit)(struct tcpc_device *tcpc); +#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */ + +#ifdef CONFIG_TYPEC_CAP_FORCE_DISCHARGE +#ifdef CONFIG_TCPC_FORCE_DISCHARGE_IC + int (*set_force_discharge)(struct tcpc_device *tcpc, bool en, int mv); +#endif /* CONFIG_TCPC_FORCE_DISCHARGE_IC */ +#endif /* CONFIG_TYPEC_CAP_FORCE_DISCHARGE */ + +#endif /* CONFIG_USB_POWER_DELIVERY */ +}; + +#define TCPC_VBUS_SOURCE_0V (0) +#define TCPC_VBUS_SOURCE_5V (5000) + +#define TCPC_VBUS_SINK_0V (0) +#define TCPC_VBUS_SINK_5V (5000) + +#define TCPC_LOW_POWER_MODE_RETRY 5 + +/* + * Confirm DUT is connected to legacy cable or not + * after suupect_counter > this threshold (0 = always check) + */ + +#define TCPC_LEGACY_CABLE_SUSPECT_THD 1 + +/* + * Try another s/w workaround after retry_counter more than this value + * Try which soltuion first is determined by tcpc_flags + */ + +#define TCPC_LEGACY_CABLE_RETRY_SOLUTION 2 + +struct tcpc_managed_res; + +/* + * tcpc device + */ + +struct tcpc_device { + struct i2c_client *client; + struct tcpc_ops *ops; + void *drv_data; + struct tcpc_desc desc; + struct device dev; + uint8_t wake_lock_pd; + struct wakeup_source *attach_wake_lock; + struct wakeup_source *detach_wake_lock; + + /* For tcpc timer & event */ + uint32_t timer_handle_index; + struct hrtimer tcpc_timer[PD_TIMER_NR]; + + struct alarm wake_up_timer; + struct delayed_work wake_up_work; + struct wakeup_source *wakeup_wake_lock; + + ktime_t last_expire[PD_TIMER_NR]; + struct mutex access_lock; + struct mutex typec_lock; + struct mutex timer_lock; + struct semaphore timer_enable_mask_lock; + spinlock_t timer_tick_lock; + atomic_t pending_event; + uint64_t timer_tick; + uint64_t timer_enable_mask; + wait_queue_head_t event_wait_que; + wait_queue_head_t timer_wait_que; + struct task_struct *event_task; + struct task_struct *timer_task; + + struct delayed_work init_work; + struct delayed_work event_init_work; + struct workqueue_struct *evt_wq; + struct srcu_notifier_head evt_nh[TCP_NOTIFY_IDX_NR]; + struct tcpc_managed_res *mr_head; + struct mutex mr_lock; + + /* For TCPC TypeC */ + uint8_t typec_state; + uint8_t typec_role; + uint8_t typec_role_new; + uint8_t typec_attach_old; + uint8_t typec_attach_new; + uint8_t typec_local_cc; + uint8_t typec_local_rp_level; + uint8_t typec_remote_cc[2]; + uint8_t typec_remote_rp_level; + uint8_t typec_wait_ps_change; + bool typec_polarity; + bool typec_drp_try_timeout; + bool typec_lpm; + bool typec_cable_only; + bool typec_power_ctrl; + bool typec_watchdog; + bool typec_reach_vsafe0v; + bool typec_is_attached_src; + + int typec_usb_sink_curr; + +#ifdef CONFIG_TYPEC_CAP_CUSTOM_HV + bool typec_during_custom_hv; +#endif /* CONFIG_TYPEC_CAP_CUSTOM_HV */ + + uint8_t typec_lpm_pull; + uint8_t typec_lpm_retry; + +#ifdef CONFIG_TYPEC_WAKEUP_ONCE_LOW_DUTY + bool typec_wakeup_once; + bool typec_low_rp_duty_cntdown; +#endif /* CONFIG_TYPEC_WAKEUP_ONCE_LOW_DUTY */ + +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE + uint8_t typec_legacy_cable; +#if TCPC_LEGACY_CABLE_SUSPECT_THD + uint8_t typec_legacy_cable_suspect; +#endif /* TCPC_LEGACY_CABLE_SUSPECT_THD */ + +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE2 + uint8_t typec_legacy_retry_wk; +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE2 */ +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */ + +#ifdef CONFIG_TYPEC_CAP_ROLE_SWAP + uint8_t typec_during_role_swap; +#endif /* CONFIG_TYPEC_CAP_ROLE_SWAP */ + +#ifdef CONFIG_TYPEC_CAP_AUTO_DISCHARGE +#ifdef CONFIG_TCPC_AUTO_DISCHARGE_IC + bool typec_auto_discharge; +#endif /* CONFIG_TCPC_AUTO_DISCHARGE_IC */ +#endif /* CONFIG_TYPEC_CAP_AUTO_DISCHARGE */ + +#ifdef CONFIG_TCPC_EXT_DISCHARGE + bool typec_ext_discharge; +#endif /* CONFIG_TCPC_EXT_DISCHARGE */ + +#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE + uint8_t tcpc_vconn_supply; +#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */ + +#ifdef CONFIG_TCPC_SOURCE_VCONN + bool tcpc_source_vconn; +#endif /* CONFIG_TCPC_SOURCE_VCONN */ + + uint32_t tcpc_flags; + +#ifdef CONFIG_DUAL_ROLE_USB_INTF + struct dual_role_phy_instance *dr_usb; + uint8_t dual_role_supported_modes; + uint8_t dual_role_mode; + uint8_t dual_role_pr; + uint8_t dual_role_dr; + uint8_t dual_role_vconn; +#endif /* CONFIG_DUAL_ROLE_USB_INTF */ + +#ifdef CONFIG_USB_POWER_DELIVERY + /* Event */ + uint8_t pd_event_count; + uint8_t pd_event_head_index; + uint8_t pd_msg_buffer_allocated; + + uint8_t pd_last_vdm_msg_id; + bool pd_pending_vdm_event; + bool pd_pending_vdm_reset; + bool pd_pending_vdm_good_crc; + bool pd_pending_vdm_discard; + bool pd_pending_vdm_attention; + bool pd_postpone_vdm_timeout; + + struct pd_msg pd_last_vdm_msg; + struct pd_msg pd_attention_vdm_msg; + struct pd_event pd_vdm_event; + + struct pd_msg pd_msg_buffer[PD_MSG_BUF_SIZE]; + struct pd_event pd_event_ring_buffer[PD_EVENT_BUF_SIZE]; + + uint8_t tcp_event_count; + uint8_t tcp_event_head_index; + struct tcp_dpm_event tcp_event_ring_buffer[TCP_EVENT_BUF_SIZE]; + + bool pd_pe_running; + bool pd_wait_pe_idle; + bool pd_hard_reset_event_pending; + bool pd_wait_hard_reset_complete; + bool pd_wait_pr_swap_complete; + bool pd_ping_event_pending; + uint8_t pd_bist_mode; + uint8_t pd_transmit_state; + uint8_t pd_wait_vbus_once; + +#ifdef CONFIG_USB_PD_DIRECT_CHARGE + bool pd_during_direct_charge; +#endif /* CONFIG_USB_PD_DIRECT_CHARGE */ + +#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD + bool pd_discard_pending; +#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */ + +#ifdef CONFIG_TYPEC_CAP_FORCE_DISCHARGE +#ifdef CONFIG_TCPC_FORCE_DISCHARGE_IC + bool pd_force_discharge; +#endif /* CONFIG_TCPC_FORCE_DISCHARGE_IC */ +#endif /* CONFIG_TYPEC_CAP_FORCE_DISCHARGE */ + +#ifdef CONFIG_USB_PD_REV30 + uint8_t pd_retry_count; +#endif /* CONFIG_USB_PD_REV30 */ + +#ifdef CONFIG_USB_PD_DISABLE_PE + bool disable_pe; /* typec only */ +#endif /* CONFIG_USB_PD_DISABLE_PE */ + + struct pd_port pd_port; +#ifdef CONFIG_USB_PD_REV30 + struct notifier_block bat_nb; + struct delayed_work bat_update_work; + struct power_supply *bat_psy; + uint8_t charging_status; + int bat_soc; +#endif /* CONFIG_USB_PD_REV30 */ +#ifdef CONFIG_USB_PD_WAIT_BC12 + uint8_t pd_wait_bc12_count; + struct power_supply *usb_psy; +#endif /* CONFIG_USB_PD_WAIT_BC12 */ +#endif /* CONFIG_USB_POWER_DELIVERY */ + u8 vbus_level:2; +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC + bool vbus_safe0v; +#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */ + bool vbus_present; + u8 irq_enabled:1; + u8 pd_inited_flag:1; + + /* TypeC Shield Protection */ +#ifdef CONFIG_WATER_DETECTION + int usbid_calib; +#endif /* CONFIG_WATER_DETECTION */ +#ifdef CONFIG_CABLE_TYPE_DETECTION + enum tcpc_cable_type typec_cable_type; +#endif /* CONFIG_CABLE_TYPE_DETECTION */ +}; + +#define to_tcpc_device(obj) container_of(obj, struct tcpc_device, dev) + +#ifdef CONFIG_USB_POWER_DELIVERY +static inline uint8_t pd_get_rev(struct pd_port *pd_port, uint8_t sop_type) +{ + uint8_t pd_rev = PD_REV20; +#ifdef CONFIG_USB_PD_REV30_SYNC_SPEC_REV + struct pe_data *pe_data = &pd_port->pe_data; + struct tcpc_device *tcpc = pd_port->tcpc; + + if (sop_type == TCPC_TX_SOP) { + pd_rev = pd_port->pd_revision[0]; + } else { + if (pe_data->explicit_contract || pe_data->cable_rev_discovered) + pd_rev = pd_port->pd_revision[1]; + else if (tcpc->tcpc_flags & TCPC_FLAGS_PD_REV30) + pd_rev = PD_REV30; + } +#endif /* CONFIG_USB_PD_REV30_SYNC_SPEC_REV */ + + return pd_rev; +} + +static inline bool pd_check_rev30(struct pd_port *pd_port) +{ + return pd_get_rev(pd_port, TCPC_TX_SOP) >= PD_REV30; +} +#endif /* CONFIG_USB_POWER_DELIVERY */ + +#ifdef CONFIG_PD_DBG_INFO +#define __RT_DBG_INFO pd_dbg_info +#else +#define __RT_DBG_INFO pr_info +#endif /* CONFIG_PD_DBG_INFO */ + +#ifdef CONFIG_TCPC_LOG_WITH_PORT_NAME +#define RT_DBG_INFO(format, args...) __RT_DBG_INFO(format, \ + tcpc->desc.name, ##args) +#else +#define RT_DBG_INFO(format, args...) __RT_DBG_INFO(format, ##args) +#endif /* CONFIG_TCPC_LOG_WITH_PORT_NAME */ + +#if TYPEC_DBG_ENABLE +#define TYPEC_DBG(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "TYPEC:" format, ##args) +#else +#define TYPEC_DBG(format, args...) +#endif /* TYPEC_DBG_ENABLE */ + +#if TYPEC_INFO_ENABLE +#define TYPEC_INFO(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "TYPEC:" format, ##args) +#else +#define TYPEC_INFO(format, args...) +#endif /* TYPEC_INFO_ENABLE */ + +#if TYPEC_INFO2_ENABLE +#define TYPEC_INFO2(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "TYPEC:" format, ##args) +#else +#define TYPEC_INFO2(format, args...) +#endif /* TYPEC_INFO_ENABLE */ + +#if TCPC_INFO_ENABLE +#define TCPC_INFO(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "TCPC:" format, ##args) +#else +#define TCPC_INFO(foramt, args...) +#endif /* TCPC_INFO_ENABLE */ + +#if TCPC_DBG_ENABLE +#define TCPC_DBG(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "TCPC:" format, ##args) +#else +#define TCPC_DBG(format, args...) +#endif /* TCPC_DBG_ENABLE */ + +#if TCPC_DBG2_ENABLE +#define TCPC_DBG2(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "TCPC:" format, ##args) +#else +#define TCPC_DBG2(format, args...) +#endif /* TCPC_DBG2_ENABLE */ + +#define TCPC_ERR(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "TCPC-ERR:" format, ##args) + +#define DP_ERR(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "DP-ERR:" format, ##args) + +#if DPM_INFO_ENABLE +#define DPM_INFO(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "DPM:" format, ##args) +#else +#define DPM_INFO(format, args...) +#endif /* DPM_DBG_INFO */ + +#if DPM_INFO2_ENABLE +#define DPM_INFO2(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "DPM:" format, ##args) +#else +#define DPM_INFO2(format, args...) +#endif /* DPM_DBG_INFO */ + +#if DPM_DBG_ENABLE +#define DPM_DBG(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "DPM:" format, ##args) +#else +#define DPM_DBG(format, args...) +#endif /* DPM_DBG_ENABLE */ + +#if PD_ERR_ENABLE +#define PD_ERR(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "PD-ERR:" format, ##args) +#else +#define PD_ERR(format, args...) +#endif /* PD_ERR_ENABLE */ + +#if PE_INFO_ENABLE +#define PE_INFO(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "PE:" format, ##args) +#else +#define PE_INFO(format, args...) +#endif /* PE_INFO_ENABLE */ + +#if PE_EVENT_DBG_ENABLE +#define PE_EVT_INFO(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "PE-EVT:" format, ##args) +#else +#define PE_EVT_INFO(format, args...) +#endif /* PE_EVENT_DBG_ENABLE */ + +#if PE_DBG_ENABLE +#define PE_DBG(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "PE:" format, ##args) +#else +#define PE_DBG(format, args...) +#endif /* PE_DBG_ENABLE */ + +#if PE_STATE_INFO_ENABLE +#define PE_STATE_INFO(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "PE:" format, ##args) +#else +#define PE_STATE_INFO(format, args...) +#endif /* PE_STATE_IFNO_ENABLE */ + +#if DP_INFO_ENABLE +#define DP_INFO(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "DP:" format, ##args) +#else +#define DP_INFO(format, args...) +#endif /* DP_INFO_ENABLE */ + +#if DP_DBG_ENABLE +#define DP_DBG(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "DP:" format, ##args) +#else +#define DP_DBG(format, args...) +#endif /* DP_DBG_ENABLE */ + +#if UVDM_INFO_ENABLE +#define UVDM_INFO(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "UVDM:" format, ##args) +#else +#define UVDM_INFO(format, args...) +#endif + +#if TCPM_DBG_ENABLE +#define TCPM_DBG(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "TCPM:" format, ##args) +#else +#define TCPM_DBG(format, args...) +#endif + +#ifdef CONFIG_USB_PD_ALT_MODE_RTDC + +#if DC_INFO_ENABLE +#define DC_INFO(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "DC> " format, ##args) +#else +#define DC_INFO(format, args...) +#endif + +#if DC_DBG_ENABLE +#define DC_DBG(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "DC> " format, ##args) +#else +#define DC_DBG(format, args...) +#endif + +#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */ + +#if MMI_DBG_ENABLE +#define MMI_INFO(format, args...) \ + RT_DBG_INFO(CONFIG_TCPC_DBG_PRESTR "MMI> " format, ##args) +#else +#define MMI_INFO(format, args...) +#endif + +#endif /* #ifndef __LINUX_RT_TCPCI_CORE_H */ diff --git a/drivers/usb/typec/mmi_tcpc/inc/tcpci_event.h b/drivers/usb/typec/mmi_tcpc/inc/tcpci_event.h new file mode 100644 index 000000000000..9b8c22df91a2 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/tcpci_event.h @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#ifndef TCPC_EVENT_BUF_H_INCLUDED +#define TCPC_EVENT_BUF_H_INCLUDED + +#include "tcpci_timer.h" +#include "tcpm.h" + + +#define PD_MSG_BUF_SIZE (4*2) +#define PD_EVENT_BUF_SIZE (8*2) +#define TCP_EVENT_BUF_SIZE (2*2) + +struct tcpc_device; + +struct pd_msg { + uint8_t frame_type; + uint16_t msg_hdr; + uint32_t payload[7]; +}; + +struct pd_event { + uint8_t event_type; + uint8_t msg; + uint8_t msg_sec; + struct pd_msg *pd_msg; +}; + +struct pd_msg *pd_alloc_msg(struct tcpc_device *tcpc); +void pd_free_msg(struct tcpc_device *tcpc, struct pd_msg *pd_msg); + +bool pd_get_event(struct tcpc_device *tcpc, struct pd_event *pd_event); +bool pd_put_event(struct tcpc_device *tcpc, + const struct pd_event *pd_event, bool from_port_partner); +void pd_free_event(struct tcpc_device *tcpc, struct pd_event *pd_event); + +bool pd_get_vdm_event(struct tcpc_device *tcpc, struct pd_event *pd_event); +bool pd_put_vdm_event(struct tcpc_device *tcpc, + struct pd_event *pd_event, bool from_port_partner); + +bool pd_put_last_vdm_event(struct tcpc_device *tcpc); + +bool pd_get_deferred_tcp_event( + struct tcpc_device *tcpc, struct tcp_dpm_event *tcp_event); +bool pd_put_deferred_tcp_event( + struct tcpc_device *tcpc, const struct tcp_dpm_event *tcp_event); + +extern int tcpci_event_init(struct tcpc_device *tcpc); +extern int tcpci_event_deinit(struct tcpc_device *tcpc); +extern void pd_event_buf_reset(struct tcpc_device *tcpc); + +bool __pd_put_cc_attached_event(struct tcpc_device *tcpc, uint8_t type); +bool pd_put_cc_attached_event(struct tcpc_device *tcpc, uint8_t type); +void pd_put_cc_detached_event(struct tcpc_device *tcpc); +void pd_put_recv_hard_reset_event(struct tcpc_device *tcpc); +void pd_put_sent_hard_reset_event(struct tcpc_device *tcpc); +bool pd_put_pd_msg_event(struct tcpc_device *tcpc, struct pd_msg *pd_msg); +void pd_put_hard_reset_completed_event(struct tcpc_device *tcpc); +void pd_put_vbus_changed_event(struct tcpc_device *tcpc, bool from_ic); +void pd_put_vbus_safe0v_event(struct tcpc_device *tcpc); +void pd_put_vbus_stable_event(struct tcpc_device *tcpc); +void pd_put_vbus_present_event(struct tcpc_device *tcpc); + +enum pd_event_type { + PD_EVT_PD_MSG = 0, /* either ctrl msg or data msg */ + PD_EVT_CTRL_MSG, + PD_EVT_DATA_MSG, + +#ifdef CONFIG_USB_PD_REV30 + PD_EVT_EXT_MSG, +#endif /* CONFIG_USB_PD_REV30 */ + + PD_EVT_PD_MSG_END, + + PD_EVT_DPM_MSG = PD_EVT_PD_MSG_END, + PD_EVT_HW_MSG, + PD_EVT_PE_MSG, + PD_EVT_TIMER_MSG, + + PD_EVT_TCP_MSG, +}; + +enum pd_msg_type { +/* Control Message type */ + /* 0 Reserved */ + PD_CTRL_GOOD_CRC = 1, + PD_CTRL_GOTO_MIN = 2, + PD_CTRL_ACCEPT = 3, + PD_CTRL_REJECT = 4, + PD_CTRL_PING = 5, + PD_CTRL_PS_RDY = 6, + PD_CTRL_GET_SOURCE_CAP = 7, + PD_CTRL_GET_SINK_CAP = 8, + PD_CTRL_DR_SWAP = 9, + PD_CTRL_PR_SWAP = 10, + PD_CTRL_VCONN_SWAP = 11, + PD_CTRL_WAIT = 12, + PD_CTRL_SOFT_RESET = 13, + /* 14-15 Reserved */ + PD_CTRL_PD30_START = 0x10 + 0, +#ifdef CONFIG_USB_PD_REV30 + PD_CTRL_NOT_SUPPORTED = 0x10 + 0, + PD_CTRL_GET_SOURCE_CAP_EXT = 0x10 + 1, + PD_CTRL_GET_STATUS = 0x10 + 2, + PD_CTRL_FR_SWAP = 0x10 + 3, + PD_CTRL_GET_PPS_STATUS = 0x10 + 4, + PD_CTRL_GET_COUNTRY_CODE = 0x10 + 5, +#endif /* CONFIG_USB_PD_REV30 */ + /* 22-31 Reserved */ + PD_CTRL_MSG_NR, +/* Data message type */ + /* 0 Reserved */ + PD_DATA_SOURCE_CAP = 1, + PD_DATA_REQUEST = 2, + PD_DATA_BIST = 3, + PD_DATA_SINK_CAP = 4, + PD_DATA_PD30_START = 5, +#ifdef CONFIG_USB_PD_REV30 + PD_DATA_BAT_STATUS = 5, + PD_DATA_ALERT = 6, + PD_DATA_GET_COUNTRY_INFO = 7, +#endif /* CONFIG_USB_PD_REV30 */ + /* 7-14 Reserved */ + PD_DATA_VENDOR_DEF = 15, + PD_DATA_MSG_NR, +#ifdef CONFIG_USB_PD_REV30 +/* Extended message type */ + /* 0 Reserved */ + PD_EXT_SOURCE_CAP_EXT = 1, + PD_EXT_STATUS = 2, + PD_EXT_GET_BAT_CAP = 3, + PD_EXT_GET_BAT_STATUS = 4, + PD_EXT_BAT_CAP = 5, + PD_EXT_GET_MFR_INFO = 6, + PD_EXT_MFR_INFO = 7, + PD_EXT_SEC_REQUEST = 8, + PD_EXT_SEC_RESPONSE = 9, + PD_EXT_FW_UPDATE_REQUEST = 10, + PD_EXT_FW_UPDATE_RESPONSE = 11, + PD_EXT_PPS_STATUS = 12, + PD_EXT_COUNTRY_INFO = 13, + PD_EXT_COUNTRY_CODES = 14, + /* 15 Reserved */ + PD_EXT_MSG_NR, +#endif /* CONFIG_USB_PD_REV30 */ +/* HW Message type */ + PD_HW_CC_DETACHED = 0, + PD_HW_CC_ATTACHED, + PD_HW_RECV_HARD_RESET, + PD_HW_VBUS_PRESENT, + PD_HW_VBUS_ABSENT, + PD_HW_VBUS_SAFE0V, + PD_HW_VBUS_STABLE, + PD_HW_TX_FAILED, /* no good crc or discard */ + PD_HW_TX_DISCARD, /* discard vdm msg */ + PD_HW_RETRY_VDM, /* discard vdm msg (retry) */ +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + PD_HW_SINK_TX_CHANGE, +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ + PD_HW_MSG_NR, +/* PE Message type*/ + PD_PE_RESET_PRL_COMPLETED = 0, + PD_PE_POWER_ROLE_AT_DEFAULT, + PD_PE_HARD_RESET_COMPLETED, + PD_PE_IDLE, + PD_PE_VDM_RESET, + PD_PE_VDM_NOT_SUPPORT, + PD_PE_MSG_NR, +/* DPM Message type */ + PD_DPM_NOTIFIED = 0, + PD_DPM_ACK = PD_DPM_NOTIFIED, + PD_DPM_NAK, + PD_DPM_CAP_CHANGED, + PD_DPM_NOT_SUPPORT, + PD_DPM_MSG_NR, +}; + +enum pd_dpm_notify_type { + PD_DPM_NOTIFY_OK = 0, + PD_DPM_NOTIFY_CAP_MISMATCH, +}; + +enum pd_dpm_nak_type { + PD_DPM_NAK_REJECT = 0, + PD_DPM_NAK_WAIT = 1, +}; + +enum pd_tcp_sec_msg_type { + PD_TCP_FROM_TCPM = 0, + PD_TCP_FROM_PE = 1, +}; + +enum pd_tx_transmit_state { + PD_TX_STATE_GOOD_CRC = 0, + PD_TX_STATE_NO_GOOD_CRC, + PD_TX_STATE_DISCARD, + PD_TX_STATE_HARD_RESET, + PD_TX_STATE_NO_RESPONSE, + + PD_TX_STATE_WAIT, + PD_TX_STATE_WAIT_CRC_VDM = PD_TX_STATE_WAIT, + PD_TX_STATE_WAIT_CRC_PD, + PD_TX_STATE_WAIT_HARD_RESET, +}; + +static inline bool pd_event_msg_match(struct pd_event *pd_event, + uint8_t type, uint8_t msg) +{ + if (pd_event->event_type != type) + return false; + + return pd_event->msg == msg; +} + +static inline bool pd_event_ctrl_msg_match( + struct pd_event *pd_event, uint8_t msg) +{ + return pd_event_msg_match(pd_event, PD_EVT_CTRL_MSG, msg); +} + +static inline bool pd_event_data_msg_match( + struct pd_event *pd_event, uint8_t msg) +{ + return pd_event_msg_match(pd_event, PD_EVT_DATA_MSG, msg); +} + +static inline bool pd_event_hw_msg_match(struct pd_event *pd_event, uint8_t msg) +{ + return pd_event_msg_match(pd_event, PD_EVT_HW_MSG, msg); +} + +static inline bool pd_event_pe_msg_match(struct pd_event *pd_event, uint8_t msg) +{ + return pd_event_msg_match(pd_event, PD_EVT_PE_MSG, msg); +} + +static inline bool pd_event_timer_msg_match( + struct pd_event *pd_event, uint8_t msg) +{ + return pd_event_msg_match(pd_event, PD_EVT_TIMER_MSG, msg); +} + +#ifdef CONFIG_USB_PD_REV30 + +static inline bool pd_event_ext_msg_match( + struct pd_event *pd_event, uint8_t msg) +{ + return pd_event_msg_match(pd_event, PD_EVT_EXT_MSG, msg); +} + +#endif /* CONFIG_USB_PD_REV30 */ + +#endif /* TCPC_EVENT_BUF_H_INCLUDED */ diff --git a/drivers/usb/typec/mmi_tcpc/inc/tcpci_timer.h b/drivers/usb/typec/mmi_tcpc/inc/tcpci_timer.h new file mode 100644 index 000000000000..901919bdbdc8 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/tcpci_timer.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#ifndef TCPC_TIMER_H_INCLUDED +#define TCPC_TIMER_H_INCLUDED + +#include +#include "tcpci_config.h" + +struct tcpc_device; +enum { +#ifdef CONFIG_USB_POWER_DELIVERY + PD_TIMER_DISCOVER_ID = 0, + PD_TIMER_BIST_CONT_MODE, + PD_TIMER_HARD_RESET_COMPLETE, + PD_TIMER_NO_RESPONSE, + PD_TIMER_PS_HARD_RESET, + PD_TIMER_PS_SOURCE_OFF, + PD_TIMER_PS_SOURCE_ON, + PD_TIMER_PS_TRANSITION, + PD_TIMER_SENDER_RESPONSE, + PD_TIMER_SINK_REQUEST, + PD_TIMER_SINK_WAIT_CAP, + PD_TIMER_SOURCE_CAPABILITY, + PD_TIMER_SOURCE_START, + PD_TIMER_VCONN_ON, +#ifdef CONFIG_USB_PD_VCONN_STABLE_DELAY + PD_TIMER_VCONN_STABLE, +#endif /* CONFIG_USB_PD_VCONN_STABLE_DELAY */ + PD_TIMER_VDM_MODE_ENTRY, + PD_TIMER_VDM_MODE_EXIT, + PD_TIMER_VDM_RESPONSE, + PD_TIMER_SOURCE_TRANSITION, + PD_TIMER_SRC_RECOVER, +#ifdef CONFIG_USB_PD_REV30 + PD_TIMER_CK_NOT_SUPPORTED, +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + PD_TIMER_SINK_TX, +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ +#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE + PD_TIMER_SOURCE_PPS_TIMEOUT, +#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */ +#endif /* CONFIG_USB_PD_REV30 */ + +/* PD_TIMER (out of spec )*/ + PD_TIMER_VSAFE0V_DELAY, + PD_TIMER_VSAFE0V_TOUT, + PD_TIMER_DISCARD, + PD_TIMER_VBUS_STABLE, + PD_TIMER_VBUS_PRESENT, + PD_TIMER_UVDM_RESPONSE, + PD_TIMER_DFP_FLOW_DELAY, + PD_TIMER_UFP_FLOW_DELAY, + PD_TIMER_VCONN_READY, + PD_PE_VDM_POSTPONE, + +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + PD_TIMER_DEFERRED_EVT, +#ifdef CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP + PD_TIMER_SNK_FLOW_DELAY, +#endif /* CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP */ +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ +#endif /* CONFIG_USB_PD_REV30 */ + + PD_TIMER_PE_IDLE_TOUT, + PD_PE_TIMER_END_ID, +#endif /* CONFIG_USB_POWER_DELIVERY */ + +/* TYPEC_RT_TIMER (out of spec )*/ +#ifdef CONFIG_USB_POWER_DELIVERY + TYPEC_RT_TIMER_START_ID = PD_PE_TIMER_END_ID, +#else + TYPEC_RT_TIMER_START_ID = 0, +#endif /* CONFIG_USB_POWER_DELIVERY */ + + TYPEC_RT_TIMER_SAFE0V_DELAY = TYPEC_RT_TIMER_START_ID, + TYPEC_RT_TIMER_SAFE0V_TOUT, + TYPEC_RT_TIMER_ROLE_SWAP_START, + TYPEC_RT_TIMER_ROLE_SWAP_STOP, + TYPEC_RT_TIMER_STATE_CHANGE, + TYPEC_RT_TIMER_NOT_LEGACY, + TYPEC_RT_TIMER_LEGACY_STABLE, + TYPEC_RT_TIMER_LEGACY_RECYCLE, + TYPEC_RT_TIMER_DISCHARGE, + TYPEC_RT_TIMER_LOW_POWER_MODE, + +#ifdef CONFIG_USB_POWER_DELIVERY + TYPEC_RT_TIMER_PE_IDLE, +#ifdef CONFIG_USB_PD_WAIT_BC12 + TYPEC_RT_TIMER_PD_WAIT_BC12, +#endif /* CONFIG_USB_PD_WAIT_BC12 */ +#endif /* CONFIG_USB_POWER_DELIVERY */ + TYPEC_TIMER_ERROR_RECOVERY, +/* TYPEC-TRY-TIMER */ + TYPEC_TRY_TIMER_START_ID, + TYPEC_TRY_TIMER_DRP_TRY = TYPEC_TRY_TIMER_START_ID, + TYPEC_TRY_TIMER_DRP_TRYWAIT, +/* TYPEC-DEBOUNCE-TIMER */ + TYPEC_TIMER_START_ID, + TYPEC_TIMER_CCDEBOUNCE = TYPEC_TIMER_START_ID, + TYPEC_TIMER_PDDEBOUNCE, +#ifdef CONFIG_COMPATIBLE_APPLE_TA + TYPEC_TIMER_APPLE_CC_OPEN, +#endif /* CONFIG_COMPATIBLE_APPLE_TA */ + TYPEC_TIMER_TRYCCDEBOUNCE, + TYPEC_TIMER_SRCDISCONNECT, + TYPEC_TIMER_DRP_SRC_TOGGLE, +#ifdef CONFIG_TYPEC_CAP_NORP_SRC + TYPEC_TIMER_NORP_SRC, +#endif /* CONFIG_TYPEC_CAP_NORP_SRC */ + PD_TIMER_NR, +}; + +extern int tcpci_timer_init(struct tcpc_device *tcpc); +extern int tcpci_timer_deinit(struct tcpc_device *tcpc); +extern void tcpc_restart_timer(struct tcpc_device *tcpc, uint32_t timer_id); +extern void tcpc_enable_timer(struct tcpc_device *tcpc, uint32_t timer_id); +extern void tcpc_enable_wakeup_timer(struct tcpc_device *tcpc, bool en); +extern void tcpc_disable_timer( + struct tcpc_device *tcpc, uint32_t timer_id); +extern void tcpc_reset_typec_try_timer(struct tcpc_device *tcpc); +extern void tcpc_reset_typec_debounce_timer(struct tcpc_device *tcpc); + +extern void tcpc_reset_pe_timer(struct tcpc_device *tcpc); +#endif /* TCPC_TIMER_H_INCLUDED */ diff --git a/drivers/usb/typec/mmi_tcpc/inc/tcpci_typec.h b/drivers/usb/typec/mmi_tcpc/inc/tcpci_typec.h new file mode 100644 index 000000000000..4b76dfb1644f --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/tcpci_typec.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#ifndef __LINUX_TCPCI_TYPEC_H +#define __LINUX_TCPCI_TYPEC_H +#include "tcpci.h" + +struct tcpc_device; +extern bool tcpc_typec_is_act_as_sink_role(struct tcpc_device *tcpc); + +/****************************************************************************** + * Call following function to trigger TYPEC Connection State Change + * + * 1. H/W -> CC/PS Change. + * 2. Timer -> CCDebounce or PDDebounce or others Timeout + * 3. Policy Engine -> PR_SWAP, Error_Recovery, PE_Idle + *****************************************************************************/ + +extern int tcpc_typec_enter_lpm_again(struct tcpc_device *tcpc); +extern int tcpc_typec_handle_cc_change(struct tcpc_device *tcpc); + +extern int tcpc_typec_handle_ps_change( + struct tcpc_device *tcpc, int vbus_level); + +extern int tcpc_typec_handle_timeout( + struct tcpc_device *tcpc, uint32_t timer_id); + +extern int tcpc_typec_handle_vsafe0v(struct tcpc_device *tcpc); + +extern int tcpc_typec_set_rp_level(struct tcpc_device *tcpc, uint8_t rp_lvl); + +extern int tcpc_typec_error_recovery(struct tcpc_device *tcpc); + +extern int tcpc_typec_disable(struct tcpc_device *tcpc); +extern int tcpc_typec_enable(struct tcpc_device *tcpc); + +extern int tcpc_typec_change_role( + struct tcpc_device *tcpc, uint8_t typec_role, bool postpone); + +#ifdef CONFIG_USB_POWER_DELIVERY +extern int tcpc_typec_handle_pe_pr_swap(struct tcpc_device *tcpc); +extern int pd_adapter_create(struct platform_device *pdev); +#endif /* CONFIG_USB_POWER_DELIVERY */ + +#ifdef CONFIG_TYPEC_CAP_ROLE_SWAP +extern int tcpc_typec_swap_role(struct tcpc_device *tcpc); +#endif /* CONFIG_TYPEC_CAP_ROLE_SWAP */ + +#ifdef CONFIG_WATER_DETECTION +extern int tcpc_typec_handle_wd(struct tcpc_device *tcpc, bool wd); +#endif /* CONFIG_WATER_DETECTION */ + +#ifdef CONFIG_CABLE_TYPE_DETECTION +extern int tcpc_typec_handle_ctd(struct tcpc_device *tcpc, + enum tcpc_cable_type cable_type); +#endif /* CONFIG_CABLE_TYPEC_DETECTION */ + +#define typec_get_cc1() \ + tcpc->typec_remote_cc[0] +#define typec_get_cc2() \ + tcpc->typec_remote_cc[1] +#define typec_get_cc_res() \ + (tcpc->typec_polarity ? typec_get_cc2() : typec_get_cc1()) + +#endif /* #ifndef __LINUX_TCPCI_TYPEC_H */ diff --git a/drivers/usb/typec/mmi_tcpc/inc/tcpm.h b/drivers/usb/typec/mmi_tcpc/inc/tcpm.h new file mode 100644 index 000000000000..b8e42300fe04 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/tcpm.h @@ -0,0 +1,1934 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#ifndef TCPM_H_ +#define TCPM_H_ + +#include +#include + +#include "tcpci_config.h" +#include "tcpm_pd.h" + +struct tcpc_device; + +/* + * Type-C Port Notify Chain + */ + +enum typec_attach_type { + TYPEC_UNATTACHED = 0, + TYPEC_ATTACHED_SNK, + TYPEC_ATTACHED_SRC, + TYPEC_ATTACHED_AUDIO, + TYPEC_ATTACHED_DEBUG, /* Rd, Rd */ + +/* CONFIG_TYPEC_CAP_DBGACC_SNK */ + TYPEC_ATTACHED_DBGACC_SNK, /* Rp, Rp */ + +/* CONFIG_TYPEC_CAP_CUSTOM_SRC */ + TYPEC_ATTACHED_CUSTOM_SRC, /* Same Rp */ + +/* CONFIG_TYPEC_CAP_NORP_SRC */ + TYPEC_ATTACHED_NORP_SRC, /* No Rp */ +}; + +enum pd_connect_result { + PD_CONNECT_NONE = 0, + PD_CONNECT_TYPEC_ONLY_SNK_DFT, + PD_CONNECT_TYPEC_ONLY_SNK, + PD_CONNECT_TYPEC_ONLY_SRC, + PD_CONNECT_PE_READY_SNK, + PD_CONNECT_PE_READY_SRC, + PD_CONNECT_PE_READY_SNK_PD30, + PD_CONNECT_PE_READY_SRC_PD30, + PD_CONNECT_PE_READY_SNK_APDO, + PD_CONNECT_HARD_RESET, + +/* CONFIG_USB_PD_CUSTOM_DBGACC */ + PD_CONNECT_PE_READY_DBGACC_UFP, + PD_CONNECT_PE_READY_DBGACC_DFP, +}; + +enum tcpc_vconn_supply_mode { + /* Never provide vconn even in TYPE-C state (reject swap to On) */ + TCPC_VCONN_SUPPLY_NEVER = 0, + + /* Always provide vconn */ + TCPC_VCONN_SUPPLY_ALWAYS, + + /* Always provide vconn only if we detect Ra, otherwise startup only */ + TCPC_VCONN_SUPPLY_EMARK_ONLY, + + /* Only provide vconn during DPM initial (aginst spec) */ + TCPC_VCONN_SUPPLY_STARTUP, + + TCPC_VCONN_SUPPLY_NR, +}; + +/* Power role */ +#define PD_ROLE_SINK 0 +#define PD_ROLE_SOURCE 1 + +/* Data role */ +#define PD_ROLE_UFP 0 +#define PD_ROLE_DFP 1 + +/* Vconn role */ +#define PD_ROLE_VCONN_OFF 0 +#define PD_ROLE_VCONN_ON 1 +#define PD_ROLE_VCONN_DYNAMIC_OFF 2 +#define PD_ROLE_VCONN_DYNAMIC_ON 3 + +enum { + TCP_NOTIFY_IDX_MODE = 0, + TCP_NOTIFY_IDX_VBUS, + TCP_NOTIFY_IDX_USB, + TCP_NOTIFY_IDX_MISC, + TCP_NOTIFY_IDX_NR, +}; + +#define TCP_NOTIFY_TYPE_MODE (1 << TCP_NOTIFY_IDX_MODE) +#define TCP_NOTIFY_TYPE_VBUS (1 << TCP_NOTIFY_IDX_VBUS) +#define TCP_NOTIFY_TYPE_USB (1 << TCP_NOTIFY_IDX_USB) +#define TCP_NOTIFY_TYPE_MISC (1 << TCP_NOTIFY_IDX_MISC) + +#define TCP_NOTIFY_TYPE_ALL ((1 << TCP_NOTIFY_IDX_NR) - 1) + +enum { + /* TCP_NOTIFY_TYPE_MODE */ + TCP_NOTIFY_ENTER_MODE, + TCP_NOTIFY_MODE_START = TCP_NOTIFY_ENTER_MODE, + TCP_NOTIFY_EXIT_MODE, + TCP_NOTIFY_AMA_DP_STATE, + TCP_NOTIFY_AMA_DP_ATTENTION, + TCP_NOTIFY_AMA_DP_HPD_STATE, + TCP_NOTIFY_DC_EN_UNLOCK, + TCP_NOTIFY_UVDM, + TCP_NOTIFY_MODE_END = TCP_NOTIFY_UVDM, + + /* TCP_NOTIFY_TYPE_VBUS */ + TCP_NOTIFY_DIS_VBUS_CTRL, + TCP_NOTIFY_VBUS_START = TCP_NOTIFY_DIS_VBUS_CTRL, + TCP_NOTIFY_SOURCE_VCONN, + TCP_NOTIFY_SOURCE_VBUS, + TCP_NOTIFY_SINK_VBUS, + TCP_NOTIFY_EXT_DISCHARGE, + TCP_NOTIFY_ATTACHWAIT_SNK, + TCP_NOTIFY_ATTACHWAIT_SRC, + TCP_NOTIFY_VBUS_END = TCP_NOTIFY_ATTACHWAIT_SRC, + + /* TCP_NOTIFY_TYPE_USB */ + TCP_NOTIFY_TYPEC_STATE, + TCP_NOTIFY_USB_START = TCP_NOTIFY_TYPEC_STATE, + TCP_NOTIFY_PD_STATE, + TCP_NOTIFY_USB_END = TCP_NOTIFY_PD_STATE, + + /* TCP_NOTIFY_TYPE_MISC */ + TCP_NOTIFY_PR_SWAP, + TCP_NOTIFY_MISC_START = TCP_NOTIFY_PR_SWAP, + TCP_NOTIFY_DR_SWAP, + TCP_NOTIFY_VCONN_SWAP, + TCP_NOTIFY_HARD_RESET_STATE, + TCP_NOTIFY_ALERT, + TCP_NOTIFY_STATUS, + TCP_NOTIFY_REQUEST_BAT_INFO, + TCP_NOTIFY_WD_STATUS, + TCP_NOTIFY_CABLE_TYPE, + TCP_NOTIFY_MISC_END = TCP_NOTIFY_CABLE_TYPE, + + /*mmi pd vdm verify*/ + TCP_NOTIFY_PD_VDM_VERIFY, +}; + +struct tcp_ny_pd_state { + uint8_t connected; + uint8_t vdm_verify; +}; + +struct tcp_ny_swap_state { + uint8_t new_role; +}; + +struct tcp_ny_enable_state { + bool en; +}; + +struct tcp_ny_typec_state { + uint8_t rp_level; + uint8_t local_rp_level; + uint8_t polarity; + uint8_t old_state; + uint8_t new_state; +}; + +enum { + TCP_VBUS_CTRL_REMOVE = 0, + TCP_VBUS_CTRL_TYPEC = 1, + TCP_VBUS_CTRL_PD = 2, + + TCP_VBUS_CTRL_HRESET = TCP_VBUS_CTRL_PD, + TCP_VBUS_CTRL_PR_SWAP = 3, + TCP_VBUS_CTRL_REQUEST = 4, + TCP_VBUS_CTRL_STANDBY = 5, + TCP_VBUS_CTRL_STANDBY_UP = 6, + TCP_VBUS_CTRL_STANDBY_DOWN = 7, + + TCP_VBUS_CTRL_PD_DETECT = (1 << 7), + + TCP_VBUS_CTRL_PD_HRESET = + TCP_VBUS_CTRL_HRESET | TCP_VBUS_CTRL_PD_DETECT, + + TCP_VBUS_CTRL_PD_PR_SWAP = + TCP_VBUS_CTRL_PR_SWAP | TCP_VBUS_CTRL_PD_DETECT, + + TCP_VBUS_CTRL_PD_REQUEST = + TCP_VBUS_CTRL_REQUEST | TCP_VBUS_CTRL_PD_DETECT, + + TCP_VBUS_CTRL_PD_STANDBY = + TCP_VBUS_CTRL_STANDBY | TCP_VBUS_CTRL_PD_DETECT, + + TCP_VBUS_CTRL_PD_STANDBY_UP = + TCP_VBUS_CTRL_STANDBY_UP | TCP_VBUS_CTRL_PD_DETECT, + + TCP_VBUS_CTRL_PD_STANDBY_DOWN = + TCP_VBUS_CTRL_STANDBY_DOWN | TCP_VBUS_CTRL_PD_DETECT, +}; + +struct tcp_ny_vbus_state { + int mv; + int ma; + uint8_t type; +}; + +struct tcp_ny_mode_ctrl { + uint16_t svid; + uint8_t ops; + uint32_t mode; +}; + +enum { + SW_USB = 0, + SW_DFP_D, + SW_UFP_D, +}; + +struct tcp_ny_ama_dp_state { + uint8_t sel_config; + uint8_t signal; + uint8_t pin_assignment; + uint8_t polarity; + uint8_t active; +}; + +enum { + TCP_DP_UFP_U_MASK = 0x7C, + TCP_DP_UFP_U_POWER_LOW = 1 << 2, + TCP_DP_UFP_U_ENABLED = 1 << 3, + TCP_DP_UFP_U_MF_PREFER = 1 << 4, + TCP_DP_UFP_U_USB_CONFIG = 1 << 5, + TCP_DP_UFP_U_EXIT_MODE = 1 << 6, +}; + +struct tcp_ny_ama_dp_attention { + uint8_t state; +}; + +struct tcp_ny_ama_dp_hpd_state { + uint8_t irq; + uint8_t state; +}; + +struct tcp_ny_uvdm { + bool ack; + uint8_t uvdm_cnt; + uint16_t uvdm_svid; + uint32_t *uvdm_data; +}; + +/* + * TCP_NOTIFY_HARD_RESET_STATE + * + * Please don't expect that every signal will have a corresponding result. + * The signal can be generated multiple times before receiving a result. + */ + +enum { + /* HardReset finished because recv GoodCRC or TYPE-C only */ + TCP_HRESET_RESULT_DONE = 0, + + /* HardReset failed because detach or error recovery */ + TCP_HRESET_RESULT_FAIL, + + /* HardReset signal from Local Policy Engine */ + TCP_HRESET_SIGNAL_SEND, + + /* HardReset signal from Port Partner */ + TCP_HRESET_SIGNAL_RECV, +}; + +struct tcp_ny_hard_reset_state { + uint8_t state; +}; + +struct tcp_ny_alert { + uint32_t ado; +}; + +struct tcp_ny_status { + const struct pd_status *sdb; +}; + +struct tcp_ny_request_bat { + enum pd_battery_reference ref; +}; + +struct tcp_ny_wd_status { + bool water_detected; +}; + +enum tcpc_cable_type { + TCPC_CABLE_TYPE_NONE = 0, + TCPC_CABLE_TYPE_A2C, + TCPC_CABLE_TYPE_C2C, + TCPC_CABLE_TYPE_MAX, +}; + +struct tcp_ny_cable_type { + enum tcpc_cable_type type; +}; + +struct tcp_notify { + union { + struct tcp_ny_enable_state en_state; + struct tcp_ny_vbus_state vbus_state; + struct tcp_ny_typec_state typec_state; + struct tcp_ny_swap_state swap_state; + struct tcp_ny_pd_state pd_state; + struct tcp_ny_mode_ctrl mode_ctrl; + struct tcp_ny_ama_dp_state ama_dp_state; + struct tcp_ny_ama_dp_attention ama_dp_attention; + struct tcp_ny_ama_dp_hpd_state ama_dp_hpd_state; + struct tcp_ny_uvdm uvdm_msg; + struct tcp_ny_hard_reset_state hreset_state; + struct tcp_ny_alert alert_msg; + struct tcp_ny_status status_msg; + struct tcp_ny_request_bat request_bat; + struct tcp_ny_wd_status wd_status; + struct tcp_ny_cable_type cable_type; + }; +}; + +/* + * Type-C Port Control I/F + */ + +enum tcpm_error_list { + TCPM_SUCCESS = 0, + TCPM_ERROR_UNKNOWN = -1, + TCPM_ERROR_UNATTACHED = -2, + TCPM_ERROR_PARAMETER = -3, + TCPM_ERROR_PUT_EVENT = -4, + TCPM_ERROR_NO_SUPPORT = -5, + TCPM_ERROR_NO_PD_CONNECTED = -6, + TCPM_ERROR_NO_POWER_CABLE = -7, + TCPM_ERROR_NO_PARTNER_INFORM = -8, + TCPM_ERROR_NO_SOURCE_CAP = -9, + TCPM_ERROR_NO_SINK_CAP = -10, + TCPM_ERROR_NOT_DRP_ROLE = -11, + TCPM_ERROR_DURING_ROLE_SWAP = -12, + TCPM_ERROR_NO_EXPLICIT_CONTRACT = -13, + TCPM_ERROR_ERROR_RECOVERY = -14, + TCPM_ERROR_NOT_FOUND = -15, + TCPM_ERROR_INVALID_POLICY = -16, + TCPM_ERROR_EXPECT_CB2 = -17, + TCPM_ERROR_POWER_ROLE = -18, + TCPM_ERROR_PE_NOT_READY = -19, + TCPM_ERROR_REPEAT_POLICY = -20, + TCPM_ERROR_CUSTOM_SRC = -21, + TCPM_ERROR_NO_IMPLEMENT = -22, + TCPM_ALERT = -23, +}; + +/* Inquire TCPM status */ + +enum tcpc_cc_voltage_status { + TYPEC_CC_VOLT_OPEN = 0, + TYPEC_CC_VOLT_RA = 1, + TYPEC_CC_VOLT_RD = 2, + + TYPEC_CC_VOLT_SNK_DFT = 5, + TYPEC_CC_VOLT_SNK_1_5 = 6, + TYPEC_CC_VOLT_SNK_3_0 = 7, + + TYPEC_CC_DRP_TOGGLING = 15, +}; + +enum tcpm_vbus_level { +#ifdef CONFIG_TCPC_VSAFE0V_DETECT + TCPC_VBUS_SAFE0V = 0, /* < 0.8V */ + TCPC_VBUS_INVALID, /* > 0.8V */ + TCPC_VBUS_VALID, /* > 4.5V */ +#else + TCPC_VBUS_INVALID = 0, + TCPC_VBUS_VALID, +#endif /* CONFIG_TCPC_VSAFE0V_DETECT */ +}; + +enum typec_role_defination { + TYPEC_ROLE_UNKNOWN = 0, + TYPEC_ROLE_SNK, + TYPEC_ROLE_SRC, + TYPEC_ROLE_DRP, + TYPEC_ROLE_TRY_SRC, + TYPEC_ROLE_TRY_SNK, + TYPEC_ROLE_NR, +}; + +enum pd_cable_current_limit { + PD_CABLE_CURR_UNKNOWN = 0, + PD_CABLE_CURR_1A5 = 1, + PD_CABLE_CURR_3A = 2, + PD_CABLE_CURR_5A = 3, +}; + +/* DPM Flags */ + +#define DPM_FLAGS_PARTNER_DR_POWER (1<<0) +#define DPM_FLAGS_PARTNER_DR_DATA (1<<1) +#define DPM_FLAGS_PARTNER_EXTPOWER (1<<2) +#define DPM_FLAGS_PARTNER_USB_COMM (1<<3) +#define DPM_FLAGS_PARTNER_USB_SUSPEND (1<<4) +#define DPM_FLAGS_PARTNER_HIGH_CAP (1<<5) + +#define DPM_FLAGS_PARTNER_MISMATCH (1<<7) +#define DPM_FLAGS_PARTNER_GIVE_BACK (1<<8) +#define DPM_FLAGS_PARTNER_NO_SUSPEND (1<<9) + +#define DPM_FLAGS_RESET_PARTNER_MASK \ + (DPM_FLAGS_PARTNER_DR_POWER | DPM_FLAGS_PARTNER_DR_DATA |\ + DPM_FLAGS_PARTNER_EXTPOWER | DPM_FLAGS_PARTNER_USB_COMM |\ + DPM_FLAGS_PARTNER_USB_SUSPEND) + +/* DPM_CAPS */ + +#define DPM_CAP_LOCAL_DR_POWER (1<<0) +#define DPM_CAP_LOCAL_DR_DATA (1<<1) +#define DPM_CAP_LOCAL_EXT_POWER (1<<2) +#define DPM_CAP_LOCAL_USB_COMM (1<<3) +#define DPM_CAP_LOCAL_USB_SUSPEND (1<<4) +#define DPM_CAP_LOCAL_HIGH_CAP (1<<5) +#define DPM_CAP_LOCAL_GIVE_BACK (1<<6) +#define DPM_CAP_LOCAL_NO_SUSPEND (1<<7) +#define DPM_CAP_LOCAL_VCONN_SUPPLY (1<<8) + +#define DPM_CAP_ATTEMP_ENTER_DC_MODE (1<<11) +#define DPM_CAP_ATTEMP_DISCOVER_CABLE_DFP (1<<12) +#define DPM_CAP_ATTEMP_ENTER_DP_MODE (1<<13) +#define DPM_CAP_ATTEMP_DISCOVER_CABLE (1<<14) +#define DPM_CAP_ATTEMP_DISCOVER_ID (1<<15) +#define DPM_CAP_ATTEMP_DISCOVER_SVID (1<<16) + +enum dpm_cap_pr_check_prefer { + DPM_CAP_PR_CHECK_DISABLE = 0, + DPM_CAP_PR_CHECK_PREFER_SNK = 1, + DPM_CAP_PR_CHECK_PREFER_SRC = 2, +}; + +#define DPM_CAP_PR_CHECK_PROP(cap) ((cap & 0x03) << 18) +#define DPM_CAP_EXTRACT_PR_CHECK(raw) ((raw >> 18) & 0x03) +#define DPM_CAP_PR_SWAP_REJECT_AS_SRC (1<<20) +#define DPM_CAP_PR_SWAP_REJECT_AS_SNK (1<<21) +#define DPM_CAP_PR_SWAP_CHECK_GP_SRC (1<<22) +#define DPM_CAP_PR_SWAP_CHECK_GP_SNK (1<<23) +#define DPM_CAP_PR_SWAP_CHECK_GOOD_POWER \ + (DPM_CAP_PR_SWAP_CHECK_GP_SRC | DPM_CAP_PR_SWAP_CHECK_GP_SNK) + +#define DPM_CAP_CHECK_EXT_POWER \ + (DPM_CAP_LOCAL_EXT_POWER | DPM_CAP_PR_SWAP_CHECK_GOOD_POWER) + +enum dpm_cap_dr_check_prefer { + DPM_CAP_DR_CHECK_DISABLE = 0, + DPM_CAP_DR_CHECK_PREFER_UFP = 1, + DPM_CAP_DR_CHECK_PREFER_DFP = 2, +}; + +#define DPM_CAP_DR_CHECK_PROP(cap) ((cap & 0x03) << 22) +#define DPM_CAP_EXTRACT_DR_CHECK(raw) ((raw >> 22) & 0x03) +#define DPM_CAP_DR_SWAP_REJECT_AS_DFP (1<<24) +#define DPM_CAP_DR_SWAP_REJECT_AS_UFP (1<<25) + +#define DPM_CAP_DP_PREFER_MF (1<<29) + + +/* Power Data Object related structure */ + +struct tcpm_power_cap { + uint8_t cnt; + uint32_t pdos[PDO_MAX_NR]; +}; + +struct tcpm_remote_power_cap { + uint8_t selected_cap_idx; + uint8_t nr; + int max_mv[PDO_MAX_NR]; + int min_mv[PDO_MAX_NR]; + int ma[PDO_MAX_NR]; + uint8_t type[PDO_MAX_NR]; +}; + +enum tcpm_power_cap_val_type { + TCPM_POWER_CAP_VAL_TYPE_FIXED = 0, + TCPM_POWER_CAP_VAL_TYPE_BATTERY = 1, + TCPM_POWER_CAP_VAL_TYPE_VARIABLE = 2, + TCPM_POWER_CAP_VAL_TYPE_AUGMENT = 3, + + TCPM_POWER_CAP_VAL_TYPE_UNKNOWN = 0xff, +}; + +#define TCPM_APDO_TYPE_MASK (0x0f) + +enum tcpm_power_cap_apdo_type { + TCPM_POWER_CAP_APDO_TYPE_PPS = 1 << 0, + + TCPM_POWER_CAP_APDO_TYPE_PPS_CF = (1 << 7), +}; + +struct tcpm_power_cap_val { + uint8_t type; + uint8_t apdo_type; + uint8_t pwr_limit; + + int max_mv; + int min_mv; + + union { + int uw; + int ma; + }; +}; + +struct tcpm_power_cap_list { + uint8_t nr; + struct tcpm_power_cap_val cap_val[PDO_MAX_NR]; +}; + +/* Request TCPM to execure PD/VDM function */ + +struct tcp_dpm_event; + +enum tcp_dpm_return_code { + TCP_DPM_RET_SUCCESS = 0, + TCP_DPM_RET_SENT = 0, + TCP_DPM_RET_VDM_ACK = 0, + + TCP_DPM_RET_DENIED_UNKNOWN, + TCP_DPM_RET_DENIED_NOT_READY, + TCP_DPM_RET_DENIED_LOCAL_CAP, + TCP_DPM_RET_DENIED_PARTNER_CAP, + TCP_DPM_RET_DENIED_SAME_ROLE, + TCP_DPM_RET_DENIED_INVALID_REQUEST, + TCP_DPM_RET_DENIED_REPEAT_REQUEST, + TCP_DPM_RET_DENIED_WRONG_DATA_ROLE, + TCP_DPM_RET_DENIED_PD_REV, + + TCP_DPM_RET_DROP_CC_DETACH, + TCP_DPM_RET_DROP_SENT_SRESET, + TCP_DPM_RET_DROP_RECV_SRESET, + TCP_DPM_RET_DROP_SENT_HRESET, + TCP_DPM_RET_DROP_RECV_HRESET, + TCP_DPM_RET_DROP_ERROR_REOCVERY, + TCP_DPM_RET_DROP_SEND_BIST, + TCP_DPM_RET_DROP_PE_BUSY, /* SinkTXNg*/ + + TCP_DPM_RET_WAIT, + TCP_DPM_RET_REJECT, + TCP_DPM_RET_TIMEOUT, + TCP_DPM_RET_VDM_NAK, + TCP_DPM_RET_NOT_SUPPORT, + + TCP_DPM_RET_BK_TIMEOUT, + TCP_DPM_RET_NO_RESPONSE, + + TCP_DPM_RET_NR, +}; + +enum TCP_DPM_EVT_ID { + TCP_DPM_EVT_UNKONW = 0, + + TCP_DPM_EVT_PD_COMMAND, + + TCP_DPM_EVT_PR_SWAP_AS_SNK = TCP_DPM_EVT_PD_COMMAND, + TCP_DPM_EVT_PR_SWAP_AS_SRC, + TCP_DPM_EVT_DR_SWAP_AS_UFP, + TCP_DPM_EVT_DR_SWAP_AS_DFP, + TCP_DPM_EVT_VCONN_SWAP_OFF, + TCP_DPM_EVT_VCONN_SWAP_ON, + TCP_DPM_EVT_GOTOMIN, + + TCP_DPM_EVT_SOFTRESET, + TCP_DPM_EVT_CABLE_SOFTRESET, + + TCP_DPM_EVT_GET_SOURCE_CAP, + TCP_DPM_EVT_GET_SINK_CAP, + + TCP_DPM_EVT_REQUEST, + TCP_DPM_EVT_REQUEST_EX, + TCP_DPM_EVT_REQUEST_AGAIN, + TCP_DPM_EVT_BIST_CM2, + + TCP_DPM_EVT_DUMMY, /* wakeup event thread */ + +#ifdef CONFIG_USB_PD_REV30 + TCP_DPM_EVT_PD30_COMMAND, + TCP_DPM_EVT_GET_SOURCE_CAP_EXT = TCP_DPM_EVT_PD30_COMMAND, + TCP_DPM_EVT_GET_STATUS, + TCP_DPM_EVT_FR_SWAP_AS_SINK, + TCP_DPM_EVT_FR_SWAP_AS_SOURCE, + TCP_DPM_EVT_GET_COUNTRY_CODE, + TCP_DPM_EVT_GET_PPS_STATUS, + + TCP_DPM_EVT_ALERT, + TCP_DPM_EVT_GET_COUNTRY_INFO, + + TCP_DPM_EVT_GET_BAT_CAP, + TCP_DPM_EVT_GET_BAT_STATUS, + TCP_DPM_EVT_GET_MFRS_INFO, +#endif /* CONFIG_USB_PD_REV30 */ + + TCP_DPM_EVT_VDM_COMMAND, + TCP_DPM_EVT_DISCOVER_CABLE = TCP_DPM_EVT_VDM_COMMAND, + TCP_DPM_EVT_DISCOVER_ID, + TCP_DPM_EVT_DISCOVER_SVIDS, + TCP_DPM_EVT_DISCOVER_MODES, + TCP_DPM_EVT_ENTER_MODE, + TCP_DPM_EVT_EXIT_MODE, + TCP_DPM_EVT_ATTENTION, + +#ifdef CONFIG_USB_PD_ALT_MODE + TCP_DPM_EVT_DP_ATTENTION, +#ifdef CONFIG_USB_PD_ALT_MODE_DFP + TCP_DPM_EVT_DP_STATUS_UPDATE, + TCP_DPM_EVT_DP_CONFIG, +#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ +#endif /* CONFIG_USB_PD_ALT_MODE */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM + TCP_DPM_EVT_UVDM, +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + + TCP_DPM_EVT_IMMEDIATELY, + TCP_DPM_EVT_HARD_RESET = TCP_DPM_EVT_IMMEDIATELY, + TCP_DPM_EVT_ERROR_RECOVERY, + + TCP_DPM_EVT_NR, +}; + +typedef int (*tcp_dpm_event_cb)( + struct tcpc_device *tcpc, int ret, struct tcp_dpm_event *event); + +struct tcp_dpm_event_cb_data { + void *user_data; + tcp_dpm_event_cb event_cb; +}; + +static const struct tcp_dpm_event_cb_data tcp_dpm_evt_cb_null = { + .user_data = NULL, + .event_cb = NULL, +}; + +struct tcp_dpm_new_role { + uint8_t new_role; +}; + +struct tcp_dpm_pd_request { + int mv; + int ma; +}; + +struct tcp_dpm_pd_request_ex { + uint8_t pos; + + union { + uint32_t max; + uint32_t max_uw; + uint32_t max_ma; + }; + + union { + uint32_t oper; + uint32_t oper_uw; + uint32_t oper_ma; + }; +}; + +struct tcp_dpm_svdm_data { + uint16_t svid; + uint8_t ops; +}; + +struct tcp_dpm_dp_data { + uint32_t val; + uint32_t mask; +}; + +struct tcp_dpm_custom_vdm_data { + bool wait_resp; + uint8_t cnt; + uint32_t vdos[PD_DATA_OBJ_SIZE]; +}; + +struct tcp_dpm_event { + uint8_t event_id; + void *user_data; + tcp_dpm_event_cb event_cb; + + union { + struct tcp_dpm_pd_request pd_req; + struct tcp_dpm_pd_request_ex pd_req_ex; + + + struct tcp_dpm_dp_data dp_data; + struct tcp_dpm_custom_vdm_data vdm_data; + + struct tcp_dpm_svdm_data svdm_data; + + + struct pd_get_battery_capabilities gbcdb; + struct pd_get_battery_status gbsdb; + struct pd_get_manufacturer_info gmidb; + + uint32_t index; + uint32_t data_object[PD_DATA_OBJ_SIZE]; + } tcp_dpm_data; +}; + +/* KEEP_SVID */ + +struct tcpm_svid_list { + uint8_t cnt; + uint16_t svids[VDO_MAX_SVID_NR]; +}; + +struct tcpm_mode_list { + uint8_t cnt; + uint32_t modes[VDO_MAX_NR]; +}; + +/* ALT_DP */ + +enum pd_dp_ufp_u_state { + DP_UFP_U_NONE = 0, + DP_UFP_U_STARTUP, + DP_UFP_U_WAIT, + DP_UFP_U_OPERATION, + DP_UFP_U_STATE_NR, + + DP_UFP_U_ERR = 0X10, +}; + +enum pd_dp_dfp_u_state { + DP_DFP_U_NONE = 0, + DP_DFP_U_DISCOVER_ID, + DP_DFP_U_DISCOVER_SVIDS, + DP_DFP_U_DISCOVER_MODES, + DP_DFP_U_ENTER_MODE, + DP_DFP_U_STATUS_UPDATE, + DP_DFP_U_WAIT_ATTENTION, + DP_DFP_U_CONFIGURE, + DP_DFP_U_OPERATION, + DP_DFP_U_STATE_NR, + + DP_DFP_U_ERR = 0X10, + + DP_DFP_U_ERR_DISCOVER_ID_TYPE, + DP_DFP_U_ERR_DISCOVER_ID_NAK_TIMEOUT, + + DP_DFP_U_ERR_DISCOVER_SVID_DP_SID, + DP_DFP_U_ERR_DISCOVER_SVID_NAK_TIMEOUT, + + DP_DFP_U_ERR_DISCOVER_MODE_DP_SID, + DP_DFP_U_ERR_DISCOVER_MODE_CAP, /* NO SUPPORT UFP-D */ + DP_DFP_U_ERR_DISCOVER_MODE_NAK_TIMEROUT, + + DP_DFP_U_ERR_ENTER_MODE_DP_SID, + DP_DFP_U_ERR_ENTER_MODE_NAK_TIMEOUT, + + DP_DFP_U_ERR_EXIT_MODE_DP_SID, + DP_DFP_U_ERR_EXIT_MODE_NAK_TIMEOUT, + + DP_DFP_U_ERR_STATUS_UPDATE_DP_SID, + DP_DFP_U_ERR_STATUS_UPDATE_NAK_TIMEOUT, + DP_DFP_U_ERR_STATUS_UPDATE_ROLE, + + DP_DFP_U_ERR_CONFIGURE_SELECT_MODE, +}; + +/* Custom VDM */ + +#define PD_UVDM_HDR(vid, custom) \ + (((vid) << 16) | ((custom) & 0x7FFF)) + +#define PD_SVDM_HDR(vid, custom) \ + (((vid) << 16) | (1<<15) | ((custom) & 0x1FDF)) + +#define PD_UVDM_HDR_CMD(hdr) \ + (hdr & 0x7FFF) + +#define PD_SVDM_HDR_CMD(hdr) \ + (hdr & 0x1FDF) + +#define DPM_CHARGING_POLICY_MASK (0x0f) + +/* Charging Policy */ + +enum dpm_charging_policy { + /* VSafe5V only */ + DPM_CHARGING_POLICY_VSAFE5V = 0, + + /* Max Power */ + DPM_CHARGING_POLICY_MAX_POWER = 1, + + /* Custom defined Policy */ + DPM_CHARGING_POLICY_CUSTOM = 2, + + /* Runtime Policy, restore to default after plug-out or hard-reset */ + DPM_CHARGING_POLICY_RUNTIME = 3, + + /* Direct charge */ + DPM_CHARGING_POLICY_DIRECT_CHARGE = 3, + + /* PPS */ + DPM_CHARGING_POLICY_PPS = 4, + + /* Default Charging Policy */ + DPM_CHARGING_POLICY_DEFAULT = 0xff, + + DPM_CHARGING_POLICY_IGNORE_MISMATCH_CURR = 1 << 4, + DPM_CHARGING_POLICY_PREFER_LOW_VOLTAGE = 1 << 5, + DPM_CHARGING_POLICY_PREFER_HIGH_VOLTAGE = 1 << 6, + + DPM_CHARGING_POLICY_MAX_POWER_LV = + DPM_CHARGING_POLICY_MAX_POWER | + DPM_CHARGING_POLICY_PREFER_LOW_VOLTAGE, + DPM_CHARGING_POLICY_MAX_POWER_LVIC = + DPM_CHARGING_POLICY_MAX_POWER_LV | + DPM_CHARGING_POLICY_IGNORE_MISMATCH_CURR, + + DPM_CHARGING_POLICY_MAX_POWER_HV = + DPM_CHARGING_POLICY_MAX_POWER | + DPM_CHARGING_POLICY_PREFER_HIGH_VOLTAGE, + DPM_CHARGING_POLICY_MAX_POWER_HVIC = + DPM_CHARGING_POLICY_MAX_POWER_HV | + DPM_CHARGING_POLICY_IGNORE_MISMATCH_CURR, + + /* DPM_CHARGING_POLICY_PPS */ + + DPM_CHARGING_POLICY_PPS_IC = + DPM_CHARGING_POLICY_PPS | + DPM_CHARGING_POLICY_IGNORE_MISMATCH_CURR, +}; + +#if IS_ENABLED(CONFIG_TCPC_CLASS) + +extern struct tcpc_device + *tcpc_dev_get_by_name(const char *name); + +extern int register_tcp_dev_notifier(struct tcpc_device *tcp_dev, + struct notifier_block *nb, uint8_t flags); +extern int unregister_tcp_dev_notifier(struct tcpc_device *tcp_dev, + struct notifier_block *nb, uint8_t flags); + +extern int tcpm_shutdown(struct tcpc_device *tcpc); + +extern int tcpm_inquire_remote_cc(struct tcpc_device *tcpc, + uint8_t *cc1, uint8_t *cc2, bool from_ic); +extern int tcpm_inquire_vbus_level(struct tcpc_device *tcpc, bool from_ic); +extern int tcpm_inquire_typec_remote_rp_curr(struct tcpc_device *tcpc); +extern bool tcpm_inquire_cc_polarity(struct tcpc_device *tcpc); +extern uint8_t tcpm_inquire_typec_attach_state(struct tcpc_device *tcpc); +extern uint8_t tcpm_inquire_typec_role(struct tcpc_device *tcpc); +extern uint8_t tcpm_inquire_typec_local_rp(struct tcpc_device *tcpc); + +extern int tcpm_typec_set_usb_sink_curr( + struct tcpc_device *tcpc, int curr); + +extern int tcpm_typec_set_rp_level( + struct tcpc_device *tcpc, uint8_t level); + +extern int tcpm_typec_set_custom_hv( + struct tcpc_device *tcpc, bool en); + +extern int tcpm_typec_role_swap( + struct tcpc_device *tcpc); + +extern int tcpm_typec_change_role( + struct tcpc_device *tcpc, uint8_t typec_role); + +extern int tcpm_typec_change_mode( + struct tcpc_device *tcpc, uint8_t mode); + +extern int tcpm_typec_change_role_postpone( + struct tcpc_device *tcpc, uint8_t typec_role, bool postpone); + +extern int tcpm_typec_error_recovery(struct tcpc_device *tcpc); + +extern int tcpm_typec_disable_function( + struct tcpc_device *tcpc, bool disable); + +#ifdef CONFIG_USB_POWER_DELIVERY + +extern bool tcpm_inquire_pd_connected( + struct tcpc_device *tcpc); + +extern bool tcpm_inquire_pd_prev_connected( + struct tcpc_device *tcpc); + +extern uint8_t tcpm_inquire_pd_data_role( + struct tcpc_device *tcpc); + +extern uint8_t tcpm_inquire_pd_power_role( + struct tcpc_device *tcpc); + +extern uint8_t tcpm_inquire_pd_vconn_role( + struct tcpc_device *tcpc); + +extern uint8_t tcpm_inquire_pd_pe_ready( + struct tcpc_device *tcpc); + +extern uint8_t tcpm_inquire_cable_current( + struct tcpc_device *tcpc); + +extern uint32_t tcpm_inquire_dpm_flags( + struct tcpc_device *tcpc); + +extern uint32_t tcpm_inquire_dpm_caps( + struct tcpc_device *tcpc); + +extern void tcpm_set_dpm_caps( + struct tcpc_device *tcpc, uint32_t caps); + +/* Request TCPM to send PD Request */ + +extern int tcpm_put_tcp_dpm_event( + struct tcpc_device *tcpc, struct tcp_dpm_event *event); + +/* TCPM DPM PD I/F */ + +extern int tcpm_inquire_pd_contract( + struct tcpc_device *tcpc, int *mv, int *ma); +extern int tcpm_inquire_cable_inform( + struct tcpc_device *tcpc, uint32_t *vdos); +extern int tcpm_inquire_pd_partner_inform( + struct tcpc_device *tcpc, uint32_t *vdos); +extern int tcpm_inquire_pd_partner_svids( + struct tcpc_device *tcpc, struct tcpm_svid_list *list); +extern int tcpm_inquire_pd_partner_modes( + struct tcpc_device *tcpc, uint16_t svid, struct tcpm_mode_list *list); +extern int tcpm_inquire_pd_source_cap( + struct tcpc_device *tcpc, struct tcpm_power_cap *cap); +extern int tcpm_inquire_pd_sink_cap( + struct tcpc_device *tcpc, struct tcpm_power_cap *cap); + +extern bool tcpm_extract_power_cap_val( + uint32_t pdo, struct tcpm_power_cap_val *cap); + +extern bool tcpm_extract_power_cap_list( + struct tcpm_power_cap *cap, struct tcpm_power_cap_list *cap_list); + +extern int tcpm_get_remote_power_cap(struct tcpc_device *tcpc, + struct tcpm_remote_power_cap *cap); + +extern int tcpm_inquire_select_source_cap( + struct tcpc_device *tcpc, struct tcpm_power_cap_val *cap_val); + +/* Request TCPM to send PD Request */ + +extern int tcpm_dpm_pd_power_swap(struct tcpc_device *tcpc, + uint8_t role, const struct tcp_dpm_event_cb_data *data); +extern int tcpm_dpm_pd_data_swap(struct tcpc_device *tcpc, + uint8_t role, const struct tcp_dpm_event_cb_data *data); +extern int tcpm_dpm_pd_vconn_swap(struct tcpc_device *tcpc, + uint8_t role, const struct tcp_dpm_event_cb_data *data); +extern int tcpm_dpm_pd_goto_min(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data); +extern int tcpm_dpm_pd_soft_reset(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data); +extern int tcpm_dpm_pd_get_source_cap(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data); +extern int tcpm_dpm_pd_get_sink_cap(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data); +extern int tcpm_dpm_pd_request(struct tcpc_device *tcpc, + int mv, int ma, const struct tcp_dpm_event_cb_data *data); +extern int tcpm_dpm_pd_request_ex(struct tcpc_device *tcpc, + uint8_t pos, uint32_t max, uint32_t oper, + const struct tcp_dpm_event_cb_data *data); +extern int tcpm_dpm_pd_bist_cm2(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data); + +#ifdef CONFIG_USB_PD_REV30 +extern int tcpm_dpm_pd_get_source_cap_ext(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data, + struct pd_source_cap_ext *src_cap_ext); +extern int tcpm_dpm_pd_fast_swap(struct tcpc_device *tcpc, + uint8_t role, const struct tcp_dpm_event_cb_data *data); +extern int tcpm_dpm_pd_get_status(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data, struct pd_status *status); +extern int tcpm_dpm_pd_get_pps_status_raw(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *cb_data, + struct pd_pps_status_raw *pps_status); +extern int tcpm_dpm_pd_get_pps_status(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data, + struct pd_pps_status *pps_status); +extern int tcpm_dpm_pd_get_country_code(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data, + struct pd_country_codes *ccdb); +extern int tcpm_dpm_pd_get_country_info(struct tcpc_device *tcpc, + uint32_t ccdo, const struct tcp_dpm_event_cb_data *data, + struct pd_country_info *cidb); +extern int tcpm_dpm_pd_get_bat_cap(struct tcpc_device *tcpc, + struct pd_get_battery_capabilities *gbcdb, + const struct tcp_dpm_event_cb_data *data, + struct pd_battery_capabilities *bcdb); +extern int tcpm_dpm_pd_get_bat_status(struct tcpc_device *tcpc, + struct pd_get_battery_status *gbsdb, + const struct tcp_dpm_event_cb_data *data, uint32_t *bsdo); +extern int tcpm_dpm_pd_get_mfrs_info(struct tcpc_device *tcpc, + struct pd_get_manufacturer_info *gmidb, + const struct tcp_dpm_event_cb_data *data, + struct pd_manufacturer_info *midb); +extern int tcpm_dpm_pd_alert(struct tcpc_device *tcpc, + uint32_t ado, const struct tcp_dpm_event_cb_data *data); +#endif /* CONFIG_USB_PD_REV30 */ + +extern int tcpm_dpm_pd_hard_reset(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data); +extern int tcpm_dpm_pd_error_recovery(struct tcpc_device *tcpc); + +/* Request TCPM to send SOP' request */ + +extern int tcpm_dpm_pd_cable_soft_reset( + struct tcpc_device *tcpc, const struct tcp_dpm_event_cb_data *data); +extern int tcpm_dpm_vdm_discover_cable( + struct tcpc_device *tcpc, const struct tcp_dpm_event_cb_data *data); + +/* Request TCPM to send VDM request */ + +extern int tcpm_dpm_vdm_discover_id( + struct tcpc_device *tcpc, const struct tcp_dpm_event_cb_data *data); +extern int tcpm_dpm_vdm_discover_svid( + struct tcpc_device *tcpc, const struct tcp_dpm_event_cb_data *data); +extern int tcpm_dpm_vdm_discover_mode(struct tcpc_device *tcpc, + uint16_t svid, const struct tcp_dpm_event_cb_data *data); +extern int tcpm_dpm_vdm_enter_mode(struct tcpc_device *tcpc, + uint16_t svid, uint8_t ops, const struct tcp_dpm_event_cb_data *data); +extern int tcpm_dpm_vdm_exit_mode(struct tcpc_device *tcpc, + uint16_t svid, uint8_t ops, const struct tcp_dpm_event_cb_data *data); +extern int tcpm_dpm_vdm_attention(struct tcpc_device *tcpc, + uint16_t svid, uint8_t ops, const struct tcp_dpm_event_cb_data *data); + +/* Request TCPM to send DP Request */ + +#ifdef CONFIG_USB_PD_ALT_MODE + +extern int tcpm_inquire_dp_ufp_u_state( + struct tcpc_device *tcpc, uint8_t *state); + +extern int tcpm_dpm_dp_attention(struct tcpc_device *tcpc, + uint32_t dp_status, uint32_t mask, + const struct tcp_dpm_event_cb_data *data); + +#ifdef CONFIG_USB_PD_ALT_MODE_DFP + +extern int tcpm_inquire_dp_dfp_u_state( + struct tcpc_device *tcpc, uint8_t *state); + +extern int tcpm_dpm_dp_status_update(struct tcpc_device *tcpc, + uint32_t dp_status, uint32_t mask, + const struct tcp_dpm_event_cb_data *data); + +extern int tcpm_dpm_dp_config(struct tcpc_device *tcpc, + uint32_t dp_config, uint32_t mask, + const struct tcp_dpm_event_cb_data *data); +#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ +#endif /* CONFIG_USB_PD_ALT_MODE */ + +/* Request TCPM to send PD-UVDM Request */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM + +extern int tcpm_dpm_send_custom_vdm( + struct tcpc_device *tcpc, + struct tcp_dpm_custom_vdm_data *vdm_data, + const struct tcp_dpm_event_cb_data *cb_data); + +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + +/* Notify TCPM */ + +extern int tcpm_notify_vbus_stable(struct tcpc_device *tcpc); + +/* Charging Policy: Select PDO */ + +extern int tcpm_reset_pd_charging_policy(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data); + +extern int tcpm_set_pd_charging_policy(struct tcpc_device *tcpc, + uint8_t policy, const struct tcp_dpm_event_cb_data *data); + +extern int tcpm_set_pd_charging_policy_default( + struct tcpc_device *tcpc, uint8_t policy); + +extern uint8_t tcpm_inquire_pd_charging_policy(struct tcpc_device *tcpc); +extern uint8_t tcpm_inquire_pd_charging_policy_default( + struct tcpc_device *tcpc); + +#ifdef CONFIG_USB_PD_DIRECT_CHARGE +extern int tcpm_set_direct_charge_en(struct tcpc_device *tcpc, bool en); +extern bool tcpm_inquire_during_direct_charge(struct tcpc_device *tcpc); +#endif /* CONFIG_USB_PD_DIRECT_CHARGE */ + + +#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE +extern int tcpm_dpm_set_vconn_supply_mode( + struct tcpc_device *tcpc, uint8_t mode); +#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */ + + +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_PPS_SINK +extern int tcpm_set_apdo_charging_policy( + struct tcpc_device *tcpc, uint8_t policy, int mv, int ma, + const struct tcp_dpm_event_cb_data *data); +extern int tcpm_inquire_pd_source_apdo(struct tcpc_device *tcpc, + uint8_t apdo_type, uint8_t *cap_i, struct tcpm_power_cap_val *cap); +extern bool tcpm_inquire_during_pps_charge(struct tcpc_device *tcpc); +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + +#ifdef CONFIG_USB_PD_REV30_BAT_INFO + +/** + * tcpm_update_bat_status + * + * Update current capacity and charging status of the specified battery + * + * If the battery's real capacity in not known, + * all batteries's capacity can be updated with SoC. + * + * If the battery status is only update when a noticiation is received, + * using no_mutex version, otherwise it will cause deadlock. + * + * This function may trigger DPM to send alert message + * + * @ ref : Specifies which battery to update + * @ status : refer to BSDO_BAT_INFO, idle, charging, discharging + * @ wh : current capacity, unit 1/10 wh + * @ soc : current soc, unit: 0.1 % + * + */ + +extern int tcpm_update_bat_status_wh(struct tcpc_device *tcpc, + enum pd_battery_reference ref, uint8_t status, uint16_t wh); + +extern int tcpm_update_bat_status_wh_no_mutex(struct tcpc_device *tcpc, + enum pd_battery_reference ref, uint8_t status, uint16_t wh); + +extern int tcpm_update_bat_status_soc(struct tcpc_device *tcpc, + enum pd_battery_reference ref, uint8_t status, uint16_t soc); + +extern int tcpm_update_bat_status_soc_no_mutex(struct tcpc_device *tcpc, + enum pd_battery_reference ref, uint8_t status, uint16_t soc); + +/** + * tcpm_update_bat_last_full + * + * Update last full capacity of the specified battery + * + * If the battery status is only update when a noticiation is received, + * using no_mutex version, otherwise it will cause deadlock. + * + * @ ref : Specifies which battery to update + * @ wh : current capacity, unit 1/10 wh + * + */ + +extern int tcpm_update_bat_last_full(struct tcpc_device *tcpc, + enum pd_battery_reference ref, uint16_t wh); + +extern int tcpm_update_bat_last_full_no_mutex(struct tcpc_device *tcpc, + enum pd_battery_reference ref, uint16_t wh); + +#endif /* CONFIG_USB_PD_REV30_BAT_INFO */ + + +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL + +/** + * tcpm_update_pd_status + * + * Update local status: temperature, power input, and OT/OC/OV event. + * + * This function may trigger DPM to send alert message + * + * @ ptf : + * Present Temperature Flag + * @ temperature : + * 0 = feature not supported + * 1 = temperature is less than 2 degree c. + * 2-255 = temperature in degree c. + * + * @ input/input_mask : + * refer to pd_status.present_input + * + * @ bat_in / bat_in_mask : + * refer to pd_status.present_battey_input + * This function will auto_update INT_POWER_BAT + * + * @ evt : + * refer to pd_status.event_flags + */ + +extern int tcpm_update_pd_status_temp(struct tcpc_device *tcpc, + enum pd_present_temperature_flag ptf, uint8_t temperature); + +extern int tcpm_update_pd_status_input( + struct tcpc_device *tcpc, uint8_t input, uint8_t mask); + +extern int tcpm_update_pd_status_bat_input( + struct tcpc_device *tcpc, uint8_t bat_input, uint8_t bat_mask); + +extern int tcpm_update_pd_status_event( + struct tcpc_device *tcpc, uint8_t evt); + +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ + + +#endif /* CONFIG_USB_PD_REV30 */ + +#endif /* CONFIG_USB_POWER_DELIVERY */ +#endif /* CONFIG_TCPC_CLASS */ + +/* Empty function if configuration not defined */ + +#define TCPC_CLASS_NA +#define USB_POWER_DELIVERY_NA +#define USB_PD_REV30_NA +#define USB_PD_ALT_MODE_NA +#define USB_PD_ALT_MODE_DFP_NA +#define USB_PD_CUSTOM_VDM_NA +#define USB_PD_DIRECT_CHARGE_NA +#define TCPC_VCONN_SUPPLY_MODE_NA +#define USB_PD_REV30_PPS_SINK_NA +#define USB_PD_REV30_BAT_INFO_NA +#define USB_PD_REV30_STATUS_NA + +#if IS_ENABLED(CONFIG_TCPC_CLASS) +#undef TCPC_CLASS_NA + +#ifdef CONFIG_USB_POWER_DELIVERY +#undef USB_POWER_DELIVERY_NA + +#ifdef CONFIG_USB_PD_REV30 +#undef USB_PD_REV30_NA +#ifdef CONFIG_USB_PD_REV30_PPS_SINK +#undef USB_PD_REV30_PPS_SINK_NA +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ +#ifdef CONFIG_USB_PD_REV30_BAT_INFO +#undef USB_PD_REV30_BAT_INFO_NA +#endif /* CONFIG_USB_PD_REV30_BAT_INFO */ +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL +#undef USB_PD_REV30_STATUS_NA +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ +#endif /* CONFIG_USB_PD_REV30 */ + +#ifdef CONFIG_USB_PD_ALT_MODE +#undef USB_PD_ALT_MODE_NA +#ifdef CONFIG_USB_PD_ALT_MODE_DFP +#undef USB_PD_ALT_MODE_DFP_NA +#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ +#endif /* CONFIG_USB_PD_ALT_MODE */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM +#undef USB_PD_CUSTOM_VDM_NA +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + +#ifdef CONFIG_USB_PD_DIRECT_CHARGE +#undef USB_PD_DIRECT_CHARGE_NA +#endif /* CONFIG_USB_PD_DIRECT_CHARGE */ + +#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE +#undef TCPC_VCONN_SUPPLY_MODE_NA +#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */ + +#endif /* CONFIG_USB_POWER_DELIVERY */ +#endif /* CONFIG_TCPC_CLASS */ + +#ifdef TCPC_CLASS_NA + +static inline struct tcpc_device + *tcpc_dev_get_by_name(const char *name) +{ + return NULL; +} + +static inline int register_tcp_dev_notifier(struct tcpc_device *tcp_dev, + struct notifier_block *nb, uint8_t flags) +{ + return -ENODEV; +} + +static inline int unregister_tcp_dev_notifier(struct tcpc_device *tcp_dev, + struct notifier_block *nb, uint8_t flags) +{ + return -ENODEV; +} + +static inline int tcpm_shutdown(struct tcpc_device *tcpc) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_inquire_remote_cc(struct tcpc_device *tcpc, + uint8_t *cc1, uint8_t *cc2, bool from_ic) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_inquire_vbus_level( + struct tcpc_device *tcpc, bool from_ic) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_inquire_typec_remote_rp_curr( + struct tcpc_device *tcpc) +{ + return 0; +} + +static inline bool tcpm_inquire_cc_polarity(struct tcpc_device *tcpc) +{ + return false; +} + +static inline uint8_t tcpm_inquire_typec_attach_state( + struct tcpc_device *tcpc) +{ + return TYPEC_UNATTACHED; +} + +static inline uint8_t tcpm_inquire_typec_role(struct tcpc_device *tcpc) +{ + return TYPEC_ROLE_UNKNOWN; +} + +static inline uint8_t tcpm_inquire_typec_local_rp(struct tcpc_device *tcpc) +{ + return 0; +} + +static inline int tcpm_typec_set_wake_lock( + struct tcpc_device *tcpc, bool user_lock) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_typec_set_usb_sink_curr( + struct tcpc_device *tcpc, int curr) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_typec_set_rp_level( + struct tcpc_device *tcpc, uint8_t level) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_typec_set_custom_hv( + struct tcpc_device *tcpc, bool en) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_typec_role_swap( + struct tcpc_device *tcpc) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_typec_change_role( + struct tcpc_device *tcpc, uint8_t typec_role) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_typec_change_mode( + struct tcpc_device *tcpc, uint8_t mode) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_typec_change_role_postpone( + struct tcpc_device *tcpc, uint8_t typec_role, bool postpone) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_typec_error_recovery(struct tcpc_device *tcpc) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} +#endif /* TCPC_CLASS_NA */ + +#ifdef USB_POWER_DELIVERY_NA + +static inline bool tcpm_inquire_pd_connected( + struct tcpc_device *tcpc) +{ + return false; +} + +static inline bool tcpm_inquire_pd_prev_connected( + struct tcpc_device *tcpc) +{ + return false; +} + +static inline uint8_t tcpm_inquire_pd_data_role( + struct tcpc_device *tcpc) +{ + return 0; +} + +static inline uint8_t tcpm_inquire_pd_power_role( + struct tcpc_device *tcpc) +{ + return 0; +} + +static inline uint8_t tcpm_inquire_pd_vconn_role( + struct tcpc_device *tcpc) +{ + return 0; +} + +static inline uint8_t tcpm_inquire_pd_pe_ready( + struct tcpc_device *tcpc) +{ + return 0; +} + +static inline uint8_t tcpm_inquire_cable_current( + struct tcpc_device *tcpc) +{ + return PD_CABLE_CURR_UNKNOWN; +} + +static inline uint32_t tcpm_inquire_dpm_flags( + struct tcpc_device *tcpc) +{ + return 0; +} + +static inline uint32_t tcpm_inquire_dpm_caps( + struct tcpc_device *tcpc) +{ + return 0; +} + +static inline void tcpm_set_dpm_caps( + struct tcpc_device *tcpc, uint32_t caps) +{ +} + +static inline int tcpm_put_tcp_dpm_event( + struct tcpc_device *tcpc, struct tcp_dpm_event *event) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_inquire_pd_contract( + struct tcpc_device *tcpc, int *mv, int *ma) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_inquire_cable_inform( + struct tcpc_device *tcpc, uint32_t *vdos) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_inquire_pd_partner_inform( + struct tcpc_device *tcpc, uint32_t *vdos) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_inquire_pd_partner_svids( + struct tcpc_device *tcpc, struct tcpm_svid_list *list) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_inquire_pd_partner_modes( + struct tcpc_device *tcpc, uint16_t svid, struct tcpm_mode_list *list) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_inquire_pd_source_cap( + struct tcpc_device *tcpc, struct tcpm_power_cap *cap) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_inquire_pd_sink_cap( + struct tcpc_device *tcpc, struct tcpm_power_cap *cap) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline bool tcpm_extract_power_cap_val( + uint32_t pdo, struct tcpm_power_cap_val *cap) +{ + return false; +} + +static inline bool tcpm_extract_power_cap_list( + struct tcpm_power_cap *cap, struct tcpm_power_cap_list *cap_list) +{ + return false; +} + +static inline int tcpm_get_remote_power_cap(struct tcpc_device *tcpc, + struct tcpm_remote_power_cap *cap) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_inquire_select_source_cap( + struct tcpc_device *tcpc, struct tcpm_power_cap_val *cap_val) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_power_swap(struct tcpc_device *tcpc, + uint8_t role, const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_data_swap(struct tcpc_device *tcpc, + uint8_t role, const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_vconn_swap(struct tcpc_device *tcpc, + uint8_t role, const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_goto_min(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_soft_reset(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_get_source_cap(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_get_sink_cap(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_request(struct tcpc_device *tcpc, + int mv, int ma, const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_request_ex(struct tcpc_device *tcpc, + uint8_t pos, uint32_t max, uint32_t oper, + const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_bist_cm2(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} +#endif /* USB_POWER_DELIVERY_NA */ + +#ifdef USB_PD_REV30_NA +static inline int tcpm_dpm_pd_get_source_cap_ext(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data, + struct pd_source_cap_ext *src_cap_ext) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_fast_swap(struct tcpc_device *tcpc, + uint8_t role, const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_get_status(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data, struct pd_status *status) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_get_pps_status(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data, + struct pd_pps_status *pps_status) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_get_country_code(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data, + struct pd_country_codes *ccdb) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_get_country_info(struct tcpc_device *tcpc, + uint32_t ccdo, const struct tcp_dpm_event_cb_data *data, + struct pd_country_info *cidb) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_get_bat_cap(struct tcpc_device *tcpc, + struct pd_get_battery_capabilities *gbcdb, + const struct tcp_dpm_event_cb_data *data, + struct pd_battery_capabilities *bcdb) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_get_bat_status(struct tcpc_device *tcpc, + struct pd_get_battery_status *gbsdb, + const struct tcp_dpm_event_cb_data *data, uint32_t *bsdo) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_get_mfrs_info(struct tcpc_device *tcpc, + struct pd_get_manufacturer_info *gmidb, + const struct tcp_dpm_event_cb_data *data, + struct pd_manufacturer_info *midb) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_alert(struct tcpc_device *tcpc, + uint32_t ado, const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} +#endif /* USB_PD_REV30_NA */ + +#ifdef USB_POWER_DELIVERY_NA + +static inline int tcpm_dpm_pd_hard_reset(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_pd_error_recovery(struct tcpc_device *tcpc) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +/* Request TCPM to send SOP' request */ + +static inline int tcpm_dpm_pd_cable_soft_reset( + struct tcpc_device *tcpc, const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_vdm_discover_cable( + struct tcpc_device *tcpc, const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +/* Request TCPM to send VDM request */ + +static inline int tcpm_dpm_vdm_discover_id( + struct tcpc_device *tcpc, const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_vdm_discover_svid( + struct tcpc_device *tcpc, const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_vdm_discover_mode(struct tcpc_device *tcpc, + uint16_t svid, const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_vdm_enter_mode(struct tcpc_device *tcpc, + uint16_t svid, uint8_t ops, const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_vdm_attention(struct tcpc_device *tcpc, + uint16_t svid, uint8_t ops, const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_vdm_exit_mode(struct tcpc_device *tcpc, + uint16_t svid, uint8_t ops, const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} +#endif /* USB_POWER_DELIVERY_NA */ + +#ifdef USB_PD_ALT_MODE_NA +static inline int tcpm_inquire_dp_ufp_u_state( + struct tcpc_device *tcpc, uint8_t *state) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_dp_attention(struct tcpc_device *tcpc, + uint32_t dp_status, uint32_t mask, + const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} +#endif /* USB_PD_ALT_MODE_NA */ + +#ifdef USB_PD_ALT_MODE_DFP_NA +static inline int tcpm_inquire_dp_dfp_u_state( + struct tcpc_device *tcpc, uint8_t *state) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_dp_status_update(struct tcpc_device *tcpc, + uint32_t dp_status, uint32_t mask, + const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_dpm_dp_config(struct tcpc_device *tcpc, + uint32_t dp_config, uint32_t mask, + const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} +#endif /* USB_PD_ALT_MODE_DFP_NA */ + +#ifdef USB_PD_CUSTOM_VDM_NA +static inline int tcpm_dpm_send_custom_vdm( + struct tcpc_device *tcpc, + struct tcp_dpm_custom_vdm_data *vdm_data, + const struct tcp_dpm_event_cb_data *cb_data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} +#endif /* USB_PD_CUSTOM_VDM_NA */ + +#ifdef USB_POWER_DELIVERY_NA +static inline int tcpm_notify_vbus_stable(struct tcpc_device *tcpc) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_reset_pd_charging_policy(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_set_pd_charging_policy(struct tcpc_device *tcpc, + uint8_t policy, const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_set_pd_charging_policy_default( + struct tcpc_device *tcpc, uint8_t policy) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline uint8_t tcpm_inquire_pd_charging_policy( + struct tcpc_device *tcpc) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline uint8_t tcpm_inquire_pd_charging_policy_default( + struct tcpc_device *tcpc) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} +#endif /* USB_POWER_DELIVERY_NA */ + +#ifdef USB_PD_DIRECT_CHARGE_NA +static inline int tcpm_set_direct_charge_en( + struct tcpc_device *tcpc, bool en) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline bool tcpm_inquire_during_direct_charge( + struct tcpc_device *tcpc) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} +#endif /* USB_PD_DIRECT_CHARGE_NA */ + +#ifdef TCPC_VCONN_SUPPLY_MODE_NA +static inline int tcpm_dpm_set_vconn_supply_mode( + struct tcpc_device *tcpc, uint8_t mode) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} +#endif /* TCPC_VCONN_SUPPLY_MODE_NA */ + +#ifdef USB_PD_REV30_PPS_SINK_NA +static inline int tcpm_set_apdo_charging_policy( + struct tcpc_device *tcpc, uint8_t policy, int mv, int ma, + const struct tcp_dpm_event_cb_data *data) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_inquire_pd_source_apdo(struct tcpc_device *tcpc, + uint8_t apdo_type, uint8_t *cap_i, struct tcpm_power_cap_val *cap) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline bool tcpm_inquire_during_pps_charge(struct tcpc_device *tcpc) +{ + return false; +} + +#endif /* USB_PD_REV30_PPS_SINK_NA */ + +#ifdef USB_PD_REV30_BAT_INFO_NA + +static inline int tcpm_update_bat_status_wh(struct tcpc_device *tcpc, + enum pd_battery_reference ref, uint8_t status, uint16_t wh) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_update_bat_status_wh_no_mutex(struct tcpc_device *tcpc, + enum pd_battery_reference ref, uint8_t status, uint16_t wh) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_update_bat_status_soc(struct tcpc_device *tcpc, + uint8_t status, uint16_t soc) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_update_bat_status_soc_no_mutex(struct tcpc_device *tcpc, + uint8_t status, uint16_t soc) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_update_bat_last_full(struct tcpc_device *tcpc, + enum pd_battery_reference ref, uint16_t wh) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_update_bat_last_full_no_mutex(struct tcpc_device *tcpc, + enum pd_battery_reference ref, uint16_t wh) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +#endif /* USB_PD_REV30_BAT_INFO_NA */ + + +#ifdef USB_PD_REV30_STATUS_NA + +static inline int tcpm_update_pd_status_temp(struct tcpc_device *tcpc, + enum pd_present_temperature_flag ptf, uint8_t temperature) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_update_pd_status_input( + struct tcpc_device *tcpc, uint8_t input, uint8_t mask) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_update_pd_status_bat_input( + struct tcpc_device *tcpc, uint8_t bat_input, uint8_t bat_mask) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +static inline int tcpm_update_pd_status_event( + struct tcpc_device *tcpc, uint8_t evt) +{ + return TCPM_ERROR_NO_IMPLEMENT; +} + +#endif /* USB_PD_REV30_STATUS_NA */ + +#undef TCPC_CLASS_NA +#undef USB_POWER_DELIVERY_NA +#undef USB_PD_REV30_NA +#undef USB_PD_ALT_MODE_NA +#undef USB_PD_ALT_MODE_DFP_NA +#undef USB_PD_CUSTOM_VDM_NA +#undef USB_PD_DIRECT_CHARGE_NA +#undef TCPC_VCONN_SUPPLY_MODE_NA +#undef USB_PD_REV30_PPS_SINK_NA +#undef USB_PD_REV30_BAT_INFO_NA +#undef USB_PD_REV30_STATUS_NA + +#endif /* TCPM_H_ */ diff --git a/drivers/usb/typec/mmi_tcpc/inc/tcpm_pd.h b/drivers/usb/typec/mmi_tcpc/inc/tcpm_pd.h new file mode 100644 index 000000000000..723d73697586 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/inc/tcpm_pd.h @@ -0,0 +1,335 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#ifndef TCPM_PD_H_ +#define TCPM_PD_H_ + +#include "tcpci_config.h" + +/* --- PD data message helpers --- */ + +#define PD_DATA_OBJ_SIZE (7) +#define PDO_MAX_NR (PD_DATA_OBJ_SIZE) +#define VDO_MAX_NR (PD_DATA_OBJ_SIZE-1) +#define VDO_MAX_SVID_NR (VDO_MAX_NR*2) + +#define VDO_DISCOVER_ID_IDH 0 +#define VDO_DISCOVER_ID_CSTAT 1 +#define VDO_DISCOVER_ID_PRODUCT 2 +#define VDO_DISCOVER_ID_CABLE 3 +#define VDO_DISCOVER_ID_AMA 3 + +/******************* PD30 *******************/ + +#ifdef CONFIG_USB_PD_REV30_CHUNKING_BY_PE +#define MAX_EXTENDED_MSG_LEN 260 +#else +#define MAX_EXTENDED_MSG_LEN 26 +#endif /* CONFIG_USB_PD_REV30_CHUNKING */ + +/* PD30 Data Message Data Object */ + +#define PD_BSDO_SIZE 1 +#define PD_CCDO_SIZE 1 +#define PD_ADO_SIZE 1 + +/* + * Battery Status Data Object (BSDO) + * ---------- + * <31:16> :: Battery Present Capacity (1/10 WH, 0xFFFF = SOC unknown) + * <15:8> :: Battery Info + * <7:0> :: Reserved and Shall be set to zero + */ + +#define BSDO(cap, info) (((cap) << 16) | ((info) << 8)) + +#define BSDO_BAT_INFO(x) ((x>>8) & 0xff) + +#define BSDO_BAT_INFO_INVALID_REF (1<<0) +#define BSDO_BAT_INFO_PRESENT (1<<1) +#define BSDO_BAT_INFO_CHARGING ((0<<2) | BSDO_BAT_INFO_PRESENT) +#define BSDO_BAT_INFO_DISCHARGING ((1<<2) | BSDO_BAT_INFO_PRESENT) +#define BSDO_BAT_INFO_IDLE ((2<<2) | BSDO_BAT_INFO_PRESENT) + +#define BSDO_BAT_CAP_UNKNOWN (0xffff) + +/* + * Country Code Data Object (CCDO) + * ---------- + * <31:24> :: First character of the Alpha-2 Country Code + * <23:16> :: Second character of the Alpha-2 Country Code + * <15:0> :: Reserved and Shall be set to zero + */ + +#define CCDO(code) ((code) << 16) + +#define CCDO_COUNTRY_CODE(x) ((x >> 16) & 0xffff) +#define CCDO_COUNTRY_CODE1(x) ((x >> 24) & 0xff) +#define CCDO_COUNTRY_CODE2(x) ((x >> 16) & 0xff) + +/* + * Alert Data Object (ADO) + * ---------- + * <31:24> :: Type Of Alert + * <23:20> :: Fixed Batteries (bit field) + * <19:16> :: Hot Swappable Batteries (bit field) + */ + +#define ADO_ALERT_BAT_CHANGED (1<<1) +#define ADO_ALERT_OCP (1<<2) +#define ADO_ALERT_OTP (1<<3) +#define ADO_ALERT_OPER_CHANGED (1<<4) +#define ADO_ALERT_SRC_IN_CHANGED (1<<5) +#define ADO_ALERT_OVP (1<<6) + +#define ADO_ALERT_TYPE(raw) (raw >> 24) +#define ADO_FIXED_BAT(raw) ((raw >> 20) & 0x0f) +#define ADO_HOT_SWAP_BAT(raw) ((raw >> 16) & 0x0f) + +#define ADO_ALERT_TYPE_SET(type) (type << 24) +#define ADO_FIXED_BAT_SET(i) ((i) << 20) +#define ADO_HOT_SWAP_BAT_SET(i) ((i) << 16) + +#define ADO(type, fixed, swap) \ + (((type) << 24) | ((fixed) << 20) | ((swap) << 16)) + +#define ADO_GET_STATUS_ONCE_MASK ADO(\ + ADO_ALERT_BAT_CHANGED|ADO_ALERT_SRC_IN_CHANGED,\ + 0xff, 0xff) + +/* PD30 Extend Message Data Object */ + +enum pd_present_temperature_flag { + PD_PTF_NO_SUPPORT = 0, + PD_PTF_NORMAL, + PD_PTF_WARNING, + PD_PTF_OVER_TEMP, +}; + +enum pd_battery_reference { + PD_BAT_REF_FIXED0 = 0, + PD_BAT_REF_FIXED1, + PD_BAT_REF_FIXED2, + PD_BAT_REF_FIXED3, + + PD_BAT_REF_SWAP0 = 4, + PD_BAT_REF_SWAP1, + PD_BAT_REF_SWAP2, + PD_BAT_REF_SWAP3, + + PD_BAT_REF_MAX, + + /* 8 ~ 255 are reserved and shall not be used */ +}; + + +/* SCEDB, Source_Capabilities_Extended */ + +#define PD_SCEDB_SIZE 24 + +#define PD_SCEDB_VR(load_step, ioc) \ + ((load_step) | (ioc << 2)) + +enum { + PD_SCEDB_VR_LOAD_STEP_150 = 0x00, + PD_SCEDB_VR_LOAD_STEP_500 = 0x01, +}; + +enum { + PD_SCEDB_VR_IOC_25 = 0x00, + PD_SCEDB_VR_IOC_90 = 0x01, +}; + +#define PD_SCEDB_COMPLIANCE_LPS (1<<0) +#define PD_SCEDB_COMPLIANCE_PS1 (1<<1) +#define PD_SCEDB_COMPLIANCE_PS2 (1<<2) + +#define PD_SCEDB_TC_LOW_TC (1<<0) +#define PD_SCEDB_TC_GROUND (1<<1) +#define PD_SCEDB_TC_GROUND_INTEND (1<<2) + +#define PD_SCEDB_TT_IEC_60950 0 +#define PD_SCEDB_TT_IEC_62368_TS1 1 +#define PD_SCEDB_TT_IEC_62368_TS2 2 + +#define PD_SCEDB_INPUT_EXT (1<<0) +#define PD_SCEDB_INPUT_EXT_UNCONSTRAINED (1<<1) +#define PD_SCEDB_INPUT_INT (1<<2) + +#define PD_SCEDB_BATTERIES(swap_nr, fixed_nr) \ + (swap_nr << 4 | fixed_nr) + +#define PD_SCEDB_FIX_BAT_NR(raw) (raw & 0xf) +#define PD_SCEDB_SWAP_BAT_NR(raw) ((raw >> 4) & 0xf) + +struct pd_source_cap_ext { + uint16_t vid; + uint16_t pid; + uint32_t xid; + uint8_t fw_ver; + uint8_t hw_ver; + uint8_t voltage_regulation; + uint8_t hold_time_ms; + uint8_t compliance; /* bit field */ + uint8_t touch_current; /* bit field */ + uint16_t peak_current[3]; + uint8_t touch_temp; + uint8_t source_inputs; /* bit field */ + uint8_t batteries; + uint8_t source_pdp; +}; + +/* GBSDB, Get_Battery_Status */ + +#define PD_GBSDB_SIZE 1 + +struct pd_get_battery_status { + uint8_t bat_status_ref; /* pd_battery_reference */ +}; + +/* GBCDB, Get_Battery_Cap */ + +#define PD_GBCDB_SIZE 1 + +struct pd_get_battery_capabilities { + uint8_t bat_cap_ref; /* pd_battery_reference */ +}; + +/* BCDB, Battery_Capabilities */ + +#define PD_BCDB_SIZE 9 + +#define PD_BCDB_BAT_CAP_NOT_PRESENT 0x0000 +#define PD_BCDB_BAT_CAP_UNKNOWN 0Xffff +#define PD_BCDB_BAT_CAP_RAW(cap_wh) (cap_wh*10) +#define PD_BCDB_BAT_CAP_VAL(raw) (raw/10) + +#define PD_BCDB_BAT_TYPE_INVALID (1<<0) + +struct pd_battery_capabilities { + uint16_t vid; + uint16_t pid; + uint16_t bat_design_cap; + uint16_t bat_last_full_cap; + uint8_t bat_type; +}; + +/* GMIDB, Get_Manufacturer_Info */ + +#define PD_GMIDB_SIZE 2 + +#define PD_GMIDB_TARGET_PORT 0 +#define PD_GMIDB_TARGET_BATTRY 1 + +struct pd_get_manufacturer_info { + uint8_t info_target; + uint8_t info_ref; +}; + +/* MIDB, Manufacturer_Info */ + +#define PD_MIDB_MIN_SIZE 4 +#define PD_MIDB_MAX_SIZE 26 +#define PD_MIDB_DYNAMIC_SIZE (PD_MIDB_MAX_SIZE-PD_MIDB_MIN_SIZE) + +struct pd_manufacturer_info { + uint16_t vid; + uint16_t pid; + uint8_t mfrs_string[PD_MIDB_DYNAMIC_SIZE]; +}; + +/* CCDB, Country_Codes */ + +#define PD_CCDB_MIN_SIZE 4 +#define PD_CCDB_MAX_SIZE MAX_EXTENDED_MSG_LEN +#define PD_CCDB_DYNAMIC_SIZE (PD_CCDB_MAX_SIZE-PD_CCDB_MIN_SIZE) + +struct pd_country_codes { + uint16_t length; + uint16_t country_code[1+PD_CCDB_DYNAMIC_SIZE/2]; +}; + +/* CIDB, country_info */ + +#define PD_CIDB_MIN_SIZE 4 +#define PD_CIDB_MAX_SIZE MAX_EXTENDED_MSG_LEN + +#define PD_CIDB_DYNAMIC_SIZE (PD_CIDB_MAX_SIZE-PD_CIDB_MIN_SIZE) + +struct pd_country_info { + uint16_t country_code; + uint16_t reserved; + uint8_t country_special_data[PD_CIDB_DYNAMIC_SIZE]; +}; + +/* SDB, Status */ + +#define PD_STATUS_INPUT_EXT_POWER (1<<1) +#define PD_STATUS_INPUT_EXT_POWER_FROM_AC (1<<2) +#define PD_STATUS_INPUT_INT_POWER_BAT (1<<3) +#define PD_STATUS_INPUT_INT_POWER_NOT_BAT (1<<4) + +#define PD_STASUS_EVENT_OCP (1<<1) +#define PD_STATUS_EVENT_OTP (1<<2) +#define PD_STATUS_EVENT_OVP (1<<3) +#define PD_STATUS_EVENT_CF_MODE (1<<4) + +#define PD_STASUS_EVENT_READ_CLEAR (\ + PD_STASUS_EVENT_OCP|PD_STATUS_EVENT_OTP|PD_STATUS_EVENT_OVP) + +#define PD_STASUS_EVENT_MASK (\ + PD_STASUS_EVENT_OCP|PD_STATUS_EVENT_OTP|PD_STATUS_EVENT_OVP|\ + PD_STATUS_EVENT_CF_MODE) + +#define PD_STATUS_TEMP_PTF(raw) ((raw & 0x06) >> 1) +#define PD_STATUS_TEMP_SET_PTF(val) ((val & 0x03) << 1) + +struct pd_status { + uint8_t internal_temp; /* 0 means no support */ + uint8_t present_input; /* bit filed */ + uint8_t present_battery_input; + uint8_t event_flags; /* bit filed */ + uint8_t temp_status; /* bit filed */ + uint8_t power_status; /* bit filed */ + uint8_t power_state_change; +}; + +#define PD_SDB_SIZE sizeof(struct pd_status) + +/* PPSSDB, PPSStatus */ + +#define PD_PPSSDB_SIZE 4 /* PPS_Status */ + +#define PD_PPS_GET_OUTPUT_MV(raw) (raw*20) +#define PD_PPS_GET_OUTPUT_MA(raw) (raw*50) + +#define PD_PPS_SET_OUTPUT_MV(mv) (((mv) / 20) & 0xFFFF) +#define PD_PPS_SET_OUTPUT_MA(ma) (((ma) / 50) & 0xFF) + +#define PD_PPS_FLAGS_CFF (1 << 3) +#define PD_PPS_FLGAS_PTF(raw) ((raw & 0x06) >> 1) + +#define PD_PPS_FLAGS_SET_PTF(flags) ((flags & 0x03) << 1) + +struct pd_pps_status_raw { + uint16_t output_vol_raw; /* 0xffff means no support */ + uint8_t output_curr_raw; /* 0xff means no support */ + uint8_t real_time_flags; +}; + +struct pd_pps_status { + int output_mv; + int output_ma; + uint8_t real_time_flags; +}; +#endif /* TCPM_PD_H_ */ diff --git a/drivers/usb/typec/mmi_tcpc/pd_adapter.c b/drivers/usb/typec/mmi_tcpc/pd_adapter.c new file mode 100644 index 000000000000..0946ddc57768 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_adapter.c @@ -0,0 +1,367 @@ +/* Copyright (c) 2020, 2021 The Linux Foundation. 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 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "inc/tcpci_typec.h" +#include + +struct mmi_pd_adapter_info { + struct tcpc_device *tcpc; + struct notifier_block pd_nb; + struct adapter_device *adapter_dev; + struct task_struct *adapter_task; + const char *adapter_dev_name; + bool enable_kpoc_shdn; + int pd_type; +}; + +static int pd_get_property(struct adapter_device *dev, + enum adapter_property sta) +{ + struct mmi_pd_adapter_info *info; + + info = (struct mmi_pd_adapter_info *)adapter_dev_get_drvdata(dev); + if (info == NULL || info->tcpc == NULL) + return -1; + + switch (sta) { + case TYPEC_RP_LEVEL: + { + return tcpm_inquire_typec_remote_rp_curr(info->tcpc); + } + break; + case PD_TYPE: + { + return info->pd_type; + } + break; + default: + { + } + break; + } + return -1; +} + +static int pd_set_cap(struct adapter_device *dev, enum adapter_cap_type type, + int pdo_idx, int mV, int mA) +{ + int ret = MMI_ADAPTER_OK; + int tcpm_ret = TCPM_SUCCESS; + struct mmi_pd_adapter_info *info; + + pr_notice("[%s] type:%d mV:%d mA:%d\n", + __func__, type, mV, mA); + + pdo_idx = 0; + info = (struct mmi_pd_adapter_info *)adapter_dev_get_drvdata(dev); + if (info == NULL || info->tcpc == NULL) { + pr_notice("[%s] info null\n", __func__); + return -1; + } + + if (type == MMI_PD_APDO_START) { + tcpm_ret = tcpm_set_apdo_charging_policy(info->tcpc, + DPM_CHARGING_POLICY_PPS, mV, mA, NULL); + } else if (type == MMI_PD_APDO_END) { + tcpm_ret = tcpm_reset_pd_charging_policy(info->tcpc, NULL); + } else if (type == MMI_PD_APDO) { + tcpm_ret = tcpm_dpm_pd_request(info->tcpc, mV, mA, NULL); + } else if (type == MMI_PD_FIXED) { + tcpm_ret = tcpm_dpm_pd_request(info->tcpc, mV, + mA, NULL); + } + + pr_notice("[%s] type:%d mV:%d mA:%d ret:%d\n", + __func__, type, mV, mA, tcpm_ret); + + + if (tcpm_ret == TCP_DPM_RET_REJECT) + return MMI_ADAPTER_REJECT; + else if (tcpm_ret != 0) + return MMI_ADAPTER_ERROR; + + return ret; +} + +int pd_get_output(struct adapter_device *dev, int *mV, int *mA) +{ + int ret = MMI_ADAPTER_OK; + int tcpm_ret = TCPM_SUCCESS; + struct pd_pps_status pps_status; + struct mmi_pd_adapter_info *info; + + info = (struct mmi_pd_adapter_info *)adapter_dev_get_drvdata(dev); + if (info == NULL || info->tcpc == NULL) + return MMI_ADAPTER_NOT_SUPPORT; + + + tcpm_ret = tcpm_dpm_pd_get_pps_status(info->tcpc, NULL, &pps_status); + if (tcpm_ret == TCP_DPM_RET_NOT_SUPPORT) + return MMI_ADAPTER_NOT_SUPPORT; + else if (tcpm_ret != 0) + return MMI_ADAPTER_ERROR; + + *mV = pps_status.output_mv; + *mA = pps_status.output_ma; + + return ret; +} + +int pd_get_status(struct adapter_device *dev, + struct adapter_status *sta) +{ + struct pd_status TAstatus = {0,}; + int ret = MMI_ADAPTER_OK; + int tcpm_ret = TCPM_SUCCESS; + struct mmi_pd_adapter_info *info; + + info = (struct mmi_pd_adapter_info *)adapter_dev_get_drvdata(dev); + if (info == NULL || info->tcpc == NULL) + return MMI_ADAPTER_ERROR; + + tcpm_ret = tcpm_dpm_pd_get_status(info->tcpc, NULL, &TAstatus); + + sta->temperature = TAstatus.internal_temp; + sta->ocp = TAstatus.event_flags & PD_STASUS_EVENT_OCP; + sta->otp = TAstatus.event_flags & PD_STATUS_EVENT_OTP; + sta->ovp = TAstatus.event_flags & PD_STATUS_EVENT_OVP; + + if (tcpm_ret == TCP_DPM_RET_NOT_SUPPORT) + return MMI_ADAPTER_NOT_SUPPORT; + else if (tcpm_ret == TCP_DPM_RET_TIMEOUT) + return MMI_ADAPTER_TIMEOUT; + else if (tcpm_ret == TCP_DPM_RET_SUCCESS) + return MMI_ADAPTER_OK; + else + return MMI_ADAPTER_ERROR; + + return ret; + +} + +static int pd_get_cap(struct adapter_device *dev, + enum adapter_cap_type type, + struct adapter_power_cap *tacap) +{ + struct tcpm_power_cap_val apdo_cap; + struct tcpm_remote_power_cap pd_cap; + struct pd_source_cap_ext cap_ext; + + uint8_t cap_i = 0; + int ret; + int idx = 0; + unsigned int i, j; + struct mmi_pd_adapter_info *info; + + info = (struct mmi_pd_adapter_info *)adapter_dev_get_drvdata(dev); + if (info == NULL || info->tcpc == NULL) + return MMI_ADAPTER_ERROR; + + if (type == MMI_PD_APDO) { + while (1) { + ret = tcpm_inquire_pd_source_apdo(info->tcpc, + TCPM_POWER_CAP_APDO_TYPE_PPS, + &cap_i, &apdo_cap); + if (ret == TCPM_ERROR_NOT_FOUND) { + break; + } else if (ret != TCPM_SUCCESS) { + pr_notice("[%s] tcpm_inquire_pd_source_apdo failed(%d)\n", + __func__, ret); + break; + } + + ret = tcpm_dpm_pd_get_source_cap_ext(info->tcpc, + NULL, &cap_ext); + if (ret == TCPM_SUCCESS) + tacap->pdp = cap_ext.source_pdp; + else { + tacap->pdp = 0; + pr_notice("[%s] tcpm_dpm_pd_get_source_cap_ext failed(%d)\n", + __func__, ret); + } + + tacap->pwr_limit[idx] = apdo_cap.pwr_limit; + /* If TA has PDP, we set pwr_limit as true */ + if (tacap->pdp > 0 && !tacap->pwr_limit[idx]) + tacap->pwr_limit[idx] = 1; + tacap->ma[idx] = apdo_cap.ma; + tacap->max_mv[idx] = apdo_cap.max_mv; + tacap->min_mv[idx] = apdo_cap.min_mv; + tacap->maxwatt[idx] = apdo_cap.max_mv * apdo_cap.ma; + tacap->minwatt[idx] = apdo_cap.min_mv * apdo_cap.ma; + tacap->type[idx] = MMI_PD_APDO; + + idx++; + pr_notice("pps_boundary[%d], %d mv ~ %d mv, %d ma pl:%d\n", + cap_i, + apdo_cap.min_mv, apdo_cap.max_mv, + apdo_cap.ma, apdo_cap.pwr_limit); + if (idx >= ADAPTER_CAP_MAX_NR) { + pr_notice("CAP NR > %d\n", ADAPTER_CAP_MAX_NR); + break; + } + } + tacap->nr = idx; + + for (i = 0; i < tacap->nr; i++) { + pr_notice("pps_cap[%d:%d], %d mv ~ %d mv, %d ma pl:%d pdp:%d\n", + i, (int)tacap->nr, tacap->min_mv[i], + tacap->max_mv[i], tacap->ma[i], + tacap->pwr_limit[i], tacap->pdp); + } + + if (cap_i == 0) + pr_notice("no APDO for pps\n"); + + } else if (type == MMI_PD_FIXED) { + pd_cap.nr = 0; + pd_cap.selected_cap_idx = 0; + tcpm_get_remote_power_cap(info->tcpc, &pd_cap); + + if (pd_cap.nr != 0) { + + tacap->selected_cap_idx = pd_cap.selected_cap_idx - 1; + pr_notice("[%s] nr:%d idx:%d\n", + __func__, pd_cap.nr, pd_cap.selected_cap_idx - 1); + + j = 0; + pr_notice("adapter cap: nr:%d\n", pd_cap.nr); + for (i = 0; i < pd_cap.nr; i++) { + if (pd_cap.type[i] == TCPM_POWER_CAP_VAL_TYPE_FIXED && + j >= 0 && + j < ADAPTER_CAP_MAX_NR) { + tacap->type[j] = MMI_PD_FIXED; + tacap->ma[j] = pd_cap.ma[i]; + tacap->max_mv[j] = pd_cap.max_mv[i]; + tacap->min_mv[j] = pd_cap.min_mv[i]; + tacap->maxwatt[j] = + tacap->max_mv[j] * tacap->ma[i]; + tacap->minwatt[j] = + tacap->min_mv[j] * tacap->ma[i]; + j++; + } + + pr_notice("[%s]:%d mv:[%d,%d] mA:%d type:%d %d\n", + __func__, i, pd_cap.min_mv[i], + pd_cap.max_mv[i], pd_cap.ma[i], + pd_cap.type[i], pd_cap.type[i]); + } + + tacap->nr = j; + pr_notice("pd cap: nr:%d\n", tacap->nr); + for (i = 0; i < tacap->nr; i++) { + pr_notice("[%s]:%d mv:[%d,%d] mA:%d max:%d min:%d type:%d %d\n", + __func__, i, tacap->min_mv[i], + tacap->max_mv[i], tacap->ma[i], + tacap->maxwatt[i], tacap->minwatt[i], + tacap->type[i], tacap->type[i]); + } + } + } else if (type == MMI_PD_ALL) { + pd_cap.nr = 0; + pd_cap.selected_cap_idx = 0; + tcpm_get_remote_power_cap(info->tcpc, &pd_cap); + tacap->nr = pd_cap.nr; + + if (pd_cap.nr != 0) { + + tacap->selected_cap_idx = pd_cap.selected_cap_idx - 1; + pr_notice("[%s] nr:%d idx:%d\n", + __func__, pd_cap.nr, pd_cap.selected_cap_idx - 1); + + for (i = 0; i < pd_cap.nr; i++) { + tacap->ma[i] = pd_cap.ma[i]; + tacap->max_mv[i] = pd_cap.max_mv[i]; + tacap->min_mv[i] = pd_cap.min_mv[i]; + tacap->maxwatt[i] = + tacap->max_mv[i] * tacap->ma[i]; + if (pd_cap.type[i] == TCPM_POWER_CAP_VAL_TYPE_FIXED) + tacap->type[i] = MMI_PD_FIXED; + else if (pd_cap.type[i] == TCPM_POWER_CAP_VAL_TYPE_AUGMENT) + tacap->type[i] = MMI_PD_APDO; + else + tacap->type[i] = MMI_CAP_TYPE_UNKNOWN; + + pr_notice("[%s]:%d mv:[%d,%d] %d max:%d min:%d type:%d %d\n", + __func__, i, tacap->min_mv[i], + tacap->max_mv[i], tacap->ma[i], + tacap->maxwatt[i], tacap->minwatt[i], + tacap->type[i], pd_cap.type[i]); + } + } + } + + return MMI_ADAPTER_OK; +} + +static struct adapter_ops adapter_ops = { + .get_status = pd_get_status, + .set_cap = pd_set_cap, + .get_output = pd_get_output, + .get_property = pd_get_property, + .get_cap = pd_get_cap, +}; + +int pd_adapter_create(struct platform_device *pdev) +{ + int ret = 0; + struct mmi_pd_adapter_info *info = NULL; + static bool is_deferred; + + pr_info("%s\n", __func__); + + info = devm_kzalloc(&pdev->dev, sizeof(struct mmi_pd_adapter_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->adapter_dev = adapter_device_register("pd_adapter", + &pdev->dev, info, &adapter_ops, NULL); + if (IS_ERR_OR_NULL(info->adapter_dev)) { + pr_info("%s: adapter_device_register err\n", __func__); + ret = PTR_ERR(info->adapter_dev); + goto err_register_adapter_dev; + } + + adapter_dev_set_drvdata(info->adapter_dev, info); + + info->tcpc = tcpc_dev_get_by_name("type_c_port0"); + if (info->tcpc == NULL) { + if (is_deferred == false) { + pr_info("%s: tcpc device not ready, defer\n", __func__); + is_deferred = true; + ret = -EPROBE_DEFER; + } else { + pr_info("%s: failed to get tcpc device\n", __func__); + ret = -EINVAL; + } + goto err_get_tcpc_dev; + } + + return 0; + +err_get_tcpc_dev: + adapter_device_unregister(info->adapter_dev); +err_register_adapter_dev: + devm_kfree(&pdev->dev, info); + + return ret; +} +EXPORT_SYMBOL(pd_adapter_create); diff --git a/drivers/usb/typec/mmi_tcpc/pd_core.c b/drivers/usb/typec/mmi_tcpc/pd_core.c new file mode 100644 index 000000000000..ba6790f7a47e --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_core.c @@ -0,0 +1,1517 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Core Driver + * + * 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. + */ + +#include +#include +#include "inc/tcpci.h" +#include "inc/pd_core.h" +#include "inc/pd_dpm_core.h" +#include "inc/tcpci_typec.h" +#include "inc/tcpci_event.h" +#include "inc/pd_policy_engine.h" +#ifdef CONFIG_DUAL_ROLE_USB_INTF +#include +#endif /* CONFIG_DUAL_ROLE_USB_INTF */ + +/* From DTS */ + +#ifdef CONFIG_USB_PD_REV30_BAT_INFO +static inline void pd_parse_pdata_bat_info( + struct pd_port *pd_port, struct device_node *sub, + struct pd_battery_info *bat_info) +{ + int ret = 0; + u32 design_cap; + uint32_t vid, pid; + const char *mstring; + + struct pd_battery_capabilities *bat_cap = &bat_info->bat_cap; + +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL + struct pd_manufacturer_info *mfrs_info = &bat_info->mfrs_info; +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */ + + ret = of_property_read_u32(sub, "bat,vid", (u32 *)&vid); + if (ret < 0) { + pr_err("%s get pd vid fail\n", __func__); + vid = PD_IDH_VID(pd_port->id_vdos[0]); + } + + ret = of_property_read_u32(sub, "bat,pid", (u32 *)&pid); + if (ret < 0) { + pr_err("%s get pd pid fail\n", __func__); + pid = PD_PRODUCT_PID(pd_port->id_vdos[2]); + } + +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL + mfrs_info->vid = vid; + mfrs_info->pid = pid; + + ret = of_property_read_string(sub, "bat,mfrs", &mstring); + if (ret < 0) { + pr_err("%s get bat,mfrs fail\n", __func__); + mstring = "no_bat_mfrs_string"; + } + ret = snprintf(mfrs_info->mfrs_string, + sizeof(mfrs_info->mfrs_string), "%s", mstring); + if (ret < 0 || ret >= sizeof(mfrs_info->mfrs_string)) + pr_info("%s-%d snprintf fail\n", __func__, __LINE__); +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */ + + ret = of_property_read_u32(sub, "bat,design_cap", &design_cap); + if (ret < 0) { + bat_cap->bat_design_cap = PD_BCDB_BAT_CAP_UNKNOWN; + pr_err("%s get bat,dsn_cat fail\n", __func__); + } else { + bat_cap->bat_design_cap = (uint16_t) + PD_BCDB_BAT_CAP_RAW(design_cap); + } + + bat_cap->vid = vid; + bat_cap->pid = pid; + bat_cap->bat_last_full_cap = PD_BCDB_BAT_CAP_UNKNOWN; + + bat_info->bat_status = BSDO( + BSDO_BAT_CAP_UNKNOWN, BSDO_BAT_INFO_IDLE); +} + +static inline int pd_parse_pdata_bats( + struct pd_port *pd_port, struct device_node *np) +{ + u32 val; + int ret = 0, i; + struct device_node *sub; + char temp_string[26]; + + ret = of_property_read_u32(np, "bat,nr", &val); + if (ret < 0) { + pr_err("%s get pd bat NR fail\n", __func__); + pd_port->bat_nr = 0; + return 0; + } + + pd_port->bat_nr = val; + pr_info("%s Battery NR = %d\n", __func__, pd_port->bat_nr); + + pd_port->fix_bat_info = devm_kzalloc(&pd_port->tcpc->dev, + sizeof(struct pd_battery_info)*pd_port->bat_nr, + GFP_KERNEL); + + if (!pd_port->fix_bat_info) { + pr_err("%s get fix_bat_info memory fail\n", __func__); + return -ENOMEM; + } + + for (i = 0; i < pd_port->bat_nr; i++) { + snprintf(temp_string, 26, "bat-info%d", i); + sub = of_find_node_by_name(np, temp_string); + if (!sub) { + pr_err("%s get sub bat node fail\n", __func__); + return -ENODEV; + } + + pd_parse_pdata_bat_info( + pd_port, sub, &pd_port->fix_bat_info[i]); + } + + for (i = 0; i < pd_port->bat_nr; i++) { + pr_info("%s fix_bat_info[%d].mfrs_info.vid = 0x%x, .mfrs_info.pid = 0x%x, .mfrs_string = %s, .bat_design_cap = %d\n", + __func__, i, + pd_port->fix_bat_info[i].mfrs_info.vid, + pd_port->fix_bat_info[i].mfrs_info.pid, + pd_port->fix_bat_info[i].mfrs_info.mfrs_string, + PD_BCDB_BAT_CAP_VAL( + pd_port->fix_bat_info[i].bat_cap.bat_design_cap)); + } + + return 0; +} +#endif /* CONFIG_USB_PD_REV30_BAT_INFO */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_AUTHORITY +static inline int pd_parse_pdata_country( + struct pd_port *pd_port, struct device_node *sub, + struct pd_country_authority *country_info) +{ + u32 val; + int ret = 0, j; + u32 *temp_u32; + + ret = of_property_read_u32(sub, "pd,country_code", &val); + if (ret < 0) { + pr_err("%s get country code fail\n", __func__); + return -ENODEV; + } + + country_info->code = (uint16_t) val; + + ret = of_property_read_u32(sub, "pd,country_len", &val); + if (ret < 0) { + pr_err("%s get country len fail\n", __func__); + return -ENODEV; + } + + country_info->len = (uint16_t) val; + + country_info->data = devm_kzalloc( + &pd_port->tcpc->dev, + sizeof(uint8_t)*country_info->len, + GFP_KERNEL); + + if (!country_info->data) { + pr_err("%s get country info data mem fail\n", + __func__); + return -ENOMEM; + } + + temp_u32 = devm_kzalloc(&pd_port->tcpc->dev, + sizeof(u32)*country_info->len, GFP_KERNEL); + + ret = of_property_read_u32_array(sub, "pd,country_data", + temp_u32, + country_info->len); + if (ret < 0) + pr_err("%s get country data fail\n", __func__); + + for (j = 0; j < country_info->len; j++) + country_info->data[j] = (uint8_t) temp_u32[j]; + + devm_kfree(&pd_port->tcpc->dev, temp_u32); + + return 0; +} + +static inline int pd_parse_pdata_countries( + struct pd_port *pd_port, struct device_node *np) +{ + int ret = 0, i, j; + struct device_node *sub; + char temp_string[26]; + + ret = of_property_read_u32(np, "pd,country_nr", + (u32 *)&pd_port->country_nr); + if (ret < 0) { + pr_err("%s get country nr fail\n", __func__); + pd_port->country_nr = 0; + return 0; + } + + pr_info("%s Country NR = %d\n", __func__, pd_port->country_nr); + + pd_port->country_info = devm_kzalloc(&pd_port->tcpc->dev, + sizeof(struct pd_country_authority)*pd_port->country_nr, + GFP_KERNEL); + + if (!pd_port->country_info) { + pr_err("%s get country info memory fail\n", __func__); + return -ENOMEM; + } + + for (i = 0; i < pd_port->country_nr; i++) { + snprintf(temp_string, 26, "country%d", i); + sub = of_find_node_by_name(np, temp_string); + if (!sub) { + pr_err("%s get sub country node fail\n", + __func__); + return -ENODEV; + } + + ret = pd_parse_pdata_country(pd_port, sub, + &pd_port->country_info[i]); + if (ret < 0) + return ret; + } + + for (i = 0; i < pd_port->country_nr; i++) { + pr_info("%s country_info[%d].code = 0x%x, .len = %d\n", + __func__, i, + pd_port->country_info[i].code, + pd_port->country_info[i].len); + for (j = 0; j < pd_port->country_info[i].len; j++) { + pr_info("%s country_info[%d].data[%d] = 0x%x\n", + __func__, i, j, + pd_port->country_info[i].data[j]); + } + } + return 0; +} +#endif /* CONFIG_USB_PD_REV30_COUNTRY_AUTHORITY */ + +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL +static void pd_parse_log_src_cap_ext(struct pd_source_cap_ext *cap) +{ + pr_info("%s vid = 0x%x, pid = 0x%x, xid = 0x%x, fw_ver = 0x%x, hw_ver = 0x%0x\n", + __func__, + cap->vid, cap->pid, cap->xid, + cap->fw_ver, cap->hw_ver); + + pr_info("%s voltage_regulation = %d, hold_time_ms = %d, compliance = 0x%x, touch_current = 0x%x, peak_current = %d %d %d\n", + __func__, + cap->voltage_regulation, + cap->hold_time_ms, + cap->compliance, + cap->touch_current, + cap->peak_current[0], + cap->peak_current[1], + cap->peak_current[2]); + + pr_info("%s touch_temp = %d, source_inputs = 0x%x, batteries = 0x%x, source_pdp = 0x%x\n", + __func__, + cap->touch_temp, + cap->source_inputs, + cap->batteries, + cap->source_pdp); +} +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ + +static inline void pd_parse_pdata_src_cap_ext( + struct pd_port *pd_port, struct device_node *np) +{ +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL + int ret = 0; + + ret = of_property_read_u32_array(np, "pd,source-cap-ext", + (u32 *) &pd_port->src_cap_ext, + sizeof(struct pd_source_cap_ext)/4); + + if (ret < 0) + pr_err("%s get source-cap-ext fail\n", __func__); + else + pd_parse_log_src_cap_ext(&pd_port->src_cap_ext); + +#ifdef CONFIG_USB_PD_REV30_BAT_INFO + pd_port->src_cap_ext.batteries = + PD_SCEDB_BATTERIES(0, pd_port->bat_nr); + + if (pd_port->src_cap_ext.batteries) + pd_port->src_cap_ext.source_inputs |= PD_SCEDB_INPUT_INT; +#endif /* CONFIG_USB_PD_REV30_BAT_INFO */ +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ +} + +static inline void pd_parse_pdata_mfrs( + struct pd_port *pd_port, struct device_node *np) +{ + int ret = 0; + uint32_t vid, pid; +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL + const char *mstring; +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL + struct pd_manufacturer_info *mfrs_info = &pd_port->mfrs_info; +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */ + + ret = of_property_read_u32(np, "pd,vid", (u32 *)&vid); + if (ret < 0) { + pr_err("%s get pd vid fail\n", __func__); + vid = PD_IDH_VID(pd_port->id_vdos[0]); + } + + ret = of_property_read_u32(np, "pd,pid", (u32 *)&pid); + if (ret < 0) { + pr_err("%s get pd pid fail\n", __func__); + pid = PD_PRODUCT_PID(pd_port->id_vdos[2]); + } + + pr_info("%s VID = 0x%x, PID = 0x%x\n", __func__, vid, pid); + +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL + mfrs_info->vid = vid; + mfrs_info->pid = pid; + + ret = of_property_read_string(np, "pd,mfrs", &mstring); + if (ret < 0) { + pr_err("%s get pd mfrs fail\n", __func__); + mstring = "no_pd_mfrs_string"; + } + ret = snprintf(mfrs_info->mfrs_string, + sizeof(mfrs_info->mfrs_string), "%s", mstring); + if (ret < 0 || ret >= sizeof(mfrs_info->mfrs_string)) + pr_info("%s-%d snprintf fail\n", __func__, __LINE__); + + pr_info("%s PD mfrs_string = %s\n", + __func__, mfrs_info->mfrs_string); +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL + pd_port->src_cap_ext.vid = vid; + pd_port->src_cap_ext.pid = pid; +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ + + pd_port->id_vdos[0] &= ~PD_IDH_VID_MASK; + pd_port->id_vdos[0] |= PD_IDH_VID(vid); + + pd_port->id_vdos[2] = VDO_PRODUCT( + pid, PD_PRODUCT_BCD(pd_port->id_vdos[2])); + + pd_port->id_header = pd_port->id_vdos[0]; +} + +static int pd_parse_pdata(struct pd_port *pd_port) +{ + u32 val; + struct device_node *np; + int ret = 0, i; + + pr_info("%s\n", __func__); + np = of_find_node_by_name(pd_port->tcpc->dev.of_node, "pd-data"); + + if (np) { + ret = of_property_read_u32(np, "pd,source-pdo-size", + (u32 *)&pd_port->local_src_cap_default.nr); + if (ret < 0) + pr_err("%s get source pdo size fail\n", __func__); + + ret = of_property_read_u32_array(np, "pd,source-pdo-data", + (u32 *)pd_port->local_src_cap_default.pdos, + pd_port->local_src_cap_default.nr); + if (ret < 0) + pr_err("%s get source pdo data fail\n", __func__); + + pr_info("%s src pdo data =\n", __func__); + for (i = 0; i < pd_port->local_src_cap_default.nr; i++) { + pr_info("%s %d: 0x%08x\n", __func__, i, + pd_port->local_src_cap_default.pdos[i]); + } + + ret = of_property_read_u32(np, "pd,sink-pdo-size", + (u32 *)&pd_port->local_snk_cap.nr); + if (ret < 0) + pr_err("%s get sink pdo size fail\n", __func__); + + ret = of_property_read_u32_array(np, "pd,sink-pdo-data", + (u32 *)pd_port->local_snk_cap.pdos, + pd_port->local_snk_cap.nr); + if (ret < 0) + pr_err("%s get sink pdo data fail\n", __func__); + + pr_info("%s snk pdo data =\n", __func__); + for (i = 0; i < pd_port->local_snk_cap.nr; i++) { + pr_info("%s %d: 0x%08x\n", __func__, i, + pd_port->local_snk_cap.pdos[i]); + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + if (PDO_TYPE(pd_port->local_snk_cap.pdos[i]) != + PDO_TYPE_APDO) + pd_port->local_snk_cap_nr_pd20++; +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + } + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + pd_port->local_snk_cap_nr_pd30 = pd_port->local_snk_cap.nr; +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + ret = of_property_read_u32(np, "pd,id-vdo-size", + (u32 *)&pd_port->id_vdo_nr); + if (ret < 0) + pr_err("%s get id vdo size fail\n", __func__); + ret = of_property_read_u32_array(np, "pd,id-vdo-data", + (u32 *)pd_port->id_vdos, pd_port->id_vdo_nr); + if (ret < 0) + pr_err("%s get id vdo data fail\n", __func__); + + pr_info("%s id vdos data =\n", __func__); + for (i = 0; i < pd_port->id_vdo_nr; i++) + pr_info("%s %d: 0x%08x\n", __func__, i, + pd_port->id_vdos[i]); + + pd_port->id_header = pd_port->id_vdos[0]; + + val = DPM_CHARGING_POLICY_MAX_POWER_LVIC; + if (of_property_read_u32(np, "pd,charging_policy", &val) < 0) + pr_info("%s get charging policy fail\n", __func__); + + pd_port->dpm_charging_policy = val; + pd_port->dpm_charging_policy_default = val; + pr_info("%s charging_policy = %d\n", __func__, val); + +#ifdef CONFIG_USB_PD_REV30_BAT_INFO + ret = pd_parse_pdata_bats(pd_port, np); + if (ret < 0) + return ret; +#endif /* CONFIG_USB_PD_REV30_BAT_INFO */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_AUTHORITY + ret = pd_parse_pdata_countries(pd_port, np); + if (ret < 0) + return ret; +#endif /* CONFIG_USB_PD_REV30_COUNTRY_AUTHORITY */ + + pd_parse_pdata_src_cap_ext(pd_port, np); + pd_parse_pdata_mfrs(pd_port, np); + } + + return 0; +} + +static const struct { + const char *prop_name; + uint32_t val; +} supported_dpm_caps[] = { + {"local_dr_power", DPM_CAP_LOCAL_DR_POWER}, + {"local_dr_data", DPM_CAP_LOCAL_DR_DATA}, + {"local_ext_power", DPM_CAP_LOCAL_EXT_POWER}, + {"local_usb_comm", DPM_CAP_LOCAL_USB_COMM}, + {"local_usb_suspend", DPM_CAP_LOCAL_USB_SUSPEND}, + {"local_high_cap", DPM_CAP_LOCAL_HIGH_CAP}, + {"local_give_back", DPM_CAP_LOCAL_GIVE_BACK}, + {"local_no_suspend", DPM_CAP_LOCAL_NO_SUSPEND}, + {"local_vconn_supply", DPM_CAP_LOCAL_VCONN_SUPPLY}, + + {"attemp_discover_cable_dfp", DPM_CAP_ATTEMP_DISCOVER_CABLE_DFP}, + {"attemp_enter_dp_mode", DPM_CAP_ATTEMP_ENTER_DP_MODE}, + {"attemp_discover_cable", DPM_CAP_ATTEMP_DISCOVER_CABLE}, + {"attemp_discover_id", DPM_CAP_ATTEMP_DISCOVER_ID}, + {"attemp_discover_svid", DPM_CAP_ATTEMP_DISCOVER_SVID}, + + {"pr_reject_as_source", DPM_CAP_PR_SWAP_REJECT_AS_SRC}, + {"pr_reject_as_sink", DPM_CAP_PR_SWAP_REJECT_AS_SNK}, + {"pr_check_gp_source", DPM_CAP_PR_SWAP_CHECK_GP_SRC}, + {"pr_check_gp_sink", DPM_CAP_PR_SWAP_CHECK_GP_SNK}, + + {"dr_reject_as_dfp", DPM_CAP_DR_SWAP_REJECT_AS_DFP}, + {"dr_reject_as_ufp", DPM_CAP_DR_SWAP_REJECT_AS_UFP}, +}; + +static void pd_core_power_flags_init(struct pd_port *pd_port) +{ + uint32_t src_flag, snk_flag, val; + struct device_node *np; + int i; + struct pd_port_power_caps *snk_cap = &pd_port->local_snk_cap; + struct pd_port_power_caps *src_cap = + &pd_port->local_src_cap_default; + + np = of_find_node_by_name(pd_port->tcpc->dev.of_node, "dpm_caps"); + + for (i = 0; i < ARRAY_SIZE(supported_dpm_caps); i++) { + if (of_property_read_bool(np, + supported_dpm_caps[i].prop_name)) + pd_port->dpm_caps |= + supported_dpm_caps[i].val; + pr_info("dpm_caps: %s\n", + supported_dpm_caps[i].prop_name); + } + + if (of_property_read_u32(np, "pr_check", &val) == 0) + pd_port->dpm_caps |= DPM_CAP_PR_CHECK_PROP(val); + else + pr_err("%s get pr_check data fail\n", __func__); + + if (of_property_read_u32(np, "dr_check", &val) == 0) + pd_port->dpm_caps |= DPM_CAP_DR_CHECK_PROP(val); + else + pr_err("%s get dr_check data fail\n", __func__); + + pr_info("dpm_caps = 0x%08x\n", pd_port->dpm_caps); + + src_flag = 0; + if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) + src_flag |= PDO_FIXED_DUAL_ROLE; + + if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_DATA) + src_flag |= PDO_FIXED_DATA_SWAP; + + if (pd_port->dpm_caps & DPM_CAP_LOCAL_EXT_POWER) + src_flag |= PDO_FIXED_EXTERNAL; + + if (pd_port->dpm_caps & DPM_CAP_LOCAL_USB_COMM) + src_flag |= PDO_FIXED_COMM_CAP; + + snk_flag = src_flag; + + if (pd_port->dpm_caps & DPM_CAP_LOCAL_USB_SUSPEND) + src_flag |= PDO_FIXED_SUSPEND; + + if (pd_port->dpm_caps & DPM_CAP_LOCAL_HIGH_CAP) + snk_flag |= PDO_FIXED_HIGH_CAP; + + snk_cap->pdos[0] |= snk_flag; + src_cap->pdos[0] |= src_flag; +} + +#ifdef CONFIG_RECV_BAT_ABSENT_NOTIFY +static void fg_bat_absent_work(struct work_struct *work) +{ + struct pd_port *pd_port = container_of(work, struct pd_port, + fg_bat_work); + struct tcpc_device *tcpc = pd_port->tcpc; + int ret = 0; + + ret = tcpm_shutdown(tcpc); + if (ret < 0) + pr_notice("%s: tcpm shutdown fail\n", __func__); +} +#endif /* ONFIG_RECV_BAT_ABSENT_NOTIFY */ + +int pd_core_init(struct tcpc_device *tcpc) +{ + struct pd_port *pd_port = &tcpc->pd_port; + int ret; + + mutex_init(&pd_port->pd_lock); + +#ifdef CONFIG_USB_PD_BLOCK_TCPM + mutex_init(&pd_port->tcpm_bk_lock); + init_waitqueue_head(&pd_port->tcpm_bk_wait_que); +#endif /* CONFIG_USB_PD_BLOCK_TCPM */ + + pd_port->tcpc = tcpc; + pd_port->pe_pd_state = PE_IDLE2; + pd_port->cap_miss_match = 0; /* For src_cap miss match */ + + ret = pd_parse_pdata(pd_port); + if (ret < 0) + return ret; + + pd_core_power_flags_init(pd_port); + + pd_dpm_core_init(pd_port); + +#ifdef CONFIG_RECV_BAT_ABSENT_NOTIFY + INIT_WORK(&pd_port->fg_bat_work, fg_bat_absent_work); +#endif /* CONFIG_RECV_BAT_ABSENT_NOTIFY */ + + PE_INFO("%s\n", __func__); + return 0; +} + +void pd_extract_rdo_power(uint32_t rdo, uint32_t pdo, + uint32_t *op_curr, uint32_t *max_curr) +{ + uint32_t op_power, max_power, vmin; + + switch (pdo & PDO_TYPE_MASK) { + case PDO_TYPE_FIXED: + case PDO_TYPE_VARIABLE: + *op_curr = RDO_FIXED_VAR_EXTRACT_OP_CURR(rdo); + *max_curr = RDO_FIXED_VAR_EXTRACT_MAX_CURR(rdo); + break; + + case PDO_TYPE_BATTERY: /* TODO: check it later !! */ + vmin = PDO_BATT_EXTRACT_MIN_VOLT(pdo); + op_power = RDO_BATT_EXTRACT_OP_POWER(rdo); + max_power = RDO_BATT_EXTRACT_MAX_POWER(rdo); + + *op_curr = op_power / vmin; + *max_curr = max_power / vmin; + break; + +#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE + case PDO_TYPE_APDO: + *op_curr = RDO_APDO_EXTRACT_OP_MA(rdo); + *max_curr = RDO_APDO_EXTRACT_OP_MA(rdo); + break; +#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */ + + default: + *op_curr = *max_curr = 0; + break; + } +} + +uint32_t pd_reset_pdo_power(struct tcpc_device *tcpc, + uint32_t pdo, uint32_t imax) +{ + uint32_t ioper; + + switch (pdo & PDO_TYPE_MASK) { + case PDO_TYPE_FIXED: + ioper = PDO_FIXED_EXTRACT_CURR(pdo); + if (ioper > imax) + return PDO_FIXED_RESET_CURR(pdo, imax); + break; + + case PDO_TYPE_VARIABLE: + ioper = PDO_VAR_EXTRACT_CURR(pdo); + if (ioper > imax) + return PDO_VAR_RESET_CURR(pdo, imax); + break; + + case PDO_TYPE_BATTERY: + /* TODO: check it later !! */ + PD_ERR("No Support\n"); + break; + +#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE + case PDO_TYPE_APDO: + /* TODO: check it later !! */ + break; +#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */ + } + return pdo; +} + +uint32_t pd_get_cable_curr_lvl(struct pd_port *pd_port) +{ + return PD_VDO_CABLE_CURR( + pd_port->pe_data.cable_vdos[VDO_DISCOVER_ID_CABLE]); +} + +uint32_t pd_get_cable_current_limit(struct pd_port *pd_port) +{ + switch (pd_get_cable_curr_lvl(pd_port)) { + case CABLE_CURR_1A5: + return 1500; + case CABLE_CURR_5A: + return 5000; + default: + case CABLE_CURR_3A: + return 3000; + } +} + +static inline bool pd_is_cable_communication_available( + struct pd_port *pd_port) +{ + /* + * After pr_swap or fr_swap, + * the source (must be Vconn SRC) can communicate with Cable, + * the sink doesn't communicate with cable even if it's DFP. + * + * When an Explicit Contract is in place, + * Only the Vconn SRC can communicate with Cable. + */ + +#ifdef CONFIG_USB_PD_REV30_DISCOVER_CABLE_WITH_VCONN + if (pd_check_rev30(pd_port) && (!pd_port->vconn_role)) + return false; +#endif /* CONFIG_USB_PD_REV30_DISCOVER_CABLE_WITH_VCONN */ + + return true; +} + +bool pd_is_reset_cable(struct pd_port *pd_port) +{ + if (!dpm_reaction_check(pd_port, DPM_REACTION_CAP_RESET_CABLE)) + return false; + + return pd_is_cable_communication_available(pd_port); +} + +bool pd_is_discover_cable(struct pd_port *pd_port) +{ + if (!dpm_reaction_check(pd_port, DPM_REACTION_CAP_DISCOVER_CABLE)) + return false; + + if (pd_port->pe_data.discover_id_counter >= PD_DISCOVER_ID_COUNT) { + dpm_reaction_clear(pd_port, + DPM_REACTION_DISCOVER_CABLE | + DPM_REACTION_CAP_DISCOVER_CABLE); + return false; + } + + return pd_is_cable_communication_available(pd_port); +} + +void pd_reset_svid_data(struct pd_port *pd_port) +{ + uint8_t i; + struct svdm_svid_data *svid_data; + + for (i = 0; i < pd_port->svid_data_cnt; i++) { + svid_data = &pd_port->svid_data[i]; + svid_data->exist = false; + svid_data->remote_mode.mode_cnt = 0; + svid_data->active_mode = 0; + } +} + +#define PE_RESET_MSG_ID(pd_port, sop) { \ + pd_port->pe_data.msg_id_tx[sop] = 0; \ + pd_port->pe_data.msg_id_rx[sop] = PD_MSG_ID_MAX; \ +} + +int pd_reset_protocol_layer(struct pd_port *pd_port, bool sop_only) +{ + struct pe_data *pe_data = &pd_port->pe_data; + + pd_port->state_machine = PE_STATE_MACHINE_NORMAL; + + pd_notify_pe_reset_protocol(pd_port); + +#ifdef CONFIG_USB_PD_PE_SOURCE + pe_data->cap_counter = 0; +#endif /* CONFIG_USB_PD_PE_SOURCE */ + + pe_data->explicit_contract = false; + pe_data->local_selected_cap = 0; + pe_data->remote_selected_cap = 0; + pe_data->during_swap = 0; + pd_port->cap_miss_match = 0; + +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE + pe_data->remote_alert = 0; +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ + +#ifdef CONFIG_USB_PD_DFP_FLOW_DELAY_RESET + if (pe_data->pd_prev_connected) + dpm_reaction_set(pd_port, DPM_REACTION_DFP_FLOW_DELAY); +#endif /* CONFIG_USB_PD_DFP_FLOW_DELAY_RESET */ + +#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID + dpm_reaction_clear(pd_port, DPM_REACTION_RETURN_VCONN_SRC); +#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */ + + PE_RESET_MSG_ID(pd_port, TCPC_TX_SOP); + + if (!sop_only) { + pd_port->request_i = -1; + pd_port->request_v = TCPC_VBUS_SINK_5V; + + PE_RESET_MSG_ID(pd_port, TCPC_TX_SOP_PRIME); + PE_RESET_MSG_ID(pd_port, TCPC_TX_SOP_PRIME_PRIME); + } + +#ifdef CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP + pd_port->msg_id_pr_swap_last = 0xff; +#endif /* CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP */ + + return 0; +} + +int pd_set_rx_enable(struct pd_port *pd_port, uint8_t enable) +{ + pd_port->rx_cap = enable; + return tcpci_set_rx_enable(pd_port->tcpc, enable); +} + +int pd_enable_vbus_valid_detection(struct pd_port *pd_port, bool wait_valid) +{ + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + PE_DBG("WaitVBUS=%d\n", wait_valid); + pd_notify_pe_wait_vbus_once(pd_port, + wait_valid ? PD_WAIT_VBUS_VALID_ONCE : + PD_WAIT_VBUS_INVALID_ONCE); + return 0; +} + +int pd_enable_vbus_safe0v_detection(struct pd_port *pd_port) +{ + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + PE_DBG("WaitVSafe0V\n"); + pd_notify_pe_wait_vbus_once(pd_port, PD_WAIT_VBUS_SAFE0V_ONCE); + return 0; +} + +int pd_enable_vbus_stable_detection(struct pd_port *pd_port) +{ + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + PE_DBG("WaitVStable\n"); + pd_notify_pe_wait_vbus_once(pd_port, PD_WAIT_VBUS_STABLE_ONCE); + return 0; +} + +static inline int pd_update_msg_header(struct pd_port *pd_port) +{ + return tcpci_set_msg_header(pd_port->tcpc, + pd_port->power_role, pd_port->data_role); +} + +int pd_set_data_role(struct pd_port *pd_port, uint8_t dr) +{ + int ret = 0; + + if (dr == pd_port->data_role) + return ret; + + pd_port->data_role = dr; + ret = pd_update_msg_header(pd_port); + if (ret < 0) + return ret; + +#ifdef CONFIG_DUAL_ROLE_USB_INTF + /* dual role usb--> 0:ufp, 1:dfp */ + pd_port->tcpc->dual_role_mode = pd_port->data_role; + /* dual role usb --> 0: Device, 1: Host */ + pd_port->tcpc->dual_role_dr = !(pd_port->data_role); + dual_role_instance_changed(pd_port->tcpc->dr_usb); +#endif /* CONFIG_DUAL_ROLE_USB_INTF */ + + tcpci_notify_role_swap(pd_port->tcpc, TCP_NOTIFY_DR_SWAP, dr); + return ret; +} + +int pd_set_power_role(struct pd_port *pd_port, uint8_t pr) +{ + int ret = 0; + + if (pr == pd_port->power_role) + return ret; + + pd_port->power_role = pr; + ret = pd_update_msg_header(pd_port); + if (ret < 0) + return ret; + + pd_notify_pe_pr_changed(pd_port); + +#ifdef CONFIG_DUAL_ROLE_USB_INTF + /* 0:sink, 1: source */ + pd_port->tcpc->dual_role_pr = !(pd_port->power_role); + dual_role_instance_changed(pd_port->tcpc->dr_usb); +#endif /* CONFIG_DUAL_ROLE_USB_INTF */ + + tcpci_notify_role_swap(pd_port->tcpc, TCP_NOTIFY_PR_SWAP, pr); + return ret; +} + +static void pd_init_spec_revision(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_REV30_SYNC_SPEC_REV + if (pd_port->tcpc->tcpc_flags & TCPC_FLAGS_PD_REV30) { + pd_port->pd_revision[0] = PD_REV30; + pd_port->pd_revision[1] = PD_REV30; + } else { + pd_port->pd_revision[0] = PD_REV20; + pd_port->pd_revision[1] = PD_REV20; + } +#endif /* CONFIG_USB_PD_REV30_SYNC_SPEC_REV */ +} + +int pd_init_message_hdr(struct pd_port *pd_port, bool act_as_sink) +{ + if (act_as_sink) { + pd_port->power_role = PD_ROLE_SINK; + pd_port->data_role = PD_ROLE_UFP; + pd_port->vconn_role = PD_ROLE_VCONN_OFF; + } else { + pd_port->power_role = PD_ROLE_SOURCE; + pd_port->data_role = PD_ROLE_DFP; + pd_port->vconn_role = PD_ROLE_VCONN_ON; + } + + pd_init_spec_revision(pd_port); + return pd_update_msg_header(pd_port); +} + +int pd_set_vconn(struct pd_port *pd_port, uint8_t role) +{ + int ret = 0; + bool enable = !!(role & PD_ROLE_VCONN_ON); + bool en_role = role != PD_ROLE_VCONN_OFF; + bool en_role_old = pd_port->vconn_role != PD_ROLE_VCONN_OFF; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + PE_DBG("%s:%d\n", __func__, role); + +#ifdef CONFIG_USB_PD_VCONN_SAFE5V_ONLY + if (pd_port->pe_data.vconn_highv_prot) { + PE_DBG("VC_OVER5V\n"); + return -EPERM; + } +#endif /* CONFIG_USB_PD_VCONN_SAFE5V_ONLY */ + + if (role == pd_port->vconn_role) + goto out; + + pd_port->vconn_role = role; + ret = tcpci_set_vconn(tcpc, enable); + if (ret < 0) + return ret; + + if (en_role != en_role_old) { + tcpci_notify_role_swap(tcpc, TCP_NOTIFY_VCONN_SWAP, en_role); +#ifdef CONFIG_DUAL_ROLE_USB_INTF + tcpc->dual_role_vconn = en_role; + dual_role_instance_changed(tcpc->dr_usb); +#endif /* CONFIG_DUAL_ROLE_USB_INTF */ + } +out: +#ifdef CONFIG_USB_PD_VCONN_STABLE_DELAY + if (role == PD_ROLE_VCONN_DYNAMIC_ON) + pd_restart_timer(pd_port, PD_TIMER_VCONN_STABLE); +#endif /* CONFIG_USB_PD_VCONN_STABLE_DELAY */ + + if (!enable) + PE_RESET_MSG_ID(pd_port, TCPC_TX_SOP_PRIME); + + return ret; +} + +static inline int pd_reset_modal_operation(struct pd_port *pd_port) +{ + uint8_t i; + struct svdm_svid_data *svid_data; + + for (i = 0; i < pd_port->svid_data_cnt; i++) { + svid_data = &pd_port->svid_data[i]; + + if (svid_data->active_mode) { + svid_data->active_mode = 0; + tcpci_exit_mode(pd_port->tcpc, svid_data->svid); + } + } + + pd_port->pe_data.modal_operation = false; + return 0; +} + +int pd_reset_local_hw(struct pd_port *pd_port) +{ + uint8_t dr; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + pd_notify_pe_transit_to_default(pd_port); + pd_unlock_msg_output(pd_port); + + pd_reset_pe_timer(pd_port); + pd_set_rx_enable(pd_port, PD_RX_CAP_PE_HARDRESET); + + pd_port->pe_data.explicit_contract = false; + pd_port->pe_data.pd_connected = false; + pd_port->pe_data.pe_ready = false; + +#ifdef CONFIG_USB_PD_VCONN_SAFE5V_ONLY + pd_port->pe_data.vconn_highv_prot = false; + pd_port->pe_data.vconn_highv_prot_role = PD_ROLE_VCONN_OFF; +#endif /* CONFIG_USB_PD_VCONN_SAFE5V_ONLY */ + +#ifdef CONFIG_USB_PD_RESET_CABLE + dpm_reaction_clear(pd_port, DPM_REACTION_CAP_RESET_CABLE); +#endif /* CONFIG_USB_PD_RESET_CABLE */ + + pd_reset_modal_operation(pd_port); + + pd_set_vconn(pd_port, PD_ROLE_VCONN_OFF); + + if (pd_port->power_role == PD_ROLE_SINK) + dr = PD_ROLE_UFP; + else + dr = PD_ROLE_DFP; + + pd_port->state_machine = PE_STATE_MACHINE_NORMAL; + + pd_set_data_role(pd_port, dr); + pd_init_spec_revision(pd_port); + pd_dpm_notify_pe_hardreset(pd_port); + PE_DBG("reset_local_hw\n"); + + return 0; +} + +int pd_enable_bist_test_mode(struct pd_port *pd_port, bool en) +{ + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + PE_DBG("bist_test_mode=%d\n", en); + return tcpci_set_bist_test_mode(tcpc, en); +} + +/* ---- Handle PD Message ----*/ + +int pd_handle_soft_reset(struct pd_port *pd_port) +{ + PE_STATE_RECV_SOFT_RESET(pd_port); + + pd_reset_protocol_layer(pd_port, true); + pd_notify_tcp_event_buf_reset(pd_port, TCP_DPM_RET_DROP_RECV_SRESET); + return pd_send_sop_ctrl_msg(pd_port, PD_CTRL_ACCEPT); +} + +void pd_handle_first_pd_command(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_REV30 + pd_sync_sop_spec_revision(pd_port); +#endif /* CONFIG_USB_PD_REV30 */ + + pd_port->pe_data.pd_connected = true; + pd_port->pe_data.pd_prev_connected = true; +} + +void pd_handle_hard_reset_recovery(struct pd_port *pd_port) +{ + /* Stop NoResponseTimer and reset HardResetCounter to zero */ + pd_port->pe_data.hard_reset_counter = 0; + pd_disable_timer(pd_port, PD_TIMER_NO_RESPONSE); + +#ifdef CONFIG_USB_PD_RENEGOTIATION_COUNTER + pd_port->pe_data.renegotiation_count++; +#endif /* CONFIG_USB_PD_RENEGOTIATION_COUNTER */ + +#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER + pd_port->pe_data.recv_hard_reset_count = 0; +#endif /* CONFIG_USB_PD_RECV_HRESET_COUNTER */ + + pd_notify_pe_hard_reset_completed(pd_port); +} + +/* ---- Send PD Message ----*/ + +int pd_send_message(struct pd_port *pd_port, uint8_t sop_type, + uint8_t msg, bool ext, uint16_t count, const uint32_t *data) +{ + int ret; + uint8_t msg_id; + uint16_t msg_hdr; + uint16_t msg_hdr_private; + uint8_t pd_rev = pd_get_rev(pd_port, sop_type); + uint8_t type = PD_TX_STATE_WAIT_CRC_PD; + struct pe_data *pe_data = &pd_port->pe_data; + struct tcpc_device *tcpc = pd_port->tcpc; + + if (tcpc->typec_attach_old == 0) { + PE_DBG("[SendMsg] Unattached\n"); + return 0; + } + + if (tcpc->pd_hard_reset_event_pending) { + PE_DBG("[SendMsg] HardReset Pending"); + return 0; + } + + if (sop_type == TCPC_TX_SOP) + msg_hdr_private = PD_HEADER_ROLE( + pd_port->power_role, pd_port->data_role); + else + msg_hdr_private = 0; + +#ifdef CONFIG_USB_PD_REV30 + if (pd_rev >= PD_REV30) + tcpc->pd_retry_count = PD30_RETRY_COUNT; + else + tcpc->pd_retry_count = PD_RETRY_COUNT; +#endif /* CONFIG_USB_PD_REV30 */ + + msg_id = pe_data->msg_id_tx[sop_type]; + msg_hdr = PD_HEADER_COMMON( + msg, pd_rev, msg_id, count, ext, msg_hdr_private); + + /* ext-cmd 15 is reserved */ + if ((count > 0) && (msg == PD_DATA_VENDOR_DEF)) + type = PD_TX_STATE_WAIT_CRC_VDM; + + pe_data->msg_id_tx[sop_type] = (msg_id+1) % PD_MSG_ID_MAX; + + pd_notify_pe_transmit_msg(pd_port, type); + ret = tcpci_transmit(pd_port->tcpc, sop_type, msg_hdr, data); + if (ret < 0) + PD_ERR("[SendMsg] Failed, %d\n", ret); + + return ret; +} + +int pd_send_data_msg(struct pd_port *pd_port, + uint8_t sop_type, uint8_t msg, uint8_t cnt, uint32_t *payload) +{ + return pd_send_message(pd_port, + sop_type, msg, false, cnt, payload); +} + +int pd_send_sop_ctrl_msg( + struct pd_port *pd_port, uint8_t msg) +{ + return pd_send_message( + pd_port, TCPC_TX_SOP, msg, false, 0, NULL); +} + +int pd_send_sop_prime_ctrl_msg(struct pd_port *pd_port, uint8_t msg) +{ + return pd_send_message( + pd_port, TCPC_TX_SOP_PRIME, msg, false, 0, NULL); +} + +int pd_send_sop_data_msg(struct pd_port *pd_port, + uint8_t msg, uint8_t cnt, const uint32_t *payload) +{ + return pd_send_message(pd_port, + TCPC_TX_SOP, msg, false, cnt, payload); +} + +int pd_reply_wait_reject_msg(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + return pd_reply_wait_reject_msg_no_resp(pd_port); +} + +int pd_reply_wait_reject_msg_no_resp(struct pd_port *pd_port) +{ + struct pd_event *pd_event = pd_get_curr_pd_event(pd_port); + uint8_t msg = pd_event->msg_sec == PD_DPM_NAK_REJECT ? + PD_CTRL_REJECT : PD_CTRL_WAIT; + + return pd_send_sop_ctrl_msg(pd_port, msg); +} + +#ifdef CONFIG_USB_PD_REV30 +int pd_send_ext_msg(struct pd_port *pd_port, + uint8_t sop_type, uint8_t msg, bool request, + uint8_t chunk_nr, uint8_t size, const uint8_t *data) +{ + uint8_t cnt; + uint32_t payload[PD_DATA_OBJ_SIZE]; + + uint16_t *ext_hdr = (uint16_t *)payload; + + cnt = ((size + PD_EXT_HEADER_PAYLOAD_INDEX - 1) / 4) + 1; + payload[cnt-1] = 0; /* Padding Byte should be 0 */ + + *ext_hdr = PD_EXT_HEADER_CK(size, request, chunk_nr, true); + memcpy(&ext_hdr[1], data, size); + + return pd_send_message(pd_port, sop_type, msg, true, cnt, payload); +} +#endif /* CONFIG_USB_PD_REV30 */ + +#ifdef CONFIG_USB_PD_RESET_CABLE +int pd_send_cable_soft_reset(struct pd_port *pd_port) +{ + /* reset_protocol_layer */ + PE_RESET_MSG_ID(pd_port, TCPC_TX_SOP_PRIME); + dpm_reaction_clear(pd_port, DPM_REACTION_CAP_RESET_CABLE); + + return pd_send_sop_prime_ctrl_msg(pd_port, PD_CTRL_SOFT_RESET); +} +#endif /* CONFIG_USB_PD_RESET_CABLE */ + +int pd_send_soft_reset(struct pd_port *pd_port) +{ + PE_STATE_SEND_SOFT_RESET(pd_port); + + pd_reset_protocol_layer(pd_port, true); + pd_notify_tcp_event_buf_reset(pd_port, TCP_DPM_RET_DROP_SENT_SRESET); + return pd_send_sop_ctrl_msg(pd_port, PD_CTRL_SOFT_RESET); +} + +int pd_send_hard_reset(struct pd_port *pd_port) +{ + struct tcpc_device *tcpc = pd_port->tcpc; + + PE_DBG("Send HARD Reset\n"); + __pm_wakeup_event(tcpc->attach_wake_lock, 6000); + + pd_port->pe_data.hard_reset_counter++; + pd_notify_pe_send_hard_reset(pd_port); + + return tcpci_transmit(tcpc, TCPC_TX_HARD_RESET, 0, NULL); +} + +int pd_send_bist_mode2(struct pd_port *pd_port) +{ + int ret = 0; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + pd_notify_tcp_event_buf_reset(pd_port, TCP_DPM_RET_DROP_SEND_BIST); + +#ifdef CONFIG_USB_PD_TRANSMIT_BIST2 + TCPC_DBG("BIST_MODE_2\n"); + ret = tcpci_transmit(tcpc, TCPC_TX_BIST_MODE_2, 0, NULL); +#else + ret = tcpci_set_bist_carrier_mode(tcpc, 1 << 2); +#endif + + return ret; +} + +int pd_disable_bist_mode2(struct pd_port *pd_port) +{ +#ifndef CONFIG_USB_PD_TRANSMIT_BIST2 + return tcpci_set_bist_carrier_mode( + pd_port->tcpc, 0); +#else + return 0; +#endif +} + +/* ---- Send / Reply VDM Command ----*/ + +int pd_send_svdm_request(struct pd_port *pd_port, + uint8_t sop_type, uint16_t svid, uint8_t vdm_cmd, + uint8_t obj_pos, uint8_t cnt, uint32_t *data_obj, + uint32_t timer_id) +{ +#ifdef CONFIG_USB_PD_STOP_SEND_VDM_IF_RX_BUSY + int rv; + uint32_t alert_status; +#endif /* CONFIG_USB_PD_STOP_SEND_VDM_IF_RX_BUSY */ + + int ret; + uint8_t ver = SVDM_REV10; + uint32_t payload[PD_DATA_OBJ_SIZE]; + + if (cnt > VDO_MAX_NR) { + PD_BUG_ON(1); + return -EINVAL; + } + + if (pd_get_rev(pd_port, sop_type) >= PD_REV30) + ver = SVDM_REV20; + + payload[0] = VDO_S(svid, ver, CMDT_INIT, vdm_cmd, obj_pos); + memcpy(&payload[1], data_obj, sizeof(uint32_t) * cnt); + +#ifdef CONFIG_USB_PD_STOP_SEND_VDM_IF_RX_BUSY + rv = tcpci_get_alert_status(pd_port->tcpc, &alert_status); + if (rv) + return rv; + + if (alert_status & TCPC_REG_ALERT_RX_STATUS) { + PE_DBG("RX Busy, stop send VDM\n"); + return 0; + } +#endif /* CONFIG_USB_PD_STOP_SEND_VDM_IF_RX_BUSY */ + + ret = pd_send_data_msg( + pd_port, sop_type, PD_DATA_VENDOR_DEF, 1+cnt, payload); + + if (ret == 0 && timer_id != 0) + VDM_STATE_RESPONSE_CMD(pd_port, timer_id); + + return ret; +} + +int pd_reply_svdm_request(struct pd_port *pd_port, + uint8_t reply, uint8_t cnt, uint32_t *data_obj) +{ +#ifdef CONFIG_USB_PD_STOP_REPLY_VDM_IF_RX_BUSY + int rv; + uint32_t alert_status; +#endif /* CONFIG_USB_PD_STOP_REPLY_VDM_IF_RX_BUSY */ + uint8_t ver = SVDM_REV10; + uint32_t payload[PD_DATA_OBJ_SIZE]; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + PD_BUG_ON(cnt > VDO_MAX_NR); + + if (pd_check_rev30(pd_port)) + ver = SVDM_REV20; + + payload[0] = VDO_REPLY(ver, reply, pd_get_msg_vdm_hdr(pd_port)); + + if (cnt > 0 && cnt <= PD_DATA_OBJ_SIZE - 1) { + PD_BUG_ON(data_obj == NULL); + memcpy(&payload[1], data_obj, sizeof(uint32_t) * cnt); + } + +#ifdef CONFIG_USB_PD_STOP_REPLY_VDM_IF_RX_BUSY + rv = tcpci_get_alert_status(tcpc, &alert_status); + if (rv) + return rv; + + if (alert_status & TCPC_REG_ALERT_RX_STATUS) { + PE_DBG("RX Busy, stop reply VDM\n"); + return 0; + } +#endif /* CONFIG_USB_PD_STOP_REPLY_VDM_IF_RX_BUSY */ + + if (reply != CMDT_RSP_ACK) + PE_INFO("VDM_NAK_BUSY\n"); + else + PE_INFO("VDM_ACK\n"); + + VDM_STATE_REPLY_SVDM_REQUEST(pd_port); + + return pd_send_sop_data_msg(pd_port, + PD_DATA_VENDOR_DEF, 1+cnt, payload); +} + +#ifdef CONFIG_USB_PD_CUSTOM_VDM + +int pd_send_custom_vdm(struct pd_port *pd_port, uint8_t sop_type) +{ + return pd_send_data_msg(pd_port, + sop_type, PD_DATA_VENDOR_DEF, + pd_port->uvdm_cnt, pd_port->uvdm_data); +} + +int pd_reply_custom_vdm(struct pd_port *pd_port, uint8_t sop_type, + uint8_t cnt, uint32_t *payload) +{ + VDM_STATE_REPLY_SVDM_REQUEST(pd_port); + + return pd_send_data_msg(pd_port, + sop_type, PD_DATA_VENDOR_DEF, cnt, payload); +} + +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + +void pd_reset_pe_timer(struct pd_port *pd_port) +{ + tcpc_reset_pe_timer(pd_port->tcpc); + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + if (pd_port->request_apdo) { + pd_port->request_apdo = false; + pd_dpm_start_pps_request_thread(pd_port, false); + } +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ +} + +void pd_lock_msg_output(struct pd_port *pd_port) +{ + if (pd_port->msg_output_lock) + return; + pd_port->msg_output_lock = true; + + pd_dbg_info_lock(); +} + +void pd_add_miss_msg(struct pd_port *pd_port,struct pd_event *pd_event, + uint8_t msg) +{ + struct pd_msg *pd_msg = pd_event->pd_msg; + struct pd_msg * miss_msg = NULL; + uint8_t sop_type = 0; + struct pd_event evt = { + .event_type = PD_EVT_CTRL_MSG, + .msg = msg, + .pd_msg = NULL, + }; + if (pd_msg != NULL) { + sop_type = pd_msg->frame_type; + } + pd_put_event(pd_port->tcpc,&evt,true); + miss_msg = pd_alloc_msg(pd_port->tcpc); + if (miss_msg == NULL) { + return; + } + if (pd_msg != NULL) + memcpy(miss_msg,pd_msg,sizeof(struct pd_msg)); + + pd_put_pd_msg_event(pd_port->tcpc,miss_msg); + pd_port->pe_data.msg_id_rx[sop_type]--; + return; +} + +void pd_unlock_msg_output(struct pd_port *pd_port) +{ + if (!pd_port->msg_output_lock) + return; + pd_port->msg_output_lock = false; + + pd_dbg_info_unlock(); +} + +int pd_update_connect_state(struct pd_port *pd_port, uint8_t state) +{ + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_port->pd_connect_state == state) + return 0; + + pd_port->pd_connect_state = state; + PE_INFO("pd_state=%d\n", state); + return tcpci_notify_pd_state(tcpc, state); +} + +int pd_update_vdm_verify_state(struct pd_port *pd_port, uint8_t state) +{ + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_port->pd_vdm_verify_state == state) + return 0; + + pd_port->pd_vdm_verify_state = state; + PE_INFO("pd_vdm_verify_state=%d\n", state); + return tcpci_notify_pd_vdm_verify(tcpc, state); +} + +#ifdef CONFIG_USB_PD_REV30 + +/* + * Collision Avoidance : check tx ok + */ + +#ifndef MIN +#define MIN(a, b) ((a < b) ? (a) : (b)) +#endif + +void pd_set_sink_tx(struct pd_port *pd_port, uint8_t cc) +{ +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (cc == PD30_SINK_TX_OK && + pd_port->pe_data.pd_traffic_control != PD_SINK_TX_OK) { + PE_INFO("sink_tx_ok\n"); + tcpci_lock_typec(tcpc); + tcpci_set_cc(tcpc, cc); + tcpci_unlock_typec(tcpc); + pd_port->pe_data.pd_traffic_control = PD_SINK_TX_OK; + pd_disable_timer(pd_port, PD_TIMER_SINK_TX); + } else if (cc == PD30_SINK_TX_NG && + pd_port->pe_data.pd_traffic_control == PD_SINK_TX_OK) { + PE_INFO("sink_tx_ng\n"); + tcpci_lock_typec(tcpc); + tcpci_set_cc(tcpc, cc); + tcpci_unlock_typec(tcpc); + pd_port->pe_data.pd_traffic_control = PD_SINK_TX_NG; + pd_enable_timer(pd_port, PD_TIMER_SINK_TX); + } +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ +} + +void pd_sync_sop_spec_revision(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_REV30_SYNC_SPEC_REV + uint8_t rev = pd_get_msg_hdr_rev(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (!pd_port->pe_data.pd_connected) { + pd_port->pd_revision[0] = MIN(PD_REV30, rev); + pd_port->pd_revision[1] = MIN(pd_port->pd_revision[1], rev); + + PE_INFO("pd_rev=%d\n", pd_port->pd_revision[0]); + } +#endif /* CONFIG_USB_PD_REV30_SYNC_SPEC_REV */ +} + +void pd_sync_sop_prime_spec_revision(struct pd_port *pd_port, uint8_t rev) +{ +#ifdef CONFIG_USB_PD_REV30_SYNC_SPEC_REV + struct pe_data *pe_data = &pd_port->pe_data; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (!pe_data->cable_rev_discovered) { + pe_data->cable_rev_discovered = true; + pd_port->pd_revision[1] = MIN(pd_port->pd_revision[1], rev); + PE_INFO("cable_rev=%d\n", pd_port->pd_revision[1]); + } +#endif /* CONFIG_USB_PD_REV30_SYNC_SPEC_REV */ +} + +bool pd_is_multi_chunk_msg(struct pd_port *pd_port) +{ + uint16_t size; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_get_msg_hdr_ext(pd_port)) { + size = pd_get_msg_data_size(pd_port); + if (size > MAX_EXTENDED_MSG_CHUNK_LEN) { + PE_INFO("multi_chunk_msg = TRUE (%d)\n", size); + return true; + } + } + + return false; +} + +struct pd_battery_info *pd_get_battery_info( + struct pd_port *pd_port, enum pd_battery_reference ref) +{ +#ifdef CONFIG_USB_PD_REV30_BAT_INFO + if (ref < pd_get_fix_battery_nr(pd_port)) + return &pd_port->fix_bat_info[ref]; +#endif /* CONFIG_USB_PD_REV30_BAT_INFO */ + + /* TODO: for swap battery */ + return NULL; +} + +#endif /* CONFIG_USB_PD_REV30 */ diff --git a/drivers/usb/typec/mmi_tcpc/pd_dbg_info.c b/drivers/usb/typec/mmi_tcpc/pd_dbg_info.c new file mode 100644 index 000000000000..29a3d92350fc --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_dbg_info.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "inc/pd_dbg_info.h" + +#ifdef CONFIG_PD_DBG_INFO + +#define PD_INFO_BUF_SIZE (2048*256) +#define MSG_POLLING_MS 20 + +#define OUT_BUF_MAX (128) +static struct { + int used; + char buf[PD_INFO_BUF_SIZE + 1 + OUT_BUF_MAX]; +} pd_dbg_buffer[2]; + +static struct mutex buff_lock; +static unsigned int using_buf; +static wait_queue_head_t print_out_wait_que; +static atomic_t pending_print_out; +static atomic_t busy = ATOMIC_INIT(0); + +void pd_dbg_info_lock(void) +{ + atomic_inc(&busy); +} + +void pd_dbg_info_unlock(void) +{ + atomic_dec_if_positive(&busy); +} + +static inline bool pd_dbg_print_out(void) +{ + char temp; + int used; + unsigned int index, i; + + mutex_lock(&buff_lock); + index = using_buf; + using_buf ^= 0x01; /* exchange buffer */ + mutex_unlock(&buff_lock); + + used = pd_dbg_buffer[index].used; + + if (used == 0) + return false; + + pd_dbg_buffer[index].buf[used] = '\0'; + + pr_info("///PD dbg info %ud\n", used); + + for (i = 0; i < used; i += OUT_BUF_MAX) { + temp = pd_dbg_buffer[index].buf[OUT_BUF_MAX + i]; + pd_dbg_buffer[index].buf[OUT_BUF_MAX + i] = '\0'; + + while (atomic_read(&busy)) + usleep_range(1000, 2000); + + pr_notice("%s", pd_dbg_buffer[index].buf + i); + pd_dbg_buffer[index].buf[OUT_BUF_MAX + i] = temp; + } + + /* pr_info("PD dbg info///\n"); */ + pd_dbg_buffer[index].used = 0; + msleep(MSG_POLLING_MS); + return true; +} + +static int print_out_thread_fn(void *data) +{ + int ret = 0; + + while (true) { + ret = wait_event_interruptible(print_out_wait_que, + atomic_read(&pending_print_out) || + kthread_should_stop()); + if (kthread_should_stop() || ret) { + pr_notice("%s exits(%d)\n", __func__, ret); + break; + } + do { + atomic_dec_if_positive(&pending_print_out); + } while (pd_dbg_print_out() && !kthread_should_stop()); + } + + return 0; +} + +int pd_dbg_info(const char *fmt, ...) +{ + unsigned int index; + va_list args; + int r; + int used; + u64 ts; + unsigned long rem_usec; + + ts = local_clock(); + rem_usec = do_div(ts, 1000000000) / 1000 / 1000; + va_start(args, fmt); + mutex_lock(&buff_lock); + index = using_buf; + used = pd_dbg_buffer[index].used; + r = snprintf(pd_dbg_buffer[index].buf + used, + PD_INFO_BUF_SIZE - used, "<%5lu.%03lu>", + (unsigned long)ts, rem_usec); + if (r > 0) + used += r; + r = vsnprintf(pd_dbg_buffer[index].buf + used, + PD_INFO_BUF_SIZE - used, fmt, args); + if (r > 0) + used += r; + + if (pd_dbg_buffer[index].used == 0) { + atomic_inc(&pending_print_out); + wake_up(&print_out_wait_que); + } + + pd_dbg_buffer[index].used = used; + mutex_unlock(&buff_lock); + va_end(args); + return r; +} +EXPORT_SYMBOL(pd_dbg_info); + +static struct task_struct *print_out_task; + +int pd_dbg_info_init(void) +{ + pr_info("%s\n", __func__); + mutex_init(&buff_lock); + init_waitqueue_head(&print_out_wait_que); + atomic_set(&pending_print_out, 0); + print_out_task = kthread_run(print_out_thread_fn, NULL, "pd_dbg_info"); + + return 0; +} + +void pd_dbg_info_exit(void) +{ + kthread_stop(print_out_task); + mutex_destroy(&buff_lock); +} + +MODULE_DESCRIPTION("PD Debug Info Module"); +MODULE_AUTHOR("Patrick Chang "); +MODULE_LICENSE("GPL"); + +#endif /* CONFIG_PD_DBG_INFO */ diff --git a/drivers/usb/typec/mmi_tcpc/pd_dpm_alt_mode_dc.c b/drivers/usb/typec/mmi_tcpc/pd_dpm_alt_mode_dc.c new file mode 100644 index 000000000000..c28fe29a8518 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_dpm_alt_mode_dc.c @@ -0,0 +1,603 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * PD Device Policy Manager for Direct Charge + * + * 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. + */ + +#include +#include "inc/tcpci.h" +#include "inc/pd_policy_engine.h" +#include "inc/pd_dpm_core.h" +#include "pd_dpm_prv.h" + +#ifdef CONFIG_USB_POWER_DELIVERY +#ifdef CONFIG_USB_PD_ALT_MODE_RTDC + +#define RTDC_UVDM_EN_UNLOCK 0x2024 +#define RTDC_UVDM_RECV_EN_UNLOCK 0x4024 +#define RTDC_SVDM_PPS_AUTHORIZATION 0x10 + +#define RTDC_VALID_MODE 0x01 +#define RTDC_UVDM_EN_UNLOCK_SUCCESS 0x01 + +void crcbits(uint32_t data, uint32_t *crc, uint32_t *ppolynomial) +{ + uint32_t i, newbit, newword, rl_crc; + + for (i = 0; i < 32; i++) { + newbit = ((*crc >> 31) ^ ((data >> i) & 1)) & 1; + if (newbit) + newword = *ppolynomial; + else + newword = 0; + rl_crc = (*crc << 1) | newbit; + *crc = rl_crc ^ newword; + } +} + +uint32_t crcwrap(uint32_t V) +{ + uint32_t ret = 0, i, j, bit; + + V = ~V; + for (i = 0; i < 32; i++) { + j = 31 - i; + bit = (V >> i) & 1; + ret |= bit << j; + } + + return ret; +} + +static uint32_t dc_get_random_code(void) +{ + uint32_t num; + + get_random_bytes(&num, sizeof(num)); + return num; +} + +static uint32_t dc_get_authorization_code(uint32_t data) +{ + uint32_t dwpolynomial = 0x04C11DB6, dwCrc = 0xFFFFFFFF; + + crcbits(data, &dwCrc, &dwpolynomial); + dwCrc = crcwrap(dwCrc); + return dwCrc; +} + +static inline bool dc_dfp_send_en_unlock(struct pd_port *pd_port, + uint32_t cmd, uint32_t data0, uint32_t data1) +{ + pd_port->uvdm_cnt = 3; + pd_port->uvdm_wait_resp = true; + + pd_port->uvdm_data[0] = PD_UVDM_HDR(USB_VID_DIRECTCHARGE, cmd); + pd_port->uvdm_data[1] = data0; + pd_port->uvdm_data[2] = data1; + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + if (pd_port->pe_data.dc_pps_mode) { + pd_port->uvdm_data[0] = VDO_S( + USB_VID_DIRECTCHARGE, + SVDM_REV20, + CMDT_INIT, + RTDC_SVDM_PPS_AUTHORIZATION, + 0); + } +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + return pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_UVDM); +} + +enum pd_dc_dfp_state { + DC_DFP_NONE = 0, + DC_DFP_DISCOVER_ID, + DC_DFP_DISCOVER_SVIDS, + DC_DFP_DISCOVER_MODES, + DC_DFP_ENTER_MODE, + DC_DFP_EN_UNLOCK1, + DC_DFP_EN_UNLOCK2, + DC_DFP_OPERATION, + +#ifdef RTDC_TA_EMULATE + DC_UFP_T0, + DC_UFP_T1, + DC_UFP_T2, +#endif + + DC_DFP_STATE_NR, + + DC_DFP_ERR = 0X10, + + DC_DFP_ERR_DISCOVER_ID_TYPE, + DC_DFP_ERR_DISCOVER_ID_NAK_TIMEOUT, + + DC_DFP_ERR_DISCOVER_SVID_DC_SID, + DC_DFP_ERR_DISCOVER_SVID_NAK_TIMEOUT, + + DC_DFP_ERR_DISCOVER_CABLE, + + DC_DFP_ERR_DISCOVER_MODE_DC_SID, + DC_DFP_ERR_DISCOVER_MODE_CAP, + DC_DFP_ERR_DISCOVER_MODE_NAK_TIMEROUT, + + DC_DFP_ERR_ENTER_MODE_DC_SID, + DC_DFP_ERR_ENTER_MODE_NAK_TIMEOUT, + + DC_DFP_ERR_EXIT_MODE_DC_SID, + DC_DFP_ERR_EXIT_MODE_NAK_TIMEOUT, + + DC_DFP_ERR_EN_UNLOCK1_FAILED, + DC_DFP_ERR_EN_UNLOCK1_NAK_TIMEOUT, + + DC_DFP_ERR_EN_UNLOCK2_FAILED, + DC_DFP_ERR_EN_UNLOCK2_NAK_TIMEOUT, + + DC_DFP_ERR_PD_REV30, +}; + +#if DC_DBG_ENABLE +static const char * const dc_dfp_state_name[] = { + "dc_dfp_none", + "dc_dfp_discover_id", + "dc_dfp_discover_svids", + "dc_dfp_discover_modes", + "dc_dfp_enter_mode", + "dc_dfp_en_unlock1", + "dc_dfp_en_unlock2", + "dc_dfp_operation", + +#ifdef RTDC_TA_EMULATE + "dc1", + "dc2", + "dc3", +#endif + +}; +#endif /* DC_DBG_ENABLE */ + +void dc_dfp_set_state(struct pd_port *pd_port, uint8_t state) +{ + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + pd_port->dc_dfp_state = state; + + if (pd_port->dc_dfp_state < DC_DFP_STATE_NR) + DC_DBG("%s\n", dc_dfp_state_name[state]); + else + DC_DBG("dc_dfp_stop (%d)\n", state); +} + +bool dc_dfp_start_en_unlock1(struct pd_port *pd_port) +{ + uint32_t rn_code[2]; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + rn_code[0] = dc_get_random_code(); + rn_code[1] = dc_get_random_code(); + pd_port->dc_pass_code = dc_get_authorization_code( + (rn_code[0] & 0xffff) | (rn_code[1] & 0xffff0000)); + + DC_DBG("en_unlock1: 0x%x, 0x%x\n", rn_code[0], rn_code[1]); + + dc_dfp_send_en_unlock( + pd_port, RTDC_UVDM_EN_UNLOCK, rn_code[0], rn_code[1]); + + dc_dfp_set_state(pd_port, DC_DFP_EN_UNLOCK1); + + return true; +} + +#define SVDM_CMD_STATE_MASK(raw) (raw & (0x80df)) +#define SVDM_CMD_STATE(cmd, cmd_type) \ + ((1 << 15) | (cmd & 0x1f) | ((cmd_type & 0x03) << 6)) + +bool dc_dfp_verify_en_unlock1(struct pd_port *pd_port) +{ + uint32_t resp_cmd, expect_resp; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + expect_resp = RTDC_UVDM_RECV_EN_UNLOCK; + resp_cmd = PD_UVDM_HDR_CMD(pd_port->uvdm_data[0]); + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + if (pd_port->pe_data.dc_pps_mode) { + resp_cmd = SVDM_CMD_STATE_MASK(pd_port->uvdm_data[0]); + expect_resp = SVDM_CMD_STATE( + RTDC_SVDM_PPS_AUTHORIZATION, CMDT_RSP_ACK); + } +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + if (resp_cmd != expect_resp) { + DC_INFO("en_unlock1: unexpect resp (0x%x)\n", resp_cmd); + dc_dfp_set_state(pd_port, DC_DFP_ERR_EN_UNLOCK1_FAILED); + return false; + } + + if (pd_port->dc_pass_code != pd_port->uvdm_data[1]) { + DC_INFO("en_unlock1: pass wrong 0x%x 0x%x\n", + pd_port->dc_pass_code, pd_port->uvdm_data[1]); + dc_dfp_set_state(pd_port, DC_DFP_ERR_EN_UNLOCK1_FAILED); + return false; + } + + return true; +} + + +bool dc_dfp_start_en_unlock2(struct pd_port *pd_port) +{ + uint32_t rn_code = dc_get_random_code(); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + pd_port->dc_pass_code = + dc_get_authorization_code(pd_port->uvdm_data[2]); + + DC_DBG("en_unlock2: 0x%x, 0x%x\n", pd_port->dc_pass_code, rn_code); + + dc_dfp_send_en_unlock(pd_port, RTDC_UVDM_EN_UNLOCK, + pd_port->dc_pass_code, rn_code); + + dc_dfp_set_state(pd_port, DC_DFP_EN_UNLOCK2); + + return true; +} + +bool dc_dfp_verify_en_unlock2(struct pd_port *pd_port) +{ + uint32_t resp_cmd, expect_resp; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + expect_resp = RTDC_UVDM_RECV_EN_UNLOCK; + resp_cmd = PD_UVDM_HDR_CMD(pd_port->uvdm_data[0]); + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + if (pd_port->pe_data.dc_pps_mode) { + resp_cmd = SVDM_CMD_STATE_MASK(pd_port->uvdm_data[0]); + expect_resp = SVDM_CMD_STATE( + RTDC_SVDM_PPS_AUTHORIZATION, CMDT_RSP_ACK); + } +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + if (resp_cmd != expect_resp) { + DC_INFO("en_unlock2: unexpect resp (0x%x)\n", resp_cmd); + dc_dfp_set_state(pd_port, DC_DFP_ERR_EN_UNLOCK2_FAILED); + return false; + } + + if (pd_port->uvdm_data[1] != RTDC_UVDM_EN_UNLOCK_SUCCESS) { + DC_INFO("en_unlock2: failed\n"); + dc_dfp_set_state(pd_port, DC_DFP_ERR_EN_UNLOCK2_FAILED); + return false; + } + + return true; +} + +bool dc_dfp_notify_pe_startup( + struct pd_port *pd_port, struct svdm_svid_data *svid_data) +{ + if (!(pd_port->id_vdos[0] & PD_IDH_MODAL_SUPPORT)) + return false; + + if (pd_port->dpm_caps & DPM_CAP_ATTEMP_ENTER_DC_MODE) + dc_dfp_set_state(pd_port, DC_DFP_DISCOVER_ID); + +#ifdef RTDC_TA_EMULATE + dc_dfp_set_state(pd_port, DC_UFP_T0); +#endif + + return true; +} + +int dc_dfp_notify_pe_ready(struct pd_port *pd_port, + struct svdm_svid_data *svid_data) +{ +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + +#ifdef RTDC_TA_EMULATE + if (pd_port->data_role == PD_ROLE_DFP && svid_data->exist) { + pd_put_tcp_pd_event(pd_port, TCP_DPM_EVT_DR_SWAP_AS_UFP); + return 1; + } else { + return 0; + } +#endif + + if (pd_port->data_role != PD_ROLE_DFP) + return 0; + + if (pd_port->dc_dfp_state != DC_DFP_DISCOVER_MODES) + return 0; + +#ifdef CONFIG_USB_PD_RTDC_CHECK_CABLE + if (!pd_port->pe_data.power_cable_present) { + dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_CABLE); + return 0; + } + + if (pd_get_cable_curr_lvl(pd_port) != CABLE_CURR_5A) { + dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_CABLE); + return 0; + } +#endif /* CONFIG_USB_PD_RTDC_CHECK_CABLE */ + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + /* If TA support pd revision30, using standard PPS flow */ + if (pd_check_rev30(pd_port)) { + dc_dfp_set_state(pd_port, DC_DFP_ERR_PD_REV30); + return 0; + } + + if (pd_is_source_support_apdo(pd_port)) { + DC_INFO("pps_mode\n"); + pd_port->pe_data.dc_pps_mode = true; + return dc_dfp_start_en_unlock1(pd_port); + } +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + pd_port->mode_svid = USB_VID_DIRECTCHARGE; + pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_DISCOVER_MODES); + return 1; +} + +bool dc_dfp_notify_discover_id(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack) +{ + if (pd_port->dc_dfp_state != DC_DFP_DISCOVER_ID) + return true; + + if (!ack) { + dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_ID_NAK_TIMEOUT); + return true; + } + + /* TODO: Check device ID or Type */ + + dc_dfp_set_state(pd_port, DC_DFP_DISCOVER_SVIDS); + return true; +} + +bool dc_dfp_notify_discover_svid(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack) +{ + if (pd_port->dc_dfp_state != DC_DFP_DISCOVER_SVIDS) + return false; + + if (!ack) { + dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_SVID_NAK_TIMEOUT); + return false; + } + + if (!svid_data->exist) { + dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_SVID_DC_SID); + return false; + } + + dpm_reaction_set(pd_port, DPM_REACTION_DISCOVER_CABLE_FLOW); + dc_dfp_set_state(pd_port, DC_DFP_DISCOVER_MODES); + return true; +} + +bool dc_dfp_notify_discover_modes(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack) +{ + if (pd_port->dc_dfp_state != DC_DFP_DISCOVER_MODES) + return false; + + if (!ack) { + dc_dfp_set_state(pd_port, + DC_DFP_ERR_DISCOVER_MODE_NAK_TIMEROUT); + return false; + } + + if (svid_data->remote_mode.mode_cnt == 0) { + dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_MODE_DC_SID); + return false; + } + + if (svid_data->remote_mode.mode_vdo[0] != RTDC_VALID_MODE) { + dc_dfp_set_state(pd_port, DC_DFP_ERR_DISCOVER_MODE_CAP); + return false; + } + + pd_port->mode_obj_pos = 1; + dc_dfp_set_state(pd_port, DC_DFP_ENTER_MODE); + pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_ENTER_MODE); + return true; +} + +bool dc_dfp_notify_enter_mode(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, uint8_t ops, bool ack) +{ + if (pd_port->dc_dfp_state != DC_DFP_ENTER_MODE) + return true; + + if (!ack) { + dc_dfp_set_state(pd_port, DC_DFP_ERR_ENTER_MODE_NAK_TIMEOUT); + return true; + } + + if (svid_data->active_mode == 0) { + dc_dfp_set_state(pd_port, DC_DFP_ERR_ENTER_MODE_DC_SID); + return false; + } + + return dc_dfp_start_en_unlock1(pd_port); +} + +bool dc_dfp_notify_exit_mode(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, uint8_t ops) +{ + if (pd_port->dc_dfp_state <= DC_DFP_ENTER_MODE) + return false; + + if (svid_data->svid != USB_VID_DIRECTCHARGE) + return false; + + dc_dfp_set_state(pd_port, DC_DFP_NONE); + return true; +} + +static inline bool dc_dfp_notify_en_unlock1(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack) +{ + if (pd_port->dc_dfp_state != DC_DFP_EN_UNLOCK1) + return false; + + if (!ack) { + dc_dfp_set_state(pd_port, DC_DFP_ERR_EN_UNLOCK1_NAK_TIMEOUT); + return false; + } + + if (!dc_dfp_verify_en_unlock1(pd_port)) + return false; + + return dc_dfp_start_en_unlock2(pd_port); +} + +static inline bool dc_dfp_notify_en_unlock2(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack) +{ + if (pd_port->dc_dfp_state != DC_DFP_EN_UNLOCK2) + return false; + + if (!ack) { + dc_dfp_set_state(pd_port, DC_DFP_ERR_EN_UNLOCK2_NAK_TIMEOUT); + return false; + } + + if (!dc_dfp_verify_en_unlock2(pd_port)) + return false; + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK +#ifdef CONFIG_USB_PD_REV30_SYNC_SPEC_REV + if (pd_port->pe_data.dc_pps_mode) + pd_port->pd_revision[0] = PD_REV30; +#endif /* CONFIG_USB_PD_REV30_SYNC_SPEC_REV */ +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + dc_dfp_set_state(pd_port, DC_DFP_OPERATION); + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + /* PPS shoult not use en_unlock to notify system */ + if (pd_port->pe_data.dc_pps_mode) + return true; +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + tcpci_dc_notify_en_unlock(pd_port->tcpc); + return true; +} + +bool dc_dfp_notify_uvdm(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack) +{ + switch (pd_port->dc_dfp_state) { + case DC_DFP_EN_UNLOCK1: + dc_dfp_notify_en_unlock1(pd_port, svid_data, ack); + break; + + case DC_DFP_EN_UNLOCK2: + dc_dfp_notify_en_unlock2(pd_port, svid_data, ack); + break; + } + + return true; +} + +bool dc_ufp_notify_uvdm(struct pd_port *pd_port, + struct svdm_svid_data *svid_data) +{ +#ifdef RTDC_TA_EMULATE + uint32_t reply_cmd[3]; + uint32_t recv_code[2], rn_code, pass_code; + + reply_cmd[0] = PD_UVDM_HDR(0x29cf, RTDC_UVDM_EN_UNLOCK); + uint32_t cmd = PD_UVDM_HDR_CMD(pd_port->uvdm_data[0]); + + if (cmd != RTDC_UVDM_EN_UNLOCK) { + DC_INFO("What!?"); + return true; + } + + switch (pd_port->dc_dfp_state) { + case DC_UFP_T0: + { + recv_code[0] = pd_port->uvdm_data[1]; + recv_code[1] = pd_port->uvdm_data[2]; + DC_INFO("T0: recv_code: 0x%x, 0x%x\n", + recv_code[0], recv_code[1]); + + pass_code = dc_get_authorization_code( + (recv_code[0] & 0xffff) | + (recv_code[1] & 0xffff0000)); + + rn_code = dc_get_random_code(); + + pd_port->dc_pass_code = + dc_get_authorization_code(rn_code); + + DC_INFO("T0: reply: 0x%x, 0x%x\n", + rn_code, pd_port->dc_pass_code); + + reply_cmd[1] = pass_code; + reply_cmd[2] = rn_code; + + pd_reply_custom_vdm(pd_port, TCPC_TX_SOP, 3, reply_cmd); + dc_dfp_set_state(pd_port, DC_UFP_T1); + } + break; + + case DC_UFP_T1: + { + recv_code[0] = pd_port->uvdm_data[1]; + recv_code[1] = pd_port->uvdm_data[2]; + DC_INFO("T1: recv_code: 0x%x, 0x%x\n", + recv_code[0], recv_code[1]); + + if (recv_code[0] != pd_port->dc_pass_code) { + DC_INFO("T1: pass_code error\n"); + reply_cmd[1] = 0; + } else { + DC_INFO("T1: pass_code success\n"); + reply_cmd[1] = 1; + } + + reply_cmd[2] = dc_get_random_code(); + pd_reply_custom_vdm(pd_port, TCPC_TX_SOP, 3, reply_cmd); + dc_dfp_set_state(pd_port, DC_UFP_T2); + } + break; + } +#endif /* RTDC_TA_EMULATE */ + return true; +} + +bool dc_reset_state(struct pd_port *pd_port, struct svdm_svid_data *svid_data) +{ + dc_dfp_set_state(pd_port, DC_DFP_NONE); + return true; +} + +bool dc_parse_svid_data(struct pd_port *pd_port, + struct svdm_svid_data *svid_data) +{ + svid_data->local_mode.mode_cnt = 1; + svid_data->local_mode.mode_vdo[0] = 0x00; + pd_port->dpm_caps |= DPM_CAP_ATTEMP_ENTER_DC_MODE; + return true; +} +#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */ +#endif /* CONFIG_USB_POWER_DELIVERY */ diff --git a/drivers/usb/typec/mmi_tcpc/pd_dpm_alt_mode_dp.c b/drivers/usb/typec/mmi_tcpc/pd_dpm_alt_mode_dp.c new file mode 100644 index 000000000000..b6b73c388baf --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_dpm_alt_mode_dp.c @@ -0,0 +1,1111 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * PD Device Policy Manager for DisplayPort + * + * 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. + */ + +#include + +#include "inc/tcpci.h" +#include "inc/pd_policy_engine.h" +#include "inc/pd_dpm_core.h" +#include "pd_dpm_prv.h" + +#ifdef CONFIG_USB_POWER_DELIVERY +#ifdef CONFIG_USB_PD_ALT_MODE + +/* Display Port DFP_U / UFP_U */ + + +/* DP_Role : DFP_D & UFP_D Both or DFP_D only */ + +#define DP_CHECK_DP_CONNECTED_MATCH(a, b) \ + ((a|b) == DPSTS_BOTH_CONNECTED) + +#define DP_DFP_U_CHECK_ROLE_CAP_MATCH(a, b) \ + ((MODE_DP_PORT_CAP(a)|MODE_DP_PORT_CAP(b)) == MODE_DP_BOTH) + +#define DP_SELECT_CONNECTED(b) ((b == DPSTS_DFP_D_CONNECTED) ? \ + DPSTS_UFP_D_CONNECTED : DPSTS_DFP_D_CONNECTED) + +/* + * If we support ufp_d & dfp_d both, we should choose another role. + * If we don't support both, check dp_connected valid or not + */ +static inline bool dp_update_dp_connected_one(struct pd_port *pd_port, + uint32_t dp_connected, uint32_t dp_local_connected) +{ + bool valid_connected; + struct dp_data *dp_data = pd_get_dp_data(pd_port); + + if (dp_local_connected != DPSTS_BOTH_CONNECTED) { + valid_connected = DP_CHECK_DP_CONNECTED_MATCH( + dp_connected, dp_local_connected); + } else { + valid_connected = true; + dp_data->local_status = DP_SELECT_CONNECTED(dp_connected); + } + + return valid_connected; +} + +/* + * If we support ufp_d & dfp_d both, we should decide to use which role. + * For dfp_u, the dp_connected is invalid, and re-send dp_status. + * For ufp_u, the dp_connected is valid, and wait for dp_status from dfp_u + * + * If we don't support both, the dp_connected always is valid + * + */ + +static inline bool dp_update_dp_connected_both(struct pd_port *pd_port, + uint32_t dp_local_connected, bool both_connected_valid) +{ + struct dp_data *dp_data = pd_get_dp_data(pd_port); + bool valid_connected = true; + + if (dp_local_connected == DPSTS_BOTH_CONNECTED) { + dp_data->local_status = pd_port->dp_second_connected; + valid_connected = both_connected_valid; + } + + return valid_connected; +} + +/* DP : DFP_U */ +#ifdef CONFIG_USB_PD_ALT_MODE_DFP +#if DP_DBG_ENABLE +static const char * const dp_dfp_u_state_name[] = { + "dp_dfp_u_none", + "dp_dfp_u_discover_id", + "dp_dfp_u_discover_svids", + "dp_dfp_u_discover_modes", + "dp_dfp_u_enter_mode", + "dp_dfp_u_status_update", + "dp_dfp_u_wait_attention", + "dp_dfp_u_configure", + "dp_dfp_u_operation", +}; +#endif /* DP_DBG_ENABLE */ + +void dp_dfp_u_set_state(struct pd_port *pd_port, uint8_t state) +{ + struct dp_data *dp_data = pd_get_dp_data(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + dp_data->dfp_u_state = state; + + if (dp_data->dfp_u_state < DP_DFP_U_STATE_NR) + DP_DBG("%s\n", dp_dfp_u_state_name[state]); + else + DP_DBG("dp_dfp_u_stop (%d)\n", state); +} + +bool dp_dfp_u_notify_pe_startup( + struct pd_port *pd_port, struct svdm_svid_data *svid_data) +{ + if (!(pd_port->id_vdos[0] & PD_IDH_MODAL_SUPPORT)) + return true; + + if (pd_port->dpm_caps & DPM_CAP_ATTEMP_ENTER_DP_MODE) + dp_dfp_u_set_state(pd_port, DP_DFP_U_DISCOVER_ID); + + return true; +} + +int dp_dfp_u_notify_pe_ready( + struct pd_port *pd_port, struct svdm_svid_data *svid_data) +{ + struct dp_data *dp_data = pd_get_dp_data(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + DPM_DBG("%s\n", __func__); + + if (pd_port->data_role != PD_ROLE_DFP) + return 0; + + if (dp_data->dfp_u_state != DP_DFP_U_DISCOVER_MODES) + return 0; + + /* Check Cable later */ + pd_port->mode_svid = USB_SID_DISPLAYPORT; + pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_DISCOVER_MODES); + return 1; +} + +bool dp_notify_pe_shutdown( + struct pd_port *pd_port, struct svdm_svid_data *svid_data) +{ + if (svid_data->active_mode) { + pd_send_vdm_exit_mode(pd_port, TCPC_TX_SOP, + svid_data->svid, svid_data->active_mode); + } + + return true; +} + +bool dp_dfp_u_notify_discover_id(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack) +{ + struct dp_data *dp_data = pd_get_dp_data(pd_port); + uint32_t *payload = pd_get_msg_data_payload(pd_port); + + if (dp_data->dfp_u_state != DP_DFP_U_DISCOVER_ID) + return true; + + if (!ack) { + dp_dfp_u_set_state(pd_port, + DP_DFP_U_ERR_DISCOVER_ID_NAK_TIMEOUT); + return true; + } + + if (payload[VDO_INDEX_IDH] & PD_IDH_MODAL_SUPPORT) + dp_dfp_u_set_state(pd_port, DP_DFP_U_DISCOVER_SVIDS); + else + dp_dfp_u_set_state(pd_port, DP_DFP_U_ERR_DISCOVER_ID_TYPE); + + return true; +} + +bool dp_dfp_u_notify_discover_svid( + struct pd_port *pd_port, struct svdm_svid_data *svid_data, bool ack) +{ + struct dp_data *dp_data = pd_get_dp_data(pd_port); + + if (dp_data->dfp_u_state != DP_DFP_U_DISCOVER_SVIDS) + return false; + + if (!ack) { + dp_dfp_u_set_state(pd_port, + DP_DFP_U_ERR_DISCOVER_SVID_NAK_TIMEOUT); + return false; + } + + if (!svid_data->exist) { + dp_dfp_u_set_state(pd_port, DP_DFP_U_ERR_DISCOVER_SVID_DP_SID); + return false; + } + + dpm_reaction_set(pd_port, DPM_REACTION_DISCOVER_CABLE_FLOW); + dp_dfp_u_set_state(pd_port, DP_DFP_U_DISCOVER_MODES); + return true; +} + +static inline bool is_dp_v1_cap_valid(uint32_t dp_cap) +{ + if (((dp_cap >> 24) == 0) && ((dp_cap & 0x00ffffff) != 0)) + return true; + return false; +} + +#define DP_RECEPTACLE (1 << 6) + +/* priority : B -> A -> D/F -> C/E */ +static inline int eval_dp_match_score(uint32_t local_mode, + uint32_t remote_mode, uint32_t *local_dp_config, + uint32_t *remote_dp_config) +{ + uint32_t local_pin_assignment = 0, remote_pin_assignment = 0; + uint32_t common_pin_assignment; + bool remote_is_ufp_pin_assignment = false; + bool local_is_dfp_pin_assignment = false; + int score = 0; + + if (!DP_DFP_U_CHECK_ROLE_CAP_MATCH(local_mode, remote_mode)) + return 0; + + if (((local_mode & MODE_DP_BOTH) == 0) || + ((remote_mode & MODE_DP_BOTH) == 0)) + return 0; + + if (local_mode & DP_RECEPTACLE) { + if (remote_mode & DP_RECEPTACLE) { + if (local_mode & MODE_DP_SRC) { + local_pin_assignment = + MODE_DP_PIN_DFP(local_mode); + remote_pin_assignment = + MODE_DP_PIN_UFP(remote_mode); + remote_is_ufp_pin_assignment = true; + local_is_dfp_pin_assignment = true; + } else { + local_pin_assignment = + MODE_DP_PIN_UFP(local_mode); + remote_pin_assignment = + MODE_DP_PIN_DFP(remote_mode); + remote_is_ufp_pin_assignment = false; + local_is_dfp_pin_assignment = false; + } + } else { + /* remote is plug */ + if (local_mode & MODE_DP_SRC) { + local_pin_assignment = + MODE_DP_PIN_DFP(local_mode); + remote_pin_assignment = + MODE_DP_PIN_DFP(remote_mode); + remote_is_ufp_pin_assignment = false; + local_is_dfp_pin_assignment = true; + } + } + } else { + /* local is plug */ + if (remote_mode & DP_RECEPTACLE) { + if (local_mode & MODE_DP_SNK) { + local_pin_assignment = + MODE_DP_PIN_DFP(local_mode); + remote_pin_assignment = + MODE_DP_PIN_DFP(remote_mode); + remote_is_ufp_pin_assignment = false; + } + } + } + + common_pin_assignment = local_pin_assignment & remote_pin_assignment; + if (common_pin_assignment & (MODE_DP_PIN_C | MODE_DP_PIN_E)) { + score = 1; + if (common_pin_assignment & MODE_DP_PIN_E) { + *local_dp_config = local_is_dfp_pin_assignment ? + VDO_DP_DFP_CFG(DP_PIN_ASSIGN_SUPPORT_E, + DP_SIG_DPV13) : VDO_DP_UFP_CFG( + DP_PIN_ASSIGN_SUPPORT_E, DP_SIG_DPV13); + *remote_dp_config = remote_is_ufp_pin_assignment ? + VDO_DP_UFP_CFG(DP_PIN_ASSIGN_SUPPORT_E, + DP_SIG_DPV13) : VDO_DP_DFP_CFG( + DP_PIN_ASSIGN_SUPPORT_E, DP_SIG_DPV13); + } else { + *local_dp_config = local_is_dfp_pin_assignment ? + VDO_DP_DFP_CFG(DP_PIN_ASSIGN_SUPPORT_C, + DP_SIG_DPV13) : VDO_DP_UFP_CFG( + DP_PIN_ASSIGN_SUPPORT_E, DP_SIG_DPV13); + *remote_dp_config = remote_is_ufp_pin_assignment ? + VDO_DP_UFP_CFG(DP_PIN_ASSIGN_SUPPORT_C, + DP_SIG_DPV13) : VDO_DP_DFP_CFG( + DP_PIN_ASSIGN_SUPPORT_E, DP_SIG_DPV13); + } + } + if (common_pin_assignment & (MODE_DP_PIN_D | MODE_DP_PIN_F)) { + score = 2; + if (common_pin_assignment & MODE_DP_PIN_F) { + *local_dp_config = local_is_dfp_pin_assignment ? + VDO_DP_DFP_CFG(DP_PIN_ASSIGN_SUPPORT_F, + DP_SIG_DPV13) : VDO_DP_UFP_CFG( + DP_PIN_ASSIGN_SUPPORT_E, DP_SIG_DPV13); + *remote_dp_config = remote_is_ufp_pin_assignment ? + VDO_DP_UFP_CFG(DP_PIN_ASSIGN_SUPPORT_F, + DP_SIG_DPV13) : VDO_DP_DFP_CFG( + DP_PIN_ASSIGN_SUPPORT_E, DP_SIG_DPV13); + } else { + *local_dp_config = local_is_dfp_pin_assignment ? + VDO_DP_DFP_CFG(DP_PIN_ASSIGN_SUPPORT_D, + DP_SIG_DPV13) : VDO_DP_UFP_CFG( + DP_PIN_ASSIGN_SUPPORT_E, DP_SIG_DPV13); + *remote_dp_config = remote_is_ufp_pin_assignment ? + VDO_DP_UFP_CFG(DP_PIN_ASSIGN_SUPPORT_D, + DP_SIG_DPV13) : VDO_DP_DFP_CFG( + DP_PIN_ASSIGN_SUPPORT_E, DP_SIG_DPV13); + } + } + if ((MODE_DP_SIGNAL_SUPPORT(local_mode) & MODE_DP_GEN2) && + (MODE_DP_SIGNAL_SUPPORT(remote_mode) & MODE_DP_GEN2)) { + if (common_pin_assignment & MODE_DP_PIN_A) { + score = 3; + *local_dp_config = local_is_dfp_pin_assignment ? + VDO_DP_DFP_CFG(DP_PIN_ASSIGN_SUPPORT_A, + DP_SIG_DPV13) : VDO_DP_UFP_CFG( + DP_PIN_ASSIGN_SUPPORT_E, DP_SIG_GEN2); + *remote_dp_config = remote_is_ufp_pin_assignment ? + VDO_DP_UFP_CFG(DP_PIN_ASSIGN_SUPPORT_A, + DP_SIG_DPV13) : VDO_DP_DFP_CFG( + DP_PIN_ASSIGN_SUPPORT_E, DP_SIG_GEN2); + } + if (common_pin_assignment & MODE_DP_PIN_B) { + score = 4; + *local_dp_config = local_is_dfp_pin_assignment ? + VDO_DP_DFP_CFG(DP_PIN_ASSIGN_SUPPORT_B, + DP_SIG_DPV13) : VDO_DP_UFP_CFG( + DP_PIN_ASSIGN_SUPPORT_E, DP_SIG_GEN2); + *remote_dp_config = remote_is_ufp_pin_assignment ? + VDO_DP_UFP_CFG(DP_PIN_ASSIGN_SUPPORT_B, + DP_SIG_DPV13) : VDO_DP_DFP_CFG( + DP_PIN_ASSIGN_SUPPORT_E, DP_SIG_GEN2); + } + } + return score; +} + +static inline uint8_t dp_dfp_u_select_mode(struct pd_port *pd_port, + struct dp_data *dp_data, struct svdm_svid_data *svid_data) +{ + uint32_t dp_local_mode, dp_remote_mode, + remote_dp_config = 0, local_dp_config = 0; + struct svdm_mode *remote, *local; + int i, j; + int match_score, best_match_score = 0; + int local_index = -1, remote_index = -1; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + local = &svid_data->local_mode; + remote = &svid_data->remote_mode; + + /* TODO: Evaluate All Modes later ... */ + for (j = 0; j < local->mode_cnt; j++) { + dp_local_mode = local->mode_vdo[j]; + if (!is_dp_v1_cap_valid(dp_local_mode)) + continue; + for (i = 0; i < remote->mode_cnt; i++) { + dp_remote_mode = remote->mode_vdo[i]; + if (!is_dp_v1_cap_valid(dp_remote_mode)) + continue; + match_score = eval_dp_match_score(dp_local_mode, + dp_remote_mode, &local_dp_config, + &remote_dp_config); + if (match_score > best_match_score) { + local_index = j; + remote_index = i; + dp_data->local_config = local_dp_config; + dp_data->remote_config = remote_dp_config; + } + } + } + +#if DP_INFO_ENABLE + for (i = 0; i < svid_data->remote_mode.mode_cnt; i++) { + DP_INFO("Mode%d=0x%08x\n", i, + svid_data->remote_mode.mode_vdo[i]); + } + + DP_INFO("SelectMode:%d\n", remote_index); +#endif /* DP_INFO_ENABLE */ + + /* + * dp_mode = svid_data->remote_mode.mode_vdo[0]; + * dp_local_mode = svid_data->local_mode.mode_vdo[0]; + * cap_match = DP_DFP_U_CHECK_ROLE_CAP_MATCH(dp_mode, dp_local_mode), + * return cap_match ? 1 : 0; + */ + return remote_index + 1; +} + +bool dp_dfp_u_notify_discover_modes( + struct pd_port *pd_port, struct svdm_svid_data *svid_data, bool ack) +{ + struct dp_data *dp_data = pd_get_dp_data(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (dp_data->dfp_u_state != DP_DFP_U_DISCOVER_MODES) + return false; + + if (!ack) { + dp_dfp_u_set_state(pd_port, + DP_DFP_U_ERR_DISCOVER_MODE_NAK_TIMEROUT); + return false; + } + + if (svid_data->remote_mode.mode_cnt == 0) { + dp_dfp_u_set_state(pd_port, DP_DFP_U_ERR_DISCOVER_MODE_DP_SID); + return false; + } + + pd_port->mode_obj_pos = dp_dfp_u_select_mode( + pd_port, dp_data, svid_data); + + if (pd_port->mode_obj_pos == 0) { + DPM_DBG("Can't find match mode\n"); + dp_dfp_u_set_state(pd_port, DP_DFP_U_ERR_DISCOVER_MODE_CAP); + return false; + } + + dp_dfp_u_set_state(pd_port, DP_DFP_U_ENTER_MODE); + pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_ENTER_MODE); + return true; +} + +bool dp_dfp_u_notify_enter_mode(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, uint8_t ops, bool ack) +{ + struct dp_data *dp_data = pd_get_dp_data(pd_port); + + if (dp_data->dfp_u_state != DP_DFP_U_ENTER_MODE) + return true; + + if (!ack) { + dp_dfp_u_set_state(pd_port, + DP_DFP_U_ERR_ENTER_MODE_NAK_TIMEOUT); + return true; + } + + if (svid_data->active_mode == 0) { + dp_dfp_u_set_state(pd_port, DP_DFP_U_ERR_ENTER_MODE_DP_SID); + return false; + } + + dp_data->local_status = pd_port->dp_first_connected; + dp_dfp_u_set_state(pd_port, DP_DFP_U_STATUS_UPDATE); + +#ifdef CONFIG_USB_PD_DBG_DP_DFP_D_AUTO_UPDATE + /* + * For Real Product, + * DFP_U should not send status_update until USB status is changed + * From : "USB Mode, USB Configration" + * To : "DisplayPlay Mode, USB Configration" + * + * After USB status is changed, + * please call following function to continue DFP_U flow. + * tcpm_dpm_dp_status_update(tcpc, 0, 0, NULL) + */ + + pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_DP_STATUS_UPDATE); +#endif /* CONFIG_USB_PD_DBG_DP_DFP_D_AUTO_UPDATE */ + + return true; +} + +bool dp_dfp_u_notify_exit_mode( + struct pd_port *pd_port, struct svdm_svid_data *svid_data, uint8_t ops) +{ + struct dp_data *dp_data = pd_get_dp_data(pd_port); + + if (dp_data->dfp_u_state <= DP_DFP_U_ENTER_MODE) + return false; + + if (svid_data->svid != USB_SID_DISPLAYPORT) + return false; + + memset(dp_data, 0, sizeof(struct dp_data)); + dp_dfp_u_set_state(pd_port, DP_DFP_U_NONE); + return true; +} + +static inline bool dp_dfp_u_select_pin_mode(struct pd_port *pd_port) +{ + uint32_t dp_local_connected; + uint32_t dp_mode[2], pin_cap[2]; + uint32_t pin_caps, signal; + struct dp_data *dp_data = pd_get_dp_data(pd_port); + struct svdm_svid_data *svid_data = + dpm_get_svdm_svid_data(pd_port, USB_SID_DISPLAYPORT); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (svid_data == NULL) + return false; + + dp_mode[0] = SVID_DATA_LOCAL_MODE(svid_data, 0); + dp_mode[1] = SVID_DATA_DFP_GET_ACTIVE_MODE(svid_data); + + dp_local_connected = PD_VDO_DPSTS_CONNECT(dp_data->local_status); + + switch (dp_local_connected) { + case DPSTS_DFP_D_CONNECTED: + pin_cap[0] = PD_DP_DFP_D_PIN_CAPS(dp_mode[0]); + pin_cap[1] = PD_DP_UFP_D_PIN_CAPS(dp_mode[1]); + break; + + case DPSTS_UFP_D_CONNECTED: + /* TODO: checkit next version*/ + pin_cap[0] = PD_DP_UFP_D_PIN_CAPS(dp_mode[0]); + pin_cap[1] = PD_DP_DFP_D_PIN_CAPS(dp_mode[1]); + break; + default: + DP_ERR("select_pin error1\n"); + return false; + } + + PE_DBG("modes=0x%x 0x%x\n", dp_mode[0], dp_mode[1]); + PE_DBG("pins=0x%x 0x%x\n", pin_cap[0], pin_cap[1]); + + pin_caps = pin_cap[0] & pin_cap[1]; + + /* if don't want multi-function then ignore those pin configs */ + if (!PD_VDO_DPSTS_MF_PREF(dp_data->remote_status)) + pin_caps &= ~MODE_DP_PIN_MF_MASK; + + /* TODO: If DFP & UFP driver USB Gen2 signal */ + signal = DP_SIG_DPV13; + pin_caps &= ~MODE_DP_PIN_BR2_MASK; + + if (!pin_caps) { + DP_ERR("select_pin error2\n"); + return false; + } + + /* Priority */ + if (pin_caps & MODE_DP_PIN_D) + pin_caps = MODE_DP_PIN_D; + else if (pin_caps & MODE_DP_PIN_F) + pin_caps = MODE_DP_PIN_F; + else if (pin_caps & MODE_DP_PIN_C) + pin_caps = MODE_DP_PIN_C; + else if (pin_caps & MODE_DP_PIN_E) + pin_caps = MODE_DP_PIN_E; + + if (dp_local_connected == DPSTS_DFP_D_CONNECTED) { + dp_data->local_config = VDO_DP_DFP_CFG(pin_caps, signal); + dp_data->remote_config = VDO_DP_UFP_CFG(pin_caps, signal); + } else { + dp_data->local_config = VDO_DP_UFP_CFG(pin_caps, signal); + dp_data->remote_config = VDO_DP_DFP_CFG(pin_caps, signal); + } + + return true; +} + +void dp_dfp_u_request_dp_configuration(struct pd_port *pd_port) +{ + if (!dp_dfp_u_select_pin_mode(pd_port)) { + dp_dfp_u_set_state(pd_port, + DP_DFP_U_ERR_CONFIGURE_SELECT_MODE); + return; + } + + tcpci_dp_notify_config_start(pd_port->tcpc); + + dp_dfp_u_set_state(pd_port, DP_DFP_U_CONFIGURE); + pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_DP_CONFIG); +} + +static inline bool dp_dfp_u_update_dp_connected(struct pd_port *pd_port) +{ + bool valid_connected = false; + uint32_t dp_connected, dp_local_connected; + struct dp_data *dp_data = pd_get_dp_data(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + dp_connected = PD_VDO_DPSTS_CONNECT(dp_data->remote_status); + dp_local_connected = PD_VDO_DPSTS_CONNECT(dp_data->local_status); + + switch (dp_connected) { + case DPSTS_DFP_D_CONNECTED: + case DPSTS_UFP_D_CONNECTED: + valid_connected = dp_update_dp_connected_one( + pd_port, dp_connected, dp_local_connected); + + if (!valid_connected) { + dp_dfp_u_set_state(pd_port, + DP_DFP_U_ERR_STATUS_UPDATE_ROLE); + } + break; + + case DPSTS_DISCONNECT: + dp_dfp_u_set_state(pd_port, DP_DFP_U_WAIT_ATTENTION); + break; + + case DPSTS_BOTH_CONNECTED: + valid_connected = dp_update_dp_connected_both( + pd_port, dp_local_connected, false); + + if (!valid_connected) { + DP_INFO("BOTH_SEL_ONE\n"); + pd_put_tcp_vdm_event(pd_port, + TCP_DPM_EVT_DP_STATUS_UPDATE); + } + break; + } + + return valid_connected; +} + +bool dp_dfp_u_notify_dp_status_update(struct pd_port *pd_port, bool ack) +{ + bool oper_mode = false; + bool valid_connected; + + struct dp_data *dp_data = pd_get_dp_data(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + switch (dp_data->dfp_u_state) { + case DP_DFP_U_OPERATION: + oper_mode = true; + case DP_DFP_U_STATUS_UPDATE: + break; + + default: + return false; + } + + if (!ack) { + tcpci_dp_notify_status_update_done(tcpc, 0, false); + dp_dfp_u_set_state(pd_port, + DP_DFP_U_ERR_STATUS_UPDATE_NAK_TIMEOUT); + return false; + } + + if (dpm_vdm_get_svid(pd_port) != USB_SID_DISPLAYPORT) { + dp_dfp_u_set_state(pd_port, DP_DFP_U_ERR_STATUS_UPDATE_DP_SID); + return true; + } + + dp_data->remote_status = pd_get_msg_vdm_data_payload(pd_port)[0]; + DP_INFO("dp_status: 0x%x\n", dp_data->remote_status); + + if (oper_mode) { + tcpci_dp_notify_status_update_done( + tcpc, dp_data->remote_status, ack); + } else { + valid_connected = + dp_dfp_u_update_dp_connected(pd_port); + if (valid_connected) + dp_dfp_u_request_dp_configuration(pd_port); + } + return true; +} + +static inline void dp_ufp_u_auto_update(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_DBG_DP_UFP_U_AUTO_UPDATE + struct dp_data *dp_data = pd_get_dp_data(pd_port); + + if (dp_data->dfp_u_state == DP_DFP_U_OPERATION) + return; + + pd_port->mode_svid = USB_SID_DISPLAYPORT; + dp_data->local_status |= DPSTS_DP_ENABLED | DPSTS_DP_HPD_STATUS; + pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_DP_STATUS_UPDATE); +#endif /* CONFIG_USB_PD_DBG_DP_UFP_U_AUTO_UPDATE */ +} + +bool dp_dfp_u_notify_dp_configuration(struct pd_port *pd_port, bool ack) +{ + struct dp_data *dp_data = pd_get_dp_data(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (ack) { + dp_ufp_u_auto_update(pd_port); + dp_dfp_u_set_state(pd_port, DP_DFP_U_OPERATION); + } else + DP_ERR("config failed: 0x%0x\n", dp_data->remote_config); + + tcpci_dp_notify_config_done(tcpc, + dp_data->local_config, dp_data->remote_config, ack); + + return true; +} + +bool dp_dfp_u_notify_attention(struct pd_port *pd_port, + struct svdm_svid_data *svid_data) +{ + bool valid_connected; + struct dp_data *dp_data = pd_get_dp_data(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + dp_data->remote_status = pd_get_msg_vdm_data_payload(pd_port)[0]; + + DP_INFO("dp_status: 0x%x\n", dp_data->remote_status); + + switch (dp_data->dfp_u_state) { + case DP_DFP_U_WAIT_ATTENTION: + valid_connected = + dp_dfp_u_update_dp_connected(pd_port); + if (valid_connected) + dp_dfp_u_request_dp_configuration(pd_port); + break; + + case DP_DFP_U_OPERATION: + tcpci_dp_attention(tcpc, dp_data->remote_status); + break; + } + + return true; +} + +#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ + +/* DP : UFP_U */ + +#if DPM_DBG_ENABLE +static const char * const dp_ufp_u_state_name[] = { + "dp_ufp_u_none", + "dp_ufp_u_startup", + "dp_ufp_u_wait", + "dp_ufp_u_operation", +}; +#endif /* DPM_DBG_ENABLE */ + +static void dp_ufp_u_set_state(struct pd_port *pd_port, uint8_t state) +{ + struct dp_data *dp_data = pd_get_dp_data(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + dp_data->ufp_u_state = state; + + if (dp_data->ufp_u_state < DP_UFP_U_STATE_NR) + DPM_DBG("%s\n", dp_ufp_u_state_name[state]); + else + DPM_DBG("dp_ufp_u_stop\n"); +} + +void dp_ufp_u_request_enter_mode( + struct pd_port *pd_port, struct svdm_svid_data *svid_data, uint8_t ops) +{ + struct dp_data *dp_data = pd_get_dp_data(pd_port); + + dp_data->local_status = pd_port->dp_first_connected; + + if (pd_port->dpm_caps & DPM_CAP_DP_PREFER_MF) + dp_data->local_status |= DPSTS_DP_MF_PREF; + + if (pd_port->dp_first_connected == DPSTS_DISCONNECT) + dp_ufp_u_set_state(pd_port, DP_UFP_U_STARTUP); + else + dp_ufp_u_set_state(pd_port, DP_UFP_U_WAIT); +} + +void dp_ufp_u_request_exit_mode( + struct pd_port *pd_port, struct svdm_svid_data *svid_data, uint8_t ops) +{ + struct dp_data *dp_data = pd_get_dp_data(pd_port); + + memset(dp_data, 0, sizeof(struct dp_data)); + dp_ufp_u_set_state(pd_port, DP_UFP_U_NONE); +} + +static inline bool dp_ufp_u_update_dp_connected(struct pd_port *pd_port) +{ + bool valid_connected = false; + uint32_t dp_connected, dp_local_connected; + struct dp_data *dp_data = pd_get_dp_data(pd_port); + + dp_connected = PD_VDO_DPSTS_CONNECT(dp_data->remote_status); + dp_local_connected = PD_VDO_DPSTS_CONNECT(dp_data->local_status); + + switch (dp_connected) { + case DPSTS_DFP_D_CONNECTED: + case DPSTS_UFP_D_CONNECTED: + valid_connected = dp_update_dp_connected_one( + pd_port, dp_connected, dp_local_connected); + break; + + case DPSTS_BOTH_CONNECTED: + valid_connected = dp_update_dp_connected_both( + pd_port, dp_local_connected, true); + break; + + default: + break; + } + + return valid_connected; +} + +static inline int dp_ufp_u_request_dp_status(struct pd_port *pd_port) +{ + bool ack; + struct dp_data *dp_data = pd_get_dp_data(pd_port); + + dp_data->remote_status = pd_get_msg_vdm_data_payload(pd_port)[0]; + + switch (dp_data->ufp_u_state) { + case DP_UFP_U_WAIT: + ack = dp_ufp_u_update_dp_connected(pd_port); + break; + + case DP_UFP_U_STARTUP: + case DP_UFP_U_OPERATION: + ack = true; + tcpci_dp_status_update( + pd_port->tcpc, dp_data->remote_status); + break; + + default: + ack = false; + break; + } + + if (ack) { + return pd_reply_svdm_request(pd_port, + CMDT_RSP_ACK, 1, &dp_data->local_status); + } else { + return dpm_vdm_reply_svdm_nak(pd_port); + } +} + +bool dp_ufp_u_is_valid_dp_config(struct pd_port *pd_port, uint32_t dp_config) +{ + /* TODO: Check it later .... */ + uint32_t sel_config; + bool retval = false; + uint32_t local_pin; + struct svdm_svid_data *svid_data = &pd_port->svid_data[0]; + uint32_t local_mode = svid_data->local_mode.mode_vdo[0]; + uint32_t remote_pin = PD_DP_CFG_PIN(dp_config); + + sel_config = MODE_DP_PORT_CAP(dp_config); + switch (sel_config) { + case DP_CONFIG_USB: + retval = true; + break; + + case DP_CONFIG_DFP_D: + local_pin = PD_DP_DFP_D_PIN_CAPS(local_mode); + if ((local_pin & remote_pin) && + (MODE_DP_PORT_CAP(local_mode) & MODE_DP_SRC)) + retval = true; + break; + + case DP_CONFIG_UFP_D: + local_pin = PD_DP_UFP_D_PIN_CAPS(local_mode); + if ((local_pin & remote_pin) && + (MODE_DP_PORT_CAP(local_mode) & MODE_DP_SNK)) + retval = true; + break; + } + + return retval; +} + +static inline void dp_ufp_u_auto_attention(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_DBG_DP_UFP_U_AUTO_ATTENTION + struct dp_data *dp_data = pd_get_dp_data(pd_port); + + pd_port->mode_svid = USB_SID_DISPLAYPORT; + dp_data->local_status |= DPSTS_DP_ENABLED | DPSTS_DP_HPD_STATUS; +#endif /* CONFIG_USB_PD_DBG_DP_UFP_U_AUTO_ATTENTION */ +} + +static inline int dp_ufp_u_request_dp_config(struct pd_port *pd_port) +{ + bool ack = false; + uint32_t dp_config; + struct dp_data *dp_data = pd_get_dp_data(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + dp_config = pd_get_msg_vdm_data_payload(pd_port)[0]; + DPM_DBG("dp_config: 0x%x\n", dp_config); + + switch (dp_data->ufp_u_state) { + case DP_UFP_U_STARTUP: + case DP_UFP_U_WAIT: + case DP_UFP_U_OPERATION: + ack = dp_ufp_u_is_valid_dp_config(pd_port, dp_config); + + if (ack) { + dp_data->local_config = dp_config; + tcpci_dp_configure(tcpc, dp_config); + dp_ufp_u_auto_attention(pd_port); + dp_ufp_u_set_state(pd_port, DP_UFP_U_OPERATION); + } + break; + } + + return dpm_vdm_reply_svdm_request(pd_port, ack); +} + +static inline void dp_ufp_u_send_dp_attention(struct pd_port *pd_port) +{ + struct svdm_svid_data *svid_data; + struct dp_data *dp_data = pd_get_dp_data(pd_port); + + switch (dp_data->ufp_u_state) { + case DP_UFP_U_STARTUP: + case DP_UFP_U_OPERATION: + svid_data = dpm_get_svdm_svid_data( + pd_port, USB_SID_DISPLAYPORT); + PD_BUG_ON(svid_data == NULL); + + pd_send_vdm_dp_attention(pd_port, TCPC_TX_SOP, + svid_data->active_mode, dp_data->local_status); + break; + + default: + VDM_STATE_DPM_INFORMED(pd_port); + pd_notify_tcp_vdm_event_2nd_result( + pd_port, TCP_DPM_RET_DENIED_NOT_READY); + break; + } +} + +/* ---- UFP : DP Only ---- */ + +int pd_dpm_ufp_request_dp_status(struct pd_port *pd_port) +{ + return dp_ufp_u_request_dp_status(pd_port); +} + +int pd_dpm_ufp_request_dp_config(struct pd_port *pd_port) +{ + return dp_ufp_u_request_dp_config(pd_port); +} + +void pd_dpm_ufp_send_dp_attention(struct pd_port *pd_port) +{ + dp_ufp_u_send_dp_attention(pd_port); +} + +/* ---- DFP : DP Only ---- */ + +#ifdef CONFIG_USB_PD_ALT_MODE_DFP + +void pd_dpm_dfp_send_dp_status_update(struct pd_port *pd_port) +{ + struct dp_data *dp_data = pd_get_dp_data(pd_port); + + pd_send_vdm_dp_status(pd_port, TCPC_TX_SOP, + pd_port->mode_obj_pos, 1, &dp_data->local_status); +} + +void pd_dpm_dfp_inform_dp_status_update( + struct pd_port *pd_port, bool ack) +{ + VDM_STATE_DPM_INFORMED(pd_port); + dp_dfp_u_notify_dp_status_update(pd_port, ack); +} + +void pd_dpm_dfp_send_dp_configuration(struct pd_port *pd_port) +{ + struct dp_data *dp_data = pd_get_dp_data(pd_port); + + pd_send_vdm_dp_config(pd_port, TCPC_TX_SOP, + pd_port->mode_obj_pos, 1, &dp_data->remote_config); +} + +void pd_dpm_dfp_inform_dp_configuration( + struct pd_port *pd_port, bool ack) +{ + VDM_STATE_DPM_INFORMED(pd_port); + dp_dfp_u_notify_dp_configuration(pd_port, ack); +} + +#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ + +bool dp_reset_state(struct pd_port *pd_port, struct svdm_svid_data *svid_data) +{ + struct dp_data *dp_data = pd_get_dp_data(pd_port); + + memset(dp_data, 0, sizeof(struct dp_data)); + return true; +} + +#define DEFAULT_DP_ROLE_CAP (MODE_DP_SRC) +#define DEFAULT_DP_FIRST_CONNECTED (DPSTS_DFP_D_CONNECTED) +#define DEFAULT_DP_SECOND_CONNECTED (DPSTS_DFP_D_CONNECTED) + +#ifdef CONFIG_USB_PD_ALT_MODE +static const struct { + const char *prop_name; + uint32_t mode; +} supported_dp_pin_modes[] = { + { "pin_assignment,mode_a", MODE_DP_PIN_A }, + { "pin_assignment,mode_b", MODE_DP_PIN_B }, + { "pin_assignment,mode_c", MODE_DP_PIN_C }, + { "pin_assignment,mode_d", MODE_DP_PIN_D }, + { "pin_assignment,mode_e", MODE_DP_PIN_E }, + { "pin_assignment,mode_f", MODE_DP_PIN_F }, +}; + +static const struct { + const char *conn_mode; + uint32_t val; +} dp_connect_mode[] = { + {"both", DPSTS_BOTH_CONNECTED}, + {"dfp_d", DPSTS_DFP_D_CONNECTED}, + {"ufp_d", DPSTS_UFP_D_CONNECTED}, +}; + +bool dp_parse_svid_data( + struct pd_port *pd_port, struct svdm_svid_data *svid_data) +{ + struct device_node *np, *ufp_np, *dfp_np; + const char *connection; + uint32_t ufp_d_pin_cap = 0; + uint32_t dfp_d_pin_cap = 0; + uint32_t signal = MODE_DP_V13; + uint32_t receptacle = 1; + uint32_t usb2 = 0; + int i = 0; + + np = of_find_node_by_name( + pd_port->tcpc->dev.of_node, "displayport"); + if (np == NULL) { + pr_err("%s get displayport data fail\n", __func__); + return false; + } + + pr_info("dp, svid\n"); + svid_data->svid = USB_SID_DISPLAYPORT; + ufp_np = of_find_node_by_name(np, "ufp_d"); + dfp_np = of_find_node_by_name(np, "dfp_d"); + + if (ufp_np) { + pr_info("dp, ufp_np\n"); + for (i = 0; i < ARRAY_SIZE(supported_dp_pin_modes); i++) { + if (of_property_read_bool(ufp_np, + supported_dp_pin_modes[i].prop_name)) + ufp_d_pin_cap |= + supported_dp_pin_modes[i].mode; + } + } + + if (dfp_np) { + pr_info("dp, dfp_np\n"); + for (i = 0; i < ARRAY_SIZE(supported_dp_pin_modes); i++) { + if (of_property_read_bool(dfp_np, + supported_dp_pin_modes[i].prop_name)) + dfp_d_pin_cap |= + supported_dp_pin_modes[i].mode; + } + } + + if (of_property_read_bool(np, "signal,dp_v13")) + signal |= MODE_DP_V13; + if (of_property_read_bool(np, "signal,dp_gen2")) + signal |= MODE_DP_GEN2; + if (of_property_read_bool(np, "usbr20_not_used")) + usb2 = 1; + if (of_property_read_bool(np, "typec,receptacle")) + receptacle = 1; + + svid_data->local_mode.mode_cnt = 1; + svid_data->local_mode.mode_vdo[0] = VDO_MODE_DP( + ufp_d_pin_cap, dfp_d_pin_cap, + usb2, receptacle, signal, (ufp_d_pin_cap ? MODE_DP_SNK : 0) + | (dfp_d_pin_cap ? MODE_DP_SRC : 0)); + + pd_port->dp_first_connected = DEFAULT_DP_FIRST_CONNECTED; + pd_port->dp_second_connected = DEFAULT_DP_SECOND_CONNECTED; + + if (of_property_read_string(np, "1st_connection", &connection) == 0) { + pr_info("dp, 1st_connection\n"); + for (i = 0; i < ARRAY_SIZE(dp_connect_mode); i++) { + if (strcasecmp(connection, + dp_connect_mode[i].conn_mode) == 0) { + pd_port->dp_first_connected = + dp_connect_mode[i].val; + break; + } + } + } + + if (of_property_read_string(np, "2nd_connection", &connection) == 0) { + pr_info("dp, 2nd_connection\n"); + for (i = 0; i < ARRAY_SIZE(dp_connect_mode); i++) { + if (strcasecmp(connection, + dp_connect_mode[i].conn_mode) == 0) { + pd_port->dp_second_connected = + dp_connect_mode[i].val; + break; + } + } + } + /* 2nd connection must not be BOTH */ + PD_BUG_ON(pd_port->dp_second_connected == DPSTS_BOTH_CONNECTED); + /* UFP or DFP can't both be invalid */ + PD_BUG_ON(ufp_d_pin_cap == 0 && dfp_d_pin_cap == 0); + if (pd_port->dp_first_connected == DPSTS_BOTH_CONNECTED) { + PD_BUG_ON(ufp_d_pin_cap == 0); + PD_BUG_ON(dfp_d_pin_cap == 0); + } + + return true; +} +#endif /* CONFIG_USB_PD_ALT_MODE */ + +#endif /* CONFIG_USB_PD_ALT_MODE */ +#endif /* CONFIG_USB_POWER_DELIVERY */ diff --git a/drivers/usb/typec/mmi_tcpc/pd_dpm_core.c b/drivers/usb/typec/mmi_tcpc/pd_dpm_core.c new file mode 100644 index 000000000000..90440096f49f --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_dpm_core.c @@ -0,0 +1,2356 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * PD Device Policy Manager Core Driver + * + * 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. + */ + +#include +#include +#include + +#include "inc/tcpci.h" +#include "inc/pd_policy_engine.h" +#include "inc/pd_dpm_core.h" +#include "inc/pd_dpm_pdo_select.h" +#include "inc/pd_core.h" +#include "pd_dpm_prv.h" + +struct pd_device_policy_manager { + uint8_t temp; +}; + +static const struct svdm_svid_ops svdm_svid_ops[] = { +#ifdef CONFIG_USB_PD_ALT_MODE + { + .name = "DisplayPort", + .svid = USB_SID_DISPLAYPORT, + +#ifdef CONFIG_USB_PD_ALT_MODE_DFP + .dfp_inform_id = dp_dfp_u_notify_discover_id, + .dfp_inform_svids = dp_dfp_u_notify_discover_svid, + .dfp_inform_modes = dp_dfp_u_notify_discover_modes, + + .dfp_inform_enter_mode = dp_dfp_u_notify_enter_mode, + .dfp_inform_exit_mode = dp_dfp_u_notify_exit_mode, + + .dfp_inform_attention = dp_dfp_u_notify_attention, +#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ + + .ufp_request_enter_mode = dp_ufp_u_request_enter_mode, + .ufp_request_exit_mode = dp_ufp_u_request_exit_mode, + +#ifdef CONFIG_USB_PD_ALT_MODE_DFP + .notify_pe_startup = dp_dfp_u_notify_pe_startup, + .notify_pe_ready = dp_dfp_u_notify_pe_ready, +#endif /* #ifdef CONFIG_USB_PD_ALT_MODE_DFP */ + + .reset_state = dp_reset_state, + .parse_svid_data = dp_parse_svid_data, + }, +#endif /* CONFIG_USB_PD_ALT_MODE */ + +#ifdef CONFIG_USB_PD_RICHTEK_UVDM + { + .name = "Richtek", + .svid = USB_VID_RICHTEK, + + .dfp_notify_uvdm = richtek_dfp_notify_uvdm, + .ufp_notify_uvdm = richtek_ufp_notify_uvdm, + + .notify_pe_startup = richtek_dfp_notify_pe_startup, + .notify_pe_ready = richtek_dfp_notify_pe_ready, + }, +#endif /* CONFIG_USB_PD_RICHTEK_UVDM */ + +#ifdef CONFIG_USB_PD_ALT_MODE_RTDC + { + .name = "Direct Charge", + .svid = USB_VID_DIRECTCHARGE, + + .dfp_inform_id = dc_dfp_notify_discover_id, + .dfp_inform_svids = dc_dfp_notify_discover_svid, + .dfp_inform_modes = dc_dfp_notify_discover_modes, + + .dfp_inform_enter_mode = dc_dfp_notify_enter_mode, + .dfp_inform_exit_mode = dc_dfp_notify_exit_mode, + + .notify_pe_startup = dc_dfp_notify_pe_startup, + .notify_pe_ready = dc_dfp_notify_pe_ready, + + .dfp_notify_uvdm = dc_dfp_notify_uvdm, + .ufp_notify_uvdm = dc_ufp_notify_uvdm, + + .parse_svid_data = dc_parse_svid_data, + .reset_state = dc_reset_state, + }, +#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */ + +#ifdef CONFIG_SUPPORT_MMI_ADAPTER + { + .name = "mmi adapter", + .svid = USB_VID_MMI_ADAPTER, + .notify_pe_startup = mmi_dfp_notify_pe_startup, + .dfp_inform_svids = mmi_dfp_u_notify_discover_svid, + .dfp_notify_uvdm = mmi_dfp_notify_uvdm, + .notify_pe_ready = mmi_notify_pe_ready, + }, +#endif /* CONFIG_SUPPORT_MMI_ADAPTER */ +}; + +int dpm_check_supported_modes(void) +{ + int i; + bool is_disorder = false; + bool found_error = false; + + for (i = 0; i < ARRAY_SIZE(svdm_svid_ops); i++) { + if (i < (ARRAY_SIZE(svdm_svid_ops) - 1)) { + if (svdm_svid_ops[i + 1].svid <= + svdm_svid_ops[i].svid) + is_disorder = true; + } + pr_info("SVDM supported mode [%d]: name = %s, svid = 0x%x\n", + i, svdm_svid_ops[i].name, + svdm_svid_ops[i].svid); + } + pr_info("%s : found \"disorder\"...\n", __func__); + found_error |= is_disorder; + return found_error ? -EFAULT : 0; +} + +/* + * DPM Init + */ + +static void pd_dpm_update_pdos_flags(struct pd_port *pd_port, uint32_t pdo) +{ + uint16_t dpm_flags = pd_port->pe_data.dpm_flags + & ~DPM_FLAGS_RESET_PARTNER_MASK; + + /* Only update PDO flags if pdo's type is fixed */ + if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_FIXED) { + if (pdo & PDO_FIXED_DUAL_ROLE) + dpm_flags |= DPM_FLAGS_PARTNER_DR_POWER; + + if (pdo & PDO_FIXED_DATA_SWAP) + dpm_flags |= DPM_FLAGS_PARTNER_DR_DATA; + + if (pdo & PDO_FIXED_EXTERNAL) + dpm_flags |= DPM_FLAGS_PARTNER_EXTPOWER; + + if (pdo & PDO_FIXED_COMM_CAP) + dpm_flags |= DPM_FLAGS_PARTNER_USB_COMM; + + if (pdo & PDO_FIXED_SUSPEND) + dpm_flags |= DPM_FLAGS_PARTNER_USB_SUSPEND; + } + + pd_port->pe_data.dpm_flags = dpm_flags; +} + + +int pd_dpm_send_sink_caps(struct pd_port *pd_port) +{ + struct pd_port_power_caps *snk_cap = &pd_port->local_snk_cap; + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + if (pd_check_rev30(pd_port)) + snk_cap->nr = pd_port->local_snk_cap_nr_pd30; + else + snk_cap->nr = pd_port->local_snk_cap_nr_pd20; +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + return pd_send_sop_data_msg(pd_port, PD_DATA_SINK_CAP, + snk_cap->nr, snk_cap->pdos); +} + +int pd_dpm_send_source_caps(struct pd_port *pd_port) +{ + uint8_t i; + uint32_t cable_curr = 3000; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + struct pd_port_power_caps *src_cap0 = &pd_port->local_src_cap_default; + struct pd_port_power_caps *src_cap1 = &pd_port->local_src_cap; + + if (pd_port->pe_data.power_cable_present) { + cable_curr = pd_get_cable_current_limit(pd_port); + DPM_DBG("cable_limit: %dmA\n", cable_curr); + } + + src_cap1->nr = src_cap0->nr; + for (i = 0; i < src_cap0->nr; i++) { + src_cap1->pdos[i] = + pd_reset_pdo_power(tcpc, src_cap0->pdos[i], cable_curr); + } + + return pd_send_sop_data_msg(pd_port, PD_DATA_SOURCE_CAP, + src_cap1->nr, src_cap1->pdos); +} + +void pd_dpm_inform_cable_id(struct pd_port *pd_port, bool src_startup) +{ +#ifdef CONFIG_USB_PD_REV30 + struct pe_data *pe_data = &pd_port->pe_data; +#endif /* CONFIG_USB_PD_REV30 */ + uint32_t *payload = pd_get_msg_vdm_data_payload(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (payload) { + memcpy(pd_port->pe_data.cable_vdos, payload, + pd_get_msg_data_size(pd_port)); + + DPM_DBG("InformCable, 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", + payload[0], payload[1], payload[2], payload[3]); + + dpm_reaction_clear(pd_port, DPM_REACTION_DISCOVER_CABLE); + } else { +#ifdef CONFIG_USB_PD_REV30 + if (pe_data->discover_id_counter >= PD_DISCOVER_ID30_COUNT) + pd_sync_sop_prime_spec_revision(pd_port, PD_REV20); +#endif /* CONFIG_USB_PD_REV30 */ + } + + if (src_startup) + pd_enable_timer(pd_port, PD_TIMER_SOURCE_START); + else + VDM_STATE_DPM_INFORMED(pd_port); +} + +static bool dpm_response_request(struct pd_port *pd_port, bool accept) +{ + if (accept) + return pd_put_dpm_ack_event(pd_port); + + return pd_put_dpm_nak_event(pd_port, PD_DPM_NAK_REJECT); +} + +/* ---- SNK ---- */ + +static void dpm_build_sink_pdo_info(struct dpm_pdo_info_t *sink_pdo_info, + uint8_t type, int request_v, int request_i) +{ + sink_pdo_info->type = type; + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + if (type == DPM_PDO_TYPE_APDO) { + request_v = (request_v / 20) * 20; + request_i = (request_i / 50) * 50; + } else +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + request_i = (request_i / 10) * 10; + + sink_pdo_info->vmin = sink_pdo_info->vmax = request_v; + sink_pdo_info->ma = request_i; + sink_pdo_info->uw = request_v * request_i; +} + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK +static int pps_request_thread_fn(void *data) +{ + struct tcpc_device *tcpc = data; + struct pd_port *pd_port = &tcpc->pd_port; + int ret = 0; + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_REQUEST_AGAIN, + }; + + while (true) { + ret = wait_event_interruptible(pd_port->pps_request_wait_que, + atomic_read(&pd_port->pps_request) || + kthread_should_stop()); + if (kthread_should_stop() || ret) { + dev_notice(&tcpc->dev, "%s exits(%d)\n", __func__, ret); + break; + } + while (!wait_event_timeout(pd_port->pps_request_wait_que, + !atomic_read(&pd_port->pps_request) || + kthread_should_stop(), + msecs_to_jiffies(7*1000))) { + pd_put_deferred_tcp_event(tcpc, &tcp_event); + } + } + + return 0; +} + +void pd_dpm_start_pps_request_thread(struct pd_port *pd_port, bool en) +{ + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + DPM_INFO("pps_thread (%s)\n", en ? "start" : "end"); + if (en) { + __pm_stay_awake(pd_port->pps_request_wake_lock); + atomic_set(&pd_port->pps_request, true); + wake_up(&pd_port->pps_request_wait_que); + } else { + atomic_set(&pd_port->pps_request, false); + wake_up(&pd_port->pps_request_wait_que); + __pm_relax(pd_port->pps_request_wake_lock); + } +} + +static bool dpm_build_request_info_apdo( + struct pd_port *pd_port, struct dpm_rdo_info_t *req_info, + struct pd_port_power_caps *src_cap, uint8_t charging_policy) +{ + struct dpm_pdo_info_t sink_pdo_info; + + dpm_build_sink_pdo_info(&sink_pdo_info, DPM_PDO_TYPE_APDO, + pd_port->request_v_apdo, pd_port->request_i_apdo); + + return dpm_find_match_req_info(req_info, + &sink_pdo_info, src_cap->nr, src_cap->pdos, + -1, charging_policy); +} +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + +static bool dpm_build_request_info_pdo( + struct pd_port *pd_port, struct dpm_rdo_info_t *req_info, + struct pd_port_power_caps *src_cap, uint8_t charging_policy) +{ + bool find_cap = false; + int i, max_uw = -1; + struct dpm_pdo_info_t sink_pdo_info; + struct pd_port_power_caps *snk_cap = &pd_port->local_snk_cap; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + for (i = 0; i < snk_cap->nr; i++) { + DPM_DBG("EvaSinkCap%d\n", i+1); + dpm_extract_pdo_info(snk_cap->pdos[i], &sink_pdo_info); + + find_cap = dpm_find_match_req_info(req_info, + &sink_pdo_info, src_cap->nr, src_cap->pdos, + max_uw, charging_policy); + + if (find_cap) { + if (req_info->type == DPM_PDO_TYPE_BAT) + max_uw = req_info->oper_uw; + else + max_uw = req_info->vmax * req_info->oper_ma; + + DPM_DBG("Find SrcCap%d(%s):%d mw\n", + req_info->pos, req_info->mismatch ? + "Mismatch" : "Match", max_uw/1000); + pd_port->pe_data.local_selected_cap = i + 1; + } + } + + return max_uw > 0; +} + +static bool dpm_build_request_info( + struct pd_port *pd_port, struct dpm_rdo_info_t *req_info) +{ + int i; + uint8_t charging_policy = pd_port->dpm_charging_policy; + struct pd_port_power_caps *src_cap = &pd_port->pe_data.remote_src_cap; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + memset(req_info, 0, sizeof(struct dpm_rdo_info_t)); + + DPM_INFO("Policy=0x%X\n", charging_policy); + + for (i = 0; i < src_cap->nr; i++) + DPM_DBG("SrcCap%d: 0x%08x\n", i+1, src_cap->pdos[i]); + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + if ((charging_policy & DPM_CHARGING_POLICY_MASK) + == DPM_CHARGING_POLICY_PPS) { + return dpm_build_request_info_apdo( + pd_port, req_info, src_cap, charging_policy); + } +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + return dpm_build_request_info_pdo( + pd_port, req_info, src_cap, charging_policy); +} + +static bool dpm_build_default_request_info( + struct pd_port *pd_port, struct dpm_rdo_info_t *req_info) +{ + struct dpm_pdo_info_t sink, source; + struct pd_port_power_caps *snk_cap = &pd_port->local_snk_cap; + struct pd_port_power_caps *src_cap = &pd_port->pe_data.remote_src_cap; + + pd_port->pe_data.local_selected_cap = 1; + + dpm_extract_pdo_info(snk_cap->pdos[0], &sink); + dpm_extract_pdo_info(src_cap->pdos[0], &source); + + req_info->pos = 1; + req_info->type = source.type; + req_info->mismatch = true; + req_info->vmax = 5000; + req_info->vmin = 5000; + + if (req_info->type == DPM_PDO_TYPE_BAT) { + req_info->max_uw = sink.uw; + req_info->oper_uw = source.uw; + + } else { + req_info->max_ma = sink.ma; + req_info->oper_ma = source.ma; + } + + return true; +} + +static inline void dpm_update_request_i_new( + struct pd_port *pd_port, struct dpm_rdo_info_t *req_info) +{ + if (req_info->mismatch) + pd_port->request_i_new = pd_port->request_i_op; + else + pd_port->request_i_new = pd_port->request_i_max; +} + +static inline void dpm_update_request_bat(struct pd_port *pd_port, + struct dpm_rdo_info_t *req_info, uint32_t flags) +{ + uint32_t mw_op, mw_max; + + mw_op = req_info->oper_uw / 1000; + mw_max = req_info->max_uw / 1000; + + pd_port->request_i_op = req_info->oper_uw / req_info->vmin; + pd_port->request_i_max = req_info->max_uw / req_info->vmin; + + dpm_update_request_i_new(pd_port, req_info); + + pd_port->last_rdo = RDO_BATT( + req_info->pos, mw_op, mw_max, flags); +} + +static inline void dpm_update_request_not_bat(struct pd_port *pd_port, + struct dpm_rdo_info_t *req_info, uint32_t flags) +{ + pd_port->request_i_op = req_info->oper_ma; + pd_port->request_i_max = req_info->max_ma; + + dpm_update_request_i_new(pd_port, req_info); + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + if (req_info->type == DPM_PDO_TYPE_APDO) { + pd_port->request_apdo_new = true; + pd_port->last_rdo = RDO_APDO( + req_info->pos, req_info->vmin, + req_info->oper_ma, flags); + return; + } +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + if (req_info->mismatch && (pd_port->cap_miss_match == 0x3)) { + pd_port->cap_miss_match = 0; + req_info->mismatch = 0; + flags &= ~RDO_CAP_MISMATCH; + pd_port->last_rdo = RDO_FIXED( + req_info->pos, req_info->oper_ma, + req_info->oper_ma, flags); + } else { + pd_port->last_rdo = RDO_FIXED( + req_info->pos, req_info->oper_ma, + req_info->max_ma, flags); + } +} + +static inline void dpm_update_request( + struct pd_port *pd_port, struct dpm_rdo_info_t *req_info) +{ + uint32_t flags = 0; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + pd_port->request_apdo_new = false; +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + if (pd_port->dpm_caps & DPM_CAP_LOCAL_GIVE_BACK) + flags |= RDO_GIVE_BACK; + + if (pd_port->dpm_caps & DPM_CAP_LOCAL_NO_SUSPEND) + flags |= RDO_NO_SUSPEND; + + if (pd_port->dpm_caps & DPM_CAP_LOCAL_USB_COMM) + flags |= RDO_COMM_CAP; + + if (req_info->mismatch) { + flags |= RDO_CAP_MISMATCH; + pd_port->cap_miss_match |= 0x1; + DPM_INFO("cap miss match case\n"); + } + + pd_port->request_v_new = req_info->vmax; + + if (req_info->type == DPM_PDO_TYPE_BAT) + dpm_update_request_bat(pd_port, req_info, flags); + else + dpm_update_request_not_bat(pd_port, req_info, flags); + +#ifdef CONFIG_USB_PD_DIRECT_CHARGE + pd_notify_pe_direct_charge(pd_port, + req_info->vmin < TCPC_VBUS_SINK_5V); +#endif /* CONFIG_USB_PD_DIRECT_CHARGE */ +} + +int pd_dpm_update_tcp_request(struct pd_port *pd_port, + struct tcp_dpm_pd_request *pd_req) +{ + bool find_cap = false; + uint8_t type = DPM_PDO_TYPE_FIXED; + struct dpm_rdo_info_t req_info; + struct dpm_pdo_info_t sink_pdo_info; + uint8_t charging_policy = pd_port->dpm_charging_policy; + struct pd_port_power_caps *src_cap = &pd_port->pe_data.remote_src_cap; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + memset(&req_info, 0, sizeof(struct dpm_rdo_info_t)); + + DPM_DBG("charging_policy=0x%X\n", charging_policy); + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + if ((charging_policy & DPM_CHARGING_POLICY_MASK) + == DPM_CHARGING_POLICY_PPS) + type = DPM_PDO_TYPE_APDO; +#endif /*CONFIG_USB_PD_REV30_PPS_SINK */ + + dpm_build_sink_pdo_info(&sink_pdo_info, type, pd_req->mv, pd_req->ma); + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + if (pd_port->request_apdo && + (sink_pdo_info.vmin == pd_port->request_v) && + (sink_pdo_info.ma == pd_port->request_i)) + return TCP_DPM_RET_DENIED_REPEAT_REQUEST; +#endif /*CONFIG_USB_PD_REV30_PPS_SINK */ + + find_cap = dpm_find_match_req_info(&req_info, + &sink_pdo_info, src_cap->nr, src_cap->pdos, + -1, charging_policy); + + if (!find_cap) { + DPM_INFO("Can't find match_cap\n"); + return TCP_DPM_RET_DENIED_INVALID_REQUEST; + } + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + if ((charging_policy & DPM_CHARGING_POLICY_MASK) + == DPM_CHARGING_POLICY_PPS) { + pd_port->request_v_apdo = sink_pdo_info.vmin; + pd_port->request_i_apdo = sink_pdo_info.ma; + } +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + dpm_update_request(pd_port, &req_info); + return TCP_DPM_RET_SUCCESS; +} + +int pd_dpm_update_tcp_request_ex(struct pd_port *pd_port, + struct tcp_dpm_pd_request_ex *pd_req) +{ + struct dpm_pdo_info_t source; + struct dpm_rdo_info_t req_info; + struct pd_port_power_caps *src_cap = &pd_port->pe_data.remote_src_cap; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_req->pos > src_cap->nr) + return false; + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + if (pd_port->dpm_charging_policy == DPM_CHARGING_POLICY_PPS) { + DPM_INFO("Reject tcp_rqeuest_ex if charging_policy=pps\n"); + return TCP_DPM_RET_DENIED_INVALID_REQUEST; + } +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + dpm_extract_pdo_info(src_cap->pdos[pd_req->pos-1], &source); + + req_info.pos = pd_req->pos; + req_info.type = source.type; + req_info.mismatch = false; + req_info.vmax = source.vmax; + req_info.vmin = source.vmin; + + if (req_info.type == DPM_PDO_TYPE_BAT) { + req_info.max_uw = pd_req->max_uw; + req_info.oper_uw = pd_req->oper_uw; + if (pd_req->oper_uw < pd_req->max_uw) + req_info.mismatch = true; + } else { + req_info.max_ma = pd_req->max_ma; + req_info.oper_ma = pd_req->oper_ma; + if (pd_req->oper_ma < pd_req->max_ma) + req_info.mismatch = true; + } + + dpm_update_request(pd_port, &req_info); + return TCP_DPM_RET_SUCCESS; +} + +int pd_dpm_update_tcp_request_again(struct pd_port *pd_port) +{ + bool find_cap = false; + int sink_nr, source_nr; + struct dpm_rdo_info_t req_info; + struct pd_port_power_caps *snk_cap = &pd_port->local_snk_cap; + struct pd_port_power_caps *src_cap = &pd_port->pe_data.remote_src_cap; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + sink_nr = snk_cap->nr; + source_nr = src_cap->nr; + + if ((source_nr <= 0) || (sink_nr <= 0)) { + DPM_INFO("SrcNR or SnkNR = 0\n"); + return TCP_DPM_RET_DENIED_INVALID_REQUEST; + } + + find_cap = dpm_build_request_info(pd_port, &req_info); + + /* If we can't find any cap to use, choose default setting */ + if (!find_cap) { + DPM_INFO("Can't find any SrcCap\n"); + dpm_build_default_request_info(pd_port, &req_info); + } else + DPM_INFO("Select SrcCap%d\n", req_info.pos); + + dpm_update_request(pd_port, &req_info); + return TCP_DPM_RET_SUCCESS; +} + +void pd_dpm_snk_evaluate_caps(struct pd_port *pd_port) +{ + bool find_cap = false; + struct dpm_rdo_info_t req_info; + struct pd_port_power_caps *snk_cap = &pd_port->local_snk_cap; + struct pd_port_power_caps *src_cap = &pd_port->pe_data.remote_src_cap; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + PD_BUG_ON(pd_get_msg_data_payload(pd_port) == NULL); + + pd_dpm_dr_inform_source_cap(pd_port); + + if ((src_cap->nr <= 0) || (snk_cap->nr <= 0)) { + DPM_INFO("SrcNR or SnkNR = 0\n"); + return; + } + + find_cap = dpm_build_request_info(pd_port, &req_info); + + /* If we can't find any cap to use, choose default setting */ + if (!find_cap) { + DPM_INFO("Can't find any SrcCap\n"); + dpm_build_default_request_info(pd_port, &req_info); + } else + DPM_INFO("Select SrcCap%d\n", req_info.pos); + + dpm_update_request(pd_port, &req_info); + + if (req_info.pos > 0) + pd_put_dpm_notify_event(pd_port, req_info.pos); +} + +void pd_dpm_snk_standby_power(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_SNK_STANDBY_POWER + /* + * pSnkStdby : + * Maximum power consumption while in Sink Standby. (2.5W) + * I1 = (pSnkStdby/VBUS) + * I2 = (pSnkStdby/VBUS) + cSnkBulkPd(DVBUS/Dt) + * STANDBY_UP = I1 < I2, STANDBY_DOWN = I1 > I2 + * + * tSnkNewPower (t1): + * Maximum transition time between power levels. (15ms) + */ + + uint8_t type; + int ma = -1; + int standby_curr = 2500000 / max(pd_port->request_v, + pd_port->request_v_new); + +#ifdef CONFIG_USB_PD_VCONN_SAFE5V_ONLY + struct tcpc_device *tcpc = pd_port->tcpc; + struct pe_data *pe_data = &pd_port->pe_data; + bool vconn_highv_prot = pd_port->request_v_new > 5000; + + if (!pe_data->vconn_highv_prot && vconn_highv_prot && + tcpc->tcpc_flags & TCPC_FLAGS_VCONN_SAFE5V_ONLY) { + PE_INFO("VC_HIGHV_PROT: %d\n", vconn_highv_prot); + pe_data->vconn_highv_prot_role = pd_port->vconn_role; + pd_set_vconn(pd_port, PD_ROLE_VCONN_OFF); + pe_data->vconn_highv_prot = vconn_highv_prot; + } +#endif /* CONFIG_USB_PD_VCONN_SAFE5V_ONLY */ + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + /* + * A Sink is not required to transition to Sink Standby + * when operating with a Programmable Power Supply + * (Check it later, Aginst new spec) + */ + if (pd_port->request_apdo_new) + return; +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + if (pd_port->request_v_new > pd_port->request_v) { + /* Case2 Increasing the Voltage */ + /* Case3 Increasing the Voltage and Current */ + /* Case4 Increasing the Voltage and Decreasing the Curren */ + ma = standby_curr; + type = TCP_VBUS_CTRL_STANDBY_UP; + } else if (pd_port->request_v_new < pd_port->request_v) { + /* Case5 Decreasing the Voltage and Increasing the Current */ + /* Case7 Decreasing the Voltage */ + /* Case8 Decreasing the Voltage and the Current*/ + ma = standby_curr; + type = TCP_VBUS_CTRL_STANDBY_DOWN; + } else if (pd_port->request_i_new < pd_port->request_i) { + /* Case6 Decreasing the Current, t1 i = new */ + ma = pd_port->request_i_new; + type = TCP_VBUS_CTRL_STANDBY; + } + + if (ma >= 0) { + tcpci_sink_vbus( + pd_port->tcpc, type, pd_port->request_v_new, ma); + } +#else +#ifdef CONFIG_USB_PD_SNK_GOTOMIN + tcpci_sink_vbus(pd_port->tcpc, TCP_VBUS_CTRL_REQUEST, + pd_port->request_v, pd_port->request_i_new); +#endif /* CONFIG_USB_PD_SNK_GOTOMIN */ +#endif /* CONFIG_USB_PD_SNK_STANDBY_POWER */ +} + +void pd_dpm_snk_transition_power(struct pd_port *pd_port) +{ + tcpci_sink_vbus(pd_port->tcpc, TCP_VBUS_CTRL_REQUEST, + pd_port->request_v_new, pd_port->request_i_new); + + pd_port->request_v = pd_port->request_v_new; + pd_port->request_i = pd_port->request_i_new; + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + if (pd_port->request_apdo != pd_port->request_apdo_new) { + pd_port->request_apdo = pd_port->request_apdo_new; + pd_dpm_start_pps_request_thread( + pd_port, pd_port->request_apdo_new); + } +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ +} + +void pd_dpm_snk_hard_reset(struct pd_port *pd_port) +{ + /* + * tSnkHardResetPrepare : + * Time allotted for the Sink power electronics + * to prepare for a Hard Reset + */ + + int mv = 0, ma = 0; + bool ignore_hreset = false; + +#ifdef CONFIG_USB_PD_SNK_HRESET_KEEP_DRAW + if (!pd_port->pe_data.pd_prev_connected) { +#ifdef CONFIG_USB_PD_SNK_IGNORE_HRESET_IF_TYPEC_ONLY + ignore_hreset = true; +#else + ma = -1; + mv = TCPC_VBUS_SINK_5V; +#endif /* CONFIG_USB_PD_SNK_IGNORE_HRESET_IF_TYPEC_ONLY */ + } +#endif /* CONFIG_USB_PD_SNK_HRESET_KEEP_DRAW */ + + if (!ignore_hreset) { + tcpci_sink_vbus( + pd_port->tcpc, TCP_VBUS_CTRL_HRESET, mv, ma); + } + + pd_put_pe_event(pd_port, PD_PE_POWER_ROLE_AT_DEFAULT); +} + +/* ---- SRC ---- */ + +static inline bool dpm_evaluate_request( + struct pd_port *pd_port, uint32_t rdo, uint8_t rdo_pos) +{ + uint32_t pdo; + uint32_t sink_v; + uint32_t op_curr, max_curr; + struct dpm_pdo_info_t src_info; + struct pd_port_power_caps *src_cap = &pd_port->local_src_cap; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + pd_port->pe_data.dpm_flags &= (~DPM_FLAGS_PARTNER_MISMATCH); + + if ((rdo_pos == 0) || (rdo_pos > src_cap->nr)) { + DPM_INFO("RequestPos Wrong (%d)\n", rdo_pos); + return false; + } + + pdo = src_cap->pdos[rdo_pos-1]; + + dpm_extract_pdo_info(pdo, &src_info); + pd_extract_rdo_power(rdo, pdo, &op_curr, &max_curr); + + if (src_info.ma < op_curr) { + DPM_INFO("src_i (%d) < op_i (%d)\n", src_info.ma, op_curr); + return false; + } + + if (rdo & RDO_CAP_MISMATCH) { + /* TODO: handle it later */ + DPM_INFO("CAP_MISMATCH\n"); + pd_port->pe_data.dpm_flags |= DPM_FLAGS_PARTNER_MISMATCH; + } else if (src_info.ma < max_curr) { + DPM_INFO("src_i (%d) < max_i (%d)\n", src_info.ma, max_curr); + return false; + } + + sink_v = src_info.vmin; + +#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE + if ((pdo & PDO_TYPE_MASK) == PDO_TYPE_APDO) { + sink_v = RDO_APDO_EXTRACT_OP_MV(rdo); + + if ((sink_v < src_info.vmin) || (sink_v > src_info.vmax)) { + DPM_INFO("sink_v (%d) not in src_v (%d~%d)\n", + sink_v, src_info.vmin, src_info.vmax); + return false; + } + } +#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */ + + /* Accept request */ + + pd_port->request_i_op = op_curr; + pd_port->request_i_max = max_curr; + + if (rdo & RDO_CAP_MISMATCH) + pd_port->request_i_new = op_curr; + else + pd_port->request_i_new = max_curr; + + pd_port->request_v_new = sink_v; + return true; +} + +void pd_dpm_src_evaluate_request(struct pd_port *pd_port) +{ + uint32_t rdo; + uint8_t rdo_pos; + struct pe_data *pe_data; + uint32_t *payload = pd_get_msg_data_payload(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + PD_BUG_ON(payload == NULL); + + rdo = payload[0]; + rdo_pos = RDO_POS(rdo); + + DPM_INFO("RequestCap%d\n", rdo_pos); + + pe_data = &pd_port->pe_data; + + if (dpm_evaluate_request(pd_port, rdo, rdo_pos)) { + pe_data->local_selected_cap = rdo_pos; + pd_put_dpm_notify_event(pd_port, rdo_pos); + } else { + /* + * "Contract Invalid" means that the previously + * negotiated Voltage and Current values + * are no longer included in the Sources new Capabilities. + * If the Sink fails to make a valid Request in this case + * then Power Delivery operation is no longer possible + * and Power Delivery mode is exited with a Hard Reset. + */ + + pe_data->invalid_contract = false; + pe_data->local_selected_cap = 0; + pd_put_dpm_nak_event(pd_port, PD_DPM_NAK_REJECT); + } +} + +void pd_dpm_src_transition_power(struct pd_port *pd_port) +{ + pd_enable_vbus_stable_detection(pd_port); + +#ifdef CONFIG_USB_PD_SRC_HIGHCAP_POWER + if (pd_port->request_v > pd_port->request_v_new) { + mutex_lock(&pd_port->tcpc->access_lock); + tcpci_enable_force_discharge( + pd_port->tcpc, true, pd_port->request_v_new); + mutex_unlock(&pd_port->tcpc->access_lock); + } +#endif /* CONFIG_USB_PD_SRC_HIGHCAP_POWER */ + + tcpci_source_vbus(pd_port->tcpc, TCP_VBUS_CTRL_REQUEST, + pd_port->request_v_new, pd_port->request_i_new); + + if (pd_port->request_v == pd_port->request_v_new) + pd_put_vbus_stable_event(pd_port->tcpc); +#if CONFIG_USB_PD_VBUS_STABLE_TOUT + else + pd_enable_timer(pd_port, PD_TIMER_VBUS_STABLE); +#endif /* CONFIG_USB_PD_VBUS_STABLE_TOUT */ + + pd_port->request_v = pd_port->request_v_new; + pd_port->request_i = pd_port->request_i_new; +} + +void pd_dpm_src_hard_reset(struct pd_port *pd_port) +{ + tcpci_source_vbus(pd_port->tcpc, + TCP_VBUS_CTRL_HRESET, TCPC_VBUS_SOURCE_0V, 0); + pd_enable_vbus_safe0v_detection(pd_port); +} + +/* ---- UFP : update_svid_data ---- */ + +static inline bool dpm_ufp_update_svid_data_enter_mode( + struct pd_port *pd_port, uint16_t svid, uint8_t ops) +{ + struct svdm_svid_data *svid_data; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + DPM_DBG("EnterMode (svid0x%04x, ops:%d)\n", svid, ops); + + svid_data = dpm_get_svdm_svid_data(pd_port, svid); + + if (svid_data == NULL) + return false; + + /* Only accept 1 mode active at the same time */ + if (svid_data->active_mode) + return false; + + if ((ops == 0) || (ops > svid_data->local_mode.mode_cnt)) + return false; + + svid_data->active_mode = ops; + pd_port->pe_data.modal_operation = true; + + svdm_ufp_request_enter_mode(pd_port, svid, ops); + + tcpci_enter_mode(tcpc, svid, ops, svid_data->local_mode.mode_vdo[ops]); + return true; +} + +static inline bool dpm_ufp_update_svid_data_exit_mode( + struct pd_port *pd_port, uint16_t svid, uint8_t ops) +{ + uint8_t i; + bool modal_operation; + struct svdm_svid_data *svid_data; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + DPM_DBG("ExitMode (svid0x%04x, mode:%d)\n", svid, ops); + + svid_data = dpm_get_svdm_svid_data(pd_port, svid); + + if (svid_data == NULL) + return false; + + if (svid_data->active_mode == 0) + return false; + + if ((ops == 0) || (ops == svid_data->active_mode)) { + svid_data->active_mode = 0; + + modal_operation = false; + for (i = 0; i < pd_port->svid_data_cnt; i++) { + svid_data = &pd_port->svid_data[i]; + + if (svid_data->active_mode) { + modal_operation = true; + break; + } + } + + pd_port->pe_data.modal_operation = modal_operation; + + svdm_ufp_request_exit_mode(pd_port, svid, ops); + tcpci_exit_mode(pd_port->tcpc, svid); + return true; + } + + return false; +} + + +/* ---- UFP : Response VDM Request ---- */ + +static int dpm_vdm_ufp_response_id(struct pd_port *pd_port) +{ + if (pd_check_rev30(pd_port)) + pd_port->id_vdos[0] = pd_port->id_header; + else + pd_port->id_vdos[0] = VDO_IDH_PD20(pd_port->id_header); + + return pd_reply_svdm_request(pd_port, CMDT_RSP_ACK, + pd_check_rev30(pd_port) ? pd_port->id_vdo_nr : 3, + pd_port->id_vdos); +} + +static int dpm_ufp_response_svids(struct pd_port *pd_port) +{ + struct svdm_svid_data *svid_data; + uint16_t svid_list[2]; + uint32_t svids[VDO_MAX_NR]; + uint8_t i = 0, j = 0, cnt = pd_port->svid_data_cnt; + + PD_BUG_ON(pd_port->svid_data_cnt >= VDO_MAX_SVID_NR); + + while (i < cnt) { + svid_data = &pd_port->svid_data[i++]; + svid_list[0] = svid_data->svid; + + if (i < cnt) { + svid_data = &pd_port->svid_data[i++]; + svid_list[1] = svid_data->svid; + } else + svid_list[1] = 0; + + svids[j++] = VDO_SVID(svid_list[0], svid_list[1]); + } + + if ((cnt % 2) == 0) + svids[j++] = VDO_SVID(0, 0); + + return pd_reply_svdm_request(pd_port, CMDT_RSP_ACK, j, svids); +} + +static int dpm_vdm_ufp_response_modes(struct pd_port *pd_port) +{ + struct svdm_svid_data *svid_data; + uint16_t svid = dpm_vdm_get_svid(pd_port); + + svid_data = dpm_get_svdm_svid_data(pd_port, svid); + + PD_BUG_ON(svid_data == NULL); + + return pd_reply_svdm_request( + pd_port, CMDT_RSP_ACK, + svid_data->local_mode.mode_cnt, + svid_data->local_mode.mode_vdo); +} + +/* ---- UFP : Handle VDM Request ---- */ + +void pd_dpm_ufp_request_id_info(struct pd_port *pd_port) +{ + bool ack = dpm_vdm_get_svid(pd_port) == USB_SID_PD; + + if (!ack) { + dpm_vdm_reply_svdm_nak(pd_port); + return; + } + + dpm_vdm_ufp_response_id(pd_port); +} + +void pd_dpm_ufp_request_svid_info(struct pd_port *pd_port) +{ + bool ack = false; + + if (pd_is_support_modal_operation(pd_port)) + ack = (dpm_vdm_get_svid(pd_port) == USB_SID_PD); + + if (!ack) { + dpm_vdm_reply_svdm_nak(pd_port); + return; + } + + dpm_ufp_response_svids(pd_port); +} + +void pd_dpm_ufp_request_mode_info(struct pd_port *pd_port) +{ + uint16_t svid = dpm_vdm_get_svid(pd_port); + bool ack = dpm_get_svdm_svid_data(pd_port, svid) != NULL; + + if (!ack) { + dpm_vdm_reply_svdm_nak(pd_port); + return; + } + + dpm_vdm_ufp_response_modes(pd_port); +} + +void pd_dpm_ufp_request_enter_mode(struct pd_port *pd_port) +{ + bool ack = dpm_ufp_update_svid_data_enter_mode(pd_port, + dpm_vdm_get_svid(pd_port), dpm_vdm_get_ops(pd_port)); + + dpm_vdm_reply_svdm_request(pd_port, ack); +} + +void pd_dpm_ufp_request_exit_mode(struct pd_port *pd_port) +{ + bool ack = dpm_ufp_update_svid_data_exit_mode(pd_port, + dpm_vdm_get_svid(pd_port), dpm_vdm_get_ops(pd_port)); + + dpm_vdm_reply_svdm_request(pd_port, ack); +} + +/* ---- DFP : update_svid_data ---- */ + +static inline void dpm_dfp_update_partner_id( + struct pd_port *pd_port, uint32_t *payload) +{ +#ifdef CONFIG_USB_PD_KEEP_PARTNER_ID + uint8_t cnt = pd_get_msg_vdm_data_count(pd_port); + uint32_t size = sizeof(uint32_t) * (cnt); + + pd_port->pe_data.partner_id_present = true; + memcpy(pd_port->pe_data.partner_vdos, payload, size); +#endif /* CONFIG_USB_PD_KEEP_PARTNER_ID */ +} +static inline void dpm_dfp_update_svid_data_exist( + struct pd_port *pd_port, uint16_t svid) +{ + uint8_t k; + struct svdm_svid_data *svid_data; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; +#ifdef CONFIG_USB_PD_KEEP_SVIDS + struct svdm_svid_list *list = &pd_port->pe_data.remote_svid_list; + + if (list->cnt < VDO_MAX_SVID_NR) + list->svids[list->cnt++] = svid; + else + DPM_INFO("ERR:SVIDCNT\n"); +#endif /* CONFIG_USB_PD_KEEP_SVIDS */ + + for (k = 0; k < pd_port->svid_data_cnt; k++) { + + svid_data = &pd_port->svid_data[k]; + + if (svid_data->svid == svid) + svid_data->exist = 1; + } +} + +static inline void dpm_dfp_update_svid_data_modes(struct pd_port *pd_port, + uint16_t svid, uint32_t *mode_list, uint8_t count) +{ + uint8_t i; + struct svdm_svid_data *svid_data; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + DPM_DBG("InformMode (0x%04x:%d):\n", svid, count); + for (i = 0; i < count; i++) + DPM_DBG("Mode[%d]: 0x%08x\n", i, mode_list[i]); + + svid_data = dpm_get_svdm_svid_data(pd_port, svid); + if (svid_data == NULL) + return; + + svid_data->remote_mode.mode_cnt = count; + + if (count != 0) { + memcpy(svid_data->remote_mode.mode_vdo, + mode_list, sizeof(uint32_t) * count); + } +} + +static inline void dpm_dfp_update_svid_enter_mode( + struct pd_port *pd_port, uint16_t svid, uint8_t ops) +{ + struct svdm_svid_data *svid_data; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + DPM_DBG("EnterMode (svid0x%04x, mode:%d)\n", svid, ops); + + svid_data = dpm_get_svdm_svid_data(pd_port, svid); + if (svid_data == NULL) + return; + + svid_data->active_mode = ops; + pd_port->pe_data.modal_operation = true; + + tcpci_enter_mode(tcpc, + svid_data->svid, ops, svid_data->remote_mode.mode_vdo[ops]); +} + +static inline void dpm_dfp_update_svid_data_exit_mode( + struct pd_port *pd_port, uint16_t svid, uint8_t ops) +{ + uint8_t i; + bool modal_operation; + struct svdm_svid_data *svid_data; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + DPM_DBG("ExitMode (svid0x%04x, mode:%d)\n", svid, ops); + + svid_data = dpm_get_svdm_svid_data(pd_port, svid); + if (svid_data == NULL) + return; + + if ((ops == 7) || (ops == svid_data->active_mode)) { + svid_data->active_mode = 0; + + modal_operation = false; + for (i = 0; i < pd_port->svid_data_cnt; i++) { + + svid_data = &pd_port->svid_data[i]; + + if (svid_data->active_mode) { + modal_operation = true; + break; + } + } + + pd_port->pe_data.modal_operation = modal_operation; + tcpci_exit_mode(tcpc, svid); + } +} + + +/* ---- DFP : Inform VDM Result ---- */ + +void pd_dpm_dfp_inform_id(struct pd_port *pd_port, bool ack) +{ + uint32_t *payload = pd_get_msg_vdm_data_payload(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + VDM_STATE_DPM_INFORMED(pd_port); + + if (!payload) { + dpm_reaction_clear(pd_port, DPM_REACTION_DISCOVER_ID | + DPM_REACTION_DISCOVER_SVID); + return; + } + + if (ack) { + DPM_DBG("InformID, 0x%02x, 0x%02x, 0x%02x, 0x%02x\n", + payload[0], payload[1], payload[2], payload[3]); + + dpm_dfp_update_partner_id(pd_port, payload); + } + + if (!pd_port->pe_data.vdm_discard_retry_flag) { + /* + * For PD compliance test, + * If device doesn't reply discoverID + * or doesn't support modal operation, + * then don't send discoverSVID + */ + if (!ack || !(payload[0] & PD_IDH_MODAL_SUPPORT)) + dpm_reaction_clear(pd_port, DPM_REACTION_DISCOVER_SVID); + else + dpm_reaction_set(pd_port, DPM_REACTION_DISCOVER_SVID); + + svdm_dfp_inform_id(pd_port, ack); + dpm_reaction_clear(pd_port, DPM_REACTION_DISCOVER_ID); + } +} + +static inline int dpm_dfp_consume_svids( + struct pd_port *pd_port, uint32_t *svid_list, uint8_t count) +{ + bool discover_again = true; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + uint8_t i, j; + uint16_t svid[2]; + + DPM_DBG("InformSVID (%d):\n", count); + + if (count < 6) + discover_again = false; + + for (i = 0; i < count; i++) { + svid[0] = PD_VDO_SVID_SVID0(svid_list[i]); + svid[1] = PD_VDO_SVID_SVID1(svid_list[i]); + + DPM_DBG("svid[%d]: 0x%04x 0x%04x\n", i, svid[0], svid[1]); + + for (j = 0; j < 2; j++) { + if (svid[j] == 0) { + discover_again = false; + break; + } + + dpm_dfp_update_svid_data_exist(pd_port, svid[j]); + } + } + + if (discover_again) { + DPM_DBG("DiscoverSVID Again\n"); + pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_DISCOVER_SVIDS); + return 1; + } + + return 0; +} + +void pd_dpm_dfp_inform_svids(struct pd_port *pd_port, bool ack) +{ + uint8_t count; + uint32_t *svid_list; + + VDM_STATE_DPM_INFORMED(pd_port); + + if (ack) { + count = pd_get_msg_vdm_data_count(pd_port); + svid_list = pd_get_msg_vdm_data_payload(pd_port); + if (!svid_list) + return; + if (dpm_dfp_consume_svids(pd_port, svid_list, count)) + return; + } + + if (!pd_port->pe_data.vdm_discard_retry_flag) { + svdm_dfp_inform_svids(pd_port, ack); + dpm_reaction_clear(pd_port, DPM_REACTION_DISCOVER_SVID); + } +} + +void pd_dpm_dfp_inform_modes(struct pd_port *pd_port, bool ack) +{ + uint8_t count; + uint16_t svid = 0; + uint32_t *payload; + uint16_t expected_svid = pd_port->mode_svid; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (ack) { + svid = dpm_vdm_get_svid(pd_port); + + if (svid != expected_svid) { + ack = false; + DPM_INFO("Not expected SVID (0x%04x, 0x%04x)\n", + svid, expected_svid); + } else { + count = pd_get_msg_vdm_data_count(pd_port); + payload = pd_get_msg_vdm_data_payload(pd_port); + if (payload) + dpm_dfp_update_svid_data_modes( + pd_port, svid, payload, count); + } + } + + svdm_dfp_inform_modes(pd_port, expected_svid, ack); + VDM_STATE_DPM_INFORMED(pd_port); +} + +void pd_dpm_dfp_inform_enter_mode(struct pd_port *pd_port, bool ack) +{ + uint8_t ops = 0; + uint16_t svid = 0; + uint16_t expected_svid = pd_port->mode_svid; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (ack) { + ops = dpm_vdm_get_ops(pd_port); + svid = dpm_vdm_get_svid(pd_port); + + /* TODO: check ops later ?! */ + if (svid != expected_svid) { + ack = false; + DPM_INFO("Not expected SVID (0x%04x, 0x%04x)\n", + svid, expected_svid); + } else { + dpm_dfp_update_svid_enter_mode(pd_port, svid, ops); + } + } + + svdm_dfp_inform_enter_mode(pd_port, expected_svid, ops, ack); + VDM_STATE_DPM_INFORMED(pd_port); +} + +void pd_dpm_dfp_inform_exit_mode(struct pd_port *pd_port) +{ + uint8_t ops = dpm_vdm_get_ops(pd_port); + uint16_t svid = dpm_vdm_get_svid(pd_port); + uint8_t expected_ops = pd_port->mode_obj_pos; + uint16_t expected_svid = pd_port->mode_svid; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if ((expected_svid != svid) || (expected_ops != ops)) + DPM_DBG("expected_svid & ops wrong\n"); + + dpm_dfp_update_svid_data_exit_mode( + pd_port, expected_svid, expected_ops); + + svdm_dfp_inform_exit_mode(pd_port, expected_svid, expected_ops); + VDM_STATE_DPM_INFORMED(pd_port); +} + +void pd_dpm_dfp_inform_attention(struct pd_port *pd_port) +{ +#if DPM_DBG_ENABLE + uint8_t ops = dpm_vdm_get_ops(pd_port); +#endif + uint16_t svid = dpm_vdm_get_svid(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + DPM_DBG("Attention (svid0x%04x, mode:%d)\n", svid, ops); + + svdm_dfp_inform_attention(pd_port, svid); + VDM_STATE_DPM_INFORMED(pd_port); +} + +/* ---- Unstructured VDM ---- */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM + +void pd_dpm_ufp_recv_uvdm(struct pd_port *pd_port) +{ + struct svdm_svid_data *svid_data; + uint16_t svid = dpm_vdm_get_svid(pd_port); + + svid_data = dpm_get_svdm_svid_data(pd_port, svid); + + pd_port->uvdm_svid = svid; + pd_port->uvdm_cnt = pd_get_msg_data_count(pd_port); + + memcpy(pd_port->uvdm_data, + pd_get_msg_data_payload(pd_port), + pd_get_msg_data_size(pd_port)); + + if (svid_data) { + if (svid_data->ops->ufp_notify_uvdm) + svid_data->ops->ufp_notify_uvdm(pd_port, svid_data); + else + VDM_STATE_DPM_INFORMED(pd_port); + + tcpci_notify_uvdm(pd_port->tcpc, true); + } else { + pd_put_dpm_event(pd_port, PD_DPM_NOT_SUPPORT); + VDM_STATE_DPM_INFORMED(pd_port); + } +} + +void pd_dpm_dfp_send_uvdm(struct pd_port *pd_port) +{ + pd_send_custom_vdm(pd_port, TCPC_TX_SOP); + pd_port->uvdm_svid = PD_VDO_VID(pd_port->uvdm_data[0]); + + if (pd_port->uvdm_wait_resp) + VDM_STATE_RESPONSE_CMD(pd_port, PD_TIMER_UVDM_RESPONSE); +} + +void pd_dpm_dfp_inform_uvdm(struct pd_port *pd_port, bool ack) +{ + uint16_t svid; + uint16_t expected_svid = pd_port->uvdm_svid; + struct svdm_svid_data *svid_data = + dpm_get_svdm_svid_data(pd_port, expected_svid); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (ack && pd_port->uvdm_wait_resp) { + svid = dpm_vdm_get_svid(pd_port); + + if (svid != expected_svid) { + ack = false; + DPM_INFO("Not expected SVID (0x%04x, 0x%04x)\n", + svid, expected_svid); + } else { + pd_port->uvdm_cnt = pd_get_msg_data_count(pd_port); + memcpy(pd_port->uvdm_data, + pd_get_msg_data_payload(pd_port), + pd_get_msg_data_size(pd_port)); + } + } + + if (svid_data) { + if (svid_data->ops->dfp_notify_uvdm) + svid_data->ops->dfp_notify_uvdm( + pd_port, svid_data, ack); + } + + tcpci_notify_uvdm(tcpc, ack); + pd_notify_tcp_vdm_event_2nd_result(pd_port, + ack ? TCP_DPM_RET_VDM_ACK : TCP_DPM_RET_VDM_NAK); + VDM_STATE_DPM_INFORMED(pd_port); +} + +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + +void pd_dpm_ufp_send_svdm_nak(struct pd_port *pd_port) +{ + dpm_vdm_reply_svdm_nak(pd_port); +} + +/* + * DRP : Inform Source/Sink Cap + */ + +void pd_dpm_dr_inform_sink_cap(struct pd_port *pd_port) +{ + const uint32_t reaction_clear = DPM_REACTION_GET_SINK_CAP + | DPM_REACTION_ATTEMPT_GET_FLAG; + + struct pd_event *pd_event = pd_get_curr_pd_event(pd_port); + struct pd_port_power_caps *snk_cap = &pd_port->pe_data.remote_snk_cap; + + if (!pd_event_data_msg_match(pd_event, PD_DATA_SINK_CAP)) + return; + + snk_cap->nr = pd_get_msg_data_count(pd_port); + memcpy(snk_cap->pdos, + pd_get_msg_data_payload(pd_port), + pd_get_msg_data_size(pd_port)); + + pd_dpm_update_pdos_flags(pd_port, snk_cap->pdos[0]); + + dpm_reaction_clear(pd_port, reaction_clear); +} + +void pd_dpm_dr_inform_source_cap(struct pd_port *pd_port) +{ + uint32_t reaction_clear = DPM_REACTION_GET_SOURCE_CAP + | DPM_REACTION_ATTEMPT_GET_FLAG; + + struct pd_event *pd_event = pd_get_curr_pd_event(pd_port); + struct pd_port_power_caps *src_cap = &pd_port->pe_data.remote_src_cap; + + if (!pd_event_data_msg_match(pd_event, PD_DATA_SOURCE_CAP)) + return; + + src_cap->nr = pd_get_msg_data_count(pd_port); + memcpy(src_cap->pdos, + pd_get_msg_data_payload(pd_port), + pd_get_msg_data_size(pd_port)); + + pd_dpm_update_pdos_flags(pd_port, src_cap->pdos[0]); + + if (!(pd_port->pe_data.dpm_flags & DPM_FLAGS_PARTNER_DR_POWER)) + reaction_clear |= DPM_REACTION_GET_SINK_CAP; + + dpm_reaction_clear(pd_port, reaction_clear); +} + +/* + * DRP : Data Role Swap + */ + +#ifdef CONFIG_USB_PD_DR_SWAP + +void pd_dpm_drs_evaluate_swap(struct pd_port *pd_port, uint8_t role) +{ + pd_put_dpm_ack_event(pd_port); +} + +void pd_dpm_drs_change_role(struct pd_port *pd_port, uint8_t role) +{ + pd_set_data_role(pd_port, role); + + pd_port->pe_data.pe_ready = false; + +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + pd_port->pe_data.pd_traffic_idle = false; +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ + +#ifdef CONFIG_USB_PD_DFP_FLOW_DELAY_DRSWAP + dpm_reaction_set(pd_port, DPM_REACTION_DFP_FLOW_DELAY); +#else + dpm_reaction_clear(pd_port, DPM_REACTION_DFP_FLOW_DELAY); +#endif /* CONFIG_USB_PD_DFP_FLOW_DELAY_DRSWAP */ + + PE_STATE_DPM_INFORMED(pd_port); +} + +#endif /* CONFIG_USB_PD_DR_SWAP */ + +/* + * DRP : Power Role Swap + */ + +#ifdef CONFIG_USB_PD_PR_SWAP + +#if 0 +static bool pd_dpm_evaluate_source_cap_match(pd_port_t *pd_port) +{ + int i, j; + bool find_cap = false; + struct dpm_pdo_info_t sink, source; + struct pd_port_power_caps *snk_cap = &pd_port->local_snk_cap; + struct pd_port_power_caps *src_cap = &pd_port->pe_data.remote_src_cap; + + if ((src_cap->nr <= 0) || (snk_cap->nr <= 0)) + return false; + + for (j = 0; (j < snk_cap->nr) && (!find_cap); j++) { + dpm_extract_pdo_info(snk_cap->pdos[j], &sink); + + for (i = 0; (i < src_cap->nr) && (!find_cap); i++) { + dpm_extract_pdo_info(src_cap->pdos[i], &source); + + find_cap = dpm_is_valid_pdo_pair( + &sink, &source, pd_port->dpm_caps); + } + } + + return find_cap; +} +#endif + +/* + * Rules: + * External Sources -> EXS + * Provider/Consumers -> PC + * Consumers/Provider -> CP + * 1. PC (with EXS) shall always deny PR_SWAP from CP (without EXS) + * 2. PC (without EXS) shall always acppet PR_SWAP from CP (with EXS) + * unless the requester isn't able to provide PDOs. + */ + +int dpm_check_good_power(struct pd_port *pd_port) +{ + bool local_ex, partner_ex; + + local_ex = + (pd_port->dpm_caps & DPM_CAP_LOCAL_EXT_POWER) != 0; + + partner_ex = + (pd_port->pe_data.dpm_flags & DPM_FLAGS_PARTNER_EXTPOWER) != 0; + + if (local_ex != partner_ex) { + if (partner_ex) + return GOOD_PW_PARTNER; + return GOOD_PW_LOCAL; + } + + if (local_ex) + return GOOD_PW_BOTH; + + return GOOD_PW_NONE; +} + +void pd_dpm_prs_evaluate_swap(struct pd_port *pd_port, uint8_t role) +{ + int good_power; + bool sink, accept = true; + + bool check_src = (pd_port->dpm_caps & DPM_CAP_PR_SWAP_CHECK_GP_SRC) ? + true : false; + bool check_snk = (pd_port->dpm_caps & DPM_CAP_PR_SWAP_CHECK_GP_SNK) ? + true : false; + +#ifdef CONFIG_USB_PD_SRC_REJECT_PR_SWAP_IF_GOOD_PW + bool check_ext = + (pd_port->dpm_caps & DPM_CAP_CHECK_EXT_POWER) ? true : false; + + if (check_ext) + check_src = true; +#endif /* CONFIG_USB_PD_SRC_REJECT_PR_SWAP_IF_GOOD_PW */ + + if (check_src|check_snk) { + sink = pd_port->power_role == PD_ROLE_SINK; + good_power = dpm_check_good_power(pd_port); + + switch (good_power) { + case GOOD_PW_PARTNER: + if (sink && check_snk) + accept = false; + break; + + case GOOD_PW_LOCAL: + if ((!sink) && (check_src)) + accept = false; + break; + + default: + accept = true; + break; + } + } + + dpm_response_request(pd_port, accept); +} + +void pd_dpm_prs_turn_off_power_sink(struct pd_port *pd_port) +{ + /* iSnkSwapStdby : 2.5mA */ + tcpci_sink_vbus(pd_port->tcpc, + TCP_VBUS_CTRL_PR_SWAP, TCPC_VBUS_SINK_0V, 0); +} + +void pd_dpm_prs_enable_power_source(struct pd_port *pd_port, bool en) +{ + int vbus_level = en ? TCPC_VBUS_SOURCE_5V : TCPC_VBUS_SOURCE_0V; + + tcpci_source_vbus(pd_port->tcpc, + TCP_VBUS_CTRL_PR_SWAP, vbus_level, -1); + + if (en) + pd_enable_vbus_valid_detection(pd_port, en); + else + pd_enable_vbus_safe0v_detection(pd_port); +} + +void pd_dpm_prs_change_role(struct pd_port *pd_port, uint8_t role) +{ +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + pd_port->pe_data.pd_traffic_idle = false; +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ + + dpm_reaction_clear(pd_port, DPM_REACTION_REQUEST_PR_SWAP); + pd_set_power_role(pd_port, role); + pd_put_dpm_ack_event(pd_port); +} + +#endif /* CONFIG_USB_PD_PR_SWAP */ + +/* + * DRP : Vconn Swap + */ + +#ifdef CONFIG_USB_PD_VCONN_SWAP + +void pd_dpm_vcs_evaluate_swap(struct pd_port *pd_port) +{ + bool accept = true; + +#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE + struct tcpc_device *tcpc = pd_port->tcpc; + + /* Reject it if we don't want supply vconn */ + if ((!pd_port->vconn_role) && + (tcpc->tcpc_vconn_supply == TCPC_VCONN_SUPPLY_NEVER)) + accept = false; +#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */ + + dpm_response_request(pd_port, accept); +} + +void pd_dpm_vcs_enable_vconn(struct pd_port *pd_port, uint8_t role) +{ + pd_set_vconn(pd_port, role); + + /* If we can't enable vconn immediately, + * then after vconn_on, + * Vconn Controller should pd_put_dpm_ack_event() + */ + +#if CONFIG_USB_PD_VCONN_READY_TOUT + if (role != PD_ROLE_VCONN_OFF) { + pd_enable_timer(pd_port, PD_TIMER_VCONN_READY); + return; + } +#endif /* CONFIG_USB_PD_VCONN_READY_TOUT */ + + PE_STATE_DPM_ACK_IMMEDIATELY(pd_port); +} + +#endif /* CONFIG_USB_PD_VCONN_SWAP */ + +/* + * PE : PD3.0 + */ + +#ifdef CONFIG_USB_PD_REV30 + +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE +void pd_dpm_inform_source_cap_ext(struct pd_port *pd_port) +{ + struct pd_source_cap_ext *scedb; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (dpm_check_ext_msg_event(pd_port, PD_EXT_SOURCE_CAP_EXT)) { + scedb = pd_get_msg_data_payload(pd_port); + DPM_INFO2("vid=0x%04x, pid=0x%04x\n", scedb->vid, scedb->pid); + DPM_INFO2("fw_ver=0x%02x, hw_ver=0x%02x\n", + scedb->fw_ver, scedb->hw_ver); + + dpm_reaction_clear(pd_port, + DPM_REACTION_GET_SOURCE_CAP_EXT); + } +} +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL +int pd_dpm_send_source_cap_ext(struct pd_port *pd_port) +{ + return pd_send_sop_ext_msg(pd_port, PD_EXT_SOURCE_CAP_EXT, + PD_SCEDB_SIZE, &pd_port->src_cap_ext); +} +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL + +static const struct pd_battery_capabilities c_invalid_bcdb = { + 0, 0, 0, 0, PD_BCDB_BAT_TYPE_INVALID +}; + +int pd_dpm_send_battery_cap(struct pd_port *pd_port) +{ + struct pd_battery_info *bat_info; + const struct pd_battery_capabilities *bcdb; + struct pd_get_battery_capabilities *gbcdb = + pd_get_msg_data_payload(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + DPM_INFO2("bat_ref=%d\n", gbcdb->bat_cap_ref); + + bat_info = pd_get_battery_info(pd_port, gbcdb->bat_cap_ref); + + if (bat_info != NULL) { + tcpci_notify_request_bat_info( + tcpc, gbcdb->bat_cap_ref); + bcdb = &bat_info->bat_cap; + } else + bcdb = &c_invalid_bcdb; + + return pd_send_sop_ext_msg(pd_port, PD_EXT_BAT_CAP, + PD_BCDB_SIZE, bcdb); +} +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE +void pd_dpm_inform_battery_cap(struct pd_port *pd_port) +{ + struct pd_battery_capabilities *bcdb; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (dpm_check_ext_msg_event(pd_port, PD_EXT_BAT_CAP)) { + bcdb = pd_get_msg_data_payload(pd_port); + DPM_INFO2("vid=0x%04x, pid=0x%04x\n", + bcdb->vid, bcdb->pid); + } +} +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL + +static const uint32_t c_invalid_bsdo = + BSDO(0xffff, BSDO_BAT_INFO_INVALID_REF); + +int pd_dpm_send_battery_status(struct pd_port *pd_port) +{ + const uint32_t *bsdo; + struct pd_battery_info *bat_info; + struct pd_get_battery_status *gbsdb = + pd_get_msg_data_payload(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + DPM_INFO2("bat_ref=%d\n", gbsdb->bat_status_ref); + + bat_info = pd_get_battery_info(pd_port, gbsdb->bat_status_ref); + + if (bat_info != NULL) { + tcpci_notify_request_bat_info( + tcpc, gbsdb->bat_status_ref); + bsdo = &bat_info->bat_status; + } else + bsdo = &c_invalid_bsdo; + +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL + pd_port->pe_data.get_status_once = true; +#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */ + + return pd_send_sop_data_msg(pd_port, + PD_DATA_BAT_STATUS, PD_BSDO_SIZE, bsdo); +} +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */ + + +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE +void pd_dpm_inform_battery_status(struct pd_port *pd_port) +{ + uint32_t *payload; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (dpm_check_data_msg_event(pd_port, PD_DATA_BAT_STATUS)) { + payload = pd_get_msg_data_payload(pd_port); + DPM_INFO2("0x%08x\n", payload[0]); + } +} +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */ + +static const struct pd_manufacturer_info c_invalid_mfrs = { + .vid = 0xFFFF, .pid = 0, .mfrs_string = "Not Supported", +}; + +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL +int pd_dpm_send_mfrs_info(struct pd_port *pd_port) +{ + uint8_t len = 0; + struct pd_battery_info *bat_info; + const struct pd_manufacturer_info *midb = NULL; + + struct pd_get_manufacturer_info *gmidb = + pd_get_msg_data_payload(pd_port); + + if (gmidb->info_target == PD_GMIDB_TARGET_PORT) + midb = &pd_port->mfrs_info; + + if (gmidb->info_target == PD_GMIDB_TARGET_BATTRY) { + bat_info = pd_get_battery_info(pd_port, gmidb->info_ref); + if (bat_info) + midb = &bat_info->mfrs_info; + } + + if (midb == NULL) + midb = &c_invalid_mfrs; + + len = strnlen((char *)midb->mfrs_string, sizeof(midb->mfrs_string)); + if (len < sizeof(midb->mfrs_string)) + len++; + return pd_send_sop_ext_msg(pd_port, PD_EXT_MFR_INFO, + PD_MIDB_MIN_SIZE + len, midb); +} +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE +void pd_dpm_inform_mfrs_info(struct pd_port *pd_port) +{ + struct pd_manufacturer_info *midb; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (dpm_check_ext_msg_event(pd_port, PD_EXT_MFR_INFO)) { + midb = pd_get_msg_data_payload(pd_port); + DPM_INFO2("vid=0x%x, pid=0x%x\n", midb->vid, midb->pid); + } +} +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */ + + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE +void pd_dpm_inform_country_codes(struct pd_port *pd_port) +{ + struct pd_country_codes *ccdb; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (dpm_check_ext_msg_event(pd_port, PD_EXT_COUNTRY_CODES)) { + ccdb = pd_get_msg_data_payload(pd_port); + DPM_INFO2("len=%d, country_code[0]=0x%04x\n", + ccdb->length, ccdb->country_code[0]); + } +} +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */ + + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL +int pd_dpm_send_country_codes(struct pd_port *pd_port) +{ + uint8_t i; + struct pd_country_codes ccdb; + struct pd_country_authority *ca = pd_port->country_info; + + ccdb.length = pd_port->country_nr; + + for (i = 0; i < ccdb.length; i++) + ccdb.country_code[i] = ca[i].code; + + return pd_send_sop_ext_msg(pd_port, PD_EXT_COUNTRY_CODES, + 2 + ccdb.length*2, &ccdb); +} +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE +void pd_dpm_inform_country_info(struct pd_port *pd_port) +{ + struct pd_country_info *cidb; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (dpm_check_ext_msg_event(pd_port, PD_EXT_COUNTRY_INFO)) { + cidb = pd_get_msg_data_payload(pd_port); + DPM_INFO2("cc=0x%04x, ci=%d\n", + cidb->country_code, cidb->country_special_data[0]); + } +} +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL +int pd_dpm_send_country_info(struct pd_port *pd_port) +{ + uint8_t i, cidb_size; + struct pd_country_info cidb; + struct pd_country_authority *ca = pd_port->country_info; + uint32_t *pccdo = pd_get_msg_data_payload(pd_port); + uint16_t cc = CCDO_COUNTRY_CODE(*pccdo); + + cidb_size = PD_CIDB_MIN_SIZE; + cidb.country_code = cc; + cidb.reserved = 0; + cidb.country_special_data[0] = 0; + + for (i = 0; i < pd_port->country_nr; i++) { + if (ca[i].code == cc) { + cidb_size += ca[i].len; + memcpy(cidb.country_special_data, + ca[i].data, ca[i].len); + break; + } + } + + return pd_send_sop_ext_msg(pd_port, PD_EXT_COUNTRY_INFO, + cidb_size, &cidb); +} +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE +void pd_dpm_inform_alert(struct pd_port *pd_port) +{ + uint32_t *data = pd_get_msg_data_payload(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + DPM_INFO("inform_alert:0x%08x\n", data[0]); + + pd_port->pe_data.pd_traffic_idle = false; + pd_port->pe_data.remote_alert = data[0]; + tcpci_notify_alert(pd_port->tcpc, data[0]); +} +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL +int pd_dpm_send_alert(struct pd_port *pd_port) +{ + uint32_t ado = pd_port->pe_data.local_alert; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + pd_port->pe_data.local_alert = 0; + DPM_INFO("send_alert:0x%08x\n", ado); + + return pd_send_sop_data_msg(pd_port, PD_DATA_ALERT, + PD_ADO_SIZE, &ado); +} +#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE +void pd_dpm_inform_status(struct pd_port *pd_port) +{ + struct pd_status *sdb; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (dpm_check_ext_msg_event(pd_port, PD_EXT_STATUS)) { + sdb = pd_get_msg_data_payload(pd_port); + DPM_INFO2("Temp=%d, IN=0x%x, BAT_IN=0x%x, EVT=0x%x, PTF=0x%x\n", + sdb->internal_temp, sdb->present_input, + sdb->present_battery_input, sdb->event_flags, + PD_STATUS_TEMP_PTF(sdb->temp_status)); + + tcpci_notify_status(tcpc, sdb); + } +} +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL +int pd_dpm_send_status(struct pd_port *pd_port) +{ + struct pd_status sdb; + struct pe_data *pe_data = &pd_port->pe_data; + + memset(&sdb, 0, PD_SDB_SIZE); + + sdb.present_input = pd_port->pd_status_present_in; + +#ifdef CONFIG_USB_PD_REV30_BAT_INFO + if (sdb.present_input & + PD_STATUS_INPUT_INT_POWER_BAT) { + sdb.present_battery_input = pd_port->pd_status_bat_in; + } +#endif /* CONFIG_USB_PD_REV30_BAT_INFO */ + + sdb.event_flags = pe_data->pd_status_event; + pe_data->pd_status_event &= ~PD_STASUS_EVENT_READ_CLEAR; + +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL_TEMP + sdb.internal_temp = pd_port->pd_status_temp; + sdb.temp_status = pd_port->pd_status_temp_status; +#else + sdb.internal_temp = 0; + sdb.temp_status = 0; +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL_TEMP */ + + if (sdb.event_flags & PD_STATUS_EVENT_OTP) + sdb.temp_status = PD_STATUS_TEMP_SET_PTF(PD_PTF_OVER_TEMP); + + if (pd_port->power_role != PD_ROLE_SINK) + sdb.event_flags &= ~PD_STATUS_EVENT_OVP; + +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL + pe_data->get_status_once = true; +#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */ + + return pd_send_sop_ext_msg(pd_port, PD_EXT_STATUS, + PD_SDB_SIZE, &sdb); +} +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ + + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK +void pd_dpm_inform_pps_status(struct pd_port *pd_port) +{ + struct pd_pps_status_raw *ppssdb; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (dpm_check_ext_msg_event(pd_port, PD_EXT_PPS_STATUS)) { + ppssdb = pd_get_msg_data_payload(pd_port); + DPM_INFO2("mv=%d, ma=%d\n", + PD_PPS_GET_OUTPUT_MV(ppssdb->output_vol_raw), + PD_PPS_GET_OUTPUT_MA(ppssdb->output_curr_raw)); + } +} +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + +void pd_dpm_inform_not_support(struct pd_port *pd_port) +{ + /* TODO */ +} + +#endif /* CONFIG_USB_PD_REV30 */ + +/* + * PE : Dynamic Control Vconn + */ + +void pd_dpm_dynamic_enable_vconn(struct pd_port *pd_port) +{ +#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE + struct tcpc_device *tcpc = pd_port->tcpc; + + if (tcpc->tcpc_vconn_supply <= TCPC_VCONN_SUPPLY_ALWAYS) + return; + + if (pd_port->vconn_role == PD_ROLE_VCONN_DYNAMIC_OFF) { + DPM_INFO2("DynamicVCEn\n"); + pd_set_vconn(pd_port, PD_ROLE_VCONN_DYNAMIC_ON); + } +#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */ +} + +void pd_dpm_dynamic_disable_vconn(struct pd_port *pd_port) +{ +#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE + bool keep_vconn; + struct tcpc_device *tcpc = pd_port->tcpc; + + if (!pd_port->vconn_role) + return; + + switch (tcpc->tcpc_vconn_supply) { + case TCPC_VCONN_SUPPLY_EMARK_ONLY: + keep_vconn = pd_port->pe_data.power_cable_present; + break; + case TCPC_VCONN_SUPPLY_STARTUP: + keep_vconn = false; + break; + default: + keep_vconn = true; + break; + } + + if (keep_vconn) + return; + + if (tcpc->tcp_event_count) + return; + + if (pd_port->vconn_role != PD_ROLE_VCONN_DYNAMIC_OFF) { + DPM_INFO2("DynamicVCDis\n"); + pd_set_vconn(pd_port, PD_ROLE_VCONN_DYNAMIC_OFF); + } +#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */ +} + +/* + * PE : Notify DPM + */ + +int pd_dpm_notify_pe_startup(struct pd_port *pd_port) +{ + uint32_t reactions = DPM_REACTION_CAP_ALWAYS; + +#ifdef CONFIG_USB_PD_DFP_FLOW_DELAY_STARTUP + reactions |= DPM_REACTION_DFP_FLOW_DELAY; +#endif /* CONFIG_USB_PD_DFP_FLOW_DELAY_STARTUP */ + +#ifdef CONFIG_USB_PD_UFP_FLOW_DELAY + reactions |= DPM_REACTION_UFP_FLOW_DELAY; +#endif /* CONFIG_USB_PD_UFP_FLOW_DELAY */ + +#ifdef CONFIG_USB_PD_SRC_TRY_PR_SWAP_IF_BAD_PW + reactions |= DPM_REACTION_ATTEMPT_GET_FLAG | + DPM_REACTION_REQUEST_PR_SWAP; +#else + if (DPM_CAP_EXTRACT_PR_CHECK(pd_port->dpm_caps)) { + reactions |= DPM_REACTION_REQUEST_PR_SWAP; + if (DPM_CAP_EXTRACT_PR_CHECK(pd_port->dpm_caps) == + DPM_CAP_PR_CHECK_PREFER_SNK) + reactions |= DPM_REACTION_ATTEMPT_GET_FLAG; + } + + if (pd_port->dpm_caps & DPM_CAP_CHECK_EXT_POWER) + reactions |= DPM_REACTION_ATTEMPT_GET_FLAG; +#endif /* CONFIG_USB_PD_SRC_TRY_PR_SWAP_IF_BAD_PW */ + + if (DPM_CAP_EXTRACT_DR_CHECK(pd_port->dpm_caps)) { + reactions |= DPM_REACTION_REQUEST_DR_SWAP; + if (DPM_CAP_EXTRACT_DR_CHECK(pd_port->dpm_caps) == + DPM_CAP_DR_CHECK_PREFER_UFP) + reactions |= DPM_REACTION_ATTEMPT_GET_FLAG; + } + + if (pd_port->dpm_caps & DPM_CAP_ATTEMP_DISCOVER_CABLE) + reactions |= DPM_REACTION_CAP_DISCOVER_CABLE; + + if (pd_port->dpm_caps & DPM_CAP_ATTEMP_DISCOVER_CABLE_DFP) + reactions |= DPM_REACTION_DISCOVER_CABLE_FLOW; + +#ifdef CONFIG_USB_PD_ATTEMP_ENTER_MODE + reactions |= DPM_REACTION_DISCOVER_ID | + DPM_REACTION_DISCOVER_SVID; +#else + if (pd_port->dpm_caps & DPM_CAP_ATTEMP_DISCOVER_ID) + reactions |= DPM_REACTION_DISCOVER_ID; + if (pd_port->dpm_caps & DPM_CAP_ATTEMP_DISCOVER_SVID) + reactions |= DPM_REACTION_DISCOVER_SVID; +#endif /* CONFIG_USB_PD_ATTEMP_ENTER_MODE */ + +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE + reactions |= DPM_REACTION_GET_SOURCE_CAP_EXT; +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ +#endif /* CONFIG_USB_PD_REV30 */ + + dpm_reaction_set(pd_port, reactions); + + svdm_reset_state(pd_port); + svdm_notify_pe_startup(pd_port); + return 0; + +} + +int pd_dpm_notify_pe_hardreset(struct pd_port *pd_port) +{ + struct pe_data *pe_data = &pd_port->pe_data; + + svdm_reset_state(pd_port); + + pe_data->pe_ready = false; + +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + pe_data->pd_traffic_idle = false; +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ + + if (pe_data->dpm_svdm_retry_cnt >= CONFIG_USB_PD_DPM_SVDM_RETRY) + return 0; + + pe_data->dpm_svdm_retry_cnt++; + +#ifdef CONFIG_USB_PD_ATTEMP_ENTER_MODE + dpm_reaction_set(pd_port, DPM_REACTION_DISCOVER_ID | + DPM_REACTION_DISCOVER_SVID); +#endif /* CONFIG_USB_PD_ATTEMP_ENTER_MODE */ + + svdm_notify_pe_startup(pd_port); + return 0; +} + +/* + * SVDM + */ + +static inline bool dpm_register_svdm_ops(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, const struct svdm_svid_ops *ops) +{ + bool ret = true; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (ops->parse_svid_data) + ret = ops->parse_svid_data(pd_port, svid_data); + + if (ret) { + svid_data->ops = ops; + svid_data->svid = ops->svid; + DPM_DBG("register_svdm: 0x%x\n", ops->svid); + } + + return ret; +} + +struct svdm_svid_data *dpm_get_svdm_svid_data( + struct pd_port *pd_port, uint16_t svid) +{ + uint8_t i; + struct svdm_svid_data *svid_data; + + for (i = 0; i < pd_port->svid_data_cnt; i++) { + svid_data = &pd_port->svid_data[i]; + if (svid_data->svid == svid) + return svid_data; + } + + return NULL; +} + +bool svdm_reset_state(struct pd_port *pd_port) +{ + int i; + struct svdm_svid_data *svid_data; + + pd_port->dpm_charging_policy = pd_port->dpm_charging_policy_default; + + for (i = 0; i < pd_port->svid_data_cnt; i++) { + svid_data = &pd_port->svid_data[i]; + if (svid_data->ops && svid_data->ops->reset_state) + svid_data->ops->reset_state(pd_port, svid_data); + } + + return true; +} + +bool svdm_notify_pe_startup(struct pd_port *pd_port) +{ + int i; + struct svdm_svid_data *svid_data; + + for (i = 0; i < pd_port->svid_data_cnt; i++) { + svid_data = &pd_port->svid_data[i]; + if (svid_data->ops && svid_data->ops->notify_pe_startup) + svid_data->ops->notify_pe_startup(pd_port, svid_data); + } + + return true; +} + +/* + * dpm_core_init + */ + +int pd_dpm_core_init(struct pd_port *pd_port) +{ + int i, j; + bool ret; + uint8_t svid_ops_nr = ARRAY_SIZE(svdm_svid_ops); + struct tcpc_device *tcpc = pd_port->tcpc; + + pd_port->svid_data = devm_kzalloc(&tcpc->dev, + sizeof(struct svdm_svid_data) * svid_ops_nr, GFP_KERNEL); + + if (!pd_port->svid_data) + return -ENOMEM; + + for (i = 0, j = 0; i < svid_ops_nr; i++) { + ret = dpm_register_svdm_ops(pd_port, + &pd_port->svid_data[j], &svdm_svid_ops[i]); + + if (ret) + j++; + } + + pd_port->svid_data_cnt = j; + +#ifdef CONFIG_USB_PD_REV30 + pd_port->pps_request_wake_lock = + wakeup_source_register(&tcpc->dev, "pd_pps_request_wake_lock"); + init_waitqueue_head(&pd_port->pps_request_wait_que); + atomic_set(&pd_port->pps_request, false); + pd_port->pps_request_task = kthread_run(pps_request_thread_fn, tcpc, + "pps_request_%s", + tcpc->desc.name); +#endif /* CONFIG_USB_PD_REV30 */ + + return 0; +} diff --git a/drivers/usb/typec/mmi_tcpc/pd_dpm_mmi_adapter.c b/drivers/usb/typec/mmi_tcpc/pd_dpm_mmi_adapter.c new file mode 100644 index 000000000000..9887bed65749 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_dpm_mmi_adapter.c @@ -0,0 +1,292 @@ +#include +#include "inc/tcpci.h" +#include "inc/pd_policy_engine.h" +#include "inc/pd_dpm_core.h" +#include "pd_dpm_prv.h" +#include "inc/tcpci_core.h" +#include + +#ifdef CONFIG_SUPPORT_MMI_ADAPTER + +#define CMD_HEADER_REQUEST 0x01 +#define CMD_HEADER_ACK 0x02 + +#define CMD_CHARGER_CAPACITY 0x01 +#define CMD_CHARGER_AUTHENTICATION 0x02 +#define CMD_CHARGER_TEMPERATURE 0x03 +#define CMD_CHARGER_VERIFY 0x04 + +#define CMD(header,cmd) ((header << 8) + cmd) +#define MMI_UVDM_CMD(vid,header,cmd) ((vid << 16) + CMD(header,cmd)) + +#define CONFIG_MOTO_ADAPTER 0x4d4f544f +#define CONFIG_ADAPTER_68W 0x30363841 + +#define SHA256_NUM 4 +static uint32_t random_num[SHA256_NUM] = {0}; +#define MMI_KEY "Motorola-Hyper-Proprietary-2021Gen1" + +enum MMI_STATE { + MMI_NONE = 0, + MMI_REQUEST_CAP, + MMI_ENCRYPT, + MMI_REQUEST_TEMP, + MMI_VERIFY, + MMI_READY, +}; + +static const char * const mmi_state[] = { + "None", + "Request cap", + "Encrypt", + "Request temp", + "Verify", + "mmi adapter ready", +}; + +static int hmac_sha256(u8 *key, u8 ksize, char *plaintext, u8 psize, u8 *output) +{ + struct crypto_shash *tfm; + struct shash_desc *shash; + int ret; + + if (!ksize) + return -EINVAL; + + tfm = crypto_alloc_shash("hmac(sha256)", 0, 0); + if (IS_ERR(tfm)) { + MMI_INFO("crypto_alloc_ahash failed: err %ld", PTR_ERR(tfm)); + return PTR_ERR(tfm); + } + + ret = crypto_shash_setkey(tfm, key, ksize); + if (ret) { + MMI_INFO("crypto_ahash_setkey failed: err %d", ret); + goto failed; + } + + shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm), + GFP_KERNEL); + if (!shash) { + ret = -ENOMEM; + goto failed; + } + + shash->tfm = tfm; + + ret = crypto_shash_digest(shash, plaintext, psize, output); + + kfree(shash); + +failed: + crypto_free_shash(tfm); + return ret; +} + +static int mmi_hmac_sha256(u8 *key, u8 ksize,uint32_t *plaintext, + u8 psize,uint32_t *output) +{ + uint8_t *data_in; + uint8_t *data_out; + int i = 0,j = 0; + + data_in = kzalloc(sizeof(*data_in) * psize * 4,GFP_KERNEL); + if (!data_in) { + return -ENOMEM; + } + + data_out = kzalloc(sizeof(*data_out) * psize * 4,GFP_KERNEL); + if (!data_out) { + kfree(data_in); + return -ENOMEM; + } + + for (i = 0;i < psize;i++) { + for (j = 0; j < sizeof(*plaintext); j++) { + data_in[(i * 4) + 3 - j] = (plaintext[i] >> (j * 8)) & 0xff; + } + } + + hmac_sha256(key,ksize,data_in,4*psize,data_out); + + for (i = 0;i < psize;i++) { + output[i] = data_out[3 + (4 * i)] + (data_out[2 + (4 * i)] << 8) + + (data_out[1 + (4 * i)] << 16) + (data_out[0 + (4 * i)] << 24); + } + + kfree(data_in); + kfree(data_out); + return 0; +} + +static bool mmi_uvdm_ack(struct pd_port *pd_port, + struct svdm_svid_data *svid_data) +{ + uint16_t vid = svid_data->svid; + uint32_t data = pd_port->uvdm_data[0]; + if ((data >> 16) != vid) { + return false; + } + if (((data & 0xff) == pd_port->mmi_adapter_state) && + ((data >> 8) & 0xff) == CMD_HEADER_ACK) { + return true; + } + return false; +} + +static void mmi_adapter_set_state(struct pd_port *pd_port,uint8_t state) { + MMI_INFO("%s -> %s\n",mmi_state[pd_port->mmi_adapter_state], + mmi_state[state]); + pd_port->mmi_adapter_state = state; +} + + +static void mmi_adapter_cap_request(struct pd_port *pd_port, + struct svdm_svid_data *svid_data) +{ + pd_port->uvdm_cnt = 1; + pd_port->uvdm_wait_resp = true; + pd_port->uvdm_data[0] = MMI_UVDM_CMD(svid_data->svid, + CMD_HEADER_REQUEST,CMD_CHARGER_CAPACITY); + pd_put_tcp_vdm_event(pd_port,TCP_DPM_EVT_UVDM); +} + +static void mmi_adapter_encrypt(struct pd_port *pd_port, + struct svdm_svid_data *svid_data) +{ + uint8_t index = 0; + pd_port->uvdm_cnt = 1 + SHA256_NUM; + pd_port->uvdm_wait_resp = true; + + pd_port->uvdm_data[0] = MMI_UVDM_CMD(svid_data->svid, + CMD_HEADER_REQUEST,CMD_CHARGER_AUTHENTICATION); + for (index = 0;index < SHA256_NUM;index++) { + get_random_bytes(&(random_num[index]),sizeof(*random_num)); + //random_num[index] = index + 1; + pd_port->uvdm_data[index+1] = random_num[index]; + } + pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_UVDM); +} + +static void mmi_adapter_req_temp(struct pd_port *pd_port, + struct svdm_svid_data *svid_data) +{ + pd_port->uvdm_cnt = 1; + pd_port->uvdm_wait_resp = true; + pd_port->uvdm_data[0] = MMI_UVDM_CMD(svid_data->svid, + CMD_HEADER_REQUEST,CMD_CHARGER_TEMPERATURE); + pd_put_tcp_vdm_event(pd_port,TCP_DPM_EVT_UVDM); +} + +static void mmi_adapter_verify(struct pd_port *pd_port, + struct svdm_svid_data *svid_data) +{ + pd_port->uvdm_cnt = 1; + pd_port->uvdm_wait_resp = true; + pd_port->uvdm_data[0] = MMI_UVDM_CMD(svid_data->svid, + CMD_HEADER_ACK,CMD_CHARGER_VERIFY); + pd_put_tcp_vdm_event(pd_port,TCP_DPM_EVT_UVDM); +} + +bool mmi_dfp_notify_pe_startup( + struct pd_port *pd_port, struct svdm_svid_data *svid_data) +{ + mmi_adapter_set_state(pd_port,MMI_NONE); + return true; +} + +bool mmi_dfp_u_notify_discover_svid( + struct pd_port *pd_port, struct svdm_svid_data *svid_data, bool ack) +{ + MMI_INFO("discover\n"); + if (!ack) { + return false; + } + mmi_adapter_set_state(pd_port,MMI_REQUEST_CAP); + return true; +} + +bool mmi_dfp_notify_uvdm(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack) +{ + uint32_t out[SHA256_NUM] = {0}; + char key[] = MMI_KEY; + int i = 0; + switch(pd_port->mmi_adapter_state) { + case MMI_REQUEST_CAP: + if (mmi_uvdm_ack(pd_port,svid_data) && + pd_port->uvdm_data[1] == CONFIG_MOTO_ADAPTER && + pd_port->uvdm_data[2] == CONFIG_ADAPTER_68W) { +#ifdef CONFIG_SUPPORT_MMI_ADAPTER_GET_TEMP + mmi_adapter_set_state(pd_port,MMI_REQUEST_TEMP); +#else + mmi_adapter_set_state(pd_port,MMI_ENCRYPT); +#endif /* CONFIG_SUPPORT_MMI_ADAPTER_GET_TEMP */ + return true; + } + break; + case MMI_REQUEST_TEMP: + if (mmi_uvdm_ack(pd_port,svid_data)) { + MMI_INFO("now adapter temp = %d\n",pd_port->uvdm_data[1]); + mmi_adapter_set_state(pd_port,MMI_ENCRYPT); + return true; + } + break; + case MMI_ENCRYPT: + if (mmi_uvdm_ack(pd_port,svid_data)) { + /** + * todo hmac-sha256 check + * src data : pd_port->uvdm_data[1-4] + * random : random_num + */ + mmi_hmac_sha256(key,sizeof(key),random_num,SHA256_NUM,out); + for (i = 0;i < SHA256_NUM;i++) { + if (pd_port->uvdm_data[i+1] != out[i]) + goto out; + } + mmi_adapter_set_state(pd_port,MMI_VERIFY); + return true; + } + break; + case MMI_VERIFY: + if (mmi_uvdm_ack(pd_port,svid_data) && + pd_port->uvdm_data[1] == 0x01) { + mmi_adapter_set_state(pd_port,MMI_READY); + dpm_reaction_set(pd_port, DPM_REACTION_GET_SOURCE_CAP); + return true; + } + break; + default: + break; + } +out: + mmi_adapter_set_state(pd_port,MMI_NONE); + return true; +} + +int mmi_notify_pe_ready(struct pd_port *pd_port, + struct svdm_svid_data *svid_data) +{ + switch(pd_port->mmi_adapter_state) { + case MMI_REQUEST_CAP: + mmi_adapter_cap_request(pd_port,svid_data); + break; + case MMI_REQUEST_TEMP: + mmi_adapter_req_temp(pd_port,svid_data); + break; + case MMI_ENCRYPT: + mmi_adapter_encrypt(pd_port,svid_data); + break; + case MMI_VERIFY: + mmi_adapter_verify(pd_port,svid_data); + break; + case MMI_READY: + pd_update_vdm_verify_state(pd_port, true); + break; + default: + break; + } + return 0; +} + +#endif /* CONFIG_SUPPORT_MMI_ADAPTER */ \ No newline at end of file diff --git a/drivers/usb/typec/mmi_tcpc/pd_dpm_pdo_select.c b/drivers/usb/typec/mmi_tcpc/pd_dpm_pdo_select.c new file mode 100644 index 000000000000..5332f4c35604 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_dpm_pdo_select.c @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Core Driver + * + * 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. + */ + +#include "inc/pd_dpm_pdo_select.h" + +#ifdef CONFIG_USB_POWER_DELIVERY + +struct dpm_select_info_t { + uint8_t pos; + int max_uw; + int cur_mv; + uint8_t policy; +}; + +static inline void dpm_extract_apdo_info( + uint32_t pdo, struct dpm_pdo_info_t *info) +{ +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + switch (APDO_TYPE(pdo)) { + case APDO_TYPE_PPS: + info->apdo_type = DPM_APDO_TYPE_PPS; + + if (pdo & APDO_PPS_CURR_FOLDBACK) + info->apdo_type |= DPM_APDO_TYPE_PPS_CF; + + info->pwr_limit = APDO_PPS_EXTRACT_PWR_LIMIT(pdo); + info->ma = APDO_PPS_EXTRACT_CURR(pdo); + info->vmin = APDO_PPS_EXTRACT_MIN_VOLT(pdo); + info->vmax = APDO_PPS_EXTRACT_MAX_VOLT(pdo); + info->uw = info->ma * info->vmax; + return; + } +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + info->type = TCPM_POWER_CAP_VAL_TYPE_UNKNOWN; +} + +void dpm_extract_pdo_info( + uint32_t pdo, struct dpm_pdo_info_t *info) +{ + memset(info, 0, sizeof(struct dpm_pdo_info_t)); + + info->type = PDO_TYPE_VAL(pdo); + + switch (PDO_TYPE(pdo)) { + case PDO_TYPE_FIXED: + info->ma = PDO_FIXED_EXTRACT_CURR(pdo); + info->vmax = info->vmin = PDO_FIXED_EXTRACT_VOLT(pdo); + info->uw = info->ma * info->vmax; + break; + case PDO_TYPE_VARIABLE: + info->ma = PDO_VAR_EXTRACT_CURR(pdo); + info->vmin = PDO_VAR_EXTRACT_MIN_VOLT(pdo); + info->vmax = PDO_VAR_EXTRACT_MAX_VOLT(pdo); + info->uw = info->ma * info->vmax; + break; + + case PDO_TYPE_BATTERY: + info->uw = PDO_BATT_EXTRACT_OP_POWER(pdo) * 1000; + info->vmin = PDO_BATT_EXTRACT_MIN_VOLT(pdo); + info->vmax = PDO_BATT_EXTRACT_MAX_VOLT(pdo); + info->ma = info->uw / info->vmin; + break; + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + case PDO_TYPE_APDO: + dpm_extract_apdo_info(pdo, info); + break; +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + } +} + +#ifndef MIN +#define MIN(a, b) ((a < b) ? (a) : (b)) +#endif + +static inline int dpm_calc_src_cap_power_uw( + struct dpm_pdo_info_t *source, struct dpm_pdo_info_t *sink) +{ + int uw, ma; + + if (source->type == DPM_PDO_TYPE_BAT) { + uw = source->uw; + + if (sink->type == DPM_PDO_TYPE_BAT) + uw = MIN(uw, sink->uw); + } else { + ma = source->ma; + + if (sink->type != DPM_PDO_TYPE_BAT) + ma = MIN(ma, sink->ma); + + uw = ma * source->vmax; + } + + return uw; +} + +/* + * Select PDO from VSafe5V + */ + +static bool dpm_select_pdo_from_vsafe5v( + struct dpm_select_info_t *select_info, + struct dpm_pdo_info_t *sink, struct dpm_pdo_info_t *source) +{ + int uw; + + if ((sink->vmax != TCPC_VBUS_SINK_5V) || + (sink->vmin != TCPC_VBUS_SINK_5V) || + (source->vmax != TCPC_VBUS_SINK_5V) || + (source->vmin != TCPC_VBUS_SINK_5V)) + return false; + + uw = dpm_calc_src_cap_power_uw(source, sink); + if (uw > select_info->max_uw) { + select_info->max_uw = uw; + select_info->cur_mv = source->vmax; + return true; + } + + return false; +} + +/* + * Select PDO from Direct Charge + */ + +#ifdef CONFIG_USB_PD_ALT_MODE_RTDC +static bool dpm_select_pdo_from_direct_charge( + struct dpm_select_info_t *select_info, + struct dpm_pdo_info_t *sink, struct dpm_pdo_info_t *source) +{ + int uw; + + if (sink->type != DPM_PDO_TYPE_VAR + || source->type != DPM_PDO_TYPE_VAR) + return false; + + if (source->vmin >= TCPC_VBUS_SINK_5V) + return false; + + if (sink->vmax < source->vmax) + return false; + + if (sink->vmin > source->vmin) + return false; + + uw = dpm_calc_src_cap_power_uw(source, sink); + if (uw > select_info->max_uw) { + select_info->max_uw = uw; + select_info->cur_mv = source->vmax; + return true; + } + + return false; +} +#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */ + +/* + * Select PDO from Custom + */ + +static bool dpm_select_pdo_from_custom( + struct dpm_select_info_t *select_info, + struct dpm_pdo_info_t *sink, struct dpm_pdo_info_t *source) +{ + /* TODO */ + return dpm_select_pdo_from_vsafe5v(select_info, sink, source); +} + +/* + * Select PDO from Max Power + */ + +static inline bool dpm_is_valid_pdo_pair(struct dpm_pdo_info_t *sink, + struct dpm_pdo_info_t *source, uint32_t policy) +{ + if (sink->vmax < source->vmax) + return false; + + if (sink->vmin > source->vmin) + return false; + + if (policy & DPM_CHARGING_POLICY_IGNORE_MISMATCH_CURR) + return true; + + return sink->ma <= source->ma; +} + +static bool dpm_select_pdo_from_max_power( + struct dpm_select_info_t *select_info, + struct dpm_pdo_info_t *sink, struct dpm_pdo_info_t *source) +{ + bool overload; + int uw; + +#ifdef CONFIG_USB_PD_ALT_MODE_RTDC + /* Variable for direct charge only */ + if ((sink->type == DPM_PDO_TYPE_VAR) && (sink->vmin < 5000)) + return false; +#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */ + +#ifdef CONFIG_USB_PD_REV30 + if (sink->type == DPM_PDO_TYPE_APDO) + return false; +#endif /* CONFIG_USB_PD_REV30 */ + + if (!dpm_is_valid_pdo_pair(sink, source, select_info->policy)) + return false; + + uw = dpm_calc_src_cap_power_uw(source, sink); + + overload = uw > select_info->max_uw; + + if ((!overload) && (uw == select_info->max_uw)) { + if (select_info->policy & + DPM_CHARGING_POLICY_PREFER_LOW_VOLTAGE) + overload = (source->vmax < select_info->cur_mv); + else if (select_info->policy & + DPM_CHARGING_POLICY_PREFER_HIGH_VOLTAGE) + overload = (source->vmax > select_info->cur_mv); + } + + if (overload) { + select_info->max_uw = uw; + select_info->cur_mv = source->vmax; + return true; + } + + return false; +} + +/* + * Select PDO from PPS + */ + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK +static bool dpm_select_pdo_from_pps( + struct dpm_select_info_t *select_info, + struct dpm_pdo_info_t *sink, struct dpm_pdo_info_t *source) +{ + bool overload; + int uw, diff_mv; + const int tolerance = 300; /* 5900 * 5% */ + + if (sink->type != DPM_PDO_TYPE_APDO || + source->type != DPM_PDO_TYPE_APDO) + return false; + + if (!(source->apdo_type & DPM_APDO_TYPE_PPS)) + return false; + + if (sink->vmax > source->vmax) + return false; + + if (sink->vmin < source->vmin) + return false; + + if (!(select_info->policy & DPM_CHARGING_POLICY_IGNORE_MISMATCH_CURR)) { + if (source->ma < sink->ma) + return false; + } + + uw = sink->vmax * source->ma; + diff_mv = source->vmax - sink->vmax; + + if (uw > select_info->max_uw) + overload = true; + else if (uw < select_info->max_uw) + overload = false; + else if ((select_info->cur_mv < tolerance) && (diff_mv > tolerance)) + overload = true; + else if (diff_mv < select_info->cur_mv) + overload = true; + else + overload = false; + + if (overload) { + select_info->max_uw = uw; + select_info->cur_mv = diff_mv; + return true; + } + + return false; +} +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + +/* + * Select PDO from defined rule ... + */ + +typedef bool (*dpm_select_pdo_fun)( + struct dpm_select_info_t *select_info, + struct dpm_pdo_info_t *sink, struct dpm_pdo_info_t *source); + +bool dpm_find_match_req_info(struct dpm_rdo_info_t *req_info, + struct dpm_pdo_info_t *sink, int cnt, uint32_t *src_pdos, + int min_uw, uint32_t policy) +{ + int i; + struct dpm_select_info_t select; + struct dpm_pdo_info_t source; + dpm_select_pdo_fun select_pdo_fun; + + select.pos = 0; + select.cur_mv = 0; + select.max_uw = min_uw; + select.policy = policy; + + switch (policy & DPM_CHARGING_POLICY_MASK) { + case DPM_CHARGING_POLICY_MAX_POWER: + select_pdo_fun = dpm_select_pdo_from_max_power; + break; + + case DPM_CHARGING_POLICY_CUSTOM: + select_pdo_fun = dpm_select_pdo_from_custom; + break; + +#ifdef CONFIG_USB_PD_ALT_MODE_RTDC + case DPM_CHARGING_POLICY_DIRECT_CHARGE: + select_pdo_fun = dpm_select_pdo_from_direct_charge; + break; +#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */ + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + case DPM_CHARGING_POLICY_PPS: + select_pdo_fun = dpm_select_pdo_from_pps; + break; +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + default: /* DPM_CHARGING_POLICY_VSAFE5V */ + select_pdo_fun = dpm_select_pdo_from_vsafe5v; + break; + } + + for (i = 0; i < cnt; i++) { + dpm_extract_pdo_info(src_pdos[i], &source); + + if (select_pdo_fun(&select, sink, &source)) + select.pos = i+1; + } + + if (select.pos > 0) { + dpm_extract_pdo_info(src_pdos[select.pos-1], &source); + + req_info->pos = select.pos; + req_info->type = source.type; + req_info->vmax = source.vmax; + req_info->vmin = source.vmin; + + if (sink->type == DPM_PDO_TYPE_BAT) + req_info->mismatch = select.max_uw < sink->uw; + else + req_info->mismatch = source.ma < sink->ma; + + if (source.type == DPM_PDO_TYPE_BAT) { + req_info->max_uw = sink->uw; + req_info->oper_uw = select.max_uw; + } else { + req_info->max_ma = sink->ma; + req_info->oper_ma = MIN(sink->ma, source.ma); + } + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + if (source.type == DPM_PDO_TYPE_APDO) { + req_info->vmax = sink->vmax; + req_info->vmin = sink->vmin; + } +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + return true; + } + + return false; +} +#endif /* CONFIG_USB_POWER_DELIVERY */ diff --git a/drivers/usb/typec/mmi_tcpc/pd_dpm_prv.h b/drivers/usb/typec/mmi_tcpc/pd_dpm_prv.h new file mode 100644 index 000000000000..d33ac19f07b9 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_dpm_prv.h @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#ifndef PD_DPM_PRV_H_INCLUDED +#define PD_DPM_PRV_H_INCLUDED + +#include +#include + +#define SVID_DATA_LOCAL_MODE(svid_data, n) \ + (svid_data->local_mode.mode_vdo[n]) + +#define SVID_DATA_REMOTE_MODE(svid_data, n) \ + (svid_data->remote_mode.mode_vdo[n]) + +#define SVID_DATA_DFP_GET_ACTIVE_MODE(svid_data)\ + SVID_DATA_REMOTE_MODE(svid_data, svid_data->active_mode-1) + +#define SVID_DATA_UFP_GET_ACTIVE_MODE(svid_data)\ + SVID_DATA_LOCAL_MODE(svid_data, svid_data->active_mode-1) + +extern int dpm_check_supported_modes(void); + +struct svdm_svid_ops { + const char *name; + uint16_t svid; + + bool (*dfp_inform_id)(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack); + bool (*dfp_inform_svids)(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack); + bool (*dfp_inform_modes)(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack); + + bool (*dfp_inform_enter_mode)(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, uint8_t ops, bool ack); + bool (*dfp_inform_exit_mode)(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, uint8_t ops); + + bool (*dfp_inform_attention)(struct pd_port *pd_port, + struct svdm_svid_data *svid_data); + + void (*ufp_request_enter_mode)(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, uint8_t ops); + void (*ufp_request_exit_mode)(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, uint8_t ops); + + bool (*notify_pe_startup)(struct pd_port *pd_port, + struct svdm_svid_data *svid_data); + int (*notify_pe_ready)(struct pd_port *pd_port, + struct svdm_svid_data *svid_data); + bool (*notify_pe_shutdown)(struct pd_port *pd_port, + struct svdm_svid_data *svid_data); + +#ifdef CONFIG_USB_PD_CUSTOM_VDM + bool (*dfp_notify_uvdm)(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack); + + bool (*ufp_notify_uvdm)(struct pd_port *pd_port, + struct svdm_svid_data *svid_data); +#endif + + bool (*reset_state)(struct pd_port *pd_port, + struct svdm_svid_data *svid_data); + + bool (*parse_svid_data)(struct pd_port *pd_port, + struct svdm_svid_data *svid_data); +}; + +static inline bool dpm_check_data_msg_event( + struct pd_port *pd_port, uint8_t msg) +{ + return pd_event_data_msg_match( + pd_get_curr_pd_event(pd_port), msg); +} + +#ifdef CONFIG_USB_PD_REV30 +static inline bool dpm_check_ext_msg_event( + struct pd_port *pd_port, uint8_t msg) +{ + return pd_event_ext_msg_match( + pd_get_curr_pd_event(pd_port), msg); +} +#endif /* CONFIG_USB_PD_REV30 */ + +static inline uint8_t dpm_vdm_get_ops(struct pd_port *pd_port) +{ + return pd_port->curr_vdm_ops; +} + +static inline uint16_t dpm_vdm_get_svid(struct pd_port *pd_port) +{ + return pd_port->curr_vdm_svid; +} + +static inline int dpm_vdm_reply_svdm_request( + struct pd_port *pd_port, bool ack) +{ + return pd_reply_svdm_request_simply( + pd_port, ack ? CMDT_RSP_ACK : CMDT_RSP_NAK); +} + +static inline int dpm_vdm_reply_svdm_nak(struct pd_port *pd_port) +{ + return pd_reply_svdm_request_simply(pd_port, CMDT_RSP_NAK); +} + +enum { + GOOD_PW_NONE = 0, /* both no GP */ + GOOD_PW_PARTNER, /* partner has GP */ + GOOD_PW_LOCAL, /* local has GP */ + GOOD_PW_BOTH, /* both have GPs */ +}; + +int dpm_check_good_power(struct pd_port *pd_port); + +/* SVDM */ + +extern struct svdm_svid_data * + dpm_get_svdm_svid_data(struct pd_port *pd_port, uint16_t svid); + +extern bool svdm_reset_state(struct pd_port *pd_port); +extern bool svdm_notify_pe_startup(struct pd_port *pd_port); + +static inline int svdm_notify_pe_ready(struct pd_port *pd_port) +{ + int i, ret; + struct svdm_svid_data *svid_data; + + for (i = 0; i < pd_port->svid_data_cnt; i++) { + svid_data = &pd_port->svid_data[i]; + if (svid_data->ops && svid_data->ops->notify_pe_ready) { + ret = svid_data->ops->notify_pe_ready( + pd_port, svid_data); + if (ret != 0) + return ret; + } + } + + return 0; +} + +static inline bool svdm_notify_pe_shutdown( + struct pd_port *pd_port) +{ + int i; + struct svdm_svid_data *svid_data; + + for (i = 0; i < pd_port->svid_data_cnt; i++) { + svid_data = &pd_port->svid_data[i]; + if (svid_data->ops && svid_data->ops->notify_pe_shutdown) { + svid_data->ops->notify_pe_shutdown( + pd_port, svid_data); + } + } + + return 0; +} + +static inline bool svdm_dfp_inform_id(struct pd_port *pd_port, bool ack) +{ + int i; + struct svdm_svid_data *svid_data; + + for (i = 0; i < pd_port->svid_data_cnt; i++) { + svid_data = &pd_port->svid_data[i]; + if (svid_data->ops && svid_data->ops->dfp_inform_id) + svid_data->ops->dfp_inform_id(pd_port, svid_data, ack); + } + + return true; +} + +static inline bool svdm_dfp_inform_svids(struct pd_port *pd_port, bool ack) +{ + int i; + struct svdm_svid_data *svid_data; + + for (i = 0; i < pd_port->svid_data_cnt; i++) { + svid_data = &pd_port->svid_data[i]; + if (svid_data->ops && svid_data->ops->dfp_inform_svids) + svid_data->ops->dfp_inform_svids( + pd_port, svid_data, ack); + } + + return true; +} + +static inline bool svdm_dfp_inform_modes( + struct pd_port *pd_port, uint16_t svid, bool ack) +{ + struct svdm_svid_data *svid_data; + + svid_data = dpm_get_svdm_svid_data(pd_port, svid); + if (svid_data == NULL) + return false; + + if (svid_data->ops && svid_data->ops->dfp_inform_modes) + svid_data->ops->dfp_inform_modes(pd_port, svid_data, ack); + + return true; +} + +static inline bool svdm_dfp_inform_enter_mode( + struct pd_port *pd_port, uint16_t svid, uint8_t ops, bool ack) +{ + struct svdm_svid_data *svid_data; + + svid_data = dpm_get_svdm_svid_data(pd_port, svid); + if (svid_data == NULL) + return false; + + if (svid_data->ops && svid_data->ops->dfp_inform_enter_mode) + svid_data->ops->dfp_inform_enter_mode( + pd_port, svid_data, ops, ack); + + return true; +} + +static inline bool svdm_dfp_inform_exit_mode( + struct pd_port *pd_port, uint16_t svid, uint8_t ops) +{ + struct svdm_svid_data *svid_data; + + svid_data = dpm_get_svdm_svid_data(pd_port, svid); + if (svid_data == NULL) + return false; + + if (svid_data->ops && svid_data->ops->dfp_inform_exit_mode) + svid_data->ops->dfp_inform_exit_mode(pd_port, svid_data, ops); + + return true; +} + +static inline bool svdm_dfp_inform_attention( + struct pd_port *pd_port, uint16_t svid) +{ + struct svdm_svid_data *svid_data; + + svid_data = dpm_get_svdm_svid_data(pd_port, svid); + if (svid_data == NULL) + return false; + + if (svid_data->ops && svid_data->ops->dfp_inform_attention) + svid_data->ops->dfp_inform_attention(pd_port, svid_data); + + return true; +} + +static inline bool svdm_ufp_request_enter_mode( + struct pd_port *pd_port, uint16_t svid, uint8_t ops) +{ + struct svdm_svid_data *svid_data; + + svid_data = dpm_get_svdm_svid_data(pd_port, svid); + if (svid_data == NULL) + return false; + + if (svid_data->ops && svid_data->ops->ufp_request_enter_mode) + svid_data->ops->ufp_request_enter_mode(pd_port, svid_data, ops); + + return true; +} + +static inline bool svdm_ufp_request_exit_mode( + struct pd_port *pd_port, uint16_t svid, uint8_t ops) +{ + struct svdm_svid_data *svid_data; + + svid_data = dpm_get_svdm_svid_data(pd_port, svid); + if (svid_data == NULL) + return false; + + if (svid_data->ops && svid_data->ops->ufp_request_exit_mode) + svid_data->ops->ufp_request_exit_mode(pd_port, svid_data, ops); + + return true; +} + +#endif /* PD_DPM_PRV_H_INCLUDED */ diff --git a/drivers/usb/typec/mmi_tcpc/pd_dpm_reaction.c b/drivers/usb/typec/mmi_tcpc/pd_dpm_reaction.c new file mode 100644 index 000000000000..38809d2b89cf --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_dpm_reaction.c @@ -0,0 +1,770 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * PD Device Policy Manager Ready State reactions + * + * 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. + */ + +#include "inc/tcpci.h" +#include "inc/pd_policy_engine.h" +#include "inc/pd_dpm_core.h" +#include "inc/pd_dpm_pdo_select.h" +#include "pd_dpm_prv.h" + +#define DPM_REACTION_COND_ALWAYS (1<<0) +#define DPM_REACTION_COND_UFP_ONLY (1<<1) +#define DPM_REACTION_COND_DFP_ONLY (1<<2) +#define DPM_REACTION_COND_PD30 (1<<3) + +#define DPM_REACCOND_DFP \ + (DPM_REACTION_COND_ALWAYS | DPM_REACTION_COND_DFP_ONLY) + +#define DPM_REACCOND_UFP \ + (DPM_REACTION_COND_ALWAYS | DPM_REACTION_COND_UFP_ONLY) + +#define DPM_REACTION_COND_CHECK_ONCE (1<<5) +#define DPM_REACTION_COND_ONE_SHOT (1<<6) +#define DPM_REACTION_COND_LIMITED_RETRIES (1<<7) + +/* + * DPM flow delay reactions + */ + +#ifdef CONFIG_USB_PD_UFP_FLOW_DELAY +static uint8_t dpm_reaction_ufp_flow_delay(struct pd_port *pd_port) +{ + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + DPM_INFO("UFP Delay\n"); + pd_restart_timer(pd_port, PD_TIMER_UFP_FLOW_DELAY); + return DPM_READY_REACTION_BUSY; +} +#endif /* CONFIG_USB_PD_UFP_FLOW_DELAY */ + +#ifdef CONFIG_USB_PD_DFP_FLOW_DELAY +static uint8_t dpm_reaction_dfp_flow_delay(struct pd_port *pd_port) +{ + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + DPM_INFO("DFP Delay\n"); + pd_restart_timer(pd_port, PD_TIMER_DFP_FLOW_DELAY); + return DPM_READY_REACTION_BUSY; +} +#endif /* CONFIG_USB_PD_DFP_FLOW_DELAY */ + +#ifdef CONFIG_USB_PD_VCONN_STABLE_DELAY +static uint8_t dpm_reaction_vconn_stable_delay(struct pd_port *pd_port) +{ + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_port->vconn_role == PD_ROLE_VCONN_DYNAMIC_ON) { + DPM_INFO("VStable Delay\n"); + return DPM_READY_REACTION_BUSY; + } + + return 0; +} +#endif /* CONFIG_USB_PD_VCONN_STABLE_DELAY */ + +/* + * DPM get cap reaction + */ + +#ifdef CONFIG_USB_PD_REV30 +static uint8_t dpm_reaction_get_source_cap_ext(struct pd_port *pd_port) +{ + if (pd_port->power_role == PD_ROLE_SINK) + return TCP_DPM_EVT_GET_SOURCE_CAP_EXT; + + return 0; +} +#endif /* CONFIG_USB_PD_REV30 */ + +static uint8_t dpm_reaction_get_sink_cap(struct pd_port *pd_port) +{ + return TCP_DPM_EVT_GET_SINK_CAP; +} + +static uint8_t dpm_reaction_get_source_cap(struct pd_port *pd_port) +{ + return TCP_DPM_EVT_GET_SOURCE_CAP; +} + +static uint8_t dpm_reaction_attemp_get_flag(struct pd_port *pd_port) +{ + return TCP_DPM_EVT_GET_SINK_CAP; +} + +/* + * DPM swap reaction + */ + +#ifdef CONFIG_USB_PD_PR_SWAP +static uint8_t dpm_reaction_request_pr_swap(struct pd_port *pd_port) +{ + uint32_t prefer_role = + DPM_CAP_EXTRACT_PR_CHECK(pd_port->dpm_caps); + + if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER)) + return 0; + + if (pd_port->power_role == PD_ROLE_SINK) { + if (prefer_role == DPM_CAP_PR_CHECK_PREFER_SRC) + return TCP_DPM_EVT_PR_SWAP_AS_SRC; + } else { +#ifdef CONFIG_USB_PD_SRC_TRY_PR_SWAP_IF_BAD_PW + if (dpm_check_good_power(pd_port) == GOOD_PW_PARTNER) + return TCP_DPM_EVT_PR_SWAP_AS_SNK; +#endif /* CONFIG_USB_PD_SRC_TRY_PR_SWAP_IF_BAD_PW */ + + if (prefer_role == DPM_CAP_PR_CHECK_PREFER_SNK) + return TCP_DPM_EVT_PR_SWAP_AS_SNK; + } + + return 0; +} +#endif /* CONFIG_USB_PD_PR_SWAP */ + +#ifdef CONFIG_USB_PD_DR_SWAP +static uint8_t dpm_reaction_request_dr_swap(struct pd_port *pd_port) +{ + uint32_t prefer_role = + DPM_CAP_EXTRACT_DR_CHECK(pd_port->dpm_caps); + + if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_DR_DATA)) + return 0; + + if (pd_port->data_role == PD_ROLE_DFP + && prefer_role == DPM_CAP_DR_CHECK_PREFER_UFP) + return TCP_DPM_EVT_DR_SWAP_AS_UFP; + + if (pd_port->data_role == PD_ROLE_UFP + && prefer_role == DPM_CAP_DR_CHECK_PREFER_DFP) + return TCP_DPM_EVT_DR_SWAP_AS_DFP; + + return 0; +} +#endif /* #ifdef CONFIG_USB_PD_DR_SWAP */ + + +/* + * DPM DiscoverCable reaction + */ + +#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE +static uint8_t dpm_reaction_dynamic_vconn(struct pd_port *pd_port) +{ + pd_dpm_dynamic_enable_vconn(pd_port); + return 0; +} +#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */ + +#ifdef CONFIG_USB_PD_DISCOVER_CABLE_REQUEST_VCONN +static uint8_t dpm_reaction_request_vconn_source(struct pd_port *pd_port) +{ + bool return_vconn = true; + + if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_VCONN_SUPPLY)) + return 0; + + if (pd_port->vconn_role) + return 0; + +#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE + if (pd_port->tcpc->tcpc_vconn_supply == TCPC_VCONN_SUPPLY_STARTUP) + return_vconn = false; +#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */ + + if (pd_check_rev30(pd_port)) + return_vconn = false; + + if (return_vconn) + dpm_reaction_set(pd_port, DPM_REACTION_RETURN_VCONN_SRC); + + return TCP_DPM_EVT_VCONN_SWAP_ON; +} +#endif /* CONFIG_USB_PD_DISCOVER_CABLE_REQUEST_VCONN */ + +#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID +static uint8_t pd_dpm_reaction_discover_cable(struct pd_port *pd_port) +{ +#ifdef CONFIG_PD_DFP_RESET_CABLE + if (pd_is_reset_cable(pd_port)) + return TCP_DPM_EVT_CABLE_SOFTRESET; +#endif /* CONFIG_PD_DFP_RESET_CABLE */ + + if (pd_is_discover_cable(pd_port)) { + pd_restart_timer(pd_port, PD_TIMER_DISCOVER_ID); + return DPM_READY_REACTION_BUSY; + } + + return 0; +} +#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */ + +#ifdef CONFIG_USB_PD_DISCOVER_CABLE_RETURN_VCONN +static uint8_t dpm_reaction_return_vconn_source(struct pd_port *pd_port) +{ + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_port->vconn_role) { + DPM_DBG("VconnReturn\n"); + return TCP_DPM_EVT_VCONN_SWAP_OFF; + } + + return 0; +} +#endif /* CONFIG_USB_PD_DISCOVER_CABLE_RETURN_VCONN */ + +/* + * DPM EnterMode reaction + */ + +#ifdef CONFIG_USB_PD_ATTEMP_DISCOVER_ID +static uint8_t dpm_reaction_discover_id(struct pd_port *pd_port) +{ + return TCP_DPM_EVT_DISCOVER_ID; +} +#endif /* CONFIG_USB_PD_ATTEMP_DISCOVER_ID */ + +#ifdef CONFIG_USB_PD_ATTEMP_DISCOVER_SVID +static uint8_t dpm_reaction_discover_svid(struct pd_port *pd_port) +{ + return TCP_DPM_EVT_DISCOVER_SVIDS; +} +#endif /* CONFIG_USB_PD_ATTEMP_DISCOVER_SVID */ + +#ifdef CONFIG_USB_PD_MODE_OPERATION +static uint8_t dpm_reaction_mode_operation(struct pd_port *pd_port) +{ + if (svdm_notify_pe_ready(pd_port)) + return DPM_READY_REACTION_BUSY; + + return 0; +} +#endif /* CONFIG_USB_PD_MODE_OPERATION */ + +/* + * DPM Local/Remote Alert reaction + */ + +#ifdef CONFIG_USB_PD_REV30 + +#ifdef CONFIG_USB_PD_DPM_AUTO_SEND_ALERT + +static uint8_t dpm_reaction_send_alert(struct pd_port *pd_port) +{ + uint32_t alert_urgent; + struct pe_data *pe_data = &pd_port->pe_data; + + alert_urgent = pe_data->local_alert; + alert_urgent &= ~ADO_GET_STATUS_ONCE_MASK; + + if (!pe_data->get_status_once) + pe_data->local_alert = alert_urgent; + + if ((!pe_data->pe_ready) && (alert_urgent == 0)) + return 0; + + if (pe_data->local_alert == 0) + return 0; + + return TCP_DPM_EVT_ALERT; +} + +#endif /* CONFIG_USB_PD_DPM_AUTO_SEND_ALERT */ + +#ifdef CONFIG_USB_PD_DPM_AUTO_GET_STATUS + +const uint32_t c_get_status_alert_type = ADO_ALERT_OCP| + ADO_ALERT_OTP|ADO_ALERT_OVP|ADO_ALERT_OPER_CHANGED| + ADO_ALERT_SRC_IN_CHANGED; + +static inline uint8_t dpm_reaction_alert_status_changed(struct pd_port *pd_port) +{ + pd_port->pe_data.remote_alert &= + ~ADO_ALERT_TYPE_SET(c_get_status_alert_type); + + return TCP_DPM_EVT_GET_STATUS; +} + +static inline uint8_t dpm_reaction_alert_battry_changed(struct pd_port *pd_port) +{ + uint8_t i; + uint8_t mask; + uint8_t bat_change_i = 255; + uint8_t bat_change_mask1, bat_change_mask2; + + bat_change_mask1 = ADO_FIXED_BAT(pd_port->pe_data.remote_alert); + bat_change_mask2 = ADO_HOT_SWAP_BAT(pd_port->pe_data.remote_alert); + + if (bat_change_mask1) { + for (i = 0; i < 4; i++) { + mask = 1<pe_data.remote_alert &= + ~ADO_FIXED_BAT_SET(bat_change_mask1); + break; + } + } + } else if (bat_change_mask2) { + for (i = 0; i < 4; i++) { + mask = 1<pe_data.remote_alert &= + ~ADO_HOT_SWAP_BAT_SET(bat_change_mask2); + break; + } + } + } + + if (bat_change_mask1 == 0 && bat_change_mask2 == 0) { + pd_port->pe_data.remote_alert &= + ~ADO_ALERT_TYPE_SET(ADO_ALERT_BAT_CHANGED); + } + + if (bat_change_i == 255) + return 0; + + pd_port->tcp_event.tcp_dpm_data.data_object[0] = bat_change_i; + return TCP_DPM_EVT_GET_BAT_STATUS; +} + +static uint8_t dpm_reaction_handle_alert(struct pd_port *pd_port) +{ + uint32_t alert_type = ADO_ALERT_TYPE(pd_port->pe_data.remote_alert); + + if (alert_type & c_get_status_alert_type) + return dpm_reaction_alert_status_changed(pd_port); + + if (alert_type & ADO_ALERT_BAT_CHANGED) + return dpm_reaction_alert_battry_changed(pd_port); + + return 0; +} +#endif /* CONFIG_USB_PD_DPM_AUTO_GET_STATUS */ + +#endif /* CONFIG_USB_PD_REV30 */ + +/* + * DPM Idle reaction + */ + +static inline uint8_t dpm_get_pd_connect_state(struct pd_port *pd_port) +{ + if (pd_port->power_role == PD_ROLE_SOURCE) { + if (pd_check_rev30(pd_port)) + return PD_CONNECT_PE_READY_SRC_PD30; + + return PD_CONNECT_PE_READY_SRC; + } + + if (pd_check_rev30(pd_port)) { + if (pd_is_source_support_apdo(pd_port)) + return PD_CONNECT_PE_READY_SNK_APDO; + + return PD_CONNECT_PE_READY_SNK_PD30; + } + + return PD_CONNECT_PE_READY_SNK; +} + +static inline void dpm_check_vconn_highv_prot(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_VCONN_SAFE5V_ONLY + struct tcpc_device *tcpc = pd_port->tcpc; + struct pe_data *pe_data = &pd_port->pe_data; + bool vconn_highv_prot = pd_port->request_v_new > 5000; + + if (pe_data->vconn_highv_prot && !vconn_highv_prot && + tcpc->tcpc_flags & TCPC_FLAGS_VCONN_SAFE5V_ONLY) { + DPM_INFO("VC_HIGHV_PROT: %d\n", vconn_highv_prot); + pe_data->vconn_highv_prot = vconn_highv_prot; + pd_set_vconn(pd_port, pe_data->vconn_highv_prot_role); + } +#endif /* CONFIG_USB_PD_VCONN_SAFE5V_ONLY */ +} + +static uint8_t dpm_reaction_update_pe_ready(struct pd_port *pd_port) +{ + uint8_t state; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (!pd_port->pe_data.pe_ready) { + DPM_INFO("PE_READY\n"); + pd_port->pe_data.pe_ready = true; +#ifdef CONFIG_DUAL_ROLE_USB_INTF + dual_role_instance_changed(pd_port->tcpc->dr_usb); +#endif /* CONFIG_DUAL_ROLE_USB_INTF */ + } + + state = dpm_get_pd_connect_state(pd_port); + pd_update_connect_state(pd_port, state); + + dpm_check_vconn_highv_prot(pd_port); + pd_dpm_dynamic_disable_vconn(pd_port); + +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + pd_port->pe_data.pd_traffic_idle = true; + if (pd_check_rev30(pd_port) && + (pd_port->power_role == PD_ROLE_SOURCE)) + pd_set_sink_tx(pd_port, PD30_SINK_TX_OK); +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ + + return 0; +} + +/* + * DPM reaction declaration + */ + +typedef uint8_t (*dpm_reaction_fun)(struct pd_port *pd_port); + +struct dpm_ready_reaction { + uint32_t bit_mask; + uint8_t condition; + dpm_reaction_fun handler; +}; + +#define DECL_DPM_REACTION(xmask, xcond, xhandler) { \ + .bit_mask = xmask, \ + .condition = xcond, \ + .handler = xhandler, \ +} + +#define DECL_DPM_REACTION_ALWAYS(xhandler) \ + DECL_DPM_REACTION(DPM_REACTION_CAP_ALWAYS, \ + DPM_REACTION_COND_ALWAYS, \ + xhandler) + +#define DECL_DPM_REACTION_CHECK_ONCE(xmask, xhandler) \ + DECL_DPM_REACTION(xmask, \ + DPM_REACTION_COND_ALWAYS | \ + DPM_REACTION_COND_CHECK_ONCE, \ + xhandler) + +#define DECL_DPM_REACTION_LIMITED_RETRIES(xmask, xhandler) \ + DECL_DPM_REACTION(xmask, \ + DPM_REACTION_COND_ALWAYS |\ + DPM_REACTION_COND_LIMITED_RETRIES, \ + xhandler) + +#define DECL_DPM_REACTION_ONE_SHOT(xmask, xhandler) \ + DECL_DPM_REACTION(xmask, \ + DPM_REACTION_COND_ALWAYS | \ + DPM_REACTION_COND_ONE_SHOT, \ + xhandler) + +#define DECL_DPM_REACTION_UFP(xmask, xhandler) \ + DECL_DPM_REACTION(xmask, \ + DPM_REACTION_COND_UFP_ONLY, \ + xhandler) + +#define DECL_DPM_REACTION_DFP(xmask, xhandler) \ + DECL_DPM_REACTION(xmask, \ + DPM_REACTION_COND_DFP_ONLY, \ + xhandler) + +#define DECL_DPM_REACTION_PD30(xmask, xhandler) \ + DECL_DPM_REACTION(xmask, \ + DPM_REACTION_COND_PD30, \ + xhandler) + +#define DECL_DPM_REACTION_PD30_LIMITED_RETRIES(xmask, xhandler) \ + DECL_DPM_REACTION(xmask, \ + DPM_REACTION_COND_PD30 |\ + DPM_REACTION_COND_LIMITED_RETRIES, \ + xhandler) + +#define DECL_DPM_REACTION_PD30_ONE_SHOT(xmask, xhandler) \ + DECL_DPM_REACTION(xmask, \ + DPM_REACTION_COND_PD30 | \ + DPM_REACTION_COND_ONE_SHOT, \ + xhandler) + +#define DECL_DPM_REACTION_DFP_PD30_ONE_SHOT(xmask, xhandler) \ + DECL_DPM_REACTION(xmask, \ + DPM_REACTION_COND_DFP_ONLY |\ + DPM_REACTION_COND_PD30 | \ + DPM_REACTION_COND_ONE_SHOT, \ + xhandler) + +#define DECL_DPM_REACTION_DFP_PD30_LIMITED_RETRIES(xmask, xhandler) \ + DECL_DPM_REACTION(xmask, \ + DPM_REACTION_COND_DFP_ONLY |\ + DPM_REACTION_COND_PD30 | \ + DPM_REACTION_COND_LIMITED_RETRIES, \ + xhandler) + +#define DECL_DPM_REACTION_DFP_PD30_CHECK_ONCE(xmask, xhandler) \ + DECL_DPM_REACTION(xmask, \ + DPM_REACTION_COND_DFP_ONLY |\ + DPM_REACTION_COND_PD30 | \ + DPM_REACTION_COND_CHECK_ONCE, \ + xhandler) + +#define DECL_DPM_REACTION_DFP_PD30_RUN_ONCE(xmask, xhandler) \ + DECL_DPM_REACTION(xmask, \ + DPM_REACTION_COND_DFP_ONLY |\ + DPM_REACTION_COND_PD30 | \ + DPM_REACTION_COND_CHECK_ONCE | \ + DPM_REACTION_COND_ONE_SHOT, \ + xhandler) + +static const struct dpm_ready_reaction dpm_reactions[] = { + +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_DPM_AUTO_SEND_ALERT + DECL_DPM_REACTION_PD30( + DPM_REACTION_CAP_ALWAYS, + dpm_reaction_send_alert), +#endif /* CONFIG_USB_PD_DPM_AUTO_SEND_ALERT */ +#ifdef CONFIG_USB_PD_DPM_AUTO_GET_STATUS + DECL_DPM_REACTION_PD30( + DPM_REACTION_CAP_ALWAYS, + dpm_reaction_handle_alert), +#endif /* CONFIG_USB_PD_DPM_AUTO_GET_STATUS */ +#endif /* CONFIG_USB_PD_REV30 */ + +#ifdef CONFIG_USB_PD_DFP_FLOW_DELAY + DECL_DPM_REACTION_DFP( + DPM_REACTION_DFP_FLOW_DELAY, + dpm_reaction_dfp_flow_delay), +#endif /* CONFIG_USB_PD_DFP_FLOW_DELAY */ + +#ifdef CONFIG_USB_PD_UFP_FLOW_DELAY + DECL_DPM_REACTION_UFP( + DPM_REACTION_UFP_FLOW_DELAY, + dpm_reaction_ufp_flow_delay), +#endif /* CONFIG_USB_PD_UFP_FLOW_DELAY */ + + DECL_DPM_REACTION_LIMITED_RETRIES( + DPM_REACTION_GET_SINK_CAP, + dpm_reaction_get_sink_cap), + + DECL_DPM_REACTION_LIMITED_RETRIES( + DPM_REACTION_GET_SOURCE_CAP, + dpm_reaction_get_source_cap), + + DECL_DPM_REACTION_LIMITED_RETRIES( + DPM_REACTION_ATTEMPT_GET_FLAG, + dpm_reaction_attemp_get_flag), + +#ifdef CONFIG_USB_PD_REV30 + DECL_DPM_REACTION_PD30_ONE_SHOT( + DPM_REACTION_GET_SOURCE_CAP_EXT, + dpm_reaction_get_source_cap_ext), +#endif /* CONFIG_USB_PD_REV30 */ + +#ifdef CONFIG_USB_PD_PR_SWAP + DECL_DPM_REACTION_CHECK_ONCE( + DPM_REACTION_REQUEST_PR_SWAP, + dpm_reaction_request_pr_swap), +#endif /* CONFIG_USB_PD_PR_SWAP */ + +#ifdef CONFIG_USB_PD_DR_SWAP + DECL_DPM_REACTION_CHECK_ONCE( + DPM_REACTION_REQUEST_DR_SWAP, + dpm_reaction_request_dr_swap), +#endif /* CONFIG_USB_PD_DR_SWAP */ + +#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE + DECL_DPM_REACTION_DFP_PD30_CHECK_ONCE( + DPM_REACTION_DYNAMIC_VCONN, + dpm_reaction_dynamic_vconn), +#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */ + +#ifdef CONFIG_USB_PD_DISCOVER_CABLE_REQUEST_VCONN + DECL_DPM_REACTION_DFP_PD30_RUN_ONCE( + DPM_REACTION_REQUEST_VCONN_SRC, + dpm_reaction_request_vconn_source), +#endif /* CONFIG_USB_PD_DISCOVER_CABLE_REQUEST_VCONN */ + +#ifdef CONFIG_USB_PD_VCONN_STABLE_DELAY + DECL_DPM_REACTION_DFP_PD30_CHECK_ONCE( + DPM_REACTION_VCONN_STABLE_DELAY, + dpm_reaction_vconn_stable_delay), +#endif /* CONFIG_USB_PD_VCONN_STABLE_DELAY */ + +#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID + DECL_DPM_REACTION_DFP_PD30_CHECK_ONCE( + DPM_REACTION_DISCOVER_CABLE, + pd_dpm_reaction_discover_cable), +#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */ + +#ifdef CONFIG_USB_PD_DISCOVER_CABLE_RETURN_VCONN + DECL_DPM_REACTION_DFP_PD30_RUN_ONCE( + DPM_REACTION_RETURN_VCONN_SRC, + dpm_reaction_return_vconn_source), +#endif /* CONFIG_USB_PD_DISCOVER_CABLE_RETURN_VCONN */ + +#ifdef CONFIG_USB_PD_ATTEMP_DISCOVER_ID + DECL_DPM_REACTION_DFP_PD30_LIMITED_RETRIES( + DPM_REACTION_DISCOVER_ID, + dpm_reaction_discover_id), +#endif /* CONFIG_USB_PD_ATTEMP_DISCOVER_ID */ + +#ifdef CONFIG_USB_PD_ATTEMP_DISCOVER_SVID + DECL_DPM_REACTION_DFP_PD30_LIMITED_RETRIES( + DPM_REACTION_DISCOVER_SVID, + dpm_reaction_discover_svid), +#endif /* CONFIG_USB_PD_ATTEMP_DISCOVER_SVID */ + +#ifdef CONFIG_USB_PD_MODE_OPERATION + DECL_DPM_REACTION_ALWAYS( + dpm_reaction_mode_operation), +#endif /* CONFIG_USB_PD_MODE_OPERATION */ + + DECL_DPM_REACTION_ALWAYS( + dpm_reaction_update_pe_ready), +}; + +/** + * dpm_get_reaction_env + * + * Get current reaction's environmental conditions. + * + * Returns environmental conditions. + */ + +static inline uint8_t dpm_get_reaction_env(struct pd_port *pd_port) +{ + uint8_t conditions; + + if (pd_port->data_role == PD_ROLE_DFP) + conditions = DPM_REACCOND_DFP; + else + conditions = DPM_REACCOND_UFP; + + if (pd_check_rev30(pd_port)) + conditions |= DPM_REACTION_COND_PD30; + + return conditions; +} + +/** + * dpm_check_reaction_busy + * + * check this reaction is still keep busy + * + * @ reaction : which reaction is checked. + * + * Return Boolean to indicate busy or not. + */ + +static inline bool dpm_check_reaction_busy(struct pd_port *pd_port, + const struct dpm_ready_reaction *reaction) +{ + if (reaction->bit_mask & DPM_REACTION_CAP_ALWAYS) + return false; + + return !dpm_reaction_check( + pd_port, DPM_REACTION_CAP_READY_ONCE); +} + +/** + * dpm_check_reaction_available + * + * check this reaction is available. + * + * @ env : Environmental conditions of reaction table + * @ reaction : which reaction is checked. + * + * Returns TCP_DPM_EVT_ID if available; + * Returns Zero to indicate if not available. + * Returns DPM_READY_REACTION_BUSY to indicate + * this reaction is waiting for being finished. + */ + +static inline uint8_t dpm_check_reaction_available(struct pd_port *pd_port, + uint8_t env, const struct dpm_ready_reaction *reaction) +{ + int ret; + + if (!dpm_reaction_check(pd_port, reaction->bit_mask)) + return 0; + + if (!(reaction->condition & env)) + return 0; + + if (dpm_check_reaction_busy(pd_port, reaction)) + return DPM_READY_REACTION_BUSY; + + ret = reaction->handler(pd_port); + + if (ret == 0 && + reaction->condition & DPM_REACTION_COND_CHECK_ONCE) + dpm_reaction_clear(pd_port, reaction->bit_mask); + + return ret; +} + +/** + * dpm_check_reset_reaction + * + * Once trigger one reaction, + * check if automatically clear this reaction flag. + * + * For the following reactions type: + * DPM_REACTION_COND_ONE_SHOT, + * DPM_REACTION_COND_LIMITED_RETRIES + * + * @ reaction : which reaction is triggered. + * + * Return Boolean to indicate automatically clear or not. + */ + +static inline bool dpm_check_clear_reaction(struct pd_port *pd_port, + const struct dpm_ready_reaction *reaction) +{ + if (pd_port->pe_data.dpm_reaction_id != reaction->bit_mask) + pd_port->pe_data.dpm_reaction_retry = 0; + + pd_port->pe_data.dpm_reaction_retry++; + pd_port->pe_data.dpm_reaction_id = reaction->bit_mask; + + if (reaction->condition & DPM_REACTION_COND_ONE_SHOT) + return true; + + if (reaction->condition & DPM_REACTION_COND_LIMITED_RETRIES) + return pd_port->pe_data.dpm_reaction_retry > 3; + + return false; +} + +uint8_t pd_dpm_get_ready_reaction(struct pd_port *pd_port) +{ + uint8_t evt; + uint8_t env; + uint32_t clear_reaction = DPM_REACTION_CAP_READY_ONCE; + const struct dpm_ready_reaction *reaction = dpm_reactions; + const struct dpm_ready_reaction *reaction_last = + dpm_reactions + ARRAY_SIZE(dpm_reactions); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + env = dpm_get_reaction_env(pd_port); + + do { + evt = dpm_check_reaction_available(pd_port, env, reaction); + } while ((evt == 0) && (++reaction < reaction_last)); + + if (evt > 0 && dpm_check_clear_reaction(pd_port, reaction)) { + clear_reaction |= reaction->bit_mask; + DPM_DBG("clear_reaction=%d\n", evt); + } + + dpm_reaction_clear(pd_port, clear_reaction); + return evt; +} diff --git a/drivers/usb/typec/mmi_tcpc/pd_dpm_uvdm.c b/drivers/usb/typec/mmi_tcpc/pd_dpm_uvdm.c new file mode 100644 index 000000000000..2a47c3ffc195 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_dpm_uvdm.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * PD Device Policy Manager for UVDM + * + * 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. + */ + +#include "inc/tcpci.h" +#include "inc/pd_policy_engine.h" +#include "inc/pd_dpm_core.h" +#include "pd_dpm_prv.h" + +#ifdef CONFIG_USB_PD_RICHTEK_UVDM + +bool richtek_dfp_notify_pe_startup( + struct pd_port *pd_port, struct svdm_svid_data *svid_data) +{ + UVDM_INFO("%s\n", __func__); + pd_port->richtek_init_done = false; + return true; +} + +int richtek_dfp_notify_pe_ready( + struct pd_port *pd_port, struct svdm_svid_data *svid_data) +{ + if (pd_port->data_role != PD_ROLE_DFP) + return 0; + + if (pd_port->richtek_init_done) + return 0; + + pd_port->richtek_init_done = true; + UVDM_INFO("%s\n", __func__); + +#if 0 + pd_port->uvdm_cnt = 3; + pd_port->uvdm_wait_resp = true; + + pd_port->uvdm_data[0] = PD_UVDM_HDR(USB_VID_RICHTEK, 0x4321); + pd_port->uvdm_data[1] = 0x11223344; + pd_port->uvdm_data[2] = 0x44332211; + + pd_put_tcp_vdm_event(pd_port, TCP_DPM_EVT_UVDM); +#endif + + return 1; +} + +bool richtek_dfp_notify_uvdm(struct pd_port *pd_port, + struct svdm_svid_data *svid_data, bool ack) +{ + uint32_t resp_cmd = 0; + + if (ack) { + if (pd_port->uvdm_wait_resp) + resp_cmd = PD_UVDM_HDR_CMD(pd_port->uvdm_data[0]); + + UVDM_INFO("dfp_notify: ACK (0x%x)\n", resp_cmd); + } else + UVDM_INFO("dfp_notify: NAK\n"); + + return true; +} + +bool richtek_ufp_notify_uvdm(struct pd_port *pd_port, + struct svdm_svid_data *svid_data) +{ + uint8_t i; + uint32_t reply_cmd[VDO_MAX_NR]; + uint16_t cmd = (uint16_t) PD_UVDM_HDR_CMD(pd_port->uvdm_data[0]); + + UVDM_INFO("ufp_notify: 0x%x\n", cmd); + + if (cmd >= 0x1000) { + UVDM_INFO("uvdm_no_reply\n"); + VDM_STATE_DPM_INFORMED(pd_port); + return true; + } + + reply_cmd[0] = PD_UVDM_HDR(USB_VID_RICHTEK, cmd+1); + + for (i = 1; i < pd_port->uvdm_cnt; i++) + reply_cmd[i] = ~pd_port->uvdm_data[i]; + + pd_reply_custom_vdm(pd_port, TCPC_TX_SOP, + pd_port->uvdm_cnt, reply_cmd); + VDM_STATE_NORESP_CMD(pd_port); + return true; +} + +#endif /* CONFIG_USB_PD_RICHTEK_UVDM */ diff --git a/drivers/usb/typec/mmi_tcpc/pd_policy_engine.c b/drivers/usb/typec/mmi_tcpc/pd_policy_engine.c new file mode 100644 index 000000000000..e28de142648f --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_policy_engine.c @@ -0,0 +1,1372 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Policy Engine Driver + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/pd_dpm_core.h" +#include "inc/tcpci.h" +#include "inc/pd_process_evt.h" +#include "inc/pd_policy_engine.h" + +/* ---- Policy Engine State ---- */ + +#if PE_DBG_ENABLE | PE_STATE_INFO_ENABLE +#if PE_STATE_FULL_NAME + +static const char *const pe_state_name[] = { + +/******************* Source *******************/ +#ifdef CONFIG_USB_PD_PE_SOURCE + "PE_SRC_STARTUP", + "PE_SRC_DISCOVERY", + "PE_SRC_SEND_CAPABILITIES", + "PE_SRC_NEGOTIATE_CAPABILITIES", + "PE_SRC_TRANSITION_SUPPLY", + "PE_SRC_TRANSITION_SUPPLY2", + "PE_SRC_READY", + "PE_SRC_DISABLED", + "PE_SRC_CAPABILITY_RESPONSE", + "PE_SRC_HARD_RESET", + "PE_SRC_HARD_RESET_RECEIVED", + "PE_SRC_TRANSITION_TO_DEFAULT", + "PE_SRC_GET_SINK_CAP", + "PE_SRC_WAIT_NEW_CAPABILITIES", + "PE_SRC_SEND_SOFT_RESET", + "PE_SRC_SOFT_RESET", +/* Source Startup Discover Cable */ +#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID +#ifdef CONFIG_PD_SRC_RESET_CABLE + "PE_SRC_CBL_SEND_SOFT_RESET", +#endif /* CONFIG_PD_SRC_RESET_CABLE */ + "PE_SRC_VDM_IDENTITY_REQUEST", + "PE_SRC_VDM_IDENTITY_ACKED", + "PE_SRC_VDM_IDENTITY_NAKED", +#endif /* PD_CAP_PE_SRC_STARTUP_DISCOVER_ID */ +/* Source for PD30 */ +#ifdef CONFIG_USB_PD_REV30 + "PE_SRC_SEND_NOT_SUPPORTED", + "PE_SRC_NOT_SUPPORTED_RECEIVED", + "PE_SRC_CHUNK_RECEIVED", +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL + "PE_SRC_SEND_SOURCE_ALERT", +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE + "PE_SRC_SINK_ALERT_RECEIVED", +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL + "PE_SRC_GIVE_SOURCE_CAP_EXT", +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL + "PE_SRC_GIVE_SOURCE_STATUS", +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE + "PE_SRC_GET_SINK_STATUS", +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE + "PE_SRC_GIVE_PPS_STATUS", +#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */ +#endif /* CONFIG_USB_PD_REV30 */ +#endif /* CONFIG_USB_PD_PE_SOURCE */ +/******************* Sink *******************/ +#ifdef CONFIG_USB_PD_PE_SINK + "PE_SNK_STARTUP", + "PE_SNK_DISCOVERY", + "PE_SNK_WAIT_FOR_CAPABILITIES", + "PE_SNK_EVALUATE_CAPABILITY", + "PE_SNK_SELECT_CAPABILITY", + "PE_SNK_TRANSITION_SINK", + "PE_SNK_READY", + "PE_SNK_HARD_RESET", + "PE_SNK_TRANSITION_TO_DEFAULT", + "PE_SNK_GIVE_SINK_CAP", + "PE_SNK_GET_SOURCE_CAP", + "PE_SNK_SEND_SOFT_RESET", + "PE_SNK_SOFT_RESET", +/* Sink for PD30 */ +#ifdef CONFIG_USB_PD_REV30 + "PE_SNK_SEND_NOT_SUPPORTED", + "PE_SNK_NOT_SUPPORTED_RECEIVED", + "PE_SNK_CHUNK_RECEIVED", +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE + "PE_SNK_SOURCE_ALERT_RECEIVED", +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL + "PE_SNK_SEND_SINK_ALERT", +#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE + "PE_SNK_GET_SOURCE_CAP_EXT", +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE + "PE_SNK_GET_SOURCE_STATUS", +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL + "PE_SNK_GIVE_SINK_STATUS", +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + "PE_SNK_GET_PPS_STATUS", +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ +#endif /* CONFIG_USB_PD_REV30 */ +#endif /* CONFIG_USB_PD_PE_SINK */ +/******************* DR_SWAP *******************/ +#ifdef CONFIG_USB_PD_DR_SWAP +/* DR_SWAP_DFP */ + "PE_DRS_DFP_UFP_EVALUATE_DR_SWAP", + "PE_DRS_DFP_UFP_ACCEPT_DR_SWAP", + "PE_DRS_DFP_UFP_CHANGE_TO_UFP", + "PE_DRS_DFP_UFP_SEND_DR_SWAP", + "PE_DRS_DFP_UFP_REJECT_DR_SWAP", +/* DR_SWAP_UFP */ + "PE_DRS_UFP_DFP_EVALUATE_DR_SWAP", + "PE_DRS_UFP_DFP_ACCEPT_DR_SWAP", + "PE_DRS_UFP_DFP_CHANGE_TO_DFP", + "PE_DRS_UFP_DFP_SEND_DR_SWAP", + "PE_DRS_UFP_DFP_REJECT_DR_SWAP", +#endif /* CONFIG_USB_PD_DR_SWAP */ +/******************* PR_SWAP *******************/ +#ifdef CONFIG_USB_PD_PR_SWAP +/* PR_SWAP_SRC */ + "PE_PRS_SRC_SNK_EVALUATE_PR_SWAP", + "PE_PRS_SRC_SNK_ACCEPT_PR_SWAP", + "PE_PRS_SRC_SNK_TRANSITION_TO_OFF", + "PE_PRS_SRC_SNK_ASSERT_RD", + "PE_PRS_SRC_SNK_WAIT_SOURCE_ON", + "PE_PRS_SRC_SNK_SEND_SWAP", + "PE_PRS_SRC_SNK_REJECT_PR_SWAP", +/* PR_SWAP_SNK */ + "PE_PRS_SNK_SRC_EVALUATE_PR_SWAP", + "PE_PRS_SNK_SRC_ACCEPT_PR_SWAP", + "PE_PRS_SNK_SRC_TRANSITION_TO_OFF", + "PE_PRS_SNK_SRC_ASSERT_RP", + "PE_PRS_SNK_SRC_SOURCE_ON", + "PE_PRS_SNK_SRC_SEND_SWAP", + "PE_PRS_SNK_SRC_REJECT_SWAP", +/* get same role cap */ + "PE_DR_SRC_GET_SOURCE_CAP", + "PE_DR_SRC_GIVE_SINK_CAP", + "PE_DR_SNK_GET_SINK_CAP", + "PE_DR_SNK_GIVE_SOURCE_CAP", +/* get same role cap for PD30 */ +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL + "PE_DR_SNK_GIVE_SOURCE_CAP_EXT", +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE + "PE_DR_SRC_GET_SOURCE_CAP_EXT", +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ +#endif /* CONFIG_USB_PD_REV30 */ +#endif /* CONFIG_USB_PD_PR_SWAP */ +/******************* VCONN_SWAP *******************/ +#ifdef CONFIG_USB_PD_VCONN_SWAP + "PE_VCS_SEND_SWAP", + "PE_VCS_EVALUATE_SWAP", + "PE_VCS_ACCEPT_SWAP", + "PE_VCS_REJECT_VCONN_SWAP", + "PE_VCS_WAIT_FOR_VCONN", + "PE_VCS_TURN_OFF_VCONN", + "PE_VCS_TURN_ON_VCONN", + "PE_VCS_SEND_PS_RDY", +#endif /* CONFIG_USB_PD_VCONN_SWAP */ +/******************* UFP_VDM *******************/ + "PE_UFP_VDM_GET_IDENTITY", + "PE_UFP_VDM_GET_SVIDS", + "PE_UFP_VDM_GET_MODES", + "PE_UFP_VDM_EVALUATE_MODE_ENTRY", + "PE_UFP_VDM_MODE_EXIT", + "PE_UFP_VDM_ATTENTION_REQUEST", +#ifdef CONFIG_USB_PD_ALT_MODE + "PE_UFP_VDM_DP_STATUS_UPDATE", + "PE_UFP_VDM_DP_CONFIGURE", +#endif/* CONFIG_USB_PD_ALT_MODE */ +/******************* DFP_VDM *******************/ + "PE_DFP_UFP_VDM_IDENTITY_REQUEST", + "PE_DFP_UFP_VDM_IDENTITY_ACKED", + "PE_DFP_UFP_VDM_IDENTITY_NAKED", + "PE_DFP_CBL_VDM_IDENTITY_REQUEST", + "PE_DFP_CBL_VDM_IDENTITY_ACKED", + "PE_DFP_CBL_VDM_IDENTITY_NAKED", + "PE_DFP_VDM_SVIDS_REQUEST", + "PE_DFP_VDM_SVIDS_ACKED", + "PE_DFP_VDM_SVIDS_NAKED", + "PE_DFP_VDM_MODES_REQUEST", + "PE_DFP_VDM_MODES_ACKED", + "PE_DFP_VDM_MODES_NAKED", + "PE_DFP_VDM_MODE_ENTRY_REQUEST", + "PE_DFP_VDM_MODE_ENTRY_ACKED", + "PE_DFP_VDM_MODE_ENTRY_NAKED", + "PE_DFP_VDM_MODE_EXIT_REQUEST", + "PE_DFP_VDM_MODE_EXIT_ACKED", + "PE_DFP_VDM_ATTENTION_REQUEST", +#ifdef CONFIG_PD_DFP_RESET_CABLE + "PE_DFP_CBL_SEND_SOFT_RESET", + "PE_DFP_CBL_SEND_CABLE_RESET", +#endif /* CONFIG_PD_DFP_RESET_CABLE */ +#ifdef CONFIG_USB_PD_ALT_MODE_DFP + "PE_DFP_VDM_DP_STATUS_UPDATE_REQUEST", + "PE_DFP_VDM_DP_STATUS_UPDATE_ACKED", + "PE_DFP_VDM_DP_STATUS_UPDATE_NAKED", + "PE_DFP_VDM_DP_CONFIGURATION_REQUEST", + "PE_DFP_VDM_DP_CONFIGURATION_ACKED", + "PE_DFP_VDM_DP_CONFIGURATION_NAKED", +#endif/* CONFIG_USB_PD_ALT_MODE_DFP */ +/******************* UVDM & SVDM *******************/ +#ifdef CONFIG_USB_PD_CUSTOM_VDM + "PE_UFP_UVDM_RECV", + "PE_DFP_UVDM_SEND", + "PE_DFP_UVDM_ACKED", + "PE_DFP_UVDM_NAKED", +#endif/* CONFIG_USB_PD_CUSTOM_VDM */ + "PE_UFP_VDM_SEND_NAK", +/******************* PD30 Common *******************/ +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE + "PE_GET_BATTERY_CAP", +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL + "PE_GIVE_BATTERY_CAP", +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE + "PE_GET_BATTERY_STATUS", +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL + "PE_GIVE_BATTERY_STATUS", +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE + "PE_GET_MANUFACTURER_INFO", +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL + "PE_GIVE_MANUFACTURER_INFO", +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE + "PE_GET_COUNTRY_CODES", +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL + "PE_GIVE_COUNTRY_CODES", +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE + "PE_GET_COUNTRY_INFO", +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL + "PE_GIVE_COUNTRY_INFO", +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL */ + + "PE_VDM_NOT_SUPPORTED", +#endif /* CONFIG_USB_PD_REV30 */ +/******************* Others *******************/ +#ifdef CONFIG_USB_PD_CUSTOM_DBGACC + "PE_DBG_READY", +#endif/* CONFIG_USB_PD_CUSTOM_DBGACC */ +#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER + "PE_OVER_RECV_HRESET_LIMIT", +#endif/* CONFIG_USB_PD_RECV_HRESET_COUNTER */ + "PE_REJECT", + "PE_ERROR_RECOVERY", +#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE + "PE_ERROR_RECOVERY_ONCE", +#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */ + "PE_BIST_TEST_DATA", + "PE_BIST_CARRIER_MODE_2", +/* Wait tx finished */ + "PE_IDLE1", + "PE_IDLE2", +}; + +#else + +static const char *const pe_state_name[] = { +/******************* Source *******************/ +#ifdef CONFIG_USB_PD_PE_SOURCE + "SRC_START", + "SRC_DISC", + "SRC_SEND_CAP", + "SRC_NEG_CAP", + "SRC_TRANS_SUPPLY", + "SRC_TRANS_SUPPLY2", + "SRC_READY", + "SRC_DISABLED", + "SRC_CAP_RESP", + "SRC_HRESET", + "SRC_HRESET_RECV", + "SRC_TRANS_DFT", + "SRC_GET_CAP", + "SRC_WAIT_CAP", + "SRC_SEND_SRESET", + "SRC_SRESET", +/* Source Startup Discover Cable */ +#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID +#ifdef CONFIG_PD_SRC_RESET_CABLE + "SRC_CBL_SEND_SRESET", +#endif /* CONFIG_PD_SRC_RESET_CABLE */ + "SRC_VDM_ID_REQ", + "SRC_VDM_ID_ACK", + "SRC_VDM_ID_NAK", +#endif /* PD_CAP_PE_SRC_STARTUP_DISCOVER_ID */ +/* Source for PD30 */ +#ifdef CONFIG_USB_PD_REV30 + "SRC_NO_SUPP", + "SRC_NO_SUPP_RECV", + "SRC_CK_RECV", +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL + "SRC_ALERT", +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE + "SRC_RECV_ALERT", +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL + "SRC_GIVE_CAP_EXT", +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL + "SRC_GIVE_STATUS", +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE + "SRC_GET_STATUS", +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE + "SRC_GIVE_PPS", +#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */ +#endif /* CONFIG_USB_PD_REV30 */ +#endif /* CONFIG_USB_PD_PE_SOURCE */ +/******************* Sink *******************/ +#ifdef CONFIG_USB_PD_PE_SINK +/* Sink Init */ + "SNK_START", + "SNK_DISC", + "SNK_WAIT_CAP", + "SNK_EVA_CAP", + "SNK_SEL_CAP", + "SNK_TRANS_SINK", + "SNK_READY", + "SNK_HRESET", + "SNK_TRANS_DFT", + "SNK_GIVE_CAP", + "SNK_GET_CAP", + "SNK_SEND_SRESET", + "SNK_SRESET", +/* Sink for PD30 */ +#ifdef CONFIG_USB_PD_REV30 + "SNK_NO_SUPP", + "SNK_NO_SUPP_RECV", + "SNK_CK_RECV", +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE + "SNK_RECV_ALERT", +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL + "SNK_ALERT", +#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE + "SNK_GET_CAP_EX", +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE + "SNK_GET_STATUS", +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL + "SNK_GIVE_STATUS", +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + "SNK_GET_PPS", +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ +#endif /* CONFIG_USB_PD_REV30 */ +#endif /* CONFIG_USB_PD_PE_SINK */ +/******************* DR_SWAP *******************/ +#ifdef CONFIG_USB_PD_DR_SWAP +/* DR_SWAP_DFP */ + "D_DFP_EVA", + "D_DFP_ACCEPT", + "D_DFP_CHANGE", + "D_DFP_SEND", + "D_DFP_REJECT", +/* DR_SWAP_UFP */ + "D_UFP_EVA", + "D_UFP_ACCEPT", + "D_UFP_CHANGE", + "D_UFP_SEND", + "D_UFP_REJECT", +#endif /* CONFIG_USB_PD_DR_SWAP */ +/******************* PR_SWAP *******************/ +#ifdef CONFIG_USB_PD_PR_SWAP +/* PR_SWAP_SRC */ + "P_SRC_EVA", + "P_SRC_ACCEPT", + "P_SRC_TRANS_OFF", + "P_SRC_ASSERT", + "P_SRC_WAIT_ON", + "P_SRC_SEND", + "P_SRC_REJECT", + +/* PR_SWAP_SNK */ + "P_SNK_EVA", + "P_SNK_ACCEPT", + "P_SNK_TRANS_OFF", + "P_SNK_ASSERT", + "P_SNK_SOURCE_ON", + "P_SNK_SEND", + "P_SNK_REJECT", +/* get same role cap */ + "DR_SRC_GET_CAP", + "DR_SRC_GIVE_CAP", + "DR_SNK_GET_CAP", + "DR_SNK_GIVE_CAP", +/* get same role cap for PD30 */ +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL + "DR_SNK_GIVE_CAP_EXT", +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE + "DR_SRC_GET_CAP_EXT", +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ +#endif /* CONFIG_USB_PD_REV30 */ +#endif /* CONFIG_USB_PD_PR_SWAP */ +/******************* VCONN_SWAP *******************/ +#ifdef CONFIG_USB_PD_VCONN_SWAP + "V_SEND", + "V_EVA", + "V_ACCEPT", + "V_REJECT", + "V_WAIT_VCONN", + "V_TURN_OFF", + "V_TURN_ON", + "V_PS_RDY", +#endif /* CONFIG_USB_PD_VCONN_SWAP */ +/******************* UFP_VDM *******************/ + "U_GET_ID", + "U_GET_SVID", + "U_GET_MODE", + "U_EVA_MODE", + "U_MODE_EX", + "U_ATTENTION", +#ifdef CONFIG_USB_PD_ALT_MODE + "U_D_STATUS", + "U_D_CONFIG", +#endif/* CONFIG_USB_PD_ALT_MODE */ +/******************* DFP_VDM *******************/ + "D_UID_REQ", + "D_UID_A", + "D_UID_N", + "D_CID_REQ", + "D_CID_ACK", + "D_CID_NAK", + "D_SVID_REQ", + "D_SVID_ACK", + "D_SVID_NAK", + "D_MODE_REQ", + "D_MODE_ACK", + "D_MODE_NAK", + "D_MODE_EN_REQ", + "D_MODE_EN_ACK", + "D_MODE_EN_NAK", + "D_MODE_EX_REQ", + "D_MODE_EX_ACK", + "D_ATTENTION", +#ifdef CONFIG_PD_DFP_RESET_CABLE + "D_C_SRESET", + "D_C_CRESET", +#endif /* CONFIG_PD_DFP_RESET_CABLE */ +#ifdef CONFIG_USB_PD_ALT_MODE_DFP + "D_DP_STATUS_REQ", + "D_DP_STATUS_ACK", + "D_DP_STATUS_NAK", + "D_DP_CONFIG_REQ", + "D_DP_CONFIG_ACK", + "D_DP_CONFIG_NAK", +#endif/* CONFIG_USB_PD_ALT_MODE_DFP */ +/******************* UVDM & SVDM *******************/ +#ifdef CONFIG_USB_PD_CUSTOM_VDM + "U_UVDM_RECV", + "D_UVDM_SEND", + "D_UVDM_ACKED", + "D_UVDM_NAKED", +#endif/* CONFIG_USB_PD_CUSTOM_VDM */ + "U_SEND_NAK", +/******************* PD30 Common *******************/ +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE + "GET_BAT_CAP", +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL + "GIVE_BAT_CAP", +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE + "GET_BAT_STATUS", +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL + "GIVE_BAT_STATUS", +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE + "GET_MFRS_INFO", +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL + "GIVE_MFRS_INFO", +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE + "GET_CC", +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL + "GIVE_CC", +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE + "GET_CI", +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL + "GIVE_CI", +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL */ + + "VDM_NO_SUPP", +#endif /* CONFIG_USB_PD_REV30 */ +/******************* Others *******************/ +#ifdef CONFIG_USB_PD_CUSTOM_DBGACC + "DBG_READY", +#endif/* CONFIG_USB_PD_CUSTOM_DBGACC */ +#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER + "OVER_HRESET_LIMIT", +#endif/* CONFIG_USB_PD_RECV_HRESET_COUNTER */ + "REJECT", + "ERR_RECOVERY", +#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE + "ERR_RECOVERY1", +#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */ + "BIST_TD", + "BIST_C2", +/* Wait tx finished */ + "IDLE1", + "IDLE2", +}; +#endif /* PE_STATE_FULL_NAME */ +#endif /* PE_DBG_ENABLE | PE_STATE_INFO_ENABLE */ + +struct pe_state_actions { + void (*entry_action) + (struct pd_port *pd_port); + /* const void (*exit_action) + * (struct pd_port *pd_port, struct pd_event *pd_event); + */ +}; + +#define PE_STATE_ACTIONS(state) { .entry_action = state##_entry, } + +static const struct pe_state_actions pe_state_actions[] = { +/******************* Source *******************/ +#ifdef CONFIG_USB_PD_PE_SOURCE + PE_STATE_ACTIONS(pe_src_startup), + PE_STATE_ACTIONS(pe_src_discovery), + PE_STATE_ACTIONS(pe_src_send_capabilities), + PE_STATE_ACTIONS(pe_src_negotiate_capabilities), + PE_STATE_ACTIONS(pe_src_transition_supply), + PE_STATE_ACTIONS(pe_src_transition_supply2), + PE_STATE_ACTIONS(pe_src_ready), + PE_STATE_ACTIONS(pe_src_disabled), + PE_STATE_ACTIONS(pe_src_capability_response), + PE_STATE_ACTIONS(pe_src_hard_reset), + PE_STATE_ACTIONS(pe_src_hard_reset_received), + PE_STATE_ACTIONS(pe_src_transition_to_default), + PE_STATE_ACTIONS(pe_src_get_sink_cap), + PE_STATE_ACTIONS(pe_src_wait_new_capabilities), + PE_STATE_ACTIONS(pe_src_send_soft_reset), + PE_STATE_ACTIONS(pe_src_soft_reset), +/* Source Startup Discover Cable */ +#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID +#ifdef CONFIG_PD_SRC_RESET_CABLE + PE_STATE_ACTIONS(pe_src_cbl_send_soft_reset), +#endif /* CONFIG_PD_SRC_RESET_CABLE */ + PE_STATE_ACTIONS(pe_src_vdm_identity_request), + PE_STATE_ACTIONS(pe_src_vdm_identity_acked), + PE_STATE_ACTIONS(pe_src_vdm_identity_naked), +#endif /* PD_CAP_PE_SRC_STARTUP_DISCOVER_ID */ +/* Source for PD30 */ +#ifdef CONFIG_USB_PD_REV30 + PE_STATE_ACTIONS(pe_src_send_not_supported), + PE_STATE_ACTIONS(pe_src_not_supported_received), + PE_STATE_ACTIONS(pe_src_chunk_received), +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL + PE_STATE_ACTIONS(pe_src_send_source_alert), +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE + PE_STATE_ACTIONS(pe_src_sink_alert_received), +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL + PE_STATE_ACTIONS(pe_src_give_source_cap_ext), +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL + PE_STATE_ACTIONS(pe_src_give_source_status), +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE + PE_STATE_ACTIONS(pe_src_get_sink_status), +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE + PE_STATE_ACTIONS(pe_src_give_pps_status), +#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */ +#endif /* CONFIG_USB_PD_REV30 */ +#endif /* CONFIG_USB_PD_PE_SOURCE */ +/******************* Sink *******************/ +#ifdef CONFIG_USB_PD_PE_SINK +/* Sink Init */ + PE_STATE_ACTIONS(pe_snk_startup), + PE_STATE_ACTIONS(pe_snk_discovery), + PE_STATE_ACTIONS(pe_snk_wait_for_capabilities), + PE_STATE_ACTIONS(pe_snk_evaluate_capability), + PE_STATE_ACTIONS(pe_snk_select_capability), + PE_STATE_ACTIONS(pe_snk_transition_sink), + PE_STATE_ACTIONS(pe_snk_ready), + PE_STATE_ACTIONS(pe_snk_hard_reset), + PE_STATE_ACTIONS(pe_snk_transition_to_default), + PE_STATE_ACTIONS(pe_snk_give_sink_cap), + PE_STATE_ACTIONS(pe_snk_get_source_cap), + PE_STATE_ACTIONS(pe_snk_send_soft_reset), + PE_STATE_ACTIONS(pe_snk_soft_reset), +/* Sink for PD30 */ +#ifdef CONFIG_USB_PD_REV30 + PE_STATE_ACTIONS(pe_snk_send_not_supported), + PE_STATE_ACTIONS(pe_snk_not_supported_received), + PE_STATE_ACTIONS(pe_snk_chunk_received), +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE + PE_STATE_ACTIONS(pe_snk_source_alert_received), +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL + PE_STATE_ACTIONS(pe_snk_send_sink_alert), +#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE + PE_STATE_ACTIONS(pe_snk_get_source_cap_ext), +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE + PE_STATE_ACTIONS(pe_snk_get_source_status), +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL + PE_STATE_ACTIONS(pe_snk_give_sink_status), +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + PE_STATE_ACTIONS(pe_snk_get_pps_status), +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ +#endif /* CONFIG_USB_PD_REV30 */ +#endif /* CONFIG_USB_PD_PE_SINK */ +/******************* DR_SWAP *******************/ +#ifdef CONFIG_USB_PD_DR_SWAP +/* DR_SWAP_DFP */ + PE_STATE_ACTIONS(pe_drs_dfp_ufp_evaluate_dr_swap), + PE_STATE_ACTIONS(pe_drs_dfp_ufp_accept_dr_swap), + PE_STATE_ACTIONS(pe_drs_dfp_ufp_change_to_ufp), + PE_STATE_ACTIONS(pe_drs_dfp_ufp_send_dr_swap), + PE_STATE_ACTIONS(pe_drs_dfp_ufp_reject_dr_swap), +/* DR_SWAP_UFP */ + PE_STATE_ACTIONS(pe_drs_ufp_dfp_evaluate_dr_swap), + PE_STATE_ACTIONS(pe_drs_ufp_dfp_accept_dr_swap), + PE_STATE_ACTIONS(pe_drs_ufp_dfp_change_to_dfp), + PE_STATE_ACTIONS(pe_drs_ufp_dfp_send_dr_swap), + PE_STATE_ACTIONS(pe_drs_ufp_dfp_reject_dr_swap), +#endif /* CONFIG_USB_PD_DR_SWAP */ +/******************* PR_SWAP *******************/ +#ifdef CONFIG_USB_PD_PR_SWAP +/* PR_SWAP_SRC */ + PE_STATE_ACTIONS(pe_prs_src_snk_evaluate_pr_swap), + PE_STATE_ACTIONS(pe_prs_src_snk_accept_pr_swap), + PE_STATE_ACTIONS(pe_prs_src_snk_transition_to_off), + PE_STATE_ACTIONS(pe_prs_src_snk_assert_rd), + PE_STATE_ACTIONS(pe_prs_src_snk_wait_source_on), + PE_STATE_ACTIONS(pe_prs_src_snk_send_swap), + PE_STATE_ACTIONS(pe_prs_src_snk_reject_pr_swap), + +/* PR_SWAP_SNK */ + PE_STATE_ACTIONS(pe_prs_snk_src_evaluate_pr_swap), + PE_STATE_ACTIONS(pe_prs_snk_src_accept_pr_swap), + PE_STATE_ACTIONS(pe_prs_snk_src_transition_to_off), + PE_STATE_ACTIONS(pe_prs_snk_src_assert_rp), + PE_STATE_ACTIONS(pe_prs_snk_src_source_on), + PE_STATE_ACTIONS(pe_prs_snk_src_send_swap), + PE_STATE_ACTIONS(pe_prs_snk_src_reject_swap), +/* get same role cap */ + PE_STATE_ACTIONS(pe_dr_src_get_source_cap), + PE_STATE_ACTIONS(pe_dr_src_give_sink_cap), + PE_STATE_ACTIONS(pe_dr_snk_get_sink_cap), + PE_STATE_ACTIONS(pe_dr_snk_give_source_cap), +/* get same role cap for PD30 */ +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL + PE_STATE_ACTIONS(pe_dr_snk_give_source_cap_ext), +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE + PE_STATE_ACTIONS(pe_dr_src_get_source_cap_ext), +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ +#endif /* CONFIG_USB_PD_REV30 */ +#endif /* CONFIG_USB_PD_PR_SWAP */ +/******************* VCONN_SWAP *******************/ +#ifdef CONFIG_USB_PD_VCONN_SWAP + PE_STATE_ACTIONS(pe_vcs_send_swap), + PE_STATE_ACTIONS(pe_vcs_evaluate_swap), + PE_STATE_ACTIONS(pe_vcs_accept_swap), + PE_STATE_ACTIONS(pe_vcs_reject_vconn_swap), + PE_STATE_ACTIONS(pe_vcs_wait_for_vconn), + PE_STATE_ACTIONS(pe_vcs_turn_off_vconn), + PE_STATE_ACTIONS(pe_vcs_turn_on_vconn), + PE_STATE_ACTIONS(pe_vcs_send_ps_rdy), +#endif /* CONFIG_USB_PD_VCONN_SWAP */ +/******************* UFP_VDM *******************/ + PE_STATE_ACTIONS(pe_ufp_vdm_get_identity), + PE_STATE_ACTIONS(pe_ufp_vdm_get_svids), + PE_STATE_ACTIONS(pe_ufp_vdm_get_modes), + PE_STATE_ACTIONS(pe_ufp_vdm_evaluate_mode_entry), + PE_STATE_ACTIONS(pe_ufp_vdm_mode_exit), + PE_STATE_ACTIONS(pe_ufp_vdm_attention_request), +#ifdef CONFIG_USB_PD_ALT_MODE + PE_STATE_ACTIONS(pe_ufp_vdm_dp_status_update), + PE_STATE_ACTIONS(pe_ufp_vdm_dp_configure), +#endif/* CONFIG_USB_PD_ALT_MODE */ +/******************* DFP_VDM *******************/ + PE_STATE_ACTIONS(pe_dfp_ufp_vdm_identity_request), + PE_STATE_ACTIONS(pe_dfp_ufp_vdm_identity_acked), + PE_STATE_ACTIONS(pe_dfp_ufp_vdm_identity_naked), + PE_STATE_ACTIONS(pe_dfp_cbl_vdm_identity_request), + PE_STATE_ACTIONS(pe_dfp_cbl_vdm_identity_acked), + PE_STATE_ACTIONS(pe_dfp_cbl_vdm_identity_naked), + PE_STATE_ACTIONS(pe_dfp_vdm_svids_request), + PE_STATE_ACTIONS(pe_dfp_vdm_svids_acked), + PE_STATE_ACTIONS(pe_dfp_vdm_svids_naked), + PE_STATE_ACTIONS(pe_dfp_vdm_modes_request), + PE_STATE_ACTIONS(pe_dfp_vdm_modes_acked), + PE_STATE_ACTIONS(pe_dfp_vdm_modes_naked), + PE_STATE_ACTIONS(pe_dfp_vdm_mode_entry_request), + PE_STATE_ACTIONS(pe_dfp_vdm_mode_entry_acked), + PE_STATE_ACTIONS(pe_dfp_vdm_mode_entry_naked), + PE_STATE_ACTIONS(pe_dfp_vdm_mode_exit_request), + PE_STATE_ACTIONS(pe_dfp_vdm_mode_exit_acked), + PE_STATE_ACTIONS(pe_dfp_vdm_attention_request), +#ifdef CONFIG_PD_DFP_RESET_CABLE + PE_STATE_ACTIONS(pe_dfp_cbl_send_soft_reset), + PE_STATE_ACTIONS(pe_dfp_cbl_send_cable_reset), +#endif /* CONFIG_PD_DFP_RESET_CABLE */ +#ifdef CONFIG_USB_PD_ALT_MODE_DFP + PE_STATE_ACTIONS(pe_dfp_vdm_dp_status_update_request), + PE_STATE_ACTIONS(pe_dfp_vdm_dp_status_update_acked), + PE_STATE_ACTIONS(pe_dfp_vdm_dp_status_update_naked), + PE_STATE_ACTIONS(pe_dfp_vdm_dp_configuration_request), + PE_STATE_ACTIONS(pe_dfp_vdm_dp_configuration_acked), + PE_STATE_ACTIONS(pe_dfp_vdm_dp_configuration_naked), +#endif/* CONFIG_USB_PD_ALT_MODE_DFP */ +/******************* UVDM & SVDM *******************/ +#ifdef CONFIG_USB_PD_CUSTOM_VDM + PE_STATE_ACTIONS(pe_ufp_uvdm_recv), + PE_STATE_ACTIONS(pe_dfp_uvdm_send), + PE_STATE_ACTIONS(pe_dfp_uvdm_acked), + PE_STATE_ACTIONS(pe_dfp_uvdm_naked), +#endif/* CONFIG_USB_PD_CUSTOM_VDM */ + PE_STATE_ACTIONS(pe_ufp_vdm_send_nak), +/******************* PD30 Common *******************/ +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE + PE_STATE_ACTIONS(pe_get_battery_cap), +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL + PE_STATE_ACTIONS(pe_give_battery_cap), +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE + PE_STATE_ACTIONS(pe_get_battery_status), +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL + PE_STATE_ACTIONS(pe_give_battery_status), +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE + PE_STATE_ACTIONS(pe_get_manufacturer_info), +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL + PE_STATE_ACTIONS(pe_give_manufacturer_info), +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE + PE_STATE_ACTIONS(pe_get_country_codes), +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL + PE_STATE_ACTIONS(pe_give_country_codes), +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE + PE_STATE_ACTIONS(pe_get_country_info), +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */ +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL + PE_STATE_ACTIONS(pe_give_country_info), +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL */ + PE_STATE_ACTIONS(pe_vdm_not_supported), +#endif /* CONFIG_USB_PD_REV30 */ +/******************* Others *******************/ +#ifdef CONFIG_USB_PD_CUSTOM_DBGACC + PE_STATE_ACTIONS(pe_dbg_ready), +#endif/* CONFIG_USB_PD_CUSTOM_DBGACC */ +#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER + PE_STATE_ACTIONS(pe_over_recv_hreset_limit), +#endif/* CONFIG_USB_PD_RECV_HRESET_COUNTER */ + PE_STATE_ACTIONS(pe_reject), + PE_STATE_ACTIONS(pe_error_recovery), +#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE + PE_STATE_ACTIONS(pe_error_recovery_once), +#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */ + PE_STATE_ACTIONS(pe_bist_test_data), + PE_STATE_ACTIONS(pe_bist_carrier_mode_2), +/* Wait tx finished */ + PE_STATE_ACTIONS(pe_idle1), + PE_STATE_ACTIONS(pe_idle2), +}; + +/* pd_state_action_fcn_t pe_get_exit_action(uint8_t pe_state) */ +void (*pe_get_exit_action(uint8_t pe_state)) + (struct pd_port *) +{ + void (*retval)(struct pd_port *) = NULL; + + switch (pe_state) { +/******************* Source *******************/ +#ifdef CONFIG_USB_PD_PE_SOURCE + case PE_SRC_TRANSITION_TO_DEFAULT: + retval = pe_src_transition_to_default_exit; + break; + case PE_SRC_GET_SINK_CAP: + retval = pe_src_get_sink_cap_exit; + break; +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE + case PE_SRC_GET_SINK_STATUS: + retval = pe_src_get_sink_status_exit; + break; +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ +#endif /* CONFIG_USB_PD_REV30 */ +#endif /* CONFIG_USB_PD_PE_SOURCE */ + +/******************* Sink *******************/ +#ifdef CONFIG_USB_PD_PE_SINK + case PE_SNK_SELECT_CAPABILITY: + retval = pe_snk_select_capability_exit; + break; + +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE + case PE_SNK_GET_SOURCE_CAP_EXT: + retval = pe_snk_get_source_cap_ext_exit; + break; +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE + case PE_SNK_GET_SOURCE_STATUS: + retval = pe_snk_get_source_status_exit; + break; +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + case PE_SNK_GET_PPS_STATUS: + retval = pe_snk_get_pps_status_exit; + break; +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + +#endif /* CONFIG_USB_PD_REV30 */ +#endif /* CONFIG_USB_PD_PE_SINK */ + +/******************* PR_SWAP *******************/ +#ifdef CONFIG_USB_PD_PR_SWAP + case PE_DR_SRC_GET_SOURCE_CAP: + retval = pe_dr_src_get_source_cap_exit; + break; + + case PE_DR_SNK_GET_SINK_CAP: + retval = pe_dr_snk_get_sink_cap_exit; + break; + +/* get same role cap for PD30 */ +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE + case PE_DR_SRC_GET_SOURCE_CAP_EXT: + retval = pe_dr_src_get_source_cap_ext_exit; + break; +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ +#endif /* CONFIG_USB_PD_REV30 */ +#endif /* CONFIG_USB_PD_PR_SWAP */ + +/******************* PD30 Common *******************/ +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE + case PE_GET_BATTERY_CAP: + retval = pe_get_battery_cap_exit; + break; +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE + case PE_GET_BATTERY_STATUS: + retval = pe_get_battery_status_exit; + break; +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE + case PE_GET_MANUFACTURER_INFO: + retval = pe_get_manufacturer_info_exit; + break; +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */ + + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE + case PE_GET_COUNTRY_CODES: + retval = pe_get_country_codes_exit; + break; +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE + case PE_GET_COUNTRY_INFO: + retval = pe_get_country_info_exit; + break; +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */ + +#endif /* CONFIG_USB_PD_REV30 */ + case PE_BIST_TEST_DATA: + retval = pe_bist_test_data_exit; + break; + case PE_BIST_CARRIER_MODE_2: + retval = pe_bist_carrier_mode_2_exit; + break; + default: + break; + } + + return retval; +} + +static inline void print_state( + struct pd_port *pd_port, uint8_t state) +{ + /* + * Source (P, Provider), Sink (C, Consumer) + * DFP (D), UFP (U) + * Vconn Source (Y/N) + */ + bool __maybe_unused vdm_evt = pd_curr_is_vdm_evt(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + +#if PE_DBG_ENABLE + PE_DBG("%s -> %s (%c%c%c)\n", + vdm_evt ? "VDM" : "PD", pe_state_name[state], + pd_port->power_role ? 'P' : 'C', + pd_port->data_role ? 'D' : 'U', + pd_port->vconn_role ? 'Y' : 'N'); +#else + PE_STATE_INFO("%s-> %s\n", + vdm_evt ? "VDM" : "PD", pe_state_name[state]); +#endif /* PE_DBG_ENABLE */ +} + +static void pe_reset_vdm_state_variable( + struct pd_port *pd_port, struct pe_data *pe_data) +{ + if (pe_data->vdm_state_timer) + pd_disable_timer(pd_port, pe_data->vdm_state_timer); + + pe_data->vdm_state_flags = 0; + pe_data->vdm_state_timer = 0; +} + +static inline void pd_pe_state_change( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + void (*prev_exit_action)(struct pd_port *pd_port); + void (*next_entry_action)(struct pd_port *pd_port); + struct pe_data *pe_data = &pd_port->pe_data; + + uint8_t old_state = pd_port->pe_state_curr; + uint8_t new_state = pd_port->pe_state_next; + + if (old_state >= PD_NR_PE_STATES || new_state >= PD_NR_PE_STATES) { + PD_BUG_ON(1); + return; + } + + if (new_state < PE_IDLE1) + prev_exit_action = pe_get_exit_action(old_state); + else + prev_exit_action = NULL; + + next_entry_action = pe_state_actions[new_state].entry_action; + +#if PE_STATE_INFO_VDM_DIS + if (!pd_curr_is_vdm_evt(pd_port)) +#endif /* PE_STATE_INFO_VDM_DIS */ + print_state(pd_port, new_state); + + if (pe_data->pe_state_flags & + PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER) { + pd_disable_timer(pd_port, PD_TIMER_SENDER_RESPONSE); + } + + if (pd_curr_is_vdm_evt(pd_port)) + pe_reset_vdm_state_variable(pd_port, pe_data); + else if (pe_data->pe_state_timer) { + pd_disable_timer(pd_port, pe_data->pe_state_timer); + pe_data->pe_state_timer = 0; + } + + pe_data->pe_state_flags = 0; + pe_data->pe_state_flags2 = 0; + + if (prev_exit_action) + prev_exit_action(pd_port); + + if (next_entry_action) + next_entry_action(pd_port); + + if (pd_curr_is_vdm_evt(pd_port)) + pd_port->pe_vdm_state = new_state; + else + pd_port->pe_pd_state = new_state; + + pd_port->pe_state_curr = new_state; + + /* Change RX cap first for compliance */ + if (pd_port->state_machine > PE_STATE_MACHINE_NORMAL) + pd_set_rx_enable(pd_port, PD_RX_CAP_PE_SWAP); +} + +static int pd_handle_event( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + bool dpm_imme; + struct pe_data *pe_data = &pd_port->pe_data; + + if (pd_curr_is_vdm_evt(pd_port)) { + dpm_imme = pd_port->pe_data.vdm_state_flags + & VDM_STATE_FLAG_DPM_ACK_IMMEDIATELY; + if ((pe_data->reset_vdm_state && (!dpm_imme)) || + (pd_event->event_type == PD_EVT_TCP_MSG)) { + pe_data->reset_vdm_state = false; + pd_port->pe_vdm_state = pd_port->pe_pd_state; + pe_reset_vdm_state_variable(pd_port, pe_data); + } + + pd_port->pe_state_curr = pd_port->pe_vdm_state; + } else { + pd_port->pe_state_curr = pd_port->pe_pd_state; + } + + if (pd_process_event(pd_port, pd_event)) + pd_pe_state_change(pd_port, pd_event); + + pd_free_event(pd_port->tcpc, pd_event); + return 1; +} + +/* + * Get Next Event + */ + +enum PE_NEW_EVT_TYPE { + PE_NEW_EVT_NULL = 0, + PE_NEW_EVT_PD = 1, + PE_NEW_EVT_VDM = 2, +}; + +static inline bool pd_try_get_vdm_event( + struct tcpc_device *tcpc, struct pd_event *pd_event) +{ + bool ret = false; + struct pd_port *pd_port = &tcpc->pd_port; + int rv = 0; + uint32_t chip_id; + rv = tcpci_get_chip_id(tcpc, &chip_id); + + switch (pd_port->pe_pd_state) { +#ifdef CONFIG_USB_PD_PE_SINK + case PE_SNK_TRANSITION_SINK: + if (!rv && SC2150A_DID == chip_id) { + ret = pd_get_vdm_event(tcpc, pd_event); + } + break; + case PE_SNK_READY: + ret = pd_get_vdm_event(tcpc, pd_event); + break; +#endif /* CONFIG_USB_PD_PE_SINK */ + +#ifdef CONFIG_USB_PD_PE_SOURCE + case PE_SRC_READY: + ret = pd_get_vdm_event(tcpc, pd_event); + break; + case PE_SRC_STARTUP: + ret = pd_get_vdm_event(tcpc, pd_event); + break; + case PE_SRC_DISCOVERY: + ret = pd_get_vdm_event(tcpc, pd_event); + break; + +#ifdef CONFIG_PD_SRC_RESET_CABLE + case PE_SRC_CBL_SEND_SOFT_RESET: + ret = pd_get_vdm_event(tcpc, pd_event); + break; +#endif /* CONFIG_PD_SRC_RESET_CABLE */ +#endif /* CONFIG_USB_PD_PE_SOURCE */ + +#ifdef CONFIG_USB_PD_CUSTOM_DBGACC + case PE_DBG_READY: + ret = pd_get_vdm_event(tcpc, pd_event); + break; +#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */ + case PE_IDLE1: + ret = pd_get_vdm_event(tcpc, pd_event); + break; + default: + break; + } + + return ret; +} + +#ifdef CONFIG_USB_PD_REV30 + +static inline bool pd_check_sink_tx_ok(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + if (pd_check_rev30(pd_port) && + (pd_port->pe_data.pd_traffic_control != PD_SINK_TX_OK)) + return false; +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ + + return true; +} + +static inline bool pd_check_source_tx_ok(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + if (!pd_check_rev30(pd_port)) + return true; + + if (pd_port->pe_data.pd_traffic_control == PD_SOURCE_TX_OK) + return true; + + if (!pd_port->pe_data.pd_traffic_idle) + pd_set_sink_tx(pd_port, PD30_SINK_TX_NG); + + return false; +#else + return true; +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ +} + +static inline bool pd_check_pd30_tx_ready(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_PE_SINK + if (pd_port->pe_pd_state == PE_SNK_READY) + return pd_check_sink_tx_ok(pd_port); +#endif /* CONFIG_USB_PD_PE_SINK */ + +#ifdef CONFIG_USB_PD_PE_SOURCE + if (pd_port->pe_pd_state == PE_SRC_READY) + return pd_check_source_tx_ok(pd_port); +#endif /* CONFIG_USB_PD_PE_SOURCE */ + +#ifdef CONFIG_USB_PD_CUSTOM_DBGACC + if (pd_port->pe_pd_state == PE_DBG_READY) + return true; +#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */ + + return false; +} +#else +static inline bool pd_check_pd20_tx_ready(struct pd_port *pd_port) +{ + switch (pd_port->pe_pd_state) { +#ifdef CONFIG_USB_PD_PE_SINK + case PE_SNK_READY: + return true; +#endif /* CONFIG_USB_PD_PE_SINK */ + +#ifdef CONFIG_USB_PD_PE_SOURCE + case PE_SRC_READY: + return true; +#endif /* CONFIG_USB_PD_PE_SOURCE */ + +#ifdef CONFIG_USB_PD_CUSTOM_DBGACC + case PE_DBG_READY: + return true; +#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */ + + default: + return false; + } +} +#endif /* ndef CONFIG_USB_PD_REV30 */ + +/** + * pd_check_tx_ready + * + * Check PE is ready to initiate an active event (AMS). + * + * For revision 2, checking the PE state is in Ready. + * For revision 3, checking SinkTx besides above description. + * + * If PR=SRC and CC=SinkTxOK, change CC to SinkTxNG; + * + * Returns a boolean value to present PE is available or not. + */ + +static inline bool pd_check_tx_ready(struct pd_port *pd_port) +{ + /* VDM BUSY : Waiting for response */ + if (pd_port->pe_data.vdm_state_timer) + return false; + +#ifdef CONFIG_USB_PD_REV30 + return pd_check_pd30_tx_ready(pd_port); +#else + return pd_check_pd20_tx_ready(pd_port); +#endif /* CONFIG_USB_PD_REV30 */ +} + +/** + * pd_try_get_deferred_tcp_event + * + * Get a pending TCPM event from the event queue + * + * Returns TCP_DPM_EVT_ID if succeeded, + * otherwise returns DPM_READY_REACTION_BUSY. + */ + +static inline uint8_t pd_try_get_deferred_tcp_event(struct pd_port *pd_port) +{ + if (!pd_get_deferred_tcp_event( + pd_port->tcpc, &pd_port->tcp_event)) + return DPM_READY_REACTION_BUSY; + +#ifdef CONFIG_USB_PD_TCPM_CB_2ND + pd_port->tcp_event_drop_reset_once = true; +#endif /* CONFIG_USB_PD_TCPM_CB_2ND */ + + pd_port->tcp_event_id_1st = pd_port->tcp_event.event_id; + return pd_port->tcp_event_id_1st; +} + +/** + * pd_try_get_active_event + * + * Get a pending active event if TX is available + * + * Event Priority : + * DPM reactions, TCPM request. + * + * Returns PE_NEW_EVT_TYPE. + */ + +static inline uint8_t pd_try_get_active_event( + struct tcpc_device *tcpc, struct pd_event *pd_event) +{ + uint8_t ret; + uint8_t from_pe = PD_TCP_FROM_PE; + struct pd_port *pd_port = &tcpc->pd_port; + + if (!pd_check_tx_ready(pd_port)) + return PE_NEW_EVT_NULL; + + ret = pd_dpm_get_ready_reaction(pd_port); + + if (ret == 0) { + from_pe = PD_TCP_FROM_TCPM; + ret = pd_try_get_deferred_tcp_event(pd_port); + } + +#if DPM_DBG_ENABLE + if ((ret != 0) && (ret != DPM_READY_REACTION_BUSY)) { + DPM_DBG("from_pe: %d, evt:%d, reaction:0x%x\n", + from_pe, ret, pd_port->pe_data.dpm_reaction_id); + } +#endif /* DPM_DBG_ENABLE */ + + if (ret == DPM_READY_REACTION_BUSY) + return PE_NEW_EVT_NULL; + + pd_event->event_type = PD_EVT_TCP_MSG; + pd_event->msg = ret; + pd_event->msg_sec = from_pe; + pd_event->pd_msg = NULL; + + if (ret >= TCP_DPM_EVT_VDM_COMMAND) + return PE_NEW_EVT_VDM; + + return PE_NEW_EVT_PD; +} + +/** + * pd_try_get_next_event + * + * Get a pending event + * + * Event Priority : + * PD state machine's event, VDM state machine's event, + * Active event (DPM reactions, TCPM request) + * + * Returns PE_NEW_EVT_TYPE. + */ + +static inline uint8_t pd_try_get_next_event( + struct tcpc_device *tcpc, struct pd_event *pd_event) +{ + uint8_t ret = 0; + struct pd_port *pd_port = &tcpc->pd_port; + + if (pd_get_event(tcpc, pd_event)) + return PE_NEW_EVT_PD; + + if (pd_try_get_vdm_event(tcpc, pd_event)) + return PE_NEW_EVT_VDM; + + mutex_lock(&pd_port->pd_lock); + ret = pd_try_get_active_event(tcpc, pd_event); + mutex_unlock(&pd_port->pd_lock); + + return ret; +} + +/* + * Richtek Policy Engine + */ + +static inline int pd_handle_dpm_immediately( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + bool dpm_immediately; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_curr_is_vdm_evt(pd_port)) { + dpm_immediately = pd_port->pe_data.vdm_state_flags + & VDM_STATE_FLAG_DPM_ACK_IMMEDIATELY; + } else { + dpm_immediately = pd_port->pe_data.pe_state_flags2 + & PE_STATE_FLAG_DPM_ACK_IMMEDIATELY; + } + + if (dpm_immediately) { + PE_DBG("DPM_Immediately\n"); + pd_event->event_type = PD_EVT_DPM_MSG; + pd_event->msg = PD_DPM_ACK; + return pd_handle_event(pd_port, pd_event); + } + + return false; +} + +int pd_policy_engine_run(struct tcpc_device *tcpc) +{ + bool loop = true; + uint8_t ret; + struct pd_port *pd_port = &tcpc->pd_port; + struct pd_event *pd_event = pd_get_curr_pd_event(pd_port); + + ret = pd_try_get_next_event(tcpc, pd_event); + if (ret == PE_NEW_EVT_NULL) { + loop = false; + goto out; + } + + mutex_lock(&pd_port->pd_lock); + + pd_port->curr_is_vdm_evt = (ret == PE_NEW_EVT_VDM); + + pd_handle_event(pd_port, pd_event); + pd_handle_dpm_immediately(pd_port, pd_event); + + mutex_unlock(&pd_port->pd_lock); +out: + return loop; +} diff --git a/drivers/usb/typec/mmi_tcpc/pd_policy_engine_com.c b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_com.c new file mode 100644 index 000000000000..eec3c499ab96 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_com.c @@ -0,0 +1,373 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Policy Engine for Common + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/pd_dpm_core.h" +#include "inc/tcpci.h" +#include "inc/pd_policy_engine.h" + +static inline void *pd_get_tcp_event_data_object(struct pd_port *pd_port) +{ + return pd_port->tcp_event.tcp_dpm_data.data_object; +} + +/* + * Policy Engine General State Activity + */ + +static void pe_idle_reset_data(struct pd_port *pd_port) +{ + pd_reset_pe_timer(pd_port); + pd_reset_svid_data(pd_port); + + pd_port->pe_data.pd_prev_connected = false; + pd_port->state_machine = PE_STATE_MACHINE_IDLE; + + pd_enable_bist_test_mode(pd_port, false); + +#ifndef CONFIG_USB_PD_TRANSMIT_BIST2 + pd_disable_bist_mode2(pd_port); +#endif /* CONFIG_USB_PD_TRANSMIT_BIST2 */ + + pd_enable_timer(pd_port, PD_TIMER_PE_IDLE_TOUT); + + pd_unlock_msg_output(pd_port); +} + +void pe_idle1_entry(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE + pd_port->error_recovery_once = 0; +#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */ + + pe_idle_reset_data(pd_port); + pd_try_put_pe_idle_event(pd_port); +} + +void pe_idle2_entry(struct pd_port *pd_port) +{ + memset(&pd_port->pe_data, 0, sizeof(struct pe_data)); + pd_set_rx_enable(pd_port, PD_RX_CAP_PE_IDLE); + pd_disable_timer(pd_port, PD_TIMER_PE_IDLE_TOUT); + pd_notify_pe_idle(pd_port); +} + +void pe_reject_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_REJECT); +} + +void pe_error_recovery_entry(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE + pd_port->error_recovery_once++; +#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */ + + pe_idle_reset_data(pd_port); + pd_notify_pe_error_recovery(pd_port); + pd_try_put_pe_idle_event(pd_port); +} + +#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE +void pe_error_recovery_once_entry(struct pd_port *pd_port) +{ + uint8_t state = PD_CONNECT_TYPEC_ONLY_SNK_DFT; + + pd_notify_pe_hard_reset_completed(pd_port); + + if (pd_port->power_role == PD_ROLE_SOURCE) { + state = PD_CONNECT_TYPEC_ONLY_SRC; + pd_dpm_dynamic_disable_vconn(pd_port); + } else + pd_dpm_sink_vbus(pd_port, true); + + pd_update_connect_state(pd_port, state); +} +#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */ + +#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER +void pe_over_recv_hreset_limit_entry(struct pd_port *pd_port) +{ + PE_INFO("OverHResetLimit++\n"); + pe_idle_reset_data(pd_port); + pd_notify_pe_over_recv_hreset(pd_port); + PE_INFO("OverHResetLimit--\n"); +} +#endif /* CONFIG_USB_PD_RECV_HRESET_COUNTER */ + +void pe_bist_test_data_entry(struct pd_port *pd_port) +{ + PE_STATE_IGNORE_UNKNOWN_EVENT(pd_port); + + pd_enable_bist_test_mode(pd_port, true); +} + +void pe_bist_test_data_exit(struct pd_port *pd_port) +{ + pd_enable_bist_test_mode(pd_port, false); +} + +void pe_bist_carrier_mode_2_entry(struct pd_port *pd_port) +{ + pd_send_bist_mode2(pd_port); + pd_enable_pe_state_timer(pd_port, PD_TIMER_BIST_CONT_MODE); +} + +void pe_bist_carrier_mode_2_exit(struct pd_port *pd_port) +{ + pd_disable_bist_mode2(pd_port); +} + +/* + * Policy Engine Share State Activity + */ + +static inline uint8_t pe30_power_ready_entry(struct pd_port *pd_port) +{ + uint8_t rx_cap = PD_RX_CAP_PE_READY_UFP; + + if (pd_port->vconn_role) + rx_cap = PD_RX_CAP_PE_READY_DFP; + +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + dpm_reaction_clear(pd_port, + DPM_REACTION_DFP_FLOW_DELAY | + DPM_REACTION_UFP_FLOW_DELAY); +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ + + return rx_cap; +} + +static inline uint8_t pe20_power_ready_entry(struct pd_port *pd_port) +{ + uint8_t rx_cap = PD_RX_CAP_PE_READY_UFP; + + if (pd_port->data_role == PD_ROLE_DFP) + rx_cap = PD_RX_CAP_PE_READY_DFP; + + return rx_cap; +} + +void pe_power_ready_entry(struct pd_port *pd_port) +{ + uint8_t rx_cap; + + pd_port->state_machine = PE_STATE_MACHINE_NORMAL; + + pd_port->pe_data.during_swap = false; + pd_port->pe_data.explicit_contract = true; + +#ifdef CONFIG_USB_PD_RENEGOTIATION_COUNTER + pd_port->pe_data.renegotiation_count = 0; +#endif /* CONFIG_USB_PD_RENEGOTIATION_COUNTER */ + + if (pd_check_rev30(pd_port)) + rx_cap = pe30_power_ready_entry(pd_port); + else + rx_cap = pe20_power_ready_entry(pd_port); + + pd_set_rx_enable(pd_port, rx_cap); + + dpm_reaction_set(pd_port, DPM_REACTION_CAP_READY_ONCE); + pd_notify_tcp_event_2nd_result(pd_port, TCP_DPM_RET_SUCCESS); +} + +#ifdef CONFIG_USB_PD_REV30 + +/* + * [PD3.0] Figure 8-85 Get Battery Capabilities State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE +void pe_get_battery_cap_entry(struct pd_port *pd_port) +{ + struct pd_get_battery_capabilities *gbcdb = + pd_get_tcp_event_data_object(pd_port); + + PE_STATE_WAIT_MSG(pd_port); + pd_send_sop_ext_msg(pd_port, + PD_EXT_GET_BAT_CAP, PD_GBCDB_SIZE, gbcdb); +} + +void pe_get_battery_cap_exit(struct pd_port *pd_port) +{ + pd_dpm_inform_battery_cap(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */ + +/* + * [PD3.0] Figure 8-86 Give Battery Capabilities State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL +void pe_give_battery_cap_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + + pd_dpm_send_battery_cap(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */ + +/* + * [PD3.0] Figure 8-87 Get Battery Status State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE +void pe_get_battery_status_entry(struct pd_port *pd_port) +{ + struct pd_get_battery_status *gbsdb = + pd_get_tcp_event_data_object(pd_port); + + PE_STATE_WAIT_MSG(pd_port); + pd_send_sop_ext_msg(pd_port, + PD_EXT_GET_BAT_STATUS, PD_GBSDB_SIZE, gbsdb); +} + +void pe_get_battery_status_exit(struct pd_port *pd_port) +{ + pd_dpm_inform_battery_status(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */ + +/* + * [PD3.0] Figure 8-88 Give Battery Status State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL +void pe_give_battery_status_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + + pd_dpm_send_battery_status(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */ + +/* + * [PD3.0] Figure 8-89 Get Manufacturer Information State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE + +void pe_get_manufacturer_info_entry(struct pd_port *pd_port) +{ + struct pd_get_manufacturer_info *gmidb = + pd_get_tcp_event_data_object(pd_port); + + PE_STATE_WAIT_MSG(pd_port); + pd_send_sop_ext_msg(pd_port, + PD_EXT_GET_MFR_INFO, PD_GMIDB_SIZE, gmidb); +} + +void pe_get_manufacturer_info_exit(struct pd_port *pd_port) +{ + pd_dpm_inform_mfrs_info(pd_port); +} + +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */ + +/* + * [PD3.0] Figure 8-90 Give Manufacturer Information State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL + +void pe_give_manufacturer_info_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + + pd_dpm_send_mfrs_info(pd_port); +} + +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */ + +/* + * [PD3.0] Figure 8-91 Get Country Codes State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE +void pe_get_country_codes_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_MSG(pd_port); + + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_COUNTRY_CODE); +} + +void pe_get_country_codes_exit(struct pd_port *pd_port) +{ + pd_dpm_inform_country_codes(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */ + +/* + * [PD3.0] Figure 8-92 Give Country Codes State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL +void pe_give_country_codes_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + + pd_dpm_send_country_codes(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */ + +/* + * [PD3.0] Figure 8-93 Get Country Information State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE +void pe_get_country_info_entry(struct pd_port *pd_port) +{ + uint32_t *ccdo = + pd_get_tcp_event_data_object(pd_port); + + PE_STATE_WAIT_MSG(pd_port); + pd_send_sop_data_msg(pd_port, + PD_DATA_GET_COUNTRY_INFO, PD_CCDO_SIZE, ccdo); +} + +void pe_get_country_info_exit(struct pd_port *pd_port) +{ + pd_dpm_inform_country_info(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */ + +/* + * [PD3.0] Figure 8-94 Give Country Information State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL +void pe_give_country_info_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + + pd_dpm_send_country_info(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_LOCAL */ + +/* + * [PD3.0] Unsupported, Unrecognized UVDM and Unsupported SVDM. + */ + +void pe_vdm_not_supported_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_NOT_SUPPORTED); +} + +#endif /* CONFIG_USB_PD_REV30 */ diff --git a/drivers/usb/typec/mmi_tcpc/pd_policy_engine_dbg.c b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_dbg.c new file mode 100644 index 000000000000..fa9abce905f4 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_dbg.c @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Policy Engine for DBGACC + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/pd_dpm_core.h" +#include "inc/tcpci.h" +#include "inc/pd_policy_engine.h" + +#ifdef CONFIG_USB_PD_CUSTOM_DBGACC + +void pe_dbg_ready_entry(struct pd_port *pd_port) +{ + uint8_t state; + + if (pd_port->pe_data.pe_ready) + return; + + pd_port->pe_data.pe_ready = true; + pd_reset_protocol_layer(pd_port, false); + + if (pd_port->data_role == PD_ROLE_UFP) { + PE_INFO("Custom_DBGACC : UFP\n"); + state = PD_CONNECT_PE_READY_DBGACC_UFP; + pd_set_rx_enable(pd_port, PD_RX_CAP_PE_READY_UFP); + } else { + PE_INFO("Custom_DBGACC : DFP\n"); + state = PD_CONNECT_PE_READY_DBGACC_DFP; + pd_set_rx_enable(pd_port, PD_RX_CAP_PE_READY_DFP); + } + + pd_update_connect_state(pd_port, state); +} + +#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */ diff --git a/drivers/usb/typec/mmi_tcpc/pd_policy_engine_dfp.c b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_dfp.c new file mode 100644 index 000000000000..caedc29c105e --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_dfp.c @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Policy Engine for DFP + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/pd_dpm_core.h" +#include "inc/tcpci.h" +#include "inc/pd_policy_engine.h" + +/* + * [PD2.0] Figure 8-64 DFP to UFP VDM Discover Identity State Diagram + */ + +void pe_dfp_ufp_vdm_identity_request_entry(struct pd_port *pd_port) +{ + pd_send_vdm_discover_id(pd_port, TCPC_TX_SOP); +} + +void pe_dfp_ufp_vdm_identity_acked_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_inform_id(pd_port, true); +} + +void pe_dfp_ufp_vdm_identity_naked_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_inform_id(pd_port, false); +} + +/* + * [PD2.0] Figure 8-65 DFP VDM Discover Identity State Diagram + */ + +void pe_dfp_cbl_vdm_identity_request_entry(struct pd_port *pd_port) +{ + pd_port->pe_data.discover_id_counter++; + pd_send_vdm_discover_id(pd_port, TCPC_TX_SOP_PRIME); +} + +void pe_dfp_cbl_vdm_identity_acked_entry(struct pd_port *pd_port) +{ + pd_dpm_inform_cable_id(pd_port, false); +} + +void pe_dfp_cbl_vdm_identity_naked_entry(struct pd_port *pd_port) +{ + pd_dpm_inform_cable_id(pd_port, false); +} + +/* + * [PD2.0] Figure 8-66 DFP VDM Discover SVIDs State Diagram + */ + +void pe_dfp_vdm_svids_request_entry(struct pd_port *pd_port) +{ + pd_send_vdm_discover_svids(pd_port, TCPC_TX_SOP); +} + +void pe_dfp_vdm_svids_acked_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_inform_svids(pd_port, true); +} + +void pe_dfp_vdm_svids_naked_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_inform_svids(pd_port, false); +} + +/* + * [PD2.0] Figure 8-67 DFP VDM Discover Modes State Diagram + */ + +void pe_dfp_vdm_modes_request_entry(struct pd_port *pd_port) +{ + pd_send_vdm_discover_modes(pd_port, TCPC_TX_SOP, pd_port->mode_svid); +} + +void pe_dfp_vdm_modes_acked_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_inform_modes(pd_port, true); +} + +void pe_dfp_vdm_modes_naked_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_inform_modes(pd_port, false); +} + +/* + * [PD2.0] Figure 8-68 DFP VDM Mode Entry State Diagram + */ + +void pe_dfp_vdm_mode_entry_request_entry(struct pd_port *pd_port) +{ + pd_send_vdm_enter_mode(pd_port, TCPC_TX_SOP, + pd_port->mode_svid, pd_port->mode_obj_pos); +} + +void pe_dfp_vdm_mode_entry_acked_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_inform_enter_mode(pd_port, true); +} + +void pe_dfp_vdm_mode_entry_naked_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_inform_enter_mode(pd_port, false); +} + +/* + * [PD2.0] Figure 8-69 DFP VDM Mode Exit State Diagram + */ + +void pe_dfp_vdm_mode_exit_request_entry(struct pd_port *pd_port) +{ + pd_send_vdm_exit_mode(pd_port, TCPC_TX_SOP, + pd_port->mode_svid, pd_port->mode_obj_pos); +} + +void pe_dfp_vdm_mode_exit_acked_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_inform_exit_mode(pd_port); +} + +/* + * [PD2.0] Figure 8-70 DFP VDM Attention State Diagram + */ + +void pe_dfp_vdm_attention_request_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_inform_attention(pd_port); +} + +/* + * [PD2.0] Figure 8-83 DFP Cable Soft Reset or Cable Reset State Diagram + */ + +#ifdef CONFIG_PD_DFP_RESET_CABLE + +void pe_dfp_cbl_send_soft_reset_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_MSG_OR_TX_FAILED(pd_port); + + pd_send_cable_soft_reset(pd_port); +} + +void pe_dfp_cbl_send_cable_reset_entry(struct pd_port *pd_port) +{ + /* TODO : we don't do cable reset now */ +} + +#endif /* CONFIG_PD_DFP_RESET_CABLE */ + +/* + * [PD2.0] Display Port + */ + +#ifdef CONFIG_USB_PD_ALT_MODE_DFP + +void pe_dfp_vdm_dp_status_update_request_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_send_dp_status_update(pd_port); +} + +void pe_dfp_vdm_dp_status_update_acked_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_inform_dp_status_update(pd_port, true); +} + +void pe_dfp_vdm_dp_status_update_naked_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_inform_dp_status_update(pd_port, false); +} + +void pe_dfp_vdm_dp_configuration_request_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_send_dp_configuration(pd_port); +} + +void pe_dfp_vdm_dp_configuration_acked_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_inform_dp_configuration(pd_port, true); +} + +void pe_dfp_vdm_dp_configuration_naked_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_inform_dp_configuration(pd_port, false); +} + +#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ + +/* + * UVDM + */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM + +void pe_dfp_uvdm_send_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_send_uvdm(pd_port); +} + +void pe_dfp_uvdm_acked_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_inform_uvdm(pd_port, true); +} + +void pe_dfp_uvdm_naked_entry(struct pd_port *pd_port) +{ + pd_dpm_dfp_inform_uvdm(pd_port, false); +} + +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ diff --git a/drivers/usb/typec/mmi_tcpc/pd_policy_engine_dr.c b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_dr.c new file mode 100644 index 000000000000..c9d305ddf64c --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_dr.c @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Policy Engine for DR + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/pd_dpm_core.h" +#include "inc/tcpci.h" +#include "inc/pd_policy_engine.h" + +/* + * [PD2.0] + * Figure 8-53 Dual-Role (Source) Get Source Capabilities diagram + * Figure 8-54 Dual-Role (Source) Give Sink Capabilities diagram + * Figure 8-55 Dual-Role (Sink) Get Sink Capabilities State Diagram + * Figure 8-56 Dual-Role (Sink) Give Source Capabilities State Diagram + */ + +void pe_dr_src_get_source_cap_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_MSG_OR_RJ(pd_port); + + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_SOURCE_CAP); +} + +void pe_dr_src_get_source_cap_exit(struct pd_port *pd_port) +{ + pd_dpm_dr_inform_source_cap(pd_port); +} + +void pe_dr_src_give_sink_cap_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + + pd_dpm_send_sink_caps(pd_port); +} + +void pe_dr_snk_get_sink_cap_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_MSG_OR_RJ(pd_port); + + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_SINK_CAP); +} + +void pe_dr_snk_get_sink_cap_exit(struct pd_port *pd_port) +{ + pd_dpm_dr_inform_sink_cap(pd_port); +} + +void pe_dr_snk_give_source_cap_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + + pd_dpm_send_source_caps(pd_port); +} + +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL +void pe_dr_snk_give_source_cap_ext_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + + pd_dpm_send_source_cap_ext(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE +void pe_dr_src_get_source_cap_ext_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_MSG(pd_port); + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_SOURCE_CAP_EXT); +} + +void pe_dr_src_get_source_cap_ext_exit(struct pd_port *pd_port) +{ + pd_dpm_inform_source_cap_ext(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ diff --git a/drivers/usb/typec/mmi_tcpc/pd_policy_engine_drs.c b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_drs.c new file mode 100644 index 000000000000..50e149817903 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_drs.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Policy Engine for DRS + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/pd_dpm_core.h" +#include "inc/tcpci.h" +#include "inc/pd_policy_engine.h" + +/* + * [PD2.0] Figure 8-49: Type-C DFP to UFP Data Role Swap State Diagram + */ + +void pe_drs_dfp_ufp_evaluate_dr_swap_entry(struct pd_port *pd_port) +{ + pd_dpm_drs_evaluate_swap(pd_port, PD_ROLE_UFP); +} + +void pe_drs_dfp_ufp_accept_dr_swap_entry(struct pd_port *pd_port) +{ + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_ACCEPT); +} + +void pe_drs_dfp_ufp_change_to_ufp_entry(struct pd_port *pd_port) +{ + pd_dpm_drs_change_role(pd_port, PD_ROLE_UFP); +} + +void pe_drs_dfp_ufp_send_dr_swap_entry(struct pd_port *pd_port) +{ + pe_send_swap_request_entry(pd_port, PD_CTRL_DR_SWAP); +} + +void pe_drs_dfp_ufp_reject_dr_swap_entry(struct pd_port *pd_port) +{ + pd_reply_wait_reject_msg(pd_port); +} + +/* + * [PD2.0] Figure 8-50: Type-C UFP to DFP Data Role Swap State Diagram + */ + +void pe_drs_ufp_dfp_evaluate_dr_swap_entry(struct pd_port *pd_port) +{ + pd_dpm_drs_evaluate_swap(pd_port, PD_ROLE_DFP); +} + +void pe_drs_ufp_dfp_accept_dr_swap_entry(struct pd_port *pd_port) +{ + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_ACCEPT); +} + +void pe_drs_ufp_dfp_change_to_dfp_entry(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_RESET_CABLE + dpm_reaction_set(pd_port, DPM_REACTION_CAP_RESET_CABLE); +#endif /* CONFIG_USB_PD_RESET_CABLE */ + + pd_dpm_drs_change_role(pd_port, PD_ROLE_DFP); +} + +void pe_drs_ufp_dfp_send_dr_swap_entry(struct pd_port *pd_port) +{ + pe_send_swap_request_entry(pd_port, PD_CTRL_DR_SWAP); +} + +void pe_drs_ufp_dfp_reject_dr_swap_entry( + struct pd_port *pd_port) +{ + pd_reply_wait_reject_msg(pd_port); +} diff --git a/drivers/usb/typec/mmi_tcpc/pd_policy_engine_prs.c b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_prs.c new file mode 100644 index 000000000000..a198bd91837a --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_prs.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Policy Engine for PRS + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/pd_dpm_core.h" +#include "inc/tcpci.h" +#include "inc/pd_policy_engine.h" + +/* + * [PD2.0] Figure 8-51: + * Dual-Role Port in Source to Sink Power Role Swap State Diagram + */ + +void pe_prs_src_snk_evaluate_pr_swap_entry(struct pd_port *pd_port) +{ + pd_dpm_prs_evaluate_swap(pd_port, PD_ROLE_SINK); +} + +void pe_prs_src_snk_accept_pr_swap_entry(struct pd_port *pd_port) +{ + pd_notify_pe_execute_pr_swap(pd_port, true); + + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_ACCEPT); +} + +void pe_prs_src_snk_transition_to_off_entry(struct pd_port *pd_port) +{ + pd_lock_msg_output(pd_port); /* for tSRCTransition */ + pd_notify_pe_execute_pr_swap(pd_port, true); + + pd_enable_timer(pd_port, PD_TIMER_SOURCE_TRANSITION); +} + +void pe_prs_src_snk_assert_rd_entry(struct pd_port *pd_port) +{ + pd_dpm_prs_change_role(pd_port, PD_ROLE_SINK); +} + +void pe_prs_src_snk_wait_source_on_entry(struct pd_port *pd_port) +{ + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_PS_RDY); +} + +void pe_prs_src_snk_send_swap_entry(struct pd_port *pd_port) +{ + pe_send_swap_request_entry(pd_port, PD_CTRL_PR_SWAP); +} + +void pe_prs_src_snk_reject_pr_swap_entry(struct pd_port *pd_port) +{ + pd_reply_wait_reject_msg(pd_port); +} + +/* + * [PD2.0] Figure 8-52: + * Dual-role Port in Sink to Source Power Role Swap State Diagram + */ + +void pe_prs_snk_src_evaluate_pr_swap_entry(struct pd_port *pd_port) +{ + pd_dpm_prs_evaluate_swap(pd_port, PD_ROLE_SOURCE); +} + +void pe_prs_snk_src_accept_pr_swap_entry( + struct pd_port *pd_port) +{ + pd_notify_pe_execute_pr_swap(pd_port, true); + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_ACCEPT); +} + +void pe_prs_snk_src_transition_to_off_entry(struct pd_port *pd_port) +{ + /* + * Sink should call pd_notify_pe_execute_pr_swap before this state, + * because source may turn off power & change CC before we got + * GoodCRC or Accept. + */ + + pd_port->pe_data.during_swap = true; + pd_enable_pe_state_timer(pd_port, PD_TIMER_PS_SOURCE_OFF); + pd_dpm_prs_turn_off_power_sink(pd_port); +} + +void pe_prs_snk_src_assert_rp_entry(struct pd_port *pd_port) +{ + pd_dpm_prs_change_role(pd_port, PD_ROLE_SOURCE); +} + +void pe_prs_snk_src_source_on_entry(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_RESET_CABLE + dpm_reaction_set(pd_port, DPM_REACTION_CAP_RESET_CABLE); +#endif /* CONFIG_USB_PD_RESET_CABLE */ + + pd_dpm_dynamic_enable_vconn(pd_port); + pd_dpm_prs_enable_power_source(pd_port, true); + + /* Send PS_Rdy in process_event after source_on */ +} + +void pe_prs_snk_src_send_swap_entry(struct pd_port *pd_port) +{ + pd_notify_pe_execute_pr_swap(pd_port, false); + pe_send_swap_request_entry(pd_port, PD_CTRL_PR_SWAP); +} + +void pe_prs_snk_src_reject_swap_entry(struct pd_port *pd_port) +{ + pd_reply_wait_reject_msg(pd_port); +} diff --git a/drivers/usb/typec/mmi_tcpc/pd_policy_engine_snk.c b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_snk.c new file mode 100644 index 000000000000..b7151f8ca5a5 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_snk.c @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Policy Engine for SNK + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/pd_dpm_core.h" +#include "inc/tcpci.h" +#include "inc/pd_policy_engine.h" + +/* + * [PD2.0] Figure 8-39 Sink Port state diagram + */ + +void pe_snk_startup_entry(struct pd_port *pd_port) +{ + uint8_t rx_cap = PD_RX_CAP_PE_STARTUP; + bool pr_swap = pd_port->state_machine == PE_STATE_MACHINE_PR_SWAP; + +#ifdef CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP + uint8_t msg_id_last = pd_port->pe_data.msg_id_rx[TCPC_TX_SOP]; +#endif /* CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP */ + + pd_reset_protocol_layer(pd_port, false); + + if (pr_swap) { + /* + * If PE reset rx_cap to startup in here, + * maybe can't meet tSwapSink for PR_SWAP case + */ + rx_cap = PD_RX_CAP_PE_SEND_WAIT_CAP; + +#ifdef CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP + pd_port->msg_id_pr_swap_last = msg_id_last; +#endif /* CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP */ + } + + pd_set_rx_enable(pd_port, rx_cap); + pd_put_pe_event(pd_port, PD_PE_RESET_PRL_COMPLETED); +} + +void pe_snk_discovery_entry(struct pd_port *pd_port) +{ + bool wait_valid = true; + + if (pd_check_pe_during_hard_reset(pd_port)) { + wait_valid = false; + pd_enable_pe_state_timer(pd_port, PD_TIMER_PS_TRANSITION); + } + pd_enable_vbus_valid_detection(pd_port, wait_valid); +} + +void pe_snk_wait_for_capabilities_entry( + struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_SNK_HRESET_KEEP_DRAW + /* Default current draw after HardReset */ + if (pd_check_pe_during_hard_reset(pd_port)) + pd_dpm_sink_vbus(pd_port, true); +#endif /* CONFIG_USB_PD_SNK_HRESET_KEEP_DRAW */ + + pd_notify_pe_hard_reset_completed(pd_port); + + pd_set_rx_enable(pd_port, PD_RX_CAP_PE_SEND_WAIT_CAP); + pd_enable_pe_state_timer(pd_port, PD_TIMER_SINK_WAIT_CAP); +} + +void pe_snk_evaluate_capability_entry(struct pd_port *pd_port) +{ + /* Disable UART output for Source SenderResponse */ + pd_lock_msg_output(pd_port); + + pd_handle_hard_reset_recovery(pd_port); + pd_handle_first_pd_command(pd_port); + + pd_port->pe_data.explicit_contract = false; + pd_dpm_snk_evaluate_caps(pd_port); +} + +void pe_snk_select_capability_entry(struct pd_port *pd_port) +{ + struct pd_event *pd_event = pd_get_curr_pd_event(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + PE_STATE_WAIT_MSG_HRESET_IF_TOUT(pd_port); + + if (pd_event->event_type == PD_EVT_DPM_MSG) { + PE_DBG("SelectCap%d, rdo:0x%08x\n", + pd_event->msg_sec, pd_port->last_rdo); + } else { + /* new request, for debug only */ + /* pd_dpm_sink_vbus(pd_port, false); */ + PE_DBG("NewReq, rdo:0x%08x\n", pd_port->last_rdo); + } + + /* Disable UART output for Sink SenderResponse */ + pd_lock_msg_output(pd_port); + + pd_send_sop_data_msg(pd_port, + PD_DATA_REQUEST, 1, &pd_port->last_rdo); +} + +void pe_snk_select_capability_exit(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_RENEGOTIATION_COUNTER + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; +#endif /* CONFIG_USB_PD_RENEGOTIATION_COUNTER */ + + if (pd_check_ctrl_msg_event(pd_port, PD_CTRL_ACCEPT)) { + pd_port->pe_data.remote_selected_cap = + RDO_POS(pd_port->last_rdo); + pd_port->cap_miss_match = 0; + } else if (pd_check_ctrl_msg_event(pd_port, PD_CTRL_REJECT)) { +#ifdef CONFIG_USB_PD_RENEGOTIATION_COUNTER + if (pd_port->cap_miss_match == 0x01) { + PE_INFO("reset renegotiation cnt by cap mismatch\n"); + pd_port->pe_data.renegotiation_count = 0; + } +#endif /* CONFIG_USB_PD_RENEGOTIATION_COUNTER */ + pd_port->cap_miss_match |= (1 << 1); + } else + pd_port->cap_miss_match = 0; + + /* Waiting for Hard-Reset Done */ + if (!pd_check_timer_msg_event(pd_port, PD_TIMER_SENDER_RESPONSE)) + pd_unlock_msg_output(pd_port); +} + +void pe_snk_transition_sink_entry(struct pd_port *pd_port) +{ + pd_enable_pe_state_timer(pd_port, PD_TIMER_PS_TRANSITION); + +#ifdef CONFIG_USB_PD_SNK_GOTOMIN + if (pd_check_ctrl_msg_event(pd_port, PD_CTRL_GOTO_MIN)) { + if (pd_port->dpm_caps & DPM_CAP_LOCAL_GIVE_BACK) + pd_port->request_i_new = pd_port->request_i_op; + } +#endif /* CONFIG_USB_PD_SNK_GOTOMIN */ + + pd_dpm_snk_standby_power(pd_port); +} + +void pe_snk_ready_entry(struct pd_port *pd_port) +{ + if (pd_check_ctrl_msg_event(pd_port, PD_CTRL_WAIT)) + pd_enable_timer(pd_port, PD_TIMER_SINK_REQUEST); + + pd_notify_pe_snk_explicit_contract(pd_port); + pe_power_ready_entry(pd_port); +} + +void pe_snk_hard_reset_entry(struct pd_port *pd_port) +{ + int rv = 0; + uint32_t chip_id = 0; + + pd_send_hard_reset(pd_port); + rv = tcpci_get_chip_id(pd_port->tcpc, &chip_id); + if (!rv && (SC2150A_DID == chip_id)) { + pd_enable_timer(pd_port,PD_TIMER_HARD_RESET_COMPLETE); + } +} + +void pe_snk_transition_to_default_entry(struct pd_port *pd_port) +{ + pd_reset_local_hw(pd_port); + pd_dpm_snk_hard_reset(pd_port); +} + +void pe_snk_give_sink_cap_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + + pd_dpm_send_sink_caps(pd_port); +} + +void pe_snk_get_source_cap_entry(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_TCPM_CB_2ND + PE_STATE_WAIT_MSG(pd_port); +#else + PE_STATE_WAIT_TX_SUCCESS(pd_port); +#endif /* CONFIG_USB_PD_TCPM_CB_2ND */ + + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_SOURCE_CAP); +} + +void pe_snk_send_soft_reset_entry(struct pd_port *pd_port) +{ + pd_send_soft_reset(pd_port); +} + +void pe_snk_soft_reset_entry(struct pd_port *pd_port) +{ + pd_handle_soft_reset(pd_port); +} + +/* ---- Policy Engine (PD30) ---- */ + +#ifdef CONFIG_USB_PD_REV30 + +/* + * [PD3.0] Figure 8-71 Sink Port Not Supported Message State Diagram + */ + +void pe_snk_send_not_supported_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_NOT_SUPPORTED); +} + +void pe_snk_not_supported_received_entry(struct pd_port *pd_port) +{ + PE_STATE_DPM_INFORMED(pd_port); + + pd_dpm_inform_not_support(pd_port); +} + +void pe_snk_chunk_received_entry(struct pd_port *pd_port) +{ + pd_enable_timer(pd_port, PD_TIMER_CK_NOT_SUPPORTED); +} + +/* + * [PD3.0] Figure 8-74 Sink Port Source Alert State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE +void pe_snk_source_alert_received_entry(struct pd_port *pd_port) +{ + PE_STATE_DPM_INFORMED(pd_port); + + pd_dpm_inform_alert(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ + +/* + * [PD3.0] Figure 8-75 Sink Port Sink Alert State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL +void pe_snk_send_sink_alert_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + pd_dpm_send_alert(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ + +/* + * [PD3.0] Figure 8-77 Sink Port Get Source Capabilities Extended State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE +void pe_snk_get_source_cap_ext_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_MSG(pd_port); + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_SOURCE_CAP_EXT); +} + +void pe_snk_get_source_cap_ext_exit(struct pd_port *pd_port) +{ + pd_dpm_inform_source_cap_ext(pd_port); +} + +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ + +/* + * [PD3.0] Figure 8-79 Sink Port Get Source Status State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE +void pe_snk_get_source_status_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_MSG(pd_port); + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_STATUS); +} + +void pe_snk_get_source_status_exit(struct pd_port *pd_port) +{ + pd_dpm_inform_status(pd_port); +} + +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ + +/* + * [PD3.0] Figure 8-82 Sink Give Sink Status State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL +void pe_snk_give_sink_status_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + + pd_dpm_send_status(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ + +/* + * [PD3.0] Figure 8-83 Sink Port Get Source PPS Status State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK +void pe_snk_get_pps_status_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_MSG(pd_port); + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_PPS_STATUS); +} + +void pe_snk_get_pps_status_exit(struct pd_port *pd_port) +{ + pd_dpm_inform_pps_status(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + +#endif /* CONFIG_USB_PD_REV30 */ diff --git a/drivers/usb/typec/mmi_tcpc/pd_policy_engine_src.c b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_src.c new file mode 100644 index 000000000000..2eff99cf4afb --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_src.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Policy Engine for SRC + * + * 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. + */ + +#include +#include "inc/pd_core.h" +#include "inc/pd_dpm_core.h" +#include "inc/tcpci.h" +#include "inc/pd_policy_engine.h" + +/* + * [PD2.0] Figure 8-38 Source Port Policy Engine state diagram + */ + +void pe_src_startup_entry(struct pd_port *pd_port) +{ + pd_reset_protocol_layer(pd_port, false); + pd_set_rx_enable(pd_port, PD_RX_CAP_PE_STARTUP); + + /* + * When entering this state, + * VBUS must be valid even for cc_attached. + */ + + pd_enable_timer(pd_port, PD_TIMER_SOURCE_START); +} + +void pe_src_discovery_entry(struct pd_port *pd_port) +{ + /* The SourceCapabilitiesTimer continues to run during the states + * defined in Source Startup Structured VDM Discover Identity State + * Diagram + */ + + pd_port->pe_data.pd_connected = false; + + pd_enable_timer(pd_port, PD_TIMER_SOURCE_CAPABILITY); + +#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID + if (pd_is_discover_cable(pd_port)) + pd_enable_timer(pd_port, PD_TIMER_DISCOVER_ID); +#endif +} + +void pe_src_send_capabilities_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_MSG_HRESET_IF_TOUT(pd_port); + + pd_set_rx_enable(pd_port, PD_RX_CAP_PE_SEND_WAIT_CAP); + + pd_dpm_send_source_caps(pd_port); + pd_port->pe_data.cap_counter++; +} + +void pe_src_negotiate_capabilities_entry( + struct pd_port *pd_port) +{ + pd_handle_first_pd_command(pd_port); + pd_dpm_src_evaluate_request(pd_port); +} + +void pe_src_transition_supply_entry(struct pd_port *pd_port) +{ + uint8_t msg = PD_CTRL_ACCEPT; + struct pd_event *pd_event = pd_get_curr_pd_event(pd_port); + + /* goto-min */ + if (pd_event->event_type == PD_EVT_TCP_MSG) { + msg = PD_CTRL_GOTO_MIN; + pd_port->request_i_new = pd_port->request_i_op; + } + + pd_send_sop_ctrl_msg(pd_port, msg); +} + +void pe_src_transition_supply2_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_PS_RDY); +} + +void pe_src_ready_entry(struct pd_port *pd_port) +{ + pd_notify_pe_src_explicit_contract(pd_port); + pe_power_ready_entry(pd_port); +} + +void pe_src_disabled_entry(struct pd_port *pd_port) +{ + pd_notify_pe_hard_reset_completed(pd_port); + + pd_set_rx_enable(pd_port, PD_RX_CAP_PE_DISABLE); + pd_update_connect_state(pd_port, PD_CONNECT_TYPEC_ONLY_SRC); + pd_dpm_dynamic_disable_vconn(pd_port); +} + +void pe_src_capability_response_entry(struct pd_port *pd_port) +{ + pd_reply_wait_reject_msg_no_resp(pd_port); +} + +void pe_src_hard_reset_entry(struct pd_port *pd_port) +{ + pd_send_hard_reset(pd_port); + pd_enable_timer(pd_port, PD_TIMER_PS_HARD_RESET); + pd_enable_timer(pd_port, PD_TIMER_NO_RESPONSE); +} + +void pe_src_hard_reset_received_entry(struct pd_port *pd_port) +{ + pd_enable_timer(pd_port, PD_TIMER_PS_HARD_RESET); + pd_enable_timer(pd_port, PD_TIMER_NO_RESPONSE); +} + +void pe_src_transition_to_default_entry(struct pd_port *pd_port) +{ + pd_reset_local_hw(pd_port); + pd_dpm_src_hard_reset(pd_port); +} + +void pe_src_transition_to_default_exit(struct pd_port *pd_port) +{ + pd_set_vconn(pd_port, PD_ROLE_VCONN_ON); +} + +void pe_src_get_sink_cap_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_MSG(pd_port); + + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_SINK_CAP); +} + +void pe_src_get_sink_cap_exit(struct pd_port *pd_port) +{ + pd_dpm_dr_inform_sink_cap(pd_port); +} + +void pe_src_wait_new_capabilities_entry( + struct pd_port *pd_port) +{ + /* Wait for new Source Capabilities */ +} + +void pe_src_send_soft_reset_entry(struct pd_port *pd_port) +{ + pd_send_soft_reset(pd_port); +} + +void pe_src_soft_reset_entry(struct pd_port *pd_port) +{ + pd_handle_soft_reset(pd_port); +} + +/* + * [PD2.0] Figure 8-81 + Source Startup Structured VDM Discover Identity State Diagram (TODO) + */ + +#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID + +#ifdef CONFIG_PD_SRC_RESET_CABLE +void pe_src_cbl_send_soft_reset_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_RESPONSE(pd_port); + + pd_set_rx_enable(pd_port, PD_RX_CAP_PE_DISCOVER_CABLE); + + pd_send_cable_soft_reset(pd_port); +} +#endif /* CONFIG_PD_SRC_RESET_CABLE */ + +void pe_src_vdm_identity_request_entry(struct pd_port *pd_port) +{ + pd_set_rx_enable(pd_port, PD_RX_CAP_PE_DISCOVER_CABLE); + + pd_port->pe_data.discover_id_counter++; + pd_send_vdm_discover_id(pd_port, TCPC_TX_SOP_PRIME); +} + +void pe_src_vdm_identity_acked_entry(struct pd_port *pd_port) +{ + pd_dpm_inform_cable_id(pd_port, true); +} + +void pe_src_vdm_identity_naked_entry(struct pd_port *pd_port) +{ + pd_dpm_inform_cable_id(pd_port, true); +} + +#endif /* CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */ + + +#ifdef CONFIG_USB_PD_REV30 + +/* + * [PD3.0] Source Port Not Supported Message State Diagram + */ + +void pe_src_send_not_supported_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_NOT_SUPPORTED); +} + +void pe_src_not_supported_received_entry(struct pd_port *pd_port) +{ + PE_STATE_DPM_INFORMED(pd_port); + + pd_dpm_inform_not_support(pd_port); +} + +void pe_src_chunk_received_entry(struct pd_port *pd_port) +{ + pd_enable_timer(pd_port, PD_TIMER_CK_NOT_SUPPORTED); +} + +/* + * [PD3.0] Figure 8-73 Source Port Source Alert State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL +void pe_src_send_source_alert_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + pd_dpm_send_alert(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ + +/* + * [PD3.0] Figure 8-76 Source Port Sink Alert State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE +void pe_src_sink_alert_received_entry(struct pd_port *pd_port) +{ + PE_STATE_DPM_INFORMED(pd_port); + + pd_dpm_inform_alert(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ + +/* + * [PD3.0] Figure 8-78 Source Give Source Capabilities Extended State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL +void pe_src_give_source_cap_ext_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + + pd_dpm_send_source_cap_ext(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ + +/* + * [PD3.0] Figure 8-80 Source Give Source Status State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL +void pe_src_give_source_status_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + + pd_dpm_send_status(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ + +/* + * [PD3.0] Figure 8-81 Source Port Get Sink Status State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE +void pe_src_get_sink_status_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_MSG(pd_port); + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_GET_STATUS); +} + +void pe_src_get_sink_status_exit(struct pd_port *pd_port) +{ + pd_dpm_inform_status(pd_port); +} +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ + +/* + * [PD3.0] Figure 8-84 Source Give Source PPS Status State Diagram + */ + +#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE +void pe_src_give_pps_status_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + + /* TODO */ + PD_BUG_ON(1); +} +#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */ + +#endif /* CONFIG_USB_PD_REV30 */ diff --git a/drivers/usb/typec/mmi_tcpc/pd_policy_engine_ufp.c b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_ufp.c new file mode 100644 index 000000000000..ce2196b18c3f --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_ufp.c @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Policy Engine for UFP + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/pd_dpm_core.h" +#include "inc/tcpci.h" +#include "inc/pd_policy_engine.h" + +/* + * [PD2.0] Figure 8-58 UFP Structured VDM Discover Identity State Diagram + */ + +void pe_ufp_vdm_get_identity_entry(struct pd_port *pd_port) +{ + pd_dpm_ufp_request_id_info(pd_port); +} + +/* + * [PD2.0] Figure 8-59 UFP Structured VDM Discover SVIDs State Diagram + */ + +void pe_ufp_vdm_get_svids_entry(struct pd_port *pd_port) +{ + pd_dpm_ufp_request_svid_info(pd_port); +} + +/* + * [PD2.0] Figure 8-60 UFP Structured VDM Discover Modes State Diagram + */ + +void pe_ufp_vdm_get_modes_entry(struct pd_port *pd_port) +{ + pd_dpm_ufp_request_mode_info(pd_port); +} + +/* + * [PD2.0] Figure 8-61 UFP Structured VDM Enter Mode State Diagram + */ + +void pe_ufp_vdm_evaluate_mode_entry_entry( + struct pd_port *pd_port) +{ + pd_dpm_ufp_request_enter_mode(pd_port); +} + +/* + * [PD2.0] Figure 8-62 UFP Structured VDM Exit Mode State Diagram + */ + +void pe_ufp_vdm_mode_exit_entry(struct pd_port *pd_port) +{ + pd_dpm_ufp_request_exit_mode(pd_port); +} + +/* + * [PD2.0] Figure 8-63 UFP VDM Attention State Diagram + */ + +void pe_ufp_vdm_attention_request_entry( + struct pd_port *pd_port) +{ + VDM_STATE_NORESP_CMD(pd_port); + + switch (pd_port->mode_svid) { +#ifdef CONFIG_USB_PD_ALT_MODE + case USB_SID_DISPLAYPORT: + pd_dpm_ufp_send_dp_attention(pd_port); + break; +#endif + default: + pd_send_vdm_attention(pd_port, + TCPC_TX_SOP, pd_port->mode_svid, pd_port->mode_obj_pos); + break; + } +} + +/* + * ALT Mode + */ + +#ifdef CONFIG_USB_PD_ALT_MODE + +void pe_ufp_vdm_dp_status_update_entry(struct pd_port *pd_port) +{ + pd_dpm_ufp_request_dp_status(pd_port); +} + +void pe_ufp_vdm_dp_configure_entry(struct pd_port *pd_port) +{ + pd_dpm_ufp_request_dp_config(pd_port); +} + +#endif /* CONFIG_USB_PD_ALT_MODE */ + +/* + * SVMD/UVDM + */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM + +void pe_ufp_uvdm_recv_entry(struct pd_port *pd_port) +{ + pd_dpm_ufp_recv_uvdm(pd_port); +} + +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + +void pe_ufp_vdm_send_nak_entry(struct pd_port *pd_port) +{ + pd_dpm_ufp_send_svdm_nak(pd_port); + VDM_STATE_DPM_INFORMED(pd_port); +} diff --git a/drivers/usb/typec/mmi_tcpc/pd_policy_engine_vcs.c b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_vcs.c new file mode 100644 index 000000000000..525183069346 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_policy_engine_vcs.c @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Policy Engine for VCS + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/pd_dpm_core.h" +#include "inc/tcpci.h" +#include "inc/pd_policy_engine.h" + +/* + * [PD2.0] Figure 8-57 VCONN Swap State Diagram + */ + +void pe_vcs_send_swap_entry(struct pd_port *pd_port) +{ + pe_send_swap_request_entry(pd_port, PD_CTRL_VCONN_SWAP); +} + +void pe_vcs_evaluate_swap_entry(struct pd_port *pd_port) +{ + pd_dpm_vcs_evaluate_swap(pd_port); +} + +void pe_vcs_accept_swap_entry(struct pd_port *pd_port) +{ + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_ACCEPT); +} + +void pe_vcs_reject_vconn_swap_entry(struct pd_port *pd_port) +{ + pd_reply_wait_reject_msg(pd_port); +} + +void pe_vcs_wait_for_vconn_entry(struct pd_port *pd_port) +{ + pd_enable_pe_state_timer(pd_port, PD_TIMER_VCONN_ON); +} + +void pe_vcs_turn_off_vconn_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_DPM_ACK(pd_port); + pd_dpm_vcs_enable_vconn(pd_port, PD_ROLE_VCONN_OFF); +} + +void pe_vcs_turn_on_vconn_entry(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_RESET_CABLE + dpm_reaction_set(pd_port, DPM_REACTION_CAP_RESET_CABLE); +#endif /* CONFIG_USB_PD_RESET_CABLE */ +#endif /* CONFIG_USB_PD_REV30 */ + + pd_dpm_vcs_enable_vconn(pd_port, PD_ROLE_VCONN_DYNAMIC_ON); +} + +void pe_vcs_send_ps_rdy_entry(struct pd_port *pd_port) +{ + PE_STATE_WAIT_TX_SUCCESS(pd_port); + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_PS_RDY); +} diff --git a/drivers/usb/typec/mmi_tcpc/pd_process_evt.c b/drivers/usb/typec/mmi_tcpc/pd_process_evt.c new file mode 100644 index 000000000000..2da6fbf5aa59 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_process_evt.c @@ -0,0 +1,870 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Process Event + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/tcpci_event.h" +#include "inc/pd_process_evt.h" +#include "inc/pd_dpm_core.h" + +/* + * [BLOCK] print event + */ + +#if PE_EVENT_DBG_ENABLE +static const char * const pd_ctrl_msg_name[] = { + "ctrl0", + "good_crc", + "goto_min", + "accept", + "reject", + "ping", + "ps_rdy", + "get_src_cap", + "get_snk_cap", + "dr_swap", + "pr_swap", + "vs_swap", + "wait", + "soft_reset", + "ctrlE", + "ctrlF", +#ifdef CONFIG_USB_PD_REV30 + "no_support", + "get_src_cap_ex", + "get_status", + "fr_swap", + "get_pps", + "get_cc", +#endif /* CONFIG_USB_PD_REV30 */ +}; + +static inline void print_ctrl_msg_event(struct tcpc_device *tcpc, uint8_t msg) +{ + if (msg < PD_CTRL_MSG_NR) + PE_EVT_INFO("%s\n", pd_ctrl_msg_name[msg]); +} + +static const char * const pd_data_msg_name[] = { + "data0", + "src_cap", + "request", + "bist", + "sink_cap", +#ifdef CONFIG_USB_PD_REV30 + "bat_status", + "alert", + "get_ci", +#else + "data5", + "data6", + "data7", +#endif /* CONFIG_USB_PD_REV30 */ + "data8", + "data9", + "dataA", + "dataB", + "dataC", + "dataD", + "dataE", + "vdm", +}; + +static inline void print_data_msg_event(struct tcpc_device *tcpc, uint8_t msg) +{ + if (msg < PD_DATA_MSG_NR) + PE_EVT_INFO("%s\n", pd_data_msg_name[msg]); +} + +#ifdef CONFIG_USB_PD_REV30 + +static const char *const pd_ext_msg_name[] = { + "ext0", + "src_cap_ex", + "status", + "get_bat_cap", + "get_bat_status", + "bat_cap", + "get_mfr_info", + "mfr_info", + "sec_req", + "sec_resp", + "fw_update_req", + "fw_update_res", + "pps_status", + "ci", + "cc", +}; + +static inline void print_ext_msg_event(struct tcpc_device *tcpc, uint8_t msg) +{ + if (msg < PD_EXT_MSG_NR) + PE_EVT_INFO("%s\n", pd_ext_msg_name[msg]); +} + +#endif /* CONFIG_USB_PD_REV30 */ + +static const char *const pd_hw_msg_name[] = { + "Detached", + "Attached", + "hard_reset", + "vbus_high", + "vbus_low", + "vbus_0v", + "vbus_stable", + "tx_err", + "discard", + "retry_vdm", + +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + "sink_tx_change", +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ +}; + +static inline void print_hw_msg_event(struct tcpc_device *tcpc, uint8_t msg) +{ + if (msg < PD_HW_MSG_NR) + PE_EVT_INFO("%s\n", pd_hw_msg_name[msg]); +} + +static const char *const pd_pe_msg_name[] = { + "reset_prl_done", + "pr_at_dft", + "hard_reset_done", + "pe_idle", + "vdm_reset", + "vdm_not_support", +}; + +static inline void print_pe_msg_event(struct tcpc_device *tcpc, uint8_t msg) +{ + if (msg < PD_PE_MSG_NR) + PE_EVT_INFO("%s\n", pd_pe_msg_name[msg]); +} + +static const char * const pd_dpm_msg_name[] = { + "ack", + "nak", + "cap_change", + "not_support", +}; + +static inline void print_dpm_msg_event(struct tcpc_device *tcpc, uint8_t msg) +{ + if (msg < PD_DPM_MSG_NR) + PE_EVT_INFO("dpm_%s\n", pd_dpm_msg_name[msg]); +} + +static const char *const tcp_dpm_evt_name[] = { + /* TCP_DPM_EVT_UNKONW */ + "unknown", + + /* TCP_DPM_EVT_PD_COMMAND */ + "pr_swap_snk", + "pr_swap_src", + "dr_swap_ufp", + "dr_swap_dfp", + "vc_swap_off", + "vc_swap_on", + "goto_min", + "soft_reset", + "cable_soft_reset", + "get_src_cap", + "get_snk_cap", + "request", + "request_ex", + "request_again", + "bist_cm2", + "dummy", + +#ifdef CONFIG_USB_PD_REV30 + "get_src_cap_ext", + "get_status", + "fr_swap_snk", + "fr_swap_src", + "get_cc", + "get_pps", + + "alert", + "get_ci", + "get_bat_cap", + "get_bat_status", + "get_mfrs_info", +#endif /* CONFIG_USB_PD_REV30 */ + + /* TCP_DPM_EVT_VDM_COMMAND */ + "disc_cable", + "disc_id", + "disc_svid", + "disc_mode", + "enter_mode", + "exit_mode", + "attention", + +#ifdef CONFIG_USB_PD_ALT_MODE + "dp_atten", +#ifdef CONFIG_USB_PD_ALT_MODE_DFP + "dp_status", + "dp_config", +#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ +#endif /* CONFIG_USB_PD_ALT_MODE */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM + "uvdm", +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + + /* TCP_DPM_EVT_IMMEDIATELY */ + "hard_reset", + "error_recovery", +}; + +static inline void print_tcp_event(struct tcpc_device *tcpc, uint8_t msg) +{ + if (msg < TCP_DPM_EVT_NR) + PE_EVT_INFO("tcp_event(%s), %d\n", + tcp_dpm_evt_name[msg], msg); +} +#endif + +static inline void print_event( + struct pd_port *pd_port, struct pd_event *pd_event) +{ +#if PE_EVENT_DBG_ENABLE + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + switch (pd_event->event_type) { + case PD_EVT_CTRL_MSG: + print_ctrl_msg_event(tcpc, pd_event->msg); + break; + + case PD_EVT_DATA_MSG: + print_data_msg_event(tcpc, pd_event->msg); + break; + +#ifdef CONFIG_USB_PD_REV30 + case PD_EVT_EXT_MSG: + print_ext_msg_event(tcpc, pd_event->msg); + break; +#endif /* CONFIG_USB_PD_REV30 */ + + case PD_EVT_DPM_MSG: + print_dpm_msg_event(tcpc, pd_event->msg); + break; + + case PD_EVT_HW_MSG: + print_hw_msg_event(tcpc, pd_event->msg); + break; + + case PD_EVT_PE_MSG: + print_pe_msg_event(tcpc, pd_event->msg); + break; + + case PD_EVT_TIMER_MSG: + PE_EVT_INFO("timer\n"); + break; + + case PD_EVT_TCP_MSG: + print_tcp_event(tcpc, pd_event->msg); + break; + } +#endif +} + +/*---------------------------------------------------------------------------*/ + +bool pd_make_pe_state_transit(struct pd_port *pd_port, + uint8_t curr_state, const struct pe_state_reaction *state_reaction) +{ + int i; + const struct pe_state_transition *state_transition = + state_reaction->state_transition; + + for (i = 0; i < state_reaction->nr_transition; i++) { + if (state_transition[i].curr_state == curr_state) { + PE_TRANSIT_STATE(pd_port, + state_transition[i].next_state); + return true; + } + } + + return false; +} + +/*---------------------------------------------------------------------------*/ + +static inline bool pd_process_ready_protocol_error(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_REV30 + bool multi_chunk; +#endif /* CONFIG_USB_PD_REV30 */ + + if (!pd_port->curr_unsupported_msg) { + pe_transit_soft_reset_state(pd_port); + return true; + } + + if (!pd_check_rev30(pd_port)) { + PE_TRANSIT_STATE(pd_port, PE_REJECT); + return true; + } + +#ifdef CONFIG_USB_PD_REV30 + multi_chunk = pd_is_multi_chunk_msg(pd_port); + + if (pd_port->power_role == PD_ROLE_SINK) { + PE_TRANSIT_STATE(pd_port, multi_chunk ? + PE_SNK_CHUNK_RECEIVED : PE_SNK_SEND_NOT_SUPPORTED); + return true; + } + + PE_TRANSIT_STATE(pd_port, multi_chunk ? + PE_SRC_CHUNK_RECEIVED : PE_SRC_SEND_NOT_SUPPORTED); + return true; +#else + return false; +#endif /* CONFIG_USB_PD_REV30 */ +} + +bool pd_process_protocol_error( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + bool ret = false; + bool power_change = false; +#if PE_INFO_ENABLE + uint8_t event_type = pd_event->event_type; + uint8_t msg_type = pd_event->msg; + uint8_t msg_id = pd_get_msg_hdr_id(pd_port); +#endif + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_port->pe_data.pe_state_flags & + PE_STATE_FLAG_IGNORE_UNKNOWN_EVENT) { + PE_INFO("Ignore Unknown Event\n"); + goto out; + } + + if (pd_check_pe_during_hard_reset(pd_port)) { + PE_INFO("Ignore Event during HReset\n"); + goto out; + } + + switch (pd_port->pe_state_curr) { + case PE_SNK_TRANSITION_SINK: + /* fall through */ + case PE_SRC_TRANSITION_SUPPLY: /* never recv ping for Source =.=*/ + /* fall through */ + case PE_SRC_TRANSITION_SUPPLY2: + power_change = true; + if (pd_event_msg_match(pd_event, + PD_EVT_CTRL_MSG, PD_CTRL_PING)) { + PE_INFO("Ignore Ping\n"); + goto out; + } + break; + +#ifdef CONFIG_USB_PD_PR_SWAP + case PE_PRS_SRC_SNK_WAIT_SOURCE_ON: +#endif /* CONFIG_USB_PD_PR_SWAP */ + if (pd_event_msg_match(pd_event, + PD_EVT_CTRL_MSG, PD_CTRL_PING)) { + PE_INFO("Ignore Ping\n"); + goto out; + } + break; + + case PE_SNK_READY: + case PE_SRC_READY: + if (pd_process_ready_protocol_error(pd_port)) { + ret = true; + goto out; + } + break; + }; + + ret = true; + + if (pd_port->pe_data.during_swap) { +#ifdef CONFIG_USB_PD_PR_SWAP_ERROR_RECOVERY + PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY); +#else + pe_transit_hard_reset_state(pd_port); +#endif + } else if (power_change) + pe_transit_hard_reset_state(pd_port); + else + pe_transit_soft_reset_state(pd_port); + + /* + * event_type: PD_EVT_CTRL_MSG (1), PD_EVT_DATA_MSG (2) + */ +out: + PE_INFO("PRL_ERR: %d-%d-%d\n", event_type, msg_type, msg_id); + + return ret; +} + +bool pd_process_tx_failed(struct pd_port *pd_port) +{ + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_check_pe_state_ready(pd_port) || + pd_check_pe_during_hard_reset(pd_port)) { + PE_DBG("Ignore tx_failed\n"); + return false; + } + + pe_transit_soft_reset_state(pd_port); + return true; +} + +/*---------------------------------------------------------------------------*/ + +#ifdef CONFIG_USB_PD_RESET_CABLE +static inline bool pd_process_cable_ctrl_msg_accept( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_port->pe_state_curr) { +#ifdef CONFIG_PD_SRC_RESET_CABLE + case PE_SRC_CBL_SEND_SOFT_RESET: + vdm_put_dpm_discover_cable_event(pd_port); + return false; +#endif /* CONFIG_PD_SRC_RESET_CABLE */ + +#ifdef CONFIG_PD_DFP_RESET_CABLE + case PE_DFP_CBL_SEND_SOFT_RESET: + pe_transit_ready_state(pd_port); + return true; +#endif /* CONFIG_PD_DFP_RESET_CABLE */ + } + + return false; +} +#endif /* CONFIG_USB_PD_RESET_CABLE */ + +static inline bool pd_process_event_cable( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + bool ret = false; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + +#ifdef CONFIG_USB_PD_RESET_CABLE + if (pd_event->msg == PD_CTRL_ACCEPT) + ret = pd_process_cable_ctrl_msg_accept(pd_port, pd_event); +#endif /* CONFIG_USB_PD_RESET_CABLE */ + + if (!ret) + PE_DBG("Ignore not SOP Ctrl Msg\n"); + + return ret; +} + +/*---------------------------------------------------------------------------*/ + +static void pd_copy_msg_data(struct pd_port *pd_port, + uint8_t *payload, uint16_t count, uint8_t unit_sz) +{ + pd_port->pd_msg_data_size = count * unit_sz; + pd_port->pd_msg_data_count = count; + pd_port->pd_msg_data_payload = payload; +} + +#ifdef CONFIG_USB_PD_REV30 +static inline void pd_copy_msg_data_from_ext_evt( + struct pd_port *pd_port, struct pd_msg *pd_msg) +{ + uint32_t *payload = pd_msg->payload; + + uint16_t *ext_hdr = (uint16_t *) payload; + uint8_t *ext_data = (uint8_t *) (ext_hdr+1); + + uint16_t size = PD_EXT_HEADER_DATA_SIZE(*ext_hdr); + + pd_copy_msg_data(pd_port, ext_data, size, 1); +} +#endif /* CONFIG_USB_PD_REV30 */ + +static inline void pd_copy_msg_data_from_evt( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + struct pd_msg *pd_msg = pd_event->pd_msg; + + switch (pd_event->event_type) { + case PD_EVT_DATA_MSG: + PD_BUG_ON(pd_msg == NULL); + pd_copy_msg_data(pd_port, (uint8_t *)pd_msg->payload, + pd_get_msg_hdr_cnt(pd_port), sizeof(uint32_t)); + break; + +#ifdef CONFIG_USB_PD_REV30 + case PD_EVT_EXT_MSG: + PD_BUG_ON(pd_msg == NULL); + pd_copy_msg_data_from_ext_evt(pd_port, pd_msg); + return; +#endif /* CONFIG_USB_PD_REV30 */ + + default: + pd_copy_msg_data(pd_port, NULL, 0, 0); + } + +} + +/*---------------------------------------------------------------------------*/ + +/* + * + * @ true : valid message + * @ false : invalid message, pe should drop the message + */ + +static inline bool pe_is_valid_pd_msg_id(struct pd_port *pd_port, + struct pd_event *pd_event, struct pd_msg *pd_msg) +{ + uint8_t sop_type = pd_msg->frame_type; + uint8_t msg_id = pd_get_msg_hdr_id(pd_port); + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_port->pe_state_curr == PE_BIST_TEST_DATA) + return false; + + if (pd_event->event_type == PD_EVT_CTRL_MSG) { + switch (pd_event->msg) { + /* SofReset always has a MessageID value of zero */ + case PD_CTRL_SOFT_RESET: + if (msg_id != 0) { + PE_INFO("Repeat soft_reset\n"); + return false; + } + return true; + + case PD_CTRL_GOOD_CRC: + PE_DBG("Discard_CRC\n"); + return true; + +#ifdef CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP + case PD_CTRL_PS_RDY: + if (pd_port->msg_id_pr_swap_last == msg_id) { + PE_INFO("Repeat ps_rdy\n"); + return false; + } + break; +#endif /* CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP */ + } + } + + if (pd_port->pe_data.msg_id_rx[sop_type] == msg_id) { + PE_INFO("Repeat msg: %c:%d:%d\n", + (pd_event->event_type == PD_EVT_CTRL_MSG) ? 'C' : 'D', + pd_event->msg, msg_id); + return false; + } + + if (((pd_port->pe_data.msg_id_rx[sop_type] + 2) % PD_MSG_ID_MAX) + == msg_id) { + PE_INFO("Miss Msg!!!\n"); + pd_port->miss_msg = true; + } + + pd_port->pe_data.msg_id_rx[sop_type] = msg_id; + return true; +} + +static inline bool pe_is_valid_pd_msg_role(struct pd_port *pd_port, + struct pd_event *pd_event, struct pd_msg *pd_msg) +{ + bool ret = true; + uint8_t msg_pr, msg_dr; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_msg == NULL) /* Good-CRC */ + return true; + + if (pd_msg->frame_type != TCPC_TX_SOP) + return true; + + msg_pr = PD_HEADER_PR(pd_msg->msg_hdr); + msg_dr = PD_HEADER_DR(pd_msg->msg_hdr); + + /* + * The Port Power Role field of a received Message shall not be verified + * by the receiver and no error recovery action shall be + * taken if it is incorrect. + */ + + if (msg_pr == pd_port->power_role) + PE_DBG("Wrong PR:%d\n", msg_pr); + + /* + * Should a Type-C Port receive a Message with the Port Data Role field + * set to the same Data Role as its current Data Role, + * except for the GoodCRC Message, + * Type-C error recovery actions as defined + * in [USBType-C 1.0] shall be performed. + */ + + if (msg_dr == pd_port->data_role) { +#ifdef CONFIG_USB_PD_CHECK_DATA_ROLE + ret = false; +#endif + PE_INFO("Wrong DR:%d\n", msg_dr); + } + + return ret; +} + +static inline void pe_translate_pd_msg_event(struct pd_port *pd_port, + struct pd_event *pd_event, struct pd_msg *pd_msg) +{ + uint16_t msg_hdr; + + PD_BUG_ON(pd_msg == NULL); + + msg_hdr = pd_msg->msg_hdr; + pd_port->curr_msg_hdr = msg_hdr; + pd_event->msg = PD_HEADER_TYPE(msg_hdr); + + if (PD_HEADER_CNT(msg_hdr)) + pd_event->event_type = PD_EVT_DATA_MSG; + else + pd_event->event_type = PD_EVT_CTRL_MSG; + +#ifdef CONFIG_USB_PD_REV30 + if (PD_HEADER_EXT(msg_hdr)) + pd_event->event_type = PD_EVT_EXT_MSG; + + if (pd_msg->frame_type == TCPC_TX_SOP_PRIME) { + pd_sync_sop_prime_spec_revision( + pd_port, PD_HEADER_REV(msg_hdr)); + } +#endif /* CONFIG_USB_PD_REV30 */ +} + +static inline uint8_t pe_get_startup_state( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + bool act_as_sink = true; + uint8_t startup_state = 0xff; + +#ifdef CONFIG_USB_PD_CUSTOM_DBGACC + pd_port->custom_dbgacc = false; +#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */ + + switch (pd_event->msg_sec) { + case TYPEC_ATTACHED_DBGACC_SNK: +#ifdef CONFIG_USB_PD_CUSTOM_DBGACC + pd_port->custom_dbgacc = true; + startup_state = PE_DBG_READY; + break; +#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */ + case TYPEC_ATTACHED_SNK: + startup_state = PE_SNK_STARTUP; + break; + + case TYPEC_ATTACHED_SRC: + act_as_sink = false; + startup_state = PE_SRC_STARTUP; + break; + } + + /* At least > 4 for Ellisys VNDI PR_SWAP */ +#ifdef CONFIG_USB_PD_ERROR_RECOVERY_ONCE + if (pd_port->error_recovery_once > 4) + startup_state = PE_ERROR_RECOVERY_ONCE; +#endif /* CONFIG_USB_PD_ERROR_RECOVERY_ONCE */ + + pd_init_message_hdr(pd_port, act_as_sink); + return startup_state; +} + +static inline bool pe_transit_startup_state( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + uint8_t startup_state = + pe_get_startup_state(pd_port, pd_event); + + if (startup_state == 0xff) + return false; + + pd_dpm_notify_pe_startup(pd_port); + PE_TRANSIT_STATE(pd_port, startup_state); + + return true; +} + +enum { + TII_TRAP_IN_IDLE = 0, + TII_TRANSIT_STATE = 1, + TII_PE_RUNNING = 2, +}; + +static inline uint8_t pe_check_trap_in_idle_state( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + switch (pd_port->pe_pd_state) { + case PE_IDLE1: + case PE_ERROR_RECOVERY: + if (pd_event_pe_msg_match(pd_event, PD_PE_IDLE)) { + PE_TRANSIT_STATE(pd_port, PE_IDLE2); + return TII_TRANSIT_STATE; + } + + pd_try_put_pe_idle_event(pd_port); + break; + + case PE_IDLE2: + if (pd_event_hw_msg_match(pd_event, PD_HW_CC_ATTACHED)) { + if (pe_transit_startup_state(pd_port, pd_event)) + return TII_TRANSIT_STATE; + } + + /* The original IDLE2 may trigger by PE_IDLE_TOUT */ + if (pd_event_hw_msg_match(pd_event, PD_HW_CC_DETACHED)) + pd_notify_pe_idle(pd_port); + break; + + default: + if (pd_event_hw_msg_match(pd_event, PD_HW_CC_DETACHED)) { + PE_TRANSIT_STATE(pd_port, PE_IDLE1); + return TII_TRANSIT_STATE; + } + return TII_PE_RUNNING; + } + + PE_DBG("Trap in idle state, Ignore All MSG (%d:%d)\n", + pd_event->event_type, pd_event->msg); + return TII_TRAP_IN_IDLE; +} + +static inline void pe_init_curr_state(struct pd_port *pd_port) +{ + if (pd_port->power_role == PD_ROLE_SINK) { + pd_port->curr_ready_state = PE_SNK_READY; + pd_port->curr_hreset_state = PE_SNK_HARD_RESET; + pd_port->curr_sreset_state = PE_SNK_SEND_SOFT_RESET; + } else { + pd_port->curr_ready_state = PE_SRC_READY; + pd_port->curr_hreset_state = PE_SRC_HARD_RESET; + pd_port->curr_sreset_state = PE_SRC_SEND_SOFT_RESET; + } + + pd_port->curr_unsupported_msg = false; + +#ifdef CONFIG_USB_PD_CUSTOM_DBGACC + if (pd_port->custom_dbgacc) + pd_port->curr_ready_state = PE_DBG_READY; +#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */ +} + +bool pd_process_event( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + bool ret = false; + struct pd_msg *pd_msg = pd_event->pd_msg; + uint8_t tii = pe_check_trap_in_idle_state(pd_port, pd_event); + int rv = 0; + uint32_t chip_id = 0; + + if (tii < TII_PE_RUNNING) + return tii; + + pe_init_curr_state(pd_port); + + if (pd_event->event_type == PD_EVT_PD_MSG) + pe_translate_pd_msg_event(pd_port, pd_event, pd_msg); + +#if PE_EVT_INFO_VDM_DIS + if (!pd_curr_is_vdm_evt(pd_port)) +#endif + print_event(pd_port, pd_event); + + if ((pd_event->event_type < PD_EVT_PD_MSG_END) && (pd_msg != NULL)) { + + if (!pe_is_valid_pd_msg_id(pd_port, pd_event, pd_msg)) + return false; + + if (!pe_is_valid_pd_msg_role(pd_port, pd_event, pd_msg)) { + PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY); + return true; + } + rv = tcpci_get_chip_id(pd_port->tcpc, &chip_id); + if (!rv && (SC2150A_DID == chip_id) &&pd_port->miss_msg) { + if (pd_port->pe_pd_state == PE_SNK_TRANSITION_SINK) { + pd_add_miss_msg(pd_port,pd_event,PD_CTRL_PS_RDY); + } else if (pd_port->pe_pd_state == PE_SNK_SELECT_CAPABILITY){ + switch (pd_event->msg) { + case PD_CTRL_PS_RDY: + pd_add_miss_msg(pd_port,pd_event,PD_CTRL_ACCEPT); + break; + case PD_DATA_SOURCE_CAP: + pd_add_miss_msg(pd_port,pd_event,PD_CTRL_REJECT); + break; + } + } + pd_port->miss_msg = false; + return false; + } + } + + pd_copy_msg_data_from_evt(pd_port, pd_event); + + if (pd_curr_is_vdm_evt(pd_port)) + return pd_process_event_vdm(pd_port, pd_event); + + if (pd_event->event_type == PD_EVT_TCP_MSG) + return pd_process_event_tcp(pd_port, pd_event); + +#ifdef CONFIG_USB_PD_CUSTOM_DBGACC + if (pd_port->custom_dbgacc) + return pd_process_event_dbg(pd_port, pd_event); +#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */ + + if ((pd_event->event_type == PD_EVT_CTRL_MSG) && + (pd_event->msg != PD_CTRL_GOOD_CRC) && + (pd_msg != NULL) && (pd_msg->frame_type != TCPC_TX_SOP)) + return pd_process_event_cable(pd_port, pd_event); + + if (pd_process_event_com(pd_port, pd_event)) + return true; + + switch (pd_port->state_machine) { +#ifdef CONFIG_USB_PD_DR_SWAP + case PE_STATE_MACHINE_DR_SWAP: + ret = pd_process_event_drs(pd_port, pd_event); + break; +#endif /* CONFIG_USB_PD_DR_SWAP */ + +#ifdef CONFIG_USB_PD_PR_SWAP + case PE_STATE_MACHINE_PR_SWAP: + ret = pd_process_event_prs(pd_port, pd_event); + break; +#endif /* CONFIG_USB_PD_PR_SWAP */ + +#ifdef CONFIG_USB_PD_VCONN_SWAP + case PE_STATE_MACHINE_VCONN_SWAP: + ret = pd_process_event_vcs(pd_port, pd_event); + break; +#endif /* CONFIG_USB_PD_VCONN_SWAP */ + } + + if (ret) + return true; + + if (pd_port->power_role == PD_ROLE_SINK) + ret = pd_process_event_snk(pd_port, pd_event); + else + ret = pd_process_event_src(pd_port, pd_event); + + return ret; +} diff --git a/drivers/usb/typec/mmi_tcpc/pd_process_evt_com.c b/drivers/usb/typec/mmi_tcpc/pd_process_evt_com.c new file mode 100644 index 000000000000..85fd086033d3 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_process_evt_com.c @@ -0,0 +1,665 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Policy Engine for Common + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/tcpci_event.h" +#include "inc/pd_process_evt.h" +#include "inc/pd_dpm_core.h" + +/* + * [BLOCK] DRP (dr_swap, pr_swap, vconn_swap) + */ + +#ifdef CONFIG_USB_PD_DR_SWAP +static inline bool pd_evaluate_reject_dr_swap(struct pd_port *pd_port) +{ + if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_DATA) { + if (pd_port->power_role == PD_ROLE_DFP) + return pd_port->dpm_caps & + DPM_CAP_DR_SWAP_REJECT_AS_UFP; + return pd_port->dpm_caps & DPM_CAP_DR_SWAP_REJECT_AS_DFP; + } + + return true; +} +#endif /* CONFIG_USB_PD_DR_SWAP */ + +#ifdef CONFIG_USB_PD_PR_SWAP +static inline bool pd_evaluate_reject_pr_swap(struct pd_port *pd_port) +{ + if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) { + if (pd_port->power_role == PD_ROLE_SOURCE) + return pd_port->dpm_caps & + DPM_CAP_PR_SWAP_REJECT_AS_SNK; + return pd_port->dpm_caps & DPM_CAP_PR_SWAP_REJECT_AS_SRC; + } + + return true; +} +#endif /* CONFIG_USB_PD_PR_SWAP */ + +#ifdef CONFIG_USB_PD_VCONN_SWAP +static inline bool pd_evaluate_accept_vconn_swap(struct pd_port *pd_port) +{ + if (pd_port->dpm_caps & DPM_CAP_LOCAL_VCONN_SUPPLY) + return true; + + return false; +} +#endif /* CONFIG_USB_PD_VCONN_SWAP */ + +static inline bool pd_process_ctrl_msg_dr_swap( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + if (pd_port->pe_data.modal_operation) { + pe_transit_hard_reset_state(pd_port); + return true; + } + +#ifdef CONFIG_USB_PD_DR_SWAP + if (!pd_check_pe_state_ready(pd_port)) + return false; + + if (!pd_evaluate_reject_dr_swap(pd_port)) { + pd_port->pe_data.during_swap = false; + pd_port->state_machine = PE_STATE_MACHINE_DR_SWAP; + + PE_TRANSIT_DATA_STATE(pd_port, + PE_DRS_UFP_DFP_EVALUATE_DR_SWAP, + PE_DRS_DFP_UFP_EVALUATE_DR_SWAP); + return true; + } +#endif /* CONFIG_USB_PD_DR_SWAP */ + + PE_TRANSIT_STATE(pd_port, PE_REJECT); + return true; +} + +static inline bool pd_process_ctrl_msg_pr_swap( + struct pd_port *pd_port, struct pd_event *pd_event) +{ +#ifdef CONFIG_USB_PD_PR_SWAP + if (!pd_evaluate_reject_pr_swap(pd_port)) { + pd_port->pe_data.during_swap = false; + pd_port->state_machine = PE_STATE_MACHINE_PR_SWAP; + pe_transit_evaluate_pr_swap_state(pd_port); + return true; + } +#endif /* CONFIG_USB_PD_PR_SWAP */ + + PE_TRANSIT_STATE(pd_port, PE_REJECT); + return true; +} + +static inline bool pd_process_ctrl_msg_vconn_swap( + struct pd_port *pd_port, struct pd_event *pd_event) +{ +#ifdef CONFIG_USB_PD_VCONN_SWAP + if (!pd_check_pe_state_ready(pd_port)) + return false; + + if (pd_evaluate_accept_vconn_swap(pd_port)) { + pd_port->state_machine = PE_STATE_MACHINE_VCONN_SWAP; + PE_TRANSIT_STATE(pd_port, PE_VCS_EVALUATE_SWAP); + return true; + } +#endif /* CONFIG_USB_PD_VCONN_SWAP */ + + if (!pd_check_rev30(pd_port)) { + PE_TRANSIT_STATE(pd_port, PE_REJECT); + return true; + } + + return false; +} + +/* + * [BLOCK] BIST + */ + +static inline bool pd_process_data_msg_bist( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_port->request_v > 5000) { + PE_INFO("bist_not_vsafe5v\n"); + return false; + } + + switch (BDO_MODE(pd_event->pd_msg->payload[0])) { + case BDO_MODE_TEST_DATA: + PE_DBG("bist_test\n"); + PE_TRANSIT_STATE(pd_port, PE_BIST_TEST_DATA); + pd_noitfy_pe_bist_mode(pd_port, PD_BIST_MODE_TEST_DATA); + return true; + + case BDO_MODE_CARRIER2: + PE_DBG("bist_cm2\n"); + PE_TRANSIT_STATE(pd_port, PE_BIST_CARRIER_MODE_2); + pd_noitfy_pe_bist_mode(pd_port, PD_BIST_MODE_DISABLE); + return true; + + default: +#if 0 + case BDO_MODE_RECV: + case BDO_MODE_TRANSMIT: + case BDO_MODE_COUNTERS: + case BDO_MODE_CARRIER0: + case BDO_MODE_CARRIER1: + case BDO_MODE_CARRIER3: + case BDO_MODE_EYE: +#endif + PE_DBG("Unsupport BIST\n"); + pd_noitfy_pe_bist_mode(pd_port, PD_BIST_MODE_DISABLE); + return false; + } + + return false; +} + +/* + * [BLOCK] Porcess Ctrl MSG + */ + +static void pd_cancel_dpm_reaction(struct pd_port *pd_port) +{ + if (pd_port->pe_data.dpm_reaction_id < DPM_REACTION_REJECT_CANCEL) + dpm_reaction_clear(pd_port, pd_port->pe_data.dpm_reaction_id); +} + +static bool pd_process_ctrl_msg_wait_reject(struct pd_port *pd_port) +{ + pd_cancel_dpm_reaction(pd_port); + + if (pd_port->state_machine == PE_STATE_MACHINE_PR_SWAP) + pd_notify_pe_cancel_pr_swap(pd_port); + + pe_transit_ready_state(pd_port); + return true; +} + +static inline bool pd_process_ctrl_msg_wait(struct pd_port *pd_port) +{ + + return pd_process_ctrl_msg_wait_reject(pd_port); +} + +static inline bool pd_process_ctrl_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + bool ret = false; + +#ifdef CONFIG_USB_PD_REV30 + if (!pd_check_rev30(pd_port) && + pd_event->msg >= PD_CTRL_PD30_START) { + pd_event->msg = PD_CTRL_MSG_NR; + return false; + } +#endif /* CONFIG_USB_PD_REV30 */ + + switch (pd_event->msg) { + case PD_CTRL_GOOD_CRC: + if (pd_port->pe_data.pe_state_flags & + PE_STATE_FLAG_ENABLE_SENDER_RESPONSE_TIMER) + pd_enable_timer(pd_port, PD_TIMER_SENDER_RESPONSE); + + if (pd_port->pe_data.pe_state_flags2 & + PE_STATE_FLAG_BACK_READY_IF_RECV_GOOD_CRC) { + pe_transit_ready_state(pd_port); + return true; + } + break; + + case PD_CTRL_REJECT: + pd_notify_tcp_event_2nd_result(pd_port, TCP_DPM_RET_REJECT); + + if (pd_port->pe_data.pe_state_flags & + PE_STATE_FLAG_BACK_READY_IF_RECV_REJECT) { + return pd_process_ctrl_msg_wait_reject(pd_port); + } + break; + case PD_CTRL_WAIT: + pd_notify_tcp_event_2nd_result(pd_port, TCP_DPM_RET_WAIT); + + if (pd_port->pe_data.pe_state_flags & + PE_STATE_FLAG_BACK_READY_IF_RECV_WAIT) { + return pd_process_ctrl_msg_wait(pd_port); + } + break; + + case PD_CTRL_SOFT_RESET: + if (!pd_port->pe_data.during_swap && + !pd_check_pe_during_hard_reset(pd_port)) { + pe_transit_soft_reset_recv_state(pd_port); + return true; + } + break; + + /* Swap */ + case PD_CTRL_DR_SWAP: + ret = pd_process_ctrl_msg_dr_swap(pd_port, pd_event); + break; + + case PD_CTRL_PR_SWAP: + ret = pd_process_ctrl_msg_pr_swap(pd_port, pd_event); + break; + + case PD_CTRL_VCONN_SWAP: + ret = pd_process_ctrl_msg_vconn_swap(pd_port, pd_event); + break; + +#ifdef CONFIG_USB_PD_REV30 + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL + case PD_CTRL_GET_COUNTRY_CODE: + if (pd_port->country_nr) { + ret = PE_MAKE_STATE_TRANSIT_SINGLE( + pe_get_curr_ready_state(pd_port), + PE_GIVE_COUNTRY_CODES); + } + break; +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */ + + case PD_CTRL_NOT_SUPPORTED: + pd_cancel_dpm_reaction(pd_port); + pd_notify_tcp_event_2nd_result( + pd_port, TCP_DPM_RET_NOT_SUPPORT); + + if (pd_port->pe_data.pe_state_flags & + PE_STATE_FLAG_BACK_READY_IF_SR_TIMER_TOUT) { + pe_transit_ready_state(pd_port); + return true; + } else if (pd_port->pe_data.vdm_state_timer) { + vdm_put_pe_event( + pd_port->tcpc, PD_PE_VDM_NOT_SUPPORT); + } + break; +#endif /* CONFIG_USB_PD_REV30 */ + } + + return ret; +} + +/* + * [BLOCK] Porcess Data MSG + */ + +static inline bool pd_process_data_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + bool ret = false; + uint8_t ready_state = pe_get_curr_ready_state(pd_port); + +#ifdef CONFIG_USB_PD_REV30 + if (!pd_check_rev30(pd_port) && + pd_event->msg >= PD_DATA_PD30_START) { + pd_event->msg = PD_DATA_MSG_NR; + return false; + } +#endif /* CONFIG_USB_PD_REV30 */ + + switch (pd_event->msg) { + case PD_DATA_BIST: + if (pd_port->pe_state_curr == ready_state) + ret = pd_process_data_msg_bist(pd_port, pd_event); + break; + +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE + case PD_DATA_BAT_STATUS: + ret = PE_MAKE_STATE_TRANSIT_SINGLE( + PE_GET_BATTERY_STATUS, ready_state); + break; +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL + case PD_DATA_GET_COUNTRY_INFO: + if (pd_port->country_nr) { + ret = PE_MAKE_STATE_TRANSIT_SINGLE( + ready_state, PE_GIVE_COUNTRY_INFO); + } + break; +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_LOCAL */ +#endif /* CONFIG_USB_PD_REV30 */ + } + + return ret; +} + +/* + * [BLOCK] Porcess Extend MSG + */ + +#ifdef CONFIG_USB_PD_REV30 +static inline bool pd_process_ext_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + bool ret = false; + uint8_t ready_state = pe_get_curr_ready_state(pd_port); + + if (!pd_check_rev30(pd_port)) { + pd_event->msg = PD_DATA_MSG_NR; + return false; + } + +#ifndef CONFIG_USB_PD_REV30_CHUNKING_BY_PE + if (pd_port->pe_state_curr == ready_state && + pd_is_multi_chunk_msg(pd_port)) { + pd_port->curr_unsupported_msg = true; + return pd_process_protocol_error(pd_port, pd_event); + } +#endif /* CONFIG_USB_PD_REV30_CHUNKING_BY_PE */ + + switch (pd_event->msg) { + +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_LOCAL + case PD_EXT_GET_BAT_CAP: + if (pd_port->bat_nr) { + ret = PE_MAKE_STATE_TRANSIT_SINGLE( + ready_state, PE_GIVE_BATTERY_CAP); + } + break; +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL + case PD_EXT_GET_BAT_STATUS: + if (pd_port->bat_nr) { + ret = PE_MAKE_STATE_TRANSIT_SINGLE( + ready_state, PE_GIVE_BATTERY_STATUS); + } + break; +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE + case PD_EXT_BAT_CAP: + ret = PE_MAKE_STATE_TRANSIT_SINGLE( + PE_GET_BATTERY_CAP, ready_state); + break; +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL + case PD_EXT_GET_MFR_INFO: + ret = PE_MAKE_STATE_TRANSIT_SINGLE( + ready_state, PE_GIVE_MANUFACTURER_INFO); + break; +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE + case PD_EXT_MFR_INFO: + ret = PE_MAKE_STATE_TRANSIT_SINGLE( + PE_GET_MANUFACTURER_INFO, ready_state); + break; +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE + case PD_EXT_COUNTRY_INFO: + ret = PE_MAKE_STATE_TRANSIT_SINGLE( + PE_GET_COUNTRY_INFO, ready_state); + break; +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE + case PD_EXT_COUNTRY_CODES: + ret = PE_MAKE_STATE_TRANSIT_SINGLE( + PE_GET_COUNTRY_CODES, ready_state); + break; +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */ + } + + return ret; +} +#endif /* CONFIG_USB_PD_REV30 */ + +/* + * [BLOCK] Porcess DPM MSG + */ + +static inline bool pd_process_dpm_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + bool ret = false; + + switch (pd_event->msg) { + case PD_DPM_ACK: + if (pd_port->pe_data.pe_state_flags2 & + PE_STATE_FLAG_BACK_READY_IF_DPM_ACK) { + pe_transit_ready_state(pd_port); + return true; + } + break; + +#ifdef CONFIG_USB_PD_REV30 + case PD_DPM_NOT_SUPPORT: + if (pd_check_rev30(pd_port)) { + PE_TRANSIT_STATE(pd_port, PE_VDM_NOT_SUPPORTED); + return true; + } + break; +#endif /* CONFIG_USB_PD_REV30 */ + } + + return ret; +} + +/* + * [BLOCK] Porcess HW MSG + */ + +static inline bool pd_process_recv_hard_reset( + struct pd_port *pd_port, struct pd_event *pd_event) +{ +#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER + if (pd_port->pe_data.recv_hard_reset_count > PD_HARD_RESET_COUNT) { + PE_TRANSIT_STATE(pd_port, PE_OVER_RECV_HRESET_LIMIT); + return true; + } + + pd_port->pe_data.recv_hard_reset_count++; +#endif /* CONFIG_USB_PD_RECV_HRESET_COUNTER */ + +#ifdef CONFIG_USB_PD_RENEGOTIATION_COUNTER + if (pd_check_pe_during_hard_reset(pd_port)) + pd_port->pe_data.renegotiation_count++; +#endif /* CONFIG_USB_PD_RENEGOTIATION_COUNTER */ + + pe_transit_hard_reset_recv_state(pd_port); + return true; +} + +static inline bool pd_process_hw_msg_tx_failed( + struct pd_port *pd_port, struct pd_event *pd_event) +{ +#ifdef CONFIG_USB_PD_RENEGOTIATION_COUNTER + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_port->pe_data.renegotiation_count > PD_HARD_RESET_COUNT) { + PE_INFO("renegotiation failed\n"); + PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY); + return true; + } +#endif /* CONFIG_USB_PD_RENEGOTIATION_COUNTER */ + + if (pd_port->pe_data.pe_state_flags & + PE_STATE_FLAG_BACK_READY_IF_TX_FAILED) { + pd_notify_tcp_event_2nd_result( + pd_port, TCP_DPM_RET_NO_RESPONSE); + pe_transit_ready_state(pd_port); + return true; + } else if (pd_port->pe_data.pe_state_flags & + PE_STATE_FLAG_HRESET_IF_TX_FAILED) { + pe_transit_hard_reset_state(pd_port); + return true; + } + + return false; +} + +static inline bool pd_process_hw_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_HW_RECV_HARD_RESET: + return pd_process_recv_hard_reset(pd_port, pd_event); + + case PD_HW_TX_FAILED: + return pd_process_hw_msg_tx_failed(pd_port, pd_event); + + default: + return false; + }; +} + +/* + * [BLOCK] Porcess Timer MSG + */ + +#ifdef CONFIG_USB_PD_CHECK_RX_PENDING_IF_SRTOUT +static inline bool pd_check_rx_pending(struct pd_port *pd_port) +{ + uint32_t alert; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (tcpci_get_alert_status(tcpc, &alert)) + return false; + + if (alert & TCPC_REG_ALERT_RX_STATUS) { + PE_INFO("rx_pending\n"); +#ifndef CONFIG_USB_PD_ONLY_PRINT_SYSTEM_BUSY + pd_enable_timer(pd_port, PD_TIMER_SENDER_RESPONSE); +#endif + return true; + } + + return false; +} +#endif /* CONFIG_USB_PD_CHECK_RX_PENDING_IF_SRTOUT */ + +static inline bool pd_process_timer_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + uint8_t ready_state = pe_get_curr_ready_state(pd_port); + + switch (pd_event->msg) { +#ifndef CONFIG_USB_PD_DBG_IGRONE_TIMEOUT + case PD_TIMER_SENDER_RESPONSE: + +#ifdef CONFIG_USB_PD_CHECK_RX_PENDING_IF_SRTOUT +#ifndef CONFIG_USB_PD_ONLY_PRINT_SYSTEM_BUSY + if (pd_check_rx_pending(pd_port)) + return false; +#else + pd_check_rx_pending(pd_port); +#endif /* CONFIG_USB_PD_PRINT_SYSTEM_BUSY */ +#endif /* CONFIG_USB_PD_CHECK_RX_PENDING_IF_SRTOUT */ + + + pd_cancel_dpm_reaction(pd_port); + pd_notify_pe_cancel_pr_swap(pd_port); + pd_notify_tcp_event_2nd_result(pd_port, TCP_DPM_RET_TIMEOUT); + if (pd_port->pe_data.pe_state_flags & + PE_STATE_FLAG_BACK_READY_IF_SR_TIMER_TOUT) { + PE_TRANSIT_STATE(pd_port, ready_state); + return true; + } + + if (pd_port->pe_data.pe_state_flags & + PE_STATE_FLAG_HRESET_IF_SR_TIMEOUT) { + pe_transit_hard_reset_state(pd_port); + return true; + } + break; +#endif /* CONFIG_USB_PD_DBG_IGRONE_TIMEOUT */ + case PD_TIMER_BIST_CONT_MODE: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_BIST_CARRIER_MODE_2, ready_state)) + return true; + break; + +#ifdef CONFIG_USB_PD_DFP_FLOW_DELAY + case PD_TIMER_DFP_FLOW_DELAY: + if (pd_port->pe_state_curr == ready_state && + pd_port->data_role == PD_ROLE_DFP) { + dpm_reaction_set_clear(pd_port, + DPM_REACTION_CAP_READY_ONCE, + DPM_REACTION_DFP_FLOW_DELAY); + } + break; +#endif /* CONFIG_USB_PD_DFP_FLOW_DELAY */ + +#ifdef CONFIG_USB_PD_UFP_FLOW_DELAY + case PD_TIMER_UFP_FLOW_DELAY: + if (pd_port->pe_state_curr == ready_state && + pd_port->data_role == PD_ROLE_UFP) { + dpm_reaction_set_clear(pd_port, + DPM_REACTION_CAP_READY_ONCE, + DPM_REACTION_UFP_FLOW_DELAY); + } + break; +#endif /* CONFIG_USB_PD_UFP_FLOW_DELAY */ + +#ifdef CONFIG_USB_PD_VCONN_STABLE_DELAY + case PD_TIMER_VCONN_STABLE: + if (pd_port->vconn_role == PD_ROLE_VCONN_DYNAMIC_ON) { + pd_set_vconn(pd_port, PD_ROLE_VCONN_ON); + dpm_reaction_set_clear(pd_port, + DPM_REACTION_CAP_READY_ONCE, + DPM_REACTION_VCONN_STABLE_DELAY); + } + break; +#endif /* CONFIG_USB_PD_VCONN_STABLE_DELAY */ + +#if defined(CONFIG_USB_PD_REV30) && defined(CONFIG_USB_PD_REV30_COLLISION_AVOID) + case PD_TIMER_DEFERRED_EVT: + pd_notify_tcp_event_buf_reset( + pd_port, TCP_DPM_RET_DROP_PE_BUSY); + break; +#endif + + default: + break; + } + + return false; +} + +bool pd_process_event_com( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->event_type) { + case PD_EVT_CTRL_MSG: + return pd_process_ctrl_msg(pd_port, pd_event); + + case PD_EVT_DATA_MSG: + return pd_process_data_msg(pd_port, pd_event); + +#ifdef CONFIG_USB_PD_REV30 + case PD_EVT_EXT_MSG: + return pd_process_ext_msg(pd_port, pd_event); +#endif /* CONFIG_USB_PD_REV30 */ + + case PD_EVT_DPM_MSG: + return pd_process_dpm_msg(pd_port, pd_event); + + case PD_EVT_HW_MSG: + return pd_process_hw_msg(pd_port, pd_event); + + case PD_EVT_TIMER_MSG: + return pd_process_timer_msg(pd_port, pd_event); + + default: + return false; + } +} diff --git a/drivers/usb/typec/mmi_tcpc/pd_process_evt_dbg.c b/drivers/usb/typec/mmi_tcpc/pd_process_evt_dbg.c new file mode 100644 index 000000000000..928cb70d1ba3 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_process_evt_dbg.c @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Process Event For DBGACC + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/tcpci_event.h" +#include "inc/pd_process_evt.h" + +#ifdef CONFIG_USB_PD_CUSTOM_DBGACC + +bool pd_process_event_dbg(struct pd_port *pd_port, struct pd_event *pd_event) +{ + /* Don't need to handle any PD message, Keep VBUS 5V, and using VDM */ + return false; +} + +#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */ diff --git a/drivers/usb/typec/mmi_tcpc/pd_process_evt_drs.c b/drivers/usb/typec/mmi_tcpc/pd_process_evt_drs.c new file mode 100644 index 000000000000..b2a9bcaddcd9 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_process_evt_drs.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Process Event For DRS + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/tcpci_event.h" +#include "inc/pd_process_evt.h" + +/* PD Control MSG reactions */ + +DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_GOOD_CRC) = { + { PE_DRS_DFP_UFP_ACCEPT_DR_SWAP, PE_DRS_DFP_UFP_CHANGE_TO_UFP }, + { PE_DRS_UFP_DFP_ACCEPT_DR_SWAP, PE_DRS_UFP_DFP_CHANGE_TO_DFP }, +}; +DECL_PE_STATE_REACTION(PD_CTRL_MSG_GOOD_CRC); + +DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_ACCEPT) = { + { PE_DRS_DFP_UFP_SEND_DR_SWAP, PE_DRS_DFP_UFP_CHANGE_TO_UFP }, + { PE_DRS_UFP_DFP_SEND_DR_SWAP, PE_DRS_UFP_DFP_CHANGE_TO_DFP }, +}; +DECL_PE_STATE_REACTION(PD_CTRL_MSG_ACCEPT); + +/* DPM Event reactions */ + +DECL_PE_STATE_TRANSITION(PD_DPM_MSG_ACK) = { + { PE_DRS_DFP_UFP_EVALUATE_DR_SWAP, PE_DRS_DFP_UFP_ACCEPT_DR_SWAP }, + { PE_DRS_UFP_DFP_EVALUATE_DR_SWAP, PE_DRS_UFP_DFP_ACCEPT_DR_SWAP }, +}; +DECL_PE_STATE_REACTION(PD_DPM_MSG_ACK); + +DECL_PE_STATE_TRANSITION(PD_DPM_MSG_NAK) = { + { PE_DRS_DFP_UFP_EVALUATE_DR_SWAP, PE_DRS_DFP_UFP_REJECT_DR_SWAP }, + { PE_DRS_UFP_DFP_EVALUATE_DR_SWAP, PE_DRS_UFP_DFP_REJECT_DR_SWAP }, +}; +DECL_PE_STATE_REACTION(PD_DPM_MSG_NAK); + + +/* + * [BLOCK] Porcess PD Ctrl MSG + */ + +static inline bool pd_process_ctrl_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_CTRL_GOOD_CRC: + return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_GOOD_CRC); + + case PD_CTRL_ACCEPT: + return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_ACCEPT); + + default: + return false; + } +} + +/* + * [BLOCK] Porcess DPM MSG + */ + +static inline bool pd_process_dpm_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_DPM_ACK: + return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_ACK); + + case PD_DPM_NAK: + return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_NAK); + } + + return false; +} + +/* + * [BLOCK] Process Policy Engine's DRS Message + */ + +bool pd_process_event_drs(struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->event_type) { + case PD_EVT_CTRL_MSG: + return pd_process_ctrl_msg(pd_port, pd_event); + + case PD_EVT_DPM_MSG: + return pd_process_dpm_msg(pd_port, pd_event); + + default: + return false; + } +} diff --git a/drivers/usb/typec/mmi_tcpc/pd_process_evt_prs.c b/drivers/usb/typec/mmi_tcpc/pd_process_evt_prs.c new file mode 100644 index 000000000000..b6e34b93918a --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_process_evt_prs.c @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Process Event For PRS + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/tcpci_event.h" +#include "inc/pd_process_evt.h" +#include "inc/pd_dpm_core.h" + +#ifdef CONFIG_USB_PD_PR_SWAP_ERROR_RECOVERY +#define PE_PRS_SNK_HARD_RESET PE_ERROR_RECOVERY +#define PE_PRS_SRC_HARD_RESET PE_ERROR_RECOVERY +#else +#define PE_PRS_SNK_HARD_RESET PE_SNK_HARD_RESET +#define PE_PRS_SRC_HARD_RESET PE_SRC_HARD_RESET +#endif /* CONFIG_USB_PD_PR_SWAP_ERROR_RECOVERY */ + +/* PD Control MSG reactions */ + +DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_GOOD_CRC) = { + { PE_PRS_SRC_SNK_ACCEPT_PR_SWAP, PE_PRS_SRC_SNK_TRANSITION_TO_OFF }, + { PE_PRS_SNK_SRC_ACCEPT_PR_SWAP, PE_PRS_SNK_SRC_TRANSITION_TO_OFF }, + + /* VBUS-ON & PS_RDY SENT */ + { PE_PRS_SNK_SRC_SOURCE_ON, PE_SRC_STARTUP }, +}; +DECL_PE_STATE_REACTION(PD_CTRL_MSG_GOOD_CRC); + +DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_ACCEPT) = { + { PE_PRS_SRC_SNK_SEND_SWAP, PE_PRS_SRC_SNK_TRANSITION_TO_OFF }, + { PE_PRS_SNK_SRC_SEND_SWAP, PE_PRS_SNK_SRC_TRANSITION_TO_OFF }, +}; +DECL_PE_STATE_REACTION(PD_CTRL_MSG_ACCEPT); + +DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_PS_RDY) = { + { PE_PRS_SRC_SNK_WAIT_SOURCE_ON, PE_SNK_STARTUP }, + { PE_PRS_SNK_SRC_TRANSITION_TO_OFF, PE_PRS_SNK_SRC_ASSERT_RP }, +}; +DECL_PE_STATE_REACTION(PD_CTRL_MSG_PS_RDY); + +/* DPM Event reactions */ + +DECL_PE_STATE_TRANSITION(PD_DPM_MSG_ACK) = { + { PE_PRS_SRC_SNK_EVALUATE_PR_SWAP, PE_PRS_SRC_SNK_ACCEPT_PR_SWAP }, + { PE_PRS_SNK_SRC_EVALUATE_PR_SWAP, PE_PRS_SNK_SRC_ACCEPT_PR_SWAP }, + + { PE_PRS_SRC_SNK_ASSERT_RD, PE_PRS_SRC_SNK_WAIT_SOURCE_ON }, + { PE_PRS_SNK_SRC_ASSERT_RP, PE_PRS_SNK_SRC_SOURCE_ON }, +}; +DECL_PE_STATE_REACTION(PD_DPM_MSG_ACK); + +DECL_PE_STATE_TRANSITION(PD_DPM_MSG_NAK) = { + { PE_PRS_SRC_SNK_EVALUATE_PR_SWAP, PE_PRS_SRC_SNK_REJECT_PR_SWAP }, + { PE_PRS_SNK_SRC_EVALUATE_PR_SWAP, PE_PRS_SNK_SRC_REJECT_SWAP }, +}; +DECL_PE_STATE_REACTION(PD_DPM_MSG_NAK); + +/* HW Event reactions */ + +DECL_PE_STATE_TRANSITION(PD_HW_VBUS_PRESENT) = { +#ifdef CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP + { PE_PRS_SRC_SNK_WAIT_SOURCE_ON, PE_SNK_STARTUP }, +#endif /* CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP */ +}; +DECL_PE_STATE_REACTION(PD_HW_VBUS_PRESENT); + +DECL_PE_STATE_TRANSITION(PD_HW_TX_FAILED) = { + { PE_PRS_SRC_SNK_WAIT_SOURCE_ON, PE_PRS_SNK_HARD_RESET }, + { PE_PRS_SNK_SRC_SOURCE_ON, PE_PRS_SRC_HARD_RESET }, +}; +DECL_PE_STATE_REACTION(PD_HW_TX_FAILED); + +DECL_PE_STATE_TRANSITION(PD_HW_VBUS_SAFE0V) = { + { PE_PRS_SRC_SNK_TRANSITION_TO_OFF, PE_PRS_SRC_SNK_ASSERT_RD }, +#ifdef CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP + { PE_PRS_SNK_SRC_TRANSITION_TO_OFF, PE_PRS_SNK_SRC_ASSERT_RP }, +#endif /* CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP */ +}; +DECL_PE_STATE_REACTION(PD_HW_VBUS_SAFE0V); + +/* + * [BLOCK] Porcess PD Ctrl MSG + */ + +static inline bool pd_process_ctrl_msg_good_crc( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_port->pe_state_curr) { + case PE_PRS_SRC_SNK_WAIT_SOURCE_ON: + pd_enable_pe_state_timer(pd_port, PD_TIMER_PS_SOURCE_ON); + pd_unlock_msg_output(pd_port); /* for tSRCTransition */ + return false; + + default: + return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_GOOD_CRC); + } +} + +static inline bool pd_process_ctrl_msg_ps_rdy( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_port->pe_state_curr) { +#ifdef CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP + case PE_PRS_SRC_SNK_WAIT_SOURCE_ON: + pd_enable_vbus_valid_detection(pd_port, true); + return false; + + case PE_PRS_SNK_SRC_TRANSITION_TO_OFF: + pd_enable_vbus_safe0v_detection(pd_port); + return false; + +#endif /* CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP */ + default: + return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_PS_RDY); + } +} + +static inline bool pd_process_ctrl_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_CTRL_GOOD_CRC: + return pd_process_ctrl_msg_good_crc(pd_port, pd_event); + + case PD_CTRL_ACCEPT: + return PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_ACCEPT); + + case PD_CTRL_PS_RDY: + return pd_process_ctrl_msg_ps_rdy(pd_port, pd_event); + + default: + return false; + } +} + +/* + * [BLOCK] Porcess DPM MSG + */ + +static inline bool pd_process_dpm_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_DPM_ACK: + return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_ACK); + + case PD_DPM_NAK: + return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_NAK); + } + + return false; +} + +/* + * [BLOCK] Porcess HW MSG + */ + +static inline bool pd_process_hw_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_HW_VBUS_PRESENT: + if (pd_port->pe_state_curr == PE_PRS_SNK_SRC_SOURCE_ON) + pd_send_sop_ctrl_msg(pd_port, PD_CTRL_PS_RDY); + + return PE_MAKE_STATE_TRANSIT(PD_HW_VBUS_PRESENT); + + case PD_HW_TX_FAILED: + return PE_MAKE_STATE_TRANSIT(PD_HW_TX_FAILED); + + case PD_HW_VBUS_SAFE0V: + return PE_MAKE_STATE_TRANSIT(PD_HW_VBUS_SAFE0V); + + default: + return false; + } +} + +/* + * [BLOCK] Porcess Timer MSG + */ + +static inline bool pd_process_timer_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_TIMER_PS_SOURCE_ON: + return PE_MAKE_STATE_TRANSIT_SINGLE( + PE_PRS_SRC_SNK_WAIT_SOURCE_ON, PE_PRS_SNK_HARD_RESET); + + case PD_TIMER_PS_SOURCE_OFF: + return PE_MAKE_STATE_TRANSIT_SINGLE( + PE_PRS_SNK_SRC_TRANSITION_TO_OFF, + PE_PRS_SNK_HARD_RESET); + + case PD_TIMER_SOURCE_TRANSITION: + pd_dpm_prs_enable_power_source(pd_port, false); + return false; + + default: + return false; + } +} + +/* + * [BLOCK] Process Policy Engine's PRS Message + */ + +bool pd_process_event_prs(struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->event_type) { + case PD_EVT_CTRL_MSG: + return pd_process_ctrl_msg(pd_port, pd_event); + + case PD_EVT_DPM_MSG: + return pd_process_dpm_msg(pd_port, pd_event); + + case PD_EVT_HW_MSG: + return pd_process_hw_msg(pd_port, pd_event); + + case PD_EVT_TIMER_MSG: + return pd_process_timer_msg(pd_port, pd_event); + + default: + return false; + } +} diff --git a/drivers/usb/typec/mmi_tcpc/pd_process_evt_snk.c b/drivers/usb/typec/mmi_tcpc/pd_process_evt_snk.c new file mode 100644 index 000000000000..0a98711a6d12 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_process_evt_snk.c @@ -0,0 +1,495 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Process Event For SNK + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/pd_dpm_core.h" +#include "inc/tcpci_event.h" +#include "inc/pd_process_evt.h" +#include "inc/tcpci_typec.h" + +/* PD Control MSG reactions */ + +DECL_PE_STATE_TRANSITION(PD_CTRL_MSG_ACCEPT) = { + { PE_SNK_SELECT_CAPABILITY, PE_SNK_TRANSITION_SINK }, + { PE_SNK_SEND_SOFT_RESET, PE_SNK_WAIT_FOR_CAPABILITIES }, +}; +DECL_PE_STATE_REACTION(PD_CTRL_MSG_ACCEPT); + +/* PD Data MSG reactions */ + +DECL_PE_STATE_TRANSITION(PD_DATA_MSG_SOURCE_CAP) = { + { PE_SNK_WAIT_FOR_CAPABILITIES, PE_SNK_EVALUATE_CAPABILITY }, + { PE_SNK_READY, PE_SNK_EVALUATE_CAPABILITY }, + + /* PR-Swap issue (Check it later) */ + { PE_SNK_STARTUP, PE_SNK_EVALUATE_CAPABILITY }, + { PE_SNK_DISCOVERY, PE_SNK_EVALUATE_CAPABILITY }, + +#ifdef CONFIG_USB_PD_TCPM_CB_2ND + { PE_SNK_GET_SOURCE_CAP, PE_SNK_EVALUATE_CAPABILITY }, +#endif /* CONFIG_USB_PD_TCPM_CB_2ND */ +}; +DECL_PE_STATE_REACTION(PD_DATA_MSG_SOURCE_CAP); + +/* + * [BLOCK] Porcess Ctrl MSG + */ + +static bool pd_process_ctrl_msg_get_source_cap( + struct pd_port *pd_port, uint8_t next) +{ + if (pd_port->pe_state_curr != PE_SNK_READY) + return false; + +#ifdef CONFIG_USB_PD_PR_SWAP + if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) { + PE_TRANSIT_STATE(pd_port, next); + return true; + } +#endif /* CONFIG_USB_PD_PR_SWAP */ + + pd_port->curr_unsupported_msg = true; + + return false; +} + +static inline bool pd_process_ctrl_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ +#ifdef CONFIG_USB_PD_PARTNER_CTRL_MSG_FIRST + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + switch (pd_port->pe_state_curr) { + case PE_SNK_GET_SOURCE_CAP: + +#ifdef CONFIG_USB_PD_PR_SWAP + case PE_DR_SNK_GET_SINK_CAP: +#endif /* CONFIG_USB_PD_PR_SWAP */ + if (pd_event->msg >= PD_CTRL_GET_SOURCE_CAP && + pd_event->msg <= PD_CTRL_VCONN_SWAP) { + PE_DBG("Port Partner Request First\n"); + pd_port->pe_state_curr = PE_SNK_READY; + pd_disable_timer( + pd_port, PD_TIMER_SENDER_RESPONSE); + } + break; + } +#endif /* CONFIG_USB_PD_PARTNER_CTRL_MSG_FIRST */ + + switch (pd_event->msg) { + case PD_CTRL_GOOD_CRC: + return PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SNK_SOFT_RESET, PE_SNK_WAIT_FOR_CAPABILITIES); + + case PD_CTRL_GOTO_MIN: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SNK_READY, PE_SNK_TRANSITION_SINK)) + return true; + break; + + case PD_CTRL_ACCEPT: + if (PE_MAKE_STATE_TRANSIT(PD_CTRL_MSG_ACCEPT)) + return true; + break; + + case PD_CTRL_PS_RDY: + switch (pd_port->pe_state_curr) { + case PE_SNK_TRANSITION_SINK: + pd_dpm_snk_transition_power(pd_port); + PE_TRANSIT_STATE(pd_port, PE_SNK_READY); + return true; + +#ifdef CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP + case PE_PRS_SRC_SNK_WAIT_SOURCE_ON: + case PE_PRS_SNK_SRC_TRANSITION_TO_OFF: + return false; +#endif /* CONFIG_USB_PD_VBUS_DETECTION_DURING_PR_SWAP */ + default: + break; + } + break; + + case PD_CTRL_GET_SOURCE_CAP: + if (pd_process_ctrl_msg_get_source_cap( + pd_port, PE_DR_SNK_GIVE_SOURCE_CAP)) + return true; + break; + + case PD_CTRL_GET_SINK_CAP: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SNK_READY, PE_SNK_GIVE_SINK_CAP)) + return true; + break; + + case PD_CTRL_REJECT: + case PD_CTRL_WAIT: + if (pd_port->pe_state_curr == PE_SNK_SELECT_CAPABILITY) { + if (pd_port->pe_data.explicit_contract) + PE_TRANSIT_STATE(pd_port, PE_SNK_READY); + else { + PE_TRANSIT_STATE(pd_port, + PE_SNK_WAIT_FOR_CAPABILITIES); + } + return true; + } + break; + +#ifdef CONFIG_USB_PD_REV30 + case PD_CTRL_NOT_SUPPORTED: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SNK_READY, PE_SNK_NOT_SUPPORTED_RECEIVED)) + return true; + break; + +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL + case PD_CTRL_GET_SOURCE_CAP_EXT: + if (pd_process_ctrl_msg_get_source_cap( + pd_port, PE_DR_SNK_GIVE_SOURCE_CAP_EXT)) + return true; + break; +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL + case PD_CTRL_GET_STATUS: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SNK_READY, PE_SNK_GIVE_SINK_STATUS)) + return true; + break; +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ +#endif /* CONFIG_USB_PD_REV30 */ + + default: + pd_port->curr_unsupported_msg = true; + break; + } + + return pd_process_protocol_error(pd_port, pd_event); +} + +/* + * [BLOCK] Porcess Data MSG + */ + +static inline bool pd_process_data_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_DATA_SOURCE_CAP: +#ifdef CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP + pd_port->msg_id_pr_swap_last = 0xff; +#endif /* CONFIG_USB_PD_IGNORE_PS_RDY_AFTER_PR_SWAP */ + if (PE_MAKE_STATE_TRANSIT(PD_DATA_MSG_SOURCE_CAP)) + return true; + break; + +#ifdef CONFIG_USB_PD_PR_SWAP + case PD_DATA_SINK_CAP: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_DR_SNK_GET_SINK_CAP, PE_SNK_READY)) + return true; + break; +#endif /* CONFIG_USB_PD_PR_SWAP */ + +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE + case PD_DATA_ALERT: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SNK_READY, PE_SNK_SOURCE_ALERT_RECEIVED)) + return true; + break; +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ +#endif /* CONFIG_USB_PD_REV30 */ + default: + pd_port->curr_unsupported_msg = true; + break; + } + + return pd_process_protocol_error(pd_port, pd_event); +} + +/* + * [BLOCK] Porcess Extend MSG + */ +#ifdef CONFIG_USB_PD_REV30 +static inline bool pd_process_ext_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE + case PD_EXT_SOURCE_CAP_EXT: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SNK_GET_SOURCE_CAP_EXT, PE_SNK_READY)) + return true; + break; +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL + case PD_EXT_STATUS: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SNK_GET_SOURCE_STATUS, PE_SNK_READY)) + return true; + break; +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + case PD_EXT_PPS_STATUS: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SNK_GET_PPS_STATUS, PE_SNK_READY)) + return true; + break; +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + default: + pd_port->curr_unsupported_msg = true; + break; + } + + return pd_process_protocol_error(pd_port, pd_event); +} +#endif /* CONFIG_USB_PD_REV30 */ + +/* + * [BLOCK] Porcess DPM MSG + */ + +static inline bool pd_process_dpm_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_DPM_ACK: + return PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SNK_EVALUATE_CAPABILITY, PE_SNK_SELECT_CAPABILITY); + + default: + return false; + } +} + +/* + * [BLOCK] Porcess HW MSG + */ + +static inline bool pd_process_hw_msg_sink_tx_change( + struct pd_port *pd_port, struct pd_event *pd_event) +{ +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + struct pe_data *pe_data = &pd_port->pe_data; + uint8_t pd_traffic; + +#ifdef CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP + if (pe_data->pd_traffic_control == PD_SINK_TX_START) + return false; +#endif /* CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP */ + + if (!pd_check_rev30(pd_port)) + return false; + + pd_traffic = pd_event->msg_sec ? + PD_SINK_TX_OK : PD_SINK_TX_NG; + + if (pe_data->pd_traffic_control == pd_traffic) + return false; + + pe_data->pd_traffic_control = pd_traffic; + dpm_reaction_set_ready_once(pd_port); +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ + + return false; +} + +static inline bool pd_process_vbus_absent(struct pd_port *pd_port) +{ + if (pd_port->pe_state_curr != PE_SNK_DISCOVERY) + return false; +#ifdef CONFIG_USB_PD_SNK_HRESET_KEEP_DRAW + /* iSafe0mA: Maximum current a Sink + * is allowed to draw when VBUS is driven to vSafe0V + */ + pd_dpm_sink_vbus(pd_port, false); +#endif /* CONFIG_USB_PD_SNK_HRESET_KEEP_DRAW */ + pd_disable_pe_state_timer(pd_port); + pd_enable_vbus_valid_detection(pd_port, true); + return false; +} + +static inline bool pd_process_hw_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_HW_VBUS_PRESENT: + return PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SNK_DISCOVERY, PE_SNK_WAIT_FOR_CAPABILITIES); + + case PD_HW_VBUS_ABSENT: + return pd_process_vbus_absent(pd_port); + + case PD_HW_TX_FAILED: + return pd_process_tx_failed(pd_port); + +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + case PD_HW_SINK_TX_CHANGE: + return pd_process_hw_msg_sink_tx_change(pd_port, pd_event); +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ + }; + + return false; +} + +/* + * [BLOCK] Porcess PE MSG + */ + +static inline bool pd_process_pe_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_PE_RESET_PRL_COMPLETED: + return PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SNK_STARTUP, PE_SNK_DISCOVERY); + + case PD_PE_HARD_RESET_COMPLETED: + return PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SNK_HARD_RESET, PE_SNK_TRANSITION_TO_DEFAULT); + + case PD_PE_POWER_ROLE_AT_DEFAULT: + return PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SNK_TRANSITION_TO_DEFAULT, PE_SNK_STARTUP); + + default: + return false; + } + +} + +/* + * [BLOCK] Porcess Timer MSG + */ + +static inline void pd_report_typec_only_charger(struct pd_port *pd_port) +{ + uint8_t state; + struct tcpc_device *tcpc = pd_port->tcpc; + + if (tcpc->typec_remote_rp_level == TYPEC_CC_VOLT_SNK_DFT) + state = PD_CONNECT_TYPEC_ONLY_SNK_DFT; + else + state = PD_CONNECT_TYPEC_ONLY_SNK; + + PE_INFO("TYPE-C Only Charger!\n"); + pd_dpm_sink_vbus(pd_port, true); + pd_set_rx_enable(pd_port, PD_RX_CAP_PE_IDLE); + pd_notify_pe_hard_reset_completed(pd_port); + pd_update_connect_state(pd_port, state); +} + +static inline bool pd_process_timer_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ +#ifndef CONFIG_USB_PD_DBG_IGRONE_TIMEOUT + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; +#endif /* CONFIG_USB_PD_DBG_IGRONE_TIMEOUT */ + struct pe_data __maybe_unused *pe_data = &pd_port->pe_data; + + switch (pd_event->msg) { + case PD_TIMER_SINK_REQUEST: + return PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SNK_READY, PE_SNK_SELECT_CAPABILITY); + +#ifndef CONFIG_USB_PD_DBG_IGRONE_TIMEOUT + case PD_TIMER_SINK_WAIT_CAP: + case PD_TIMER_PS_TRANSITION: + if ((pd_port->pe_state_curr != PE_SNK_DISCOVERY) && + (pe_data->hard_reset_counter <= PD_HARD_RESET_COUNT)) { + PE_TRANSIT_STATE(pd_port, PE_SNK_HARD_RESET); + return true; + } + + PE_INFO("SRC NoResp\n"); + if (pd_port->request_v == TCPC_VBUS_SINK_5V) { + pd_report_typec_only_charger(pd_port); + } else { + PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY); + return true; + } + break; +#endif /* CONFIG_USB_PD_DBG_IGRONE_TIMEOUT */ + +#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID + case PD_TIMER_DISCOVER_ID: + vdm_put_dpm_discover_cable_event(pd_port); + break; +#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */ + /* fall through */ +#ifdef CONFIG_USB_PD_REV30 + case PD_TIMER_CK_NOT_SUPPORTED: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SNK_CHUNK_RECEIVED, PE_SNK_SEND_NOT_SUPPORTED)) + return true; + /* fall through */ +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID +#ifdef CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP + case PD_TIMER_SNK_FLOW_DELAY: + if (pe_data->pd_traffic_control == PD_SINK_TX_START) { + if (typec_get_cc_res() == TYPEC_CC_VOLT_SNK_3_0) + pe_data->pd_traffic_control = PD_SINK_TX_OK; + else + pe_data->pd_traffic_control = PD_SINK_TX_NG; + if (pd_check_rev30(pd_port)) + dpm_reaction_set_ready_once(pd_port); + } + break; +#endif /* CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP */ +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ +#endif /* CONFIG_USB_PD_REV30 */ + } + + return false; +} + +/* + * [BLOCK] Process Policy Engine's SNK Message + */ + +bool pd_process_event_snk(struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->event_type) { + case PD_EVT_CTRL_MSG: + return pd_process_ctrl_msg(pd_port, pd_event); + + case PD_EVT_DATA_MSG: + return pd_process_data_msg(pd_port, pd_event); + +#ifdef CONFIG_USB_PD_REV30 + case PD_EVT_EXT_MSG: + return pd_process_ext_msg(pd_port, pd_event); +#endif /* CONFIG_USB_PD_REV30 */ + + case PD_EVT_DPM_MSG: + return pd_process_dpm_msg(pd_port, pd_event); + + case PD_EVT_HW_MSG: + return pd_process_hw_msg(pd_port, pd_event); + + case PD_EVT_PE_MSG: + return pd_process_pe_msg(pd_port, pd_event); + + case PD_EVT_TIMER_MSG: + return pd_process_timer_msg(pd_port, pd_event); + + default: + return false; + } +} diff --git a/drivers/usb/typec/mmi_tcpc/pd_process_evt_src.c b/drivers/usb/typec/mmi_tcpc/pd_process_evt_src.c new file mode 100644 index 000000000000..ac71bafe6869 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_process_evt_src.c @@ -0,0 +1,542 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Process Event For SRC + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/tcpci_event.h" +#include "inc/pd_process_evt.h" +#include "inc/pd_dpm_core.h" + + +/* PD Data MSG reactions */ + +DECL_PE_STATE_TRANSITION(PD_DATA_MSG_REQUEST) = { + { PE_SRC_SEND_CAPABILITIES, PE_SRC_NEGOTIATE_CAPABILITIES }, + { PE_SRC_READY, PE_SRC_NEGOTIATE_CAPABILITIES }, +}; +DECL_PE_STATE_REACTION(PD_DATA_MSG_REQUEST); + +/* DPM Event reactions */ + +DECL_PE_STATE_TRANSITION(PD_DPM_MSG_ACK) = { + { PE_SRC_NEGOTIATE_CAPABILITIES, PE_SRC_TRANSITION_SUPPLY }, +}; +DECL_PE_STATE_REACTION(PD_DPM_MSG_ACK); + +DECL_PE_STATE_TRANSITION(PD_DPM_MSG_CAP_CHANGED) = { + { PE_SRC_READY, PE_SRC_SEND_CAPABILITIES }, + { PE_SRC_WAIT_NEW_CAPABILITIES, PE_SRC_SEND_CAPABILITIES }, +}; +DECL_PE_STATE_REACTION(PD_DPM_MSG_CAP_CHANGED); + +/* Timer Event reactions */ + +DECL_PE_STATE_TRANSITION(PD_TIMER_PS_HARD_RESET) = { + { PE_SRC_HARD_RESET, PE_SRC_TRANSITION_TO_DEFAULT }, + { PE_SRC_HARD_RESET_RECEIVED, PE_SRC_TRANSITION_TO_DEFAULT }, +}; +DECL_PE_STATE_REACTION(PD_TIMER_PS_HARD_RESET); + +/* + * [BLOCK] Porcess Ctrl MSG + */ + +static inline bool pd_process_ctrl_msg_good_crc( + struct pd_port *pd_port, struct pd_event *pd_event) + +{ + switch (pd_port->pe_state_curr) { + case PE_SRC_SEND_CAPABILITIES: + pd_port->pe_data.cap_counter = 0; + pd_handle_hard_reset_recovery(pd_port); + return false; + + case PE_SRC_TRANSITION_SUPPLY: + pd_enable_pe_state_timer(pd_port, PD_TIMER_SOURCE_TRANSITION); + return false; + + case PE_SRC_CAPABILITY_RESPONSE: + if (!pd_port->pe_data.explicit_contract) + PE_TRANSIT_STATE(pd_port, PE_SRC_WAIT_NEW_CAPABILITIES); + else if (pd_port->pe_data.invalid_contract) + PE_TRANSIT_STATE(pd_port, PE_SRC_HARD_RESET); + else + PE_TRANSIT_STATE(pd_port, PE_SRC_READY); + return true; + + case PE_SRC_SOFT_RESET: + PE_TRANSIT_STATE(pd_port, PE_SRC_SEND_CAPABILITIES); + return true; + + default: + return false; + } +} + +static inline bool pd_process_ctrl_msg_get_sink_cap( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + if (pd_port->pe_state_curr != PE_SRC_READY) + return false; + +#ifdef CONFIG_USB_PD_PR_SWAP + if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) { + PE_TRANSIT_STATE(pd_port, PE_DR_SRC_GIVE_SINK_CAP); + return true; + } +#endif /* CONFIG_USB_PD_PR_SWAP */ + + pd_port->curr_unsupported_msg = true; + + return false; +} + +static inline bool pd_process_ctrl_msg( + struct pd_port *pd_port, struct pd_event *pd_event) + +{ +#ifdef CONFIG_USB_PD_PARTNER_CTRL_MSG_FIRST + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + switch (pd_port->pe_state_curr) { + case PE_SRC_GET_SINK_CAP: + +#ifdef CONFIG_USB_PD_PR_SWAP + case PE_DR_SRC_GET_SOURCE_CAP: +#endif /* CONFIG_USB_PD_PR_SWAP */ + if (pd_event->msg >= PD_CTRL_GET_SOURCE_CAP && + pd_event->msg <= PD_CTRL_VCONN_SWAP) { + PE_DBG("Port Partner Request First\n"); + pd_port->pe_state_curr = PE_SRC_READY; + pd_disable_timer( + pd_port, PD_TIMER_SENDER_RESPONSE); + } + break; + } +#endif /* CONFIG_USB_PD_PARTNER_CTRL_MSG_FIRST */ + + switch (pd_event->msg) { + case PD_CTRL_GOOD_CRC: + return pd_process_ctrl_msg_good_crc(pd_port, pd_event); + + case PD_CTRL_ACCEPT: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SRC_SEND_SOFT_RESET, PE_SRC_SEND_CAPABILITIES)) + return true; + break; + + case PD_CTRL_GET_SOURCE_CAP: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SRC_READY, PE_SRC_SEND_CAPABILITIES)) + return true; + break; + + case PD_CTRL_GET_SINK_CAP: + if (pd_process_ctrl_msg_get_sink_cap(pd_port, pd_event)) + return true; + break; + +#ifdef CONFIG_USB_PD_REV30 + case PD_CTRL_NOT_SUPPORTED: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SRC_READY, PE_SRC_NOT_SUPPORTED_RECEIVED)) + return true; + break; + +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL + case PD_CTRL_GET_SOURCE_CAP_EXT: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SRC_READY, PE_SRC_GIVE_SOURCE_CAP_EXT)) + return true; + break; +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL + case PD_CTRL_GET_STATUS: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SRC_READY, PE_SRC_GIVE_SOURCE_STATUS)) + return true; + break; +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE + case PD_CTRL_GET_PPS_STATUS: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SRC_READY, PE_SRC_GIVE_PPS_STATUS)) + return true; + break; +#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */ +#endif /* CONFIG_USB_PD_REV30 */ + + default: + pd_port->curr_unsupported_msg = true; + break; + } + + return pd_process_protocol_error(pd_port, pd_event); +} + +/* + * [BLOCK] Porcess Data MSG + */ + +static inline bool pd_process_data_msg( + struct pd_port *pd_port, struct pd_event *pd_event) + +{ + switch (pd_event->msg) { +#ifdef CONFIG_USB_PD_PR_SWAP + case PD_DATA_SOURCE_CAP: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_DR_SRC_GET_SOURCE_CAP, PE_SRC_READY)) + return true; + break; +#endif /* CONFIG_USB_PD_PR_SWAP */ + + case PD_DATA_SINK_CAP: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SRC_GET_SINK_CAP, PE_SRC_READY)) + return true; + break; + + case PD_DATA_REQUEST: + if (PE_MAKE_STATE_TRANSIT(PD_DATA_MSG_REQUEST)) + return true; + break; + +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE + case PD_DATA_ALERT: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SRC_READY, PE_SRC_SINK_ALERT_RECEIVED)) + return true; + break; +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ +#endif /* CONFIG_USB_PD_REV30 */ + + default: + pd_port->curr_unsupported_msg = true; + break; + } + + return pd_process_protocol_error(pd_port, pd_event); +} + +/* + * [BLOCK] Porcess Extend MSG + */ +#ifdef CONFIG_USB_PD_REV30 + +static inline bool pd_process_ext_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE + case PD_EXT_SOURCE_CAP_EXT: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_DR_SRC_GET_SOURCE_CAP_EXT, PE_SRC_READY)) + return true; + break; +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL + case PD_EXT_STATUS: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SRC_GET_SINK_STATUS, PE_SRC_READY)) + return true; + break; +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ + + default: + pd_port->curr_unsupported_msg = true; + break; + } + + return pd_process_protocol_error(pd_port, pd_event); +} + +#endif /* CONFIG_USB_PD_REV30 */ + +/* + * [BLOCK] Porcess DPM MSG + */ + +static inline bool pd_process_dpm_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_DPM_ACK: + return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_ACK); + + case PD_DPM_NAK: + return PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SRC_NEGOTIATE_CAPABILITIES, + PE_SRC_CAPABILITY_RESPONSE); + + case PD_DPM_CAP_CHANGED: + return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_CAP_CHANGED); + + default: + return false; + } +} + +/* + * [BLOCK] Porcess HW MSG + */ + +static inline bool pd_process_hw_msg_vbus_present( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_port->pe_state_curr) { + case PE_SRC_STARTUP: + pd_enable_timer(pd_port, PD_TIMER_SOURCE_START); + break; + + case PE_SRC_TRANSITION_TO_DEFAULT: + pd_put_pe_event(pd_port, PD_PE_POWER_ROLE_AT_DEFAULT); + break; + } + + return false; +} + +static inline bool pd_process_hw_msg_tx_failed( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + struct pe_data *pe_data = &pd_port->pe_data; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_port->pe_state_curr == PE_SRC_SEND_CAPABILITIES) { + if (!pe_data->pd_connected || !pe_data->explicit_contract) { + PE_TRANSIT_STATE(pd_port, PE_SRC_DISCOVERY); + return true; + } + } + +#ifdef CONFIG_PD_SRC_RESET_CABLE + if (pd_port->pe_state_curr == PE_SRC_CBL_SEND_SOFT_RESET) { + PE_TRANSIT_STATE(pd_port, PE_SRC_SEND_CAPABILITIES); + return true; + } +#endif /* CONFIG_PD_SRC_RESET_CABLE */ + + return pd_process_tx_failed(pd_port); +} + +static inline bool pd_process_hw_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_HW_VBUS_PRESENT: + return pd_process_hw_msg_vbus_present(pd_port, pd_event); + + case PD_HW_VBUS_SAFE0V: + pd_enable_timer(pd_port, PD_TIMER_SRC_RECOVER); + return false; + + case PD_HW_VBUS_STABLE: + return PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SRC_TRANSITION_SUPPLY, PE_SRC_TRANSITION_SUPPLY2); + + case PD_HW_TX_FAILED: + return pd_process_hw_msg_tx_failed(pd_port, pd_event); + + default: + return false; + }; +} + +/* + * [BLOCK] Porcess PE MSG + */ + +static inline bool pd_process_pe_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_PE_RESET_PRL_COMPLETED: + return PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SRC_STARTUP, PE_SRC_SEND_CAPABILITIES); + + case PD_PE_POWER_ROLE_AT_DEFAULT: + return PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SRC_TRANSITION_TO_DEFAULT, PE_SRC_STARTUP); + + default: + return false; + } +} + +/* + * [BLOCK] Porcess Timer MSG + */ +static inline bool pd_process_timer_msg_source_start( + struct pd_port *pd_port, struct pd_event *pd_event) +{ +#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID + if (pd_is_discover_cable(pd_port) && + pd_port->pe_data.msg_id_tx[TCPC_TX_SOP_PRIME] == 0) { + +#ifdef CONFIG_PD_SRC_RESET_CABLE + if (pd_is_reset_cable(pd_port)) { + PE_TRANSIT_STATE(pd_port, PE_SRC_CBL_SEND_SOFT_RESET); + return true; + } +#endif /* CONFIG_PD_SRC_RESET_CABLE */ + + if (vdm_put_dpm_discover_cable_event(pd_port)) + return false; + } +#endif /* CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */ + + switch (pd_port->pe_state_curr) { + case PE_SRC_STARTUP: +#ifdef CONFIG_PD_SRC_RESET_CABLE + case PE_SRC_CBL_SEND_SOFT_RESET: +#endif /* CONFIG_PD_SRC_RESET_CABLE */ + PE_TRANSIT_STATE(pd_port, PE_SRC_SEND_CAPABILITIES); + return true; + } + + return false; +}; + +static inline bool pd_process_timer_msg_source_cap( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + if (pd_port->pe_state_curr != PE_SRC_DISCOVERY) + return false; + + if (pd_port->pe_data.cap_counter <= PD_CAPS_COUNT) + PE_TRANSIT_STATE(pd_port, PE_SRC_SEND_CAPABILITIES); + else /* in this state, PD always not connected */ + PE_TRANSIT_STATE(pd_port, PE_SRC_DISABLED); + + return true; +} + +static inline bool pd_process_timer_msg_no_response( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + if (pd_port->pe_data.hard_reset_counter <= PD_HARD_RESET_COUNT) + PE_TRANSIT_STATE(pd_port, PE_SRC_HARD_RESET); + else if (pd_port->pe_data.pd_prev_connected) + PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY); + else + PE_TRANSIT_STATE(pd_port, PE_SRC_DISABLED); + + return true; +} + +static inline bool pd_process_timer_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_TIMER_SOURCE_CAPABILITY: + return pd_process_timer_msg_source_cap(pd_port, pd_event); + +#ifndef CONFIG_USB_PD_DBG_IGRONE_TIMEOUT +#ifdef CONFIG_PD_SRC_RESET_CABLE + case PD_TIMER_SENDER_RESPONSE: + return PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SRC_CBL_SEND_SOFT_RESET, PE_SRC_SEND_CAPABILITIES); +#endif /* CONFIG_PD_SRC_RESET_CABLE */ +#endif + + case PD_TIMER_PS_HARD_RESET: + return PE_MAKE_STATE_TRANSIT(PD_TIMER_PS_HARD_RESET); + + case PD_TIMER_SOURCE_START: + return pd_process_timer_msg_source_start(pd_port, pd_event); + +#ifndef CONFIG_USB_PD_DBG_IGRONE_TIMEOUT + case PD_TIMER_NO_RESPONSE: + return pd_process_timer_msg_no_response(pd_port, pd_event); +#endif + + case PD_TIMER_SOURCE_TRANSITION: + if (pd_port->state_machine != PE_STATE_MACHINE_PR_SWAP) + pd_dpm_src_transition_power(pd_port); + break; + +#ifdef CONFIG_PD_DISCOVER_CABLE_ID + case PD_TIMER_DISCOVER_ID: + vdm_put_dpm_discover_cable_event(pd_port); + break; +#endif /* CONFIG_PD_DISCOVER_CABLE_ID */ + + case PD_TIMER_SRC_RECOVER: + pd_dpm_source_vbus(pd_port, true); + pd_enable_vbus_valid_detection(pd_port, true); + break; + +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + case PD_TIMER_SINK_TX: + if (pd_port->pe_data.pd_traffic_control == PD_SINK_TX_NG) + pd_port->pe_data.pd_traffic_control = PD_SOURCE_TX_OK; + +#ifdef CONFIG_USB_PD_REV30_SRC_FLOW_DELAY_STARTUP + if (pd_port->pe_data.pd_traffic_control == PD_SOURCE_TX_START) + pd_port->pe_data.pd_traffic_control = PD_SINK_TX_OK; +#endif /* CONFIG_USB_PD_REV30_SRC_FLOW_DELAY_STARTUP */ + + break; +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ + /* fall through */ +#ifdef CONFIG_USB_PD_REV30 + case PD_TIMER_CK_NOT_SUPPORTED: + return PE_MAKE_STATE_TRANSIT_SINGLE( + PE_SRC_CHUNK_RECEIVED, PE_SRC_SEND_NOT_SUPPORTED); +#endif /* CONFIG_USB_PD_REV30 */ + } + + return false; +} + +/* + * [BLOCK] Process Policy Engine's SRC Message + */ + +bool pd_process_event_src(struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->event_type) { + case PD_EVT_CTRL_MSG: + return pd_process_ctrl_msg(pd_port, pd_event); + + case PD_EVT_DATA_MSG: + return pd_process_data_msg(pd_port, pd_event); + +#ifdef CONFIG_USB_PD_REV30 + case PD_EVT_EXT_MSG: + return pd_process_ext_msg(pd_port, pd_event); +#endif /* CONFIG_USB_PD_REV30 */ + + case PD_EVT_DPM_MSG: + return pd_process_dpm_msg(pd_port, pd_event); + + case PD_EVT_HW_MSG: + return pd_process_hw_msg(pd_port, pd_event); + + case PD_EVT_PE_MSG: + return pd_process_pe_msg(pd_port, pd_event); + + case PD_EVT_TIMER_MSG: + return pd_process_timer_msg(pd_port, pd_event); + + default: + return false; + } +} diff --git a/drivers/usb/typec/mmi_tcpc/pd_process_evt_tcp.c b/drivers/usb/typec/mmi_tcpc/pd_process_evt_tcp.c new file mode 100644 index 000000000000..986e105789ff --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_process_evt_tcp.c @@ -0,0 +1,470 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Process Event for TCP + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/tcpci_event.h" +#include "inc/pd_process_evt.h" +#include "inc/pd_dpm_core.h" + +#ifdef CONFIG_USB_PD_PR_SWAP +static inline int pd_handle_tcp_event_pr_swap( + struct pd_port *pd_port, uint8_t new_role) +{ + if (pd_port->power_role == new_role) + return TCP_DPM_RET_DENIED_SAME_ROLE; + + if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER)) + return TCP_DPM_RET_DENIED_LOCAL_CAP; + + if (!pd_check_pe_state_ready(pd_port)) + return TCP_DPM_RET_DENIED_NOT_READY; + + pd_port->pe_data.during_swap = false; + pd_port->state_machine = PE_STATE_MACHINE_PR_SWAP; + + pe_transit_send_pr_swap_state(pd_port); + return TCP_DPM_RET_SENT; +} +#endif /* CONFIG_USB_PD_PR_SWAP */ + +#ifdef CONFIG_USB_PD_DR_SWAP +static inline int pd_handle_tcp_event_dr_swap( + struct pd_port *pd_port, uint8_t new_role) +{ + if (pd_port->data_role == new_role) + return TCP_DPM_RET_DENIED_SAME_ROLE; + + if (!(pd_port->dpm_caps & DPM_CAP_LOCAL_DR_DATA)) + return TCP_DPM_RET_DENIED_LOCAL_CAP; + + if (!pd_check_pe_state_ready(pd_port)) + return TCP_DPM_RET_DENIED_NOT_READY; + + pd_port->pe_data.during_swap = false; + pd_port->state_machine = PE_STATE_MACHINE_DR_SWAP; + + PE_TRANSIT_DATA_STATE(pd_port, + PE_DRS_UFP_DFP_SEND_DR_SWAP, + PE_DRS_DFP_UFP_SEND_DR_SWAP); + + return TCP_DPM_RET_SENT; +} +#endif /* CONFIG_USB_PD_DR_SWAP */ + +#ifdef CONFIG_USB_PD_VCONN_SWAP +static inline int pd_handle_tcp_event_vconn_swap( + struct pd_port *pd_port, uint8_t new_role) +{ + uint8_t old_role = pd_port->vconn_role ? 1 : 0; + + if (old_role == new_role) + return TCP_DPM_RET_DENIED_SAME_ROLE; + + if ((!pd_port->vconn_role) && + (!(pd_port->dpm_caps & DPM_CAP_LOCAL_VCONN_SUPPLY))) + return TCP_DPM_RET_DENIED_LOCAL_CAP; + + if (!pd_check_pe_state_ready(pd_port)) + return TCP_DPM_RET_DENIED_NOT_READY; + + pd_port->state_machine = PE_STATE_MACHINE_VCONN_SWAP; + PE_TRANSIT_STATE(pd_port, PE_VCS_SEND_SWAP); + return TCP_DPM_RET_SENT; +} +#endif /* CONFIG_USB_PD_VCONN_SWAP */ + +#ifdef CONFIG_USB_PD_PE_SOURCE +static inline int pd_handle_tcp_event_gotomin(struct pd_port *pd_port) +{ + if (pd_port->pe_state_curr != PE_SRC_READY) + return TCP_DPM_RET_DENIED_NOT_READY; + + if (!(pd_port->pe_data.dpm_flags & DPM_FLAGS_PARTNER_GIVE_BACK)) + return TCP_DPM_RET_DENIED_PARTNER_CAP; + + PE_TRANSIT_STATE(pd_port, PE_SRC_TRANSITION_SUPPLY); + return TCP_DPM_RET_SENT; +} +#endif /* CONFIG_USB_PD_PE_SOURCE */ + +static inline int pd_handle_tcp_event_softreset(struct pd_port *pd_port) +{ + if (!pd_check_pe_state_ready(pd_port)) + return TCP_DPM_RET_DENIED_NOT_READY; + + pe_transit_soft_reset_state(pd_port); + return TCP_DPM_RET_SENT; +} + +#ifdef CONFIG_PD_DFP_RESET_CABLE +static inline int pd_handle_tcp_event_cable_softreset(struct pd_port *pd_port) +{ + bool role_check; + + if (!pd_check_pe_state_ready(pd_port)) + return TCP_DPM_RET_DENIED_NOT_READY; + + role_check = pd_port->data_role == PD_ROLE_DFP; + + if (pd_check_rev30(pd_port)) + role_check = pd_port->vconn_role; + + if (!role_check) + return TCP_DPM_RET_DENIED_WRONG_DATA_ROLE; + + PE_TRANSIT_STATE(pd_port, PE_DFP_CBL_SEND_SOFT_RESET); + return TCP_DPM_RET_SENT; +} +#endif /* CONFIG_PD_DFP_RESET_CABLE */ + +static inline int pd_handle_tcp_event_get_source_cap(struct pd_port *pd_port) +{ + switch (pd_port->pe_state_curr) { + case PE_SNK_READY: + PE_TRANSIT_STATE(pd_port, PE_SNK_GET_SOURCE_CAP); + return TCP_DPM_RET_SENT; + +#ifdef CONFIG_USB_PD_PR_SWAP + case PE_SRC_READY: + if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) { + PE_TRANSIT_STATE(pd_port, PE_DR_SRC_GET_SOURCE_CAP); + return TCP_DPM_RET_SENT; + } +#endif /* CONFIG_USB_PD_PR_SWAP */ + + return TCP_DPM_RET_DENIED_LOCAL_CAP; + } + + return TCP_DPM_RET_DENIED_NOT_READY; +} + +static inline int pd_handle_tcp_event_get_sink_cap(struct pd_port *pd_port) +{ + switch (pd_port->pe_state_curr) { + case PE_SRC_READY: + PE_TRANSIT_STATE(pd_port, PE_SRC_GET_SINK_CAP); + return TCP_DPM_RET_SENT; + +#ifdef CONFIG_USB_PD_PR_SWAP + case PE_SNK_READY: + if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) { + PE_TRANSIT_STATE(pd_port, PE_DR_SNK_GET_SINK_CAP); + return TCP_DPM_RET_SENT; + } +#endif /* CONFIG_USB_PD_PR_SWAP */ + + return TCP_DPM_RET_DENIED_LOCAL_CAP; + } + + return TCP_DPM_RET_DENIED_NOT_READY; +} + +#ifdef CONFIG_USB_PD_PE_SINK +static inline int pd_handle_tcp_event_request(struct pd_port *pd_port) +{ + int ret = 0; + struct tcp_dpm_event *tcp_event = &pd_port->tcp_event; + + if (pd_port->pe_state_curr != PE_SNK_READY) + return TCP_DPM_RET_DENIED_NOT_READY; + + switch (pd_get_curr_pd_event(pd_port)->msg) { + case TCP_DPM_EVT_REQUEST: + ret = pd_dpm_update_tcp_request( + pd_port, &tcp_event->tcp_dpm_data.pd_req); + break; + case TCP_DPM_EVT_REQUEST_EX: + ret = pd_dpm_update_tcp_request_ex( + pd_port, &tcp_event->tcp_dpm_data.pd_req_ex); + break; + case TCP_DPM_EVT_REQUEST_AGAIN: + ret = pd_dpm_update_tcp_request_again(pd_port); + break; + } + + if (ret != TCP_DPM_RET_SUCCESS) + return ret; + + PE_TRANSIT_STATE(pd_port, PE_SNK_SELECT_CAPABILITY); + return TCP_DPM_RET_SENT; +} +#endif /* CONFIG_USB_PD_PE_SINK */ + +static inline int pd_handle_tcp_event_bist_cm2(struct pd_port *pd_port) +{ + uint32_t bist = BDO_MODE_CARRIER2; + + if (!pd_check_pe_state_ready(pd_port)) + return TCP_DPM_RET_DENIED_NOT_READY; + + pd_send_sop_data_msg(pd_port, PD_DATA_BIST, 1, &bist); + return TCP_DPM_RET_SENT; +} + +#ifdef CONFIG_USB_PD_REV30 + +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE +static inline int pd_handle_tcp_event_get_source_cap_ext( + struct pd_port *pd_port) +{ + switch (pd_port->pe_state_curr) { + case PE_SNK_READY: + PE_TRANSIT_STATE(pd_port, PE_SNK_GET_SOURCE_CAP_EXT); + return TCP_DPM_RET_SENT; + +#ifdef CONFIG_USB_PD_PR_SWAP + case PE_SRC_READY: + if (pd_port->dpm_caps & DPM_CAP_LOCAL_DR_POWER) { + PE_TRANSIT_STATE(pd_port, + PE_DR_SRC_GET_SOURCE_CAP_EXT); + return TCP_DPM_RET_SENT; + } +#endif /* CONFIG_USB_PD_PR_SWAP */ + return TCP_DPM_RET_DENIED_LOCAL_CAP; + } + + return TCP_DPM_RET_DENIED_NOT_READY; +} +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK +static inline int pd_handle_tcp_event_get_pps_status(struct pd_port *pd_port) +{ + if (pd_port->pe_state_curr != PE_SNK_READY) + return TCP_DPM_RET_DENIED_NOT_READY; + + PE_TRANSIT_STATE(pd_port, PE_SNK_GET_PPS_STATUS); + return TCP_DPM_RET_SENT; +} +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + +static inline int pd_make_tcp_event_transit_ready( + struct pd_port *pd_port, uint8_t state) +{ + if (!pd_check_pe_state_ready(pd_port)) + return TCP_DPM_RET_DENIED_NOT_READY; + + PE_TRANSIT_STATE(pd_port, state); + return TCP_DPM_RET_SENT; +} + +static inline int pd_make_tcp_event_transit_ready2( + struct pd_port *pd_port, uint8_t snk_state, uint8_t src_state) +{ + switch (pd_port->pe_state_curr) { + +#ifdef CONFIG_USB_PD_PE_SINK + case PE_SNK_READY: + PE_TRANSIT_STATE(pd_port, snk_state); + return TCP_DPM_RET_SENT; +#endif /* CONFIG_USB_PD_PE_SINK */ + +#ifdef CONFIG_USB_PD_PE_SOURCE + case PE_SRC_READY: + PE_TRANSIT_STATE(pd_port, src_state); + return TCP_DPM_RET_SENT; +#endif /* CONFIG_USB_PD_PE_SOURCE */ + } + + return TCP_DPM_RET_DENIED_NOT_READY; +} + +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL +static inline int pd_handle_tcp_event_alert(struct pd_port *pd_port) +{ + struct tcp_dpm_event *tcp_event = &pd_port->tcp_event; + + if (pd_get_curr_pd_event(pd_port)->msg_sec == PD_TCP_FROM_TCPM) + pd_port->pe_data.local_alert |= tcp_event->tcp_dpm_data.index; + + return pd_make_tcp_event_transit_ready2(pd_port, + PE_SNK_SEND_SINK_ALERT, PE_SRC_SEND_SOURCE_ALERT); +} +#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */ + +#endif /* CONFIG_USB_PD_REV30 */ + +static inline int pd_handle_tcp_event_hardreset(struct pd_port *pd_port) +{ + pe_transit_hard_reset_state(pd_port); + return TCP_DPM_RET_SENT; +} + +static inline int pd_handle_tcp_event_error_recovery(struct pd_port *pd_port) +{ + PE_TRANSIT_STATE(pd_port, PE_ERROR_RECOVERY); + return TCP_DPM_RET_SENT; +} + +static inline int pd_handle_tcp_dpm_event( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + int ret = TCP_DPM_RET_DENIED_UNKNOWN; + +#ifdef CONFIG_USB_PD_REV30 + if (pd_event->msg >= TCP_DPM_EVT_PD30_COMMAND + && pd_event->msg < TCP_DPM_EVT_VDM_COMMAND) { + if (!pd_check_rev30(pd_port)) + return TCP_DPM_RET_DENIED_PD_REV; + } +#endif /* CONFIG_USB_PD_REV30 */ + + switch (pd_event->msg) { + default: + break; + case TCP_DPM_EVT_PR_SWAP_AS_SNK: + case TCP_DPM_EVT_PR_SWAP_AS_SRC: +#ifdef CONFIG_USB_PD_PR_SWAP + ret = pd_handle_tcp_event_pr_swap(pd_port, + pd_event->msg - TCP_DPM_EVT_PR_SWAP_AS_SNK); +#endif /* CONFIG_USB_PD_PR_SWAP */ + break; + + case TCP_DPM_EVT_DR_SWAP_AS_UFP: + case TCP_DPM_EVT_DR_SWAP_AS_DFP: +#ifdef CONFIG_USB_PD_DR_SWAP + ret = pd_handle_tcp_event_dr_swap(pd_port, + pd_event->msg - TCP_DPM_EVT_DR_SWAP_AS_UFP); +#endif /* CONFIG_USB_PD_DR_SWAP */ + break; + + case TCP_DPM_EVT_VCONN_SWAP_OFF: + case TCP_DPM_EVT_VCONN_SWAP_ON: +#ifdef CONFIG_USB_PD_VCONN_SWAP + ret = pd_handle_tcp_event_vconn_swap(pd_port, + pd_event->msg - TCP_DPM_EVT_VCONN_SWAP_OFF); +#endif /* CONFIG_USB_PD_VCONN_SWAP */ + break; + + case TCP_DPM_EVT_GOTOMIN: +#ifdef CONFIG_USB_PD_PE_SOURCE + ret = pd_handle_tcp_event_gotomin(pd_port); +#endif /* CONFIG_USB_PD_PE_SOURCE */ + break; + case TCP_DPM_EVT_SOFTRESET: + ret = pd_handle_tcp_event_softreset(pd_port); + break; + + case TCP_DPM_EVT_CABLE_SOFTRESET: +#ifdef CONFIG_PD_DFP_RESET_CABLE + ret = pd_handle_tcp_event_cable_softreset(pd_port); +#endif /* CONFIG_PD_DFP_RESET_CABLE */ + break; + + case TCP_DPM_EVT_GET_SOURCE_CAP: + ret = pd_handle_tcp_event_get_source_cap(pd_port); + break; + + case TCP_DPM_EVT_GET_SINK_CAP: + ret = pd_handle_tcp_event_get_sink_cap(pd_port); + break; + +#ifdef CONFIG_USB_PD_PE_SINK + case TCP_DPM_EVT_REQUEST: + ret = pd_handle_tcp_event_request(pd_port); + break; + case TCP_DPM_EVT_REQUEST_EX: + ret = pd_handle_tcp_event_request(pd_port); + break; + case TCP_DPM_EVT_REQUEST_AGAIN: + ret = pd_handle_tcp_event_request(pd_port); + break; +#endif /* CONFIG_USB_PD_PE_SINK */ + + case TCP_DPM_EVT_BIST_CM2: + ret = pd_handle_tcp_event_bist_cm2(pd_port); + break; + +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE + case TCP_DPM_EVT_GET_SOURCE_CAP_EXT: + ret = pd_handle_tcp_event_get_source_cap_ext(pd_port); + break; +#endif /* CONFIG_USB_PD_REV30_SRC_CAP_EXT_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE + case TCP_DPM_EVT_GET_STATUS: + ret = pd_make_tcp_event_transit_ready2(pd_port, + PE_SNK_GET_SOURCE_STATUS, PE_SRC_GET_SINK_STATUS); + break; +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE + case TCP_DPM_EVT_GET_COUNTRY_CODE: + ret = pd_make_tcp_event_transit_ready( + pd_port, PE_GET_COUNTRY_CODES); + break; +#endif /* CONFIG_USB_PD_REV30_COUNTRY_CODE_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + case TCP_DPM_EVT_GET_PPS_STATUS: + ret = pd_handle_tcp_event_get_pps_status(pd_port); + break; +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL + case TCP_DPM_EVT_ALERT: + ret = pd_handle_tcp_event_alert(pd_port); + break; +#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */ + +#ifdef CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE + case TCP_DPM_EVT_GET_COUNTRY_INFO: + ret = pd_make_tcp_event_transit_ready( + pd_port, PE_GET_COUNTRY_INFO); + break; +#endif /* CONFIG_USB_PD_REV30_COUNTRY_INFO_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_BAT_CAP_REMOTE + case TCP_DPM_EVT_GET_BAT_CAP: + ret = pd_make_tcp_event_transit_ready( + pd_port, PE_GET_BATTERY_CAP); + break; +#endif /* CONFIG_USB_PD_REV30_BAT_CAP_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE + case TCP_DPM_EVT_GET_BAT_STATUS: + ret = pd_make_tcp_event_transit_ready( + pd_port, PE_GET_BATTERY_STATUS); + break; +#endif /* CONFIG_USB_PD_REV30_BAT_STATUS_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE + case TCP_DPM_EVT_GET_MFRS_INFO: + ret = pd_make_tcp_event_transit_ready( + pd_port, PE_GET_MANUFACTURER_INFO); + break; +#endif /* CONFIG_USB_PD_REV30_MFRS_INFO_REMOTE */ +#endif /* CONFIG_USB_PD_REV30 */ + + case TCP_DPM_EVT_HARD_RESET: + ret = pd_handle_tcp_event_hardreset(pd_port); + break; + + case TCP_DPM_EVT_ERROR_RECOVERY: + ret = pd_handle_tcp_event_error_recovery(pd_port); + break; + } + + return ret; +} + +bool pd_process_event_tcp(struct pd_port *pd_port, struct pd_event *pd_event) +{ + int ret = pd_handle_tcp_dpm_event(pd_port, pd_event); + + pd_notify_tcp_event_1st_result(pd_port, ret); + return ret == TCP_DPM_RET_SENT; +} diff --git a/drivers/usb/typec/mmi_tcpc/pd_process_evt_vcs.c b/drivers/usb/typec/mmi_tcpc/pd_process_evt_vcs.c new file mode 100644 index 000000000000..f2d94fab2bfe --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_process_evt_vcs.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Process Event For VCS + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/tcpci_event.h" +#include "inc/pd_process_evt.h" + +#ifdef CONFIG_USB_PD_VCONN_SWAP +/* DPM Event reactions */ + +DECL_PE_STATE_TRANSITION(PD_DPM_MSG_ACK) = { + { PE_VCS_EVALUATE_SWAP, PE_VCS_ACCEPT_SWAP }, + { PE_VCS_TURN_ON_VCONN, PE_VCS_SEND_PS_RDY }, +}; +DECL_PE_STATE_REACTION(PD_DPM_MSG_ACK); + +/* + * [BLOCK] Porcess PD Ctrl MSG + */ + +static inline bool pd_process_ctrl_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + uint8_t vconn_state = pd_port->vconn_role ? + PE_VCS_WAIT_FOR_VCONN : PE_VCS_TURN_ON_VCONN; + + switch (pd_event->msg) { + case PD_CTRL_GOOD_CRC: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_VCS_ACCEPT_SWAP, vconn_state)) + return true; + break; + + case PD_CTRL_ACCEPT: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_VCS_SEND_SWAP, vconn_state)) + return true; + break; + + case PD_CTRL_PS_RDY: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_VCS_WAIT_FOR_VCONN, PE_VCS_TURN_OFF_VCONN)) + return true; + break; + } + + return false; +} + +/* + * [BLOCK] Porcess DPM MSG + */ + +static inline bool pd_process_dpm_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_DPM_ACK: + return PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_ACK); + + case PD_DPM_NAK: + if (PE_MAKE_STATE_TRANSIT_SINGLE( + PE_VCS_EVALUATE_SWAP, PE_VCS_REJECT_VCONN_SWAP)) + return true; + break; + } + + return false; +} + +/* + * [BLOCK] Porcess Timer MSG + */ + +static inline bool pd_process_timer_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_TIMER_VCONN_ON: + if (PE_MAKE_STATE_TRANSIT_TO_HRESET(PE_VCS_WAIT_FOR_VCONN)) + return true; + break; + +#if CONFIG_USB_PD_VCONN_READY_TOUT != 0 + case PD_TIMER_VCONN_READY: + PE_STATE_DPM_ACK_IMMEDIATELY(pd_port); + break; +#endif + } + + return false; +} + +/* + * [BLOCK] Process Policy Engine's VCS Message + */ + +bool pd_process_event_vcs(struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->event_type) { + case PD_EVT_CTRL_MSG: + return pd_process_ctrl_msg(pd_port, pd_event); + + case PD_EVT_DPM_MSG: + return pd_process_dpm_msg(pd_port, pd_event); + + case PD_EVT_TIMER_MSG: + return pd_process_timer_msg(pd_port, pd_event); + + default: + return false; + } +} +#endif /* CONFIG_USB_PD_VCONN_SWAP */ diff --git a/drivers/usb/typec/mmi_tcpc/pd_process_evt_vdm.c b/drivers/usb/typec/mmi_tcpc/pd_process_evt_vdm.c new file mode 100644 index 000000000000..05b88ad2fe6d --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/pd_process_evt_vdm.c @@ -0,0 +1,973 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Process Event For VDM + * + * 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. + */ + +#include "inc/pd_core.h" +#include "inc/tcpci_event.h" +#include "inc/pd_process_evt.h" +#include "inc/pd_dpm_core.h" +#include "pd_dpm_prv.h" + +/* VDM reactions */ + +#define VDM_CMD_FLAG_CABLE_CMD (1<<0) +#define VDM_CMD_FLAG_SEND_BY_UFP (1<<1) +#define VDM_CMD_FLAG_SEND_BY_DFP (1<<2) +#define VDM_CMD_FLAG_RECV_BY_UFP (1<<3) +#define VDM_CMD_FLAG_RECV_BY_DFP (1<<4) +#define VDM_CMD_FLAG_PD30_DUPLEX (1<<5) + +struct vdm_state_transition { + uint8_t vdm_cmd; + uint8_t vdm_init_state; + uint8_t vdm_request_state; + uint8_t vdm_cmd_flags; +}; + +#define VDM_CMD_REACTION(cmd, init, req, flags) { \ + .vdm_cmd = cmd, \ + .vdm_init_state = init, \ + .vdm_request_state = req, \ + .vdm_cmd_flags = flags, \ +} + +#define VDM_DFP_CMD_REACTION(cmd, init, req) \ + VDM_CMD_REACTION(cmd, init, req, \ + VDM_CMD_FLAG_SEND_BY_DFP | VDM_CMD_FLAG_RECV_BY_UFP) + +#define VDM_UFP_CMD_REACTION(cmd, init, req) \ + VDM_CMD_REACTION(cmd, init, req, \ + VDM_CMD_FLAG_SEND_BY_UFP | VDM_CMD_FLAG_RECV_BY_DFP) + +#define VDM_CABLE_CMD_REACTION(cmd, init, req) \ + VDM_CMD_REACTION(cmd, init, req, \ + VDM_CMD_FLAG_SEND_BY_UFP | VDM_CMD_FLAG_SEND_BY_DFP \ + | VDM_CMD_FLAG_CABLE_CMD) + +#define VDM_DFP_CMD_REACTION_PD30(cmd, init, req) \ + VDM_CMD_REACTION(cmd, init, req, \ + VDM_CMD_FLAG_SEND_BY_DFP | VDM_CMD_FLAG_RECV_BY_UFP | \ + VDM_CMD_FLAG_PD30_DUPLEX) + +#define VDM_UFP_CMD_REACTION_PD30(cmd, init, req) \ + VDM_CMD_REACTION(cmd, init, req, \ + VDM_CMD_FLAG_SEND_BY_UFP | VDM_CMD_FLAG_RECV_BY_DFP | \ + VDM_CMD_FLAG_PD30_DUPLEX) + +static const struct vdm_state_transition pe_vdm_state_reactions[] = { + + VDM_DFP_CMD_REACTION_PD30(CMD_DISCOVER_IDENT, + PE_UFP_VDM_GET_IDENTITY, + PE_DFP_UFP_VDM_IDENTITY_REQUEST + ), + + VDM_DFP_CMD_REACTION_PD30(CMD_DISCOVER_SVID, + PE_UFP_VDM_GET_SVIDS, + PE_DFP_VDM_SVIDS_REQUEST + ), + + VDM_DFP_CMD_REACTION_PD30(CMD_DISCOVER_MODES, + PE_UFP_VDM_GET_MODES, + PE_DFP_VDM_MODES_REQUEST + ), + + VDM_DFP_CMD_REACTION(CMD_ENTER_MODE, + PE_UFP_VDM_EVALUATE_MODE_ENTRY, + PE_DFP_VDM_MODE_ENTRY_REQUEST + ), + + VDM_DFP_CMD_REACTION(CMD_EXIT_MODE, + PE_UFP_VDM_MODE_EXIT, + PE_DFP_VDM_MODE_EXIT_REQUEST + ), + + VDM_UFP_CMD_REACTION_PD30(CMD_ATTENTION, + PE_DFP_VDM_ATTENTION_REQUEST, + PE_UFP_VDM_ATTENTION_REQUEST + ), + +#ifdef CONFIG_USB_PD_ALT_MODE_DFP + VDM_DFP_CMD_REACTION(CMD_DP_STATUS, + PE_UFP_VDM_DP_STATUS_UPDATE, + PE_DFP_VDM_DP_STATUS_UPDATE_REQUEST + ), + + VDM_DFP_CMD_REACTION(CMD_DP_CONFIG, + PE_UFP_VDM_DP_CONFIGURE, + PE_DFP_VDM_DP_CONFIGURATION_REQUEST + ), +#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM + /* Only handle Timeout Case */ + VDM_DFP_CMD_REACTION_PD30(0, + PE_UFP_UVDM_RECV, + PE_DFP_UVDM_SEND + ), +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + +#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID + VDM_CABLE_CMD_REACTION(CMD_DISCOVER_IDENT, + 0, + PE_SRC_VDM_IDENTITY_REQUEST + ), +#endif /* CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */ + +#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID + VDM_CABLE_CMD_REACTION(CMD_DISCOVER_IDENT, + 0, + PE_DFP_CBL_VDM_IDENTITY_REQUEST + ), +#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */ +}; + +static inline bool pd_vdm_state_transit_rx(struct pd_port *pd_port, + const struct vdm_state_transition *state_transition) +{ + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (!pd_check_pe_state_ready(pd_port)) { + PE_DBG("670 : invalid, current status\n"); + return false; + } + + PE_TRANSIT_STATE(pd_port, state_transition->vdm_init_state); + return true; +} + +static inline bool pd_vdm_state_transit_tx(struct pd_port *pd_port, + uint8_t vdm_cmdt, const struct vdm_state_transition *state_transition) +{ + int ret_code = TCP_DPM_RET_VDM_NAK; + uint8_t curr_state = state_transition->vdm_request_state; + uint8_t next_state = curr_state + 1; + + switch (vdm_cmdt) { + + case CMDT_RSP_NAK: + if (curr_state != PE_DFP_VDM_MODE_EXIT_REQUEST) + next_state = curr_state+2; + break; + + case CMDT_RSP_BUSY: + if (curr_state == PE_DFP_VDM_MODE_EXIT_REQUEST) + next_state = pe_get_curr_hard_reset_state(pd_port); + else + next_state = curr_state+2; + break; + + default: + ret_code = TCP_DPM_RET_VDM_ACK; + break; + } + + pd_notify_tcp_vdm_event_2nd_result(pd_port, ret_code); + PE_TRANSIT_STATE(pd_port, next_state); + return true; +} + +bool vdm_is_state_transition_available(struct pd_port *pd_port, + bool recv, const struct vdm_state_transition *state_transition) +{ + uint8_t shift = 1; + uint8_t vdm_cmd_flags = state_transition->vdm_cmd_flags; + + if (pd_check_rev30(pd_port) && + (vdm_cmd_flags & VDM_CMD_FLAG_PD30_DUPLEX)) { + return true; + } + + if (pd_port->data_role == PD_ROLE_DFP) + shift += 1; + + if (recv) + shift += 2; + + return vdm_cmd_flags & (1 << shift); +} + +static bool pd_vdm_state_transit( + struct pd_port *pd_port, uint8_t vdm_cmdt, + const struct vdm_state_transition *state_transition) +{ + uint8_t curr_state; + + curr_state = pd_port->pe_state_curr; + + if (curr_state == PE_UFP_VDM_ATTENTION_REQUEST) { + VDM_STATE_DPM_INFORMED(pd_port); + pd_notify_tcp_vdm_event_2nd_result( + pd_port, TCP_DPM_RET_TIMEOUT); + return false; + } + + if (vdm_cmdt == CMDT_INIT) { /* Recv */ + if (!vdm_is_state_transition_available( + pd_port, true, state_transition)) + return false; + + return pd_vdm_state_transit_rx(pd_port, state_transition); + } + + /* Send */ + if (!vdm_is_state_transition_available( + pd_port, false, state_transition)) + return false; + + if (curr_state != state_transition->vdm_request_state) + return false; + + return pd_vdm_state_transit_tx( + pd_port, vdm_cmdt, state_transition); +} + +enum { + VDM_STATE_TRANSIT_SOP_CMD = 0, + VDM_STATE_TRANSIT_SOP_PRIME_CMD = VDM_CMD_FLAG_CABLE_CMD, + VDM_STATE_TRANSIT_NAK = 2, + + VDM_STATE_TRANSIT_CHECK_TX = 3, +}; + +static bool pe_check_vdm_state_transit_valid( + struct pd_port *pd_port, uint8_t transit_type, uint8_t *vdm_cmdt, + const struct vdm_state_transition *state_transition) +{ + uint8_t curr_state; + uint8_t vdm_cmd; + uint8_t cable_cmd; + uint8_t vdm_cmd_flags; + uint32_t curr_vdm_hdr; + + curr_state = pd_port->pe_state_curr; + + if (transit_type == VDM_STATE_TRANSIT_NAK) { + if (curr_state != state_transition->vdm_request_state) + return false; + + *vdm_cmdt = CMDT_RSP_BUSY; + return true; + } else if (transit_type >= VDM_STATE_TRANSIT_CHECK_TX) { + return state_transition->vdm_request_state == transit_type; + } + + curr_vdm_hdr = pd_port->curr_vdm_hdr; + + vdm_cmd = PD_VDO_CMD(curr_vdm_hdr); + *vdm_cmdt = PD_VDO_CMDT(curr_vdm_hdr); + + if (state_transition->vdm_cmd != vdm_cmd) + return false; + + vdm_cmd_flags = state_transition->vdm_cmd_flags; + cable_cmd = vdm_cmd_flags & VDM_CMD_FLAG_CABLE_CMD; + + if (cable_cmd != transit_type) + return false; + + if (cable_cmd && curr_state != state_transition->vdm_request_state) + return false; + + return true; +} + +static bool pd_make_vdm_state_transit( + struct pd_port *pd_port, uint8_t transit_type) +{ + int i; + bool check_tx; + uint8_t vdm_cmdt; + const struct vdm_state_transition *state_transition; + + uint8_t nr_transition = ARRAY_SIZE(pe_vdm_state_reactions); + const struct vdm_state_transition *state_reaction = + pe_vdm_state_reactions; + + check_tx = transit_type >= VDM_STATE_TRANSIT_CHECK_TX; + + for (i = 0; i < nr_transition; i++) { + state_transition = &state_reaction[i]; + + if (!pe_check_vdm_state_transit_valid( + pd_port, transit_type, &vdm_cmdt, state_transition)) + continue; + + if (check_tx) { + return vdm_is_state_transition_available( + pd_port, false, state_transition); + } else { + return pd_vdm_state_transit( + pd_port, vdm_cmdt, state_transition); + } + } + + return false; +} + +static inline bool pd_make_vdm_state_transit_sop(struct pd_port *pd_port) +{ + return pd_make_vdm_state_transit( + pd_port, VDM_STATE_TRANSIT_SOP_CMD); +} + +static inline bool pd_make_vdm_state_transit_cable(struct pd_port *pd_port) +{ + return pd_make_vdm_state_transit( + pd_port, VDM_STATE_TRANSIT_SOP_PRIME_CMD); +} + +static inline bool pd_make_vdm_state_transit_nak(struct pd_port *pd_port) +{ + return pd_make_vdm_state_transit( + pd_port, VDM_STATE_TRANSIT_NAK); +} + +/* Discover Cable ID */ + +#ifdef CONFIG_PD_DISCOVER_CABLE_ID +DECL_PE_STATE_TRANSITION(PD_DPM_MSG_DISCOVER_CABLE) = { +#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID + { PE_SRC_STARTUP, PE_SRC_VDM_IDENTITY_REQUEST}, + { PE_SRC_DISCOVERY, PE_SRC_VDM_IDENTITY_REQUEST}, +#endif + +#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID + { PE_SRC_READY, PE_DFP_CBL_VDM_IDENTITY_REQUEST}, + { PE_SNK_READY, PE_DFP_CBL_VDM_IDENTITY_REQUEST}, +#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */ + +#ifdef CONFIG_PD_SRC_RESET_CABLE + { PE_SRC_CBL_SEND_SOFT_RESET, PE_SRC_VDM_IDENTITY_REQUEST}, +#endif /* CONFIG_PD_SRC_RESET_CABLE */ +}; +DECL_PE_STATE_REACTION(PD_DPM_MSG_DISCOVER_CABLE); +#endif /* CONFIG_PD_DISCOVER_CABLE_ID */ + +/* + * [BLOCK] Porcess Ctrl MSG + */ + +#ifdef CONFIG_USB_PD_ALT_MODE +#ifdef CONFIG_USB_PD_DBG_DP_UFP_U_AUTO_ATTENTION +static inline bool pd_ufp_u_auto_send_attention(struct pd_port *pd_port) +{ + struct dp_data *dp_data = pd_get_dp_data(pd_port); + + if (dp_data->local_config != 0) { + PE_TRANSIT_STATE(pd_port, + PE_UFP_VDM_ATTENTION_REQUEST); + return true; + } + + return false; +} +#endif /* CONFIG_USB_PD_DBG_DP_UFP_U_AUTO_ATTENTION */ +#endif /* CONFIG_USB_PD_ALT_MODE */ + +static inline bool pd_process_ctrl_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + struct pe_data *pe_data = &pd_port->pe_data; + + /* VDM Only handle CtrlMsg = GoodCRC */ + + if (pd_event->msg != PD_CTRL_GOOD_CRC) + return false; + + if (pe_data->vdm_state_flags & + VDM_STATE_FLAG_ENABLE_VDM_RESPONSE_TIMER) + pd_enable_timer(pd_port, pe_data->vdm_state_timer); + + switch (pd_port->pe_state_curr) { +#ifdef CONFIG_USB_PD_ALT_MODE +#ifdef CONFIG_USB_PD_DBG_DP_UFP_U_AUTO_ATTENTION + case PE_UFP_VDM_DP_CONFIGURE: + if (pd_ufp_u_auto_send_attention(pd_port)) + return true; + break; +#endif /* CONFIG_USB_PD_DBG_DP_UFP_U_AUTO_ATTENTION */ + + case PE_UFP_VDM_ATTENTION_REQUEST: + pd_notify_tcp_vdm_event_2nd_result( + pd_port, TCP_DPM_RET_VDM_ACK); + break; +#endif /* CONFIG_USB_PD_ALT_MODE */ + +#ifdef CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID + case PE_SRC_VDM_IDENTITY_REQUEST: + pe_data->power_cable_present = true; + return false; +#endif /* CONFIG_USB_PD_SRC_STARTUP_DISCOVER_ID */ + +#ifdef CONFIG_USB_PD_DFP_READY_DISCOVER_ID + case PE_DFP_CBL_VDM_IDENTITY_REQUEST: + pe_data->power_cable_present = true; + return false; +#endif /* CONFIG_USB_PD_DFP_READY_DISCOVER_ID */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM + case PE_DFP_UVDM_SEND: + if (!pd_port->uvdm_wait_resp) { + PE_TRANSIT_STATE(pd_port, PE_DFP_UVDM_ACKED); + return true; + } + break; +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + } + + if (pe_data->vdm_state_flags + & VDM_STATE_FLAG_BACK_READY_IF_RECV_GOOD_CRC) { + pe_transit_ready_state(pd_port); + return true; + } + + return false; +} + +/* + * [BLOCK] Porcess Custom MSG (SVDM/UVDM) + */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM + +bool pd_process_custom_vdm(struct pd_port *pd_port, bool svdm) +{ + bool recv = false; + + if (pd_port->data_role == PD_ROLE_UFP) + recv = true; + + if (pd_check_rev30(pd_port) && svdm) + recv = true; + + if (pd_check_pe_state_ready(pd_port)) { + if (recv) { + PE_TRANSIT_STATE(pd_port, PE_UFP_UVDM_RECV); + return true; + } + } else if (pd_port->pe_state_curr == PE_DFP_UVDM_SEND) { + PE_TRANSIT_STATE(pd_port, PE_DFP_UVDM_ACKED); + return true; + } + + pd_put_dpm_event(pd_port, PD_DPM_NOT_SUPPORT); + return false; +} + +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + +static inline bool pd_process_uvdm( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + +#ifdef CONFIG_USB_PD_UVDM + /* support sop only */ + if (pd_event->pd_msg->frame_type != TCPC_TX_SOP) + return false; + + if (pd_process_custom_vdm(pd_port, false)) + return true; +#else + pd_put_dpm_event(pd_port, PD_DPM_NOT_SUPPORT); +#endif /* CONFIG_USB_PD_UVDM */ + + /* TODO: Reply Not_Supported Message*/ + PE_DBG("659 : Invalid UVDM\n"); + return false; +} + +/* + * [BLOCK] Porcess Data MSG (VDM) + */ + +#if (PE_EVT_INFO_VDM_DIS == 0) +static const char * const pe_vdm_cmd_name[] = { + "DiscoverID", + "DiscoverSVID", + "DiscoverMode", + "EnterMode", + "ExitMode", + "Attention", +}; + +#if PE_INFO_ENABLE +static const char *const pe_vdm_cmd_type_name[] = { + "INIT", + "ACK", + "NACK", + "BUSY", +}; +#endif /* PE_INFO_ENABLE */ + +static inline const char *assign_vdm_cmd_name(uint8_t cmd) +{ + if (cmd <= ARRAY_SIZE(pe_vdm_cmd_name) && cmd != 0) { + cmd -= 1; + return pe_vdm_cmd_name[cmd]; + } + + return NULL; +} + +#ifdef CONFIG_USB_PD_ALT_MODE +static const char *const pe_vdm_dp_cmd_name[] = { + "DPStatus", + "DPConfig", +}; + +static inline const char *assign_vdm_dp_cmd_name(uint8_t cmd) +{ + if (cmd >= CMD_DP_STATUS) { + cmd -= CMD_DP_STATUS; + if (cmd < ARRAY_SIZE(pe_vdm_dp_cmd_name)) + return pe_vdm_dp_cmd_name[cmd]; + } + + return NULL; +} +#endif /* CONFIG_USB_PD_ALT_MODE */ + +#endif /* if (PE_EVT_INFO_VDM_DIS == 0) */ + +static inline void print_vdm_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ +#if (PE_EVT_INFO_VDM_DIS == 0) + uint8_t cmd; + uint8_t cmd_type; + uint16_t svid; + const char *name = NULL; + uint32_t vdm_hdr = pd_port->curr_vdm_hdr; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + cmd = PD_VDO_CMD(vdm_hdr); + cmd_type = PD_VDO_CMDT(vdm_hdr); + svid = PD_VDO_VID(vdm_hdr); + + name = assign_vdm_cmd_name(cmd); + +#ifdef CONFIG_USB_PD_ALT_MODE + if (name == NULL && svid == USB_SID_DISPLAYPORT) + name = assign_vdm_dp_cmd_name(cmd); +#endif /* CONFIG_USB_PD_ALT_MODE */ + + if (name == NULL) + return; + +#if PE_INFO_ENABLE + if (cmd_type >= ARRAY_SIZE(pe_vdm_cmd_type_name)) + return; + + PE_INFO("%s:%s\n", name, pe_vdm_cmd_type_name[cmd_type]); +#endif /* PE_INFO_ENABLE */ + +#endif /* PE_EVT_INFO_VDM_DIS */ +} + +static inline bool pd_process_sop_vdm( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_make_vdm_state_transit_sop(pd_port)) + return true; + +#ifdef CONFIG_USB_PD_SVDM + if (pd_process_custom_vdm(pd_port, true)) + return true; +#endif /* CONFIG_USB_PD_SVDM */ + + PE_DBG("Unknown SVDM\n"); + return false; +} + +static inline bool pd_process_sop_prime_vdm( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + return pd_make_vdm_state_transit_cable(pd_port); +} + +static inline bool pd_process_data_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + bool ret = false; + uint32_t vdm_hdr; + struct pd_msg *pd_msg = pd_event->pd_msg; + + if (pd_event->msg != PD_DATA_VENDOR_DEF) + return ret; + + vdm_hdr = pd_msg->payload[0]; + pd_port->curr_vdm_hdr = vdm_hdr; + pd_port->curr_vdm_ops = PD_VDO_OPOS(vdm_hdr); + pd_port->curr_vdm_svid = PD_VDO_VID(vdm_hdr); + + if (!PD_VDO_SVDM(vdm_hdr)) + return pd_process_uvdm(pd_port, pd_event); + + /* From Port Partner, copy curr_state from pd_state */ + if (PD_VDO_CMDT(vdm_hdr) == CMDT_INIT) { + pd_port->pe_vdm_state = pd_port->pe_pd_state; + pd_port->pe_state_curr = pd_port->pe_pd_state; + +#if PE_DBG_RESET_VDM_DIS == 0 + PE_DBG("reset vdm_state\n"); +#endif /* if PE_DBG_RESET_VDM_DIS == 0 */ + } + + print_vdm_msg(pd_port, pd_event); + + if (pd_port->curr_vdm_svid != USB_SID_PD && + !dpm_get_svdm_svid_data(pd_port, pd_port->curr_vdm_svid) && + pd_port->curr_vdm_svid != USB_VID_MQP) { + PE_TRANSIT_STATE(pd_port, PE_UFP_VDM_SEND_NAK); + ret = true; + } else if (pd_msg->frame_type == TCPC_TX_SOP_PRIME) { + ret = pd_process_sop_prime_vdm(pd_port, pd_event); + } else { + ret = pd_process_sop_vdm(pd_port, pd_event); + } + + return ret; +} + +/* + * [BLOCK] Porcess PDM MSG + */ + +static inline bool pd_process_dpm_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + if (pd_event->msg != PD_DPM_ACK) + return false; + + if (pd_port->pe_data.vdm_state_flags & + VDM_STATE_FLAG_BACK_READY_IF_DPM_ACK) { + pe_transit_ready_state(pd_port); + return true; + } + + return false; +} + +/* + * [BLOCK] Porcess HW MSG + */ + +static inline bool pd_process_hw_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + struct pe_data *pe_data = &pd_port->pe_data; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_event->msg == PD_HW_TX_DISCARD && + (pe_data->vdm_discard_retry_count < 10)) { + PE_INFO("vdm_discard_retry\n"); + pe_data->vdm_discard_retry_flag = true; + pe_data->vdm_discard_retry_count++; + } + + switch (pd_event->msg) { + case PD_HW_TX_FAILED: + case PD_HW_TX_DISCARD: + return pd_make_vdm_state_transit_nak(pd_port); + + case PD_HW_RETRY_VDM: + if (pd_port->pe_data.vdm_state_timer) + return pd_make_vdm_state_transit_nak(pd_port); + PE_DBG("RetryVDM\n"); + return pd_process_sop_vdm(pd_port, pd_event); + } + + return false; +} + +/* + * [BLOCK] Porcess PE MSG + */ + +static inline bool pd_process_pe_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_PE_VDM_NOT_SUPPORT: + break; + + case PD_PE_VDM_RESET: + pd_port->pe_data.reset_vdm_state = true; + break; + + default: + return false; + } + + return pd_make_vdm_state_transit_nak(pd_port); +} + +/* + * [BLOCK] Porcess Timer MSG + */ + +static inline bool pd_process_timer_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { + case PD_TIMER_VDM_MODE_ENTRY: + case PD_TIMER_VDM_MODE_EXIT: + case PD_TIMER_VDM_RESPONSE: + case PD_TIMER_UVDM_RESPONSE: + return pd_make_vdm_state_transit_nak(pd_port); + + default: + return false; + } +} + +/* + * [BLOCK] Porcess TCP MSG + */ + +const uint8_t tcp_vdm_evt_init_state[] = { + PE_DFP_UFP_VDM_IDENTITY_REQUEST, /* TCP_DPM_EVT_DISCOVER_ID */ + PE_DFP_VDM_SVIDS_REQUEST, /* TCP_DPM_EVT_DISCOVER_SVIDS */ + PE_DFP_VDM_MODES_REQUEST, /* TCP_DPM_EVT_DISCOVER_MODES */ + PE_DFP_VDM_MODE_ENTRY_REQUEST, /* TCP_DPM_EVT_ENTER_MODE */ + PE_DFP_VDM_MODE_EXIT_REQUEST, /* TCP_DPM_EVT_EXIT_MODE */ + PE_UFP_VDM_ATTENTION_REQUEST, /* TCP_DPM_EVT_ATTENTION */ + +#ifdef CONFIG_USB_PD_ALT_MODE + PE_UFP_VDM_ATTENTION_REQUEST, /* TCP_DPM_EVT_DP_ATTENTION */ +#ifdef CONFIG_USB_PD_ALT_MODE_DFP + PE_DFP_VDM_DP_STATUS_UPDATE_REQUEST, /* TCP_DPM_EVT_DP_STATUS_UPDATE */ + PE_DFP_VDM_DP_CONFIGURATION_REQUEST, /* TCP_DPM_EVT_DP_CONFIG */ +#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ +#endif /* CONFIG_USB_PD_ALT_MODE */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM + PE_DFP_UVDM_SEND, /* TCP_DPM_EVT_UVDM */ +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ +}; + +static inline bool pd_process_tcp_cable_event( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + bool ret; + int tcp_ret; +#ifdef CONFIG_PD_DISCOVER_CABLE_ID + bool role_check = true; + + if (pd_check_rev30(pd_port)) + role_check = pd_port->vconn_role; + + if (role_check) { + ret = PE_MAKE_STATE_TRANSIT(PD_DPM_MSG_DISCOVER_CABLE); + tcp_ret = ret ? TCP_DPM_RET_SENT : TCP_DPM_RET_DENIED_NOT_READY; + } else { + ret = false; + tcp_ret = TCP_DPM_RET_DENIED_WRONG_DATA_ROLE; + } +#else + ret = false; + tcp_ret = TCP_DPM_RET_DENIED_UNKNOWN; +#endif /* CONFIG_PD_DISCOVER_CABLE_ID */ + + pd_notify_tcp_event_1st_result(pd_port, tcp_ret); + return ret; +} + +#ifdef CONFIG_USB_PD_ALT_MODE + +static inline uint32_t tcpc_update_bits( + uint32_t var, uint32_t update, uint32_t mask) +{ + return (var & (~mask)) | (update & mask); +} + +static inline void pd_parse_tcp_dpm_evt_svdm(struct pd_port *pd_port) +{ + struct tcp_dpm_svdm_data *svdm_data = + &pd_port->tcp_event.tcp_dpm_data.svdm_data; + + pd_port->mode_svid = svdm_data->svid; + pd_port->mode_obj_pos = svdm_data->ops; +} + +static inline void pd_parse_tcp_dpm_evt_dp_status(struct pd_port *pd_port) +{ + struct tcp_dpm_dp_data *dp_data_tcp = + &pd_port->tcp_event.tcp_dpm_data.dp_data; + + struct dp_data *dp_data = pd_get_dp_data(pd_port); + + pd_port->mode_svid = USB_SID_DISPLAYPORT; + dp_data->local_status = tcpc_update_bits( + dp_data->local_status, + dp_data_tcp->val, dp_data_tcp->mask); +} + +#ifdef CONFIG_USB_PD_ALT_MODE_DFP + +static inline void pd_parse_tcp_dpm_evt_dp_config(struct pd_port *pd_port) +{ + struct tcp_dpm_dp_data *dp_data_tcp = + &pd_port->tcp_event.tcp_dpm_data.dp_data; + struct dp_data *dp_data = pd_get_dp_data(pd_port); + + pd_port->mode_svid = USB_SID_DISPLAYPORT; + dp_data->local_config = tcpc_update_bits( + dp_data->local_config, dp_data_tcp->val, dp_data_tcp->mask); +} + +#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ +#endif /* CONFIG_USB_PD_ALT_MODE */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM +static inline void pd_parse_tcp_dpm_evt_uvdm(struct pd_port *pd_port) +{ + struct tcp_dpm_custom_vdm_data *vdm_data = + &pd_port->tcp_event.tcp_dpm_data.vdm_data; + + pd_port->uvdm_cnt = vdm_data->cnt; + pd_port->uvdm_wait_resp = vdm_data->wait_resp; + memcpy(pd_port->uvdm_data, + vdm_data->vdos, sizeof(uint32_t) * vdm_data->cnt); + +#ifdef CONFIG_USB_PD_SVDM + if (pd_check_rev30(pd_port) && + (pd_port->uvdm_data[0] & VDO_SVDM_TYPE)) + pd_port->uvdm_data[0] |= VDO_SVDM_VERS(SVDM_REV20); +#endif /* CONFIG_USB_PD_SVDM */ +} +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + +static inline void pd_parse_tcp_dpm_evt_from_tcpm( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + switch (pd_event->msg) { +#ifdef CONFIG_USB_PD_KEEP_SVIDS + case TCP_DPM_EVT_DISCOVER_SVIDS: + pd_port->pe_data.remote_svid_list.cnt = 0; + break; +#endif /* CONFIG_USB_PD_KEEP_SVIDS */ + +#ifdef CONFIG_USB_PD_ALT_MODE + case TCP_DPM_EVT_DISCOVER_MODES: + case TCP_DPM_EVT_ENTER_MODE: + case TCP_DPM_EVT_EXIT_MODE: + case TCP_DPM_EVT_ATTENTION: + pd_parse_tcp_dpm_evt_svdm(pd_port); + break; + + case TCP_DPM_EVT_DP_ATTENTION: + pd_parse_tcp_dpm_evt_dp_status(pd_port); + break; + +#ifdef CONFIG_USB_PD_ALT_MODE_DFP + case TCP_DPM_EVT_DP_STATUS_UPDATE: + pd_parse_tcp_dpm_evt_dp_status(pd_port); + break; + case TCP_DPM_EVT_DP_CONFIG: + pd_parse_tcp_dpm_evt_dp_config(pd_port); + break; +#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ +#endif /* CONFIG_USB_PD_ALT_MODE */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM + case TCP_DPM_EVT_UVDM: + pd_parse_tcp_dpm_evt_uvdm(pd_port); + break; +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + } +} + +static inline bool pd_check_tcp_msg_valid( + struct pd_port *pd_port, uint8_t new_state) +{ + return pd_make_vdm_state_transit(pd_port, new_state); +} + +static inline bool pd_process_tcp_msg( + struct pd_port *pd_port, struct pd_event *pd_event) +{ + uint8_t new_state; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_event->msg == TCP_DPM_EVT_DISCOVER_CABLE) + return pd_process_tcp_cable_event(pd_port, pd_event); + + if (!pd_check_pe_state_ready(pd_port)) { + pd_notify_tcp_event_1st_result( + pd_port, TCP_DPM_RET_DENIED_NOT_READY); + PE_DBG("skip vdm_request, not ready_state (%d)\n", + pd_port->pe_state_curr); + return false; + } + + new_state = pd_event->msg - TCP_DPM_EVT_DISCOVER_ID; + if (new_state >= ARRAY_SIZE(tcp_vdm_evt_init_state)) { + PD_BUG_ON(1); + return false; + } + + new_state = tcp_vdm_evt_init_state[new_state]; + + if (!pd_check_tcp_msg_valid(pd_port, new_state)) { + pd_notify_tcp_event_1st_result( + pd_port, TCP_DPM_RET_DENIED_WRONG_DATA_ROLE); + PE_DBG("skip vdm_request, WRONG DATA ROLE\n"); + return false; + } + + if (pd_event->msg_sec == PD_TCP_FROM_TCPM) + pd_parse_tcp_dpm_evt_from_tcpm(pd_port, pd_event); + + PE_TRANSIT_STATE(pd_port, new_state); + pd_notify_tcp_event_1st_result(pd_port, TCP_DPM_RET_SENT); + return true; +} + +/* + * [BLOCK] Process Policy Engine's VDM Message + */ + +bool pd_process_event_vdm(struct pd_port *pd_port, struct pd_event *pd_event) +{ + pd_port->pe_data.vdm_discard_retry_flag = false; + + switch (pd_event->event_type) { + case PD_EVT_CTRL_MSG: + return pd_process_ctrl_msg(pd_port, pd_event); + + case PD_EVT_DATA_MSG: + return pd_process_data_msg(pd_port, pd_event); + + case PD_EVT_DPM_MSG: + return pd_process_dpm_msg(pd_port, pd_event); + + case PD_EVT_HW_MSG: + return pd_process_hw_msg(pd_port, pd_event); + + case PD_EVT_PE_MSG: + return pd_process_pe_msg(pd_port, pd_event); + + case PD_EVT_TIMER_MSG: + return pd_process_timer_msg(pd_port, pd_event); + + case PD_EVT_TCP_MSG: + return pd_process_tcp_msg(pd_port, pd_event); + } + + return false; +} diff --git a/drivers/usb/typec/mmi_tcpc/rt-regmap.c b/drivers/usb/typec/mmi_tcpc/rt-regmap.c new file mode 100644 index 000000000000..66f9ae78cf5b --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/rt-regmap.c @@ -0,0 +1,1641 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020 MediaTek Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inc/rt-regmap.h" +#define RT_REGMAP_VERSION "1.2.1_G" + +#define ERR_MSG_SIZE 128 +#define MAX_BYTE_SIZE 32 + +struct rt_regmap_ops { + int (*regmap_block_write)(struct rt_regmap_device *rd, u32 reg, + int bytes, const void *src); + int (*regmap_block_read)(struct rt_regmap_device *rd, u32 reg, + int bytes, void *dst); +}; + +enum { + RT_DBG_REG_ADDR, + RT_DBG_DATA, + RT_DBG_REGS, + RT_DBG_SYNC, + RT_DBG_ERROR, + RT_DBG_NAME, + RT_DBG_BLOCK, + RT_DBG_SIZE, + RT_DBG_DEVICE_ADDR, + RT_DBG_SUPPORT_MODE, + RT_DBG_IO_LOG, + RT_DBG_CACHE_MODE, + RT_DBG_REG_SIZE, + RT_DBG_MAX, +}; + +struct reg_index_offset { + int index; + int offset; +}; + +#ifdef CONFIG_DEBUG_FS +struct rt_debug_data { + struct reg_index_offset rio; + unsigned int reg_addr; + unsigned int reg_size; +}; + +struct rt_debug_st { + void *info; + int id; +}; +#endif /* CONFIG_DEBUG_FS */ + +/* rt_regmap_device + * + * Richtek regmap device. One for each rt_regmap. + * + */ +struct rt_regmap_device { + struct rt_regmap_properties props; + struct rt_regmap_fops *rops; + struct rt_regmap_ops regmap_ops; + struct device dev; + void *client; + struct semaphore semaphore; + struct semaphore write_mode_lock; + struct delayed_work rt_work; + int dev_addr; + unsigned char *alloc_data; + unsigned char **cache_data; + unsigned long *cached; + unsigned long *cache_dirty; + char *err_msg; + unsigned char error_occurred:1; + unsigned char regval[MAX_BYTE_SIZE]; + + int (*rt_block_write[4])(struct rt_regmap_device *rd, + const struct rt_register *rm, int size, + const unsigned char *wdata, int count, + int cache_idx, int cache_offset); +#ifdef CONFIG_DEBUG_FS + struct dentry *rt_den; + struct dentry *rt_debug_file[RT_DBG_MAX]; + struct rt_debug_st rtdbg_st[RT_DBG_MAX]; + struct dentry **rt_reg_file; + struct rt_debug_st **reg_st; + struct rt_debug_data dbg_data; +#endif /* CONFIG_DEBUG_FS */ +}; + +static struct reg_index_offset find_register_index( + const struct rt_regmap_device *rd, u32 reg) +{ + int i = 0, j = 0, unit = RT_1BYTE_MODE; + struct reg_index_offset rio = {-1, -1}; + const rt_register_map_t *rm = rd->props.rm; + + for (i = 0; i < rd->props.register_num; i++) { + if (reg == rm[i]->addr) { + rio.index = i; + rio.offset = 0; + break; + } + if (reg > rm[i]->addr && + (reg - rm[i]->addr) < rm[i]->size) { + rio.index = i; + for (j = 0; rd->props.group[j].mode != RT_DUMMY_MODE; + j++) { + if (reg >= rd->props.group[j].start && + reg <= rd->props.group[j].end) { + unit = rd->props.group[j].mode; + break; + } + } + rio.offset = (reg - rm[i]->addr) * unit; + } + } + return rio; +} + +static int rt_chip_block_write(struct rt_regmap_device *rd, u32 reg, + int bytes, const void *src); + +/* rt_regmap_cache_sync - sync all cache data to chip */ +void rt_regmap_cache_sync(struct rt_regmap_device *rd) +{ + int ret = 0, i = 0, j = 0; + const rt_register_map_t rm = NULL; + + down(&rd->semaphore); + for (i = 0; i < rd->props.register_num; i++) { + if (!rd->cache_dirty[i]) + continue; + rm = rd->props.rm[i]; + for (j = 0; j < rm->size; j++) { + if (!test_bit(j, &rd->cache_dirty[i])) + continue; + ret = rt_chip_block_write(rd, rm->addr + j, 1, + rd->cache_data[i] + j); + if (ret < 0) { + dev_notice(&rd->dev, + "%s block write fail(%d) @ 0x%02x\n", + __func__, ret, rm->addr + j); + goto err_cache_sync; + } + } + rd->cache_dirty[i] = 0; + } + dev_info(&rd->dev, "%s successfully\n", __func__); +err_cache_sync: + up(&rd->semaphore); +} +EXPORT_SYMBOL(rt_regmap_cache_sync); + +/* rt_regmap_cache_write_back - write current cache data to chip + * @rd: rt_regmap_device pointer. + * @reg: register address + */ +void rt_regmap_cache_write_back(struct rt_regmap_device *rd, u32 reg) +{ + int ret = 0, j = 0; + struct reg_index_offset rio = {-1, -1}; + const rt_register_map_t rm = NULL; + + rio = find_register_index(rd, reg); + if (rio.index < 0 || rio.offset != 0) { + dev_notice(&rd->dev, "%s reg 0x%02x is out of map\n", + __func__, reg); + return; + } + + down(&rd->semaphore); + if (!rd->cache_dirty[rio.index]) + goto out; + rm = rd->props.rm[rio.index]; + for (j = 0; j < rm->size; j++) { + if (!test_bit(j, &rd->cache_dirty[rio.index])) + continue; + ret = rt_chip_block_write(rd, rm->addr + j, 1, + rd->cache_data[rio.index] + j); + if (ret < 0) { + dev_notice(&rd->dev, + "%s block write fail(%d) @ 0x%02x\n", + __func__, ret, rm->addr + j); + goto err_cache_write_back; + } + } + rd->cache_dirty[rio.index] = 0; +out: + dev_info(&rd->dev, "%s successfully\n", __func__); +err_cache_write_back: + up(&rd->semaphore); +} +EXPORT_SYMBOL(rt_regmap_cache_write_back); + +/* rt_is_reg_volatile - check register map is volatile or not + * @rd: rt_regmap_device pointer. + * @reg: register address. + */ +int rt_is_reg_volatile(struct rt_regmap_device *rd, u32 reg) +{ + struct reg_index_offset rio = {-1, -1}; + const rt_register_map_t rm = NULL; + + rio = find_register_index(rd, reg); + if (rio.index < 0) { + dev_notice(&rd->dev, "%s reg 0x%02x is out of range\n", + __func__, reg); + return -EINVAL; + } + rm = rd->props.rm[rio.index]; + + return (rm->reg_type & RT_REG_TYPE_MASK) == RT_VOLATILE ? 1 : 0; +} +EXPORT_SYMBOL(rt_is_reg_volatile); + +/* rt_reg_regsize - get register map size for specific register + * @rd: rt_regmap_device pointer. + * @reg: register address + */ +int rt_get_regsize(struct rt_regmap_device *rd, u32 reg) +{ + struct reg_index_offset rio = {-1, -1}; + + rio = find_register_index(rd, reg); + if (rio.index < 0 || rio.offset != 0) { + dev_notice(&rd->dev, "%s reg 0x%02x is out of map\n", + __func__, reg); + return -EINVAL; + } + return rd->props.rm[rio.index]->size; +} +EXPORT_SYMBOL(rt_get_regsize); + +static void rt_work_func(struct work_struct *work) +{ + struct rt_regmap_device *rd = + container_of(work, struct rt_regmap_device, rt_work.work); + + dev_info(&rd->dev, "%s\n", __func__); + rt_regmap_cache_sync(rd); +} + +static int rt_chip_block_write(struct rt_regmap_device *rd, u32 reg, + int bytes, const void *src) +{ + if ((rd->props.rt_regmap_mode & RT_IO_BLK_MODE_MASK) == RT_IO_BLK_ALL || + (rd->props.rt_regmap_mode & RT_IO_BLK_MODE_MASK) == RT_IO_BLK_CHIP) + return -EPERM; + + return rd->rops->write_device(rd->client, reg, bytes, src); +} + +static int rt_chip_block_read(struct rt_regmap_device *rd, u32 reg, + int bytes, void *dst) +{ + return rd->rops->read_device(rd->client, reg, bytes, dst); +} + +static int rt_block_write(struct rt_regmap_device *rd, + const struct rt_register *rm, int size, + const unsigned char *wdata, int count, + int cache_idx, int cache_offset) +{ + int ret = 0, j = 0, change = 0; + + down(&rd->write_mode_lock); + for (j = cache_offset; j < cache_offset + size; j++, count++) { + ret = test_and_set_bit(j, &rd->cached[cache_idx]); + if (ret && (rm->reg_type & RT_WR_ONCE)) { + if (rd->cache_data[cache_idx][j] == + (wdata[count] & rm->wbit_mask[j])) + continue; + } + rd->cache_data[cache_idx][j] = wdata[count] & rm->wbit_mask[j]; + change++; + } + + if (!change) + goto out; + + ret = rt_chip_block_write(rd, rm->addr + cache_offset, size, + rd->cache_data[cache_idx] + cache_offset); + if (ret < 0) + dev_notice(&rd->dev, "%s block write fail(%d) @ 0x%02x\n", + __func__, ret, rm->addr + cache_offset); +out: + up(&rd->write_mode_lock); + return ret < 0 ? ret : 0; +} + +static int rt_block_write_blk_all(struct rt_regmap_device *rd, + const struct rt_register *rm, int size, + const unsigned char *wdata, int count, + int cache_idx, int cache_offset) +{ + return 0; +} + +static int rt_block_write_blk_cache(struct rt_regmap_device *rd, + const struct rt_register *rm, int size, + const unsigned char *wdata, int count, + int cache_idx, int cache_offset) +{ + int ret = 0; + + down(&rd->write_mode_lock); + ret = rt_chip_block_write(rd, rm->addr + cache_offset, size, + &wdata[count]); + up(&rd->write_mode_lock); + if (ret < 0) + dev_notice(&rd->dev, "%s block write fail(%d) @ 0x%02x\n", + __func__, ret, rm->addr + cache_offset); + return ret < 0 ? ret : 0; +} + +static int rt_block_write_blk_chip(struct rt_regmap_device *rd, + const struct rt_register *rm, int size, + const unsigned char *wdata, int count, + int cache_idx, int cache_offset) +{ + int ret = 0, j = 0; + + down(&rd->write_mode_lock); + for (j = cache_offset; j < cache_offset + size; j++, count++) { + ret = test_and_set_bit(j, &rd->cached[cache_idx]); + if (ret && (rm->reg_type & RT_WR_ONCE)) { + if (rd->cache_data[cache_idx][j] == + (wdata[count] & rm->wbit_mask[j])) + continue; + } + rd->cache_data[cache_idx][j] = wdata[count] & rm->wbit_mask[j]; + set_bit(j, &rd->cache_dirty[cache_idx]); + } + up(&rd->write_mode_lock); + return 0; +} + +static int (*rt_block_map[])(struct rt_regmap_device *rd, + const struct rt_register *rm, int size, + const unsigned char *wdata, int count, + int cache_idx, int cache_offset) = { + &rt_block_write, + &rt_block_write_blk_all, + &rt_block_write_blk_cache, + &rt_block_write_blk_chip, +}; + +static int _rt_cache_block_write(struct rt_regmap_device *rd, u32 reg, + int bytes, const void *src, bool asyn) +{ + int ret = 0, i = 0, j = 0, count = 0, size = 0; + struct reg_index_offset rio = {-1, -1}; + const rt_register_map_t rm = NULL; + const unsigned char *wdata = src; + unsigned char wri_data[128], blk_index = 0; + + rio = find_register_index(rd, reg); + if (rio.index < 0) { + dev_notice(&rd->dev, "%s reg 0x%02x is out of range\n", + __func__, reg); + return -EINVAL; + } + + for (i = rio.index, j = rio.offset, count = 0; + i < rd->props.register_num && count < bytes; + i++, j = 0, count += size) { + rm = rd->props.rm[i]; + size = (bytes - count) <= (rm->size - j) ? + (bytes - count) : (rm->size - j); + if ((rm->reg_type & RT_REG_TYPE_MASK) == RT_VOLATILE) { + ret = rt_chip_block_write(rd, rm->addr + j, size, + &wdata[count]); + } else if (asyn) { + ret = rd->props.rt_regmap_mode & RT_IO_BLK_MODE_MASK; + if (ret == RT_IO_BLK_ALL || ret == RT_IO_BLK_CACHE) { + dev_notice(&rd->dev, "%s ret = %d\n", + __func__, ret); + ret = -EPERM; + goto err_cache_block_write; + } + + ret = rt_block_write_blk_chip + (rd, rm, size, wdata, count, i, j); + } else { + blk_index = (rd->props.rt_regmap_mode & + RT_IO_BLK_MODE_MASK) >> 3; + + ret = rd->rt_block_write[blk_index] + (rd, rm, size, wdata, count, i, j); + } + if (ret < 0) { + dev_notice(&rd->dev, + "%s block write fail(%d) @ 0x%02x\n", + __func__, ret, rm->addr + j); + goto err_cache_block_write; + } + } + if (rd->props.io_log_en) { + j = 0; + for (i = 0; i < count; i++) { + ret = snprintf(wri_data + j, sizeof(wri_data) - j, + "%02x,", wdata[i]); + if ((ret < 0) || (ret >= sizeof(wri_data) - j)) + return -EINVAL; + + j += ret; + } + dev_info(&rd->dev, "RT_REGMAP [WRITE] reg0x%02x [Data] %s\n", + reg, wri_data); + } + return 0; +err_cache_block_write: + return ret; +} + +static int rt_cache_block_write(struct rt_regmap_device *rd, u32 reg, + int bytes, const void *src) +{ + return _rt_cache_block_write(rd, reg, bytes, src, false); +} + +static int rt_asyn_cache_block_write(struct rt_regmap_device *rd, u32 reg, + int bytes, const void *src) +{ + int ret = 0; + + ret = _rt_cache_block_write(rd, reg, bytes, src, true); + if (ret >= 0) + mod_delayed_work(system_wq, &rd->rt_work, msecs_to_jiffies(1)); + return ret; +} + +static int rt_cache_block_read(struct rt_regmap_device *rd, u32 reg, + int bytes, void *dest) +{ + int ret = 0, i = 0, j = 0, count = 0, total_bytes = 0; + struct reg_index_offset rio = {-1, -1}; + const rt_register_map_t rm = NULL; + unsigned long mask = 0; + + rio = find_register_index(rd, reg); + if (rio.index < 0) { + dev_notice(&rd->dev, "%s reg 0x%02x is out of range\n", + __func__, reg); + return -EINVAL; + } + + rm = rd->props.rm[rio.index]; + + total_bytes += (rm->size - rio.offset); + + for (i = rio.index + 1; i < rd->props.register_num; i++) + total_bytes += rd->props.rm[i]->size; + + if (bytes > total_bytes) { + dev_notice(&rd->dev, "%s bytes %d is out of range\n", + __func__, bytes); + return -EINVAL; + } + + for (i = rio.index, j = rio.offset, count = 0; + i < rd->props.register_num && count < bytes; i++, j = 0) { + rm = rd->props.rm[i]; + mask = GENMASK(rm->size - 1, j); + if ((rd->cached[i] & mask) == mask) { + count += rm->size - j; + continue; + } + ret = rd->rops->read_device(rd->client, rm->addr, + rm->size, rd->regval); + if (ret < 0) { + dev_notice(&rd->dev, + "%s read device fail(%d) @ 0x%02x\n", + __func__, ret, rm->addr); + return ret; + } + for (; j < rm->size && count < bytes; j++, count++) { + if (test_bit(j, &rd->cached[i])) + continue; + rd->cache_data[i][j] = rd->regval[j]; + if ((rm->reg_type & RT_REG_TYPE_MASK) != RT_VOLATILE) + set_bit(j, &rd->cached[i]); + } + } + + if (rd->props.io_log_en) + dev_info(&rd->dev, "RT_REGMAP [READ] reg0x%02x\n", reg); + + memcpy(dest, &rd->cache_data[rio.index][rio.offset], bytes); + + return 0; +} + +static u32 cpu_to_chip(struct rt_regmap_device *rd, u32 cpu_data, int size) +{ + u32 chip_data = 0; + + if (rd->props.rt_format == RT_BIG_ENDIAN) { +#ifdef CONFIG_CPU_BIG_ENDIAN + chip_data = cpu_data << (4 - size) * 8; +#else + chip_data = cpu_to_be32(cpu_data); + chip_data >>= (4 - size) * 8; +#endif /* CONFIG_CPU_BIG_ENDIAN */ + } else { + chip_data = cpu_to_le32(cpu_data); + } + + return chip_data; +} + +static u32 chip_to_cpu(struct rt_regmap_device *rd, u32 chip_data, int size) +{ + u32 cpu_data = 0; + + if (rd->props.rt_format == RT_BIG_ENDIAN) { +#ifdef CONFIG_CPU_BIG_ENDIAN + cpu_data = chip_data >> ((4 - size) * 8); +#else + cpu_data = be32_to_cpu(chip_data); + cpu_data >>= (4 - size) * 8; +#endif /* CONFIG_CPU_BIG_ENDIAN */ + } else { + cpu_data = le32_to_cpu(chip_data); + } + + return cpu_data; +} + +/* _rt_regmap_reg_write - write data to specific register map + * only support 1, 2, 4 bytes regisetr map + * @rd: rt_regmap_device pointer. + * @rrd: rt_reg_data pointer. + */ +static int _rt_regmap_reg_write(struct rt_regmap_device *rd, + struct rt_reg_data *rrd, bool asyn) +{ + int ret = -ENOTSUPP, size = 0; + struct reg_index_offset rio = {-1, -1}; + const rt_register_map_t *rm = rd->props.rm; + u32 tmp_data = 0; + + rio = find_register_index(rd, rrd->reg); + if (rio.index < 0 || rio.offset != 0) { + dev_notice(&rd->dev, "%s reg 0x%02x is out of map\n", + __func__, rrd->reg); + return -EINVAL; + } + + size = rm[rio.index]->size; + if (size < 1 || size > 4) { + dev_notice(&rd->dev, "%s only support 1~4 bytes(%d)\n", + __func__, size); + return -EINVAL; + } + + tmp_data = cpu_to_chip(rd, rrd->rt_data.data_u32, size); + + down(&rd->semaphore); + ret = (asyn ? rt_asyn_cache_block_write : + rd->regmap_ops.regmap_block_write) + (rd, rrd->reg, size, &tmp_data); + up(&rd->semaphore); + if (ret < 0) + dev_notice(&rd->dev, "%s block write fail(%d) @ 0x%02x\n", + __func__, ret, rrd->reg); + return (ret < 0) ? ret : 0; +} + +int rt_regmap_reg_write(struct rt_regmap_device *rd, struct rt_reg_data *rrd, + u32 reg, const u32 data) +{ + rrd->reg = reg; + rrd->rt_data.data_u32 = data; + return _rt_regmap_reg_write(rd, rrd, false); +} +EXPORT_SYMBOL(rt_regmap_reg_write); + +int rt_asyn_regmap_reg_write(struct rt_regmap_device *rd, + struct rt_reg_data *rrd, u32 reg, const u32 data) +{ + rrd->reg = reg; + rrd->rt_data.data_u32 = data; + return _rt_regmap_reg_write(rd, rrd, true); +} +EXPORT_SYMBOL(rt_asyn_regmap_reg_write); + +/* _rt_regmap_reg_read - register read for specific register map + * only support 1, 2, 4 bytes register map. + * @rd: rt_regmap_device pointer. + * @rrd: rt_reg_data pointer. + */ +static int _rt_regmap_reg_read(struct rt_regmap_device *rd, + struct rt_reg_data *rrd) +{ + int ret = -ENOTSUPP, size = 0; + struct reg_index_offset rio = {-1, -1}; + const rt_register_map_t *rm = rd->props.rm; + u32 data = 0; + + rio = find_register_index(rd, rrd->reg); + if (rio.index < 0 || rio.offset != 0) { + dev_notice(&rd->dev, "%s reg 0x%02x is out of map\n", + __func__, rrd->reg); + return -EINVAL; + } + + size = rm[rio.index]->size; + if (size < 1 || size > 4) { + dev_notice(&rd->dev, "%s only support 1~4 bytes(%d)\n", + __func__, size); + return -EINVAL; + } + + down(&rd->semaphore); + ret = rd->regmap_ops.regmap_block_read(rd, rrd->reg, size, &data); + up(&rd->semaphore); + if (ret < 0) { + dev_notice(&rd->dev, "%s block read fail(%d) @ 0x%02x\n", + __func__, ret, rrd->reg); + goto out; + } + rrd->rt_data.data_u32 = chip_to_cpu(rd, data, size); +out: + return (ret < 0) ? ret : 0; +} + +int rt_regmap_reg_read(struct rt_regmap_device *rd, + struct rt_reg_data *rrd, u32 reg) +{ + rrd->reg = reg; + return _rt_regmap_reg_read(rd, rrd); +} +EXPORT_SYMBOL(rt_regmap_reg_read); + +/* _rt_regmap_update_bits - assign bits specific register map */ +static int _rt_regmap_update_bits(struct rt_regmap_device *rd, + struct rt_reg_data *rrd) +{ + + int ret = -ENOTSUPP, size = 0; + struct reg_index_offset rio = {-1, -1}; + const rt_register_map_t *rm = rd->props.rm; + u32 new = 0, old = 0; + bool change = false; + + rio = find_register_index(rd, rrd->reg); + if (rio.index < 0 || rio.offset != 0) { + dev_notice(&rd->dev, "%s reg 0x%02x is out of map\n", + __func__, rrd->reg); + return -EINVAL; + } + + size = rm[rio.index]->size; + if (size < 1 || size > 4) { + dev_notice(&rd->dev, "%s only support 1~4 bytes(%d)\n", + __func__, size); + return -EINVAL; + } + + down(&rd->semaphore); + ret = rd->regmap_ops.regmap_block_read(rd, rrd->reg, size, &old); + if (ret < 0) { + dev_notice(&rd->dev, "%s block read fail(%d) @ 0x%02x\n", + __func__, ret, rrd->reg); + goto out; + } + + old = chip_to_cpu(rd, old, size); + + new = (old & ~(rrd->mask)) | (rrd->rt_data.data_u32 & rrd->mask); + change = old != new; + if ((rm[rio.index]->reg_type & RT_WR_ONCE) && !change) + goto out; + + new = cpu_to_chip(rd, new, size); + + ret = rd->regmap_ops.regmap_block_write + (rd, rrd->reg, size, &new); + if (ret < 0) + dev_notice(&rd->dev, "%s block write fail(%d) @ 0x%02x\n", + __func__, ret, rrd->reg); +out: + up(&rd->semaphore); + return (ret < 0) ? ret : 0; +} + +int rt_regmap_update_bits(struct rt_regmap_device *rd, struct rt_reg_data *rrd, + u32 reg, u32 mask, u32 data) +{ + rrd->reg = reg; + rrd->mask = mask; + rrd->rt_data.data_u32 = data; + return _rt_regmap_update_bits(rd, rrd); +} +EXPORT_SYMBOL(rt_regmap_update_bits); + +/* rt_regmap_block_write - block write data to register + * @rd: rt_regmap_device pointer + * @reg: register address + * @bytes: length for write + * @src: source of write data + */ +int rt_regmap_block_write(struct rt_regmap_device *rd, u32 reg, + int bytes, const void *src) +{ + int ret = 0; + + down(&rd->semaphore); + ret = rd->regmap_ops.regmap_block_write(rd, reg, bytes, src); + up(&rd->semaphore); + return (ret < 0) ? ret : 0; +} +EXPORT_SYMBOL(rt_regmap_block_write); + +/* rt_asyn_regmap_block_write - asyn block write */ +int rt_asyn_regmap_block_write(struct rt_regmap_device *rd, u32 reg, + int bytes, const void *src) +{ + int ret = 0; + + down(&rd->semaphore); + ret = rt_asyn_cache_block_write(rd, reg, bytes, src); + up(&rd->semaphore); + return (ret < 0) ? ret : 0; +} +EXPORT_SYMBOL(rt_asyn_regmap_block_write); + +/* rt_regmap_block_read - block read data form register + * @rd: rt_regmap_device pointer + * @reg: register address + * @bytes: length for read + * @dst: destination for read data + */ +int rt_regmap_block_read(struct rt_regmap_device *rd, u32 reg, + int bytes, void *dst) +{ + int ret = 0; + + down(&rd->semaphore); + ret = rd->regmap_ops.regmap_block_read(rd, reg, bytes, dst); + up(&rd->semaphore); + return (ret < 0) ? ret : 0; +} +EXPORT_SYMBOL(rt_regmap_block_read); + +void rt_cache_getlasterror(struct rt_regmap_device *rd, char *buf, size_t size) +{ + int ret = 0; + + down(&rd->semaphore); + ret = snprintf(buf, size, "%s", rd->err_msg); + up(&rd->semaphore); + if ((ret < 0) || (ret >= size)) + dev_notice(&rd->dev, "%s snprintf fail(%d)\n", __func__, ret); +} +EXPORT_SYMBOL(rt_cache_getlasterror); + +void rt_cache_clrlasterror(struct rt_regmap_device *rd) +{ + int ret = 0; + + down(&rd->semaphore); + rd->error_occurred = 0; + rd->err_msg[0] = 0; + up(&rd->semaphore); + if ((ret < 0) || (ret >= ERR_MSG_SIZE)) + dev_notice(&rd->dev, "%s snprintf fail(%d)\n", __func__, ret); +} +EXPORT_SYMBOL(rt_cache_clrlasterror); + +/* initialize cache data from rt_register */ +static int rt_regmap_cache_init(struct rt_regmap_device *rd) +{ + int ret = 0, i = 0, j = 0, count = 0, bytes_num = 0; + const rt_register_map_t *rm = rd->props.rm; + + pr_info("%s\n", __func__); + + down(&rd->semaphore); + rd->cache_data = devm_kzalloc(&rd->dev, rd->props.register_num * + sizeof(*rd->cache_data), GFP_KERNEL); + rd->cached = devm_kzalloc(&rd->dev, rd->props.register_num * + sizeof(*rd->cached), GFP_KERNEL); + rd->cache_dirty = devm_kzalloc(&rd->dev, rd->props.register_num * + sizeof(*rd->cache_dirty), GFP_KERNEL); + + if (!rd->cache_data || !rd->cached || !rd->cache_dirty) { + ret = -ENOMEM; + goto out; + } + + if (rd->props.group == NULL) { + rd->props.group = devm_kzalloc(&rd->dev, + sizeof(*rd->props.group) * 2, + GFP_KERNEL); + if (!rd->props.group) { + ret = -ENOMEM; + goto out; + } + rd->props.group[0].start = 0; + rd->props.group[0].end = U32_MAX; + rd->props.group[0].mode = RT_1BYTE_MODE; + rd->props.group[1].mode = RT_DUMMY_MODE; + } + + for (i = 0; i < rd->props.register_num; i++) + bytes_num += rm[i]->size; + + rd->alloc_data = devm_kzalloc(&rd->dev, bytes_num, GFP_KERNEL); + if (!rd->alloc_data) { + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < rd->props.register_num; i++) { + rd->cache_data[i] = rd->alloc_data + count; + count += rm[i]->size; + } + + /* set 0xff writeable mask for NORMAL and RESERVE type */ + for (i = 0; i < rd->props.register_num; i++) { + if ((rm[i]->reg_type & RT_REG_TYPE_MASK) == RT_NORMAL || + (rm[i]->reg_type & RT_REG_TYPE_MASK) == RT_RESERVE) { + for (j = 0; j < rm[i]->size; j++) + rm[i]->wbit_mask[j] = 0xff; + } + } + + pr_info("%s successfully\n", __func__); +out: + up(&rd->semaphore); + return ret; +} + +/* rt_regmap_cache_reload - reload cache from chip */ +int rt_regmap_cache_reload(struct rt_regmap_device *rd) +{ + int i = 0; + + dev_info(&rd->dev, "%s\n", __func__); + down(&rd->semaphore); + for (i = 0; i < rd->props.register_num; i++) + rd->cache_dirty[i] = rd->cached[i] = 0; + up(&rd->semaphore); + return 0; +} +EXPORT_SYMBOL(rt_regmap_cache_reload); + +/* rt_regmap_add_debubfs - add user own debugfs node + * @rd: rt_regmap_devcie pointer. + * @name: a pointer to a string containing the name of the file to create. + * @mode: the permission that the file should have. + * @data: a pointer to something that the caller will want to get to later + * on. The inode.i_private pointer will point to this value on + * the open() call. + * @fops: a pointer to a struct file_operations that should be used for + * this file. + */ +int rt_regmap_add_debugfs(struct rt_regmap_device *rd, const char *name, + umode_t mode, void *data, + const struct file_operations *fops) +{ +#ifdef CONFIG_DEBUG_FS + struct dentry *den = NULL; + + den = debugfs_create_file(name, mode, rd->rt_den, data, fops); + if (!den) + return -EINVAL; +#endif /* CONFIG_DEBUG_FS */ + return 0; +} +EXPORT_SYMBOL(rt_regmap_add_debugfs); + +static void rt_regmap_set_cache_mode(struct rt_regmap_device *rd, + unsigned char mode) +{ + unsigned char mode_mask = mode & RT_CACHE_MODE_MASK; + + dev_info(&rd->dev, "%s mode_mask = %d\n", __func__, mode_mask); + + down(&rd->write_mode_lock); + if (mode_mask == RT_CACHE_WR_THROUGH) { + rt_regmap_cache_reload(rd); + rd->regmap_ops.regmap_block_write = rt_cache_block_write; + rd->regmap_ops.regmap_block_read = rt_cache_block_read; + } else if (mode_mask == RT_CACHE_WR_BACK) { + rt_regmap_cache_reload(rd); + rd->regmap_ops.regmap_block_write = rt_asyn_cache_block_write; + rd->regmap_ops.regmap_block_read = rt_cache_block_read; + } else if (mode_mask == RT_CACHE_DISABLE) { + rd->regmap_ops.regmap_block_write = rt_chip_block_write; + rd->regmap_ops.regmap_block_read = rt_chip_block_read; + } else { + dev_notice(&rd->dev, "%s invalid cache mode\n", __func__); + goto err_mode; + } + + rd->props.rt_regmap_mode &= ~RT_CACHE_MODE_MASK; + rd->props.rt_regmap_mode |= mode_mask; +err_mode: + up(&rd->write_mode_lock); +} + +#ifdef CONFIG_DEBUG_FS +struct dentry *rt_regmap_dir; + +#define erro_printf(rd, fmt, ...) \ +do { \ + int ret = 0; \ + size_t len = 0; \ + \ + dev_notice(&rd->dev, fmt, ##__VA_ARGS__); \ + down(&rd->semaphore); \ + len = strlen(rd->err_msg); \ + ret = snprintf(rd->err_msg + len, ERR_MSG_SIZE - len, \ + fmt, ##__VA_ARGS__); \ + rd->error_occurred = 1; \ + up(&rd->semaphore); \ + if ((ret < 0) || (ret >= ERR_MSG_SIZE - len)) \ + dev_notice(&rd->dev, "%s snprintf fail(%d)\n", \ + __func__, ret); \ +} while (0) + +static int get_parameters(char *buf, unsigned long *param, int num_of_par) +{ + int cnt = 0; + char *token = strsep(&buf, " "); + + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token) { + if (kstrtoul(token, 0, ¶m[cnt]) != 0) + return -EINVAL; + + token = strsep(&buf, " "); + } else + return -EINVAL; + } + + return 0; +} + +static int get_data(const char *buf, size_t count, + unsigned char *data_buffer, unsigned int data_length) +{ + int i = 0, ptr = 0; + u8 value = 0; + char token[5] = {0}; + + token[0] = '0'; + token[1] = 'x'; + token[4] = 0; + if (buf[0] != '0' || buf[1] != 'x') + return -EINVAL; + + ptr = 2; + for (i = 0; (i < data_length) && (ptr + 2 <= count); i++) { + token[2] = buf[ptr++]; + token[3] = buf[ptr++]; + ptr++; + if (kstrtou8(token, 16, &value) != 0) + return -EINVAL; + data_buffer[i] = value; + } + return 0; +} + +static void rt_show_regs(struct rt_regmap_device *rd, struct seq_file *seq_file) +{ + int ret = 0, i = 0, j = 0; + const rt_register_map_t *rm = rd->props.rm; + + for (i = 0; i < rd->props.register_num; i++) { + down(&rd->semaphore); + ret = rd->regmap_ops.regmap_block_read(rd, rm[i]->addr, + rm[i]->size, rd->regval); + up(&rd->semaphore); + if (ret < 0) { + erro_printf(rd, "%s block read fail(%d) @ 0x%02x\n", + __func__, ret, rm[i]->addr); + break; + } + + if ((rm[i]->reg_type & RT_REG_TYPE_MASK) != RT_RESERVE) { + seq_printf(seq_file, "reg0x%02x:0x", rm[i]->addr); + for (j = 0; j < rm[i]->size; j++) + seq_printf(seq_file, "%02x,", rd->regval[j]); + seq_puts(seq_file, "\n"); + } else + seq_printf(seq_file, + "reg0x%02x:reserve\n", rm[i]->addr); + } +} + +static int general_read(struct seq_file *seq_file, void *_data) +{ + int ret = 0, i = 0, size = 0; + struct rt_debug_st *st = seq_file->private; + struct rt_regmap_device *rd = st->info; + unsigned char data = 0; + + switch (st->id) { + case RT_DBG_REG_ADDR: + seq_printf(seq_file, "0x%02x\n", rd->dbg_data.reg_addr); + break; + case RT_DBG_DATA: + if (rd->dbg_data.reg_size == 0) + rd->dbg_data.reg_size = 1; + size = rd->dbg_data.reg_size; + + down(&rd->semaphore); + if (rd->dbg_data.rio.index < 0) + ret = rt_chip_block_read(rd, rd->dbg_data.reg_addr, + size, rd->regval); + else + ret = rd->regmap_ops.regmap_block_read(rd, + rd->dbg_data.reg_addr, + size, rd->regval); + up(&rd->semaphore); + if (ret < 0) { + erro_printf(rd, "%s block read fail(%d) @ 0x%02x\n", + __func__, ret, rd->dbg_data.reg_addr); + break; + } + + seq_puts(seq_file, "0x"); + for (i = 0; i < size; i++) + seq_printf(seq_file, "%02x,", rd->regval[i]); + seq_puts(seq_file, "\n"); + break; + case RT_DBG_ERROR: + seq_puts(seq_file, "======== Error Message ========\n"); + seq_puts(seq_file, rd->error_occurred ? rd->err_msg : + "No Error\n"); + break; + case RT_DBG_REGS: + rt_show_regs(rd, seq_file); + break; + case RT_DBG_NAME: + seq_printf(seq_file, "%s\n", rd->props.aliases); + break; + case RT_DBG_SIZE: + seq_printf(seq_file, "%u\n", rd->dbg_data.reg_size); + break; + case RT_DBG_BLOCK: + data = rd->props.rt_regmap_mode & RT_IO_BLK_MODE_MASK; + if (data == RT_IO_PASS_THROUGH) + seq_puts(seq_file, "0 => IO_PASS_THROUGH\n"); + else if (data == RT_IO_BLK_ALL) + seq_puts(seq_file, "1 => IO_BLK_ALL\n"); + else if (data == RT_IO_BLK_CACHE) + seq_puts(seq_file, "2 => IO_BLK_CACHE\n"); + else if (data == RT_IO_BLK_CHIP) + seq_puts(seq_file, "3 => IO_BLK_CHIP\n"); + break; + case RT_DBG_DEVICE_ADDR: + seq_printf(seq_file, "0x%02x\n", rd->dev_addr); + break; + case RT_DBG_SUPPORT_MODE: + seq_puts(seq_file, " == BLOCK MODE ==\n"); + seq_puts(seq_file, "0 => IO_PASS_THROUGH\n"); + seq_puts(seq_file, "1 => IO_BLK_ALL\n"); + seq_puts(seq_file, "2 => IO_BLK_CACHE\n"); + seq_puts(seq_file, "3 => IO_BLK_CHIP\n"); + seq_puts(seq_file, " == CACHE MODE ==\n"); + seq_puts(seq_file, "0 => CACHE_WR_THROUGH\n"); + seq_puts(seq_file, "1 => CACHE_WR_BACK\n"); + seq_puts(seq_file, "2 => CACHE_DISABLE\n"); + break; + case RT_DBG_IO_LOG: + seq_printf(seq_file, "%d\n", rd->props.io_log_en); + break; + case RT_DBG_CACHE_MODE: + data = rd->props.rt_regmap_mode & RT_CACHE_MODE_MASK; + if (data == RT_CACHE_WR_THROUGH) + seq_puts(seq_file, "0 => CACHE_WR_THROUGH\n"); + else if (data == RT_CACHE_WR_BACK) + seq_puts(seq_file, "1 => CACHE_WR_BACK\n"); + else if (data == RT_CACHE_DISABLE) + seq_puts(seq_file, "2 => CACHE_DISABLE\n"); + break; + case RT_DBG_REG_SIZE: + size = rt_get_regsize(rd, rd->dbg_data.reg_addr); + seq_printf(seq_file, "%d\n", size); + break; + } + return 0; +} + +static int general_open(struct inode *inode, struct file *file) +{ + return single_open(file, general_read, inode->i_private); +} + +static ssize_t general_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + int ret = 0; + struct rt_debug_st *st = + ((struct seq_file *)file->private_data)->private; + struct rt_regmap_device *rd = st->info; + struct reg_index_offset rio = {-1, -1}; + char lbuf[128]; + ssize_t res = 0; + unsigned int size = 0; + unsigned long param = 0; + + dev_info(&rd->dev, "%s @ %p, count = %u, pos = %llu\n", + __func__, ubuf, (unsigned int)count, *ppos); + *ppos = 0; + res = simple_write_to_buffer(lbuf, sizeof(lbuf) - 1, ppos, ubuf, count); + if (res <= 0) + return -EFAULT; + count = res; + lbuf[count] = '\0'; + + switch (st->id) { + case RT_DBG_REG_ADDR: + ret = get_parameters(lbuf, ¶m, 1); + if (ret < 0) + return ret; + rio = find_register_index(rd, param); + down(&rd->semaphore); + rd->dbg_data.rio = rio; + rd->dbg_data.reg_addr = param; + up(&rd->semaphore); + if (rio.index < 0) + erro_printf(rd, "%s reg 0x%02lx is out of range\n", + __func__, param); + break; + case RT_DBG_DATA: + if (rd->dbg_data.reg_size == 0) + rd->dbg_data.reg_size = 1; + size = rd->dbg_data.reg_size; + + if ((size - 1) * 3 + 5 != count) { + erro_printf(rd, + "%s wrong input length, size = %u, count = %u\n", + __func__, size, (unsigned int)count); + return -EINVAL; + } + + memset(rd->regval, 0, sizeof(rd->regval)); + ret = get_data(lbuf, count, rd->regval, size); + if (ret < 0) { + erro_printf(rd, "%s get data fail(%d)\n", + __func__, ret); + return ret; + } + + down(&rd->semaphore); + if (rd->dbg_data.rio.index < 0) + ret = rt_chip_block_write(rd, rd->dbg_data.reg_addr, + size, rd->regval); + else + ret = rd->regmap_ops.regmap_block_write(rd, + rd->dbg_data.reg_addr, + size, rd->regval); + up(&rd->semaphore); + if (ret < 0) { + erro_printf(rd, "%s block write fail(%d) @ 0x%02x\n", + __func__, ret, rd->dbg_data.reg_addr); + return ret; + } + break; + case RT_DBG_SYNC: + ret = get_parameters(lbuf, ¶m, 1); + if (ret < 0) + return ret; + if (param) + rt_regmap_cache_sync(rd); + break; + case RT_DBG_ERROR: + ret = get_parameters(lbuf, ¶m, 1); + if (ret < 0) + return ret; + if (param) + rt_cache_clrlasterror(rd); + break; + case RT_DBG_SIZE: + ret = get_parameters(lbuf, ¶m, 1); + if (ret < 0) + return ret; + if (param >= 1 && param <= MAX_BYTE_SIZE) { + down(&rd->semaphore); + rd->dbg_data.reg_size = param; + up(&rd->semaphore); + } else { + erro_printf(rd, "%s size(%lu) must be %d ~ %d\n", + __func__, param, 1, MAX_BYTE_SIZE); + return -EINVAL; + } + break; + case RT_DBG_BLOCK: + ret = get_parameters(lbuf, ¶m, 1); + if (ret < 0) + return ret; + if (param > 3) + param = 3; + param <<= 3; + + down(&rd->semaphore); + rd->props.rt_regmap_mode &= ~RT_IO_BLK_MODE_MASK; + rd->props.rt_regmap_mode |= param; + up(&rd->semaphore); + if (param == RT_IO_PASS_THROUGH) + rt_regmap_cache_sync(rd); + break; + case RT_DBG_IO_LOG: + ret = get_parameters(lbuf, ¶m, 1); + if (ret < 0) + return ret; + down(&rd->semaphore); + rd->props.io_log_en = !!param; + up(&rd->semaphore); + break; + case RT_DBG_CACHE_MODE: + ret = get_parameters(lbuf, ¶m, 1); + if (ret < 0) + return ret; + if (param > 2) + param = 2; + param <<= 1; + rt_regmap_set_cache_mode(rd, param); + break; + default: + return -EINVAL; + } + + return count; +} + +static const struct file_operations general_ops = { + .owner = THIS_MODULE, + .open = general_open, + .write = general_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +#define RT_CREATE_GENERAL_FILE(_id, _name, _mode) \ +{ \ + rd->rtdbg_st[_id].info = rd; \ + rd->rtdbg_st[_id].id = _id; \ + rd->rt_debug_file[_id] = debugfs_create_file(_name, _mode, dir, \ + &rd->rtdbg_st[_id], &general_ops); \ + if (!rd->rt_debug_file[_id]) \ + return -EINVAL; \ +} + +/* create general debugfs node */ +static int rt_create_general_debug(struct rt_regmap_device *rd, + struct dentry *dir) +{ + RT_CREATE_GENERAL_FILE(RT_DBG_REG_ADDR, "reg_addr", 0444); + RT_CREATE_GENERAL_FILE(RT_DBG_DATA, "data", 0444); + RT_CREATE_GENERAL_FILE(RT_DBG_REGS, "regs", 0444); + RT_CREATE_GENERAL_FILE(RT_DBG_SYNC, "sync", 0444); + RT_CREATE_GENERAL_FILE(RT_DBG_ERROR, "Error", 0444); + RT_CREATE_GENERAL_FILE(RT_DBG_NAME, "name", 0444); + RT_CREATE_GENERAL_FILE(RT_DBG_BLOCK, "block", 0444); + RT_CREATE_GENERAL_FILE(RT_DBG_SIZE, "size", 0444); + RT_CREATE_GENERAL_FILE(RT_DBG_DEVICE_ADDR, "device_addr", 0444); + RT_CREATE_GENERAL_FILE(RT_DBG_SUPPORT_MODE, "support_mode", 0444); + RT_CREATE_GENERAL_FILE(RT_DBG_IO_LOG, "io_log", 0444); + RT_CREATE_GENERAL_FILE(RT_DBG_CACHE_MODE, "cache_mode", 0444); + RT_CREATE_GENERAL_FILE(RT_DBG_REG_SIZE, "reg_size", 0444); + + return 0; +} + +static int eachreg_open(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t eachreg_read(struct file *file, char __user *ubuf, + size_t count, loff_t *ppos) +{ + int ret = 0, i = 0, j = 0; + struct rt_debug_st *st = file->private_data; + struct rt_regmap_device *rd = st->info; + const rt_register_map_t rm = rd->props.rm[st->id]; + char *lbuf = NULL; + const size_t lbuf_size = MAX_BYTE_SIZE * 3 + 11; + + lbuf = kzalloc(lbuf_size, GFP_KERNEL); + if (!lbuf) + return -ENOMEM; + + down(&rd->semaphore); + ret = rd->regmap_ops.regmap_block_read(rd, rm->addr, + rm->size, rd->regval); + up(&rd->semaphore); + if (ret < 0) { + dev_notice(&rd->dev, "%s block read fail(%d) @ 0x%02x\n", + __func__, ret, rm->size); + goto out; + } + + ret = snprintf(lbuf + j, lbuf_size - j, "reg0x%02x:0x", rm->addr); + if ((ret < 0) || (ret >= lbuf_size - j)) { + ret = -EINVAL; + goto out; + } + j += ret; + for (i = 0; i < rm->size; i++) { + ret = snprintf(lbuf + j, lbuf_size - j, "%02x,", rd->regval[i]); + if ((ret < 0) || (ret >= lbuf_size - j)) { + ret = -EINVAL; + goto out; + } + j += ret; + } + ret = snprintf(lbuf + j, lbuf_size - j, "\n"); + if ((ret < 0) || (ret >= lbuf_size - j)) { + ret = -EINVAL; + goto out; + } + j += ret; + ret = simple_read_from_buffer(ubuf, count, ppos, lbuf, strlen(lbuf)); +out: + kfree(lbuf); + return ret; +} + +static ssize_t eachreg_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + int ret = 0; + struct rt_debug_st *st = file->private_data; + struct rt_regmap_device *rd = st->info; + const rt_register_map_t rm = rd->props.rm[st->id]; + char lbuf[128]; + ssize_t res = 0; + + if ((rm->size - 1) * 3 + 5 != count) { + dev_notice(&rd->dev, + "%s wrong input length, size = %u, count = %u\n", + __func__, rm->size, (unsigned int)count); + return -EINVAL; + } + + dev_info(&rd->dev, "%s @ %p, count = %u, pos = %llu\n", + __func__, ubuf, (unsigned int)count, *ppos); + *ppos = 0; + res = simple_write_to_buffer(lbuf, sizeof(lbuf) - 1, ppos, ubuf, count); + if (res <= 0) + return -EFAULT; + count = res; + lbuf[count] = '\0'; + + memset(rd->regval, 0, sizeof(rd->regval)); + ret = get_data(lbuf, count, rd->regval, rm->size); + if (ret < 0) { + dev_notice(&rd->dev, "%s get data fail(%d)\n", __func__, ret); + return ret; + } + + down(&rd->semaphore); + ret = rd->regmap_ops.regmap_block_write(rd, rm->addr, rm->size, + rd->regval); + up(&rd->semaphore); + if (ret < 0) { + dev_notice(&rd->dev, "%s block write fail(%d) @ 0x%02x\n", + __func__, ret, rm->addr); + return ret; + } + + return count; +} + +static const struct file_operations eachreg_ops = { + .open = eachreg_open, + .read = eachreg_read, + .write = eachreg_write, +}; + +/* create every register node at debugfs */ +static int rt_create_every_debug(struct rt_regmap_device *rd, + struct dentry *dir) +{ + int ret = 0, i = 0; + char buf[10]; + + rd->rt_reg_file = devm_kzalloc(&rd->dev, + rd->props.register_num * sizeof(*rd->rt_reg_file), GFP_KERNEL); + if (!rd->rt_reg_file) + return -ENOMEM; + + rd->reg_st = devm_kzalloc(&rd->dev, + rd->props.register_num * sizeof(*rd->reg_st), + GFP_KERNEL); + if (!rd->reg_st) + return -ENOMEM; + + for (i = 0; i < rd->props.register_num; i++) { + ret = snprintf(buf, sizeof(buf), + "reg0x%02x", rd->props.rm[i]->addr); + if ((ret < 0) || (ret >= sizeof(buf))) { + dev_notice(&rd->dev, "%s snprintf fail(%d)\n", + __func__, ret); + continue; + } + + rd->rt_reg_file[i] = devm_kzalloc(&rd->dev, + sizeof(*rd->rt_reg_file[i]), + GFP_KERNEL); + rd->reg_st[i] = devm_kzalloc(&rd->dev, + sizeof(*rd->reg_st[i]), + GFP_KERNEL); + if (!rd->rt_reg_file[i] || !rd->reg_st[i]) + return -ENOMEM; + + rd->reg_st[i]->info = rd; + rd->reg_st[i]->id = i; + rd->rt_reg_file[i] = debugfs_create_file(buf, 0444, dir, + rd->reg_st[i], + &eachreg_ops); + if (!rd->rt_reg_file[i]) + return -EINVAL; + } + + return 0; +} +#endif /* CONFIG_DEBUG_FS */ + +/* check the rt_register format is correct */ +static int rt_regmap_check(struct rt_regmap_device *rd) +{ + int i = 0; + const rt_register_map_t *rm = rd->props.rm; + + /* check name property */ + if (!rd->props.name) { + pr_notice("%s no name\n", __func__); + return -EINVAL; + } + + for (i = 0; i < rd->props.register_num; i++) { + /* check byte size, 1 byte ~ 32 bytes is valid */ + if (rm[i]->size < 1 || rm[i]->size > MAX_BYTE_SIZE) { + pr_notice("%s size(%d) must be %d ~ %d @ 0x%02x\n", + __func__, rm[i]->size, 1, MAX_BYTE_SIZE, + rm[i]->addr); + return -EINVAL; + } + } + + for (i = 0; i < rd->props.register_num - 1; i++) { + /* check register sequence */ + if (rm[i]->addr >= rm[i + 1]->addr) { + pr_info("%s sequence error @ 0x%02x\n", + __func__, rm[i]->addr); + } + } + + /* no default reg_addr and reister_map first addr is not 0x00 */ +#ifdef CONFIG_DEBUG_FS + if (!rd->dbg_data.reg_addr && rm[0]->addr) { + rd->dbg_data.reg_addr = rm[0]->addr; + rd->dbg_data.rio.index = 0; + rd->dbg_data.rio.offset = 0; + } +#endif /* CONFIG_DEBUG_FS */ + return 0; +} + +struct rt_regmap_device *rt_regmap_device_register_ex + (struct rt_regmap_properties *props, + struct rt_regmap_fops *rops, + struct device *parent, + void *client, int dev_addr, void *drvdata) +{ + int ret = 0, i = 0; + struct rt_regmap_device *rd = NULL; + + if (!props) { + pr_notice("%s rt_regmap_properties is NULL\n", __func__); + return NULL; + } + if (!rops) { + pr_notice("%s rt_regmap_fops is NULL\n", __func__); + return NULL; + } + + pr_info("%s name = %s\n", __func__, props->name); + rd = kzalloc(sizeof(*rd), GFP_KERNEL); + if (!rd) + return NULL; + + memcpy(&rd->props, props, sizeof(rd->props)); + rd->rops = rops; + rd->dev.parent = parent; + dev_set_drvdata(&rd->dev, drvdata); + rd->client = client; + sema_init(&rd->semaphore, 1); + sema_init(&rd->write_mode_lock, 1); + INIT_DELAYED_WORK(&rd->rt_work, rt_work_func); + rd->dev_addr = dev_addr; + + /* check rt_registe_map format */ + ret = rt_regmap_check(rd); + if (ret < 0) { + pr_notice("%s check fail(%d)\n", __func__, ret); + goto out; + } + + dev_set_name(&rd->dev, "rt_regmap_%s", rd->props.name); + ret = device_register(&rd->dev); + if (ret) { + pr_notice("%s device register fail(%d)\n", __func__, ret); + goto out; + } + + rd->err_msg = devm_kzalloc(&rd->dev, ERR_MSG_SIZE, GFP_KERNEL); + if (!rd->err_msg) + goto err_msgalloc; + + ret = rt_regmap_cache_init(rd); + if (ret < 0) { + pr_notice("%s init fail(%d)\n", __func__, ret); + goto err_cacheinit; + } + + for (i = 0; i <= 3; i++) + rd->rt_block_write[i] = rt_block_map[i]; + + rt_regmap_set_cache_mode(rd, rd->props.rt_regmap_mode); + +#ifdef CONFIG_DEBUG_FS + rd->rt_den = debugfs_create_dir(props->name, rt_regmap_dir); + if (rd->rt_den) { + ret = rt_create_general_debug(rd, rd->rt_den); + if (ret < 0) { + pr_notice("%s create general debug fail(%d)\n", + __func__, ret); + goto err_create_general_debug; + } + if (rd->props.rt_regmap_mode & RT_DBG_MODE_MASK) { + ret = rt_create_every_debug(rd, rd->rt_den); + if (ret < 0) { + pr_notice("%s create every debug fail(%d)\n", + __func__, ret); + goto err_create_every_debug; + } + } + } else { + pr_notice("%s debugfs create dir fail\n", __func__); + goto err_debug; + } +#endif /* CONFIG_DEBUG_FS */ + + return rd; + +#ifdef CONFIG_DEBUG_FS +err_create_every_debug: +err_create_general_debug: + debugfs_remove_recursive(rd->rt_den); +err_debug: +#endif /* CONFIG_DEBUG_FS */ +err_cacheinit: +err_msgalloc: + device_unregister(&rd->dev); +out: + kfree(rd); + return NULL; +} +EXPORT_SYMBOL(rt_regmap_device_register_ex); + +/* rt_regmap_device_unregister - unregister rt_regmap_device */ +void rt_regmap_device_unregister(struct rt_regmap_device *rd) +{ + if (!rd) + return; + down(&rd->semaphore); + rd->rops = NULL; + up(&rd->semaphore); +#ifdef CONFIG_DEBUG_FS + debugfs_remove_recursive(rd->rt_den); +#endif /* CONFIG_DEBUG_FS */ + device_unregister(&rd->dev); + kfree(rd); +} +EXPORT_SYMBOL(rt_regmap_device_unregister); + +int regmap_plat_init(void) +{ + pr_info("Init Richtek RegMap %s\n", RT_REGMAP_VERSION); +#ifdef CONFIG_DEBUG_FS + rt_regmap_dir = debugfs_create_dir("rt-regmap", NULL); + if (!rt_regmap_dir) { + pr_notice("%s debugfs create dir fail\n", __func__); + return -EINVAL; + } +#endif /* CONFIG_DEBUG_FS */ + return 0; +} + +void regmap_plat_exit(void) +{ +#ifdef CONFIG_DEBUG_FS + debugfs_remove(rt_regmap_dir); +#endif /* CONFIG_DEBUG_FS */ +} + +MODULE_DESCRIPTION("Richtek regmap Driver"); +MODULE_AUTHOR("Jeff Chang "); +MODULE_VERSION(RT_REGMAP_VERSION); +MODULE_LICENSE("GPL"); + +/* Release Note + * 1.2.1 + * Fix the deadlock in rt_asyn_cache_block_write() + * + * 1.2.0 + * Revise memory allocation, code flow, and error handling + * + * 1.1.14 + * Fix Coverity by Mandatory's + */ diff --git a/drivers/usb/typec/mmi_tcpc/rt_pd_manager.c b/drivers/usb/typec/mmi_tcpc/rt_pd_manager.c new file mode 100644 index 000000000000..a3920a349c25 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/rt_pd_manager.c @@ -0,0 +1,1203 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Richtek RT PD Manager + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "inc/tcpci_typec.h" +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +#include +#endif +#define RT_PD_MANAGER_VERSION "0.0.8_G" +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) +#define PROBE_CNT_MAX 15 +#else +#define PROBE_CNT_MAX 3 +#endif +/* 10ms * 100 = 1000ms = 1s */ +#define USB_TYPE_POLLING_INTERVAL 10 + +#if defined(CONFIG_TCPC_MAX_POLLING_COUNT) +#define USB_TYPE_POLLING_CNT_MAX CONFIG_TCPC_MAX_POLLING_COUNT +#else +#define USB_TYPE_POLLING_CNT_MAX 100 +#endif + +enum dr { + DR_IDLE, + DR_DEVICE, + DR_HOST, + DR_DEVICE_TO_HOST, + DR_HOST_TO_DEVICE, + DR_MAX, +}; + +static const char * const dr_names[DR_MAX] = { + "Idle", "Device", "Host", "Device to Host", "Host to Device", +}; + +struct rt_pd_manager_data { + struct device *dev; + struct extcon_dev *extcon; + struct iio_channel **iio_channels; + struct delayed_work usb_dwork; + struct tcpc_device *tcpc; + struct notifier_block pd_nb; + enum dr usb_dr; + int usb_type_polling_cnt; + int sink_mv_new; + int sink_ma_new; + int sink_mv_old; + int sink_ma_old; + + struct typec_capability typec_caps; + struct typec_port *typec_port; + struct typec_partner *partner; + struct typec_partner_desc partner_desc; + struct usb_pd_identity partner_identity; + + struct charger_device *master_chg_dev; +}; + +static const unsigned int rpm_extcon_cable[] = { + EXTCON_USB, + EXTCON_USB_HOST, + EXTCON_NONE, +}; + +enum iio_psy_property { + POWER_SUPPLY_IIO_USB_REAL_TYPE = 0, + POWER_SUPPLY_IIO_OTG_ENABLE, + POWER_SUPPLY_IIO_TYPEC_MODE, + POWER_SUPPLY_IIO_PD_ACTIVE, + POWER_SUPPLY_IIO_MMI_PD_VDM_VERIFY, + POWER_SUPPLY_IIO_PROP_MAX, +}; + +static const char * const iio_channel_map[] = { + "usb_real_type", "otg_enable", "typec_mode", "pd_active", + "mmi_pd_vdm_verify", +}; + +static int mmi_get_psy_iio_property(struct rt_pd_manager_data *rpmd, + enum iio_psy_property ipp, + union power_supply_propval *val) +{ + int ret = 0, value = 0; + + ret = iio_read_channel_processed(rpmd->iio_channels[ipp], &value); + if (ret < 0) { + dev_err(rpmd->dev, "%s fail(%d), ipp = %d\n", + __func__, ret, ipp); + return ret; + } + + val->intval = value; + return 0; +} + +static int mmi_set_psy_iio_property(struct rt_pd_manager_data *rpmd, + enum iio_psy_property ipp, + const union power_supply_propval *val) +{ + int ret = 0; + + ret = iio_write_channel_raw(rpmd->iio_channels[ipp], val->intval); + if (ret < 0) { + dev_err(rpmd->dev, "%s fail(%d), ipp = %d\n", + __func__, ret, ipp); + return ret; + } + + return 0; +} + +static int extcon_init(struct rt_pd_manager_data *rpmd) +{ + int ret = 0; + + /* + * associate extcon with the dev as it could have a DT + * node which will be useful for extcon_get_edev_by_phandle() + */ + rpmd->extcon = devm_extcon_dev_allocate(rpmd->dev, rpm_extcon_cable); + if (IS_ERR(rpmd->extcon)) { + ret = PTR_ERR(rpmd->extcon); + dev_err(rpmd->dev, "%s extcon dev alloc fail(%d)\n", + __func__, ret); + goto out; + } + + ret = devm_extcon_dev_register(rpmd->dev, rpmd->extcon); + if (ret) { + dev_err(rpmd->dev, "%s extcon dev reg fail(%d)\n", + __func__, ret); + goto out; + } + + /* Support reporting polarity and speed via properties */ + extcon_set_property_capability(rpmd->extcon, EXTCON_USB, + EXTCON_PROP_USB_TYPEC_POLARITY); + extcon_set_property_capability(rpmd->extcon, EXTCON_USB, + EXTCON_PROP_USB_SS); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + extcon_set_property_capability(rpmd->extcon, EXTCON_USB, + EXTCON_PROP_USB_TYPEC_MED_HIGH_CURRENT); +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) */ + extcon_set_property_capability(rpmd->extcon, EXTCON_USB_HOST, + EXTCON_PROP_USB_TYPEC_POLARITY); + extcon_set_property_capability(rpmd->extcon, EXTCON_USB_HOST, + EXTCON_PROP_USB_SS); +out: + return ret; +} + +static void mmi_ignore_require_dpdm(struct rt_pd_manager_data *rpmd, bool value) +{ + int rc = 0; + + if (!rpmd->master_chg_dev) + rpmd->master_chg_dev = get_charger_by_name("master_chg"); + + if (!rpmd->master_chg_dev) + return; + + if (value) { + rc = charger_dev_set_dp_dm(rpmd->master_chg_dev, + MMI_POWER_SUPPLY_IGNORE_REQUEST_DPDM); + dev_info(rpmd->dev, "%s ignore dp dm request rc=%d\n", rc? "Couldn't" : " ", rc); + } else { + rc = charger_dev_set_dp_dm(rpmd->master_chg_dev, + MMI_POWER_SUPPLY_DONOT_IGNORE_REQUEST_DPDM); + dev_info(rpmd->dev, "%s enable dp dm request rc=%d\n", rc? "Couldn't" : " ", rc); + } +} + +static inline void stop_usb_host(struct rt_pd_manager_data *rpmd) +{ + extcon_set_state_sync(rpmd->extcon, EXTCON_USB_HOST, false); + mmi_ignore_require_dpdm(rpmd, false); +} + +static inline void start_usb_host(struct rt_pd_manager_data *rpmd) +{ + union extcon_property_value val = {.intval = 0}; + + val.intval = tcpm_inquire_cc_polarity(rpmd->tcpc); + extcon_set_property(rpmd->extcon, EXTCON_USB_HOST, + EXTCON_PROP_USB_TYPEC_POLARITY, val); + + val.intval = 1; + extcon_set_property(rpmd->extcon, EXTCON_USB_HOST, + EXTCON_PROP_USB_SS, val); + + extcon_set_state_sync(rpmd->extcon, EXTCON_USB_HOST, true); + mmi_ignore_require_dpdm(rpmd, true); +} + +static inline void stop_usb_peripheral(struct rt_pd_manager_data *rpmd) +{ + extcon_set_state_sync(rpmd->extcon, EXTCON_USB, false); +} + +static inline void start_usb_peripheral(struct rt_pd_manager_data *rpmd) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + int rp = 0; +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) */ + union extcon_property_value val = {.intval = 0}; + + val.intval = tcpm_inquire_cc_polarity(rpmd->tcpc); + extcon_set_property(rpmd->extcon, EXTCON_USB, + EXTCON_PROP_USB_TYPEC_POLARITY, val); + + val.intval = 1; + extcon_set_property(rpmd->extcon, EXTCON_USB, EXTCON_PROP_USB_SS, val); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) + rp = tcpm_inquire_typec_remote_rp_curr(rpmd->tcpc); + val.intval = rp > 500 ? 1 : 0; + extcon_set_property(rpmd->extcon, EXTCON_USB, + EXTCON_PROP_USB_TYPEC_MED_HIGH_CURRENT, val); +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)) */ + extcon_set_state_sync(rpmd->extcon, EXTCON_USB, true); +} + +static void usb_dwork_handler(struct work_struct *work) +{ + int ret = 0; + struct delayed_work *dwork = to_delayed_work(work); + struct rt_pd_manager_data *rpmd = + container_of(dwork, struct rt_pd_manager_data, usb_dwork); + enum dr usb_dr = rpmd->usb_dr; + union power_supply_propval val = {.intval = 0}; + + if (usb_dr < DR_IDLE || usb_dr >= DR_MAX) { + dev_err(rpmd->dev, "%s invalid usb_dr = %d\n", + __func__, usb_dr); + return; + } + + dev_info(rpmd->dev, "%s %s\n", __func__, dr_names[usb_dr]); + + switch (usb_dr) { + case DR_IDLE: + case DR_MAX: + stop_usb_peripheral(rpmd); + stop_usb_host(rpmd); + break; + case DR_DEVICE: + ret = mmi_get_psy_iio_property(rpmd, + POWER_SUPPLY_IIO_USB_REAL_TYPE, &val); + if (ret) { + dev_info(rpmd->dev, "%s charge type is failed\n", __func__); + val.intval = POWER_SUPPLY_TYPE_UNKNOWN; + } + dev_info(rpmd->dev, "%s polling_cnt = %d, ret = %d type = %d\n", + __func__, ++rpmd->usb_type_polling_cnt, + ret, val.intval); + if (ret < 0 || val.intval == POWER_SUPPLY_TYPE_UNKNOWN) { + if (rpmd->usb_type_polling_cnt < + USB_TYPE_POLLING_CNT_MAX) + schedule_delayed_work(&rpmd->usb_dwork, + msecs_to_jiffies( + USB_TYPE_POLLING_INTERVAL)); + break; + } else if (val.intval != POWER_SUPPLY_TYPE_USB && + val.intval != POWER_SUPPLY_TYPE_USB_CDP && +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,15,0) + val.intval != QTI_POWER_SUPPLY_TYPE_USB_FLOAT) +#else + val.intval != POWER_SUPPLY_TYPE_USB_FLOAT) +#endif + break; + case DR_HOST_TO_DEVICE: + stop_usb_host(rpmd); + start_usb_peripheral(rpmd); + break; + case DR_HOST: + case DR_DEVICE_TO_HOST: + stop_usb_peripheral(rpmd); + start_usb_host(rpmd); + break; + } +} + +static void sink_vbus_set_vol_and_cur(struct rt_pd_manager_data *rpmd, + int mv, int ma, uint8_t type) +{ + /* *** + * The input voltage/current limit hints from TCPC/RT1715 + */ +} + +static int pd_tcp_notifier_call(struct notifier_block *nb, + unsigned long event, void *data) +{ + int ret = 0; + struct tcp_notify *noti = data; + struct rt_pd_manager_data *rpmd = + container_of(nb, struct rt_pd_manager_data, pd_nb); + uint8_t old_state = TYPEC_UNATTACHED, new_state = TYPEC_UNATTACHED; + enum typec_pwr_opmode opmode = TYPEC_PWR_MODE_USB; + uint32_t partner_vdos[VDO_MAX_NR]; + union power_supply_propval val = {0}; + + switch (event) { + case TCP_NOTIFY_SINK_VBUS: + rpmd->sink_mv_new = noti->vbus_state.mv; + rpmd->sink_ma_new = noti->vbus_state.ma; + dev_info(rpmd->dev, "%s sink vbus %dmV %dmA type(0x%02X)\n", + __func__, rpmd->sink_mv_new, + rpmd->sink_ma_new, noti->vbus_state.type); + + if ((rpmd->sink_mv_new != rpmd->sink_mv_old) || + (rpmd->sink_ma_new != rpmd->sink_ma_old)) { + rpmd->sink_mv_old = rpmd->sink_mv_new; + rpmd->sink_ma_old = rpmd->sink_ma_new; + if (rpmd->sink_mv_new && rpmd->sink_ma_new) { + /* enable VBUS power path */ + } else { + /* disable VBUS power path */ + } + } + + sink_vbus_set_vol_and_cur(rpmd, rpmd->sink_mv_new, + rpmd->sink_ma_new, + noti->vbus_state.type); + break; + case TCP_NOTIFY_SOURCE_VBUS: + dev_info(rpmd->dev, "%s source vbus %dmV\n", + __func__, noti->vbus_state.mv); + /* enable/disable OTG power output */ + val.intval = (noti->vbus_state.mv) ? true : false; + mmi_set_psy_iio_property(rpmd, + POWER_SUPPLY_IIO_OTG_ENABLE, &val); + break; + case TCP_NOTIFY_TYPEC_STATE: + old_state = noti->typec_state.old_state; + new_state = noti->typec_state.new_state; + if (old_state == TYPEC_UNATTACHED && + (new_state == TYPEC_ATTACHED_SNK || + new_state == TYPEC_ATTACHED_NORP_SRC || + new_state == TYPEC_ATTACHED_CUSTOM_SRC || + new_state == TYPEC_ATTACHED_DBGACC_SNK)) { + dev_info(rpmd->dev, + "%s Charger plug in, polarity = %d, remote rp = %d\n", + __func__, noti->typec_state.polarity, + noti->typec_state.rp_level); + /* + * start charger type detection, + * and enable device connection + */ + cancel_delayed_work_sync(&rpmd->usb_dwork); + rpmd->usb_dr = DR_DEVICE; + rpmd->usb_type_polling_cnt = 0; + schedule_delayed_work(&rpmd->usb_dwork, + msecs_to_jiffies( + USB_TYPE_POLLING_INTERVAL)); + typec_set_data_role(rpmd->typec_port, TYPEC_DEVICE); + typec_set_pwr_role(rpmd->typec_port, TYPEC_SINK); + typec_set_pwr_opmode(rpmd->typec_port, + noti->typec_state.rp_level - + TYPEC_CC_VOLT_SNK_DFT); + typec_set_vconn_role(rpmd->typec_port, TYPEC_SINK); + } else if ((old_state == TYPEC_ATTACHED_SNK || + old_state == TYPEC_ATTACHED_NORP_SRC || + old_state == TYPEC_ATTACHED_CUSTOM_SRC || + old_state == TYPEC_ATTACHED_DBGACC_SNK) && + new_state == TYPEC_UNATTACHED) { + dev_info(rpmd->dev, "%s Charger plug out\n", __func__); + /* + * report charger plug-out, + * and disable device connection + */ + cancel_delayed_work_sync(&rpmd->usb_dwork); + rpmd->usb_dr = DR_IDLE; + schedule_delayed_work(&rpmd->usb_dwork, 0); + val.intval = MMI_POWER_SUPPLY_TYPEC_NONE; + mmi_set_psy_iio_property(rpmd, + POWER_SUPPLY_IIO_TYPEC_MODE, &val); + } else if (old_state == TYPEC_UNATTACHED && + (new_state == TYPEC_ATTACHED_SRC || + new_state == TYPEC_ATTACHED_DEBUG)) { + dev_info(rpmd->dev, + "%s OTG plug in, polarity = %d, local_rp = %d\n", + __func__, noti->typec_state.polarity, + noti->typec_state.local_rp_level); + /* enable host connection */ + cancel_delayed_work_sync(&rpmd->usb_dwork); + rpmd->usb_dr = DR_HOST; + schedule_delayed_work(&rpmd->usb_dwork, 0); + typec_set_data_role(rpmd->typec_port, TYPEC_HOST); + typec_set_pwr_role(rpmd->typec_port, TYPEC_SOURCE); + switch (noti->typec_state.local_rp_level) { + case TYPEC_RP_3_0: + opmode = TYPEC_PWR_MODE_3_0A; + break; + case TYPEC_RP_1_5: + opmode = TYPEC_PWR_MODE_1_5A; + break; + case TYPEC_RP_DFT: + default: + opmode = TYPEC_PWR_MODE_USB; + break; + } + typec_set_pwr_opmode(rpmd->typec_port, opmode); + typec_set_vconn_role(rpmd->typec_port, TYPEC_SOURCE); + } else if ((old_state == TYPEC_ATTACHED_SRC || + old_state == TYPEC_ATTACHED_DEBUG) && + new_state == TYPEC_UNATTACHED) { + dev_info(rpmd->dev, "%s OTG plug out\n", __func__); + /* disable host connection */ + cancel_delayed_work_sync(&rpmd->usb_dwork); + rpmd->usb_dr = DR_IDLE; + schedule_delayed_work(&rpmd->usb_dwork, 0); + val.intval = MMI_POWER_SUPPLY_TYPEC_NONE; + mmi_set_psy_iio_property(rpmd, + POWER_SUPPLY_IIO_TYPEC_MODE, &val); + } else if (old_state == TYPEC_UNATTACHED && + new_state == TYPEC_ATTACHED_AUDIO) { + dev_info(rpmd->dev, "%s Audio plug in\n", __func__); + /* enable AudioAccessory connection */ + val.intval = MMI_POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER; + mmi_set_psy_iio_property(rpmd, + POWER_SUPPLY_IIO_TYPEC_MODE, &val); + } else if (old_state == TYPEC_ATTACHED_AUDIO && + new_state == TYPEC_UNATTACHED) { + dev_info(rpmd->dev, "%s Audio plug out\n", __func__); + /* disable AudioAccessory connection */ + val.intval = MMI_POWER_SUPPLY_TYPEC_NONE; + mmi_set_psy_iio_property(rpmd, + POWER_SUPPLY_IIO_TYPEC_MODE, &val); + } + + if (new_state == TYPEC_UNATTACHED) { + typec_unregister_partner(rpmd->partner); + rpmd->partner = NULL; + if (rpmd->typec_caps.prefer_role == TYPEC_SOURCE) { + typec_set_data_role(rpmd->typec_port, + TYPEC_HOST); + typec_set_pwr_role(rpmd->typec_port, + TYPEC_SOURCE); + typec_set_pwr_opmode(rpmd->typec_port, + TYPEC_PWR_MODE_USB); + typec_set_vconn_role(rpmd->typec_port, + TYPEC_SOURCE); + } else { + typec_set_data_role(rpmd->typec_port, + TYPEC_DEVICE); + typec_set_pwr_role(rpmd->typec_port, + TYPEC_SINK); + typec_set_pwr_opmode(rpmd->typec_port, + TYPEC_PWR_MODE_USB); + typec_set_vconn_role(rpmd->typec_port, + TYPEC_SINK); + } + } else if (!rpmd->partner) { + memset(&rpmd->partner_identity, 0, + sizeof(rpmd->partner_identity)); + rpmd->partner_desc.usb_pd = false; + switch (new_state) { + case TYPEC_ATTACHED_AUDIO: + rpmd->partner_desc.accessory = + TYPEC_ACCESSORY_AUDIO; + break; + case TYPEC_ATTACHED_DEBUG: + case TYPEC_ATTACHED_DBGACC_SNK: + case TYPEC_ATTACHED_CUSTOM_SRC: + rpmd->partner_desc.accessory = + TYPEC_ACCESSORY_DEBUG; + break; + default: + rpmd->partner_desc.accessory = + TYPEC_ACCESSORY_NONE; + break; + } + rpmd->partner = typec_register_partner(rpmd->typec_port, + &rpmd->partner_desc); + if (IS_ERR(rpmd->partner)) { + ret = PTR_ERR(rpmd->partner); + dev_notice(rpmd->dev, + "%s typec register partner fail(%d)\n", + __func__, ret); + } + } + + if (new_state == TYPEC_ATTACHED_SNK) { + switch (noti->typec_state.rp_level) { + /* SNK_RP_3P0 */ + case TYPEC_CC_VOLT_SNK_3_0: + val.intval = MMI_POWER_SUPPLY_TYPEC_SOURCE_HIGH; + mmi_set_psy_iio_property(rpmd, + POWER_SUPPLY_IIO_TYPEC_MODE, &val); + break; + /* SNK_RP_1P5 */ + case TYPEC_CC_VOLT_SNK_1_5: + val.intval = MMI_POWER_SUPPLY_TYPEC_SOURCE_MEDIUM; + mmi_set_psy_iio_property(rpmd, + POWER_SUPPLY_IIO_TYPEC_MODE, &val); + break; + /* SNK_RP_STD */ + case TYPEC_CC_VOLT_SNK_DFT: + default: + val.intval = MMI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT; + mmi_set_psy_iio_property(rpmd, + POWER_SUPPLY_IIO_TYPEC_MODE, &val); + break; + } + } else if (new_state == TYPEC_ATTACHED_CUSTOM_SRC || + new_state == TYPEC_ATTACHED_DBGACC_SNK) { + switch (noti->typec_state.rp_level) { + /* DAM_3000 */ + case TYPEC_CC_VOLT_SNK_3_0: + val.intval = MMI_POWER_SUPPLY_TYPEC_SOURCE_HIGH; + mmi_set_psy_iio_property(rpmd, + POWER_SUPPLY_IIO_TYPEC_MODE, &val); + break; + /* DAM_1500 */ + case TYPEC_CC_VOLT_SNK_1_5: + val.intval = MMI_POWER_SUPPLY_TYPEC_SOURCE_MEDIUM; + mmi_set_psy_iio_property(rpmd, + POWER_SUPPLY_IIO_TYPEC_MODE, &val); + break; + /* DAM_500 */ + case TYPEC_CC_VOLT_SNK_DFT: + default: + val.intval = MMI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT; + mmi_set_psy_iio_property(rpmd, + POWER_SUPPLY_IIO_TYPEC_MODE, &val); + break; + } + } else if (new_state == TYPEC_ATTACHED_NORP_SRC) { + /* Both CCs are open */ + } + break; + case TCP_NOTIFY_PR_SWAP: + dev_info(rpmd->dev, "%s power role swap, new role = %d\n", + __func__, noti->swap_state.new_role); + if (noti->swap_state.new_role == PD_ROLE_SINK) { + dev_info(rpmd->dev, "%s swap power role to sink\n", + __func__); + /* + * report charger plug-in without charger type detection + * to not interfering with USB2.0 communication + */ + + typec_set_pwr_role(rpmd->typec_port, TYPEC_SINK); + } else if (noti->swap_state.new_role == PD_ROLE_SOURCE) { + dev_info(rpmd->dev, "%s swap power role to source\n", + __func__); + /* report charger plug-out */ + + typec_set_pwr_role(rpmd->typec_port, TYPEC_SOURCE); + } + break; + case TCP_NOTIFY_DR_SWAP: + dev_info(rpmd->dev, "%s data role swap, new role = %d\n", + __func__, noti->swap_state.new_role); + if (noti->swap_state.new_role == PD_ROLE_UFP) { + dev_info(rpmd->dev, "%s swap data role to device\n", + __func__); + /* + * disable host connection, + * and enable device connection + */ + cancel_delayed_work_sync(&rpmd->usb_dwork); + rpmd->usb_dr = DR_HOST_TO_DEVICE; + schedule_delayed_work(&rpmd->usb_dwork, 0); + typec_set_data_role(rpmd->typec_port, TYPEC_DEVICE); + } else if (noti->swap_state.new_role == PD_ROLE_DFP) { + dev_info(rpmd->dev, "%s swap data role to host\n", + __func__); + /* + * disable device connection, + * and enable host connection + */ + cancel_delayed_work_sync(&rpmd->usb_dwork); + rpmd->usb_dr = DR_DEVICE_TO_HOST; + schedule_delayed_work(&rpmd->usb_dwork, 0); + typec_set_data_role(rpmd->typec_port, TYPEC_HOST); + } + break; + case TCP_NOTIFY_VCONN_SWAP: + dev_info(rpmd->dev, "%s vconn role swap, new role = %d\n", + __func__, noti->swap_state.new_role); + if (noti->swap_state.new_role) { + dev_info(rpmd->dev, "%s swap vconn role to on\n", + __func__); + typec_set_vconn_role(rpmd->typec_port, TYPEC_SOURCE); + } else { + dev_info(rpmd->dev, "%s swap vconn role to off\n", + __func__); + typec_set_vconn_role(rpmd->typec_port, TYPEC_SINK); + } + break; + case TCP_NOTIFY_EXT_DISCHARGE: + dev_info(rpmd->dev, "%s ext discharge = %d\n", + __func__, noti->en_state.en); + /* enable/disable VBUS discharge */ + break; + case TCP_NOTIFY_PD_STATE: + dev_info(rpmd->dev, "%s pd state = %d\n", + __func__, noti->pd_state.connected); + switch (noti->pd_state.connected) { + case PD_CONNECT_NONE: + val.intval = MMI_POWER_SUPPLY_PD_INACTIVE; + mmi_set_psy_iio_property(rpmd, + POWER_SUPPLY_IIO_PD_ACTIVE, &val); + break; + case PD_CONNECT_HARD_RESET: + break; + case PD_CONNECT_PE_READY_SNK: + case PD_CONNECT_PE_READY_SNK_PD30: + case PD_CONNECT_PE_READY_SNK_APDO: + case PD_CONNECT_PE_READY_SRC: + case PD_CONNECT_PE_READY_SRC_PD30: + if ((noti->pd_state.connected == PD_CONNECT_PE_READY_SNK) + ||(noti->pd_state.connected == PD_CONNECT_PE_READY_SNK_PD30)) { + val.intval = MMI_POWER_SUPPLY_PD_ACTIVE; + mmi_set_psy_iio_property(rpmd, + POWER_SUPPLY_IIO_PD_ACTIVE, &val); + } else if (noti->pd_state.connected == PD_CONNECT_PE_READY_SNK_APDO) { + val.intval = MMI_POWER_SUPPLY_PD_PPS_ACTIVE; + mmi_set_psy_iio_property(rpmd, + POWER_SUPPLY_IIO_PD_ACTIVE, &val); + } else if ((noti->pd_state.connected == PD_CONNECT_PE_READY_SRC) + ||(noti->pd_state.connected == PD_CONNECT_PE_READY_SRC_PD30)) { + val.intval = MMI_POWER_SUPPLY_PD_INACTIVE; + mmi_set_psy_iio_property(rpmd, + POWER_SUPPLY_IIO_PD_ACTIVE, &val); + } + + typec_set_pwr_opmode(rpmd->typec_port, + TYPEC_PWR_MODE_PD); + if (!rpmd->partner) + break; + ret = tcpm_inquire_pd_partner_inform(rpmd->tcpc, + partner_vdos); + if (ret != TCPM_SUCCESS) + break; + rpmd->partner_identity.id_header = partner_vdos[0]; + rpmd->partner_identity.cert_stat = partner_vdos[1]; + rpmd->partner_identity.product = partner_vdos[2]; + typec_partner_set_identity(rpmd->partner); + break; + }; + break; + case TCP_NOTIFY_HARD_RESET_STATE: + switch (noti->hreset_state.state) { + case TCP_HRESET_SIGNAL_SEND: + case TCP_HRESET_SIGNAL_RECV: + break; + default: + break; + } + break; + case TCP_NOTIFY_PD_VDM_VERIFY: + dev_info(rpmd->dev, "%s mmi pd vdm verify state = %d\n", + __func__, noti->pd_state.vdm_verify); + val.intval = noti->pd_state.vdm_verify; + mmi_set_psy_iio_property(rpmd, + POWER_SUPPLY_IIO_MMI_PD_VDM_VERIFY, &val); + break; + default: + break; + }; + return NOTIFY_OK; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +static int tcpc_typec_try_role(struct typec_port *port, int role) +{ + struct rt_pd_manager_data *rpmd = typec_get_drvdata(port); +#else +static int tcpc_typec_try_role(const struct typec_capability *cap, int role) +{ + struct rt_pd_manager_data *rpmd = + container_of(cap, struct rt_pd_manager_data, typec_caps); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ + uint8_t typec_role = TYPEC_ROLE_UNKNOWN; + + dev_info(rpmd->dev, "%s role = %d\n", __func__, role); + + switch (role) { + case TYPEC_NO_PREFERRED_ROLE: + typec_role = TYPEC_ROLE_DRP; + break; + case TYPEC_SINK: + typec_role = TYPEC_ROLE_TRY_SNK; + break; + case TYPEC_SOURCE: + typec_role = TYPEC_ROLE_TRY_SRC; + break; + default: + return 0; + } + + return tcpm_typec_change_role_postpone(rpmd->tcpc, typec_role, true); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +static int tcpc_typec_dr_set(struct typec_port *port, enum typec_data_role role) +{ + struct rt_pd_manager_data *rpmd = typec_get_drvdata(port); +#else +static int tcpc_typec_dr_set(const struct typec_capability *cap, + enum typec_data_role role) +{ + struct rt_pd_manager_data *rpmd = + container_of(cap, struct rt_pd_manager_data, typec_caps); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ + int ret = 0; + uint8_t data_role = tcpm_inquire_pd_data_role(rpmd->tcpc); + bool do_swap = false; + + dev_info(rpmd->dev, "%s role = %d\n", __func__, role); + + if (role == TYPEC_HOST) { + if (data_role == PD_ROLE_UFP) { + do_swap = true; + data_role = PD_ROLE_DFP; + } + } else if (role == TYPEC_DEVICE) { + if (data_role == PD_ROLE_DFP) { + do_swap = true; + data_role = PD_ROLE_UFP; + } + } else { + dev_err(rpmd->dev, "%s invalid role\n", __func__); + return -EINVAL; + } + + if (do_swap) { + ret = tcpm_dpm_pd_data_swap(rpmd->tcpc, data_role, NULL); + if (ret != TCPM_SUCCESS) { + dev_err(rpmd->dev, "%s data role swap fail(%d)\n", + __func__, ret); + return -EPERM; + } + } + + return 0; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +static int tcpc_typec_pr_set(struct typec_port *port, enum typec_role role) +{ + struct rt_pd_manager_data *rpmd = typec_get_drvdata(port); +#else +static int tcpc_typec_pr_set(const struct typec_capability *cap, + enum typec_role role) +{ + struct rt_pd_manager_data *rpmd = + container_of(cap, struct rt_pd_manager_data, typec_caps); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ + int ret = 0; + uint8_t power_role = tcpm_inquire_pd_power_role(rpmd->tcpc); + bool do_swap = false; + + dev_info(rpmd->dev, "%s role = %d\n", __func__, role); + + if (role == TYPEC_SOURCE) { + if (power_role == PD_ROLE_SINK) { + do_swap = true; + power_role = PD_ROLE_SOURCE; + } + } else if (role == TYPEC_SINK) { + if (power_role == PD_ROLE_SOURCE) { + do_swap = true; + power_role = PD_ROLE_SINK; + } + } else { + dev_err(rpmd->dev, "%s invalid role\n", __func__); + return -EINVAL; + } + + if (do_swap) { + ret = tcpm_dpm_pd_power_swap(rpmd->tcpc, power_role, NULL); + if (ret == TCPM_ERROR_NO_PD_CONNECTED) + ret = tcpm_typec_role_swap(rpmd->tcpc); + if (ret != TCPM_SUCCESS) { + dev_err(rpmd->dev, "%s power role swap fail(%d)\n", + __func__, ret); + return -EPERM; + } + } + + return 0; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +static int tcpc_typec_vconn_set(struct typec_port *port, enum typec_role role) +{ + struct rt_pd_manager_data *rpmd = typec_get_drvdata(port); +#else +static int tcpc_typec_vconn_set(const struct typec_capability *cap, + enum typec_role role) +{ + struct rt_pd_manager_data *rpmd = + container_of(cap, struct rt_pd_manager_data, typec_caps); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ + int ret = 0; + uint8_t vconn_role = tcpm_inquire_pd_vconn_role(rpmd->tcpc); + bool do_swap = false; + + dev_info(rpmd->dev, "%s role = %d\n", __func__, role); + + if (role == TYPEC_SOURCE) { + if (vconn_role == PD_ROLE_VCONN_OFF) { + do_swap = true; + vconn_role = PD_ROLE_VCONN_ON; + } + } else if (role == TYPEC_SINK) { + if (vconn_role == PD_ROLE_VCONN_ON) { + do_swap = true; + vconn_role = PD_ROLE_VCONN_OFF; + } + } else { + dev_err(rpmd->dev, "%s invalid role\n", __func__); + return -EINVAL; + } + + if (do_swap) { + ret = tcpm_dpm_pd_vconn_swap(rpmd->tcpc, vconn_role, NULL); + if (ret != TCPM_SUCCESS) { + dev_err(rpmd->dev, "%s vconn role swap fail(%d)\n", + __func__, ret); + return -EPERM; + } + } + + return 0; +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +static int tcpc_typec_port_type_set(struct typec_port *port, + enum typec_port_type type) +{ + struct rt_pd_manager_data *rpmd = typec_get_drvdata(port); + const struct typec_capability *cap = &rpmd->typec_caps; +#else +static int tcpc_typec_port_type_set(const struct typec_capability *cap, + enum typec_port_type type) +{ + struct rt_pd_manager_data *rpmd = + container_of(cap, struct rt_pd_manager_data, typec_caps); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ + bool as_sink = tcpc_typec_is_act_as_sink_role(rpmd->tcpc); + uint8_t typec_role = TYPEC_ROLE_UNKNOWN; + + dev_info(rpmd->dev, "%s type = %d, as_sink = %d\n", + __func__, type, as_sink); + + if (0 == tcpm_typec_change_mode(rpmd->tcpc, type)) { + dev_info(rpmd->dev, "%s Only type C mode and not support PD\n", + __func__); + return 0; + } + + switch (type) { + case TYPEC_PORT_SNK: + if (as_sink) + return 0; + break; + case TYPEC_PORT_SRC: + if (!as_sink) + return 0; + break; + case TYPEC_PORT_DRP: + if (cap->prefer_role == TYPEC_SOURCE) + typec_role = TYPEC_ROLE_TRY_SRC; + else if (cap->prefer_role == TYPEC_SINK) + typec_role = TYPEC_ROLE_TRY_SNK; + else + typec_role = TYPEC_ROLE_DRP; + return tcpm_typec_change_role(rpmd->tcpc, typec_role); + default: + return 0; + } + + return tcpm_typec_role_swap(rpmd->tcpc); +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +const struct typec_operations tcpc_typec_ops = { + .try_role = tcpc_typec_try_role, + .dr_set = tcpc_typec_dr_set, + .pr_set = tcpc_typec_pr_set, + .vconn_set = tcpc_typec_vconn_set, + .port_type_set = tcpc_typec_port_type_set, +}; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ + +static int typec_init(struct rt_pd_manager_data *rpmd) +{ + int ret = 0; + + rpmd->typec_caps.type = TYPEC_PORT_DRP; + rpmd->typec_caps.data = TYPEC_PORT_DRD; + rpmd->typec_caps.revision = 0x0120; + rpmd->typec_caps.pd_revision = 0x0300; + switch (rpmd->tcpc->desc.role_def) { + case TYPEC_ROLE_SRC: + case TYPEC_ROLE_TRY_SRC: + rpmd->typec_caps.prefer_role = TYPEC_SOURCE; + break; + case TYPEC_ROLE_SNK: + case TYPEC_ROLE_TRY_SNK: + rpmd->typec_caps.prefer_role = TYPEC_SINK; + break; + default: + rpmd->typec_caps.prefer_role = TYPEC_NO_PREFERRED_ROLE; + break; + } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) + rpmd->typec_caps.driver_data = rpmd; + rpmd->typec_caps.ops = &tcpc_typec_ops; +#else + rpmd->typec_caps.try_role = tcpc_typec_try_role; + rpmd->typec_caps.dr_set = tcpc_typec_dr_set; + rpmd->typec_caps.pr_set = tcpc_typec_pr_set; + rpmd->typec_caps.vconn_set = tcpc_typec_vconn_set; + rpmd->typec_caps.port_type_set = tcpc_typec_port_type_set; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) */ + + rpmd->typec_port = typec_register_port(rpmd->dev, &rpmd->typec_caps); + if (IS_ERR(rpmd->typec_port)) { + ret = PTR_ERR(rpmd->typec_port); + dev_err(rpmd->dev, "%s typec register port fail(%d)\n", + __func__, ret); + goto out; + } + + rpmd->partner_desc.identity = &rpmd->partner_identity; +out: + return ret; +} + +#ifdef CONFIG_TCPC_NOTIFIER_LATE_SYNC +#ifdef CONFIG_USB_POWER_DELIVERY +#ifdef CONFIG_RECV_BAT_ABSENT_NOTIFY +static int fg_bat_notifier_call(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct pd_port *pd_port = container_of(nb, struct pd_port, fg_bat_nb); + struct tcpc_device *tcpc = pd_port->tcpc; + + switch (event) { + case EVENT_BATTERY_PLUG_OUT: + dev_info(&tcpc->dev, "%s: fg battery absent\n", __func__); + schedule_work(&pd_port->fg_bat_work); + break; + default: + break; + } + + return NOTIFY_OK; +} +#endif /* CONFIG_RECV_BAT_ABSENT_NOTIFY */ +#endif /* CONFIG_USB_POWER_DELIVERY */ + +static int __tcpc_class_complete_work(struct device *dev, void *data) +{ + struct tcpc_device *tcpc = dev_get_drvdata(dev); +#ifdef CONFIG_USB_POWER_DELIVERY +#ifdef CONFIG_RECV_BAT_ABSENT_NOTIFY + struct notifier_block *fg_bat_nb = &tcpc->pd_port.fg_bat_nb; + int ret = 0; +#endif /* CONFIG_RECV_BAT_ABSENT_NOTIFY */ +#endif /* CONFIG_USB_POWER_DELIVERY */ + + if (tcpc != NULL) { + pr_info("%s = %s\n", __func__, dev_name(dev)); +#if 1 + tcpc_device_irq_enable(tcpc); +#else + schedule_delayed_work(&tcpc->init_work, + msecs_to_jiffies(1000)); +#endif + +#ifdef CONFIG_USB_POWER_DELIVERY +#ifdef CONFIG_RECV_BAT_ABSENT_NOTIFY + fg_bat_nb->notifier_call = fg_bat_notifier_call; + ret = register_battery_notifier(fg_bat_nb); + if (ret < 0) { + pr_notice("%s: register bat notifier fail\n", __func__); + return -EINVAL; + } +#endif /* CONFIG_RECV_BAT_ABSENT_NOTIFY */ +#endif /* CONFIG_USB_POWER_DELIVERY */ + } + return 0; +} + +static int tcpc_class_complete_init(void) +{ + if (!IS_ERR(tcpc_class)) { + class_for_each_device(tcpc_class, NULL, NULL, + __tcpc_class_complete_work); + } + return 0; +} +#endif /* CONFIG_TCPC_NOTIFIER_LATE_SYNC */ + +static int rt_pd_manager_probe(struct platform_device *pdev) +{ + int ret = 0; + static int probe_cnt = 0; + struct rt_pd_manager_data *rpmd = NULL; + int i = 0; + + dev_info(&pdev->dev, "%s (%s) probe_cnt = %d\n", + __func__, RT_PD_MANAGER_VERSION, ++probe_cnt); + + rpmd = devm_kzalloc(&pdev->dev, sizeof(*rpmd), GFP_KERNEL); + if (!rpmd) + return -ENOMEM; + + rpmd->dev = &pdev->dev; + + ret = extcon_init(rpmd); + if (ret) { + dev_err(rpmd->dev, "%s init extcon fail(%d)\n", __func__, ret); + ret = -EPROBE_DEFER; + if (probe_cnt >= PROBE_CNT_MAX) + goto out; + else + goto err_init_extcon; + } + + rpmd->iio_channels = devm_kcalloc(rpmd->dev, POWER_SUPPLY_IIO_PROP_MAX, + sizeof(rpmd->iio_channels[0]), + GFP_KERNEL); + if (!rpmd->iio_channels) { + dev_err(rpmd->dev, "%s kcalloc fail\n", __func__); + ret = -EPROBE_DEFER; + if (probe_cnt >= PROBE_CNT_MAX) + goto out; + else + goto err_get_iio_chan; + } + + for (i = 0; i < POWER_SUPPLY_IIO_PROP_MAX; i++) { + rpmd->iio_channels[i] = + devm_iio_channel_get(rpmd->dev, iio_channel_map[i]); + if (IS_ERR(rpmd->iio_channels[i])) { + ret = PTR_ERR(rpmd->iio_channels[i]); + dev_err(rpmd->dev, "%s get iio chan %s fail(%d)\n", + __func__, iio_channel_map[i], ret); + ret = -EPROBE_DEFER; + if (probe_cnt >= PROBE_CNT_MAX) + goto out; + else + goto err_get_iio_chan; + } + } + + rpmd->tcpc = tcpc_dev_get_by_name("type_c_port0"); + if (!rpmd->tcpc) { + dev_err(rpmd->dev, "%s get tcpc dev fail\n", __func__); + ret = -EPROBE_DEFER; + if (probe_cnt >= PROBE_CNT_MAX) + goto out; + else + goto err_get_tcpc_dev; + } + + INIT_DELAYED_WORK(&rpmd->usb_dwork, usb_dwork_handler); + rpmd->usb_dr = DR_IDLE; + rpmd->usb_type_polling_cnt = 0; + rpmd->sink_mv_old = -1; + rpmd->sink_ma_old = -1; + + ret = typec_init(rpmd); + if (ret < 0) { + dev_err(rpmd->dev, "%s init typec fail(%d)\n", __func__, ret); + ret = -EPROBE_DEFER; + if (probe_cnt >= PROBE_CNT_MAX) + goto out; + else + goto err_init_typec; + } + + rpmd->pd_nb.notifier_call = pd_tcp_notifier_call; + ret = register_tcp_dev_notifier(rpmd->tcpc, &rpmd->pd_nb, + TCP_NOTIFY_TYPE_ALL); + if (ret < 0) { + dev_err(rpmd->dev, "%s register tcpc notifier fail(%d)\n", + __func__, ret); + ret = -EPROBE_DEFER; + if (probe_cnt >= PROBE_CNT_MAX) + goto out; + else + goto err_reg_tcpc_notifier; + } +#ifdef CONFIG_TCPC_NOTIFIER_LATE_SYNC + tcpc_class_complete_init(); +#endif /* CONFIG_TCPC_NOTIFIER_LATE_SYNC */ +out: + platform_set_drvdata(pdev, rpmd); + +#ifdef CONFIG_USB_POWER_DELIVERY + pd_adapter_create(pdev); +#endif + dev_info(rpmd->dev, "%s %s!!\n", __func__, ret == -EPROBE_DEFER ? + "Over probe cnt max" : "OK"); + return 0; + +err_reg_tcpc_notifier: + typec_unregister_port(rpmd->typec_port); +err_init_typec: +err_get_tcpc_dev: +err_get_iio_chan: +err_init_extcon: + return ret; +} + +static int rt_pd_manager_remove(struct platform_device *pdev) +{ + int ret = 0; + struct rt_pd_manager_data *rpmd = platform_get_drvdata(pdev); + + if (!rpmd) + return -EINVAL; + + ret = unregister_tcp_dev_notifier(rpmd->tcpc, &rpmd->pd_nb, + TCP_NOTIFY_TYPE_ALL); + if (ret < 0) + dev_err(rpmd->dev, "%s unregister tcpc notifier fail(%d)\n", + __func__, ret); + typec_unregister_port(rpmd->typec_port); + + return ret; +} + +static const struct of_device_id rt_pd_manager_of_match[] = { + { .compatible = "richtek,rt-pd-manager" }, + { } +}; +MODULE_DEVICE_TABLE(of, rt_pd_manager_of_match); + +static struct platform_driver rt_pd_manager_driver = { + .driver = { + .name = "rt-pd-manager", + .of_match_table = of_match_ptr(rt_pd_manager_of_match), + }, + .probe = rt_pd_manager_probe, + .remove = rt_pd_manager_remove, +}; +module_platform_driver(rt_pd_manager_driver); + +MODULE_AUTHOR("Jeff Chang"); +MODULE_DESCRIPTION("Richtek pd manager driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(RT_PD_MANAGER_VERSION); + +/* + * Release Note + * 0.0.8 + * (1) Add support for msm-5.4 + * + * 0.0.7 + * (1) Set properties of usb_psy + * + * 0.0.6 + * (1) Register typec_port + * + * 0.0.5 + * (1) Control USB mode in delayed work + * (2) Remove param_lock because pd_tcp_notifier_call() is single-entry + * + * 0.0.4 + * (1) Limit probe count + * + * 0.0.3 + * (1) Add extcon for controlling USB mode + * + * 0.0.2 + * (1) Initialize old_state and new_state + * + * 0.0.1 + * (1) Add all possible notification events + */ diff --git a/drivers/usb/typec/mmi_tcpc/tcpc_aw35616.c b/drivers/usb/typec/mmi_tcpc/tcpc_aw35616.c new file mode 100644 index 000000000000..b5cacf5b4a79 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/tcpc_aw35616.c @@ -0,0 +1,1149 @@ +// SPDX-License-Identifier: GPL-2.0 +/******************************************************************************* + **** Copyright (C), 2022, Shanghai awinic technology Co.,Ltd. + all rights reserved. *********** + ******************************************************************************* + * File Name : tcpc_aw35616.c + * Author : awinic + * Date : 2022-12-16 + * Description : .C file function description + * Version : 1.0 + * Function List : + ******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#include "inc/tcpci_timer.h" +//#include "inc/tcpci_typec.h" +//#include "inc/pd_dbg_info.h" +#include "inc/tcpci.h" +#include "inc/tcpc_aw35616.h" +#include + +#define AW35616_DRIVER_VERSION "V0.3.0" + +#define AW35616_IRQ_WAKE_TIME (1000) /* ms */ + +const unsigned char aw35616_reg_access[AW35616_REG_MAX] = { + [AW35616_REG_DEV_ID] = REG_RD_ACCESS, + [AW35616_REG_CTR] = REG_RD_ACCESS|REG_WR_ACCESS, + [AW35616_REG_INT] = REG_RD_ACCESS, + [AW35616_REG_STATUS] = REG_RD_ACCESS, + [AW35616_REG_STATUS1] = REG_RD_ACCESS, + [AW35616_REG_RSTN] = REG_WR_ACCESS, + [AW35616_REG_USB_VID0] = REG_RD_ACCESS, + [AW35616_REG_USB_VID1] = REG_RD_ACCESS, + [AW35616_REG_CTR2] = REG_RD_ACCESS|REG_WR_ACCESS +}; + +/****************************************************** + * + * aw35616 i2c write/read + * + ******************************************************/ +static int aw35616_i2c_write(struct aw35616_chip *chip, + u8 reg_addr, u8 reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + __pm_stay_awake(chip->for_i2c_wake_lock); + + while (cnt++ < AW35616_I2C_RETRIES) { + ret = i2c_smbus_write_byte_data(chip->client, reg_addr, reg_data); + if (ret < 0) { + pr_err("%s: i2c_write cnt=%d error=%d\n", __func__, + cnt, ret); + } else { + break; + } + usleep_range(1000, 2000); + } + + __pm_relax(chip->for_i2c_wake_lock); + + return ret; +} + +static int aw35616_i2c_read(struct aw35616_chip *chip, + u8 reg_addr, u8 *reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + + __pm_stay_awake(chip->for_i2c_wake_lock); + + while (cnt++ < AW35616_I2C_RETRIES) { + ret = i2c_smbus_read_byte_data(chip->client, reg_addr); + if (ret < 0) { + pr_err("%s: i2c_read cnt=%d error=%d\n", __func__, + cnt, ret); + } else { + *reg_data = ret; + break; + } + usleep_range(1000, 2000); + } + + __pm_relax(chip->for_i2c_wake_lock); + + return ret; +} + +static int aw35616_i2c_read_bits(struct aw35616_chip *chip, + u8 reg_addr, u8 *buf, u8 len) +{ + int ret = 0; + unsigned char *rdbuf = NULL; + unsigned char cnt = 0; + struct i2c_msg msgs[] = { + { + .addr = chip->client->addr, + .flags = 0, + .len = 1, + .buf = ®_addr, + }, + { + .addr = chip->client->addr, + .flags = I2C_M_RD, + .len = len, + }, + }; + + __pm_stay_awake(chip->for_i2c_wake_lock); + + if (chip->client == NULL) { + pr_err("msg %s i2c client is NULL\n", __func__); + return -ENODEV; + } + + rdbuf = kmalloc(len, GFP_KERNEL); + if (rdbuf == NULL) + return -ENOMEM; + + msgs[1].buf = rdbuf; + + while (cnt < AW35616_I2C_RETRIES) { + ret = i2c_transfer(chip->client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + pr_err("msg %s i2c read error: %d\n", __func__, ret); + else + break; + cnt++; + usleep_range(1000, 2000); + } + + if (buf != NULL) + memcpy(buf, rdbuf, len); + + kfree(rdbuf); + + __pm_relax(chip->for_i2c_wake_lock); + + return ret; +} + +static int aw35616_remote_rp_level(struct tcpc_device *tcpc) +{ + struct aw35616_chip *chip = tcpc_get_dev_data(tcpc); + if (chip->tcpc->typec_usb_sink_curr == 1) + return TYPEC_CC_VOLT_SNK_DFT; + else if (chip->tcpc->typec_usb_sink_curr == 2) + return TYPEC_CC_VOLT_SNK_1_5; + else if (chip->tcpc->typec_usb_sink_curr == 3) + return TYPEC_CC_VOLT_SNK_3_0; + else + return TYPEC_CC_VOLT_SNK_DFT; +} + +static int first_check_flag; +static void aw35616_first_check_typec_work(struct work_struct *work) +{ + struct aw35616_chip *chip = container_of(work, + struct aw35616_chip, first_check_typec_work.work); + int i = 0; + u8 reg_data[4]; + + tcpci_lock_typec(chip->tcpc); + first_check_flag = 1; + /* get interrupt */ + aw35616_i2c_read(chip, AW35616_REG_INT, &chip->reg.ints.byte); + AW_LOG("int_sts[0x%02x]\n", chip->reg.ints.byte); + if (chip->reg.ints.intb_flag == NO_INTB) { + aw35616_i2c_read_bits(chip, AW35616_REG_CTR, reg_data, 4); + for (i = 1; i < 5; i++) + AW_LOG("reg:0x%02x=0x%02x\n", i, reg_data[i - 1]); + + tcpci_unlock_typec(chip->tcpc); + return; + } + + if (chip->reg.ints.intb_flag == ATTACHED) { + aw35616_i2c_read_bits(chip, AW35616_REG_STATUS, chip->reg.status.byte, 2); + AW_LOG("plug_st = %d snk_det_rp_dbg = %d\n", + chip->reg.status.plug_st, chip->reg.status.snk_det_rp_dbg); + if (chip->reg.status.plug_ori > 0) + chip->tcpc->typec_polarity = chip->reg.status.plug_ori - 1; + AW_LOG("plug_ori = %d typec_polarity = %d\n",chip->reg.status.plug_ori,chip->tcpc->typec_polarity); + switch (chip->reg.status.plug_st) { + case STANDBY: + AW_LOG("plug status not connected\n"); + break; + case SINK: + chip->tcpc_desc->rp_lvl = TYPEC_CC_RP_1_5; + if (chip->tcpc->typec_attach_new != TYPEC_ATTACHED_SRC) { + chip->tcpc->typec_attach_new = TYPEC_ATTACHED_SRC; + tcpci_source_vbus(chip->tcpc, + TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SOURCE_5V, 1000); + tcpci_notify_typec_state(chip->tcpc); + chip->tcpc->typec_attach_old = TYPEC_ATTACHED_SRC; + } + break; + case SOURCE: + if (chip->tcpc->typec_attach_new != TYPEC_ATTACHED_SNK) { + chip->tcpc->typec_attach_new = TYPEC_ATTACHED_SNK; + AW_LOG("snk_cur_md:%d\n",chip->reg.status.snk_cur_md); + chip->tcpc->typec_usb_sink_curr = chip->reg.status.snk_cur_md; + chip->tcpc->typec_remote_rp_level = aw35616_remote_rp_level(chip->tcpc); + tcpci_notify_typec_state(chip->tcpc); + chip->tcpc->typec_attach_old = TYPEC_ATTACHED_SNK; + } + break; + case AUD_ACC: + if (chip->tcpc->typec_attach_new != TYPEC_ATTACHED_AUDIO) { + chip->tcpc->typec_attach_new = TYPEC_ATTACHED_AUDIO; + tcpci_notify_typec_state(chip->tcpc); + chip->tcpc->typec_attach_old = TYPEC_ATTACHED_AUDIO; + } + break; + case DUG_ACC: + if (chip->reg.status.snk_det_rp_dbg) { + AW_LOG("plug int Rp-Rp\n"); + + AW_LOG("snk_cur_md:%d\n",chip->reg.status.snk_cur_md); + chip->tcpc->typec_usb_sink_curr = chip->reg.status.snk_cur_md; + chip->tcpc->typec_remote_rp_level = aw35616_remote_rp_level(chip->tcpc); + + if (chip->tcpc->typec_attach_new != TYPEC_ATTACHED_SNK) { + chip->tcpc->typec_attach_new = TYPEC_ATTACHED_SNK; + tcpci_notify_typec_state(chip->tcpc); + chip->tcpc->typec_attach_old = TYPEC_ATTACHED_SNK; + } + } else { + AW_LOG("plug int Rd-Rd\n"); + chip->tcpc_desc->rp_lvl = TYPEC_CC_RP_1_5; + if (chip->tcpc->typec_attach_new != TYPEC_ATTACHED_SRC) { + chip->tcpc->typec_attach_new = TYPEC_ATTACHED_SRC; + tcpci_source_vbus(chip->tcpc, + TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SOURCE_5V, 1000); + tcpci_notify_typec_state(chip->tcpc); + chip->tcpc->typec_attach_old = TYPEC_ATTACHED_SRC; + } + } + break; + default: + AW_LOG("plug status unknown = %d\n", chip->reg.status.plug_st); + break; + } + } else if (chip->reg.ints.intb_flag == DETACHED) { + if ((chip->reg.status.plug_st == SINK) || + (chip->reg.status.plug_st == SOURCE) || + (chip->reg.status.plug_st == DUG_ACC) || + (chip->reg.status.plug_st == AUD_ACC)) { + chip->tcpc->typec_attach_new = TYPEC_UNATTACHED; + tcpci_notify_typec_state(chip->tcpc); + if (chip->tcpc->typec_attach_old == TYPEC_ATTACHED_SRC) { + tcpci_source_vbus(chip->tcpc, + TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SOURCE_0V, 0); + } + chip->tcpc->typec_attach_old = TYPEC_UNATTACHED; + } else { + AW_LOG("unknown plug out\n"); + } + } + + tcpci_unlock_typec(chip->tcpc); +} + +static irqreturn_t aw35616_irq_work_handler(int irq, void *data) +{ + struct aw35616_chip *chip = data; + + int i = 0; + u8 reg_data[4]; + u8 reg_data_20; + + if (first_check_flag == 0) + return IRQ_HANDLED; + + tcpci_lock_typec(chip->tcpc); + /* get interrupt */ + aw35616_i2c_read(chip, AW35616_REG_INT, &chip->reg.ints.byte); + AW_LOG("int_sts[0x%02x]\n", chip->reg.ints.byte); + reg_data_20 = 0x0; + if (chip->reg.ints.intb_flag == NO_INTB) { + aw35616_i2c_read_bits(chip, AW35616_REG_CTR, reg_data, 4); + for (i = 1; i < 5; i++) + AW_LOG("reg:0x%02x=0x%02x\n", i, reg_data[i - 1]); + aw35616_i2c_read(chip,AW35616_REG_TYPEC_STATUS,®_data_20); + AW_LOG("reg:0x20=0x%02x\n",reg_data_20); + tcpci_unlock_typec(chip->tcpc); + return IRQ_HANDLED; + } + + if (chip->reg.ints.intb_flag == ATTACHED) { + aw35616_i2c_read_bits(chip, AW35616_REG_STATUS, chip->reg.status.byte, 2); + AW_LOG("plug_st = %d snk_det_rp_dbg = %d\n", + chip->reg.status.plug_st, chip->reg.status.snk_det_rp_dbg); + if(chip->reg.status.plug_ori > 0) + chip->tcpc->typec_polarity = chip->reg.status.plug_ori -1; + AW_LOG("plug_ori = %d, typec_polarity = %d", chip->reg.status.plug_ori, chip->tcpc->typec_polarity); + switch (chip->reg.status.plug_st) { + case STANDBY: + AW_LOG("plug status not connected\n"); + break; + case SINK: + chip->tcpc_desc->rp_lvl = TYPEC_CC_RP_1_5; + if (chip->tcpc->typec_attach_new != TYPEC_ATTACHED_SRC) { + chip->tcpc->typec_attach_new = TYPEC_ATTACHED_SRC; + tcpci_source_vbus(chip->tcpc, + TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SOURCE_5V, 1000); + tcpci_notify_typec_state(chip->tcpc); + chip->tcpc->typec_attach_old = TYPEC_ATTACHED_SRC; + } + break; + case SOURCE: + if (chip->tcpc->typec_attach_new != TYPEC_ATTACHED_SNK) { + chip->tcpc->typec_attach_new = TYPEC_ATTACHED_SNK; + AW_LOG("snk_cur_md:%d\n",chip->reg.status.snk_cur_md); + chip->tcpc->typec_usb_sink_curr = chip->reg.status.snk_cur_md; + chip->tcpc->typec_remote_rp_level = aw35616_remote_rp_level(chip->tcpc); + tcpci_notify_typec_state(chip->tcpc); + chip->tcpc->typec_attach_old = TYPEC_ATTACHED_SNK; + } + break; + case AUD_ACC: + if (chip->tcpc->typec_attach_new != TYPEC_ATTACHED_AUDIO) { + chip->tcpc->typec_attach_new = TYPEC_ATTACHED_AUDIO; + tcpci_notify_typec_state(chip->tcpc); + chip->tcpc->typec_attach_old = TYPEC_ATTACHED_AUDIO; + } + break; + case DUG_ACC: + if (chip->reg.status.snk_det_rp_dbg) { + AW_LOG("plug int Rp-Rp\n"); + + AW_LOG("snk_cur_md:%d\n",chip->reg.status.snk_cur_md); + chip->tcpc->typec_usb_sink_curr = chip->reg.status.snk_cur_md; + chip->tcpc->typec_remote_rp_level = aw35616_remote_rp_level(chip->tcpc); + + if (chip->tcpc->typec_attach_new != TYPEC_ATTACHED_SNK) { + chip->tcpc->typec_attach_new = TYPEC_ATTACHED_SNK; + tcpci_notify_typec_state(chip->tcpc); + chip->tcpc->typec_attach_old = TYPEC_ATTACHED_SNK; + } + } else { + AW_LOG("plug int Rd-Rd\n"); + chip->tcpc_desc->rp_lvl = TYPEC_CC_RP_1_5; + if (chip->tcpc->typec_attach_new != TYPEC_ATTACHED_SRC) { + chip->tcpc->typec_attach_new = TYPEC_ATTACHED_SRC; + tcpci_source_vbus(chip->tcpc, + TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SOURCE_5V, 1000); + tcpci_notify_typec_state(chip->tcpc); + chip->tcpc->typec_attach_old = TYPEC_ATTACHED_SRC; + } + } + break; + default: + AW_LOG("plug status unknown = %d\n", chip->reg.status.plug_st); + break; + } + } else if (chip->reg.ints.intb_flag == DETACHED) { + if ((chip->reg.status.plug_st == SINK) || + (chip->reg.status.plug_st == SOURCE) || + (chip->reg.status.plug_st == DUG_ACC) || + (chip->reg.status.plug_st == AUD_ACC)) { + chip->tcpc->typec_attach_new = TYPEC_UNATTACHED; + tcpci_notify_typec_state(chip->tcpc); + if (chip->tcpc->typec_attach_old == TYPEC_ATTACHED_SRC) { + tcpci_source_vbus(chip->tcpc, + TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SOURCE_0V, 0); + } + chip->tcpc->typec_attach_old = TYPEC_UNATTACHED; + } else { + AW_LOG("unknown plug out\n"); + } + } + + aw35616_i2c_read_bits(chip,AW35616_REG_CTR,reg_data,4); + for (i = 1;i < 5;i++) + AW_LOG("reg:0x%02x=0x%02x\n",i,reg_data[i -1]); + aw35616_i2c_read(chip,AW35616_REG_TYPEC_STATUS,®_data_20); + AW_LOG("reg:0x20=0x%02x\n",reg_data_20); + + tcpci_unlock_typec(chip->tcpc); + + return IRQ_HANDLED; +} + +static int aw35616_init_alert(struct tcpc_device *tcpc) +{ + struct aw35616_chip *chip = tcpc_get_dev_data(tcpc); + int ret; + char *name; + int len; + + len = strlen(chip->tcpc_desc->name); + name = devm_kzalloc(chip->dev, len+5, GFP_KERNEL); + if (!name) + return -ENOMEM; + + snprintf(name, PAGE_SIZE, "%s-IRQ", chip->tcpc_desc->name); + + pr_err("%s name = %s, gpio = %d\n", __func__, + chip->tcpc_desc->name, chip->irq_gpio); + + ret = devm_gpio_request(chip->dev, chip->irq_gpio, name); + if (ret < 0) { + pr_err("Error: failed to request GPIO%d (ret = %d)\n", + chip->irq_gpio, ret); + goto init_alert_err; + } + + ret = gpio_direction_input(chip->irq_gpio); + if (ret < 0) { + pr_err("Error: failed to set GPIO%d as input pin(ret = %d)\n", + chip->irq_gpio, ret); + goto init_alert_err; + } + + chip->irq = gpio_to_irq(chip->irq_gpio); + if (chip->irq <= 0) { + pr_err("%s gpio to irq fail, chip->irq(%d)\n", + __func__, chip->irq); + goto init_alert_err; + } + + pr_err("%s : IRQ number = %d\n", __func__, chip->irq); + + ret = devm_request_threaded_irq(chip->dev, chip->irq, NULL, + aw35616_irq_work_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + name, chip); + + if(ret < 0){ + pr_err("Error: failed to request irq%d(gpio = %d, ret=%d)\n", + chip->irq, chip->irq_gpio, ret); + goto init_alert_err; + } + + + device_init_wakeup(chip->dev, true); + return 0; +init_alert_err: + return -EINVAL; +} + +int aw35616_alert_status_clear(struct tcpc_device *tcpc, uint32_t mask) +{ + AW_LOG("enter\n"); + return 0; +} + +static int aw35616_tcpc_init(struct tcpc_device *tcpc, bool sw_reset) +{ + struct aw35616_chip *chip = tcpc_get_dev_data(tcpc); + + AW_LOG("enter\n"); + + /* set toggle cycle time */ + chip->reg.ctr2.tog_save_md = chip->toggle_cycle; + aw35616_i2c_write(chip, AW35616_REG_CTR2, chip->reg.ctr2.byte); + + /* set power role mode */ + switch (chip->tcpc_desc->role_def) { + case 1: /* set only sink */ + chip->reg.ctr.wkmd = SNK; + chip->reg.ctr.try_md = NO_TRY; + break; + case 2: /* set only source */ + chip->reg.ctr.wkmd = SRC; + chip->reg.ctr.try_md = NO_TRY; + break; + case 3: /* set only drp */ + chip->reg.ctr.wkmd = DRP; + chip->reg.ctr.try_md = NO_TRY; + break; + case 4: /* set try src */ + chip->reg.ctr.wkmd = DRP; + chip->reg.ctr.try_md = TRY_SRC; + break; + case 5: /* set try snk */ + chip->reg.ctr.wkmd = DRP; + chip->reg.ctr.try_md = TRY_SNK; + break; + default: + break; + } + + /* set source current */ + switch (chip->tcpc_desc->rp_lvl) { + case TYPEC_CC_RP_DFT: /* RP Default */ + chip->reg.ctr.src_cur_md = SRC_DEF; + break; + case TYPEC_CC_RP_1_5: /* RP 1.5V */ + chip->reg.ctr.src_cur_md = SRC_1_5A; + break; + case TYPEC_CC_RP_3_0: /* RP 3.0V */ + chip->reg.ctr.src_cur_md = SRC_3_0A; + break; + default: + break; + } + + /* set acc mode */ + chip->reg.ctr.accdis = chip->acc_support; + aw35616_i2c_write(chip, AW35616_REG_CTR, chip->reg.ctr.byte); + aw35616_i2c_write(chip, AW35616_reg_val1, AW35616_reg_val2); + aw35616_i2c_write(chip, AW35616_reg_val3, AW35616_reg_val4); + aw35616_i2c_write(chip, AW35616_reg_val1, AW35616_reg_val5); + + /* The first detection interrupt can be completed. delay 150ms */ + msleep(150); + + schedule_delayed_work(&chip->first_check_typec_work, msecs_to_jiffies(1000)); + + return 0; +} + +int aw35616_fault_status_clear(struct tcpc_device *tcpc, uint8_t status) +{ + AW_LOG("enter\n"); + return 0; +} + +int aw35616_get_alert_mask(struct tcpc_device *tcpc, uint32_t *mask) +{ + AW_LOG("enter\n"); + return 0; +} + +int aw35616_get_alert_status(struct tcpc_device *tcpc, uint32_t *alert) +{ + AW_LOG("enter\n"); + return 0; +} + +static int aw35616_get_power_status( + struct tcpc_device *tcpc, uint16_t *pwr_status) +{ + AW_LOG("enter\n"); + return 0; +} + +int aw35616_get_fault_status(struct tcpc_device *tcpc, uint8_t *status) +{ + + AW_LOG("enter\n"); + return 0; +} + +static int aw35616_get_cc(struct tcpc_device *tcpc, int *cc1, int *cc2) +{ + AW_LOG("enter\n"); + return 0; +} + +int aw35616_typec_cc_orientation(void) +{ + AW_LOG("enter\n"); + return 0; +} + +static int aw35616_set_role(struct tcpc_device *tcpc, int mode); +static int aw35616_set_cc(struct tcpc_device *tcpc, int pull) +{ + int ret; + uint8_t mode = 0; + + AW_LOG("enter\n"); + + if (pull == TYPEC_CC_RP) + mode = REVERSE_CHG_SOURCE; + else if (pull == TYPEC_CC_RD) + mode = REVERSE_CHG_SINK; + else if (pull == TYPEC_CC_DRP) + mode = REVERSE_CHG_DRP; + + ret = aw35616_set_role(tcpc, mode); + if (ret < 0) + pr_err("%s: set mode fail!\n", __func__); + + return 0; +} + +static int aw35616_set_polarity(struct tcpc_device *tcpc, int polarity) +{ + AW_LOG("enter\n"); + return 0; +} + +static int aw35616_set_low_rp_duty(struct tcpc_device *tcpc, bool low_rp) +{ + AW_LOG("enter\n"); + return 0; +} + +static int aw35616_set_vconn(struct tcpc_device *tcpc, int enable) +{ + AW_LOG("enter\n"); + return 0; +} + +static int aw35616_tcpc_deinit(struct tcpc_device *tcpc_dev) +{ + AW_LOG("enter\n"); + +#ifdef CONFIG_TCPC_SHUTDOWN_CC_DETACH + aw35616_set_cc(tcpc_dev, TYPEC_CC_RD); +#endif + + return 0; +} + +static int aw35616_set_role(struct tcpc_device *tcpc, int mode) +{ + AW_LOG("dhx--set role end %d\n", mode); + return 0; +} + +#ifdef CONFIG_USB_POWER_DELIVERY +static int aw35616_set_msg_header(struct tcpc_device *tcpc, + uint8_t power_role, uint8_t data_role) +{ + AW_LOG("enter\n"); + return 0; +} + +static int aw35616_set_rx_enable(struct tcpc_device *tcpc, uint8_t enable) +{ + AW_LOG("enter\n"); + return 0; +} + +static int aw35616_protocol_reset(struct tcpc_device *tcpc_dev) +{ + AW_LOG("enter\n"); + return 0; +} + +static int aw35616_get_message(struct tcpc_device *tcpc, uint32_t *payload, + uint16_t *msg_head, enum tcpm_transmit_type *frame_type) +{ + AW_LOG("enter\n"); + return 0; +} + +static int aw35616_transmit(struct tcpc_device *tcpc, + enum tcpm_transmit_type type, uint16_t header, const uint32_t *data) +{ + AW_LOG("enter\n"); + return 0; +} + +static int aw35616_set_bist_test_mode(struct tcpc_device *tcpc, bool en) +{ + AW_LOG("enter\n"); + return 0; +} + +static int aw35616_set_bist_carrier_mode( + struct tcpc_device *tcpc, uint8_t pattern) +{ + AW_LOG("enter\n"); + return 0; +} +#endif /* CONFIG_USB_POWER_DELIVERY */ + +static struct tcpc_ops aw35616_tcpc_ops = { + .init = aw35616_tcpc_init, + .alert_status_clear = aw35616_alert_status_clear, + .fault_status_clear = aw35616_fault_status_clear, + .get_alert_mask = aw35616_get_alert_mask, + .get_alert_status = aw35616_get_alert_status, + .get_power_status = aw35616_get_power_status, + .get_fault_status = aw35616_get_fault_status, + .get_cc = aw35616_get_cc, + .set_cc = aw35616_set_cc, + //.set_role = aw35616_set_role, + //.get_mode = aw35616_tcpc_get_mode, + .set_polarity = aw35616_set_polarity, + .set_low_rp_duty = aw35616_set_low_rp_duty, + .set_vconn = aw35616_set_vconn, + .deinit = aw35616_tcpc_deinit, + +#ifdef CONFIG_USB_POWER_DELIVERY + .set_msg_header = aw35616_set_msg_header, + .set_rx_enable = aw35616_set_rx_enable, + .protocol_reset = aw35616_protocol_reset, + .get_message = aw35616_get_message, + .transmit = aw35616_transmit, + .set_bist_test_mode = aw35616_set_bist_test_mode, + .set_bist_carrier_mode = aw35616_set_bist_carrier_mode, +#endif /* CONFIG_USB_POWER_DELIVERY */ +}; + + +static int aw35616_parse_dt(struct aw35616_chip *chip, struct device *dev) +{ + struct device_node *np = dev->of_node; + int ret; + + if (!np) + return -EINVAL; + + AW_LOG("enter\n"); + + np = of_find_node_by_name(NULL, "usb_type_c_aw35616"); + if (!np) { + pr_err("%s find node type_c_port0 fail\n", __func__); + return -ENODEV; + } + + ret = of_get_named_gpio(np, "aw35616,irq-gpio", 0); + if (ret < 0) { + pr_err("%s no intr_gpio info\n", __func__); + return ret; + } + chip->irq_gpio = ret; + + return ret; +} + +static int aw35616_tcpcdev_init(struct aw35616_chip *chip, struct device *dev) +{ + struct tcpc_desc *desc; + struct device_node *np; + u32 val, len; + + const char *name = "default"; + + np = of_find_node_by_name(NULL, "usb_type_c_aw35616"); + if (!np) { + pr_err("%s find node aw35616 fail\n", __func__); + return -ENODEV; + } + + desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + if (of_property_read_u32(np, "aw35616_acc_support", &val) >= 0) { + chip->acc_support = (bool)val; + AW_LOG("aw35616_acc_support = %d\n", chip->acc_support); + } else { + dev_info(dev, "use default val\n"); + chip->acc_support = false; + } + if (of_property_read_u32(np, "aw35616-tcpc,role_def", &val) >= 0) { + if (val >= TYPEC_ROLE_NR) + desc->role_def = TYPEC_ROLE_DRP; + else + desc->role_def = val; + AW_LOG("aw35616_role_def = %d\n", desc->role_def); + } else { + dev_info(dev, "use default Role DRP\n"); + desc->role_def = TYPEC_ROLE_DRP; + } + + if (of_property_read_u32( + np, "aw35616-tcpc,notifier_supply_num", &val) >= 0) { + if (val < 0) + desc->notifier_supply_num = 0; + else + desc->notifier_supply_num = val; + } else + desc->notifier_supply_num = 0; + + if (of_property_read_u32(np, "aw35616,rp_level", &val) >= 0) { + switch (val) { + case 0: /* RP Default */ + desc->rp_lvl = TYPEC_CC_RP_DFT; + break; + case 1: /* RP 1.5V */ + desc->rp_lvl = TYPEC_CC_RP_1_5; + break; + case 2: /* RP 3.0V */ + desc->rp_lvl = TYPEC_CC_RP_3_0; + break; + default: + break; + } + AW_LOG("aw35616_rp_level = %d\n", desc->rp_lvl); + } + + if (of_property_read_u32(np, "aw35616_toggle_cycle", &val) >= 0) { + chip->toggle_cycle = (uint8_t)val; + AW_LOG("aw35616_toggle_cycle = %d\n", chip->toggle_cycle); + } else { + dev_info(dev, "use default val\n"); + chip->toggle_cycle = 0; + } + of_property_read_string(np, "aw35616-tcpc,name", (char const **)&name); + + len = strlen(name); + desc->name = kzalloc(len+1, GFP_KERNEL); + if (!desc->name) + return -ENOMEM; + + strscpy((char *)desc->name, name, len+1); + + chip->tcpc_desc = desc; + + chip->tcpc = tcpc_device_register(dev, + desc, &aw35616_tcpc_ops, chip); + if (IS_ERR(chip->tcpc)) + return -EINVAL; + + chip->tcpc->typec_attach_old = TYPEC_UNATTACHED; + chip->tcpc->typec_attach_new = TYPEC_UNATTACHED; + + return 0; +} + +static inline bool aw35616_check_revision(struct i2c_client *client) +{ + int rc = 0; + int i = 0; + + for (i = 0; i <= AW35616_CHECK_RETEY; i++) { + /* Read chip id */ + rc = i2c_smbus_read_byte_data(client, AW35616_REG_DEV_ID); + if (rc >= 0) { + if ((rc & 0x7) == AW35616_VENDOR_ID) + AW_LOG("Device check passed chip id = 0x%x\n", rc); + return true; + } + + AW_LOG("ERROR: Could not communicate with device over i2c!\n"); + usleep_range(1000, 2000); + } + + return false; +} + +static void aw35616_init_reg(struct aw35616_chip *chip) +{ + chip->reg.rstn.sft_rstn = 1; + aw35616_i2c_write(chip, AW35616_REG_RSTN, chip->reg.rstn.byte); + chip->reg.rstn.sft_rstn = 0; + /* Detect between gpio and i2c modes */ + msleep(30); + aw35616_i2c_read(chip, AW35616_REG_DEV_ID, &chip->reg.dev_id.byte); + aw35616_i2c_read(chip, AW35616_REG_CTR, &chip->reg.ctr.byte); + aw35616_i2c_read_bits(chip, AW35616_REG_STATUS, chip->reg.status.byte, 2); + aw35616_i2c_read_bits(chip, AW35616_REG_USB_VID0, chip->reg.vid.byte, 2); + aw35616_i2c_read(chip, AW35616_REG_CTR2, &chip->reg.ctr2.byte); +} + +/****************************************************** + * + * sys group attribute: reg + * + ******************************************************/ +static ssize_t regdump_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct aw35616_chip *chip = i2c_get_clientdata(client); + ssize_t len = 0; + unsigned char i = 0; + unsigned char reg_val = 0; + + for (i = 0; i < AW35616_REG_MAX; i++) { + if (!(aw35616_reg_access[i] & REG_RD_ACCESS)) + continue; + aw35616_i2c_read(chip, i, ®_val); + AW_LOG("i = %d reg_val = %x\n", i, reg_val); + len += snprintf(buf + len, PAGE_SIZE - len, "reg:0x%02x=0x%02x\n", + i, reg_val); + } + + return len; +} + +static ssize_t regdump_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct aw35616_chip *chip = i2c_get_clientdata(client); + u32 databuf[2] = {0, 0}; + + if (sscanf(buf, "%x %x", &databuf[0], &databuf[1]) == 2) + aw35616_i2c_write(chip, (u8)databuf[0], (u8)databuf[1]); + + return count; +} +DEVICE_ATTR_RW(regdump); + +static ssize_t typec_polarity_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct aw35616_chip *chip = i2c_get_clientdata(client); + + aw35616_i2c_read(chip, AW35616_REG_STATUS, &chip->reg.status.byte[0]); + AW_LOG("reg_val[0x04] = %x\n", chip->reg.status.byte[0]); + + return sprintf(buf, "%s\n", + (chip->reg.status.plug_ori == CC1) ? "CC1" : + (chip->reg.status.plug_ori == CC2) ? "CC2" : + (chip->reg.status.plug_ori == CC1_CC2) ? "CC1&&CC2" : "None"); +} +DEVICE_ATTR_RO(typec_polarity); + +static int aw35616_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct aw35616_chip *chip; + int ret; + bool chip_ret; + + AW_LOG("enter\n"); + if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_BYTE_DATA)) + AW_LOG("I2C functionality : OK...\n"); + else + pr_err("I2C functionality check : failuare...\n"); + + chip_ret = aw35616_check_revision(client); + if (chip_ret == false) { + pr_err("aw35616 init fail\n"); + return -ENODEV; + } + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + aw35616_parse_dt(chip, &client->dev); + + chip->dev = &client->dev; + chip->client = client; + + sema_init(&chip->suspend_lock, 1); + i2c_set_clientdata(client, chip); + + INIT_DELAYED_WORK(&chip->first_check_typec_work, + aw35616_first_check_typec_work); + + schedule_delayed_work(&chip->first_check_typec_work, msecs_to_jiffies(3000)); + + chip->for_irq_wake_lock = + wakeup_source_register(chip->dev, "aw35616_irq_wakelock"); + chip->for_i2c_wake_lock = + wakeup_source_register(chip->dev, "aw35616_i2c_wakelock"); + + aw35616_init_reg(chip); + + ret = aw35616_tcpcdev_init(chip, &client->dev); + if (ret < 0) { + dev_err(&client->dev, "aw35616 tcpc dev init fail\n"); + goto err_tcpc_reg; + } + + ret = aw35616_init_alert(chip->tcpc); + if (ret < 0) { + pr_err("aw35616 init alert fail\n"); + goto err_irq_init; + } + + ret = device_create_file(&client->dev, &dev_attr_regdump); + if (ret < 0) { + pr_err("failed to create regdump\n"); + ret = -ENODEV; + goto err_irq_init; + } + + + ret = device_create_file(&client->dev, &dev_attr_typec_polarity); + if (ret < 0) { + pr_err("failed to create typec_polarity\n"); + ret = -ENODEV; + goto err_create_fregdump_file; + } + + AW_LOG("probe OK!\n"); + return 0; + +err_create_fregdump_file: + device_remove_file(&client->dev, &dev_attr_regdump); +err_irq_init: + tcpc_device_unregister(chip->dev, chip->tcpc); +err_tcpc_reg: + wakeup_source_unregister(chip->for_i2c_wake_lock); + wakeup_source_unregister(chip->for_irq_wake_lock); + return ret; +} + +static int aw35616_i2c_remove(struct i2c_client *client) +{ + struct aw35616_chip *chip = i2c_get_clientdata(client); + + if (chip) { + cancel_delayed_work_sync(&chip->first_check_typec_work); + tcpc_device_unregister(chip->dev, chip->tcpc); + device_remove_file(&client->dev, &dev_attr_regdump); + device_remove_file(&client->dev, &dev_attr_typec_polarity); + wakeup_source_unregister(chip->for_i2c_wake_lock); + wakeup_source_unregister(chip->for_irq_wake_lock); + } + + return 0; +} + +#ifdef CONFIG_PM +static int aw35616_i2c_suspend(struct device *dev) +{ + struct aw35616_chip *chip; + struct i2c_client *client = to_i2c_client(dev); + + if (client) { + chip = i2c_get_clientdata(client); + if (device_may_wakeup(chip->dev)) + enable_irq_wake(chip->irq); + disable_irq(chip->irq); + } + + return 0; +} + +static int aw35616_i2c_resume(struct device *dev) +{ + struct aw35616_chip *chip; + struct i2c_client *client = to_i2c_client(dev); + + if (client) { + chip = i2c_get_clientdata(client); + enable_irq(chip->irq); + if (device_may_wakeup(chip->dev)) + disable_irq_wake(chip->irq); + } + + return 0; +} + +static void aw35616_shutdown(struct i2c_client *client) +{ + struct aw35616_chip *chip = i2c_get_clientdata(client); + + /* Please reset IC here */ + chip->reg.rstn.sft_rstn = 0x1; + aw35616_i2c_write(chip, AW35616_REG_RSTN, chip->reg.rstn.byte); + if (chip != NULL) { + if (chip->irq) + disable_irq(chip->irq); + } +} + +#ifdef CONFIG_PM_RUNTIME +static int aw35616_pm_suspend_runtime(struct device *device) +{ + AW_LOG("pm_runtime: suspending...\n"); + return 0; +} + +static int aw35616_pm_resume_runtime(struct device *device) +{ + AW_LOG("pm_runtime: resuming...\n"); + return 0; +} +#endif /* CONFIG_PM_RUNTIME */ + + +static const struct dev_pm_ops aw35616_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS( + aw35616_i2c_suspend, + aw35616_i2c_resume) +#ifdef CONFIG_PM_RUNTIME + SET_RUNTIME_PM_OPS( + aw35616_pm_suspend_runtime, + aw35616_pm_resume_runtime, + NULL + ) +#endif /* CONFIG_PM_RUNTIME */ +}; + +#define aw35616_PM_OPS (&aw35616_pm_ops) +#else +#define aw35616_PM_OPS (NULL) +#endif /* CONFIG_PM */ + +static const struct i2c_device_id aw35616_id_table[] = { + {"aw35616", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, aw35616_id_table); + +static const struct of_device_id aw35616_match_table[] = { + {.compatible = "awinic,usb_type_c_aw35616",}, + {}, +}; + +static struct i2c_driver aw35616_driver = { + .driver = { + .name = "usb_type_c_aw35616", + .owner = THIS_MODULE, + .of_match_table = aw35616_match_table, + .pm = aw35616_PM_OPS, + }, + .probe = aw35616_i2c_probe, + .remove = aw35616_i2c_remove, + .shutdown = aw35616_shutdown, + .id_table = aw35616_id_table, +}; + +static int __init aw35616_init(void) +{ + AW_LOG("start driver init\n"); + AW_LOG("aw35616 driver version %s\n", AW35616_DRIVER_VERSION); + + return i2c_add_driver(&aw35616_driver); +} +subsys_initcall(aw35616_init); + +static void __exit aw35616_exit(void) +{ + i2c_del_driver(&aw35616_driver); +} +module_exit(aw35616_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("AWINIC"); +MODULE_DESCRIPTION("aw35616 TCPC Driver"); +MODULE_VERSION(AW35616_DRIVER_VERSION); + + diff --git a/drivers/usb/typec/mmi_tcpc/tcpc_rt1711h.c b/drivers/usb/typec/mmi_tcpc/tcpc_rt1711h.c new file mode 100644 index 000000000000..da3997ca67db --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/tcpc_rt1711h.c @@ -0,0 +1,1781 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Richtek RT1711H Type-C Port Control Driver + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inc/pd_dbg_info.h" +#include "inc/tcpci.h" +#include "inc/rt1711h.h" + +#ifdef CONFIG_RT_REGMAP +#include "inc/rt-regmap.h" +#endif /* CONFIG_RT_REGMAP */ + +#if 1 /* #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0))*/ +#include +#endif /* #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)) */ + +#define RT1711H_DRV_VERSION "2.0.6_G" + +#define RT1711H_IRQ_WAKE_TIME (500) /* ms */ + +struct rt1711_chip { + struct i2c_client *client; + struct device *dev; +#ifdef CONFIG_RT_REGMAP + struct rt_regmap_device *m_dev; +#endif /* CONFIG_RT_REGMAP */ + struct tcpc_desc *tcpc_desc; + struct tcpc_device *tcpc; +#ifdef CONFIG_TCPC_RT1711H + atomic_t poll_count; + struct delayed_work poll_work; +#endif /* CONFIG_TCPC_RT1711H */ + int irq_gpio; + int irq; + int chip_id; +}; + +#ifdef CONFIG_RT_REGMAP +RT_REG_DECL(TCPC_V10_REG_VID, 2, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(TCPC_V10_REG_PID, 2, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(TCPC_V10_REG_DID, 2, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(TCPC_V10_REG_TYPEC_REV, 2, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(TCPC_V10_REG_PD_REV, 2, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(TCPC_V10_REG_PDIF_REV, 2, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(TCPC_V10_REG_ALERT, 2, RT_VOLATILE, {}); +RT_REG_DECL(TCPC_V10_REG_ALERT_MASK, 2, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(TCPC_V10_REG_POWER_STATUS_MASK, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(TCPC_V10_REG_FAULT_STATUS_MASK, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(TCPC_V10_REG_TCPC_CTRL, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(TCPC_V10_REG_ROLE_CTRL, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(TCPC_V10_REG_FAULT_CTRL, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(TCPC_V10_REG_POWER_CTRL, 1, RT_VOLATILE, {}); +RT_REG_DECL(TCPC_V10_REG_CC_STATUS, 1, RT_VOLATILE, {}); +RT_REG_DECL(TCPC_V10_REG_POWER_STATUS, 1, RT_VOLATILE, {}); +RT_REG_DECL(TCPC_V10_REG_FAULT_STATUS, 1, RT_VOLATILE, {}); +RT_REG_DECL(TCPC_V10_REG_COMMAND, 1, RT_VOLATILE, {}); +RT_REG_DECL(TCPC_V10_REG_MSG_HDR_INFO, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(TCPC_V10_REG_RX_DETECT, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(TCPC_V10_REG_RX_BYTE_CNT, 1, RT_VOLATILE, {}); +RT_REG_DECL(TCPC_V10_REG_RX_BUF_FRAME_TYPE, 1, RT_VOLATILE, {}); +RT_REG_DECL(TCPC_V10_REG_RX_HDR, 2, RT_VOLATILE, {}); +RT_REG_DECL(TCPC_V10_REG_RX_DATA, 28, RT_VOLATILE, {}); +RT_REG_DECL(TCPC_V10_REG_TRANSMIT, 1, RT_VOLATILE, {}); +RT_REG_DECL(TCPC_V10_REG_TX_BYTE_CNT, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(TCPC_V10_REG_TX_HDR, 2, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(TCPC_V10_REG_TX_DATA, 28, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(RT1711H_REG_CONFIG_GPIO0, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(RT1711H_REG_PHY_CTRL1, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(RT1711H_REG_CLK_CTRL2, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(RT1711H_REG_CLK_CTRL3, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(RT1711H_REG_PRL_FSM_RESET, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT1711H_REG_BMC_CTRL, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT1711H_REG_BMCIO_RXDZSEL, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(RT1711H_REG_VCONN_CLIMITEN, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(RT1711H_REG_RT_STATUS, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT1711H_REG_RT_INT, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT1711H_REG_RT_MASK, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(RT1711H_REG_IDLE_CTRL, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(RT1711H_REG_INTRST_CTRL, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(RT1711H_REG_WATCHDOG_CTRL, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(RT1711H_REG_I2CRST_CTRL, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(RT1711H_REG_SWRESET, 1, RT_VOLATILE, {}); +RT_REG_DECL(RT1711H_REG_TTCPC_FILTER, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(RT1711H_REG_DRP_TOGGLE_CYCLE, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(RT1711H_REG_DRP_DUTY_CTRL, 2, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(RT1711H_REG_BMCIO_RXDZEN, 1, RT_NORMAL_WR_ONCE, {}); +RT_REG_DECL(RT1711H_REG_UNLOCK_PW_2, 2, RT_VOLATILE, {}); +RT_REG_DECL(RT1711H_REG_EFUSE5, 1, RT_VOLATILE, {}); + +static const rt_register_map_t rt1711_chip_regmap[] = { + RT_REG(TCPC_V10_REG_VID), + RT_REG(TCPC_V10_REG_PID), + RT_REG(TCPC_V10_REG_DID), + RT_REG(TCPC_V10_REG_TYPEC_REV), + RT_REG(TCPC_V10_REG_PD_REV), + RT_REG(TCPC_V10_REG_PDIF_REV), + RT_REG(TCPC_V10_REG_ALERT), + RT_REG(TCPC_V10_REG_ALERT_MASK), + RT_REG(TCPC_V10_REG_POWER_STATUS_MASK), + RT_REG(TCPC_V10_REG_FAULT_STATUS_MASK), + RT_REG(TCPC_V10_REG_TCPC_CTRL), + RT_REG(TCPC_V10_REG_ROLE_CTRL), + RT_REG(TCPC_V10_REG_FAULT_CTRL), + RT_REG(TCPC_V10_REG_POWER_CTRL), + RT_REG(TCPC_V10_REG_CC_STATUS), + RT_REG(TCPC_V10_REG_POWER_STATUS), + RT_REG(TCPC_V10_REG_FAULT_STATUS), + RT_REG(TCPC_V10_REG_COMMAND), + RT_REG(TCPC_V10_REG_MSG_HDR_INFO), + RT_REG(TCPC_V10_REG_RX_DETECT), + RT_REG(TCPC_V10_REG_RX_BYTE_CNT), + RT_REG(TCPC_V10_REG_RX_BUF_FRAME_TYPE), + RT_REG(TCPC_V10_REG_RX_HDR), + RT_REG(TCPC_V10_REG_RX_DATA), + RT_REG(TCPC_V10_REG_TRANSMIT), + RT_REG(TCPC_V10_REG_TX_BYTE_CNT), + RT_REG(TCPC_V10_REG_TX_HDR), + RT_REG(TCPC_V10_REG_TX_DATA), + RT_REG(RT1711H_REG_CONFIG_GPIO0), + RT_REG(RT1711H_REG_PHY_CTRL1), + RT_REG(RT1711H_REG_CLK_CTRL2), + RT_REG(RT1711H_REG_CLK_CTRL3), + RT_REG(RT1711H_REG_PRL_FSM_RESET), + RT_REG(RT1711H_REG_BMC_CTRL), + RT_REG(RT1711H_REG_BMCIO_RXDZSEL), + RT_REG(RT1711H_REG_VCONN_CLIMITEN), + RT_REG(RT1711H_REG_RT_STATUS), + RT_REG(RT1711H_REG_RT_INT), + RT_REG(RT1711H_REG_RT_MASK), + RT_REG(RT1711H_REG_IDLE_CTRL), + RT_REG(RT1711H_REG_INTRST_CTRL), + RT_REG(RT1711H_REG_WATCHDOG_CTRL), + RT_REG(RT1711H_REG_I2CRST_CTRL), + RT_REG(RT1711H_REG_SWRESET), + RT_REG(RT1711H_REG_TTCPC_FILTER), + RT_REG(RT1711H_REG_DRP_TOGGLE_CYCLE), + RT_REG(RT1711H_REG_DRP_DUTY_CTRL), + RT_REG(RT1711H_REG_BMCIO_RXDZEN), + RT_REG(RT1711H_REG_UNLOCK_PW_2), + RT_REG(RT1711H_REG_EFUSE5), +}; +#define RT1711_CHIP_REGMAP_SIZE ARRAY_SIZE(rt1711_chip_regmap) + +#endif /* CONFIG_RT_REGMAP */ + +static int rt1711_read_device(void *client, u32 reg, int len, void *dst) +{ + struct i2c_client *i2c = client; + int ret = 0, count = 5; + u64 t1 = 0, t2 = 0; + + while (1) { + t1 = local_clock(); + ret = i2c_smbus_read_i2c_block_data(i2c, reg, len, dst); + t2 = local_clock(); + RT1711_INFO("%s del = %lluus, reg = %02X, len = %d\n", + __func__, (t2 - t1) / NSEC_PER_USEC, reg, len); + if (ret < 0 && count > 1) + count--; + else + break; + udelay(100); + } + return ret; +} + +static int rt1711_write_device(void *client, u32 reg, int len, const void *src) +{ + struct i2c_client *i2c = client; + int ret = 0, count = 5; + u64 t1 = 0, t2 = 0; + + while (1) { + t1 = local_clock(); + ret = i2c_smbus_write_i2c_block_data(i2c, reg, len, src); + t2 = local_clock(); + RT1711_INFO("%s del = %lluus, reg = %02X, len = %d\n", + __func__, (t2 - t1) / NSEC_PER_USEC, reg, len); + if (ret < 0 && count > 1) + count--; + else + break; + udelay(100); + } + return ret; +} + +static int rt1711_reg_read(struct i2c_client *i2c, u8 reg) +{ + struct rt1711_chip *chip = i2c_get_clientdata(i2c); + u8 val = 0; + int ret = 0; + +#ifdef CONFIG_RT_REGMAP + ret = rt_regmap_block_read(chip->m_dev, reg, 1, &val); +#else + ret = rt1711_read_device(chip->client, reg, 1, &val); +#endif /* CONFIG_RT_REGMAP */ + if (ret < 0) { + dev_err(chip->dev, "rt1711 reg read fail\n"); + return ret; + } + return val; +} + +static int rt1711_reg_write(struct i2c_client *i2c, u8 reg, const u8 data) +{ + struct rt1711_chip *chip = i2c_get_clientdata(i2c); + int ret = 0; + +#ifdef CONFIG_RT_REGMAP + ret = rt_regmap_block_write(chip->m_dev, reg, 1, &data); +#else + ret = rt1711_write_device(chip->client, reg, 1, &data); +#endif /* CONFIG_RT_REGMAP */ + if (ret < 0) + dev_err(chip->dev, "rt1711 reg write fail\n"); + return ret; +} + +static int rt1711_block_read(struct i2c_client *i2c, + u8 reg, int len, void *dst) +{ + struct rt1711_chip *chip = i2c_get_clientdata(i2c); + int ret = 0; +#ifdef CONFIG_RT_REGMAP + ret = rt_regmap_block_read(chip->m_dev, reg, len, dst); +#else + ret = rt1711_read_device(chip->client, reg, len, dst); +#endif /* #ifdef CONFIG_RT_REGMAP */ + if (ret < 0) + dev_err(chip->dev, "rt1711 block read fail\n"); + return ret; +} + +static int rt1711_block_write(struct i2c_client *i2c, + u8 reg, int len, const void *src) +{ + struct rt1711_chip *chip = i2c_get_clientdata(i2c); + int ret = 0; +#ifdef CONFIG_RT_REGMAP + ret = rt_regmap_block_write(chip->m_dev, reg, len, src); +#else + ret = rt1711_write_device(chip->client, reg, len, src); +#endif /* #ifdef CONFIG_RT_REGMAP */ + if (ret < 0) + dev_err(chip->dev, "rt1711 block write fail\n"); + return ret; +} + +static int32_t rt1711_write_word(struct i2c_client *client, + uint8_t reg_addr, uint16_t data) +{ + int ret; + + /* don't need swap */ + ret = rt1711_block_write(client, reg_addr, 2, (uint8_t *)&data); + return ret; +} + +static int32_t rt1711_read_word(struct i2c_client *client, + uint8_t reg_addr, uint16_t *data) +{ + int ret; + + /* don't need swap */ + ret = rt1711_block_read(client, reg_addr, 2, (uint8_t *)data); + return ret; +} + +static inline int rt1711_i2c_write8( + struct tcpc_device *tcpc, u8 reg, const u8 data) +{ + struct rt1711_chip *chip = tcpc_get_dev_data(tcpc); + + return rt1711_reg_write(chip->client, reg, data); +} + +static inline int rt1711_i2c_write16( + struct tcpc_device *tcpc, u8 reg, const u16 data) +{ + struct rt1711_chip *chip = tcpc_get_dev_data(tcpc); + + return rt1711_write_word(chip->client, reg, data); +} + +static inline int rt1711_i2c_read8(struct tcpc_device *tcpc, u8 reg) +{ + struct rt1711_chip *chip = tcpc_get_dev_data(tcpc); + + return rt1711_reg_read(chip->client, reg); +} + +static inline int rt1711_i2c_read16( + struct tcpc_device *tcpc, u8 reg) +{ + struct rt1711_chip *chip = tcpc_get_dev_data(tcpc); + u16 data; + int ret; + + ret = rt1711_read_word(chip->client, reg, &data); + if (ret < 0) + return ret; + return data; +} + +#ifdef CONFIG_RT_REGMAP +static struct rt_regmap_fops rt1711_regmap_fops = { + .read_device = rt1711_read_device, + .write_device = rt1711_write_device, +}; +#endif /* CONFIG_RT_REGMAP */ + +static int rt1711_regmap_init(struct rt1711_chip *chip) +{ +#ifdef CONFIG_RT_REGMAP + struct rt_regmap_properties *props; + char name[32]; + int len; + + props = devm_kzalloc(chip->dev, sizeof(*props), GFP_KERNEL); + if (!props) + return -ENOMEM; + + props->register_num = RT1711_CHIP_REGMAP_SIZE; + props->rm = rt1711_chip_regmap; + + props->rt_regmap_mode = RT_MULTI_BYTE | + RT_IO_PASS_THROUGH | RT_DBG_SPECIAL; + snprintf(name, sizeof(name), "rt1711-%02x", chip->client->addr); + + len = strlen(name); + props->name = kzalloc(len+1, GFP_KERNEL); + props->aliases = kzalloc(len+1, GFP_KERNEL); + + if ((!props->name) || (!props->aliases)) + return -ENOMEM; + + strlcpy((char *)props->name, name, len+1); + strlcpy((char *)props->aliases, name, len+1); + props->io_log_en = 0; + + chip->m_dev = rt_regmap_device_register(props, + &rt1711_regmap_fops, chip->dev, chip->client, chip); + if (!chip->m_dev) { + dev_err(chip->dev, "rt1711 chip rt_regmap register fail\n"); + return -EINVAL; + } +#endif + return 0; +} + +static int rt1711_regmap_deinit(struct rt1711_chip *chip) +{ +#ifdef CONFIG_RT_REGMAP + rt_regmap_device_unregister(chip->m_dev); +#endif + return 0; +} + +static inline int rt1711_software_reset(struct tcpc_device *tcpc) +{ + int ret = rt1711_i2c_write8(tcpc, RT1711H_REG_SWRESET, 1); +#ifdef CONFIG_RT_REGMAP + struct rt1711_chip *chip = tcpc_get_dev_data(tcpc); +#endif /* CONFIG_RT_REGMAP */ + + if (ret < 0) + return ret; +#ifdef CONFIG_RT_REGMAP + rt_regmap_cache_reload(chip->m_dev); +#endif /* CONFIG_RT_REGMAP */ + usleep_range(1000, 2000); + return 0; +} + +static inline int rt1711_command(struct tcpc_device *tcpc, uint8_t cmd) +{ + return rt1711_i2c_write8(tcpc, TCPC_V10_REG_COMMAND, cmd); +} + +static int rt1711_init_vbus_cal(struct tcpc_device *tcpc) +{ + struct rt1711_chip *chip = tcpc_get_dev_data(tcpc); + const u8 val_en_test_mode[] = {0x86, 0x62}; + const u8 val_dis_test_mode[] = {0x00, 0x00}; + int ret = 0; + u8 data = 0; + s8 cal = 0; + + ret = rt1711_block_write(chip->client, RT1711H_REG_UNLOCK_PW_2, + ARRAY_SIZE(val_en_test_mode), val_en_test_mode); + if (ret < 0) + dev_notice(chip->dev, "%s en test mode fail(%d)\n", + __func__, ret); + + ret = rt1711_reg_read(chip->client, RT1711H_REG_EFUSE5); + if (ret < 0) + goto out; + + data = ret; + data = (data & RT1711H_REG_M_VBUS_CAL) >> RT1711H_REG_S_VBUS_CAL; + cal = (data & BIT(2)) ? (data | GENMASK(7, 3)) : data; + cal -= 2; + if (cal < RT1711H_REG_MIN_VBUS_CAL) + cal = RT1711H_REG_MIN_VBUS_CAL; + data = (cal << RT1711H_REG_S_VBUS_CAL) | (ret & GENMASK(4, 0)); + + ret = rt1711_reg_write(chip->client, RT1711H_REG_EFUSE5, data); +out: + ret = rt1711_block_write(chip->client, RT1711H_REG_UNLOCK_PW_2, + ARRAY_SIZE(val_dis_test_mode), val_dis_test_mode); + if (ret < 0) + dev_notice(chip->dev, "%s dis test mode fail(%d)\n", + __func__, ret); + + return ret; +} + +static int rt1711_init_alert_mask(struct tcpc_device *tcpc) +{ + uint16_t mask; + struct rt1711_chip *chip = tcpc_get_dev_data(tcpc); + + mask = TCPC_V10_REG_ALERT_CC_STATUS | TCPC_V10_REG_ALERT_POWER_STATUS; + +#ifdef CONFIG_USB_POWER_DELIVERY + /* Need to handle RX overflow */ + mask |= TCPC_V10_REG_ALERT_TX_SUCCESS | TCPC_V10_REG_ALERT_TX_DISCARDED + | TCPC_V10_REG_ALERT_TX_FAILED + | TCPC_V10_REG_ALERT_RX_HARD_RST + | TCPC_V10_REG_ALERT_RX_STATUS + | TCPC_V10_REG_RX_OVERFLOW; +#endif + + mask |= TCPC_REG_ALERT_FAULT; + + return rt1711_write_word(chip->client, TCPC_V10_REG_ALERT_MASK, mask); +} + +static int rt1711_init_power_status_mask(struct tcpc_device *tcpc) +{ + const uint8_t mask = TCPC_V10_REG_POWER_STATUS_VBUS_PRES; + + return rt1711_i2c_write8(tcpc, + TCPC_V10_REG_POWER_STATUS_MASK, mask); +} + +static int rt1711_init_fault_mask(struct tcpc_device *tcpc) +{ + const uint8_t mask = + TCPC_V10_REG_FAULT_STATUS_VCONN_OV | + TCPC_V10_REG_FAULT_STATUS_VCONN_OC; + + return rt1711_i2c_write8(tcpc, + TCPC_V10_REG_FAULT_STATUS_MASK, mask); +} + +static int rt1711_init_rt_mask(struct tcpc_device *tcpc) +{ + uint8_t rt_mask = 0; +#ifdef CONFIG_TCPC_WATCHDOG_EN + rt_mask |= RT1711H_REG_M_WATCHDOG; +#endif /* CONFIG_TCPC_WATCHDOG_EN */ +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC + rt_mask |= RT1711H_REG_M_VBUS_80; +#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */ + +#ifdef CONFIG_TYPEC_CAP_RA_DETACH + if (tcpc->tcpc_flags & TCPC_FLAGS_CHECK_RA_DETACHE) + rt_mask |= RT1711H_REG_M_RA_DETACH; +#endif /* CONFIG_TYPEC_CAP_RA_DETACH */ + +#ifdef CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG + if (tcpc->tcpc_flags & TCPC_FLAGS_LPM_WAKEUP_WATCHDOG) + rt_mask |= RT1711H_REG_M_WAKEUP; +#endif /* CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG */ + + return rt1711_i2c_write8(tcpc, RT1711H_REG_RT_MASK, rt_mask); +} + +#ifdef CONFIG_TCPC_RT1711H +static inline void rt1711_poll_ctrl(struct rt1711_chip *chip) +{ + cancel_delayed_work_sync(&chip->poll_work); + + if (atomic_read(&chip->poll_count) == 0) { + atomic_inc(&chip->poll_count); + cpu_idle_poll_ctrl(true); + } + + schedule_delayed_work( + &chip->poll_work, msecs_to_jiffies(40)); +} +#endif /* CONFIG_TCPC_RT1711H */ + +#ifdef CONFIG_TCPC_RT1711H +static void rt1711_poll_work(struct work_struct *work) +{ + struct rt1711_chip *chip = container_of( + work, struct rt1711_chip, poll_work.work); + + if (atomic_dec_and_test(&chip->poll_count)) + cpu_idle_poll_ctrl(false); +} +#endif /* CONFIG_TCPC_RT1711H */ + +static irqreturn_t rt1711_intr_handler(int irq, void *data) +{ + struct rt1711_chip *chip = data; + + pm_wakeup_event(chip->dev, RT1711H_IRQ_WAKE_TIME); + +#ifdef CONFIG_TCPC_RT1711H + rt1711_poll_ctrl(chip); +#endif /* CONFIG_TCPC_RT1711H */ + + tcpci_lock_typec(chip->tcpc); + tcpci_alert(chip->tcpc); + tcpci_unlock_typec(chip->tcpc); + + return IRQ_HANDLED; +} + +static int rt1711_init_alert(struct tcpc_device *tcpc) +{ + struct rt1711_chip *chip = tcpc_get_dev_data(tcpc); + int ret = 0; + char *name = NULL; + + /* Clear Alert Mask & Status */ + rt1711_write_word(chip->client, TCPC_V10_REG_ALERT_MASK, 0); + rt1711_write_word(chip->client, TCPC_V10_REG_ALERT, 0xffff); + + name = devm_kasprintf(chip->dev, GFP_KERNEL, "%s-IRQ", + chip->tcpc_desc->name); + if (!name) + return -ENOMEM; + + dev_info(chip->dev, "%s name = %s, gpio = %d\n", + __func__, chip->tcpc_desc->name, chip->irq_gpio); + + ret = devm_gpio_request(chip->dev, chip->irq_gpio, name); + if (ret < 0) { + dev_notice(chip->dev, "%s request GPIO fail(%d)\n", + __func__, ret); + return ret; + } + + ret = gpio_direction_input(chip->irq_gpio); + if (ret < 0) { + dev_notice(chip->dev, "%s set GPIO fail(%d)\n", __func__, ret); + return ret; + } + + chip->irq = gpio_to_irq(chip->irq_gpio); + if (chip->irq < 0) { + dev_notice(chip->dev, "%s gpio to irq fail(%d)", + __func__, ret); + return ret; + } + + dev_info(chip->dev, "%s IRQ number = %d\n", __func__, chip->irq); + + ret = devm_request_threaded_irq(chip->dev, chip->irq, NULL, + rt1711_intr_handler, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + name, chip); + if (ret < 0) { + dev_notice(chip->dev, "%s request irq fail(%d)\n", + __func__, ret); + return ret; + } + device_init_wakeup(chip->dev, true); + + return 0; +} + +int rt1711_alert_status_clear(struct tcpc_device *tcpc, uint32_t mask) +{ + int ret; + uint16_t mask_t1; + +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC + uint8_t mask_t2; +#endif + + /* Write 1 clear */ + mask_t1 = (uint16_t) mask; + if (mask_t1) { + ret = rt1711_i2c_write16(tcpc, TCPC_V10_REG_ALERT, mask_t1); + if (ret < 0) + return ret; + } + +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC + mask_t2 = mask >> 16; + if (mask_t2) { + ret = rt1711_i2c_write8(tcpc, RT1711H_REG_RT_INT, mask_t2); + if (ret < 0) + return ret; + } +#endif + + return 0; +} + +static int rt1711h_set_clock_gating(struct tcpc_device *tcpc, bool en) +{ + int ret = 0; + +#ifdef CONFIG_TCPC_CLOCK_GATING + int i = 0; + uint8_t clk2 = RT1711H_REG_CLK_DIV_600K_EN + | RT1711H_REG_CLK_DIV_300K_EN | RT1711H_REG_CLK_CK_300K_EN; + uint8_t clk3 = RT1711H_REG_CLK_DIV_2P4M_EN; + + if (!en) { + clk2 |= + RT1711H_REG_CLK_BCLK2_EN | RT1711H_REG_CLK_BCLK_EN; + clk3 |= + RT1711H_REG_CLK_CK_24M_EN | RT1711H_REG_CLK_PCLK_EN; + } + + if (en) { + for (i = 0; i < 2; i++) + ret = rt1711_alert_status_clear(tcpc, + TCPC_REG_ALERT_RX_ALL_MASK); + } + + if (ret == 0) + ret = rt1711_i2c_write8(tcpc, RT1711H_REG_CLK_CTRL2, clk2); + if (ret == 0) + ret = rt1711_i2c_write8(tcpc, RT1711H_REG_CLK_CTRL3, clk3); +#endif /* CONFIG_TCPC_CLOCK_GATING */ + + return ret; +} + +static inline int rt1711h_init_cc_params( + struct tcpc_device *tcpc, uint8_t cc_res) +{ + int rv = 0; + +#ifdef CONFIG_USB_POWER_DELIVERY +#ifdef CONFIG_USB_PD_SNK_DFT_NO_GOOD_CRC + uint8_t en, sel; + struct rt1711_chip *chip = tcpc_get_dev_data(tcpc); + + if (cc_res == TYPEC_CC_VOLT_SNK_DFT) { /* 0.55 */ + en = 0; + sel = 0x81; + } else if (chip->chip_id >= RT1715_DID_D) { /* 0.35 & 0.75 */ + en = 1; + sel = 0x81; + } else { /* 0.4 & 0.7 */ + en = 1; + sel = 0x80; + } + + rv = rt1711_i2c_write8(tcpc, RT1711H_REG_BMCIO_RXDZEN, en); + if (rv == 0) + rv = rt1711_i2c_write8(tcpc, RT1711H_REG_BMCIO_RXDZSEL, sel); +#endif /* CONFIG_USB_PD_SNK_DFT_NO_GOOD_CRC */ +#endif /* CONFIG_USB_POWER_DELIVERY */ + + return rv; +} + +static int rt1711_tcpc_init(struct tcpc_device *tcpc, bool sw_reset) +{ + int ret; + bool retry_discard_old = false; + struct rt1711_chip *chip = tcpc_get_dev_data(tcpc); + + RT1711_INFO("\n"); + + if (sw_reset) { + ret = rt1711_software_reset(tcpc); + if (ret < 0) + return ret; + } + +#ifdef CONFIG_TCPC_I2CRST_EN + rt1711_i2c_write8(tcpc, + RT1711H_REG_I2CRST_CTRL, + RT1711H_REG_I2CRST_SET(true, 0x0f)); +#endif /* CONFIG_TCPC_I2CRST_EN */ + + /* UFP Both RD setting */ + /* DRP = 0, RpVal = 0 (Default), Rd, Rd */ + rt1711_i2c_write8(tcpc, TCPC_V10_REG_ROLE_CTRL, + TCPC_V10_REG_ROLE_CTRL_RES_SET(0, 0, CC_RD, CC_RD)); + + if (chip->chip_id == RT1711H_DID_A) { + rt1711_i2c_write8(tcpc, TCPC_V10_REG_FAULT_CTRL, + TCPC_V10_REG_FAULT_CTRL_DIS_VCONN_OV); + } else if (chip->chip_id == SC2150A_DID) + rt1711_i2c_write8(tcpc, TCPC_V10_REG_COMMAND, + TCPM_CMD_ENABLE_VBUS_DETECT); + + /* + * CC Detect Debounce : 26.7*val us + * Transition window count : spec 12~20us, based on 2.4MHz + * DRP Toggle Cycle : 51.2 + 6.4*val ms + * DRP Duyt Ctrl : dcSRC: /1024 + */ + + rt1711_i2c_write8(tcpc, RT1711H_REG_TTCPC_FILTER, 10); + rt1711_i2c_write8(tcpc, RT1711H_REG_DRP_TOGGLE_CYCLE, 4); + rt1711_i2c_write16(tcpc, + RT1711H_REG_DRP_DUTY_CTRL, TCPC_NORMAL_RP_DUTY); + + /* Vconn OC */ + rt1711_i2c_write8(tcpc, RT1711H_REG_VCONN_CLIMITEN, 1); + + /* RX/TX Clock Gating (Auto Mode)*/ + if (!sw_reset) + rt1711h_set_clock_gating(tcpc, true); + + if (!(tcpc->tcpc_flags & TCPC_FLAGS_RETRY_CRC_DISCARD)) + retry_discard_old = true; + + rt1711_i2c_write8(tcpc, RT1711H_REG_CONFIG_GPIO0, 0x80); + + /* For BIST, Change Transition Toggle Counter (Noise) from 3 to 7 */ + rt1711_i2c_write8(tcpc, RT1711H_REG_PHY_CTRL1, + RT1711H_REG_PHY_CTRL1_SET(retry_discard_old, 7, 0, 1)); + + tcpci_alert_status_clear(tcpc, 0xffffffff); + + rt1711_init_vbus_cal(tcpc); + rt1711_init_power_status_mask(tcpc); + rt1711_init_alert_mask(tcpc); + rt1711_init_fault_mask(tcpc); + rt1711_init_rt_mask(tcpc); + + /* CK_300K from 320K, SHIPPING off, AUTOIDLE enable, TIMEOUT = 6.4ms */ + rt1711_i2c_write8(tcpc, RT1711H_REG_IDLE_CTRL, + RT1711H_REG_IDLE_SET(0, 1, 1, 0)); + mdelay(1); + + return 0; +} + +static inline int rt1711_fault_status_vconn_ov(struct tcpc_device *tcpc) +{ + int ret; + + ret = rt1711_i2c_read8(tcpc, RT1711H_REG_BMC_CTRL); + if (ret < 0) + return ret; + + ret &= ~RT1711H_REG_DISCHARGE_EN; + return rt1711_i2c_write8(tcpc, RT1711H_REG_BMC_CTRL, ret); +} + +int rt1711_fault_status_clear(struct tcpc_device *tcpc, uint8_t status) +{ + int ret; + + if (status & TCPC_V10_REG_FAULT_STATUS_VCONN_OV) + ret = rt1711_fault_status_vconn_ov(tcpc); + + rt1711_i2c_write8(tcpc, TCPC_V10_REG_FAULT_STATUS, status); + return 0; +} + +int rt1711_get_chip_id(struct tcpc_device *tcpc, uint32_t *chip_id) +{ + struct rt1711_chip *chip = tcpc_get_dev_data(tcpc); + + *chip_id = chip->chip_id; + + return 0; +} + +int rt1711_get_alert_mask(struct tcpc_device *tcpc, uint32_t *mask) +{ + int ret; +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC + uint8_t v2; +#endif + + ret = rt1711_i2c_read16(tcpc, TCPC_V10_REG_ALERT_MASK); + if (ret < 0) + return ret; + + *mask = (uint16_t) ret; + +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC + ret = rt1711_i2c_read8(tcpc, RT1711H_REG_RT_MASK); + if (ret < 0) + return ret; + + v2 = (uint8_t) ret; + *mask |= v2 << 16; +#endif + + return 0; +} + +int rt1711_get_alert_status(struct tcpc_device *tcpc, uint32_t *alert) +{ + int ret; +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC + uint8_t v2; +#endif + + ret = rt1711_i2c_read16(tcpc, TCPC_V10_REG_ALERT); + if (ret < 0) + return ret; + + *alert = (uint16_t) ret; + +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC + ret = rt1711_i2c_read8(tcpc, RT1711H_REG_RT_INT); + if (ret < 0) + return ret; + + v2 = (uint8_t) ret; + *alert |= v2 << 16; +#endif + + return 0; +} + +static int rt1711_get_power_status( + struct tcpc_device *tcpc, uint16_t *pwr_status) +{ + int ret; + + ret = rt1711_i2c_read8(tcpc, TCPC_V10_REG_POWER_STATUS); + if (ret < 0) + return ret; + + *pwr_status = 0; + + if (ret & TCPC_V10_REG_POWER_STATUS_VBUS_PRES) + *pwr_status |= TCPC_REG_POWER_STATUS_VBUS_PRES; + +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC + ret = rt1711_i2c_read8(tcpc, RT1711H_REG_RT_STATUS); + if (ret < 0) + return ret; + + if (ret & RT1711H_REG_VBUS_80) + *pwr_status |= TCPC_REG_POWER_STATUS_EXT_VSAFE0V; +#endif + return 0; +} + +int rt1711_get_fault_status(struct tcpc_device *tcpc, uint8_t *status) +{ + int ret; + + ret = rt1711_i2c_read8(tcpc, TCPC_V10_REG_FAULT_STATUS); + if (ret < 0) + return ret; + *status = (uint8_t) ret; + return 0; +} + +static int rt1711_get_cc(struct tcpc_device *tcpc, int *cc1, int *cc2) +{ + int status, role_ctrl, cc_role; + bool act_as_sink, act_as_drp; + struct rt1711_chip *chip = tcpc_get_dev_data(tcpc); + + status = rt1711_i2c_read8(tcpc, TCPC_V10_REG_CC_STATUS); + if (status < 0) + return status; + + role_ctrl = rt1711_i2c_read8(tcpc, TCPC_V10_REG_ROLE_CTRL); + if (role_ctrl < 0) + return role_ctrl; + + if (status & TCPC_V10_REG_CC_STATUS_DRP_TOGGLING) { + *cc1 = TYPEC_CC_DRP_TOGGLING; + *cc2 = TYPEC_CC_DRP_TOGGLING; + return 0; + } + + *cc1 = TCPC_V10_REG_CC_STATUS_CC1(status); + *cc2 = TCPC_V10_REG_CC_STATUS_CC2(status); + + act_as_drp = TCPC_V10_REG_ROLE_CTRL_DRP & role_ctrl; + + if (act_as_drp) { + act_as_sink = TCPC_V10_REG_CC_STATUS_DRP_RESULT(status); + } else { + if (tcpc->typec_polarity) + cc_role = TCPC_V10_REG_CC_STATUS_CC2(role_ctrl); + else + cc_role = TCPC_V10_REG_CC_STATUS_CC1(role_ctrl); + if (cc_role == TYPEC_CC_RP) + act_as_sink = false; + else + act_as_sink = true; + } + + /* + * If status is not open, then OR in termination to convert to + * enum tcpc_cc_voltage_status. + */ + if (chip->chip_id == SC2150A_DID && act_as_drp && + act_as_sink) { + if ((*cc1 + *cc2) > (2 * TYPEC_CC_VOLT_RA)) { + if (*cc1 == TYPEC_CC_VOLT_RA) + *cc1 = TYPEC_CC_VOLT_OPEN; + if (*cc2 == TYPEC_CC_VOLT_RA) + *cc2 = TYPEC_CC_VOLT_OPEN; + } + } + + if (*cc1 != TYPEC_CC_VOLT_OPEN) + *cc1 |= (act_as_sink << 2); + + if (*cc2 != TYPEC_CC_VOLT_OPEN) + *cc2 |= (act_as_sink << 2); + + rt1711h_init_cc_params(tcpc, + (uint8_t)tcpc->typec_polarity ? *cc2 : *cc1); + + return 0; +} + +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC +static int rt1711_enable_vsafe0v_detect( + struct tcpc_device *tcpc, bool enable) +{ + int ret = 0; + + ret = rt1711_i2c_read8(tcpc, RT1711H_REG_RT_MASK); + + if (ret < 0) + return ret; + + if (enable) + ret |= RT1711H_REG_M_VBUS_80; + else + ret &= ~RT1711H_REG_M_VBUS_80; + + return rt1711_i2c_write8(tcpc, RT1711H_REG_RT_MASK, (uint8_t) ret); +} +#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */ + +static int rt1711_set_cc(struct tcpc_device *tcpc, int pull) +{ + int ret = 0; + uint8_t data = 0; + uint8_t old_data = 0; + int rp_lvl = TYPEC_CC_PULL_GET_RP_LVL(pull), pull1, pull2; + struct rt1711_chip *chip = tcpc_get_dev_data(tcpc); + + RT1711_INFO("pull = 0x%02X\n", pull); + pull = TYPEC_CC_PULL_GET_RES(pull); + if (chip->chip_id == SC2150A_DID) + old_data = rt1711_i2c_read8(tcpc, TCPC_V10_REG_ROLE_CTRL); + if (pull == TYPEC_CC_DRP) { + data = TCPC_V10_REG_ROLE_CTRL_RES_SET( + 1, rp_lvl, TYPEC_CC_RD, TYPEC_CC_RD); + if (chip->chip_id != SC2150A_DID || old_data != data) + ret = rt1711_i2c_write8( + tcpc, TCPC_V10_REG_ROLE_CTRL, data); + + if (ret == 0) { +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC + rt1711_enable_vsafe0v_detect(tcpc, false); +#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */ + ret = rt1711_command(tcpc, TCPM_CMD_LOOK_CONNECTION); + } + } else { +#ifdef CONFIG_USB_POWER_DELIVERY + if (pull == TYPEC_CC_RD && tcpc->pd_wait_pr_swap_complete) + rt1711h_init_cc_params(tcpc, TYPEC_CC_VOLT_SNK_DFT); +#endif /* CONFIG_USB_POWER_DELIVERY */ + + pull1 = pull2 = pull; + + if (chip->chip_id != SC2150A_DID) { + if (pull == TYPEC_CC_RP && tcpc->typec_is_attached_src) { + if (tcpc->typec_polarity) + pull1 = TYPEC_CC_OPEN; + else + pull2 = TYPEC_CC_OPEN; + } + } + data = TCPC_V10_REG_ROLE_CTRL_RES_SET(0, rp_lvl, pull1, pull2); + if (chip->chip_id != SC2150A_DID || old_data != data) + ret = rt1711_i2c_write8(tcpc, TCPC_V10_REG_ROLE_CTRL, data); + } + + return 0; +} + +static int rt1711_set_polarity(struct tcpc_device *tcpc, int polarity) +{ + int data; + + data = rt1711h_init_cc_params(tcpc, + tcpc->typec_remote_cc[polarity]); + if (data) + return data; + + data = rt1711_i2c_read8(tcpc, TCPC_V10_REG_TCPC_CTRL); + if (data < 0) + return data; + + data &= ~TCPC_V10_REG_TCPC_CTRL_PLUG_ORIENT; + data |= polarity ? TCPC_V10_REG_TCPC_CTRL_PLUG_ORIENT : 0; + + return rt1711_i2c_write8(tcpc, TCPC_V10_REG_TCPC_CTRL, data); +} + +static int rt1711_set_low_rp_duty(struct tcpc_device *tcpc, bool low_rp) +{ + uint16_t duty = low_rp ? TCPC_LOW_RP_DUTY : TCPC_NORMAL_RP_DUTY; + + return rt1711_i2c_write16(tcpc, RT1711H_REG_DRP_DUTY_CTRL, duty); +} + +static int rt1711_set_vconn(struct tcpc_device *tcpc, int enable) +{ + int rv; + int data; + + data = rt1711_i2c_read8(tcpc, TCPC_V10_REG_POWER_CTRL); + if (data < 0) + return data; + + data &= ~TCPC_V10_REG_POWER_CTRL_VCONN; + data |= enable ? TCPC_V10_REG_POWER_CTRL_VCONN : 0; + + rv = rt1711_i2c_write8(tcpc, TCPC_V10_REG_POWER_CTRL, data); + if (rv < 0) + return rv; + + return rt1711_i2c_write8(tcpc, RT1711H_REG_IDLE_CTRL, + RT1711H_REG_IDLE_SET(0, 1, enable ? 0 : 1, 0)); +} + +#ifdef CONFIG_TCPC_LOW_POWER_MODE +static int rt1711_is_low_power_mode(struct tcpc_device *tcpc) +{ + int rv = rt1711_i2c_read8(tcpc, RT1711H_REG_BMC_CTRL); + + if (rv < 0) + return rv; + + return (rv & RT1711H_REG_BMCIO_LPEN) != 0; +} + +static int rt1711_set_low_power_mode( + struct tcpc_device *tcpc, bool en, int pull) +{ + int ret = 0; + uint8_t data; + + ret = rt1711_i2c_write8(tcpc, RT1711H_REG_IDLE_CTRL, + RT1711H_REG_IDLE_SET(0, 1, en ? 0 : 1, 0)); + if (ret < 0) + return ret; +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC + rt1711_enable_vsafe0v_detect(tcpc, !en); +#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */ + if (en) { + data = RT1711H_REG_BMCIO_LPEN; + + if (pull & TYPEC_CC_RP) + data |= RT1711H_REG_BMCIO_LPRPRD; + +#ifdef CONFIG_TYPEC_CAP_NORP_SRC + data |= RT1711H_REG_BMCIO_BG_EN | RT1711H_REG_VBUS_DET_EN; +#endif + } else { + data = RT1711H_REG_BMCIO_BG_EN | + RT1711H_REG_VBUS_DET_EN | RT1711H_REG_BMCIO_OSC_EN; + } + + return rt1711_i2c_write8(tcpc, RT1711H_REG_BMC_CTRL, data); +} +#endif /* CONFIG_TCPC_LOW_POWER_MODE */ + +#ifdef CONFIG_TCPC_WATCHDOG_EN +int rt1711h_set_watchdog(struct tcpc_device *tcpc, bool en) +{ + uint8_t data = RT1711H_REG_WATCHDOG_CTRL_SET(en, 7); + + return rt1711_i2c_write8(tcpc, + RT1711H_REG_WATCHDOG_CTRL, data); +} +#endif /* CONFIG_TCPC_WATCHDOG_EN */ + +#ifdef CONFIG_TCPC_INTRST_EN +int rt1711h_set_intrst(struct tcpc_device *tcpc, bool en) +{ + return rt1711_i2c_write8(tcpc, + RT1711H_REG_INTRST_CTRL, RT1711H_REG_INTRST_SET(en, 3)); +} +#endif /* CONFIG_TCPC_INTRST_EN */ + +static int rt1711_tcpc_deinit(struct tcpc_device *tcpc) +{ +#ifdef CONFIG_RT_REGMAP + struct rt1711_chip *chip = tcpc_get_dev_data(tcpc); +#endif /* CONFIG_RT_REGMAP */ + +#ifdef CONFIG_TCPC_SHUTDOWN_CC_DETACH + rt1711_set_cc(tcpc, TYPEC_CC_DRP); + rt1711_set_cc(tcpc, TYPEC_CC_OPEN); + + rt1711_i2c_write8(tcpc, + RT1711H_REG_I2CRST_CTRL, + RT1711H_REG_I2CRST_SET(true, 4)); + + rt1711_i2c_write8(tcpc, + RT1711H_REG_INTRST_CTRL, + RT1711H_REG_INTRST_SET(true, 0)); + + if (chip->chip_id == SC2150A_DID) + rt1711_i2c_write8(tcpc, RT1711H_REG_SWRESET, 1); +#else + rt1711_i2c_write8(tcpc, RT1711H_REG_SWRESET, 1); +#endif /* CONFIG_TCPC_SHUTDOWN_CC_DETACH */ +#ifdef CONFIG_RT_REGMAP + rt_regmap_cache_reload(chip->m_dev); +#endif /* CONFIG_RT_REGMAP */ + + return 0; +} + +#ifdef CONFIG_USB_POWER_DELIVERY +static int rt1711_set_msg_header( + struct tcpc_device *tcpc, uint8_t power_role, uint8_t data_role) +{ + uint8_t msg_hdr = TCPC_V10_REG_MSG_HDR_INFO_SET( + data_role, power_role); + + return rt1711_i2c_write8( + tcpc, TCPC_V10_REG_MSG_HDR_INFO, msg_hdr); +} + +static int rt1711_protocol_reset(struct tcpc_device *tcpc) +{ + rt1711_i2c_write8(tcpc, RT1711H_REG_PRL_FSM_RESET, 0); + mdelay(1); + rt1711_i2c_write8(tcpc, RT1711H_REG_PRL_FSM_RESET, 1); + return 0; +} + +static int rt1711_set_rx_enable(struct tcpc_device *tcpc, uint8_t enable) +{ + int ret = 0; + + if (enable) + ret = rt1711h_set_clock_gating(tcpc, false); + + if (ret == 0) + ret = rt1711_i2c_write8(tcpc, TCPC_V10_REG_RX_DETECT, enable); + + if ((ret == 0) && (!enable)) { + rt1711_protocol_reset(tcpc); + ret = rt1711h_set_clock_gating(tcpc, true); + } + + return ret; +} + +static int rt1711_get_message(struct tcpc_device *tcpc, uint32_t *payload, + uint16_t *msg_head, enum tcpm_transmit_type *frame_type) +{ + struct rt1711_chip *chip = tcpc_get_dev_data(tcpc); + int rv = 0; + uint8_t cnt = 0; + uint8_t buf[4]; + + rv = rt1711_block_read(chip->client, TCPC_V10_REG_RX_BYTE_CNT, 4, buf); + if (rv < 0) + return rv; + + cnt = buf[0]; + *frame_type = buf[1]; + *msg_head = le16_to_cpu(*(uint16_t *)&buf[2]); + + /* TCPC 1.0 ==> no need to subtract the size of msg_head */ + if (cnt > 3) { + cnt -= 3; /* MSG_HDR */ + rv = rt1711_block_read(chip->client, TCPC_V10_REG_RX_DATA, cnt, + payload); + } + + return rv; +} + +static int rt1711_set_bist_carrier_mode( + struct tcpc_device *tcpc, uint8_t pattern) +{ + /* Don't support this function */ + return 0; +} + +#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD +static int rt1711_retransmit(struct tcpc_device *tcpc) +{ + return rt1711_i2c_write8(tcpc, TCPC_V10_REG_TRANSMIT, + TCPC_V10_REG_TRANSMIT_SET( + tcpc->pd_retry_count, TCPC_TX_SOP)); +} +#endif + +#pragma pack(push, 1) +struct tcpc_transmit_packet { + uint8_t cnt; + uint16_t msg_header; + uint8_t data[sizeof(uint32_t)*7]; +}; +#pragma pack(pop) + +static int rt1711_transmit(struct tcpc_device *tcpc, + enum tcpm_transmit_type type, uint16_t header, const uint32_t *data) +{ + struct rt1711_chip *chip = tcpc_get_dev_data(tcpc); + int rv; + int data_cnt; + struct tcpc_transmit_packet packet; + + if (type < TCPC_TX_HARD_RESET) { + data_cnt = sizeof(uint32_t) * PD_HEADER_CNT(header); + + packet.cnt = data_cnt + sizeof(uint16_t); + packet.msg_header = header; + + if (data_cnt > 0) + memcpy(packet.data, (uint8_t *) data, data_cnt); + if (chip->chip_id == SC2150A_DID) + packet.cnt += 4; + + rv = rt1711_block_write(chip->client, + TCPC_V10_REG_TX_BYTE_CNT, + packet.cnt+1, (uint8_t *) &packet); + if (rv < 0) + return rv; + } + + rv = rt1711_i2c_write8(tcpc, TCPC_V10_REG_TRANSMIT, + TCPC_V10_REG_TRANSMIT_SET( + tcpc->pd_retry_count, type)); + return rv; +} + +static int rt1711_set_bist_test_mode(struct tcpc_device *tcpc, bool en) +{ + int data; + + data = rt1711_i2c_read8(tcpc, TCPC_V10_REG_TCPC_CTRL); + if (data < 0) + return data; + + data &= ~TCPC_V10_REG_TCPC_CTRL_BIST_TEST_MODE; + data |= en ? TCPC_V10_REG_TCPC_CTRL_BIST_TEST_MODE : 0; + + return rt1711_i2c_write8(tcpc, TCPC_V10_REG_TCPC_CTRL, data); +} +#endif /* CONFIG_USB_POWER_DELIVERY */ + +static struct tcpc_ops rt1711_tcpc_ops = { + .init = rt1711_tcpc_init, + .alert_status_clear = rt1711_alert_status_clear, + .fault_status_clear = rt1711_fault_status_clear, + .get_chip_id= rt1711_get_chip_id, + .get_alert_mask = rt1711_get_alert_mask, + .get_alert_status = rt1711_get_alert_status, + .get_power_status = rt1711_get_power_status, + .get_fault_status = rt1711_get_fault_status, + .get_cc = rt1711_get_cc, + .set_cc = rt1711_set_cc, + .set_polarity = rt1711_set_polarity, + .set_low_rp_duty = rt1711_set_low_rp_duty, + .set_vconn = rt1711_set_vconn, + .deinit = rt1711_tcpc_deinit, + +#ifdef CONFIG_TCPC_LOW_POWER_MODE + .is_low_power_mode = rt1711_is_low_power_mode, + .set_low_power_mode = rt1711_set_low_power_mode, +#endif /* CONFIG_TCPC_LOW_POWER_MODE */ + +#ifdef CONFIG_TCPC_WATCHDOG_EN + .set_watchdog = rt1711h_set_watchdog, +#endif /* CONFIG_TCPC_WATCHDOG_EN */ + +#ifdef CONFIG_TCPC_INTRST_EN + .set_intrst = rt1711h_set_intrst, +#endif /* CONFIG_TCPC_INTRST_EN */ + +#ifdef CONFIG_USB_POWER_DELIVERY + .set_msg_header = rt1711_set_msg_header, + .set_rx_enable = rt1711_set_rx_enable, + .protocol_reset = rt1711_protocol_reset, + .get_message = rt1711_get_message, + .transmit = rt1711_transmit, + .set_bist_test_mode = rt1711_set_bist_test_mode, + .set_bist_carrier_mode = rt1711_set_bist_carrier_mode, +#endif /* CONFIG_USB_POWER_DELIVERY */ + +#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD + .retransmit = rt1711_retransmit, +#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */ +}; + +static int rt_parse_dt(struct rt1711_chip *chip, struct device *dev) +{ + struct device_node *np = dev->of_node; + int ret = 0; + + pr_info("%s\n", __func__); + +#if (!defined(CONFIG_MTK_GPIO) || defined(CONFIG_MTK_GPIOLIB_STAND)) + ret = of_get_named_gpio(np, "rt1711pd,intr_gpio", 0); + if (ret < 0) { + pr_err("%s no intr_gpio info\n", __func__); + return ret; + } + chip->irq_gpio = ret; +#else + ret = of_property_read_u32(np, + "rt1711pd,intr_gpio_num", &chip->irq_gpio); + if (ret < 0) + pr_err("%s no intr_gpio info\n", __func__); +#endif + return ret < 0 ? ret : 0; +} + +/* + * In some platform pr_info may spend too much time on printing debug message. + * So we use this function to test the printk performance. + * If your platform cannot not pass this check function, please config + * PD_DBG_INFO, this will provide the threaded debug message for you. + */ +#if TCPC_ENABLE_ANYMSG +static void check_printk_performance(void) +{ + int i; + u64 t1, t2; + u32 nsrem; + +#ifdef CONFIG_PD_DBG_INFO + for (i = 0; i < 10; i++) { + t1 = local_clock(); + pd_dbg_info("%d\n", i); + t2 = local_clock(); + t2 -= t1; + nsrem = do_div(t2, 1000000000); + pd_dbg_info("pd_dbg_info : t2-t1 = %lu\n", + (unsigned long)nsrem / 1000); + } + for (i = 0; i < 10; i++) { + t1 = local_clock(); + pr_info("%d\n", i); + t2 = local_clock(); + t2 -= t1; + nsrem = do_div(t2, 1000000000); + pr_info("pr_info : t2-t1 = %lu\n", + (unsigned long)nsrem / 1000); + } +#else + for (i = 0; i < 10; i++) { + t1 = local_clock(); + pr_info("%d\n", i); + t2 = local_clock(); + t2 -= t1; + nsrem = do_div(t2, 1000000000); + pr_info("t2-t1 = %lu\n", + (unsigned long)nsrem / 1000); + PD_BUG_ON(nsrem > 100*1000); + } +#endif /* CONFIG_PD_DBG_INFO */ +} +#endif /* TCPC_ENABLE_ANYMSG */ + +static int rt1711_tcpcdev_init(struct rt1711_chip *chip, struct device *dev) +{ + struct tcpc_desc *desc; + struct device_node *np = dev->of_node; + u32 val, len; + const char *name = "default"; + + dev_info(dev, "%s\n", __func__); + + desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + if (of_property_read_u32(np, "rt-tcpc,role_def", &val) >= 0) { + if (val >= TYPEC_ROLE_NR) + desc->role_def = TYPEC_ROLE_DRP; + else + desc->role_def = val; + } else { + dev_info(dev, "use default Role DRP\n"); + desc->role_def = TYPEC_ROLE_DRP; + } + + if (of_property_read_u32( + np, "rt-tcpc,notifier_supply_num", &val) >= 0) { + if (val < 0) + desc->notifier_supply_num = 0; + else + desc->notifier_supply_num = val; + } else + desc->notifier_supply_num = 0; + + if (of_property_read_u32(np, "rt-tcpc,rp_level", &val) >= 0) { + switch (val) { + case TYPEC_RP_DFT: + case TYPEC_RP_1_5: + case TYPEC_RP_3_0: + desc->rp_lvl = val; + break; + default: + break; + } + } + +#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE + if (of_property_read_u32(np, "rt-tcpc,vconn_supply", &val) >= 0) { + if (val >= TCPC_VCONN_SUPPLY_NR) + desc->vconn_supply = TCPC_VCONN_SUPPLY_ALWAYS; + else + desc->vconn_supply = val; + } else { + dev_info(dev, "use default VconnSupply\n"); + desc->vconn_supply = TCPC_VCONN_SUPPLY_ALWAYS; + } +#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */ + + if (of_property_read_string(np, "rt-tcpc,name", + (char const **)&name) < 0) { + dev_info(dev, "use default name\n"); + } + + len = strlen(name); + desc->name = kzalloc(len+1, GFP_KERNEL); + if (!desc->name) + return -ENOMEM; + + strlcpy((char *)desc->name, name, len+1); + + chip->tcpc_desc = desc; + + chip->tcpc = tcpc_device_register(dev, + desc, &rt1711_tcpc_ops, chip); + if (IS_ERR_OR_NULL(chip->tcpc)) + return -EINVAL; + +#ifdef CONFIG_USB_PD_DISABLE_PE + chip->tcpc->disable_pe = + of_property_read_bool(np, "rt-tcpc,disable_pe"); +#endif /* CONFIG_USB_PD_DISABLE_PE */ + + chip->tcpc->tcpc_flags = TCPC_FLAGS_LPM_WAKEUP_WATCHDOG | + TCPC_FLAGS_VCONN_SAFE5V_ONLY; + + if (chip->chip_id > RT1711H_DID_B) + chip->tcpc->tcpc_flags |= TCPC_FLAGS_CHECK_RA_DETACHE; + +#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD + if (chip->chip_id > RT1715_DID_D) + chip->tcpc->tcpc_flags |= TCPC_FLAGS_RETRY_CRC_DISCARD; +#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */ + +#ifdef CONFIG_USB_PD_REV30 + if ((chip->chip_id >= RT1715_DID_D) || (chip->chip_id == SC2150A_DID)) + chip->tcpc->tcpc_flags |= TCPC_FLAGS_PD_REV30; + + if (chip->tcpc->tcpc_flags & TCPC_FLAGS_PD_REV30) + dev_info(dev, "PD_REV30\n"); + else + dev_info(dev, "PD_REV20\n"); +#endif /* CONFIG_USB_PD_REV30 */ + chip->tcpc->tcpc_flags |= TCPC_FLAGS_ALERT_V10; + + return 0; +} + +#define RICHTEK_1711_VID 0x29cf +#define RICHTEK_1711_PID 0x1711 +#define SC2150A_VID 0x311C +#define SC2150A_PID 0x2150 + +static inline int rt1711h_check_revision(struct i2c_client *client) +{ + u16 vid, pid, did; + int ret; + u8 data = 1; + + ret = rt1711_read_device(client, TCPC_V10_REG_VID, 2, &vid); + if (ret < 0) { + dev_err(&client->dev, "read chip ID fail(%d)\n", ret); + return -EIO; + } + + if ((vid != RICHTEK_1711_VID) && (vid != SC2150A_VID)) { + pr_info("%s failed, VID=0x%04x\n", __func__, vid); + return -ENODEV; + } + + ret = rt1711_read_device(client, TCPC_V10_REG_PID, 2, &pid); + if (ret < 0) { + dev_err(&client->dev, "read product ID fail(%d)\n", ret); + return -EIO; + } + + if ((pid != RICHTEK_1711_PID) && (pid != SC2150A_PID)) { + pr_info("%s failed, PID=0x%04x\n", __func__, pid); + return -ENODEV; + } + + ret = rt1711_write_device(client, RT1711H_REG_SWRESET, 1, &data); + if (ret < 0) + return ret; + + usleep_range(1000, 2000); + + ret = rt1711_read_device(client, TCPC_V10_REG_DID, 2, &did); + if (ret < 0) { + dev_err(&client->dev, "read device ID fail(%d)\n", ret); + return -EIO; + } + + return did; +} + +static int rt1711_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct rt1711_chip *chip; + int ret = 0, chip_id; + bool use_dt = client->dev.of_node; + + pr_info("%s (%s)\n", __func__, RT1711H_DRV_VERSION); + if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_BYTE_DATA)) + pr_info("I2C functionality : OK...\n"); + else + pr_info("I2C functionality check : failuare...\n"); + + chip_id = rt1711h_check_revision(client); + if (chip_id < 0) + return chip_id; + +#if TCPC_ENABLE_ANYMSG + check_printk_performance(); +#endif /* TCPC_ENABLE_ANYMSG */ + + chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + if (use_dt) { + ret = rt_parse_dt(chip, &client->dev); + if (ret < 0) + return ret; + } else { + dev_err(&client->dev, "no dts node\n"); + return -ENODEV; + } + chip->dev = &client->dev; + chip->client = client; + i2c_set_clientdata(client, chip); +#ifdef CONFIG_TCPC_RT1711H + INIT_DELAYED_WORK(&chip->poll_work, rt1711_poll_work); +#endif /* CONFIG_TCPC_RT1711H */ + chip->chip_id = chip_id; + pr_info("rt1711h_chipID = 0x%0x\n", chip_id); + + ret = rt1711_regmap_init(chip); + if (ret < 0) { + dev_err(chip->dev, "rt1711 regmap init fail\n"); + goto err_regmap_init; + } + + ret = rt1711_tcpcdev_init(chip, &client->dev); + if (ret < 0) { + dev_err(&client->dev, "rt1711 tcpc dev init fail\n"); + goto err_tcpc_reg; + } + + ret = rt1711_init_alert(chip->tcpc); + if (ret < 0) { + pr_err("rt1711 init alert fail\n"); + goto err_irq_init; + } + + tcpc_schedule_init_work(chip->tcpc); + pr_info("%s probe OK!\n", __func__); + return 0; + +err_irq_init: + tcpc_device_unregister(chip->dev, chip->tcpc); +err_tcpc_reg: + rt1711_regmap_deinit(chip); +err_regmap_init: + return ret; +} + +static int rt1711_i2c_remove(struct i2c_client *client) +{ + struct rt1711_chip *chip = i2c_get_clientdata(client); + + if (chip) { +#ifdef CONFIG_TCPC_RT1711H + cancel_delayed_work_sync(&chip->poll_work); +#endif /* CONFIG_TCPC_RT1711H */ + tcpc_device_unregister(chip->dev, chip->tcpc); + rt1711_regmap_deinit(chip); + } + + return 0; +} + +#ifdef CONFIG_PM +static int rt1711_i2c_suspend(struct device *dev) +{ + struct rt1711_chip *chip = dev_get_drvdata(dev); + + dev_info(dev, "%s\n", __func__); + if (device_may_wakeup(dev)) + enable_irq_wake(chip->irq); + disable_irq(chip->irq); + + return 0; +} + +static int rt1711_i2c_resume(struct device *dev) +{ + struct rt1711_chip *chip = dev_get_drvdata(dev); + + dev_info(dev, "%s\n", __func__); + enable_irq(chip->irq); + if (device_may_wakeup(dev)) + disable_irq_wake(chip->irq); + + return 0; +} + +static void rt1711_shutdown(struct i2c_client *client) +{ + struct rt1711_chip *chip = i2c_get_clientdata(client); + + /* Please reset IC here */ + if (chip != NULL) { + if (chip->irq) + disable_irq(chip->irq); + tcpm_shutdown(chip->tcpc); + } else { + i2c_smbus_write_byte_data( + client, RT1711H_REG_SWRESET, 0x01); + } +} + +#ifdef CONFIG_PM_RUNTIME +static int rt1711_pm_suspend_runtime(struct device *device) +{ + dev_dbg(device, "pm_runtime: suspending...\n"); + return 0; +} + +static int rt1711_pm_resume_runtime(struct device *device) +{ + dev_dbg(device, "pm_runtime: resuming...\n"); + return 0; +} +#endif /* #ifdef CONFIG_PM_RUNTIME */ + +static const struct dev_pm_ops rt1711_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS( + rt1711_i2c_suspend, + rt1711_i2c_resume) +#ifdef CONFIG_PM_RUNTIME + SET_RUNTIME_PM_OPS( + rt1711_pm_suspend_runtime, + rt1711_pm_resume_runtime, + NULL + ) +#endif /* #ifdef CONFIG_PM_RUNTIME */ +}; +#define RT1711_PM_OPS (&rt1711_pm_ops) +#else +#define RT1711_PM_OPS (NULL) +#endif /* CONFIG_PM */ + +static const struct i2c_device_id rt1711_id_table[] = { + {"rt1711h", 0}, + {"rt1715", 0}, + {"rt1716", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, rt1711_id_table); + +static const struct of_device_id rt_match_table[] = { + {.compatible = "richtek,rt1711h",}, + {.compatible = "richtek,rt1715",}, + {.compatible = "richtek,rt1716",}, + {}, +}; + +static struct i2c_driver rt1711_driver = { + .driver = { + .name = "usb_type_c", + .owner = THIS_MODULE, + .of_match_table = rt_match_table, + .pm = RT1711_PM_OPS, + }, + .probe = rt1711_i2c_probe, + .remove = rt1711_i2c_remove, + .shutdown = rt1711_shutdown, + .id_table = rt1711_id_table, +}; + +module_i2c_driver(rt1711_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jeff Chang "); +MODULE_DESCRIPTION("RT1711 TCPC Driver"); +MODULE_VERSION(RT1711H_DRV_VERSION); + +/**** Release Note **** + * 2.0.6_G + * (1) Revise IRQ handling + * + * 2.0.5_G + * (1) Utilize rt-regmap to reduce I2C accesses + * (2) Decrease VBUS present threshold (VBUS_CAL) by 60mV (2LSBs) + * + * 2.0.4_G + * (1) Mask vSafe0V IRQ before entering low power mode + * (2) Disable auto idle mode before entering low power mode + * (3) Reset Protocol FSM and clear RX alerts twice before clock gating + * + * 2.0.3_G + * (1) Single Rp as Attatched.SRC for Ellisys TD.4.9.4 + * + * 2.0.2_G + * (1) Replace wake_lock with wakeup_source + * (2) Move down the shipping off + * (3) Add support for NoRp.SRC + * (4) Reg0x71[7] = 1'b1 to workaround unstable VDD Iq in low power mode + * (5) Add get_alert_mask of tcpc_ops + * + * 2.0.1_G + * First released PD3.0 Driver + */ diff --git a/drivers/usb/typec/mmi_tcpc/tcpc_sgm7220.c b/drivers/usb/typec/mmi_tcpc/tcpc_sgm7220.c new file mode 100644 index 000000000000..4602fa7f96dc --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/tcpc_sgm7220.c @@ -0,0 +1,1240 @@ +/* + * SG Micro sgm7220 Type-C Port Control Driver + * + * 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. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "inc/tcpci.h" +#include "inc/sgm7220.h" +#include +#include + +#define SGM7220_DRV_VERSION "1.0.0_SGM7220" +#define SGM7220_IRQ_WAKE_TIME (500) /* ms */ + +/****************************************************************************** + * Constants + ******************************************************************************/ + +enum drp_toggle_type { + TOGGLE_DFP_DRP_30 = 0, + TOGGLE_DFP_DRP_40, + TOGGLE_DFP_DRP_50, + TOGGLE_DFP_DRP_60 +}; + +enum current_adv_type { + HOST_CUR_USB = 0, /*default 500mA or 900mA*/ + HOST_CUR_1P5, /*1.5A*/ + HOST_CUR_3A /*3A*/ +}; + +enum current_det_type { + DET_CUR_USB = 0, /*default 500mA or 900mA*/ + DET_CUR_1P5, + DET_CUR_ACCESSORY, /*charg through accessory 500mA*/ + DET_CUR_3A +}; + +enum accessory_attach_type { + ACCESSORY_NOT_ATTACHED = 0, + ACCESSORY_AUDIO = 4, + ACCESSORY_CHG_THRU_AUDIO = 5, + ACCESSORY_DEBUG = 6 +}; + +enum cable_attach_type { + CABLE_NOT_ATTACHED = 0, + CABLE_ATTACHED +}; + +enum cable_state_type { + CABLE_STATE_NOT_ATTACHED = 0, + CABLE_STATE_AS_DFP, + CABLE_STATE_AS_UFP, + CABLE_STATE_TO_ACCESSORY +}; + +enum cable_dir_type { + ORIENT_CC1 = 0, + ORIENT_CC2 +}; + +/* Type-C Attrs */ +struct type_c_parameters { + enum current_det_type current_det; /*charging current on UFP*/ + enum accessory_attach_type accessory_attach; /*if an accessory is attached*/ + enum cable_attach_type active_cable_attach; /*if an active_cable is attached*/ + enum cable_state_type attach_state; /*DFP->UFP or UFP->DFP*/ + enum cable_dir_type cable_dir; /*cc1 or cc2*/ +}; + +struct state_disorder_monitor { + int count; + int err_detected; + enum cable_state_type former_state; + unsigned long time_before; +}; + +struct sgm7220_chip { + struct i2c_client *client; + struct device *dev; + struct semaphore io_lock; + struct semaphore suspend_lock; + struct tcpc_desc *tcpc_desc; + struct tcpc_device *tcpc; + struct kthread_worker irq_worker; + struct kthread_work irq_work; + struct task_struct *irq_worker_task; + struct wakeup_source *irq_wake_lock; + struct wakeup_source *i2c_wake_lock; + + atomic_t poll_count; + struct delayed_work poll_work; + struct delayed_work first_check_typec_work; + + int irq_gpio; + uint8_t dev_id; + uint8_t dev_sub_id; + int irq; + int chip_id; + struct type_c_parameters type_c_param; + struct state_disorder_monitor monitor; +}; + +static struct i2c_client *w_client; + +static int sgm7220_read_device(void *client, u32 reg, int len, void *dst) +{ + struct i2c_client *i2c = (struct i2c_client *)client; + struct sgm7220_chip *chip = i2c_get_clientdata(i2c); + int ret = 0, count = 5; + + __pm_stay_awake(chip->i2c_wake_lock); + down(&chip->suspend_lock); + while (count) { + if (len > 1) { + ret = i2c_smbus_read_i2c_block_data(i2c, reg, len, dst); + if (ret < 0) + count--; + else + goto out; + } else { + ret = i2c_smbus_read_byte_data(i2c, reg); + if (ret < 0) + count--; + else { + *(u8 *)dst = (u8)ret; + goto out; + } + } + udelay(100); + } +out: + up(&chip->suspend_lock); + __pm_relax(chip->i2c_wake_lock); + return ret; +} + +static int sgm7220_write_device(void *client, u32 reg, int len, const void *src) +{ + const u8 *data; + struct i2c_client *i2c = (struct i2c_client *)client; + struct sgm7220_chip *chip = i2c_get_clientdata(i2c); + int ret = 0, count = 5; + + __pm_stay_awake(chip->i2c_wake_lock); + down(&chip->suspend_lock); + while (count) { + if (len > 1) { + ret = i2c_smbus_write_i2c_block_data(i2c, + reg, len, src); + if (ret < 0) + count--; + else + goto out; + } else { + data = src; + ret = i2c_smbus_write_byte_data(i2c, reg, *data); + if (ret < 0) + count--; + else + goto out; + } + udelay(100); + } +out: + up(&chip->suspend_lock); + __pm_relax(chip->i2c_wake_lock); + return ret; +} + +static int sgm7220_reg_write(struct i2c_client *i2c, u8 reg, const u8 data) +{ + struct sgm7220_chip *chip = i2c_get_clientdata(i2c); + int ret = 0; + + ret = sgm7220_write_device(chip->client, reg, 1, &data); + if (ret < 0) + dev_err(chip->dev, "sgm7220 reg write fail\n"); + return ret; +} + +static int sgm7220_reg_update(struct i2c_client *i2c, u8 reg, const u8 data, u8 mask) +{ + struct sgm7220_chip *chip = i2c_get_clientdata(i2c); + int ret; + u8 old_val, new_val; + + ret = sgm7220_read_device(chip->client, reg, 1, &old_val); + if (ret < 0) { + dev_err(chip->dev, "sgm7220 reg read fail\n"); + return ret; + } + + if (ret >= 0) { + old_val = ret & 0xff; + new_val = (data & mask) | (old_val & (~mask)); + ret = sgm7220_write_device(chip->client, reg, 1, &new_val); + if (ret < 0) + dev_err(chip->dev, "sgm7220 reg write fail\n"); + } + return ret; +} + +/* Detect non-DFP->DFP changes that happen more than 3 times within 10 Secs */ +static void sgm7220_state_disorder_detect(struct sgm7220_chip *chip) +{ + unsigned long timeout; + + /* count the (non-DFP -> DFP) changes */ + if ((chip->monitor.former_state != chip->type_c_param.attach_state) + && (chip->type_c_param.attach_state == CABLE_STATE_AS_DFP)) { + if (!chip->monitor.count) { + chip->monitor.time_before = jiffies; + } + chip->monitor.count++; + } + + /* store the state */ + chip->monitor.former_state = chip->type_c_param.attach_state; + + if (chip->monitor.count > 3) { + timeout = msecs_to_jiffies(10 * 1000); /* 10 Seconds */ + if (time_before(jiffies, chip->monitor.time_before + timeout)) { + chip->monitor.err_detected = 1; + /* disbale id irq before qpnp react to cc chip's id output */ + //interfere_id_irq_from_usb(0); + } + chip->monitor.count = 0; + } + + if ((chip->type_c_param.attach_state == CABLE_STATE_NOT_ATTACHED) + && chip->monitor.err_detected) { + /* enable id irq */ + //interfere_id_irq_from_usb(1); + chip->monitor.err_detected = 0; + } +} + +static void sgm7220_process_mode_register(struct sgm7220_chip *chip, u8 status) +{ + u8 val; + u8 tmp = status; + + /* check current_detect */ + val = ((tmp & MOD_CURRENT_MODE_DETECT) >> MOD_CURRENT_MODE_DETECT_SHIFT); + chip->type_c_param.current_det = val; + + /* check accessory attch */ + tmp = status; + val = ((tmp & MOD_ACCESSORY_CONNECTED) >> MOD_ACCESSORY_CONNECTED_SHIFT); + chip->type_c_param.accessory_attach = val; + + /* check cable attach */ + tmp = status; + val = (tmp & MOD_ACTIVE_CABLE_DETECTION); + chip->type_c_param.active_cable_attach = val; + + pr_info("sgm7220 %s , current_det = %d, accessory_attach = %d, active_cable_attach = %d\n", + __func__, + chip->type_c_param.current_det, + chip->type_c_param.accessory_attach, + chip->type_c_param.active_cable_attach); +} + +static int sgm7220_remote_rp_level(struct sgm7220_chip *chip) +{ + if (chip->type_c_param.current_det == DET_CUR_USB) + return TYPEC_CC_VOLT_SNK_DFT; + else if (chip->type_c_param.current_det == DET_CUR_1P5) + return TYPEC_CC_VOLT_SNK_1_5; + else if (chip->type_c_param.current_det == DET_CUR_3A) + return TYPEC_CC_VOLT_SNK_3_0; + else + return TYPEC_CC_VOLT_SNK_DFT; +} + +static int sgm7220_local_rp_level(struct sgm7220_chip *chip) +{ + return TYPEC_CC_RP_1_5; +} + +static void sgm7220_process_interrupt_register(struct sgm7220_chip *chip, u8 status) +{ + struct tcpc_device *tcpc; + u8 val; + u8 tmp = status; + + tcpc = chip->tcpc; + + /* check attach state */ + val = ((tmp & INT_ATTACHED_STATE) >> INT_ATTACHED_STATE_SHIFT); + chip->type_c_param.attach_state = val; + + /* update current adv when act as DFP */ + if (chip->type_c_param.attach_state == CABLE_STATE_AS_DFP || + chip->type_c_param.attach_state == CABLE_STATE_TO_ACCESSORY) { + val = (HOST_CUR_USB << MOD_CURRENT_MODE_ADVERTISE_SHIFT); + } else { + val = (HOST_CUR_1P5 << MOD_CURRENT_MODE_ADVERTISE_SHIFT); + } + sgm7220_reg_update(chip->client, REG_MOD, val, MOD_CURRENT_MODE_ADVERTISE); + + /* in case configured as DRP may detect some non-standard SDP */ + /* chargers as UFP, which may lead to a cyclic switching of DFP */ + /* and UFP on state detection result. */ + sgm7220_state_disorder_detect(chip); + + /* check cable dir */ + tmp = status; + val = ((tmp & INT_CABLE_DIR) >> INT_CABLE_DIR_SHIFT); + chip->type_c_param.cable_dir = val; + tcpc->typec_polarity = chip->type_c_param.cable_dir; + pr_info("%s type=%d,cc_stat(%d)\n", __func__, chip->type_c_param.attach_state, tcpc->typec_polarity); + pr_info("%s attach_new=%d,attach_old=%d\n", __func__, tcpc->typec_attach_new, tcpc->typec_attach_old); + + switch (chip->type_c_param.attach_state) { + case CABLE_STATE_NOT_ATTACHED: + tcpc->typec_attach_new = TYPEC_UNATTACHED; + if (tcpc->typec_attach_old == TYPEC_ATTACHED_SRC) { + tcpci_source_vbus(tcpc, TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SOURCE_0V, 0); + } + tcpci_notify_typec_state(tcpc); + tcpc->typec_attach_old = TYPEC_UNATTACHED; + + val = SET_MODE_SELECT_DRP << SET_MODE_SELECT_SHIFT; + if (sgm7220_reg_update(chip->client, REG_SET, val, SET_MODE_SELECT) < 0) + pr_err("%s: force drp mode fail!\n", __func__); + break; + case CABLE_STATE_AS_DFP: + if (tcpc->typec_attach_new != TYPEC_ATTACHED_SRC) { + tcpc->typec_attach_new = TYPEC_ATTACHED_SRC; + tcpc->typec_local_rp_level = sgm7220_local_rp_level(chip); + tcpci_source_vbus(tcpc, TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SOURCE_5V, 0); + tcpci_notify_typec_state(tcpc); + tcpc->typec_attach_old = TYPEC_ATTACHED_SRC; + } + break; + case CABLE_STATE_AS_UFP: + if (tcpc->typec_attach_new != TYPEC_ATTACHED_SNK) { + tcpc->typec_attach_new = TYPEC_ATTACHED_SNK; + tcpc->typec_remote_rp_level = sgm7220_remote_rp_level(chip); + tcpci_notify_typec_state(tcpc); + tcpc->typec_attach_old = TYPEC_ATTACHED_SNK; + } + break; + case CABLE_STATE_TO_ACCESSORY: + if (chip->type_c_param.accessory_attach == ACCESSORY_AUDIO + && tcpc->typec_attach_new != TYPEC_ATTACHED_AUDIO) { + tcpc->typec_attach_new = TYPEC_ATTACHED_AUDIO; + tcpci_notify_typec_state(tcpc); + tcpc->typec_attach_old = TYPEC_ATTACHED_AUDIO; + } + break; + + default: + pr_err("%s: Unknwon type[0x%02x]\n", __func__, chip->type_c_param.attach_state); + break; + } +} + +static int first_check_flag = 0; +static void sgm7220_irq_work_handler(struct kthread_work *work) +{ + struct sgm7220_chip *chip = container_of(work, struct sgm7220_chip, irq_work); + struct tcpc_device *tcpc; + u8 reg_val; + int ret; + + if (0 == first_check_flag) + return; + + tcpc = chip->tcpc; + tcpci_lock_typec(tcpc); + + pr_info("%s enter\n", __func__); + + ret = sgm7220_read_device(chip->client, REG_MOD, 1, ®_val); + if (ret < 0) { + pr_err("%s: read reg fail!\n", __func__); + } + + pr_info("%s mode_register:0x%x\n", __func__, reg_val); + sgm7220_process_mode_register(chip, reg_val); + + ret = sgm7220_read_device(chip->client, REG_INT, 1, ®_val); + if (ret < 0) { + pr_err("%s: read reg fail!\n", __func__); + } + + pr_info("%s interrupt_register:0x%x\n", __func__, reg_val); + sgm7220_process_interrupt_register(chip, reg_val); + + ret = sgm7220_reg_update(chip->client, REG_INT, (0x1 << INT_INTERRUPT_STATUS_SHIFT), INT_INTERRUPT_STATUS); + if (ret < 0) { + pr_err("%s: update reg fail!\n", __func__); + } + + + tcpci_unlock_typec(tcpc); +} + + +static irqreturn_t sgm7220_intr_handler(int irq, void *data) +{ + struct sgm7220_chip *chip = data; + + __pm_wakeup_event(chip->irq_wake_lock, SGM7220_IRQ_WAKE_TIME); + + kthread_queue_work(&chip->irq_worker, &chip->irq_work); + return IRQ_HANDLED; +} + +static int sgm7220_init_alert(struct tcpc_device *tcpc) +{ + struct sgm7220_chip *chip = tcpc_get_dev_data(tcpc); + struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 }; + int ret; + char *name; + int len; + + len = strlen(chip->tcpc_desc->name); + name = devm_kzalloc(chip->dev, len+5, GFP_KERNEL); + if (!name) + return -ENOMEM; + + snprintf (name, PAGE_SIZE, "%s-IRQ", chip->tcpc_desc->name); + + pr_info("%s name = %s, gpio = %d\n", __func__, + chip->tcpc_desc->name, chip->irq_gpio); + + ret = devm_gpio_request(chip->dev, chip->irq_gpio, name); + if (ret < 0) { + pr_err("Error: failed to request GPIO%d (ret = %d)\n", + chip->irq_gpio, ret); + goto init_alert_err; + } + + ret = gpio_direction_input(chip->irq_gpio); + if (ret < 0) { + pr_err("Error: failed to set GPIO%d as input pin(ret = %d)\n", + chip->irq_gpio, ret); + goto init_alert_err; + } + + chip->irq = gpio_to_irq(chip->irq_gpio); + if (chip->irq <= 0) { + pr_err("%s gpio to irq fail, chip->irq(%d)\n", + __func__, chip->irq); + goto init_alert_err; + } + + pr_info("%s : IRQ number = %d\n", __func__, chip->irq); + + kthread_init_worker(&chip->irq_worker); + chip->irq_worker_task = kthread_run(kthread_worker_fn, + &chip->irq_worker, "%s", "chip->tcpc_desc->name"); + if (IS_ERR(chip->irq_worker_task)) { + pr_err("Error: Could not create tcpc task\n"); + goto init_alert_err; + } + + sched_setscheduler(chip->irq_worker_task, SCHED_FIFO, ¶m); + kthread_init_work(&chip->irq_work, sgm7220_irq_work_handler); + ret = request_irq(chip->irq, sgm7220_intr_handler, + IRQF_TRIGGER_FALLING | IRQF_NO_THREAD | + IRQF_NO_SUSPEND, name, chip); + if (ret < 0) { + pr_err("Error: failed to request irq%d (gpio = %d, ret = %d)\n", + chip->irq, chip->irq_gpio, ret); + goto init_alert_err; + } + + enable_irq_wake(chip->irq); + return 0; +init_alert_err: + return -EINVAL; +} + +int sgm7220_alert_status_clear(struct tcpc_device *tcpc, uint32_t mask) +{ + pr_info("%s enter \n", __func__); + return 0; +} + +static int sgm7220_tcpc_init(struct tcpc_device *tcpc, bool sw_reset) +{ + pr_info("%s enter \n", __func__); + return 0; +} + +int sgm7220_fault_status_clear(struct tcpc_device *tcpc, uint8_t status) +{ + pr_info("%s enter \n", __func__); + return 0; +} + +int sgm7220_get_alert_mask(struct tcpc_device *tcpc, uint32_t *mask) +{ + pr_info("%s enter \n", __func__); + return 0; +} + +int sgm7220_get_alert_status(struct tcpc_device *tcpc, uint32_t *alert) +{ + pr_info("%s enter \n", __func__); + return 0; +} + +static int sgm7220_get_power_status(struct tcpc_device *tcpc, uint16_t *pwr_status) +{ + pr_info("%s enter \n", __func__); + *pwr_status = 0; + return 0; +} + +int sgm7220_get_fault_status(struct tcpc_device *tcpc, uint8_t *status) +{ + pr_info("%s enter \n", __func__); + return 0; +} + +static int sgm7220_get_cc(struct tcpc_device *tcpc, int *cc1, int *cc2) +{ + pr_info("%s enter \n", __func__); + return 0; +} + +static int sgm7220_set_cc(struct tcpc_device *tcpc, int pull) +{ + pr_info("%s enter \n", __func__); + return 0; +} + +static int sgm7220_set_polarity(struct tcpc_device *tcpc, int polarity) +{ + pr_info("%s enter \n", __func__); + return 0; +} + +static int sgm7220_set_low_rp_duty(struct tcpc_device *tcpc, bool low_rp) +{ + pr_info("%s enter \n", __func__); + return 0; +} + +static int sgm7220_set_vconn(struct tcpc_device *tcpc, int enable) +{ + pr_info("%s enter \n", __func__); + return 0; +} + +static int sgm7220_tcpc_deinit(struct tcpc_device *tcpc_dev) +{ + pr_info("%s enter \n", __func__); + return 0; +} + +int sgm7220_set_port_type(struct tcpc_device *tcpc, int mode) +{ + struct sgm7220_chip *chip = tcpc_get_dev_data(tcpc); + u8 value; + int ret; + + ret = sgm7220_read_device(chip->client, REG_SET, 1, &value); + if (ret < 0) { + pr_err("%s: read REG_SET fail!\n", __func__); + return ret; + } + value = (value & SET_MODE_SELECT) >> SET_MODE_SELECT_SHIFT; + pr_info("%s enter: mode = %d, now mode = %d\n", + __func__, mode, value); + + if ((mode == TYPEC_PORT_SRC && value == SET_MODE_SELECT_SRC) + ||(mode == TYPEC_PORT_SNK && value == SET_MODE_SELECT_SNK) + ||(mode == TYPEC_PORT_DRP && value == SET_MODE_SELECT_DRP)) { + pr_err("%s: alreadly at mode %d so quit\n", __func__, mode); + return 0; + } + + ret = sgm7220_reg_update(chip->client, REG_SET, 0x01, SET_I2C_DISABLE_TERM); + if (ret < 0) { + pr_err("%s: Disable RD and RP fail!\n", __func__); + return ret; + } + + if (mode == TYPEC_PORT_SRC) + value = SET_MODE_SELECT_SRC; + else if (mode == TYPEC_PORT_SNK) + value = SET_MODE_SELECT_SNK; + else if (mode == TYPEC_PORT_DRP) + value = SET_MODE_SELECT_DRP; + else + return -EINVAL; + value = value << SET_MODE_SELECT_SHIFT; + ret = sgm7220_reg_update(chip->client, REG_SET, value, SET_MODE_SELECT); + if (ret < 0) { + pr_err("%s: update mode reg fail!\n", __func__); + return ret; + } + msleep(20); + + ret = sgm7220_reg_update(chip->client, REG_SET, 0x00, SET_I2C_DISABLE_TERM); + if (ret < 0) { + pr_err("%s: Enable RD and RP fail!\n", __func__); + return ret; + } + + return ret; +} + +#ifdef CONFIG_TCPC_WATCHDOG_EN +int sgm7220_set_watchdog(struct tcpc_device *tcpc, bool en) +{ + return 0; +} +#endif /* CONFIG_TCPC_WATCHDOG_EN */ + +#ifdef CONFIG_TCPC_INTRST_EN +int sgm7220_set_intrst(struct tcpc_device *tcpc, bool en) +{ + return 0; +} +#endif + +#ifdef CONFIG_USB_POWER_DELIVERY +static int sgm7220_set_msg_header( + struct tcpc_device *tcpc, uint8_t power_role, uint8_t data_role) +{ + return 0; +} + +static int sgm7220_protocol_reset(struct tcpc_device *tcpc) +{ + return 0; +} + +static int sgm7220_set_rx_enable(struct tcpc_device *tcpc, uint8_t enable) +{ + return 0; +} + +static int sgm7220_get_message(struct tcpc_device *tcpc, uint32_t *payload, + uint16_t *msg_head, enum tcpm_transmit_type *frame_type) +{ + return 0; +} + +static int sgm7220_set_bist_carrier_mode( + struct tcpc_device *tcpc, uint8_t pattern) +{ + /* Don't support this function */ + return 0; +} + +#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD +static int sgm7220_retransmit(struct tcpc_device *tcpc) +{ + return 0; +} +#endif +static int sgm7220_transmit(struct tcpc_device *tcpc, + enum tcpm_transmit_type type, uint16_t header, const uint32_t *data) +{ + return 0; +} + +static int sgm7220_set_bist_test_mode(struct tcpc_device *tcpc, bool en) +{ + return 0; +} +#endif /* CONFIG_USB_POWER_DELIVERY */ + +static struct tcpc_ops sgm7220_tcpc_ops = { + .init = sgm7220_tcpc_init, + .alert_status_clear = sgm7220_alert_status_clear, + .fault_status_clear = sgm7220_fault_status_clear, + .get_alert_mask = sgm7220_get_alert_mask, + .get_alert_status = sgm7220_get_alert_status, + .get_power_status = sgm7220_get_power_status, + .get_fault_status = sgm7220_get_fault_status, + .get_cc = sgm7220_get_cc, + .set_cc = sgm7220_set_cc, + .set_polarity = sgm7220_set_polarity, + .set_low_rp_duty = sgm7220_set_low_rp_duty, + .set_vconn = sgm7220_set_vconn, + .deinit = sgm7220_tcpc_deinit, + .set_port_type = sgm7220_set_port_type, +#ifdef CONFIG_TCPC_WATCHDOG_EN + .set_watchdog = sgm7220_set_watchdog, +#endif /* CONFIG_TCPC_WATCHDOG_EN */ + +#ifdef CONFIG_TCPC_INTRST_EN + .set_intrst = sgm7220set_intrst, +#endif + +#ifdef CONFIG_USB_POWER_DELIVERY + .set_msg_header = sgm7220_set_msg_header, + .set_rx_enable = sgm7220_set_rx_enable, + .protocol_reset = sgm7220_protocol_reset, + .get_message = sgm7220_get_message, + .transmit = sgm7220_transmit, + .set_bist_test_mode = sgm7220_set_bist_test_mode, + .set_bist_carrier_mode = sgm7220_set_bist_carrier_mode, +#endif /* CONFIG_USB_POWER_DELIVERY */ +#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD + .retransmit = sgm7220_retransmit, +#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */ +}; + + +static int sgm_parse_dt(struct sgm7220_chip *chip, struct device *dev) +{ + struct device_node *np = dev->of_node; + int ret; + + if (!np) + return -EINVAL; + + pr_info("%s\n", __func__); + +#if (!defined(CONFIG_MTK_GPIO) || defined(CONFIG_MTK_GPIOLIB_STAND)) + ret = of_get_named_gpio(np, "sgm7220,intr_gpio", 0); + if (ret < 0) { + pr_err("%s no intr_gpio info\n", __func__); + return ret; + } + chip->irq_gpio = ret; +#else + ret = of_property_read_u32( + np, "sgm7220,intr_gpio_num", &chip->irq_gpio); + if (ret < 0) + pr_err("%s no intr_gpio info\n", __func__); +#endif + return ret; +} + +static void sgm7220_first_check_typec_work(struct work_struct *work) +{ + struct sgm7220_chip *chip = container_of(work, struct sgm7220_chip, first_check_typec_work.work); + struct tcpc_device *tcpc; + u8 reg_val; + int ret; + + tcpc = chip->tcpc; + tcpci_lock_typec(tcpc); + + first_check_flag = 1; + + pr_info("%s enter\n", __func__); + + ret = sgm7220_read_device(chip->client, REG_MOD, 1, ®_val); + if (ret < 0) { + pr_err("%s: read reg fail!\n", __func__); + } + + pr_info("%s mode_register:0x%x\n", __func__, reg_val); + sgm7220_process_mode_register(chip, reg_val); + + ret = sgm7220_read_device(chip->client, REG_INT, 1, ®_val); + if (ret < 0) { + pr_err("%s: read reg fail!\n", __func__); + } + + pr_info("%s interrupt_register:0x%x\n", __func__, reg_val); + sgm7220_process_interrupt_register(chip, reg_val); + + ret = sgm7220_reg_update(chip->client, + REG_INT, (0x1 << INT_INTERRUPT_STATUS_SHIFT), INT_INTERRUPT_STATUS); + if (ret < 0) { + pr_err("%s: update reg fail!\n", __func__); + } + + tcpci_unlock_typec(tcpc); +} + +static int sgm7220_tcpcdev_init(struct sgm7220_chip *chip, struct device *dev) +{ + struct tcpc_desc *desc; + struct device_node *np = dev->of_node; + u32 val, len; + + const char *name = "default"; + if (!np) + return -EINVAL; + + desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + if (of_property_read_u32(np, "sgm7220-tcpc,role_def", &val) >= 0) { + if (val >= TYPEC_ROLE_NR) + desc->role_def = TYPEC_ROLE_DRP; + else + desc->role_def = val; + } else { + dev_info(dev, "use default Role DRP\n"); + desc->role_def = TYPEC_ROLE_DRP; + } + + if (of_property_read_u32( + np, "sgm7220-tcpc,notifier_supply_num", &val) >= 0) { + if (val < 0) + desc->notifier_supply_num = 0; + else + desc->notifier_supply_num = val; + } else + desc->notifier_supply_num = 0; + + if (of_property_read_u32(np, "sgm7220-tcpc,rp_level", &val) >= 0) { + switch (val) { + case 0: /* RP Default */ + desc->rp_lvl = TYPEC_CC_RP_DFT; + break; + case 1: /* RP 1.5V */ + desc->rp_lvl = TYPEC_CC_RP_1_5; + break; + case 2: /* RP 3.0V */ + desc->rp_lvl = TYPEC_CC_RP_3_0; + break; + default: + break; + } + } + +#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE + if (of_property_read_u32(np, "sgm7220-tcpc,vconn_supply", &val) >= 0) { + if (val >= TCPC_VCONN_SUPPLY_NR) + desc->vconn_supply = TCPC_VCONN_SUPPLY_NEVER; + else + desc->vconn_supply = val; + } else { + dev_info(dev, "use never Vconn Supply\n"); + desc->vconn_supply = TCPC_VCONN_SUPPLY_NEVER; + } +#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */ + + of_property_read_string(np, "sgm7220-tcpc,name", (char const **)&name); + + len = strlen(name); + desc->name = kzalloc(len+1, GFP_KERNEL); + if (!desc->name) + return -ENOMEM; + + strncpy((char *)desc->name, name, strlen(name)+1); + + chip->tcpc_desc = desc; + chip->tcpc = tcpc_device_register(dev, + desc, &sgm7220_tcpc_ops, chip); + if (IS_ERR(chip->tcpc)) + return -EINVAL; + + schedule_delayed_work(&chip->first_check_typec_work, msecs_to_jiffies(3000)); + pr_info("%s end\n", __func__); + return 0; +} + +static int sgm7220_initialization(struct sgm7220_chip *chip) +{ + int ret = 0; + u8 reg_val; + /* do initialization here, before enable irq, + * clear irq, + * config DRP/UFP/DFP mode, + * and etc.. + */ + pr_info("%s enter\n", __func__); + ret = sgm7220_read_device(chip->client, REG_REVISION, 1, ®_val); + if (ret < 0) { + return ret; + } + + /* 1 Disable term(default 0) [bit 0] + * 2 Source pref(DRP performs try.SNK 01) [bit 2:1] + * 3 Soft reset(default 0) [bit 3] + * 4 Mode slect(DRP start from Try.SNK 00) [bit 5:4] + * 5 Debounce time of voltage change on CC pin(default 0) [bit 7:6] + */ + reg_val = 0x32; + ret = sgm7220_reg_write(chip->client, REG_SET, reg_val); + if (ret < 0) { + pr_err("%s: init REG_SET fail!\n", __func__); + return ret; + } + + /* CURRENT MODE ADVERTISE 3A [bit 7:6] */ + reg_val = (HOST_CUR_1P5 << MOD_CURRENT_MODE_ADVERTISE_SHIFT); + ret = sgm7220_reg_update(chip->client, REG_MOD, reg_val, MOD_CURRENT_MODE_ADVERTISE); + if (ret < 0) { + pr_err("%s: init REG_MOD fail!\n", __func__); + return ret; + } + + return ret; +} + +/************************************************************************ + * + * chip_info_show + * + ************************************************************************/ +static ssize_t chip_info_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sgm7220_chip *chip = i2c_get_clientdata(client); + u8 reg_val; + char name[ID_REG_LEN + 1] = { 0 }; + u8 revision = 0; + int i, j, ret; + + for (i = 0, j = 0; i < ID_REG_LEN; i++) { + reg_val = 0; + ret = sgm7220_read_device(chip->client, (REG_BASE + (ID_REG_LEN - 1) - i), 1, ®_val); + if (ret < 0) { + pr_err("%s: read reg fail!\n", __func__); + snprintf(name, ID_REG_LEN, "%s", "Error"); + break; + } + if (!reg_val) { + j++; + continue; + } + name[i - j] = (char)reg_val; + } + + ret = sgm7220_read_device(chip->client, REG_REVISION, 1, &revision); + if (ret < 0) { + pr_err("%s: read reg fail!\n", __func__); + return snprintf(buf, PAGE_SIZE, "USB TypeC chip info:\n" + " Manufacture: SGM\n" + " Chip Name : %s\n" + " Revision ID : Error\n", + name); + } else { + return snprintf(buf, PAGE_SIZE, "USB TypeC chip info:\n" + " Manufacture: SGM\n" + " Chip Name : %s\n" + " Revision ID : %d\n", + name, revision); + } +} +DEVICE_ATTR(chip_info, S_IRUGO, chip_info_show, NULL); + +/************************************************************************ + * + * cc_orientation_show + * + ************************************************************************/ +static ssize_t cc_orientation_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sgm7220_chip *chip = i2c_get_clientdata(client); + int ret; + + ret = snprintf (buf, PAGE_SIZE, "cc_sts (%d)\n", chip->type_c_param.cable_dir); + + return ret; +} +DEVICE_ATTR(cc_orientation, S_IRUGO, cc_orientation_show, NULL); + +static int sgm7220_check_revision(struct i2c_client *client) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, REG_REVISION); + if (ret < 0) { + dev_err(&client->dev, "read chip ID fail\n"); + return -EIO; + } + + return ret; +} + +static int sgm7220_pwr_on(struct device *dev) +{ + struct device_node *np = dev->of_node; + int pwr_en_gpio; + int ret; + + pwr_en_gpio = of_get_named_gpio(np, "sgm7220,pwr_gpio", 0); + if (pwr_en_gpio < 0) { + pr_err("%s no sgm7220,pwr_gpio info\n", __func__); + return -EINVAL; + } + + if (!gpio_is_valid(pwr_en_gpio)) { + pr_err("%s pwr gpio is invalid\n", __func__); + return -EINVAL; + } + + ret = devm_gpio_request(dev, pwr_en_gpio, "sgm7220-pwr-gpio"); + if (ret < 0) { + pr_err("%s failed to request GPIO%d (ret = %d)\n", __func__, pwr_en_gpio, ret); + return -EINVAL; + } + + ret = gpio_direction_output(pwr_en_gpio, 1); + if (ret < 0) { + pr_err("%s: failed to set GPIO%d as out high(ret = %d)\n", + __func__, pwr_en_gpio, ret); + return -EINVAL; + } + + return 0; +} + +static int sgm7220_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct sgm7220_chip *chip; + int ret = 0, chip_id = 0; + bool use_dt = client->dev.of_node; + + pr_info("%s\n", __func__); + if (i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_BYTE_DATA)) + pr_info("I2C functionality : OK...\n"); + else + pr_info("I2C functionality check : failuare...\n"); + + sgm7220_pwr_on(&client->dev); + chip_id = sgm7220_check_revision(client); + if (chip_id < 0) + return chip_id; + + chip = devm_kzalloc(&client->dev, sizeof (*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + if (use_dt) + sgm_parse_dt(chip, &client->dev); + else { + dev_err(&client->dev, "no dts node\n"); + return -ENODEV; + } + chip->dev = &client->dev; + chip->client = client; + sema_init(&chip->io_lock, 1); + sema_init(&chip->suspend_lock, 1); + i2c_set_clientdata(client, chip); + w_client = chip->client; + INIT_DELAYED_WORK(&chip->first_check_typec_work, sgm7220_first_check_typec_work); + chip->irq_wake_lock = + wakeup_source_register(chip->dev, "sgm7220_irq_wakelock"); + chip->i2c_wake_lock = + wakeup_source_register(chip->dev, "sgm7220_i2c_wakelock"); + + chip->chip_id = chip_id; + pr_info("sgm7220_chipID = 0x%0x\n", chip_id); + + /* try initialize device before request irq */ + ret = sgm7220_initialization(chip); + if (ret < 0) { + pr_err("%s: fails to do initialization %d\n", __func__, ret); + goto err_init_dev; + } + + ret = sgm7220_tcpcdev_init(chip, &client->dev); + if (ret < 0) { + dev_err(&client->dev, "sgm7220 tcpc dev init fail\n"); + goto err_tcpc_reg; + } + + ret = sgm7220_init_alert(chip->tcpc); + if (ret < 0) { + pr_err("sgm7220 init alert fail\n"); + goto err_irq_init; + } + + ret = device_create_file(&client->dev, &dev_attr_chip_info); + if (ret < 0) { + dev_err(&client->dev, "failed to create dev_attr_chip_info\n"); + ret = -ENODEV; + goto err_create_chip_info_file; + } + + ret = device_create_file(&client->dev, &dev_attr_cc_orientation); + if (ret < 0) { + dev_err(&client->dev, "failed to create dev_attr_cc_orientation\n"); + ret = -ENODEV; + goto err_create_cc_orientation_file; + } + + pr_info("%s probe OK!\n", __func__); + return 0; +err_create_cc_orientation_file: + device_remove_file(&client->dev, &dev_attr_cc_orientation); +err_create_chip_info_file: + device_remove_file(&client->dev, &dev_attr_chip_info); +err_irq_init: + tcpc_device_unregister(chip->dev, chip->tcpc); +err_tcpc_reg: + wakeup_source_unregister(chip->i2c_wake_lock); + wakeup_source_unregister(chip->irq_wake_lock); +err_init_dev: + i2c_set_clientdata(client, NULL); + + return ret; +} + +static int sgm7220_i2c_remove(struct i2c_client *client) +{ + struct sgm7220_chip *chip = i2c_get_clientdata(client); + + if (chip) { + cancel_delayed_work_sync(&chip->first_check_typec_work); + tcpc_device_unregister(chip->dev, chip->tcpc); + device_remove_file(&client->dev, &dev_attr_cc_orientation); + device_remove_file(&client->dev, &dev_attr_chip_info); + } + + return 0; +} + +#ifdef CONFIG_PM +static int sgm7220_i2c_suspend(struct device *dev) +{ + struct sgm7220_chip *chip; + struct i2c_client *client = to_i2c_client(dev); + + if (client) { + chip = i2c_get_clientdata(client); + if (chip) + down(&chip->suspend_lock); + } + + return 0; +} + +static int sgm7220_i2c_resume(struct device *dev) +{ + struct sgm7220_chip *chip; + struct i2c_client *client = to_i2c_client(dev); + + if (client) { + chip = i2c_get_clientdata(client); + if (chip) + up(&chip->suspend_lock); + } + + return 0; +} + +static void sgm7220_shutdown(struct i2c_client *client) +{ + struct sgm7220_chip *chip = i2c_get_clientdata(client); + + if (chip != NULL) { + if (chip->irq) + disable_irq(chip->irq); + } +} + +#ifdef CONFIG_PM_RUNTIME +static int sgm7220_pm_suspend_runtime(struct device *device) +{ + dev_dbg(device, "pm_runtime: suspending...\n"); + return 0; +} + +static int sgm7220_pm_resume_runtime(struct device *device) +{ + dev_dbg(device, "pm_runtime: resuming...\n"); + return 0; +} +#endif /* CONFIG_PM_RUNTIME */ + + +static const struct dev_pm_ops sgm7220_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS( + sgm7220_i2c_suspend, + sgm7220_i2c_resume) +#ifdef CONFIG_PM_RUNTIME + SET_RUNTIME_PM_OPS( + sgm7220_pm_suspend_runtime, + sgm7220_pm_resume_runtime, + NULL + ) +#endif /* CONFIG_PM_RUNTIME */ +}; +#define SGM7220_PM_OPS (&sgm7220_pm_ops) +#else +#define SGM7220_PM_OPS (NULL) +#endif /* CONFIG_PM */ + +static const struct i2c_device_id sgm7220_id_table[] = { + {"sgm7220", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, sgm7220_id_table); + +static const struct of_device_id sgm_match_table[] = { + {.compatible = "sgm,usb_type_c_sgm7220",}, + {}, +}; + +static struct i2c_driver sgm7220_driver = { + .driver = { + .name = "usb_type_c_sgm7220", + .owner = THIS_MODULE, + .of_match_table = sgm_match_table, + .pm = SGM7220_PM_OPS, + }, + .probe = sgm7220_i2c_probe, + .remove = sgm7220_i2c_remove, + .shutdown = sgm7220_shutdown, + .id_table = sgm7220_id_table, +}; + +module_i2c_driver(sgm7220_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("SGM7220 TCPC Driver"); +MODULE_VERSION(SGM7220_DRV_VERSION); diff --git a/drivers/usb/typec/mmi_tcpc/tcpci.c b/drivers/usb/typec/mmi_tcpc/tcpci.c new file mode 100644 index 000000000000..8e5995c443c1 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/tcpci.c @@ -0,0 +1,1020 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * 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. + */ + +#include "inc/tcpci.h" +#include +#include + +#define TCPC_NOTIFY_OVERTIME (20) /* ms */ + +#ifdef CONFIG_TCPC_NOTIFICATION_NON_BLOCKING +struct tcp_notify_work { + struct work_struct work; + struct tcpc_device *tcpc; + struct tcp_notify tcp_noti; + uint8_t type; + uint8_t state; +}; + +static void tcp_notify_func(struct work_struct *work) +{ + struct tcp_notify_work *tn_work = + container_of(work, struct tcp_notify_work, work); + struct tcpc_device *tcpc = tn_work->tcpc; + struct tcp_notify *tcp_noti = &tn_work->tcp_noti; + uint8_t type = tn_work->type; + uint8_t state = tn_work->state; +#ifdef CONFIG_PD_BEGUG_ON + struct timeval begin, end; + int timeval = 0; + + do_gettimeofday(&begin); + srcu_notifier_call_chain(&tcpc->evt_nh[type], state, tcp_noti); + do_gettimeofday(&end); + timeval = (timeval_to_ns(end) - timeval_to_ns(begin))/1000/1000; + PD_BUG_ON(timeval > TCPC_NOTIFY_OVERTIME); +#else + srcu_notifier_call_chain(&tcpc->evt_nh[type], state, tcp_noti); +#endif + + kfree(tn_work); +} + +static int tcpc_check_notify_time(struct tcpc_device *tcpc, + struct tcp_notify *tcp_noti, uint8_t type, uint8_t state) +{ + struct tcp_notify_work *tn_work; + + tn_work = kzalloc(sizeof(*tn_work), GFP_KERNEL); + if (!tn_work) + return -ENOMEM; + + INIT_WORK(&tn_work->work, tcp_notify_func); + tn_work->tcpc = tcpc; + tn_work->tcp_noti = *tcp_noti; + tn_work->type = type; + tn_work->state = state; + + return queue_work(tcpc->evt_wq, &tn_work->work) ? 0 : -EAGAIN; +} +#else +static int tcpc_check_notify_time(struct tcpc_device *tcpc, + struct tcp_notify *tcp_noti, uint8_t type, uint8_t state) +{ + int ret; +#ifdef CONFIG_PD_BEGUG_ON + struct timeval begin, end; + int timeval = 0; + + do_gettimeofday(&begin); + ret = srcu_notifier_call_chain(&tcpc->evt_nh[type], state, tcp_noti); + do_gettimeofday(&end); + timeval = (timeval_to_ns(end) - timeval_to_ns(begin))/1000/1000; + PD_BUG_ON(timeval > TCPC_NOTIFY_OVERTIME); +#else + ret = srcu_notifier_call_chain(&tcpc->evt_nh[type], state, tcp_noti); +#endif + return ret; +} +#endif /* CONFIG_TCPC_NOTIFICATION_BLOCKING */ + +int tcpci_check_vbus_valid_from_ic(struct tcpc_device *tcpc) +{ + uint16_t power_status; + int vbus_level = tcpc->vbus_level; + + if (tcpci_get_power_status(tcpc, &power_status) == 0) { + if (vbus_level != tcpc->vbus_level) { + TCPC_INFO("[Warning] ps_changed %d -> %d\n", + vbus_level, tcpc->vbus_level); + } + } + + return tcpci_check_vbus_valid(tcpc); +} + +int tcpci_check_vsafe0v( + struct tcpc_device *tcpc, bool detect_en) +{ + int ret = 0; + +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC + ret = (tcpc->vbus_level == TCPC_VBUS_SAFE0V); +#else + ret = (tcpc->vbus_level == TCPC_VBUS_INVALID); +#endif + + return ret; +} + +int tcpci_get_chip_id(struct tcpc_device *tcpc, uint32_t *chip_id) +{ + if (tcpc->ops->get_chip_id == NULL) + return -ENOTSUPP; + else + tcpc->ops->get_chip_id(tcpc, chip_id); + + return 0; +} + +int tcpci_alert_status_clear( + struct tcpc_device *tcpc, uint32_t mask) +{ + PD_BUG_ON(tcpc->ops->alert_status_clear == NULL); + + return tcpc->ops->alert_status_clear(tcpc, mask); +} +EXPORT_SYMBOL(tcpci_alert_status_clear); + +int tcpci_fault_status_clear( + struct tcpc_device *tcpc, uint8_t status) +{ + PD_BUG_ON(tcpc->ops->fault_status_clear == NULL); + + return tcpc->ops->fault_status_clear(tcpc, status); +} + +int tcpci_set_alert_mask(struct tcpc_device *tcpc, uint32_t mask) +{ + int rv = 0; + + if (tcpc->ops->set_alert_mask) + return tcpc->ops->set_alert_mask(tcpc, mask); + + return rv; +} + +int tcpci_get_alert_mask( + struct tcpc_device *tcpc, uint32_t *mask) +{ + PD_BUG_ON(tcpc->ops->get_alert_mask == NULL); + + return tcpc->ops->get_alert_mask(tcpc, mask); +} + +int tcpci_get_alert_status( + struct tcpc_device *tcpc, uint32_t *alert) +{ + PD_BUG_ON(tcpc->ops->get_alert_status == NULL); + + return tcpc->ops->get_alert_status(tcpc, alert); +} + +int tcpci_get_fault_status( + struct tcpc_device *tcpc, uint8_t *fault) +{ + if (tcpc->ops->get_fault_status) + return tcpc->ops->get_fault_status(tcpc, fault); + + *fault = 0; + return 0; +} + +int tcpci_get_power_status( + struct tcpc_device *tcpc, uint16_t *pw_status) +{ + int ret; + + PD_BUG_ON(tcpc->ops->get_power_status == NULL); + + ret = tcpc->ops->get_power_status(tcpc, pw_status); + if (ret < 0) + return ret; + + tcpci_vbus_level_init(tcpc, *pw_status); + return 0; +} + +int tcpci_init(struct tcpc_device *tcpc, bool sw_reset) +{ + int ret; + uint16_t power_status; + + PD_BUG_ON(tcpc->ops->init == NULL); + + ret = tcpc->ops->init(tcpc, sw_reset); + if (ret < 0) + return ret; + + return tcpci_get_power_status(tcpc, &power_status); +} + +int tcpci_init_alert_mask(struct tcpc_device *tcpc) +{ + if (tcpc->ops->init_alert_mask) + return tcpc->ops->init_alert_mask(tcpc); + return 0; +} + +int tcpci_get_cc(struct tcpc_device *tcpc) +{ + int ret; + int cc1, cc2; + + PD_BUG_ON(tcpc->ops->get_cc == NULL); + + ret = tcpc->ops->get_cc(tcpc, &cc1, &cc2); + if (ret < 0) + return ret; + + if ((cc1 == tcpc->typec_remote_cc[0]) && + (cc2 == tcpc->typec_remote_cc[1])) { + return 0; + } + + tcpc->typec_remote_cc[0] = cc1; + tcpc->typec_remote_cc[1] = cc2; + + return 1; +} + +int tcpci_set_cc(struct tcpc_device *tcpc, int pull) +{ +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE +#ifdef CONFIG_TYPEC_LEGACY3_ALWAYS_LOCAL_RP + uint8_t rp_lvl = TYPEC_RP_DFT, res = TYPEC_CC_DRP; +#endif /* CONFIG_TYPEC_LEGACY3_ALWAYS_LOCAL_RP */ +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */ + +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE + if (pull == TYPEC_CC_DRP && tcpc->typec_legacy_cable) { +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE2 + if (tcpc->typec_legacy_cable == 2) + pull = TYPEC_CC_RP; + else if (tcpc->typec_legacy_retry_wk > 1) + pull = TYPEC_CC_RP_3_0; + else +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE2 */ + pull = TYPEC_CC_RP_1_5; + TCPC_DBG2("LC->Toggling (%d)\n", pull); + } else if (!tcpc->typec_legacy_cable) { +#ifdef CONFIG_TYPEC_LEGACY3_ALWAYS_LOCAL_RP + rp_lvl = TYPEC_CC_PULL_GET_RP_LVL(pull); + res = TYPEC_CC_PULL_GET_RES(pull); + pull = TYPEC_CC_PULL(rp_lvl == TYPEC_RP_DFT ? + tcpc->typec_local_rp_level : rp_lvl, res); +#endif /* CONFIG_TYPEC_LEGACY3_ALWAYS_LOCAL_RP */ + } +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */ + + return __tcpci_set_cc(tcpc, pull); +} + +int tcpci_set_polarity(struct tcpc_device *tcpc, int polarity) +{ + PD_BUG_ON(tcpc->ops->set_polarity == NULL); + + return tcpc->ops->set_polarity(tcpc, polarity); +} + +int tcpci_set_port_type(struct tcpc_device *tcpc, int mode) +{ + if (tcpc->ops->set_port_type == NULL) + return -ENOTSUPP; + else + tcpc->ops->set_port_type(tcpc, mode); + + return 0; +} + +int tcpci_set_low_rp_duty(struct tcpc_device *tcpc, bool low_rp) +{ +#ifdef CONFIG_TYPEC_CAP_LOW_RP_DUTY + if (low_rp) + TCPC_INFO("low_rp_duty\n"); + + if (tcpc->ops->set_low_rp_duty) + return tcpc->ops->set_low_rp_duty(tcpc, low_rp); +#endif /* CONFIG_TYPEC_CAP_LOW_RP_DUTY */ + + return 0; +} + +int tcpci_set_vconn(struct tcpc_device *tcpc, int enable) +{ +#ifdef CONFIG_TCPC_SOURCE_VCONN + struct tcp_notify tcp_noti; + + if (tcpc->tcpc_source_vconn == enable) + return 0; + + tcpc->tcpc_source_vconn = enable; + + tcp_noti.en_state.en = enable != 0; + tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_VBUS, TCP_NOTIFY_SOURCE_VCONN); + + if (tcpc->ops->set_vconn) + return tcpc->ops->set_vconn(tcpc, enable); +#endif /* CONFIG_TCPC_SOURCE_VCONN */ + + return 0; +} + +int tcpci_is_low_power_mode(struct tcpc_device *tcpc) +{ + int rv = 1; + +#ifdef CONFIG_TCPC_LOW_POWER_MODE + if (tcpc->ops->is_low_power_mode) + rv = tcpc->ops->is_low_power_mode(tcpc); +#endif /* CONFIG_TCPC_LOW_POWER_MODE */ + + return rv; +} + +int tcpci_set_low_power_mode( + struct tcpc_device *tcpc, bool en, int pull) +{ + int rv = 0; + +#ifdef CONFIG_TCPC_LOW_POWER_MODE + if (tcpc->ops->set_low_power_mode) + rv = tcpc->ops->set_low_power_mode(tcpc, en, pull); +#endif /* CONFIG_TCPC_LOW_POWER_MODE */ + + return rv; +} + +int tcpci_set_watchdog(struct tcpc_device *tcpc, bool en) +{ + int rv = 0; + + if (tcpc->tcpc_flags & TCPC_FLAGS_WATCHDOG_EN) + if (tcpc->ops->set_watchdog) + rv = tcpc->ops->set_watchdog(tcpc, en); + + return rv; +} + +int tcpci_alert_vendor_defined_handler(struct tcpc_device *tcpc) +{ + int rv = 0; + + if (tcpc->ops->alert_vendor_defined_handler) + rv = tcpc->ops->alert_vendor_defined_handler(tcpc); + + return rv; +} + +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC +int tcpci_is_vsafe0v(struct tcpc_device *tcpc) +{ + int rv = -ENOTSUPP; + + if (tcpc->ops->is_vsafe0v) + rv = tcpc->ops->is_vsafe0v(tcpc); + + return rv; +} +#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */ + +#ifdef CONFIG_WATER_DETECTION +int tcpci_is_water_detected(struct tcpc_device *tcpc) +{ + if (tcpc->ops->is_water_detected) + return tcpc->ops->is_water_detected(tcpc); + return 0; +} + +int tcpci_set_water_protection(struct tcpc_device *tcpc, bool en) +{ + if (tcpc->ops->set_water_protection) + return tcpc->ops->set_water_protection(tcpc, en); + return 0; +} + +int tcpci_set_usbid_polling(struct tcpc_device *tcpc, bool en) +{ + if (tcpc->ops->set_usbid_polling) + return tcpc->ops->set_usbid_polling(tcpc, en); + return 0; +} + +int tcpci_notify_wd_status(struct tcpc_device *tcpc, bool water_detected) +{ + struct tcp_notify tcp_noti; + + tcp_noti.wd_status.water_detected = water_detected; + return tcpc_check_notify_time(tcpc, &tcp_noti, TCP_NOTIFY_IDX_MISC, + TCP_NOTIFY_WD_STATUS); +} +#endif /* CONFIG_WATER_DETECTION */ + +#ifdef CONFIG_CABLE_TYPE_DETECTION +int tcpci_notify_cable_type(struct tcpc_device *tcpc) +{ + struct tcp_notify tcp_noti; + + tcp_noti.cable_type.type = tcpc->typec_cable_type; + return tcpc_check_notify_time(tcpc, &tcp_noti, TCP_NOTIFY_IDX_MISC, + TCP_NOTIFY_CABLE_TYPE); +} +#endif /* CONFIG_CABLE_TYPE_DETECTION */ + +#ifdef CONFIG_USB_POWER_DELIVERY + +int tcpci_set_msg_header(struct tcpc_device *tcpc, + uint8_t power_role, uint8_t data_role) +{ + PD_BUG_ON(tcpc->ops->set_msg_header == NULL); + + return tcpc->ops->set_msg_header(tcpc, power_role, data_role); +} + +int tcpci_set_rx_enable(struct tcpc_device *tcpc, uint8_t enable) +{ + PD_BUG_ON(tcpc->ops->set_rx_enable == NULL); + + return tcpc->ops->set_rx_enable(tcpc, enable); +} + +int tcpci_protocol_reset(struct tcpc_device *tcpc) +{ + if (tcpc->ops->protocol_reset) + return tcpc->ops->protocol_reset(tcpc); + + return 0; +} + +int tcpci_get_message(struct tcpc_device *tcpc, + uint32_t *payload, uint16_t *head, enum tcpm_transmit_type *type) +{ + PD_BUG_ON(tcpc->ops->get_message == NULL); + + return tcpc->ops->get_message(tcpc, payload, head, type); +} + +int tcpci_transmit(struct tcpc_device *tcpc, + enum tcpm_transmit_type type, uint16_t header, const uint32_t *data) +{ + PD_BUG_ON(tcpc->ops->transmit == NULL); + + return tcpc->ops->transmit(tcpc, type, header, data); +} + +int tcpci_set_bist_test_mode(struct tcpc_device *tcpc, bool en) +{ + if (tcpc->ops->set_bist_test_mode) + return tcpc->ops->set_bist_test_mode(tcpc, en); + + return 0; +} + +int tcpci_set_bist_carrier_mode(struct tcpc_device *tcpc, uint8_t pattern) +{ + PD_BUG_ON(tcpc->ops->set_bist_carrier_mode == NULL); + + if (pattern) /* wait for GoodCRC */ + udelay(240); + + return tcpc->ops->set_bist_carrier_mode(tcpc, pattern); +} + +#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD +int tcpci_retransmit(struct tcpc_device *tcpc) +{ + PD_BUG_ON(tcpc->ops->retransmit == NULL); + + return tcpc->ops->retransmit(tcpc); +} +#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */ +#endif /* CONFIG_USB_POWER_DELIVERY */ + +int tcpci_notify_typec_state(struct tcpc_device *tcpc) +{ + struct tcp_notify tcp_noti; + int ret; + + tcp_noti.typec_state.polarity = tcpc->typec_polarity; + tcp_noti.typec_state.old_state = tcpc->typec_attach_old; + tcp_noti.typec_state.new_state = tcpc->typec_attach_new; + tcp_noti.typec_state.rp_level = tcpc->typec_remote_rp_level; + tcp_noti.typec_state.local_rp_level = tcpc->typec_local_rp_level; + + ret = tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_USB, TCP_NOTIFY_TYPEC_STATE); + return ret; +} +EXPORT_SYMBOL(tcpci_notify_typec_state); + +int tcpci_notify_role_swap( + struct tcpc_device *tcpc, uint8_t event, uint8_t role) +{ + struct tcp_notify tcp_noti; + int ret; + + tcp_noti.swap_state.new_role = role; + ret = tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_MISC, event); + return ret; +} + +int tcpci_notify_pd_state(struct tcpc_device *tcpc, uint8_t connect) +{ + struct tcp_notify tcp_noti; + int ret; + + tcp_noti.pd_state.connected = connect; + ret = tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_USB, TCP_NOTIFY_PD_STATE); + return ret; +} + +int tcpci_notify_pd_vdm_verify(struct tcpc_device *tcpc, uint8_t vdm_verify) +{ + struct tcp_notify tcp_noti; + int ret; + + tcp_noti.pd_state.vdm_verify = vdm_verify; + ret = tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_USB, TCP_NOTIFY_PD_VDM_VERIFY); + return ret; +} + +int tcpci_set_intrst(struct tcpc_device *tcpc, bool en) +{ +#ifdef CONFIG_TCPC_INTRST_EN + if (tcpc->ops->set_intrst) + tcpc->ops->set_intrst(tcpc, en); +#endif /* CONFIG_TCPC_INTRST_EN */ + + return 0; +} + +int tcpci_enable_watchdog(struct tcpc_device *tcpc, bool en) +{ + if (!(tcpc->tcpc_flags & TCPC_FLAGS_WATCHDOG_EN)) + return 0; + + TCPC_DBG2("enable_WG: %d\n", en); + + if (tcpc->typec_watchdog == en) + return 0; + + mutex_lock(&tcpc->access_lock); + tcpc->typec_watchdog = en; + + if (tcpc->ops->set_watchdog) + tcpc->ops->set_watchdog(tcpc, en); + +#ifdef CONFIG_TCPC_INTRST_EN + if (!en || tcpc->attach_wake_lock->active) + tcpci_set_intrst(tcpc, en); +#endif /* CONFIG_TCPC_INTRST_EN */ + + mutex_unlock(&tcpc->access_lock); + + return 0; +} + +int tcpci_source_vbus( + struct tcpc_device *tcpc, uint8_t type, int mv, int ma) +{ + struct tcp_notify tcp_noti; + int ret; + +#ifdef CONFIG_USB_POWER_DELIVERY + if (type >= TCP_VBUS_CTRL_PD && + tcpc->pd_port.pe_data.pd_prev_connected) + type |= TCP_VBUS_CTRL_PD_DETECT; +#endif /* CONFIG_USB_POWER_DELIVERY */ + + if (ma < 0) { + if (mv != 0) { + switch (tcpc->typec_local_rp_level) { + case TYPEC_RP_3_0: + ma = 3000; + break; + case TYPEC_RP_1_5: + ma = 1500; + break; + case TYPEC_RP_DFT: + default: + ma = CONFIG_TYPEC_SRC_CURR_DFT; + break; + } + } else + ma = 0; + } + + tcp_noti.vbus_state.ma = ma; + tcp_noti.vbus_state.mv = mv; + tcp_noti.vbus_state.type = type; + + tcpci_enable_watchdog(tcpc, mv != 0); + TCPC_DBG("source_vbus: %d mV, %d mA\n", mv, ma); + ret = tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_VBUS, TCP_NOTIFY_SOURCE_VBUS); + return ret; +} +EXPORT_SYMBOL(tcpci_source_vbus); + +int tcpci_sink_vbus( + struct tcpc_device *tcpc, uint8_t type, int mv, int ma) +{ + struct tcp_notify tcp_noti; + int ret; + +#ifdef CONFIG_USB_POWER_DELIVERY + if (type >= TCP_VBUS_CTRL_PD && + tcpc->pd_port.pe_data.pd_prev_connected) + type |= TCP_VBUS_CTRL_PD_DETECT; +#endif /* CONFIG_USB_POWER_DELIVERY */ + + if (ma < 0) { + if (mv != 0) { + switch (tcpc->typec_remote_rp_level) { + case TYPEC_CC_VOLT_SNK_1_5: + ma = 1500; + break; + case TYPEC_CC_VOLT_SNK_3_0: + ma = 3000; + break; + default: + case TYPEC_CC_VOLT_SNK_DFT: + ma = tcpc->typec_usb_sink_curr; + break; + } +#if CONFIG_TYPEC_SNK_CURR_LIMIT > 0 + if (ma > CONFIG_TYPEC_SNK_CURR_LIMIT) + ma = CONFIG_TYPEC_SNK_CURR_LIMIT; +#endif /* CONFIG_TYPEC_SNK_CURR_LIMIT */ + } else + ma = 0; + } + + tcp_noti.vbus_state.ma = ma; + tcp_noti.vbus_state.mv = mv; + tcp_noti.vbus_state.type = type; + + TCPC_DBG("sink_vbus: %d mV, %d mA\n", mv, ma); + ret = tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_VBUS, TCP_NOTIFY_SINK_VBUS); + return ret; +} + +int tcpci_disable_vbus_control(struct tcpc_device *tcpc) +{ +#ifdef CONFIG_TYPEC_USE_DIS_VBUS_CTRL + struct tcp_notify tcp_noti; + int ret; + + TCPC_DBG("disable_vbus\n"); + tcpci_enable_watchdog(tcpc, false); + + ret = tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_VBUS, TCP_NOTIFY_DIS_VBUS_CTRL); + return ret; +#else + tcpci_sink_vbus(tcpc, TCP_VBUS_CTRL_REMOVE, TCPC_VBUS_SINK_0V, 0); + tcpci_source_vbus(tcpc, TCP_VBUS_CTRL_REMOVE, TCPC_VBUS_SOURCE_0V, 0); + return 0; +#endif /* CONFIG_TYPEC_USE_DIS_VBUS_CTRL */ +} + +int tcpci_notify_attachwait_state(struct tcpc_device *tcpc, bool as_sink) +{ +#ifdef CONFIG_TYPEC_NOTIFY_ATTACHWAIT + uint8_t notify = 0; + struct tcp_notify tcp_noti; + int ret; + +#ifdef CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SNK + if (as_sink) + notify = TCP_NOTIFY_ATTACHWAIT_SNK; +#endif /* CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SNK */ + +#ifdef CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SRC + if (!as_sink) + notify = TCP_NOTIFY_ATTACHWAIT_SRC; +#endif /* CONFIG_TYPEC_NOTIFY_ATTACHWAIT_SRC */ + + if (notify == 0) + return 0; + + ret = tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_VBUS, notify); + return ret; +#else + return 0; +#endif /* CONFIG_TYPEC_NOTIFY_ATTACHWAIT */ + +} + +int tcpci_enable_auto_discharge(struct tcpc_device *tcpc, bool en) +{ + int ret = 0; + +#ifdef CONFIG_TYPEC_CAP_AUTO_DISCHARGE +#ifdef CONFIG_TCPC_AUTO_DISCHARGE_IC + if (tcpc->typec_auto_discharge != en) { + tcpc->typec_auto_discharge = en; + if (tcpc->ops->set_auto_discharge) + ret = tcpc->ops->set_auto_discharge(tcpc, en); + } +#endif /* CONFIG_TCPC_AUTO_DISCHARGE_IC */ +#endif /* CONFIG_TYPEC_CAP_AUTO_DISCHARGE */ + + return ret; +} + +static int __tcpci_enable_force_discharge( + struct tcpc_device *tcpc, bool en, int mv) +{ + int ret = 0; + +#ifdef CONFIG_TYPEC_CAP_FORCE_DISCHARGE +#ifdef CONFIG_TCPC_FORCE_DISCHARGE_IC + if (tcpc->pd_force_discharge != en) { + tcpc->pd_force_discharge = en; + if (tcpc->ops->set_force_discharge) + ret = tcpc->ops->set_force_discharge(tcpc, en, mv); + } +#endif /* CONFIG_TCPC_FORCE_DISCHARGE_IC */ +#endif /* CONFIG_TYPEC_CAP_FORCE_DISCHARGE */ + + return ret; +} + +static int __tcpci_enable_ext_discharge(struct tcpc_device *tcpc, bool en) +{ + int ret = 0; + +#ifdef CONFIG_TCPC_EXT_DISCHARGE + struct tcp_notify tcp_noti; + + if (tcpc->typec_ext_discharge != en) { + tcpc->typec_ext_discharge = en; + tcp_noti.en_state.en = en; + TCPC_DBG("EXT-Discharge: %d\n", en); + ret = tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_VBUS, TCP_NOTIFY_EXT_DISCHARGE); + } +#endif /* CONFIG_TCPC_EXT_DISCHARGE */ + + return ret; +} + +int tcpci_enable_force_discharge(struct tcpc_device *tcpc, bool en, int mv) +{ + int ret = 0; + +#ifdef CONFIG_TYPEC_CAP_FORCE_DISCHARGE + ret = __tcpci_enable_force_discharge(tcpc, en, mv); +#ifdef CONFIG_TCPC_FORCE_DISCHARGE_EXT + ret = __tcpci_enable_ext_discharge(tcpc, en); +#endif /* CONFIG_TCPC_FORCE_DISCHARGE_EXT */ +#endif /* CONFIG_TYPEC_CAP_FORCE_DISCHARGE */ + + return ret; +} + +#ifdef CONFIG_USB_POWER_DELIVERY + +int tcpci_notify_hard_reset_state(struct tcpc_device *tcpc, uint8_t state) +{ + struct tcp_notify tcp_noti; + int ret; + + tcp_noti.hreset_state.state = state; + + if (state >= TCP_HRESET_SIGNAL_SEND) + tcpc->pd_wait_hard_reset_complete = true; + else if (tcpc->pd_wait_hard_reset_complete) + tcpc->pd_wait_hard_reset_complete = false; + else + return 0; + + ret = tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_MISC, TCP_NOTIFY_HARD_RESET_STATE); + return ret; +} + +int tcpci_enter_mode(struct tcpc_device *tcpc, + uint16_t svid, uint8_t ops, uint32_t mode) +{ + struct tcp_notify tcp_noti; + int ret; + + tcp_noti.mode_ctrl.svid = svid; + tcp_noti.mode_ctrl.ops = ops; + tcp_noti.mode_ctrl.mode = mode; + + ret = tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_MODE, TCP_NOTIFY_ENTER_MODE); + return ret; +} + +int tcpci_exit_mode(struct tcpc_device *tcpc, uint16_t svid) +{ + struct tcp_notify tcp_noti; + int ret; + + tcp_noti.mode_ctrl.svid = svid; + ret = tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_MODE, TCP_NOTIFY_EXIT_MODE); + return ret; + +} + +#ifdef CONFIG_USB_PD_ALT_MODE + +int tcpci_report_hpd_state(struct tcpc_device *tcpc, uint32_t dp_status) +{ + struct tcp_notify tcp_noti; + struct dp_data *dp_data = pd_get_dp_data(&tcpc->pd_port); + + /* UFP_D to DFP_D only */ + + if (PD_DP_CFG_DFP_D(dp_data->local_config)) { + tcp_noti.ama_dp_hpd_state.irq = PD_VDO_DPSTS_HPD_IRQ(dp_status); + tcp_noti.ama_dp_hpd_state.state = + PD_VDO_DPSTS_HPD_LVL(dp_status); + tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_MODE, TCP_NOTIFY_AMA_DP_HPD_STATE); + } + + return 0; +} + +int tcpci_dp_status_update(struct tcpc_device *tcpc, uint32_t dp_status) +{ + DP_INFO("Status0: 0x%x\n", dp_status); + tcpci_report_hpd_state(tcpc, dp_status); + return 0; +} + +int tcpci_dp_configure(struct tcpc_device *tcpc, uint32_t dp_config) +{ + struct tcp_notify tcp_noti; + int ret; + + DP_INFO("LocalCFG: 0x%x\n", dp_config); + + switch (dp_config & 0x03) { + case 0: + tcp_noti.ama_dp_state.sel_config = SW_USB; + break; + case MODE_DP_SNK: + tcp_noti.ama_dp_state.sel_config = SW_DFP_D; + tcp_noti.ama_dp_state.pin_assignment = (dp_config >> 8) & 0xff; + break; + case MODE_DP_SRC: + tcp_noti.ama_dp_state.sel_config = SW_UFP_D; + tcp_noti.ama_dp_state.pin_assignment = (dp_config >> 16) & 0xff; + break; + } + if (tcp_noti.ama_dp_state.pin_assignment == 0) + tcp_noti.ama_dp_state.pin_assignment = (dp_config >> 16) & 0xff; + + DP_INFO("pin assignment: 0x%x\n", + tcp_noti.ama_dp_state.pin_assignment); + tcp_noti.ama_dp_state.signal = (dp_config >> 2) & 0x0f; + tcp_noti.ama_dp_state.polarity = tcpc->typec_polarity; + tcp_noti.ama_dp_state.active = 1; + ret = tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_MODE, TCP_NOTIFY_AMA_DP_STATE); + return ret; +} + +int tcpci_dp_attention(struct tcpc_device *tcpc, uint32_t dp_status) +{ + /* DFP_U : Not call this function during internal flow */ + struct tcp_notify tcp_noti; + + DP_INFO("Attention: 0x%x\n", dp_status); + tcp_noti.ama_dp_attention.state = (uint8_t) dp_status; + tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_MODE, TCP_NOTIFY_AMA_DP_ATTENTION); + return tcpci_report_hpd_state(tcpc, dp_status); +} + +int tcpci_dp_notify_status_update_done( + struct tcpc_device *tcpc, uint32_t dp_status, bool ack) +{ + /* DFP_U : Not call this function during internal flow */ + DP_INFO("Status1: 0x%x, ack=%d\n", dp_status, ack); + return 0; +} + +int tcpci_dp_notify_config_start(struct tcpc_device *tcpc) +{ + /* DFP_U : Put signal & mux into the Safe State */ + struct tcp_notify tcp_noti; + + DP_INFO("ConfigStart\n"); + tcp_noti.ama_dp_state.sel_config = SW_USB; + tcp_noti.ama_dp_state.active = 0; + tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_MODE, TCP_NOTIFY_AMA_DP_STATE); + return 0; +} + +int tcpci_dp_notify_config_done(struct tcpc_device *tcpc, + uint32_t local_cfg, uint32_t remote_cfg, bool ack) +{ + /* DFP_U : If DP success, + * internal flow will enter this function finally + */ + DP_INFO("ConfigDone, L:0x%x, R:0x%x, ack=%d\n", + local_cfg, remote_cfg, ack); + + if (ack) + tcpci_dp_configure(tcpc, local_cfg); + + return 0; +} + +#endif /* CONFIG_USB_PD_ALT_MODE */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM +int tcpci_notify_uvdm(struct tcpc_device *tcpc, bool ack) +{ + struct tcp_notify tcp_noti; + struct pd_port *pd_port = &tcpc->pd_port; + + tcp_noti.uvdm_msg.ack = ack; + + if (ack) { + tcp_noti.uvdm_msg.uvdm_cnt = pd_port->uvdm_cnt; + tcp_noti.uvdm_msg.uvdm_svid = pd_port->uvdm_svid; + tcp_noti.uvdm_msg.uvdm_data = pd_port->uvdm_data; + } + + tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_MODE, TCP_NOTIFY_UVDM); + return 0; +} +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + +#ifdef CONFIG_USB_PD_ALT_MODE_RTDC +int tcpci_dc_notify_en_unlock(struct tcpc_device *tcpc) +{ + struct tcp_notify tcp_noti; + int ret; + + DC_INFO("DirectCharge en_unlock\n"); + ret = tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_MODE, TCP_NOTIFY_DC_EN_UNLOCK); + return ret; +} +#endif /* CONFIG_USB_PD_ALT_MODE_RTDC */ + +/* ---- Policy Engine (PD30) ---- */ + +#ifdef CONFIG_USB_PD_REV30 + +#ifdef CONFIG_USB_PD_REV30_ALERT_REMOTE +int tcpci_notify_alert(struct tcpc_device *tcpc, uint32_t ado) +{ + struct tcp_notify tcp_noti; + int ret; + + tcp_noti.alert_msg.ado = ado; + ret = tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_MISC, TCP_NOTIFY_ALERT); + return ret; +} +#endif /* CONFIG_USB_PD_REV30_ALERT_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_REMOTE +int tcpci_notify_status(struct tcpc_device *tcpc, struct pd_status *sdb) +{ + struct tcp_notify tcp_noti; + int ret; + + tcp_noti.status_msg.sdb = sdb; + ret = tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_MISC, TCP_NOTIFY_STATUS); + return ret; +} +#endif /* CONFIG_USB_PD_REV30_STATUS_REMOTE */ + +#ifdef CONFIG_USB_PD_REV30_BAT_INFO +int tcpci_notify_request_bat_info( + struct tcpc_device *tcpc, enum pd_battery_reference ref) +{ + struct tcp_notify tcp_noti; + int ret; + + tcp_noti.request_bat.ref = ref; + ret = tcpc_check_notify_time(tcpc, &tcp_noti, + TCP_NOTIFY_IDX_MISC, TCP_NOTIFY_REQUEST_BAT_INFO); + return ret; +} +#endif /* CONFIG_USB_PD_REV30_BAT_INFO */ + +#endif /* CONFIG_USB_PD_REV30 */ + +#endif /* CONFIG_USB_POWER_DELIVERY */ diff --git a/drivers/usb/typec/mmi_tcpc/tcpci_alert.c b/drivers/usb/typec/mmi_tcpc/tcpci_alert.c new file mode 100644 index 000000000000..67aefd75cb58 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/tcpci_alert.c @@ -0,0 +1,605 @@ +/* + * Copyright (C) 2021 Richtek Inc. + * + * TCPC Interface for alert handler + * + * 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. + */ + +#include "inc/tcpci_typec.h" +#ifdef CONFIG_USB_POWER_DELIVERY +#include "inc/tcpci_event.h" +#endif /* CONFIG_USB_POWER_DELIVERY */ +#ifdef CONFIG_DUAL_ROLE_USB_INTF +#include +#endif /* CONFIG_DUAL_ROLE_USB_INTF */ + +/* + * [BLOCK] TCPCI IRQ Handler + */ + +static int tcpci_alert_cc_changed(struct tcpc_device *tcpc) +{ + return tcpc_typec_handle_cc_change(tcpc); +} + +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC +static inline int tcpci_alert_vsafe0v(struct tcpc_device *tcpc) +{ + tcpc_typec_handle_vsafe0v(tcpc); +#ifdef CONFIG_USB_POWER_DELIVERY +#ifdef CONFIG_USB_PD_SAFE0V_DELAY + tcpc_enable_timer(tcpc, PD_TIMER_VSAFE0V_DELAY); +#else + pd_put_vbus_safe0v_event(tcpc); +#endif /* CONFIG_USB_PD_SAFE0V_DELAY */ +#endif /* CONFIG_USB_POWER_DELIVERY */ + return 0; +} +#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */ + +static inline void tcpci_vbus_level_init_v10( + struct tcpc_device *tcpc, uint16_t power_status) +{ + mutex_lock(&tcpc->access_lock); + tcpc->vbus_level = power_status & TCPC_REG_POWER_STATUS_VBUS_PRES ? + TCPC_VBUS_VALID : TCPC_VBUS_INVALID; +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC + if (power_status & TCPC_REG_POWER_STATUS_EXT_VSAFE0V) { + if (tcpc->vbus_level == TCPC_VBUS_INVALID) + tcpc->vbus_level = TCPC_VBUS_SAFE0V; + else + TCPC_INFO("ps_confused: 0x%04x\n", power_status); + } +#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */ + mutex_unlock(&tcpc->access_lock); +} + +static void __tcpci_vbus_level_refresh(struct tcpc_device *tcpc) +{ + tcpc->vbus_level = tcpc->vbus_present ? TCPC_VBUS_VALID : + TCPC_VBUS_INVALID; +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC + if (tcpc->vbus_safe0v) { + if (tcpc->vbus_level == TCPC_VBUS_INVALID) + tcpc->vbus_level = TCPC_VBUS_SAFE0V; + else + TCPC_INFO("ps_confused: %d\n", tcpc->vbus_level); + } +#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */ +} + +static inline void tcpci_vbus_level_refresh(struct tcpc_device *tcpc) +{ + mutex_lock(&tcpc->access_lock); + __tcpci_vbus_level_refresh(tcpc); + mutex_unlock(&tcpc->access_lock); +} + +void tcpci_vbus_level_init(struct tcpc_device *tcpc, uint16_t power_status) +{ + if (tcpc->tcpc_flags & TCPC_FLAGS_ALERT_V10) { + tcpci_vbus_level_init_v10(tcpc, power_status); + return; + } + + mutex_lock(&tcpc->access_lock); + tcpc->vbus_present = !!(power_status & TCPC_REG_POWER_STATUS_VBUS_PRES); + __tcpci_vbus_level_refresh(tcpc); + mutex_unlock(&tcpc->access_lock); +} + +static int tcpci_vbus_level_changed(struct tcpc_device *tcpc) +{ + int rv = 0; + + TCPC_INFO("ps_change=%d\n", tcpc->vbus_level); + + rv = tcpc_typec_handle_ps_change(tcpc, tcpc->vbus_level); + if (rv < 0) + return rv; + +#ifdef CONFIG_USB_POWER_DELIVERY + pd_put_vbus_changed_event(tcpc, true); +#endif /* CONFIG_USB_POWER_DELIVERY */ + +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC + if (tcpc->vbus_level == TCPC_VBUS_SAFE0V) + rv = tcpci_alert_vsafe0v(tcpc); +#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */ + + return rv; +} + +static int tcpci_alert_power_status_changed(struct tcpc_device *tcpc) +{ + int rv = 0; + uint16_t power_status = 0; + + rv = tcpci_get_power_status(tcpc, &power_status); + if (rv < 0) + return rv; + + if (tcpc->tcpc_flags & TCPC_FLAGS_ALERT_V10) + return tcpci_vbus_level_changed(tcpc); + + return rv; +} + +#ifdef CONFIG_USB_POWER_DELIVERY +static int tcpci_alert_tx_success(struct tcpc_device *tcpc) +{ + uint8_t tx_state = PD_TX_STATE_GOOD_CRC; + struct pd_event evt = { + .event_type = PD_EVT_CTRL_MSG, + .msg = PD_CTRL_GOOD_CRC, + .pd_msg = NULL, + }; + + mutex_lock(&tcpc->access_lock); + tx_state = tcpc->pd_transmit_state; + tcpc->pd_transmit_state = PD_TX_STATE_GOOD_CRC; + mutex_unlock(&tcpc->access_lock); + + if (tx_state == PD_TX_STATE_WAIT_CRC_VDM) + pd_put_vdm_event(tcpc, &evt, false); + else + pd_put_event(tcpc, &evt, false); + + return 0; +} + +static int tcpci_alert_tx_failed(struct tcpc_device *tcpc) +{ + uint8_t tx_state = PD_TX_STATE_GOOD_CRC; + + mutex_lock(&tcpc->access_lock); + tx_state = tcpc->pd_transmit_state; + tcpc->pd_transmit_state = PD_TX_STATE_NO_GOOD_CRC; + mutex_unlock(&tcpc->access_lock); + + if (tx_state == PD_TX_STATE_WAIT_CRC_VDM) + vdm_put_hw_event(tcpc, PD_HW_TX_FAILED); + else + pd_put_hw_event(tcpc, PD_HW_TX_FAILED); + + return 0; +} + +static int tcpci_alert_tx_discard(struct tcpc_device *tcpc) +{ + uint8_t tx_state = PD_TX_STATE_GOOD_CRC; + bool retry_crc_discard = + !!(tcpc->tcpc_flags & TCPC_FLAGS_RETRY_CRC_DISCARD); + + TCPC_INFO("Discard\n"); + + mutex_lock(&tcpc->access_lock); + tx_state = tcpc->pd_transmit_state; + tcpc->pd_transmit_state = PD_TX_STATE_DISCARD; + mutex_unlock(&tcpc->access_lock); + + if (tx_state == PD_TX_STATE_WAIT_CRC_VDM) + pd_put_last_vdm_event(tcpc); + else { + if (retry_crc_discard) { +#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD + tcpc->pd_discard_pending = true; + tcpc_enable_timer(tcpc, PD_TIMER_DISCARD); +#else + TCPC_ERR("RETRY_CRC_DISCARD\n"); +#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */ + } else { + pd_put_hw_event(tcpc, PD_HW_TX_FAILED); + } + } + + return 0; +} + +static int tcpci_alert_recv_msg(struct tcpc_device *tcpc) +{ + int rv = 0; + struct pd_msg *pd_msg = NULL; + enum tcpm_transmit_type type = TCPC_TX_SOP; + uint32_t chip_id = 0; + int retval = 0; + + retval = tcpci_get_chip_id(tcpc, &chip_id); + if (!retval && (SC2150A_DID == chip_id) && + tcpc->pd_bist_mode == PD_BIST_MODE_DISABLE) { + tcpci_set_rx_enable(tcpc, PD_RX_CAP_PE_STARTUP); + } + + pd_msg = pd_alloc_msg(tcpc); + if (pd_msg == NULL) { + rv = -EINVAL; + goto out; + } + + rv = tcpci_get_message(tcpc, pd_msg->payload, &pd_msg->msg_hdr, &type); + if (rv < 0) { + TCPC_INFO("recv_msg failed: %d\n", rv); + pd_free_msg(tcpc, pd_msg); + goto out; + } + + pd_msg->frame_type = type; + pd_put_pd_msg_event(tcpc, pd_msg); +out: + tcpci_alert_status_clear(tcpc, TCPC_REG_ALERT_RX_MASK); + if (!retval && (SC2150A_DID == chip_id) && + tcpc->pd_bist_mode == PD_BIST_MODE_DISABLE) { + tcpci_set_rx_enable(tcpc, tcpc->pd_port.rx_cap); + } + + return rv; +} + +static int tcpci_alert_rx_overflow(struct tcpc_device *tcpc) +{ + TCPC_INFO("RX_OVERFLOW\n"); + return tcpci_alert_recv_msg(tcpc); +} + +static int tcpci_alert_recv_hard_reset(struct tcpc_device *tcpc) +{ + TCPC_INFO("HardResetAlert\n"); + pd_put_recv_hard_reset_event(tcpc); + return tcpci_init_alert_mask(tcpc); +} +#endif /* CONFIG_USB_POWER_DELIVERY */ + +static int tcpci_alert_vendor_defined(struct tcpc_device *tcpc) +{ + return tcpci_alert_vendor_defined_handler(tcpc); +} + +static int tcpci_alert_fault(struct tcpc_device *tcpc) +{ + uint8_t fault_status = 0; + + tcpci_get_fault_status(tcpc, &fault_status); + TCPC_INFO("FaultAlert=0x%02x\n", fault_status); + tcpci_fault_status_clear(tcpc, fault_status); + return 0; +} +EXPORT_SYMBOL(tcpci_alert); + +#ifdef CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG +static int tcpci_alert_wakeup(struct tcpc_device *tcpc) +{ + if (tcpc->tcpc_flags & TCPC_FLAGS_LPM_WAKEUP_WATCHDOG) { + TCPC_INFO("Wakeup\n"); + if (tcpc->typec_remote_cc[0] == TYPEC_CC_DRP_TOGGLING) + tcpc_enable_wakeup_timer(tcpc, true); + } + + return 0; +} +#endif /* CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG */ + +#ifdef CONFIG_TYPEC_CAP_RA_DETACH +static int tcpci_alert_ra_detach(struct tcpc_device *tcpc) +{ + if (tcpc->tcpc_flags & TCPC_FLAGS_CHECK_RA_DETACHE) { + TCPC_INFO("RA_DETACH\n"); + if (tcpc->typec_remote_cc[0] == TYPEC_CC_DRP_TOGGLING) + tcpc_typec_enter_lpm_again(tcpc); + } + + return 0; +} +#endif /* CONFIG_TYPEC_CAP_RA_DETACH */ + +struct tcpci_alert_handler { + uint32_t bit_mask; + int (*handler)(struct tcpc_device *tcpc); +}; + +#define DECL_TCPCI_ALERT_HANDLER(xbit, xhandler) {\ + .bit_mask = 1 << xbit,\ + .handler = xhandler,\ +} + +static const struct tcpci_alert_handler tcpci_alert_handlers[] = { + DECL_TCPCI_ALERT_HANDLER(15, tcpci_alert_vendor_defined), +#ifdef CONFIG_USB_POWER_DELIVERY + DECL_TCPCI_ALERT_HANDLER(4, tcpci_alert_tx_failed), + DECL_TCPCI_ALERT_HANDLER(5, tcpci_alert_tx_discard), + DECL_TCPCI_ALERT_HANDLER(6, tcpci_alert_tx_success), + DECL_TCPCI_ALERT_HANDLER(2, tcpci_alert_recv_msg), + DECL_TCPCI_ALERT_HANDLER(7, NULL), + DECL_TCPCI_ALERT_HANDLER(8, NULL), + DECL_TCPCI_ALERT_HANDLER(3, tcpci_alert_recv_hard_reset), + DECL_TCPCI_ALERT_HANDLER(10, tcpci_alert_rx_overflow), +#endif /* CONFIG_USB_POWER_DELIVERY */ + +#ifdef CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG + DECL_TCPCI_ALERT_HANDLER(16, tcpci_alert_wakeup), +#endif /* CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG */ + +#ifdef CONFIG_TYPEC_CAP_RA_DETACH + DECL_TCPCI_ALERT_HANDLER(21, tcpci_alert_ra_detach), +#endif /* CONFIG_TYPEC_CAP_RA_DETACH */ + + DECL_TCPCI_ALERT_HANDLER(9, tcpci_alert_fault), + DECL_TCPCI_ALERT_HANDLER(0, tcpci_alert_cc_changed), + DECL_TCPCI_ALERT_HANDLER(1, tcpci_alert_power_status_changed), +}; + +#ifdef CONFIG_USB_POWER_DELIVERY +static inline bool tcpci_check_hard_reset_complete( + struct tcpc_device *tcpc, uint32_t alert_status) +{ + if ((alert_status & TCPC_REG_ALERT_HRESET_SUCCESS) + == TCPC_REG_ALERT_HRESET_SUCCESS) { + pd_put_sent_hard_reset_event(tcpc); + return true; + } + + if (alert_status & TCPC_REG_ALERT_TX_DISCARDED) { + TCPC_INFO("HResetFailed\n"); + tcpci_transmit(tcpc, TCPC_TX_HARD_RESET, 0, NULL); + } + + return false; +} +#endif /* CONFIG_USB_POWER_DELIVERY */ + +int tcpci_alert(struct tcpc_device *tcpc) +{ + int rv = 0, i = 0; + uint32_t alert_status = 0, alert_mask = 0; + const uint8_t typec_role = tcpc->typec_role; + uint32_t chip_id; + + rv = tcpci_get_alert_status(tcpc, &alert_status); + if (rv < 0) + return rv; + + rv = tcpci_get_alert_mask(tcpc, &alert_mask); + if (rv < 0) + return rv; + +#ifdef CONFIG_USB_PD_DBG_ALERT_STATUS + TCPC_INFO("Alert:0x%04x, Mask:0x%04x\n", alert_status, alert_mask); +#endif /* CONFIG_USB_PD_DBG_ALERT_STATUS */ + + rv = tcpci_get_chip_id(tcpc,&chip_id); + if (rv || SC2150A_DID != chip_id) + alert_status &= alert_mask; + + if (typec_role == TYPEC_ROLE_UNKNOWN || + typec_role >= TYPEC_ROLE_NR) { + TYPEC_INFO("Wrong TypeC-Role: %d\n", typec_role); + tcpci_alert_status_clear(tcpc, alert_status); + return 0; + } + + /* mask all alert */ + rv = tcpci_set_alert_mask(tcpc, 0); + if (rv < 0) { + tcpci_alert_status_clear(tcpc, alert_status); + return rv; + } + + tcpci_alert_status_clear(tcpc, alert_status & ~TCPC_REG_ALERT_RX_MASK); + + if ((tcpc->tcpc_flags & TCPC_FLAGS_ALERT_V10) && + (alert_status & TCPC_REG_ALERT_EXT_VBUS_80)) + alert_status |= TCPC_REG_ALERT_POWER_STATUS; + +#ifdef CONFIG_USB_POWER_DELIVERY + if (tcpc->pd_transmit_state == PD_TX_STATE_WAIT_HARD_RESET) { + tcpci_check_hard_reset_complete(tcpc, alert_status); + alert_status &= ~TCPC_REG_ALERT_TX_MASK; + } +#endif /* CONFIG_USB_POWER_DELIVERY */ + +#ifndef CONFIG_USB_PD_DBG_SKIP_ALERT_HANDLER + for (i = 0; i < ARRAY_SIZE(tcpci_alert_handlers); i++) { + if (tcpci_alert_handlers[i].bit_mask & alert_status) { + if (tcpci_alert_handlers[i].handler) + tcpci_alert_handlers[i].handler(tcpc); + } + } +#endif /* CONFIG_USB_PD_DBG_SKIP_ALERT_HANDLER */ + + /* unmask alert */ + rv = tcpci_set_alert_mask(tcpc, alert_mask); + + if (tcpc->tcpc_flags & TCPC_FLAGS_ALERT_V10) + return rv; + + tcpci_vbus_level_refresh(tcpc); + tcpci_vbus_level_changed(tcpc); + + return rv; +} + +/* + * [BLOCK] TYPEC device changed + */ + +static inline int tcpci_set_wake_lock(struct tcpc_device *tcpc, bool pd_lock) +{ + if (!!pd_lock != !!tcpc->wake_lock_pd) { + if (pd_lock) { + TCPC_DBG("wake_lock=1\n"); + __pm_wakeup_event(tcpc->attach_wake_lock, + CONFIG_TCPC_ATTACH_WAKE_LOCK_TOUT); + if (tcpc->typec_watchdog) + tcpci_set_intrst(tcpc, true); + } else { + TCPC_DBG("wake_lock=0\n"); + if (tcpc->typec_watchdog) + tcpci_set_intrst(tcpc, false); + __pm_relax(tcpc->attach_wake_lock); + } + return 1; + } + + return 0; +} + +static int tcpci_set_wake_lock_pd(struct tcpc_device *tcpc, bool pd_lock) +{ + uint8_t wake_lock_pd = 0; + + mutex_lock(&tcpc->access_lock); + + wake_lock_pd = tcpc->wake_lock_pd; + if (pd_lock) + wake_lock_pd++; + else if (wake_lock_pd > 0) + wake_lock_pd--; + + if (wake_lock_pd == 0) + __pm_wakeup_event(tcpc->detach_wake_lock, 5000); + + tcpci_set_wake_lock(tcpc, wake_lock_pd); + tcpc->wake_lock_pd = wake_lock_pd; + + if (wake_lock_pd == 1) + __pm_relax(tcpc->detach_wake_lock); + + mutex_unlock(&tcpc->access_lock); + + return 0; +} + +static inline int tcpci_report_usb_port_attached(struct tcpc_device *tcpc) +{ + TCPC_INFO("usb_port_attached\n"); + + tcpci_set_wake_lock_pd(tcpc, true); + +#ifdef CONFIG_DUAL_ROLE_USB_INTF + switch (tcpc->typec_attach_new) { + case TYPEC_ATTACHED_SNK: + case TYPEC_ATTACHED_NORP_SRC: + case TYPEC_ATTACHED_CUSTOM_SRC: + case TYPEC_ATTACHED_DBGACC_SNK: + tcpc->dual_role_pr = DUAL_ROLE_PROP_PR_SNK; + tcpc->dual_role_dr = DUAL_ROLE_PROP_DR_DEVICE; + tcpc->dual_role_mode = DUAL_ROLE_PROP_MODE_UFP; + tcpc->dual_role_vconn = DUAL_ROLE_PROP_VCONN_SUPPLY_NO; + break; + case TYPEC_ATTACHED_SRC: + case TYPEC_ATTACHED_DEBUG: + tcpc->dual_role_pr = DUAL_ROLE_PROP_PR_SRC; + tcpc->dual_role_dr = DUAL_ROLE_PROP_DR_HOST; + tcpc->dual_role_mode = DUAL_ROLE_PROP_MODE_DFP; + tcpc->dual_role_vconn = DUAL_ROLE_PROP_VCONN_SUPPLY_YES; + break; + default: + break; + } + dual_role_instance_changed(tcpc->dr_usb); +#endif /* CONFIG_DUAL_ROLE_USB_INTF */ + +#ifdef CONFIG_USB_POWER_DELIVERY +#ifdef CONFIG_USB_PD_DISABLE_PE + if (tcpc->disable_pe) + return 0; +#endif /* CONFIG_USB_PD_DISABLE_PE */ + + if (tcpc->pd_inited_flag) + pd_put_cc_attached_event(tcpc, tcpc->typec_attach_new); +#endif /* CONFIG_USB_POWER_DLEIVERY */ + + return 0; +} + +static inline int tcpci_report_usb_port_detached(struct tcpc_device *tcpc) +{ + TCPC_INFO("usb_port_detached\n"); + +#ifdef CONFIG_DUAL_ROLE_USB_INTF + tcpc->dual_role_pr = DUAL_ROLE_PROP_PR_NONE; + tcpc->dual_role_dr = DUAL_ROLE_PROP_DR_NONE; + tcpc->dual_role_mode = DUAL_ROLE_PROP_MODE_NONE; + tcpc->dual_role_vconn = DUAL_ROLE_PROP_VCONN_SUPPLY_NO; + dual_role_instance_changed(tcpc->dr_usb); +#endif /* CONFIG_DUAL_ROLE_USB_INTF */ + +#ifdef CONFIG_USB_POWER_DELIVERY + if (tcpc->pd_inited_flag) + pd_put_cc_detached_event(tcpc); + else { + pd_event_buf_reset(tcpc); + tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_PE_IDLE); + } +#endif /* CONFIG_USB_POWER_DELIVERY */ + + tcpci_set_wake_lock_pd(tcpc, false); + + return 0; +} + +int tcpci_report_usb_port_changed(struct tcpc_device *tcpc) +{ + tcpci_notify_typec_state(tcpc); + + if (tcpc->typec_attach_old == TYPEC_UNATTACHED) + tcpci_report_usb_port_attached(tcpc); + else if (tcpc->typec_attach_new == TYPEC_UNATTACHED) + tcpci_report_usb_port_detached(tcpc); + else + TCPC_DBG2("TCPC Attach Again\n"); + + return 0; +} +EXPORT_SYMBOL(tcpci_report_usb_port_changed); + +/* + * [BLOCK] TYPEC power control changed + */ + +static inline int tcpci_report_power_control_on(struct tcpc_device *tcpc) +{ + tcpci_set_wake_lock_pd(tcpc, true); + + mutex_lock(&tcpc->access_lock); + tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_DISCHARGE); + tcpci_enable_auto_discharge(tcpc, true); + tcpci_enable_force_discharge(tcpc, false, 0); + mutex_unlock(&tcpc->access_lock); + + return 0; +} + +static inline int tcpci_report_power_control_off(struct tcpc_device *tcpc) +{ + mutex_lock(&tcpc->access_lock); + tcpci_enable_force_discharge(tcpc, true, 0); + tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_DISCHARGE); + mutex_unlock(&tcpc->access_lock); + + tcpci_set_wake_lock_pd(tcpc, false); + + return 0; +} + +int tcpci_report_power_control(struct tcpc_device *tcpc, bool en) +{ + if (tcpc->typec_power_ctrl == en) + return 0; + + tcpc->typec_power_ctrl = en; + + if (en) + tcpci_report_power_control_on(tcpc); + else + tcpci_report_power_control_off(tcpc); + + return 0; +} diff --git a/drivers/usb/typec/mmi_tcpc/tcpci_core.c b/drivers/usb/typec/mmi_tcpc/tcpci_core.c new file mode 100644 index 000000000000..e4faff4a0747 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/tcpci_core.c @@ -0,0 +1,1010 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Richtek TypeC Port Control Interface Core Driver + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "inc/tcpci.h" +#include "inc/tcpci_typec.h" + +#ifdef CONFIG_USB_POWER_DELIVERY +#include "pd_dpm_prv.h" +#include "inc/tcpm.h" +#ifdef CONFIG_RECV_BAT_ABSENT_NOTIFY +#include "mtk_battery.h" +#endif /* CONFIG_RECV_BAT_ABSENT_NOTIFY */ +#endif /* CONFIG_USB_POWER_DELIVERY */ +#include "inc/pd_dbg_info.h" +#include "inc/rt-regmap.h" + +#define TCPC_CORE_VERSION "2.0.17_G" + +static ssize_t tcpc_show_property(struct device *dev, + struct device_attribute *attr, char *buf); +static ssize_t tcpc_store_property(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +#define TCPC_DEVICE_ATTR(_name, _mode) \ +{ \ + .attr = { .name = #_name, .mode = _mode }, \ + .show = tcpc_show_property, \ + .store = tcpc_store_property, \ +} + +struct class *tcpc_class; +EXPORT_SYMBOL_GPL(tcpc_class); + +static struct device_type tcpc_dev_type; + +static struct device_attribute tcpc_device_attributes[] = { + TCPC_DEVICE_ATTR(role_def, 0444), + TCPC_DEVICE_ATTR(rp_lvl, 0444), + TCPC_DEVICE_ATTR(pd_test, 0664), + TCPC_DEVICE_ATTR(info, 0444), + TCPC_DEVICE_ATTR(timer, 0664), + TCPC_DEVICE_ATTR(caps_info, 0444), + TCPC_DEVICE_ATTR(pe_ready, 0444), + TCPC_DEVICE_ATTR(cc_orientation, S_IRUGO | S_IWUSR | S_IWGRP), +}; + +enum { + TCPC_DESC_ROLE_DEF = 0, + TCPC_DESC_RP_LEVEL, + TCPC_DESC_PD_TEST, + TCPC_DESC_INFO, + TCPC_DESC_TIMER, + TCPC_DESC_CAP_INFO, + TCPC_DESC_PE_READY, + TCPC_DESC_CC_POLA, +}; + +static struct attribute *__tcpc_attrs[ARRAY_SIZE(tcpc_device_attributes) + 1]; +static struct attribute_group tcpc_attr_group = { + .attrs = __tcpc_attrs, +}; + +static const struct attribute_group *tcpc_attr_groups[] = { + &tcpc_attr_group, + NULL, +}; + +static const char * const role_text[] = { + "Unknown", + "SNK Only", + "SRC Only", + "DRP", + "Try.SRC", + "Try.SNK", +}; + +static ssize_t tcpc_show_property(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct tcpc_device *tcpc = to_tcpc_device(dev); + const ptrdiff_t offset = attr - tcpc_device_attributes; + int i = 0; +#ifdef CONFIG_USB_POWER_DELIVERY + struct pe_data *pe_data; + struct pd_port *pd_port; + struct tcpm_power_cap_val cap; +#endif /* CONFIG_USB_POWER_DELIVERY */ + + switch (offset) { +#ifdef CONFIG_USB_POWER_DELIVERY + case TCPC_DESC_CAP_INFO: + pd_port = &tcpc->pd_port; + pe_data = &pd_port->pe_data; + snprintf(buf+strlen(buf), 256, "%s = %d\n%s = %d\n", + "local_selected_cap", + pe_data->local_selected_cap, + "remote_selected_cap", + pe_data->remote_selected_cap); + + snprintf(buf+strlen(buf), 256, "%s\n", + "local_src_cap(type, vmin, vmax, oper)"); + for (i = 0; i < pd_port->local_src_cap.nr; i++) { + tcpm_extract_power_cap_val( + pd_port->local_src_cap.pdos[i], + &cap); + snprintf(buf+strlen(buf), 256, "%d %d %d %d\n", + cap.type, cap.min_mv, cap.max_mv, cap.ma); + } + snprintf(buf+strlen(buf), 256, "%s\n", + "local_snk_cap(type, vmin, vmax, ioper)"); + for (i = 0; i < pd_port->local_snk_cap.nr; i++) { + tcpm_extract_power_cap_val( + pd_port->local_snk_cap.pdos[i], + &cap); + snprintf(buf+strlen(buf), 256, "%d %d %d %d\n", + cap.type, cap.min_mv, cap.max_mv, cap.ma); + } + snprintf(buf+strlen(buf), 256, "%s\n", + "remote_src_cap(type, vmin, vmax, ioper)"); + for (i = 0; i < pe_data->remote_src_cap.nr; i++) { + tcpm_extract_power_cap_val( + pe_data->remote_src_cap.pdos[i], + &cap); + snprintf(buf+strlen(buf), 256, "%d %d %d %d\n", + cap.type, cap.min_mv, cap.max_mv, cap.ma); + } + snprintf(buf+strlen(buf), 256, "%s\n", + "remote_snk_cap(type, vmin, vmax, ioper)"); + for (i = 0; i < pe_data->remote_snk_cap.nr; i++) { + tcpm_extract_power_cap_val( + pe_data->remote_snk_cap.pdos[i], + &cap); + snprintf(buf+strlen(buf), 256, "%d %d %d %d\n", + cap.type, cap.min_mv, cap.max_mv, cap.ma); + } + break; +#endif /* CONFIG_USB_POWER_DELIVERY */ + case TCPC_DESC_ROLE_DEF: + snprintf(buf, 256, "%s\n", role_text[tcpc->desc.role_def]); + break; + case TCPC_DESC_RP_LEVEL: + if (tcpc->typec_local_rp_level == TYPEC_RP_DFT) + snprintf(buf, 256, "%s\n", "Default"); + else if (tcpc->typec_local_rp_level == TYPEC_RP_1_5) + snprintf(buf, 256, "%s\n", "1.5"); + else if (tcpc->typec_local_rp_level == TYPEC_RP_3_0) + snprintf(buf, 256, "%s\n", "3.0"); + break; + case TCPC_DESC_PD_TEST: + snprintf(buf, 256, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", + "1: pr_swap", "2: dr_swap", "3: vconn_swap", + "4: soft reset", "5: hard reset", + "6: get_src_cap", "7: get_sink_cap", + "8: discover_id", "9: discover_cable"); + break; + case TCPC_DESC_INFO: + i += snprintf(buf + i, + 256, "|^|==( %s info )==|^|\n", tcpc->desc.name); + i += snprintf(buf + i, + 256, "role = %s\n", role_text[tcpc->desc.role_def]); + if (tcpc->typec_local_rp_level == TYPEC_RP_DFT) + i += snprintf(buf + i, 256, "rplvl = %s\n", "Default"); + else if (tcpc->typec_local_rp_level == TYPEC_RP_1_5) + i += snprintf(buf + i, 256, "rplvl = %s\n", "1.5"); + else if (tcpc->typec_local_rp_level == TYPEC_RP_3_0) + i += snprintf(buf + i, 256, "rplvl = %s\n", "3.0"); + break; +#ifdef CONFIG_USB_POWER_DELIVERY + case TCPC_DESC_PE_READY: + pd_port = &tcpc->pd_port; + if (pd_port->pe_data.pe_ready) + snprintf(buf, 256, "%s\n", "yes"); + else + snprintf(buf, 256, "%s\n", "no"); + break; +#endif + case TCPC_DESC_CC_POLA: + if (tcpm_inquire_cc_polarity(tcpc)) + snprintf(buf, 256, "%s\n", "CC2"); + else + snprintf(buf, 256, "%s\n", "CC1"); + break; + + default: + break; + } + return strlen(buf); +} + +static int get_parameters(char *buf, unsigned long *param, int num_of_par) +{ + int cnt = 0; + char *token = strsep(&buf, " "); + + for (cnt = 0; cnt < num_of_par; cnt++) { + if (token) { + if (kstrtoul(token, 0, ¶m[cnt]) != 0) + return -EINVAL; + + token = strsep(&buf, " "); + } else + return -EINVAL; + } + + return 0; +} + +static ssize_t tcpc_store_property(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ +#ifdef CONFIG_USB_POWER_DELIVERY + uint8_t role; +#endif /* CONFIG_USB_POWER_DELIVERY */ + + struct tcpc_device *tcpc = to_tcpc_device(dev); + const ptrdiff_t offset = attr - tcpc_device_attributes; + int ret; + long int val; + + switch (offset) { + case TCPC_DESC_ROLE_DEF: + ret = get_parameters((char *)buf, &val, 1); + if (ret < 0) { + dev_err(dev, "get parameters fail\n"); + return -EINVAL; + } + + tcpm_typec_change_role(tcpc, val); + break; + case TCPC_DESC_TIMER: + ret = get_parameters((char *)buf, &val, 1); + if (ret < 0) { + dev_err(dev, "get parameters fail\n"); + return -EINVAL; + } + #ifdef CONFIG_USB_POWER_DELIVERY + if (val > 0 && val <= PD_PE_TIMER_END_ID) + pd_enable_timer(&tcpc->pd_port, val); + else if (val > PD_PE_TIMER_END_ID && val < PD_TIMER_NR) + tcpc_enable_timer(tcpc, val); + #else + if (val > 0 && val < PD_TIMER_NR) + tcpc_enable_timer(tcpc, val); + #endif /* CONFIG_USB_POWER_DELIVERY */ + break; + #ifdef CONFIG_USB_POWER_DELIVERY + case TCPC_DESC_PD_TEST: + ret = get_parameters((char *)buf, &val, 1); + if (ret < 0) { + dev_err(dev, "get parameters fail\n"); + return -EINVAL; + } + switch (val) { + case 1: /* Power Role Swap */ + role = tcpm_inquire_pd_power_role(tcpc); + if (role == PD_ROLE_SINK) + role = PD_ROLE_SOURCE; + else + role = PD_ROLE_SINK; + tcpm_dpm_pd_power_swap(tcpc, role, NULL); + break; + case 2: /* Data Role Swap */ + role = tcpm_inquire_pd_data_role(tcpc); + if (role == PD_ROLE_UFP) + role = PD_ROLE_DFP; + else + role = PD_ROLE_UFP; + tcpm_dpm_pd_data_swap(tcpc, role, NULL); + break; + case 3: /* Vconn Swap */ + role = tcpm_inquire_pd_vconn_role(tcpc); + if (role == PD_ROLE_VCONN_OFF) + role = PD_ROLE_VCONN_ON; + else + role = PD_ROLE_VCONN_OFF; + tcpm_dpm_pd_vconn_swap(tcpc, role, NULL); + break; + case 4: /* Software Reset */ + tcpm_dpm_pd_soft_reset(tcpc, NULL); + break; + case 5: /* Hardware Reset */ + tcpm_dpm_pd_hard_reset(tcpc, NULL); + break; + case 6: + tcpm_dpm_pd_get_source_cap(tcpc, NULL); + break; + case 7: + tcpm_dpm_pd_get_sink_cap(tcpc, NULL); + break; + case 8: + tcpm_dpm_vdm_discover_id(tcpc, NULL); + break; + case 9: + tcpm_dpm_vdm_discover_cable(tcpc, NULL); + break; + default: + break; + } + break; + #endif /* CONFIG_USB_POWER_DELIVERY */ + default: + break; + } + return count; +} + +static int tcpc_match_device_by_name(struct device *dev, const void *data) +{ + const char *name = data; + struct tcpc_device *tcpc = dev_get_drvdata(dev); + + return strcmp(tcpc->desc.name, name) == 0; +} + +struct tcpc_device *tcpc_dev_get_by_name(const char *name) +{ + struct device *dev = class_find_device(tcpc_class, + NULL, (const void *)name, tcpc_match_device_by_name); + return dev ? dev_get_drvdata(dev) : NULL; +} +EXPORT_SYMBOL(tcpc_dev_get_by_name); + +static void tcpc_device_release(struct device *dev) +{ + struct tcpc_device *tcpc = to_tcpc_device(dev); + + pr_info("%s : %s device release\n", __func__, dev_name(dev)); + PD_BUG_ON(tcpc == NULL); + /* Un-init pe thread */ +#ifdef CONFIG_USB_POWER_DELIVERY + tcpci_event_deinit(tcpc); +#endif /* CONFIG_USB_POWER_DELIVERY */ + /* Un-init timer thread */ + tcpci_timer_deinit(tcpc); + /* Un-init Mutex */ + /* Do initialization */ +} + +static void tcpc_init_work(struct work_struct *work); +static void tcpc_event_init_work(struct work_struct *work); + +struct tcpc_device *tcpc_device_register(struct device *parent, + struct tcpc_desc *tcpc_desc, struct tcpc_ops *ops, void *drv_data) +{ + struct tcpc_device *tcpc; + int ret = 0, i = 0; + + pr_info("%s register tcpc device (%s)\n", __func__, tcpc_desc->name); + tcpc = devm_kzalloc(parent, sizeof(*tcpc), GFP_KERNEL); + if (!tcpc) { + pr_err("%s : allocate tcpc memeory failed\n", __func__); + return NULL; + } + + tcpc->evt_wq = alloc_ordered_workqueue("%s", 0, tcpc_desc->name); + for (i = 0; i < TCP_NOTIFY_IDX_NR; i++) + srcu_init_notifier_head(&tcpc->evt_nh[i]); + + mutex_init(&tcpc->access_lock); + mutex_init(&tcpc->typec_lock); + mutex_init(&tcpc->timer_lock); + mutex_init(&tcpc->mr_lock); + sema_init(&tcpc->timer_enable_mask_lock, 1); + spin_lock_init(&tcpc->timer_tick_lock); + + tcpc->dev.class = tcpc_class; + tcpc->dev.type = &tcpc_dev_type; + tcpc->dev.parent = parent; + tcpc->dev.release = tcpc_device_release; + dev_set_drvdata(&tcpc->dev, tcpc); + tcpc->drv_data = drv_data; + dev_set_name(&tcpc->dev, "%s", tcpc_desc->name); + tcpc->desc = *tcpc_desc; + tcpc->ops = ops; + tcpc->typec_local_rp_level = tcpc_desc->rp_lvl; + +#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE + tcpc->tcpc_vconn_supply = tcpc_desc->vconn_supply; +#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */ + + device_set_of_node_from_dev(&tcpc->dev, parent); + + ret = device_register(&tcpc->dev); + if (ret) { + kfree(tcpc); + return ERR_PTR(ret); + } + + INIT_DELAYED_WORK(&tcpc->init_work, tcpc_init_work); + INIT_DELAYED_WORK(&tcpc->event_init_work, tcpc_event_init_work); + + /* If system support "WAKE_LOCK_IDLE", + * please use it instead of "WAKE_LOCK_SUSPEND" + */ + tcpc->attach_wake_lock = + wakeup_source_register(&tcpc->dev, "tcpc_attach_wake_lock"); + tcpc->detach_wake_lock = + wakeup_source_register(&tcpc->dev, "tcpc_detach_wake_lock"); + + tcpci_timer_init(tcpc); +#ifdef CONFIG_USB_POWER_DELIVERY + pd_core_init(tcpc); +#endif /* CONFIG_USB_POWER_DELIVERY */ + +#ifdef CONFIG_DUAL_ROLE_USB_INTF + ret = tcpc_dual_role_phy_init(tcpc); + if (ret < 0) + dev_err(&tcpc->dev, "dual role usb init fail\n"); +#endif /* CONFIG_DUAL_ROLE_USB_INTF */ + + return tcpc; +} +EXPORT_SYMBOL(tcpc_device_register); + +int tcpc_device_irq_enable(struct tcpc_device *tcpc) +{ + int ret; + + if (!tcpc->ops->init) { + pr_notice("%s Please implment tcpc ops init function\n", + __func__); + return -EINVAL; + } + + tcpci_lock_typec(tcpc); + ret = tcpci_init(tcpc, false); + if (ret < 0) { + tcpci_unlock_typec(tcpc); + pr_err("%s tcpc init fail\n", __func__); + return ret; + } + + ret = tcpc_typec_init(tcpc, tcpc->desc.role_def); + tcpci_unlock_typec(tcpc); + if (ret < 0) { + pr_err("%s : tcpc typec init fail\n", __func__); + return ret; + } + + schedule_delayed_work( + &tcpc->event_init_work, msecs_to_jiffies(10*1000)); + + pr_info("%s : tcpc irq enable OK!\n", __func__); + return 0; +} +EXPORT_SYMBOL(tcpc_device_irq_enable); + +#ifdef CONFIG_USB_PD_REV30 +static void bat_update_work_func(struct work_struct *work) +{ + struct tcpc_device *tcpc = container_of(work, + struct tcpc_device, bat_update_work.work); + union power_supply_propval value; + int ret; + + ret = power_supply_get_property( + tcpc->bat_psy, POWER_SUPPLY_PROP_CAPACITY, &value); + if (ret == 0) { + TCPC_INFO("%s battery update soc = %d\n", + __func__, value.intval); + tcpc->bat_soc = value.intval; + } else + TCPC_ERR("%s get battery capacity fail\n", __func__); + + ret = power_supply_get_property(tcpc->bat_psy, + POWER_SUPPLY_PROP_STATUS, &value); + if (ret == 0) { + if (value.intval == POWER_SUPPLY_STATUS_CHARGING) { + TCPC_INFO("%s Battery Charging\n", __func__); + tcpc->charging_status = BSDO_BAT_INFO_CHARGING; + } else if (value.intval == POWER_SUPPLY_STATUS_DISCHARGING) { + TCPC_INFO("%s Battery Discharging\n", __func__); + tcpc->charging_status = BSDO_BAT_INFO_DISCHARGING; + } else { + TCPC_INFO("%s Battery Idle\n", __func__); + tcpc->charging_status = BSDO_BAT_INFO_IDLE; + } + } + if (ret < 0) + TCPC_ERR("%s get battery charger now fail\n", __func__); + + tcpm_update_bat_status_soc(tcpc, + PD_BAT_REF_FIXED0, tcpc->charging_status, tcpc->bat_soc * 10); +} + +static int bat_nb_call_func( + struct notifier_block *nb, unsigned long val, void *v) +{ + struct tcpc_device *tcpc = container_of(nb, struct tcpc_device, bat_nb); + struct power_supply *psy = (struct power_supply *)v; + + if (!tcpc) { + TCPC_ERR("%s tcpc is null\n", __func__); + return NOTIFY_OK; + } + + if (val == PSY_EVENT_PROP_CHANGED && + strcmp(psy->desc->name, "battery") == 0) + schedule_delayed_work(&tcpc->bat_update_work, 0); + return NOTIFY_OK; +} +#endif /* CONFIG_USB_PD_REV30 */ + +static void tcpc_event_init_work(struct work_struct *work) +{ +#ifdef CONFIG_USB_POWER_DELIVERY + struct tcpc_device *tcpc = container_of( + work, struct tcpc_device, event_init_work.work); +#ifdef CONFIG_USB_PD_REV30 + int retval; +#endif /* CONFIG_USB_PD_REV30 */ + + tcpci_lock_typec(tcpc); + tcpci_event_init(tcpc); +#ifdef CONFIG_USB_PD_WAIT_BC12 + tcpc->usb_psy = power_supply_get_by_name("usb"); + if (!tcpc->usb_psy) { + tcpci_unlock_typec(tcpc); + TCPC_ERR("%s get usb psy fail\n", __func__); + return; + } +#endif /* CONFIG_USB_PD_WAIT_BC12 */ + tcpc->pd_inited_flag = 1; + pr_info("%s typec attach new = %d\n", + __func__, tcpc->typec_attach_new); + if (tcpc->typec_attach_new) + pd_put_cc_attached_event(tcpc, tcpc->typec_attach_new); + tcpci_unlock_typec(tcpc); + +#ifdef CONFIG_USB_PD_REV30 + INIT_DELAYED_WORK(&tcpc->bat_update_work, bat_update_work_func); + tcpc->bat_psy = power_supply_get_by_name("battery"); + if (!tcpc->bat_psy) { + TCPC_ERR("%s get battery psy fail\n", __func__); + return; + } + tcpc->charging_status = BSDO_BAT_INFO_IDLE; + tcpc->bat_soc = 0; + tcpc->bat_nb.notifier_call = bat_nb_call_func; + tcpc->bat_nb.priority = 0; + retval = power_supply_reg_notifier(&tcpc->bat_nb); + if (retval < 0) + pr_err("%s register power supply notifier fail\n", __func__); +#endif /* CONFIG_USB_PD_REV30 */ + +#endif /* CONFIG_USB_POWER_DELIVERY */ +} + +static void tcpc_init_work(struct work_struct *work) +{ + struct tcpc_device *tcpc = container_of( + work, struct tcpc_device, init_work.work); + +#ifndef CONFIG_TCPC_NOTIFIER_LATE_SYNC + if (tcpc->desc.notifier_supply_num == 0) + return; +#endif + pr_info("%s force start\n", __func__); + + tcpc->desc.notifier_supply_num = 0; + tcpc_device_irq_enable(tcpc); +} + +int tcpc_schedule_init_work(struct tcpc_device *tcpc) +{ +#ifndef CONFIG_TCPC_NOTIFIER_LATE_SYNC + if (tcpc->desc.notifier_supply_num == 0) + return tcpc_device_irq_enable(tcpc); + + pr_info("%s wait %d num\n", __func__, tcpc->desc.notifier_supply_num); + + schedule_delayed_work( + &tcpc->init_work, msecs_to_jiffies(30*1000)); +#endif + return 0; +} +EXPORT_SYMBOL(tcpc_schedule_init_work); + +struct tcp_notifier_block_wrapper { + struct notifier_block stub_nb; + struct notifier_block *action_nb; +}; + +static int tcp_notifier_func_stub(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct tcp_notifier_block_wrapper *nb_wrapper = + container_of(nb, struct tcp_notifier_block_wrapper, stub_nb); + struct notifier_block *action_nb = nb_wrapper->action_nb; + + return nb_wrapper->action_nb->notifier_call(action_nb, action, data); +} + +struct tcpc_managed_res { + void *res; + void *key; + int prv_id; + struct tcpc_managed_res *next; +}; + + +static int __add_wrapper_to_managed_res_list(struct tcpc_device *tcp_dev, + void *res, void *key, int prv_id) +{ + struct tcpc_managed_res *tail; + struct tcpc_managed_res *mres; + + mres = devm_kzalloc(&tcp_dev->dev, sizeof(*mres), GFP_KERNEL); + if (!mres) + return -ENOMEM; + mres->res = res; + mres->key = key; + mres->prv_id = prv_id; + mutex_lock(&tcp_dev->mr_lock); + tail = tcp_dev->mr_head; + if (tail) { + while (tail->next) + tail = tail->next; + tail->next = mres; + } else + tcp_dev->mr_head = mres; + mutex_unlock(&tcp_dev->mr_lock); + + return 0; +} + +static int __register_tcp_dev_notifier(struct tcpc_device *tcp_dev, + struct notifier_block *nb, uint8_t idx) +{ + struct tcp_notifier_block_wrapper *nb_wrapper; + int retval; + + nb_wrapper = devm_kzalloc( + &tcp_dev->dev, sizeof(*nb_wrapper), GFP_KERNEL); + if (!nb_wrapper) + return -ENOMEM; + nb_wrapper->action_nb = nb; + nb_wrapper->stub_nb.notifier_call = tcp_notifier_func_stub; + retval = srcu_notifier_chain_register( + tcp_dev->evt_nh + idx, &nb_wrapper->stub_nb); + if (retval < 0) { + devm_kfree(&tcp_dev->dev, nb_wrapper); + return retval; + } + retval = __add_wrapper_to_managed_res_list( + tcp_dev, nb_wrapper, nb, idx); + if (retval < 0) + dev_warn(&tcp_dev->dev, + "Failed to add resource to manager(%d)\n", retval); + + return 0; +} + +static bool __is_mulit_bits_set(uint32_t flags) +{ + if (flags) { + flags &= (flags - 1); + return flags ? true : false; + } + + return false; +} + +int register_tcp_dev_notifier(struct tcpc_device *tcp_dev, + struct notifier_block *nb, uint8_t flags) +{ + int ret = 0, i = 0; + + if (__is_mulit_bits_set(flags)) { + for (i = 0; i < TCP_NOTIFY_IDX_NR; i++) { + if (flags & (1 << i)) { + ret = __register_tcp_dev_notifier( + tcp_dev, nb, i); + if (ret < 0) + return ret; + } + } + } else { /* single bit */ + for (i = 0; i < TCP_NOTIFY_IDX_NR; i++) { + if (flags & (1 << i)) { + ret = srcu_notifier_chain_register( + &tcp_dev->evt_nh[i], nb); + break; + } + } + } + +#ifndef CONFIG_TCPC_NOTIFIER_LATE_SYNC + if (tcp_dev->desc.notifier_supply_num == 0) { + pr_info("%s already started\n", __func__); + return 0; + } + + tcp_dev->desc.notifier_supply_num--; + pr_info("%s supply_num = %d\n", __func__, + tcp_dev->desc.notifier_supply_num); + + if (tcp_dev->desc.notifier_supply_num == 0) { + cancel_delayed_work(&tcp_dev->init_work); + tcpc_device_irq_enable(tcp_dev); + } +#endif + return ret; +} +EXPORT_SYMBOL(register_tcp_dev_notifier); + + +static void *__remove_wrapper_from_managed_res_list( + struct tcpc_device *tcp_dev, void *key, int prv_id) +{ + void *retval = NULL; + struct tcpc_managed_res *mres = tcp_dev->mr_head; + struct tcpc_managed_res *prev = NULL; + + mutex_lock(&tcp_dev->mr_lock); + if (mres) { + while (mres) { + if (mres->key == key && mres->prv_id == prv_id) { + retval = mres->res; + if (prev) + prev->next = mres->next; + else + tcp_dev->mr_head = NULL; + devm_kfree(&tcp_dev->dev, mres); + break; + } + prev = mres; + mres = mres->next; + } + } + mutex_unlock(&tcp_dev->mr_lock); + + return retval; +} + +static int __unregister_tcp_dev_notifier(struct tcpc_device *tcp_dev, + struct notifier_block *nb, uint8_t idx) +{ + struct tcp_notifier_block_wrapper *nb_wrapper; + int retval; + + nb_wrapper = __remove_wrapper_from_managed_res_list(tcp_dev, nb, idx); + if (nb_wrapper) { + retval = srcu_notifier_chain_unregister( + tcp_dev->evt_nh + idx, &nb_wrapper->stub_nb); + devm_kfree(&tcp_dev->dev, nb_wrapper); + return retval; + } + + return -ENOENT; +} + +int unregister_tcp_dev_notifier(struct tcpc_device *tcp_dev, + struct notifier_block *nb, uint8_t flags) +{ + int i = 0, ret = 0; + + for (i = 0; i < TCP_NOTIFY_IDX_NR; i++) { + if (flags & (1 << i)) { + ret = __unregister_tcp_dev_notifier(tcp_dev, nb, i); + if (ret == -ENOENT) + ret = srcu_notifier_chain_unregister( + tcp_dev->evt_nh + i, nb); + if (ret < 0) + return ret; + } + } + return ret; +} +EXPORT_SYMBOL(unregister_tcp_dev_notifier); + + +void tcpc_device_unregister(struct device *dev, struct tcpc_device *tcpc) +{ + if (!tcpc) + return; + + tcpc_typec_deinit(tcpc); + +#ifdef CONFIG_USB_PD_REV30 + wakeup_source_unregister(tcpc->pd_port.pps_request_wake_lock); +#endif /* CONFIG_USB_PD_REV30 */ + wakeup_source_unregister(tcpc->detach_wake_lock); + wakeup_source_unregister(tcpc->attach_wake_lock); + +#ifdef CONFIG_DUAL_ROLE_USB_INTF + devm_dual_role_instance_unregister(&tcpc->dev, tcpc->dr_usb); +#endif /* CONFIG_DUAL_ROLE_USB_INTF */ + + device_unregister(&tcpc->dev); + +} +EXPORT_SYMBOL(tcpc_device_unregister); + +void *tcpc_get_dev_data(struct tcpc_device *tcpc) +{ + return tcpc->drv_data; +} +EXPORT_SYMBOL(tcpc_get_dev_data); + +void tcpci_lock_typec(struct tcpc_device *tcpc) +{ + mutex_lock(&tcpc->typec_lock); +} +EXPORT_SYMBOL(tcpci_lock_typec); + +void tcpci_unlock_typec(struct tcpc_device *tcpc) +{ + mutex_unlock(&tcpc->typec_lock); +} +EXPORT_SYMBOL(tcpci_unlock_typec); + +static void tcpc_init_attrs(struct device_type *dev_type) +{ + int i; + + dev_type->groups = tcpc_attr_groups; + for (i = 0; i < ARRAY_SIZE(tcpc_device_attributes); i++) + __tcpc_attrs[i] = &tcpc_device_attributes[i].attr; +} + +static int __init tcpc_class_init(void) +{ + pr_info("%s (%s)\n", __func__, TCPC_CORE_VERSION); + +#ifdef CONFIG_USB_POWER_DELIVERY + dpm_check_supported_modes(); +#endif /* CONFIG_USB_POWER_DELIVERY */ + + tcpc_class = class_create(THIS_MODULE, "tcpc"); + if (IS_ERR(tcpc_class)) { + pr_info("Unable to create tcpc class; errno = %ld\n", + PTR_ERR(tcpc_class)); + return PTR_ERR(tcpc_class); + } + tcpc_init_attrs(&tcpc_dev_type); + + pd_dbg_info_init(); + regmap_plat_init(); + + pr_info("TCPC class init OK\n"); + return 0; +} + +static void __exit tcpc_class_exit(void) +{ + regmap_plat_exit(); + pd_dbg_info_exit(); + + class_destroy(tcpc_class); + pr_info("TCPC class un-init OK\n"); +} + +subsys_initcall(tcpc_class_init); +module_exit(tcpc_class_exit); + +MODULE_DESCRIPTION("Richtek TypeC Port Control Core"); +MODULE_AUTHOR("Jeff Chang "); +MODULE_VERSION(TCPC_CORE_VERSION); +MODULE_LICENSE("GPL"); + +/* Release Version + * 2.0.17_G + * (1) Add CONFIG_TYPEC_LEGACY3_ALWAYS_LOCAL_RP + * (2) Fix a synchronization/locking problem in pd_notify_pe_error_recovery() + * (3) Add USB_VID_MQP + * (4) Revise the return value checking of tcpc_device_register() + * + * 2.0.16_G + * (1) Check the return value of wait_event_interruptible() + * (2) Revise *_get_cc() + * (3) Revise role_def + * (4) Fix COMMON.CHECK.PD.10 + * + * 2.0.15_G + * (1) undef CONFIG_COMPATIBLE_APPLE_TA + * (2) Fix TEST.PD.PROT.ALL.5 Unrecognized Message (PD2) + * (3) Fix TEST.PD.PROT.ALL3.3 Invalid Manufacturer Info Target + * (4) Fix TEST.PD.PROT.ALL3.4 Invalid Manufacturer Info Ref + * (5) Fix TEST.PD.PROT.SRC.11 Unexpected Message Received in Ready State (PD2) + * (6) Fix TEST.PD.PROT.SRC.13 PR_Swap - GoodCRC not sent in Response to PS_RDY + * (7) Fix TEST.PD.VDM.SRC.2 Invalid Fields - Discover Identity (PD2) + * (8) Revise the usages of PD_TIMER_NO_RESPONSE + * (9) Retry to send Source_Capabilities after PR_Swap + * (10) Fix tcpm_get_remote_power_cap() and __tcpm_inquire_select_source_cap() + * (11) Increase the threshold to enter PE_ERROR_RECOVERY_ONCE from 2 to 4 + * (12) Change wait_event() back to wait_event_interruptible() for not being + * detected as hung tasks + * + * 2.0.14_G + * (1) Move out typec_port registration and operation to rt_pd_manager.c + * (2) Rename CONFIG_TYPEC_WAIT_BC12 to CONFIG_USB_PD_WAIT_BC12 + * (3) Not to set power/data/vconn role repeatedly + * (4) Revise vconn highV protection + * (5) Revise tcpc timer + * (6) Decrease VBUS present threshold (VBUS_CAL) by 60mV (2LSBs) for RT171x + * (7) Replace \r\n with \n for resolving logs without newlines + * (8) Remove the member time_stamp from struct pd_msg + * (9) Remove NoResponseTimer as Sink for new PD spec + * (10) Revise responses of Reject and Not_Supported + * (11) Revise the usages of pd_traffic_control and typec_power_ctrl + * (12) Revise the usages of wait_event_*() + * (13) Add PD capability for TYPEC_ATTACHED_DBGACC_SNK + * (14) Utilize rt-regmap to reduce I2C accesses + * + * 2.0.13_G + * (1) Add TCPC flags for VCONN_SAFE5V_ONLY + * (2) Add boolean property attemp_discover_svid in dts/dtsi + * (3) Add a TCPM API for postponing Type-C role change until unattached + * (4) Update VDOs according new PD spec + * (5) Add an option for enabling/disabling the support of DebugAccessory.SRC + * (6) Add the workaround for delayed ps_change related to PS_RDY + * during PR_SWAP + * (7) Always Back to PE ready state in pd_dpm_dfp_inform_id() and + * pd_dpm_dfp_inform_svids() + * (8) Re-fetch triggered_timer and enable_mask after lock acquisition + * (9) Leave low power mode only when CC is detached + * (10) Revise code related to pd_check_rev30() + * (11) Bypass BC1.2 for PR_SWAP from Source to Sink (MTK only) + * (12) Support charging icon for AudioAccessory (MTK only) + * (13) Replace tcpc_dev with tcpc + * (14) TCPCI Alert V10 and V20 co-exist + * (15) Resolve DP Source/Sink Both Connected when acting as DFP_U + * (16) Change CONFIG_TYPEC_SNK_CURR_DFT from 150 to 100 (mA) + * (17) Define CONFIG_USB_PD_PR_SWAP_ERROR_RECOVERY by default + * (18) Add an option for TCPC log with port name + * (19) USB-C states go from ErrorRecovery to Unattached.SRC with Try.SRC role + * (20) Revise dts/dtsi value for DisplayPort Alternative Mode + * (21) Mask vSafe0V IRQ before entering low power mode + * (22) Disable auto idle mode before entering low power mode + * (23) Reset Protocol FSM and clear RX alerts twice before clock gating + * + * 2.0.12_G + * (1) Fix voltage/current steps of RDO for APDO + * (2) Non-blocking TCPC notification by default + * (3) Fix synchronization/locking problems + * (4) Fix NoRp.SRC support + * + * 2.0.11_G + * (1) Fix PD compliance failures of Ellisys and MQP + * (2) Wait the result of BC1.2 before starting PD policy engine + * (3) Fix compile warnings + * (4) Fix NoRp.SRC support + * + * 2.0.10_G + * (1) fix battery noitifier plug out cause recursive locking detected in + * nh->srcu. + * + * 2.0.9_G + * (1) fix 10k A-to-C legacy cable workaround side effect when + * cable plug in at worakround flow. + * + * 2.0.8_G + * (1) fix timeout thread flow for wakeup pd event thread + * after disable timer first. + * + * 2.0.7_G + * (1) add extract pd source capability pdo defined in + * PD30 v1.1 ECN for pe40 get apdo profile. + * + * 2.0.6_G + * (1) register battery notifier for battery plug out + * avoid TA hardreset 3 times will show charing icon. + * + * 2.0.5_G + * (1) add CONFIG_TYPEC_CAP_NORP_SRC to support + * A-to-C No-Rp cable. + * (2) add handler pd eint with eint mask + * + * 2.0.4_G + * (1) add CONFIG_TCPC_NOTIFIER_LATE_SYNC to + * move irq_enable to late_initcall_sync stage + * to prevent from notifier_supply_num setting wrong. + * + * 2.0.3_G + * (1) use local_irq_XXX to instead raw_local_irq_XXX + * to fix lock prov WARNING + * (2) Remove unnecessary charger detect flow. it does + * not need to switch BC1-2 path on otg_en + * + * 2.0.2_G + * (1) Fix Coverity and check patch issue + * (2) Fix 32-bit project build error + * + * 2.0.1_G + * First released PD3.0 Driver + */ diff --git a/drivers/usb/typec/mmi_tcpc/tcpci_dual_role.c b/drivers/usb/typec/mmi_tcpc/tcpci_dual_role.c new file mode 100644 index 000000000000..4005533592e3 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/tcpci_dual_role.c @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * TCPC Interface for dual role + * + * 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. + */ + +#include +#include +#include +#include +#include +#include "inc/tcpci.h" +#include "inc/tcpci_typec.h" + +#ifdef CONFIG_DUAL_ROLE_USB_INTF +static enum dual_role_property tcpc_dual_role_props[] = { + DUAL_ROLE_PROP_SUPPORTED_MODES, + DUAL_ROLE_PROP_MODE, + DUAL_ROLE_PROP_PR, + DUAL_ROLE_PROP_DR, + DUAL_ROLE_PROP_VCONN_SUPPLY, +}; + +static int tcpc_dual_role_get_prop(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, unsigned int *val) +{ + struct tcpc_device *tcpc = dev_get_drvdata(dual_role->dev.parent); + int ret = 0; + + switch (prop) { + case DUAL_ROLE_PROP_SUPPORTED_MODES: + *val = tcpc->dual_role_supported_modes; + break; + case DUAL_ROLE_PROP_MODE: + *val = tcpc->dual_role_mode; + break; + case DUAL_ROLE_PROP_PR: + *val = tcpc->dual_role_pr; + break; + case DUAL_ROLE_PROP_DR: + *val = tcpc->dual_role_dr; + break; + case DUAL_ROLE_PROP_VCONN_SUPPLY: + *val = tcpc->dual_role_vconn; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int tcpc_dual_role_prop_is_writeable( + struct dual_role_phy_instance *dual_role, enum dual_role_property prop) +{ + int retval = -EINVAL; + struct tcpc_device *tcpc = dev_get_drvdata(dual_role->dev.parent); + + switch (prop) { +#ifdef CONFIG_USB_POWER_DELIVERY + case DUAL_ROLE_PROP_PR: + case DUAL_ROLE_PROP_DR: + case DUAL_ROLE_PROP_VCONN_SUPPLY: +#else + case DUAL_ROLE_PROP_MODE: +#endif /* CONFIG_USB_POWER_DELIVERY */ + if (tcpc->dual_role_supported_modes == + DUAL_ROLE_SUPPORTED_MODES_DFP_AND_UFP) + retval = 1; + break; + default: + break; + } + return retval; +} + +#ifdef CONFIG_USB_POWER_DELIVERY + +static int tcpc_dual_role_set_prop_pr( + struct tcpc_device *tcpc, unsigned int val) +{ + int ret; + uint8_t role; + + switch (val) { + case DUAL_ROLE_PROP_PR_SRC: + role = PD_ROLE_SOURCE; + break; + case DUAL_ROLE_PROP_PR_SNK: + role = PD_ROLE_SINK; + break; + default: + return 0; + } + + if (val == tcpc->dual_role_pr) { + pr_info("%s wrong role (%d->%d)\n", + __func__, tcpc->dual_role_pr, val); + return 0; + } + + ret = tcpm_dpm_pd_power_swap(tcpc, role, NULL); + pr_info("%s power role swap (%d->%d): %d\n", + __func__, tcpc->dual_role_pr, val, ret); + + if (ret == TCPM_ERROR_NO_PD_CONNECTED) { + ret = tcpm_typec_role_swap(tcpc); + pr_info("%s typec role swap (%d->%d): %d\n", + __func__, tcpc->dual_role_pr, val, ret); + } + + return ret; +} + +static int tcpc_dual_role_set_prop_dr( + struct tcpc_device *tcpc, unsigned int val) +{ + int ret; + uint8_t role; + + switch (val) { + case DUAL_ROLE_PROP_DR_HOST: + role = PD_ROLE_DFP; + break; + case DUAL_ROLE_PROP_DR_DEVICE: + role = PD_ROLE_UFP; + break; + default: + return 0; + } + + if (val == tcpc->dual_role_dr) { + pr_info("%s wrong role (%d->%d)\n", + __func__, tcpc->dual_role_dr, val); + return 0; + } + + ret = tcpm_dpm_pd_data_swap(tcpc, role, NULL); + pr_info("%s data role swap (%d->%d): %d\n", + __func__, tcpc->dual_role_dr, val, ret); + + return ret; +} + +static int tcpc_dual_role_set_prop_vconn( + struct tcpc_device *tcpc, unsigned int val) +{ + int ret; + uint8_t role; + + switch (val) { + case DUAL_ROLE_PROP_VCONN_SUPPLY_NO: + role = PD_ROLE_VCONN_OFF; + break; + case DUAL_ROLE_PROP_VCONN_SUPPLY_YES: + role = PD_ROLE_VCONN_ON; + break; + default: + return 0; + } + + if (val == tcpc->dual_role_vconn) { + pr_info("%s wrong role (%d->%d)\n", + __func__, tcpc->dual_role_vconn, val); + return 0; + } + + ret = tcpm_dpm_pd_vconn_swap(tcpc, role, NULL); + pr_info("%s vconn swap (%d->%d): %d\n", + __func__, tcpc->dual_role_vconn, val, ret); + + return ret; +} + +#else /* TypeC Only */ + +static int tcpc_dual_role_set_prop_mode( + struct tcpc_device *tcpc, unsigned int val) +{ + int ret; + + if (val == tcpc->dual_role_mode) { + pr_info("%s wrong role (%d->%d)\n", + __func__, tcpc->dual_role_mode, val); + return 0; + } + + ret = tcpm_typec_role_swap(tcpc); + pr_info("%s typec role swap (%d->%d): %d\n", + __func__, tcpc->dual_role_mode, val, ret); + + return ret; +} + +#endif /* CONFIG_USB_POWER_DELIVERY */ + +static int tcpc_dual_role_set_prop(struct dual_role_phy_instance *dual_role, + enum dual_role_property prop, const unsigned int *val) +{ + struct tcpc_device *tcpc = dev_get_drvdata(dual_role->dev.parent); + + switch (prop) { +#ifdef CONFIG_USB_POWER_DELIVERY + case DUAL_ROLE_PROP_PR: + tcpc_dual_role_set_prop_pr(tcpc, *val); + break; + case DUAL_ROLE_PROP_DR: + tcpc_dual_role_set_prop_dr(tcpc, *val); + break; + case DUAL_ROLE_PROP_VCONN_SUPPLY: + tcpc_dual_role_set_prop_vconn(tcpc, *val); + break; +#else /* TypeC Only */ + case DUAL_ROLE_PROP_MODE: + tcpc_dual_role_set_prop_mode(tcpc, *val); + break; +#endif /* CONFIG_USB_POWER_DELIVERY */ + + default: + break; + } + + return 0; +} + +static void tcpc_get_dual_desc(struct tcpc_device *tcpc) +{ + struct device_node *np = of_find_node_by_name(NULL, tcpc->desc.name); + u32 val; + + if (!np) + return; + + if (of_property_read_u32(np, "tcpc-dual,supported_modes", &val) >= 0) { + if (val > DUAL_ROLE_PROP_SUPPORTED_MODES_TOTAL) + tcpc->dual_role_supported_modes = + DUAL_ROLE_SUPPORTED_MODES_DFP_AND_UFP; + else + tcpc->dual_role_supported_modes = val; + } +} + +int tcpc_dual_role_phy_init( + struct tcpc_device *tcpc) +{ + struct dual_role_phy_desc *dual_desc; + int len; + char *str_name; + + tcpc->dr_usb = devm_kzalloc(&tcpc->dev, + sizeof(*tcpc->dr_usb), GFP_KERNEL); + + dual_desc = devm_kzalloc(&tcpc->dev, sizeof(*dual_desc), GFP_KERNEL); + if (!dual_desc) + return -ENOMEM; + + tcpc_get_dual_desc(tcpc); + + len = strlen(tcpc->desc.name); + str_name = devm_kzalloc(&tcpc->dev, len+11, GFP_KERNEL); + snprintf(str_name, PAGE_SIZE, "dual-role-%s", tcpc->desc.name); + dual_desc->name = str_name; + + dual_desc->properties = tcpc_dual_role_props; + dual_desc->num_properties = ARRAY_SIZE(tcpc_dual_role_props); + dual_desc->get_property = tcpc_dual_role_get_prop; + dual_desc->set_property = tcpc_dual_role_set_prop; + dual_desc->property_is_writeable = tcpc_dual_role_prop_is_writeable; + + tcpc->dr_usb = devm_dual_role_instance_register(&tcpc->dev, dual_desc); + if (IS_ERR(tcpc->dr_usb)) { + dev_err(&tcpc->dev, "tcpc fail to register dual role usb\n"); + return -EINVAL; + } + /* init dual role phy instance property */ + tcpc->dual_role_pr = DUAL_ROLE_PROP_PR_NONE; + tcpc->dual_role_dr = DUAL_ROLE_PROP_DR_NONE; + tcpc->dual_role_mode = DUAL_ROLE_PROP_MODE_NONE; + tcpc->dual_role_vconn = DUAL_ROLE_PROP_VCONN_SUPPLY_NO; + return 0; +} +#endif /* CONFIG_DUAL_ROLE_USB_INTF */ diff --git a/drivers/usb/typec/mmi_tcpc/tcpci_event.c b/drivers/usb/typec/mmi_tcpc/tcpci_event.c new file mode 100644 index 000000000000..4cd346b9b684 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/tcpci_event.c @@ -0,0 +1,1351 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * TCPC Interface for event handler + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "inc/tcpci_event.h" +#include "inc/tcpci_typec.h" +#include "inc/tcpci.h" +#include "inc/pd_policy_engine.h" +#include "inc/pd_dpm_core.h" + +#ifdef CONFIG_USB_PD_POSTPONE_VDM +static void postpone_vdm_event(struct tcpc_device *tcpc) +{ + /* + * Postpone VDM retry event due to the retry reason + * maybe interrupt by some PD event .... + */ + + struct pd_event *vdm_event = &tcpc->pd_vdm_event; + + if (tcpc->pd_pending_vdm_event && vdm_event->pd_msg) { + tcpc->pd_postpone_vdm_timeout = false; + tcpc_restart_timer(tcpc, PD_PE_VDM_POSTPONE); + } +} +#endif /* CONFIG_USB_PD_POSTPONE_VDM */ + +struct pd_msg *__pd_alloc_msg(struct tcpc_device *tcpc) +{ + int i; + uint8_t mask; + + for (i = 0, mask = 1; i < PD_MSG_BUF_SIZE; i++, mask <<= 1) { + if ((mask & tcpc->pd_msg_buffer_allocated) == 0) { + tcpc->pd_msg_buffer_allocated |= mask; + return tcpc->pd_msg_buffer + i; + } + } + + PD_ERR("pd_alloc_msg failed\n"); + PD_BUG_ON(true); + + return (struct pd_msg *)NULL; +} + +struct pd_msg *pd_alloc_msg(struct tcpc_device *tcpc) +{ + struct pd_msg *pd_msg = NULL; + + mutex_lock(&tcpc->access_lock); + pd_msg = __pd_alloc_msg(tcpc); + mutex_unlock(&tcpc->access_lock); + + return pd_msg; +} + +static void __pd_free_msg(struct tcpc_device *tcpc, struct pd_msg *pd_msg) +{ + int index = pd_msg - tcpc->pd_msg_buffer; + uint8_t mask = 1 << index; + + PD_BUG_ON((mask & tcpc->pd_msg_buffer_allocated) == 0); + tcpc->pd_msg_buffer_allocated &= (~mask); +} + +static void __pd_free_event( + struct tcpc_device *tcpc, struct pd_event *pd_event) +{ + if (pd_event->pd_msg) { + __pd_free_msg(tcpc, pd_event->pd_msg); + pd_event->pd_msg = NULL; + } +} + +void pd_free_msg(struct tcpc_device *tcpc, struct pd_msg *pd_msg) +{ + mutex_lock(&tcpc->access_lock); + __pd_free_msg(tcpc, pd_msg); + mutex_unlock(&tcpc->access_lock); +} + +void pd_free_event(struct tcpc_device *tcpc, struct pd_event *pd_event) +{ + mutex_lock(&tcpc->access_lock); + __pd_free_event(tcpc, pd_event); + mutex_unlock(&tcpc->access_lock); +} + +/*----------------------------------------------------------------------------*/ + +static bool __pd_get_event( + struct tcpc_device *tcpc, struct pd_event *pd_event) +{ + int index = 0; + + if (tcpc->pd_event_count <= 0) + return false; + + tcpc->pd_event_count--; + + *pd_event = + tcpc->pd_event_ring_buffer[tcpc->pd_event_head_index]; + + if (tcpc->pd_event_count) { + index = tcpc->pd_event_head_index + 1; + index %= PD_EVENT_BUF_SIZE; + } + tcpc->pd_event_head_index = index; + return true; +} + +bool pd_get_event(struct tcpc_device *tcpc, struct pd_event *pd_event) +{ + bool ret; + + mutex_lock(&tcpc->access_lock); + ret = __pd_get_event(tcpc, pd_event); + mutex_unlock(&tcpc->access_lock); + return ret; +} + +static bool __pd_put_event(struct tcpc_device *tcpc, + const struct pd_event *pd_event, bool from_port_partner) +{ + int index; + +#ifdef CONFIG_USB_PD_POSTPONE_OTHER_VDM + if (from_port_partner) + postpone_vdm_event(tcpc); +#endif /* CONFIG_USB_PD_POSTPONE_OTHER_VDM */ + + if (tcpc->pd_event_count >= PD_EVENT_BUF_SIZE) { + PD_ERR("pd_put_event failed\n"); + return false; + } + + index = (tcpc->pd_event_head_index + tcpc->pd_event_count); + index %= PD_EVENT_BUF_SIZE; + + tcpc->pd_event_count++; + tcpc->pd_event_ring_buffer[index] = *pd_event; + + atomic_inc(&tcpc->pending_event); + wake_up(&tcpc->event_wait_que); + return true; +} + +bool pd_put_event(struct tcpc_device *tcpc, const struct pd_event *pd_event, + bool from_port_partner) +{ + bool ret; + + mutex_lock(&tcpc->access_lock); + ret = __pd_put_event(tcpc, pd_event, from_port_partner); + mutex_unlock(&tcpc->access_lock); + + return ret; +} + +/*----------------------------------------------------------------------------*/ + +static inline void pd_get_attention_event( + struct tcpc_device *tcpc, struct pd_event *pd_event) +{ + struct pd_event attention_evt = { + .event_type = PD_EVT_PD_MSG, + .msg = PD_DATA_VENDOR_DEF, + .pd_msg = NULL, + }; + + *pd_event = attention_evt; + pd_event->pd_msg = __pd_alloc_msg(tcpc); + + if (pd_event->pd_msg == NULL) + return; + + tcpc->pd_pending_vdm_attention = false; + *pd_event->pd_msg = tcpc->pd_attention_vdm_msg; +} + +static inline bool pd_check_vdm_state_ready(struct pd_port *pd_port) +{ + switch (pd_port->pe_vdm_state) { + case PE_SNK_READY: + case PE_SRC_READY: +#ifdef CONFIG_USB_PD_CUSTOM_DBGACC + case PE_DBG_READY: +#endif /* CONFIG_USB_PD_CUSTOM_DBGACC */ + return true; + + default: + return false; + } +} + +bool pd_get_vdm_event(struct tcpc_device *tcpc, struct pd_event *pd_event) +{ + struct pd_event delay_evt = { + .event_type = PD_EVT_CTRL_MSG, + .msg = PD_CTRL_GOOD_CRC, + .pd_msg = NULL, + }; + + struct pd_event reset_evt = { + .event_type = PD_EVT_PE_MSG, + .msg = PD_PE_VDM_RESET, + .pd_msg = NULL, + }; + + struct pd_event discard_evt = { + .event_type = PD_EVT_HW_MSG, + .msg = PD_HW_TX_DISCARD, + .pd_msg = NULL, + }; + + struct pd_event *vdm_event = &tcpc->pd_vdm_event; + + if (tcpc->pd_pending_vdm_discard) { + mutex_lock(&tcpc->access_lock); + *pd_event = discard_evt; + tcpc->pd_pending_vdm_discard = false; + mutex_unlock(&tcpc->access_lock); + return true; + } + + if (tcpc->pd_pending_vdm_event) { + if (vdm_event->pd_msg && !tcpc->pd_postpone_vdm_timeout) + return false; + + mutex_lock(&tcpc->access_lock); + if (tcpc->pd_pending_vdm_good_crc) { + *pd_event = delay_evt; + tcpc->pd_pending_vdm_good_crc = false; + } else if (tcpc->pd_pending_vdm_reset) { + *pd_event = reset_evt; + tcpc->pd_pending_vdm_reset = false; + } else { + *pd_event = *vdm_event; + tcpc->pd_pending_vdm_event = false; + } + + mutex_unlock(&tcpc->access_lock); + return true; + } + + if (tcpc->pd_pending_vdm_attention + && pd_check_vdm_state_ready(&tcpc->pd_port)) { + mutex_lock(&tcpc->access_lock); + pd_get_attention_event(tcpc, pd_event); + mutex_unlock(&tcpc->access_lock); + return true; + } + + return false; +} + +static inline bool reset_pe_vdm_state( + struct tcpc_device *tcpc, uint32_t vdm_hdr) +{ + bool vdm_reset = false; + struct pd_port *pd_port = &tcpc->pd_port; + + if (PD_VDO_SVDM(vdm_hdr)) { + if (PD_VDO_CMDT(vdm_hdr) == CMDT_INIT) + vdm_reset = true; + } else { + if (pd_port->data_role == PD_ROLE_UFP) + vdm_reset = true; + } + + if (vdm_reset) + tcpc->pd_pending_vdm_reset = true; + + return vdm_reset; +} + +static inline bool pd_is_init_attention_event( + struct tcpc_device *tcpc, struct pd_event *pd_event) +{ + uint32_t vdm_hdr = pd_event->pd_msg->payload[0]; + + if (!PD_VDO_SVDM(vdm_hdr)) + return false; + + if ((PD_VDO_CMDT(vdm_hdr) == CMDT_INIT) && + PD_VDO_CMD(vdm_hdr) == CMD_ATTENTION) { + return true; + } + + return false; +} + +bool pd_put_vdm_event(struct tcpc_device *tcpc, + struct pd_event *pd_event, bool from_port_partner) +{ + bool ignore_evt = false; + struct pd_msg *pd_msg = pd_event->pd_msg; + + mutex_lock(&tcpc->access_lock); + + if (from_port_partner && + pd_is_init_attention_event(tcpc, pd_event)) { + TCPC_DBG("AttEvt\n"); + ignore_evt = true; + tcpc->pd_pending_vdm_attention = true; + tcpc->pd_attention_vdm_msg = *pd_msg; + + atomic_inc(&tcpc->pending_event); + wake_up(&tcpc->event_wait_que); + } + + if (tcpc->pd_pending_vdm_event && (!ignore_evt)) { + /* If message from port partner, we have to overwrite it */ + /* If message from TCPM, we will reset_vdm later */ + ignore_evt = !from_port_partner; + + if (from_port_partner) { + if (pd_event_ctrl_msg_match( + &tcpc->pd_vdm_event, + PD_CTRL_GOOD_CRC)) { + TCPC_DBG2("PostponeVDM GoodCRC\n"); + tcpc->pd_pending_vdm_good_crc = true; + } + + __pd_free_event(tcpc, &tcpc->pd_vdm_event); + } + } + + if (ignore_evt) { + __pd_free_event(tcpc, pd_event); + mutex_unlock(&tcpc->access_lock); + return false; + } + + tcpc->pd_vdm_event = *pd_event; + tcpc->pd_pending_vdm_event = true; + tcpc->pd_postpone_vdm_timeout = true; + + if (from_port_partner) { + + PD_BUG_ON(pd_msg == NULL); + tcpc->pd_last_vdm_msg = *pd_msg; + reset_pe_vdm_state(tcpc, pd_msg->payload[0]); + +#ifdef CONFIG_USB_PD_POSTPONE_FIRST_VDM + postpone_vdm_event(tcpc); + mutex_unlock(&tcpc->access_lock); + return true; +#endif /* CONFIG_USB_PD_POSTPONE_FIRST_VDM */ + } + + atomic_inc(&tcpc->pending_event); + wake_up(&tcpc->event_wait_que); + mutex_unlock(&tcpc->access_lock); + + return true; +} + +bool pd_put_last_vdm_event(struct tcpc_device *tcpc) +{ + struct pd_msg *pd_msg = &tcpc->pd_last_vdm_msg; + struct pd_event *vdm_event = &tcpc->pd_vdm_event; + + mutex_lock(&tcpc->access_lock); + + tcpc->pd_pending_vdm_discard = true; + atomic_inc(&tcpc->pending_event); + wake_up(&tcpc->event_wait_que); + + /* If the last VDM event isn't INIT event, don't put it again */ + if (!reset_pe_vdm_state(tcpc, pd_msg->payload[0])) { + mutex_unlock(&tcpc->access_lock); + return true; + } + + vdm_event->event_type = PD_EVT_HW_MSG; + vdm_event->msg = PD_HW_RETRY_VDM; + + if (tcpc->pd_pending_vdm_event) + __pd_free_event(tcpc, &tcpc->pd_vdm_event); + + vdm_event->pd_msg = __pd_alloc_msg(tcpc); + + if (vdm_event->pd_msg == NULL) { + mutex_unlock(&tcpc->access_lock); + return false; + } + + *vdm_event->pd_msg = *pd_msg; + tcpc->pd_pending_vdm_event = true; + tcpc->pd_postpone_vdm_timeout = true; + +#ifdef CONFIG_USB_PD_POSTPONE_RETRY_VDM + postpone_vdm_event(tcpc); +#endif /* CONFIG_USB_PD_POSTPONE_RETRY_VDM */ + + mutex_unlock(&tcpc->access_lock); + return true; +} + +/*----------------------------------------------------------------------------*/ + +static bool __pd_get_deferred_tcp_event( + struct tcpc_device *tcpc, struct tcp_dpm_event *tcp_event) +{ + int index = 0; + + if (tcpc->tcp_event_count <= 0) + return false; + + tcpc->tcp_event_count--; + + *tcp_event = + tcpc->tcp_event_ring_buffer[tcpc->tcp_event_head_index]; + + if (tcpc->tcp_event_count) { + index = tcpc->tcp_event_head_index + 1; + index %= TCP_EVENT_BUF_SIZE; + } + tcpc->tcp_event_head_index = index; + return true; +} + +bool pd_get_deferred_tcp_event( + struct tcpc_device *tcpc, struct tcp_dpm_event *tcp_event) +{ + bool ret; + + mutex_lock(&tcpc->access_lock); + ret = __pd_get_deferred_tcp_event(tcpc, tcp_event); +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + if (tcpc->tcp_event_count) + tcpc_restart_timer(tcpc, PD_TIMER_DEFERRED_EVT); + else + tcpc_disable_timer(tcpc, PD_TIMER_DEFERRED_EVT); +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ +#endif /* CONFIG_USB_PD_REV30 */ + mutex_unlock(&tcpc->access_lock); + + return ret; +} + +static bool __pd_put_deferred_tcp_event( + struct tcpc_device *tcpc, const struct tcp_dpm_event *tcp_event) +{ + int index; + + index = (tcpc->tcp_event_head_index + tcpc->tcp_event_count); + index %= TCP_EVENT_BUF_SIZE; + + tcpc->tcp_event_count++; + tcpc->tcp_event_ring_buffer[index] = *tcp_event; + + atomic_inc(&tcpc->pending_event); + wake_up(&tcpc->event_wait_que); + return true; +} + +bool pd_put_deferred_tcp_event( + struct tcpc_device *tcpc, const struct tcp_dpm_event *tcp_event) +{ + bool ret = true; + struct pd_port *pd_port = &tcpc->pd_port; + + mutex_lock(&pd_port->pd_lock); + mutex_lock(&tcpc->access_lock); + + if (!tcpc->pd_pe_running || tcpc->pd_wait_pe_idle) { + PD_ERR("pd_put_tcp_event failed0\n"); + ret = false; + goto unlock_out; + } + + if (tcpc->tcp_event_count >= TCP_EVENT_BUF_SIZE) { + PD_ERR("pd_put_tcp_event failed1\n"); + ret = false; + goto unlock_out; + } + + if (tcpc->pd_wait_hard_reset_complete) { + PD_ERR("pd_put_tcp_event failed2\n"); + ret = false; + goto unlock_out; + } + + switch (tcp_event->event_id) { + case TCP_DPM_EVT_DISCOVER_CABLE: + case TCP_DPM_EVT_CABLE_SOFTRESET: + dpm_reaction_set(pd_port, + DPM_REACTION_DYNAMIC_VCONN | + DPM_REACTION_VCONN_STABLE_DELAY); + break; + } + + ret = __pd_put_deferred_tcp_event(tcpc, tcp_event); +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + if (ret) + pd_port->pe_data.pd_traffic_idle = false; + if (tcpc->tcp_event_count == 1) + tcpc_enable_timer(tcpc, PD_TIMER_DEFERRED_EVT); +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ +#endif /* CONFIG_USB_PD_REV30 */ + + dpm_reaction_set_ready_once(pd_port); + +unlock_out: + mutex_unlock(&tcpc->access_lock); + mutex_unlock(&pd_port->pd_lock); + + return ret; +} + +void pd_notify_tcp_vdm_event_2nd_result(struct pd_port *pd_port, uint8_t ret) +{ +#ifdef CONFIG_USB_PD_TCPM_CB_2ND + struct tcp_dpm_event *tcp_event = &pd_port->tcp_event; + + if (pd_port->tcp_event_id_2nd == TCP_DPM_EVT_UNKONW) + return; + + if (pd_port->tcp_event_id_2nd < TCP_DPM_EVT_VDM_COMMAND || + pd_port->tcp_event_id_2nd >= TCP_DPM_EVT_IMMEDIATELY) + return; + + if (tcp_event->event_cb != NULL) + tcp_event->event_cb(pd_port->tcpc, ret, tcp_event); + + pd_port->tcp_event_id_2nd = TCP_DPM_EVT_UNKONW; +#endif /* CONFIG_USB_PD_TCPM_CB_2ND */ +} + +void pd_notify_tcp_event_2nd_result(struct pd_port *pd_port, int ret) +{ +#ifdef CONFIG_USB_PD_TCPM_CB_2ND + struct tcp_dpm_event *tcp_event = &pd_port->tcp_event; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_port->tcp_event_id_2nd == TCP_DPM_EVT_UNKONW) + return; + + switch (ret) { + case TCP_DPM_RET_DROP_SENT_SRESET: + if (pd_port->tcp_event_id_2nd == TCP_DPM_EVT_SOFTRESET + && pd_port->tcp_event_drop_reset_once) { + pd_port->tcp_event_drop_reset_once = false; + return; + } + break; + case TCP_DPM_RET_DROP_SENT_HRESET: + if (pd_port->tcp_event_id_2nd == TCP_DPM_EVT_HARD_RESET + && pd_port->tcp_event_drop_reset_once) { + pd_port->tcp_event_drop_reset_once = false; + return; + } + break; + case TCP_DPM_RET_SUCCESS: + /* Ignore VDM */ + if (pd_port->tcp_event_id_2nd >= TCP_DPM_EVT_VDM_COMMAND + && pd_port->tcp_event_id_2nd < TCP_DPM_EVT_IMMEDIATELY) + return; + + break; + } + + TCPC_DBG2("tcp_event_2nd:evt%d=%d\n", + pd_port->tcp_event_id_2nd, ret); + + if (tcp_event->event_cb != NULL) + tcp_event->event_cb(tcpc, ret, tcp_event); + + pd_port->tcp_event_id_2nd = TCP_DPM_EVT_UNKONW; +#endif /* CONFIG_USB_PD_TCPM_CB_2ND */ +} + +void pd_notify_tcp_event_1st_result(struct pd_port *pd_port, int ret) +{ + bool cb = true; + struct tcp_dpm_event *tcp_event = &pd_port->tcp_event; + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (pd_port->tcp_event_id_1st == TCP_DPM_EVT_UNKONW) + return; + + TCPC_DBG2("tcp_event_1st:evt%d=%d\n", + pd_port->tcp_event_id_1st, ret); + +#ifdef CONFIG_USB_PD_TCPM_CB_2ND + if (ret == TCP_DPM_RET_SENT) { + cb = false; + pd_port->tcp_event_id_2nd = tcp_event->event_id; + } +#endif /* CONFIG_USB_PD_TCPM_CB_2ND */ + + if (cb && tcp_event->event_cb != NULL) + tcp_event->event_cb(tcpc, ret, tcp_event); + + pd_port->tcp_event_id_1st = TCP_DPM_EVT_UNKONW; +} + +static void __tcp_event_buf_reset( + struct tcpc_device *tcpc, uint8_t reason) +{ + struct tcp_dpm_event tcp_event; + + pd_notify_tcp_event_2nd_result(&tcpc->pd_port, reason); + + while (__pd_get_deferred_tcp_event(tcpc, &tcp_event)) { + if (tcp_event.event_cb != NULL) + tcp_event.event_cb(tcpc, reason, &tcp_event); + } +} + +void pd_notify_tcp_event_buf_reset(struct pd_port *pd_port, uint8_t reason) +{ + struct tcpc_device *tcpc = pd_port->tcpc; + + pd_notify_tcp_event_1st_result(pd_port, reason); + + mutex_lock(&tcpc->access_lock); + __tcp_event_buf_reset(tcpc, reason); + mutex_unlock(&tcpc->access_lock); +} + +/*----------------------------------------------------------------------------*/ + +static void __pd_event_buf_reset(struct tcpc_device *tcpc, uint8_t reason) +{ + struct pd_event pd_event; + + tcpc->pd_hard_reset_event_pending = false; + while (__pd_get_event(tcpc, &pd_event)) + __pd_free_event(tcpc, &pd_event); + + if (tcpc->pd_pending_vdm_event) { + __pd_free_event(tcpc, &tcpc->pd_vdm_event); + tcpc->pd_pending_vdm_event = false; + } + + tcpc->pd_pending_vdm_reset = false; + tcpc->pd_pending_vdm_good_crc = false; + tcpc->pd_pending_vdm_attention = false; + tcpc->pd_pending_vdm_discard = false; + + __tcp_event_buf_reset(tcpc, reason); + /* PD_BUG_ON(tcpc->pd_msg_buffer_allocated != 0); */ +} + +void pd_event_buf_reset(struct tcpc_device *tcpc) +{ + mutex_lock(&tcpc->access_lock); + __pd_event_buf_reset(tcpc, TCP_DPM_RET_DROP_CC_DETACH); + mutex_unlock(&tcpc->access_lock); +} + +/*----------------------------------------------------------------------------*/ + +static inline bool __pd_put_hw_event( + struct tcpc_device *tcpc, uint8_t hw_event) +{ + struct pd_event evt = { + .event_type = PD_EVT_HW_MSG, + .msg = hw_event, + .pd_msg = NULL, + }; + + return __pd_put_event(tcpc, &evt, false); +} + +static inline bool __pd_put_pe_event( + struct tcpc_device *tcpc, uint8_t pe_event) +{ + struct pd_event evt = { + .event_type = PD_EVT_PE_MSG, + .msg = pe_event, + .pd_msg = NULL, + }; + + return __pd_put_event(tcpc, &evt, false); +} + +bool __pd_put_cc_attached_event( + struct tcpc_device *tcpc, uint8_t type) +{ + struct pd_event evt = { + .event_type = PD_EVT_HW_MSG, + .msg = PD_HW_CC_ATTACHED, + .msg_sec = type, + .pd_msg = NULL, + }; + + switch (type) { + case TYPEC_ATTACHED_SNK: + case TYPEC_ATTACHED_SRC: + case TYPEC_ATTACHED_DBGACC_SNK: + tcpc->pd_pe_running = true; + tcpc->pd_wait_pe_idle = false; + break; + default: + break; + } + + return __pd_put_event(tcpc, &evt, false); +} + +bool pd_put_cc_attached_event( + struct tcpc_device *tcpc, uint8_t type) +{ + bool ret = false; +#ifdef CONFIG_USB_POWER_DELIVERY +#ifdef CONFIG_USB_PD_WAIT_BC12 + int rv = 0; + union power_supply_propval val = {.intval = 0}; +#endif /* CONFIG_USB_PD_WAIT_BC12 */ +#endif /* CONFIG_USB_POWER_DELIVERY */ + + mutex_lock(&tcpc->access_lock); + +#ifdef CONFIG_USB_POWER_DELIVERY +#ifdef CONFIG_USB_PD_WAIT_BC12 + rv = power_supply_get_property(tcpc->usb_psy, + POWER_SUPPLY_PROP_REAL_TYPE, &val); + if ((type == TYPEC_ATTACHED_SNK || type == TYPEC_ATTACHED_DBGACC_SNK) && + (rv < 0 || val.intval == POWER_SUPPLY_TYPE_UNKNOWN)) { + tcpc->pd_wait_bc12_count = 1; + tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_PD_WAIT_BC12); + mutex_unlock(&tcpc->access_lock); + return ret; + } + tcpc->pd_wait_bc12_count = 0; + tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_PD_WAIT_BC12); +#endif /* CONFIG_USB_PD_WAIT_BC12 */ +#endif /* CONFIG_USB_POWER_DELIVERY */ + + ret = __pd_put_cc_attached_event(tcpc, type); + + mutex_unlock(&tcpc->access_lock); + + return ret; +} + +void pd_put_cc_detached_event(struct tcpc_device *tcpc) +{ + mutex_lock(&tcpc->access_lock); + +#ifdef CONFIG_USB_POWER_DELIVERY +#ifdef CONFIG_USB_PD_WAIT_BC12 + tcpc->pd_wait_bc12_count = 0; + tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_PD_WAIT_BC12); +#endif /* CONFIG_USB_PD_WAIT_BC12 */ +#endif /* CONFIG_USB_POWER_DELIVERY */ + + tcpci_notify_hard_reset_state( + tcpc, TCP_HRESET_RESULT_FAIL); + + __pd_event_buf_reset(tcpc, TCP_DPM_RET_DROP_CC_DETACH); + __pd_put_hw_event(tcpc, PD_HW_CC_DETACHED); + + tcpc->pd_wait_pe_idle = true; + tcpc->pd_pe_running = false; + tcpc->pd_wait_pr_swap_complete = false; + tcpc->pd_hard_reset_event_pending = false; + tcpc->pd_wait_vbus_once = PD_WAIT_VBUS_DISABLE; + tcpc->pd_bist_mode = PD_BIST_MODE_DISABLE; + tcpc->pd_ping_event_pending = false; + +#ifdef CONFIG_USB_PD_DIRECT_CHARGE + tcpc->pd_during_direct_charge = false; +#endif /* CONFIG_USB_PD_DIRECT_CHARGE */ + +#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD + tcpc->pd_discard_pending = false; +#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */ + + mutex_unlock(&tcpc->access_lock); +} + +void pd_put_recv_hard_reset_event(struct tcpc_device *tcpc) +{ + mutex_lock(&tcpc->access_lock); + + tcpci_notify_hard_reset_state( + tcpc, TCP_HRESET_SIGNAL_RECV); + + tcpc->pd_transmit_state = PD_TX_STATE_HARD_RESET; + + if ((!tcpc->pd_hard_reset_event_pending) && + (!tcpc->pd_wait_pe_idle) && + tcpc->pd_pe_running) { + __pd_event_buf_reset(tcpc, TCP_DPM_RET_DROP_RECV_HRESET); + __pd_put_hw_event(tcpc, PD_HW_RECV_HARD_RESET); + tcpc->pd_bist_mode = PD_BIST_MODE_DISABLE; + tcpc->pd_hard_reset_event_pending = true; + tcpc->pd_ping_event_pending = false; + +#ifdef CONFIG_USB_PD_DIRECT_CHARGE + tcpc->pd_during_direct_charge = false; +#endif /* CONFIG_USB_PD_DIRECT_CHARGE */ + } + +#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD + tcpc->pd_discard_pending = false; +#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */ + + mutex_unlock(&tcpc->access_lock); +} + +void pd_put_sent_hard_reset_event(struct tcpc_device *tcpc) +{ + mutex_lock(&tcpc->access_lock); + + if (tcpc->pd_wait_hard_reset_complete) + __pd_event_buf_reset(tcpc, TCP_DPM_RET_DROP_SENT_HRESET); + else + TCPC_DBG2("[HReset] Unattached\n"); + + tcpc->pd_transmit_state = PD_TX_STATE_GOOD_CRC; + __pd_put_pe_event(tcpc, PD_PE_HARD_RESET_COMPLETED); + + mutex_unlock(&tcpc->access_lock); +} + +bool pd_put_pd_msg_event(struct tcpc_device *tcpc, struct pd_msg *pd_msg) +{ + uint32_t cnt, cmd, extend; + +#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD + bool discard_pending = false; +#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */ + + struct pd_event evt = { + .event_type = PD_EVT_PD_MSG, + .pd_msg = pd_msg, + }; + + cnt = PD_HEADER_CNT(pd_msg->msg_hdr); + cmd = PD_HEADER_TYPE(pd_msg->msg_hdr); + extend = PD_HEADER_EXT(pd_msg->msg_hdr); + + /* bist mode */ + mutex_lock(&tcpc->access_lock); + if (tcpc->pd_bist_mode != PD_BIST_MODE_DISABLE) { + TCPC_DBG2("BIST_MODE_RX\n"); + __pd_free_event(tcpc, &evt); + mutex_unlock(&tcpc->access_lock); + return 0; + } + +#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD + if (tcpc->pd_discard_pending && + (pd_msg->frame_type == TCPC_TX_SOP) && + (tcpc->tcpc_flags & TCPC_FLAGS_RETRY_CRC_DISCARD)) { + + discard_pending = true; + tcpc->pd_discard_pending = false; + + if ((cmd == PD_CTRL_GOOD_CRC) && (cnt == 0)) { + TCPC_DBG2("RETRANSMIT\n"); + __pd_free_event(tcpc, &evt); + mutex_unlock(&tcpc->access_lock); + + /* TODO: check it later */ + tcpc_disable_timer(tcpc, PD_TIMER_DISCARD); + tcpci_retransmit(tcpc); + return 0; + } + } +#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */ + +#ifdef CONFIG_USB_PD_DROP_REPEAT_PING + if (cnt == 0 && cmd == PD_CTRL_PING) { + /* reset ping_test_mode only if cc_detached */ + if (!tcpc->pd_ping_event_pending) { + TCPC_INFO("ping_test_mode\n"); + tcpc->pd_ping_event_pending = true; + tcpci_set_bist_test_mode(tcpc, true); + } else { + __pd_free_event(tcpc, &evt); + mutex_unlock(&tcpc->access_lock); + return 0; + } + } +#endif /* CONFIG_USB_PD_DROP_REPEAT_PING */ + + if (cnt != 0 && cmd == PD_DATA_BIST && extend == 0) + tcpc->pd_bist_mode = PD_BIST_MODE_EVENT_PENDING; + + mutex_unlock(&tcpc->access_lock); + +#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD + if (discard_pending) { + tcpc_disable_timer(tcpc, PD_TIMER_DISCARD); + pd_put_hw_event(tcpc, PD_HW_TX_FAILED); + } +#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */ + + if (cnt != 0 && cmd == PD_DATA_VENDOR_DEF) + return pd_put_vdm_event(tcpc, &evt, true); + + if (!pd_put_event(tcpc, &evt, true)) { + pd_free_event(tcpc, &evt); + return false; + } + + return true; +} + +static void pd_report_vbus_present(struct tcpc_device *tcpc) +{ + tcpc->pd_wait_vbus_once = PD_WAIT_VBUS_DISABLE; + __pd_put_hw_event(tcpc, PD_HW_VBUS_PRESENT); +} + +void pd_put_vbus_changed_event(struct tcpc_device *tcpc, bool from_ic) +{ + int vbus_valid; + bool postpone_vbus_present = false; + + mutex_lock(&tcpc->access_lock); + vbus_valid = tcpci_check_vbus_valid(tcpc); + + switch (tcpc->pd_wait_vbus_once) { + case PD_WAIT_VBUS_VALID_ONCE: + if (vbus_valid) { +#if CONFIG_USB_PD_VBUS_PRESENT_TOUT + postpone_vbus_present = from_ic; +#endif /* CONFIG_USB_PD_VBUS_PRESENT_TOUT */ + if (!postpone_vbus_present) + pd_report_vbus_present(tcpc); + } + break; + + case PD_WAIT_VBUS_INVALID_ONCE: + if (!vbus_valid) { + tcpc->pd_wait_vbus_once = PD_WAIT_VBUS_DISABLE; + __pd_put_hw_event(tcpc, PD_HW_VBUS_ABSENT); + } + break; + } + mutex_unlock(&tcpc->access_lock); + +#if CONFIG_USB_PD_VBUS_PRESENT_TOUT + if (postpone_vbus_present) + tcpc_enable_timer(tcpc, PD_TIMER_VBUS_PRESENT); +#endif /* CONFIG_USB_PD_VBUS_PRESENT_TOUT */ +} + +void pd_put_vbus_safe0v_event(struct tcpc_device *tcpc) +{ +#ifdef CONFIG_USB_PD_SAFE0V_TIMEOUT + tcpc_disable_timer(tcpc, PD_TIMER_VSAFE0V_TOUT); +#endif /* CONFIG_USB_PD_SAFE0V_TIMEOUT */ + + mutex_lock(&tcpc->access_lock); + if (tcpc->pd_wait_vbus_once == PD_WAIT_VBUS_SAFE0V_ONCE) { + tcpc->pd_wait_vbus_once = PD_WAIT_VBUS_DISABLE; + tcpci_enable_force_discharge(tcpc, false, 0); + __pd_put_hw_event(tcpc, PD_HW_VBUS_SAFE0V); + } + mutex_unlock(&tcpc->access_lock); +} + +void pd_put_vbus_stable_event(struct tcpc_device *tcpc) +{ + mutex_lock(&tcpc->access_lock); + if (tcpc->pd_wait_vbus_once == PD_WAIT_VBUS_STABLE_ONCE) { + tcpc->pd_wait_vbus_once = PD_WAIT_VBUS_DISABLE; +#ifdef CONFIG_USB_PD_SRC_HIGHCAP_POWER + tcpci_enable_force_discharge(tcpc, false, 0); +#endif /* CONFIG_USB_PD_SRC_HIGHCAP_POWER */ + __pd_put_hw_event(tcpc, PD_HW_VBUS_STABLE); + } + mutex_unlock(&tcpc->access_lock); +} + +void pd_put_vbus_present_event(struct tcpc_device *tcpc) +{ + mutex_lock(&tcpc->access_lock); + pd_report_vbus_present(tcpc); + mutex_unlock(&tcpc->access_lock); +} + +/* ---- PD Notify TCPC ---- */ + +void pd_try_put_pe_idle_event(struct pd_port *pd_port) +{ + struct tcpc_device *tcpc = pd_port->tcpc; + + mutex_lock(&tcpc->access_lock); + if (tcpc->pd_transmit_state < PD_TX_STATE_WAIT) + __pd_put_pe_event(tcpc, PD_PE_IDLE); + mutex_unlock(&tcpc->access_lock); +} + +void pd_notify_pe_idle(struct pd_port *pd_port) +{ + bool notify_pe_idle = false; + struct tcpc_device *tcpc = pd_port->tcpc; + + mutex_lock(&tcpc->access_lock); + if (tcpc->pd_wait_pe_idle) { + notify_pe_idle = true; + tcpc->pd_wait_pe_idle = false; + } + + tcpc->pd_pe_running = false; + mutex_unlock(&tcpc->access_lock); + + pd_update_connect_state(pd_port, PD_CONNECT_NONE); + pd_update_vdm_verify_state(pd_port, false); + + if (notify_pe_idle) + tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_PE_IDLE); +} + +void pd_notify_pe_wait_vbus_once(struct pd_port *pd_port, int wait_evt) +{ + struct tcpc_device *tcpc = pd_port->tcpc; + + mutex_lock(&tcpc->access_lock); + tcpc->pd_wait_vbus_once = wait_evt; + mutex_unlock(&tcpc->access_lock); + + switch (wait_evt) { + case PD_WAIT_VBUS_VALID_ONCE: + case PD_WAIT_VBUS_INVALID_ONCE: + pd_put_vbus_changed_event(tcpc, false); + break; + + case PD_WAIT_VBUS_SAFE0V_ONCE: +#ifdef CONFIG_TCPC_VSAFE0V_DETECT + if (tcpci_check_vsafe0v(tcpc, true)) { + pd_put_vbus_safe0v_event(tcpc); + break; + } +#else + pd_enable_timer(pd_port, PD_TIMER_VSAFE0V_DELAY); +#endif /* CONFIG_TCPC_VSAFE0V_DETECT */ + +#ifdef CONFIG_USB_PD_SAFE0V_TIMEOUT + pd_enable_timer(pd_port, PD_TIMER_VSAFE0V_TOUT); +#endif /* CONFIG_USB_PD_SAFE0V_TIMEOUT */ + + mutex_lock(&tcpc->access_lock); + tcpci_enable_force_discharge(tcpc, true, 0); + mutex_unlock(&tcpc->access_lock); + break; + } +} + +void pd_notify_pe_error_recovery(struct pd_port *pd_port) +{ + struct tcpc_device *tcpc = pd_port->tcpc; + + mutex_lock(&tcpc->access_lock); + + tcpci_notify_hard_reset_state( + tcpc, TCP_HRESET_RESULT_FAIL); + + tcpc->pd_wait_pr_swap_complete = false; + __tcp_event_buf_reset(tcpc, TCP_DPM_RET_DROP_ERROR_REOCVERY); + mutex_unlock(&tcpc->access_lock); + + tcpci_lock_typec(tcpc); + tcpc_typec_error_recovery(tcpc); + tcpci_unlock_typec(tcpc); +} + +#ifdef CONFIG_USB_PD_RECV_HRESET_COUNTER +void pd_notify_pe_over_recv_hreset(struct pd_port *pd_port) +{ + struct tcpc_device *tcpc = pd_port->tcpc; + + mutex_lock(&tcpc->access_lock); + tcpc->pd_wait_hard_reset_complete = false; + tcpc->pd_wait_pr_swap_complete = false; + mutex_unlock(&tcpc->access_lock); + + disable_irq(chip->irq); + tcpci_lock_typec(tcpc); + tcpci_init(tcpc, true); + tcpci_set_cc(tcpc, TYPEC_CC_OPEN); + tcpci_unlock_typec(tcpc); + tcpci_set_rx_enable(tcpc, PD_RX_CAP_PE_IDLE); + tcpc_enable_timer(tcpc, TYPEC_TIMER_ERROR_RECOVERY); + enable_irq_wake(chip->irq); +} +#endif /* CONFIG_USB_PD_RECV_HRESET_COUNTER */ + +void pd_notify_pe_transit_to_default(struct pd_port *pd_port) +{ + struct tcpc_device *tcpc = pd_port->tcpc; + + pd_update_connect_state(pd_port, PD_CONNECT_HARD_RESET); + + mutex_lock(&tcpc->access_lock); + tcpc->pd_hard_reset_event_pending = false; + tcpc->pd_wait_pr_swap_complete = false; + tcpc->pd_bist_mode = PD_BIST_MODE_DISABLE; + +#ifdef CONFIG_USB_PD_DIRECT_CHARGE + tcpc->pd_during_direct_charge = false; +#endif /* CONFIG_USB_PD_DIRECT_CHARGE */ + mutex_unlock(&tcpc->access_lock); +} + +void pd_notify_pe_hard_reset_completed(struct pd_port *pd_port) +{ + struct tcpc_device *tcpc = pd_port->tcpc; + + mutex_lock(&tcpc->access_lock); + tcpci_notify_hard_reset_state( + tcpc, TCP_HRESET_RESULT_DONE); + mutex_unlock(&tcpc->access_lock); +} + +void pd_notify_pe_send_hard_reset(struct pd_port *pd_port) +{ + struct tcpc_device *tcpc = pd_port->tcpc; + + mutex_lock(&tcpc->access_lock); + tcpc->pd_transmit_state = PD_TX_STATE_WAIT_HARD_RESET; + tcpci_notify_hard_reset_state(tcpc, TCP_HRESET_SIGNAL_SEND); + mutex_unlock(&tcpc->access_lock); +} + +void pd_notify_pe_execute_pr_swap(struct pd_port *pd_port, bool start_swap) +{ + struct tcpc_device *tcpc = pd_port->tcpc; + + pd_port->pe_data.during_swap = start_swap; + mutex_lock(&tcpc->access_lock); + tcpc->pd_wait_pr_swap_complete = true; + mutex_unlock(&tcpc->access_lock); +} + +void pd_notify_pe_cancel_pr_swap(struct pd_port *pd_port) +{ + struct tcpc_device *tcpc = pd_port->tcpc; + + if (!tcpc->pd_wait_pr_swap_complete) + return; + + pd_port->pe_data.during_swap = false; + mutex_lock(&tcpc->access_lock); + tcpc->pd_wait_pr_swap_complete = false; + mutex_unlock(&tcpc->access_lock); + + /* + * CC_Alert was ignored if pd_wait_pr_swap_complete = true + * So enable PDDebounce to detect CC_Again after cancel_pr_swap. + */ + + tcpc_enable_timer(tcpc, TYPEC_TIMER_PDDEBOUNCE); + + if (!tcpci_check_vbus_valid(tcpc) + && (pd_port->request_v >= 4000)) { + TCPC_DBG("cancel_pr_swap_vbus=0\n"); + pd_put_tcp_pd_event(pd_port, TCP_DPM_EVT_ERROR_RECOVERY); + } +} + +void pd_notify_pe_reset_protocol(struct pd_port *pd_port) +{ + struct tcpc_device *tcpc = pd_port->tcpc; + + mutex_lock(&tcpc->access_lock); + tcpc->pd_wait_pr_swap_complete = false; + mutex_unlock(&tcpc->access_lock); +} + +void pd_noitfy_pe_bist_mode(struct pd_port *pd_port, uint8_t mode) +{ + struct tcpc_device *tcpc = pd_port->tcpc; + + mutex_lock(&tcpc->access_lock); + tcpc->pd_bist_mode = mode; + mutex_unlock(&tcpc->access_lock); +} + +void pd_notify_pe_transmit_msg( + struct pd_port *pd_port, uint8_t type) +{ + struct tcpc_device *tcpc = pd_port->tcpc; + + mutex_lock(&tcpc->access_lock); + tcpc->pd_transmit_state = type; + mutex_unlock(&tcpc->access_lock); +} + +void pd_notify_pe_pr_changed(struct pd_port *pd_port) +{ + struct tcpc_device *tcpc = pd_port->tcpc; + + /* Check mutex later, actually, + * typec layer will ignore all cc-change during PR-SWAP + */ + + /* mutex_lock(&tcpc->access_lock); */ + tcpc_typec_handle_pe_pr_swap(tcpc); + /* mutex_unlock(&tcpc->access_lock); */ +} + +void pd_notify_pe_snk_explicit_contract(struct pd_port *pd_port) +{ +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + struct pe_data *pe_data = &pd_port->pe_data; + struct tcpc_device *tcpc = pd_port->tcpc; + + if (pe_data->explicit_contract) + return; + + if (tcpc->typec_remote_rp_level == TYPEC_CC_VOLT_SNK_3_0) + pe_data->pd_traffic_control = PD_SINK_TX_OK; + else + pe_data->pd_traffic_control = PD_SINK_TX_NG; + +#ifdef CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP + if (pe_data->pd_traffic_control == PD_SINK_TX_OK) { + pe_data->pd_traffic_control = PD_SINK_TX_START; + pd_restart_timer(pd_port, PD_TIMER_SNK_FLOW_DELAY); + } +#endif /* CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP */ +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ +} + +void pd_notify_pe_src_explicit_contract(struct pd_port *pd_port) +{ + uint8_t pull = 0; + + struct pe_data *pe_data = &pd_port->pe_data; + struct tcpc_device *tcpc = pd_port->tcpc; + + if (pe_data->explicit_contract) { +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID +#ifdef CONFIG_USB_PD_REV30_SRC_FLOW_DELAY_STARTUP + if (pd_check_rev30(pd_port) && + (pe_data->pd_traffic_control == PD_SOURCE_TX_START)) + pd_restart_timer(pd_port, PD_TIMER_SINK_TX); +#endif /* CONFIG_USB_PD_REV30_SRC_FLOW_DELAY_STARTUP */ +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ + return; + } + + if (tcpc->typec_local_rp_level == TYPEC_RP_DFT) + pull = TYPEC_CC_RP_1_5; + +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + if (pd_check_rev30(pd_port)) { + pull = TYPEC_CC_RP_3_0; + +#ifdef CONFIG_USB_PD_REV30_SRC_FLOW_DELAY_STARTUP + pe_data->pd_traffic_control = PD_SOURCE_TX_START; + pd_enable_timer(pd_port, PD_TIMER_SINK_TX); +#else + pe_data->pd_traffic_control = PD_SINK_TX_OK; +#endif /* CONFIG_USB_PD_REV30_SRC_FLOW_DELAY_STARTUP */ + } +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ + + if (pull) { + tcpci_lock_typec(tcpc); + tcpci_set_cc(tcpc, pull); + tcpci_unlock_typec(tcpc); + } +} + +#ifdef CONFIG_USB_PD_DIRECT_CHARGE +void pd_notify_pe_direct_charge(struct pd_port *pd_port, bool en) +{ + struct tcpc_device *tcpc = pd_port->tcpc; + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + /* TODO: check it later */ + if (pd_port->request_apdo) + en = true; +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + + mutex_lock(&tcpc->access_lock); + tcpc->pd_during_direct_charge = en; + mutex_unlock(&tcpc->access_lock); +} +#endif /* CONFIG_USB_PD_DIRECT_CHARGE */ + +/* ---- init ---- */ +static int tcpc_event_thread_fn(void *data) +{ + struct tcpc_device *tcpc = data; + struct sched_param sch_param = {.sched_priority = MAX_RT_PRIO - 2}; + int ret = 0; + + /* set_user_nice(current, -20); */ + /* current->flags |= PF_NOFREEZE;*/ + + sched_setscheduler(current, SCHED_FIFO, &sch_param); + + while (true) { + ret = wait_event_interruptible(tcpc->event_wait_que, + atomic_read(&tcpc->pending_event) || + kthread_should_stop()); + if (kthread_should_stop() || ret) { + dev_notice(&tcpc->dev, "%s exits(%d)\n", __func__, ret); + break; + } + do { + atomic_dec_if_positive(&tcpc->pending_event); + } while (pd_policy_engine_run(tcpc) && !kthread_should_stop()); + } + + return 0; +} + +int tcpci_event_init(struct tcpc_device *tcpc) +{ + init_waitqueue_head(&tcpc->event_wait_que); + atomic_set(&tcpc->pending_event, 0); + tcpc->event_task = kthread_run(tcpc_event_thread_fn, tcpc, + "tcpc_event_%s", tcpc->desc.name); + + return 0; +} + +int tcpci_event_deinit(struct tcpc_device *tcpc) +{ + if (tcpc->event_task != NULL) + kthread_stop(tcpc->event_task); + + return 0; +} diff --git a/drivers/usb/typec/mmi_tcpc/tcpci_timer.c b/drivers/usb/typec/mmi_tcpc/tcpci_timer.c new file mode 100644 index 000000000000..b32516ad8b57 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/tcpci_timer.c @@ -0,0 +1,1403 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * TCPC Interface for timer handler + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "inc/tcpci.h" +#include "inc/tcpci_timer.h" +#include "inc/tcpci_typec.h" + +#define RT_MASK64(i) (((uint64_t)1) << i) + +#define TIMEOUT_VAL(val) (val * 1000) +#define TIMEOUT_RANGE(min, max) ((min * 4000 + max * 1000)/5) +#define TIMEOUT_VAL_US(val) (val) + +/* Debug message Macro */ +#if TCPC_TIMER_DBG_EN +#define TCPC_TIMER_DBG(tcpc, id) \ +{ \ + RT_DBG_INFO("Trigger %s\n", tcpc_timer_name[id]); \ +} +#else +#define TCPC_TIMER_DBG(format, args...) +#endif /* TCPC_TIMER_DBG_EN */ + +#if TCPC_TIMER_INFO_EN +#define TCPC_TIMER_EN_DBG(tcpc, id) \ +{ \ + RT_DBG_INFO("Enable %s\n", tcpc_timer_name[id]); \ +} +#else +#define TCPC_TIMER_EN_DBG(format, args...) +#endif /* TCPC_TIMER_INFO_EN */ + +static inline void tcpc_clear_timer_tick(struct tcpc_device *tcpc, int nr); + +static inline uint64_t tcpc_get_timer_enable_mask(struct tcpc_device *tcpc) +{ + uint64_t data; + unsigned long flags; + + down(&tcpc->timer_enable_mask_lock); + local_irq_save(flags); + data = tcpc->timer_enable_mask; + local_irq_restore(flags); + up(&tcpc->timer_enable_mask_lock); + + return data; +} + +static inline void tcpc_clear_timer_enable_mask( + struct tcpc_device *tcpc, int nr) +{ + unsigned long flags; + + down(&tcpc->timer_enable_mask_lock); + local_irq_save(flags); + tcpc->timer_enable_mask &= ~RT_MASK64(nr); + + spin_lock(&tcpc->timer_tick_lock); + tcpc->timer_tick &= ~RT_MASK64(nr); + spin_unlock(&tcpc->timer_tick_lock); + + local_irq_restore(flags); + up(&tcpc->timer_enable_mask_lock); +} + +static inline void tcpc_set_timer_enable_mask( + struct tcpc_device *tcpc, int nr) +{ + unsigned long flags; + + down(&tcpc->timer_enable_mask_lock); + local_irq_save(flags); + tcpc->timer_enable_mask |= RT_MASK64(nr); + local_irq_restore(flags); + up(&tcpc->timer_enable_mask_lock); +} + +static inline uint64_t tcpc_get_timer_tick(struct tcpc_device *tcpc) +{ + uint64_t data; + unsigned long flags; + + spin_lock_irqsave(&tcpc->timer_tick_lock, flags); + data = tcpc->timer_tick; + spin_unlock_irqrestore(&tcpc->timer_tick_lock, flags); + + return data; +} + +static inline void tcpc_clear_timer_tick(struct tcpc_device *tcpc, int nr) +{ + unsigned long flags; + + spin_lock_irqsave(&tcpc->timer_tick_lock, flags); + tcpc->timer_tick &= ~RT_MASK64(nr); + spin_unlock_irqrestore(&tcpc->timer_tick_lock, flags); +} + +static inline void tcpc_set_timer_tick(struct tcpc_device *tcpc, int nr) +{ + unsigned long flags; + + spin_lock_irqsave(&tcpc->timer_tick_lock, flags); + tcpc->timer_tick |= RT_MASK64(nr); + spin_unlock_irqrestore(&tcpc->timer_tick_lock, flags); +} + +#if TCPC_TIMER_DBG_EN || TCPC_TIMER_INFO_EN +static const char *const tcpc_timer_name[] = { +#ifdef CONFIG_USB_POWER_DELIVERY + "PD_TIMER_DISCOVER_ID", + "PD_TIMER_BIST_CONT_MODE", + "PD_TIMER_HARD_RESET_COMPLETE", + "PD_TIMER_NO_RESPONSE", + "PD_TIMER_PS_HARD_RESET", + "PD_TIMER_PS_SOURCE_OFF", + "PD_TIMER_PS_SOURCE_ON", + "PD_TIMER_PS_TRANSITION", + "PD_TIMER_SENDER_RESPONSE", + "PD_TIMER_SINK_REQUEST", + "PD_TIMER_SINK_WAIT_CAP", + "PD_TIMER_SOURCE_CAPABILITY", + "PD_TIMER_SOURCE_START", + "PD_TIMER_VCONN_ON", +#ifdef CONFIG_USB_PD_VCONN_STABLE_DELAY + "PD_TIMER_VCONN_STABLE", +#endif /* CONFIG_USB_PD_VCONN_STABLE_DELAY */ + "PD_TIMER_VDM_MODE_ENTRY", + "PD_TIMER_VDM_MODE_EXIT", + "PD_TIMER_VDM_RESPONSE", + "PD_TIMER_SOURCE_TRANSITION", + "PD_TIMER_SRC_RECOVER", +#ifdef CONFIG_USB_PD_REV30 + "PD_TIMER_CK_NOT_SUPPORTED", +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + "PD_TIMER_SINK_TX", +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ +#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE + "PD_TIMER_SOURCE_PPS_TIMEOUT", +#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */ +#endif /* CONFIG_USB_PD_REV30 */ + +/* PD_TIMER (out of spec ) */ + "PD_TIMER_VSAFE0V_DELAY", + "PD_TIMER_VSAFE0V_TOUT", + "PD_TIMER_DISCARD", + "PD_TIMER_VBUS_STABLE", + "PD_TIMER_VBUS_PRESENT", + "PD_TIMER_UVDM_RESPONSE", + "PD_TIMER_DFP_FLOW_DELAY", + "PD_TIMER_UFP_FLOW_DELAY", + "PD_TIMER_VCONN_READY", + "PD_PE_VDM_POSTPONE", + +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + "PD_TIMER_DEFERRED_EVT", +#ifdef CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP + "PD_TIMER_SNK_FLOW_DELAY", +#endif /* CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP */ +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ +#endif /* CONFIG_USB_PD_REV30 */ + + "PD_TIMER_PE_IDLE_TOUT", +#endif /* CONFIG_USB_POWER_DELIVERY */ + +/* TYPEC_RT_TIMER (out of spec) */ + "TYPEC_RT_TIMER_SAFE0V_DELAY", + "TYPEC_RT_TIMER_SAFE0V_TOUT", + "TYPEC_RT_TIMER_ROLE_SWAP_START", + "TYPEC_RT_TIMER_ROLE_SWAP_STOP", + "TYPEC_RT_TIMER_STATE_CHANGE", + "TYPEC_RT_TIMER_NOT_LEGACY", + "TYPEC_RT_TIMER_LEGACY_STABLE", + "TYPEC_RT_TIMER_LEGACY_RECYCLE", + "TYPEC_RT_TIMER_AUTO_DISCHARGE", + "TYPEC_RT_TIMER_LOW_POWER_MODE", +#ifdef CONFIG_USB_POWER_DELIVERY + "TYPEC_RT_TIMER_PE_IDLE", +#ifdef CONFIG_USB_PD_WAIT_BC12 + "TYPEC_RT_TIMER_PD_WAIT_BC12", +#endif /* CONFIG_USB_PD_WAIT_BC12 */ +#endif /* CONFIG_USB_POWER_DELIVERY */ + "TYPEC_TIMER_ERROR_RECOVERY", +/* TYPEC-TRY-TIMER */ + "TYPEC_TRY_TIMER_DRP_TRY", + "TYPEC_TRY_TIMER_DRP_TRYWAIT", +/* TYPEC-DEBOUNCE-TIMER */ + "TYPEC_TIMER_CCDEBOUNCE", + "TYPEC_TIMER_PDDEBOUNCE", +#ifdef CONFIG_COMPATIBLE_APPLE_TA + "TYPEC_TIMER_APPLE_CC_OPEN", +#endif /* CONFIG_COMPATIBLE_APPLE_TA */ + "TYPEC_TIMER_TRYCCDEBOUNCE", + "TYPEC_TIMER_SRCDISCONNECT", + "TYPEC_TIMER_DRP_SRC_TOGGLE", +#ifdef CONFIG_TYPEC_CAP_NORP_SRC + "TYPEC_TIMER_NORP_SRC", +#endif /* CONFIG_TYPEC_CAP_NORP_SRC */ +}; +#endif /* TCPC_TIMER_DBG_EN || TCPC_TIMER_INFO_EN */ +/* CONFIG_USB_PD_SAFE0V_DELAY */ +#ifdef CONFIG_TCPC_VSAFE0V_DETECT +#define PD_TIMER_VSAFE0V_DLY_TOUT 50 +#else +/* #ifndef CONFIG_TCPC_VSAFE0V_DETECT (equal timeout)*/ +#define PD_TIMER_VSAFE0V_DLY_TOUT 400 +#endif /* CONFIG_TCPC_VSAFE0V_DETECT */ + +/* CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_DELAY */ +#ifdef CONFIG_TCPC_VSAFE0V_DETECT +#define TYPEC_RT_TIMER_SAFE0V_DLY_TOUT 35 +#else +#define TYPEC_RT_TIMER_SAFE0V_DLY_TOUT 100 +#endif + +#define DECL_TCPC_TIMEOUT(enum, ms) \ + TIMEOUT_VAL(ms) + +#define DECL_TCPC_TIMEOUT_US(enum, us) \ + TIMEOUT_VAL_US(us) + +#define DECL_TCPC_TIMEOUT_RANGE(enum, min, max) \ + TIMEOUT_RANGE(min, max) + +static const uint32_t tcpc_timer_timeout[PD_TIMER_NR] = { +#ifdef CONFIG_USB_POWER_DELIVERY +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_DISCOVER_ID, 40, 50), +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_BIST_CONT_MODE, 30, 60), +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_HARD_RESET_COMPLETE, 4, 5), +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_NO_RESPONSE, 4500, 5500), +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_PS_HARD_RESET, 25, 35), +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_PS_SOURCE_OFF, 750, 920), +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_PS_SOURCE_ON, 390, 480), + +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_PS_TRANSITION, 450, 550), +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_SENDER_RESPONSE, 24, 30), +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_SINK_REQUEST, 100, 100), +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_SINK_WAIT_CAP, 310, 620), +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_SOURCE_CAPABILITY, 100, 200), + +DECL_TCPC_TIMEOUT(PD_TIMER_SOURCE_START, 20), +DECL_TCPC_TIMEOUT(PD_TIMER_VCONN_ON, 100), + +#ifdef CONFIG_USB_PD_VCONN_STABLE_DELAY +DECL_TCPC_TIMEOUT(PD_TIMER_VCONN_STABLE, 50), +#endif /* CONFIG_USB_PD_VCONN_STABLE_DELAY */ + +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_VDM_MODE_ENTRY, 40, 50), +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_VDM_MODE_EXIT, 40, 50), +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_VDM_RESPONSE, 24, 30), + +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_SOURCE_TRANSITION, 25, 35), +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_SRC_RECOVER, 660, 1000), + +#ifdef CONFIG_USB_PD_REV30 +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_CK_NOT_SUPPORTED, 40, 50), +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID +DECL_TCPC_TIMEOUT_RANGE(PD_TIMER_SINK_TX, 16, 20), +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ +#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE +DECL_TCPC_TIMEOUT(PD_TIMER_SOURCE_PPS_TIMEOUT, 14000), +#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */ +#endif /* CONFIG_USB_PD_REV30 */ + +/* PD_TIMER (out of spec) */ +DECL_TCPC_TIMEOUT(PD_TIMER_VSAFE0V_DELAY, PD_TIMER_VSAFE0V_DLY_TOUT), +DECL_TCPC_TIMEOUT(PD_TIMER_VSAFE0V_TOUT, 650), +DECL_TCPC_TIMEOUT(PD_TIMER_DISCARD, 3), + +DECL_TCPC_TIMEOUT(PD_TIMER_VBUS_STABLE, + CONFIG_USB_PD_VBUS_STABLE_TOUT), +DECL_TCPC_TIMEOUT(PD_TIMER_VBUS_PRESENT, + CONFIG_USB_PD_VBUS_PRESENT_TOUT), +DECL_TCPC_TIMEOUT(PD_TIMER_UVDM_RESPONSE, + CONFIG_USB_PD_CUSTOM_VDM_TOUT), +DECL_TCPC_TIMEOUT(PD_TIMER_DFP_FLOW_DELAY, + CONFIG_USB_PD_DFP_FLOW_DLY), +DECL_TCPC_TIMEOUT(PD_TIMER_UFP_FLOW_DELAY, + CONFIG_USB_PD_UFP_FLOW_DLY), +DECL_TCPC_TIMEOUT(PD_TIMER_VCONN_READY, + CONFIG_USB_PD_VCONN_READY_TOUT), + +DECL_TCPC_TIMEOUT_US(PD_PE_VDM_POSTPONE, 3000), + +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID +DECL_TCPC_TIMEOUT(PD_TIMER_DEFERRED_EVT, 5000), +#ifdef CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP +DECL_TCPC_TIMEOUT(PD_TIMER_SNK_FLOW_DELAY, + CONFIG_USB_PD_UFP_FLOW_DLY), +#endif /* CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP */ +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ +#endif /* CONFIG_USB_PD_REV30 */ + +DECL_TCPC_TIMEOUT(PD_TIMER_PE_IDLE_TOUT, 10), +#endif /* CONFIG_USB_POWER_DELIVERY */ + +/* TYPEC_RT_TIMER (out of spec) */ +DECL_TCPC_TIMEOUT(TYPEC_RT_TIMER_SAFE0V_DELAY, + TYPEC_RT_TIMER_SAFE0V_DLY_TOUT), +DECL_TCPC_TIMEOUT(TYPEC_RT_TIMER_SAFE0V_TOUT, 650), + +DECL_TCPC_TIMEOUT(TYPEC_RT_TIMER_ROLE_SWAP_START, 20), +DECL_TCPC_TIMEOUT(TYPEC_RT_TIMER_ROLE_SWAP_STOP, + CONFIG_TYPEC_CAP_ROLE_SWAP_TOUT), + +DECL_TCPC_TIMEOUT(TYPEC_RT_TIMER_STATE_CHANGE, 50), +DECL_TCPC_TIMEOUT(TYPEC_RT_TIMER_NOT_LEGACY, 5000), +DECL_TCPC_TIMEOUT(TYPEC_RT_TIMER_LEGACY_STABLE, 30*1000), +DECL_TCPC_TIMEOUT(TYPEC_RT_TIMER_LEGACY_RECYCLE, 300*1000), + +DECL_TCPC_TIMEOUT(TYPEC_RT_TIMER_DISCHARGE, CONFIG_TYPEC_CAP_DISCHARGE_TOUT), +DECL_TCPC_TIMEOUT(TYPEC_RT_TIMER_LOW_POWER_MODE, 500), + +#ifdef CONFIG_USB_POWER_DELIVERY +DECL_TCPC_TIMEOUT(TYPEC_RT_TIMER_PE_IDLE, 1), +#ifdef CONFIG_USB_PD_WAIT_BC12 +DECL_TCPC_TIMEOUT(TYPEC_RT_TIMER_PD_WAIT_BC12, 50), +#endif /* CONFIG_USB_PD_WAIT_BC12 */ +#endif /* CONFIG_USB_POWER_DELIVERY */ +DECL_TCPC_TIMEOUT_RANGE(TYPEC_TIMER_ERROR_RECOVERY, 25, 25), + +/* TYPEC-TRY-TIMER */ +DECL_TCPC_TIMEOUT_RANGE(TYPEC_TRY_TIMER_DRP_TRY, 75, 150), +DECL_TCPC_TIMEOUT_RANGE(TYPEC_TRY_TIMER_DRP_TRYWAIT, 400, 800), + +/* TYPEC-DEBOUNCE-TIMER */ +DECL_TCPC_TIMEOUT_RANGE(TYPEC_TIMER_CCDEBOUNCE, 100, 200), +DECL_TCPC_TIMEOUT_RANGE(TYPEC_TIMER_PDDEBOUNCE, 10, 10), +#ifdef CONFIG_COMPATIBLE_APPLE_TA +DECL_TCPC_TIMEOUT_RANGE(TYPEC_TIMER_APPLE_CC_OPEN, 200, 200), +#endif /* CONFIG_COMPATIBLE_APPLE_TA */ +DECL_TCPC_TIMEOUT_RANGE(TYPEC_TIMER_TRYCCDEBOUNCE, 10, 20), +DECL_TCPC_TIMEOUT_RANGE(TYPEC_TIMER_SRCDISCONNECT, 0, 20), +DECL_TCPC_TIMEOUT(TYPEC_TIMER_DRP_SRC_TOGGLE, 60), +#ifdef CONFIG_TYPEC_CAP_NORP_SRC +DECL_TCPC_TIMEOUT(TYPEC_TIMER_NORP_SRC, 300), +#endif /* CONFIG_TYPEC_CAP_NORP_SRC */ +}; + +typedef enum hrtimer_restart (*tcpc_hrtimer_call)(struct hrtimer *timer); + +#ifdef CONFIG_USB_POWER_DELIVERY +static inline void on_pe_timer_timeout( + struct tcpc_device *tcpc, uint32_t timer_id) +{ + struct pd_event pd_event = {0}; + int rv = 0; + uint32_t chip_id = 0; + + pd_event.event_type = PD_EVT_TIMER_MSG; + pd_event.msg = timer_id; + pd_event.pd_msg = NULL; + + tcpc_disable_timer(tcpc, timer_id); + + switch (timer_id) { + case PD_TIMER_VDM_MODE_ENTRY: + case PD_TIMER_VDM_MODE_EXIT: + case PD_TIMER_VDM_RESPONSE: + case PD_TIMER_UVDM_RESPONSE: + pd_put_vdm_event(tcpc, &pd_event, false); + break; + + case PD_TIMER_VSAFE0V_DELAY: + pd_put_vbus_safe0v_event(tcpc); + break; + +#ifdef CONFIG_USB_PD_SAFE0V_TIMEOUT + case PD_TIMER_VSAFE0V_TOUT: + TCPC_INFO("VSafe0V TOUT (%d)\n", tcpc->vbus_level); + if (!tcpci_check_vbus_valid_from_ic(tcpc)) + pd_put_vbus_safe0v_event(tcpc); + break; +#endif /* CONFIG_USB_PD_SAFE0V_TIMEOUT */ + +#ifdef CONFIG_USB_PD_RETRY_CRC_DISCARD + case PD_TIMER_DISCARD: + tcpc->pd_discard_pending = false; + pd_put_hw_event(tcpc, PD_HW_TX_FAILED); + break; +#endif /* CONFIG_USB_PD_RETRY_CRC_DISCARD */ + +#if CONFIG_USB_PD_VBUS_STABLE_TOUT + case PD_TIMER_VBUS_STABLE: + pd_put_vbus_stable_event(tcpc); + break; +#endif /* CONFIG_USB_PD_VBUS_STABLE_TOUT */ + +#if CONFIG_USB_PD_VBUS_PRESENT_TOUT + case PD_TIMER_VBUS_PRESENT: + pd_put_vbus_present_event(tcpc); + break; +#endif /* CONFIG_USB_PD_VBUS_PRESENT_TOUT */ + + case PD_PE_VDM_POSTPONE: + tcpc->pd_postpone_vdm_timeout = true; + atomic_inc(&tcpc->pending_event); + wake_up(&tcpc->event_wait_que); + break; + + case PD_TIMER_PE_IDLE_TOUT: + TCPC_INFO("pe_idle tout\n"); + pd_put_pe_event(&tcpc->pd_port, PD_PE_IDLE); + break; + + case PD_TIMER_HARD_RESET_COMPLETE: + rv = tcpci_get_chip_id(tcpc, &chip_id); + if (!rv && (SC2150A_DID == chip_id)) { + pd_put_sent_hard_reset_event(tcpc); + } + break; + + default: + pd_put_event(tcpc, &pd_event, false); + break; + } +} +#endif /* CONFIG_USB_POWER_DELIVERY */ + +#define TCPC_TIMER_TRIGGER() do \ +{ \ + tcpc_set_timer_tick(tcpc, index); \ + wake_up(&tcpc->timer_wait_que); \ +} while (0) + +#ifdef CONFIG_USB_POWER_DELIVERY +static enum hrtimer_restart tcpc_timer_bist_cont_mode(struct hrtimer *timer) +{ + int index = PD_TIMER_BIST_CONT_MODE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_discover_id(struct hrtimer *timer) +{ + int index = PD_TIMER_DISCOVER_ID; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart + tcpc_timer_hard_reset_complete(struct hrtimer *timer) +{ + int index = PD_TIMER_HARD_RESET_COMPLETE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_no_response(struct hrtimer *timer) +{ + int index = PD_TIMER_NO_RESPONSE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_ps_hard_reset(struct hrtimer *timer) +{ + int index = PD_TIMER_PS_HARD_RESET; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_ps_source_off(struct hrtimer *timer) +{ + int index = PD_TIMER_PS_SOURCE_OFF; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_ps_source_on(struct hrtimer *timer) +{ + int index = PD_TIMER_PS_SOURCE_ON; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_ps_transition(struct hrtimer *timer) +{ + int index = PD_TIMER_PS_TRANSITION; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_sender_response(struct hrtimer *timer) +{ + int index = PD_TIMER_SENDER_RESPONSE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_sink_request(struct hrtimer *timer) +{ + int index = PD_TIMER_SINK_REQUEST; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_sink_wait_cap(struct hrtimer *timer) +{ + int index = PD_TIMER_SINK_WAIT_CAP; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_source_capability(struct hrtimer *timer) +{ + int index = PD_TIMER_SOURCE_CAPABILITY; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_source_start(struct hrtimer *timer) +{ + int index = PD_TIMER_SOURCE_START; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_vconn_on(struct hrtimer *timer) +{ + int index = PD_TIMER_VCONN_ON; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +#ifdef CONFIG_USB_PD_VCONN_STABLE_DELAY +static enum hrtimer_restart tcpc_timer_vconn_stable(struct hrtimer *timer) +{ + int index = PD_TIMER_VCONN_STABLE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} +#endif /* CONFIG_USB_PD_VCONN_STABLE_DELAY */ + +static enum hrtimer_restart tcpc_timer_vdm_mode_entry(struct hrtimer *timer) +{ + int index = PD_TIMER_VDM_MODE_ENTRY; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_vdm_mode_exit(struct hrtimer *timer) +{ + int index = PD_TIMER_VDM_MODE_EXIT; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_vdm_response(struct hrtimer *timer) +{ + int index = PD_TIMER_VDM_RESPONSE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_source_transition(struct hrtimer *timer) +{ + int index = PD_TIMER_SOURCE_TRANSITION; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_src_recover(struct hrtimer *timer) +{ + int index = PD_TIMER_SRC_RECOVER; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +#ifdef CONFIG_USB_PD_REV30 +static enum hrtimer_restart tcpc_timer_ck_no_support(struct hrtimer *timer) +{ + int index = PD_TIMER_CK_NOT_SUPPORTED; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID +static enum hrtimer_restart tcpc_timer_sink_tx(struct hrtimer *timer) +{ + int index = PD_TIMER_SINK_TX; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ + +#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE +static enum hrtimer_restart tcpc_timer_source_pps(struct hrtimer *timer) +{ + int index = PD_TIMER_SOURCE_PPS_TIMEOUT; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} +#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */ + +#endif /* CONFIG_USB_PD_REV30 */ + +/* PD_TIMER (out of spec )*/ +static enum hrtimer_restart tcpc_timer_vsafe0v_delay(struct hrtimer *timer) +{ + int index = PD_TIMER_VSAFE0V_DELAY; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_vsafe0v_tout(struct hrtimer *timer) +{ + int index = PD_TIMER_VSAFE0V_TOUT; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_pd_discard(struct hrtimer *timer) +{ + int index = PD_TIMER_DISCARD; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_vbus_stable(struct hrtimer *timer) +{ + int index = PD_TIMER_VBUS_STABLE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_vbus_present(struct hrtimer *timer) +{ + int index = PD_TIMER_VBUS_PRESENT; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_uvdm_response(struct hrtimer *timer) +{ + int index = PD_TIMER_UVDM_RESPONSE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_dfp_flow_delay(struct hrtimer *timer) +{ + int index = PD_TIMER_DFP_FLOW_DELAY; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_ufp_flow_delay(struct hrtimer *timer) +{ + int index = PD_TIMER_UFP_FLOW_DELAY; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_vconn_ready(struct hrtimer *timer) +{ + int index = PD_TIMER_VCONN_READY; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_vdm_postpone(struct hrtimer *timer) +{ + int index = PD_PE_VDM_POSTPONE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID +static enum hrtimer_restart tcpc_timer_deferred_evt(struct hrtimer *timer) +{ + int index = PD_TIMER_DEFERRED_EVT; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +#ifdef CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP +static enum hrtimer_restart tcpc_timer_snk_flow_delay(struct hrtimer *timer) +{ + int index = PD_TIMER_SNK_FLOW_DELAY; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} +#endif /* CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP */ +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ +#endif /* CONFIG_USB_PD_REV30 */ + +static enum hrtimer_restart tcpc_timer_pe_idle_tout(struct hrtimer *timer) +{ + int index = PD_TIMER_PE_IDLE_TOUT; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +#endif /* CONFIG_USB_POWER_DELIVERY */ + +/* TYPEC_RT_TIMER (out of spec ) */ + +static enum hrtimer_restart tcpc_timer_rt_vsafe0v_delay(struct hrtimer *timer) +{ + int index = TYPEC_RT_TIMER_SAFE0V_DELAY; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_rt_vsafe0v_tout(struct hrtimer *timer) +{ + int index = TYPEC_RT_TIMER_SAFE0V_TOUT; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_rt_role_swap_start(struct hrtimer *timer) +{ + int index = TYPEC_RT_TIMER_ROLE_SWAP_START; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_rt_role_swap_stop(struct hrtimer *timer) +{ + int index = TYPEC_RT_TIMER_ROLE_SWAP_STOP; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_rt_legacy(struct hrtimer *timer) +{ + int index = TYPEC_RT_TIMER_STATE_CHANGE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_rt_not_legacy(struct hrtimer *timer) +{ + int index = TYPEC_RT_TIMER_NOT_LEGACY; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_rt_legacy_stable(struct hrtimer *timer) +{ + int index = TYPEC_RT_TIMER_LEGACY_STABLE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_rt_legacy_recycle(struct hrtimer *timer) +{ + int index = TYPEC_RT_TIMER_LEGACY_RECYCLE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_rt_discharge(struct hrtimer *timer) +{ + int index = TYPEC_RT_TIMER_DISCHARGE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_rt_low_power_mode(struct hrtimer *timer) +{ + int index = TYPEC_RT_TIMER_LOW_POWER_MODE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +#ifdef CONFIG_USB_POWER_DELIVERY +static enum hrtimer_restart tcpc_timer_rt_pe_idle(struct hrtimer *timer) +{ + int index = TYPEC_RT_TIMER_PE_IDLE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +#ifdef CONFIG_USB_PD_WAIT_BC12 +static enum hrtimer_restart tcpc_timer_rt_pd_wait_bc12(struct hrtimer *timer) +{ + int index = TYPEC_RT_TIMER_PD_WAIT_BC12; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} +#endif /* CONFIG_USB_PD_WAIT_BC12 */ +#endif /* CONFIG_USB_POWER_DELIVERY */ + +/* TYPEC-TRY-TIMER */ +static enum hrtimer_restart tcpc_timer_try_drp_try(struct hrtimer *timer) +{ + int index = TYPEC_TRY_TIMER_DRP_TRY; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_try_drp_trywait(struct hrtimer *timer) +{ + int index = TYPEC_TRY_TIMER_DRP_TRYWAIT; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +/* TYPEC-DEBOUNCE-TIMER */ +static enum hrtimer_restart tcpc_timer_ccdebounce(struct hrtimer *timer) +{ + int index = TYPEC_TIMER_CCDEBOUNCE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_pddebounce(struct hrtimer *timer) +{ + int index = TYPEC_TIMER_PDDEBOUNCE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +#ifdef CONFIG_COMPATIBLE_APPLE_TA +static enum hrtimer_restart tcpc_timer_apple_cc_open(struct hrtimer *timer) +{ + int index = TYPEC_TIMER_APPLE_CC_OPEN; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} +#endif /* CONFIG_COMPATIBLE_APPLE_TA */ + +static enum hrtimer_restart tcpc_timer_tryccdebounce(struct hrtimer *timer) +{ + int index = TYPEC_TIMER_TRYCCDEBOUNCE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_srcdisconnect(struct hrtimer *timer) +{ + int index = TYPEC_TIMER_SRCDISCONNECT; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_error_recovery(struct hrtimer *timer) +{ + int index = TYPEC_TIMER_ERROR_RECOVERY; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +static void wake_up_work_func(struct work_struct *work) +{ + struct tcpc_device *tcpc = container_of( + work, struct tcpc_device, wake_up_work.work); + + mutex_lock(&tcpc->typec_lock); + + TCPC_INFO("%s\n", __func__); +#ifdef CONFIG_TYPEC_WAKEUP_ONCE_LOW_DUTY + tcpc->typec_wakeup_once = true; +#endif /* CONFIG_TYPEC_WAKEUP_ONCE_LOW_DUTY */ + + tcpc_typec_enter_lpm_again(tcpc); + + mutex_unlock(&tcpc->typec_lock); + __pm_relax(tcpc->wakeup_wake_lock); +} + +static enum alarmtimer_restart + tcpc_timer_wakeup(struct alarm *alarm, ktime_t now) +{ + struct tcpc_device *tcpc = + container_of(alarm, struct tcpc_device, wake_up_timer); + + __pm_wakeup_event(tcpc->wakeup_wake_lock, 1000); + schedule_delayed_work(&tcpc->wake_up_work, 0); + return ALARMTIMER_NORESTART; +} + +static enum hrtimer_restart tcpc_timer_drp_src_toggle(struct hrtimer *timer) +{ + int index = TYPEC_TIMER_DRP_SRC_TOGGLE; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} + +#ifdef CONFIG_TYPEC_CAP_NORP_SRC +static enum hrtimer_restart tcpc_timer_norp_src(struct hrtimer *timer) +{ + int index = TYPEC_TIMER_NORP_SRC; + struct tcpc_device *tcpc = + container_of(timer, struct tcpc_device, tcpc_timer[index]); + + TCPC_TIMER_TRIGGER(); + return HRTIMER_NORESTART; +} +#endif /* CONFIG_TYPEC_CAP_NORP_SRC */ + +static tcpc_hrtimer_call tcpc_timer_call[PD_TIMER_NR] = { +#ifdef CONFIG_USB_POWER_DELIVERY + tcpc_timer_discover_id, + tcpc_timer_bist_cont_mode, + tcpc_timer_hard_reset_complete, + tcpc_timer_no_response, + tcpc_timer_ps_hard_reset, + tcpc_timer_ps_source_off, + tcpc_timer_ps_source_on, + tcpc_timer_ps_transition, + tcpc_timer_sender_response, + tcpc_timer_sink_request, + tcpc_timer_sink_wait_cap, + tcpc_timer_source_capability, + tcpc_timer_source_start, + tcpc_timer_vconn_on, +#ifdef CONFIG_USB_PD_VCONN_STABLE_DELAY + tcpc_timer_vconn_stable, +#endif /* CONFIG_USB_PD_VCONN_STABLE_DELAY */ + tcpc_timer_vdm_mode_entry, + tcpc_timer_vdm_mode_exit, + tcpc_timer_vdm_response, + tcpc_timer_source_transition, + tcpc_timer_src_recover, +#ifdef CONFIG_USB_PD_REV30 + tcpc_timer_ck_no_support, +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + tcpc_timer_sink_tx, +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ +#ifdef CONFIG_USB_PD_REV30_PPS_SOURCE + tcpc_timer_source_pps, +#endif /* CONFIG_USB_PD_REV30_PPS_SOURCE */ +#endif /* CONFIG_USB_PD_REV30 */ + +/* PD_TIMER (out of spec )*/ + tcpc_timer_vsafe0v_delay, + tcpc_timer_vsafe0v_tout, + tcpc_timer_pd_discard, + tcpc_timer_vbus_stable, + tcpc_timer_vbus_present, + tcpc_timer_uvdm_response, + tcpc_timer_dfp_flow_delay, + tcpc_timer_ufp_flow_delay, + tcpc_timer_vconn_ready, + tcpc_timer_vdm_postpone, + +#ifdef CONFIG_USB_PD_REV30 +#ifdef CONFIG_USB_PD_REV30_COLLISION_AVOID + tcpc_timer_deferred_evt, +#ifdef CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP + tcpc_timer_snk_flow_delay, +#endif /* CONFIG_USB_PD_REV30_SNK_FLOW_DELAY_STARTUP */ +#endif /* CONFIG_USB_PD_REV30_COLLISION_AVOID */ +#endif /* CONFIG_USB_PD_REV30 */ + + tcpc_timer_pe_idle_tout, +#endif /* CONFIG_USB_POWER_DELIVERY */ + +/* TYPEC_RT_TIMER (out of spec )*/ + tcpc_timer_rt_vsafe0v_delay, + tcpc_timer_rt_vsafe0v_tout, + tcpc_timer_rt_role_swap_start, + tcpc_timer_rt_role_swap_stop, + tcpc_timer_rt_legacy, + tcpc_timer_rt_not_legacy, + tcpc_timer_rt_legacy_stable, + tcpc_timer_rt_legacy_recycle, + tcpc_timer_rt_discharge, + tcpc_timer_rt_low_power_mode, +#ifdef CONFIG_USB_POWER_DELIVERY + tcpc_timer_rt_pe_idle, +#ifdef CONFIG_USB_PD_WAIT_BC12 + tcpc_timer_rt_pd_wait_bc12, +#endif /* CONFIG_USB_PD_WAIT_BC12 */ +#endif /* CONFIG_USB_POWER_DELIVERY */ + tcpc_timer_error_recovery, +/* TYPEC-TRY-TIMER */ + tcpc_timer_try_drp_try, + tcpc_timer_try_drp_trywait, +/* TYPEC-DEBOUNCE-TIMER */ + tcpc_timer_ccdebounce, + tcpc_timer_pddebounce, +#ifdef CONFIG_COMPATIBLE_APPLE_TA + tcpc_timer_apple_cc_open, +#endif /* CONFIG_COMPATIBLE_APPLE_TA */ + tcpc_timer_tryccdebounce, + tcpc_timer_srcdisconnect, + tcpc_timer_drp_src_toggle, +#ifdef CONFIG_TYPEC_CAP_NORP_SRC + tcpc_timer_norp_src, +#endif +}; + +/* + * [BLOCK] Control Timer + */ + +static void __tcpc_enable_wakeup_timer(struct tcpc_device *tcpc, bool en) +{ + int tout = 300; /* s */ + + if (en) { + TCPC_INFO("wakeup_timer\n"); + +#ifdef CONFIG_TYPEC_WAKEUP_ONCE_LOW_DUTY + if (!tcpc->typec_wakeup_once) { + if (tcpc->typec_low_rp_duty_cntdown) + tout = 5; + else + tout = 20; + } +#endif /* CONFIG_TYPEC_WAKEUP_ONCE_LOW_DUTY */ + + alarm_start_relative(&tcpc->wake_up_timer, ktime_set(tout, 0)); + } else + alarm_cancel(&tcpc->wake_up_timer); +} + +static inline void tcpc_reset_timer_range( + struct tcpc_device *tcpc, int start, int end) +{ + int i; + uint64_t mask; + + mask = tcpc_get_timer_enable_mask(tcpc); + + for (i = start; i < end; i++) { + if (mask & RT_MASK64(i)) { + hrtimer_try_to_cancel(&tcpc->tcpc_timer[i]); + tcpc_clear_timer_enable_mask(tcpc, i); + } + } + + if (end == PD_TIMER_NR) + __tcpc_enable_wakeup_timer(tcpc, false); +} + +void tcpc_restart_timer(struct tcpc_device *tcpc, uint32_t timer_id) +{ + tcpc_disable_timer(tcpc, timer_id); + tcpc_enable_timer(tcpc, timer_id); +} + +void tcpc_enable_wakeup_timer(struct tcpc_device *tcpc, bool en) +{ + mutex_lock(&tcpc->timer_lock); + __tcpc_enable_wakeup_timer(tcpc, en); + mutex_unlock(&tcpc->timer_lock); +} + +void tcpc_enable_timer(struct tcpc_device *tcpc, uint32_t timer_id) +{ + uint32_t r, mod, tout; + + TCPC_TIMER_EN_DBG(tcpc, timer_id); + if (timer_id >= PD_TIMER_NR) { + PD_BUG_ON(1); + return; + } + mutex_lock(&tcpc->timer_lock); + if (timer_id >= TYPEC_TIMER_START_ID) + tcpc_reset_timer_range(tcpc, TYPEC_TIMER_START_ID, PD_TIMER_NR); + + tcpc_set_timer_enable_mask(tcpc, timer_id); + + tout = tcpc_timer_timeout[timer_id]; + +#ifdef CONFIG_USB_PD_RANDOM_FLOW_DELAY + if (timer_id == PD_TIMER_DFP_FLOW_DELAY || + timer_id == PD_TIMER_UFP_FLOW_DELAY) + tout += TIMEOUT_VAL(jiffies & 0x07); +#endif /* CONFIG_USB_PD_RANDOM_FLOW_DELAY */ + + r = tout / 1000000; + mod = tout % 1000000; + + mutex_unlock(&tcpc->timer_lock); + hrtimer_start(&tcpc->tcpc_timer[timer_id], + ktime_set(r, mod*1000), HRTIMER_MODE_REL); +} + +void tcpc_disable_timer(struct tcpc_device *tcpc, uint32_t timer_id) +{ + uint64_t mask; + + mask = tcpc_get_timer_enable_mask(tcpc); + + if (timer_id >= PD_TIMER_NR) { + PD_BUG_ON(1); + return; + } + if (mask & RT_MASK64(timer_id)) { + hrtimer_try_to_cancel(&tcpc->tcpc_timer[timer_id]); + tcpc_clear_timer_enable_mask(tcpc, timer_id); + } +} + +#ifdef CONFIG_USB_POWER_DELIVERY +void tcpc_reset_pe_timer(struct tcpc_device *tcpc) +{ + mutex_lock(&tcpc->timer_lock); + tcpc_reset_timer_range(tcpc, 0, PD_PE_TIMER_END_ID); + mutex_unlock(&tcpc->timer_lock); +} +#endif /* CONFIG_USB_POWER_DELIVERY */ + +void tcpc_reset_typec_debounce_timer(struct tcpc_device *tcpc) +{ + mutex_lock(&tcpc->timer_lock); + tcpc_reset_timer_range(tcpc, TYPEC_TIMER_START_ID, PD_TIMER_NR); + mutex_unlock(&tcpc->timer_lock); +} + +void tcpc_reset_typec_try_timer(struct tcpc_device *tcpc) +{ + mutex_lock(&tcpc->timer_lock); + tcpc_reset_timer_range(tcpc, + TYPEC_TRY_TIMER_START_ID, TYPEC_TIMER_START_ID); + mutex_unlock(&tcpc->timer_lock); +} + +static void tcpc_handle_timer_triggered(struct tcpc_device *tcpc) +{ + int i = 0; + uint64_t triggered_timer = tcpc_get_timer_tick(tcpc); + uint64_t enable_mask = tcpc_get_timer_enable_mask(tcpc); + +#ifdef CONFIG_USB_POWER_DELIVERY + for (i = 0; i < PD_PE_TIMER_END_ID; i++) { + if (triggered_timer & RT_MASK64(i)) { + TCPC_TIMER_DBG(tcpc, i); + if (enable_mask & RT_MASK64(i)) + on_pe_timer_timeout(tcpc, i); + tcpc_clear_timer_tick(tcpc, i); + } + } +#endif /* CONFIG_USB_POWER_DELIVERY */ + + mutex_lock(&tcpc->typec_lock); + triggered_timer = tcpc_get_timer_tick(tcpc); + enable_mask = tcpc_get_timer_enable_mask(tcpc); + for (; i < PD_TIMER_NR; i++) { + if (triggered_timer & RT_MASK64(i)) { + TCPC_TIMER_DBG(tcpc, i); + if (enable_mask & RT_MASK64(i)) + tcpc_typec_handle_timeout(tcpc, i); + tcpc_clear_timer_tick(tcpc, i); + } + } + mutex_unlock(&tcpc->typec_lock); + +} + +static int tcpc_timer_thread_fn(void *data) +{ + struct tcpc_device *tcpc = data; + struct sched_param sch_param = {.sched_priority = MAX_RT_PRIO - 1}; + int ret = 0; + + sched_setscheduler(current, SCHED_FIFO, &sch_param); + + while (true) { + ret = wait_event_interruptible(tcpc->timer_wait_que, + tcpc_get_timer_tick(tcpc) || + kthread_should_stop()); + if (kthread_should_stop() || ret) { + dev_notice(&tcpc->dev, "%s exits(%d)\n", __func__, ret); + break; + } + tcpc_handle_timer_triggered(tcpc); + } + + return 0; +} + +int tcpci_timer_init(struct tcpc_device *tcpc) +{ + int i; + + pr_info("PD Timer number = %d\n", PD_TIMER_NR); + + init_waitqueue_head(&tcpc->timer_wait_que); + tcpc->timer_tick = 0; + tcpc->timer_enable_mask = 0; + tcpc->timer_task = kthread_run(tcpc_timer_thread_fn, tcpc, + "tcpc_timer_%s", tcpc->desc.name); + for (i = 0; i < PD_TIMER_NR; i++) { + hrtimer_init(&tcpc->tcpc_timer[i], + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + tcpc->tcpc_timer[i].function = tcpc_timer_call[i]; + } + tcpc->wakeup_wake_lock = + wakeup_source_register(&tcpc->dev, "tcpc_wakeup_wake_lock"); + INIT_DELAYED_WORK(&tcpc->wake_up_work, wake_up_work_func); + alarm_init(&tcpc->wake_up_timer, ALARM_REALTIME, tcpc_timer_wakeup); + + pr_info("%s : init OK\n", __func__); + return 0; +} + +int tcpci_timer_deinit(struct tcpc_device *tcpc) +{ + kthread_stop(tcpc->timer_task); + mutex_lock(&tcpc->timer_lock); + tcpc_reset_timer_range(tcpc, 0, PD_TIMER_NR); + mutex_unlock(&tcpc->timer_lock); + cancel_delayed_work_sync(&tcpc->wake_up_work); + wakeup_source_unregister(tcpc->wakeup_wake_lock); + + pr_info("%s : de init OK\n", __func__); + return 0; +} diff --git a/drivers/usb/typec/mmi_tcpc/tcpci_typec.c b/drivers/usb/typec/mmi_tcpc/tcpci_typec.c new file mode 100644 index 000000000000..c8485e5e198f --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/tcpci_typec.c @@ -0,0 +1,2956 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * TCPC Type-C Driver for Richtek + * + * 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. + */ + +#include +#include + +#include "inc/tcpci.h" +#include "inc/tcpci_typec.h" +#include "inc/tcpci_timer.h" + +#ifdef CONFIG_WATER_DETECTION +#ifdef CONFIG_MTK_KERNEL_POWER_OFF_CHARGING +#include +#endif /* CONFIG_MTK_KERNEL_POWER_OFF_CHARGING */ +#endif /* CONFIG_WATER_DETECTION */ + +#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE +#define CONFIG_TYPEC_CAP_TRY_STATE +#endif + +#ifdef CONFIG_TYPEC_CAP_TRY_SINK +#undef CONFIG_TYPEC_CAP_TRY_STATE +#define CONFIG_TYPEC_CAP_TRY_STATE +#endif /* CONFIG_TYPEC_CAP_TRY_SINK */ + +#define RICHTEK_PD_COMPLIANCE_FAKE_AUDIO_ACC /* For Rp3A */ +#define RICHTEK_PD_COMPLIANCE_FAKE_EMRAK_ONLY /* For Rp3A */ +#define RICHTEK_PD_COMPLIANCE_FAKE_RA_DETACH /* For Rp-DFT */ + +enum TYPEC_WAIT_PS_STATE { + TYPEC_WAIT_PS_DISABLE = 0, + TYPEC_WAIT_PS_SNK_VSAFE5V, + TYPEC_WAIT_PS_SRC_VSAFE0V, + TYPEC_WAIT_PS_SRC_VSAFE5V, +#ifdef CONFIG_TYPEC_CAP_DBGACC + TYPEC_WAIT_PS_DBG_VSAFE5V, +#endif /* CONFIG_TYPEC_CAP_DBGACC */ +}; + +enum TYPEC_ROLE_SWAP_STATE { + TYPEC_ROLE_SWAP_NONE = 0, + TYPEC_ROLE_SWAP_TO_SNK, + TYPEC_ROLE_SWAP_TO_SRC, +}; + +#if TYPEC_INFO2_ENABLE +static const char *const typec_wait_ps_name[] = { + "Disable", + "SNK_VSafe5V", + "SRC_VSafe0V", + "SRC_VSafe5V", + "DBG_VSafe5V", +}; +#endif /* TYPEC_INFO2_ENABLE */ + +static inline void typec_wait_ps_change(struct tcpc_device *tcpc, + enum TYPEC_WAIT_PS_STATE state) +{ +#if TYPEC_INFO2_ENABLE + uint8_t old_state = tcpc->typec_wait_ps_change; + uint8_t new_state = (uint8_t) state; + + if (new_state != old_state) + TYPEC_INFO2("wait_ps=%s\n", typec_wait_ps_name[new_state]); +#endif /* TYPEC_INFO2_ENABLE */ + +#ifdef CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT + if (state == TYPEC_WAIT_PS_SRC_VSAFE0V) + tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_SAFE0V_TOUT); +#endif /* CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT */ + + if (tcpc->typec_wait_ps_change == TYPEC_WAIT_PS_SRC_VSAFE0V + && state != TYPEC_WAIT_PS_SRC_VSAFE0V) { + tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_SAFE0V_DELAY); + +#ifdef CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT + tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_SAFE0V_TOUT); +#endif /* CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT */ + } + + tcpc->typec_wait_ps_change = (uint8_t) state; +} + +/* #define TYPEC_EXIT_ATTACHED_SRC_NO_DEBOUNCE */ +#define TYPEC_EXIT_ATTACHED_SNK_VIA_VBUS + +static inline int typec_enable_low_power_mode( + struct tcpc_device *tcpc, uint8_t pull); + +#define typec_check_cc1(cc) \ + (typec_get_cc1() == cc) + +#define typec_check_cc2(cc) \ + (typec_get_cc2() == cc) + +#define typec_check_cc(cc1, cc2) \ + (typec_check_cc1(cc1) && typec_check_cc2(cc2)) + +#define typec_check_cc_both(res) \ + (typec_check_cc(res, res)) + +#define typec_check_cc_any(res) \ + (typec_check_cc1(res) || typec_check_cc2(res)) + +#define typec_is_drp_toggling() \ + (typec_get_cc1() == TYPEC_CC_DRP_TOGGLING) + +#define typec_is_cc_open() \ + typec_check_cc_both(TYPEC_CC_VOLT_OPEN) + +#define typec_is_cable_only() \ + (typec_get_cc1() + typec_get_cc2() == TYPEC_CC_VOLT_RA) + +#define typec_is_sink_with_emark() \ + (typec_get_cc1() + typec_get_cc2() == \ + TYPEC_CC_VOLT_RA+TYPEC_CC_VOLT_RD) + +#define typec_is_cc_no_res() \ + (typec_is_drp_toggling() || typec_is_cc_open()) + +static inline int typec_enable_vconn(struct tcpc_device *tcpc) +{ +#ifndef CONFIG_USB_POWER_DELIVERY + if (!typec_is_sink_with_emark()) + return 0; +#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */ + +#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE + if (tcpc->tcpc_vconn_supply == TCPC_VCONN_SUPPLY_NEVER) + return 0; +#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */ + + return tcpci_set_vconn(tcpc, true); +} + +/* + * [BLOCK] TYPEC Connection State Definition + */ + +enum TYPEC_CONNECTION_STATE { + typec_disabled = 0, + typec_errorrecovery, + + typec_unattached_snk, + typec_unattached_src, + + typec_attachwait_snk, + typec_attachwait_src, + + typec_attached_snk, + typec_attached_src, + +#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE + /* Require : Assert Rp + * Exit(-> Attached.SRC) : Detect Rd (tPDDebounce). + * Exit(-> TryWait.SNK) : Not detect Rd after tDRPTry + */ + typec_try_src, + + /* Require : Assert Rd + * Exit(-> Attached.SNK) : Detect Rp (tCCDebounce) and Vbus present. + * Exit(-> Unattached.SNK) : Not detect Rp (tPDDebounce) + */ + + typec_trywait_snk, + typec_trywait_snk_pe, +#endif + +#ifdef CONFIG_TYPEC_CAP_TRY_SINK + + /* Require : Assert Rd + * Wait for tDRPTry and only then begin monitoring CC. + * Exit (-> Attached.SNK) : Detect Rp (tPDDebounce) and Vbus present. + * Exit (-> TryWait.SRC) : Not detect Rp for tPDDebounce. + */ + typec_try_snk, + + /* + * Require : Assert Rp + * Exit (-> Attached.SRC) : Detect Rd (tCCDebounce) + * Exit (-> Unattached.SNK) : Not detect Rd after tDRPTry + */ + + typec_trywait_src, + typec_trywait_src_pe, +#endif /* CONFIG_TYPEC_CAP_TRY_SINK */ + + typec_audioaccessory, +#ifdef CONFIG_TYPEC_CAP_DBGACC + typec_debugaccessory, +#endif /* CONFIG_TYPEC_CAP_DBGACC */ + +#ifdef CONFIG_TYPEC_CAP_DBGACC_SNK + typec_attached_dbgacc_snk, +#endif /* CONFIG_TYPEC_CAP_DBGACC_SNK */ + +#ifdef CONFIG_TYPEC_CAP_CUSTOM_SRC + typec_attached_custom_src, +#endif /* CONFIG_TYPEC_CAP_CUSTOM_SRC */ + +#ifdef CONFIG_TYPEC_CAP_NORP_SRC + typec_attached_norp_src, +#endif /* CONFIG_TYPEC_CAP_NORP_SRC */ + +#ifdef CONFIG_TYPEC_CAP_ROLE_SWAP + typec_role_swap, +#endif /* CONFIG_TYPEC_CAP_ROLE_SWAP */ + +#ifdef CONFIG_WATER_DETECTION + typec_water_protection_wait, + typec_water_protection, +#endif /* CONFIG_WATER_DETECTION */ + + typec_unattachwait_pe, /* Wait Policy Engine go to Idle */ +}; + +#if TYPEC_INFO_ENABLE || TCPC_INFO_ENABLE +static const char *const typec_state_name[] = { + "Disabled", + "ErrorRecovery", + + "Unattached.SNK", + "Unattached.SRC", + + "AttachWait.SNK", + "AttachWait.SRC", + + "Attached.SNK", + "Attached.SRC", + +#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE + "Try.SRC", + "TryWait.SNK", + "TryWait.SNK.PE", +#endif /* CONFIG_TYPEC_CAP_TRY_SOURCE */ + +#ifdef CONFIG_TYPEC_CAP_TRY_SINK + "Try.SNK", + "TryWait.SRC", + "TryWait.SRC.PE", +#endif /* CONFIG_TYPEC_CAP_TRY_SINK */ + + "AudioAccessory", +#ifdef CONFIG_TYPEC_CAP_DBGACC + "DebugAccessory", +#endif /* CONFIG_TYPEC_CAP_DBGACC */ + +#ifdef CONFIG_TYPEC_CAP_DBGACC_SNK + "DBGACC.SNK", +#endif /* CONFIG_TYPEC_CAP_DBGACC_SNK */ + +#ifdef CONFIG_TYPEC_CAP_CUSTOM_SRC + "Custom.SRC", +#endif /* CONFIG_TYPEC_CAP_CUSTOM_SRC */ + +#ifdef CONFIG_TYPEC_CAP_NORP_SRC + "NoRp.SRC", +#endif /* CONFIG_TYPEC_CAP_NORP_SRC */ + +#ifdef CONFIG_TYPEC_CAP_ROLE_SWAP + "RoleSwap", +#endif /* CONFIG_TYPEC_CAP_ROLE_SWAP */ + +#ifdef CONFIG_WATER_DETECTION + "WaterProtection.Wait", + "WaterProtection", +#endif /* CONFIG_WATER_DETECTION */ + + "UnattachWait.PE", +}; +#endif /* TYPEC_INFO_ENABLE || TCPC_INFO_ENABLE */ + +static inline void typec_transfer_state(struct tcpc_device *tcpc, + enum TYPEC_CONNECTION_STATE state) +{ +#if TYPEC_INFO_ENABLE + if (state >= 0 && state < ARRAY_SIZE(typec_state_name)) + TYPEC_INFO("** %s\n", typec_state_name[state]); +#endif /* TYPEC_INFO_ENABLE */ + tcpc->typec_state = (uint8_t) state; +} + +#define TYPEC_NEW_STATE(state) \ + (typec_transfer_state(tcpc, state)) + +/* + * [BLOCK] TypeC Alert Attach Status Changed + */ + +#if TYPEC_INFO_ENABLE || TYPEC_DBG_ENABLE +static const char *const typec_attach_name[] = { + "NULL", + "SINK", + "SOURCE", + "AUDIO", + "DEBUG", + + "DBGACC_SNK", + "CUSTOM_SRC", + "NORP_SRC", +}; +#endif /* TYPEC_INFO_ENABLE || TYPEC_DBG_ENABLE */ + +static int typec_alert_attach_state_change(struct tcpc_device *tcpc) +{ + int ret = 0; + +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE + if (tcpc->typec_legacy_cable) + tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_NOT_LEGACY); + else + tcpc_restart_timer(tcpc, TYPEC_RT_TIMER_NOT_LEGACY); +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */ + + if (tcpc->typec_attach_old == tcpc->typec_attach_new) { + TYPEC_DBG("Attached-> %s(repeat)\n", + typec_attach_name[tcpc->typec_attach_new]); + return 0; + } + + TYPEC_INFO("Attached-> %s\n", + typec_attach_name[tcpc->typec_attach_new]); + + /*Report function */ + ret = tcpci_report_usb_port_changed(tcpc); + + tcpc->typec_attach_old = tcpc->typec_attach_new; + return ret; +} + +static inline int typec_set_drp_toggling(struct tcpc_device *tcpc) +{ + int ret; + + ret = tcpci_set_cc(tcpc, TYPEC_CC_DRP); + if (ret < 0) + return ret; + + return typec_enable_low_power_mode(tcpc, TYPEC_CC_DRP); +} + +#ifdef CONFIG_WATER_DETECTION +static int typec_check_water_status(struct tcpc_device *tcpc) +{ + int ret; + + if (!(tcpc->tcpc_flags & TCPC_FLAGS_WATER_DETECTION)) + return 0; + + ret = tcpci_is_water_detected(tcpc); + if (ret < 0) + return ret; + if (ret) { + tcpc_typec_handle_wd(tcpc, true); + return 1; + } + return 0; +} +#endif /* CONFIG_WATER_DETECTION */ + +/* + * [BLOCK] NoRpSRC Entry + */ + +#ifdef CONFIG_TYPEC_CAP_NORP_SRC +static bool typec_try_enter_norp_src(struct tcpc_device *tcpc) +{ + if (tcpci_check_vbus_valid_from_ic(tcpc) && + typec_is_cc_no_res() && + tcpc->typec_state == typec_unattached_snk) { + TYPEC_INFO("norp_src=1\n"); + tcpc_enable_timer(tcpc, TYPEC_TIMER_NORP_SRC); + return true; + } + + return false; +} + +static void typec_unattach_wait_pe_idle_entry(struct tcpc_device *tcpc); +static bool typec_try_exit_norp_src(struct tcpc_device *tcpc) +{ + if ((!tcpci_check_vbus_valid_from_ic(tcpc) || + !typec_is_cc_no_res()) && + tcpc->typec_state == typec_attached_norp_src) { + TYPEC_INFO("norp_src=0\n"); + typec_unattach_wait_pe_idle_entry(tcpc); + typec_alert_attach_state_change(tcpc); + return true; + } + + return false; +} + +static inline int typec_norp_src_attached_entry(struct tcpc_device *tcpc) +{ +#ifdef CONFIG_WATER_DETECTION +#ifdef CONFIG_WD_POLLING_ONLY + if (!tcpc->typec_power_ctrl) { + if (get_boot_mode() == KERNEL_POWER_OFF_CHARGING_BOOT || + get_boot_mode() == LOW_POWER_OFF_CHARGING_BOOT) + typec_check_water_status(tcpc); + + tcpci_set_usbid_polling(tcpc, false); + } +#else + if (!tcpc->typec_power_ctrl && typec_check_water_status(tcpc)) + return 0; +#endif /* CONFIG_WD_POLLING_ONLY */ +#endif /* CONFIG_WATER_DETECTION */ + + TYPEC_NEW_STATE(typec_attached_norp_src); + tcpc->typec_attach_new = TYPEC_ATTACHED_NORP_SRC; + +#ifdef CONFIG_TYPEC_CAP_A2C_C2C + tcpc->typec_a2c_cable = true; +#endif /* CONFIG_TYPEC_CAP_A2C_C2C */ + + tcpci_report_power_control(tcpc, true); + tcpci_sink_vbus(tcpc, TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_5V, 500); + + typec_alert_attach_state_change(tcpc); + return 0; +} +#endif /* CONFIG_TYPEC_CAP_NORP_SRC */ + +/* + * [BLOCK] Unattached Entry + */ + +static inline int typec_try_low_power_mode(struct tcpc_device *tcpc) +{ + int ret = tcpci_set_low_power_mode( + tcpc, true, tcpc->typec_lpm_pull); + if (ret < 0) + return ret; + +#ifdef CONFIG_TCPC_LPM_CONFIRM + ret = tcpci_is_low_power_mode(tcpc); + if (ret < 0) + return ret; + + if (ret == 1) + return 0; + + if (tcpc->typec_lpm_retry == 0) { + TYPEC_INFO("TryLPM Failed\n"); + return 0; + } + + tcpc->typec_lpm_retry--; + TYPEC_DBG("RetryLPM : %d\n", tcpc->typec_lpm_retry); + tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_LOW_POWER_MODE); +#endif /* CONFIG_TCPC_LPM_CONFIRM */ + + return 0; +} + +static inline int typec_enter_low_power_mode(struct tcpc_device *tcpc) +{ + int ret = 0; + +#ifdef CONFIG_TCPC_LPM_POSTPONE + tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_LOW_POWER_MODE); +#else + ret = typec_try_low_power_mode(tcpc); +#endif /* CONFIG_TCPC_POSTPONE_LPM */ + + return ret; +} + +static inline int typec_enable_low_power_mode( + struct tcpc_device *tcpc, uint8_t pull) +{ + int ret = 0; + +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE + if (tcpc->typec_legacy_cable) { + TYPEC_DBG("LPM_LCOnly\n"); + return 0; + } +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */ + + if (tcpc->typec_cable_only) { + TYPEC_DBG("LPM_RaOnly\n"); + +#ifdef CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG + if (tcpc->tcpc_flags & TCPC_FLAGS_LPM_WAKEUP_WATCHDOG) + tcpc_enable_wakeup_timer(tcpc, true); +#endif /* CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG */ + + return 0; + } + + if (tcpc->typec_lpm != true) { + tcpc->typec_lpm = true; + tcpc->typec_lpm_retry = TCPC_LOW_POWER_MODE_RETRY; + tcpc->typec_lpm_pull = (uint8_t) pull; + ret = typec_enter_low_power_mode(tcpc); + } + + return ret; +} + +static inline int typec_disable_low_power_mode( + struct tcpc_device *tcpc) +{ + int ret = 0; + + if (tcpc->typec_lpm != false) { + tcpc->typec_lpm = false; + tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_LOW_POWER_MODE); + tcpci_set_low_rp_duty(tcpc, false); + ret = tcpci_set_low_power_mode(tcpc, false, TYPEC_CC_DRP); + } + +#ifdef CONFIG_TYPEC_WAKEUP_ONCE_LOW_DUTY + tcpc->typec_wakeup_once = 0; + tcpc->typec_low_rp_duty_cntdown = 0; +#endif /* CONFIG_TYPEC_WAKEUP_ONCE_LOW_DUTY */ + + return ret; +} + +static void typec_unattached_power_entry(struct tcpc_device *tcpc) +{ + typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE); + + if (tcpc->typec_power_ctrl) { + tcpci_set_vconn(tcpc, false); + tcpci_disable_vbus_control(tcpc); + tcpci_report_power_control(tcpc, false); + } +} + +static inline void typec_unattached_src_and_drp_entry(struct tcpc_device *tcpc) +{ + TYPEC_NEW_STATE(typec_unattached_src); + tcpci_set_cc(tcpc, TYPEC_CC_RP); + tcpc_enable_timer(tcpc, TYPEC_TIMER_DRP_SRC_TOGGLE); +} + +static inline void typec_unattached_snk_and_drp_entry(struct tcpc_device *tcpc) +{ + TYPEC_NEW_STATE(typec_unattached_snk); + tcpci_set_cc(tcpc, TYPEC_CC_DRP); + typec_enable_low_power_mode(tcpc, TYPEC_CC_DRP); +} + +static inline void typec_unattached_cc_entry(struct tcpc_device *tcpc) +{ +#ifdef CONFIG_TYPEC_CAP_ROLE_SWAP + if (tcpc->typec_during_role_swap) { + TYPEC_NEW_STATE(typec_role_swap); + return; + } +#endif /* CONFIG_TYPEC_CAP_ROLE_SWAP */ +#ifdef CONFIG_CABLE_TYPE_DETECTION + tcpc_typec_handle_ctd(tcpc, TCPC_CABLE_TYPE_NONE); +#endif /* CONFIG_CABLE_TYPE_DETECTION */ + + tcpc->typec_role = tcpc->typec_role_new; + switch (tcpc->typec_role) { + case TYPEC_ROLE_SNK: + TYPEC_NEW_STATE(typec_unattached_snk); + tcpci_set_cc(tcpc, TYPEC_CC_RD); + typec_enable_low_power_mode(tcpc, TYPEC_CC_RD); + break; + case TYPEC_ROLE_SRC: +#ifdef CONFIG_TYPEC_CHECK_SRC_UNATTACH_OPEN + if (typec_check_cc_any(TYPEC_CC_VOLT_RD)) { + TYPEC_DBG("typec_src_unattach not open\n"); + tcpci_set_cc(tcpc, TYPEC_CC_OPEN); + usleep_rnage(5000, 6000); + } +#endif /* CONFIG_TYPEC_CHECK_SRC_UNATTACH_OPEN */ + TYPEC_NEW_STATE(typec_unattached_src); + tcpci_set_cc(tcpc, TYPEC_CC_RP); + typec_enable_low_power_mode(tcpc, TYPEC_CC_RP); + break; + case TYPEC_ROLE_TRY_SRC: + if (tcpc->typec_state == typec_errorrecovery) { + typec_unattached_src_and_drp_entry(tcpc); + break; + } + /* pass through */ + default: + switch (tcpc->typec_state) { + case typec_attachwait_snk: + case typec_audioaccessory: + typec_unattached_src_and_drp_entry(tcpc); + break; + default: + typec_unattached_snk_and_drp_entry(tcpc); + break; + } + break; + } + +#ifdef CONFIG_TYPEC_CAP_NORP_SRC + typec_try_enter_norp_src(tcpc); +#endif /* CONFIG_TYPEC_CAP_NORP_SRC */ +} + +static void typec_unattached_entry(struct tcpc_device *tcpc) +{ +#ifdef CONFIG_TYPEC_CAP_CUSTOM_HV + tcpc->typec_during_custom_hv = false; +#endif /* CONFIG_TYPEC_CAP_CUSTOM_HV */ + + tcpc->typec_usb_sink_curr = CONFIG_TYPEC_SNK_CURR_DFT; + + if (tcpc->typec_power_ctrl) + tcpci_set_vconn(tcpc, false); + typec_unattached_cc_entry(tcpc); + typec_unattached_power_entry(tcpc); +} + +static void typec_unattach_wait_pe_idle_entry(struct tcpc_device *tcpc) +{ + tcpc->typec_attach_new = TYPEC_UNATTACHED; + +#ifdef CONFIG_USB_POWER_DELIVERY + if (tcpc->pd_pe_running) { + TYPEC_NEW_STATE(typec_unattachwait_pe); + return; + } +#endif /* CONFIG_USB_POWER_DELIVERY */ + + typec_unattached_entry(tcpc); +} + +static void typec_postpone_state_change(struct tcpc_device *tcpc) +{ + TYPEC_DBG("Postpone AlertChange\n"); + tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_STATE_CHANGE); +} + +static void typec_cc_open_entry(struct tcpc_device *tcpc, uint8_t state) +{ + mutex_lock(&tcpc->access_lock); + TYPEC_NEW_STATE(state); + tcpc->typec_attach_new = TYPEC_UNATTACHED; + mutex_unlock(&tcpc->access_lock); + + tcpci_set_cc(tcpc, TYPEC_CC_OPEN); + typec_unattached_power_entry(tcpc); + + typec_postpone_state_change(tcpc); +} + +static inline void typec_error_recovery_entry(struct tcpc_device *tcpc) +{ + typec_cc_open_entry(tcpc, typec_errorrecovery); + tcpc_reset_typec_debounce_timer(tcpc); + tcpc_enable_timer(tcpc, TYPEC_TIMER_ERROR_RECOVERY); +} + +static inline void typec_disable_entry(struct tcpc_device *tcpc) +{ + typec_cc_open_entry(tcpc, typec_disabled); +} + +/* + * [BLOCK] Attached Entry + */ + +static inline int typec_set_polarity(struct tcpc_device *tcpc, + bool polarity) +{ + tcpc->typec_polarity = polarity; + return tcpci_set_polarity(tcpc, polarity); +} + +static inline int typec_set_plug_orient(struct tcpc_device *tcpc, + uint8_t pull, bool polarity) +{ + int rv = typec_set_polarity(tcpc, polarity); + + if (rv) + return rv; + + return tcpci_set_cc(tcpc, pull); +} + +static void typec_source_attached_with_vbus_entry(struct tcpc_device *tcpc) +{ + tcpc->typec_attach_new = TYPEC_ATTACHED_SRC; + typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE); +} + +static inline void typec_source_attached_entry(struct tcpc_device *tcpc) +{ + TYPEC_NEW_STATE(typec_attached_src); + tcpc->typec_is_attached_src = true; + typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_SRC_VSAFE5V); + + tcpc_disable_timer(tcpc, TYPEC_TRY_TIMER_DRP_TRY); + +#ifdef CONFIG_TYPEC_CAP_ROLE_SWAP + if (tcpc->typec_during_role_swap) { + tcpc->typec_during_role_swap = TYPEC_ROLE_SWAP_NONE; + tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_ROLE_SWAP_STOP); + } +#endif /* CONFIG_TYPEC_CAP_ROLE_SWAP */ + + typec_set_plug_orient(tcpc, + TYPEC_CC_PULL(tcpc->typec_local_rp_level, TYPEC_CC_RP), + typec_check_cc2(TYPEC_CC_VOLT_RD)); + + tcpci_report_power_control(tcpc, true); + typec_enable_vconn(tcpc); + tcpci_source_vbus(tcpc, + TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SOURCE_5V, -1); +} + +static inline void typec_sink_attached_entry(struct tcpc_device *tcpc) +{ + TYPEC_NEW_STATE(typec_attached_snk); + typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE); + + tcpc->typec_attach_new = TYPEC_ATTACHED_SNK; + +#ifdef CONFIG_TYPEC_CAP_TRY_STATE + if (tcpc->typec_role >= TYPEC_ROLE_DRP) + tcpc_reset_typec_try_timer(tcpc); +#endif /* CONFIG_TYPEC_CAP_TRY_STATE */ + +#ifdef CONFIG_TYPEC_CAP_ROLE_SWAP + if (tcpc->typec_during_role_swap) { + tcpc->typec_during_role_swap = TYPEC_ROLE_SWAP_NONE; + tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_ROLE_SWAP_STOP); + } +#endif /* CONFIG_TYPEC_CAP_ROLE_SWAP */ + + typec_set_plug_orient(tcpc, TYPEC_CC_RD, + !typec_check_cc2(TYPEC_CC_VOLT_OPEN)); + tcpc->typec_remote_rp_level = typec_get_cc_res(); + + tcpci_report_power_control(tcpc, true); + tcpci_sink_vbus(tcpc, TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_5V, -1); +} + +static inline void typec_custom_src_attached_entry( + struct tcpc_device *tcpc) +{ +#ifdef CONFIG_TYPEC_CAP_DBGACC_SNK + TYPEC_DBG("[Warning] Same Rp (%d)\n", typec_get_cc1()); +#else + TYPEC_DBG("[Warning] CC Both Rp\n"); +#endif + +#ifdef CONFIG_TYPEC_CAP_CUSTOM_SRC + TYPEC_NEW_STATE(typec_attached_custom_src); + tcpc->typec_attach_new = TYPEC_ATTACHED_CUSTOM_SRC; + + tcpc->typec_remote_rp_level = typec_get_cc1(); + + tcpci_report_power_control(tcpc, true); + tcpci_sink_vbus(tcpc, TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_5V, -1); +#endif /* CONFIG_TYPEC_CAP_CUSTOM_SRC */ +} + +#ifdef CONFIG_TYPEC_CAP_DBGACC_SNK + +static inline uint8_t typec_get_sink_dbg_acc_rp_level( + int cc1, int cc2) +{ + if (cc2 == TYPEC_CC_VOLT_SNK_DFT) + return cc1; + + return TYPEC_CC_VOLT_SNK_DFT; +} + +static inline void typec_sink_dbg_acc_attached_entry( + struct tcpc_device *tcpc) +{ + bool polarity; + uint8_t rp_level; + + uint8_t cc1 = typec_get_cc1(); + uint8_t cc2 = typec_get_cc2(); + + if (cc1 == cc2) { + typec_custom_src_attached_entry(tcpc); + return; + } + + TYPEC_NEW_STATE(typec_attached_dbgacc_snk); + + tcpc->typec_attach_new = TYPEC_ATTACHED_DBGACC_SNK; + + polarity = cc2 > cc1; + + if (polarity) + rp_level = typec_get_sink_dbg_acc_rp_level(cc2, cc1); + else + rp_level = typec_get_sink_dbg_acc_rp_level(cc1, cc2); + + typec_set_plug_orient(tcpc, TYPEC_CC_RD, polarity); + tcpc->typec_remote_rp_level = rp_level; + + tcpci_report_power_control(tcpc, true); + tcpci_sink_vbus(tcpc, TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_5V, -1); +} +#else +static inline void typec_sink_dbg_acc_attached_entry( + struct tcpc_device *tcpc) +{ + typec_custom_src_attached_entry(tcpc); +} +#endif /* CONFIG_TYPEC_CAP_DBGACC_SNK */ + + +/* + * [BLOCK] Try.SRC / TryWait.SNK + */ + +#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE + +static inline bool typec_role_is_try_src( + struct tcpc_device *tcpc) +{ + if (tcpc->typec_role != TYPEC_ROLE_TRY_SRC) + return false; + +#ifdef CONFIG_TYPEC_CAP_ROLE_SWAP + if (tcpc->typec_during_role_swap) + return false; +#endif /* CONFIG_TYPEC_CAP_ROLE_SWAP */ + + return true; +} + +static inline void typec_try_src_entry(struct tcpc_device *tcpc) +{ + uint32_t chip_id; + int rv = 0; + TYPEC_NEW_STATE(typec_try_src); + tcpc->typec_drp_try_timeout = false; + + tcpci_set_cc(tcpc, TYPEC_CC_RP); + tcpc_enable_timer(tcpc, TYPEC_TRY_TIMER_DRP_TRY); + + rv = tcpci_get_chip_id(tcpc, &chip_id); + if (!rv && SC2150A_DID == chip_id) { + tcpc_typec_handle_cc_change(tcpc); + } +} + +static inline void typec_trywait_snk_entry(struct tcpc_device *tcpc) +{ + TYPEC_NEW_STATE(typec_trywait_snk); + typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE); + + tcpci_set_vconn(tcpc, false); + tcpci_set_cc(tcpc, TYPEC_CC_RD); + tcpci_source_vbus(tcpc, + TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SOURCE_0V, 0); + tcpc_disable_timer(tcpc, TYPEC_TRY_TIMER_DRP_TRY); + + tcpc_enable_timer(tcpc, TYPEC_TIMER_PDDEBOUNCE); +} + +static inline void typec_trywait_snk_pe_entry(struct tcpc_device *tcpc) +{ + tcpc->typec_attach_new = TYPEC_UNATTACHED; + +#ifdef CONFIG_USB_POWER_DELIVERY + if (tcpc->typec_attach_old) { + TYPEC_NEW_STATE(typec_trywait_snk_pe); + return; + } +#endif + + typec_trywait_snk_entry(tcpc); +} + +#endif /* CONFIG_TYPEC_CAP_TRY_SOURCE */ + +/* + * [BLOCK] Try.SNK / TryWait.SRC + */ + +#ifdef CONFIG_TYPEC_CAP_TRY_SINK + +static inline bool typec_role_is_try_sink( + struct tcpc_device *tcpc) +{ + if (tcpc->typec_role != TYPEC_ROLE_TRY_SNK) + return false; + +#ifdef CONFIG_TYPEC_CAP_ROLE_SWAP + if (tcpc->typec_during_role_swap) + return false; +#endif /* CONFIG_TYPEC_CAP_ROLE_SWAP */ + + return true; +} + +static inline void typec_try_snk_entry(struct tcpc_device *tcpc) +{ + int rv = 0; + uint32_t chip_id; + TYPEC_NEW_STATE(typec_try_snk); + tcpc->typec_drp_try_timeout = false; + + tcpci_set_cc(tcpc, TYPEC_CC_RD); + tcpc_enable_timer(tcpc, TYPEC_TRY_TIMER_DRP_TRY); + + rv = tcpci_get_chip_id(tcpc, &chip_id); + if (!rv && SC2150A_DID == chip_id) { + tcpc_typec_handle_cc_change(tcpc); + } +} + +static inline void typec_trywait_src_entry(struct tcpc_device *tcpc) +{ + TYPEC_NEW_STATE(typec_trywait_src); + tcpc->typec_drp_try_timeout = false; + + tcpci_set_cc(tcpc, TYPEC_CC_RP); + tcpci_sink_vbus(tcpc, TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_0V, 0); + tcpc_enable_timer(tcpc, TYPEC_TRY_TIMER_DRP_TRY); +} + +#endif /* CONFIG_TYPEC_CAP_TRY_SINK */ + +/* + * [BLOCK] Attach / Detach + */ + +static inline void typec_cc_snk_detect_vsafe5v_entry( + struct tcpc_device *tcpc) +{ + typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE); + + if (!typec_check_cc_any(TYPEC_CC_VOLT_OPEN)) { /* Both Rp */ + typec_sink_dbg_acc_attached_entry(tcpc); + return; + } + +#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE + if (typec_role_is_try_src(tcpc)) { + if (tcpc->typec_state == typec_attachwait_snk) { + typec_try_src_entry(tcpc); + return; + } + } +#endif /* CONFIG_TYPEC_CAP_TRY_SOURCE */ + + typec_sink_attached_entry(tcpc); +} + +static inline void typec_cc_snk_detect_entry(struct tcpc_device *tcpc) +{ + /* If Port Partner act as Source without VBUS, wait vSafe5V */ + if (tcpci_check_vbus_valid(tcpc)) + typec_cc_snk_detect_vsafe5v_entry(tcpc); + else + typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_SNK_VSAFE5V); +} + +static inline void typec_cc_src_detect_vsafe0v_entry( + struct tcpc_device *tcpc) +{ + typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE); + +#ifdef CONFIG_TYPEC_CAP_TRY_SINK + if (typec_role_is_try_sink(tcpc)) { + if (tcpc->typec_state == typec_attachwait_src) { + typec_try_snk_entry(tcpc); + return; + } + } +#endif /* CONFIG_TYPEC_CAP_TRY_SINK */ + + typec_source_attached_entry(tcpc); +} + +static inline void typec_cc_src_detect_entry( + struct tcpc_device *tcpc) +{ + /* If Port Partner act as Sink with low VBUS, wait vSafe0v */ + bool vbus_absent = tcpci_check_vsafe0v(tcpc, true); + + if (vbus_absent || tcpc->typec_reach_vsafe0v) + typec_cc_src_detect_vsafe0v_entry(tcpc); + else + typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_SRC_VSAFE0V); +} + +static inline void typec_cc_src_remove_entry(struct tcpc_device *tcpc) +{ + typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE); + + tcpc->typec_is_attached_src = false; + +#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE + if (typec_role_is_try_src(tcpc)) { + switch (tcpc->typec_state) { + case typec_attached_src: + typec_trywait_snk_pe_entry(tcpc); + return; + case typec_try_src: + typec_trywait_snk_entry(tcpc); + return; + } + } +#endif /* CONFIG_TYPEC_CAP_TRY_SOURCE */ + + typec_unattach_wait_pe_idle_entry(tcpc); +} + +static inline void typec_cc_snk_remove_entry(struct tcpc_device *tcpc) +{ + typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE); + +#ifdef CONFIG_TYPEC_CAP_TRY_SINK + if (tcpc->typec_state == typec_try_snk) { + typec_trywait_src_entry(tcpc); + return; + } +#endif /* CONFIG_TYPEC_CAP_TRY_SINK */ + + typec_unattach_wait_pe_idle_entry(tcpc); +} + +/* + * [BLOCK] Check Legacy Cable + */ + +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE + +static inline void typec_legacy_reset_cable_suspect( + struct tcpc_device *tcpc) +{ +#if TCPC_LEGACY_CABLE_SUSPECT_THD + tcpc->typec_legacy_cable_suspect = 0; +#endif /* TCPC_LEGACY_CABLE_SUSPECT_THD != 0 */ +} + +static inline void typec_legacy_reset_retry_wk( + struct tcpc_device *tcpc) +{ +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE2 + tcpc->typec_legacy_retry_wk = 0; +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE2 */ +} + +static inline void typec_legacy_enable_discharge( + struct tcpc_device *tcpc, bool en) +{ +#ifdef CONFIG_TYPEC_CAP_FORCE_DISCHARGE + if (tcpc->tcpc_flags & TCPC_FLAGS_PREFER_LEGACY2) { + mutex_lock(&tcpc->access_lock); + tcpci_enable_force_discharge(tcpc, en, 0); + mutex_unlock(&tcpc->access_lock); + } +#endif /* CONFIG_TYPEC_CAP_FORCE_DISCHARGE */ +} + +static inline void typec_legacy_keep_default_rp( + struct tcpc_device *tcpc, bool en) +{ +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE2 + typec_legacy_enable_discharge(tcpc, en); + + if (en) { + tcpci_set_cc(tcpc, TYPEC_CC_RD); + usleep_range(1000, 2000); + tcpci_set_cc(tcpc, TYPEC_CC_RP); + usleep_range(1000, 2000); + } +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE2 */ +} + +static inline bool typec_legacy_charge( + struct tcpc_device *tcpc) +{ + int i, vbus_level = 0; + + TYPEC_INFO("LC->Charge\n"); + tcpci_source_vbus(tcpc, + TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SOURCE_5V, 100); + + for (i = 0; i < 6; i++) { /* 275 ms */ + vbus_level = tcpm_inquire_vbus_level(tcpc, true); + if (vbus_level >= TCPC_VBUS_VALID) + return true; + msleep(50); + } + + TYPEC_INFO("LC->Charge Failed\n"); + return false; +} + +static inline bool typec_legacy_discharge( + struct tcpc_device *tcpc) +{ + int i, vbus_level = 0; + + TYPEC_INFO("LC->Discharge\n"); + tcpci_source_vbus(tcpc, + TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SOURCE_0V, 0); + + for (i = 0; i < 6; i++) { /* 275 ms */ + vbus_level = tcpm_inquire_vbus_level(tcpc, true); + if (vbus_level < TCPC_VBUS_VALID) + return true; + msleep(50); + } + + TYPEC_INFO("LC->Discharge Failed\n"); + return false; +} + +static inline bool typec_legacy_suspect(struct tcpc_device *tcpc) +{ + int i = 0, vbus_level = 0; + + TYPEC_INFO("LC->Suspect\n"); + typec_legacy_reset_cable_suspect(tcpc); + + while (1) { + vbus_level = tcpm_inquire_vbus_level(tcpc, true); + if (vbus_level < TCPC_VBUS_VALID) + break; + + i++; + if (i > 3) { /* 150 ms */ + TYPEC_INFO("LC->TAIn\n"); + return false; + } + + msleep(50); + }; + + tcpci_set_cc(tcpc, TYPEC_CC_RP_1_5); + usleep_range(1000, 2000); + + return tcpci_get_cc(tcpc) != 0; +} + +static inline bool typec_legacy_stable1(struct tcpc_device *tcpc) +{ + typec_legacy_charge(tcpc); + typec_legacy_discharge(tcpc); + TYPEC_INFO("LC->Stable\n"); + tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_LEGACY_STABLE); + + return true; +} + +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE2 + +static inline bool typec_is_run_legacy_stable2(struct tcpc_device *tcpc) +{ + bool run_legacy2; + uint8_t retry_max = TCPC_LEGACY_CABLE_RETRY_SOLUTION; + + run_legacy2 = tcpc->tcpc_flags & TCPC_FLAGS_PREFER_LEGACY2; + + TYPEC_INFO("LC->Retry%d\n", tcpc->typec_legacy_retry_wk++); + + if (tcpc->typec_legacy_retry_wk <= retry_max) + return run_legacy2; + + if (tcpc->typec_legacy_retry_wk > (retry_max*2)) + typec_legacy_reset_retry_wk(tcpc); + + return !run_legacy2; +} + +static inline bool typec_legacy_stable2(struct tcpc_device *tcpc) +{ + tcpc->typec_legacy_cable = 2; + TYPEC_INFO("LC->Stable2\n"); + typec_legacy_keep_default_rp(tcpc, true); + + tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_LEGACY_STABLE); + +#ifdef CONFIG_TYPEC_LEGACY2_AUTO_RECYCLE + tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_LEGACY_RECYCLE); +#endif /* CONFIG_TYPEC_LEGACY2_AUTO_RECYCLE */ + + return true; +} +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE2 */ + +static inline bool typec_legacy_confirm(struct tcpc_device *tcpc) +{ + TYPEC_INFO("LC->Confirm\n"); + tcpc->typec_legacy_cable = 1; + tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_NOT_LEGACY); + +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE2 + if (typec_is_run_legacy_stable2(tcpc)) + return typec_legacy_stable2(tcpc); +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE2 */ + + return typec_legacy_stable1(tcpc); +} + +static inline bool typec_legacy_check_cable(struct tcpc_device *tcpc) +{ + bool check_legacy = false; + + if (tcpc->tcpc_flags & TCPC_FLAGS_DISABLE_LEGACY) + return false; + +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE2 + if (tcpc->typec_legacy_cable == 2) { + typec_unattached_src_and_drp_entry(tcpc); + return true; + } +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE2 */ + + if (typec_check_cc(TYPEC_CC_VOLT_RD, TYPEC_CC_VOLT_OPEN) || + typec_check_cc(TYPEC_CC_VOLT_OPEN, TYPEC_CC_VOLT_RD)) + check_legacy = true; + +#if TCPC_LEGACY_CABLE_SUSPECT_THD + if (tcpc->typec_legacy_cable_suspect < + TCPC_LEGACY_CABLE_SUSPECT_THD) + check_legacy = false; +#endif /* TCPC_LEGACY_CABLE_SUSPECT_THD */ + + if (check_legacy) { + if (typec_legacy_suspect(tcpc)) { + typec_legacy_confirm(tcpc); + return true; + } + + tcpc->typec_legacy_cable = false; + tcpci_set_cc(tcpc, TYPEC_CC_RP); + } + + return false; +} + +static inline void typec_legacy_reset_timer(struct tcpc_device *tcpc) +{ +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE2 + if (tcpc->typec_legacy_cable == 2) + tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_LEGACY_RECYCLE); + + tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_LEGACY_STABLE); +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE2 */ +} + +static inline void typec_legacy_reach_vsafe5v(struct tcpc_device *tcpc) +{ + TYPEC_INFO("LC->Attached\n"); + tcpc->typec_legacy_cable = false; + tcpci_set_cc(tcpc, TYPEC_CC_RD); + typec_legacy_reset_timer(tcpc); +} + +static inline void typec_legacy_reach_vsafe0v(struct tcpc_device *tcpc) +{ + TYPEC_INFO("LC->Detached (PS)\n"); + tcpc->typec_legacy_cable = false; + typec_set_drp_toggling(tcpc); + tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_LEGACY_STABLE); +} + +static inline void typec_legacy_handle_ps_change( + struct tcpc_device *tcpc, int vbus_level) +{ +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE2 + if (tcpc->typec_legacy_cable != 1) + return; +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE2 */ + + if (vbus_level >= TCPC_VBUS_VALID) + typec_legacy_reach_vsafe5v(tcpc); + else if (vbus_level == TCPC_VBUS_SAFE0V) + typec_legacy_reach_vsafe0v(tcpc); +} + +static inline void typec_legacy_handle_detach(struct tcpc_device *tcpc) +{ +#if TCPC_LEGACY_CABLE_SUSPECT_THD + bool suspect_legacy = false; + + if (tcpc->typec_state == typec_attachwait_src) + suspect_legacy = true; + else if (tcpc->typec_state == typec_attached_src) { + if (tcpc->typec_attach_old != TYPEC_ATTACHED_SRC) + suspect_legacy = true; + } + + if (suspect_legacy) { + tcpc->typec_legacy_cable_suspect++; + TYPEC_INFO2("LC->Suspect: %d\n", + tcpc->typec_legacy_cable_suspect); + } +#endif /* TCPC_LEGACY_CABLE_SUSPECT_THD != 0 */ +} + +static inline int typec_legacy_handle_cc_open(struct tcpc_device *tcpc) +{ +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE2 + if (tcpc->typec_legacy_cable == 2) { + typec_legacy_keep_default_rp(tcpc, false); + return 1; + } +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE2 */ + + return 0; +} + +static inline int typec_legacy_handle_cc_present(struct tcpc_device *tcpc) +{ +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE2 + return tcpc->typec_legacy_cable == 1; +#else + return 1; +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE2 */ +} + +static inline int typec_legacy_handle_cc_change(struct tcpc_device *tcpc) +{ + int ret = 0; + + if (typec_is_cc_open() || typec_is_cable_only()) + ret = typec_legacy_handle_cc_open(tcpc); + else + ret = typec_legacy_handle_cc_present(tcpc); + + if (ret == 0) + return 0; + + TYPEC_INFO("LC->Detached (CC)\n"); + + tcpc->typec_legacy_cable = false; + typec_set_drp_toggling(tcpc); + typec_legacy_reset_timer(tcpc); + return 1; +} + +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */ + +/* + * [BLOCK] CC Change (after debounce) + */ + +#ifdef CONFIG_TYPEC_CAP_DBGACC +static void typec_debug_acc_attached_with_vbus_entry( + struct tcpc_device *tcpc) +{ + tcpc->typec_attach_new = TYPEC_ATTACHED_DEBUG; + typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE); +} +#endif /* CONFIG_TYPEC_CAP_DBGACC */ + +static inline void typec_debug_acc_attached_entry(struct tcpc_device *tcpc) +{ +#ifdef CONFIG_TYPEC_CAP_DBGACC + TYPEC_NEW_STATE(typec_debugaccessory); + TYPEC_DBG("[Debug] CC1&2 Both Rd\n"); + typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DBG_VSAFE5V); + + tcpci_report_power_control(tcpc, true); + tcpci_source_vbus(tcpc, + TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SOURCE_5V, -1); +#endif /* CONFIG_TYPEC_CAP_DBGACC */ +} + +#ifdef CONFIG_TYPEC_CAP_AUDIO_ACC_SINK_VBUS +static inline bool typec_audio_acc_sink_vbus( + struct tcpc_device *tcpc, bool vbus_valid) +{ + if (vbus_valid) { + tcpci_report_power_control(tcpc, true); + tcpci_sink_vbus(tcpc, + TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_5V, 500); + } else { + tcpci_sink_vbus(tcpc, + TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_0V, 0); + tcpci_report_power_control(tcpc, false); + } + + return true; +} +#endif /* CONFIG_TYPEC_CAP_AUDIO_ACC_SINK_VBUS */ + +static bool typec_is_fake_ra_rp30(struct tcpc_device *tcpc) +{ + if (TYPEC_CC_PULL_GET_RP_LVL(tcpc->typec_local_cc) == TYPEC_RP_3_0) { + __tcpci_set_cc(tcpc, TYPEC_CC_RP_DFT); + usleep_range(1000, 2000); + return tcpci_get_cc(tcpc) != 0; + } + + return false; +} + +static inline bool typec_audio_acc_attached_entry(struct tcpc_device *tcpc) +{ +#ifdef RICHTEK_PD_COMPLIANCE_FAKE_AUDIO_ACC + if (typec_is_fake_ra_rp30(tcpc)) { + TYPEC_DBG("[Audio] Fake Both Ra\n"); + if (typec_check_cc_any(TYPEC_CC_VOLT_RD)) + typec_cc_src_detect_entry(tcpc); + else + typec_cc_src_remove_entry(tcpc); + return 0; + } +#endif /* RICHTEK_PD_COMPLIANCE_FAKE_AUDIO_ACC */ + + TYPEC_NEW_STATE(typec_audioaccessory); + TYPEC_DBG("[Audio] CC1&2 Both Ra\n"); + tcpc->typec_attach_new = TYPEC_ATTACHED_AUDIO; + +#ifdef CONFIG_TYPEC_CAP_AUDIO_ACC_SINK_VBUS + if (tcpci_check_vbus_valid(tcpc)) + typec_audio_acc_sink_vbus(tcpc, true); +#endif /* CONFIG_TYPEC_CAP_AUDIO_ACC_SINK_VBUS */ + + return true; +} + +static inline bool typec_cc_change_source_entry(struct tcpc_device *tcpc) +{ + bool src_remove = false; + + switch (tcpc->typec_state) { + case typec_attached_src: + if (typec_get_cc_res() != TYPEC_CC_VOLT_RD) + src_remove = true; + break; + case typec_audioaccessory: + if (!typec_check_cc_both(TYPEC_CC_VOLT_RA)) + src_remove = true; + break; +#ifdef CONFIG_TYPEC_CAP_DBGACC + case typec_debugaccessory: + if (!typec_check_cc_both(TYPEC_CC_VOLT_RD)) + src_remove = true; + break; +#endif /* CONFIG_TYPEC_CAP_DBGACC */ + default: + if (typec_check_cc_both(TYPEC_CC_VOLT_RD)) + typec_debug_acc_attached_entry(tcpc); + else if (typec_check_cc_both(TYPEC_CC_VOLT_RA)) + typec_audio_acc_attached_entry(tcpc); + else if (typec_check_cc_any(TYPEC_CC_VOLT_RD)) + typec_cc_src_detect_entry(tcpc); + else + src_remove = true; + break; + } + + if (src_remove) + typec_cc_src_remove_entry(tcpc); + + return true; +} + +static inline bool typec_attached_snk_cc_change(struct tcpc_device *tcpc) +{ + uint8_t cc_res = typec_get_cc_res(); + + if (cc_res != tcpc->typec_remote_rp_level) { + TYPEC_INFO("RpLvl Change\n"); + tcpc->typec_remote_rp_level = cc_res; + +#ifdef CONFIG_TYPEC_CAP_CUSTOM_HV + if (tcpc->typec_during_custom_hv) + return true; +#endif /* CONFIG_TYPEC_CAP_CUSTOM_HV */ + + tcpci_sink_vbus(tcpc, + TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_5V, -1); + } + + return true; +} + +static inline bool typec_cc_change_sink_entry(struct tcpc_device *tcpc) +{ + bool snk_remove = false; + + switch (tcpc->typec_state) { + case typec_attached_snk: + if (typec_get_cc_res() == TYPEC_CC_VOLT_OPEN) + snk_remove = true; + else + typec_attached_snk_cc_change(tcpc); + break; + +#ifdef CONFIG_TYPEC_CAP_DBGACC_SNK + case typec_attached_dbgacc_snk: + if (typec_get_cc_res() == TYPEC_CC_VOLT_OPEN) + snk_remove = true; + else + typec_attached_snk_cc_change(tcpc); + break; +#endif /* CONFIG_TYPEC_CAP_DBGACC_SNK */ + +#ifdef CONFIG_TYPEC_CAP_CUSTOM_SRC + case typec_attached_custom_src: + if (typec_check_cc_any(TYPEC_CC_VOLT_OPEN)) + snk_remove = true; + break; +#endif /* CONFIG_TYPEC_CAP_CUSTOM_SRC */ + + default: + if (!typec_is_cc_open()) + typec_cc_snk_detect_entry(tcpc); + else + snk_remove = true; + } + + if (snk_remove) + typec_cc_snk_remove_entry(tcpc); + + return true; +} + +bool tcpc_typec_is_act_as_sink_role(struct tcpc_device *tcpc) +{ + bool as_sink = true; + uint8_t cc_sum; + + switch (TYPEC_CC_PULL_GET_RES(tcpc->typec_local_cc)) { + case TYPEC_CC_RP: + as_sink = false; + break; + case TYPEC_CC_RD: + as_sink = true; + break; + case TYPEC_CC_DRP: + cc_sum = typec_get_cc1() + typec_get_cc2(); + as_sink = (cc_sum >= TYPEC_CC_VOLT_SNK_DFT); + break; + } + + return as_sink; +} +EXPORT_SYMBOL(tcpc_typec_is_act_as_sink_role); + +static inline bool typec_handle_cc_changed_entry(struct tcpc_device *tcpc) +{ + TYPEC_INFO("[CC_Change] %d/%d\n", typec_get_cc1(), typec_get_cc2()); + + tcpc->typec_attach_new = tcpc->typec_attach_old; + + if (tcpc_typec_is_act_as_sink_role(tcpc)) + typec_cc_change_sink_entry(tcpc); + else + typec_cc_change_source_entry(tcpc); + + typec_alert_attach_state_change(tcpc); + return true; +} + +/* + * [BLOCK] Handle cc-change event (from HW) + */ + +static inline void typec_attach_wait_entry(struct tcpc_device *tcpc) +{ + bool as_sink; + int rv = 0; + uint32_t chip_id; +#ifdef CONFIG_USB_POWER_DELIVERY + struct pd_port *pd_port = &tcpc->pd_port; +#endif /* CONFIG_USB_POWER_DELIVERY */ + + if (tcpc->typec_attach_old == TYPEC_ATTACHED_SNK || + tcpc->typec_attach_old == TYPEC_ATTACHED_DBGACC_SNK) { +#ifdef CONFIG_USB_POWER_DELIVERY + if (pd_port->pe_data.pd_connected && pd_check_rev30(pd_port)) + pd_put_sink_tx_event(tcpc, typec_get_cc_res()); +#endif /* CONFIG_USB_POWER_DELIVERY */ + tcpc_enable_timer(tcpc, TYPEC_TIMER_PDDEBOUNCE); + TYPEC_DBG("RpLvl Alert\n"); + return; + } + + if (tcpc->typec_attach_old || + tcpc->typec_state == typec_attached_src) { + tcpc_reset_typec_debounce_timer(tcpc); + TYPEC_DBG("Attached, Ignore cc_attach\n"); +#ifndef CONFIG_USB_POWER_DELIVERY + typec_enable_vconn(tcpc); +#endif /* CONFIG_USB_POWER_DELIVERY */ + return; + } + + switch (tcpc->typec_state) { + +#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE + case typec_try_src: + tcpc_enable_timer(tcpc, TYPEC_TIMER_TRYCCDEBOUNCE); + return; + + case typec_trywait_snk: + tcpc_enable_timer(tcpc, TYPEC_TIMER_CCDEBOUNCE); + return; +#endif + +#ifdef CONFIG_TYPEC_CAP_TRY_SINK + case typec_try_snk: /* typec_drp_try_timeout = true */ + tcpc_enable_timer(tcpc, TYPEC_TIMER_TRYCCDEBOUNCE); + return; + + case typec_trywait_src: /* typec_drp_try_timeout = unknown */ + tcpc_enable_timer(tcpc, TYPEC_TIMER_TRYCCDEBOUNCE); + return; +#endif /* CONFIG_TYPEC_CAP_TRY_SINK */ + +#ifdef CONFIG_USB_POWER_DELIVERY + case typec_unattachwait_pe: + TYPEC_INFO("Force PE Idle\n"); + tcpc->pd_wait_pe_idle = false; + tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_PE_IDLE); + typec_unattached_power_entry(tcpc); + break; +#endif + default: + break; + } + + as_sink = tcpc_typec_is_act_as_sink_role(tcpc); + +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE + if (!as_sink && typec_legacy_check_cable(tcpc)) + return; +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */ + +#ifdef CONFIG_TYPEC_NOTIFY_ATTACHWAIT + tcpci_notify_attachwait_state(tcpc, as_sink); +#endif /* CONFIG_TYPEC_NOTIFY_ATTACHWAIT */ + rv = tcpci_get_chip_id(tcpc, &chip_id); + if (as_sink) { + TYPEC_NEW_STATE(typec_attachwait_snk); + if (!rv && SC2150A_DID == chip_id) + tcpci_set_cc(tcpc, TYPEC_CC_RD); + } else { + /* Advertise Rp level before Attached.SRC Ellisys 3.1.6359 */ + tcpci_set_cc(tcpc, + TYPEC_CC_PULL(tcpc->typec_local_rp_level, TYPEC_CC_RP)); + TYPEC_NEW_STATE(typec_attachwait_src); + } + + tcpc_enable_timer(tcpc, TYPEC_TIMER_CCDEBOUNCE); +} + +#ifdef TYPEC_EXIT_ATTACHED_SNK_VIA_VBUS +static inline int typec_attached_snk_cc_detach(struct tcpc_device *tcpc) +{ + tcpc_reset_typec_debounce_timer(tcpc); +#ifdef CONFIG_USB_POWER_DELIVERY + /* + * For Source detach during HardReset, + * However Apple TA may keep cc_open about 150 ms during HardReset + */ + if (tcpc->pd_wait_hard_reset_complete) { +#ifdef CONFIG_COMPATIBLE_APPLE_TA + TYPEC_INFO2("Detach_CC (HardReset), compatible apple TA\n"); + tcpc_enable_timer(tcpc, TYPEC_TIMER_APPLE_CC_OPEN); +#else + TYPEC_INFO2("Detach_CC (HardReset)\n"); + tcpc_enable_timer(tcpc, TYPEC_TIMER_PDDEBOUNCE); +#endif /* CONFIG_COMPATIBLE_APPLE_TA */ + } else if (tcpc->pd_port.pe_data.pd_prev_connected) { + TYPEC_INFO2("Detach_CC (PD)\n"); + tcpc_enable_timer(tcpc, TYPEC_TIMER_PDDEBOUNCE); + } +#endif /* CONFIG_USB_POWER_DELIVERY */ + return 0; +} +#endif /* TYPEC_EXIT_ATTACHED_SNK_VIA_VBUS */ + +static inline void typec_detach_wait_entry(struct tcpc_device *tcpc) +{ +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE + typec_legacy_handle_detach(tcpc); +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */ + + switch (tcpc->typec_state) { +#ifdef TYPEC_EXIT_ATTACHED_SNK_VIA_VBUS + case typec_attached_snk: +#ifdef CONFIG_TYPEC_CAP_DBGACC_SNK + case typec_attached_dbgacc_snk: +#endif /* CONFIG_TYPEC_CAP_DBGACC_SNK */ + typec_attached_snk_cc_detach(tcpc); + break; +#endif /* TYPEC_EXIT_ATTACHED_SNK_VIA_VBUS */ + + case typec_attached_src: + tcpc_enable_timer(tcpc, TYPEC_TIMER_SRCDISCONNECT); + break; + + case typec_audioaccessory: + tcpc_enable_timer(tcpc, TYPEC_TIMER_CCDEBOUNCE); + break; + +#ifdef TYPEC_EXIT_ATTACHED_SRC_NO_DEBOUNCE + case typec_attached_src: + TYPEC_INFO("Exit Attached.SRC immediately\n"); + tcpc_reset_typec_debounce_timer(tcpc); + + /* force to terminate TX */ + tcpci_init(tcpc, true); + + typec_cc_src_remove_entry(tcpc); + typec_alert_attach_state_change(tcpc); + break; +#endif /* TYPEC_EXIT_ATTACHED_SRC_NO_DEBOUNCE */ + +#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE + case typec_try_src: + if (tcpc->typec_drp_try_timeout) + tcpc_enable_timer(tcpc, TYPEC_TIMER_PDDEBOUNCE); + else { + tcpc_reset_typec_debounce_timer(tcpc); + TYPEC_DBG("[Try] Ignore cc_detach\n"); + } + break; +#endif /* CONFIG_TYPEC_CAP_TRY_SOURCE */ + +#ifdef CONFIG_TYPEC_CAP_TRY_SINK + case typec_trywait_src: + if (tcpc->typec_drp_try_timeout) + tcpc_enable_timer(tcpc, TYPEC_TIMER_TRYCCDEBOUNCE); + else { + tcpc_reset_typec_debounce_timer(tcpc); + TYPEC_DBG("[Try] Ignore cc_detach\n"); + } + break; +#endif /* CONFIG_TYPEC_CAP_TRY_SINK */ + default: + tcpc_enable_timer(tcpc, TYPEC_TIMER_PDDEBOUNCE); + break; + } +} + +static inline bool typec_is_cc_attach(struct tcpc_device *tcpc) +{ + bool cc_attach = false; + int cc1 = typec_get_cc1(); + int cc2 = typec_get_cc2(); + int cc_res = typec_get_cc_res(); + + tcpc->typec_cable_only = false; + +#ifdef RICHTEK_PD_COMPLIANCE_FAKE_RA_DETACH + if (tcpc->typec_attach_old == TYPEC_ATTACHED_SRC + && (cc_res == TYPEC_CC_VOLT_RA) && + (tcpc->typec_local_cc == TYPEC_CC_RP_DFT)) { + + tcpci_set_cc(tcpc, TYPEC_CC_RP_1_5); + usleep_range(1000, 2000); + + if (tcpci_get_cc(tcpc)) { + TYPEC_DBG("[Detach] Fake Ra\n"); + cc1 = typec_get_cc1(); + cc2 = typec_get_cc2(); + cc_res = typec_get_cc_res(); + } + } +#endif /* RICHTEK_PD_COMPLIANCE_FAKE_RA_DETACH */ + switch (tcpc->typec_attach_old) { + case TYPEC_ATTACHED_SNK: + case TYPEC_ATTACHED_SRC: + if ((cc_res != TYPEC_CC_VOLT_OPEN) && + (cc_res != TYPEC_CC_VOLT_RA)) + cc_attach = true; + break; +#ifdef CONFIG_TYPEC_CAP_CUSTOM_SRC + case TYPEC_ATTACHED_CUSTOM_SRC: + if ((cc_res != TYPEC_CC_VOLT_OPEN) && + (cc_res != TYPEC_CC_VOLT_RA)) + cc_attach = true; + break; +#endif /* CONFIG_TYPEC_CAP_CUSTOM_SRC */ + +#ifdef CONFIG_TYPEC_CAP_DBGACC_SNK + case TYPEC_ATTACHED_DBGACC_SNK: + if ((cc_res != TYPEC_CC_VOLT_OPEN) && + (cc_res != TYPEC_CC_VOLT_RA)) + cc_attach = true; + break; +#endif /* CONFIG_TYPEC_CAP_DBGACC_SNK */ + case TYPEC_ATTACHED_AUDIO: + if (typec_check_cc_both(TYPEC_CC_VOLT_RA)) + cc_attach = true; + break; + +#ifdef CONFIG_TYPEC_CAP_DBGACC + case TYPEC_ATTACHED_DEBUG: + if (typec_check_cc_both(TYPEC_CC_VOLT_RD)) + cc_attach = true; + break; +#endif /* CONFIG_TYPEC_CAP_DBGACC */ + default: /* TYPEC_UNATTACHED */ + if (cc1 != TYPEC_CC_VOLT_OPEN) + cc_attach = true; + + if (cc2 != TYPEC_CC_VOLT_OPEN) + cc_attach = true; + + /* Cable Only, no device */ + if ((cc1+cc2) == TYPEC_CC_VOLT_RA) { +#ifdef RICHTEK_PD_COMPLIANCE_FAKE_EMRAK_ONLY + if (typec_is_fake_ra_rp30(tcpc)) { + TYPEC_DBG("[Cable] Fake Ra\n"); + if ((cc1+cc2) == TYPEC_CC_VOLT_RD) + cc_attach = true; + break; + } +#endif /* RICHTEK_PD_COMPLIANCE_FAKE_EMRAK_ONLY */ + cc_attach = false; + tcpc->typec_cable_only = true; + TYPEC_DBG("[Cable] Ra Only\n"); + } + break; + } + + return cc_attach; +} + +/** + * typec_check_false_ra_detach + * + * Check the Single Ra resistance (eMark) exists or not when + * 1) Ra_detach INT triggered. + * 2) Wakeup_Timer triggered. + * + * If reentering low-power mode and eMark still exists, + * it may cause an infinite loop. + * + * If the CC status is both open, return true; otherwise return false + * + */ + +static inline bool typec_check_false_ra_detach(struct tcpc_device *tcpc) +{ + bool drp = tcpc->typec_role >= TYPEC_ROLE_DRP; + + /* + * If the DUT is DRP and current CC status has stopped toggle, + * let cc_handler to handle it later. (after debounce) + * + * If CC is toggling, force CC to present Rp. + */ + + if (drp) { + tcpci_get_cc(tcpc); + + if (!typec_is_drp_toggling()) { + TYPEC_DBG("False_RaDetach1 (%d, %d)\n", + typec_get_cc1(), typec_get_cc2()); + return true; + } + + tcpci_set_cc(tcpc, TYPEC_CC_RP); + usleep_range(1000, 2000); + } + + /* + * Check the CC status + * Rd (device) -> let cc_handler to handle it later + * eMark Only -> Reschedule wakeup timer + * + * Open -> (true condition) + * Ready to reenter low-power mode. + * If we repeatedly enter this situation, + * it will trigger low rp duty protection. + */ + + tcpci_get_cc(tcpc); + + if (typec_is_cc_open()) + tcpc->typec_cable_only = false; + else if (typec_get_cc1() + typec_get_cc2() == TYPEC_CC_VOLT_RA) { + tcpc->typec_cable_only = true; + TYPEC_DBG("False_RaDetach2 (eMark)\n"); + } else { + tcpc->typec_cable_only = false; + TYPEC_DBG("False_RaDetach3 (%d, %d)\n", + typec_get_cc1(), typec_get_cc2()); + return true; + } + +#ifdef CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG + if (tcpc->typec_cable_only && + tcpc->tcpc_flags & TCPC_FLAGS_LPM_WAKEUP_WATCHDOG) + tcpc_enable_wakeup_timer(tcpc, true); +#endif /* CONFIG_TYPEC_CAP_LPM_WAKEUP_WATCHDOG */ + +#ifdef CONFIG_TYPEC_WAKEUP_ONCE_LOW_DUTY + if (!tcpc->typec_cable_only) { + if (tcpc->typec_low_rp_duty_cntdown) + tcpci_set_low_rp_duty(tcpc, true); + else { + tcpc->typec_wakeup_once = false; + tcpc->typec_low_rp_duty_cntdown = true; + } + } +#endif /* CONFIG_TYPEC_WAKEUP_ONCE_LOW_DUTY */ + + /* + * If the DUT is DRP, force CC to toggle again. + */ + + if (drp) { + tcpci_set_cc(tcpc, TYPEC_CC_DRP); + tcpci_alert_status_clear(tcpc, + TCPC_REG_ALERT_EXT_RA_DETACH); + } + + return tcpc->typec_cable_only; +} + +int tcpc_typec_enter_lpm_again(struct tcpc_device *tcpc) +{ + bool check_ra = (tcpc->typec_lpm) || (tcpc->typec_cable_only); + + if (check_ra && typec_check_false_ra_detach(tcpc)) + return 0; + + TYPEC_DBG("RetryLPM\n"); + + tcpc->typec_lpm = true; + + tcpci_set_low_power_mode(tcpc, true, + (tcpc->typec_role != TYPEC_ROLE_SRC) ? + TYPEC_CC_DRP : TYPEC_CC_RP); + + return 0; +} + +#ifdef CONFIG_TYPEC_CAP_TRY_SINK +static inline int typec_handle_try_sink_cc_change( + struct tcpc_device *tcpc) +{ + /* + * The port shall wait for tDRPTry and only then begin + * begin monitoring the CC1 and CC2 pins for the SNK.Rp state + */ + + if (!tcpc->typec_drp_try_timeout) { + TYPEC_DBG("[Try.SNK] Ignore CC_Alert\n"); + return 1; + } + + if (!typec_is_cc_open()) { + tcpci_notify_attachwait_state(tcpc, true); + return 0; + } + + return 0; +} +#endif /* CONFIG_TYPEC_CAP_TRY_SINK */ + +static inline int typec_get_rp_present_flag(struct tcpc_device *tcpc) +{ + uint8_t rp_flag = 0; + + if (tcpc->typec_remote_cc[0] >= TYPEC_CC_VOLT_SNK_DFT + && tcpc->typec_remote_cc[0] != TYPEC_CC_DRP_TOGGLING) + rp_flag |= 1; + + if (tcpc->typec_remote_cc[1] >= TYPEC_CC_VOLT_SNK_DFT + && tcpc->typec_remote_cc[1] != TYPEC_CC_DRP_TOGGLING) + rp_flag |= 2; + + return rp_flag; +} + +static bool typec_is_cc_open_state(struct tcpc_device *tcpc) +{ + if (tcpc->typec_state == typec_errorrecovery) + return true; + + if (tcpc->typec_state == typec_disabled) + return true; + +#ifdef CONFIG_WATER_DETECTION + if ((tcpc->tcpc_flags & TCPC_FLAGS_WATER_DETECTION) && + (tcpc->typec_state == typec_water_protection_wait || + tcpc->typec_state == typec_water_protection)) + return true; +#endif /* CONFIG_WATER_DETECTION */ + + return false; +} + +static inline bool typec_is_ignore_cc_change( + struct tcpc_device *tcpc, uint8_t rp_present) +{ + if (typec_is_cc_open_state(tcpc)) + return true; + +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE + if (tcpc->typec_legacy_cable && + typec_legacy_handle_cc_change(tcpc)) { + return true; + } +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */ + +#ifdef CONFIG_USB_POWER_DELIVERY + if (tcpc->typec_state == typec_attachwait_snk && + typec_get_rp_present_flag(tcpc) == rp_present) { + TYPEC_DBG("[AttachWait] Ignore RpLvl Alert\n"); + return true; + } + + if (tcpc->pd_wait_pr_swap_complete) { + TYPEC_DBG("[PR.Swap] Ignore CC_Alert\n"); + return true; + } +#endif /* CONFIG_USB_POWER_DELIVERY */ + +#ifdef CONFIG_TYPEC_CAP_TRY_SINK + if (tcpc->typec_state == typec_try_snk) { + if (typec_handle_try_sink_cc_change(tcpc) > 0) + return true; + } + + if (tcpc->typec_state == typec_trywait_src_pe) { + TYPEC_DBG("[Try.PE] Ignore CC_Alert\n"); + return true; + } +#endif /* CONFIG_TYPEC_CAP_TRY_SINK */ + +#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE + if (tcpc->typec_state == typec_trywait_snk_pe) { + TYPEC_DBG("[Try.PE] Ignore CC_Alert\n"); + return true; + } +#endif /* CONFIG_TYPEC_CAP_TRY_SOURCE */ + + return false; +} + +int tcpc_typec_handle_cc_change(struct tcpc_device *tcpc) +{ + int ret; + uint8_t rp_present; + +#ifdef CONFIG_WATER_DETECTION + /* For ellisys rp/rp to rp/open */ + u8 typec_state_old = tcpc->typec_state; +#endif /* CONFIG_WATER_DETECTION */ + + rp_present = typec_get_rp_present_flag(tcpc); + + ret = tcpci_get_cc(tcpc); + if (ret < 0) + return ret; + + TYPEC_INFO("[CC_Alert] %d/%d\n", typec_get_cc1(), typec_get_cc2()); + + if (typec_is_cc_no_res()) { + TYPEC_DBG("[Warning] CC No Res\n"); + if (tcpc->typec_lpm && !tcpc->typec_cable_only) + typec_enter_low_power_mode(tcpc); + if (typec_is_drp_toggling()) + return 0; + } + +#ifdef CONFIG_TYPEC_CAP_NORP_SRC + if (typec_try_exit_norp_src(tcpc)) + return 0; +#endif /* CONFIG_TYPEC_CAP_NORP_SRC */ + + if (typec_is_ignore_cc_change(tcpc, rp_present)) + return 0; + + if (tcpc->typec_state == typec_attachwait_snk + || tcpc->typec_state == typec_attachwait_src) + typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE); + + if (typec_is_cc_attach(tcpc)) { + typec_disable_low_power_mode(tcpc); + typec_attach_wait_entry(tcpc); +#ifdef CONFIG_WATER_DETECTION + if (typec_state_old == typec_unattached_snk || + typec_state_old == typec_unattached_src) { +#ifdef CONFIG_WD_POLLING_ONLY + if (get_boot_mode() == KERNEL_POWER_OFF_CHARGING_BOOT + || get_boot_mode() == LOW_POWER_OFF_CHARGING_BOOT) + typec_check_water_status(tcpc); +#else + typec_check_water_status(tcpc); +#endif /* CONFIG_WD_POLLING_ONLY */ + } +#endif /* CONFIG_WATER_DETECTION */ + } else + typec_detach_wait_entry(tcpc); + + return 0; +} + +/* + * [BLOCK] Handle timeout event + */ + +#ifdef CONFIG_TYPEC_CAP_TRY_STATE +static inline int typec_handle_drp_try_timeout(struct tcpc_device *tcpc) +{ + bool src_detect = false, en_timer; + + tcpc->typec_drp_try_timeout = true; + tcpc_disable_timer(tcpc, TYPEC_TRY_TIMER_DRP_TRY); + + if (typec_is_drp_toggling()) { + TYPEC_DBG("[Warning] DRP Toggling\n"); + return 0; + } + + src_detect = typec_check_cc_any(TYPEC_CC_VOLT_RD); + + switch (tcpc->typec_state) { +#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE + case typec_try_src: + en_timer = !src_detect; + break; +#endif /* CONFIG_TYPEC_CAP_TRY_SOURCE */ + +#ifdef CONFIG_TYPEC_CAP_TRY_SINK + case typec_trywait_src: + en_timer = !src_detect; + break; + + case typec_try_snk: + en_timer = true; + if (!typec_is_cc_open()) + tcpci_notify_attachwait_state(tcpc, true); + break; +#endif /* CONFIG_TYPEC_CAP_TRY_SINK */ + + default: + en_timer = false; + break; + } + + if (en_timer) + tcpc_enable_timer(tcpc, TYPEC_TIMER_TRYCCDEBOUNCE); + + return 0; +} +#endif /* CONFIG_TYPEC_CAP_TRY_STATE */ + +static inline int typec_handle_debounce_timeout(struct tcpc_device *tcpc) +{ +#ifdef CONFIG_TYPEC_CAP_NORP_SRC + if (typec_is_cc_no_res() && tcpci_check_vbus_valid(tcpc) + && (tcpc->typec_state == typec_unattached_snk)) + return typec_norp_src_attached_entry(tcpc); +#endif + + if (typec_is_drp_toggling()) { + TYPEC_DBG("[Warning] DRP Toggling\n"); + return 0; + } + +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE + tcpc_disable_timer(tcpc, TYPEC_RT_TIMER_STATE_CHANGE); +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */ + + typec_handle_cc_changed_entry(tcpc); + return 0; +} + +static inline int typec_handle_error_recovery_timeout( + struct tcpc_device *tcpc) +{ +#ifdef CONFIG_USB_POWER_DELIVERY + tcpc->pd_wait_pe_idle = false; +#endif /* CONFIG_USB_POWER_DELIVERY */ + + typec_unattach_wait_pe_idle_entry(tcpc); + typec_alert_attach_state_change(tcpc); + return 0; +} + +#ifdef CONFIG_USB_POWER_DELIVERY +static inline int typec_handle_pe_idle(struct tcpc_device *tcpc) +{ + switch (tcpc->typec_state) { + +#ifdef CONFIG_TYPEC_CAP_TRY_SOURCE + case typec_trywait_snk_pe: + typec_trywait_snk_entry(tcpc); + break; +#endif + + case typec_unattachwait_pe: + typec_unattached_entry(tcpc); + break; + + default: + TYPEC_DBG("Dummy pe_idle\n"); + break; + } + + return 0; +} + +#ifdef CONFIG_USB_PD_WAIT_BC12 +static inline void typec_handle_pd_wait_bc12(struct tcpc_device *tcpc) +{ + int ret = 0; + uint8_t type = TYPEC_UNATTACHED; + union power_supply_propval val = {.intval = 0}; + + mutex_lock(&tcpc->access_lock); + + type = tcpc->typec_attach_new; + ret = power_supply_get_property(tcpc->usb_psy, + POWER_SUPPLY_PROP_REAL_TYPE, &val); + TYPEC_INFO("type=%d, ret,chg_type=%d,%d, count=%d\n", type, + ret, val.intval, tcpc->pd_wait_bc12_count); + + if (type != TYPEC_ATTACHED_SNK && type != TYPEC_ATTACHED_DBGACC_SNK) + goto out; + + if ((ret >= 0 && val.intval != POWER_SUPPLY_TYPE_UNKNOWN) || + tcpc->pd_wait_bc12_count >= 20) { + __pd_put_cc_attached_event(tcpc, type); + } else { + tcpc->pd_wait_bc12_count++; + tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_PD_WAIT_BC12); + } +out: + mutex_unlock(&tcpc->access_lock); +} +#endif /* CONFIG_USB_PD_WAIT_BC12 */ +#endif /* CONFIG_USB_POWER_DELIVERY */ + +static inline int typec_handle_src_reach_vsafe0v(struct tcpc_device *tcpc) +{ + if (typec_is_drp_toggling()) { + TYPEC_DBG("[Warning] DRP Toggling\n"); + return 0; + } + + tcpc->typec_reach_vsafe0v = true; + typec_cc_src_detect_vsafe0v_entry(tcpc); + typec_alert_attach_state_change(tcpc); + return 0; +} + +static inline int typec_handle_src_toggle_timeout(struct tcpc_device *tcpc) +{ +#ifdef CONFIG_TYPEC_CAP_ROLE_SWAP + if (tcpc->typec_during_role_swap) + return 0; +#endif /* CONFIG_TYPEC_CAP_ROLE_SWAP */ + + if (tcpc->typec_state == typec_unattached_src) { + typec_unattached_snk_and_drp_entry(tcpc); + typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE); +#ifdef CONFIG_TYPEC_CAP_NORP_SRC + typec_try_enter_norp_src(tcpc); +#endif /* CONFIG_TYPEC_CAP_NORP_SRC */ + } + + return 0; +} + +#ifdef CONFIG_TYPEC_CAP_ROLE_SWAP +static inline int typec_handle_role_swap_start(struct tcpc_device *tcpc) +{ + uint8_t role_swap = tcpc->typec_during_role_swap; + + if (role_swap == TYPEC_ROLE_SWAP_TO_SNK) { + TYPEC_INFO("Role Swap to Sink\n"); + tcpci_set_cc(tcpc, TYPEC_CC_RD); + tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_ROLE_SWAP_STOP); + } else if (role_swap == TYPEC_ROLE_SWAP_TO_SRC) { + TYPEC_INFO("Role Swap to Source\n"); + tcpci_set_cc(tcpc, TYPEC_CC_RP); + tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_ROLE_SWAP_STOP); + } + + return 0; +} + +static inline int typec_handle_role_swap_stop(struct tcpc_device *tcpc) +{ + if (tcpc->typec_during_role_swap) { + TYPEC_INFO("TypeC Role Swap Failed\n"); + tcpc->typec_during_role_swap = TYPEC_ROLE_SWAP_NONE; + tcpc_enable_timer(tcpc, TYPEC_TIMER_PDDEBOUNCE); + } + + return 0; +} +#endif /* CONFIG_TYPEC_CAP_ROLE_SWAP */ + +int tcpc_typec_handle_timeout(struct tcpc_device *tcpc, uint32_t timer_id) +{ + int ret = 0; + +#ifdef CONFIG_TYPEC_CAP_TRY_STATE + if (timer_id == TYPEC_TRY_TIMER_DRP_TRY) + return typec_handle_drp_try_timeout(tcpc); +#endif /* CONFIG_TYPEC_CAP_TRY_STATE */ + +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE + if (timer_id == TYPEC_TIMER_DRP_SRC_TOGGLE && + (tcpc->typec_state != typec_unattached_src)) { + TCPC_DBG("Dummy SRC_TOGGLE\n"); + return 0; + } +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */ + + if (timer_id >= TYPEC_TIMER_START_ID) + tcpc_reset_typec_debounce_timer(tcpc); + else if (timer_id >= TYPEC_RT_TIMER_START_ID) + tcpc_disable_timer(tcpc, timer_id); + + if (timer_id == TYPEC_TIMER_ERROR_RECOVERY) + return typec_handle_error_recovery_timeout(tcpc); + else if (timer_id == TYPEC_RT_TIMER_STATE_CHANGE) + return typec_alert_attach_state_change(tcpc); + else if (typec_is_cc_open_state(tcpc)) { + TYPEC_DBG("[Open] Ignore timer_evt\n"); + return 0; + } + +#ifdef CONFIG_USB_POWER_DELIVERY + if (tcpc->pd_wait_pr_swap_complete) { + TYPEC_DBG("[PR.Swap] Ignore timer_evt\n"); + return 0; + } +#endif /* CONFIG_USB_POWER_DELIVERY */ + + switch (timer_id) { +#ifdef CONFIG_USB_POWER_DELIVERY +#ifdef CONFIG_COMPATIBLE_APPLE_TA + case TYPEC_TIMER_APPLE_CC_OPEN: +#endif /* CONFIG_COMPATIBLE_APPLE_TA */ +#endif /* CONFIG_USB_POWER_DELIVERY */ + case TYPEC_TIMER_CCDEBOUNCE: + case TYPEC_TIMER_PDDEBOUNCE: + case TYPEC_TIMER_TRYCCDEBOUNCE: + case TYPEC_TIMER_SRCDISCONNECT: + /* fall through */ +#ifdef CONFIG_TYPEC_CAP_NORP_SRC + case TYPEC_TIMER_NORP_SRC: +#endif /* CONFIG_TYPEC_CAP_NORP_SRC */ + ret = typec_handle_debounce_timeout(tcpc); + break; + +#ifdef CONFIG_USB_POWER_DELIVERY + case TYPEC_RT_TIMER_PE_IDLE: + ret = typec_handle_pe_idle(tcpc); + break; +#ifdef CONFIG_USB_PD_WAIT_BC12 + case TYPEC_RT_TIMER_PD_WAIT_BC12: + typec_handle_pd_wait_bc12(tcpc); + break; +#endif /* CONFIG_USB_PD_WAIT_BC12 */ +#endif /* CONFIG_USB_POWER_DELIVERY */ + +#ifdef CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_DELAY + case TYPEC_RT_TIMER_SAFE0V_DELAY: + ret = typec_handle_src_reach_vsafe0v(tcpc); + break; +#endif /* CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_DELAY */ + + case TYPEC_RT_TIMER_LOW_POWER_MODE: + if (tcpc->typec_lpm) + typec_try_low_power_mode(tcpc); + break; + +#ifdef CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT + case TYPEC_RT_TIMER_SAFE0V_TOUT: + TCPC_INFO("VSafe0V TOUT (%d)\n", tcpc->vbus_level); + + if (!tcpci_check_vbus_valid_from_ic(tcpc)) + ret = tcpc_typec_handle_vsafe0v(tcpc); + break; +#endif /* CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_TIMEOUT */ + + case TYPEC_TIMER_DRP_SRC_TOGGLE: + ret = typec_handle_src_toggle_timeout(tcpc); + break; + +#ifdef CONFIG_TYPEC_CAP_ROLE_SWAP + case TYPEC_RT_TIMER_ROLE_SWAP_START: + typec_handle_role_swap_start(tcpc); + break; + + case TYPEC_RT_TIMER_ROLE_SWAP_STOP: + typec_handle_role_swap_stop(tcpc); + break; +#endif /* CONFIG_TYPEC_CAP_ROLE_SWAP */ + + case TYPEC_RT_TIMER_DISCHARGE: + if (!tcpc->typec_power_ctrl) { + mutex_lock(&tcpc->access_lock); + tcpci_enable_auto_discharge(tcpc, false); + tcpci_enable_force_discharge(tcpc, false, 0); + mutex_unlock(&tcpc->access_lock); + } + break; + +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE + case TYPEC_RT_TIMER_NOT_LEGACY: + tcpc->typec_legacy_cable = false; + typec_legacy_reset_retry_wk(tcpc); + typec_legacy_reset_cable_suspect(tcpc); + break; + +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE2 + case TYPEC_RT_TIMER_LEGACY_STABLE: + if (tcpc->typec_legacy_cable) + tcpc->typec_legacy_retry_wk--; + break; + +#ifdef CONFIG_TYPEC_LEGACY2_AUTO_RECYCLE + case TYPEC_RT_TIMER_LEGACY_RECYCLE: + if (tcpc->typec_legacy_cable == 2) { + TYPEC_INFO("LC->Recycle\n"); + tcpc->typec_legacy_cable = false; + typec_legacy_keep_default_rp(tcpc, false); + typec_set_drp_toggling(tcpc); + } + break; +#endif /* CONFIG_TYPEC_LEGACY2_AUTO_RECYCLE */ +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE2 */ +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */ + } + + return ret; +} + +/* + * [BLOCK] Handle ps-change event + */ + +static inline int typec_handle_vbus_present(struct tcpc_device *tcpc) +{ + switch (tcpc->typec_wait_ps_change) { + case TYPEC_WAIT_PS_SNK_VSAFE5V: + typec_cc_snk_detect_vsafe5v_entry(tcpc); + typec_alert_attach_state_change(tcpc); + break; + case TYPEC_WAIT_PS_SRC_VSAFE5V: + typec_source_attached_with_vbus_entry(tcpc); + +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE + if (typec_get_cc_res() != TYPEC_CC_VOLT_RD) { + typec_postpone_state_change(tcpc); + break; + } +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */ + + typec_alert_attach_state_change(tcpc); + break; +#ifdef CONFIG_TYPEC_CAP_DBGACC + case TYPEC_WAIT_PS_DBG_VSAFE5V: + typec_debug_acc_attached_with_vbus_entry(tcpc); + typec_alert_attach_state_change(tcpc); + break; +#endif /* CONFIG_TYPEC_CAP_DBGACC */ + } + + return 0; +} + +static inline int typec_attached_snk_vbus_absent(struct tcpc_device *tcpc) +{ +#ifdef TYPEC_EXIT_ATTACHED_SNK_VIA_VBUS +#ifdef CONFIG_USB_POWER_DELIVERY +#ifdef CONFIG_USB_PD_DIRECT_CHARGE + if (tcpc->pd_during_direct_charge && + !tcpci_check_vsafe0v(tcpc, true)) { + TYPEC_DBG("Ignore vbus_absent(snk), DirectCharge\n"); + return 0; + } +#endif /* CONFIG_USB_PD_DIRECT_CHARGE */ + + if (tcpc->pd_wait_hard_reset_complete) { +#ifdef CONFIG_COMPATIBLE_APPLE_TA + TYPEC_DBG("Ignore vbus_absent(snk) and CC, HReset(apple)\n"); + return 0; +#else + if (typec_get_cc_res() != TYPEC_CC_VOLT_OPEN) { + TYPEC_DBG( + "Ignore vbus_absent(snk), HReset & CC!=0\n"); + return 0; + } +#endif /* CONFIG_COMPATIBLE_APPLE_TA */ + } +#endif /* CONFIG_USB_POWER_DELIVERY */ + + typec_unattach_wait_pe_idle_entry(tcpc); + typec_alert_attach_state_change(tcpc); +#endif /* TYPEC_EXIT_ATTACHED_SNK_VIA_VBUS */ + + return 0; +} + + +static inline int typec_handle_vbus_absent(struct tcpc_device *tcpc) +{ +#ifdef CONFIG_USB_POWER_DELIVERY + if (tcpc->pd_wait_pr_swap_complete) { + TYPEC_DBG("[PR.Swap] Ignore vbus_absent\n"); + return 0; + } +#endif /* CONFIG_USB_POWER_DELIVERY */ + + switch (tcpc->typec_state) { + case typec_attached_snk: +#ifdef CONFIG_TYPEC_CAP_DBGACC_SNK + case typec_attached_dbgacc_snk: +#endif /* CONFIG_TYPEC_CAP_DBGACC_SNK */ + typec_attached_snk_vbus_absent(tcpc); + break; + default: + break; + } + +#ifndef CONFIG_TCPC_VSAFE0V_DETECT + tcpc_typec_handle_vsafe0v(tcpc); +#endif /* CONFIG_TCPC_VSAFE0V_DETECT */ + + return 0; +} + +int tcpc_typec_handle_ps_change(struct tcpc_device *tcpc, int vbus_level) +{ + uint32_t chip_id; + int rv = 0; + + tcpc->typec_reach_vsafe0v = false; + + rv = tcpci_get_chip_id(tcpc, &chip_id); + if (!rv && chip_id == SC2150A_DID) { + // open vsafe0.8v irq + if (vbus_level >= TCPC_VBUS_VALID) { + typec_disable_low_power_mode(tcpc); + } else { + typec_enter_low_power_mode(tcpc); + } + } + +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE + if (tcpc->typec_legacy_cable) { + typec_legacy_handle_ps_change(tcpc, vbus_level); + return 0; + } +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */ + +#ifdef CONFIG_TYPEC_CAP_NORP_SRC + if (!typec_try_enter_norp_src(tcpc)) + if (typec_try_exit_norp_src(tcpc)) + return 0; +#endif /* CONFIG_TYPEC_CAP_NORP_SRC */ + + if (typec_is_cc_no_res()) { + TYPEC_DBG("[Warning] CC No Res\n"); + if (tcpc->typec_lpm && !tcpc->typec_cable_only) + typec_enter_low_power_mode(tcpc); + if (typec_is_drp_toggling()) + return 0; + } + +#ifdef CONFIG_TYPEC_CAP_AUDIO_ACC_SINK_VBUS + if (tcpc->typec_state == typec_audioaccessory) { + return typec_audio_acc_sink_vbus( + tcpc, vbus_level >= TCPC_VBUS_VALID); + } +#endif /* CONFIG_TYPEC_CAP_AUDIO_ACC_SINK_VBUS */ + + if (vbus_level >= TCPC_VBUS_VALID) + return typec_handle_vbus_present(tcpc); + + return typec_handle_vbus_absent(tcpc); +} + +/* + * [BLOCK] Handle PE event + */ + +#ifdef CONFIG_USB_POWER_DELIVERY + +int tcpc_typec_handle_pe_pr_swap(struct tcpc_device *tcpc) +{ + int ret = 0; + + mutex_lock(&tcpc->typec_lock); + switch (tcpc->typec_state) { + case typec_attached_snk: + TYPEC_NEW_STATE(typec_attached_src); + tcpc->typec_is_attached_src = true; + tcpc->typec_attach_new = TYPEC_ATTACHED_SRC; + tcpci_set_cc(tcpc, + TYPEC_CC_PULL(tcpc->typec_local_rp_level, TYPEC_CC_RP)); + break; + case typec_attached_src: + TYPEC_NEW_STATE(typec_attached_snk); + tcpc->typec_is_attached_src = false; + tcpc->typec_attach_new = TYPEC_ATTACHED_SNK; + tcpci_set_cc(tcpc, TYPEC_CC_RD); + break; + default: + break; + } + + typec_alert_attach_state_change(tcpc); + mutex_unlock(&tcpc->typec_lock); + return ret; +} + +#endif /* CONFIG_USB_POWER_DELIVERY */ + +/* + * [BLOCK] Handle reach vSafe0V event + */ + +int tcpc_typec_handle_vsafe0v(struct tcpc_device *tcpc) +{ +#ifdef CONFIG_WATER_DETECTION + if ((tcpc->tcpc_flags & TCPC_FLAGS_WATER_DETECTION) && + tcpc->typec_state == typec_water_protection_wait) { + TYPEC_NEW_STATE(typec_water_protection); + tcpci_set_water_protection(tcpc, true); + return 0; + } +#endif /* CONFIG_WATER_DETECTION */ + + if (tcpc->typec_wait_ps_change == TYPEC_WAIT_PS_SRC_VSAFE0V) { +#ifdef CONFIG_TYPEC_ATTACHED_SRC_SAFE0V_DELAY + tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_SAFE0V_DELAY); +#else + typec_handle_src_reach_vsafe0v(tcpc); +#endif + } + + return 0; +} + +/* + * [BLOCK] TCPCI TypeC I/F + */ + +#if TYPEC_INFO_ENABLE +static const char *const typec_role_name[] = { + "UNKNOWN", + "SNK", + "SRC", + "DRP", + "TrySRC", + "TrySNK", +}; +#endif /* TYPEC_INFO_ENABLE */ + +#ifdef CONFIG_TYPEC_CAP_ROLE_SWAP +int tcpc_typec_swap_role(struct tcpc_device *tcpc) +{ + if (tcpc->typec_role < TYPEC_ROLE_DRP) + return TCPM_ERROR_NOT_DRP_ROLE; + + if (tcpc->typec_during_role_swap) + return TCPM_ERROR_DURING_ROLE_SWAP; + + switch (tcpc->typec_attach_old) { + case TYPEC_ATTACHED_SNK: + tcpc->typec_during_role_swap = TYPEC_ROLE_SWAP_TO_SRC; + break; + case TYPEC_ATTACHED_SRC: + tcpc->typec_during_role_swap = TYPEC_ROLE_SWAP_TO_SNK; + break; + } + + if (tcpc->typec_during_role_swap) { + TYPEC_INFO("TypeC Role Swap Start\n"); + tcpci_set_cc(tcpc, TYPEC_CC_OPEN); + tcpc_enable_timer(tcpc, TYPEC_RT_TIMER_ROLE_SWAP_START); + return TCPM_SUCCESS; + } + + return TCPM_ERROR_UNATTACHED; +} +#endif /* CONFIG_TYPEC_CAP_ROLE_SWAP */ + +int tcpc_typec_set_rp_level(struct tcpc_device *tcpc, uint8_t rp_lvl) +{ + switch (rp_lvl) { + case TYPEC_RP_DFT: + case TYPEC_RP_1_5: + case TYPEC_RP_3_0: + TYPEC_INFO("TypeC-Rp: %d\n", rp_lvl); + tcpc->typec_local_rp_level = rp_lvl; + break; + default: + TYPEC_INFO("TypeC-Unknown-Rp (%d)\n", rp_lvl); + return -EINVAL; + } + + return 0; +} + +int tcpc_typec_error_recovery(struct tcpc_device *tcpc) +{ + if (tcpc->typec_state != typec_errorrecovery) + typec_error_recovery_entry(tcpc); + + return 0; +} + +int tcpc_typec_disable(struct tcpc_device *tcpc) +{ + if (tcpc->typec_state != typec_disabled) + typec_disable_entry(tcpc); + + return 0; +} + +int tcpc_typec_enable(struct tcpc_device *tcpc) +{ + if (tcpc->typec_state == typec_disabled) + typec_unattached_entry(tcpc); + + return 0; +} + +int tcpc_typec_change_role( + struct tcpc_device *tcpc, uint8_t typec_role, bool postpone) +{ + if (typec_role == TYPEC_ROLE_UNKNOWN || + typec_role >= TYPEC_ROLE_NR) { + TYPEC_INFO("Wrong TypeC-Role: %d\n", typec_role); + return -EINVAL; + } + + if (tcpc->typec_role_new == typec_role) { + TYPEC_INFO("typec_new_role: %s is the same\n", + typec_role_name[typec_role]); + return 0; + } + tcpc->typec_role_new = typec_role; + + TYPEC_INFO("typec_new_role: %s\n", typec_role_name[typec_role]); + + if (!postpone || tcpc->typec_attach_old == TYPEC_UNATTACHED) + return tcpc_typec_error_recovery(tcpc); + else + return 0; +} + +#ifdef CONFIG_TYPEC_CAP_POWER_OFF_CHARGE +static int typec_init_power_off_charge(struct tcpc_device *tcpc) +{ + bool cc_open; + int ret = tcpci_get_cc(tcpc); + + if (ret < 0) + return ret; + + if (tcpc->typec_role == TYPEC_ROLE_SRC) + return 0; + + cc_open = typec_is_cc_open(); + +#ifndef CONFIG_TYPEC_CAP_NORP_SRC + if (cc_open) + return 0; +#endif /* CONFIG_TYPEC_CAP_NORP_SRC */ + + if (!tcpci_check_vbus_valid(tcpc)) + return 0; + + TYPEC_INFO2("PowerOffCharge\n"); + + TYPEC_NEW_STATE(typec_unattached_snk); + typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_DISABLE); + + tcpci_set_cc(tcpc, TYPEC_CC_DRP); + typec_enable_low_power_mode(tcpc, TYPEC_CC_DRP); + usleep_range(1000, 2000); + +#ifdef CONFIG_TYPEC_CAP_NORP_SRC + if (cc_open) { + tcpc_enable_timer(tcpc, TYPEC_TIMER_PDDEBOUNCE); + return 1; + } +#endif /* CONFIG_TYPEC_CAP_NORP_SRC */ + + tcpci_set_cc(tcpc, TYPEC_CC_RD); + + return 1; +} +#endif /* CONFIG_TYPEC_CAP_POWER_OFF_CHARGE */ + +int tcpc_typec_init(struct tcpc_device *tcpc, uint8_t typec_role) +{ + int ret = 0; + + if (typec_role == TYPEC_ROLE_UNKNOWN || + typec_role >= TYPEC_ROLE_NR) { + TYPEC_INFO("Wrong TypeC-Role: %d\n", typec_role); + return -EINVAL; + } + + TYPEC_INFO("typec_init: %s\n", typec_role_name[typec_role]); + + tcpc->typec_role = typec_role; + tcpc->typec_role_new = typec_role; + tcpc->typec_attach_new = TYPEC_UNATTACHED; + tcpc->typec_attach_old = TYPEC_UNATTACHED; + + tcpc->typec_remote_cc[0] = TYPEC_CC_VOLT_OPEN; + tcpc->typec_remote_cc[1] = TYPEC_CC_VOLT_OPEN; + + mutex_lock(&tcpc->access_lock); + tcpc->wake_lock_pd = 0; + mutex_unlock(&tcpc->access_lock); + tcpc->typec_usb_sink_curr = CONFIG_TYPEC_SNK_CURR_DFT; + +#ifdef CONFIG_TYPEC_CAP_CUSTOM_HV + tcpc->typec_during_custom_hv = false; +#endif /* CONFIG_TYPEC_CAP_CUSTOM_HV */ + +#ifdef CONFIG_TYPEC_CHECK_LEGACY_CABLE + tcpc->typec_legacy_cable = false; + typec_legacy_reset_retry_wk(tcpc); + typec_legacy_reset_cable_suspect(tcpc); +#endif /* CONFIG_TYPEC_CHECK_LEGACY_CABLE */ + +#ifdef CONFIG_TYPEC_CAP_POWER_OFF_CHARGE + ret = typec_init_power_off_charge(tcpc); + if (ret != 0) + return ret; +#endif /* CONFIG_TYPEC_CAP_POWER_OFF_CHARGE */ + +#ifdef CONFIG_TYPEC_POWER_CTRL_INIT + tcpc->typec_power_ctrl = true; +#endif /* CONFIG_TYPEC_POWER_CTRL_INIT */ + + typec_unattached_entry(tcpc); + return ret; +} + +void tcpc_typec_deinit(struct tcpc_device *tcpc) +{ +} + +#ifdef CONFIG_WATER_DETECTION +int tcpc_typec_handle_wd(struct tcpc_device *tcpc, bool wd) +{ + int ret = 0; + + if (!(tcpc->tcpc_flags & TCPC_FLAGS_WATER_DETECTION)) + return 0; + + TYPEC_INFO("%s %d\n", __func__, wd); + if (!wd) { + tcpci_set_water_protection(tcpc, false); + tcpc_typec_error_recovery(tcpc); + goto out; + } + +#ifdef CONFIG_MTK_KERNEL_POWER_OFF_CHARGING + ret = get_boot_mode(); + if (ret == KERNEL_POWER_OFF_CHARGING_BOOT || + ret == LOW_POWER_OFF_CHARGING_BOOT) { + TYPEC_INFO("KPOC does not enter water protection\n"); + goto out; + } +#endif /* CONFIG_MTK_KERNEL_POWER_OFF_CHARGING */ + + tcpc->typec_attach_new = TYPEC_UNATTACHED; + ret = tcpci_set_cc(tcpc, TYPEC_CC_OPEN); +#ifdef CONFIG_TCPC_VSAFE0V_DETECT_IC + ret = tcpci_is_vsafe0v(tcpc); + if (ret == 0) { + TYPEC_NEW_STATE(typec_water_protection_wait); + typec_wait_ps_change(tcpc, TYPEC_WAIT_PS_SRC_VSAFE0V); + } else { + TYPEC_NEW_STATE(typec_water_protection); + tcpci_set_water_protection(tcpc, true); + } +#else + /* TODO: Wait ps change ? */ +#endif /* CONFIG_TCPC_VSAFE0V_DETECT_IC */ + +out: + tcpci_notify_wd_status(tcpc, wd); + if (tcpc->typec_state == typec_water_protection || + tcpc->typec_state == typec_water_protection_wait) { + typec_alert_attach_state_change(tcpc); + tcpc->typec_attach_old = tcpc->typec_attach_new; + } + return ret; +} +#endif /* CONFIG_WATER_DETECTION */ + +#ifdef CONFIG_CABLE_TYPE_DETECTION +int tcpc_typec_handle_ctd(struct tcpc_device *tcpc, + enum tcpc_cable_type cable_type) +{ + int ret; + + if (!(tcpc->tcpc_flags & TCPC_FLAGS_CABLE_TYPE_DETECTION)) + return 0; + + + /* Filter out initial no cable */ + if (cable_type == TCPC_CABLE_TYPE_C2C) { + ret = tcpci_get_cc(tcpc); + if (ret >= 0) { + if (typec_is_cc_no_res() && + (tcpc->typec_state == typec_unattached_snk || + tcpc->typec_state == typec_unattached_src)) { + TCPC_INFO("%s toggling or open\n", __func__); + cable_type = TCPC_CABLE_TYPE_NONE; + } + } + } + + TCPC_INFO("%s cable (%d, %d)\n", __func__, tcpc->typec_cable_type, + cable_type); + + if (tcpc->typec_cable_type == cable_type) + return 0; + + if (tcpc->typec_cable_type != TCPC_CABLE_TYPE_NONE && + cable_type != TCPC_CABLE_TYPE_NONE) { + TCPC_INFO("%s ctd done once %d\n", __func__, + tcpc->typec_cable_type); + return 0; + } + + tcpc->typec_cable_type = cable_type; + + TCPC_INFO("%s cable type %d\n", __func__, tcpc->typec_cable_type); + tcpci_notify_cable_type(tcpc); + return 0; +} +#endif /* CONFIG_CABLE_TYPE_DETECTION */ diff --git a/drivers/usb/typec/mmi_tcpc/tcpm.c b/drivers/usb/typec/mmi_tcpc/tcpm.c new file mode 100644 index 000000000000..d230d16ffd95 --- /dev/null +++ b/drivers/usb/typec/mmi_tcpc/tcpm.c @@ -0,0 +1,2018 @@ +/* + * Copyright (C) 2020 Richtek Inc. + * + * Power Delivery Managert Driver + * + * 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. + */ + +#include "inc/tcpm.h" +#include "inc/tcpci.h" +#include "inc/tcpci_typec.h" + +#ifdef CONFIG_USB_POWER_DELIVERY +#include "inc/pd_core.h" +#include "inc/pd_dpm_core.h" +#include "pd_dpm_prv.h" +#include "inc/pd_policy_engine.h" +#include "inc/pd_dpm_pdo_select.h" +#endif /* CONFIG_USB_POWER_DELIVERY */ + + +/* Check status */ +static int tcpm_check_typec_attached(struct tcpc_device *tcpc) +{ + if (tcpc->typec_attach_old == TYPEC_UNATTACHED || + tcpc->typec_attach_new == TYPEC_UNATTACHED) + return TCPM_ERROR_UNATTACHED; + + return 0; +} + +#ifdef CONFIG_USB_POWER_DELIVERY +static int tcpm_check_pd_attached(struct tcpc_device *tcpc) +{ + int ret = TCPM_SUCCESS; + struct pd_port *pd_port = &tcpc->pd_port; + struct pe_data *pe_data = &pd_port->pe_data; + + tcpci_lock_typec(tcpc); + + if (!tcpc->pd_inited_flag) { + ret = TCPM_ERROR_PE_NOT_READY; + goto unlock_typec_out; + } + + ret = tcpm_check_typec_attached(tcpc); + +unlock_typec_out: + tcpci_unlock_typec(tcpc); + if (ret != TCPM_SUCCESS) + return ret; + + mutex_lock(&pd_port->pd_lock); + + if (!pe_data->pd_prev_connected) { + ret = TCPM_ERROR_NO_PD_CONNECTED; + goto unlock_pd_out; + } + + if (!pe_data->pe_ready) + ret = TCPM_ERROR_PE_NOT_READY; + +unlock_pd_out: + mutex_unlock(&pd_port->pd_lock); + return ret; +} +#endif /* CONFIG_USB_POWER_DELIVERY */ + + +/* Inquire TCPC status */ + +int tcpm_shutdown(struct tcpc_device *tcpc) +{ +#ifdef CONFIG_TCPC_SHUTDOWN_VBUS_DISABLE + if (tcpc->typec_power_ctrl) + tcpci_disable_vbus_control(tcpc); +#endif /* CONFIG_TCPC_SHUTDOWN_VBUS_DISABLE */ + + if (tcpc->ops->deinit) + tcpc->ops->deinit(tcpc); + + return 0; +} +EXPORT_SYMBOL(tcpm_shutdown); + +int tcpm_inquire_remote_cc(struct tcpc_device *tcpc, + uint8_t *cc1, uint8_t *cc2, bool from_ic) +{ + int rv = 0; + + tcpci_lock_typec(tcpc); + if (from_ic) { + rv = tcpci_get_cc(tcpc); + if (rv < 0) + goto out; + } + + *cc1 = tcpc->typec_remote_cc[0]; + *cc2 = tcpc->typec_remote_cc[1]; +out: + tcpci_unlock_typec(tcpc); + return rv; +} + +int tcpm_inquire_typec_remote_rp_curr(struct tcpc_device *tcpc) +{ + int rp_lvl, ret = 0; + + if (tcpm_check_typec_attached(tcpc)) + return 0; + + rp_lvl = tcpc->typec_remote_rp_level; + switch (rp_lvl) { + case TYPEC_CC_VOLT_SNK_DFT: + ret = 500; + break; + case TYPEC_CC_VOLT_SNK_1_5: + ret = 1500; + break; + case TYPEC_CC_VOLT_SNK_3_0: + ret = 3000; + break; + default: + break; + } + + return ret; +} +EXPORT_SYMBOL(tcpm_inquire_typec_remote_rp_curr); + +int tcpm_inquire_vbus_level( + struct tcpc_device *tcpc, bool from_ic) +{ + int rv = 0; + uint16_t power_status = 0; + + if (from_ic) { + rv = tcpci_get_power_status(tcpc, &power_status); + if (rv < 0) + return rv; + } + + return tcpc->vbus_level; +} + +bool tcpm_inquire_cc_polarity( + struct tcpc_device *tcpc) +{ + return tcpc->typec_polarity; +} +EXPORT_SYMBOL(tcpm_inquire_cc_polarity); + +uint8_t tcpm_inquire_typec_attach_state( + struct tcpc_device *tcpc) +{ + return tcpc->typec_attach_new; +} + +uint8_t tcpm_inquire_typec_role( + struct tcpc_device *tcpc) +{ + return tcpc->typec_role; +} +EXPORT_SYMBOL(tcpm_inquire_typec_role); + +uint8_t tcpm_inquire_typec_local_rp( + struct tcpc_device *tcpc) +{ + return tcpc->typec_local_rp_level; +} + +int tcpm_typec_set_usb_sink_curr( + struct tcpc_device *tcpc, int curr) +{ + bool force_sink_vbus = true; + +#ifdef CONFIG_USB_POWER_DELIVERY + struct pd_port *pd_port = &tcpc->pd_port; + + if (pd_port->pe_data.pd_prev_connected) + force_sink_vbus = false; +#endif /* CONFIG_USB_POWER_DELIVERY */ + + tcpci_lock_typec(tcpc); + tcpc->typec_usb_sink_curr = curr; + + if (tcpc->typec_remote_rp_level != TYPEC_CC_VOLT_SNK_DFT) + force_sink_vbus = false; + + if (force_sink_vbus) { + tcpci_sink_vbus(tcpc, + TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_5V, -1); + } + tcpci_unlock_typec(tcpc); + + return 0; +} + +int tcpm_typec_set_rp_level( + struct tcpc_device *tcpc, uint8_t level) +{ + int ret = 0; + + tcpci_lock_typec(tcpc); + ret = tcpc_typec_set_rp_level(tcpc, level); + tcpci_unlock_typec(tcpc); + + return ret; +} + +int tcpm_typec_set_custom_hv(struct tcpc_device *tcpc, bool en) +{ +#ifdef CONFIG_TYPEC_CAP_CUSTOM_HV + int ret = tcpm_check_typec_attached(tcpc); + + if (ret != TCPM_SUCCESS) + return ret; + + tcpci_lock_typec(tcpc); + tcpc->typec_during_custom_hv = en; + tcpci_unlock_typec(tcpc); + + return ret; +#else + return TCPM_ERROR_NO_SUPPORT; +#endif /* CONFIG_TYPEC_CAP_CUSTOM_HV */ +} + +int tcpm_typec_role_swap(struct tcpc_device *tcpc) +{ +#ifdef CONFIG_TYPEC_CAP_ROLE_SWAP + int ret = tcpm_check_typec_attached(tcpc); + + if (ret != TCPM_SUCCESS) + return ret; + + tcpci_lock_typec(tcpc); + ret = tcpc_typec_swap_role(tcpc); + tcpci_unlock_typec(tcpc); + + return ret; +#else + return TCPM_ERROR_NO_SUPPORT; +#endif /* CONFIG_TYPEC_CAP_ROLE_SWAP */ +} +EXPORT_SYMBOL(tcpm_typec_role_swap); + +int tcpm_typec_change_mode( + struct tcpc_device *tcpc, uint8_t mode) +{ + int ret = 0; + + tcpci_lock_typec(tcpc); + ret = tcpci_set_port_type(tcpc, mode); + tcpci_unlock_typec(tcpc); + + return ret; +} +EXPORT_SYMBOL(tcpm_typec_change_mode); + +int tcpm_typec_change_role( + struct tcpc_device *tcpc, uint8_t typec_role) +{ + int ret = 0; + + tcpci_lock_typec(tcpc); + ret = tcpc_typec_change_role(tcpc, typec_role, false); + tcpci_unlock_typec(tcpc); + + return ret; +} +EXPORT_SYMBOL(tcpm_typec_change_role); + +/* @postpone: whether to postpone Type-C role change until unattached */ +int tcpm_typec_change_role_postpone( + struct tcpc_device *tcpc, uint8_t typec_role, bool postpone) +{ + int ret = 0; + + tcpci_lock_typec(tcpc); + ret = tcpc_typec_change_role(tcpc, typec_role, postpone); + tcpci_unlock_typec(tcpc); + + return ret; +} +EXPORT_SYMBOL(tcpm_typec_change_role_postpone); + +int tcpm_typec_error_recovery(struct tcpc_device *tcpc) +{ + int ret = 0; + + tcpci_lock_typec(tcpc); + ret = tcpc_typec_error_recovery(tcpc); + tcpci_unlock_typec(tcpc); + + return ret; +} + +int tcpm_typec_disable_function( + struct tcpc_device *tcpc, bool disable) +{ + int ret = 0; + + tcpci_lock_typec(tcpc); + ret = (disable ? tcpc_typec_disable : tcpc_typec_enable)(tcpc); + tcpci_unlock_typec(tcpc); + + return ret; +} + +#ifdef CONFIG_USB_POWER_DELIVERY + +bool tcpm_inquire_pd_connected( + struct tcpc_device *tcpc) +{ + struct pd_port *pd_port = &tcpc->pd_port; + + return pd_port->pe_data.pd_connected; +} + +bool tcpm_inquire_pd_prev_connected( + struct tcpc_device *tcpc) +{ + struct pd_port *pd_port = &tcpc->pd_port; + + return pd_port->pe_data.pd_prev_connected; +} + +uint8_t tcpm_inquire_pd_data_role( + struct tcpc_device *tcpc) +{ + struct pd_port *pd_port = &tcpc->pd_port; + + return pd_port->data_role; +} +EXPORT_SYMBOL(tcpm_inquire_pd_data_role); + +uint8_t tcpm_inquire_pd_power_role( + struct tcpc_device *tcpc) +{ + struct pd_port *pd_port = &tcpc->pd_port; + + return pd_port->power_role; +} +EXPORT_SYMBOL(tcpm_inquire_pd_power_role); + +uint8_t tcpm_inquire_pd_vconn_role( + struct tcpc_device *tcpc) +{ + struct pd_port *pd_port = &tcpc->pd_port; + + return pd_port->vconn_role; +} +EXPORT_SYMBOL(tcpm_inquire_pd_vconn_role); + +uint8_t tcpm_inquire_pd_pe_ready( + struct tcpc_device *tcpc) +{ + struct pd_port *pd_port = &tcpc->pd_port; + + return pd_port->pe_data.pe_ready; +} + +uint8_t tcpm_inquire_cable_current( + struct tcpc_device *tcpc) +{ + struct pd_port *pd_port = &tcpc->pd_port; + + if (pd_port->pe_data.power_cable_present) + return pd_get_cable_curr_lvl(pd_port)+1; + + return PD_CABLE_CURR_UNKNOWN; +} + +uint32_t tcpm_inquire_dpm_flags(struct tcpc_device *tcpc) +{ + struct pd_port *pd_port = &tcpc->pd_port; + + return pd_port->pe_data.dpm_flags; +} +EXPORT_SYMBOL(tcpm_inquire_dpm_flags); + +uint32_t tcpm_inquire_dpm_caps(struct tcpc_device *tcpc) +{ + struct pd_port *pd_port = &tcpc->pd_port; + + return pd_port->dpm_caps; +} + +void tcpm_set_dpm_caps(struct tcpc_device *tcpc, uint32_t caps) +{ + struct pd_port *pd_port = &tcpc->pd_port; + + mutex_lock(&pd_port->pd_lock); + pd_port->dpm_caps = caps; + mutex_unlock(&pd_port->pd_lock); +} + +/* Inquire TCPC to get PD Information */ + +int tcpm_inquire_pd_contract( + struct tcpc_device *tcpc, int *mv, int *ma) +{ + int ret; + struct pd_port *pd_port = &tcpc->pd_port; + + ret = tcpm_check_pd_attached(tcpc); + if (ret != TCPM_SUCCESS) + return ret; + + if ((mv == NULL) || (ma == NULL)) + return TCPM_ERROR_PARAMETER; + + mutex_lock(&pd_port->pd_lock); + if (pd_port->pe_data.explicit_contract) { + *mv = pd_port->request_v; + *ma = pd_port->request_i; + } else + ret = TCPM_ERROR_NO_EXPLICIT_CONTRACT; + mutex_unlock(&pd_port->pd_lock); + + return ret; + +} + +int tcpm_inquire_cable_inform( + struct tcpc_device *tcpc, uint32_t *vdos) +{ + int ret; + struct pd_port *pd_port = &tcpc->pd_port; + + ret = tcpm_check_pd_attached(tcpc); + if (ret != TCPM_SUCCESS) + return ret; + + if (vdos == NULL) + return TCPM_ERROR_PARAMETER; + + mutex_lock(&pd_port->pd_lock); + if (pd_port->pe_data.power_cable_present) { + memcpy(vdos, pd_port->pe_data.cable_vdos, + sizeof(uint32_t) * VDO_MAX_NR); + } else + ret = TCPM_ERROR_NO_POWER_CABLE; + mutex_unlock(&pd_port->pd_lock); + + return ret; +} + +int tcpm_inquire_pd_partner_inform( + struct tcpc_device *tcpc, uint32_t *vdos) +{ +#ifdef CONFIG_USB_PD_KEEP_PARTNER_ID + int ret; + struct pd_port *pd_port = &tcpc->pd_port; + + ret = tcpm_check_pd_attached(tcpc); + if (ret != TCPM_SUCCESS) + return ret; + + if (vdos == NULL) + return TCPM_ERROR_PARAMETER; + + mutex_lock(&pd_port->pd_lock); + if (pd_port->pe_data.partner_id_present) { + memcpy(vdos, pd_port->pe_data.partner_vdos, + sizeof(uint32_t) * VDO_MAX_NR); + } else + ret = TCPM_ERROR_NO_PARTNER_INFORM; + mutex_unlock(&pd_port->pd_lock); + + return ret; +#else + return TCPM_ERROR_NO_SUPPORT; +#endif /* CONFIG_USB_PD_KEEP_PARTNER_ID */ +} +EXPORT_SYMBOL(tcpm_inquire_pd_partner_inform); + +int tcpm_inquire_pd_partner_svids( + struct tcpc_device *tcpc, struct tcpm_svid_list *list) +{ +#ifdef CONFIG_USB_PD_KEEP_SVIDS + int ret; + struct pd_port *pd_port = &tcpc->pd_port; + struct svdm_svid_list *svdm_list = &pd_port->pe_data.remote_svid_list; + + ret = tcpm_check_pd_attached(tcpc); + if (ret != TCPM_SUCCESS) + return ret; + + if (list == NULL) + return TCPM_ERROR_PARAMETER; + + mutex_lock(&pd_port->pd_lock); + if (svdm_list->cnt) { + list->cnt = svdm_list->cnt; + memcpy(list->svids, svdm_list->svids, + sizeof(uint16_t) * svdm_list->cnt); + } else + ret = TCPM_ERROR_NO_PARTNER_INFORM; + mutex_unlock(&pd_port->pd_lock); + + return ret; +#else + return TCPM_ERROR_NO_SUPPORT; +#endif /* CONFIG_USB_PD_KEEP_SVIDS */ +} + +int tcpm_inquire_pd_partner_modes( + struct tcpc_device *tcpc, uint16_t svid, struct tcpm_mode_list *list) +{ +#ifdef CONFIG_USB_PD_ALT_MODE + int ret = TCPM_SUCCESS; + struct svdm_svid_data *svid_data = NULL; + struct pd_port *pd_port = &tcpc->pd_port; + + mutex_lock(&pd_port->pd_lock); + + svid_data = + dpm_get_svdm_svid_data(pd_port, USB_SID_DISPLAYPORT); + + if (svid_data == NULL) { + mutex_unlock(&pd_port->pd_lock); + return TCPM_ERROR_PARAMETER; + } + + if (svid_data->remote_mode.mode_cnt) { + list->cnt = svid_data->remote_mode.mode_cnt; + memcpy(list->modes, svid_data->remote_mode.mode_vdo, + sizeof(uint32_t) * list->cnt); + } else + ret = TCPM_ERROR_NO_PARTNER_INFORM; + mutex_unlock(&pd_port->pd_lock); + + return ret; +#else + return TCPM_ERROR_NO_SUPPORT; +#endif /* CONFIG_USB_PD_KEEP_SVIDS */ +} + +int tcpm_inquire_pd_source_cap( + struct tcpc_device *tcpc, struct tcpm_power_cap *cap) +{ + int ret; + struct pd_port *pd_port = &tcpc->pd_port; + + ret = tcpm_check_pd_attached(tcpc); + if (ret != TCPM_SUCCESS) + return ret; + + if (cap == NULL) + return TCPM_ERROR_PARAMETER; + + mutex_lock(&pd_port->pd_lock); + if (pd_port->pe_data.remote_src_cap.nr) { + cap->cnt = pd_port->pe_data.remote_src_cap.nr; + memcpy(cap->pdos, pd_port->pe_data.remote_src_cap.pdos, + sizeof(uint32_t) * cap->cnt); + } else + ret = TCPM_ERROR_NO_SOURCE_CAP; + mutex_unlock(&pd_port->pd_lock); + + return ret; +} + +int tcpm_inquire_pd_sink_cap( + struct tcpc_device *tcpc, struct tcpm_power_cap *cap) +{ + int ret; + struct pd_port *pd_port = &tcpc->pd_port; + + ret = tcpm_check_pd_attached(tcpc); + if (ret != TCPM_SUCCESS) + return ret; + + if (cap == NULL) + return TCPM_ERROR_PARAMETER; + + mutex_lock(&pd_port->pd_lock); + if (pd_port->pe_data.remote_snk_cap.nr) { + cap->cnt = pd_port->pe_data.remote_snk_cap.nr; + memcpy(cap->pdos, pd_port->pe_data.remote_snk_cap.pdos, + sizeof(uint32_t) * cap->cnt); + } else + ret = TCPM_ERROR_NO_SINK_CAP; + mutex_unlock(&pd_port->pd_lock); + + return ret; +} + +bool tcpm_extract_power_cap_val( + uint32_t pdo, struct tcpm_power_cap_val *cap) +{ + struct dpm_pdo_info_t info; + + dpm_extract_pdo_info(pdo, &info); + + cap->type = info.type; + cap->min_mv = info.vmin; + cap->max_mv = info.vmax; + + if (info.type == DPM_PDO_TYPE_BAT) + cap->uw = info.uw; + else + cap->ma = info.ma; + +#ifdef CONFIG_USB_PD_REV30 + if (info.type == DPM_PDO_TYPE_APDO) { + cap->apdo_type = info.apdo_type; + cap->pwr_limit = info.pwr_limit; + } +#endif /* CONFIG_USB_PD_REV30 */ + + return cap->type != TCPM_POWER_CAP_VAL_TYPE_UNKNOWN; +} + +extern bool tcpm_extract_power_cap_list( + struct tcpm_power_cap *cap, struct tcpm_power_cap_list *cap_list) +{ + uint8_t i; + + cap_list->nr = cap->cnt; + for (i = 0; i < cap_list->nr; i++) { + if (!tcpm_extract_power_cap_val( + cap->pdos[i], &cap_list->cap_val[i])) + return false; + } + + return true; +} + +int tcpm_get_remote_power_cap(struct tcpc_device *tcpc, + struct tcpm_remote_power_cap *remote_cap) +{ + struct pd_port *pd_port = &tcpc->pd_port; + struct tcpm_power_cap_val cap; + int i; + + mutex_lock(&pd_port->pd_lock); + remote_cap->selected_cap_idx = pd_port->pe_data.remote_selected_cap; + remote_cap->nr = pd_port->pe_data.remote_src_cap.nr; + for (i = 0; i < remote_cap->nr; i++) { + tcpm_extract_power_cap_val( + pd_port->pe_data.remote_src_cap.pdos[i], &cap); + remote_cap->max_mv[i] = cap.max_mv; + remote_cap->min_mv[i] = cap.min_mv; + if (cap.type == DPM_PDO_TYPE_BAT) + remote_cap->ma[i] = cap.uw / cap.min_mv; + else + remote_cap->ma[i] = cap.ma; + remote_cap->type[i] = cap.type; + } + mutex_unlock(&pd_port->pd_lock); + + return TCPM_SUCCESS; +} + +int tcpm_set_remote_power_cap(struct tcpc_device *tcpc, + int mv, int ma) +{ + return tcpm_dpm_pd_request(tcpc, mv, ma, NULL); +} + +static inline int __tcpm_inquire_select_source_cap( + struct pd_port *pd_port, struct tcpm_power_cap_val *cap_val) +{ + uint8_t sel; + struct pe_data *pe_data = &pd_port->pe_data; + + if (pd_port->power_role != PD_ROLE_SINK) + return TCPM_ERROR_POWER_ROLE; + + sel = RDO_POS(pd_port->last_rdo) - 1; + if (sel >= pe_data->remote_src_cap.nr) + return TCPM_ERROR_NO_SOURCE_CAP; + + if (!tcpm_extract_power_cap_val( + pe_data->remote_src_cap.pdos[sel], cap_val)) + return TCPM_ERROR_NOT_FOUND; + + return TCPM_SUCCESS; +} + +int tcpm_inquire_select_source_cap( + struct tcpc_device *tcpc, struct tcpm_power_cap_val *cap_val) +{ + int ret; + struct pd_port *pd_port = &tcpc->pd_port; + + if (cap_val == NULL) + return TCPM_ERROR_PARAMETER; + + ret = tcpm_check_pd_attached(tcpc); + if (ret != TCPM_SUCCESS) + return ret; + + mutex_lock(&pd_port->pd_lock); + ret = __tcpm_inquire_select_source_cap(pd_port, cap_val); + mutex_unlock(&pd_port->pd_lock); + + return ret; +} + + +/* Request TCPC to send PD Request */ + +#ifdef CONFIG_USB_PD_BLOCK_TCPM + +#define TCPM_BK_PD_CMD_TOUT 500 + +/* tPSTransition 550 ms */ +#define TCPM_BK_REQUEST_TOUT 1500 + +/* tPSSourceOff 920 ms, tPSSourceOn 480 ms*/ +#define TCPM_BK_PR_SWAP_TOUT 2500 +#define TCPM_BK_HARD_RESET_TOUT 3500 + +static int tcpm_put_tcp_dpm_event_bk( + struct tcpc_device *tcpc, struct tcp_dpm_event *event, + uint32_t tout_ms, uint8_t *data, uint8_t size); +#endif /* CONFIG_USB_PD_BLOCK_TCPM */ + +int tcpm_put_tcp_dpm_event_cb(struct tcpc_device *tcpc, + struct tcp_dpm_event *event, + const struct tcp_dpm_event_cb_data *cb_data) +{ + event->user_data = cb_data->user_data; + event->event_cb = cb_data->event_cb; + + return tcpm_put_tcp_dpm_event(tcpc, event); +} + +static int tcpm_put_tcp_dpm_event_cbk1(struct tcpc_device *tcpc, + struct tcp_dpm_event *event, + const struct tcp_dpm_event_cb_data *cb_data, uint32_t tout_ms) +{ +#ifdef CONFIG_USB_PD_BLOCK_TCPM + if (cb_data == NULL) { + return tcpm_put_tcp_dpm_event_bk( + tcpc, event, tout_ms, NULL, 0); + } +#endif /* CONFIG_USB_PD_BLOCK_TCPM */ + + return tcpm_put_tcp_dpm_event_cb(tcpc, event, cb_data); +} + +int tcpm_dpm_pd_power_swap(struct tcpc_device *tcpc, + uint8_t role, const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_PR_SWAP_AS_SNK + role, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PR_SWAP_TOUT); +} +EXPORT_SYMBOL(tcpm_dpm_pd_power_swap); + +int tcpm_dpm_pd_data_swap(struct tcpc_device *tcpc, + uint8_t role, const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_DR_SWAP_AS_UFP + role, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT); +} +EXPORT_SYMBOL(tcpm_dpm_pd_data_swap); + +int tcpm_dpm_pd_vconn_swap(struct tcpc_device *tcpc, + uint8_t role, const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_VCONN_SWAP_OFF + role, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT); +} +EXPORT_SYMBOL(tcpm_dpm_pd_vconn_swap); + +int tcpm_dpm_pd_goto_min(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_GOTOMIN, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT); +} + +int tcpm_dpm_pd_soft_reset(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_SOFTRESET, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_REQUEST_TOUT); +} + +int tcpm_dpm_pd_get_source_cap(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_GET_SOURCE_CAP, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_REQUEST_TOUT); +} + +int tcpm_dpm_pd_get_sink_cap(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_GET_SINK_CAP, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT); +} + +int tcpm_dpm_pd_request(struct tcpc_device *tcpc, + int mv, int ma, const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_REQUEST, + .tcp_dpm_data.pd_req.mv = mv, + .tcp_dpm_data.pd_req.ma = ma, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_REQUEST_TOUT); +} + +int tcpm_dpm_pd_request_ex(struct tcpc_device *tcpc, + uint8_t pos, uint32_t max, uint32_t oper, + const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_REQUEST_EX, + .tcp_dpm_data.pd_req_ex.pos = pos, + .tcp_dpm_data.pd_req_ex.max = max, + .tcp_dpm_data.pd_req_ex.oper = oper, + }; + + if (oper > max) + return TCPM_ERROR_PARAMETER; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_REQUEST_TOUT); +} + +int tcpm_dpm_pd_bist_cm2(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_BIST_CM2, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT); +} + +#ifdef CONFIG_USB_PD_REV30 + +static int tcpm_put_tcp_dpm_event_cbk2(struct tcpc_device *tcpc, + struct tcp_dpm_event *event, + const struct tcp_dpm_event_cb_data *cb_data, + uint32_t tout_ms, uint8_t *data, uint8_t size) +{ +#ifdef CONFIG_USB_PD_BLOCK_TCPM + if (cb_data == NULL) { + return tcpm_put_tcp_dpm_event_bk( + tcpc, event, tout_ms, data, size); + } +#endif /* CONFIG_USB_PD_BLOCK_TCPM */ + + return tcpm_put_tcp_dpm_event_cb(tcpc, event, cb_data); +} + +int tcpm_dpm_pd_get_source_cap_ext(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *cb_data, + struct pd_source_cap_ext *src_cap_ext) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_GET_SOURCE_CAP_EXT, + }; + + return tcpm_put_tcp_dpm_event_cbk2( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT, + (uint8_t *) src_cap_ext, PD_SCEDB_SIZE); +} + +int tcpm_dpm_pd_fast_swap(struct tcpc_device *tcpc, + uint8_t role, const struct tcp_dpm_event_cb_data *cb_data) +{ + return TCPM_ERROR_NO_SUPPORT; +} + +int tcpm_dpm_pd_get_status(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *cb_data, struct pd_status *status) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_GET_STATUS, + }; + + return tcpm_put_tcp_dpm_event_cbk2( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT, + (uint8_t *) status, PD_SDB_SIZE); +} + +int tcpm_dpm_pd_get_pps_status_raw(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *cb_data, + struct pd_pps_status_raw *pps_status) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_GET_PPS_STATUS, + }; + + return tcpm_put_tcp_dpm_event_cbk2( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT, + (uint8_t *) pps_status, PD_PPSSDB_SIZE); +} + +int tcpm_dpm_pd_get_pps_status(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *cb_data, + struct pd_pps_status *pps_status) +{ + int ret; + struct pd_pps_status_raw pps_status_raw; + + ret = tcpm_dpm_pd_get_pps_status_raw( + tcpc, cb_data, &pps_status_raw); + + if (ret != 0) + return ret; + + if (pps_status_raw.output_vol_raw == 0xffff) + pps_status->output_mv = -1; + else + pps_status->output_mv = + PD_PPS_GET_OUTPUT_MV(pps_status_raw.output_vol_raw); + + if (pps_status_raw.output_curr_raw == 0xff) + pps_status->output_ma = -1; + else + pps_status->output_ma = + PD_PPS_GET_OUTPUT_MA(pps_status_raw.output_curr_raw); + + pps_status->real_time_flags = pps_status_raw.real_time_flags; + return ret; +} + +int tcpm_dpm_pd_get_country_code(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *cb_data, + struct pd_country_codes *ccdb) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_GET_COUNTRY_CODE, + }; + + return tcpm_put_tcp_dpm_event_cbk2( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT, + (uint8_t *) ccdb, PD_CCDB_MAX_SIZE); +} + +int tcpm_dpm_pd_get_country_info(struct tcpc_device *tcpc, uint32_t ccdo, + const struct tcp_dpm_event_cb_data *cb_data, + struct pd_country_info *cidb) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_GET_COUNTRY_INFO, + .tcp_dpm_data.index = ccdo, + }; + + return tcpm_put_tcp_dpm_event_cbk2( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT, + (uint8_t *) cidb, PD_CIDB_MAX_SIZE); +} + +int tcpm_dpm_pd_get_bat_cap(struct tcpc_device *tcpc, + struct pd_get_battery_capabilities *gbcdb, + const struct tcp_dpm_event_cb_data *cb_data, + struct pd_battery_capabilities *bcdb) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_GET_BAT_CAP, + .tcp_dpm_data.gbcdb = *gbcdb, + }; + + return tcpm_put_tcp_dpm_event_cbk2( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT, + (uint8_t *) bcdb, PD_BCDB_SIZE); +} + +int tcpm_dpm_pd_get_bat_status(struct tcpc_device *tcpc, + struct pd_get_battery_status *gbsdb, + const struct tcp_dpm_event_cb_data *cb_data, + uint32_t *bsdo) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_GET_BAT_STATUS, + .tcp_dpm_data.gbsdb = *gbsdb, + }; + + return tcpm_put_tcp_dpm_event_cbk2( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT, + (uint8_t *) bsdo, sizeof(uint32_t) * PD_BSDO_SIZE); +} + +int tcpm_dpm_pd_get_mfrs_info(struct tcpc_device *tcpc, + struct pd_get_manufacturer_info *gmidb, + const struct tcp_dpm_event_cb_data *cb_data, + struct pd_manufacturer_info *midb) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_GET_MFRS_INFO, + .tcp_dpm_data.gmidb = *gmidb, + }; + + return tcpm_put_tcp_dpm_event_cbk2( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT, + (uint8_t *) midb, PD_MIDB_MAX_SIZE); +} + +int tcpm_dpm_pd_alert(struct tcpc_device *tcpc, + uint32_t ado, const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_ALERT, + .tcp_dpm_data.index = ado, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT); +} + +#endif /* CONFIG_USB_PD_REV30 */ + +int tcpm_dpm_pd_hard_reset(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_HARD_RESET, + }; + + /* check not block case !!! */ + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_HARD_RESET_TOUT); +} + +int tcpm_dpm_pd_error_recovery(struct tcpc_device *tcpc) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_ERROR_RECOVERY, + }; + + if (tcpm_put_tcp_dpm_event(tcpc, &tcp_event) != TCPM_SUCCESS) + tcpm_typec_error_recovery(tcpc); + + return TCPM_SUCCESS; +} + +int tcpm_dpm_pd_cable_soft_reset(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_CABLE_SOFTRESET, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT); +} + +int tcpm_dpm_vdm_discover_cable(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_DISCOVER_CABLE, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT); +} + +int tcpm_dpm_vdm_discover_id(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_DISCOVER_ID, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT); +} + +int tcpm_dpm_vdm_discover_svid(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_DISCOVER_SVIDS, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT); +} + +int tcpm_dpm_vdm_discover_mode(struct tcpc_device *tcpc, + uint16_t svid, const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_DISCOVER_MODES, + .tcp_dpm_data.svdm_data.svid = svid, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT); +} + +int tcpm_dpm_vdm_enter_mode(struct tcpc_device *tcpc, + uint16_t svid, uint8_t ops, + const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_ENTER_MODE, + .tcp_dpm_data.svdm_data.svid = svid, + .tcp_dpm_data.svdm_data.ops = ops, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT); +} + +int tcpm_dpm_vdm_exit_mode(struct tcpc_device *tcpc, + uint16_t svid, uint8_t ops, + const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_EXIT_MODE, + .tcp_dpm_data.svdm_data.svid = svid, + .tcp_dpm_data.svdm_data.ops = ops, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT); +} + +int tcpm_dpm_vdm_attention(struct tcpc_device *tcpc, + uint16_t svid, uint8_t ops, + const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_ATTENTION, + .tcp_dpm_data.svdm_data.svid = svid, + .tcp_dpm_data.svdm_data.ops = ops, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT); +} + +#ifdef CONFIG_USB_PD_ALT_MODE + +int tcpm_inquire_dp_ufp_u_state( + struct tcpc_device *tcpc, uint8_t *state) +{ + int ret; + struct pd_port *pd_port = &tcpc->pd_port; + + ret = tcpm_check_pd_attached(tcpc); + if (ret != TCPM_SUCCESS) + return ret; + + if (state == NULL) + return TCPM_ERROR_PARAMETER; + + mutex_lock(&pd_port->pd_lock); + *state = pd_get_dp_data(pd_port)->ufp_u_state; + mutex_unlock(&pd_port->pd_lock); + + return ret; +} + +int tcpm_dpm_dp_attention(struct tcpc_device *tcpc, + uint32_t dp_status, uint32_t mask, + const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_DP_ATTENTION, + .tcp_dpm_data.dp_data.val = dp_status, + .tcp_dpm_data.dp_data.mask = mask, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT); +} + +#ifdef CONFIG_USB_PD_ALT_MODE_DFP + +int tcpm_inquire_dp_dfp_u_state( + struct tcpc_device *tcpc, uint8_t *state) +{ + int ret; + struct pd_port *pd_port = &tcpc->pd_port; + + ret = tcpm_check_pd_attached(tcpc); + if (ret != TCPM_SUCCESS) + return ret; + + if (state == NULL) + return TCPM_ERROR_PARAMETER; + + mutex_lock(&pd_port->pd_lock); + *state = pd_get_dp_data(pd_port)->dfp_u_state; + mutex_unlock(&pd_port->pd_lock); + + return ret; +} + +int tcpm_dpm_dp_status_update(struct tcpc_device *tcpc, + uint32_t dp_status, uint32_t mask, + const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_DP_STATUS_UPDATE, + .tcp_dpm_data.dp_data.val = dp_status, + .tcp_dpm_data.dp_data.mask = mask, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT); +} + +int tcpm_dpm_dp_config(struct tcpc_device *tcpc, + uint32_t dp_config, uint32_t mask, + const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_DP_CONFIG, + .tcp_dpm_data.dp_data.val = dp_config, + .tcp_dpm_data.dp_data.mask = mask, + }; + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT); +} + +#endif /* CONFIG_USB_PD_ALT_MODE_DFP */ +#endif /* CONFIG_USB_PD_ALT_MODE */ + +#ifdef CONFIG_USB_PD_CUSTOM_VDM + +int tcpm_dpm_send_custom_vdm( + struct tcpc_device *tcpc, + struct tcp_dpm_custom_vdm_data *vdm_data, + const struct tcp_dpm_event_cb_data *cb_data) +{ + int ret; + struct pd_port *pd_port = &tcpc->pd_port; + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_UVDM, + }; + + if (vdm_data->cnt > PD_DATA_OBJ_SIZE) + return TCPM_ERROR_PARAMETER; + + memcpy(&tcp_event.tcp_dpm_data.vdm_data, + vdm_data, sizeof(struct tcp_dpm_custom_vdm_data)); + + /* Check it later */ + ret = tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT); + +#ifdef CONFIG_USB_PD_TCPM_CB_2ND + if ((ret == TCP_DPM_RET_SUCCESS) + && (cb_data == NULL) && vdm_data->wait_resp) { + mutex_lock(&pd_port->pd_lock); + vdm_data->cnt = tcpc->pd_port.uvdm_cnt; + memcpy(vdm_data->vdos, + tcpc->pd_port.uvdm_data, + sizeof(uint32_t) * vdm_data->cnt); + mutex_unlock(&pd_port->pd_lock); + } +#endif /* CONFIG_USB_PD_TCPM_CB_2ND */ + + return ret; +} +#endif /* CONFIG_USB_PD_CUSTOM_VDM */ + +#ifdef CONFIG_USB_PD_TCPM_CB_2ND +void tcpm_replace_curr_tcp_event( + struct pd_port *pd_port, struct tcp_dpm_event *event) +{ + int reason = TCP_DPM_RET_DENIED_UNKNOWN; + + switch (event->event_id) { + case TCP_DPM_EVT_HARD_RESET: + reason = TCP_DPM_RET_DROP_SENT_HRESET; + break; + case TCP_DPM_EVT_ERROR_RECOVERY: + reason = TCP_DPM_RET_DROP_ERROR_REOCVERY; + break; + } + + mutex_lock(&pd_port->pd_lock); + pd_notify_tcp_event_2nd_result(pd_port, reason); + pd_port->tcp_event_drop_reset_once = true; + pd_port->tcp_event_id_2nd = event->event_id; + memcpy(&pd_port->tcp_event, event, sizeof(struct tcp_dpm_event)); + mutex_unlock(&pd_port->pd_lock); +} +#endif /* CONFIG_USB_PD_TCPM_CB_2ND */ + +int tcpm_put_tcp_dpm_event( + struct tcpc_device *tcpc, struct tcp_dpm_event *event) +{ + int ret; + bool imme = event->event_id >= TCP_DPM_EVT_IMMEDIATELY; + struct pd_port *pd_port = &tcpc->pd_port; + + ret = tcpm_check_pd_attached(tcpc); + if (ret != TCPM_SUCCESS && + (imme && ret == TCPM_ERROR_UNATTACHED)) + return ret; + + if (imme) { + ret = pd_put_tcp_pd_event(pd_port, event->event_id); + +#ifdef CONFIG_USB_PD_TCPM_CB_2ND + if (ret) + tcpm_replace_curr_tcp_event(pd_port, event); +#endif /* CONFIG_USB_PD_TCPM_CB_2ND */ + } else + ret = pd_put_deferred_tcp_event(tcpc, event); + + if (!ret) + return TCPM_ERROR_PUT_EVENT; + + return TCPM_SUCCESS; +} + +int tcpm_notify_vbus_stable( + struct tcpc_device *tcpc) +{ +#if CONFIG_USB_PD_VBUS_STABLE_TOUT + tcpc_disable_timer(tcpc, PD_TIMER_VBUS_STABLE); +#endif + + pd_put_vbus_stable_event(tcpc); + return TCPM_SUCCESS; +} + +uint8_t tcpm_inquire_pd_charging_policy(struct tcpc_device *tcpc) +{ + struct pd_port *pd_port = &tcpc->pd_port; + + return pd_port->dpm_charging_policy; +} + +uint8_t tcpm_inquire_pd_charging_policy_default(struct tcpc_device *tcpc) +{ + struct pd_port *pd_port = &tcpc->pd_port; + + return pd_port->dpm_charging_policy_default; +} + +int tcpm_reset_pd_charging_policy(struct tcpc_device *tcpc, + const struct tcp_dpm_event_cb_data *cb_data) +{ + return tcpm_set_pd_charging_policy( + tcpc, tcpc->pd_port.dpm_charging_policy_default, cb_data); +} + +int tcpm_set_pd_charging_policy_default( + struct tcpc_device *tcpc, uint8_t policy) +{ + struct pd_port *pd_port = &tcpc->pd_port; + + /* PPS should not be default charging policy ... */ + if ((policy & DPM_CHARGING_POLICY_MASK) >= DPM_CHARGING_POLICY_PPS) + return TCPM_ERROR_PARAMETER; + + mutex_lock(&pd_port->pd_lock); + pd_port->dpm_charging_policy_default = policy; + mutex_unlock(&pd_port->pd_lock); + + return TCPM_SUCCESS; +} + +int tcpm_set_pd_charging_policy(struct tcpc_device *tcpc, + uint8_t policy, const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_REQUEST_AGAIN, + }; + + struct pd_port *pd_port = &tcpc->pd_port; + + /* PPS should call another function ... */ + if ((policy & DPM_CHARGING_POLICY_MASK) >= DPM_CHARGING_POLICY_PPS) + return TCPM_ERROR_PARAMETER; + + mutex_lock(&pd_port->pd_lock); + if (pd_port->dpm_charging_policy == policy) { + mutex_unlock(&pd_port->pd_lock); + return TCPM_SUCCESS; + } + + pd_port->dpm_charging_policy = policy; + mutex_unlock(&pd_port->pd_lock); + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_REQUEST_TOUT); +} + +#ifdef CONFIG_USB_PD_DIRECT_CHARGE +int tcpm_set_direct_charge_en(struct tcpc_device *tcpc, bool en) +{ + struct pd_port *pd_port = &tcpc->pd_port; + + mutex_lock(&pd_port->pd_lock); + tcpc->pd_during_direct_charge = en; + mutex_unlock(&pd_port->pd_lock); + + return 0; +} + +bool tcpm_inquire_during_direct_charge(struct tcpc_device *tcpc) +{ + return tcpc->pd_during_direct_charge; +} +#endif /* CONFIG_USB_PD_DIRECT_CHARGE */ + +static int tcpm_put_tcp_dummy_event(struct tcpc_device *tcpc) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_DUMMY, + }; + + return tcpm_put_tcp_dpm_event(tcpc, &tcp_event); +} + +#ifdef CONFIG_TCPC_VCONN_SUPPLY_MODE + +int tcpm_dpm_set_vconn_supply_mode(struct tcpc_device *tcpc, uint8_t mode) +{ + struct pd_port *pd_port = &tcpc->pd_port; + + mutex_lock(&pd_port->pd_lock); + tcpc->tcpc_vconn_supply = mode; + dpm_reaction_set(pd_port, + DPM_REACTION_DYNAMIC_VCONN | + DPM_REACTION_VCONN_STABLE_DELAY); + mutex_unlock(&pd_port->pd_lock); + + return tcpm_put_tcp_dummy_event(tcpc); +} + +#endif /* CONFIG_TCPC_VCONN_SUPPLY_MODE */ + +#ifdef CONFIG_USB_PD_REV30_PPS_SINK + +int tcpm_set_apdo_charging_policy(struct tcpc_device *tcpc, + uint8_t policy, int mv, int ma, + const struct tcp_dpm_event_cb_data *cb_data) +{ + struct tcp_dpm_event tcp_event = { + .event_id = TCP_DPM_EVT_REQUEST_AGAIN, + }; + + struct pd_port *pd_port = &tcpc->pd_port; + + /* Not PPS should call another function ... */ + if ((policy & DPM_CHARGING_POLICY_MASK) < DPM_CHARGING_POLICY_PPS) + return TCPM_ERROR_PARAMETER; + + mutex_lock(&pd_port->pd_lock); + if (pd_port->dpm_charging_policy == policy) { + mutex_unlock(&pd_port->pd_lock); + TCPC_INFO("BUG!!! FIX IT!!!\n"); + return tcpm_dpm_pd_request(tcpc, mv, ma, NULL); + /* return TCPM_ERROR_REPEAT_POLICY; */ + } + + if (pd_port->pd_connect_state != PD_CONNECT_PE_READY_SNK_APDO) { + mutex_unlock(&pd_port->pd_lock); + return TCPM_ERROR_INVALID_POLICY; + } + + pd_port->dpm_charging_policy = policy; + pd_port->request_v_apdo = mv; + pd_port->request_i_apdo = ma; + mutex_unlock(&pd_port->pd_lock); + + return tcpm_put_tcp_dpm_event_cbk1( + tcpc, &tcp_event, cb_data, TCPM_BK_REQUEST_TOUT); +} + +int tcpm_inquire_pd_source_apdo(struct tcpc_device *tcpc, + uint8_t apdo_type, uint8_t *cap_i, struct tcpm_power_cap_val *cap_val) +{ + int ret; + uint8_t i; + struct tcpm_power_cap cap; + + ret = tcpm_inquire_pd_source_cap(tcpc, &cap); + if (ret != TCPM_SUCCESS) + return ret; + + for (i = *cap_i; i < cap.cnt; i++) { + if (!tcpm_extract_power_cap_val(cap.pdos[i], cap_val)) + continue; + + if (cap_val->type != DPM_PDO_TYPE_APDO) + continue; + + if ((cap_val->apdo_type & TCPM_APDO_TYPE_MASK) != apdo_type) + continue; + + *cap_i = i+1; + return TCPM_SUCCESS; + } + + return TCPM_ERROR_NOT_FOUND; +} + +bool tcpm_inquire_during_pps_charge(struct tcpc_device *tcpc) +{ + int ret; + struct tcpm_power_cap_val cap = {0}; + + ret = tcpm_inquire_select_source_cap(tcpc, &cap); + if (ret != 0) + return false; + + return (cap.type == TCPM_POWER_CAP_VAL_TYPE_AUGMENT); +} + +#endif /* CONFIG_USB_PD_REV30_PPS_SINK */ + +#ifdef CONFIG_USB_PD_REV30_BAT_INFO + +static void tcpm_alert_bat_changed( + struct pd_port *pd_port, enum pd_battery_reference ref) +{ +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL + uint8_t fixed = 0, swap = 0; + + if (ref >= PD_BAT_REF_SWAP0) + swap = 1 << (ref - PD_BAT_REF_SWAP0); + else + fixed = 1 << (ref - PD_BAT_REF_FIXED0); + + pd_port->pe_data.local_alert |= + ADO(ADO_ALERT_BAT_CHANGED, fixed, swap); +#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */ +} + +static inline uint16_t tcpm_calc_battery_cap( + struct pd_battery_info *battery_info, uint16_t soc) +{ + uint16_t wh; + struct pd_battery_capabilities *bat_cap; + + bat_cap = &battery_info->bat_cap; + + if (bat_cap->bat_last_full_cap != BSDO_BAT_CAP_UNKNOWN) + wh = bat_cap->bat_last_full_cap; + else if (bat_cap->bat_design_cap != BSDO_BAT_CAP_UNKNOWN) + wh = bat_cap->bat_design_cap; + else + wh = BSDO_BAT_CAP_UNKNOWN; + + if (wh != BSDO_BAT_CAP_UNKNOWN) + wh = wh * soc / 1000; + + return wh; +} + +static int tcpm_update_bsdo( + struct pd_port *pd_port, enum pd_battery_reference ref, + uint16_t soc, uint16_t wh, uint8_t status) +{ + uint8_t status_old; + struct pd_battery_info *battery_info; + + battery_info = pd_get_battery_info(pd_port, ref); + if (battery_info == NULL) + return TCPM_ERROR_PARAMETER; + + status_old = BSDO_BAT_INFO(battery_info->bat_status); + + if (soc != 0) + wh = tcpm_calc_battery_cap(battery_info, soc); + + battery_info->bat_status = BSDO(wh, status); + + if (status_old != status) { + tcpm_alert_bat_changed(pd_port, ref); + return TCPM_ALERT; + } + + return TCPM_SUCCESS; +} + +int tcpm_update_bat_status_wh(struct tcpc_device *tcpc, + enum pd_battery_reference ref, uint8_t status, uint16_t wh) +{ + int ret; + + mutex_lock(&tcpc->pd_port.pd_lock); + ret = tcpm_update_bat_status_wh_no_mutex(tcpc, ref, status, wh); + mutex_unlock(&tcpc->pd_port.pd_lock); + + if (ret == TCPM_ALERT) { + ret = TCPM_SUCCESS; + tcpm_put_tcp_dummy_event(tcpc); + } + + return ret; +} + +int tcpm_update_bat_status_wh_no_mutex(struct tcpc_device *tcpc, + enum pd_battery_reference ref, uint8_t status, uint16_t wh) +{ + return tcpm_update_bsdo(&tcpc->pd_port, ref, 0, wh, status); +} + +int tcpm_update_bat_status_soc(struct tcpc_device *tcpc, + enum pd_battery_reference ref, uint8_t status, uint16_t soc) +{ + int ret; + + mutex_lock(&tcpc->pd_port.pd_lock); + ret = tcpm_update_bat_status_soc_no_mutex(tcpc, ref, status, soc); + mutex_unlock(&tcpc->pd_port.pd_lock); + + if (ret == TCPM_ALERT) { + ret = TCPM_SUCCESS; + tcpm_put_tcp_dummy_event(tcpc); + } + + return ret; +} + +int tcpm_update_bat_status_soc_no_mutex(struct tcpc_device *tcpc, + enum pd_battery_reference ref, uint8_t status, uint16_t soc) +{ + return tcpm_update_bsdo(&tcpc->pd_port, ref, soc, 0, status); +} + +int tcpm_update_bat_last_full(struct tcpc_device *tcpc, + enum pd_battery_reference ref, uint16_t wh) +{ + int ret; + + mutex_lock(&tcpc->pd_port.pd_lock); + ret = tcpm_update_bat_last_full_no_mutex(tcpc, ref, wh); + mutex_unlock(&tcpc->pd_port.pd_lock); + + return ret; +} + +int tcpm_update_bat_last_full_no_mutex(struct tcpc_device *tcpc, + enum pd_battery_reference ref, uint16_t wh) +{ + struct pd_battery_info *battery_info; + + battery_info = pd_get_battery_info(&tcpc->pd_port, ref); + if (battery_info == NULL) + return TCPM_ERROR_PARAMETER; + + battery_info->bat_cap.bat_last_full_cap = wh; + return TCPM_SUCCESS; +} + +#endif /* CONFIG_USB_PD_REV30_BAT_INFO */ + +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL + +static void tcpm_alert_status_changed( + struct tcpc_device *tcpc, uint8_t ado_type) +{ +#ifdef CONFIG_USB_PD_REV30_ALERT_LOCAL + tcpc->pd_port.pe_data.local_alert |= ADO(ado_type, 0, 0); +#endif /* CONFIG_USB_PD_REV30_ALERT_LOCAL */ +} + +int tcpm_update_pd_status_temp(struct tcpc_device *tcpc, + enum pd_present_temperature_flag ptf_new, uint8_t temperature) +{ +#ifdef CONFIG_USB_PD_REV30_STATUS_LOCAL_TEMP + uint8_t ado_type = 0; + enum pd_present_temperature_flag ptf_now; + struct pd_port *pd_port = &tcpc->pd_port; + + mutex_lock(&pd_port->pd_lock); + + ptf_now = PD_STATUS_TEMP_PTF(pd_port->pd_status_temp_status); + + if (ptf_now != ptf_new) { + if ((ptf_new >= PD_PTF_WARNING) || + (ptf_now >= PD_PTF_WARNING)) + ado_type |= ADO_ALERT_OPER_CHANGED; + + if (ptf_new == PD_PTF_OVER_TEMP) { + ado_type |= ADO_ALERT_OTP; + pd_port->pe_data.pd_status_event |= PD_STATUS_EVENT_OTP; + } + + pd_port->pd_status_temp_status = + PD_STATUS_TEMP_SET_PTF(ptf_new); + } + + pd_port->pd_status_temp = temperature; + tcpm_alert_status_changed(tcpc, ado_type); + mutex_unlock(&pd_port->pd_lock); + + if (ado_type) + tcpm_put_tcp_dummy_event(tcpc); + + return TCPM_SUCCESS; +#else + return TCPM_ERROR_NO_IMPLEMENT; +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL_TEMP */ +} + +int tcpm_update_pd_status_input( + struct tcpc_device *tcpc, uint8_t input_new, uint8_t mask) +{ + uint8_t input; + uint8_t ado_type = 0; + struct pd_port *pd_port = &tcpc->pd_port; + + mutex_lock(&pd_port->pd_lock); + input = pd_port->pd_status_present_in; + input &= ~mask; + input |= (input_new & mask); + + if (input != pd_port->pd_status_present_in) { + ado_type = ADO_ALERT_SRC_IN_CHANGED; + pd_port->pd_status_present_in = input; + } + + tcpm_alert_status_changed(tcpc, ado_type); + mutex_unlock(&pd_port->pd_lock); + + if (ado_type) + tcpm_put_tcp_dummy_event(tcpc); + + return TCPM_SUCCESS; +} + +int tcpm_update_pd_status_bat_input( + struct tcpc_device *tcpc, uint8_t bat_input, uint8_t bat_mask) +{ + uint8_t input; + uint8_t ado_type = 0; + const uint8_t flag = PD_STATUS_INPUT_INT_POWER_BAT; + struct pd_port *pd_port = &tcpc->pd_port; + + mutex_lock(&pd_port->pd_lock); + input = pd_port->pd_status_bat_in; + input &= ~bat_mask; + input |= (bat_input & bat_mask); + + if (input != pd_port->pd_status_bat_in) { + if (input) + pd_port->pd_status_present_in |= flag; + else + pd_port->pd_status_present_in &= ~flag; + + ado_type = ADO_ALERT_SRC_IN_CHANGED; + } + + tcpm_alert_status_changed(tcpc, ado_type); + mutex_unlock(&pd_port->pd_lock); + + if (ado_type) + tcpm_put_tcp_dummy_event(tcpc); + + return TCPM_SUCCESS; +} + +int tcpm_update_pd_status_event( + struct tcpc_device *tcpc, uint8_t evt) +{ + uint8_t ado_type = 0; + struct pd_port *pd_port = &tcpc->pd_port; + + if (evt & PD_STASUS_EVENT_OCP) + ado_type |= ADO_ALERT_OCP; + + if (evt & PD_STATUS_EVENT_OTP) + ado_type |= ADO_ALERT_OTP; + + if (evt & PD_STATUS_EVENT_OVP) + ado_type |= ADO_ALERT_OVP; + + evt &= PD_STASUS_EVENT_MASK; + + mutex_lock(&pd_port->pd_lock); + pd_port->pe_data.pd_status_event |= evt; + tcpm_alert_status_changed(tcpc, ado_type); + mutex_unlock(&pd_port->pd_lock); + + if (ado_type) + tcpm_put_tcp_dummy_event(tcpc); + + return TCPM_SUCCESS; +} + +#endif /* CONFIG_USB_PD_REV30_STATUS_LOCAL */ + +#ifdef CONFIG_USB_PD_BLOCK_TCPM + +#if TCPM_DBG_ENABLE +static const char * const bk_event_ret_name[] = { + "OK", + "Unknown", /* or not support by TCPM */ + "NotReady", + "LocalCap", + "PartnerCap", + "SameRole", + "InvalidReq", + "RepeatReq", + "WrongDR", + "PDRev", + + "Detach", + "SReset0", + "SReset1", + "HReset0", + "HReset1", + "Recovery", + "BIST", + "PEBusy", + + "Wait", + "Reject", + "TOUT", + "NAK", + "NotSupport", + + "BKTOUT", + "NoResponse", +}; +#endif /* TCPM_DBG_ENABLE */ + +#ifdef CONFIG_USB_PD_TCPM_CB_2ND +static inline void tcpm_dpm_bk_copy_data(struct pd_port *pd_port) +{ + uint8_t size = pd_port->tcpm_bk_cb_data_max; + + if (size >= pd_get_msg_data_size(pd_port)) + size = pd_get_msg_data_size(pd_port); + + if (pd_port->tcpm_bk_cb_data != NULL) { + memcpy(pd_port->tcpm_bk_cb_data, + pd_get_msg_data_payload(pd_port), size); + } +} +#endif /* CONFIG_USB_PD_TCPM_CB_2ND */ + +int tcpm_dpm_bk_event_cb( + struct tcpc_device *tcpc, int ret, struct tcp_dpm_event *event) +{ + struct pd_port *pd_port = &tcpc->pd_port; + + if (pd_port->tcpm_bk_event_id != event->event_id) { + TCPM_DBG("bk_event_cb_dummy: expect:%d real:%d\n", + pd_port->tcpm_bk_event_id, event->event_id); + return 0; + } + + pd_port->tcpm_bk_ret = ret; + pd_port->tcpm_bk_done = true; + +#ifdef CONFIG_USB_PD_TCPM_CB_2ND + if (ret == TCP_DPM_RET_SUCCESS) + tcpm_dpm_bk_copy_data(pd_port); +#endif /* CONFIG_USB_PD_TCPM_CB_2ND */ + + wake_up(&pd_port->tcpm_bk_wait_que); + return 0; +} + +static inline int __tcpm_dpm_wait_bk_event( + struct pd_port *pd_port, uint32_t tout_ms) +{ + int ret = TCP_DPM_RET_BK_TIMEOUT; + + wait_event_timeout(pd_port->tcpm_bk_wait_que, pd_port->tcpm_bk_done, + msecs_to_jiffies(tout_ms)); + + if (pd_port->tcpm_bk_done) + return pd_port->tcpm_bk_ret; + mutex_lock(&pd_port->pd_lock); + + pd_port->tcpm_bk_event_id = TCP_DPM_EVT_UNKONW; + +#ifdef CONFIG_USB_PD_TCPM_CB_2ND + pd_port->tcpm_bk_cb_data = NULL; +#endif /* CONFIG_USB_PD_TCPM_CB_2ND */ + + mutex_unlock(&pd_port->pd_lock); + + return ret; +} + +int tcpm_dpm_wait_bk_event(struct pd_port *pd_port, uint32_t tout_ms) +{ + int ret = __tcpm_dpm_wait_bk_event(pd_port, tout_ms); +#if TCPM_DBG_ENABLE + struct tcpc_device __maybe_unused *tcpc = pd_port->tcpc; + + if (ret < TCP_DPM_RET_NR && ret >= 0 + && ret < ARRAY_SIZE(bk_event_ret_name)) + TCPM_DBG("bk_event_cb -> %s\n", bk_event_ret_name[ret]); +#endif /* TCPM_DBG_ENABLE */ + + return ret; +} + +#ifdef CONFIG_MTK_HANDLE_PPS_TIMEOUT +static void mtk_handle_tcp_event_result( + struct tcpc_device *tcpc, struct tcp_dpm_event *event, int ret) +{ + struct tcp_dpm_event evt_hreset = { + .event_id = TCP_DPM_EVT_HARD_RESET, + }; + + if (ret == TCPM_SUCCESS || ret == TCP_DPM_RET_NOT_SUPPORT) + return; + + if (event->event_id == TCP_DPM_EVT_GET_STATUS + || event->event_id == TCP_DPM_EVT_GET_PPS_STATUS) { + tcpm_put_tcp_dpm_event(tcpc, &evt_hreset); + } +} +#endif /* CONFIG_MTK_HANDLE_PPS_TIMEOUT */ + +static int __tcpm_put_tcp_dpm_event_bk( + struct tcpc_device *tcpc, struct tcp_dpm_event *event, + uint32_t tout_ms, uint8_t *data, uint8_t size) +{ + int ret; + struct pd_port *pd_port = &tcpc->pd_port; + + pd_port->tcpm_bk_done = false; + pd_port->tcpm_bk_event_id = event->event_id; + +#ifdef CONFIG_USB_PD_TCPM_CB_2ND + pd_port->tcpm_bk_cb_data = data; + pd_port->tcpm_bk_cb_data_max = size; +#endif /* CONFIG_USB_PD_TCPM_CB_2ND */ + + ret = tcpm_put_tcp_dpm_event(tcpc, event); + if (ret != TCPM_SUCCESS) + return ret; + + return tcpm_dpm_wait_bk_event(pd_port, tout_ms); +} + +static int tcpm_put_tcp_dpm_event_bk( + struct tcpc_device *tcpc, struct tcp_dpm_event *event, + uint32_t tout_ms, uint8_t *data, uint8_t size) +{ + int ret; + uint8_t retry = CONFIG_USB_PD_TCPM_CB_RETRY; + struct pd_port *pd_port = &tcpc->pd_port; + + event->event_cb = tcpm_dpm_bk_event_cb; + + mutex_lock(&pd_port->tcpm_bk_lock); + + while (1) { + ret = __tcpm_put_tcp_dpm_event_bk( + tcpc, event, tout_ms, data, size); + + if ((ret != TCP_DPM_RET_TIMEOUT) || (retry == 0)) + break; + + retry--; + } + + mutex_unlock(&pd_port->tcpm_bk_lock); + +#ifndef CONFIG_USB_PD_TCPM_CB_2ND + if ((data != NULL) && (ret == TCPM_SUCCESS)) + return TCPM_ERROR_EXPECT_CB2; +#endif /* CONFIG_USB_PD_TCPM_CB_2ND */ + + if (ret == TCP_DPM_RET_DENIED_REPEAT_REQUEST) + ret = TCPM_SUCCESS; + +#ifdef CONFIG_MTK_HANDLE_PPS_TIMEOUT + mtk_handle_tcp_event_result(tcpc, event, ret); +#endif /* CONFIG_MTK_HANDLE_PPS_TIMEOUT */ + + return ret; +} + +#endif /* CONFIG_USB_PD_BLOCK_TCPM */ + +#endif /* CONFIG_USB_POWER_DELIVERY */ diff --git a/drivers/uwb/qm35/Android.mk b/drivers/uwb/qm35/Android.mk new file mode 100644 index 000000000000..4a8551d17c26 --- /dev/null +++ b/drivers/uwb/qm35/Android.mk @@ -0,0 +1,14 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) + KERNEL_CFLAGS += CONFIG_QM35_SPI_DEBUG_FW=y + KBUILD_OPTIONS += CONFIG_QM35_SPI_DEBUG_FW=y +endif + +include $(CLEAR_VARS) +LOCAL_MODULE := qm35.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_info.ko +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/uwb/qm35/Kbuild b/drivers/uwb/qm35/Kbuild new file mode 100644 index 000000000000..992c67fa345c --- /dev/null +++ b/drivers/uwb/qm35/Kbuild @@ -0,0 +1,34 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + + +ifneq ($(filter m y,$(CONFIG_QM35_SPI_DEBUG_FW)),) + EXTRA_CFLAGS += -DCONFIG_QM35_SPI_DEBUG_FW +endif + +obj-m := qm35.o + +qm35-y := \ + qm35-spi.o \ + qm35_rb.o \ + qmrom_spi.o \ + libqmrom/src/qmrom_common.o \ + libqmrom/src/qm357xx_rom_common.o \ + libqmrom/src/qm357xx_rom_b0.o \ + libqmrom/src/qm357xx_rom_c0.o \ + libqmrom/src/qmrom_log.o \ + libfwupdater/src/fwupdater.o \ + hsspi.o \ + hsspi_uci.o \ + hsspi_log.o \ + hsspi_coredump.o \ + debug.o \ + hsspi_test.o + +ifneq ($(filter m y,$(CONFIG_QM35_SPI_DEBUG_FW)),) + qm35-y += debug_qmrom.o +endif +qm35-y += qm35-trace.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../mmi_info/Module.symvers diff --git a/drivers/uwb/qm35/Makefile b/drivers/uwb/qm35/Makefile new file mode 100644 index 000000000000..4916d32b6d02 --- /dev/null +++ b/drivers/uwb/qm35/Makefile @@ -0,0 +1,17 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS := $(M)/../../mmi_info/Module.symvers + diff --git a/drivers/uwb/qm35/debug.c b/drivers/uwb/qm35/debug.c new file mode 100644 index 000000000000..1d9f6d8c227d --- /dev/null +++ b/drivers/uwb/qm35/debug.c @@ -0,0 +1,461 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 LOG layer HSSPI Protocol + */ + +#include +#include +#include + +#include +#include + +#include "qm35.h" +#include "debug.h" +#include "hsspi_test.h" + +#if IS_ENABLED(CONFIG_QM35_SPI_DEBUG_FW) +extern void debug_rom_code_init(struct debug *debug); +#endif + +static const struct file_operations debug_enable_fops; +static const struct file_operations debug_log_level_fops; +static const struct file_operations debug_test_hsspi_sleep_fops; + +static void *priv_from_file(const struct file *filp) +{ + return filp->f_path.dentry->d_inode->i_private; +} + +static ssize_t debug_enable_write(struct file *filp, const char __user *buff, + size_t count, loff_t *off) +{ + struct debug *debug; + u8 enabled; + + debug = priv_from_file(filp); + + if (kstrtou8_from_user(buff, count, 10, &enabled)) + return -EFAULT; + + if (debug->trace_ops) + debug->trace_ops->enable_set(debug, enabled == 1 ? 1 : 0); + else + return -ENOSYS; + + return count; +} + +static ssize_t debug_enable_read(struct file *filp, char __user *buff, + size_t count, loff_t *off) +{ + char enabled[2]; + struct debug *debug; + + debug = priv_from_file(filp); + + if (debug->trace_ops) + enabled[0] = debug->trace_ops->enable_get(debug) + '0'; + else + return -ENOSYS; + + enabled[1] = '\n'; + + return simple_read_from_buffer(buff, count, off, enabled, + sizeof(enabled)); +} + +static ssize_t debug_log_level_write(struct file *filp, const char __user *buff, + size_t count, loff_t *off) +{ + u8 log_level = 0; + struct log_module *log_module; + + log_module = priv_from_file(filp); + if (kstrtou8_from_user(buff, count, 10, &log_level)) + return -EFAULT; + + if (log_module->debug->trace_ops) + log_module->debug->trace_ops->level_set(log_module->debug, + log_module, log_level); + else + return -ENOSYS; + + return count; +} + +static ssize_t debug_log_level_read(struct file *filp, char __user *buff, + size_t count, loff_t *off) +{ + char log_level[2]; + struct log_module *log_module; + + log_module = priv_from_file(filp); + + if (log_module->debug->trace_ops) + log_level[0] = log_module->debug->trace_ops->level_get( + log_module->debug, log_module) + + '0'; + else + return -ENOSYS; + + log_level[1] = '\n'; + + return simple_read_from_buffer(buff, count, off, log_level, + sizeof(log_level)); +} + +static ssize_t debug_test_hsspi_sleep_write(struct file *filp, + const char __user *buff, + size_t count, loff_t *off) +{ + int sleep_inter_frame_ms; + + if (kstrtoint_from_user(buff, count, 10, &sleep_inter_frame_ms)) + return -EFAULT; + + hsspi_test_set_inter_frame_ms(sleep_inter_frame_ms); + return count; +} + +static ssize_t debug_traces_read(struct file *filp, char __user *buff, + size_t count, loff_t *off) +{ + char *entry; + rb_entry_size_t entry_size; + uint16_t ret; + struct debug *debug; + + debug = priv_from_file(filp); + + if (!debug->trace_ops) + return -ENOSYS; + + entry_size = debug->trace_ops->trace_get_next_size(debug); + if (!entry_size) { + if (filp->f_flags & O_NONBLOCK) + return 0; + + ret = wait_event_interruptible( + debug->wq, + (entry_size = + debug->trace_ops->trace_get_next_size(debug))); + if (ret) + return ret; + } + + if (entry_size > count) + return -EMSGSIZE; + + entry = debug->trace_ops->trace_get_next(debug, &entry_size); + if (!entry) + return 0; + + ret = copy_to_user(buff, entry, entry_size); + + kfree(entry); + + return ret ? -EFAULT : entry_size; +} + +static __poll_t debug_traces_poll(struct file *filp, + struct poll_table_struct *wait) +{ + struct debug *debug; + __poll_t mask = 0; + + debug = priv_from_file(filp); + + poll_wait(filp, &debug->wq, wait); + + if (debug->trace_ops && debug->trace_ops->trace_next_avail(debug)) + mask |= POLLIN; + + return mask; +} + +static int debug_traces_open(struct inode *inodep, struct file *filep) +{ + struct debug *debug; + + debug = priv_from_file(filep); + + mutex_lock(&debug->pv_filp_lock); + if (debug->pv_filp) { + mutex_unlock(&debug->pv_filp_lock); + return -EBUSY; + } + + debug->pv_filp = filep; + + if (debug->trace_ops) + debug->trace_ops->trace_reset(debug); + + mutex_unlock(&debug->pv_filp_lock); + + return 0; +} + +static int debug_traces_release(struct inode *inodep, struct file *filep) +{ + struct debug *debug; + + debug = priv_from_file(filep); + + mutex_lock(&debug->pv_filp_lock); + debug->pv_filp = NULL; + mutex_unlock(&debug->pv_filp_lock); + + return 0; +} + +static ssize_t debug_hw_reset_write(struct file *filp, const char __user *buff, + size_t count, loff_t *off) +{ + struct qm35_ctx *qm35_hdl; + struct debug *debug; + int ret; + u8 reset; + + debug = priv_from_file(filp); + qm35_hdl = container_of(debug, struct qm35_ctx, debug); + + if (kstrtou8_from_user(buff, count, 10, &reset)) + return -EFAULT; + + ret = -1; + if (reset != 0) { + pr_info("qm35: resetting chip...\n"); + ret = qm35_reset_sync(qm35_hdl); + } else + pr_warn("qm35: write non null value to force a hw reset\n"); + + if (ret) + return -ENOSYS; + + return count; +} + +static const struct file_operations debug_enable_fops = { + .owner = THIS_MODULE, + .write = debug_enable_write, + .read = debug_enable_read, +}; + +static const struct file_operations debug_log_level_fops = { + .owner = THIS_MODULE, + .write = debug_log_level_write, + .read = debug_log_level_read +}; + +static const struct file_operations debug_test_hsspi_sleep_fops = { + .owner = THIS_MODULE, + .write = debug_test_hsspi_sleep_write +}; + +static const struct file_operations debug_traces_fops = { + .owner = THIS_MODULE, + .open = debug_traces_open, + .release = debug_traces_release, + .read = debug_traces_read, + .poll = debug_traces_poll, + .llseek = no_llseek, +}; + +static const struct file_operations debug_hw_reset_fops = { + .owner = THIS_MODULE, + .write = debug_hw_reset_write, +}; + +int debug_create_module_entry(struct debug *debug, + struct log_module *log_module) +{ + struct dentry *dir; + struct dentry *file; + + dir = debugfs_create_dir(log_module->name, debug->fw_dir); + if (!dir) { + pr_err("qm35: failed to create /sys/kernel/debug/uwb0/%s\n", + log_module->name); + return -1; + } + + file = debugfs_create_file("log_level", 0644, dir, log_module, + &debug_log_level_fops); + if (!file) { + pr_err("qm35: failed to create /sys/kernel/debug/uwb0/%s/log_level\n", + log_module->name); + return -1; + } + + pr_info("qm35 debug: created /sys/kernel/debug/uwb0/%s/log_level\n", + log_module->name); + + return 0; +} + +void debug_new_trace_available(struct debug *debug) +{ + if (debug->pv_filp) { +#if IS_ENABLED(CONFIG_QM35_SPI_DEBUG_FW) + fsnotify_modify(debug->pv_filp); +#endif + } + + wake_up_interruptible(&debug->wq); +} + +static int debug_devid_show(struct seq_file *s, void *unused) +{ + struct debug *debug = (struct debug *)s->private; + uint16_t dev_id; + int rc; + + if (debug->trace_ops && debug->trace_ops->get_dev_id) { + rc = debug->trace_ops->get_dev_id(debug, &dev_id); + if (rc < 0) + return -EIO; + seq_printf(s, "deca%04x\n", dev_id); + } + return 0; +} + +static int debug_socid_show(struct seq_file *s, void *unused) +{ + struct debug *debug = (struct debug *)s->private; + uint8_t soc_id[QM357XX_ROM_SOC_ID_LEN]; + int rc; + + if (debug->trace_ops && debug->trace_ops->get_soc_id) { + rc = debug->trace_ops->get_soc_id(debug, soc_id); + if (rc < 0) + return -EIO; + seq_printf(s, "%*phN\n", QM357XX_ROM_SOC_ID_LEN, soc_id); + } + return 0; +} + +DEFINE_SHOW_ATTRIBUTE(debug_devid); +DEFINE_SHOW_ATTRIBUTE(debug_socid); + +void debug_soc_info_available(struct debug *debug) +{ + struct dentry *file; + + file = debugfs_create_file("dev_id", 0444, debug->chip_dir, debug, + &debug_devid_fops); + if (!file) { + pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/dev_id\n"); + goto unregister; + } + + file = debugfs_create_file("soc_id", 0444, debug->chip_dir, debug, + &debug_socid_fops); + if (!file) { + pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/soc_id\n"); + goto unregister; + } + + return; + +unregister: + debugfs_remove_recursive(debug->chip_dir); +} + +int debug_init_root(struct debug *debug, struct dentry *root) +{ + debug->root_dir = debugfs_create_dir("uwb0", root); + if (!debug->root_dir) { + pr_err("qm35: failed to create /sys/kernel/debug/uwb0\n"); + return -1; + } + + return 0; +} + +int debug_init(struct debug *debug) +{ + struct dentry *file; + + init_waitqueue_head(&debug->wq); + mutex_init(&debug->pv_filp_lock); + debug->pv_filp = NULL; + + debug->fw_dir = debugfs_create_dir("fw", debug->root_dir); + if (!debug->fw_dir) { + pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw\n"); + goto unregister; + } + + debug->chip_dir = debugfs_create_dir("chip", debug->root_dir); + if (!debug->chip_dir) { + pr_err("qm35: failed to create /sys/kernel/debug/uwb0/chip\n"); + goto unregister; + } + + file = debugfs_create_file("hw_reset", 0444, debug->chip_dir, debug, + &debug_hw_reset_fops); + if (!file) { + pr_err("qm35: failed to create /sys/kernel/debug/uwb0/chip/hw_reset\n"); + goto unregister; + } + + file = debugfs_create_file("enable", 0644, debug->fw_dir, debug, + &debug_enable_fops); + if (!file) { + pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/enable\n"); + goto unregister; + } + + file = debugfs_create_file("traces", 0444, debug->fw_dir, debug, + &debug_traces_fops); + if (!file) { + pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/traces\n"); + goto unregister; + } + + file = debugfs_create_file("test_sleep_hsspi_ms", 0200, debug->fw_dir, + debug, &debug_test_hsspi_sleep_fops); + if (!file) { + pr_err("qm35: failed to create /sys/kernel/debug/uwb0/fw/test_sleep_hsspi\n"); + goto unregister; + } + +#if IS_ENABLED(CONFIG_QM35_SPI_DEBUG_FW) + debug_rom_code_init(debug); +#endif + + return 0; + +unregister: + debug_deinit(debug); + return -1; +} + +void debug_deinit(struct debug *debug) +{ + wake_up_interruptible(&debug->wq); + debugfs_remove_recursive(debug->root_dir); +} diff --git a/drivers/uwb/qm35/debug.h b/drivers/uwb/qm35/debug.h new file mode 100644 index 000000000000..d105d1fad41d --- /dev/null +++ b/drivers/uwb/qm35/debug.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 LOG layer HSSPI Protocol + */ + +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +#include +#include +#include "qm35_rb.h" + +struct debug; +struct log_module; + +struct debug_trace_ops { + void (*enable_set)(struct debug *dbg, int enable); + int (*enable_get)(struct debug *dbg); + void (*level_set)(struct debug *dbg, struct log_module *log_module, + int lvl); + int (*level_get)(struct debug *dbg, struct log_module *log_module); + char *(*trace_get_next)(struct debug *dbg, rb_entry_size_t *len); + rb_entry_size_t (*trace_get_next_size)(struct debug *dbg); + bool (*trace_next_avail)(struct debug *dbg); + void (*trace_reset)(struct debug *dbg); + int (*get_dev_id)(struct debug *dbg, uint16_t *dev_id); + int (*get_soc_id)(struct debug *dbg, uint8_t *soc_id); +}; + +struct debug { + struct dentry *root_dir; + struct dentry *fw_dir; + struct dentry *chip_dir; + const struct debug_trace_ops *trace_ops; + struct wait_queue_head wq; + struct file *pv_filp; + struct mutex pv_filp_lock; + struct firmware *certificate; +}; + +// TODO move this from here to a commom place for both log layer and debug +struct log_module { + uint8_t id; + uint8_t lvl; + char name[64]; + struct completion *read_done; + struct debug *debug; +}; + +int debug_init_root(struct debug *debug, struct dentry *root); +int debug_init(struct debug *debug); +void debug_deinit(struct debug *debug); + +int debug_create_module_entry(struct debug *debug, + struct log_module *log_module); + +void debug_new_trace_available(struct debug *debug); + +void debug_soc_info_available(struct debug *debug); + +#endif // __DEBUG_H__ diff --git a/drivers/uwb/qm35/debug_qmrom.c b/drivers/uwb/qm35/debug_qmrom.c new file mode 100644 index 000000000000..7f39c961fcf2 --- /dev/null +++ b/drivers/uwb/qm35/debug_qmrom.c @@ -0,0 +1,471 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 LOG layer HSSPI Protocol + */ + +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) +#include +#endif +#include +#include + +#include +#include +#include +#include + +#include + +#include "qm35.h" +#include "debug.h" + +extern int fu_spi_speed_hz; +extern int qmrom_spi_speed_hz; + +#define QMROM_RETRIES 10 + +static void *priv_from_file(const struct file *filp) +{ + return filp->f_path.dentry->d_inode->i_private; +} + +static struct qm35_ctx *rom_test_prepare(struct file *filp) +{ + struct debug *debug = priv_from_file(filp); + struct qm35_ctx *qm35_hdl = container_of(debug, struct qm35_ctx, debug); + + qm35_hsspi_stop(qm35_hdl); + qmrom_set_log_device(&qm35_hdl->spi->dev, LOG_DBG); + + enable_irq(qm35_hdl->ss_rdy_irq); + + qm35_hdl->flashing = true; + return qm35_hdl; +} + +static void rom_test_unprepare(struct qm35_ctx *qm35_hdl) +{ + disable_irq_nosync(qm35_hdl->ss_rdy_irq); + + qm35_hdl->flashing = false; + qmrom_set_log_device(&qm35_hdl->spi->dev, LOG_WARN); + qm35_hsspi_start(qm35_hdl); +} + +static struct firmware *file2firmware(const char *filename) +{ + struct firmware *firmware = + kmalloc(sizeof(struct firmware), GFP_KERNEL | __GFP_ZERO); + if (!firmware) { + goto fail; + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) + { + ssize_t bytes_read = kernel_read_file_from_path( + filename, 0, (void **)&firmware->data, INT_MAX, + &firmware->size, READING_FIRMWARE); + if (bytes_read < 0) { + pr_err("qm35: kernel_read_file_from_path(%s) returned %d\n", + filename, (int)bytes_read); + goto fail; + } + if (bytes_read != firmware->size) { + pr_err("qm35: kernel_read_file_from_path returned %zu; expected %zu\n", + bytes_read, firmware->size); + goto fail; + } + } +#else + { + loff_t size = 0; + int ret = kernel_read_file_from_path(filename, + (void **)&firmware->data, + &size, INT_MAX, + READING_FIRMWARE); + if (ret < 0) { + pr_err("qm35: kernel_read_file_from_path(%s) returned %d\n", + filename, ret); + goto fail; + } + firmware->size = (size_t)size; + } +#endif + + print_hex_dump(KERN_DEBUG, "qm35: Bin file:", DUMP_PREFIX_ADDRESS, 16, + 1, firmware->data, 16, false); + return firmware; + +fail: + if (firmware) { + if (firmware->data) + vfree(firmware->data); + kfree(firmware); + } + return NULL; +} + +static ssize_t rom_probe(struct file *filp, const char __user *buff, + size_t count, loff_t *off) +{ + struct qm35_ctx *qm35_hdl = rom_test_prepare(filp); + struct qmrom_handle *h; + + pr_info("qm35: Starting the probe test...\n"); + h = qmrom_init(&qm35_hdl->spi->dev, qm35_hdl, qm35_hdl, + qm35_hdl->gpio_ss_irq, qmrom_spi_speed_hz, QMROM_RETRIES, + qmrom_spi_reset_device, DEVICE_GEN_QM357XX); + if (!h) { + pr_err("qm35: qmrom_init failed\n"); + goto end; + } + + pr_info("qm35: chip_revision %#2x\n", h->chip_rev); + pr_info("qm35: device version %#02x\n", h->device_version); + if (h->chip_rev != 0xa001) { + pr_info("qm35: lcs_state %u\n", h->qm357xx_soc_info.lcs_state); + print_hex_dump(KERN_DEBUG, "qm35: soc_id:", DUMP_PREFIX_NONE, + 16, 1, h->qm357xx_soc_info.soc_id, + sizeof(h->qm357xx_soc_info.soc_id), false); + print_hex_dump(KERN_DEBUG, "qm35: uuid:", DUMP_PREFIX_NONE, 16, + 1, h->qm357xx_soc_info.uuid, + sizeof(h->qm357xx_soc_info.uuid), false); + } + qmrom_deinit(h); + qmrom_spi_reset_device(qm35_hdl); + qmrom_msleep(100); + +end: + rom_test_unprepare(qm35_hdl); + return count; +} + +static ssize_t rom_flash_dbg_cert(struct file *filp, const char __user *buff, + size_t count, loff_t *off) +{ + struct qm35_ctx *qm35_hdl = rom_test_prepare(filp); + struct firmware *certificate = NULL; + struct qmrom_handle *h = NULL; + char *filename; + int err; + + filename = kmalloc(count, GFP_KERNEL); + if (!filename) { + count = -ENOMEM; + goto end; + } + + err = copy_from_user(filename, buff, count); + if (err) { + pr_err("qm35: copy_from_user failed with error %d\n", err); + count = err; + goto end; + } + filename[count - 1] = '\0'; + certificate = file2firmware(filename); + if (!certificate || certificate->size != DEBUG_CERTIFICATE_SIZE) { + pr_err("qm35: %s: file retrieval failed, abort (%s)\n", + __func__, certificate ? "wrong size" : "not found"); + count = -1; + goto end; + } + + /* Flash the debug certificate */ + pr_info("Flashing debug certificate %s...\n", filename); + + h = qmrom_init(&qm35_hdl->spi->dev, qm35_hdl, qm35_hdl, + qm35_hdl->gpio_ss_irq, qmrom_spi_speed_hz, QMROM_RETRIES, + qmrom_spi_reset_device, DEVICE_GEN_QM357XX); + if (!h) { + pr_err("qm35: qmrom_init failed\n"); + goto end; + } + err = qm357xx_rom_flash_dbg_cert(h, certificate); + if (err) + pr_err("qm35: Flashing debug certificate %s failed with %d!\n", + filename, err); + else + pr_info("qm35: Flashing debug certificate %s succeeded!\n", + filename); + +end: + if (filename) + kfree(filename); + if (h) + qmrom_deinit(h); + if (certificate) { + if (certificate->data) + vfree(certificate->data); + kfree(certificate); + } + qmrom_spi_reset_device(qm35_hdl); + qmrom_msleep(100); + + rom_test_unprepare(qm35_hdl); + return count; +} + +static ssize_t rom_erase_dbg_cert(struct file *filp, const char __user *buff, + size_t count, loff_t *off) +{ + struct qm35_ctx *qm35_hdl = rom_test_prepare(filp); + struct qmrom_handle *h = NULL; + int err; + + pr_info("qm35: Erasing debug certificate...\n"); + + h = qmrom_init(&qm35_hdl->spi->dev, qm35_hdl, qm35_hdl, + qm35_hdl->gpio_ss_irq, qmrom_spi_speed_hz, QMROM_RETRIES, + qmrom_spi_reset_device, DEVICE_GEN_QM357XX); + if (!h) { + pr_err("qm35: qmrom_init failed\n"); + goto end; + } + err = qm357xx_rom_erase_dbg_cert(h); + if (err) + pr_err("qm35: Erasing debug certificate failed with %d!\n", + err); + else + pr_info("qm35: Erasing debug certificate succeeded!\n"); + +end: + if (h) + qmrom_deinit(h); + + qmrom_spi_reset_device(qm35_hdl); + qmrom_msleep(100); + + rom_test_unprepare(qm35_hdl); + return count; +} + +static ssize_t rom_flash_fw(struct file *filp, const char __user *buff, + size_t count, loff_t *off) +{ + struct qm35_ctx *qm35_hdl = rom_test_prepare(filp); + struct firmware *fw = NULL; + struct qmrom_handle *h = NULL; + char *filename; + int rc; + + filename = kmalloc(count, GFP_KERNEL); + if (!filename) { + count = -ENOMEM; + goto end; + } + + rc = copy_from_user(filename, buff, count); + if (rc) { + pr_err("qm35: copy_from_user failed with error %d\n", rc); + goto end; + } + filename[count - 1] = '\0'; + fw = file2firmware(filename); + if (!fw) { + pr_err("qm35: %s: file %s retrieval failed, abort\n", __func__, + filename); + goto end; + } + + pr_info("qm35: Flashing image %s (%pK->data %pK)...\n", filename, fw, + fw->data); + + h = qmrom_init(&qm35_hdl->spi->dev, qm35_hdl, qm35_hdl, + qm35_hdl->gpio_ss_irq, qmrom_spi_speed_hz, QMROM_RETRIES, + qmrom_spi_reset_device, DEVICE_GEN_QM357XX); + if (!h) { + pr_err("qm35: qmrom_init failed\n"); + goto end; + } + rc = qm357xx_rom_flash_fw(h, fw); + if (rc) + pr_err("qm35: Flashing firmware %s failed with %d!\n", filename, + rc); + else + pr_info("qm35: Flashing firmware %s succeeded!\n", filename); + +end: + kfree(filename); + if (h) + qmrom_deinit(h); + if (fw) { + if (fw->data) + vfree(fw->data); + kfree(fw); + } + rom_test_unprepare(qm35_hdl); + return count; +} + +static ssize_t rom_flash_fw_pkg(struct file *filp, const char __user *buff, + size_t count, loff_t *off) +{ + struct qm35_ctx *qm35_hdl = rom_test_prepare(filp); + const struct firmware *fw = NULL; + struct qmrom_handle *h = NULL; + char *filename = NULL, *fw_data; + uint32_t fw_size; + int rc; + + if (fu_spi_speed_hz == 0) + fu_spi_speed_hz = FWUPDATER_SPI_SPEED_HZ; + + filename = kmalloc(count, GFP_KERNEL); + if (!filename) { + count = -ENOMEM; + goto end; + } + rc = copy_from_user(filename, buff, count); + if (rc) { + pr_err("qm35: copy_from_user failed with error %d\n", rc); + goto end; + } + filename[count - 1] = '\0'; + fw = file2firmware(filename); + if (!fw || !fw->data) { + pr_err("qm35: %s file %s retrieval failed (%s), abort\n", + __func__, filename, fw ? "no data read" : "not found"); + goto end; + } + + pr_info("qm35: Flashing fw_updater...\n"); + h = qmrom_init(&qm35_hdl->spi->dev, qm35_hdl, qm35_hdl, + qm35_hdl->gpio_ss_irq, qmrom_spi_speed_hz, QMROM_RETRIES, + qmrom_spi_reset_device, DEVICE_GEN_QM357XX); + if (!h) { + pr_err("qm35: qmrom_init failed\n"); + goto end; + } + h->skip_check_fw_boot = true; + rc = qm357xx_rom_flash_fw(h, fw); + h->skip_check_fw_boot = false; + if (rc) { + pr_err("qm35: Flashing fw_updater failed with %d!\n", rc); + goto end; + } + + pr_info("qm35: Flashing fw_updater succeeded, flashing the fw package now...\n"); + qmrom_spi_set_freq(fu_spi_speed_hz); + + rc = qm357xx_rom_fw_macro_pkg_get_fw_idx(fw, 1, &fw_size, &fw_data); + if (rc) { + pr_err("qm35: %s FW MACRO PACKAGE corupted = %d\n", __func__, + rc); + goto end; + } + + if (*(uint32_t *)fw_data == CRYPTO_FIRMWARE_PACK_MAGIC_VALUE) { + rc = run_fwupdater(h, fw_data, fw_size); + } else { + pr_err("qm35: FW PACKAGE not found - %04x! fw_size = %d\n", + *(uint32_t *)fw_data, fw_size); + goto end; + } + pr_info("qm35: FW package flashing %s (rc = %d), rebooting the QM...\n", + rc ? "failed" : "succeeded", rc); + +end: + qmrom_spi_reset_device(qm35_hdl); + + if (filename) + kfree(filename); + if (h) + qmrom_deinit(h); + if (fw) { + if (fw->data) + vfree(fw->data); + kfree(fw); + } + rom_test_unprepare(qm35_hdl); + return count; +} + +static const struct file_operations rom_probe_fops = { .owner = THIS_MODULE, + .write = rom_probe }; + +static const struct file_operations rom_flash_dbg_cert_fops = { + .owner = THIS_MODULE, + .write = rom_flash_dbg_cert +}; + +static const struct file_operations rom_erase_dbg_cert_fops = { + .owner = THIS_MODULE, + .write = rom_erase_dbg_cert +}; + +static const struct file_operations rom_flash_fw_fops = { + .owner = THIS_MODULE, + .write = rom_flash_fw +}; + +static const struct file_operations rom_flash_fw_pkg_fops = { + .owner = THIS_MODULE, + .write = rom_flash_fw_pkg +}; + +void debug_rom_code_init(struct debug *debug) +{ + struct dentry *file; + file = debugfs_create_file("rom_probe", 0200, debug->fw_dir, debug, + &rom_probe_fops); + if (!file) { + pr_err("qm35: failed to create uwb0/fw/rom_probe\n"); + return; + } + + file = debugfs_create_file("rom_flash_dbg_cert", 0200, debug->fw_dir, + debug, &rom_flash_dbg_cert_fops); + if (!file) { + pr_err("qm35: failed to create uwb0/fw/rom_flash_dbg_cert\n"); + return; + } + + file = debugfs_create_file("rom_erase_dbg_cert", 0200, debug->fw_dir, + debug, &rom_erase_dbg_cert_fops); + if (!file) { + pr_err("qm35: failed to create uwb0/fw/rom_erase_dbg_cert\n"); + return; + } + + file = debugfs_create_file("rom_flash_fw", 0200, debug->fw_dir, debug, + &rom_flash_fw_fops); + if (!file) { + pr_err("qm35: failed to create uwb0/fw/rom_flash_fw\n"); + return; + } + + file = debugfs_create_file("rom_flash_fw_pkg", 0200, debug->fw_dir, + debug, &rom_flash_fw_pkg_fops); + if (!file) { + pr_err("qm35: failed to create uwb0/fw/rom_flash_fw_pkg\n"); + return; + } +} diff --git a/drivers/uwb/qm35/hsspi.c b/drivers/uwb/qm35/hsspi.c new file mode 100644 index 000000000000..174540de3a3b --- /dev/null +++ b/drivers/uwb/qm35/hsspi.c @@ -0,0 +1,754 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2021 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 HSSPI Protocol + */ + +#include +#include + +#include "qm35-trace.h" +#include "hsspi.h" + +/* STC HOST flags */ +#define STC_HOST_WR BIT(7) +#define STC_HOST_PRD BIT(6) +#define STC_HOST_RD BIT(5) + +/* STC SOC flags */ +#define STC_SOC_ODW BIT(7) +#define STC_SOC_OA BIT(6) +#define STC_SOC_RDY BIT(5) +#define STC_SOC_ERR BIT(4) + +#define SS_READY_TIMEOUT_MS (250) + +#define MAX_SUCCESSIVE_ERRORS (5) +#define SPI_CS_SETUP_DELAY_US (5) + +#ifdef HSSPI_MANUAL_CS_SETUP +#define HSSPI_MANUAL_CS_SETUP_US SPI_CS_SETUP_DELAY_US +#endif + +extern int spi_speed_hz; + +struct hsspi_work { + struct list_head list; + enum hsspi_work_type type; + union { + struct { + struct hsspi_block *blk; + struct hsspi_layer *layer; + } tx; + struct completion *completion; + }; +}; + +int test_sleep_after_ss_ready_us = 0; + +static inline bool layer_id_is_valid(struct hsspi *hsspi, u8 ul) +{ + return (ul < ARRAY_SIZE(hsspi->layers)); +} + +/** + * get_work() - get a work from the list + * + * @hsspi: &struct hsspi + * + * Return: a &struct hsspi_work + * The work can be: + * - a TX work enqueued by hsspi_send + * - a COMPLETION work used for synchronization (always allocated on stack) + */ +static struct hsspi_work *get_work(struct hsspi *hsspi) +{ + struct hsspi_work *hw; + + spin_lock(&hsspi->lock); + + hw = list_first_entry_or_null(&hsspi->work_list, struct hsspi_work, + list); + if (hw) + list_del(&hw->list); + + spin_unlock(&hsspi->lock); + + trace_hsspi_get_work(&hsspi->spi->dev, hw ? hw->type : -1); + return hw; +} + +/** + * is_txrx_waiting() - is there something to do + * + * @hsspi: &struct hsspi + * + * Return: True if there is a TX work available or if the SS_IRQ flag + * is set. False otherwise. + */ +static bool is_txrx_waiting(struct hsspi *hsspi) +{ + enum hsspi_state state; + bool is_empty; + + spin_lock(&hsspi->lock); + + is_empty = list_empty(&hsspi->work_list); + state = hsspi->state; + + spin_unlock(&hsspi->lock); + + trace_hsspi_is_txrx_waiting(&hsspi->spi->dev, is_empty, state); + /* + * There is no work in the list but we must check SS_IRQ to + * know if we need to make an empty TX transfer with PRE_READ + * flag set. + */ + return !is_empty || ((state == HSSPI_RUNNING) && + test_bit(HSSPI_FLAGS_SS_IRQ, hsspi->flags)); +} + +/** + * hsspi_wait_ss_ready() - waits for ss_ready to be up + * + * @hsspi: &struct hsspi + * + * Return: 0 if ss_ready is up and EAGAIN if not. + */ +static int hsspi_wait_ss_ready(struct hsspi *hsspi) +{ + int ret; + + hsspi->waiting_ss_rdy = true; + + if (!test_bit(HSSPI_FLAGS_SS_BUSY, hsspi->flags)) { + /* The ss_ready went low, so the fw is not busy anymore, + * if the ss_ready is high, we can proceed, else, + * either the fw went to sleep or crashed, in any case + * we need to wait for it to be ready again. + */ + clear_bit(HSSPI_FLAGS_SS_READY, hsspi->flags); + if (gpiod_get_value(hsspi->gpio_ss_rdy)) { + return 0; + } + } + + /* Check if the QM went to sleep and wake it up if it did */ + if (!gpiod_get_value(hsspi->gpio_exton)) { + hsspi->wakeup(hsspi); + } + + ret = wait_event_interruptible_timeout( + hsspi->wq_ready, + test_and_clear_bit(HSSPI_FLAGS_SS_READY, hsspi->flags), + msecs_to_jiffies(SS_READY_TIMEOUT_MS)); + if (ret == 0) { + dev_warn(&hsspi->spi->dev, + "timed out waiting for ss_ready(%d)\n", + test_bit(HSSPI_FLAGS_SS_READY, hsspi->flags)); + return -EAGAIN; + } + if (ret < 0) { + dev_err(&hsspi->spi->dev, + "Error %d while waiting for ss_ready\n", ret); + return ret; + } + /* WA: QM35 C0 have a very short (<100ns) ss_ready toggle + * in the ROM code when waking up from S4. If we transfer immediately, + * the ROM code will enter its command mode and we'll end up + * communicating with the ROM code instead of the firmware. + */ + if (!gpiod_get_value(hsspi->gpio_ss_rdy)) + return -EAGAIN; + + hsspi->waiting_ss_rdy = false; + return 0; +} + +#ifdef HSSPI_MANUAL_CS_SETUP +int hsspi_set_cs_level(struct spi_device *spi, int level) +{ + struct spi_transfer xfer[] = { + { + .cs_change = !level, + .speed_hz = 1000000, + }, + }; + + return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); +} +#endif + +/** + * spi_xfer() - Single SPI transfer + * + * @hsspi: &struct hsspi + * @tx: tx payload + * @rx: rx payload + * @length: payload length + */ +static int spi_xfer(struct hsspi *hsspi, const void *tx, void *rx, + size_t length) +{ + struct spi_transfer xfers[2] = { + { + .tx_buf = hsspi->host, + .rx_buf = hsspi->soc, + .len = sizeof(*(hsspi->host)), + .speed_hz = spi_speed_hz, + }, + { + .tx_buf = tx, + .rx_buf = rx, + .len = length, + .speed_hz = spi_speed_hz, + }, + }; + int ret, retry = 5; + + hsspi->soc->flags = 0; + hsspi->soc->ul = 0; + hsspi->soc->length = 0; + + do { + ret = hsspi_wait_ss_ready(hsspi); + if (ret < 0) { + continue; + } + + if (test_sleep_after_ss_ready_us > 0) + usleep_range(test_sleep_after_ss_ready_us, + test_sleep_after_ss_ready_us + 1); + + hsspi_set_spi_slave_busy(hsspi); +#ifdef HSSPI_MANUAL_CS_SETUP + hsspi_set_cs_level(hsspi->spi, 0); + udelay(HSSPI_MANUAL_CS_SETUP_US); +#endif + ret = spi_sync_transfer(hsspi->spi, xfers, length ? 2 : 1); + + trace_hsspi_spi_xfer(&hsspi->spi->dev, hsspi->host, hsspi->soc, + ret); + + if (ret) { + dev_err(&hsspi->spi->dev, "spi_sync_transfer: %d\n", + ret); + continue; + } + + if (!(hsspi->soc->flags & STC_SOC_RDY) || + (hsspi->soc->flags & 0x0f)) { + hsspi->wakeup(hsspi); + ret = -EAGAIN; + continue; + } + + /* All looks good! */ + break; + } while ((ret == -EAGAIN) && (--retry > 0)); + + if (!(hsspi->soc->flags & STC_SOC_RDY) || (hsspi->soc->flags & 0x0f)) { + dev_err(&hsspi->spi->dev, "FW not ready (flags %#02x)\n", + hsspi->soc->flags); + } + + hsspi->waiting_ss_rdy = false; + return ret; +} + +static bool check_soc_flag(const struct device *dev, const char *func_name, + u8 soc_flags, bool is_tx) +{ + u8 expected; + bool res; + + expected = is_tx ? 0x0 : STC_SOC_OA; + res = (soc_flags & (STC_SOC_ERR | STC_SOC_OA)) == expected; + + if (!res) { + dev_warn(dev, "%s: bad soc flags %#hhx, expected %#hhx\n", + func_name, soc_flags, expected); + } + return res; +} + +/** + * hsspi_rx() - request data from the QM35 on the HSSPI + * + * @hsspi: &struct hsspi + * @ul: upper layer id + * @length: length of data requested + */ +static int hsspi_rx(struct hsspi *hsspi, u8 ul, u16 length) +{ + struct hsspi_layer *layer; + struct hsspi_block *blk; + int ret; + + hsspi->host->flags = STC_HOST_RD; + hsspi->host->ul = ul; + hsspi->host->length = length; + + if (layer_id_is_valid(hsspi, ul)) { + spin_lock(&hsspi->lock); + layer = hsspi->layers[ul]; + spin_unlock(&hsspi->lock); + } else + layer = NULL; + + blk = layer ? layer->ops->get(layer, length) : NULL; + if (blk) { + ret = spi_xfer(hsspi, NULL, blk->data, blk->size); + + layer->ops->received(layer, blk, ret); + } else + ret = spi_xfer(hsspi, NULL, NULL, 0); + + if (ret) + return ret; + + if (!check_soc_flag(&hsspi->spi->dev, __func__, hsspi->soc->flags, + false)) { + ret = -1; + } + + if ((hsspi->soc->ul != ul) || (hsspi->soc->length != length)) { + dev_warn(&hsspi->spi->dev, + "%s: received %hhu %hu but expecting %hhu %hu\n", + __func__, hsspi->soc->ul, hsspi->soc->length, ul, + length); + ret = -1; + } + + if (!(hsspi->soc->flags & STC_SOC_ODW) && + test_and_clear_bit(HSSPI_FLAGS_SS_IRQ, hsspi->flags)) + hsspi->odw_cleared(hsspi); + + return ret; +} + +/** + * hsspi_tx() - send a hsspi block to the QM35 on the HSSPI + * + * @hsspi: &struct hsspi + * @layer: &struct hsspi_layer + * @blk: &struct hsspi_block + * + * It also adds PRD flag if SS_IRQ is set. Therefore it will try a RX + * transfer accordingly. + */ +static int hsspi_tx(struct hsspi *hsspi, struct hsspi_layer *layer, + struct hsspi_block *blk) +{ + int ret; + + hsspi->host->flags = STC_HOST_WR; + hsspi->host->ul = layer->id; + hsspi->host->length = blk->length; + + if (test_bit(HSSPI_FLAGS_SS_IRQ, hsspi->flags)) + hsspi->host->flags |= STC_HOST_PRD; + + ret = spi_xfer(hsspi, blk->data, NULL, blk->size); + + layer->ops->sent(layer, blk, ret); + + if (ret) + return ret; + + /* Ignore tx check flags */ + check_soc_flag(&hsspi->spi->dev, __func__, hsspi->soc->flags, true); + + if ((hsspi->host->flags & STC_HOST_PRD) && + (hsspi->soc->flags & STC_SOC_ODW)) + return hsspi_rx(hsspi, hsspi->soc->ul, hsspi->soc->length); + + return ret; +} + +/** + * hsspi_pre_read() - send a PRE_READ with no TX and do RX + * + * @hsspi: &struct hsspi + * + * This function is a particular case of hsspi_tx with no layer or + * blk. + * + */ +static int hsspi_pre_read(struct hsspi *hsspi) +{ + int ret; + + hsspi->host->flags = STC_HOST_PRD; + hsspi->host->ul = 0; + hsspi->host->length = 0; + + ret = spi_xfer(hsspi, NULL, NULL, 0); + if (ret) + return ret; + + /* Ignore pre-read check flags */ + check_soc_flag(&hsspi->spi->dev, __func__, hsspi->soc->flags, true); + + if (hsspi->soc->flags & STC_SOC_ODW) + return hsspi_rx(hsspi, hsspi->soc->ul, hsspi->soc->length); + else + /* Pre-read error. Maybe FW is a little late to setup HSSPI header. */ + return -1; +} + +/** + * hsspi_thread_fn() - the thread that manage all SPI transfers + * @data: the &struct hsspi + * + */ +static int hsspi_thread_fn(void *data) +{ + struct hsspi *hsspi = data; + static int successive_errors; + + successive_errors = 0; + while (1) { + struct hsspi_work *hw; + int ret; + + ret = wait_event_interruptible(hsspi->wq, + is_txrx_waiting(hsspi) || + kthread_should_stop()); + if (ret) + return ret; + + if (kthread_should_stop()) + break; + + hw = get_work(hsspi); + if (hw) { + if (hw->type == HSSPI_WORK_COMPLETION) { + complete(hw->completion); + /* on the stack no need to free */ + continue; + } else if (hw->type == HSSPI_WORK_TX) { + ret = hsspi_tx(hsspi, hw->tx.layer, hw->tx.blk); + kfree(hw); + } else { + dev_err(&hsspi->spi->dev, + "unknown hsspi_work type: %d\n", + hw->type); + continue; + } + } else + /* If there is no work, we are here because + * SS_IRQ is set. + */ + ret = hsspi_pre_read(hsspi); + + if (ret) { + successive_errors++; + + if (successive_errors > MAX_SUCCESSIVE_ERRORS) { + dev_err(&hsspi->spi->dev, + "Max successive errors %d reached, likely entered ROM code...\n", + successive_errors); + + /* When the device reboots, the ROM code might raise + * ss_ready; if a SPI transfer is requested, the AP + * will initiate the SPI xfer and the ROM code will + * enter its command mode infinite loop... + * No choice but rebooting the device. + */ + hsspi->reset_qm35(hsspi); + successive_errors = 0; + } + } else { + successive_errors = 0; + } + } + return 0; +} + +int hsspi_init(struct hsspi *hsspi, struct spi_device *spi) +{ + memset(hsspi, 0, sizeof(*hsspi)); + + spin_lock_init(&hsspi->lock); + INIT_LIST_HEAD(&hsspi->work_list); + + hsspi->state = HSSPI_STOPPED; + hsspi->spi = spi; + + init_waitqueue_head(&hsspi->wq); + init_waitqueue_head(&hsspi->wq_ready); + + hsspi->host = kmalloc(sizeof(*(hsspi->host)), GFP_KERNEL | GFP_DMA); + hsspi->soc = kmalloc(sizeof(*(hsspi->soc)), GFP_KERNEL | GFP_DMA); + + hsspi->thread = kthread_create(hsspi_thread_fn, hsspi, "hsspi"); + if (IS_ERR(hsspi->thread)) + return PTR_ERR(hsspi->thread); + + wake_up_process(hsspi->thread); + + dev_info(&hsspi->spi->dev, "HSSPI initialized\n"); + return 0; +} + +void hsspi_set_gpios(struct hsspi *hsspi, struct gpio_desc *gpio_ss_rdy, + struct gpio_desc *gpio_exton) +{ + hsspi->gpio_ss_rdy = gpio_ss_rdy; + hsspi->gpio_exton = gpio_exton; +} + +int hsspi_deinit(struct hsspi *hsspi) +{ + int i; + + spin_lock(&hsspi->lock); + + if (hsspi->state == HSSPI_RUNNING) { + spin_unlock(&hsspi->lock); + return -EBUSY; + } + + for (i = 0; i < ARRAY_SIZE(hsspi->layers); i++) { + if (!hsspi->layers[i]) + continue; + + dev_err(&hsspi->spi->dev, + "HSSPI upper layer '%s' not unregistered\n", + hsspi->layers[i]->name); + spin_unlock(&hsspi->lock); + return -EBUSY; + } + + spin_unlock(&hsspi->lock); + + kthread_stop(hsspi->thread); + + kfree(hsspi->host); + kfree(hsspi->soc); + + dev_info(&hsspi->spi->dev, "HSSPI uninitialized\n"); + return 0; +} + +int hsspi_register(struct hsspi *hsspi, struct hsspi_layer *layer) +{ + int ret = 0; + + if (!layer_id_is_valid(hsspi, layer->id)) + return -EINVAL; + + spin_lock(&hsspi->lock); + + if (hsspi->layers[layer->id]) + ret = -EBUSY; + else + hsspi->layers[layer->id] = layer; + + spin_unlock(&hsspi->lock); + + if (ret) { + dev_err(&hsspi->spi->dev, "%s: '%s' ret: %d\n", __func__, + layer->name, ret); + return ret; + } + + dev_dbg(&hsspi->spi->dev, "HSSPI upper layer '%s' registered\n", + layer->name); + return 0; +} + +int hsspi_unregister(struct hsspi *hsspi, struct hsspi_layer *layer) +{ + DECLARE_COMPLETION_ONSTACK(complete); + struct hsspi_work complete_work = { + .type = HSSPI_WORK_COMPLETION, + .completion = &complete, + }; + int ret = 0; + + if (!layer_id_is_valid(hsspi, layer->id)) + return -EINVAL; + + spin_lock(&hsspi->lock); + + if (hsspi->layers[layer->id] == layer) { + hsspi->layers[layer->id] = NULL; + + list_add_tail(&complete_work.list, &hsspi->work_list); + } else + ret = -EINVAL; + + spin_unlock(&hsspi->lock); + + if (ret) { + dev_err(&hsspi->spi->dev, "%s: '%s' ret: %d\n", __func__, + layer->name, ret); + return ret; + } + + wake_up_interruptible(&hsspi->wq); + + /* when completed there is no more reference to layer in the + * work_list or in the hsspi_thread_fn + */ + wait_for_completion(&complete); + + dev_dbg(&hsspi->spi->dev, "HSSPI upper layer '%s' unregistered\n", + layer->name); + return 0; +} + +void hsspi_clear_spi_slave_busy(struct hsspi *hsspi) +{ + clear_bit(HSSPI_FLAGS_SS_BUSY, hsspi->flags); +} + +void hsspi_set_spi_slave_busy(struct hsspi *hsspi) +{ + set_bit(HSSPI_FLAGS_SS_BUSY, hsspi->flags); +} + +void hsspi_set_spi_slave_ready(struct hsspi *hsspi) +{ + set_bit(HSSPI_FLAGS_SS_READY, hsspi->flags); + + wake_up_interruptible(&hsspi->wq_ready); +} + +void hsspi_clear_spi_slave_ready(struct hsspi *hsspi) +{ + clear_bit(HSSPI_FLAGS_SS_READY, hsspi->flags); +} + +void hsspi_set_output_data_waiting(struct hsspi *hsspi) +{ + set_bit(HSSPI_FLAGS_SS_IRQ, hsspi->flags); + + wake_up_interruptible(&hsspi->wq); +} + +int hsspi_init_block(struct hsspi_block *blk, u16 length) +{ + void *data; + + data = krealloc(blk->data, length, GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + + blk->data = data; + blk->length = length; + blk->size = length; + + return 0; +} + +void hsspi_deinit_block(struct hsspi_block *blk) +{ + kfree(blk->data); + blk->data = NULL; +} + +int hsspi_send(struct hsspi *hsspi, struct hsspi_layer *layer, + struct hsspi_block *blk) +{ + struct hsspi_work *tx_work; + int ret = 0; + + if (!layer || !blk) + return -EINVAL; + + if (!layer_id_is_valid(hsspi, layer->id)) + return -EINVAL; + + tx_work = kzalloc(sizeof(*tx_work), GFP_KERNEL); + if (!tx_work) + return -ENOMEM; + + tx_work->type = HSSPI_WORK_TX; + tx_work->tx.blk = blk; + tx_work->tx.layer = layer; + + spin_lock(&hsspi->lock); + + if (hsspi->state == HSSPI_RUNNING) { + if (hsspi->layers[layer->id] == layer) + list_add_tail(&tx_work->list, &hsspi->work_list); + else + ret = -EINVAL; + } else + ret = -EAGAIN; + + spin_unlock(&hsspi->lock); + + if (ret) { + kfree(tx_work); + dev_err(&hsspi->spi->dev, "%s: %d\n", __func__, ret); + return ret; + } + + wake_up_interruptible(&hsspi->wq); + + dev_dbg(&hsspi->spi->dev, "send %d bytes on HSSPI '%s' layer\n", + blk->length, layer->name); + return 0; +} + +void hsspi_start(struct hsspi *hsspi) +{ + spin_lock(&hsspi->lock); + + hsspi->state = HSSPI_RUNNING; + + spin_unlock(&hsspi->lock); + + wake_up_interruptible(&hsspi->wq); + + dev_dbg(&hsspi->spi->dev, "HSSPI started\n"); +} + +void hsspi_stop(struct hsspi *hsspi) +{ + DECLARE_COMPLETION_ONSTACK(complete); + struct hsspi_work complete_work = { + .type = HSSPI_WORK_COMPLETION, + .completion = &complete, + }; + + spin_lock(&hsspi->lock); + + hsspi->state = HSSPI_STOPPED; + + list_add_tail(&complete_work.list, &hsspi->work_list); + + spin_unlock(&hsspi->lock); + + wake_up_interruptible(&hsspi->wq); + + wait_for_completion(&complete); + + dev_dbg(&hsspi->spi->dev, "HSSPI stopped\n"); +} diff --git a/drivers/uwb/qm35/hsspi.h b/drivers/uwb/qm35/hsspi.h new file mode 100644 index 000000000000..784b44dbd70a --- /dev/null +++ b/drivers/uwb/qm35/hsspi.h @@ -0,0 +1,329 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2021 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 HSSPI Protocol + */ + +#ifndef __HSSPI_H__ +#define __HSSPI_H__ + +#include +#include +#include +#include +#include +#include + +enum { + UL_RESERVED, + UL_BOOT_FLASH, + UL_UCI_APP, + UL_COREDUMP, + UL_LOG, + UL_TEST_HSSPI, + UL_MAX_IDX +}; + +struct stc_header { + u8 flags; + u8 ul; + u16 length; +} __packed; + +enum hsspi_work_type { + HSSPI_WORK_TX = 0, + HSSPI_WORK_COMPLETION, +}; + +/** + * struct hsspi_block - Memory block used by the HSSPI. + * @data: pointer to some memory + * @length: requested length of the data + * @size: size of the data (could be greater than length) + * + * This structure represents the memory used by the HSSPI driver for + * sending or receiving message. Upper layer must provides the HSSPI + * driver a way to allocate such structure for reception and uses this + * structure for sending function. + * + * The goal here is to prevent useless copy. + */ +struct hsspi_block { + void *data; + u16 length; + u16 size; +}; + +struct hsspi_layer; + +/** + * struct hsspi_layer_ops - Upper layer operations. + * + * @registered: Called when this upper layer is registered. + * @unregistered: Called when unregistered. + * + * @get: Called when the HSSPI driver need some memory for + * reception. This &struct hsspi_block will be give back to the upper + * layer in the received callback. + * + * @received: Called when the HSSPI driver received some data for this + * upper layer. In case of error, status is used to notify the upper + * layer. + * + * @sent: Called when a &struct hsspi_block is sent by the HSSPI + * driver. In case of error, status is used to notify the upper layer. + * + * Operation needed to be implemented by an upper layer. All ops are + * called by the HSSPI driver and are mandatory. + */ +struct hsspi_layer_ops { + int (*registered)(struct hsspi_layer *upper_layer); + void (*unregistered)(struct hsspi_layer *upper_layer); + + struct hsspi_block *(*get)(struct hsspi_layer *upper_layer, u16 length); + void (*received)(struct hsspi_layer *upper_layer, + struct hsspi_block *blk, int status); + void (*sent)(struct hsspi_layer *upper_layer, struct hsspi_block *blk, + int status); +}; + +/** + * struct hsspi_layer - HSSPI upper layer. + * @name: Name of this upper layer. + * @id: id (ul used in the STC header) of this upper layer + * @ops: &struct hsspi_layer_ops + * + * Basic upper layer structure. Inherit from it to implement a + * concrete upper layer. + */ +struct hsspi_layer { + char *name; + u8 id; + const struct hsspi_layer_ops *ops; +}; + +enum hsspi_flags { + HSSPI_FLAGS_SS_IRQ = 0, + HSSPI_FLAGS_SS_READY = 1, + HSSPI_FLAGS_SS_BUSY = 2, + HSSPI_FLAGS_MAX = 3, +}; + +enum hsspi_state { + HSSPI_RUNNING = 0, + HSSPI_ERROR = 1, + HSSPI_STOPPED = 2, +}; + +/** + * struct hsspi - HSSPI driver. + * + * Some things need to be refine: + * 1. a better way to disable/enable ss_irq or ss_ready GPIOs + * 2. be able to change spi speed on the fly (for flashing purpose) + * + * Actually this structure should be abstract. + * + */ +struct hsspi { + spinlock_t lock; /* protect work_list, layers and state */ + struct list_head work_list; + struct hsspi_layer *layers[UL_MAX_IDX]; + enum hsspi_state state; + + DECLARE_BITMAP(flags, HSSPI_FLAGS_MAX); + struct wait_queue_head wq; + struct wait_queue_head wq_ready; + struct task_struct *thread; + + // re-enable SS_IRQ + void (*odw_cleared)(struct hsspi *hsspi); + + // wakeup QM35 + void (*wakeup)(struct hsspi *hsspi); + + // reset QM35 + void (*reset_qm35)(struct hsspi *hsspi); + + struct spi_device *spi; + + struct stc_header *host, *soc; + ktime_t next_cs_active_time; + + struct gpio_desc *gpio_ss_rdy; + struct gpio_desc *gpio_exton; + + volatile bool waiting_ss_rdy; +}; + +/** + * hsspi_init() - Initialiaze the HSSPI + * @hsspi: pointer to a &struct hsspi + * @spi: pointer to the &struct spi_device used by the HSSPI for SPI + * transmission + * + * Initialize the HSSPI structure. + * + * Return: 0 if no error or -errno. + * + */ +int hsspi_init(struct hsspi *hsspi, struct spi_device *spi); +void hsspi_set_gpios(struct hsspi *hsspi, struct gpio_desc *gpio_ss_rdy, + struct gpio_desc *gpio_exton); + +/** + * hsspi_deinit() - Initialiaze the HSSPI + * @hsspi: pointer to a &struct hsspi + * + * Deinitialize the HSSPI structure. + * + * Return: 0 if no error or -errno + */ +int hsspi_deinit(struct hsspi *hsspi); + +/** + * hsspi_register() - Register an upper layer + * @hsspi: pointer to a &struct hsspi + * @layer: pointer to a &struct hsspi_layer + * + * Register an upper layer. + * + * Return: 0 if no error or -errno. + * + */ +int hsspi_register(struct hsspi *hsspi, struct hsspi_layer *layer); + +/** + * hsspi_unregister() - Unregister an upper layer + * @hsspi: pointer to a &struct hsspi + * @layer: pointer to a &struct hsspi_layer + * + * Unregister an upper layer. + * + * Return: 0 if no error or -errno. + * + */ +int hsspi_unregister(struct hsspi *hsspi, struct hsspi_layer *layer); + +/** + * hsspi_set_spi_slave_ready() - tell the hsspi that the ss_ready is active + * @hsspi: pointer to a &struct hsspi + * + * This function is called in the ss_ready irq handler. It notices the + * HSSPI driver that the QM is ready for transfer. + */ +void hsspi_set_spi_slave_ready(struct hsspi *hsspi); + +/** + * hsspi_clear_spi_slave_ready() - tell the hsspi that the ss_ready has + * been lowered meaning that the fw is busy or asleep, + * @hsspi: pointer to a &struct hsspi + */ +void hsspi_clear_spi_slave_ready(struct hsspi *hsspi); + +/** + * hsspi_set_spi_slave_busy() - tell the hsspi that the ss_ready has + * not been lowered and raised again meaning that the fw is busy, + * @hsspi: pointer to a &struct hsspi + */ +void hsspi_set_spi_slave_busy(struct hsspi *hsspi); + +/** + * hsspi_clear_spi_slave_busy() - tell the hsspi that the ss_ready has + * been lowered and raised again meaning that the fw is not busy anymore, + * @hsspi: pointer to a &struct hsspi + * + * This function is called in the ss_ready irq handler. It notices the + * HSSPI driver that the QM has acknowledged the last SPI xfer. + */ +void hsspi_clear_spi_slave_busy(struct hsspi *hsspi); + +/** + * hsspi_set_output_data_waiting() - tell the hsspi that the ss_irq is active + * @hsspi: pointer to a &struct hsspi + * + * This function is called in the ss_irq irq handler. It notices the + * HSSPI dirver that the QM has some date to outpput. + * + * The HSSPI must work with or without the ss_irq gpio. The current + * implementation is far from ideal regarding this requirement. + * + * W/o the gpio we should send repeatly some 0-length write transfer + * in order to check for ODW flag in SOC STC header. + */ +void hsspi_set_output_data_waiting(struct hsspi *hsspi); + +/** + * hsspi_init_block() - allocate a block data that suits HSSPI. + * + * @blk: point to a &struct hsspi_block + * @length: block length + * + * Return: 0 or -ENOMEM on error + */ +int hsspi_init_block(struct hsspi_block *blk, u16 length); + +/** + * hsspi_deinit_block() - deallocate a block data. + * + * @blk: point to a &struct hsspi_block + * + */ +void hsspi_deinit_block(struct hsspi_block *blk); + +/** + * hsspi_send() - send a &struct hsspi_block for a &struct hsspi_layer + * layer. + * + * @hsspi: pointer to a &struct hsspi + * @layer: pointer to a &struct hsspi_layer + * @blk: pointer to a &struct hsspi_block + * + * Send the block `blk` of the upper layer `layer` on the `hsspi` + * driver. + * + * Return: 0 if no error or -errno. + * + */ +int hsspi_send(struct hsspi *hsspi, struct hsspi_layer *layer, + struct hsspi_block *blk); + +/** + * hsspi_start() - start the HSSPI + * + * @hsspi: pointer to a &struct hsspi + * + */ +void hsspi_start(struct hsspi *hsspi); + +/** + * hsspi_stop() - stop the HSSPI + * + * @hsspi: pointer to a &struct hsspi + * + */ +void hsspi_stop(struct hsspi *hsspi); + +#endif // __HSSPI_H__ diff --git a/drivers/uwb/qm35/hsspi_coredump.c b/drivers/uwb/qm35/hsspi_coredump.c new file mode 100644 index 000000000000..d8250cd119ec --- /dev/null +++ b/drivers/uwb/qm35/hsspi_coredump.c @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 COREDUMP layer HSSPI Protocol + */ + +#include "qm35.h" +#include "hsspi_coredump.h" + +#define COREDUMP_HEADER_NTF 0x00 +#define COREDUMP_BODY_NTF 0x01 +#define COREDUMP_RCV_STATUS 0x02 +#define COREDUMP_FORCE_CMD 0x03 + +#define COREDUMP_RCV_NACK 0x00 +#define COREDUMP_RCV_ACK 0x01 + +#define COREDUMP_RCV_TIMER_TIMEOUT_S 2 + +struct __packed coredump_common_hdr { + uint8_t cmd_id; +}; + +struct __packed coredump_hdr_ntf { + uint32_t size; + uint16_t crc; +}; + +struct __packed coredump_rcv_status { + uint8_t ack; +}; + +struct coredump_packet *coredump_packet_alloc(u16 length) +{ + struct coredump_packet *p; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return NULL; + + if (hsspi_init_block(&p->blk, length)) { + kfree(p); + return NULL; + } + return p; +} + +void coredump_packet_free(struct coredump_packet *p) +{ + hsspi_deinit_block(&p->blk); + kfree(p); +} + +static int coredump_send_rcv_status(struct coredump_layer *layer, uint8_t ack) +{ + struct coredump_packet *p; + struct coredump_common_hdr hdr; + struct coredump_rcv_status rcv; + struct qm35_ctx *qm35_hdl; + + pr_info("qm35: coredump: sending status %s\n", + layer->coredump_status == COREDUMP_RCV_ACK ? "ACK" : "NACK"); + + qm35_hdl = container_of(layer, struct qm35_ctx, coredump_layer); + + p = coredump_packet_alloc(sizeof(hdr) + sizeof(rcv)); + if (!p) + return -ENOMEM; + + hdr.cmd_id = COREDUMP_RCV_STATUS; + rcv.ack = ack; + + memcpy(p->blk.data, &hdr, sizeof(hdr)); + memcpy(p->blk.data + sizeof(hdr), &rcv, sizeof(rcv)); + + return hsspi_send(&qm35_hdl->hsspi, &qm35_hdl->coredump_layer.hlayer, + &p->blk); +} + +static uint16_t coredump_get_checksum(struct coredump_layer *layer) +{ + uint32_t idx; + uint16_t crc = 0; + + for (idx = 0; idx < layer->coredump_data_wr_idx; idx++) { + uint8_t *val; + + val = layer->coredump_data + idx; + crc += *val; + } + + return crc; +} + +static void coredump_on_expired_timer(struct timer_list *timer) +{ + struct coredump_layer *layer = + container_of(timer, struct coredump_layer, timer); + + pr_warn("qm35: coredump receive timer expired\n"); + + coredump_send_rcv_status(layer, layer->coredump_status); +} + +static int coredump_registered(struct hsspi_layer *hlayer) +{ + return 0; +} + +static void coredump_unregistered(struct hsspi_layer *hlayer) +{ + ; +} + +static struct hsspi_block *coredump_get(struct hsspi_layer *hlayer, u16 length) +{ + struct coredump_packet *p; + + p = coredump_packet_alloc(length); + if (!p) + return NULL; + + return &p->blk; +} + +static void coredump_header_ntf_received(struct coredump_layer *layer, + struct coredump_hdr_ntf chn) +{ + void *data; + + pr_info("qm35: coredump: receiving coredump with len: %d and crc: 0x%x\n", + chn.size, chn.crc); + + layer->coredump_data_wr_idx = 0; + layer->coredump_size = chn.size; + layer->coredump_crc = chn.crc; + layer->coredump_status = COREDUMP_RCV_NACK; + + data = krealloc(layer->coredump_data, layer->coredump_size, GFP_KERNEL); + if (ZERO_OR_NULL_PTR(data)) { + layer->coredump_data = NULL; + layer->coredump_size = 0; + layer->coredump_crc = 0; + pr_err("qm35: failed to allocate coredump mem: %px\n", data); + } else { + layer->coredump_data = data; + } +} + +static int coredump_body_ntf_received(struct coredump_layer *layer, + uint8_t *cch_body, uint16_t cch_body_size) +{ + if (!layer->coredump_data) { + pr_err("qm35: failed to save coredump, mem not allocated\n"); + return 1; + } + + if (cch_body_size + layer->coredump_data_wr_idx > + layer->coredump_size) { + pr_err("qm35: failed to save coredump, mem overflow: max size: %d, wr_idx: %d, cd size: %d\n", + layer->coredump_size, layer->coredump_data_wr_idx, + cch_body_size); + return 1; + } + + memcpy(layer->coredump_data + layer->coredump_data_wr_idx, cch_body, + cch_body_size); + layer->coredump_data_wr_idx += cch_body_size; + + return 0; +} + +static void coredump_received(struct hsspi_layer *hlayer, + struct hsspi_block *blk, int status) +{ + struct coredump_common_hdr cch; + struct coredump_hdr_ntf chn; + uint8_t *cch_body; + uint16_t cch_body_size; + + struct coredump_packet *packet = + container_of(blk, struct coredump_packet, blk); + + struct coredump_layer *layer = + container_of(hlayer, struct coredump_layer, hlayer); + + if (status) + goto out; + + if (blk->length < sizeof(struct coredump_common_hdr)) { + pr_err("qm35: coredump packet header too small: %d bytes\n", + blk->length); + goto out; + } + + del_timer_sync(&layer->timer); + + memcpy(&cch, blk->data, sizeof(cch)); + cch_body = blk->data + sizeof(cch); + cch_body_size = blk->length - sizeof(cch); + + switch (cch.cmd_id) { + case COREDUMP_HEADER_NTF: + if (cch_body_size < sizeof(chn)) { + pr_err("qm35: coredump packet header ntf too small: %d bytes\n", + cch_body_size); + break; + } + + memcpy(&chn, cch_body, sizeof(chn)); + coredump_header_ntf_received(layer, chn); + break; + + case COREDUMP_BODY_NTF: + pr_debug( + "qm35: coredump: saving coredump data with len: %d [%d/%d]\n", + cch_body_size, + layer->coredump_data_wr_idx + cch_body_size, + layer->coredump_size); + + if (coredump_body_ntf_received(layer, cch_body, cch_body_size)) + break; + + if (layer->coredump_data_wr_idx == layer->coredump_size) { + uint16_t crc = coredump_get_checksum(layer); + + pr_info("qm35: coredump: calculated crc: 0x%x, header crc: 0x%x\n", + crc, layer->coredump_crc); + + if (crc == layer->coredump_crc) + layer->coredump_status = COREDUMP_RCV_ACK; + + coredump_send_rcv_status(layer, layer->coredump_status); + + if (layer->coredump_status == COREDUMP_RCV_ACK) + wake_up_interruptible(&layer->wq); + break; + } + + mod_timer(&layer->timer, + jiffies + COREDUMP_RCV_TIMER_TIMEOUT_S * HZ); + + break; + + default: + pr_err("qm35: coredump: wrong cmd id received: 0x%x\n", + cch.cmd_id); + break; + } + +out: + + coredump_packet_free(packet); +} + +static void coredump_sent(struct hsspi_layer *hlayer, struct hsspi_block *blk, + int status) +{ + struct coredump_packet *buf = + container_of(blk, struct coredump_packet, blk); + + coredump_packet_free(buf); +} + +static const struct hsspi_layer_ops coredump_ops = { + .registered = coredump_registered, + .unregistered = coredump_unregistered, + .get = coredump_get, + .received = coredump_received, + .sent = coredump_sent, +}; + +void coredump_layer_init(struct coredump_layer *layer) +{ + layer->hlayer.name = "QM35 COREDUMP"; + layer->hlayer.id = UL_COREDUMP; + layer->hlayer.ops = &coredump_ops; + layer->coredump_data = NULL; + layer->coredump_data_wr_idx = 0; + layer->coredump_size = 0; + layer->coredump_crc = 0; + layer->coredump_status = 0; + init_waitqueue_head(&layer->wq); + timer_setup(&layer->timer, coredump_on_expired_timer, 0); +} + +void coredump_layer_deinit(struct coredump_layer *layer) +{ + wake_up_interruptible(&layer->wq); + kfree(layer->coredump_data); +} + +bool coredump_layer_force_coredump(struct coredump_layer *coredump) +{ + struct coredump_packet *p; + struct coredump_common_hdr hdr = { .cmd_id = COREDUMP_FORCE_CMD }; + struct qm35_ctx *qm35_hdl; + + pr_info("qm35: force coredump"); + + qm35_hdl = container_of(coredump, struct qm35_ctx, coredump_layer); + + p = coredump_packet_alloc(sizeof(hdr)); + if (!p) + return -ENOMEM; + + memcpy(p->blk.data, &hdr, sizeof(hdr)); + + return hsspi_send(&qm35_hdl->hsspi, &qm35_hdl->coredump_layer.hlayer, + &p->blk); +} + +bool coredump_layer_new_coredump_available(struct coredump_layer *coredump) +{ + return coredump->coredump_status == COREDUMP_RCV_ACK; +} + +uint8_t *coredump_layer_get_coredump(struct coredump_layer *coredump) +{ + return coredump->coredump_data; +} + +uint32_t coredump_layer_get_coredump_size(struct coredump_layer *coredump) +{ + return coredump->coredump_data_wr_idx; +} + +void coredump_layer_reset_status(struct coredump_layer *coredump) +{ + coredump->coredump_status = COREDUMP_RCV_NACK; +} diff --git a/drivers/uwb/qm35/hsspi_coredump.h b/drivers/uwb/qm35/hsspi_coredump.h new file mode 100644 index 000000000000..2c81f7d864e2 --- /dev/null +++ b/drivers/uwb/qm35/hsspi_coredump.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 COREDUMP layer HSSPI Protocol + */ + +#ifndef __HSSPI_COREDUMP_H__ +#define __HSSPI_COREDUMP_H__ + +#include +#include +#include + +#include "hsspi.h" + +struct coredump_packet { + struct hsspi_block blk; +}; + +struct coredump_layer { + struct hsspi_layer hlayer; + void *coredump_data; + uint32_t coredump_data_wr_idx; + uint32_t coredump_size; + uint16_t coredump_crc; + uint8_t coredump_status; + struct timer_list timer; + wait_queue_head_t wq; +}; + +void coredump_layer_init(struct coredump_layer *coredump); +void coredump_layer_deinit(struct coredump_layer *coredump); + +/** + * coredump_layer_force_coredump() - force coredump + * @coredump: pointer to &struct coredump_layer + * + * Return: true if ok, false otherwise. + */ +bool coredump_layer_force_coredump(struct coredump_layer *coredump); + +/** + * coredump_layer_new_coredump_available() - checks if the layer has + * ack coredump data to be read + * @coredump: pointer to &struct coredump_layer + * + * Return: true if coredump is available, false otherwise. + */ +bool coredump_layer_new_coredump_available(struct coredump_layer *coredump); + +/** + * coredump_layer_get_coredump() - get coredump + * @coredump: pointer to &struct coredump_layer + * + * Return: a &uint8_t pointing to the coredump data if succeed, NULL otherwise + */ +uint8_t *coredump_layer_get_coredump(struct coredump_layer *coredump); + +/** + * coredump_layer_get_coredump_size() - get coredump size + * @coredump: pointer to &struct coredump_layer + * + * Return: a uint32_t holding the size of the current coredump + */ +uint32_t coredump_layer_get_coredump_size(struct coredump_layer *coredump); + +/** + * coredump_layer_reset_status() - resets coredump status + * @coredump: pointer to &struct coredump_layer + * + * Return: void, + */ +void coredump_layer_reset_status(struct coredump_layer *coredump); + +#endif // __HSSPI_COREDUMP_H__ diff --git a/drivers/uwb/qm35/hsspi_log.c b/drivers/uwb/qm35/hsspi_log.c new file mode 100644 index 000000000000..d94cc2554bd4 --- /dev/null +++ b/drivers/uwb/qm35/hsspi_log.c @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 LOG layer HSSPI Protocol + */ + +#include + +#include "qm35.h" +#include "hsspi_log.h" + +#define LOG_CID_TRACE_NTF 0x0000 +#define LOG_CID_SET_LOG_LVL 0x0001 +#define LOG_CID_GET_LOG_LVL 0x0002 +#define LOG_CID_GET_LOG_SRC 0x0003 + +#define TRACE_RB_SIZE 0xFFFFF // 1MB + +struct __packed log_packet_hdr { + uint16_t cmd_id; + uint16_t b_size; +}; + +struct __packed log_level_set_cmd { + struct log_packet_hdr hdr; + uint8_t id; + uint8_t lvl; +}; + +struct __packed log_level_get_cmd { + struct log_packet_hdr hdr; + uint8_t id; +}; + +struct log_packet *log_packet_alloc(u16 length) +{ + struct log_packet *p; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return NULL; + + if (hsspi_init_block(&p->blk, length)) { + kfree(p); + return NULL; + } + return p; +} + +void log_packet_free(struct log_packet *p) +{ + hsspi_deinit_block(&p->blk); + kfree(p); +} + +static struct log_packet *encode_get_log_sources_packet(void) +{ + struct log_packet *p; + struct log_packet_hdr msg; + + p = log_packet_alloc(sizeof(msg)); + if (!p) + return p; + + msg.cmd_id = LOG_CID_GET_LOG_SRC; + msg.b_size = 0; + + memcpy(p->blk.data, &msg, sizeof(msg)); + + return p; +} + +static struct log_packet *encode_set_log_level_packet(uint8_t id, uint8_t lvl) +{ + struct log_packet *p; + struct log_level_set_cmd cmd; + + p = log_packet_alloc(sizeof(cmd)); + if (!p) + return p; + + cmd.hdr.cmd_id = LOG_CID_SET_LOG_LVL; + cmd.hdr.b_size = sizeof(cmd) - sizeof(cmd.hdr); + cmd.id = id; + cmd.lvl = lvl; + + memcpy(p->blk.data, &cmd, sizeof(cmd)); + + return p; +} + +static struct log_packet *encode_get_log_level_packet(uint8_t id) +{ + struct log_packet *p; + struct log_level_get_cmd cmd; + + p = log_packet_alloc(sizeof(cmd)); + if (!p) + return p; + + cmd.hdr.cmd_id = LOG_CID_GET_LOG_LVL; + cmd.hdr.b_size = sizeof(cmd) - sizeof(cmd.hdr); + cmd.id = id; + + memcpy(p->blk.data, &cmd, sizeof(cmd)); + + return p; +} + +static int parse_log_sources_response(struct log_layer *layer, uint8_t *data, + uint16_t data_size) +{ + struct qm35_ctx *qm35_hdl; + int idx = 0; + uint16_t current_len = 0; + + qm35_hdl = container_of(layer, struct qm35_ctx, log_layer); + + layer->log_modules_count = *data++; + + kfree(layer->log_modules); + + layer->log_modules = kcalloc(layer->log_modules_count, + sizeof(struct log_module), GFP_KERNEL); + if (!layer->log_modules) + return 1; + + for (idx = 0; idx < layer->log_modules_count; idx++) { + layer->log_modules[idx].debug = &qm35_hdl->debug; + layer->log_modules[idx].id = *data++; + layer->log_modules[idx].lvl = *data++; + current_len = strlen((char *)data) + 1; + if (current_len > sizeof(layer->log_modules[idx].name)) { + pr_err("qm35: log module name bigger than allocated buffer: current_len = %d bytes\n", + current_len); + strncpy(layer->log_modules[idx].name, "ERROR", 6); + } else + strncpy(layer->log_modules[idx].name, (char *)data, + current_len); + data += current_len; + + debug_create_module_entry(&qm35_hdl->debug, + &layer->log_modules[idx]); + } + + return 0; +} + +static int parse_get_log_lvl_response(struct log_layer *layer, uint8_t *data, + uint16_t data_size) +{ + uint8_t src_id, src_lvl; + int idx = 0; + + src_id = *data++; + src_lvl = *data; + + for (idx = 0; idx < layer->log_modules_count; idx++) { + if (layer->log_modules[idx].id == src_id) { + layer->log_modules[idx].lvl = src_lvl; + + if (layer->log_modules[idx].read_done) + complete(layer->log_modules[idx].read_done); + } + } + + return 0; +} + +static int log_registered(struct hsspi_layer *hlayer) +{ + return 0; +} + +static void log_unregistered(struct hsspi_layer *hlayer) +{ + ; +} + +static struct hsspi_block *log_get(struct hsspi_layer *hlayer, u16 length) +{ + struct log_packet *p; + + p = log_packet_alloc(length); + if (!p) + return NULL; + + return &p->blk; +} + +static void log_received(struct hsspi_layer *hlayer, struct hsspi_block *blk, + int status) +{ + struct log_layer *layer; + struct log_packet_hdr hdr; + uint8_t *body; + struct qm35_ctx *qm35_hdl; + struct log_packet *p; + + p = container_of(blk, struct log_packet, blk); + + if (status) + goto out; + + layer = container_of(hlayer, struct log_layer, hlayer); + qm35_hdl = container_of(layer, struct qm35_ctx, log_layer); + + if (blk->length < sizeof(struct log_packet_hdr)) { + pr_err("qm35: log packet header too small: %d bytes\n", + blk->length); + goto out; + } + + memcpy(&hdr, blk->data, sizeof(struct log_packet_hdr)); + body = blk->data + sizeof(struct log_packet_hdr); + + if (blk->length < sizeof(struct log_packet_hdr) + hdr.b_size) { + pr_err("qm35: incomplete log packet: %d/%d bytes\n", + blk->length, hdr.b_size); + goto out; + } + if (qm35_hdl->log_qm_traces) + pr_info("qm35_log: %.*s\n", hdr.b_size - 2, body); + + switch (hdr.cmd_id) { + case LOG_CID_TRACE_NTF: + rb_push(&layer->rb, (const char *)body, hdr.b_size); + debug_new_trace_available(&qm35_hdl->debug); + break; + case LOG_CID_SET_LOG_LVL: + break; + case LOG_CID_GET_LOG_LVL: + parse_get_log_lvl_response(layer, body, hdr.b_size); + break; + case LOG_CID_GET_LOG_SRC: + parse_log_sources_response(layer, body, hdr.b_size); + break; + default: + break; + } +out: + log_packet_free(p); +} + +static void log_sent(struct hsspi_layer *hlayer, struct hsspi_block *blk, + int status) +{ + struct log_packet *p = container_of(blk, struct log_packet, blk); + + log_packet_free(p); +} + +static const struct hsspi_layer_ops log_ops = { + .registered = log_registered, + .unregistered = log_unregistered, + .get = log_get, + .received = log_received, + .sent = log_sent, +}; + +static void log_enable_set(struct debug *dbg, int enable) +{ + struct qm35_ctx *qm35_hdl; + struct log_packet *p; + int ret; + + // TODO: what happens if enable is false? + // if (!enable) + // return; + qm35_hdl = container_of(dbg, struct qm35_ctx, debug); + + if (qm35_hdl->log_layer.enabled) { + pr_warn("qm35: logging already enabled\n"); + return; + } + + p = encode_get_log_sources_packet(); + if (!p) { + pr_err("failed to encode get log sources packet\n"); + return; + } + + ret = hsspi_send(&qm35_hdl->hsspi, &qm35_hdl->log_layer.hlayer, + &p->blk); + + if (ret) { + pr_err("failed to send spi packet\n"); + log_packet_free(p); + } else { + qm35_hdl->log_layer.enabled = enable; + } +} + +static int log_enable_get(struct debug *dbg) +{ + struct qm35_ctx *qm35_hdl; + + qm35_hdl = container_of(dbg, struct qm35_ctx, debug); + + return qm35_hdl->log_layer.enabled; +} + +static void log_level_set(struct debug *dbg, struct log_module *log_module, + int lvl) +{ + struct qm35_ctx *qm35_hdl; + struct log_packet *p; + int ret; + + qm35_hdl = container_of(dbg, struct qm35_ctx, debug); + + p = encode_set_log_level_packet(log_module->id, lvl); + if (!p) { + pr_err("failed to encode set log level packet\n"); + return; + } + + ret = hsspi_send(&qm35_hdl->hsspi, &qm35_hdl->log_layer.hlayer, + &p->blk); + if (ret) { + pr_err("failed to send spi packet\n"); + log_packet_free(p); + } +} + +static int log_level_get(struct debug *dbg, struct log_module *log_module) +{ + struct qm35_ctx *qm35_hdl; + struct log_packet *p; + int ret = 0; + DECLARE_COMPLETION_ONSTACK(comp); + + qm35_hdl = container_of(dbg, struct qm35_ctx, debug); + + p = encode_get_log_level_packet(log_module->id); + if (!p) { + pr_err("failed to encode get log level packet\n"); + return 0; + } + + log_module->read_done = ∁ + + ret = hsspi_send(&qm35_hdl->hsspi, &qm35_hdl->log_layer.hlayer, + &p->blk); + if (ret) { + pr_err("failed to send spi packet\n"); + log_packet_free(p); + return 0; + } + + wait_for_completion(log_module->read_done); + + return log_module->lvl; +} + +static char *log_trace_get_next(struct debug *dbg, rb_entry_size_t *len) +{ + struct qm35_ctx *qm35_hdl; + + qm35_hdl = container_of(dbg, struct qm35_ctx, debug); + + return rb_pop(&qm35_hdl->log_layer.rb, len); +} + +static rb_entry_size_t log_trace_get_next_size(struct debug *dbg) +{ + struct qm35_ctx *qm35_hdl; + + qm35_hdl = container_of(dbg, struct qm35_ctx, debug); + + return rb_next_size(&qm35_hdl->log_layer.rb); +} + +static bool log_trace_next_avail(struct debug *dbg) +{ + struct qm35_ctx *qm35_hdl; + + qm35_hdl = container_of(dbg, struct qm35_ctx, debug); + + return rb_can_pop(&qm35_hdl->log_layer.rb); +} + +static void log_trace_reset(struct debug *dbg) +{ + struct qm35_ctx *qm35_hdl; + + qm35_hdl = container_of(dbg, struct qm35_ctx, debug); + + rb_reset(&qm35_hdl->log_layer.rb); +} + +static int get_dev_id(struct debug *dbg, uint16_t *dev_id) +{ + struct qm35_ctx *qm35_hdl; + + qm35_hdl = container_of(dbg, struct qm35_ctx, debug); + + return qm_get_dev_id(qm35_hdl, dev_id); +} + +static int get_soc_id(struct debug *dbg, uint8_t *soc_id) +{ + struct qm35_ctx *qm35_hdl; + + qm35_hdl = container_of(dbg, struct qm35_ctx, debug); + + return qm_get_soc_id(qm35_hdl, soc_id); +} + +static const struct debug_trace_ops debug_trace_ops = { + .enable_set = log_enable_set, + .enable_get = log_enable_get, + .level_set = log_level_set, + .level_get = log_level_get, + .trace_get_next = log_trace_get_next, + .trace_get_next_size = log_trace_get_next_size, + .trace_next_avail = log_trace_next_avail, + .trace_reset = log_trace_reset, + .get_dev_id = get_dev_id, + .get_soc_id = get_soc_id, +}; + +int log_layer_init(struct log_layer *log, struct debug *debug) +{ + int ret; + + ret = rb_init(&log->rb, TRACE_RB_SIZE); + if (ret) + return ret; + + log->hlayer.name = "QM35 LOG"; + log->hlayer.id = UL_LOG; + log->hlayer.ops = &log_ops; + log->enabled = false; + log->log_modules = NULL; + log->log_modules_count = 0; + + debug->trace_ops = &debug_trace_ops; + + return 0; +} + +void log_layer_deinit(struct log_layer *log) +{ + kfree(log->log_modules); + rb_deinit(&log->rb); +} diff --git a/drivers/uwb/qm35/hsspi_log.h b/drivers/uwb/qm35/hsspi_log.h new file mode 100644 index 000000000000..e5e11ad98fcf --- /dev/null +++ b/drivers/uwb/qm35/hsspi_log.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 LOG layer HSSPI Protocol + */ + +#ifndef __HSSPI_LOG_H__ +#define __HSSPI_LOG_H__ + +#include "hsspi.h" +#include "debug.h" +#include "qm35_rb.h" + +struct log_packet { + struct hsspi_block blk; + struct completion *write_done; +}; + +struct log_layer { + struct hsspi_layer hlayer; + uint8_t log_modules_count; + struct log_module *log_modules; + struct rb rb; + bool enabled; +}; + +/** + * log_packet_alloc() - Allocate an LOG packet + * @length: length of the LOG packet + * + * Allocate an LOG packet that can be used by the HSSPI driver in + * order to send or receive an LOG packet. + * + * Return: a newly allocated &struct log_packet or NULL + */ +struct log_packet *log_packet_alloc(u16 length); + +/** + * log_packet_free() - Free an LOG packet + * @p: pointer to the &struct log_packet to free + * + */ +void log_packet_free(struct log_packet *p); + +int log_layer_init(struct log_layer *log, struct debug *debug); +void log_layer_deinit(struct log_layer *log); + +#endif // __HSSPI_LOG_H__ diff --git a/drivers/uwb/qm35/hsspi_test.c b/drivers/uwb/qm35/hsspi_test.c new file mode 100644 index 000000000000..2ac5e21ba74a --- /dev/null +++ b/drivers/uwb/qm35/hsspi_test.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2021 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 UCI layer HSSPI Protocol + */ + +#include "hsspi_test.h" +#include "hsspi_uci.h" +#include +#include + +int hsspi_test_registered(struct hsspi_layer *upper_layer); +void hsspi_test_unregistered(struct hsspi_layer *upper_layer); +struct hsspi_block *hsspi_test_get(struct hsspi_layer *upper_layer, u16 length); +void hsspi_test_received(struct hsspi_layer *upper_layer, + struct hsspi_block *blk, int status); +void hsspi_test_sent(struct hsspi_layer *upper_layer, struct hsspi_block *blk, + int status); + +struct hsspi_layer_ops test_hsspi_layer_ops = { + .registered = hsspi_test_registered, + .unregistered = hsspi_test_unregistered, + .get = hsspi_test_get, + .received = hsspi_test_received, + .sent = hsspi_test_sent, +}; + +struct hsspi_layer test_hsspi_layer = { + .name = "hsspi_test_layer", + .id = UL_TEST_HSSPI, + .ops = &test_hsspi_layer_ops, +}; + +static struct hsspi *ghsspi; +int sleep_inter_frame_ms; +extern int test_sleep_after_ss_ready_us; + +int hsspi_test_init(struct hsspi *hsspi) +{ + ghsspi = hsspi; + return hsspi_register(hsspi, &test_hsspi_layer); +} + +void hsspi_test_deinit(struct hsspi *hsspi) +{ + hsspi_unregister(hsspi, &test_hsspi_layer); +} + +int hsspi_test_registered(struct hsspi_layer *upper_layer) +{ + return 0; +} + +void hsspi_test_unregistered(struct hsspi_layer *upper_layer) +{ +} + +static int check_rx(const u8 *rx, int len) +{ + int idx = 0, err = 0; + for (; idx < len; idx++) { + if (rx[idx] != (idx & 0xff)) { + pr_err("hsspi test: check_rx rx[%u] != %u\n", idx, + rx[idx]); + print_hex_dump(KERN_DEBUG, "rx:", DUMP_PREFIX_ADDRESS, + 16, 1, rx, len, false); + err = -5963; + break; + } + } + return err; +} + +struct hsspi_block *hsspi_test_get(struct hsspi_layer *layer, u16 length) +{ + struct hsspi_block *blk = kzalloc(sizeof(*blk) + length, GFP_KERNEL); + if (blk) { + blk->data = blk + 1; + blk->size = length; + blk->length = length; + } + return blk; +} + +void hsspi_test_set_inter_frame_ms(int ms) +{ + sleep_inter_frame_ms = ms; +} + +void hsspi_test_received(struct hsspi_layer *layer, struct hsspi_block *blk, + int status) +{ + static uint64_t bytes, msgs, errors, bytes0, msgs0, errors0; + static time64_t last_perf_dump; + uint32_t rem; + int error = check_rx(blk->data, blk->length) ? 1 : 0; + time64_t now; + errors += error; + + if (!last_perf_dump) { + last_perf_dump = ktime_get_seconds(); + } + now = ktime_get_seconds(); + + /* inject latencies between each message and between the check + * of ss-ready and the xfer. + * The test is expected to fail if + * sleep_inter_frame_ms > CONFIG_PM_RET_SLEEP_DELAY_US + */ + if (sleep_inter_frame_ms > 0) { + static int delay_us = 0; + test_sleep_after_ss_ready_us = + sleep_inter_frame_ms * 1000 - delay_us; + usleep_range(delay_us, delay_us + 1); + delay_us += 100; + if (delay_us > sleep_inter_frame_ms * 1000) + delay_us = 0; + } else { + test_sleep_after_ss_ready_us = 0; + } + bytes += blk->length; + msgs++; + error |= hsspi_send(ghsspi, layer, blk); + div_u64_rem(msgs, 100, &rem); + if (error || (rem == 0)) + pr_info("hsspi test: bytes received %llu, msgs %llu, errors %llu\n", + bytes, msgs, errors); + if (now > last_perf_dump) { + uint64_t dbytes = bytes >= bytes0 ? bytes - bytes0 : + ~0ULL - bytes0 + bytes; + uint64_t dmsgs = msgs >= msgs0 ? msgs - msgs0 : + ~0ULL - msgs0 + msgs; + uint64_t derrors = errors >= errors0 ? errors - errors0 : + ~0ULL - errors0 + errors; + pr_info("hsspi test perfs: %llu B/s, %llu msgs/s, %llu errors/s\n", + div_u64(dbytes, (now - last_perf_dump)), + div_u64(dmsgs, (now - last_perf_dump)), + div_u64(derrors, (now - last_perf_dump))); + bytes0 = bytes; + msgs0 = msgs; + errors0 = errors; + last_perf_dump = now; + } +} + +void hsspi_test_sent(struct hsspi_layer *layer, struct hsspi_block *blk, + int status) +{ + static uint64_t bytes, msgs, errors; + uint32_t rem; + errors += status ? 1 : 0; + msgs++; + bytes += blk->length; + div_u64_rem(msgs, 100, &rem); + if (status || (rem == 0)) + pr_info("hsspi test: bytes sent %llu, msgs %llu, errors %llu\n", + bytes, msgs, errors); + kfree(blk); +} diff --git a/drivers/uwb/qm35/hsspi_test.h b/drivers/uwb/qm35/hsspi_test.h new file mode 100644 index 000000000000..3a8eefcdb97c --- /dev/null +++ b/drivers/uwb/qm35/hsspi_test.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 Ringbuffer + */ + +#ifndef __HSSPI_TEST_H___ +#define __HSSPI_TEST_H___ + +#include "hsspi.h" + +int hsspi_test_init(struct hsspi *hsspi); +void hsspi_test_deinit(struct hsspi *hsspi); + +void hsspi_test_set_inter_frame_ms(int ms); + +#endif /* __HSSPI_TEST_H___ */ \ No newline at end of file diff --git a/drivers/uwb/qm35/hsspi_uci.c b/drivers/uwb/qm35/hsspi_uci.c new file mode 100644 index 000000000000..0d8559103246 --- /dev/null +++ b/drivers/uwb/qm35/hsspi_uci.c @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2021 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 UCI layer HSSPI Protocol + */ + +#include "qm35.h" +#include "hsspi_uci.h" + +struct uci_packet *uci_packet_alloc(u16 length) +{ + struct uci_packet *p; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return NULL; + + if (hsspi_init_block(&p->blk, length)) { + kfree(p); + return NULL; + } + + p->data = p->blk.data; + p->length = p->blk.length; + return p; +} + +void uci_packet_free(struct uci_packet *p) +{ + hsspi_deinit_block(&p->blk); + kfree(p); +} + +static int uci_registered(struct hsspi_layer *layer) +{ + return 0; +} + +static void clear_rx_list(struct uci_layer *uci) +{ + struct uci_packet *p; + + mutex_lock(&uci->lock); + + while (!list_empty(&uci->rx_list)) { + p = list_first_entry(&uci->rx_list, struct uci_packet, list); + + list_del(&p->list); + + uci_packet_free(p); + } + + mutex_unlock(&uci->lock); + + wake_up_interruptible(&uci->wq); +} + +static void uci_unregistered(struct hsspi_layer *hlayer) +{ + struct uci_layer *uci = container_of(hlayer, struct uci_layer, hlayer); + + clear_rx_list(uci); +} + +static struct hsspi_block *uci_get(struct hsspi_layer *hlayer, u16 length) +{ + struct uci_packet *p; + + p = uci_packet_alloc(length); + if (!p) + return NULL; + + return &p->blk; +} + +static void uci_sent(struct hsspi_layer *hlayer, struct hsspi_block *blk, + int status) +{ + struct uci_packet *p = container_of(blk, struct uci_packet, blk); + + p->status = status; + complete(p->write_done); +} + +#define UCI_CONTROL_PACKET_PAYLOAD_SIZE_LOCATION (3) + +static size_t get_payload_size_from_header(const u8 *header) +{ + bool is_data_packet = ((header[0] >> 5) & 0x07) == 0; + bool is_control_packet = !is_data_packet && ((header[0] >> 7) == 0); + + if (is_control_packet) + return header[UCI_CONTROL_PACKET_PAYLOAD_SIZE_LOCATION]; + + return (header[3] << 8) | header[2]; +} + +#define UCI_PACKET_HEADER_SIZE (4) + +static void uci_received(struct hsspi_layer *hlayer, struct hsspi_block *blk, + int status) +{ + struct uci_layer *uci = container_of(hlayer, struct uci_layer, hlayer); + struct uci_packet *p = container_of(blk, struct uci_packet, blk); + + if (status) + uci_packet_free(p); + else { + struct uci_packet *next; + size_t readn = 0; + size_t payload_size; + + while (1) { + if (blk->length - readn < UCI_PACKET_HEADER_SIZE) + // Incomplete UCI header + break; + + payload_size = get_payload_size_from_header( + (u8 *)blk->data + readn); + + if (blk->length - readn <= + UCI_PACKET_HEADER_SIZE + payload_size) + // blk contains no additional packet + break; + + next = kzalloc(sizeof(*next), GFP_KERNEL); + if (!next) + break; + + next->data = p->blk.data + readn; + next->length = UCI_PACKET_HEADER_SIZE + payload_size; + + readn += next->length; + + mutex_lock(&uci->lock); + list_add_tail(&next->list, &uci->rx_list); + mutex_unlock(&uci->lock); + } + + p->data = p->blk.data + readn; + p->length = p->blk.length - readn; + + mutex_lock(&uci->lock); + list_add_tail(&p->list, &uci->rx_list); + mutex_unlock(&uci->lock); + + wake_up_interruptible(&uci->wq); + } +} + +static const struct hsspi_layer_ops uci_ops = { + .registered = uci_registered, + .unregistered = uci_unregistered, + .get = uci_get, + .received = uci_received, + .sent = uci_sent, +}; + +int uci_layer_init(struct uci_layer *uci) +{ + uci->hlayer.name = "UCI"; + uci->hlayer.id = UL_UCI_APP; + uci->hlayer.ops = &uci_ops; + + INIT_LIST_HEAD(&uci->rx_list); + mutex_init(&uci->lock); + init_waitqueue_head(&uci->wq); + return 0; +} + +void uci_layer_deinit(struct uci_layer *uci) +{ + clear_rx_list(uci); +} + +bool uci_layer_has_data_available(struct uci_layer *uci) +{ + bool ret; + + mutex_lock(&uci->lock); + ret = !list_empty(&uci->rx_list); + mutex_unlock(&uci->lock); + return ret; +} + +struct uci_packet *uci_layer_read(struct uci_layer *uci, size_t max_size, + bool non_blocking) +{ + struct uci_packet *p; + int ret; + + if (!non_blocking) { + ret = wait_event_interruptible( + uci->wq, uci_layer_has_data_available(uci)); + if (ret) + return ERR_PTR(ret); + } + + mutex_lock(&uci->lock); + p = list_first_entry_or_null(&uci->rx_list, struct uci_packet, list); + if (p) { + if (p->length > max_size) + p = ERR_PTR(-EMSGSIZE); + else + list_del(&p->list); + } else + p = ERR_PTR(-EAGAIN); + + mutex_unlock(&uci->lock); + return p; +} diff --git a/drivers/uwb/qm35/hsspi_uci.h b/drivers/uwb/qm35/hsspi_uci.h new file mode 100644 index 000000000000..8890424b1baa --- /dev/null +++ b/drivers/uwb/qm35/hsspi_uci.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2021 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 UCI layer HSSPI Protocol + */ + +#ifndef __HSSPI_UCI_H__ +#define __HSSPI_UCI_H__ + +#include +#include +#include +#include + +#include "hsspi.h" + +/** + * struct uci_packet - UCI packet that implements a &struct hsspi_block. + * @blk: &struct hsspi_block + * @write_done: norify when the packet has been really send + * @list: link with &struct uci_layer.rx_list + * @status: status of the transfer + */ +struct uci_packet { + struct hsspi_block blk; + struct completion *write_done; + struct list_head list; + u8 *data; + int length; + int status; +}; + +/** + * uci_packet_alloc() - Allocate an UCI packet + * @length: length of the UCI packet + * + * Allocate an UCI packet that can be used by the HSSPI driver in + * order to send or receive an UCI packet. + * + * Return: a newly allocated &struct uci_packet or NULL + */ +struct uci_packet *uci_packet_alloc(u16 length); + +/** + * uci_packet_free() - Free an UCI packet + * @p: pointer to the &struct uci_packet to free + * + */ +void uci_packet_free(struct uci_packet *p); + +/** + * struct uci_layer - Implement an HSSPI Layer + * @hlayer: &struct hsspi_layer + * @rx_list: list of received UCI packets + * @lock: protect the &struct uci_layer.rx_list + * @wq: notify when the &struct uci_layer.rx_list is not empty + */ +struct uci_layer { + struct hsspi_layer hlayer; + struct list_head rx_list; + struct mutex lock; + wait_queue_head_t wq; +}; + +/** + * uci_layer_init() - Initialize UCI Layer + * + */ +int uci_layer_init(struct uci_layer *uci); + +/** + * uci_layer_deinit() - Deinitialize UCI Layer + * + */ +void uci_layer_deinit(struct uci_layer *uci); + +/** + * uci_layer_has_data_availal() - checks if the layer has some rx packets + * @uci: pointer to &struct uci_layer + * + * Function that checks if the UCI layer has some data waiting to be read. + * + * Return: true if some data is available, false otherwise. + */ +bool uci_layer_has_data_available(struct uci_layer *uci); + +/** + * uci_layer_read() - get a packet from the rx_list + * @uci: pointer to &struct uci_layer + * @max_size: maximum size possible for the UCI packet + * @non_blocking: true if non blocking, false otherwise + * + * This function returns an UCI packet if available in the + * &struct uci_layer.rx_list. The max_size argument logic is due to the way + * the /dev/uci is make. We should ensure that we return an entire + * packet in uci_read, so we must get a packet only if the caller has + * enougth room for it. + * + * Return: a &struct uci_packet if succeed, + * -EINTR if it was interrupted (in blocking mode), + * -ESMGSIZE if the available UCI packet is bigger than max_size, + * -EAGAIN if there is no available UCI packet (in non blocking mode) + */ +struct uci_packet *uci_layer_read(struct uci_layer *uci, size_t max_size, + bool non_blocking); + +#endif // __HSSPI_UCI_H__ diff --git a/drivers/uwb/qm35/libfwupdater/src/fwupdater.c b/drivers/uwb/qm35/libfwupdater/src/fwupdater.c new file mode 100644 index 000000000000..129fe8c7a323 --- /dev/null +++ b/drivers/uwb/qm35/libfwupdater/src/fwupdater.c @@ -0,0 +1,375 @@ +// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 +/* + * Copyright 2023 Qorvo US, Inc. + * + */ + +#ifndef __KERNEL__ +#include +#endif + +#include +#include +#include +#include + +#include + +/* Extract from C0 rom code */ +#define MAX_CERTIFICATE_SIZE 0x400 +#define MAX_CHUNK_SIZE 3072 +#define WAIT_REBOOT_DELAY_MS 250 +#define WAIT_SS_RDY_CHUNK_TIMEOUT 100 +#define WAIT_SS_RDY_STATUS_TIMEOUT 10 +#define RESULT_RETRIES 3 +#define RESULT_CMD_INTERVAL_MS 50 +#define CRC_TYPE uint32_t +#define CRC_SIZE (sizeof(CRC_TYPE)) +#define TRANPORT_HEADER_SIZE (sizeof(struct stc) + CRC_SIZE) +#define EMERGENCY_SPI_FREQ 1000000 /* 1MHz */ + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +#ifndef __KERNEL__ +_Static_assert(MAX_CHUNK_SIZE >= CRYPTO_IMAGES_CERT_PKG_SIZE); +_Static_assert(TRANPORT_HEADER_SIZE + MAX_CERTIFICATE_SIZE < MAX_CHUNK_SIZE); +#endif + +/* local stats */ +static int gstats_spi_errors; +static int gstats_ss_rdy_timeouts; + +static int send_data_chunks(struct qmrom_handle *handle, char *data, + size_t size); +static int check_fw_boot(struct qmrom_handle *handle); + +int run_fwupdater(struct qmrom_handle *handle, char *fwpkg_bin, size_t size) +{ + int rc; + + gstats_spi_errors = 0; + gstats_ss_rdy_timeouts = 0; + + if (size < sizeof(struct fw_pkg_hdr_t) + + sizeof(struct fw_pkg_img_hdr_t) + + CRYPTO_IMAGES_CERT_PKG_SIZE + + CRYPTO_FIRMWARE_CHUNK_MIN_SIZE) { + LOG_ERR("%s cannot extract enough data from fw package binary\n", + __func__); + return -EINVAL; + } + + rc = send_data_chunks(handle, fwpkg_bin, size); + if (rc) { + LOG_ERR("Sending image failed with %d\n", rc); + return rc; + } + return 0; +} + +static int run_fwupdater_get_status(struct qmrom_handle *handle, + struct stc *hstc, struct stc *sstc, + struct fw_updater_status_t *status) +{ + uint32_t i = 0; + CRC_TYPE *crc = (CRC_TYPE *)(hstc + 1); + bool owa; + memset(hstc, 0, TRANPORT_HEADER_SIZE + sizeof(*status)); + + while (i++ < RESULT_RETRIES) { + // Poll the QM + sstc->all = 0; + hstc->all = 0; + *crc = 0; + qmrom_spi_transfer(handle->spi_handle, (char *)sstc, + (const char *)hstc, TRANPORT_HEADER_SIZE); + qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle, + WAIT_SS_RDY_STATUS_TIMEOUT); + sstc->all = 0; + hstc->all = 0; + hstc->host_flags.pre_read = 1; + *crc = 0; + qmrom_spi_transfer(handle->spi_handle, (char *)sstc, + (const char *)hstc, TRANPORT_HEADER_SIZE); + // LOG_INFO("Pre-Read received:\n"); + // hexdump(LOG_INFO, sstc, sizeof(sstc)); + + /* Stops the loop when QM has a result to share */ + owa = sstc->soc_flags.out_waiting; + qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle, + WAIT_SS_RDY_STATUS_TIMEOUT); + sstc->all = 0; + hstc->all = 0; + hstc->host_flags.read = 1; + hstc->len = sizeof(*status); + *crc = 0; + qmrom_spi_transfer(handle->spi_handle, (char *)sstc, + (const char *)hstc, + TRANPORT_HEADER_SIZE + sizeof(*status)); + // LOG_INFO("Read received:\n"); + // hexdump(LOG_INFO, sstc, sizeof(*hstc) + sizeof(uint32_t)); + if (owa) { + memcpy(status, sstc->payload, sizeof(*status)); + if (status->magic == FWUPDATER_STATUS_MAGIC) + break; + } + qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle, + WAIT_SS_RDY_STATUS_TIMEOUT); + // Failed to get the status, reduces the spi speed to + // an emergency speed to maximize the chance to get the + // final status + qmrom_spi_set_freq(EMERGENCY_SPI_FREQ); + gstats_spi_errors++; + } + if (status->magic != FWUPDATER_STATUS_MAGIC) { + LOG_ERR("Timedout waiting for result\n"); + return -1; + } + return 0; +} + +static CRC_TYPE checksum(const void *data, const size_t size) +{ + CRC_TYPE crc = 0; + CRC_TYPE *ptr = (CRC_TYPE *)data; + CRC_TYPE remainder = size & (CRC_SIZE - 1); + size_t idx; + + for (idx = 0; idx < size; idx += CRC_SIZE, ptr++) + crc += *ptr; + + if (remainder) { + crc += ((uint8_t *)data)[size - 1]; + if (remainder > 1) { + crc += ((uint8_t *)data)[size - 2]; + if (remainder > 2) { + crc += ((uint8_t *)data)[size - 3]; + } + } + } + return crc; +} + +static void prepare_hstc(struct stc *hstc, char *data, size_t len) +{ + CRC_TYPE *crc = (CRC_TYPE *)(hstc + 1); + void *payload = crc + 1; + + hstc->all = 0; + hstc->host_flags.write = 1; + hstc->len = len + CRC_SIZE; + *crc = checksum(data, len); +#ifdef CONFIG_INJECT_ERROR + *crc += 2; +#endif + memcpy(payload, data, len); +} + +static int xfer_payload_prep_next(struct qmrom_handle *handle, + const char *step_name, struct stc *hstc, + struct stc *sstc, struct stc *hstc_next, + char **data, size_t *size) +{ + int rc = 0, nb_retry = CONFIG_NB_RETRIES; + CRC_TYPE *crc = (CRC_TYPE *)(hstc + 1); + + do { + int ss_rdy_rc, irq_up; + sstc->all = 0; + rc = qmrom_spi_transfer(handle->spi_handle, (char *)sstc, + (const char *)hstc, + hstc->len + sizeof(struct stc)); + if (hstc_next) { + /* Don't wait idle, prepare the next hstc to be sent */ + size_t to_send = MIN(MAX_CHUNK_SIZE, *size); + prepare_hstc(hstc_next, *data, to_send); + *size -= to_send; + *data += to_send; + hstc_next = NULL; + } + ss_rdy_rc = qmrom_spi_wait_for_ready_line( + handle->ss_rdy_handle, WAIT_SS_RDY_CHUNK_TIMEOUT); + if (ss_rdy_rc) { + LOG_ERR("%s Waiting for ss-rdy failed with %d (nb_retry %d , crc 0x%x)\n", + step_name, ss_rdy_rc, nb_retry, *crc); + gstats_ss_rdy_timeouts++; + rc = -EAGAIN; + } + irq_up = qmrom_spi_read_irq_line(handle->ss_irq_handle); + if ((!rc && !sstc->soc_flags.ready) || irq_up) { + LOG_ERR("%s Retry rc %d, sstc 0x%08x, irq %d, crc %08x\n", + step_name, rc, sstc->all, irq_up, *crc); + rc = -EAGAIN; + gstats_spi_errors++; + } +#ifdef CONFIG_INJECT_ERROR + (*crc)--; +#endif + } while (rc && --nb_retry > 0); + if (rc) { + LOG_ERR("%s transfer failed with %d - (sstc 0x%08x)\n", + step_name, rc, sstc->all); + } + return rc; +} + +static int xfer_payload(struct qmrom_handle *handle, const char *step_name, + struct stc *hstc, struct stc *sstc) +{ + return xfer_payload_prep_next(handle, step_name, hstc, sstc, NULL, NULL, + NULL); +} + +static int send_data_chunks(struct qmrom_handle *handle, char *data, + size_t size) +{ + struct fw_updater_status_t status; + struct stc *hstc, *sstc, *hstc_current, *hstc_next; + uint32_t chunk_nr = 0; + char *rx, *tx; + CRC_TYPE *crc; + int rc = 0; + + LOG_DBG("chunk_nr:%u\n", chunk_nr); + + qmrom_alloc(rx, MAX_CHUNK_SIZE + TRANPORT_HEADER_SIZE); + qmrom_alloc(tx, 2 * (MAX_CHUNK_SIZE + TRANPORT_HEADER_SIZE)); + if (!rx || !tx) { + LOG_ERR("Rx/Tx buffers allocation failure\n"); + rc = -ENOMEM; + goto exit_nomem; + } + + sstc = (struct stc *)rx; + hstc = (struct stc *)tx; + hstc_current = hstc; + hstc_next = (struct stc *)&tx[MAX_CHUNK_SIZE + TRANPORT_HEADER_SIZE]; + crc = (CRC_TYPE *)(hstc + 1); + + /* wait for the QM to be ready */ + rc = qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle, + WAIT_SS_RDY_CHUNK_TIMEOUT); + if (rc) + LOG_ERR("Waiting for ss-rdy failed with %d\n", rc); + + /* Sending the fw package header */ + prepare_hstc(hstc, data, sizeof(struct fw_pkg_hdr_t)); + LOG_INFO("Sending the fw package header (%zu bytes, crc is 0x%08x)\n", + sizeof(struct fw_pkg_hdr_t), *crc); + // hexdump(LOG_INFO, hstc->payload + 4, sizeof(struct fw_pkg_hdr_t)); + rc = xfer_payload(handle, "fw package header", hstc, sstc); + if (rc) + goto exit; + /* Move the data to the next offset minus the header footprint */ + size -= sizeof(struct fw_pkg_hdr_t); + data += sizeof(struct fw_pkg_hdr_t); + + /* Sending the image header */ + prepare_hstc(hstc, data, sizeof(struct fw_pkg_img_hdr_t)); + LOG_INFO("Sending the image header (%zu bytes crc 0x%08x)\n", + sizeof(struct fw_pkg_img_hdr_t), *crc); + // hexdump(LOG_INFO, hstc->payload + 4, sizeof(struct fw_pkg_img_hdr_t)); + rc = xfer_payload(handle, "image header", hstc, sstc); + if (rc) + goto exit; + size -= sizeof(struct fw_pkg_img_hdr_t); + data += sizeof(struct fw_pkg_img_hdr_t); + + /* Sending the cert chain */ + prepare_hstc(hstc, data, CRYPTO_IMAGES_CERT_PKG_SIZE); + LOG_INFO("Sending the cert chain (%d bytes crc 0x%08x)\n", + CRYPTO_IMAGES_CERT_PKG_SIZE, *crc); + rc = xfer_payload(handle, "cert chain", hstc, sstc); + if (rc) + goto exit; + size -= CRYPTO_IMAGES_CERT_PKG_SIZE; + data += CRYPTO_IMAGES_CERT_PKG_SIZE; + + /* Sending the fw image */ + LOG_INFO("Sending the image (%zu bytes)\n", size); + LOG_DBG("Sending a chunk (%zu bytes crc 0x%08x)\n", + MIN(MAX_CHUNK_SIZE, size), *crc); + prepare_hstc(hstc_current, data, MIN(MAX_CHUNK_SIZE, size)); + size -= hstc_current->len - CRC_SIZE; + data += hstc_current->len - CRC_SIZE; + do { + rc = xfer_payload_prep_next(handle, "data chunk", hstc_current, + sstc, hstc_next, &data, &size); + if (rc) + goto exit; + chunk_nr++; + /* swap hstcs */ + hstc = hstc_current; + hstc_current = hstc_next; + hstc_next = hstc; + } while (size); + + /* Sends the last now */ + rc = xfer_payload_prep_next(handle, "data chunk", hstc_current, sstc, + NULL, NULL, NULL); + +exit: + // tries to get the flashing status anyway... + rc = run_fwupdater_get_status(handle, hstc, sstc, &status); + if (!rc) { + if (status.status) { + LOG_ERR("Flashing failed, fw updater status %#x (errors: sub %#x, crc %u, rram %u, crypto %d)\n", + status.status, status.suberror, + status.crc_errors, status.rram_errors, + status.crypto_errors); + rc = status.status; + } else { + if (gstats_ss_rdy_timeouts + gstats_spi_errors + + status.crc_errors + status.rram_errors + + status.crypto_errors) { + LOG_WARN( + "Flashing succeeded with errors (host %u, ss_rdy_timeout %u, QM %u, crc %u, rram %u, crypto %d)\n", + gstats_spi_errors, + gstats_ss_rdy_timeouts, + status.spi_errors, status.crc_errors, + status.rram_errors, + status.crypto_errors); + } else { + LOG_INFO( + "Flashing succeeded without any errors\n"); + } + + if (!handle->skip_check_fw_boot) + rc = check_fw_boot(handle); + } + } else { + LOG_ERR("run_fwupdater_get_status returned %d\n", rc); + } +exit_nomem: + if (rx) + qmrom_free(rx); + if (tx) + qmrom_free(tx); + return rc; +} + +static int check_fw_boot(struct qmrom_handle *handle) +{ + uint8_t raw_flags; + struct stc hstc, sstc; + + handle->dev_ops.reset(handle->reset_handle); + + qmrom_msleep(WAIT_REBOOT_DELAY_MS); + + // Poll the QM + sstc.all = 0; + hstc.all = 0; + while(sstc.all == 0) + qmrom_spi_transfer(handle->spi_handle, (char *)&sstc, + (const char *)&hstc, sizeof(hstc)); + + raw_flags = sstc.raw_flags; + /* The ROM code sends the same quartets for the first byte of each xfers */ + if (((raw_flags & 0xf0) >> 4) == (raw_flags & 0xf)) { + LOG_ERR("%s: firmware not properly started: %#x\n", + __func__, raw_flags); + return -2; + } + return 0; +} diff --git a/drivers/uwb/qm35/libqmrom/src/qm357xx_rom_b0.c b/drivers/uwb/qm35/libqmrom/src/qm357xx_rom_b0.c new file mode 100644 index 000000000000..558a201f82c2 --- /dev/null +++ b/drivers/uwb/qm35/libqmrom/src/qm357xx_rom_b0.c @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 +/* + * Copyright 2022 Qorvo US, Inc. + * + */ + +#include +#include +#include +#include +#include + +#define DEFAULT_SPI_CLOCKRATE 3000000 +#define CHIP_VERSION_CHIP_REV_PAYLOAD_OFFSET 1 +#define CHIP_VERSION_DEV_REV_PAYLOAD_OFFSET 3 +#define CHUNK_SIZE_B0 1008 + +enum B0_CMD { + ROM_CMD_B0_SEC_LOAD_ICV_IMG_TO_RRAM = 0x0, + ROM_CMD_B0_SEC_LOAD_OEM_IMG_TO_RRAM = 0x1, + ROM_CMD_B0_GET_CHIP_VER = 0x2, + ROM_CMD_B0_GET_SOC_INFO = 0x3, + ROM_CMD_B0_ERASE_DBG_CERT = 0x4, + ROM_CMD_B0_WRITE_DBG_CERT = 0x5, + ROM_CMD_B0_SEC_IMAGE_DATA = 0xf, + ROM_CMD_B0_CERT_DATA = 0x10, + ROM_CMD_B0_DEBUG_CERT_SIZE = 0x11, +}; + +enum B0_RESP { + READY_FOR_CS_LOW_CMD = 0x00, + WRONG_CS_LOW_CMD = 0x01, + WAITING_FOR_NS_RRAM_FILE_SIZE = 0x02, + WAITING_FOR_NS_SRAM_FILE_SIZE = 0x03, + WAITING_FOR_NS_RRAM_FILE_DATA = 0x04, + WAITING_FOR_NS_SRAM_FILE_DATA = 0x05, + WAITING_FOR_SEC_FILE_DATA = 0x06, + ERR_NS_SRAM_OR_RRAM_SIZE_CMD = 0x07, + ERR_SEC_RRAM_SIZE_CMD = 0x08, + ERR_WAITING_FOR_NS_IMAGE_DATA_CMD = 0x09, + ERR_WAITING_FOR_SEC_IMAGE_DATA_CMD = 0x0A, + ERR_IMAGE_SIZE_IS_ZERO = 0x0B, + /* Got more data than expected size */ + ERR_IMAGE_SIZE_TOO_BIG = 0x0C, + /* Image must divide in 16 without remainder */ + ERR_IMAGE_IS_NOT_16BYTES_MUL = 0x0D, + ERR_GOT_DATA_MORE_THAN_ALLOWED = 0x0E, + /* Remainder is allowed only for last packet */ + ERR_RRAM_DATA_REMAINDER_NOT_ALLOWED = 0x0F, + ERR_WAITING_FOR_CERT_DATA_CMD = 0x10, + WAITING_FOR_FIRST_KEY_CERT = 0x11, + WAITING_FOR_SECOND_KEY_CERT = 0x12, + WAITING_FOR_CONTENT_CERT = 0x13, + WAITING_FOR_DEBUG_CERT_DATA = 0x14, + ERR_FIRST_KEY_CERT_OR_FW_VER = 0x15, + ERR_SECOND_KEY_CERT = 0x16, + ERR_CONTENT_CERT_DOWNLOAD_ADDR = 0x17, + /* If the content certificate contains to much images */ + ERR_TOO_MANY_IMAGES_IN_CONTENT_CERT = 0x18, + ERR_ADDRESS_NOT_DIVIDED_BY_8 = 0x19, + ERR_IMAGE_BOUNDARIES = 0x1A, + /* Expected ICV type and got OEM */ + ERR_CERT_TYPE = 0x1B, + ERR_PRODUCT_ID = 0x1C, + ERR_RRAM_RANGE_OR_WRITE = 0x1D, + WAITING_TO_DEBUG_CERTIFICATE_SIZE = 0x1E, + ERR_DEBUG_CERT_SIZE = 0x1F, +}; + +static int qm357xx_rom_b0_flash_debug_cert(struct qmrom_handle *handle, + struct firmware *dbg_cert); +static int qm357xx_rom_b0_erase_debug_cert(struct qmrom_handle *handle); +static int +qm357xx_rom_b0_flash_unstitched_fw(struct qmrom_handle *handle, + const struct unstitched_firmware *all_fws); + +static void qm357xx_rom_b0_poll_soc(struct qmrom_handle *handle) +{ + int retries = handle->comms_retries; + memset(handle->hstc, 0, sizeof(struct stc)); + qmrom_msleep(SPI_READY_TIMEOUT_MS); + do { + qmrom_spi_transfer(handle->spi_handle, (char *)handle->sstc, + (const char *)handle->hstc, + sizeof(struct stc) + handle->hstc->len); + } while (retries-- && handle->sstc->raw_flags == 0); +} + +static int qm357xx_rom_b0_wait_ready(struct qmrom_handle *handle) +{ + int retries = handle->comms_retries; + int rc; + + qm357xx_rom_b0_poll_soc(handle); + + /* handle->sstc has been updated */ + while (retries-- && + handle->sstc->raw_flags != SPI_SH_READY_CMD_BIT_MASK) { + if (handle->sstc->soc_flags.out_waiting) { + qmrom_pre_read(handle); + } else if (handle->sstc->soc_flags.out_active) { + rc = qmrom_read(handle); + if (rc) + return rc; + } else { + /* error? */ + qm357xx_rom_b0_poll_soc(handle); + } + } + + return handle->sstc->raw_flags == SPI_SH_READY_CMD_BIT_MASK ? + 0 : + SPI_ERR_WAIT_READY_TIMEOUT; +} + +static int qm357xx_rom_b0_poll_cmd_resp(struct qmrom_handle *handle) +{ + int retries = handle->comms_retries; + + qm357xx_rom_b0_poll_soc(handle); + do { + if (handle->sstc->soc_flags.out_waiting) { + qmrom_pre_read(handle); + if (handle->sstc->len > 0xff) { + /* likely the wrong endianness, A0? */ + return -1; + } + qmrom_read(handle); + break; + } else + qm357xx_rom_b0_poll_soc(handle); + } while (retries--); + + return retries > 0 ? 0 : -1; +} + +int qm357xx_rom_b0_probe_device(struct qmrom_handle *handle) +{ + int rc, i; + uint8_t *soc_lcs_uuid; + handle->is_be = false; + check_stcs(__func__, __LINE__, handle); + + if (handle->spi_speed == 0) + qmrom_spi_set_freq(DEFAULT_SPI_CLOCKRATE); + else + qmrom_spi_set_freq(handle->spi_speed); + + rc = qmrom_reboot_bootloader(handle); + if (rc) { + LOG_ERR("%s: cannot reset the device...\n", __func__); + return rc; + } + + rc = qm357xx_rom_b0_wait_ready(handle); + if (rc) { + LOG_INFO("%s: maybe not a B0 device\n", __func__); + return rc; + } + + rc = qm357xx_rom_write_cmd(handle, ROM_CMD_B0_GET_CHIP_VER); + if (rc) + return rc; + + rc = qm357xx_rom_b0_poll_cmd_resp(handle); + if (rc) + return rc; + + handle->chip_rev = + SSTC2UINT16(handle, CHIP_VERSION_CHIP_REV_PAYLOAD_OFFSET) & + 0xFF; + handle->device_version = bswap_16( + SSTC2UINT16(handle, CHIP_VERSION_DEV_REV_PAYLOAD_OFFSET)); + if (handle->chip_rev != CHIP_REVISION_B0) { + LOG_ERR("%s: wrong chip revision 0x%x\n", __func__, + handle->chip_rev); + handle->chip_rev = CHIP_REVISION_UNKNOWN; + return -1; + } + + rc = qm357xx_rom_b0_wait_ready(handle); + if (rc) { + LOG_ERR("%s: hmm something went wrong!!!\n", __func__); + return rc; + } + + rc = qm357xx_rom_write_cmd(handle, ROM_CMD_B0_GET_SOC_INFO); + if (rc) + return rc; + + rc = qm357xx_rom_b0_poll_cmd_resp(handle); + if (rc) + return rc; + + /* skip the first byte */ + soc_lcs_uuid = &(handle->sstc->payload[1]); + for (i = 0; i < QM357XX_ROM_SOC_ID_LEN; i++) + handle->qm357xx_soc_info.soc_id[i] = + soc_lcs_uuid[QM357XX_ROM_SOC_ID_LEN - i - 1]; + soc_lcs_uuid += QM357XX_ROM_SOC_ID_LEN; + handle->qm357xx_soc_info.lcs_state = soc_lcs_uuid[0]; + soc_lcs_uuid += 1; + for (i = 0; i < QM357XX_ROM_UUID_LEN; i++) + handle->qm357xx_soc_info.uuid[i] = + soc_lcs_uuid[QM357XX_ROM_UUID_LEN - i - 1]; + + /* Set device type */ + handle->dev_gen = DEVICE_GEN_QM357XX; + /* Set rom ops */ + handle->qm357xx_rom_ops.flash_unstitched_fw = + qm357xx_rom_b0_flash_unstitched_fw; + handle->qm357xx_rom_ops.flash_debug_cert = + qm357xx_rom_b0_flash_debug_cert; + handle->qm357xx_rom_ops.erase_debug_cert = + qm357xx_rom_b0_erase_debug_cert; + + check_stcs(__func__, __LINE__, handle); + return 0; +} + +static int qm357xx_rom_b0_flash_data(struct qmrom_handle *handle, + struct firmware *fw, uint8_t cmd, + uint8_t exp) +{ + int rc, sent = 0; + const char *bin_data = (const char *)fw->data; + + check_stcs(__func__, __LINE__, handle); + while (sent < fw->size) { + uint32_t tx_bytes = fw->size - sent; + if (tx_bytes > CHUNK_SIZE_B0) + tx_bytes = CHUNK_SIZE_B0; + + LOG_DBG("%s: poll soc...\n", __func__); + check_stcs(__func__, __LINE__, handle); + qm357xx_rom_b0_poll_soc(handle); + qmrom_pre_read(handle); + qmrom_read(handle); + if (handle->sstc->payload[0] != exp) { + LOG_ERR("%s: wrong data expected (%#x vs %#x)!!!\n", + __func__, handle->sstc->payload[0] & 0xff, exp); + if (handle->sstc->payload[0] == + ERR_FIRST_KEY_CERT_OR_FW_VER) + return PEG_ERR_FIRST_KEY_CERT_OR_FW_VER; + else + return SPI_PROTO_WRONG_RESP; + } + + LOG_DBG("%s: sending %d command with %" PRIu32 " bytes\n", + __func__, cmd, tx_bytes); + rc = qm357xx_rom_write_size_cmd(handle, cmd, tx_bytes, + bin_data); + if (rc) + return rc; + sent += tx_bytes; + bin_data += tx_bytes; + check_stcs(__func__, __LINE__, handle); + } + return 0; +} + +static int +qm357xx_rom_b0_flash_unstitched_fw(struct qmrom_handle *handle, + const struct unstitched_firmware *all_fws) +{ + int rc = 0; + uint8_t flash_cmd = handle->qm357xx_soc_info.lcs_state == + CC_BSV_SECURE_LCS ? + ROM_CMD_B0_SEC_LOAD_OEM_IMG_TO_RRAM : + ROM_CMD_B0_SEC_LOAD_ICV_IMG_TO_RRAM; + + if (all_fws->key1_crt->data[HBK_LOC] == HBK_2E_ICV && + handle->qm357xx_soc_info.lcs_state != CC_BSV_CHIP_MANUFACTURE_LCS) { + LOG_ERR("%s: Trying to flash an ICV fw on a non ICV platform\n", + __func__); + rc = -EINVAL; + goto end; + } + + if (all_fws->key1_crt->data[HBK_LOC] == HBK_2E_OEM && + handle->qm357xx_soc_info.lcs_state != CC_BSV_SECURE_LCS) { + LOG_ERR("%s: Trying to flash an OEM fw on a non OEM platform\n", + __func__); + rc = -EINVAL; + goto end; + } + + LOG_DBG("%s: starting...\n", __func__); + check_stcs(__func__, __LINE__, handle); + + rc = qm357xx_rom_b0_wait_ready(handle); + if (rc) + goto end; + + check_stcs(__func__, __LINE__, handle); + LOG_DBG("%s: sending flash_cmd %u command\n", __func__, flash_cmd); + rc = qm357xx_rom_write_cmd(handle, flash_cmd); + if (rc) + goto end; + + check_stcs(__func__, __LINE__, handle); + rc = qm357xx_rom_b0_flash_data(handle, all_fws->key1_crt, + ROM_CMD_B0_CERT_DATA, + WAITING_FOR_FIRST_KEY_CERT); + if (rc) + goto end; + + check_stcs(__func__, __LINE__, handle); + rc = qm357xx_rom_b0_flash_data(handle, all_fws->key2_crt, + ROM_CMD_B0_CERT_DATA, + WAITING_FOR_SECOND_KEY_CERT); + if (rc) + goto end; + + check_stcs(__func__, __LINE__, handle); + rc = qm357xx_rom_b0_flash_data(handle, all_fws->fw_crt, + ROM_CMD_B0_CERT_DATA, + WAITING_FOR_CONTENT_CERT); + if (rc) + goto end; + + check_stcs(__func__, __LINE__, handle); + rc = qm357xx_rom_b0_flash_data(handle, all_fws->fw_img, + ROM_CMD_B0_SEC_IMAGE_DATA, + WAITING_FOR_SEC_FILE_DATA); + + if (!rc) + qmrom_msleep(SPI_READY_TIMEOUT_MS); + +end: + check_stcs(__func__, __LINE__, handle); + qmrom_free(all_fws->fw_img); + qmrom_free(all_fws->fw_crt); + qmrom_free(all_fws->key1_crt); + qmrom_free(all_fws->key2_crt); + return rc; +} + +static int qm357xx_rom_b0_flash_debug_cert(struct qmrom_handle *handle, + struct firmware *dbg_cert) +{ + int rc; + + LOG_DBG("%s: starting...\n", __func__); + check_stcs(__func__, __LINE__, handle); + + rc = qm357xx_rom_b0_wait_ready(handle); + if (rc) + return rc; + + check_stcs(__func__, __LINE__, handle); + LOG_DBG("%s: sending ROM_CMD_B0_WRITE_DBG_CERT command\n", __func__); + rc = qm357xx_rom_write_cmd(handle, ROM_CMD_B0_WRITE_DBG_CERT); + if (rc) + return rc; + + check_stcs(__func__, __LINE__, handle); + LOG_DBG("%s: poll soc...\n", __func__); + qm357xx_rom_b0_poll_soc(handle); + qmrom_pre_read(handle); + qmrom_read(handle); + + check_stcs(__func__, __LINE__, handle); + LOG_DBG("%s: sending ROM_CMD_B0_DEBUG_CERT_SIZE command\n", __func__); + rc = qm357xx_rom_write_size_cmd(handle, ROM_CMD_B0_DEBUG_CERT_SIZE, + sizeof(uint32_t), + (const char *)&dbg_cert->size); + if (handle->sstc->payload[0] != WAITING_TO_DEBUG_CERTIFICATE_SIZE) { + LOG_ERR("%s: wrong debug cert size result (0x%x vs 0x%x)!!!\n", + __func__, handle->sstc->payload[0] & 0xff, + WAITING_TO_DEBUG_CERTIFICATE_SIZE); + return SPI_PROTO_WRONG_RESP; + } + if (rc) + return rc; + + rc = qm357xx_rom_b0_flash_data(handle, dbg_cert, ROM_CMD_B0_CERT_DATA, + WAITING_FOR_DEBUG_CERT_DATA); + check_stcs(__func__, __LINE__, handle); + qmrom_msleep(SPI_READY_TIMEOUT_MS); + return rc; +} + +static int qm357xx_rom_b0_erase_debug_cert(struct qmrom_handle *handle) +{ + int rc; + + LOG_INFO("%s: starting...\n", __func__); + check_stcs(__func__, __LINE__, handle); + + rc = qm357xx_rom_b0_wait_ready(handle); + if (!rc) + return rc; + + LOG_DBG("%s: sending ROM_CMD_B0_ERASE_DBG_CERT command\n", __func__); + rc = qm357xx_rom_write_cmd(handle, ROM_CMD_B0_ERASE_DBG_CERT); + if (rc) + return rc; + + qmrom_msleep(SPI_READY_TIMEOUT_MS); + check_stcs(__func__, __LINE__, handle); + return 0; +} diff --git a/drivers/uwb/qm35/libqmrom/src/qm357xx_rom_c0.c b/drivers/uwb/qm35/libqmrom/src/qm357xx_rom_c0.c new file mode 100644 index 000000000000..657af2771609 --- /dev/null +++ b/drivers/uwb/qm35/libqmrom/src/qm357xx_rom_c0.c @@ -0,0 +1,587 @@ +// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 +/* + * Copyright 2022 Qorvo US, Inc. + * + */ + +#include +#include +#include +#include +#include + +#define DEFAULT_SPI_CLOCKRATE 5000000 +#define CHIP_VERSION_CHIP_REV_PAYLOAD_OFFSET 4 +#define CHIP_VERSION_DEV_REV_PAYLOAD_OFFSET 6 +#define CHUNK_SIZE_C0 2040 +#define SPI_READY_TIMEOUT_MS_C0 200 +#define SPI_ERROR_DETECTED_DELAY_MS 65 + +#ifndef CONFIG_CHUNK_FLASHING_RETRIES +#define CONFIG_CHUNK_FLASHING_RETRIES 10 +#endif + +#ifdef C0_WRITE_STATS +#include +#endif + +#define SPI_SH_READY_CMD_BIT_MASK_C0 \ + (SPI_SH_READY_CMD_BIT_MASK >> 4 | SPI_SH_READY_CMD_BIT_MASK) + +enum C0_CMD { + ROM_CMD_C0_SEC_LOAD_ICV_IMG_TO_RRAM = 0x0, + ROM_CMD_C0_SEC_LOAD_OEM_IMG_TO_RRAM = 0x1, + ROM_CMD_C0_GET_CHIP_VER = 0x2, + ROM_CMD_C0_GET_SOC_INFO = 0x3, + ROM_CMD_C0_ERASE_DBG_CERT = 0x4, + ROM_CMD_C0_USE_DIRECT_RRAM_WR = 0X5, + ROM_CMD_C0_USE_INDIRECT_RRAM_WR = 0x6, + ROM_CMD_C0_WRITE_DBG_CERT = 0x7, + ROM_CMD_C0_SEC_IMAGE_DATA = 0x12, + ROM_CMD_C0_CERT_DATA = 0x13, + ROM_CMD_C0_DEBUG_CERT_SIZE = 0x14, +}; + +enum C0_RESP { + READY_FOR_CS_LOW_CMD = 0x00, + WRONG_CS_LOW_CMD = 0x01, + WAITING_FOR_NS_RRAM_FILE_SIZE = 0x02, + WAITING_FOR_NS_SRAM_FILE_SIZE = 0x03, + WAITING_FOR_NS_RRAM_FILE_DATA = 0x04, + WAITING_FOR_NS_SRAM_FILE_DATA = 0x05, + WAITING_FOR_SEC_FILE_DATA = 0x06, + ERR_NS_SRAM_OR_RRAM_SIZE_CMD = 0x07, + ERR_SEC_RRAM_SIZE_CMD = 0x08, + ERR_WAITING_FOR_NS_IMAGE_DATA_CMD = 0x09, + ERR_WAITING_FOR_SEC_IMAGE_DATA_CMD = 0x0A, + ERR_IMAGE_SIZE_IS_ZERO = 0x0B, + /* Got more data than expected size */ + ERR_IMAGE_SIZE_TOO_BIG = 0x0C, + /* Image must divide in 16 without remainder */ + ERR_IMAGE_IS_NOT_16BYTES_MUL = 0x0D, + ERR_GOT_DATA_MORE_THAN_ALLOWED = 0x0E, + /* Remainder is allowed only for last packet */ + ERR_RRAM_DATA_REMAINDER_NOT_ALLOWED = 0x0F, + ERR_WAITING_FOR_CERT_DATA_CMD = 0x10, + WAITING_FOR_FIRST_KEY_CERT = 0x11, + WAITING_FOR_SECOND_KEY_CERT = 0x12, + WAITING_FOR_CONTENT_CERT = 0x13, + WAITING_FOR_DEBUG_CERT_DATA = 0x14, + ERR_FIRST_KEY_CERT_OR_FW_VER = 0x15, + ERR_SECOND_KEY_CERT = 0x16, + ERR_CONTENT_CERT_DOWNLOAD_ADDR = 0x17, + /* If the content certificate contains to much images */ + ERR_TOO_MANY_IMAGES_IN_CONTENT_CERT = 0x18, + ERR_ADDRESS_NOT_DIVIDED_BY_8 = 0x19, + ERR_IMAGE_BOUNDARIES = 0x1A, + /* Expected ICV type and got OEM */ + ERR_CERT_TYPE = 0x1B, + ERR_PRODUCT_ID = 0x1C, + ERR_RRAM_RANGE_OR_WRITE = 0x1D, + WAITING_TO_DEBUG_CERTIFICATE_SIZE = 0x1E, + ERR_DEBUG_CERT_SIZE = 0x1F, +}; + +static int +qm357xx_rom_c0_flash_unstitched_fw(struct qmrom_handle *handle, + const struct unstitched_firmware *all_fws); +static int qm357xx_rom_c0_flash_debug_cert(struct qmrom_handle *handle, + struct firmware *dbg_cert); +static int qm357xx_rom_c0_erase_debug_cert(struct qmrom_handle *handle); + +#define qmrom_pre_read_c0(h) \ + ({ \ + int rc; \ + qmrom_spi_wait_for_ready_line((h)->ss_rdy_handle, \ + SPI_READY_TIMEOUT_MS_C0); \ + rc = qmrom_pre_read((h)); \ + rc; \ + }) +#define qmrom_read_c0(h) \ + ({ \ + int rc; \ + qmrom_spi_wait_for_ready_line((h)->ss_rdy_handle, \ + SPI_READY_TIMEOUT_MS_C0); \ + rc = qmrom_read((h)); \ + rc; \ + }) +#define qm357xx_rom_write_cmd_c0(h, cmd) \ + ({ \ + int rc; \ + qmrom_spi_wait_for_ready_line((h)->ss_rdy_handle, \ + SPI_READY_TIMEOUT_MS_C0); \ + rc = qm357xx_rom_write_cmd((h), (cmd)); \ + rc; \ + }) +#define qm357xx_rom_write_cmd32_c0(h, cmd) \ + ({ \ + int rc; \ + qmrom_spi_wait_for_ready_line((h)->ss_rdy_handle, \ + SPI_READY_TIMEOUT_MS_C0); \ + rc = qm357xx_rom_write_cmd32((h), (cmd)); \ + rc; \ + }) +#define qm357xx_rom_write_size_cmd_c0(h, cmd, ds, d) \ + ({ \ + int rc; \ + qmrom_spi_wait_for_ready_line((h)->ss_rdy_handle, \ + SPI_READY_TIMEOUT_MS_C0); \ + rc = qm357xx_rom_write_size_cmd((h), (cmd), (ds), (d)); \ + rc; \ + }) +#define qm357xx_rom_write_size_cmd32_c0(h, cmd, ds, d) \ + ({ \ + int rc; \ + qmrom_spi_wait_for_ready_line((h)->ss_rdy_handle, \ + SPI_READY_TIMEOUT_MS_C0); \ + rc = qm357xx_rom_write_size_cmd32((h), (cmd), (ds), (d)); \ + rc; \ + }) + +static void qm357xx_rom_c0_poll_soc(struct qmrom_handle *handle) +{ + int retries = handle->comms_retries; + memset(handle->hstc, 0, sizeof(struct stc)); + handle->sstc->raw_flags = 0; + do { + int rc = qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle, + SPI_READY_TIMEOUT_MS_C0); + if (rc) { + LOG_ERR("%s qmrom_spi_wait_for_ready_line failed\n", + __func__); + continue; + } + qmrom_spi_transfer(handle->spi_handle, (char *)handle->sstc, + (const char *)handle->hstc, + sizeof(struct stc) + handle->hstc->len); + } while (retries-- && handle->sstc->raw_flags == 0); +} + +static int qm357xx_rom_c0_wait_ready(struct qmrom_handle *handle) +{ + int retries = handle->comms_retries; + + qm357xx_rom_c0_poll_soc(handle); + + /* handle->sstc has been updated */ + while (retries-- && + handle->sstc->raw_flags != SPI_SH_READY_CMD_BIT_MASK_C0) { + if (handle->sstc->soc_flags.out_waiting) { + qmrom_pre_read_c0(handle); + qmrom_read_c0(handle); + } else if (handle->sstc->soc_flags.out_active) { + return qmrom_read_c0(handle); + } else + qm357xx_rom_c0_poll_soc(handle); + } + + return handle->sstc->raw_flags == SPI_SH_READY_CMD_BIT_MASK_C0 ? + 0 : + SPI_ERR_WAIT_READY_TIMEOUT; +} + +static int qm357xx_rom_c0_poll_cmd_resp(struct qmrom_handle *handle) +{ + int retries = handle->comms_retries; + + qm357xx_rom_c0_poll_soc(handle); + do { + if (handle->sstc->soc_flags.out_waiting) { + qmrom_pre_read_c0(handle); + return qmrom_read_c0(handle); + } else + qm357xx_rom_c0_poll_soc(handle); + } while (retries--); + + LOG_ERR("%s failed after %d replies\n", __func__, + handle->comms_retries); + + return -EPERM; +} + +int qm357xx_rom_c0_probe_device(struct qmrom_handle *handle) +{ + int rc, i; + uint8_t *soc_lcs_uuid; + + handle->is_be = false; + + if (handle->spi_speed == 0) + qmrom_spi_set_freq(DEFAULT_SPI_CLOCKRATE); + else + qmrom_spi_set_freq(handle->spi_speed); + + rc = qmrom_reboot_bootloader(handle); + if (rc) { + LOG_ERR("%s: cannot reset the device...\n", __func__); + return rc; + } + + rc = qm357xx_rom_c0_wait_ready(handle); + if (rc) { + LOG_INFO("%s: maybe not a C0 device\n", __func__); + return rc; + } + + rc = qm357xx_rom_write_cmd32_c0(handle, ROM_CMD_C0_GET_CHIP_VER); + if (rc) + return rc; + + rc = qm357xx_rom_c0_poll_cmd_resp(handle); + if (rc) + return rc; + + handle->chip_rev = + SSTC2UINT16(handle, CHIP_VERSION_CHIP_REV_PAYLOAD_OFFSET) & + 0xFF; + handle->device_version = bswap_16( + SSTC2UINT16(handle, CHIP_VERSION_DEV_REV_PAYLOAD_OFFSET)); + if ((handle->chip_rev != CHIP_REVISION_C0) && + ((handle->chip_rev != CHIP_REVISION_C2))) { + LOG_ERR("%s: wrong chip revision %#x\n", __func__, + handle->chip_rev); + handle->chip_rev = CHIP_REVISION_UNKNOWN; + return -1; + } + + rc = qm357xx_rom_c0_wait_ready(handle); + if (rc) { + LOG_ERR("%s: hmm something went wrong!!!\n", __func__); + return rc; + } + + rc = qm357xx_rom_write_cmd32_c0(handle, ROM_CMD_C0_GET_SOC_INFO); + if (rc) + return rc; + + rc = qm357xx_rom_c0_poll_cmd_resp(handle); + if (rc) + return rc; + + /* skip the first 4 bytes */ + soc_lcs_uuid = &(handle->sstc->payload[4]); + for (i = 0; i < QM357XX_ROM_SOC_ID_LEN; i++) + handle->qm357xx_soc_info.soc_id[i] = + soc_lcs_uuid[QM357XX_ROM_SOC_ID_LEN - i - 1]; + soc_lcs_uuid += QM357XX_ROM_SOC_ID_LEN; + memcpy(&handle->qm357xx_soc_info.lcs_state, soc_lcs_uuid, + sizeof(uint32_t)); + soc_lcs_uuid += 4; + for (i = 0; i < QM357XX_ROM_UUID_LEN; i++) + handle->qm357xx_soc_info.uuid[i] = + soc_lcs_uuid[QM357XX_ROM_UUID_LEN - i - 1]; + + /* Set device type */ + handle->dev_gen = DEVICE_GEN_QM357XX; + /* Set rom ops */ + handle->qm357xx_rom_ops.flash_unstitched_fw = + qm357xx_rom_c0_flash_unstitched_fw; + handle->qm357xx_rom_ops.flash_debug_cert = + qm357xx_rom_c0_flash_debug_cert; + handle->qm357xx_rom_ops.erase_debug_cert = + qm357xx_rom_c0_erase_debug_cert; + + return 0; +} + +#ifdef C0_WRITE_STATS +static uint64_t total_bytes, total_time_ns; +static uint32_t max_write_time_ns, min_write_time_ns = ~0; + +static void update_write_max_chunk_stats(ktime_t start_time) +{ + uint64_t elapsed_time_ns; + + total_bytes += CHUNK_SIZE_C0; + elapsed_time_ns = ktime_to_ns(ktime_sub(ktime_get(), start_time)); + total_time_ns += elapsed_time_ns; + if (elapsed_time_ns > max_write_time_ns) + max_write_time_ns = elapsed_time_ns; + if (elapsed_time_ns < min_write_time_ns) + min_write_time_ns = elapsed_time_ns; +} + +static void dump_stats(void) +{ + uint32_t nb_chunks = div_u64(total_bytes, CHUNK_SIZE_C0); + LOG_WARN( + "C0 flashing time stats: %llu bytes over %llu us (chunk size %u, write timings: mean %u us, min %u us, max %u us)\n", + total_bytes, div_u64(total_time_ns, 1000), CHUNK_SIZE_C0, + (uint32_t)(div_u64((div_u64(total_time_ns, nb_chunks)), 1000)), + min_write_time_ns / 1000, max_write_time_ns / 1000); +} +#endif + +static int qm357xx_rom_c0_flash_data(struct qmrom_handle *handle, + struct firmware *fw, uint8_t cmd, + uint8_t resp, bool skip_last_check) +{ + int rc, sent = 0, nb_poll_retry, chunk = 0; + const char *bin_data = (const char *)fw->data; +#ifdef C0_WRITE_STATS + ktime_t start_time; +#endif + + while (sent < fw->size) { + uint32_t tx_bytes = fw->size - sent; + if (tx_bytes > CHUNK_SIZE_C0) + tx_bytes = CHUNK_SIZE_C0; + + LOG_DBG("%s: sending command %#x with %" PRIu32 " bytes\n", + __func__, cmd, tx_bytes); +#ifdef C0_WRITE_STATS + start_time = ktime_get(); +#endif + rc = qm357xx_rom_write_size_cmd32_c0(handle, cmd, tx_bytes, + bin_data); + if (rc) + return rc; + sent += tx_bytes; + bin_data += tx_bytes; + if (skip_last_check && sent == fw->size) { + LOG_INFO("%s: flashing done, quitting now\n", __func__); + break; + } + qm357xx_rom_c0_poll_soc(handle); + nb_poll_retry = CONFIG_CHUNK_FLASHING_RETRIES; + while (handle->sstc->soc_flags.err && --nb_poll_retry >= 0) { + qmrom_msleep(SPI_ERROR_DETECTED_DELAY_MS); + qm357xx_rom_c0_poll_soc(handle); + LOG_ERR("%s: spi error detected for cmd %#x chunk %d, retry %d, soc_flags 0x%02x\n", + __func__, cmd, chunk, + CONFIG_CHUNK_FLASHING_RETRIES - nb_poll_retry, + handle->sstc->raw_flags); + rc = qm357xx_rom_write_size_cmd32_c0( + handle, cmd, tx_bytes, bin_data - tx_bytes); + qm357xx_rom_c0_poll_soc(handle); + } +#ifdef C0_WRITE_STATS + if (tx_bytes == CHUNK_SIZE_C0) + update_write_max_chunk_stats(start_time); +#endif + qmrom_pre_read_c0(handle); + qmrom_read_c0(handle); + if (handle->sstc->payload[0] != resp) { + LOG_ERR("%s: wrong data result (%#x vs %#x)!!!\n", + __func__, handle->sstc->payload[0] & 0xff, + resp); + if (handle->sstc->payload[0] == + ERR_FIRST_KEY_CERT_OR_FW_VER) + return PEG_ERR_FIRST_KEY_CERT_OR_FW_VER; + else + return SPI_PROTO_WRONG_RESP; + } + chunk++; + } + qmrom_msleep(SPI_READY_TIMEOUT_MS_C0); + return 0; +} + +static int +qm357xx_rom_c0_flash_unstitched_fw(struct qmrom_handle *handle, + const struct unstitched_firmware *all_fws) +{ + int rc = 0; + uint8_t flash_cmd = + handle->qm357xx_soc_info.lcs_state == CC_BSV_SECURE_LCS || + handle->qm357xx_soc_info.lcs_state == + CC_BSV_RMA_LCS ? + ROM_CMD_C0_SEC_LOAD_OEM_IMG_TO_RRAM : + ROM_CMD_C0_SEC_LOAD_ICV_IMG_TO_RRAM; + + if (all_fws->key1_crt->data[HBK_LOC] == HBK_2E_ICV && + handle->qm357xx_soc_info.lcs_state != CC_BSV_CHIP_MANUFACTURE_LCS) { + LOG_ERR("%s: Trying to flash an ICV fw on a non ICV platform\n", + __func__); + rc = -EINVAL; + goto end; + } + + if (all_fws->key1_crt->data[HBK_LOC] == HBK_2E_OEM && + handle->qm357xx_soc_info.lcs_state != CC_BSV_SECURE_LCS && + handle->qm357xx_soc_info.lcs_state != CC_BSV_RMA_LCS) { + LOG_ERR("%s: Trying to flash an OEM fw on a non OEM platform\n", + __func__); + rc = -EINVAL; + goto end; + } + + LOG_DBG("%s: starting...\n", __func__); + + /* Set RRAM write mode */ + rc = qm357xx_rom_c0_wait_ready(handle); + if (rc) + goto end; + + if (handle->chip_rev != CHIP_REVISION_C2) { + LOG_DBG("%s: sending ROM_CMD_C0_USE_INDIRECT_RRAM_WR command (chip_rev %x)\n", + __func__, handle->chip_rev); + rc = qm357xx_rom_write_cmd32_c0( + handle, ROM_CMD_C0_USE_INDIRECT_RRAM_WR); + if (rc) + goto end; + } + + qm357xx_rom_c0_poll_soc(handle); + qmrom_pre_read_c0(handle); + qmrom_read_c0(handle); + qm357xx_rom_c0_poll_soc(handle); + + LOG_ERR("%s: sending flash_cmd %u command\n", __func__, flash_cmd); + rc = qm357xx_rom_write_cmd32_c0(handle, flash_cmd); + if (rc) + goto end; + + qm357xx_rom_c0_poll_cmd_resp(handle); + if (handle->sstc->payload[0] != WAITING_FOR_FIRST_KEY_CERT) { + LOG_ERR("%s: Waiting for WAITING_FOR_FIRST_KEY_CERT(%#x) but got %#x\n", + __func__, WAITING_FOR_FIRST_KEY_CERT, + handle->sstc->payload[0]); + rc = -1; + goto end; + } + + qm357xx_rom_c0_poll_soc(handle); + + rc = qm357xx_rom_c0_flash_data(handle, all_fws->key1_crt, + ROM_CMD_C0_CERT_DATA, + WAITING_FOR_SECOND_KEY_CERT, false); + if (rc) + goto end; + + rc = qm357xx_rom_c0_flash_data(handle, all_fws->key2_crt, + ROM_CMD_C0_CERT_DATA, + WAITING_FOR_CONTENT_CERT, false); + if (rc) + goto end; + + rc = qm357xx_rom_c0_flash_data(handle, all_fws->fw_crt, + ROM_CMD_C0_CERT_DATA, + WAITING_FOR_SEC_FILE_DATA, false); + if (rc) + goto end; + + rc = qm357xx_rom_c0_flash_data(handle, all_fws->fw_img, + ROM_CMD_C0_SEC_IMAGE_DATA, + WAITING_FOR_SEC_FILE_DATA, true); + +#ifdef C0_WRITE_STATS + dump_stats(); +#endif + + if (qmrom_spi_read_irq_line(handle->ss_irq_handle)) { + int retries = handle->comms_retries; + + do { + /* A final product id error likely occured */ + qmrom_pre_read_c0(handle); + qmrom_read_c0(handle); + rc = handle->sstc->payload[0]; + if (rc) { + LOG_ERR("%s: flashing error %d (0x%x) detected\n", + __func__, rc, rc); + break; + } + } while (--retries); + + if (retries <= 0) { + LOG_ERR("%s: flashing error detected but couldn't be fetched\n", + __func__); + rc = -1; + } + } + + /* Flashing is done, the fw should reboot, check we are not still talking to the ROM code */ + if (!rc && !handle->skip_check_fw_boot) { + uint8_t raw_flags; + + qmrom_msleep(SPI_READY_TIMEOUT_MS_C0); + qm357xx_rom_c0_poll_soc(handle); + raw_flags = handle->sstc->raw_flags; + /* The ROM code sends the same quartets for the first byte of each xfers */ + if (((raw_flags & 0xf0) >> 4) == (raw_flags & 0xf)) { + LOG_ERR("%s: firmware not properly started: %#x\n", + __func__, raw_flags); + rc = -2; + } + } + +end: + qmrom_free(all_fws->fw_img); + qmrom_free(all_fws->fw_crt); + qmrom_free(all_fws->key1_crt); + qmrom_free(all_fws->key2_crt); + return rc; +} + +static int qm357xx_rom_c0_flash_debug_cert(struct qmrom_handle *handle, + struct firmware *dbg_cert) +{ + int rc; + + LOG_DBG("%s: starting...\n", __func__); + rc = qm357xx_rom_c0_wait_ready(handle); + if (rc) + return rc; + + if (handle->chip_rev != CHIP_REVISION_C2) { + LOG_DBG("%s: using ROM_CMD_C0_USE_INDIRECT_RRAM_WR command\n", + __func__); + rc = qm357xx_rom_write_cmd32_c0( + handle, ROM_CMD_C0_USE_INDIRECT_RRAM_WR); + if (rc) + return rc; + } + + qm357xx_rom_c0_poll_soc(handle); + qmrom_pre_read_c0(handle); + qmrom_read_c0(handle); + qm357xx_rom_c0_poll_soc(handle); + + LOG_DBG("%s: sending ROM_CMD_C0_WRITE_DBG_CERT command\n", __func__); + rc = qm357xx_rom_write_cmd32_c0(handle, ROM_CMD_C0_WRITE_DBG_CERT); + if (rc) + return rc; + qm357xx_rom_c0_poll_cmd_resp(handle); + if (handle->sstc->payload[0] != WAITING_TO_DEBUG_CERTIFICATE_SIZE) { + LOG_ERR("%s: Waiting for WAITING_TO_DEBUG_CERTIFICATE_SIZE(%#x) but got %#x\n", + __func__, WAITING_TO_DEBUG_CERTIFICATE_SIZE, + handle->sstc->payload[0]); + return rc; + } + + LOG_DBG("%s: sending ROM_CMD_C0_DEBUG_CERT_SIZE command\n", __func__); + rc = qm357xx_rom_write_size_cmd32_c0(handle, ROM_CMD_C0_DEBUG_CERT_SIZE, + sizeof(uint32_t), + (const char *)&dbg_cert->size); + qm357xx_rom_c0_poll_cmd_resp(handle); + if (handle->sstc->payload[0] != WAITING_FOR_DEBUG_CERT_DATA) { + LOG_ERR("%s: Waiting for WAITING_FOR_DEBUG_CERT_DATA(%#x) but got %#x\n", + __func__, WAITING_FOR_DEBUG_CERT_DATA, + handle->sstc->payload[0]); + return rc; + } + + rc = qm357xx_rom_c0_flash_data(handle, dbg_cert, ROM_CMD_C0_CERT_DATA, + WAITING_FOR_DEBUG_CERT_DATA, true); + return 0; +} + +static int qm357xx_rom_c0_erase_debug_cert(struct qmrom_handle *handle) +{ + int rc; + + LOG_DBG("%s: starting...\n", __func__); + + rc = qm357xx_rom_c0_wait_ready(handle); + if (rc) + return rc; + + LOG_DBG("%s: sending ROM_CMD_C0_ERASE_DBG_CERT command\n", __func__); + rc = qm357xx_rom_write_cmd32_c0(handle, ROM_CMD_C0_ERASE_DBG_CERT); + if (rc) + return rc; + + qmrom_msleep(SPI_READY_TIMEOUT_MS_C0); + return 0; +} diff --git a/drivers/uwb/qm35/libqmrom/src/qm357xx_rom_common.c b/drivers/uwb/qm35/libqmrom/src/qm357xx_rom_common.c new file mode 100644 index 000000000000..05bab45d8e85 --- /dev/null +++ b/drivers/uwb/qm35/libqmrom/src/qm357xx_rom_common.c @@ -0,0 +1,412 @@ +// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 +/* + * Copyright 2022 Qorvo US, Inc. + * + */ + +#include +#include +#include +#include +#include + +#include + +int qm357xx_rom_b0_probe_device(struct qmrom_handle *handle); +int qm357xx_rom_c0_probe_device(struct qmrom_handle *handle); + +int qm357xx_rom_write_cmd(struct qmrom_handle *handle, uint8_t cmd) +{ + handle->hstc->all = 0; + handle->hstc->host_flags.write = 1; + handle->hstc->ul = 1; + handle->hstc->len = 1; + handle->hstc->payload[0] = cmd; + + return qmrom_spi_transfer(handle->spi_handle, (char *)handle->sstc, + (const char *)handle->hstc, + sizeof(struct stc) + handle->hstc->len); +} + +int qm357xx_rom_write_cmd32(struct qmrom_handle *handle, uint32_t cmd) +{ + handle->hstc->all = 0; + handle->hstc->host_flags.write = 1; + handle->hstc->ul = 1; + handle->hstc->len = sizeof(cmd); + memcpy(handle->hstc->payload, &cmd, sizeof(cmd)); + + return qmrom_spi_transfer(handle->spi_handle, (char *)handle->sstc, + (const char *)handle->hstc, + sizeof(struct stc) + handle->hstc->len); +} + +int qm357xx_rom_write_size_cmd(struct qmrom_handle *handle, uint8_t cmd, + uint16_t data_size, const char *data) +{ + handle->hstc->all = 0; + handle->hstc->host_flags.write = 1; + handle->hstc->ul = 1; + handle->hstc->len = data_size + 1; + handle->hstc->payload[0] = cmd; + memcpy(&handle->hstc->payload[1], data, data_size); + + return qmrom_spi_transfer(handle->spi_handle, (char *)handle->sstc, + (const char *)handle->hstc, + sizeof(struct stc) + handle->hstc->len); +} + +int qm357xx_rom_write_size_cmd32(struct qmrom_handle *handle, uint32_t cmd, + uint16_t data_size, const char *data) +{ + handle->hstc->all = 0; + handle->hstc->host_flags.write = 1; + handle->hstc->ul = 1; + handle->hstc->len = data_size + sizeof(cmd); + memcpy(handle->hstc->payload, &cmd, sizeof(cmd)); + memcpy(&handle->hstc->payload[sizeof(cmd)], data, data_size); + + return qmrom_spi_transfer(handle->spi_handle, (char *)handle->sstc, + (const char *)handle->hstc, + sizeof(struct stc) + handle->hstc->len); +} + +/* + * Unfortunately, B0 and C0 have different + * APIs to get the chip version... + * + */ +int qm357xx_rom_probe_device(struct qmrom_handle *handle) +{ + int rc; + + /* Test C0 first */ + rc = qm357xx_rom_c0_probe_device(handle); + if (!rc) + return rc; + + /* Test B0 next */ + rc = qm357xx_rom_b0_probe_device(handle); + if (!rc) + return rc; + + /* None matched!!! */ + return -1; +} + +int qm357xx_rom_flash_dbg_cert(struct qmrom_handle *handle, + struct firmware *dbg_cert) +{ + if (!handle->qm357xx_rom_ops.flash_debug_cert) { + LOG_ERR("%s: flash debug certificate not support on this device\n", + __func__); + return -EINVAL; + } + return handle->qm357xx_rom_ops.flash_debug_cert(handle, dbg_cert); +} + +int qm357xx_rom_erase_dbg_cert(struct qmrom_handle *handle) +{ + if (!handle->qm357xx_rom_ops.erase_debug_cert) { + LOG_ERR("%s: erase debug certificate not support on this device\n", + __func__); + return -EINVAL; + } + return handle->qm357xx_rom_ops.erase_debug_cert(handle); +} + +int qm357xx_rom_flash_fw(struct qmrom_handle *handle, const struct firmware *fw) +{ + int rc = 0; + struct unstitched_firmware all_fws = { 0 }; + + if (*(uint32_t *)&fw->data[0] == + CRYPTO_MACRO_FIRMWARE_PACK_MAGIC_VALUE) { + //macro package detected + //write the FW UPDATER + rc = qm357xx_rom_unpack_fw_macro_pkg(fw, &all_fws); + if (rc) { + LOG_ERR("%s: Unpack macro FW package unsuccessful!\n", + __func__); + return rc; + } + } else { + rc = qm357xx_rom_unstitch_fw(fw, &all_fws, handle->chip_rev); + if (rc) { + LOG_ERR("%s: Unstitched fw flashing not supported yet\n", + __func__); + return rc; + } + } + rc = qm357xx_rom_flash_unstitched_fw(handle, &all_fws); + return rc; +} + +int qm357xx_rom_flash_unstitched_fw(struct qmrom_handle *handle, + const struct unstitched_firmware *fw) +{ + if (!handle->qm357xx_rom_ops.flash_unstitched_fw) { + LOG_ERR("%s: flash un-stitched firmware not supported on this device\n", + __func__); + return -EINVAL; + } + return handle->qm357xx_rom_ops.flash_unstitched_fw(handle, fw); +} + +int qm357xx_rom_unstitch_fw(const struct firmware *fw, + struct unstitched_firmware *unstitched_fw, + enum chip_revision_e revision) +{ + uint32_t tot_len = 0; + uint32_t fw_img_sz = 0; + uint32_t fw_crt_sz = 0; + uint32_t key1_crt_sz = 0; + uint32_t key2_crt_sz = 0; + uint8_t *p_key1; + uint8_t *p_key2; + uint8_t *p_crt; + uint8_t *p_fw; + int ret = 0; + + if (fw->size < 2 * sizeof(key1_crt_sz)) { + LOG_ERR("%s: Not enough data (%zu) to unstitch\n", __func__, + fw->size); + return -EINVAL; + } + LOG_INFO("%s: Unstitching %zu bytes\n", __func__, fw->size); + + /* key1 */ + key1_crt_sz = *(uint32_t *)&fw->data[tot_len]; + if (tot_len + key1_crt_sz + sizeof(key1_crt_sz) > fw->size) { + LOG_ERR("%s: Invalid or corrupted stitched file at offset \ + %" PRIu32 " (key1)\n", + __func__, tot_len); + ret = -EINVAL; + goto out; + } + tot_len += sizeof(key1_crt_sz); + p_key1 = (uint8_t *)&fw->data[tot_len]; + tot_len += key1_crt_sz; + + /* key2 */ + key2_crt_sz = *(uint32_t *)&fw->data[tot_len]; + if (tot_len + key2_crt_sz + sizeof(key2_crt_sz) > fw->size) { + LOG_ERR("%s: Invalid or corrupted stitched file at offset \ + %" PRIu32 " (key2)\n", + __func__, tot_len); + ret = -EINVAL; + goto out; + } + tot_len += sizeof(key2_crt_sz); + p_key2 = (uint8_t *)&fw->data[tot_len]; + tot_len += key2_crt_sz; + + /* cert */ + fw_crt_sz = *(uint32_t *)&fw->data[tot_len]; + if (tot_len + fw_crt_sz + sizeof(fw_crt_sz) > fw->size) { + LOG_ERR("%s: Invalid or corrupted stitched file at offset \ + %" PRIu32 " (content cert)\n", + __func__, tot_len); + ret = -EINVAL; + goto out; + } + tot_len += sizeof(fw_crt_sz); + p_crt = (uint8_t *)&fw->data[tot_len]; + tot_len += fw_crt_sz; + + /* fw */ + fw_img_sz = *(uint32_t *)&fw->data[tot_len]; + if (tot_len + fw_img_sz + sizeof(fw_img_sz) != fw->size) { + LOG_ERR("%s: Invalid or corrupted stitched file at offset \ + %" PRIu32 " (firmnware)\n", + __func__, tot_len); + ret = -EINVAL; + goto out; + } + tot_len += sizeof(fw_img_sz); + p_fw = (uint8_t *)&fw->data[tot_len]; + + qmrom_alloc(unstitched_fw->fw_img, fw_img_sz + sizeof(struct firmware)); + if (unstitched_fw->fw_img == NULL) { + ret = -ENOMEM; + goto out; + } + + qmrom_alloc(unstitched_fw->fw_crt, fw_crt_sz + sizeof(struct firmware)); + if (unstitched_fw->fw_crt == NULL) { + ret = -ENOMEM; + goto err; + } + + qmrom_alloc(unstitched_fw->key1_crt, + key1_crt_sz + sizeof(struct firmware)); + if (unstitched_fw->key1_crt == NULL) { + ret = -ENOMEM; + goto err; + } + + qmrom_alloc(unstitched_fw->key2_crt, + key2_crt_sz + sizeof(struct firmware)); + if (unstitched_fw->key2_crt == NULL) { + ret = -ENOMEM; + goto err; + } + + unstitched_fw->key1_crt->data = + (const uint8_t *)(unstitched_fw->key1_crt + 1); + unstitched_fw->key2_crt->data = + (const uint8_t *)(unstitched_fw->key2_crt + 1); + unstitched_fw->fw_crt->data = + (const uint8_t *)(unstitched_fw->fw_crt + 1); + unstitched_fw->fw_img->data = + (const uint8_t *)(unstitched_fw->fw_img + 1); + unstitched_fw->key1_crt->size = key1_crt_sz; + unstitched_fw->key2_crt->size = key2_crt_sz; + unstitched_fw->fw_crt->size = fw_crt_sz; + unstitched_fw->fw_img->size = fw_img_sz; + + memcpy((void *)unstitched_fw->key1_crt->data, p_key1, key1_crt_sz); + memcpy((void *)unstitched_fw->key2_crt->data, p_key2, key2_crt_sz); + memcpy((void *)unstitched_fw->fw_crt->data, p_crt, fw_crt_sz); + memcpy((void *)unstitched_fw->fw_img->data, p_fw, fw_img_sz); + return 0; + +err: + if (unstitched_fw->fw_img) + qmrom_free(unstitched_fw->fw_img); + if (unstitched_fw->fw_crt) + qmrom_free(unstitched_fw->fw_crt); + if (unstitched_fw->key1_crt) + qmrom_free(unstitched_fw->key1_crt); + if (unstitched_fw->key2_crt) + qmrom_free(unstitched_fw->key2_crt); + +out: + return ret; +} + +int qm357xx_rom_fw_macro_pkg_get_fw_idx(const struct firmware *fw, int idx, + uint32_t *fw_size, char **fw_data) +{ + struct fw_macro_pkg_hdr_t *fw_macro_pkg_hdr = + (struct fw_macro_pkg_hdr_t *)&fw->data[0]; + struct fw_img_desc_t *fw_pkg; + + if (fw_macro_pkg_hdr->nb_descriptors >= 1 && + idx < fw_macro_pkg_hdr->nb_descriptors) { + fw_pkg = (struct fw_img_desc_t *)(&fw_macro_pkg_hdr + ->img_desc[idx]); + if (fw_pkg->offset + fw_pkg->length > fw->size) { + LOG_ERR("Wrong FW PKG offset = %04x; len = %04x; idx = %d!\n", + fw_pkg->offset, fw_pkg->length, idx); + return -EINVAL; + } + } else { + LOG_ERR("%s: No FW pkg found in macro package! nb_descriptors = %d\n", + __func__, fw_macro_pkg_hdr->nb_descriptors); + return -EINVAL; + } + *fw_size = fw_pkg->length; + *fw_data = (char *)&fw->data[fw_pkg->offset]; + return 0; +} + +int qm357xx_rom_unpack_fw_macro_pkg(const struct firmware *fw, + struct unstitched_firmware *all_fws) +{ + int rc = 0; + char *fw_data; + uint32_t fw_size; + struct firmware fw_pkg; + + rc = qm357xx_rom_fw_macro_pkg_get_fw_idx(fw, 0, &fw_size, &fw_data); + if (rc) { + LOG_ERR("%s: FW MACRO PACKAGE corupted = %d\n", __func__, rc); + return rc; + } + + fw_pkg.data = (const uint8_t *)fw_data; + fw_pkg.size = fw_size; + return qm357xx_rom_unpack_fw_pkg(&fw_pkg, all_fws); +} + +int qm357xx_rom_unpack_fw_pkg(const struct firmware *fw_pkg, + struct unstitched_firmware *all_fws) +{ + int rc = 0; + uint8_t *p_key1; + uint8_t *p_key2; + uint8_t *p_crt; + uint8_t *p_fw; + struct fw_pkg_img_hdr_t *fw_pkg_img_hdr; + + fw_pkg_img_hdr = + (struct fw_pkg_img_hdr_t *)(fw_pkg->data + + sizeof(struct fw_pkg_hdr_t)); + + if (fw_pkg_img_hdr->magic != CRYPTO_FIRMWARE_IMAGE_MAGIC_VALUE) { + LOG_ERR("%s: Invalid or corrupted file! magic = %04x\n", + __func__, fw_pkg_img_hdr->magic); + return -EINVAL; + } + qmrom_alloc(all_fws->fw_img, + fw_pkg_img_hdr->descs[0].length + sizeof(struct firmware)); + if (all_fws->fw_img == NULL) { + rc = -ENOMEM; + goto err; + } + qmrom_alloc(all_fws->key1_crt, + CRYPTO_IMAGES_CERT_KEY_SIZE + sizeof(struct firmware)); + if (all_fws->key1_crt == NULL) { + rc = -ENOMEM; + goto err; + } + + qmrom_alloc(all_fws->key2_crt, + CRYPTO_IMAGES_CERT_KEY_SIZE + sizeof(struct firmware)); + if (all_fws->key2_crt == NULL) { + rc = -ENOMEM; + goto err; + } + + qmrom_alloc(all_fws->fw_crt, + CRYPTO_IMAGES_CERT_CONTENT_SIZE + sizeof(struct firmware)); + if (all_fws->fw_crt == NULL) { + rc = -ENOMEM; + goto err; + } + + all_fws->key1_crt->data = (const uint8_t *)(all_fws->key1_crt + 1); + all_fws->key2_crt->data = (const uint8_t *)(all_fws->key2_crt + 1); + all_fws->fw_crt->data = (const uint8_t *)(all_fws->fw_crt + 1); + all_fws->fw_img->data = (const uint8_t *)(all_fws->fw_img + 1); + all_fws->key1_crt->size = CRYPTO_IMAGES_CERT_KEY_SIZE; + all_fws->key2_crt->size = CRYPTO_IMAGES_CERT_KEY_SIZE; + all_fws->fw_crt->size = CRYPTO_IMAGES_CERT_CONTENT_SIZE; + all_fws->fw_img->size = fw_pkg_img_hdr->descs[0].length; + + p_key1 = (uint8_t *)fw_pkg_img_hdr + fw_pkg_img_hdr->cert_chain_offset; + p_key2 = p_key1 + CRYPTO_IMAGES_CERT_KEY_SIZE; + p_crt = p_key2 + CRYPTO_IMAGES_CERT_KEY_SIZE; + p_fw = (uint8_t *)fw_pkg_img_hdr + fw_pkg_img_hdr->descs[0].offset; + + memcpy((void *)all_fws->key1_crt->data, p_key1, + all_fws->key1_crt->size); + memcpy((void *)all_fws->key2_crt->data, p_key2, + all_fws->key2_crt->size); + memcpy((void *)all_fws->fw_crt->data, p_crt, all_fws->fw_crt->size); + memcpy((void *)all_fws->fw_img->data, p_fw, all_fws->fw_img->size); + return 0; + +err: + if (all_fws->fw_img) + qmrom_free(all_fws->fw_img); + if (all_fws->fw_crt) + qmrom_free(all_fws->fw_crt); + if (all_fws->key1_crt) + qmrom_free(all_fws->key1_crt); + if (all_fws->key2_crt) + qmrom_free(all_fws->key2_crt); + + return rc; +} diff --git a/drivers/uwb/qm35/libqmrom/src/qm358xx_rom.c b/drivers/uwb/qm35/libqmrom/src/qm358xx_rom.c new file mode 100644 index 000000000000..2cb1ab65e9be --- /dev/null +++ b/drivers/uwb/qm35/libqmrom/src/qm358xx_rom.c @@ -0,0 +1,1039 @@ +// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 +/* + * Copyright 2022 Qorvo US, Inc. + * + */ + +#include +#include +#include +#include +#include +#include + +#define DEFAULT_SPI_CLOCKRATE 5000000 +#define CHIP_VERSION_CHIP_REV_PAYLOAD_OFFSET (sizeof(resp_t)) +#define CHIP_VERSION_DEV_REV_PAYLOAD_OFFSET \ + (CHIP_VERSION_CHIP_REV_PAYLOAD_OFFSET + 2) +#define CHUNK_SIZE 2036 +#define CHUNK_PAYLOAD_MAX_SIZE (CHUNK_SIZE - CRYPTO_FIRMWARE_CHUNK_HDR_SIZE) +#define QM358_SPI_READY_TIMEOUT_MS 200 + +#ifdef QM358XX_WRITE_STATS +#include +#endif + +#define QM358_SPI_SH_READY_CMD_BIT_MASK \ + (SPI_SH_READY_CMD_BIT_MASK >> 4 | SPI_SH_READY_CMD_BIT_MASK) + +#define SB_OK 0x3E9 + +enum qm358xx_rom_cmd_e { + /*! Defines the null placeholder for no command code. */ + CMD_NONE = 0x0u, + /*! Defines the get chip version command code. */ + CMD_GET_CHIP_VERSION = 0x2u, + /*! Defines the get soc info command code. */ + CMD_GET_SOC_INFO, + /*! Defines the load firmware package command code. */ + CMD_LOAD_FW_PKG, + /*! Defines the load asset package command code. */ + CMD_LOAD_ASSET_PKG, + /*! Defines the load secure debug package command code. */ + CMD_LOAD_SECURE_DEBUG_PKG, + /*! Defines the erase secure debug package command code. */ + CMD_ERASE_SECURE_DEBUG_PKG, + /*! Defines the load SRAM firmware command code. */ + CMD_LOAD_SRAM, + /*! Defines the run test mode command code. */ + CMD_RUN_TEST_MODE, + /*! Defines the generate master secret and uuid command code. */ + CMD_GENERATE_MASTER_SECRET_AND_UUID, +}; + +/*! Defines the null placeholder for no sub-command code. */ +#define SUB_CMD_NONE 0x0 +/*! Defines the sub-command code base value. */ +#define SUB_CMD_BASE 0x0 + +enum qm358xx_rom_sub_cmd_load_fw_pkg_e { + /*! Defines the load firmware package header sub-command code. */ + SUB_CMD_LOAD_FW_PKG_HDR = SUB_CMD_BASE, + /*! Defines the load firmware package payload chunk sub-command code. */ + SUB_CMD_LOAD_FW_PKG_PAYLOAD_CHUNK +}; + +enum qm358xx_rom_sub_cmd_load_sram_e { + /*! Defines the load SRAM file size sub-command code. */ + SUB_CMD_LOAD_SRAM_FILE_SIZE = SUB_CMD_BASE, + /*! Defines the load SRAM image data sub-command code. */ + SUB_CMD_LOAD_SRAM_IMG_DATA +}; + +/*! Defines the null placeholder for no response code. */ +#define RESP_NONE 0x0000 +/*! Defines the response code base value. */ +#define RESP_OK 0xA000 +/*! Defines the response code error base value. */ +#define RESP_ERR_BASE 0xE000 + +/*! Defines the command mode ready response code. */ +#define RESP_CMD_MODE_READY RESP_OK + +/*! Defines the invalid command response code. */ +#define RESP_INVALID_CMD_BASE (RESP_ERR_BASE + 0x100) + +enum qm358xx_rom_invalid_cmd_rsp_e { + /*! Defines the invalid command response code. */ + RESP_INVALID_CMD = (RESP_INVALID_CMD_BASE + 0x1u), + /*! Defines the invalid I3C command response code. */ + RESP_INVALID_I3C_CMD +}; + +/*! Defines the soc info error response code base. */ +#define RESP_GET_SOC_INFO_ERR_BASE (RESP_ERR_BASE + 0x200) +/*! Defines the soc info failed response code. */ +#define RESP_GET_SOC_INFO_FAILED (RESP_GET_SOC_INFO_ERR_BASE + 0x1) + +/*! Defines the load asset package error base code. */ +#define RESP_LOAD_ASSET_PKG_ERR_BASE (RESP_ERR_BASE + 0x300) + +enum qm358xx_rom_load_asset_pkg_rsp_e { + /*! Defines the load asset package failed response code. */ + RESP_LOAD_ASSET_PKG_FAILED = (RESP_LOAD_ASSET_PKG_ERR_BASE + 0x1), + /*! Defines the load asset package failed response code. Cmd not allowed for this LCS */ + RESP_LOAD_ASSET_PKG_CMD_NOT_ALLOWED_FOR_CURRENT_LCS, + /*! Defines the load asset package invalid size response code. */ + RESP_LOAD_ASSET_PKG_INVALID_ASSET_SIZE +}; + +/*! Defines the load asset package error base code. */ +#define RESP_LOAD_FW_PKG_ERR_BASE (RESP_ERR_BASE + 0x400) + +enum qm358xx_rom_load_fw_pkg_rsp_e { + /*! Defines the load firmware package invalid command response code. */ + RESP_LOAD_FW_PKG_INVALID_CMD = (RESP_LOAD_FW_PKG_ERR_BASE + 0x1), + /*! Defines the load firmware package invalid sub-command response code. */ + RESP_LOAD_FW_PKG_INVALID_SUB_CMD, + /*! Defines the load firmware package invalid certificate size response code. */ + RESP_LOAD_FW_PKG_INVALID_CERT_SIZE, + /*! Defines the load firmware package header verification failed response code. */ + RESP_LOAD_FW_PKG_HDR_VERIFY_FAILED, + /*! Defines the load firmware package payload chunk verification failed response code. */ + RESP_LOAD_FW_PKG_PAYLOAD_CHUNK_VERIFY_FAILED, + /*! Defines the load firmware package set IV failed response code. */ + RESP_LOAD_FW_PKG_SET_IV_FAILED, + /*! Defines the load firmware package decrypt payload failed response code. */ + RESP_LOAD_FW_PKG_DECRYPT_PAYLOAD_FAILED, + /*! Defines the load firmware package image header verification failed response code. */ + RESP_LOAD_FW_PKG_VERIFY_IMG_HDR_FAILED, + /*! Defines the load firmware package secure boot package verification failed response code. */ + RESP_LOAD_FW_PKG_VERIFY_SB_PKG_FAILED, + /*! Defines the load firmware package secure boot package storing failed response code. */ + RESP_LOAD_FW_PKG_STORE_SB_PKG_FAILED, + /*! Defines the load firmware package image storing failed response code. */ + RESP_LOAD_FW_PKG_STORE_IMG_FAILED, + /*! Defines the load firmware package image verification failed response code. */ + RESP_LOAD_FW_PKG_VERIFY_IMG_FAILED, + /*! Defines the load firmware package invalid chunk size response code. */ + RESP_LOAD_FW_PKG_INVALID_CHUNK_SIZE, + /*! Defines the load firmware package secure boot package L2 encryption lock check failed response code. */ + RESP_LOAD_FW_PKG_LOCK_L2_ENC_CHECK_FAILED +}; + +/*! Defines the load to SRAM response code. Size is 0*/ +#define RESP_LOAD_SRAM_ERR_BASE (RESP_ERR_BASE + 0x500) + +enum qm358xx_rom_load_sram_rsp_e { + /*! Defines the load to SRAM response code. Size is 0*/ + RESP_LOAD_SRAM_SIZE_0 = (RESP_LOAD_SRAM_ERR_BASE + 1), + /*! Defines the load to SRAM response code. Size is too big */ + RESP_LOAD_SRAM_SIZE_TOO_BIG, + /*! Defines the load to SRAM response code. Wrong sub command */ + RESP_LOAD_SRAM_INVALID_SUB_CMD, + /*! Defines the load to SRAM response code. Cmd not allowed for this LCS */ + RESP_LOAD_SRAM_CMD_NOT_ALLOWED_FOR_CURRENT_LCS, + /*! Defines the load to SRAM got data more data than expected */ + RESP_LOAD_SRAM_GOT_DATA_MORE_THAN_EXPECTED, + /*! Defines the load to SRAM got wrong command */ + RESP_LOAD_SRAM_INVALID_CMD +}; +/*! Defines the run test error base code. */ +#define RESP_RUN_TEST_ERR_BASE (RESP_ERR_BASE + 0x600) + +enum qm358xx_rom_run_test_mode_rsp_e { + /*! Defines run test failed response code. */ + RESP_RUN_TEST_FAILED = (RESP_RUN_TEST_ERR_BASE + 0x1), + /*! Defines run test failed response code. Cmd not allowed for this LCS */ + RESP_RUN_TEST_CMD_NOT_ALLOWED_FOR_CURRENT_LCS +}; + +/*! Defines the secure debug package processing error base code. */ +#define RESP_LOAD_DEBUG_PKG_ERR_BASE (RESP_ERR_BASE + 0x700) + +enum qm358xx_rom_load_secure_dbg_pkg_rsp_e { + /*! Defines the load secure debug package failed response code. */ + RESP_LOAD_SEC_DEBUG_PKG_FAILED = (RESP_LOAD_DEBUG_PKG_ERR_BASE + 0x1), + /*! Defines the load secure debug package storing failed response code. */ + RESP_LOAD_SEC_DEBUG_PKG_STORE_FAILED, + /*! Defines the load secure debug package command not allowed for this LCS. */ + RESP_LOAD_SEC_DEBUG_PKG_NOT_ALLOWED_FOR_CURRENT_LCS, + /*! Defines the load secure debug package invalid size response code. */ + RESP_LOAD_SEC_DEBUG_PKG_INVALID_SIZE +}; + +/*! Defines the secure debug package erasing error base code. */ +#define RESP_ERASE_DEBUG_PKG_ERR_BASE (RESP_ERR_BASE + 0x800) +/*! Defines the erase secure debug package wiping failed response code. */ +#define RESP_ERASE_SEC_DEBUG_PKG_WIPE_FAILED \ + (RESP_ERASE_DEBUG_PKG_ERR_BASE + 0x1) + +/*! Defines the generate master secret and UUID error base code. */ +#define RESP_GENERATE_MASTER_SECRET_AND_UUID_ERR_BASE (RESP_ERR_BASE + 0xA00) + +enum qm358xx_rom_gen_secrets_rsp_e { + /*! Defines the generate master secret and UUID error during master secret generation. */ + RESP_GENERATE_MASTER_SECRET_ERROR = + (RESP_GENERATE_MASTER_SECRET_AND_UUID_ERR_BASE + 0x1), + /*! Defines the generate master secret and UUID error during UUID loading. */ + RESP_GENERATE_UUID_ERROR, + /*! Defines the generate master secret and UUID return code for when the UUID has already been generated. */ + RESP_GENERATE_UUID_ALREADY_GENERATED, + /*! Defines the generate master secret and UUID return code for when the current LCS does not allow the command. */ + RESP_GENERATE_MASTER_SECRETS_NOT_ALLOWED_LCS +}; + +/*! Defines the get chip version success response code. */ +#define RESP_GET_CHIP_VERSION_SUCCESS (RESP_OK + CMD_GET_CHIP_VERSION) +/*! Defines the soc info success response code. */ +#define RESP_GET_SOC_INFO_SUCCESS (RESP_OK + CMD_GET_SOC_INFO) +/*! Defines the load asset package completed successfully response code */ +#define RESP_LOAD_ASSET_PKG_SUCCESS (RESP_OK + CMD_LOAD_ASSET_PKG) +/*! Defines the load firmware package all images verified response code. */ +#define RESP_LOAD_FW_PKG_VERIFIED_IMGS_COMPLETED (RESP_OK + CMD_LOAD_FW_PKG) +/*! Defines the load secure debug package success response code. */ +#define RESP_LOAD_SEC_DEBUG_PKG_SUCCESS (RESP_OK + CMD_LOAD_SECURE_DEBUG_PKG) +/*! Defines the load secure debug package storing failed response code. */ +#define RESP_ERASE_SEC_DEBUG_PKG_SUCCESS (RESP_OK + CMD_ERASE_SECURE_DEBUG_PKG) +/*! Defines the generation of master secret and UUID success response code. */ +#define RESP_GENERATE_MASTER_SECRET_AND_UUID_SUCCESS \ + (RESP_OK + CMD_GENERATE_MASTER_SECRET_AND_UUID) + +/*! Command header structure typedef. */ +typedef struct { + union { + struct { + /*! Command code. */ + uint8_t cmd; + /*! Sub-command code. */ + uint8_t sub_cmd; + /*! Response code. */ + uint16_t resp; + }; + uint32_t all; + }; +} cmd_hdr_t; + +#define MAKE_CMD_HDR(c, sc, r) \ + (cmd_hdr_t) \ + { \ + .cmd = (c), .sub_cmd = (sc), .resp = (r) \ + } + +/*! Response header structure typedef. */ +typedef struct { + /*! Command header. */ + cmd_hdr_t cmd_hdr; + /*! Error code. */ + uint32_t err_code; +} resp_t; + +#define MAKE_RSP_HDR(ch, ec) \ + (resp_t) \ + { \ + .cmd_hdr = (ch), .err_code = (ec) \ + } + +#define CMD_MODE_READY_RSP_HDR \ + MAKE_RSP_HDR(MAKE_CMD_HDR(CMD_NONE, SUB_CMD_NONE, \ + RESP_CMD_MODE_READY), \ + SB_OK) + +#define CMD_GET_CHIP_VERSION_RSP_HDR \ + MAKE_RSP_HDR(MAKE_CMD_HDR(CMD_GET_CHIP_VERSION, SUB_CMD_NONE, \ + RESP_GET_CHIP_VERSION_SUCCESS), \ + SB_OK) + +#define CMD_GET_SOC_INFO_RSP_HDR \ + MAKE_RSP_HDR(MAKE_CMD_HDR(CMD_GET_SOC_INFO, SUB_CMD_NONE, \ + RESP_GET_SOC_INFO_SUCCESS), \ + SB_OK) + +#define CMD_GENERATE_MASTER_SECRET_AND_UUID_RSP_HDR \ + MAKE_RSP_HDR( \ + MAKE_CMD_HDR(CMD_GENERATE_MASTER_SECRET_AND_UUID, \ + SUB_CMD_NONE, \ + RESP_GENERATE_MASTER_SECRET_AND_UUID_SUCCESS), \ + SB_OK) + +#define CMD_LOAD_ASSET_PKG_RSP_HDR \ + MAKE_RSP_HDR(MAKE_CMD_HDR(CMD_LOAD_ASSET_PKG, SUB_CMD_NONE, \ + RESP_LOAD_ASSET_PKG_SUCCESS), \ + SB_OK) + +#define CMD_LOAD_FW_PKG_LOAD_PAYLOAD_CHUNK_RSP_HDR \ + MAKE_RSP_HDR(MAKE_CMD_HDR(CMD_LOAD_FW_PKG, \ + SUB_CMD_LOAD_FW_PKG_PAYLOAD_CHUNK, RESP_OK), \ + SB_OK) + +#define CMD_LOAD_FW_PKG_LOAD_PAYLOAD_CHUNK_COMPLETE_RSP_HDR \ + MAKE_RSP_HDR(MAKE_CMD_HDR(CMD_LOAD_FW_PKG, \ + SUB_CMD_LOAD_FW_PKG_PAYLOAD_CHUNK, \ + RESP_LOAD_FW_PKG_VERIFIED_IMGS_COMPLETED), \ + SB_OK) + +#define CMD_LOAD_SECURE_DEBUG_PKG_RSP_HDR \ + MAKE_RSP_HDR(MAKE_CMD_HDR(CMD_LOAD_SECURE_DEBUG_PKG, SUB_CMD_NONE, \ + RESP_LOAD_SEC_DEBUG_PKG_SUCCESS), \ + SB_OK) + +#define CMD_ERASE_SECURE_DEBUG_PKG_RSP_HDR \ + MAKE_RSP_HDR(MAKE_CMD_HDR(CMD_ERASE_SECURE_DEBUG_PKG, SUB_CMD_NONE, \ + RESP_ERASE_SEC_DEBUG_PKG_SUCCESS), \ + SB_OK) + +#define CMD_LOAD_SRAM_RSP_HDR \ + MAKE_RSP_HDR(MAKE_CMD_HDR(CMD_LOAD_SRAM, SUB_CMD_LOAD_SRAM_IMG_DATA, \ + RESP_OK), \ + SB_OK) + +#define COMPARE_RSP(act_rsp, exp_rsp) memcmp(act_rsp, exp_rsp, sizeof(resp_t)) + +#define RSP_HDR_FMT_STR "{ .cmd = %#x, .sub_cmd = %#x, .resp = %#x, err = %#x }" +#define RSP_HDR_EXP(r) \ + (r)->cmd_hdr.cmd, (r)->cmd_hdr.sub_cmd, (r)->cmd_hdr.resp, (r)->err_code + +static int gen_secrets(struct qmrom_handle *handle); +static int load_asset_pkg(struct qmrom_handle *handle, + const struct firmware *asset_pkg); +static int load_fw_pkg(struct qmrom_handle *handle, + const struct firmware *fw_pkg); +static int load_secure_debug_pkg(struct qmrom_handle *handle, + struct firmware *dbg_pkg); +static int erase_secure_debug_pkg(struct qmrom_handle *handle); +static int run_test_mode(struct qmrom_handle *handle); +static int load_sram_fw(struct qmrom_handle *handle, + const struct firmware *sram_fw); + +#define qm358xx_rom_pre_read(h) \ + ({ \ + int rc; \ + qmrom_spi_wait_for_ready_line((h)->ss_rdy_handle, \ + QM358_SPI_READY_TIMEOUT_MS); \ + rc = qmrom_pre_read((h)); \ + rc; \ + }) +#define qm358xx_rom_read(h) \ + ({ \ + int rc; \ + qmrom_spi_wait_for_ready_line((h)->ss_rdy_handle, \ + QM358_SPI_READY_TIMEOUT_MS); \ + rc = qmrom_read((h)); \ + rc; \ + }) +#define qm358xx_rom_write_cmd(h, cmd) \ + ({ \ + int rc; \ + qmrom_spi_wait_for_ready_line((h)->ss_rdy_handle, \ + QM358_SPI_READY_TIMEOUT_MS); \ + rc = _qm358xx_rom_write_cmd((h), (cmd)); \ + rc; \ + }) +#define qm358xx_rom_write_size_cmd(h, cmd, ds, d) \ + ({ \ + int rc; \ + qmrom_spi_wait_for_ready_line((h)->ss_rdy_handle, \ + QM358_SPI_READY_TIMEOUT_MS); \ + rc = _qm358xx_rom_write_size_cmd((h), (cmd), (ds), (d)); \ + rc; \ + }) + +static int _qm358xx_rom_write_cmd(struct qmrom_handle *handle, cmd_hdr_t *cmd) +{ + handle->hstc->all = 0; + handle->hstc->host_flags.write = 1; + handle->hstc->ul = 1; + handle->hstc->len = sizeof(cmd_hdr_t); + memcpy(&handle->hstc->payload[0], cmd, sizeof(cmd_hdr_t)); + + return qmrom_spi_transfer(handle->spi_handle, (char *)handle->sstc, + (const char *)handle->hstc, + sizeof(struct stc) + handle->hstc->len); +} + +static int _qm358xx_rom_write_size_cmd(struct qmrom_handle *handle, + cmd_hdr_t *cmd, uint16_t data_size, + const char *data) +{ + handle->hstc->all = 0; + handle->hstc->host_flags.write = 1; + handle->hstc->ul = 1; + handle->hstc->len = sizeof(cmd_hdr_t) + data_size; + memcpy(&handle->hstc->payload[0], cmd, sizeof(cmd_hdr_t)); + memcpy(&handle->hstc->payload[sizeof(cmd_hdr_t)], data, data_size); + + return qmrom_spi_transfer(handle->spi_handle, (char *)handle->sstc, + (const char *)handle->hstc, + sizeof(struct stc) + handle->hstc->len); +} + +static void qm358xx_rom_poll_soc(struct qmrom_handle *handle) +{ + int retries = handle->comms_retries; + memset(handle->hstc, 0, sizeof(struct stc)); + handle->sstc->raw_flags = 0; + do { + int rc = + qmrom_spi_wait_for_ready_line(handle->ss_rdy_handle, 0); + if (rc) { + LOG_ERR("%s qmrom_spi_wait_for_ready_line failed\n", + __func__); + continue; + } + qmrom_spi_transfer(handle->spi_handle, (char *)handle->sstc, + (const char *)handle->hstc, + sizeof(struct stc) + handle->hstc->len); + } while (retries-- && handle->sstc->raw_flags == 0); +} + +static int qm358xx_rom_wait_ready(struct qmrom_handle *handle) +{ + int retries = handle->comms_retries; + + qm358xx_rom_poll_soc(handle); + + /* handle->sstc has been updated */ + while (retries-- && + !(handle->sstc->raw_flags & QM358_SPI_SH_READY_CMD_BIT_MASK)) { + if (handle->sstc->soc_flags.out_waiting) { + qm358xx_rom_pre_read(handle); + qm358xx_rom_read(handle); + } else if (handle->sstc->soc_flags.out_active) { + return qm358xx_rom_read(handle); + } else + qm358xx_rom_poll_soc(handle); + } + + return handle->sstc->raw_flags & QM358_SPI_SH_READY_CMD_BIT_MASK ? + 0 : + SPI_ERR_WAIT_READY_TIMEOUT; +} + +static int qm358xx_rom_poll_cmd_resp(struct qmrom_handle *handle) +{ + int retries = handle->comms_retries; + + qm358xx_rom_poll_soc(handle); + do { + if (handle->sstc->soc_flags.out_waiting) { + qm358xx_rom_pre_read(handle); + return qm358xx_rom_read(handle); + } else + qm358xx_rom_poll_soc(handle); + } while (retries--); + if (retries <= 0) + LOG_ERR("%s failed after %d replies\n", __func__, + handle->comms_retries); + + return retries > 0 ? 0 : -1; +} + +int qm358xx_rom_probe_device(struct qmrom_handle *handle) +{ + int rc; + + handle->is_be = false; + + if (handle->spi_speed == 0) + qmrom_spi_set_freq(DEFAULT_SPI_CLOCKRATE); + else + qmrom_spi_set_freq(handle->spi_speed); + + rc = qmrom_reboot_bootloader(handle); + if (rc) { + LOG_ERR("%s: cannot reset the device...\n", __func__); + return rc; + } + + rc = qm358xx_rom_wait_ready(handle); + if (rc) { + LOG_INFO("%s: maybe not a QM358xx device\n", __func__); + return rc; + } + + LOG_DBG("%s: checking for command mode ready response\n", __func__); + qm358xx_rom_poll_cmd_resp(handle); + if (COMPARE_RSP(&handle->sstc->payload[0], &CMD_MODE_READY_RSP_HDR) != + 0) { + LOG_ERR("%s: Expected: " RSP_HDR_FMT_STR + " Received: " RSP_HDR_FMT_STR "\n", + __func__, RSP_HDR_EXP(&CMD_MODE_READY_RSP_HDR), + RSP_HDR_EXP((resp_t *)&handle->sstc->payload[0])); + return SPI_PROTO_WRONG_RESP; + } + + LOG_DBG("%s: sending CMD_GET_CHIP_VERSION command\n", __func__); + rc = qm358xx_rom_write_cmd(handle, + &MAKE_CMD_HDR(CMD_GET_CHIP_VERSION, + SUB_CMD_NONE, RESP_NONE)); + qm358xx_rom_poll_cmd_resp(handle); + if (COMPARE_RSP(&handle->sstc->payload[0], + &CMD_GET_CHIP_VERSION_RSP_HDR) != 0) { + LOG_ERR("%s: Expected: " RSP_HDR_FMT_STR + " Received: " RSP_HDR_FMT_STR "\n", + __func__, RSP_HDR_EXP(&CMD_GET_CHIP_VERSION_RSP_HDR), + RSP_HDR_EXP((resp_t *)&handle->sstc->payload[0])); + return SPI_PROTO_WRONG_RESP; + } + + handle->chip_rev = + bswap_16(SSTC2UINT16(handle, + CHIP_VERSION_CHIP_REV_PAYLOAD_OFFSET)) & + 0xFF; + handle->device_version = bswap_16( + SSTC2UINT16(handle, CHIP_VERSION_DEV_REV_PAYLOAD_OFFSET)); + if (handle->chip_rev != CHIP_REVISION_A0) { + LOG_ERR("%s: wrong chip revision %#x\n", __func__, + handle->chip_rev); + handle->chip_rev = -1; + return -1; + } + + rc = qm358xx_rom_wait_ready(handle); + if (rc) { + LOG_ERR("%s: hmm something went wrong!!!\n", __func__); + return rc; + } + + rc = qm358xx_rom_write_cmd(handle, + &MAKE_CMD_HDR(CMD_GET_SOC_INFO, SUB_CMD_NONE, + RESP_NONE)); + qm358xx_rom_poll_cmd_resp(handle); + if (COMPARE_RSP(&handle->sstc->payload[0], &CMD_GET_SOC_INFO_RSP_HDR) != + 0) { + LOG_ERR("%s: Expected: " RSP_HDR_FMT_STR + " Received: " RSP_HDR_FMT_STR "\n", + __func__, RSP_HDR_EXP(&CMD_GET_SOC_INFO_RSP_HDR), + RSP_HDR_EXP((resp_t *)&handle->sstc->payload[0])); + return SPI_PROTO_WRONG_RESP; + } + + /* skip the resp header bytes */ + memcpy(&handle->qm358xx_soc_info, + &handle->sstc->payload[sizeof(resp_t)], + sizeof(handle->qm358xx_soc_info)); + + /* Set device type */ + handle->dev_gen = DEVICE_GEN_QM358XX; + /* Set rom ops */ + handle->qm358xx_rom_ops.gen_secrets = gen_secrets; + handle->qm358xx_rom_ops.load_asset_pkg = load_asset_pkg; + handle->qm358xx_rom_ops.load_fw_pkg = load_fw_pkg; + handle->qm358xx_rom_ops.load_secure_dbg_pkg = load_secure_debug_pkg; + handle->qm358xx_rom_ops.erase_secure_dbg_pkg = erase_secure_debug_pkg; + handle->qm358xx_rom_ops.load_sram_fw = load_sram_fw; + handle->qm358xx_rom_ops.run_test_mode = run_test_mode; + + return 0; +} + +#ifdef QM358XX_WRITE_STATS +static uint64_t total_bytes, total_time_ns; +static uint32_t max_write_time_ns, min_write_time_ns = ~0; + +static void update_write_max_chunk_stats(ktime_t start_time) +{ + uint64_t elapsed_time_ns; + + total_bytes += CHUNK_SIZE; + elapsed_time_ns = ktime_to_ns(ktime_sub(ktime_get(), start_time)); + total_time_ns += elapsed_time_ns; + if (elapsed_time_ns > max_write_time_ns) + max_write_time_ns = elapsed_time_ns; + if (elapsed_time_ns < min_write_time_ns) + min_write_time_ns = elapsed_time_ns; +} + +static void dump_stats(void) +{ + uint32_t nb_chunks = total_bytes / CHUNK_SIZE; + LOG_INFO( + "QM358XX flashing time stats: %llu bytes over %llu us (chunk size %u, write timings: mean %u us, min %u us, max %u us)\n", + total_bytes, total_time_ns / 1000, CHUNK_SIZE, + (uint32_t)((total_time_ns / nb_chunks) / 1000), + min_write_time_ns / 1000, max_write_time_ns / 1000); +} +#endif + +static int gen_secrets(struct qmrom_handle *handle) +{ + int rc; + + LOG_DBG("%s: starting...\n", __func__); + + rc = qm358xx_rom_wait_ready(handle); + if (rc) + return rc; + + LOG_DBG("%s: sending CMD_GENERATE_MASTER_SECRET_AND_UUID command\n", + __func__); + rc = qm358xx_rom_write_cmd( + handle, &MAKE_CMD_HDR(CMD_GENERATE_MASTER_SECRET_AND_UUID, + SUB_CMD_NONE, RESP_NONE)); + qm358xx_rom_poll_cmd_resp(handle); + if (COMPARE_RSP(&handle->sstc->payload[0], + &CMD_GENERATE_MASTER_SECRET_AND_UUID_RSP_HDR) != 0) { + LOG_ERR("%s: Expected: " RSP_HDR_FMT_STR + " Received: " RSP_HDR_FMT_STR "\n", + __func__, + RSP_HDR_EXP( + &CMD_GENERATE_MASTER_SECRET_AND_UUID_RSP_HDR), + RSP_HDR_EXP((resp_t *)&handle->sstc->payload[0])); + return SPI_PROTO_WRONG_RESP; + } + + return 0; +} + +bool verify_fw_pkg_img_chunk(const char *data) +{ + fw_pkg_payload_chunk_hdr_t *hdr = (fw_pkg_payload_chunk_hdr_t *)data; + if ((hdr->magic != CRYPTO_FIRMWARE_CHUNK_MAGIC_VALUE) || + (hdr->version != CRYPTO_FIRMWARE_CHUNK_VERSION) || + (hdr->length > CHUNK_PAYLOAD_MAX_SIZE)) { + return false; + } + LOG_DBG("%s, image chunk: hdr->magic: %4s hdr->version: %#x hdr->length: %#x\n", + __func__, (char *)&hdr->magic, hdr->version, hdr->length); + + return true; +} + +static int load_fw_pkg(struct qmrom_handle *handle, + const struct firmware *fw_pkg) +{ + int rc, sent = 0, init_tx = 0; + const char *bin_data = (const char *)fw_pkg->data; + char *img_chunk = NULL; + bool bin_data_pre_chunked = false; +#ifdef QM358XX_WRITE_STATS + ktime_t start_time; +#endif + LOG_DBG("%s: starting...\n", __func__); + + if (((uint32_t) * (uint32_t *)bin_data != + CRYPTO_FIRMWARE_PACK_MAGIC_VALUE) || + (fw_pkg->size < + (CRYPTO_FIRMWARE_PACK_HEADER_SIZE + + CRYPTO_FIRMWARE_IMAGE_CERT_SIZE + CRYPTO_IMAGES_CERT_PKG_SIZE + + CRYPTO_FIRMWARE_CHUNK_MIN_SIZE))) { + LOG_ERR("%s: Invalid firmware package.\n", __func__); + rc = -EINVAL; + goto exit; + } + + rc = qm358xx_rom_wait_ready(handle); + if (rc) + goto exit; + + LOG_DBG("%s: sending CMD_LOAD_FW_PKG:HEADER command\n", __func__); + rc = qm358xx_rom_write_size_cmd( + handle, + &MAKE_CMD_HDR(CMD_LOAD_FW_PKG, SUB_CMD_LOAD_FW_PKG_HDR, + RESP_NONE), + CRYPTO_FIRMWARE_PACK_HEADER_SIZE, bin_data); + qm358xx_rom_poll_cmd_resp(handle); + if (COMPARE_RSP(&handle->sstc->payload[0], + &CMD_LOAD_FW_PKG_LOAD_PAYLOAD_CHUNK_RSP_HDR) != 0) { + LOG_ERR("%s: Expected: " RSP_HDR_FMT_STR + " Received: " RSP_HDR_FMT_STR "\n", + __func__, + RSP_HDR_EXP( + &CMD_LOAD_FW_PKG_LOAD_PAYLOAD_CHUNK_RSP_HDR), + RSP_HDR_EXP((resp_t *)&handle->sstc->payload[0])); + rc = SPI_PROTO_WRONG_RESP; + goto exit; + } + + sent += CRYPTO_FIRMWARE_PACK_HEADER_SIZE; + bin_data += CRYPTO_FIRMWARE_PACK_HEADER_SIZE; + + qmrom_alloc(img_chunk, CHUNK_SIZE); + if (!img_chunk) { + LOG_ERR("Image chunk buffer allocation failure\n"); + rc = -1; + goto exit; + } + ((fw_pkg_payload_chunk_hdr_t *)img_chunk)->magic = + CRYPTO_FIRMWARE_CHUNK_MAGIC_VALUE; + ((fw_pkg_payload_chunk_hdr_t *)img_chunk)->version = + CRYPTO_FIRMWARE_CHUNK_VERSION; + + while (sent < fw_pkg->size) { + uint32_t tx_bytes = 0; + const char *payload = NULL; + if (verify_fw_pkg_img_chunk(bin_data)) { + bin_data_pre_chunked = true; + tx_bytes = + ((fw_pkg_payload_chunk_hdr_t *)bin_data)->length; + if (tx_bytes > CHUNK_SIZE) { + LOG_ERR("Invalid payload chunk size\n"); + rc = -1; + goto exit; + } + payload = bin_data; + } else { + if (bin_data_pre_chunked) { + LOG_ERR("Invalid pre-chunked package\n"); + rc = -1; + goto exit; + } + tx_bytes = fw_pkg->size - sent; + if (tx_bytes > CHUNK_PAYLOAD_MAX_SIZE) + tx_bytes = CHUNK_PAYLOAD_MAX_SIZE; + ((fw_pkg_payload_chunk_hdr_t *)img_chunk)->length = + tx_bytes; + memcpy(&img_chunk[CRYPTO_FIRMWARE_CHUNK_HDR_SIZE], + bin_data, tx_bytes); + payload = img_chunk; + } + + LOG_DBG("%s: sending CMD_LOAD_FW_PKG:PAYLOAD_CHUNK command with %" PRIu32 + " bytes\n", + __func__, tx_bytes); +#ifdef QM358XX_WRITE_STATS + start_time = ktime_get(); +#endif + rc = qm358xx_rom_write_size_cmd( + handle, + &MAKE_CMD_HDR(CMD_LOAD_FW_PKG, + SUB_CMD_LOAD_FW_PKG_PAYLOAD_CHUNK, + RESP_NONE), + CRYPTO_FIRMWARE_CHUNK_HDR_SIZE + tx_bytes, payload); + if (rc) + goto exit; + sent += tx_bytes; + bin_data += tx_bytes; + if (bin_data_pre_chunked) { + sent += CRYPTO_FIRMWARE_CHUNK_HDR_SIZE; + bin_data += CRYPTO_FIRMWARE_CHUNK_HDR_SIZE; + } + if (!init_tx) { + init_tx = 1; + continue; + } + qm358xx_rom_poll_cmd_resp(handle); +#ifdef QM358XX_WRITE_STATS + if (tx_bytes == CHUNK_SIZE) + update_write_max_chunk_stats(start_time); +#endif + if (COMPARE_RSP(&handle->sstc->payload[0], + &CMD_LOAD_FW_PKG_LOAD_PAYLOAD_CHUNK_RSP_HDR) == + 0) { + continue; + } else if (COMPARE_RSP( + &handle->sstc->payload[0], + &CMD_LOAD_FW_PKG_LOAD_PAYLOAD_CHUNK_COMPLETE_RSP_HDR) == + 0) { + LOG_INFO("%s: flashing done, quitting now\n", __func__); + rc = 0; + goto exit; + } else { + LOG_ERR("%s: Expected: " RSP_HDR_FMT_STR + " OR " RSP_HDR_FMT_STR + " Received: " RSP_HDR_FMT_STR "\n", + __func__, + RSP_HDR_EXP( + &CMD_LOAD_FW_PKG_LOAD_PAYLOAD_CHUNK_RSP_HDR), + RSP_HDR_EXP( + &CMD_LOAD_FW_PKG_LOAD_PAYLOAD_CHUNK_COMPLETE_RSP_HDR), + RSP_HDR_EXP( + (resp_t *)&handle->sstc->payload[0])); + rc = SPI_PROTO_WRONG_RESP; + goto exit; + } + } + qm358xx_rom_poll_cmd_resp(handle); + if (COMPARE_RSP(&handle->sstc->payload[0], + &CMD_LOAD_FW_PKG_LOAD_PAYLOAD_CHUNK_COMPLETE_RSP_HDR) != + 0) { + LOG_ERR("%s: Expected: " RSP_HDR_FMT_STR + " Received: " RSP_HDR_FMT_STR "\n", + __func__, + RSP_HDR_EXP( + &CMD_LOAD_FW_PKG_LOAD_PAYLOAD_CHUNK_COMPLETE_RSP_HDR), + RSP_HDR_EXP((resp_t *)&handle->sstc->payload[0])); + rc = SPI_PROTO_WRONG_RESP; + } +exit: + if (img_chunk) + qmrom_free(img_chunk); + return rc; +} + +static int load_asset_pkg(struct qmrom_handle *handle, + const struct firmware *asset_pkg) +{ + int rc; + + LOG_DBG("%s: starting...\n", __func__); + rc = qm358xx_rom_wait_ready(handle); + if (rc) + return rc; + + LOG_DBG("%s: sending CMD_LOAD_ASSET_PKG command\n", __func__); + rc = qm358xx_rom_write_size_cmd( + handle, + &MAKE_CMD_HDR(CMD_LOAD_ASSET_PKG, SUB_CMD_NONE, RESP_NONE), + asset_pkg->size, (const char *)asset_pkg->data); + qm358xx_rom_poll_cmd_resp(handle); + if (COMPARE_RSP(&handle->sstc->payload[0], + &CMD_LOAD_ASSET_PKG_RSP_HDR) != 0) { + LOG_ERR("%s: Expected: " RSP_HDR_FMT_STR + " Received: " RSP_HDR_FMT_STR "\n", + __func__, RSP_HDR_EXP(&CMD_LOAD_ASSET_PKG_RSP_HDR), + RSP_HDR_EXP((resp_t *)&handle->sstc->payload[0])); + return SPI_PROTO_WRONG_RESP; + } + + return 0; +} + +static int load_secure_debug_pkg(struct qmrom_handle *handle, + struct firmware *dbg_pkg) +{ + int rc; + + LOG_DBG("%s: starting...\n", __func__); + rc = qm358xx_rom_wait_ready(handle); + if (rc) + return rc; + + LOG_DBG("%s: sending CMD_LOAD_SECURE_DEBUG_PKG command\n", __func__); + rc = qm358xx_rom_write_size_cmd(handle, + &MAKE_CMD_HDR(CMD_LOAD_SECURE_DEBUG_PKG, + SUB_CMD_NONE, RESP_NONE), + dbg_pkg->size, + (const char *)dbg_pkg->data); + qm358xx_rom_poll_cmd_resp(handle); + if (COMPARE_RSP(&handle->sstc->payload[0], + &CMD_LOAD_SECURE_DEBUG_PKG_RSP_HDR) != 0) { + LOG_ERR("%s: Expected: " RSP_HDR_FMT_STR + " Received: " RSP_HDR_FMT_STR "\n", + __func__, + RSP_HDR_EXP(&CMD_LOAD_SECURE_DEBUG_PKG_RSP_HDR), + RSP_HDR_EXP((resp_t *)&handle->sstc->payload[0])); + return SPI_PROTO_WRONG_RESP; + } + + return 0; +} + +static int erase_secure_debug_pkg(struct qmrom_handle *handle) +{ + int rc; + + LOG_DBG("%s: starting...\n", __func__); + + rc = qm358xx_rom_wait_ready(handle); + if (rc) + return rc; + + LOG_DBG("%s: sending CMD_ERASE_SECURE_DEBUG_PKG command\n", __func__); + rc = qm358xx_rom_write_cmd(handle, + &MAKE_CMD_HDR(CMD_ERASE_SECURE_DEBUG_PKG, + SUB_CMD_NONE, RESP_NONE)); + qm358xx_rom_poll_cmd_resp(handle); + if (COMPARE_RSP(&handle->sstc->payload[0], + &CMD_ERASE_SECURE_DEBUG_PKG_RSP_HDR) != 0) { + LOG_ERR("%s: Expected: " RSP_HDR_FMT_STR + " Received: " RSP_HDR_FMT_STR "\n", + __func__, + RSP_HDR_EXP(&CMD_ERASE_SECURE_DEBUG_PKG_RSP_HDR), + RSP_HDR_EXP((resp_t *)&handle->sstc->payload[0])); + return SPI_PROTO_WRONG_RESP; + } + + return 0; +} + +static int run_test_mode(struct qmrom_handle *handle) +{ + int rc; + + LOG_DBG("%s: starting...\n", __func__); + + if ((handle->qm358xx_soc_info.lcs_state != LCS_TEST) && + (handle->qm358xx_soc_info.lcs_state != LCS_RMA)) { + LOG_ERR("%s: Test mode only available in TEST and RMA LCS.\n", + __func__); + return -1; + } + + rc = qm358xx_rom_wait_ready(handle); + if (rc) + return rc; + + LOG_DBG("%s: sending CMD_RUN_TEST_MODE command\n", __func__); + rc = qm358xx_rom_write_cmd(handle, + &MAKE_CMD_HDR(CMD_RUN_TEST_MODE, + SUB_CMD_NONE, RESP_NONE)); + + return 0; +} + +static int load_sram_fw(struct qmrom_handle *handle, + const struct firmware *sram_fw) +{ + int rc, sent = 0; + const char *bin_data = (const char *)sram_fw->data; +#ifdef QM358XX_WRITE_STATS + ktime_t start_time; +#endif + LOG_DBG("%s: starting...\n", __func__); + rc = qm358xx_rom_wait_ready(handle); + if (rc) + return rc; + + LOG_DBG("%s: sending CMD_LOAD_SRAM:FILE_SIZE command\n", __func__); + rc = qm358xx_rom_write_size_cmd( + handle, + &MAKE_CMD_HDR(CMD_LOAD_SRAM, SUB_CMD_LOAD_SRAM_FILE_SIZE, + RESP_NONE), + sizeof(uint32_t), (const char *)&sram_fw->size); + qm358xx_rom_poll_cmd_resp(handle); + if (COMPARE_RSP(&handle->sstc->payload[0], &CMD_LOAD_SRAM_RSP_HDR) != + 0) { + LOG_ERR("%s: Expected: " RSP_HDR_FMT_STR + " Received: " RSP_HDR_FMT_STR "\n", + __func__, RSP_HDR_EXP(&CMD_LOAD_SRAM_RSP_HDR), + RSP_HDR_EXP((resp_t *)&handle->sstc->payload[0])); + return SPI_PROTO_WRONG_RESP; + } + + while (sent < sram_fw->size) { + uint32_t tx_bytes = sram_fw->size - sent; + if (tx_bytes > CHUNK_SIZE) + tx_bytes = CHUNK_SIZE; + + LOG_DBG("%s: sending CMD_LOAD_SRAM:IMG_DATA command with %" PRIu32 + " bytes\n", + __func__, tx_bytes); +#ifdef QM358XX_WRITE_STATS + start_time = ktime_get(); +#endif + rc = qm358xx_rom_write_size_cmd( + handle, + &MAKE_CMD_HDR(CMD_LOAD_SRAM, SUB_CMD_LOAD_SRAM_IMG_DATA, + RESP_NONE), + tx_bytes, bin_data); + if (rc) + return rc; + sent += tx_bytes; + bin_data += tx_bytes; + if (sent == sram_fw->size) { + LOG_INFO("%s: flashing done, quitting now\n", __func__); + break; + } + qm358xx_rom_poll_cmd_resp(handle); +#ifdef QM358XX_WRITE_STATS + if (tx_bytes == CHUNK_SIZE) + update_write_max_chunk_stats(start_time); +#endif + if (COMPARE_RSP(&handle->sstc->payload[0], + &CMD_LOAD_SRAM_RSP_HDR) != 0) { + LOG_ERR("%s: Expected: " RSP_HDR_FMT_STR + " Received: " RSP_HDR_FMT_STR "\n", + __func__, RSP_HDR_EXP(&CMD_LOAD_SRAM_RSP_HDR), + RSP_HDR_EXP( + (resp_t *)&handle->sstc->payload[0])); + return SPI_PROTO_WRONG_RESP; + } + } + + return 0; +} + +int qm358xx_rom_gen_secrets(struct qmrom_handle *handle) +{ + if (!handle->qm358xx_rom_ops.gen_secrets) { + LOG_ERR("%s: generate secrets not supported on this device\n", + __func__); + return -EINVAL; + } + return handle->qm358xx_rom_ops.gen_secrets(handle); +} + +int qm358xx_rom_load_asset_pkg(struct qmrom_handle *handle, + struct firmware *asset_pkg) +{ + if (!handle->qm358xx_rom_ops.load_asset_pkg) { + LOG_ERR("%s: loading asset packages not supported on this device\n", + __func__); + return -EINVAL; + } + return handle->qm358xx_rom_ops.load_asset_pkg(handle, asset_pkg); +} + +int qm358xx_rom_load_fw_pkg(struct qmrom_handle *handle, + struct firmware *fw_pkg) +{ + if (!handle->qm358xx_rom_ops.load_fw_pkg) { + LOG_ERR("%s: loading firmware packages not supported on this device\n", + __func__); + return -EINVAL; + } + return handle->qm358xx_rom_ops.load_fw_pkg(handle, fw_pkg); +} + +int qm358xx_rom_load_secure_dbg_pkg(struct qmrom_handle *handle, + struct firmware *dbg_pkg) +{ + if (!handle->qm358xx_rom_ops.load_secure_dbg_pkg) { + LOG_ERR("%s: loading secure debug packages not supported on this device\n", + __func__); + return -EINVAL; + } + return handle->qm358xx_rom_ops.load_secure_dbg_pkg(handle, dbg_pkg); +} + +int qm358xx_rom_erase_secure_dbg_pkg(struct qmrom_handle *handle) +{ + if (!handle->qm358xx_rom_ops.erase_secure_dbg_pkg) { + LOG_ERR("%s: erasing secure debug packages not supported on this device\n", + __func__); + return -EINVAL; + } + return handle->qm358xx_rom_ops.erase_secure_dbg_pkg(handle); +} + +int qm358xx_rom_run_test_mode(struct qmrom_handle *handle) +{ + if (!handle->qm358xx_rom_ops.run_test_mode) { + LOG_ERR("%s: test mode not supported on this device\n", + __func__); + return -EINVAL; + } + return handle->qm358xx_rom_ops.run_test_mode(handle); +} + +int qm358xx_rom_load_sram_fw(struct qmrom_handle *handle, + struct firmware *sram_fw) +{ + if (!handle->qm358xx_rom_ops.load_sram_fw) { + LOG_ERR("%s: loading SRAM firmware not supported on this device\n", + __func__); + return -EINVAL; + } + return handle->qm358xx_rom_ops.load_sram_fw(handle, sram_fw); +} diff --git a/drivers/uwb/qm35/libqmrom/src/qmrom_common.c b/drivers/uwb/qm35/libqmrom/src/qmrom_common.c new file mode 100644 index 000000000000..314ba2d6af1c --- /dev/null +++ b/drivers/uwb/qm35/libqmrom/src/qmrom_common.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 +/* + * Copyright 2022 Qorvo US, Inc. + * + */ + +#include +#include +#include +#include +#include +#include + +int qm357xx_rom_probe_device(struct qmrom_handle *handle); +int qm358xx_rom_probe_device(struct qmrom_handle *handle); + +static void qmrom_free_stcs(struct qmrom_handle *h) +{ + if (h->hstc) + qmrom_free(h->hstc); + if (h->sstc) + qmrom_free(h->sstc); +} + +#ifdef CHECK_STCS +void check_stcs(const char *func, int line, struct qmrom_handle *h) +{ + uint32_t *buff = (uint32_t *)h->hstc; + if (buff[MAX_STC_FRAME_LEN / sizeof(uint32_t)] != 0xfeeddeef) { + LOG_ERR("%s:%d - hstc %pK corrupted\n", func, line, + (void *)h->hstc); + } else { + LOG_ERR("%s:%d - hstc %pK safe\n", func, line, (void *)h->hstc); + } + buff = (uint32_t *)h->sstc; + if (buff[MAX_STC_FRAME_LEN / sizeof(uint32_t)] != 0xfeeddeef) { + LOG_ERR("%s:%d - sstc %pK corrupted\n", func, line, + (void *)h->sstc); + } else { + LOG_ERR("%s:%d - sstc %pK safe\n", func, line, (void *)h->sstc); + } +} +#endif + +static int qmrom_allocate_stcs(struct qmrom_handle *h) +{ + int rc = 0; + uint8_t *tx_buf = NULL, *rx_buf = NULL; + + qmrom_alloc(tx_buf, MAX_STC_FRAME_LEN + sizeof(uint32_t)); + if (tx_buf == NULL) { + rc = -ENOMEM; + goto out; + } + + qmrom_alloc(rx_buf, MAX_STC_FRAME_LEN + sizeof(uint32_t)); + if (rx_buf == NULL) { + qmrom_free(tx_buf); + rc = -ENOMEM; + goto out; + } + +#ifdef CHECK_STCS + ((uint32_t *)tx_buf)[MAX_STC_FRAME_LEN / sizeof(uint32_t)] = 0xfeeddeef; + ((uint32_t *)rx_buf)[MAX_STC_FRAME_LEN / sizeof(uint32_t)] = 0xfeeddeef; +#endif + h->hstc = (struct stc *)tx_buf; + h->sstc = (struct stc *)rx_buf; + return rc; +out: + qmrom_free_stcs(h); + return rc; +} + +int qmrom_pre_read(struct qmrom_handle *handle) +{ + handle->hstc->all = 0; + handle->hstc->host_flags.pre_read = 1; + handle->hstc->ul = 1; + handle->hstc->len = 0; + handle->hstc->payload[0] = 0; + return qmrom_spi_transfer(handle->spi_handle, (char *)handle->sstc, + (const char *)handle->hstc, + sizeof(struct stc) + handle->hstc->len); +} + +int qmrom_read(struct qmrom_handle *handle) +{ + size_t rd_size = handle->sstc->len; + if (rd_size > MAX_STC_FRAME_LEN) + return SPI_ERR_INVALID_STC_LEN; + LOG_DBG("%s: reading %zu bytes...\n", __func__, rd_size); + memset(handle->hstc, 0, sizeof(struct stc) + rd_size); + handle->hstc->host_flags.read = 1; + handle->hstc->ul = 1; + handle->hstc->len = handle->sstc->len; + + return qmrom_spi_transfer(handle->spi_handle, (char *)handle->sstc, + (const char *)handle->hstc, + sizeof(struct stc) + rd_size); +} + +/* + * Unfortunately, A0, B0 and C0 have different + * APIs to get the chip version... + * + */ +int qmrom_probe_device(struct qmrom_handle *handle, + enum device_generation_e dev_gen_hint) +{ + int rc = -1; + + if (dev_gen_hint == DEVICE_GEN_QM358XX || + dev_gen_hint == DEVICE_GEN_UNKNOWN) + rc = qm358xx_rom_probe_device(handle); + if (rc && (dev_gen_hint == DEVICE_GEN_QM357XX || + dev_gen_hint == DEVICE_GEN_UNKNOWN)) + rc = qm357xx_rom_probe_device(handle); + + return rc; +} + +struct qmrom_handle *qmrom_init(void *spi_handle, void *reset_handle, + void *ss_rdy_handle, void *ss_irq_handle, + int spi_speed, int comms_retries, + reset_device_fn reset, + enum device_generation_e dev_gen_hint) +{ + struct qmrom_handle *handle; + int rc; + + qmrom_alloc(handle, sizeof(struct qmrom_handle)); + if (!handle) { + LOG_ERR("%s: Couldn't allocate %zu bytes...\n", __func__, + sizeof(struct qmrom_handle)); + return NULL; + } + rc = qmrom_allocate_stcs(handle); + if (rc) { + LOG_ERR("%s: Couldn't allocate stcs...\n", __func__); + qmrom_free(handle); + return NULL; + } + + handle->spi_handle = spi_handle; + handle->reset_handle = reset_handle; + handle->ss_rdy_handle = ss_rdy_handle; + handle->ss_irq_handle = ss_irq_handle; + handle->comms_retries = comms_retries; + handle->chip_rev = CHIP_REVISION_UNKNOWN; + handle->device_version = -1; + handle->spi_speed = spi_speed; + handle->skip_check_fw_boot = false; + + handle->dev_ops.reset = reset; + + rc = qmrom_probe_device(handle, dev_gen_hint); + if (rc) { + LOG_ERR("%s: qmrom_probe_device returned %d!\n", __func__, rc); + qmrom_free_stcs(handle); + qmrom_free(handle); + return NULL; + } + + check_stcs(__func__, __LINE__, handle); + return handle; +} + +void qmrom_deinit(struct qmrom_handle *handle) +{ + LOG_DBG("Deinitializing %pK\n", (void *)handle); + qmrom_free_stcs(handle); + qmrom_free(handle); +} + +int qmrom_reboot_bootloader(struct qmrom_handle *handle) +{ + int rc; + + qmrom_msleep(500); + rc = qmrom_spi_set_cs_level(handle->spi_handle, 1); + if (rc) { + LOG_ERR("%s: spi_set_cs_level(0) failed with %d\n", __func__, + rc); + return rc; + } + qmrom_msleep(SPI_RST_LOW_DELAY_MS); + + handle->dev_ops.reset(handle->reset_handle); + + qmrom_msleep(SPI_RST_LOW_DELAY_MS); + + rc = qmrom_spi_set_cs_level(handle->spi_handle, 0); + if (rc) { + LOG_ERR("%s: spi_set_cs_level(1) failed with %d\n", __func__, + rc); + return rc; + } + + qmrom_msleep(SPI_RST_LOW_DELAY_MS); + + return 0; +} diff --git a/drivers/uwb/qm35/libqmrom/src/qmrom_log.c b/drivers/uwb/qm35/libqmrom/src/qmrom_log.c new file mode 100644 index 000000000000..87a06350965d --- /dev/null +++ b/drivers/uwb/qm35/libqmrom/src/qmrom_log.c @@ -0,0 +1,78 @@ +/* + * Copyright 2021 Qorvo US, Inc. + * + * SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 + * + * This file is provided under the Apache License 2.0, or the + * GNU General Public License v2.0. + * + */ +#include + +#define LOG_PRINT(lvl, ...) \ + do { \ + switch (lvl) { \ + case LOG_ERR: \ + LOG_ERR(__VA_ARGS__); \ + break; \ + case LOG_WARN: \ + LOG_WARN(__VA_ARGS__); \ + break; \ + case LOG_INFO: \ + LOG_INFO(__VA_ARGS__); \ + break; \ + case LOG_DBG: \ + LOG_DBG(__VA_ARGS__); \ + break; \ + default: \ + break; \ + } \ + } while (0) + +enum log_level_e __log_level__ = LOG_DBG; + +void set_log_level(enum log_level_e lvl) +{ + __log_level__ = lvl; +} + +#ifdef __KERNEL__ +#include + +struct device *__qmrom_log_dev__ = NULL; + +void qmrom_set_log_device(struct device *dev, enum log_level_e lvl) +{ + __qmrom_log_dev__ = dev; + __log_level__ = lvl; +} +#endif + +void hexdump(enum log_level_e lvl, void *_array, unsigned short length) +{ + unsigned char *array = _array; + int i; + + if (lvl < __log_level__) + return; + + for (i = 0; i < length; i += 16) { + int j = 0; + for (; j < 15 && i + j + 1 < length; j++) + LOG_PRINT(lvl, "0x%02x, ", array[i + j]); + LOG_PRINT(lvl, "0x%02x\n", array[i + j]); + } +} + +void hexrawdump(enum log_level_e lvl, void *_array, unsigned short length) +{ + unsigned char *array = _array; + int i; + + if (lvl < __log_level__) + return; + + for (i = 0; i < length; i++) { + LOG_PRINT(lvl, "%02x", array[i]); + } +} diff --git a/drivers/uwb/qm35/qm35-spi.c b/drivers/uwb/qm35/qm35-spi.c new file mode 100644 index 000000000000..e8bf1235c930 --- /dev/null +++ b/drivers/uwb/qm35/qm35-spi.c @@ -0,0 +1,1261 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2021 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 UCI over HSSPI protocol + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_QM35_DEBOUNCE_TIME_US +#include +#endif + +#include +#include +#include +#include + +#include + +#include "qm35.h" +#include "uci_ioctls.h" +#include "qm35_coredump_ioctls.h" +#include "hsspi.h" +#include "hsspi_uci.h" +#include "hsspi_test.h" +#include "hsspi_coredump.h" + +#define QM35_REGULATOR_DELAY_US 1000 +#define QMROM_RETRIES 10 + +#ifndef CONFIG_FLASHING_RETRIES +#define CONFIG_FLASHING_RETRIES 10 /* Intentionally high value */ +#endif + +/* Redefine certificate error here since original definitions are separate */ +#define REGULATORS_ENABLED(x) (x->vdd1 || x->vdd2 || x->vdd3 || x->vdd4) + +#ifndef NO_UWB_HAL +#define NO_UWB_HAL false +#endif + +static int qm_firmware_load(struct qm35_ctx *qm35_hdl); +static void qm35_regulators_set(struct qm35_ctx *qm35_hdl, bool on); + +static const struct file_operations uci_fops; + +static const struct of_device_id qm35_dt_ids[] = { + { .compatible = "qorvo,qm35" }, + {}, +}; +MODULE_DEVICE_TABLE(of, qm35_dt_ids); + +static bool flash_on_probe = false; +module_param(flash_on_probe, bool, 0444); +MODULE_PARM_DESC(flash_on_probe, "Flash during the module probe"); + +int spi_speed_hz; +module_param(spi_speed_hz, int, 0644); +MODULE_PARM_DESC(spi_speed_hz, "SPI speed (if not set use DTS's one)"); + +static char *fwname = NULL; +module_param(fwname, charp, 0444); +MODULE_PARM_DESC(fwname, "Use fwname as firmware binary to flash QM35"); + +static bool wake_use_wakeup = true; +module_param(wake_use_wakeup, bool, 0644); +MODULE_PARM_DESC(wake_use_wakeup, "Use wakeup pin to wake up QM35"); + +static bool wake_use_csn = false; +module_param(wake_use_csn, bool, 0644); +MODULE_PARM_DESC(wake_use_csn, "Use HSSPI CSn pin to wake up QM35"); + +static bool wake_on_ssirq = true; +module_param(wake_on_ssirq, bool, 0644); +MODULE_PARM_DESC(wake_on_ssirq, + "Allow QM35 to wakeup the platform using ss_irq"); + +int trace_spi_xfers; +module_param(trace_spi_xfers, int, 0644); +MODULE_PARM_DESC(trace_spi_xfers, "Trace all the SPI transfers"); + +int qmrom_retries = QMROM_RETRIES; +module_param(qmrom_retries, int, 0644); +MODULE_PARM_DESC(qmrom_retries, "QMROM retries"); + +int flashing_retries = CONFIG_FLASHING_RETRIES; +module_param(flashing_retries, int, 0644); +MODULE_PARM_DESC(flashing_retries, "Flashing retries"); + +int reset_on_error = 1; +module_param(reset_on_error, int, 0644); +MODULE_PARM_DESC(reset_on_error, "Reset the QM35 on successive errors"); + +int log_qm_traces = 1; +module_param(log_qm_traces, int, 0444); +MODULE_PARM_DESC(log_qm_traces, "Logs the QM35 traces in the kernel messages"); + +bool reset_when_disabled = NO_UWB_HAL ? false : true; +module_param(reset_when_disabled, bool, 0644); +MODULE_PARM_DESC( + reset_when_disabled, + "Assert reset line when UWB is disabled (if at least one regulator is defined in DTS)"); + +int fu_spi_speed_hz; +module_param(fu_spi_speed_hz, int, 0644); +MODULE_PARM_DESC(fu_spi_speed_hz, "FW updater SPI speed"); + +int qmrom_spi_speed_hz; +module_param(qmrom_spi_speed_hz, int, 0644); +MODULE_PARM_DESC(qmrom_spi_speed_hz, "FW updater SPI speed"); + +static uint8_t qm_soc_id[QM357XX_ROM_SOC_ID_LEN]; +static uint16_t qm_dev_id; + +static struct gpio_desc *global_temporary_gpio_csn; +void qm35_set_csn_level(int level) +{ + gpiod_set_value(global_temporary_gpio_csn, level ? 1 : 0); +} + +/* + * uci_open() : open operation for uci device + * + */ +static int uci_open(struct inode *inode, struct file *file) +{ + struct miscdevice *uci_dev = file->private_data; + struct qm35_ctx *qm35_hdl = + container_of(uci_dev, struct qm35_ctx, uci_dev); + + return hsspi_register(&qm35_hdl->hsspi, &qm35_hdl->uci_layer.hlayer); +} + +/* + * uci_ioctl() - ioctl operation for uci device. + * + */ +static long uci_ioctl(struct file *filp, unsigned int cmd, unsigned long args) +{ + void __user *argp = (void __user *)args; + struct miscdevice *uci_dev = filp->private_data; + struct qm35_ctx *qm35_hdl = + container_of(uci_dev, struct qm35_ctx, uci_dev); + int ret; + + switch (cmd) { + case QM35_CTRL_GET_STATE: { + return copy_to_user(argp, &qm35_hdl->state, + sizeof(qm35_hdl->state)) ? + -EFAULT : + 0; + } + case QM35_CTRL_RESET: { + qm35_hsspi_stop(qm35_hdl); + + ret = qm35_reset(qm35_hdl, QM_RESET_LOW_MS); + + msleep(QM_BOOT_MS); + + qm35_hsspi_start(qm35_hdl); + + if (ret) + return ret; + + return copy_to_user(argp, &qm35_hdl->state, + sizeof(qm35_hdl->state)) ? + -EFAULT : + 0; + } + case QM35_CTRL_FW_UPLOAD: { + qm35_hsspi_stop(qm35_hdl); + + ret = qm_firmware_load(qm35_hdl); + + msleep(QM_BOOT_MS); + + qm35_hsspi_start(qm35_hdl); + + if (ret) + return ret; + + return copy_to_user(argp, &qm35_hdl->state, + sizeof(qm35_hdl->state)) ? + -EFAULT : + 0; + } + case QM35_CTRL_POWER: { + unsigned int on; + + ret = get_user(on, (unsigned int __user *)argp); + if (ret) + return ret; + + qm35_hsspi_stop(qm35_hdl); + + if (REGULATORS_ENABLED(qm35_hdl)) + qm35_regulators_set(qm35_hdl, on); + + /* + * Always reset QM as regulators could be shared with + * other devices and power may not be controlled as + * expected + */ + if (!reset_when_disabled) + qm35_reset(qm35_hdl, QM_RESET_LOW_MS); + + msleep(QM_BOOT_MS); + + /* If reset or power on */ + if (!REGULATORS_ENABLED(qm35_hdl) || + (REGULATORS_ENABLED(qm35_hdl) && on)) + qm35_hsspi_start(qm35_hdl); + + return 0; + } + default: + dev_err(&qm35_hdl->spi->dev, "unknown ioctl %x to %s device\n", + cmd, qm35_hdl->uci_dev.name); + return -EINVAL; + } +} + +/* + * uci_release() - release operation for uci device. + * + */ +static int uci_release(struct inode *inode, struct file *filp) +{ + struct miscdevice *uci_dev = filp->private_data; + struct qm35_ctx *qm35_hdl = + container_of(uci_dev, struct qm35_ctx, uci_dev); + + hsspi_unregister(&qm35_hdl->hsspi, &qm35_hdl->uci_layer.hlayer); + return 0; +} + +static ssize_t uci_read(struct file *filp, char __user *buf, size_t len, + loff_t *off) +{ + struct miscdevice *uci_dev = filp->private_data; + struct qm35_ctx *qm35_hdl = + container_of(uci_dev, struct qm35_ctx, uci_dev); + struct uci_packet *p; + int ret; + + p = uci_layer_read(&qm35_hdl->uci_layer, len, + filp->f_flags & O_NONBLOCK); + if (IS_ERR(p)) + return PTR_ERR(p); + + ret = copy_to_user(buf, p->data, p->length); + if (!ret) + ret = p->length; + + uci_packet_free(p); + return ret; +} + +static ssize_t uci_write(struct file *filp, const char __user *buf, size_t len, + loff_t *off) +{ + struct miscdevice *uci_dev = filp->private_data; + struct qm35_ctx *qm35_hdl = + container_of(uci_dev, struct qm35_ctx, uci_dev); + struct uci_packet *p; + DECLARE_COMPLETION_ONSTACK(comp); + int ret; + + p = uci_packet_alloc(len); + if (!p) + return -ENOMEM; + + p->write_done = ∁ + + if (copy_from_user(p->data, buf, (u16)len)) { + ret = -EFAULT; + goto free; + } + + ret = hsspi_send(&qm35_hdl->hsspi, &qm35_hdl->uci_layer.hlayer, + &p->blk); + if (ret) + goto free; + + wait_for_completion(&comp); + + ret = p->status ? p->status : (u16)len; +free: + uci_packet_free(p); + return ret; +} + +static __poll_t uci_poll(struct file *filp, struct poll_table_struct *wait) +{ + struct miscdevice *uci_dev = filp->private_data; + struct qm35_ctx *qm35_ctx = + container_of(uci_dev, struct qm35_ctx, uci_dev); + __poll_t mask = 0; + + poll_wait(filp, &qm35_ctx->uci_layer.wq, wait); + + if (uci_layer_has_data_available(&qm35_ctx->uci_layer)) + mask |= EPOLLIN; + + return mask; +} + +static const struct file_operations uci_fops = { + .owner = THIS_MODULE, + .open = uci_open, + .release = uci_release, + .unlocked_ioctl = uci_ioctl, + .read = uci_read, + .write = uci_write, + .poll = uci_poll, +}; + +/* + * coredump_ioctl() - ioctl operation for coredump device. + * + */ +static long coredump_ioctl(struct file *filp, unsigned int cmd, + unsigned long args) +{ + void __user *argp = (void __user *)args; + struct miscdevice *dev = filp->private_data; + struct qm35_ctx *qm35_hdl = + container_of(dev, struct qm35_ctx, coredump_dev); + + switch (cmd) { + case QM35_COREDUMP_FORCE: { + return coredump_layer_force_coredump(&qm35_hdl->coredump_layer); + } + case QM35_COREDUMP_GET_SIZE: { + uint32_t size = coredump_layer_get_coredump_size( + &qm35_hdl->coredump_layer); + if (copy_to_user(argp, &size, sizeof(size))) + return -EINVAL; + return 0; + } + default: + dev_err(&qm35_hdl->spi->dev, "unknown ioctl %x to %s device\n", + cmd, qm35_hdl->uci_dev.name); + return -EINVAL; + } +} + +static ssize_t coredump_read(struct file *filp, char __user *buf, size_t len, + loff_t *off) +{ + struct miscdevice *dev = filp->private_data; + struct qm35_ctx *qm35_hdl = + container_of(dev, struct qm35_ctx, coredump_dev); + ssize_t ret = 0; + uint32_t size = + coredump_layer_get_coredump_size(&qm35_hdl->coredump_layer); + uint8_t *cd = coredump_layer_get_coredump(&qm35_hdl->coredump_layer); + + if (IS_ERR(cd)) + return PTR_ERR(cd); + + ret = simple_read_from_buffer(buf, len, off, cd, size); + if (ret == 0 || *off == size) { + /* reset coredump */ + coredump_layer_reset_status(&qm35_hdl->coredump_layer); + } + + return ret; +} + +static __poll_t coredump_poll(struct file *filp, struct poll_table_struct *wait) +{ + struct miscdevice *dev = filp->private_data; + struct qm35_ctx *qm35_ctx = + container_of(dev, struct qm35_ctx, coredump_dev); + __poll_t mask = 0; + + poll_wait(filp, &qm35_ctx->coredump_layer.wq, wait); + + if (coredump_layer_new_coredump_available(&qm35_ctx->coredump_layer)) + mask |= EPOLLIN; + + return mask; +} + +static const struct file_operations coredump_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = coredump_ioctl, + .read = coredump_read, + .poll = coredump_poll, +}; + +static irqreturn_t qm35_irq_handler(int irq, void *qm35_ctx) +{ + struct qm35_ctx *qm35_hdl = qm35_ctx; + + spin_lock(&qm35_hdl->lock); + qm35_hdl->state = QM35_CTRL_STATE_READY; + spin_unlock(&qm35_hdl->lock); + + disable_irq_nosync(irq); + + hsspi_set_output_data_waiting(&qm35_hdl->hsspi); + + return IRQ_HANDLED; +} + +static void reenable_ss_irq(struct hsspi *hsspi) +{ + struct qm35_ctx *qm35_hdl = container_of(hsspi, struct qm35_ctx, hsspi); + + enable_irq(qm35_hdl->spi->irq); +} + +static irqreturn_t qm35_ss_rdy_handler(int irq, void *data) +{ + struct qm35_ctx *qm35_hdl = data; +#ifdef CONFIG_QM35_DEBOUNCE_TIME_US + static ktime_t old_time; + ktime_t current_time; + + current_time = ktime_get(); + + if (ktime_after(ktime_add(old_time, + CONFIG_QM35_DEBOUNCE_TIME_US * 1000), + current_time)) + return IRQ_HANDLED; + + old_time = current_time; +#endif + /* Should be low already but in case we just woke up QM */ + if (wake_use_csn) + gpiod_set_value(qm35_hdl->gpio_csn, 0); + if (wake_use_wakeup) + gpiod_set_value(qm35_hdl->gpio_wakeup, 0); + + if (!qm35_hdl->flashing) { + hsspi_clear_spi_slave_busy(&qm35_hdl->hsspi); + hsspi_set_spi_slave_ready(&qm35_hdl->hsspi); + } else { + qm35_hdl->qmrom_qm_ready = true; + wake_up_interruptible(&qm35_hdl->qmrom_wq_ready); + } + + return IRQ_HANDLED; +} + +static void qm35_wakeup(struct hsspi *hsspi) +{ + struct qm35_ctx *qm35_hdl = container_of(hsspi, struct qm35_ctx, hsspi); + + if (wake_use_csn) + gpiod_set_value(qm35_hdl->gpio_csn, 1); + if (wake_use_wakeup) + gpiod_set_value(qm35_hdl->gpio_wakeup, 1); + + /* The wake up will be cleared only when ss-rdy is raised again */ +} + +static void qm35_reset_hook(struct hsspi *hsspi) +{ + struct qm35_ctx *qm35_hdl = container_of(hsspi, struct qm35_ctx, hsspi); + + if (reset_on_error) + qm35_reset(qm35_hdl, QM_RESET_LOW_MS); + usleep_range(QM_BEFORE_RESET_MS * 1000, QM_BEFORE_RESET_MS * 1000); +} + +static irqreturn_t qm35_exton_handler(int irq, void *data) +{ + struct qm35_ctx *qm35_hdl = data; + + hsspi_clear_spi_slave_ready(&qm35_hdl->hsspi); + + if (qm35_hdl->hsspi.waiting_ss_rdy) + qm35_wakeup(&qm35_hdl->hsspi); + + return IRQ_HANDLED; +} + +void qm35_hsspi_start(struct qm35_ctx *qm35_hdl) +{ + /* nothing to do as HSSPI is already started */ + if (qm35_hdl->hsspi.state == HSSPI_RUNNING) + return; + + enable_irq(qm35_hdl->ss_rdy_irq); +#ifdef CONFIG_QM35_RISING_IRQ_NOT_TRIGGERED + /* Some IRQ controller will trigger a rising edge if + * the gpio is high when enabling the IRQ, some will + * not (RPI board for example). In the second case we + * can miss an event depending on if the level rise + * before or after enable_irq. Besides, the handler + * can also run *after* hsspi_start, breaking the + * hsspi thread with a false information. So let's + * sleep and force the SS_READY bit after. + */ + + if (gpiod_get_value(qm35_hdl->gpio_ss_rdy)) + hsspi_set_spi_slave_ready(&qm35_hdl->hsspi); +#endif + + hsspi_start(&qm35_hdl->hsspi); +} + +void qm35_hsspi_stop(struct qm35_ctx *qm35_hdl) +{ + /* nothing to do as HSSPI is already stopped */ + if (qm35_hdl->hsspi.state == HSSPI_STOPPED) + return; + + hsspi_stop(&qm35_hdl->hsspi); + + disable_irq_nosync(qm35_hdl->ss_rdy_irq); + + clear_bit(HSSPI_FLAGS_SS_READY, qm35_hdl->hsspi.flags); +} + +int qm35_reset_sync(struct qm35_ctx *qm35_hdl) +{ + int ret; + + qm35_hsspi_stop(qm35_hdl); + ret = qm35_reset(qm35_hdl, QM_RESET_LOW_MS); + msleep(QM_BOOT_MS); + qm35_hsspi_start(qm35_hdl); + + return ret; +} + +static int qm_firmware_flash_fw(struct qm35_ctx *qm35_hdl, + struct qmrom_handle *h, + const struct firmware *fw) +{ + int rc = 0, nb_retries = flashing_retries; + do { + /* If the previous flashing failed, re-enter the QM + * rom code so it will be in the same initial state + */ + if (rc) + qmrom_reboot_bootloader(h); + + rc = qm357xx_rom_flash_fw(h, fw); + if (rc) + dev_err(&qm35_hdl->spi->dev, + "Attempt %d: flashing failed with %d!\n", + flashing_retries - nb_retries, rc); + if (rc == PEG_ERR_FIRST_KEY_CERT_OR_FW_VER) + break; + } while (rc && --nb_retries > 0); + return rc; +} + +static int qm_firmware_flash_macro_pkg(struct qm35_ctx *qm35_hdl, + struct qmrom_handle *h, + const struct firmware *fw) +{ + int rc, nb_retries = flashing_retries; + char *fw_data; + uint32_t fw_size; + + rc = qm357xx_rom_fw_macro_pkg_get_fw_idx(fw, 1, &fw_size, &fw_data); + if (rc) { + dev_err(&qm35_hdl->spi->dev, + "FW MACRO PACKAGE corrupted = %d\n", rc); + return rc; + } + if (*(uint32_t *)fw_data != CRYPTO_FIRMWARE_PACK_MAGIC_VALUE) { + rc = -EINVAL; + dev_err(&qm35_hdl->spi->dev, + "FW PACKAGE not found - magic is %04x, size is %d\n", + *(uint32_t *)fw_data, fw_size); + return rc; + } + + do { + /* If the previous flashing failed, re-enter the QM + * rom code so it will be in the same initial state + */ + if (spi_speed_hz) + qmrom_spi_set_freq(spi_speed_hz); + else + qmrom_spi_set_freq(DEFAULT_SPI_CLOCKRATE); + + if (rc) + qmrom_reboot_bootloader(h); + + // The Macro Package contain the fw updater and the package + // simply flash the first and provide the second to + // the updater lib + h->skip_check_fw_boot = true; + rc = qm357xx_rom_flash_fw(h, fw); + h->skip_check_fw_boot = false; + if (rc) { + dev_err(&qm35_hdl->spi->dev, + "Attempt %d: fw updater flashing failed with %d!\n", + flashing_retries - nb_retries, rc); + if (rc == PEG_ERR_FIRST_KEY_CERT_OR_FW_VER) + break; + continue; + } + + // now flash the firmware proper + if (fu_spi_speed_hz) + qmrom_spi_set_freq(fu_spi_speed_hz); + else + qmrom_spi_set_freq(FWUPDATER_SPI_SPEED_HZ); + rc = run_fwupdater(h, fw_data, fw_size); + if (rc) { + dev_err(&qm35_hdl->spi->dev, + "Attempt %d: fw app flashing failed with %d!\n", + flashing_retries - nb_retries, rc); + } + } while (rc && --nb_retries > 0); + return rc; +} + +static int qm_firmware_flashing(void *handle, struct qmrom_handle *h, + bool *is_macro_pkg, bool use_prod_fw) +{ + struct qm35_ctx *qm35_hdl = (struct qm35_ctx *)handle; + struct spi_device *spi = qm35_hdl->spi; + int rc = 0; + const struct firmware *fw; +#ifdef C0_WRITE_STATS + uint64_t elapsed_time_ns; + ktime_t start_time; +#endif + + fw = qmrom_spi_get_firmware(&spi->dev, h, is_macro_pkg, use_prod_fw); + if (fw == NULL) { + dev_err(&spi->dev, "Firmware file not present!\n"); + return -1; + } + +#ifdef C0_WRITE_STATS + start_time = ktime_get(); +#endif + + qm35_hdl->flashing = true; + if (!*is_macro_pkg) + rc = qm_firmware_flash_fw(qm35_hdl, h, fw); + else + rc = qm_firmware_flash_macro_pkg(qm35_hdl, h, fw); + +#ifdef C0_WRITE_STATS + elapsed_time_ns = ktime_to_ns(ktime_sub(ktime_get(), start_time)); + if (!rc) + dev_warn(&spi->dev, "Firmware flashed in %llu us\n", + div_u64(elapsed_time_ns, 1000)); +#endif + + qmrom_spi_release_firmware(fw); + qm35_hdl->flashing = false; + + // reset the device anyway + qmrom_spi_reset_device(qm35_hdl); + return rc; +} + +static int qm_firmware_load(struct qm35_ctx *qm35_hdl) +{ + struct spi_device *spi = qm35_hdl->spi; + unsigned int state = qm35_get_state(qm35_hdl); + struct qmrom_handle *h; + bool is_macro_pkg = false; + int ret; + + qm35_set_state(qm35_hdl, QM35_CTRL_STATE_FW_DOWNLOADING); + + qmrom_set_log_device(&spi->dev, LOG_WARN); + + enable_irq(qm35_hdl->ss_rdy_irq); + + h = qmrom_init(&spi->dev, qm35_hdl, qm35_hdl, qm35_hdl->gpio_ss_irq, + qmrom_spi_speed_hz, qmrom_retries, + qmrom_spi_reset_device, DEVICE_GEN_QM357XX); + if (!h) { + pr_err("qmrom_init failed\n"); + ret = -1; + goto out; + } + + dev_info(&spi->dev, " chip_ver: %x\n", h->chip_rev); + dev_info(&spi->dev, " dev_id: deca%04x\n", h->device_version); + + if (h->chip_rev != CHIP_REVISION_A0) { + dev_info(&spi->dev, " soc_id: %*phN\n", + QM357XX_ROM_SOC_ID_LEN, h->qm357xx_soc_info.soc_id); + dev_info(&spi->dev, " uuid: %*phN\n", + QM357XX_ROM_UUID_LEN, h->qm357xx_soc_info.uuid); + dev_info(&spi->dev, " lcs_state: %u\n", + h->qm357xx_soc_info.lcs_state); + + memcpy(&qm_dev_id, &h->device_version, sizeof(qm_dev_id)); + memcpy(qm_soc_id, h->qm357xx_soc_info.soc_id, + QM357XX_ROM_SOC_ID_LEN); + + debug_soc_info_available(&qm35_hdl->debug); + } else { + dev_dbg(&spi->dev, + "SoC info not supported on chip revision A0\n"); + } + + dev_dbg(&spi->dev, "Starting device flashing!\n"); + ret = qm_firmware_flashing(qm35_hdl, h, &is_macro_pkg, true); + if (ret) { + qmrom_reboot_bootloader(h); + ret = qm_firmware_flashing(qm35_hdl, h, &is_macro_pkg, false); + } + + if (ret) + dev_err(&spi->dev, "Firmware download failed with %d!\n", ret); + else + dev_info(&spi->dev, "Device flashing succeeded!\n"); + +out: + disable_irq_nosync(qm35_hdl->ss_rdy_irq); + + qm35_set_state(qm35_hdl, state); + + return ret; +} + +int qm_get_dev_id(struct qm35_ctx *qm35_hdl, uint16_t *dev_id) +{ + memcpy(dev_id, &qm_dev_id, sizeof(qm_dev_id)); + + return 0; +} + +int qm_get_soc_id(struct qm35_ctx *qm35_hdl, uint8_t *soc_id) +{ + memcpy(soc_id, qm_soc_id, QM357XX_ROM_SOC_ID_LEN); + + return 0; +} + +/** + * hsspi_irqs_setup() - setup all irqs needed by HSSPI + * @qm35_ctx: pointer to &struct qm35_ctx + * + * SS_IRQ + * ------ + * + * If `ss-irq-gpios` exists in the DTS, it is used. If not, it's using + * the `interrupts` definition from the SPI device. + * + * SS_READY + * -------- + * + * The `ss-ready-gpios` is mandatory. It is used by the FW to signal + * the driver that it can handle a SPI transaction. + * + * Return: 0 if no error, -errno otherwise + */ +static int hsspi_irqs_setup(struct qm35_ctx *qm35_ctx) +{ + int ret, irq; + + /* Get READY GPIO */ + qm35_ctx->gpio_ss_rdy = + devm_gpiod_get(&qm35_ctx->spi->dev, "ss-ready", GPIOD_IN); + if (IS_ERR(qm35_ctx->gpio_ss_rdy)) + return PTR_ERR(qm35_ctx->gpio_ss_rdy); + + irq = gpiod_to_irq(qm35_ctx->gpio_ss_rdy); + if (irq < 0) { + dev_err(&qm35_ctx->spi->dev, + "%s: gpiod_to_irq(ss-ready) returns %d", __func__, irq); + return irq; + } + qm35_ctx->ss_rdy_irq = irq; + ret = devm_request_irq(&qm35_ctx->spi->dev, irq, &qm35_ss_rdy_handler, + IRQF_TRIGGER_RISING, "hsspi-ss-rdy", qm35_ctx); + if (ret) { + dev_err(&qm35_ctx->spi->dev, "%s: devm_request_irq returned %d", + __func__, ret); + return ret; + } + /* devm_request_irq enables the interrupt. On probe, the hsspi is in + * HSSPI_STOPPED state by default. In this state, the interrupt is + * expected to be disabled. */ + disable_irq(irq); + + /* get SS_IRQ GPIO */ + qm35_ctx->gpio_ss_irq = + devm_gpiod_get(&qm35_ctx->spi->dev, "ss-irq", GPIOD_IN); + if (IS_ERR(qm35_ctx->gpio_ss_irq)) { + dev_err(&qm35_ctx->spi->dev, + "%s: gpiod_get_index(ss-irq) returned %pK", __func__, + qm35_ctx->gpio_ss_irq); + } + + qm35_ctx->spi->irq = gpiod_to_irq(qm35_ctx->gpio_ss_irq); + if (qm35_ctx->spi->irq < 0) { + dev_err(&qm35_ctx->spi->dev, + "%s: gpiod_to_irq(ss-irq) returned %d", __func__, + qm35_ctx->spi->irq); + return qm35_ctx->spi->irq; + } + ret = devm_request_irq(&qm35_ctx->spi->dev, qm35_ctx->spi->irq, + &qm35_irq_handler, IRQF_TRIGGER_HIGH, + "hsspi-ss-irq", qm35_ctx); + if (ret) { + dev_err(&qm35_ctx->spi->dev, "%s: devm_request_irq returned %d", + __func__, ret); + return ret; + } + + if (wake_on_ssirq) { + ret = enable_irq_wake(qm35_ctx->spi->irq); + if (ret) { + dev_err(&qm35_ctx->spi->dev, + "%s: enable_irq_wake returned %d", __func__, + ret); + return ret; + } + } + + qm35_ctx->hsspi.odw_cleared = reenable_ss_irq; + qm35_ctx->hsspi.wakeup = qm35_wakeup; + qm35_ctx->hsspi.reset_qm35 = qm35_reset_hook; + + /* Get exton */ + qm35_ctx->gpio_exton = + devm_gpiod_get_optional(&qm35_ctx->spi->dev, "exton", GPIOD_IN); + if (qm35_ctx->gpio_exton) { + if (IS_ERR(qm35_ctx->gpio_exton)) + return PTR_ERR(qm35_ctx->gpio_exton); + + irq = gpiod_to_irq(qm35_ctx->gpio_exton); + if (irq < 0) { + dev_err(&qm35_ctx->spi->dev, + "%s: gpiod_to_irq(exton) returned %d", __func__, + irq); + return irq; + } + + ret = devm_request_irq(&qm35_ctx->spi->dev, irq, + &qm35_exton_handler, + IRQF_TRIGGER_FALLING, "hsspi-exton", + qm35_ctx); + if (ret) + return ret; + + if (!gpiod_get_value(qm35_ctx->gpio_exton)) + hsspi_clear_spi_slave_ready(&qm35_ctx->hsspi); + } + + /* Get spi csn */ + if (wake_use_csn) { + qm35_ctx->gpio_csn = devm_gpiod_get(&qm35_ctx->spi->dev, "csn", + GPIOD_OUT_HIGH); + if (IS_ERR(qm35_ctx->gpio_csn)) + return PTR_ERR(qm35_ctx->gpio_csn); + } + + /* Get wakeup */ + if (wake_use_wakeup) { + qm35_ctx->gpio_wakeup = devm_gpiod_get(&qm35_ctx->spi->dev, + "wakeup", GPIOD_OUT_LOW); + if (IS_ERR(qm35_ctx->gpio_wakeup)) + return PTR_ERR(qm35_ctx->gpio_wakeup); + } + + return 0; +} + +static int qm35_regulator_set_one(struct regulator *reg, bool on) +{ + if (!reg) + return 0; + + return on ? regulator_enable(reg) : regulator_disable(reg); +} + +static void qm35_regulators_set(struct qm35_ctx *qm35_hdl, bool on) +{ + static const char *str_fmt = "failed to %s %s regulator: %d\n"; + struct device *dev = &qm35_hdl->spi->dev; + const char *on_str = on ? "enable" : "disable"; + bool is_enabled; + int ret; + + if (NO_UWB_HAL) { + on = true; + on_str = "enable"; + } + + spin_lock(&qm35_hdl->lock); + + is_enabled = qm35_hdl->regulators_enabled; + qm35_hdl->regulators_enabled = on; + + spin_unlock(&qm35_hdl->lock); + + /* nothing to do we are already in the desired state */ + if (is_enabled == on) + return; + + if (reset_when_disabled && !on && + qm35_get_state(qm35_hdl) != QM35_CTRL_STATE_RESET) { + if (qm35_hdl->gpio_reset) { + gpiod_set_value(qm35_hdl->gpio_reset, 1); + } + qm35_set_state(qm35_hdl, QM35_CTRL_STATE_RESET); + } + + ret = qm35_regulator_set_one(qm35_hdl->vdd1, on); + if (ret) + dev_err(dev, str_fmt, on_str, "vdd1", ret); + + ret = qm35_regulator_set_one(qm35_hdl->vdd2, on); + if (ret) + dev_err(dev, str_fmt, on_str, "vdd2", ret); + + ret = qm35_regulator_set_one(qm35_hdl->vdd3, on); + if (ret) + dev_err(dev, str_fmt, on_str, "vdd3", ret); + + ret = qm35_regulator_set_one(qm35_hdl->vdd4, on); + if (ret) + dev_err(dev, str_fmt, on_str, "vdd4", ret); + + /* wait for regulator stabilization */ + usleep_range(QM35_REGULATOR_DELAY_US, QM35_REGULATOR_DELAY_US + 100); + + if (reset_when_disabled && on && + qm35_get_state(qm35_hdl) == QM35_CTRL_STATE_RESET) { + if (qm35_hdl->gpio_reset) { + gpiod_set_value(qm35_hdl->gpio_reset, 0); + } + qm35_set_state(qm35_hdl, QM35_CTRL_STATE_UNKNOWN); + } +} + +static void qm35_regulators_setup_one(struct regulator **reg, + struct device *dev, const char *name) +{ + static const char *str_fmt = + "%s regulator not defined in device tree: %d\n"; + struct regulator *tmp; + + tmp = devm_regulator_get_optional(dev, name); + if (IS_ERR(tmp)) { + dev_notice(dev, str_fmt, name, PTR_ERR(tmp)); + tmp = NULL; + } + *reg = tmp; +} + +static void qm35_regulators_setup(struct qm35_ctx *qm35_hdl) +{ + struct device *dev = &qm35_hdl->spi->dev; + + qm35_regulators_setup_one(&qm35_hdl->vdd1, dev, "qm35-vdd1"); + qm35_regulators_setup_one(&qm35_hdl->vdd2, dev, "qm35-vdd2"); + qm35_regulators_setup_one(&qm35_hdl->vdd3, dev, "qm35-vdd3"); + qm35_regulators_setup_one(&qm35_hdl->vdd4, dev, "qm35-vdd4"); + + qm35_hdl->regulators_enabled = false; + qm35_hdl->state = QM35_CTRL_STATE_RESET; +} + +static int qm35_probe(struct spi_device *spi) +{ + struct qm35_ctx *qm35_ctx; + struct miscdevice *uci_misc; + struct miscdevice *coredump_misc; + struct clk *uwb_clk; + int ret = 0; + + if (fwname) { + qmrom_set_fwname(fwname); + } + + if (spi_speed_hz) { + spi->max_speed_hz = spi_speed_hz; + + ret = spi_setup(spi); + if (ret) { + dev_err(&spi->dev, + "spi_setup: requested spi speed=%d ret=%d\n", + spi_speed_hz, ret); + return ret; + } + } + + qm35_ctx = devm_kzalloc(&spi->dev, sizeof(*qm35_ctx), GFP_KERNEL); + if (!qm35_ctx) + return -ENOMEM; + + qm35_ctx->spi = spi; + qm35_ctx->log_qm_traces = log_qm_traces; + spin_lock_init(&qm35_ctx->lock); + init_waitqueue_head(&qm35_ctx->qmrom_wq_ready); + + spi_set_drvdata(spi, qm35_ctx); + + uwb_clk = devm_clk_get(&spi->dev, "uwb_rf_clk5"); + if (IS_ERR(uwb_clk)) { + dev_err(&qm35_ctx->spi->dev, "%s: uwb_clk not found", __func__); + } else { + ret = clk_prepare_enable(uwb_clk); + if(ret) + dev_err(&qm35_ctx->spi->dev, "%s: uwb_clk enable failed", __func__); + } + + qm35_ctx->gpio_csn = devm_gpiod_get_optional(&spi->dev, "csn", + GPIOD_OUT_LOW); + + if (!qm35_ctx->gpio_csn) + { + dev_err(&qm35_ctx->spi->dev, "%s: csn-gpios not found", __func__); + } else { + dev_err(&qm35_ctx->spi->dev, "%s: csn-gpios found!!", __func__); + } + global_temporary_gpio_csn = qm35_ctx->gpio_csn; + + qm35_ctx->gpio_reset = + devm_gpiod_get_optional(&spi->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(qm35_ctx->gpio_reset)) { + ret = PTR_ERR(qm35_ctx->gpio_reset); + return ret; + } + + qm35_regulators_setup(qm35_ctx); + + uci_misc = &qm35_ctx->uci_dev; + uci_misc->minor = MISC_DYNAMIC_MINOR; + uci_misc->name = UCI_DEV_NAME; + uci_misc->fops = &uci_fops; + uci_misc->parent = &spi->dev; + + coredump_misc = &qm35_ctx->coredump_dev; + coredump_misc->minor = MISC_DYNAMIC_MINOR; + coredump_misc->name = COREDUMP_DEV_NAME; + coredump_misc->fops = &coredump_fops; + coredump_misc->parent = &spi->dev; + + /* we need the debugfs root initialized here to be able + * to display the soc info populated if flash_on_probe + * is set for chips different than A0 + */ + ret = debug_init_root(&qm35_ctx->debug, NULL); + if (ret) { + debug_deinit(&qm35_ctx->debug); + goto poweroff; + } + + ret = hsspi_init(&qm35_ctx->hsspi, spi); + if (ret) + goto poweroff; + + ret = uci_layer_init(&qm35_ctx->uci_layer); + if (ret) + goto hsspi_deinit; + + ret = debug_init(&qm35_ctx->debug); + if (ret) + goto uci_layer_deinit; + + ret = hsspi_test_init(&qm35_ctx->hsspi); + if (ret) + goto debug_deinit; + + coredump_layer_init(&qm35_ctx->coredump_layer); + + ret = log_layer_init(&qm35_ctx->log_layer, &qm35_ctx->debug); + if (ret) + goto coredump_layer_deinit; + + ret = hsspi_register(&qm35_ctx->hsspi, + &qm35_ctx->coredump_layer.hlayer); + if (ret) + goto log_layer_deinit; + + ret = hsspi_register(&qm35_ctx->hsspi, &qm35_ctx->log_layer.hlayer); + if (ret) + goto coredump_layer_unregister; + + msleep(QM_BOOT_MS); + + ret = hsspi_irqs_setup(qm35_ctx); + if (ret) + goto log_layer_unregister; + + if (flash_on_probe) { + qm35_regulators_set(qm35_ctx, true); + ret = qm_firmware_load(qm35_ctx); + qm35_regulators_set(qm35_ctx, false); + if (ret) + goto log_layer_unregister; + } + + hsspi_set_gpios(&qm35_ctx->hsspi, qm35_ctx->gpio_ss_rdy, + qm35_ctx->gpio_exton); + + if (!NO_UWB_HAL) { + /* If regulators not available, QM is powered on */ + if (!REGULATORS_ENABLED(qm35_ctx)) + qm35_hsspi_start(qm35_ctx); + } else { + qm35_regulators_set(qm35_ctx, true); + usleep_range(100000, 100000); + qm35_hsspi_start(qm35_ctx); + } + + ret = misc_register(&qm35_ctx->coredump_dev); + if (ret) { + dev_err(&spi->dev, "Failed to register coredump device\n"); + goto log_layer_unregister; + } + + dev_info(&spi->dev, "Registered: [%s] misc device\n", + coredump_misc->name); + + ret = misc_register(&qm35_ctx->uci_dev); + if (ret) { + dev_err(&spi->dev, "Failed to register uci device\n"); + goto coredump_misc_deregister; + } + + dev_info(&spi->dev, "Registered: [%s] misc device\n", uci_misc->name); + + dev_info(&spi->dev, "QM35 spi driver version " DRV_VERSION " probed\n"); + return 0; + +coredump_misc_deregister: + misc_deregister(&qm35_ctx->coredump_dev); +log_layer_unregister: + hsspi_unregister(&qm35_ctx->hsspi, &qm35_ctx->log_layer.hlayer); +coredump_layer_unregister: + hsspi_unregister(&qm35_ctx->hsspi, &qm35_ctx->coredump_layer.hlayer); +log_layer_deinit: + log_layer_deinit(&qm35_ctx->log_layer); +coredump_layer_deinit: + coredump_layer_deinit(&qm35_ctx->coredump_layer); + hsspi_test_deinit(&qm35_ctx->hsspi); +debug_deinit: + debug_deinit(&qm35_ctx->debug); +uci_layer_deinit: + uci_layer_deinit(&qm35_ctx->uci_layer); +hsspi_deinit: + hsspi_deinit(&qm35_ctx->hsspi); +poweroff: + qm35_regulators_set(qm35_ctx, false); + return ret; +} + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0)) +static int qm35_remove(struct spi_device *spi) +#else +static void qm35_remove(struct spi_device *spi) +#endif +{ + struct qm35_ctx *qm35_hdl = spi_get_drvdata(spi); + + misc_deregister(&qm35_hdl->uci_dev); + misc_deregister(&qm35_hdl->coredump_dev); + + qm35_hsspi_stop(qm35_hdl); + + hsspi_unregister(&qm35_hdl->hsspi, &qm35_hdl->log_layer.hlayer); + hsspi_unregister(&qm35_hdl->hsspi, &qm35_hdl->coredump_layer.hlayer); + + log_layer_deinit(&qm35_hdl->log_layer); + coredump_layer_deinit(&qm35_hdl->coredump_layer); + hsspi_test_deinit(&qm35_hdl->hsspi); + debug_deinit(&qm35_hdl->debug); + uci_layer_deinit(&qm35_hdl->uci_layer); + + hsspi_deinit(&qm35_hdl->hsspi); + + qm35_regulators_set(qm35_hdl, false); + + dev_info(&spi->dev, "Deregistered: [%s] misc device\n", + qm35_hdl->uci_dev.name); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0)) + return 0; +#endif +} + +#ifdef CONFIG_PM_SLEEP +static int qm35_pm_suspend(struct device *dev) +{ + struct qm35_ctx *qm35_hdl = dev_get_drvdata(dev); + + qm35_hsspi_stop(qm35_hdl); + + return 0; +} + +static int qm35_pm_resume(struct device *dev) +{ + struct qm35_ctx *qm35_hdl = dev_get_drvdata(dev); + + qm35_hsspi_start(qm35_hdl); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(qm35_spi_ops, qm35_pm_suspend, qm35_pm_resume); +#if KERNEL_VERSION(5, 17, 0) > LINUX_VERSION_CODE +#define pm_sleep_ptr(_ptr) (IS_ENABLED(CONFIG_PM_SLEEP) ? (_ptr) : NULL) +#endif + +static struct spi_driver qm35_spi_driver = { + .driver = { + .name = "qm35", + .of_match_table = of_match_ptr(qm35_dt_ids), + .pm = pm_sleep_ptr(&qm35_spi_ops), + }, + .probe = qm35_probe, + .remove = qm35_remove, +}; +module_spi_driver(qm35_spi_driver); + +MODULE_AUTHOR("Qorvo US, Inc."); +MODULE_DESCRIPTION("QM35 SPI device interface"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); diff --git a/drivers/uwb/qm35/qm35-trace.c b/drivers/uwb/qm35/qm35-trace.c new file mode 100644 index 000000000000..9bbc0f386c5d --- /dev/null +++ b/drivers/uwb/qm35/qm35-trace.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2021 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 trace + */ + +#include + +#define CREATE_TRACE_POINTS +#include "qm35-trace.h" diff --git a/drivers/uwb/qm35/qm35-trace.h b/drivers/uwb/qm35/qm35-trace.h new file mode 100644 index 000000000000..6c23566391ed --- /dev/null +++ b/drivers/uwb/qm35/qm35-trace.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2021 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 trace + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM qm35 + +#if !defined(_QM35_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _QM35_TRACE_H + +#include + +#include "hsspi.h" + +#define show_work_type(type) \ + __print_symbolic(type, \ + { \ + HSSPI_WORK_TX, \ + "TX", \ + }, \ + { \ + HSSPI_WORK_COMPLETION, \ + "COMPLETION", \ + }, \ + { \ + -1, \ + "no", \ + }) + +TRACE_DEFINE_ENUM(HSSPI_WORK_TX); +TRACE_DEFINE_ENUM(HSSPI_WORK_COMPLETION); + +TRACE_EVENT(hsspi_get_work, TP_PROTO(const struct device *dev, int type), + TP_ARGS(dev, type), + TP_STRUCT__entry(__string(dev, dev_name(dev)) __field(int, type)), + TP_fast_assign(__assign_str(dev, dev_name(dev)); + __entry->type = type;), + TP_printk("[%s]: %s work", __get_str(dev), + show_work_type(__entry->type))); + +#define show_hsspi_state(state) \ + __print_symbolic(state, \ + { \ + HSSPI_RUNNING, \ + "running", \ + }, \ + { \ + HSSPI_ERROR, \ + "error", \ + }, \ + { \ + HSSPI_STOPPED, \ + "stopped", \ + }) + +TRACE_DEFINE_ENUM(HSSPI_RUNNING); +TRACE_DEFINE_ENUM(HSSPI_ERROR); +TRACE_DEFINE_ENUM(HSSPI_STOPPED); + +TRACE_EVENT(hsspi_is_txrx_waiting, + TP_PROTO(const struct device *dev, bool is_empty, + enum hsspi_state state), + TP_ARGS(dev, is_empty, state), + TP_STRUCT__entry(__string(dev, dev_name(dev)) + __field(bool, is_empty) + __field(enum hsspi_state, state)), + TP_fast_assign(__assign_str(dev, dev_name(dev)); + __entry->is_empty = is_empty; + __entry->state = state;), + TP_printk("[%s]: is_empty: %d state: %s", __get_str(dev), + __entry->is_empty, show_hsspi_state(__entry->state))); + +#define STC_ENTRY(header) \ + __field(u8, header##flags) __field(u8, header##ul) \ + __field(u16, header##length) +#define STC_ASSIGN(header, var) \ + __entry->header##flags = var->flags; \ + __entry->header##ul = var->ul; \ + __entry->header##length = var->length; +#define STC_FMT "flags:0x%hhx ul:%hhd len:%hd" +#define STC_ARG(header) \ + __entry->header##flags, __entry->header##ul, __entry->header##length + +TRACE_EVENT(hsspi_spi_xfer, + TP_PROTO(const struct device *dev, const struct stc_header *host, + struct stc_header *soc, int ret), + TP_ARGS(dev, host, soc, ret), + TP_STRUCT__entry(__string(dev, dev_name(dev)) STC_ENTRY(host) + STC_ENTRY(soc) __field(int, ret)), + TP_fast_assign(__assign_str(dev, dev_name(dev)); + STC_ASSIGN(host, host); STC_ASSIGN(soc, soc); + __entry->ret = ret;), + TP_printk("[%s]: host " STC_FMT " | soc " STC_FMT " rc=%d", + __get_str(dev), STC_ARG(host), STC_ARG(soc), + __entry->ret)); + +#endif /* _QM35_TRACE_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH ../../../../motorola/kernel/modules/drivers/uwb/qm35 +// clang-format off +#define TRACE_INCLUDE_FILE qm35-trace +// clang-format on +#include diff --git a/drivers/uwb/qm35/qm35.h b/drivers/uwb/qm35/qm35.h new file mode 100644 index 000000000000..61ef2ba884f4 --- /dev/null +++ b/drivers/uwb/qm35/qm35.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __QM35_H___ +#define __QM35_H___ + +#include +#include +#include +#include +#include +#include + +#include "uci_ioctls.h" +#include "qm35_coredump_ioctls.h" +#include "hsspi.h" +#include "hsspi_uci.h" +#include "hsspi_coredump.h" +#include "hsspi_log.h" +#include "debug.h" + +#define FWUPDATER_SPI_SPEED_HZ 20000000 +#define DEFAULT_SPI_CLOCKRATE 3000000 + +#define DEBUG_CERTIFICATE_SIZE 2560 +#define QM_RESET_LOW_MS 2 +/* + * value found using a SALAE + */ +#define QM_BOOT_MS 450 +#define QM_BEFORE_RESET_MS 450 + +#define DRV_VERSION "magenta-7.4.0-rc2" + +struct regulator; + +/** + * struct qm35_ctx - QM35 driver context + * + */ +struct qm35_ctx { + unsigned int state; + struct miscdevice uci_dev; + struct miscdevice coredump_dev; + struct spi_device *spi; + struct gpio_desc *gpio_csn; + struct gpio_desc *gpio_reset; + struct gpio_desc *gpio_ss_rdy; + struct gpio_desc *gpio_ss_irq; + struct gpio_desc *gpio_exton; + struct gpio_desc *gpio_wakeup; + int ss_rdy_irq; + spinlock_t lock; + bool out_data_wait; + bool out_active; + bool soc_error; + struct hsspi hsspi; + struct uci_layer uci_layer; + struct coredump_layer coredump_layer; + struct log_layer log_layer; + struct debug debug; + struct regulator *vdd1; + struct regulator *vdd2; + struct regulator *vdd3; + struct regulator *vdd4; + bool regulators_enabled; + bool log_qm_traces; + + /* qmrom support */ + struct wait_queue_head qmrom_wq_ready; + bool qmrom_qm_ready; + bool flashing; +}; + +static inline unsigned int qm35_get_state(struct qm35_ctx *qm35_hdl) +{ + return qm35_hdl->state; +} + +static inline void qm35_set_state(struct qm35_ctx *qm35_hdl, int state) +{ + unsigned long flags; + + spin_lock_irqsave(&qm35_hdl->lock, flags); + qm35_hdl->state = state; + spin_unlock_irqrestore(&qm35_hdl->lock, flags); +} + +static inline int qm35_reset(struct qm35_ctx *qm35_hdl, int timeout_ms) +{ + if (qm35_hdl->gpio_reset) { + qm35_set_state(qm35_hdl, QM35_CTRL_STATE_RESET); + gpiod_set_value(qm35_hdl->gpio_reset, 1); + usleep_range(timeout_ms * 1000UL, timeout_ms * 1000UL); + gpiod_set_value(qm35_hdl->gpio_reset, 0); + qm35_set_state(qm35_hdl, QM35_CTRL_STATE_UNKNOWN); + return 0; + } + + return -ENODEV; +} + +int qm35_reset_sync(struct qm35_ctx *qm35_hdl); + +int qm_get_dev_id(struct qm35_ctx *qm35_hdl, uint16_t *dev_id); +int qm_get_soc_id(struct qm35_ctx *qm35_hdl, uint8_t *soc_id); + +void qm35_hsspi_start(struct qm35_ctx *qm35_hdl); +void qm35_hsspi_stop(struct qm35_ctx *qm35_hdl); + +void qmrom_set_fwname(const char *name); + +#endif /* __QM35_H___ */ diff --git a/drivers/uwb/qm35/qm35_coredump_ioctls.h b/drivers/uwb/qm35/qm35_coredump_ioctls.h new file mode 100644 index 000000000000..98f5e5d7c403 --- /dev/null +++ b/drivers/uwb/qm35/qm35_coredump_ioctls.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __QM35_COREDUMP_IOCTLS_H___ +#define __QM35_COREDUMP_IOCTLS_H___ + +#include + +#define COREDUMP_DEV_NAME "qm35-coredump" +#define COREDUMP_IOC_TYPE 'U' + +#define QM35_COREDUMP_FORCE _IO(COREDUMP_IOC_TYPE, 1) +#define QM35_COREDUMP_GET_SIZE _IOR(COREDUMP_IOC_TYPE, 2, unsigned int) + +#endif /* __QM35_COREDUMP_IOCTLS_H___ */ diff --git a/drivers/uwb/qm35/qm35_rb.c b/drivers/uwb/qm35/qm35_rb.c new file mode 100644 index 000000000000..cc7becfcd20d --- /dev/null +++ b/drivers/uwb/qm35/qm35_rb.c @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 Ringbuffer + */ + +#include +#include +#include + +#include "qm35_rb.h" + +static bool __rb_can_pop(struct rb *rb, uint32_t *tail) +{ + // if tail equals with the head index, no data can be popped + return *tail != rb->head; +} + +bool rb_can_pop(struct rb *rb) +{ + bool can_pop = false; + + mutex_lock(&rb->lock); + can_pop = __rb_can_pop(rb, &rb->rdtail); + mutex_unlock(&rb->lock); + + return can_pop; +} + +static rb_entry_size_t __rb_next_size(struct rb *rb, uint32_t *tail) +{ + rb_entry_size_t next_packet_size = 0; + + // check if something is available to pop from the ring buffer + if (!__rb_can_pop(rb, tail)) + return 0; + + // read next packet size + memcpy(&next_packet_size, rb->buf + *tail, sizeof(next_packet_size)); + + if (next_packet_size == 0) { + if (*tail == 0) + return 0; + // if the next packet size is 0x00, it is an + // indication that we hit the end marker so we should + // start reading from the beginning + *tail = 0; + memcpy(&next_packet_size, rb->buf + *tail, + sizeof(next_packet_size)); + } + + return next_packet_size; +} + +rb_entry_size_t rb_next_size(struct rb *rb) +{ + rb_entry_size_t next_packet_size = 0; + + mutex_lock(&rb->lock); + next_packet_size = __rb_next_size(rb, &rb->rdtail); + mutex_unlock(&rb->lock); + + return next_packet_size; +} + +static char *__rb_pop(struct rb *rb, rb_entry_size_t *len, uint32_t *tail) +{ + char *trace; + + *len = __rb_next_size(rb, tail); + + if (*len == 0) + return 0; + + // advance ptr with sizeof(next_entry_size) + *tail += sizeof(*len); + + // allocate memory for data + trace = kmalloc(*len, GFP_KERNEL); + + // get the data + memcpy(trace, rb->buf + *tail, *len); + *tail += *len; + + return trace; +} + +char *rb_pop(struct rb *rb, rb_entry_size_t *len) +{ + char *entry; + + mutex_lock(&rb->lock); + entry = __rb_pop(rb, len, &rb->rdtail); + mutex_unlock(&rb->lock); + + return entry; +} + +static bool __rb_skip(struct rb *rb) +{ + rb_entry_size_t next_entry_size; + + // read next packet size + memcpy(&next_entry_size, rb->buf + rb->tail, sizeof(next_entry_size)); + + if (next_entry_size == 0) { + if (rb->tail == 0) + return false; + // if the next packet size is 0x00, it is an + // indication that we hit the end marker so we should + // start reading from the beginning + rb->tail = 0; + memcpy(&next_entry_size, rb->buf + rb->tail, + sizeof(next_entry_size)); + } + + // advance ptr with sizeof(next_entry_size) + rb->tail += sizeof(next_entry_size) + next_entry_size; + + return true; +} + +int rb_push(struct rb *rb, const char *data, rb_entry_size_t len) +{ + rb_entry_size_t entry_size; + uint32_t available_to_end; + + // doesn't make sense to push a packet with the payload len 0. + if (len == 0) + return 1; + + mutex_lock(&rb->lock); + + // calculate how much data we want to store + // we add the size of the trace with the size of the size of the trace + // because we want to store the size as well for reading + entry_size = sizeof(len) + len; + + // available data to the end of the ring buffer + available_to_end = rb->size - rb->head; + + // we check that by writing the buffer still has 2 bytes left + // to write the buffer end marker 0x0000 if not, we just add + // the marker and start from beginning + if (available_to_end <= entry_size + sizeof(rb_entry_size_t)) { + memset(rb->buf + rb->head, 0, sizeof(rb_entry_size_t)); + if (rb->tail >= rb->head) + rb->tail = 0; + if (rb->rdtail >= rb->head) + rb->rdtail = 0; + rb->head = 0; + } + + // check if writing the new data will cause the head index to + // overrun the tail index + while ((rb->head <= rb->tail) && (rb->head + entry_size >= rb->tail)) { + bool equal_tails = rb->tail == rb->rdtail; + // need to adjust the tail to point to the next log + // not overwritten + bool skipped = __rb_skip(rb); + + if (equal_tails) + rb->rdtail = rb->tail; + + if (!skipped) + break; + } + + // copy the size first + memcpy(rb->buf + rb->head, &len, sizeof(len)); + // move the head by sizeof(trace->size) to be in place for copying the data + rb->head += sizeof(len); + // copy the data + memcpy(rb->buf + rb->head, data, len); + // update the head ptr to the next write + rb->head += len; + + mutex_unlock(&rb->lock); + + return 0; +} + +void rb_reset(struct rb *rb) +{ + mutex_lock(&rb->lock); + rb->rdtail = rb->tail; + mutex_unlock(&rb->lock); +} + +int rb_init(struct rb *rb, uint32_t size) +{ + rb->buf = kmalloc(size, GFP_KERNEL); + if (!rb->buf) + return -ENOMEM; + + mutex_init(&rb->lock); + rb->head = 0; + rb->tail = 0; + rb->rdtail = 0; + rb->size = size; + + return 0; +} + +void rb_deinit(struct rb *rb) +{ + kfree(rb->buf); +} diff --git a/drivers/uwb/qm35/qm35_rb.h b/drivers/uwb/qm35/qm35_rb.h new file mode 100644 index 000000000000..46d3478ead99 --- /dev/null +++ b/drivers/uwb/qm35/qm35_rb.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2022 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 Ringbuffer + */ + +#ifndef __QM35_RB_H__ +#define __QM35_RB_H__ + +#include + +typedef uint16_t rb_entry_size_t; + +struct rb { + uint8_t *buf; + uint32_t size; + uint32_t head; + uint32_t tail; + uint32_t rdtail; + struct mutex lock; +}; + +bool rb_can_pop(struct rb *rb); +rb_entry_size_t rb_next_size(struct rb *rb); + +char *rb_pop(struct rb *rb, rb_entry_size_t *len); +int rb_push(struct rb *rb, const char *data, rb_entry_size_t len); +void rb_reset(struct rb *rb); + +int rb_init(struct rb *rb, uint32_t size); +void rb_deinit(struct rb *rb); + +#endif // __QM35_RB_H__ diff --git a/drivers/uwb/qm35/qmrom_spi.c b/drivers/uwb/qm35/qmrom_spi.c new file mode 100644 index 000000000000..af631a7542b2 --- /dev/null +++ b/drivers/uwb/qm35/qmrom_spi.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * This file is part of the QM35 UCI stack for linux. + * + * Copyright (c) 2021 Qorvo US, Inc. + * + * This software is provided under the GNU General Public License, version 2 + * (GPLv2), as well as under a Qorvo commercial license. + * + * You may choose to use this software under the terms of the GPLv2 License, + * version 2 ("GPLv2"), as published by the Free Software Foundation. + * You should have received a copy of the GPLv2 along with this program. If + * not, see . + * + * This program is distributed under the GPLv2 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 GPLv2 for more + * details. + * + * If you cannot meet the requirements of the GPLv2, you may not use this + * software for any purpose without first obtaining a commercial license from + * Qorvo. + * Please contact Qorvo to inquire about licensing terms. + * + * QM35 FW ROM protocol SPI ops + */ + +#include +#include + +#include +#include + +#include "qm35.h" + +/* TODO Compile QM358XX code */ +int qm358xx_rom_probe_device(struct qmrom_handle *handle) +{ + return -1; +} + +static const char *fwname = NULL; +static unsigned int speed_hz; +extern int trace_spi_xfers; + +void qmrom_set_fwname(const char *name) +{ + fwname = name; +} + +int qmrom_spi_transfer(void *handle, char *rbuf, const char *wbuf, size_t size) +{ + struct spi_device *spi = (struct spi_device *)handle; + struct qm35_ctx *qm35_ctx = spi_get_drvdata(spi); + int rc; + + struct spi_transfer xfer[] = { + { + .tx_buf = wbuf, + .rx_buf = rbuf, + .len = size, + .speed_hz = qmrom_spi_get_freq(), + }, + }; + + qm35_ctx->qmrom_qm_ready = false; + rc = spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); + + if (trace_spi_xfers) { + print_hex_dump(KERN_DEBUG, "qm35 tx:", DUMP_PREFIX_NONE, 16, 1, + wbuf, size, false); + print_hex_dump(KERN_DEBUG, "qm35 rx:", DUMP_PREFIX_NONE, 16, 1, + rbuf, size, false); + } + + return rc; +} + +#if 0 +int qmrom_spi_set_cs_level(void *handle, int level) +{ + struct spi_device *spi = (struct spi_device *)handle; + uint8_t dummy = 0; + + struct spi_transfer xfer[] = { + { + .tx_buf = &dummy, + .len = 1, + .cs_change = !level, + .speed_hz = qmrom_spi_get_freq(), + }, + }; + + return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); +} +#else +extern void qm35_set_csn_level(int level); +int qmrom_spi_set_cs_level(void *handle, int level) +{ + qm35_set_csn_level(level); + return 0; +} +#endif + +int qmrom_spi_reset_device(void *reset_handle) +{ + struct qm35_ctx *qm35_hdl = (struct qm35_ctx *)reset_handle; + + return qm35_reset(qm35_hdl, SPI_RST_LOW_DELAY_MS); +} + +const struct firmware *qmrom_spi_get_firmware(void *handle, + struct qmrom_handle *qmrom_h, + bool *is_macro_pkg, + bool use_prod_fw) +{ + const struct firmware *fw; + struct spi_device *spi = handle; + const char *fw_name; + int ret; + uint32_t lcs_state = qmrom_h->qm357xx_soc_info.lcs_state; + + if (!fwname) { + if (lcs_state != CC_BSV_SECURE_LCS) { + dev_warn(&spi->dev, "LCS state is not secure."); + } + + if (use_prod_fw) + fw_name = "qm35_fw_pkg_prod.bin"; + else if (qmrom_h->chip_rev >= CHIP_REVISION_C0) + fw_name = "qm35_fw_pkg.bin"; + else + fw_name = "qm35_b0_fw_pkg.bin"; + *is_macro_pkg = true; + } else { + fw_name = fwname; + } + dev_info(&spi->dev, "Requesting fw %s!\n", fw_name); + + ret = request_firmware(&fw, fw_name, &spi->dev); + if (ret) { + if (lcs_state != CC_BSV_SECURE_LCS) { + dev_warn(&spi->dev, "LCS state is not secure."); + } + + /* Didn't get the macro package, try the stitched image */ + *is_macro_pkg = false; + if (use_prod_fw) + fw_name = "qm35_oem_prod.bin"; + else + fw_name = "qm35_oem.bin"; + dev_info(&spi->dev, "Requesting fw %s!\n", fw_name); + ret = request_firmware(&fw, fw_name, &spi->dev); + if (!ret) { + dev_info(&spi->dev, "Firmware size is %zu!\n", + fw->size); + return fw; + } + + release_firmware(fw); + dev_err(&spi->dev, + "request_firmware failed (ret=%d) for '%s'\n", ret, + fw_name); + return NULL; + } + + dev_info(&spi->dev, "Firmware size is %zu!\n", fw->size); + + return fw; +} + +void qmrom_spi_release_firmware(const struct firmware *fw) +{ + release_firmware(fw); +} + +int qmrom_spi_wait_for_ready_line(void *handle, unsigned int timeout_ms) +{ + struct qm35_ctx *qm35_ctx = (struct qm35_ctx *)handle; + + wait_event_interruptible_timeout(qm35_ctx->qmrom_wq_ready, + qm35_ctx->qmrom_qm_ready, + msecs_to_jiffies(timeout_ms)); + return gpiod_get_value(qm35_ctx->gpio_ss_rdy) > 0 ? 0 : -1; +} + +int qmrom_spi_read_irq_line(void *handle) +{ + return gpiod_get_value(handle); +} + +void qmrom_spi_set_freq(unsigned int freq) +{ + speed_hz = freq; +} + +unsigned int qmrom_spi_get_freq() +{ + return speed_hz; +} diff --git a/drivers/uwb/qm35/uci_ioctls.h b/drivers/uwb/qm35/uci_ioctls.h new file mode 100644 index 000000000000..b0adf2fed44f --- /dev/null +++ b/drivers/uwb/qm35/uci_ioctls.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __UCI_IOCTLS_H___ +#define __UCI_IOCTLS_H___ + +#include + +#define UCI_DEV_NAME "uci" +#define UCI_IOC_TYPE 'U' + +#define QM35_CTRL_RESET _IOR(UCI_IOC_TYPE, 1, unsigned int) +#define QM35_CTRL_GET_STATE _IOR(UCI_IOC_TYPE, 2, unsigned int) +#define QM35_CTRL_FW_UPLOAD _IOR(UCI_IOC_TYPE, 3, unsigned int) +#define QM35_CTRL_POWER _IOW(UCI_IOC_TYPE, 4, unsigned int) + +/* qm35 states */ +enum qm35_ctrl_state { + QM35_CTRL_STATE_UNKNOWN = 0x0000, + QM35_CTRL_STATE_OFF = 0x0001, + QM35_CTRL_STATE_RESET = 0x0002, + QM35_CTRL_STATE_COREDUMP = 0x0004, + QM35_CTRL_STATE_READY = 0x0008, + QM35_CTRL_STATE_FW_DOWNLOADING = 0x0010, + QM35_CTRL_STATE_UCI_APP = 0x0020, +}; + +#endif /* __UCI_IOCTLS_H___ */ diff --git a/drivers/uwb/sr100/Android.mk b/drivers/uwb/sr100/Android.mk new file mode 100644 index 000000000000..928a25e6c2f7 --- /dev/null +++ b/drivers/uwb/sr100/Android.mk @@ -0,0 +1,9 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := sr100.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_info.ko +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/uwb/sr100/Kbuild b/drivers/uwb/sr100/Kbuild new file mode 100644 index 000000000000..dbd8f83d3b3b --- /dev/null +++ b/drivers/uwb/sr100/Kbuild @@ -0,0 +1,11 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +ifneq ($(filter banks%, $(TARGET_PRODUCT)),) + EXTRA_CFLAGS += -DSOFTDEP_GPIO_PCAL6408 +endif + +obj-m += sr100.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../../mmi_info/Module.symvers diff --git a/drivers/uwb/sr100/Makefile b/drivers/uwb/sr100/Makefile new file mode 100644 index 000000000000..4916d32b6d02 --- /dev/null +++ b/drivers/uwb/sr100/Makefile @@ -0,0 +1,17 @@ +all: modules + +modules: + $(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 + +KBUILD_EXTRA_SYMBOLS := $(M)/../../mmi_info/Module.symvers + diff --git a/drivers/uwb/sr100/sr100.c b/drivers/uwb/sr100/sr100.c new file mode 100644 index 000000000000..b7b107e21d11 --- /dev/null +++ b/drivers/uwb/sr100/sr100.c @@ -0,0 +1,1178 @@ +/*====================================================================================*/ +/* */ +/* Copyright 2018-2019 NXP */ +/* */ +/* 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. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*====================================================================================*/ + +/** +* \addtogroup spi_driver +* +* @{ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* To control VDD gpios in Hikey for HVH board */ +#define HVH_VDD_ENABLE 0 +/* Cold reset Feature in case of Secure Element tx timeout */ +#define ESE_COLD_RESET 0 + +#if ESE_COLD_RESET +#include "../pn8xT/pn553-i2c/cold_reset.h" +/*Invoke cold reset if no response from eSE*/ +extern int ese_cold_reset(ese_cold_reset_origin_t src); +#endif +static bool read_abort_requested = false; +static bool is_fw_dwnld_enabled = false; +#define SR100_TXBUF_SIZE 4200 +#define SR100_RXBUF_SIZE 4200 +#define SR100_MAX_TX_BUF_SIZE 4200 +#define MAX_READ_RETRY_COUNT 10 +#define UCI_MT_MASK 0xE0 +/* Macro to define SPI clock frequency */ + +#define SR100_SPI_CLOCK 16000000L; +#define ENABLE_THROUGHPUT_MEASUREMENT 0 + +/* Maximum UCI packet size supported from the driver */ +#define MAX_UCI_PKT_SIZE 4200 + +/* Different driver debug lever */ +enum SR100_DEBUG_LEVEL { SR100_DEBUG_OFF, SR100_FULL_DEBUG }; +enum spi_status_codes{ + spi_transcive_success, + spi_transcive_fail, + spi_irq_wait_request, + spi_irq_wait_timeout +}; +enum spi_operation_modes{SR100_WRITE_MODE, SR100_READ_MODE}; + + +/* Variable to store current debug level request by ioctl */ +static unsigned char debug_level; + +#define SR100_DBG_MSG(msg...) \ + switch (debug_level) { \ + case SR100_DEBUG_OFF: \ + break; \ + case SR100_FULL_DEBUG: \ + printk(KERN_INFO "[NXP-SR100] : " msg); \ + break; \ + default: \ + printk(KERN_ERR "[NXP-SR100] : Wrong debug level %d", debug_level); \ + break; \ + } + +#define SR100_ERR_MSG(msg...) printk(KERN_ERR "[NXP-SR100] : " msg); + +/* Device specific macro and structure */ +struct sr100_dev { + wait_queue_head_t read_wq; /* wait queue for read interrupt */ + struct spi_device* spi; /* spi device structure */ + struct miscdevice sr100_device; /* char device as misc driver */ + unsigned int ce_gpio; /* SW Reset gpio */ + unsigned int irq_gpio; /* SR100 will interrupt DH for any ntf */ + unsigned int spi_handshake_gpio; /* host ready to read data */ + unsigned int rtc_sync_gpio; /* rtc sync support for helios ranging */ + bool irq_enabled; /* flag to indicate disable/enable irq sequence */ + bool irq_received; /* flag to indicate that irq is received */ + spinlock_t irq_enabled_lock; /* spin lock for read irq */ + unsigned char* tx_buffer; /* transmit buffer */ + unsigned char* rx_buffer; /* receive buffer buffer */ + unsigned int write_count; /* Holds nubers of byte writen*/ + unsigned int read_count; /* Hold nubers of byte read */ + struct mutex sr100_access_lock;/* Hold mutex lock to between read and write */ + size_t totalBtyesToRead; + size_t IsExtndLenIndication; + int mode; + long timeOutInMs; +#if HVH_VDD_ENABLE + unsigned int vdd_1v8_gpio; + unsigned int vdd_1v8_rf_gpio; + unsigned int vbat_3v6_gpio; +#endif +}; +#if (ENABLE_THROUGHPUT_MEASUREMENT == 1) +#define READ_THROUGH_PUT 0x01 +#define WRITE_THROUGH_PUT 0x02 +struct sr100_through_put { + struct timeval rstart_tv; + struct timeval wstart_tv; + struct timeval rstop_tv; + struct timeval wstop_tv; + unsigned long total_through_put_wbytes; + unsigned long total_through_put_rbytes; + unsigned long total_through_put_rtime; + unsigned long total_through_put_wtime; +}; +static struct sr100_through_put sr100_through_put_t; +static void sr100_start_throughput_measurement(unsigned int type); +static void sr100_stop_throughput_measurement(unsigned int type, + int no_of_bytes); + +/****************************************************************************** + * Function : sr100_start_throughput_measurement + * + * Description : Start this api to measaure the spi performance + * + * Parameters : type : sr100 device Write/Read + * + * Returns : Returns void + ****************************************************************************/ +static void sr100_start_throughput_measurement(unsigned int type) { + if (type == READ_THROUGH_PUT) { + memset(&sr100_through_put_t.rstart_tv, 0x00, sizeof(struct timeval)); + do_gettimeofday(&sr100_through_put_t.rstart_tv); + } else if (type == WRITE_THROUGH_PUT) { + memset(&sr100_through_put_t.wstart_tv, 0x00, sizeof(struct timeval)); + do_gettimeofday(&sr100_through_put_t.wstart_tv); + + } else { + printk(KERN_ALERT " sr100_start_throughput_measurement: wrong type = %d", + type); + } +} +/****************************************************************************** + * Function : sr100_stop_throughput_measurement + * + * Description : Stop this api to end the measaure of the spi performance + * + * Parameters : type : sr100 device Write/Read + * + * Returns : Returns void + ****************************************************************************/ +static void sr100_stop_throughput_measurement(unsigned int type, + int no_of_bytes) { + if (type == READ_THROUGH_PUT) { + memset(&sr100_through_put_t.rstop_tv, 0x00, sizeof(struct timeval)); + do_gettimeofday(&sr100_through_put_t.rstop_tv); + sr100_through_put_t.total_through_put_rbytes += no_of_bytes; + sr100_through_put_t.total_through_put_rtime += + (sr100_through_put_t.rstop_tv.tv_usec - + sr100_through_put_t.rstart_tv.tv_usec) + + ((sr100_through_put_t.rstop_tv.tv_sec - + sr100_through_put_t.rstart_tv.tv_sec) * + 1000000); + } else if (type == WRITE_THROUGH_PUT) { + memset(&sr100_through_put_t.wstop_tv, 0x00, sizeof(struct timeval)); + do_gettimeofday(&sr100_through_put_t.wstop_tv); + sr100_through_put_t.total_through_put_wbytes += no_of_bytes; + sr100_through_put_t.total_through_put_wtime += + (sr100_through_put_t.wstop_tv.tv_usec - + sr100_through_put_t.wstart_tv.tv_usec) + + ((sr100_through_put_t.wstop_tv.tv_sec - + sr100_through_put_t.wstart_tv.tv_sec) * + 1000000); + } else { + printk(KERN_ALERT " sr100_stop_throughput_measurement: wrong type = %d", + type); + } +} +#endif + +/****************************************************************************** + * Function : sr100_dev_open + * + * Description : Open sr100 device node and returns instance to the user space + * + * Parameters : inode : sr100 device node path + * filep : File pointer to structure of sr100 device + * + * Returns : Returns file descriptor for sr100 device + * otherwise indicate each error code + ****************************************************************************/ +static int sr100_dev_open(struct inode* inode, struct file* filp) { + struct sr100_dev* sr100_dev = + container_of(filp->private_data, struct sr100_dev, sr100_device); + + filp->private_data = sr100_dev; + SR100_DBG_MSG("%s : Major No: %d, Minor No: %d\n", __func__, imajor(inode), + iminor(inode)); + + return 0; +} + +/****************************************************************************** + * Function : sr100_disable_irq + * + * Description : To disable IR + * + * Parameters : sr100_dev : sr100 device structure pointer + * + * Returns : Returns void + ****************************************************************************/ +static void sr100_disable_irq(struct sr100_dev* sr100_dev) { + unsigned long flags; + spin_lock_irqsave(&sr100_dev->irq_enabled_lock, flags); + if((sr100_dev->irq_enabled)){ + disable_irq_nosync(sr100_dev->spi->irq); + sr100_dev->irq_received = true; + sr100_dev->irq_enabled = false; + } + spin_unlock_irqrestore(&sr100_dev->irq_enabled_lock, flags); +} +/****************************************************************************** + * Function : sr100_enable_irq + * + * Description : Set the irq flag status + * + * Parameters : sr100_dev : sr100 device structure pointer + * + * Returns : Returns void + ****************************************************************************/ +static void sr100_enable_irq(struct sr100_dev* sr100_dev) { + unsigned long flags; + spin_lock_irqsave(&sr100_dev->irq_enabled_lock, flags); + if(!sr100_dev->irq_enabled){ + enable_irq(sr100_dev->spi->irq); + sr100_dev->irq_enabled = true; + sr100_dev->irq_received = false; + } + spin_unlock_irqrestore(&sr100_dev->irq_enabled_lock, flags); +} + +/****************************************************************************** + * Function : sr100_dev_irq_handler + * + * Description : Will get called when interrupt line asserted from SR100 + * + * Parameters : irq : IRQ Number + * dev_id : sr100 device Id + * + * Returns : Returns IRQ Handler + ****************************************************************************/ +static irqreturn_t sr100_dev_irq_handler(int irq, void* dev_id) { + struct sr100_dev* sr100_dev = dev_id; + sr100_disable_irq(sr100_dev); + /* Wake up waiting readers */ + wake_up(&sr100_dev->read_wq); + return IRQ_HANDLED; +} + +/****************************************************************************** + * Function : sr100_dev_iotcl + * + * Description : Input/OutPut control from user space to perform required + * operation on sr100 device. + * + * Parameters : cmd : Indicates what operation needs to be done sr100 + * arg : Value to be passed to sr100 to do the required + * opeation + * + * Returns : 0 on success and (-1) on error + ****************************************************************************/ +static long sr100_dev_ioctl(struct file* filp, unsigned int cmd, + unsigned long arg) { + int ret = 0; + struct sr100_dev* sr100_dev = NULL; + pr_info("sr100 - %s\n", __FUNCTION__); + sr100_dev = filp->private_data; + switch (cmd) { + case SR100_SET_PWR: + if (arg == PWR_ENABLE) { + printk(KERN_ALERT " enable power request"); + gpio_set_value_cansleep(sr100_dev->ce_gpio, 1); + msleep(10); + } else if (arg == PWR_DISABLE) { + printk(KERN_ALERT "disable power request"); + gpio_set_value_cansleep(sr100_dev->ce_gpio, 0); + sr100_disable_irq(sr100_dev); + msleep(10); + } else if (arg == ABORT_READ_PENDING) { + pr_info("%s Abort Read Pending\n", __func__); + read_abort_requested = true; + sr100_disable_irq(sr100_dev); + /* Wake up waiting readers */ + wake_up(&sr100_dev->read_wq); + } + break; + case SR100_SET_FWD: + if (arg == 1) { + is_fw_dwnld_enabled = true; + read_abort_requested = false; + pr_info("%s FW download enabled.\n", __func__); + } else if(arg == 0){ + is_fw_dwnld_enabled = false; + pr_info("%s FW download disabled.\n", __func__); + } + break; + + case SR100_GET_THROUGHPUT: + if (arg == 0) { +#if (ENABLE_THROUGHPUT_MEASUREMENT == 1) + printk(KERN_ALERT + " **************** Write-Read Throughput: **************"); + printk(KERN_ALERT " No of Write Bytes = %ld", + sr100_through_put_t.total_through_put_wbytes); + printk(KERN_ALERT " No of Read Bytes = %ld", + sr100_through_put_t.total_through_put_rbytes); + printk(KERN_ALERT " Total Write Time (uSec) = %ld", + sr100_through_put_t.total_through_put_wtime); + printk(KERN_ALERT " Total Read Time (uSec) = %ld", + sr100_through_put_t.total_through_put_rtime); + printk(KERN_ALERT " Total Write-Read Time (uSec) = %ld", + sr100_through_put_t.total_through_put_wtime + + sr100_through_put_t.total_through_put_rtime); + sr100_through_put_t.total_through_put_wbytes = 0; + sr100_through_put_t.total_through_put_rbytes = 0; + sr100_through_put_t.total_through_put_wtime = 0; + sr100_through_put_t.total_through_put_rtime = 0; + printk(KERN_ALERT + " **************** Write-Read Throughput: **************"); +#endif + } + break; +#if ESE_COLD_RESET + case SR100_ESE_RESET: + pr_info("%s SR100_ESE_RESET Enter\n", __func__); + ret = ese_cold_reset(ESE_COLD_RESET_SOURCE_UWB); + break; +#endif + default: + printk(KERN_ALERT " Error case"); + ret = -EINVAL; // ToDo: After adding proper switch cases we have to + // return with error statusi here + } + + return ret; +} +/****************************************************************************** +* Function : sr100_dev_transceive +* +* Description : Used to Write/read data from SR100 +* +* Parameters : sr100_dev :sr100 device structure pointer +* op_mode :Indicates write/read mode +* count : Number of bytes to be write/read +* Returns : Number of bytes write/read if read is success else (-1) +* otherwise indicate each error code +****************************************************************************/ + +static int sr100_dev_transceive(struct sr100_dev* sr100_dev, int op_mode, int count ){ + int ret,retry_count; + mutex_lock(&sr100_dev->sr100_access_lock); + sr100_dev->mode = op_mode; + sr100_dev->totalBtyesToRead = 0; + sr100_dev->IsExtndLenIndication = 0; + ret = -1; + retry_count = 0; + + switch(sr100_dev->mode){ + case SR100_WRITE_MODE: + { + sr100_dev->write_count = 0; + /* UCI Header write */ + ret = spi_write(sr100_dev->spi, sr100_dev->tx_buffer, NORMAL_MODE_HEADER_LEN); + if (ret < 0) { + ret = -EIO; + printk("spi_write header : Failed.\n"); + goto transcive_end; + } else { + count -= NORMAL_MODE_HEADER_LEN; + } + if(count > 0) { + usleep_range(30,50); + /* UCI Payload write */ + ret = spi_write(sr100_dev->spi, sr100_dev->tx_buffer + NORMAL_MODE_HEADER_LEN, count); + if (ret < 0) { + ret = -EIO; + printk("spi_write payload : Failed.\n"); + goto transcive_end; + } + } + sr100_dev->write_count = count + NORMAL_MODE_HEADER_LEN; + ret = spi_transcive_success; + } + break; + case SR100_READ_MODE: + { + if(!gpio_get_value(sr100_dev->irq_gpio)){ + printk("IRQ might have gone low due to write "); + ret = spi_irq_wait_request; + goto transcive_end; + } + retry_count = 0; + gpio_set_value_cansleep(sr100_dev->spi_handshake_gpio, 1); + while (gpio_get_value(sr100_dev->irq_gpio)) { + if (retry_count == 100) { + break; + } + udelay(10); + retry_count++; + } + sr100_enable_irq(sr100_dev); + sr100_dev->read_count = 0; + retry_count = 0; + /* wait for inetrrupt upto 500ms after that timeout will happen and returns read fail */ + ret = wait_event_interruptible_timeout(sr100_dev->read_wq, sr100_dev->irq_received,sr100_dev->timeOutInMs); + if (ret == 0) { + printk("wait_event_interruptible timeout() : Failed.\n"); + ret = spi_irq_wait_timeout; + goto transcive_end; + } +#if 0 // ideally below code is not required to check the gpio status in loop + sr100_set_irq_flag(sr100_dev); + if(!gpio_get_value(sr100_dev->irq_gpio)){ + printk("Spurious interrupt detected at second irq"); + retry_count++; + if(retry_count == MAX_READ_RETRY_COUNT){ + retry_count = 0; + printk("Max retry count reached at second irq"); + } else{ + msleep(3); + sr100_dev->irq_enabled = false; + goto second_irq_wait; + } + } +#endif + if(!gpio_get_value(sr100_dev->irq_gpio)){ + printk("Second IRQ is Low"); + ret = -1; + goto transcive_end; + } + ret = spi_read(sr100_dev->spi, (void*)sr100_dev->rx_buffer, NORMAL_MODE_HEADER_LEN); + if (ret < 0) { + pr_info("sr100_dev_read: spi read error %d\n ", ret); + goto transcive_end; + } + + if ((sr100_dev->rx_buffer[0] & UCI_MT_MASK) == 0) { + sr100_dev->totalBtyesToRead = sr100_dev->rx_buffer[NORMAL_MODE_LEN_OFFSET]; + sr100_dev->totalBtyesToRead = ((sr100_dev->totalBtyesToRead << 8) | sr100_dev->rx_buffer[EXTENDED_LENGTH_OFFSET]); + } else { + sr100_dev->IsExtndLenIndication = (sr100_dev->rx_buffer[EXTND_LEN_INDICATOR_OFFSET] & EXTND_LEN_INDICATOR_OFFSET_MASK); + sr100_dev->totalBtyesToRead = sr100_dev->rx_buffer[NORMAL_MODE_LEN_OFFSET]; + if (sr100_dev->IsExtndLenIndication) { + sr100_dev->totalBtyesToRead = ((sr100_dev->totalBtyesToRead << 8) | sr100_dev->rx_buffer[EXTENDED_LENGTH_OFFSET]); + } + } + if(sr100_dev->totalBtyesToRead > (MAX_UCI_PKT_SIZE - NORMAL_MODE_HEADER_LEN)) { + printk("Length %d exceeds the max limit %d....",(int)sr100_dev->totalBtyesToRead,(int)MAX_UCI_PKT_SIZE); + ret = -1; + goto transcive_end; + } + if(sr100_dev->totalBtyesToRead > 0){ + ret = spi_read(sr100_dev->spi, (void*)(sr100_dev->rx_buffer + NORMAL_MODE_HEADER_LEN), sr100_dev->totalBtyesToRead); + if (ret < 0) { + printk("sr100_dev_read: spi read error.. %d\n ", ret); + goto transcive_end; + } + } + sr100_dev->read_count = (unsigned int)(sr100_dev->totalBtyesToRead + NORMAL_MODE_HEADER_LEN); + retry_count = 0; + do{ + usleep_range(10,15); + retry_count++; + if(retry_count == 1000){ + printk("Slave not released the IRQ even after 10ms"); + break; + } + }while(gpio_get_value(sr100_dev->irq_gpio)); + ret = spi_transcive_success; + gpio_set_value_cansleep(sr100_dev->spi_handshake_gpio, 0); + } + break; + default: + printk("invalid operation ....."); + break; + } +transcive_end: + if(sr100_dev->mode == SR100_READ_MODE){ + gpio_set_value_cansleep(sr100_dev->spi_handshake_gpio, 0); + } + mutex_unlock(&sr100_dev->sr100_access_lock); + return ret; +} + +/****************************************************************************** +* Function : sr100_hbci_write +* +* Description : Used to write hbci packets +* +* Parameters : sr100_dev :sr100 device structure pointer +* count : Number of bytes to be write +* Returns : return success(spi_transcive_success)or fail (-1) +****************************************************************************/ + +static int sr100_hbci_write(struct sr100_dev* sr100_dev, int count ){ + int ret = -1; + sr100_dev->write_count = 0; + /* HBCI write */ + ret = spi_write(sr100_dev->spi, sr100_dev->tx_buffer, count); + if (ret < 0) { + ret = -EIO; + printk("spi_write fw download : Failed.\n"); + goto hbci_write_fail; + } + sr100_dev->write_count = count; + sr100_enable_irq(sr100_dev); + ret = spi_transcive_success; + return ret; +hbci_write_fail: + printk("sr100_hbci_write failed...%d", ret); + return ret; +} + +/****************************************************************************** + * Function : sr100_dev_write + * + * Description : Write Data to sr100 on SPI line + * + * Parameters : filp : Device Node File Pointer + * buf : Buffer which contains data to be sent to sr100 + * count : Number of bytes to be send + * offset : Pointer to a object that indicates file position + * user is accessing. + * Returns : Number of bytes writen if write is success else (-1) + * otherwise indicate each error code + ****************************************************************************/ +static ssize_t sr100_dev_write(struct file* filp, const char* buf, size_t count, + loff_t* offset) { + int ret = -1; + struct sr100_dev* sr100_dev; + sr100_dev = filp->private_data; + if (count > SR100_MAX_TX_BUF_SIZE || count > SR100_TXBUF_SIZE) { + SR100_ERR_MSG("%s : Write Size Exceeds\n", __func__); + ret = -ENOBUFS; + goto write_end; + } + if (copy_from_user(sr100_dev->tx_buffer, buf, count)) { + SR100_ERR_MSG("%s : failed to copy from user space \n", __func__); + return -EFAULT; + } +#if (ENABLE_THROUGHPUT_MEASUREMENT == 1) + sr100_start_throughput_measurement(WRITE_THROUGH_PUT); +#endif + if(is_fw_dwnld_enabled){ + ret = sr100_hbci_write(sr100_dev, count); + }else{ + ret = sr100_dev_transceive(sr100_dev,SR100_WRITE_MODE, count); + } + if(ret == spi_transcive_success){ + ret = sr100_dev->write_count; + } else{ + printk("write failed......"); + } +#if (ENABLE_THROUGHPUT_MEASUREMENT == 1) + sr100_stop_throughput_measurement(WRITE_THROUGH_PUT, ret); +#endif +write_end: + return ret; +} + +/****************************************************************************** + * Function : sr100_hbci_read + * + * Description : Read Data From sr100 on SPI line + * + * Parameters : sr100_dev : sr100 device structure + * buf : Buffer which contains data to be read from sr100 + * count : Number of bytes to be read + * + * Returns : Number of bytes read if read is success else (-1) + * otherwise indicate each error code + ****************************************************************************/ +static ssize_t sr100_hbci_read(struct sr100_dev *sr100_dev,char* buf, size_t count){ + int ret = -EIO; + + if(count > SR100_RXBUF_SIZE) { + SR100_ERR_MSG("count(%lu) out of range(0-%d)\n", count, SR100_RXBUF_SIZE); + ret = -EINVAL; + goto hbci_fail; + } + + /* wait for inetrrupt upto 500ms after that timeout will happen and returns read fail */ + ret = wait_event_interruptible_timeout(sr100_dev->read_wq, sr100_dev->irq_received, sr100_dev->timeOutInMs); + if (ret == 0) { + printk("wait_event_interruptible() : Failed.\n"); + ret = -1; + goto hbci_fail; + } + + if (read_abort_requested) { + read_abort_requested = false; + printk("HBCI Abort Read pending......"); + return ret; + } + if(!gpio_get_value(sr100_dev->irq_gpio)){ + printk("IRQ is low during firmware download"); + goto hbci_fail; + } + +#if (ENABLE_THROUGHPUT_MEASUREMENT == 1) + sr100_start_throughput_measurement(READ_THROUGH_PUT); +#endif + ret = spi_read(sr100_dev->spi, (void*)sr100_dev->rx_buffer, count); + if (ret < 0) { + pr_info("sr100_dev_read: spi read error %d\n ", ret); + goto hbci_fail; + } + ret = count; +#if (ENABLE_THROUGHPUT_MEASUREMENT == 1) + sr100_stop_throughput_measurement(READ_THROUGH_PUT, count); +#endif + if (copy_to_user(buf, sr100_dev->rx_buffer, count)) { + pr_info("sr100_dev_read: copy to user failed\n"); + ret = -EFAULT; + } + return ret; +hbci_fail: + printk("Error sr100_fw_download ret %d Exit\n", ret); + return ret; +} +/****************************************************************************** + * Function : sr100_dev_read + * + * Description : Used to read data from SR100 + * + * Parameters : filp : Device Node File Pointer + * buf : Buffer which contains data to be read from sr100 + * count : Number of bytes to be read + * offset : Pointer to a object that indicates file position + * user is accessing. + * Returns : Number of bytes read if read is success else (-1) + * otherwise indicate each error code + ****************************************************************************/ +static ssize_t sr100_dev_read(struct file* filp, char* buf, size_t count, + loff_t* offset) { + struct sr100_dev* sr100_dev = filp->private_data; + int ret = -EIO; + /*500ms timeout in jiffies*/ + sr100_dev->timeOutInMs = ((500*HZ)/1000); + memset(sr100_dev->rx_buffer, 0x00, SR100_RXBUF_SIZE); + if (!gpio_get_value(sr100_dev->irq_gpio)) { + if (filp->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + goto read_end; + } + } + /*HBCI packet read*/ + if(is_fw_dwnld_enabled){ + return sr100_hbci_read(sr100_dev,buf,count); + } + /*UCI packet read*/ +first_irq_wait: + sr100_enable_irq(sr100_dev); + if(!read_abort_requested) { + ret = wait_event_interruptible(sr100_dev->read_wq, sr100_dev->irq_received); + if (ret) { + printk("wait_event_interruptible() : Failed.\n"); + goto read_end; + } + } + if (read_abort_requested) { + read_abort_requested = false; + printk("Abort Read pending......"); + return ret; + } + ret = sr100_dev_transceive(sr100_dev,SR100_READ_MODE, count); + if(ret == spi_transcive_success){ + if (copy_to_user(buf, sr100_dev->rx_buffer, sr100_dev->read_count)) { + pr_info("sr100_dev_read: copy to user failed\n"); + ret = -EFAULT; + goto read_end; + } + ret = sr100_dev->read_count; + } else if(ret == spi_irq_wait_request){ + printk(" irg is low due to write hence irq is requested again..."); + goto first_irq_wait; + } else if(ret == spi_irq_wait_timeout){ + printk("second irq is not received..Time out..."); + ret = -1; + } else { + printk("spi read failed...%d", ret); + ret = -1; + } +read_end: + return ret; +} + +/****************************************************************************** + * Function : sr100_hw_setup + * + * Description : Used to read data from SR100 + * + * Parameters : platform_data : struct sr100_spi_platform_data * + * sr100_dev : struct sr100_dev * + * spi : struct spi_device * + * + * Returns : retval 0 if ok else -1 on error + ****************************************************************************/ +static int sr100_hw_setup(struct sr100_spi_platform_data* platform_data, + struct sr100_dev* sr100_dev, struct spi_device* spi) { + int ret = -1; + + SR100_DBG_MSG("Entry : %s\n", __FUNCTION__); + ret = gpio_request(platform_data->irq_gpio, "sr100 irq"); + if (ret < 0) { + SR100_ERR_MSG("gpio request failed gpio = 0x%x\n", platform_data->irq_gpio); + goto fail; + } + + ret = gpio_direction_input(platform_data->irq_gpio); + if (ret < 0) { + SR100_ERR_MSG("gpio request failed gpio = 0x%x\n", platform_data->irq_gpio); + goto fail_irq; + } + + ret = gpio_direction_output(platform_data->ce_gpio, 0); + if (ret < 0) { + pr_info("sr100 - Failed setting ce gpio - %d\n", platform_data->ce_gpio); + goto fail_gpio; + } + + ret = gpio_request(platform_data->spi_handshake_gpio, "sr100 ri"); + if (ret < 0) { + pr_info("sr100 - Failed requesting ri gpio - %d\n", platform_data->spi_handshake_gpio); + goto fail_gpio; + } + + ret = gpio_direction_output(platform_data->spi_handshake_gpio, 0); + if (ret < 0) { + pr_info("sr100 - Failed setting spi handeshake gpio - %d\n", platform_data->spi_handshake_gpio); + goto fail_gpio; + } + + if (gpio_is_valid(platform_data->rtc_sync_gpio)) { + ret = gpio_request(platform_data->rtc_sync_gpio, "sr100 rtc"); + if (ret < 0) { + pr_info("sr100 - Failed requesting rtc gpio - %d\n", platform_data->rtc_sync_gpio); + goto fail_gpio; + } + + ret = gpio_direction_output(platform_data->rtc_sync_gpio, 1); + if (ret < 0) { + pr_info("sr100 - Failed setting rtc gpio - %d\n", platform_data->rtc_sync_gpio); + goto fail_gpio; + } + } +#if HVH_VDD_ENABLE + ret = gpio_request(platform_data->vdd_1v8_gpio, "sup_vdd_1v8"); + if (ret) { + pr_info("%s: sr100 vdd_1v8_gpio failed\n", __func__); + goto fail_gpio; + } + ret = gpio_direction_output(sr100_dev->vdd_1v8_gpio, 0); + if (ret < 0) { + pr_err("%s : not able to set vdd_1v8_gpio as output\n", __func__); + goto fail_gpio; + } + ret = gpio_request(platform_data->vdd_1v8_rf_gpio, "sup_vdd_rf"); + if (ret) { + pr_info("%s: sr100 vdd_1v8_rf_gpio failed\n", __func__); + goto fail_gpio; + } + ret = gpio_direction_output(sr100_dev->vdd_1v8_rf_gpio, 0); + if (ret < 0) { + pr_err("%s : not able to set vdd_1v8_rf_gpio as output\n", __func__); + goto fail_gpio; + } + ret = gpio_request(platform_data->vbat_3v6_gpio, "sup_vbat_3v6"); + if (ret) { + pr_info("%s: sr100 sup_vbat_3v6 failed\n", __func__); + goto fail_gpio; + } + ret = gpio_direction_output(sr100_dev->vbat_3v6_gpio, 0); + if (ret < 0) { + pr_err("%s : not able to set vbat_3v6_gpio as output\n", __func__); + goto fail_gpio; + } + + pr_info(" HVH Power enable: %s \n", __func__); +#endif + ret = 0; + SR100_DBG_MSG("Exit : %s\n", __FUNCTION__); + return ret; + +fail_gpio: + gpio_free(platform_data->spi_handshake_gpio); + gpio_free(platform_data->rtc_sync_gpio); +#if HVH_VDD_ENABLE + gpio_free(platform_data->vdd_1v8_gpio); + gpio_free(platform_data->vdd_1v8_rf_gpio); + gpio_free(platform_data->vbat_3v6_gpio); +#endif +fail_irq: + gpio_free(platform_data->irq_gpio); +fail: + SR100_ERR_MSG("sr100_hw_setup failed\n"); + return ret; +} +/****************************************************************************** + * Function : sr100_set_data + * + * Description : Set the SR100 device specific context for future use + * + * Parameters : spi : struct spi_device * + * data: void* + * + * Returns : retval 0 if ok else -1 on error + ****************************************************************************/ +static inline void sr100_set_data(struct spi_device* spi, void* data) { + dev_set_drvdata(&spi->dev, data); +} + +/****************************************************************************** + * Function : sr100_get_data + * + * Description : Get the SR100 device specific context + * + * Parameters : spi : struct spi_device * + * + * Returns : retval 0 if ok else -1 on error + ****************************************************************************/ +static inline void* sr100_get_data(const struct spi_device* spi) { + return dev_get_drvdata(&spi->dev); +} + +/* possible fops on the sr100 device */ +static const struct file_operations sr100_dev_fops = { + .owner = THIS_MODULE, + .read = sr100_dev_read, + .write = sr100_dev_write, + .open = sr100_dev_open, + .unlocked_ioctl = sr100_dev_ioctl, +}; +/****************************************************************************** + * Function : sr100_parse_dt + * + * Description : Parse the dtsi configartion + * + * Parameters : dev : struct spi_device * + * pdata: Ponter to platform data + * + * Returns : retval 0 if ok else -1 on error + ****************************************************************************/ +static int sr100_parse_dt(struct device* dev, + struct sr100_spi_platform_data* pdata) { + struct device_node* np = dev->of_node; + + pr_info("sr100 - %s\n", __FUNCTION__); + pdata->irq_gpio = of_get_named_gpio(np, "nxp,sr100-irq", 0); + if (!gpio_is_valid(pdata->irq_gpio)) { + return -EINVAL; + } + pdata->ce_gpio = of_get_named_gpio(np, "nxp,sr100-ce", 0); + if (!gpio_is_valid(pdata->ce_gpio)) { + return -EINVAL; + } + pdata->spi_handshake_gpio = of_get_named_gpio(np, "nxp,sr100-ri", 0); + if (!gpio_is_valid(pdata->spi_handshake_gpio)) { + return -EINVAL; + } + pdata->rtc_sync_gpio = of_get_named_gpio(np, "nxp,sr100-rtc", 0); + if (!gpio_is_valid(pdata->rtc_sync_gpio)) { + pr_info("Not rtc gpio set\n"); + } +#if HVH_VDD_ENABLE + pdata->vdd_1v8_gpio = of_get_named_gpio(np, "nxp,sr100-vdd", 0); + if ((!gpio_is_valid(pdata->vdd_1v8_gpio))) + return -EINVAL; + + pdata->vdd_1v8_rf_gpio = of_get_named_gpio(np, "nxp,sr100-dig", 0); + if ((!gpio_is_valid(pdata->vdd_1v8_rf_gpio))) + return -EINVAL; + + pdata->vbat_3v6_gpio = of_get_named_gpio(np, "nxp,sr100-vbat", 0); + if ((!gpio_is_valid(pdata->vbat_3v6_gpio))) + return -EINVAL; + pr_info("sr100 : vdd_1v8_gpio = %d, vdd_1v8_rf_gpio = %d, vbat_3v6_gpio = %d \n", + pdata->irq_gpio, pdata->vdd_1v8_rf_gpio,pdata->vbat_3v6_gpio); +#endif + pr_info("sr100 : irq_gpio = %d, ce_gpio = %d, spi_handshake_gpio = %d, rtc_sync_gpio = %d \n", + pdata->irq_gpio, pdata->ce_gpio,pdata->spi_handshake_gpio,pdata->rtc_sync_gpio); + return 0; +} +/****************************************************************************** + * Function : sr100_probe + * + * Description : To probe for SR100 SPI interface. If found initialize the SPI + * clock,bit rate & SPI mode. It will create the dev entry + * (SR100) for user space. + * Parameters : spi : struct spi_device * + * + * Returns : retval 0 if ok else -1 on error + ****************************************************************************/ +static int sr100_probe(struct spi_device* spi) { + int ret = -1; + int rc = 0; + struct sr100_spi_platform_data* platform_data = NULL; + struct sr100_spi_platform_data platform_data1; + struct sr100_dev* sr100_dev = NULL; + struct clk *rf_clk2; + unsigned int irq_flags; + + SR100_DBG_MSG("%s chip select : %d , bus number = %d \n", __FUNCTION__, + spi->chip_select, spi->master->bus_num); + + if (spi->dev.of_node && !mmi_device_is_available(spi->dev.of_node)) { + SR100_ERR_MSG("%s : mmi: device not supported\n", __func__); + return -ENODEV; + } else { + SR100_ERR_MSG("%s : supported device found\n", __func__); + } + + ret = sr100_parse_dt(&spi->dev, &platform_data1); + if (ret) { + pr_err("%s - Failed to parse DT\n", __func__); + goto err_exit; + } + platform_data = &platform_data1; + + sr100_dev = kzalloc(sizeof(*sr100_dev), GFP_KERNEL); + if (sr100_dev == NULL) { + SR100_ERR_MSG("failed to allocate memory for module data\n"); + ret = -ENOMEM; + goto err_exit; + } + ret = sr100_hw_setup(platform_data, sr100_dev, spi); + if (ret < 0) { + SR100_ERR_MSG("Failed to sr100_enable_SR100_IRQ_ENABLE\n"); + goto err_exit0; + } + + spi->bits_per_word = 8; + spi->mode = SPI_MODE_0; + spi->max_speed_hz = SR100_SPI_CLOCK; + ret = spi_setup(spi); + if (ret < 0) { + SR100_ERR_MSG("failed to do spi_setup()\n"); + goto err_exit0; + } + + sr100_dev->spi = spi; + sr100_dev->sr100_device.minor = MISC_DYNAMIC_MINOR; + sr100_dev->sr100_device.name = "sr100"; + sr100_dev->sr100_device.fops = &sr100_dev_fops; + sr100_dev->sr100_device.parent = &spi->dev; + sr100_dev->irq_gpio = platform_data->irq_gpio; + sr100_dev->ce_gpio = platform_data->ce_gpio; + sr100_dev->spi_handshake_gpio = platform_data->spi_handshake_gpio; + sr100_dev->rtc_sync_gpio = platform_data->rtc_sync_gpio; + + if (of_get_property(spi->dev.of_node, "clocks", NULL)) { + rf_clk2 = devm_clk_get(&spi->dev,"rf_clk2"); + if(rf_clk2 != NULL) { + rc = clk_prepare_enable(rf_clk2); + if(rc) { + SR100_ERR_MSG("failed to enable clk\n"); + } + } else { + SR100_ERR_MSG("failed to get clk\n"); + } + } + +#if HVH_VDD_ENABLE + sr100_dev->vdd_1v8_gpio = platform_data->vdd_1v8_gpio; + sr100_dev->vdd_1v8_rf_gpio = platform_data->vdd_1v8_rf_gpio; + sr100_dev->vbat_3v6_gpio = platform_data->vbat_3v6_gpio; +#endif + sr100_dev->tx_buffer = kzalloc(SR100_TXBUF_SIZE, GFP_KERNEL); + sr100_dev->rx_buffer = kzalloc(SR100_RXBUF_SIZE, GFP_KERNEL); + if (sr100_dev->tx_buffer == NULL) { + ret = -ENOMEM; + goto exit_free_dev; + } + if (sr100_dev->rx_buffer == NULL) { + ret = -ENOMEM; + goto exit_free_dev; + } + + dev_set_drvdata(&spi->dev, sr100_dev); + + /* init mutex and queues */ + init_waitqueue_head(&sr100_dev->read_wq); + mutex_init(&sr100_dev->sr100_access_lock); + + spin_lock_init(&sr100_dev->irq_enabled_lock); + + ret = misc_register(&sr100_dev->sr100_device); + if (ret < 0) { + SR100_ERR_MSG("misc_register failed! %d\n", ret); + goto err_exit0; + } + + sr100_dev->spi->irq = gpio_to_irq(platform_data->irq_gpio); + + if (sr100_dev->spi->irq < 0) { + SR100_ERR_MSG("gpio_to_irq request failed gpio = 0x%x\n", + platform_data->irq_gpio); + goto err_exit1; + } + /* request irq. the irq is set whenever the chip has data available + * for reading. it is cleared when all data has been read. + */ + //irq_flags = IRQF_TRIGGER_RISING; + irq_flags = IRQ_TYPE_LEVEL_HIGH; + sr100_dev->irq_enabled = true; + sr100_dev->irq_received = false; + + ret = request_irq(sr100_dev->spi->irq, sr100_dev_irq_handler, irq_flags, + sr100_dev->sr100_device.name, sr100_dev); + if (ret) { + SR100_ERR_MSG("request_irq failed\n"); + goto err_exit1; + } + sr100_disable_irq(sr100_dev); +#if HVH_VDD_ENABLE + gpio_set_value(sr100_dev->vdd_1v8_gpio, 1); + gpio_set_value(sr100_dev->vdd_1v8_rf_gpio, 1); + gpio_set_value(sr100_dev->vbat_3v6_gpio, 1); + pr_info(" VDD Req for HVH: %s\n", __func__); +#endif + + SR100_DBG_MSG("Exit : %s\n", __FUNCTION__); + return ret; +err_exit1: +exit_free_dev: + if (sr100_dev != NULL) { + if (sr100_dev->tx_buffer) { + kfree(sr100_dev->tx_buffer); + } + if (sr100_dev->rx_buffer) { + kfree(sr100_dev->rx_buffer); + } + misc_deregister(&sr100_dev->sr100_device); + } +err_exit0: + if (sr100_dev != NULL) kfree(sr100_dev); + if (sr100_dev != NULL) { + mutex_destroy(&sr100_dev->sr100_access_lock); + } +err_exit: + if (sr100_dev != NULL) kfree(sr100_dev); + SR100_DBG_MSG("ERROR: Exit : %s ret %d\n", __FUNCTION__, ret); + return ret; +} + +/****************************************************************************** + * Function : sr100_remove + * + * Description : Will get called when the device is removed to release the + * resources. + * + * Parameters : spi : struct spi_device * + * + * Returns : retval 0 if ok else -1 on error + ****************************************************************************/ +static int sr100_remove(struct spi_device* spi) { + struct sr100_dev* sr100_dev = sr100_get_data(spi); + SR100_DBG_MSG("Entry : %s\n", __FUNCTION__); + gpio_free(sr100_dev->ce_gpio); + mutex_destroy(&sr100_dev->sr100_access_lock); + free_irq(sr100_dev->spi->irq, sr100_dev); + gpio_free(sr100_dev->irq_gpio); + gpio_free(sr100_dev->spi_handshake_gpio); + gpio_free(sr100_dev->rtc_sync_gpio); +#if HVH_VDD_ENABLE + gpio_free(sr100_dev->vdd_1v8_gpio); + gpio_free(sr100_dev->vdd_1v8_rf_gpio); + gpio_free(sr100_dev->vbat_3v6_gpio); +#endif + misc_deregister(&sr100_dev->sr100_device); + if (sr100_dev != NULL) { + if (sr100_dev->tx_buffer != NULL) kfree(sr100_dev->tx_buffer); + if (sr100_dev->rx_buffer != NULL) kfree(sr100_dev->rx_buffer); + if (sr100_dev != NULL) kfree(sr100_dev); + } + SR100_DBG_MSG("Exit : %s\n", __FUNCTION__); + return 0; +} +static struct of_device_id sr100_dt_match[] = {{ + .compatible = "nxp,sr100", + }, + {}}; +static struct spi_driver sr100_driver = { + .driver = + { + .name = "sr100", + // .bus = &spi_bus_type, + .owner = THIS_MODULE, + .of_match_table = sr100_dt_match, + }, + .probe = sr100_probe, + .remove = (sr100_remove), +}; + +/****************************************************************************** + * Function : sr100_dev_init + * + * Description : Module init interface + * + * Parameters :void + * + * Returns : returns handle + ****************************************************************************/ +static int __init sr100_dev_init(void) { + debug_level = SR100_FULL_DEBUG; + + SR100_DBG_MSG("Entry : %s\n", __FUNCTION__); + + return spi_register_driver(&sr100_driver); + + SR100_DBG_MSG("Exit : %s\n", __FUNCTION__); +} +module_init(sr100_dev_init); + +/****************************************************************************** + * Function : sr100_dev_exit + * + * Description : Module Exit interface + * + * Parameters :void + * + * Returns : returns void + ****************************************************************************/ +static void __exit sr100_dev_exit(void) { + SR100_DBG_MSG("Entry : %s\n", __FUNCTION__); + + spi_unregister_driver(&sr100_driver); + SR100_DBG_MSG("Exit : %s\n", __FUNCTION__); +} +module_exit(sr100_dev_exit); + +MODULE_AUTHOR("Manjunatha Venkatesh"); +MODULE_DESCRIPTION("NXP SR100 SPI driver"); +MODULE_LICENSE("GPL"); + +#ifdef SOFTDEP_GPIO_PCAL6408 +MODULE_SOFTDEP("pre: gpio-pcal6408"); +#endif + +/** @} */ diff --git a/drivers/watchdog_cpu_ctx/Android.mk b/drivers/watchdog_cpu_ctx/Android.mk new file mode 100644 index 000000000000..3ccc43528d7e --- /dev/null +++ b/drivers/watchdog_cpu_ctx/Android.mk @@ -0,0 +1,13 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := watchdog_cpu_ctx.ko +LOCAL_MODULE_TAGS := optional +ifeq ($(DLKM_INSTALL_TO_VENDOR_OUT),true) +LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/lib/modules/ +else +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +endif +LOCAL_ADDITIONAL_DEPENDENCIES := $(KERNEL_MODULES_OUT)/mmi_info.ko +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/watchdog_cpu_ctx/Kbuild b/drivers/watchdog_cpu_ctx/Kbuild new file mode 100644 index 000000000000..7772a58e5a88 --- /dev/null +++ b/drivers/watchdog_cpu_ctx/Kbuild @@ -0,0 +1,9 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += watchdog_cpu_ctx.o + +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_annotate/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(CURDIR)/$(KBUILD_EXTMOD)/../mmi_info/Module.symvers +KBUILD_EXTRA_SYMBOLS += $(KBUILD_OUTPUT)/Module.symver diff --git a/drivers/watchdog_cpu_ctx/Makefile b/drivers/watchdog_cpu_ctx/Makefile new file mode 100644 index 000000000000..34b09dd815a7 --- /dev/null +++ b/drivers/watchdog_cpu_ctx/Makefile @@ -0,0 +1,10 @@ +KERNEL_SRC ?= /lib/modules/$(shell uname -r)/build + +all: + $(MAKE) -C $(KERNEL_SRC) M=$(shell pwd) modules $(KBUILD_OPTIONS) + +modules_install: + $(MAKE) INSTALL_MOD_STRIP=1 -C $(KERNEL_SRC) M=$(shell pwd) modules_install + +clean: + $(MAKE) -C $(KERNEL_SRC) M=$(PWD) clean \ No newline at end of file diff --git a/drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.c b/drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.c new file mode 100644 index 000000000000..0db33a27cfb3 --- /dev/null +++ b/drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.c @@ -0,0 +1,901 @@ +/* + * Copyright (C) 2018 Motorola Mobility LLC + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "watchdog_cpu_ctx.h" + +#ifndef TASK_STATE_TO_CHAR_STR +#define TASK_STATE_TO_CHAR_STR "RSDTtXZxKWPNn" +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0) + #define VA_START (UL(0xffffffffffffffff) -\ + (UL(1)<va_bits = VA_BITS; + lnx->pa_bits = aa64_pa_max(); + lnx->kaslr_offset = get_kaslr_offset(); +} + +#elif defined(CONFIG_ARM) + +struct wdog_frame_struct { + unsigned long addr; + unsigned long stack; +}; + +#if defined(CONFIG_ARM_LPAE) +static int armv7_pa_max(void) +{ + int cached_mem_size = (read_cpuid_ext(CPUID_EXT_MMFR3) >> 24) & 0xf; + + switch (cached_mem_size) { + case 0x0: + return 32; + case 0x1: + return 36; + case 0x2: + return 40; + default: + pr_err("PAMax: Unknown mem size: %#x\n", cached_mem_size); + break; + } + return 32; +} +#else +static int armv7_pa_max(void) +{ + int supersection = (read_cpuid_ext(CPUID_EXT_MMFR3) >> 28) & 0xf; + + if ((supersection == 0x0) && IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT)) + return 40; + else + return 32; +} +#endif /* CONFIG_ARM_LPAE */ + +static void msm_wdog_ctx_lnx_arch(struct msm_wdog_lnx_info *lnx) +{ + lnx->va_bits = 32; + lnx->pa_bits = armv7_pa_max(); + lnx->aa32_pxn = ((read_cpuid_ext(CPUID_EXT_MMFR0) & 0xf) >= 0x4); + lnx->kaslr_offset = 0; +} +#endif +static inline uint64_t system_read_TTBR1_EL1(void) +{ + uint64_t val; + asm volatile("mrs %0, ttbr1_el1": "=r"(val)); + return (val & 0xffffffffffc0) | ((val & 0x3c) << 46); +} +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0) +static void __nocfi msm_wdog_ctx_lnx(struct msm_wdog_lnx_info *lnx) +#else +static void msm_wdog_ctx_lnx(struct msm_wdog_lnx_info *lnx) +#endif +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0) + unsigned long init_mm_addr = 0; +#else + /* Since this is a module we cannot link to these symbols + * directly, so use kallsyms. kallsyms is needed to find the + * names of the stacktrace symbols anyways. + */ + unsigned long _text_addr = kallsyms_lookup_name("_text"); + /* This is a way to get the address of swapper_pg_dir which + * is not available via kallsyms_lookup_name directly + */ + unsigned long init_mm_addr = kallsyms_lookup_name("init_mm"); +#endif + struct mm_struct *init_mm = NULL; + if(init_mm_addr) { + init_mm = (struct mm_struct *)init_mm_addr; + } + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,9,0) || IS_ENABLED(CONFIG_ARM) + lnx->ti_tsk_offset = offsetof(struct thread_info, task); +#else + lnx->ti_tsk_offset = 0; +#endif + lnx->tsk_size = sizeof(struct task_struct); + lnx->aa64 = IS_ENABLED(CONFIG_ARM64); + lnx->lpae = IS_ENABLED(CONFIG_ARM_LPAE); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0) + lnx->pgd_paddr = system_read_TTBR1_EL1(); + lnx->text_paddr = 0; +#else + lnx->text_paddr = _text_addr ? virt_to_phys((void *)_text_addr) : 0; + lnx->pgd_paddr = init_mm ? virt_to_phys(init_mm->pgd) : 0; +#endif + lnx->page_shift = PAGE_SHIFT; + lnx->thread_size = THREAD_SIZE; + msm_wdog_ctx_lnx_arch(lnx); +} + +static void msm_wdog_ctx_header_init(struct msm_wdog_cpuctx *ctxi) +{ + struct msm_wdog_cpuctx_header *header = &ctxi->header[0]; + + memset_io(header, 0, sizeof(*header)); + memcpy(header, mwc_header, sizeof(mwc_header)); +} + +static void msm_wdog_ctx_reset(struct msm_wdog_cpuctx *ctx, size_t ctx_size) +{ + struct msm_wdog_cpuctx *ctxi; + int cpu; + + memset_io(ctx, 0, ctx_size); + for_each_cpu(cpu, cpu_present_mask) { + ctxi = &ctx[cpu]; + ctxi->info.sig = MSM_WDOG_CTX_SIG; + ctxi->info.rev = MSM_WDOG_CTX_REV; + ctxi->info.size = WDOG_CPUCTX_SIZE_PERCPU; +#if IS_ENABLED(CONFIG_KALLSYMS) + ctxi->info.syms_avail = 1; +#else + ctxi->info.syms_avail = 0; +#endif + msm_wdog_ctx_header_init(ctxi); + msm_wdog_ctx_lnx(&ctxi->lnx); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0) + if(!ctxi->lnx.pgd_paddr || + (IS_ENABLED(CONFIG_ARM64) && !ctxi->lnx.kaslr_offset)) + ctxi->info.syms_avail = 0; +#else + if(!ctxi->lnx.text_paddr || + !ctxi->lnx.pgd_paddr || + (IS_ENABLED(CONFIG_ARM64) && !ctxi->lnx.kaslr_offset)) + ctxi->info.syms_avail = 0; +#endif + } +} + +#if defined(CONFIG_ARM64) +static inline int virt_is_valid(unsigned long addr) +{ + return (addr >= VA_START); +} +#else +static inline int virt_is_valid(unsigned long addr) +{ + return virt_addr_valid(addr); +} +#endif + +static int msm_wdog_cpu_regs_version_unknown(uint32_t version) +{ + if (version != CPU_FORMAT_VERSION4) + return 1; + return 0; +} + +static int msm_wdog_ctx_header_check(struct msm_wdog_cpuctx *ctxi) +{ + struct msm_wdog_cpuctx_header *header = &ctxi->header[0]; + return memcmp(header, mwc_header, sizeof(mwc_header)); +} + +static void msm_wdt_show_raw_mem(unsigned long addr, int nbytes, + unsigned long old_addr, const char *label) +{ + int i, j; + int nlines; + unsigned long *p; + + MSMWDTD("%s: %#lx: ", label, old_addr); + if (!virt_is_valid(old_addr)) { + MSMWDTD("is not valid kernel address.\n"); + return; + } + MSMWDTD("\n"); + + /* + * round address down to unsigned long aligned boundary + * and always dump a multiple of 32 bytes + */ + p = (unsigned long *)(addr & ~(sizeof(unsigned long) - 1)); + nbytes += (addr & (sizeof(unsigned long) - 1)); + nlines = (nbytes + 31) / 32; + + for (i = 0; i < nlines; i++) { + /* + * just display low 16 bits of address to keep + * each line of the dump < 80 characters + */ + MSMWDTD("%04lx ", (unsigned long)old_addr & 0xffff); + for (j = 0; j < (32 / sizeof(unsigned long)); j++) { + if (sizeof(unsigned long) == 4) + MSMWDTD(" %08lx", *p); + else + MSMWDTD(" %016lx", *p); + ++p; + old_addr += sizeof(*p); + } + MSMWDTD("\n"); + } +} + +static void msm_wdog_show_sc_status(uint32_t sc_status) +{ + if (sc_status & SC_STATUS_DBI) + MSMWDTD("SDI: Secure watchdog bite. "); + if (sc_status & SC_STATUS_CTX_BY_TZ) + MSMWDTD("TZ: Non-secure watchdog bite. "); + MSMWDTD("%sS ", (sc_status & SC_STATUS_NS) ? "N" : " "); + if (sc_status & SC_STATUS_WDT) + MSMWDTD("WDT "); + if (sc_status & SC_STATUS_SGI) + MSMWDTD("SGI "); + if (sc_status & SC_STATUS_WARM_BOOT) + MSMWDTD("WARM_BOOT "); + MSMWDTD("\n"); +} + +static void msm_wdt_show_thread_saved_pc(struct task_struct *p, + struct thread_info *ti); +static void msm_wdt_show_task(struct task_struct *p, struct thread_info *ti) +{ + unsigned state; + const char *stat_nam = TASK_STATE_TO_CHAR_STR; + + state = p->state ? __ffs(p->state) + 1 : 0; + MSMWDTD("%-15.15s %c", p->comm, + state < strlen(stat_nam) - 1 ? stat_nam[state] : '?'); + if (state == TASK_RUNNING) + MSMWDTD(" running "); + else + msm_wdt_show_thread_saved_pc(p, ti); + MSMWDTD("pid %6d tgid %6d 0x%08lx\n", task_pid_nr(p), task_tgid_nr(p), + (unsigned long)ti->flags); +} + +#if defined(CONFIG_ARM64) +static void msm_wdt_show_thread_saved_pc(struct task_struct *p, + struct thread_info *ti) +{ + MSMWDTD(" %016lx ", (unsigned long)p->thread.cpu_context.pc); +} + +static int msm_wdt_unwind_frame_aa64(struct stackframe *frame, + unsigned long stack, + unsigned long *sp) +{ + unsigned long high, low; + unsigned long fp = frame->fp; + + low = *sp; + high = ALIGN(low, THREAD_SIZE) - 0x20; + + if (fp < low || fp > high || fp & 0xf) + return -EINVAL; + + *sp = (unsigned long)(fp + 0x10); + frame->fp = (*(unsigned long *)(fp) & (THREAD_SIZE - 1)) + stack; + frame->pc = *(unsigned long *)(fp + 8); + + return 0; +} + +static void msm_wdt_show_regs(struct sysdbgCPUCtxtType *sysdbg_ctx) +{ + struct sysdbg_cpu64_ctxt_regs *regs = &sysdbg_ctx->cpu_regs.cpu64_ctxt; + uint64_t *gp_reg; + int i; + + MSMWDTD("PC is at %016llx\n", regs->pc); + MSMWDTD("sp : %016llx ", regs->sp_el1); + MSMWDTD("x30: %016llx\n", regs->x30); + gp_reg = (uint64_t *)regs; + for (i = 29; i >= 0; i--) { + MSMWDTD("x%-2d: %016llx ", i, gp_reg[i]); + if (i % 2 == 0) + MSMWDTD("\n"); + } + MSMWDTD("currentEL : %016llx\n", regs->currentEL); + MSMWDTD("EL3: elr : %016llx spsr : %016llx sp : %016llx\n", + regs->elr_el3, regs->spsr_el3, regs->sp_el3); + MSMWDTD("EL2: elr : %016llx spsr : %016llx sp : %016llx\n", + regs->elr_el2, regs->spsr_el2, regs->sp_el2); + MSMWDTD("EL1: elr : %016llx spsr : %016llx sp : %016llx\n", + regs->elr_el1, regs->spsr_el1, regs->sp_el1); + MSMWDTD("EL0: sp : %016llx\n", regs->sp_el0); +} + +static void msm_wdt_unwind(struct sysdbgCPUCtxtType *sysdbg_ctx, + unsigned long addr, unsigned long kaslr_offset, + unsigned long stack) +{ + struct stackframe frame; + unsigned long sp; + int offset; + char sym_buf[KSYM_NAME_LEN]; + struct sysdbg_cpu64_ctxt_regs *regs = &sysdbg_ctx->cpu_regs.cpu64_ctxt; + unsigned long cur_kaslr_offset = get_kaslr_offset(); + + if (!virt_is_valid(addr)) { + MSMWDTD("%016lx is not valid kernel address.\n", addr); + return; + } + + if ((regs->sp_el1 & ~(THREAD_SIZE - 1)) == addr) { + frame.fp = (regs->x29 & (THREAD_SIZE - 1)) + stack; + sp = (regs->sp_el1 & (THREAD_SIZE - 1)) + stack; + frame.pc = regs->pc; + } + else { + MSMWDTD("Unexpected stack address\n"); + return; + } + + msm_wdt_show_raw_mem(stack, 96, addr, "thread_info"); + offset = (sp - stack - 128) & ~(128 - 1); + if (offset < 0) { + MSMWDT_ERR("Unexpected offset: %d, sp_el1: 0x%llx\n", offset, regs->sp_el1); + return; + } + msm_wdt_show_raw_mem(stack + offset, THREAD_SIZE - offset, + addr + offset, "stack"); + + sprint_symbol(sym_buf, + regs->pc - kaslr_offset + cur_kaslr_offset); + MSMWDTD("PC: %s <%016llx>\n", sym_buf, regs->pc); + sprint_symbol(sym_buf, + regs->x30 - kaslr_offset + cur_kaslr_offset); + MSMWDTD("LR: %s <%016llx>\n", sym_buf, regs->x30); + + while (1) { + int urc; + unsigned long where = frame.pc; + + /* Symbols are located at a random offset...subtract + * the old one and add the current one + */ + sprint_symbol(sym_buf, + frame.pc - kaslr_offset + cur_kaslr_offset); + MSMWDTD("[<%016lx>] %s\n", where, sym_buf); + + urc = msm_wdt_unwind_frame_aa64(&frame, stack, &sp); + if (urc < 0) + break; + } +} + +#elif defined(CONFIG_ARM) + +static int __in_irqentry_text(unsigned long ptr) +{ + unsigned long _irqentry_text_start_addr = kallsyms_lookup_name("__irqentry_text_start"); + unsigned long __irqentry_text_end_addr = kallsyms_lookup_name("__irqentry_text_end"); + return ptr >= (unsigned long)&_irqentry_text_start_addr && + ptr < (unsigned long)&__irqentry_text_end_addr; +} + +static int in_exception_text(unsigned long ptr) +{ + unsigned long _exception_text_start_addr = kallsyms_lookup_name("__exception_text_start"); + unsigned long _exception_text_end_addr = kallsyms_lookup_name("__exception_text_end"); + int in; + in = ptr >= (unsigned long)&_exception_text_start_addr && + ptr < (unsigned long)&_exception_text_end_addr; + + return in ? : __in_irqentry_text(ptr); +} + +static int msm_wdt_stackframe(struct stackframe *frame, void *data) +{ + unsigned long where = frame->pc; + struct wdog_frame_struct * frame_data = (struct wdog_frame_struct *)data; + + MSMWDTD("[<%08lx>] (%pS) from [<%08lx>] (%pS)\n", where, + (void *)where, frame->pc, (void *)frame->pc); + if (in_exception_text(where)) { + struct pt_regs *excep_regs = + (struct pt_regs *)(frame->sp); + if ((excep_regs->ARM_sp & ~(THREAD_SIZE - 1)) == frame_data->addr) { + excep_regs->ARM_sp -= frame_data->addr; + excep_regs->ARM_sp += frame_data->stack; + } + } + return 0; +} + +static void msm_wdt_show_thread_saved_pc(struct task_struct *p, + struct thread_info *ti) +{ + MSMWDTD(" %08lx ", (unsigned long)ti->cpu_context.pc); +} + +static void msm_wdt_show_regs(struct sysdbgCPUCtxtType *sysdbg_ctx) +{ + struct sysdbg_cpu32_ctxt_regs *regs = &sysdbg_ctx->cpu_regs.cpu32_ctxt; + unsigned long _stext_addr = kallsyms_lookup_name("_stext"); + unsigned long _etext_addr = kallsyms_lookup_name("_etext"); + + if ((regs->pc >= _stext_addr) && + (regs->pc <= _etext_addr)) + MSMWDTD("PC is at %pS <%08x>\n", + (void *)(unsigned long)regs->pc, (uint32_t)regs->pc); + else + MSMWDTD("PC is at %08x\n", (uint32_t)regs->pc); + MSMWDTD("\tr12: %08x r11: %08x r10: %08x r9 : %08x r8 : %08x\n", + regs->r12, regs->r11, regs->r10, regs->r9, regs->r8); + MSMWDTD("\tr7 : %08x r6 : %08x r5 : %08x r4 : %08x\n", + regs->r7, regs->r6, regs->r5, regs->r4); + MSMWDTD("\tr3 : %08x r2 : %08x r1 : %08x r0 : %08x\n", + regs->r3, regs->r2, regs->r1, regs->r0); + MSMWDTD("USR:\tr13: %08x r14: %08x\n", regs->r13_usr, regs->r14_usr); + MSMWDTD("HYP:\tr13: %08x r14: %08x\n", regs->r13_hyp, regs->r14_hyp); + MSMWDTD("IRQ:\tr13: %08x r14: %08x\n", regs->r13_irq, regs->r14_irq); + MSMWDTD("SVC:\tr13: %08x r14: %08x\n", regs->r13_svc, regs->r14_svc); + MSMWDTD("ABT:\tr13: %08x r14: %08x\n", regs->r13_abt, regs->r14_abt); + MSMWDTD("UND:\tr13: %08x r14: %08x\n", regs->r13_und, regs->r14_und); + MSMWDTD("MON:\tr13: %08x r14: %08x\n", regs->r13_mon, regs->r14_mon); + MSMWDTD("FIQ:\tr13: %08x r14: %08x\n", regs->r13_fiq, regs->r14_fiq); + MSMWDTD("\tr12: %08x r11: %08x r10: %08x r9 : %08x r8 : %08x\n", + regs->r12_fiq, regs->r11_fiq, regs->r10_fiq, + regs->r9_fiq, regs->r8_fiq); +} + +static void msm_wdt_unwind(struct sysdbgCPUCtxtType *sysdbg_ctx, + unsigned long addr, unsigned long kaslr_offset, + unsigned long stack) +{ + struct stackframe frame; + int offset; + struct wdog_frame_struct frame_data; + struct sysdbg_cpu32_ctxt_regs *regs = &sysdbg_ctx->cpu_regs.cpu32_ctxt; + + if (!virt_is_valid(addr)) { + MSMWDTD("%08lx is not valid kernel address.\n", addr); + return; + } + + frame_data.addr = addr; + frame_data.stack = stack; + + if ((regs->r13_svc & ~(THREAD_SIZE - 1)) == addr) { + frame.fp = (regs->r11 & (THREAD_SIZE - 1)) + stack; + frame.sp = (regs->r13_svc & (THREAD_SIZE - 1)) + stack; + frame.lr = regs->r14_svc; + frame.pc = regs->pc; + } else { + struct thread_info *ti = (struct thread_info *)stack; + + frame.fp = ti->cpu_context.fp - addr + stack; + frame.sp = ti->cpu_context.sp - addr + stack; + frame.lr = 0; + frame.pc = ti->cpu_context.pc; + } + msm_wdt_show_raw_mem(stack, 96, addr, "thread_info"); + offset = (frame.sp - stack - 128) & ~(128 - 1); + if (offset < 0) { + MSMWDT_ERR("Unexpected offset: %d, r13_svc: 0x%x\n", offset, regs->r13_svc); + return; + } + msm_wdt_show_raw_mem(stack + offset, THREAD_SIZE - offset, + addr + offset, "stack"); + walk_stackframe(&frame, &msm_wdt_stackframe, &frame_data); +} +#endif + +static void msm_wdog_ctx_print(struct msm_wdog_cpuctx *ctx, + phys_addr_t paddr, size_t ctx_size) +{ + struct msm_wdog_cpuctx *ctxi; + struct msm_dump_data *cpu_data; + struct msm_wdog_cpuctx_info *info; + struct msm_wdog_cpuctx_stat *stat; + cpumask_t cpus, cpus_nodump, cpus_regs, cpus_dd; + unsigned long stack_tmp = 0; + int cpu; + + cpumask_clear(&cpus); + for_each_cpu(cpu, cpu_present_mask) { + ctxi = &ctx[cpu]; + cpu_data = &ctxi->cpu_data; + if (msm_wdog_ctx_header_check(ctxi)) { + MSMWDTD_IFWDOG("CPU%d: ctx header invalid\n", cpu); + continue; + } + + info = &ctxi->info; + if ((info->sig != MSM_WDOG_CTX_SIG) || + (info->rev2 != MSM_WDOG_CTX_REV) || + (info->rev != MSM_WDOG_CTX_REV) || + (info->size != WDOG_CPUCTX_SIZE_PERCPU) || + (info->ret != ERR_NONE)) { + MSMWDTD_IFWDOG("CPU%d: sig %x rev %x/%x sz %x ret %x\n", + cpu, info->sig, (unsigned)info->rev, + info->rev2, info->size, info->ret); + continue; + } + cpumask_set_cpu(cpu, &cpus); + } + + if (cpumask_empty(&cpus)) + return; + + cpumask_clear(&cpus_nodump); + cpumask_clear(&cpus_regs); + cpumask_clear(&cpus_dd); + + for_each_cpu(cpu, &cpus) { + uint32_t *status; + ctxi = &ctx[cpu]; + cpu_data = &ctxi->cpu_data; + stat = &ctxi->stat; + + if (!cpu_data->magic && !cpu_data->version && + !ctxi->sysdbg.data.status[0]) { + MSMWDTD_IFWDOG("CPU%d: No Dump!\n", cpu); + cpumask_set_cpu(cpu, &cpus_nodump); + continue; + } + if (cpu_data->magic != DUMP_MAGIC_NUMBER) { + MSMWDTD_IFWDOG("CPU%d: dump magic mismatch %x/%x\n", + cpu, cpu_data->magic, DUMP_MAGIC_NUMBER); + continue; + } + if (msm_wdog_cpu_regs_version_unknown(cpu_data->version)) { + MSMWDTD_IFWDOG("CPU%d: unknown version %d\n", + cpu, cpu_data->version); + continue; + } + cpumask_set_cpu(cpu, &cpus_regs); + status = &ctxi->sysdbg.data.status[0]; + MSMWDTD("CPU%d: %x %x ", cpu, status[0], status[1]); + msm_wdog_show_sc_status(status[1]); + if (stat->ret == ERR_NONE || stat->ret == ERR_TASK_INVAL) + cpumask_set_cpu(cpu, &cpus_dd); + } + + if (cpumask_equal(&cpus_nodump, cpu_present_mask)) { + MSMWDTD_IFWDOG("Might be Secure Watchdog Bite!\n"); + return; + } + if (cpumask_empty(&cpus_regs)) + return; + MSMWDTD("\n"); + for_each_cpu(cpu, &cpus_regs) { + struct msm_wdog_copy *job; + + ctxi = &ctx[cpu]; + stat = &ctxi->stat; + MSMWDTD("CPU%d: ret %x", cpu, stat->ret); + if (stat->stack_va) { + MSMWDTD(" stack %lx ", (unsigned long)stat->stack_va); + job = &stat->jobs[LNX_STACK]; + MSMWDTD("%lx -> %lx (%lx) ", (unsigned long)job->from, + (unsigned long)job->to, + (unsigned long)job->size); + job = &stat->jobs[LNX_TASK]; + MSMWDTD("%lx -> %lx (%lx)", (unsigned long)job->from, + (unsigned long)job->to, + (unsigned long)job->size); + } + MSMWDTD("\n"); + } + for_each_cpu(cpu, &cpus_regs) { + ctxi = &ctx[cpu]; + MSMWDTD("\nCPU%d\n", cpu); + msm_wdt_show_regs(&ctxi->sysdbg.data); + } + for_each_cpu(cpu, &cpus_dd) { + unsigned long data; + + ctxi = &ctx[cpu]; + stat = &ctxi->stat; + if (!IS_ALIGNED((unsigned long)ctxi->stack, THREAD_SIZE) + && !stack_tmp) { + stack_tmp = __get_free_pages(GFP_KERNEL, + THREAD_SIZE_ORDER); + if (!stack_tmp) + MSMWDT_ERR("Alloc temp stack failed.\n"); + } + if (stack_tmp) { + memcpy_fromio((void *)stack_tmp, ctxi->stack, + THREAD_SIZE); + data = stack_tmp; + } else { + data = (unsigned long)ctxi->stack; + } + + MSMWDTD("\nCPU%d\n", cpu); + if(stat->ret != ERR_TASK_INVAL) { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,9,0) || !IS_ENABLED(CONFIG_THREAD_INFO_IN_TASK) + msm_wdt_show_task(&ctxi->task, + (struct thread_info *)ctxi->stack); +#else + msm_wdt_show_task(&ctxi->task, + &ctxi->task.thread_info); +#endif + } + msm_wdt_unwind(&ctxi->sysdbg.data, ctxi->stat.stack_va, + ctx->lnx.kaslr_offset, data); + } + MSMWDTD("\n"); + if (stack_tmp) + free_pages(stack_tmp, THREAD_SIZE_ORDER); +} + +static void watchdog_cpu_ctx_table_register(struct device *dev, + struct platform_data *pdata) +{ + int err = 0; + struct msm_dump_entry dump_entry; + struct msm_dump_data *ctx_dump_data; + + ctx_dump_data = kzalloc(sizeof(struct msm_dump_data), + GFP_KERNEL); + + if (!ctx_dump_data) { + dev_err(dev, "Cannot alloc dump data structure.\n"); + goto err; + } + + ctx_dump_data->addr = pdata->mem_address; + ctx_dump_data->len = pdata->mem_size; + dump_entry.id = MSM_DUMP_DATA_WDOG_CPU_CTX; + dump_entry.addr = virt_to_phys(ctx_dump_data); + err = msm_dump_data_register(MSM_DUMP_TABLE_APPS, &dump_entry); + if (err) { + dev_err(dev, "Registering dump data failed.\n"); + kfree(ctx_dump_data); + } +err: + return; +} + +static int watchdog_cpu_ctx_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct platform_data *pdata; + struct resource res; + struct device_node *node; + struct msm_wdog_cpuctx *ctx_vaddr; + int err = 0; + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + err = -ENOMEM; + goto err; + } + + /* Get reserved memory region from Device-tree */ + node = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!node) { + dev_err(dev, "No %s specified\n", "memory-region"); + goto err; + } + + err = of_address_to_resource(node, 0, &res); + of_node_put(node); + if (err) { + dev_err(dev, "No memory address assigned to the region\n"); + goto err; + } + + pdata->mem_size = resource_size(&res); + pdata->mem_address = res.start; + + dev_info(dev, "size %zx", pdata->mem_size); + dev_info(dev, "addr %lx", (unsigned long)pdata->mem_address); + dev_info(dev, "size per cpu is %lx", (unsigned long)WDOG_CPUCTX_SIZE_PERCPU); + + if (pdata->mem_size < WDOG_CPUCTX_SIZE) { + dev_err(dev, "Mem size too small %zx/%lu\n", + pdata->mem_size, (unsigned long)WDOG_CPUCTX_SIZE); + err = -ENOMEM; + goto err; + } + + watchdog_cpu_ctx_table_register(dev, pdata); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,4,0) + ctx_vaddr = ioremap_wc(pdata->mem_address, pdata->mem_size); +#else + ctx_vaddr = dma_remap(dev, NULL, pdata->mem_address, + pdata->mem_size, 0); +#endif + if (!ctx_vaddr) { + dev_err(dev, "Cannot remap buffer %pa size %zx\n", + &pdata->mem_address, pdata->mem_size); + err = -ENOMEM; + goto err; + } + + msm_wdog_ctx_print(ctx_vaddr, pdata->mem_address, pdata->mem_size); + msm_wdog_ctx_reset(ctx_vaddr, pdata->mem_size); + +err: + return err; +} + +static int watchdog_cpu_ctx_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id watchdog_cpu_ctx_match[] = { + { .compatible = "mmi,watchdog_cpu_ctx" }, + {} +}; + +static struct platform_driver watchdog_cpu_ctx_driver = { + .probe = watchdog_cpu_ctx_probe, + .remove = watchdog_cpu_ctx_remove, + .driver = { + .name = "watchdog_cpu_ctx", + .of_match_table = watchdog_cpu_ctx_match, + }, +}; + +static int watchdog_cpu_ctx_init(void) +{ + return platform_driver_register(&watchdog_cpu_ctx_driver); +} + +static void watchdog_cpu_ctx_exit(void) +{ + platform_driver_unregister(&watchdog_cpu_ctx_driver); +} + +module_init(watchdog_cpu_ctx_init); +module_exit(watchdog_cpu_ctx_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Watchdog cpu ctx"); diff --git a/drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.h b/drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.h new file mode 100644 index 000000000000..e50e5bfb4b59 --- /dev/null +++ b/drivers/watchdog_cpu_ctx/watchdog_cpu_ctx.h @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2018 Motorola Mobility LLC + * + * 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. + * + */ +#ifndef __WATCHDOG_CPU_CTX_H +#define __WATCHDOG_CPU_CTX_H + +#include +#include + +#define MAX_CPU_CTX_SIZE 2048 +#define CPU_FORMAT_VERSION4 0x14 + +#define DUMP_MAGIC_NUMBER 0x42445953 +#define CPUCTX_LNX_INFO_MAGIC 0x4c4e5831 +#define CPUCTX_STAT_MAGIC 0x4c4e5832 +#define CPUCTX_DUMP_DATA_MAGIC 0x4c4e5833 +#define CPUCTX_TASK_STRUCT_MAGIC 0x4c4e5834 +#define CPUCTX_THREAD_MAGIC 0x4c4e5835 + +#define MSM_WDOG_CTX_SIG 0x77647473 +#define MSM_WDOG_CTX_REV 0x00020000 + +#define RET_STAGE_SHIFT (28) +#define RET_SECTION_SHIFT (24) +#define RET_ERR_SHIFT (16) +#define RET_MISC_SHIFT (12) + +#define RET_TZ (0xfu << RET_STAGE_SHIFT) +#define ERR_NONE (RET_TZ | (0xffu << RET_ERR_SHIFT)) +#define ERR_NO_SYMS (RET_TZ | (0xe9u << RET_ERR_SHIFT)) +#define ERR_TASK_INVAL (RET_TZ | (0xeau << RET_ERR_SHIFT)) + +#define SC_STATUS_NS 0x01 +#define SC_STATUS_WDT 0x02 +#define SC_STATUS_SGI 0x04 +#define SC_STATUS_WARM_BOOT 0x08 +#define SC_STATUS_DBI 0x10 +#define SC_STATUS_CTX_BY_TZ 0x20 + +#ifndef THREAD_SIZE_ORDER +#define THREAD_SIZE_ORDER (get_order(THREAD_SIZE)) +#endif + +enum lnx_jobs { + LNX_STACK, + LNX_TASK, + LNX_CTX_AREAS, +}; + +enum cpuctx_type { + CPUCTX_LNX_INFO, + CPUCTX_STAT, + CPUCTX_DUMP_DATA, + CPUCTX_TASK_STRUCT, + CPUCTX_THREAD, + CPUCTX_TYPES, + CPUCTX_MAX = 20, +}; + +struct msm_wdog_copy { + uint64_t from; + uint64_t to; + uint64_t size; +} __packed __aligned(4); + +struct sysdbg_cpu64_ctxt_regs { + uint64_t x0; + uint64_t x1; + uint64_t x2; + uint64_t x3; + uint64_t x4; + uint64_t x5; + uint64_t x6; + uint64_t x7; + uint64_t x8; + uint64_t x9; + uint64_t x10; + uint64_t x11; + uint64_t x12; + uint64_t x13; + uint64_t x14; + uint64_t x15; + uint64_t x16; + uint64_t x17; + uint64_t x18; + uint64_t x19; + uint64_t x20; + uint64_t x21; + uint64_t x22; + uint64_t x23; + uint64_t x24; + uint64_t x25; + uint64_t x26; + uint64_t x27; + uint64_t x28; + uint64_t x29; + uint64_t x30; + uint64_t pc; + uint64_t currentEL; + uint64_t sp_el3; + uint64_t elr_el3; + uint64_t spsr_el3; + uint64_t sp_el2; + uint64_t elr_el2; + uint64_t spsr_el2; + uint64_t sp_el1; + uint64_t elr_el1; + uint64_t spsr_el1; + uint64_t sp_el0; + uint64_t __reserved1; + uint64_t __reserved2; + uint64_t __reserved3; + uint64_t __reserved4; +}; + +struct sysdbg_cpu32_ctxt_regs { + union { + uint32_t r0; + uint64_t x0; + }; + union { + uint32_t r1; + uint64_t x1; + }; + union { + uint32_t r2; + uint64_t x2; + }; + union { + uint32_t r3; + uint64_t x3; + }; + union { + uint32_t r4; + uint64_t x4; + }; + union { + uint32_t r5; + uint64_t x5; + }; + union { + uint32_t r6; + uint64_t x6; + }; + union { + uint32_t r7; + uint64_t x7; + }; + union { + uint32_t r8; + uint64_t x8; + }; + union { + uint32_t r9; + uint64_t x9; + }; + union { + uint32_t r10; + uint64_t x10; + }; + union { + uint32_t r11; + uint64_t x11; + }; + union { + uint32_t r12; + uint64_t x12; + }; + union { + uint32_t r13_usr; + uint64_t x13; + }; + union { + uint32_t r14_usr; + uint64_t x14; + }; + union { + uint32_t r13_hyp; + uint64_t x15; + }; + union { + uint32_t r14_irq; + uint64_t x16; + }; + union { + uint32_t r13_irq; + uint64_t x17; + }; + union { + uint32_t r14_svc; + uint64_t x18; + }; + union { + uint32_t r13_svc; + uint64_t x19; + }; + union { + uint32_t r14_abt; + uint64_t x20; + }; + union { + uint32_t r13_abt; + uint64_t x21; + }; + union { + uint32_t r14_und; + uint64_t x22; + }; + union { + uint32_t r13_und; + uint64_t x23; + }; + union { + uint32_t r8_fiq; + uint64_t x24; + }; + union { + uint32_t r9_fiq; + uint64_t x25; + }; + union { + uint32_t r10_fiq; + uint64_t x26; + }; + union { + uint32_t r11_fiq; + uint64_t x27; + }; + union { + uint32_t r12_fiq; + uint64_t x28; + }; + union { + uint32_t r13_fiq; + uint64_t x29; + }; + union { + uint32_t r14_fiq; + uint64_t x30; + }; + union { + uint32_t pc; + uint64_t x31; + }; + union { + uint32_t r13_mon; + uint64_t x32; + }; + union { + uint32_t r14_mon; + uint64_t x33; + }; + union { + uint32_t r14_hyp; + uint64_t x34; + }; + union { + uint32_t _reserved; + uint64_t x35; + }; + union { + uint32_t __reserved1; + uint64_t x36; + }; + union { + uint32_t __reserved2; + uint64_t x37; + }; + union { + uint32_t __reserved3; + uint64_t x38; + }; + union { + uint32_t __reserved4; + uint64_t x39; + }; +}; + + +union sysdbg_cpu_ctxt_regs_type { + struct sysdbg_cpu32_ctxt_regs cpu32_ctxt; + struct sysdbg_cpu64_ctxt_regs cpu64_ctxt; +}; + +struct sysdbgCPUCtxtType { + uint32_t status[4]; + union sysdbg_cpu_ctxt_regs_type cpu_regs; + union sysdbg_cpu_ctxt_regs_type __reserved3; /* Secure - Not used */ +}; + +union sysdbg_cpuctx { + struct sysdbgCPUCtxtType data; + uint8_t space[MAX_CPU_CTX_SIZE]; +}; + +struct msm_wdog_cpuctx_info { + uint32_t sig; + uint32_t rev; + uint32_t ret; + uint32_t size; + uint32_t rev2; + uint32_t syms_avail; + uint32_t reserve2; + uint32_t reserve3; +} __packed __aligned(4); + +struct msm_wdog_cpuctx_header { + uint32_t type; /* cpuctx_type */ + uint32_t magic; + uint64_t offset; + uint64_t length; +} __packed __aligned(4); + +struct msm_wdog_lnx_info { + uint32_t tsk_size; + uint32_t ti_tsk_offset; + uint32_t aa64; + uint32_t lpae; + uint64_t text_paddr; + uint64_t pgd_paddr; + uint32_t thread_size; + uint32_t va_bits; + uint32_t pa_bits; + uint32_t page_shift; + uint32_t aa32_pxn; + uint32_t pad1; + uint64_t kaslr_offset; +} __packed __aligned(4); + +struct msm_wdog_cpuctx_stat { + uint32_t ret; + uint32_t reserved; + uint64_t stack_va; + struct msm_wdog_copy jobs[LNX_CTX_AREAS]; +} __packed __aligned(4); + +struct msm_wdog_cpuctx { + union sysdbg_cpuctx sysdbg; + struct msm_wdog_cpuctx_info info; + struct msm_wdog_cpuctx_header header[CPUCTX_MAX]; + struct msm_wdog_lnx_info lnx; + struct msm_wdog_cpuctx_stat stat; + struct msm_dump_data cpu_data; + struct task_struct task; + u8 stack[THREAD_SIZE] __aligned(1024); +} __packed __aligned(4); + +#define WDOG_CPUCTX_SIZE_PERCPU (sizeof(struct msm_wdog_cpuctx)) +#define WDOG_CPUCTX_SIZE (num_present_cpus() * WDOG_CPUCTX_SIZE_PERCPU) + +#define KASLR_MAGIC_NUM 0xdead4ead +#define KASLR_REGION_LEN 32 + +#endif /* __WATCHDOG_CPU_CTX_H */ diff --git a/drivers/watchdogtest/Android.mk b/drivers/watchdogtest/Android.mk new file mode 100644 index 000000000000..f3e91f92216e --- /dev/null +++ b/drivers/watchdogtest/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := watchdogtest.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/watchdogtest/Kbuild b/drivers/watchdogtest/Kbuild new file mode 100644 index 000000000000..4b499571a86f --- /dev/null +++ b/drivers/watchdogtest/Kbuild @@ -0,0 +1,6 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(ANDROID_BUILD_TOP)/motorola/kernel/modules/include + +obj-m += watchdogtest.o +KBUILD_EXTRA_SYMBOLS += $(KBUILD_OUTPUT)/Module.symvers diff --git a/drivers/watchdogtest/Makefile b/drivers/watchdogtest/Makefile new file mode 100644 index 000000000000..bfbdc14740ef --- /dev/null +++ b/drivers/watchdogtest/Makefile @@ -0,0 +1,14 @@ +all: modules + +modules: + $(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 diff --git a/drivers/watchdogtest/watchdogtest.c b/drivers/watchdogtest/watchdogtest.c new file mode 100644 index 000000000000..185e4ba55ac4 --- /dev/null +++ b/drivers/watchdogtest/watchdogtest.c @@ -0,0 +1,101 @@ +/* + * Simple softlockup watchdog regression test module + * + * Copyright (C) 2012 Motorola Mobility LLC. + * + * 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; version 2 + * of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct dentry *dbgfs_fire_softlockup; +static struct dentry *dbgfs_fire_watchdog; + +static void (*trigger_wdog_bite)(void); + +struct completion wdt_timeout_complete; +static void wdt_timeout_work(struct work_struct *work) +{ + local_irq_disable(); + while (1) + ; + local_irq_enable(); + complete(&wdt_timeout_complete); +} +static DECLARE_WORK(wdt_timeout_work_struct, wdt_timeout_work); + +static int fire_watchdog_reset_set(void *data, u64 val) +{ + + printk(KERN_ERR "MMI Fire test hardware watchdog reset.\n"); + printk(KERN_ERR "Please wait ...\n"); + if (trigger_wdog_bite) { + trigger_wdog_bite(); + } else { + init_completion(&wdt_timeout_complete); + schedule_work_on(0, &wdt_timeout_work_struct); + wait_for_completion(&wdt_timeout_complete); + } + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(fire_watchdog_reset_fops, + NULL, fire_watchdog_reset_set, "%llu\n"); + +static int fire_softlockup_reset_set(void *data, u64 val) +{ + printk(KERN_ERR "MMI Fire test softlockup watchdog reset.\n"); + printk(KERN_ERR "Please wait ...\n"); + preempt_disable(); + while (1) + ; + + return 0; +} +DEFINE_SIMPLE_ATTRIBUTE(fire_softlockup_reset_fops, + NULL, fire_softlockup_reset_set, "%llu\n"); + +static int watchdog_test_init(void) +{ + dbgfs_fire_softlockup = debugfs_create_file("fire_softlockup_reset", + 0200, NULL, NULL, &fire_softlockup_reset_fops); + dbgfs_fire_watchdog = debugfs_create_file("fire_watchdog_reset", + 0200, NULL, NULL, &fire_watchdog_reset_fops); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,4,0) + trigger_wdog_bite = (void *)kallsyms_lookup_name("msm_trigger_wdog_bite"); +#else +#if IS_ENABLED(CONFIG_QCOM_WATCHDOG_V2) + trigger_wdog_bite = msm_trigger_wdog_bite; +#elif IS_ENABLED(CONFIG_QCOM_WDT_CORE) + trigger_wdog_bite = qcom_wdt_trigger_bite; +#else + trigger_wdog_bite = NULL; +#endif +#endif + if (!trigger_wdog_bite) + printk(KERN_ERR "Failed to get trigger_wdog_bite address\n"); + return 0; +} + +static void watchdog_test_exit(void) +{ + if(dbgfs_fire_softlockup) + debugfs_remove(dbgfs_fire_softlockup); + if(dbgfs_fire_watchdog) + debugfs_remove(dbgfs_fire_watchdog); +} + +module_init(watchdog_test_init); +module_exit(watchdog_test_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/wlan_antenna/Android.mk b/drivers/wlan_antenna/Android.mk new file mode 100644 index 000000000000..5c5d663a7e9f --- /dev/null +++ b/drivers/wlan_antenna/Android.mk @@ -0,0 +1,8 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := wlan_antenna.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/drivers/wlan_antenna/Kbuild b/drivers/wlan_antenna/Kbuild new file mode 100644 index 000000000000..d56c9d0c4c9a --- /dev/null +++ b/drivers/wlan_antenna/Kbuild @@ -0,0 +1,5 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall + +obj-m += wlan_antenna.o + diff --git a/drivers/wlan_antenna/Makefile b/drivers/wlan_antenna/Makefile new file mode 100644 index 000000000000..365d3fe73a44 --- /dev/null +++ b/drivers/wlan_antenna/Makefile @@ -0,0 +1,15 @@ +all: modules + +modules: + $(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 + diff --git a/drivers/wlan_antenna/wlan_antenna.c b/drivers/wlan_antenna/wlan_antenna.c new file mode 100644 index 000000000000..7f71369330e4 --- /dev/null +++ b/drivers/wlan_antenna/wlan_antenna.c @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#define DRIVER_VERSION "0.0.1" + +struct wlan_antenna_drvdata { + struct device *dev; + struct pinctrl *pinctrl; + struct pinctrl_state *pstate_active; + struct pinctrl_state *pstate_suspend; +}; + +static ssize_t wlan_antenna_en_read(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct wlan_antenna_drvdata *data = dev_get_drvdata(dev); + + if (!data) { + pr_err("wlan_antenna drvdata is NULL\n"); + return -EINVAL; + } + + return snprintf(buf, PAGE_SIZE, "wlan_antenna: not support!\n"); +} + +static ssize_t wlan_antenna_en_write(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct wlan_antenna_drvdata *data = dev_get_drvdata(dev); + int ret = 0; + unsigned int res = 0; + + if (!data) { + pr_err("wlan_antenna drvdata is NULL\n"); + return -EINVAL; + } + + ret = kstrtouint(buf, 0, &res); + if(ret) { + pr_err("wlan_antenna failed to get data, set as default!\n"); + } + + if(1 == res) { + ret = pinctrl_select_state(data->pinctrl, data->pstate_active); + } else { + ret = pinctrl_select_state(data->pinctrl, data->pstate_suspend); + } + + if(ret) { + pr_err("wlan_antenna failed to set pinctrl!\n"); + } else { + pr_info("wlan_antenna success set pinctrl\n"); + } + + return count; +} + +static DEVICE_ATTR(wlan_antenna_en, 0644, wlan_antenna_en_read, wlan_antenna_en_write); + +static struct attribute *wlan_antenna_sysfs_attrs[] = { + &dev_attr_wlan_antenna_en.attr, + NULL, +}; + +static struct attribute_group wlan_antenna_sysfs_attr_grp = { + .attrs = wlan_antenna_sysfs_attrs, +}; + +static int wlan_antenna_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct wlan_antenna_drvdata *drvdata; + int ret = 0; + + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->dev = &pdev->dev; + platform_set_drvdata(pdev, drvdata); + + /* Get pinctrl if target uses pinctrl */ + drvdata->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(drvdata->pinctrl)) { + ret = PTR_ERR(drvdata->pinctrl); + pr_err("%s: Pincontrol DT property returned %X\n", __func__, ret); + return ret; + } + + drvdata->pstate_active = pinctrl_lookup_state(drvdata->pinctrl, "antenna_active"); + if (IS_ERR_OR_NULL(drvdata->pstate_active)) { + ret = PTR_ERR(drvdata->pstate_active); + pr_err("Can not lookup antenna_active pinstate %d\n", ret); + return ret; + } + + drvdata->pstate_suspend = pinctrl_lookup_state(drvdata->pinctrl, "antenna_suspend"); + if (IS_ERR_OR_NULL(drvdata->pstate_suspend)) { + ret = PTR_ERR(drvdata->pstate_suspend); + pr_err("Can not lookup antenna_suspend pinstate %d\n", ret); + return ret; + } + + ret = pinctrl_select_state(drvdata->pinctrl, drvdata->pstate_suspend); + if(ret) { + pr_err("wlan_antenna failed to set pinctrl pstate_suspend!\n"); + } else { + pr_info("wlan_antenna enable pstate_suspend\n"); + } + + ret = sysfs_create_group(&dev->kobj, &wlan_antenna_sysfs_attr_grp); + if (ret) { + pr_err("%s: sysfs group creation failed %d\n", __func__, ret); + return ret; + } + dev_info(&pdev->dev, "probe: All success !\n"); + + return ret; +} + +static int wlan_antenna_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id wlan_antenna_match[] = { + { .compatible = "moto,wlan_antenna" }, + {} +}; + +static struct platform_driver wlan_antenna_plat_driver = { + .probe = wlan_antenna_probe, + .remove = wlan_antenna_remove, + .driver = { + .name = "wlan_antenna", + .owner = THIS_MODULE, + .of_match_table = wlan_antenna_match, + }, +}; + +static int wlan_antenna_init(void) +{ + return platform_driver_register(&wlan_antenna_plat_driver); +} + +static void wlan_antenna_exit(void) +{ + platform_driver_unregister(&wlan_antenna_plat_driver); +} + +module_init(wlan_antenna_init); +module_exit(wlan_antenna_exit); + +MODULE_AUTHOR("Motorola Mobiity"); +MODULE_DESCRIPTION("wlan antenna control interface driver"); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/include/linux/drv2624_mmi.h b/include/linux/drv2624_mmi.h new file mode 100644 index 000000000000..b65923c06741 --- /dev/null +++ b/include/linux/drv2624_mmi.h @@ -0,0 +1,337 @@ +#ifndef __DRV2624_H__ +#define __DRV2624_H__ +/* +** ============================================================================= +** Copyright (c)2016 Texas Instruments 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 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: +** drv2624.h +** +** Description: +** Header file for drv2624.c +** +** ============================================================================= +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define HAPTICS_DEVICE_NAME "drv2624" + +#define DRV2624_REG_ID 0x00 +#define DRV2624_ID 0x02 + +#define DRV2624_REG_STATUS 0x01 +#define DIAG_MASK 0x80 +#define DIAG_SUCCESS 0x00 +#define DIAG_SHIFT 0x07 +#define INT_MASK 0x1f +#define PRG_ERR_MASK 0x10 +#define PROCESS_DONE_MASK 0x08 +#define ULVO_MASK 0x04 +#define OVERTEMPRATURE_MASK 0x02 +#define OVERCURRENT_MASK 0x01 + +#define DRV2624_REG_INT_ENABLE 0x02 +#define INT_MASK_ALL 0x1f +#define INT_ENABLE_ALL 0x00 + +#define DRV2624_REG_DIAG_Z 0x03 + +#define DRV2624_REG_MODE 0x07 +#define DRV2624_MODE_MASK 0x03 +#define MODE_RTP 0x00 +#define MODE_WAVEFORM_SEQUENCER 0x01 +#define MODE_DIAGNOSTIC 0x02 +#define MODE_CALIBRATION 0x03 +#define PINFUNC_MASK 0x0c +#define PINFUNC_INT 0x02 +#define PINFUNC_SHIFT 0x02 + +#define DRV2624_REG_CONTROL1 0x08 +#define ACTUATOR_MASK 0x80 +#define ACTUATOR_SHIFT 7 +#define LOOP_MASK 0x40 +#define LOOP_SHIFT 6 +#define AUTOBRK_OK_MASK 0x10 +#define AUTOBRK_OK_ENABLE 0x10 + +#define DRV2624_REG_GO 0x0c + +#define DRV2624_REG_CONTROL2 0x0d +#define LIB_LRA 0x02 +#define LIB_ERM 0x03 +#define LIB_MASK 0xc0 +#define LIB_SHIFT 0x06 +#define SCALE_MASK 0x03 +#define INTERVAL_MASK 0x20 +#define INTERVAL_SHIFT 0x05 + +#define DRV2624_REG_RTP_INPUT 0x0e + +#define DRV2624_REG_SEQUENCER_1 0x0f + +#define DRV2624_REG_SEQ_LOOP_1 0x17 + +#define DRV2624_REG_SEQ_LOOP_2 0x18 + +#define DRV2624_REG_MAIN_LOOP 0x19 + +#define DRV2624_REG_RATED_VOLTAGE 0x1f + +#define DRV2624_REG_OVERDRIVE_CLAMP 0x20 + +#define DRV2624_REG_CAL_COMP 0x21 + +#define DRV2624_REG_CAL_BEMF 0x22 + +#define DRV2624_REG_LOOP_CONTROL 0x23 + +#define DRV2624_REG_RATED_VOLTAGE_CLAMP 0X24 +#define BEMFGAIN_MASK 0x03 + +#define DRV2624_REG_DRIVE_TIME 0x27 +#define DRIVE_TIME_MASK 0x1f + +#define DRV2624_REG_SAMPLE_TIME 0x29 +#define SAMPLE_TIME_MASK 0x0c + +#define MINFREQ_SEL_45HZ 0x01 +#define MINFREQ_SEL_MASK 0x80 +#define MINFREQ_SEL_SHIFT 0x07 + +#define DRV2624_REG_AUTO_CAL_TIME 0x2A + +#define DRV2624_REG_OL_PERIOD_H 0x2e + +#define DRV2624_REG_OL_PERIOD_L 0x2f + +#define DRV2624_REG_DIAG_K 0x30 + +#define DRV2624_REG_RAM_ADDR_UPPER 0xfd +#define DRV2624_REG_RAM_ADDR_LOWER 0xfe +#define DRV2624_REG_RAM_DATA 0xff + +#define GO_BIT_POLL_INTERVAL 15 +#define STANDBY_WAKE_DELAY 1 +#define WAKE_STANDBY_DELAY 3 + +/* Commands */ +#define HAPTIC_CMDID_PLAY_SINGLE_EFFECT 0x01 +#define HAPTIC_CMDID_PLAY_EFFECT_SEQUENCE 0x02 +#define HAPTIC_CMDID_PLAY_TIMED_EFFECT 0x03 +#define HAPTIC_CMDID_GET_DEV_ID 0x04 +#define HAPTIC_CMDID_RUN_DIAG 0x05 +#define HAPTIC_CMDID_AUDIOHAPTIC_ENABLE 0x06 +#define HAPTIC_CMDID_AUDIOHAPTIC_DISABLE 0x07 +#define HAPTIC_CMDID_AUDIOHAPTIC_GETSTATUS 0x08 + +#define HAPTIC_CMDID_REG_WRITE 0x09 +#define HAPTIC_CMDID_REG_READ 0x0a +#define HAPTIC_CMDID_REG_SETBIT 0x0b + +#define HAPTIC_CMDID_PATTERN_RTP 0x0c +#define HAPTIC_CMDID_RTP_SEQUENCE 0x0d +#define HAPTIC_CMDID_GET_EFFECT_COUNT 0x10 +#define HAPTIC_CMDID_UPDATE_FIRMWARE 0x11 +#define HAPTIC_CMDID_READ_FIRMWARE 0x12 +#define HAPTIC_CMDID_AUTO_CAL 0x13 + +#define HAPTIC_CMDID_STOP 0xFF + +#define MAX_TIMEOUT 10000 /* 10s */ +#define MAX_READ_BYTES 0xff +#define DRV2624_SEQUENCER_SIZE 8 + +#define WORK_IDLE 0 +#define WORK_VIBRATOR 0x01 +#define WORK_IRQ 0x02 + +#define YES 1 +#define NO 0 +#define GO 1 +#define STOP 0 + +enum actuator_type { + ERM, + LRA +}; + +enum work_mode { + NORMAL, + REDUCED +}; + +enum loop_type { + CLOSE_LOOP, + OPEN_LOOP +}; + +struct actuator_data { + enum actuator_type meActuatorType; + enum work_mode meWorkMode; + unsigned char mnRatedVoltage; + unsigned char mnOverDriveClampVoltage; + unsigned char mnRateVoltageClamp; + unsigned char mnRTPINPUTReduced; + unsigned char mnRTPINPUT; + unsigned char mnLRAFreq; + unsigned char mnSampleTime; + unsigned char mnDriveTime; +}; + +enum wave_seq_loop { + SEQ_NO_LOOP, + SEQ_LOOP_ONCE, + SEQ_LOOP_TWICE, + SEQ_LOOP_TRIPPLE +}; + +enum wave_main_loop { + MAIN_NO_LOOP, + MAIN_LOOP_ONCE, + MAIN_LOOP_TWICE, + MAIN_LOOP_3_TIMES, + MAIN_LOOP_4_TIMES, + MAIN_LOOP_5_TIMES, + MAIN_LOOP_6_TIMES, + MAIN_LOOP_INFINITELY +}; + +enum wave_main_scale { + PERCENTAGE_100, + PERCENTAGE_75, + PERCENTAGE_50, + PERCENTAGE_25 +}; + +enum wave_main_interval { + INTERVAL_5MS, + INTERVAL_1MS +}; + +struct drv2624_waveform { + enum wave_seq_loop meLoop; + unsigned char mnIndex; +}; + +struct drv2624_waveform_sequencer { + enum wave_main_loop meLoop; + struct drv2624_waveform msWaveform[DRV2624_SEQUENCER_SIZE]; +}; + +struct drv2624_platform_data { + int mnGpioNRST; + int mnGpioNPWR; + int mnGpioINT; + int mnGpioVCTRL; + enum loop_type meLoop; + struct actuator_data msActuator; + bool auto_cal; + u8 auto_cal_time; + bool no_firmware; +}; + +#define DRV2624_MAGIC 0x2624 + +struct drv2624_fw_header { + unsigned int fw_magic; + unsigned int fw_size; + unsigned int fw_date; + unsigned int fw_chksum; + unsigned int fw_effCount; +}; + +struct drv2624_data { + struct drv2624_platform_data msPlatData; + unsigned char mnDeviceID; + struct i2c_client *i2c_client; + struct device *dev; + struct regmap *mpRegmap; + unsigned char mnIntStatus; + struct drv2624_waveform_sequencer msWaveformSequencer; + unsigned char mnFileCmd; + int mnVibratorPlaying; + char mnWorkMode; + unsigned char mnCurrentReg; + int mnCurrentVibrationTime; + bool factory_mode; + bool reduced_pwr_capable; + + atomic_t reduce_pwr; + + struct wake_lock wklock; + struct hrtimer timer; + struct mutex lock; + struct work_struct vibrator_work; + struct led_classdev cdev; + + struct drv2624_fw_header msFwHeader; + unsigned char mnFwAddUpper; + unsigned char mnFwAddLower; +}; + +struct drv2624_seq_loop { + enum wave_seq_loop mpLoop[DRV2624_SEQUENCER_SIZE]; +}; + +struct drv2624_wave_seq { + unsigned char mpWaveIndex[DRV2624_SEQUENCER_SIZE]; +}; + +struct drv2624_wave_setting { + enum wave_main_loop meLoop; + enum wave_main_scale meScale; + enum wave_main_interval meInterval; +}; + +struct drv2624_autocal_result { + int mnFinished; + unsigned char mnResult; + unsigned char mnCalComp; + unsigned char mnCalBemf; + unsigned char mnCalGain; +}; + +struct drv2624_diag_result { + int mnFinished; + unsigned char mnResult; + unsigned char mnDiagZ; + unsigned char mnDiagK; +}; + +#define DRV2624_MAGIC_NUMBER 0x32363234 /* '2624' */ + +#define DRV2624_SET_SEQ_LOOP _IOWR(DRV2624_MAGIC_NUMBER, 1, \ + struct drv2624_seq_loop *) +#define DRV2624_SET_MAIN _IOWR(DRV2624_MAGIC_NUMBER, 2, \ + struct drv2624_wave_setting *) +#define DRV2624_SET_WAV_SEQ _IOWR(DRV2624_MAGIC_NUMBER, 3, \ + struct drv2624_wave_seq *) +#define DRV2624_WAVSEQ_PLAY _IOWR(DRV2624_MAGIC_NUMBER, 4, unsigned long) +#define DRV2624_STOP _IOWR(DRV2624_MAGIC_NUMBER, 5, unsigned long) +#define DRV2624_RUN_DIAGNOSTIC _IOWR(DRV2624_MAGIC_NUMBER, 6, unsigned long) +#define DRV2624_GET_DIAGRESULT _IOWR(DRV2624_MAGIC_NUMBER, 7, \ + struct drv2624_diag_result *) +#define DRV2624_RUN_AUTOCAL _IOWR(DRV2624_MAGIC_NUMBER, 8, unsigned long) +#define DRV2624_GET_CALRESULT _IOWR(DRV2624_MAGIC_NUMBER, 9, \ + struct drv2624_autocal_result *) + +#endif diff --git a/include/linux/fingerprint_mmi.h b/include/linux/fingerprint_mmi.h new file mode 100644 index 000000000000..09328bc8aaa4 --- /dev/null +++ b/include/linux/fingerprint_mmi.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2020 Motorola Mobility LLC + * + * 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. + */ + +#ifndef __LINUX_FINGERPRINT_MMI_H_ +#define __LINUX_FINGERPRINT_MMI_H_ + +#include + +extern int FPS_register_notifier(struct notifier_block *nb, unsigned long stype, bool report); +extern int FPS_unregister_notifier(struct notifier_block *nb, unsigned long stype); + +#endif diff --git a/include/linux/hssp_programmer.h b/include/linux/hssp_programmer.h new file mode 100644 index 000000000000..c0e038d941d8 --- /dev/null +++ b/include/linux/hssp_programmer.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2015, The Linux Foundation. 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 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. + */ + +#include + +void register_hssp_update_notify(struct notifier_block *nb); +void unregister_hssp_update_notify(struct notifier_block *nb); +int cycapsense_fw_update(void); +int cycapsense_reset(void); +/* +static inline void register_hssp_update_notify(struct notifier_block *nb) { } +static inline void unregister_hssp_update_notify(struct notifier_block *nb) { } +static inline int cycapsense_fw_update(void) { return 0; } +static inline int cycapsense_reset(void) { return 0; } +*/ +#define HSSP_START 0 +#define HSSP_STOP 1 diff --git a/include/linux/input/abov_sar.h b/include/linux/input/abov_sar.h new file mode 100644 index 000000000000..800883e72d45 --- /dev/null +++ b/include/linux/input/abov_sar.h @@ -0,0 +1,244 @@ +/* + * 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. + */ +#ifndef ABOV_SAR_H +#define ABOV_SAR_H + +#include +#include +#include + +/* Cap sensor report key, including cs0, cs1, cs2 and comb */ +#define KEY_CAP_CS0 0x270 +#define KEY_CAP_CS1 0x271 +#define KEY_CAP_CS2 0x272 +#define KEY_CAP_COMB 0x272 + +/* + * I2C Registers + */ +#define ABOV_VERSION_REG 0x01 +#define ABOV_MODELNO_REG 0x02 +#define ABOV_ABOV_WHOAMI_REG 0x03 +#define ABOV_IRQSTAT_REG 0x04 +#define ABOV_SOFTRESET_REG 0x06 +#define ABOV_CTRL_MODE_RET 0x07 +#define ABOV_CTRL_CHANNEL_REG 0x08 +#define ABOV_CH0_DIFF_MSB_REG 0x1C +#define ABOV_CH0_DIFF_LSB_REG 0x1D +#define ABOV_CH1_DIFF_MSB_REG 0x1E +#define ABOV_CH1_DIFF_LSB_REG 0x1F +#define ABOV_RECALI_REG 0xFB + +/* enable body stat */ +#define ABOV_TCHCMPSTAT_TCHSTAT1_FLAG 0x0C + +/* enable body stat */ +#define ABOV_TCHCMPSTAT_TCHSTAT0_FLAG 0x03 + +enum boot_mode { + NORMAL_MODE, + BOOTLOADER_MODE, + UNKONOW_MODE, +}; + +/************************************** +* define platform data +* +**************************************/ +struct smtc_reg_data { + unsigned char reg; + unsigned char val; +}; + +typedef struct smtc_reg_data smtc_reg_data_t; +typedef struct smtc_reg_data *psmtc_reg_data_t; + + +struct _buttonInfo { + /* The Key to send to the input */ + int keycode; + /* Mask to look for on Touch Status */ + int mask; + /* Current state of button. */ + int state; +}; + +struct _totalButtonInformation { + struct _buttonInfo *buttons; + int buttonSize; + struct input_dev *input_top; + struct input_dev *input_bottom; +}; + +typedef struct _totalButtonInformation buttonInformation_t; +typedef struct _totalButtonInformation *pbuttonInformation_t; + +/* Define Registers that need to be initialized to values different than + * default + */ +static struct smtc_reg_data abov_i2c_reg_setup[] = { + { + .reg = ABOV_CTRL_MODE_RET, + .val = 0x00, + }, + { + .reg = ABOV_CTRL_CHANNEL_REG, + .val = 0x07, + }, + { + .reg = ABOV_RECALI_REG, + .val = 0x01, + }, +}; + + + +static struct _buttonInfo psmtcButtons[] = { + { + .keycode = KEY_CAP_CS0, + .mask = ABOV_TCHCMPSTAT_TCHSTAT0_FLAG, + }, + { + .keycode = KEY_CAP_CS1, + .mask = ABOV_TCHCMPSTAT_TCHSTAT1_FLAG, + }, +}; + +struct abov_platform_data { + int i2c_reg_num; + struct smtc_reg_data *pi2c_reg; + struct regulator *cap_vdd; + struct regulator *cap_svdd; + bool cap_vdd_en; + bool cap_svdd_en; + unsigned irq_gpio; + /* used for custom setting for channel and scan period */ + u32 cust_prox_ctrl0; + u32 cust_raw_data_channel; + int cap_channel_top; + int cap_channel_bottom; + pbuttonInformation_t pbuttonInformation; + const char *fw_name; + + int (*get_is_nirq_low)(unsigned irq_gpio); + int (*init_platform_hw)(void); + void (*exit_platform_hw)(void); +}; +typedef struct abov_platform_data abov_platform_data_t; +typedef struct abov_platform_data *pabov_platform_data_t; + +#ifdef USE_SENSORS_CLASS +static struct sensors_classdev sensors_capsensor_top_cdev = { + .name = "capsense_top", + .vendor = "abov", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +static struct sensors_classdev sensors_capsensor_bottom_cdev = { + .name = "capsense_bottom", + .vendor = "abov", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#endif +/*************************************** +* define data struct/interrupt +* @pdev: pdev common device struction for linux +* @dworker: work struct for worker function +* @board: constant pointer to platform data +* @mutex: mutex for interrupt process +* @lock: Spin Lock used for nirq worker function +* @bus: either i2c_client or spi_client +* @pDevice: device specific struct pointer +*@read_flag : used for dump specified register +* @irq: irq number used +* @irqTimeout: msecs only set if useIrqTimer is true +* @irq_disabled: whether irq should be ignored +* @irq_gpio: irq gpio number +* @useIrqTimer: older models need irq timer for pen up cases +* @read_reg: record reg address which want to read +*@cust_prox_ctrl0 : used for custom setting for channel and scan period +* @init: (re)initialize device +* @refreshStatus: read register status +* @get_nirq_low: get whether nirq is low (platform data) +* @statusFunc: array of functions to call for corresponding status bit +***************************************/ +#define USE_THREADED_IRQ + +#define MAX_NUM_STATUS_BITS (8) + +struct fw_update { + struct work_struct worker; + bool force_update; +}; + +typedef struct abovXX abovXX_t, *pabovXX_t; +struct abovXX { + struct device *pdev; + struct delayed_work dworker; + struct abov_platform_data *board; +#if defined(USE_THREADED_IRQ) + struct mutex mutex; +#else + spinlock_t lock; +#endif + void *bus; + void *pDevice; + int read_flag; + int get_ch0_flag; + int irq; + int irqTimeout; + char irq_disabled; + /* whether irq should be ignored.. cases if enable/disable irq is not used + * or does not work properly */ + u8 useIrqTimer; + u8 read_reg; + + struct work_struct ps_notify_work; + struct notifier_block ps_notif; + bool ps_is_present; + bool loading_fw; + + struct fw_update fw_update_work; + + /* Function Pointers */ + int (*init)(pabovXX_t this); + /* since we are trying to avoid knowing registers, create a pointer to a + * common read register which would be to read what the interrupt source + * is from + */ + int (*refreshStatus)(pabovXX_t this); + int (*get_nirq_low)(unsigned irq_gpio); + void (*statusFunc[MAX_NUM_STATUS_BITS])(pabovXX_t this); +}; + +void abovXX_suspend(pabovXX_t this); +void abovXX_resume(pabovXX_t this); +int abovXX_sar_init(pabovXX_t this); +int abovXX_sar_remove(pabovXX_t this); +static int abov_tk_fw_mode_enter(struct i2c_client *client); + +#endif diff --git a/include/linux/input/abov_sar_a96t.h b/include/linux/input/abov_sar_a96t.h new file mode 100644 index 000000000000..af77e1f7e1a9 --- /dev/null +++ b/include/linux/input/abov_sar_a96t.h @@ -0,0 +1,293 @@ +/* + * 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. + */ +#ifndef ABOV_SAR_H +#define ABOV_SAR_H + +#include +#include +#include + +#define KEY_CAP_CS0 0x270 +#define KEY_CAP_CS1 0x271 +#define KEY_CAP_CS2 0x272 +#define KEY_CAP_COMB 0x272 +/* + * I2C Registers + */ +#define ABOV_VERSION_REG 0x01 +#define ABOV_MODELNO_REG 0x02 +#define ABOV_VENDOR_ID_REG 0x03 +#define ABOV_IRQSTAT_REG 0x04 +#define ABOV_SOFTRESET_REG 0x06 +#define ABOV_CTRL_MODE_REG 0x07 +#define ABOV_CTRL_CHANNEL_REG 0x08 +#define ABOV_CH0_CAP_MSB_REG 0x18 +#define ABOV_CH0_CAP_LSB_REG 0x19 +#define ABOV_CH1_CAP_MSB_REG 0x1A +#define ABOV_CH1_CAP_LSB_REG 0x1B +#define ABOV_CH2_CAP_MSB_REG 0x1C +#define ABOV_CH2_CAP_LSB_REG 0x1D +#define ABOV_REF0_CAP_MSB_REG 0x1E +#define ABOV_REF0_CAP_LSB_REG 0x1F +#define ABOV_REF1_CAP_MSB_REG 0x20 +#define ABOV_REF1_CAP_LSB_REG 0x21 +#define ABOV_CH0_DIFF_MSB_REG 0x34 +#define ABOV_CH0_DIFF_LSB_REG 0x35 +#define ABOV_CH1_DIFF_MSB_REG 0x36 +#define ABOV_CH1_DIFF_LSB_REG 0x37 +#define ABOV_CH2_DIFF_MSB_REG 0x38 +#define ABOV_CH2_DIFF_LSB_REG 0x39 +#define ABOV_TH_LEVEL_EN_REG 0x3F +#define ABOV_RECALI_REG 0xFB +#define ABOV_CHANNEL_NUMBER_TWO 0x02 + +/* enable body stat */ +#define ABOV_TCHCMPSTAT_TCHSTAT0_FLAG 0x03 +/* enable body stat */ +#define ABOV_TCHCMPSTAT_TCHSTAT1_FLAG 0x0C +/* enable body stat */ +#define ABOV_TCHCMPSTAT_TCHSTAT2_FLAG 0x30 + + +/************************************** +* define platform data +* +**************************************/ +struct smtc_reg_data { + unsigned char reg; + unsigned char val; +}; + +typedef struct smtc_reg_data smtc_reg_data_t; +typedef struct smtc_reg_data *psmtc_reg_data_t; + + +struct _buttonInfo { + /* The Key to send to the input */ + int keycode; + /* Mask to look for on Touch Status */ + int mask; + /* Current state of button. */ + int state; +}; + +struct _totalButtonInformation { + struct _buttonInfo *buttons; + int buttonSize; + struct input_dev *input_top; + struct input_dev *input_bottom; + struct input_dev *input_bottom_left; + struct input_dev *input_bottom_right; +}; + +typedef struct _totalButtonInformation buttonInformation_t; +typedef struct _totalButtonInformation *pbuttonInformation_t; + +/* Define Registers that need to be initialized to values different than + * default + */ +static struct smtc_reg_data abov_i2c_reg_setup[] = { + { + .reg = ABOV_CTRL_MODE_REG, + .val = 0x00, + }, + { + .reg = ABOV_CTRL_CHANNEL_REG, + .val = 0x1F, + }, + { + .reg = ABOV_RECALI_REG, + .val = 0x01, + }, +}; + + + +static struct _buttonInfo psmtcButtons[] = { + { + .keycode = KEY_CAP_CS0, + .mask = ABOV_TCHCMPSTAT_TCHSTAT0_FLAG, + }, + { + .keycode = KEY_CAP_CS1, + .mask = ABOV_TCHCMPSTAT_TCHSTAT1_FLAG, + }, + { + .keycode = KEY_CAP_CS2, + .mask = ABOV_TCHCMPSTAT_TCHSTAT2_FLAG, + }, +}; + +struct abov_platform_data { + int i2c_reg_num; + struct smtc_reg_data *pi2c_reg; + struct regulator *cap_vdd; + struct regulator *cap_svdd; + bool cap_vdd_en; + bool cap_svdd_en; + unsigned irq_gpio; + /* used for custom setting for channel and scan period */ + int cap_channel_top; + int cap_channel_bottom; + int cap_channel_bottom_left; + int cap_channel_bottom_right; + pbuttonInformation_t pbuttonInformation; + const char *fw_name; + + int (*get_is_nirq_low)(unsigned irq_gpio); + int (*init_platform_hw)(void); + void (*exit_platform_hw)(void); +}; +typedef struct abov_platform_data abov_platform_data_t; +typedef struct abov_platform_data *pabov_platform_data_t; + +#ifdef USE_SENSORS_CLASS +static struct sensors_classdev sensors_capsensor_top_cdev = { + .name = "capsense_top", + .vendor = "abov", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +static struct sensors_classdev sensors_capsensor_bottom_cdev = { + .name = "capsense_bottom", + .vendor = "abov", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +static struct sensors_classdev sensors_capsensor_bottom_left_cdev = { + .name = "capsense_bottom_left", + .vendor = "abov", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +static struct sensors_classdev sensors_capsensor_bottom_right_cdev = { + .name = "capsense_bottom_right", + .vendor = "abov", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#endif + + +typedef enum{ + CAPSENSOR_ENABLE_FLAG_TOP=1, + CAPSENSOR_ENABLE_FLAG_BOTTOM=2, + CAPSENSOR_ENABLE_FLAG_BOTTOM_LEFT=4, + CAPSENSOR_ENABLE_FLAG_BOTTOM_RIGHT=8, + CAPSENSOR_ENABLE_FLAG_MAX +}capsensor_enable_flag_t; +/*************************************** +* define data struct/interrupt +* @pdev: pdev common device struction for linux +* @dworker: work struct for worker function +* @board: constant pointer to platform data +* @mutex: mutex for interrupt process +* @lock: Spin Lock used for nirq worker function +* @bus: either i2c_client or spi_client +* @pDevice: device specific struct pointer +*@read_flag : used for dump specified register +* @irq: irq number used +* @irqTimeout: msecs only set if useIrqTimer is true +* @irq_disabled: whether irq should be ignored +* @irq_gpio: irq gpio number +* @useIrqTimer: older models need irq timer for pen up cases +* @read_reg: record reg address which want to read +*@cust_prox_ctrl0 : used for custom setting for channel and scan period +* @init: (re)initialize device +* @get_nirq_low: get whether nirq is low (platform data) +* @statusFunc: array of functions to call for corresponding status bit +***************************************/ +#define USE_THREADED_IRQ + +#define MAX_NUM_STATUS_BITS (8) + +typedef struct abovXX abovXX_t, *pabovXX_t; +struct abovXX { + struct device *pdev; + struct delayed_work dworker; + struct abov_platform_data *board; +#if defined(USE_THREADED_IRQ) + struct mutex mutex; +#else + spinlock_t lock; +#endif + void *bus; + void *pDevice; + int read_flag; + int irq; + int irqTimeout; + char irq_disabled; + /* whether irq should be ignored.. cases if enable/disable irq is not used + * or does not work properly */ + u8 useIrqTimer; + u8 read_reg; + + // sensors enable flag + capsensor_enable_flag_t enable_flag; + + struct work_struct ps_notify_work; + struct notifier_block ps_notif; + bool ps_is_present; + bool loading_fw; + + struct work_struct fw_update_work; + + /* Function Pointers */ + int (*init)(pabovXX_t this); + /* since we are trying to avoid knowing registers, create a pointer to a + * common read register which would be to read what the interrupt source + * is from + */ + int (*get_nirq_low)(unsigned irq_gpio); + void (*statusFunc[MAX_NUM_STATUS_BITS])(pabovXX_t this); +}; + +void abovXX_suspend(pabovXX_t this); +void abovXX_resume(pabovXX_t this); +int abovXX_sar_init(pabovXX_t this); +int abovXX_sar_remove(pabovXX_t this); + +#endif diff --git a/include/linux/input/abov_sar_mmi_overlay.h b/include/linux/input/abov_sar_mmi_overlay.h new file mode 100644 index 000000000000..5abf20a9e3ba --- /dev/null +++ b/include/linux/input/abov_sar_mmi_overlay.h @@ -0,0 +1,315 @@ +/* + * 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. + */ +#ifndef ABOV_SAR_H +#define ABOV_SAR_H + +#include +#include +#include + +#define KEY_CAP_CS0 0x270 +#define KEY_CAP_CS1 0x271 +#define KEY_CAP_CS2 0x272 +#define KEY_CAP_CS3 0x273 +#define KEY_CAP_COMB 0x272 +/* + * I2C Registers + */ +#define ABOV_VERSION_REG 0x01 +#define ABOV_MODELNO_REG 0x02 +#define ABOV_VENDOR_ID_REG 0x03 +#define ABOV_IRQSTAT_REG 0x04 +#define ABOV_SOFTRESET_REG 0x06 +#define ABOV_CTRL_MODE_REG 0x07 +#define ABOV_CTRL_CHANNEL_REG 0x08 +#define ABOV_CH0_CAP_MSB_REG 0x18 +#define ABOV_CH0_CAP_LSB_REG 0x19 +#define ABOV_CH1_CAP_MSB_REG 0x1A +#define ABOV_CH1_CAP_LSB_REG 0x1B +#define ABOV_CH2_CAP_MSB_REG 0x1C +#define ABOV_CH2_CAP_LSB_REG 0x1D +#define ABOV_REF0_CAP_MSB_REG 0x1E +#define ABOV_REF0_CAP_LSB_REG 0x1F +#define ABOV_REF1_CAP_MSB_REG 0x20 +#define ABOV_REF1_CAP_LSB_REG 0x21 +#define ABOV_CH0_DIFF_MSB_REG 0x34 +#define ABOV_CH0_DIFF_LSB_REG 0x35 +#define ABOV_CH1_DIFF_MSB_REG 0x36 +#define ABOV_CH1_DIFF_LSB_REG 0x37 +#define ABOV_CH2_DIFF_MSB_REG 0x38 +#define ABOV_CH2_DIFF_LSB_REG 0x39 +#define ABOV_TH_LEVEL_EN_REG 0x3F +#define ABOV_RECALI_REG 0xFB +#define ABOV_CHANNEL_NUMBER_TWO 0x02 + +/* enable body stat */ +#define ABOV_TCHCMPSTAT_TCHSTAT0_FLAG 0x03 +/* enable body stat */ +#define ABOV_TCHCMPSTAT_TCHSTAT1_FLAG 0x0C +/* enable body stat */ +#define ABOV_TCHCMPSTAT_TCHSTAT2_FLAG 0x30 +/* enable body stat */ +#define ABOV_TCHCMPSTAT_TCHSTAT3_FLAG 0xC0 + +/* + * A96T346DF FW MODE:0x39, A96T346HW FW MODE:0x3A + */ +#define ABOV_DF_FW_MODE 0x39 +#define ABOV_HW_FW_MODE 0x3A + +/************************************** +* define platform data +* +**************************************/ +struct smtc_reg_data { + unsigned char reg; + unsigned char val; +}; + +typedef struct smtc_reg_data smtc_reg_data_t; +typedef struct smtc_reg_data *psmtc_reg_data_t; + + +struct _buttonInfo { + /* The Key to send to the input */ + int keycode; + /* Mask to look for on Touch Status */ + int mask; + /* Current state of button. */ + int state; +}; + +struct _totalButtonInformation { + struct _buttonInfo *buttons; + int buttonSize; + struct input_dev *input_ch0; + struct input_dev *input_ch1; + struct input_dev *input_ch2; + struct input_dev *input_ch3; +}; + +typedef struct _totalButtonInformation buttonInformation_t; +typedef struct _totalButtonInformation *pbuttonInformation_t; + +/* Define Registers that need to be initialized to values different than + * default + */ +static struct smtc_reg_data abov_i2c_reg_setup[] = { + { + .reg = ABOV_CTRL_MODE_REG, + .val = 0x00, + }, + { + .reg = ABOV_CTRL_CHANNEL_REG, + .val = 0x1F, + }, + { + .reg = ABOV_RECALI_REG, + .val = 0x01, + }, +}; + + + +static struct _buttonInfo psmtcButtons[] = { + { + .keycode = KEY_CAP_CS0, + .mask = ABOV_TCHCMPSTAT_TCHSTAT0_FLAG, + }, + { + .keycode = KEY_CAP_CS1, + .mask = ABOV_TCHCMPSTAT_TCHSTAT1_FLAG, + }, + { + .keycode = KEY_CAP_CS2, + .mask = ABOV_TCHCMPSTAT_TCHSTAT2_FLAG, + }, + { + .keycode = KEY_CAP_CS3, + .mask = ABOV_TCHCMPSTAT_TCHSTAT3_FLAG, + }, +}; + +struct abov_platform_data { + int i2c_reg_num; + struct smtc_reg_data *pi2c_reg; + struct regulator *cap_vdd; + struct regulator *cap_svdd; + bool cap_vdd_en; + bool cap_svdd_en; + unsigned irq_gpio; + /* used for custom setting for channel and scan period */ + int cap_channel_ch0; + int cap_channel_ch1; + int cap_channel_ch2; + int cap_channel_ch3; + + const char *cap_ch0_name; + const char *cap_ch1_name; + const char *cap_ch2_name; + const char *cap_ch3_name; + pbuttonInformation_t pbuttonInformation; + const char *fw_name; + int fw_mode_ret; + int cap_button_mask; + + int (*get_is_nirq_low)(unsigned irq_gpio); + int (*init_platform_hw)(void); + void (*exit_platform_hw)(void); +}; +typedef struct abov_platform_data abov_platform_data_t; +typedef struct abov_platform_data *pabov_platform_data_t; + +#ifdef USE_SENSORS_CLASS +static struct sensors_classdev sensors_capsensor_ch0_cdev = { + //.name = "capsense_top", + .vendor = "abov", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +static struct sensors_classdev sensors_capsensor_ch1_cdev = { + //.name = "capsense_bottom", + .vendor = "abov", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +static struct sensors_classdev sensors_capsensor_ch2_cdev = { + //.name = "capsense_bottom_left", + .vendor = "abov", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +static struct sensors_classdev sensors_capsensor_ch3_cdev = { + //.name = "capsense_bottom_left", + .vendor = "abov", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#endif + + +typedef enum{ + CAPSENSOR_ENABLE_FLAG_CH0=1, + CAPSENSOR_ENABLE_FLAG_CH1=2, + CAPSENSOR_ENABLE_FLAG_CH2=4, + CAPSENSOR_ENABLE_FLAG_CH3=8, + CAPSENSOR_ENABLE_FLAG_MAX +}capsensor_enable_flag_t; +/*************************************** +* define data struct/interrupt +* @pdev: pdev common device struction for linux +* @dworker: work struct for worker function +* @board: constant pointer to platform data +* @mutex: mutex for interrupt process +* @lock: Spin Lock used for nirq worker function +* @bus: either i2c_client or spi_client +* @pDevice: device specific struct pointer +*@read_flag : used for dump specified register +* @irq: irq number used +* @irqTimeout: msecs only set if useIrqTimer is true +* @irq_disabled: whether irq should be ignored +* @irq_gpio: irq gpio number +* @useIrqTimer: older models need irq timer for pen up cases +* @read_reg: record reg address which want to read +*@cust_prox_ctrl0 : used for custom setting for channel and scan period +* @init: (re)initialize device +* @get_nirq_low: get whether nirq is low (platform data) +* @statusFunc: array of functions to call for corresponding status bit +***************************************/ +#define USE_THREADED_IRQ + +#define MAX_NUM_STATUS_BITS (8) + +typedef struct abovXX abovXX_t, *pabovXX_t; +struct abovXX { + struct device *pdev; + struct delayed_work dworker; + struct abov_platform_data *board; +#if defined(USE_THREADED_IRQ) + struct mutex mutex; +#else + spinlock_t lock; +#endif + void *bus; + void *pDevice; + int read_flag; + int irq; + int irqTimeout; + /* interrupt check flag */ + int int_state; + char irq_disabled; + /* whether irq should be ignored.. cases if enable/disable irq is not used + * or does not work properly */ + u8 useIrqTimer; + u8 read_reg; + + // sensors enable flag + capsensor_enable_flag_t enable_flag; + + struct work_struct ps_notify_work; + struct notifier_block ps_notif; + bool ps_is_present; + bool loading_fw; + int fw_dl_status; + + struct work_struct fw_update_work; + + /* Function Pointers */ + int (*init)(pabovXX_t this); + /* since we are trying to avoid knowing registers, create a pointer to a + * common read register which would be to read what the interrupt source + * is from + */ + int (*get_nirq_low)(unsigned irq_gpio); + void (*statusFunc[MAX_NUM_STATUS_BITS])(pabovXX_t this); +}; + +void abovXX_suspend(pabovXX_t this); +void abovXX_resume(pabovXX_t this); +int abovXX_sar_init(pabovXX_t this); +int abovXX_sar_remove(pabovXX_t this); + +#endif diff --git a/include/linux/input/abov_sar_mmi_overlay_7ch.h b/include/linux/input/abov_sar_mmi_overlay_7ch.h new file mode 100644 index 000000000000..b2bfef4a3da5 --- /dev/null +++ b/include/linux/input/abov_sar_mmi_overlay_7ch.h @@ -0,0 +1,347 @@ +/* + * 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. + */ +#ifndef ABOV_SAR_H +#define ABOV_SAR_H + +#include +#include +#include + +#define KEY_CAP_CS0 0x270 +#define KEY_CAP_CS1 0x271 +#define KEY_CAP_CS2 0x272 +#define KEY_CAP_CS3 0x273 +#define KEY_CAP_CS4 0x274 +#define KEY_CAP_COMB 0x272 +/* + * I2C Registers + */ +#define ABOV_VERSION_REG 0x01 +#define ABOV_MODELNO_REG 0x02 +#define ABOV_VENDOR_ID_REG 0x03 +#define ABOV_IRQSTAT_REG 0x04 +#define ABOV_IRQSTAT1_REG 0x05 +#define ABOV_SOFTRESET_REG 0x06 +#define ABOV_CTRL_MODE_REG 0x07 +#define ABOV_CTRL_CHANNEL_REG 0x08 +#define ABOV_RECALI_REG 0xFB +#define ABOV_CHANNEL_NUMBER_TWO 0x02 +#define ABOV_CHANNEL_NUMBER_THREE 0x03 +#define ABOV_CHANNEL_NUMBER_FOUR 0x04 +#define ABOV_CHANNEL_NUMBER_FIVE 0x05 +#define ABOV_CTRL_MODE_ACTIVE 0x00 +#define ABOV_CTRL_MODE_SLEEP 0x01 +#define ABOV_CTRL_MODE_STOP 0x02 + +/* enable body stat */ +#define ABOV_TCHCMPSTAT_TCHSTAT0_FLAG 0x0003 +/* enable body stat */ +#define ABOV_TCHCMPSTAT_TCHSTAT1_FLAG 0x000C +/* enable body stat */ +#define ABOV_TCHCMPSTAT_TCHSTAT2_FLAG 0x0030 +/* enable body stat */ +#define ABOV_TCHCMPSTAT_TCHSTAT3_FLAG 0x00C0 +/* enable body stat */ +#define ABOV_TCHCMPSTAT_TCHSTAT4_FLAG 0x0300 + +/* cap_button_mask */ +#define ABOV_CAP_BUTTON_2CH_MASK 0x05 +#define ABOV_CAP_BUTTON_3CH_MASK 0x15 +#define ABOV_CAP_BUTTON_4CH_MASK 0x55 +#define ABOV_CAP_BUTTON_5CH_MASK 0x155 + +/* Flash erase time */ +#define ABOV_T346_T349_FLASH_ERASE_TIME 1400 +#define ABOV_T376_FLASH_ERASE_TIME 3000 + +/* + * A96T346DF/A96T349DF FW MODE:0x39 + * A96T346HW/A96T349HW FW MODE:0x3A + * A96T376EF FW MODE:0x70 + * A96T375DF FW MODE:0x71 + */ +#define ABOV_DF_FW_MODE 0x39 +#define ABOV_HW_FW_MODE 0x3A +#define ABOV_T376_FW_MODE 0x70 +#define ABOV_T375_FW_MODE 0x71 + +/************************************** +* define platform data +* +**************************************/ +struct smtc_reg_data { + unsigned char reg; + unsigned char val; +}; + +typedef struct smtc_reg_data smtc_reg_data_t; +typedef struct smtc_reg_data *psmtc_reg_data_t; + + +struct _buttonInfo { + /* The Key to send to the input */ + int keycode; + /* Mask to look for on Touch Status */ + int mask; + /* Current state of button. */ + int state; +}; + +struct _totalButtonInformation { + struct _buttonInfo *buttons; + int buttonSize; + struct input_dev *input_ch0; + struct input_dev *input_ch1; + struct input_dev *input_ch2; + struct input_dev *input_ch3; + struct input_dev *input_ch4; +}; + +typedef struct _totalButtonInformation buttonInformation_t; +typedef struct _totalButtonInformation *pbuttonInformation_t; + +/* Define Registers that need to be initialized to values different than + * default + */ +static struct smtc_reg_data abov_i2c_reg_setup[] = { + { + .reg = ABOV_CTRL_MODE_REG, + .val = 0x00, + }, + { + .reg = ABOV_CTRL_CHANNEL_REG, + .val = 0x1F, + }, + { + .reg = ABOV_RECALI_REG, + .val = 0x01, + }, +}; + + + +static struct _buttonInfo psmtcButtons[] = { + { + .keycode = KEY_CAP_CS0, + .mask = ABOV_TCHCMPSTAT_TCHSTAT0_FLAG, + }, + { + .keycode = KEY_CAP_CS1, + .mask = ABOV_TCHCMPSTAT_TCHSTAT1_FLAG, + }, + { + .keycode = KEY_CAP_CS2, + .mask = ABOV_TCHCMPSTAT_TCHSTAT2_FLAG, + }, + { + .keycode = KEY_CAP_CS3, + .mask = ABOV_TCHCMPSTAT_TCHSTAT3_FLAG, + }, + { + .keycode = KEY_CAP_CS4, + .mask = ABOV_TCHCMPSTAT_TCHSTAT4_FLAG, + }, +}; + +struct abov_platform_data { + int i2c_reg_num; + struct smtc_reg_data *pi2c_reg; + struct regulator *cap_vdd; + struct regulator *cap_svdd; + bool cap_vdd_en; + bool cap_svdd_en; + unsigned irq_gpio; + /* used for custom setting for channel and scan period */ + int cap_channel_ch0; + int cap_channel_ch1; + int cap_channel_ch2; + int cap_channel_ch3; + int cap_channel_ch4; + + const char *cap_ch0_name; + const char *cap_ch1_name; + const char *cap_ch2_name; + const char *cap_ch3_name; + const char *cap_ch4_name; + pbuttonInformation_t pbuttonInformation; + const char *fw_name; + int fw_mode_ret; + int cap_button_mask; + + int (*get_is_nirq_low)(unsigned irq_gpio); + int (*init_platform_hw)(void); + void (*exit_platform_hw)(void); +}; +typedef struct abov_platform_data abov_platform_data_t; +typedef struct abov_platform_data *pabov_platform_data_t; + +#ifdef USE_SENSORS_CLASS +static struct sensors_classdev sensors_capsensor_ch0_cdev = { + //.name = "capsense_top", + .vendor = "abov", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +static struct sensors_classdev sensors_capsensor_ch1_cdev = { + //.name = "capsense_bottom", + .vendor = "abov", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +static struct sensors_classdev sensors_capsensor_ch2_cdev = { + //.name = "capsense_bottom_left", + .vendor = "abov", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +static struct sensors_classdev sensors_capsensor_ch3_cdev = { + //.name = "capsense_bottom_left", + .vendor = "abov", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +static struct sensors_classdev sensors_capsensor_ch4_cdev = { + //.name = "capsense_bottom_left", + .vendor = "abov", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#endif + + +typedef enum{ + CAPSENSOR_ENABLE_FLAG_CH0=1, + CAPSENSOR_ENABLE_FLAG_CH1=2, + CAPSENSOR_ENABLE_FLAG_CH2=4, + CAPSENSOR_ENABLE_FLAG_CH3=8, + CAPSENSOR_ENABLE_FLAG_CH4=16, + CAPSENSOR_ENABLE_FLAG_MAX +}capsensor_enable_flag_t; +/*************************************** +* define data struct/interrupt +* @pdev: pdev common device struction for linux +* @dworker: work struct for worker function +* @board: constant pointer to platform data +* @mutex: mutex for interrupt process +* @lock: Spin Lock used for nirq worker function +* @bus: either i2c_client or spi_client +* @pDevice: device specific struct pointer +*@read_flag : used for dump specified register +* @irq: irq number used +* @irqTimeout: msecs only set if useIrqTimer is true +* @irq_disabled: whether irq should be ignored +* @irq_gpio: irq gpio number +* @useIrqTimer: older models need irq timer for pen up cases +* @read_reg: record reg address which want to read +*@cust_prox_ctrl0 : used for custom setting for channel and scan period +* @init: (re)initialize device +* @get_nirq_low: get whether nirq is low (platform data) +* @statusFunc: array of functions to call for corresponding status bit +***************************************/ +#define USE_THREADED_IRQ + +#define MAX_NUM_STATUS_BITS (8) + +typedef struct abovXX abovXX_t, *pabovXX_t; +struct abovXX { + struct device *pdev; + struct delayed_work dworker; + struct abov_platform_data *board; +#if defined(USE_THREADED_IRQ) + struct mutex mutex; +#else + spinlock_t lock; +#endif + void *bus; + void *pDevice; + int read_flag; + int irq; + int irqTimeout; + /* interrupt check flag */ + int int_state; + char irq_disabled; + /* whether irq should be ignored.. cases if enable/disable irq is not used + * or does not work properly */ + u8 useIrqTimer; + u8 read_reg; + + // sensors enable flag + capsensor_enable_flag_t enable_flag; + + struct work_struct ps_notify_work; + struct notifier_block ps_notif; + bool ps_is_present; + bool loading_fw; + int fw_dl_status; + + struct work_struct fw_update_work; + + /* Function Pointers */ + int (*init)(pabovXX_t this); + /* since we are trying to avoid knowing registers, create a pointer to a + * common read register which would be to read what the interrupt source + * is from + */ + int (*get_nirq_low)(unsigned irq_gpio); + void (*statusFunc[MAX_NUM_STATUS_BITS])(pabovXX_t this); +}; + +void abovXX_suspend(pabovXX_t this); +void abovXX_resume(pabovXX_t this); +int abovXX_sar_init(pabovXX_t this); +int abovXX_sar_remove(pabovXX_t this); + +#endif diff --git a/include/linux/input/aw9610x.h b/include/linux/input/aw9610x.h new file mode 100644 index 000000000000..12c751a5b2c1 --- /dev/null +++ b/include/linux/input/aw9610x.h @@ -0,0 +1,482 @@ +#ifndef _AW9610X_H_ +#define _AW9610X_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define AW_POWER_ON + +#define AW9610X_CHIP_ID (0xa961) +#define AW_SAR_SUCCESS (0) +#define AW_VCC_MIN_UV (2800000) +#define AW_VCC_MAX_UV (3600000) +#define AW_SPE_REG_NUM (8) +#define AW_CLA1_SPE_REG_NUM (6) +#define AW_SPE_REG_DWORD (8) +#define AW_DATA_PROCESS_FACTOR (1024) +#define AW_CHANNEL_NUMBER_TWO 0x02 +#define AW_CHANNEL_NUMBER_THREE 0x03 +#define AW_CHANNEL_NUMBER_FOUR 0x04 +#define AW_CHANNEL_NUMBER_FIVE 0x05 + +#if 0 +#define AWLOGD(dev, format, arg...) \ + do {\ + dev_printk(KERN_DEBUG, dev, \ + "[%s:%d] "format"\n", __func__, __LINE__, ##arg);\ + } while (0) + +#define AWLOGI(dev, format, arg...) \ + do {\ + dev_printk(KERN_INFO, dev, \ + "[%s:%d] "format"\n", __func__, __LINE__, ##arg);\ + } while (0) + +#define AWLOGE(dev, format, arg...) \ + do {\ + dev_printk(KERN_ERR, dev, \ + "[%s:%d] "format"\n", __func__, __LINE__, ##arg);\ + } while (0) +#endif + +/********************************************** +* cfg load situation +**********************************************/ +enum aw9610x_cfg_situ { + AW_CFG_UNLOAD, + AW_CFG_LOADED, +}; + +/********************************************** +*spereg cali flag +**********************************************/ +enum aw9610x_cali_flag { + AW_NO_CALI, + AW_CALI, +}; + +enum aw9610x_sar_vers { + AW9610X = 2, + AW9610XA = 6, + AW9610XB = 0xa, +}; + +enum aw9610x_operation_mode { + AW9610X_ACTIVE_MODE = 1, + AW9610X_SLEEP_MODE, + AW9610X_DEEPSLEEP_MODE, +}; + +enum aw9610x_operation_ap_mode { + AW9610X_SLEEP_AP_MODE = 0, + AW9610X_ACTIVE_AP_MODE, + AW9610X_DEEPSLEEP_AP_MODE, +}; +/********************************************** +*spereg addr offset +**********************************************/ +enum aw9610x_spereg_addr_offset { + AW_CL1SPE_CALI_OS = 20, + AW_CL1SPE_DEAL_OS = 60, + AW_CL2SPE_CALI_OS = 4, + AW_CL2SPE_DEAL_OS = 4, +}; + +/********************************************** + *the flag of i2c read/write + **********************************************/ +enum aw9610x_function_flag { + AW9610X_FUNC_OFF, + AW9610X_FUNC_ON, +}; + +/********************************************** + *the flag of i2c read/write + **********************************************/ +enum aw9610x_i2c_flags { + AW9610X_I2C_WR, + AW9610X_I2C_RD, +}; + +/********************************************************* +* aw9610x error flag: +* @AW_MALLOC_FAILED: malloc space failed. +* @AW_CHIPID_FAILED: the chipid is error. +* @AW_IRQIO_FAILED: irq gpio invalid. +* @AW_IRQ_REQUEST_FAILED: irq requst failed. +* @AW_INPUT_ALLOCATE_FILED : inpt malloc failed. +* @AW_INPUT_REGISTER_FAILED : inpt_dev register failed. +* @AW_CFG_LOAD_TIME_FAILED : cfg situation not confirmed. +* @AW_TRIM_ERROR : the chip no trim. +* @AW_IRQGPIO_FAILED : irq gpio is invalid. +* @AW_MULTIPLE_SAR_FAILED : sar-num is error in aw9610x.dtsi +**********************************************************/ +enum aw9610x_err_flags { + AW_MALLOC_FAILED = 200, + AW_CHIPID_FAILED, + AW_IRQIO_FAILED, + AW_IRQ_REQUEST_FAILED, + AW_INPUT_ALLOCATE_FILED, + AW_INPUT_REGISTER_FAILED, + AW_CFG_LOAD_TIME_FAILED, + AW_TRIM_ERROR, + AW_IRQGPIO_FAILED, + AW_MULTIPLE_SAR_FAILED, + AW_VERS_ERR, +}; + +/********************************************** +* mutiple sar define +**********************************************/ +enum aw9610x_multiple_sar { + AW_SAR0, + AW_SAR1, + AW_SAR_MAX, +}; + +/********************************************** +* mutiple channel define +**********************************************/ +enum aw9610x_ichannel { + AW_CHANNEL0, + AW_CHANNEL1, + AW_CHANNEL2, + AW_CHANNEL3, + AW_CHANNEL4, + AW_CHANNEL5, + AW_CHANNEL_MAX, +}; + +/************************************************************ +* Interrupts near or far from the threshold will be triggered +*************************************************************/ +enum aw9610x_irq_trigger_position { + FAR, + TRIGGER_TH0, + TRIGGER_TH1 = 0x03, + TRIGGER_TH2 = 0x07, + TRIGGER_TH3 = 0x0f, +}; + + +struct aw_i2c_package { + uint8_t addr_bytes; + uint8_t data_bytes; + uint8_t reg_num; + uint8_t init_addr[4]; + uint8_t *p_reg_data; +}; + +struct aw_pad { + uint8_t curr_state; + uint8_t last_state; + struct input_dev *input; + uint8_t *name; +}; + +struct aw9610x { + uint8_t cali_flag; + uint8_t power_prox; + uint8_t vers; + uint8_t mode_flag0; + uint8_t mode_flag1; + uint8_t pad; + uint32_t aw_channel_number; + uint32_t aw_ref_channel; + const char *aw_ch_name[12]; + + int32_t vcc_3v0_Pin; + int32_t pwr_by_gpio; + uint32_t sar_num; +#ifdef CONFIG_USE_HARDWARE_VERSION + uint32_t hw_version; +#endif + int32_t irq_gpio; + int32_t to_irq; + uint32_t irq_status; + uint32_t hostirqen; + uint32_t first_irq_flag; + uint32_t mode; + uint32_t int_state; + uint16_t read_reg; + bool read_flag; + bool pwprox_dete; + bool firmware_flag; + bool satu_release; + bool power_enable; + + struct work_struct ps_notify_work; + struct notifier_block ps_notif; + bool ps_is_present; + + struct delayed_work cfg_work; + struct i2c_client *i2c; + struct device *dev; + struct delayed_work dworker; /* work struct for worker function */ + struct aw_bin *aw_bin; + struct aw_i2c_package aw_i2c_package; + struct aw_pad *aw_pad; + struct regulator *vcc; + uint8_t satu_flag[6]; + uint32_t spedata[8]; + uint32_t satu_data[6]; + uint32_t nvspe_data[8]; + uint8_t chip_name[9]; + uint8_t cfg_name[20]; + uint8_t cfg_vers[2]; +}; + +struct aw9610x_cfg { + int len; + unsigned int data[]; +}; + +struct aw_pad pad_event[] = +{ + { + .curr_state = 0, + .last_state = 0, + //.name = "sar0_pad0", + }, + { + .curr_state = 0, + .last_state = 0, + //.name = "sar0_pad1", + }, + { + .curr_state = 0, + .last_state = 0, + //.name = "sar0_pad2", + }, + { + .curr_state = 0, + .last_state = 0, + //.name = "sar0_pad3", + }, + { + .curr_state = 0, + .last_state = 0, + //.name = "sar0_pad4", + }, + { + .curr_state = 0, + .last_state = 0, + //.name = "sar0_pad5", + }, + { + .curr_state = 0, + .last_state = 0, + //.name = "sar1_pad0", + }, + { + .curr_state = 0, + .last_state = 0, + //.name = "sar1_pad1", + }, + { + .curr_state = 0, + .last_state = 0, + //.name = "sar1_pad2", + }, + { + .curr_state = 0, + .last_state = 0, + //.name = "sar1_pad3", + }, + { + .curr_state = 0, + .last_state = 0, + //.name = "sar1_pad4", + }, + { + .curr_state = 0, + .last_state = 0, + //.name = "sar1_pad5", + }, +}; + +uint32_t attr_buf[] = { + 8, 10, + 9, 100, + 10, 1000, +}; + +#ifdef USE_SENSORS_CLASS +struct sensors_classdev sensors_capsensor_chs[AW_CHANNEL_MAX] = { + [0 ... AW_CHANNEL_MAX-1] = { + //.name = "sar0_pad0", + .vendor = "awinic", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "0.12", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, + } +}; +#endif +/****************************************************** +* Register Detail +******************************************************/ +uint32_t aw9610x_reg_default[] = { + 0x0000, 0x00003f3f, + 0x0004, 0x00000064, + 0x0008, 0x0017c11e, + 0x000c, 0x05000000, + 0x0010, 0x00093ffd, + 0x0014, 0x19240009, + 0x0018, 0xd81c0207, + 0x001c, 0xff000000, + 0x0020, 0x00241900, + 0x0024, 0x00093ff7, + 0x0028, 0x58020009, + 0x002c, 0xd81c0207, + 0x0030, 0xff000000, + 0x0034, 0x00025800, + 0x0038, 0x00093fdf, + 0x003c, 0x7d3b0009, + 0x0040, 0xd81c0207, + 0x0044, 0xff000000, + 0x0048, 0x003b7d00, + 0x004c, 0x00093f7f, + 0x0050, 0xe9310009, + 0x0054, 0xd81c0207, + 0x0058, 0xff000000, + 0x005c, 0x0031e900, + 0x0060, 0x00093dff, + 0x0064, 0x1a0c0009, + 0x0068, 0xd81c0207, + 0x006c, 0xff000000, + 0x0070, 0x000c1a00, + 0x0074, 0x80093fff, + 0x0078, 0x043d0009, + 0x007c, 0xd81c0207, + 0x0080, 0xff000000, + 0x0084, 0x003d0400, + 0x00a0, 0xe6400000, + 0x00a4, 0x00000000, + 0x00a8, 0x010408d2, + 0x00ac, 0x00000000, + 0x00b0, 0x00000000, + 0x00b8, 0x00005fff, + 0x00bc, 0x00000000, + 0x00c0, 0x00000000, + 0x00c4, 0x00000000, + 0x00c8, 0x00000000, + 0x00cc, 0x00000000, + 0x00d0, 0x00000000, + 0x00d4, 0x00000000, + 0x00d8, 0x00000000, + 0x00dc, 0xe6447800, + 0x00e0, 0x78000000, + 0x00e4, 0x010408d2, + 0x00e8, 0x00000000, + 0x00ec, 0x00000000, + 0x00f4, 0x00005fff, + 0x00f8, 0x00000000, + 0x00fc, 0x00000000, + 0x0100, 0x00000000, + 0x0104, 0x00000000, + 0x0108, 0x00000000, + 0x010c, 0x02000000, + 0x0110, 0x00000000, + 0x0114, 0x00000000, + 0x0118, 0xe6447800, + 0x011c, 0x78000000, + 0x0120, 0x010408d2, + 0x0124, 0x00000000, + 0x0128, 0x00000000, + 0x0130, 0x00005fff, + 0x0134, 0x00000000, + 0x0138, 0x00000000, + 0x013c, 0x00000000, + 0x0140, 0x00000000, + 0x0144, 0x00000000, + 0x0148, 0x02000000, + 0x014c, 0x00000000, + 0x0150, 0x00000000, + 0x0154, 0xe6447800, + 0x0158, 0x78000000, + 0x015c, 0x010408d2, + 0x0160, 0x00000000, + 0x0164, 0x00000000, + 0x016c, 0x00005fff, + 0x0170, 0x00000000, + 0x0174, 0x00000000, + 0x0178, 0x00000000, + 0x017c, 0x00000000, + 0x0180, 0x00000000, + 0x0184, 0x02000000, + 0x0188, 0x00000000, + 0x018c, 0x00000000, + 0x0190, 0xe6447800, + 0x0194, 0x78000000, + 0x0198, 0x010408d2, + 0x019c, 0x00000000, + 0x01a0, 0x00000000, + 0x01a8, 0x00005fff, + 0x01ac, 0x00000000, + 0x01b0, 0x00000000, + 0x01b4, 0x00000000, + 0x01b8, 0x00000000, + 0x01bc, 0x00000000, + 0x01c0, 0x02000000, + 0x01c4, 0x00000000, + 0x01c8, 0x00000000, + 0x01cc, 0xe6407800, + 0x01d0, 0x78000000, + 0x01d4, 0x010408d2, + 0x01d8, 0x00000000, + 0x01dc, 0x00000000, + 0x01e4, 0x00005fff, + 0x01e8, 0x00000000, + 0x01ec, 0x00000000, + 0x01f0, 0x00000000, + 0x01f4, 0x00000000, + 0x01f8, 0x00000000, + 0x01fc, 0x02000000, + 0x0200, 0x00000000, + 0x0204, 0x00000000, + 0x0208, 0x00000008, + 0x020c, 0x0000000d, + 0x41fc, 0x00000000, + 0x4400, 0x00000000, + 0x4410, 0x00000000, + 0x4420, 0x00000000, + 0x4430, 0x00000000, + 0x4440, 0x00000000, + 0x4450, 0x00000000, + 0x4460, 0x00000000, + 0x4470, 0x00000000, + 0xf080, 0x00003018, + 0xf084, 0x00000fff, + 0xf800, 0x00000000, + 0xf804, 0x00002e00, + 0xf8d0, 0x00000001, + 0xf8d4, 0x00000000, + 0xff00, 0x00000301, + 0xff0c, 0x01000000, + 0xffe0, 0x00000000, + 0xfff4, 0x00004011, + 0x0090, 0x00000000, + 0x0094, 0x00000000, + 0x0098, 0x00000000, + 0x009c, 0x3f3f3f3f, +}; +#endif diff --git a/include/linux/input/aw9610x_reg.h b/include/linux/input/aw9610x_reg.h new file mode 100644 index 000000000000..7d6f37da3ae1 --- /dev/null +++ b/include/linux/input/aw9610x_reg.h @@ -0,0 +1,350 @@ +#ifndef _AW9610X_REG_H_ +#define _AW9610X_REG_H_ + +/******************************************** +* Register List +********************************************/ +#define REG_SCANCTRL0 0x0000 +#define REG_SCANCTRL1 0x0004 +#define REG_SCANCTRL2 0x0008 +#define REG_SCANCTRL3 0x000C +#define REG_AFECFG0_CH0 0x0010 +#define REG_AFECFG1_CH0 0x0014 +#define REG_AFECFG2_CH0 0x0018 +#define REG_AFECFG3_CH0 0x001C +#define REG_AFECFG4_CH0 0x0020 +#define REG_AFECFG0_CH1 0x0024 +#define REG_AFECFG1_CH1 0x0028 +#define REG_AFECFG2_CH1 0x002C +#define REG_AFECFG3_CH1 0x0030 +#define REG_AFECFG4_CH1 0x0034 +#define REG_AFECFG0_CH2 0x0038 +#define REG_AFECFG1_CH2 0x003C +#define REG_AFECFG2_CH2 0x0040 +#define REG_AFECFG3_CH2 0x0044 +#define REG_AFECFG4_CH2 0x0048 +#define REG_AFECFG0_CH3 0x004C +#define REG_AFECFG1_CH3 0x0050 +#define REG_AFECFG2_CH3 0x0054 +#define REG_AFECFG3_CH3 0x0058 +#define REG_AFECFG4_CH3 0x005C +#define REG_AFECFG0_CH4 0x0060 +#define REG_AFECFG1_CH4 0x0064 +#define REG_AFECFG2_CH4 0x0068 +#define REG_AFECFG3_CH4 0x006C +#define REG_AFECFG4_CH4 0x0070 +#define REG_AFECFG0_CH5 0x0074 +#define REG_AFECFG1_CH5 0x0078 +#define REG_AFECFG2_CH5 0x007C +#define REG_AFECFG3_CH5 0x0080 +#define REG_AFECFG4_CH5 0x0084 +#define REG_FIRMVERSION 0x0088 +#define REG_STAT0 0x0090 +#define REG_STAT1 0x0094 +#define REG_STAT2 0x0098 +#define REG_PROXINTEN 0x009C +#define REG_FILTREF0_CH0 0x00A0 +#define REG_FILTREF1_CH0 0x00A4 +#define REG_BLFILT1_CH0 0x00A8 +#define REG_BLFILT2_CH0 0x00AC +#define REG_PROXCTRL_CH0 0x00B0 +#define REG_PROXTH0_CH0 0x00B8 +#define REG_PROXTH1_CH0 0x00BC +#define REG_PROXTH2_CH0 0x00C0 +#define REG_PROXTH3_CH0 0x00C4 +#define REG_STDDET_CH0 0x00C8 +#define REG_INITPROX0_CH0 0x00CC +#define REG_INITPROX1_CH0 0x00D0 +#define REG_DATAOFFSET_CH0 0x00D4 +#define REG_CORRTARDATA_CH0 0x00D8 +#define REG_FILTREF0_CH1 0x00DC +#define REG_FILTREF1_CH1 0x00E0 +#define REG_BLFILT1_CH1 0x00E4 +#define REG_BLFILT2_CH1 0x00E8 +#define REG_PROXCTRL_CH1 0x00EC +#define REG_PROXTH0_CH1 0x00F4 +#define REG_PROXTH1_CH1 0x00F8 +#define REG_PROXTH2_CH1 0x00FC +#define REG_PROXTH3_CH1 0x0100 +#define REG_STDDET_CH1 0x0104 +#define REG_INITPROX0_CH1 0x0108 +#define REG_INITPROX1_CH1 0x010C +#define REG_DATAOFFSET_CH1 0x0110 +#define REG_CORRTARDATA_CH1 0x0114 +#define REG_FILTREF0_CH2 0x0118 +#define REG_FILTREF1_CH2 0x011C +#define REG_BLFILT1_CH2 0x0120 +#define REG_BLFILT2_CH2 0x0124 +#define REG_PROXCTRL_CH2 0x0128 +#define REG_PROXTH0_CH2 0x0130 +#define REG_PROXTH1_CH2 0x0134 +#define REG_PROXTH2_CH2 0x0138 +#define REG_PROXTH3_CH2 0x013C +#define REG_STDDET_CH2 0x0140 +#define REG_INITPROX0_CH2 0x0144 +#define REG_INITPROX1_CH2 0x0148 +#define REG_DATAOFFSET_CH2 0x014C +#define REG_BASELINE_CH2 0x0150 +#define REG_FILTREF0_CH3 0x0154 +#define REG_FILTREF1_CH3 0x0158 +#define REG_BLFILT1_CH3 0x015C +#define REG_BLFILT2_CH3 0x0160 +#define REG_PROXCTRL_CH3 0x0164 +#define REG_PROXTH0_CH3 0x016C +#define REG_PROXTH1_CH3 0x0170 +#define REG_PROXTH2_CH3 0x0174 +#define REG_PROXTH3_CH3 0x0178 +#define REG_STDDET_CH3 0x017C +#define REG_INITPROX0_CH3 0x0180 +#define REG_INITPROX1_CH3 0x0184 +#define REG_DATAOFFSET_CH3 0x0188 +#define REG_CORRTARDATA_CH3 0x018C +#define REG_FILTREF0_CH4 0x0190 +#define REG_FILTREF1_CH4 0x0194 +#define REG_BLFILT1_CH4 0x0198 +#define REG_BLFILT2_CH4 0x019C +#define REG_PROXCTRL_CH4 0x01A0 +#define REG_PROXTH0_CH4 0x01A8 +#define REG_PROXTH1_CH4 0x01AC +#define REG_PROXTH2_CH4 0x01B0 +#define REG_PROXTH3_CH4 0x01B4 +#define REG_STDDET_CH4 0x01B8 +#define REG_INITPROX0_CH4 0x01BC +#define REG_INITPROX1_CH4 0x01C0 +#define REG_DATAOFFSET_CH4 0x01C4 +#define REG_CORRTARDATA_CH4 0x01C8 +#define REG_FILTREF0_CH5 0x01CC +#define REG_FILTREF1_CH5 0x01D0 +#define REG_BLFILT1_CH5 0x01D4 +#define REG_BLFILT2_CH5 0x01D8 +#define REG_PROXCTRL_CH5 0x01DC +#define REG_PROXTH0_CH5 0x01E4 +#define REG_PROXTH1_CH5 0x01E8 +#define REG_PROXTH2_CH5 0x01EC +#define REG_PROXTH3_CH5 0x01F0 +#define REG_STDDET_CH5 0x01F4 +#define REG_INITPROX0_CH5 0x01F8 +#define REG_INITPROX1_CH5 0x01FC +#define REG_DATAOFFSET_CH5 0x0200 +#define REG_CORRTARDATA_CH5 0x0204 +#define REG_REFACFG 0x0208 +#define REG_REFBCFG 0x020C +#define REG_VALID_CH0 0x0210 +#define REG_VALID_CH1 0x0214 +#define REG_VALID_CH2 0x0218 +#define REG_VALID_CH3 0x021C +#define REG_VALID_CH4 0x0220 +#define REG_VALID_CH5 0x0224 +#define REG_BASELINE_CH0 0x0228 +#define REG_DIFF_CH0 0x0240 +#define REG_AMID_CH0 0x0258 +#define REG_AMAD_CH0 0x0270 +#define REG_AAMD_CH0 0x0288 +#define REG_SMID_CH0 0x02A0 +#define REG_SMAD_CH0 0x02B8 +#define REG_RAW_CH0 0x02D0 +#define REG_LOPT_CH0 0x02E8 +#define REG_VAPD_CH0 0x0300 +#define REG_VADD_CH0 0x0318 +#define REG_PSCBD_CH0 0x0330 +#define REG_AFD_CH0 0x0348 +#define REG_EEDA0 0x0408 +#define REG_EEDA1 0x040C +#define REG_DARE 0x41FC +#define REG_DICR 0x4400 +#define REG_ITRE 0x4410 +#define REG_PUUC 0x4420 +#define REG_PUDN 0x4430 +#define REG_IEB 0x4440 +#define REG_PSR0 0x4450 +#define REG_PSR1 0x4460 +#define REG_CONRG 0x4470 +#define REG_CMD 0xF008 +#define REG_HOSTIRQSRC 0xF080 +#define REG_HOSTIRQEN 0xF084 +#define REG_MCR 0xF800 +#define REG_ACR 0xF804 +#define REG_HOSTCTRL1 0xFF00 +#define REG_HOSTCTRL2 0xFF0C +#define REG_CHIP_ID 0xFF10 +#define REG_ATN 0xFFE0 +#define REG_HAAPEREN 0xFFF4 +/******************************************** + * Register Access + *******************************************/ +#define REG_NONE_ACCESS 0 +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) + +/************************************** + * define platform data + * + **************************************/ +struct aw_reg_data { + unsigned int reg; + unsigned int val; + unsigned char rw; +}; + +static const struct aw_reg_data aw9610x_reg_access[] = { + {.reg = REG_SCANCTRL0, .rw = REG_RD_ACCESS|REG_WR_ACCESS,}, + {.reg = REG_SCANCTRL1, .rw = REG_RD_ACCESS|REG_WR_ACCESS,}, + {.reg = REG_SCANCTRL2, .rw = REG_RD_ACCESS|REG_WR_ACCESS,}, + {.reg = REG_SCANCTRL3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG1_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG2_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG3_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG4_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG1_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG2_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG3_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG4_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG0_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG1_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG2_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG3_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG4_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG0_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG1_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG2_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG3_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG4_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG0_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG1_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG2_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG3_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG4_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG0_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG1_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG2_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG3_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_AFECFG4_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_STAT0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_STAT1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_STAT2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXINTEN, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_FILTREF0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_FILTREF1_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_BLFILT1_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_BLFILT2_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXCTRL_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH1_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH2_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH3_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_STDDET_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_INITPROX0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_INITPROX1_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_DATAOFFSET_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_CORRTARDATA_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_FILTREF0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_FILTREF1_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_BLFILT1_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_BLFILT2_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXCTRL_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH1_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH2_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH3_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_STDDET_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_INITPROX0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_INITPROX1_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_DATAOFFSET_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_CORRTARDATA_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_FILTREF0_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_FILTREF1_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_BLFILT1_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_BLFILT2_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXCTRL_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH0_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH1_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH2_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH3_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_STDDET_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_INITPROX0_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_INITPROX1_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_DATAOFFSET_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_BASELINE_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_FILTREF0_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_FILTREF1_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_BLFILT1_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_BLFILT2_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXCTRL_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH0_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH1_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH2_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH3_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_STDDET_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_INITPROX0_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_INITPROX1_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_DATAOFFSET_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_CORRTARDATA_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_FILTREF0_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_FILTREF1_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_BLFILT1_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_BLFILT2_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXCTRL_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH0_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH1_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH2_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH3_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_STDDET_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_INITPROX0_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_INITPROX1_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_DATAOFFSET_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_CORRTARDATA_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_FILTREF0_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_FILTREF1_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_BLFILT1_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_BLFILT2_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXCTRL_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH0_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH1_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH2_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PROXTH3_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_STDDET_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_INITPROX0_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_INITPROX1_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_DATAOFFSET_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_CORRTARDATA_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_REFACFG, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_REFBCFG, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_VALID_CH0, .rw = REG_RD_ACCESS,}, + {.reg = REG_BASELINE_CH0, .rw = REG_RD_ACCESS,}, + {.reg = REG_DIFF_CH0, .rw = REG_RD_ACCESS,}, + {.reg = REG_AMID_CH0, .rw = REG_RD_ACCESS,}, + {.reg = REG_AMAD_CH0, .rw = REG_RD_ACCESS,}, + {.reg = REG_AAMD_CH0, .rw = REG_RD_ACCESS,}, + {.reg = REG_SMID_CH0, .rw = REG_RD_ACCESS,}, + {.reg = REG_SMAD_CH0, .rw = REG_RD_ACCESS,}, + {.reg = REG_RAW_CH0, .rw = REG_RD_ACCESS,}, + {.reg = REG_LOPT_CH0, .rw = REG_RD_ACCESS,}, + {.reg = REG_VAPD_CH0, .rw = REG_RD_ACCESS,}, + {.reg = REG_VADD_CH0, .rw = REG_RD_ACCESS,}, + {.reg = REG_PSCBD_CH0, .rw = REG_RD_ACCESS,}, + {.reg = REG_AFD_CH0, .rw = REG_RD_ACCESS,}, + {.reg = REG_EEDA0, .rw = REG_RD_ACCESS,}, + {.reg = REG_EEDA1, .rw = REG_RD_ACCESS,}, + {.reg = REG_DARE, .rw = REG_RD_ACCESS,}, + {.reg = REG_DICR, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_ITRE, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PUUC, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PUDN, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_IEB, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PSR0, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_PSR1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_CONRG, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_HOSTIRQSRC, .rw = REG_RD_ACCESS,}, + {.reg = REG_HOSTIRQEN, .rw = REG_WR_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_MCR, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_ACR, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_HOSTCTRL1, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_HOSTCTRL2, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_CHIP_ID, .rw = REG_RD_ACCESS,}, + {.reg = REG_ATN, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, + {.reg = REG_HAAPEREN, .rw = REG_RD_ACCESS | REG_WR_ACCESS,}, +}; + +#endif diff --git a/include/linux/input/aw_bin_parse.h b/include/linux/input/aw_bin_parse.h new file mode 100644 index 000000000000..10e8703aa3d0 --- /dev/null +++ b/include/linux/input/aw_bin_parse.h @@ -0,0 +1,73 @@ +#ifndef __AW_BIN_PARSE_H__ +#define __AW_BIN_PARSE_H__ + +#define NULL ((void *)0) +#define GET_32_DATA(w, x, y, z) ((unsigned int)(((w) << 24) | ((x) << 16) | ((y) << 8) | (z))) +#define BIN_NUM_MAX 100 +#define HEADER_LEN 60 +/********************************************************* + * + * header information + * + ********************************************************/ +enum bin_header_version_enum { + HEADER_VERSION_1_0_0 = 0x01000000, +}; + +enum data_type_enum { + DATA_TYPE_REGISTER = 0x00000000, + DATA_TYPE_DSP_REG = 0x00000010, + DATA_TYPE_DSP_CFG = 0x00000011, + DATA_TYPE_SOC_REG = 0x00000020, + DATA_TYPE_SOC_APP = 0x00000021, + DATA_TYPE_MULTI_BINS = 0x00002000, +}; + +enum data_version_enum { + DATA_VERSION_V1 = 0X00000001, /*default little edian */ + DATA_VERSION_MAX, +}; + +struct bin_header_info { + unsigned int header_len; /* Frame header length */ + unsigned int check_sum; /* Frame header information-Checksum */ + unsigned int header_ver; /* Frame header information-Frame header version */ + unsigned int bin_data_type; /* Frame header information-Data type */ + unsigned int bin_data_ver; /* Frame header information-Data version */ + unsigned int bin_data_len; /* Frame header information-Data length */ + unsigned int ui_ver; /* Frame header information-ui version */ + unsigned char chip_type[20]; /* Frame header information-chip type */ + unsigned int reg_byte_len; /* Frame header information-reg byte len */ + unsigned int data_byte_len; /* Frame header information-data byte len */ + unsigned int device_addr; /* Frame header information-device addr */ + unsigned int valid_data_len; /* Length of valid data obtained after parsing */ + unsigned int valid_data_addr; /* The offset address of the valid data obtained after parsing relative to info */ + + unsigned int reg_num; /* The number of registers obtained after parsing */ + unsigned int reg_data_byte_len; /* The byte length of the register obtained after parsing */ + unsigned int download_addr; /* The starting address or download address obtained after parsing */ + unsigned int app_version; /* The software version number obtained after parsing */ +}; + +/************************************************************ +* +* function define +* +************************************************************/ +struct bin_container { + unsigned int len; /* The size of the bin file obtained from the firmware */ + unsigned char data[]; /* Store the bin file obtained from the firmware */ +}; + +struct aw_bin { + char *p_addr; /* Offset pointer (backward offset pointer to obtain frame header information and important information) */ + unsigned int all_bin_parse_num; /* The number of all bin files */ + unsigned int multi_bin_parse_num; /* The number of single bin files */ + unsigned int single_bin_parse_num; /* The number of multiple bin files */ + struct bin_header_info header_info[BIN_NUM_MAX]; /* Frame header information and other important data obtained after parsing */ + struct bin_container info; /* Obtained bin file data that needs to be parsed */ +}; + +extern int aw_parsing_bin_file(struct aw_bin *bin); +int aw_parse_bin_header_1_0_0(struct aw_bin *bin); +#endif diff --git a/include/linux/input/awinic/aw9610x.h b/include/linux/input/awinic/aw9610x.h new file mode 100644 index 000000000000..01f5b6dc2a69 --- /dev/null +++ b/include/linux/input/awinic/aw9610x.h @@ -0,0 +1,582 @@ +#ifndef _AW9610X_H_ +#define _AW9610X_H_ +#include "aw_sar_type.h" + +//#define AW9610X_TVS_ABNORMAL_CAIL +#define AW9610X_AOT_SCAN_OVER_CNT (32) + +#define AW9610X_CHIP_ID (0xa961) +#define AW9610x_DATA_PROCESS_FACTOR (1024) +#define AW_CHIP_AW9610XA (0x03000b00) +#define AW9610X_CPU_WORK_MASK (1) + +#define AW9610X_SAR_VCC_MIN_UV (1700000) +#define AW9610X_SAR_VCC_MAX_UV (3600000) + +#define AW_REG_IRQEN_CLOSE (0) +#define AW_REG_IRQSRC_AOT_OVER_BIT (3) +#define REG_IRQSRC_SCAN_OVER_BIT (4) +#define REG_REG_WST_SLEEP_MODE (0x3) +#define AW9610X_AOT_OVER_DELAY_MAX_MS (6000) +#define AW9610X_AOT_MASK (0x3f) +#define AW9610X_AOT_BIT (8) +#define AW9610X_REG_OFFSET_STEP (4) + +enum aw9610x_sar_vers { + AW9610X = 2, + AW9610XA = 6, + AW9610XB = 0xa, +}; + +enum aw9610x_operation_mode { + AW9610X_ACTIVE_MODE = 1, + AW9610X_SLEEP_MODE, + AW9610X_DEEPSLEEP_MODE, + AW9610XB_DEEPSLEEP_MODE, +}; + +/********************************************** +*spereg addr offset +**********************************************/ +enum aw9610x_spereg_addr_offset { + AW_CL1SPE_CALI_OS = 20, + AW_CL1SPE_DEAL_OS = 60, + AW_CL2SPE_CALI_OS = 4, + AW_CL2SPE_DEAL_OS = 4, +}; + + +/********************************************** + *the flag of i2c read/write + **********************************************/ +enum aw9610x_function_flag { + AW9610X_FUNC_OFF, + AW9610X_FUNC_ON, +}; + +/********************************************** +* mutiple sar define +**********************************************/ +enum aw9610x_multiple_sar { + AW_SAR0, + AW_SAR1, + AW_SAR_MAX, +}; + +#define AW9610X_CHANNEL_MAX (6) + +enum aw9610x_irq_trigger_position { + AW9610X_FAR, + AW9610X_TRIGGER_TH0, + AW9610X_TRIGGER_TH1 = 0x03, + AW9610X_TRIGGER_TH2 = 0x07, + AW9610X_TRIGGER_TH3 = 0x0f, +}; + +struct aw_i2c_package { + uint8_t addr_bytes; + uint8_t data_bytes; + uint8_t reg_num; + uint8_t init_addr[4]; + uint8_t *p_reg_data; +}; + +struct aw9610x { + uint8_t vers; + uint8_t channel; + uint32_t irq_status; + uint8_t chip_name[9]; + uint8_t chip_type[9]; + bool satu_release; + + struct aw_i2c_package aw_i2c_package; + + uint8_t satu_flag[6]; + uint32_t satu_data[6]; +}; + +/******************************************** +* Register List +********************************************/ +#define AFE_BASE_ADDR (0x0000) +#define DSP_BASE_ADDR (0x0000) +#define STAT_BASE_ADDR (0x0000) +#define SFR_BASE_ADDR (0x0000) +#define DATA_BASE_ADDR (0x0000) +/* registers list */ +//AW96105A_UI_REGISTER_AFE MAP +#define REG_SCANCTRL0 ((0x0000) + AFE_BASE_ADDR) +#define REG_SCANCTRL1 ((0x0004) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH0 ((0x0010) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH0 ((0x0014) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH0 ((0x001C) + AFE_BASE_ADDR) +#define REG_AFECFG4_CH0 ((0x0020) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH1 ((0x0024) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH1 ((0x0028) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH1 ((0x0030) + AFE_BASE_ADDR) +#define REG_AFECFG4_CH1 ((0x0034) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH2 ((0x0038) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH2 ((0x003C) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH2 ((0x0044) + AFE_BASE_ADDR) +#define REG_AFECFG4_CH2 ((0x0048) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH3 ((0x004C) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH3 ((0x0050) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH3 ((0x0058) + AFE_BASE_ADDR) +#define REG_AFECFG4_CH3 ((0x005C) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH4 ((0x0060) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH4 ((0x0064) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH4 ((0x006C) + AFE_BASE_ADDR) +#define REG_AFECFG4_CH4 ((0x0070) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH5 ((0x0074) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH5 ((0x0078) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH5 ((0x0080) + AFE_BASE_ADDR) +#define REG_AFECFG4_CH5 ((0x0084) + AFE_BASE_ADDR) + +/* registers list */ +//AW96105A_UI_REGISTER_DSP MAP +#define REG_DSPCFG0_CH0 ((0x00A0) + DSP_BASE_ADDR) +#define REG_DSPCFG1_CH0 ((0x00A4) + DSP_BASE_ADDR) +#define REG_BLFILT_CH0 ((0x00A8) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH0 ((0x00B0) + DSP_BASE_ADDR) +#define REG_BLRSTRNG_CH0 ((0x00B4) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH0 ((0x00B8) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH0 ((0x00BC) + DSP_BASE_ADDR) +#define REG_PROXTH2_CH0 ((0x00C0) + DSP_BASE_ADDR) +#define REG_PROXTH3_CH0 ((0x00C4) + DSP_BASE_ADDR) +#define REG_STDDET_CH0 ((0x00C8) + DSP_BASE_ADDR) +#define REG_INITPROX0_CH0 ((0x00CC) + DSP_BASE_ADDR) +#define REG_INITPROX1_CH0 ((0x00D0) + DSP_BASE_ADDR) +#define REG_DATAOFFSET_CH0 ((0x00D4) + DSP_BASE_ADDR) +#define REG_AOTTAR_CH0 ((0x00D8) + DSP_BASE_ADDR) +#define REG_DSPCFG0_CH1 ((0x00DC) + DSP_BASE_ADDR) +#define REG_DSPCFG1_CH1 ((0x00E0) + DSP_BASE_ADDR) +#define REG_BLFILT_CH1 ((0x00E4) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH1 ((0x00EC) + DSP_BASE_ADDR) +#define REG_BLRSTRNG_CH1 ((0x00F0) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH1 ((0x00F4) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH1 ((0x00F8) + DSP_BASE_ADDR) +#define REG_PROXTH2_CH1 ((0x00FC) + DSP_BASE_ADDR) +#define REG_PROXTH3_CH1 ((0x0100) + DSP_BASE_ADDR) +#define REG_STDDET_CH1 ((0x0104) + DSP_BASE_ADDR) +#define REG_INITPROX0_CH1 ((0x0108) + DSP_BASE_ADDR) +#define REG_INITPROX1_CH1 ((0x010C) + DSP_BASE_ADDR) +#define REG_RAWOFFSET_CH1 ((0x0110) + DSP_BASE_ADDR) +#define REG_AOTTAR_CH1 ((0x0114) + DSP_BASE_ADDR) +#define REG_DSPCFG0_CH2 ((0x0118) + DSP_BASE_ADDR) +#define REG_DSPCFG1_CH2 ((0x011C) + DSP_BASE_ADDR) +#define REG_BLFILT_CH2 ((0x0120) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH2 ((0x0128) + DSP_BASE_ADDR) +#define REG_BLRSTRNG_CH2 ((0x012C) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH2 ((0x0130) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH2 ((0x0134) + DSP_BASE_ADDR) +#define REG_PROXTH2_CH2 ((0x0138) + DSP_BASE_ADDR) +#define REG_PROXTH3_CH2 ((0x013C) + DSP_BASE_ADDR) +#define REG_STDDET_CH2 ((0x0140) + DSP_BASE_ADDR) +#define REG_INITPROX0_CH2 ((0x0144) + DSP_BASE_ADDR) +#define REG_INITPROX1_CH2 ((0x0148) + DSP_BASE_ADDR) +#define REG_DATAOFFSET_CH2 ((0x014C) + DSP_BASE_ADDR) +#define REG_AOTTAR_CH2 ((0x0150) + DSP_BASE_ADDR) +#define REG_DSPCFG0_CH3 ((0x0154) + DSP_BASE_ADDR) +#define REG_DSPCFG1_CH3 ((0x0158) + DSP_BASE_ADDR) +#define REG_BLFILT_CH3 ((0x015C) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH3 ((0x0164) + DSP_BASE_ADDR) +#define REG_BLRSTRNG_CH3 ((0x0168) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH3 ((0x016C) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH3 ((0x0170) + DSP_BASE_ADDR) +#define REG_PROXTH2_CH3 ((0x0174) + DSP_BASE_ADDR) +#define REG_PROXTH3_CH3 ((0x0178) + DSP_BASE_ADDR) +#define REG_STDDET_CH3 ((0x017C) + DSP_BASE_ADDR) +#define REG_INITPROX0_CH3 ((0x0180) + DSP_BASE_ADDR) +#define REG_INITPROX1_CH3 ((0x0184) + DSP_BASE_ADDR) +#define REG_DATAOFFSET_CH3 ((0x0188) + DSP_BASE_ADDR) +#define REG_AOTTAR_CH3 ((0x018C) + DSP_BASE_ADDR) +#define REG_DSPCFG0_CH4 ((0x0190) + DSP_BASE_ADDR) +#define REG_DSPCFG1_CH4 ((0x0194) + DSP_BASE_ADDR) +#define REG_BLFILT_CH4 ((0x0198) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH4 ((0x01A0) + DSP_BASE_ADDR) +#define REG_BLRSTRNG_CH4 ((0x01A4) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH4 ((0x01A8) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH4 ((0x01AC) + DSP_BASE_ADDR) +#define REG_PROXTH2_CH4 ((0x01B0) + DSP_BASE_ADDR) +#define REG_PROXTH3_CH4 ((0x01B4) + DSP_BASE_ADDR) +#define REG_STDDET_CH4 ((0x01B8) + DSP_BASE_ADDR) +#define REG_INITPROX0_CH4 ((0x01BC) + DSP_BASE_ADDR) +#define REG_INITPROX1_CH4 ((0x01C0) + DSP_BASE_ADDR) +#define REG_DATAOFFSET_CH4 ((0x01C4) + DSP_BASE_ADDR) +#define REG_AOTTAR_CH4 ((0x01C8) + DSP_BASE_ADDR) +#define REG_DSPCFG0_CH5 ((0x01CC) + DSP_BASE_ADDR) +#define REG_DSPCFG1_CH5 ((0x01D0) + DSP_BASE_ADDR) +#define REG_BLFILT_CH5 ((0x01D4) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH5 ((0x01DC) + DSP_BASE_ADDR) +#define REG_BLRSTRNG_CH5 ((0x01E0) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH5 ((0x01E4) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH5 ((0x01E8) + DSP_BASE_ADDR) +#define REG_PROXTH2_CH5 ((0x01EC) + DSP_BASE_ADDR) +#define REG_PROXTH3_CH5 ((0x01F0) + DSP_BASE_ADDR) +#define REG_STDDET_CH5 ((0x01F4) + DSP_BASE_ADDR) +#define REG_INITPROX0_CH5 ((0x01F8) + DSP_BASE_ADDR) +#define REG_INITPROX1_CH5 ((0x01FC) + DSP_BASE_ADDR) +#define REG_DATAOFFSET_CH5 ((0x0200) + DSP_BASE_ADDR) +#define REG_AOTTAR_CH5 ((0x0204) + DSP_BASE_ADDR) +#define REG_ATCCR0 ((0x0208) + DSP_BASE_ADDR) +#define REG_ATCCR1 ((0x020C) + DSP_BASE_ADDR) + +/* registers list */ +//AW96105A_UI_REGISTER_STAT MAP +#define REG_FWVER ((0x0088) + STAT_BASE_ADDR) +#define REG_WST ((0x008C) + STAT_BASE_ADDR) +#define REG_STAT0 ((0x0090) + STAT_BASE_ADDR) +#define REG_STAT1 ((0x0094) + STAT_BASE_ADDR) +#define REG_STAT2 ((0x0098) + STAT_BASE_ADDR) +#define REG_CHINTEN ((0x009C) + STAT_BASE_ADDR) + +/* registers list */ +//AW96105A_UI_REGISTER_SFR MAP +#define REG_CMD ((0xF008) + SFR_BASE_ADDR) +#define REG_IRQSRC ((0xF080) + SFR_BASE_ADDR) +#define REG_IRQEN ((0xF084) + SFR_BASE_ADDR) +#define REG_I2CADDR ((0xF0F0) + SFR_BASE_ADDR) +#define REG_OSCEN ((0xFF00) + SFR_BASE_ADDR) +#define REG_RESET ((0xFF0C) + SFR_BASE_ADDR) +#define REG_CHIPID ((0xFF10) + SFR_BASE_ADDR) + +/* registers list */ +//AW96105A_UI_REGISTER_DATA MAP +#define REG_COMP_CH0 ((0x0210) + DATA_BASE_ADDR) +#define REG_COMP_CH1 ((0x0214) + DATA_BASE_ADDR) +#define REG_COMP_CH2 ((0x0218) + DATA_BASE_ADDR) +#define REG_COMP_CH3 ((0x021C) + DATA_BASE_ADDR) +#define REG_COMP_CH4 ((0x0220) + DATA_BASE_ADDR) +#define REG_COMP_CH5 ((0x0224) + DATA_BASE_ADDR) +#define REG_BASELINE_CH0 ((0x0228) + DATA_BASE_ADDR) +#define REG_BASELINE_CH1 ((0x022C) + DATA_BASE_ADDR) +#define REG_BASELINE_CH2 ((0x0230) + DATA_BASE_ADDR) +#define REG_BASELINE_CH3 ((0x0234) + DATA_BASE_ADDR) +#define REG_BASELINE_CH4 ((0x0238) + DATA_BASE_ADDR) +#define REG_BASELINE_CH5 ((0x023C) + DATA_BASE_ADDR) +#define REG_DIFF_CH0 ((0x0240) + DATA_BASE_ADDR) +#define REG_DIFF_CH1 ((0x0244) + DATA_BASE_ADDR) +#define REG_DIFF_CH2 ((0x0248) + DATA_BASE_ADDR) +#define REG_DIFF_CH3 ((0x024C) + DATA_BASE_ADDR) +#define REG_DIFF_CH4 ((0x0250) + DATA_BASE_ADDR) +#define REG_DIFF_CH5 ((0x0254) + DATA_BASE_ADDR) +#define REG_FWVER2 ((0x0410) + DATA_BASE_ADDR) + +struct aw_reg_data { + unsigned char rw; + unsigned short reg; +}; +/******************************************** + * Register Access + *******************************************/ +#define REG_NONE_ACCESS (0) +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) + +static const struct aw_reg_data g_aw9610x_reg_access[] = { + //AFE MAP + { .reg = REG_SCANCTRL0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_SCANCTRL1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG4_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG4_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG4_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG4_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG4_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG4_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + + //DSP MAP + { .reg = REG_DSPCFG0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_DSPCFG1_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILT_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLRSTRNG_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH2_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH3_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_STDDET_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX1_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_DATAOFFSET_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AOTTAR_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_DSPCFG0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_DSPCFG1_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILT_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLRSTRNG_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH2_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH3_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_STDDET_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX1_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_RAWOFFSET_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AOTTAR_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_DSPCFG0_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_DSPCFG1_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILT_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLRSTRNG_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH2_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH3_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_STDDET_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX0_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX1_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_DATAOFFSET_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AOTTAR_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_DSPCFG0_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_DSPCFG1_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILT_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLRSTRNG_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH2_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH3_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_STDDET_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX0_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX1_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_DATAOFFSET_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AOTTAR_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_DSPCFG0_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_DSPCFG1_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILT_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLRSTRNG_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH2_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH3_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_STDDET_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX0_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX1_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_DATAOFFSET_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AOTTAR_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_DSPCFG0_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_DSPCFG1_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILT_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLRSTRNG_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH2_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH3_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_STDDET_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX0_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX1_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_DATAOFFSET_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AOTTAR_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_ATCCR0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_ATCCR1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + + //STAT MAP + { .reg = REG_FWVER, .rw = REG_RD_ACCESS, }, + { .reg = REG_WST, .rw = REG_RD_ACCESS, }, + { .reg = REG_STAT0, .rw = REG_RD_ACCESS, }, + { .reg = REG_STAT1, .rw = REG_RD_ACCESS, }, + { .reg = REG_STAT2, .rw = REG_RD_ACCESS, }, + { .reg = REG_CHINTEN, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + + //SFR MAP + { .reg = REG_CMD, .rw = REG_NONE_ACCESS, }, + { .reg = REG_IRQSRC, .rw = REG_RD_ACCESS, }, + { .reg = REG_IRQEN, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_I2CADDR, .rw = REG_RD_ACCESS, }, + { .reg = REG_OSCEN, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_RESET, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_CHIPID, .rw = REG_RD_ACCESS, }, + + //DATA MAP + { .reg = REG_COMP_CH0, .rw = REG_RD_ACCESS, }, + { .reg = REG_COMP_CH1, .rw = REG_RD_ACCESS, }, + { .reg = REG_COMP_CH2, .rw = REG_RD_ACCESS, }, + { .reg = REG_COMP_CH3, .rw = REG_RD_ACCESS, }, + { .reg = REG_COMP_CH4, .rw = REG_RD_ACCESS, }, + { .reg = REG_COMP_CH5, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH0, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH1, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH2, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH3, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH4, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH5, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH0, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH1, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH2, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH3, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH4, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH5, .rw = REG_RD_ACCESS, }, + { .reg = REG_FWVER2, .rw = REG_RD_ACCESS, }, +}; + + +/****************************************************** +* Register Detail +******************************************************/ +const static uint32_t aw9610x_reg_default[] = { + 0x0000, 0x00003f3f, + 0x0004, 0x00000064, + 0x0008, 0x0017c11e, + 0x000c, 0x05000000, + 0x0010, 0x00093ffd, + 0x0014, 0x19240009, + 0x0018, 0xd81c0207, + 0x001c, 0xff000000, + 0x0020, 0x00241900, + 0x0024, 0x00093ff7, + 0x0028, 0x58020009, + 0x002c, 0xd81c0207, + 0x0030, 0xff000000, + 0x0034, 0x00025800, + 0x0038, 0x00093fdf, + 0x003c, 0x7d3b0009, + 0x0040, 0xd81c0207, + 0x0044, 0xff000000, + 0x0048, 0x003b7d00, + 0x004c, 0x00093f7f, + 0x0050, 0xe9310009, + 0x0054, 0xd81c0207, + 0x0058, 0xff000000, + 0x005c, 0x0031e900, + 0x0060, 0x00093dff, + 0x0064, 0x1a0c0009, + 0x0068, 0xd81c0207, + 0x006c, 0xff000000, + 0x0070, 0x000c1a00, + 0x0074, 0x80093fff, + 0x0078, 0x043d0009, + 0x007c, 0xd81c0207, + 0x0080, 0xff000000, + 0x0084, 0x003d0400, + 0x00a0, 0xe6400000, + 0x00a4, 0x00000000, + 0x00a8, 0x010408d2, + 0x00ac, 0x00000000, + 0x00b0, 0x00000000, + 0x00b8, 0x00005fff, + 0x00bc, 0x00000000, + 0x00c0, 0x00000000, + 0x00c4, 0x00000000, + 0x00c8, 0x00000000, + 0x00cc, 0x00000000, + 0x00d0, 0x00000000, + 0x00d4, 0x00000000, + 0x00d8, 0x00000000, + 0x00dc, 0xe6447800, + 0x00e0, 0x78000000, + 0x00e4, 0x010408d2, + 0x00e8, 0x00000000, + 0x00ec, 0x00000000, + 0x00f4, 0x00005fff, + 0x00f8, 0x00000000, + 0x00fc, 0x00000000, + 0x0100, 0x00000000, + 0x0104, 0x00000000, + 0x0108, 0x00000000, + 0x010c, 0x02000000, + 0x0110, 0x00000000, + 0x0114, 0x00000000, + 0x0118, 0xe6447800, + 0x011c, 0x78000000, + 0x0120, 0x010408d2, + 0x0124, 0x00000000, + 0x0128, 0x00000000, + 0x0130, 0x00005fff, + 0x0134, 0x00000000, + 0x0138, 0x00000000, + 0x013c, 0x00000000, + 0x0140, 0x00000000, + 0x0144, 0x00000000, + 0x0148, 0x02000000, + 0x014c, 0x00000000, + 0x0150, 0x00000000, + 0x0154, 0xe6447800, + 0x0158, 0x78000000, + 0x015c, 0x010408d2, + 0x0160, 0x00000000, + 0x0164, 0x00000000, + 0x016c, 0x00005fff, + 0x0170, 0x00000000, + 0x0174, 0x00000000, + 0x0178, 0x00000000, + 0x017c, 0x00000000, + 0x0180, 0x00000000, + 0x0184, 0x02000000, + 0x0188, 0x00000000, + 0x018c, 0x00000000, + 0x0190, 0xe6447800, + 0x0194, 0x78000000, + 0x0198, 0x010408d2, + 0x019c, 0x00000000, + 0x01a0, 0x00000000, + 0x01a8, 0x00005fff, + 0x01ac, 0x00000000, + 0x01b0, 0x00000000, + 0x01b4, 0x00000000, + 0x01b8, 0x00000000, + 0x01bc, 0x00000000, + 0x01c0, 0x02000000, + 0x01c4, 0x00000000, + 0x01c8, 0x00000000, + 0x01cc, 0xe6407800, + 0x01d0, 0x78000000, + 0x01d4, 0x010408d2, + 0x01d8, 0x00000000, + 0x01dc, 0x00000000, + 0x01e4, 0x00005fff, + 0x01e8, 0x00000000, + 0x01ec, 0x00000000, + 0x01f0, 0x00000000, + 0x01f4, 0x00000000, + 0x01f8, 0x00000000, + 0x01fc, 0x02000000, + 0x0200, 0x00000000, + 0x0204, 0x00000000, + 0x0208, 0x00000008, + 0x020c, 0x0000000d, + 0x41fc, 0x00000000, + 0x4400, 0x00000000, + 0x4410, 0x00000000, + 0x4420, 0x00000000, + 0x4430, 0x00000000, + 0x4440, 0x00000000, + 0x4450, 0x00000000, + 0x4460, 0x00000000, + 0x4470, 0x00000000, + 0xf080, 0x00003018, + 0xf084, 0x00000fff, + 0xf800, 0x00000000, + 0xf804, 0x00002e00, + 0xf8d0, 0x00000001, + 0xf8d4, 0x00000000, + 0xff00, 0x00000301, + 0xff0c, 0x01000000, + 0xffe0, 0x00000000, + 0xfff4, 0x00004011, + 0x0090, 0x00000000, + 0x0094, 0x00000000, + 0x0098, 0x00000000, + 0x009c, 0x3f3f3f3f, +}; + +#endif diff --git a/include/linux/input/awinic/aw9620x.h b/include/linux/input/awinic/aw9620x.h new file mode 100644 index 000000000000..be74cb9c1d1b --- /dev/null +++ b/include/linux/input/awinic/aw9620x.h @@ -0,0 +1,453 @@ +#ifndef AW9620X_H_ +#define AW9620X_H_ +#include "aw_sar_type.h" + +#define AW9620X_CHANNEL_NUM_MAX (8) +#define AW9620X_FINE_ADJUST_STEP0 (165) +#define AW9620X_COARSE_ADJUST_STEP0 (9900) +#define AW9620X_FINE_ADJUST_STEP1 (330) +#define AW9620X_COARSE_ADJUST_STEP1 (19800) +#define AW9620X_CFG_REG_STEP (0x8c) +#define AW9620X_PARA_TIMES (10000) +#define AW9620X_PROT_STOP_WAIT_IRQ_CYCLES (100) +#define AW9620X_REG_UPDATA_DIS_MASK (0x00ffffff) +#define AW9620X_AFECFG3_CVMULTUALMOD (11) +#define AW9620X_AFECFG3_CVOFF2X (10) +#define AW9620X_REMOVE_FLOAT_COEF (1024) +#define AW9620X_REG_MCFG_DELAY_MS (20) + +#define AW9620X_REG_RSTNALL_VAL (0x3c) +#define AW9620X_POWER_ON_DELAY_MS (25) +#define AW9620X_REG_STEP (0x8c) +#define AW9620X_REMOVE_FLOAT_COEF (1024) + +#define AW9620X_SAR_VCC_MIN_UV (1700000) +#define AW9620X_SAR_VCC_MAX_UV (3600000) +#define AW9320X_SRAM_ERROR_CODE (0x1c00) + +enum aw_sar_irq_trigger_position { + AW9620X_TRIGGER_FAR, + AW9620X_TRIGGER_TH0, + AW9620X_TRIGGER_TH1 = 0x03, +}; + +enum AW9620X_CHIP_ID_VALUE { + AW96203CSR_CHIP_ID = 0x20226203, + AW96205DNR_CHIP_ID = 0x20226205, + AW96208CSR_CHIP_ID = 0x20226208, +}; + +enum AW9620X_OPERAION_MODE { + AW9620X_ACTIVE_MODE = 1, + AW9620X_SLEEP_MODE, + AW9620X_DEEPSLEEP_MODE, +}; + +/******************************************** +* Register List +********************************************/ +#define AFE_BASE_ADDR (0x1A00) +#define DSP_BASE_ADDR (0x1A00) +#define STAT_BASE_ADDR (0x1A00) +#define DATA_BASE_ADDR (0x1A00) +#define SFR_BASE_ADDR (0x0000) +/* registers list */ +//96208_0917_AFE MAP +#define REG_SCANCTRL0 ((0x0000) + AFE_BASE_ADDR) +#define REG_SCANCTRL1 ((0x0004) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH0 ((0x0040) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH0 ((0x0044) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH0 ((0x004C) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH1 ((0x00CC) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH1 ((0x00D0) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH1 ((0x00D8) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH2 ((0x0158) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH2 ((0x015C) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH2 ((0x0164) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH3 ((0x01E4) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH3 ((0x01E8) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH3 ((0x01F0) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH4 ((0x0270) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH4 ((0x0274) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH4 ((0x027C) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH5 ((0x02FC) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH5 ((0x0300) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH5 ((0x0308) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH6 ((0x0388) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH6 ((0x038C) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH6 ((0x0394) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH7 ((0x0414) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH7 ((0x0418) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH7 ((0x0420) + AFE_BASE_ADDR) + +/* registers list */ +//96208_0917_DSP MAP +#define REG_FILTCTRL0_CH0 ((0x0050) + DSP_BASE_ADDR) +#define REG_BLFILT_CH0 ((0x0078) + DSP_BASE_ADDR) +#define REG_BLERRAOT_CH0 ((0x008C) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH0 ((0x0090) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH0 ((0x0094) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH0 ((0x0098) + DSP_BASE_ADDR) +#define REG_AOTTAR_CH0 ((0x009C) + DSP_BASE_ADDR) +#define REG_FILTCTRL0_CH1 ((0x00DC) + DSP_BASE_ADDR) +#define REG_BLFILT_CH1 ((0x0104) + DSP_BASE_ADDR) +#define REG_BLERRAOT_CH1 ((0x0118) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH1 ((0x011C) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH1 ((0x0120) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH1 ((0x0124) + DSP_BASE_ADDR) +#define REG_AOTTAR_CH1 ((0x0128) + DSP_BASE_ADDR) +#define REG_FILTCTRL0_CH2 ((0x0168) + DSP_BASE_ADDR) +#define REG_BLFILT_CH2 ((0x0190) + DSP_BASE_ADDR) +#define REG_BLERRAOT_CH2 ((0x01A4) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH2 ((0x01A8) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH2 ((0x01AC) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH2 ((0x01B0) + DSP_BASE_ADDR) +#define REG_AOTTAR_CH2 ((0x01B4) + DSP_BASE_ADDR) +#define REG_FILTCTRL0_CH3 ((0x01F4) + DSP_BASE_ADDR) +#define REG_BLFILT_CH3 ((0x021C) + DSP_BASE_ADDR) +#define REG_BLERRAOT_CH3 ((0x0230) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH3 ((0x0234) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH3 ((0x0238) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH3 ((0x023C) + DSP_BASE_ADDR) +#define REG_AOTTAR_CH3 ((0x0240) + DSP_BASE_ADDR) +#define REG_FILTCTRL0_CH4 ((0x0280) + DSP_BASE_ADDR) +#define REG_BLFILT_CH4 ((0x02A8) + DSP_BASE_ADDR) +#define REG_BLERRAOT_CH4 ((0x02BC) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH4 ((0x02C0) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH4 ((0x02C4) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH4 ((0x02C8) + DSP_BASE_ADDR) +#define REG_AOTTAR_CH4 ((0x02CC) + DSP_BASE_ADDR) +#define REG_FILTCTRL0_CH5 ((0x030C) + DSP_BASE_ADDR) +#define REG_BLFILT_CH5 ((0x0334) + DSP_BASE_ADDR) +#define REG_BLERRAOT_CH5 ((0x0348) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH5 ((0x034C) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH5 ((0x0350) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH5 ((0x0354) + DSP_BASE_ADDR) +#define REG_AOTTAR_CH5 ((0x0358) + DSP_BASE_ADDR) +#define REG_FILTCTRL0_CH6 ((0x0398) + DSP_BASE_ADDR) +#define REG_BLFILT_CH6 ((0x03C0) + DSP_BASE_ADDR) +#define REG_BLERRAOT_CH6 ((0x03D4) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH6 ((0x03D8) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH6 ((0x03DC) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH6 ((0x03E0) + DSP_BASE_ADDR) +#define REG_AOTTAR_CH6 ((0x03E4) + DSP_BASE_ADDR) +#define REG_FILTCTRL0_CH7 ((0x0424) + DSP_BASE_ADDR) +#define REG_BLFILT_CH7 ((0x044C) + DSP_BASE_ADDR) +#define REG_BLERRAOT_CH7 ((0x0460) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH7 ((0x0464) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH7 ((0x0468) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH7 ((0x046C) + DSP_BASE_ADDR) +#define REG_AOTTAR_CH7 ((0x0470) + DSP_BASE_ADDR) + +/* registers list */ +//96208_0917_STAT MAP +#define REG_FWVER ((0x000C) + STAT_BASE_ADDR) +#define REG_WST ((0x0010) + STAT_BASE_ADDR) +#define REG_STAT0 ((0x0014) + STAT_BASE_ADDR) +#define REG_STAT1 ((0x0018) + STAT_BASE_ADDR) + +/* registers list */ +//96208_0917_DATA MAP +#define REG_TEMPERATURE ((0x001C) + DATA_BASE_ADDR) +#define REG_BASELINE_CH0 ((0x00AC) + DATA_BASE_ADDR) +#define REG_DIFF_CH0 ((0x00B0) + DATA_BASE_ADDR) +#define REG_BASELINE_CH1 ((0x0138) + DATA_BASE_ADDR) +#define REG_DIFF_CH1 ((0x013C) + DATA_BASE_ADDR) +#define REG_BASELINE_CH2 ((0x01C4) + DATA_BASE_ADDR) +#define REG_DIFF_CH2 ((0x01C8) + DATA_BASE_ADDR) +#define REG_BASELINE_CH3 ((0x0250) + DATA_BASE_ADDR) +#define REG_DIFF_CH3 ((0x0254) + DATA_BASE_ADDR) +#define REG_BASELINE_CH4 ((0x02DC) + DATA_BASE_ADDR) +#define REG_DIFF_CH4 ((0x02E0) + DATA_BASE_ADDR) +#define REG_BASELINE_CH5 ((0x0368) + DATA_BASE_ADDR) +#define REG_DIFF_CH5 ((0x036C) + DATA_BASE_ADDR) +#define REG_BASELINE_CH6 ((0x03F4) + DATA_BASE_ADDR) +#define REG_DIFF_CH6 ((0x03F8) + DATA_BASE_ADDR) +#define REG_BASELINE_CH7 ((0x0480) + DATA_BASE_ADDR) +#define REG_DIFF_CH7 ((0x0484) + DATA_BASE_ADDR) + +/* registers list */ +//96208_0917_SFR MAP +#define REG_IRQSRC ((0x4410) + SFR_BASE_ADDR) +#define REG_IRQEN ((0x4414) + SFR_BASE_ADDR) +#define REG_I2CDEVID ((0x4440) + SFR_BASE_ADDR) +#define REG_CHIP_ID ((0xFF00) + SFR_BASE_ADDR) +#define REG_CHIPSTAT ((0xFF04) + SFR_BASE_ADDR) +#define REG_HOSTCTRL ((0xFF10) + SFR_BASE_ADDR) +#define REG_RSTNALL ((0xFF18) + SFR_BASE_ADDR) + +//driver user +#define REG_ACCESSEN ((0xFF20) + SFR_BASE_ADDR) +#define REG_MCFG ((0x4444) + SFR_BASE_ADDR) +#define REG_ISPGO ((0x4714) + SFR_BASE_ADDR) +#define REG_PMU_CFG ((0x4820) + SFR_BASE_ADDR) +#define REG_ARRAY2_EW_EN ((0x4794) + SFR_BASE_ADDR) +#define REG_ISPADR ((0x4704) + SFR_BASE_ADDR) +#define REG_ISPWDATA ((0x4708) + SFR_BASE_ADDR) +#define REG_ISPRDATA ((0x470C) + SFR_BASE_ADDR) +#define REG_ISPCMD ((0x4710) + SFR_BASE_ADDR) +#define REG_T_RCV ((0x472C) + SFR_BASE_ADDR) +#define REG_CMD ((0x4408) + SFR_BASE_ADDR) +#define REG_BOOT_LOADER_ACTIVE ((0x4748) + SFR_BASE_ADDR) + +#define REG_OPEN_APB_ACCESS_EN (0x3c00f091) +#define REG_SET_PMU_CFG (0x6) +#define REG_SET_BTROM_EW_EN (0x5a637955) +#define REG_ISP_CMD_CONFIG (0x0c) +#define REG_SET_ISP_GO (0x1) +#define REG_ENSET_PMU_CFG (0x4) +#define REG_ACCESS_MAIN_ARR (0x05) +#define REG_SET_T_RCV (0xf0) +#define REG_SET_T_RCV_EN (0x16) +#define REG_OPEN_REG_ACCESS_EN (0x3c00ffff) +#define REG_OPEN_MCFG_EN (0x10000) +#define REG_ENSET_BTROM_EW_EN (0x0) +#define REG_SET_MCFG00 (0x0) +#define REG_ISP_CMD_MAIN_ARR (0X03) +#define REG_HOSTCTRL_EN (0x1) +#define REG_ACCESSEN_OPEN (0x3c00F091) +#define REG_ACCESSEN_CLOSE (0x3c00f011) +#define AW_REG_FLASH_WAKE_UP (0x4700) +#define REG_UPDATA_DIS (0x4744) +#define AW_REG_FLASH_WAKE_UP_ON (0x110) +#define AW_REG_FLASH_WAKE_UP_OFF (0x102) + +#define AW_BT_HOST2CPU_TRIG_ONLINE_UPGRADE_CMD (0x14) + +#define AW_BT_STATE_INFO_ERR (0x1c00) +#define AW_BT_STATE_INFO_PST (0x1C04) +#define AW_BT_STATE_INFO_FLAG (0x1C08) +#define AW_BT_PROT_CMD_PACK_ADDR (0x1C10) +#define AW_BT_PROT_ACK_PACK_ADDR (0x1C14) +#define AW_BT_MESSAGE_MSG0 (0x1C20) +#define AW_BT_MESSAGE_MSG1 (0x1C24) +#define AW_BT_MESSAGE_MSG_FLAG (0x1C28) +#define AW_BT_VER_INF_VERSION (0x1C30) +#define AW_BT_VER_INF_DATE (0x1C34) +#define AW_BT_HOST2CPU_TRIG (0x4408) +#define AW_BTCPU2HOST_TRIG (0x4410) + +#define AW_SRAM_FIRST_DETECT (0x20000800) + +struct aw_reg_data { + unsigned char rw; + unsigned short reg; +}; +/******************************************** + * Register Access + *******************************************/ +#define REG_NONE_ACCESS (0) +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) + +static const struct aw_reg_data g_aw9620x_reg_access[] = { + //AFE MAP + { .reg = REG_SCANCTRL0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_SCANCTRL1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + + //DSP MAP + { .reg = REG_FILTCTRL0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILT_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLERRAOT_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AOTTAR_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILT_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLERRAOT_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AOTTAR_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL0_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILT_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLERRAOT_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AOTTAR_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL0_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILT_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLERRAOT_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AOTTAR_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL0_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILT_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLERRAOT_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AOTTAR_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL0_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILT_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLERRAOT_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AOTTAR_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL0_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILT_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLERRAOT_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AOTTAR_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL0_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILT_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLERRAOT_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AOTTAR_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + + //STAT MAP + { .reg = REG_FWVER, .rw = REG_RD_ACCESS, }, + { .reg = REG_WST, .rw = REG_RD_ACCESS, }, + { .reg = REG_STAT0, .rw = REG_RD_ACCESS, }, + { .reg = REG_STAT1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + + //DATA MAP + { .reg = REG_TEMPERATURE, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH0, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH0, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH1, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH1, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH2, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH2, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH3, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH3, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH4, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH4, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH5, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH5, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH6, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH6, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH7, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH7, .rw = REG_RD_ACCESS, }, + + //SFR MAP + { .reg = REG_IRQSRC, .rw = REG_RD_ACCESS, }, + { .reg = REG_IRQEN, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_I2CDEVID, .rw = REG_RD_ACCESS, }, + { .reg = REG_CHIP_ID, .rw = REG_RD_ACCESS, }, + { .reg = REG_CHIPSTAT, .rw = REG_RD_ACCESS, }, + { .reg = REG_HOSTCTRL, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_RSTNALL, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, +}; + +//aw96208 reg config +static const uint32_t aw9620x_reg_default[] = { + 0x1A00, 0x003FFFFF, + 0x1A04, 0x00000032, + 0x1A40, 0x09500001, + 0x1A44, 0x3F000000, + 0x1A4C, 0x00000000, + 0x1ACC, 0x09500004, + 0x1AD0, 0x3F000000, + 0x1AD8, 0x00000000, + 0x1B58, 0x09500010, + 0x1B5C, 0x3F000000, + 0x1B64, 0x00000000, + 0x1BE4, 0x09500040, + 0x1BE8, 0x3F000000, + 0x1BF0, 0x00000000, + 0x1C70, 0x09500100, + 0x1C74, 0x3F000000, + 0x1C7C, 0x00000000, + 0x1CFC, 0x09500400, + 0x1D00, 0x3F000000, + 0x1D08, 0x00000000, + 0x1D88, 0x09501000, + 0x1D8C, 0x3F000000, + 0x1D94, 0x00000000, + 0x1E14, 0x09504000, + 0x1E18, 0x3F000000, + 0x1E20, 0x00000000, + 0x1A50, 0x00000004, + 0x1A78, 0x00640000, + 0x1A8C, 0xFF000001, + 0x1A90, 0x00000000, + 0x1A94, 0x00002710, + 0x1A98, 0x00004E20, + 0x1A9C, 0x00000000, + 0x1ADC, 0x00000004, + 0x1B04, 0x00640000, + 0x1B18, 0xFF000001, + 0x1B1C, 0x00000000, + 0x1B20, 0x00002710, + 0x1B24, 0x00004E20, + 0x1B28, 0x00000000, + 0x1B68, 0x00000004, + 0x1B90, 0x00640000, + 0x1BA4, 0xFF000001, + 0x1BA8, 0x00000000, + 0x1BAC, 0x00002710, + 0x1BB0, 0x00004E20, + 0x1BB4, 0x00000000, + 0x1BF4, 0x00000004, + 0x1C1C, 0x00640000, + 0x1C30, 0xFF000001, + 0x1C34, 0x00000000, + 0x1C38, 0x00002710, + 0x1C3C, 0x00004E20, + 0x1C40, 0x00000000, + 0x1C80, 0x00000004, + 0x1CA8, 0x00640000, + 0x1CBC, 0xFF000001, + 0x1CC0, 0x00000000, + 0x1CC4, 0x00002710, + 0x1CC8, 0x00004E20, + 0x1CCC, 0x00000000, + 0x1D0C, 0x00000004, + 0x1D34, 0x00640000, + 0x1D48, 0xFF000001, + 0x1D4C, 0x00000000, + 0x1D50, 0x00002710, + 0x1D54, 0x00004E20, + 0x1D58, 0x00000000, + 0x1D98, 0x00000004, + 0x1DC0, 0x00640000, + 0x1DD4, 0xFF000001, + 0x1DD8, 0x00000000, + 0x1DDC, 0x00002710, + 0x1DE0, 0x00004E20, + 0x1DE4, 0x00000000, + 0x1E24, 0x00000004, + 0x1E4C, 0x00640000, + 0x1E60, 0xFF000001, + 0x1E64, 0x00000000, + 0x1E68, 0x00002710, + 0x1E6C, 0x00004E20, + 0x1E70, 0x00000000, + 0x4414, 0x0000081F, + 0x1A18, 0x00FFFFFF, +}; + +#endif diff --git a/include/linux/input/awinic/aw963xx.h b/include/linux/input/awinic/aw963xx.h new file mode 100644 index 000000000000..ad7904ac930a --- /dev/null +++ b/include/linux/input/awinic/aw963xx.h @@ -0,0 +1,1538 @@ +#ifndef AW963XX_H_ +#define AW963XX_H_ +#include "aw_sar_type.h" +#include "linux/sensors.h" + +#define USE_SENSORS_CLASS +#define AW963XX_CHANNEL_NUM_MAX (12) +#define AW963XX_VALID_TH (2) +#define AW963XX_DATA_PROCESS_FACTOR (1024) +#define AW9620X_SAR_VCC_MIN_UV (1700000) +#define AW9620X_SAR_VCC_MAX_UV (3600000) +#define AW963XX_SRAM_UPDATE_ONE_PACK_SIZE (1024) +#define AW963XX_SRAM_UPDATE_ONE_UINT_SIZE (4) +#define AW963XX_SRAM_START_ADDR (0x2000) +#define AW963XX_SRAM_END_ADDR (0x4ff4 + 4) +#define AW963XX_SRAM_SIZE (AW963XX_SRAM_END_ADDR - AW963XX_SRAM_START_ADDR) +#define AW963XX_SRAM_DEFAULT_VAL (0xffffffff) + +#define AW963XX_TRIGGER_FAR (0) + +#define AW963XX_STEP_LEN_UNSIGNED_CAP_ROUGH_ADJ (9900) +#define AW963XX_STEP_LEN_UNSIGNED_CAP_FINE_ADJ (152) +#define AW963XX_STEP_LEN_UNSIGNED_CAP_ENLARGE (10000) + +#define AW963XX_CPU_OSC_CTRL_MASK (1) + +#define AW963XX_ABNORMAL_IRQ_HANDLE_DELAY_MS (30000) + +#define HALF_WORD (0xf) +#define ONE_WORD (0xff) +#define AW_BIT8 (8) +#define AW_BIT16 (16) +#define AW96303 ("aw96303") +#define AW96305 ("aw96305") +#define AW96308 ("aw96308") +#define AW96310 ("aw96310") + +enum aw963xx_cap_mode { + AW963XX_UNSIGNED_CAP = 0, + AW963XX_SIGNED_CAP = 4, + AW963XX_MUTUAL_CAP = 5, +}; + +enum aw963xx_approach_state { + AW963XX_FAR_AWAY = 0, + AW963XX_APPROACH = 1, +}; + +enum aw963xx_cs_2_irq { + AW963XX_CS2_IRQ = 2, + AW963XX_CS5_IRQ = 5, +}; + +enum aw963xx_operation_mode { + AW963XX_ACTIVE_MODE = 0x01, + AW963XX_SLEEP_MODE = 0x02, + AW963XX_DEEPSLEEP_MODE = 0x03, +}; + +enum aw963xx_chip_id { + AW96303_CHIP_ID = 0xA9630340, + AW96305_CHIP_ID = 0xA9630520, + AW96308_CHIP_ID = 0xA9630810, + AW96310_CHIP_ID = 0xA9631010, +}; + +enum aw963xx_boot_mode { + AW963XX_ROM_MODE = 0, + AW963XX_RAM_MODE = 1, +}; + +struct aw963xx { + uint32_t irq_mux; + uint32_t start_mode; + uint32_t ref_ch_en[AW963XX_CHANNEL_NUM_MAX]; + void *p_aw_sar; + + struct work_struct abnormal_irq_work; + struct timer_list abnormal_irq_timer; + + struct class capsense_class; + + uint8_t read_flag; + uint16_t read_reg; +#ifdef USE_SENSORS_CLASS + struct sensors_classdev sensors_capsensor_chs[AW963XX_CHANNEL_NUM_MAX]; +#endif + +}; + +#define REG_CPU_MODE_SET (0xf800) +#define AW963XX_RESET_CPU_SET_BOOT_SATRT (0x00010100) +#define AW963XX_EXIT_RESET_CPU_SET_BOOT_SATRT (0x00000100) +#define REG_CPU_RESET (0XFF0C) +#define AW963XX_RESET_SET (0) +#define REG_ENABLE_RAM (0xfff4) +#define AW963XX_ENABLE_DATA_RAM (0x3c00C11f) +#define REG_RAM_PASSWORD (0xFFE4) +#define AW963XX_NO_ENCRYPTION (0x3C000000) +#define REG_ENABLE_CHECKSUM_ADDR (0x4FF8) +#define AW963XX_EN_CHECKSUM_CODE (0x21662166) +#define AW963XX_START_CPU (0x00000100) +#define AW963XX_FILTCTRL0_CHX_REFAEN (16) +#define AW963XX_FILTCTRL0_CHX_REFASEL (11) +#define AW963XX_FILTCTRL0_CHX_REFBEN (9) +#define AW963XX_FILTCTRL0_CHX_REFBSEL (4) +#define AW963XX_REF_EN (1) +#define AW963XX_REF_DIS (0) +#define REG_SA_RSTNALL (0xFF0C) +#define AW963XX_SOFT_RST_EN (0) +#define AW963XX_CHIP_INIT_MAX_TIME_MS (30) + +#define AW963XX_RAM_START_ADDR (0x2000) + +#define AFE_BASE_ADDR (0x0000) +#define DSP_BASE_ADDR (0x0000) +#define DSPEX_BASE_ADDR (0x0000) +#define STAT_BASE_ADDR (0x0000) +#define DATA_BASE_ADDR (0x0000) +#define SFR_BASE_ADDR (0x0000) +#define HIDDEN_BASE_ADDR (0x0000) +/* registers list */ +//AFE MAP +#define REG_SCANCTRL0 ((0x0000) + AFE_BASE_ADDR) +#define REG_SCANCTRL1 ((0x0004) + AFE_BASE_ADDR) +#define REG_SCANCTRL2 ((0x0008) + AFE_BASE_ADDR) +#define REG_SCANCTRL3 ((0x000C) + AFE_BASE_ADDR) +#define REG_AFESOFTCFG0_CH0 ((0x010C) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH0 ((0x0114) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH0 ((0x0118) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH0 ((0x0120) + AFE_BASE_ADDR) +#define REG_AFECFG4_CH0 ((0x0124) + AFE_BASE_ADDR) +#define REG_CHLSEL0_CH0 ((0x0128) + AFE_BASE_ADDR) +#define REG_AFESOFTCFG0_CH1 ((0x0224) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH1 ((0x022C) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH1 ((0x0230) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH1 ((0x0238) + AFE_BASE_ADDR) +#define REG_AFECFG4_CH1 ((0x023C) + AFE_BASE_ADDR) +#define REG_CHLSEL0_CH1 ((0x0240) + AFE_BASE_ADDR) +#define REG_AFESOFTCFG0_CH2 ((0x033C) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH2 ((0x0344) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH2 ((0x0348) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH2 ((0x0350) + AFE_BASE_ADDR) +#define REG_AFECFG4_CH2 ((0x0354) + AFE_BASE_ADDR) +#define REG_CHLSEL0_CH2 ((0x0358) + AFE_BASE_ADDR) +#define REG_AFESOFTCFG0_CH3 ((0x0454) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH3 ((0x045C) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH3 ((0x0460) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH3 ((0x0468) + AFE_BASE_ADDR) +#define REG_AFECFG4_CH3 ((0x046C) + AFE_BASE_ADDR) +#define REG_CHLSEL0_CH3 ((0x0470) + AFE_BASE_ADDR) +#define REG_AFESOFTCFG0_CH4 ((0x056C) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH4 ((0x0574) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH4 ((0x0578) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH4 ((0x0580) + AFE_BASE_ADDR) +#define REG_AFECFG4_CH4 ((0x0584) + AFE_BASE_ADDR) +#define REG_CHLSEL0_CH4 ((0x0588) + AFE_BASE_ADDR) +#define REG_AFESOFTCFG0_CH5 ((0x0684) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH5 ((0x068C) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH5 ((0x0690) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH5 ((0x0698) + AFE_BASE_ADDR) +#define REG_AFECFG4_CH5 ((0x069C) + AFE_BASE_ADDR) +#define REG_CHLSEL0_CH5 ((0x06A0) + AFE_BASE_ADDR) +#define REG_AFESOFTCFG0_CH6 ((0x079C) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH6 ((0x07A4) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH6 ((0x07A8) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH6 ((0x07B0) + AFE_BASE_ADDR) +#define REG_AFECFG4_CH6 ((0x07B4) + AFE_BASE_ADDR) +#define REG_CHLSEL0_CH6 ((0x07B8) + AFE_BASE_ADDR) +#define REG_AFESOFTCFG0_CH7 ((0x08B4) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH7 ((0x08BC) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH7 ((0x08C0) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH7 ((0x08C8) + AFE_BASE_ADDR) +#define REG_AFECFG4_CH7 ((0x08CC) + AFE_BASE_ADDR) +#define REG_CHLSEL0_CH7 ((0x08D0) + AFE_BASE_ADDR) +#define REG_AFESOFTCFG0_CH8 ((0x09CC) + AFE_BASE_ADDR) +#define REG_AFECFG0_CH8 ((0x09D4) + AFE_BASE_ADDR) +#define REG_AFECFG1_CH8 ((0x09D8) + AFE_BASE_ADDR) +#define REG_AFECFG3_CH8 ((0x09E0) + AFE_BASE_ADDR) +#define REG_AFECFG4_CH8 ((0x09E4) + AFE_BASE_ADDR) +#define REG_CHLSEL0_CH8 ((0x09E8) + AFE_BASE_ADDR) + +/* registers list */ +//DSP MAP +#define REG_FILTCTRL0_CH0 ((0x0130) + DSP_BASE_ADDR) +#define REG_FILTCTRL1_CH0 ((0x013C) + DSP_BASE_ADDR) +#define REG_BLFILTA_CH0 ((0x0140) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH0 ((0x0148) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH0 ((0x014C) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH0 ((0x0150) + DSP_BASE_ADDR) +#define REG_PROXTH2_CH0 ((0x0154) + DSP_BASE_ADDR) +#define REG_PROXTH3_CH0 ((0x0158) + DSP_BASE_ADDR) +#define REG_NEGPROXTH_CH0 ((0x015C) + DSP_BASE_ADDR) +#define REG_STDDET_CH0 ((0x0160) + DSP_BASE_ADDR) +#define REG_INITPROX0_CH0 ((0x0164) + DSP_BASE_ADDR) +#define REG_INITPROX1_CH0 ((0x0168) + DSP_BASE_ADDR) +#define REG_FILTCTRL0_CH1 ((0x0248) + DSP_BASE_ADDR) +#define REG_FILTCTRL1_CH1 ((0x0254) + DSP_BASE_ADDR) +#define REG_BLFILTA_CH1 ((0x0258) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH1 ((0x0260) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH1 ((0x0264) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH1 ((0x0268) + DSP_BASE_ADDR) +#define REG_PROXTH2_CH1 ((0x026C) + DSP_BASE_ADDR) +#define REG_PROXTH3_CH1 ((0x0270) + DSP_BASE_ADDR) +#define REG_NEGPROXTH_CH1 ((0x0274) + DSP_BASE_ADDR) +#define REG_STDDET_CH1 ((0x0278) + DSP_BASE_ADDR) +#define REG_INITPROX0_CH1 ((0x027C) + DSP_BASE_ADDR) +#define REG_INITPROX1_CH1 ((0x0280) + DSP_BASE_ADDR) +#define REG_FILTCTRL0_CH2 ((0x0360) + DSP_BASE_ADDR) +#define REG_FILTCTRL1_CH2 ((0x036C) + DSP_BASE_ADDR) +#define REG_BLFILTA_CH2 ((0x0370) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH2 ((0x0378) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH2 ((0x037C) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH2 ((0x0380) + DSP_BASE_ADDR) +#define REG_PROXTH2_CH2 ((0x0384) + DSP_BASE_ADDR) +#define REG_PROXTH3_CH2 ((0x0388) + DSP_BASE_ADDR) +#define REG_NEGPROXTH_CH2 ((0x038C) + DSP_BASE_ADDR) +#define REG_STDDET_CH2 ((0x0390) + DSP_BASE_ADDR) +#define REG_INITPROX0_CH2 ((0x0394) + DSP_BASE_ADDR) +#define REG_INITPROX1_CH2 ((0x0398) + DSP_BASE_ADDR) +#define REG_FILTCTRL0_CH3 ((0x0478) + DSP_BASE_ADDR) +#define REG_FILTCTRL1_CH3 ((0x0484) + DSP_BASE_ADDR) +#define REG_BLFILTA_CH3 ((0x0488) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH3 ((0x0490) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH3 ((0x0494) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH3 ((0x0498) + DSP_BASE_ADDR) +#define REG_PROXTH2_CH3 ((0x049C) + DSP_BASE_ADDR) +#define REG_PROXTH3_CH3 ((0x04A0) + DSP_BASE_ADDR) +#define REG_NEGPROXTH_CH3 ((0x04A4) + DSP_BASE_ADDR) +#define REG_STDDET_CH3 ((0x04A8) + DSP_BASE_ADDR) +#define REG_INITPROX0_CH3 ((0x04AC) + DSP_BASE_ADDR) +#define REG_INITPROX1_CH3 ((0x04B0) + DSP_BASE_ADDR) +#define REG_FILTCTRL0_CH4 ((0x0590) + DSP_BASE_ADDR) +#define REG_FILTCTRL1_CH4 ((0x059C) + DSP_BASE_ADDR) +#define REG_BLFILTA_CH4 ((0x05A0) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH4 ((0x05A8) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH4 ((0x05AC) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH4 ((0x05B0) + DSP_BASE_ADDR) +#define REG_PROXTH2_CH4 ((0x05B4) + DSP_BASE_ADDR) +#define REG_PROXTH3_CH4 ((0x05B8) + DSP_BASE_ADDR) +#define REG_NEGPROXTH_CH4 ((0x05BC) + DSP_BASE_ADDR) +#define REG_STDDET_CH4 ((0x05C0) + DSP_BASE_ADDR) +#define REG_INITPROX0_CH4 ((0x05C4) + DSP_BASE_ADDR) +#define REG_INITPROX1_CH4 ((0x05C8) + DSP_BASE_ADDR) +#define REG_FILTCTRL0_CH5 ((0x06A8) + DSP_BASE_ADDR) +#define REG_FILTCTRL1_CH5 ((0x06B4) + DSP_BASE_ADDR) +#define REG_BLFILTA_CH5 ((0x06B8) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH5 ((0x06C0) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH5 ((0x06C4) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH5 ((0x06C8) + DSP_BASE_ADDR) +#define REG_PROXTH2_CH5 ((0x06CC) + DSP_BASE_ADDR) +#define REG_PROXTH3_CH5 ((0x06D0) + DSP_BASE_ADDR) +#define REG_NEGPROXTH_CH5 ((0x06D4) + DSP_BASE_ADDR) +#define REG_STDDET_CH5 ((0x06D8) + DSP_BASE_ADDR) +#define REG_INITPROX0_CH5 ((0x06DC) + DSP_BASE_ADDR) +#define REG_INITPROX1_CH5 ((0x06E0) + DSP_BASE_ADDR) +#define REG_FILTCTRL0_CH6 ((0x07C0) + DSP_BASE_ADDR) +#define REG_FILTCTRL1_CH6 ((0x07CC) + DSP_BASE_ADDR) +#define REG_BLFILTA_CH6 ((0x07D0) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH6 ((0x07D8) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH6 ((0x07DC) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH6 ((0x07E0) + DSP_BASE_ADDR) +#define REG_PROXTH2_CH6 ((0x07E4) + DSP_BASE_ADDR) +#define REG_PROXTH3_CH6 ((0x07E8) + DSP_BASE_ADDR) +#define REG_NEGPROXTH_CH6 ((0x07EC) + DSP_BASE_ADDR) +#define REG_STDDET_CH6 ((0x07F0) + DSP_BASE_ADDR) +#define REG_INITPROX0_CH6 ((0x07F4) + DSP_BASE_ADDR) +#define REG_INITPROX1_CH6 ((0x07F8) + DSP_BASE_ADDR) +#define REG_FILTCTRL0_CH7 ((0x08D8) + DSP_BASE_ADDR) +#define REG_FILTCTRL1_CH7 ((0x08E4) + DSP_BASE_ADDR) +#define REG_BLFILTA_CH7 ((0x08E8) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH7 ((0x08F0) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH7 ((0x08F4) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH7 ((0x08F8) + DSP_BASE_ADDR) +#define REG_PROXTH2_CH7 ((0x08FC) + DSP_BASE_ADDR) +#define REG_PROXTH3_CH7 ((0x0900) + DSP_BASE_ADDR) +#define REG_NEGPROXTH_CH7 ((0x0904) + DSP_BASE_ADDR) +#define REG_STDDET_CH7 ((0x0908) + DSP_BASE_ADDR) +#define REG_INITPROX0_CH7 ((0x090C) + DSP_BASE_ADDR) +#define REG_INITPROX1_CH7 ((0x0910) + DSP_BASE_ADDR) +#define REG_FILTCTRL0_CH8 ((0x09F0) + DSP_BASE_ADDR) +#define REG_FILTCTRL1_CH8 ((0x09FC) + DSP_BASE_ADDR) +#define REG_BLFILTA_CH8 ((0x0A00) + DSP_BASE_ADDR) +#define REG_PROXCTRL_CH8 ((0x0A08) + DSP_BASE_ADDR) +#define REG_PROXTH0_CH8 ((0x0A0C) + DSP_BASE_ADDR) +#define REG_PROXTH1_CH8 ((0x0A10) + DSP_BASE_ADDR) +#define REG_PROXTH2_CH8 ((0x0A14) + DSP_BASE_ADDR) +#define REG_PROXTH3_CH8 ((0x0A18) + DSP_BASE_ADDR) +#define REG_NEGPROXTH_CH8 ((0x0A1C) + DSP_BASE_ADDR) +#define REG_STDDET_CH8 ((0x0A20) + DSP_BASE_ADDR) +#define REG_INITPROX0_CH8 ((0x0A24) + DSP_BASE_ADDR) +#define REG_INITPROX1_CH8 ((0x0A28) + DSP_BASE_ADDR) + +/* registers list */ +//DSPEX MAP +#define REG_REFRAWTH_CH0 ((0x019C) + DSPEX_BASE_ADDR) +#define REG_REFCOEFMAX_CH0 ((0x01A0) + DSPEX_BASE_ADDR) +#define REG_REFCOEFMIN_CH0 ((0x01A4) + DSPEX_BASE_ADDR) +#define REG_REFCOEFINIT_CH0 ((0x01A8) + DSPEX_BASE_ADDR) +#define REG_REFCOEFLAST_CH0 ((0x01AC) + DSPEX_BASE_ADDR) +#define REG_REFKP_CH0 ((0x01B0) + DSPEX_BASE_ADDR) +#define REG_REFCOEFLIMIT_CH0 ((0x01B4) + DSPEX_BASE_ADDR) +#define REG_REFCOMP_CH0 ((0x01B8) + DSPEX_BASE_ADDR) +#define REG_REFVALIDTAR_CH0 ((0x01BC) + DSPEX_BASE_ADDR) +#define REG_REFRAWLAST_CH0 ((0x01C0) + DSPEX_BASE_ADDR) +#define REG_BLNEWFILTERA_CH0 ((0x01D0) + DSPEX_BASE_ADDR) +#define REG_BLNEWFILTERB_CH0 ((0x01D4) + DSPEX_BASE_ADDR) +#define REG_THCOMPARE_CH0 ((0x01D8) + DSPEX_BASE_ADDR) +#define REG_THDELTA1_CH0 ((0x01DC) + DSPEX_BASE_ADDR) +#define REG_THDELTA2_CH0 ((0x01E0) + DSPEX_BASE_ADDR) +#define REG_JUDGE_CH0 ((0x01E4) + DSPEX_BASE_ADDR) +#define REG_REFRAWTH_CH1 ((0x02B4) + DSPEX_BASE_ADDR) +#define REG_REFCOEFMAX_CH1 ((0x02B8) + DSPEX_BASE_ADDR) +#define REG_REFCOEFMIN_CH1 ((0x02BC) + DSPEX_BASE_ADDR) +#define REG_REFCOEFINIT_CH1 ((0x02C0) + DSPEX_BASE_ADDR) +#define REG_REFCOEFLAST_CH1 ((0x02C4) + DSPEX_BASE_ADDR) +#define REG_REFKP_CH1 ((0x02C8) + DSPEX_BASE_ADDR) +#define REG_REFCOEFLIMIT_CH1 ((0x02CC) + DSPEX_BASE_ADDR) +#define REG_REFCOMP_CH1 ((0x02D0) + DSPEX_BASE_ADDR) +#define REG_REFVALIDTAR_CH1 ((0x02D4) + DSPEX_BASE_ADDR) +#define REG_REFRAWLAST_CH1 ((0x02D8) + DSPEX_BASE_ADDR) +#define REG_BLNEWFILTERA_CH1 ((0x02E8) + DSPEX_BASE_ADDR) +#define REG_BLNEWFILTERB_CH1 ((0x02EC) + DSPEX_BASE_ADDR) +#define REG_THCOMPARE_CH1 ((0x02F0) + DSPEX_BASE_ADDR) +#define REG_THDELTA1_CH1 ((0x02F4) + DSPEX_BASE_ADDR) +#define REG_THDELTA2_CH1 ((0x02F8) + DSPEX_BASE_ADDR) +#define REG_JUDGE_CH1 ((0x02FC) + DSPEX_BASE_ADDR) +#define REG_REFRAWTH_CH2 ((0x03CC) + DSPEX_BASE_ADDR) +#define REG_REFCOEFMAX_CH2 ((0x03D0) + DSPEX_BASE_ADDR) +#define REG_REFCOEFMIN_CH2 ((0x03D4) + DSPEX_BASE_ADDR) +#define REG_REFCOEFINIT_CH2 ((0x03D8) + DSPEX_BASE_ADDR) +#define REG_REFCOEFLAST_CH2 ((0x03DC) + DSPEX_BASE_ADDR) +#define REG_REFKP_CH2 ((0x03E0) + DSPEX_BASE_ADDR) +#define REG_REFCOEFLIMIT_CH2 ((0x03E4) + DSPEX_BASE_ADDR) +#define REG_REFCOMP_CH2 ((0x03E8) + DSPEX_BASE_ADDR) +#define REG_REFVALIDTAR_CH2 ((0x03EC) + DSPEX_BASE_ADDR) +#define REG_REFRAWLAST_CH2 ((0x03F0) + DSPEX_BASE_ADDR) +#define REG_BLNEWFILTERA_CH2 ((0x0400) + DSPEX_BASE_ADDR) +#define REG_BLNEWFILTERB_CH2 ((0x0404) + DSPEX_BASE_ADDR) +#define REG_THCOMPARE_CH2 ((0x0408) + DSPEX_BASE_ADDR) +#define REG_THDELTA1_CH2 ((0x040C) + DSPEX_BASE_ADDR) +#define REG_THDELTA2_CH2 ((0x0410) + DSPEX_BASE_ADDR) +#define REG_JUDGE_CH2 ((0x0414) + DSPEX_BASE_ADDR) +#define REG_REFRAWTH_CH3 ((0x04E4) + DSPEX_BASE_ADDR) +#define REG_REFCOEFMAX_CH3 ((0x04E8) + DSPEX_BASE_ADDR) +#define REG_REFCOEFMIN_CH3 ((0x04EC) + DSPEX_BASE_ADDR) +#define REG_REFCOEFINIT_CH3 ((0x04F0) + DSPEX_BASE_ADDR) +#define REG_REFCOEFLAST_CH3 ((0x04F4) + DSPEX_BASE_ADDR) +#define REG_REFKP_CH3 ((0x04F8) + DSPEX_BASE_ADDR) +#define REG_REFCOEFLIMIT_CH3 ((0x04FC) + DSPEX_BASE_ADDR) +#define REG_REFCOMP_CH3 ((0x0500) + DSPEX_BASE_ADDR) +#define REG_REFVALIDTAR_CH3 ((0x0504) + DSPEX_BASE_ADDR) +#define REG_REFRAWLAST_CH3 ((0x0508) + DSPEX_BASE_ADDR) +#define REG_BLNEWFILTERA_CH3 ((0x0518) + DSPEX_BASE_ADDR) +#define REG_BLNEWFILTERB_CH3 ((0x051C) + DSPEX_BASE_ADDR) +#define REG_THCOMPARE_CH3 ((0x0520) + DSPEX_BASE_ADDR) +#define REG_THDELTA1_CH3 ((0x0524) + DSPEX_BASE_ADDR) +#define REG_THDELTA2_CH3 ((0x0528) + DSPEX_BASE_ADDR) +#define REG_JUDGE_CH3 ((0x052C) + DSPEX_BASE_ADDR) +#define REG_REFRAWTH_CH4 ((0x05FC) + DSPEX_BASE_ADDR) +#define REG_REFCOEFMAX_CH4 ((0x0600) + DSPEX_BASE_ADDR) +#define REG_REFCOEFMIN_CH4 ((0x0604) + DSPEX_BASE_ADDR) +#define REG_REFCOEFINIT_CH4 ((0x0608) + DSPEX_BASE_ADDR) +#define REG_REFCOEFLAST_CH4 ((0x060C) + DSPEX_BASE_ADDR) +#define REG_REFKP_CH4 ((0x0610) + DSPEX_BASE_ADDR) +#define REG_REFCOEFLIMIT_CH4 ((0x0614) + DSPEX_BASE_ADDR) +#define REG_REFCOMP_CH4 ((0x0618) + DSPEX_BASE_ADDR) +#define REG_REFVALIDTAR_CH4 ((0x061C) + DSPEX_BASE_ADDR) +#define REG_REFRAWLAST_CH4 ((0x0620) + DSPEX_BASE_ADDR) +#define REG_BLNEWFILTERA_CH4 ((0x0630) + DSPEX_BASE_ADDR) +#define REG_BLNEWFILTERB_CH4 ((0x0634) + DSPEX_BASE_ADDR) +#define REG_THCOMPARE_CH4 ((0x0638) + DSPEX_BASE_ADDR) +#define REG_THDELTA1_CH4 ((0x063C) + DSPEX_BASE_ADDR) +#define REG_THDELTA2_CH4 ((0x0640) + DSPEX_BASE_ADDR) +#define REG_JUDGE_CH4 ((0x0644) + DSPEX_BASE_ADDR) +#define REG_REFRAWTH_CH5 ((0x0714) + DSPEX_BASE_ADDR) +#define REG_REFCOEFMAX_CH5 ((0x0718) + DSPEX_BASE_ADDR) +#define REG_REFCOEFMIN_CH5 ((0x071C) + DSPEX_BASE_ADDR) +#define REG_REFCOEFINIT_CH5 ((0x0720) + DSPEX_BASE_ADDR) +#define REG_REFCOEFLAST_CH5 ((0x0724) + DSPEX_BASE_ADDR) +#define REG_REFKP_CH5 ((0x0728) + DSPEX_BASE_ADDR) +#define REG_REFCOEFLIMIT_CH5 ((0x072C) + DSPEX_BASE_ADDR) +#define REG_REFCOMP_CH5 ((0x0730) + DSPEX_BASE_ADDR) +#define REG_REFVALIDTAR_CH5 ((0x0734) + DSPEX_BASE_ADDR) +#define REG_REFRAWLAST_CH5 ((0x0738) + DSPEX_BASE_ADDR) +#define REG_BLNEWFILTERA_CH5 ((0x0748) + DSPEX_BASE_ADDR) +#define REG_BLNEWFILTERB_CH5 ((0x074C) + DSPEX_BASE_ADDR) +#define REG_THCOMPARE_CH5 ((0x0750) + DSPEX_BASE_ADDR) +#define REG_THDELTA1_CH5 ((0x0754) + DSPEX_BASE_ADDR) +#define REG_THDELTA2_CH5 ((0x0758) + DSPEX_BASE_ADDR) +#define REG_JUDGE_CH5 ((0x075C) + DSPEX_BASE_ADDR) +#define REG_REFRAWTH_CH6 ((0x082C) + DSPEX_BASE_ADDR) +#define REG_REFCOEFMAX_CH6 ((0x0830) + DSPEX_BASE_ADDR) +#define REG_REFCOEFMIN_CH6 ((0x0834) + DSPEX_BASE_ADDR) +#define REG_REFCOEFINIT_CH6 ((0x0838) + DSPEX_BASE_ADDR) +#define REG_REFCOEFLAST_CH6 ((0x083C) + DSPEX_BASE_ADDR) +#define REG_REFKP_CH6 ((0x0840) + DSPEX_BASE_ADDR) +#define REG_REFCOEFLIMIT_CH6 ((0x0844) + DSPEX_BASE_ADDR) +#define REG_REFCOMP_CH6 ((0x0848) + DSPEX_BASE_ADDR) +#define REG_REFVALIDTAR_CH6 ((0x084C) + DSPEX_BASE_ADDR) +#define REG_REFRAWLAST_CH6 ((0x0850) + DSPEX_BASE_ADDR) +#define REG_BLNEWFILTERA_CH6 ((0x0860) + DSPEX_BASE_ADDR) +#define REG_BLNEWFILTERB_CH6 ((0x0864) + DSPEX_BASE_ADDR) +#define REG_THCOMPARE_CH6 ((0x0868) + DSPEX_BASE_ADDR) +#define REG_THDELTA1_CH6 ((0x086C) + DSPEX_BASE_ADDR) +#define REG_THDELTA2_CH6 ((0x0870) + DSPEX_BASE_ADDR) +#define REG_JUDGE_CH6 ((0x0874) + DSPEX_BASE_ADDR) +#define REG_REFRAWTH_CH7 ((0x0944) + DSPEX_BASE_ADDR) +#define REG_REFCOEFMAX_CH7 ((0x0948) + DSPEX_BASE_ADDR) +#define REG_REFCOEFMIN_CH7 ((0x094C) + DSPEX_BASE_ADDR) +#define REG_REFCOEFINIT_CH7 ((0x0950) + DSPEX_BASE_ADDR) +#define REG_REFCOEFLAST_CH7 ((0x0954) + DSPEX_BASE_ADDR) +#define REG_REFKP_CH7 ((0x0958) + DSPEX_BASE_ADDR) +#define REG_REFCOEFLIMIT_CH7 ((0x095C) + DSPEX_BASE_ADDR) +#define REG_REFCOMP_CH7 ((0x0960) + DSPEX_BASE_ADDR) +#define REG_REFVALIDTAR_CH7 ((0x0964) + DSPEX_BASE_ADDR) +#define REG_REFRAWLAST_CH7 ((0x0968) + DSPEX_BASE_ADDR) +#define REG_BLNEWFILTERA_CH7 ((0x0978) + DSPEX_BASE_ADDR) +#define REG_BLNEWFILTERB_CH7 ((0x097C) + DSPEX_BASE_ADDR) +#define REG_THCOMPARE_CH7 ((0x0980) + DSPEX_BASE_ADDR) +#define REG_THDELTA1_CH7 ((0x0984) + DSPEX_BASE_ADDR) +#define REG_THDELTA2_CH7 ((0x0988) + DSPEX_BASE_ADDR) +#define REG_JUDGE_CH7 ((0x098C) + DSPEX_BASE_ADDR) +#define REG_REFRAWTH_CH8 ((0x0A5C) + DSPEX_BASE_ADDR) +#define REG_REFCOEFMAX_CH8 ((0x0A60) + DSPEX_BASE_ADDR) +#define REG_REFCOEFMIN_CH8 ((0x0A64) + DSPEX_BASE_ADDR) +#define REG_REFCOEFINIT_CH8 ((0x0A68) + DSPEX_BASE_ADDR) +#define REG_REFCOEFLAST_CH8 ((0x0A6C) + DSPEX_BASE_ADDR) +#define REG_REFKP_CH8 ((0x0A70) + DSPEX_BASE_ADDR) +#define REG_REFCOEFLIMIT_CH8 ((0x0A74) + DSPEX_BASE_ADDR) +#define REG_REFCOMP_CH8 ((0x0A78) + DSPEX_BASE_ADDR) +#define REG_REFVALIDTAR_CH8 ((0x0A7C) + DSPEX_BASE_ADDR) +#define REG_REFRAWLAST_CH8 ((0x0A80) + DSPEX_BASE_ADDR) +#define REG_BLNEWFILTERA_CH8 ((0x0A90) + DSPEX_BASE_ADDR) +#define REG_BLNEWFILTERB_CH8 ((0x0A94) + DSPEX_BASE_ADDR) +#define REG_THCOMPARE_CH8 ((0x0A98) + DSPEX_BASE_ADDR) +#define REG_THDELTA1_CH8 ((0x0A9C) + DSPEX_BASE_ADDR) +#define REG_THDELTA2_CH8 ((0x0AA0) + DSPEX_BASE_ADDR) +#define REG_JUDGE_CH8 ((0x0AA4) + DSPEX_BASE_ADDR) +#define REG_PIDCONTROLCFG ((0x0E2C) + DSPEX_BASE_ADDR) +#define REG_PIDCFG_CH0 ((0x0E30) + DSPEX_BASE_ADDR) +#define REG_PIDCFG_CH1 ((0x0E40) + DSPEX_BASE_ADDR) +#define REG_PIDCFG_CH2 ((0x0E50) + DSPEX_BASE_ADDR) +#define REG_PIDCFG_CH3 ((0x0E60) + DSPEX_BASE_ADDR) +#define REG_PIDCFG_CH4 ((0x0E70) + DSPEX_BASE_ADDR) +#define REG_PIDCFG_CH5 ((0x0E80) + DSPEX_BASE_ADDR) +#define REG_PIDCFG_CH6 ((0x0E90) + DSPEX_BASE_ADDR) +#define REG_PIDCFG_CH7 ((0x0EA0) + DSPEX_BASE_ADDR) +#define REG_PIDCFG_CH8 ((0x0EB0) + DSPEX_BASE_ADDR) + +/* registers list */ +//STAT MAP +#define REG_WST ((0x001C) + STAT_BASE_ADDR) +#define REG_STAT0 ((0x0020) + STAT_BASE_ADDR) +#define REG_STAT1 ((0x0024) + STAT_BASE_ADDR) +#define REG_STAT2 ((0x0028) + STAT_BASE_ADDR) +#define REG_STAT3 ((0x002C) + STAT_BASE_ADDR) +#define REG_STAT4 ((0x0030) + STAT_BASE_ADDR) +#define REG_STAT5 ((0x0034) + STAT_BASE_ADDR) +#define REG_STAT7 ((0x003C) + STAT_BASE_ADDR) +#define REG_STAT8 ((0x0040) + STAT_BASE_ADDR) +#define REG_STAT9 ((0x0044) + STAT_BASE_ADDR) +#define REG_STAT10 ((0x0048) + STAT_BASE_ADDR) +#define REG_STAT11 ((0x004C) + STAT_BASE_ADDR) +#define REG_STAT12 ((0x0050) + STAT_BASE_ADDR) +#define REG_STAT13 ((0x0054) + STAT_BASE_ADDR) +#define REG_STAT14 ((0x0058) + STAT_BASE_ADDR) +#define REG_FWVER ((0x005C) + STAT_BASE_ADDR) + +/* registers list */ +//DATA MAP +#define REG_BASELINE_CH0 ((0x01E8) + DATA_BASE_ADDR) +#define REG_DIFF_CH0 ((0x01EC) + DATA_BASE_ADDR) +#define REG_BASELINE_CH1 ((0x0300) + DATA_BASE_ADDR) +#define REG_DIFF_CH1 ((0x0304) + DATA_BASE_ADDR) +#define REG_BASELINE_CH2 ((0x0418) + DATA_BASE_ADDR) +#define REG_DIFF_CH2 ((0x041C) + DATA_BASE_ADDR) +#define REG_BASELINE_CH3 ((0x0530) + DATA_BASE_ADDR) +#define REG_DIFF_CH3 ((0x0534) + DATA_BASE_ADDR) +#define REG_BASELINE_CH4 ((0x0648) + DATA_BASE_ADDR) +#define REG_DIFF_CH4 ((0x064C) + DATA_BASE_ADDR) +#define REG_BASELINE_CH5 ((0x0760) + DATA_BASE_ADDR) +#define REG_DIFF_CH5 ((0x0764) + DATA_BASE_ADDR) +#define REG_BASELINE_CH6 ((0x0878) + DATA_BASE_ADDR) +#define REG_DIFF_CH6 ((0x087C) + DATA_BASE_ADDR) +#define REG_BASELINE_CH7 ((0x0990) + DATA_BASE_ADDR) +#define REG_DIFF_CH7 ((0x0994) + DATA_BASE_ADDR) +#define REG_BASELINE_CH8 ((0x0AA8) + DATA_BASE_ADDR) +#define REG_DIFF_CH8 ((0x0AAC) + DATA_BASE_ADDR) + +/* registers list */ +//SFR MAP +#define REG_IRQSRC ((0xF080) + SFR_BASE_ADDR) +#define REG_IRQEN ((0xF084) + SFR_BASE_ADDR) +#define REG_I2CTIMEOUTEN ((0xF0E4) + SFR_BASE_ADDR) +#define REG_ANACFG ((0xF804) + SFR_BASE_ADDR) +#define REG_CHIPSTAT ((0xFF00) + SFR_BASE_ADDR) +#define REG_CHIP_ID0 ((0xFF10) + SFR_BASE_ADDR) +#define REG_CHIP_ID1 ((0xFF14) + SFR_BASE_ADDR) +#define REG_CHIP_ID2 ((0xFF20) + SFR_BASE_ADDR) +#define REG_I3C_OSC_EN ((0xFF40) + SFR_BASE_ADDR) + +/* registers list */ +// MAP +#define REG_BASELINE_OLD_CH0 ((0x10D4) + HIDDEN_BASE_ADDR) +#define REG_BASELINE_PRE_CH0 ((0x10D8) + HIDDEN_BASE_ADDR) +#define REG_AFECFG1_M_CH0 ((0x10F8) + HIDDEN_BASE_ADDR) +#define REG_BASELINE_OLD_CH1 ((0x1160) + HIDDEN_BASE_ADDR) +#define REG_BASELINE_PRE_CH1 ((0x1164) + HIDDEN_BASE_ADDR) +#define REG_AFECFG1_M_CH1 ((0x1184) + HIDDEN_BASE_ADDR) +#define REG_BASELINE_OLD_CH2 ((0x11EC) + HIDDEN_BASE_ADDR) +#define REG_BASELINE_PRE_CH2 ((0x11F0) + HIDDEN_BASE_ADDR) +#define REG_AFECFG1_M_CH2 ((0x1210) + HIDDEN_BASE_ADDR) +#define REG_BASELINE_OLD_CH3 ((0x1278) + HIDDEN_BASE_ADDR) +#define REG_BASELINE_PRE_CH3 ((0x127C) + HIDDEN_BASE_ADDR) +#define REG_AFECFG1_M_CH3 ((0x129C) + HIDDEN_BASE_ADDR) +#define REG_BASELINE_OLD_CH4 ((0x1304) + HIDDEN_BASE_ADDR) +#define REG_BASELINE_PRE_CH4 ((0x1308) + HIDDEN_BASE_ADDR) +#define REG_AFECFG1_M_CH4 ((0x1328) + HIDDEN_BASE_ADDR) +#define REG_BASELINE_OLD_CH5 ((0x1390) + HIDDEN_BASE_ADDR) +#define REG_BASELINE_PRE_CH5 ((0x1394) + HIDDEN_BASE_ADDR) +#define REG_AFECFG1_M_CH5 ((0x13B4) + HIDDEN_BASE_ADDR) +#define REG_BASELINE_OLD_CH6 ((0x141C) + HIDDEN_BASE_ADDR) +#define REG_BASELINE_PRE_CH6 ((0x1420) + HIDDEN_BASE_ADDR) +#define REG_AFECFG1_M_CH6 ((0x1440) + HIDDEN_BASE_ADDR) +#define REG_BASELINE_OLD_CH7 ((0x14A8) + HIDDEN_BASE_ADDR) +#define REG_BASELINE_PRE_CH7 ((0x14AC) + HIDDEN_BASE_ADDR) +#define REG_AFECFG1_M_CH7 ((0x14CC) + HIDDEN_BASE_ADDR) +#define REG_BASELINE_OLD_CH8 ((0x1534) + HIDDEN_BASE_ADDR) +#define REG_BASELINE_PRE_CH8 ((0x1538) + HIDDEN_BASE_ADDR) +#define REG_AFECFG1_M_CH8 ((0x1558) + HIDDEN_BASE_ADDR) + +#define REG_CMD ((0xf008) + SFR_BASE_ADDR) + +#define REG_AFECFG2_CH0 ((0x011c) + AFE_BASE_ADDR) +#define REG_AFECFG2_CH1 ((0x0234) + AFE_BASE_ADDR) + +struct reg_data { + unsigned char rw; + unsigned short reg; +}; +/******************************************** + * Register Access + *******************************************/ +#define REG_NONE_ACCESS (0) +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) + +static const struct reg_data g_aw963xx_reg_access[] = { + //AFE MAP + { .reg = REG_SCANCTRL0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_SCANCTRL1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_SCANCTRL2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_SCANCTRL3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFESOFTCFG0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG4_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_CHLSEL0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFESOFTCFG0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG4_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_CHLSEL0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFESOFTCFG0_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG4_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_CHLSEL0_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFESOFTCFG0_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG4_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_CHLSEL0_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFESOFTCFG0_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG4_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_CHLSEL0_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFESOFTCFG0_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG4_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_CHLSEL0_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFESOFTCFG0_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG4_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_CHLSEL0_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFESOFTCFG0_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG4_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_CHLSEL0_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFESOFTCFG0_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG0_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG1_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG3_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_AFECFG4_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_CHLSEL0_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + + //DSP MAP + { .reg = REG_FILTCTRL0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL1_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILTA_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH2_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH3_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_NEGPROXTH_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_STDDET_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX0_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX1_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL1_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILTA_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH2_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH3_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_NEGPROXTH_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_STDDET_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX0_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX1_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL0_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL1_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILTA_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH2_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH3_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_NEGPROXTH_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_STDDET_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX0_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX1_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL0_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL1_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILTA_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH2_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH3_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_NEGPROXTH_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_STDDET_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX0_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX1_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL0_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL1_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILTA_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH2_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH3_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_NEGPROXTH_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_STDDET_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX0_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX1_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL0_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL1_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILTA_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH2_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH3_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_NEGPROXTH_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_STDDET_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX0_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX1_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL0_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL1_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILTA_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH2_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH3_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_NEGPROXTH_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_STDDET_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX0_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX1_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL0_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL1_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILTA_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH2_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH3_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_NEGPROXTH_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_STDDET_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX0_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX1_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL0_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FILTCTRL1_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLFILTA_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXCTRL_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH0_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH1_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH2_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PROXTH3_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_NEGPROXTH_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_STDDET_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX0_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_INITPROX1_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + + //DSPEX MAP + { .reg = REG_REFRAWTH_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFMAX_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFMIN_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFINIT_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFLAST_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFKP_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFLIMIT_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOMP_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFVALIDTAR_CH0, .rw = REG_RD_ACCESS, }, + { .reg = REG_REFRAWLAST_CH0, .rw = REG_RD_ACCESS, }, + { .reg = REG_BLNEWFILTERA_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLNEWFILTERB_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THCOMPARE_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THDELTA1_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THDELTA2_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_JUDGE_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFRAWTH_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFMAX_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFMIN_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFINIT_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFLAST_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFKP_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFLIMIT_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOMP_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFVALIDTAR_CH1, .rw = REG_RD_ACCESS, }, + { .reg = REG_REFRAWLAST_CH1, .rw = REG_RD_ACCESS, }, + { .reg = REG_BLNEWFILTERA_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLNEWFILTERB_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THCOMPARE_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THDELTA1_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THDELTA2_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_JUDGE_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFRAWTH_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFMAX_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFMIN_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFINIT_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFLAST_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFKP_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFLIMIT_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOMP_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFVALIDTAR_CH2, .rw = REG_RD_ACCESS, }, + { .reg = REG_REFRAWLAST_CH2, .rw = REG_RD_ACCESS, }, + { .reg = REG_BLNEWFILTERA_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLNEWFILTERB_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THCOMPARE_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THDELTA1_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THDELTA2_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_JUDGE_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFRAWTH_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFMAX_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFMIN_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFINIT_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFLAST_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFKP_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFLIMIT_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOMP_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFVALIDTAR_CH3, .rw = REG_RD_ACCESS, }, + { .reg = REG_REFRAWLAST_CH3, .rw = REG_RD_ACCESS, }, + { .reg = REG_BLNEWFILTERA_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLNEWFILTERB_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THCOMPARE_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THDELTA1_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THDELTA2_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_JUDGE_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFRAWTH_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFMAX_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFMIN_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFINIT_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFLAST_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFKP_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFLIMIT_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOMP_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFVALIDTAR_CH4, .rw = REG_RD_ACCESS, }, + { .reg = REG_REFRAWLAST_CH4, .rw = REG_RD_ACCESS, }, + { .reg = REG_BLNEWFILTERA_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLNEWFILTERB_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THCOMPARE_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THDELTA1_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THDELTA2_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_JUDGE_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFRAWTH_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFMAX_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFMIN_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFINIT_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFLAST_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFKP_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFLIMIT_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOMP_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFVALIDTAR_CH5, .rw = REG_RD_ACCESS, }, + { .reg = REG_REFRAWLAST_CH5, .rw = REG_RD_ACCESS, }, + { .reg = REG_BLNEWFILTERA_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLNEWFILTERB_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THCOMPARE_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THDELTA1_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THDELTA2_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_JUDGE_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFRAWTH_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFMAX_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFMIN_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFINIT_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFLAST_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFKP_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFLIMIT_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOMP_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFVALIDTAR_CH6, .rw = REG_RD_ACCESS, }, + { .reg = REG_REFRAWLAST_CH6, .rw = REG_RD_ACCESS, }, + { .reg = REG_BLNEWFILTERA_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLNEWFILTERB_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THCOMPARE_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THDELTA1_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THDELTA2_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_JUDGE_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFRAWTH_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFMAX_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFMIN_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFINIT_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFLAST_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFKP_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFLIMIT_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOMP_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFVALIDTAR_CH7, .rw = REG_RD_ACCESS, }, + { .reg = REG_REFRAWLAST_CH7, .rw = REG_RD_ACCESS, }, + { .reg = REG_BLNEWFILTERA_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLNEWFILTERB_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THCOMPARE_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THDELTA1_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THDELTA2_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_JUDGE_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFRAWTH_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFMAX_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFMIN_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFINIT_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFLAST_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFKP_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOEFLIMIT_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFCOMP_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_REFVALIDTAR_CH8, .rw = REG_RD_ACCESS, }, + { .reg = REG_REFRAWLAST_CH8, .rw = REG_RD_ACCESS, }, + { .reg = REG_BLNEWFILTERA_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BLNEWFILTERB_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THCOMPARE_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THDELTA1_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_THDELTA2_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_JUDGE_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PIDCONTROLCFG, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PIDCFG_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PIDCFG_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PIDCFG_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PIDCFG_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PIDCFG_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PIDCFG_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PIDCFG_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PIDCFG_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_PIDCFG_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + + //STAT MAP + { .reg = REG_WST, .rw = REG_RD_ACCESS, }, + { .reg = REG_STAT0, .rw = REG_RD_ACCESS, }, + { .reg = REG_STAT1, .rw = REG_RD_ACCESS, }, + { .reg = REG_STAT2, .rw = REG_RD_ACCESS, }, + { .reg = REG_STAT3, .rw = REG_RD_ACCESS, }, + { .reg = REG_STAT4, .rw = REG_RD_ACCESS, }, + { .reg = REG_STAT5, .rw = REG_RD_ACCESS, }, + { .reg = REG_STAT7, .rw = REG_RD_ACCESS, }, + { .reg = REG_STAT8, .rw = REG_RD_ACCESS, }, + { .reg = REG_STAT9, .rw = REG_RD_ACCESS, }, + { .reg = REG_STAT10, .rw = REG_RD_ACCESS, }, + { .reg = REG_STAT11, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_STAT12, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_STAT13, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_STAT14, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_FWVER, .rw = REG_RD_ACCESS, }, + + //DATA MAP + { .reg = REG_BASELINE_CH0, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH0, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH1, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH1, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH2, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH2, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH3, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH3, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH4, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH4, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH5, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH5, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH6, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH6, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH7, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH7, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_CH8, .rw = REG_RD_ACCESS, }, + { .reg = REG_DIFF_CH8, .rw = REG_RD_ACCESS, }, + + //SFR MAP + { .reg = REG_IRQSRC, .rw = REG_RD_ACCESS, }, + { .reg = REG_IRQEN, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_I2CTIMEOUTEN, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_ANACFG, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_CHIPSTAT, .rw = REG_RD_ACCESS, }, + { .reg = REG_CHIP_ID0, .rw = REG_RD_ACCESS, }, + { .reg = REG_CHIP_ID1, .rw = REG_RD_ACCESS, }, + { .reg = REG_CHIP_ID2, .rw = REG_RD_ACCESS, }, + { .reg = REG_I3C_OSC_EN, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + + //MAP + { .reg = REG_BASELINE_OLD_CH0, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_PRE_CH0, .rw = REG_RD_ACCESS, }, + { .reg = REG_AFECFG1_M_CH0, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BASELINE_OLD_CH1, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_PRE_CH1, .rw = REG_RD_ACCESS, }, + { .reg = REG_AFECFG1_M_CH1, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BASELINE_OLD_CH2, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_PRE_CH2, .rw = REG_RD_ACCESS, }, + { .reg = REG_AFECFG1_M_CH2, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BASELINE_OLD_CH3, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_PRE_CH3, .rw = REG_RD_ACCESS, }, + { .reg = REG_AFECFG1_M_CH3, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BASELINE_OLD_CH4, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_PRE_CH4, .rw = REG_RD_ACCESS, }, + { .reg = REG_AFECFG1_M_CH4, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BASELINE_OLD_CH5, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_PRE_CH5, .rw = REG_RD_ACCESS, }, + { .reg = REG_AFECFG1_M_CH5, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BASELINE_OLD_CH6, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_PRE_CH6, .rw = REG_RD_ACCESS, }, + { .reg = REG_AFECFG1_M_CH6, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BASELINE_OLD_CH7, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_PRE_CH7, .rw = REG_RD_ACCESS, }, + { .reg = REG_AFECFG1_M_CH7, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, + { .reg = REG_BASELINE_OLD_CH8, .rw = REG_RD_ACCESS, }, + { .reg = REG_BASELINE_PRE_CH8, .rw = REG_RD_ACCESS, }, + { .reg = REG_AFECFG1_M_CH8, .rw = REG_RD_ACCESS | REG_WR_ACCESS, }, +}; +static const uint32_t aw963xx_reg_default[] = { + 0x0000,0x000000FF, + 0x0004,0x00000FFF, + 0x0008,0x00000FFF, + 0x000C,0x00000064, + 0x0010,0x0000705F, + 0x0014,0x80000000, + 0x0060,0x00000000, + 0x0064,0x00000000, + 0x0068,0x00000000, + 0x006C,0x00000000, + 0x0070,0x00000000, + 0x0074,0x00000000, + 0x0078,0x00000000, + 0x007C,0x00000000, + 0x0080,0x00000000, + 0x0084,0x00000000, + 0x0088,0x00000000, + 0x008C,0x00000000, + 0x0168,0x00000000, + 0x016C,0x003F0000, + 0x0170,0x00050100, + 0x0174,0x00000000, + 0x0178,0x341C9207, + 0x017C,0x00008000, + 0x0180,0x00000909, + 0x0184,0x00000001, + 0x0188,0x00000000, + 0x0318,0x00000000, + 0x031C,0x003F0000, + 0x0320,0x00050100, + 0x0324,0x00000000, + 0x0328,0x341C9207, + 0x032C,0x00008000, + 0x0330,0x00000909, + 0x0334,0x00000008, + 0x0338,0x00000000, + 0x04C8,0x00000000, + 0x04CC,0x003F0000, + 0x04D0,0x00050100, + 0x04D4,0x00000000, + 0x04D8,0x341C9207, + 0x04DC,0x00008000, + 0x04E0,0x00000909, + 0x04E4,0x00000040, + 0x04E8,0x00000000, + 0x0678,0x00000000, + 0x067C,0x003F0000, + 0x0680,0x00050100, + 0x0684,0x00000000, + 0x0688,0x341C9207, + 0x068C,0x00008000, + 0x0690,0x00000909, + 0x0694,0x00000200, + 0x0698,0x00000000, + 0x0828,0x00000000, + 0x082C,0x003F0000, + 0x0830,0x00050100, + 0x0834,0x00000000, + 0x0838,0x341C9207, + 0x083C,0x00008000, + 0x0840,0x00000909, + 0x0844,0x00001000, + 0x0848,0x00000000, + 0x09D8,0x00000000, + 0x09DC,0x003F0000, + 0x09E0,0x00050100, + 0x09E4,0x00000000, + 0x09E8,0x341C9207, + 0x09EC,0x00008000, + 0x09F0,0x00000909, + 0x09F4,0x00008000, + 0x09F8,0x00000000, + 0x0B88,0x00000000, + 0x0B8C,0x003F0000, + 0x0B90,0x00050100, + 0x0B94,0x00000000, + 0x0B98,0x341C9207, + 0x0B9C,0x00008000, + 0x0BA0,0x00000909, + 0x0BA4,0x00040000, + 0x0BA8,0x00000000, + 0x0D38,0x00000000, + 0x0D3C,0x003F0000, + 0x0D40,0x00050100, + 0x0D44,0x00000000, + 0x0D48,0x341C9207, + 0x0D4C,0x00008000, + 0x0D50,0x00000909, + 0x0D54,0x00200000, + 0x0D58,0x00000000, + 0x0EE8,0x00000000, + 0x0EEC,0x003F0000, + 0x0EF0,0x00050100, + 0x0EF4,0x00000000, + 0x0EF8,0x341C9207, + 0x0EFC,0x00008000, + 0x0F00,0x00000909, + 0x0F04,0x00000000, + 0x0F08,0x00000000, + 0x1098,0x00000000, + 0x109C,0x003F0000, + 0x10A0,0x00050100, + 0x10A4,0x00000000, + 0x10A8,0x341C9207, + 0x10AC,0x00008000, + 0x10B0,0x00000909, + 0x10B4,0x00000000, + 0x10B8,0x00000000, + 0x1248,0x00000000, + 0x124C,0x003F0000, + 0x1250,0x00050100, + 0x1254,0x00000000, + 0x1258,0x341C9207, + 0x125C,0x00008000, + 0x1260,0x00000909, + 0x1264,0x00000000, + 0x1268,0x00000000, + 0x13F8,0x00000000, + 0x13FC,0x003F0000, + 0x1400,0x00050100, + 0x1404,0x00000000, + 0x1408,0x341C9207, + 0x140C,0x00008000, + 0x1410,0x00000909, + 0x1414,0x00000000, + 0x1418,0x00000000, + 0x018C,0xE0400000, + 0x0190,0x00000000, + 0x0194,0x00000000, + 0x0198,0x000A0000, + 0x019C,0x000008D2, + 0x01A0,0x00000000, + 0x01A4,0x00000040, + 0x01A8,0x000186A0, + 0x01AC,0x00030D40, + 0x01B0,0x00061A80, + 0x01B4,0x000C3500, + 0x01B8,0x00000000, + 0x01BC,0x00000000, + 0x01C0,0x00000000, + 0x01C4,0x00000000, + 0x01C8,0x00000000, + 0x01CC,0x00000000, + 0x033C,0xE0400000, + 0x0340,0x00000000, + 0x0344,0x00000000, + 0x0348,0x000A0000, + 0x034C,0x000008D2, + 0x0350,0x00000000, + 0x0354,0x00000040, + 0x0358,0x000186A0, + 0x035C,0x00030D40, + 0x0360,0x00061A80, + 0x0364,0x000C3500, + 0x0368,0x00000000, + 0x036C,0x00000000, + 0x0370,0x00000000, + 0x0374,0x00000000, + 0x0378,0x00000000, + 0x037C,0x00000000, + 0x04EC,0xE0400000, + 0x04F0,0x00000000, + 0x04F4,0x00000000, + 0x04F8,0x000A0000, + 0x04FC,0x000008D2, + 0x0500,0x00000000, + 0x0504,0x00000040, + 0x0508,0x000186A0, + 0x050C,0x00030D40, + 0x0510,0x00061A80, + 0x0514,0x000C3500, + 0x0518,0x00000000, + 0x051C,0x00000000, + 0x0520,0x00000000, + 0x0524,0x00000000, + 0x0528,0x00000000, + 0x052C,0x00000000, + 0x069C,0xE0400000, + 0x06A0,0x00000000, + 0x06A4,0x00000000, + 0x06A8,0x000A0000, + 0x06AC,0x000008D2, + 0x06B0,0x00000000, + 0x06B4,0x00000040, + 0x06B8,0x000186A0, + 0x06BC,0x00030D40, + 0x06C0,0x00061A80, + 0x06C4,0x000C3500, + 0x06C8,0x00000000, + 0x06CC,0x00000000, + 0x06D0,0x00000000, + 0x06D4,0x00000000, + 0x06D8,0x00000000, + 0x06DC,0x00000000, + 0x084C,0xE0400000, + 0x0850,0x00000000, + 0x0854,0x00000000, + 0x0858,0x000A0000, + 0x085C,0x000008D2, + 0x0860,0x00000000, + 0x0864,0x00000040, + 0x0868,0x000186A0, + 0x086C,0x00030D40, + 0x0870,0x00061A80, + 0x0874,0x000C3500, + 0x0878,0x00000000, + 0x087C,0x00000000, + 0x0880,0x00000000, + 0x0884,0x00000000, + 0x0888,0x00000000, + 0x088C,0x00000000, + 0x09FC,0xE0400000, + 0x0A00,0x00000000, + 0x0A04,0x00000000, + 0x0A08,0x000A0000, + 0x0A0C,0x000008D2, + 0x0A10,0x00000000, + 0x0A14,0x00000040, + 0x0A18,0x000186A0, + 0x0A1C,0x00030D40, + 0x0A20,0x00061A80, + 0x0A24,0x000C3500, + 0x0A28,0x00000000, + 0x0A2C,0x00000000, + 0x0A30,0x00000000, + 0x0A34,0x00000000, + 0x0A38,0x00000000, + 0x0A3C,0x00000000, + 0x0BAC,0xE0400000, + 0x0BB0,0x00000000, + 0x0BB4,0x00000000, + 0x0BB8,0x000A0000, + 0x0BBC,0x000008D2, + 0x0BC0,0x00000000, + 0x0BC4,0x00000040, + 0x0BC8,0x000186A0, + 0x0BCC,0x00030D40, + 0x0BD0,0x00061A80, + 0x0BD4,0x000C3500, + 0x0BD8,0x00000000, + 0x0BDC,0x00000000, + 0x0BE0,0x00000000, + 0x0BE4,0x00000000, + 0x0BE8,0x00000000, + 0x0BEC,0x00000000, + 0x0D5C,0xE0400000, + 0x0D60,0x00000000, + 0x0D64,0x00000000, + 0x0D68,0x000A0000, + 0x0D6C,0x000008D2, + 0x0D70,0x00000000, + 0x0D74,0x00000040, + 0x0D78,0x000186A0, + 0x0D7C,0x00030D40, + 0x0D80,0x00061A80, + 0x0D84,0x000C3500, + 0x0D88,0x00000000, + 0x0D8C,0x00000000, + 0x0D90,0x00000000, + 0x0D94,0x00000000, + 0x0D98,0x00000000, + 0x0D9C,0x00000000, + 0x0F0C,0xE0400000, + 0x0F10,0x00000000, + 0x0F14,0x00000000, + 0x0F18,0x000A0000, + 0x0F1C,0x000008D2, + 0x0F20,0x00000000, + 0x0F24,0x00000040, + 0x0F28,0x000186A0, + 0x0F2C,0x00000000, + 0x0F30,0x00000000, + 0x0F34,0x00000000, + 0x0F38,0x00000000, + 0x0F3C,0x00000000, + 0x0F40,0x00000000, + 0x0F44,0x00000000, + 0x0F48,0x00000000, + 0x0F4C,0x00000000, + 0x10BC,0xE0400000, + 0x10C0,0x00000000, + 0x10C4,0x00000000, + 0x10C8,0x000A0000, + 0x10CC,0x000008D2, + 0x10D0,0x00000000, + 0x10D4,0x00000040, + 0x10D8,0x00000000, + 0x10DC,0x00000000, + 0x10E0,0x00000000, + 0x10E4,0x00000000, + 0x10E8,0x00000000, + 0x10EC,0x00000000, + 0x10F0,0x00000000, + 0x10F4,0x00000000, + 0x10F8,0x00000000, + 0x10FC,0x00000000, + 0x126C,0xE0400000, + 0x1270,0x00000000, + 0x1274,0x00000000, + 0x1278,0x000A0000, + 0x127C,0x000008D2, + 0x1280,0x00000000, + 0x1284,0x00000040, + 0x1288,0x00000000, + 0x128C,0x00000000, + 0x1290,0x00000000, + 0x1294,0x00000000, + 0x1298,0x00000000, + 0x129C,0x00000000, + 0x12A0,0x00000000, + 0x12A4,0x00000000, + 0x12A8,0x00000000, + 0x12AC,0x00000000, + 0x141C,0xE0400000, + 0x1420,0x00000000, + 0x1424,0x00000000, + 0x1428,0x000A0000, + 0x142C,0x000008D2, + 0x1430,0x00000000, + 0x1434,0x00000040, + 0x1438,0x00000000, + 0x143C,0x00000000, + 0x1440,0x00000000, + 0x1444,0x00000000, + 0x1448,0x00000000, + 0x144C,0x00000000, + 0x1450,0x00000000, + 0x1454,0x00000000, + 0x1458,0x00000000, + 0x145C,0x00000000, + 0x01D0,0x00000000, + 0x01D4,0x00000000, + 0x01D8,0x00000000, + 0x01DC,0x00000000, + 0x01E0,0x00000000, + 0x01E4,0xFFFFFFFF, + 0x01E8,0x00000000, + 0x01EC,0x00000000, + 0x01F0,0x00000000, + 0x01F4,0x00000000, + 0x01F8,0x070004B0, + 0x01FC,0x0E000000, + 0x0200,0xF2000000, + 0x0204,0x02000000, + 0x0208,0x02000000, + 0x020C,0x00002000, + 0x0210,0x00010000, + 0x0214,0x80007530, + 0x0220,0x00000000, + 0x0224,0x00000000, + 0x0380,0x00000000, + 0x0384,0x00000000, + 0x0388,0x00000000, + 0x038C,0x00000000, + 0x0390,0x00000000, + 0x0394,0xFFFFFFFF, + 0x0398,0x00000000, + 0x039C,0x00000000, + 0x03A0,0x00000000, + 0x03A4,0x00000000, + 0x03A8,0x070004B0, + 0x03AC,0x0E000000, + 0x03B0,0xF2000000, + 0x03B4,0x02000000, + 0x03B8,0x02000000, + 0x03BC,0x00002000, + 0x03C0,0x00010000, + 0x03C4,0x80007530, + 0x03D0,0x00000000, + 0x03D4,0x00000000, + 0x0530,0x00000000, + 0x0534,0x00000000, + 0x0538,0x00000000, + 0x053C,0x00000000, + 0x0540,0x00000000, + 0x0544,0xFFFFFFFF, + 0x0548,0x00000000, + 0x054C,0x00000000, + 0x0550,0x00000000, + 0x0554,0x00000000, + 0x0558,0x070004B0, + 0x055C,0x0E000000, + 0x0560,0xF2000000, + 0x0564,0x02000000, + 0x0568,0x02000000, + 0x056C,0x00002000, + 0x0570,0x00010000, + 0x0574,0x80007530, + 0x0580,0x00000000, + 0x0584,0x00000000, + 0x06E0,0x00000000, + 0x06E4,0x00000000, + 0x06E8,0x00000000, + 0x06EC,0x00000000, + 0x06F0,0x00000000, + 0x06F4,0xFFFFFFFF, + 0x06F8,0x00000000, + 0x06FC,0x00000000, + 0x0700,0x00000000, + 0x0704,0x00000000, + 0x0708,0x070004B0, + 0x070C,0x0E000000, + 0x0710,0xF2000000, + 0x0714,0x02000000, + 0x0718,0x02000000, + 0x071C,0x00002000, + 0x0720,0x00010000, + 0x0724,0x80007530, + 0x0730,0x00000000, + 0x0734,0x00000000, + 0x0890,0x00000000, + 0x0894,0x00000000, + 0x0898,0x00000000, + 0x089C,0x00000000, + 0x08A0,0x00000000, + 0x08A4,0xFFFFFFFF, + 0x08A8,0x00000000, + 0x08AC,0x00000000, + 0x08B0,0x00000000, + 0x08B4,0x00000000, + 0x08B8,0x070004B0, + 0x08BC,0x0E000000, + 0x08C0,0xF2000000, + 0x08C4,0x02000000, + 0x08C8,0x02000000, + 0x08CC,0x00002000, + 0x08D0,0x00010000, + 0x08D4,0x80007530, + 0x08E0,0x00000000, + 0x08E4,0x00000000, + 0x0A40,0x00000000, + 0x0A44,0x00000000, + 0x0A48,0x00000000, + 0x0A4C,0x00000000, + 0x0A50,0x00000000, + 0x0A54,0xFFFFFFFF, + 0x0A58,0x00000000, + 0x0A5C,0x00000000, + 0x0A60,0x00000000, + 0x0A64,0x00000000, + 0x0A68,0x070004B0, + 0x0A6C,0x0E000000, + 0x0A70,0xF2000000, + 0x0A74,0x02000000, + 0x0A78,0x02000000, + 0x0A7C,0x00002000, + 0x0A80,0x00010000, + 0x0A84,0x80007530, + 0x0A90,0x00000000, + 0x0A94,0x00000000, + 0x0BF0,0x00000000, + 0x0BF4,0x00000000, + 0x0BF8,0x00000000, + 0x0BFC,0x00000000, + 0x0C00,0x00000000, + 0x0C04,0xFFFFFFFF, + 0x0C08,0x00000000, + 0x0C0C,0x00000000, + 0x0C10,0x00000000, + 0x0C14,0x00000000, + 0x0C18,0x070004B0, + 0x0C1C,0x0E000000, + 0x0C20,0xF2000000, + 0x0C24,0x02000000, + 0x0C28,0x02000000, + 0x0C2C,0x00002000, + 0x0C30,0x00010000, + 0x0C34,0x80007530, + 0x0C40,0x00000000, + 0x0C44,0x00000000, + 0x0DA0,0x00000000, + 0x0DA4,0x00000000, + 0x0DA8,0x00000000, + 0x0DAC,0x00000000, + 0x0DB0,0x00000000, + 0x0DB4,0xFFFFFFFF, + 0x0DB8,0x00000000, + 0x0DBC,0x00000000, + 0x0DC0,0x00000000, + 0x0DC4,0x00000000, + 0x0DC8,0x070004B0, + 0x0DCC,0x0E000000, + 0x0DD0,0xF2000000, + 0x0DD4,0x02000000, + 0x0DD8,0x02000000, + 0x0DDC,0x00002000, + 0x0DE0,0x00010000, + 0x0DE4,0x80007530, + 0x0DF0,0x00000000, + 0x0DF4,0x00000000, + 0x0F50,0x00000000, + 0x0F54,0x00000000, + 0x0F58,0x00000000, + 0x0F5C,0x00000000, + 0x0F60,0x00000000, + 0x0F64,0xFFFFFFFF, + 0x0F68,0x00000000, + 0x0F6C,0x00000000, + 0x0F70,0x00000000, + 0x0F74,0x00000000, + 0x0F78,0x070004B0, + 0x0F7C,0x0E000000, + 0x0F80,0xF2000000, + 0x0F84,0x02000000, + 0x0F88,0x02000000, + 0x0F8C,0x00002000, + 0x0F90,0x00010000, + 0x0F94,0x80007530, + 0x0FA0,0x00000000, + 0x0FA4,0x00000000, + 0x1100,0x00000000, + 0x1104,0x00000000, + 0x1108,0x00000000, + 0x110C,0x00000000, + 0x1110,0x00000000, + 0x1114,0xFFFFFFFF, + 0x1118,0x00000000, + 0x111C,0x00000000, + 0x1120,0x00000000, + 0x1124,0x00000000, + 0x1128,0x070004B0, + 0x112C,0x0E000000, + 0x1130,0xF2000000, + 0x1134,0x02000000, + 0x1138,0x02000000, + 0x113C,0x00002000, + 0x1140,0x00010000, + 0x1144,0x80007530, + 0x1150,0x00000000, + 0x1154,0x00000000, + 0x12B0,0x00000000, + 0x12B4,0x00000000, + 0x12B8,0x00000000, + 0x12BC,0x00000000, + 0x12C0,0x00000000, + 0x12C4,0xFFFFFFFF, + 0x12C8,0x00000000, + 0x12CC,0x00000000, + 0x12D0,0x00000000, + 0x12D4,0x00000000, + 0x12D8,0x070004B0, + 0x12DC,0x0E000000, + 0x12E0,0xF2000000, + 0x12E4,0x02000000, + 0x12E8,0x02000000, + 0x12EC,0x00002000, + 0x12F0,0x00010000, + 0x12F4,0x80007530, + 0x1300,0x00000000, + 0x1304,0x00000000, + 0x1460,0x00000000, + 0x1464,0x00000000, + 0x1468,0x00000000, + 0x146C,0x00000000, + 0x1470,0x00000000, + 0x1474,0xFFFFFFFF, + 0x1478,0x00000000, + 0x147C,0x00000000, + 0x1480,0x00000000, + 0x1484,0x00000000, + 0x1488,0x070004B0, + 0x148C,0x0E000000, + 0x1490,0xF2000000, + 0x1494,0x02000000, + 0x1498,0x02000000, + 0x149C,0x00002000, + 0x14A0,0x00010000, + 0x14A4,0x80007530, + 0x14B0,0x00000000, + 0x14B4,0x00000000, + 0xF084,0x00000006, + 0x004C,0xFFFFFFFF, + 0x0050,0xFFFFFFFF, + 0x0054,0xFFFFFFFF, + 0x0058,0xFFFFFFFF, + 0x0090,0x00000000, + 0x0094,0x00000064, + 0x0098,0x40000000, + 0x009C,0x00000000, + 0x00A0,0x00140014, + 0x00A4,0x0019000D, + 0x00A8,0x0096004B, +}; +#endif diff --git a/include/linux/input/awinic/aw_sar.h b/include/linux/input/awinic/aw_sar.h new file mode 100644 index 000000000000..cf0ebfc425d1 --- /dev/null +++ b/include/linux/input/awinic/aw_sar.h @@ -0,0 +1,13 @@ +#ifndef AW_SAR_H_ +#define AW_SAR_H_ + +void aw_sar_disable_irq(struct aw_sar *p_sar); +void aw_sar_enable_irq(struct aw_sar *p_sar); + +int32_t aw_sar_soft_reset(struct aw_sar *p_sar); +int32_t aw_sar_check_init_over_irq(struct aw_sar *p_sar); +int32_t aw_sar_update_fw(struct aw_sar *p_sar); +int32_t aw_sar_load_def_reg_bin(struct aw_sar *p_sar); +void aw_sar_mode_set(struct aw_sar *p_sar, uint8_t curr_mode); +int32_t aw_sar_update_reg_set_func(struct aw_sar *p_sar); +#endif \ No newline at end of file diff --git a/include/linux/input/awinic/aw_sar_chip_interface.h b/include/linux/input/awinic/aw_sar_chip_interface.h new file mode 100644 index 000000000000..e26e06a2f584 --- /dev/null +++ b/include/linux/input/awinic/aw_sar_chip_interface.h @@ -0,0 +1,33 @@ +#ifndef _SAR_SUPPORT_CHIP_H_ +#define _SAR_SUPPORT_CHIP_H_ +#include "aw_sar_type.h" + +enum aw_sar_driver_list_t { + AW_SAR_AW9610X, + AW_SAR_AW9620X, + AW_SAR_AW963XX, + + AW_SAR_DRIVER_MAX, +}; + +int32_t aw9610x_check_chipid(void *data); +int32_t aw9610x_init(struct aw_sar *p_sar); +void aw9610x_deinit(struct aw_sar *p_sar); + +int32_t aw9620x_check_chipid(void *data); +int32_t aw9620x_init(struct aw_sar *p_sar); +void aw9620x_deinit(struct aw_sar *p_sar); + +int32_t aw963xx_check_chipid(void *data); +int32_t aw963xx_init(struct aw_sar *p_sar); +void aw963xx_deinit(struct aw_sar *p_sar); + + +static const struct aw_sar_driver_type g_aw_sar_driver_list[] = { + { AW_SAR_AW9610X, aw9610x_check_chipid, aw9610x_init, aw9610x_deinit }, + { AW_SAR_AW9620X, aw9620x_check_chipid, aw9620x_init, aw9620x_deinit }, + { AW_SAR_AW963XX, aw963xx_check_chipid, aw963xx_init, aw963xx_deinit }, +}; + + +#endif diff --git a/include/linux/input/awinic/aw_sar_comm_interface.h b/include/linux/input/awinic/aw_sar_comm_interface.h new file mode 100644 index 000000000000..796aa39004f5 --- /dev/null +++ b/include/linux/input/awinic/aw_sar_comm_interface.h @@ -0,0 +1,221 @@ +#ifndef _AW_SAR_PLAT_HW_INTERFACE_H_ +#define _AW_SAR_PLAT_HW_INTERFACE_H_ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum aw_sar_chip_list_t { + AW_SAR_NONE_CHECK_CHIP, + + SAR_AW9610X = 1 << 1, + SAR_AW9610XA = 1 << 2, + + SAR_AW96203 = 1 << 3, + SAR_AW96205 = 1 << 4, + SAR_AW96208 = 1 << 5, + + SAR_AW96303 = 1 << 6, + SAR_AW96305 = 1 << 7, + SAR_AW96308 = 1 << 8, + SAR_AW96310 = 1 << 9, + SAR_AW96312 = 1 << 10, +}; + +enum AW_SAR_UPDATE_FW_MODE { + PROT_UPDATE_FW, + REG_UPDATE_FW, +}; + +#define AW_SAR_LABLE " " +#define AW_SAR_FILENAME(x) strrchr((x), '/') ? (strrchr((x), '/') + 1) : (x) +//dev_dbg +#define AW_SAR_DEBUG_LOG +#ifdef AW_SAR_DEBUG_LOG +#define AWLOGD(dev, format, arg...) \ + do {\ + dev_dbg(dev, \ + AW_SAR_LABLE"[%s:%s:%d] "format"\n", AW_SAR_FILENAME(__FILE__), __func__, __LINE__, ##arg);\ + } while (0) +#else +#define AWLOGD(dev, format, arg...) +#endif + +#define AW_SAR_INFO_LOG +#ifdef AW_SAR_INFO_LOG +#define AWLOGI(dev, format, arg...) \ + do {\ + dev_info(dev, \ + AW_SAR_LABLE"[%s:%s:%d] "format"\n", AW_SAR_FILENAME(__FILE__), __func__, __LINE__, ##arg);\ + } while (0) +#else +#define AWLOGI(dev, format, arg...) +#endif + +#define AW_SAR_ERR_LOG +#ifdef AW_SAR_ERR_LOG +#define AWLOGE(dev, format, arg...) \ + do {\ + dev_err(dev, \ + AW_SAR_LABLE"[%s:%s:%d] "format"\n", AW_SAR_FILENAME(__FILE__), __func__, __LINE__, ##arg);\ + } while (0) +#else +#define AWLOGE(dev, format, arg...) +#endif + +#ifndef AW_TRUE +#define AW_TRUE (1) +#endif + +#ifndef AW_FALSE +#define AW_FALSE (0) +#endif + +/* +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; + +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; + +typedef uint8_t bool; +*/ +enum aw_sar_rst_val { + AW_OK, + AW_ERR, + AW_PARA_ERR, + AW_VERS_ERR, + AW_ERR_CHIPID, + AW_ERR_IRQ_INIT_OVER, + AW_PROT_UPDATE_ERR, + AW_REG_LOAD_ERR, + AW_INVALID_PARA, + AW_BIN_PARA_INVALID, + + AW_FW_CHECK_ERR, + AW_BT_CHECK_ERR, + AW_OTEHR_CHECK_ERR, + AW_BIN_NAME_CHECK_ERR, +}; + +#define AW_SAR_FLIP_U16(value) ((((value) & (0x00FF)) << (8)) | (((value) & (0xFF00)) >> (8))) +#define AW_SAR_FLIP_U32(value) ((((value) & (0x000000FF)) << (24)) | ((((value) & (0x0000FF00))) << (8)) | (((value) & (0x00FF0000)) >> (8)) | (((value) & (0xFF000000)) >> (24))) + +#ifndef OFFSET_BIT_0 +#define OFFSET_BIT_0 (0) +#endif + +#ifndef OFFSET_BIT_8 +#define OFFSET_BIT_8 (8) +#endif + +#ifndef OFFSET_BIT_16 +#define OFFSET_BIT_16 (16) +#endif + +#ifndef OFFSET_BIT_24 +#define OFFSET_BIT_24 (24) +#endif + +#ifndef WORD_LEN +#define WORD_LEN (4) +#endif + +#ifndef GET_BITS_7_0 +#define GET_BITS_7_0 (0x00FF) +#endif + +#ifndef GET_BITS_15_8 +#define GET_BITS_15_8 (0xFF00) +#endif + +#ifndef GET_BITS_24_16 +#define GET_BITS_24_16 (0x00FF0000) +#endif + +#ifndef GET_BITS_31_25 +#define GET_BITS_31_25 (0xFF000000) +#endif + +#define AW_SAR_ABS(x) (((x) > 0) ? (x) : (-(x))) +#define AW_SAR_GET_32_DATA(w, x, y, z) ((unsigned int)(((w) << 24) | ((x) << 16) | ((y) << 8) | (z))) +#define AW_SAR_GET_16_DATA(a, b) (((uint32_t)(a) << (8)) | ((uint32_t)(b) << (0))) + +enum AW_SAR_HOST_IRQ_STAT { + IRQ_ENABLE, + IRQ_DISABLE, +}; + +#define AW_SAR_BIN_NUM_MAX 100 +#define AW_SAR_HEADER_LEN 60 + +struct bin_header_info { + unsigned int header_len; /* Frame header length */ + unsigned int check_sum; /* Frame header information-Checksum */ + unsigned int header_ver; /* Frame header information-Frame header version */ + unsigned int bin_data_type; /* Frame header information-Data type */ + unsigned int bin_data_ver; /* Frame header information-Data version */ + unsigned int bin_data_len; /* Frame header information-Data length */ + unsigned int ui_ver; /* Frame header information-ui version */ + unsigned char chip_type[8]; /* Frame header information-chip type */ + unsigned int reg_byte_len; /* Frame header information-reg byte len */ + unsigned int data_byte_len; /* Frame header information-data byte len */ + unsigned int device_addr; /* Frame header information-device addr */ + unsigned int valid_data_len; /* Length of valid data obtained after parsing */ + unsigned int valid_data_addr; /* The offset address of the valid data obtained after parsing relative to info */ + + unsigned int reg_num; /* The number of registers obtained after parsing */ + unsigned int reg_data_byte_len; /* The byte length of the register obtained after parsing */ + unsigned int download_addr; /* The starting address or download address obtained after parsing */ + unsigned int app_version; /* The software version number obtained after parsing */ +}; + +struct bin_container { + unsigned int len; /* The size of the bin file obtained from the firmware */ + unsigned char data[]; /* Store the bin file obtained from the firmware */ +}; + +struct aw_bin { + char *p_addr; /* Offset pointer (backward offset pointer to obtain frame header information and important information) */ + unsigned int all_bin_parse_num; /* The number of all bin files */ + unsigned int multi_bin_parse_num; /* The number of single bin files */ + unsigned int single_bin_parse_num; /* The number of multiple bin files */ + struct bin_header_info header_info[AW_SAR_BIN_NUM_MAX]; /* Frame header information and other important data obtained after parsing */ + struct bin_container info; /* Obtained bin file data that needs to be parsed */ +}; + +//I2C communication API +extern int32_t aw_sar_i2c_read(struct i2c_client *i2c, uint16_t reg_addr16, uint32_t *reg_data32); +extern int32_t aw_sar_i2c_write(struct i2c_client *i2c, uint16_t reg_addr16, uint32_t reg_data32); +extern int32_t aw_sar_i2c_write_bits(struct i2c_client *i2c, uint16_t reg_addr16, uint32_t mask, uint32_t val); +extern int32_t aw_sar_i2c_write_seq(struct i2c_client *i2c, uint8_t *tr_data, uint16_t len); +extern int32_t aw_sar_i2c_read_seq(struct i2c_client *i2c, uint8_t *addr, uint8_t addr_len, uint8_t *data, uint16_t data_len); +extern void aw_sar_delay_ms(uint32_t ms); + +extern int aw_sar_parsing_bin_file(struct aw_bin *bin); +extern uint32_t aw_sar_pow2(uint32_t cnt); +extern int32_t aw_sar_load_reg(struct aw_bin *aw_bin, struct i2c_client *i2c); + +#endif diff --git a/include/linux/input/awinic/aw_sar_type.h b/include/linux/input/awinic/aw_sar_type.h new file mode 100644 index 000000000000..21173b7d8d29 --- /dev/null +++ b/include/linux/input/awinic/aw_sar_type.h @@ -0,0 +1,359 @@ +#ifndef _SAR_TYPE_H_ +#define _SAR_TYPE_H_ + +#include "aw_sar_comm_interface.h" + +typedef int32_t (*aw_sar_chip_other_operation_t)(void *data); +typedef void (*aw_sar_chip_other_opera_free_t)(void *data); + +enum aw_i2c_flags { + AW_SAR_I2C_WR, + AW_SAR_I2C_RD, + AW_SAR_PACKAGE_RD, +}; + +typedef int32_t (*aw_sar_bin_opera_t)(struct aw_bin *aw_bin, void *load_bin_para); +typedef int32_t (*aw_sar_bin_load_fail_opera_t)(struct aw_bin *aw_bin, void *load_bin_para); + +struct aw_sar_get_chip_info_t { + void (*p_get_chip_info_node_fn)(void *data, char *buf, ssize_t *p_len); +}; + +struct aw_sar_load_bin_t { + const uint8_t *bin_name; + aw_sar_bin_opera_t bin_opera_func; + aw_sar_bin_load_fail_opera_t bin_load_fail_opera; + + void (*p_get_prot_update_fw_node_fn)(void *data, char *buf, ssize_t *p_len); + + /*Perform different operations to update parameters*/ + int32_t (*p_update_fn)(void *data); +}; + +struct aw_sar_reg_data { + unsigned char rw; + unsigned short reg; +}; + +struct aw_sar_awrw_t { + ssize_t (*p_set_awrw_node_fn)(void *data, const char *buf, size_t count); + ssize_t (*p_get_awrw_node_fn)(void *data, char *buf); +}; + +struct aw_sar_reg_list_t { + uint8_t reg_none_access; + uint8_t reg_rd_access; + uint8_t reg_wd_access; + const struct aw_sar_reg_data *reg_perm; + uint32_t reg_num; +}; + +typedef void (*aw_sar_update_work_t)(struct work_struct *work); +struct aw_sar_update_static_t { + aw_sar_update_work_t update_work_func; + uint32_t delay_ms; +}; + +typedef irqreturn_t (*aw_sar_irq_t)(int irq, void *data); +typedef uint32_t (*sar_rc_irqscr_t)(void *i2c); +//If the return value is 1, there is an initialization completion interrupt; if the return value is 0, there is no +typedef uint32_t (*aw_sar_is_init_over_irq)(uint32_t irq_status); +typedef void (*aw_sar_irq_spec_handler_t)(uint32_t irq_status, void *data); + +struct aw_sar_check_chipid_t { + /*Read chipid and check chipid, Must be implemented externally*/ + int32_t (*p_check_chipid_fn)(void *data); +}; + +struct aw_sar_irq_init_t { + unsigned long flags; + unsigned long irq_flags; + irq_handler_t handler; + irq_handler_t thread_fn; + /*Interrupt processing parameters*/ + sar_rc_irqscr_t rc_irq_fn; + //aw_sar_is_init_over_irq is_init_over_irq_fn; + aw_sar_irq_spec_handler_t irq_spec_handler_fn; + + /*Use a different initialization interrupt to initialize the operation*/ + int32_t (*p_irq_init_fn)(void *data); + /*Release interrupt resource*/ +// void const (*p_irq_deinit_fn)(void *data); + int (*p_irq_deinit_fn)(void *data); +}; + +struct aw_sar_pm_t { + uint32_t suspend_set_mode; + uint32_t resume_set_mode; + uint32_t shutdown_set_mode; + //system api + int32_t (*p_suspend_fn)(void *data); + int32_t (*p_resume_fn)(void *data); + int32_t (*p_shutdown_fn)(void *data); +}; + +struct aw_sar_chip_mode_t { + uint32_t init_mode; + uint32_t active; + uint32_t pre_init_mode; +}; + +struct aw_sar_regulator_config_t { + //Note that "_sar_num" after VCC name is defined by SAR C auto add + const uint8_t *vcc_name; + int32_t min_uV; + int32_t max_uV; +}; + +struct aw_channels_info { + uint16_t used; + uint32_t last_channel_info; + struct input_dev *input; + uint8_t name[20]; +}; + +struct aw_sar_dts_info { + uint32_t sar_num; + int32_t irq_gpio; + uint32_t channel_use_flag; + bool use_regulator_flag; + bool use_inter_pull_up; + bool use_pm; + bool update_fw_flag; + bool use_plug_cail_flag; +}; + +struct aw_sar_irq_init_comm_t { + int32_t to_irq; + uint8_t host_irq_stat; + void *data; + uint8_t label[30]; + uint8_t dev_id[30]; +}; + +struct aw_sar_load_bin_comm_t { + uint8_t bin_name[30]; + aw_sar_bin_opera_t bin_opera_func; + aw_sar_bin_load_fail_opera_t bin_load_fail_opera_func; +}; + +struct aw_awrw_info { + uint8_t rw_flag; + uint8_t addr_len; + uint8_t data_len; + uint8_t reg_num; + uint32_t i2c_tranfar_data_len; + uint8_t *p_i2c_tranfar_data; +}; + +/****************mode set start******************/ +typedef void (*sar_enable_clock_t)(void *i2c); +typedef void (*sar_operation_irq_t)(int to_irq); +typedef void (*sar_mode_update_t)(void *i2c); + +struct aw_sar_mode_switch_ops { + sar_enable_clock_t enable_clock; + sar_rc_irqscr_t rc_irqscr; + sar_mode_update_t mode_update; +}; + +struct aw_sar_chip_mode { + uint8_t curr_mode; + uint8_t last_mode; +}; + +struct aw_sar_mode_set_t { + uint8_t chip_id; + struct aw_sar_chip_mode chip_mode; + struct aw_sar_mode_switch_ops mode_switch_ops; +}; + +struct aw_sar_mode_t { + const struct aw_sar_mode_set_t *mode_set_arr; + uint8_t mode_set_arr_len; + ssize_t (*p_set_mode_node_fn)(void *data, uint8_t curr_mode); + ssize_t (*p_get_mode_node_fn)(void *data, char *buf); +}; +/********************mode set end****************/ + +struct aw_sar_init_over_irq_t { + int16_t wait_times; + uint8_t daley_step; + uint32_t reg_irqsrc; + uint32_t irq_offset_bit; + uint32_t irq_mask; + uint32_t irq_flag; + /*Perform different verification initialization to complete the interrupt operation*/ + int32_t (*p_check_init_over_irq_fn)(void *data); + /*When initialization fails, get the corresponding error type and apply it to the chip with flash*/ + int32_t (*p_get_err_type_fn)(void *data); +}; + +struct aw_sar_soft_rst_t { + uint16_t reg_rst; + uint32_t reg_rst_val; + uint32_t delay_ms; + /*Perform different soft reset operations*/ + int32_t (*p_soft_reset_fn)(void *data); +}; + +struct aw_sar_aot_t { + uint32_t aot_reg; + uint32_t aot_mask; + uint32_t aot_flag; + ssize_t (*p_set_aot_node_fn)(void *data); +}; + +struct aw_sar_diff_t { + uint16_t diff0_reg; + uint16_t diff_step; + //Data format:S21.10, Floating point types generally need to be removed + uint32_t rm_float; + + ssize_t (*p_get_diff_node_fn)(void *data, char *buf); +}; + +struct aw_sar_offset_t { + ssize_t (*p_get_offset_node_fn)(void *data, char *buf); +}; + +struct aw_sar_pinctrl { + struct pinctrl *pinctrl; + struct pinctrl_state *default_sta; + struct pinctrl_state *int_out_high; + struct pinctrl_state *int_out_low; +}; + +//update reg node +struct aw_sar_para_load_t { + const uint32_t *reg_arr; + uint32_t reg_arr_len; +}; + +struct aw_sar_platform_config { + /*The chip needs to parse more DTS contents for addition*/ + int32_t (*p_add_parse_dts_fn)(void *data); + + const struct aw_sar_regulator_config_t *p_regulator_config; + + /*The chip needs to add more nodes*/ + int32_t (*p_add_node_create_fn)(void *data); + /*Release the added node*/ + int32_t (*p_add_node_free_fn)(void *data); + + /*Use a different initialization interrupt to initialize the operation*/ + int32_t (*p_input_init_fn)(void *data); + /*Release input resource*/ + int32_t (*p_input_deinit_fn)(void *data); + + //The parameters passed in are required for interrupt initialization + const struct aw_sar_irq_init_t *p_irq_init; + + //The chip is set to different modes in different power management interfaces + const struct aw_sar_pm_t *p_pm_chip_mode; +}; + +/*Parameter passed in by different SAR sensors. + It must be implemented in each sensor code. + If it is not necessary that some members can be assigned null, + the corresponding function will not be implemented +*/ +struct aw_sar_chip_config { + uint8_t ch_num_max; //Number of channels of the chip + + //Chip related platform content configuration + const struct aw_sar_platform_config *p_platform_config; + //Parameters required for verification of chipid + const struct aw_sar_check_chipid_t *p_check_chipid; + //Parameters required for soft reset + const struct aw_sar_soft_rst_t *p_soft_rst; + //Verify the parameters required to initialize a complete interrupt + const struct aw_sar_init_over_irq_t *p_init_over_irq; + //Parameters required for load boot bin file, + //If the chip does not have flash, please ignore and assign the value to null + const struct aw_sar_load_bin_t *p_fw_bin; + //Parameters required for load register bin file + const struct aw_sar_load_bin_t *p_reg_bin; + //The mode set before and after the initialization of the chip + const struct aw_sar_chip_mode_t *p_chip_mode; + + //Node usage parameters + //Register permission table + const struct aw_sar_reg_list_t *p_reg_list; + //Default register table + const struct aw_sar_para_load_t *p_reg_arr; + //Parameters required for set Auto-Offset-Tuning(aot) + const struct aw_sar_aot_t *p_aot; + //Parameters required for get chip diff val + const struct aw_sar_diff_t *p_diff; + //Parameters required for get chip offset val + const struct aw_sar_offset_t *p_offset; + //Set the parameters of different working modes of the chip + const struct aw_sar_mode_t *p_mode; + //Upgrading firmware using the debug node protocol + const struct aw_sar_load_bin_t *p_prox_fw; + //Upgrading firmware using the debug node reg + const struct aw_sar_load_bin_t *p_reg_fw; + //Obtain the necessary information of the chip + const struct aw_sar_get_chip_info_t *p_get_chip_info; + //Continuous read/write register interface + const struct aw_sar_awrw_t *p_aw_sar_awrw; + //Parameters required for load boot bin file, + //If the chip does not have flash, please ignore and assign the value to null + const struct aw_sar_load_bin_t *p_boot_bin; + + /*Other operations during initialization, Add according to different usage*/ + aw_sar_chip_other_operation_t p_other_operation; + /*If requested by resources, please release*/ + aw_sar_chip_other_opera_free_t p_other_opera_free; +}; + +struct aw_sar { + struct i2c_client *i2c; + struct device *dev; + struct regulator *vcc; + struct delayed_work update_work; + //Set pin pull-up mode + struct aw_sar_pinctrl pinctrl; + struct work_struct ps_notify_work; + struct notifier_block ps_notif; + + bool ps_is_present; + uint8_t chip_type; + uint8_t chip_name[20]; + + bool power_enable; + bool fw_fail_flag; + uint8_t last_mode; + uint8_t ret_val; + uint8_t curr_use_driver_type; + int32_t prot_update_state; + + uint8_t intrrupt_init_state; + + //Parameters related to platform logic + struct aw_sar_dts_info dts_info; + struct aw_sar_load_bin_comm_t load_bin; + struct aw_sar_irq_init_comm_t irq_init; + struct aw_awrw_info awrw_info; + struct aw_channels_info *channels_arr; + + //Private arguments required for public functions + const struct aw_sar_chip_config *p_sar_para; + //Private arguments required for private functions + void *priv_data; +}; + +//Determine whether the chip exists by verifying chipid +typedef int32_t (*aw_sar_who_am_i_t)(void *data); +typedef int32_t (*aw_sar_chip_init_t)(struct aw_sar *p_sar); +typedef void (*aw_sar_chip_deinit_t)(struct aw_sar *p_sar); + +struct aw_sar_driver_type { + uint8_t driver_type; + aw_sar_who_am_i_t p_who_am_i; + aw_sar_chip_init_t p_chip_init; + aw_sar_chip_deinit_t p_chip_deinit; +}; + +#endif diff --git a/include/linux/input/awinic/aw_update_fw.h b/include/linux/input/awinic/aw_update_fw.h new file mode 100644 index 000000000000..d2bd469525cc --- /dev/null +++ b/include/linux/input/awinic/aw_update_fw.h @@ -0,0 +1,335 @@ +#ifndef __AW_UPDATE_H_ +#define __AW_UPDATE_H_ +#include "aw_sar_comm_interface.h" +#include "aw_sar_type.h" +#include "aw_update_fw_interface.h" +#ifdef __cplusplus +extern "C" { +#endif + +#define AW_SAR_LOG_TAG "awinic sar " +#define AW_FW_UPDATE_DEBUG_LOG +#ifdef AW_FW_UPDATE_DEBUG_LOG +#define AWLOGD_FW(format, arg...) \ + do {\ + pr_info(AW_SAR_LOG_TAG "[%s:%d] "format"\n", __func__, __LINE__, ##arg);\ + } while (0) +#else +#define AWLOGD_FW(format, arg...) +#endif + +#define AW_FW_UPDATE_DEBUG_LOG +#ifdef AW_FW_UPDATE_DEBUG_LOG +#define AWLOGI_FW(format, arg...) \ + do {\ + pr_info(AW_SAR_LOG_TAG "[%s:%d] "format"\n", __func__, __LINE__, ##arg);\ + } while (0) +#else +#define AWLOGI_FW(format, arg...) +#endif + +#define AW_FW_UPDATE_DEBUG_LOG +#ifdef AW_FW_UPDATE_DEBUG_LOG +#define AWLOGE_FW(format, arg...) \ + do {\ + pr_err(AW_SAR_LOG_TAG "[%s:%d] "format"\n", __func__, __LINE__, ##arg);\ + } while (0) +#else +#define AWLOGE_FW(format, arg...) +#endif + +#define REG_IRQSRC (0x4410) + +//driver user +#define REG_ACCESSEN (0xFF20) +#define REG_MCFG (0x4444) +#define REG_ISPGO (0x4714) +#define REG_PMU_CFG (0x4820) +#define REG_ARRAY2_EW_EN (0x4794) +#define REG_ISPADR (0x4704) +#define REG_ISPWDATA (0x4708) +#define REG_ISPRDATA (0x470C) +#define REG_ISPCMD (0x4710) +#define REG_T_RCV (0x472C) +#define REG_CMD (0x4408) +#define REG_BOOT_LOADER_ACTIVE (0x4748) + +#define REG_OPEN_APB_ACCESS_EN (0x3c00f091) +#define REG_SET_PMU_CFG (0x6) +#define REG_SET_BTROM_EW_EN (0x5a637955) +#define REG_ISP_CMD_CONFIG (0x0c) +#define REG_SET_ISP_GO (0x1) +#define REG_ENSET_PMU_CFG (0x4) +#define REG_ACCESS_MAIN_ARR (0x05) +#define REG_SET_T_RCV (0xf0) +#define REG_SET_T_RCV_EN (0x16) +#define REG_OPEN_REG_ACCESS_EN (0x3c00ffff) +#define REG_OPEN_MCFG_EN (0x10000) +#define REG_ENSET_BTROM_EW_EN (0x0) +#define REG_SET_MCFG00 (0x0) +#define REG_ISP_CMD_MAIN_ARR (0X03) +#define REG_HOSTCTRL_EN (0x1) +#define REG_ACCESSEN_OPEN (0x3c00F091) +#define REG_ACCESSEN_CLOSE (0x3c00f011) +#define AW_REG_FLASH_WAKE_UP (0x4700) +#define REG_UPDATA_DIS (0x4744) +#define AW_REG_FLASH_WAKE_UP_ON (0x110) +#define AW_REG_FLASH_WAKE_UP_OFF (0x102) + +#define AW_BT_HOST2CPU_TRIG_ONLINE_UPGRADE_CMD (0x14) + +#define AW_BT_STATE_INFO_ERR (0x1c00) +#define AW_BT_STATE_INFO_PST (0x1C04) +#define AW_BT_STATE_INFO_FLAG (0x1C08) +#define AW_BT_PROT_CMD_PACK_ADDR (0x1C10) +#define AW_BT_PROT_ACK_PACK_ADDR (0x1C14) +#define AW_BT_MESSAGE_MSG0 (0x1C20) +#define AW_BT_MESSAGE_MSG1 (0x1C24) +#define AW_BT_MESSAGE_MSG_FLAG (0x1C28) +#define AW_BT_VER_INF_VERSION (0x1C30) +#define AW_BT_VER_INF_DATE (0x1C34) +#define AW_BT_HOST2CPU_TRIG (0x4408) +#define AW_BTCPU2HOST_TRIG (0x4410) + +#define AW_SRAM_FIRST_DETECT (0x20000800) + +#define AW_POWER_ON_DELAY_MS (25) +#define REG_MCFG_DELAY_MS (20) +#define AW_IRQ_DELAY_MS (100) +#define REG_RSTNALL_VAL (0x3c) +#define INIT_OVER_IRQ (1) +#define CMD_SLEEP_MODE (2) + +#define AW_WORD_LEN (4) +#define AW_REG_DATA_LEN (4) +#define AW_REG_ADDR_LEN (2) +#define AW_TX_BUF_LEN (AW_REG_DATA_LEN + AW_REG_ADDR_LEN) + +#define AW_RETRIES (5) +#define TRANSFER_SEQ_LEN (4) +#define TRANSFER_DTS_ADDR_LEN (4) +#define AW_FLASH_DEFAULT_VAL (0xffffffff) + +#define AW_PROT_STOP_WAIT_IRQ_CYCLES (100) +#define AW_REG_UPDATA_DIS_MASK (0x00ffffff) +#define AW_AFECFG3_CVMULTUALMOD (11) +#define AW_AFECFG3_CVOFF2X (10) + +#define AW_REG_MCFG (0x4444) +#define AW_REG_ACESS_EN (0xff20) +#define AW_REG_BOOTLOADER_ACTIVER (0x4748) +#define AW_REG_MCFG (0x4444) +#define AW_REG_RSTNALL (0xff18) +#define AW_CPU_HALT (0x00010000) +#define AW_ACC_PERI (0x3c00ffff) +#define AWDIS_HARD_BT_MODE (0x00000000) +#define AW_CPU_RUN (0x00000000) +#define AW_RSTNALL (0x0000003c) + +enum AW_UPDATE_RET { + AW_FW_OK, + AW_FW_ERR, +}; +enum AW9620X_UPDATE_MODE { + BOOT_UPDATE, + FIRMWARE_UPDATE, +}; + +#define PROT_SEND_ADDR (0x0800) +#define SEND_ADDR_LEN (2) +#define AW_ACK_ADDR (0x1800) + + +#define AW_PACK_FIXED_SIZE (12) +#define AW_ADDR_SIZE (2) + +//hecksum_en host->slave +#define AW_HEADER_VAL (0x02) + +#define REG_H2C_TRIG_PARSE_CMD (0x15) + +#define AW_WAIT_IRQ_CYCLES (10) +#define AW_WAIT_ENTER_SLEEP_MODE (100) +#define AW_WAIT_ENTER_TR_MODE (100) + +#define INIT_OVER_IRQ_OK (1) + +#define TRANSFER_SEQ_LEN (4) +#define TRANSFER_DTS_ADDR_LEN (4) + +#define AW_BT_CHECK_SUM_ADDR (0x07fc) + +#define AW_START_CMD_ACK_STATUS (2) +#define AW_START_CMD_ACK_ADDR_LEN (2) + +#define P_AW_START_CMD_SEND_VALUE (NULL) +#define AW_START_CMD_SEND_VALUE_LEN (0) +#define P_AW_ERASE_CHIP_CMD_SEND_VALUE (NULL) +#define AW_ERASE_CHIP_CMD_SEND_VALUE_LEN (0) +#define P_AW_RESTORE_CMD_SEND_VALUE (NULL) +#define AW_RESTORE_CMD_SEND_VALUE_LEN (0) + +#define AW_EN_TR_CHECK_VALUE_LEN (16) +#define AW_FLASH_ERASE_CHIO_CMD_ACK_LEN (4) + +#define AW_REG_WST_MODE_SLEEP (0x03000000) + + +enum AW9620X_FLASH_ADDR { + FLASH_ADDR_BOOT, + FLASH_ADDR_FW, +}; + +enum AW9620X_CMP_VAL { + BIN_VER_EQUALS_SOC_VER, + BIN_VER_NOT_EQUALS_SOC_VER, +}; + +enum AW_MODULE { + UPDATE_MODULE = 0x01, + FLASH_MODULE, + QUERY_MODULE, +}; + +enum AW_UPDATE_COMMAND { + UPDATE_START_CMD = 0X01, + UPDATE_TRANSFER_CMD, + UPDATE_STOP_CMD, + UPDATE_RESTORE_FLASHBT_CMD, +}; + +enum AW_UPDATE_COMMAND_ACK { + UPDATE_START_CMD_ACK = 0X01, + UPDATE_RANSFER_CMD_ACK, + UPDATE_STOP_CMD_ACK, + UPDATE_RESTORE_FLASHBT_CMD_ACK, +}; + +enum AW_FLASH_COMMAND { + FLASH_ERASE_CHIP_CMD = 0X01, + FLASH_ERASE_SECTOR_CMD, +}; + +enum AW_FLASH_COMMAND_ACK { + FLASH_ERASE_CHIP_CMD_ACK = 0X01, + FLASH_ERASE_SECTOR_CMD_ACK, +}; + +enum AW_QUERY_COMMAND { + QUERY_BT_VER_CMD = 0X01, + QUERY_BT_DATE_CMD, + QUERY_ERR_CODE_CMD, + QUERY_PST_CMD, + QUERY_CACHE_CMD, +}; + +enum AW_QUERY_COMMAND_ACK { + QUERY_BT_VER_CMD_ACK = 0X01, + QUERY_BT_DATE_CMD_ACK, + QUERY_ERR_CODE_CMD_ACK, + QUERY_PST_CMD_ACK, + QUERY_CACHE_CMD_ACK, +}; + +struct aw_soc_protocol { + uint16_t header; + uint16_t size; + uint32_t checksum; + uint8_t module; + uint8_t command; + uint16_t length; + uint8_t value[0]; +}; + +enum AW_UPDATE_FW_STATE { + SEND_UPDTAE_CMD, + SEND_START_CMD, + SEND_ERASE_SECTOR_CMD, + SEND_UPDATE_FW_CMD, + SEND_UPDATE_CHECK_CODE_CMD, + SEND_RESTORE_CMD, + SEND_STOP_CMD, +}; + +enum AW_PROT_UPDATE_FW_DELAY_TIME{ + SEND_UPDTAE_CMD_DELAY_MS = 1, + SEND_START_CMD_DELAY_MS = 3, + SEND_ERASE_SECTOR_CMD_DELAY_MS = 5, + SEND_UPDATE_FW_CMD_DELAY_MS = 100, + SEND_UPDATE_CHECK_CODE_CMD_DELAY_MS = 1, + SEND_RESTORE_CMD_DELAY_MS = 27, + SEND_STOP_CMD_DELAY_MS = 25, +}; + +enum AW_PROT_RECV_LEN { + SEND_START_CMD_RECV_LEN = 8, + SEND_ERASE_CHIP_CMD_RECV_LEN = 4, + SEND_UPDATE_FW_CMD_RECV_LEN = 8, + SEND_UPDATE_CHECK_CODE_CMD_RECV_LEN = 4, + SEND_RESTORE_CMD_RECV_LEN = 4, +}; +/********************reg update end****************/ +struct aw_flash_update_info { + uint32_t tr_start_addr; + uint32_t check_len;//The verification code is calculated by the verification length and the firmware chip + uint32_t check_en_addr; + uint32_t check_code_addr; + uint32_t check_en_val; + + uint32_t erase_start_addr; + uint32_t erase_sector_cnt; + uint32_t sector_size; +}; + +enum chip_select_flag { + CHIP_AW9620X, + //CHIP_AW9320X, + //CHIP_AW9328XQNR, +}; + +struct aw_fw_data_info { + const uint8_t *data_start_offset; + uint32_t update_data_len; + uint32_t fw_version; +}; + +struct aw_update_chip_diff { + uint16_t fwver_reg_addr; + //uint16_t wst_reg_addr; + + uint16_t cmd_reg_addr; + uint32_t cmd_sleep_mode; + enum chip_select_flag chip_flag; +}; + +struct aw_fw_info { + uint32_t single_wr_len; + const struct aw_flash_update_info *flash_info; +}; + +struct aw_update_para { + uint8_t direct_update_flag; //Upgrade without comparing version numbers + const struct aw_update_chip_diff *aw_update_diff; //Differences in chip updates +}; + +struct aw_update_fw { + struct aw_fw_info *fw_info; + struct aw_update_para *update_para; + struct aw_fw_data_info *fw_data_info; //Firmware information +}; + + +struct aw_update_common { + uint8_t update_flag; + const uint8_t *w_bin_offset; + uint32_t update_data_len; + const struct aw_flash_update_info *flash_info; +}; + + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/include/linux/input/awinic/aw_update_fw_interface.h b/include/linux/input/awinic/aw_update_fw_interface.h new file mode 100644 index 000000000000..8c917621bd5c --- /dev/null +++ b/include/linux/input/awinic/aw_update_fw_interface.h @@ -0,0 +1,14 @@ +#ifndef __AW_UPDATE_FW_INTERFAACE_H_ +#define __AW_UPDATE_FW_INTERFAACE_H_ + +#define AW_SAR_I2C_TYPE struct i2c_client * + +int32_t aw9620x_prot_update_fw(AW_SAR_I2C_TYPE i2c, uint8_t direct_update_flag, + const uint8_t *data_start_offset, uint32_t update_data_len ,uint32_t fw_version); + +int32_t aw9620x_reg_update_fw(AW_SAR_I2C_TYPE i2c, + const uint8_t *data_start_offset, uint32_t update_data_len); + +int32_t aw9620x_reg_update_boot(AW_SAR_I2C_TYPE i2c, + const uint8_t *data_start_offset, uint32_t update_data_len); +#endif \ No newline at end of file diff --git a/include/linux/input/focaltech_mmi_modules.h b/include/linux/input/focaltech_mmi_modules.h new file mode 100644 index 000000000000..7b676206e305 --- /dev/null +++ b/include/linux/input/focaltech_mmi_modules.h @@ -0,0 +1,82 @@ +/* + * + * FocalTech mmi TouchScreen driver header file. + * + * Copyright (c) 2010 Focal tech Ltd. + * Copyright (c) 2012-2016, The Linux Foundation. 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. + * + */ +#ifndef __LINUX_FOCALTECH_MMI_MODULES_H__ +#define __LINUX_FOCALTECH_MMI_MODULES_H__ + +#define FT5X06_ID 0x55 +#define FT5X16_ID 0x0A +#define FT5X36_ID 0x14 +#define FT6X06_ID 0x06 +#define FT6X36_ID 0x36 +#define FT8716_ID 0x87 +#define FT5X46_ID 0x54 +#define FT8006U_ID 0xF0 +#define FT5422U_ID 0x22 + +#define MAX_PANEL_SUPPLIERS 5 + +struct fw_upgrade_info { + bool auto_cal; + u16 delay_aa; + u16 delay_55; + u8 upgrade_id_1; + u8 upgrade_id_2; + u16 delay_readid; + u16 delay_erase_flash; +}; + +struct ft_ts_platform_data { + struct fw_upgrade_info info; + const char *name; + const char *fw_name; + u32 irqflags; + u32 irq_gpio; + u32 irq_gpio_flags; + u32 reset_gpio; + u32 reset_gpio_flags; + u32 family_id; + u32 x_max; + u32 y_max; + u32 x_min; + u32 y_min; + u32 panel_minx; + u32 panel_miny; + u32 panel_maxx; + u32 panel_maxy; + u32 group_id; + u32 hard_rst_dly; + u32 soft_rst_dly; + u32 num_max_touches; + bool fw_vkey_support; + bool fw_update_mmi; + bool force_send_sleep; + bool no_force_update; + bool i2c_pull_up; + bool ignore_id_check; + bool gesture_support; + bool resume_in_workqueue; + bool x_flip; + bool y_flip; + int (*power_init)(bool); + int (*power_on)(bool); + u32 num_vendor_ids; + u32 vendor_ids[MAX_PANEL_SUPPLIERS]; + const char *vendor_names[MAX_PANEL_SUPPLIERS]; +}; + +#endif diff --git a/include/linux/input/sx9310_triple.h b/include/linux/input/sx9310_triple.h new file mode 100644 index 000000000000..89f3e0934295 --- /dev/null +++ b/include/linux/input/sx9310_triple.h @@ -0,0 +1,415 @@ +/* + * 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. + */ +#ifndef SX9310_TRIPLE_H +#define SX9310_TRIPLE_H + +#include +#include +#include + +/* + * I2C Registers + */ +#define SX9310_IRQSTAT_REG 0x00 +#define SX9310_STAT0_REG 0x01 +#define SX9310_STAT1_REG 0x02 +#define SX9310_IRQ_ENABLE_REG 0x03 +#define SX9310_IRQFUNC_REG 0x04 + +#define SX9310_CPS_CTRL0_REG 0x10 +#define SX9310_CPS_CTRL1_REG 0x11 +#define SX9310_CPS_CTRL2_REG 0x12 +#define SX9310_CPS_CTRL3_REG 0x13 +#define SX9310_CPS_CTRL4_REG 0x14 +#define SX9310_CPS_CTRL5_REG 0x15 +#define SX9310_CPS_CTRL6_REG 0x16 +#define SX9310_CPS_CTRL7_REG 0x17 +#define SX9310_CPS_CTRL8_REG 0x18 +#define SX9310_CPS_CTRL9_REG 0x19 +#define SX9310_CPS_CTRL10_REG 0x1A +#define SX9310_CPS_CTRL11_REG 0x1B +#define SX9310_CPS_CTRL12_REG 0x1C +#define SX9310_CPS_CTRL13_REG 0x1D +#define SX9310_CPS_CTRL14_REG 0x1E +#define SX9310_CPS_CTRL15_REG 0x1F +#define SX9310_CPS_CTRL16_REG 0x20 +#define SX9310_CPS_CTRL17_REG 0x21 +#define SX9310_CPS_CTRL18_REG 0x22 +#define SX9310_CPS_CTRL19_REG 0x23 +#define SX9310_SAR_CTRL0_REG 0x2A +#define SX9310_SAR_CTRL1_REG 0x2B +#define SX9310_SAR_CTRL2_REG 0x2C + +#define SX9310_SOFTRESET_REG 0x7F + +/* Sensor Readback */ +#define SX9310_CPSRD 0x30 + +#define SX9310_USEMSB 0x31 +#define SX9310_USELSB 0x32 + +#define SX9310_AVGMSB 0x33 +#define SX9310_AVGLSB 0x34 + +#define SX9310_DIFFMSB 0x35 +#define SX9310_DIFFLSB 0x36 +#define SX9310_OFFSETMSB 0x37 +#define SX9310_OFFSETLSB 0x38 + +#define SX9310_SARMSB 0x39 +#define SX9310_SARLSB 0x3A + +#define SX9310_WHOAMI 0x42 + +/* IrqStat 0:Inactive 1:Active */ +#define SX9310_IRQSTAT_RESET_FLAG 0x80 +#define SX9310_IRQSTAT_TOUCH_FLAG 0x40 +#define SX9310_IRQSTAT_RELEASE_FLAG 0x20 +#define SX9310_IRQSTAT_COMPDONE_FLAG 0x10 +#define SX9310_IRQSTAT_CONV_FLAG 0x08 +#define SX9310_IRQSTAT_CLOSEALL_FLAG 0x04 +#define SX9310_IRQSTAT_FARALL_FLAG 0x02 +#define SX9310_IRQSTAT_SMARTSAR_FLAG 0x01 + + +/* CpsStat */ +#define SX9310_TCHCMPSTAT_TCHCOMB_FLAG 0x08 +/* enable body stat */ +#define SX9310_TCHCMPSTAT_TCHSTAT2_FLAG 0x84 +/* enable body stat */ +#define SX9310_TCHCMPSTAT_TCHSTAT1_FLAG 0x42 +/* enable body stat */ +#define SX9310_TCHCMPSTAT_TCHSTAT0_FLAG 0x11 + + +/* SoftReset */ +#define SX9310_SOFTRESET 0xDE + +/* useful channel number */ +#define USE_CHANNEL_NUM 3 + +/* channel number need to debug */ +#define DEBUG_CHANNEL_NUM 4 + +/* default settings */ +/* Channel enable: CS0:1,CS1:2,CS2:4,COMB:8 + * Defines the Active scan period : + * 0000: Min (no idle time) + * 0001: 15ms + * 0010: 30 ms (Typ.) + * 0011: 45 ms + * 0100: 60 ms + * 0101: 90 ms + * 0110: 120 ms + * 0111: 200 ms + * 1000: 400 ms + * 1001: 600 ms + * 1010: 800 ms + * 1011: 1 s + * 1100: 2 s + * 1101: 3 s + * 1110: 4 s + * 1111: 5 s + */ +#define DUMMY_USE_CHANNEL 0x1 +#define DUMMY_SCAN_PERIOD 0x2 +#define DUMMY_RAW_DATA_CHANNEL 0x00 + +/* Cap sensor report key, including cs0, cs1, cs2 and comb */ +#define KEY_CAP_CS0 0x270 +#define KEY_CAP_CS1 0x271 +#define KEY_CAP_CS2 0x272 +#define KEY_CAP_COMB 0x272 + +/************************************** +* define platform data +* +**************************************/ +struct smtc_reg_data { + unsigned char reg; + unsigned char val; +}; + +typedef struct smtc_reg_data smtc_reg_data_t; +typedef struct smtc_reg_data *psmtc_reg_data_t; + + +struct _buttonInfo { + /* The Key to send to the input */ + int keycode; + /* Mask to look for on Touch Status */ + int mask; + /* Current state of button. */ + int state; +}; + +struct _totalButtonInformation { + struct _buttonInfo *buttons; + int buttonSize; + struct input_dev *input_top; + struct input_dev *input_bottom; +}; + +typedef struct _totalButtonInformation buttonInformation_t; +typedef struct _totalButtonInformation *pbuttonInformation_t; + +/* Define Registers that need to be initialized to values different than + * default + */ +static struct smtc_reg_data sx9310_i2c_reg_setup[] = { + { + .reg = SX9310_IRQ_ENABLE_REG, + .val = 0xF0, + }, + { + .reg = SX9310_IRQFUNC_REG, + .val = 0x00, + }, + { + .reg = SX9310_CPS_CTRL1_REG, + .val = 0x02, + }, + { + .reg = SX9310_CPS_CTRL2_REG, + .val = 0x0b, + }, + { + .reg = SX9310_CPS_CTRL3_REG, + .val = 0x0A, + }, + { + .reg = SX9310_CPS_CTRL4_REG, + .val = 0x0D, + }, + { + .reg = SX9310_CPS_CTRL5_REG, + .val = 0xC1, + }, + { + .reg = SX9310_CPS_CTRL6_REG, + .val = 0x20, + }, + { + .reg = SX9310_CPS_CTRL7_REG, + .val = 0x4C, + }, + { + .reg = SX9310_CPS_CTRL8_REG, + .val = 0x7A, + }, + { + .reg = SX9310_CPS_CTRL9_REG, + .val = 0x7D, + }, + { + .reg = SX9310_CPS_CTRL10_REG, + .val = 0x10, + }, + { + .reg = SX9310_CPS_CTRL11_REG, + .val = 0x00, + }, + { + .reg = SX9310_CPS_CTRL12_REG, + .val = 0x00, + }, + { + .reg = SX9310_CPS_CTRL13_REG, + .val = 0x00, + }, + { + .reg = SX9310_CPS_CTRL14_REG, + .val = 0x00, + }, + { + .reg = SX9310_CPS_CTRL15_REG, + .val = 0x00, + }, + { + .reg = SX9310_CPS_CTRL16_REG, + .val = 0x00, + }, + { + .reg = SX9310_CPS_CTRL17_REG, + .val = 0x04, + }, + { + .reg = SX9310_CPS_CTRL18_REG, + .val = 0x00, + }, + { + .reg = SX9310_CPS_CTRL19_REG, + .val = 0x00, + }, + { + .reg = SX9310_SAR_CTRL0_REG, + .val = 0x00, + }, + { + .reg = SX9310_SAR_CTRL1_REG, + .val = 0x80, + }, + { + .reg = SX9310_SAR_CTRL2_REG, + .val = 0x0C, + }, + { + .reg = SX9310_CPS_CTRL0_REG, + .val = 0x27, + }, +}; + +static struct _buttonInfo psmtcButtons[] = { + { + .keycode = KEY_CAP_CS0, + .mask = SX9310_TCHCMPSTAT_TCHSTAT0_FLAG, + }, + { + .keycode = KEY_CAP_CS1, + .mask = SX9310_TCHCMPSTAT_TCHSTAT1_FLAG, + }, + { + .keycode = KEY_CAP_CS2, + .mask = SX9310_TCHCMPSTAT_TCHSTAT2_FLAG, + }, + { + .keycode = KEY_CAP_COMB, + .mask = SX9310_TCHCMPSTAT_TCHCOMB_FLAG, + }, +}; + +struct sx9310_platform_data { + int i2c_reg_num; + struct smtc_reg_data *pi2c_reg; + struct regulator *cap_vdd; + struct regulator *cap_svdd; + bool cap_vdd_en; + bool cap_svdd_en; + unsigned irq_gpio; + /* used for custom setting for channel and scan period */ + u32 cust_prox_ctrl0; + u32 cust_raw_data_channel; + int cap_channel_top; + int cap_channel_bottom; + pbuttonInformation_t pbuttonInformation; + + int (*get_is_nirq_low)(unsigned irq_gpio); + int (*init_platform_hw)(void); + void (*exit_platform_hw)(void); +}; +typedef struct sx9310_platform_data sx9310_platform_data_t; +typedef struct sx9310_platform_data *psx9310_platform_data_t; + +#ifdef USE_SENSORS_CLASS +static struct sensors_classdev sensors_capsensor_top_cdev = { + .name = "capsense_top", + .vendor = "semtech", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +static struct sensors_classdev sensors_capsensor_bottom_cdev = { + .name = "capsense_bottom", + .vendor = "semtech", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#endif +/*************************************** +* define data struct/interrupt +* @pdev: pdev common device struction for linux +* @dworker: work struct for worker function +* @board: constant pointer to platform data +* @mutex: mutex for interrupt process +* @lock: Spin Lock used for nirq worker function +* @bus: either i2c_client or spi_client +* @pDevice: device specific struct pointer +*@read_flag : used for dump specified register +* @irq: irq number used +* @irqTimeout: msecs only set if useIrqTimer is true +* @irq_disabled: whether irq should be ignored +* @irq_gpio: irq gpio number +* @useIrqTimer: older models need irq timer for pen up cases +* @read_reg: record reg address which want to read +*@cust_prox_ctrl0 : used for custom setting for channel and scan period +* @init: (re)initialize device +* @refreshStatus: read register status +* @get_nirq_low: get whether nirq is low (platform data) +* @statusFunc: array of functions to call for corresponding status bit +***************************************/ +#define USE_THREADED_IRQ + +#define MAX_NUM_STATUS_BITS (8) + +typedef struct sx93XX sx93XX_t, *psx93XX_t; +struct sx93XX { + struct device *pdev; + struct delayed_work dworker; + struct sx9310_platform_data *board; +#if defined(USE_THREADED_IRQ) + struct mutex mutex; +#else + spinlock_t lock; +#endif + void *bus; + void *pDevice; + int read_flag; + int irq; + int irqTimeout; + char irq_disabled; + /* whether irq should be ignored.. + * cases if enable/disable irq is not used + * or does not work properly */ + u8 useIrqTimer; + u8 read_reg; + + struct work_struct ps_notify_work; + struct notifier_block ps_notif; + bool ps_is_present; + +#if defined(CONFIG_FB) + struct work_struct fb_notify_work; + struct notifier_block fb_notif; +#endif + + /* Function Pointers */ + int (*init)(psx93XX_t this); + /* since we are trying to avoid knowing registers, create a pointer to a + * common read register which would be to read what the interrupt source + * is from + */ + int (*refreshStatus)(psx93XX_t this); + int (*get_nirq_low)(unsigned irq_gpio); + + void (*statusFunc[MAX_NUM_STATUS_BITS])(psx93XX_t this); + +}; + +void sx93XX_suspend(psx93XX_t this); +void sx93XX_resume(psx93XX_t this); +int sx93XX_sar_init(psx93XX_t this); +int sx93XX_sar_remove(psx93XX_t this); + +#endif diff --git a/include/linux/input/sx9325_sar.h b/include/linux/input/sx9325_sar.h new file mode 100644 index 000000000000..cea427ff72cb --- /dev/null +++ b/include/linux/input/sx9325_sar.h @@ -0,0 +1,565 @@ +/* + * 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. + */ +#ifndef SX9325_TRIPLE_H +#define SX9325_TRIPLE_H + +#include +#include +#include + +/* + * I2C Registers + */ + +#define SX932x_IRQSTAT_REG 0x00 +#define SX932x_STAT0_REG 0x01 +#define SX932x_STAT1_REG 0x02 +#define SX932x_STAT2_REG 0x03 +#define SX932x_STAT3_REG 0x04 +#define SX932x_IRQ_ENABLE_REG 0x05 +#define SX932x_IRQCFG0_REG 0x06 +#define SX932x_IRQCFG1_REG 0x07 +#define SX932x_IRQCFG2_REG 0x08 +//-General control +#define SX932x_CTRL0_REG 0x10 +#define SX932x_CTRL1_REG 0x11 +#define SX932x_I2CADDR_REG 0x14 +#define SX932x_CLKSPRD 0x15 +//-AFE Control +#define SX932x_AFE_CTRL0_REG 0x20 +#define SX932x_AFE_CTRL1_REG 0x21 +#define SX932x_AFE_CTRL2_REG 0x22 +#define SX932x_AFE_CTRL3_REG 0x23 +#define SX932x_AFE_CTRL4_REG 0x24 +#define SX932x_AFE_CTRL5_REG 0x25 +#define SX932x_AFE_CTRL6_REG 0x26 +#define SX932x_AFE_CTRL7_REG 0x27 +#define SX932x_AFE_PH0_REG 0x28 +#define SX932x_AFE_PH1_REG 0x29 +#define SX932x_AFE_PH2_REG 0x2A +#define SX932x_AFE_PH3_REG 0x2B +#define SX932x_AFE_CTRL8 0x2C +#define SX932x_AFE_CTRL9 0x2D +//-Main Digital Processing (Prox) control +#define SX932x_PROX_CTRL0_REG 0x30 +#define SX932x_PROX_CTRL1_REG 0x31 +#define SX932x_PROX_CTRL2_REG 0x32 +#define SX932x_PROX_CTRL3_REG 0x33 +#define SX932x_PROX_CTRL4_REG 0x34 +#define SX932x_PROX_CTRL5_REG 0x35 +#define SX932x_PROX_CTRL6_REG 0x36 +#define SX932x_PROX_CTRL7_REG 0x37 +//-Advanced Digital Processing control +#define SX932x_ADV_CTRL0_REG 0x40 +#define SX932x_ADV_CTRL1_REG 0x41 +#define SX932x_ADV_CTRL2_REG 0x42 +#define SX932x_ADV_CTRL3_REG 0x43 +#define SX932x_ADV_CTRL4_REG 0x44 +#define SX932x_ADV_CTRL5_REG 0x45 +#define SX932x_ADV_CTRL6_REG 0x46 +#define SX932x_ADV_CTRL7_REG 0x47 +#define SX932x_ADV_CTRL8_REG 0x48 +#define SX932x_ADV_CTRL9_REG 0x49 +#define SX932x_ADV_CTRL10_REG 0x4A +#define SX932x_ADV_CTRL11_REG 0x4B +#define SX932x_ADV_CTRL12_REG 0x4C +#define SX932x_ADV_CTRL13_REG 0x4D +#define SX932x_ADV_CTRL14_REG 0x4E +#define SX932x_ADV_CTRL15_REG 0x4F +#define SX932x_ADV_CTRL16_REG 0x50 +#define SX932x_ADV_CTRL17_REG 0x51 +#define SX932x_ADV_CTRL18_REG 0x52 +#define SX932x_ADV_CTRL19_REG 0x53 +#define SX932x_ADV_CTRL20_REG 0x54 +/* Sensor Readback */ +#define SX932x_CPSRD 0x60 +#define SX932x_USEMSB 0x61 +#define SX932x_USELSB 0x62 +#define SX932x_AVGMSB 0x63 +#define SX932x_AVGLSB 0x64 +#define SX932x_DIFFMSB 0x65 +#define SX932x_DIFFLSB 0x66 +#define SX932x_OFFSETMSB 0x67 +#define SX932x_OFFSETLSB 0x68 +#define SX932x_SARMSB 0x69 +#define SX932x_SARLSB 0x6A + +#define SX932x_SOFTRESET_REG 0x9F +#define SX932x_WHOAMI_REG 0xFA +#define SX932x_REV_REG 0xFB + +/* IrqStat 0:Inactive 1:Active */ +#define SX932x_IRQSTAT_RESET_FLAG 0x80 +#define SX932x_IRQSTAT_TOUCH_FLAG 0x40 +#define SX932x_IRQSTAT_RELEASE_FLAG 0x20 +#define SX932x_IRQSTAT_COMPDONE_FLAG 0x10 +#define SX932x_IRQSTAT_CONV_FLAG 0x08 +#define SX932x_IRQSTAT_PROG2_FLAG 0x04 +#define SX932x_IRQSTAT_PROG1_FLAG 0x02 +#define SX932x_IRQSTAT_PROG0_FLAG 0x01 + + +/* RegStat0 */ +#define SX932x_PROXSTAT_PH3_FLAG 0x08 +#define SX932x_PROXSTAT_PH2_FLAG 0x04 +#define SX932x_PROXSTAT_PH1_FLAG 0x02 +#define SX932x_PROXSTAT_PH0_FLAG 0x01 + +/* SoftReset */ +#define SX932x_SOFTRESET 0xDE +#define SX932x_WHOAMI_VALUE 0x22 //just for sx9325 +#define SX932x_REV_VALUE 0x22 //just for sx9325 + + +/* CpsStat */ +#define SX932x_TCHCMPSTAT_TCHCOMB_FLAG 0x88 +/* enable body stat */ +#define SX932x_TCHCMPSTAT_TCHSTAT2_FLAG 0x44 +/* enable body stat */ +#define SX932x_TCHCMPSTAT_TCHSTAT1_FLAG 0x22 +/* enable body stat */ +#define SX932x_TCHCMPSTAT_TCHSTAT0_FLAG 0x11 + + + +/* useful channel number */ +#define USE_CHANNEL_NUM 3 + +/* channel number need to debug */ +#define DEBUG_CHANNEL_NUM 4 + +/* default settings */ +/* Channel enable: CS0:1,CS1:2,CS2:4,COMB:8 + * Defines the Active scan period : + * 0000: Min (no idle time) + * 0001: 15ms + * 0010: 30 ms (Typ.) + * 0011: 45 ms + * 0100: 60 ms + * 0101: 90 ms + * 0110: 120 ms + * 0111: 200 ms + * 1000: 400 ms + * 1001: 600 ms + * 1010: 800 ms + * 1011: 1 s + * 1100: 2 s + * 1101: 3 s + * 1110: 4 s + * 1111: 5 s + */ +#define DUMMY_USE_CHANNEL 0x1 +#define DUMMY_SCAN_PERIOD 0x2 +#define DUMMY_RAW_DATA_CHANNEL 0x00 + +/* Cap sensor report key, including cs0, cs1, cs2 and comb */ +#define KEY_CAP_CS0 0x270 +#define KEY_CAP_CS1 0x271 +#define KEY_CAP_CS2 0x272 +#define KEY_CAP_COMB 0x272 + +/************************************** +* define platform data +* +**************************************/ +struct smtc_reg_data { + unsigned char reg; + unsigned char val; +}; + +typedef struct smtc_reg_data smtc_reg_data_t; +typedef struct smtc_reg_data *psmtc_reg_data_t; + + +struct _buttonInfo { + /* The Key to send to the input */ + int keycode; + /* Mask to look for on Touch Status */ + int mask; + /* Current state of button. */ + int state; +}; + +struct _totalButtonInformation { + struct _buttonInfo *buttons; + int buttonSize; + struct input_dev *input_top; + struct input_dev *input_bottom; +}; + +typedef struct _totalButtonInformation buttonInformation_t; +typedef struct _totalButtonInformation *pbuttonInformation_t; + +/* Define Registers that need to be initialized to values different than + * default + */ +static struct smtc_reg_data sx9325_i2c_reg_setup[] = { +//Interrupt and config + { + .reg = SX932x_IRQ_ENABLE_REG, //0x05 + .val = 0x64, // Enavle Close and Far -> enable compensation interrupt + }, + { + .reg = SX932x_IRQCFG0_REG, //0x06 + .val = 0x00, // + }, + { + .reg = SX932x_IRQCFG1_REG, //0x07 + .val = 0x00, + }, + { + .reg = SX932x_IRQCFG2_REG, //0x08 + .val = 0x00, //Activ Low + }, + //--------General control + { + .reg = SX932x_CTRL0_REG, //0x10 + .val = 0x09, // Scanperiod : 100ms(10110) + }, + { + .reg = SX932x_I2CADDR_REG, //0x14 + .val = 0x00, //I2C Address : 0x28 + }, + { + .reg = SX932x_CLKSPRD, //0x15 + .val = 0x00, // + }, + //--------AFE Control + { + .reg = SX932x_AFE_CTRL0_REG, //0x20 + .val = 0x00, // CSx pin during sleep mode : HZ + }, + { + .reg = SX932x_AFE_CTRL1_REG, //0x21 + .val = 0x10, //reserved + }, + { + .reg = SX932x_AFE_CTRL2_REG, //0x22 + .val = 0x00, //reserved + }, + { + .reg = SX932x_AFE_CTRL3_REG, //0x23 + .val = 0x00, //Analog Range(ph0/1) : Small + }, + { + .reg = SX932x_AFE_CTRL4_REG, //0x24 + .val = 0x44, //Sampling Freq(ph0/1) : 83.33khz(01000), Resolution(ph0/1) : 128(100) + }, + { + .reg = SX932x_AFE_CTRL5_REG, //0x25 + .val = 0x00, //reserved + }, + { + .reg = SX932x_AFE_CTRL6_REG, //0x26 + .val = 0x01, //big//Analog Range(ph2/3) : Small + }, + { + .reg = SX932x_AFE_CTRL7_REG, //0x27 + .val = 0x44, //Sampling Freq(ph2/3) : 83.33khz(01000), Resolution(ph2/3) : 128(100) + }, + { + .reg = SX932x_AFE_PH0_REG, //0x28 + .val = 0x01,//0x04, // CS2:HZ CS1:Input CS0 :HZ + }, + { + .reg = SX932x_AFE_PH1_REG, //0x29 + .val = 0x04,//0x10, // CS2:Input CS1:HZ Shield CS0 :HZ + }, + { + .reg = SX932x_AFE_PH2_REG, //0x2A + .val = 0x10,//0x1B, //CS2:HZ CS1:HZ CS0 :HZ + }, + { + .reg = SX932x_AFE_PH3_REG, //0x2B + .val = 0x00, //CS2:HZ CS1:HZ CS0 :HZ + }, + { + .reg = SX932x_AFE_CTRL8, //0x2C + .val = 0x12, // input register(kohm) 4(0010) + }, + { + .reg = SX932x_AFE_CTRL9, //0x2D + .val = 0x08, // Analg gain : x1(1000) + }, + //--------PROX control + { + .reg = SX932x_PROX_CTRL0_REG, //0x30 + .val = 0x13, // Digital Gain(ph0/1) : off(001) Digital Filter(ph0/1) : 1-1/2(001) + }, + { + .reg = SX932x_PROX_CTRL1_REG, //0x31 + .val = 0x0a, // Digital Gain(ph2/3) : off(001) Digital Filter(ph2/3) : 1-1/2(001) + }, + { + .reg = SX932x_PROX_CTRL2_REG, //0x32 + .val = 0x08, //AVGNEGTHRESH : 16384 + }, + { + .reg = SX932x_PROX_CTRL3_REG, // 0x33 + .val = 0x20, //AVGPOSTHRESH : 16384 + }, + { + .reg = SX932x_PROX_CTRL4_REG, //0x34 + .val = 0x0a, //AVGFREEZEDIS : on(0) ,AVGNEGFILT :1-1/2(001) ,AVGPOSFILT : 1-1/256(100) + }, + { + .reg = SX932x_PROX_CTRL5_REG, //0x35 + .val = 0x0a, //FARCOND: PROXDIFF < (THRESH.HYST), HYST : None, CLOSEDEB : off ,FARDEB : off + }, + { + .reg = SX932x_PROX_CTRL6_REG, //0x36 + .val = 0x1e, // Prox Theshold(ph0/1) : 200 + }, + { + .reg = SX932x_PROX_CTRL7_REG, //0x37 + .val = 0x15, // Prox Theshold(ph2/3) : 200 + }, + //--------Advanced control (defult) + { + .reg = SX932x_ADV_CTRL0_REG, + .val = 0x00, + }, + { + .reg = SX932x_ADV_CTRL1_REG, + .val = 0x00, + }, + { + .reg = SX932x_ADV_CTRL2_REG, + .val = 0x18,//0x10, + }, + { + .reg = SX932x_ADV_CTRL3_REG, + .val = 0x2a, + }, + { + .reg = SX932x_ADV_CTRL4_REG, + .val = 0x02, + }, + { + .reg = SX932x_ADV_CTRL5_REG, + .val = 0x05, + }, + { + .reg = SX932x_ADV_CTRL6_REG, + .val = 0x00, + }, + { + .reg = SX932x_ADV_CTRL7_REG, + .val = 0x00, + }, + { + .reg = SX932x_ADV_CTRL8_REG, + .val = 0x00, + }, + { + .reg = SX932x_ADV_CTRL9_REG, + .val = 0x80, + }, + { + .reg = SX932x_ADV_CTRL10_REG, + .val = 0x11, + }, + { + .reg = SX932x_ADV_CTRL11_REG, + .val = 0x00, + }, + { + .reg = SX932x_ADV_CTRL12_REG, + .val = 0x00, + }, + { + .reg = SX932x_ADV_CTRL13_REG, + .val = 0x00, + }, + { + .reg = SX932x_ADV_CTRL14_REG, + .val = 0x80, + }, + { + .reg = SX932x_ADV_CTRL15_REG, + .val = 0x0C, + }, + { + .reg = SX932x_ADV_CTRL16_REG, + .val = 0x00, + }, + { + .reg = SX932x_ADV_CTRL17_REG, + .val = 0x00, + }, + { + .reg = SX932x_ADV_CTRL18_REG, + .val = 0x00, + }, + { + .reg = SX932x_ADV_CTRL19_REG, + .val = 0xF0, + }, + { + .reg = SX932x_ADV_CTRL20_REG, + .val = 0xF0, + }, + //--------Sensor enable + { + .reg = SX932x_CTRL1_REG, //0x11 + .val = 0x24, //enable PH2 + }, +}; + + + + +static struct _buttonInfo psmtcButtons[] = { + { + .keycode = KEY_CAP_CS0, + .mask = SX932x_TCHCMPSTAT_TCHSTAT0_FLAG, + }, + { + .keycode = KEY_CAP_CS1, + .mask = SX932x_TCHCMPSTAT_TCHSTAT1_FLAG, + }, + { + .keycode = KEY_CAP_CS2, + .mask = SX932x_TCHCMPSTAT_TCHSTAT2_FLAG, + }, + { + .keycode = KEY_CAP_COMB, + .mask = SX932x_TCHCMPSTAT_TCHCOMB_FLAG, + }, +}; + +struct sx9325_platform_data { + int i2c_reg_num; + struct smtc_reg_data *pi2c_reg; + struct regulator *cap_vdd; + struct regulator *cap_svdd; + bool cap_vdd_en; + bool cap_svdd_en; + unsigned irq_gpio; + /* used for custom setting for channel and scan period */ + u32 cust_prox_ctrl0; + u32 cust_raw_data_channel; + int cap_channel_top; + int cap_channel_bottom; + pbuttonInformation_t pbuttonInformation; + + int (*get_is_nirq_low)(unsigned irq_gpio); + int (*init_platform_hw)(void); + void (*exit_platform_hw)(void); +}; +typedef struct sx9325_platform_data sx9325_platform_data_t; +typedef struct sx9325_platform_data *psx9325_platform_data_t; + +#ifdef USE_SENSORS_CLASS +static struct sensors_classdev sensors_capsensor_top_cdev = { + .name = "capsense_top", + .vendor = "semtech", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +static struct sensors_classdev sensors_capsensor_bottom_cdev = { + .name = "capsense_bottom", + .vendor = "semtech", + .version = 1, + .type = SENSOR_TYPE_MOTO_CAPSENSE, + .max_range = "5", + .resolution = "5.0", + .sensor_power = "3", + .min_delay = 0, /* in microseconds */ + .fifo_reserved_event_count = 0, + .fifo_max_event_count = 0, + .enabled = 0, + .delay_msec = 100, + .sensors_enable = NULL, + .sensors_poll_delay = NULL, +}; +#endif +/*************************************** +* define data struct/interrupt +* @pdev: pdev common device struction for linux +* @dworker: work struct for worker function +* @board: constant pointer to platform data +* @mutex: mutex for interrupt process +* @lock: Spin Lock used for nirq worker function +* @bus: either i2c_client or spi_client +* @pDevice: device specific struct pointer +*@read_flag : used for dump specified register +* @irq: irq number used +* @irqTimeout: msecs only set if useIrqTimer is true +* @irq_disabled: whether irq should be ignored +* @irq_gpio: irq gpio number +* @useIrqTimer: older models need irq timer for pen up cases +* @read_reg: record reg address which want to read +*@cust_prox_ctrl0 : used for custom setting for channel and scan period +* @init: (re)initialize device +* @refreshStatus: read register status +* @get_nirq_low: get whether nirq is low (platform data) +* @statusFunc: array of functions to call for corresponding status bit +***************************************/ +#define USE_THREADED_IRQ + +#define MAX_NUM_STATUS_BITS (8) + +typedef struct sx93XX sx93XX_t, *psx93XX_t; +struct sx93XX { + struct device *pdev; + struct delayed_work dworker; + struct sx9325_platform_data *board; +#if defined(USE_THREADED_IRQ) + struct mutex mutex; +#else + spinlock_t lock; +#endif + void *bus; + void *pDevice; + int read_flag; + int irq; + int irqTimeout; + char irq_disabled; + /* whether irq should be ignored.. + * cases if enable/disable irq is not used + * or does not work properly */ + u8 useIrqTimer; + u8 read_reg; + + struct work_struct ps_notify_work; + struct notifier_block ps_notif; + bool ps_is_present; + +#if defined(CONFIG_FB) + struct work_struct fb_notify_work; + struct notifier_block fb_notif; +#endif + + /* Function Pointers */ + int (*init)(psx93XX_t this); + /* since we are trying to avoid knowing registers, create a pointer to a + * common read register which would be to read what the interrupt source + * is from + */ + int (*refreshStatus)(psx93XX_t this); + int (*get_nirq_low)(unsigned irq_gpio); + + void (*statusFunc[MAX_NUM_STATUS_BITS])(psx93XX_t this); + +}; + +void sx93XX_suspend(psx93XX_t this); +void sx93XX_resume(psx93XX_t this); +int sx93XX_sar_init(psx93XX_t this); +int sx93XX_sar_remove(psx93XX_t this); + +#endif diff --git a/include/linux/input/sx933x.h b/include/linux/input/sx933x.h new file mode 100644 index 000000000000..1b6d4de8f0a6 --- /dev/null +++ b/include/linux/input/sx933x.h @@ -0,0 +1,816 @@ +/* + * 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. + */ +#ifndef SX933x_H +#define SX933x_H + +#include +#include +#include + +#ifdef CONFIG_CAPSENSE_FLIP_CAL +#include +#endif + +#if 0 +#include +#include +#include +#endif +/* + * I2C Registers + */ +#define SX933X_HOSTIRQSRC_REG 0x4000 +#define SX933X_HOSTIRQMSK_REG 0x4004 +#define SX933X_HOSTIRQCTRL_REG 0x4008 +#define SX933X_PAUSESTAT_REG 0x4010 +#define SX933X_AFECTRL_REG 0x4054 +#define SX933X_CLKGEN_REG 0x4200 +#define SX933X_I2CADDR_REG 0x41C4 +#define SX933X_RESET_REG 0x4240 +#define SX933X_CMD_REG 0x4280 +#define SX933X_TOPSTAT0_REG 0x4284 +#define SX933X_PINCFG_REG 0x42C0 +#define SX933X_PINDOUT_REG 0x42C4 +#define SX933X_PINDIN_REG 0x42C8 +#define SX933X_INFO_REG 0x42D8 +#define SX933X_STAT0_REG 0x8000 +#define SX933X_STAT1_REG 0x8004 +#define SX933X_STAT2_REG 0x8008 +#define SX933X_IRQCFG0_REG 0x800C +#define SX933X_IRQCFG1_REG 0x8010 +#define SX933X_IRQCFG2_REG 0x8014 +#define SX933X_IRQCFG3_REG 0x8018 +#define SX933X_SCANPERIOD_REG 0x801C +#define SX933X_GNRLCTRL2_REG 0x8020 +#define SX933X_AFEPARAMSPH0_REG 0x8024 +#define SX933X_AFEPHPH0_REG 0x8028 +#define SX933X_AFEPARAMSPH1_REG 0x802C +#define SX933X_AFEPHPH1_REG 0x8030 +#define SX933X_AFEPARAMSPH2_REG 0x8034 +#define SX933X_AFEPHPH2_REG 0x8038 +#define SX933X_AFEPARAMSPH3_REG 0x803C +#define SX933X_AFEPHPH3_REG 0x8040 +#define SX933X_AFEPARAMSPH4_REG 0x8044 +#define SX933X_AFEPHPH4_REG 0x8048 +#define SX933X_AFEPARAMSPH5_REG 0x804c +#define SX933X_AFEPHPH5_REG 0x8050 + +#define SX933X_ADCFILTPH0_REG 0x8054 +#define SX933X_AVGBFILTPH0_REG 0x8058 +#define SX933X_AVGAFILTPH0_REG 0x805C +#define SX933X_ADVDIG0PH0_REG 0x8060 +#define SX933X_ADVDIG1PH0_REG 0x8064 +#define SX933X_ADVDIG2PH0_REG 0x8068 +#define SX933X_ADVDIG3PH0_REG 0x806C +#define SX933X_ADVDIG4PH0_REG 0x8070 +#define SX933X_ADCFILTPH1_REG 0x8074 +#define SX933X_AVGBFILTPH1_REG 0x8078 +#define SX933X_AVGAFILTPH1_REG 0x807C +#define SX933X_ADVDIG0PH1_REG 0x8080 +#define SX933X_ADVDIG1PH1_REG 0x8084 +#define SX933X_ADVDIG2PH1_REG 0x8088 +#define SX933X_ADVDIG3PH1_REG 0x808C +#define SX933X_ADVDIG4PH1_REG 0x8090 +#define SX933X_ADCFILTPH2_REG 0x8094 +#define SX933X_AVGBFILTPH2_REG 0x8098 +#define SX933X_AVGAFILTPH2_REG 0x809C +#define SX933X_ADVDIG0PH2_REG 0x80A0 +#define SX933X_ADVDIG1PH2_REG 0x80A4 +#define SX933X_ADVDIG2PH2_REG 0x80A8 +#define SX933X_ADVDIG3PH2_REG 0x80AC +#define SX933X_ADVDIG4PH2_REG 0x80B0 +#define SX933X_ADCFILTPH3_REG 0x80B4 +#define SX933X_AVGBFILTPH3_REG 0x80B8 +#define SX933X_AVGAFILTPH3_REG 0x80BC +#define SX933X_ADVDIG0PH3_REG 0x80C0 +#define SX933X_ADVDIG1PH3_REG 0x80C4 +#define SX933X_ADVDIG2PH3_REG 0x80C8 +#define SX933X_ADVDIG3PH3_REG 0x80CC +#define SX933X_ADVDIG4PH3_REG 0x80D0 +#define SX933X_ADCFILTPH4_REG 0x80D4 +#define SX933X_AVGBFILTPH4_REG 0x80D8 +#define SX933X_AVGAFILTPH4_REG 0x80DC +#define SX933X_ADVDIG0PH4_REG 0x80E0 +#define SX933X_ADVDIG1PH4_REG 0x80E4 +#define SX933X_ADVDIG2PH4_REG 0x80E8 +#define SX933X_ADVDIG3PH4_REG 0x80EC +#define SX933X_ADVDIG4PH4_REG 0x80F0 +#define SX933X_ADCFILTPH5_REG 0x80F4 +#define SX933X_AVGBFILTPH5_REG 0x80F8 +#define SX933X_AVGAFILTPH5_REG 0x80FC +#define SX933X_ADVDIG0PH5_REG 0x8100 +#define SX933X_ADVDIG1PH5_REG 0x8104 +#define SX933X_ADVDIG2PH5_REG 0x8108 +#define SX933X_ADVDIG3PH5_REG 0x810C +#define SX933X_ADVDIG4PH5_REG 0x8110 + +#define SX933X_REFCORRA_REG 0x8124 +#define SX933X_REFCORRB_REG 0x8128 + +#define SX933X_SMARTSAR0A_REG 0x812C +#define SX933X_SMARTSAR1A_REG 0x8130 +#define SX933X_SMARTSAR2A_REG 0x8134 + +#define SX933X_SMARTSAR0B_REG 0x8138 +#define SX933X_SMARTSAR1B_REG 0x813C +#define SX933X_SMARTSAR2B_REG 0x8140 + +#define SX933X_SMARTSAR0C_REG 0x8144 +#define SX933X_SMARTSAR1C_REG 0x8148 +#define SX933X_SMARTSAR2C_REG 0x814C + + + + +#define SX933X_AUTOFREQ0_REG 0x8154 +#define SX933X_AUTOFREQ1_REG 0x8158 + +#define SX933X_USEPH0_REG 0x815C +#define SX933X_USEPH1_REG 0x8160 +#define SX933X_USEPH2_REG 0x8164 +#define SX933X_USEPH3_REG 0x8168 +#define SX933X_USEPH4_REG 0x816C +#define SX933X_USEPH5_REG 0x8170 + +#define SX933X_AVGPH0_REG 0x8174 +#define SX933X_AVGPH1_REG 0x8178 +#define SX933X_AVGPH2_REG 0x817C +#define SX933X_AVGPH3_REG 0x8180 +#define SX933X_AVGPH4_REG 0x8184 +#define SX933X_AVGPH5_REG 0x8188 + +#define SX933X_DIFFPH0_REG 0x818C +#define SX933X_DIFFPH1_REG 0x8190 +#define SX933X_DIFFPH2_REG 0x8194 +#define SX933X_DIFFPH3_REG 0x8198 +#define SX933X_DIFFPH4_REG 0x819C +#define SX933X_DIFFPH5_REG 0x81A0 + +#define SX933X_OFFSETPH0_REG SX933X_AFEPHPH0_REG//bit14:0 +#define SX933X_OFFSETPH1_REG SX933X_AFEPHPH1_REG//bit14:0 +#define SX933X_OFFSETPH2_REG SX933X_AFEPHPH2_REG//bit14:0 +#define SX933X_OFFSETPH3_REG SX933X_AFEPHPH3_REG//bit14:0 +#define SX933X_OFFSETPH4_REG SX933X_AFEPHPH4_REG//bit14:0 +#define SX933X_OFFSETPH5_REG SX933X_AFEPHPH5_REG//bit14:0 + +#define SX933X_REG_DBG_PHASE_SEL 0x81A4 +#define SX933X_REG_PROX_ADC_MIN 0x81A8 +#define SX933X_REG_PROX_ADC_MAX 0x81AC +#define SX933X_REG_PROX_RAW 0x81B0 +#define SX933X_REG_DLT_VAR 0X81B4 + +//i2c register bit mask +#define MSK_IRQSTAT_RESET 0x00000080 +#define MSK_IRQSTAT_TOUCH 0x00000040 +#define MSK_IRQSTAT_RELEASE 0x00000020 +#define MSK_IRQSTAT_COMP 0x00000010 +#define MSK_IRQSTAT_CONV 0x00000008 +#define MSK_IRQSTAT_PROG2IRQ 0x00000004 +#define MSK_IRQSTAT_PROG1IRQ 0x00000002 +#define MSK_IRQSTAT_PROG0IRQ 0x00000001 + +#define I2C_SOFTRESET_VALUE 0x000000DE + +#define I2C_REGCMD_PHEN 0x0000000F +#define I2C_REGCMD_COMPEN 0x0000000E +#define I2C_REGCMD_EN_SLEEP 0x0000000D +#define I2C_REGCMD_EX_SLEEP 0x0000000C + +#define I2C_REGGNRLCTRL2_PHEN_MSK 0x000000FF +#define I2C_REGGNRLCTRL2_COMPEN_MSK 0x00ff0000 + +#define MSK_REGSTAT0_STEADYSTAT5 0x20 +#define MSK_REGSTAT0_STEADYSTAT4 0x10 +#define MSK_REGSTAT0_STEADYSTAT3 0x08 +#define MSK_REGSTAT0_STEADYSTAT2 0x04 +#define MSK_REGSTAT0_STEADYSTAT1 0x02 +#define MSK_REGSTAT0_STEADYSTAT0 0x01 + +#define MSK_REGSTAT0_BODYSTAT5 ((long int)0x20<<8) +#define MSK_REGSTAT0_BODYSTAT4 ((long int)0x10<<8) +#define MSK_REGSTAT0_BODYSTAT3 ((long int)0x08<<8) +#define MSK_REGSTAT0_BODYSTAT2 ((long int)0x04<<8) +#define MSK_REGSTAT0_BODYSTAT1 ((long int)0x02<<8) +#define MSK_REGSTAT0_BODYSTAT0 ((long int)0x01<<8) + +#define MSK_REGSTAT0_TABLESTAT5 ((long int)0x20<<16) +#define MSK_REGSTAT0_TABLESTAT4 ((long int)0x10<<16) +#define MSK_REGSTAT0_TABLESTAT3 ((long int)0x08<<16) +#define MSK_REGSTAT0_TABLESTAT2 ((long int)0x04<<16) +#define MSK_REGSTAT0_TABLESTAT1 ((long int)0x02<<16) +#define MSK_REGSTAT0_TABLESTAT0 ((long int)0x01<<16) + +#define MSK_REGSTAT0_PROXSTAT5 ((long int)0x20<<24) +#define MSK_REGSTAT0_PROXSTAT4 ((long int)0x10<<24) +#define MSK_REGSTAT0_PROXSTAT3 ((long int)0x08<<24) +#define MSK_REGSTAT0_PROXSTAT2 ((long int)0x04<<24) +#define MSK_REGSTAT0_PROXSTAT1 ((long int)0x02<<24) +#define MSK_REGSTAT0_PROXSTAT0 ((long int)0x01<<24) + +#define MSK_REGSTAT1_FAILSTAT5 ((long int)0x20<<24) +#define MSK_REGSTAT1_FAILSTAT4 ((long int)0x10<<24) +#define MSK_REGSTAT1_FAILSTAT3 ((long int)0x08<<24) +#define MSK_REGSTAT1_FAILSTAT2 ((long int)0x04<<24) +#define MSK_REGSTAT1_FAILSTAT1 ((long int)0x02<<24) +#define MSK_REGSTAT1_FAILSTAT0 ((long int)0x01<<24) + +#define MSK_REGSTAT1_COMPSTAT5 ((long int)0x20<<16) +#define MSK_REGSTAT1_COMPSTAT4 ((long int)0x10<<16) +#define MSK_REGSTAT1_COMPSTAT3 ((long int)0x08<<16) +#define MSK_REGSTAT1_COMPSTAT2 ((long int)0x04<<16) +#define MSK_REGSTAT1_COMPSTAT1 ((long int)0x02<<16) +#define MSK_REGSTAT1_COMPSTAT0 ((long int)0x01<<16) +#define MSK_REGSTAT1_SATSTAT5 ((long int)0x20<<8) +#define MSK_REGSTAT1_SATSTAT4 ((long int)0x10<<8) +#define MSK_REGSTAT1_SATSTAT3 ((long int)0x08<<8) +#define MSK_REGSTAT1_SATSTAT2 ((long int)0x04<<8) +#define MSK_REGSTAT1_SATSTAT1 ((long int)0x02<<8) +#define MSK_REGSTAT1_SATSTAT0 ((long int)0x01<<8) + + + +#define MSK_REGSTAT2_STARTUPSTAT5 ((long int)0x20) +#define MSK_REGSTAT2_STARTUPSTAT4 ((long int)0x10) +#define MSK_REGSTAT2_STARTUPSTAT3 ((long int)0x08) +#define MSK_REGSTAT2_STARTUPSTAT2 ((long int)0x04) +#define MSK_REGSTAT2_STARTUPSTAT1 ((long int)0x02) +#define MSK_REGSTAT2_STARTUPSTAT0 ((long int)0x01) + +#define MSK_REGSTAT1_STEADYSTATALL 0x02 + + +#define SX933X_STAT0_PROXSTAT_PH5_FLAG 0x20000000 +#define SX933X_STAT0_PROXSTAT_PH4_FLAG 0x10000000 +#define SX933X_STAT0_PROXSTAT_PH3_FLAG 0x08000000 +#define SX933X_STAT0_PROXSTAT_PH2_FLAG 0x04000000 +#define SX933X_STAT0_PROXSTAT_PH1_FLAG 0x02000000 +#define SX933X_STAT0_PROXSTAT_PH0_FLAG 0x01000000 + +#define SX933X_STAT0_BODYSTAT_PH5_FLAG 0x00002000 +#define SX933X_STAT0_BODYSTAT_PH4_FLAG 0x00001000 +#define SX933X_STAT0_BODYSTAT_PH3_FLAG 0x00000800 +#define SX933X_STAT0_BODYSTAT_PH2_FLAG 0x00000400 +#define SX933X_STAT0_BODYSTAT_PH1_FLAG 0x00000200 +#define SX933X_STAT0_BODYSTAT_PH0_FLAG 0x00000100 + +/* Chip ID */ +#define SX933X_WHOAMI_VALUE 0x00003113 +#define SX9338_DFN_WHOAMI_VALUE 0x00003817 + +/*command*/ +#define SX933X_PHASE_CONTROL 0x0000000F +#define SX933X_COMPENSATION_CONTROL 0x0000000E +#define SX933X_ENTER_CONTROL 0x0000000D +#define SX933X_EXIT_CONTROL 0x0000000C + + +typedef enum{ + SX933X_POWER_SUPPLY_TYPE_PMIC_LDO, // pmic LDO + SX933X_POWER_SUPPLY_TYPE_ALWAYS_ON, // power-supply always on + SX933X_POWER_SUPPLY_TYPE_EXTERNAL_LDO, // external LDO +}sx933x_power_supply_type_t; + +/************************************** + * define platform data + * + **************************************/ +struct smtc_reg_data +{ + unsigned int reg; + unsigned int val; +}; + +typedef struct smtc_reg_data smtc_reg_data_t; +typedef struct smtc_reg_data *psmtc_reg_data_t; + + +struct _buttonInfo +{ + /*! The Key to send to the input */ + // int keycode; //not use + /*! Mask to look for on Prox Touch Status */ + int ProxMask; + /*! Mask to look for on Table Touch Status */ + int BodyMask; + /*! Current state of button. */ + int state; + struct input_dev *input_dev; + char *name; + struct sensors_classdev sensors_capsensor_cdev; + bool enabled; + //may different project use different buttons + bool used; + int offset; +}; + +struct totalButtonInformation +{ + struct _buttonInfo *buttons; + int buttonSize; + //struct input_dev *input; //not used +}; + +typedef struct totalButtonInformation buttonInformation_t; +typedef struct totalButtonInformation *pbuttonInformation_t; + + +/* Define Registers that need to be initialized to values different than + * default + */ +/*define the value without Phase enable settings for easy changes in driver*/ +static const struct smtc_reg_data sx933x_i2c_reg_setup[] = +{ + // + { + .reg = SX933X_AFECTRL_REG, + .val = 0x00000400, + }, + // + { + .reg = SX933X_CLKGEN_REG, + .val = 0x00000008, + }, + // + { + .reg = SX933X_PINCFG_REG, + .val = 0x08000000, + }, + { + .reg = SX933X_PINDOUT_REG, + .val = 0x0000003F, + }, + // + { + .reg = SX933X_IRQCFG0_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_IRQCFG1_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_IRQCFG2_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_IRQCFG3_REG, + .val = 0x00000000, + }, + // + { + .reg = SX933X_SCANPERIOD_REG, + .val = 0x00000032, + }, + { + .reg = SX933X_GNRLCTRL2_REG, + .val = 0x3F001F, //enable phase0~phase4 0x00FF0001, + }, + { + .reg = SX933X_AFEPARAMSPH0_REG, + .val = 0x00010001, + }, + { + .reg = SX933X_AFEPHPH0_REG, + .val = 0x00A00BE3, + }, + { + .reg = SX933X_AFEPARAMSPH1_REG, + .val = 0x300004D4, + }, + { + .reg = SX933X_AFEPHPH1_REG, + .val = 0x00, + }, + { + .reg = SX933X_AFEPARAMSPH2_REG, + .val = 0x300004D4,//0x44F,//AFE setting for phase2 3.85pf + }, + { + .reg = SX933X_AFEPHPH2_REG, + .val = 0x00,//Configure for phase2 + }, + { + .reg = SX933X_AFEPARAMSPH3_REG, + .val = 0x300004D4,//0x44F,//AFE setting for phase3 3.3pf + }, + { + .reg = SX933X_AFEPHPH3_REG, + .val = 0x00,//Configure for phase3 + }, + { + .reg = SX933X_AFEPARAMSPH4_REG, + .val = 0x300004D4,//0x44F,//AFE setting for phase4 9.9pf + }, + { + .reg = SX933X_AFEPHPH4_REG, + .val = 0x00,//Configure for phase4 + }, + { + .reg = SX933X_AFEPARAMSPH5_REG, + .val = 0x300004D4, + }, + { + .reg = SX933X_AFEPHPH5_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADCFILTPH0_REG, + .val = 0x10162000, + }, + { + .reg = SX933X_AVGBFILTPH0_REG, + .val = 0x20400017, + }, + { + .reg = SX933X_AVGAFILTPH0_REG, + .val = 0x31CF9110, + }, + { + .reg = SX933X_ADVDIG0PH0_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG1PH0_REG, + .val = 0x00, + }, + { + .reg = SX933X_ADVDIG2PH0_REG, + .val = 0x00, + }, + { + .reg = SX933X_ADVDIG3PH0_REG , + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG4PH0_REG, + .val = 0x10000, + }, + { + .reg = SX933X_ADCFILTPH1_REG, + .val = 0x100000, + }, + { + .reg = SX933X_AVGBFILTPH1_REG, + .val = 0x20600C00, + }, + { + .reg = SX933X_AVGAFILTPH1_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG0PH1_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG1PH1_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG2PH1_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG3PH1_REG , + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG4PH1_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADCFILTPH2_REG, + .val = 0x100000, + }, + { + .reg = SX933X_AVGBFILTPH2_REG, + .val = 0x20600C00, + }, + { + .reg = SX933X_AVGAFILTPH2_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG0PH2_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG1PH2_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG2PH2_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG3PH2_REG , + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG4PH2_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADCFILTPH3_REG, + .val = 0x100000, + }, + { + .reg = SX933X_AVGBFILTPH3_REG, + .val = 0x20600C00, + }, + { + .reg = SX933X_AVGAFILTPH3_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG0PH3_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG1PH3_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG2PH3_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG3PH3_REG , + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG4PH3_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADCFILTPH4_REG, + .val = 0x100000, + }, + { + .reg = SX933X_AVGBFILTPH4_REG, + .val = 0x20600C00, + }, + { + .reg = SX933X_AVGAFILTPH4_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG0PH4_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG1PH4_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG2PH4_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG3PH4_REG , + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG4PH4_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADCFILTPH5_REG, + .val = 0x100000, + }, + { + .reg = SX933X_AVGBFILTPH5_REG, + .val = 0x20600C00, + }, + { + .reg = SX933X_AVGAFILTPH5_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG0PH5_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG1PH5_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG2PH5_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG3PH5_REG , + .val = 0x00000000, + }, + { + .reg = SX933X_ADVDIG4PH5_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_REFCORRA_REG, + .val = 0x7B13B10, + }, + { + .reg = SX933X_REFCORRB_REG, + .val = 0x4000000, + }, + { + .reg = SX933X_SMARTSAR0A_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_SMARTSAR1A_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_SMARTSAR2A_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_SMARTSAR0B_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_SMARTSAR1B_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_SMARTSAR2B_REG, + .val = 0x00000000, + }, + // + { + .reg = SX933X_AUTOFREQ0_REG, + .val = 0x00000000, + }, + { + .reg = SX933X_AUTOFREQ1_REG, + .val = 0x00000000, + }, + // + { + .reg = SX933X_HOSTIRQMSK_REG, + .val = 0x00000078, + }, + { + .reg = SX933X_HOSTIRQCTRL_REG, + .val = 0x00, + } +}; + +static struct _buttonInfo psmtcButtons[] = +{ + { + .ProxMask = SX933X_STAT0_PROXSTAT_PH0_FLAG, + .BodyMask = SX933X_STAT0_BODYSTAT_PH0_FLAG, + .name = "Moto CapSense Ch0", + .enabled = false, + .used = false, + .offset = 0, + }, + { + .ProxMask = SX933X_STAT0_PROXSTAT_PH1_FLAG, + .BodyMask = SX933X_STAT0_BODYSTAT_PH1_FLAG, + .name = "Moto CapSense Ch1", + .enabled = false, + .used = false, + .offset = 1, + }, + { + .ProxMask = SX933X_STAT0_PROXSTAT_PH2_FLAG, + .BodyMask = SX933X_STAT0_BODYSTAT_PH2_FLAG, + .name = "Moto CapSense Ch2", + .enabled = false, + .used = false, + .offset = 2, + }, + { + .ProxMask = SX933X_STAT0_PROXSTAT_PH3_FLAG, + .BodyMask = SX933X_STAT0_BODYSTAT_PH3_FLAG, + .name = "Moto CapSense Ch3", + .enabled = false, + .used = false, + .offset = 3, + }, + { + .ProxMask = SX933X_STAT0_PROXSTAT_PH4_FLAG, + .BodyMask = SX933X_STAT0_BODYSTAT_PH4_FLAG, + .name = "Moto CapSense Ch4", + .enabled = false, + .used = false, + .offset = 4, + }, +}; + +struct sx933x_platform_data +{ + int i2c_reg_num; + u32 button_used_flag; + struct regulator *cap_vdd; + bool cap_vdd_en; + struct smtc_reg_data *pi2c_reg; + int irq_gpio; + int ref_phase_a; + int ref_phase_b; + int eldo_gpio; + bool eldo_vdd_en; + sx933x_power_supply_type_t power_supply_type; +#ifdef CONFIG_CAPSENSE_HEADSET_STATE + int headset_operate_reg_num; + struct smtc_reg_data *headset_operate_reg; + struct smtc_reg_data *headset_operate_reg_bck; +#endif + +#ifdef CONFIG_CAPSENSE_USB_CAL + struct work_struct ps_notify_work; + struct notifier_block ps_notif; + bool ps_is_present; +#ifdef CONFIG_CAPSENSE_ATTACH_CAL + bool phone_is_present; +#endif +#ifdef CONFIG_CAPSENSE_FLIP_CAL + struct notifier_block flip_notif; + struct extcon_dev *ext_flip_det; + bool phone_flip_state; + bool phone_flip_update_regs; + int phone_flip_open_val; + int num_flip_closed_regs; + int num_flip_open_regs; + struct smtc_reg_data *flip_open_regs; + struct smtc_reg_data *flip_closed_regs; +#endif +#endif + bool reinit_on_i2c_failure; + pbuttonInformation_t pbuttonInformation; + + int (*get_is_nirq_low)(void); + + int (*init_platform_hw)(struct i2c_client *client); + void (*exit_platform_hw)(struct i2c_client *client); +}; +typedef struct sx933x_platform_data sx933x_platform_data_t; +typedef struct sx933x_platform_data *psx933x_platform_data_t; + +/*************************************** + * define data struct/interrupt + * + ***************************************/ + +#define MAX_NUM_STATUS_BITS (8) + +typedef struct sx93XX sx93XX_t, *psx93XX_t; +struct sx93XX +{ + void * bus; /* either i2c_client or spi_client */ + + struct device *pdev; /* common device struction for linux */ + + void *pDevice; /* device specific struct pointer */ + + /* Function Pointers */ + int (*init)(psx93XX_t this); /* (re)initialize device */ + + /* since we are trying to avoid knowing registers, create a pointer to a + * common read register which would be to read what the interrupt source + * is from + */ + int (*refreshStatus)(psx93XX_t this); /* read register status */ + + int (*get_nirq_low)(void); /* get whether nirq is low (platform data) */ + + /* array of functions to call for corresponding status bit */ + void (*statusFunc[MAX_NUM_STATUS_BITS])(psx93XX_t this); + + /* Global variable */ + u8 failStatusCode; /*Fail status code*/ + bool reg_in_dts; + + spinlock_t lock; /* Spin Lock used for nirq worker function */ + int irq; /* irq number used */ + + /* whether irq should be ignored.. cases if enable/disable irq is not used + * or does not work properly */ + char irq_disabled; + + /* interrupt check flag */ + int int_state; + + u8 useIrqTimer; /* older models need irq timer for pen up cases */ + + int irqTimeout; /* msecs only set if useIrqTimer is true */ + + /* struct workqueue_struct *ts_workq; */ /* if want to use non default */ + struct delayed_work dworker; /* work struct for worker function */ + u8 phaseselect; + + int reset_count; + atomic_t init_busy; + struct delayed_work i2c_watchdog_work; + int suspended; +}; + +int sx93XX_IRQ_init(psx93XX_t this); + +#endif diff --git a/include/linux/input/sx937x.h b/include/linux/input/sx937x.h new file mode 100644 index 000000000000..aecfe100b17c --- /dev/null +++ b/include/linux/input/sx937x.h @@ -0,0 +1,539 @@ +/* + * 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. + */ +#ifndef __SX937x_H__ +#define __SX937x_H__ + +#include +#include +#include + +#ifdef CONFIG_CAPSENSE_FLIP_CAL +#include +#endif + + + +//Interrupt Control +#define SX937X_IRQ_SOURCE 0x4000 +#define SX937X_IRQ_MASK_A 0x4004 +#define SX937X_IRQ_MASK_B 0x800C +#define SX937X_IRQ_SETUP 0x4008 + +//Device State Configuration +#define SX937X_DEVICE_RESET 0x4240 +#define SX937X_COMMAND 0x4280 +#define SX937X_CMMAND_BUSY 0x4284 + +//Pin Customization +#define SX937X_PIN_SETUP_A 0x42C0 +#define SX937X_PIN_SETUP_B 0x42C4 + +//Device Information +#define SX937X_DEVICE_INFO 0x42CC + +//Status Registers +#define SX937X_DEVICE_STATUS_A 0x8000 +#define SX937X_DEVICE_STATUS_B 0x8004 +#define SX937X_DEVICE_STATUS_C 0x8008 + +//Status Output Setup +#define SX937X_STATUS_OUTPUT_0 0x8010 +#define SX937X_STATUS_OUTPUT_1 0x8014 +#define SX937X_STATUS_OUTPUT_2 0x8018 +#define SX937X_STATUS_OUTPUT_3 0x801C + +//Phase Enabling +#define SX937X_SCAN_PERIOD_SETUP 0x8020 +#define SX937X_GENERAL_SETUP 0x8024 + +//AFE Parameter Setup +#define SX937X_AFE_PARAMETERS_PH0 0x8028 +#define SX937X_AFE_PARAMETERS_PH1 0x8034 +#define SX937X_AFE_PARAMETERS_PH2 0x8040 +#define SX937X_AFE_PARAMETERS_PH3 0x804C +#define SX937X_AFE_PARAMETERS_PH4 0x8058 +#define SX937X_AFE_PARAMETERS_PH5 0x8064 +#define SX937X_AFE_PARAMETERS_PH6 0x8070 +#define SX937X_AFE_PARAMETERS_PH7 0x807C + +//AFE Connection Setup +#define SX937X_AFE_CS_USAGE_PH0 0x8030 +#define SX937X_AFE_CS_USAGE_PH1 0x803C +#define SX937X_AFE_CS_USAGE_PH2 0x8048 +#define SX937X_AFE_CS_USAGE_PH3 0x8054 +#define SX937X_AFE_CS_USAGE_PH4 0x8060 +#define SX937X_AFE_CS_USAGE_PH5 0x806C +#define SX937X_AFE_CS_USAGE_PH6 0x8078 +#define SX937X_AFE_CS_USAGE_PH7 0x8084 + +//ADC, RAW Filter and Debounce Filter +#define SX937X_FILTER_SETUP_A_PH0 0x8088 +#define SX937X_FILTER_SETUP_A_PH1 0x80A8 +#define SX937X_FILTER_SETUP_A_PH2 0x80C8 +#define SX937X_FILTER_SETUP_A_PH3 0x80E8 +#define SX937X_FILTER_SETUP_A_PH4 0x8108 +#define SX937X_FILTER_SETUP_A_PH5 0x8128 +#define SX937X_FILTER_SETUP_A_PH6 0x8148 +#define SX937X_FILTER_SETUP_A_PH7 0x8168 + +//Average and UseFilter Filter +#define SX937X_FILTER_SETUP_B_PH0 0x808C +#define SX937X_FILTER_SETUP_B_PH1 0x80AC +#define SX937X_FILTER_SETUP_B_PH2 0x80CC +#define SX937X_FILTER_SETUP_B_PH3 0x80EC +#define SX937X_FILTER_SETUP_B_PH4 0x810C +#define SX937X_FILTER_SETUP_B_PH5 0x812C +#define SX937X_FILTER_SETUP_B_PH6 0x814C +#define SX937X_FILTER_SETUP_B_PH7 0x816C + +//Filter Setup C +#define SX937X_USE_FLT_SETUP_PH0 0x8090 +#define SX937X_USE_FLT_SETUP_PH1 0x80B0 +#define SX937X_USE_FLT_SETUP_PH2 0x80D0 +#define SX937X_USE_FLT_SETUP_PH3 0x80F0 +#define SX937X_USE_FLT_SETUP_PH4 0x8110 +#define SX937X_USE_FLT_SETUP_PH5 0x8130 +#define SX937X_USE_FLT_SETUP_PH6 0x8150 +#define SX937X_USE_FLT_SETUP_PH7 0x8170 + +//Filter Setup D +#define SX937X_ADC_QUICK_FILTER_0 0x81E0 +#define SX937X_ADC_QUICK_FILTER_1 0x81E4 +#define SX937X_ADC_QUICK_FILTER_2 0x81E8 +#define SX937X_ADC_QUICK_FILTER_3 0x81EC + +//Steady and Saturation Setup +#define SX937X_STEADY_AND_SATURATION_PH0 0x809C +#define SX937X_STEADY_AND_SATURATION_PH1 0x80BC +#define SX937X_STEADY_AND_SATURATION_PH2 0x80DC +#define SX937X_STEADY_AND_SATURATION_PH3 0x80FC +#define SX937X_STEADY_AND_SATURATION_PH4 0x811C +#define SX937X_STEADY_AND_SATURATION_PH5 0x813C +#define SX937X_STEADY_AND_SATURATION_PH6 0x815C +#define SX937X_STEADY_AND_SATURATION_PH7 0x817C + +//Failure Threshold Setup +#define SX937X_FAILURE_THRESHOLD_PH0 0x80A4 +#define SX937X_FAILURE_THRESHOLD_PH1 0x80C4 +#define SX937X_FAILURE_THRESHOLD_PH2 0x80E4 +#define SX937X_FAILURE_THRESHOLD_PH3 0x8104 +#define SX937X_FAILURE_THRESHOLD_PH4 0x8124 +#define SX937X_FAILURE_THRESHOLD_PH5 0x8144 +#define SX937X_FAILURE_THRESHOLD_PH6 0x8164 +#define SX937X_FAILURE_THRESHOLD_PH7 0x8184 + +//Proximity Threshold +#define SX937X_PROX_THRESH_PH0 0x8098 +#define SX937X_PROX_THRESH_PH1 0x80B8 +#define SX937X_PROX_THRESH_PH2 0x80D8 +#define SX937X_PROX_THRESH_PH3 0x80F8 +#define SX937X_PROX_THRESH_PH4 0x8118 +#define SX937X_PROX_THRESH_PH5 0x8138 +#define SX937X_PROX_THRESH_PH6 0x8158 +#define SX937X_PROX_THRESH_PH7 0x8178 + +//Startup Setup +#define SX937X_STARTUP_PH0 0x8094 +#define SX937X_STARTUP_PH1 0x80B4 +#define SX937X_STARTUP_PH2 0x80D4 +#define SX937X_STARTUP_PH3 0x80F4 +#define SX937X_STARTUP_PH4 0x8114 +#define SX937X_STARTUP_PH5 0x8134 +#define SX937X_STARTUP_PH6 0x8154 +#define SX937X_STARTUP_PH7 0x8174 + +//Reference Correction Setup A +#define SX937X_REFERENCE_CORRECTION_PH0 0x80A0 +#define SX937X_REFERENCE_CORRECTION_PH1 0x80C0 +#define SX937X_REFERENCE_CORRECTION_PH2 0x80E0 +#define SX937X_REFERENCE_CORRECTION_PH3 0x8100 +#define SX937X_REFERENCE_CORRECTION_PH4 0x8120 +#define SX937X_REFERENCE_CORRECTION_PH5 0x8140 +#define SX937X_REFERENCE_CORRECTION_PH6 0x8160 +#define SX937X_REFERENCE_CORRECTION_PH7 0x8180 + +//Reference Correction Setup B +#define SX937X_REF_ENGINE_1_CONFIG 0x8188 +#define SX937X_REF_ENGINE_2_CONFIG 0x818C +#define SX937X_REF_ENGINE_3_CONFIG 0x8190 +#define SX937X_REF_ENGINE_4_CONFIG 0x8194 + +//Smart Human Sensing Setup A +#define SX937X_ENGINE_1_CONFIG 0x8198 +#define SX937X_ENGINE_1_X0 0x819C +#define SX937X_ENGINE_1_X1 0x81A0 +#define SX937X_ENGINE_1_X2 0x81A4 +#define SX937X_ENGINE_1_X3 0x81A8 +#define SX937X_ENGINE_1_Y0 0x81AC +#define SX937X_ENGINE_1_Y1 0x81B0 +#define SX937X_ENGINE_1_Y2 0x81B4 +#define SX937X_ENGINE_1_Y3 0x81B8 + +//Smart Human Sensing Setup B +#define SX937X_ENGINE_2_CONFIG 0x81BC +#define SX937X_ENGINE_2_X0 0x81C0 +#define SX937X_ENGINE_2_X1 0x81C4 +#define SX937X_ENGINE_2_X2 0x81C8 +#define SX937X_ENGINE_2_X3 0x81CC +#define SX937X_ENGINE_2_Y0 0x81D0 +#define SX937X_ENGINE_2_Y1 0x81D4 +#define SX937X_ENGINE_2_Y2 0x81D8 +#define SX937X_ENGINE_2_Y3 0x81DC + +//Offset Readback +#define SX937X_OFFSET_PH0 0x802C +#define SX937X_OFFSET_PH1 0x8038 +#define SX937X_OFFSET_PH2 0x8044 +#define SX937X_OFFSET_PH3 0x8050 +#define SX937X_OFFSET_PH4 0x805C +#define SX937X_OFFSET_PH5 0x8068 +#define SX937X_OFFSET_PH6 0x8074 +#define SX937X_OFFSET_PH7 0x8080 + +//Useful Readback +#define SX937X_USEFUL_PH0 0x81F0 +#define SX937X_USEFUL_PH1 0x81F4 +#define SX937X_USEFUL_PH2 0x81F8 +#define SX937X_USEFUL_PH3 0x81FC +#define SX937X_USEFUL_PH4 0x8200 +#define SX937X_USEFUL_PH5 0x8204 +#define SX937X_USEFUL_PH6 0x8208 +#define SX937X_USEFUL_PH7 0x820C + +//UseFilter Readback +#define SX937X_USEFILTER_PH0 0x8250 +#define SX937X_USEFILTER_PH1 0x8254 +#define SX937X_USEFILTER_PH2 0x8258 +#define SX937X_USEFILTER_PH3 0x825C +#define SX937X_USEFILTER_PH4 0x8260 +#define SX937X_USEFILTER_PH5 0x8264 +#define SX937X_USEFILTER_PH6 0x8268 +#define SX937X_USEFILTER_PH7 0x826C + +//Average Readback +#define SX937X_AVERAGE_PH0 0x8210 +#define SX937X_AVERAGE_PH1 0x8214 +#define SX937X_AVERAGE_PH2 0x8218 +#define SX937X_AVERAGE_PH3 0x821C +#define SX937X_AVERAGE_PH4 0x8220 +#define SX937X_AVERAGE_PH5 0x8224 +#define SX937X_AVERAGE_PH6 0x8228 +#define SX937X_AVERAGE_PH7 0x822C + +//Diff Readback +#define SX937X_DIFF_PH0 0x8230 +#define SX937X_DIFF_PH1 0x8234 +#define SX937X_DIFF_PH2 0x8238 +#define SX937X_DIFF_PH3 0x823C +#define SX937X_DIFF_PH4 0x8240 +#define SX937X_DIFF_PH5 0x8244 +#define SX937X_DIFF_PH6 0x8248 +#define SX937X_DIFF_PH7 0x824C + +//Specialized Readback +#define SX937X_DEBUG_SETUP 0x8274 +#define SX937X_DEBUG_READBACK_0 0x8278 +#define SX937X_DEBUG_READBACK_1 0x827C +#define SX937X_DEBUG_READBACK_2 0x8280 +#define SX937X_DEBUG_READBACK_3 0x8284 +#define SX937X_DEBUG_READBACK_4 0x8288 +#define SX937X_DEBUG_READBACK_5 0x828C +#define SX937X_DEBUG_READBACK_6 0x8290 +#define SX937X_DEBUG_READBACK_7 0x8294 +#define SX937X_DEBUG_READBACK_8 0x8298 + +#if 0 +#include +#include +#include +#endif + + +/* Chip ID */ +#define SX937X_WHOAMI_VALUE 0x9375 +/*command*/ +#define SX937X_PHASE_CONTROL 0x0000000F +#define SX937X_COMPENSATION_CONTROL 0x0000000E +#define SX937X_ENTER_CONTROL 0x0000000D +#define SX937X_EXIT_CONTROL 0x0000000C + + +typedef enum{ + SX937X_POWER_SUPPLY_TYPE_PMIC_LDO, // pmic LDO + SX937X_POWER_SUPPLY_TYPE_ALWAYS_ON, // power-supply always on + SX937X_POWER_SUPPLY_TYPE_EXTERNAL_LDO, // external LDO +}sx937x_power_supply_type_t; + +/************************************** + * define platform data + * + **************************************/ +struct smtc_reg_data +{ + unsigned int reg; + unsigned int val; +}; + +typedef struct smtc_reg_data smtc_reg_data_t; +typedef struct smtc_reg_data *psmtc_reg_data_t; + + +struct _buttonInfo +{ + /*! The Key to send to the input */ + // int keycode; //not use + /*! Mask to look for on Prox Touch Status */ + int ProxMask; + /*! Mask to look for on Table Touch Status */ + int BodyMask; + /*! Current state of button. */ + int state; + struct input_dev *input_dev; + char *name; + struct sensors_classdev sensors_capsensor_cdev; + bool enabled; + //may different project use different buttons + bool used; + int offset; +}; + +struct totalButtonInformation +{ + struct _buttonInfo *buttons; + int buttonSize; + //struct input_dev *input; //not used +}; + +typedef struct totalButtonInformation buttonInformation_t; +typedef struct totalButtonInformation *pbuttonInformation_t; + + +/* Define Registers that need to be initialized to values different than + * default + */ +/*define the value without Phase enable settings for easy changes in driver*/ +static const struct smtc_reg_data sx937x_i2c_reg_setup[] = +{ + {0x8024, 0xF700}, //PHEN + {0x4004, 0x7F}, //irq mask + + {0x8028, 0x85C}, //AFE_PARAM_PH0 + {0x8034, 0x85C}, //AFE_PARAM_PH1 + {0x8040, 0x85C}, //AFE_PARAM_PH2 +// {0x804C, 0x85C}, //AFE_PARAM_PH3 + {0x8058, 0x85C}, //AFE_PARAM_PH4 + {0x8064, 0x85C}, //AFE_PARAM_PH5 + {0x8070, 0x85C}, //AFE_PARAM_PH6 + {0x807C, 0x85C}, //AFE_PARAM_PH7 + + //set CS3 to hiz, because it used as IRQ pin + {0x8030, 0xFFF9FD}, //REG_AFEPH_PH0 + {0x803C, 0xFFF9EF}, //REG_AFEPH_PH1 + {0x8048, 0xFFF97F}, //REG_AFEPH_PH2 +// {0x8054, 0xFFFBFF}, //REG_AFEPH_PH3 + {0x8060, 0xFFD9FF}, //REG_AFEPH_PH4 + {0x806C, 0xFEF9FF}, //REG_AFEPH_PH5 + {0x8078, 0xF7F9FF}, //REG_AFEPH_PH6 + {0x8084, 0xBFF9FF}, //REG_AFEPH_PH7 + + //prox1 5000 0x64 + //prox2 10000 0x8D + //prox3 15000 0xAD + //prox4 20000 0xC8 + {0x8098, 0xC8AD8D64}, //prox threshold ph0 + {0x80B8, 0xC8AD8D64}, //prox threshold ph1 + {0x80D8, 0xC8AD8D64}, //prox threshold ph2 +// {0x80F8, 0x64}, //prox threshold ph3 + {0x8118, 0xC8AD8D64}, //prox threshold ph4 + {0x8138, 0xC8AD8D64}, //prox threshold ph5 + {0x8158, 0xC8AD8D64}, //prox threshold ph6 + {0x8178, 0xC8AD8D64}, //prox threshold ph7 + + {0x8024, 0xF7F7}, //PHEN +}; + +static struct _buttonInfo psmtcButtons[] = +{ + { + .ProxMask = 1 << 24, + .BodyMask = 1 << 16, + .name = "Moto CapSense Ch0", + .enabled = false, + .used = false, + .offset = 0, + }, + { + .ProxMask = 1 << 25, + .BodyMask = 1 << 17, + .name = "Moto CapSense Ch1", + .enabled = false, + .used = false, + .offset = 1, + }, + { + .ProxMask = 1 << 26, + .BodyMask = 1 << 18, + .name = "Moto CapSense Ch2", + .enabled = false, + .used = false, + .offset = 2, + }, + { + .ProxMask = 1 << 27, + .BodyMask = 1 << 19, + .name = "Moto CapSense Ch3", + .enabled = false, + .used = false, + .offset = 3, + }, + { + .ProxMask = 1 << 28, + .BodyMask = 1 << 20, + .name = "Moto CapSense Ch4", + .enabled = false, + .used = false, + .offset = 4, + }, + { + .ProxMask = 1 << 29, + .BodyMask = 1 << 21, + .name = "Moto CapSense Ch5", + .enabled = false, + .used = false, + .offset = 5, + }, + { + .ProxMask = 1 << 30, + .BodyMask = 1 << 22, + .name = "Moto CapSense Ch6", + .enabled = false, + .used = false, + .offset = 6, + }, + { + .ProxMask = 1 << 31, + .BodyMask = 1 << 23, + .name = "Moto CapSense Ch7", + .enabled = false, + .used = false, + .offset = 7, + }, + +}; + +struct sx937x_platform_data +{ + int i2c_reg_num; + u32 button_used_flag; + struct regulator *cap_vdd; + bool cap_vdd_en; + struct smtc_reg_data *pi2c_reg; + int irq_gpio; + int ref_phase_a; + int ref_phase_b; + int ref_phase_c; + int eldo_gpio; + bool eldo_vdd_en; + sx937x_power_supply_type_t power_supply_type; +#ifdef CONFIG_CAPSENSE_HEADSET_STATE + int headset_operate_reg_num; + struct smtc_reg_data *headset_operate_reg; + struct smtc_reg_data *headset_operate_reg_bck; +#endif + +#ifdef CONFIG_CAPSENSE_USB_CAL + struct work_struct ps_notify_work; + struct notifier_block ps_notif; + bool ps_is_present; +#ifdef CONFIG_CAPSENSE_ATTACH_CAL + bool phone_is_present; +#endif +#ifdef CONFIG_CAPSENSE_FLIP_CAL + struct notifier_block flip_notif; + struct extcon_dev *ext_flip_det; + bool phone_flip_state; + bool phone_flip_update_regs; + int phone_flip_open_val; + int num_flip_closed_regs; + int num_flip_open_regs; + struct smtc_reg_data *flip_open_regs; + struct smtc_reg_data *flip_closed_regs; +#endif +#endif + bool reinit_on_i2c_failure; + pbuttonInformation_t pbuttonInformation; + + int (*get_is_nirq_low)(void); + + int (*init_platform_hw)(struct i2c_client *client); + void (*exit_platform_hw)(struct i2c_client *client); +}; +typedef struct sx937x_platform_data sx937x_platform_data_t; +typedef struct sx937x_platform_data *psx937x_platform_data_t; + +/*************************************** + * define data struct/interrupt + * + ***************************************/ + +#define MAX_NUM_STATUS_BITS (8) + +typedef struct sx93XX sx93XX_t, *psx93XX_t; +struct sx93XX +{ + void * bus; /* either i2c_client or spi_client */ + + struct device *pdev; /* common device struction for linux */ + + void *pDevice; /* device specific struct pointer */ + + /* Function Pointers */ + int (*init)(psx93XX_t this); /* (re)initialize device */ + + /* since we are trying to avoid knowing registers, create a pointer to a + * common read register which would be to read what the interrupt source + * is from + */ + int (*refreshStatus)(psx93XX_t this); /* read register status */ + + int (*get_nirq_low)(void); /* get whether nirq is low (platform data) */ + + /* array of functions to call for corresponding status bit */ + void (*statusFunc[MAX_NUM_STATUS_BITS])(psx93XX_t this); + + /* Global variable */ + u8 failStatusCode; /*Fail status code*/ + bool reg_in_dts; + + spinlock_t lock; /* Spin Lock used for nirq worker function */ + int irq; /* irq number used */ + + /* whether irq should be ignored.. cases if enable/disable irq is not used + * or does not work properly */ + char irq_disabled; + + /* interrupt check flag */ + int int_state; + + u8 useIrqTimer; /* older models need irq timer for pen up cases */ + + int irqTimeout; /* msecs only set if useIrqTimer is true */ + + int read_flag; /* used for dump specified register*/ + u16 read_reg; /* record reg address which want to read*/ + /* struct workqueue_struct *ts_workq; */ /* if want to use non default */ + struct delayed_work dworker; /* work struct for worker function */ + u8 phaseselect; + + int reset_count; + atomic_t init_busy; + struct delayed_work i2c_watchdog_work; + int suspended; +}; + +int sx93XX_IRQ_init(psx93XX_t this); + +#endif diff --git a/include/linux/input/sx937x_multi.h b/include/linux/input/sx937x_multi.h new file mode 100644 index 000000000000..ae233471e343 --- /dev/null +++ b/include/linux/input/sx937x_multi.h @@ -0,0 +1,515 @@ +/* + * 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. + */ +#ifndef __SX937x_H__ +#define __SX937x_H__ + +#include +#include +#include + + +//Interrupt Control +#define SX937X_IRQ_SOURCE 0x4000 +#define SX937X_IRQ_MASK_A 0x4004 +#define SX937X_IRQ_MASK_B 0x800C +#define SX937X_IRQ_SETUP 0x4008 + +//Device State Configuration +#define SX937X_DEVICE_RESET 0x4240 +#define SX937X_COMMAND 0x4280 +#define SX937X_CMMAND_BUSY 0x4284 + +//Pin Customization +#define SX937X_PIN_SETUP_A 0x42C0 +#define SX937X_PIN_SETUP_B 0x42C4 + +//Device Information +#define SX937X_DEVICE_INFO 0x42CC + +//Status Registers +#define SX937X_DEVICE_STATUS_A 0x8000 +#define SX937X_DEVICE_STATUS_B 0x8004 +#define SX937X_DEVICE_STATUS_C 0x8008 + +//Status Output Setup +#define SX937X_STATUS_OUTPUT_0 0x8010 +#define SX937X_STATUS_OUTPUT_1 0x8014 +#define SX937X_STATUS_OUTPUT_2 0x8018 +#define SX937X_STATUS_OUTPUT_3 0x801C + +//Phase Enabling +#define SX937X_SCAN_PERIOD_SETUP 0x8020 +#define SX937X_GENERAL_SETUP 0x8024 + +//AFE Parameter Setup +#define SX937X_AFE_PARAMETERS_PH0 0x8028 +#define SX937X_AFE_PARAMETERS_PH1 0x8034 +#define SX937X_AFE_PARAMETERS_PH2 0x8040 +#define SX937X_AFE_PARAMETERS_PH3 0x804C +#define SX937X_AFE_PARAMETERS_PH4 0x8058 +#define SX937X_AFE_PARAMETERS_PH5 0x8064 +#define SX937X_AFE_PARAMETERS_PH6 0x8070 +#define SX937X_AFE_PARAMETERS_PH7 0x807C + +//AFE Connection Setup +#define SX937X_AFE_CS_USAGE_PH0 0x8030 +#define SX937X_AFE_CS_USAGE_PH1 0x803C +#define SX937X_AFE_CS_USAGE_PH2 0x8048 +#define SX937X_AFE_CS_USAGE_PH3 0x8054 +#define SX937X_AFE_CS_USAGE_PH4 0x8060 +#define SX937X_AFE_CS_USAGE_PH5 0x806C +#define SX937X_AFE_CS_USAGE_PH6 0x8078 +#define SX937X_AFE_CS_USAGE_PH7 0x8084 + +//ADC, RAW Filter and Debounce Filter +#define SX937X_FILTER_SETUP_A_PH0 0x8088 +#define SX937X_FILTER_SETUP_A_PH1 0x80A8 +#define SX937X_FILTER_SETUP_A_PH2 0x80C8 +#define SX937X_FILTER_SETUP_A_PH3 0x80E8 +#define SX937X_FILTER_SETUP_A_PH4 0x8108 +#define SX937X_FILTER_SETUP_A_PH5 0x8128 +#define SX937X_FILTER_SETUP_A_PH6 0x8148 +#define SX937X_FILTER_SETUP_A_PH7 0x8168 + +//Average and UseFilter Filter +#define SX937X_FILTER_SETUP_B_PH0 0x808C +#define SX937X_FILTER_SETUP_B_PH1 0x80AC +#define SX937X_FILTER_SETUP_B_PH2 0x80CC +#define SX937X_FILTER_SETUP_B_PH3 0x80EC +#define SX937X_FILTER_SETUP_B_PH4 0x810C +#define SX937X_FILTER_SETUP_B_PH5 0x812C +#define SX937X_FILTER_SETUP_B_PH6 0x814C +#define SX937X_FILTER_SETUP_B_PH7 0x816C + +//Filter Setup C +#define SX937X_USE_FLT_SETUP_PH0 0x8090 +#define SX937X_USE_FLT_SETUP_PH1 0x80B0 +#define SX937X_USE_FLT_SETUP_PH2 0x80D0 +#define SX937X_USE_FLT_SETUP_PH3 0x80F0 +#define SX937X_USE_FLT_SETUP_PH4 0x8110 +#define SX937X_USE_FLT_SETUP_PH5 0x8130 +#define SX937X_USE_FLT_SETUP_PH6 0x8150 +#define SX937X_USE_FLT_SETUP_PH7 0x8170 + +//Filter Setup D +#define SX937X_ADC_QUICK_FILTER_0 0x81E0 +#define SX937X_ADC_QUICK_FILTER_1 0x81E4 +#define SX937X_ADC_QUICK_FILTER_2 0x81E8 +#define SX937X_ADC_QUICK_FILTER_3 0x81EC + +//Steady and Saturation Setup +#define SX937X_STEADY_AND_SATURATION_PH0 0x809C +#define SX937X_STEADY_AND_SATURATION_PH1 0x80BC +#define SX937X_STEADY_AND_SATURATION_PH2 0x80DC +#define SX937X_STEADY_AND_SATURATION_PH3 0x80FC +#define SX937X_STEADY_AND_SATURATION_PH4 0x811C +#define SX937X_STEADY_AND_SATURATION_PH5 0x813C +#define SX937X_STEADY_AND_SATURATION_PH6 0x815C +#define SX937X_STEADY_AND_SATURATION_PH7 0x817C + +//Failure Threshold Setup +#define SX937X_FAILURE_THRESHOLD_PH0 0x80A4 +#define SX937X_FAILURE_THRESHOLD_PH1 0x80C4 +#define SX937X_FAILURE_THRESHOLD_PH2 0x80E4 +#define SX937X_FAILURE_THRESHOLD_PH3 0x8104 +#define SX937X_FAILURE_THRESHOLD_PH4 0x8124 +#define SX937X_FAILURE_THRESHOLD_PH5 0x8144 +#define SX937X_FAILURE_THRESHOLD_PH6 0x8164 +#define SX937X_FAILURE_THRESHOLD_PH7 0x8184 + +//Proximity Threshold +#define SX937X_PROX_THRESH_PH0 0x8098 +#define SX937X_PROX_THRESH_PH1 0x80B8 +#define SX937X_PROX_THRESH_PH2 0x80D8 +#define SX937X_PROX_THRESH_PH3 0x80F8 +#define SX937X_PROX_THRESH_PH4 0x8118 +#define SX937X_PROX_THRESH_PH5 0x8138 +#define SX937X_PROX_THRESH_PH6 0x8158 +#define SX937X_PROX_THRESH_PH7 0x8178 + +//Startup Setup +#define SX937X_STARTUP_PH0 0x8094 +#define SX937X_STARTUP_PH1 0x80B4 +#define SX937X_STARTUP_PH2 0x80D4 +#define SX937X_STARTUP_PH3 0x80F4 +#define SX937X_STARTUP_PH4 0x8114 +#define SX937X_STARTUP_PH5 0x8134 +#define SX937X_STARTUP_PH6 0x8154 +#define SX937X_STARTUP_PH7 0x8174 + +//Reference Correction Setup A +#define SX937X_REFERENCE_CORRECTION_PH0 0x80A0 +#define SX937X_REFERENCE_CORRECTION_PH1 0x80C0 +#define SX937X_REFERENCE_CORRECTION_PH2 0x80E0 +#define SX937X_REFERENCE_CORRECTION_PH3 0x8100 +#define SX937X_REFERENCE_CORRECTION_PH4 0x8120 +#define SX937X_REFERENCE_CORRECTION_PH5 0x8140 +#define SX937X_REFERENCE_CORRECTION_PH6 0x8160 +#define SX937X_REFERENCE_CORRECTION_PH7 0x8180 + +//Reference Correction Setup B +#define SX937X_REF_ENGINE_1_CONFIG 0x8188 +#define SX937X_REF_ENGINE_2_CONFIG 0x818C +#define SX937X_REF_ENGINE_3_CONFIG 0x8190 +#define SX937X_REF_ENGINE_4_CONFIG 0x8194 + +//Smart Human Sensing Setup A +#define SX937X_ENGINE_1_CONFIG 0x8198 +#define SX937X_ENGINE_1_X0 0x819C +#define SX937X_ENGINE_1_X1 0x81A0 +#define SX937X_ENGINE_1_X2 0x81A4 +#define SX937X_ENGINE_1_X3 0x81A8 +#define SX937X_ENGINE_1_Y0 0x81AC +#define SX937X_ENGINE_1_Y1 0x81B0 +#define SX937X_ENGINE_1_Y2 0x81B4 +#define SX937X_ENGINE_1_Y3 0x81B8 + +//Smart Human Sensing Setup B +#define SX937X_ENGINE_2_CONFIG 0x81BC +#define SX937X_ENGINE_2_X0 0x81C0 +#define SX937X_ENGINE_2_X1 0x81C4 +#define SX937X_ENGINE_2_X2 0x81C8 +#define SX937X_ENGINE_2_X3 0x81CC +#define SX937X_ENGINE_2_Y0 0x81D0 +#define SX937X_ENGINE_2_Y1 0x81D4 +#define SX937X_ENGINE_2_Y2 0x81D8 +#define SX937X_ENGINE_2_Y3 0x81DC + +//Offset Readback +#define SX937X_OFFSET_PH0 0x802C +#define SX937X_OFFSET_PH1 0x8038 +#define SX937X_OFFSET_PH2 0x8044 +#define SX937X_OFFSET_PH3 0x8050 +#define SX937X_OFFSET_PH4 0x805C +#define SX937X_OFFSET_PH5 0x8068 +#define SX937X_OFFSET_PH6 0x8074 +#define SX937X_OFFSET_PH7 0x8080 + +//Useful Readback +#define SX937X_USEFUL_PH0 0x81F0 +#define SX937X_USEFUL_PH1 0x81F4 +#define SX937X_USEFUL_PH2 0x81F8 +#define SX937X_USEFUL_PH3 0x81FC +#define SX937X_USEFUL_PH4 0x8200 +#define SX937X_USEFUL_PH5 0x8204 +#define SX937X_USEFUL_PH6 0x8208 +#define SX937X_USEFUL_PH7 0x820C + +//UseFilter Readback +#define SX937X_USEFILTER_PH0 0x8250 +#define SX937X_USEFILTER_PH1 0x8254 +#define SX937X_USEFILTER_PH2 0x8258 +#define SX937X_USEFILTER_PH3 0x825C +#define SX937X_USEFILTER_PH4 0x8260 +#define SX937X_USEFILTER_PH5 0x8264 +#define SX937X_USEFILTER_PH6 0x8268 +#define SX937X_USEFILTER_PH7 0x826C + +//Average Readback +#define SX937X_AVERAGE_PH0 0x8210 +#define SX937X_AVERAGE_PH1 0x8214 +#define SX937X_AVERAGE_PH2 0x8218 +#define SX937X_AVERAGE_PH3 0x821C +#define SX937X_AVERAGE_PH4 0x8220 +#define SX937X_AVERAGE_PH5 0x8224 +#define SX937X_AVERAGE_PH6 0x8228 +#define SX937X_AVERAGE_PH7 0x822C + +//Diff Readback +#define SX937X_DIFF_PH0 0x8230 +#define SX937X_DIFF_PH1 0x8234 +#define SX937X_DIFF_PH2 0x8238 +#define SX937X_DIFF_PH3 0x823C +#define SX937X_DIFF_PH4 0x8240 +#define SX937X_DIFF_PH5 0x8244 +#define SX937X_DIFF_PH6 0x8248 +#define SX937X_DIFF_PH7 0x824C + +//Specialized Readback +#define SX937X_DEBUG_SETUP 0x8274 +#define SX937X_DEBUG_READBACK_0 0x8278 +#define SX937X_DEBUG_READBACK_1 0x827C +#define SX937X_DEBUG_READBACK_2 0x8280 +#define SX937X_DEBUG_READBACK_3 0x8284 +#define SX937X_DEBUG_READBACK_4 0x8288 +#define SX937X_DEBUG_READBACK_5 0x828C +#define SX937X_DEBUG_READBACK_6 0x8290 +#define SX937X_DEBUG_READBACK_7 0x8294 +#define SX937X_DEBUG_READBACK_8 0x8298 + +#if 0 +#include +#include +#include +#endif + + +/* Chip ID */ +#define SX937X_WHOAMI_VALUE 0x9370 +/*command*/ +#define SX937X_PHASE_CONTROL 0x0000000F +#define SX937X_COMPENSATION_CONTROL 0x0000000E +#define SX937X_ENTER_CONTROL 0x0000000D +#define SX937X_EXIT_CONTROL 0x0000000C + + +typedef enum{ + SX937X_POWER_SUPPLY_TYPE_PMIC_LDO, // pmic LDO + SX937X_POWER_SUPPLY_TYPE_ALWAYS_ON, // power-supply always on + SX937X_POWER_SUPPLY_TYPE_EXTERNAL_LDO, // external LDO +}sx937x_power_supply_type_t; + +/************************************** + * define platform data + * + **************************************/ + + + + + + +/* Define Registers that need to be initialized to values different than + * default + */ +/*define the value without Phase enable settings for easy changes in driver*/ +typedef struct smtc_reg_data +{ + unsigned int reg; + unsigned int val; +}smtc_reg_data_t; + +static const smtc_reg_data_t sx937x_i2c_reg_setup[] = +{ + {0x8028, 0x85C}, //AFE_PARAM_PH0 + {0x8034, 0x85C}, //AFE_PARAM_PH1 + {0x8040, 0x85C}, //AFE_PARAM_PH2 +// {0x804C, 0x85C}, //AFE_PARAM_PH3 + {0x8058, 0x85C}, //AFE_PARAM_PH4 + {0x8064, 0x85C}, //AFE_PARAM_PH5 + {0x8070, 0x85C}, //AFE_PARAM_PH6 + {0x807C, 0x85C}, //AFE_PARAM_PH7 + + //set CS3 to hiz, because it used as IRQ pin + {0x8030, 0xFFF9FD}, //REG_AFEPH_PH0 + {0x803C, 0xFFF9EF}, //REG_AFEPH_PH1 + {0x8048, 0xFFF97F}, //REG_AFEPH_PH2 +// {0x8054, 0xFFFBFF}, //REG_AFEPH_PH3 + {0x8060, 0xFFD9FF}, //REG_AFEPH_PH4 + {0x806C, 0xFEF9FF}, //REG_AFEPH_PH5 + {0x8078, 0xF7F9FF}, //REG_AFEPH_PH6 + {0x8084, 0xBFF9FF}, //REG_AFEPH_PH7 + + //prox1 5000 0x64 + //prox2 10000 0x8D + //prox3 15000 0xAD + //prox4 20000 0xC8 + {0x8098, 0xC8AD8D64}, //prox threshold ph0 + {0x80B8, 0xC8AD8D64}, //prox threshold ph1 + {0x80D8, 0xC8AD8D64}, //prox threshold ph2 +// {0x80F8, 0x64}, //prox threshold ph3 + {0x8118, 0xC8AD8D64}, //prox threshold ph4 + {0x8138, 0xC8AD8D64}, //prox threshold ph5 + {0x8158, 0xC8AD8D64}, //prox threshold ph6 + {0x8178, 0xC8AD8D64}, //prox threshold ph7 +}; + +struct _buttonInfo +{ + /*! The Key to send to the input */ + // int keycode; //not use + /*! Mask to look for on Prox Touch Status */ + int ProxMask; + /*! Mask to look for on Table Touch Status */ + int BodyMask; + /*! Current state of button. */ + int state; + struct input_dev *input_dev; + const char *name; + struct sensors_classdev sensors_capsensor_cdev; + bool enabled; + //may different project use different buttons + bool used; + int offset; +}; + +typedef struct totalButtonInformation +{ + struct _buttonInfo *buttons; + int buttonSize; +}buttonInformation_t; + +typedef struct totalButtonInformation *pbuttonInformation_t; + +static struct _buttonInfo psmtcButtons[] = +{ + { + .ProxMask = 1 << 24, + .BodyMask = 1 << 16, + .name = "Moto CapSense Ch0", + .enabled = false, + .used = false, + .offset = 0, + }, + { + .ProxMask = 1 << 25, + .BodyMask = 1 << 17, + .name = "Moto CapSense Ch1", + .enabled = false, + .used = false, + .offset = 1, + }, + { + .ProxMask = 1 << 26, + .BodyMask = 1 << 18, + .name = "Moto CapSense Ch2", + .enabled = false, + .used = false, + .offset = 2, + }, + { + .ProxMask = 1 << 27, + .BodyMask = 1 << 19, + .name = "Moto CapSense Ch3", + .enabled = false, + .used = false, + .offset = 3, + }, + { + .ProxMask = 1 << 28, + .BodyMask = 1 << 20, + .name = "Moto CapSense Ch4", + .enabled = false, + .used = false, + .offset = 4, + }, + { + .ProxMask = 1 << 29, + .BodyMask = 1 << 21, + .name = "Moto CapSense Ch5", + .enabled = false, + .used = false, + .offset = 5, + }, + { + .ProxMask = 1 << 30, + .BodyMask = 1 << 22, + .name = "Moto CapSense Ch6", + .enabled = false, + .used = false, + .offset = 6, + }, + { + .ProxMask = 1 << 31, + .BodyMask = 1 << 23, + .name = "Moto CapSense Ch7", + .enabled = false, + .used = false, + .offset = 7, + }, + +}; + +typedef struct sx937x_platform_data +{ + const char *dbg_name; + void *bus; + int i2c_reg_num; + int flip_reg_num; + int flip_far_reg_num; + int dev_id; + struct smtc_reg_data *pi2c_reg; + struct smtc_reg_data *flip_near_reg; + struct smtc_reg_data *flip_far_reg; + int irq_gpio; + int ref_phase_a; + int ref_phase_b; + int ref_phase_c; + //vdd + struct regulator *cap_vdd; + bool cap_vdd_en; + int eldo_gpio; + bool eldo_vdd_en; + sx937x_power_supply_type_t power_supply_type; + + //button info + u32 button_used_flag; + struct _buttonInfo buttons[8]; + int buttonSize; + pbuttonInformation_t pbuttonInformation; + bool reinit_on_cali; + bool reinit_on_i2c_failure; + + int (*get_is_nirq_low)(void); + + int (*init_platform_hw)(struct i2c_client *client); + void (*exit_platform_hw)(struct i2c_client *client); +}sx937x_platform_data_t; +typedef struct sx937x_platform_data *psx937x_platform_data_t; + +/*************************************** + * define data struct/interrupt + * + ***************************************/ + +#define MAX_NUM_STATUS_BITS (8) + +typedef struct sx93XX *psx93XX_t; +typedef struct sx93XX +{ + void * bus; /* either i2c_client or spi_client */ + + struct device *pdev; /* common device struction for linux */ + + struct device *dbg_dev; + + psx937x_platform_data_t hw; + + /* Function Pointers */ + int (*init)(psx93XX_t this); /* (re)initialize device */ + + /* since we are trying to avoid knowing registers, create a pointer to a + * common read register which would be to read what the interrupt source + * is from + */ + int (*refreshStatus)(psx93XX_t this); /* read register status */ + + int (*get_nirq_low)(psx93XX_t this); /* get whether nirq is low (platform data) */ + + /* array of functions to call for corresponding status bit */ + void (*statusFunc[MAX_NUM_STATUS_BITS])(psx93XX_t this); + + /* Global variable */ + u8 failStatusCode; /*Fail status code*/ + bool reg_in_dts; + + spinlock_t lock; /* Spin Lock used for nirq worker function */ + int irq; /* irq number used */ + + /* whether irq should be ignored.. cases if enable/disable irq is not used + * or does not work properly */ + char irq_disabled; + + /* interrupt check flag */ + int int_state; + + u8 useIrqTimer; /* older models need irq timer for pen up cases */ + + int irqTimeout; /* msecs only set if useIrqTimer is true */ + + /* struct workqueue_struct *ts_workq; */ /* if want to use non default */ + struct delayed_work dworker; /* work struct for worker function */ + u8 phaseselect; + + int reset_count; + atomic_t init_busy; + struct delayed_work i2c_watchdog_work; + int suspended; +}sx93XX_t; + +//int sx93XX_IRQ_init(psx93XX_t this); + +#endif diff --git a/include/linux/input/synaptics_dsx_mmi.h b/include/linux/input/synaptics_dsx_mmi.h new file mode 100644 index 000000000000..a4dc94a3cc1b --- /dev/null +++ b/include/linux/input/synaptics_dsx_mmi.h @@ -0,0 +1,78 @@ +/* + * Synaptics DSX touchscreen driver + * + * Copyright (C) 2012 Synaptics Incorporated + * + * Copyright (C) 2012 Alexandra Chin + * Copyright (C) 2012 Scott Lin + * + * 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 _SYNAPTICS_DSX_H_ +#define _SYNAPTICS_DSX_H_ + +/* + * struct synaptics_dsx_cap_button_map - 0d button map + * @nbuttons: number of 0d buttons + * @map: pointer to array of button types + */ +struct synaptics_dsx_cap_button_map { + unsigned char nbuttons; + unsigned char *map; +}; + +/* + * struct synaptics_dsx_platform_data - dsx platform data + * @x_flip: x flip flag + * @y_flip: y flip flag + * @i2c_pull_up: pull up i2c bus with regulator + * @power_down_enable: enable complete regulator shutdown in suspend + * @irq_gpio: attention interrupt gpio + * @irq_flags: flags used by the irq + * @reset_flags: flags used by reset line + * @reset_gpio: reset gpio + * @disp_maxx: display panel maximum values on the x + * @disp_maxy: display panel maximum values on the y + * @disp_minx: display panel minimum values on the x + * @disp_miny: display panel minimum values on the y + * @panel_maxx: touch panel maximum values on the x + * @panel_maxy: touch panel maximum values on the y + * @panel_minx: touch panel minimum values on the x + * @panel_miny: touch panel minimum values on the y + * @reset_delay: reset delay + * @gpio_config: pointer to gpio configuration function + * @cap_button_map: pointer to 0d button map + */ +struct synaptics_dsx_platform_data { + bool x_flip; + bool y_flip; + bool i2c_pull_up; + bool power_down_enable; + bool disable_gpios; + bool do_lockdown; + bool regulator_en; + unsigned irq_gpio; + unsigned long irq_flags; + unsigned reset_gpio; + unsigned panel_minx; + unsigned panel_miny; + unsigned panel_maxx; + unsigned panel_maxy; + unsigned disp_minx; + unsigned disp_miny; + unsigned reset_delay; + int (*gpio_config)(struct synaptics_dsx_platform_data *pdata, + bool configure); + struct synaptics_dsx_cap_button_map *cap_button_map; +}; + +#endif diff --git a/include/linux/mmi_annotate.h b/include/linux/mmi_annotate.h new file mode 100644 index 000000000000..d259b5532d6f --- /dev/null +++ b/include/linux/mmi_annotate.h @@ -0,0 +1,19 @@ +/* Copyright (c) 2013-2014, The Linux Foundation. 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 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. + */ + +#ifndef __MMI_ANNOTATE_H_INCLUDED +#define __MMI_ANNOTATE_H_INCLUDED + +int mmi_annotate(const char *fmt, ...); +int mmi_annotate_persist(const char *fmt, ...); + +#endif /* __MMI_ANNOTATE_H_INCLUDED */ diff --git a/include/linux/mmi_device.h b/include/linux/mmi_device.h new file mode 100644 index 000000000000..97dda682fc87 --- /dev/null +++ b/include/linux/mmi_device.h @@ -0,0 +1,19 @@ +/* Copyright (c) 2020-2021, The Linux Foundation. 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 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. + */ + +#ifndef __MMI_DEVICE_H_INCLUDED +#define __MMI_DEVICE_H_INCLUDED + +bool mmi_device_is_available(struct device_node *np); +bool mmi_check_dynamic_device_node(char *dev_name); + +#endif /* __MMI_DEVICE_H_INCLUDED */ diff --git a/include/linux/mmi_discrete_charger_class.h b/include/linux/mmi_discrete_charger_class.h new file mode 100644 index 000000000000..3b6c455e3631 --- /dev/null +++ b/include/linux/mmi_discrete_charger_class.h @@ -0,0 +1,172 @@ +/* Copyright (c) 2020, 2021 The Linux Foundation. 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 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. + */ + +#ifndef MMI_DISCRETE_CHARGER_CLASS_H +#define MMI_DISCRETE_CHARGER_CLASS_H + +#include +#include +#include +#include + +struct charger_properties { + const char *alias_name; +}; + +/* Data of notifier from charger device */ +struct chgdev_notify { + bool apsd_done; + bool hvdcp_done; +}; + +struct charger_device { + struct charger_properties props; + struct chgdev_notify noti; + const struct charger_ops *ops; + struct mutex ops_lock; + struct device dev; + struct srcu_notifier_head evt_nh; + void *driver_data; + bool is_polling_mode; +}; + +struct charger_ops { + + int (*set_input_voltage_limit)(struct charger_device *dev, u32 uV); + + int (*set_dp_dm)(struct charger_device *dev, int val); + int (*get_pulse_cnt)(struct charger_device *dev, int *count); + + int (*get_real_charger_type)(struct charger_device *dev, int *charger_type); + int (*get_qc3p_power)(struct charger_device *dev, int *qc3p_power); + int (*config_pd_active)(struct charger_device *dev, int val); + + /* enable/disable hz */ + int (*enable_hz)(struct charger_device *dev, bool en); + + /* enable/disable charger */ + int (*enable_charging)(struct charger_device *dev, bool en); + int (*is_enabled_charging)(struct charger_device *dev, bool *en); + + /* get/set charging current*/ + int (*get_charging_current)(struct charger_device *dev, u32 *uA); + int (*set_charging_current)(struct charger_device *dev, u32 uA); + int (*get_min_charging_current)(struct charger_device *dev, u32 *uA); + + /* set cv */ + int (*set_constant_voltage)(struct charger_device *dev, u32 uV); + int (*get_constant_voltage)(struct charger_device *dev, u32 *uV); + + /* get/set input_current */ + int (*get_input_current)(struct charger_device *dev, u32 *uA); + int (*set_input_current)(struct charger_device *dev, u32 uA); + int (*get_min_input_current)(struct charger_device *dev, u32 *uA); + + /* enable/disable usb suspend */ + int (*is_usb_suspend)(struct charger_device *dev, bool *suspend_en); + int (*set_usb_suspend)(struct charger_device *dev, bool suspend_en); + + int (*enable_hw_jeita)(struct charger_device *dev, bool en); + + /*OTG*/ + int (*enable_otg)(struct charger_device *dev, bool en); + int (*set_boost_current_limit)(struct charger_device *dev, u32 uA); + + int (*rerun_aicl)(struct charger_device *dev); + int (*is_charge_halted)(struct charger_device *dev, bool *en); + + int (*dump_registers)(struct charger_device *dev, struct seq_file *m); + int (*enable_termination)(struct charger_device *dev, bool en); +}; + +#define to_charger_device(obj) container_of(obj, struct charger_device, dev) + +static inline void *charger_dev_get_drvdata( + const struct charger_device *charger_dev) +{ + return charger_dev->driver_data; +} + +static inline void charger_dev_set_drvdata( + struct charger_device *charger_dev, void *data) +{ + charger_dev->driver_data = data; +} + +extern int charger_dev_is_charge_halted(struct charger_device *chg_dev, bool *en); + +extern int charger_dev_set_constant_voltage(struct charger_device *chg_dev, u32 uV); + +extern int charger_dev_set_charging_current(struct charger_device *chg_dev, u32 uA); + +extern int charger_dev_get_charging_current(struct charger_device *chg_dev, u32 *uA); + +extern int charger_dev_enable_hz(struct charger_device *chg_dev, bool en); + +extern int charger_dev_enable_charging(struct charger_device *chg_dev, bool en); + +extern int charger_dev_enable_termination(struct charger_device *chg_dev, bool en); + +extern int charger_dev_is_enabled_charging(struct charger_device *chg_dev, bool *en); + +extern int charger_dev_dump_registers(struct charger_device *chg_dev, struct seq_file *m); + +extern int charger_dev_rerun_aicl(struct charger_device *chg_dev); + +extern int charger_dev_enable_otg(struct charger_device *chg_dev, bool en); + +extern int charger_dev_set_boost_current_limit(struct charger_device *chg_dev, u32 uA); + +extern int charger_dev_enable_hw_jeita(struct charger_device *chg_dev, bool enable); + +extern int charger_dev_is_usb_suspend(struct charger_device *chg_dev, bool *suspend_en); + +extern int charger_dev_set_usb_suspend(struct charger_device *chg_dev, bool suspend_en); + +extern int charger_dev_get_input_current(struct charger_device *charger_dev, u32 *uA); + +extern int charger_dev_set_input_current(struct charger_device *chg_dev, u32 uA); + +extern int charger_dev_set_input_voltage(struct charger_device *chg_dev, u32 uV); + +extern int charger_dev_get_pulse_cnt(struct charger_device *chg_dev, int *count); + +extern int charger_dev_set_dp_dm(struct charger_device *chg_dev, int val); + +extern int charger_dev_get_real_charger_type(struct charger_device *chg_dev, int *charger_type); + +extern int charger_dev_get_qc3p_power(struct charger_device *chg_dev, int *qc3p_power); + +extern int charger_dev_config_pd_active(struct charger_device *chg_dev, int val); + +extern int register_charger_device_notifier( + struct charger_device *charger_dev, + struct notifier_block *nb); + +extern int unregister_charger_device_notifier( + struct charger_device *charger_dev, + struct notifier_block *nb); + +extern int charger_dev_notify(struct charger_device *charger_dev); + +extern struct charger_device *charger_device_register( + const char *name, + struct device *parent, void *devdata, const struct charger_ops *ops, + const struct charger_properties *props); + +extern void charger_device_unregister( + struct charger_device *charger_dev); + +extern struct charger_device *get_charger_by_name( + const char *name); + +#endif diff --git a/include/linux/mmi_discrete_power_supply.h b/include/linux/mmi_discrete_power_supply.h new file mode 100644 index 000000000000..7a9a196c8a35 --- /dev/null +++ b/include/linux/mmi_discrete_power_supply.h @@ -0,0 +1,65 @@ +/* Copyright (c) 2020, 2021 The Linux Foundation. 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 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. + */ + +#ifndef MMI_DISCRETE_POWER_SUPPLY_H +#define MMI_DISCRETE_POWER_SUPPLY_H + +/* Indicates USB Type-C CC connection status */ +enum mmi_power_supply_typec_mode { + MMI_POWER_SUPPLY_TYPEC_NONE, + + /* Acting as source */ + MMI_POWER_SUPPLY_TYPEC_SINK, /* Rd only */ + MMI_POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE, /* Rd/Ra */ + MMI_POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY,/* Rd/Rd */ + MMI_POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER, /* Ra/Ra */ + MMI_POWER_SUPPLY_TYPEC_POWERED_CABLE_ONLY, /* Ra only */ + + /* Acting as sink */ + MMI_POWER_SUPPLY_TYPEC_SOURCE_DEFAULT, /* Rp default */ + MMI_POWER_SUPPLY_TYPEC_SOURCE_MEDIUM, /* Rp 1.5A */ + MMI_POWER_SUPPLY_TYPEC_SOURCE_HIGH, /* Rp 3A */ + MMI_POWER_SUPPLY_TYPEC_NON_COMPLIANT, +}; + +enum { + MMI_POWER_SUPPLY_PD_INACTIVE = 0, + MMI_POWER_SUPPLY_PD_ACTIVE, + MMI_POWER_SUPPLY_PD_PPS_ACTIVE, +}; + +enum { + MMI_POWER_SUPPLY_TYPEC_ACCESSORY_NONE, + MMI_POWER_SUPPLY_TYPEC_ACCESSORY_AUDIO, + MMI_POWER_SUPPLY_TYPEC_ACCESSORY_DEBUG, +}; + +enum { + MMI_POWER_SUPPLY_DP_DM_UNKNOWN = 0, + MMI_POWER_SUPPLY_DP_DM_DP_PULSE = 1, + MMI_POWER_SUPPLY_DP_DM_DM_PULSE = 2, + MMI_POWER_SUPPLY_IGNORE_REQUEST_DPDM = 3, + MMI_POWER_SUPPLY_DONOT_IGNORE_REQUEST_DPDM = 4, +}; + +enum mmi_qc3p_power { + MMI_POWER_SUPPLY_QC3P_NONE, +#ifdef CONFIG_MMI_QC3P_WT6670_DETECTED + MMI_POWER_SUPPLY_QC3P_18W = 0x8, +#else + MMI_POWER_SUPPLY_QC3P_18W, +#endif + MMI_POWER_SUPPLY_QC3P_27W, + MMI_POWER_SUPPLY_QC3P_45W, +}; + +#endif diff --git a/include/linux/mmi_kernel_common.h b/include/linux/mmi_kernel_common.h new file mode 100644 index 000000000000..d0e2e930ad40 --- /dev/null +++ b/include/linux/mmi_kernel_common.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2020-2021, The Linux Foundation. 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 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. + */ + +#ifndef _MMI_KERNEL_COMMON_H +#define _MMI_KERNEL_COMMON_H + +#include +#include +#include +#include + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +#define TIME_TYPE timespec64 +#define GET_TIME_OF_DAY(_timeval) \ + ktime_get_real_ts64(_timeval); +#else +#define TIME_TYPE timeval +#define GET_TIME_OF_DAY(_timeval) \ + do_gettimeofday(_timeval); + +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)) +#define SIGINFO kernel_siginfo +#else +#define SIGINFO siginfo +#endif + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) +#define TIME_SPEC timespec64 +#else +#define TIME_SPEC timespec +#endif + +#endif /*_MMI_KERNEL_COMMON_H*/ diff --git a/include/linux/mmi_relay.h b/include/linux/mmi_relay.h new file mode 100644 index 000000000000..10e80010ca7d --- /dev/null +++ b/include/linux/mmi_relay.h @@ -0,0 +1,38 @@ +#ifndef __MMI_RELAY_H__ +#define __MMI_RELAY_H__ + +#include + +/* + * Notify device event type definitions + */ +enum { + NOTIFY_EVENT_LPD_STATUS = 1, + NOTIFY_EVENT_TUD_STATUS = 2, + NOTIFY_EVENT_CID_STATUS = 3, +}; + +typedef enum notifier_type { + ATOMIC, + BLOCKING, + NUM_TYPES, +}notifier_t; + +typedef enum notifier_dev { + LCD, + FPS, + MMI_RELAY, + LPD, + NUM_DEVS, +}notifier_d; + +enum mmi_relay_event { + RELAY_NOTIFY_REGISTER, + RELAY_NOTIFY_UNREGISTER, +}; + +extern int relay_notifier_fire(notifier_t type, notifier_d dev, unsigned long val, void *v); +extern int relay_register_action(notifier_t type, notifier_d dev, struct notifier_block * nb); +extern int relay_unregister_action(notifier_t type, notifier_d dev, struct notifier_block * nb); + +#endif diff --git a/include/linux/mmi_sys_temp.h b/include/linux/mmi_sys_temp.h new file mode 100644 index 000000000000..a198cde27b5d --- /dev/null +++ b/include/linux/mmi_sys_temp.h @@ -0,0 +1,26 @@ +#ifndef _MMI_SYS_TEMP_IOCTL_H +#define _MMI_SYS_TEMP_IOCTL_H + +#include + +#define MMI_SYS_TEMP_IOCTL_NAME "mmi_sys_temp" +#define MMI_SYS_TEMP_NAME_LENGTH 20 + +struct __attribute__((__packed__)) mmi_sys_temp_ioctl { + int temperature; + char name[MMI_SYS_TEMP_NAME_LENGTH]; +}; + +enum { + MMI_SYS_TEMP_SET = 0x00, + MMI_SYS_TEMP_MAX_NUM, +}; + +#define MMI_SYS_TEMP_MAGIC_NUM 0xDF + + +#define MMI_SYS_TEMP_SET_TEMP _IOW(MMI_SYS_TEMP_MAGIC_NUM, \ + MMI_SYS_TEMP_SET, \ + struct mmi_sys_temp_ioctl) + +#endif diff --git a/include/linux/mmi_wake_lock.h b/include/linux/mmi_wake_lock.h new file mode 100644 index 000000000000..87f0c2f7d4ac --- /dev/null +++ b/include/linux/mmi_wake_lock.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2020-2021, The Linux Foundation. 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 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. + */ + +#ifndef _MMI_WAKE_LOCK_H +#define _MMI_WAKE_LOCK_H + +#include +#include +#include +#include + +#define PM_STAY_AWAKE(_wakeup_source) __pm_stay_awake(_wakeup_source); +#define PM_RELAX(_wakeup_source) __pm_relax(_wakeup_source); +#define PM_WAKEUP_EVENT(_wakeup_source, _time) \ + __pm_wakeup_event(_wakeup_source, _time); + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 110) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 163) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0))) +#define PM_WAKEUP_REGISTER(_dev, _wakeup_source, _name) \ + _wakeup_source = wakeup_source_register(_dev, _name); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 81) +#define PM_WAKEUP_REGISTER(_dev, _wakeup_source, _name) \ + _wakeup_source = wakeup_source_register(_name); +#else +#define PM_WAKEUP_REGISTER(_dev, _wakeup_source, _name) { \ + _wakeup_source = (struct wakeup_source *)kmalloc(sizeof(struct wakeup_source), GFP_KERNEL); \ + if(!_wakeup_source) return -ENOMEM; \ + wakeup_source_init(_wakeup_source, _name); \ +} +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 81) +#define PM_WAKEUP_UNREGISTER(_wakeup_source) \ + wakeup_source_unregister(_wakeup_source); +#else +#define PM_WAKEUP_UNREGISTER(_wakeup_source) { \ + wakeup_source_trash(_wakeup_source); \ + kfree(_wakeup_source); \ +} +#endif + +#endif // _MMI_WAKE_LOCK_H diff --git a/include/linux/msm_drm_notify.h b/include/linux/msm_drm_notify.h new file mode 100644 index 000000000000..67bb67b96e7e --- /dev/null +++ b/include/linux/msm_drm_notify.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + */ +#ifndef _MSM_DRM_NOTIFY_H_ +#define _MSM_DRM_NOTIFY_H_ + +#include + +/* A hardware display blank change occurred */ +#define MSM_DRM_EVENT_BLANK 0x01 +/* A hardware display blank early change occurred */ +#define MSM_DRM_EARLY_EVENT_BLANK 0x02 + +enum { + /* panel: power on */ + MSM_DRM_BLANK_UNBLANK, + /* panel: power off */ + MSM_DRM_BLANK_POWERDOWN, +}; + +enum msm_drm_display_id { + /* primary display */ + MSM_DRM_PRIMARY_DISPLAY, + /* external display */ + MSM_DRM_EXTERNAL_DISPLAY, + MSM_DRM_DISPLAY_MAX +}; + +struct msm_drm_notifier { + enum msm_drm_display_id id; + void *data; +}; + +#ifdef CONFIG_DRM_MSM +int msm_drm_register_client(struct notifier_block *nb); +int msm_drm_unregister_client(struct notifier_block *nb); +#else +static inline int msm_drm_register_client(struct notifier_block *nb) +{ + return 0; +} + +static inline int msm_drm_unregister_client(struct notifier_block *nb) +{ + return 0; +} +#endif +#endif diff --git a/include/linux/nfc_common.h b/include/linux/nfc_common.h new file mode 100644 index 000000000000..52d5a94ff52d --- /dev/null +++ b/include/linux/nfc_common.h @@ -0,0 +1,256 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. + */ + +#ifndef _NFC_COMMON_H_ +#define _NFC_COMMON_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +// Max device count for this driver +#define DEV_COUNT 1 + +// NFC device class +#define CLASS_NAME "nfc" + +// NFC character device name, this will be in /dev/ +#define NFC_CHAR_DEV_NAME "nq-nci" + +// HDR length of NCI packet +#define NCI_HDR_LEN 3 +#define NCI_PAYLOAD_IDX 3 +#define NCI_PAYLOAD_LEN_IDX 2 + +#define NCI_RESET_CMD_LEN (4) +#define NCI_RESET_RSP_LEN (4) +#define NCI_RESET_NTF_LEN (13) +#define NCI_GET_VERSION_CMD_LEN (8) +#define NCI_GET_VERSION_RSP_LEN (12) + +// Below offsets should be subtracted from core reset ntf len + +#define NFC_CHIP_TYPE_OFF (3) +#define NFC_ROM_VERSION_OFF (2) +#define NFC_FW_MAJOR_OFF (1) + +#define COLD_RESET_CMD_LEN 3 +#define COLD_RESET_RSP_LEN 4 +#define COLD_RESET_CMD_GID 0x2F +#define COLD_RESET_CMD_PAYLOAD_LEN 0x00 +#define COLD_RESET_RSP_GID 0x4F +#define COLD_RESET_OID 0x1E + +#define MAX_NCI_PAYLOAD_LEN (255) +/* + * From MW 11.04 buffer size increased to support + * frame size of 554 in FW download mode + * Frame len(2) + Frame Header(6) + DATA(512) + HASH(32) + CRC(2) + RFU(4) + */ +#define MAX_BUFFER_SIZE (558) + +// Maximum retry count for standby writes +#define MAX_RETRY_COUNT (3) + +// Retry count for normal write +#define NO_RETRY (1) +#define MAX_IRQ_WAIT_TIME (90) +#define WAKEUP_SRC_TIMEOUT (2000) + +#define NFC_MAGIC 0xE9 + +// Ioctls +// The type should be aligned with MW HAL definitions + +#define NFC_SET_PWR _IOW(NFC_MAGIC, 0x01, unsigned int) +#define ESE_SET_PWR _IOW(NFC_MAGIC, 0x02, unsigned int) +#define ESE_GET_PWR _IOR(NFC_MAGIC, 0x03, unsigned int) +#define NFC_GET_PLATFORM_TYPE _IO(NFC_MAGIC, 0x04) + +#define DTS_IRQ_GPIO_STR "qcom,sn-irq" +#define DTS_VEN_GPIO_STR "qcom,sn-ven" +#define DTS_FWDN_GPIO_STR "qcom,sn-firm" +#define DTS_CLKREQ_GPIO_STR "qcom,sn-clkreq" +#define DTS_CLKSRC_GPIO_STR "qcom,clk-src" +#define NFC_LDO_SUPPLY_DT_NAME "qcom,sn-vdd-1p8" +#define NFC_LDO_SUPPLY_NAME "qcom,sn-vdd-1p8-supply" +#define NFC_LDO_VOL_DT_NAME "qcom,sn-vdd-1p8-voltage" +#define NFC_LDO_CUR_DT_NAME "qcom,sn-vdd-1p8-current" + +//as per SN1x0 datasheet +#define NFC_VDDIO_MIN 1650000 //in uV +#define NFC_VDDIO_MAX 1950000 //in uV +#define NFC_CURRENT_MAX 157000 //in uA + +enum ese_ioctl_request { + /* eSE POWER ON */ + ESE_POWER_ON = 0, + /* eSE POWER OFF */ + ESE_POWER_OFF, + /* eSE COLD RESET */ + ESE_COLD_RESET, + /* eSE POWER STATE */ + ESE_POWER_STATE +}; + +enum nfcc_ioctl_request { + /* NFC disable request with VEN LOW */ + NFC_POWER_OFF = 0, + /* NFC enable request with VEN Toggle */ + NFC_POWER_ON, + /* firmware download request with VEN Toggle */ + NFC_FW_DWL_VEN_TOGGLE, + /* ISO reset request */ + NFC_ISO_RESET, + /* request for firmware download gpio HIGH */ + NFC_FW_DWL_HIGH, + /* VEN hard reset request */ + NFC_VEN_FORCED_HARD_RESET, + /* request for firmware download gpio LOW */ + NFC_FW_DWL_LOW, + /* NFC enable without VEN gpio modification */ + NFC_ENABLE, + /* NFC disable without VEN gpio modification */ + NFC_DISABLE, + /*for HDR size change in FW mode */ + NFC_FW_HDR_LEN, +}; + +/*nfc platform interface type*/ +enum interface_flags { + /*I2C physical IF for NFCC */ + PLATFORM_IF_I2C = 0, + /*I3C physical IF for NFCC */ + PLATFORM_IF_I3C, +}; + +/* + * Power state for IBI handing, mainly needed to defer the IBI handling + * for the IBI received in suspend state to do it later in resume call + */ + +enum pm_state_flags { + PM_STATE_NORMAL = 0, + PM_STATE_SUSPEND, + PM_STATE_IBI_BEFORE_RESUME, +}; + +/* Enum for GPIO values*/ +enum gpio_values { + GPIO_INPUT = 0x0, + GPIO_OUTPUT = 0x1, + GPIO_HIGH = 0x2, + GPIO_OUTPUT_HIGH = 0x3, + GPIO_IRQ = 0x4, +}; + +enum nfcc_chip_variant { + NFCC_SN100_A = 0xa3, /**< NFCC SN100_A */ + NFCC_SN100_B = 0xa4, /**< NFCC SN100_B */ + NFCC_NOT_SUPPORTED = 0xFF /**< NFCC is not supported */ +}; + +// NFC GPIO variables +struct platform_gpio { + unsigned int irq; + unsigned int ven; + unsigned int clkreq; + unsigned int dwl_req; +}; + +// NFC LDO entries from DT +struct platform_ldo { + int vdd_levels[2]; + int max_current; +}; + +//Features specific Parameters +struct cold_reset { + wait_queue_head_t read_wq; + bool rsp_pending; + uint8_t status; + /* Is NFC enabled from UI */ + bool is_nfc_enabled; +}; + +/* Device specific structure */ +struct nfc_dev { + wait_queue_head_t read_wq; + struct mutex read_mutex; + struct mutex dev_ref_mutex; + unsigned int dev_ref_count; + struct class *nfc_class; + struct device *nfc_device; + struct cdev c_dev; + dev_t devno; + /* Interface flag */ + uint8_t interface; + /* NFC VEN pin state */ + bool nfc_ven_enabled; + bool is_vreg_enabled; + bool is_ese_session_active; + union { + struct i2c_dev i2c_dev; + struct i3c_dev i3c_dev; + }; + struct platform_gpio gpio; + struct platform_ldo ldo; + struct cold_reset cold_reset; + struct regulator *reg; + + /* read buffer*/ + size_t kbuflen; + u8 *kbuf; + + union nqx_uinfo nqx_info; + + int (*nfc_read)(struct nfc_dev *dev, + char *buf, size_t count); + int (*nfc_write)(struct nfc_dev *dev, + const char *buf, const size_t count, int max_retry_cnt); + int (*nfc_enable_intr)(struct nfc_dev *dev); + int (*nfc_disable_intr)(struct nfc_dev *dev); +}; + +int nfc_dev_open(struct inode *inode, struct file *filp); +int nfc_dev_close(struct inode *inode, struct file *filp); +#ifdef CONFIG_COMPAT +long nfc_compat_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg); +#endif +long nfc_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg); +int nfc_parse_dt(struct device *dev, struct platform_gpio *nfc_gpio, + struct platform_ldo *ldo, uint8_t interface); +int nfc_misc_probe(struct nfc_dev *nfc_dev, + const struct file_operations *nfc_fops, int count, + char *devname, char *classname); +void nfc_misc_remove(struct nfc_dev *nfc_dev, int count); +int configure_gpio(unsigned int gpio, int flag); +void read_cold_reset_rsp(struct nfc_dev *nfc_dev, char *header); +void gpio_set_ven(struct nfc_dev *nfc_dev, int value); +int nfcc_hw_check(struct nfc_dev *nfc_dev); +int nfc_ldo_config(struct device *dev, struct nfc_dev *nfc_dev); +int nfc_ldo_vote(struct nfc_dev *nfc_dev); +int nfc_ldo_unvote(struct nfc_dev *nfc_dev); + +#endif //_NFC_COMMON_H_ diff --git a/include/linux/nfc_i2c_drv.h b/include/linux/nfc_i2c_drv.h new file mode 100644 index 000000000000..2416051090c4 --- /dev/null +++ b/include/linux/nfc_i2c_drv.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. + */ + +#ifndef _NFC_I2C_DRV_H_ +#define _NFC_I2C_DRV_H_ +#include + +#define NFC_I2C_DRV_STR "qcom,sn-nci" /*kept same as dts */ +#define NFC_I2C_DEV_ID "sn-i2c" + +struct nfc_dev; + +//Interface specific parameters +struct i2c_dev { + struct i2c_client *client; + // IRQ parameters + bool irq_enabled; + spinlock_t irq_enabled_lock; + // NFC_IRQ wake-up state + bool irq_wake_up; +}; +long nfc_i2c_dev_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg); +int nfc_i2c_dev_probe(struct i2c_client *client, + const struct i2c_device_id *id); +int nfc_i2c_dev_remove(struct i2c_client *client); +int nfc_i2c_dev_suspend(struct device *device); +int nfc_i2c_dev_resume(struct device *device); + + +int i2c_enable_irq(struct nfc_dev *dev); +int i2c_disable_irq(struct nfc_dev *dev); +int i2c_write(struct nfc_dev *dev, const char *buf, size_t count, + int max_retry_cnt); +int i2c_read(struct nfc_dev *dev, char *buf, size_t count); + +#endif //_NFC_I2C_DRV_H_ diff --git a/include/linux/nfc_i3c_drv.h b/include/linux/nfc_i3c_drv.h new file mode 100644 index 000000000000..7ef3dfda2990 --- /dev/null +++ b/include/linux/nfc_i3c_drv.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + */ + +#ifndef _NFC_I3C_DRV_H_ +#define _NFC_I3C_DRV_H_ + +#include +#include + +//to to kept same as dt +#define NFC_I3C_DRV_STR "qcom,sn-nci-i3c" +#define NFC_I3C_MANU_ID (0x011B) +#define NFC_I3C_PART_ID (0) + +//Byte indicating I3C Read +#define NFC_I3C_READ 1 + +//Byte indicating I3C Write +#define NFC_I3C_WRITE 0 + +// Maximum no of IBI slot +#define NUM_NFC_IBI_SLOT 1 + +// Maximum IBI payload length +#define MAX_IBI_PAYLOAD_LEN 0 + +// CRC len to be read +#define FW_CRC_LEN 2 + +// FW DNLD HDR length +#define FW_HDR_LEN 2 + +// Time to wait before retrying I3C writes, in micro seconds +#define RETRY_WAIT_TIME_USEC (2000) + +// Retry count for enable/disable IBI CCC +#define RETRY_COUNT_IBI (3) + +// I3C WorkQueue name +#define NFC_I3C_WORKQUEUE "nfc_i3c_workq" + +/* Time(in ms) to wait for NCI packet respones */ +#define MAX_IBI_WAIT_TIME (2000) +struct nfc_dev; + +/** + * struct nci_buf - NCI buffer used to store and retrieve read data from device. + * @read_offset: The offset pointing to data available to read in nci buf. + * @write_offset: The offset pointing to free buf available to write. + * @total_size: Size of nci buf. + * @kbuf: allocated nci buf. + */ +struct nci_buf { + unsigned int read_offset; + unsigned int write_offset; + size_t total_size; + char *kbuf; +}; + +/** + * struct i3c_dev Structure representing device and driver data. + * @i3c_device Structure to represent I3C device. + * @wq: NCI workqueue for handling IBI request. + * @work: Work added to workqueue to read data from IBI handler. + * @buf Driver buf store read data from device.Read call will + * fetch from this buffer. + * @nci_buf_mutex: mutex to protect NCI buf retrieve/store . + * @read_cplt: Completion to wait for read data to be available. + * @read_kbuf_len: Temp buf len to hold I3C data. + * @read_kbuf: Temp buf to hold I3C data. + * @read_hdr Header size for reads. + * @ibi_enabled: IBI enabled or not. + * @pm_state: PM state of NFC I3C device. + * @nfc_read_direct Do NFC read bypassing the read buffer. + * @is_probe_done Is NFC I3C probe completed. + */ +struct i3c_dev { + struct i3c_device *device; + struct workqueue_struct *wq; + struct work_struct work; + struct nci_buf buf; + struct mutex nci_buf_mutex; + struct completion read_cplt; + size_t read_kbuf_len; + char *read_kbuf; + unsigned char read_hdr; + bool ibi_enabled; + atomic_t pm_state; + int (*nfc_read_direct)(struct nfc_dev *dev, + char *buf, size_t count); + bool is_probe_done; +}; + +int nfc_i3c_dev_probe(struct i3c_device *device); +int nfc_i3c_dev_remove(struct i3c_device *device); +int nfc_i3c_dev_suspend(struct device *device); +int nfc_i3c_dev_resume(struct device *device); + +#if IS_ENABLED(CONFIG_NFC_QTI_I3C) + +int i3c_enable_ibi(struct nfc_dev *dev); +int i3c_disable_ibi(struct nfc_dev *dev); +int i3c_write(struct nfc_dev *dev, const char *buf, const size_t count, + int max_retry_cnt); +int i3c_read(struct nfc_dev *dev, char *buf, size_t count); +int i3c_nci_kbuf_retrieve(struct nfc_dev *dev, char *buf, + size_t count); + +#else + +static inline int i3c_enable_ibi(struct nfc_dev *dev) +{ + return -ENXIO; +} + +static inline int i3c_disable_ibi(struct nfc_dev *dev) +{ + return -ENXIO; +} + +static inline int i3c_write(struct nfc_dev *dev, + const char *buf, const size_t count, int max_retry_cnt) +{ + return -ENXIO; +} + +static inline int i3c_read(struct nfc_dev *dev, + char *buf, size_t count) +{ + return -ENXIO; +} + +static inline int i3c_nci_kbuf_retrieve(struct nfc_dev *dev, + char *buf, size_t count) +{ + return -ENXIO; +} + +#endif + +#endif //_NFC_I3C_DRV_H_ diff --git a/include/linux/panel_notifier.h b/include/linux/panel_notifier.h new file mode 100644 index 000000000000..ecb2dba3e92d --- /dev/null +++ b/include/linux/panel_notifier.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2016, Motorola Mobility, LLC. + * + * 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. + * + */ + +#ifndef __PANEL_NOTIFIER_H__ +#define __PANEL_NOTIFIER_H__ + +#include + +enum panel_event { + PANEL_EVENT_PRE_DISPLAY_OFF, + PANEL_EVENT_PRE_DISPLAY_ON, + PANEL_EVENT_DISPLAY_OFF, + PANEL_EVENT_DISPLAY_ON, + PANEL_EVENT_DISPLAY_ON_PREPARE, +}; + +#ifdef CONFIG_PANEL_NOTIFICATIONS + +int panel_register_notifier(struct notifier_block *nb); +int panel_unregister_notifier(struct notifier_block *nb); +int panel_notify(unsigned int event, void *data); + +#else + +#include +static inline int panel_register_notifier(struct notifier_block *nb) +{ + return -ENOSYS; +} +static inline int panel_unregister_notifier(struct notifier_block *nb) +{ + return -ENOSYS; +} +static inline int panel_notify(unsigned int event, void *data) +{ + return -ENOSYS; +} + +#endif +#endif diff --git a/include/linux/pen_detection_notify.h b/include/linux/pen_detection_notify.h new file mode 100644 index 000000000000..87f1898d5de9 --- /dev/null +++ b/include/linux/pen_detection_notify.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2019, The Linux Foundation. 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 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. + */ +#ifndef _PEN_DETECTION_DRM_NOTIFY_H_ +#define _PEN_DETECTION_DRM_NOTIFY_H_ + +#include + +/* The event of pen insertion */ +#define PEN_DETECTION_INSERT 0x01 +/* The event of pen pull-out */ +#define PEN_DETECTION_PULL 0x02 + +int pen_detection_register_client(struct notifier_block *nb); +int pen_detection_unregister_client(struct notifier_block *nb); +#endif diff --git a/include/linux/platform_data/cs40l2x.h b/include/linux/platform_data/cs40l2x.h new file mode 100644 index 000000000000..881a379f2566 --- /dev/null +++ b/include/linux/platform_data/cs40l2x.h @@ -0,0 +1,32 @@ +/* + * linux/platform_data/cs40l2x.h -- Platform data for + * CS40L20/CS40L25/CS40L25A/CS40L25B + * + * Copyright 2018 Cirrus Logic, Inc. + * + * 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. + */ + +#ifndef __CS40L2X_H +#define __CS40L2X_H + +struct cs40l2x_platform_data { + unsigned int boost_ind; + unsigned int boost_cap; + unsigned int boost_ipk; + bool refclk_gpio2; + unsigned int f0_default; + unsigned int f0_min; + unsigned int f0_max; + unsigned int redc_default; + unsigned int redc_min; + unsigned int redc_max; + unsigned int gpio1_rise_index; + unsigned int gpio1_fall_index; + unsigned int gpio1_fall_timeout; + unsigned int gpio1_mode; +}; + +#endif /* __CS40L2X_H */ diff --git a/include/linux/power/bm_adsp_ulog.h b/include/linux/power/bm_adsp_ulog.h new file mode 100644 index 000000000000..91d9a294df9d --- /dev/null +++ b/include/linux/power/bm_adsp_ulog.h @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2021, The Linux Foundation. All rights reserved. + */ + +#ifndef __BM_ADSP_ULOG_H__ +#define __BM_ADSP_ULOG_H__ + +/* Generic definitions */ +enum bm_ulog_level_type { + BM_LOG_LEVEL_NONE = 0, + BM_LOG_LEVEL_ERR = 1, + BM_LOG_LEVEL_WARN = 2, + BM_LOG_LEVEL_INFO = 3, + BM_LOG_LEVEL_DEBUG = 4, + BM_LOG_LEVEL_ALL_LOGS +}; + +enum bm_ulog_category_bitmap { + BM_WPP_SELF_HOST = 1ULL << 0, + BM_WPP_DEVICE = 1ULL << 1, + BM_WPP_IOIMPL = 1ULL << 2, + BM_PMIC_WPP_DEVICE = 1ULL << 3, + BM_WPP_NONE = 1ULL << 4, + BM_WPP_SMCHGFGGGE = 1ULL << 5, + BM_WPP_PLATFORM = 1ULL << 6, + BM_WPP_IRQ = 1ULL << 7, + BM_WPP_NOTIFY = 1ULL << 8, + BM_WPP_STATEMACHINE = 1ULL << 9, + BM_WPP_STATEMACHINELEVEL = 1ULL << 10, + BM_WPP_BATTERRHANDLE = 1ULL << 11, + BM_WPP_FGGGE = 1ULL << 12, + BM_WPP_PARALLELCHARGING = 1ULL << 13, + BM_WPP_SMCHG = 1ULL << 14, + BM_WPP_SWJEITA = 1ULL << 15, + BM_WPP_QBG = 1ULL << 16, + BM_QBG = 1ULL << 17, + BM_ADC = 1ULL << 18, + BM_CAD = 1ULL << 19, + BM_PMICPLATFORM = 1ULL << 20, + BM_PMICHAL = 1ULL << 21, + BM_AGGREGATOR = 1ULL << 22, + BM_SUMMARY = 1ULL << 23, + BM_USBC_COMMON = 1ULL << 24, + BM_USBC_DPM = 1ULL << 25, + BM_USBC_LPM = 1ULL << 26, + BM_USBC_PRL = 1ULL << 27, + BM_USBC_PE = 1ULL << 28, + BM_USBC_TCPC = 1ULL << 29, + BM_USBC_TCPCPLATFORM = 1ULL << 30, + BM_USBC_SUMMARY = 1ULL << 31, + BM_INIT = 1ULL << 32, + BM_ULOG = 1ULL << 33, + BM_ICMLOG = 1ULL << 34, + BM_SSDEVLOG = 1ULL << 35, + BM_USBC_ULOG = 1ULL << 36, + BM_LOGCATEGORYMAX = 1ULL << 63, +}; + +#define BM_USBC_ALL ( \ + BM_USBC_COMMON | BM_USBC_DPM | BM_USBC_LPM | BM_USBC_PRL | BM_USBC_PE | \ + BM_USBC_TCPC | BM_USBC_TCPCPLATFORM | BM_USBC_SUMMARY | BM_USBC_ULOG) + +#define BM_BM_ALL ( \ + BM_WPP_SELF_HOST | BM_WPP_DEVICE | BM_WPP_IOIMPL | \ + BM_PMIC_WPP_DEVICE | BM_WPP_NONE | BM_WPP_SMCHGFGGGE | \ + BM_WPP_PLATFORM | BM_WPP_IRQ | BM_WPP_NOTIFY | \ + BM_WPP_STATEMACHINE | BM_WPP_STATEMACHINELEVEL | \ + BM_WPP_BATTERRHANDLE | BM_WPP_FGGGE | \ + BM_WPP_PARALLELCHARGING | BM_WPP_SMCHG | \ + BM_WPP_SWJEITA | BM_WPP_QBG | BM_QBG | \ + BM_ADC | BM_CAD | BM_PMICPLATFORM | \ + BM_PMICHAL | BM_SUMMARY | BM_INIT | \ + BM_ULOG | BM_ICMLOG | BM_SSDEVLOG) + +#define BM_ALL (BM_USBC_ALL | BM_BM_ALL) + +/* exported APIs */ +int bm_ulog_get_log(char *buf, u32 size); +int bm_ulog_get_mask_log(enum bm_ulog_category_bitmap categories, + enum bm_ulog_level_type level, + char *buf, u32 size); + +int bm_ulog_print_log(u32 size); +int bm_ulog_print_mask_log(enum bm_ulog_category_bitmap categories, + enum bm_ulog_level_type level, u32 size); + +#endif /* __BM_ADSP_ULOG_H__ */ diff --git a/include/linux/power/max170xx_battery_mmi.h b/include/linux/power/max170xx_battery_mmi.h new file mode 100644 index 000000000000..8d769f804e19 --- /dev/null +++ b/include/linux/power/max170xx_battery_mmi.h @@ -0,0 +1,263 @@ +/* + * Fuel gauge driver for Maxim 17042 / 8966 / 8997 + * Note that Maxim 8966 and 8997 are mfd and this is its subdevice. + * + * Copyright (C) 2011 Samsung Electronics + * MyungJoo Ham + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __MAX170XX_BATTERY_MMI_H_ +#define __MAX170XX_BATTERY_MMI_H_ + +#define MAX17042_STATUS_BattAbsent (1 << 3) +#define MAX17042_BATTERY_FULL (100) +#define MAX17042_DEFAULT_SNS_RESISTOR (10000) + +#define MAX17042_CHARACTERIZATION_DATA_SIZE 48 + +#define MAX17042_CONFIG_TEX (BIT(0)<<8) +#define MAX17042_CONFIG_TEN (BIT(1)<<8) +#define MAX17042_ALRT_MSK_MAX 0xFF00 +#define MAX17042_ALRT_MSK_MIN 0x00FF + +enum max17042_register { + MAX17042_STATUS = 0x00, + MAX17042_VALRT_Th = 0x01, + MAX17042_TALRT_Th = 0x02, + MAX17042_SALRT_Th = 0x03, + MAX17042_AtRate = 0x04, + MAX17042_RepCap = 0x05, + MAX17042_RepSOC = 0x06, + MAX17042_Age = 0x07, + MAX17042_TEMP = 0x08, + MAX17042_VCELL = 0x09, + MAX17042_Current = 0x0A, + MAX17042_AvgCurrent = 0x0B, + + MAX17042_SOC = 0x0D, + MAX17042_AvSOC = 0x0E, + MAX17042_RemCap = 0x0F, + MAX17042_FullCAP = 0x10, + MAX17042_TTE = 0x11, + MAX17042_V_empty = 0x12, + + MAX17042_RSLOW = 0x14, + + MAX17042_AvgTA = 0x16, + MAX17042_Cycles = 0x17, + MAX17042_DesignCap = 0x18, + MAX17042_AvgVCELL = 0x19, + MAX17042_MinMaxTemp = 0x1A, + MAX17042_MinMaxVolt = 0x1B, + MAX17042_MinMaxCurr = 0x1C, + MAX17042_CONFIG = 0x1D, + MAX17042_ICHGTerm = 0x1E, + MAX17042_AvCap = 0x1F, + MAX17042_ManName = 0x20, + MAX17042_DevName = 0x21, + + MAX17042_FullCAPNom = 0x23, + MAX17042_TempNom = 0x24, + MAX17042_TempLim = 0x25, + MAX17042_TempHot = 0x26, + MAX17042_AIN = 0x27, + MAX17042_LearnCFG = 0x28, + MAX17042_FilterCFG = 0x29, + MAX17042_RelaxCFG = 0x2A, + MAX17042_MiscCFG = 0x2B, + MAX17042_TGAIN = 0x2C, + MAx17042_TOFF = 0x2D, + MAX17042_CGAIN = 0x2E, + MAX17042_COFF = 0x2F, + + MAX17042_MaskSOC = 0x32, + MAX17042_SOC_empty = 0x33, + MAX17042_T_empty = 0x34, + + MAX17042_FullCAP0 = 0x35, + MAX17042_LAvg_empty = 0x36, + MAX17042_FCTC = 0x37, + MAX17042_RCOMP0 = 0x38, + MAX17042_TempCo = 0x39, + MAX17042_EmptyTempCo = 0x3A, + MAX17042_K_empty0 = 0x3B, + MAX17042_TaskPeriod = 0x3C, + MAX17042_FSTAT = 0x3D, + + MAX17042_SHDNTIMER = 0x3F, + + MAX17042_dQacc = 0x45, + MAX17042_dPacc = 0x46, + + MAX17042_VFSOC0 = 0x48, + + MAX17042_QH0 = 0x4C, + MAX17042_QH = 0x4D, + MAX17042_QL = 0x4E, + + MAX17042_VFSOC0Enable = 0x60, + MAX17042_MLOCKReg1 = 0x62, + MAX17042_MLOCKReg2 = 0x63, + + MAX17042_MODELChrTbl = 0x80, + + MAX17042_OCV = 0xEE, + + MAX17042_OCVInternal = 0xFB, + + MAX17042_VFSOC = 0xFF, +}; + +/* Registers specific to max17047/50 */ +enum max17047_register { + MAX17047_QRTbl00 = 0x12, + MAX17047_FullSOCThr = 0x13, + MAX17047_QRTbl10 = 0x22, + MAX17047_QRTbl20 = 0x32, + MAX17047_V_empty = 0x3A, + MAX17047_QRTbl30 = 0x42, +}; + +enum max170xx_chip_type { + MAXIM_DEVICE_TYPE_UNKNOWN = 0, + MAXIM_DEVICE_TYPE_MAX17042, + MAXIM_DEVICE_TYPE_MAX17047, + MAXIM_DEVICE_TYPE_MAX17050, + + MAXIM_DEVICE_TYPE_NUM +}; + +/* + * used for setting a register to a desired value + * addr : address for a register + * data : setting value for the register + */ +struct max17042_reg_data { + u8 addr; + u16 data; +}; + +struct max17042_config_data { + /* Revision of this Data*/ + u16 revision; + + /* External current sense resistor value in milli-ohms */ + u32 cur_sense_val; + + /* A/D measurement */ + u16 tgain; /* 0x2C */ + u16 toff; /* 0x2D */ + u16 cgain; /* 0x2E */ + u16 coff; /* 0x2F */ + + /* Alert / Status */ + u16 valrt_thresh; /* 0x01 */ + u16 talrt_thresh; /* 0x02 */ + u16 soc_alrt_thresh; /* 0x03 */ + u16 config; /* 0x01D */ + u16 shdntimer; /* 0x03F */ + + /* App data */ + u16 full_soc_thresh; /* 0x13 */ + u16 design_cap; /* 0x18 */ + u16 ichgt_term; /* 0x1E */ + + /* MG3 config */ + u16 at_rate; /* 0x04 */ + u16 learn_cfg; /* 0x28 */ + u16 filter_cfg; /* 0x29 */ + u16 relax_cfg; /* 0x2A */ + u16 misc_cfg; /* 0x2B */ + u16 masksoc; /* 0x32 */ + + /* MG3 save and restore */ + u16 fullcap; /* 0x10 */ + u16 fullcapnom; /* 0x23 */ + u16 socempty; /* 0x33 */ + u16 lavg_empty; /* 0x36 */ + u16 dqacc; /* 0x45 */ + u16 dpacc; /* 0x46 */ + u16 qrtbl00; /* 0x12 */ + u16 qrtbl10; /* 0x22 */ + u16 qrtbl20; /* 0x32 */ + u16 qrtbl30; /* 0x42 */ + + /* Cell technology from power_supply.h */ + u16 cell_technology; + + /* Cell Data */ + u16 vempty; /* 0x12 */ + u16 temp_nom; /* 0x24 */ + u16 temp_lim; /* 0x25 */ + u16 fctc; /* 0x37 */ + u16 rcomp0; /* 0x38 */ + u16 tcompc0; /* 0x39 */ + u16 empty_tempco; /* 0x3A */ + u16 kempty0; /* 0x3B */ + u16 cell_char_tbl[MAX17042_CHARACTERIZATION_DATA_SIZE]; +} __packed; + +/* + * used to convert a value from Temperature register to a "real" temp value. + * This conversion table can be used when configuring tgain and toff is not + * sufficient to get accurate temperature measurements. + * The result[0] corresponds to start temp, result[1] to (start + 1) temp, etc. + */ +struct max17042_temp_conv { + s16 start; /* Centigrade */ + s16 *result; /* Deci-centigrade */ + int num_result; /* Number of entries in result array */ +}; + +struct max17042_platform_data { + struct max17042_reg_data *init_data; + struct max17042_config_data *config_data; + int num_init_data; /* Number of enties in init_data array */ + struct gpio *gpio_list; + int num_gpio_list; /* Number of entries in gpio_list array */ + struct max17042_temp_conv *tcnv; /* temp conversion table */ + bool enable_current_sense; + bool enable_por_init; /* Use POR init from Maxim appnote */ + + /* + * R_sns in micro-ohms. + * default 10000 (if r_sns = 0) as it is the recommended value by + * the datasheet although it can be changed by board designers. + */ + unsigned int r_sns; + int vmin; /* in millivolts */ + int vmax; /* in millivolts */ + int temp_min; /* in tenths of degree Celsius */ + int temp_max; /* in tenths of degree Celsius */ + + /* + * Enable this flag to report "0" SOC iff battery undervoltage interrupt + * has fired. + */ + bool batt_undervoltage_zero_soc; + + const char *batt_psy_name; + int warm_temp_c; + int hot_temp_c; + int cool_temp_c; + int cold_temp_c; + int hotspot_thrs_c; + bool use_external_temp; + bool main_psy; +}; + +#endif /* __MAX170XX_BATTERY_MMI_H_ */ diff --git a/include/linux/power/mmi_chg_tcmd.h b/include/linux/power/mmi_chg_tcmd.h new file mode 100644 index 000000000000..bddf6f2f89c8 --- /dev/null +++ b/include/linux/power/mmi_chg_tcmd.h @@ -0,0 +1,61 @@ +#ifndef __MOTO_CHG_TCMD_H__ +#define __MOTO_CHG_TCMD_H__ + +enum MOTO_CHG_TCMD_CLIENT_ID { + MOTO_CHG_TCMD_CLIENT_CHG, + MOTO_CHG_TCMD_CLIENT_BAT, + MOTO_CHG_TCMD_CLIENT_AP_ADC, + MOTO_CHG_TCMD_CLIENT_PM_ADC, + MOTO_CHG_TCMD_ClIENT_MAX +}; + + +enum MOTO_ADC_TCMD_CHANNEL { + MOTO_ADC_TCMD_CHANNEL_CPU = 0, + MOTO_ADC_TCMD_CHANNEL_CHG, + MOTO_ADC_TCMD_CHANNEL_PA, + MOTO_ADC_TCMD_CHANNEL_BATID, + MOTO_ADC_TCMD_CHANNEL_VBAT, + MOTO_ADC_TCMD_CHANNEL_MAX +}; + +struct CHARGER_TEMPERATURE { + __s32 CHARGER_Temp; + __s32 TemperatureR; +}; + +struct mmi_chg_tcmd_client { + void *data; + int client_id; + + bool factory_kill_disable; + +#ifdef USE_LIST_HEAD + struct list_head list; +#endif + + int (*reg_read)(void *input, unsigned char reg, unsigned char *val);//hex + int (*reg_write)(void *input, unsigned char reg, unsigned char val);//hex + + int (*get_chg_current)(void *input, int* val);//unit mA + int (*set_chg_current)(void *input, int val);//unit mA + int (*set_chg_enable)(void *input, int val); + + int (*get_usb_current)(void *input, int* val);//unit mA + int (*set_usb_current)(void *input, int val);//unit mA + int (*set_usb_enable)(void *input, int val); + + int (*get_usb_voltage)(void *input, int* val);//unit uV + int (*get_charger_type)(void *input, int* val); + + int (*get_bat_temp)(void *input, int* val);//unit C + int (*get_bat_voltage)(void *input, int* val);//unit uV + int (*get_bat_ocv)(void *input, int* val);//unit mV + int (*get_bat_id)(void *input, int* val); + + int (*get_adc_value)(void *input, int channel, int* val); +}; + +int mmi_chg_tcmd_register(struct mmi_chg_tcmd_client *client); + +#endif diff --git a/include/linux/sensors.h b/include/linux/sensors.h new file mode 100644 index 000000000000..2362daaf3db3 --- /dev/null +++ b/include/linux/sensors.h @@ -0,0 +1,177 @@ +/* Copyright (c) 2013-2014, The Linux Foundation. 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 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. + */ + +#ifndef __LINUX_SENSORS_H_INCLUDED +#define __LINUX_SENSORS_H_INCLUDED + +#include +#include +#include + +#define SENSOR_TYPE_DEVICE_PRIVATE_BASE 0x10000 + +#define SENSORS_ACCELERATION_HANDLE 0 +#define SENSORS_MAGNETIC_FIELD_HANDLE 1 +#define SENSORS_ORIENTATION_HANDLE 2 +#define SENSORS_LIGHT_HANDLE 3 +#define SENSORS_PROXIMITY_HANDLE 4 +#define SENSORS_GYROSCOPE_HANDLE 5 +#define SENSORS_PRESSURE_HANDLE 6 + +#define SENSOR_TYPE_ACCELEROMETER 1 +#define SENSOR_TYPE_GEOMAGNETIC_FIELD 2 +#define SENSOR_TYPE_MAGNETIC_FIELD SENSOR_TYPE_GEOMAGNETIC_FIELD +#define SENSOR_TYPE_ORIENTATION 3 +#define SENSOR_TYPE_GYROSCOPE 4 +#define SENSOR_TYPE_LIGHT 5 +#define SENSOR_TYPE_PRESSURE 6 +#define SENSOR_TYPE_TEMPERATURE 7 +#define SENSOR_TYPE_PROXIMITY 8 +#define SENSOR_TYPE_GRAVITY 9 +#define SENSOR_TYPE_LINEAR_ACCELERATION 10 +#define SENSOR_TYPE_ROTATION_VECTOR 11 +#define SENSOR_TYPE_RELATIVE_HUMIDITY 12 +#define SENSOR_TYPE_AMBIENT_TEMPERATURE 13 +#define SENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED 14 +#define SENSOR_TYPE_GAME_ROTATION_VECTOR 15 +#define SENSOR_TYPE_GYROSCOPE_UNCALIBRATED 16 +#define SENSOR_TYPE_SIGNIFICANT_MOTION 17 +#define SENSOR_TYPE_STEP_DETECTOR 18 +#define SENSOR_TYPE_STEP_COUNTER 19 +#define SENSOR_TYPE_GEOMAGNETIC_ROTATION_VECTOR 20 + +#define SENSOR_TYPE_MOTO_CAPSENSE (SENSOR_TYPE_DEVICE_PRIVATE_BASE + 16) + +#define SENSOR_TYPE_MOTO_HALL (SENSOR_TYPE_DEVICE_PRIVATE_BASE+44) + +#define SENSOR_TYPE_MOTO_DOUBLE_TAP (SENSOR_TYPE_DEVICE_PRIVATE_BASE + 30) +#define SENSOR_TYPE_MOTO_TOUCH_PALM (SENSOR_TYPE_DEVICE_PRIVATE_BASE + 31) +#define SENSOR_TYPE_MOTO_BODYTEMP (SENSOR_TYPE_DEVICE_PRIVATE_BASE + 42) +enum LIS3DH_AXIS { + AXIS_X = 0, + AXIS_Y, + AXIS_Z, + AXIS_XYZ, +}; + +enum LIS3DH_THRES { + AXIS_THRESHOLD_H = 0, + AXIS_THRESHOLD_L, + AXIS_BIAS, +}; + +#define AXIS_FACTOR 0 +#define AXIS_OFFSET 1 + + +struct cal_result_t { + union { + + struct { + int offset_x; /*axis offset of x axis*/ + int offset_y; /*axis offset of x axis*/ + int offset_z; /*axis offset of x axis*/ + }; + struct { + int threshold_h; /*proximity threshold_h*/ + int threshold_l; /*proximity threshold_l*/ + int bias; /*proximity measure data noise*/ + }; + int offset[3]; + }; + int factor; /*light sensor factor for real ligt strength*/ + int range; + struct cal_result_t *node; +}; + +/** + * struct sensors_classdev - hold the sensor general parameters and APIs + * @dev: The device to register. + * @node: The list for the all the sensor drivers. + * @name: Name of this sensor. + * @vendor: The vendor of the hardware part. + * @handle: The handle that identifies this sensors. + * @type: The sensor type. + * @max_range: The maximum range of this sensor's value in SI units. + * @resolution: The smallest difference between two values reported by + * this sensor. + * @sensor_power: The rough estimate of this sensor's power consumption + * in mA. + * @min_delay: This value depends on the trigger mode: + * continuous: minimum period allowed in microseconds + * on-change : 0 + * one-shot :-1 + * special : 0, unless otherwise noted + * @fifo_reserved_event_count: The number of events reserved for this sensor + * in the batch mode FIFO. + * @fifo_max_event_count: The maximum number of events of this sensor + * that could be batched. + * @max_delay: The slowest rate the sensor supports in millisecond. + * @flags: Should be '1' if the sensor is a wake up sensor. + * set it to '0' otherwise. + * @enabled: Store the sensor driver enable status. + * @delay_msec: Store the sensor driver delay value. The data unit is + * millisecond. + * @wakeup: Indicate if the wake up interrupt has been enabled. + * @max_latency: Max report latency in millisecond + * @sensors_enable: The handle for enable and disable sensor. + * @sensors_poll_delay: The handle for set the sensor polling delay time. + * @sensors_set_latency:Set the max report latency of the sensor. + * @sensors_flush: Flush sensor events in FIFO and report it to user space. + * @params The sensor calibrate string format params up to userspace. + * @cal_result The sensor calibrate parameters, cal_result is a struct for sensor. + */ +struct sensors_classdev { + struct device *dev; + struct list_head node; + const char *name; + const char *vendor; + int version; + int handle; + int type; + const char *max_range; + const char *resolution; + const char *sensor_power; + int min_delay; + int fifo_reserved_event_count; + int fifo_max_event_count; + int32_t max_delay; + uint32_t flags; + + unsigned int enabled; + unsigned int delay_msec; + unsigned int wakeup; + unsigned int max_latency; + char *params; + struct cal_result_t cal_result; + /* enable and disable the sensor handle*/ + int (*sensors_enable)(struct sensors_classdev *sensors_cdev, + unsigned int enabled); + int (*sensors_poll_delay)(struct sensors_classdev *sensors_cdev, + unsigned int delay_msec); + int (*sensors_self_test)(struct sensors_classdev *sensors_cdev); + int (*sensors_set_latency)(struct sensors_classdev *sensor_cdev, + unsigned int max_latency); + int (*sensors_enable_wakeup)(struct sensors_classdev *sensor_cdev, + unsigned int enable); + int (*sensors_flush)(struct sensors_classdev *sensors_cdev); + int (*sensors_calibrate)(struct sensors_classdev *sensor_cdev, + int axis, int apply_now); + int (*sensors_write_cal_params)(struct sensors_classdev + *sensor_cdev, struct cal_result_t *cal_result); +}; + +extern int sensors_classdev_register(struct device *parent, + struct sensors_classdev *sensors_cdev); +extern void sensors_classdev_unregister(struct sensors_classdev *sensors_cdev); + +#endif /* __LINUX_SENSORS_H_INCLUDED */ diff --git a/include/linux/sr100.h b/include/linux/sr100.h new file mode 100644 index 000000000000..0975fd49c31f --- /dev/null +++ b/include/linux/sr100.h @@ -0,0 +1,53 @@ +/*====================================================================================*/ +/* */ +/* Copyright 2018-2019 NXP */ +/* */ +/* 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. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* */ +/*====================================================================================*/ + +#include + +#define SR100_MAGIC 0xEA +#define SR100_SET_PWR _IOW(SR100_MAGIC, 0x01, long) +#define SR100_SET_DBG _IOW(SR100_MAGIC, 0x02, long) +#define SR100_SET_POLL _IOW(SR100_MAGIC, 0x03, long) +#define SR100_SET_FWD _IOW(SR100_MAGIC, 0x04, long) +#define SR100_GET_THROUGHPUT _IOW(SR100_MAGIC, 0x05, long) +#define SR100_ESE_RESET _IOW(SR100_MAGIC, 0x06, long) + +#define NORMAL_MODE_HEADER_LEN 4 +#define HBCI_MODE_HEADER_LEN 4 +#define NORMAL_MODE_LEN_OFFSET 3 +#define UCI_NORMAL_PKT_SIZE 0 + +#define EXTND_LEN_INDICATOR_OFFSET 1 +#define EXTND_LEN_INDICATOR_OFFSET_MASK 0x80 +#define EXTENDED_LENGTH_OFFSET 2 + +struct sr100_spi_platform_data { + unsigned int irq_gpio; + unsigned int ce_gpio; + unsigned int spi_handshake_gpio; + unsigned int rtc_sync_gpio; + unsigned int vdd_1v8_gpio; /* to control VDD for super interposer board */ + unsigned int vdd_1v8_rf_gpio; + unsigned int vbat_3v6_gpio; +}; +enum { + PWR_DISABLE = 0, + PWR_ENABLE, + ABORT_READ_PENDING +}; diff --git a/include/linux/st21nfc.h b/include/linux/st21nfc.h new file mode 100644 index 000000000000..4be75fa53cd0 --- /dev/null +++ b/include/linux/st21nfc.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2013 ST Microelectronics S.A. + * Copyright (C) 2010 Stollmann E+V GmbH + * Copyright (C) 2010 Trusted Logic S.A. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#define ST21NFC_MAGIC 0xEA + +#define ST21NFC_NAME "st21nfc" +/* + * ST21NFC power control via ioctl + * ST21NFC_GET_WAKEUP : poll gpio-level for Wakeup pin + */ +#define ST21NFC_GET_WAKEUP _IO(ST21NFC_MAGIC, 0x01) +#define ST21NFC_PULSE_RESET _IO(ST21NFC_MAGIC, 0x02) +#define ST21NFC_SET_POLARITY_RISING _IO(ST21NFC_MAGIC, 0x03) +#define ST21NFC_SET_POLARITY_FALLING _IO(ST21NFC_MAGIC, 0x04) +#define ST21NFC_SET_POLARITY_HIGH _IO(ST21NFC_MAGIC, 0x05) +#define ST21NFC_SET_POLARITY_LOW _IO(ST21NFC_MAGIC, 0x06) +#define ST21NFC_GET_POLARITY _IO(ST21NFC_MAGIC, 0x07) +#define ST21NFC_RECOVERY _IO(ST21NFC_MAGIC, 0x08) +#define ST21NFC_USE_ESE _IOW(ST21NFC_MAGIC, 0x09, unsigned int) + +// Keep compatibility with older user applications. +#define ST21NFC_LEGACY_GET_WAKEUP _IOR(ST21NFC_MAGIC, 0x01, unsigned int) +#define ST21NFC_LEGACY_PULSE_RESET _IOR(ST21NFC_MAGIC, 0x02, unsigned int) +#define ST21NFC_LEGACY_SET_POLARITY_RISING _IOR(ST21NFC_MAGIC, 0x03, \ + unsigned int) +#define ST21NFC_LEGACY_SET_POLARITY_FALLING _IOR(ST21NFC_MAGIC, 0x04, \ + unsigned int) +#define ST21NFC_LEGACY_SET_POLARITY_HIGH _IOR(ST21NFC_MAGIC, 0x05, unsigned int) +#define ST21NFC_LEGACY_SET_POLARITY_LOW _IOR(ST21NFC_MAGIC, 0x06, unsigned int) +#define ST21NFC_LEGACY_GET_POLARITY _IOR(ST21NFC_MAGIC, 0x07, unsigned int) +#define ST21NFC_LEGACY_RECOVERY _IOR(ST21NFC_MAGIC, 0x08, unsigned int) + +struct st21nfc_platform_data { + unsigned int irq_gpio; + unsigned int ena_gpio; + unsigned int reset_gpio; + unsigned int clkreq_gpio; + unsigned int polarity_mode; +}; + +#define ST54SPI_CB_RESET_END 0 +#define ST54SPI_CB_RESET_START 1 +#define ST54SPI_CB_ESE_NOT_USED 2 +#define ST54SPI_CB_ESE_USED 3 +void st21nfc_register_st54spi_cb(void (*cb)(int, void *), void *data); +void st21nfc_unregister_st54spi_cb(void); diff --git a/include/linux/touchscreen_mmi.h b/include/linux/touchscreen_mmi.h new file mode 100644 index 000000000000..ccee2f10105e --- /dev/null +++ b/include/linux/touchscreen_mmi.h @@ -0,0 +1,613 @@ +/* + * Copyright (C) 2019 Motorola Mobility LLC + * + * 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. + */ + +#ifndef __LINUX_TOUCHSCREEN_MMI_H_ +#define __LINUX_TOUCHSCREEN_MMI_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_PANEL_NOTIFICATIONS) + +#include + +#define REGISTER_PANEL_NOTIFIER {\ + touch_cdev->panel_nb.notifier_call = ts_mmi_panel_cb; \ + ret = panel_register_notifier(&touch_cdev->panel_nb); \ +} + +#define UNREGISTER_PANEL_NOTIFIER {\ + panel_unregister_notifier(&touch_cdev->panel_nb); \ +} + +#define GET_CONTROL_DSI_INDEX { \ + if (evd) \ + idx = *(int *)evd; \ +} + +#define EVENT_PRE_DISPLAY_OFF \ + (event == PANEL_EVENT_PRE_DISPLAY_OFF) + +#define EVENT_DISPLAY_OFF \ + (event == PANEL_EVENT_DISPLAY_OFF) + +#define EVENT_PRE_DISPLAY_ON \ + (event == PANEL_EVENT_PRE_DISPLAY_ON) + +#define EVENT_DISPLAY_ON \ + (event == PANEL_EVENT_DISPLAY_ON) + +#else /* CONFIG_PANEL_NOTIFICATIONS */ +#if defined(CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS) +#include + +#define REGISTER_PANEL_NOTIFIER { \ + void *cookie = NULL; \ + struct ts_mmi_dev_pdata *ppdata = &touch_cdev->pdata; \ + if (!ppdata->ctrl_dsi) { \ + cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_PRIMARY, \ + PANEL_EVENT_NOTIFIER_CLIENT_PRIMARY_TOUCH, touch_cdev->active_panel, \ + &ts_mmi_panel_cb, touch_cdev); \ + } else { \ + cookie = panel_event_notifier_register(PANEL_EVENT_NOTIFICATION_SECONDARY, \ + PANEL_EVENT_NOTIFIER_CLIENT_SECONDARY_TOUCH, touch_cdev->active_panel, \ + &ts_mmi_panel_cb, touch_cdev); \ + } \ + if (!cookie) \ + ret = -1; \ + else \ + touch_cdev->notifier_cookie = cookie; \ +} + +#define UNREGISTER_PANEL_NOTIFIER { \ + panel_event_notifier_unregister(touch_cdev->notifier_cookie); \ +} + +#define EVENT_PRE_DISPLAY_OFF \ + ((event == DRM_PANEL_EVENT_BLANK) && \ + (evdata.early_trigger)) + +#define EVENT_DISPLAY_OFF \ + ((event == DRM_PANEL_EVENT_BLANK) && \ + (!evdata.early_trigger)) + +#define EVENT_PRE_DISPLAY_ON \ + ((event == DRM_PANEL_EVENT_UNBLANK) && \ + (evdata.early_trigger)) + +#define EVENT_DISPLAY_ON \ + ((event == DRM_PANEL_EVENT_UNBLANK) && \ + (!evdata.early_trigger)) + +#else /* CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS */ +#if defined(CONFIG_DRM_PANEL_NOTIFICATIONS) +#include +#define REGISTER_PANEL_NOTIFIER {\ + touch_cdev->panel_nb.notifier_call = ts_mmi_panel_cb; \ + ret = drm_panel_notifier_register(touch_cdev->active_panel, &touch_cdev->panel_nb); \ +} + +#define UNREGISTER_PANEL_NOTIFIER {\ + drm_panel_notifier_unregister(touch_cdev->active_panel, &touch_cdev->panel_nb);\ +} + +#define GET_CONTROL_DSI_INDEX \ +int *blank; \ +struct drm_panel_notifier *evdata = evd; \ +{ \ + if (!(evdata && evdata->data)) { \ + dev_dbg(DEV_MMI, "%s: invalid evdata\n", __func__); \ + return 0; \ + } \ + idx = 0;\ + blank = (int *)evdata->data; \ + dev_dbg(DEV_MMI, "%s: drm notification: event = %lu, blank = %d\n", \ + __func__, event, *blank); \ +} + +#define EVENT_PRE_DISPLAY_OFF \ + ((event == DRM_PANEL_EARLY_EVENT_BLANK) && \ + (*blank == DRM_PANEL_BLANK_POWERDOWN)) + +#define EVENT_DISPLAY_OFF \ + ((event == DRM_PANEL_EVENT_BLANK) && \ + (*blank == DRM_PANEL_BLANK_POWERDOWN)) + +#define EVENT_PRE_DISPLAY_ON \ + ((event == DRM_PANEL_EARLY_EVENT_BLANK) && \ + (*blank == DRM_PANEL_BLANK_UNBLANK)) + +#define EVENT_DISPLAY_ON \ + ((event == DRM_PANEL_EVENT_BLANK) && \ + (*blank == DRM_PANEL_BLANK_UNBLANK)) + +#else /* CONFIG_DRM_PANEL_NOTIFICATIONS */ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,14,0) +#if defined(CONFIG_DRM_MSM) + +#include + +#define REGISTER_PANEL_NOTIFIER {\ + touch_cdev->panel_nb.notifier_call = ts_mmi_panel_cb; \ + ret = msm_drm_register_client(&touch_cdev->panel_nb); \ +} + +#define UNREGISTER_PANEL_NOTIFIER {\ + msm_drm_unregister_client(&touch_cdev->panel_nb); \ +} + +#define GET_CONTROL_DSI_INDEX \ +int *blank; \ +struct msm_drm_notifier *evdata = evd; \ +{ \ + if (!(evdata && evdata->data)) { \ + dev_dbg(DEV_MMI, "%s: invalid evdata\n", __func__); \ + return 0; \ + } \ + idx = evdata->id; \ + blank = (int *)evdata->data; \ + dev_dbg(DEV_MMI, "%s: drm notification: event = %lu, blank = %d\n", \ + __func__, event, *blank); \ +} + +#define EVENT_PRE_DISPLAY_OFF \ + ((event == MSM_DRM_EARLY_EVENT_BLANK) && \ + (*blank == MSM_DRM_BLANK_POWERDOWN)) + +#define EVENT_DISPLAY_OFF \ + ((event == MSM_DRM_EVENT_BLANK) && \ + (*blank == MSM_DRM_BLANK_POWERDOWN)) + +#define EVENT_PRE_DISPLAY_ON \ + ((event == MSM_DRM_EARLY_EVENT_BLANK) && \ + (*blank == MSM_DRM_BLANK_UNBLANK)) + +#define EVENT_DISPLAY_ON \ + ((event == MSM_DRM_EVENT_BLANK) && \ + (*blank == MSM_DRM_BLANK_UNBLANK)) + +/* enable internal config option to refernece in the code */ +#define CONFIG_MSM_DRM_NOTIFICATIONS + +#else /* CONFIG_DRM_MSM */ + +#warning That isn't supposed to happen!!! +#define register_panel_notifier(...) ret +#define unregister_panel_notifier(...) + +#endif /* CONFIG_DRM_MSM */ +#else /* LINUX_VERSION_CODE */ + +#warning Panel notifier undefined!!! +#define register_panel_notifier(...) ret +#define unregister_panel_notifier(...) + +#endif /* LINUX_VERSION_CODE */ +#endif /* CONFIG_DRM_PANEL_NOTIFICATIONS */ +#endif /* CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS */ +#endif /* CONFIG_PANEL_NOTIFICATIONS */ + + +#if defined (TS_MMI_TOUCH_GESTURE_POISON_EVENT) +#ifndef TS_MMI_TOUCH_EDGE_GESTURE +#define TS_MMI_TOUCH_EDGE_GESTURE +#endif +#endif + + +#define NANO_SEC 1000000000 +#define SEC_TO_MSEC 1000 +#define NANO_TO_MSEC 1000000 + +static inline unsigned long long timediff_ms( + struct TIME_SPEC start, struct TIME_SPEC end) +{ + struct TIME_SPEC temp; + + if ((end.tv_nsec - start.tv_nsec) < 0) { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = NANO_SEC + end.tv_nsec - start.tv_nsec; + } else { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return (temp.tv_sec * SEC_TO_MSEC) + (temp.tv_nsec / NANO_TO_MSEC); +} + +#define TS_MMI_MAX_POINT_NUM 10 +#define TS_MMI_MAX_FW_PATH 64 +#define TS_MMI_MAX_FULL_FW_PATH 128 +#define TS_MMI_MAX_ID_LEN 16 +#define TS_MMI_MAX_VENDOR_LEN 16 +#define TS_MMI_MAX_INFO_LEN 16 +#define TS_MMI_MAX_CLASS_NAME_LEN 16 +#define TS_MMI_MAX_PANEL_LEN 16 +#define TS_MMI_PILL_REGION_REQ_ARGS_NUM 3 +#define TS_MMI_ACTIVE_REGION_REQ_ARGS_NUM 4 +#define TS_MMI_FW_PARAM_PATH "/data/vendor/param/touch/" + +enum touch_event_mode { + TS_COORDINATE_ACTION_NONE = 0, + TS_COORDINATE_ACTION_PRESS, + TS_COORDINATE_ACTION_RELEASE, + TS_COORDINATE_ACTION_MOVE, + TS_COORDINATE_ACTION_FORCE_LIFT +}; + +enum TS_FW_UPGRADE_MODE { + FW_DEFAULT_MODE = 0, + FW_PARAM_MODE, + FW_SDCARD_MODE, +}; + +struct touch_event_data { + bool skip_report; + enum touch_event_mode type; /* TS_TOUCH, TS_RELEASE */ + int id; /* Finger id */ + int x, y, w, p; /* X, Y, Area, Pressure */ + int major, minor; /* Major, Minor */ +}; + +struct gesture_event_data { + union { + int evcode; + struct touch_event_data evdata; + }; +}; + +struct touch_clip_area { + unsigned xUL, yUL, xBR, yBR; + bool inversion; /* clip inside (when true) or outside otherwise */ +}; + +/** + * struct touchscreen_mmi_class_methods - export class methods to vendor + * + * @report_gesture: report gesture event + */ +struct ts_mmi_class_methods { + int (*report_gesture)(struct gesture_event_data *gev); + int (*get_gesture_type)(struct device *dev, unsigned char *gesture_type); + int (*report_palm)(bool value); + int (*get_class_fname)(struct device *dev , const char **fname); + int (*get_supplier)(struct device *dev , const char **sname); + int (*report_touch_event)(struct touch_event_data *tev, struct input_dev *input_dev); + int (*clip_touch_event)(struct device *dev, struct touch_event_data *tev, struct input_dev *input_dev); + int (*report_liquid_detection_status)(struct device *parent, int status); + struct kobject *kobj_notify; +}; + +enum ts_mmi_pm_mode { + TS_MMI_PM_DEEPSLEEP = 0, + TS_MMI_PM_GESTURE, + TS_MMI_PM_ACTIVE + }; + +enum ts_mmi_panel_event { + TS_MMI_EVENT_PRE_DISPLAY_OFF, + TS_MMI_EVENT_PRE_DISPLAY_ON, + TS_MMI_EVENT_DISPLAY_OFF, + TS_MMI_EVENT_DISPLAY_ON, + TS_MMI_EVENT_DISPLAY_ON_PREPARE, + TS_MMI_EVENT_UNKNOWN +}; + +enum ts_mmi_work { + TS_MMI_DO_RESUME, + TS_MMI_DO_PS, + TS_MMI_DO_REFRESH_RATE, + TS_MMI_DO_FPS, + TS_MMI_TASK_INIT, + TS_MMI_DO_LIQUID_DETECTION, +}; + +#define TS_MMI_RESET_SOFT 0 +#define TS_MMI_RESET_HARD 1 +#define TS_MMI_POWER_OFF 0 +#define TS_MMI_POWER_ON 1 +#define TS_MMI_PINCTL_OFF 0 +#define TS_MMI_PINCTL_ON 1 +#define TS_MMI_IRQ_OFF 0 +#define TS_MMI_IRQ_ON 1 +#define TS_MMI_UPDATE_BASELINE_OFF 0 +#define TS_MMI_UPDATE_BASELINE_ON 1 + +#define TS_MMI_DISABLE_SUPPRESSION_NONE 0 +#define TS_MMI_DISABLE_SUPPRESSION_ALL 1 +#define TS_MMI_DISABLE_SUPPRESSION_LEFT 2 +#define TS_MMI_DISABLE_SUPPRESSION_RIGHT 3 +#define TOUCHSCREEN_MMI_DEFAULT_GS_DISTANCE 0x1E +#define TOUCHSCREEN_MMI_DEFAULT_POISON_TIMEOUT_MS 800 +#define TOUCHSCREEN_MMI_DEFAULT_POISON_TRIGGER_DISTANCE 120 +#define TOUCHSCREEN_MMI_DEFAULT_POISON_DISTANCE 25 + +#define TS_MMI_GESTURE_ZERO 0x01 +#define TS_MMI_GESTURE_SINGLE 0x02 +#define TS_MMI_GESTURE_DOUBLE 0x04 +#define TS_MMI_GESTURE_PALM 0x08 + +/** + * struct touchscreen_mmi_methods - hold vendor provided functions + * + * @convert_data: convert internal touch data to common format + * @get_vendor: returns vendor name + * @get_product_id: returns product ID string + * @get_config_id: returns configuration ID string + * @get_irq_status: return current IRQ pin status + * @reset: performs touch IC reset (hard or soft) + * @irq: enable/disable IRQ handling + * @firmware_update: performs firmware update from provided file + * @firmware_erase: performs chip erasure + */ + struct ts_mmi_methods { + int (*convert_data)(struct device *dev, struct touch_event_data *data, int max); + /* GET methods */ + int (*get_vendor)(struct device *dev, void *cdata); + int (*get_productinfo)(struct device *dev, void *cdata); + int (*get_build_id)(struct device *dev, void *cdata); + int (*get_config_id)(struct device *dev, void *cdata); + int (*get_class_entry_name)(struct device *dev, void *cdata); + int (*get_bus_type)(struct device *dev, void *idata); + int (*get_irq_status)(struct device *dev, void *idata); + int (*get_drv_irq)(struct device *dev, void *idata); + int (*get_poweron)(struct device *dev, void *idata); + int (*get_flashprog)(struct device *dev, void *idata); + int (*get_suppression)(struct device *dev, void *idata); + int (*get_hold_grip)(struct device *dev, void *idata); + int (*get_flash_mode)(struct device *dev, void *idata); + int (*get_pill_region)(struct device *dev, void *uiadata); + int (*get_hold_distance)(struct device *dev, void *idata); + int (*get_gs_distance)(struct device *dev, void *idata); + int (*get_poison_timeout)(struct device *dev, void *idata); + int (*get_poison_distance)(struct device *dev, void *idata); + int (*get_poison_trigger_distance)(struct device *dev, void *idata); + int (*get_active_region)(struct device *dev, void *uiadata); + /* SET methods */ + int (*reset)(struct device *dev, int type); + int (*drv_irq)(struct device *dev, int state); + int (*power)(struct device *dev, int on); + int (*pinctrl)(struct device *dev, int on); + int (*refresh_rate)(struct device *dev, int freq); + int (*charger_mode)(struct device *dev, int mode); + int (*palm_set_enable)(struct device *dev, unsigned int enable); + int (*suppression)(struct device *dev, int state); + int (*hold_grip)(struct device *dev, int state); + int (*flash_mode)(struct device *dev, int state); + int (*pill_region)(struct device *dev, int *region_array); + int (*hold_distance)(struct device *dev, int dis); + int (*gs_distance)(struct device *dev, int dis); + int (*poison_timeout)(struct device *dev, int timeout); + int (*poison_distance)(struct device *dev, int dis); + int (*poison_trigger_distance)(struct device *dev, int dis); + int (*update_baseline)(struct device *dev, int enable); + int (*update_fod_mode)(struct device *dev, int enable); + int (*active_region)(struct device *dev, int *region_array); + int (*update_liquid_detect_mode)(struct device *dev, int enable); + /* Firmware */ + int (*firmware_update)(struct device *dev, char *fwname); + int (*firmware_erase)(struct device *dev); + /* vendor specific attribute group */ + int (*extend_attribute_group)(struct device *dev, struct attribute_group **group); + /* PM callback */ + int (*panel_state)(struct device *dev, enum ts_mmi_pm_mode from, enum ts_mmi_pm_mode to); + int (*wait_for_ready)(struct device *dev); + int (*pre_resume)(struct device *dev); + int (*post_resume)(struct device *dev); + int (*pre_suspend)(struct device *dev); + int (*post_suspend)(struct device *dev); + /* + * class exported methods + */ + struct ts_mmi_class_methods exports; +}; + +#define TO_CHARP(dp) ((char*)(dp)) +#define TO_INT(dp) (*(int*)(dp)) + +#define TOUCHSCREEN_MMI_BUS_TYPE_I2C 0 +#define TOUCHSCREEN_MMI_BUS_TYPE_SPI 1 + +struct ts_mmi_dev_pdata { + bool power_off_suspend; + bool fps_detection; + bool fod_detection; + bool usb_detection; + bool update_refresh_rate; + bool gestures_enabled; + bool cli_gestures_enabled; + bool palm_enabled; + bool fw_load_resume; + bool suppression_ctrl; + bool pill_region_ctrl; + bool hold_distance_ctrl; + bool gs_distance_ctrl; + bool hold_grip_ctrl; + bool poison_slot_ctrl; + bool active_region_ctrl; + bool support_liquid_detection; + bool clip_area_ctrl; + int max_x; + int max_y; + int fod_x; + int fod_y; + int ctrl_dsi; + int reset; + const char *class_entry_name; + const char *bound_display; +#ifdef CONFIG_BOARD_USES_DOUBLE_TAP_CTRL + int supported_gesture_type; +#endif +}; + +/** + * struct touchscreen_mmi_dev - hold touch general parameters and APIs + * + * @handle: Handle that identifies this sensors. + * @class_dev Touchscreen class device. + * @class_dev_minor Minor number of touchscreen class device. + * @node: List for the all the touch drivers. + * @bus_type: IC bus type. + * 0 I2C + * 1 SPI + * @mdata: Vendor specific methods + * @class_entry_name: + * + */ +struct ts_mmi_dev { + /* + * internally used + */ + struct device *dev; + struct device *class_dev; + dev_t class_dev_no; + int forcereflash; + unsigned char gesture_mode_type; + int panel_status; + struct ts_mmi_dev_pdata pdata; +#if defined(CONFIG_DRM_PANEL_NOTIFICATIONS) || defined (CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS) + struct drm_panel *active_panel; +#endif +#ifdef CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS + void *notifier_cookie; +#else + struct notifier_block panel_nb; +#endif + struct mutex extif_mutex; + struct mutex method_mutex; + struct pinctrl *pinctrl_node; + struct pinctrl_state *pinctrl_on_state; + struct pinctrl_state *pinctrl_off_state; + + atomic_t touch_stopped; + enum ts_mmi_pm_mode pm_mode; + + atomic_t resume_should_stop; + struct delayed_work work; + struct kfifo cmd_pipe; + + struct notifier_block freq_nb; + unsigned char refresh_rate; + + struct work_struct ps_notify_work; + struct notifier_block ps_notif; + bool ps_is_present; + + struct notifier_block fps_notif; + bool is_fps_registered; /* FPS notif registration might be delayed */ + bool fps_state; + bool delay_baseline_update; + + struct notifier_block lpd_notif; + bool is_lpd_registered; /* LPD notif registration might be delayed */ + bool lpd_state; + int liquid_status; + + /* + * sys entey variable + */ + char vendor[TS_MMI_MAX_VENDOR_LEN]; + char productinfo[TS_MMI_MAX_INFO_LEN]; + char build_id[TS_MMI_MAX_ID_LEN]; + char config_id[TS_MMI_MAX_ID_LEN]; + char class_entry_name[TS_MMI_MAX_CLASS_NAME_LEN]; + char panel_supplier[TS_MMI_MAX_PANEL_LEN]; + int bus_type; + int irq_status; + int drv_irq; + int poweron; + int flashprog; + int suppression; + unsigned int pill_region[TS_MMI_PILL_REGION_REQ_ARGS_NUM]; + unsigned int active_region[TS_MMI_ACTIVE_REGION_REQ_ARGS_NUM]; + int hold_distance; + int gs_distance; + int hold_grip; + int flash_mode; + int poison_timeout; + int poison_distance; + int poison_trigger_distance; + int charger_mode; + int reset; + int pinctrl; + int update_baseline; + struct attribute_group *extern_group; + struct list_head node; + struct touch_clip_area clip; + /* + * vendor provided + */ + struct ts_mmi_methods *mdata; +}; + +#define DEV_MMI (touch_cdev->class_dev) +#define DEV_TS (touch_cdev->dev) +#define MMI_DEV_TO_TS_DEV(cdev) (((struct ts_mmi_dev *)dev_get_drvdata(dev))->dev) +/* call this after hold method mutex */ +#define _TRY_TO_CALL(_method, ...) \ +do { \ + if (touch_cdev->mdata->_method) { \ + ret = touch_cdev->mdata->_method(DEV_TS, ##__VA_ARGS__); \ + } \ +} while (0) +#define TRY_TO_CALL(_method, ...) \ +do { \ + ret = ret; \ + mutex_lock(&touch_cdev->method_mutex); \ + _TRY_TO_CALL(_method, ##__VA_ARGS__); \ + mutex_unlock(&touch_cdev->method_mutex); \ +} while (0) +#define TRY_TO_GET(_method, ...) \ +do { \ + ret = ret; \ + if (touch_cdev->mdata->get_##_method) { \ + mutex_lock(&touch_cdev->method_mutex); \ + ret = touch_cdev->mdata->get_##_method(DEV_TS, ##__VA_ARGS__); \ + mutex_unlock(&touch_cdev->method_mutex); \ + } \ +} while (0) +#define is_touch_stopped (atomic_read(&touch_cdev->touch_stopped) == 1) +#define is_touch_active (touch_cdev->pm_mode == TS_MMI_PM_ACTIVE) + +extern int ts_mmi_notifiers_register(struct ts_mmi_dev *touch_cdev); +extern void ts_mmi_notifiers_unregister(struct ts_mmi_dev *touch_cdev); +extern int ts_mmi_panel_register(struct ts_mmi_dev *touch_cdev); +extern void ts_mmi_panel_unregister(struct ts_mmi_dev *touch_cdev); +extern int ts_mmi_dev_register(struct device *parent, + struct ts_mmi_methods *mdata); +extern void ts_mmi_dev_unregister(struct device *parent); +extern int ts_mmi_parse_dt(struct ts_mmi_dev *touch_cdev, struct device_node *of_node); +extern int ts_mmi_gesture_init(struct ts_mmi_dev *data); +extern int ts_mmi_gesture_remove(struct ts_mmi_dev *data); +extern int ts_mmi_cli_gesture_init(struct ts_mmi_dev *data); +extern int ts_mmi_cli_gesture_remove(struct ts_mmi_dev *data); +extern int ts_mmi_palm_init(struct ts_mmi_dev *data); +extern int ts_mmi_palm_remove(struct ts_mmi_dev *data); +#ifdef TS_MMI_TOUCH_EDGE_GESTURE +extern int ts_mmi_gesture_suspend(struct ts_mmi_dev *touch_cdev); +#endif +#if defined (CONFIG_DRM_PANEL_NOTIFICATIONS) || defined (CONFIG_DRM_PANEL_EVENT_NOTIFICATIONS) +int ts_mmi_check_drm_panel(struct ts_mmi_dev* touch_cdev, struct device_node *of_node); +#endif +extern bool ts_mmi_is_panel_match(const char *panel_node, char *touch_ic_name); + +/*sensor*/ +extern bool ts_mmi_is_sensor_enable(void); + +extern const char *mmi_bl_bootmode(void); +#endif /* __LINUX_TOUCHSCREEN_MMI_H_ */ diff --git a/include/linux/usb/adapter_class.h b/include/linux/usb/adapter_class.h new file mode 100644 index 000000000000..f866cca8de8a --- /dev/null +++ b/include/linux/usb/adapter_class.h @@ -0,0 +1,160 @@ +/* Copyright (c) 2020, 2021 The Linux Foundation. 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 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. + */ + +#ifndef LINUX_POWER_ADAPTER_CLASS_H +#define LINUX_POWER_ADAPTER_CLASS_H + +#include +#include +#include + + +#define ADAPTER_CAP_MAX_NR 10 + +struct adapter_power_cap { + uint8_t selected_cap_idx; + uint8_t nr; + uint8_t pdp; + uint8_t pwr_limit[ADAPTER_CAP_MAX_NR]; + int max_mv[ADAPTER_CAP_MAX_NR]; + int min_mv[ADAPTER_CAP_MAX_NR]; + int ma[ADAPTER_CAP_MAX_NR]; + int maxwatt[ADAPTER_CAP_MAX_NR]; + int minwatt[ADAPTER_CAP_MAX_NR]; + uint8_t type[ADAPTER_CAP_MAX_NR]; + int info[ADAPTER_CAP_MAX_NR]; +}; + +enum adapter_type { + MMI_PD_ADAPTER, +}; + +enum adapter_event { + MMI_PD_CONNECT_NONE, + MMI_PD_CONNECT_HARD_RESET, + MMI_PD_CONNECT_PE_READY_SNK, + MMI_PD_CONNECT_PE_READY_SNK_PD30, + MMI_PD_CONNECT_PE_READY_SNK_APDO, + MMI_PD_CONNECT_TYPEC_ONLY_SNK, + MMI_TYPEC_WD_STATUS, + MMI_TYPEC_HRESET_STATUS, +}; + +enum adapter_property { + TYPEC_RP_LEVEL, + PD_TYPE, +}; + +enum adapter_cap_type { + /*start pd30 apdo*/ + MMI_PD_APDO_START, + /*end pd30 apdo*/ + MMI_PD_APDO_END, + /*pd30 apdo*/ + MMI_PD_APDO, + /*pd20 fixed pdo*/ + MMI_PD_FIXED, + /*all pdo: pd20&pd30*/ + MMI_PD_ALL, + MMI_CAP_TYPE_UNKNOWN, +}; + +enum adapter_return_value { + MMI_ADAPTER_OK = 0, + MMI_ADAPTER_NOT_SUPPORT, + MMI_ADAPTER_TIMEOUT, + MMI_ADAPTER_REJECT, + MMI_ADAPTER_ERROR, + MMI_ADAPTER_ADJUST, +}; + + +struct adapter_status { + int temperature; + bool ocp; + bool otp; + bool ovp; +}; + +struct adapter_properties { + const char *alias_name; +}; + +struct adapter_device { + struct adapter_properties props; + const struct adapter_ops *ops; + struct mutex ops_lock; + struct device dev; + struct srcu_notifier_head evt_nh; + void *driver_data; + +}; + +struct adapter_ops { + int (*suspend)(struct adapter_device *dev, pm_message_t state); + int (*resume)(struct adapter_device *dev); + int (*get_property)(struct adapter_device *dev, + enum adapter_property pro); + int (*get_status)(struct adapter_device *dev, + struct adapter_status *sta); + int (*set_cap)(struct adapter_device *dev, enum adapter_cap_type type, + int pdo_idx, int mV, int mA); + int (*get_cap)(struct adapter_device *dev, enum adapter_cap_type type, + struct adapter_power_cap *cap); + int (*get_output)(struct adapter_device *dev, int *mV, int *mA); + +}; + +static inline void *adapter_dev_get_drvdata( + const struct adapter_device *adapter_dev) +{ + return adapter_dev->driver_data; +} + +static inline void adapter_dev_set_drvdata( + struct adapter_device *adapter_dev, void *data) +{ + adapter_dev->driver_data = data; +} + +extern struct adapter_device *adapter_device_register( + const char *name, + struct device *parent, void *devdata, const struct adapter_ops *ops, + const struct adapter_properties *props); +extern void adapter_device_unregister( + struct adapter_device *adapter_dev); +extern int register_adapter_device_notifier(struct adapter_device *adapter_dev, + struct notifier_block *nb); +extern int unregister_adapter_device_notifier( + struct adapter_device *adapter_dev, + struct notifier_block *nb); +extern struct adapter_device *get_adapter_by_name( + const char *name); + +#define to_adapter_device(obj) container_of(obj, struct adapter_device, dev) + +extern int adapter_dev_get_property(struct adapter_device *adapter_dev, + enum adapter_property sta); +extern int adapter_dev_get_status(struct adapter_device *adapter_dev, + struct adapter_status *sta); +extern int adapter_dev_get_output(struct adapter_device *adapter_dev, + int *mV, int *mA); +extern int adapter_dev_set_cap(struct adapter_device *adapter_dev, + enum adapter_cap_type type, int pdo_idx, + int mV, int mA); +extern int adapter_dev_get_cap(struct adapter_device *adapter_dev, + enum adapter_cap_type type, + struct adapter_power_cap *cap); + + +#endif /*LINUX_POWER_ADAPTER_CLASS_H*/ + diff --git a/include/linux/uwb/byteswap.h b/include/linux/uwb/byteswap.h new file mode 100644 index 000000000000..68527364d6b3 --- /dev/null +++ b/include/linux/uwb/byteswap.h @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 +/* + * Copyright 2022 Qorvo US, Inc. + * + */ + +#ifndef BYTESWAP_H +#define BYTESWAP_H +#define bswap_16 __builtin_bswap16 +#endif diff --git a/include/linux/uwb/fwupdater.h b/include/linux/uwb/fwupdater.h new file mode 100644 index 000000000000..ded5b5c74dbf --- /dev/null +++ b/include/linux/uwb/fwupdater.h @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 +/* + * Copyright 2023 Qorvo US, Inc. + * + */ + +#ifndef __FWUPDATER_H__ +#define __FWUPDATER_H__ + +#ifndef __KERNEL__ +#include +#include +#else +#include +#endif + +#include "qmrom.h" +#include "qm357xx_fwpkg.h" + +#ifndef CONFIG_NB_RETRIES +#define CONFIG_NB_RETRIES 10 +#endif + +// #define CONFIG_INJECT_ERROR 1 + +enum fw_pkg_error_e { + FW_PKG_SUCESS = 0, + FW_PKG_DOWNLOAD_ERROR, + FW_PKG_MAGIC_NUM_INVALID, + FW_PKG_VERSION_INVALID, + FW_PKG_ENCRYPTION_MODE_INVALID, + FW_PKG_IMG_HDR_MAGIC_NUM_INVALID, + FW_PKG_IMG_HDR_VERSION_INVALID, + FW_PKG_IMG_HDR_CERT_SIZE_INVALID, + FW_PKG_IMG_HDR_IMG_NUM_INVALID, + FW_PKG_PLD_CHK_MAGIC_NUM_INVALID, + FW_PKG_PLD_CHK_VERSION_INVALID, + FW_PKG_PLD_CHK_LENGTH_INVALID, + FW_PKG_HDR_CRYPTO_ERROR, + FW_PKG_HDR_CRYPTO_INVALID, + FW_PKG_HDR_PAYLOAD_SIZE_INVALID, + FW_PKG_IMG_HDR_SIZE_INVALID, + FW_PKG_IMG_HDR_CRYPTO_ERROR, + FW_PKG_IMG_HDR_NUMDESCS_INVALID, + FW_PKG_CERT_CHAIN_SIZE_INVALID, + FW_PKG_CERT_CHAIN_INVALID, + FW_PKG_CERT_INSTALLED_FW_VERSION_INVALID, + FW_PKG_CERT_KEY1_CHECK_INVALID, + FW_PKG_CERT_KEY2_CHECK_INVALID, + FW_PKG_CERT_CONTENT_CHECK_INVALID, + FW_PKG_CERT_CHAIN_CRYPTO_ERROR, + FW_PKG_CERT_CHAIN_IMG_RANGE_INVALID, + FW_PKG_CERT_CHAIN_WRITE_ERROR, + FW_PKG_IMG_CHUNK_CRYPTO_ERROR, + FW_PKG_IMG_CHUNK_WRITE_ERROR, + FW_PKG_IMG_FIXUP_FAILED +}; + +/*! Firmware Update Status fields */ +struct fw_updater_status_t { + uint32_t magic; + uint32_t status; + uint32_t suberror; + uint32_t spi_errors; + uint32_t crc_errors; + uint32_t rram_errors; + uint32_t crypto_errors; +} __attribute__((packed)); + +#define FWUPDATER_STATUS_MAGIC 0xCAFECAFE + +void run_fwupdater_unit_tests(void *spi_handle); +int run_fwupdater(struct qmrom_handle *handle, char *fwpkg_bin, size_t size); + +#endif /* __FWUPDATER_H__ */ diff --git a/include/linux/uwb/qm357xx_fwpkg.h b/include/linux/uwb/qm357xx_fwpkg.h new file mode 100644 index 000000000000..ea4948bd0350 --- /dev/null +++ b/include/linux/uwb/qm357xx_fwpkg.h @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 +/* + * Copyright 2021 Qorvo US, Inc. + * + */ + +#ifndef __QM357XX_FWPKG_H__ +#define __QM357XX_FWPKG_H__ + +/*! Pkg version word conversion macro */ +#define PKG_VER_TO_U32(maj, min) (((maj) << 16) | ((min) << 24)) + +/*! Pkg magic word conversion macro */ +#define MAGIC_STR_TO_U32(x) \ + ((uint32_t)((x[3]) | (x[2] << 8) | (x[1] << 16) | (x[0] << 24))) + +// Package size defines +#define CRYPTO_FIRMWARE_PACK_ENC_DATA_SIZE 16 +#define CRYPTO_FIRMWARE_PACK_FW_VERSION_SIZE 32 +#define CRYPTO_FIRMWARE_PACK_TAG_SIZE 16 + +// Firmware image size define +#define CRYPTO_FW_PKG_IMG_HDR_IMG_NUM_MAX 8 + +// Field values for macro firmware package +#define CRYPTO_MACRO_FIRMWARE_PACK_MAGIC_VALUE MAGIC_STR_TO_U32("FWMP") + +// Field values for firmware package +#define CRYPTO_FIRMWARE_PACK_MAGIC_VALUE MAGIC_STR_TO_U32("CFWP") +#define CRYPTO_FIRMWARE_PACK_VERSION PKG_VER_TO_U32(1, 0) +#define CRYPTO_FIRMWARE_PACK_ENC_MODE_NOT_ENCRYPTED 0x0 +#define CRYPTO_FIRMWARE_PACK_ENC_ALGO_128BIT_AES_CTR 0x00 +#define CRYPTO_FIRMWARE_PACK_ENC_KEY_L2_SIZE 64 + +// Field values for firmware image +#define CRYPTO_FIRMWARE_IMAGE_MAGIC_VALUE MAGIC_STR_TO_U32("IMGS") +#define CRYPTO_FIRMWARE_IMAGE_VERSION PKG_VER_TO_U32(1, 0) + +// Field values for firmware chunks +#define CRYPTO_FIRMWARE_CHUNK_MAGIC_VALUE MAGIC_STR_TO_U32("CFWC") +#define CRYPTO_FIRMWARE_CHUNK_VERSION PKG_VER_TO_U32(1, 0) +#define CRYPTO_FIRMWARE_CHUNK_MIN_SIZE 16 + +// fw certificate sizes +#define CRYPTO_IMAGES_CERT_KEY_SIZE 840 +#define CRYPTO_IMAGES_CERT_CONTENT_SIZE 868 +#define CRYPTO_IMAGES_CERT_PKG_SIZE \ + (2 * CRYPTO_IMAGES_CERT_KEY_SIZE + CRYPTO_IMAGES_CERT_CONTENT_SIZE) +#define CRYPTO_IMAGES_NB_CERTS 3 +#define CRYPTO_IMAGES_MAX_NB_IMAGES 8 + +#define CRYPTO_FIRMWARE_IMAGE_HDR_TOTAL_SIZE \ + (sizeof(struct fw_pkg_img_hdr_t) + \ + CRYPTO_IMAGES_NB_CERTS * sizeof(struct fw_img_desc_t)) + +/*! Encryption mode enum. */ +enum fw_pkg_enc_mode_e { + CRYPTO_FIRMWARE_PACK_ENC_MODE_NONE = 0x0, + CRYPTO_FIRMWARE_PACK_ENC_MODE_ENCRYPTED = 0x1 +}; + +/*! Package type enum. */ +enum fw_pkg_package_type_e { + CRYPTO_FIRMWARE_PACK_PACKAGE_TYPE_ICV = 0x01, + CRYPTO_FIRMWARE_PACK_PACKAGE_TYPE_OEM = 0x02 +}; + +/*! IV type enum. */ +enum fw_pkg_iv_type_e { + CRYPTO_FIRMWARE_PACK_IV_TYPE_HDR = 0x00, + CRYPTO_FIRMWARE_PACK_IV_TYPE_IMG = 0x01 +}; + +/*! Firmware Package Header fields */ +struct fw_pkg_hdr_t { + uint32_t magic; /**< Magic number. */ + uint32_t version; /**< Version. */ + uint8_t package_type; /**< Package type. */ + uint8_t enc_mode; /**< Encryption mode. */ + uint8_t enc_algo; /**< Encryption algorithm. */ + uint8_t reserved; /**< Reserved for alignment. */ + uint8_t enc_data + [CRYPTO_FIRMWARE_PACK_ENC_DATA_SIZE]; /**< Encryption data. */ + uint8_t enc_key_l2 + [CRYPTO_FIRMWARE_PACK_ENC_KEY_L2_SIZE]; /**< Code encryption L2 key data. */ + uint8_t fw_version + [CRYPTO_FIRMWARE_PACK_FW_VERSION_SIZE]; /**< Firwmare version included in the package. */ + uint32_t payload_len; /**< Payload length. */ + uint8_t tag[CRYPTO_FIRMWARE_PACK_TAG_SIZE]; /**< AES-CMAC Tag. */ +} __attribute__((packed)); + +/*! Firmware Image Metadata fields */ +struct fw_img_desc_t { + uint32_t offset; /**< Offset. */ + uint32_t length; /**< Length. */ +} __attribute__((packed)); + +/*! Firmware Image Header fields */ +struct fw_pkg_img_hdr_t { + uint32_t magic; /**< Magic number. */ + uint32_t version; /**< Version number. */ + uint32_t cert_chain_offset; /**< Certificate chain offset. */ + uint16_t cert_chain_length; /**< Certificate chain length. */ + uint8_t num_descs; /**< Number of images descriptors. */ + uint8_t reserved; /**< Reserved data. */ + struct fw_img_desc_t descs + [CRYPTO_FW_PKG_IMG_HDR_IMG_NUM_MAX]; /**< Firmware image metadata. */ +} __attribute__((packed)); + +/*! Firmware Chunk fields */ +struct fw_pkg_payload_chunk_t { + uint32_t magic; /**< Magic number. */ + uint32_t version; /**< Version number. */ + uint32_t length; /**< Length. */ + uint8_t payload[]; /**< Payload data pointer. */ +} __attribute__((packed)); + +/*! Firmware Macro Package Header fields */ +struct fw_macro_pkg_hdr_t { + uint32_t magic; /**< Magic number. */ + uint32_t version; /**< Version. */ + uint8_t nb_descriptors; + uint8_t reserved[3]; + struct fw_img_desc_t img_desc[]; +} __attribute__((packed)); + +#define MACRO_PKG_HASH_SIZE (32) +#define COMPUTE_FW_MACRO_PKG_HDR_SIZE(ndescs) \ + (sizeof(struct fw_macro_pkg_hdr_t) + \ + ndescs * sizeof(struct fw_img_desc_t) + MACRO_PKG_HASH_SIZE) + +#endif /* __QM357XX_FWPKG_H__ */ diff --git a/include/linux/uwb/qm357xx_rom.h b/include/linux/uwb/qm357xx_rom.h new file mode 100644 index 000000000000..163cc457feb2 --- /dev/null +++ b/include/linux/uwb/qm357xx_rom.h @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 +/* + * Copyright 2021 Qorvo US, Inc. + * + */ + +#ifndef __QM357XX_ROM_H__ +#define __QM357XX_ROM_H__ + +#define PEG_ERR_TIMEOUT PEG_ERR_BASE - 1 +#define PEG_ERR_ROM_NOT_READY PEG_ERR_BASE - 2 +#define PEG_ERR_SEND_CERT_WRITE PEG_ERR_BASE - 3 +#define PEG_ERR_WRONG_REVISION PEG_ERR_BASE - 4 +#define PEG_ERR_FIRST_KEY_CERT_OR_FW_VER PEG_ERR_BASE - 5 + +#define HBK_LOC 12 +typedef enum { + HBK_2E_ICV = 0, + HBK_2E_OEM = 1, + HBK_1E_ICV_OEM = 2, +} hbk_t; + +#define ROM_VERSION_A0 0x01a0 +#define ROM_VERSION_B0 0xb000 + +#define QM357XX_ROM_SOC_ID_LEN 0x20 +#define QM357XX_ROM_UUID_LEN 0x10 + +/* Life cycle state definitions. */ + +/*! Defines the CM life-cycle state value. */ +#define CC_BSV_CHIP_MANUFACTURE_LCS 0x0 +/*! Defines the DM life-cycle state value. */ +#define CC_BSV_DEVICE_MANUFACTURE_LCS 0x1 +/*! Defines the Secure life-cycle state value. */ +#define CC_BSV_SECURE_LCS 0x5 +/*! Defines the RMA life-cycle state value. */ +#define CC_BSV_RMA_LCS 0x7 + +struct unstitched_firmware { + struct firmware *fw_img; + struct firmware *fw_crt; + struct firmware *key1_crt; + struct firmware *key2_crt; +}; + +/* Those functions allow the libqmrom to call + * revision specific functions + */ +typedef int (*flash_fw_fn)(struct qmrom_handle *handle, + const struct firmware *fw); +typedef int (*flash_unstitched_fw_fn)(struct qmrom_handle *handle, + const struct unstitched_firmware *fw); +typedef int (*flash_debug_cert_fn)(struct qmrom_handle *handle, + struct firmware *dbg_cert); +typedef int (*erase_debug_cert_fn)(struct qmrom_handle *handle); + +struct qm357xx_rom_code_ops { + flash_fw_fn flash_fw; + flash_unstitched_fw_fn flash_unstitched_fw; + flash_debug_cert_fn flash_debug_cert; + erase_debug_cert_fn erase_debug_cert; +}; + +struct qm357xx_soc_infos { + uint8_t soc_id[QM357XX_ROM_SOC_ID_LEN]; + uint8_t uuid[QM357XX_ROM_UUID_LEN]; + uint32_t lcs_state; +}; + +int qm357xx_rom_unstitch_fw(const struct firmware *fw, + struct unstitched_firmware *unstitched_fw, + enum chip_revision_e revision); +int qm357xx_rom_fw_macro_pkg_get_fw_idx(const struct firmware *fw, int idx, + uint32_t *fw_size, char **fw_fata); +int qm357xx_rom_unpack_fw_macro_pkg(const struct firmware *fw, + struct unstitched_firmware *all_fws); +int qm357xx_rom_unpack_fw_pkg(const struct firmware *fw_pkg, + struct unstitched_firmware *all_fws); +int qm357xx_rom_flash_dbg_cert(struct qmrom_handle *handle, + struct firmware *dbg_cert); +int qm357xx_rom_erase_dbg_cert(struct qmrom_handle *handle); +int qm357xx_rom_flash_fw(struct qmrom_handle *handle, + const struct firmware *fw); +int qm357xx_rom_flash_unstitched_fw(struct qmrom_handle *handle, + const struct unstitched_firmware *fw); + +int qm357xx_rom_write_cmd(struct qmrom_handle *handle, uint8_t cmd); +int qm357xx_rom_write_cmd32(struct qmrom_handle *handle, uint32_t cmd); +int qm357xx_rom_write_size_cmd(struct qmrom_handle *handle, uint8_t cmd, + uint16_t data_size, const char *data); +int qm357xx_rom_write_size_cmd32(struct qmrom_handle *handle, uint32_t cmd, + uint16_t data_size, const char *data); + +#endif /* __QM357XX_ROM_H__ */ diff --git a/include/linux/uwb/qm358xx_firmware_pack_handling.h b/include/linux/uwb/qm358xx_firmware_pack_handling.h new file mode 100644 index 000000000000..f16a3a42e8e1 --- /dev/null +++ b/include/linux/uwb/qm358xx_firmware_pack_handling.h @@ -0,0 +1,174 @@ +/* +* Copyright 2021 Qorvo US, Inc. +* +* SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 +* +* This file is provided under the Apache License 2.0, or the +* GNU General Public License v2.0. +* +*/ + +#ifndef _QM358XX_FIRMWARE_PACK_HANDLING_H_ +#define _QM358XX_FIRMWARE_PACK_HANDLING_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/*! +@addtogroup secureboot_firmware_pack_handling +@{ +*/ + +/*! Cert magic word conversion macro */ +#define MAGIC_STR_TO_U32(x) ((x[3]) | (x[2] << 8) | (x[1] << 16) | (x[0] << 24)) + +/*! Cert version word conversion macro */ +#define CERT_VER_TO_U32(maj, min) (((maj) << 16) | ((min) << 24)) + +// Field values for firmware package +#define CRYPTO_FIRMWARE_PACK_MAGIC_VALUE MAGIC_STR_TO_U32("CFWP") + +// Package size defines +#define CRYPTO_FIRMWARE_PACK_ENC_KEY_L2_ENC_SIZE 64u +#define CRYPTO_FIRMWARE_PACK_ENC_DATA_SIZE 16u +#define CRYPTO_FIRMWARE_PACK_FW_VERSION_SIZE 32u +#define CRYPTO_FIRMWARE_PACK_TAG_SIZE 16u + +// Firmware image size define +#define CRYPTO_FW_PKG_IMG_HDR_IMG_NUM_MAX 8u + +#define CRYPTO_FIRMWARE_PACK_HEADER_SIZE sizeof(fw_pkg_hdr_t) +#define CRYPTO_FIRMWARE_IMAGE_CERT_SIZE sizeof(fw_pkg_img_hdr_t) + +// Field values for firmware chunks +#define CRYPTO_FIRMWARE_CHUNK_MAGIC_VALUE MAGIC_STR_TO_U32("CFWC") +#define CRYPTO_FIRMWARE_CHUNK_VERSION CERT_VER_TO_U32(1, 0) +#define CRYPTO_FIRMWARE_CHUNK_MIN_SIZE 16u +#define CRYPTO_FIRMWARE_CHUNK_HDR_SIZE sizeof(fw_pkg_payload_chunk_hdr_t) + +/*! Firmware Package Header fields */ +typedef struct { + uint32_t magic; /**< Magic number. */ + uint32_t version; /**< Version. */ + uint8_t package_type; /**< Package type. */ + uint8_t enc_mode; /**< Encryption mode. */ + uint8_t enc_algo; /**< Encryption algorithm. */ + union { + uint8_t enc_key_l2_cfg; /**< Code encryption L2 key config. */ + struct { + uint8_t use : 1; /**< Use Kce_l2 for decryption. */ + uint8_t lock : 1; /**< Lock using Kce_l2 for decryption. */ + uint8_t : 6; + } enc_key_l2_cfg_b; + }; + uint8_t enc_data + [CRYPTO_FIRMWARE_PACK_ENC_DATA_SIZE]; /**< Encryption data. */ + uint8_t enc_key_l2 + [CRYPTO_FIRMWARE_PACK_ENC_KEY_L2_ENC_SIZE]; /**< Code encryption L2 key data. */ + uint8_t fw_version + [CRYPTO_FIRMWARE_PACK_FW_VERSION_SIZE]; /**< Firwmare version included in the package. */ + uint32_t payload_len; /**< Payload length. */ + uint8_t tag[CRYPTO_FIRMWARE_PACK_TAG_SIZE]; /**< AES-CMAC Tag. */ +} fw_pkg_hdr_t; + +/*! Firmware Image Metadata fields */ +typedef struct { + uint32_t offset; /**< Offset. */ + uint32_t length; /**< Length. */ +} fw_image_metadata_t; + +/*! Firmware Image Header fields */ +typedef struct { + uint32_t magic; /**< Magic number. */ + uint32_t version; /**< Version number. */ + uint32_t cert_chain_offset; /**< Certificate chain offset. */ + uint16_t cert_chain_length; /**< Certificate chain length. */ + uint8_t img_num; /**< Number of images. */ + uint8_t reserved; /**< Reserved data. */ + fw_image_metadata_t metadata + [CRYPTO_FW_PKG_IMG_HDR_IMG_NUM_MAX]; /**< Firmware image metadata. */ +} fw_pkg_img_hdr_t; + +/*! Firmware Chunk fields */ +typedef struct { + uint32_t magic; /**< Magic number. */ + uint32_t version; /**< Version number. */ + uint32_t length; /**< Length. */ +} fw_pkg_payload_chunk_hdr_t; + +#define CRYPTO_IMAGES_CERT_PUBLIC_KEY_SIZE 384 +#define CRYPTO_KEY_HASH_TRUNC_SIZE_BYTES 16u +#define CRYPTO_SHA256_SIZE_BYTES 32u +#define CRYPTO_IMAGES_CERT_MIN_IMAGE_NUMBER 1u +#define CRYPTO_IMAGES_CERT_PKG_SIZE sizeof(cert_chain_struct) + +/*! Certificate or file header struct */ +typedef struct { + uint32_t magic; /**< Magic value. */ + uint32_t version; /**< Certificate version. */ + uint32_t size; /**< Certificate size. */ +} cert_common_header_struct; + +/*! Key certificate fields */ +typedef struct { + cert_common_header_struct + header; /**< Pointer to key certificate header data. */ + uint8_t rsa_public_key + [CRYPTO_IMAGES_CERT_PUBLIC_KEY_SIZE]; /**< RSA public key data. */ + uint32_t context_hash_id; /**< Context and hash ID combined in one uint32. */ + uint32_t customer_id; /**< Customer ID. */ + uint32_t product_id; /**< Product ID. */ + uint8_t sec_version; /**< Security version number. */ + uint8_t reserved[3]; /**< Reserved data. */ + uint32_t arb_version; /**< Anti-rollback version number. */ + uint8_t hash[CRYPTO_KEY_HASH_TRUNC_SIZE_BYTES]; /**< Content cert public key hash. */ + uint8_t rsa_signature + [CRYPTO_IMAGES_CERT_PUBLIC_KEY_SIZE]; /**< RSA signature data. */ +} cert_key_struct; + +/*! Content of metadata fields */ +typedef struct { + uint8_t img_hash[CRYPTO_SHA256_SIZE_BYTES]; /**< Image hash result data. */ + uint32_t flash_addr; /**< Flash (RRAM) address. */ + uint32_t max_len; /**< Maximum length. */ + uint32_t len; /**< Length. */ + uint32_t load_verify_scheme; /**< Schema to load and verify firmware image. */ + uint32_t load_addr; /**< SRAM load address. */ +} cert_content_img_metadata_t; + +/*! Content certificate fields */ +typedef struct { + cert_common_header_struct + header; /**< Pointer to content certificate header data. */ + uint8_t rsa_public_key + [CRYPTO_IMAGES_CERT_PUBLIC_KEY_SIZE]; /**< RSA public key data. */ + uint32_t context; /**< Context. */ + uint32_t customer_id; /**< Customer ID. */ + uint32_t product_id; /**< Product ID. */ + uint8_t sec_version; /**< Security version number. */ + uint8_t reserved[3]; /**< Reserved data. */ + uint32_t arb_version; /**< Anti-rollback version number. */ + uint32_t img_num; /**< Number of images. */ + cert_content_img_metadata_t img_metadata + [CRYPTO_IMAGES_CERT_MIN_IMAGE_NUMBER]; /**< Image metadata. */ +} cert_content_struct; + +/*! Certificate chain fields */ +typedef struct { + cert_key_struct cert_key; /**< Pointer to key certificate data. */ + cert_content_struct + cert_content; /**< Pointer to content certificate data. */ +} cert_chain_struct; + +/** +@} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* _QM358XX_FIRMWARE_PACK_HANDLING_H_ */ diff --git a/include/linux/uwb/qm358xx_rom.h b/include/linux/uwb/qm358xx_rom.h new file mode 100644 index 000000000000..c76b57618599 --- /dev/null +++ b/include/linux/uwb/qm358xx_rom.h @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 +/* + * Copyright 2021 Qorvo US, Inc. + * + */ + +#ifndef __QM358XX_ROM_H__ +#define __QM358XX_ROM_H__ + +#define QM358XX_ROM_UUID_LEN 0x10 + +/* Life cycle state definitions. */ +#define LCS_TEST (0x00) /* Previously: CC_BSV_CHIP_MANUFACTURE_LCS */ +#define LCS_OEM (0x01) /* Previously: CC_BSV_DEVICE_MANUFACTURE_LCS */ +#define LCS_USER (0x02) /* Previously: CC_BSV_SECURE_LCS */ +#define LCS_RMA (0x03) /* Previously: CC_BSV_RMA_LCS */ + +typedef int (*gen_secrets_fn)(struct qmrom_handle *handle); +typedef int (*load_asset_pkg_fn)(struct qmrom_handle *handle, + const struct firmware *asset_pkg); +typedef int (*load_fw_pkg_fn)(struct qmrom_handle *handle, + const struct firmware *fw_pkg); +typedef int (*load_secure_dbg_pkg_fn)(struct qmrom_handle *handle, + struct firmware *dbg_pkg); +typedef int (*erase_secure_dbg_pkg_fn)(struct qmrom_handle *handle); +typedef int (*run_test_mode_fn)(struct qmrom_handle *handle); +typedef int (*load_sram_fw_fn)(struct qmrom_handle *handle, + const struct firmware *sram_fw); + +struct qm358xx_rom_code_ops { + gen_secrets_fn gen_secrets; + load_asset_pkg_fn load_asset_pkg; + load_fw_pkg_fn load_fw_pkg; + load_secure_dbg_pkg_fn load_secure_dbg_pkg; + erase_secure_dbg_pkg_fn erase_secure_dbg_pkg; + run_test_mode_fn run_test_mode; + load_sram_fw_fn load_sram_fw; +}; + +struct qm358xx_soc_infos { + uint8_t uuid[QM358XX_ROM_UUID_LEN]; + uint32_t customer_id; + uint32_t product_id; + uint32_t arb_ver_icv; + uint32_t arb_ver_oem; + uint8_t lcs_state; + uint8_t working_model; + uint8_t business_ctx; + uint8_t secure_debug; + uint8_t secure_boot_oem; + uint8_t fw_encryption_oem; + uint8_t lock_debug; + uint8_t sec_ver_icv; + uint8_t sec_ver_oem; + uint8_t enc_l2_lock; +}; + +int qm358xx_rom_gen_secrets(struct qmrom_handle *handle); +int qm358xx_rom_load_asset_pkg(struct qmrom_handle *handle, + struct firmware *asset_pkg); +int qm358xx_rom_load_fw_pkg(struct qmrom_handle *handle, + struct firmware *fw_pkg); +int qm358xx_rom_load_secure_dbg_pkg(struct qmrom_handle *handle, + struct firmware *dbg_pkg); +int qm358xx_rom_erase_secure_dbg_pkg(struct qmrom_handle *handle); +int qm358xx_rom_run_test_mode(struct qmrom_handle *handle); +int qm358xx_rom_load_sram_fw(struct qmrom_handle *handle, + struct firmware *sram_fw); + +#endif /* __QM358XX_ROM_H__ */ diff --git a/include/linux/uwb/qmrom.h b/include/linux/uwb/qmrom.h new file mode 100644 index 000000000000..6ef78d8a1de5 --- /dev/null +++ b/include/linux/uwb/qmrom.h @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 +/* + * Copyright 2021 Qorvo US, Inc. + * + */ + +#ifndef __QMROM_H__ +#define __QMROM_H__ + +#ifndef __KERNEL__ +#include +#include +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#define bswap_16 be16_to_cpu +#define PRIu32 "u" +#endif + +#include "qmrom_error.h" + +#undef CHECK_STCS + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + +enum chip_revision_e { + CHIP_REVISION_A0 = 0xA0, + CHIP_REVISION_B0 = 0xB0, + CHIP_REVISION_C0 = 0xC0, + CHIP_REVISION_C2 = 0xC2, + CHIP_REVISION_UNKNOWN = 0xFF +}; + +enum device_generation_e { + DEVICE_GEN_QM357XX, + DEVICE_GEN_QM358XX, + DEVICE_GEN_UNKNOWN = 0xFF +}; + +struct qmrom_handle; + +#include "qm357xx_rom.h" +#include "qm358xx_rom.h" + +#define SSTC2UINT32(handle, offset) \ + ({ \ + uint32_t tmp = 0xbeefdeed; \ + if ((handle)->sstc->len >= (offset) + sizeof(tmp)) \ + memcpy(&tmp, &(handle)->sstc->payload[(offset)], \ + sizeof(tmp)); \ + tmp; \ + }) + +#define SSTC2UINT16(handle, offset) \ + ({ \ + uint16_t tmp = 0xbeed; \ + if ((handle)->sstc->len >= (offset) + sizeof(tmp)) \ + memcpy(&tmp, &(handle)->sstc->payload[(offset)], \ + sizeof(tmp)); \ + tmp; \ + }) + +/* Those functions allow the libqmrom to call + * device specific functions + */ +typedef int (*reset_device_fn)(void *handle); + +struct device_ops { + reset_device_fn reset; +}; + +struct qmrom_handle { + void *spi_handle; + void *reset_handle; + void *ss_rdy_handle; + void *ss_irq_handle; + int comms_retries; + enum device_generation_e dev_gen; + enum chip_revision_e chip_rev; + uint16_t device_version; + struct device_ops dev_ops; + int spi_speed; + union { + struct qm357xx_rom_code_ops qm357xx_rom_ops; + struct qm358xx_rom_code_ops qm358xx_rom_ops; + }; + struct stc *hstc; + struct stc *sstc; + union { + struct qm357xx_soc_infos qm357xx_soc_info; + struct qm358xx_soc_infos qm358xx_soc_info; + }; + bool is_be; + bool skip_check_fw_boot; +}; + +struct qmrom_handle *qmrom_init(void *spi_handle, void *reset_handle, + void *ss_rdy_handle, void *ss_irq_handle, + int spi_speed, int comms_retries, + reset_device_fn reset, + enum device_generation_e dev_gen_hint); +void qmrom_deinit(struct qmrom_handle *handle); +int qmrom_reboot_bootloader(struct qmrom_handle *handle); + +int qmrom_pre_read(struct qmrom_handle *handle); +int qmrom_read(struct qmrom_handle *handle); + +#ifdef CHECK_STCS +void check_stcs(const char *func, int line, struct qmrom_handle *h); +#else +#define check_stcs(f, l, h) +#endif +#endif /* __QMROM_H__ */ diff --git a/include/linux/uwb/qmrom_error.h b/include/linux/uwb/qmrom_error.h new file mode 100644 index 000000000000..b893bdfdf867 --- /dev/null +++ b/include/linux/uwb/qmrom_error.h @@ -0,0 +1,21 @@ +/* + * Copyright 2021 Qorvo US, Inc. + * + * SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 + * + * This file is provided under the Apache License 2.0, or the + * GNU General Public License v2.0. + * + */ +#ifndef __QMROM_ERROR_H__ +#define __QMROM_ERROR_H__ + +#define SPI_ERR_BASE -8000 +#define PEG_ERR_BASE -9000 +#define SPI_PROTO_ERR_BASE -10000 + +#define IS_PTR_ERROR(ptr) (((intptr_t)ptr) <= 0) +#define PTR2ERROR(ptr) ((int)((intptr_t)ptr)) +#define ERROR2PTR(error) ((void *)((intptr_t)error)) + +#endif /* __QMROM_ERROR_H__ */ \ No newline at end of file diff --git a/include/linux/uwb/qmrom_log.h b/include/linux/uwb/qmrom_log.h new file mode 100644 index 000000000000..5bd4400534b7 --- /dev/null +++ b/include/linux/uwb/qmrom_log.h @@ -0,0 +1,94 @@ +/* + * Copyright 2021 Qorvo US, Inc. + * + * SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 + * + * This file is provided under the Apache License 2.0, or the + * GNU General Public License v2.0. + * + */ +#ifndef __QMROM_LOGGER_H__ +#define __QMROM_LOGGER_H__ + +enum log_level_e { + LOG_QUIET = 0, + LOG_ERR, + LOG_WARN, + LOG_INFO, + LOG_DBG, + LOG_LVL_MAX = LOG_DBG, +}; + +extern enum log_level_e __log_level__; + +void set_log_level(enum log_level_e lvl); + +static inline int is_debug_mode(void) +{ + return __log_level__ >= LOG_DBG; +} + +static inline int is_log_level_allowed(enum log_level_e lvl) +{ + return (__log_level__ >= lvl); +} + +void hexdump(enum log_level_e lvl, void *address, unsigned short length); +void hexrawdump(enum log_level_e lvl, void *address, unsigned short length); + +#ifndef __KERNEL__ +#include +#define LOG_ERR(...) \ + do { \ + if (__log_level__ > LOG_QUIET) \ + fprintf(stderr, __VA_ARGS__); \ + } while (0) +#define LOG_WARN(...) \ + do { \ + if (__log_level__ > LOG_ERR) \ + fprintf(stderr, __VA_ARGS__); \ + } while (0) +#define LOG_INFO(...) \ + do { \ + if (__log_level__ > LOG_WARN) \ + fprintf(stdout, __VA_ARGS__); \ + } while (0) +#define LOG_DBG(...) \ + do { \ + if (__log_level__ > LOG_INFO) \ + fprintf(stdout, __VA_ARGS__); \ + } while (0) +#else +#include + +extern struct device *__qmrom_log_dev__; + +void qmrom_set_log_device(struct device *dev, enum log_level_e lvl); + +#define LOG_ERR(...) \ + do { \ + if (__qmrom_log_dev__) \ + if (__log_level__ > LOG_QUIET) \ + dev_err(__qmrom_log_dev__, __VA_ARGS__); \ + } while (0) +#define LOG_WARN(...) \ + do { \ + if (__qmrom_log_dev__) \ + if (__log_level__ > LOG_ERR) \ + dev_warn(__qmrom_log_dev__, __VA_ARGS__); \ + } while (0) +#define LOG_INFO(...) \ + do { \ + if (__qmrom_log_dev__) \ + if (__log_level__ > LOG_WARN) \ + dev_info(__qmrom_log_dev__, __VA_ARGS__); \ + } while (0) +#define LOG_DBG(...) \ + do { \ + if (__qmrom_log_dev__) \ + if (__log_level__ > LOG_INFO) \ + dev_dbg(__qmrom_log_dev__, __VA_ARGS__); \ + } while (0) +#endif + +#endif /* __QMROM_LOGGER_H__ */ diff --git a/include/linux/uwb/qmrom_spi.h b/include/linux/uwb/qmrom_spi.h new file mode 100644 index 000000000000..e23d302e78b0 --- /dev/null +++ b/include/linux/uwb/qmrom_spi.h @@ -0,0 +1,63 @@ +/* + * Copyright 2021 Qorvo US, Inc. + * + * SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 + * + * This file is provided under the Apache License 2.0, or the + * GNU General Public License v2.0. + * + */ +#ifndef __QMROM_SPI_H__ +#define __QMROM_SPI_H__ + +#include +#include "qmrom_error.h" +#include "qmrom.h" + +#ifndef __KERNEL__ +struct firmware { + size_t size; + const uint8_t *data; +}; +#else +#include +#endif + +#define DEFAULT_SPI_LATENCY_MS 2 + +#define SPI_ERR_NOCHAN SPI_ERR_BASE - 1 +#define SPI_ERR_INFNOTFOUND SPI_ERR_BASE - 2 +#define SPI_ERR_NOMEM SPI_ERR_BASE - 3 +#define SPI_ERR_READ_INCOMPLETE SPI_ERR_BASE - 4 +#define SPI_ERR_GPIO_WRITE_CMD_INCOMPLETE SPI_ERR_BASE - 5 +#define SPI_ERR_GPIO_READ_CMD_INCOMPLETE SPI_ERR_BASE - 6 +#define SPI_ERR_READY_LINE_TIMEOUT SPI_ERR_BASE - 7 +#define SPI_ERR_WRITE_INCOMPLETE SPI_ERR_BASE - 8 +#define SPI_ERR_RW_INCOMPLETE SPI_ERR_BASE - 9 +#define SPI_ERR_INVALID_STC_LEN SPI_ERR_BASE - 10 +#define SPI_ERR_WAIT_READY_TIMEOUT SPI_ERR_BASE - 11 + +/*Make sure that the error ranges don't overlap */ +#define SPI_ERR_LIB_BASE (SPI_ERR_BASE - 500) +#define SPI_ERR_LIB(rc) (SPI_ERR_LIB_BASE - rc) + +void *qmrom_spi_init(int spi_interface_index); +void qmrom_spi_uninit(void *handle); +int qmrom_spi_read(void *handle, char *buffer, size_t size); +int qmrom_spi_write(void *handle, const char *buffer, size_t size); +int qmrom_spi_transfer(void *handle, char *rbuf, const char *wbuf, size_t size); +int qmrom_spi_set_cs_level(void *handle, int level); +int qmrom_spi_reset_device(void *reset_handle); +const struct firmware *qmrom_spi_get_firmware(void *handle, + struct qmrom_handle *qmrom_h, + bool *is_fw_updater, + bool use_prod_fw); +const struct firmware *qmrom_spi_get_firmware_package(void *handle, + int lcs_state); +void qmrom_spi_release_firmware(const struct firmware *fw); +int qmrom_spi_wait_for_ready_line(void *handle, unsigned int timeout_ms); +int qmrom_spi_read_irq_line(void *handle); +void qmrom_spi_set_freq(unsigned int freq); +unsigned int qmrom_spi_get_freq(void); + +#endif /* __QMROM_SPI_H__ */ diff --git a/include/linux/uwb/qmrom_utils.h b/include/linux/uwb/qmrom_utils.h new file mode 100644 index 000000000000..75331944372c --- /dev/null +++ b/include/linux/uwb/qmrom_utils.h @@ -0,0 +1,56 @@ +/* + * Copyright 2021 Qorvo US, Inc. + * + * SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 + * + * This file is provided under the Apache License 2.0, or the + * GNU General Public License v2.0. + * + */ +#ifndef __QMROM_UTILS_H__ +#define __QMROM_UTILS_H__ + +#ifndef __KERNEL__ +#include +#include + +#ifndef __linux__ +extern void usleep(unsigned int us); +#else +#include +#endif + +#define qmrom_msleep(ms) \ + do { \ + usleep(ms * 1000); \ + } while (0) + +#define qmrom_alloc(ptr, size) \ + do { \ + ptr = calloc(1, size); \ + } while (0) + +#define qmrom_free free + +#define qmrom_data_dma_able(d) true +#else + +#include +#include +#include + +#define qmrom_msleep(ms) \ + do { \ + usleep_range(ms * 1000, ms * 1000); \ + } while (0) + +#define qmrom_alloc(ptr, size) \ + do { \ + ptr = kzalloc(size, GFP_KERNEL | GFP_DMA); \ + } while (0) + +#define qmrom_free kfree + +#define qmrom_data_dma_able(d) !is_vmalloc_addr(d) +#endif +#endif /* __QMROM_UTILS_H__ */ diff --git a/include/linux/uwb/spi_rom_protocol.h b/include/linux/uwb/spi_rom_protocol.h new file mode 100644 index 000000000000..0bb7dd8d948b --- /dev/null +++ b/include/linux/uwb/spi_rom_protocol.h @@ -0,0 +1,92 @@ +/* + * Copyright 2021 Qorvo US, Inc. + * + * SPDX-License-Identifier: GPL-2.0 OR Apache-2.0 + * + * This file is provided under the Apache License 2.0, or the + * GNU General Public License v2.0. + * + */ +#ifndef __SPI_ROM_PROTOCOL_H__ +#define __SPI_ROM_PROTOCOL_H__ + +#include "qmrom_error.h" +#include "qmrom.h" + +#ifndef __KERNEL__ +#include +#else +#include +#endif + +#define SPI_PROTO_WRONG_RESP SPI_PROTO_ERR_BASE - 1 + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +struct stc { + union { + struct { + union { + struct { + uint8_t reserved : 5; + uint8_t read : 1; // Read indication: Set to one by the host to tell the SOC SPI driver that this transaction is a read, otherwise it is set to zero. + uint8_t pre_read : 1; // Pre-read indication: Set to one by the host to tell the SOC SPI driver that the next transaction will be a read, otherwise it is set to zero + uint8_t write : 1; // Write indication: Set to one by the host when it is doing a write transaction. Set to zero when the host is not doing a write. + } host_flags; + struct { + uint8_t reserved : 4; + uint8_t err : 1; // Error indication: This is set to one by the SOC SPI driver to tell the HOST that it has detected some error in the SPI protocol. + uint8_t ready : 1; // + uint8_t out_active : 1; // Output active indication: Set to one by the SOC SPI driver to tell the HOST that it is outputting data on MISO line and expecting that the host is doing a read transaction at this time. This is set to zero for all other transactions. + uint8_t out_waiting : 1; // Output data waiting indication: Set to one by the SOC SPI driver to tell the HOST there is data awaiting reading. This is set to zero when there is no data pending output. + } soc_flags; + uint8_t raw_flags; + }; + uint8_t ul; + uint16_t len; + }; + uint32_t all; + }; + uint8_t payload[0]; +} __attribute__((packed)); +#pragma GCC diagnostic pop + +/* Host to Soc (HS) masks */ +#define SPI_HS_WRITE_CMD_BIT_MASK 0x80 +#define SPI_HS_PRD_CMD_BIT_MASK 0x40 +#define SPI_HS_RD_CMD_BIT_MASK 0x20 + +/* Soc to Host (SH) masks */ +#define SPI_SH_ODW_BIT_MASK 0x80 +#define SPI_SH_ACTIVE_BIT_MASK 0x40 +#define SPI_SH_READY_CMD_BIT_MASK 0x20 +#define SPI_DEVICE_READY_FLAGS SPI_SH_ODW_BIT_MASK + +/* Communication parameters */ +#define MAX_STC_FRAME_LEN 2048 +#define MAX_STC_PAYLOAD_LEN (MAX_STC_FRAME_LEN - sizeof(struct stc)) +#define SPI_NUM_READS_FOR_READY 1 +#define SPI_NUM_FAILS_RETRY 4 +#define SPI_ET_PROTOCOL 5 +#define SPI_RST_LOW_DELAY_MS 20 +#define SPI_INTERCMD_DELAY_MS 1 +#define SPI_DEVICE_POLL_RETRY 10 +#define SPI_READY_TIMEOUT_MS 50 +#define SPI_ET_VERSION_LOCATION 0x601f0000 + +/* ROM boot proto */ +#define SPI_ROM_READ_VERSION_SIZE_A0 3 +#define SPI_ROM_READ_IMAGE_CERT_SIZE_A0 3 +#define SPI_ROM_READ_IMAGE_SIZE_SIZE_A0 3 + +#define SPI_ROM_READ_VERSION_SIZE_B0 5 +#define SPI_ROM_READ_INFO_SIZE_B0 50 +#define SPI_ROM_READ_IMAGE_CERT_SIZE_B0 1 +#define SPI_ROM_READ_IMAGE_SIZE_SIZE_B0 1 + +#define SPI_ROM_WRITE_KEY_CERT_SIZE 6 +#define SPI_ROM_WRITE_IMAGE_CERT_SIZE 40 +#define SPI_ROM_WRITE_IMAGE_SIZE_SIZE 4 +#define SPI_ROM_DBG_CERT_SIZE_SIZE 5 + +#endif /* __SPI_ROM_PROTOCOL_H__ */ diff --git a/include/media/cci_intf.h b/include/media/cci_intf.h new file mode 100644 index 000000000000..16b31687d0fd --- /dev/null +++ b/include/media/cci_intf.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2018 Motorola Mobility LLC. + * + * 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. + * + */ + +#ifndef _CCI_INTF_H_ +#define _CCI_INTF_H_ + +#include +#include + +#define MSM_CCI_INTF_MAX_XFER 256 + +struct msm_cci_intf_register { + unsigned short width; /* 1 or 2 */ + unsigned short addr; +}; + +struct msm_cci_intf_data { + unsigned short count; /* between 1 and CCI_INTF_MAX_XFER */ + unsigned short width; + uint8_t buf[MSM_CCI_INTF_MAX_XFER]; +}; + +struct msm_cci_intf_xfer { + unsigned short cci_device; /* 0 = DEVICE_0, 1 = DEVICE_1 */ + unsigned short cci_bus; /* 0 = MASTER_0, 1 = MASTER_1 */ + unsigned short slave_addr; /* 7-bit addr of intended device */ + struct msm_cci_intf_register reg; + struct msm_cci_intf_data data; +}; + +#define MSM_CCI_INTF_READ \ + _IOWR('X', BASE_VIDIOC_PRIVATE + 50, struct msm_cci_intf_xfer) +#define MSM_CCI_INTF_READ32 \ + _IOWR('X', BASE_VIDIOC_PRIVATE + 50, struct msm_cci_intf_xfer) + +#define MSM_CCI_INTF_WRITE \ + _IOWR('X', BASE_VIDIOC_PRIVATE + 51, struct msm_cci_intf_xfer) +#define MSM_CCI_INTF_WRITE32 \ + _IOWR('X', BASE_VIDIOC_PRIVATE + 51, struct msm_cci_intf_xfer) + +#endif diff --git a/include/soc/qcom/mmi_boot_info.h b/include/soc/qcom/mmi_boot_info.h new file mode 100644 index 000000000000..e5e40eca3e34 --- /dev/null +++ b/include/soc/qcom/mmi_boot_info.h @@ -0,0 +1,82 @@ +/* + * include/soc/qcom/mmi_boot_info.h: Include file for boot information + * provided on Motorola phones + * + * Copyright (C) 2009 Motorola, Inc. + * Copyright (C) 2018 Motorola, 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 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Date Author Comment + * 01/07/2009 Motorola Initial version + */ + +#ifndef __ASMARM_BOOTINFO_H +#define __ASMARM_BOOTINFO_H + + +/* + * These #defines are used for the bits in powerup_reason. + */ +#define PU_REASON_USB_CABLE 0x00000010 /* Bit 4 */ +#define PU_REASON_FACTORY_CABLE 0x00000020 /* Bit 5 */ +#define PU_REASON_PWR_KEY_PRESS 0x00000080 /* Bit 7 */ +#define PU_REASON_CHARGER 0x00000100 /* Bit 8 */ +#define PU_REASON_POWER_CUT 0x00000200 /* bit 9 */ +#define PU_REASON_SW_AP_RESET 0x00004000 /* Bit 14 */ +#define PU_REASON_WDOG_AP_RESET 0x00008000 /* Bit 15 */ +#define PU_REASON_AP_KERNEL_PANIC 0x00020000 /* Bit 17 */ +#define PU_REASON_MODEM_RESET 0x80000000 /* Bit 31 */ +#define PU_REASON_INVALID 0xFFFFFFFF + +/* + * These #defines are used for the battery status at boot. + * When no battery is present, the status is BATTERY_LO_VOLTAGE. + */ +#define BATTERY_GOOD_VOLTAGE 1 +#define BATTERY_LO_VOLTAGE 2 +#define BATTERY_UNKNOWN (-1) + +/* + * /proc/bootinfo has a strict format. Each line contains a name/value + * pair which are separated with a colon and a single space on both + * sides of the colon. The following defines help you size the + * buffers used to read the data from /proc/bootinfo. + * + * BOOTINFO_MAX_NAME_LEN: maximum size in bytes of a name in the + * bootinfo line. Don't forget to add space + * for the NUL if you need it. + * BOOTINFO_MAX_VAL_LEN: maximum size in bytes of a value in the + * bootinfo line. Don't forget to add space + * for the NUL if you need it. + * BOOTINFO_BUF_SIZE: size in bytes of buffer that is large enough + * to read a /proc/bootinfo line. The extra + * 3 is for the " : ". Don't forget to add + * space for the NUL and newline if you + * need them. + */ +#define BOOTINFO_MAX_NAME_LEN 32 +#define BOOTINFO_MAX_VAL_LEN 128 +#define BOOTINFO_BUF_SIZE (BOOTINFO_MAX_NAME_LEN + \ + 3 + BOOTINFO_MAX_VAL_LEN) + +u32 bi_powerup_reason(void); +const char *bi_bootreason(void); +u32 bi_mbm_version(void); +const char *bi_bootmode(void); +u32 bi_boot_seq(void); +u32 bi_hwrev(void); + +#endif diff --git a/include/sound/tas2560_algo.h b/include/sound/tas2560_algo.h new file mode 100644 index 000000000000..92c65d8f9653 --- /dev/null +++ b/include/sound/tas2560_algo.h @@ -0,0 +1,131 @@ +/* +** ============================================================================= +** Copyright (c) 2016 Texas Instruments 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; version 2. +** +** 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: +** tas2560_algo.h +** +** Description: +** Parameter IDs, Control IDs and Functions to communicate with tas2560 +** algo Module present in AFE. +** +** ============================================================================ +*/ + +#ifndef _TAS2560_ALGO_H +#define _TAS2560_ALGO_H + +#include +#include +#include +#include + +/* Below 4 IDs should be same as in CAPIv2 tas2560 algo Module */ +#define AFE_PARAM_ID_TAS2560_ALGO_RX_ENABLE 0x00012D13 +#define AFE_PARAM_ID_TAS2560_ALGO_TX_ENABLE 0x00012D14 +#define AFE_PARAM_ID_TAS2560_ALGO_RX_CFG 0x00012D15 +#define AFE_PARAM_ID_TAS2560_ALGO_TX_CFG 0x00012D16 + +/* Below 2 IDs should be same as in CAPIv2 tas2560 algo Module */ +#define AFE_TAS2560_ALGO_MODULE_RX 0x1000FC00 +#define AFE_TAS2560_ALGO_MODULE_TX 0x1000FD00 + +#define MAX_TAS2560_ALGO_PARAM_INDEX 600 + +#define TAS2560_ALGO_GET_PARAM 1 +#define TAS2560_ALGO_SET_PARAM 0 +#define TAS2560_ALGO_PAYLOAD_SIZE 14 +#define TAS2560_ALGO_RX_PORT_ID 0x1016 /*Quinary Port-Rx*/ +#define TAS2560_ALGO_TX_PORT_ID 0x1017 /*Quinary Port-Tx*/ +/*I2C Slave addresses, 0x98 is used in Mono-Case*/ +#define SLAVE1 0x98 +#define SLAVE2 0x9A +#define SLAVE3 0x9C +#define SLAVE4 0x9E +#define SLAVE_BASE 0x96 +#define GET_SLAVE(X) ((X * 2) + SLAVE_BASE) + +/*(Page,Offset) -> Parameter Index Formula*/ +#define TAS2560_ALGO_GET_IDX(X1, X2) \ + ((X1 - 1) * 30 + (X2 - 8) / 4) +/*Special parameter ranges from 3810 to 3899 +* (these are handled in capiv2 framework and doesn't reach core algorithm) +*/ +#define TAS2560_ALGO_IS_SPL_IDX(X) \ + ((((X) >= 3810) && ((X) < 3899)) ? 1 : 0) +/*Param Id to be sent to capiv2 library +* |31 . . . . 24|23 . . . . . 24|15 . . . . . . . . . . 0| +* |Instance ID |Length |Parameter Index | +*/ +#define TAS2560_ALGO_CALC_PIDX(X1, X2, X3, X4) \ + (X1 | X2 | (X3 << 16) | (((X4 - SLAVE_BASE) / 2) << 24)) +#define TAS2560_ALGO_MIN(X1, X2) \ + ((X1 < X2) ? (X1) : (X2)) + +#define TAS2560_ALGO_CMD 0 +#define TAS2560_ALGO_CMD_SET_RE 1 + +/*Param ID to bypass algorithm dynamically*/ +#define TAS2560_ALGO_CFG_ENABLE 415 + +/*Param ID to disable/enable FB*/ +#define TAS2560_ALGO_FB 101 +#define TAS2560_ALGO_FB_ENABLE (0xc0000000) +#define TAS2560_ALGO_FB_DISABLE (0x40000000) +#define TAS2560_ALGO_SWAP 413 + + +/*Special parameter ranges from 3810 to 3899 +* (these are handled in capiv2 framework and doesn't reach core algorithm) +*/ +/*Read F0*/ +#define TAS2560_ALGO_GET_F0 3810 +/*Read Q*/ +#define TAS2560_ALGO_GET_Q 3811 +/* Tv */ +#define TAS2560_ALGO_GET_TV 3812 +/*Read Rdc*/ +#define TAS2560_ALGO_GET_RDC 3813 +/*Start Command for Calibration*/ +#define TAS2560_ALGO_CALIB_START_RDC 3814 +/*Stop Command for Calibration*/ +#define TAS2560_ALGO_CALIB_STOP_RDC 3815 +/*Set Calibrated Rdc*/ +#define TAS2560_ALGO_CFG_SET_RE 3816 +/*Start Command for F0,Q test*/ +#define TAS2560_ALGO_CALIB_START_F0_Q 3817 +/*Stop Command for F0,Q test*/ +#define TAS2560_ALGO_CALIB_STOP_F0_Q 3818 +/*Set Profile(Music(0),Ringtone(1)...etc)*/ +#define TAS2560_ALGO_PROFILE 3819 + +#define AFE_PARAM_ID_TAS2560_SPEAKER_ID 3820 + +struct afe_tas2560_algo_params_t { + uint32_t payload[TAS2560_ALGO_PAYLOAD_SIZE]; +} __packed; + +struct afe_tas2560_algo_set_config_t { + struct apr_hdr hdr; + struct afe_port_cmd_set_param_v2 param; + struct afe_port_param_data_v2 data; +} __packed; + +struct afe_tas2560_algo_get_config_t { + struct apr_hdr hdr; + struct afe_port_cmd_get_param_v2 param; + struct afe_port_param_data_v2 data; +} __packed; + +int tas2560_algo_routing_init(struct snd_soc_pcm_runtime *rtd); + +int afe_tas2560_algo_ctrl(u8 *data, u32 param_id, + u32 module_id, u8 dir, u8 size); +#endif diff --git a/include/trace/events/mmi_sigprint.h b/include/trace/events/mmi_sigprint.h new file mode 100644 index 000000000000..396cb98788d8 --- /dev/null +++ b/include/trace/events/mmi_sigprint.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 Motorola Mobility Inc. + * + * 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. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mmi_sigprint + +#if !defined(_TRACE_MMI_SIGPRINT_EVENTS_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_MMI_SIGPRINT_EVENTS_H + +#include +/* +DECLARE_TRACE(mmi_sigprint_signal_generate, + TP_PROTO(int firstarg, struct task_struct *p), + TP_ARGS(firstarg, p)); + +DECLARE_TRACE(mmi_sigprintsignal_deliver, + TP_PROTO(int firstarg, struct task_struct *p), + TP_ARGS(firstarg, p)); +*/ + +#endif /* _TRACE_MMI_SIGPRINT_EVENTS_H */ + +/* This part must be outside protection */ +#include diff --git a/sound/soc/codecs/Android.mk b/sound/soc/codecs/Android.mk new file mode 100644 index 000000000000..a344e58d8139 --- /dev/null +++ b/sound/soc/codecs/Android.mk @@ -0,0 +1,38 @@ +DLKM_DIR := motorola/kernel/modules +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := snd_soc_tfa9874.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := tas2560.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := tas2560_algo.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := tas2560_calib.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := tas2562.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk + +include $(CLEAR_VARS) +LOCAL_MODULE := snd_smartpa_aw882xx.ko +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE_PATH := $(KERNEL_MODULES_OUT) +include $(DLKM_DIR)/AndroidKernelModule.mk diff --git a/sound/soc/codecs/Kbuild b/sound/soc/codecs/Kbuild new file mode 100644 index 000000000000..3892d7a44ae1 --- /dev/null +++ b/sound/soc/codecs/Kbuild @@ -0,0 +1,71 @@ +# add -Wall to try to catch everything we can. +EXTRA_CFLAGS += -Wall +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/include + +HAVE_AUDIO_KERNEL = $(shell test -d $(TOP)/vendor/qcom/opensource/audio-kernel && echo 1) + +ifeq ($(HAVE_AUDIO_KERNEL),1) +$(info audio kernel path exist) +EXTRA_CFLAGS += -I$(TOP)/vendor/qcom/opensource/audio-kernel/include +EXTRA_CFLAGS += -I$(TOP)/vendor/qcom/opensource/audio-kernel/include/uapi +else +$(info audio kernel path not exist, include techpack audio header) +EXTRA_CFLAGS += -I$(TOP)/kernel/techpack/audio/include +EXTRA_CFLAGS += -I$(TOP)/kernel/techpack/audio/include/uapi +endif + + +ifneq (,$(filter channel%, $(TARGET_PRODUCT))) +EXTRA_CFLAGS += -DCONFIG_TAS2560_REGMAP -DCONFIG_TAS2560_CODEC -DCONFIG_TAS2560_MISC -DCONFIG_SND_SOC_TAS2560 +endif + +ifneq (,$(filter sofiar%, $(TARGET_PRODUCT))) +EXTRA_CFLAGS += -DCONFIG_TAS2562_REGMAP -DCONFIG_TAS2562_CODEC -DCONFIG_SND_SOC_TAS2562 -DTAS2558_CODEC -DCONFIG_TAS25XX_ALGO -DCONFIG_TAS25XX_ALGO_STEREO -DCONFIG_TAS25XX_CALIB +endif + +ifneq (,$(filter rav%, $(TARGET_PRODUCT))) +EXTRA_CFLAGS += -DCONFIG_TAS2562_REGMAP -DCONFIG_TAS2562_CODEC -DCONFIG_SND_SOC_TAS2562 -DTAS2558_CODEC -DCONFIG_TAS25XX_ALGO -DCONFIG_TAS25XX_CALIB +endif + +ifneq (,$(filter astro%, $(TARGET_PRODUCT))) +EXTRA_CFLAGS += -DCONFIG_AW882XX_CODEC -DCONFIG_AW882XX_DSP +endif + +ifneq (,$(filter surf% ocean%, $(TARGET_PRODUCT))) +ifeq ($(KDIR),) +PLMA_HOSTSW_DIR=$(realpath -f $(srctree)/$(src)) +else +PLMA_HOSTSW_DIR=$(realpath -f $(src)) +endif +GIT_VERSION=$(shell cd $(PLMA_HOSTSW_DIR); git describe --tags --dirty --match "v[0-9]*.[0-9]*.[0-9]*") +EXTRA_CFLAGS += -DTFA98XX_GIT_VERSIONS=\"$(GIT_VERSION)\" +# debugging support (also enables trace_printk) +EXTRA_CFLAGS += -DTFA9874_NONDSP_STEREO +EXTRA_CFLAGS += -I$(TOP)/motorola/kernel/modules/sound/soc/codecs/inc +EXTRA_CFLAGS += -Werror + +snd_soc_tfa9874-objs += tfa98xx.o +snd_soc_tfa9874-objs += tfa_container.o +snd_soc_tfa9874-objs += tfa_dsp.o +snd_soc_tfa9874-objs += tfa_init.o +ifdef TFA_DEBUG +EXTRA_CFLAGS += -DTFA_DEBUG -DDEBUG +snd_soc_tfa9874-objs += tfa_debug.o +endif +obj-m += snd_soc_tfa9874.o +endif + +tas2560-objs = tas2560-core.o tas2560-misc.o tas2560-codec.o tas2560-regmap.o +obj-m += tas2560.o + +tas2560_algo-objs = tas2560-algo.o +obj-m += tas2560_algo.o + +tas2560_calib-objs = tas2560-calib.o +obj-m += tas2560_calib.o + +tas2562-objs = tas2562-codec.o tas2562-regmap.o tas25xx-algo.o tas25xx-calib.o +obj-m += tas2562.o + +snd_smartpa_aw882xx-objs = aw882xx.o +obj-m += snd_smartpa_aw882xx.o diff --git a/sound/soc/codecs/aw882xx.c b/sound/soc/codecs/aw882xx.c new file mode 100644 index 000000000000..0e009fd94192 --- /dev/null +++ b/sound/soc/codecs/aw882xx.c @@ -0,0 +1,2831 @@ +/* + * aw882xx.c aw882xx codec module + * + * Version: v0.1.9 + * + * keep same with AW882XX_VERSION + * + * Copyright (c) 2019 AWINIC Technology CO., LTD + * + * Author: Nick Li + * + * 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. + */ + +#ifdef CONFIG_AW882XX_CODEC + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aw882xx.h" +#include "aw882xx_reg.h" + +/****************************************************** + * + * Marco + * + ******************************************************/ +#define AW882XX_I2C_NAME "aw882xx_smartpa" + +#define AW882XX_VERSION "v0.1.9" + +#define AW882XX_RATES SNDRV_PCM_RATE_8000_48000 +#define AW882XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) + + +#define AW_I2C_RETRIES 5 /* 5 times */ +#define AW_I2C_RETRY_DELAY 5 /* 5 ms */ +#define AW_READ_CHIPID_RETRIES 5 /* 5 times */ +#define AW_READ_CHIPID_RETRY_DELAY 5 /* 5 ms */ + + +#define CALI_BUF_MAX 100 +#define AWINIC_CALI_FILE "/mnt/vendor/persist/factory/audio/aw_cali.bin" + +#ifdef CONFIG_AW882XX_DSP +extern int aw_send_afe_cal_apr(uint32_t param_id, void *buf,int cmd_size, bool write); +extern int aw_send_rx_module_enable(void *buf, int cmd_size); +extern int aw_send_tx_module_enable(void *buf, int cmd_size); +extern int aw_adm_param_enable(int port_id, int module_id, int param_id, int enable); +#else +static int aw_send_afe_cal_apr(uint32_t param_id, void *buf,int cmd_size, bool write) { + return 0; +} +static int aw_send_rx_module_enable(void *buf, int cmd_size) +{ + return 0; +} +static int aw_send_tx_module_enable(void *buf, int cmd_size) +{ + return 0; +} +static int aw_adm_param_enable(int port_id, int copp_idx, int module_id, int param_id, int enable) +{ + return 0; +} +#endif + +static int aw882xx_get_cali_re_form_nv(int32_t *cali_re); +static int aw882xx_set_cali_re(struct aw882xx *aw882xx, int32_t cali_re); +static int aw882xx_load_profile_params(struct aw882xx *aw882xx); +static void aw882xx_skt_set_dsp(int value); +static int aw882xx_send_profile_params_to_dsp(struct aw882xx *aw882xx, int profile_id, bool is_fade); +static void aw882xx_fade_in_out(struct aw882xx *aw882xx, bool is_fade_in); + +/*monitor voltage and temperature table*/ +static struct aw882xx_low_vol vol_down_table[] = { + {3500, IPEAK_2P50_A, GAIN_NEG_1P5_DB}, + {3700, IPEAK_2P75_A, GAIN_NEG_1P0_DB}, + {3900, IPEAK_3P00_A, GAIN_NEG_0P5_DB}, + }; +static struct aw882xx_low_vol vol_up_table[] = { + {4000, IPEAK_3P50_A, GAIN_NEG_0P0_DB}, + {3800, IPEAK_3P00_A, GAIN_NEG_0P5_DB}, + {3600, IPEAK_2P75_A, GAIN_NEG_1P0_DB}, + }; +static struct aw882xx_low_temp temp_down_table[] = { + {-5, IPEAK_2P50_A, GAIN_NEG_6P0_DB, VMAX_063_PERCENTAGE}, + { 0, IPEAK_2P75_A, GAIN_NEG_4P5_DB, VMAX_075_PERCENTAGE}, + { 5, IPEAK_3P00_A, GAIN_NEG_3P0_DB, VMAX_086_PERCENTAGE}, + }; +static struct aw882xx_low_temp temp_up_table[] = { + { 7, IPEAK_3P50_A, GAIN_NEG_0P0_DB, VMAX_100_PERCENTAGE}, + { 2, IPEAK_3P00_A, GAIN_NEG_3P0_DB, VMAX_086_PERCENTAGE}, + {-2, IPEAK_2P75_A, GAIN_NEG_4P5_DB, VMAX_075_PERCENTAGE}, + }; + +static int aw882xx_monitor_start(struct aw882xx_monitor *monitor); +static int aw882xx_monitor_stop(struct aw882xx_monitor *monitor); + +/****************************************************** + * + * Value + * + ******************************************************/ +static int aw882xx_spk_control; +static int aw882xx_rcv_control; + +static atomic_t g_algo_rx_enable; +static atomic_t g_algo_tx_enable; +static atomic_t g_skt_disable; +static struct aw882xx *g_aw882xx = NULL; +static int8_t g_aw882xx_cali_flag = 0; +static int8_t g_aw882xx_profile_flag = 0; + +#define AW882XX_CFG_NAME_MAX 64 +static char aw882xx_cfg_name[][AW882XX_CFG_NAME_MAX] = { + {"aw882xx_spk_reg.bin"}, + {"aw882xx_rcv_reg.bin"}, +}; + +static unsigned int aw882xx_mode_cfg_shift[AW882XX_MODE_SHIFT_MAX] = { + AW882XX_MODE_SPK_SHIFT, + AW882XX_MODE_RCV_SHIFT, +}; + +/****************************************************** + * + * aw882xx i2c write/read + * + ******************************************************/ +static int aw882xx_i2c_writes(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned char *buf, unsigned int len) +{ + int ret = -1; + unsigned char *data; + + data = kmalloc(len+1, GFP_KERNEL); + if (data == NULL) { + pr_err("%s: can not allocate memory\n", __func__); + return -ENOMEM; + } + + data[0] = reg_addr; + memcpy(&data[1], buf, len); + + ret = i2c_master_send(aw882xx->i2c, data, len+1); + if (ret < 0) + pr_err("%s: i2c master send error\n", __func__); + + kfree(data); + + return ret; +} + +static int aw882xx_i2c_reads(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned char *data_buf, unsigned int data_len) +{ + int ret; + struct i2c_msg msg[] = { + [0] = { + .addr = aw882xx->i2c->addr, + .flags = 0, + .len = sizeof(uint8_t), + .buf = ®_addr, + }, + [1] = { + .addr = aw882xx->i2c->addr, + .flags = I2C_M_RD, + .len = data_len, + .buf = data_buf, + }, + }; + + ret = i2c_transfer(aw882xx->i2c->adapter, msg, ARRAY_SIZE(msg)); + if (ret < 0) { + pr_err("%s: i2c master send error, ret=%d\n", + __func__, ret); + return ret; + } else if (ret != AW882XX_I2C_READ_MSG_NUM) { + pr_err("%s: couldn't read registers, return %d bytes\n", + __func__, ret); + return -ENXIO; + } + + return 0; +} + +static int aw882xx_i2c_write(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char buf[2]; + + buf[0] = (reg_data&0xff00)>>8; + buf[1] = (reg_data&0x00ff)>>0; + if (aw882xx->monitor.sysctrl) { + pr_info("%s: 0x%x <= 0x%x\n", __func__, reg_addr, reg_data); + } + while (cnt < AW_I2C_RETRIES) { + ret = aw882xx_i2c_writes(aw882xx, reg_addr, buf, 2); + if (ret < 0) + pr_err("%s: i2c_write cnt=%d error=%d\n", + __func__, cnt, ret); + else + break; + cnt++; + } + + return ret; +} + +static int aw882xx_i2c_read(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int *reg_data) +{ + int ret = -1; + unsigned char cnt = 0; + unsigned char buf[2]; + + while (cnt < AW_I2C_RETRIES) { + ret = aw882xx_i2c_reads(aw882xx, reg_addr, buf, 2); + if (ret < 0) { + pr_err("%s: i2c_read cnt=%d error=%d\n", + __func__, cnt, ret); + } else { + *reg_data = (buf[0]<<8) | (buf[1]<<0); + break; + } + cnt++; + } + + return ret; +} + +static int aw882xx_i2c_write_bits(struct aw882xx *aw882xx, + unsigned char reg_addr, unsigned int mask, unsigned int reg_data) +{ + int ret = -1; + unsigned int reg_val = 0; + + ret = aw882xx_i2c_read(aw882xx, reg_addr, ®_val); + if (ret < 0) { + pr_err("%s: i2c read error, ret=%d\n", __func__, ret); + return ret; + } + + if (aw882xx->monitor.sysctrl) { + if ((reg_addr == AW882XX_SYSCTRL_REG) && ((reg_val & 0x40) == 0)) { + pr_err("%s: 0x04 => 0x%x, I2SEN wrong!\n", __func__, reg_val); + reg_val &= 0xFFC7; + reg_val |= 0x4440; // init value in bin + pr_info("%s: errata 0x04 => 0x%x\n", __func__, reg_val); + } else if ((reg_addr == AW882XX_I2SCTRL_REG) && ((reg_val & 0x1000) == 0)) { + pr_err("%s: 0x06 => 0x%x, I2SRXEN wrong!\n", __func__, reg_val); + reg_val &= 0x3FFF; + reg_val |= 0x1408; // init value in bin + pr_info("%s: errata 0x06 => 0x%x\n", __func__, reg_val); + } + } + + reg_val &= mask; + reg_val |= reg_data; + ret = aw882xx_i2c_write(aw882xx, reg_addr, reg_val); + if (ret < 0) { + pr_err("%s: i2c read error, ret=%d\n", __func__, ret); + return ret; + } + + return 0; +} + +/****************************************************** + * + * aw882xx control + * + ******************************************************/ +static void aw882xx_run_mute(struct aw882xx *aw882xx, bool mute) +{ + pr_debug("%s: enter\n", __func__); + + if (mute) { + aw882xx_fade_in_out(aw882xx, false); + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL2_REG, + AW882XX_HMUTE_MASK, + AW882XX_HMUTE_ENABLE_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL2_REG, + AW882XX_HMUTE_MASK, + AW882XX_HMUTE_DISABLE_VALUE); + aw882xx_fade_in_out(aw882xx, true); + } +} + +static bool aw882xx_get_power_status(struct aw882xx *aw882xx) +{ + unsigned int reg_value = 0; + int ret = 0; + pr_debug("%s: enter\n", __func__); + + ret = aw882xx_i2c_read(aw882xx, AW882XX_SYSCTRL_REG, ®_value); + if (ret < 0) { + pr_err("%s: read reg %d failed \n", __func__, AW882XX_SYSCTRL_REG); + return false; + } + if (aw882xx->monitor.sysctrl) { + pr_info("%s: 0x04 => 0x%x\n", __func__, reg_value); + } + /*bit 0: 1 power off, 0 power on*/ + if (reg_value & 0x01) { + return false; + } else { + return true; + } +} + +static void aw882xx_run_pwd(struct aw882xx *aw882xx, bool pwd) +{ + pr_debug("%s: enter\n", __func__); + + if (pwd) { + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL_REG, + AW882XX_PWDN_MASK, + AW882XX_PWDN_POWER_DOWN_VALUE); + } else { + aw882xx_i2c_write_bits(aw882xx, AW882XX_SYSCTRL_REG, + AW882XX_PWDN_MASK, + AW882XX_PWDN_NORMAL_WORKING_VALUE); + } +} + +static int aw882xx_sysst_check(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned char i; + unsigned int reg_val = 0; + + for (i = 0; i < AW882XX_SYSST_CHECK_MAX; i++) { + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_val); + if (((reg_val & (~AW882XX_SYSST_CHECK_MASK)) & AW882XX_SYSST_CHECK) == + AW882XX_SYSST_CHECK) { + ret = 0; + break; + } else { + pr_debug("%s: check fail, cnt=%d, reg_val=0x%04x\n", + __func__, i, reg_val); + msleep(2); + } + } + if (ret < 0) + pr_info("%s: check fail\n", __func__); + + return ret; +} + +/* +static int aw882xx_get_sysint(struct aw882xx *aw882xx, unsigned int *sysint) +{ + int ret = -1; + unsigned int reg_val = 0; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_SYSINT_REG, ®_val); + if (ret < 0) + pr_info("%s: read sysint fail, ret=%d\n", __func__, ret); + else + *sysint = reg_val; + + return ret; +} + +static int aw882xx_get_iis_status(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned int reg_val = 0; + + pr_debug("%s: enter\n", __func__); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_val); + if (reg_val & AW882XX_PLLS_LOCKED_VALUE) + ret = 0; + + return ret; +} +*/ + +static int aw882xx_get_icalk(struct aw882xx *aw882xx, int16_t *icalk) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t reg_icalk = 0; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_EFRM1_REG, ®_val); + reg_icalk = (uint16_t)reg_val & AW882XX_EF_ISN_GESLP_MASK; + + if (reg_icalk & AW882XX_EF_ISN_GESLP_SIGN_MASK) + reg_icalk = reg_icalk | AW882XX_EF_ISN_GESLP_NEG; + + *icalk = (int16_t)reg_icalk; + + return ret; +} + +static int aw882xx_get_vcalk(struct aw882xx *aw882xx, int16_t *vcalk) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t reg_vcalk = 0; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_EFRH_REG, ®_val); + + reg_vcalk = (uint16_t)reg_val & AW882XX_EF_VSN_GESLP_MASK; + + if (reg_vcalk & AW882XX_EF_VSN_GESLP_SIGN_MASK) + reg_vcalk = reg_vcalk | AW882XX_EF_VSN_GESLP_NEG; + + *vcalk = (int16_t)reg_vcalk; + + return ret; +} + +static int aw882xx_set_vcalb(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned int reg_val; + int vcalb; + int icalk; + int vcalk; + int16_t icalk_val = 0; + int16_t vcalk_val = 0; + + ret = aw882xx_get_icalk(aw882xx, &icalk_val); + ret = aw882xx_get_vcalk(aw882xx, &vcalk_val); + + icalk = AW882XX_CABL_BASE_VALUE + AW882XX_ICABLK_FACTOR * icalk_val; + vcalk = AW882XX_CABL_BASE_VALUE + AW882XX_VCABLK_FACTOR * vcalk_val; + + vcalb = AW882XX_VCAL_FACTOR * icalk / vcalk; + + reg_val = (unsigned int)vcalb; + pr_debug("%s: icalk=%d, vcalk=%d, vcalb=%d, reg_val=%d\n", + __func__, icalk, vcalk, vcalb, reg_val); + + ret = aw882xx_i2c_write(aw882xx, AW882XX_VTMCTRL3_REG, reg_val); + + return ret; +} + +static void aw882xx_send_cali_re_to_dsp(struct aw882xx *aw882xx) +{ + int ret = 0; + if ((aw882xx != NULL) && (aw882xx->cali_re != ERRO_CALI_VALUE)) { + ret = aw_send_afe_cal_apr(AFE_PARAM_ID_AWDSP_RX_RE_L, + &aw882xx->cali_re, sizeof(int32_t), true); + } + if (ret) + pr_err("%s : set cali re to dsp failed 0x%x\n", + __func__ , AFE_PARAM_ID_AWDSP_RX_RE_L); +} + +static void aw882xx_start(struct aw882xx *aw882xx) +{ + int ret = -1; + int32_t cali_re; + pr_debug("%s: enter\n", __func__); + + ret = aw882xx_get_cali_re_form_nv(&cali_re); + if (ret < 0) { + cali_re = 29900; /* 7.3*4096 */ + pr_err("%s: use default vaule", __func__); + } + ret = aw882xx_set_cali_re(aw882xx, cali_re); + if (ret < 0) + pr_err("%s: set cali re failed: %d\n", __func__, ret); + + mutex_lock(&aw882xx->lock); + pr_info("%s: monitor is_enable %d,spk_rcv_mode %d\n", + __func__, aw882xx->monitor.is_enable, aw882xx->spk_rcv_mode); + if (aw882xx->monitor.is_enable && + (aw882xx->spk_rcv_mode == AW882XX_SPEAKER_MODE)) { + aw882xx_monitor_start(&aw882xx->monitor); + } + ret = aw882xx_send_profile_params_to_dsp(aw882xx, aw882xx->profile.cur_profile, false); + if (ret) { + pr_err("%s: set profile %d failed \n", __func__,aw882xx->profile.cur_profile); + aw882xx->profile.cur_profile = 0; + } + aw882xx_send_cali_re_to_dsp(aw882xx); + + aw882xx_run_pwd(aw882xx, false); + ret = aw882xx_sysst_check(aw882xx); + if (ret < 0) { + aw882xx_run_mute(aw882xx, true); + aw882xx_run_pwd(aw882xx, true); + aw882xx->init = AW882XX_INIT_NG; + } else { + aw882xx_run_mute(aw882xx, false); + aw882xx->init = AW882XX_INIT_OK; + } + + mutex_unlock(&aw882xx->lock); +} + +static void aw882xx_stop(struct aw882xx *aw882xx) +{ + pr_debug("%s: enter\n", __func__); + + mutex_lock(&aw882xx->lock); + aw882xx_run_mute(aw882xx, true); + aw882xx_run_pwd(aw882xx, true); + aw882xx->profile.cur_profile = 0; + if (aw882xx->monitor.is_enable) + aw882xx_monitor_stop(&aw882xx->monitor); + mutex_unlock(&aw882xx->lock); +} + +/****************************************************** + * + * aw882xx config + * + ******************************************************/ +static int aw882xx_get_cali_re_form_nv(int32_t *cali_re) +{ + /*custom add, if success return value is 0 , else -1*/ + struct file *fp; + char buf[CALI_BUF_MAX]; + int32_t read_re; + loff_t pos = 0; + mm_segment_t fs; + + memset(buf, 0, CALI_BUF_MAX); + /*open cali file*/ + fp = filp_open(AWINIC_CALI_FILE, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("%s: open %s failed!", __func__, AWINIC_CALI_FILE); + return -EINVAL; + } + /*set fs kernel*/ + fs = get_fs(); + set_fs(get_ds()); + + /*read file*/ + vfs_read(fp, buf, CALI_BUF_MAX - 1, &pos); + + /*get cali re value*/ + if (sscanf(buf, "%d", &read_re) != 1) { + pr_err("%s: file read error", __func__); + set_fs(fs); + filp_close(fp, NULL); + return -EINVAL; + } + set_fs(fs); + + /*close file*/ + filp_close(fp, NULL); + + *cali_re = read_re; + pr_info("%s: %d", __func__, read_re); + return 0; +} + +static int aw882xx_set_cali_re(struct aw882xx *aw882xx, int32_t cali_re) +{ + if (aw882xx == NULL) + return -EINVAL; + aw882xx->cali_re = cali_re; + return 0; +} + +static int aw882xx_reg_container_update(struct aw882xx *aw882xx, + struct aw882xx_container *aw882xx_cont) +{ + int i = 0; + int reg_addr = 0; + int reg_val = 0; + int ret = -1; + + pr_debug("%s: enter\n", __func__); + + for (i = 0; i < aw882xx_cont->len; i += 4) { + reg_addr = (aw882xx_cont->data[i+1]<<8) + + aw882xx_cont->data[i+0]; + reg_val = (aw882xx_cont->data[i+3]<<8) + + aw882xx_cont->data[i+2]; + pr_debug("%s: reg=0x%04x, val = 0x%04x\n", + __func__, reg_addr, reg_val); + ret = aw882xx_i2c_write(aw882xx, + (unsigned char)reg_addr, + (unsigned int)reg_val); + if (ret < 0) + break; + } + + pr_debug("%s: exit\n", __func__); + + return ret; +} + +static void aw882xx_reg_loaded(const struct firmware *cont, void *context) +{ + struct aw882xx *aw882xx = context; + struct aw882xx_container *aw882xx_cfg; + int ret = -1; + + if (!cont) { + pr_err("%s: failed to read %s\n", __func__, + aw882xx_cfg_name[aw882xx->cfg_num]); + release_firmware(cont); + return; + } + + pr_info("%s: loaded %s - size: %zu\n", __func__, + aw882xx_cfg_name[aw882xx->cfg_num], cont ? cont->size : 0); + + aw882xx_cfg = kzalloc(cont->size+sizeof(int), GFP_KERNEL); + if (!aw882xx_cfg) { + release_firmware(cont); + pr_err("%s: error allocating memory\n", __func__); + return; + } + aw882xx_cfg->len = cont->size; + memcpy(aw882xx_cfg->data, cont->data, cont->size); + release_firmware(cont); + + mutex_lock(&aw882xx->lock); + ret = aw882xx_reg_container_update(aw882xx, aw882xx_cfg); + if (ret < 0) { + pr_err("%s: reg update fail\n", __func__); + } else { + pr_err("%s: reg update sucess\n", __func__); + aw882xx_run_mute(aw882xx, true); + aw882xx_set_vcalb(aw882xx); + } + mutex_unlock(&aw882xx->lock); + kfree(aw882xx_cfg); + aw882xx_load_profile_params(aw882xx); + aw882xx_start(aw882xx); +} + +static int aw882xx_load_reg(struct aw882xx *aw882xx) +{ + pr_info("%s: enter\n", __func__); + + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + aw882xx_cfg_name[aw882xx->cfg_num], + aw882xx->dev, GFP_KERNEL, + aw882xx, aw882xx_reg_loaded); +} + +static void aw882xx_get_cfg_shift(struct aw882xx *aw882xx) +{ + aw882xx->cfg_num = aw882xx_mode_cfg_shift[aw882xx->spk_rcv_mode]; + pr_debug("%s: cfg_num=%d\n", __func__, aw882xx->cfg_num); +} + +static void aw882xx_cold_start(struct aw882xx *aw882xx) +{ + int ret = -1; + + pr_info("%s: enter\n", __func__); + + aw882xx_get_cfg_shift(aw882xx); + + ret = aw882xx_load_reg(aw882xx); + if (ret < 0) + pr_err("%s: cfg loading requested failed: %d\n", __func__, ret); + +} + +static void aw882xx_smartpa_cfg(struct aw882xx *aw882xx, bool flag) +{ + pr_info("%s: flag = %d\n", __func__, flag); + + if (flag == true) { + if ((aw882xx->init == AW882XX_INIT_ST) || + (aw882xx->init == AW882XX_INIT_NG)) { + pr_info("%s: init = %d\n", __func__, aw882xx->init); + aw882xx_cold_start(aw882xx); + } else { + aw882xx_start(aw882xx); + } + } else { + aw882xx_stop(aw882xx); + } +} + +/****************************************************** + * + * kcontrol + * + ******************************************************/ +static const char *const spk_function[] = { "Off", "On" }; +static const char *const rcv_function[] = { "Off", "On" }; +static const char *const awinic_algo[] = { "Disable", "Enable" }; +static const char *const awinic_profile[AW_PROFILE_MAX] = { +"Music", "Ringtone", "Notification", "Voice"}; +static const char *const awinic_profile_param_file_name[] = { + "aw882xx_afe_params.bin", + }; + +static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 50, 0); + +struct soc_mixer_control aw882xx_mixer = { + .reg = AW882XX_HAGCCFG4_REG, + .shift = AW882XX_VOL_START_BIT, + .max = AW882XX_VOLUME_MAX, + .min = AW882XX_VOLUME_MIN, +}; + +static int aw882xx_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + /* set kcontrol info */ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mc->max - mc->min; + return 0; +} + +static int aw882xx_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = snd_soc_codec_get_drvdata(codec); + unsigned int reg_val = 0; + unsigned int value = 0; + struct soc_mixer_control *mc = + (struct soc_mixer_control *) kcontrol->private_value; + + aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_val); + ucontrol->value.integer.value[0] = (value >> mc->shift) & + (AW882XX_VOL_MASK); + return 0; +} + +static int aw882xx_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *) kcontrol->private_value; + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = snd_soc_codec_get_drvdata(codec); + unsigned int value = 0; + unsigned int reg_value = 0; + + /* value is right */ + value = ucontrol->value.integer.value[0]; + if (value > (mc->max-mc->min) || value < 0) { + pr_err("%s: value over range\n", __func__); + return -ERANGE; + } + + /* smartpa have clk */ + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_value); + if (!(reg_value & AW882XX_PLLS_LOCKED_VALUE)) { + pr_err("%s: NO I2S CLK ,cat not write reg\n", __func__); + return 0; + } + + /* cal real value */ + value = (value << mc->shift) & AW882XX_VOL_MASK; + aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_value); + value = value | (reg_value & 0x00ff); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW882XX_HAGCCFG4_REG, value); + + return 0; +} + +static struct snd_kcontrol_new aw882xx_volume = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "aw882xx_rx_volume", + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE, + .tlv.p = (digital_gain), + .info = aw882xx_volume_info, + .get = aw882xx_volume_get, + .put = aw882xx_volume_put, + .private_value = (unsigned long)&aw882xx_mixer, +}; + +static int aw882xx_spk_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw882xx_spk_control=%d\n", + __func__, aw882xx_spk_control); + ucontrol->value.integer.value[0] = aw882xx_spk_control; + return 0; +} + +static int aw882xx_spk_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + if (ucontrol->value.integer.value[0] == aw882xx_spk_control) + return 1; + + aw882xx_spk_control = ucontrol->value.integer.value[0]; + + if (AW882XX_SPEAKER_MODE != aw882xx->spk_rcv_mode) { + aw882xx->spk_rcv_mode = AW882XX_SPEAKER_MODE; + aw882xx->init = AW882XX_INIT_ST; + } + + return 0; +} + +static int aw882xx_rcv_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw882xx_rcv_control=%d\n", __func__, aw882xx_rcv_control); + ucontrol->value.integer.value[0] = aw882xx_rcv_control; + return 0; +} + +static int aw882xx_rcv_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + if (ucontrol->value.integer.value[0] == aw882xx_rcv_control) + return 1; + + aw882xx_rcv_control = ucontrol->value.integer.value[0]; + + if (AW882XX_RECEIVER_MODE != aw882xx->spk_rcv_mode) { + aw882xx->spk_rcv_mode = AW882XX_RECEIVER_MODE; + aw882xx->init = AW882XX_INIT_ST; + } + + return 0; +} + +static int aw882xx_algo_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw882xx_algo enable=%d\n", + __func__, atomic_read(&g_algo_rx_enable)); + + ucontrol->value.integer.value[0] = atomic_read(&g_algo_rx_enable); + + return 0; +} + +static int aw882xx_algo_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + ctrl_value = ucontrol->value.integer.value[0]; + ret = aw_send_rx_module_enable(&ctrl_value, sizeof(uint32_t)); + if (ret) + pr_err("%s: set algo %d failed, ret=%d\n", + __func__, ctrl_value, ret); + atomic_set(&g_algo_rx_enable, ctrl_value); + return 0; +} + +static int aw882xx_tx_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw882xx_tx_control=%d\n", __func__, + atomic_read(&g_algo_tx_enable)); + + ucontrol->value.integer.value[0] = atomic_read(&g_algo_tx_enable); + + return 0; +} + +static int aw882xx_tx_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int ret = -EINVAL; + uint32_t ctrl_value = 0; + + pr_debug("%s: ucontrol->value.integer.value[0]=%ld\n", + __func__, ucontrol->value.integer.value[0]); + + ctrl_value = ucontrol->value.integer.value[0]; + ret = aw_send_tx_module_enable(&ctrl_value, sizeof(uint32_t)); + if (ret) + pr_err("%s: set tx enable %d, ret=%d\n", __func__, ctrl_value, ret); + atomic_set(&g_algo_tx_enable, ctrl_value); + return 0; +} + +static int aw882xx_profile_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: profile : %s \n", __func__, + awinic_profile[aw882xx->profile.cur_profile]); + ucontrol->value.integer.value[0] = aw882xx->profile.cur_profile; + return 0; +} +static int aw882xx_profile_firmware_parse(struct aw882xx *aw882xx, const struct firmware *cont) +{ + aw_afe_params_hdr_t *hdr; + char *fw_data = NULL; + uint8_t sum = 0; + uint32_t param_size = 0; + char *param_data = NULL; + int i; + pr_info("%s: loaded %s - size: %zu\n", __func__, + awinic_profile_param_file_name[0], cont ? cont->size : 0); + + if (cont->size == 0) { + goto load_failed; + } + + fw_data = kzalloc(cont->size, GFP_KERNEL); + if (fw_data == NULL) { + pr_err("%s: alloc memory failed \n", __func__); + goto load_failed; + } + memcpy(fw_data, cont->data, cont->size); + + /*output bin info*/ + hdr = (aw_afe_params_hdr_t *)fw_data; + pr_info("%s: fw v_%d.%d.%d \n", __func__, + hdr->fw[0], hdr->fw[1], hdr->fw[2]); + pr_info("%s: param v_%d.%d.%d \n", __func__, + hdr->cfg[0], hdr->cfg[1], hdr->cfg[2]); + pr_info("%s: project name : %s \n", __func__, hdr->project); + pr_info("%s: param len: %d", __func__, hdr->params_len); + + param_data = fw_data + sizeof(aw_afe_params_hdr_t); + param_size = cont->size - sizeof(aw_afe_params_hdr_t); + + for (i = 0; i < param_size; i++) { + sum += param_data[i]; + } + if (sum != hdr->check_sum) { + pr_err("%s:data check sum failed : %d -- %d \n", __func__, + sum, hdr->check_sum); + kfree(fw_data); + goto load_failed; + } + + if (hdr->profile_num < AW_PROFILE_MAX) { + pr_err("%s: profile num is not match %d -- %d ", __func__, + hdr->profile_num, AW_PROFILE_MAX); + kfree(fw_data); + goto load_failed; + } + + if (hdr->params_len != (param_size / hdr->profile_num)) { + pr_err("%s: profile params len is not match %d ", __func__, + hdr->params_len); + kfree(fw_data); + goto load_failed; + } + + mutex_lock(&aw882xx->profile.lock); + param_data = fw_data + sizeof(aw_afe_params_hdr_t); + for (i = 0; i < AW_PROFILE_MAX; i++) { + aw882xx->profile.data[i] = kzalloc(hdr->params_len, GFP_KERNEL); + if (aw882xx->profile.data[i] == NULL) { + pr_err("%s: alloc failed !", __func__); + kfree(fw_data); + mutex_unlock(&aw882xx->profile.lock); + goto load_failed; + } + memcpy(aw882xx->profile.data[i], param_data , hdr->params_len); + param_data += hdr->params_len; + } + aw882xx->profile.len = hdr->params_len; + aw882xx->profile.status = AW882XX_INIT_OK; + mutex_unlock(&aw882xx->profile.lock); + kfree(fw_data); + return 0; + +load_failed: + mutex_lock(&aw882xx->profile.lock); + for (i = 0; i < AW_PROFILE_MAX; i++) { + if (aw882xx->profile.data[i]) { + kfree(aw882xx->profile.data[i]); + aw882xx->profile.data[i] = NULL; + } + } + aw882xx->profile.status = AW882XX_INIT_NG; + aw882xx->profile.len = 0; + mutex_unlock(&aw882xx->profile.lock); + pr_err("%s: load params bin failed !", __func__); + return 0; +} + +static int aw882xx_load_profile_params(struct aw882xx *aw882xx) +{ + const struct firmware *fw = NULL; + int ret; + pr_info("%s: enter\n", __func__); + + ret = request_firmware(&fw, + awinic_profile_param_file_name[0], + aw882xx->dev); + if (ret) { + pr_err("%s: load profile params failed !", __func__); + return -EINVAL; + } + + ret = aw882xx_profile_firmware_parse(aw882xx, fw); + return ret; +} + +static void aw882xx_volume_set(struct aw882xx *aw882xx, unsigned int value) +{ + unsigned int reg_value = 0; + unsigned int real_value = ((value / VOLUME_STEP_DB) << 4) + (value % VOLUME_STEP_DB) * 2; + + /* cal real value */ + aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_value); + real_value = (real_value << 8) | (reg_value & 0x00ff); + + /* write value */ + aw882xx_i2c_write(aw882xx, AW882XX_HAGCCFG4_REG, real_value); +} + +static void aw882xx_fade_in_out(struct aw882xx *aw882xx, bool is_fade_in) +{ + int i = 0; + + //volume up + if (is_fade_in) { + i = VOLUME_MIN_NEG_90_DB; + do { + aw882xx_volume_set(aw882xx, i); + i -= FADE_STEP_DB; + usleep_range(1400,1600); + } while (i >= 0); + } else { //volume down + do { + aw882xx_volume_set(aw882xx, i); + i += FADE_STEP_DB; + usleep_range(1400,1600); + } while (i <= VOLUME_MIN_NEG_90_DB); + } +} + +static int aw882xx_send_profile_params_to_dsp(struct aw882xx *aw882xx, int profile_id, bool is_fade) +{ + int ret ; + int32_t params_id = AFE_PARAM_ID_AWDSP_RX_PARAMS; + + mutex_lock(&aw882xx->profile.lock); + if (aw882xx->profile.data[profile_id] == NULL) { + pr_err("%s: profile %s not data is NULL \n", __func__, + awinic_profile[profile_id]); + mutex_unlock(&aw882xx->profile.lock); + return -EINVAL; + } + if (is_fade) { + g_aw882xx_profile_flag = true; + aw882xx_run_mute(aw882xx, true); + } + ret = aw_send_afe_cal_apr(params_id, + aw882xx->profile.data[profile_id], + aw882xx->profile.len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, params_id); + aw882xx_run_mute(aw882xx, false); + g_aw882xx_profile_flag = false; + mutex_unlock(&aw882xx->profile.lock); + return ret; + } + if (is_fade) { + aw882xx_run_mute(aw882xx, false); + g_aw882xx_profile_flag = false; + } + mutex_unlock(&aw882xx->profile.lock); + + pr_debug("%s: set params done", __func__); + return 0; +} + +static int aw882xx_profile_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct aw882xx *aw882xx = snd_soc_codec_get_drvdata(codec); + unsigned int ctl_value = 0; + int ret; + ctl_value = ucontrol->value.integer.value[0]; + + if (ctl_value >= AW_PROFILE_MAX) { + pr_err("%s: unsuport profile %d", __func__, ctl_value); + return -EINVAL; + } + pr_debug("%s: profile switch to %s \n", + __func__, awinic_profile[ctl_value]); + + mutex_lock(&aw882xx->lock); + if (aw882xx->profile.cur_profile != ctl_value) { + /*PA have power on ,set params direct*/ + if (aw882xx_get_power_status(aw882xx)) { + ret = aw882xx_send_profile_params_to_dsp(aw882xx, ctl_value, true); + if (ret < 0) { + mutex_unlock(&aw882xx->lock); + return -EINVAL; + } + aw882xx_send_cali_re_to_dsp(aw882xx); + aw882xx->profile.cur_profile = ctl_value; + } else { + /*PA power only set params status*/ + aw882xx->profile.cur_profile = ctl_value; + } + } + mutex_unlock(&aw882xx->lock); + + return 0; +} + +static int aw882xx_skt_disable_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + pr_debug("%s: aw882xx_skt_disable %d\n", + __func__, atomic_read(&g_skt_disable)); + + ucontrol->value.integer.value[0] = atomic_read(&g_skt_disable); + + return 0; +} + +static void aw882xx_skt_set_dsp(int value) +{ + int ret; + int port_id = AFE_RX_PROT_ID; + int module_id = AW_MODULE_ID_COPP; + int param_id = AW_MODULE_PARAMS_ID_COPP_ENABLE; + + ret = aw_adm_param_enable(port_id, module_id, param_id, value); + if (ret) { + pr_err("%s: set skt %d failed \n", __func__, value); + return; + } + + pr_info("%s: set skt %s", __func__, value == 1 ? "enable" : "disable"); +} + +static int aw882xx_skt_disable_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int value = ucontrol->value.integer.value[0]; + + pr_debug("%s: aw882xx_skt_disable %d \n", __func__, value); + aw882xx_skt_set_dsp(!value); + atomic_set(&g_skt_disable, value); + + return 0; +} + +static const struct soc_enum aw882xx_snd_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rcv_function), rcv_function), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(awinic_algo), awinic_algo), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(awinic_profile), awinic_profile), +}; + +static struct snd_kcontrol_new aw882xx_controls[] = { + SOC_ENUM_EXT("aw882xx_speaker_switch", aw882xx_snd_enum[0], + aw882xx_spk_get, aw882xx_spk_set), + SOC_ENUM_EXT("aw882xx_receiver_switch", aw882xx_snd_enum[1], + aw882xx_rcv_get, aw882xx_rcv_set), + SOC_ENUM_EXT("aw882xx_rx_switch", aw882xx_snd_enum[2], + aw882xx_algo_get, aw882xx_algo_set), + SOC_ENUM_EXT("aw882xx_tx_switch", aw882xx_snd_enum[2], + aw882xx_tx_get, aw882xx_tx_set), + SOC_ENUM_EXT("aw882xx_profile_switch", aw882xx_snd_enum[3], + aw882xx_profile_get, aw882xx_profile_set), + SOC_ENUM_EXT("aw882xx_skt_disable", aw882xx_snd_enum[0], + aw882xx_skt_disable_get, aw882xx_skt_disable_set), +}; + +static void aw882xx_add_codec_controls(struct aw882xx *aw882xx) +{ + pr_info("%s: enter\n", __func__); + + snd_soc_add_codec_controls(aw882xx->codec, aw882xx_controls, + ARRAY_SIZE(aw882xx_controls)); + + snd_soc_add_codec_controls(aw882xx->codec, &aw882xx_volume, 1); +} + +/****************************************************** + * + * Digital Audio Interface + * + ******************************************************/ + + +static int aw882xx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct aw882xx *aw882xx = snd_soc_codec_get_drvdata(codec); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + pr_info("%s: playback enter\n", __func__); + aw882xx_run_pwd(aw882xx, false); + } else { + pr_info("%s: capture enter\n", __func__); + } + + return 0; +} + +static int aw882xx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + /*struct aw882xx *aw882xx = snd_soc_codec_get_drvdata(dai->codec);*/ + struct snd_soc_codec *codec = dai->codec; + + pr_info("%s: fmt=0x%x\n", __func__, fmt); + + /* Supported mode: regular I2S, slave, or PDM */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != + SND_SOC_DAIFMT_CBS_CFS) { + dev_err(codec->dev, "%s: invalid codec master mode\n", + __func__); + return -EINVAL; + } + break; + default: + dev_err(codec->dev, "%s: unsupported DAI format %d\n", + __func__, fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + return 0; +} + +static int aw882xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct aw882xx *aw882xx = snd_soc_codec_get_drvdata(codec_dai->codec); + + pr_info("%s: freq=%d\n", __func__, freq); + + aw882xx->sysclk = freq; + return 0; +} + +static int aw882xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct aw882xx *aw882xx = snd_soc_codec_get_drvdata(codec); + unsigned int rate = 0; + uint32_t cco_mux_value; + int reg_value = 0; + int width = 0; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + pr_debug("%s: requested rate: %d, sample size: %d\n", + __func__, rate, snd_pcm_format_width(params_format(params))); + return 0; + } + /* get rate param */ + aw882xx->rate = rate = params_rate(params); + pr_debug("%s: requested rate: %d, sample size: %d\n", + __func__, rate, snd_pcm_format_width(params_format(params))); + + /* match rate */ + switch (rate) { + case 8000: + reg_value = AW882XX_I2SSR_8KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_8_16_32KHZ_VALUE; + break; + case 16000: + reg_value = AW882XX_I2SSR_16KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_8_16_32KHZ_VALUE; + break; + case 32000: + reg_value = AW882XX_I2SSR_32KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_8_16_32KHZ_VALUE; + break; + case 44100: + reg_value = AW882XX_I2SSR_44P1KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + break; + case 48000: + reg_value = AW882XX_I2SSR_48KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + break; + case 96000: + reg_value = AW882XX_I2SSR_96KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + break; + case 192000: + reg_value = AW882XX_I2SSR_192KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + break; + default: + reg_value = AW882XX_I2SSR_48KHZ_VALUE; + cco_mux_value = AW882XX_I2S_CCO_MUX_EXC_8_16_32KHZ_VALUE; + pr_err("%s: rate can not support\n", __func__); + break; + } + aw882xx_i2c_write_bits(aw882xx, AW882XX_PLLCTRL1_REG, + AW882XX_I2S_CCO_MUX_MASK, cco_mux_value); + + /* set chip rate */ + if (-1 != reg_value) { + aw882xx_i2c_write_bits(aw882xx, AW882XX_I2SCTRL_REG, + AW882XX_I2SSR_MASK, reg_value); + } + + /* get bit width */ + width = params_width(params); + pr_debug("%s: width = %d\n", __func__, width); + switch (width) { + case 16: + reg_value = AW882XX_I2SFS_16_BITS_VALUE; + break; + case 20: + reg_value = AW882XX_I2SFS_20_BITS_VALUE; + break; + case 24: + reg_value = AW882XX_I2SFS_24_BITS_VALUE; + break; + case 32: + reg_value = AW882XX_I2SFS_32_BITS_VALUE; + break; + default: + reg_value = AW882XX_I2SFS_16_BITS_VALUE; + pr_err("%s: width can not support\n", __func__); + break; + } + + /* get width */ + if (-1 != reg_value) { + aw882xx_i2c_write_bits(aw882xx, AW882XX_I2SCTRL_REG, + AW882XX_I2SFS_MASK, reg_value); + } + + return 0; +} + +static int aw882xx_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_codec *codec = dai->codec; + struct aw882xx *aw882xx = snd_soc_codec_get_drvdata(codec); + + pr_info("%s: mute state=%d\n", __func__, mute); + + if (!(aw882xx->flags & AW882XX_FLAG_START_ON_MUTE)) + return 0; + + if (mute) { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + aw882xx_smartpa_cfg(aw882xx, false); + atomic_set(&g_algo_tx_enable, 0); + atomic_set(&g_algo_rx_enable, 0); + atomic_set(&g_skt_disable, 0); + } else { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + aw882xx_smartpa_cfg(aw882xx, true); + } + + return 0; +} + +static void aw882xx_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct aw882xx *aw882xx = snd_soc_codec_get_drvdata(codec); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + aw882xx->rate = 0; + aw882xx_run_pwd(aw882xx, true); + } +} + +static const struct snd_soc_dai_ops aw882xx_dai_ops = { + .startup = aw882xx_startup, + .set_fmt = aw882xx_set_fmt, + .set_sysclk = aw882xx_set_dai_sysclk, + .hw_params = aw882xx_hw_params, + .mute_stream = aw882xx_mute, + .shutdown = aw882xx_shutdown, +}; + +static struct snd_soc_dai_driver aw882xx_dai[] = { + { + .name = "aw882xx-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AW882XX_RATES, + .formats = AW882XX_FORMATS, + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AW882XX_RATES, + .formats = AW882XX_FORMATS, + }, + .ops = &aw882xx_dai_ops, + .symmetric_rates = 1, +#if 0 + .symmetric_channels = 1, + .symmetric_samplebits = 1, +#endif + }, +}; + +/***************************************************** + * + * codec driver + * + *****************************************************/ +static int aw882xx_probe(struct snd_soc_codec *codec) +{ + struct aw882xx *aw882xx = snd_soc_codec_get_drvdata(codec); + int ret = -1; + + pr_info("%s: enter\n", __func__); + + aw882xx->codec = codec; + + aw882xx_add_codec_controls(aw882xx); + + //if (codec->dev->of_node) + // dev_set_name(codec->dev, "%s", "aw882xx_smartpa"); + + pr_info("%s: exit\n", __func__); + + ret = 0; + return ret; +} + +static int aw882xx_remove(struct snd_soc_codec *codec) +{ + /*struct aw882xx *aw882xx = snd_soc_codec_get_drvdata(codec);*/ + pr_info("%s: enter\n", __func__); + + return 0; +} + + +static unsigned int aw882xx_codec_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + struct aw882xx *aw882xx = snd_soc_codec_get_drvdata(codec); + unsigned int value = 0; + int ret = -1; + + pr_debug("%s: enter\n", __func__); + + if (aw882xx_reg_access[reg] & REG_RD_ACCESS) { + ret = aw882xx_i2c_read(aw882xx, reg, &value); + if (ret < 0) + pr_debug("%s: read register failed\n", __func__); + } else { + pr_debug("%s: register 0x%x no read access\n", __func__, reg); + } + return ret; +} + +static int aw882xx_codec_write(struct snd_soc_codec *codec, + unsigned int reg, unsigned int value) +{ + int ret = -1; + struct aw882xx *aw882xx = snd_soc_codec_get_drvdata(codec); + + pr_debug("%s: enter ,reg is 0x%x value is 0x%x\n", + __func__, reg, value); + + if (aw882xx_reg_access[reg]®_WR_ACCESS) { + ret = aw882xx_i2c_write(aw882xx, reg, value); + } else { + pr_debug("%s: register 0x%x no write access\n", + __func__, reg); + } + + return ret; +} + +static struct snd_soc_codec_driver soc_codec_dev_aw882xx = { + .probe = aw882xx_probe, + .remove = aw882xx_remove, + .read = aw882xx_codec_read, + .write = aw882xx_codec_write, + .reg_cache_size = AW882XX_REG_MAX, + .reg_word_size = 2, +}; + +/****************************************************** + * + * irq + * + ******************************************************/ +static void aw882xx_interrupt_setup(struct aw882xx *aw882xx) +{ + unsigned int reg_val; + + pr_info("%s: enter\n", __func__); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSINTM_REG, ®_val); + reg_val &= (~AW882XX_UVLS_VDD_BELOW_2P8V_VALUE); + reg_val &= (~AW882XX_NOCLKS_TRIG_VALUE); + reg_val &= (~AW882XX_CLKS_TRIG_VALUE); + aw882xx_i2c_write(aw882xx, AW882XX_SYSINTM_REG, reg_val); +} + +static void aw882xx_interrupt_clear(struct aw882xx *aw882xx) +{ + unsigned int reg_val = 0; + + pr_info("%s: enter\n", __func__); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSST_REG, ®_val); + pr_info("%s: reg SYSST=0x%x\n", __func__, reg_val); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSINT_REG, ®_val); + pr_info("%s: reg SYSINT=0x%x\n", __func__, reg_val); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSINTM_REG, ®_val); + pr_info("%s: reg SYSINTM=0x%x\n", __func__, reg_val); +} + +static irqreturn_t aw882xx_irq(int irq, void *data) +{ + struct aw882xx *aw882xx = data; + + pr_info("%s: enter\n", __func__); + + aw882xx_interrupt_clear(aw882xx); + + pr_info("%s: exit\n", __func__); + + return IRQ_HANDLED; +} + +/***************************************************** + * + * device tree + * + *****************************************************/ +static int aw882xx_parse_dt(struct device *dev, struct aw882xx *aw882xx, + struct device_node *np) +{ + int ret = 0; + struct aw882xx_monitor *monitor = &aw882xx->monitor; + /* gpio */ + aw882xx->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (aw882xx->reset_gpio < 0) { + dev_err(dev, "%s: no reset gpio provided, will not HW reset device\n", + __func__); + } else { + dev_info(dev, "%s: reset gpio provided ok\n", __func__); + } + aw882xx->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (aw882xx->irq_gpio < 0) + dev_info(dev, "%s: no irq gpio provided.\n", __func__); + else + dev_info(dev, "%s: irq gpio provided ok.\n", __func__); + + ret = of_property_read_u32(np, "monitor-flag", &monitor->is_enable); + if (ret) { + monitor->is_enable = AW882XX_MONITOR_DEFAULT_FLAG; + dev_err(dev, "%s: monitor-flag get failed ,user default value!\n", __func__); + } else { + dev_info(dev, "%s: monitor-flag = %d\n", + __func__, monitor->is_enable); + } + + ret = of_property_read_u32(np, "monitor-timer-val", &monitor->timer_val); + if (ret) { + monitor->timer_val = AW882XX_MONITOR_DEFAULT_TIMER_VAL; + dev_err(dev, "%s: monitor-timer-val get failed,user default value!\n", __func__); + } else { + dev_info(dev, "%s: monitor-timer-val = %d\n", + __func__, monitor->timer_val); + } + + ret = of_property_read_u32(np, "monitor-sysctrl", &monitor->sysctrl); + if (ret) { + monitor->sysctrl = AW882XX_MONITOR_SYSCTRL; + dev_err(dev, "%s: monitor-sysctrl get failed,user default value!\n", __func__); + } else { + dev_info(dev, "%s: monitor-sysctrl = %d\n", + __func__, monitor->sysctrl); + } + return 0; +} + +int aw882xx_hw_reset(struct aw882xx *aw882xx) +{ + pr_info("%s: enter\n", __func__); + + if (aw882xx && gpio_is_valid(aw882xx->reset_gpio)) { + gpio_set_value_cansleep(aw882xx->reset_gpio, 0); + msleep(1); + gpio_set_value_cansleep(aw882xx->reset_gpio, 1); + msleep(2); + } else { + dev_err(aw882xx->dev, "%s: failed\n", __func__); + } + return 0; +} + +/***************************************************** + * + * check chip id + * + *****************************************************/ +int aw882xx_read_chipid(struct aw882xx *aw882xx) +{ + int ret = -1; + unsigned int cnt = 0; + unsigned int reg = 0; + + while (cnt < AW_READ_CHIPID_RETRIES) { + ret = aw882xx_i2c_read(aw882xx, AW882XX_ID_REG, ®); + if (ret < 0) { + dev_err(aw882xx->dev, + "%s: failed to read REG_ID: %d\n", + __func__, ret); + return -EIO; + } + switch (reg) { + case AW882XX_ID: + pr_info("%s: aw882xx detected\n", __func__); + aw882xx->flags |= AW882XX_FLAG_SKIP_INTERRUPTS; + aw882xx->flags |= AW882XX_FLAG_START_ON_MUTE; + aw882xx->chipid = AW882XX_ID; + pr_info("%s: aw882xx->flags=0x%x\n", + __func__, aw882xx->flags); + return 0; + default: + pr_info("%s: unsupported device revision (0x%x)\n", + __func__, reg); + break; + } + cnt++; + + msleep(AW_READ_CHIPID_RETRY_DELAY); + } + + return -EINVAL; +} + +/****************************************************** + * + * sys group attribute: reg + * + ******************************************************/ +static ssize_t aw882xx_reg_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + unsigned int databuf[2] = {0}; + + if (2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) + aw882xx_i2c_write(aw882xx, databuf[0], databuf[1]); + + return count; +} + +static ssize_t aw882xx_reg_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned char i = 0; + unsigned int reg_val = 0; + + for (i = 0; i < AW882XX_REG_MAX; i++) { + if (aw882xx_reg_access[i]®_RD_ACCESS) { + aw882xx_i2c_read(aw882xx, i, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, + "reg:0x%02x=0x%04x\n", i, reg_val); + } + } + return len; +} + +static ssize_t aw882xx_rw_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + unsigned int databuf[2] = {0}; + + if (2 == sscanf(buf, "%x %x", &databuf[0], &databuf[1])) { + aw882xx->reg_addr = (unsigned char)databuf[0]; + aw882xx_i2c_write(aw882xx, databuf[0], databuf[1]); + } else if (1 == sscanf(buf, "%x", &databuf[0])) { + aw882xx->reg_addr = (unsigned char)databuf[0]; + } + + return count; +} + +static ssize_t aw882xx_rw_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + unsigned int reg_val = 0; + + if (aw882xx_reg_access[aw882xx->reg_addr] & REG_RD_ACCESS) { + aw882xx_i2c_read(aw882xx, aw882xx->reg_addr, ®_val); + len += snprintf(buf+len, PAGE_SIZE-len, + "reg:0x%02x=0x%04x\n", aw882xx->reg_addr, reg_val); + } + return len; +} + +static ssize_t aw882xx_spk_rcv_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + + int ret = -1; + unsigned int databuf[2] = {0}; + + ret = kstrtouint(buf, 0, &databuf[0]); + if (ret < 0) + return ret; + + aw882xx->spk_rcv_mode = databuf[0]; + + return count; +} + +static ssize_t aw882xx_spk_rcv_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + + if (aw882xx->spk_rcv_mode == AW882XX_SPEAKER_MODE) + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx spk_rcv: %d, speaker mode\n", + aw882xx->spk_rcv_mode); + else if (aw882xx->spk_rcv_mode == AW882XX_RECEIVER_MODE) + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx spk_rcv: %d, receiver mode\n", + aw882xx->spk_rcv_mode); + else + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx spk_rcv: %d, unknown mode\n", + aw882xx->spk_rcv_mode); + + return len; +} + +static ssize_t aw882xx_mec_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + /*struct aw882xx *aw882xx = dev_get_drvdata(dev);*/ + uint32_t mec_ctr = 0; + uint32_t param_id = AFE_PARAM_ID_AWDSP_RX_SET_ENABLE; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &mec_ctr); + if (ret < 0) + return ret; + + pr_info("%s: mec_ctr=%d\n", __func__, mec_ctr); + + ret = aw_send_afe_cal_apr(param_id, &mec_ctr, sizeof(uint32_t), true); + if (ret) + pr_err("%s: dsp_msg error, ret=%d\n", __func__, ret); + + mdelay(2); + + return count; +} + +static ssize_t aw882xx_mec_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + /*struct aw882xx *aw882xx = dev_get_drvdata(dev);*/ + ssize_t len = 0; + int ret = -1; + uint8_t *buffer = NULL; + uint32_t mec_ctr = 0; + uint32_t param_id = AFE_PARAM_ID_AWDSP_RX_SET_ENABLE; + + buffer = kmalloc(sizeof(uint32_t), GFP_KERNEL); + if (buffer == NULL) { + pr_err("%s can not allocate memory\n", __func__); + return -ENOMEM; + } + + ret = aw_send_afe_cal_apr(param_id, buffer, sizeof(uint32_t), false); + if (ret) { + pr_err("%s: dsp_msg_read error: %d\n", __func__, ret); + kfree(buffer); + return -EFAULT; + } + + memcpy(&mec_ctr, buffer, sizeof(uint32_t)); + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx mec: %d\n", mec_ctr); + + kfree(buffer); + + return len; +} +#ifdef AW_DEBUG +static ssize_t aw882xx_vol_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + uint32_t vol = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtouint(buf, 0, &vol); + if (ret < 0) + return ret; + + pr_info("%s: vol set =%d\n", __func__, vol); + aw882xx->monitor.test_vol = vol; + + return count; +} + +static ssize_t aw882xx_vol_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + uint32_t local_vol = aw882xx->monitor.test_vol; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx vol: %d\n", local_vol); + return len; +} +static ssize_t aw882xx_temp_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + int32_t temp = 0; + int ret = -1; + + if (count == 0) + return 0; + + ret = kstrtoint(buf, 0, &temp); + if (ret < 0) + return ret; + + pr_info("%s: temp set =%d\n", __func__, temp); + aw882xx->monitor.test_temp = temp; + + return count; +} + +static ssize_t aw882xx_temp_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct aw882xx *aw882xx = dev_get_drvdata(dev); + ssize_t len = 0; + int32_t local_temp = aw882xx->monitor.test_temp; + + len += snprintf(buf+len, PAGE_SIZE-len, + "aw882xx vol: %d\n", local_temp); + return len; +} +#endif +static int aw882xx_cali_operation(struct aw882xx *aw882xx, + unsigned int cmd, unsigned long arg) +{ + int16_t data_len = _IOC_SIZE(cmd); + int ret = 0; + char *data_ptr = NULL; + + + data_ptr = kmalloc(data_len, GFP_KERNEL); + if (data_ptr == NULL) { + pr_err("%s : malloc failed !\n", __func__); + return -EFAULT; + } + + pr_info("cmd : %d, data_len%d\n", cmd , data_len); + switch (cmd) { + case AW882XX_IOCTL_ENABLE_CALI: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + g_aw882xx_cali_flag = (int8_t)data_ptr[0]; + pr_info("%s:set cali %s", __func__, + (g_aw882xx_cali_flag == 0) ? ("disable") : ("enable")); + } break; + case AW882XX_IOCTL_SET_CALI_CFG: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + ret = aw_send_afe_cal_apr( + AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L); + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_GET_CALI_CFG: { + ret = aw_send_afe_cal_apr( + AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_GET_CALI_DATA: { + ret = aw_send_afe_cal_apr( + AFE_PARAM_ID_AWDSP_RX_REAL_DATA_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_REAL_DATA_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_SET_NOISE: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + ret = aw_send_afe_cal_apr(AFE_PARAM_ID_AWDSP_RX_NOISE_L, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_NOISE_L); + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_GET_F0: { + ret = aw_send_afe_cal_apr(AFE_PARAM_ID_AWDSP_RX_F0_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_F0_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_SET_CALI_RE: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + ret = aw_send_afe_cal_apr(AFE_PARAM_ID_AWDSP_RX_RE_L, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_RE_L); + ret = -EFAULT; + goto exit; + } + aw882xx_set_cali_re(aw882xx, *((int32_t *)data_ptr)); + } break; + case AW882XX_IOCTL_GET_CALI_RE: { + ret = aw_send_afe_cal_apr(AFE_PARAM_ID_AWDSP_RX_RE_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_RE_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_GET_VMAX: { + ret = aw_send_afe_cal_apr(AFE_PARAM_ID_AWDSP_RX_VMAX_L, + data_ptr, data_len, false); + if (ret) { + pr_err("%s: dsp_msg_read error:0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_VMAX_L); + ret = -EFAULT; + goto exit; + } + if (copy_to_user((void __user *)arg, + data_ptr, data_len)) { + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_SET_VMAX: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + ret = aw_send_afe_cal_apr(AFE_PARAM_ID_AWDSP_RX_VMAX_L, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_VMAX_L); + ret = -EFAULT; + goto exit; + } + } break; + case AW882XX_IOCTL_SET_PARAM: { + if (copy_from_user(data_ptr, + (void __user *)arg, data_len)) { + ret = -EFAULT; + goto exit; + } + ret = aw_send_afe_cal_apr(AFE_PARAM_ID_AWDSP_RX_PARAMS, + data_ptr, data_len, true); + if (ret) { + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_PARAMS); + ret = -EFAULT; + goto exit; + } + pr_debug("%s: set params done", __func__); + } break; + default: { + pr_err("%s : cmd %d\n", __func__, cmd); + } break; + } +exit: + kfree(data_ptr); + return ret; +} + + +static DEVICE_ATTR(reg, S_IWUSR | S_IRUGO, + aw882xx_reg_show, aw882xx_reg_store); +static DEVICE_ATTR(rw, S_IWUSR | S_IRUGO, + aw882xx_rw_show, aw882xx_rw_store); +static DEVICE_ATTR(spk_rcv, S_IWUSR | S_IRUGO, + aw882xx_spk_rcv_show, aw882xx_spk_rcv_store); +static DEVICE_ATTR(mec, S_IWUSR | S_IRUGO, + aw882xx_mec_show, aw882xx_mec_store); +#ifdef AW_DEBUG +static DEVICE_ATTR(vol, S_IWUSR | S_IRUGO, + aw882xx_vol_show, aw882xx_vol_store); +static DEVICE_ATTR(temp, S_IWUSR | S_IRUGO, + aw882xx_temp_show, aw882xx_temp_store); +#endif + +static struct attribute *aw882xx_attributes[] = { + &dev_attr_reg.attr, + &dev_attr_rw.attr, + &dev_attr_spk_rcv.attr, + &dev_attr_mec.attr, +#ifdef AW_DEBUG + &dev_attr_vol.attr, + &dev_attr_temp.attr, +#endif + NULL +}; + +static struct attribute_group aw882xx_attribute_group = { + .attrs = aw882xx_attributes +}; + +#define AW882XX_SMARTPA_NAME "aw882xx_smartpa" +static int aw882xx_file_open(struct inode *inode, struct file *file) +{ + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + file->private_data = (void *)g_aw882xx; + + pr_debug("open success"); + return 0; +} + +static int aw882xx_file_release(struct inode *inode, struct file *file) +{ + file->private_data = (void *)NULL; + + pr_debug("release successi\n"); + return 0; +} + +static long aw882xx_file_unlocked_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct aw882xx *aw882xx = NULL; + + if (((_IOC_TYPE(cmd)) != (AW882XX_IOCTL_MAGIC))) { + pr_err("%s: cmd magic err\n", __func__); + return -EINVAL; + } + aw882xx = (struct aw882xx *)file->private_data; + ret = aw882xx_cali_operation(aw882xx, cmd, arg); + if (ret) + return -EINVAL; + + return 0; +} + +static struct file_operations aw882xx_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = aw882xx_file_unlocked_ioctl, + .open = aw882xx_file_open, + .release = aw882xx_file_release, +}; + +static struct miscdevice aw882xx_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = AW882XX_SMARTPA_NAME, + .fops = &aw882xx_fops, +}; + +void init_aw882xx_misc_driver(struct aw882xx *aw882xx) +{ + int ret; + + ret = misc_register(&aw882xx_misc); + if (ret) { + pr_err("%s: misc register fail: %d\n", __func__, ret); + return; + } + pr_debug("%s: misc register success", __func__); +} + +/*monitor*/ +static int aw882xx_monitor_start(struct aw882xx_monitor *monitor) +{ + pr_debug("%s: enter\n", __func__); + + if (!hrtimer_active(&monitor->timer)) { + hrtimer_start(&monitor->timer, + ktime_set(monitor->timer_val/1000, + (monitor->timer_val%1000)*1000000), HRTIMER_MODE_REL); + } + + return 0; +} + +static int aw882xx_monitor_stop(struct aw882xx_monitor *monitor) +{ + pr_info("%s: enter\n", __func__); + + if (hrtimer_active(&monitor->timer)) { + hrtimer_cancel(&monitor->timer); + } + return 0; +} + +static enum hrtimer_restart + aw882xx_monitor_timer_func(struct hrtimer *timer) +{ + struct aw882xx_monitor *monitor = + container_of(timer, struct aw882xx_monitor, timer); + + pr_debug("%s : enter\n", __func__); + + if (monitor->is_enable) + schedule_work(&monitor->work); + + return HRTIMER_NORESTART; +} +static int aw882xx_monitor_get_voltage(struct aw882xx *aw882xx, + unsigned int *vol) +{ + int ret = -1; + uint16_t local_vol = 0; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_VBAT_REG, vol); + if (ret < 0) { + pr_err("%s: read voltage failed !\n", __func__); + return ret; + } + local_vol = (*vol) * AW882XX_MONITOR_VBAT_RANGE + / AW882XX_MONITOR_INT_10BIT; + + *vol = local_vol; + pr_debug("%s: chip voltage is %d\n", __func__, local_vol); + return 0; +} +static int aw882xx_monitor_voltage(struct aw882xx *aw882xx, + struct aw882xx_low_vol *vol_cfg) +{ + int ret = -1; + int i; + unsigned int voltage = 0; + struct aw882xx_monitor *monitor = NULL; + + if (aw882xx == NULL || vol_cfg == NULL) { + pr_err("%s: pointer is NUL\n", __func__); + return ret; + } + monitor = &aw882xx->monitor; +#ifdef AW_DEBUG + if (monitor->test_vol == 0) { + ret = aw882xx_monitor_get_voltage(aw882xx, &voltage); + if (ret < 0) + return ret; + } else { + voltage = monitor->test_vol; + } +#else + ret = aw882xx_monitor_get_voltage(aw882xx, &voltage); + if (ret < 0) + return ret; +#endif + if (monitor->pre_vol > voltage) { + /* vol down*/ + for (i = 0; i < 3; i++) { + if (voltage < vol_down_table[i].vol) { + *vol_cfg = vol_down_table[i]; + break; + } + } + if (i == 3) { + vol_cfg->ipeak = IPEAK_NONE; + vol_cfg->gain = GAIN_NONE; + } + } else if (monitor->pre_vol < voltage) { + /*vol up*/ + for (i = 0; i < 3; i++) { + if (voltage > vol_up_table[i].vol) { + *vol_cfg = vol_up_table[i]; + break; + } + } + if (i == 3) { + vol_cfg->ipeak = IPEAK_NONE; + vol_cfg->gain = GAIN_NONE; + } + } else { + /*vol no change*/ + vol_cfg->ipeak = IPEAK_NONE; + vol_cfg->gain = GAIN_NONE; + } + monitor->pre_vol = voltage; + return 0; +} +static int aw882xx_monitor_get_temperature(struct aw882xx *aw882xx, int *temp) +{ + int ret = -1; + unsigned int reg_val = 0; + uint16_t local_temp; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_TEMP_REG, ®_val); + if (ret < 0) { + pr_err("%s: get temperature failed !\n", __func__); + return ret; + } + + local_temp = reg_val; + if (local_temp & AW882XX_MONITOR_TEMP_SIGN_MASK) + local_temp = local_temp | AW882XX_MONITOR_TEMP_NEG_MASK; + + *temp = (int)local_temp; + pr_debug("%s: chip temperature = %d\n", __func__, local_temp); + return 0; +} + +static int aw882xx_monitor_temperature(struct aw882xx *aw882xx, + struct aw882xx_low_temp *temp_cfg) +{ + int ret; + int i; + struct aw882xx_monitor *monitor = NULL; + int current_temp = 0; + + monitor = &aw882xx->monitor; +#ifdef AW_DEBUG + if (monitor->test_temp == 0) { + ret = aw882xx_monitor_get_temperature(aw882xx, ¤t_temp); + if (ret) + return ret; + } else { + current_temp = monitor->test_temp; + } +#else + ret = aw882xx_monitor_get_temperature(aw882xx, ¤t_temp); + if (ret < 0) + return ret; +#endif + if (monitor->pre_temp > current_temp) { + /*temp down*/ + for (i = 0; i < 3; i++) { + if (current_temp < temp_down_table[i].temp) { + temp_cfg->ipeak = temp_down_table[i].ipeak; + temp_cfg->gain = temp_down_table[i].gain; + temp_cfg->vmax = temp_down_table[i].vmax; + break; + } + } + + if (i == 3) { + temp_cfg->ipeak = IPEAK_NONE; + temp_cfg->gain = GAIN_NONE; + temp_cfg->vmax = VMAX_NONE; + } + } else if (monitor->pre_temp < current_temp) { + /*temp up*/ + for (i = 0; i < 3; i++) { + if (current_temp > temp_up_table[i].temp) { + temp_cfg->ipeak = temp_up_table[i].ipeak; + temp_cfg->gain = temp_up_table[i].gain; + temp_cfg->vmax = temp_up_table[i].vmax; + break; + } + } + if (i == 3) { + temp_cfg->ipeak = IPEAK_NONE; + temp_cfg->gain = GAIN_NONE; + temp_cfg->vmax = VMAX_NONE; + } + } else { + /*temp no change*/ + temp_cfg->ipeak = IPEAK_NONE; + temp_cfg->gain = GAIN_NONE; + temp_cfg->vmax = VMAX_NONE; + } + monitor->pre_temp = current_temp; + return 0; +} + +static void aw882xx_monitor_get_cfg(struct aw882xx_low_temp *temp, + struct aw882xx_low_vol *vol) +{ + if (vol->ipeak == IPEAK_NONE) + return; + + if (temp->ipeak == IPEAK_NONE) { + temp->ipeak = vol->ipeak; + temp->gain = vol->gain; + return; + } + + /*get min ipeak*/ + if (temp->ipeak > vol->ipeak) + temp->ipeak = vol->ipeak; + + /*get min gain*/ + if (temp->gain < vol->gain) + temp->gain = vol->gain; + +} +static void aw882xx_monitor_set_ipeak(struct aw882xx *aw882xx, uint8_t ipeak) +{ + unsigned int reg_val = 0; + unsigned int read_reg_val; + int ret; + + if (ipeak == IPEAK_NONE) + return; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_SYSCTRL2_REG, ®_val); + if (ret < 0) { + pr_err("%s: read ipeak failed\n", __func__); + return; + } + + read_reg_val = reg_val; + read_reg_val &= AW882XX_BIT_SYSCTRL2_BST_IPEAK_MASK; + + if (read_reg_val == ipeak) { + pr_debug("%s: ipeak = 0x%x, no change\n", + __func__, read_reg_val); + return; + } + reg_val &= (~AW882XX_BIT_SYSCTRL2_BST_IPEAK_MASK); + read_reg_val = ipeak; + reg_val |= read_reg_val; + + ret = aw882xx_i2c_write(aw882xx, AW882XX_SYSCTRL2_REG, reg_val); + if (ret < 0) { + pr_err("%s: write ipeak failed\n", __func__); + return; + } + pr_debug("%s: set reg val = 0x%x, ipeak = 0x%x\n", + __func__, reg_val, ipeak); +} +static void aw882xx_monitor_set_gain(struct aw882xx *aw882xx, uint8_t gain) +{ + unsigned int reg_val = 0; + unsigned int read_reg_val; + int ret; + + if (gain == GAIN_NONE) + return; + + ret = aw882xx_i2c_read(aw882xx, AW882XX_HAGCCFG4_REG, ®_val); + if (ret < 0) { + pr_err("%s: read gain failed\n", __func__); + return; + } + + read_reg_val = reg_val; + read_reg_val = read_reg_val >> AW882XX_BIT_HAGCCFG4_GAIN_SHIFT; + + if (read_reg_val == gain) { + pr_debug("%s: gain = 0x%x, no change\n", + __func__, read_reg_val); + return; + } + reg_val &= AW882XX_BIT_HAGCCFG4_GAIN_MASK; + read_reg_val = gain; + reg_val |= (read_reg_val << AW882XX_BIT_HAGCCFG4_GAIN_SHIFT); + + ret = aw882xx_i2c_write(aw882xx, AW882XX_HAGCCFG4_REG, reg_val); + if (ret < 0) { + pr_err("%s: write gain failed\n", __func__); + return; + } + pr_debug("%s: set reg val = 0x%x, gain = 0x%x\n", + __func__, reg_val, gain); +} +static void aw882xx_monitor_set_vmax(struct aw882xx *aw882xx, uint32_t vmax) +{ + uint32_t local_vmax = vmax; + int ret; + + if (vmax == VMAX_NONE) + return; + + ret = aw_send_afe_cal_apr(AFE_PARAM_ID_AWDSP_RX_VMAX_L, + &local_vmax, sizeof(uint32_t), true); + if (ret) + pr_err("%s: dsp_msg_write error: 0x%x\n", + __func__, AFE_PARAM_ID_AWDSP_RX_VMAX_L); + + pr_debug("%s: set vmax = 0x%x\n", __func__, vmax); +} +static void aw882xx_monitor_work(struct aw882xx *aw882xx) +{ + struct aw882xx_low_vol vol_cfg; + struct aw882xx_low_temp temp_cfg; + int ret; + + if (aw882xx == NULL) { + pr_err("%s: pointer is NULL\n", __func__); + return; + } + if (g_aw882xx_cali_flag != 0 || g_aw882xx_profile_flag != 0) { + pr_info("%s: done nothing while start cali", __func__); + return; + } + + ret = aw882xx_monitor_voltage(aw882xx, &vol_cfg); + if (ret < 0) { + pr_err("%s: monitor voltage failed\n", __func__); + return; + } + + ret = aw882xx_monitor_temperature(aw882xx, &temp_cfg); + if (ret < 0) { + pr_err("%s: monitor temperature failed\n", __func__); + return; + } + pr_debug("%s: vol: ipeak = 0x%x, gain = 0x%x\n", + __func__, vol_cfg.ipeak, vol_cfg.gain); + pr_debug("%s: temp: ipeak = 0x%x, gain = 0x%x, vmax = 0x%x\n", + __func__, temp_cfg.ipeak, temp_cfg.gain, temp_cfg.vmax); + + aw882xx_monitor_get_cfg(&temp_cfg, &vol_cfg); + + aw882xx_monitor_set_ipeak(aw882xx, temp_cfg.ipeak); + + aw882xx_monitor_set_gain(aw882xx, temp_cfg.gain); + + aw882xx_monitor_set_vmax(aw882xx, temp_cfg.vmax); +} + +static int aw882xx_get_hmute(struct aw882xx *aw882xx) +{ + unsigned int reg_val = 0; + int ret; + + pr_debug("%s: enter\n", __func__); + + aw882xx_i2c_read(aw882xx, AW882XX_SYSCTRL2_REG, ®_val); + if ((~AW882XX_HMUTE_MASK) & reg_val) + ret = 1; + else + ret = 0; + + return ret; +} +static void aw882xx_monitor_work_func(struct work_struct *work) +{ + struct aw882xx_monitor *monitor = container_of(work, + struct aw882xx_monitor, work); + struct aw882xx *aw882xx = container_of(monitor, + struct aw882xx, monitor); + + pr_debug("%s: enter\n", __func__); + mutex_lock(&aw882xx->lock); + if (!aw882xx_get_hmute(aw882xx)) { + aw882xx_monitor_work(aw882xx); + aw882xx_monitor_start(monitor); + } + mutex_unlock(&aw882xx->lock); +} + +void init_aw882xx_monitor(struct aw882xx_monitor *monitor) +{ + pr_info("%s: enter\n", __func__); + hrtimer_init(&monitor->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + monitor->timer.function = aw882xx_monitor_timer_func; + INIT_WORK(&monitor->work, aw882xx_monitor_work_func); + monitor->pre_vol = 0; + monitor->pre_temp = 0; +#ifdef AW_DEBUG + monitor->test_vol = 0; + monitor->test_temp = 0; +#endif +} + +/****************************************************** + * + * i2c driver + * + ******************************************************/ +static int aw882xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct snd_soc_dai_driver *dai; + struct aw882xx *aw882xx; + struct device_node *np = i2c->dev.of_node; + int irq_flags = 0; + int ret = -1; + int i; + + pr_info("%s: enter\n", __func__); + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed\n"); + return -EIO; + } + + aw882xx = devm_kzalloc(&i2c->dev, sizeof(struct aw882xx), GFP_KERNEL); + if (aw882xx == NULL) + return -ENOMEM; + + aw882xx->dev = &i2c->dev; + aw882xx->i2c = i2c; + + i2c_set_clientdata(i2c, aw882xx); + mutex_init(&aw882xx->lock); + + /* aw882xx rst & int */ + if (np) { + ret = aw882xx_parse_dt(&i2c->dev, aw882xx, np); + if (ret) { + dev_err(&i2c->dev, + "%s: failed to parse device tree node\n", + __func__); + goto err_parse_dt; + } + } else { + aw882xx->reset_gpio = -1; + aw882xx->irq_gpio = -1; + } + + if (gpio_is_valid(aw882xx->reset_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw882xx->reset_gpio, + GPIOF_OUT_INIT_LOW, "aw882xx_rst"); + if (ret) { + dev_err(&i2c->dev, "%s: rst request failed\n", + __func__); + goto err_reset_gpio_request; + } + } + + if (gpio_is_valid(aw882xx->irq_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, aw882xx->irq_gpio, + GPIOF_DIR_IN, "aw882xx_int"); + if (ret) { + dev_err(&i2c->dev, "%s: int request failed\n", + __func__); + goto err_irq_gpio_request; + } + } + + /* hardware reset */ + aw882xx_hw_reset(aw882xx); + + /* aw882xx chip id */ + ret = aw882xx_read_chipid(aw882xx); + if (ret < 0) { + dev_err(&i2c->dev, "%s: aw882xx_read_chipid failed ret=%d\n", + __func__, ret); + goto err_id; + } + + /* aw882xx device name */ + //if (i2c->dev.of_node) + // dev_set_name(&i2c->dev, "%s", "aw882xx_smartpa"); + //else + // dev_err(&i2c->dev, "%s failed to set device name: %d\n", + // __func__, ret); + + /* register codec */ + dai = devm_kzalloc(&i2c->dev, sizeof(aw882xx_dai), GFP_KERNEL); + if (!dai) + goto err_dai_kzalloc; + + memcpy(dai, aw882xx_dai, sizeof(aw882xx_dai)); + pr_info("%s: dai->name(%s)\n", __func__, dai->name); + + ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_aw882xx, + dai, ARRAY_SIZE(aw882xx_dai)); + if (ret < 0) { + dev_err(&i2c->dev, "%s failed to register aw882xx: %d\n", + __func__, ret); + goto err_register_codec; + } + + /* aw882xx irq */ + if (gpio_is_valid(aw882xx->irq_gpio) && + !(aw882xx->flags & AW882XX_FLAG_SKIP_INTERRUPTS)) { + aw882xx_interrupt_setup(aw882xx); + /* register irq handler */ + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, + gpio_to_irq(aw882xx->irq_gpio), + NULL, aw882xx_irq, irq_flags, + "aw882xx", aw882xx); + if (ret != 0) { + dev_err(&i2c->dev, "failed to request IRQ %d: %d\n", + gpio_to_irq(aw882xx->irq_gpio), ret); + goto err_irq; + } + } else { + dev_info(&i2c->dev, "%s skipping IRQ registration\n", __func__); + /* disable feature support if gpio was invalid */ + aw882xx->flags |= AW882XX_FLAG_SKIP_INTERRUPTS; + } + + dev_set_drvdata(&i2c->dev, aw882xx); + ret = sysfs_create_group(&i2c->dev.kobj, &aw882xx_attribute_group); + if (ret < 0) { + dev_info(&i2c->dev, + "%s error creating sysfs attr files\n", + __func__); + goto err_sysfs; + } + + init_aw882xx_monitor(&aw882xx->monitor); + + init_aw882xx_misc_driver(aw882xx); + g_aw882xx = aw882xx; + + /*init profile*/ + mutex_init(&aw882xx->profile.lock); + aw882xx->profile.cur_profile = 0; + aw882xx->profile.status = AW882XX_INIT_ST; + aw882xx->profile.len = 0; + for (i = 0; i < AW_PROFILE_MAX; i++) { + aw882xx->profile.data[i] = NULL; + } + pr_info("%s: probe completed successfully!\n", __func__); + + return 0; + +err_sysfs: + devm_free_irq(&i2c->dev, gpio_to_irq(aw882xx->irq_gpio), aw882xx); +err_irq: + snd_soc_unregister_codec(&i2c->dev); +err_register_codec: + devm_kfree(&i2c->dev, dai); + dai = NULL; +err_dai_kzalloc: +err_id: + if (gpio_is_valid(aw882xx->irq_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->irq_gpio); +err_irq_gpio_request: + if (gpio_is_valid(aw882xx->reset_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->reset_gpio); +err_reset_gpio_request: +err_parse_dt: + devm_kfree(&i2c->dev, aw882xx); + aw882xx = NULL; + g_aw882xx = NULL; + return ret; +} + +static int aw882xx_i2c_remove(struct i2c_client *i2c) +{ + struct aw882xx *aw882xx = i2c_get_clientdata(i2c); + + pr_info("%s: enter\n", __func__); + misc_deregister(&aw882xx_misc); + if (gpio_to_irq(aw882xx->irq_gpio)) + devm_free_irq(&i2c->dev, + gpio_to_irq(aw882xx->irq_gpio), + aw882xx); + + snd_soc_unregister_codec(&i2c->dev); + + if (gpio_is_valid(aw882xx->irq_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->irq_gpio); + if (gpio_is_valid(aw882xx->reset_gpio)) + devm_gpio_free(&i2c->dev, aw882xx->reset_gpio); + + devm_kfree(&i2c->dev, aw882xx); + aw882xx = NULL; + g_aw882xx = NULL; + return 0; +} + +static const struct i2c_device_id aw882xx_i2c_id[] = { + { AW882XX_I2C_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, aw882xx_i2c_id); + +static struct of_device_id aw882xx_dt_match[] = { + { .compatible = "awinic,aw882xx_smartpa" }, + { }, +}; + +static struct i2c_driver aw882xx_i2c_driver = { + .driver = { + .name = AW882XX_I2C_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(aw882xx_dt_match), + }, + .probe = aw882xx_i2c_probe, + .remove = aw882xx_i2c_remove, + .id_table = aw882xx_i2c_id, +}; + + +static int __init aw882xx_i2c_init(void) +{ + int ret = -1; + + pr_info("%s: aw882xx driver version %s\n", __func__, AW882XX_VERSION); + + ret = i2c_add_driver(&aw882xx_i2c_driver); + if (ret) + pr_err("%s: fail to add aw882xx device into i2c\n", __func__); + + return ret; +} +module_init(aw882xx_i2c_init); + + +static void __exit aw882xx_i2c_exit(void) +{ + i2c_del_driver(&aw882xx_i2c_driver); +} +module_exit(aw882xx_i2c_exit); + +MODULE_DESCRIPTION("ASoC AW882XX Smart PA Driver"); +MODULE_LICENSE("GPL v2"); + +#endif /* CONFIG_AW882XX_CODEC */ diff --git a/sound/soc/codecs/aw882xx.h b/sound/soc/codecs/aw882xx.h new file mode 100644 index 000000000000..0010f8f847b7 --- /dev/null +++ b/sound/soc/codecs/aw882xx.h @@ -0,0 +1,239 @@ +#ifndef _AW882XX_H_ +#define _AW882XX_H_ + +//#define AW_DEBUG +/* + * i2c transaction on Linux limited to 64k + * (See Linux kernel documentation: Documentation/i2c/writing-clients) +*/ +#define MAX_I2C_BUFFER_SIZE 65536 +#define AW882XX_I2C_READ_MSG_NUM 2 + +#define AW882XX_FLAG_START_ON_MUTE (1 << 0) +#define AW882XX_FLAG_SKIP_INTERRUPTS (1 << 1) +#define AW882XX_FLAG_SAAM_AVAILABLE (1 << 2) +#define AW882XX_FLAG_STEREO_DEVICE (1 << 3) +#define AW882XX_FLAG_MULTI_MIC_INPUTS (1 << 4) + +#define AW882XX_NUM_RATES 9 +#define AW882XX_SYSST_CHECK_MAX 10 + +#define AW882XX_MODE_SHIFT_MAX 2 + +#define AFE_RX_PROT_ID 0x1000 /*AFE_PORT_ID_PRIMARY_MI2S_RX*/ +#define AW_MODULE_ID_COPP (0X10013D02) /*SKT module id*/ +#define AW_MODULE_PARAMS_ID_COPP_ENABLE (0X10013D14) /*SKT enable param id*/ + + +#define DEFAULT_CALI_VALUE (7) +#define ERRO_CALI_VALUE (0) +#define AFE_PARAM_ID_AWDSP_RX_SET_ENABLE (0x10013D11) +#define AFE_PARAM_ID_AWDSP_RX_PARAMS (0x10013D12) +#define AFE_PARAM_ID_AWDSP_TX_SET_ENABLE (0x10013D13) +#define AFE_PARAM_ID_AWDSP_RX_VMAX_L (0X10013D17) +#define AFE_PARAM_ID_AWDSP_RX_VMAX_R (0X10013D18) +#define AFE_PARAM_ID_AWDSP_RX_CALI_CFG_L (0X10013D19) +#define AFE_PARAM_ID_AWDSP_RX_CALI_CFG_R (0x10013d1A) +#define AFE_PARAM_ID_AWDSP_RX_RE_L (0x10013d1B) +#define AFE_PARAM_ID_AWDSP_RX_RE_R (0X10013D1C) +#define AFE_PARAM_ID_AWDSP_RX_NOISE_L (0X10013D1D) +#define AFE_PARAM_ID_AWDSP_RX_NOISE_R (0X10013D1E) +#define AFE_PARAM_ID_AWDSP_RX_F0_L (0X10013D1F) +#define AFE_PARAM_ID_AWDSP_RX_F0_R (0X10013D20) +#define AFE_PARAM_ID_AWDSP_RX_REAL_DATA_L (0X10013D21) +#define AFE_PARAM_ID_AWDSP_RX_REAL_DATA_R (0X10013D22) + + +enum AWINIC_CALI_CMD{ + AW_CALI_CMD_NONE = 0, + AW_CALI_CMD_START, + AW_CALI_CMD_END, + AW_CALI_CMD_SET_CALI_CFG, + AW_CALI_CMD_GET_CALI_CFG, + AW_CALI_CMD_GET_CALI_DATA, + AW_CALI_CMD_SET_NOISE, + AW_CALI_CMD_GET_F0, + AW_CALI_CMD_SET_CALI_RE, + AW_CALI_CMD_GET_CALI_RE, + AW_CALI_CMD_SET_VMAX, + AW_CALI_CMD_GET_VMAX, + AW_CALI_CMD_MAX, +}; + +#define AW882XX_CALI_CFG_NUM 3 +#define AW882XX_CALI_DATA_NUM 6 +#define AW882XX_PARAMS_NUM 400 +struct cali_cfg{ + int32_t data[AW882XX_CALI_CFG_NUM]; +}; +struct cali_data{ + int32_t data[AW882XX_CALI_DATA_NUM]; +}; +struct params_data{ + int32_t data[AW882XX_PARAMS_NUM]; +}; + +#define AW882XX_IOCTL_MAGIC 'a' +#define AW882XX_IOCTL_SET_CALI_CFG _IOWR(AW882XX_IOCTL_MAGIC, 1, struct cali_cfg) +#define AW882XX_IOCTL_GET_CALI_CFG _IOWR(AW882XX_IOCTL_MAGIC, 2, struct cali_cfg) +#define AW882XX_IOCTL_GET_CALI_DATA _IOWR(AW882XX_IOCTL_MAGIC, 3, struct cali_data) +#define AW882XX_IOCTL_SET_NOISE _IOWR(AW882XX_IOCTL_MAGIC, 4, int32_t) +#define AW882XX_IOCTL_GET_F0 _IOWR(AW882XX_IOCTL_MAGIC, 5, int32_t) +#define AW882XX_IOCTL_SET_CALI_RE _IOWR(AW882XX_IOCTL_MAGIC, 6, int32_t) +#define AW882XX_IOCTL_GET_CALI_RE _IOWR(AW882XX_IOCTL_MAGIC, 7, int32_t) +#define AW882XX_IOCTL_SET_VMAX _IOWR(AW882XX_IOCTL_MAGIC, 8, int32_t) +#define AW882XX_IOCTL_GET_VMAX _IOWR(AW882XX_IOCTL_MAGIC, 9, int32_t) +#define AW882XX_IOCTL_SET_PARAM _IOWR(AW882XX_IOCTL_MAGIC, 10,struct params_data) +#define AW882XX_IOCTL_ENABLE_CALI _IOWR(AW882XX_IOCTL_MAGIC, 11,int8_t) + +enum aw882xx_init { + AW882XX_INIT_ST = 0, + AW882XX_INIT_OK = 1, + AW882XX_INIT_NG = 2, +}; + +enum aw882xx_chipid { + AW882XX_ID = 0x1852, +}; + +enum aw882xx_modeshift { + AW882XX_MODE_SPK_SHIFT = 0, + AW882XX_MODE_RCV_SHIFT = 1, +}; + +enum aw882xx_mode_spk_rcv { + AW882XX_SPEAKER_MODE = 0, + AW882XX_RECEIVER_MODE = 1, +}; +//smartpa monitor +enum aw882xx_ipeak { + IPEAK_3P50_A = 0x08, + IPEAK_3P00_A = 0x06, + IPEAK_2P75_A = 0x05, + IPEAK_2P50_A = 0x04, + IPEAK_NONE = 0xFF, +}; + +enum aw882xx_gain { + GAIN_NEG_0P0_DB = 0x00, + GAIN_NEG_0P5_DB = 0x01, + GAIN_NEG_1P0_DB = 0x02, + GAIN_NEG_1P5_DB = 0x03, + GAIN_NEG_3P0_DB = 0x06, + GAIN_NEG_4P5_DB = 0x09, + GAIN_NEG_6P0_DB = 0x10, + GAIN_NONE = 0xFF, +}; + +enum aw882xx_vmax_percentage { + VMAX_100_PERCENTAGE = 0x00000000, + VMAX_086_PERCENTAGE = 0xFFED714D, + VMAX_075_PERCENTAGE = 0xFFD80505, + VMAX_063_PERCENTAGE = 0xFFBEAE7E, + VMAX_NONE = 0xFFFFFFFF, +}; + +#define AW882XX_MONITOR_DEFAULT_FLAG 0 +#define AW882XX_MONITOR_SYSCTRL 0 +#define AW882XX_MONITOR_DEFAULT_TIMER_VAL 30000 +#define AW882XX_MONITOR_VBAT_RANGE 6025 +#define AW882XX_MONITOR_INT_10BIT 1023 +#define AW882XX_MONITOR_TEMP_SIGN_MASK (1<<9) +#define AW882XX_MONITOR_TEMP_NEG_MASK (0XFC00) +#define AW882XX_BIT_SYSCTRL2_BST_IPEAK_MASK ( 15<< 0) +#define AW882XX_BIT_HAGCCFG4_GAIN_SHIFT (8) +#define AW882XX_BIT_HAGCCFG4_GAIN_MASK (0x00ff) + +struct aw882xx_low_vol { + uint32_t vol; + uint8_t ipeak; + uint8_t gain; +}; + +struct aw882xx_low_temp { + int16_t temp; + uint8_t ipeak; + uint8_t gain; + uint32_t vmax; +}; + +struct aw882xx_monitor{ + struct hrtimer timer; + uint32_t timer_val; + struct work_struct work; + uint32_t is_enable; + uint32_t sysctrl; + uint16_t pre_vol; + int16_t pre_temp; +#ifdef AW_DEBUG + uint16_t test_vol; + int16_t test_temp; +#endif +}; + +enum AWINIC_PROFILE{ + AW_PROFILE_MUSIC = 0, + AW_PROFILE_RINGTONE, + AW_PROFILE_NOTIFICATION, + AW_PROFILE_VOICE, + AW_PROFILE_MAX, +}; + +#define VERSION_MAX 4 +#define PROJECT_NAME_MAX 24 +#define VOLUME_STEP_DB (6) +#define VOLUME_MIN_NEG_90_DB (90) +#define FADE_STEP_DB (6) + + +typedef struct awinic_afe_param_header{ + uint8_t fw[VERSION_MAX]; + uint8_t cfg[VERSION_MAX]; + uint8_t project[PROJECT_NAME_MAX]; + uint32_t start; + uint32_t params_len; + uint8_t check_sum; + uint8_t profile_num; + uint8_t reserve[2]; +}aw_afe_params_hdr_t; + +struct profile_info { + struct mutex lock; + int cur_profile; + int status; + int len; + char* data[AW_PROFILE_MAX]; +}; + +struct aw882xx { + struct regmap *regmap; + struct i2c_client *i2c; + struct snd_soc_codec *codec; + struct device *dev; + struct mutex lock; + + struct aw882xx_monitor monitor; + int sysclk; + int rate; + int pstream; + int cstream; + + int reset_gpio; + int irq_gpio; + + unsigned char reg_addr; + + unsigned int flags; + unsigned int chipid; + unsigned int init; + unsigned int spk_rcv_mode; + int32_t cali_re; + unsigned int cfg_num; + struct profile_info profile; +}; + +struct aw882xx_container { + int len; + unsigned char data[]; +}; +#endif diff --git a/sound/soc/codecs/aw882xx_reg.h b/sound/soc/codecs/aw882xx_reg.h new file mode 100644 index 000000000000..5b1c179211ac --- /dev/null +++ b/sound/soc/codecs/aw882xx_reg.h @@ -0,0 +1,1568 @@ +#ifndef __AW882XX_REG_H__ +#define __AW882XX_REG_H__ + +/* registers list */ +#define AW882XX_ID_REG (0x00) +#define AW882XX_SYSST_REG (0x01) +#define AW882XX_SYSINT_REG (0x02) +#define AW882XX_SYSINTM_REG (0x03) +#define AW882XX_SYSCTRL_REG (0x04) +#define AW882XX_SYSCTRL2_REG (0x05) +#define AW882XX_I2SCTRL_REG (0x06) +#define AW882XX_I2SCFG1_REG (0x07) +#define AW882XX_I2SCFG2_REG (0x08) +#define AW882XX_HAGCCFG1_REG (0x09) +#define AW882XX_HAGCCFG2_REG (0x0A) +#define AW882XX_HAGCCFG3_REG (0x0B) +#define AW882XX_HAGCCFG4_REG (0x0C) +#define AW882XX_HAGCCFG5_REG (0x0D) +#define AW882XX_HAGCCFG6_REG (0x0E) +#define AW882XX_HAGCCFG7_REG (0x0F) +#define AW882XX_HAGCST_REG (0x10) +#define AW882XX_PRODID_REG (0x11) +#define AW882XX_VBAT_REG (0x12) +#define AW882XX_TEMP_REG (0x13) +#define AW882XX_PVDD_REG (0x14) +#define AW882XX_DBGCTRL_REG (0x20) +#define AW882XX_I2SINT_REG (0x21) +#define AW882XX_I2SCAPCNT_REG (0x22) +#define AW882XX_CRCIN_REG (0x38) +#define AW882XX_CRCOUT_REG (0x39) +#define AW882XX_VSNCTRL1_REG (0x50) +#define AW882XX_ISNCTRL1_REG (0x52) +#define AW882XX_ISNCTRL2_REG (0x53) +#define AW882XX_VTMCTRL1_REG (0x54) +#define AW882XX_VTMCTRL2_REG (0x55) +#define AW882XX_VTMCTRL3_REG (0x56) +#define AW882XX_ISNDAT_REG (0x57) +#define AW882XX_VSNDAT_REG (0x58) +#define AW882XX_PWMCTRL_REG (0x59) +#define AW882XX_PWMCTRL2_REG (0x5A) +#define AW882XX_BSTCTRL1_REG (0x60) +#define AW882XX_BSTCTRL2_REG (0x61) +#define AW882XX_BSTCTRL3_REG (0x62) +#define AW882XX_BSTDBG1_REG (0x63) +#define AW882XX_BSTDBG2_REG (0x64) +#define AW882XX_BSTDBG3_REG (0x65) +#define AW882XX_PLLCTRL1_REG (0x66) +#define AW882XX_PLLCTRL2_REG (0x67) +#define AW882XX_PLLCTRL3_REG (0x68) +#define AW882XX_CDACTRL1_REG (0x69) +#define AW882XX_CDACTRL2_REG (0x6A) +#define AW882XX_SADCCTRL_REG (0x6B) +#define AW882XX_TESTCTRL1_REG (0x70) +#define AW882XX_TESTCTRL2_REG (0x71) +#define AW882XX_EFCTRL1_REG (0x72) +#define AW882XX_EFCTRL2_REG (0x73) +#define AW882XX_EFWH_REG (0x74) +#define AW882XX_EFWM2_REG (0x75) +#define AW882XX_EFWM1_REG (0x76) +#define AW882XX_EFWL_REG (0x77) +#define AW882XX_EFRH_REG (0x78) +#define AW882XX_EFRM2_REG (0x79) +#define AW882XX_EFRM1_REG (0x7A) +#define AW882XX_EFRL_REG (0x7B) +#define AW882XX_TESTDET_REG (0x7C) + +/******************************************** + * Register Access + *******************************************/ +#define AW882XX_REG_MAX (0x7D) + +#define REG_NONE_ACCESS (0) +#define REG_RD_ACCESS (1 << 0) +#define REG_WR_ACCESS (1 << 1) + +const unsigned char aw882xx_reg_access[AW882XX_REG_MAX] = { + [AW882XX_ID_REG] = (REG_RD_ACCESS), + [AW882XX_SYSST_REG] = (REG_RD_ACCESS), + [AW882XX_SYSINT_REG] = (REG_RD_ACCESS), + [AW882XX_SYSINTM_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_SYSCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_SYSCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_I2SCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_I2SCFG1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_I2SCFG2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG4_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG5_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG6_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCCFG7_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_HAGCST_REG] = (REG_RD_ACCESS), + [AW882XX_PRODID_REG] = (REG_RD_ACCESS), + [AW882XX_VBAT_REG] = (REG_RD_ACCESS), + [AW882XX_TEMP_REG] = (REG_RD_ACCESS), + [AW882XX_PVDD_REG] = (REG_RD_ACCESS), + [AW882XX_DBGCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_I2SINT_REG] = (REG_RD_ACCESS), + [AW882XX_I2SCAPCNT_REG] = (REG_RD_ACCESS), + [AW882XX_CRCIN_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_CRCOUT_REG] = (REG_RD_ACCESS), + [AW882XX_VSNCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_ISNCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_ISNCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_VTMCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_VTMCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_VTMCTRL3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_ISNDAT_REG] = (REG_RD_ACCESS), + [AW882XX_VSNDAT_REG] = (REG_RD_ACCESS), + [AW882XX_PWMCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_PWMCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTCTRL3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTDBG1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTDBG2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_BSTDBG3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_PLLCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_PLLCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_PLLCTRL3_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_CDACTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_CDACTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_SADCCTRL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_TESTCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_TESTCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFCTRL1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFCTRL2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFWH_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFWM2_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFWM1_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFWL_REG] = (REG_RD_ACCESS | REG_WR_ACCESS), + [AW882XX_EFRH_REG] = (REG_RD_ACCESS), + [AW882XX_EFRM2_REG] = (REG_RD_ACCESS), + [AW882XX_EFRM1_REG] = (REG_RD_ACCESS), + [AW882XX_EFRL_REG] = (REG_RD_ACCESS), + [AW882XX_TESTDET_REG] = (REG_RD_ACCESS), +}; + +/******************************************** + * Register Detail + *******************************************/ +/* detail information of registers begin */ +/* ID (0x00) detail */ +/* IDCODE bit 15:0 (ID 0x00) */ +#define AW882XX_IDCODE_START_BIT (0) +#define AW882XX_IDCODE_BITS_LEN (16) +#define AW882XX_IDCODE_MASK \ + (~(((1< +#include +#include +#include +#include + +#define _ASSERT(e) +#define PRINT_ASSERT(e)if ((e)) printk(KERN_ERR "PrintAssert:%s (%s:%d) error code:%d\n",__FUNCTION__,__FILE__,__LINE__, e) + +#if defined(CONFIG_TRACING) && defined(DEBUG) + #define tfa98xx_trace_printk(...) trace_printk(__VA_ARGS__) +#else + #define tfa98xx_trace_printk(...) +#endif + +#endif /* __CONFIG_LINUX_KERNEL_INC__ */ + diff --git a/sound/soc/codecs/inc/dbgprint.h b/sound/soc/codecs/inc/dbgprint.h new file mode 100644 index 000000000000..c30157c84e73 --- /dev/null +++ b/sound/soc/codecs/inc/dbgprint.h @@ -0,0 +1,165 @@ +/* + * Copyright 2014-2017 NXP Semiconductors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DBGPRINT_H +# define _DBGPRINT_H + +/* Debugging macro's. */ +# ifndef DEBUG +# define DEBUG +# endif + +# ifndef ASSERT +//#define ASSERT +# endif + //TODO wwwim +# ifndef _ASSERT + #define _ASSERT(e) +# endif + +# ifndef PREFIX +# define PREFIX "tfa98xx: " +# define DRIVER_NAME "tfa98xx" +# endif + +#ifdef __KERNEL__ + +# ifdef DEBUG +# define _DEBUG(level,fmt,va...) do {\ + if (unlikely(debug >= (level))) \ + printk(KERN_INFO PREFIX "%s:%d: "fmt,__func__,__LINE__,##va); \ + } while (0) + +# else +# define _DEBUG(level,fmt,va...) do {} while(0) +# endif + +# define MSG(fmt,va...) printk(KERN_INFO PREFIX "%s:%d: "fmt,__func__,__LINE__,##va) +# define _ERRORMSG(fmt,va...) printk(KERN_ERR PREFIX "ERROR %s:%d: "fmt,__func__,__LINE__, ##va) + + +# define DEBUG0(x...) MSG(x) +# define DEBUG1(x...) _DEBUG(1,x) +# define DEBUG2(x...) _DEBUG(2,x) +# define DEBUG3(x...) _DEBUG(3,x) +# define ERRORMSG(x...) _ERRORMSG(x) +# define PRINT(x...) printk(x) +# define PRINT_ERROR(x...) printk(KERN_INFO PREFIX " **ERROR** " x) +# define PRINT_ASSERT(e)if ((e)) printk(KERN_ERR "PrintAssert:%s (%s:%d) error code:%d\n",__FUNCTION__,__FILE__,__LINE__, e) + +# define PRINT_ENTRY DEBUG2("+[%s]\n", __func__) +# define PRINT_EXIT DEBUG2("-[%s]\n", __func__) + +# ifdef ASSERT +# define assert(cond,action) do { if (unlikely(!(cond))) { DEBUG0("Assert: %s\n",#cond); action; }} while(0) +# else +# define assert(cond,action) do { } while (0) +# endif + +#else /* __KERNEL__ */ +#if defined(WIN32) || defined(_X64) +#include +/* user mode */ +# ifdef DEBUG +# define _DEBUGMSG(level,fmt,...) printf(PREFIX "%s:%d: "fmt,__FUNCTION__,__LINE__,__VA_ARGS__); +# else +# define _DEBUGMSG(level,fmt,...) do {} while(0) +# endif + +# define _ERRORMSG(fmt,...) printf(PREFIX "%s:%s:%d: "fmt,__FILE__,__FUNCTION__,__LINE__,__VA_ARGS__) + +# define DEBUG0(...) MSG(__VA_ARGS__) +# define DEBUG1(...) _DEBUGMSG(1,__VA_ARGS__) +# define DEBUG2(...) _DEBUGMSG(2,__VA_ARGS__) +# define DEBUG3(...) _DEBUGMSG(3,__VA_ARGS__) +# define ERRORMSG(fmt,...) _ERRORMSG(fmt,__VA_ARGS__) +# define PRINT(...) printf(__VA_ARGS__) +/* +# define PRINT(...) { FILE *stream; \ + if((stream = freopen("nxp_tfa.txt", "ab+", stdout)) == NULL) exit(-1); \ + printf(__VA_ARGS__); \ + freopen( "CON", "ab+", stdout ); \ + } +*/ +# define PRINT_ERROR(...) fprintf(stderr,__VA_ARGS__) +# define PRINT_FILE(file,...) fprintf(file,__VA_ARGS__) +# define PRINT_ASSERT(e)if ((e)) fprintf(stderr, "PrintAssert:%s (%s:%d) error code:%d\n",__FUNCTION__,__FILE__,__LINE__, e) +//# define PRINT_ASSERT(e) if ((e)) fprintf(stderr, "PrintAssert:%s (%s:%d) %s\n",__FUNCTION__,__FILE__,__LINE__, Tfa98xx_GetErrorString(e)) + +#elif defined(__CODE_RED) +#include "app_global.h" +# ifdef DEBUG +# define _DEBUG(level,fmt,va...) TB_TRACE_INF(TbTracePfx2("tfa",TB_FUNC,va)) +//printf(PREFIX "%s:%d: "fmt,__func__,__LINE__,##va); +# else +# define _DEBUG(level,fmt,va...) do {} while(0) +# endif + +# define MSG(fmt,...) TB_TRACE_INF(TbTracePfx2("tfa",TB_FUNC,__VA_ARGS__)) +//printf(PREFIX "%s:%s:%d: "fmt,__FILE__,__func__,__LINE__,##va) +//TB_TRACE_INF(TbTracePfx2(APP_PFX,TB_FUNC,"path=%s, chan=%u, muted=%s, vol=%d\n", +// path->isRecording ? "recording" : "playback", +// i, +// channelVol.currentMuteValue ? "YES" : "NO", +// channelVol.currentVolumeValue +// )); +//# define _ERRORMSG(fmt,va...) TB_TRACE_INF(TbTracePfx2("tfa",TB_FUNC,va)) +# define ERRORMSG(...) TB_TRACE_INF(TbTracePfx2("tfa",TB_FUNC,__VA_ARGS__)) +//fprintf(stderr, PREFIX "ERROR %s:%s:%d: "fmt,__FILE__,__func__,__LINE__, ##va) + +# define DEBUG0(x...) MSG(x) +# define DEBUG1(x...) _DEBUG(1,x) +# define DEBUG2(x...) _DEBUG(2,x) +# define DEBUG3(x...) _DEBUG(3,x) +//# define ERRORMSG(x...) _ERRORMSG(x) +# define PRINT(x...) TB_TRACE_INF(TbTracePfx2("tfa",TB_FUNC,x)) +//printf(x) +# define PRINT_ERROR(x...) TB_TRACE_INF(TbTracePfx2("tfa",TB_FUNC,x)) +//fprintf(stderr,__VA_ARGS__) +# define PRINT_FILE(file,x...) TB_TRACE_INF(TbTracePfx2("tfa",TB_FUNC,x)) +//fprintf(file,__VA_ARGS__) +# define PRINT_ASSERT(e) +//TB_TRACE_INF(TbTracePfx2("tfa",TB_FUNC,Tfa98xx_GetErrorString(e))) +//if ((e)) fprintf(stderr, "PrintAssert:%s (%s:%d) %s\n",__FUNCTION__,__FILE__,__LINE__, Tfa98xx_GetErrorString(e)) +#else +#include +/* user mode */ +# ifdef DEBUG +# define _DEBUG(level,fmt,va...) printf(PREFIX "%s:%d: "fmt,__func__,__LINE__,##va); +# else +# define _DEBUG(level,fmt,va...) do {} while(0) +# endif + +# define MSG(fmt,va...) printf(PREFIX "%s:%s:%d: "fmt,__FILE__,__func__,__LINE__,##va) +# define _ERRORMSG(fmt,va...) fprintf(stderr, PREFIX "ERROR %s:%s:%d: "fmt,__FILE__,__func__,__LINE__, ##va) + +# define DEBUG0(x...) MSG(x) +# define DEBUG1(x...) _DEBUG(1,x) +# define DEBUG2(x...) _DEBUG(2,x) +# define DEBUG3(x...) _DEBUG(3,x) +# define ERRORMSG(x...) _ERRORMSG(x) +# define PRINT(x...) printf(x) +# define PRINT_ERROR(...) fprintf(stderr,__VA_ARGS__) +# define PRINT_FILE(file,...) fprintf(file,__VA_ARGS__) +# define PRINT_ASSERT(e)if ((e)) fprintf(stderr, "PrintAssert:%s (%s:%d) error code:%d\n",__FUNCTION__,__FILE__,__LINE__, e) +//# define PRINT_ASSERT(e) if ((e)) fprintf(stderr, "PrintAssert:%s (%s:%d) %s\n",__FUNCTION__,__FILE__,__LINE__, Tfa98xx_GetErrorString(e)) + + +#endif /* WIN32 */ + +#endif /* user */ + +#endif /* _DBGPRINT_H --------------- */ diff --git a/sound/soc/codecs/inc/tfa.h b/sound/soc/codecs/inc/tfa.h new file mode 100644 index 000000000000..2cf59f107e89 --- /dev/null +++ b/sound/soc/codecs/inc/tfa.h @@ -0,0 +1,49 @@ +/* + * Copyright 2015-2017 NXP Semiconductors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TFA_H_ +#define TFA_H_ + +/* set the limit for the container file length */ +#define TFA_MAX_CNT_LENGTH (256*1024) + +extern struct tfa_device **devs; + +/** + * tfa error return codes + */ +enum tfa_error { + tfa_error_ok, /**< no error */ + tfa_error_device, /**< no response from device */ + tfa_error_bad_param,/**< parameter no accepted */ + tfa_error_noclock, /**< required clock not present */ + tfa_error_timeout, /**< a timeout occurred */ + tfa_error_dsp, /**< a DSP error was returned */ + tfa_error_container,/**< no or wrong container file */ + tfa_error_max /**< impossible value, max enum */ +}; + +enum Tfa98xx_Error tfa_write_filters(struct tfa_device *tfa, int prof_idx); + +struct tfa_device ** tfa_devs_create(int count); +void tfa_devs_destroy(int count); + +struct tfa_device ** tfa_get_device_struct(void); + +int tfa_plop_noise_interrupt(struct tfa_device *tfa, int profile, int vstep); +void tfa_lp_mode_interrupt(struct tfa_device *tfa); + +#endif /* TFA_H_ */ diff --git a/sound/soc/codecs/inc/tfa1_tfafieldnames.h b/sound/soc/codecs/inc/tfa1_tfafieldnames.h new file mode 100644 index 000000000000..f83156ea9e11 --- /dev/null +++ b/sound/soc/codecs/inc/tfa1_tfafieldnames.h @@ -0,0 +1,900 @@ +/** Filename: Tfa1_TfaFieldnames.h + * This file was generated automatically on 03/20/2015 at 01:55:46 PM. + * Source file: TFA9897N1B_I2C_list_URT_Source_v34.xls + */ +#define TFA9897_I2CVERSION 34 +typedef enum nxpTfa1BfEnumList { + TFA1_BF_VDDS = 0x0000, /*!< Power-on-reset flag */ + TFA1_BF_PLLS = 0x0010, /*!< PLL lock */ + TFA1_BF_OTDS = 0x0020, /*!< Over Temperature Protection alarm */ + TFA1_BF_OVDS = 0x0030, /*!< Over Voltage Protection alarm */ + TFA1_BF_UVDS = 0x0040, /*!< Under Voltage Protection alarm */ + TFA1_BF_OCDS = 0x0050, /*!< Over Current Protection alarm */ + TFA1_BF_CLKS = 0x0060, /*!< Clocks stable flag */ + TFA1_BF_CLIPS = 0x0070, /*!< Amplifier clipping */ + TFA1_BF_MTPB = 0x0080, /*!< MTP busy */ + TFA1_BF_NOCLK = 0x0090, /*!< Flag lost clock from clock generation unit */ + TFA1_BF_SPKS = 0x00a0, /*!< Speaker error flag */ + TFA1_BF_ACS = 0x00b0, /*!< Cold Start flag */ + TFA1_BF_SWS = 0x00c0, /*!< Flag Engage */ + TFA1_BF_WDS = 0x00d0, /*!< Flag watchdog reset */ + TFA1_BF_AMPS = 0x00e0, /*!< Amplifier is enabled by manager */ + TFA1_BF_AREFS = 0x00f0, /*!< References are enabled by manager */ + TFA1_BF_BATS = 0x0109, /*!< Battery voltage readout; 0 .. 5.5 [V] */ + TFA1_BF_TEMPS = 0x0208, /*!< Temperature readout from the temperature sensor */ + TFA1_BF_REV = 0x030b, /*!< Device type number is B97 */ + TFA1_BF_RCV = 0x0420, /*!< Enable Receiver Mode */ + TFA1_BF_CHS12 = 0x0431, /*!< Channel Selection TDM input for Coolflux */ + TFA1_BF_INPLVL= 0x0450, /*!< Input level selection control */ + TFA1_BF_CHSA = 0x0461, /*!< Input selection for amplifier */ + TFA1_BF_I2SDOE= 0x04b0, /*!< Enable data output */ + TFA1_BF_AUDFS = 0x04c3, /*!< Audio sample rate setting */ + TFA1_BF_BSSCR = 0x0501, /*!< Protection Attack Time */ + TFA1_BF_BSST = 0x0523, /*!< ProtectionThreshold */ + TFA1_BF_BSSRL = 0x0561, /*!< Protection Maximum Reduction */ + TFA1_BF_BSSRR = 0x0582, /*!< Battery Protection Release Time */ + TFA1_BF_BSSHY = 0x05b1, /*!< Battery Protection Hysteresis */ + TFA1_BF_BSSR = 0x05e0, /*!< battery voltage for I2C read out only */ + TFA1_BF_BSSBY = 0x05f0, /*!< bypass clipper battery protection */ + TFA1_BF_DPSA = 0x0600, /*!< Enable dynamic powerstage activation */ + TFA1_BF_CFSM = 0x0650, /*!< Soft mute in CoolFlux */ + TFA1_BF_BSSS = 0x0670, /*!< BatSenseSteepness */ + TFA1_BF_VOL = 0x0687, /*!< volume control (in CoolFlux) */ + TFA1_BF_DCVO = 0x0702, /*!< Boost Voltage */ + TFA1_BF_DCMCC = 0x0733, /*!< Max boost coil current - step of 175 mA */ + TFA1_BF_DCIE = 0x07a0, /*!< Adaptive boost mode */ + TFA1_BF_DCSR = 0x07b0, /*!< Soft RampUp/Down mode for DCDC controller */ + TFA1_BF_DCPAVG= 0x07c0, /*!< ctrl_peak2avg for analog part of DCDC */ + TFA1_BF_TROS = 0x0800, /*!< Select external temperature also the ext_temp will be put on the temp read out */ + TFA1_BF_EXTTS = 0x0818, /*!< external temperature setting to be given by host */ + TFA1_BF_PWDN = 0x0900, /*!< Device Mode */ + TFA1_BF_I2CR = 0x0910, /*!< I2C Reset */ + TFA1_BF_CFE = 0x0920, /*!< Enable CoolFlux */ + TFA1_BF_AMPE = 0x0930, /*!< Enable Amplifier */ + TFA1_BF_DCA = 0x0940, /*!< EnableBoost */ + TFA1_BF_SBSL = 0x0950, /*!< Coolflux configured */ + TFA1_BF_AMPC = 0x0960, /*!< Selection on how Amplifier is enabled */ + TFA1_BF_DCDIS = 0x0970, /*!< DCDC not connected */ + TFA1_BF_PSDR = 0x0980, /*!< IDDQ test amplifier */ + TFA1_BF_DCCV = 0x0991, /*!< Coil Value */ + TFA1_BF_CCFD = 0x09b0, /*!< Selection CoolFlux Clock */ + TFA1_BF_INTPAD= 0x09c1, /*!< INT pad configuration control */ + TFA1_BF_IPLL = 0x09e0, /*!< PLL input reference clock selection */ + TFA1_BF_MTPK = 0x0b07, /*!< 5Ah, 90d To access KEY1_Protected registers (Default for engineering) */ + TFA1_BF_CVFDLY= 0x0c25, /*!< Fractional delay adjustment between current and voltage sense */ + TFA1_BF_TDMPRF= 0x1011, /*!< TDM_usecase */ + TFA1_BF_TDMEN = 0x1030, /*!< TDM interface control */ + TFA1_BF_TDMCKINV= 0x1040, /*!< TDM clock inversion */ + TFA1_BF_TDMFSLN= 0x1053, /*!< TDM FS length */ + TFA1_BF_TDMFSPOL= 0x1090, /*!< TDM FS polarity */ + TFA1_BF_TDMSAMSZ= 0x10a4, /*!< TDM Sample Size for all tdm sinks/sources */ + TFA1_BF_TDMSLOTS= 0x1103, /*!< Number of slots */ + TFA1_BF_TDMSLLN= 0x1144, /*!< Slot length */ + TFA1_BF_TDMBRMG= 0x1194, /*!< Bits remaining */ + TFA1_BF_TDMDDEL= 0x11e0, /*!< Data delay */ + TFA1_BF_TDMDADJ= 0x11f0, /*!< Data adjustment */ + TFA1_BF_TDMTXFRM= 0x1201, /*!< TXDATA format */ + TFA1_BF_TDMUUS0= 0x1221, /*!< TXDATA format unused slot sd0 */ + TFA1_BF_TDMUUS1= 0x1241, /*!< TXDATA format unused slot sd1 */ + TFA1_BF_TDMSI0EN= 0x1270, /*!< TDM sink0 enable */ + TFA1_BF_TDMSI1EN= 0x1280, /*!< TDM sink1 enable */ + TFA1_BF_TDMSI2EN= 0x1290, /*!< TDM sink2 enable */ + TFA1_BF_TDMSO0EN= 0x12a0, /*!< TDM source0 enable */ + TFA1_BF_TDMSO1EN= 0x12b0, /*!< TDM source1 enable */ + TFA1_BF_TDMSO2EN= 0x12c0, /*!< TDM source2 enable */ + TFA1_BF_TDMSI0IO= 0x12d0, /*!< tdm_sink0_io */ + TFA1_BF_TDMSI1IO= 0x12e0, /*!< tdm_sink1_io */ + TFA1_BF_TDMSI2IO= 0x12f0, /*!< tdm_sink2_io */ + TFA1_BF_TDMSO0IO= 0x1300, /*!< tdm_source0_io */ + TFA1_BF_TDMSO1IO= 0x1310, /*!< tdm_source1_io */ + TFA1_BF_TDMSO2IO= 0x1320, /*!< tdm_source2_io */ + TFA1_BF_TDMSI0SL= 0x1333, /*!< sink0_slot [GAIN IN] */ + TFA1_BF_TDMSI1SL= 0x1373, /*!< sink1_slot [CH1 IN] */ + TFA1_BF_TDMSI2SL= 0x13b3, /*!< sink2_slot [CH2 IN] */ + TFA1_BF_TDMSO0SL= 0x1403, /*!< source0_slot [GAIN OUT] */ + TFA1_BF_TDMSO1SL= 0x1443, /*!< source1_slot [Voltage Sense] */ + TFA1_BF_TDMSO2SL= 0x1483, /*!< source2_slot [Current Sense] */ + TFA1_BF_NBCK = 0x14c3, /*!< NBCK */ + TFA1_BF_INTOVDDS= 0x2000, /*!< flag_por_int_out */ + TFA1_BF_INTOPLLS= 0x2010, /*!< flag_pll_lock_int_out */ + TFA1_BF_INTOOTDS= 0x2020, /*!< flag_otpok_int_out */ + TFA1_BF_INTOOVDS= 0x2030, /*!< flag_ovpok_int_out */ + TFA1_BF_INTOUVDS= 0x2040, /*!< flag_uvpok_int_out */ + TFA1_BF_INTOOCDS= 0x2050, /*!< flag_ocp_alarm_int_out */ + TFA1_BF_INTOCLKS= 0x2060, /*!< flag_clocks_stable_int_out */ + TFA1_BF_INTOCLIPS= 0x2070, /*!< flag_clip_int_out */ + TFA1_BF_INTOMTPB= 0x2080, /*!< mtp_busy_int_out */ + TFA1_BF_INTONOCLK= 0x2090, /*!< flag_lost_clk_int_out */ + TFA1_BF_INTOSPKS= 0x20a0, /*!< flag_cf_speakererror_int_out */ + TFA1_BF_INTOACS= 0x20b0, /*!< flag_cold_started_int_out */ + TFA1_BF_INTOSWS= 0x20c0, /*!< flag_engage_int_out */ + TFA1_BF_INTOWDS= 0x20d0, /*!< flag_watchdog_reset_int_out */ + TFA1_BF_INTOAMPS= 0x20e0, /*!< flag_enbl_amp_int_out */ + TFA1_BF_INTOAREFS= 0x20f0, /*!< flag_enbl_ref_int_out */ + TFA1_BF_INTOACK= 0x2201, /*!< Interrupt status register output - Corresponding flag */ + TFA1_BF_INTIVDDS= 0x2300, /*!< flag_por_int_in */ + TFA1_BF_INTIPLLS= 0x2310, /*!< flag_pll_lock_int_in */ + TFA1_BF_INTIOTDS= 0x2320, /*!< flag_otpok_int_in */ + TFA1_BF_INTIOVDS= 0x2330, /*!< flag_ovpok_int_in */ + TFA1_BF_INTIUVDS= 0x2340, /*!< flag_uvpok_int_in */ + TFA1_BF_INTIOCDS= 0x2350, /*!< flag_ocp_alarm_int_in */ + TFA1_BF_INTICLKS= 0x2360, /*!< flag_clocks_stable_int_in */ + TFA1_BF_INTICLIPS= 0x2370, /*!< flag_clip_int_in */ + TFA1_BF_INTIMTPB= 0x2380, /*!< mtp_busy_int_in */ + TFA1_BF_INTINOCLK= 0x2390, /*!< flag_lost_clk_int_in */ + TFA1_BF_INTISPKS= 0x23a0, /*!< flag_cf_speakererror_int_in */ + TFA1_BF_INTIACS= 0x23b0, /*!< flag_cold_started_int_in */ + TFA1_BF_INTISWS= 0x23c0, /*!< flag_engage_int_in */ + TFA1_BF_INTIWDS= 0x23d0, /*!< flag_watchdog_reset_int_in */ + TFA1_BF_INTIAMPS= 0x23e0, /*!< flag_enbl_amp_int_in */ + TFA1_BF_INTIAREFS= 0x23f0, /*!< flag_enbl_ref_int_in */ + TFA1_BF_INTIACK= 0x2501, /*!< Interrupt register input */ + TFA1_BF_INTENVDDS= 0x2600, /*!< flag_por_int_enable */ + TFA1_BF_INTENPLLS= 0x2610, /*!< flag_pll_lock_int_enable */ + TFA1_BF_INTENOTDS= 0x2620, /*!< flag_otpok_int_enable */ + TFA1_BF_INTENOVDS= 0x2630, /*!< flag_ovpok_int_enable */ + TFA1_BF_INTENUVDS= 0x2640, /*!< flag_uvpok_int_enable */ + TFA1_BF_INTENOCDS= 0x2650, /*!< flag_ocp_alarm_int_enable */ + TFA1_BF_INTENCLKS= 0x2660, /*!< flag_clocks_stable_int_enable */ + TFA1_BF_INTENCLIPS= 0x2670, /*!< flag_clip_int_enable */ + TFA1_BF_INTENMTPB= 0x2680, /*!< mtp_busy_int_enable */ + TFA1_BF_INTENNOCLK= 0x2690, /*!< flag_lost_clk_int_enable */ + TFA1_BF_INTENSPKS= 0x26a0, /*!< flag_cf_speakererror_int_enable */ + TFA1_BF_INTENACS= 0x26b0, /*!< flag_cold_started_int_enable */ + TFA1_BF_INTENSWS= 0x26c0, /*!< flag_engage_int_enable */ + TFA1_BF_INTENWDS= 0x26d0, /*!< flag_watchdog_reset_int_enable */ + TFA1_BF_INTENAMPS= 0x26e0, /*!< flag_enbl_amp_int_enable */ + TFA1_BF_INTENAREFS= 0x26f0, /*!< flag_enbl_ref_int_enable */ + TFA1_BF_INTENACK= 0x2801, /*!< Interrupt enable register */ + TFA1_BF_INTPOLVDDS= 0x2900, /*!< flag_por_int_pol */ + TFA1_BF_INTPOLPLLS= 0x2910, /*!< flag_pll_lock_int_pol */ + TFA1_BF_INTPOLOTDS= 0x2920, /*!< flag_otpok_int_pol */ + TFA1_BF_INTPOLOVDS= 0x2930, /*!< flag_ovpok_int_pol */ + TFA1_BF_INTPOLUVDS= 0x2940, /*!< flag_uvpok_int_pol */ + TFA1_BF_INTPOLOCDS= 0x2950, /*!< flag_ocp_alarm_int_pol */ + TFA1_BF_INTPOLCLKS= 0x2960, /*!< flag_clocks_stable_int_pol */ + TFA1_BF_INTPOLCLIPS= 0x2970, /*!< flag_clip_int_pol */ + TFA1_BF_INTPOLMTPB= 0x2980, /*!< mtp_busy_int_pol */ + TFA1_BF_INTPOLNOCLK= 0x2990, /*!< flag_lost_clk_int_pol */ + TFA1_BF_INTPOLSPKS= 0x29a0, /*!< flag_cf_speakererror_int_pol */ + TFA1_BF_INTPOLACS= 0x29b0, /*!< flag_cold_started_int_pol */ + TFA1_BF_INTPOLSWS= 0x29c0, /*!< flag_engage_int_pol */ + TFA1_BF_INTPOLWDS= 0x29d0, /*!< flag_watchdog_reset_int_pol */ + TFA1_BF_INTPOLAMPS= 0x29e0, /*!< flag_enbl_amp_int_pol */ + TFA1_BF_INTPOLAREFS= 0x29f0, /*!< flag_enbl_ref_int_pol */ + TFA1_BF_INTPOLACK= 0x2b01, /*!< Interrupt status flags polarity register */ + TFA1_BF_CLIP = 0x4900, /*!< Bypass clip control */ + TFA1_BF_CIMTP = 0x62b0, /*!< start copying all the data from i2cregs_mtp to mtp [Key 2 protected] */ + TFA1_BF_RST = 0x7000, /*!< Reset CoolFlux DSP */ + TFA1_BF_DMEM = 0x7011, /*!< Target memory for access */ + TFA1_BF_AIF = 0x7030, /*!< Autoincrement-flag for memory-address */ + TFA1_BF_CFINT = 0x7040, /*!< Interrupt CoolFlux DSP */ + TFA1_BF_REQ = 0x7087, /*!< request for access (8 channels) */ + TFA1_BF_REQCMD= 0x7080, /*!< Firmware event request rpc command */ + TFA1_BF_REQRST= 0x7090, /*!< Firmware event request reset restart */ + TFA1_BF_REQMIPS= 0x70a0, /*!< Firmware event request short on mips */ + TFA1_BF_REQMUTED= 0x70b0, /*!< Firmware event request mute sequence ready */ + TFA1_BF_REQVOL= 0x70c0, /*!< Firmware event request volume ready */ + TFA1_BF_REQDMG= 0x70d0, /*!< Firmware event request speaker damage detected */ + TFA1_BF_REQCAL= 0x70e0, /*!< Firmware event request calibration completed */ + TFA1_BF_REQRSV= 0x70f0, /*!< Firmware event request reserved */ + TFA1_BF_MADD = 0x710f, /*!< memory-address to be accessed */ + TFA1_BF_MEMA = 0x720f, /*!< activate memory access (24- or 32-bits data is written/read to/from memory */ + TFA1_BF_ERR = 0x7307, /*!< Coolflux error flags */ + TFA1_BF_ACK = 0x7387, /*!< acknowledge of requests (8 channels) */ + TFA1_BF_MTPOTC= 0x8000, /*!< Calibration schedule (key2 protected) */ + TFA1_BF_MTPEX = 0x8010, /*!< (key2 protected) */ +} nxpTfa1BfEnumList_t; +#define TFA1_NAMETABLE static tfaBfName_t Tfa1DatasheetNames[]= {\ + { 0x0, "VDDS"}, /* Power-on-reset flag , */\ + { 0x10, "PLLS"}, /* PLL lock , */\ + { 0x20, "OTDS"}, /* Over Temperature Protection alarm , */\ + { 0x30, "OVDS"}, /* Over Voltage Protection alarm , */\ + { 0x40, "UVDS"}, /* Under Voltage Protection alarm , */\ + { 0x50, "OCDS"}, /* Over Current Protection alarm , */\ + { 0x60, "CLKS"}, /* Clocks stable flag , */\ + { 0x70, "CLIPS"}, /* Amplifier clipping , */\ + { 0x80, "MTPB"}, /* MTP busy , */\ + { 0x90, "NOCLK"}, /* Flag lost clock from clock generation unit , */\ + { 0xa0, "SPKS"}, /* Speaker error flag , */\ + { 0xb0, "ACS"}, /* Cold Start flag , */\ + { 0xc0, "SWS"}, /* Flag Engage , */\ + { 0xd0, "WDS"}, /* Flag watchdog reset , */\ + { 0xe0, "AMPS"}, /* Amplifier is enabled by manager , */\ + { 0xf0, "AREFS"}, /* References are enabled by manager , */\ + { 0x109, "BATS"}, /* Battery voltage readout; 0 .. 5.5 [V] , */\ + { 0x208, "TEMPS"}, /* Temperature readout from the temperature sensor , */\ + { 0x30b, "REV"}, /* Device type number is B97 , */\ + { 0x420, "RCV"}, /* Enable Receiver Mode , */\ + { 0x431, "CHS12"}, /* Channel Selection TDM input for Coolflux , */\ + { 0x450, "INPLVL"}, /* Input level selection control , */\ + { 0x461, "CHSA"}, /* Input selection for amplifier , */\ + { 0x4b0, "I2SDOE"}, /* Enable data output , */\ + { 0x4c3, "AUDFS"}, /* Audio sample rate setting , */\ + { 0x501, "SSCR"}, /* Protection Attack Time , */\ + { 0x523, "SST"}, /* ProtectionThreshold , */\ + { 0x561, "SSRL"}, /* Protection Maximum Reduction , */\ + { 0x582, "SSRR"}, /* Battery Protection Release Time , */\ + { 0x5b1, "SSHY"}, /* Battery Protection Hysteresis , */\ + { 0x5e0, "SSR"}, /* battery voltage for I2C read out only , */\ + { 0x5f0, "SSBY"}, /* bypass clipper battery protection , */\ + { 0x600, "DPSA"}, /* Enable dynamic powerstage activation , */\ + { 0x650, "CFSM"}, /* Soft mute in CoolFlux , */\ + { 0x670, "SSS"}, /* BatSenseSteepness , */\ + { 0x687, "VOL"}, /* volume control (in CoolFlux) , */\ + { 0x702, "DCVO"}, /* Boost Voltage , */\ + { 0x733, "DCMCC"}, /* Max boost coil current - step of 175 mA , */\ + { 0x7a0, "DCIE"}, /* Adaptive boost mode , */\ + { 0x7b0, "DCSR"}, /* Soft RampUp/Down mode for DCDC controller , */\ + { 0x7c0, "DCPAVG"}, /* ctrl_peak2avg for analog part of DCDC , */\ + { 0x800, "TROS"}, /* Select external temperature also the ext_temp will be put on the temp read out , */\ + { 0x818, "EXTTS"}, /* external temperature setting to be given by host , */\ + { 0x900, "PWDN"}, /* Device Mode , */\ + { 0x910, "I2CR"}, /* I2C Reset , */\ + { 0x920, "CFE"}, /* Enable CoolFlux , */\ + { 0x930, "AMPE"}, /* Enable Amplifier , */\ + { 0x940, "DCA"}, /* EnableBoost , */\ + { 0x950, "SBSL"}, /* Coolflux configured , */\ + { 0x960, "AMPC"}, /* Selection on how Amplifier is enabled , */\ + { 0x970, "DCDIS"}, /* DCDC not connected , */\ + { 0x980, "PSDR"}, /* IDDQ test amplifier , */\ + { 0x991, "DCCV"}, /* Coil Value , */\ + { 0x9b0, "CCFD"}, /* Selection CoolFlux Clock , */\ + { 0x9c1, "INTPAD"}, /* INT pad configuration control , */\ + { 0x9e0, "IPLL"}, /* PLL input reference clock selection , */\ + { 0xb07, "MTPK"}, /* 5Ah, 90d To access KEY1_Protected registers (Default for engineering), */\ + { 0xc25, "CVFDLY"}, /* Fractional delay adjustment between current and voltage sense, */\ + { 0x1011, "TDMPRF"}, /* TDM_usecase , */\ + { 0x1030, "TDMEN"}, /* TDM interface control , */\ + { 0x1040, "TDMCKINV"}, /* TDM clock inversion , */\ + { 0x1053, "TDMFSLN"}, /* TDM FS length , */\ + { 0x1090, "TDMFSPOL"}, /* TDM FS polarity , */\ + { 0x10a4, "TDMSAMSZ"}, /* TDM Sample Size for all tdm sinks/sources , */\ + { 0x1103, "TDMSLOTS"}, /* Number of slots , */\ + { 0x1144, "TDMSLLN"}, /* Slot length , */\ + { 0x1194, "TDMBRMG"}, /* Bits remaining , */\ + { 0x11e0, "TDMDDEL"}, /* Data delay , */\ + { 0x11f0, "TDMDADJ"}, /* Data adjustment , */\ + { 0x1201, "TDMTXFRM"}, /* TXDATA format , */\ + { 0x1221, "TDMUUS0"}, /* TXDATA format unused slot sd0 , */\ + { 0x1241, "TDMUUS1"}, /* TXDATA format unused slot sd1 , */\ + { 0x1270, "TDMSI0EN"}, /* TDM sink0 enable , */\ + { 0x1280, "TDMSI1EN"}, /* TDM sink1 enable , */\ + { 0x1290, "TDMSI2EN"}, /* TDM sink2 enable , */\ + { 0x12a0, "TDMSO0EN"}, /* TDM source0 enable , */\ + { 0x12b0, "TDMSO1EN"}, /* TDM source1 enable , */\ + { 0x12c0, "TDMSO2EN"}, /* TDM source2 enable , */\ + { 0x12d0, "TDMSI0IO"}, /* tdm_sink0_io , */\ + { 0x12e0, "TDMSI1IO"}, /* tdm_sink1_io , */\ + { 0x12f0, "TDMSI2IO"}, /* tdm_sink2_io , */\ + { 0x1300, "TDMSO0IO"}, /* tdm_source0_io , */\ + { 0x1310, "TDMSO1IO"}, /* tdm_source1_io , */\ + { 0x1320, "TDMSO2IO"}, /* tdm_source2_io , */\ + { 0x1333, "TDMSI0SL"}, /* sink0_slot [GAIN IN] , */\ + { 0x1373, "TDMSI1SL"}, /* sink1_slot [CH1 IN] , */\ + { 0x13b3, "TDMSI2SL"}, /* sink2_slot [CH2 IN] , */\ + { 0x1403, "TDMSO0SL"}, /* source0_slot [GAIN OUT] , */\ + { 0x1443, "TDMSO1SL"}, /* source1_slot [Voltage Sense] , */\ + { 0x1483, "TDMSO2SL"}, /* source2_slot [Current Sense] , */\ + { 0x14c3, "NBCK"}, /* NBCK , */\ + { 0x2000, "INTOVDDS"}, /* flag_por_int_out , */\ + { 0x2010, "INTOPLLS"}, /* flag_pll_lock_int_out , */\ + { 0x2020, "INTOOTDS"}, /* flag_otpok_int_out , */\ + { 0x2030, "INTOOVDS"}, /* flag_ovpok_int_out , */\ + { 0x2040, "INTOUVDS"}, /* flag_uvpok_int_out , */\ + { 0x2050, "INTOOCDS"}, /* flag_ocp_alarm_int_out , */\ + { 0x2060, "INTOCLKS"}, /* flag_clocks_stable_int_out , */\ + { 0x2070, "INTOCLIPS"}, /* flag_clip_int_out , */\ + { 0x2080, "INTOMTPB"}, /* mtp_busy_int_out , */\ + { 0x2090, "INTONOCLK"}, /* flag_lost_clk_int_out , */\ + { 0x20a0, "INTOSPKS"}, /* flag_cf_speakererror_int_out , */\ + { 0x20b0, "INTOACS"}, /* flag_cold_started_int_out , */\ + { 0x20c0, "INTOSWS"}, /* flag_engage_int_out , */\ + { 0x20d0, "INTOWDS"}, /* flag_watchdog_reset_int_out , */\ + { 0x20e0, "INTOAMPS"}, /* flag_enbl_amp_int_out , */\ + { 0x20f0, "INTOAREFS"}, /* flag_enbl_ref_int_out , */\ + { 0x2201, "INTOACK"}, /* Interrupt status register output - Corresponding flag, */\ + { 0x2300, "INTIVDDS"}, /* flag_por_int_in , */\ + { 0x2310, "INTIPLLS"}, /* flag_pll_lock_int_in , */\ + { 0x2320, "INTIOTDS"}, /* flag_otpok_int_in , */\ + { 0x2330, "INTIOVDS"}, /* flag_ovpok_int_in , */\ + { 0x2340, "INTIUVDS"}, /* flag_uvpok_int_in , */\ + { 0x2350, "INTIOCDS"}, /* flag_ocp_alarm_int_in , */\ + { 0x2360, "INTICLKS"}, /* flag_clocks_stable_int_in , */\ + { 0x2370, "INTICLIPS"}, /* flag_clip_int_in , */\ + { 0x2380, "INTIMTPB"}, /* mtp_busy_int_in , */\ + { 0x2390, "INTINOCLK"}, /* flag_lost_clk_int_in , */\ + { 0x23a0, "INTISPKS"}, /* flag_cf_speakererror_int_in , */\ + { 0x23b0, "INTIACS"}, /* flag_cold_started_int_in , */\ + { 0x23c0, "INTISWS"}, /* flag_engage_int_in , */\ + { 0x23d0, "INTIWDS"}, /* flag_watchdog_reset_int_in , */\ + { 0x23e0, "INTIAMPS"}, /* flag_enbl_amp_int_in , */\ + { 0x23f0, "INTIAREFS"}, /* flag_enbl_ref_int_in , */\ + { 0x2501, "INTIACK"}, /* Interrupt register input , */\ + { 0x2600, "INTENVDDS"}, /* flag_por_int_enable , */\ + { 0x2610, "INTENPLLS"}, /* flag_pll_lock_int_enable , */\ + { 0x2620, "INTENOTDS"}, /* flag_otpok_int_enable , */\ + { 0x2630, "INTENOVDS"}, /* flag_ovpok_int_enable , */\ + { 0x2640, "INTENUVDS"}, /* flag_uvpok_int_enable , */\ + { 0x2650, "INTENOCDS"}, /* flag_ocp_alarm_int_enable , */\ + { 0x2660, "INTENCLKS"}, /* flag_clocks_stable_int_enable , */\ + { 0x2670, "INTENCLIPS"}, /* flag_clip_int_enable , */\ + { 0x2680, "INTENMTPB"}, /* mtp_busy_int_enable , */\ + { 0x2690, "INTENNOCLK"}, /* flag_lost_clk_int_enable , */\ + { 0x26a0, "INTENSPKS"}, /* flag_cf_speakererror_int_enable , */\ + { 0x26b0, "INTENACS"}, /* flag_cold_started_int_enable , */\ + { 0x26c0, "INTENSWS"}, /* flag_engage_int_enable , */\ + { 0x26d0, "INTENWDS"}, /* flag_watchdog_reset_int_enable , */\ + { 0x26e0, "INTENAMPS"}, /* flag_enbl_amp_int_enable , */\ + { 0x26f0, "INTENAREFS"}, /* flag_enbl_ref_int_enable , */\ + { 0x2801, "INTENACK"}, /* Interrupt enable register , */\ + { 0x2900, "INTPOLVDDS"}, /* flag_por_int_pol , */\ + { 0x2910, "INTPOLPLLS"}, /* flag_pll_lock_int_pol , */\ + { 0x2920, "INTPOLOTDS"}, /* flag_otpok_int_pol , */\ + { 0x2930, "INTPOLOVDS"}, /* flag_ovpok_int_pol , */\ + { 0x2940, "INTPOLUVDS"}, /* flag_uvpok_int_pol , */\ + { 0x2950, "INTPOLOCDS"}, /* flag_ocp_alarm_int_pol , */\ + { 0x2960, "INTPOLCLKS"}, /* flag_clocks_stable_int_pol , */\ + { 0x2970, "INTPOLCLIPS"}, /* flag_clip_int_pol , */\ + { 0x2980, "INTPOLMTPB"}, /* mtp_busy_int_pol , */\ + { 0x2990, "INTPOLNOCLK"}, /* flag_lost_clk_int_pol , */\ + { 0x29a0, "INTPOLSPKS"}, /* flag_cf_speakererror_int_pol , */\ + { 0x29b0, "INTPOLACS"}, /* flag_cold_started_int_pol , */\ + { 0x29c0, "INTPOLSWS"}, /* flag_engage_int_pol , */\ + { 0x29d0, "INTPOLWDS"}, /* flag_watchdog_reset_int_pol , */\ + { 0x29e0, "INTPOLAMPS"}, /* flag_enbl_amp_int_pol , */\ + { 0x29f0, "INTPOLAREFS"}, /* flag_enbl_ref_int_pol , */\ + { 0x2b01, "INTPOLACK"}, /* Interrupt status flags polarity register , */\ + { 0x4900, "CLIP"}, /* Bypass clip control , */\ + { 0x62b0, "CIMTP"}, /* start copying all the data from i2cregs_mtp to mtp [Key 2 protected], */\ + { 0x7000, "RST"}, /* Reset CoolFlux DSP , */\ + { 0x7011, "DMEM"}, /* Target memory for access , */\ + { 0x7030, "AIF"}, /* Autoincrement-flag for memory-address , */\ + { 0x7040, "CFINT"}, /* Interrupt CoolFlux DSP , */\ + { 0x7087, "REQ"}, /* request for access (8 channels) , */\ + { 0x7080, "REQCMD"}, /* Firmware event request rpc command , */\ + { 0x7090, "REQRST"}, /* Firmware event request reset restart , */\ + { 0x70a0, "REQMIPS"}, /* Firmware event request short on mips , */\ + { 0x70b0, "REQMUTED"}, /* Firmware event request mute sequence ready , */\ + { 0x70c0, "REQVOL"}, /* Firmware event request volume ready , */\ + { 0x70d0, "REQDMG"}, /* Firmware event request speaker damage detected , */\ + { 0x70e0, "REQCAL"}, /* Firmware event request calibration completed , */\ + { 0x70f0, "REQRSV"}, /* Firmware event request reserved , */\ + { 0x710f, "MADD"}, /* memory-address to be accessed , */\ + { 0x720f, "MEMA"}, /* activate memory access (24- or 32-bits data is written/read to/from memory, */\ + { 0x7307, "ERR"}, /* Coolflux error flags , */\ + { 0x7387, "ACK"}, /* acknowledge of requests (8 channels) , */\ + { 0x7380, "ACKCMD"}, /* Firmware event acknowledge rpc command , */\ + { 0x7390, "ACKRST"}, /* Firmware event acknowledge reset restart , */\ + { 0x73a0, "ACKMIPS"}, /* Firmware event acknowledge short on mips , */\ + { 0x73b0, "ACKMUTED"}, /* Firmware event acknowledge mute sequence ready , */\ + { 0x73c0, "ACKVOL"}, /* Firmware event acknowledge volume ready , */\ + { 0x73d0, "ACKDMG"}, /* Firmware event acknowledge speaker damage detected, */\ + { 0x73e0, "ACKCAL"}, /* Firmware event acknowledge calibration completed , */\ + { 0x73f0, "ACKRSV"}, /* Firmware event acknowledge reserved , */\ + { 0x8000, "MTPOTC"}, /* Calibration schedule (key2 protected) , */\ + { 0x8010, "MTPEX"}, /* (key2 protected) , */\ + { 0x8045, "SWPROFIL" },\ + { 0x80a5, "SWVSTEP" },\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA1_BITNAMETABLE static tfaBfName_t Tfa1BitNames[]= {\ + { 0x0, "flag_por"}, /* Power-on-reset flag , */\ + { 0x10, "flag_pll_lock"}, /* PLL lock , */\ + { 0x20, "flag_otpok"}, /* Over Temperature Protection alarm , */\ + { 0x30, "flag_ovpok"}, /* Over Voltage Protection alarm , */\ + { 0x40, "flag_uvpok"}, /* Under Voltage Protection alarm , */\ + { 0x50, "flag_ocp_alarm"}, /* Over Current Protection alarm , */\ + { 0x60, "flag_clocks_stable"}, /* Clocks stable flag , */\ + { 0x70, "flag_clip"}, /* Amplifier clipping , */\ + { 0x80, "mtp_busy"}, /* MTP busy , */\ + { 0x90, "flag_lost_clk"}, /* Flag lost clock from clock generation unit , */\ + { 0xa0, "flag_cf_speakererror"}, /* Speaker error flag , */\ + { 0xb0, "flag_cold_started"}, /* Cold Start flag , */\ + { 0xc0, "flag_engage"}, /* Flag Engage , */\ + { 0xd0, "flag_watchdog_reset"}, /* Flag watchdog reset , */\ + { 0xe0, "flag_enbl_amp"}, /* Amplifier is enabled by manager , */\ + { 0xf0, "flag_enbl_ref"}, /* References are enabled by manager , */\ + { 0x109, "bat_adc"}, /* Battery voltage readout; 0 .. 5.5 [V] , */\ + { 0x208, "temp_adc"}, /* Temperature readout from the temperature sensor , */\ + { 0x30b, "rev_reg"}, /* Device type number is B97 , */\ + { 0x420, "ctrl_rcv"}, /* Enable Receiver Mode , */\ + { 0x431, "chan_sel"}, /* Channel Selection TDM input for Coolflux , */\ + { 0x450, "input_level"}, /* Input level selection control , */\ + { 0x461, "vamp_sel"}, /* Input selection for amplifier , */\ + { 0x4c3, "audio_fs"}, /* Audio sample rate setting , */\ + { 0x501, "vbat_prot_attacktime"}, /* Protection Attack Time , */\ + { 0x523, "vbat_prot_thlevel"}, /* ProtectionThreshold , */\ + { 0x561, "vbat_prot_max_reduct"}, /* Protection Maximum Reduction , */\ + { 0x582, "vbat_prot_release_t"}, /* Battery Protection Release Time , */\ + { 0x5b1, "vbat_prot_hysterese"}, /* Battery Protection Hysteresis , */\ + { 0x5d0, "reset_min_vbat"}, /* reset clipper , */\ + { 0x5e0, "sel_vbat"}, /* battery voltage for I2C read out only , */\ + { 0x5f0, "bypass_clipper"}, /* bypass clipper battery protection , */\ + { 0x600, "dpsa"}, /* Enable dynamic powerstage activation , */\ + { 0x650, "cf_mute"}, /* Soft mute in CoolFlux , */\ + { 0x670, "batsense_steepness"}, /* BatSenseSteepness , */\ + { 0x687, "vol"}, /* volume control (in CoolFlux) , */\ + { 0x702, "boost_volt"}, /* Boost Voltage , */\ + { 0x733, "boost_cur"}, /* Max boost coil current - step of 175 mA , */\ + { 0x7a0, "boost_intel"}, /* Adaptive boost mode , */\ + { 0x7b0, "boost_speed"}, /* Soft RampUp/Down mode for DCDC controller , */\ + { 0x7c0, "boost_peak2avg"}, /* ctrl_peak2avg for analog part of DCDC , */\ + { 0x800, "ext_temp_sel"}, /* Select external temperature also the ext_temp will be put on the temp read out , */\ + { 0x818, "ext_temp"}, /* external temperature setting to be given by host , */\ + { 0x8b2, "dcdc_synchronisation"}, /* DCDC synchronisation off + 7 positions , */\ + { 0x900, "powerdown"}, /* Device Mode , */\ + { 0x910, "reset"}, /* I2C Reset , */\ + { 0x920, "enbl_coolflux"}, /* Enable CoolFlux , */\ + { 0x930, "enbl_amplifier"}, /* Enable Amplifier , */\ + { 0x940, "enbl_boost"}, /* EnableBoost , */\ + { 0x950, "coolflux_configured"}, /* Coolflux configured , */\ + { 0x960, "sel_enbl_amplifier"}, /* Selection on how Amplifier is enabled , */\ + { 0x970, "dcdcoff_mode"}, /* DCDC not connected , */\ + { 0x980, "iddqtest"}, /* IDDQ test amplifier , */\ + { 0x991, "coil_value"}, /* Coil Value , */\ + { 0x9b0, "sel_cf_clock"}, /* Selection CoolFlux Clock , */\ + { 0x9c1, "int_pad_io"}, /* INT pad configuration control , */\ + { 0x9e0, "sel_fs_bck"}, /* PLL input reference clock selection , */\ + { 0x9f0, "sel_scl_cf_clock"}, /* Coolflux sub-system clock , */\ + { 0xb07, "mtpkey2"}, /* 5Ah, 90d To access KEY1_Protected registers (Default for engineering), */\ + { 0xc00, "enbl_volt_sense"}, /* Voltage sense enabling control bit , */\ + { 0xc10, "vsense_pwm_sel"}, /* Voltage sense PWM source selection control , */\ + { 0xc25, "vi_frac_delay"}, /* Fractional delay adjustment between current and voltage sense, */\ + { 0xc80, "sel_voltsense_out"}, /* TDM output data selection control , */\ + { 0xc90, "vsense_bypass_avg"}, /* Voltage Sense Average Block Bypass , */\ + { 0xd05, "cf_frac_delay"}, /* Fractional delay adjustment between current and voltage sense by firmware, */\ + { 0xe00, "bypass_dcdc_curr_prot"}, /* Control to switch off dcdc current reduction with bat protection, */\ + { 0xe80, "disable_clock_sh_prot"}, /* disable clock_sh protection , */\ + { 0xe96, "reserve_reg_1_15_9"}, /* , */\ + { 0x1011, "tdm_usecase"}, /* TDM_usecase , */\ + { 0x1030, "tdm_enable"}, /* TDM interface control , */\ + { 0x1040, "tdm_clk_inversion"}, /* TDM clock inversion , */\ + { 0x1053, "tdm_fs_ws_length"}, /* TDM FS length , */\ + { 0x1090, "tdm_fs_ws_polarity"}, /* TDM FS polarity , */\ + { 0x10a4, "tdm_sample_size"}, /* TDM Sample Size for all tdm sinks/sources , */\ + { 0x1103, "tdm_nb_of_slots"}, /* Number of slots , */\ + { 0x1144, "tdm_slot_length"}, /* Slot length , */\ + { 0x1194, "tdm_bits_remaining"}, /* Bits remaining , */\ + { 0x11e0, "tdm_data_delay"}, /* Data delay , */\ + { 0x11f0, "tdm_data_adjustment"}, /* Data adjustment , */\ + { 0x1201, "tdm_txdata_format"}, /* TXDATA format , */\ + { 0x1221, "tdm_txdata_format_unused_slot_sd0"}, /* TXDATA format unused slot sd0 , */\ + { 0x1241, "tdm_txdata_format_unused_slot_sd1"}, /* TXDATA format unused slot sd1 , */\ + { 0x1270, "tdm_sink0_enable"}, /* TDM sink0 enable , */\ + { 0x1280, "tdm_sink1_enable"}, /* TDM sink1 enable , */\ + { 0x1290, "tdm_sink2_enable"}, /* TDM sink2 enable , */\ + { 0x12a0, "tdm_source0_enable"}, /* TDM source0 enable , */\ + { 0x12b0, "tdm_source1_enable"}, /* TDM source1 enable , */\ + { 0x12c0, "tdm_source2_enable"}, /* TDM source2 enable , */\ + { 0x12d0, "tdm_sink0_io"}, /* tdm_sink0_io , */\ + { 0x12e0, "tdm_sink1_io"}, /* tdm_sink1_io , */\ + { 0x12f0, "tdm_sink2_io"}, /* tdm_sink2_io , */\ + { 0x1300, "tdm_source0_io"}, /* tdm_source0_io , */\ + { 0x1310, "tdm_source1_io"}, /* tdm_source1_io , */\ + { 0x1320, "tdm_source2_io"}, /* tdm_source2_io , */\ + { 0x1333, "tdm_sink0_slot"}, /* sink0_slot [GAIN IN] , */\ + { 0x1373, "tdm_sink1_slot"}, /* sink1_slot [CH1 IN] , */\ + { 0x13b3, "tdm_sink2_slot"}, /* sink2_slot [CH2 IN] , */\ + { 0x1403, "tdm_source0_slot"}, /* source0_slot [GAIN OUT] , */\ + { 0x1443, "tdm_source1_slot"}, /* source1_slot [Voltage Sense] , */\ + { 0x1483, "tdm_source2_slot"}, /* source2_slot [Current Sense] , */\ + { 0x14c3, "tdm_nbck"}, /* NBCK , */\ + { 0x1500, "flag_tdm_lut_error"}, /* TDM LUT error flag , */\ + { 0x1512, "flag_tdm_status"}, /* TDM interface status bits , */\ + { 0x1540, "flag_tdm_error"}, /* TDM interface error indicator , */\ + { 0x2000, "flag_por_int_out"}, /* flag_por_int_out , */\ + { 0x2010, "flag_pll_lock_int_out"}, /* flag_pll_lock_int_out , */\ + { 0x2020, "flag_otpok_int_out"}, /* flag_otpok_int_out , */\ + { 0x2030, "flag_ovpok_int_out"}, /* flag_ovpok_int_out , */\ + { 0x2040, "flag_uvpok_int_out"}, /* flag_uvpok_int_out , */\ + { 0x2050, "flag_ocp_alarm_int_out"}, /* flag_ocp_alarm_int_out , */\ + { 0x2060, "flag_clocks_stable_int_out"}, /* flag_clocks_stable_int_out , */\ + { 0x2070, "flag_clip_int_out"}, /* flag_clip_int_out , */\ + { 0x2080, "mtp_busy_int_out"}, /* mtp_busy_int_out , */\ + { 0x2090, "flag_lost_clk_int_out"}, /* flag_lost_clk_int_out , */\ + { 0x20a0, "flag_cf_speakererror_int_out"}, /* flag_cf_speakererror_int_out , */\ + { 0x20b0, "flag_cold_started_int_out"}, /* flag_cold_started_int_out , */\ + { 0x20c0, "flag_engage_int_out"}, /* flag_engage_int_out , */\ + { 0x20d0, "flag_watchdog_reset_int_out"}, /* flag_watchdog_reset_int_out , */\ + { 0x20e0, "flag_enbl_amp_int_out"}, /* flag_enbl_amp_int_out , */\ + { 0x20f0, "flag_enbl_ref_int_out"}, /* flag_enbl_ref_int_out , */\ + { 0x2100, "flag_voutcomp_int_out"}, /* flag_voutcomp_int_out , */\ + { 0x2110, "flag_voutcomp93_int_out"}, /* flag_voutcomp93_int_out , */\ + { 0x2120, "flag_voutcomp86_int_out"}, /* flag_voutcomp86_int_out , */\ + { 0x2130, "flag_hiz_int_out"}, /* flag_hiz_int_out , */\ + { 0x2140, "flag_ocpokbst_int_out"}, /* flag_ocpokbst_int_out , */\ + { 0x2150, "flag_peakcur_int_out"}, /* flag_peakcur_int_out , */\ + { 0x2160, "flag_ocpokap_int_out"}, /* flag_ocpokap_int_out , */\ + { 0x2170, "flag_ocpokan_int_out"}, /* flag_ocpokan_int_out , */\ + { 0x2180, "flag_ocpokbp_int_out"}, /* flag_ocpokbp_int_out , */\ + { 0x2190, "flag_ocpokbn_int_out"}, /* flag_ocpokbn_int_out , */\ + { 0x21a0, "flag_adc10_ready_int_out"}, /* flag_adc10_ready_int_out , */\ + { 0x21b0, "flag_clipa_high_int_out"}, /* flag_clipa_high_int_out , */\ + { 0x21c0, "flag_clipa_low_int_out"}, /* flag_clipa_low_int_out , */\ + { 0x21d0, "flag_clipb_high_int_out"}, /* flag_clipb_high_int_out , */\ + { 0x21e0, "flag_clipb_low_int_out"}, /* flag_clipb_low_int_out , */\ + { 0x21f0, "flag_tdm_error_int_out"}, /* flag_tdm_error_int_out , */\ + { 0x2201, "interrupt_out3"}, /* Interrupt status register output - Corresponding flag, */\ + { 0x2300, "flag_por_int_in"}, /* flag_por_int_in , */\ + { 0x2310, "flag_pll_lock_int_in"}, /* flag_pll_lock_int_in , */\ + { 0x2320, "flag_otpok_int_in"}, /* flag_otpok_int_in , */\ + { 0x2330, "flag_ovpok_int_in"}, /* flag_ovpok_int_in , */\ + { 0x2340, "flag_uvpok_int_in"}, /* flag_uvpok_int_in , */\ + { 0x2350, "flag_ocp_alarm_int_in"}, /* flag_ocp_alarm_int_in , */\ + { 0x2360, "flag_clocks_stable_int_in"}, /* flag_clocks_stable_int_in , */\ + { 0x2370, "flag_clip_int_in"}, /* flag_clip_int_in , */\ + { 0x2380, "mtp_busy_int_in"}, /* mtp_busy_int_in , */\ + { 0x2390, "flag_lost_clk_int_in"}, /* flag_lost_clk_int_in , */\ + { 0x23a0, "flag_cf_speakererror_int_in"}, /* flag_cf_speakererror_int_in , */\ + { 0x23b0, "flag_cold_started_int_in"}, /* flag_cold_started_int_in , */\ + { 0x23c0, "flag_engage_int_in"}, /* flag_engage_int_in , */\ + { 0x23d0, "flag_watchdog_reset_int_in"}, /* flag_watchdog_reset_int_in , */\ + { 0x23e0, "flag_enbl_amp_int_in"}, /* flag_enbl_amp_int_in , */\ + { 0x23f0, "flag_enbl_ref_int_in"}, /* flag_enbl_ref_int_in , */\ + { 0x2400, "flag_voutcomp_int_in"}, /* flag_voutcomp_int_in , */\ + { 0x2410, "flag_voutcomp93_int_in"}, /* flag_voutcomp93_int_in , */\ + { 0x2420, "flag_voutcomp86_int_in"}, /* flag_voutcomp86_int_in , */\ + { 0x2430, "flag_hiz_int_in"}, /* flag_hiz_int_in , */\ + { 0x2440, "flag_ocpokbst_int_in"}, /* flag_ocpokbst_int_in , */\ + { 0x2450, "flag_peakcur_int_in"}, /* flag_peakcur_int_in , */\ + { 0x2460, "flag_ocpokap_int_in"}, /* flag_ocpokap_int_in , */\ + { 0x2470, "flag_ocpokan_int_in"}, /* flag_ocpokan_int_in , */\ + { 0x2480, "flag_ocpokbp_int_in"}, /* flag_ocpokbp_int_in , */\ + { 0x2490, "flag_ocpokbn_int_in"}, /* flag_ocpokbn_int_in , */\ + { 0x24a0, "flag_adc10_ready_int_in"}, /* flag_adc10_ready_int_in , */\ + { 0x24b0, "flag_clipa_high_int_in"}, /* flag_clipa_high_int_in , */\ + { 0x24c0, "flag_clipa_low_int_in"}, /* flag_clipa_low_int_in , */\ + { 0x24d0, "flag_clipb_high_int_in"}, /* flag_clipb_high_int_in , */\ + { 0x24e0, "flag_clipb_low_int_in"}, /* flag_clipb_low_int_in , */\ + { 0x24f0, "flag_tdm_error_int_in"}, /* flag_tdm_error_int_in , */\ + { 0x2501, "interrupt_in3"}, /* Interrupt register input , */\ + { 0x2600, "flag_por_int_enable"}, /* flag_por_int_enable , */\ + { 0x2610, "flag_pll_lock_int_enable"}, /* flag_pll_lock_int_enable , */\ + { 0x2620, "flag_otpok_int_enable"}, /* flag_otpok_int_enable , */\ + { 0x2630, "flag_ovpok_int_enable"}, /* flag_ovpok_int_enable , */\ + { 0x2640, "flag_uvpok_int_enable"}, /* flag_uvpok_int_enable , */\ + { 0x2650, "flag_ocp_alarm_int_enable"}, /* flag_ocp_alarm_int_enable , */\ + { 0x2660, "flag_clocks_stable_int_enable"}, /* flag_clocks_stable_int_enable , */\ + { 0x2670, "flag_clip_int_enable"}, /* flag_clip_int_enable , */\ + { 0x2680, "mtp_busy_int_enable"}, /* mtp_busy_int_enable , */\ + { 0x2690, "flag_lost_clk_int_enable"}, /* flag_lost_clk_int_enable , */\ + { 0x26a0, "flag_cf_speakererror_int_enable"}, /* flag_cf_speakererror_int_enable , */\ + { 0x26b0, "flag_cold_started_int_enable"}, /* flag_cold_started_int_enable , */\ + { 0x26c0, "flag_engage_int_enable"}, /* flag_engage_int_enable , */\ + { 0x26d0, "flag_watchdog_reset_int_enable"}, /* flag_watchdog_reset_int_enable , */\ + { 0x26e0, "flag_enbl_amp_int_enable"}, /* flag_enbl_amp_int_enable , */\ + { 0x26f0, "flag_enbl_ref_int_enable"}, /* flag_enbl_ref_int_enable , */\ + { 0x2700, "flag_voutcomp_int_enable"}, /* flag_voutcomp_int_enable , */\ + { 0x2710, "flag_voutcomp93_int_enable"}, /* flag_voutcomp93_int_enable , */\ + { 0x2720, "flag_voutcomp86_int_enable"}, /* flag_voutcomp86_int_enable , */\ + { 0x2730, "flag_hiz_int_enable"}, /* flag_hiz_int_enable , */\ + { 0x2740, "flag_ocpokbst_int_enable"}, /* flag_ocpokbst_int_enable , */\ + { 0x2750, "flag_peakcur_int_enable"}, /* flag_peakcur_int_enable , */\ + { 0x2760, "flag_ocpokap_int_enable"}, /* flag_ocpokap_int_enable , */\ + { 0x2770, "flag_ocpokan_int_enable"}, /* flag_ocpokan_int_enable , */\ + { 0x2780, "flag_ocpokbp_int_enable"}, /* flag_ocpokbp_int_enable , */\ + { 0x2790, "flag_ocpokbn_int_enable"}, /* flag_ocpokbn_int_enable , */\ + { 0x27a0, "flag_adc10_ready_int_enable"}, /* flag_adc10_ready_int_enable , */\ + { 0x27b0, "flag_clipa_high_int_enable"}, /* flag_clipa_high_int_enable , */\ + { 0x27c0, "flag_clipa_low_int_enable"}, /* flag_clipa_low_int_enable , */\ + { 0x27d0, "flag_clipb_high_int_enable"}, /* flag_clipb_high_int_enable , */\ + { 0x27e0, "flag_clipb_low_int_enable"}, /* flag_clipb_low_int_enable , */\ + { 0x27f0, "flag_tdm_error_int_enable"}, /* flag_tdm_error_int_enable , */\ + { 0x2801, "interrupt_enable3"}, /* Interrupt enable register , */\ + { 0x2900, "flag_por_int_pol"}, /* flag_por_int_pol , */\ + { 0x2910, "flag_pll_lock_int_pol"}, /* flag_pll_lock_int_pol , */\ + { 0x2920, "flag_otpok_int_pol"}, /* flag_otpok_int_pol , */\ + { 0x2930, "flag_ovpok_int_pol"}, /* flag_ovpok_int_pol , */\ + { 0x2940, "flag_uvpok_int_pol"}, /* flag_uvpok_int_pol , */\ + { 0x2950, "flag_ocp_alarm_int_pol"}, /* flag_ocp_alarm_int_pol , */\ + { 0x2960, "flag_clocks_stable_int_pol"}, /* flag_clocks_stable_int_pol , */\ + { 0x2970, "flag_clip_int_pol"}, /* flag_clip_int_pol , */\ + { 0x2980, "mtp_busy_int_pol"}, /* mtp_busy_int_pol , */\ + { 0x2990, "flag_lost_clk_int_pol"}, /* flag_lost_clk_int_pol , */\ + { 0x29a0, "flag_cf_speakererror_int_pol"}, /* flag_cf_speakererror_int_pol , */\ + { 0x29b0, "flag_cold_started_int_pol"}, /* flag_cold_started_int_pol , */\ + { 0x29c0, "flag_engage_int_pol"}, /* flag_engage_int_pol , */\ + { 0x29d0, "flag_watchdog_reset_int_pol"}, /* flag_watchdog_reset_int_pol , */\ + { 0x29e0, "flag_enbl_amp_int_pol"}, /* flag_enbl_amp_int_pol , */\ + { 0x29f0, "flag_enbl_ref_int_pol"}, /* flag_enbl_ref_int_pol , */\ + { 0x2a00, "flag_voutcomp_int_pol"}, /* flag_voutcomp_int_pol , */\ + { 0x2a10, "flag_voutcomp93_int_pol"}, /* flag_voutcomp93_int_pol , */\ + { 0x2a20, "flag_voutcomp86_int_pol"}, /* flag_voutcomp86_int_pol , */\ + { 0x2a30, "flag_hiz_int_pol"}, /* flag_hiz_int_pol , */\ + { 0x2a40, "flag_ocpokbst_int_pol"}, /* flag_ocpokbst_int_pol , */\ + { 0x2a50, "flag_peakcur_int_pol"}, /* flag_peakcur_int_pol , */\ + { 0x2a60, "flag_ocpokap_int_pol"}, /* flag_ocpokap_int_pol , */\ + { 0x2a70, "flag_ocpokan_int_pol"}, /* flag_ocpokan_int_pol , */\ + { 0x2a80, "flag_ocpokbp_int_pol"}, /* flag_ocpokbp_int_pol , */\ + { 0x2a90, "flag_ocpokbn_int_pol"}, /* flag_ocpokbn_int_pol , */\ + { 0x2aa0, "flag_adc10_ready_int_pol"}, /* flag_adc10_ready_int_pol , */\ + { 0x2ab0, "flag_clipa_high_int_pol"}, /* flag_clipa_high_int_pol , */\ + { 0x2ac0, "flag_clipa_low_int_pol"}, /* flag_clipa_low_int_pol , */\ + { 0x2ad0, "flag_clipb_high_int_pol"}, /* flag_clipb_high_int_pol , */\ + { 0x2ae0, "flag_clipb_low_int_pol"}, /* flag_clipb_low_int_pol , */\ + { 0x2af0, "flag_tdm_error_int_pol"}, /* flag_tdm_error_int_pol , */\ + { 0x2b01, "status_polarity3"}, /* Interrupt status flags polarity register , */\ + { 0x3000, "flag_voutcomp"}, /* flag_voutcomp, indication Vset is larger than Vbat, */\ + { 0x3010, "flag_voutcomp93"}, /* flag_voutcomp93, indication Vset is larger than 1.07* Vbat, */\ + { 0x3020, "flag_voutcomp86"}, /* flag_voutcomp86, indication Vset is larger than 1.14* Vbat, */\ + { 0x3030, "flag_hiz"}, /* flag_hiz, indication Vbst is larger than Vbat , */\ + { 0x3040, "flag_ocpokbst"}, /* flag_ocpokbst, indication no over current in boost converter pmos switch, */\ + { 0x3050, "flag_peakcur"}, /* flag_peakcur, indication current is max in dcdc converter, */\ + { 0x3060, "flag_ocpokap"}, /* flag_ocpokap, indication no over current in amplifier "a" pmos output stage, */\ + { 0x3070, "flag_ocpokan"}, /* flag_ocpokan, indication no over current in amplifier "a" nmos output stage, */\ + { 0x3080, "flag_ocpokbp"}, /* flag_ocpokbp, indication no over current in amplifier "b" pmos output stage, */\ + { 0x3090, "flag_ocpokbn"}, /* flag_ocpokbn, indication no over current in amplifier"b" nmos output stage, */\ + { 0x30a0, "flag_adc10_ready"}, /* flag_adc10_ready, indication adc10 is ready , */\ + { 0x30b0, "flag_clipa_high"}, /* flag_clipa_high, indication pmos amplifier "a" is clipping, */\ + { 0x30c0, "flag_clipa_low"}, /* flag_clipa_low, indication nmos amplifier "a" is clipping, */\ + { 0x30d0, "flag_clipb_high"}, /* flag_clipb_high, indication pmos amplifier "b" is clipping, */\ + { 0x30e0, "flag_clipb_low"}, /* flag_clipb_low, indication nmos amplifier "b" is clipping, */\ + { 0x310f, "mtp_man_data_out"}, /* single word read from MTP (manual copy) , */\ + { 0x3200, "key01_locked"}, /* key01_locked, indication key 1 is locked , */\ + { 0x3210, "key02_locked"}, /* key02_locked, indication key 2 is locked , */\ + { 0x3225, "mtp_ecc_tcout"}, /* mtp_ecc_tcout , */\ + { 0x3280, "mtpctrl_valid_test_rd"}, /* mtp test readout for read , */\ + { 0x3290, "mtpctrl_valid_test_wr"}, /* mtp test readout for write , */\ + { 0x32a0, "flag_in_alarm_state"}, /* Alarm state , */\ + { 0x32b0, "mtp_ecc_err2"}, /* two or more bit errors detected in MTP, can not reconstruct value, */\ + { 0x32c0, "mtp_ecc_err1"}, /* one bit error detected in MTP, reconstructed value, */\ + { 0x32d0, "mtp_mtp_hvf"}, /* high voltage ready flag for MTP , */\ + { 0x32f0, "mtp_zero_check_fail"}, /* zero check failed (tbd) for MTP , */\ + { 0x3309, "data_adc10_tempbat"}, /* data_adc10_tempbat[9;0], adc 10 data output for testing, */\ + { 0x400f, "hid_code"}, /* 5A6Bh, 23147d to access registers (Default for engineering), */\ + { 0x4100, "bypass_hp"}, /* Bypass_High Pass Filter , */\ + { 0x4110, "hard_mute"}, /* Hard Mute , */\ + { 0x4120, "soft_mute"}, /* Soft Mute , */\ + { 0x4134, "pwm_delay"}, /* PWM DelayBits to set the delay , */\ + { 0x4180, "pwm_shape"}, /* PWM Shape , */\ + { 0x4190, "pwm_bitlength"}, /* PWM Bitlength in noise shaper , */\ + { 0x4203, "drive"}, /* Drive bits to select amount of power stage amplifier, */\ + { 0x4240, "reclock_pwm"}, /* , */\ + { 0x4250, "reclock_voltsense"}, /* , */\ + { 0x4281, "dpsalevel"}, /* DPSA Threshold level , */\ + { 0x42a1, "dpsa_release"}, /* DPSA Release time , */\ + { 0x42c0, "coincidence"}, /* Prevent simultaneously switching of output stage , */\ + { 0x42d0, "kickback"}, /* Prevent double pulses of output stage , */\ + { 0x4306, "drivebst"}, /* Drive bits to select the powertransistor sections boost converter, */\ + { 0x43a0, "ocptestbst"}, /* Boost OCP. For old ocp (ctrl_reversebst is 0);For new ocp (ctrl_reversebst is 1);, */\ + { 0x43d0, "test_abistfft_enbl"}, /* FFT coolflux , */\ + { 0x43f0, "test_bcontrol"}, /* test _bcontrol , */\ + { 0x4400, "reversebst"}, /* OverCurrent Protection selection of power stage boost converter, */\ + { 0x4410, "sensetest"}, /* Test option for the sense NMOS in booster for current mode control., */\ + { 0x4420, "enbl_engagebst"}, /* Enable power stage dcdc controller , */\ + { 0x4470, "enbl_slopecur"}, /* Enable bit of max-current dac , */\ + { 0x4480, "enbl_voutcomp"}, /* Enable vout comparators , */\ + { 0x4490, "enbl_voutcomp93"}, /* Enable vout-93 comparators , */\ + { 0x44a0, "enbl_voutcomp86"}, /* Enable vout-86 comparators , */\ + { 0x44b0, "enbl_hizcom"}, /* Enable hiz comparator , */\ + { 0x44c0, "enbl_peakcur"}, /* Enable peak current , */\ + { 0x44d0, "bypass_ovpglitch"}, /* Bypass OVP Glitch Filter , */\ + { 0x44e0, "enbl_windac"}, /* Enable window dac , */\ + { 0x44f0, "enbl_powerbst"}, /* Enable line of the powerstage , */\ + { 0x4507, "ocp_thr"}, /* ocp_thr threshold level for OCP , */\ + { 0x4580, "bypass_glitchfilter"}, /* Bypass glitch filter , */\ + { 0x4590, "bypass_ovp"}, /* Bypass OVP , */\ + { 0x45a0, "bypass_uvp"}, /* Bypass UVP , */\ + { 0x45b0, "bypass_otp"}, /* Bypass OTP , */\ + { 0x45c0, "bypass_ocp"}, /* Bypass OCP , */\ + { 0x45d0, "bypass_ocpcounter"}, /* BypassOCPCounter , */\ + { 0x45e0, "bypass_lost_clk"}, /* Bypasslost_clk detector , */\ + { 0x45f0, "vpalarm"}, /* vpalarm (uvp ovp handling) , */\ + { 0x4600, "bypass_gc"}, /* bypass_gc, bypasses the CS gain correction , */\ + { 0x4610, "cs_gain_control"}, /* gain control by means of MTP or i2c; 0 is MTP , */\ + { 0x4627, "cs_gain"}, /* + / - 128 steps in steps of 1/4 percent 2's compliment, */\ + { 0x46a0, "bypass_lp"}, /* bypass Low-Pass filter in temperature sensor , */\ + { 0x46b0, "bypass_pwmcounter"}, /* bypass_pwmcounter , */\ + { 0x46c0, "cs_negfixed"}, /* does not switch to neg , */\ + { 0x46d2, "cs_neghyst"}, /* switches to neg depending on level , */\ + { 0x4700, "switch_fb"}, /* switch_fb , */\ + { 0x4713, "se_hyst"}, /* se_hyst , */\ + { 0x4754, "se_level"}, /* se_level , */\ + { 0x47a5, "ktemp"}, /* temperature compensation trimming , */\ + { 0x4800, "cs_negin"}, /* negin , */\ + { 0x4810, "cs_sein"}, /* cs_sein , */\ + { 0x4820, "cs_coincidence"}, /* Coincidence current sense , */\ + { 0x4830, "iddqtestbst"}, /* for iddq testing in powerstage of boost convertor , */\ + { 0x4840, "coincidencebst"}, /* Switch protection on to prevent simultaneously switching power stages bst and amp, */\ + { 0x4876, "delay_se_neg"}, /* delay of se and neg , */\ + { 0x48e1, "cs_ttrack"}, /* sample & hold track time , */\ + { 0x4900, "bypass_clip"}, /* Bypass clip control , */\ + { 0x4920, "cf_cgate_off"}, /* to disable clock gating in the coolflux , */\ + { 0x4940, "clipfast"}, /* clock switch for battery protection clipper, it switches back to old frequency, */\ + { 0x4950, "cs_8ohm"}, /* 8 ohm mode for current sense (gain mode) , */\ + { 0x4974, "delay_clock_sh"}, /* delay_sh, tunes S7H delay , */\ + { 0x49c0, "inv_clksh"}, /* Invert the sample/hold clock for current sense ADC, */\ + { 0x49d0, "inv_neg"}, /* Invert neg signal , */\ + { 0x49e0, "inv_se"}, /* Invert se signal , */\ + { 0x49f0, "setse"}, /* switches between Single Ende and differential mode; 1 is single ended, */\ + { 0x4a12, "adc10_sel"}, /* select the input to convert the 10b ADC , */\ + { 0x4a60, "adc10_reset"}, /* Global asynchronous reset (active HIGH) 10 bit ADC, */\ + { 0x4a81, "adc10_test"}, /* Test mode selection signal 10 bit ADC , */\ + { 0x4aa0, "bypass_lp_vbat"}, /* lp filter in batt sensor , */\ + { 0x4ae0, "dc_offset"}, /* switch offset control on/off, is decimator offset control, */\ + { 0x4af0, "tsense_hibias"}, /* bit to set the biasing in temp sensor to high , */\ + { 0x4b00, "adc13_iset"}, /* Micadc Setting of current consumption. Debug use only, */\ + { 0x4b14, "adc13_gain"}, /* Micadc gain setting (2-compl) , */\ + { 0x4b61, "adc13_slowdel"}, /* Micadc Delay setting for internal clock. Debug use only, */\ + { 0x4b83, "adc13_offset"}, /* Micadc ADC offset setting , */\ + { 0x4bc0, "adc13_bsoinv"}, /* Micadc bit stream output invert mode for test , */\ + { 0x4bd0, "adc13_resonator_enable"}, /* Micadc Give extra SNR with less stability. Debug use only, */\ + { 0x4be0, "testmicadc"}, /* Mux at input of MICADC for test purpose , */\ + { 0x4c0f, "abist_offset"}, /* offset control for ABIST testing , */\ + { 0x4d05, "windac"}, /* for testing direct control windac , */\ + { 0x4dc3, "pwm_dcc_cnt"}, /* control pwm duty cycle when enbl_pwm_dcc is 1 , */\ + { 0x4e04, "slopecur"}, /* for testing direct control slopecur , */\ + { 0x4e50, "ctrl_dem"}, /* dyn element matching control, rest of codes are optional, */\ + { 0x4ed0, "enbl_pwm_dcc"}, /* to enable direct control of pwm duty cycle , */\ + { 0x5007, "gain"}, /* Gain setting of the gain multiplier , */\ + { 0x5081, "sourceb"}, /* Set OUTB to , */\ + { 0x50a1, "sourcea"}, /* Set OUTA to , */\ + { 0x50c1, "sourcebst"}, /* Sets the source of the pwmbst output to boost converter input for testing, */\ + { 0x50e0, "tdm_enable_loopback"}, /* TDM loopback test , */\ + { 0x5104, "pulselengthbst"}, /* pulse length setting test input for boost converter, */\ + { 0x5150, "bypasslatchbst"}, /* bypass_latch in boost converter , */\ + { 0x5160, "invertbst"}, /* invert pwmbst test signal , */\ + { 0x5174, "pulselength"}, /* pulse length setting test input for amplifier , */\ + { 0x51c0, "bypasslatch"}, /* bypass_latch in PWM source selection module , */\ + { 0x51d0, "invertb"}, /* invert pwmb test signal , */\ + { 0x51e0, "inverta"}, /* invert pwma test signal , */\ + { 0x51f0, "bypass_ctrlloop"}, /* bypass_ctrlloop bypasses the control loop of the amplifier, */\ + { 0x5210, "test_rdsona"}, /* tbd for rdson testing , */\ + { 0x5220, "test_rdsonb"}, /* tbd for rdson testing , */\ + { 0x5230, "test_rdsonbst"}, /* tbd for rdson testing , */\ + { 0x5240, "test_cvia"}, /* tbd for rdson testing , */\ + { 0x5250, "test_cvib"}, /* tbd for rdson testing , */\ + { 0x5260, "test_cvibst"}, /* tbd for rdson testing , */\ + { 0x5306, "digimuxa_sel"}, /* DigimuxA input selection control (see Digimux list for details), */\ + { 0x5376, "digimuxb_sel"}, /* DigimuxB input selection control (see Digimux list for details), */\ + { 0x5400, "hs_mode"}, /* hs_mode, high speed mode I2C bus , */\ + { 0x5412, "test_parametric_io"}, /* test_parametric_io for testing pads , */\ + { 0x5440, "enbl_ringo"}, /* enbl_ringo, for test purpose to check with ringo , */\ + { 0x5456, "digimuxc_sel"}, /* DigimuxC input selection control (see Digimux list for details), */\ + { 0x54c0, "dio_ehs"}, /* Slew control for DIO in output mode , */\ + { 0x54d0, "gainio_ehs"}, /* Slew control for GAINIO in output mode , */\ + { 0x550d, "enbl_amp"}, /* enbl_amp for testing to enable all analoge blocks in amplifier, */\ + { 0x5600, "use_direct_ctrls"}, /* use_direct_ctrls, to overrule several functions direct for testing, */\ + { 0x5610, "rst_datapath"}, /* rst_datapath, datapath reset , */\ + { 0x5620, "rst_cgu"}, /* rst_cgu, cgu reset , */\ + { 0x5637, "enbl_ref"}, /* for testing to enable all analoge blocks in references, */\ + { 0x56b0, "enbl_engage"}, /* Enable output stage amplifier , */\ + { 0x56c0, "use_direct_clk_ctrl"}, /* use_direct_clk_ctrl, to overrule several functions direct for testing, */\ + { 0x56d0, "use_direct_pll_ctrl"}, /* use_direct_pll_ctrl, to overrule several functions direct for testing, */\ + { 0x56e0, "use_direct_ctrls_2"}, /* use_direct_sourseamp_ctrls, to overrule several functions direct for testing, */\ + { 0x5707, "anamux"}, /* Anamux control , */\ + { 0x57c0, "ocptest"}, /* ctrl_ocptest, deactivates the over current protection in the power stages of the amplifier. The ocp flag signals stay active., */\ + { 0x57e0, "otptest"}, /* otptest, test mode otp amplifier , */\ + { 0x57f0, "reverse"}, /* 1: Normal mode, slope is controlled , */\ + { 0x5813, "pll_selr"}, /* pll_selr , */\ + { 0x5854, "pll_selp"}, /* pll_selp , */\ + { 0x58a5, "pll_seli"}, /* pll_seli , */\ + { 0x5950, "pll_mdec_msb"}, /* most significant bits of pll_mdec[16] , */\ + { 0x5960, "pll_ndec_msb"}, /* most significant bits of pll_ndec[9] , */\ + { 0x5970, "pll_frm"}, /* pll_frm , */\ + { 0x5980, "pll_directi"}, /* pll_directi , */\ + { 0x5990, "pll_directo"}, /* pll_directo , */\ + { 0x59a0, "enbl_pll"}, /* enbl_pll , */\ + { 0x59f0, "pll_bypass"}, /* pll_bypass , */\ + { 0x5a0f, "tsig_freq"}, /* tsig_freq, internal sinus test generator, frequency control, */\ + { 0x5b02, "tsig_freq_msb"}, /* select internal sinus test generator, frequency control msb bits, */\ + { 0x5b30, "inject_tsig"}, /* inject_tsig, control bit to switch to internal sinus test generator, */\ + { 0x5b44, "adc10_prog_sample"}, /* control ADC10 , */\ + { 0x5c0f, "pll_mdec"}, /* bits 15..0 of pll_mdec[16;0] , */\ + { 0x5d06, "pll_pdec"}, /* pll_pdec , */\ + { 0x5d78, "pll_ndec"}, /* bits 8..0 of pll_ndec[9;0] , */\ + { 0x6007, "mtpkey1"}, /* 5Ah, 90d To access KEY1_Protected registers (Default for engineering), */\ + { 0x6185, "mtp_ecc_tcin"}, /* Mtp_ecc_tcin , */\ + { 0x6203, "mtp_man_address_in"}, /* address from I2C regs for writing one word single mtp, */\ + { 0x6260, "mtp_ecc_eeb"}, /* enable code bit generation (active low!) , */\ + { 0x6270, "mtp_ecc_ecb"}, /* enable correction signal (active low!) , */\ + { 0x6280, "man_copy_mtp_to_iic"}, /* start copying single word from mtp to i2cregs_mtp , */\ + { 0x6290, "man_copy_iic_to_mtp"}, /* start copying single word from i2cregs_mtp to mtp [Key 1 protected], */\ + { 0x62a0, "auto_copy_mtp_to_iic"}, /* start copying all the data from mtp to i2cregs_mtp, */\ + { 0x62b0, "auto_copy_iic_to_mtp"}, /* start copying all the data from i2cregs_mtp to mtp [Key 2 protected], */\ + { 0x62d2, "mtp_speed_mode"}, /* Speed mode , */\ + { 0x6340, "mtp_direct_enable"}, /* mtp_direct_enable (key1 protected) , */\ + { 0x6350, "mtp_direct_wr"}, /* mtp_direct_wr (key1 protected) , */\ + { 0x6360, "mtp_direct_rd"}, /* mtp_direct_rd (key1 protected) , */\ + { 0x6370, "mtp_direct_rst"}, /* mtp_direct_rst (key1 protected) , */\ + { 0x6380, "mtp_direct_ers"}, /* mtp_direct_ers (key1 protected) , */\ + { 0x6390, "mtp_direct_prg"}, /* mtp_direct_prg (key1 protected) , */\ + { 0x63a0, "mtp_direct_epp"}, /* mtp_direct_epp (key1 protected) , */\ + { 0x63b4, "mtp_direct_test"}, /* mtp_direct_test (key1 protected) , */\ + { 0x640f, "mtp_man_data_in"}, /* single word to be written to MTP (manual copy) , */\ + { 0x7000, "cf_rst_dsp"}, /* Reset CoolFlux DSP , */\ + { 0x7011, "cf_dmem"}, /* Target memory for access , */\ + { 0x7030, "cf_aif"}, /* Autoincrement-flag for memory-address , */\ + { 0x7040, "cf_int"}, /* Interrupt CoolFlux DSP , */\ + { 0x7087, "cf_req"}, /* request for access (8 channels) , */\ + { 0x710f, "cf_madd"}, /* memory-address to be accessed , */\ + { 0x720f, "cf_mema"}, /* activate memory access (24- or 32-bits data is written/read to/from memory, */\ + { 0x7307, "cf_err"}, /* Coolflux error flags , */\ + { 0x7387, "cf_ack"}, /* acknowledge of requests (8 channels) , */\ + { 0x8000, "calibration_onetime"}, /* Calibration schedule (key2 protected) , */\ + { 0x8010, "calibr_ron_done"}, /* (key2 protected) , */\ + { 0x8105, "calibr_vout_offset"}, /* calibr_vout_offset (DCDCoffset) 2's compliment (key1 protected), */\ + { 0x8163, "calibr_delta_gain"}, /* delta gain for vamp (alpha) 2's compliment (key1 protected), */\ + { 0x81a5, "calibr_offs_amp"}, /* offset for vamp (Ampoffset) 2's compliment (key1 protected), */\ + { 0x8207, "calibr_gain_cs"}, /* gain current sense (Imeasalpha) 2's compliment (key1 protected), */\ + { 0x8284, "calibr_temp_offset"}, /* temperature offset 2's compliment (key1 protected), */\ + { 0x82d2, "calibr_temp_gain"}, /* temperature gain 2's compliment (key1 protected) , */\ + { 0x830f, "calibr_ron"}, /* Ron resistance of coil (key1 protected) , */\ + { 0x8505, "type_bits_HW"}, /* Key1_Protected_MTP5 , */\ + { 0x8601, "type_bits_1_0_SW"}, /* MTP-control SW , */\ + { 0x8681, "type_bits_8_9_SW"}, /* MTP-control SW , */\ + { 0x870f, "type_bits2_SW"}, /* MTP-control SW2 , */\ + { 0x8806, "htol_iic_addr"}, /* 7-bit I2C address to be used during HTOL testing , */\ + { 0x8870, "htol_iic_addr_en"}, /* HTOL_I2C_Address_Enable , */\ + { 0x8881, "ctrl_ovp_response"}, /* OVP response control , */\ + { 0x88a0, "disable_ovp_alarm_state"}, /* OVP alarm state control , */\ + { 0x88b0, "enbl_stretch_ovp"}, /* OVP alram strech control , */\ + { 0x88c0, "cf_debug_mode"}, /* Coolflux debug mode , */\ + { 0x8a0f, "production_data1"}, /* (key1 protected) , */\ + { 0x8b0f, "production_data2"}, /* (key1 protected) , */\ + { 0x8c0f, "production_data3"}, /* (key1 protected) , */\ + { 0x8d0f, "production_data4"}, /* (key1 protected) , */\ + { 0x8e0f, "production_data5"}, /* (key1 protected) , */\ + { 0x8f0f, "production_data6"}, /* (key1 protected) , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +enum tfa1_irq { + tfa1_irq_vdds = 0, + tfa1_irq_plls = 1, + tfa1_irq_ds = 2, + tfa1_irq_vds = 3, + tfa1_irq_uvds = 4, + tfa1_irq_cds = 5, + tfa1_irq_clks = 6, + tfa1_irq_clips = 7, + tfa1_irq_mtpb = 8, + tfa1_irq_clk = 9, + tfa1_irq_spks = 10, + tfa1_irq_acs = 11, + tfa1_irq_sws = 12, + tfa1_irq_wds = 13, + tfa1_irq_amps = 14, + tfa1_irq_arefs = 15, + tfa1_irq_ack = 32, + tfa1_irq_max = 33, + tfa1_irq_all = -1 /* all irqs */}; + +#define TFA1_IRQ_NAMETABLE static tfaIrqName_t Tfa1IrqNames[]= {\ + { 0, "VDDS"},\ + { 1, "PLLS"},\ + { 2, "DS"},\ + { 3, "VDS"},\ + { 4, "UVDS"},\ + { 5, "CDS"},\ + { 6, "CLKS"},\ + { 7, "CLIPS"},\ + { 8, "MTPB"},\ + { 9, "CLK"},\ + { 10, "SPKS"},\ + { 11, "ACS"},\ + { 12, "SWS"},\ + { 13, "WDS"},\ + { 14, "AMPS"},\ + { 15, "AREFS"},\ + { 16, "16"},\ + { 17, "17"},\ + { 18, "18"},\ + { 19, "19"},\ + { 20, "20"},\ + { 21, "21"},\ + { 22, "22"},\ + { 23, "23"},\ + { 24, "24"},\ + { 25, "25"},\ + { 26, "26"},\ + { 27, "27"},\ + { 28, "28"},\ + { 29, "29"},\ + { 30, "30"},\ + { 31, "31"},\ + { 32, "ACK"},\ + { 33, "33"},\ +}; diff --git a/sound/soc/codecs/inc/tfa2_tfafieldnames_N1C.h b/sound/soc/codecs/inc/tfa2_tfafieldnames_N1C.h new file mode 100644 index 000000000000..ebbb86390dcf --- /dev/null +++ b/sound/soc/codecs/inc/tfa2_tfafieldnames_N1C.h @@ -0,0 +1,1522 @@ +/** Filename: Tfa98xx_TfaFieldnames.h + * This file was generated automatically on 09/01/15 at 09:40:28. + * Source file: TFA9888_N1C_I2C_regmap_V1.xlsx + */ +#define TFA9888_I2CVERSION 18 +typedef enum nxpTfa2BfEnumList { + TFA2_BF_PWDN = 0x0000, /*!< Powerdown selection */ + TFA2_BF_I2CR = 0x0010, /*!< I2C Reset - Auto clear */ + TFA2_BF_CFE = 0x0020, /*!< Enable CoolFlux */ + TFA2_BF_AMPE = 0x0030, /*!< Activate Amplifier */ + TFA2_BF_DCA = 0x0040, /*!< Activate DC-to-DC converter */ + TFA2_BF_SBSL = 0x0050, /*!< Coolflux configured */ + TFA2_BF_AMPC = 0x0060, /*!< CoolFlux controls amplifier */ + TFA2_BF_INTP = 0x0071, /*!< Interrupt config */ + TFA2_BF_FSSSEL= 0x0091, /*!< Audio sample reference */ + TFA2_BF_BYPOCP= 0x00b0, /*!< Bypass OCP */ + TFA2_BF_TSTOCP= 0x00c0, /*!< OCP testing control */ + TFA2_BF_AMPINSEL= 0x0101, /*!< Amplifier input selection */ + TFA2_BF_MANSCONF= 0x0120, /*!< I2C configured */ + TFA2_BF_MANCOLD= 0x0130, /*!< Execute cold start */ + TFA2_BF_MANAOOSC= 0x0140, /*!< Internal osc off at PWDN */ + TFA2_BF_MANROBOD= 0x0150, /*!< Reaction on BOD */ + TFA2_BF_BODE = 0x0160, /*!< BOD Enable */ + TFA2_BF_BODHYS= 0x0170, /*!< BOD Hysteresis */ + TFA2_BF_BODFILT= 0x0181, /*!< BOD filter */ + TFA2_BF_BODTHLVL= 0x01a1, /*!< BOD threshold */ + TFA2_BF_MUTETO= 0x01d0, /*!< Time out SB mute sequence */ + TFA2_BF_RCVNS = 0x01e0, /*!< Noise shaper selection */ + TFA2_BF_MANWDE= 0x01f0, /*!< Watchdog manager reaction */ + TFA2_BF_AUDFS = 0x0203, /*!< Sample rate (fs) */ + TFA2_BF_INPLEV= 0x0240, /*!< TDM output attenuation */ + TFA2_BF_FRACTDEL= 0x0255, /*!< V/I Fractional delay */ + TFA2_BF_BYPHVBF= 0x02b0, /*!< Bypass HVBAT filter */ + TFA2_BF_LDOBYP= 0x02c0, /*!< Receiver LDO bypass */ + TFA2_BF_REV = 0x030f, /*!< Revision info */ + TFA2_BF_REFCKEXT= 0x0401, /*!< PLL external ref clock */ + TFA2_BF_REFCKSEL= 0x0420, /*!< PLL internal ref clock */ + TFA2_BF_SSLEFTE= 0x0500, /*!< Enable left channel */ + TFA2_BF_SSRIGHTE= 0x0510, /*!< Enable right channel */ + TFA2_BF_VSLEFTE= 0x0520, /*!< Voltage sense left */ + TFA2_BF_VSRIGHTE= 0x0530, /*!< Voltage sense right */ + TFA2_BF_CSLEFTE= 0x0540, /*!< Current sense left */ + TFA2_BF_CSRIGHTE= 0x0550, /*!< Current sense right */ + TFA2_BF_SSPDME= 0x0560, /*!< Sub-system PDM */ + TFA2_BF_STGAIN= 0x0d18, /*!< Side tone gain */ + TFA2_BF_PDMSMUTE= 0x0da0, /*!< Side tone soft mute */ + TFA2_BF_SWVSTEP= 0x0e06, /*!< Register for the host SW to record the current active vstep */ + TFA2_BF_VDDS = 0x1000, /*!< POR */ + TFA2_BF_PLLS = 0x1010, /*!< PLL lock */ + TFA2_BF_OTDS = 0x1020, /*!< OTP alarm */ + TFA2_BF_OVDS = 0x1030, /*!< OVP alarm */ + TFA2_BF_UVDS = 0x1040, /*!< UVP alarm */ + TFA2_BF_CLKS = 0x1050, /*!< Clocks stable */ + TFA2_BF_MTPB = 0x1060, /*!< MTP busy */ + TFA2_BF_NOCLK = 0x1070, /*!< Lost clock */ + TFA2_BF_SPKS = 0x1080, /*!< Speaker error */ + TFA2_BF_ACS = 0x1090, /*!< Cold Start */ + TFA2_BF_SWS = 0x10a0, /*!< Amplifier engage */ + TFA2_BF_WDS = 0x10b0, /*!< Watchdog */ + TFA2_BF_AMPS = 0x10c0, /*!< Amplifier enable */ + TFA2_BF_AREFS = 0x10d0, /*!< References enable */ + TFA2_BF_ADCCR = 0x10e0, /*!< Control ADC */ + TFA2_BF_BODNOK= 0x10f0, /*!< BOD */ + TFA2_BF_DCIL = 0x1100, /*!< DCDC current limiting */ + TFA2_BF_DCDCA = 0x1110, /*!< DCDC active */ + TFA2_BF_DCOCPOK= 0x1120, /*!< DCDC OCP nmos */ + TFA2_BF_DCHVBAT= 0x1140, /*!< DCDC level 1x */ + TFA2_BF_DCH114= 0x1150, /*!< DCDC level 1.14x */ + TFA2_BF_DCH107= 0x1160, /*!< DCDC level 1.07x */ + TFA2_BF_STMUTEB= 0x1170, /*!< side tone (un)mute busy */ + TFA2_BF_STMUTE= 0x1180, /*!< side tone mute state */ + TFA2_BF_TDMLUTER= 0x1190, /*!< TDM LUT error */ + TFA2_BF_TDMSTAT= 0x11a2, /*!< TDM status bits */ + TFA2_BF_TDMERR= 0x11d0, /*!< TDM error */ + TFA2_BF_HAPTIC= 0x11e0, /*!< Status haptic driver */ + TFA2_BF_OCPOAPL= 0x1200, /*!< OCPOK pmos A left */ + TFA2_BF_OCPOANL= 0x1210, /*!< OCPOK nmos A left */ + TFA2_BF_OCPOBPL= 0x1220, /*!< OCPOK pmos B left */ + TFA2_BF_OCPOBNL= 0x1230, /*!< OCPOK nmos B left */ + TFA2_BF_CLIPAHL= 0x1240, /*!< Clipping A left to Vddp */ + TFA2_BF_CLIPALL= 0x1250, /*!< Clipping A left to gnd */ + TFA2_BF_CLIPBHL= 0x1260, /*!< Clipping B left to Vddp */ + TFA2_BF_CLIPBLL= 0x1270, /*!< Clipping B left to gnd */ + TFA2_BF_OCPOAPRC= 0x1280, /*!< OCPOK pmos A RCV */ + TFA2_BF_OCPOANRC= 0x1290, /*!< OCPOK nmos A RCV */ + TFA2_BF_OCPOBPRC= 0x12a0, /*!< OCPOK pmos B RCV */ + TFA2_BF_OCPOBNRC= 0x12b0, /*!< OCPOK nmos B RCV */ + TFA2_BF_RCVLDOR= 0x12c0, /*!< RCV LDO regulates */ + TFA2_BF_RCVLDOBR= 0x12d0, /*!< Receiver LDO ready */ + TFA2_BF_OCDSL = 0x12e0, /*!< OCP left amplifier */ + TFA2_BF_CLIPSL= 0x12f0, /*!< Amplifier left clipping */ + TFA2_BF_OCPOAPR= 0x1300, /*!< OCPOK pmos A right */ + TFA2_BF_OCPOANR= 0x1310, /*!< OCPOK nmos A right */ + TFA2_BF_OCPOBPR= 0x1320, /*!< OCPOK pmos B right */ + TFA2_BF_OCPOBNR= 0x1330, /*!< OCPOK nmos B right */ + TFA2_BF_CLIPAHR= 0x1340, /*!< Clipping A right to Vddp */ + TFA2_BF_CLIPALR= 0x1350, /*!< Clipping A right to gnd */ + TFA2_BF_CLIPBHR= 0x1360, /*!< Clipping B left to Vddp */ + TFA2_BF_CLIPBLR= 0x1370, /*!< Clipping B right to gnd */ + TFA2_BF_OCDSR = 0x1380, /*!< OCP right amplifier */ + TFA2_BF_CLIPSR= 0x1390, /*!< Amplifier right clipping */ + TFA2_BF_OCPOKMC= 0x13a0, /*!< OCPOK MICVDD */ + TFA2_BF_MANALARM= 0x13b0, /*!< Alarm state */ + TFA2_BF_MANWAIT1= 0x13c0, /*!< Wait HW I2C settings */ + TFA2_BF_MANWAIT2= 0x13d0, /*!< Wait CF config */ + TFA2_BF_MANMUTE= 0x13e0, /*!< Audio mute sequence */ + TFA2_BF_MANOPER= 0x13f0, /*!< Operating state */ + TFA2_BF_SPKSL = 0x1400, /*!< Left speaker status */ + TFA2_BF_SPKSR = 0x1410, /*!< Right speaker status */ + TFA2_BF_CLKOOR= 0x1420, /*!< External clock status */ + TFA2_BF_MANSTATE= 0x1433, /*!< Device manager status */ + TFA2_BF_BATS = 0x1509, /*!< Battery voltage (V) */ + TFA2_BF_TEMPS = 0x1608, /*!< IC Temperature (C) */ + TFA2_BF_TDMUC = 0x2003, /*!< Usecase setting */ + TFA2_BF_TDME = 0x2040, /*!< Enable interface */ + TFA2_BF_TDMMODE= 0x2050, /*!< Slave/master */ + TFA2_BF_TDMCLINV= 0x2060, /*!< Reception data to BCK clock */ + TFA2_BF_TDMFSLN= 0x2073, /*!< FS length (master mode only) */ + TFA2_BF_TDMFSPOL= 0x20b0, /*!< FS polarity */ + TFA2_BF_TDMNBCK= 0x20c3, /*!< N-BCK's in FS */ + TFA2_BF_TDMSLOTS= 0x2103, /*!< N-slots in Frame */ + TFA2_BF_TDMSLLN= 0x2144, /*!< N-bits in slot */ + TFA2_BF_TDMBRMG= 0x2194, /*!< N-bits remaining */ + TFA2_BF_TDMDEL= 0x21e0, /*!< data delay to FS */ + TFA2_BF_TDMADJ= 0x21f0, /*!< data adjustment */ + TFA2_BF_TDMOOMP= 0x2201, /*!< Received audio compression */ + TFA2_BF_TDMSSIZE= 0x2224, /*!< Sample size per slot */ + TFA2_BF_TDMTXDFO= 0x2271, /*!< Format unused bits */ + TFA2_BF_TDMTXUS0= 0x2291, /*!< Format unused slots GAINIO */ + TFA2_BF_TDMTXUS1= 0x22b1, /*!< Format unused slots DIO1 */ + TFA2_BF_TDMTXUS2= 0x22d1, /*!< Format unused slots DIO2 */ + TFA2_BF_TDMLE = 0x2310, /*!< Control audio left */ + TFA2_BF_TDMRE = 0x2320, /*!< Control audio right */ + TFA2_BF_TDMVSRE= 0x2340, /*!< Control voltage sense right */ + TFA2_BF_TDMCSRE= 0x2350, /*!< Control current sense right */ + TFA2_BF_TDMVSLE= 0x2360, /*!< Voltage sense left control */ + TFA2_BF_TDMCSLE= 0x2370, /*!< Current sense left control */ + TFA2_BF_TDMCFRE= 0x2380, /*!< DSP out right control */ + TFA2_BF_TDMCFLE= 0x2390, /*!< DSP out left control */ + TFA2_BF_TDMCF3E= 0x23a0, /*!< AEC ref left control */ + TFA2_BF_TDMCF4E= 0x23b0, /*!< AEC ref right control */ + TFA2_BF_TDMPD1E= 0x23c0, /*!< PDM 1 control */ + TFA2_BF_TDMPD2E= 0x23d0, /*!< PDM 2 control */ + TFA2_BF_TDMLIO= 0x2421, /*!< IO audio left */ + TFA2_BF_TDMRIO= 0x2441, /*!< IO audio right */ + TFA2_BF_TDMVSRIO= 0x2481, /*!< IO voltage sense right */ + TFA2_BF_TDMCSRIO= 0x24a1, /*!< IO current sense right */ + TFA2_BF_TDMVSLIO= 0x24c1, /*!< IO voltage sense left */ + TFA2_BF_TDMCSLIO= 0x24e1, /*!< IO current sense left */ + TFA2_BF_TDMCFRIO= 0x2501, /*!< IO dspout right */ + TFA2_BF_TDMCFLIO= 0x2521, /*!< IO dspout left */ + TFA2_BF_TDMCF3IO= 0x2541, /*!< IO AEC ref left control */ + TFA2_BF_TDMCF4IO= 0x2561, /*!< IO AEC ref right control */ + TFA2_BF_TDMPD1IO= 0x2581, /*!< IO pdm1 */ + TFA2_BF_TDMPD2IO= 0x25a1, /*!< IO pdm2 */ + TFA2_BF_TDMLS = 0x2643, /*!< Position audio left */ + TFA2_BF_TDMRS = 0x2683, /*!< Position audio right */ + TFA2_BF_TDMVSRS= 0x2703, /*!< Position voltage sense right */ + TFA2_BF_TDMCSRS= 0x2743, /*!< Position current sense right */ + TFA2_BF_TDMVSLS= 0x2783, /*!< Position voltage sense left */ + TFA2_BF_TDMCSLS= 0x27c3, /*!< Position current sense left */ + TFA2_BF_TDMCFRS= 0x2803, /*!< Position dspout right */ + TFA2_BF_TDMCFLS= 0x2843, /*!< Position dspout left */ + TFA2_BF_TDMCF3S= 0x2883, /*!< Position AEC ref left control */ + TFA2_BF_TDMCF4S= 0x28c3, /*!< Position AEC ref right control */ + TFA2_BF_TDMPD1S= 0x2903, /*!< Position pdm1 */ + TFA2_BF_TDMPD2S= 0x2943, /*!< Position pdm2 */ + TFA2_BF_PDMSM = 0x3100, /*!< PDM control */ + TFA2_BF_PDMSTSEL= 0x3111, /*!< Side tone input */ + TFA2_BF_PDMLSEL= 0x3130, /*!< PDM data selection for left channel during PDM direct mode */ + TFA2_BF_PDMRSEL= 0x3140, /*!< PDM data selection for right channel during PDM direct mode */ + TFA2_BF_MICVDDE= 0x3150, /*!< Enable MICVDD */ + TFA2_BF_PDMCLRAT= 0x3201, /*!< PDM BCK/Fs ratio */ + TFA2_BF_PDMGAIN= 0x3223, /*!< PDM gain */ + TFA2_BF_PDMOSEL= 0x3263, /*!< PDM output selection - RE/FE data combination */ + TFA2_BF_SELCFHAPD= 0x32a0, /*!< Select the source for haptic data output (not for customer) */ + TFA2_BF_HAPTIME= 0x3307, /*!< Duration (ms) */ + TFA2_BF_HAPLEVEL= 0x3387, /*!< DC value (FFS) */ + TFA2_BF_GPIODIN= 0x3403, /*!< Receiving value */ + TFA2_BF_GPIOCTRL= 0x3500, /*!< GPIO master control over GPIO1/2 ports (not for customer) */ + TFA2_BF_GPIOCONF= 0x3513, /*!< Configuration */ + TFA2_BF_GPIODOUT= 0x3553, /*!< Transmitting value */ + TFA2_BF_ISTVDDS= 0x4000, /*!< Status POR */ + TFA2_BF_ISTPLLS= 0x4010, /*!< Status PLL lock */ + TFA2_BF_ISTOTDS= 0x4020, /*!< Status OTP alarm */ + TFA2_BF_ISTOVDS= 0x4030, /*!< Status OVP alarm */ + TFA2_BF_ISTUVDS= 0x4040, /*!< Status UVP alarm */ + TFA2_BF_ISTCLKS= 0x4050, /*!< Status clocks stable */ + TFA2_BF_ISTMTPB= 0x4060, /*!< Status MTP busy */ + TFA2_BF_ISTNOCLK= 0x4070, /*!< Status lost clock */ + TFA2_BF_ISTSPKS= 0x4080, /*!< Status speaker error */ + TFA2_BF_ISTACS= 0x4090, /*!< Status cold start */ + TFA2_BF_ISTSWS= 0x40a0, /*!< Status amplifier engage */ + TFA2_BF_ISTWDS= 0x40b0, /*!< Status watchdog */ + TFA2_BF_ISTAMPS= 0x40c0, /*!< Status amplifier enable */ + TFA2_BF_ISTAREFS= 0x40d0, /*!< Status Ref enable */ + TFA2_BF_ISTADCCR= 0x40e0, /*!< Status Control ADC */ + TFA2_BF_ISTBODNOK= 0x40f0, /*!< Status BOD */ + TFA2_BF_ISTBSTCU= 0x4100, /*!< Status DCDC current limiting */ + TFA2_BF_ISTBSTHI= 0x4110, /*!< Status DCDC active */ + TFA2_BF_ISTBSTOC= 0x4120, /*!< Status DCDC OCP */ + TFA2_BF_ISTBSTPKCUR= 0x4130, /*!< Status bst peakcur */ + TFA2_BF_ISTBSTVC= 0x4140, /*!< Status DCDC level 1x */ + TFA2_BF_ISTBST86= 0x4150, /*!< Status DCDC level 1.14x */ + TFA2_BF_ISTBST93= 0x4160, /*!< Status DCDC level 1.07x */ + TFA2_BF_ISTRCVLD= 0x4170, /*!< Status rcvldop ready */ + TFA2_BF_ISTOCPL= 0x4180, /*!< Status ocp alarm left */ + TFA2_BF_ISTOCPR= 0x4190, /*!< Status ocp alarm right */ + TFA2_BF_ISTMWSRC= 0x41a0, /*!< Status Waits HW I2C settings */ + TFA2_BF_ISTMWCFC= 0x41b0, /*!< Status waits CF config */ + TFA2_BF_ISTMWSMU= 0x41c0, /*!< Status Audio mute sequence */ + TFA2_BF_ISTCFMER= 0x41d0, /*!< Status cfma error */ + TFA2_BF_ISTCFMAC= 0x41e0, /*!< Status cfma ack */ + TFA2_BF_ISTCLKOOR= 0x41f0, /*!< Status flag_clk_out_of_range */ + TFA2_BF_ISTTDMER= 0x4200, /*!< Status tdm error */ + TFA2_BF_ISTCLPL= 0x4210, /*!< Status clip left */ + TFA2_BF_ISTCLPR= 0x4220, /*!< Status clip right */ + TFA2_BF_ISTOCPM= 0x4230, /*!< Status mic ocpok */ + TFA2_BF_ICLVDDS= 0x4400, /*!< Clear POR */ + TFA2_BF_ICLPLLS= 0x4410, /*!< Clear PLL lock */ + TFA2_BF_ICLOTDS= 0x4420, /*!< Clear OTP alarm */ + TFA2_BF_ICLOVDS= 0x4430, /*!< Clear OVP alarm */ + TFA2_BF_ICLUVDS= 0x4440, /*!< Clear UVP alarm */ + TFA2_BF_ICLCLKS= 0x4450, /*!< Clear clocks stable */ + TFA2_BF_ICLMTPB= 0x4460, /*!< Clear mtp busy */ + TFA2_BF_ICLNOCLK= 0x4470, /*!< Clear lost clk */ + TFA2_BF_ICLSPKS= 0x4480, /*!< Clear speaker error */ + TFA2_BF_ICLACS= 0x4490, /*!< Clear cold started */ + TFA2_BF_ICLSWS= 0x44a0, /*!< Clear amplifier engage */ + TFA2_BF_ICLWDS= 0x44b0, /*!< Clear watchdog */ + TFA2_BF_ICLAMPS= 0x44c0, /*!< Clear enbl amp */ + TFA2_BF_ICLAREFS= 0x44d0, /*!< Clear ref enable */ + TFA2_BF_ICLADCCR= 0x44e0, /*!< Clear control ADC */ + TFA2_BF_ICLBODNOK= 0x44f0, /*!< Clear BOD */ + TFA2_BF_ICLBSTCU= 0x4500, /*!< Clear DCDC current limiting */ + TFA2_BF_ICLBSTHI= 0x4510, /*!< Clear DCDC active */ + TFA2_BF_ICLBSTOC= 0x4520, /*!< Clear DCDC OCP */ + TFA2_BF_ICLBSTPC= 0x4530, /*!< Clear bst peakcur */ + TFA2_BF_ICLBSTVC= 0x4540, /*!< Clear DCDC level 1x */ + TFA2_BF_ICLBST86= 0x4550, /*!< Clear DCDC level 1.14x */ + TFA2_BF_ICLBST93= 0x4560, /*!< Clear DCDC level 1.07x */ + TFA2_BF_ICLRCVLD= 0x4570, /*!< Clear rcvldop ready */ + TFA2_BF_ICLOCPL= 0x4580, /*!< Clear ocp alarm left */ + TFA2_BF_ICLOCPR= 0x4590, /*!< Clear ocp alarm right */ + TFA2_BF_ICLMWSRC= 0x45a0, /*!< Clear wait HW I2C settings */ + TFA2_BF_ICLMWCFC= 0x45b0, /*!< Clear wait cf config */ + TFA2_BF_ICLMWSMU= 0x45c0, /*!< Clear audio mute sequence */ + TFA2_BF_ICLCFMER= 0x45d0, /*!< Clear cfma err */ + TFA2_BF_ICLCFMAC= 0x45e0, /*!< Clear cfma ack */ + TFA2_BF_ICLCLKOOR= 0x45f0, /*!< Clear flag_clk_out_of_range */ + TFA2_BF_ICLTDMER= 0x4600, /*!< Clear tdm error */ + TFA2_BF_ICLCLPL= 0x4610, /*!< Clear clip left */ + TFA2_BF_ICLCLPR= 0x4620, /*!< Clear clip right */ + TFA2_BF_ICLOCPM= 0x4630, /*!< Clear mic ocpok */ + TFA2_BF_IEVDDS= 0x4800, /*!< Enable por */ + TFA2_BF_IEPLLS= 0x4810, /*!< Enable pll lock */ + TFA2_BF_IEOTDS= 0x4820, /*!< Enable OTP alarm */ + TFA2_BF_IEOVDS= 0x4830, /*!< Enable OVP alarm */ + TFA2_BF_IEUVDS= 0x4840, /*!< Enable UVP alarm */ + TFA2_BF_IECLKS= 0x4850, /*!< Enable clocks stable */ + TFA2_BF_IEMTPB= 0x4860, /*!< Enable mtp busy */ + TFA2_BF_IENOCLK= 0x4870, /*!< Enable lost clk */ + TFA2_BF_IESPKS= 0x4880, /*!< Enable speaker error */ + TFA2_BF_IEACS = 0x4890, /*!< Enable cold started */ + TFA2_BF_IESWS = 0x48a0, /*!< Enable amplifier engage */ + TFA2_BF_IEWDS = 0x48b0, /*!< Enable watchdog */ + TFA2_BF_IEAMPS= 0x48c0, /*!< Enable enbl amp */ + TFA2_BF_IEAREFS= 0x48d0, /*!< Enable ref enable */ + TFA2_BF_IEADCCR= 0x48e0, /*!< Enable Control ADC */ + TFA2_BF_IEBODNOK= 0x48f0, /*!< Enable BOD */ + TFA2_BF_IEBSTCU= 0x4900, /*!< Enable DCDC current limiting */ + TFA2_BF_IEBSTHI= 0x4910, /*!< Enable DCDC active */ + TFA2_BF_IEBSTOC= 0x4920, /*!< Enable DCDC OCP */ + TFA2_BF_IEBSTPC= 0x4930, /*!< Enable bst peakcur */ + TFA2_BF_IEBSTVC= 0x4940, /*!< Enable DCDC level 1x */ + TFA2_BF_IEBST86= 0x4950, /*!< Enable DCDC level 1.14x */ + TFA2_BF_IEBST93= 0x4960, /*!< Enable DCDC level 1.07x */ + TFA2_BF_IERCVLD= 0x4970, /*!< Enable rcvldop ready */ + TFA2_BF_IEOCPL= 0x4980, /*!< Enable ocp alarm left */ + TFA2_BF_IEOCPR= 0x4990, /*!< Enable ocp alarm right */ + TFA2_BF_IEMWSRC= 0x49a0, /*!< Enable waits HW I2C settings */ + TFA2_BF_IEMWCFC= 0x49b0, /*!< Enable man wait cf config */ + TFA2_BF_IEMWSMU= 0x49c0, /*!< Enable man Audio mute sequence */ + TFA2_BF_IECFMER= 0x49d0, /*!< Enable cfma err */ + TFA2_BF_IECFMAC= 0x49e0, /*!< Enable cfma ack */ + TFA2_BF_IECLKOOR= 0x49f0, /*!< Enable flag_clk_out_of_range */ + TFA2_BF_IETDMER= 0x4a00, /*!< Enable tdm error */ + TFA2_BF_IECLPL= 0x4a10, /*!< Enable clip left */ + TFA2_BF_IECLPR= 0x4a20, /*!< Enable clip right */ + TFA2_BF_IEOCPM1= 0x4a30, /*!< Enable mic ocpok */ + TFA2_BF_IPOVDDS= 0x4c00, /*!< Polarity por */ + TFA2_BF_IPOPLLS= 0x4c10, /*!< Polarity pll lock */ + TFA2_BF_IPOOTDS= 0x4c20, /*!< Polarity OTP alarm */ + TFA2_BF_IPOOVDS= 0x4c30, /*!< Polarity OVP alarm */ + TFA2_BF_IPOUVDS= 0x4c40, /*!< Polarity UVP alarm */ + TFA2_BF_IPOCLKS= 0x4c50, /*!< Polarity clocks stable */ + TFA2_BF_IPOMTPB= 0x4c60, /*!< Polarity mtp busy */ + TFA2_BF_IPONOCLK= 0x4c70, /*!< Polarity lost clk */ + TFA2_BF_IPOSPKS= 0x4c80, /*!< Polarity speaker error */ + TFA2_BF_IPOACS= 0x4c90, /*!< Polarity cold started */ + TFA2_BF_IPOSWS= 0x4ca0, /*!< Polarity amplifier engage */ + TFA2_BF_IPOWDS= 0x4cb0, /*!< Polarity watchdog */ + TFA2_BF_IPOAMPS= 0x4cc0, /*!< Polarity enbl amp */ + TFA2_BF_IPOAREFS= 0x4cd0, /*!< Polarity ref enable */ + TFA2_BF_IPOADCCR= 0x4ce0, /*!< Polarity Control ADC */ + TFA2_BF_IPOBODNOK= 0x4cf0, /*!< Polarity BOD */ + TFA2_BF_IPOBSTCU= 0x4d00, /*!< Polarity DCDC current limiting */ + TFA2_BF_IPOBSTHI= 0x4d10, /*!< Polarity DCDC active */ + TFA2_BF_IPOBSTOC= 0x4d20, /*!< Polarity DCDC OCP */ + TFA2_BF_IPOBSTPC= 0x4d30, /*!< Polarity bst peakcur */ + TFA2_BF_IPOBSTVC= 0x4d40, /*!< Polarity DCDC level 1x */ + TFA2_BF_IPOBST86= 0x4d50, /*!< Polarity DCDC level 1.14x */ + TFA2_BF_IPOBST93= 0x4d60, /*!< Polarity DCDC level 1.07x */ + TFA2_BF_IPORCVLD= 0x4d70, /*!< Polarity rcvldop ready */ + TFA2_BF_IPOOCPL= 0x4d80, /*!< Polarity ocp alarm left */ + TFA2_BF_IPOOCPR= 0x4d90, /*!< Polarity ocp alarm right */ + TFA2_BF_IPOMWSRC= 0x4da0, /*!< Polarity waits HW I2C settings */ + TFA2_BF_IPOMWCFC= 0x4db0, /*!< Polarity man wait cf config */ + TFA2_BF_IPOMWSMU= 0x4dc0, /*!< Polarity man audio mute sequence */ + TFA2_BF_IPOCFMER= 0x4dd0, /*!< Polarity cfma err */ + TFA2_BF_IPOCFMAC= 0x4de0, /*!< Polarity cfma ack */ + TFA2_BF_IPCLKOOR= 0x4df0, /*!< Polarity flag_clk_out_of_range */ + TFA2_BF_IPOTDMER= 0x4e00, /*!< Polarity tdm error */ + TFA2_BF_IPOCLPL= 0x4e10, /*!< Polarity clip left */ + TFA2_BF_IPOCLPR= 0x4e20, /*!< Polarity clip right */ + TFA2_BF_IPOOCPM= 0x4e30, /*!< Polarity mic ocpok */ + TFA2_BF_BSSCR = 0x5001, /*!< Battery protection attack Time */ + TFA2_BF_BSST = 0x5023, /*!< Battery protection threshold voltage level */ + TFA2_BF_BSSRL = 0x5061, /*!< Battery protection maximum reduction */ + TFA2_BF_BSSRR = 0x5082, /*!< Battery protection release time */ + TFA2_BF_BSSHY = 0x50b1, /*!< Battery protection hysteresis */ + TFA2_BF_BSSR = 0x50e0, /*!< Battery voltage read out */ + TFA2_BF_BSSBY = 0x50f0, /*!< Bypass HW clipper */ + TFA2_BF_BSSS = 0x5100, /*!< Vbat prot steepness */ + TFA2_BF_INTSMUTE= 0x5110, /*!< Soft mute HW */ + TFA2_BF_CFSML = 0x5120, /*!< Soft mute FW left */ + TFA2_BF_CFSMR = 0x5130, /*!< Soft mute FW right */ + TFA2_BF_HPFBYPL= 0x5140, /*!< Bypass HPF left */ + TFA2_BF_HPFBYPR= 0x5150, /*!< Bypass HPF right */ + TFA2_BF_DPSAL = 0x5160, /*!< Enable DPSA left */ + TFA2_BF_DPSAR = 0x5170, /*!< Enable DPSA right */ + TFA2_BF_VOL = 0x5187, /*!< FW volume control for primary audio channel */ + TFA2_BF_HNDSFRCV= 0x5200, /*!< Selection receiver */ + TFA2_BF_CLIPCTRL= 0x5222, /*!< Clip control setting */ + TFA2_BF_AMPGAIN= 0x5257, /*!< Amplifier gain */ + TFA2_BF_SLOPEE= 0x52d0, /*!< Enables slope control */ + TFA2_BF_SLOPESET= 0x52e1, /*!< Set slope */ + TFA2_BF_VOLSEC= 0x5a07, /*!< FW volume control for secondary audio channel */ + TFA2_BF_SWPROFIL= 0x5a87, /*!< Software profile data */ + TFA2_BF_DCVO = 0x7002, /*!< Boost voltage */ + TFA2_BF_DCMCC = 0x7033, /*!< Max coil current */ + TFA2_BF_DCCV = 0x7071, /*!< Coil Value */ + TFA2_BF_DCIE = 0x7090, /*!< Adaptive boost mode */ + TFA2_BF_DCSR = 0x70a0, /*!< Soft ramp up/down */ + TFA2_BF_DCSYNCP= 0x70b2, /*!< DCDC synchronization off + 7 positions */ + TFA2_BF_DCDIS = 0x70e0, /*!< DCDC on/off */ + TFA2_BF_RST = 0x9000, /*!< Reset */ + TFA2_BF_DMEM = 0x9011, /*!< Target memory */ + TFA2_BF_AIF = 0x9030, /*!< Auto increment */ + TFA2_BF_CFINT = 0x9040, /*!< Interrupt - auto clear */ + TFA2_BF_CFCGATE= 0x9050, /*!< Coolflux clock gating disabling control */ + TFA2_BF_REQ = 0x9087, /*!< request for access (8 channels) */ + TFA2_BF_REQCMD= 0x9080, /*!< Firmware event request rpc command */ + TFA2_BF_REQRST= 0x9090, /*!< Firmware event request reset restart */ + TFA2_BF_REQMIPS= 0x90a0, /*!< Firmware event request short on mips */ + TFA2_BF_REQMUTED= 0x90b0, /*!< Firmware event request mute sequence ready */ + TFA2_BF_REQVOL= 0x90c0, /*!< Firmware event request volume ready */ + TFA2_BF_REQDMG= 0x90d0, /*!< Firmware event request speaker damage detected */ + TFA2_BF_REQCAL= 0x90e0, /*!< Firmware event request calibration completed */ + TFA2_BF_REQRSV= 0x90f0, /*!< Firmware event request reserved */ + TFA2_BF_MADD = 0x910f, /*!< Memory address */ + TFA2_BF_MEMA = 0x920f, /*!< Activate memory access */ + TFA2_BF_ERR = 0x9307, /*!< Error flags */ + TFA2_BF_ACK = 0x9387, /*!< Acknowledge of requests */ + TFA2_BF_ACKCMD= 0x9380, /*!< Firmware event acknowledge rpc command */ + TFA2_BF_ACKRST= 0x9390, /*!< Firmware event acknowledge reset restart */ + TFA2_BF_ACKMIPS= 0x93a0, /*!< Firmware event acknowledge short on mips */ + TFA2_BF_ACKMUTED= 0x93b0, /*!< Firmware event acknowledge mute sequence ready */ + TFA2_BF_ACKVOL= 0x93c0, /*!< Firmware event acknowledge volume ready */ + TFA2_BF_ACKDMG= 0x93d0, /*!< Firmware event acknowledge speaker damage detected */ + TFA2_BF_ACKCAL= 0x93e0, /*!< Firmware event acknowledge calibration completed */ + TFA2_BF_ACKRSV= 0x93f0, /*!< Firmware event acknowledge reserved */ + TFA2_BF_MTPK = 0xa107, /*!< MTP KEY2 register */ + TFA2_BF_KEY1LOCKED= 0xa200, /*!< Indicates KEY1 is locked */ + TFA2_BF_KEY2LOCKED= 0xa210, /*!< Indicates KEY2 is locked */ + TFA2_BF_CIMTP = 0xa360, /*!< Start copying data from I2C mtp registers to mtp */ + TFA2_BF_MTPRDMSB= 0xa50f, /*!< MSB word of MTP manual read data */ + TFA2_BF_MTPRDLSB= 0xa60f, /*!< LSB word of MTP manual read data */ + TFA2_BF_EXTTS = 0xb108, /*!< External temperature (C) */ + TFA2_BF_TROS = 0xb190, /*!< Select temp Speaker calibration */ + TFA2_BF_MTPOTC= 0xf000, /*!< Calibration schedule */ + TFA2_BF_MTPEX = 0xf010, /*!< Calibration Ron executed */ + TFA2_BF_DCMCCAPI= 0xf020, /*!< Calibration current limit DCDC */ + TFA2_BF_DCMCCSB= 0xf030, /*!< Sign bit for delta calibration current limit DCDC */ + TFA2_BF_USERDEF= 0xf042, /*!< Calibration delta current limit DCDC */ + TFA2_BF_R25CL = 0xf40f, /*!< Ron resistance of left channel speaker coil */ + TFA2_BF_R25CR = 0xf50f, /*!< Ron resistance of right channel speaker coil */ +} nxpTfa2BfEnumList_t; +#define TFA2_NAMETABLE static tfaBfName_t Tfa2DatasheetNames[]= {\ + { 0x0, "PWDN"}, /* Powerdown selection , */\ + { 0x10, "I2CR"}, /* I2C Reset - Auto clear , */\ + { 0x20, "CFE"}, /* Enable CoolFlux , */\ + { 0x30, "AMPE"}, /* Activate Amplifier , */\ + { 0x40, "DCA"}, /* Activate DC-to-DC converter , */\ + { 0x50, "SBSL"}, /* Coolflux configured , */\ + { 0x60, "AMPC"}, /* CoolFlux controls amplifier , */\ + { 0x71, "INTP"}, /* Interrupt config , */\ + { 0x91, "FSSSEL"}, /* Audio sample reference , */\ + { 0xb0, "BYPOCP"}, /* Bypass OCP , */\ + { 0xc0, "TSTOCP"}, /* OCP testing control , */\ + { 0x101, "AMPINSEL"}, /* Amplifier input selection , */\ + { 0x120, "MANSCONF"}, /* I2C configured , */\ + { 0x130, "MANCOLD"}, /* Execute cold start , */\ + { 0x140, "MANAOOSC"}, /* Internal osc off at PWDN , */\ + { 0x150, "MANROBOD"}, /* Reaction on BOD , */\ + { 0x160, "BODE"}, /* BOD Enable , */\ + { 0x170, "BODHYS"}, /* BOD Hysteresis , */\ + { 0x181, "BODFILT"}, /* BOD filter , */\ + { 0x1a1, "BODTHLVL"}, /* BOD threshold , */\ + { 0x1d0, "MUTETO"}, /* Time out SB mute sequence , */\ + { 0x1e0, "RCVNS"}, /* Noise shaper selection , */\ + { 0x1f0, "MANWDE"}, /* Watchdog manager reaction , */\ + { 0x203, "AUDFS"}, /* Sample rate (fs) , */\ + { 0x240, "INPLEV"}, /* TDM output attenuation , */\ + { 0x255, "FRACTDEL"}, /* V/I Fractional delay , */\ + { 0x2b0, "BYPHVBF"}, /* Bypass HVBAT filter , */\ + { 0x2c0, "LDOBYP"}, /* Receiver LDO bypass , */\ + { 0x30f, "REV"}, /* Revision info , */\ + { 0x401, "REFCKEXT"}, /* PLL external ref clock , */\ + { 0x420, "REFCKSEL"}, /* PLL internal ref clock , */\ + { 0x500, "SSLEFTE"}, /* Enable left channel , */\ + { 0x510, "SSRIGHTE"}, /* Enable right channel , */\ + { 0x520, "VSLEFTE"}, /* Voltage sense left , */\ + { 0x530, "VSRIGHTE"}, /* Voltage sense right , */\ + { 0x540, "CSLEFTE"}, /* Current sense left , */\ + { 0x550, "CSRIGHTE"}, /* Current sense right , */\ + { 0x560, "SSPDME"}, /* Sub-system PDM , */\ + { 0xd18, "STGAIN"}, /* Side tone gain , */\ + { 0xda0, "PDMSMUTE"}, /* Side tone soft mute , */\ + { 0xe06, "SWVSTEP"}, /* Register for the host SW to record the current active vstep, */\ + { 0x1000, "VDDS"}, /* POR , */\ + { 0x1010, "PLLS"}, /* PLL lock , */\ + { 0x1020, "OTDS"}, /* OTP alarm , */\ + { 0x1030, "OVDS"}, /* OVP alarm , */\ + { 0x1040, "UVDS"}, /* UVP alarm , */\ + { 0x1050, "CLKS"}, /* Clocks stable , */\ + { 0x1060, "MTPB"}, /* MTP busy , */\ + { 0x1070, "NOCLK"}, /* Lost clock , */\ + { 0x1080, "SPKS"}, /* Speaker error , */\ + { 0x1090, "ACS"}, /* Cold Start , */\ + { 0x10a0, "SWS"}, /* Amplifier engage , */\ + { 0x10b0, "WDS"}, /* Watchdog , */\ + { 0x10c0, "AMPS"}, /* Amplifier enable , */\ + { 0x10d0, "AREFS"}, /* References enable , */\ + { 0x10e0, "ADCCR"}, /* Control ADC , */\ + { 0x10f0, "BODNOK"}, /* BOD , */\ + { 0x1100, "DCIL"}, /* DCDC current limiting , */\ + { 0x1110, "DCDCA"}, /* DCDC active , */\ + { 0x1120, "DCOCPOK"}, /* DCDC OCP nmos , */\ + { 0x1140, "DCHVBAT"}, /* DCDC level 1x , */\ + { 0x1150, "DCH114"}, /* DCDC level 1.14x , */\ + { 0x1160, "DCH107"}, /* DCDC level 1.07x , */\ + { 0x1170, "STMUTEB"}, /* side tone (un)mute busy , */\ + { 0x1180, "STMUTE"}, /* side tone mute state , */\ + { 0x1190, "TDMLUTER"}, /* TDM LUT error , */\ + { 0x11a2, "TDMSTAT"}, /* TDM status bits , */\ + { 0x11d0, "TDMERR"}, /* TDM error , */\ + { 0x11e0, "HAPTIC"}, /* Status haptic driver , */\ + { 0x1200, "OCPOAPL"}, /* OCPOK pmos A left , */\ + { 0x1210, "OCPOANL"}, /* OCPOK nmos A left , */\ + { 0x1220, "OCPOBPL"}, /* OCPOK pmos B left , */\ + { 0x1230, "OCPOBNL"}, /* OCPOK nmos B left , */\ + { 0x1240, "CLIPAHL"}, /* Clipping A left to Vddp , */\ + { 0x1250, "CLIPALL"}, /* Clipping A left to gnd , */\ + { 0x1260, "CLIPBHL"}, /* Clipping B left to Vddp , */\ + { 0x1270, "CLIPBLL"}, /* Clipping B left to gnd , */\ + { 0x1280, "OCPOAPRC"}, /* OCPOK pmos A RCV , */\ + { 0x1290, "OCPOANRC"}, /* OCPOK nmos A RCV , */\ + { 0x12a0, "OCPOBPRC"}, /* OCPOK pmos B RCV , */\ + { 0x12b0, "OCPOBNRC"}, /* OCPOK nmos B RCV , */\ + { 0x12c0, "RCVLDOR"}, /* RCV LDO regulates , */\ + { 0x12d0, "RCVLDOBR"}, /* Receiver LDO ready , */\ + { 0x12e0, "OCDSL"}, /* OCP left amplifier , */\ + { 0x12f0, "CLIPSL"}, /* Amplifier left clipping , */\ + { 0x1300, "OCPOAPR"}, /* OCPOK pmos A right , */\ + { 0x1310, "OCPOANR"}, /* OCPOK nmos A right , */\ + { 0x1320, "OCPOBPR"}, /* OCPOK pmos B right , */\ + { 0x1330, "OCPOBNR"}, /* OCPOK nmos B right , */\ + { 0x1340, "CLIPAHR"}, /* Clipping A right to Vddp , */\ + { 0x1350, "CLIPALR"}, /* Clipping A right to gnd , */\ + { 0x1360, "CLIPBHR"}, /* Clipping B left to Vddp , */\ + { 0x1370, "CLIPBLR"}, /* Clipping B right to gnd , */\ + { 0x1380, "OCDSR"}, /* OCP right amplifier , */\ + { 0x1390, "CLIPSR"}, /* Amplifier right clipping , */\ + { 0x13a0, "OCPOKMC"}, /* OCPOK MICVDD , */\ + { 0x13b0, "MANALARM"}, /* Alarm state , */\ + { 0x13c0, "MANWAIT1"}, /* Wait HW I2C settings , */\ + { 0x13d0, "MANWAIT2"}, /* Wait CF config , */\ + { 0x13e0, "MANMUTE"}, /* Audio mute sequence , */\ + { 0x13f0, "MANOPER"}, /* Operating state , */\ + { 0x1400, "SPKSL"}, /* Left speaker status , */\ + { 0x1410, "SPKSR"}, /* Right speaker status , */\ + { 0x1420, "CLKOOR"}, /* External clock status , */\ + { 0x1433, "MANSTATE"}, /* Device manager status , */\ + { 0x1509, "BATS"}, /* Battery voltage (V) , */\ + { 0x1608, "TEMPS"}, /* IC Temperature (C) , */\ + { 0x2003, "TDMUC"}, /* Usecase setting , */\ + { 0x2040, "TDME"}, /* Enable interface , */\ + { 0x2050, "TDMMODE"}, /* Slave/master , */\ + { 0x2060, "TDMCLINV"}, /* Reception data to BCK clock , */\ + { 0x2073, "TDMFSLN"}, /* FS length (master mode only) , */\ + { 0x20b0, "TDMFSPOL"}, /* FS polarity , */\ + { 0x20c3, "TDMNBCK"}, /* N-BCK's in FS , */\ + { 0x2103, "TDMSLOTS"}, /* N-slots in Frame , */\ + { 0x2144, "TDMSLLN"}, /* N-bits in slot , */\ + { 0x2194, "TDMBRMG"}, /* N-bits remaining , */\ + { 0x21e0, "TDMDEL"}, /* data delay to FS , */\ + { 0x21f0, "TDMADJ"}, /* data adjustment , */\ + { 0x2201, "TDMOOMP"}, /* Received audio compression , */\ + { 0x2224, "TDMSSIZE"}, /* Sample size per slot , */\ + { 0x2271, "TDMTXDFO"}, /* Format unused bits , */\ + { 0x2291, "TDMTXUS0"}, /* Format unused slots GAINIO , */\ + { 0x22b1, "TDMTXUS1"}, /* Format unused slots DIO1 , */\ + { 0x22d1, "TDMTXUS2"}, /* Format unused slots DIO2 , */\ + { 0x2310, "TDMLE"}, /* Control audio left , */\ + { 0x2320, "TDMRE"}, /* Control audio right , */\ + { 0x2340, "TDMVSRE"}, /* Control voltage sense right , */\ + { 0x2350, "TDMCSRE"}, /* Control current sense right , */\ + { 0x2360, "TDMVSLE"}, /* Voltage sense left control , */\ + { 0x2370, "TDMCSLE"}, /* Current sense left control , */\ + { 0x2380, "TDMCFRE"}, /* DSP out right control , */\ + { 0x2390, "TDMCFLE"}, /* DSP out left control , */\ + { 0x23a0, "TDMCF3E"}, /* AEC ref left control , */\ + { 0x23b0, "TDMCF4E"}, /* AEC ref right control , */\ + { 0x23c0, "TDMPD1E"}, /* PDM 1 control , */\ + { 0x23d0, "TDMPD2E"}, /* PDM 2 control , */\ + { 0x2421, "TDMLIO"}, /* IO audio left , */\ + { 0x2441, "TDMRIO"}, /* IO audio right , */\ + { 0x2481, "TDMVSRIO"}, /* IO voltage sense right , */\ + { 0x24a1, "TDMCSRIO"}, /* IO current sense right , */\ + { 0x24c1, "TDMVSLIO"}, /* IO voltage sense left , */\ + { 0x24e1, "TDMCSLIO"}, /* IO current sense left , */\ + { 0x2501, "TDMCFRIO"}, /* IO dspout right , */\ + { 0x2521, "TDMCFLIO"}, /* IO dspout left , */\ + { 0x2541, "TDMCF3IO"}, /* IO AEC ref left control , */\ + { 0x2561, "TDMCF4IO"}, /* IO AEC ref right control , */\ + { 0x2581, "TDMPD1IO"}, /* IO pdm1 , */\ + { 0x25a1, "TDMPD2IO"}, /* IO pdm2 , */\ + { 0x2643, "TDMLS"}, /* Position audio left , */\ + { 0x2683, "TDMRS"}, /* Position audio right , */\ + { 0x2703, "TDMVSRS"}, /* Position voltage sense right , */\ + { 0x2743, "TDMCSRS"}, /* Position current sense right , */\ + { 0x2783, "TDMVSLS"}, /* Position voltage sense left , */\ + { 0x27c3, "TDMCSLS"}, /* Position current sense left , */\ + { 0x2803, "TDMCFRS"}, /* Position dspout right , */\ + { 0x2843, "TDMCFLS"}, /* Position dspout left , */\ + { 0x2883, "TDMCF3S"}, /* Position AEC ref left control , */\ + { 0x28c3, "TDMCF4S"}, /* Position AEC ref right control , */\ + { 0x2903, "TDMPD1S"}, /* Position pdm1 , */\ + { 0x2943, "TDMPD2S"}, /* Position pdm2 , */\ + { 0x3100, "PDMSM"}, /* PDM control , */\ + { 0x3111, "PDMSTSEL"}, /* Side tone input , */\ + { 0x3130, "PDMLSEL"}, /* PDM data selection for left channel during PDM direct mode, */\ + { 0x3140, "PDMRSEL"}, /* PDM data selection for right channel during PDM direct mode, */\ + { 0x3150, "MICVDDE"}, /* Enable MICVDD , */\ + { 0x3201, "PDMCLRAT"}, /* PDM BCK/Fs ratio , */\ + { 0x3223, "PDMGAIN"}, /* PDM gain , */\ + { 0x3263, "PDMOSEL"}, /* PDM output selection - RE/FE data combination , */\ + { 0x32a0, "SELCFHAPD"}, /* Select the source for haptic data output (not for customer), */\ + { 0x3307, "HAPTIME"}, /* Duration (ms) , */\ + { 0x3387, "HAPLEVEL"}, /* DC value (FFS) , */\ + { 0x3403, "GPIODIN"}, /* Receiving value , */\ + { 0x3500, "GPIOCTRL"}, /* GPIO master control over GPIO1/2 ports (not for customer), */\ + { 0x3513, "GPIOCONF"}, /* Configuration , */\ + { 0x3553, "GPIODOUT"}, /* Transmitting value , */\ + { 0x4000, "ISTVDDS"}, /* Status POR , */\ + { 0x4010, "ISTPLLS"}, /* Status PLL lock , */\ + { 0x4020, "ISTOTDS"}, /* Status OTP alarm , */\ + { 0x4030, "ISTOVDS"}, /* Status OVP alarm , */\ + { 0x4040, "ISTUVDS"}, /* Status UVP alarm , */\ + { 0x4050, "ISTCLKS"}, /* Status clocks stable , */\ + { 0x4060, "ISTMTPB"}, /* Status MTP busy , */\ + { 0x4070, "ISTNOCLK"}, /* Status lost clock , */\ + { 0x4080, "ISTSPKS"}, /* Status speaker error , */\ + { 0x4090, "ISTACS"}, /* Status cold start , */\ + { 0x40a0, "ISTSWS"}, /* Status amplifier engage , */\ + { 0x40b0, "ISTWDS"}, /* Status watchdog , */\ + { 0x40c0, "ISTAMPS"}, /* Status amplifier enable , */\ + { 0x40d0, "ISTAREFS"}, /* Status Ref enable , */\ + { 0x40e0, "ISTADCCR"}, /* Status Control ADC , */\ + { 0x40f0, "ISTBODNOK"}, /* Status BOD , */\ + { 0x4100, "ISTBSTCU"}, /* Status DCDC current limiting , */\ + { 0x4110, "ISTBSTHI"}, /* Status DCDC active , */\ + { 0x4120, "ISTBSTOC"}, /* Status DCDC OCP , */\ + { 0x4130, "ISTBSTPKCUR"}, /* Status bst peakcur , */\ + { 0x4140, "ISTBSTVC"}, /* Status DCDC level 1x , */\ + { 0x4150, "ISTBST86"}, /* Status DCDC level 1.14x , */\ + { 0x4160, "ISTBST93"}, /* Status DCDC level 1.07x , */\ + { 0x4170, "ISTRCVLD"}, /* Status rcvldop ready , */\ + { 0x4180, "ISTOCPL"}, /* Status ocp alarm left , */\ + { 0x4190, "ISTOCPR"}, /* Status ocp alarm right , */\ + { 0x41a0, "ISTMWSRC"}, /* Status Waits HW I2C settings , */\ + { 0x41b0, "ISTMWCFC"}, /* Status waits CF config , */\ + { 0x41c0, "ISTMWSMU"}, /* Status Audio mute sequence , */\ + { 0x41d0, "ISTCFMER"}, /* Status cfma error , */\ + { 0x41e0, "ISTCFMAC"}, /* Status cfma ack , */\ + { 0x41f0, "ISTCLKOOR"}, /* Status flag_clk_out_of_range , */\ + { 0x4200, "ISTTDMER"}, /* Status tdm error , */\ + { 0x4210, "ISTCLPL"}, /* Status clip left , */\ + { 0x4220, "ISTCLPR"}, /* Status clip right , */\ + { 0x4230, "ISTOCPM"}, /* Status mic ocpok , */\ + { 0x4400, "ICLVDDS"}, /* Clear POR , */\ + { 0x4410, "ICLPLLS"}, /* Clear PLL lock , */\ + { 0x4420, "ICLOTDS"}, /* Clear OTP alarm , */\ + { 0x4430, "ICLOVDS"}, /* Clear OVP alarm , */\ + { 0x4440, "ICLUVDS"}, /* Clear UVP alarm , */\ + { 0x4450, "ICLCLKS"}, /* Clear clocks stable , */\ + { 0x4460, "ICLMTPB"}, /* Clear mtp busy , */\ + { 0x4470, "ICLNOCLK"}, /* Clear lost clk , */\ + { 0x4480, "ICLSPKS"}, /* Clear speaker error , */\ + { 0x4490, "ICLACS"}, /* Clear cold started , */\ + { 0x44a0, "ICLSWS"}, /* Clear amplifier engage , */\ + { 0x44b0, "ICLWDS"}, /* Clear watchdog , */\ + { 0x44c0, "ICLAMPS"}, /* Clear enbl amp , */\ + { 0x44d0, "ICLAREFS"}, /* Clear ref enable , */\ + { 0x44e0, "ICLADCCR"}, /* Clear control ADC , */\ + { 0x44f0, "ICLBODNOK"}, /* Clear BOD , */\ + { 0x4500, "ICLBSTCU"}, /* Clear DCDC current limiting , */\ + { 0x4510, "ICLBSTHI"}, /* Clear DCDC active , */\ + { 0x4520, "ICLBSTOC"}, /* Clear DCDC OCP , */\ + { 0x4530, "ICLBSTPC"}, /* Clear bst peakcur , */\ + { 0x4540, "ICLBSTVC"}, /* Clear DCDC level 1x , */\ + { 0x4550, "ICLBST86"}, /* Clear DCDC level 1.14x , */\ + { 0x4560, "ICLBST93"}, /* Clear DCDC level 1.07x , */\ + { 0x4570, "ICLRCVLD"}, /* Clear rcvldop ready , */\ + { 0x4580, "ICLOCPL"}, /* Clear ocp alarm left , */\ + { 0x4590, "ICLOCPR"}, /* Clear ocp alarm right , */\ + { 0x45a0, "ICLMWSRC"}, /* Clear wait HW I2C settings , */\ + { 0x45b0, "ICLMWCFC"}, /* Clear wait cf config , */\ + { 0x45c0, "ICLMWSMU"}, /* Clear audio mute sequence , */\ + { 0x45d0, "ICLCFMER"}, /* Clear cfma err , */\ + { 0x45e0, "ICLCFMAC"}, /* Clear cfma ack , */\ + { 0x45f0, "ICLCLKOOR"}, /* Clear flag_clk_out_of_range , */\ + { 0x4600, "ICLTDMER"}, /* Clear tdm error , */\ + { 0x4610, "ICLCLPL"}, /* Clear clip left , */\ + { 0x4620, "ICLCLPR"}, /* Clear clip right , */\ + { 0x4630, "ICLOCPM"}, /* Clear mic ocpok , */\ + { 0x4800, "IEVDDS"}, /* Enable por , */\ + { 0x4810, "IEPLLS"}, /* Enable pll lock , */\ + { 0x4820, "IEOTDS"}, /* Enable OTP alarm , */\ + { 0x4830, "IEOVDS"}, /* Enable OVP alarm , */\ + { 0x4840, "IEUVDS"}, /* Enable UVP alarm , */\ + { 0x4850, "IECLKS"}, /* Enable clocks stable , */\ + { 0x4860, "IEMTPB"}, /* Enable mtp busy , */\ + { 0x4870, "IENOCLK"}, /* Enable lost clk , */\ + { 0x4880, "IESPKS"}, /* Enable speaker error , */\ + { 0x4890, "IEACS"}, /* Enable cold started , */\ + { 0x48a0, "IESWS"}, /* Enable amplifier engage , */\ + { 0x48b0, "IEWDS"}, /* Enable watchdog , */\ + { 0x48c0, "IEAMPS"}, /* Enable enbl amp , */\ + { 0x48d0, "IEAREFS"}, /* Enable ref enable , */\ + { 0x48e0, "IEADCCR"}, /* Enable Control ADC , */\ + { 0x48f0, "IEBODNOK"}, /* Enable BOD , */\ + { 0x4900, "IEBSTCU"}, /* Enable DCDC current limiting , */\ + { 0x4910, "IEBSTHI"}, /* Enable DCDC active , */\ + { 0x4920, "IEBSTOC"}, /* Enable DCDC OCP , */\ + { 0x4930, "IEBSTPC"}, /* Enable bst peakcur , */\ + { 0x4940, "IEBSTVC"}, /* Enable DCDC level 1x , */\ + { 0x4950, "IEBST86"}, /* Enable DCDC level 1.14x , */\ + { 0x4960, "IEBST93"}, /* Enable DCDC level 1.07x , */\ + { 0x4970, "IERCVLD"}, /* Enable rcvldop ready , */\ + { 0x4980, "IEOCPL"}, /* Enable ocp alarm left , */\ + { 0x4990, "IEOCPR"}, /* Enable ocp alarm right , */\ + { 0x49a0, "IEMWSRC"}, /* Enable waits HW I2C settings , */\ + { 0x49b0, "IEMWCFC"}, /* Enable man wait cf config , */\ + { 0x49c0, "IEMWSMU"}, /* Enable man Audio mute sequence , */\ + { 0x49d0, "IECFMER"}, /* Enable cfma err , */\ + { 0x49e0, "IECFMAC"}, /* Enable cfma ack , */\ + { 0x49f0, "IECLKOOR"}, /* Enable flag_clk_out_of_range , */\ + { 0x4a00, "IETDMER"}, /* Enable tdm error , */\ + { 0x4a10, "IECLPL"}, /* Enable clip left , */\ + { 0x4a20, "IECLPR"}, /* Enable clip right , */\ + { 0x4a30, "IEOCPM1"}, /* Enable mic ocpok , */\ + { 0x4c00, "IPOVDDS"}, /* Polarity por , */\ + { 0x4c10, "IPOPLLS"}, /* Polarity pll lock , */\ + { 0x4c20, "IPOOTDS"}, /* Polarity OTP alarm , */\ + { 0x4c30, "IPOOVDS"}, /* Polarity OVP alarm , */\ + { 0x4c40, "IPOUVDS"}, /* Polarity UVP alarm , */\ + { 0x4c50, "IPOCLKS"}, /* Polarity clocks stable , */\ + { 0x4c60, "IPOMTPB"}, /* Polarity mtp busy , */\ + { 0x4c70, "IPONOCLK"}, /* Polarity lost clk , */\ + { 0x4c80, "IPOSPKS"}, /* Polarity speaker error , */\ + { 0x4c90, "IPOACS"}, /* Polarity cold started , */\ + { 0x4ca0, "IPOSWS"}, /* Polarity amplifier engage , */\ + { 0x4cb0, "IPOWDS"}, /* Polarity watchdog , */\ + { 0x4cc0, "IPOAMPS"}, /* Polarity enbl amp , */\ + { 0x4cd0, "IPOAREFS"}, /* Polarity ref enable , */\ + { 0x4ce0, "IPOADCCR"}, /* Polarity Control ADC , */\ + { 0x4cf0, "IPOBODNOK"}, /* Polarity BOD , */\ + { 0x4d00, "IPOBSTCU"}, /* Polarity DCDC current limiting , */\ + { 0x4d10, "IPOBSTHI"}, /* Polarity DCDC active , */\ + { 0x4d20, "IPOBSTOC"}, /* Polarity DCDC OCP , */\ + { 0x4d30, "IPOBSTPC"}, /* Polarity bst peakcur , */\ + { 0x4d40, "IPOBSTVC"}, /* Polarity DCDC level 1x , */\ + { 0x4d50, "IPOBST86"}, /* Polarity DCDC level 1.14x , */\ + { 0x4d60, "IPOBST93"}, /* Polarity DCDC level 1.07x , */\ + { 0x4d70, "IPORCVLD"}, /* Polarity rcvldop ready , */\ + { 0x4d80, "IPOOCPL"}, /* Polarity ocp alarm left , */\ + { 0x4d90, "IPOOCPR"}, /* Polarity ocp alarm right , */\ + { 0x4da0, "IPOMWSRC"}, /* Polarity waits HW I2C settings , */\ + { 0x4db0, "IPOMWCFC"}, /* Polarity man wait cf config , */\ + { 0x4dc0, "IPOMWSMU"}, /* Polarity man audio mute sequence , */\ + { 0x4dd0, "IPOCFMER"}, /* Polarity cfma err , */\ + { 0x4de0, "IPOCFMAC"}, /* Polarity cfma ack , */\ + { 0x4df0, "IPCLKOOR"}, /* Polarity flag_clk_out_of_range , */\ + { 0x4e00, "IPOTDMER"}, /* Polarity tdm error , */\ + { 0x4e10, "IPOCLPL"}, /* Polarity clip left , */\ + { 0x4e20, "IPOCLPR"}, /* Polarity clip right , */\ + { 0x4e30, "IPOOCPM"}, /* Polarity mic ocpok , */\ + { 0x5001, "BSSCR"}, /* Battery protection attack Time , */\ + { 0x5023, "BSST"}, /* Battery protection threshold voltage level , */\ + { 0x5061, "BSSRL"}, /* Battery protection maximum reduction , */\ + { 0x5082, "BSSRR"}, /* Battery protection release time , */\ + { 0x50b1, "BSSHY"}, /* Battery protection hysteresis , */\ + { 0x50e0, "BSSR"}, /* Battery voltage read out , */\ + { 0x50f0, "BSSBY"}, /* Bypass HW clipper , */\ + { 0x5100, "BSSS"}, /* Vbat prot steepness , */\ + { 0x5110, "INTSMUTE"}, /* Soft mute HW , */\ + { 0x5120, "CFSML"}, /* Soft mute FW left , */\ + { 0x5130, "CFSMR"}, /* Soft mute FW right , */\ + { 0x5140, "HPFBYPL"}, /* Bypass HPF left , */\ + { 0x5150, "HPFBYPR"}, /* Bypass HPF right , */\ + { 0x5160, "DPSAL"}, /* Enable DPSA left , */\ + { 0x5170, "DPSAR"}, /* Enable DPSA right , */\ + { 0x5187, "VOL"}, /* FW volume control for primary audio channel , */\ + { 0x5200, "HNDSFRCV"}, /* Selection receiver , */\ + { 0x5222, "CLIPCTRL"}, /* Clip control setting , */\ + { 0x5257, "AMPGAIN"}, /* Amplifier gain , */\ + { 0x52d0, "SLOPEE"}, /* Enables slope control , */\ + { 0x52e1, "SLOPESET"}, /* Set slope , */\ + { 0x5a07, "VOLSEC"}, /* FW volume control for secondary audio channel , */\ + { 0x5a87, "SWPROFIL"}, /* Software profile data , */\ + { 0x7002, "DCVO"}, /* Boost voltage , */\ + { 0x7033, "DCMCC"}, /* Max coil current , */\ + { 0x7071, "DCCV"}, /* Coil Value , */\ + { 0x7090, "DCIE"}, /* Adaptive boost mode , */\ + { 0x70a0, "DCSR"}, /* Soft ramp up/down , */\ + { 0x70b2, "DCSYNCP"}, /* DCDC synchronization off + 7 positions , */\ + { 0x70e0, "DCDIS"}, /* DCDC on/off , */\ + { 0x9000, "RST"}, /* Reset , */\ + { 0x9011, "DMEM"}, /* Target memory , */\ + { 0x9030, "AIF"}, /* Auto increment , */\ + { 0x9040, "CFINT"}, /* Interrupt - auto clear , */\ + { 0x9050, "CFCGATE"}, /* Coolflux clock gating disabling control , */\ + { 0x9080, "REQCMD"}, /* Firmware event request rpc command , */\ + { 0x9090, "REQRST"}, /* Firmware event request reset restart , */\ + { 0x90a0, "REQMIPS"}, /* Firmware event request short on mips , */\ + { 0x90b0, "REQMUTED"}, /* Firmware event request mute sequence ready , */\ + { 0x90c0, "REQVOL"}, /* Firmware event request volume ready , */\ + { 0x90d0, "REQDMG"}, /* Firmware event request speaker damage detected , */\ + { 0x90e0, "REQCAL"}, /* Firmware event request calibration completed , */\ + { 0x90f0, "REQRSV"}, /* Firmware event request reserved , */\ + { 0x910f, "MADD"}, /* Memory address , */\ + { 0x920f, "MEMA"}, /* Activate memory access , */\ + { 0x9307, "ERR"}, /* Error flags , */\ + { 0x9387, "ACK"}, /* Acknowledge of requests , */\ + { 0x9380, "ACKCMD"}, /* Firmware event acknowledge rpc command , */\ + { 0x9390, "ACKRST"}, /* Firmware event acknowledge reset restart , */\ + { 0x93a0, "ACKMIPS"}, /* Firmware event acknowledge short on mips , */\ + { 0x93b0, "ACKMUTED"}, /* Firmware event acknowledge mute sequence ready , */\ + { 0x93c0, "ACKVOL"}, /* Firmware event acknowledge volume ready , */\ + { 0x93d0, "ACKDMG"}, /* Firmware event acknowledge speaker damage detected, */\ + { 0x93e0, "ACKCAL"}, /* Firmware event acknowledge calibration completed , */\ + { 0x93f0, "ACKRSV"}, /* Firmware event acknowledge reserved , */\ + { 0xa107, "MTPK"}, /* MTP KEY2 register , */\ + { 0xa200, "KEY1LOCKED"}, /* Indicates KEY1 is locked , */\ + { 0xa210, "KEY2LOCKED"}, /* Indicates KEY2 is locked , */\ + { 0xa360, "CIMTP"}, /* Start copying data from I2C mtp registers to mtp , */\ + { 0xa50f, "MTPRDMSB"}, /* MSB word of MTP manual read data , */\ + { 0xa60f, "MTPRDLSB"}, /* LSB word of MTP manual read data , */\ + { 0xb108, "EXTTS"}, /* External temperature (C) , */\ + { 0xb190, "TROS"}, /* Select temp Speaker calibration , */\ + { 0xf000, "MTPOTC"}, /* Calibration schedule , */\ + { 0xf010, "MTPEX"}, /* Calibration Ron executed , */\ + { 0xf020, "DCMCCAPI"}, /* Calibration current limit DCDC , */\ + { 0xf030, "DCMCCSB"}, /* Sign bit for delta calibration current limit DCDC , */\ + { 0xf042, "USERDEF"}, /* Calibration delta current limit DCDC , */\ + { 0xf40f, "R25CL"}, /* Ron resistance of left channel speaker coil , */\ + { 0xf50f, "R25CR"}, /* Ron resistance of right channel speaker coil , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA2_BITNAMETABLE static tfaBfName_t Tfa2BitNames[]= {\ + { 0x0, "powerdown"}, /* Powerdown selection , */\ + { 0x10, "reset"}, /* I2C Reset - Auto clear , */\ + { 0x20, "enbl_coolflux"}, /* Enable CoolFlux , */\ + { 0x30, "enbl_amplifier"}, /* Activate Amplifier , */\ + { 0x40, "enbl_boost"}, /* Activate DC-to-DC converter , */\ + { 0x50, "coolflux_configured"}, /* Coolflux configured , */\ + { 0x60, "sel_enbl_amplifier"}, /* CoolFlux controls amplifier , */\ + { 0x71, "int_pad_io"}, /* Interrupt config , */\ + { 0x91, "fs_pulse_sel"}, /* Audio sample reference , */\ + { 0xb0, "bypass_ocp"}, /* Bypass OCP , */\ + { 0xc0, "test_ocp"}, /* OCP testing control , */\ + { 0x101, "vamp_sel"}, /* Amplifier input selection , */\ + { 0x120, "src_set_configured"}, /* I2C configured , */\ + { 0x130, "execute_cold_start"}, /* Execute cold start , */\ + { 0x140, "enbl_osc1m_auto_off"}, /* Internal osc off at PWDN , */\ + { 0x150, "man_enbl_brown_out"}, /* Reaction on BOD , */\ + { 0x160, "enbl_bod"}, /* BOD Enable , */\ + { 0x170, "enbl_bod_hyst"}, /* BOD Hysteresis , */\ + { 0x181, "bod_delay"}, /* BOD filter , */\ + { 0x1a1, "bod_lvlsel"}, /* BOD threshold , */\ + { 0x1d0, "disable_mute_time_out"}, /* Time out SB mute sequence , */\ + { 0x1e0, "pwm_sel_rcv_ns"}, /* Noise shaper selection , */\ + { 0x1f0, "man_enbl_watchdog"}, /* Watchdog manager reaction , */\ + { 0x203, "audio_fs"}, /* Sample rate (fs) , */\ + { 0x240, "input_level"}, /* TDM output attenuation , */\ + { 0x255, "cs_frac_delay"}, /* V/I Fractional delay , */\ + { 0x2b0, "bypass_hvbat_filter"}, /* Bypass HVBAT filter , */\ + { 0x2c0, "ctrl_rcvldop_bypass"}, /* Receiver LDO bypass , */\ + { 0x30f, "device_rev"}, /* Revision info , */\ + { 0x401, "pll_clkin_sel"}, /* PLL external ref clock , */\ + { 0x420, "pll_clkin_sel_osc"}, /* PLL internal ref clock , */\ + { 0x500, "enbl_spkr_ss_left"}, /* Enable left channel , */\ + { 0x510, "enbl_spkr_ss_right"}, /* Enable right channel , */\ + { 0x520, "enbl_volsense_left"}, /* Voltage sense left , */\ + { 0x530, "enbl_volsense_right"}, /* Voltage sense right , */\ + { 0x540, "enbl_cursense_left"}, /* Current sense left , */\ + { 0x550, "enbl_cursense_right"}, /* Current sense right , */\ + { 0x560, "enbl_pdm_ss"}, /* Sub-system PDM , */\ + { 0xd00, "side_tone_gain_sel"}, /* PDM side tone gain selector , */\ + { 0xd18, "side_tone_gain"}, /* Side tone gain , */\ + { 0xda0, "mute_side_tone"}, /* Side tone soft mute , */\ + { 0xe06, "ctrl_digtoana"}, /* Register for the host SW to record the current active vstep, */\ + { 0xe70, "enbl_cmfb_left"}, /* Current sense common mode feedback control for left channel, */\ + { 0xf0f, "hidden_code"}, /* 5A6Bh, 23147d to access registers (default for engineering), */\ + { 0x1000, "flag_por"}, /* POR , */\ + { 0x1010, "flag_pll_lock"}, /* PLL lock , */\ + { 0x1020, "flag_otpok"}, /* OTP alarm , */\ + { 0x1030, "flag_ovpok"}, /* OVP alarm , */\ + { 0x1040, "flag_uvpok"}, /* UVP alarm , */\ + { 0x1050, "flag_clocks_stable"}, /* Clocks stable , */\ + { 0x1060, "flag_mtp_busy"}, /* MTP busy , */\ + { 0x1070, "flag_lost_clk"}, /* Lost clock , */\ + { 0x1080, "flag_cf_speakererror"}, /* Speaker error , */\ + { 0x1090, "flag_cold_started"}, /* Cold Start , */\ + { 0x10a0, "flag_engage"}, /* Amplifier engage , */\ + { 0x10b0, "flag_watchdog_reset"}, /* Watchdog , */\ + { 0x10c0, "flag_enbl_amp"}, /* Amplifier enable , */\ + { 0x10d0, "flag_enbl_ref"}, /* References enable , */\ + { 0x10e0, "flag_adc10_ready"}, /* Control ADC , */\ + { 0x10f0, "flag_bod_vddd_nok"}, /* BOD , */\ + { 0x1100, "flag_bst_bstcur"}, /* DCDC current limiting , */\ + { 0x1110, "flag_bst_hiz"}, /* DCDC active , */\ + { 0x1120, "flag_bst_ocpok"}, /* DCDC OCP nmos , */\ + { 0x1130, "flag_bst_peakcur"}, /* Indicates current is max in DC-to-DC converter , */\ + { 0x1140, "flag_bst_voutcomp"}, /* DCDC level 1x , */\ + { 0x1150, "flag_bst_voutcomp86"}, /* DCDC level 1.14x , */\ + { 0x1160, "flag_bst_voutcomp93"}, /* DCDC level 1.07x , */\ + { 0x1170, "flag_soft_mute_busy"}, /* side tone (un)mute busy , */\ + { 0x1180, "flag_soft_mute_state"}, /* side tone mute state , */\ + { 0x1190, "flag_tdm_lut_error"}, /* TDM LUT error , */\ + { 0x11a2, "flag_tdm_status"}, /* TDM status bits , */\ + { 0x11d0, "flag_tdm_error"}, /* TDM error , */\ + { 0x11e0, "flag_haptic_busy"}, /* Status haptic driver , */\ + { 0x1200, "flag_ocpokap_left"}, /* OCPOK pmos A left , */\ + { 0x1210, "flag_ocpokan_left"}, /* OCPOK nmos A left , */\ + { 0x1220, "flag_ocpokbp_left"}, /* OCPOK pmos B left , */\ + { 0x1230, "flag_ocpokbn_left"}, /* OCPOK nmos B left , */\ + { 0x1240, "flag_clipa_high_left"}, /* Clipping A left to Vddp , */\ + { 0x1250, "flag_clipa_low_left"}, /* Clipping A left to gnd , */\ + { 0x1260, "flag_clipb_high_left"}, /* Clipping B left to Vddp , */\ + { 0x1270, "flag_clipb_low_left"}, /* Clipping B left to gnd , */\ + { 0x1280, "flag_ocpokap_rcv"}, /* OCPOK pmos A RCV , */\ + { 0x1290, "flag_ocpokan_rcv"}, /* OCPOK nmos A RCV , */\ + { 0x12a0, "flag_ocpokbp_rcv"}, /* OCPOK pmos B RCV , */\ + { 0x12b0, "flag_ocpokbn_rcv"}, /* OCPOK nmos B RCV , */\ + { 0x12c0, "flag_rcvldop_ready"}, /* RCV LDO regulates , */\ + { 0x12d0, "flag_rcvldop_bypassready"}, /* Receiver LDO ready , */\ + { 0x12e0, "flag_ocp_alarm_left"}, /* OCP left amplifier , */\ + { 0x12f0, "flag_clip_left"}, /* Amplifier left clipping , */\ + { 0x1300, "flag_ocpokap_right"}, /* OCPOK pmos A right , */\ + { 0x1310, "flag_ocpokan_right"}, /* OCPOK nmos A right , */\ + { 0x1320, "flag_ocpokbp_right"}, /* OCPOK pmos B right , */\ + { 0x1330, "flag_ocpokbn_right"}, /* OCPOK nmos B right , */\ + { 0x1340, "flag_clipa_high_right"}, /* Clipping A right to Vddp , */\ + { 0x1350, "flag_clipa_low_right"}, /* Clipping A right to gnd , */\ + { 0x1360, "flag_clipb_high_right"}, /* Clipping B left to Vddp , */\ + { 0x1370, "flag_clipb_low_right"}, /* Clipping B right to gnd , */\ + { 0x1380, "flag_ocp_alarm_right"}, /* OCP right amplifier , */\ + { 0x1390, "flag_clip_right"}, /* Amplifier right clipping , */\ + { 0x13a0, "flag_mic_ocpok"}, /* OCPOK MICVDD , */\ + { 0x13b0, "flag_man_alarm_state"}, /* Alarm state , */\ + { 0x13c0, "flag_man_wait_src_settings"}, /* Wait HW I2C settings , */\ + { 0x13d0, "flag_man_wait_cf_config"}, /* Wait CF config , */\ + { 0x13e0, "flag_man_start_mute_audio"}, /* Audio mute sequence , */\ + { 0x13f0, "flag_man_operating_state"}, /* Operating state , */\ + { 0x1400, "flag_cf_speakererror_left"}, /* Left speaker status , */\ + { 0x1410, "flag_cf_speakererror_right"}, /* Right speaker status , */\ + { 0x1420, "flag_clk_out_of_range"}, /* External clock status , */\ + { 0x1433, "man_state"}, /* Device manager status , */\ + { 0x1509, "bat_adc"}, /* Battery voltage (V) , */\ + { 0x1608, "temp_adc"}, /* IC Temperature (C) , */\ + { 0x2003, "tdm_usecase"}, /* Usecase setting , */\ + { 0x2040, "tdm_enable"}, /* Enable interface , */\ + { 0x2050, "tdm_mode"}, /* Slave/master , */\ + { 0x2060, "tdm_clk_inversion"}, /* Reception data to BCK clock , */\ + { 0x2073, "tdm_fs_ws_length"}, /* FS length (master mode only) , */\ + { 0x20b0, "tdm_fs_ws_polarity"}, /* FS polarity , */\ + { 0x20c3, "tdm_nbck"}, /* N-BCK's in FS , */\ + { 0x2103, "tdm_nb_of_slots"}, /* N-slots in Frame , */\ + { 0x2144, "tdm_slot_length"}, /* N-bits in slot , */\ + { 0x2194, "tdm_bits_remaining"}, /* N-bits remaining , */\ + { 0x21e0, "tdm_data_delay"}, /* data delay to FS , */\ + { 0x21f0, "tdm_data_adjustment"}, /* data adjustment , */\ + { 0x2201, "tdm_audio_sample_compression"}, /* Received audio compression , */\ + { 0x2224, "tdm_sample_size"}, /* Sample size per slot , */\ + { 0x2271, "tdm_txdata_format"}, /* Format unused bits , */\ + { 0x2291, "tdm_txdata_format_unused_slot_sd0"}, /* Format unused slots GAINIO , */\ + { 0x22b1, "tdm_txdata_format_unused_slot_sd1"}, /* Format unused slots DIO1 , */\ + { 0x22d1, "tdm_txdata_format_unused_slot_sd2"}, /* Format unused slots DIO2 , */\ + { 0x2300, "tdm_sink0_enable"}, /* Control gainin (not used in DSP) , */\ + { 0x2310, "tdm_sink1_enable"}, /* Control audio left , */\ + { 0x2320, "tdm_sink2_enable"}, /* Control audio right , */\ + { 0x2330, "tdm_source0_enable"}, /* Control gainout (not used in DSP) , */\ + { 0x2340, "tdm_source1_enable"}, /* Control voltage sense right , */\ + { 0x2350, "tdm_source2_enable"}, /* Control current sense right , */\ + { 0x2360, "tdm_source3_enable"}, /* Voltage sense left control , */\ + { 0x2370, "tdm_source4_enable"}, /* Current sense left control , */\ + { 0x2380, "tdm_source5_enable"}, /* DSP out right control , */\ + { 0x2390, "tdm_source6_enable"}, /* DSP out left control , */\ + { 0x23a0, "tdm_source7_enable"}, /* AEC ref left control , */\ + { 0x23b0, "tdm_source8_enable"}, /* AEC ref right control , */\ + { 0x23c0, "tdm_source9_enable"}, /* PDM 1 control , */\ + { 0x23d0, "tdm_source10_enable"}, /* PDM 2 control , */\ + { 0x2401, "tdm_sink0_io"}, /* IO gainin (not used in DSP) , */\ + { 0x2421, "tdm_sink1_io"}, /* IO audio left , */\ + { 0x2441, "tdm_sink2_io"}, /* IO audio right , */\ + { 0x2461, "tdm_source0_io"}, /* IO gainout (not used in DSP) , */\ + { 0x2481, "tdm_source1_io"}, /* IO voltage sense right , */\ + { 0x24a1, "tdm_source2_io"}, /* IO current sense right , */\ + { 0x24c1, "tdm_source3_io"}, /* IO voltage sense left , */\ + { 0x24e1, "tdm_source4_io"}, /* IO current sense left , */\ + { 0x2501, "tdm_source5_io"}, /* IO dspout right , */\ + { 0x2521, "tdm_source6_io"}, /* IO dspout left , */\ + { 0x2541, "tdm_source7_io"}, /* IO AEC ref left control , */\ + { 0x2561, "tdm_source8_io"}, /* IO AEC ref right control , */\ + { 0x2581, "tdm_source9_io"}, /* IO pdm1 , */\ + { 0x25a1, "tdm_source10_io"}, /* IO pdm2 , */\ + { 0x2603, "tdm_sink0_slot"}, /* Position gainin (not used in DSP) , */\ + { 0x2643, "tdm_sink1_slot"}, /* Position audio left , */\ + { 0x2683, "tdm_sink2_slot"}, /* Position audio right , */\ + { 0x26c3, "tdm_source0_slot"}, /* Position gainout (not used in DSP) , */\ + { 0x2703, "tdm_source1_slot"}, /* Position voltage sense right , */\ + { 0x2743, "tdm_source2_slot"}, /* Position current sense right , */\ + { 0x2783, "tdm_source3_slot"}, /* Position voltage sense left , */\ + { 0x27c3, "tdm_source4_slot"}, /* Position current sense left , */\ + { 0x2803, "tdm_source5_slot"}, /* Position dspout right , */\ + { 0x2843, "tdm_source6_slot"}, /* Position dspout left , */\ + { 0x2883, "tdm_source7_slot"}, /* Position AEC ref left control , */\ + { 0x28c3, "tdm_source8_slot"}, /* Position AEC ref right control , */\ + { 0x2903, "tdm_source9_slot"}, /* Position pdm1 , */\ + { 0x2943, "tdm_source10_slot"}, /* Position pdm2 , */\ + { 0x3100, "pdm_mode"}, /* PDM control , */\ + { 0x3111, "pdm_side_tone_sel"}, /* Side tone input , */\ + { 0x3130, "pdm_left_sel"}, /* PDM data selection for left channel during PDM direct mode, */\ + { 0x3140, "pdm_right_sel"}, /* PDM data selection for right channel during PDM direct mode, */\ + { 0x3150, "enbl_micvdd"}, /* Enable MICVDD , */\ + { 0x3160, "bypass_micvdd_ocp"}, /* Bypass control for the MICVDD OCP flag processing , */\ + { 0x3201, "pdm_nbck"}, /* PDM BCK/Fs ratio , */\ + { 0x3223, "pdm_gain"}, /* PDM gain , */\ + { 0x3263, "sel_pdm_out_data"}, /* PDM output selection - RE/FE data combination , */\ + { 0x32a0, "sel_cf_haptic_data"}, /* Select the source for haptic data output (not for customer), */\ + { 0x3307, "haptic_duration"}, /* Duration (ms) , */\ + { 0x3387, "haptic_data"}, /* DC value (FFS) , */\ + { 0x3403, "gpio_datain"}, /* Receiving value , */\ + { 0x3500, "gpio_ctrl"}, /* GPIO master control over GPIO1/2 ports (not for customer), */\ + { 0x3513, "gpio_dir"}, /* Configuration , */\ + { 0x3553, "gpio_dataout"}, /* Transmitting value , */\ + { 0x4000, "int_out_flag_por"}, /* Status POR , */\ + { 0x4010, "int_out_flag_pll_lock"}, /* Status PLL lock , */\ + { 0x4020, "int_out_flag_otpok"}, /* Status OTP alarm , */\ + { 0x4030, "int_out_flag_ovpok"}, /* Status OVP alarm , */\ + { 0x4040, "int_out_flag_uvpok"}, /* Status UVP alarm , */\ + { 0x4050, "int_out_flag_clocks_stable"}, /* Status clocks stable , */\ + { 0x4060, "int_out_flag_mtp_busy"}, /* Status MTP busy , */\ + { 0x4070, "int_out_flag_lost_clk"}, /* Status lost clock , */\ + { 0x4080, "int_out_flag_cf_speakererror"}, /* Status speaker error , */\ + { 0x4090, "int_out_flag_cold_started"}, /* Status cold start , */\ + { 0x40a0, "int_out_flag_engage"}, /* Status amplifier engage , */\ + { 0x40b0, "int_out_flag_watchdog_reset"}, /* Status watchdog , */\ + { 0x40c0, "int_out_flag_enbl_amp"}, /* Status amplifier enable , */\ + { 0x40d0, "int_out_flag_enbl_ref"}, /* Status Ref enable , */\ + { 0x40e0, "int_out_flag_adc10_ready"}, /* Status Control ADC , */\ + { 0x40f0, "int_out_flag_bod_vddd_nok"}, /* Status BOD , */\ + { 0x4100, "int_out_flag_bst_bstcur"}, /* Status DCDC current limiting , */\ + { 0x4110, "int_out_flag_bst_hiz"}, /* Status DCDC active , */\ + { 0x4120, "int_out_flag_bst_ocpok"}, /* Status DCDC OCP , */\ + { 0x4130, "int_out_flag_bst_peakcur"}, /* Status bst peakcur , */\ + { 0x4140, "int_out_flag_bst_voutcomp"}, /* Status DCDC level 1x , */\ + { 0x4150, "int_out_flag_bst_voutcomp86"}, /* Status DCDC level 1.14x , */\ + { 0x4160, "int_out_flag_bst_voutcomp93"}, /* Status DCDC level 1.07x , */\ + { 0x4170, "int_out_flag_rcvldop_ready"}, /* Status rcvldop ready , */\ + { 0x4180, "int_out_flag_ocp_alarm_left"}, /* Status ocp alarm left , */\ + { 0x4190, "int_out_flag_ocp_alarm_right"}, /* Status ocp alarm right , */\ + { 0x41a0, "int_out_flag_man_wait_src_settings"}, /* Status Waits HW I2C settings , */\ + { 0x41b0, "int_out_flag_man_wait_cf_config"}, /* Status waits CF config , */\ + { 0x41c0, "int_out_flag_man_start_mute_audio"}, /* Status Audio mute sequence , */\ + { 0x41d0, "int_out_flag_cfma_err"}, /* Status cfma error , */\ + { 0x41e0, "int_out_flag_cfma_ack"}, /* Status cfma ack , */\ + { 0x41f0, "int_out_flag_clk_out_of_range"}, /* Status flag_clk_out_of_range , */\ + { 0x4200, "int_out_flag_tdm_error"}, /* Status tdm error , */\ + { 0x4210, "int_out_flag_clip_left"}, /* Status clip left , */\ + { 0x4220, "int_out_flag_clip_right"}, /* Status clip right , */\ + { 0x4230, "int_out_flag_mic_ocpok"}, /* Status mic ocpok , */\ + { 0x4400, "int_in_flag_por"}, /* Clear POR , */\ + { 0x4410, "int_in_flag_pll_lock"}, /* Clear PLL lock , */\ + { 0x4420, "int_in_flag_otpok"}, /* Clear OTP alarm , */\ + { 0x4430, "int_in_flag_ovpok"}, /* Clear OVP alarm , */\ + { 0x4440, "int_in_flag_uvpok"}, /* Clear UVP alarm , */\ + { 0x4450, "int_in_flag_clocks_stable"}, /* Clear clocks stable , */\ + { 0x4460, "int_in_flag_mtp_busy"}, /* Clear mtp busy , */\ + { 0x4470, "int_in_flag_lost_clk"}, /* Clear lost clk , */\ + { 0x4480, "int_in_flag_cf_speakererror"}, /* Clear speaker error , */\ + { 0x4490, "int_in_flag_cold_started"}, /* Clear cold started , */\ + { 0x44a0, "int_in_flag_engage"}, /* Clear amplifier engage , */\ + { 0x44b0, "int_in_flag_watchdog_reset"}, /* Clear watchdog , */\ + { 0x44c0, "int_in_flag_enbl_amp"}, /* Clear enbl amp , */\ + { 0x44d0, "int_in_flag_enbl_ref"}, /* Clear ref enable , */\ + { 0x44e0, "int_in_flag_adc10_ready"}, /* Clear control ADC , */\ + { 0x44f0, "int_in_flag_bod_vddd_nok"}, /* Clear BOD , */\ + { 0x4500, "int_in_flag_bst_bstcur"}, /* Clear DCDC current limiting , */\ + { 0x4510, "int_in_flag_bst_hiz"}, /* Clear DCDC active , */\ + { 0x4520, "int_in_flag_bst_ocpok"}, /* Clear DCDC OCP , */\ + { 0x4530, "int_in_flag_bst_peakcur"}, /* Clear bst peakcur , */\ + { 0x4540, "int_in_flag_bst_voutcomp"}, /* Clear DCDC level 1x , */\ + { 0x4550, "int_in_flag_bst_voutcomp86"}, /* Clear DCDC level 1.14x , */\ + { 0x4560, "int_in_flag_bst_voutcomp93"}, /* Clear DCDC level 1.07x , */\ + { 0x4570, "int_in_flag_rcvldop_ready"}, /* Clear rcvldop ready , */\ + { 0x4580, "int_in_flag_ocp_alarm_left"}, /* Clear ocp alarm left , */\ + { 0x4590, "int_in_flag_ocp_alarm_right"}, /* Clear ocp alarm right , */\ + { 0x45a0, "int_in_flag_man_wait_src_settings"}, /* Clear wait HW I2C settings , */\ + { 0x45b0, "int_in_flag_man_wait_cf_config"}, /* Clear wait cf config , */\ + { 0x45c0, "int_in_flag_man_start_mute_audio"}, /* Clear audio mute sequence , */\ + { 0x45d0, "int_in_flag_cfma_err"}, /* Clear cfma err , */\ + { 0x45e0, "int_in_flag_cfma_ack"}, /* Clear cfma ack , */\ + { 0x45f0, "int_in_flag_clk_out_of_range"}, /* Clear flag_clk_out_of_range , */\ + { 0x4600, "int_in_flag_tdm_error"}, /* Clear tdm error , */\ + { 0x4610, "int_in_flag_clip_left"}, /* Clear clip left , */\ + { 0x4620, "int_in_flag_clip_right"}, /* Clear clip right , */\ + { 0x4630, "int_in_flag_mic_ocpok"}, /* Clear mic ocpok , */\ + { 0x4800, "int_enable_flag_por"}, /* Enable por , */\ + { 0x4810, "int_enable_flag_pll_lock"}, /* Enable pll lock , */\ + { 0x4820, "int_enable_flag_otpok"}, /* Enable OTP alarm , */\ + { 0x4830, "int_enable_flag_ovpok"}, /* Enable OVP alarm , */\ + { 0x4840, "int_enable_flag_uvpok"}, /* Enable UVP alarm , */\ + { 0x4850, "int_enable_flag_clocks_stable"}, /* Enable clocks stable , */\ + { 0x4860, "int_enable_flag_mtp_busy"}, /* Enable mtp busy , */\ + { 0x4870, "int_enable_flag_lost_clk"}, /* Enable lost clk , */\ + { 0x4880, "int_enable_flag_cf_speakererror"}, /* Enable speaker error , */\ + { 0x4890, "int_enable_flag_cold_started"}, /* Enable cold started , */\ + { 0x48a0, "int_enable_flag_engage"}, /* Enable amplifier engage , */\ + { 0x48b0, "int_enable_flag_watchdog_reset"}, /* Enable watchdog , */\ + { 0x48c0, "int_enable_flag_enbl_amp"}, /* Enable enbl amp , */\ + { 0x48d0, "int_enable_flag_enbl_ref"}, /* Enable ref enable , */\ + { 0x48e0, "int_enable_flag_adc10_ready"}, /* Enable Control ADC , */\ + { 0x48f0, "int_enable_flag_bod_vddd_nok"}, /* Enable BOD , */\ + { 0x4900, "int_enable_flag_bst_bstcur"}, /* Enable DCDC current limiting , */\ + { 0x4910, "int_enable_flag_bst_hiz"}, /* Enable DCDC active , */\ + { 0x4920, "int_enable_flag_bst_ocpok"}, /* Enable DCDC OCP , */\ + { 0x4930, "int_enable_flag_bst_peakcur"}, /* Enable bst peakcur , */\ + { 0x4940, "int_enable_flag_bst_voutcomp"}, /* Enable DCDC level 1x , */\ + { 0x4950, "int_enable_flag_bst_voutcomp86"}, /* Enable DCDC level 1.14x , */\ + { 0x4960, "int_enable_flag_bst_voutcomp93"}, /* Enable DCDC level 1.07x , */\ + { 0x4970, "int_enable_flag_rcvldop_ready"}, /* Enable rcvldop ready , */\ + { 0x4980, "int_enable_flag_ocp_alarm_left"}, /* Enable ocp alarm left , */\ + { 0x4990, "int_enable_flag_ocp_alarm_right"}, /* Enable ocp alarm right , */\ + { 0x49a0, "int_enable_flag_man_wait_src_settings"}, /* Enable waits HW I2C settings , */\ + { 0x49b0, "int_enable_flag_man_wait_cf_config"}, /* Enable man wait cf config , */\ + { 0x49c0, "int_enable_flag_man_start_mute_audio"}, /* Enable man Audio mute sequence , */\ + { 0x49d0, "int_enable_flag_cfma_err"}, /* Enable cfma err , */\ + { 0x49e0, "int_enable_flag_cfma_ack"}, /* Enable cfma ack , */\ + { 0x49f0, "int_enable_flag_clk_out_of_range"}, /* Enable flag_clk_out_of_range , */\ + { 0x4a00, "int_enable_flag_tdm_error"}, /* Enable tdm error , */\ + { 0x4a10, "int_enable_flag_clip_left"}, /* Enable clip left , */\ + { 0x4a20, "int_enable_flag_clip_right"}, /* Enable clip right , */\ + { 0x4a30, "int_enable_flag_mic_ocpok"}, /* Enable mic ocpok , */\ + { 0x4c00, "int_polarity_flag_por"}, /* Polarity por , */\ + { 0x4c10, "int_polarity_flag_pll_lock"}, /* Polarity pll lock , */\ + { 0x4c20, "int_polarity_flag_otpok"}, /* Polarity OTP alarm , */\ + { 0x4c30, "int_polarity_flag_ovpok"}, /* Polarity OVP alarm , */\ + { 0x4c40, "int_polarity_flag_uvpok"}, /* Polarity UVP alarm , */\ + { 0x4c50, "int_polarity_flag_clocks_stable"}, /* Polarity clocks stable , */\ + { 0x4c60, "int_polarity_flag_mtp_busy"}, /* Polarity mtp busy , */\ + { 0x4c70, "int_polarity_flag_lost_clk"}, /* Polarity lost clk , */\ + { 0x4c80, "int_polarity_flag_cf_speakererror"}, /* Polarity speaker error , */\ + { 0x4c90, "int_polarity_flag_cold_started"}, /* Polarity cold started , */\ + { 0x4ca0, "int_polarity_flag_engage"}, /* Polarity amplifier engage , */\ + { 0x4cb0, "int_polarity_flag_watchdog_reset"}, /* Polarity watchdog , */\ + { 0x4cc0, "int_polarity_flag_enbl_amp"}, /* Polarity enbl amp , */\ + { 0x4cd0, "int_polarity_flag_enbl_ref"}, /* Polarity ref enable , */\ + { 0x4ce0, "int_polarity_flag_adc10_ready"}, /* Polarity Control ADC , */\ + { 0x4cf0, "int_polarity_flag_bod_vddd_nok"}, /* Polarity BOD , */\ + { 0x4d00, "int_polarity_flag_bst_bstcur"}, /* Polarity DCDC current limiting , */\ + { 0x4d10, "int_polarity_flag_bst_hiz"}, /* Polarity DCDC active , */\ + { 0x4d20, "int_polarity_flag_bst_ocpok"}, /* Polarity DCDC OCP , */\ + { 0x4d30, "int_polarity_flag_bst_peakcur"}, /* Polarity bst peakcur , */\ + { 0x4d40, "int_polarity_flag_bst_voutcomp"}, /* Polarity DCDC level 1x , */\ + { 0x4d50, "int_polarity_flag_bst_voutcomp86"}, /* Polarity DCDC level 1.14x , */\ + { 0x4d60, "int_polarity_flag_bst_voutcomp93"}, /* Polarity DCDC level 1.07x , */\ + { 0x4d70, "int_polarity_flag_rcvldop_ready"}, /* Polarity rcvldop ready , */\ + { 0x4d80, "int_polarity_flag_ocp_alarm_left"}, /* Polarity ocp alarm left , */\ + { 0x4d90, "int_polarity_flag_ocp_alarm_right"}, /* Polarity ocp alarm right , */\ + { 0x4da0, "int_polarity_flag_man_wait_src_settings"}, /* Polarity waits HW I2C settings , */\ + { 0x4db0, "int_polarity_flag_man_wait_cf_config"}, /* Polarity man wait cf config , */\ + { 0x4dc0, "int_polarity_flag_man_start_mute_audio"}, /* Polarity man audio mute sequence , */\ + { 0x4dd0, "int_polarity_flag_cfma_err"}, /* Polarity cfma err , */\ + { 0x4de0, "int_polarity_flag_cfma_ack"}, /* Polarity cfma ack , */\ + { 0x4df0, "int_polarity_flag_clk_out_of_range"}, /* Polarity flag_clk_out_of_range , */\ + { 0x4e00, "int_polarity_flag_tdm_error"}, /* Polarity tdm error , */\ + { 0x4e10, "int_polarity_flag_clip_left"}, /* Polarity clip left , */\ + { 0x4e20, "int_polarity_flag_clip_right"}, /* Polarity clip right , */\ + { 0x4e30, "int_polarity_flag_mic_ocpok"}, /* Polarity mic ocpok , */\ + { 0x5001, "vbat_prot_attack_time"}, /* Battery protection attack Time , */\ + { 0x5023, "vbat_prot_thlevel"}, /* Battery protection threshold voltage level , */\ + { 0x5061, "vbat_prot_max_reduct"}, /* Battery protection maximum reduction , */\ + { 0x5082, "vbat_prot_release_time"}, /* Battery protection release time , */\ + { 0x50b1, "vbat_prot_hysterese"}, /* Battery protection hysteresis , */\ + { 0x50d0, "rst_min_vbat"}, /* Reset clipper - Auto clear , */\ + { 0x50e0, "sel_vbat"}, /* Battery voltage read out , */\ + { 0x50f0, "bypass_clipper"}, /* Bypass HW clipper , */\ + { 0x5100, "batsense_steepness"}, /* Vbat prot steepness , */\ + { 0x5110, "soft_mute"}, /* Soft mute HW , */\ + { 0x5120, "cf_mute_left"}, /* Soft mute FW left , */\ + { 0x5130, "cf_mute_right"}, /* Soft mute FW right , */\ + { 0x5140, "bypass_hp_left"}, /* Bypass HPF left , */\ + { 0x5150, "bypass_hp_right"}, /* Bypass HPF right , */\ + { 0x5160, "enbl_dpsa_left"}, /* Enable DPSA left , */\ + { 0x5170, "enbl_dpsa_right"}, /* Enable DPSA right , */\ + { 0x5187, "cf_volume"}, /* FW volume control for primary audio channel , */\ + { 0x5200, "ctrl_rcv"}, /* Selection receiver , */\ + { 0x5210, "ctrl_rcv_fb_100k"}, /* Selection of feedback resistor for receiver mode (not for customer), */\ + { 0x5222, "ctrl_cc"}, /* Clip control setting , */\ + { 0x5257, "gain"}, /* Amplifier gain , */\ + { 0x52d0, "ctrl_slopectrl"}, /* Enables slope control , */\ + { 0x52e1, "ctrl_slope"}, /* Set slope , */\ + { 0x5301, "dpsa_level"}, /* DPSA threshold levels , */\ + { 0x5321, "dpsa_release"}, /* DPSA Release time , */\ + { 0x5340, "clipfast"}, /* Clock selection for HW clipper for battery protection, */\ + { 0x5350, "bypass_lp"}, /* Bypass the low power filter inside temperature sensor, */\ + { 0x5360, "enbl_low_latency"}, /* CF low latency outputs for add module , */\ + { 0x5400, "first_order_mode"}, /* Overrule to 1st order mode of control stage when clipping, */\ + { 0x5410, "bypass_ctrlloop"}, /* Switch amplifier into open loop configuration , */\ + { 0x5420, "fb_hz"}, /* Feedback resistor set to high ohmic , */\ + { 0x5430, "icomp_engage"}, /* Engage of icomp , */\ + { 0x5440, "ctrl_kickback"}, /* Prevent double pulses of output stage , */\ + { 0x5450, "icomp_engage_overrule"}, /* To overrule the functional icomp_engage signal during validation, */\ + { 0x5503, "ctrl_dem"}, /* Enable DEM icomp and DEM one bit dac , */\ + { 0x5543, "ctrl_dem_mismatch"}, /* Enable DEM icomp mismatch for testing , */\ + { 0x5581, "dpsa_drive"}, /* Control of the number of power stage sections, total of 4 sections. Each section is 1/4 of the total power stages., */\ + { 0x560a, "enbl_amp_left"}, /* Switch on the class-D power sections, each part of the analog sections can be switched on/off individually - Left channel, */\ + { 0x56b0, "enbl_engage_left"}, /* Enables/engage power stage and control loop - left channel, */\ + { 0x570a, "enbl_amp_right"}, /* Switch on the class-D power sections, each part of the analog sections can be switched on/off individually - Right channel, */\ + { 0x57b0, "enbl_engage_right"}, /* Enables/engage power stage and control loop - right channel, */\ + { 0x5800, "hard_mute_left"}, /* Hard mute - PWM module left , */\ + { 0x5810, "hard_mute_right"}, /* Hard mute - PWM module right , */\ + { 0x5820, "pwm_shape"}, /* PWM shape , */\ + { 0x5830, "pwm_bitlength"}, /* PWM bit length in noise shaper , */\ + { 0x5844, "pwm_delay"}, /* PWM delay bits to set the delay, clockd is 1/(k*2048*fs), */\ + { 0x5890, "reclock_pwm"}, /* Reclock the pwm signal inside analog , */\ + { 0x58a0, "reclock_voltsense"}, /* Reclock the voltage sense pwm signal , */\ + { 0x58b0, "enbl_pwm_phase_shift_left"}, /* Control for pwm phase shift, inverted function - left channel, */\ + { 0x58c0, "enbl_pwm_phase_shift_right"}, /* Control for pwm phase shift - right channel , */\ + { 0x5900, "ctrl_rcvldop_pulldown"}, /* Pulldown of LDO (2.7V) , */\ + { 0x5910, "ctrl_rcvldop_test_comp"}, /* Enable testing of LDO comparator , */\ + { 0x5920, "ctrl_rcvldop_test_loadedldo"}, /* Load connected to rcvldo , */\ + { 0x5930, "enbl_rcvldop"}, /* Enables the LDO (2.7) , */\ + { 0x5a07, "cf_volume_sec"}, /* FW volume control for secondary audio channel , */\ + { 0x5a87, "sw_profile"}, /* Software profile data , */\ + { 0x7002, "boost_volt"}, /* Boost voltage , */\ + { 0x7033, "boost_cur"}, /* Max coil current , */\ + { 0x7071, "bst_coil_value"}, /* Coil Value , */\ + { 0x7090, "boost_intel"}, /* Adaptive boost mode , */\ + { 0x70a0, "boost_speed"}, /* Soft ramp up/down , */\ + { 0x70b2, "dcdc_synchronisation"}, /* DCDC synchronization off + 7 positions , */\ + { 0x70e0, "dcdcoff_mode"}, /* DCDC on/off , */\ + { 0x7104, "bst_drive"}, /* Binary coded drive setting for boost converter power stage, */\ + { 0x7151, "bst_scalecur"}, /* For testing direct control scale current , */\ + { 0x7174, "bst_slopecur"}, /* For testing direct control slope current , */\ + { 0x71c1, "bst_slope"}, /* Boost slope speed , */\ + { 0x71e0, "bst_bypass_bstcur"}, /* Bypass control for boost current settings , */\ + { 0x71f0, "bst_bypass_bstfoldback"}, /* Bypass control for boost foldback , */\ + { 0x7200, "enbl_bst_engage"}, /* Enable power stage dcdc controller , */\ + { 0x7210, "enbl_bst_hizcom"}, /* Enable hiz comparator , */\ + { 0x7220, "enbl_bst_peak2avg"}, /* Enable boost peak2avg functionality , */\ + { 0x7230, "enbl_bst_peakcur"}, /* Enable peak current , */\ + { 0x7240, "enbl_bst_power"}, /* Enable line of the powerstage , */\ + { 0x7250, "enbl_bst_slopecur"}, /* Enable bit of max-current dac , */\ + { 0x7260, "enbl_bst_voutcomp"}, /* Enable vout comparators , */\ + { 0x7270, "enbl_bst_voutcomp86"}, /* Enable vout-86 comparators , */\ + { 0x7280, "enbl_bst_voutcomp93"}, /* Enable vout-93 comparators , */\ + { 0x7290, "enbl_bst_windac"}, /* Enable window dac , */\ + { 0x72a5, "bst_windac"}, /* for testing direct control windac , */\ + { 0x7300, "boost_alg"}, /* Control for boost adaptive loop gain , */\ + { 0x7311, "boost_loopgain"}, /* DCDC boost loopgain setting , */\ + { 0x7332, "bst_freq"}, /* DCDC bost frequency control , */\ + { 0x8001, "sel_clk_cs"}, /* Current sense clock duty cycle control , */\ + { 0x8021, "micadc_speed"}, /* Current sense clock for MiCADC selection - 32/44.1/48 KHz Fs band only, */\ + { 0x8040, "cs_dc_offset"}, /* Current sense decimator offset control , */\ + { 0x8050, "cs_gain_control"}, /* Current sense gain control , */\ + { 0x8060, "cs_bypass_gc"}, /* Bypasses the CS gain correction , */\ + { 0x8087, "cs_gain"}, /* Current sense gain , */\ + { 0x8110, "invertpwm_left"}, /* Current sense common mode feedback pwm invert control for left channel, */\ + { 0x8122, "cmfb_gain_left"}, /* Current sense common mode feedback control gain for left channel, */\ + { 0x8154, "cmfb_offset_left"}, /* Current sense common mode feedback control offset for left channel, */\ + { 0x8200, "enbl_cmfb_right"}, /* Current sense common mode feedback control for right channel, */\ + { 0x8210, "invertpwm_right"}, /* Current sense common mode feedback pwm invert control for right channel, */\ + { 0x8222, "cmfb_gain_right"}, /* Current sense common mode feedback control gain for right channel, */\ + { 0x8254, "cmfb_offset_right"}, /* Current sense common mode feedback control offset for right channel, */\ + { 0x8305, "cs_ktemp"}, /* Current sense temperature compensation trimming (1 - VALUE*TEMP)*signal, */\ + { 0x8400, "cs_adc_bsoinv"}, /* Bitstream inversion for current sense ADC , */\ + { 0x8421, "cs_adc_hifreq"}, /* Frequency mode current sense ADC , */\ + { 0x8440, "cs_adc_nortz"}, /* Return to zero for current sense ADC , */\ + { 0x8453, "cs_adc_offset"}, /* Micadc ADC offset setting , */\ + { 0x8490, "cs_adc_slowdel"}, /* Select delay for current sense ADC (internal decision circuitry), */\ + { 0x84a4, "cs_adc_gain"}, /* Gain setting for current sense ADC (two's complement), */\ + { 0x8500, "cs_resonator_enable"}, /* Enable for resonator to improve SRN , */\ + { 0x8510, "cs_classd_tran_skip"}, /* Skip current sense connection during a classD amplifier transition, */\ + { 0x8530, "cs_inn_short"}, /* Short current sense negative to common mode , */\ + { 0x8540, "cs_inp_short"}, /* Short current sense positive to common mode , */\ + { 0x8550, "cs_ldo_bypass"}, /* Bypass current sense LDO , */\ + { 0x8560, "cs_ldo_pulldown"}, /* Pull down current sense LDO, only valid if left_enbl_cs_ldo is high, */\ + { 0x8574, "cs_ldo_voset"}, /* Current sense LDO voltage level setting (two's complement), */\ + { 0x8600, "enbl_cs_adc_left"}, /* Enable current sense ADC , */\ + { 0x8610, "enbl_cs_inn1_left"}, /* Enable connection of current sense negative1 , */\ + { 0x8630, "enbl_cs_inp1_left"}, /* Enable connection of current sense positive1 , */\ + { 0x8650, "enbl_cs_ldo_left"}, /* Enable current sense LDO , */\ + { 0x8660, "enbl_cs_nofloating_n_left"}, /* Connect current sense negative to gnda at transitions of booster or classd amplifiers. Otherwise floating (0), */\ + { 0x8670, "enbl_cs_nofloating_p_left"}, /* Connect current sense positive to gnda at transitions of booster or classd amplifiers. Otherwise floating (0), */\ + { 0x8680, "enbl_cs_vbatldo_left"}, /* Enable of current sense LDO , */\ + { 0x8700, "enbl_cs_adc_right"}, /* Enable current sense ADC , */\ + { 0x8710, "enbl_cs_inn1_right"}, /* Enable connection of current sense negative1 , */\ + { 0x8730, "enbl_cs_inp1_right"}, /* Enable connection of current sense positive1 , */\ + { 0x8750, "enbl_cs_ldo_right"}, /* Enable current sense LDO , */\ + { 0x8760, "enbl_cs_nofloating_n_right"}, /* Connect current sense negative to gnda at transitions of booster or classd amplifiers. Otherwise floating (0), */\ + { 0x8770, "enbl_cs_nofloating_p_right"}, /* Connect current sense positive to gnda at transitions of booster or classd amplifiers. Otherwise floating (0), */\ + { 0x8780, "enbl_cs_vbatldo_right"}, /* Enable of current sense LDO , */\ + { 0x8800, "volsense_pwm_sel"}, /* Voltage sense PWM source selection control , */\ + { 0x8810, "volsense_dc_offset"}, /* Voltage sense decimator offset control , */\ + { 0x9000, "cf_rst_dsp"}, /* Reset , */\ + { 0x9011, "cf_dmem"}, /* Target memory , */\ + { 0x9030, "cf_aif"}, /* Auto increment , */\ + { 0x9040, "cf_int"}, /* Interrupt - auto clear , */\ + { 0x9050, "cf_cgate_off"}, /* Coolflux clock gating disabling control , */\ + { 0x9080, "cf_req_cmd"}, /* Firmware event request rpc command , */\ + { 0x9090, "cf_req_reset"}, /* Firmware event request reset restart , */\ + { 0x90a0, "cf_req_mips"}, /* Firmware event request short on mips , */\ + { 0x90b0, "cf_req_mute_ready"}, /* Firmware event request mute sequence ready , */\ + { 0x90c0, "cf_req_volume_ready"}, /* Firmware event request volume ready , */\ + { 0x90d0, "cf_req_damage"}, /* Firmware event request speaker damage detected , */\ + { 0x90e0, "cf_req_calibrate_ready"}, /* Firmware event request calibration completed , */\ + { 0x90f0, "cf_req_reserved"}, /* Firmware event request reserved , */\ + { 0x910f, "cf_madd"}, /* Memory address , */\ + { 0x920f, "cf_mema"}, /* Activate memory access , */\ + { 0x9307, "cf_err"}, /* Error flags , */\ + { 0x9387, "cf_ack"}, /* Acknowledge of requests , */\ + { 0x9380, "cf_ack_cmd"}, /* Firmware event acknowledge rpc command , */\ + { 0x9390, "cf_ack_reset"}, /* Firmware event acknowledge reset restart , */\ + { 0x93a0, "cf_ack_mips"}, /* Firmware event acknowledge short on mips , */\ + { 0x93b0, "cf_ack_mute_ready"}, /* Firmware event acknowledge mute sequence ready , */\ + { 0x93c0, "cf_ack_volume_ready"}, /* Firmware event acknowledge volume ready , */\ + { 0x93d0, "cf_ack_damage"}, /* Firmware event acknowledge speaker damage detected, */\ + { 0x93e0, "cf_ack_calibrate_ready"}, /* Firmware event acknowledge calibration completed , */\ + { 0x93f0, "cf_ack_reserved"}, /* Firmware event acknowledge reserved , */\ + { 0x980f, "ivt_addr0_msb"}, /* Coolflux interrupt vector table address0 MSB , */\ + { 0x990f, "ivt_addr0_lsb"}, /* Coolflux interrupt vector table address0 LSB , */\ + { 0x9a0f, "ivt_addr1_msb"}, /* Coolflux interrupt vector table address1 MSB , */\ + { 0x9b0f, "ivt_addr1_lsb"}, /* Coolflux interrupt vector table address1 LSB , */\ + { 0x9c0f, "ivt_addr2_msb"}, /* Coolflux interrupt vector table address2 MSB , */\ + { 0x9d0f, "ivt_addr2_lsb"}, /* Coolflux interrupt vector table address2 LSB , */\ + { 0x9e0f, "ivt_addr3_msb"}, /* Coolflux interrupt vector table address3 MSB , */\ + { 0x9f0f, "ivt_addr3_lsb"}, /* Coolflux interrupt vector table address3 LSB , */\ + { 0xa007, "mtpkey1"}, /* 5Ah, 90d To access KEY1_Protected registers (Default for engineering), */\ + { 0xa107, "mtpkey2"}, /* MTP KEY2 register , */\ + { 0xa200, "key01_locked"}, /* Indicates KEY1 is locked , */\ + { 0xa210, "key02_locked"}, /* Indicates KEY2 is locked , */\ + { 0xa302, "mtp_man_address_in"}, /* MTP address from I2C register for read/writing mtp in manual single word mode, */\ + { 0xa330, "man_copy_mtp_to_iic"}, /* Start copying single word from mtp to I2C mtp register, */\ + { 0xa340, "man_copy_iic_to_mtp"}, /* Start copying single word from I2C mtp register to mtp, */\ + { 0xa350, "auto_copy_mtp_to_iic"}, /* Start copying all the data from mtp to I2C mtp registers, */\ + { 0xa360, "auto_copy_iic_to_mtp"}, /* Start copying data from I2C mtp registers to mtp , */\ + { 0xa400, "faim_set_clkws"}, /* Sets the faim controller clock wait state register, */\ + { 0xa410, "faim_sel_evenrows"}, /* All even rows of the faim are selected, active high, */\ + { 0xa420, "faim_sel_oddrows"}, /* All odd rows of the faim are selected, all rows in combination with sel_evenrows, */\ + { 0xa430, "faim_program_only"}, /* Skip the erase access at wr_faim command (write-program-marginread), */\ + { 0xa440, "faim_erase_only"}, /* Skip the program access at wr_faim command (write-erase-marginread), */\ + { 0xa50f, "mtp_man_data_out_msb"}, /* MSB word of MTP manual read data , */\ + { 0xa60f, "mtp_man_data_out_lsb"}, /* LSB word of MTP manual read data , */\ + { 0xa70f, "mtp_man_data_in_msb"}, /* MSB word of write data for MTP manual write , */\ + { 0xa80f, "mtp_man_data_in_lsb"}, /* LSB word of write data for MTP manual write , */\ + { 0xb010, "bypass_ocpcounter"}, /* Bypass OCP Counter , */\ + { 0xb020, "bypass_glitchfilter"}, /* Bypass glitch filter , */\ + { 0xb030, "bypass_ovp"}, /* Bypass OVP , */\ + { 0xb040, "bypass_uvp"}, /* Bypass UVP , */\ + { 0xb050, "bypass_otp"}, /* Bypass OTP , */\ + { 0xb060, "bypass_lost_clk"}, /* Bypass lost clock detector , */\ + { 0xb070, "ctrl_vpalarm"}, /* vpalarm (uvp ovp handling) , */\ + { 0xb087, "ocp_threshold"}, /* OCP threshold level , */\ + { 0xb108, "ext_temp"}, /* External temperature (C) , */\ + { 0xb190, "ext_temp_sel"}, /* Select temp Speaker calibration , */\ + { 0xc000, "use_direct_ctrls"}, /* Direct control to overrule several functions for testing, */\ + { 0xc010, "rst_datapath"}, /* Direct control for datapath reset , */\ + { 0xc020, "rst_cgu"}, /* Direct control for cgu reset , */\ + { 0xc038, "enbl_ref"}, /* Switch on the analog references, each part of the references can be switched on/off individually, */\ + { 0xc0d0, "enbl_ringo"}, /* Enable the ring oscillator for test purpose , */\ + { 0xc0e0, "use_direct_clk_ctrl"}, /* Direct clock control to overrule several functions for testing, */\ + { 0xc0f0, "use_direct_pll_ctrl"}, /* Direct PLL control to overrule several functions for testing, */\ + { 0xc100, "enbl_tsense"}, /* Temperature sensor enable control - I2C direct mode, */\ + { 0xc110, "tsense_hibias"}, /* Bit to set the biasing in temp sensor to high , */\ + { 0xc120, "enbl_flag_vbg"}, /* Enable flagging of bandgap out of control , */\ + { 0xc20f, "abist_offset"}, /* Offset control for ABIST testing (two's complement), */\ + { 0xc300, "bypasslatch"}, /* Bypass latch , */\ + { 0xc311, "sourcea"}, /* Set OUTA to , */\ + { 0xc331, "sourceb"}, /* Set OUTB to , */\ + { 0xc350, "inverta"}, /* Invert pwma test signal , */\ + { 0xc360, "invertb"}, /* Invert pwmb test signal , */\ + { 0xc374, "pulselength"}, /* Pulse length setting test input for amplifier (clock d - k*2048*fs), */\ + { 0xc3c0, "tdm_enable_loopback"}, /* TDM loopback test , */\ + { 0xc3d0, "test_abistfft_enbl"}, /* FFT Coolflux , */\ + { 0xc3e0, "test_pwr_switch"}, /* Test mode for digital power switches core sw/mem sw/micvdd sw, */\ + { 0xc400, "bst_bypasslatch"}, /* Bypass latch in boost converter , */\ + { 0xc411, "bst_source"}, /* Sets the source of the pwmbst output to boost converter input for testing, */\ + { 0xc430, "bst_invertb"}, /* Invert pwmbst test signal , */\ + { 0xc444, "bst_pulselength"}, /* Pulse length setting test input for boost converter , */\ + { 0xc490, "test_bst_ctrlsthv"}, /* Test mode for boost control stage , */\ + { 0xc4a0, "test_bst_iddq"}, /* IDDQ testing in power stage of boost converter , */\ + { 0xc4b0, "test_bst_rdson"}, /* RDSON testing - boost power stage , */\ + { 0xc4c0, "test_bst_cvi"}, /* CVI testing - boost power stage , */\ + { 0xc4d0, "test_bst_ocp"}, /* Boost OCP. For old ocp (ctrl_reversebst is 0), For new ocp (ctrl_reversebst is 1), */\ + { 0xc4e0, "test_bst_sense"}, /* Test option for the sense NMOS in booster for current mode control., */\ + { 0xc500, "test_cvi"}, /* Analog BIST, switch choose which transistor will be used as current source (also cross coupled sources possible), */\ + { 0xc510, "test_discrete"}, /* Test function noise measurement , */\ + { 0xc520, "test_iddq"}, /* Set the power stages in iddq mode for gate stress., */\ + { 0xc540, "test_rdson"}, /* Analog BIST, switch to enable Rdson measurement , */\ + { 0xc550, "test_sdelta"}, /* Analog BIST, noise test , */\ + { 0xc570, "test_enbl_cs"}, /* Enable for digimux mode of current sense , */\ + { 0xc600, "enbl_pwm_dcc"}, /* Enables direct control of pwm duty cycle for DCDC power stage, */\ + { 0xc613, "pwm_dcc_cnt"}, /* Control pwm duty cycle when enbl_pwm_dcc is 1 , */\ + { 0xc650, "enbl_ldo_stress"}, /* Enable stress of internal supply voltages powerstages, */\ + { 0xc660, "bypass_diosw_ovp"}, /* Bypass ovp for memory switch diosw , */\ + { 0xc670, "enbl_powerswitch"}, /* Vddd core power switch control - overrules the manager control, */\ + { 0xc707, "digimuxa_sel"}, /* DigimuxA input selection control routed to GPIO1 (see Digimux list for details), */\ + { 0xc787, "digimuxb_sel"}, /* DigimuxB input selection control routed to GPIO2 (see Digimux list for details), */\ + { 0xc807, "digimuxc_sel"}, /* DigimuxC input selection control routed to GPIO3 (see Digimux list for details), */\ + { 0xc887, "digimuxd_sel"}, /* DigimuxD input selection control routed to GPIO4 (see Digimux list for details), */\ + { 0xc901, "dio1_ehs"}, /* Speed/load setting for DIO1 IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc921, "dio2_ehs"}, /* Speed/load setting for DIO2 IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc941, "gainio_ehs"}, /* Speed/load setting for GAINIO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc961, "pdmo_ehs"}, /* Speed/load setting for PDMO IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc981, "int_ehs"}, /* Speed/load setting for INT IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc9a1, "tdo_ehs"}, /* Speed/load setting for TDO IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc9c0, "hs_mode"}, /* I2C high speed mode control , */\ + { 0xca00, "enbl_anamux1"}, /* Enable anamux1 , */\ + { 0xca10, "enbl_anamux2"}, /* Enable anamux2 , */\ + { 0xca20, "enbl_anamux3"}, /* Enable anamux3 , */\ + { 0xca30, "enbl_anamux4"}, /* Enable anamux4 , */\ + { 0xca40, "enbl_anamux5"}, /* Enable anamux5 , */\ + { 0xca50, "enbl_anamux6"}, /* Enable anamux6 , */\ + { 0xca60, "enbl_anamux7"}, /* Enable anamux7 , */\ + { 0xca74, "anamux1"}, /* Anamux selection control - anamux on TEST1 , */\ + { 0xcb04, "anamux2"}, /* Anamux selection control - anamux on TEST2 , */\ + { 0xcb54, "anamux3"}, /* Anamux selection control - anamux on TEST3 , */\ + { 0xcba4, "anamux4"}, /* Anamux selection control - anamux on TEST4 , */\ + { 0xcc04, "anamux5"}, /* Anamux selection control - anamux on TEST5 , */\ + { 0xcc54, "anamux6"}, /* Anamux selection control - anamux on TEST6 , */\ + { 0xcca4, "anamux7"}, /* Anamux selection control - anamux on TEST7 , */\ + { 0xcd05, "pll_seli"}, /* PLL SELI - I2C direct PLL control mode only , */\ + { 0xcd64, "pll_selp"}, /* PLL SELP - I2C direct PLL control mode only , */\ + { 0xcdb3, "pll_selr"}, /* PLL SELR - I2C direct PLL control mode only , */\ + { 0xcdf0, "pll_frm"}, /* PLL free running mode control; 1 in TCB direct control mode, else this control bit, */\ + { 0xce09, "pll_ndec"}, /* PLL NDEC - I2C direct PLL control mode only , */\ + { 0xcea0, "pll_mdec_msb"}, /* MSB of pll_mdec - I2C direct PLL control mode only, */\ + { 0xceb0, "enbl_pll"}, /* Enables PLL in I2C direct PLL control mode only , */\ + { 0xcec0, "enbl_osc"}, /* Enables OSC1M in I2C direct control mode only , */\ + { 0xced0, "pll_bypass"}, /* PLL bypass control in I2C direct PLL control mode only, */\ + { 0xcee0, "pll_directi"}, /* PLL directi control in I2C direct PLL control mode only, */\ + { 0xcef0, "pll_directo"}, /* PLL directo control in I2C direct PLL control mode only, */\ + { 0xcf0f, "pll_mdec_lsb"}, /* Bits 15..0 of PLL MDEC are I2C direct PLL control mode only, */\ + { 0xd006, "pll_pdec"}, /* PLL PDEC - I2C direct PLL control mode only , */\ + { 0xd10f, "tsig_freq_lsb"}, /* Internal sinus test generator frequency control , */\ + { 0xd202, "tsig_freq_msb"}, /* Select internal sinus test generator, frequency control msb bits, */\ + { 0xd230, "inject_tsig"}, /* Control bit to switch to internal sinus test generator, */\ + { 0xd243, "tsig_gain_left"}, /* Test signal gain for left channel , */\ + { 0xd283, "tsig_gain_right"}, /* Test signal gain for right channel , */\ + { 0xd300, "adc10_reset"}, /* Reset for ADC10 - I2C direct control mode , */\ + { 0xd311, "adc10_test"}, /* Test mode selection signal for ADC10 - I2C direct control mode, */\ + { 0xd332, "adc10_sel"}, /* Select the input to convert for ADC10 - I2C direct control mode, */\ + { 0xd364, "adc10_prog_sample"}, /* ADC10 program sample setting - I2C direct control mode, */\ + { 0xd3b0, "adc10_enbl"}, /* Enable ADC10 - I2C direct control mode , */\ + { 0xd3c0, "bypass_lp_vbat"}, /* Bypass control for Low pass filter in batt sensor , */\ + { 0xd409, "data_adc10_tempbat"}, /* ADC 10 data output data for testing , */\ + { 0xd506, "ctrl_digtoana_hidden"}, /* Spare digital to analog control bits - Hidden , */\ + { 0xd570, "enbl_clk_out_of_range"}, /* Clock out of range , */\ + { 0xf000, "calibration_onetime"}, /* Calibration schedule , */\ + { 0xf010, "calibr_ron_done"}, /* Calibration Ron executed , */\ + { 0xf020, "calibr_dcdc_api_calibrate"}, /* Calibration current limit DCDC , */\ + { 0xf030, "calibr_dcdc_delta_sign"}, /* Sign bit for delta calibration current limit DCDC , */\ + { 0xf042, "calibr_dcdc_delta"}, /* Calibration delta current limit DCDC , */\ + { 0xf078, "calibr_speaker_info"}, /* Reserved space for allowing customer to store speaker information, */\ + { 0xf105, "calibr_vout_offset"}, /* DCDC offset calibration 2's complement (key1 protected), */\ + { 0xf163, "calibr_gain_left"}, /* HW gain module - left channel (2's complement) , */\ + { 0xf1a5, "calibr_offset_left"}, /* Offset for amplifier, HW gain module - left channel (2's complement), */\ + { 0xf203, "calibr_gain_right"}, /* HW gain module - right channel (2's complement) , */\ + { 0xf245, "calibr_offset_right"}, /* Offset for amplifier, HW gain module - right channel (2's complement), */\ + { 0xf2a3, "calibr_rcvldop_trim"}, /* Trimming of LDO (2.7V) , */\ + { 0xf307, "calibr_gain_cs_left"}, /* Current sense gain - left channel (signed two's complement format), */\ + { 0xf387, "calibr_gain_cs_right"}, /* Current sense gain - right channel (signed two's complement format), */\ + { 0xf40f, "calibr_R25C_L"}, /* Ron resistance of left channel speaker coil , */\ + { 0xf50f, "calibr_R25C_R"}, /* Ron resistance of right channel speaker coil , */\ + { 0xf606, "ctrl_offset_a_left"}, /* Offset of left amplifier level shifter A , */\ + { 0xf686, "ctrl_offset_b_left"}, /* Offset of left amplifier level shifter B , */\ + { 0xf706, "ctrl_offset_a_right"}, /* Offset of right amplifier level shifter A , */\ + { 0xf786, "ctrl_offset_b_right"}, /* Offset of right amplifier level shifter B , */\ + { 0xf806, "htol_iic_addr"}, /* 7-bit I2C address to be used during HTOL testing , */\ + { 0xf870, "htol_iic_addr_en"}, /* HTOL I2C address enable control , */\ + { 0xf884, "calibr_temp_offset"}, /* Temperature offset 2's compliment (key1 protected), */\ + { 0xf8d2, "calibr_temp_gain"}, /* Temperature gain 2's compliment (key1 protected) , */\ + { 0xf900, "mtp_lock_dcdcoff_mode"}, /* Disable function dcdcoff_mode , */\ + { 0xf910, "mtp_lock_enbl_coolflux"}, /* Disable function enbl_coolflux , */\ + { 0xf920, "mtp_lock_bypass_clipper"}, /* Disable function bypass_clipper , */\ + { 0xf930, "mtp_lock_max_dcdc_voltage"}, /* Disable programming of max dcdc boost voltage , */\ + { 0xf943, "calibr_vbg_trim"}, /* Bandgap trimming control , */\ + { 0xf987, "type_bits_fw"}, /* MTP-control FW - See Firmware I2C API document for details, */\ + { 0xfa0f, "mtpdataA"}, /* MTPdataA (key1 protected) , */\ + { 0xfb0f, "mtpdataB"}, /* MTPdataB (key1 protected) , */\ + { 0xfc0f, "mtpdataC"}, /* MTPdataC (key1 protected) , */\ + { 0xfd0f, "mtpdataD"}, /* MTPdataD (key1 protected) , */\ + { 0xfe0f, "mtpdataE"}, /* MTPdataE (key1 protected) , */\ + { 0xff05, "calibr_osc_delta_ndiv"}, /* Calibration data for OSC1M, signed number representation, */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +enum tfa2_irq { + tfa2_irq_stvdds = 0, + tfa2_irq_stplls = 1, + tfa2_irq_stotds = 2, + tfa2_irq_stovds = 3, + tfa2_irq_stuvds = 4, + tfa2_irq_stclks = 5, + tfa2_irq_stmtpb = 6, + tfa2_irq_stnoclk = 7, + tfa2_irq_stspks = 8, + tfa2_irq_stacs = 9, + tfa2_irq_stsws = 10, + tfa2_irq_stwds = 11, + tfa2_irq_stamps = 12, + tfa2_irq_starefs = 13, + tfa2_irq_stadccr = 14, + tfa2_irq_stbodnok = 15, + tfa2_irq_stbstcu = 16, + tfa2_irq_stbsthi = 17, + tfa2_irq_stbstoc = 18, + tfa2_irq_stbstpkcur = 19, + tfa2_irq_stbstvc = 20, + tfa2_irq_stbst86 = 21, + tfa2_irq_stbst93 = 22, + tfa2_irq_strcvld = 23, + tfa2_irq_stocpl = 24, + tfa2_irq_stocpr = 25, + tfa2_irq_stmwsrc = 26, + tfa2_irq_stmwcfc = 27, + tfa2_irq_stmwsmu = 28, + tfa2_irq_stcfmer = 29, + tfa2_irq_stcfmac = 30, + tfa2_irq_stclkoor = 31, + tfa2_irq_sttdmer = 32, + tfa2_irq_stclpl = 33, + tfa2_irq_stclpr = 34, + tfa2_irq_stocpm = 35, + tfa2_irq_max = 36, + tfa2_irq_all = -1 /* all irqs */}; + +#define TFA2_IRQ_NAMETABLE static tfaIrqName_t Tfa2IrqNames[]= {\ + { 0, "STVDDS"},\ + { 1, "STPLLS"},\ + { 2, "STOTDS"},\ + { 3, "STOVDS"},\ + { 4, "STUVDS"},\ + { 5, "STCLKS"},\ + { 6, "STMTPB"},\ + { 7, "STNOCLK"},\ + { 8, "STSPKS"},\ + { 9, "STACS"},\ + { 10, "STSWS"},\ + { 11, "STWDS"},\ + { 12, "STAMPS"},\ + { 13, "STAREFS"},\ + { 14, "STADCCR"},\ + { 15, "STBODNOK"},\ + { 16, "STBSTCU"},\ + { 17, "STBSTHI"},\ + { 18, "STBSTOC"},\ + { 19, "STBSTPKCUR"},\ + { 20, "STBSTVC"},\ + { 21, "STBST86"},\ + { 22, "STBST93"},\ + { 23, "STRCVLD"},\ + { 24, "STOCPL"},\ + { 25, "STOCPR"},\ + { 26, "STMWSRC"},\ + { 27, "STMWCFC"},\ + { 28, "STMWSMU"},\ + { 29, "STCFMER"},\ + { 30, "STCFMAC"},\ + { 31, "STCLKOOR"},\ + { 32, "STTDMER"},\ + { 33, "STCLPL"},\ + { 34, "STCLPR"},\ + { 35, "STOCPM"},\ + { 36, "36"},\ +}; diff --git a/sound/soc/codecs/inc/tfa9872_tfafieldnames.h b/sound/soc/codecs/inc/tfa9872_tfafieldnames.h new file mode 100644 index 000000000000..23669d4c0011 --- /dev/null +++ b/sound/soc/codecs/inc/tfa9872_tfafieldnames.h @@ -0,0 +1,1219 @@ +/** Filename: Tfa9872_TfaFieldnames.h + * This file was generated automatically on 10/11/16 at 10:19:27. + * Source file: TFA9872N1B2_DefaultI2CSettings -v16.xlsx + */ + +#ifndef _TFA9872_TFAFIELDNAMES_H +#define _TFA9872_TFAFIELDNAMES_H + + +#define TFA9872_I2CVERSION_N1A 26 +#define TFA9872_I2CVERSION_N1B 29 +#define TFA9872_I2CVERSION_N1B2 21 + + +typedef enum nxpTfa9872BfEnumList { + TFA9872_BF_PWDN = 0x0000, /*!< Powerdown selection */ + TFA9872_BF_I2CR = 0x0010, /*!< I2C Reset - Auto clear */ + TFA9872_BF_AMPE = 0x0030, /*!< Activate Amplifier */ + TFA9872_BF_DCA = 0x0040, /*!< Activate DC-to-DC converter */ + TFA9872_BF_INTP = 0x0071, /*!< Interrupt config */ + TFA9872_BF_BYPOCP= 0x00b0, /*!< Bypass OCP */ + TFA9872_BF_TSTOCP= 0x00c0, /*!< OCP testing control */ + TFA9872_BF_MANSCONF= 0x0120, /*!< I2C configured */ + TFA9872_BF_MANAOOSC= 0x0140, /*!< Internal osc off at PWDN */ + TFA9872_BF_MUTETO= 0x01d0, /*!< Time out SB mute sequence */ + TFA9872_BF_RCVNS = 0x01e0, /*!< Noise shaper selection */ + TFA9872_BF_AUDFS = 0x0203, /*!< Sample rate (fs) */ + TFA9872_BF_INPLEV= 0x0240, /*!< TDM output attenuation */ + TFA9872_BF_FRACTDEL= 0x0255, /*!< V/I Fractional delay */ + TFA9872_BF_BYPHVBF= 0x02b0, /*!< Bypass HVBAT filter */ + TFA9872_BF_REV = 0x030f, /*!< Revision info */ + TFA9872_BF_REFCKEXT= 0x0401, /*!< PLL external ref clock */ + TFA9872_BF_REFCKSEL= 0x0420, /*!< PLL internal ref clock */ + TFA9872_BF_SSE = 0x0510, /*!< Enable speaker path */ + TFA9872_BF_VSE = 0x0530, /*!< Voltage sense */ + TFA9872_BF_CSE = 0x0550, /*!< Current sense */ + TFA9872_BF_SSPDME= 0x0560, /*!< Sub-system PDM */ + TFA9872_BF_PGAE = 0x0580, /*!< Enable PGA chop clock */ + TFA9872_BF_SSTDME= 0x0590, /*!< Sub-system TDM */ + TFA9872_BF_SSPBSTE= 0x05a0, /*!< Sub-system boost */ + TFA9872_BF_SSADCE= 0x05b0, /*!< Sub-system ADC */ + TFA9872_BF_SSFAIME= 0x05c0, /*!< Sub-system FAIM */ + TFA9872_BF_STGAIN= 0x0d18, /*!< Side tone gain */ + TFA9872_BF_STSMUTE= 0x0da0, /*!< Side tone soft mute */ + TFA9872_BF_ST1C = 0x0db0, /*!< side tone one s complement */ + TFA9872_BF_VDDS = 0x1000, /*!< POR */ + TFA9872_BF_PLLS = 0x1010, /*!< PLL lock */ + TFA9872_BF_OTDS = 0x1020, /*!< OTP alarm */ + TFA9872_BF_OVDS = 0x1030, /*!< OVP alarm */ + TFA9872_BF_UVDS = 0x1040, /*!< UVP alarm */ + TFA9872_BF_CLKS = 0x1050, /*!< Clocks stable */ + TFA9872_BF_MTPB = 0x1060, /*!< MTP busy */ + TFA9872_BF_NOCLK = 0x1070, /*!< Lost clock */ + TFA9872_BF_SWS = 0x10a0, /*!< Amplifier engage */ + TFA9872_BF_AMPS = 0x10c0, /*!< Amplifier enable */ + TFA9872_BF_AREFS = 0x10d0, /*!< References enable */ + TFA9872_BF_ADCCR = 0x10e0, /*!< Control ADC */ + TFA9872_BF_DCIL = 0x1100, /*!< DCDC current limiting */ + TFA9872_BF_DCDCA = 0x1110, /*!< DCDC active */ + TFA9872_BF_DCOCPOK= 0x1120, /*!< DCDC OCP nmos */ + TFA9872_BF_DCHVBAT= 0x1140, /*!< DCDC level 1x */ + TFA9872_BF_DCH114= 0x1150, /*!< DCDC level 1.14x */ + TFA9872_BF_DCH107= 0x1160, /*!< DCDC level 1.07x */ + TFA9872_BF_STMUTEB= 0x1170, /*!< side tone (un)mute busy */ + TFA9872_BF_STMUTE= 0x1180, /*!< side tone mute state */ + TFA9872_BF_TDMLUTER= 0x1190, /*!< TDM LUT error */ + TFA9872_BF_TDMSTAT= 0x11a2, /*!< TDM status bits */ + TFA9872_BF_TDMERR= 0x11d0, /*!< TDM error */ + TFA9872_BF_OCPOAP= 0x1300, /*!< OCPOK pmos A */ + TFA9872_BF_OCPOAN= 0x1310, /*!< OCPOK nmos A */ + TFA9872_BF_OCPOBP= 0x1320, /*!< OCPOK pmos B */ + TFA9872_BF_OCPOBN= 0x1330, /*!< OCPOK nmos B */ + TFA9872_BF_CLIPAH= 0x1340, /*!< Clipping A to Vddp */ + TFA9872_BF_CLIPAL= 0x1350, /*!< Clipping A to gnd */ + TFA9872_BF_CLIPBH= 0x1360, /*!< Clipping B to Vddp */ + TFA9872_BF_CLIPBL= 0x1370, /*!< Clipping B to gnd */ + TFA9872_BF_OCDS = 0x1380, /*!< OCP amplifier */ + TFA9872_BF_CLIPS = 0x1390, /*!< Amplifier clipping */ + TFA9872_BF_OCPOKMC= 0x13a0, /*!< OCPOK MICVDD */ + TFA9872_BF_MANALARM= 0x13b0, /*!< Alarm state */ + TFA9872_BF_MANWAIT1= 0x13c0, /*!< Wait HW I2C settings */ + TFA9872_BF_MANMUTE= 0x13e0, /*!< Audio mute sequence */ + TFA9872_BF_MANOPER= 0x13f0, /*!< Operating state */ + TFA9872_BF_CLKOOR= 0x1420, /*!< External clock status */ + TFA9872_BF_MANSTATE= 0x1433, /*!< Device manager status */ + TFA9872_BF_DCMODE= 0x1471, /*!< DCDC mode status bits */ + TFA9872_BF_BATS = 0x1509, /*!< Battery voltage (V) */ + TFA9872_BF_TEMPS = 0x1608, /*!< IC Temperature (C) */ + TFA9872_BF_VDDPS = 0x1709, /*!< IC VDDP voltage ( 1023*VDDP/9.5 V) */ + TFA9872_BF_TDME = 0x2040, /*!< Enable interface */ + TFA9872_BF_TDMMODE= 0x2050, /*!< Slave/master */ + TFA9872_BF_TDMCLINV= 0x2060, /*!< Reception data to BCK clock */ + TFA9872_BF_TDMFSLN= 0x2073, /*!< FS length (master mode only) */ + TFA9872_BF_TDMFSPOL= 0x20b0, /*!< FS polarity */ + TFA9872_BF_TDMNBCK= 0x20c3, /*!< N-BCK's in FS */ + TFA9872_BF_TDMSLOTS= 0x2103, /*!< N-slots in Frame */ + TFA9872_BF_TDMSLLN= 0x2144, /*!< N-bits in slot */ + TFA9872_BF_TDMBRMG= 0x2194, /*!< N-bits remaining */ + TFA9872_BF_TDMDEL= 0x21e0, /*!< data delay to FS */ + TFA9872_BF_TDMADJ= 0x21f0, /*!< data adjustment */ + TFA9872_BF_TDMOOMP= 0x2201, /*!< Received audio compression */ + TFA9872_BF_TDMSSIZE= 0x2224, /*!< Sample size per slot */ + TFA9872_BF_TDMTXDFO= 0x2271, /*!< Format unused bits */ + TFA9872_BF_TDMTXUS0= 0x2291, /*!< Format unused slots DATAO */ + TFA9872_BF_TDMSPKE= 0x2300, /*!< Control audio tdm channel in 0 (spkr + dcdc) */ + TFA9872_BF_TDMDCE= 0x2310, /*!< Control audio tdm channel in 1 (dcdc) */ + TFA9872_BF_TDMCSE= 0x2330, /*!< current sense vbat temperature and vddp feedback */ + TFA9872_BF_TDMVSE= 0x2340, /*!< Voltage sense vbat temperature and vddp feedback */ + TFA9872_BF_TDMSPKS= 0x2603, /*!< tdm slot for sink 0 (speaker + dcdc) */ + TFA9872_BF_TDMDCS= 0x2643, /*!< tdm slot for sink 1 (dcdc) */ + TFA9872_BF_TDMCSS= 0x26c3, /*!< Slot Position of current sense vbat temperature and vddp feedback */ + TFA9872_BF_TDMVSS= 0x2703, /*!< Slot Position of Voltage sense vbat temperature and vddp feedback */ + TFA9872_BF_PDMSTSEL= 0x3111, /*!< Side tone input */ + TFA9872_BF_ISTVDDS= 0x4000, /*!< Status POR */ + TFA9872_BF_ISTPLLS= 0x4010, /*!< Status PLL lock */ + TFA9872_BF_ISTOTDS= 0x4020, /*!< Status OTP alarm */ + TFA9872_BF_ISTOVDS= 0x4030, /*!< Status OVP alarm */ + TFA9872_BF_ISTUVDS= 0x4040, /*!< Status UVP alarm */ + TFA9872_BF_ISTCLKS= 0x4050, /*!< Status clocks stable */ + TFA9872_BF_ISTMTPB= 0x4060, /*!< Status MTP busy */ + TFA9872_BF_ISTNOCLK= 0x4070, /*!< Status lost clock */ + TFA9872_BF_ISTSWS= 0x40a0, /*!< Status amplifier engage */ + TFA9872_BF_ISTAMPS= 0x40c0, /*!< Status amplifier enable */ + TFA9872_BF_ISTAREFS= 0x40d0, /*!< Status Ref enable */ + TFA9872_BF_ISTADCCR= 0x40e0, /*!< Status Control ADC */ + TFA9872_BF_ISTBSTCU= 0x4100, /*!< Status DCDC current limiting */ + TFA9872_BF_ISTBSTHI= 0x4110, /*!< Status DCDC active */ + TFA9872_BF_ISTBSTOC= 0x4120, /*!< Status DCDC OCP */ + TFA9872_BF_ISTBSTPKCUR= 0x4130, /*!< Status bst peakcur */ + TFA9872_BF_ISTBSTVC= 0x4140, /*!< Status DCDC level 1x */ + TFA9872_BF_ISTBST86= 0x4150, /*!< Status DCDC level 1.14x */ + TFA9872_BF_ISTBST93= 0x4160, /*!< Status DCDC level 1.07x */ + TFA9872_BF_ISTOCPR= 0x4190, /*!< Status ocp alarm */ + TFA9872_BF_ISTMWSRC= 0x41a0, /*!< Status Waits HW I2C settings */ + TFA9872_BF_ISTMWSMU= 0x41c0, /*!< Status Audio mute sequence */ + TFA9872_BF_ISTCLKOOR= 0x41f0, /*!< Status flag_clk_out_of_range */ + TFA9872_BF_ISTTDMER= 0x4200, /*!< Status tdm error */ + TFA9872_BF_ISTCLPR= 0x4220, /*!< Status clip */ + TFA9872_BF_ISTLP0= 0x4240, /*!< Status low power mode0 */ + TFA9872_BF_ISTLP1= 0x4250, /*!< Status low power mode1 */ + TFA9872_BF_ISTLA = 0x4260, /*!< Status low noise detection */ + TFA9872_BF_ISTVDDPH= 0x4270, /*!< Status VDDP greater than VBAT */ + TFA9872_BF_ICLVDDS= 0x4400, /*!< Clear POR */ + TFA9872_BF_ICLPLLS= 0x4410, /*!< Clear PLL lock */ + TFA9872_BF_ICLOTDS= 0x4420, /*!< Clear OTP alarm */ + TFA9872_BF_ICLOVDS= 0x4430, /*!< Clear OVP alarm */ + TFA9872_BF_ICLUVDS= 0x4440, /*!< Clear UVP alarm */ + TFA9872_BF_ICLCLKS= 0x4450, /*!< Clear clocks stable */ + TFA9872_BF_ICLMTPB= 0x4460, /*!< Clear mtp busy */ + TFA9872_BF_ICLNOCLK= 0x4470, /*!< Clear lost clk */ + TFA9872_BF_ICLSWS= 0x44a0, /*!< Clear amplifier engage */ + TFA9872_BF_ICLAMPS= 0x44c0, /*!< Clear enbl amp */ + TFA9872_BF_ICLAREFS= 0x44d0, /*!< Clear ref enable */ + TFA9872_BF_ICLADCCR= 0x44e0, /*!< Clear control ADC */ + TFA9872_BF_ICLBSTCU= 0x4500, /*!< Clear DCDC current limiting */ + TFA9872_BF_ICLBSTHI= 0x4510, /*!< Clear DCDC active */ + TFA9872_BF_ICLBSTOC= 0x4520, /*!< Clear DCDC OCP */ + TFA9872_BF_ICLBSTPC= 0x4530, /*!< Clear bst peakcur */ + TFA9872_BF_ICLBSTVC= 0x4540, /*!< Clear DCDC level 1x */ + TFA9872_BF_ICLBST86= 0x4550, /*!< Clear DCDC level 1.14x */ + TFA9872_BF_ICLBST93= 0x4560, /*!< Clear DCDC level 1.07x */ + TFA9872_BF_ICLOCPR= 0x4590, /*!< Clear ocp alarm */ + TFA9872_BF_ICLMWSRC= 0x45a0, /*!< Clear wait HW I2C settings */ + TFA9872_BF_ICLMWSMU= 0x45c0, /*!< Clear audio mute sequence */ + TFA9872_BF_ICLCLKOOR= 0x45f0, /*!< Clear flag_clk_out_of_range */ + TFA9872_BF_ICLTDMER= 0x4600, /*!< Clear tdm error */ + TFA9872_BF_ICLCLPR= 0x4620, /*!< Clear clip */ + TFA9872_BF_ICLLP0= 0x4640, /*!< Clear low power mode0 */ + TFA9872_BF_ICLLP1= 0x4650, /*!< Clear low power mode1 */ + TFA9872_BF_ICLLA = 0x4660, /*!< Clear low noise detection */ + TFA9872_BF_ICLVDDPH= 0x4670, /*!< Clear VDDP greater then VBAT */ + TFA9872_BF_IEVDDS= 0x4800, /*!< Enable por */ + TFA9872_BF_IEPLLS= 0x4810, /*!< Enable pll lock */ + TFA9872_BF_IEOTDS= 0x4820, /*!< Enable OTP alarm */ + TFA9872_BF_IEOVDS= 0x4830, /*!< Enable OVP alarm */ + TFA9872_BF_IEUVDS= 0x4840, /*!< Enable UVP alarm */ + TFA9872_BF_IECLKS= 0x4850, /*!< Enable clocks stable */ + TFA9872_BF_IEMTPB= 0x4860, /*!< Enable mtp busy */ + TFA9872_BF_IENOCLK= 0x4870, /*!< Enable lost clk */ + TFA9872_BF_IESWS = 0x48a0, /*!< Enable amplifier engage */ + TFA9872_BF_IEAMPS= 0x48c0, /*!< Enable enbl amp */ + TFA9872_BF_IEAREFS= 0x48d0, /*!< Enable ref enable */ + TFA9872_BF_IEADCCR= 0x48e0, /*!< Enable Control ADC */ + TFA9872_BF_IEBSTCU= 0x4900, /*!< Enable DCDC current limiting */ + TFA9872_BF_IEBSTHI= 0x4910, /*!< Enable DCDC active */ + TFA9872_BF_IEBSTOC= 0x4920, /*!< Enable DCDC OCP */ + TFA9872_BF_IEBSTPC= 0x4930, /*!< Enable bst peakcur */ + TFA9872_BF_IEBSTVC= 0x4940, /*!< Enable DCDC level 1x */ + TFA9872_BF_IEBST86= 0x4950, /*!< Enable DCDC level 1.14x */ + TFA9872_BF_IEBST93= 0x4960, /*!< Enable DCDC level 1.07x */ + TFA9872_BF_IEOCPR= 0x4990, /*!< Enable ocp alarm */ + TFA9872_BF_IEMWSRC= 0x49a0, /*!< Enable waits HW I2C settings */ + TFA9872_BF_IEMWSMU= 0x49c0, /*!< Enable man Audio mute sequence */ + TFA9872_BF_IECLKOOR= 0x49f0, /*!< Enable flag_clk_out_of_range */ + TFA9872_BF_IETDMER= 0x4a00, /*!< Enable tdm error */ + TFA9872_BF_IECLPR= 0x4a20, /*!< Enable clip */ + TFA9872_BF_IELP0 = 0x4a40, /*!< Enable low power mode0 */ + TFA9872_BF_IELP1 = 0x4a50, /*!< Enable low power mode1 */ + TFA9872_BF_IELA = 0x4a60, /*!< Enable low noise detection */ + TFA9872_BF_IEVDDPH= 0x4a70, /*!< Enable VDDP greater tehn VBAT */ + TFA9872_BF_IPOVDDS= 0x4c00, /*!< Polarity por */ + TFA9872_BF_IPOPLLS= 0x4c10, /*!< Polarity pll lock */ + TFA9872_BF_IPOOTDS= 0x4c20, /*!< Polarity OTP alarm */ + TFA9872_BF_IPOOVDS= 0x4c30, /*!< Polarity OVP alarm */ + TFA9872_BF_IPOUVDS= 0x4c40, /*!< Polarity UVP alarm */ + TFA9872_BF_IPOCLKS= 0x4c50, /*!< Polarity clocks stable */ + TFA9872_BF_IPOMTPB= 0x4c60, /*!< Polarity mtp busy */ + TFA9872_BF_IPONOCLK= 0x4c70, /*!< Polarity lost clk */ + TFA9872_BF_IPOSWS= 0x4ca0, /*!< Polarity amplifier engage */ + TFA9872_BF_IPOAMPS= 0x4cc0, /*!< Polarity enbl amp */ + TFA9872_BF_IPOAREFS= 0x4cd0, /*!< Polarity ref enable */ + TFA9872_BF_IPOADCCR= 0x4ce0, /*!< Polarity Control ADC */ + TFA9872_BF_IPOBSTCU= 0x4d00, /*!< Polarity DCDC current limiting */ + TFA9872_BF_IPOBSTHI= 0x4d10, /*!< Polarity DCDC active */ + TFA9872_BF_IPOBSTOC= 0x4d20, /*!< Polarity DCDC OCP */ + TFA9872_BF_IPOBSTPC= 0x4d30, /*!< Polarity bst peakcur */ + TFA9872_BF_IPOBSTVC= 0x4d40, /*!< Polarity DCDC level 1x */ + TFA9872_BF_IPOBST86= 0x4d50, /*!< Polarity DCDC level 1.14x */ + TFA9872_BF_IPOBST93= 0x4d60, /*!< Polarity DCDC level 1.07x */ + TFA9872_BF_IPOOCPR= 0x4d90, /*!< Polarity ocp alarm */ + TFA9872_BF_IPOMWSRC= 0x4da0, /*!< Polarity waits HW I2C settings */ + TFA9872_BF_IPOMWSMU= 0x4dc0, /*!< Polarity man audio mute sequence */ + TFA9872_BF_IPCLKOOR= 0x4df0, /*!< Polarity flag_clk_out_of_range */ + TFA9872_BF_IPOTDMER= 0x4e00, /*!< Polarity tdm error */ + TFA9872_BF_IPOCLPR= 0x4e20, /*!< Polarity clip right */ + TFA9872_BF_IPOLP0= 0x4e40, /*!< Polarity low power mode0 */ + TFA9872_BF_IPOLP1= 0x4e50, /*!< Polarity low power mode1 */ + TFA9872_BF_IPOLA = 0x4e60, /*!< Polarity low noise mode */ + TFA9872_BF_IPOVDDPH= 0x4e70, /*!< Polarity VDDP greater than VBAT */ + TFA9872_BF_BSSCR = 0x5001, /*!< Battery Safeguard attack time */ + TFA9872_BF_BSST = 0x5023, /*!< Battery Safeguard threshold voltage level */ + TFA9872_BF_BSSRL = 0x5061, /*!< Battery Safeguard maximum reduction */ + TFA9872_BF_BSSR = 0x50e0, /*!< Battery voltage read out */ + TFA9872_BF_BSSBY = 0x50f0, /*!< Bypass battery safeguard */ + TFA9872_BF_BSSS = 0x5100, /*!< Vbat prot steepness */ + TFA9872_BF_INTSMUTE= 0x5110, /*!< Soft mute HW */ + TFA9872_BF_HPFBYP= 0x5150, /*!< Bypass HPF */ + TFA9872_BF_DPSA = 0x5170, /*!< Enable DPSA */ + TFA9872_BF_CLIPCTRL= 0x5222, /*!< Clip control setting */ + TFA9872_BF_AMPGAIN= 0x5257, /*!< Amplifier gain */ + TFA9872_BF_SLOPEE= 0x52d0, /*!< Enables slope control */ + TFA9872_BF_SLOPESET= 0x52e0, /*!< Slope speed setting (bin. coded) */ + TFA9872_BF_PGAGAIN= 0x6081, /*!< PGA gain selection */ + TFA9872_BF_PGALPE= 0x60b0, /*!< Lowpass enable */ + TFA9872_BF_LPM0BYP= 0x6110, /*!< bypass low power idle mode */ + TFA9872_BF_TDMDCG= 0x6123, /*!< Second channel gain in case of stereo using a single coil. (Total gain depending on INPLEV). (In case of mono OR stereo using 2 separate DCDC channel 1 should be disabled using TDMDCE) */ + TFA9872_BF_TDMSPKG= 0x6163, /*!< Total gain depending on INPLEV setting (channel 0) */ + TFA9872_BF_STIDLEEN= 0x61b0, /*!< enable idle feature for channel 1 */ + TFA9872_BF_LNMODE= 0x62e1, /*!< ctrl select mode */ + TFA9872_BF_LPM1MODE= 0x64e1, /*!< low power mode control */ + TFA9872_BF_LPM1DIS= 0x65c0, /*!< low power mode1 detector control */ + TFA9872_BF_TDMSRCMAP= 0x6801, /*!< tdm source mapping */ + TFA9872_BF_TDMSRCAS= 0x6821, /*!< Sensed value A */ + TFA9872_BF_TDMSRCBS= 0x6841, /*!< Sensed value B */ + TFA9872_BF_ANCSEL= 0x6881, /*!< anc input */ + TFA9872_BF_ANC1C = 0x68a0, /*!< ANC one s complement */ + TFA9872_BF_SAMMODE= 0x6901, /*!< sam enable */ + TFA9872_BF_SAMSEL= 0x6920, /*!< sam source */ + TFA9872_BF_PDMOSELH= 0x6931, /*!< pdm out value when pdm_clk is higth */ + TFA9872_BF_PDMOSELL= 0x6951, /*!< pdm out value when pdm_clk is low */ + TFA9872_BF_SAMOSEL= 0x6970, /*!< ram output on mode sam and audio */ + TFA9872_BF_LP0 = 0x6e00, /*!< low power mode 0 detection */ + TFA9872_BF_LP1 = 0x6e10, /*!< low power mode 1 detection */ + TFA9872_BF_LA = 0x6e20, /*!< low amplitude detection */ + TFA9872_BF_VDDPH = 0x6e30, /*!< vddp greater than vbat */ + TFA9872_BF_DELCURCOMP= 0x6f02, /*!< delay to allign compensation signal with current sense signal */ + TFA9872_BF_SIGCURCOMP= 0x6f40, /*!< polarity of compensation for current sense */ + TFA9872_BF_ENCURCOMP= 0x6f50, /*!< enable current sense compensation */ + TFA9872_BF_SELCLPPWM= 0x6f60, /*!< Select pwm clip flag */ + TFA9872_BF_LVLCLPPWM= 0x6f72, /*!< set the amount of pwm pulse that may be skipped before clip-flag is triggered */ + TFA9872_BF_DCVOS = 0x7002, /*!< Second boost voltage level */ + TFA9872_BF_DCMCC = 0x7033, /*!< Max coil current */ + TFA9872_BF_DCCV = 0x7071, /*!< Slope compensation current, represents LxF (inductance x frequency) value */ + TFA9872_BF_DCIE = 0x7090, /*!< Adaptive boost mode */ + TFA9872_BF_DCSR = 0x70a0, /*!< Soft ramp up/down */ + TFA9872_BF_DCDIS = 0x70e0, /*!< DCDC on/off */ + TFA9872_BF_DCPWM = 0x70f0, /*!< DCDC PWM only mode */ + TFA9872_BF_DCVOF = 0x7402, /*!< 1st boost voltage level */ + TFA9872_BF_DCTRACK= 0x7430, /*!< Boost algorithm selection, effective only when boost_intelligent is set to 1 */ + TFA9872_BF_DCTRIP= 0x7444, /*!< 1st Adaptive boost trip levels, effective only when DCIE is set to 1 */ + TFA9872_BF_DCHOLD= 0x7494, /*!< Hold time for DCDC booster, effective only when boost_intelligent is set to 1 */ + TFA9872_BF_DCTRIP2= 0x7534, /*!< 2nd Adaptive boost trip levels, effective only when DCIE is set to 1 */ + TFA9872_BF_DCTRIPT= 0x7584, /*!< Track Adaptive boost trip levels, effective only when boost_intelligent is set to 1 */ + TFA9872_BF_MTPK = 0xa107, /*!< MTP KEY2 register */ + TFA9872_BF_KEY1LOCKED= 0xa200, /*!< Indicates KEY1 is locked */ + TFA9872_BF_KEY2LOCKED= 0xa210, /*!< Indicates KEY2 is locked */ + TFA9872_BF_CMTPI = 0xa350, /*!< Start copying all the data from mtp to I2C mtp registers */ + TFA9872_BF_CIMTP = 0xa360, /*!< Start copying data from I2C mtp registers to mtp */ + TFA9872_BF_MTPRDMSB= 0xa50f, /*!< MSB word of MTP manual read data */ + TFA9872_BF_MTPRDLSB= 0xa60f, /*!< LSB word of MTP manual read data */ + TFA9872_BF_EXTTS = 0xb108, /*!< External temperature (C) */ + TFA9872_BF_TROS = 0xb190, /*!< Select temp Speaker calibration */ + TFA9872_BF_SWPROFIL= 0xee0f, /*!< Software profile data */ + TFA9872_BF_SWVSTEP= 0xef0f, /*!< Software vstep information */ + TFA9872_BF_MTPOTC= 0xf000, /*!< Calibration schedule */ + TFA9872_BF_MTPEX = 0xf010, /*!< Calibration Ron executed */ + TFA9872_BF_DCMCCAPI= 0xf020, /*!< Calibration current limit DCDC */ + TFA9872_BF_DCMCCSB= 0xf030, /*!< Sign bit for delta calibration current limit DCDC */ + TFA9872_BF_USERDEF= 0xf042, /*!< Calibration delta current limit DCDC */ + TFA9872_BF_CUSTINFO= 0xf078, /*!< Reserved space for allowing customer to store speaker information */ + TFA9872_BF_R25C = 0xf50f, /*!< Ron resistance of speaker coil */ +} nxpTfa9872BfEnumList_t; +#define TFA9872_NAMETABLE static tfaBfName_t Tfa9872DatasheetNames[]= {\ + { 0x0, "PWDN"}, /* Powerdown selection , */\ + { 0x10, "I2CR"}, /* I2C Reset - Auto clear , */\ + { 0x30, "AMPE"}, /* Activate Amplifier , */\ + { 0x40, "DCA"}, /* Activate DC-to-DC converter , */\ + { 0x71, "INTP"}, /* Interrupt config , */\ + { 0xb0, "BYPOCP"}, /* Bypass OCP , */\ + { 0xc0, "TSTOCP"}, /* OCP testing control , */\ + { 0x120, "MANSCONF"}, /* I2C configured , */\ + { 0x140, "MANAOOSC"}, /* Internal osc off at PWDN , */\ + { 0x1d0, "MUTETO"}, /* Time out SB mute sequence , */\ + { 0x1e0, "RCVNS"}, /* Noise shaper selection , */\ + { 0x203, "AUDFS"}, /* Sample rate (fs) , */\ + { 0x240, "INPLEV"}, /* TDM output attenuation , */\ + { 0x255, "FRACTDEL"}, /* V/I Fractional delay , */\ + { 0x2b0, "BYPHVBF"}, /* Bypass HVBAT filter , */\ + { 0x30f, "REV"}, /* Revision info , */\ + { 0x401, "REFCKEXT"}, /* PLL external ref clock , */\ + { 0x420, "REFCKSEL"}, /* PLL internal ref clock , */\ + { 0x510, "SSE"}, /* Enable speaker path , */\ + { 0x530, "VSE"}, /* Voltage sense , */\ + { 0x550, "CSE"}, /* Current sense , */\ + { 0x560, "SSPDME"}, /* Sub-system PDM , */\ + { 0x580, "PGAE"}, /* Enable PGA chop clock , */\ + { 0x590, "SSTDME"}, /* Sub-system TDM , */\ + { 0x5a0, "SSPBSTE"}, /* Sub-system boost , */\ + { 0x5b0, "SSADCE"}, /* Sub-system ADC , */\ + { 0x5c0, "SSFAIME"}, /* Sub-system FAIM , */\ + { 0xd18, "STGAIN"}, /* Side tone gain , */\ + { 0xda0, "STSMUTE"}, /* Side tone soft mute , */\ + { 0xdb0, "ST1C"}, /* side tone one s complement , */\ + { 0x1000, "VDDS"}, /* POR , */\ + { 0x1010, "PLLS"}, /* PLL lock , */\ + { 0x1020, "OTDS"}, /* OTP alarm , */\ + { 0x1030, "OVDS"}, /* OVP alarm , */\ + { 0x1040, "UVDS"}, /* UVP alarm , */\ + { 0x1050, "CLKS"}, /* Clocks stable , */\ + { 0x1060, "MTPB"}, /* MTP busy , */\ + { 0x1070, "NOCLK"}, /* Lost clock , */\ + { 0x10a0, "SWS"}, /* Amplifier engage , */\ + { 0x10c0, "AMPS"}, /* Amplifier enable , */\ + { 0x10d0, "AREFS"}, /* References enable , */\ + { 0x10e0, "ADCCR"}, /* Control ADC , */\ + { 0x1100, "DCIL"}, /* DCDC current limiting , */\ + { 0x1110, "DCDCA"}, /* DCDC active , */\ + { 0x1120, "DCOCPOK"}, /* DCDC OCP nmos , */\ + { 0x1140, "DCHVBAT"}, /* DCDC level 1x , */\ + { 0x1150, "DCH114"}, /* DCDC level 1.14x , */\ + { 0x1160, "DCH107"}, /* DCDC level 1.07x , */\ + { 0x1170, "STMUTEB"}, /* side tone (un)mute busy , */\ + { 0x1180, "STMUTE"}, /* side tone mute state , */\ + { 0x1190, "TDMLUTER"}, /* TDM LUT error , */\ + { 0x11a2, "TDMSTAT"}, /* TDM status bits , */\ + { 0x11d0, "TDMERR"}, /* TDM error , */\ + { 0x1300, "OCPOAP"}, /* OCPOK pmos A , */\ + { 0x1310, "OCPOAN"}, /* OCPOK nmos A , */\ + { 0x1320, "OCPOBP"}, /* OCPOK pmos B , */\ + { 0x1330, "OCPOBN"}, /* OCPOK nmos B , */\ + { 0x1340, "CLIPAH"}, /* Clipping A to Vddp , */\ + { 0x1350, "CLIPAL"}, /* Clipping A to gnd , */\ + { 0x1360, "CLIPBH"}, /* Clipping B to Vddp , */\ + { 0x1370, "CLIPBL"}, /* Clipping B to gnd , */\ + { 0x1380, "OCDS"}, /* OCP amplifier , */\ + { 0x1390, "CLIPS"}, /* Amplifier clipping , */\ + { 0x13a0, "OCPOKMC"}, /* OCPOK MICVDD , */\ + { 0x13b0, "MANALARM"}, /* Alarm state , */\ + { 0x13c0, "MANWAIT1"}, /* Wait HW I2C settings , */\ + { 0x13e0, "MANMUTE"}, /* Audio mute sequence , */\ + { 0x13f0, "MANOPER"}, /* Operating state , */\ + { 0x1420, "CLKOOR"}, /* External clock status , */\ + { 0x1433, "MANSTATE"}, /* Device manager status , */\ + { 0x1471, "DCMODE"}, /* DCDC mode status bits , */\ + { 0x1509, "BATS"}, /* Battery voltage (V) , */\ + { 0x1608, "TEMPS"}, /* IC Temperature (C) , */\ + { 0x1709, "VDDPS"}, /* IC VDDP voltage ( 1023*VDDP/9.5 V) , */\ + { 0x2040, "TDME"}, /* Enable interface , */\ + { 0x2050, "TDMMODE"}, /* Slave/master , */\ + { 0x2060, "TDMCLINV"}, /* Reception data to BCK clock , */\ + { 0x2073, "TDMFSLN"}, /* FS length (master mode only) , */\ + { 0x20b0, "TDMFSPOL"}, /* FS polarity , */\ + { 0x20c3, "TDMNBCK"}, /* N-BCK's in FS , */\ + { 0x2103, "TDMSLOTS"}, /* N-slots in Frame , */\ + { 0x2144, "TDMSLLN"}, /* N-bits in slot , */\ + { 0x2194, "TDMBRMG"}, /* N-bits remaining , */\ + { 0x21e0, "TDMDEL"}, /* data delay to FS , */\ + { 0x21f0, "TDMADJ"}, /* data adjustment , */\ + { 0x2201, "TDMOOMP"}, /* Received audio compression , */\ + { 0x2224, "TDMSSIZE"}, /* Sample size per slot , */\ + { 0x2271, "TDMTXDFO"}, /* Format unused bits , */\ + { 0x2291, "TDMTXUS0"}, /* Format unused slots DATAO , */\ + { 0x2300, "TDMSPKE"}, /* Control audio tdm channel in 0 (spkr + dcdc) , */\ + { 0x2310, "TDMDCE"}, /* Control audio tdm channel in 1 (dcdc) , */\ + { 0x2330, "TDMCSE"}, /* current sense vbat temperature and vddp feedback , */\ + { 0x2340, "TDMVSE"}, /* Voltage sense vbat temperature and vddp feedback , */\ + { 0x2603, "TDMSPKS"}, /* tdm slot for sink 0 (speaker + dcdc) , */\ + { 0x2643, "TDMDCS"}, /* tdm slot for sink 1 (dcdc) , */\ + { 0x26c3, "TDMCSS"}, /* Slot Position of current sense vbat temperature and vddp feedback, */\ + { 0x2703, "TDMVSS"}, /* Slot Position of Voltage sense vbat temperature and vddp feedback, */\ + { 0x3111, "PDMSTSEL"}, /* Side tone input , */\ + { 0x4000, "ISTVDDS"}, /* Status POR , */\ + { 0x4010, "ISTPLLS"}, /* Status PLL lock , */\ + { 0x4020, "ISTOTDS"}, /* Status OTP alarm , */\ + { 0x4030, "ISTOVDS"}, /* Status OVP alarm , */\ + { 0x4040, "ISTUVDS"}, /* Status UVP alarm , */\ + { 0x4050, "ISTCLKS"}, /* Status clocks stable , */\ + { 0x4060, "ISTMTPB"}, /* Status MTP busy , */\ + { 0x4070, "ISTNOCLK"}, /* Status lost clock , */\ + { 0x40a0, "ISTSWS"}, /* Status amplifier engage , */\ + { 0x40c0, "ISTAMPS"}, /* Status amplifier enable , */\ + { 0x40d0, "ISTAREFS"}, /* Status Ref enable , */\ + { 0x40e0, "ISTADCCR"}, /* Status Control ADC , */\ + { 0x4100, "ISTBSTCU"}, /* Status DCDC current limiting , */\ + { 0x4110, "ISTBSTHI"}, /* Status DCDC active , */\ + { 0x4120, "ISTBSTOC"}, /* Status DCDC OCP , */\ + { 0x4130, "ISTBSTPKCUR"}, /* Status bst peakcur , */\ + { 0x4140, "ISTBSTVC"}, /* Status DCDC level 1x , */\ + { 0x4150, "ISTBST86"}, /* Status DCDC level 1.14x , */\ + { 0x4160, "ISTBST93"}, /* Status DCDC level 1.07x , */\ + { 0x4190, "ISTOCPR"}, /* Status ocp alarm , */\ + { 0x41a0, "ISTMWSRC"}, /* Status Waits HW I2C settings , */\ + { 0x41c0, "ISTMWSMU"}, /* Status Audio mute sequence , */\ + { 0x41f0, "ISTCLKOOR"}, /* Status flag_clk_out_of_range , */\ + { 0x4200, "ISTTDMER"}, /* Status tdm error , */\ + { 0x4220, "ISTCLPR"}, /* Status clip , */\ + { 0x4240, "ISTLP0"}, /* Status low power mode0 , */\ + { 0x4250, "ISTLP1"}, /* Status low power mode1 , */\ + { 0x4260, "ISTLA"}, /* Status low noise detection , */\ + { 0x4270, "ISTVDDPH"}, /* Status VDDP greater than VBAT , */\ + { 0x4400, "ICLVDDS"}, /* Clear POR , */\ + { 0x4410, "ICLPLLS"}, /* Clear PLL lock , */\ + { 0x4420, "ICLOTDS"}, /* Clear OTP alarm , */\ + { 0x4430, "ICLOVDS"}, /* Clear OVP alarm , */\ + { 0x4440, "ICLUVDS"}, /* Clear UVP alarm , */\ + { 0x4450, "ICLCLKS"}, /* Clear clocks stable , */\ + { 0x4460, "ICLMTPB"}, /* Clear mtp busy , */\ + { 0x4470, "ICLNOCLK"}, /* Clear lost clk , */\ + { 0x44a0, "ICLSWS"}, /* Clear amplifier engage , */\ + { 0x44c0, "ICLAMPS"}, /* Clear enbl amp , */\ + { 0x44d0, "ICLAREFS"}, /* Clear ref enable , */\ + { 0x44e0, "ICLADCCR"}, /* Clear control ADC , */\ + { 0x4500, "ICLBSTCU"}, /* Clear DCDC current limiting , */\ + { 0x4510, "ICLBSTHI"}, /* Clear DCDC active , */\ + { 0x4520, "ICLBSTOC"}, /* Clear DCDC OCP , */\ + { 0x4530, "ICLBSTPC"}, /* Clear bst peakcur , */\ + { 0x4540, "ICLBSTVC"}, /* Clear DCDC level 1x , */\ + { 0x4550, "ICLBST86"}, /* Clear DCDC level 1.14x , */\ + { 0x4560, "ICLBST93"}, /* Clear DCDC level 1.07x , */\ + { 0x4590, "ICLOCPR"}, /* Clear ocp alarm , */\ + { 0x45a0, "ICLMWSRC"}, /* Clear wait HW I2C settings , */\ + { 0x45c0, "ICLMWSMU"}, /* Clear audio mute sequence , */\ + { 0x45f0, "ICLCLKOOR"}, /* Clear flag_clk_out_of_range , */\ + { 0x4600, "ICLTDMER"}, /* Clear tdm error , */\ + { 0x4620, "ICLCLPR"}, /* Clear clip , */\ + { 0x4640, "ICLLP0"}, /* Clear low power mode0 , */\ + { 0x4650, "ICLLP1"}, /* Clear low power mode1 , */\ + { 0x4660, "ICLLA"}, /* Clear low noise detection , */\ + { 0x4670, "ICLVDDPH"}, /* Clear VDDP greater then VBAT , */\ + { 0x4800, "IEVDDS"}, /* Enable por , */\ + { 0x4810, "IEPLLS"}, /* Enable pll lock , */\ + { 0x4820, "IEOTDS"}, /* Enable OTP alarm , */\ + { 0x4830, "IEOVDS"}, /* Enable OVP alarm , */\ + { 0x4840, "IEUVDS"}, /* Enable UVP alarm , */\ + { 0x4850, "IECLKS"}, /* Enable clocks stable , */\ + { 0x4860, "IEMTPB"}, /* Enable mtp busy , */\ + { 0x4870, "IENOCLK"}, /* Enable lost clk , */\ + { 0x48a0, "IESWS"}, /* Enable amplifier engage , */\ + { 0x48c0, "IEAMPS"}, /* Enable enbl amp , */\ + { 0x48d0, "IEAREFS"}, /* Enable ref enable , */\ + { 0x48e0, "IEADCCR"}, /* Enable Control ADC , */\ + { 0x4900, "IEBSTCU"}, /* Enable DCDC current limiting , */\ + { 0x4910, "IEBSTHI"}, /* Enable DCDC active , */\ + { 0x4920, "IEBSTOC"}, /* Enable DCDC OCP , */\ + { 0x4930, "IEBSTPC"}, /* Enable bst peakcur , */\ + { 0x4940, "IEBSTVC"}, /* Enable DCDC level 1x , */\ + { 0x4950, "IEBST86"}, /* Enable DCDC level 1.14x , */\ + { 0x4960, "IEBST93"}, /* Enable DCDC level 1.07x , */\ + { 0x4990, "IEOCPR"}, /* Enable ocp alarm , */\ + { 0x49a0, "IEMWSRC"}, /* Enable waits HW I2C settings , */\ + { 0x49c0, "IEMWSMU"}, /* Enable man Audio mute sequence , */\ + { 0x49f0, "IECLKOOR"}, /* Enable flag_clk_out_of_range , */\ + { 0x4a00, "IETDMER"}, /* Enable tdm error , */\ + { 0x4a20, "IECLPR"}, /* Enable clip , */\ + { 0x4a40, "IELP0"}, /* Enable low power mode0 , */\ + { 0x4a50, "IELP1"}, /* Enable low power mode1 , */\ + { 0x4a60, "IELA"}, /* Enable low noise detection , */\ + { 0x4a70, "IEVDDPH"}, /* Enable VDDP greater tehn VBAT , */\ + { 0x4c00, "IPOVDDS"}, /* Polarity por , */\ + { 0x4c10, "IPOPLLS"}, /* Polarity pll lock , */\ + { 0x4c20, "IPOOTDS"}, /* Polarity OTP alarm , */\ + { 0x4c30, "IPOOVDS"}, /* Polarity OVP alarm , */\ + { 0x4c40, "IPOUVDS"}, /* Polarity UVP alarm , */\ + { 0x4c50, "IPOCLKS"}, /* Polarity clocks stable , */\ + { 0x4c60, "IPOMTPB"}, /* Polarity mtp busy , */\ + { 0x4c70, "IPONOCLK"}, /* Polarity lost clk , */\ + { 0x4ca0, "IPOSWS"}, /* Polarity amplifier engage , */\ + { 0x4cc0, "IPOAMPS"}, /* Polarity enbl amp , */\ + { 0x4cd0, "IPOAREFS"}, /* Polarity ref enable , */\ + { 0x4ce0, "IPOADCCR"}, /* Polarity Control ADC , */\ + { 0x4d00, "IPOBSTCU"}, /* Polarity DCDC current limiting , */\ + { 0x4d10, "IPOBSTHI"}, /* Polarity DCDC active , */\ + { 0x4d20, "IPOBSTOC"}, /* Polarity DCDC OCP , */\ + { 0x4d30, "IPOBSTPC"}, /* Polarity bst peakcur , */\ + { 0x4d40, "IPOBSTVC"}, /* Polarity DCDC level 1x , */\ + { 0x4d50, "IPOBST86"}, /* Polarity DCDC level 1.14x , */\ + { 0x4d60, "IPOBST93"}, /* Polarity DCDC level 1.07x , */\ + { 0x4d90, "IPOOCPR"}, /* Polarity ocp alarm , */\ + { 0x4da0, "IPOMWSRC"}, /* Polarity waits HW I2C settings , */\ + { 0x4dc0, "IPOMWSMU"}, /* Polarity man audio mute sequence , */\ + { 0x4df0, "IPCLKOOR"}, /* Polarity flag_clk_out_of_range , */\ + { 0x4e00, "IPOTDMER"}, /* Polarity tdm error , */\ + { 0x4e20, "IPOCLPR"}, /* Polarity clip right , */\ + { 0x4e40, "IPOLP0"}, /* Polarity low power mode0 , */\ + { 0x4e50, "IPOLP1"}, /* Polarity low power mode1 , */\ + { 0x4e60, "IPOLA"}, /* Polarity low noise mode , */\ + { 0x4e70, "IPOVDDPH"}, /* Polarity VDDP greater than VBAT , */\ + { 0x5001, "BSSCR"}, /* Battery Safeguard attack time , */\ + { 0x5023, "BSST"}, /* Battery Safeguard threshold voltage level , */\ + { 0x5061, "BSSRL"}, /* Battery Safeguard maximum reduction , */\ + { 0x50e0, "BSSR"}, /* Battery voltage read out , */\ + { 0x50f0, "BSSBY"}, /* Bypass battery safeguard , */\ + { 0x5100, "BSSS"}, /* Vbat prot steepness , */\ + { 0x5110, "INTSMUTE"}, /* Soft mute HW , */\ + { 0x5150, "HPFBYP"}, /* Bypass HPF , */\ + { 0x5170, "DPSA"}, /* Enable DPSA , */\ + { 0x5222, "CLIPCTRL"}, /* Clip control setting , */\ + { 0x5257, "AMPGAIN"}, /* Amplifier gain , */\ + { 0x52d0, "SLOPEE"}, /* Enables slope control , */\ + { 0x52e0, "SLOPESET"}, /* Slope speed setting (bin. coded) , */\ + { 0x6081, "PGAGAIN"}, /* PGA gain selection , */\ + { 0x60b0, "PGALPE"}, /* Lowpass enable , */\ + { 0x6110, "LPM0BYP"}, /* bypass low power idle mode , */\ + { 0x6123, "TDMDCG"}, /* Second channel gain in case of stereo using a single coil. (Total gain depending on INPLEV). (In case of mono OR stereo using 2 separate DCDC channel 1 should be disabled using TDMDCE), */\ + { 0x6163, "TDMSPKG"}, /* Total gain depending on INPLEV setting (channel 0), */\ + { 0x61b0, "STIDLEEN"}, /* enable idle feature for channel 1 , */\ + { 0x62e1, "LNMODE"}, /* ctrl select mode , */\ + { 0x64e1, "LPM1MODE"}, /* low power mode control , */\ + { 0x65c0, "LPM1DIS"}, /* low power mode1 detector control , */\ + { 0x6801, "TDMSRCMAP"}, /* tdm source mapping , */\ + { 0x6821, "TDMSRCAS"}, /* Sensed value A , */\ + { 0x6841, "TDMSRCBS"}, /* Sensed value B , */\ + { 0x6881, "ANCSEL"}, /* anc input , */\ + { 0x68a0, "ANC1C"}, /* ANC one s complement , */\ + { 0x6901, "SAMMODE"}, /* sam enable , */\ + { 0x6920, "SAMSEL"}, /* sam source , */\ + { 0x6931, "PDMOSELH"}, /* pdm out value when pdm_clk is higth , */\ + { 0x6951, "PDMOSELL"}, /* pdm out value when pdm_clk is low , */\ + { 0x6970, "SAMOSEL"}, /* ram output on mode sam and audio , */\ + { 0x6e00, "LP0"}, /* low power mode 0 detection , */\ + { 0x6e10, "LP1"}, /* low power mode 1 detection , */\ + { 0x6e20, "LA"}, /* low amplitude detection , */\ + { 0x6e30, "VDDPH"}, /* vddp greater than vbat , */\ + { 0x6f02, "DELCURCOMP"}, /* delay to allign compensation signal with current sense signal, */\ + { 0x6f40, "SIGCURCOMP"}, /* polarity of compensation for current sense , */\ + { 0x6f50, "ENCURCOMP"}, /* enable current sense compensation , */\ + { 0x6f60, "SELCLPPWM"}, /* Select pwm clip flag , */\ + { 0x6f72, "LVLCLPPWM"}, /* set the amount of pwm pulse that may be skipped before clip-flag is triggered, */\ + { 0x7002, "DCVOS"}, /* Second boost voltage level , */\ + { 0x7033, "DCMCC"}, /* Max coil current , */\ + { 0x7071, "DCCV"}, /* Slope compensation current, represents LxF (inductance x frequency) value , */\ + { 0x7090, "DCIE"}, /* Adaptive boost mode , */\ + { 0x70a0, "DCSR"}, /* Soft ramp up/down , */\ + { 0x70e0, "DCDIS"}, /* DCDC on/off , */\ + { 0x70f0, "DCPWM"}, /* DCDC PWM only mode , */\ + { 0x7402, "DCVOF"}, /* 1st boost voltage level , */\ + { 0x7430, "DCTRACK"}, /* Boost algorithm selection, effective only when boost_intelligent is set to 1, */\ + { 0x7444, "DCTRIP"}, /* 1st Adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7494, "DCHOLD"}, /* Hold time for DCDC booster, effective only when boost_intelligent is set to 1, */\ + { 0x7534, "DCTRIP2"}, /* 2nd Adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7584, "DCTRIPT"}, /* Track Adaptive boost trip levels, effective only when boost_intelligent is set to 1, */\ + { 0xa107, "MTPK"}, /* MTP KEY2 register , */\ + { 0xa200, "KEY1LOCKED"}, /* Indicates KEY1 is locked , */\ + { 0xa210, "KEY2LOCKED"}, /* Indicates KEY2 is locked , */\ + { 0xa350, "CMTPI"}, /* Start copying all the data from mtp to I2C mtp registers, */\ + { 0xa360, "CIMTP"}, /* Start copying data from I2C mtp registers to mtp , */\ + { 0xa50f, "MTPRDMSB"}, /* MSB word of MTP manual read data , */\ + { 0xa60f, "MTPRDLSB"}, /* LSB word of MTP manual read data , */\ + { 0xb108, "EXTTS"}, /* External temperature (C) , */\ + { 0xb190, "TROS"}, /* Select temp Speaker calibration , */\ + { 0xee0f, "SWPROFIL"}, /* Software profile data , */\ + { 0xef0f, "SWVSTEP"}, /* Software vstep information , */\ + { 0xf000, "MTPOTC"}, /* Calibration schedule , */\ + { 0xf010, "MTPEX"}, /* Calibration Ron executed , */\ + { 0xf020, "DCMCCAPI"}, /* Calibration current limit DCDC , */\ + { 0xf030, "DCMCCSB"}, /* Sign bit for delta calibration current limit DCDC , */\ + { 0xf042, "USERDEF"}, /* Calibration delta current limit DCDC , */\ + { 0xf078, "CUSTINFO"}, /* Reserved space for allowing customer to store speaker information, */\ + { 0xf50f, "R25C"}, /* Ron resistance of speaker coil , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA9872_BITNAMETABLE static tfaBfName_t Tfa9872BitNames[]= {\ + { 0x0, "powerdown"}, /* Powerdown selection , */\ + { 0x10, "reset"}, /* I2C Reset - Auto clear , */\ + { 0x30, "enbl_amplifier"}, /* Activate Amplifier , */\ + { 0x40, "enbl_boost"}, /* Activate DC-to-DC converter , */\ + { 0x71, "int_pad_io"}, /* Interrupt config , */\ + { 0xb0, "bypass_ocp"}, /* Bypass OCP , */\ + { 0xc0, "test_ocp"}, /* OCP testing control , */\ + { 0x120, "src_set_configured"}, /* I2C configured , */\ + { 0x140, "enbl_osc1m_auto_off"}, /* Internal osc off at PWDN , */\ + { 0x1d0, "disable_mute_time_out"}, /* Time out SB mute sequence , */\ + { 0x203, "audio_fs"}, /* Sample rate (fs) , */\ + { 0x240, "input_level"}, /* TDM output attenuation , */\ + { 0x255, "cs_frac_delay"}, /* V/I Fractional delay , */\ + { 0x2b0, "bypass_hvbat_filter"}, /* Bypass HVBAT filter , */\ + { 0x2d0, "sel_hysteresis"}, /* Select hysteresis for clock range detector , */\ + { 0x30f, "device_rev"}, /* Revision info , */\ + { 0x401, "pll_clkin_sel"}, /* PLL external ref clock , */\ + { 0x420, "pll_clkin_sel_osc"}, /* PLL internal ref clock , */\ + { 0x510, "enbl_spkr_ss"}, /* Enable speaker path , */\ + { 0x530, "enbl_volsense"}, /* Voltage sense , */\ + { 0x550, "enbl_cursense"}, /* Current sense , */\ + { 0x560, "enbl_pdm_ss"}, /* Sub-system PDM , */\ + { 0x580, "enbl_pga_chop"}, /* Enable PGA chop clock , */\ + { 0x590, "enbl_tdm_ss"}, /* Sub-system TDM , */\ + { 0x5a0, "enbl_bst_ss"}, /* Sub-system boost , */\ + { 0x5b0, "enbl_adc_ss"}, /* Sub-system ADC , */\ + { 0x5c0, "enbl_faim_ss"}, /* Sub-system FAIM , */\ + { 0xd18, "side_tone_gain"}, /* Side tone gain , */\ + { 0xda0, "mute_side_tone"}, /* Side tone soft mute , */\ + { 0xdb0, "side_tone_1scomplement"}, /* side tone one s complement , */\ + { 0xe07, "ctrl_digtoana"}, /* Spare control from digital to analog , */\ + { 0xf0f, "hidden_code"}, /* 5A6Bh, 23147d to access registers (default for engineering), */\ + { 0x1000, "flag_por"}, /* POR , */\ + { 0x1010, "flag_pll_lock"}, /* PLL lock , */\ + { 0x1020, "flag_otpok"}, /* OTP alarm , */\ + { 0x1030, "flag_ovpok"}, /* OVP alarm , */\ + { 0x1040, "flag_uvpok"}, /* UVP alarm , */\ + { 0x1050, "flag_clocks_stable"}, /* Clocks stable , */\ + { 0x1060, "flag_mtp_busy"}, /* MTP busy , */\ + { 0x1070, "flag_lost_clk"}, /* Lost clock , */\ + { 0x10a0, "flag_engage"}, /* Amplifier engage , */\ + { 0x10c0, "flag_enbl_amp"}, /* Amplifier enable , */\ + { 0x10d0, "flag_enbl_ref"}, /* References enable , */\ + { 0x10e0, "flag_adc10_ready"}, /* Control ADC , */\ + { 0x1100, "flag_bst_bstcur"}, /* DCDC current limiting , */\ + { 0x1110, "flag_bst_hiz"}, /* DCDC active , */\ + { 0x1120, "flag_bst_ocpok"}, /* DCDC OCP nmos , */\ + { 0x1130, "flag_bst_peakcur"}, /* Indicates current is max in DC-to-DC converter , */\ + { 0x1140, "flag_bst_voutcomp"}, /* DCDC level 1x , */\ + { 0x1150, "flag_bst_voutcomp86"}, /* DCDC level 1.14x , */\ + { 0x1160, "flag_bst_voutcomp93"}, /* DCDC level 1.07x , */\ + { 0x1170, "flag_soft_mute_busy"}, /* side tone (un)mute busy , */\ + { 0x1180, "flag_soft_mute_state"}, /* side tone mute state , */\ + { 0x1190, "flag_tdm_lut_error"}, /* TDM LUT error , */\ + { 0x11a2, "flag_tdm_status"}, /* TDM status bits , */\ + { 0x11d0, "flag_tdm_error"}, /* TDM error , */\ + { 0x1300, "flag_ocpokap"}, /* OCPOK pmos A , */\ + { 0x1310, "flag_ocpokan"}, /* OCPOK nmos A , */\ + { 0x1320, "flag_ocpokbp"}, /* OCPOK pmos B , */\ + { 0x1330, "flag_ocpokbn"}, /* OCPOK nmos B , */\ + { 0x1340, "flag_clipa_high"}, /* Clipping A to Vddp , */\ + { 0x1350, "flag_clipa_low"}, /* Clipping A to gnd , */\ + { 0x1360, "flag_clipb_high"}, /* Clipping B to Vddp , */\ + { 0x1370, "flag_clipb_low"}, /* Clipping B to gnd , */\ + { 0x1380, "flag_ocp_alarm"}, /* OCP amplifier , */\ + { 0x1390, "flag_clip"}, /* Amplifier clipping , */\ + { 0x13b0, "flag_man_alarm_state"}, /* Alarm state , */\ + { 0x13c0, "flag_man_wait_src_settings"}, /* Wait HW I2C settings , */\ + { 0x13e0, "flag_man_start_mute_audio"}, /* Audio mute sequence , */\ + { 0x13f0, "flag_man_operating_state"}, /* Operating state , */\ + { 0x1420, "flag_clk_out_of_range"}, /* External clock status , */\ + { 0x1433, "man_state"}, /* Device manager status , */\ + { 0x1471, "status_bst_mode"}, /* DCDC mode status bits , */\ + { 0x1509, "bat_adc"}, /* Battery voltage (V) , */\ + { 0x1608, "temp_adc"}, /* IC Temperature (C) , */\ + { 0x1709, "vddp_adc"}, /* IC VDDP voltage ( 1023*VDDP/9.5 V) , */\ + { 0x2040, "tdm_enable"}, /* Enable interface , */\ + { 0x2050, "tdm_mode"}, /* Slave/master , */\ + { 0x2060, "tdm_clk_inversion"}, /* Reception data to BCK clock , */\ + { 0x2073, "tdm_fs_ws_length"}, /* FS length (master mode only) , */\ + { 0x20b0, "tdm_fs_ws_polarity"}, /* FS polarity , */\ + { 0x20c3, "tdm_nbck"}, /* N-BCK's in FS , */\ + { 0x2103, "tdm_nb_of_slots"}, /* N-slots in Frame , */\ + { 0x2144, "tdm_slot_length"}, /* N-bits in slot , */\ + { 0x2194, "tdm_bits_remaining"}, /* N-bits remaining , */\ + { 0x21e0, "tdm_data_delay"}, /* data delay to FS , */\ + { 0x21f0, "tdm_data_adjustment"}, /* data adjustment , */\ + { 0x2201, "tdm_audio_sample_compression"}, /* Received audio compression , */\ + { 0x2224, "tdm_sample_size"}, /* Sample size per slot , */\ + { 0x2271, "tdm_txdata_format"}, /* Format unused bits , */\ + { 0x2291, "tdm_txdata_format_unused_slot_sd0"}, /* Format unused slots DATAO , */\ + { 0x2300, "tdm_sink0_enable"}, /* Control audio tdm channel in 0 (spkr + dcdc) , */\ + { 0x2310, "tdm_sink1_enable"}, /* Control audio tdm channel in 1 (dcdc) , */\ + { 0x2330, "tdm_source0_enable"}, /* current sense vbat temperature and vddp feedback , */\ + { 0x2340, "tdm_source1_enable"}, /* Voltage sense vbat temperature and vddp feedback , */\ + { 0x2603, "tdm_sink0_slot"}, /* tdm slot for sink 0 (speaker + dcdc) , */\ + { 0x2643, "tdm_sink1_slot"}, /* tdm slot for sink 1 (dcdc) , */\ + { 0x26c3, "tdm_source0_slot"}, /* Slot Position of current sense vbat temperature and vddp feedback, */\ + { 0x2703, "tdm_source1_slot"}, /* Slot Position of Voltage sense vbat temperature and vddp feedback, */\ + { 0x3111, "pdm_side_tone_sel"}, /* Side tone input , */\ + { 0x3201, "pdm_nbck"}, /* PDM BCK/Fs ratio , */\ + { 0x4000, "int_out_flag_por"}, /* Status POR , */\ + { 0x4010, "int_out_flag_pll_lock"}, /* Status PLL lock , */\ + { 0x4020, "int_out_flag_otpok"}, /* Status OTP alarm , */\ + { 0x4030, "int_out_flag_ovpok"}, /* Status OVP alarm , */\ + { 0x4040, "int_out_flag_uvpok"}, /* Status UVP alarm , */\ + { 0x4050, "int_out_flag_clocks_stable"}, /* Status clocks stable , */\ + { 0x4060, "int_out_flag_mtp_busy"}, /* Status MTP busy , */\ + { 0x4070, "int_out_flag_lost_clk"}, /* Status lost clock , */\ + { 0x40a0, "int_out_flag_engage"}, /* Status amplifier engage , */\ + { 0x40c0, "int_out_flag_enbl_amp"}, /* Status amplifier enable , */\ + { 0x40d0, "int_out_flag_enbl_ref"}, /* Status Ref enable , */\ + { 0x40e0, "int_out_flag_adc10_ready"}, /* Status Control ADC , */\ + { 0x4100, "int_out_flag_bst_bstcur"}, /* Status DCDC current limiting , */\ + { 0x4110, "int_out_flag_bst_hiz"}, /* Status DCDC active , */\ + { 0x4120, "int_out_flag_bst_ocpok"}, /* Status DCDC OCP , */\ + { 0x4130, "int_out_flag_bst_peakcur"}, /* Status bst peakcur , */\ + { 0x4140, "int_out_flag_bst_voutcomp"}, /* Status DCDC level 1x , */\ + { 0x4150, "int_out_flag_bst_voutcomp86"}, /* Status DCDC level 1.14x , */\ + { 0x4160, "int_out_flag_bst_voutcomp93"}, /* Status DCDC level 1.07x , */\ + { 0x4190, "int_out_flag_ocp_alarm"}, /* Status ocp alarm , */\ + { 0x41a0, "int_out_flag_man_wait_src_settings"}, /* Status Waits HW I2C settings , */\ + { 0x41c0, "int_out_flag_man_start_mute_audio"}, /* Status Audio mute sequence , */\ + { 0x41f0, "int_out_flag_clk_out_of_range"}, /* Status flag_clk_out_of_range , */\ + { 0x4200, "int_out_flag_tdm_error"}, /* Status tdm error , */\ + { 0x4220, "int_out_flag_clip"}, /* Status clip , */\ + { 0x4240, "int_out_flag_lp_detect_mode0"}, /* Status low power mode0 , */\ + { 0x4250, "int_out_flag_lp_detect_mode1"}, /* Status low power mode1 , */\ + { 0x4260, "int_out_flag_low_amplitude"}, /* Status low noise detection , */\ + { 0x4270, "int_out_flag_vddp_gt_vbat"}, /* Status VDDP greater than VBAT , */\ + { 0x4400, "int_in_flag_por"}, /* Clear POR , */\ + { 0x4410, "int_in_flag_pll_lock"}, /* Clear PLL lock , */\ + { 0x4420, "int_in_flag_otpok"}, /* Clear OTP alarm , */\ + { 0x4430, "int_in_flag_ovpok"}, /* Clear OVP alarm , */\ + { 0x4440, "int_in_flag_uvpok"}, /* Clear UVP alarm , */\ + { 0x4450, "int_in_flag_clocks_stable"}, /* Clear clocks stable , */\ + { 0x4460, "int_in_flag_mtp_busy"}, /* Clear mtp busy , */\ + { 0x4470, "int_in_flag_lost_clk"}, /* Clear lost clk , */\ + { 0x44a0, "int_in_flag_engage"}, /* Clear amplifier engage , */\ + { 0x44c0, "int_in_flag_enbl_amp"}, /* Clear enbl amp , */\ + { 0x44d0, "int_in_flag_enbl_ref"}, /* Clear ref enable , */\ + { 0x44e0, "int_in_flag_adc10_ready"}, /* Clear control ADC , */\ + { 0x4500, "int_in_flag_bst_bstcur"}, /* Clear DCDC current limiting , */\ + { 0x4510, "int_in_flag_bst_hiz"}, /* Clear DCDC active , */\ + { 0x4520, "int_in_flag_bst_ocpok"}, /* Clear DCDC OCP , */\ + { 0x4530, "int_in_flag_bst_peakcur"}, /* Clear bst peakcur , */\ + { 0x4540, "int_in_flag_bst_voutcomp"}, /* Clear DCDC level 1x , */\ + { 0x4550, "int_in_flag_bst_voutcomp86"}, /* Clear DCDC level 1.14x , */\ + { 0x4560, "int_in_flag_bst_voutcomp93"}, /* Clear DCDC level 1.07x , */\ + { 0x4590, "int_in_flag_ocp_alarm"}, /* Clear ocp alarm , */\ + { 0x45a0, "int_in_flag_man_wait_src_settings"}, /* Clear wait HW I2C settings , */\ + { 0x45c0, "int_in_flag_man_start_mute_audio"}, /* Clear audio mute sequence , */\ + { 0x45f0, "int_in_flag_clk_out_of_range"}, /* Clear flag_clk_out_of_range , */\ + { 0x4600, "int_in_flag_tdm_error"}, /* Clear tdm error , */\ + { 0x4620, "int_in_flag_clip"}, /* Clear clip , */\ + { 0x4640, "int_in_flag_lp_detect_mode0"}, /* Clear low power mode0 , */\ + { 0x4650, "int_in_flag_lp_detect_mode1"}, /* Clear low power mode1 , */\ + { 0x4660, "int_in_flag_low_amplitude"}, /* Clear low noise detection , */\ + { 0x4670, "int_in_flag_vddp_gt_vbat"}, /* Clear VDDP greater then VBAT , */\ + { 0x4800, "int_enable_flag_por"}, /* Enable por , */\ + { 0x4810, "int_enable_flag_pll_lock"}, /* Enable pll lock , */\ + { 0x4820, "int_enable_flag_otpok"}, /* Enable OTP alarm , */\ + { 0x4830, "int_enable_flag_ovpok"}, /* Enable OVP alarm , */\ + { 0x4840, "int_enable_flag_uvpok"}, /* Enable UVP alarm , */\ + { 0x4850, "int_enable_flag_clocks_stable"}, /* Enable clocks stable , */\ + { 0x4860, "int_enable_flag_mtp_busy"}, /* Enable mtp busy , */\ + { 0x4870, "int_enable_flag_lost_clk"}, /* Enable lost clk , */\ + { 0x48a0, "int_enable_flag_engage"}, /* Enable amplifier engage , */\ + { 0x48c0, "int_enable_flag_enbl_amp"}, /* Enable enbl amp , */\ + { 0x48d0, "int_enable_flag_enbl_ref"}, /* Enable ref enable , */\ + { 0x48e0, "int_enable_flag_adc10_ready"}, /* Enable Control ADC , */\ + { 0x4900, "int_enable_flag_bst_bstcur"}, /* Enable DCDC current limiting , */\ + { 0x4910, "int_enable_flag_bst_hiz"}, /* Enable DCDC active , */\ + { 0x4920, "int_enable_flag_bst_ocpok"}, /* Enable DCDC OCP , */\ + { 0x4930, "int_enable_flag_bst_peakcur"}, /* Enable bst peakcur , */\ + { 0x4940, "int_enable_flag_bst_voutcomp"}, /* Enable DCDC level 1x , */\ + { 0x4950, "int_enable_flag_bst_voutcomp86"}, /* Enable DCDC level 1.14x , */\ + { 0x4960, "int_enable_flag_bst_voutcomp93"}, /* Enable DCDC level 1.07x , */\ + { 0x4990, "int_enable_flag_ocp_alarm"}, /* Enable ocp alarm , */\ + { 0x49a0, "int_enable_flag_man_wait_src_settings"}, /* Enable waits HW I2C settings , */\ + { 0x49c0, "int_enable_flag_man_start_mute_audio"}, /* Enable man Audio mute sequence , */\ + { 0x49f0, "int_enable_flag_clk_out_of_range"}, /* Enable flag_clk_out_of_range , */\ + { 0x4a00, "int_enable_flag_tdm_error"}, /* Enable tdm error , */\ + { 0x4a20, "int_enable_flag_clip"}, /* Enable clip , */\ + { 0x4a40, "int_enable_flag_lp_detect_mode0"}, /* Enable low power mode0 , */\ + { 0x4a50, "int_enable_flag_lp_detect_mode1"}, /* Enable low power mode1 , */\ + { 0x4a60, "int_enable_flag_low_amplitude"}, /* Enable low noise detection , */\ + { 0x4a70, "int_enable_flag_vddp_gt_vbat"}, /* Enable VDDP greater tehn VBAT , */\ + { 0x4c00, "int_polarity_flag_por"}, /* Polarity por , */\ + { 0x4c10, "int_polarity_flag_pll_lock"}, /* Polarity pll lock , */\ + { 0x4c20, "int_polarity_flag_otpok"}, /* Polarity OTP alarm , */\ + { 0x4c30, "int_polarity_flag_ovpok"}, /* Polarity OVP alarm , */\ + { 0x4c40, "int_polarity_flag_uvpok"}, /* Polarity UVP alarm , */\ + { 0x4c50, "int_polarity_flag_clocks_stable"}, /* Polarity clocks stable , */\ + { 0x4c60, "int_polarity_flag_mtp_busy"}, /* Polarity mtp busy , */\ + { 0x4c70, "int_polarity_flag_lost_clk"}, /* Polarity lost clk , */\ + { 0x4ca0, "int_polarity_flag_engage"}, /* Polarity amplifier engage , */\ + { 0x4cc0, "int_polarity_flag_enbl_amp"}, /* Polarity enbl amp , */\ + { 0x4cd0, "int_polarity_flag_enbl_ref"}, /* Polarity ref enable , */\ + { 0x4ce0, "int_polarity_flag_adc10_ready"}, /* Polarity Control ADC , */\ + { 0x4d00, "int_polarity_flag_bst_bstcur"}, /* Polarity DCDC current limiting , */\ + { 0x4d10, "int_polarity_flag_bst_hiz"}, /* Polarity DCDC active , */\ + { 0x4d20, "int_polarity_flag_bst_ocpok"}, /* Polarity DCDC OCP , */\ + { 0x4d30, "int_polarity_flag_bst_peakcur"}, /* Polarity bst peakcur , */\ + { 0x4d40, "int_polarity_flag_bst_voutcomp"}, /* Polarity DCDC level 1x , */\ + { 0x4d50, "int_polarity_flag_bst_voutcomp86"}, /* Polarity DCDC level 1.14x , */\ + { 0x4d60, "int_polarity_flag_bst_voutcomp93"}, /* Polarity DCDC level 1.07x , */\ + { 0x4d90, "int_polarity_flag_ocp_alarm"}, /* Polarity ocp alarm , */\ + { 0x4da0, "int_polarity_flag_man_wait_src_settings"}, /* Polarity waits HW I2C settings , */\ + { 0x4dc0, "int_polarity_flag_man_start_mute_audio"}, /* Polarity man audio mute sequence , */\ + { 0x4df0, "int_polarity_flag_clk_out_of_range"}, /* Polarity flag_clk_out_of_range , */\ + { 0x4e00, "int_polarity_flag_tdm_error"}, /* Polarity tdm error , */\ + { 0x4e20, "int_polarity_flag_clip"}, /* Polarity clip right , */\ + { 0x4e40, "int_polarity_flag_lp_detect_mode0"}, /* Polarity low power mode0 , */\ + { 0x4e50, "int_polarity_flag_lp_detect_mode1"}, /* Polarity low power mode1 , */\ + { 0x4e60, "int_polarity_flag_low_amplitude"}, /* Polarity low noise mode , */\ + { 0x4e70, "int_polarity_flag_vddp_gt_vbat"}, /* Polarity VDDP greater than VBAT , */\ + { 0x5001, "vbat_prot_attack_time"}, /* Battery Safeguard attack time , */\ + { 0x5023, "vbat_prot_thlevel"}, /* Battery Safeguard threshold voltage level , */\ + { 0x5061, "vbat_prot_max_reduct"}, /* Battery Safeguard maximum reduction , */\ + { 0x50d0, "rst_min_vbat"}, /* Reset clipper - Auto clear , */\ + { 0x50e0, "sel_vbat"}, /* Battery voltage read out , */\ + { 0x50f0, "bypass_clipper"}, /* Bypass battery safeguard , */\ + { 0x5100, "batsense_steepness"}, /* Vbat prot steepness , */\ + { 0x5110, "soft_mute"}, /* Soft mute HW , */\ + { 0x5150, "bypass_hp"}, /* Bypass HPF , */\ + { 0x5170, "enbl_dpsa"}, /* Enable DPSA , */\ + { 0x5222, "ctrl_cc"}, /* Clip control setting , */\ + { 0x5257, "gain"}, /* Amplifier gain , */\ + { 0x52d0, "ctrl_slopectrl"}, /* Enables slope control , */\ + { 0x52e0, "ctrl_slope"}, /* Slope speed setting (bin. coded) , */\ + { 0x5301, "dpsa_level"}, /* DPSA threshold levels , */\ + { 0x5321, "dpsa_release"}, /* DPSA Release time , */\ + { 0x5340, "clipfast"}, /* Clock selection for HW clipper for Battery Safeguard, */\ + { 0x5350, "bypass_lp"}, /* Bypass the low power filter inside temperature sensor, */\ + { 0x5400, "first_order_mode"}, /* Overrule to 1st order mode of control stage when clipping, */\ + { 0x5410, "bypass_ctrlloop"}, /* Switch amplifier into open loop configuration , */\ + { 0x5420, "fb_hz"}, /* Feedback resistor set to high ohmic , */\ + { 0x5430, "icomp_engage"}, /* Engage of icomp , */\ + { 0x5440, "ctrl_kickback"}, /* Prevent double pulses of output stage , */\ + { 0x5450, "icomp_engage_overrule"}, /* To overrule the functional icomp_engage signal during validation, */\ + { 0x5503, "ctrl_dem"}, /* Enable DEM icomp and DEM one bit dac , */\ + { 0x5543, "ctrl_dem_mismatch"}, /* Enable DEM icomp mismatch for testing , */\ + { 0x5582, "dpsa_drive"}, /* Drive setting (bin. coded) , */\ + { 0x570a, "enbl_amp"}, /* Switch on the class-D power sections, each part of the analog sections can be switched on/off individually, */\ + { 0x57b0, "enbl_engage"}, /* Enables/engage power stage and control loop , */\ + { 0x57c0, "enbl_engage_pst"}, /* Enables/engage power stage and control loop , */\ + { 0x5810, "hard_mute"}, /* Hard mute - PWM , */\ + { 0x5820, "pwm_shape"}, /* PWM shape , */\ + { 0x5844, "pwm_delay"}, /* PWM delay bits to set the delay, clockd is 1/(k*2048*fs), */\ + { 0x5890, "reclock_pwm"}, /* Reclock the pwm signal inside analog , */\ + { 0x58a0, "reclock_voltsense"}, /* Reclock the voltage sense pwm signal , */\ + { 0x58c0, "enbl_pwm_phase_shift"}, /* Control for pwm phase shift , */\ + { 0x6081, "pga_gain_set"}, /* PGA gain selection , */\ + { 0x60b0, "pga_lowpass_enable"}, /* Lowpass enable , */\ + { 0x60c0, "pga_pwr_enable"}, /* Power enable, directcontrol mode only , */\ + { 0x60d0, "pga_switch_enable"}, /* Switch enable, directcontrol mode only , */\ + { 0x60e0, "pga_switch_aux_enable"}, /* Switch enable aux, directcontrol mode only , */\ + { 0x6100, "force_idle"}, /* force low power in idle mode , */\ + { 0x6110, "bypass_idle"}, /* bypass low power idle mode , */\ + { 0x6123, "ctrl_attl"}, /* Second channel gain in case of stereo using a single coil. (Total gain depending on INPLEV). (In case of mono OR stereo using 2 separate DCDC channel 1 should be disabled using TDMDCE), */\ + { 0x6163, "ctrl_attr"}, /* Total gain depending on INPLEV setting (channel 0), */\ + { 0x61a0, "idle_cnt"}, /* idle counter , */\ + { 0x61b0, "enbl_idle_ch1"}, /* enable idle feature for channel 1 , */\ + { 0x6265, "zero_lvl"}, /* low noise gain switch zero trigger level , */\ + { 0x62c1, "ctrl_fb_classd"}, /* class D gain ctrl_fb_50k ctrl_fb_100k , */\ + { 0x62e1, "lownoisegain_mode"}, /* ctrl select mode , */\ + { 0x6305, "threshold_lvl"}, /* low noise gain switch trigger level , */\ + { 0x6365, "hold_time"}, /* ctrl hold time before low audio is reckoned to be low audio, */\ + { 0x6405, "lpm1_cal_offset"}, /* low power mode1 detector ctrl cal_offset from gain module , */\ + { 0x6465, "lpm1_zero_lvl"}, /* low power mode1 zero crossing detection level , */\ + { 0x64e1, "lpm1_mode"}, /* low power mode control , */\ + { 0x6505, "lpm1_threshold_lvl"}, /* low power mode1 amplitude trigger level , */\ + { 0x6565, "lpm1_hold_time"}, /* low power mode1 detector ctrl hold time before low audio is reckoned to be low audio, */\ + { 0x65c0, "disable_low_power_mode"}, /* low power mode1 detector control , */\ + { 0x6600, "dcdc_pfm20khz_limit"}, /* DCDC in PFM mode pwm mode is activated each 50us to force a pwm pulse, */\ + { 0x6611, "dcdc_ctrl_maxzercnt"}, /* DCDC. Number of zero current flags to count before going to pfm mode, */\ + { 0x6656, "dcdc_vbat_delta_detect"}, /* Threshold before booster is reacting on a delta Vbat (in PFM mode) by temporarily switching to PWM mode, */\ + { 0x66c0, "dcdc_ignore_vbat"}, /* Ignore an increase on Vbat , */\ + { 0x6700, "enbl_minion"}, /* Enables minion (small) power stage , */\ + { 0x6713, "vth_vddpvbat"}, /* select vddp-vbat thres signal , */\ + { 0x6750, "lpen_vddpvbat"}, /* select vddp-vbat filtred vs unfiltered compare , */\ + { 0x6801, "tdm_source_mapping"}, /* tdm source mapping , */\ + { 0x6821, "tdm_sourcea_frame_sel"}, /* Sensed value A , */\ + { 0x6841, "tdm_sourceb_frame_sel"}, /* Sensed value B , */\ + { 0x6881, "pdm_anc_sel"}, /* anc input , */\ + { 0x68a0, "anc_1scomplement"}, /* ANC one s complement , */\ + { 0x6901, "sam_mode"}, /* sam enable , */\ + { 0x6920, "sam_src"}, /* sam source , */\ + { 0x6931, "pdmdat_h_sel"}, /* pdm out value when pdm_clk is higth , */\ + { 0x6951, "pdmdat_l_sel"}, /* pdm out value when pdm_clk is low , */\ + { 0x6970, "sam_spkr_sel"}, /* ram output on mode sam and audio , */\ + { 0x6a02, "rst_min_vbat_delay"}, /* rst_min_vbat delay (nb fs) , */\ + { 0x6b00, "disable_auto_engage"}, /* disable auto engange , */\ + { 0x6b10, "sel_tdm_data_valid"}, /* select tdm valid for speaker subsystem , */\ + { 0x6c02, "ns_hp2ln_criterion"}, /* 0..7 zeroes at ns as threshold to swap from high_power to low_noise, */\ + { 0x6c32, "ns_ln2hp_criterion"}, /* 0..7 zeroes at ns as threshold to swap from low_noise to high_power, */\ + { 0x6c69, "spare_out"}, /* spare_out , */\ + { 0x6d0f, "spare_in"}, /* spare_in , */\ + { 0x6e00, "flag_lp_detect_mode0"}, /* low power mode 0 detection , */\ + { 0x6e10, "flag_lp_detect_mode1"}, /* low power mode 1 detection , */\ + { 0x6e20, "flag_low_amplitude"}, /* low amplitude detection , */\ + { 0x6e30, "flag_vddp_gt_vbat"}, /* vddp greater than vbat , */\ + { 0x6f02, "cursense_comp_delay"}, /* delay to allign compensation signal with current sense signal, */\ + { 0x6f40, "cursense_comp_sign"}, /* polarity of compensation for current sense , */\ + { 0x6f50, "enbl_cursense_comp"}, /* enable current sense compensation , */\ + { 0x6f60, "sel_clip_pwms"}, /* Select pwm clip flag , */\ + { 0x6f72, "pwms_clip_lvl"}, /* set the amount of pwm pulse that may be skipped before clip-flag is triggered, */\ + { 0x7002, "scnd_boost_voltage"}, /* Second boost voltage level , */\ + { 0x7033, "boost_cur"}, /* Max coil current , */\ + { 0x7071, "bst_slpcmplvl"}, /* Slope compensation current, represents LxF (inductance x frequency) value , */\ + { 0x7090, "boost_intel"}, /* Adaptive boost mode , */\ + { 0x70a0, "boost_speed"}, /* Soft ramp up/down , */\ + { 0x70e0, "dcdcoff_mode"}, /* DCDC on/off , */\ + { 0x70f0, "dcdc_pwmonly"}, /* DCDC PWM only mode , */\ + { 0x7104, "bst_drive"}, /* Binary coded drive setting for boost converter power stage, */\ + { 0x7151, "bst_scalecur"}, /* For testing direct control scale current , */\ + { 0x7174, "bst_slopecur"}, /* For testing direct control slope current , */\ + { 0x71c1, "bst_slope"}, /* Boost slope speed , */\ + { 0x71e0, "bst_bypass_bstcur"}, /* Bypass control for boost current settings , */\ + { 0x71f0, "bst_bypass_bstfoldback"}, /* Bypass control for boost foldback , */\ + { 0x7200, "enbl_bst_engage"}, /* Enable power stage dcdc controller , */\ + { 0x7210, "enbl_bst_hizcom"}, /* Enable hiz comparator , */\ + { 0x7220, "enbl_bst_peak2avg"}, /* Enable boost peak2avg functionality , */\ + { 0x7230, "enbl_bst_peakcur"}, /* Enable peak current , */\ + { 0x7240, "enbl_bst_power"}, /* Enable line of the powerstage , */\ + { 0x7250, "enbl_bst_slopecur"}, /* Enable bit of max-current dac , */\ + { 0x7260, "enbl_bst_voutcomp"}, /* Enable vout comparators , */\ + { 0x7270, "enbl_bst_voutcomp86"}, /* Enable vout-86 comparators , */\ + { 0x7280, "enbl_bst_voutcomp93"}, /* Enable vout-93 comparators , */\ + { 0x7290, "enbl_bst_windac"}, /* Enable window dac , */\ + { 0x72a5, "bst_windac"}, /* for testing direct control windac , */\ + { 0x7300, "boost_alg"}, /* Control for boost adaptive loop gain , */\ + { 0x7311, "boost_loopgain"}, /* DCDC boost loopgain setting , */\ + { 0x7331, "bst_freq"}, /* DCDC boost frequency control , */\ + { 0x7402, "frst_boost_voltage"}, /* 1st boost voltage level , */\ + { 0x7430, "boost_track"}, /* Boost algorithm selection, effective only when boost_intelligent is set to 1, */\ + { 0x7444, "boost_trip_lvl_1st"}, /* 1st Adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7494, "boost_hold_time"}, /* Hold time for DCDC booster, effective only when boost_intelligent is set to 1, */\ + { 0x74e0, "sel_dcdc_envelope_8fs"}, /* Selection of data for adaptive boost algorithm, effective only when boost_intelligent is set to 1, */\ + { 0x74f0, "ignore_flag_voutcomp86"}, /* Ignore flag_voutcomp86 , */\ + { 0x7502, "track_decay"}, /* DCDC Boost decay speed after a peak value, effective only when boost_track is set to 1, */\ + { 0x7534, "boost_trip_lvl_2nd"}, /* 2nd Adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7584, "boost_trip_lvl_track"}, /* Track Adaptive boost trip levels, effective only when boost_intelligent is set to 1, */\ + { 0x7620, "pga_test_ldo_bypass"}, /* bypass internal PGA LDO , */\ + { 0x8001, "sel_clk_cs"}, /* Current sense clock duty cycle control , */\ + { 0x8021, "micadc_speed"}, /* Current sense clock for MiCADC selection - 32/44.1/48 KHz Fs band only, */\ + { 0x8050, "cs_gain_control"}, /* Current sense gain control , */\ + { 0x8060, "cs_bypass_gc"}, /* Bypasses the CS gain correction , */\ + { 0x8087, "cs_gain"}, /* Current sense gain , */\ + { 0x8200, "enbl_cmfb"}, /* Current sense common mode feedback control , */\ + { 0x8210, "invertpwm"}, /* Current sense common mode feedback pwm invert control, */\ + { 0x8222, "cmfb_gain"}, /* Current sense common mode feedback control gain , */\ + { 0x8254, "cmfb_offset"}, /* Current sense common mode feedback control offset , */\ + { 0x82a0, "cs_sam_set"}, /* Enable SAM input for current sense , */\ + { 0x8305, "cs_ktemp"}, /* Current sense temperature compensation trimming (1 - VALUE*TEMP)*signal, */\ + { 0x8400, "cs_adc_bsoinv"}, /* Bitstream inversion for current sense ADC , */\ + { 0x8421, "cs_adc_hifreq"}, /* Frequency mode current sense ADC , */\ + { 0x8440, "cs_adc_nortz"}, /* Return to zero for current sense ADC , */\ + { 0x8453, "cs_adc_offset"}, /* Micadc ADC offset setting , */\ + { 0x8490, "cs_adc_slowdel"}, /* Select delay for current sense ADC (internal decision circuitry), */\ + { 0x84a4, "cs_adc_gain"}, /* Gain setting for current sense ADC (two's complement), */\ + { 0x8500, "cs_resonator_enable"}, /* Enable for resonator to improve SRN , */\ + { 0x8510, "cs_classd_tran_skip"}, /* Skip current sense connection during a classD amplifier transition, */\ + { 0x8530, "cs_inn_short"}, /* Short current sense negative to common mode , */\ + { 0x8540, "cs_inp_short"}, /* Short current sense positive to common mode , */\ + { 0x8550, "cs_ldo_bypass"}, /* Bypass current sense LDO , */\ + { 0x8560, "cs_ldo_pulldown"}, /* Pull down current sense LDO, only valid if left_enbl_cs_ldo is high, */\ + { 0x8574, "cs_ldo_voset"}, /* Current sense LDO voltage level setting (two's complement), */\ + { 0x8700, "enbl_cs_adc"}, /* Enable current sense ADC , */\ + { 0x8710, "enbl_cs_inn1"}, /* Enable connection of current sense negative1 , */\ + { 0x8720, "enbl_cs_inn2"}, /* Enable connection of current sense negative2 , */\ + { 0x8730, "enbl_cs_inp1"}, /* Enable connection of current sense positive1 , */\ + { 0x8740, "enbl_cs_inp2"}, /* Enable connection of current sense positive2 , */\ + { 0x8750, "enbl_cs_ldo"}, /* Enable current sense LDO , */\ + { 0x8760, "enbl_cs_nofloating_n"}, /* Connect current sense negative to gnda at transitions of booster or classd amplifiers. Otherwise floating (0), */\ + { 0x8770, "enbl_cs_nofloating_p"}, /* Connect current sense positive to gnda at transitions of booster or classd amplifiers. Otherwise floating (0), */\ + { 0x8780, "enbl_cs_vbatldo"}, /* Enable of current sense LDO , */\ + { 0x8800, "volsense_pwm_sel"}, /* Voltage sense PWM source selection control , */\ + { 0x8810, "vol_cur_sense_dc_offset"}, /* voltage and current sense decimator offset control, */\ + { 0xa007, "mtpkey1"}, /* 5Ah, 90d To access KEY1_Protected registers (Default for engineering), */\ + { 0xa107, "mtpkey2"}, /* MTP KEY2 register , */\ + { 0xa200, "key01_locked"}, /* Indicates KEY1 is locked , */\ + { 0xa210, "key02_locked"}, /* Indicates KEY2 is locked , */\ + { 0xa302, "mtp_man_address_in"}, /* MTP address from I2C register for read/writing mtp in manual single word mode, */\ + { 0xa330, "man_copy_mtp_to_iic"}, /* Start copying single word from mtp to I2C mtp register, */\ + { 0xa340, "man_copy_iic_to_mtp"}, /* Start copying single word from I2C mtp register to mtp, */\ + { 0xa350, "auto_copy_mtp_to_iic"}, /* Start copying all the data from mtp to I2C mtp registers, */\ + { 0xa360, "auto_copy_iic_to_mtp"}, /* Start copying data from I2C mtp registers to mtp , */\ + { 0xa400, "faim_set_clkws"}, /* Sets the faim controller clock wait state register, */\ + { 0xa410, "faim_sel_evenrows"}, /* All even rows of the faim are selected, active high, */\ + { 0xa420, "faim_sel_oddrows"}, /* All odd rows of the faim are selected, all rows in combination with sel_evenrows, */\ + { 0xa430, "faim_program_only"}, /* Skip the erase access at wr_faim command (write-program-marginread), */\ + { 0xa440, "faim_erase_only"}, /* Skip the program access at wr_faim command (write-erase-marginread), */\ + { 0xa50f, "mtp_man_data_out_msb"}, /* MSB word of MTP manual read data , */\ + { 0xa60f, "mtp_man_data_out_lsb"}, /* LSB word of MTP manual read data , */\ + { 0xa70f, "mtp_man_data_in_msb"}, /* MSB word of write data for MTP manual write , */\ + { 0xa80f, "mtp_man_data_in_lsb"}, /* LSB word of write data for MTP manual write , */\ + { 0xb010, "bypass_ocpcounter"}, /* Bypass OCP Counter , */\ + { 0xb020, "bypass_glitchfilter"}, /* Bypass glitch filter , */\ + { 0xb030, "bypass_ovp"}, /* Bypass OVP , */\ + { 0xb040, "bypass_uvp"}, /* Bypass UVP , */\ + { 0xb050, "bypass_otp"}, /* Bypass OTP , */\ + { 0xb060, "bypass_lost_clk"}, /* Bypass lost clock detector , */\ + { 0xb070, "ctrl_vpalarm"}, /* vpalarm (uvp ovp handling) , */\ + { 0xb087, "ocp_threshold"}, /* OCP threshold level , */\ + { 0xb108, "ext_temp"}, /* External temperature (C) , */\ + { 0xb190, "ext_temp_sel"}, /* Select temp Speaker calibration , */\ + { 0xc000, "use_direct_ctrls"}, /* Direct control to overrule several functions for testing, */\ + { 0xc010, "rst_datapath"}, /* Direct control for datapath reset , */\ + { 0xc020, "rst_cgu"}, /* Direct control for cgu reset , */\ + { 0xc038, "enbl_ref"}, /* Switch on the analog references, each part of the references can be switched on/off individually, */\ + { 0xc0d0, "enbl_ringo"}, /* Enable the ring oscillator for test purpose , */\ + { 0xc0e0, "use_direct_clk_ctrl"}, /* Direct clock control to overrule several functions for testing, */\ + { 0xc0f0, "use_direct_pll_ctrl"}, /* Direct PLL control to overrule several functions for testing, */\ + { 0xc100, "enbl_tsense"}, /* Temperature sensor enable control - I2C direct mode, */\ + { 0xc110, "tsense_hibias"}, /* Bit to set the biasing in temp sensor to high , */\ + { 0xc120, "enbl_flag_vbg"}, /* Enable flagging of bandgap out of control , */\ + { 0xc20f, "abist_offset"}, /* Offset control for ABIST testing (two's complement), */\ + { 0xc300, "bypasslatch"}, /* Bypass latch , */\ + { 0xc311, "sourcea"}, /* Set OUTA to , */\ + { 0xc331, "sourceb"}, /* Set OUTB to , */\ + { 0xc350, "inverta"}, /* Invert pwma test signal , */\ + { 0xc360, "invertb"}, /* Invert pwmb test signal , */\ + { 0xc374, "pulselength"}, /* Pulse length setting test input for amplifier (clock d - k*2048*fs), */\ + { 0xc3c0, "tdm_enable_loopback"}, /* TDM loopback test , */\ + { 0xc400, "bst_bypasslatch"}, /* Bypass latch in boost converter , */\ + { 0xc411, "bst_source"}, /* Sets the source of the pwmbst output to boost converter input for testing, */\ + { 0xc430, "bst_invertb"}, /* Invert pwmbst test signal , */\ + { 0xc444, "bst_pulselength"}, /* Pulse length setting test input for boost converter , */\ + { 0xc490, "test_bst_ctrlsthv"}, /* Test mode for boost control stage , */\ + { 0xc4a0, "test_bst_iddq"}, /* IDDQ testing in power stage of boost converter , */\ + { 0xc4b0, "test_bst_rdson"}, /* RDSON testing - boost power stage , */\ + { 0xc4c0, "test_bst_cvi"}, /* CVI testing - boost power stage , */\ + { 0xc4d0, "test_bst_ocp"}, /* Boost OCP. For old ocp (ctrl_reversebst is 0), For new ocp (ctrl_reversebst is 1), */\ + { 0xc4e0, "test_bst_sense"}, /* Test option for the sense NMOS in booster for current mode control., */\ + { 0xc500, "test_cvi"}, /* Analog BIST, switch choose which transistor will be used as current source (also cross coupled sources possible), */\ + { 0xc510, "test_discrete"}, /* Test function noise measurement , */\ + { 0xc520, "test_iddq"}, /* Set the power stages in iddq mode for gate stress., */\ + { 0xc540, "test_rdson"}, /* Analog BIST, switch to enable Rdson measurement , */\ + { 0xc550, "test_sdelta"}, /* Analog BIST, noise test , */\ + { 0xc570, "test_enbl_cs"}, /* Enable for digimux mode of current sense , */\ + { 0xc5b0, "pga_test_enable"}, /* Enable PGA test mode , */\ + { 0xc5c0, "pga_test_offset_enable"}, /* Enable PGA test offset , */\ + { 0xc5d0, "pga_test_shortinput_enable"}, /* Enable PGA test shortinput , */\ + { 0xc600, "enbl_pwm_dcc"}, /* Enables direct control of pwm duty cycle for DCDC power stage, */\ + { 0xc613, "pwm_dcc_cnt"}, /* Control pwm duty cycle when enbl_pwm_dcc is 1 , */\ + { 0xc650, "enbl_ldo_stress"}, /* Enable stress of internal supply voltages powerstages, */\ + { 0xc707, "digimuxa_sel"}, /* DigimuxA input selection control routed to DATAO (see Digimux list for details), */\ + { 0xc787, "digimuxb_sel"}, /* DigimuxB input selection control routed to INT (see Digimux list for details), */\ + { 0xc807, "digimuxc_sel"}, /* DigimuxC input selection control routed to PDMDAT (see Digimux list for details), */\ + { 0xc981, "int_ehs"}, /* Speed/load setting for INT IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc9c0, "hs_mode"}, /* I2C high speed mode control , */\ + { 0xca00, "enbl_anamux1"}, /* Enable anamux1 , */\ + { 0xca10, "enbl_anamux2"}, /* Enable anamux2 , */\ + { 0xca20, "enbl_anamux3"}, /* Enable anamux3 , */\ + { 0xca30, "enbl_anamux4"}, /* Enable anamux4 , */\ + { 0xca74, "anamux1"}, /* Anamux selection control - anamux on TEST1 , */\ + { 0xcb04, "anamux2"}, /* Anamux selection control - anamux on TEST2 , */\ + { 0xcb54, "anamux3"}, /* Anamux selection control - anamux on TEST3 , */\ + { 0xcba4, "anamux4"}, /* Anamux selection control - anamux on TEST4 , */\ + { 0xcd05, "pll_seli"}, /* PLL SELI - I2C direct PLL control mode only , */\ + { 0xcd64, "pll_selp"}, /* PLL SELP - I2C direct PLL control mode only , */\ + { 0xcdb3, "pll_selr"}, /* PLL SELR - I2C direct PLL control mode only , */\ + { 0xcdf0, "pll_frm"}, /* PLL free running mode control; 1 in TCB direct control mode, else this control bit, */\ + { 0xce09, "pll_ndec"}, /* PLL NDEC - I2C direct PLL control mode only , */\ + { 0xcea0, "pll_mdec_msb"}, /* MSB of pll_mdec - I2C direct PLL control mode only, */\ + { 0xceb0, "enbl_pll"}, /* Enables PLL in I2C direct PLL control mode only , */\ + { 0xcec0, "enbl_osc"}, /* Enables OSC1M in I2C direct control mode only , */\ + { 0xced0, "pll_bypass"}, /* PLL bypass control in I2C direct PLL control mode only, */\ + { 0xcee0, "pll_directi"}, /* PLL directi control in I2C direct PLL control mode only, */\ + { 0xcef0, "pll_directo"}, /* PLL directo control in I2C direct PLL control mode only, */\ + { 0xcf0f, "pll_mdec_lsb"}, /* Bits 15..0 of PLL MDEC are I2C direct PLL control mode only, */\ + { 0xd006, "pll_pdec"}, /* PLL PDEC - I2C direct PLL control mode only , */\ + { 0xd10f, "tsig_freq_lsb"}, /* Internal sinus test generator frequency control , */\ + { 0xd202, "tsig_freq_msb"}, /* Select internal sinus test generator, frequency control msb bits, */\ + { 0xd230, "inject_tsig"}, /* Control bit to switch to internal sinus test generator, */\ + { 0xd283, "tsig_gain"}, /* Test signal gain , */\ + { 0xd300, "adc10_reset"}, /* Reset for ADC10 - I2C direct control mode , */\ + { 0xd311, "adc10_test"}, /* Test mode selection signal for ADC10 - I2C direct control mode, */\ + { 0xd332, "adc10_sel"}, /* Select the input to convert for ADC10 - I2C direct control mode, */\ + { 0xd364, "adc10_prog_sample"}, /* ADC10 program sample setting - I2C direct control mode, */\ + { 0xd3b0, "adc10_enbl"}, /* Enable ADC10 - I2C direct control mode , */\ + { 0xd3c0, "bypass_lp_vbat"}, /* Bypass control for Low pass filter in batt sensor , */\ + { 0xd409, "data_adc10_tempbat"}, /* ADC 10 data output data for testing , */\ + { 0xd507, "ctrl_digtoana_hidden"}, /* Spare digital to analog control bits - Hidden , */\ + { 0xd580, "enbl_clk_out_of_range"}, /* Clock out of range , */\ + { 0xd621, "clkdiv_audio_sel"}, /* Audio clock divider selection in direct clock control mode, */\ + { 0xd641, "clkdiv_muxa_sel"}, /* DCDC MUXA clock divider selection in direct clock control mode, */\ + { 0xd661, "clkdiv_muxb_sel"}, /* DCDC MUXB clock divider selection in direct clock control mode, */\ + { 0xd701, "pdmdat_ehs"}, /* Speed/load setting for PDMDAT IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xd721, "datao_ehs"}, /* Speed/load setting for DATAO IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xd740, "bck_ehs"}, /* Speed/load setting for DATAO IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xd750, "datai_ehs"}, /* Speed/load setting for DATAO IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xd760, "pdmclk_ehs"}, /* Speed/load setting for DATAO IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xd800, "source_in_testmode"}, /* tdm source in test mode (return only current and voltage sense), */\ + { 0xd810, "gainatt_feedback"}, /* gainatt feedback to tdm , */\ + { 0xd822, "test_parametric_io"}, /* test io parametric , */\ + { 0xd850, "ctrl_bst_clk_lp1"}, /* boost clock control in low power mode1 , */\ + { 0xd861, "test_spare_out1"}, /* test spare out 1 , */\ + { 0xd880, "bst_dcmbst"}, /* dcm boost , */\ + { 0xd890, "pdm_loopback"}, /* pdm loop back to tdm , */\ + { 0xd8a1, "force_pga_clock"}, /* force pga clock , */\ + { 0xd8c3, "test_spare_out2"}, /* test spare out 1 , */\ + { 0xee0f, "sw_profile"}, /* Software profile data , */\ + { 0xef0f, "sw_vstep"}, /* Software vstep information , */\ + { 0xf000, "calibration_onetime"}, /* Calibration schedule , */\ + { 0xf010, "calibr_ron_done"}, /* Calibration Ron executed , */\ + { 0xf020, "calibr_dcdc_api_calibrate"}, /* Calibration current limit DCDC , */\ + { 0xf030, "calibr_dcdc_delta_sign"}, /* Sign bit for delta calibration current limit DCDC , */\ + { 0xf042, "calibr_dcdc_delta"}, /* Calibration delta current limit DCDC , */\ + { 0xf078, "calibr_speaker_info"}, /* Reserved space for allowing customer to store speaker information, */\ + { 0xf105, "calibr_vout_offset"}, /* DCDC offset calibration 2's complement (key1 protected), */\ + { 0xf163, "spare_mpt1_9_6"}, /* HW gain module - left channel (2's complement) , */\ + { 0xf1a5, "spare_mpt1_15_10"}, /* Offset for amplifier, HW gain module - left channel (2's complement), */\ + { 0xf203, "calibr_gain"}, /* HW gain module (2's complement) , */\ + { 0xf245, "calibr_offset"}, /* Offset for amplifier, HW gain module (2's complement), */\ + { 0xf2a3, "spare_mpt2_13_10"}, /* Trimming of LDO (2.7V) , */\ + { 0xf307, "spare_mpt3_7_0"}, /* SPARE , */\ + { 0xf387, "calibr_gain_cs"}, /* Current sense gain (signed two's complement format), */\ + { 0xf40f, "spare_mtp4_15_0"}, /* SPARE , */\ + { 0xf50f, "calibr_R25C_R"}, /* Ron resistance of speaker coil , */\ + { 0xf606, "spare_mpt6_6_0"}, /* SPARE , */\ + { 0xf686, "spare_mpt6_14_8"}, /* Offset of left amplifier level shifter B , */\ + { 0xf706, "ctrl_offset_a"}, /* Offset of level shifter A , */\ + { 0xf786, "ctrl_offset_b"}, /* Offset of amplifier level shifter B , */\ + { 0xf806, "htol_iic_addr"}, /* 7-bit I2C address to be used during HTOL testing , */\ + { 0xf870, "htol_iic_addr_en"}, /* HTOL I2C address enable control , */\ + { 0xf884, "calibr_temp_offset"}, /* Temperature offset 2's compliment (key1 protected), */\ + { 0xf8d2, "calibr_temp_gain"}, /* Temperature gain 2's compliment (key1 protected) , */\ + { 0xf900, "mtp_lock_dcdcoff_mode"}, /* Disable function dcdcoff_mode , */\ + { 0xf910, "disable_sam_mode"}, /* Disable same mode , */\ + { 0xf920, "mtp_lock_bypass_clipper"}, /* Disable function bypass_clipper , */\ + { 0xf930, "mtp_lock_max_dcdc_voltage"}, /* Disable programming of max dcdc boost voltage , */\ + { 0xf943, "calibr_vbg_trim"}, /* Bandgap trimming control , */\ + { 0xf980, "mtp_enbl_amp_in_state_alarm"}, /* Enbl_amp in alarm state , */\ + { 0xf990, "mtp_enbl_pwm_delay_clock_gating"}, /* pwm delay clock auto gating , */\ + { 0xf9a0, "mtp_enbl_ocp_clock_gating"}, /* ocpclock auto gating , */\ + { 0xf9b0, "mtp_gate_cgu_clock_for_test"}, /* cgu test clock control , */\ + { 0xf9c3, "spare_mtp9_15_12"}, /* MTP-control FW - See Firmware I2C API document for details, */\ + { 0xfa0f, "mtpdataA"}, /* MTPdataA (key1 protected) , */\ + { 0xfb0f, "mtpdataB"}, /* MTPdataB (key1 protected) , */\ + { 0xfc0f, "mtpdataC"}, /* MTPdataC (key1 protected) , */\ + { 0xfd0f, "mtpdataD"}, /* MTPdataD (key1 protected) , */\ + { 0xfe0f, "mtpdataE"}, /* MTPdataE (key1 protected) , */\ + { 0xff07, "calibr_osc_delta_ndiv"}, /* Calibration data for OSC1M, signed number representation, */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +enum TFA9872_irq { + TFA9872_irq_stvdds = 0, + TFA9872_irq_stplls = 1, + TFA9872_irq_stotds = 2, + TFA9872_irq_stovds = 3, + TFA9872_irq_stuvds = 4, + TFA9872_irq_stclks = 5, + TFA9872_irq_stmtpb = 6, + TFA9872_irq_stnoclk = 7, + TFA9872_irq_stsws = 10, + TFA9872_irq_stamps = 12, + TFA9872_irq_starefs = 13, + TFA9872_irq_stadccr = 14, + TFA9872_irq_stbstcu = 16, + TFA9872_irq_stbsthi = 17, + TFA9872_irq_stbstoc = 18, + TFA9872_irq_stbstpkcur = 19, + TFA9872_irq_stbstvc = 20, + TFA9872_irq_stbst86 = 21, + TFA9872_irq_stbst93 = 22, + TFA9872_irq_stocpr = 25, + TFA9872_irq_stmwsrc = 26, + TFA9872_irq_stmwsmu = 28, + TFA9872_irq_stclkoor = 31, + TFA9872_irq_sttdmer = 32, + TFA9872_irq_stclpr = 34, + TFA9872_irq_stlp0 = 36, + TFA9872_irq_stlp1 = 37, + TFA9872_irq_stla = 38, + TFA9872_irq_stvddph = 39, + TFA9872_irq_max = 40, + TFA9872_irq_all = -1 /* all irqs */}; + +#define TFA9872_IRQ_NAMETABLE static tfaIrqName_t Tfa9872IrqNames[]= {\ + { 0, "STVDDS"},\ + { 1, "STPLLS"},\ + { 2, "STOTDS"},\ + { 3, "STOVDS"},\ + { 4, "STUVDS"},\ + { 5, "STCLKS"},\ + { 6, "STMTPB"},\ + { 7, "STNOCLK"},\ + { 8, "8"},\ + { 9, "9"},\ + { 10, "STSWS"},\ + { 11, "11"},\ + { 12, "STAMPS"},\ + { 13, "STAREFS"},\ + { 14, "STADCCR"},\ + { 15, "15"},\ + { 16, "STBSTCU"},\ + { 17, "STBSTHI"},\ + { 18, "STBSTOC"},\ + { 19, "STBSTPKCUR"},\ + { 20, "STBSTVC"},\ + { 21, "STBST86"},\ + { 22, "STBST93"},\ + { 23, "23"},\ + { 24, "24"},\ + { 25, "STOCPR"},\ + { 26, "STMWSRC"},\ + { 27, "27"},\ + { 28, "STMWSMU"},\ + { 29, "29"},\ + { 30, "30"},\ + { 31, "STCLKOOR"},\ + { 32, "STTDMER"},\ + { 33, "33"},\ + { 34, "STCLPR"},\ + { 35, "35"},\ + { 36, "STLP0"},\ + { 37, "STLP1"},\ + { 38, "STLA"},\ + { 39, "STVDDPH"},\ + { 40, "40"},\ +}; +#endif /* _TFA9872_TFAFIELDNAMES_H */ diff --git a/sound/soc/codecs/inc/tfa9874_tfafieldnames.h b/sound/soc/codecs/inc/tfa9874_tfafieldnames.h new file mode 100644 index 000000000000..72c155418810 --- /dev/null +++ b/sound/soc/codecs/inc/tfa9874_tfafieldnames.h @@ -0,0 +1,830 @@ +/** Filename: tfa9874_tfafieldnames.h + * This file was generated automatically on 08/22/17 at 13:43:43. + * Source file: TFA9874N1C0_DefaultI2CSettings.xlsx + */ + +#ifndef _TFA9874_TFAFIELDNAMES_H +#define _TFA9874_TFAFIELDNAMES_H + + +#define TFA9874_I2CVERSION 1 + +typedef enum nxpTfa9874BfEnumList { + TFA9874_BF_PWDN = 0x0000, /*!< Powerdown selection */ + TFA9874_BF_I2CR = 0x0010, /*!< I2C Reset - Auto clear */ + TFA9874_BF_AMPE = 0x0030, /*!< Activate Amplifier */ + TFA9874_BF_DCA = 0x0040, /*!< Activate DC-to-DC converter */ + TFA9874_BF_INTP = 0x0071, /*!< Interrupt config */ + TFA9874_BF_BYPOCP= 0x00b0, /*!< Bypass OCP */ + TFA9874_BF_TSTOCP= 0x00c0, /*!< OCP testing control */ + TFA9874_BF_MANSCONF= 0x0120, /*!< I2C configured */ + TFA9874_BF_MANAOOSC= 0x0140, /*!< Internal osc off at PWDN */ + TFA9874_BF_MUTETO= 0x01d0, /*!< Time out SB mute sequence */ + TFA9874_BF_OPENMTP= 0x01e0, /*!< Control for FAIM protection */ + TFA9874_BF_AUDFS = 0x0203, /*!< Sample rate (fs) */ + TFA9874_BF_INPLEV= 0x0240, /*!< TDM output attenuation */ + TFA9874_BF_FRACTDEL= 0x0255, /*!< V/I Fractional delay */ + TFA9874_BF_REV = 0x030f, /*!< Revision info */ + TFA9874_BF_REFCKEXT= 0x0401, /*!< PLL external ref clock */ + TFA9874_BF_REFCKSEL= 0x0420, /*!< PLL internal ref clock */ + TFA9874_BF_SSFAIME= 0x05c0, /*!< Sub-system FAIM */ + TFA9874_BF_AMPOCRT= 0x0802, /*!< Amplifier on-off criteria for shutdown */ + TFA9874_BF_VDDS = 0x1000, /*!< POR */ + TFA9874_BF_DCOCPOK= 0x1010, /*!< DCDC OCP nmos (sticky register , clear on read) */ + TFA9874_BF_OTDS = 0x1020, /*!< OTP alarm (sticky register , clear on read) */ + TFA9874_BF_OCDS = 0x1030, /*!< OCP amplifier (sticky register , clear on read) */ + TFA9874_BF_UVDS = 0x1040, /*!< UVP alarm (sticky register , clear on read) */ + TFA9874_BF_MANALARM= 0x1050, /*!< Alarm state */ + TFA9874_BF_TDMERR= 0x1060, /*!< TDM error */ + TFA9874_BF_NOCLK = 0x1070, /*!< Lost clock (sticky register , clear on read) */ + TFA9874_BF_DCIL = 0x1100, /*!< DCDC current limiting */ + TFA9874_BF_DCDCA = 0x1110, /*!< DCDC active (sticky register , clear on read) */ + TFA9874_BF_DCHVBAT= 0x1130, /*!< DCDC level 1x */ + TFA9874_BF_DCH114= 0x1140, /*!< DCDC level 1.14x */ + TFA9874_BF_DCH107= 0x1150, /*!< DCDC level 1.07x */ + TFA9874_BF_PLLS = 0x1160, /*!< PLL lock */ + TFA9874_BF_CLKS = 0x1170, /*!< Clocks stable */ + TFA9874_BF_TDMLUTER= 0x1180, /*!< TDM LUT error */ + TFA9874_BF_TDMSTAT= 0x1192, /*!< TDM status bits */ + TFA9874_BF_MTPB = 0x11c0, /*!< MTP busy */ + TFA9874_BF_SWS = 0x11d0, /*!< Amplifier engage */ + TFA9874_BF_AMPS = 0x11e0, /*!< Amplifier enable */ + TFA9874_BF_AREFS = 0x11f0, /*!< References enable */ + TFA9874_BF_OCPOAP= 0x1300, /*!< OCPOK pmos A */ + TFA9874_BF_OCPOAN= 0x1310, /*!< OCPOK nmos A */ + TFA9874_BF_OCPOBP= 0x1320, /*!< OCPOK pmos B */ + TFA9874_BF_OCPOBN= 0x1330, /*!< OCPOK nmos B */ + TFA9874_BF_OVDS = 0x1380, /*!< OVP alarm */ + TFA9874_BF_CLIPS = 0x1390, /*!< Amplifier clipping */ + TFA9874_BF_ADCCR = 0x13a0, /*!< Control ADC */ + TFA9874_BF_MANWAIT1= 0x13c0, /*!< Wait HW I2C settings */ + TFA9874_BF_MANMUTE= 0x13e0, /*!< Audio mute sequence */ + TFA9874_BF_MANOPER= 0x13f0, /*!< Operating state */ + TFA9874_BF_CLKOOR= 0x1420, /*!< External clock status */ + TFA9874_BF_MANSTATE= 0x1433, /*!< Device manager status */ + TFA9874_BF_DCMODE= 0x1471, /*!< DCDC mode status bits */ + TFA9874_BF_BATS = 0x1509, /*!< Battery voltage (V) */ + TFA9874_BF_TEMPS = 0x1608, /*!< IC Temperature (C) */ + TFA9874_BF_VDDPS = 0x1709, /*!< IC VDDP voltage ( 1023*VDDP/13 V) */ + TFA9874_BF_TDME = 0x2040, /*!< Enable interface */ + TFA9874_BF_TDMMODE= 0x2050, /*!< Slave/master */ + TFA9874_BF_TDMCLINV= 0x2060, /*!< Reception data to BCK clock */ + TFA9874_BF_TDMFSLN= 0x2073, /*!< FS length (master mode only) */ + TFA9874_BF_TDMFSPOL= 0x20b0, /*!< FS polarity */ + TFA9874_BF_TDMNBCK= 0x20c3, /*!< N-BCK's in FS */ + TFA9874_BF_TDMSLOTS= 0x2103, /*!< N-slots in Frame */ + TFA9874_BF_TDMSLLN= 0x2144, /*!< N-bits in slot */ + TFA9874_BF_TDMBRMG= 0x2194, /*!< N-bits remaining */ + TFA9874_BF_TDMDEL= 0x21e0, /*!< data delay to FS */ + TFA9874_BF_TDMADJ= 0x21f0, /*!< data adjustment */ + TFA9874_BF_TDMOOMP= 0x2201, /*!< Received audio compression */ + TFA9874_BF_TDMSSIZE= 0x2224, /*!< Sample size per slot */ + TFA9874_BF_TDMTXDFO= 0x2271, /*!< Format unused bits */ + TFA9874_BF_TDMTXUS0= 0x2291, /*!< Format unused slots DATAO */ + TFA9874_BF_TDMSPKE= 0x2300, /*!< Control audio tdm channel in 0 (spkr + dcdc) */ + TFA9874_BF_TDMDCE= 0x2310, /*!< Control audio tdm channel in 1 (dcdc) */ + TFA9874_BF_TDMCSE= 0x2330, /*!< current sense vbat temperature and vddp feedback */ + TFA9874_BF_TDMVSE= 0x2340, /*!< Voltage sense vbat temperature and vddp feedback */ + TFA9874_BF_TDMSPKS= 0x2603, /*!< tdm slot for sink 0 (speaker + dcdc) */ + TFA9874_BF_TDMDCS= 0x2643, /*!< tdm slot for sink 1 (dcdc) */ + TFA9874_BF_TDMCSS= 0x26c3, /*!< Slot Position of current sense vbat temperature and vddp feedback */ + TFA9874_BF_TDMVSS= 0x2703, /*!< Slot Position of Voltage sense vbat temperature and vddp feedback */ + TFA9874_BF_ISTVDDS= 0x4000, /*!< Status POR */ + TFA9874_BF_ISTBSTOC= 0x4010, /*!< Status DCDC OCP */ + TFA9874_BF_ISTOTDS= 0x4020, /*!< Status OTP alarm */ + TFA9874_BF_ISTOCPR= 0x4030, /*!< Status ocp alarm */ + TFA9874_BF_ISTUVDS= 0x4040, /*!< Status UVP alarm */ + TFA9874_BF_ISTMANALARM= 0x4050, /*!< Status nanager Alarm state */ + TFA9874_BF_ISTTDMER= 0x4060, /*!< Status tdm error */ + TFA9874_BF_ISTNOCLK= 0x4070, /*!< Status lost clock */ + TFA9874_BF_ICLVDDS= 0x4400, /*!< Clear POR */ + TFA9874_BF_ICLBSTOC= 0x4410, /*!< Clear DCDC OCP */ + TFA9874_BF_ICLOTDS= 0x4420, /*!< Clear OTP alarm */ + TFA9874_BF_ICLOCPR= 0x4430, /*!< Clear ocp alarm */ + TFA9874_BF_ICLUVDS= 0x4440, /*!< Clear UVP alarm */ + TFA9874_BF_ICLMANALARM= 0x4450, /*!< clear nanager Alarm state */ + TFA9874_BF_ICLTDMER= 0x4460, /*!< Clear tdm error */ + TFA9874_BF_ICLNOCLK= 0x4470, /*!< Clear lost clk */ + TFA9874_BF_IEVDDS= 0x4800, /*!< Enable por */ + TFA9874_BF_IEBSTOC= 0x4810, /*!< Enable DCDC OCP */ + TFA9874_BF_IEOTDS= 0x4820, /*!< Enable OTP alarm */ + TFA9874_BF_IEOCPR= 0x4830, /*!< Enable ocp alarm */ + TFA9874_BF_IEUVDS= 0x4840, /*!< Enable UVP alarm */ + TFA9874_BF_IEMANALARM= 0x4850, /*!< Enable nanager Alarm state */ + TFA9874_BF_IETDMER= 0x4860, /*!< Enable tdm error */ + TFA9874_BF_IENOCLK= 0x4870, /*!< Enable lost clk */ + TFA9874_BF_IPOVDDS= 0x4c00, /*!< Polarity por */ + TFA9874_BF_IPOBSTOC= 0x4c10, /*!< Polarity DCDC OCP */ + TFA9874_BF_IPOOTDS= 0x4c20, /*!< Polarity OTP alarm */ + TFA9874_BF_IPOOCPR= 0x4c30, /*!< Polarity ocp alarm */ + TFA9874_BF_IPOUVDS= 0x4c40, /*!< Polarity UVP alarm */ + TFA9874_BF_IPOMANALARM= 0x4c50, /*!< Polarity nanager Alarm state */ + TFA9874_BF_IPOTDMER= 0x4c60, /*!< Polarity tdm error */ + TFA9874_BF_IPONOCLK= 0x4c70, /*!< Polarity lost clk */ + TFA9874_BF_BSSCR = 0x5001, /*!< Battery Safeguard attack time */ + TFA9874_BF_BSST = 0x5023, /*!< Battery Safeguard threshold voltage level */ + TFA9874_BF_BSSRL = 0x5061, /*!< Battery Safeguard maximum reduction */ + TFA9874_BF_VBATFLTL= 0x5080, /*!< vbat filter limit */ + TFA9874_BF_BSSR = 0x50e0, /*!< Battery voltage read out */ + TFA9874_BF_BSSBY = 0x50f0, /*!< Bypass battery safeguard */ + TFA9874_BF_BSSS = 0x5100, /*!< Vbat prot steepness */ + TFA9874_BF_HPFBYP= 0x5150, /*!< Bypass HPF */ + TFA9874_BF_DPSA = 0x5170, /*!< Enable DPSA */ + TFA9874_BF_CLIPCTRL= 0x5222, /*!< Clip control setting */ + TFA9874_BF_AMPGAIN= 0x5257, /*!< Amplifier gain */ + TFA9874_BF_SLOPEE= 0x52d0, /*!< Enables slope control */ + TFA9874_BF_SLOPESET= 0x52e0, /*!< Slope speed setting (bin. coded) */ + TFA9874_BF_TDMDCG= 0x6123, /*!< Second channel gain in case of stereo using a single coil. (Total gain depending on INPLEV). (In case of mono OR stereo using 2 separate DCDC channel 1 should be disabled using TDMDCE) */ + TFA9874_BF_TDMSPKG= 0x6163, /*!< Total gain depending on INPLEV setting (channel 0) */ + TFA9874_BF_LNMODE= 0x62e1, /*!< ctrl select mode */ + TFA9874_BF_LPM1MODE= 0x64e1, /*!< low power mode control */ + TFA9874_BF_TDMSRCMAP= 0x6802, /*!< tdm source mapping */ + TFA9874_BF_TDMSRCAS= 0x6831, /*!< Sensed value A */ + TFA9874_BF_TDMSRCBS= 0x6851, /*!< Sensed value B */ + TFA9874_BF_TDMSRCACLIP= 0x6871, /*!< clip information (analog /digital) for source0 */ + TFA9874_BF_TDMSRCBCLIP= 0x6891, /*!< clip information (analog /digital) for source1 */ + TFA9874_BF_LP1 = 0x6e10, /*!< low power mode 1 detection */ + TFA9874_BF_LA = 0x6e20, /*!< low amplitude detection */ + TFA9874_BF_VDDPH = 0x6e30, /*!< vddp greater than vbat */ + TFA9874_BF_DELCURCOMP= 0x6f02, /*!< delay to allign compensation signal with current sense signal */ + TFA9874_BF_SIGCURCOMP= 0x6f40, /*!< polarity of compensation for current sense */ + TFA9874_BF_ENCURCOMP= 0x6f50, /*!< enable current sense compensation */ + TFA9874_BF_LVLCLPPWM= 0x6f72, /*!< set the amount of pwm pulse that may be skipped before clip-flag is triggered */ + TFA9874_BF_DCMCC = 0x7033, /*!< Max coil current */ + TFA9874_BF_DCCV = 0x7071, /*!< Slope compensation current, represents LxF (inductance x frequency) value */ + TFA9874_BF_DCIE = 0x7090, /*!< Adaptive boost mode */ + TFA9874_BF_DCSR = 0x70a0, /*!< Soft ramp up/down */ + TFA9874_BF_DCDIS = 0x70e0, /*!< DCDC on/off */ + TFA9874_BF_DCPWM = 0x70f0, /*!< DCDC PWM only mode */ + TFA9874_BF_DCTRACK= 0x7430, /*!< Boost algorithm selection, effective only when boost_intelligent is set to 1 */ + TFA9874_BF_DCTRIP= 0x7444, /*!< 1st Adaptive boost trip levels, effective only when DCIE is set to 1 */ + TFA9874_BF_DCHOLD= 0x7494, /*!< Hold time for DCDC booster, effective only when boost_intelligent is set to 1 */ + TFA9874_BF_DCTRIP2= 0x7534, /*!< 2nd Adaptive boost trip levels, effective only when DCIE is set to 1 */ + TFA9874_BF_DCTRIPT= 0x7584, /*!< Track Adaptive boost trip levels, effective only when boost_intelligent is set to 1 */ + TFA9874_BF_DCTRIPHYSTE= 0x75f0, /*!< Enable hysteresis on booster trip levels */ + TFA9874_BF_DCVOF = 0x7635, /*!< First boost voltage level */ + TFA9874_BF_DCVOS = 0x7695, /*!< Second boost voltage level */ + TFA9874_BF_MTPK = 0xa107, /*!< MTP KEY2 register */ + TFA9874_BF_KEY1LOCKED= 0xa200, /*!< Indicates KEY1 is locked */ + TFA9874_BF_KEY2LOCKED= 0xa210, /*!< Indicates KEY2 is locked */ + TFA9874_BF_CIMTP = 0xa360, /*!< Start copying data from I2C mtp registers to mtp */ + TFA9874_BF_MTPRDMSB= 0xa50f, /*!< MSB word of MTP manual read data */ + TFA9874_BF_MTPRDLSB= 0xa60f, /*!< LSB word of MTP manual read data */ + TFA9874_BF_EXTTS = 0xb108, /*!< External temperature (C) */ + TFA9874_BF_TROS = 0xb190, /*!< Select temp Speaker calibration */ + TFA9874_BF_SWPROFIL= 0xee0f, /*!< Software profile data */ + TFA9874_BF_SWVSTEP= 0xef0f, /*!< Software vstep information */ + TFA9874_BF_MTPOTC= 0xf000, /*!< Calibration schedule */ + TFA9874_BF_MTPEX = 0xf010, /*!< Calibration Ron executed */ + TFA9874_BF_DCMCCAPI= 0xf020, /*!< Calibration current limit DCDC */ + TFA9874_BF_DCMCCSB= 0xf030, /*!< Sign bit for delta calibration current limit DCDC */ + TFA9874_BF_USERDEF= 0xf042, /*!< Calibration delta current limit DCDC */ + TFA9874_BF_CUSTINFO= 0xf078, /*!< Reserved space for allowing customer to store speaker information */ + TFA9874_BF_R25C = 0xf50f, /*!< Ron resistance of speaker coil */ +} nxpTfa9874BfEnumList_t; +#define TFA9874_NAMETABLE static tfaBfName_t Tfa9874DatasheetNames[]= {\ + { 0x0, "PWDN"}, /* Powerdown selection , */\ + { 0x10, "I2CR"}, /* I2C Reset - Auto clear , */\ + { 0x30, "AMPE"}, /* Activate Amplifier , */\ + { 0x40, "DCA"}, /* Activate DC-to-DC converter , */\ + { 0x71, "INTP"}, /* Interrupt config , */\ + { 0xb0, "BYPOCP"}, /* Bypass OCP , */\ + { 0xc0, "TSTOCP"}, /* OCP testing control , */\ + { 0x120, "MANSCONF"}, /* I2C configured , */\ + { 0x140, "MANAOOSC"}, /* Internal osc off at PWDN , */\ + { 0x1d0, "MUTETO"}, /* Time out SB mute sequence , */\ + { 0x1e0, "OPENMTP"}, /* Control for FAIM protection , */\ + { 0x203, "AUDFS"}, /* Sample rate (fs) , */\ + { 0x240, "INPLEV"}, /* TDM output attenuation , */\ + { 0x255, "FRACTDEL"}, /* V/I Fractional delay , */\ + { 0x30f, "REV"}, /* Revision info , */\ + { 0x401, "REFCKEXT"}, /* PLL external ref clock , */\ + { 0x420, "REFCKSEL"}, /* PLL internal ref clock , */\ + { 0x5c0, "SSFAIME"}, /* Sub-system FAIM , */\ + { 0x802, "AMPOCRT"}, /* Amplifier on-off criteria for shutdown , */\ + { 0x1000, "VDDS"}, /* POR , */\ + { 0x1010, "DCOCPOK"}, /* DCDC OCP nmos (sticky register , clear on read) , */\ + { 0x1020, "OTDS"}, /* OTP alarm (sticky register , clear on read) , */\ + { 0x1030, "OCDS"}, /* OCP amplifier (sticky register , clear on read), */\ + { 0x1040, "UVDS"}, /* UVP alarm (sticky register , clear on read) , */\ + { 0x1050, "MANALARM"}, /* Alarm state , */\ + { 0x1060, "TDMERR"}, /* TDM error , */\ + { 0x1070, "NOCLK"}, /* Lost clock (sticky register , clear on read) , */\ + { 0x1100, "DCIL"}, /* DCDC current limiting , */\ + { 0x1110, "DCDCA"}, /* DCDC active (sticky register , clear on read) , */\ + { 0x1130, "DCHVBAT"}, /* DCDC level 1x , */\ + { 0x1140, "DCH114"}, /* DCDC level 1.14x , */\ + { 0x1150, "DCH107"}, /* DCDC level 1.07x , */\ + { 0x1160, "PLLS"}, /* PLL lock , */\ + { 0x1170, "CLKS"}, /* Clocks stable , */\ + { 0x1180, "TDMLUTER"}, /* TDM LUT error , */\ + { 0x1192, "TDMSTAT"}, /* TDM status bits , */\ + { 0x11c0, "MTPB"}, /* MTP busy , */\ + { 0x11d0, "SWS"}, /* Amplifier engage , */\ + { 0x11e0, "AMPS"}, /* Amplifier enable , */\ + { 0x11f0, "AREFS"}, /* References enable , */\ + { 0x1300, "OCPOAP"}, /* OCPOK pmos A , */\ + { 0x1310, "OCPOAN"}, /* OCPOK nmos A , */\ + { 0x1320, "OCPOBP"}, /* OCPOK pmos B , */\ + { 0x1330, "OCPOBN"}, /* OCPOK nmos B , */\ + { 0x1380, "OVDS"}, /* OVP alarm , */\ + { 0x1390, "CLIPS"}, /* Amplifier clipping , */\ + { 0x13a0, "ADCCR"}, /* Control ADC , */\ + { 0x13c0, "MANWAIT1"}, /* Wait HW I2C settings , */\ + { 0x13e0, "MANMUTE"}, /* Audio mute sequence , */\ + { 0x13f0, "MANOPER"}, /* Operating state , */\ + { 0x1420, "CLKOOR"}, /* External clock status , */\ + { 0x1433, "MANSTATE"}, /* Device manager status , */\ + { 0x1471, "DCMODE"}, /* DCDC mode status bits , */\ + { 0x1509, "BATS"}, /* Battery voltage (V) , */\ + { 0x1608, "TEMPS"}, /* IC Temperature (C) , */\ + { 0x1709, "VDDPS"}, /* IC VDDP voltage ( 1023*VDDP/13 V) , */\ + { 0x2040, "TDME"}, /* Enable interface , */\ + { 0x2050, "TDMMODE"}, /* Slave/master , */\ + { 0x2060, "TDMCLINV"}, /* Reception data to BCK clock , */\ + { 0x2073, "TDMFSLN"}, /* FS length (master mode only) , */\ + { 0x20b0, "TDMFSPOL"}, /* FS polarity , */\ + { 0x20c3, "TDMNBCK"}, /* N-BCK's in FS , */\ + { 0x2103, "TDMSLOTS"}, /* N-slots in Frame , */\ + { 0x2144, "TDMSLLN"}, /* N-bits in slot , */\ + { 0x2194, "TDMBRMG"}, /* N-bits remaining , */\ + { 0x21e0, "TDMDEL"}, /* data delay to FS , */\ + { 0x21f0, "TDMADJ"}, /* data adjustment , */\ + { 0x2201, "TDMOOMP"}, /* Received audio compression , */\ + { 0x2224, "TDMSSIZE"}, /* Sample size per slot , */\ + { 0x2271, "TDMTXDFO"}, /* Format unused bits , */\ + { 0x2291, "TDMTXUS0"}, /* Format unused slots DATAO , */\ + { 0x2300, "TDMSPKE"}, /* Control audio tdm channel in 0 (spkr + dcdc) , */\ + { 0x2310, "TDMDCE"}, /* Control audio tdm channel in 1 (dcdc) , */\ + { 0x2330, "TDMCSE"}, /* current sense vbat temperature and vddp feedback , */\ + { 0x2340, "TDMVSE"}, /* Voltage sense vbat temperature and vddp feedback , */\ + { 0x2603, "TDMSPKS"}, /* tdm slot for sink 0 (speaker + dcdc) , */\ + { 0x2643, "TDMDCS"}, /* tdm slot for sink 1 (dcdc) , */\ + { 0x26c3, "TDMCSS"}, /* Slot Position of current sense vbat temperature and vddp feedback, */\ + { 0x2703, "TDMVSS"}, /* Slot Position of Voltage sense vbat temperature and vddp feedback, */\ + { 0x4000, "ISTVDDS"}, /* Status POR , */\ + { 0x4010, "ISTBSTOC"}, /* Status DCDC OCP , */\ + { 0x4020, "ISTOTDS"}, /* Status OTP alarm , */\ + { 0x4030, "ISTOCPR"}, /* Status ocp alarm , */\ + { 0x4040, "ISTUVDS"}, /* Status UVP alarm , */\ + { 0x4050, "ISTMANALARM"}, /* Status nanager Alarm state , */\ + { 0x4060, "ISTTDMER"}, /* Status tdm error , */\ + { 0x4070, "ISTNOCLK"}, /* Status lost clock , */\ + { 0x4400, "ICLVDDS"}, /* Clear POR , */\ + { 0x4410, "ICLBSTOC"}, /* Clear DCDC OCP , */\ + { 0x4420, "ICLOTDS"}, /* Clear OTP alarm , */\ + { 0x4430, "ICLOCPR"}, /* Clear ocp alarm , */\ + { 0x4440, "ICLUVDS"}, /* Clear UVP alarm , */\ + { 0x4450, "ICLMANALARM"}, /* clear nanager Alarm state , */\ + { 0x4460, "ICLTDMER"}, /* Clear tdm error , */\ + { 0x4470, "ICLNOCLK"}, /* Clear lost clk , */\ + { 0x4800, "IEVDDS"}, /* Enable por , */\ + { 0x4810, "IEBSTOC"}, /* Enable DCDC OCP , */\ + { 0x4820, "IEOTDS"}, /* Enable OTP alarm , */\ + { 0x4830, "IEOCPR"}, /* Enable ocp alarm , */\ + { 0x4840, "IEUVDS"}, /* Enable UVP alarm , */\ + { 0x4850, "IEMANALARM"}, /* Enable nanager Alarm state , */\ + { 0x4860, "IETDMER"}, /* Enable tdm error , */\ + { 0x4870, "IENOCLK"}, /* Enable lost clk , */\ + { 0x4c00, "IPOVDDS"}, /* Polarity por , */\ + { 0x4c10, "IPOBSTOC"}, /* Polarity DCDC OCP , */\ + { 0x4c20, "IPOOTDS"}, /* Polarity OTP alarm , */\ + { 0x4c30, "IPOOCPR"}, /* Polarity ocp alarm , */\ + { 0x4c40, "IPOUVDS"}, /* Polarity UVP alarm , */\ + { 0x4c50, "IPOMANALARM"}, /* Polarity nanager Alarm state , */\ + { 0x4c60, "IPOTDMER"}, /* Polarity tdm error , */\ + { 0x4c70, "IPONOCLK"}, /* Polarity lost clk , */\ + { 0x5001, "BSSCR"}, /* Battery Safeguard attack time , */\ + { 0x5023, "BSST"}, /* Battery Safeguard threshold voltage level , */\ + { 0x5061, "BSSRL"}, /* Battery Safeguard maximum reduction , */\ + { 0x5080, "VBATFLTL"}, /* vbat filter limit , */\ + { 0x50e0, "BSSR"}, /* Battery voltage read out , */\ + { 0x50f0, "BSSBY"}, /* Bypass battery safeguard , */\ + { 0x5100, "BSSS"}, /* Vbat prot steepness , */\ + { 0x5150, "HPFBYP"}, /* Bypass HPF , */\ + { 0x5170, "DPSA"}, /* Enable DPSA , */\ + { 0x5222, "CLIPCTRL"}, /* Clip control setting , */\ + { 0x5257, "AMPGAIN"}, /* Amplifier gain , */\ + { 0x52d0, "SLOPEE"}, /* Enables slope control , */\ + { 0x52e0, "SLOPESET"}, /* Slope speed setting (bin. coded) , */\ + { 0x6123, "TDMDCG"}, /* Second channel gain in case of stereo using a single coil. (Total gain depending on INPLEV). (In case of mono OR stereo using 2 separate DCDC channel 1 should be disabled using TDMDCE), */\ + { 0x6163, "TDMSPKG"}, /* Total gain depending on INPLEV setting (channel 0), */\ + { 0x62e1, "LNMODE"}, /* ctrl select mode , */\ + { 0x64e1, "LPM1MODE"}, /* low power mode control , */\ + { 0x6802, "TDMSRCMAP"}, /* tdm source mapping , */\ + { 0x6831, "TDMSRCAS"}, /* Sensed value A , */\ + { 0x6851, "TDMSRCBS"}, /* Sensed value B , */\ + { 0x6871, "TDMSRCACLIP"}, /* clip information (analog /digital) for source0 , */\ + { 0x6891, "TDMSRCBCLIP"}, /* clip information (analog /digital) for source1 , */\ + { 0x6e10, "LP1"}, /* low power mode 1 detection , */\ + { 0x6e20, "LA"}, /* low amplitude detection , */\ + { 0x6e30, "VDDPH"}, /* vddp greater than vbat , */\ + { 0x6f02, "DELCURCOMP"}, /* delay to allign compensation signal with current sense signal, */\ + { 0x6f40, "SIGCURCOMP"}, /* polarity of compensation for current sense , */\ + { 0x6f50, "ENCURCOMP"}, /* enable current sense compensation , */\ + { 0x6f72, "LVLCLPPWM"}, /* set the amount of pwm pulse that may be skipped before clip-flag is triggered, */\ + { 0x7033, "DCMCC"}, /* Max coil current , */\ + { 0x7071, "DCCV"}, /* Slope compensation current, represents LxF (inductance x frequency) value , */\ + { 0x7090, "DCIE"}, /* Adaptive boost mode , */\ + { 0x70a0, "DCSR"}, /* Soft ramp up/down , */\ + { 0x70e0, "DCDIS"}, /* DCDC on/off , */\ + { 0x70f0, "DCPWM"}, /* DCDC PWM only mode , */\ + { 0x7430, "DCTRACK"}, /* Boost algorithm selection, effective only when boost_intelligent is set to 1, */\ + { 0x7444, "DCTRIP"}, /* 1st Adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7494, "DCHOLD"}, /* Hold time for DCDC booster, effective only when boost_intelligent is set to 1, */\ + { 0x7534, "DCTRIP2"}, /* 2nd Adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7584, "DCTRIPT"}, /* Track Adaptive boost trip levels, effective only when boost_intelligent is set to 1, */\ + { 0x75f0, "DCTRIPHYSTE"}, /* Enable hysteresis on booster trip levels , */\ + { 0x7635, "DCVOF"}, /* First boost voltage level , */\ + { 0x7695, "DCVOS"}, /* Second boost voltage level , */\ + { 0xa107, "MTPK"}, /* MTP KEY2 register , */\ + { 0xa200, "KEY1LOCKED"}, /* Indicates KEY1 is locked , */\ + { 0xa210, "KEY2LOCKED"}, /* Indicates KEY2 is locked , */\ + { 0xa360, "CIMTP"}, /* Start copying data from I2C mtp registers to mtp , */\ + { 0xa50f, "MTPRDMSB"}, /* MSB word of MTP manual read data , */\ + { 0xa60f, "MTPRDLSB"}, /* LSB word of MTP manual read data , */\ + { 0xb108, "EXTTS"}, /* External temperature (C) , */\ + { 0xb190, "TROS"}, /* Select temp Speaker calibration , */\ + { 0xee0f, "SWPROFIL"}, /* Software profile data , */\ + { 0xef0f, "SWVSTEP"}, /* Software vstep information , */\ + { 0xf000, "MTPOTC"}, /* Calibration schedule , */\ + { 0xf010, "MTPEX"}, /* Calibration Ron executed , */\ + { 0xf020, "DCMCCAPI"}, /* Calibration current limit DCDC , */\ + { 0xf030, "DCMCCSB"}, /* Sign bit for delta calibration current limit DCDC , */\ + { 0xf042, "USERDEF"}, /* Calibration delta current limit DCDC , */\ + { 0xf078, "CUSTINFO"}, /* Reserved space for allowing customer to store speaker information, */\ + { 0xf50f, "R25C"}, /* Ron resistance of speaker coil , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA9874_BITNAMETABLE static tfaBfName_t Tfa9874BitNames[]= {\ + { 0x0, "powerdown"}, /* Powerdown selection , */\ + { 0x10, "reset"}, /* I2C Reset - Auto clear , */\ + { 0x30, "enbl_amplifier"}, /* Activate Amplifier , */\ + { 0x40, "enbl_boost"}, /* Activate DC-to-DC converter , */\ + { 0x71, "int_pad_io"}, /* Interrupt config , */\ + { 0xb0, "bypass_ocp"}, /* Bypass OCP , */\ + { 0xc0, "test_ocp"}, /* OCP testing control , */\ + { 0x120, "src_set_configured"}, /* I2C configured , */\ + { 0x140, "enbl_osc1m_auto_off"}, /* Internal osc off at PWDN , */\ + { 0x1d0, "disable_mute_time_out"}, /* Time out SB mute sequence , */\ + { 0x1e0, "unprotect_faim"}, /* Control for FAIM protection , */\ + { 0x203, "audio_fs"}, /* Sample rate (fs) , */\ + { 0x240, "input_level"}, /* TDM output attenuation , */\ + { 0x255, "cs_frac_delay"}, /* V/I Fractional delay , */\ + { 0x2d0, "sel_hysteresis"}, /* Select hysteresis for clock range detector , */\ + { 0x30f, "device_rev"}, /* Revision info , */\ + { 0x401, "pll_clkin_sel"}, /* PLL external ref clock , */\ + { 0x420, "pll_clkin_sel_osc"}, /* PLL internal ref clock , */\ + { 0x5c0, "enbl_faim_ss"}, /* Sub-system FAIM , */\ + { 0x802, "ctrl_on2off_criterion"}, /* Amplifier on-off criteria for shutdown , */\ + { 0xe07, "ctrl_digtoana"}, /* Spare control from digital to analog , */\ + { 0xf0f, "hidden_code"}, /* 5A6Bh, 23147d to access registers (default for engineering), */\ + { 0x1000, "flag_por"}, /* POR , */\ + { 0x1010, "flag_bst_ocpok"}, /* DCDC OCP nmos (sticky register , clear on read) , */\ + { 0x1020, "flag_otpok"}, /* OTP alarm (sticky register , clear on read) , */\ + { 0x1030, "flag_ocp_alarm"}, /* OCP amplifier (sticky register , clear on read), */\ + { 0x1040, "flag_uvpok"}, /* UVP alarm (sticky register , clear on read) , */\ + { 0x1050, "flag_man_alarm_state"}, /* Alarm state , */\ + { 0x1060, "flag_tdm_error"}, /* TDM error , */\ + { 0x1070, "flag_lost_clk"}, /* Lost clock (sticky register , clear on read) , */\ + { 0x1100, "flag_bst_bstcur"}, /* DCDC current limiting , */\ + { 0x1110, "flag_bst_hiz"}, /* DCDC active (sticky register , clear on read) , */\ + { 0x1120, "flag_bst_peakcur"}, /* Indicates current is max in DC-to-DC converter , */\ + { 0x1130, "flag_bst_voutcomp"}, /* DCDC level 1x , */\ + { 0x1140, "flag_bst_voutcomp86"}, /* DCDC level 1.14x , */\ + { 0x1150, "flag_bst_voutcomp93"}, /* DCDC level 1.07x , */\ + { 0x1160, "flag_pll_lock"}, /* PLL lock , */\ + { 0x1170, "flag_clocks_stable"}, /* Clocks stable , */\ + { 0x1180, "flag_tdm_lut_error"}, /* TDM LUT error , */\ + { 0x1192, "flag_tdm_status"}, /* TDM status bits , */\ + { 0x11c0, "flag_mtp_busy"}, /* MTP busy , */\ + { 0x11d0, "flag_engage"}, /* Amplifier engage , */\ + { 0x11e0, "flag_enbl_amp"}, /* Amplifier enable , */\ + { 0x11f0, "flag_enbl_ref"}, /* References enable , */\ + { 0x1300, "flag_ocpokap"}, /* OCPOK pmos A , */\ + { 0x1310, "flag_ocpokan"}, /* OCPOK nmos A , */\ + { 0x1320, "flag_ocpokbp"}, /* OCPOK pmos B , */\ + { 0x1330, "flag_ocpokbn"}, /* OCPOK nmos B , */\ + { 0x1380, "flag_ovpok"}, /* OVP alarm , */\ + { 0x1390, "flag_clip"}, /* Amplifier clipping , */\ + { 0x13a0, "flag_adc10_ready"}, /* Control ADC , */\ + { 0x13c0, "flag_man_wait_src_settings"}, /* Wait HW I2C settings , */\ + { 0x13e0, "flag_man_start_mute_audio"}, /* Audio mute sequence , */\ + { 0x13f0, "flag_man_operating_state"}, /* Operating state , */\ + { 0x1420, "flag_clk_out_of_range"}, /* External clock status , */\ + { 0x1433, "man_state"}, /* Device manager status , */\ + { 0x1471, "status_bst_mode"}, /* DCDC mode status bits , */\ + { 0x1509, "bat_adc"}, /* Battery voltage (V) , */\ + { 0x1608, "temp_adc"}, /* IC Temperature (C) , */\ + { 0x1709, "vddp_adc"}, /* IC VDDP voltage ( 1023*VDDP/13 V) , */\ + { 0x2040, "tdm_enable"}, /* Enable interface , */\ + { 0x2050, "tdm_mode"}, /* Slave/master , */\ + { 0x2060, "tdm_clk_inversion"}, /* Reception data to BCK clock , */\ + { 0x2073, "tdm_fs_ws_length"}, /* FS length (master mode only) , */\ + { 0x20b0, "tdm_fs_ws_polarity"}, /* FS polarity , */\ + { 0x20c3, "tdm_nbck"}, /* N-BCK's in FS , */\ + { 0x2103, "tdm_nb_of_slots"}, /* N-slots in Frame , */\ + { 0x2144, "tdm_slot_length"}, /* N-bits in slot , */\ + { 0x2194, "tdm_bits_remaining"}, /* N-bits remaining , */\ + { 0x21e0, "tdm_data_delay"}, /* data delay to FS , */\ + { 0x21f0, "tdm_data_adjustment"}, /* data adjustment , */\ + { 0x2201, "tdm_audio_sample_compression"}, /* Received audio compression , */\ + { 0x2224, "tdm_sample_size"}, /* Sample size per slot , */\ + { 0x2271, "tdm_txdata_format"}, /* Format unused bits , */\ + { 0x2291, "tdm_txdata_format_unused_slot_sd0"}, /* Format unused slots DATAO , */\ + { 0x2300, "tdm_sink0_enable"}, /* Control audio tdm channel in 0 (spkr + dcdc) , */\ + { 0x2310, "tdm_sink1_enable"}, /* Control audio tdm channel in 1 (dcdc) , */\ + { 0x2330, "tdm_source0_enable"}, /* current sense vbat temperature and vddp feedback , */\ + { 0x2340, "tdm_source1_enable"}, /* Voltage sense vbat temperature and vddp feedback , */\ + { 0x2603, "tdm_sink0_slot"}, /* tdm slot for sink 0 (speaker + dcdc) , */\ + { 0x2643, "tdm_sink1_slot"}, /* tdm slot for sink 1 (dcdc) , */\ + { 0x26c3, "tdm_source0_slot"}, /* Slot Position of current sense vbat temperature and vddp feedback, */\ + { 0x2703, "tdm_source1_slot"}, /* Slot Position of Voltage sense vbat temperature and vddp feedback, */\ + { 0x4000, "int_out_flag_por"}, /* Status POR , */\ + { 0x4010, "int_out_flag_bst_ocpok"}, /* Status DCDC OCP , */\ + { 0x4020, "int_out_flag_otpok"}, /* Status OTP alarm , */\ + { 0x4030, "int_out_flag_ocp_alarm"}, /* Status ocp alarm , */\ + { 0x4040, "int_out_flag_uvpok"}, /* Status UVP alarm , */\ + { 0x4050, "int_out_flag_man_alarm_state"}, /* Status nanager Alarm state , */\ + { 0x4060, "int_out_flag_tdm_error"}, /* Status tdm error , */\ + { 0x4070, "int_out_flag_lost_clk"}, /* Status lost clock , */\ + { 0x4400, "int_in_flag_por"}, /* Clear POR , */\ + { 0x4410, "int_in_flag_bst_ocpok"}, /* Clear DCDC OCP , */\ + { 0x4420, "int_in_flag_otpok"}, /* Clear OTP alarm , */\ + { 0x4430, "int_in_flag_ocp_alarm"}, /* Clear ocp alarm , */\ + { 0x4440, "int_in_flag_uvpok"}, /* Clear UVP alarm , */\ + { 0x4450, "int_in_flag_man_alarm_state"}, /* clear nanager Alarm state , */\ + { 0x4460, "int_in_flag_tdm_error"}, /* Clear tdm error , */\ + { 0x4470, "int_in_flag_lost_clk"}, /* Clear lost clk , */\ + { 0x4800, "int_enable_flag_por"}, /* Enable por , */\ + { 0x4810, "int_enable_flag_bst_ocpok"}, /* Enable DCDC OCP , */\ + { 0x4820, "int_enable_flag_otpok"}, /* Enable OTP alarm , */\ + { 0x4830, "int_enable_flag_ocp_alarm"}, /* Enable ocp alarm , */\ + { 0x4840, "int_enable_flag_uvpok"}, /* Enable UVP alarm , */\ + { 0x4850, "int_enable_flag_man_alarm_state"}, /* Enable nanager Alarm state , */\ + { 0x4860, "int_enable_flag_tdm_error"}, /* Enable tdm error , */\ + { 0x4870, "int_enable_flag_lost_clk"}, /* Enable lost clk , */\ + { 0x4c00, "int_polarity_flag_por"}, /* Polarity por , */\ + { 0x4c10, "int_polarity_flag_bst_ocpok"}, /* Polarity DCDC OCP , */\ + { 0x4c20, "int_polarity_flag_otpok"}, /* Polarity OTP alarm , */\ + { 0x4c30, "int_polarity_flag_ocp_alarm"}, /* Polarity ocp alarm , */\ + { 0x4c40, "int_polarity_flag_uvpok"}, /* Polarity UVP alarm , */\ + { 0x4c50, "int_polarity_flag_man_alarm_state"}, /* Polarity nanager Alarm state , */\ + { 0x4c60, "int_polarity_flag_tdm_error"}, /* Polarity tdm error , */\ + { 0x4c70, "int_polarity_flag_lost_clk"}, /* Polarity lost clk , */\ + { 0x5001, "vbat_prot_attack_time"}, /* Battery Safeguard attack time , */\ + { 0x5023, "vbat_prot_thlevel"}, /* Battery Safeguard threshold voltage level , */\ + { 0x5061, "vbat_prot_max_reduct"}, /* Battery Safeguard maximum reduction , */\ + { 0x5080, "vbat_flt_limit"}, /* vbat filter limit , */\ + { 0x50d0, "rst_min_vbat"}, /* Reset clipper - Auto clear , */\ + { 0x50e0, "sel_vbat"}, /* Battery voltage read out , */\ + { 0x50f0, "bypass_clipper"}, /* Bypass battery safeguard , */\ + { 0x5100, "batsense_steepness"}, /* Vbat prot steepness , */\ + { 0x5150, "bypass_hp"}, /* Bypass HPF , */\ + { 0x5170, "enbl_dpsa"}, /* Enable DPSA , */\ + { 0x5222, "ctrl_cc"}, /* Clip control setting , */\ + { 0x5257, "gain"}, /* Amplifier gain , */\ + { 0x52d0, "ctrl_slopectrl"}, /* Enables slope control , */\ + { 0x52e0, "ctrl_slope"}, /* Slope speed setting (bin. coded) , */\ + { 0x5301, "dpsa_level"}, /* DPSA threshold levels , */\ + { 0x5321, "dpsa_release"}, /* DPSA Release time , */\ + { 0x5340, "clipfast"}, /* Clock selection for HW clipper for Battery Safeguard, */\ + { 0x5350, "bypass_lp"}, /* Bypass the low power filter inside temperature sensor, */\ + { 0x5400, "first_order_mode"}, /* Overrule to 1st order mode of control stage when clipping, */\ + { 0x5410, "bypass_ctrlloop"}, /* Switch amplifier into open loop configuration , */\ + { 0x5430, "icomp_engage"}, /* Engage of icomp , */\ + { 0x5440, "ctrl_kickback"}, /* Prevent double pulses of output stage , */\ + { 0x5450, "icomp_engage_overrule"}, /* To overrule the functional icomp_engage signal during validation, */\ + { 0x5503, "ctrl_dem"}, /* Enable DEM icomp and DEM one bit dac , */\ + { 0x5543, "ctrl_dem_mismatch"}, /* Enable DEM icomp mismatch for testing , */\ + { 0x5582, "dpsa_drive"}, /* Drive setting (bin. coded) , */\ + { 0x5690, "sel_pwm_delay_src"}, /* Control for selection for PWM delay line source , */\ + { 0x56a1, "enbl_odd_up_even_down"}, /* Control for PWM reference sawtooth generartion , */\ + { 0x570a, "enbl_amp"}, /* Switch on the class-D power sections, each part of the analog sections can be switched on/off individually, */\ + { 0x57b0, "enbl_engage"}, /* Enables/engage power stage and control loop , */\ + { 0x57c0, "enbl_engage_pst"}, /* Enables/engage power stage and control loop , */\ + { 0x5810, "hard_mute"}, /* Hard mute - PWM , */\ + { 0x5820, "pwm_shape"}, /* PWM shape , */\ + { 0x5844, "pwm_delay"}, /* PWM delay bits to set the delay, clockd is 1/(k*2048*fs), */\ + { 0x5890, "reclock_pwm"}, /* Reclock the pwm signal inside analog , */\ + { 0x58a0, "reclock_voltsense"}, /* Reclock the voltage sense pwm signal , */\ + { 0x58c0, "enbl_pwm_phase_shift"}, /* Control for pwm phase shift , */\ + { 0x6123, "ctrl_attl"}, /* Second channel gain in case of stereo using a single coil. (Total gain depending on INPLEV). (In case of mono OR stereo using 2 separate DCDC channel 1 should be disabled using TDMDCE), */\ + { 0x6163, "ctrl_attr"}, /* Total gain depending on INPLEV setting (channel 0), */\ + { 0x6265, "zero_lvl"}, /* low noise gain switch zero trigger level , */\ + { 0x62c1, "ctrl_fb_resistor"}, /* Select amplifier feedback resistor connection , */\ + { 0x62e1, "lownoisegain_mode"}, /* ctrl select mode , */\ + { 0x6305, "threshold_lvl"}, /* low noise gain switch trigger level , */\ + { 0x6365, "hold_time"}, /* ctrl hold time before low audio is reckoned to be low audio, */\ + { 0x6405, "lpm1_cal_offset"}, /* low power mode1 detector ctrl cal_offset from gain module , */\ + { 0x6465, "lpm1_zero_lvl"}, /* low power mode1 zero crossing detection level , */\ + { 0x64e1, "lpm1_mode"}, /* low power mode control , */\ + { 0x6505, "lpm1_threshold_lvl"}, /* low power mode1 amplitude trigger level , */\ + { 0x6565, "lpm1_hold_time"}, /* low power mode1 detector ctrl hold time before low audio is reckoned to be low audio, */\ + { 0x65c0, "disable_low_power_mode"}, /* low power mode1 detector control , */\ + { 0x6600, "dcdc_pfm20khz_limit"}, /* DCDC in PFM mode pwm mode is activated each 50us to force a pwm pulse, */\ + { 0x6611, "dcdc_ctrl_maxzercnt"}, /* DCDC. Number of zero current flags to count before going to pfm mode, */\ + { 0x6656, "dcdc_vbat_delta_detect"}, /* Threshold before booster is reacting on a delta Vbat (in PFM mode) by temporarily switching to PWM mode, */\ + { 0x66c0, "dcdc_ignore_vbat"}, /* Ignore an increase on Vbat , */\ + { 0x6700, "enbl_minion"}, /* Enables minion (small) power stage , */\ + { 0x6713, "vth_vddpvbat"}, /* select vddp-vbat thres signal , */\ + { 0x6750, "lpen_vddpvbat"}, /* select vddp-vbat filtred vs unfiltered compare , */\ + { 0x6761, "ctrl_rfb"}, /* Feedback resistor selection - I2C direct mode , */\ + { 0x6802, "tdm_source_mapping"}, /* tdm source mapping , */\ + { 0x6831, "tdm_sourcea_frame_sel"}, /* Sensed value A , */\ + { 0x6851, "tdm_sourceb_frame_sel"}, /* Sensed value B , */\ + { 0x6871, "tdm_source0_clip_sel"}, /* clip information (analog /digital) for source0 , */\ + { 0x6891, "tdm_source1_clip_sel"}, /* clip information (analog /digital) for source1 , */\ + { 0x6a02, "rst_min_vbat_delay"}, /* rst_min_vbat delay (nb fs) , */\ + { 0x6b00, "disable_auto_engage"}, /* disable auto engange , */\ + { 0x6b10, "disable_engage"}, /* disable engange , */\ + { 0x6c02, "ns_hp2ln_criterion"}, /* 0..7 zeroes at ns as threshold to swap from high_power to low_noise, */\ + { 0x6c32, "ns_ln2hp_criterion"}, /* 0..7 zeroes at ns as threshold to swap from low_noise to high_power, */\ + { 0x6c69, "spare_out"}, /* spare_out , */\ + { 0x6d0f, "spare_in"}, /* spare_in , */\ + { 0x6e10, "flag_lp_detect_mode1"}, /* low power mode 1 detection , */\ + { 0x6e20, "flag_low_amplitude"}, /* low amplitude detection , */\ + { 0x6e30, "flag_vddp_gt_vbat"}, /* vddp greater than vbat , */\ + { 0x6f02, "cursense_comp_delay"}, /* delay to allign compensation signal with current sense signal, */\ + { 0x6f40, "cursense_comp_sign"}, /* polarity of compensation for current sense , */\ + { 0x6f50, "enbl_cursense_comp"}, /* enable current sense compensation , */\ + { 0x6f72, "pwms_clip_lvl"}, /* set the amount of pwm pulse that may be skipped before clip-flag is triggered, */\ + { 0x7033, "boost_cur"}, /* Max coil current , */\ + { 0x7071, "bst_slpcmplvl"}, /* Slope compensation current, represents LxF (inductance x frequency) value , */\ + { 0x7090, "boost_intel"}, /* Adaptive boost mode , */\ + { 0x70a0, "boost_speed"}, /* Soft ramp up/down , */\ + { 0x70e0, "dcdcoff_mode"}, /* DCDC on/off , */\ + { 0x70f0, "dcdc_pwmonly"}, /* DCDC PWM only mode , */\ + { 0x7104, "bst_drive"}, /* Binary coded drive setting for boost converter power stage, */\ + { 0x7151, "bst_scalecur"}, /* For testing direct control scale current , */\ + { 0x7174, "bst_slopecur"}, /* For testing direct control slope current , */\ + { 0x71c1, "bst_slope"}, /* Boost slope speed , */\ + { 0x71e0, "bst_bypass_bstcur"}, /* Bypass control for boost current settings , */\ + { 0x71f0, "bst_bypass_bstfoldback"}, /* Bypass control for boost foldback , */\ + { 0x7200, "enbl_bst_engage"}, /* Enable power stage dcdc controller , */\ + { 0x7210, "enbl_bst_hizcom"}, /* Enable hiz comparator , */\ + { 0x7220, "enbl_bst_peak2avg"}, /* Enable boost peak2avg functionality , */\ + { 0x7230, "enbl_bst_peakcur"}, /* Enable peak current , */\ + { 0x7240, "enbl_bst_power"}, /* Enable line of the powerstage , */\ + { 0x7250, "enbl_bst_slopecur"}, /* Enable bit of max-current dac , */\ + { 0x7260, "enbl_bst_voutcomp"}, /* Enable vout comparators , */\ + { 0x7270, "enbl_bst_voutcomp86"}, /* Enable vout-86 comparators , */\ + { 0x7280, "enbl_bst_voutcomp93"}, /* Enable vout-93 comparators , */\ + { 0x7290, "enbl_bst_windac"}, /* Enable window dac , */\ + { 0x72a5, "bst_windac"}, /* for testing direct control windac , */\ + { 0x7300, "boost_alg"}, /* Control for boost adaptive loop gain , */\ + { 0x7311, "boost_loopgain"}, /* DCDC boost loopgain setting , */\ + { 0x7331, "bst_freq"}, /* DCDC boost frequency control , */\ + { 0x7360, "bst_use_new_zercur_detect"}, /* Enable new zero current detection for boost control, */\ + { 0x7430, "boost_track"}, /* Boost algorithm selection, effective only when boost_intelligent is set to 1, */\ + { 0x7444, "boost_trip_lvl_1st"}, /* 1st Adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7494, "boost_hold_time"}, /* Hold time for DCDC booster, effective only when boost_intelligent is set to 1, */\ + { 0x74e0, "sel_dcdc_envelope_8fs"}, /* Selection of data for adaptive boost algorithm, effective only when boost_intelligent is set to 1, */\ + { 0x74f0, "ignore_flag_voutcomp86"}, /* Determines the maximum PWM frequency be the most efficient in relation to the Booster inductor value, */\ + { 0x7534, "boost_trip_lvl_2nd"}, /* 2nd Adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7584, "boost_trip_lvl_track"}, /* Track Adaptive boost trip levels, effective only when boost_intelligent is set to 1, */\ + { 0x75f0, "enbl_trip_hyst"}, /* Enable hysteresis on booster trip levels , */\ + { 0x7635, "frst_boost_voltage"}, /* First boost voltage level , */\ + { 0x7695, "scnd_boost_voltage"}, /* Second boost voltage level , */\ + { 0x8001, "sel_clk_cs"}, /* Current sense clock duty cycle control , */\ + { 0x8021, "micadc_speed"}, /* Current sense clock for MiCADC selection - 32/44.1/48 KHz Fs band only, */\ + { 0x8050, "cs_gain_control"}, /* Current sense gain control , */\ + { 0x8060, "cs_bypass_gc"}, /* Bypasses the CS gain correction , */\ + { 0x8087, "cs_gain"}, /* Current sense gain , */\ + { 0x8210, "invertpwm"}, /* Current sense common mode feedback pwm invert control, */\ + { 0x8305, "cs_ktemp"}, /* Current sense temperature compensation trimming (1 - VALUE*TEMP)*signal, */\ + { 0x8364, "cs_ktemp2"}, /* Second order temperature compensation coefficient , */\ + { 0x8400, "cs_adc_bsoinv"}, /* Bitstream inversion for current sense ADC , */\ + { 0x8421, "cs_adc_hifreq"}, /* Frequency mode current sense ADC , */\ + { 0x8440, "cs_adc_nortz"}, /* Return to zero for current sense ADC , */\ + { 0x8453, "cs_adc_offset"}, /* Micadc ADC offset setting , */\ + { 0x8490, "cs_adc_slowdel"}, /* Select delay for current sense ADC (internal decision circuitry), */\ + { 0x84a4, "cs_adc_gain"}, /* Gain setting for current sense ADC (two's complement), */\ + { 0x8500, "cs_resonator_enable"}, /* Enable for resonator to improve SRN , */\ + { 0x8510, "cs_classd_tran_skip"}, /* Skip current sense connection during a classD amplifier transition, */\ + { 0x8530, "cs_inn_short"}, /* Short current sense negative to common mode , */\ + { 0x8540, "cs_inp_short"}, /* Short current sense positive to common mode , */\ + { 0x8550, "cs_ldo_bypass"}, /* Bypass current sense LDO , */\ + { 0x8560, "cs_ldo_pulldown"}, /* Pull down current sense LDO, only valid if left_enbl_cs_ldo is high, */\ + { 0x8574, "cs_ldo_voset"}, /* Current sense LDO voltage level setting (two's complement), */\ + { 0x8700, "enbl_cs_adc"}, /* Enable current sense ADC , */\ + { 0x8710, "enbl_cs_inn1"}, /* Enable connection of current sense negative1 , */\ + { 0x8720, "enbl_cs_inn2"}, /* Enable connection of current sense negative2 , */\ + { 0x8730, "enbl_cs_inp1"}, /* Enable connection of current sense positive1 , */\ + { 0x8740, "enbl_cs_inp2"}, /* Enable connection of current sense positive2 , */\ + { 0x8750, "enbl_cs_ldo"}, /* Enable current sense LDO , */\ + { 0x8780, "enbl_cs_vbatldo"}, /* Enable of current sense LDO , */\ + { 0x8790, "enbl_dc_filter"}, /* Control for enabling the DC blocking filter for voltage and current sense, */\ + { 0x8801, "volsense_pwm_sel"}, /* Voltage sense source selection control , */\ + { 0x8850, "vs_gain_control"}, /* Voltage sense gain control , */\ + { 0x8860, "vs_bypass_gc"}, /* Bypasses the VS gain correction , */\ + { 0x8870, "vs_igen_supply"}, /* Switch internal supply of current generator , */\ + { 0x8887, "vs_gain"}, /* voltage sense gain , */\ + { 0x8c00, "vs_adc_bsoinv"}, /* Bitstream inversion for voltage sense ADC , */\ + { 0x8c40, "vs_adc_nortz"}, /* Return to zero for voltage sense ADC , */\ + { 0x8c90, "vs_adc_slowdel"}, /* Select delay for voltage sense ADC (internal decision circuitry), */\ + { 0x8d10, "vs_classd_tran_skip"}, /* Skip voltage sense connection during a classD amplifier transition, */\ + { 0x8d30, "vs_inn_short"}, /* Short voltage sense negative to common mode , */\ + { 0x8d40, "vs_inp_short"}, /* Short voltage sense positive to common mode , */\ + { 0x8d50, "vs_ldo_bypass"}, /* Bypass voltage sense LDO , */\ + { 0x8d60, "vs_ldo_pulldown"}, /* Pull down voltage sense LDO, only valid if left_enbl_cs_ldo is high, */\ + { 0x8d74, "vs_ldo_voset"}, /* Voltage sense LDO voltage level setting (two's complement), */\ + { 0x8f00, "enbl_vs_adc"}, /* Enable voltage sense ADC (Direct Control only only others done by manager), */\ + { 0x8f10, "enbl_vs_inn1"}, /* Enable connection of voltage sense negative1 , */\ + { 0x8f20, "enbl_vs_inn2"}, /* Enable connection of voltage sense negative2 , */\ + { 0x8f30, "enbl_vs_inp1"}, /* Enable connection of voltage sense positive1 , */\ + { 0x8f40, "enbl_vs_inp2"}, /* Enable connection of voltage sense positive2 , */\ + { 0x8f50, "enbl_vs_ldo"}, /* Enable voltage sense LDO (Direct Control only only others done by manager), */\ + { 0x8f80, "enbl_vs_vbatldo"}, /* Enable of voltage sense LDO (Direct Control only others done by manager), */\ + { 0xa007, "mtpkey1"}, /* 5Ah, 90d To access KEY1_Protected registers (Default for engineering), */\ + { 0xa107, "mtpkey2"}, /* MTP KEY2 register , */\ + { 0xa200, "key01_locked"}, /* Indicates KEY1 is locked , */\ + { 0xa210, "key02_locked"}, /* Indicates KEY2 is locked , */\ + { 0xa302, "mtp_man_address_in"}, /* MTP address from I2C register for read/writing mtp in manual single word mode, */\ + { 0xa330, "man_copy_mtp_to_iic"}, /* Start copying single word from mtp to I2C mtp register, */\ + { 0xa340, "man_copy_iic_to_mtp"}, /* Start copying single word from I2C mtp register to mtp, */\ + { 0xa350, "auto_copy_mtp_to_iic"}, /* Start copying all the data from mtp to I2C mtp registers, */\ + { 0xa360, "auto_copy_iic_to_mtp"}, /* Start copying data from I2C mtp registers to mtp , */\ + { 0xa400, "faim_set_clkws"}, /* Sets the faim controller clock wait state register, */\ + { 0xa410, "faim_sel_evenrows"}, /* All even rows of the faim are selected, active high, */\ + { 0xa420, "faim_sel_oddrows"}, /* All odd rows of the faim are selected, all rows in combination with sel_evenrows, */\ + { 0xa430, "faim_program_only"}, /* Skip the erase access at wr_faim command (write-program-marginread), */\ + { 0xa440, "faim_erase_only"}, /* Skip the program access at wr_faim command (write-erase-marginread), */\ + { 0xa50f, "mtp_man_data_out_msb"}, /* MSB word of MTP manual read data , */\ + { 0xa60f, "mtp_man_data_out_lsb"}, /* LSB word of MTP manual read data , */\ + { 0xa70f, "mtp_man_data_in_msb"}, /* MSB word of write data for MTP manual write , */\ + { 0xa80f, "mtp_man_data_in_lsb"}, /* LSB word of write data for MTP manual write , */\ + { 0xb010, "bypass_ocpcounter"}, /* Bypass OCP Counter , */\ + { 0xb020, "bypass_glitchfilter"}, /* Bypass glitch filter , */\ + { 0xb030, "bypass_ovp"}, /* Bypass OVP , */\ + { 0xb040, "bypass_uvp"}, /* Bypass UVP , */\ + { 0xb050, "bypass_otp"}, /* Bypass OTP , */\ + { 0xb060, "bypass_lost_clk"}, /* Bypass lost clock detector , */\ + { 0xb070, "ctrl_vpalarm"}, /* vpalarm (uvp ovp handling) , */\ + { 0xb087, "ocp_threshold"}, /* OCP threshold level , */\ + { 0xb108, "ext_temp"}, /* External temperature (C) , */\ + { 0xb190, "ext_temp_sel"}, /* Select temp Speaker calibration , */\ + { 0xc000, "use_direct_ctrls"}, /* Direct control to overrule several functions for testing, */\ + { 0xc010, "rst_datapath"}, /* Direct control for datapath reset , */\ + { 0xc020, "rst_cgu"}, /* Direct control for cgu reset , */\ + { 0xc038, "enbl_ref"}, /* Switch on the analog references, each part of the references can be switched on/off individually, */\ + { 0xc0c0, "use_direct_vs_ctrls"}, /* voltage sense Direct control to overrule several functions for testing, */\ + { 0xc0d0, "enbl_ringo"}, /* Enable the ring oscillator for test purpose , */\ + { 0xc0e0, "use_direct_clk_ctrl"}, /* Direct clock control to overrule several functions for testing, */\ + { 0xc0f0, "use_direct_pll_ctrl"}, /* Direct PLL control to overrule several functions for testing, */\ + { 0xc100, "enbl_tsense"}, /* Temperature sensor enable control - I2C direct mode, */\ + { 0xc110, "tsense_hibias"}, /* Bit to set the biasing in temp sensor to high , */\ + { 0xc120, "enbl_flag_vbg"}, /* Enable flagging of bandgap out of control , */\ + { 0xc20f, "abist_offset"}, /* Offset control for ABIST testing (two's complement), */\ + { 0xc300, "bypasslatch"}, /* Bypass latch , */\ + { 0xc311, "sourcea"}, /* Set OUTA to , */\ + { 0xc331, "sourceb"}, /* Set OUTB to , */\ + { 0xc350, "inverta"}, /* Invert pwma test signal , */\ + { 0xc360, "invertb"}, /* Invert pwmb test signal , */\ + { 0xc374, "pulselength"}, /* Pulse length setting test input for amplifier (clock d - k*2048*fs), */\ + { 0xc3c0, "tdm_enable_loopback"}, /* TDM loopback test , */\ + { 0xc400, "bst_bypasslatch"}, /* Bypass latch in boost converter , */\ + { 0xc411, "bst_source"}, /* Sets the source of the pwmbst output to boost converter input for testing, */\ + { 0xc430, "bst_invertb"}, /* Invert pwmbst test signal , */\ + { 0xc444, "bst_pulselength"}, /* Pulse length setting test input for boost converter , */\ + { 0xc490, "test_bst_ctrlsthv"}, /* Test mode for boost control stage , */\ + { 0xc4a0, "test_bst_iddq"}, /* IDDQ testing in power stage of boost converter , */\ + { 0xc4b0, "test_bst_rdson"}, /* RDSON testing - boost power stage , */\ + { 0xc4c0, "test_bst_cvi"}, /* CVI testing - boost power stage , */\ + { 0xc4d0, "test_bst_ocp"}, /* Boost OCP. For old ocp (ctrl_reversebst is 0), For new ocp (ctrl_reversebst is 1), */\ + { 0xc4e0, "test_bst_sense"}, /* Test option for the sense NMOS in booster for current mode control., */\ + { 0xc500, "test_cvi"}, /* Analog BIST, switch choose which transistor will be used as current source (also cross coupled sources possible), */\ + { 0xc510, "test_discrete"}, /* Test function noise measurement , */\ + { 0xc520, "test_iddq"}, /* Set the power stages in iddq mode for gate stress., */\ + { 0xc540, "test_rdson"}, /* Analog BIST, switch to enable Rdson measurement , */\ + { 0xc550, "test_sdelta"}, /* Analog BIST, noise test , */\ + { 0xc570, "test_enbl_cs"}, /* Enable for digimux mode of current sense , */\ + { 0xc580, "test_enbl_vs"}, /* Enable for digimux mode of voltage sense , */\ + { 0xc600, "enbl_pwm_dcc"}, /* Enables direct control of pwm duty cycle for DCDC power stage, */\ + { 0xc613, "pwm_dcc_cnt"}, /* Control pwm duty cycle when enbl_pwm_dcc is 1 , */\ + { 0xc650, "enbl_ldo_stress"}, /* Enable stress of internal supply voltages powerstages, */\ + { 0xc707, "digimuxa_sel"}, /* DigimuxA input selection control routed to DATAO (see Digimux list for details), */\ + { 0xc787, "digimuxb_sel"}, /* DigimuxB input selection control routed to INT (see Digimux list for details), */\ + { 0xc807, "digimuxc_sel"}, /* DigimuxC input selection control routed to PDMDAT (see Digimux list for details), */\ + { 0xc981, "int_ehs"}, /* Speed/load setting for INT IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc9c0, "hs_mode"}, /* I2C high speed mode control , */\ + { 0xca00, "enbl_anamux1"}, /* Enable anamux1 , */\ + { 0xca10, "enbl_anamux2"}, /* Enable anamux2 , */\ + { 0xca20, "enbl_anamux3"}, /* Enable anamux3 , */\ + { 0xca30, "enbl_anamux4"}, /* Enable anamux4 , */\ + { 0xca74, "anamux1"}, /* Anamux selection control - anamux on TEST1 , */\ + { 0xcb04, "anamux2"}, /* Anamux selection control - anamux on TEST2 , */\ + { 0xcb53, "anamux3"}, /* Anamux selection control - anamux on TEST3 , */\ + { 0xcba3, "anamux4"}, /* Anamux selection control - anamux on TEST4 , */\ + { 0xcd05, "pll_seli"}, /* PLL SELI - I2C direct PLL control mode only , */\ + { 0xcd64, "pll_selp"}, /* PLL SELP - I2C direct PLL control mode only , */\ + { 0xcdb3, "pll_selr"}, /* PLL SELR - I2C direct PLL control mode only , */\ + { 0xcdf0, "pll_frm"}, /* PLL free running mode control; 1 in TCB direct control mode, else this control bit, */\ + { 0xce09, "pll_ndec"}, /* PLL NDEC - I2C direct PLL control mode only , */\ + { 0xcea0, "pll_mdec_msb"}, /* MSB of pll_mdec - I2C direct PLL control mode only, */\ + { 0xceb0, "enbl_pll"}, /* Enables PLL in I2C direct PLL control mode only , */\ + { 0xcec0, "enbl_osc"}, /* Enables OSC1M in I2C direct control mode only , */\ + { 0xced0, "pll_bypass"}, /* PLL bypass control in I2C direct PLL control mode only, */\ + { 0xcee0, "pll_directi"}, /* PLL directi control in I2C direct PLL control mode only, */\ + { 0xcef0, "pll_directo"}, /* PLL directo control in I2C direct PLL control mode only, */\ + { 0xcf0f, "pll_mdec_lsb"}, /* Bits 15..0 of PLL MDEC are I2C direct PLL control mode only, */\ + { 0xd006, "pll_pdec"}, /* PLL PDEC - I2C direct PLL control mode only , */\ + { 0xd10f, "tsig_freq_lsb"}, /* Internal sinus test generator frequency control , */\ + { 0xd202, "tsig_freq_msb"}, /* Select internal sinus test generator, frequency control msb bits, */\ + { 0xd230, "inject_tsig"}, /* Control bit to switch to internal sinus test generator, */\ + { 0xd283, "tsig_gain"}, /* Test signal gain , */\ + { 0xd300, "adc10_reset"}, /* Reset for ADC10 - I2C direct control mode , */\ + { 0xd311, "adc10_test"}, /* Test mode selection signal for ADC10 - I2C direct control mode, */\ + { 0xd332, "adc10_sel"}, /* Select the input to convert for ADC10 - I2C direct control mode, */\ + { 0xd364, "adc10_prog_sample"}, /* ADC10 program sample setting - I2C direct control mode, */\ + { 0xd3b0, "adc10_enbl"}, /* Enable ADC10 - I2C direct control mode , */\ + { 0xd3c0, "bypass_lp_vbat"}, /* Bypass control for Low pass filter in batt sensor , */\ + { 0xd409, "data_adc10_tempbat"}, /* ADC 10 data output data for testing , */\ + { 0xd507, "ctrl_digtoana_hidden"}, /* Spare digital to analog control bits - Hidden , */\ + { 0xd580, "enbl_clk_out_of_range"}, /* Clock out of range , */\ + { 0xd621, "clkdiv_audio_sel"}, /* Audio clock divider selection in direct clock control mode, */\ + { 0xd641, "clkdiv_muxa_sel"}, /* DCDC MUXA clock divider selection in direct clock control mode, */\ + { 0xd661, "clkdiv_muxb_sel"}, /* DCDC MUXB clock divider selection in direct clock control mode, */\ + { 0xd721, "datao_ehs"}, /* Speed/load setting for DATAO IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xd740, "bck_ehs"}, /* High-speed and standard/fast mode selection for BCK IO cell (see IIC3V3 IO cell datasheet), */\ + { 0xd750, "datai_ehs"}, /* High-speed and standard/fast mode selection for DATAI IO cell (see IIC3V3 IO cell datasheet), */\ + { 0xd800, "source_in_testmode"}, /* tdm source in test mode (return only current and voltage sense), */\ + { 0xd810, "gainatt_feedback"}, /* gainatt feedback to tdm , */\ + { 0xd822, "test_parametric_io"}, /* test io parametric , */\ + { 0xd850, "ctrl_bst_clk_lp1"}, /* boost clock control in low power mode1 , */\ + { 0xd861, "test_spare_out1"}, /* test spare out 1 , */\ + { 0xd880, "bst_dcmbst"}, /* dcm boost , */\ + { 0xd8c3, "test_spare_out2"}, /* test spare out 1 , */\ + { 0xee0f, "sw_profile"}, /* Software profile data , */\ + { 0xef0f, "sw_vstep"}, /* Software vstep information , */\ + { 0xf000, "calibration_onetime"}, /* Calibration schedule , */\ + { 0xf010, "calibr_ron_done"}, /* Calibration Ron executed , */\ + { 0xf020, "calibr_dcdc_api_calibrate"}, /* Calibration current limit DCDC , */\ + { 0xf030, "calibr_dcdc_delta_sign"}, /* Sign bit for delta calibration current limit DCDC , */\ + { 0xf042, "calibr_dcdc_delta"}, /* Calibration delta current limit DCDC , */\ + { 0xf078, "calibr_speaker_info"}, /* Reserved space for allowing customer to store speaker information, */\ + { 0xf105, "calibr_vout_offset"}, /* DCDC offset calibration 2's complement (key1 protected), */\ + { 0xf169, "spare_mpt1_15_6"}, /* SPARE , */\ + { 0xf203, "calibr_gain"}, /* HW gain module (2's complement) , */\ + { 0xf245, "calibr_offset"}, /* Offset for amplifier, HW gain module (2's complement), */\ + { 0xf2a5, "spare_mtp2_15_10"}, /* SPARE , */\ + { 0xf307, "calibr_gain_vs"}, /* Voltage sense gain , */\ + { 0xf387, "calibr_gain_cs"}, /* Current sense gain (signed two's complement format), */\ + { 0xf407, "spare_mtp4_15_0"}, /* SPARE , */\ + { 0xf487, "vs_trim"}, /* VS Trimming , */\ + { 0xf50f, "calibr_R25C_R"}, /* Ron resistance of speaker coil , */\ + { 0xf60f, "spare_mpt6_6_0"}, /* SPARE , */\ + { 0xf706, "ctrl_offset_a"}, /* Offset of level shifter A , */\ + { 0xf770, "spare_mtp7_07"}, /* SPARE , */\ + { 0xf786, "ctrl_offset_b"}, /* Offset of amplifier level shifter B , */\ + { 0xf7f0, "spare_mtp7_15"}, /* SPARE , */\ + { 0xf806, "htol_iic_addr"}, /* 7-bit I2C address to be used during HTOL testing , */\ + { 0xf870, "htol_iic_addr_en"}, /* HTOL I2C address enable control , */\ + { 0xf884, "calibr_temp_offset"}, /* Temperature offset 2's compliment (key1 protected), */\ + { 0xf8d2, "calibr_temp_gain"}, /* Temperature gain 2's compliment (key1 protected) , */\ + { 0xf900, "mtp_lock_dcdcoff_mode"}, /* Disable function dcdcoff_mode , */\ + { 0xf910, "spare_mtp9_1"}, /* SPARE , */\ + { 0xf920, "mtp_lock_bypass_clipper"}, /* Disable function bypass_clipper , */\ + { 0xf930, "mtp_lock_max_dcdc_voltage"}, /* Force Boost in follower mode , */\ + { 0xf943, "calibr_vbg_trim"}, /* Bandgap trimming control , */\ + { 0xf980, "spare_mtp9_8"}, /* SPARE , */\ + { 0xf990, "mtp_enbl_pwm_delay_clock_gating"}, /* pwm delay clock auto gating , */\ + { 0xf9a0, "mtp_enbl_ocp_clock_gating"}, /* ocpclock auto gating , */\ + { 0xf9b0, "mtp_gate_cgu_clock_for_test"}, /* cgu test clock control , */\ + { 0xf9c0, "mtp_tdm_pad_sel"}, /* tdm pad selection , */\ + { 0xf9d2, "spare_mtp9_15_12"}, /* MTP-control FW - See Firmware I2C API document for details, */\ + { 0xfa0f, "mtpdataA"}, /* MTPdataA (key1 protected) , */\ + { 0xfb0f, "mtpdataB"}, /* MTPdataB (key1 protected) , */\ + { 0xfc0f, "mtpdataC"}, /* MTPdataC (key1 protected) , */\ + { 0xfd0f, "mtpdataD"}, /* MTPdataD (key1 protected) , */\ + { 0xfe0f, "mtpdataE"}, /* MTPdataE (key1 protected) , */\ + { 0xff07, "calibr_osc_delta_ndiv"}, /* Calibration data for OSC1M, signed number representation, */\ + { 0xff87, "spare_mtp7_15_08"}, /* SPARE , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +enum tfa9874_irq { + tfa9874_irq_stvdds = 0, + tfa9874_irq_stbstoc = 1, + tfa9874_irq_stotds = 2, + tfa9874_irq_stocpr = 3, + tfa9874_irq_stuvds = 4, + tfa9874_irq_stmanalarm = 5, + tfa9874_irq_sttdmer = 6, + tfa9874_irq_stnoclk = 7, + tfa9874_irq_max = 8, + tfa9874_irq_all = -1 /* all irqs */}; + +#define TFA9874_IRQ_NAMETABLE static tfaIrqName_t Tfa9874IrqNames[]= {\ + { 0, "STVDDS"},\ + { 1, "STBSTOC"},\ + { 2, "STOTDS"},\ + { 3, "STOCPR"},\ + { 4, "STUVDS"},\ + { 5, "STMANALARM"},\ + { 6, "STTDMER"},\ + { 7, "STNOCLK"},\ + { 8, "8"},\ +}; +#endif /* _TFA9874_TFAFIELDNAMES_H */ diff --git a/sound/soc/codecs/inc/tfa9887_tfafieldnames.h b/sound/soc/codecs/inc/tfa9887_tfafieldnames.h new file mode 100644 index 000000000000..515b6bc5e0fe --- /dev/null +++ b/sound/soc/codecs/inc/tfa9887_tfafieldnames.h @@ -0,0 +1,61 @@ +/** Filename: Tfa9887_TfaFieldnames.h + * This file was generated automatically on 04/14/15 at 10:23:40. + * Source file: TFA9897N1B_I2C_list_URT_source_v34_87Only.xls + */ +#define TFA9887_I2CVERSION 34 +#define TFA9895_I2CVERSION 34 +#define TFA9887_NAMETABLE static tfaBfName_t Tfa9887DatasheetNames[]= {\ + { 0x402, "I2SF"}, /* I2SFormat data 1 input: , */\ + { 0x431, "CHS12"}, /* ChannelSelection data1 input (In CoolFlux) , */\ + { 0x450, "CHS3"}, /* ChannelSelection data 2 input (coolflux input, the DCDC converter gets the other signal), */\ + { 0x461, "CHSA"}, /* Input selection for amplifier , */\ + { 0x4b0, "I2SDOE"}, /* Enable data output , */\ + { 0x4c3, "I2SSR"}, /* sample rate setting , */\ + { 0x500, "BSSBY"}, /* , */\ + { 0x511, "BSSCR"}, /* 00 = 0.56 dB/Sample , */\ + { 0x532, "BSST"}, /* 000 = 2.92V , */\ + { 0x5f0, "I2SDOC"}, /* selection data out , */\ + { 0xa02, "DOLS"}, /* Output selection dataout left channel , */\ + { 0xa32, "DORS"}, /* Output selection dataout right channel , */\ + { 0xa62, "SPKL"}, /* Selection speaker induction , */\ + { 0xa91, "SPKR"}, /* Selection speaker impedance , */\ + { 0xab3, "DCFG"}, /* DCDC speaker current compensation gain , */\ + { 0x4134, "PWMDEL"}, /* PWM DelayBits to set the delay , */\ + { 0x4180, "PWMSH"}, /* PWM Shape , */\ + { 0x4190, "PWMRE"}, /* PWM Bitlength in noise shaper , */\ + { 0x48e1, "TCC"}, /* sample & hold track time: , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA9887_BITNAMETABLE static tfaBfName_t Tfa9887BitNames[]= {\ + { 0x402, "i2s_seti"}, /* I2SFormat data 1 input: , */\ + { 0x431, "chan_sel1"}, /* ChannelSelection data1 input (In CoolFlux) , */\ + { 0x450, "lr_sw_i2si2"}, /* ChannelSelection data 2 input (coolflux input, the DCDC converter gets the other signal), */\ + { 0x461, "input_sel"}, /* Input selection for amplifier , */\ + { 0x4b0, "enbl_datao"}, /* Enable data output , */\ + { 0x4c3, "i2s_fs"}, /* sample rate setting , */\ + { 0x500, "bypass_clipper"}, /* , */\ + { 0x511, "vbat_prot_attacktime[1:0]"}, /* 00 = 0.56 dB/Sample , */\ + { 0x532, "vbat_prot_thlevel[2:0]"}, /* 000 = 2.92V , */\ + { 0x5d0, "reset_min_vbat"}, /* to reset the clipper via I2C in case the CF is bypassed, */\ + { 0x5f0, "datao_sel"}, /* selection data out , */\ + { 0xa02, "sel_i2so_l"}, /* Output selection dataout left channel , */\ + { 0xa32, "sel_i2so_r"}, /* Output selection dataout right channel , */\ + { 0xa62, "ctrl_spkr_coil"}, /* Selection speaker induction , */\ + { 0xa91, "ctrl_spr_res"}, /* Selection speaker impedance , */\ + { 0xab3, "ctrl_dcdc_spkr_i_comp_gain"}, /* DCDC speaker current compensation gain , */\ + { 0xaf0, "ctrl_dcdc_spkr_i_comp_sign"}, /* DCDC speaker current compensation sign , */\ + { 0x4100, "bypass_hp"}, /* bypass_hp, to bypass the hp filter byhind the CoolFlux, */\ + { 0x4110, "hard_mute"}, /* hard mute setting in HW , */\ + { 0x4120, "soft_mute"}, /* Soft mute setting in HW , */\ + { 0x4134, "PWM_Delay[4:0]"}, /* PWM DelayBits to set the delay , */\ + { 0x4180, "PWM_Shape"}, /* PWM Shape , */\ + { 0x4190, "PWM_BitLength"}, /* PWM Bitlength in noise shaper , */\ + { 0x4800, "ctrl_negin"}, /* , */\ + { 0x4810, "ctrl_cs_sein"}, /* , */\ + { 0x4820, "ctrl_coincidencecs"}, /* HIGH => Prevent dcdc switching during clk_cs_clksh, */\ + { 0x4876, "delay_se_neg[6:0]"}, /* delayshiftse2 , */\ + { 0x48e1, "ctrl_cs_ttrack[1:0]"}, /* sample & hold track time: , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + diff --git a/sound/soc/codecs/inc/tfa9890_tfafieldnames.h b/sound/soc/codecs/inc/tfa9890_tfafieldnames.h new file mode 100644 index 000000000000..e6f0e0228bb6 --- /dev/null +++ b/sound/soc/codecs/inc/tfa9890_tfafieldnames.h @@ -0,0 +1,76 @@ +/** Filename: Tfa9890_TfaFieldnames.h + * This file was generated automatically on 04/07/15 at 14:46:37. + * Source file: TFA9897N1B_I2C_list_URT_source_v34_90Only.xls + */ +#define TFA9890_I2CVERSION 34 +#define TFA9890_NAMETABLE static tfaBfName_t Tfa9890DatasheetNames[]= {\ + { 0x402, "I2SF"}, /* I2SFormat data 1 input: , */\ + { 0x431, "CHS12"}, /* ChannelSelection data1 input (In CoolFlux) , */\ + { 0x450, "CHS3"}, /* ChannelSelection data 2 input (coolflux input, the DCDC converter gets the other signal), */\ + { 0x461, "CHSA"}, /* Input selection for amplifier , */\ + { 0x481, "I2SDOC"}, /* selection data out , */\ + { 0x4a0, "DISP"}, /* idp protection , */\ + { 0x4b0, "I2SDOE"}, /* Enable data output , */\ + { 0x4c3, "I2SSR"}, /* sample rate setting , */\ + { 0x732, "DCMCC"}, /* Max boost coil current - step of 500 mA , */\ + { 0x9c0, "CCFD"}, /* Selection CoolFlux Clock , */\ + { 0x9d0, "ISEL"}, /* selection input 1 or 2 , */\ + { 0xa02, "DOLS"}, /* Output selection dataout left channel , */\ + { 0xa32, "DORS"}, /* Output selection dataout right channel , */\ + { 0xa62, "SPKL"}, /* Selection speaker induction , */\ + { 0xa91, "SPKR"}, /* Selection speaker impedance , */\ + { 0xab3, "DCFG"}, /* DCDC speaker current compensation gain , */\ + { 0xf00, "VDDD"}, /* mask flag_por for interupt generation , */\ + { 0xf10, "OTDD"}, /* mask flag_otpok for interupt generation , */\ + { 0xf20, "OVDD"}, /* mask flag_ovpok for interupt generation , */\ + { 0xf30, "UVDD"}, /* mask flag_uvpok for interupt generation , */\ + { 0xf40, "OCDD"}, /* mask flag_ocp_alarm for interupt generation , */\ + { 0xf50, "CLKD"}, /* mask flag_clocks_stable for interupt generation , */\ + { 0xf60, "DCCD"}, /* mask flag_pwrokbst for interupt generation , */\ + { 0xf70, "SPKD"}, /* mask flag_cf_speakererror for interupt generation , */\ + { 0xf80, "WDD"}, /* mask flag_watchdog_reset for interupt generation , */\ + { 0xf90, "LCLK"}, /* mask flag_lost_clk for interupt generation , */\ + { 0xfe0, "INT"}, /* enabling interrupt , */\ + { 0xff0, "INTP"}, /* Setting polarity interupt , */\ + { 0x8f0f, "VERSION"}, /* (key1 protected) , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA9890_BITNAMETABLE static tfaBfName_t Tfa9890BitNames[]= {\ + { 0x402, "i2s_seti"}, /* I2SFormat data 1 input: , */\ + { 0x431, "chan_sel1"}, /* ChannelSelection data1 input (In CoolFlux) , */\ + { 0x450, "lr_sw_i2si2"}, /* ChannelSelection data 2 input (coolflux input, the DCDC converter gets the other signal), */\ + { 0x461, "input_sel"}, /* Input selection for amplifier , */\ + { 0x481, "datao_sel"}, /* selection data out , */\ + { 0x4a0, "disable_idp"}, /* idp protection , */\ + { 0x4b0, "enbl_datao"}, /* Enable data output , */\ + { 0x4c3, "i2s_fs"}, /* sample rate setting , */\ + { 0x732, "ctrl_bstcur"}, /* Max boost coil current - step of 500 mA , */\ + { 0x9c0, "sel_cf_clk"}, /* Selection CoolFlux Clock , */\ + { 0x9d0, "intf_sel"}, /* selection input 1 or 2 , */\ + { 0xa02, "sel_i2so_l"}, /* Output selection dataout left channel , */\ + { 0xa32, "sel_i2so_r"}, /* Output selection dataout right channel , */\ + { 0xa62, "ctrl_spkr_coil"}, /* Selection speaker induction , */\ + { 0xa91, "ctrl_spr_res"}, /* Selection speaker impedance , */\ + { 0xab3, "ctrl_dcdc_spkr_i_comp_gain"}, /* DCDC speaker current compensation gain , */\ + { 0xaf0, "ctrl_dcdc_spkr_i_comp_sign"}, /* DCDC speaker current compensation sign , */\ + { 0xf00, "flag_por_mask"}, /* mask flag_por for interupt generation , */\ + { 0xf10, "flag_otpok_mask"}, /* mask flag_otpok for interupt generation , */\ + { 0xf20, "flag_ovpok_mask"}, /* mask flag_ovpok for interupt generation , */\ + { 0xf30, "flag_uvpok_mask"}, /* mask flag_uvpok for interupt generation , */\ + { 0xf40, "flag_ocp_alarm_mask"}, /* mask flag_ocp_alarm for interupt generation , */\ + { 0xf50, "flag_clocks_stable_mask"}, /* mask flag_clocks_stable for interupt generation , */\ + { 0xf60, "flag_pwrokbst_mask"}, /* mask flag_pwrokbst for interupt generation , */\ + { 0xf70, "flag_cf_speakererror_mask"}, /* mask flag_cf_speakererror for interupt generation , */\ + { 0xf80, "flag_watchdog_reset_mask"}, /* mask flag_watchdog_reset for interupt generation , */\ + { 0xf90, "flag_lost_clk_mask"}, /* mask flag_lost_clk for interupt generation , */\ + { 0xfe0, "enable_interrupt"}, /* enabling interrupt , */\ + { 0xff0, "invert_int_polarity"}, /* Setting polarity interupt , */\ + { 0x4700, "switch_fb"}, /* switch_fb , */\ + { 0x4713, "se_hyst"}, /* se_hyst , */\ + { 0x4754, "se_level"}, /* se_level , */\ + { 0x47a5, "ktemp"}, /* temperature compensation trimming , */\ + { 0x8f0f, "production_data6"}, /* (key1 protected) , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + diff --git a/sound/soc/codecs/inc/tfa9891_genregs.h b/sound/soc/codecs/inc/tfa9891_genregs.h new file mode 100644 index 000000000000..14f75afc7852 --- /dev/null +++ b/sound/soc/codecs/inc/tfa9891_genregs.h @@ -0,0 +1,1125 @@ +/** Filename: Tfa98xx_genregs.h + * This file was generated automatically on 07/01/15 at 10:25:08. + * Source file: TFA9891_I2C_list_V11.xls + */ + +#ifndef TFA9891_GENREGS_H +#define TFA9891_GENREGS_H + + +#define TFA98XX_STATUSREG 0x00 +#define TFA98XX_BATTERYVOLTAGE 0x01 +#define TFA9891_TEMPERATURE 0x02 +#define TFA98XX_REVISIONNUMBER 0x03 +#define TFA98XX_I2SREG 0x04 +#define TFA98XX_BAT_PROT 0x05 +#define TFA98XX_AUDIO_CTR 0x06 +#define TFA98XX_DCDCBOOST 0x07 +#define TFA98XX_SPKR_CALIBRATION 0x08 +#define TFA98XX_SYS_CTRL 0x09 +#define TFA98XX_I2S_SEL_REG 0x0a +#define TFA98XX_HIDDEN_MTP_KEY2 0x0b +#define TFA98XX_INTERRUPT_REG 0x0f +#define TFA98XX_PDM_CTRL 0x10 +#define TFA98XX_PDM_OUT_CTRL 0x11 +#define TFA98XX_PDM_DS4_R 0x12 +#define TFA98XX_PDM_DS4_L 0x13 +#define TFA98XX_CTRL_SAAM_PGA 0x22 +#define TFA98XX_MISC_CTRL 0x25 +#define TFA98XX_CURRENTSENSE1 0x46 +#define TFA98XX_CURRENTSENSE4 0x49 +#define TFA98XX_HIDDEN_MTP_CTRL_REG3 0x62 +#define TFA9891_CF_CONTROLS 0x70 +#define TFA9891_CF_MAD 0x71 +#define TFA9891_CF_MEM 0x72 +#define TFA9891_CF_STATUS 0x73 +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP 0x80 + +/* + * (0x00)-StatusReg + */ + +/* + * POR + */ +#define TFA98XX_STATUSREG_VDDS (0x1<<0) +#define TFA98XX_STATUSREG_VDDS_POS 0 +#define TFA98XX_STATUSREG_VDDS_LEN 1 +#define TFA98XX_STATUSREG_VDDS_MAX 1 +#define TFA98XX_STATUSREG_VDDS_MSK 0x1 + +/* + * PLL_LOCK + */ +#define TFA98XX_STATUSREG_PLLS (0x1<<1) +#define TFA98XX_STATUSREG_PLLS_POS 1 +#define TFA98XX_STATUSREG_PLLS_LEN 1 +#define TFA98XX_STATUSREG_PLLS_MAX 1 +#define TFA98XX_STATUSREG_PLLS_MSK 0x2 + +/* + * flag_otpok + */ +#define TFA98XX_STATUSREG_OTDS (0x1<<2) +#define TFA98XX_STATUSREG_OTDS_POS 2 +#define TFA98XX_STATUSREG_OTDS_LEN 1 +#define TFA98XX_STATUSREG_OTDS_MAX 1 +#define TFA98XX_STATUSREG_OTDS_MSK 0x4 + +/* + * flag_ovpok + */ +#define TFA98XX_STATUSREG_OVDS (0x1<<3) +#define TFA98XX_STATUSREG_OVDS_POS 3 +#define TFA98XX_STATUSREG_OVDS_LEN 1 +#define TFA98XX_STATUSREG_OVDS_MAX 1 +#define TFA98XX_STATUSREG_OVDS_MSK 0x8 + +/* + * flag_uvpok + */ +#define TFA98XX_STATUSREG_UVDS (0x1<<4) +#define TFA98XX_STATUSREG_UVDS_POS 4 +#define TFA98XX_STATUSREG_UVDS_LEN 1 +#define TFA98XX_STATUSREG_UVDS_MAX 1 +#define TFA98XX_STATUSREG_UVDS_MSK 0x10 + +/* + * flag_OCP_alarm + */ +#define TFA98XX_STATUSREG_OCDS (0x1<<5) +#define TFA98XX_STATUSREG_OCDS_POS 5 +#define TFA98XX_STATUSREG_OCDS_LEN 1 +#define TFA98XX_STATUSREG_OCDS_MAX 1 +#define TFA98XX_STATUSREG_OCDS_MSK 0x20 + +/* + * flag_clocks_stable + */ +#define TFA98XX_STATUSREG_CLKS (0x1<<6) +#define TFA98XX_STATUSREG_CLKS_POS 6 +#define TFA98XX_STATUSREG_CLKS_LEN 1 +#define TFA98XX_STATUSREG_CLKS_MAX 1 +#define TFA98XX_STATUSREG_CLKS_MSK 0x40 + +/* + * CLIP + */ +#define TFA98XX_STATUSREG_CLIPS (0x1<<7) +#define TFA98XX_STATUSREG_CLIPS_POS 7 +#define TFA98XX_STATUSREG_CLIPS_LEN 1 +#define TFA98XX_STATUSREG_CLIPS_MAX 1 +#define TFA98XX_STATUSREG_CLIPS_MSK 0x80 + +/* + * mtp_busy + */ +#define TFA98XX_STATUSREG_MTPB (0x1<<8) +#define TFA98XX_STATUSREG_MTPB_POS 8 +#define TFA98XX_STATUSREG_MTPB_LEN 1 +#define TFA98XX_STATUSREG_MTPB_MAX 1 +#define TFA98XX_STATUSREG_MTPB_MSK 0x100 + +/* + * flag_pwrokbst + */ +#define TFA98XX_STATUSREG_DCCS (0x1<<9) +#define TFA98XX_STATUSREG_DCCS_POS 9 +#define TFA98XX_STATUSREG_DCCS_LEN 1 +#define TFA98XX_STATUSREG_DCCS_MAX 1 +#define TFA98XX_STATUSREG_DCCS_MSK 0x200 + +/* + * flag_cf_speakererror + */ +#define TFA98XX_STATUSREG_SPKS (0x1<<10) +#define TFA98XX_STATUSREG_SPKS_POS 10 +#define TFA98XX_STATUSREG_SPKS_LEN 1 +#define TFA98XX_STATUSREG_SPKS_MAX 1 +#define TFA98XX_STATUSREG_SPKS_MSK 0x400 + +/* + * flag_cold_started + */ +#define TFA98XX_STATUSREG_ACS (0x1<<11) +#define TFA98XX_STATUSREG_ACS_POS 11 +#define TFA98XX_STATUSREG_ACS_LEN 1 +#define TFA98XX_STATUSREG_ACS_MAX 1 +#define TFA98XX_STATUSREG_ACS_MSK 0x800 + +/* + * flag_engage + */ +#define TFA98XX_STATUSREG_SWS (0x1<<12) +#define TFA98XX_STATUSREG_SWS_POS 12 +#define TFA98XX_STATUSREG_SWS_LEN 1 +#define TFA98XX_STATUSREG_SWS_MAX 1 +#define TFA98XX_STATUSREG_SWS_MSK 0x1000 + +/* + * flag_watchdog_reset + */ +#define TFA98XX_STATUSREG_WDS (0x1<<13) +#define TFA98XX_STATUSREG_WDS_POS 13 +#define TFA98XX_STATUSREG_WDS_LEN 1 +#define TFA98XX_STATUSREG_WDS_MAX 1 +#define TFA98XX_STATUSREG_WDS_MSK 0x2000 + +/* + * flag_enbl_amp + */ +#define TFA98XX_STATUSREG_AMPS (0x1<<14) +#define TFA98XX_STATUSREG_AMPS_POS 14 +#define TFA98XX_STATUSREG_AMPS_LEN 1 +#define TFA98XX_STATUSREG_AMPS_MAX 1 +#define TFA98XX_STATUSREG_AMPS_MSK 0x4000 + +/* + * flag_enbl_ref + */ +#define TFA98XX_STATUSREG_AREFS (0x1<<15) +#define TFA98XX_STATUSREG_AREFS_POS 15 +#define TFA98XX_STATUSREG_AREFS_LEN 1 +#define TFA98XX_STATUSREG_AREFS_MAX 1 +#define TFA98XX_STATUSREG_AREFS_MSK 0x8000 + +/* + * (0x01)-BatteryVoltage + */ + +/* + * bat_adc + */ +#define TFA98XX_BATTERYVOLTAGE_BATS (0x3ff<<0) +#define TFA98XX_BATTERYVOLTAGE_BATS_POS 0 +#define TFA98XX_BATTERYVOLTAGE_BATS_LEN 10 +#define TFA98XX_BATTERYVOLTAGE_BATS_MAX 1023 +#define TFA98XX_BATTERYVOLTAGE_BATS_MSK 0x3ff + + +/* + * (0x02)-Temperature + */ + +/* + * temp_adc + */ +#define TFA9891_TEMPERATURE_TEMPS (0x1ff<<0) +#define TFA9891_TEMPERATURE_TEMPS_POS 0 +#define TFA9891_TEMPERATURE_TEMPS_LEN 9 +#define TFA9891_TEMPERATURE_TEMPS_MAX 511 +#define TFA9891_TEMPERATURE_TEMPS_MSK 0x1ff + + +/* + * (0x03)-RevisionNumber + */ + +/* + * rev_reg + */ +#define TFA98XX_REVISIONNUMBER_REV (0xff<<0) +#define TFA98XX_REVISIONNUMBER_REV_POS 0 +#define TFA98XX_REVISIONNUMBER_REV_LEN 8 +#define TFA98XX_REVISIONNUMBER_REV_MAX 255 +#define TFA98XX_REVISIONNUMBER_REV_MSK 0xff + + +/* + * (0x04)-I2SReg + */ + +/* + * i2s_seti + */ +#define TFA98XX_I2SREG_I2SF (0x7<<0) +#define TFA98XX_I2SREG_I2SF_POS 0 +#define TFA98XX_I2SREG_I2SF_LEN 3 +#define TFA98XX_I2SREG_I2SF_MAX 7 +#define TFA98XX_I2SREG_I2SF_MSK 0x7 + +/* + * chan_sel1 + */ +#define TFA98XX_I2SREG_CHS12 (0x3<<3) +#define TFA98XX_I2SREG_CHS12_POS 3 +#define TFA98XX_I2SREG_CHS12_LEN 2 +#define TFA98XX_I2SREG_CHS12_MAX 3 +#define TFA98XX_I2SREG_CHS12_MSK 0x18 + +/* + * lr_sw_i2si2 + */ +#define TFA98XX_I2SREG_CHS3 (0x1<<5) +#define TFA98XX_I2SREG_CHS3_POS 5 +#define TFA98XX_I2SREG_CHS3_LEN 1 +#define TFA98XX_I2SREG_CHS3_MAX 1 +#define TFA98XX_I2SREG_CHS3_MSK 0x20 + +/* + * input_sel + */ +#define TFA98XX_I2SREG_CHSA (0x3<<6) +#define TFA98XX_I2SREG_CHSA_POS 6 +#define TFA98XX_I2SREG_CHSA_LEN 2 +#define TFA98XX_I2SREG_CHSA_MAX 3 +#define TFA98XX_I2SREG_CHSA_MSK 0xc0 + +/* + * datao_sel + */ +#define TFA98XX_I2SREG_I2SDOC (0x3<<8) +#define TFA98XX_I2SREG_I2SDOC_POS 8 +#define TFA98XX_I2SREG_I2SDOC_LEN 2 +#define TFA98XX_I2SREG_I2SDOC_MAX 3 +#define TFA98XX_I2SREG_I2SDOC_MSK 0x300 + +/* + * disable_idp + */ +#define TFA98XX_I2SREG_DISP (0x1<<10) +#define TFA98XX_I2SREG_DISP_POS 10 +#define TFA98XX_I2SREG_DISP_LEN 1 +#define TFA98XX_I2SREG_DISP_MAX 1 +#define TFA98XX_I2SREG_DISP_MSK 0x400 + +/* + * enbl_datao + */ +#define TFA98XX_I2SREG_I2SDOE (0x1<<11) +#define TFA98XX_I2SREG_I2SDOE_POS 11 +#define TFA98XX_I2SREG_I2SDOE_LEN 1 +#define TFA98XX_I2SREG_I2SDOE_MAX 1 +#define TFA98XX_I2SREG_I2SDOE_MSK 0x800 + +/* + * i2s_fs + */ +#define TFA98XX_I2SREG_I2SSR (0xf<<12) +#define TFA98XX_I2SREG_I2SSR_POS 12 +#define TFA98XX_I2SREG_I2SSR_LEN 4 +#define TFA98XX_I2SREG_I2SSR_MAX 15 +#define TFA98XX_I2SREG_I2SSR_MSK 0xf000 + + +/* + * (0x05)-bat_prot + */ + +/* + * vbat_prot_attacktime + */ +#define TFA98XX_BAT_PROT_BSSCR (0x3<<0) +#define TFA98XX_BAT_PROT_BSSCR_POS 0 +#define TFA98XX_BAT_PROT_BSSCR_LEN 2 +#define TFA98XX_BAT_PROT_BSSCR_MAX 3 +#define TFA98XX_BAT_PROT_BSSCR_MSK 0x3 + +/* + * vbat_prot_thlevel + */ +#define TFA98XX_BAT_PROT_BSST (0xf<<2) +#define TFA98XX_BAT_PROT_BSST_POS 2 +#define TFA98XX_BAT_PROT_BSST_LEN 4 +#define TFA98XX_BAT_PROT_BSST_MAX 15 +#define TFA98XX_BAT_PROT_BSST_MSK 0x3c + +/* + * vbat_prot_max_reduct + */ +#define TFA98XX_BAT_PROT_BSSRL (0x3<<6) +#define TFA98XX_BAT_PROT_BSSRL_POS 6 +#define TFA98XX_BAT_PROT_BSSRL_LEN 2 +#define TFA98XX_BAT_PROT_BSSRL_MAX 3 +#define TFA98XX_BAT_PROT_BSSRL_MSK 0xc0 + +/* + * vbat_prot_release_t + */ +#define TFA98XX_BAT_PROT_BSSRR (0x7<<8) +#define TFA98XX_BAT_PROT_BSSRR_POS 8 +#define TFA98XX_BAT_PROT_BSSRR_LEN 3 +#define TFA98XX_BAT_PROT_BSSRR_MAX 7 +#define TFA98XX_BAT_PROT_BSSRR_MSK 0x700 + +/* + * vbat_prot_hysterese + */ +#define TFA98XX_BAT_PROT_BSSHY (0x3<<11) +#define TFA98XX_BAT_PROT_BSSHY_POS 11 +#define TFA98XX_BAT_PROT_BSSHY_LEN 2 +#define TFA98XX_BAT_PROT_BSSHY_MAX 3 +#define TFA98XX_BAT_PROT_BSSHY_MSK 0x1800 + +/* + * sel_vbat + */ +#define TFA98XX_BAT_PROT_BSSR (0x1<<14) +#define TFA98XX_BAT_PROT_BSSR_POS 14 +#define TFA98XX_BAT_PROT_BSSR_LEN 1 +#define TFA98XX_BAT_PROT_BSSR_MAX 1 +#define TFA98XX_BAT_PROT_BSSR_MSK 0x4000 + +/* + * bypass_clipper + */ +#define TFA98XX_BAT_PROT_BSSBY (0x1<<15) +#define TFA98XX_BAT_PROT_BSSBY_POS 15 +#define TFA98XX_BAT_PROT_BSSBY_LEN 1 +#define TFA98XX_BAT_PROT_BSSBY_MAX 1 +#define TFA98XX_BAT_PROT_BSSBY_MSK 0x8000 + + +/* + * (0x06)-audio_ctr + */ + +/* + * dpsa + */ +#define TFA98XX_AUDIO_CTR_DPSA (0x1<<0) +#define TFA98XX_AUDIO_CTR_DPSA_POS 0 +#define TFA98XX_AUDIO_CTR_DPSA_LEN 1 +#define TFA98XX_AUDIO_CTR_DPSA_MAX 1 +#define TFA98XX_AUDIO_CTR_DPSA_MSK 0x1 + +/* + * ctrl_slope + */ +#define TFA98XX_AUDIO_CTR_AMPSL (0xf<<1) +#define TFA98XX_AUDIO_CTR_AMPSL_POS 1 +#define TFA98XX_AUDIO_CTR_AMPSL_LEN 4 +#define TFA98XX_AUDIO_CTR_AMPSL_MAX 15 +#define TFA98XX_AUDIO_CTR_AMPSL_MSK 0x1e + +/* + * cf_mute + */ +#define TFA98XX_AUDIO_CTR_CFSM (0x1<<5) +#define TFA98XX_AUDIO_CTR_CFSM_POS 5 +#define TFA98XX_AUDIO_CTR_CFSM_LEN 1 +#define TFA98XX_AUDIO_CTR_CFSM_MAX 1 +#define TFA98XX_AUDIO_CTR_CFSM_MSK 0x20 + +/* + * ctrl_batsensesteepness + */ +#define TFA98XX_AUDIO_CTR_BSSS (0x1<<7) +#define TFA98XX_AUDIO_CTR_BSSS_POS 7 +#define TFA98XX_AUDIO_CTR_BSSS_LEN 1 +#define TFA98XX_AUDIO_CTR_BSSS_MAX 1 +#define TFA98XX_AUDIO_CTR_BSSS_MSK 0x80 + +/* + * vol + */ +#define TFA98XX_AUDIO_CTR_VOL (0xff<<8) +#define TFA98XX_AUDIO_CTR_VOL_POS 8 +#define TFA98XX_AUDIO_CTR_VOL_LEN 8 +#define TFA98XX_AUDIO_CTR_VOL_MAX 255 +#define TFA98XX_AUDIO_CTR_VOL_MSK 0xff00 + + +/* + * (0x07)-DCDCboost + */ + +/* + * ctrl_bstvolt + */ +#define TFA98XX_DCDCBOOST_DCVO (0x7<<0) +#define TFA98XX_DCDCBOOST_DCVO_POS 0 +#define TFA98XX_DCDCBOOST_DCVO_LEN 3 +#define TFA98XX_DCDCBOOST_DCVO_MAX 7 +#define TFA98XX_DCDCBOOST_DCVO_MSK 0x7 + +/* + * ctrl_bstcur + */ +#define TFA98XX_DCDCBOOST_DCMCC (0x7<<3) +#define TFA98XX_DCDCBOOST_DCMCC_POS 3 +#define TFA98XX_DCDCBOOST_DCMCC_LEN 3 +#define TFA98XX_DCDCBOOST_DCMCC_MAX 7 +#define TFA98XX_DCDCBOOST_DCMCC_MSK 0x38 + +/* + * boost_intel + */ +#define TFA98XX_DCDCBOOST_DCIE (0x1<<10) +#define TFA98XX_DCDCBOOST_DCIE_POS 10 +#define TFA98XX_DCDCBOOST_DCIE_LEN 1 +#define TFA98XX_DCDCBOOST_DCIE_MAX 1 +#define TFA98XX_DCDCBOOST_DCIE_MSK 0x400 + +/* + * boost_speed + */ +#define TFA98XX_DCDCBOOST_DCSR (0x1<<11) +#define TFA98XX_DCDCBOOST_DCSR_POS 11 +#define TFA98XX_DCDCBOOST_DCSR_LEN 1 +#define TFA98XX_DCDCBOOST_DCSR_MAX 1 +#define TFA98XX_DCDCBOOST_DCSR_MSK 0x800 + + +/* + * (0x08)-spkr_calibration + */ + +/* + * ext_temp_sel + */ +#define TFA98XX_SPKR_CALIBRATION_TROS (0x1<<0) +#define TFA98XX_SPKR_CALIBRATION_TROS_POS 0 +#define TFA98XX_SPKR_CALIBRATION_TROS_LEN 1 +#define TFA98XX_SPKR_CALIBRATION_TROS_MAX 1 +#define TFA98XX_SPKR_CALIBRATION_TROS_MSK 0x1 + +/* + * ext_temp + */ +#define TFA98XX_SPKR_CALIBRATION_EXTTS (0x1ff<<1) +#define TFA98XX_SPKR_CALIBRATION_EXTTS_POS 1 +#define TFA98XX_SPKR_CALIBRATION_EXTTS_LEN 9 +#define TFA98XX_SPKR_CALIBRATION_EXTTS_MAX 511 +#define TFA98XX_SPKR_CALIBRATION_EXTTS_MSK 0x3fe + + +/* + * (0x09)-sys_ctrl + */ + +/* + * PowerDown + */ +#define TFA98XX_SYS_CTRL_PWDN (0x1<<0) +#define TFA98XX_SYS_CTRL_PWDN_POS 0 +#define TFA98XX_SYS_CTRL_PWDN_LEN 1 +#define TFA98XX_SYS_CTRL_PWDN_MAX 1 +#define TFA98XX_SYS_CTRL_PWDN_MSK 0x1 + +/* + * reset + */ +#define TFA98XX_SYS_CTRL_I2CR (0x1<<1) +#define TFA98XX_SYS_CTRL_I2CR_POS 1 +#define TFA98XX_SYS_CTRL_I2CR_LEN 1 +#define TFA98XX_SYS_CTRL_I2CR_MAX 1 +#define TFA98XX_SYS_CTRL_I2CR_MSK 0x2 + +/* + * enbl_coolflux + */ +#define TFA98XX_SYS_CTRL_CFE (0x1<<2) +#define TFA98XX_SYS_CTRL_CFE_POS 2 +#define TFA98XX_SYS_CTRL_CFE_LEN 1 +#define TFA98XX_SYS_CTRL_CFE_MAX 1 +#define TFA98XX_SYS_CTRL_CFE_MSK 0x4 + +/* + * enbl_amplifier + */ +#define TFA98XX_SYS_CTRL_AMPE (0x1<<3) +#define TFA98XX_SYS_CTRL_AMPE_POS 3 +#define TFA98XX_SYS_CTRL_AMPE_LEN 1 +#define TFA98XX_SYS_CTRL_AMPE_MAX 1 +#define TFA98XX_SYS_CTRL_AMPE_MSK 0x8 + +/* + * enbl_boost + */ +#define TFA98XX_SYS_CTRL_DCA (0x1<<4) +#define TFA98XX_SYS_CTRL_DCA_POS 4 +#define TFA98XX_SYS_CTRL_DCA_LEN 1 +#define TFA98XX_SYS_CTRL_DCA_MAX 1 +#define TFA98XX_SYS_CTRL_DCA_MSK 0x10 + +/* + * cf_configured + */ +#define TFA98XX_SYS_CTRL_SBSL (0x1<<5) +#define TFA98XX_SYS_CTRL_SBSL_POS 5 +#define TFA98XX_SYS_CTRL_SBSL_LEN 1 +#define TFA98XX_SYS_CTRL_SBSL_MAX 1 +#define TFA98XX_SYS_CTRL_SBSL_MSK 0x20 + +/* + * sel_enbl_amplifier + */ +#define TFA98XX_SYS_CTRL_AMPC (0x1<<6) +#define TFA98XX_SYS_CTRL_AMPC_POS 6 +#define TFA98XX_SYS_CTRL_AMPC_LEN 1 +#define TFA98XX_SYS_CTRL_AMPC_MAX 1 +#define TFA98XX_SYS_CTRL_AMPC_MSK 0x40 + +/* + * dcdcoff_mode + */ +#define TFA98XX_SYS_CTRL_DCDIS (0x1<<7) +#define TFA98XX_SYS_CTRL_DCDIS_POS 7 +#define TFA98XX_SYS_CTRL_DCDIS_LEN 1 +#define TFA98XX_SYS_CTRL_DCDIS_MAX 1 +#define TFA98XX_SYS_CTRL_DCDIS_MSK 0x80 + +/* + * cttr_iddqtest + */ +#define TFA98XX_SYS_CTRL_PSDR (0x1<<8) +#define TFA98XX_SYS_CTRL_PSDR_POS 8 +#define TFA98XX_SYS_CTRL_PSDR_LEN 1 +#define TFA98XX_SYS_CTRL_PSDR_MAX 1 +#define TFA98XX_SYS_CTRL_PSDR_MSK 0x100 + +/* + * ctrl_coil_value + */ +#define TFA98XX_SYS_CTRL_DCCV (0x3<<9) +#define TFA98XX_SYS_CTRL_DCCV_POS 9 +#define TFA98XX_SYS_CTRL_DCCV_LEN 2 +#define TFA98XX_SYS_CTRL_DCCV_MAX 3 +#define TFA98XX_SYS_CTRL_DCCV_MSK 0x600 + +/* + * ctrl_sel_cf_clock + */ +#define TFA98XX_SYS_CTRL_CCFD (0x3<<11) +#define TFA98XX_SYS_CTRL_CCFD_POS 11 +#define TFA98XX_SYS_CTRL_CCFD_LEN 2 +#define TFA98XX_SYS_CTRL_CCFD_MAX 3 +#define TFA98XX_SYS_CTRL_CCFD_MSK 0x1800 + +/* + * intf_sel + */ +#define TFA98XX_SYS_CTRL_ISEL (0x1<<13) +#define TFA98XX_SYS_CTRL_ISEL_POS 13 +#define TFA98XX_SYS_CTRL_ISEL_LEN 1 +#define TFA98XX_SYS_CTRL_ISEL_MAX 1 +#define TFA98XX_SYS_CTRL_ISEL_MSK 0x2000 + +/* + * sel_ws_bck + */ +#define TFA98XX_SYS_CTRL_IPLL (0x1<<14) +#define TFA98XX_SYS_CTRL_IPLL_POS 14 +#define TFA98XX_SYS_CTRL_IPLL_LEN 1 +#define TFA98XX_SYS_CTRL_IPLL_MAX 1 +#define TFA98XX_SYS_CTRL_IPLL_MSK 0x4000 + + +/* + * (0x0a)-I2S_sel_reg + */ + +/* + * sel_i2so_l + */ +#define TFA98XX_I2S_SEL_REG_DOLS (0x7<<0) +#define TFA98XX_I2S_SEL_REG_DOLS_POS 0 +#define TFA98XX_I2S_SEL_REG_DOLS_LEN 3 +#define TFA98XX_I2S_SEL_REG_DOLS_MAX 7 +#define TFA98XX_I2S_SEL_REG_DOLS_MSK 0x7 + +/* + * sel_i2so_r + */ +#define TFA98XX_I2S_SEL_REG_DORS (0x7<<3) +#define TFA98XX_I2S_SEL_REG_DORS_POS 3 +#define TFA98XX_I2S_SEL_REG_DORS_LEN 3 +#define TFA98XX_I2S_SEL_REG_DORS_MAX 7 +#define TFA98XX_I2S_SEL_REG_DORS_MSK 0x38 + +/* + * ctrl_spkr_coil + */ +#define TFA98XX_I2S_SEL_REG_SPKL (0x7<<6) +#define TFA98XX_I2S_SEL_REG_SPKL_POS 6 +#define TFA98XX_I2S_SEL_REG_SPKL_LEN 3 +#define TFA98XX_I2S_SEL_REG_SPKL_MAX 7 +#define TFA98XX_I2S_SEL_REG_SPKL_MSK 0x1c0 + +/* + * ctrl_spr_res + */ +#define TFA98XX_I2S_SEL_REG_SPKR (0x3<<9) +#define TFA98XX_I2S_SEL_REG_SPKR_POS 9 +#define TFA98XX_I2S_SEL_REG_SPKR_LEN 2 +#define TFA98XX_I2S_SEL_REG_SPKR_MAX 3 +#define TFA98XX_I2S_SEL_REG_SPKR_MSK 0x600 + +/* + * ctrl_dcdc_spkr_i_comp_gain + */ +#define TFA98XX_I2S_SEL_REG_DCFG (0xf<<11) +#define TFA98XX_I2S_SEL_REG_DCFG_POS 11 +#define TFA98XX_I2S_SEL_REG_DCFG_LEN 4 +#define TFA98XX_I2S_SEL_REG_DCFG_MAX 15 +#define TFA98XX_I2S_SEL_REG_DCFG_MSK 0x7800 + + +/* + * (0x0b)-Hidden_mtp_key2 + */ + +/* + * MTP_key2 + */ +#define TFA98XX_HIDDEN_MTP_KEY2_MTPK (0xff<<0) +#define TFA98XX_HIDDEN_MTP_KEY2_MTPK_POS 0 +#define TFA98XX_HIDDEN_MTP_KEY2_MTPK_LEN 8 +#define TFA98XX_HIDDEN_MTP_KEY2_MTPK_MAX 255 +#define TFA98XX_HIDDEN_MTP_KEY2_MTPK_MSK 0xff + + +/* + * (0x0f)-interrupt_reg + */ + +/* + * flag_por_mask + */ +#define TFA98XX_INTERRUPT_REG_VDDD (0x1<<0) +#define TFA98XX_INTERRUPT_REG_VDDD_POS 0 +#define TFA98XX_INTERRUPT_REG_VDDD_LEN 1 +#define TFA98XX_INTERRUPT_REG_VDDD_MAX 1 +#define TFA98XX_INTERRUPT_REG_VDDD_MSK 0x1 + +/* + * flag_otpok_mask + */ +#define TFA98XX_INTERRUPT_REG_OTDD (0x1<<1) +#define TFA98XX_INTERRUPT_REG_OTDD_POS 1 +#define TFA98XX_INTERRUPT_REG_OTDD_LEN 1 +#define TFA98XX_INTERRUPT_REG_OTDD_MAX 1 +#define TFA98XX_INTERRUPT_REG_OTDD_MSK 0x2 + +/* + * flag_ovpok_mask + */ +#define TFA98XX_INTERRUPT_REG_OVDD (0x1<<2) +#define TFA98XX_INTERRUPT_REG_OVDD_POS 2 +#define TFA98XX_INTERRUPT_REG_OVDD_LEN 1 +#define TFA98XX_INTERRUPT_REG_OVDD_MAX 1 +#define TFA98XX_INTERRUPT_REG_OVDD_MSK 0x4 + +/* + * flag_uvpok_mask + */ +#define TFA98XX_INTERRUPT_REG_UVDD (0x1<<3) +#define TFA98XX_INTERRUPT_REG_UVDD_POS 3 +#define TFA98XX_INTERRUPT_REG_UVDD_LEN 1 +#define TFA98XX_INTERRUPT_REG_UVDD_MAX 1 +#define TFA98XX_INTERRUPT_REG_UVDD_MSK 0x8 + +/* + * flag_ocp_alarm_mask + */ +#define TFA98XX_INTERRUPT_REG_OCDD (0x1<<4) +#define TFA98XX_INTERRUPT_REG_OCDD_POS 4 +#define TFA98XX_INTERRUPT_REG_OCDD_LEN 1 +#define TFA98XX_INTERRUPT_REG_OCDD_MAX 1 +#define TFA98XX_INTERRUPT_REG_OCDD_MSK 0x10 + +/* + * flag_clocks_stable_mask + */ +#define TFA98XX_INTERRUPT_REG_CLKD (0x1<<5) +#define TFA98XX_INTERRUPT_REG_CLKD_POS 5 +#define TFA98XX_INTERRUPT_REG_CLKD_LEN 1 +#define TFA98XX_INTERRUPT_REG_CLKD_MAX 1 +#define TFA98XX_INTERRUPT_REG_CLKD_MSK 0x20 + +/* + * flag_pwrokbst_mask + */ +#define TFA98XX_INTERRUPT_REG_DCCD (0x1<<6) +#define TFA98XX_INTERRUPT_REG_DCCD_POS 6 +#define TFA98XX_INTERRUPT_REG_DCCD_LEN 1 +#define TFA98XX_INTERRUPT_REG_DCCD_MAX 1 +#define TFA98XX_INTERRUPT_REG_DCCD_MSK 0x40 + +/* + * flag_cf_speakererror_mask + */ +#define TFA98XX_INTERRUPT_REG_SPKD (0x1<<7) +#define TFA98XX_INTERRUPT_REG_SPKD_POS 7 +#define TFA98XX_INTERRUPT_REG_SPKD_LEN 1 +#define TFA98XX_INTERRUPT_REG_SPKD_MAX 1 +#define TFA98XX_INTERRUPT_REG_SPKD_MSK 0x80 + +/* + * flag_watchdog_reset_mask + */ +#define TFA98XX_INTERRUPT_REG_WDD (0x1<<8) +#define TFA98XX_INTERRUPT_REG_WDD_POS 8 +#define TFA98XX_INTERRUPT_REG_WDD_LEN 1 +#define TFA98XX_INTERRUPT_REG_WDD_MAX 1 +#define TFA98XX_INTERRUPT_REG_WDD_MSK 0x100 + +/* + * enable_interrupt + */ +#define TFA98XX_INTERRUPT_REG_INT (0x1<<14) +#define TFA98XX_INTERRUPT_REG_INT_POS 14 +#define TFA98XX_INTERRUPT_REG_INT_LEN 1 +#define TFA98XX_INTERRUPT_REG_INT_MAX 1 +#define TFA98XX_INTERRUPT_REG_INT_MSK 0x4000 + +/* + * invert_int_polarity + */ +#define TFA98XX_INTERRUPT_REG_INTP (0x1<<15) +#define TFA98XX_INTERRUPT_REG_INTP_POS 15 +#define TFA98XX_INTERRUPT_REG_INTP_LEN 1 +#define TFA98XX_INTERRUPT_REG_INTP_MAX 1 +#define TFA98XX_INTERRUPT_REG_INTP_MSK 0x8000 + + +/* + * (0x10)-pdm_ctrl + */ + +/* + * pdm_i2s_input + */ +#define TFA98XX_PDM_CTRL_PDMSEL (0x1<<0) +#define TFA98XX_PDM_CTRL_PDMSEL_POS 0 +#define TFA98XX_PDM_CTRL_PDMSEL_LEN 1 +#define TFA98XX_PDM_CTRL_PDMSEL_MAX 1 +#define TFA98XX_PDM_CTRL_PDMSEL_MSK 0x1 + +/* + * I2S_master_ena + */ +#define TFA98XX_PDM_CTRL_I2SMOUTEN (0x1<<1) +#define TFA98XX_PDM_CTRL_I2SMOUTEN_POS 1 +#define TFA98XX_PDM_CTRL_I2SMOUTEN_LEN 1 +#define TFA98XX_PDM_CTRL_I2SMOUTEN_MAX 1 +#define TFA98XX_PDM_CTRL_I2SMOUTEN_MSK 0x2 + +/* + * pdm_out_sel_r + */ +#define TFA98XX_PDM_CTRL_PDMORSEL (0x3<<2) +#define TFA98XX_PDM_CTRL_PDMORSEL_POS 2 +#define TFA98XX_PDM_CTRL_PDMORSEL_LEN 2 +#define TFA98XX_PDM_CTRL_PDMORSEL_MAX 3 +#define TFA98XX_PDM_CTRL_PDMORSEL_MSK 0xc + +/* + * pdm_out_sel_l + */ +#define TFA98XX_PDM_CTRL_PDMOLSEL (0x3<<4) +#define TFA98XX_PDM_CTRL_PDMOLSEL_POS 4 +#define TFA98XX_PDM_CTRL_PDMOLSEL_LEN 2 +#define TFA98XX_PDM_CTRL_PDMOLSEL_MAX 3 +#define TFA98XX_PDM_CTRL_PDMOLSEL_MSK 0x30 + +/* + * micdat_out_sel + */ +#define TFA98XX_PDM_CTRL_PADSEL (0x3<<6) +#define TFA98XX_PDM_CTRL_PADSEL_POS 6 +#define TFA98XX_PDM_CTRL_PADSEL_LEN 2 +#define TFA98XX_PDM_CTRL_PADSEL_MAX 3 +#define TFA98XX_PDM_CTRL_PADSEL_MSK 0xc0 + + +/* + * (0x11)-pdm_out_ctrl + */ + +/* + * secure_dly + */ +#define TFA98XX_PDM_OUT_CTRL_PDMOSDEN (0x1<<0) +#define TFA98XX_PDM_OUT_CTRL_PDMOSDEN_POS 0 +#define TFA98XX_PDM_OUT_CTRL_PDMOSDEN_LEN 1 +#define TFA98XX_PDM_OUT_CTRL_PDMOSDEN_MAX 1 +#define TFA98XX_PDM_OUT_CTRL_PDMOSDEN_MSK 0x1 + +/* + * d_out_valid_rf_mux + */ +#define TFA98XX_PDM_OUT_CTRL_PDMOSDCF (0x1<<1) +#define TFA98XX_PDM_OUT_CTRL_PDMOSDCF_POS 1 +#define TFA98XX_PDM_OUT_CTRL_PDMOSDCF_LEN 1 +#define TFA98XX_PDM_OUT_CTRL_PDMOSDCF_MAX 1 +#define TFA98XX_PDM_OUT_CTRL_PDMOSDCF_MSK 0x2 + +/* + * Speak_As_Mic_en + */ +#define TFA98XX_PDM_OUT_CTRL_SAAMEN (0x1<<4) +#define TFA98XX_PDM_OUT_CTRL_SAAMEN_POS 4 +#define TFA98XX_PDM_OUT_CTRL_SAAMEN_LEN 1 +#define TFA98XX_PDM_OUT_CTRL_SAAMEN_MAX 1 +#define TFA98XX_PDM_OUT_CTRL_SAAMEN_MSK 0x10 + +/* + * speak_as_mic_lp_mode + */ +#define TFA98XX_PDM_OUT_CTRL_SAAMLPEN (0x1<<5) +#define TFA98XX_PDM_OUT_CTRL_SAAMLPEN_POS 5 +#define TFA98XX_PDM_OUT_CTRL_SAAMLPEN_LEN 1 +#define TFA98XX_PDM_OUT_CTRL_SAAMLPEN_MAX 1 +#define TFA98XX_PDM_OUT_CTRL_SAAMLPEN_MSK 0x20 + +/* + * pdm_out_rate + */ +#define TFA98XX_PDM_OUT_CTRL_PDMOINTEN (0x1<<6) +#define TFA98XX_PDM_OUT_CTRL_PDMOINTEN_POS 6 +#define TFA98XX_PDM_OUT_CTRL_PDMOINTEN_LEN 1 +#define TFA98XX_PDM_OUT_CTRL_PDMOINTEN_MAX 1 +#define TFA98XX_PDM_OUT_CTRL_PDMOINTEN_MSK 0x40 + + +/* + * (0x12)-pdm_ds4_r + */ + +/* + * ds4_g1_r + */ +#define TFA98XX_PDM_DS4_R_PDMORG1 (0xf<<0) +#define TFA98XX_PDM_DS4_R_PDMORG1_POS 0 +#define TFA98XX_PDM_DS4_R_PDMORG1_LEN 4 +#define TFA98XX_PDM_DS4_R_PDMORG1_MAX 15 +#define TFA98XX_PDM_DS4_R_PDMORG1_MSK 0xf + +/* + * ds4_g2_r + */ +#define TFA98XX_PDM_DS4_R_PDMORG2 (0xf<<4) +#define TFA98XX_PDM_DS4_R_PDMORG2_POS 4 +#define TFA98XX_PDM_DS4_R_PDMORG2_LEN 4 +#define TFA98XX_PDM_DS4_R_PDMORG2_MAX 15 +#define TFA98XX_PDM_DS4_R_PDMORG2_MSK 0xf0 + + +/* + * (0x13)-pdm_ds4_l + */ + +/* + * ds4_g1_l + */ +#define TFA98XX_PDM_DS4_L_PDMOLG1 (0xf<<0) +#define TFA98XX_PDM_DS4_L_PDMOLG1_POS 0 +#define TFA98XX_PDM_DS4_L_PDMOLG1_LEN 4 +#define TFA98XX_PDM_DS4_L_PDMOLG1_MAX 15 +#define TFA98XX_PDM_DS4_L_PDMOLG1_MSK 0xf + +/* + * ds4_g2_l + */ +#define TFA98XX_PDM_DS4_L_PDMOLG2 (0xf<<4) +#define TFA98XX_PDM_DS4_L_PDMOLG2_POS 4 +#define TFA98XX_PDM_DS4_L_PDMOLG2_LEN 4 +#define TFA98XX_PDM_DS4_L_PDMOLG2_MAX 15 +#define TFA98XX_PDM_DS4_L_PDMOLG2_MSK 0xf0 + + +/* + * (0x22)-ctrl_saam_pga + */ + +/* + * Ctrl_saam_pga_gain + */ +#define TFA98XX_CTRL_SAAM_PGA_SAAMGAIN (0x7<<0) +#define TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_POS 0 +#define TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_LEN 3 +#define TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_MAX 7 +#define TFA98XX_CTRL_SAAM_PGA_SAAMGAIN_MSK 0x7 + +/* + * ctrl_saam_pga_src + */ +#define TFA98XX_CTRL_SAAM_PGA_SAAMPGACTRL (0x1<<5) +#define TFA98XX_CTRL_SAAM_PGA_SAAMPGACTRL_POS 5 +#define TFA98XX_CTRL_SAAM_PGA_SAAMPGACTRL_LEN 1 +#define TFA98XX_CTRL_SAAM_PGA_SAAMPGACTRL_MAX 1 +#define TFA98XX_CTRL_SAAM_PGA_SAAMPGACTRL_MSK 0x20 + + +/* + * (0x25)-misc_ctrl + */ + +/* + * pll_fcco + */ +#define TFA98XX_MISC_CTRL_PLLCCOSEL (0x1<<0) +#define TFA98XX_MISC_CTRL_PLLCCOSEL_POS 0 +#define TFA98XX_MISC_CTRL_PLLCCOSEL_LEN 1 +#define TFA98XX_MISC_CTRL_PLLCCOSEL_MAX 1 +#define TFA98XX_MISC_CTRL_PLLCCOSEL_MSK 0x1 + + +/* + * (0x46)-CurrentSense1 + */ + +/* + * bypass_gc + */ +#define TFA98XX_CURRENTSENSE1_CSBYPGC (0x1<<0) +#define TFA98XX_CURRENTSENSE1_CSBYPGC_POS 0 +#define TFA98XX_CURRENTSENSE1_CSBYPGC_LEN 1 +#define TFA98XX_CURRENTSENSE1_CSBYPGC_MAX 1 +#define TFA98XX_CURRENTSENSE1_CSBYPGC_MSK 0x1 + + +/* + * (0x49)-CurrentSense4 + */ + +/* + * ctrl_bypassclip + */ +#define TFA98XX_CURRENTSENSE4_CLIP (0x1<<0) +#define TFA98XX_CURRENTSENSE4_CLIP_POS 0 +#define TFA98XX_CURRENTSENSE4_CLIP_LEN 1 +#define TFA98XX_CURRENTSENSE4_CLIP_MAX 1 +#define TFA98XX_CURRENTSENSE4_CLIP_MSK 0x1 + +/* + * ctrl_bypassclip2 + */ +#define TFA98XX_CURRENTSENSE4_CLIP2 (0x1<<1) +#define TFA98XX_CURRENTSENSE4_CLIP2_POS 1 +#define TFA98XX_CURRENTSENSE4_CLIP2_LEN 1 +#define TFA98XX_CURRENTSENSE4_CLIP2_MAX 1 +#define TFA98XX_CURRENTSENSE4_CLIP2_MSK 0x2 + + +/* + * (0x62)-Hidden_mtp_ctrl_reg3 + */ + + +/* + * (0x70)-cf_controls + */ + +/* + * cf_rst_dsp + */ +#define TFA98XX_CF_CONTROLS_RST (0x1<<0) +#define TFA98XX_CF_CONTROLS_RST_POS 0 +#define TFA98XX_CF_CONTROLS_RST_LEN 1 +#define TFA98XX_CF_CONTROLS_RST_MAX 1 +#define TFA98XX_CF_CONTROLS_RST_MSK 0x1 + +/* + * cf_dmem + */ +#define TFA98XX_CF_CONTROLS_DMEM (0x3<<1) +#define TFA98XX_CF_CONTROLS_DMEM_POS 1 +#define TFA98XX_CF_CONTROLS_DMEM_LEN 2 +#define TFA98XX_CF_CONTROLS_DMEM_MAX 3 +#define TFA98XX_CF_CONTROLS_DMEM_MSK 0x6 + +/* + * cf_aif + */ +#define TFA98XX_CF_CONTROLS_AIF (0x1<<3) +#define TFA98XX_CF_CONTROLS_AIF_POS 3 +#define TFA98XX_CF_CONTROLS_AIF_LEN 1 +#define TFA98XX_CF_CONTROLS_AIF_MAX 1 +#define TFA98XX_CF_CONTROLS_AIF_MSK 0x8 + +/* + * cf_int + */ +#define TFA98XX_CF_CONTROLS_CFINT (0x1<<4) +#define TFA98XX_CF_CONTROLS_CFINT_POS 4 +#define TFA98XX_CF_CONTROLS_CFINT_LEN 1 +#define TFA98XX_CF_CONTROLS_CFINT_MAX 1 +#define TFA98XX_CF_CONTROLS_CFINT_MSK 0x10 + +/* + * cf_req + */ +#define TFA98XX_CF_CONTROLS_REQ (0xff<<8) +#define TFA98XX_CF_CONTROLS_REQ_POS 8 +#define TFA98XX_CF_CONTROLS_REQ_LEN 8 +#define TFA98XX_CF_CONTROLS_REQ_MAX 255 +#define TFA98XX_CF_CONTROLS_REQ_MSK 0xff00 + + +/* + * (0x71)-cf_mad + */ + +/* + * cf_madd + */ +#define TFA9891_CF_MAD_MADD (0xffff<<0) +#define TFA9891_CF_MAD_MADD_POS 0 +#define TFA9891_CF_MAD_MADD_LEN 16 +#define TFA9891_CF_MAD_MADD_MAX 65535 +#define TFA9891_CF_MAD_MADD_MSK 0xffff + + +/* + * (0x72)-cf_mem + */ + +/* + * cf_mema + */ +#define TFA9891_CF_MEM_MEMA (0xffff<<0) +#define TFA9891_CF_MEM_MEMA_POS 0 +#define TFA9891_CF_MEM_MEMA_LEN 16 +#define TFA9891_CF_MEM_MEMA_MAX 65535 +#define TFA9891_CF_MEM_MEMA_MSK 0xffff + + +/* + * (0x73)-cf_status + */ + +/* + * cf_err + */ +#define TFA9891_CF_STATUS_ERR (0xff<<0) +#define TFA9891_CF_STATUS_ERR_POS 0 +#define TFA9891_CF_STATUS_ERR_LEN 8 +#define TFA9891_CF_STATUS_ERR_MAX 255 +#define TFA9891_CF_STATUS_ERR_MSK 0xff + +/* + * cf_ack + */ +#define TFA9891_CF_STATUS_ACK (0xff<<8) +#define TFA9891_CF_STATUS_ACK_POS 8 +#define TFA9891_CF_STATUS_ACK_LEN 8 +#define TFA9891_CF_STATUS_ACK_MAX 255 +#define TFA9891_CF_STATUS_ACK_MSK 0xff00 + + +/* + * (0x80)-Key2Protected_spkr_cal_mtp + */ + +/* + * calibration_onetime + */ +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPOTC (0x1<<0) +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPOTC_POS 0 +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPOTC_LEN 1 +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPOTC_MAX 1 +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPOTC_MSK 0x1 + +/* + * calibr_ron_done + */ +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPEX (0x1<<1) +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPEX_POS 1 +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPEX_LEN 1 +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPEX_MAX 1 +#define TFA98XX_KEY2PROTECTED_SPKR_CAL_MTP_MTPEX_MSK 0x2 + +#endif /* TFA9891_GENREGS_H */ diff --git a/sound/soc/codecs/inc/tfa9891_tfafieldnames.h b/sound/soc/codecs/inc/tfa9891_tfafieldnames.h new file mode 100644 index 000000000000..563eee830344 --- /dev/null +++ b/sound/soc/codecs/inc/tfa9891_tfafieldnames.h @@ -0,0 +1,515 @@ +/* + * tfa9891_tfafieldnames.h + * + * Created on: Jul 16, 2015 + * Author: wim + */ + +#ifndef TFA_INC_TFA9891_TFAFIELDNAMES_H_ +#define TFA_INC_TFA9891_TFAFIELDNAMES_H_ + +/** Filename: Tfa9891_TfaFieldnames.h + * This file was generated automatically on 07/16/15 at 15:00:02. + * Source file: TFA9891_I2C_list_V13.xls + */ + +#define TFA9891_I2CVERSION 13 + + +#define TFA9891_NAMETABLE static tfaBfName_t Tfa9891DatasheetNames[]= {\ + { 0x0, "VDDS"}, /* POR , */\ + { 0x10, "PLLS"}, /* PLL , */\ + { 0x20, "OTDS"}, /* OTP , */\ + { 0x30, "OVDS"}, /* OVP , */\ + { 0x40, "UVDS"}, /* UVP , */\ + { 0x50, "OCDS"}, /* OCP , */\ + { 0x60, "CLKS"}, /* Clocks , */\ + { 0x70, "CLIPS"}, /* CLIP , */\ + { 0x80, "MTPB"}, /* MTP , */\ + { 0x90, "DCCS"}, /* BOOST , */\ + { 0xa0, "SPKS"}, /* Speaker , */\ + { 0xb0, "ACS"}, /* cold start flag , */\ + { 0xc0, "SWS"}, /* flag engage , */\ + { 0xd0, "WDS"}, /* flag watchdog reset , */\ + { 0xe0, "AMPS"}, /* amplifier is enabled by manager , */\ + { 0xf0, "AREFS"}, /* references are enabled by manager , */\ + { 0x109, "BATS"}, /* Battery voltage readout; 0[V]..5.5[V] , */\ + { 0x208, "TEMPS"}, /* Temperature readout , */\ + { 0x307, "REV"}, /* Device Revision , */\ + { 0x402, "I2SF"}, /* I2SFormat data 1 input , */\ + { 0x431, "CHS12"}, /* ChannelSelection data1 input (In CoolFlux) , */\ + { 0x450, "CHS3"}, /* Channel Selection data 2 input (coolflux input, the DCDC converter gets the other signal), */\ + { 0x461, "CHSA"}, /* Input selection for amplifier , */\ + { 0x481, "I2SDOC"}, /* Selection for I2S data out , */\ + { 0x4a0, "DISP"}, /* idp protection , */\ + { 0x4b0, "I2SDOE"}, /* Enable data output , */\ + { 0x4c3, "I2SSR"}, /* sample rate setting , */\ + { 0x501, "BSSCR"}, /* ProtectionAttackTime , */\ + { 0x523, "BSST"}, /* ProtectionThreshold , */\ + { 0x561, "BSSRL"}, /* ProtectionMaximumReduction , */\ + { 0x582, "BSSRR"}, /* Protection Release Timer , */\ + { 0x5b1, "BSSHY"}, /* ProtectionHysterese , */\ + { 0x5e0, "BSSR"}, /* battery voltage for I2C read out only , */\ + { 0x5f0, "BSSBY"}, /* bypass clipper battery protection , */\ + { 0x600, "DPSA"}, /* Enable dynamic powerstage activation , */\ + { 0x613, "AMPSL"}, /* control slope , */\ + { 0x650, "CFSM"}, /* Soft mute in CoolFlux , */\ + { 0x670, "BSSS"}, /* batsensesteepness , */\ + { 0x687, "VOL"}, /* volume control (in CoolFlux) , */\ + { 0x702, "DCVO"}, /* Boost voltage , */\ + { 0x732, "DCMCC"}, /* Max boost coil current , */\ + { 0x7a0, "DCIE"}, /* Adaptive boost mode , */\ + { 0x7b0, "DCSR"}, /* Soft RampUp/Down mode for DCDC controller , */\ + { 0x800, "TROS"}, /* select external temperature also the ext_temp will be put on the temp read out , */\ + { 0x818, "EXTTS"}, /* external temperature setting to be given by host , */\ + { 0x900, "PWDN"}, /* ON/OFF , */\ + { 0x910, "I2CR"}, /* I2CReset , */\ + { 0x920, "CFE"}, /* EnableCoolFlux , */\ + { 0x930, "AMPE"}, /* EnableAmplifier , */\ + { 0x940, "DCA"}, /* EnableBoost , */\ + { 0x950, "SBSL"}, /* Coolflux configured , */\ + { 0x960, "AMPC"}, /* Selection on how AmplifierEnabling , */\ + { 0x970, "DCDIS"}, /* DCDC not connected , */\ + { 0x980, "PSDR"}, /* Iddq test amplifier , */\ + { 0x991, "DCCV"}, /* Coil Value , */\ + { 0x9b1, "CCFD"}, /* Selection CoolFluxClock , */\ + { 0x9d0, "ISEL"}, /* Interface Selection , */\ + { 0x9e0, "IPLL"}, /* selection input PLL for lock , */\ + { 0xa02, "DOLS"}, /* Output selection dataout left channel , */\ + { 0xa32, "DORS"}, /* Output selection dataout right channel , */\ + { 0xa62, "SPKL"}, /* Selection speaker induction , */\ + { 0xa91, "SPKR"}, /* Selection speaker impedance , */\ + { 0xab3, "DCFG"}, /* DCDC speaker current compensation gain , */\ + { 0xb07, "MTPK"}, /* MTP KEY2 register , */\ + { 0xf00, "VDDD"}, /* mask flag_por for interupt generation , */\ + { 0xf10, "OTDD"}, /* mask flag_otpok for interupt generation , */\ + { 0xf20, "OVDD"}, /* mask flag_ovpok for interupt generation , */\ + { 0xf30, "UVDD"}, /* mask flag_uvpok for interupt generation , */\ + { 0xf40, "OCDD"}, /* mask flag_ocp_alarm for interupt generation , */\ + { 0xf50, "CLKD"}, /* mask flag_clocks_stable for interupt generation , */\ + { 0xf60, "DCCD"}, /* mask flag_pwrokbst for interupt generation , */\ + { 0xf70, "SPKD"}, /* mask flag_cf_speakererror for interupt generation , */\ + { 0xf80, "WDD"}, /* mask flag_watchdog_reset for interupt generation , */\ + { 0xfe0, "INT"}, /* enabling interrupt , */\ + { 0xff0, "INTP"}, /* Setting polarity interupt , */\ + { 0x1000, "PDMSEL"}, /* Audio input interface mode , */\ + { 0x1010, "I2SMOUTEN"}, /* I2S Master enable (CLK and WS pads) , */\ + { 0x1021, "PDMORSEL"}, /* PDM Output right channel source selection , */\ + { 0x1041, "PDMOLSEL"}, /* PDM Output Left/Mono channel source selection , */\ + { 0x1061, "PADSEL"}, /* Output interface mode and ball selection , */\ + { 0x1100, "PDMOSDEN"}, /* Secure delay Cell , */\ + { 0x1110, "PDMOSDCF"}, /* Rising Falling Resync control Mux , */\ + { 0x1140, "SAAMEN"}, /* Speaker As a Mic feature ON/OFF , */\ + { 0x1150, "SAAMLPEN"}, /* speaker_as_mic low power mode (only in PDM_out mode), */\ + { 0x1160, "PDMOINTEN"}, /* PDM output interpolation ratio , */\ + { 0x1203, "PDMORG1"}, /* PDM Interpolator Right Channel DS4 G1 Gain Value , */\ + { 0x1243, "PDMORG2"}, /* PDM Interpolator Right Channel DS4 G2 Gain Value , */\ + { 0x1303, "PDMOLG1"}, /* PDM Interpolator Left Channel DS4 G1 Gain Value , */\ + { 0x1343, "PDMOLG2"}, /* PDM Interpolator Left Channel DS4 G2 Gain Value , */\ + { 0x2202, "SAAMGAIN"}, /* pga gain , */\ + { 0x2250, "SAAMPGACTRL"}, /* 0 = active input common mode voltage source at the attenuator/PGA level, */\ + { 0x2500, "PLLCCOSEL"}, /* pll cco frequency , */\ + { 0x4600, "CSBYPGC"}, /* bypass_gc, bypasses the CS gain correction , */\ + { 0x4900, "CLIP"}, /* Bypass clip control (function depending on digimux clip_x), */\ + { 0x4910, "CLIP2"}, /* Bypass clip control (function depending on digimux clip_x), */\ + { 0x62b0, "CIMTP"}, /* start copying all the data from i2cregs_mtp to mtp [Key 2 protected], */\ + { 0x7000, "RST"}, /* Reset CoolFlux DSP , */\ + { 0x7011, "DMEM"}, /* Target memory for access , */\ + { 0x7030, "AIF"}, /* Autoincrement-flag for memory-address , */\ + { 0x7040, "CFINT"}, /* Interrupt CoolFlux DSP , */\ + { 0x7087, "REQ"}, /* request for access (8 channels) , */\ + { 0x710f, "MADD"}, /* memory-address to be accessed , */\ + { 0x720f, "MEMA"}, /* activate memory access (24- or 32-bits data is written/read to/from memory, */\ + { 0x7307, "ERR"}, /* cf error Flags , */\ + { 0x7387, "ACK"}, /* acknowledge of requests (8 channels")" , */\ + { 0x8000, "MTPOTC"}, /* Calibration schedule (key2 protected) , */\ + { 0x8010, "MTPEX"}, /* (key2 protected) calibration of Ron has been executed, */\ + { 0x8045, "SWPROFIL" },\ + { 0x80a5, "SWVSTEP" },\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA9891_BITNAMETABLE static tfaBfName_t Tfa9891BitNames[]= {\ + { 0x0, "POR"}, /* POR , */\ + { 0x10, "PLL_LOCK"}, /* PLL , */\ + { 0x20, "flag_otpok"}, /* OTP , */\ + { 0x30, "flag_ovpok"}, /* OVP , */\ + { 0x40, "flag_uvpok"}, /* UVP , */\ + { 0x50, "flag_OCP_alarm"}, /* OCP , */\ + { 0x60, "flag_clocks_stable"}, /* Clocks , */\ + { 0x70, "CLIP"}, /* CLIP , */\ + { 0x80, "mtp_busy"}, /* MTP , */\ + { 0x90, "flag_pwrokbst"}, /* BOOST , */\ + { 0xa0, "flag_cf_speakererror"}, /* Speaker , */\ + { 0xb0, "flag_cold_started"}, /* cold start flag , */\ + { 0xc0, "flag_engage"}, /* flag engage , */\ + { 0xd0, "flag_watchdog_reset"}, /* flag watchdog reset , */\ + { 0xe0, "flag_enbl_amp"}, /* amplifier is enabled by manager , */\ + { 0xf0, "flag_enbl_ref"}, /* references are enabled by manager , */\ + { 0x109, "bat_adc"}, /* Battery voltage readout; 0[V]..5.5[V] , */\ + { 0x208, "temp_adc"}, /* Temperature readout , */\ + { 0x307, "rev_reg"}, /* Device Revision , */\ + { 0x402, "i2s_seti"}, /* I2SFormat data 1 input , */\ + { 0x431, "chan_sel1"}, /* ChannelSelection data1 input (In CoolFlux) , */\ + { 0x450, "lr_sw_i2si2"}, /* Channel Selection data 2 input (coolflux input, the DCDC converter gets the other signal), */\ + { 0x461, "input_sel"}, /* Input selection for amplifier , */\ + { 0x481, "datao_sel"}, /* Selection for I2S data out , */\ + { 0x4a0, "disable_idp"}, /* idp protection , */\ + { 0x4b0, "enbl_datao"}, /* Enable data output , */\ + { 0x4c3, "i2s_fs"}, /* sample rate setting , */\ + { 0x501, "vbat_prot_attacktime"}, /* ProtectionAttackTime , */\ + { 0x523, "vbat_prot_thlevel"}, /* ProtectionThreshold , */\ + { 0x561, "vbat_prot_max_reduct"}, /* ProtectionMaximumReduction , */\ + { 0x582, "vbat_prot_release_t"}, /* Protection Release Timer , */\ + { 0x5b1, "vbat_prot_hysterese"}, /* ProtectionHysterese , */\ + { 0x5d0, "reset_min_vbat"}, /* reset clipper , */\ + { 0x5e0, "sel_vbat"}, /* battery voltage for I2C read out only , */\ + { 0x5f0, "bypass_clipper"}, /* bypass clipper battery protection , */\ + { 0x600, "dpsa"}, /* Enable dynamic powerstage activation , */\ + { 0x613, "ctrl_slope"}, /* control slope , */\ + { 0x650, "cf_mute"}, /* Soft mute in CoolFlux , */\ + { 0x660, "sel_other_vamp"}, /* Input selection for the second channel of the DCDC inteligent mode detector, */\ + { 0x670, "ctrl_batsensesteepness"}, /* batsensesteepness , */\ + { 0x687, "vol"}, /* volume control (in CoolFlux) , */\ + { 0x702, "ctrl_bstvolt"}, /* Boost voltage , */\ + { 0x732, "ctrl_bstcur"}, /* Max boost coil current , */\ + { 0x761, "ctrl_slopebst_1_0"}, /* Setting for the slope of the boost converter power stage, */\ + { 0x781, "ctrl_slopebst_3_2"}, /* Setting for the part of the power transistor voltage to be used in peak current mode control, */\ + { 0x7a0, "boost_intel"}, /* Adaptive boost mode , */\ + { 0x7b0, "boost_speed"}, /* Soft RampUp/Down mode for DCDC controller , */\ + { 0x7c1, "ctrl_delay_comp_dcdc"}, /* delay compensation in current patg compared to delay in the audio path (relative) , */\ + { 0x7e0, "boost_input"}, /* Selection intelligent boost detector input , */\ + { 0x7f0, "ctrl_supplysense"}, /* ADC10 input selection , */\ + { 0x800, "ext_temp_sel"}, /* select external temperature also the ext_temp will be put on the temp read out , */\ + { 0x818, "ext_temp"}, /* external temperature setting to be given by host , */\ + { 0x8a0, "ctrl_spk_coilpvp_bst"}, /* Peak voltage protection boost converter , */\ + { 0x8b2, "ctrl_dcdc_synchronisation"}, /* DCDC synchronisation off + 7 positions , */\ + { 0x8e0, "ctrl_cs_samplevalid"}, /* sample valid moment for CS in single sample moment mode, */\ + { 0x900, "PowerDown"}, /* ON/OFF , */\ + { 0x910, "reset"}, /* I2CReset , */\ + { 0x920, "enbl_coolflux"}, /* EnableCoolFlux , */\ + { 0x930, "enbl_amplifier"}, /* EnableAmplifier , */\ + { 0x940, "enbl_boost"}, /* EnableBoost , */\ + { 0x950, "cf_configured"}, /* Coolflux configured , */\ + { 0x960, "sel_enbl_amplifier"}, /* Selection on how AmplifierEnabling , */\ + { 0x970, "dcdcoff_mode"}, /* DCDC not connected , */\ + { 0x980, "cttr_iddqtest"}, /* Iddq test amplifier , */\ + { 0x991, "ctrl_coil_value"}, /* Coil Value , */\ + { 0x9b1, "ctrl_sel_cf_clock"}, /* Selection CoolFluxClock , */\ + { 0x9d0, "intf_sel"}, /* Interface Selection , */\ + { 0x9e0, "sel_ws_bck"}, /* selection input PLL for lock , */\ + { 0xa02, "sel_i2so_l"}, /* Output selection dataout left channel , */\ + { 0xa32, "sel_i2so_r"}, /* Output selection dataout right channel , */\ + { 0xa62, "ctrl_spkr_coil"}, /* Selection speaker induction , */\ + { 0xa91, "ctrl_spr_res"}, /* Selection speaker impedance , */\ + { 0xab3, "ctrl_dcdc_spkr_i_comp_gain"}, /* DCDC speaker current compensation gain , */\ + { 0xaf0, "ctrl_dcdc_spkr_i_comp_sign"}, /* DCDC speaker current compensation sign , */\ + { 0xb07, "MTP_key2"}, /* MTP KEY2 register , */\ + { 0xc0c, "clk_sync_delay"}, /* Delay count for clock synchronisation , */\ + { 0xcf0, "enbl_clk_sync"}, /* Enable CGU clock synchronisation , */\ + { 0xd0c, "adc_sync_delay"}, /* Delay count for ADC synchronisation , */\ + { 0xdf0, "enable_adc_sync"}, /* Enable ADC synchronisation , */\ + { 0xe00, "bypass_dcdc_curr_prot"}, /* to switch off dcdc reduction with bat prot , */\ + { 0xe24, "ctrl_digtoana6_2"}, /* for extra connections digital to analog , */\ + { 0xe70, "switch_on_icomp"}, /* icomp dem switch , */\ + { 0xe87, "reserve_reg_1_7_0"}, /* reserved , */\ + { 0xf00, "flag_por_mask"}, /* mask flag_por for interupt generation , */\ + { 0xf10, "flag_otpok_mask"}, /* mask flag_otpok for interupt generation , */\ + { 0xf20, "flag_ovpok_mask"}, /* mask flag_ovpok for interupt generation , */\ + { 0xf30, "flag_uvpok_mask"}, /* mask flag_uvpok for interupt generation , */\ + { 0xf40, "flag_ocp_alarm_mask"}, /* mask flag_ocp_alarm for interupt generation , */\ + { 0xf50, "flag_clocks_stable_mask"}, /* mask flag_clocks_stable for interupt generation , */\ + { 0xf60, "flag_pwrokbst_mask"}, /* mask flag_pwrokbst for interupt generation , */\ + { 0xf70, "flag_cf_speakererror_mask"}, /* mask flag_cf_speakererror for interupt generation , */\ + { 0xf80, "flag_watchdog_reset_mask"}, /* mask flag_watchdog_reset for interupt generation , */\ + { 0xf90, "flag_lost_clk_mask"}, /* mask flag_lost_clk for interupt generation , */\ + { 0xfe0, "enable_interrupt"}, /* enabling interrupt , */\ + { 0xff0, "invert_int_polarity"}, /* Setting polarity interupt , */\ + { 0x1000, "pdm_i2s_input"}, /* Audio input interface mode , */\ + { 0x1010, "I2S_master_ena"}, /* I2S Master enable (CLK and WS pads) , */\ + { 0x1021, "pdm_out_sel_r"}, /* PDM Output right channel source selection , */\ + { 0x1041, "pdm_out_sel_l"}, /* PDM Output Left/Mono channel source selection , */\ + { 0x1061, "micdat_out_sel"}, /* Output interface mode and ball selection , */\ + { 0x1100, "secure_dly"}, /* Secure delay Cell , */\ + { 0x1110, "d_out_valid_rf_mux"}, /* Rising Falling Resync control Mux , */\ + { 0x1140, "Speak_As_Mic_en"}, /* Speaker As a Mic feature ON/OFF , */\ + { 0x1150, "speak_as_mic_lp_mode"}, /* speaker_as_mic low power mode (only in PDM_out mode), */\ + { 0x1160, "pdm_out_rate"}, /* PDM output interpolation ratio , */\ + { 0x1203, "ds4_g1_r"}, /* PDM Interpolator Right Channel DS4 G1 Gain Value , */\ + { 0x1243, "ds4_g2_r"}, /* PDM Interpolator Right Channel DS4 G2 Gain Value , */\ + { 0x1303, "ds4_g1_l"}, /* PDM Interpolator Left Channel DS4 G1 Gain Value , */\ + { 0x1343, "ds4_g2_l"}, /* PDM Interpolator Left Channel DS4 G2 Gain Value , */\ + { 0x1400, "clk_secure_dly"}, /* Secure delay Cell on clock path , */\ + { 0x1410, "data_secure_dly"}, /* Secure delay Cell enable on PDM data path , */\ + { 0x2202, "Ctrl_saam_pga_gain"}, /* pga gain , */\ + { 0x2250, "ctrl_saam_pga_src"}, /* 0 = active input common mode voltage source at the attenuator/PGA level, */\ + { 0x2300, "flag_saam_spare"}, /* spare flag , */\ + { 0x2400, "ctrl_saam_pga_tm"}, /* enables PGA test mode , */\ + { 0x2500, "pll_fcco"}, /* pll cco frequency , */\ + { 0x3000, "flag_hi_small"}, /* positive small window dcdc converter , */\ + { 0x3010, "flag_hi_large"}, /* positive large window dcdc converter , */\ + { 0x3020, "flag_lo_small"}, /* negative small window dcdc converter , */\ + { 0x3030, "flag_lo_large"}, /* negative large window dcdc converter , */\ + { 0x3040, "flag_voutcomp"}, /* flag_voutcomp, indication Vset is larger than Vbat, */\ + { 0x3050, "flag_voutcomp93"}, /* flag_voutcomp93, indication Vset is larger than 1.07* Vbat , */\ + { 0x3060, "flag_voutcomp86"}, /* flag_voutcomp86, indication Vset is larger than 1.14* Vbat , */\ + { 0x3070, "flag_hiz"}, /* flag_hiz, indication Vbst is larger than Vbat , */\ + { 0x3080, "flag_hi_peak"}, /* flag_hi_peak, indication hi_peak , */\ + { 0x3090, "flag_ocpokbst"}, /* flag_ocpokbst, indication no over current in boost converter pmos switch, */\ + { 0x30a0, "flag_peakcur"}, /* flag_peakcur, indication current is max in dcdc converter, */\ + { 0x30b0, "flag_ocpokap"}, /* flag_ocpokap, indication no over current in amplifier "a" pmos output stage, */\ + { 0x30c0, "flag_ocpokan"}, /* flag_ocpokan, indication no over current in amplifier "a" nmos output stage, */\ + { 0x30d0, "flag_ocpokbp"}, /* flag_ocpokbp, indication no over current in amplifier "b" pmos output stage, */\ + { 0x30e0, "flag_ocpokbn"}, /* flag_ocpokbn, indication no over current in amplifier"b" nmos output stage, */\ + { 0x30f0, "lost_clk"}, /* lost_clk, lost clock indication CGU , */\ + { 0x310f, "mtp_man_data_out"}, /* single word read from MTP (manual copy) , */\ + { 0x3200, "key01_locked"}, /* key01_locked, indication key 1 is locked , */\ + { 0x3210, "key02_locked"}, /* key02_locked, indication key 2 is locked , */\ + { 0x3225, "mtp_ecc_tcout"}, /* mtp_ecc_tcout , */\ + { 0x3280, "mtpctrl_valid_test_rd"}, /* mtp test readout for read , */\ + { 0x3290, "mtpctrl_valid_test_wr"}, /* mtp test readout for write , */\ + { 0x32a0, "flag_in_alarm_state"}, /* alarm state , */\ + { 0x32b0, "mtp_ecc_err2"}, /* two or more bit errors detected in MTP, can not reconstruct value, */\ + { 0x32c0, "mtp_ecc_err1"}, /* one bit error detected in MTP, reconstructed value, */\ + { 0x32d0, "mtp_mtp_hvf"}, /* high voltage ready flag for MTP , */\ + { 0x32f0, "mtp_zero_check_fail"}, /* zero check failed (tbd) for MTP , */\ + { 0x3300, "flag_adc10_ready"}, /* flag_adc10_ready, indication adc10 is ready , */\ + { 0x3310, "flag_clipa_high"}, /* flag_clipa_high, indication pmos amplifier "a" is clipping, */\ + { 0x3320, "flag_clipa_low"}, /* flag_clipa_low, indication nmos amplifier "a" is clipping, */\ + { 0x3330, "flag_clipb_high"}, /* flag_clipb_high, indication pmos amplifier "b" is clipping, */\ + { 0x3340, "flag_clipb_low"}, /* flag_clipb_low, indication nmos amplifier "b" is clipping, */\ + { 0x3359, "data_adc10_tempbat"}, /* adc 10 data output for testing , */\ + { 0x33f0, "flag_vddd_comp_nok"}, /* power switch flag 2 for testing , */\ + { 0x400f, "hid_code"}, /* hidden code , */\ + { 0x4100, "bypass_hp"}, /* Bypass_High Pass Filter , */\ + { 0x4110, "hard_mute"}, /* Hard Mute , */\ + { 0x4120, "soft_mute"}, /* Soft Mute , */\ + { 0x4134, "PWM_Delay"}, /* PWM DelayBits to set the delay , */\ + { 0x4180, "PWM_Shape"}, /* PWM Shape , */\ + { 0x4190, "PWM_BitLength"}, /* PWM Bitlength in noise shaper , */\ + { 0x4207, "ctrl_drive"}, /* drive bits to select amount of power stages amplifier, */\ + { 0x4281, "dpsalevel"}, /* DPSA Threshold level , */\ + { 0x42a1, "dpsa_release"}, /* DPSA Release time , */\ + { 0x42c0, "ctrl_coincidence"}, /* Prevent simultaneously switching of output stage , */\ + { 0x42d0, "ctrl_kickback"}, /* Prevent double pulses of output stage , */\ + { 0x42e0, "ctrl_test_sdeltaoffset"}, /* ctrl_test_sdeltaoffset , */\ + { 0x42f0, "ctrl_test_sdeltaclk"}, /* ctrl_test_sdeltaclk , */\ + { 0x4309, "ctrl_drivebst"}, /* Drive bits to select the powertransistor sections boost converter, */\ + { 0x43a0, "ctrl_ocptestbst"}, /* Boost OCP. , */\ + { 0x43c0, "enbl_hi_peak"}, /* enable for high peak comparator , */\ + { 0x43d0, "test_abistfft_enbl"}, /* FFT coolflux , */\ + { 0x43e0, "ctrl_sensetest_amp"}, /* sensetest amplifier , */\ + { 0x43f0, "test_bcontrol"}, /* test _bcontrol , */\ + { 0x4400, "ctrl_reversebst"}, /* OverCurrent Protection selection of power stage boost converter, */\ + { 0x4410, "ctrl_sensetest"}, /* Test option for the sense NMOS in booster for current mode control., */\ + { 0x4420, "enbl_engagebst"}, /* Enable power stage dcdc controller , */\ + { 0x4430, "enbl_hi_small"}, /* Enable bit of hi (small) comparator , */\ + { 0x4440, "enbl_hi_large"}, /* Enable bit of hi (large) comparator , */\ + { 0x4450, "enbl_lo_small"}, /* Enable bit of lo (small) comparator , */\ + { 0x4460, "enbl_lo_large"}, /* Enable bit of lo (large) comparator , */\ + { 0x4470, "enbl_slopecur"}, /* Enable bit of max-current dac , */\ + { 0x4480, "enbl_voutcomp"}, /* Enable vout comparators , */\ + { 0x4490, "enbl_voutcomp93"}, /* Enable vout-93 comparators , */\ + { 0x44a0, "enbl_voutcomp86"}, /* Enable vout-86 comparators , */\ + { 0x44b0, "enbl_hizcom"}, /* Enable hiz comparator , */\ + { 0x44c0, "enbl_pcdac"}, /* Enable peak current dac , */\ + { 0x44d0, "enbl_pccomp"}, /* Enable peak current comparator , */\ + { 0x44e0, "enbl_windac"}, /* Enable window dac , */\ + { 0x44f0, "enbl_powerbst"}, /* Enable line of the powerstage , */\ + { 0x4507, "ocp_thr"}, /* ocp_thr threshold level for OCP , */\ + { 0x4580, "bypass_glitchfilter"}, /* Bypass glitchfilter , */\ + { 0x4590, "bypass_ovp"}, /* Bypass OVP , */\ + { 0x45a0, "bypass_uvp"}, /* Bypass UVP , */\ + { 0x45b0, "bypass_otp"}, /* Bypass OTP , */\ + { 0x45c0, "bypass_ocp"}, /* Bypass OCP , */\ + { 0x45d0, "bypass_ocpcounter"}, /* BypassOCPCounter , */\ + { 0x45e0, "bypass_lost_clk"}, /* Bypasslost_clk detector , */\ + { 0x45f0, "vpalarm"}, /* vpalarm (uvp ovp handling) , */\ + { 0x4600, "bypass_gc"}, /* bypass_gc, bypasses the CS gain correction , */\ + { 0x4610, "cs_gain_control"}, /* gain control by means of MTP or i2c , */\ + { 0x4627, "cs_gain"}, /* + / - 128 steps in steps of 1/4 % 2's compliment , */\ + { 0x46a0, "bypass_lp"}, /* bypass Low-Pass filter in temperature sensor , */\ + { 0x46b0, "bypass_pwmcounter"}, /* bypass_pwmcounter , */\ + { 0x46c0, "ctrl_cs_negfixed"}, /* does not switch to neg , */\ + { 0x46d2, "ctrl_cs_neghyst"}, /* switches to neg depending on level , */\ + { 0x4700, "switch_fb"}, /* switch_fb , */\ + { 0x4713, "se_hyst"}, /* se_hyst , */\ + { 0x4754, "se_level"}, /* se_level , */\ + { 0x47a5, "ktemp"}, /* temperature compensation trimming , */\ + { 0x4800, "ctrl_negin"}, /* negin , */\ + { 0x4810, "ctrl_cs_sein"}, /* cs_sein , */\ + { 0x4820, "ctrl_coincidencecs"}, /* Coincidence current sense , */\ + { 0x4830, "ctrl_iddqtestbst"}, /* for iddq testing in powerstage of boost convertor , */\ + { 0x4840, "ctrl_coincidencebst"}, /* Switch protection on to prevent simultaniously switching power stages bst and amp, */\ + { 0x4851, "clock_sh_sel"}, /* Clock SH selection , */\ + { 0x4876, "delay_se_neg"}, /* delay of se and neg , */\ + { 0x48e1, "ctrl_cs_ttrack"}, /* sample & hold track time , */\ + { 0x4900, "ctrl_bypassclip"}, /* Bypass clip control (function depending on digimux clip_x), */\ + { 0x4910, "ctrl_bypassclip2"}, /* Bypass clip control (function depending on digimux clip_x), */\ + { 0x4920, "ctrl_clkgateCFoff"}, /* to disable clock gating in the coolflux , */\ + { 0x4930, "ctrl_testabst"}, /* testabst , */\ + { 0x4940, "ctrl_clipfast"}, /* clock switch for battery protection clipper, it switches back to old frequency, */\ + { 0x4950, "ctrl_cs_8ohm"}, /* 8 ohm mode for current sense (gain mode) , */\ + { 0x4960, "reserved"}, /* reserved , */\ + { 0x4974, "delay_clock_sh"}, /* delay_sh, tunes S7H delay , */\ + { 0x49c0, "inv_clksh"}, /* Invert the sample/hold clock for current sense ADC, */\ + { 0x49d0, "inv_neg"}, /* Invert neg signal , */\ + { 0x49e0, "inv_se"}, /* Invert se signal , */\ + { 0x49f0, "setse"}, /* switches between Single Ende and differentail mode, */\ + { 0x4a12, "ctrl_adc10_sel"}, /* select the input to convert the 10b ADC , */\ + { 0x4a60, "ctrl_adc10_reset"}, /* Global asynchronous reset (active HIGH) 10 bit ADC, */\ + { 0x4a81, "ctrl_adc10_test"}, /* Test mode selection signal 10 bit ADC , */\ + { 0x4aa0, "ctrl_bypass_lp_vbat"}, /* lp filter in batt sensor , */\ + { 0x4ae0, "ctrl_dc_offset"}, /* switch offset control on/off, is decimator offset control, */\ + { 0x4af0, "ctrl_tsense_hibias"}, /* bit to set the biasing in temp sensor to high , */\ + { 0x4b00, "ctrl_adc13_iset"}, /* Micadc Setting of current consumption. Debug use only, */\ + { 0x4b14, "ctrl_adc13_gain"}, /* Micadc gain setting (2-compl) , */\ + { 0x4b61, "ctrl_adc13_slowdel"}, /* Micadc Delay setting for internal clock. Debug use only, */\ + { 0x4b83, "ctrl_adc13_offset"}, /* Micadc ADC offset setting , */\ + { 0x4bc0, "ctrl_adc13_bsoinv"}, /* Micadc bit stream output invert mode for test , */\ + { 0x4bd0, "ctrl_adc13_resonator_enable"}, /* Micadc Give extra SNR with less stability. Debug use only, */\ + { 0x4be0, "ctrl_testmicadc"}, /* Mux at input of MICADC for test purpose , */\ + { 0x4c0f, "ctrl_offset"}, /* offset control for ABIST testing , */\ + { 0x4d05, "ctrl_windac"}, /* for testing direct control windac , */\ + { 0x4d65, "ctrl_peakcur"}, /* Control peakcur , */\ + { 0x4dc3, "pwm_dcc_cnt"}, /* control pwm duty cycle when enbl_pwm_dcc is 1 , */\ + { 0x4e04, "ctrl_slopecur"}, /* for testing direct control slopecur , */\ + { 0x4e53, "ctrl_dem"}, /* dyn element matching control, rest of codes are optional, */\ + { 0x4e93, "ctrl_demmismatch"}, /* dyn element matching add offset , */\ + { 0x4ed0, "enbl_pwm_dcc"}, /* to enable direct control of pwm duty cycle , */\ + { 0x5007, "gain"}, /* gain setting of the gain multiplier gain need to increase with factor 1.41 (3dB), */\ + { 0x5081, "ctrl_sourceb"}, /* Set OUTB to , */\ + { 0x50a1, "ctrl_sourcea"}, /* Set OUTA to , */\ + { 0x50c1, "ctrl_sourcebst"}, /* Sets the source of the pwmbst output to boost converter input for testing, */\ + { 0x50e1, "ctrl_test_mono"}, /* ABIST mode to add both amplifier halfs as stereo or one amplifier half as mono, */\ + { 0x5104, "pulselengthbst"}, /* pulselength setting test input for boost converter , */\ + { 0x5150, "ctrl_bypasslatchbst"}, /* bypass_latch in boost converter , */\ + { 0x5160, "invertbst"}, /* invert pwmbst test signal , */\ + { 0x5174, "pulselength"}, /* pulselength setting test input for amplifier , */\ + { 0x51c0, "ctrl_bypasslatch"}, /* bypass_latch in boost convert , */\ + { 0x51d0, "invertb"}, /* invert pwmb test signal , */\ + { 0x51e0, "inverta"}, /* invert pwma test signal , */\ + { 0x51f0, "ctrl_bypass_ctrlloop"}, /* bypass_ctrlloop bypasses the control loop of the amplifier, */\ + { 0x5200, "ctrl_test_discrete"}, /* tbd for rdson testing , */\ + { 0x5210, "ctrl_test_rdsona"}, /* tbd for rdson testing , */\ + { 0x5220, "ctrl_test_rdsonb"}, /* tbd for rdson testing , */\ + { 0x5230, "ctrl_test_rdsonbst"}, /* tbd for rdson testing , */\ + { 0x5240, "ctrl_test_cvia"}, /* tbd for rdson testing , */\ + { 0x5250, "ctrl_test_cvib"}, /* tbd for rdson testing , */\ + { 0x5260, "ctrl_test_cvibst"}, /* tbd for rdson testing , */\ + { 0x5290, "test_bypass_pwmdiscretea"}, /* for testing ( ABIST) , */\ + { 0x52a0, "test_bypass_pwmdiscreteb"}, /* for testing ( ABIST) , */\ + { 0x52b0, "ctrl_clipc_forcehigh"}, /* test signal for clipcontrol , */\ + { 0x52c0, "ctrl_clipc_forcelow"}, /* test signal for clipcontrol , */\ + { 0x52d0, "ctrl_test_sdelta"}, /* for testing ( ABIST) , */\ + { 0x52e0, "ctrl_test_swhvp"}, /* for testing ( ABIST) , */\ + { 0x52f0, "test_gain_reduction"}, /* test gain reduction , */\ + { 0x5303, "ctrl_digimux_out_test1"}, /* Digimux TEST1 out , */\ + { 0x5343, "ctrl_digimux_out_test2"}, /* Digimux TEST2 out. output flag_clipa_low depending on cntr_bypassclip setting, */\ + { 0x5383, "ctrl_digimux_out_data1"}, /* Digimux DATA1 out (output flag_clipb_high depending on cntr_bypassclip setting), */\ + { 0x53c3, "ctrl_digimux_out_data3"}, /* Digimux DATA3 out (output flag_clipx_x depending on cntr_bypassclip setting), */\ + { 0x5400, "hs_mode"}, /* hs_mode, high speed mode I2C bus , */\ + { 0x5412, "test_parametric_io"}, /* test_parametric_io for testing pads , */\ + { 0x5440, "enbl_ringo"}, /* enbl_ringo, for test purpose to check with ringo , */\ + { 0x5480, "ctrl_cliplevel"}, /* Clip level , */\ + { 0x5491, "ctrl_anamux_sel"}, /* anamux selection , */\ + { 0x54b0, "test_vdddsw_dio"}, /* to overrule the power switches for memory , */\ + { 0x54c0, "ctrl_bypass_diosw_ovp"}, /* To disable the overvoltage protection of vddd_dio_sw, */\ + { 0x54d0, "test_vddd_sw"}, /* test vdd sw , */\ + { 0x54e0, "test_vddd_sw_comp"}, /* test vdd sw comp , */\ + { 0x550e, "enbl_amp"}, /* enbl_amp for testing to enable all analoge blocks in amplifier, */\ + { 0x55f0, "fr_fsp"}, /* extr free running clock mode for testing , */\ + { 0x5600, "use_direct_ctrls"}, /* use_direct_ctrls, to overrule several functions direct for testing, */\ + { 0x5610, "rst_datapath"}, /* rst_datapath, datapath reset , */\ + { 0x5620, "rst_cgu"}, /* rst_cgu, cgu reset , */\ + { 0x5637, "enbl_ref"}, /* for testing to enable all analoge blocks in references, */\ + { 0x56b0, "enbl_engage"}, /* Enable output stage amplifier , */\ + { 0x56c0, "use_direct_clk_ctrl"}, /* use_direct_clk_ctrl, to overrule several functions direct for testing, */\ + { 0x56d0, "use_direct_pll_ctrl"}, /* use_direct_pll_ctrl, to overrule several functions direct for test, */\ + { 0x56e0, "use_direct_ctrls_2"}, /* use_direct_sourseamp_ctrls, to overrule several functions direct for testing, */\ + { 0x5707, "ctrl_anamux_out_test1"}, /* Anamux control , */\ + { 0x5782, "ctrl_zero"}, /* Bandwith control feedbackloop , */\ + { 0x57b0, "enbl_ldo_stress"}, /* LDO stress function frinch capacitors , */\ + { 0x57c0, "ctrl_ocptest"}, /* ctrl_ocptest, deactivates the over current protection in the power stages of the amplifier. The ocp flag signals stay active., */\ + { 0x57e0, "ctrl_otptest"}, /* otptest, test mode otp amplifier , */\ + { 0x57f0, "ctrl_reverse"}, /* CTRL revers , */\ + { 0x5802, "pll_mdec_msb"}, /* most significant bits pll_mdec , */\ + { 0x5833, "pll_selr"}, /* pll_selr , */\ + { 0x5874, "pll_selp"}, /* pll_selp , */\ + { 0x58c3, "pll_seli"}, /* pll_seli , */\ + { 0x5900, "pll_psel"}, /* pll_psel , */\ + { 0x5910, "use_direct_pll_psel"}, /* use_direct_pll_psel , */\ + { 0x5923, "nbck"}, /* NBCK , */\ + { 0x5960, "auto_nbck"}, /* AUTO_NBCK , */\ + { 0x5970, "pll_frm"}, /* pll_frm , */\ + { 0x5980, "pll_directi"}, /* pll_directi , */\ + { 0x5990, "pll_directo"}, /* pll_directo , */\ + { 0x59a0, "enbl_PLL"}, /* enbl_PLL , */\ + { 0x59b0, "sel_clkout"}, /* SEL_CLKOUT , */\ + { 0x59e0, "fr_lost_clk"}, /* fr_lost_clk , */\ + { 0x59f0, "pll_bypass"}, /* pll_bypass , */\ + { 0x5a0f, "tsig_freq"}, /* tsig_freq, internal sinus test generator, frequency control, */\ + { 0x5b02, "tsig_freq_msb"}, /* select internal sinus test generator, frequency control msb bits, */\ + { 0x5b30, "inject_tsig"}, /* inject_tsig, control bit to switch to internal sinus test generator, */\ + { 0x5b44, "ctrl_adc10_prog_sample"}, /* control ADC10 , */\ + { 0x5c01, "pll_ndec_msb"}, /* most significant bits of pll_ndec , */\ + { 0x5c2d, "pll_mdec"}, /* bits 13..0 of pll_mdec , */\ + { 0x5d06, "pll_pdec"}, /* pll_pdec , */\ + { 0x5d87, "pll_ndec"}, /* bits 7..0 of pll_ndec , */\ + { 0x5e00, "pdm_ch_sel_reg"}, /* PDM channel selection , */\ + { 0x5e10, "pdm_iis_rst_reg"}, /* PDM Interface reset , */\ + { 0x5e20, "clk_src_sel_reg"}, /* WS Source Selection , */\ + { 0x5e70, "pdm_resync_bypass"}, /* PDM resynchronization bypass , */\ + { 0x6007, "MTP_key1"}, /* MTP Key1 , */\ + { 0x6185, "mtp_ecc_tcin"}, /* Mtp_ecc_tcin , */\ + { 0x6203, "mtp_man_address_in"}, /* address from i2cregs for writing one word single mtp, */\ + { 0x6260, "mtp_ecc_eeb"}, /* enable code bit generation (active low!) , */\ + { 0x6270, "mtp_ecc_ecb"}, /* enable correction signal (active low!) , */\ + { 0x6280, "man_copy_mtp_to_iic"}, /* start copying single word from mtp to i2cregs_mtp , */\ + { 0x6290, "man_copy_iic_to_mtp"}, /* start copying single word from i2cregs_mtp to mtp [Key 1 protected], */\ + { 0x62a0, "auto_copy_mtp_to_iic"}, /* start copying all the data from mtp to i2cregs_mtp, */\ + { 0x62b0, "auto_copy_iic_to_mtp"}, /* start copying all the data from i2cregs_mtp to mtp [Key 2 protected], */\ + { 0x62d2, "mtp_speed_mode"}, /* Speed mode , */\ + { 0x6340, "mtp_dircet_enable"}, /* mtp_direct_enable (key1 protected) , */\ + { 0x6350, "mtp_direct_wr"}, /* mtp_direct_wr (key1 protected) direct value for mtp pin wr. To be enabled via iic2mtp_mtp_direct_enable, */\ + { 0x6360, "mtp_direct_rd"}, /* mtp_direct_rd (key1 protected) direct value for mtp pin rd. To be enabled via iic2mtp_mtp_direct_enable, */\ + { 0x6370, "mtp_direct_rst"}, /* mtp_direct_rst (key1 protected) direct value for mtp pin rst. To be enabled via iic2mtp_mtp_direct_enable, */\ + { 0x6380, "mtp_direct_ers"}, /* mtp_direct_ers (key1 protected) direct value for mtp pin ers. To be enabled via iic2mtp_mtp_direct_enable, */\ + { 0x6390, "mtp_direct_prg"}, /* mtp_direct_prg (key1 protected) direct value for mtp pin prg. To be enabled via iic2mtp_mtp_direct_enable, */\ + { 0x63a0, "mtp_direct_epp"}, /* mtp_direct_epp (key1 protected) direct value for mtp pin epp. To be enabled via iic2mtp_mtp_direct_enable, */\ + { 0x63b4, "mtp_direct_test"}, /* mtp_direct_test (key1 protected) , */\ + { 0x640f, "mtp_man_data_in"}, /* single wordt be written to MTP (manual copy) , */\ + { 0x7000, "cf_rst_dsp"}, /* Reset CoolFlux DSP , */\ + { 0x7011, "cf_dmem"}, /* Target memory for access , */\ + { 0x7030, "cf_aif"}, /* Autoincrement-flag for memory-address , */\ + { 0x7040, "cf_int"}, /* Interrupt CoolFlux DSP , */\ + { 0x7087, "cf_req"}, /* request for access (8 channels) , */\ + { 0x710f, "cf_madd"}, /* memory-address to be accessed , */\ + { 0x720f, "cf_mema"}, /* activate memory access (24- or 32-bits data is written/read to/from memory, */\ + { 0x7307, "cf_err"}, /* cf error Flags , */\ + { 0x7387, "cf_ack"}, /* acknowledge of requests (8 channels")" , */\ + { 0x8000, "calibration_onetime"}, /* Calibration schedule (key2 protected) , */\ + { 0x8010, "calibr_ron_done"}, /* (key2 protected) calibration of Ron has been executed, */\ + { 0x8105, "calibr_vout_offset"}, /* calibr_vout_offset (DCDCoffset) 2's compliment (key1 protected), */\ + { 0x8163, "calibr_delta_gain"}, /* delta gain for vamp (alpha) 2's compliment (key1 protected), */\ + { 0x81a5, "calibr_offs_amp"}, /* offset for vamp (Ampoffset) 2's compliment (key1 protected), */\ + { 0x8207, "calibr_gain_cs"}, /* gain current sense (Imeasalpha) 2's compliment (key1 protected), */\ + { 0x8284, "calibr_temp_offset"}, /* temperature offset 2's compliment (key1 protected), */\ + { 0x82d2, "calibr_temp_gain"}, /* temperature gain 2's compliment (key1 protected) , */\ + { 0x830f, "calibr_ron"}, /* Ron resistance of coil (key1 protected) , */\ + { 0x8406, "ctrl_offset_a"}, /* Offset of amplifier level shifter , */\ + { 0x8486, "ctrl_offset_b"}, /* Offset of amplifier level shifter , */\ + { 0x850f, "type_bits_HW"}, /* HW Bits , */\ + { 0x860f, "type_bits1_SW"}, /* MTP-control SW1 , */\ + { 0x870f, "type_bits2_SW"}, /* MTP-control SW2 , */\ + { 0x8a0f, "production_data1"}, /* (key1 protected) , */\ + { 0x8b0f, "production_data2"}, /* (key1 protected) , */\ + { 0x8c0f, "production_data3"}, /* (key1 protected) , */\ + { 0x8d0f, "production_data4"}, /* (key1 protected) , */\ + { 0x8e0f, "production_data5"}, /* (key1 protected) , */\ + { 0x8f0f, "production_data6"}, /* (key1 protected) , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + + +#endif /* TFA_INC_TFA9891_TFAFIELDNAMES_H_ */ diff --git a/sound/soc/codecs/inc/tfa9894_tfafieldnames.h b/sound/soc/codecs/inc/tfa9894_tfafieldnames.h new file mode 100644 index 000000000000..25c55cd8ce74 --- /dev/null +++ b/sound/soc/codecs/inc/tfa9894_tfafieldnames.h @@ -0,0 +1,1065 @@ +/** Filename: tfa9894_tfaFieldnames.h + * This file was generated automatically on 09/06/17 at 14:51:37. + * Source file: TFA9894_N1A1_I2C_RegisterMap.xlsx + */ + +#ifndef _TFA9894_TFAFIELDNAMES_H +#define _TFA9894_TFAFIELDNAMES_H + +#define TFA9894_I2CVERSION 7 + +typedef enum nxpTfa9894BfEnumList { + TFA9894_BF_PWDN = 0x0000, /*!< Powerdown control */ + TFA9894_BF_I2CR = 0x0010, /*!< I2C Reset - Auto clear */ + TFA9894_BF_CFE = 0x0020, /*!< Enable CoolFlux DSP */ + TFA9894_BF_AMPE = 0x0030, /*!< Enable Amplifier */ + TFA9894_BF_DCA = 0x0040, /*!< Enable DCDC Boost converter */ + TFA9894_BF_SBSL = 0x0050, /*!< Coolflux configured */ + TFA9894_BF_AMPC = 0x0060, /*!< CoolFlux control over amplifier */ + TFA9894_BF_INTP = 0x0071, /*!< Interrupt config */ + TFA9894_BF_FSSSEL= 0x0090, /*!< Audio sample reference */ + TFA9894_BF_BYPOCP= 0x00a0, /*!< Bypass OCP */ + TFA9894_BF_TSTOCP= 0x00b0, /*!< OCP testing control */ + TFA9894_BF_BSSS = 0x00c0, /*!< Vbat protection steepness */ + TFA9894_BF_HPFBYP= 0x00d0, /*!< Bypass High Pass Filter */ + TFA9894_BF_DPSA = 0x00e0, /*!< Enable DPSA */ + TFA9894_BF_AMPINSEL= 0x0101, /*!< Amplifier input selection */ + TFA9894_BF_MANSCONF= 0x0120, /*!< Device I2C settings configured */ + TFA9894_BF_MANCOLD= 0x0130, /*!< Execute cold start */ + TFA9894_BF_MANROBOD= 0x0140, /*!< Reaction on BOD */ + TFA9894_BF_BODE = 0x0150, /*!< Enable BOD (only in direct control mode) */ + TFA9894_BF_BODHYS= 0x0160, /*!< Enable Hysteresis of BOD */ + TFA9894_BF_BODFILT= 0x0171, /*!< BOD filter */ + TFA9894_BF_BODTHLVL= 0x0191, /*!< BOD threshold */ + TFA9894_BF_MUTETO= 0x01b0, /*!< Time out SB mute sequence */ + TFA9894_BF_MANWDE= 0x01c0, /*!< Watchdog enable */ + TFA9894_BF_OPENMTP= 0x01e0, /*!< Control for FAIM protection */ + TFA9894_BF_FAIMVBGOVRRL= 0x01f0, /*!< Overrule the enabling of VBG for faim erase/write access */ + TFA9894_BF_AUDFS = 0x0203, /*!< Audio sample rate Fs */ + TFA9894_BF_INPLEV= 0x0240, /*!< TDM output attenuation */ + TFA9894_BF_FRACTDEL= 0x0255, /*!< Current sense fractional delay */ + TFA9894_BF_TDMPRES= 0x02b1, /*!< Control for HW manager */ + TFA9894_BF_AMPOCRT= 0x02d2, /*!< Amplifier on-off criteria for shutdown */ + TFA9894_BF_REV = 0x030f, /*!< Revision info */ + TFA9894_BF_REFCKEXT= 0x0401, /*!< PLL external reference clock */ + TFA9894_BF_REFCKSEL= 0x0420, /*!< PLL internal reference clock */ + TFA9894_BF_MCLKSEL= 0x0432, /*!< Master Clock Selection */ + TFA9894_BF_MANAOOSC= 0x0460, /*!< Internal OSC1M off at PWDN */ + TFA9894_BF_ACKCLDDIS= 0x0470, /*!< Automatic PLL reference clock selection for cold start */ + TFA9894_BF_SPKSSEN= 0x0510, /*!< Enable speaker sub-system */ + TFA9894_BF_MTPSSEN= 0x0520, /*!< Enable FAIM sub-system */ + TFA9894_BF_WDTCLKEN= 0x0530, /*!< Enable Coolflux watchdog clock */ + TFA9894_BF_VDDS = 0x1000, /*!< POR */ + TFA9894_BF_PLLS = 0x1010, /*!< PLL Lock */ + TFA9894_BF_OTDS = 0x1020, /*!< OTP alarm */ + TFA9894_BF_OVDS = 0x1030, /*!< OVP alarm */ + TFA9894_BF_UVDS = 0x1040, /*!< UVP alarm */ + TFA9894_BF_OCDS = 0x1050, /*!< OCP amplifier (sticky register, clear on read) */ + TFA9894_BF_CLKS = 0x1060, /*!< Clocks stable */ + TFA9894_BF_MTPB = 0x1070, /*!< MTP busy */ + TFA9894_BF_NOCLK = 0x1080, /*!< Lost clock */ + TFA9894_BF_ACS = 0x1090, /*!< Cold Start */ + TFA9894_BF_WDS = 0x10a0, /*!< Watchdog */ + TFA9894_BF_SWS = 0x10b0, /*!< Amplifier engage */ + TFA9894_BF_AMPS = 0x10c0, /*!< Amplifier enable */ + TFA9894_BF_AREFS = 0x10d0, /*!< References enable */ + TFA9894_BF_ADCCR = 0x10e0, /*!< Control ADC */ + TFA9894_BF_BODNOK= 0x10f0, /*!< BOD Flag - VDD NOT OK */ + TFA9894_BF_DCIL = 0x1100, /*!< DCDC current limiting */ + TFA9894_BF_DCDCA = 0x1110, /*!< DCDC active (sticky register, clear on read) */ + TFA9894_BF_DCOCPOK= 0x1120, /*!< DCDC OCP nmos (sticky register, clear on read) */ + TFA9894_BF_DCHVBAT= 0x1140, /*!< DCDC level 1x */ + TFA9894_BF_DCH114= 0x1150, /*!< DCDC level 1.14x */ + TFA9894_BF_DCH107= 0x1160, /*!< DCDC level 1.07x */ + TFA9894_BF_SPKS = 0x1170, /*!< Speaker status */ + TFA9894_BF_CLKOOR= 0x1180, /*!< External clock status */ + TFA9894_BF_MANALARM= 0x1190, /*!< Alarm state */ + TFA9894_BF_TDMERR= 0x11a0, /*!< TDM error */ + TFA9894_BF_TDMLUTER= 0x11b0, /*!< TDM lookup table error */ + TFA9894_BF_OCPOAP= 0x1200, /*!< OCPOK pmos A */ + TFA9894_BF_OCPOAN= 0x1210, /*!< OCPOK nmos A */ + TFA9894_BF_OCPOBP= 0x1220, /*!< OCPOK pmos B */ + TFA9894_BF_OCPOBN= 0x1230, /*!< OCPOK nmos B */ + TFA9894_BF_CLIPS = 0x1240, /*!< Amplifier clipping */ + TFA9894_BF_MANMUTE= 0x1250, /*!< Audio mute sequence */ + TFA9894_BF_MANOPER= 0x1260, /*!< Device in Operating state */ + TFA9894_BF_LP1 = 0x1270, /*!< Low power MODE1 detection */ + TFA9894_BF_LA = 0x1280, /*!< Low amplitude detection */ + TFA9894_BF_VDDPH = 0x1290, /*!< VDDP greater than VBAT flag */ + TFA9894_BF_TDMSTAT= 0x1402, /*!< TDM Status bits */ + TFA9894_BF_MANSTATE= 0x1433, /*!< Device Manager status */ + TFA9894_BF_DCMODE= 0x14b1, /*!< DCDC mode status bits */ + TFA9894_BF_BATS = 0x1509, /*!< Battery voltage (V) */ + TFA9894_BF_TEMPS = 0x1608, /*!< IC Temperature (C) */ + TFA9894_BF_VDDPS = 0x1709, /*!< IC VDDP voltage (1023*VDDP/13V) */ + TFA9894_BF_TDME = 0x2000, /*!< Enable interface */ + TFA9894_BF_TDMSPKE= 0x2010, /*!< Control audio tdm channel in sink0 */ + TFA9894_BF_TDMDCE= 0x2020, /*!< Control audio tdm channel in sink1 */ + TFA9894_BF_TDMCSE= 0x2030, /*!< Source 0 enable */ + TFA9894_BF_TDMVSE= 0x2040, /*!< Source 1 enable */ + TFA9894_BF_TDMCFE= 0x2050, /*!< Source 2 enable */ + TFA9894_BF_TDMCF2E= 0x2060, /*!< Source 3 enable */ + TFA9894_BF_TDMCLINV= 0x2070, /*!< Reception data to BCK clock */ + TFA9894_BF_TDMFSPOL= 0x2080, /*!< FS polarity */ + TFA9894_BF_TDMDEL= 0x2090, /*!< Data delay to FS */ + TFA9894_BF_TDMADJ= 0x20a0, /*!< Data adjustment */ + TFA9894_BF_TDMOOMP= 0x20b1, /*!< Received audio compression */ + TFA9894_BF_TDMNBCK= 0x2103, /*!< TDM NBCK - Bit clock to FS ratio */ + TFA9894_BF_TDMFSLN= 0x2143, /*!< FS length (master mode only) */ + TFA9894_BF_TDMSLOTS= 0x2183, /*!< N-slots in Frame */ + TFA9894_BF_TDMTXDFO= 0x21c1, /*!< Format unused bits */ + TFA9894_BF_TDMTXUS0= 0x21e1, /*!< Format unused slots DATAO */ + TFA9894_BF_TDMSLLN= 0x2204, /*!< N-bits in slot */ + TFA9894_BF_TDMBRMG= 0x2254, /*!< N-bits remaining */ + TFA9894_BF_TDMSSIZE= 0x22a4, /*!< Sample size per slot */ + TFA9894_BF_TDMSPKS= 0x2303, /*!< TDM slot for sink 0 */ + TFA9894_BF_TDMDCS= 0x2343, /*!< TDM slot for sink 1 */ + TFA9894_BF_TDMCFSEL= 0x2381, /*!< TDM Source 2 data selection */ + TFA9894_BF_TDMCF2SEL= 0x23a1, /*!< TDM Source 3 data selection */ + TFA9894_BF_TDMCSS= 0x2403, /*!< Slot Position of source 0 data */ + TFA9894_BF_TDMVSS= 0x2443, /*!< Slot Position of source 1 data */ + TFA9894_BF_TDMCFS= 0x2483, /*!< Slot Position of source 2 data */ + TFA9894_BF_TDMCF2S= 0x24c3, /*!< Slot Position of source 3 data */ + TFA9894_BF_ISTVDDS= 0x4000, /*!< Status POR */ + TFA9894_BF_ISTBSTOC= 0x4010, /*!< Status DCDC OCP */ + TFA9894_BF_ISTOTDS= 0x4020, /*!< Status OTP alarm */ + TFA9894_BF_ISTOCPR= 0x4030, /*!< Status OCP alarm */ + TFA9894_BF_ISTUVDS= 0x4040, /*!< Status UVP alarm */ + TFA9894_BF_ISTMANALARM= 0x4050, /*!< Status manager alarm state */ + TFA9894_BF_ISTTDMER= 0x4060, /*!< Status TDM error */ + TFA9894_BF_ISTNOCLK= 0x4070, /*!< Status lost clock */ + TFA9894_BF_ISTCFMER= 0x4080, /*!< Status cfma error */ + TFA9894_BF_ISTCFMAC= 0x4090, /*!< Status cfma ack */ + TFA9894_BF_ISTSPKS= 0x40a0, /*!< Status coolflux speaker error */ + TFA9894_BF_ISTACS= 0x40b0, /*!< Status cold started */ + TFA9894_BF_ISTWDS= 0x40c0, /*!< Status watchdog reset */ + TFA9894_BF_ISTBODNOK= 0x40d0, /*!< Status brown out detect */ + TFA9894_BF_ISTLP1= 0x40e0, /*!< Status low power mode1 detect */ + TFA9894_BF_ISTCLKOOR= 0x40f0, /*!< Status clock out of range */ + TFA9894_BF_ICLVDDS= 0x4400, /*!< Clear POR */ + TFA9894_BF_ICLBSTOC= 0x4410, /*!< Clear DCDC OCP */ + TFA9894_BF_ICLOTDS= 0x4420, /*!< Clear OTP alarm */ + TFA9894_BF_ICLOCPR= 0x4430, /*!< Clear OCP alarm */ + TFA9894_BF_ICLUVDS= 0x4440, /*!< Clear UVP alarm */ + TFA9894_BF_ICLMANALARM= 0x4450, /*!< Clear manager alarm state */ + TFA9894_BF_ICLTDMER= 0x4460, /*!< Clear TDM error */ + TFA9894_BF_ICLNOCLK= 0x4470, /*!< Clear lost clk */ + TFA9894_BF_ICLCFMER= 0x4480, /*!< Clear cfma err */ + TFA9894_BF_ICLCFMAC= 0x4490, /*!< Clear cfma ack */ + TFA9894_BF_ICLSPKS= 0x44a0, /*!< Clear coolflux speaker error */ + TFA9894_BF_ICLACS= 0x44b0, /*!< Clear cold started */ + TFA9894_BF_ICLWDS= 0x44c0, /*!< Clear watchdog reset */ + TFA9894_BF_ICLBODNOK= 0x44d0, /*!< Clear brown out detect */ + TFA9894_BF_ICLLP1= 0x44e0, /*!< Clear low power mode1 detect */ + TFA9894_BF_ICLCLKOOR= 0x44f0, /*!< Clear clock out of range */ + TFA9894_BF_IEVDDS= 0x4800, /*!< Enable POR */ + TFA9894_BF_IEBSTOC= 0x4810, /*!< Enable DCDC OCP */ + TFA9894_BF_IEOTDS= 0x4820, /*!< Enable OTP alarm */ + TFA9894_BF_IEOCPR= 0x4830, /*!< Enable OCP alarm */ + TFA9894_BF_IEUVDS= 0x4840, /*!< Enable UVP alarm */ + TFA9894_BF_IEMANALARM= 0x4850, /*!< Enable Manager Alarm state */ + TFA9894_BF_IETDMER= 0x4860, /*!< Enable TDM error */ + TFA9894_BF_IENOCLK= 0x4870, /*!< Enable lost clk */ + TFA9894_BF_IECFMER= 0x4880, /*!< Enable cfma err */ + TFA9894_BF_IECFMAC= 0x4890, /*!< Enable cfma ack */ + TFA9894_BF_IESPKS= 0x48a0, /*!< Enable coolflux speaker error */ + TFA9894_BF_IEACS = 0x48b0, /*!< Enable cold started */ + TFA9894_BF_IEWDS = 0x48c0, /*!< Enable watchdog reset */ + TFA9894_BF_IEBODNOK= 0x48d0, /*!< Enable brown out detect */ + TFA9894_BF_IELP1 = 0x48e0, /*!< Enable low power mode1 detect */ + TFA9894_BF_IECLKOOR= 0x48f0, /*!< Enable clock out of range */ + TFA9894_BF_IPOVDDS= 0x4c00, /*!< Polarity POR */ + TFA9894_BF_IPOBSTOC= 0x4c10, /*!< Polarity DCDC OCP */ + TFA9894_BF_IPOOTDS= 0x4c20, /*!< Polarity OTP alarm */ + TFA9894_BF_IPOOCPR= 0x4c30, /*!< Polarity ocp alarm */ + TFA9894_BF_IPOUVDS= 0x4c40, /*!< Polarity UVP alarm */ + TFA9894_BF_IPOMANALARM= 0x4c50, /*!< Polarity manager alarm state */ + TFA9894_BF_IPOTDMER= 0x4c60, /*!< Polarity TDM error */ + TFA9894_BF_IPONOCLK= 0x4c70, /*!< Polarity lost clk */ + TFA9894_BF_IPOCFMER= 0x4c80, /*!< Polarity cfma err */ + TFA9894_BF_IPOCFMAC= 0x4c90, /*!< Polarity cfma ack */ + TFA9894_BF_IPOSPKS= 0x4ca0, /*!< Polarity coolflux speaker error */ + TFA9894_BF_IPOACS= 0x4cb0, /*!< Polarity cold started */ + TFA9894_BF_IPOWDS= 0x4cc0, /*!< Polarity watchdog reset */ + TFA9894_BF_IPOBODNOK= 0x4cd0, /*!< Polarity brown out detect */ + TFA9894_BF_IPOLP1= 0x4ce0, /*!< Polarity low power mode1 detect */ + TFA9894_BF_IPOCLKOOR= 0x4cf0, /*!< Polarity clock out of range */ + TFA9894_BF_BSSCR = 0x5001, /*!< Battery safeguard attack time */ + TFA9894_BF_BSST = 0x5023, /*!< Battery safeguard threshold voltage level */ + TFA9894_BF_BSSRL = 0x5061, /*!< Battery safeguard maximum reduction */ + TFA9894_BF_BSSRR = 0x5082, /*!< Battery safeguard release time */ + TFA9894_BF_BSSHY = 0x50b1, /*!< Battery Safeguard hysteresis */ + TFA9894_BF_BSSR = 0x50e0, /*!< Battery voltage read out */ + TFA9894_BF_BSSBY = 0x50f0, /*!< Bypass HW clipper */ + TFA9894_BF_CFSM = 0x5130, /*!< Coolflux firmware soft mute control */ + TFA9894_BF_VOL = 0x5187, /*!< CF firmware volume control */ + TFA9894_BF_CLIPCTRL= 0x5202, /*!< Clip control setting */ + TFA9894_BF_SLOPEE= 0x5230, /*!< Enables slope control */ + TFA9894_BF_SLOPESET= 0x5240, /*!< Slope speed setting (binary coded) */ + TFA9894_BF_AMPGAIN= 0x5287, /*!< Amplifier gain */ + TFA9894_BF_TDMDCG= 0x5703, /*!< Second channel gain in case of stereo using a single coil. (Total gain depending on INPLEV). (In case of mono OR stereo using 2 separate DCDC channel 1 should be disabled using TDMDCE) */ + TFA9894_BF_TDMSPKG= 0x5743, /*!< Total gain depending on INPLEV setting (channel 0) */ + TFA9894_BF_DCINSEL= 0x5781, /*!< VAMP_OUT2 input selection */ + TFA9894_BF_LNMODE= 0x5881, /*!< Low noise gain mode control */ + TFA9894_BF_LPM1MODE= 0x5ac1, /*!< Low power mode control */ + TFA9894_BF_TDMSRCMAP= 0x5d02, /*!< TDM source mapping */ + TFA9894_BF_TDMSRCAS= 0x5d31, /*!< Sensed value A */ + TFA9894_BF_TDMSRCBS= 0x5d51, /*!< Sensed value B */ + TFA9894_BF_TDMSRCACLIP= 0x5d71, /*!< Clip information (analog /digital) for source0 */ + TFA9894_BF_TDMSRCBCLIP= 0x5d91, /*!< Clip information (analog /digital) for source1 */ + TFA9894_BF_DELCURCOMP= 0x6102, /*!< Delay to allign compensation signal with current sense signal */ + TFA9894_BF_SIGCURCOMP= 0x6130, /*!< Polarity of compensation for current sense */ + TFA9894_BF_ENCURCOMP= 0x6140, /*!< Enable current sense compensation */ + TFA9894_BF_LVLCLPPWM= 0x6152, /*!< Set the amount of pwm pulse that may be skipped before clip-flag is triggered */ + TFA9894_BF_DCVOF = 0x7005, /*!< First Boost Voltage Level */ + TFA9894_BF_DCVOS = 0x7065, /*!< Second Boost Voltage Level */ + TFA9894_BF_DCMCC = 0x70c3, /*!< Max Coil Current */ + TFA9894_BF_DCCV = 0x7101, /*!< Slope compensation current, represents LxF (inductance x frequency) value */ + TFA9894_BF_DCIE = 0x7120, /*!< Adaptive boost mode */ + TFA9894_BF_DCSR = 0x7130, /*!< Soft ramp up/down */ + TFA9894_BF_DCDIS = 0x7140, /*!< DCDC on/off */ + TFA9894_BF_DCPWM = 0x7150, /*!< DCDC PWM only mode */ + TFA9894_BF_DCTRACK= 0x7160, /*!< Boost algorithm selection, effective only when boost_intelligent is set to 1 */ + TFA9894_BF_DCTRIP= 0x7204, /*!< 1st adaptive boost trip levels, effective only when DCIE is set to 1 */ + TFA9894_BF_DCTRIP2= 0x7254, /*!< 2nd adaptive boost trip levels, effective only when DCIE is set to 1 */ + TFA9894_BF_DCTRIPT= 0x72a4, /*!< Track adaptive boost trip levels, effective only when boost_intelligent is set to 1 */ + TFA9894_BF_DCTRIPHYSTE= 0x72f0, /*!< Enable hysteresis on booster trip levels */ + TFA9894_BF_DCHOLD= 0x7304, /*!< Hold time for DCDC booster, effective only when boost_intelligent is set to 1 */ + TFA9894_BF_RST = 0x9000, /*!< Reset for Coolflux DSP */ + TFA9894_BF_DMEM = 0x9011, /*!< Target memory for CFMA using I2C interface */ + TFA9894_BF_AIF = 0x9030, /*!< Auto increment */ + TFA9894_BF_CFINT = 0x9040, /*!< Coolflux Interrupt - auto clear */ + TFA9894_BF_CFCGATE= 0x9050, /*!< Coolflux clock gating disabling control */ + TFA9894_BF_REQCMD= 0x9080, /*!< Firmware event request rpc command */ + TFA9894_BF_REQRST= 0x9090, /*!< Firmware event request reset restart */ + TFA9894_BF_REQMIPS= 0x90a0, /*!< Firmware event request short on mips */ + TFA9894_BF_REQMUTED= 0x90b0, /*!< Firmware event request mute sequence ready */ + TFA9894_BF_REQVOL= 0x90c0, /*!< Firmware event request volume ready */ + TFA9894_BF_REQDMG= 0x90d0, /*!< Firmware event request speaker damage detected */ + TFA9894_BF_REQCAL= 0x90e0, /*!< Firmware event request calibration completed */ + TFA9894_BF_REQRSV= 0x90f0, /*!< Firmware event request reserved */ + TFA9894_BF_MADD = 0x910f, /*!< CF memory address */ + TFA9894_BF_MEMA = 0x920f, /*!< Activate memory access */ + TFA9894_BF_ERR = 0x9307, /*!< CF error flags */ + TFA9894_BF_ACKCMD= 0x9380, /*!< Firmware event acknowledge rpc command */ + TFA9894_BF_ACKRST= 0x9390, /*!< Firmware event acknowledge reset restart */ + TFA9894_BF_ACKMIPS= 0x93a0, /*!< Firmware event acknowledge short on mips */ + TFA9894_BF_ACKMUTED= 0x93b0, /*!< Firmware event acknowledge mute sequence ready */ + TFA9894_BF_ACKVOL= 0x93c0, /*!< Firmware event acknowledge volume ready */ + TFA9894_BF_ACKDMG= 0x93d0, /*!< Firmware event acknowledge speaker damage detected */ + TFA9894_BF_ACKCAL= 0x93e0, /*!< Firmware event acknowledge calibration completed */ + TFA9894_BF_ACKRSV= 0x93f0, /*!< Firmware event acknowledge reserved */ + TFA9894_BF_MTPK = 0xa107, /*!< KEY2 to access KEY2 protected registers, customer key */ + TFA9894_BF_KEY1LOCKED= 0xa200, /*!< Indicates KEY1 is locked */ + TFA9894_BF_KEY2LOCKED= 0xa210, /*!< Indicates KEY2 is locked */ + TFA9894_BF_CMTPI = 0xa350, /*!< Start copying all the data from mtp to I2C mtp registers - auto clear */ + TFA9894_BF_CIMTP = 0xa360, /*!< Start copying data from I2C mtp registers to mtp - auto clear */ + TFA9894_BF_MTPRDMSB= 0xa50f, /*!< MSB word of MTP manual read data */ + TFA9894_BF_MTPRDLSB= 0xa60f, /*!< LSB word of MTP manual read data */ + TFA9894_BF_EXTTS = 0xb108, /*!< External temperature (C) */ + TFA9894_BF_TROS = 0xb190, /*!< Select temp Speaker calibration */ + TFA9894_BF_SWPROFIL= 0xe00f, /*!< Software profile data */ + TFA9894_BF_SWVSTEP= 0xe10f, /*!< Software vstep information */ + TFA9894_BF_MTPOTC= 0xf000, /*!< Calibration schedule */ + TFA9894_BF_MTPEX = 0xf010, /*!< Calibration Ron executed */ + TFA9894_BF_DCMCCAPI= 0xf020, /*!< Calibration current limit DCDC */ + TFA9894_BF_DCMCCSB= 0xf030, /*!< Sign bit for delta calibration current limit DCDC */ + TFA9894_BF_USERDEF= 0xf042, /*!< Calibration delta current limit DCDC */ + TFA9894_BF_CUSTINFO= 0xf078, /*!< Reserved space for allowing customer to store speaker information */ + TFA9894_BF_R25C = 0xf50f, /*!< Ron resistance of speaker coil */ +} nxpTfa9894BfEnumList_t; +#define TFA9894_NAMETABLE static tfaBfName_t Tfa9894DatasheetNames[]= {\ + { 0x0, "PWDN"}, /* Powerdown control , */\ + { 0x10, "I2CR"}, /* I2C Reset - Auto clear , */\ + { 0x20, "CFE"}, /* Enable CoolFlux DSP , */\ + { 0x30, "AMPE"}, /* Enable Amplifier , */\ + { 0x40, "DCA"}, /* Enable DCDC Boost converter , */\ + { 0x50, "SBSL"}, /* Coolflux configured , */\ + { 0x60, "AMPC"}, /* CoolFlux control over amplifier , */\ + { 0x71, "INTP"}, /* Interrupt config , */\ + { 0x90, "FSSSEL"}, /* Audio sample reference , */\ + { 0xa0, "BYPOCP"}, /* Bypass OCP , */\ + { 0xb0, "TSTOCP"}, /* OCP testing control , */\ + { 0xc0, "BSSS"}, /* Vbat protection steepness , */\ + { 0xd0, "HPFBYP"}, /* Bypass High Pass Filter , */\ + { 0xe0, "DPSA"}, /* Enable DPSA , */\ + { 0x101, "AMPINSEL"}, /* Amplifier input selection , */\ + { 0x120, "MANSCONF"}, /* Device I2C settings configured , */\ + { 0x130, "MANCOLD"}, /* Execute cold start , */\ + { 0x140, "MANROBOD"}, /* Reaction on BOD , */\ + { 0x150, "BODE"}, /* Enable BOD (only in direct control mode) , */\ + { 0x160, "BODHYS"}, /* Enable Hysteresis of BOD , */\ + { 0x171, "BODFILT"}, /* BOD filter , */\ + { 0x191, "BODTHLVL"}, /* BOD threshold , */\ + { 0x1b0, "MUTETO"}, /* Time out SB mute sequence , */\ + { 0x1c0, "MANWDE"}, /* Watchdog enable , */\ + { 0x1e0, "OPENMTP"}, /* Control for FAIM protection , */\ + { 0x1f0, "FAIMVBGOVRRL"}, /* Overrule the enabling of VBG for faim erase/write access, */\ + { 0x203, "AUDFS"}, /* Audio sample rate Fs , */\ + { 0x240, "INPLEV"}, /* TDM output attenuation , */\ + { 0x255, "FRACTDEL"}, /* Current sense fractional delay , */\ + { 0x2b1, "TDMPRES"}, /* Control for HW manager , */\ + { 0x2d2, "AMPOCRT"}, /* Amplifier on-off criteria for shutdown , */\ + { 0x30f, "REV"}, /* Revision info , */\ + { 0x401, "REFCKEXT"}, /* PLL external reference clock , */\ + { 0x420, "REFCKSEL"}, /* PLL internal reference clock , */\ + { 0x432, "MCLKSEL"}, /* Master Clock Selection , */\ + { 0x460, "MANAOOSC"}, /* Internal OSC1M off at PWDN , */\ + { 0x470, "ACKCLDDIS"}, /* Automatic PLL reference clock selection for cold start, */\ + { 0x510, "SPKSSEN"}, /* Enable speaker sub-system , */\ + { 0x520, "MTPSSEN"}, /* Enable FAIM sub-system , */\ + { 0x530, "WDTCLKEN"}, /* Enable Coolflux watchdog clock , */\ + { 0x1000, "VDDS"}, /* POR , */\ + { 0x1010, "PLLS"}, /* PLL Lock , */\ + { 0x1020, "OTDS"}, /* OTP alarm , */\ + { 0x1030, "OVDS"}, /* OVP alarm , */\ + { 0x1040, "UVDS"}, /* UVP alarm , */\ + { 0x1050, "OCDS"}, /* OCP amplifier (sticky register, clear on read) , */\ + { 0x1060, "CLKS"}, /* Clocks stable , */\ + { 0x1070, "MTPB"}, /* MTP busy , */\ + { 0x1080, "NOCLK"}, /* Lost clock , */\ + { 0x1090, "ACS"}, /* Cold Start , */\ + { 0x10a0, "WDS"}, /* Watchdog , */\ + { 0x10b0, "SWS"}, /* Amplifier engage , */\ + { 0x10c0, "AMPS"}, /* Amplifier enable , */\ + { 0x10d0, "AREFS"}, /* References enable , */\ + { 0x10e0, "ADCCR"}, /* Control ADC , */\ + { 0x10f0, "BODNOK"}, /* BOD Flag - VDD NOT OK , */\ + { 0x1100, "DCIL"}, /* DCDC current limiting , */\ + { 0x1110, "DCDCA"}, /* DCDC active (sticky register, clear on read) , */\ + { 0x1120, "DCOCPOK"}, /* DCDC OCP nmos (sticky register, clear on read) , */\ + { 0x1140, "DCHVBAT"}, /* DCDC level 1x , */\ + { 0x1150, "DCH114"}, /* DCDC level 1.14x , */\ + { 0x1160, "DCH107"}, /* DCDC level 1.07x , */\ + { 0x1170, "SPKS"}, /* Speaker status , */\ + { 0x1180, "CLKOOR"}, /* External clock status , */\ + { 0x1190, "MANALARM"}, /* Alarm state , */\ + { 0x11a0, "TDMERR"}, /* TDM error , */\ + { 0x11b0, "TDMLUTER"}, /* TDM lookup table error , */\ + { 0x1200, "OCPOAP"}, /* OCPOK pmos A , */\ + { 0x1210, "OCPOAN"}, /* OCPOK nmos A , */\ + { 0x1220, "OCPOBP"}, /* OCPOK pmos B , */\ + { 0x1230, "OCPOBN"}, /* OCPOK nmos B , */\ + { 0x1240, "CLIPS"}, /* Amplifier clipping , */\ + { 0x1250, "MANMUTE"}, /* Audio mute sequence , */\ + { 0x1260, "MANOPER"}, /* Device in Operating state , */\ + { 0x1270, "LP1"}, /* Low power MODE1 detection , */\ + { 0x1280, "LA"}, /* Low amplitude detection , */\ + { 0x1290, "VDDPH"}, /* VDDP greater than VBAT flag , */\ + { 0x1402, "TDMSTAT"}, /* TDM Status bits , */\ + { 0x1433, "MANSTATE"}, /* Device Manager status , */\ + { 0x14b1, "DCMODE"}, /* DCDC mode status bits , */\ + { 0x1509, "BATS"}, /* Battery voltage (V) , */\ + { 0x1608, "TEMPS"}, /* IC Temperature (C) , */\ + { 0x1709, "VDDPS"}, /* IC VDDP voltage (1023*VDDP/13V) , */\ + { 0x2000, "TDME"}, /* Enable interface , */\ + { 0x2010, "TDMSPKE"}, /* Control audio tdm channel in sink0 , */\ + { 0x2020, "TDMDCE"}, /* Control audio tdm channel in sink1 , */\ + { 0x2030, "TDMCSE"}, /* Source 0 enable , */\ + { 0x2040, "TDMVSE"}, /* Source 1 enable , */\ + { 0x2050, "TDMCFE"}, /* Source 2 enable , */\ + { 0x2060, "TDMCF2E"}, /* Source 3 enable , */\ + { 0x2070, "TDMCLINV"}, /* Reception data to BCK clock , */\ + { 0x2080, "TDMFSPOL"}, /* FS polarity , */\ + { 0x2090, "TDMDEL"}, /* Data delay to FS , */\ + { 0x20a0, "TDMADJ"}, /* Data adjustment , */\ + { 0x20b1, "TDMOOMP"}, /* Received audio compression , */\ + { 0x2103, "TDMNBCK"}, /* TDM NBCK - Bit clock to FS ratio , */\ + { 0x2143, "TDMFSLN"}, /* FS length (master mode only) , */\ + { 0x2183, "TDMSLOTS"}, /* N-slots in Frame , */\ + { 0x21c1, "TDMTXDFO"}, /* Format unused bits , */\ + { 0x21e1, "TDMTXUS0"}, /* Format unused slots DATAO , */\ + { 0x2204, "TDMSLLN"}, /* N-bits in slot , */\ + { 0x2254, "TDMBRMG"}, /* N-bits remaining , */\ + { 0x22a4, "TDMSSIZE"}, /* Sample size per slot , */\ + { 0x2303, "TDMSPKS"}, /* TDM slot for sink 0 , */\ + { 0x2343, "TDMDCS"}, /* TDM slot for sink 1 , */\ + { 0x2381, "TDMCFSEL"}, /* TDM Source 2 data selection , */\ + { 0x23a1, "TDMCF2SEL"}, /* TDM Source 3 data selection , */\ + { 0x2403, "TDMCSS"}, /* Slot Position of source 0 data , */\ + { 0x2443, "TDMVSS"}, /* Slot Position of source 1 data , */\ + { 0x2483, "TDMCFS"}, /* Slot Position of source 2 data , */\ + { 0x24c3, "TDMCF2S"}, /* Slot Position of source 3 data , */\ + { 0x4000, "ISTVDDS"}, /* Status POR , */\ + { 0x4010, "ISTBSTOC"}, /* Status DCDC OCP , */\ + { 0x4020, "ISTOTDS"}, /* Status OTP alarm , */\ + { 0x4030, "ISTOCPR"}, /* Status OCP alarm , */\ + { 0x4040, "ISTUVDS"}, /* Status UVP alarm , */\ + { 0x4050, "ISTMANALARM"}, /* Status manager alarm state , */\ + { 0x4060, "ISTTDMER"}, /* Status TDM error , */\ + { 0x4070, "ISTNOCLK"}, /* Status lost clock , */\ + { 0x4080, "ISTCFMER"}, /* Status cfma error , */\ + { 0x4090, "ISTCFMAC"}, /* Status cfma ack , */\ + { 0x40a0, "ISTSPKS"}, /* Status coolflux speaker error , */\ + { 0x40b0, "ISTACS"}, /* Status cold started , */\ + { 0x40c0, "ISTWDS"}, /* Status watchdog reset , */\ + { 0x40d0, "ISTBODNOK"}, /* Status brown out detect , */\ + { 0x40e0, "ISTLP1"}, /* Status low power mode1 detect , */\ + { 0x40f0, "ISTCLKOOR"}, /* Status clock out of range , */\ + { 0x4400, "ICLVDDS"}, /* Clear POR , */\ + { 0x4410, "ICLBSTOC"}, /* Clear DCDC OCP , */\ + { 0x4420, "ICLOTDS"}, /* Clear OTP alarm , */\ + { 0x4430, "ICLOCPR"}, /* Clear OCP alarm , */\ + { 0x4440, "ICLUVDS"}, /* Clear UVP alarm , */\ + { 0x4450, "ICLMANALARM"}, /* Clear manager alarm state , */\ + { 0x4460, "ICLTDMER"}, /* Clear TDM error , */\ + { 0x4470, "ICLNOCLK"}, /* Clear lost clk , */\ + { 0x4480, "ICLCFMER"}, /* Clear cfma err , */\ + { 0x4490, "ICLCFMAC"}, /* Clear cfma ack , */\ + { 0x44a0, "ICLSPKS"}, /* Clear coolflux speaker error , */\ + { 0x44b0, "ICLACS"}, /* Clear cold started , */\ + { 0x44c0, "ICLWDS"}, /* Clear watchdog reset , */\ + { 0x44d0, "ICLBODNOK"}, /* Clear brown out detect , */\ + { 0x44e0, "ICLLP1"}, /* Clear low power mode1 detect , */\ + { 0x44f0, "ICLCLKOOR"}, /* Clear clock out of range , */\ + { 0x4800, "IEVDDS"}, /* Enable POR , */\ + { 0x4810, "IEBSTOC"}, /* Enable DCDC OCP , */\ + { 0x4820, "IEOTDS"}, /* Enable OTP alarm , */\ + { 0x4830, "IEOCPR"}, /* Enable OCP alarm , */\ + { 0x4840, "IEUVDS"}, /* Enable UVP alarm , */\ + { 0x4850, "IEMANALARM"}, /* Enable Manager Alarm state , */\ + { 0x4860, "IETDMER"}, /* Enable TDM error , */\ + { 0x4870, "IENOCLK"}, /* Enable lost clk , */\ + { 0x4880, "IECFMER"}, /* Enable cfma err , */\ + { 0x4890, "IECFMAC"}, /* Enable cfma ack , */\ + { 0x48a0, "IESPKS"}, /* Enable coolflux speaker error , */\ + { 0x48b0, "IEACS"}, /* Enable cold started , */\ + { 0x48c0, "IEWDS"}, /* Enable watchdog reset , */\ + { 0x48d0, "IEBODNOK"}, /* Enable brown out detect , */\ + { 0x48e0, "IELP1"}, /* Enable low power mode1 detect , */\ + { 0x48f0, "IECLKOOR"}, /* Enable clock out of range , */\ + { 0x4c00, "IPOVDDS"}, /* Polarity POR , */\ + { 0x4c10, "IPOBSTOC"}, /* Polarity DCDC OCP , */\ + { 0x4c20, "IPOOTDS"}, /* Polarity OTP alarm , */\ + { 0x4c30, "IPOOCPR"}, /* Polarity ocp alarm , */\ + { 0x4c40, "IPOUVDS"}, /* Polarity UVP alarm , */\ + { 0x4c50, "IPOMANALARM"}, /* Polarity manager alarm state , */\ + { 0x4c60, "IPOTDMER"}, /* Polarity TDM error , */\ + { 0x4c70, "IPONOCLK"}, /* Polarity lost clk , */\ + { 0x4c80, "IPOCFMER"}, /* Polarity cfma err , */\ + { 0x4c90, "IPOCFMAC"}, /* Polarity cfma ack , */\ + { 0x4ca0, "IPOSPKS"}, /* Polarity coolflux speaker error , */\ + { 0x4cb0, "IPOACS"}, /* Polarity cold started , */\ + { 0x4cc0, "IPOWDS"}, /* Polarity watchdog reset , */\ + { 0x4cd0, "IPOBODNOK"}, /* Polarity brown out detect , */\ + { 0x4ce0, "IPOLP1"}, /* Polarity low power mode1 detect , */\ + { 0x4cf0, "IPOCLKOOR"}, /* Polarity clock out of range , */\ + { 0x5001, "BSSCR"}, /* Battery safeguard attack time , */\ + { 0x5023, "BSST"}, /* Battery safeguard threshold voltage level , */\ + { 0x5061, "BSSRL"}, /* Battery safeguard maximum reduction , */\ + { 0x5082, "BSSRR"}, /* Battery safeguard release time , */\ + { 0x50b1, "BSSHY"}, /* Battery Safeguard hysteresis , */\ + { 0x50e0, "BSSR"}, /* Battery voltage read out , */\ + { 0x50f0, "BSSBY"}, /* Bypass HW clipper , */\ + { 0x5130, "CFSM"}, /* Coolflux firmware soft mute control , */\ + { 0x5187, "VOL"}, /* CF firmware volume control , */\ + { 0x5202, "CLIPCTRL"}, /* Clip control setting , */\ + { 0x5230, "SLOPEE"}, /* Enables slope control , */\ + { 0x5240, "SLOPESET"}, /* Slope speed setting (binary coded) , */\ + { 0x5287, "AMPGAIN"}, /* Amplifier gain , */\ + { 0x5703, "TDMDCG"}, /* Second channel gain in case of stereo using a single coil. (Total gain depending on INPLEV). (In case of mono OR stereo using 2 separate DCDC channel 1 should be disabled using TDMDCE), */\ + { 0x5743, "TDMSPKG"}, /* Total gain depending on INPLEV setting (channel 0), */\ + { 0x5781, "DCINSEL"}, /* VAMP_OUT2 input selection , */\ + { 0x5881, "LNMODE"}, /* Low noise gain mode control , */\ + { 0x5ac1, "LPM1MODE"}, /* Low power mode control , */\ + { 0x5d02, "TDMSRCMAP"}, /* TDM source mapping , */\ + { 0x5d31, "TDMSRCAS"}, /* Sensed value A , */\ + { 0x5d51, "TDMSRCBS"}, /* Sensed value B , */\ + { 0x5d71, "TDMSRCACLIP"}, /* Clip information (analog /digital) for source0 , */\ + { 0x5d91, "TDMSRCBCLIP"}, /* Clip information (analog /digital) for source1 , */\ + { 0x6102, "DELCURCOMP"}, /* Delay to allign compensation signal with current sense signal, */\ + { 0x6130, "SIGCURCOMP"}, /* Polarity of compensation for current sense , */\ + { 0x6140, "ENCURCOMP"}, /* Enable current sense compensation , */\ + { 0x6152, "LVLCLPPWM"}, /* Set the amount of pwm pulse that may be skipped before clip-flag is triggered, */\ + { 0x7005, "DCVOF"}, /* First Boost Voltage Level , */\ + { 0x7065, "DCVOS"}, /* Second Boost Voltage Level , */\ + { 0x70c3, "DCMCC"}, /* Max Coil Current , */\ + { 0x7101, "DCCV"}, /* Slope compensation current, represents LxF (inductance x frequency) value , */\ + { 0x7120, "DCIE"}, /* Adaptive boost mode , */\ + { 0x7130, "DCSR"}, /* Soft ramp up/down , */\ + { 0x7140, "DCDIS"}, /* DCDC on/off , */\ + { 0x7150, "DCPWM"}, /* DCDC PWM only mode , */\ + { 0x7160, "DCTRACK"}, /* Boost algorithm selection, effective only when boost_intelligent is set to 1, */\ + { 0x7204, "DCTRIP"}, /* 1st adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7254, "DCTRIP2"}, /* 2nd adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x72a4, "DCTRIPT"}, /* Track adaptive boost trip levels, effective only when boost_intelligent is set to 1, */\ + { 0x72f0, "DCTRIPHYSTE"}, /* Enable hysteresis on booster trip levels , */\ + { 0x7304, "DCHOLD"}, /* Hold time for DCDC booster, effective only when boost_intelligent is set to 1, */\ + { 0x9000, "RST"}, /* Reset for Coolflux DSP , */\ + { 0x9011, "DMEM"}, /* Target memory for CFMA using I2C interface , */\ + { 0x9030, "AIF"}, /* Auto increment , */\ + { 0x9040, "CFINT"}, /* Coolflux Interrupt - auto clear , */\ + { 0x9050, "CFCGATE"}, /* Coolflux clock gating disabling control , */\ + { 0x9080, "REQCMD"}, /* Firmware event request rpc command , */\ + { 0x9090, "REQRST"}, /* Firmware event request reset restart , */\ + { 0x90a0, "REQMIPS"}, /* Firmware event request short on mips , */\ + { 0x90b0, "REQMUTED"}, /* Firmware event request mute sequence ready , */\ + { 0x90c0, "REQVOL"}, /* Firmware event request volume ready , */\ + { 0x90d0, "REQDMG"}, /* Firmware event request speaker damage detected , */\ + { 0x90e0, "REQCAL"}, /* Firmware event request calibration completed , */\ + { 0x90f0, "REQRSV"}, /* Firmware event request reserved , */\ + { 0x910f, "MADD"}, /* CF memory address , */\ + { 0x920f, "MEMA"}, /* Activate memory access , */\ + { 0x9307, "ERR"}, /* CF error flags , */\ + { 0x9380, "ACKCMD"}, /* Firmware event acknowledge rpc command , */\ + { 0x9390, "ACKRST"}, /* Firmware event acknowledge reset restart , */\ + { 0x93a0, "ACKMIPS"}, /* Firmware event acknowledge short on mips , */\ + { 0x93b0, "ACKMUTED"}, /* Firmware event acknowledge mute sequence ready , */\ + { 0x93c0, "ACKVOL"}, /* Firmware event acknowledge volume ready , */\ + { 0x93d0, "ACKDMG"}, /* Firmware event acknowledge speaker damage detected, */\ + { 0x93e0, "ACKCAL"}, /* Firmware event acknowledge calibration completed , */\ + { 0x93f0, "ACKRSV"}, /* Firmware event acknowledge reserved , */\ + { 0xa107, "MTPK"}, /* KEY2 to access KEY2 protected registers, customer key, */\ + { 0xa200, "KEY1LOCKED"}, /* Indicates KEY1 is locked , */\ + { 0xa210, "KEY2LOCKED"}, /* Indicates KEY2 is locked , */\ + { 0xa350, "CMTPI"}, /* Start copying all the data from mtp to I2C mtp registers - auto clear, */\ + { 0xa360, "CIMTP"}, /* Start copying data from I2C mtp registers to mtp - auto clear, */\ + { 0xa50f, "MTPRDMSB"}, /* MSB word of MTP manual read data , */\ + { 0xa60f, "MTPRDLSB"}, /* LSB word of MTP manual read data , */\ + { 0xb108, "EXTTS"}, /* External temperature (C) , */\ + { 0xb190, "TROS"}, /* Select temp Speaker calibration , */\ + { 0xe00f, "SWPROFIL"}, /* Software profile data , */\ + { 0xe10f, "SWVSTEP"}, /* Software vstep information , */\ + { 0xf000, "MTPOTC"}, /* Calibration schedule , */\ + { 0xf010, "MTPEX"}, /* Calibration Ron executed , */\ + { 0xf020, "DCMCCAPI"}, /* Calibration current limit DCDC , */\ + { 0xf030, "DCMCCSB"}, /* Sign bit for delta calibration current limit DCDC , */\ + { 0xf042, "USERDEF"}, /* Calibration delta current limit DCDC , */\ + { 0xf078, "CUSTINFO"}, /* Reserved space for allowing customer to store speaker information, */\ + { 0xf50f, "R25C"}, /* Ron resistance of speaker coil , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA9894_BITNAMETABLE static tfaBfName_t Tfa9894BitNames[]= {\ + { 0x0, "powerdown"}, /* Powerdown control , */\ + { 0x10, "reset"}, /* I2C Reset - Auto clear , */\ + { 0x20, "enbl_coolflux"}, /* Enable CoolFlux DSP , */\ + { 0x30, "enbl_amplifier"}, /* Enable Amplifier , */\ + { 0x40, "enbl_boost"}, /* Enable DCDC Boost converter , */\ + { 0x50, "coolflux_configured"}, /* Coolflux configured , */\ + { 0x60, "sel_enbl_amplifier"}, /* CoolFlux control over amplifier , */\ + { 0x71, "int_pad_io"}, /* Interrupt config , */\ + { 0x90, "fs_pulse_sel"}, /* Audio sample reference , */\ + { 0xa0, "bypass_ocp"}, /* Bypass OCP , */\ + { 0xb0, "test_ocp"}, /* OCP testing control , */\ + { 0xc0, "batsense_steepness"}, /* Vbat protection steepness , */\ + { 0xd0, "bypass_hp"}, /* Bypass High Pass Filter , */\ + { 0xe0, "enbl_dpsa"}, /* Enable DPSA , */\ + { 0xf0, "sel_hysteresis"}, /* Select hysteresis for clock range detector , */\ + { 0x101, "vamp_sel1"}, /* Amplifier input selection , */\ + { 0x120, "src_set_configured"}, /* Device I2C settings configured , */\ + { 0x130, "execute_cold_start"}, /* Execute cold start , */\ + { 0x140, "man_enbl_brown_out"}, /* Reaction on BOD , */\ + { 0x150, "bod_enbl"}, /* Enable BOD (only in direct control mode) , */\ + { 0x160, "bod_hyst_enbl"}, /* Enable Hysteresis of BOD , */\ + { 0x171, "bod_delay_set"}, /* BOD filter , */\ + { 0x191, "bod_lvl_set"}, /* BOD threshold , */\ + { 0x1b0, "disable_mute_time_out"}, /* Time out SB mute sequence , */\ + { 0x1c0, "man_enbl_watchdog"}, /* Watchdog enable , */\ + { 0x1d0, "disable_engage"}, /* Disable Engage , */\ + { 0x1e0, "unprotect_faim"}, /* Control for FAIM protection , */\ + { 0x1f0, "faim_enable_vbg"}, /* Overrule the enabling of VBG for faim erase/write access, */\ + { 0x203, "audio_fs"}, /* Audio sample rate Fs , */\ + { 0x240, "input_level"}, /* TDM output attenuation , */\ + { 0x255, "cs_frac_delay"}, /* Current sense fractional delay , */\ + { 0x2b1, "use_tdm_presence"}, /* Control for HW manager , */\ + { 0x2d2, "ctrl_on2off_criterion"}, /* Amplifier on-off criteria for shutdown , */\ + { 0x30f, "device_rev"}, /* Revision info , */\ + { 0x401, "pll_clkin_sel"}, /* PLL external reference clock , */\ + { 0x420, "pll_clkin_sel_osc"}, /* PLL internal reference clock , */\ + { 0x432, "mclk_sel"}, /* Master Clock Selection , */\ + { 0x460, "enbl_osc1m_auto_off"}, /* Internal OSC1M off at PWDN , */\ + { 0x470, "disable_auto_sel_refclk"}, /* Automatic PLL reference clock selection for cold start, */\ + { 0x510, "enbl_spkr_ss"}, /* Enable speaker sub-system , */\ + { 0x520, "enbl_faim_ss"}, /* Enable FAIM sub-system , */\ + { 0x530, "enbl_wdt_clk"}, /* Enable Coolflux watchdog clock , */\ + { 0xe07, "ctrl_digtoana"}, /* Spare control from digital to analog , */\ + { 0xf0f, "hidden_code"}, /* Hidden code to enable access to hidden register. (0x5A6B/23147 default for engineering), */\ + { 0x1000, "flag_por"}, /* POR , */\ + { 0x1010, "flag_pll_lock"}, /* PLL Lock , */\ + { 0x1020, "flag_otpok"}, /* OTP alarm , */\ + { 0x1030, "flag_ovpok"}, /* OVP alarm , */\ + { 0x1040, "flag_uvpok"}, /* UVP alarm , */\ + { 0x1050, "flag_ocp_alarm"}, /* OCP amplifier (sticky register, clear on read) , */\ + { 0x1060, "flag_clocks_stable"}, /* Clocks stable , */\ + { 0x1070, "flag_mtp_busy"}, /* MTP busy , */\ + { 0x1080, "flag_lost_clk"}, /* Lost clock , */\ + { 0x1090, "flag_cold_started"}, /* Cold Start , */\ + { 0x10a0, "flag_watchdog_reset"}, /* Watchdog , */\ + { 0x10b0, "flag_engage"}, /* Amplifier engage , */\ + { 0x10c0, "flag_enbl_amp"}, /* Amplifier enable , */\ + { 0x10d0, "flag_enbl_ref"}, /* References enable , */\ + { 0x10e0, "flag_adc10_ready"}, /* Control ADC , */\ + { 0x10f0, "flag_bod_vddd_nok"}, /* BOD Flag - VDD NOT OK , */\ + { 0x1100, "flag_bst_bstcur"}, /* DCDC current limiting , */\ + { 0x1110, "flag_bst_hiz"}, /* DCDC active (sticky register, clear on read) , */\ + { 0x1120, "flag_bst_ocpok"}, /* DCDC OCP nmos (sticky register, clear on read) , */\ + { 0x1130, "flag_bst_peakcur"}, /* Indicates current is max in DC-to-DC converter , */\ + { 0x1140, "flag_bst_voutcomp"}, /* DCDC level 1x , */\ + { 0x1150, "flag_bst_voutcomp86"}, /* DCDC level 1.14x , */\ + { 0x1160, "flag_bst_voutcomp93"}, /* DCDC level 1.07x , */\ + { 0x1170, "flag_cf_speakererror"}, /* Speaker status , */\ + { 0x1180, "flag_clk_out_of_range"}, /* External clock status , */\ + { 0x1190, "flag_man_alarm_state"}, /* Alarm state , */\ + { 0x11a0, "flag_tdm_error"}, /* TDM error , */\ + { 0x11b0, "flag_tdm_lut_error"}, /* TDM lookup table error , */\ + { 0x1200, "flag_ocpokap"}, /* OCPOK pmos A , */\ + { 0x1210, "flag_ocpokan"}, /* OCPOK nmos A , */\ + { 0x1220, "flag_ocpokbp"}, /* OCPOK pmos B , */\ + { 0x1230, "flag_ocpokbn"}, /* OCPOK nmos B , */\ + { 0x1240, "flag_clip"}, /* Amplifier clipping , */\ + { 0x1250, "flag_man_start_mute_audio"}, /* Audio mute sequence , */\ + { 0x1260, "flag_man_operating_state"}, /* Device in Operating state , */\ + { 0x1270, "flag_lp_detect_mode1"}, /* Low power MODE1 detection , */\ + { 0x1280, "flag_low_amplitude"}, /* Low amplitude detection , */\ + { 0x1290, "flag_vddp_gt_vbat"}, /* VDDP greater than VBAT flag , */\ + { 0x1402, "tdm_status"}, /* TDM Status bits , */\ + { 0x1433, "man_state"}, /* Device Manager status , */\ + { 0x1473, "amp_ctrl_state"}, /* Amplifier control status , */\ + { 0x14b1, "status_bst_mode"}, /* DCDC mode status bits , */\ + { 0x1509, "bat_adc"}, /* Battery voltage (V) , */\ + { 0x1608, "temp_adc"}, /* IC Temperature (C) , */\ + { 0x1709, "vddp_adc"}, /* IC VDDP voltage (1023*VDDP/13V) , */\ + { 0x2000, "tdm_enable"}, /* Enable interface , */\ + { 0x2010, "tdm_sink0_enable"}, /* Control audio tdm channel in sink0 , */\ + { 0x2020, "tdm_sink1_enable"}, /* Control audio tdm channel in sink1 , */\ + { 0x2030, "tdm_source0_enable"}, /* Source 0 enable , */\ + { 0x2040, "tdm_source1_enable"}, /* Source 1 enable , */\ + { 0x2050, "tdm_source2_enable"}, /* Source 2 enable , */\ + { 0x2060, "tdm_source3_enable"}, /* Source 3 enable , */\ + { 0x2070, "tdm_clk_inversion"}, /* Reception data to BCK clock , */\ + { 0x2080, "tdm_fs_ws_polarity"}, /* FS polarity , */\ + { 0x2090, "tdm_data_delay"}, /* Data delay to FS , */\ + { 0x20a0, "tdm_data_adjustment"}, /* Data adjustment , */\ + { 0x20b1, "tdm_audio_sample_compression"}, /* Received audio compression , */\ + { 0x2103, "tdm_nbck"}, /* TDM NBCK - Bit clock to FS ratio , */\ + { 0x2143, "tdm_fs_ws_length"}, /* FS length (master mode only) , */\ + { 0x2183, "tdm_nb_of_slots"}, /* N-slots in Frame , */\ + { 0x21c1, "tdm_txdata_format"}, /* Format unused bits , */\ + { 0x21e1, "tdm_txdata_format_unused_slot"}, /* Format unused slots DATAO , */\ + { 0x2204, "tdm_slot_length"}, /* N-bits in slot , */\ + { 0x2254, "tdm_bits_remaining"}, /* N-bits remaining , */\ + { 0x22a4, "tdm_sample_size"}, /* Sample size per slot , */\ + { 0x2303, "tdm_sink0_slot"}, /* TDM slot for sink 0 , */\ + { 0x2343, "tdm_sink1_slot"}, /* TDM slot for sink 1 , */\ + { 0x2381, "tdm_source2_sel"}, /* TDM Source 2 data selection , */\ + { 0x23a1, "tdm_source3_sel"}, /* TDM Source 3 data selection , */\ + { 0x2403, "tdm_source0_slot"}, /* Slot Position of source 0 data , */\ + { 0x2443, "tdm_source1_slot"}, /* Slot Position of source 1 data , */\ + { 0x2483, "tdm_source2_slot"}, /* Slot Position of source 2 data , */\ + { 0x24c3, "tdm_source3_slot"}, /* Slot Position of source 3 data , */\ + { 0x4000, "int_out_flag_por"}, /* Status POR , */\ + { 0x4010, "int_out_flag_bst_ocpok"}, /* Status DCDC OCP , */\ + { 0x4020, "int_out_flag_otpok"}, /* Status OTP alarm , */\ + { 0x4030, "int_out_flag_ocp_alarm"}, /* Status OCP alarm , */\ + { 0x4040, "int_out_flag_uvpok"}, /* Status UVP alarm , */\ + { 0x4050, "int_out_flag_man_alarm_state"}, /* Status manager alarm state , */\ + { 0x4060, "int_out_flag_tdm_error"}, /* Status TDM error , */\ + { 0x4070, "int_out_flag_lost_clk"}, /* Status lost clock , */\ + { 0x4080, "int_out_flag_cfma_err"}, /* Status cfma error , */\ + { 0x4090, "int_out_flag_cfma_ack"}, /* Status cfma ack , */\ + { 0x40a0, "int_out_flag_cf_speakererror"}, /* Status coolflux speaker error , */\ + { 0x40b0, "int_out_flag_cold_started"}, /* Status cold started , */\ + { 0x40c0, "int_out_flag_watchdog_reset"}, /* Status watchdog reset , */\ + { 0x40d0, "int_out_flag_bod_vddd_nok"}, /* Status brown out detect , */\ + { 0x40e0, "int_out_flag_lp_detect_mode1"}, /* Status low power mode1 detect , */\ + { 0x40f0, "int_out_flag_clk_out_of_range"}, /* Status clock out of range , */\ + { 0x4400, "int_in_flag_por"}, /* Clear POR , */\ + { 0x4410, "int_in_flag_bst_ocpok"}, /* Clear DCDC OCP , */\ + { 0x4420, "int_in_flag_otpok"}, /* Clear OTP alarm , */\ + { 0x4430, "int_in_flag_ocp_alarm"}, /* Clear OCP alarm , */\ + { 0x4440, "int_in_flag_uvpok"}, /* Clear UVP alarm , */\ + { 0x4450, "int_in_flag_man_alarm_state"}, /* Clear manager alarm state , */\ + { 0x4460, "int_in_flag_tdm_error"}, /* Clear TDM error , */\ + { 0x4470, "int_in_flag_lost_clk"}, /* Clear lost clk , */\ + { 0x4480, "int_in_flag_cfma_err"}, /* Clear cfma err , */\ + { 0x4490, "int_in_flag_cfma_ack"}, /* Clear cfma ack , */\ + { 0x44a0, "int_in_flag_cf_speakererror"}, /* Clear coolflux speaker error , */\ + { 0x44b0, "int_in_flag_cold_started"}, /* Clear cold started , */\ + { 0x44c0, "int_in_flag_watchdog_reset"}, /* Clear watchdog reset , */\ + { 0x44d0, "int_in_flag_bod_vddd_nok"}, /* Clear brown out detect , */\ + { 0x44e0, "int_in_flag_lp_detect_mode1"}, /* Clear low power mode1 detect , */\ + { 0x44f0, "int_in_flag_clk_out_of_range"}, /* Clear clock out of range , */\ + { 0x4800, "int_enable_flag_por"}, /* Enable POR , */\ + { 0x4810, "int_enable_flag_bst_ocpok"}, /* Enable DCDC OCP , */\ + { 0x4820, "int_enable_flag_otpok"}, /* Enable OTP alarm , */\ + { 0x4830, "int_enable_flag_ocp_alarm"}, /* Enable OCP alarm , */\ + { 0x4840, "int_enable_flag_uvpok"}, /* Enable UVP alarm , */\ + { 0x4850, "int_enable_flag_man_alarm_state"}, /* Enable Manager Alarm state , */\ + { 0x4860, "int_enable_flag_tdm_error"}, /* Enable TDM error , */\ + { 0x4870, "int_enable_flag_lost_clk"}, /* Enable lost clk , */\ + { 0x4880, "int_enable_flag_cfma_err"}, /* Enable cfma err , */\ + { 0x4890, "int_enable_flag_cfma_ack"}, /* Enable cfma ack , */\ + { 0x48a0, "int_enable_flag_cf_speakererror"}, /* Enable coolflux speaker error , */\ + { 0x48b0, "int_enable_flag_cold_started"}, /* Enable cold started , */\ + { 0x48c0, "int_enable_flag_watchdog_reset"}, /* Enable watchdog reset , */\ + { 0x48d0, "int_enable_flag_bod_vddd_nok"}, /* Enable brown out detect , */\ + { 0x48e0, "int_enable_flag_lp_detect_mode1"}, /* Enable low power mode1 detect , */\ + { 0x48f0, "int_enable_flag_clk_out_of_range"}, /* Enable clock out of range , */\ + { 0x4c00, "int_polarity_flag_por"}, /* Polarity POR , */\ + { 0x4c10, "int_polarity_flag_bst_ocpok"}, /* Polarity DCDC OCP , */\ + { 0x4c20, "int_polarity_flag_otpok"}, /* Polarity OTP alarm , */\ + { 0x4c30, "int_polarity_flag_ocp_alarm"}, /* Polarity ocp alarm , */\ + { 0x4c40, "int_polarity_flag_uvpok"}, /* Polarity UVP alarm , */\ + { 0x4c50, "int_polarity_flag_man_alarm_state"}, /* Polarity manager alarm state , */\ + { 0x4c60, "int_polarity_flag_tdm_error"}, /* Polarity TDM error , */\ + { 0x4c70, "int_polarity_flag_lost_clk"}, /* Polarity lost clk , */\ + { 0x4c80, "int_polarity_flag_cfma_err"}, /* Polarity cfma err , */\ + { 0x4c90, "int_polarity_flag_cfma_ack"}, /* Polarity cfma ack , */\ + { 0x4ca0, "int_polarity_flag_cf_speakererror"}, /* Polarity coolflux speaker error , */\ + { 0x4cb0, "int_polarity_flag_cold_started"}, /* Polarity cold started , */\ + { 0x4cc0, "int_polarity_flag_watchdog_reset"}, /* Polarity watchdog reset , */\ + { 0x4cd0, "int_polarity_flag_bod_vddd_nok"}, /* Polarity brown out detect , */\ + { 0x4ce0, "int_polarity_flag_lp_detect_mode1"}, /* Polarity low power mode1 detect , */\ + { 0x4cf0, "int_polarity_flag_clk_out_of_range"}, /* Polarity clock out of range , */\ + { 0x5001, "vbat_prot_attack_time"}, /* Battery safeguard attack time , */\ + { 0x5023, "vbat_prot_thlevel"}, /* Battery safeguard threshold voltage level , */\ + { 0x5061, "vbat_prot_max_reduct"}, /* Battery safeguard maximum reduction , */\ + { 0x5082, "vbat_prot_release_time"}, /* Battery safeguard release time , */\ + { 0x50b1, "vbat_prot_hysterese"}, /* Battery Safeguard hysteresis , */\ + { 0x50d0, "rst_min_vbat"}, /* Reset clipper - auto clear , */\ + { 0x50e0, "sel_vbat"}, /* Battery voltage read out , */\ + { 0x50f0, "bypass_clipper"}, /* Bypass HW clipper , */\ + { 0x5130, "cf_mute"}, /* Coolflux firmware soft mute control , */\ + { 0x5187, "cf_volume"}, /* CF firmware volume control , */\ + { 0x5202, "ctrl_cc"}, /* Clip control setting , */\ + { 0x5230, "ctrl_slopectrl"}, /* Enables slope control , */\ + { 0x5240, "ctrl_slope"}, /* Slope speed setting (binary coded) , */\ + { 0x5287, "gain"}, /* Amplifier gain , */\ + { 0x5301, "dpsa_level"}, /* DPSA threshold levels , */\ + { 0x5321, "dpsa_release"}, /* DPSA Release time , */\ + { 0x5340, "clipfast"}, /* Clock selection for HW clipper for battery safeguard, */\ + { 0x5350, "bypass_lp"}, /* Bypass the low power filter inside temperature sensor, */\ + { 0x5360, "first_order_mode"}, /* Overrule to 1st order mode of control stage when clipping, */\ + { 0x5370, "icomp_engage"}, /* Engage of icomp , */\ + { 0x5380, "ctrl_kickback"}, /* Prevent double pulses of output stage , */\ + { 0x5390, "icomp_engage_overrule"}, /* To overrule the functional icomp_engage signal during validation, */\ + { 0x53a3, "ctrl_dem"}, /* Enable DEM icomp and DEM one bit dac , */\ + { 0x5400, "bypass_ctrlloop"}, /* Switch amplifier into open loop configuration , */\ + { 0x5413, "ctrl_dem_mismatch"}, /* Enable DEM icomp mismatch for testing , */\ + { 0x5452, "dpsa_drive"}, /* Drive setting (binary coded) , */\ + { 0x550a, "enbl_amp"}, /* Switch on the class-D power sections, each part of the analog sections can be switched on/off individually, */\ + { 0x55b0, "enbl_engage"}, /* Enables/engage power stage and control loop , */\ + { 0x55c0, "enbl_engage_pst"}, /* Enables/engage power stage and control loop , */\ + { 0x5600, "pwm_shape"}, /* PWM shape , */\ + { 0x5614, "pwm_delay"}, /* PWM delay bits to set the delay, clockd is 1/(k*2048*fs), */\ + { 0x5660, "reclock_pwm"}, /* Reclock the PWM signal inside analog , */\ + { 0x5670, "reclock_voltsense"}, /* Reclock the voltage sense PWM signal , */\ + { 0x5680, "enbl_pwm_phase_shift"}, /* Control for PWM phase shift , */\ + { 0x5690, "sel_pwm_delay_src"}, /* Control for selection for PWM delay line source , */\ + { 0x56a1, "enbl_odd_up_even_down"}, /* Control for PWM reference sawtooth generartion , */\ + { 0x5703, "ctrl_att_dcdc"}, /* Second channel gain in case of stereo using a single coil. (Total gain depending on INPLEV). (In case of mono OR stereo using 2 separate DCDC channel 1 should be disabled using TDMDCE), */\ + { 0x5743, "ctrl_att_spkr"}, /* Total gain depending on INPLEV setting (channel 0), */\ + { 0x5781, "vamp_sel2"}, /* VAMP_OUT2 input selection , */\ + { 0x5805, "zero_lvl"}, /* Low noise gain switch zero trigger level , */\ + { 0x5861, "ctrl_fb_resistor"}, /* Select amplifier feedback resistor connection , */\ + { 0x5881, "lownoisegain_mode"}, /* Low noise gain mode control , */\ + { 0x5905, "threshold_lvl"}, /* Low noise gain switch trigger level , */\ + { 0x5965, "hold_time"}, /* Low noise mode hold time before entering into low noise mode, */\ + { 0x5a05, "lpm1_cal_offset"}, /* Low power mode1 detector ctrl cal_offset from gain module , */\ + { 0x5a65, "lpm1_zero_lvl"}, /* Low power mode1 zero crossing detection level , */\ + { 0x5ac1, "lpm1_mode"}, /* Low power mode control , */\ + { 0x5b05, "lpm1_threshold_lvl"}, /* Low power mode1 amplitude trigger level , */\ + { 0x5b65, "lpm1_hold_time"}, /* Low power mode hold time before entering into low power mode, */\ + { 0x5bc0, "disable_low_power_mode"}, /* Low power mode1 detector control , */\ + { 0x5c00, "enbl_minion"}, /* Enables minion (small) power stage , */\ + { 0x5c13, "vth_vddpvbat"}, /* Select vddp-vbat threshold signal , */\ + { 0x5c50, "lpen_vddpvbat"}, /* Select vddp-vbat filtred vs unfiltered compare , */\ + { 0x5c61, "ctrl_rfb"}, /* Feedback resistor selection - I2C direct mode , */\ + { 0x5d02, "tdm_source_mapping"}, /* TDM source mapping , */\ + { 0x5d31, "tdm_sourcea_frame_sel"}, /* Sensed value A , */\ + { 0x5d51, "tdm_sourceb_frame_sel"}, /* Sensed value B , */\ + { 0x5d71, "tdm_source0_clip_sel"}, /* Clip information (analog /digital) for source0 , */\ + { 0x5d91, "tdm_source1_clip_sel"}, /* Clip information (analog /digital) for source1 , */\ + { 0x5e02, "rst_min_vbat_delay"}, /* Delay for reseting the min_vbat value inside HW Clipper (number of Fs pulses), */\ + { 0x5e30, "rst_min_vbat_sel"}, /* Control for selecting reset signal for min_bat , */\ + { 0x5f00, "hard_mute"}, /* Hard mute - PWM , */\ + { 0x5f12, "ns_hp2ln_criterion"}, /* 0..7 zeroes at ns as threshold to swap from high_power to low_noise, */\ + { 0x5f42, "ns_ln2hp_criterion"}, /* 0..7 zeroes at ns as threshold to swap from low_noise to high_power, */\ + { 0x5f78, "spare_out"}, /* Spare out register , */\ + { 0x600f, "spare_in"}, /* Spare IN , */\ + { 0x6102, "cursense_comp_delay"}, /* Delay to allign compensation signal with current sense signal, */\ + { 0x6130, "cursense_comp_sign"}, /* Polarity of compensation for current sense , */\ + { 0x6140, "enbl_cursense_comp"}, /* Enable current sense compensation , */\ + { 0x6152, "pwms_clip_lvl"}, /* Set the amount of pwm pulse that may be skipped before clip-flag is triggered, */\ + { 0x7005, "frst_boost_voltage"}, /* First Boost Voltage Level , */\ + { 0x7065, "scnd_boost_voltage"}, /* Second Boost Voltage Level , */\ + { 0x70c3, "boost_cur"}, /* Max Coil Current , */\ + { 0x7101, "bst_slpcmplvl"}, /* Slope compensation current, represents LxF (inductance x frequency) value , */\ + { 0x7120, "boost_intel"}, /* Adaptive boost mode , */\ + { 0x7130, "boost_speed"}, /* Soft ramp up/down , */\ + { 0x7140, "dcdcoff_mode"}, /* DCDC on/off , */\ + { 0x7150, "dcdc_pwmonly"}, /* DCDC PWM only mode , */\ + { 0x7160, "boost_track"}, /* Boost algorithm selection, effective only when boost_intelligent is set to 1, */\ + { 0x7170, "sel_dcdc_envelope_8fs"}, /* Selection of data for adaptive boost algorithm, effective only when boost_intelligent is set to 1, */\ + { 0x7180, "ignore_flag_voutcomp86"}, /* Determines the maximum PWM frequency be the most efficient in relation to the Booster inductor value, */\ + { 0x7204, "boost_trip_lvl_1st"}, /* 1st adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x7254, "boost_trip_lvl_2nd"}, /* 2nd adaptive boost trip levels, effective only when DCIE is set to 1, */\ + { 0x72a4, "boost_trip_lvl_track"}, /* Track adaptive boost trip levels, effective only when boost_intelligent is set to 1, */\ + { 0x72f0, "enbl_trip_hyst"}, /* Enable hysteresis on booster trip levels , */\ + { 0x7304, "boost_hold_time"}, /* Hold time for DCDC booster, effective only when boost_intelligent is set to 1, */\ + { 0x7350, "dcdc_pfm20khz_limit"}, /* DCDC in PFM mode pwm mode is activated each 50us to force a pwm pulse, */\ + { 0x7361, "dcdc_ctrl_maxzercnt"}, /* Number of zero current flags to count before going to pfm mode, */\ + { 0x7386, "dcdc_vbat_delta_detect"}, /* Threshold before booster is reacting on a delta Vbat (in PFM mode) by temporarily switching to PWM mode, */\ + { 0x73f0, "dcdc_ignore_vbat"}, /* Ignore an increase on Vbat , */\ + { 0x7404, "bst_drive"}, /* Binary coded drive setting for boost converter power stage, */\ + { 0x7451, "bst_scalecur"}, /* For testing direct control scale current , */\ + { 0x7474, "bst_slopecur"}, /* For testing direct control slope current , */\ + { 0x74c1, "bst_slope"}, /* Boost slope speed , */\ + { 0x74e0, "bst_bypass_bstcur"}, /* Bypass control for boost current settings , */\ + { 0x74f0, "bst_bypass_bstfoldback"}, /* Bypass control for boost foldback , */\ + { 0x7500, "enbl_bst_engage"}, /* Enable power stage dcdc controller , */\ + { 0x7510, "enbl_bst_hizcom"}, /* Enable hiz comparator , */\ + { 0x7520, "enbl_bst_peakcur"}, /* Enable peak current , */\ + { 0x7530, "enbl_bst_power"}, /* Enable line of the powerstage , */\ + { 0x7540, "enbl_bst_slopecur"}, /* Enable bit of max-current dac , */\ + { 0x7550, "enbl_bst_voutcomp"}, /* Enable vout comparators , */\ + { 0x7560, "enbl_bst_voutcomp86"}, /* Enable vout-86 comparators , */\ + { 0x7570, "enbl_bst_voutcomp93"}, /* Enable vout-93 comparators , */\ + { 0x7580, "enbl_bst_windac"}, /* Enable window dac , */\ + { 0x7595, "bst_windac"}, /* For testing direct control windac , */\ + { 0x7600, "boost_alg"}, /* Control for boost adaptive loop gain , */\ + { 0x7611, "boost_loopgain"}, /* DCDC boost loopgain setting , */\ + { 0x7631, "bst_freq"}, /* DCDC boost frequency control , */\ + { 0x7650, "enbl_bst_peak2avg"}, /* Enable boost peak2avg functionality , */\ + { 0x7660, "bst_use_new_zercur_detect"}, /* Enable new zero current detection for boost control, */\ + { 0x8001, "sel_clk_cs"}, /* Current sense clock duty cycle control , */\ + { 0x8021, "micadc_speed"}, /* Current sense clock for MiCADC selection - 32/44.1/48 KHz Fs band only, */\ + { 0x8040, "cs_gain_control"}, /* Current sense gain control , */\ + { 0x8050, "cs_bypass_gc"}, /* Bypasses the CS gain correction , */\ + { 0x8060, "invertpwm"}, /* Current sense common mode feedback pwm invert control, */\ + { 0x8087, "cs_gain"}, /* Current sense gain , */\ + { 0x8105, "cs_ktemp"}, /* Current sense temperature compensation trimming (1 - VALUE*TEMP) * signal, */\ + { 0x8164, "cs_ktemp2"}, /* Second order temperature compensation coefficient , */\ + { 0x81b0, "enbl_cs_adc"}, /* Enable current sense ADC , */\ + { 0x81c0, "enbl_cs_inn1"}, /* Enable connection of current sense negative1 , */\ + { 0x81d0, "enbl_cs_inn2"}, /* Enable connection of current sense negative2 , */\ + { 0x81e0, "enbl_cs_inp1"}, /* Enable connection of current sense positive1 , */\ + { 0x81f0, "enbl_cs_inp2"}, /* Enable connection of current sense positive2 , */\ + { 0x8200, "enbl_cs_ldo"}, /* Enable current sense LDO , */\ + { 0x8210, "enbl_cs_vbatldo"}, /* Enable of current sense LDO , */\ + { 0x8220, "cs_adc_bsoinv"}, /* Bitstream inversion for current sense ADC , */\ + { 0x8231, "cs_adc_hifreq"}, /* Frequency mode current sense ADC , */\ + { 0x8250, "cs_adc_nortz"}, /* Return to zero for current sense ADC , */\ + { 0x8263, "cs_adc_offset"}, /* Micadc ADC offset setting , */\ + { 0x82a0, "cs_adc_slowdel"}, /* Select delay for current sense ADC (internal decision circuitry), */\ + { 0x82b4, "cs_adc_gain"}, /* Gain setting for current sense ADC (two's complement), */\ + { 0x8300, "cs_resonator_enable"}, /* Enable for resonator to improve SRN , */\ + { 0x8310, "cs_classd_tran_skip"}, /* Skip current sense connection during a classD amplifier transition, */\ + { 0x8320, "cs_inn_short"}, /* Short current sense negative to common mode , */\ + { 0x8330, "cs_inp_short"}, /* Short current sense positive to common mode , */\ + { 0x8340, "cs_ldo_bypass"}, /* Bypass current sense LDO , */\ + { 0x8350, "cs_ldo_pulldown"}, /* Pull down current sense LDO, only valid if enbl_cs_ldo is high, */\ + { 0x8364, "cs_ldo_voset"}, /* Current sense LDO voltage level setting (two's complement), */\ + { 0x8800, "ctrl_vs_igen_supply"}, /* Control for selecting supply for VS current generator, */\ + { 0x8810, "ctrl_vs_force_div2"}, /* Select input resistive divider gain , */\ + { 0x8820, "enbl_dc_filter"}, /* Control for enabling the DC blocking filter for voltage and current sense, */\ + { 0x8901, "volsense_pwm_sel"}, /* Voltage sense source selection control , */\ + { 0x8920, "vs_gain_control"}, /* Voltage sense gain control , */\ + { 0x8930, "vs_bypass_gc"}, /* Bypasses the VS gain correction , */\ + { 0x8940, "vs_adc_bsoinv"}, /* Bitstream inversion for voltage sense ADC , */\ + { 0x8950, "vs_adc_nortz"}, /* Return to zero for voltage sense ADC , */\ + { 0x8960, "vs_adc_slowdel"}, /* Select delay for voltage sense ADC (internal decision circuitry), */\ + { 0x8970, "vs_classd_tran_skip"}, /* Skip voltage sense connection during a classD amplifier transition, */\ + { 0x8987, "vs_gain"}, /* Voltage sense gain , */\ + { 0x8a00, "vs_inn_short"}, /* Short voltage sense negative to common mode , */\ + { 0x8a10, "vs_inp_short"}, /* Short voltage sense positive to common mode , */\ + { 0x8a20, "vs_ldo_bypass"}, /* Bypass voltage sense LDO , */\ + { 0x8a30, "vs_ldo_pulldown"}, /* Pull down voltage sense LDO, only valid if enbl_cs_ldo is high, */\ + { 0x8a44, "vs_ldo_voset"}, /* Voltage sense LDO voltage level setting (two's complement), */\ + { 0x8a90, "enbl_vs_adc"}, /* Enable voltage sense ADC , */\ + { 0x8aa0, "enbl_vs_inn1"}, /* Enable connection of voltage sense negative1 , */\ + { 0x8ab0, "enbl_vs_inn2"}, /* Enable connection of voltage sense negative2 , */\ + { 0x8ac0, "enbl_vs_inp1"}, /* Enable connection of voltage sense positive1 , */\ + { 0x8ad0, "enbl_vs_inp2"}, /* Enable connection of voltage sense positive2 , */\ + { 0x8ae0, "enbl_vs_ldo"}, /* Enable voltage sense LDO , */\ + { 0x8af0, "enbl_vs_vbatldo"}, /* Enable of voltage sense LDO , */\ + { 0x9000, "cf_rst_dsp"}, /* Reset for Coolflux DSP , */\ + { 0x9011, "cf_dmem"}, /* Target memory for CFMA using I2C interface , */\ + { 0x9030, "cf_aif"}, /* Auto increment , */\ + { 0x9040, "cf_int"}, /* Coolflux Interrupt - auto clear , */\ + { 0x9050, "cf_cgate_off"}, /* Coolflux clock gating disabling control , */\ + { 0x9080, "cf_req_cmd"}, /* Firmware event request rpc command , */\ + { 0x9090, "cf_req_reset"}, /* Firmware event request reset restart , */\ + { 0x90a0, "cf_req_mips"}, /* Firmware event request short on mips , */\ + { 0x90b0, "cf_req_mute_ready"}, /* Firmware event request mute sequence ready , */\ + { 0x90c0, "cf_req_volume_ready"}, /* Firmware event request volume ready , */\ + { 0x90d0, "cf_req_damage"}, /* Firmware event request speaker damage detected , */\ + { 0x90e0, "cf_req_calibrate_ready"}, /* Firmware event request calibration completed , */\ + { 0x90f0, "cf_req_reserved"}, /* Firmware event request reserved , */\ + { 0x910f, "cf_madd"}, /* CF memory address , */\ + { 0x920f, "cf_mema"}, /* Activate memory access , */\ + { 0x9307, "cf_err"}, /* CF error flags , */\ + { 0x9380, "cf_ack_cmd"}, /* Firmware event acknowledge rpc command , */\ + { 0x9390, "cf_ack_reset"}, /* Firmware event acknowledge reset restart , */\ + { 0x93a0, "cf_ack_mips"}, /* Firmware event acknowledge short on mips , */\ + { 0x93b0, "cf_ack_mute_ready"}, /* Firmware event acknowledge mute sequence ready , */\ + { 0x93c0, "cf_ack_volume_ready"}, /* Firmware event acknowledge volume ready , */\ + { 0x93d0, "cf_ack_damage"}, /* Firmware event acknowledge speaker damage detected, */\ + { 0x93e0, "cf_ack_calibrate_ready"}, /* Firmware event acknowledge calibration completed , */\ + { 0x93f0, "cf_ack_reserved"}, /* Firmware event acknowledge reserved , */\ + { 0xa007, "mtpkey1"}, /* KEY1 To access KEY1 protected registers 0x5A/90d (default for engineering), */\ + { 0xa107, "mtpkey2"}, /* KEY2 to access KEY2 protected registers, customer key, */\ + { 0xa200, "key01_locked"}, /* Indicates KEY1 is locked , */\ + { 0xa210, "key02_locked"}, /* Indicates KEY2 is locked , */\ + { 0xa302, "mtp_man_address_in"}, /* MTP address from I2C register for read/writing mtp in manual single word mode, */\ + { 0xa330, "man_copy_mtp_to_iic"}, /* Start copying single word from MTP to I2C mtp register - auto clear, */\ + { 0xa340, "man_copy_iic_to_mtp"}, /* Start copying single word from I2C mtp register to mtp - auto clear, */\ + { 0xa350, "auto_copy_mtp_to_iic"}, /* Start copying all the data from mtp to I2C mtp registers - auto clear, */\ + { 0xa360, "auto_copy_iic_to_mtp"}, /* Start copying data from I2C mtp registers to mtp - auto clear, */\ + { 0xa400, "faim_set_clkws"}, /* Sets the FaIM controller clock wait state register, */\ + { 0xa410, "faim_sel_evenrows"}, /* All even rows of the FaIM are selected, active high, */\ + { 0xa420, "faim_sel_oddrows"}, /* All odd rows of the FaIM are selected, all rows in combination with sel_evenrows, */\ + { 0xa430, "faim_program_only"}, /* Skip the erase access at wr_faim command (write-program-marginread), */\ + { 0xa440, "faim_erase_only"}, /* Skip the program access at wr_faim command (write-erase-marginread), */\ + { 0xa50f, "mtp_man_data_out_msb"}, /* MSB word of MTP manual read data , */\ + { 0xa60f, "mtp_man_data_out_lsb"}, /* LSB word of MTP manual read data , */\ + { 0xa70f, "mtp_man_data_in_msb"}, /* MSB word of write data for MTP manual write , */\ + { 0xa80f, "mtp_man_data_in_lsb"}, /* LSB word of write data for MTP manual write , */\ + { 0xb000, "bypass_ocpcounter"}, /* Bypass OCP Counter , */\ + { 0xb010, "bypass_glitchfilter"}, /* Bypass glitch filter , */\ + { 0xb020, "bypass_ovp"}, /* Bypass OVP , */\ + { 0xb030, "bypass_uvp"}, /* Bypass UVP , */\ + { 0xb040, "bypass_otp"}, /* Bypass OTP , */\ + { 0xb050, "bypass_lost_clk"}, /* Bypass lost clock detector , */\ + { 0xb060, "ctrl_vpalarm"}, /* vpalarm (uvp ovp handling) , */\ + { 0xb070, "disable_main_ctrl_change_prot"}, /* Disable main control change protection , */\ + { 0xb087, "ocp_threshold"}, /* OCP threshold level , */\ + { 0xb108, "ext_temp"}, /* External temperature (C) , */\ + { 0xb190, "ext_temp_sel"}, /* Select temp Speaker calibration , */\ + { 0xc000, "use_direct_ctrls"}, /* Direct control to overrule several functions for testing, */\ + { 0xc010, "rst_datapath"}, /* Direct control for datapath reset , */\ + { 0xc020, "rst_cgu"}, /* Direct control for cgu reset , */\ + { 0xc038, "enbl_ref"}, /* Switch on the analog references, each part of the references can be switched on/off individually, */\ + { 0xc0c0, "use_direct_vs_ctrls"}, /* Voltage sense direct control to overrule several functions for testing, */\ + { 0xc0d0, "enbl_ringo"}, /* Enable the ring oscillator for test purpose , */\ + { 0xc0e0, "use_direct_clk_ctrl"}, /* Direct clock control to overrule several functions for testing, */\ + { 0xc0f0, "use_direct_pll_ctrl"}, /* Direct PLL control to overrule several functions for testing, */\ + { 0xc100, "enbl_tsense"}, /* Temperature sensor enable control - I2C direct mode, */\ + { 0xc110, "tsense_hibias"}, /* Bit to set the biasing in temp sensor to high , */\ + { 0xc120, "enbl_flag_vbg"}, /* Enable flagging of bandgap out of control , */\ + { 0xc20f, "abist_offset"}, /* Offset control for ABIST testing (two's complement), */\ + { 0xc300, "bypasslatch"}, /* Bypass latch , */\ + { 0xc311, "sourcea"}, /* Set OUTA to , */\ + { 0xc331, "sourceb"}, /* Set OUTB to , */\ + { 0xc350, "inverta"}, /* Invert pwma test signal , */\ + { 0xc360, "invertb"}, /* Invert pwmb test signal , */\ + { 0xc374, "pulselength"}, /* Pulse length setting test input for amplifier (clock d - k*2048*fs), */\ + { 0xc3d0, "test_abistfft_enbl"}, /* Enable ABIST with FFT on Coolflux DSP , */\ + { 0xc400, "bst_bypasslatch"}, /* Bypass latch in boost converter , */\ + { 0xc411, "bst_source"}, /* Sets the source of the pwmbst output to boost converter input for testing, */\ + { 0xc430, "bst_invertb"}, /* Invert pwmbst test signal , */\ + { 0xc444, "bst_pulselength"}, /* Pulse length setting test input for boost converter , */\ + { 0xc490, "test_bst_ctrlsthv"}, /* Test mode for boost control stage , */\ + { 0xc4a0, "test_bst_iddq"}, /* IDDQ testing in power stage of boost converter , */\ + { 0xc4b0, "test_bst_rdson"}, /* RDSON testing - boost power stage , */\ + { 0xc4c0, "test_bst_cvi"}, /* CVI testing - boost power stage , */\ + { 0xc4d0, "test_bst_ocp"}, /* Boost OCP. For old ocp (ctrl_reversebst is 0), For new ocp (ctrl_reversebst is 1), */\ + { 0xc4e0, "test_bst_sense"}, /* Test option for the sense NMOS in booster for current mode control., */\ + { 0xc500, "test_cvi"}, /* Analog BIST, switch choose which transistor will be used as current source (also cross coupled sources possible), */\ + { 0xc510, "test_discrete"}, /* Test function noise measurement , */\ + { 0xc520, "test_iddq"}, /* Set the power stages in iddq mode for gate stress., */\ + { 0xc530, "test_rdson"}, /* Analog BIST, switch to enable Rdson measurement , */\ + { 0xc540, "test_sdelta"}, /* Analog BIST, noise test , */\ + { 0xc550, "test_enbl_cs"}, /* Enable for digimux mode of current sense , */\ + { 0xc560, "test_enbl_vs"}, /* Enable for digimux mode of voltage sense , */\ + { 0xc570, "enbl_pwm_dcc"}, /* Enables direct control of pwm duty cycle for DCDC power stage, */\ + { 0xc583, "pwm_dcc_cnt"}, /* Control pwm duty cycle when enbl_pwm_dcc is 1 , */\ + { 0xc5c0, "enbl_ldo_stress"}, /* Enable stress of internal supply voltages powerstages, */\ + { 0xc607, "digimuxa_sel"}, /* DigimuxA input selection control routed to DATAO , */\ + { 0xc687, "digimuxb_sel"}, /* DigimuxB input selection control routed to INT , */\ + { 0xc707, "digimuxc_sel"}, /* DigimuxC input selection control routed to ADS1 , */\ + { 0xc800, "enbl_anamux1"}, /* Enable anamux1 , */\ + { 0xc810, "enbl_anamux2"}, /* Enable anamux2 , */\ + { 0xc820, "enbl_anamux3"}, /* Enable anamux3 , */\ + { 0xc830, "enbl_anamux4"}, /* Enable anamux4 , */\ + { 0xc844, "anamux1"}, /* Anamux selection control - anamux on TEST1 , */\ + { 0xc894, "anamux2"}, /* Anamux selection control - anamux on TEST2 , */\ + { 0xc903, "anamux3"}, /* Anamux selection control - anamux on TEST3 , */\ + { 0xc943, "anamux4"}, /* Anamux selection control - anamux on TEST4 , */\ + { 0xca05, "pll_seli"}, /* PLL SELI - I2C direct PLL control mode only , */\ + { 0xca64, "pll_selp"}, /* PLL SELP - I2C direct PLL control mode only , */\ + { 0xcab3, "pll_selr"}, /* PLL SELR - I2C direct PLL control mode only , */\ + { 0xcaf0, "pll_frm"}, /* PLL free running mode control; 1 in TCB direct control mode, else this control bit, */\ + { 0xcb09, "pll_ndec"}, /* PLL NDEC - I2C direct PLL control mode only , */\ + { 0xcba0, "pll_mdec_msb"}, /* MSB of PLL_mdec - I2C direct PLL control mode only, */\ + { 0xcbb0, "enbl_pll"}, /* Enables PLL in I2C direct PLL control mode only , */\ + { 0xcbc0, "enbl_osc"}, /* Enables OSC1M in I2C direct control mode only , */\ + { 0xcbd0, "pll_bypass"}, /* PLL bypass control in I2C direct PLL control mode only, */\ + { 0xcbe0, "pll_directi"}, /* PLL directi control in I2C direct PLL control mode only, */\ + { 0xcbf0, "pll_directo"}, /* PLL directo control in I2C direct PLL control mode only, */\ + { 0xcc0f, "pll_mdec_lsb"}, /* Bits 15..0 of PLL MDEC are I2C direct PLL control mode only, */\ + { 0xcd06, "pll_pdec"}, /* PLL PDEC - I2C direct PLL control mode only , */\ + { 0xce0f, "tsig_freq_lsb"}, /* Internal sinus test generator frequency control , */\ + { 0xcf02, "tsig_freq_msb"}, /* Select internal sinus test generator, frequency control msb bits, */\ + { 0xcf33, "tsig_gain"}, /* Test signal gain , */\ + { 0xd000, "adc10_reset"}, /* Reset for ADC10 - I2C direct control mode , */\ + { 0xd011, "adc10_test"}, /* Test mode selection signal for ADC10 - I2C direct control mode, */\ + { 0xd032, "adc10_sel"}, /* Select the input to convert for ADC10 - I2C direct control mode, */\ + { 0xd064, "adc10_prog_sample"}, /* ADC10 program sample setting - I2C direct control mode, */\ + { 0xd0b0, "adc10_enbl"}, /* Enable ADC10 - I2C direct control mode , */\ + { 0xd0c0, "bypass_lp_vbat"}, /* Bypass control for Low pass filter in batt sensor , */\ + { 0xd109, "data_adc10_tempbat"}, /* ADC 10 data output data for testing , */\ + { 0xd201, "clkdiv_audio_sel"}, /* Audio clock divider selection in direct clock control mode, */\ + { 0xd221, "clkdiv_muxa_sel"}, /* DCDC MUXA clock divider selection in direct clock control mode, */\ + { 0xd241, "clkdiv_muxb_sel"}, /* DCDC MUXB clock divider selection in direct clock control mode, */\ + { 0xd301, "int_ehs"}, /* Speed/load setting for INT IO cell, clk or data mode range (see SLIMMF IO datasheet), */\ + { 0xd321, "datao_ehs"}, /* Speed/load setting for DATAO IO cell, clk or data mode range (see SLIMMF IO datasheet), */\ + { 0xd340, "hs_mode"}, /* I2C high speed mode control , */\ + { 0xd407, "ctrl_digtoana_hidden"}, /* Spare digital to analog control bits - Hidden , */\ + { 0xd480, "enbl_clk_out_of_range"}, /* Clock out of range , */\ + { 0xd491, "sel_wdt_clk"}, /* Watch dog clock divider settings , */\ + { 0xd4b0, "inject_tsig"}, /* Control bit to switch to internal sinus test generator, */\ + { 0xd500, "source_in_testmode"}, /* TDM source in test mode (return only current and voltage sense), */\ + { 0xd510, "gainatt_feedback"}, /* Gainatt feedback to tdm , */\ + { 0xd522, "test_parametric_io"}, /* Test IO parametric , */\ + { 0xd550, "ctrl_bst_clk_lp1"}, /* Boost clock control in low power mode1 , */\ + { 0xd561, "test_spare_out1"}, /* Test spare out 1 , */\ + { 0xd580, "bst_dcmbst"}, /* DCM boost , */\ + { 0xd593, "test_spare_out2"}, /* Test spare out 2 , */\ + { 0xe00f, "sw_profile"}, /* Software profile data , */\ + { 0xe10f, "sw_vstep"}, /* Software vstep information , */\ + { 0xf000, "calibration_onetime"}, /* Calibration schedule , */\ + { 0xf010, "calibr_ron_done"}, /* Calibration Ron executed , */\ + { 0xf020, "calibr_dcdc_api_calibrate"}, /* Calibration current limit DCDC , */\ + { 0xf030, "calibr_dcdc_delta_sign"}, /* Sign bit for delta calibration current limit DCDC , */\ + { 0xf042, "calibr_dcdc_delta"}, /* Calibration delta current limit DCDC , */\ + { 0xf078, "calibr_speaker_info"}, /* Reserved space for allowing customer to store speaker information, */\ + { 0xf105, "calibr_vout_offset"}, /* DCDC offset calibration 2's complement (key1 protected), */\ + { 0xf163, "calibr_vbg_trim"}, /* Bandgap trimming control , */\ + { 0xf203, "calibr_gain"}, /* HW gain module (2's complement) , */\ + { 0xf245, "calibr_offset"}, /* Offset for amplifier, HW gain module (2's complement), */\ + { 0xf307, "calibr_gain_vs"}, /* Voltage sense gain , */\ + { 0xf387, "calibr_gain_cs"}, /* Current sense gain (signed two's complement format), */\ + { 0xf40f, "mtpdata4"}, /* MTP4 data , */\ + { 0xf50f, "calibr_R25C"}, /* Ron resistance of speaker coil , */\ + { 0xf60f, "mtpdata6"}, /* MTP6 data , */\ + { 0xf706, "ctrl_offset_a"}, /* Offset of level shifter A , */\ + { 0xf786, "ctrl_offset_b"}, /* Offset of amplifier level shifter B , */\ + { 0xf806, "htol_iic_addr"}, /* 7-bit I2C address to be used during HTOL testing , */\ + { 0xf870, "htol_iic_addr_en"}, /* HTOL I2C address enable control , */\ + { 0xf884, "calibr_temp_offset"}, /* Temperature offset 2's compliment (key1 protected), */\ + { 0xf8d2, "calibr_temp_gain"}, /* Temperature gain 2's compliment (key1 protected) , */\ + { 0xf900, "mtp_lock_dcdcoff_mode"}, /* Disable functionality of dcdcoff_mode bit , */\ + { 0xf910, "mtp_lock_enbl_coolflux"}, /* Disable functionality of enbl_coolflux bit , */\ + { 0xf920, "mtp_lock_bypass_clipper"}, /* Disable function bypass_clipper , */\ + { 0xf930, "mtp_enbl_pwm_delay_clock_gating"}, /* PWM delay clock auto gating , */\ + { 0xf940, "mtp_enbl_ocp_clock_gating"}, /* OCP clock auto gating , */\ + { 0xf950, "mtp_gate_cgu_clock_for_test"}, /* CGU test clock control , */\ + { 0xf987, "type_bits_fw"}, /* MTP control for firmware features - See Firmware I2C API document for details, */\ + { 0xfa0f, "mtpdataA"}, /* MTPdataA , */\ + { 0xfb0f, "mtpdataB"}, /* MTPdataB , */\ + { 0xfc0f, "mtpdataC"}, /* MTPdataC , */\ + { 0xfd0f, "mtpdataD"}, /* MTPdataD , */\ + { 0xfe0f, "mtpdataE"}, /* MTPdataE , */\ + { 0xff07, "calibr_osc_delta_ndiv"}, /* Calibration data for OSC1M, signed number representation, */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +enum tfa9894_irq { + tfa9894_irq_max = -1, + tfa9894_irq_all = -1 /* all irqs */}; + +#define TFA9894_IRQ_NAMETABLE static tfaIrqName_t Tfa9894IrqNames[]= {\ +}; +#endif /* _TFA9894_TFAFIELDNAMES_H */ diff --git a/sound/soc/codecs/inc/tfa9896_tfafieldnames.h b/sound/soc/codecs/inc/tfa9896_tfafieldnames.h new file mode 100644 index 000000000000..9c2b15f02d42 --- /dev/null +++ b/sound/soc/codecs/inc/tfa9896_tfafieldnames.h @@ -0,0 +1,919 @@ +/** Filename: tfa9896_tfafieldnames.h + * This file was generated automatically on 08/15/16 at 09:43:38. + * Source file: TFA9896_N1B1_I2C_regmap_V16.xlsx + */ + +#ifndef _TFA9896_TFAFIELDNAMES_H +#define _TFA9896_TFAFIELDNAMES_H + + +#define TFA9896_I2CVERSION 16 + +typedef enum nxpTFA9896BfEnumList { + TFA9896_BF_VDDS = 0x0000, /*!< Power-on-reset flag (auto clear by reading) */ + TFA9896_BF_PLLS = 0x0010, /*!< PLL lock to programmed frequency */ + TFA9896_BF_OTDS = 0x0020, /*!< Over Temperature Protection alarm */ + TFA9896_BF_OVDS = 0x0030, /*!< Over Voltage Protection alarm */ + TFA9896_BF_UVDS = 0x0040, /*!< Under Voltage Protection alarm */ + TFA9896_BF_OCDS = 0x0050, /*!< Over Current Protection alarm */ + TFA9896_BF_CLKS = 0x0060, /*!< Clocks stable flag */ + TFA9896_BF_CLIPS = 0x0070, /*!< Amplifier clipping */ + TFA9896_BF_MTPB = 0x0080, /*!< MTP busy copying data to/from I2C registers */ + TFA9896_BF_NOCLK = 0x0090, /*!< lost clock detection (reference input clock) */ + TFA9896_BF_SPKS = 0x00a0, /*!< Speaker error */ + TFA9896_BF_ACS = 0x00b0, /*!< Cold Start required */ + TFA9896_BF_SWS = 0x00c0, /*!< Amplifier engage (Amp Switching) */ + TFA9896_BF_WDS = 0x00d0, /*!< watchdog reset (activates reset) */ + TFA9896_BF_AMPS = 0x00e0, /*!< Amplifier is enabled by manager */ + TFA9896_BF_AREFS = 0x00f0, /*!< References are enabled by manager */ + TFA9896_BF_BATS = 0x0109, /*!< Battery voltage from ADC readout */ + TFA9896_BF_TEMPS = 0x0208, /*!< Temperature readout from the temperature sensor ( C) */ + TFA9896_BF_REV = 0x030f, /*!< Device revision information */ + TFA9896_BF_RCV = 0x0420, /*!< Enable receiver mode */ + TFA9896_BF_CHS12 = 0x0431, /*!< Channel Selection TDM input for Coolflux */ + TFA9896_BF_INPLVL= 0x0450, /*!< Input level selection attenuator ( */ + TFA9896_BF_CHSA = 0x0461, /*!< Input selection for amplifier */ + TFA9896_BF_AUDFS = 0x04c3, /*!< Audio sample rate setting */ + TFA9896_BF_BSSCR = 0x0501, /*!< Batteery protection attack time */ + TFA9896_BF_BSST = 0x0523, /*!< Battery protection threshold level */ + TFA9896_BF_BSSRL = 0x0561, /*!< Battery protection maximum reduction */ + TFA9896_BF_BSSRR = 0x0582, /*!< Battery protection release time */ + TFA9896_BF_BSSHY = 0x05b1, /*!< Battery Protection Hysteresis */ + TFA9896_BF_BSSR = 0x05e0, /*!< Battery voltage value for read out (only) */ + TFA9896_BF_BSSBY = 0x05f0, /*!< Bypass clipper battery protection */ + TFA9896_BF_DPSA = 0x0600, /*!< Enable dynamic powerstage activation (DPSA) */ + TFA9896_BF_ATTEN = 0x0613, /*!< Gain attenuation setting */ + TFA9896_BF_CFSM = 0x0650, /*!< Soft mute in CoolFlux */ + TFA9896_BF_BSSS = 0x0670, /*!< Battery sense steepness */ + TFA9896_BF_VOL = 0x0687, /*!< Coolflux volume control */ + TFA9896_BF_DCVO2 = 0x0702, /*!< Second Boost Voltage */ + TFA9896_BF_DCMCC = 0x0733, /*!< Max boost coil current - step of 175 mA */ + TFA9896_BF_DCVO1 = 0x0772, /*!< First Boost Voltage */ + TFA9896_BF_DCIE = 0x07a0, /*!< Adaptive boost mode */ + TFA9896_BF_DCSR = 0x07b0, /*!< Soft Rampup/down mode for DCDC controller */ + TFA9896_BF_DCPAVG= 0x07c0, /*!< ctrl_peak2avg for analog part of DCDC */ + TFA9896_BF_DCPWM = 0x07d0, /*!< DCDC PWM only mode */ + TFA9896_BF_TROS = 0x0800, /*!< Selection ambient temperature for speaker calibration */ + TFA9896_BF_EXTTS = 0x0818, /*!< External temperature for speaker calibration (C) */ + TFA9896_BF_PWDN = 0x0900, /*!< powerdown selection */ + TFA9896_BF_I2CR = 0x0910, /*!< All I2C registers reset to default */ + TFA9896_BF_CFE = 0x0920, /*!< Enable CoolFlux */ + TFA9896_BF_AMPE = 0x0930, /*!< Enable Amplifier */ + TFA9896_BF_DCA = 0x0940, /*!< Enable DCDC Boost converter */ + TFA9896_BF_SBSL = 0x0950, /*!< Coolflux configured */ + TFA9896_BF_AMPC = 0x0960, /*!< Selection if Coolflux enables amplifier */ + TFA9896_BF_DCDIS = 0x0970, /*!< DCDC boost converter not connected */ + TFA9896_BF_PSDR = 0x0980, /*!< IDDQ amplifier test selection */ + TFA9896_BF_INTPAD= 0x09c1, /*!< INT pad (interrupt bump output) configuration */ + TFA9896_BF_IPLL = 0x09e0, /*!< PLL input reference clock selection */ + TFA9896_BF_DCTRIP= 0x0a04, /*!< Adaptive boost trip levels (effective only when boost_intel is set to 1) */ + TFA9896_BF_DCHOLD= 0x0a54, /*!< Hold time for DCDC booster (effective only when boost_intel is set to 1) */ + TFA9896_BF_MTPK = 0x0b07, /*!< KEY2 to access key2 protected registers (default for engineering) */ + TFA9896_BF_CVFDLY= 0x0c25, /*!< Fractional delay adjustment between current and voltage sense */ + TFA9896_BF_OPENMTP= 0x0ec0, /*!< Enable programming of the MTP memory */ + TFA9896_BF_TDMPRF= 0x1011, /*!< TDM usecase selection control */ + TFA9896_BF_TDMEN = 0x1030, /*!< TDM interface enable */ + TFA9896_BF_TDMCKINV= 0x1040, /*!< TDM clock inversion, receive on */ + TFA9896_BF_TDMFSLN= 0x1053, /*!< TDM FS length */ + TFA9896_BF_TDMFSPOL= 0x1090, /*!< TDM FS polarity (start frame) */ + TFA9896_BF_TDMSAMSZ= 0x10a4, /*!< TDM sample size for all TDM sinks and sources */ + TFA9896_BF_TDMSLOTS= 0x1103, /*!< TDM number of slots */ + TFA9896_BF_TDMSLLN= 0x1144, /*!< TDM slot length */ + TFA9896_BF_TDMBRMG= 0x1194, /*!< TDM bits remaining after the last slot */ + TFA9896_BF_TDMDDEL= 0x11e0, /*!< TDM data delay */ + TFA9896_BF_TDMDADJ= 0x11f0, /*!< TDM data adjustment */ + TFA9896_BF_TDMTXFRM= 0x1201, /*!< TDM TXDATA format */ + TFA9896_BF_TDMUUS0= 0x1221, /*!< TDM TXDATA format unused slot SD0 */ + TFA9896_BF_TDMUUS1= 0x1241, /*!< TDM TXDATA format unused slot SD1 */ + TFA9896_BF_TDMSI0EN= 0x1270, /*!< TDM sink0 enable */ + TFA9896_BF_TDMSI1EN= 0x1280, /*!< TDM sink1 enable */ + TFA9896_BF_TDMSI2EN= 0x1290, /*!< TDM sink2 enable */ + TFA9896_BF_TDMSO0EN= 0x12a0, /*!< TDM source0 enable */ + TFA9896_BF_TDMSO1EN= 0x12b0, /*!< TDM source1 enable */ + TFA9896_BF_TDMSO2EN= 0x12c0, /*!< TDM source2 enable */ + TFA9896_BF_TDMSI0IO= 0x12d0, /*!< TDM sink0 IO selection */ + TFA9896_BF_TDMSI1IO= 0x12e0, /*!< TDM sink1 IO selection */ + TFA9896_BF_TDMSI2IO= 0x12f0, /*!< TDM sink2 IO selection */ + TFA9896_BF_TDMSO0IO= 0x1300, /*!< TDM source0 IO selection */ + TFA9896_BF_TDMSO1IO= 0x1310, /*!< TDM source1 IO selection */ + TFA9896_BF_TDMSO2IO= 0x1320, /*!< TDM source2 IO selection */ + TFA9896_BF_TDMSI0SL= 0x1333, /*!< TDM sink0 slot position [GAIN IN] */ + TFA9896_BF_TDMSI1SL= 0x1373, /*!< TDM sink1 slot position [CH1 IN] */ + TFA9896_BF_TDMSI2SL= 0x13b3, /*!< TDM sink2 slot position [CH2 IN] */ + TFA9896_BF_TDMSO0SL= 0x1403, /*!< TDM source0 slot position [GAIN OUT] */ + TFA9896_BF_TDMSO1SL= 0x1443, /*!< TDM source1 slot position [Voltage Sense] */ + TFA9896_BF_TDMSO2SL= 0x1483, /*!< TDM source2 slot position [Current Sense] */ + TFA9896_BF_NBCK = 0x14c3, /*!< TDM NBCK bit clock ratio */ + TFA9896_BF_INTOVDDS= 0x2000, /*!< flag_por_int_out */ + TFA9896_BF_INTOPLLS= 0x2010, /*!< flag_pll_lock_int_out */ + TFA9896_BF_INTOOTDS= 0x2020, /*!< flag_otpok_int_out */ + TFA9896_BF_INTOOVDS= 0x2030, /*!< flag_ovpok_int_out */ + TFA9896_BF_INTOUVDS= 0x2040, /*!< flag_uvpok_int_out */ + TFA9896_BF_INTOOCDS= 0x2050, /*!< flag_ocp_alarm_int_out */ + TFA9896_BF_INTOCLKS= 0x2060, /*!< flag_clocks_stable_int_out */ + TFA9896_BF_INTOCLIPS= 0x2070, /*!< flag_clip_int_out */ + TFA9896_BF_INTOMTPB= 0x2080, /*!< mtp_busy_int_out */ + TFA9896_BF_INTONOCLK= 0x2090, /*!< flag_lost_clk_int_out */ + TFA9896_BF_INTOSPKS= 0x20a0, /*!< flag_cf_speakererror_int_out */ + TFA9896_BF_INTOACS= 0x20b0, /*!< flag_cold_started_int_out */ + TFA9896_BF_INTOSWS= 0x20c0, /*!< flag_engage_int_out */ + TFA9896_BF_INTOWDS= 0x20d0, /*!< flag_watchdog_reset_int_out */ + TFA9896_BF_INTOAMPS= 0x20e0, /*!< flag_enbl_amp_int_out */ + TFA9896_BF_INTOAREFS= 0x20f0, /*!< flag_enbl_ref_int_out */ + TFA9896_BF_INTOERR= 0x2200, /*!< flag_cfma_err_int_out */ + TFA9896_BF_INTOACK= 0x2210, /*!< flag_cfma_ack_int_out */ + TFA9896_BF_INTIVDDS= 0x2300, /*!< flag_por_int_in */ + TFA9896_BF_INTIPLLS= 0x2310, /*!< flag_pll_lock_int_in */ + TFA9896_BF_INTIOTDS= 0x2320, /*!< flag_otpok_int_in */ + TFA9896_BF_INTIOVDS= 0x2330, /*!< flag_ovpok_int_in */ + TFA9896_BF_INTIUVDS= 0x2340, /*!< flag_uvpok_int_in */ + TFA9896_BF_INTIOCDS= 0x2350, /*!< flag_ocp_alarm_int_in */ + TFA9896_BF_INTICLKS= 0x2360, /*!< flag_clocks_stable_int_in */ + TFA9896_BF_INTICLIPS= 0x2370, /*!< flag_clip_int_in */ + TFA9896_BF_INTIMTPB= 0x2380, /*!< mtp_busy_int_in */ + TFA9896_BF_INTINOCLK= 0x2390, /*!< flag_lost_clk_int_in */ + TFA9896_BF_INTISPKS= 0x23a0, /*!< flag_cf_speakererror_int_in */ + TFA9896_BF_INTIACS= 0x23b0, /*!< flag_cold_started_int_in */ + TFA9896_BF_INTISWS= 0x23c0, /*!< flag_engage_int_in */ + TFA9896_BF_INTIWDS= 0x23d0, /*!< flag_watchdog_reset_int_in */ + TFA9896_BF_INTIAMPS= 0x23e0, /*!< flag_enbl_amp_int_in */ + TFA9896_BF_INTIAREFS= 0x23f0, /*!< flag_enbl_ref_int_in */ + TFA9896_BF_INTIERR= 0x2500, /*!< flag_cfma_err_int_in */ + TFA9896_BF_INTIACK= 0x2510, /*!< flag_cfma_ack_int_in */ + TFA9896_BF_INTENVDDS= 0x2600, /*!< flag_por_int_enable */ + TFA9896_BF_INTENPLLS= 0x2610, /*!< flag_pll_lock_int_enable */ + TFA9896_BF_INTENOTDS= 0x2620, /*!< flag_otpok_int_enable */ + TFA9896_BF_INTENOVDS= 0x2630, /*!< flag_ovpok_int_enable */ + TFA9896_BF_INTENUVDS= 0x2640, /*!< flag_uvpok_int_enable */ + TFA9896_BF_INTENOCDS= 0x2650, /*!< flag_ocp_alarm_int_enable */ + TFA9896_BF_INTENCLKS= 0x2660, /*!< flag_clocks_stable_int_enable */ + TFA9896_BF_INTENCLIPS= 0x2670, /*!< flag_clip_int_enable */ + TFA9896_BF_INTENMTPB= 0x2680, /*!< mtp_busy_int_enable */ + TFA9896_BF_INTENNOCLK= 0x2690, /*!< flag_lost_clk_int_enable */ + TFA9896_BF_INTENSPKS= 0x26a0, /*!< flag_cf_speakererror_int_enable */ + TFA9896_BF_INTENACS= 0x26b0, /*!< flag_cold_started_int_enable */ + TFA9896_BF_INTENSWS= 0x26c0, /*!< flag_engage_int_enable */ + TFA9896_BF_INTENWDS= 0x26d0, /*!< flag_watchdog_reset_int_enable */ + TFA9896_BF_INTENAMPS= 0x26e0, /*!< flag_enbl_amp_int_enable */ + TFA9896_BF_INTENAREFS= 0x26f0, /*!< flag_enbl_ref_int_enable */ + TFA9896_BF_INTENERR= 0x2800, /*!< flag_cfma_err_int_enable */ + TFA9896_BF_INTENACK= 0x2810, /*!< flag_cfma_ack_int_enable */ + TFA9896_BF_INTPOLVDDS= 0x2900, /*!< flag_por_int_pol */ + TFA9896_BF_INTPOLPLLS= 0x2910, /*!< flag_pll_lock_int_pol */ + TFA9896_BF_INTPOLOTDS= 0x2920, /*!< flag_otpok_int_pol */ + TFA9896_BF_INTPOLOVDS= 0x2930, /*!< flag_ovpok_int_pol */ + TFA9896_BF_INTPOLUVDS= 0x2940, /*!< flag_uvpok_int_pol */ + TFA9896_BF_INTPOLOCDS= 0x2950, /*!< flag_ocp_alarm_int_pol */ + TFA9896_BF_INTPOLCLKS= 0x2960, /*!< flag_clocks_stable_int_pol */ + TFA9896_BF_INTPOLCLIPS= 0x2970, /*!< flag_clip_int_pol */ + TFA9896_BF_INTPOLMTPB= 0x2980, /*!< mtp_busy_int_pol */ + TFA9896_BF_INTPOLNOCLK= 0x2990, /*!< flag_lost_clk_int_pol */ + TFA9896_BF_INTPOLSPKS= 0x29a0, /*!< flag_cf_speakererror_int_pol */ + TFA9896_BF_INTPOLACS= 0x29b0, /*!< flag_cold_started_int_pol */ + TFA9896_BF_INTPOLSWS= 0x29c0, /*!< flag_engage_int_pol */ + TFA9896_BF_INTPOLWDS= 0x29d0, /*!< flag_watchdog_reset_int_pol */ + TFA9896_BF_INTPOLAMPS= 0x29e0, /*!< flag_enbl_amp_int_pol */ + TFA9896_BF_INTPOLAREFS= 0x29f0, /*!< flag_enbl_ref_int_pol */ + TFA9896_BF_INTPOLERR= 0x2b00, /*!< flag_cfma_err_int_pol */ + TFA9896_BF_INTPOLACK= 0x2b10, /*!< flag_cfma_ack_int_pol */ + TFA9896_BF_CLIP = 0x4900, /*!< Bypass clip control */ + TFA9896_BF_CIMTP = 0x62b0, /*!< Start copying data from I2C mtp registers to mtp */ + TFA9896_BF_RST = 0x7000, /*!< Reset CoolFlux DSP */ + TFA9896_BF_DMEM = 0x7011, /*!< Target memory for access */ + TFA9896_BF_AIF = 0x7030, /*!< Auto increment flag for memory-address */ + TFA9896_BF_CFINT = 0x7040, /*!< CF Interrupt - auto clear */ + TFA9896_BF_REQ = 0x7087, /*!< CF request for accessing the 8 channels */ + TFA9896_BF_MADD = 0x710f, /*!< Memory address */ + TFA9896_BF_MEMA = 0x720f, /*!< Activate memory access */ + TFA9896_BF_ERR = 0x7307, /*!< CF error flags */ + TFA9896_BF_ACK = 0x7387, /*!< CF acknowledgement of the requests channels */ + TFA9896_BF_MTPOTC= 0x8000, /*!< Calibration schedule selection */ + TFA9896_BF_MTPEX = 0x8010, /*!< Calibration of RON status bit */ +} nxpTFA9896BfEnumList_t; +#define TFA9896_NAMETABLE static tfaBfName_t Tfa9896DatasheetNames[]= {\ + { 0x0, "VDDS"}, /* Power-on-reset flag (auto clear by reading) , */\ + { 0x10, "PLLS"}, /* PLL lock to programmed frequency , */\ + { 0x20, "OTDS"}, /* Over Temperature Protection alarm , */\ + { 0x30, "OVDS"}, /* Over Voltage Protection alarm , */\ + { 0x40, "UVDS"}, /* Under Voltage Protection alarm , */\ + { 0x50, "OCDS"}, /* Over Current Protection alarm , */\ + { 0x60, "CLKS"}, /* Clocks stable flag , */\ + { 0x70, "CLIPS"}, /* Amplifier clipping , */\ + { 0x80, "MTPB"}, /* MTP busy copying data to/from I2C registers , */\ + { 0x90, "NOCLK"}, /* lost clock detection (reference input clock) , */\ + { 0xa0, "SPKS"}, /* Speaker error , */\ + { 0xb0, "ACS"}, /* Cold Start required , */\ + { 0xc0, "SWS"}, /* Amplifier engage (Amp Switching) , */\ + { 0xd0, "WDS"}, /* watchdog reset (activates reset) , */\ + { 0xe0, "AMPS"}, /* Amplifier is enabled by manager , */\ + { 0xf0, "AREFS"}, /* References are enabled by manager , */\ + { 0x109, "BATS"}, /* Battery voltage from ADC readout , */\ + { 0x208, "TEMPS"}, /* Temperature readout from the temperature sensor ( C), */\ + { 0x30f, "REV"}, /* Device revision information , */\ + { 0x420, "RCV"}, /* Enable receiver mode , */\ + { 0x431, "CHS12"}, /* Channel Selection TDM input for Coolflux , */\ + { 0x450, "INPLVL"}, /* Input level selection attenuator ( , */\ + { 0x461, "CHSA"}, /* Input selection for amplifier , */\ + { 0x4c3, "AUDFS"}, /* Audio sample rate setting , */\ + { 0x501, "BSSCR"}, /* Batteery protection attack time , */\ + { 0x523, "BSST"}, /* Battery protection threshold level , */\ + { 0x561, "BSSRL"}, /* Battery protection maximum reduction , */\ + { 0x582, "BSSRR"}, /* Battery protection release time , */\ + { 0x5b1, "BSSHY"}, /* Battery Protection Hysteresis , */\ + { 0x5e0, "BSSR"}, /* Battery voltage value for read out (only) , */\ + { 0x5f0, "BSSBY"}, /* Bypass clipper battery protection , */\ + { 0x600, "DPSA"}, /* Enable dynamic powerstage activation (DPSA) , */\ + { 0x613, "ATTEN"}, /* Gain attenuation setting , */\ + { 0x650, "CFSM"}, /* Soft mute in CoolFlux , */\ + { 0x670, "BSSS"}, /* Battery sense steepness , */\ + { 0x687, "VOL"}, /* Coolflux volume control , */\ + { 0x702, "DCVO2"}, /* Second Boost Voltage , */\ + { 0x733, "DCMCC"}, /* Max boost coil current - step of 175 mA , */\ + { 0x772, "DCVO1"}, /* First Boost Voltage , */\ + { 0x7a0, "DCIE"}, /* Adaptive boost mode , */\ + { 0x7b0, "DCSR"}, /* Soft Rampup/down mode for DCDC controller , */\ + { 0x7c0, "DCPAVG"}, /* ctrl_peak2avg for analog part of DCDC , */\ + { 0x7d0, "DCPWM"}, /* DCDC PWM only mode , */\ + { 0x800, "TROS"}, /* Selection ambient temperature for speaker calibration , */\ + { 0x818, "EXTTS"}, /* External temperature for speaker calibration (C) , */\ + { 0x900, "PWDN"}, /* powerdown selection , */\ + { 0x910, "I2CR"}, /* All I2C registers reset to default , */\ + { 0x920, "CFE"}, /* Enable CoolFlux , */\ + { 0x930, "AMPE"}, /* Enable Amplifier , */\ + { 0x940, "DCA"}, /* Enable DCDC Boost converter , */\ + { 0x950, "SBSL"}, /* Coolflux configured , */\ + { 0x960, "AMPC"}, /* Selection if Coolflux enables amplifier , */\ + { 0x970, "DCDIS"}, /* DCDC boost converter not connected , */\ + { 0x980, "PSDR"}, /* IDDQ amplifier test selection , */\ + { 0x9c1, "INTPAD"}, /* INT pad (interrupt bump output) configuration , */\ + { 0x9e0, "IPLL"}, /* PLL input reference clock selection , */\ + { 0xa04, "DCTRIP"}, /* Adaptive boost trip levels (effective only when boost_intel is set to 1), */\ + { 0xa54, "DCHOLD"}, /* Hold time for DCDC booster (effective only when boost_intel is set to 1), */\ + { 0xb07, "MTPK"}, /* KEY2 to access key2 protected registers (default for engineering), */\ + { 0xc25, "CVFDLY"}, /* Fractional delay adjustment between current and voltage sense, */\ + { 0xec0, "OPENMTP"}, /* Enable programming of the MTP memory , */\ + { 0x1011, "TDMPRF"}, /* TDM usecase selection control , */\ + { 0x1030, "TDMEN"}, /* TDM interface enable , */\ + { 0x1040, "TDMCKINV"}, /* TDM clock inversion, receive on , */\ + { 0x1053, "TDMFSLN"}, /* TDM FS length , */\ + { 0x1090, "TDMFSPOL"}, /* TDM FS polarity (start frame) , */\ + { 0x10a4, "TDMSAMSZ"}, /* TDM sample size for all TDM sinks and sources , */\ + { 0x1103, "TDMSLOTS"}, /* TDM number of slots , */\ + { 0x1144, "TDMSLLN"}, /* TDM slot length , */\ + { 0x1194, "TDMBRMG"}, /* TDM bits remaining after the last slot , */\ + { 0x11e0, "TDMDDEL"}, /* TDM data delay , */\ + { 0x11f0, "TDMDADJ"}, /* TDM data adjustment , */\ + { 0x1201, "TDMTXFRM"}, /* TDM TXDATA format , */\ + { 0x1221, "TDMUUS0"}, /* TDM TXDATA format unused slot SD0 , */\ + { 0x1241, "TDMUUS1"}, /* TDM TXDATA format unused slot SD1 , */\ + { 0x1270, "TDMSI0EN"}, /* TDM sink0 enable , */\ + { 0x1280, "TDMSI1EN"}, /* TDM sink1 enable , */\ + { 0x1290, "TDMSI2EN"}, /* TDM sink2 enable , */\ + { 0x12a0, "TDMSO0EN"}, /* TDM source0 enable , */\ + { 0x12b0, "TDMSO1EN"}, /* TDM source1 enable , */\ + { 0x12c0, "TDMSO2EN"}, /* TDM source2 enable , */\ + { 0x12d0, "TDMSI0IO"}, /* TDM sink0 IO selection , */\ + { 0x12e0, "TDMSI1IO"}, /* TDM sink1 IO selection , */\ + { 0x12f0, "TDMSI2IO"}, /* TDM sink2 IO selection , */\ + { 0x1300, "TDMSO0IO"}, /* TDM source0 IO selection , */\ + { 0x1310, "TDMSO1IO"}, /* TDM source1 IO selection , */\ + { 0x1320, "TDMSO2IO"}, /* TDM source2 IO selection , */\ + { 0x1333, "TDMSI0SL"}, /* TDM sink0 slot position [GAIN IN] , */\ + { 0x1373, "TDMSI1SL"}, /* TDM sink1 slot position [CH1 IN] , */\ + { 0x13b3, "TDMSI2SL"}, /* TDM sink2 slot position [CH2 IN] , */\ + { 0x1403, "TDMSO0SL"}, /* TDM source0 slot position [GAIN OUT] , */\ + { 0x1443, "TDMSO1SL"}, /* TDM source1 slot position [Voltage Sense] , */\ + { 0x1483, "TDMSO2SL"}, /* TDM source2 slot position [Current Sense] , */\ + { 0x14c3, "NBCK"}, /* TDM NBCK bit clock ratio , */\ + { 0x2000, "INTOVDDS"}, /* flag_por_int_out , */\ + { 0x2010, "INTOPLLS"}, /* flag_pll_lock_int_out , */\ + { 0x2020, "INTOOTDS"}, /* flag_otpok_int_out , */\ + { 0x2030, "INTOOVDS"}, /* flag_ovpok_int_out , */\ + { 0x2040, "INTOUVDS"}, /* flag_uvpok_int_out , */\ + { 0x2050, "INTOOCDS"}, /* flag_ocp_alarm_int_out , */\ + { 0x2060, "INTOCLKS"}, /* flag_clocks_stable_int_out , */\ + { 0x2070, "INTOCLIPS"}, /* flag_clip_int_out , */\ + { 0x2080, "INTOMTPB"}, /* mtp_busy_int_out , */\ + { 0x2090, "INTONOCLK"}, /* flag_lost_clk_int_out , */\ + { 0x20a0, "INTOSPKS"}, /* flag_cf_speakererror_int_out , */\ + { 0x20b0, "INTOACS"}, /* flag_cold_started_int_out , */\ + { 0x20c0, "INTOSWS"}, /* flag_engage_int_out , */\ + { 0x20d0, "INTOWDS"}, /* flag_watchdog_reset_int_out , */\ + { 0x20e0, "INTOAMPS"}, /* flag_enbl_amp_int_out , */\ + { 0x20f0, "INTOAREFS"}, /* flag_enbl_ref_int_out , */\ + { 0x2200, "INTOERR"}, /* flag_cfma_err_int_out , */\ + { 0x2210, "INTOACK"}, /* flag_cfma_ack_int_out , */\ + { 0x2300, "INTIVDDS"}, /* flag_por_int_in , */\ + { 0x2310, "INTIPLLS"}, /* flag_pll_lock_int_in , */\ + { 0x2320, "INTIOTDS"}, /* flag_otpok_int_in , */\ + { 0x2330, "INTIOVDS"}, /* flag_ovpok_int_in , */\ + { 0x2340, "INTIUVDS"}, /* flag_uvpok_int_in , */\ + { 0x2350, "INTIOCDS"}, /* flag_ocp_alarm_int_in , */\ + { 0x2360, "INTICLKS"}, /* flag_clocks_stable_int_in , */\ + { 0x2370, "INTICLIPS"}, /* flag_clip_int_in , */\ + { 0x2380, "INTIMTPB"}, /* mtp_busy_int_in , */\ + { 0x2390, "INTINOCLK"}, /* flag_lost_clk_int_in , */\ + { 0x23a0, "INTISPKS"}, /* flag_cf_speakererror_int_in , */\ + { 0x23b0, "INTIACS"}, /* flag_cold_started_int_in , */\ + { 0x23c0, "INTISWS"}, /* flag_engage_int_in , */\ + { 0x23d0, "INTIWDS"}, /* flag_watchdog_reset_int_in , */\ + { 0x23e0, "INTIAMPS"}, /* flag_enbl_amp_int_in , */\ + { 0x23f0, "INTIAREFS"}, /* flag_enbl_ref_int_in , */\ + { 0x2500, "INTIERR"}, /* flag_cfma_err_int_in , */\ + { 0x2510, "INTIACK"}, /* flag_cfma_ack_int_in , */\ + { 0x2600, "INTENVDDS"}, /* flag_por_int_enable , */\ + { 0x2610, "INTENPLLS"}, /* flag_pll_lock_int_enable , */\ + { 0x2620, "INTENOTDS"}, /* flag_otpok_int_enable , */\ + { 0x2630, "INTENOVDS"}, /* flag_ovpok_int_enable , */\ + { 0x2640, "INTENUVDS"}, /* flag_uvpok_int_enable , */\ + { 0x2650, "INTENOCDS"}, /* flag_ocp_alarm_int_enable , */\ + { 0x2660, "INTENCLKS"}, /* flag_clocks_stable_int_enable , */\ + { 0x2670, "INTENCLIPS"}, /* flag_clip_int_enable , */\ + { 0x2680, "INTENMTPB"}, /* mtp_busy_int_enable , */\ + { 0x2690, "INTENNOCLK"}, /* flag_lost_clk_int_enable , */\ + { 0x26a0, "INTENSPKS"}, /* flag_cf_speakererror_int_enable , */\ + { 0x26b0, "INTENACS"}, /* flag_cold_started_int_enable , */\ + { 0x26c0, "INTENSWS"}, /* flag_engage_int_enable , */\ + { 0x26d0, "INTENWDS"}, /* flag_watchdog_reset_int_enable , */\ + { 0x26e0, "INTENAMPS"}, /* flag_enbl_amp_int_enable , */\ + { 0x26f0, "INTENAREFS"}, /* flag_enbl_ref_int_enable , */\ + { 0x2800, "INTENERR"}, /* flag_cfma_err_int_enable , */\ + { 0x2810, "INTENACK"}, /* flag_cfma_ack_int_enable , */\ + { 0x2900, "INTPOLVDDS"}, /* flag_por_int_pol , */\ + { 0x2910, "INTPOLPLLS"}, /* flag_pll_lock_int_pol , */\ + { 0x2920, "INTPOLOTDS"}, /* flag_otpok_int_pol , */\ + { 0x2930, "INTPOLOVDS"}, /* flag_ovpok_int_pol , */\ + { 0x2940, "INTPOLUVDS"}, /* flag_uvpok_int_pol , */\ + { 0x2950, "INTPOLOCDS"}, /* flag_ocp_alarm_int_pol , */\ + { 0x2960, "INTPOLCLKS"}, /* flag_clocks_stable_int_pol , */\ + { 0x2970, "INTPOLCLIPS"}, /* flag_clip_int_pol , */\ + { 0x2980, "INTPOLMTPB"}, /* mtp_busy_int_pol , */\ + { 0x2990, "INTPOLNOCLK"}, /* flag_lost_clk_int_pol , */\ + { 0x29a0, "INTPOLSPKS"}, /* flag_cf_speakererror_int_pol , */\ + { 0x29b0, "INTPOLACS"}, /* flag_cold_started_int_pol , */\ + { 0x29c0, "INTPOLSWS"}, /* flag_engage_int_pol , */\ + { 0x29d0, "INTPOLWDS"}, /* flag_watchdog_reset_int_pol , */\ + { 0x29e0, "INTPOLAMPS"}, /* flag_enbl_amp_int_pol , */\ + { 0x29f0, "INTPOLAREFS"}, /* flag_enbl_ref_int_pol , */\ + { 0x2b00, "INTPOLERR"}, /* flag_cfma_err_int_pol , */\ + { 0x2b10, "INTPOLACK"}, /* flag_cfma_ack_int_pol , */\ + { 0x4900, "CLIP"}, /* Bypass clip control , */\ + { 0x62b0, "CIMTP"}, /* Start copying data from I2C mtp registers to mtp , */\ + { 0x7000, "RST"}, /* Reset CoolFlux DSP , */\ + { 0x7011, "DMEM"}, /* Target memory for access , */\ + { 0x7030, "AIF"}, /* Auto increment flag for memory-address , */\ + { 0x7040, "CFINT"}, /* CF Interrupt - auto clear , */\ + { 0x7087, "REQ"}, /* CF request for accessing the 8 channels , */\ + { 0x710f, "MADD"}, /* Memory address , */\ + { 0x720f, "MEMA"}, /* Activate memory access , */\ + { 0x7307, "ERR"}, /* CF error flags , */\ + { 0x7387, "ACK"}, /* CF acknowledgement of the requests channels , */\ + { 0x8000, "MTPOTC"}, /* Calibration schedule selection , */\ + { 0x8010, "MTPEX"}, /* Calibration of RON status bit , */\ + { 0x8045, "SWPROFIL" },\ + { 0x80a5, "SWVSTEP" },\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA9896_BITNAMETABLE static tfaBfName_t Tfa9896BitNames[]= {\ + { 0x0, "flag_por"}, /* Power-on-reset flag (auto clear by reading) , */\ + { 0x10, "flag_pll_lock"}, /* PLL lock to programmed frequency , */\ + { 0x20, "flag_otpok"}, /* Over Temperature Protection alarm , */\ + { 0x30, "flag_ovpok"}, /* Over Voltage Protection alarm , */\ + { 0x40, "flag_uvpok"}, /* Under Voltage Protection alarm , */\ + { 0x50, "flag_ocp_alarm"}, /* Over Current Protection alarm , */\ + { 0x60, "flag_clocks_stable"}, /* Clocks stable flag , */\ + { 0x70, "flag_clip"}, /* Amplifier clipping , */\ + { 0x80, "mtp_busy"}, /* MTP busy copying data to/from I2C registers , */\ + { 0x90, "flag_lost_clk"}, /* lost clock detection (reference input clock) , */\ + { 0xa0, "flag_cf_speakererror"}, /* Speaker error , */\ + { 0xb0, "flag_cold_started"}, /* Cold Start required , */\ + { 0xc0, "flag_engage"}, /* Amplifier engage (Amp Switching) , */\ + { 0xd0, "flag_watchdog_reset"}, /* watchdog reset (activates reset) , */\ + { 0xe0, "flag_enbl_amp"}, /* Amplifier is enabled by manager , */\ + { 0xf0, "flag_enbl_ref"}, /* References are enabled by manager , */\ + { 0x109, "bat_adc"}, /* Battery voltage from ADC readout , */\ + { 0x208, "temp_adc"}, /* Temperature readout from the temperature sensor ( C), */\ + { 0x30f, "device_rev"}, /* Device revision information , */\ + { 0x420, "ctrl_rcv"}, /* Enable receiver mode , */\ + { 0x431, "chan_sel"}, /* Channel Selection TDM input for Coolflux , */\ + { 0x450, "input_level"}, /* Input level selection attenuator ( , */\ + { 0x461, "vamp_sel"}, /* Input selection for amplifier , */\ + { 0x4c3, "audio_fs"}, /* Audio sample rate setting , */\ + { 0x501, "vbat_prot_attacktime"}, /* Batteery protection attack time , */\ + { 0x523, "vbat_prot_thlevel"}, /* Battery protection threshold level , */\ + { 0x561, "vbat_prot_max_reduct"}, /* Battery protection maximum reduction , */\ + { 0x582, "vbat_prot_release_t"}, /* Battery protection release time , */\ + { 0x5b1, "vbat_prot_hysterese"}, /* Battery Protection Hysteresis , */\ + { 0x5d0, "reset_min_vbat"}, /* Battery supply safeguard clipper reset ( if CF_DSP is bypassed), */\ + { 0x5e0, "sel_vbat"}, /* Battery voltage value for read out (only) , */\ + { 0x5f0, "bypass_clipper"}, /* Bypass clipper battery protection , */\ + { 0x600, "dpsa"}, /* Enable dynamic powerstage activation (DPSA) , */\ + { 0x613, "ctrl_att"}, /* Gain attenuation setting , */\ + { 0x650, "cf_mute"}, /* Soft mute in CoolFlux , */\ + { 0x670, "batsense_steepness"}, /* Battery sense steepness , */\ + { 0x687, "vol"}, /* Coolflux volume control , */\ + { 0x702, "scnd_boost_voltage"}, /* Second Boost Voltage , */\ + { 0x733, "boost_cur"}, /* Max boost coil current - step of 175 mA , */\ + { 0x772, "frst_boost_voltage"}, /* First Boost Voltage , */\ + { 0x7a0, "boost_intel"}, /* Adaptive boost mode , */\ + { 0x7b0, "boost_speed"}, /* Soft Rampup/down mode for DCDC controller , */\ + { 0x7c0, "boost_peak2avg"}, /* ctrl_peak2avg for analog part of DCDC , */\ + { 0x7d0, "dcdc_pwmonly"}, /* DCDC PWM only mode , */\ + { 0x7e0, "ignore_flag_voutcomp86"}, /* Ignore flag_voutcomp86 (flag from analog) , */\ + { 0x800, "ext_temp_sel"}, /* Selection ambient temperature for speaker calibration , */\ + { 0x818, "ext_temp"}, /* External temperature for speaker calibration (C) , */\ + { 0x900, "powerdown"}, /* powerdown selection , */\ + { 0x910, "reset"}, /* All I2C registers reset to default , */\ + { 0x920, "enbl_coolflux"}, /* Enable CoolFlux , */\ + { 0x930, "enbl_amplifier"}, /* Enable Amplifier , */\ + { 0x940, "enbl_boost"}, /* Enable DCDC Boost converter , */\ + { 0x950, "coolflux_configured"}, /* Coolflux configured , */\ + { 0x960, "sel_enbl_amplifier"}, /* Selection if Coolflux enables amplifier , */\ + { 0x970, "dcdcoff_mode"}, /* DCDC boost converter not connected , */\ + { 0x980, "iddqtest"}, /* IDDQ amplifier test selection , */\ + { 0x9c1, "int_pad_io"}, /* INT pad (interrupt bump output) configuration , */\ + { 0x9e0, "sel_fs_bck"}, /* PLL input reference clock selection , */\ + { 0x9f0, "sel_scl_cf_clock"}, /* Coolflux sub-system clock selection , */\ + { 0xa04, "boost_trip_lvl"}, /* Adaptive boost trip levels (effective only when boost_intel is set to 1), */\ + { 0xa54, "boost_hold_time"}, /* Hold time for DCDC booster (effective only when boost_intel is set to 1), */\ + { 0xaa1, "bst_slpcmplvl"}, /* Slope compensation current, represents LxF (inductance x frequency) value , */\ + { 0xb07, "mtpkey2"}, /* KEY2 to access key2 protected registers (default for engineering), */\ + { 0xc00, "enbl_volt_sense"}, /* Voltage sense enabling control bit , */\ + { 0xc10, "vsense_pwm_sel"}, /* Voltage sense source selection , */\ + { 0xc25, "vi_frac_delay"}, /* Fractional delay adjustment between current and voltage sense, */\ + { 0xc80, "sel_voltsense_out"}, /* TDM output data selection for AEC , */\ + { 0xc90, "vsense_bypass_avg"}, /* Voltage sense average block bypass , */\ + { 0xd05, "cf_frac_delay"}, /* Fractional delay adjustment between current and voltage sense by firmware, */\ + { 0xe00, "bypass_dcdc_curr_prot"}, /* Control to switch off dcdc current reduction with bat protection, */\ + { 0xe10, "bypass_ocp"}, /* Bypass OCP (digital IP block) , */\ + { 0xe20, "ocptest"}, /* ocptest (analog IP block) enable , */\ + { 0xe80, "disable_clock_sh_prot"}, /* Disable clock_sh protection , */\ + { 0xe92, "reserve_reg_15_09"}, /* Spare control bits for future usage , */\ + { 0xec0, "unprotect_mtp"}, /* Enable programming of the MTP memory , */\ + { 0xed2, "reserve_reg_15_13"}, /* Spare control bits for future usage , */\ + { 0xf00, "dcdc_pfm20khz_limit"}, /* DCDC in PFM mode forcing each 50us a pwm pulse , */\ + { 0xf11, "dcdc_ctrl_maxzercnt"}, /* DCDC number of zero current flags required to go to pfm mode, */\ + { 0xf36, "dcdc_vbat_delta_detect"}, /* DCDC threshold required on a delta Vbat (in PFM mode) switching to PWM mode, */\ + { 0xfa0, "dcdc_ignore_vbat"}, /* Ignore an increase on Vbat , */\ + { 0x1011, "tdm_usecase"}, /* TDM usecase selection control , */\ + { 0x1030, "tdm_enable"}, /* TDM interface enable , */\ + { 0x1040, "tdm_clk_inversion"}, /* TDM clock inversion, receive on , */\ + { 0x1053, "tdm_fs_ws_length"}, /* TDM FS length , */\ + { 0x1090, "tdm_fs_ws_polarity"}, /* TDM FS polarity (start frame) , */\ + { 0x10a4, "tdm_sample_size"}, /* TDM sample size for all TDM sinks and sources , */\ + { 0x1103, "tdm_nb_of_slots"}, /* TDM number of slots , */\ + { 0x1144, "tdm_slot_length"}, /* TDM slot length , */\ + { 0x1194, "tdm_bits_remaining"}, /* TDM bits remaining after the last slot , */\ + { 0x11e0, "tdm_data_delay"}, /* TDM data delay , */\ + { 0x11f0, "tdm_data_adjustment"}, /* TDM data adjustment , */\ + { 0x1201, "tdm_txdata_format"}, /* TDM TXDATA format , */\ + { 0x1221, "tdm_txdata_format_unused_slot_sd0"}, /* TDM TXDATA format unused slot SD0 , */\ + { 0x1241, "tdm_txdata_format_unused_slot_sd1"}, /* TDM TXDATA format unused slot SD1 , */\ + { 0x1270, "tdm_sink0_enable"}, /* TDM sink0 enable , */\ + { 0x1280, "tdm_sink1_enable"}, /* TDM sink1 enable , */\ + { 0x1290, "tdm_sink2_enable"}, /* TDM sink2 enable , */\ + { 0x12a0, "tdm_source0_enable"}, /* TDM source0 enable , */\ + { 0x12b0, "tdm_source1_enable"}, /* TDM source1 enable , */\ + { 0x12c0, "tdm_source2_enable"}, /* TDM source2 enable , */\ + { 0x12d0, "tdm_sink0_io"}, /* TDM sink0 IO selection , */\ + { 0x12e0, "tdm_sink1_io"}, /* TDM sink1 IO selection , */\ + { 0x12f0, "tdm_sink2_io"}, /* TDM sink2 IO selection , */\ + { 0x1300, "tdm_source0_io"}, /* TDM source0 IO selection , */\ + { 0x1310, "tdm_source1_io"}, /* TDM source1 IO selection , */\ + { 0x1320, "tdm_source2_io"}, /* TDM source2 IO selection , */\ + { 0x1333, "tdm_sink0_slot"}, /* TDM sink0 slot position [GAIN IN] , */\ + { 0x1373, "tdm_sink1_slot"}, /* TDM sink1 slot position [CH1 IN] , */\ + { 0x13b3, "tdm_sink2_slot"}, /* TDM sink2 slot position [CH2 IN] , */\ + { 0x1403, "tdm_source0_slot"}, /* TDM source0 slot position [GAIN OUT] , */\ + { 0x1443, "tdm_source1_slot"}, /* TDM source1 slot position [Voltage Sense] , */\ + { 0x1483, "tdm_source2_slot"}, /* TDM source2 slot position [Current Sense] , */\ + { 0x14c3, "tdm_nbck"}, /* TDM NBCK bit clock ratio , */\ + { 0x1500, "flag_tdm_lut_error"}, /* TDM LUT error flag , */\ + { 0x1512, "flag_tdm_status"}, /* TDM interface status bits , */\ + { 0x1540, "flag_tdm_error"}, /* TDM interface error indicator , */\ + { 0x1551, "status_bst_mode"}, /* DCDC mode status bits , */\ + { 0x2000, "flag_por_int_out"}, /* flag_por_int_out , */\ + { 0x2010, "flag_pll_lock_int_out"}, /* flag_pll_lock_int_out , */\ + { 0x2020, "flag_otpok_int_out"}, /* flag_otpok_int_out , */\ + { 0x2030, "flag_ovpok_int_out"}, /* flag_ovpok_int_out , */\ + { 0x2040, "flag_uvpok_int_out"}, /* flag_uvpok_int_out , */\ + { 0x2050, "flag_ocp_alarm_int_out"}, /* flag_ocp_alarm_int_out , */\ + { 0x2060, "flag_clocks_stable_int_out"}, /* flag_clocks_stable_int_out , */\ + { 0x2070, "flag_clip_int_out"}, /* flag_clip_int_out , */\ + { 0x2080, "mtp_busy_int_out"}, /* mtp_busy_int_out , */\ + { 0x2090, "flag_lost_clk_int_out"}, /* flag_lost_clk_int_out , */\ + { 0x20a0, "flag_cf_speakererror_int_out"}, /* flag_cf_speakererror_int_out , */\ + { 0x20b0, "flag_cold_started_int_out"}, /* flag_cold_started_int_out , */\ + { 0x20c0, "flag_engage_int_out"}, /* flag_engage_int_out , */\ + { 0x20d0, "flag_watchdog_reset_int_out"}, /* flag_watchdog_reset_int_out , */\ + { 0x20e0, "flag_enbl_amp_int_out"}, /* flag_enbl_amp_int_out , */\ + { 0x20f0, "flag_enbl_ref_int_out"}, /* flag_enbl_ref_int_out , */\ + { 0x2100, "flag_voutcomp_int_out"}, /* flag_voutcomp_int_out , */\ + { 0x2110, "flag_voutcomp93_int_out"}, /* flag_voutcomp93_int_out , */\ + { 0x2120, "flag_voutcomp86_int_out"}, /* flag_voutcomp86_int_out , */\ + { 0x2130, "flag_hiz_int_out"}, /* flag_hiz_int_out , */\ + { 0x2140, "flag_ocpokbst_int_out"}, /* flag_ocpokbst_int_out , */\ + { 0x2150, "flag_peakcur_int_out"}, /* flag_peakcur_int_out , */\ + { 0x2160, "flag_ocpokap_int_out"}, /* flag_ocpokap_int_out , */\ + { 0x2170, "flag_ocpokan_int_out"}, /* flag_ocpokan_int_out , */\ + { 0x2180, "flag_ocpokbp_int_out"}, /* flag_ocpokbp_int_out , */\ + { 0x2190, "flag_ocpokbn_int_out"}, /* flag_ocpokbn_int_out , */\ + { 0x21a0, "flag_adc10_ready_int_out"}, /* flag_adc10_ready_int_out , */\ + { 0x21b0, "flag_clipa_high_int_out"}, /* flag_clipa_high_int_out , */\ + { 0x21c0, "flag_clipa_low_int_out"}, /* flag_clipa_low_int_out , */\ + { 0x21d0, "flag_clipb_high_int_out"}, /* flag_clipb_high_int_out , */\ + { 0x21e0, "flag_clipb_low_int_out"}, /* flag_clipb_low_int_out , */\ + { 0x21f0, "flag_tdm_error_int_out"}, /* flag_tdm_error_int_out , */\ + { 0x2200, "flag_cfma_err_int_out"}, /* flag_cfma_err_int_out , */\ + { 0x2210, "flag_cfma_ack_int_out"}, /* flag_cfma_ack_int_out , */\ + { 0x2300, "flag_por_int_in"}, /* flag_por_int_in , */\ + { 0x2310, "flag_pll_lock_int_in"}, /* flag_pll_lock_int_in , */\ + { 0x2320, "flag_otpok_int_in"}, /* flag_otpok_int_in , */\ + { 0x2330, "flag_ovpok_int_in"}, /* flag_ovpok_int_in , */\ + { 0x2340, "flag_uvpok_int_in"}, /* flag_uvpok_int_in , */\ + { 0x2350, "flag_ocp_alarm_int_in"}, /* flag_ocp_alarm_int_in , */\ + { 0x2360, "flag_clocks_stable_int_in"}, /* flag_clocks_stable_int_in , */\ + { 0x2370, "flag_clip_int_in"}, /* flag_clip_int_in , */\ + { 0x2380, "mtp_busy_int_in"}, /* mtp_busy_int_in , */\ + { 0x2390, "flag_lost_clk_int_in"}, /* flag_lost_clk_int_in , */\ + { 0x23a0, "flag_cf_speakererror_int_in"}, /* flag_cf_speakererror_int_in , */\ + { 0x23b0, "flag_cold_started_int_in"}, /* flag_cold_started_int_in , */\ + { 0x23c0, "flag_engage_int_in"}, /* flag_engage_int_in , */\ + { 0x23d0, "flag_watchdog_reset_int_in"}, /* flag_watchdog_reset_int_in , */\ + { 0x23e0, "flag_enbl_amp_int_in"}, /* flag_enbl_amp_int_in , */\ + { 0x23f0, "flag_enbl_ref_int_in"}, /* flag_enbl_ref_int_in , */\ + { 0x2400, "flag_voutcomp_int_in"}, /* flag_voutcomp_int_in , */\ + { 0x2410, "flag_voutcomp93_int_in"}, /* flag_voutcomp93_int_in , */\ + { 0x2420, "flag_voutcomp86_int_in"}, /* flag_voutcomp86_int_in , */\ + { 0x2430, "flag_hiz_int_in"}, /* flag_hiz_int_in , */\ + { 0x2440, "flag_ocpokbst_int_in"}, /* flag_ocpokbst_int_in , */\ + { 0x2450, "flag_peakcur_int_in"}, /* flag_peakcur_int_in , */\ + { 0x2460, "flag_ocpokap_int_in"}, /* flag_ocpokap_int_in , */\ + { 0x2470, "flag_ocpokan_int_in"}, /* flag_ocpokan_int_in , */\ + { 0x2480, "flag_ocpokbp_int_in"}, /* flag_ocpokbp_int_in , */\ + { 0x2490, "flag_ocpokbn_int_in"}, /* flag_ocpokbn_int_in , */\ + { 0x24a0, "flag_adc10_ready_int_in"}, /* flag_adc10_ready_int_in , */\ + { 0x24b0, "flag_clipa_high_int_in"}, /* flag_clipa_high_int_in , */\ + { 0x24c0, "flag_clipa_low_int_in"}, /* flag_clipa_low_int_in , */\ + { 0x24d0, "flag_clipb_high_int_in"}, /* flag_clipb_high_int_in , */\ + { 0x24e0, "flag_clipb_low_int_in"}, /* flag_clipb_low_int_in , */\ + { 0x24f0, "flag_tdm_error_int_in"}, /* flag_tdm_error_int_in , */\ + { 0x2500, "flag_cfma_err_int_in"}, /* flag_cfma_err_int_in , */\ + { 0x2510, "flag_cfma_ack_int_in"}, /* flag_cfma_ack_int_in , */\ + { 0x2600, "flag_por_int_enable"}, /* flag_por_int_enable , */\ + { 0x2610, "flag_pll_lock_int_enable"}, /* flag_pll_lock_int_enable , */\ + { 0x2620, "flag_otpok_int_enable"}, /* flag_otpok_int_enable , */\ + { 0x2630, "flag_ovpok_int_enable"}, /* flag_ovpok_int_enable , */\ + { 0x2640, "flag_uvpok_int_enable"}, /* flag_uvpok_int_enable , */\ + { 0x2650, "flag_ocp_alarm_int_enable"}, /* flag_ocp_alarm_int_enable , */\ + { 0x2660, "flag_clocks_stable_int_enable"}, /* flag_clocks_stable_int_enable , */\ + { 0x2670, "flag_clip_int_enable"}, /* flag_clip_int_enable , */\ + { 0x2680, "mtp_busy_int_enable"}, /* mtp_busy_int_enable , */\ + { 0x2690, "flag_lost_clk_int_enable"}, /* flag_lost_clk_int_enable , */\ + { 0x26a0, "flag_cf_speakererror_int_enable"}, /* flag_cf_speakererror_int_enable , */\ + { 0x26b0, "flag_cold_started_int_enable"}, /* flag_cold_started_int_enable , */\ + { 0x26c0, "flag_engage_int_enable"}, /* flag_engage_int_enable , */\ + { 0x26d0, "flag_watchdog_reset_int_enable"}, /* flag_watchdog_reset_int_enable , */\ + { 0x26e0, "flag_enbl_amp_int_enable"}, /* flag_enbl_amp_int_enable , */\ + { 0x26f0, "flag_enbl_ref_int_enable"}, /* flag_enbl_ref_int_enable , */\ + { 0x2700, "flag_voutcomp_int_enable"}, /* flag_voutcomp_int_enable , */\ + { 0x2710, "flag_voutcomp93_int_enable"}, /* flag_voutcomp93_int_enable , */\ + { 0x2720, "flag_voutcomp86_int_enable"}, /* flag_voutcomp86_int_enable , */\ + { 0x2730, "flag_hiz_int_enable"}, /* flag_hiz_int_enable , */\ + { 0x2740, "flag_ocpokbst_int_enable"}, /* flag_ocpokbst_int_enable , */\ + { 0x2750, "flag_peakcur_int_enable"}, /* flag_peakcur_int_enable , */\ + { 0x2760, "flag_ocpokap_int_enable"}, /* flag_ocpokap_int_enable , */\ + { 0x2770, "flag_ocpokan_int_enable"}, /* flag_ocpokan_int_enable , */\ + { 0x2780, "flag_ocpokbp_int_enable"}, /* flag_ocpokbp_int_enable , */\ + { 0x2790, "flag_ocpokbn_int_enable"}, /* flag_ocpokbn_int_enable , */\ + { 0x27a0, "flag_adc10_ready_int_enable"}, /* flag_adc10_ready_int_enable , */\ + { 0x27b0, "flag_clipa_high_int_enable"}, /* flag_clipa_high_int_enable , */\ + { 0x27c0, "flag_clipa_low_int_enable"}, /* flag_clipa_low_int_enable , */\ + { 0x27d0, "flag_clipb_high_int_enable"}, /* flag_clipb_high_int_enable , */\ + { 0x27e0, "flag_clipb_low_int_enable"}, /* flag_clipb_low_int_enable , */\ + { 0x27f0, "flag_tdm_error_int_enable"}, /* flag_tdm_error_int_enable , */\ + { 0x2800, "flag_cfma_err_int_enable"}, /* flag_cfma_err_int_enable , */\ + { 0x2810, "flag_cfma_ack_int_enable"}, /* flag_cfma_ack_int_enable , */\ + { 0x2900, "flag_por_int_pol"}, /* flag_por_int_pol , */\ + { 0x2910, "flag_pll_lock_int_pol"}, /* flag_pll_lock_int_pol , */\ + { 0x2920, "flag_otpok_int_pol"}, /* flag_otpok_int_pol , */\ + { 0x2930, "flag_ovpok_int_pol"}, /* flag_ovpok_int_pol , */\ + { 0x2940, "flag_uvpok_int_pol"}, /* flag_uvpok_int_pol , */\ + { 0x2950, "flag_ocp_alarm_int_pol"}, /* flag_ocp_alarm_int_pol , */\ + { 0x2960, "flag_clocks_stable_int_pol"}, /* flag_clocks_stable_int_pol , */\ + { 0x2970, "flag_clip_int_pol"}, /* flag_clip_int_pol , */\ + { 0x2980, "mtp_busy_int_pol"}, /* mtp_busy_int_pol , */\ + { 0x2990, "flag_lost_clk_int_pol"}, /* flag_lost_clk_int_pol , */\ + { 0x29a0, "flag_cf_speakererror_int_pol"}, /* flag_cf_speakererror_int_pol , */\ + { 0x29b0, "flag_cold_started_int_pol"}, /* flag_cold_started_int_pol , */\ + { 0x29c0, "flag_engage_int_pol"}, /* flag_engage_int_pol , */\ + { 0x29d0, "flag_watchdog_reset_int_pol"}, /* flag_watchdog_reset_int_pol , */\ + { 0x29e0, "flag_enbl_amp_int_pol"}, /* flag_enbl_amp_int_pol , */\ + { 0x29f0, "flag_enbl_ref_int_pol"}, /* flag_enbl_ref_int_pol , */\ + { 0x2a00, "flag_voutcomp_int_pol"}, /* flag_voutcomp_int_pol , */\ + { 0x2a10, "flag_voutcomp93_int_pol"}, /* flag_voutcomp93_int_pol , */\ + { 0x2a20, "flag_voutcomp86_int_pol"}, /* flag_voutcomp86_int_pol , */\ + { 0x2a30, "flag_hiz_int_pol"}, /* flag_hiz_int_pol , */\ + { 0x2a40, "flag_ocpokbst_int_pol"}, /* flag_ocpokbst_int_pol , */\ + { 0x2a50, "flag_peakcur_int_pol"}, /* flag_peakcur_int_pol , */\ + { 0x2a60, "flag_ocpokap_int_pol"}, /* flag_ocpokap_int_pol , */\ + { 0x2a70, "flag_ocpokan_int_pol"}, /* flag_ocpokan_int_pol , */\ + { 0x2a80, "flag_ocpokbp_int_pol"}, /* flag_ocpokbp_int_pol , */\ + { 0x2a90, "flag_ocpokbn_int_pol"}, /* flag_ocpokbn_int_pol , */\ + { 0x2aa0, "flag_adc10_ready_int_pol"}, /* flag_adc10_ready_int_pol , */\ + { 0x2ab0, "flag_clipa_high_int_pol"}, /* flag_clipa_high_int_pol , */\ + { 0x2ac0, "flag_clipa_low_int_pol"}, /* flag_clipa_low_int_pol , */\ + { 0x2ad0, "flag_clipb_high_int_pol"}, /* flag_clipb_high_int_pol , */\ + { 0x2ae0, "flag_clipb_low_int_pol"}, /* flag_clipb_low_int_pol , */\ + { 0x2af0, "flag_tdm_error_int_pol"}, /* flag_tdm_error_int_pol , */\ + { 0x2b00, "flag_cfma_err_int_pol"}, /* flag_cfma_err_int_pol , */\ + { 0x2b10, "flag_cfma_ack_int_pol"}, /* flag_cfma_ack_int_pol , */\ + { 0x3000, "flag_voutcomp"}, /* Status flag_voutcomp, indication Vset is larger than Vbat, */\ + { 0x3010, "flag_voutcomp93"}, /* Status flag_voutcomp93, indication Vset is larger than 1.07 x Vbat, */\ + { 0x3020, "flag_voutcomp86"}, /* Status flag voutcomp86, indication Vset is larger than 1.14 x Vbat, */\ + { 0x3030, "flag_hiz"}, /* Status flag_hiz, indication Vbst is larger than Vbat, */\ + { 0x3040, "flag_ocpokbst"}, /* Status flag_ocpokbst, indication no over current in boost converter PMOS switch, */\ + { 0x3050, "flag_peakcur"}, /* Status flag_peakcur, indication current is max in dcdc converter, */\ + { 0x3060, "flag_ocpokap"}, /* Status flag_ocpokap, indication no over current in amplifier A PMOS output stage, */\ + { 0x3070, "flag_ocpokan"}, /* Status flag_ocpokan, indication no over current in amplifier A NMOS output stage, */\ + { 0x3080, "flag_ocpokbp"}, /* Status flag_ocpokbp, indication no over current in amplifier B PMOS output stage, */\ + { 0x3090, "flag_ocpokbn"}, /* Status flag_ocpokbn, indication no over current in amplifier B NMOS output stage, */\ + { 0x30a0, "flag_adc10_ready"}, /* Status flag_adc10_ready, indication adc10 is ready, */\ + { 0x30b0, "flag_clipa_high"}, /* Status flag_clipa_high, indication pmos amplifier A is clipping, */\ + { 0x30c0, "flag_clipa_low"}, /* Status flag_clipa_low, indication nmos amplifier A is clipping, */\ + { 0x30d0, "flag_clipb_high"}, /* Status flag_clipb_high, indication pmos amplifier B is clipping, */\ + { 0x30e0, "flag_clipb_low"}, /* Status flag_clipb_low, indication nmos amplifier B is clipping, */\ + { 0x310f, "mtp_man_data_out"}, /* MTP manual read out data , */\ + { 0x3200, "key01_locked"}, /* Indicates KEY1 is locked , */\ + { 0x3210, "key02_locked"}, /* Indicates KEY2 is locked , */\ + { 0x3225, "mtp_ecc_tcout"}, /* MTP error correction test data out , */\ + { 0x3280, "mtpctrl_valid_test_rd"}, /* MTP test readout for read , */\ + { 0x3290, "mtpctrl_valid_test_wr"}, /* MTP test readout for write , */\ + { 0x32a0, "flag_in_alarm_state"}, /* Flag alarm state , */\ + { 0x32b0, "mtp_ecc_err2"}, /* Two or more bit errors detected in MTP, can not reconstruct value, */\ + { 0x32c0, "mtp_ecc_err1"}, /* One bit error detected in MTP, reconstructed value, */\ + { 0x32d0, "mtp_mtp_hvf"}, /* High voltage ready flag for MTP , */\ + { 0x32f0, "mtp_zero_check_fail"}, /* Zero check failed for MTP , */\ + { 0x3309, "data_adc10_tempbat"}, /* ADC10 data output for testing battery voltage and temperature, */\ + { 0x400f, "hid_code"}, /* 5A6Bh, 23147d to access hidden registers (default for engineering), */\ + { 0x4100, "bypass_hp"}, /* Bypass High Pass Filter , */\ + { 0x4110, "hard_mute"}, /* Hard Mute , */\ + { 0x4120, "soft_mute"}, /* Soft Mute , */\ + { 0x4134, "pwm_delay"}, /* PWM delay setting , */\ + { 0x4180, "pwm_shape"}, /* PWM Shape , */\ + { 0x4190, "pwm_bitlength"}, /* PWM Bitlength in noise shaper , */\ + { 0x4203, "drive"}, /* Drive bits to select number of amplifier power stages, */\ + { 0x4240, "reclock_pwm"}, /* Control for enabling reclocking of PWM signal , */\ + { 0x4250, "reclock_voltsense"}, /* Control for enabling reclocking of voltage sense signal, */\ + { 0x4281, "dpsalevel"}, /* DPSA threshold level , */\ + { 0x42a1, "dpsa_release"}, /* DPSA release time , */\ + { 0x42c0, "coincidence"}, /* Prevent simultaneously switching of output stage , */\ + { 0x42d0, "kickback"}, /* Prevent double pulses of output stage , */\ + { 0x4306, "drivebst"}, /* Drive bits to select the power transistor sections boost converter, */\ + { 0x4370, "boost_alg"}, /* Control for boost adaptive loop gain , */\ + { 0x4381, "boost_loopgain"}, /* DCDC boost loopgain setting , */\ + { 0x43a0, "ocptestbst"}, /* Boost OCP. For old ocp (ctrl_reversebst is 0); For new ocp (ctrl_reversebst is 1), */\ + { 0x43d0, "test_abistfft_enbl"}, /* FFT Coolflux , */\ + { 0x43e0, "bst_dcmbst"}, /* DCM mode control for DCDC during I2C direct control mode, */\ + { 0x43f0, "test_bcontrol"}, /* test_bcontrol , */\ + { 0x4400, "reversebst"}, /* OverCurrent Protection selection of power stage boost converter, */\ + { 0x4410, "sensetest"}, /* Test option for the sense NMOS in booster for current mode control., */\ + { 0x4420, "enbl_engagebst"}, /* Enable power stage of dcdc controller , */\ + { 0x4470, "enbl_slopecur"}, /* Enable bit of max-current dac , */\ + { 0x4480, "enbl_voutcomp"}, /* Enable vout comparators , */\ + { 0x4490, "enbl_voutcomp93"}, /* Enable vout-93 comparators , */\ + { 0x44a0, "enbl_voutcomp86"}, /* Enable vout-86 comparators , */\ + { 0x44b0, "enbl_hizcom"}, /* Enable hiz comparator , */\ + { 0x44c0, "enbl_peakcur"}, /* Enable peak current , */\ + { 0x44d0, "bypass_ovpglitch"}, /* Bypass OVP Glitch Filter , */\ + { 0x44e0, "enbl_windac"}, /* Enable window dac , */\ + { 0x44f0, "enbl_powerbst"}, /* Enable line of the powerstage , */\ + { 0x4507, "ocp_thr"}, /* OCP threshold level , */\ + { 0x4580, "bypass_glitchfilter"}, /* Bypass glitch filter , */\ + { 0x4590, "bypass_ovp"}, /* Bypass OVP , */\ + { 0x45a0, "bypass_uvp"}, /* Bypass UVP , */\ + { 0x45b0, "bypass_otp"}, /* Bypass OTP , */\ + { 0x45d0, "bypass_ocpcounter"}, /* Bypass OCP counter , */\ + { 0x45e0, "bypass_lost_clk"}, /* Bypass lost clock detector , */\ + { 0x45f0, "vpalarm"}, /* vpalarm (UVP/OUP handling) , */\ + { 0x4600, "bypass_gc"}, /* Bypasses the CS gain correction , */\ + { 0x4610, "cs_gain_control"}, /* Current sense gain control , */\ + { 0x4627, "cs_gain"}, /* Current sense gain , */\ + { 0x46a0, "bypass_lp"}, /* Bypass the low power filter inside temperature sensor, */\ + { 0x46b0, "bypass_pwmcounter"}, /* Bypass PWM Counter , */\ + { 0x46c0, "cs_negfixed"}, /* Current sense does not switch to neg , */\ + { 0x46d2, "cs_neghyst"}, /* Current sense switches to neg depending on hyseteris level, */\ + { 0x4700, "switch_fb"}, /* Current sense control switch_fb , */\ + { 0x4713, "se_hyst"}, /* Current sense control se_hyst , */\ + { 0x4754, "se_level"}, /* Current sense control se_level , */\ + { 0x47a5, "ktemp"}, /* Current sense control temperature compensation trimming, */\ + { 0x4800, "cs_negin"}, /* Current sense control negin , */\ + { 0x4810, "cs_sein"}, /* Current sense control cs_sein , */\ + { 0x4820, "cs_coincidence"}, /* Coincidence current sense , */\ + { 0x4830, "iddqtestbst"}, /* IDDQ testing in powerstage of DCDC boost converter, */\ + { 0x4840, "coincidencebst"}, /* Switch protection on to prevent simultaneously switching power stages bst and amp, */\ + { 0x4876, "delay_se_neg"}, /* delay of se and neg , */\ + { 0x48e1, "cs_ttrack"}, /* Sample and hold track time , */\ + { 0x4900, "bypass_clip"}, /* Bypass clip control , */\ + { 0x4920, "cf_cgate_off"}, /* Disable clock gating in the coolflux , */\ + { 0x4940, "clipfast"}, /* Clock selection for HW clipper for battery safeguard, */\ + { 0x4950, "cs_8ohm"}, /* 8 ohm mode for current sense (gain mode) , */\ + { 0x4974, "delay_clock_sh"}, /* delay_sh, tunes S7H delay , */\ + { 0x49c0, "inv_clksh"}, /* Invert the sample/hold clock for current sense ADC, */\ + { 0x49d0, "inv_neg"}, /* Invert neg signal , */\ + { 0x49e0, "inv_se"}, /* Invert se signal , */\ + { 0x49f0, "setse"}, /* Switches between Single Ended and differential mode; 1 is single ended, */\ + { 0x4a12, "adc10_sel"}, /* Select the input to convert the 10b ADC , */\ + { 0x4a60, "adc10_reset"}, /* Reset for ADC10 - I2C direct control mode , */\ + { 0x4a81, "adc10_test"}, /* Test mode selection signal for ADC10 - I2C direct control mode, */\ + { 0x4aa0, "bypass_lp_vbat"}, /* LP filter in batt sensor , */\ + { 0x4ae0, "dc_offset"}, /* Current sense decimator offset control , */\ + { 0x4af0, "tsense_hibias"}, /* Bit to set the biasing in temp sensor to high , */\ + { 0x4b00, "adc13_iset"}, /* MICADC setting of current consumption (debug use only), */\ + { 0x4b14, "adc13_gain"}, /* MICADC gain setting (two's complement format) , */\ + { 0x4b61, "adc13_slowdel"}, /* MICADC delay setting for internal clock (debug use only), */\ + { 0x4b83, "adc13_offset"}, /* MICADC offset setting , */\ + { 0x4bc0, "adc13_bsoinv"}, /* MICADC bit stream output invert mode for test , */\ + { 0x4bd0, "adc13_resonator_enable"}, /* MICADC give extra SNR with less stability (debug use only), */\ + { 0x4be0, "testmicadc"}, /* Mux at input of MICADC for test purpose , */\ + { 0x4c0f, "abist_offset"}, /* Offset control for ABIST testing , */\ + { 0x4d05, "windac"}, /* For testing direct control windac , */\ + { 0x4dc3, "pwm_dcc_cnt"}, /* control pwm duty cycle when enbl_pwm_dcc is 1 , */\ + { 0x4e04, "slopecur"}, /* For testing direct control slopecur , */\ + { 0x4e50, "ctrl_dem"}, /* Dynamic element matching control, rest of codes are optional, */\ + { 0x4ed0, "enbl_pwm_dcc"}, /* Enable direct control of pwm duty cycle , */\ + { 0x4f00, "bst_bypass_bstcur"}, /* Bypass control for boost current settings , */\ + { 0x4f10, "bst_bypass_bstfoldback"}, /* Bypass control for boost foldback , */\ + { 0x4f20, "bst_ctrl_azbst"}, /* Control of auto-zeroing of zero current comparator, */\ + { 0x5007, "gain"}, /* Gain setting of the gain multiplier , */\ + { 0x5081, "sourceb"}, /* PWM OUTB selection control , */\ + { 0x50a1, "sourcea"}, /* PWM OUTA selection control , */\ + { 0x50c1, "sourcebst"}, /* Sets the source of the pwmbst output to boost converter input for testing, */\ + { 0x50e0, "tdm_enable_loopback"}, /* TDM loopback test , */\ + { 0x5104, "pulselengthbst"}, /* Pulse length setting test input for boost converter, */\ + { 0x5150, "bypasslatchbst"}, /* Bypass latch in boost converter , */\ + { 0x5160, "invertbst"}, /* Invert pwmbst test signal , */\ + { 0x5174, "pulselength"}, /* Pulse length setting test input for amplifier , */\ + { 0x51c0, "bypasslatch"}, /* Bypass latch in PWM source selection module , */\ + { 0x51d0, "invertb"}, /* invert pwmb test signal , */\ + { 0x51e0, "inverta"}, /* invert pwma test signal , */\ + { 0x51f0, "bypass_ctrlloop"}, /* bypass_ctrlloop bypasses the control loop of the amplifier, */\ + { 0x5210, "test_rdsona"}, /* tbd for rdson testing , */\ + { 0x5220, "test_rdsonb"}, /* tbd for rdson testing , */\ + { 0x5230, "test_rdsonbst"}, /* tbd for rdson testing , */\ + { 0x5240, "test_cvia"}, /* tbd for rdson testing , */\ + { 0x5250, "test_cvib"}, /* tbd for rdson testing , */\ + { 0x5260, "test_cvibst"}, /* tbd for rdson testing , */\ + { 0x5306, "digimuxa_sel"}, /* DigimuxA input selection control (see Digimux list for details), */\ + { 0x5376, "digimuxb_sel"}, /* DigimuxB input selection control (see Digimux list for details), */\ + { 0x5400, "hs_mode"}, /* I2C high speed mode selection control , */\ + { 0x5412, "test_parametric_io"}, /* Control for parametric tests of IO cells , */\ + { 0x5440, "enbl_ringo"}, /* Enable ring oscillator control, for test purpose to check with ringo, */\ + { 0x5456, "digimuxc_sel"}, /* DigimuxC input selection control (see Digimux list for details), */\ + { 0x54c0, "dio_ehs"}, /* Slew control for DIO in output mode , */\ + { 0x54d0, "gainio_ehs"}, /* Slew control for GAINIO in output mode , */\ + { 0x550d, "enbl_amp"}, /* enbl_amp for testing to enable all analoge blocks in amplifier, */\ + { 0x5600, "use_direct_ctrls"}, /* Use direct controls to overrule several functions for testing - I2C direct control mode, */\ + { 0x5610, "rst_datapath"}, /* Reset datapath during direct control mode , */\ + { 0x5620, "rst_cgu"}, /* Reset CGU during durect control mode , */\ + { 0x5637, "enbl_ref"}, /* For testing to enable all analoge blocks in references, */\ + { 0x56b0, "enbl_engage"}, /* Enable output stage amplifier , */\ + { 0x56c0, "use_direct_clk_ctrl"}, /* use_direct_clk_ctrl, to overrule several functions direct for testing, */\ + { 0x56d0, "use_direct_pll_ctrl"}, /* use_direct_pll_ctrl, to overrule several functions direct for testing, */\ + { 0x5707, "anamux"}, /* Anamux control , */\ + { 0x57e0, "otptest"}, /* otptest, test mode otp amplifier , */\ + { 0x57f0, "reverse"}, /* 1b = Normal mode, slope is controlled , */\ + { 0x5813, "pll_selr"}, /* PLL pll_selr , */\ + { 0x5854, "pll_selp"}, /* PLL pll_selp , */\ + { 0x58a5, "pll_seli"}, /* PLL pll_seli , */\ + { 0x5950, "pll_mdec_msb"}, /* Most significant bits of pll_mdec[16] , */\ + { 0x5960, "pll_ndec_msb"}, /* Most significant bits of pll_ndec[9] , */\ + { 0x5970, "pll_frm"}, /* PLL pll_frm , */\ + { 0x5980, "pll_directi"}, /* PLL pll_directi , */\ + { 0x5990, "pll_directo"}, /* PLL pll_directo , */\ + { 0x59a0, "enbl_pll"}, /* PLL enbl_pll , */\ + { 0x59f0, "pll_bypass"}, /* PLL bypass , */\ + { 0x5a0f, "tsig_freq"}, /* Internal sinus test generator frequency control LSB bits, */\ + { 0x5b02, "tsig_freq_msb"}, /* Select internal sine wave generator, frequency control MSB bits, */\ + { 0x5b30, "inject_tsig"}, /* Control bit to switch to internal sinus test generator, */\ + { 0x5b44, "adc10_prog_sample"}, /* ADC10 program sample setting - I2C direct control mode, */\ + { 0x5c0f, "pll_mdec"}, /* PLL MDEC - I2C direct PLL control mode only , */\ + { 0x5d06, "pll_pdec"}, /* PLL PDEC - I2C direct PLL control mode only , */\ + { 0x5d78, "pll_ndec"}, /* PLL NDEC - I2C direct PLL control mode only , */\ + { 0x6007, "mtpkey1"}, /* 5Ah, 90d To access KEY1_Protected registers (Default for engineering), */\ + { 0x6185, "mtp_ecc_tcin"}, /* MTP ECC TCIN data , */\ + { 0x6203, "mtp_man_address_in"}, /* MTP address from I2C register for read/writing mtp in manual single word mode, */\ + { 0x6260, "mtp_ecc_eeb"}, /* Enable code bit generation (active low!) , */\ + { 0x6270, "mtp_ecc_ecb"}, /* Enable correction signal (active low!) , */\ + { 0x6280, "man_copy_mtp_to_iic"}, /* Start copying single word from mtp to I2C mtp register, */\ + { 0x6290, "man_copy_iic_to_mtp"}, /* Start copying single word from I2C mtp register to mtp, */\ + { 0x62a0, "auto_copy_mtp_to_iic"}, /* Start copying all the data from mtp to I2C mtp registers, */\ + { 0x62b0, "auto_copy_iic_to_mtp"}, /* Start copying data from I2C mtp registers to mtp , */\ + { 0x62d2, "mtp_speed_mode"}, /* MTP speed mode , */\ + { 0x6340, "mtp_direct_enable"}, /* mtp_direct_enable , */\ + { 0x6350, "mtp_direct_wr"}, /* mtp_direct_wr , */\ + { 0x6360, "mtp_direct_rd"}, /* mtp_direct_rd , */\ + { 0x6370, "mtp_direct_rst"}, /* mtp_direct_rst , */\ + { 0x6380, "mtp_direct_ers"}, /* mtp_direct_ers , */\ + { 0x6390, "mtp_direct_prg"}, /* mtp_direct_prg , */\ + { 0x63a0, "mtp_direct_epp"}, /* mtp_direct_epp , */\ + { 0x63b4, "mtp_direct_test"}, /* mtp_direct_test , */\ + { 0x640f, "mtp_man_data_in"}, /* Write data for MTP manual write , */\ + { 0x7000, "cf_rst_dsp"}, /* Reset CoolFlux DSP , */\ + { 0x7011, "cf_dmem"}, /* Target memory for access , */\ + { 0x7030, "cf_aif"}, /* Auto increment flag for memory-address , */\ + { 0x7040, "cf_int"}, /* CF Interrupt - auto clear , */\ + { 0x7087, "cf_req"}, /* CF request for accessing the 8 channels , */\ + { 0x710f, "cf_madd"}, /* Memory address , */\ + { 0x720f, "cf_mema"}, /* Activate memory access , */\ + { 0x7307, "cf_err"}, /* CF error flags , */\ + { 0x7387, "cf_ack"}, /* CF acknowledgement of the requests channels , */\ + { 0x8000, "calibration_onetime"}, /* Calibration schedule selection , */\ + { 0x8010, "calibr_ron_done"}, /* Calibration of RON status bit , */\ + { 0x8105, "calibr_vout_offset"}, /* calibr_vout_offset (DCDCoffset) 2's compliment (key1 protected), */\ + { 0x8163, "calibr_delta_gain"}, /* delta gain for vamp (alpha) 2's compliment (key1 protected), */\ + { 0x81a5, "calibr_offs_amp"}, /* offset for vamp (Ampoffset) 2's compliment (key1 protected), */\ + { 0x8207, "calibr_gain_cs"}, /* gain current sense (Imeasalpha) 2's compliment (key1 protected), */\ + { 0x8284, "calibr_temp_offset"}, /* temperature offset 2's compliment (key1 protected), */\ + { 0x82d2, "calibr_temp_gain"}, /* temperature gain 2's compliment (key1 protected) , */\ + { 0x830f, "calibr_ron"}, /* calibration value of the RON resistance of the coil, */\ + { 0x8505, "type_bits_hw"}, /* bit0 = disable function dcdcoff_mode ($09[7]) , */\ + { 0x8601, "type_bits_1_0_sw"}, /* MTP control SW , */\ + { 0x8681, "type_bits_9_8_sw"}, /* MTP control SW , */\ + { 0x870f, "type_bits2_sw"}, /* MTP-control SW2 , */\ + { 0x8806, "htol_iic_addr"}, /* 7-bit I2C address to be used during HTOL testing , */\ + { 0x8870, "htol_iic_addr_en"}, /* HTOL I2C_Address_Enable , */\ + { 0x8881, "ctrl_ovp_response"}, /* OVP response control , */\ + { 0x88a0, "disable_ovp_alarm_state"}, /* OVP alarm state control , */\ + { 0x88b0, "enbl_stretch_ovp"}, /* OVP alram strech control , */\ + { 0x88c0, "cf_debug_mode"}, /* Coolflux debug mode , */\ + { 0x8a0f, "production_data1"}, /* production_data1 , */\ + { 0x8b0f, "production_data2"}, /* production_data2 , */\ + { 0x8c0f, "production_data3"}, /* production_data3 , */\ + { 0x8d0f, "production_data4"}, /* production_data4 , */\ + { 0x8e0f, "production_data5"}, /* production_data5 , */\ + { 0x8f0f, "production_data6"}, /* production_data6 , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +enum TFA9896_irq { + TFA9896_irq_vdds = 0, + TFA9896_irq_plls = 1, + TFA9896_irq_ds = 2, + TFA9896_irq_vds = 3, + TFA9896_irq_uvds = 4, + TFA9896_irq_cds = 5, + TFA9896_irq_clks = 6, + TFA9896_irq_clips = 7, + TFA9896_irq_mtpb = 8, + TFA9896_irq_clk = 9, + TFA9896_irq_spks = 10, + TFA9896_irq_acs = 11, + TFA9896_irq_sws = 12, + TFA9896_irq_wds = 13, + TFA9896_irq_amps = 14, + TFA9896_irq_arefs = 15, + TFA9896_irq_err = 32, + TFA9896_irq_ack = 33, + TFA9896_irq_max = 34, + TFA9896_irq_all = -1 /* all irqs */}; + +#define TFA9896_IRQ_NAMETABLE static tfaIrqName_t TFA9896IrqNames[]= {\ + { 0, "VDDS"},\ + { 1, "PLLS"},\ + { 2, "DS"},\ + { 3, "VDS"},\ + { 4, "UVDS"},\ + { 5, "CDS"},\ + { 6, "CLKS"},\ + { 7, "CLIPS"},\ + { 8, "MTPB"},\ + { 9, "CLK"},\ + { 10, "SPKS"},\ + { 11, "ACS"},\ + { 12, "SWS"},\ + { 13, "WDS"},\ + { 14, "AMPS"},\ + { 15, "AREFS"},\ + { 16, "16"},\ + { 17, "17"},\ + { 18, "18"},\ + { 19, "19"},\ + { 20, "20"},\ + { 21, "21"},\ + { 22, "22"},\ + { 23, "23"},\ + { 24, "24"},\ + { 25, "25"},\ + { 26, "26"},\ + { 27, "27"},\ + { 28, "28"},\ + { 29, "29"},\ + { 30, "30"},\ + { 31, "31"},\ + { 32, "ERR"},\ + { 33, "ACK"},\ + { 34, "34"},\ +}; +#endif /* _TFA9896_TFAFIELDNAMES_H */ diff --git a/sound/soc/codecs/inc/tfa98xx.h b/sound/soc/codecs/inc/tfa98xx.h new file mode 100644 index 000000000000..5fe1e05f8852 --- /dev/null +++ b/sound/soc/codecs/inc/tfa98xx.h @@ -0,0 +1,139 @@ +/* + * Copyright 2014-2017 NXP Semiconductors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __TFA98XX_INC__ +#define __TFA98XX_INC__ + +#include +#include +#include + +#include "tfa_device.h" +#include "tfa_container.h" +#include "config.h" + +/* max. length of a alsa mixer control name */ +#define MAX_CONTROL_NAME 48 + +#define TFA98XX_MAX_REGISTER 0xff + +#define TFA98XX_FLAG_SKIP_INTERRUPTS (1 << 0) +#define TFA98XX_FLAG_SAAM_AVAILABLE (1 << 1) +#define TFA98XX_FLAG_STEREO_DEVICE (1 << 2) +#define TFA98XX_FLAG_MULTI_MIC_INPUTS (1 << 3) +#define TFA98XX_FLAG_TAPDET_AVAILABLE (1 << 4) +#define TFA98XX_FLAG_CALIBRATION_CTL (1 << 5) +#define TFA98XX_FLAG_REMOVE_PLOP_NOISE (1 << 6) +#define TFA98XX_FLAG_LP_MODES (1 << 7) +#define TFA98XX_FLAG_TDM_DEVICE (1 << 8) + +#ifdef TFA9874_NONDSP_STEREO +#define TFA98XX_FLAG_CHIP_SELECTED (1 << 16) +#endif + +#define TFA98XX_NUM_RATES 9 + +/* DSP init status */ +enum tfa98xx_dsp_init_state { + TFA98XX_DSP_INIT_STOPPED, /* DSP not running */ + TFA98XX_DSP_INIT_RECOVER, /* DSP error detected at runtime */ + TFA98XX_DSP_INIT_FAIL, /* DSP init failed */ + TFA98XX_DSP_INIT_PENDING, /* DSP start requested */ + TFA98XX_DSP_INIT_DONE, /* DSP running */ + TFA98XX_DSP_INIT_INVALIDATED, /* DSP was running, requires re-init */ +}; + +enum tfa98xx_dsp_fw_state { + TFA98XX_DSP_FW_NONE = 0, + TFA98XX_DSP_FW_PENDING, + TFA98XX_DSP_FW_FAIL, + TFA98XX_DSP_FW_OK, +}; + +struct tfa98xx_firmware { + void *base; + struct tfa98xx_device *dev; + char name[9]; //TODO get length from tfa parameter defs +}; + +struct tfa98xx_baseprofile { + char basename[MAX_CONTROL_NAME]; /* profile basename */ + int len; /* profile length */ + int item_id; /* profile id */ + int sr_rate_sup[TFA98XX_NUM_RATES]; /* sample rates supported by this profile */ + struct list_head list; /* list of all profiles */ +}; + +struct tfa98xx { + struct regmap *regmap; + struct i2c_client *i2c; + struct regulator *vdd; + struct snd_soc_codec *codec; + struct workqueue_struct *tfa98xx_wq; + struct delayed_work init_work; + struct delayed_work monitor_work; + struct delayed_work interrupt_work; + struct delayed_work tapdet_work; + struct mutex dsp_lock; + int dsp_init; + int dsp_fw_state; + int sysclk; + int rst_gpio; + u16 rev; + int audio_mode; + struct tfa98xx_firmware fw; + char *fw_name; + int rate; + wait_queue_head_t wq; + struct device *dev; + unsigned int init_count; + int pstream; + int cstream; + struct input_dev *input; + bool tapdet_enabled; /* service enabled */ + bool tapdet_open; /* device file opened */ + unsigned int tapdet_profiles; /* tapdet profile bitfield */ + bool tapdet_poll; /* tapdet running on polling mode */ + + unsigned int rate_constraint_list[TFA98XX_NUM_RATES]; + struct snd_pcm_hw_constraint_list rate_constraint; + + int reset_gpio; + int power_gpio; + int irq_gpio; + + struct list_head list; + struct tfa_device *tfa; + int vstep; + int profile; + int prof_vsteps[TFACONT_MAXPROFS]; /* store vstep per profile (single device) */ + +#ifdef TFA9874_NONDSP_STEREO + unsigned int nonDSP_stereo; +#endif + +#ifdef CONFIG_DEBUG_FS + struct dentry *dbg_dir; +#endif + u8 reg; + unsigned int flags; + bool set_mtp_cal; + uint16_t cal_data; +}; + + +#endif /* __TFA98XX_INC__ */ + diff --git a/sound/soc/codecs/inc/tfa98xx_genregs_N1C.h b/sound/soc/codecs/inc/tfa98xx_genregs_N1C.h new file mode 100644 index 000000000000..7a124b82b332 --- /dev/null +++ b/sound/soc/codecs/inc/tfa98xx_genregs_N1C.h @@ -0,0 +1,3853 @@ +/** Filename: Tfa98xx_genregs.h + * This file was generated automatically on 09/01/15 at 09:40:23. + * Source file: TFA9888_N1C_I2C_regmap_V1.xlsx + */ + +#ifndef TFA2_GENREGS_H +#define TFA2_GENREGS_H + + +#define TFA98XX_SYS_CONTROL0 0x00 +#define TFA98XX_SYS_CONTROL1 0x01 +#define TFA98XX_SYS_CONTROL2 0x02 +#define TFA98XX_DEVICE_REVISION 0x03 +#define TFA98XX_CLOCK_CONTROL 0x04 +#define TFA98XX_CLOCK_GATING_CONTROL 0x05 +#define TFA98XX_SIDE_TONE_CONFIG 0x0d +#define TFA98XX_CTRL_DIGTOANA_REG 0x0e +#define TFA98XX_STATUS_FLAGS0 0x10 +#define TFA98XX_STATUS_FLAGS1 0x11 +#define TFA98XX_STATUS_FLAGS2 0x12 +#define TFA98XX_STATUS_FLAGS3 0x13 +#define TFA98XX_STATUS_FLAGS4 0x14 +#define TFA98XX_BATTERY_VOLTAGE 0x15 +#define TFA98XX_TEMPERATURE 0x16 +#define TFA98XX_TDM_CONFIG0 0x20 +#define TFA98XX_TDM_CONFIG1 0x21 +#define TFA98XX_TDM_CONFIG2 0x22 +#define TFA98XX_TDM_CONFIG3 0x23 +#define TFA98XX_TDM_CONFIG4 0x24 +#define TFA98XX_TDM_CONFIG5 0x25 +#define TFA98XX_TDM_CONFIG6 0x26 +#define TFA98XX_TDM_CONFIG7 0x27 +#define TFA98XX_TDM_CONFIG8 0x28 +#define TFA98XX_TDM_CONFIG9 0x29 +#define TFA98XX_PDM_CONFIG0 0x31 +#define TFA98XX_PDM_CONFIG1 0x32 +#define TFA98XX_HAPTIC_DRIVER_CONFIG 0x33 +#define TFA98XX_GPIO_DATAIN_REG 0x34 +#define TFA98XX_GPIO_CONFIG 0x35 +#define TFA98XX_INTERRUPT_OUT_REG1 0x40 +#define TFA98XX_INTERRUPT_OUT_REG2 0x41 +#define TFA98XX_INTERRUPT_OUT_REG3 0x42 +#define TFA98XX_INTERRUPT_IN_REG1 0x44 +#define TFA98XX_INTERRUPT_IN_REG2 0x45 +#define TFA98XX_INTERRUPT_IN_REG3 0x46 +#define TFA98XX_INTERRUPT_ENABLE_REG1 0x48 +#define TFA98XX_INTERRUPT_ENABLE_REG2 0x49 +#define TFA98XX_INTERRUPT_ENABLE_REG3 0x4a +#define TFA98XX_STATUS_POLARITY_REG1 0x4c +#define TFA98XX_STATUS_POLARITY_REG2 0x4d +#define TFA98XX_STATUS_POLARITY_REG3 0x4e +#define TFA98XX_BAT_PROT_CONFIG 0x50 +#define TFA98XX_AUDIO_CONTROL 0x51 +#define TFA98XX_AMPLIFIER_CONFIG 0x52 +#define TFA98XX_AUDIO_CONTROL2 0x5a +#define TFA98XX_DCDC_CONTROL0 0x70 +#define TFA98XX_CF_CONTROLS 0x90 +#define TFA98XX_CF_MAD 0x91 +#define TFA98XX_CF_MEM 0x92 +#define TFA98XX_CF_STATUS 0x93 +#define TFA98XX_MTPKEY2_REG 0xa1 +#define TFA98XX_MTP_STATUS 0xa2 +#define TFA98XX_KEY_PROTECTED_MTP_CONTROL 0xa3 +#define TFA98XX_MTP_DATA_OUT_MSB 0xa5 +#define TFA98XX_MTP_DATA_OUT_LSB 0xa6 +#define TFA98XX_TEMP_SENSOR_CONFIG 0xb1 +#define TFA98XX_KEY2_PROTECTED_MTP0 0xf0 +#define TFA98XX_KEY1_PROTECTED_MTP4 0xf4 +#define TFA98XX_KEY1_PROTECTED_MTP5 0xf5 + +/* + * (0x00)-sys_control0 + */ + +/* + * powerdown + */ +#define TFA98XX_SYS_CONTROL0_PWDN (0x1<<0) +#define TFA98XX_SYS_CONTROL0_PWDN_POS 0 +#define TFA98XX_SYS_CONTROL0_PWDN_LEN 1 +#define TFA98XX_SYS_CONTROL0_PWDN_MAX 1 +#define TFA98XX_SYS_CONTROL0_PWDN_MSK 0x1 + +/* + * reset + */ +#define TFA98XX_SYS_CONTROL0_I2CR (0x1<<1) +#define TFA98XX_SYS_CONTROL0_I2CR_POS 1 +#define TFA98XX_SYS_CONTROL0_I2CR_LEN 1 +#define TFA98XX_SYS_CONTROL0_I2CR_MAX 1 +#define TFA98XX_SYS_CONTROL0_I2CR_MSK 0x2 + +/* + * enbl_coolflux + */ +#define TFA98XX_SYS_CONTROL0_CFE (0x1<<2) +#define TFA98XX_SYS_CONTROL0_CFE_POS 2 +#define TFA98XX_SYS_CONTROL0_CFE_LEN 1 +#define TFA98XX_SYS_CONTROL0_CFE_MAX 1 +#define TFA98XX_SYS_CONTROL0_CFE_MSK 0x4 + +/* + * enbl_amplifier + */ +#define TFA98XX_SYS_CONTROL0_AMPE (0x1<<3) +#define TFA98XX_SYS_CONTROL0_AMPE_POS 3 +#define TFA98XX_SYS_CONTROL0_AMPE_LEN 1 +#define TFA98XX_SYS_CONTROL0_AMPE_MAX 1 +#define TFA98XX_SYS_CONTROL0_AMPE_MSK 0x8 + +/* + * enbl_boost + */ +#define TFA98XX_SYS_CONTROL0_DCA (0x1<<4) +#define TFA98XX_SYS_CONTROL0_DCA_POS 4 +#define TFA98XX_SYS_CONTROL0_DCA_LEN 1 +#define TFA98XX_SYS_CONTROL0_DCA_MAX 1 +#define TFA98XX_SYS_CONTROL0_DCA_MSK 0x10 + +/* + * coolflux_configured + */ +#define TFA98XX_SYS_CONTROL0_SBSL (0x1<<5) +#define TFA98XX_SYS_CONTROL0_SBSL_POS 5 +#define TFA98XX_SYS_CONTROL0_SBSL_LEN 1 +#define TFA98XX_SYS_CONTROL0_SBSL_MAX 1 +#define TFA98XX_SYS_CONTROL0_SBSL_MSK 0x20 + +/* + * sel_enbl_amplifier + */ +#define TFA98XX_SYS_CONTROL0_AMPC (0x1<<6) +#define TFA98XX_SYS_CONTROL0_AMPC_POS 6 +#define TFA98XX_SYS_CONTROL0_AMPC_LEN 1 +#define TFA98XX_SYS_CONTROL0_AMPC_MAX 1 +#define TFA98XX_SYS_CONTROL0_AMPC_MSK 0x40 + +/* + * int_pad_io + */ +#define TFA98XX_SYS_CONTROL0_INTP (0x3<<7) +#define TFA98XX_SYS_CONTROL0_INTP_POS 7 +#define TFA98XX_SYS_CONTROL0_INTP_LEN 2 +#define TFA98XX_SYS_CONTROL0_INTP_MAX 3 +#define TFA98XX_SYS_CONTROL0_INTP_MSK 0x180 + +/* + * fs_pulse_sel + */ +#define TFA98XX_SYS_CONTROL0_FSSSEL (0x3<<9) +#define TFA98XX_SYS_CONTROL0_FSSSEL_POS 9 +#define TFA98XX_SYS_CONTROL0_FSSSEL_LEN 2 +#define TFA98XX_SYS_CONTROL0_FSSSEL_MAX 3 +#define TFA98XX_SYS_CONTROL0_FSSSEL_MSK 0x600 + +/* + * bypass_ocp + */ +#define TFA98XX_SYS_CONTROL0_BYPOCP (0x1<<11) +#define TFA98XX_SYS_CONTROL0_BYPOCP_POS 11 +#define TFA98XX_SYS_CONTROL0_BYPOCP_LEN 1 +#define TFA98XX_SYS_CONTROL0_BYPOCP_MAX 1 +#define TFA98XX_SYS_CONTROL0_BYPOCP_MSK 0x800 + +/* + * test_ocp + */ +#define TFA98XX_SYS_CONTROL0_TSTOCP (0x1<<12) +#define TFA98XX_SYS_CONTROL0_TSTOCP_POS 12 +#define TFA98XX_SYS_CONTROL0_TSTOCP_LEN 1 +#define TFA98XX_SYS_CONTROL0_TSTOCP_MAX 1 +#define TFA98XX_SYS_CONTROL0_TSTOCP_MSK 0x1000 + + +/* + * (0x01)-sys_control1 + */ + +/* + * vamp_sel + */ +#define TFA98XX_SYS_CONTROL1_AMPINSEL (0x3<<0) +#define TFA98XX_SYS_CONTROL1_AMPINSEL_POS 0 +#define TFA98XX_SYS_CONTROL1_AMPINSEL_LEN 2 +#define TFA98XX_SYS_CONTROL1_AMPINSEL_MAX 3 +#define TFA98XX_SYS_CONTROL1_AMPINSEL_MSK 0x3 + +/* + * src_set_configured + */ +#define TFA98XX_SYS_CONTROL1_MANSCONF (0x1<<2) +#define TFA98XX_SYS_CONTROL1_MANSCONF_POS 2 +#define TFA98XX_SYS_CONTROL1_MANSCONF_LEN 1 +#define TFA98XX_SYS_CONTROL1_MANSCONF_MAX 1 +#define TFA98XX_SYS_CONTROL1_MANSCONF_MSK 0x4 + +/* + * execute_cold_start + */ +#define TFA98XX_SYS_CONTROL1_MANCOLD (0x1<<3) +#define TFA98XX_SYS_CONTROL1_MANCOLD_POS 3 +#define TFA98XX_SYS_CONTROL1_MANCOLD_LEN 1 +#define TFA98XX_SYS_CONTROL1_MANCOLD_MAX 1 +#define TFA98XX_SYS_CONTROL1_MANCOLD_MSK 0x8 + +/* + * enbl_osc1m_auto_off + */ +#define TFA98XX_SYS_CONTROL1_MANAOOSC (0x1<<4) +#define TFA98XX_SYS_CONTROL1_MANAOOSC_POS 4 +#define TFA98XX_SYS_CONTROL1_MANAOOSC_LEN 1 +#define TFA98XX_SYS_CONTROL1_MANAOOSC_MAX 1 +#define TFA98XX_SYS_CONTROL1_MANAOOSC_MSK 0x10 + +/* + * man_enbl_brown_out + */ +#define TFA98XX_SYS_CONTROL1_MANROBOD (0x1<<5) +#define TFA98XX_SYS_CONTROL1_MANROBOD_POS 5 +#define TFA98XX_SYS_CONTROL1_MANROBOD_LEN 1 +#define TFA98XX_SYS_CONTROL1_MANROBOD_MAX 1 +#define TFA98XX_SYS_CONTROL1_MANROBOD_MSK 0x20 + +/* + * enbl_bod + */ +#define TFA98XX_SYS_CONTROL1_BODE (0x1<<6) +#define TFA98XX_SYS_CONTROL1_BODE_POS 6 +#define TFA98XX_SYS_CONTROL1_BODE_LEN 1 +#define TFA98XX_SYS_CONTROL1_BODE_MAX 1 +#define TFA98XX_SYS_CONTROL1_BODE_MSK 0x40 + +/* + * enbl_bod_hyst + */ +#define TFA98XX_SYS_CONTROL1_BODHYS (0x1<<7) +#define TFA98XX_SYS_CONTROL1_BODHYS_POS 7 +#define TFA98XX_SYS_CONTROL1_BODHYS_LEN 1 +#define TFA98XX_SYS_CONTROL1_BODHYS_MAX 1 +#define TFA98XX_SYS_CONTROL1_BODHYS_MSK 0x80 + +/* + * bod_delay + */ +#define TFA98XX_SYS_CONTROL1_BODFILT (0x3<<8) +#define TFA98XX_SYS_CONTROL1_BODFILT_POS 8 +#define TFA98XX_SYS_CONTROL1_BODFILT_LEN 2 +#define TFA98XX_SYS_CONTROL1_BODFILT_MAX 3 +#define TFA98XX_SYS_CONTROL1_BODFILT_MSK 0x300 + +/* + * bod_lvlsel + */ +#define TFA98XX_SYS_CONTROL1_BODTHLVL (0x3<<10) +#define TFA98XX_SYS_CONTROL1_BODTHLVL_POS 10 +#define TFA98XX_SYS_CONTROL1_BODTHLVL_LEN 2 +#define TFA98XX_SYS_CONTROL1_BODTHLVL_MAX 3 +#define TFA98XX_SYS_CONTROL1_BODTHLVL_MSK 0xc00 + +/* + * disable_mute_time_out + */ +#define TFA98XX_SYS_CONTROL1_MUTETO (0x1<<13) +#define TFA98XX_SYS_CONTROL1_MUTETO_POS 13 +#define TFA98XX_SYS_CONTROL1_MUTETO_LEN 1 +#define TFA98XX_SYS_CONTROL1_MUTETO_MAX 1 +#define TFA98XX_SYS_CONTROL1_MUTETO_MSK 0x2000 + +/* + * pwm_sel_rcv_ns + */ +#define TFA98XX_SYS_CONTROL1_RCVNS (0x1<<14) +#define TFA98XX_SYS_CONTROL1_RCVNS_POS 14 +#define TFA98XX_SYS_CONTROL1_RCVNS_LEN 1 +#define TFA98XX_SYS_CONTROL1_RCVNS_MAX 1 +#define TFA98XX_SYS_CONTROL1_RCVNS_MSK 0x4000 + +/* + * man_enbl_watchdog + */ +#define TFA98XX_SYS_CONTROL1_MANWDE (0x1<<15) +#define TFA98XX_SYS_CONTROL1_MANWDE_POS 15 +#define TFA98XX_SYS_CONTROL1_MANWDE_LEN 1 +#define TFA98XX_SYS_CONTROL1_MANWDE_MAX 1 +#define TFA98XX_SYS_CONTROL1_MANWDE_MSK 0x8000 + + +/* + * (0x02)-sys_control2 + */ + +/* + * audio_fs + */ +#define TFA98XX_SYS_CONTROL2_AUDFS (0xf<<0) +#define TFA98XX_SYS_CONTROL2_AUDFS_POS 0 +#define TFA98XX_SYS_CONTROL2_AUDFS_LEN 4 +#define TFA98XX_SYS_CONTROL2_AUDFS_MAX 15 +#define TFA98XX_SYS_CONTROL2_AUDFS_MSK 0xf + +/* + * input_level + */ +#define TFA98XX_SYS_CONTROL2_INPLEV (0x1<<4) +#define TFA98XX_SYS_CONTROL2_INPLEV_POS 4 +#define TFA98XX_SYS_CONTROL2_INPLEV_LEN 1 +#define TFA98XX_SYS_CONTROL2_INPLEV_MAX 1 +#define TFA98XX_SYS_CONTROL2_INPLEV_MSK 0x10 + +/* + * cs_frac_delay + */ +#define TFA98XX_SYS_CONTROL2_FRACTDEL (0x3f<<5) +#define TFA98XX_SYS_CONTROL2_FRACTDEL_POS 5 +#define TFA98XX_SYS_CONTROL2_FRACTDEL_LEN 6 +#define TFA98XX_SYS_CONTROL2_FRACTDEL_MAX 63 +#define TFA98XX_SYS_CONTROL2_FRACTDEL_MSK 0x7e0 + +/* + * bypass_hvbat_filter + */ +#define TFA98XX_SYS_CONTROL2_BYPHVBF (0x1<<11) +#define TFA98XX_SYS_CONTROL2_BYPHVBF_POS 11 +#define TFA98XX_SYS_CONTROL2_BYPHVBF_LEN 1 +#define TFA98XX_SYS_CONTROL2_BYPHVBF_MAX 1 +#define TFA98XX_SYS_CONTROL2_BYPHVBF_MSK 0x800 + +/* + * ctrl_rcvldop_bypass + */ +#define TFA98XX_SYS_CONTROL2_LDOBYP (0x1<<12) +#define TFA98XX_SYS_CONTROL2_LDOBYP_POS 12 +#define TFA98XX_SYS_CONTROL2_LDOBYP_LEN 1 +#define TFA98XX_SYS_CONTROL2_LDOBYP_MAX 1 +#define TFA98XX_SYS_CONTROL2_LDOBYP_MSK 0x1000 + + +/* + * (0x03)-device_revision + */ + +/* + * device_rev + */ +#define TFA98XX_DEVICE_REVISION_REV (0xffff<<0) +#define TFA98XX_DEVICE_REVISION_REV_POS 0 +#define TFA98XX_DEVICE_REVISION_REV_LEN 16 +#define TFA98XX_DEVICE_REVISION_REV_MAX 65535 +#define TFA98XX_DEVICE_REVISION_REV_MSK 0xffff + + +/* + * (0x04)-clock_control + */ + +/* + * pll_clkin_sel + */ +#define TFA98XX_CLOCK_CONTROL_REFCKEXT (0x3<<0) +#define TFA98XX_CLOCK_CONTROL_REFCKEXT_POS 0 +#define TFA98XX_CLOCK_CONTROL_REFCKEXT_LEN 2 +#define TFA98XX_CLOCK_CONTROL_REFCKEXT_MAX 3 +#define TFA98XX_CLOCK_CONTROL_REFCKEXT_MSK 0x3 + +/* + * pll_clkin_sel_osc + */ +#define TFA98XX_CLOCK_CONTROL_REFCKSEL (0x1<<2) +#define TFA98XX_CLOCK_CONTROL_REFCKSEL_POS 2 +#define TFA98XX_CLOCK_CONTROL_REFCKSEL_LEN 1 +#define TFA98XX_CLOCK_CONTROL_REFCKSEL_MAX 1 +#define TFA98XX_CLOCK_CONTROL_REFCKSEL_MSK 0x4 + + +/* + * (0x05)-clock_gating_control + */ + +/* + * enbl_spkr_ss_left + */ +#define TFA98XX_CLOCK_GATING_CONTROL_SSLEFTE (0x1<<0) +#define TFA98XX_CLOCK_GATING_CONTROL_SSLEFTE_POS 0 +#define TFA98XX_CLOCK_GATING_CONTROL_SSLEFTE_LEN 1 +#define TFA98XX_CLOCK_GATING_CONTROL_SSLEFTE_MAX 1 +#define TFA98XX_CLOCK_GATING_CONTROL_SSLEFTE_MSK 0x1 + +/* + * enbl_spkr_ss_right + */ +#define TFA98XX_CLOCK_GATING_CONTROL_SSRIGHTE (0x1<<1) +#define TFA98XX_CLOCK_GATING_CONTROL_SSRIGHTE_POS 1 +#define TFA98XX_CLOCK_GATING_CONTROL_SSRIGHTE_LEN 1 +#define TFA98XX_CLOCK_GATING_CONTROL_SSRIGHTE_MAX 1 +#define TFA98XX_CLOCK_GATING_CONTROL_SSRIGHTE_MSK 0x2 + +/* + * enbl_volsense_left + */ +#define TFA98XX_CLOCK_GATING_CONTROL_VSLEFTE (0x1<<2) +#define TFA98XX_CLOCK_GATING_CONTROL_VSLEFTE_POS 2 +#define TFA98XX_CLOCK_GATING_CONTROL_VSLEFTE_LEN 1 +#define TFA98XX_CLOCK_GATING_CONTROL_VSLEFTE_MAX 1 +#define TFA98XX_CLOCK_GATING_CONTROL_VSLEFTE_MSK 0x4 + +/* + * enbl_volsense_right + */ +#define TFA98XX_CLOCK_GATING_CONTROL_VSRIGHTE (0x1<<3) +#define TFA98XX_CLOCK_GATING_CONTROL_VSRIGHTE_POS 3 +#define TFA98XX_CLOCK_GATING_CONTROL_VSRIGHTE_LEN 1 +#define TFA98XX_CLOCK_GATING_CONTROL_VSRIGHTE_MAX 1 +#define TFA98XX_CLOCK_GATING_CONTROL_VSRIGHTE_MSK 0x8 + +/* + * enbl_cursense_left + */ +#define TFA98XX_CLOCK_GATING_CONTROL_CSLEFTE (0x1<<4) +#define TFA98XX_CLOCK_GATING_CONTROL_CSLEFTE_POS 4 +#define TFA98XX_CLOCK_GATING_CONTROL_CSLEFTE_LEN 1 +#define TFA98XX_CLOCK_GATING_CONTROL_CSLEFTE_MAX 1 +#define TFA98XX_CLOCK_GATING_CONTROL_CSLEFTE_MSK 0x10 + +/* + * enbl_cursense_right + */ +#define TFA98XX_CLOCK_GATING_CONTROL_CSRIGHTE (0x1<<5) +#define TFA98XX_CLOCK_GATING_CONTROL_CSRIGHTE_POS 5 +#define TFA98XX_CLOCK_GATING_CONTROL_CSRIGHTE_LEN 1 +#define TFA98XX_CLOCK_GATING_CONTROL_CSRIGHTE_MAX 1 +#define TFA98XX_CLOCK_GATING_CONTROL_CSRIGHTE_MSK 0x20 + +/* + * enbl_pdm_ss + */ +#define TFA98XX_CLOCK_GATING_CONTROL_SSPDME (0x1<<6) +#define TFA98XX_CLOCK_GATING_CONTROL_SSPDME_POS 6 +#define TFA98XX_CLOCK_GATING_CONTROL_SSPDME_LEN 1 +#define TFA98XX_CLOCK_GATING_CONTROL_SSPDME_MAX 1 +#define TFA98XX_CLOCK_GATING_CONTROL_SSPDME_MSK 0x40 + + +/* + * (0x0d)-side_tone_config + */ + +/* + * side_tone_gain + */ +#define TFA98XX_SIDE_TONE_CONFIG_STGAIN (0x1ff<<1) +#define TFA98XX_SIDE_TONE_CONFIG_STGAIN_POS 1 +#define TFA98XX_SIDE_TONE_CONFIG_STGAIN_LEN 9 +#define TFA98XX_SIDE_TONE_CONFIG_STGAIN_MAX 511 +#define TFA98XX_SIDE_TONE_CONFIG_STGAIN_MSK 0x3fe + +/* + * mute_side_tone + */ +#define TFA98XX_SIDE_TONE_CONFIG_PDMSMUTE (0x1<<10) +#define TFA98XX_SIDE_TONE_CONFIG_PDMSMUTE_POS 10 +#define TFA98XX_SIDE_TONE_CONFIG_PDMSMUTE_LEN 1 +#define TFA98XX_SIDE_TONE_CONFIG_PDMSMUTE_MAX 1 +#define TFA98XX_SIDE_TONE_CONFIG_PDMSMUTE_MSK 0x400 + + +/* + * (0x0e)-ctrl_digtoana_reg + */ + +/* + * ctrl_digtoana + */ +#define TFA98XX_CTRL_DIGTOANA_REG_SWVSTEP (0x7f<<0) +#define TFA98XX_CTRL_DIGTOANA_REG_SWVSTEP_POS 0 +#define TFA98XX_CTRL_DIGTOANA_REG_SWVSTEP_LEN 7 +#define TFA98XX_CTRL_DIGTOANA_REG_SWVSTEP_MAX 127 +#define TFA98XX_CTRL_DIGTOANA_REG_SWVSTEP_MSK 0x7f + + +/* + * (0x10)-status_flags0 + */ + +/* + * flag_por + */ +#define TFA98XX_STATUS_FLAGS0_VDDS (0x1<<0) +#define TFA98XX_STATUS_FLAGS0_VDDS_POS 0 +#define TFA98XX_STATUS_FLAGS0_VDDS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_VDDS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_VDDS_MSK 0x1 + +/* + * flag_pll_lock + */ +#define TFA98XX_STATUS_FLAGS0_PLLS (0x1<<1) +#define TFA98XX_STATUS_FLAGS0_PLLS_POS 1 +#define TFA98XX_STATUS_FLAGS0_PLLS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_PLLS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_PLLS_MSK 0x2 + +/* + * flag_otpok + */ +#define TFA98XX_STATUS_FLAGS0_OTDS (0x1<<2) +#define TFA98XX_STATUS_FLAGS0_OTDS_POS 2 +#define TFA98XX_STATUS_FLAGS0_OTDS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_OTDS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_OTDS_MSK 0x4 + +/* + * flag_ovpok + */ +#define TFA98XX_STATUS_FLAGS0_OVDS (0x1<<3) +#define TFA98XX_STATUS_FLAGS0_OVDS_POS 3 +#define TFA98XX_STATUS_FLAGS0_OVDS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_OVDS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_OVDS_MSK 0x8 + +/* + * flag_uvpok + */ +#define TFA98XX_STATUS_FLAGS0_UVDS (0x1<<4) +#define TFA98XX_STATUS_FLAGS0_UVDS_POS 4 +#define TFA98XX_STATUS_FLAGS0_UVDS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_UVDS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_UVDS_MSK 0x10 + +/* + * flag_clocks_stable + */ +#define TFA98XX_STATUS_FLAGS0_CLKS (0x1<<6) +#define TFA98XX_STATUS_FLAGS0_CLKS_POS 5 +#define TFA98XX_STATUS_FLAGS0_CLKS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_CLKS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_CLKS_MSK 0x20 + +/* + * flag_mtp_busy + */ +#define TFA98XX_STATUS_FLAGS0_MTPB (0x1<<6) +#define TFA98XX_STATUS_FLAGS0_MTPB_POS 6 +#define TFA98XX_STATUS_FLAGS0_MTPB_LEN 1 +#define TFA98XX_STATUS_FLAGS0_MTPB_MAX 1 +#define TFA98XX_STATUS_FLAGS0_MTPB_MSK 0x40 + +/* + * flag_lost_clk + */ +#define TFA98XX_STATUS_FLAGS0_NOCLK (0x1<<7) +#define TFA98XX_STATUS_FLAGS0_NOCLK_POS 7 +#define TFA98XX_STATUS_FLAGS0_NOCLK_LEN 1 +#define TFA98XX_STATUS_FLAGS0_NOCLK_MAX 1 +#define TFA98XX_STATUS_FLAGS0_NOCLK_MSK 0x80 + +/* + * flag_cf_speakererror + */ +#define TFA98XX_STATUS_FLAGS0_SPKS (0x1<<8) +#define TFA98XX_STATUS_FLAGS0_SPKS_POS 8 +#define TFA98XX_STATUS_FLAGS0_SPKS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_SPKS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_SPKS_MSK 0x100 + +/* + * flag_cold_started + */ +#define TFA98XX_STATUS_FLAGS0_ACS (0x1<<9) +#define TFA98XX_STATUS_FLAGS0_ACS_POS 9 +#define TFA98XX_STATUS_FLAGS0_ACS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_ACS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_ACS_MSK 0x200 + +/* + * flag_engage + */ +#define TFA98XX_STATUS_FLAGS0_SWS (0x1<<10) +#define TFA98XX_STATUS_FLAGS0_SWS_POS 10 +#define TFA98XX_STATUS_FLAGS0_SWS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_SWS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_SWS_MSK 0x400 + +/* + * flag_watchdog_reset + */ +#define TFA98XX_STATUS_FLAGS0_WDS (0x1<<11) +#define TFA98XX_STATUS_FLAGS0_WDS_POS 11 +#define TFA98XX_STATUS_FLAGS0_WDS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_WDS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_WDS_MSK 0x800 + +/* + * flag_enbl_amp + */ +#define TFA98XX_STATUS_FLAGS0_AMPS (0x1<<12) +#define TFA98XX_STATUS_FLAGS0_AMPS_POS 12 +#define TFA98XX_STATUS_FLAGS0_AMPS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_AMPS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_AMPS_MSK 0x1000 + +/* + * flag_enbl_ref + */ +#define TFA98XX_STATUS_FLAGS0_AREFS (0x1<<13) +#define TFA98XX_STATUS_FLAGS0_AREFS_POS 13 +#define TFA98XX_STATUS_FLAGS0_AREFS_LEN 1 +#define TFA98XX_STATUS_FLAGS0_AREFS_MAX 1 +#define TFA98XX_STATUS_FLAGS0_AREFS_MSK 0x2000 + +/* + * flag_adc10_ready + */ +#define TFA98XX_STATUS_FLAGS0_ADCCR (0x1<<14) +#define TFA98XX_STATUS_FLAGS0_ADCCR_POS 14 +#define TFA98XX_STATUS_FLAGS0_ADCCR_LEN 1 +#define TFA98XX_STATUS_FLAGS0_ADCCR_MAX 1 +#define TFA98XX_STATUS_FLAGS0_ADCCR_MSK 0x4000 + +/* + * flag_bod_vddd_nok + */ +#define TFA98XX_STATUS_FLAGS0_BODNOK (0x1<<15) +#define TFA98XX_STATUS_FLAGS0_BODNOK_POS 15 +#define TFA98XX_STATUS_FLAGS0_BODNOK_LEN 1 +#define TFA98XX_STATUS_FLAGS0_BODNOK_MAX 1 +#define TFA98XX_STATUS_FLAGS0_BODNOK_MSK 0x8000 + + +/* + * (0x11)-status_flags1 + */ + +/* + * flag_bst_bstcur + */ +#define TFA98XX_STATUS_FLAGS1_DCIL (0x1<<0) +#define TFA98XX_STATUS_FLAGS1_DCIL_POS 0 +#define TFA98XX_STATUS_FLAGS1_DCIL_LEN 1 +#define TFA98XX_STATUS_FLAGS1_DCIL_MAX 1 +#define TFA98XX_STATUS_FLAGS1_DCIL_MSK 0x1 + +/* + * flag_bst_hiz + */ +#define TFA98XX_STATUS_FLAGS1_DCDCA (0x1<<1) +#define TFA98XX_STATUS_FLAGS1_DCDCA_POS 1 +#define TFA98XX_STATUS_FLAGS1_DCDCA_LEN 1 +#define TFA98XX_STATUS_FLAGS1_DCDCA_MAX 1 +#define TFA98XX_STATUS_FLAGS1_DCDCA_MSK 0x2 + +/* + * flag_bst_ocpok + */ +#define TFA98XX_STATUS_FLAGS1_DCOCPOK (0x1<<2) +#define TFA98XX_STATUS_FLAGS1_DCOCPOK_POS 2 +#define TFA98XX_STATUS_FLAGS1_DCOCPOK_LEN 1 +#define TFA98XX_STATUS_FLAGS1_DCOCPOK_MAX 1 +#define TFA98XX_STATUS_FLAGS1_DCOCPOK_MSK 0x4 + +/* + * flag_bst_voutcomp + */ +#define TFA98XX_STATUS_FLAGS1_DCHVBAT (0x1<<4) +#define TFA98XX_STATUS_FLAGS1_DCHVBAT_POS 4 +#define TFA98XX_STATUS_FLAGS1_DCHVBAT_LEN 1 +#define TFA98XX_STATUS_FLAGS1_DCHVBAT_MAX 1 +#define TFA98XX_STATUS_FLAGS1_DCHVBAT_MSK 0x10 + +/* + * flag_bst_voutcomp86 + */ +#define TFA98XX_STATUS_FLAGS1_DCH114 (0x1<<5) +#define TFA98XX_STATUS_FLAGS1_DCH114_POS 5 +#define TFA98XX_STATUS_FLAGS1_DCH114_LEN 1 +#define TFA98XX_STATUS_FLAGS1_DCH114_MAX 1 +#define TFA98XX_STATUS_FLAGS1_DCH114_MSK 0x20 + +/* + * flag_bst_voutcomp93 + */ +#define TFA98XX_STATUS_FLAGS1_DCH107 (0x1<<6) +#define TFA98XX_STATUS_FLAGS1_DCH107_POS 6 +#define TFA98XX_STATUS_FLAGS1_DCH107_LEN 1 +#define TFA98XX_STATUS_FLAGS1_DCH107_MAX 1 +#define TFA98XX_STATUS_FLAGS1_DCH107_MSK 0x40 + +/* + * flag_soft_mute_busy + */ +#define TFA98XX_STATUS_FLAGS1_STMUTEB (0x1<<7) +#define TFA98XX_STATUS_FLAGS1_STMUTEB_POS 7 +#define TFA98XX_STATUS_FLAGS1_STMUTEB_LEN 1 +#define TFA98XX_STATUS_FLAGS1_STMUTEB_MAX 1 +#define TFA98XX_STATUS_FLAGS1_STMUTEB_MSK 0x80 + +/* + * flag_soft_mute_state + */ +#define TFA98XX_STATUS_FLAGS1_STMUTE (0x1<<8) +#define TFA98XX_STATUS_FLAGS1_STMUTE_POS 8 +#define TFA98XX_STATUS_FLAGS1_STMUTE_LEN 1 +#define TFA98XX_STATUS_FLAGS1_STMUTE_MAX 1 +#define TFA98XX_STATUS_FLAGS1_STMUTE_MSK 0x100 + +/* + * flag_tdm_lut_error + */ +#define TFA98XX_STATUS_FLAGS1_TDMLUTER (0x1<<9) +#define TFA98XX_STATUS_FLAGS1_TDMLUTER_POS 9 +#define TFA98XX_STATUS_FLAGS1_TDMLUTER_LEN 1 +#define TFA98XX_STATUS_FLAGS1_TDMLUTER_MAX 1 +#define TFA98XX_STATUS_FLAGS1_TDMLUTER_MSK 0x200 + +/* + * flag_tdm_status + */ +#define TFA98XX_STATUS_FLAGS1_TDMSTAT (0x7<<10) +#define TFA98XX_STATUS_FLAGS1_TDMSTAT_POS 10 +#define TFA98XX_STATUS_FLAGS1_TDMSTAT_LEN 3 +#define TFA98XX_STATUS_FLAGS1_TDMSTAT_MAX 7 +#define TFA98XX_STATUS_FLAGS1_TDMSTAT_MSK 0x1c00 + +/* + * flag_tdm_error + */ +#define TFA98XX_STATUS_FLAGS1_TDMERR (0x1<<13) +#define TFA98XX_STATUS_FLAGS1_TDMERR_POS 13 +#define TFA98XX_STATUS_FLAGS1_TDMERR_LEN 1 +#define TFA98XX_STATUS_FLAGS1_TDMERR_MAX 1 +#define TFA98XX_STATUS_FLAGS1_TDMERR_MSK 0x2000 + +/* + * flag_haptic_busy + */ +#define TFA98XX_STATUS_FLAGS1_HAPTIC (0x1<<14) +#define TFA98XX_STATUS_FLAGS1_HAPTIC_POS 14 +#define TFA98XX_STATUS_FLAGS1_HAPTIC_LEN 1 +#define TFA98XX_STATUS_FLAGS1_HAPTIC_MAX 1 +#define TFA98XX_STATUS_FLAGS1_HAPTIC_MSK 0x4000 + + +/* + * (0x12)-status_flags2 + */ + +/* + * flag_ocpokap_left + */ +#define TFA98XX_STATUS_FLAGS2_OCPOAPL (0x1<<0) +#define TFA98XX_STATUS_FLAGS2_OCPOAPL_POS 0 +#define TFA98XX_STATUS_FLAGS2_OCPOAPL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_OCPOAPL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_OCPOAPL_MSK 0x1 + +/* + * flag_ocpokan_left + */ +#define TFA98XX_STATUS_FLAGS2_OCPOANL (0x1<<1) +#define TFA98XX_STATUS_FLAGS2_OCPOANL_POS 1 +#define TFA98XX_STATUS_FLAGS2_OCPOANL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_OCPOANL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_OCPOANL_MSK 0x2 + +/* + * flag_ocpokbp_left + */ +#define TFA98XX_STATUS_FLAGS2_OCPOBPL (0x1<<2) +#define TFA98XX_STATUS_FLAGS2_OCPOBPL_POS 2 +#define TFA98XX_STATUS_FLAGS2_OCPOBPL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_OCPOBPL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_OCPOBPL_MSK 0x4 + +/* + * flag_ocpokbn_left + */ +#define TFA98XX_STATUS_FLAGS2_OCPOBNL (0x1<<3) +#define TFA98XX_STATUS_FLAGS2_OCPOBNL_POS 3 +#define TFA98XX_STATUS_FLAGS2_OCPOBNL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_OCPOBNL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_OCPOBNL_MSK 0x8 + +/* + * flag_clipa_high_left + */ +#define TFA98XX_STATUS_FLAGS2_CLIPAHL (0x1<<4) +#define TFA98XX_STATUS_FLAGS2_CLIPAHL_POS 4 +#define TFA98XX_STATUS_FLAGS2_CLIPAHL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_CLIPAHL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_CLIPAHL_MSK 0x10 + +/* + * flag_clipa_low_left + */ +#define TFA98XX_STATUS_FLAGS2_CLIPALL (0x1<<5) +#define TFA98XX_STATUS_FLAGS2_CLIPALL_POS 5 +#define TFA98XX_STATUS_FLAGS2_CLIPALL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_CLIPALL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_CLIPALL_MSK 0x20 + +/* + * flag_clipb_high_left + */ +#define TFA98XX_STATUS_FLAGS2_CLIPBHL (0x1<<6) +#define TFA98XX_STATUS_FLAGS2_CLIPBHL_POS 6 +#define TFA98XX_STATUS_FLAGS2_CLIPBHL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_CLIPBHL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_CLIPBHL_MSK 0x40 + +/* + * flag_clipb_low_left + */ +#define TFA98XX_STATUS_FLAGS2_CLIPBLL (0x1<<7) +#define TFA98XX_STATUS_FLAGS2_CLIPBLL_POS 7 +#define TFA98XX_STATUS_FLAGS2_CLIPBLL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_CLIPBLL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_CLIPBLL_MSK 0x80 + +/* + * flag_ocpokap_rcv + */ +#define TFA98XX_STATUS_FLAGS2_OCPOAPRC (0x1<<8) +#define TFA98XX_STATUS_FLAGS2_OCPOAPRC_POS 8 +#define TFA98XX_STATUS_FLAGS2_OCPOAPRC_LEN 1 +#define TFA98XX_STATUS_FLAGS2_OCPOAPRC_MAX 1 +#define TFA98XX_STATUS_FLAGS2_OCPOAPRC_MSK 0x100 + +/* + * flag_ocpokan_rcv + */ +#define TFA98XX_STATUS_FLAGS2_OCPOANRC (0x1<<9) +#define TFA98XX_STATUS_FLAGS2_OCPOANRC_POS 9 +#define TFA98XX_STATUS_FLAGS2_OCPOANRC_LEN 1 +#define TFA98XX_STATUS_FLAGS2_OCPOANRC_MAX 1 +#define TFA98XX_STATUS_FLAGS2_OCPOANRC_MSK 0x200 + +/* + * flag_ocpokbp_rcv + */ +#define TFA98XX_STATUS_FLAGS2_OCPOBPRC (0x1<<10) +#define TFA98XX_STATUS_FLAGS2_OCPOBPRC_POS 10 +#define TFA98XX_STATUS_FLAGS2_OCPOBPRC_LEN 1 +#define TFA98XX_STATUS_FLAGS2_OCPOBPRC_MAX 1 +#define TFA98XX_STATUS_FLAGS2_OCPOBPRC_MSK 0x400 + +/* + * flag_ocpokbn_rcv + */ +#define TFA98XX_STATUS_FLAGS2_OCPOBNRC (0x1<<11) +#define TFA98XX_STATUS_FLAGS2_OCPOBNRC_POS 11 +#define TFA98XX_STATUS_FLAGS2_OCPOBNRC_LEN 1 +#define TFA98XX_STATUS_FLAGS2_OCPOBNRC_MAX 1 +#define TFA98XX_STATUS_FLAGS2_OCPOBNRC_MSK 0x800 + +/* + * flag_rcvldop_ready + */ +#define TFA98XX_STATUS_FLAGS2_RCVLDOR (0x1<<12) +#define TFA98XX_STATUS_FLAGS2_RCVLDOR_POS 12 +#define TFA98XX_STATUS_FLAGS2_RCVLDOR_LEN 1 +#define TFA98XX_STATUS_FLAGS2_RCVLDOR_MAX 1 +#define TFA98XX_STATUS_FLAGS2_RCVLDOR_MSK 0x1000 + +/* + * flag_rcvldop_bypassready + */ +#define TFA98XX_STATUS_FLAGS2_RCVLDOBR (0x1<<13) +#define TFA98XX_STATUS_FLAGS2_RCVLDOBR_POS 13 +#define TFA98XX_STATUS_FLAGS2_RCVLDOBR_LEN 1 +#define TFA98XX_STATUS_FLAGS2_RCVLDOBR_MAX 1 +#define TFA98XX_STATUS_FLAGS2_RCVLDOBR_MSK 0x2000 + +/* + * flag_ocp_alarm_left + */ +#define TFA98XX_STATUS_FLAGS2_OCDSL (0x1<<14) +#define TFA98XX_STATUS_FLAGS2_OCDSL_POS 14 +#define TFA98XX_STATUS_FLAGS2_OCDSL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_OCDSL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_OCDSL_MSK 0x4000 + +/* + * flag_clip_left + */ +#define TFA98XX_STATUS_FLAGS2_CLIPSL (0x1<<15) +#define TFA98XX_STATUS_FLAGS2_CLIPSL_POS 15 +#define TFA98XX_STATUS_FLAGS2_CLIPSL_LEN 1 +#define TFA98XX_STATUS_FLAGS2_CLIPSL_MAX 1 +#define TFA98XX_STATUS_FLAGS2_CLIPSL_MSK 0x8000 + + +/* + * (0x13)-status_flags3 + */ + +/* + * flag_ocpokap_right + */ +#define TFA98XX_STATUS_FLAGS3_OCPOAPR (0x1<<0) +#define TFA98XX_STATUS_FLAGS3_OCPOAPR_POS 0 +#define TFA98XX_STATUS_FLAGS3_OCPOAPR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_OCPOAPR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_OCPOAPR_MSK 0x1 + +/* + * flag_ocpokan_right + */ +#define TFA98XX_STATUS_FLAGS3_OCPOANR (0x1<<1) +#define TFA98XX_STATUS_FLAGS3_OCPOANR_POS 1 +#define TFA98XX_STATUS_FLAGS3_OCPOANR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_OCPOANR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_OCPOANR_MSK 0x2 + +/* + * flag_ocpokbp_right + */ +#define TFA98XX_STATUS_FLAGS3_OCPOBPR (0x1<<2) +#define TFA98XX_STATUS_FLAGS3_OCPOBPR_POS 2 +#define TFA98XX_STATUS_FLAGS3_OCPOBPR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_OCPOBPR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_OCPOBPR_MSK 0x4 + +/* + * flag_ocpokbn_right + */ +#define TFA98XX_STATUS_FLAGS3_OCPOBNR (0x1<<3) +#define TFA98XX_STATUS_FLAGS3_OCPOBNR_POS 3 +#define TFA98XX_STATUS_FLAGS3_OCPOBNR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_OCPOBNR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_OCPOBNR_MSK 0x8 + +/* + * flag_clipa_high_right + */ +#define TFA98XX_STATUS_FLAGS3_CLIPAHR (0x1<<4) +#define TFA98XX_STATUS_FLAGS3_CLIPAHR_POS 4 +#define TFA98XX_STATUS_FLAGS3_CLIPAHR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_CLIPAHR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_CLIPAHR_MSK 0x10 + +/* + * flag_clipa_low_right + */ +#define TFA98XX_STATUS_FLAGS3_CLIPALR (0x1<<5) +#define TFA98XX_STATUS_FLAGS3_CLIPALR_POS 5 +#define TFA98XX_STATUS_FLAGS3_CLIPALR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_CLIPALR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_CLIPALR_MSK 0x20 + +/* + * flag_clipb_high_right + */ +#define TFA98XX_STATUS_FLAGS3_CLIPBHR (0x1<<6) +#define TFA98XX_STATUS_FLAGS3_CLIPBHR_POS 6 +#define TFA98XX_STATUS_FLAGS3_CLIPBHR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_CLIPBHR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_CLIPBHR_MSK 0x40 + +/* + * flag_clipb_low_right + */ +#define TFA98XX_STATUS_FLAGS3_CLIPBLR (0x1<<7) +#define TFA98XX_STATUS_FLAGS3_CLIPBLR_POS 7 +#define TFA98XX_STATUS_FLAGS3_CLIPBLR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_CLIPBLR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_CLIPBLR_MSK 0x80 + +/* + * flag_ocp_alarm_right + */ +#define TFA98XX_STATUS_FLAGS3_OCDSR (0x1<<8) +#define TFA98XX_STATUS_FLAGS3_OCDSR_POS 8 +#define TFA98XX_STATUS_FLAGS3_OCDSR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_OCDSR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_OCDSR_MSK 0x100 + +/* + * flag_clip_right + */ +#define TFA98XX_STATUS_FLAGS3_CLIPSR (0x1<<9) +#define TFA98XX_STATUS_FLAGS3_CLIPSR_POS 9 +#define TFA98XX_STATUS_FLAGS3_CLIPSR_LEN 1 +#define TFA98XX_STATUS_FLAGS3_CLIPSR_MAX 1 +#define TFA98XX_STATUS_FLAGS3_CLIPSR_MSK 0x200 + +/* + * flag_mic_ocpok + */ +#define TFA98XX_STATUS_FLAGS3_OCPOKMC (0x1<<10) +#define TFA98XX_STATUS_FLAGS3_OCPOKMC_POS 10 +#define TFA98XX_STATUS_FLAGS3_OCPOKMC_LEN 1 +#define TFA98XX_STATUS_FLAGS3_OCPOKMC_MAX 1 +#define TFA98XX_STATUS_FLAGS3_OCPOKMC_MSK 0x400 + +/* + * flag_man_alarm_state + */ +#define TFA98XX_STATUS_FLAGS3_MANALARM (0x1<<11) +#define TFA98XX_STATUS_FLAGS3_MANALARM_POS 11 +#define TFA98XX_STATUS_FLAGS3_MANALARM_LEN 1 +#define TFA98XX_STATUS_FLAGS3_MANALARM_MAX 1 +#define TFA98XX_STATUS_FLAGS3_MANALARM_MSK 0x800 + +/* + * flag_man_wait_src_settings + */ +#define TFA98XX_STATUS_FLAGS3_MANWAIT1 (0x1<<12) +#define TFA98XX_STATUS_FLAGS3_MANWAIT1_POS 12 +#define TFA98XX_STATUS_FLAGS3_MANWAIT1_LEN 1 +#define TFA98XX_STATUS_FLAGS3_MANWAIT1_MAX 1 +#define TFA98XX_STATUS_FLAGS3_MANWAIT1_MSK 0x1000 + +/* + * flag_man_wait_cf_config + */ +#define TFA98XX_STATUS_FLAGS3_MANWAIT2 (0x1<<13) +#define TFA98XX_STATUS_FLAGS3_MANWAIT2_POS 13 +#define TFA98XX_STATUS_FLAGS3_MANWAIT2_LEN 1 +#define TFA98XX_STATUS_FLAGS3_MANWAIT2_MAX 1 +#define TFA98XX_STATUS_FLAGS3_MANWAIT2_MSK 0x2000 + +/* + * flag_man_start_mute_audio + */ +#define TFA98XX_STATUS_FLAGS3_MANMUTE (0x1<<14) +#define TFA98XX_STATUS_FLAGS3_MANMUTE_POS 14 +#define TFA98XX_STATUS_FLAGS3_MANMUTE_LEN 1 +#define TFA98XX_STATUS_FLAGS3_MANMUTE_MAX 1 +#define TFA98XX_STATUS_FLAGS3_MANMUTE_MSK 0x4000 + +/* + * flag_man_operating_state + */ +#define TFA98XX_STATUS_FLAGS3_MANOPER (0x1<<15) +#define TFA98XX_STATUS_FLAGS3_MANOPER_POS 15 +#define TFA98XX_STATUS_FLAGS3_MANOPER_LEN 1 +#define TFA98XX_STATUS_FLAGS3_MANOPER_MAX 1 +#define TFA98XX_STATUS_FLAGS3_MANOPER_MSK 0x8000 + + +/* + * (0x14)-status_flags4 + */ + +/* + * flag_cf_speakererror_left + */ +#define TFA98XX_STATUS_FLAGS4_SPKSL (0x1<<0) +#define TFA98XX_STATUS_FLAGS4_SPKSL_POS 0 +#define TFA98XX_STATUS_FLAGS4_SPKSL_LEN 1 +#define TFA98XX_STATUS_FLAGS4_SPKSL_MAX 1 +#define TFA98XX_STATUS_FLAGS4_SPKSL_MSK 0x1 + +/* + * flag_cf_speakererror_right + */ +#define TFA98XX_STATUS_FLAGS4_SPKSR (0x1<<1) +#define TFA98XX_STATUS_FLAGS4_SPKSR_POS 1 +#define TFA98XX_STATUS_FLAGS4_SPKSR_LEN 1 +#define TFA98XX_STATUS_FLAGS4_SPKSR_MAX 1 +#define TFA98XX_STATUS_FLAGS4_SPKSR_MSK 0x2 + +/* + * flag_clk_out_of_range + */ +#define TFA98XX_STATUS_FLAGS4_CLKOOR (0x1<<2) +#define TFA98XX_STATUS_FLAGS4_CLKOOR_POS 2 +#define TFA98XX_STATUS_FLAGS4_CLKOOR_LEN 1 +#define TFA98XX_STATUS_FLAGS4_CLKOOR_MAX 1 +#define TFA98XX_STATUS_FLAGS4_CLKOOR_MSK 0x4 + +/* + * man_state + */ +#define TFA98XX_STATUS_FLAGS4_MANSTATE (0xf<<3) +#define TFA98XX_STATUS_FLAGS4_MANSTATE_POS 3 +#define TFA98XX_STATUS_FLAGS4_MANSTATE_LEN 4 +#define TFA98XX_STATUS_FLAGS4_MANSTATE_MAX 15 +#define TFA98XX_STATUS_FLAGS4_MANSTATE_MSK 0x78 + + +/* + * (0x15)-battery_voltage + */ + +/* + * bat_adc + */ +#define TFA98XX_BATTERY_VOLTAGE_BATS (0x3ff<<0) +#define TFA98XX_BATTERY_VOLTAGE_BATS_POS 0 +#define TFA98XX_BATTERY_VOLTAGE_BATS_LEN 10 +#define TFA98XX_BATTERY_VOLTAGE_BATS_MAX 1023 +#define TFA98XX_BATTERY_VOLTAGE_BATS_MSK 0x3ff + + +/* + * (0x16)-temperature + */ + +/* + * temp_adc + */ +#define TFA98XX_TEMPERATURE_TEMPS (0x1ff<<0) +#define TFA98XX_TEMPERATURE_TEMPS_POS 0 +#define TFA98XX_TEMPERATURE_TEMPS_LEN 9 +#define TFA98XX_TEMPERATURE_TEMPS_MAX 511 +#define TFA98XX_TEMPERATURE_TEMPS_MSK 0x1ff + + +/* + * (0x20)-tdm_config0 + */ + +/* + * tdm_usecase + */ +#define TFA98XX_TDM_CONFIG0_TDMUC (0xf<<0) +#define TFA98XX_TDM_CONFIG0_TDMUC_POS 0 +#define TFA98XX_TDM_CONFIG0_TDMUC_LEN 4 +#define TFA98XX_TDM_CONFIG0_TDMUC_MAX 15 +#define TFA98XX_TDM_CONFIG0_TDMUC_MSK 0xf + +/* + * tdm_enable + */ +#define TFA98XX_TDM_CONFIG0_TDME (0x1<<4) +#define TFA98XX_TDM_CONFIG0_TDME_POS 4 +#define TFA98XX_TDM_CONFIG0_TDME_LEN 1 +#define TFA98XX_TDM_CONFIG0_TDME_MAX 1 +#define TFA98XX_TDM_CONFIG0_TDME_MSK 0x10 + +/* + * tdm_mode + */ +#define TFA98XX_TDM_CONFIG0_TDMMODE (0x1<<5) +#define TFA98XX_TDM_CONFIG0_TDMMODE_POS 5 +#define TFA98XX_TDM_CONFIG0_TDMMODE_LEN 1 +#define TFA98XX_TDM_CONFIG0_TDMMODE_MAX 1 +#define TFA98XX_TDM_CONFIG0_TDMMODE_MSK 0x20 + +/* + * tdm_clk_inversion + */ +#define TFA98XX_TDM_CONFIG0_TDMCLINV (0x1<<6) +#define TFA98XX_TDM_CONFIG0_TDMCLINV_POS 6 +#define TFA98XX_TDM_CONFIG0_TDMCLINV_LEN 1 +#define TFA98XX_TDM_CONFIG0_TDMCLINV_MAX 1 +#define TFA98XX_TDM_CONFIG0_TDMCLINV_MSK 0x40 + +/* + * tdm_fs_ws_length + */ +#define TFA98XX_TDM_CONFIG0_TDMFSLN (0xf<<7) +#define TFA98XX_TDM_CONFIG0_TDMFSLN_POS 7 +#define TFA98XX_TDM_CONFIG0_TDMFSLN_LEN 4 +#define TFA98XX_TDM_CONFIG0_TDMFSLN_MAX 15 +#define TFA98XX_TDM_CONFIG0_TDMFSLN_MSK 0x780 + +/* + * tdm_fs_ws_polarity + */ +#define TFA98XX_TDM_CONFIG0_TDMFSPOL (0x1<<11) +#define TFA98XX_TDM_CONFIG0_TDMFSPOL_POS 11 +#define TFA98XX_TDM_CONFIG0_TDMFSPOL_LEN 1 +#define TFA98XX_TDM_CONFIG0_TDMFSPOL_MAX 1 +#define TFA98XX_TDM_CONFIG0_TDMFSPOL_MSK 0x800 + +/* + * tdm_nbck + */ +#define TFA98XX_TDM_CONFIG0_TDMNBCK (0xf<<12) +#define TFA98XX_TDM_CONFIG0_TDMNBCK_POS 12 +#define TFA98XX_TDM_CONFIG0_TDMNBCK_LEN 4 +#define TFA98XX_TDM_CONFIG0_TDMNBCK_MAX 15 +#define TFA98XX_TDM_CONFIG0_TDMNBCK_MSK 0xf000 + + +/* + * (0x21)-tdm_config1 + */ + +/* + * tdm_nb_of_slots + */ +#define TFA98XX_TDM_CONFIG1_TDMSLOTS (0xf<<0) +#define TFA98XX_TDM_CONFIG1_TDMSLOTS_POS 0 +#define TFA98XX_TDM_CONFIG1_TDMSLOTS_LEN 4 +#define TFA98XX_TDM_CONFIG1_TDMSLOTS_MAX 15 +#define TFA98XX_TDM_CONFIG1_TDMSLOTS_MSK 0xf + +/* + * tdm_slot_length + */ +#define TFA98XX_TDM_CONFIG1_TDMSLLN (0x1f<<4) +#define TFA98XX_TDM_CONFIG1_TDMSLLN_POS 4 +#define TFA98XX_TDM_CONFIG1_TDMSLLN_LEN 5 +#define TFA98XX_TDM_CONFIG1_TDMSLLN_MAX 31 +#define TFA98XX_TDM_CONFIG1_TDMSLLN_MSK 0x1f0 + +/* + * tdm_bits_remaining + */ +#define TFA98XX_TDM_CONFIG1_TDMBRMG (0x1f<<9) +#define TFA98XX_TDM_CONFIG1_TDMBRMG_POS 9 +#define TFA98XX_TDM_CONFIG1_TDMBRMG_LEN 5 +#define TFA98XX_TDM_CONFIG1_TDMBRMG_MAX 31 +#define TFA98XX_TDM_CONFIG1_TDMBRMG_MSK 0x3e00 + +/* + * tdm_data_delay + */ +#define TFA98XX_TDM_CONFIG1_TDMDEL (0x1<<14) +#define TFA98XX_TDM_CONFIG1_TDMDEL_POS 14 +#define TFA98XX_TDM_CONFIG1_TDMDEL_LEN 1 +#define TFA98XX_TDM_CONFIG1_TDMDEL_MAX 1 +#define TFA98XX_TDM_CONFIG1_TDMDEL_MSK 0x4000 + +/* + * tdm_data_adjustment + */ +#define TFA98XX_TDM_CONFIG1_TDMADJ (0x1<<15) +#define TFA98XX_TDM_CONFIG1_TDMADJ_POS 15 +#define TFA98XX_TDM_CONFIG1_TDMADJ_LEN 1 +#define TFA98XX_TDM_CONFIG1_TDMADJ_MAX 1 +#define TFA98XX_TDM_CONFIG1_TDMADJ_MSK 0x8000 + + +/* + * (0x22)-tdm_config2 + */ + +/* + * tdm_audio_sample_compression + */ +#define TFA98XX_TDM_CONFIG2_TDMOOMP (0x3<<0) +#define TFA98XX_TDM_CONFIG2_TDMOOMP_POS 0 +#define TFA98XX_TDM_CONFIG2_TDMOOMP_LEN 2 +#define TFA98XX_TDM_CONFIG2_TDMOOMP_MAX 3 +#define TFA98XX_TDM_CONFIG2_TDMOOMP_MSK 0x3 + +/* + * tdm_sample_size + */ +#define TFA98XX_TDM_CONFIG2_TDMSSIZE (0x1f<<2) +#define TFA98XX_TDM_CONFIG2_TDMSSIZE_POS 2 +#define TFA98XX_TDM_CONFIG2_TDMSSIZE_LEN 5 +#define TFA98XX_TDM_CONFIG2_TDMSSIZE_MAX 31 +#define TFA98XX_TDM_CONFIG2_TDMSSIZE_MSK 0x7c + +/* + * tdm_txdata_format + */ +#define TFA98XX_TDM_CONFIG2_TDMTXDFO (0x3<<7) +#define TFA98XX_TDM_CONFIG2_TDMTXDFO_POS 7 +#define TFA98XX_TDM_CONFIG2_TDMTXDFO_LEN 2 +#define TFA98XX_TDM_CONFIG2_TDMTXDFO_MAX 3 +#define TFA98XX_TDM_CONFIG2_TDMTXDFO_MSK 0x180 + +/* + * tdm_txdata_format_unused_slot_sd0 + */ +#define TFA98XX_TDM_CONFIG2_TDMTXUS0 (0x3<<9) +#define TFA98XX_TDM_CONFIG2_TDMTXUS0_POS 9 +#define TFA98XX_TDM_CONFIG2_TDMTXUS0_LEN 2 +#define TFA98XX_TDM_CONFIG2_TDMTXUS0_MAX 3 +#define TFA98XX_TDM_CONFIG2_TDMTXUS0_MSK 0x600 + +/* + * tdm_txdata_format_unused_slot_sd1 + */ +#define TFA98XX_TDM_CONFIG2_TDMTXUS1 (0x3<<11) +#define TFA98XX_TDM_CONFIG2_TDMTXUS1_POS 11 +#define TFA98XX_TDM_CONFIG2_TDMTXUS1_LEN 2 +#define TFA98XX_TDM_CONFIG2_TDMTXUS1_MAX 3 +#define TFA98XX_TDM_CONFIG2_TDMTXUS1_MSK 0x1800 + +/* + * tdm_txdata_format_unused_slot_sd2 + */ +#define TFA98XX_TDM_CONFIG2_TDMTXUS2 (0x3<<13) +#define TFA98XX_TDM_CONFIG2_TDMTXUS2_POS 13 +#define TFA98XX_TDM_CONFIG2_TDMTXUS2_LEN 2 +#define TFA98XX_TDM_CONFIG2_TDMTXUS2_MAX 3 +#define TFA98XX_TDM_CONFIG2_TDMTXUS2_MSK 0x6000 + + +/* + * (0x23)-tdm_config3 + */ + +/* + * tdm_sink1_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMLE (0x1<<1) +#define TFA98XX_TDM_CONFIG3_TDMLE_POS 1 +#define TFA98XX_TDM_CONFIG3_TDMLE_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMLE_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMLE_MSK 0x2 + +/* + * tdm_sink2_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMRE (0x1<<2) +#define TFA98XX_TDM_CONFIG3_TDMRE_POS 2 +#define TFA98XX_TDM_CONFIG3_TDMRE_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMRE_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMRE_MSK 0x4 + +/* + * tdm_source1_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMVSRE (0x1<<4) +#define TFA98XX_TDM_CONFIG3_TDMVSRE_POS 4 +#define TFA98XX_TDM_CONFIG3_TDMVSRE_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMVSRE_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMVSRE_MSK 0x10 + +/* + * tdm_source2_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMCSRE (0x1<<5) +#define TFA98XX_TDM_CONFIG3_TDMCSRE_POS 5 +#define TFA98XX_TDM_CONFIG3_TDMCSRE_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMCSRE_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMCSRE_MSK 0x20 + +/* + * tdm_source3_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMVSLE (0x1<<6) +#define TFA98XX_TDM_CONFIG3_TDMVSLE_POS 6 +#define TFA98XX_TDM_CONFIG3_TDMVSLE_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMVSLE_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMVSLE_MSK 0x40 + +/* + * tdm_source4_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMCSLE (0x1<<7) +#define TFA98XX_TDM_CONFIG3_TDMCSLE_POS 7 +#define TFA98XX_TDM_CONFIG3_TDMCSLE_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMCSLE_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMCSLE_MSK 0x80 + +/* + * tdm_source5_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMCFRE (0x1<<8) +#define TFA98XX_TDM_CONFIG3_TDMCFRE_POS 8 +#define TFA98XX_TDM_CONFIG3_TDMCFRE_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMCFRE_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMCFRE_MSK 0x100 + +/* + * tdm_source6_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMCFLE (0x1<<9) +#define TFA98XX_TDM_CONFIG3_TDMCFLE_POS 9 +#define TFA98XX_TDM_CONFIG3_TDMCFLE_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMCFLE_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMCFLE_MSK 0x200 + +/* + * tdm_source7_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMCF3E (0x1<<10) +#define TFA98XX_TDM_CONFIG3_TDMCF3E_POS 10 +#define TFA98XX_TDM_CONFIG3_TDMCF3E_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMCF3E_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMCF3E_MSK 0x400 + +/* + * tdm_source8_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMCF4E (0x1<<11) +#define TFA98XX_TDM_CONFIG3_TDMCF4E_POS 11 +#define TFA98XX_TDM_CONFIG3_TDMCF4E_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMCF4E_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMCF4E_MSK 0x800 + +/* + * tdm_source9_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMPD1E (0x1<<12) +#define TFA98XX_TDM_CONFIG3_TDMPD1E_POS 12 +#define TFA98XX_TDM_CONFIG3_TDMPD1E_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMPD1E_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMPD1E_MSK 0x1000 + +/* + * tdm_source10_enable + */ +#define TFA98XX_TDM_CONFIG3_TDMPD2E (0x1<<13) +#define TFA98XX_TDM_CONFIG3_TDMPD2E_POS 13 +#define TFA98XX_TDM_CONFIG3_TDMPD2E_LEN 1 +#define TFA98XX_TDM_CONFIG3_TDMPD2E_MAX 1 +#define TFA98XX_TDM_CONFIG3_TDMPD2E_MSK 0x2000 + + +/* + * (0x24)-tdm_config4 + */ + +/* + * tdm_sink1_io + */ +#define TFA98XX_TDM_CONFIG4_TDMLIO (0x3<<2) +#define TFA98XX_TDM_CONFIG4_TDMLIO_POS 2 +#define TFA98XX_TDM_CONFIG4_TDMLIO_LEN 2 +#define TFA98XX_TDM_CONFIG4_TDMLIO_MAX 3 +#define TFA98XX_TDM_CONFIG4_TDMLIO_MSK 0xc + +/* + * tdm_sink2_io + */ +#define TFA98XX_TDM_CONFIG4_TDMRIO (0x3<<4) +#define TFA98XX_TDM_CONFIG4_TDMRIO_POS 4 +#define TFA98XX_TDM_CONFIG4_TDMRIO_LEN 2 +#define TFA98XX_TDM_CONFIG4_TDMRIO_MAX 3 +#define TFA98XX_TDM_CONFIG4_TDMRIO_MSK 0x30 + +/* + * tdm_source1_io + */ +#define TFA98XX_TDM_CONFIG4_TDMVSRIO (0x3<<8) +#define TFA98XX_TDM_CONFIG4_TDMVSRIO_POS 8 +#define TFA98XX_TDM_CONFIG4_TDMVSRIO_LEN 2 +#define TFA98XX_TDM_CONFIG4_TDMVSRIO_MAX 3 +#define TFA98XX_TDM_CONFIG4_TDMVSRIO_MSK 0x300 + +/* + * tdm_source2_io + */ +#define TFA98XX_TDM_CONFIG4_TDMCSRIO (0x3<<10) +#define TFA98XX_TDM_CONFIG4_TDMCSRIO_POS 10 +#define TFA98XX_TDM_CONFIG4_TDMCSRIO_LEN 2 +#define TFA98XX_TDM_CONFIG4_TDMCSRIO_MAX 3 +#define TFA98XX_TDM_CONFIG4_TDMCSRIO_MSK 0xc00 + +/* + * tdm_source3_io + */ +#define TFA98XX_TDM_CONFIG4_TDMVSLIO (0x3<<12) +#define TFA98XX_TDM_CONFIG4_TDMVSLIO_POS 12 +#define TFA98XX_TDM_CONFIG4_TDMVSLIO_LEN 2 +#define TFA98XX_TDM_CONFIG4_TDMVSLIO_MAX 3 +#define TFA98XX_TDM_CONFIG4_TDMVSLIO_MSK 0x3000 + +/* + * tdm_source4_io + */ +#define TFA98XX_TDM_CONFIG4_TDMCSLIO (0x3<<14) +#define TFA98XX_TDM_CONFIG4_TDMCSLIO_POS 14 +#define TFA98XX_TDM_CONFIG4_TDMCSLIO_LEN 2 +#define TFA98XX_TDM_CONFIG4_TDMCSLIO_MAX 3 +#define TFA98XX_TDM_CONFIG4_TDMCSLIO_MSK 0xc000 + + +/* + * (0x25)-tdm_config5 + */ + +/* + * tdm_source5_io + */ +#define TFA98XX_TDM_CONFIG5_TDMCFRIO (0x3<<0) +#define TFA98XX_TDM_CONFIG5_TDMCFRIO_POS 0 +#define TFA98XX_TDM_CONFIG5_TDMCFRIO_LEN 2 +#define TFA98XX_TDM_CONFIG5_TDMCFRIO_MAX 3 +#define TFA98XX_TDM_CONFIG5_TDMCFRIO_MSK 0x3 + +/* + * tdm_source6_io + */ +#define TFA98XX_TDM_CONFIG5_TDMCFLIO (0x3<<2) +#define TFA98XX_TDM_CONFIG5_TDMCFLIO_POS 2 +#define TFA98XX_TDM_CONFIG5_TDMCFLIO_LEN 2 +#define TFA98XX_TDM_CONFIG5_TDMCFLIO_MAX 3 +#define TFA98XX_TDM_CONFIG5_TDMCFLIO_MSK 0xc + +/* + * tdm_source7_io + */ +#define TFA98XX_TDM_CONFIG5_TDMCF3IO (0x3<<4) +#define TFA98XX_TDM_CONFIG5_TDMCF3IO_POS 4 +#define TFA98XX_TDM_CONFIG5_TDMCF3IO_LEN 2 +#define TFA98XX_TDM_CONFIG5_TDMCF3IO_MAX 3 +#define TFA98XX_TDM_CONFIG5_TDMCF3IO_MSK 0x30 + +/* + * tdm_source8_io + */ +#define TFA98XX_TDM_CONFIG5_TDMCF4IO (0x3<<6) +#define TFA98XX_TDM_CONFIG5_TDMCF4IO_POS 6 +#define TFA98XX_TDM_CONFIG5_TDMCF4IO_LEN 2 +#define TFA98XX_TDM_CONFIG5_TDMCF4IO_MAX 3 +#define TFA98XX_TDM_CONFIG5_TDMCF4IO_MSK 0xc0 + +/* + * tdm_source9_io + */ +#define TFA98XX_TDM_CONFIG5_TDMPD1IO (0x3<<8) +#define TFA98XX_TDM_CONFIG5_TDMPD1IO_POS 8 +#define TFA98XX_TDM_CONFIG5_TDMPD1IO_LEN 2 +#define TFA98XX_TDM_CONFIG5_TDMPD1IO_MAX 3 +#define TFA98XX_TDM_CONFIG5_TDMPD1IO_MSK 0x300 + +/* + * tdm_source10_io + */ +#define TFA98XX_TDM_CONFIG5_TDMPD2IO (0x3<<10) +#define TFA98XX_TDM_CONFIG5_TDMPD2IO_POS 10 +#define TFA98XX_TDM_CONFIG5_TDMPD2IO_LEN 2 +#define TFA98XX_TDM_CONFIG5_TDMPD2IO_MAX 3 +#define TFA98XX_TDM_CONFIG5_TDMPD2IO_MSK 0xc00 + + +/* + * (0x26)-tdm_config6 + */ + +/* + * tdm_sink1_slot + */ +#define TFA98XX_TDM_CONFIG6_TDMLS (0xf<<4) +#define TFA98XX_TDM_CONFIG6_TDMLS_POS 4 +#define TFA98XX_TDM_CONFIG6_TDMLS_LEN 4 +#define TFA98XX_TDM_CONFIG6_TDMLS_MAX 15 +#define TFA98XX_TDM_CONFIG6_TDMLS_MSK 0xf0 + +/* + * tdm_sink2_slot + */ +#define TFA98XX_TDM_CONFIG6_TDMRS (0xf<<8) +#define TFA98XX_TDM_CONFIG6_TDMRS_POS 8 +#define TFA98XX_TDM_CONFIG6_TDMRS_LEN 4 +#define TFA98XX_TDM_CONFIG6_TDMRS_MAX 15 +#define TFA98XX_TDM_CONFIG6_TDMRS_MSK 0xf00 + + +/* + * (0x27)-tdm_config7 + */ + +/* + * tdm_source1_slot + */ +#define TFA98XX_TDM_CONFIG7_TDMVSRS (0xf<<0) +#define TFA98XX_TDM_CONFIG7_TDMVSRS_POS 0 +#define TFA98XX_TDM_CONFIG7_TDMVSRS_LEN 4 +#define TFA98XX_TDM_CONFIG7_TDMVSRS_MAX 15 +#define TFA98XX_TDM_CONFIG7_TDMVSRS_MSK 0xf + +/* + * tdm_source2_slot + */ +#define TFA98XX_TDM_CONFIG7_TDMCSRS (0xf<<4) +#define TFA98XX_TDM_CONFIG7_TDMCSRS_POS 4 +#define TFA98XX_TDM_CONFIG7_TDMCSRS_LEN 4 +#define TFA98XX_TDM_CONFIG7_TDMCSRS_MAX 15 +#define TFA98XX_TDM_CONFIG7_TDMCSRS_MSK 0xf0 + +/* + * tdm_source3_slot + */ +#define TFA98XX_TDM_CONFIG7_TDMVSLS (0xf<<8) +#define TFA98XX_TDM_CONFIG7_TDMVSLS_POS 8 +#define TFA98XX_TDM_CONFIG7_TDMVSLS_LEN 4 +#define TFA98XX_TDM_CONFIG7_TDMVSLS_MAX 15 +#define TFA98XX_TDM_CONFIG7_TDMVSLS_MSK 0xf00 + +/* + * tdm_source4_slot + */ +#define TFA98XX_TDM_CONFIG7_TDMCSLS (0xf<<12) +#define TFA98XX_TDM_CONFIG7_TDMCSLS_POS 12 +#define TFA98XX_TDM_CONFIG7_TDMCSLS_LEN 4 +#define TFA98XX_TDM_CONFIG7_TDMCSLS_MAX 15 +#define TFA98XX_TDM_CONFIG7_TDMCSLS_MSK 0xf000 + + +/* + * (0x28)-tdm_config8 + */ + +/* + * tdm_source5_slot + */ +#define TFA98XX_TDM_CONFIG8_TDMCFRS (0xf<<0) +#define TFA98XX_TDM_CONFIG8_TDMCFRS_POS 0 +#define TFA98XX_TDM_CONFIG8_TDMCFRS_LEN 4 +#define TFA98XX_TDM_CONFIG8_TDMCFRS_MAX 15 +#define TFA98XX_TDM_CONFIG8_TDMCFRS_MSK 0xf + +/* + * tdm_source6_slot + */ +#define TFA98XX_TDM_CONFIG8_TDMCFLS (0xf<<4) +#define TFA98XX_TDM_CONFIG8_TDMCFLS_POS 4 +#define TFA98XX_TDM_CONFIG8_TDMCFLS_LEN 4 +#define TFA98XX_TDM_CONFIG8_TDMCFLS_MAX 15 +#define TFA98XX_TDM_CONFIG8_TDMCFLS_MSK 0xf0 + +/* + * tdm_source7_slot + */ +#define TFA98XX_TDM_CONFIG8_TDMCF3S (0xf<<8) +#define TFA98XX_TDM_CONFIG8_TDMCF3S_POS 8 +#define TFA98XX_TDM_CONFIG8_TDMCF3S_LEN 4 +#define TFA98XX_TDM_CONFIG8_TDMCF3S_MAX 15 +#define TFA98XX_TDM_CONFIG8_TDMCF3S_MSK 0xf00 + +/* + * tdm_source8_slot + */ +#define TFA98XX_TDM_CONFIG8_TDMCF4S (0xf<<12) +#define TFA98XX_TDM_CONFIG8_TDMCF4S_POS 12 +#define TFA98XX_TDM_CONFIG8_TDMCF4S_LEN 4 +#define TFA98XX_TDM_CONFIG8_TDMCF4S_MAX 15 +#define TFA98XX_TDM_CONFIG8_TDMCF4S_MSK 0xf000 + + +/* + * (0x29)-tdm_config9 + */ + +/* + * tdm_source9_slot + */ +#define TFA98XX_TDM_CONFIG9_TDMPD1S (0xf<<0) +#define TFA98XX_TDM_CONFIG9_TDMPD1S_POS 0 +#define TFA98XX_TDM_CONFIG9_TDMPD1S_LEN 4 +#define TFA98XX_TDM_CONFIG9_TDMPD1S_MAX 15 +#define TFA98XX_TDM_CONFIG9_TDMPD1S_MSK 0xf + +/* + * tdm_source10_slot + */ +#define TFA98XX_TDM_CONFIG9_TDMPD2S (0xf<<4) +#define TFA98XX_TDM_CONFIG9_TDMPD2S_POS 4 +#define TFA98XX_TDM_CONFIG9_TDMPD2S_LEN 4 +#define TFA98XX_TDM_CONFIG9_TDMPD2S_MAX 15 +#define TFA98XX_TDM_CONFIG9_TDMPD2S_MSK 0xf0 + + +/* + * (0x31)-pdm_config0 + */ + +/* + * pdm_mode + */ +#define TFA98XX_PDM_CONFIG0_PDMSM (0x1<<0) +#define TFA98XX_PDM_CONFIG0_PDMSM_POS 0 +#define TFA98XX_PDM_CONFIG0_PDMSM_LEN 1 +#define TFA98XX_PDM_CONFIG0_PDMSM_MAX 1 +#define TFA98XX_PDM_CONFIG0_PDMSM_MSK 0x1 + +/* + * pdm_side_tone_sel + */ +#define TFA98XX_PDM_CONFIG0_PDMSTSEL (0x3<<1) +#define TFA98XX_PDM_CONFIG0_PDMSTSEL_POS 1 +#define TFA98XX_PDM_CONFIG0_PDMSTSEL_LEN 2 +#define TFA98XX_PDM_CONFIG0_PDMSTSEL_MAX 3 +#define TFA98XX_PDM_CONFIG0_PDMSTSEL_MSK 0x6 + +/* + * pdm_left_sel + */ +#define TFA98XX_PDM_CONFIG0_PDMLSEL (0x1<<3) +#define TFA98XX_PDM_CONFIG0_PDMLSEL_POS 3 +#define TFA98XX_PDM_CONFIG0_PDMLSEL_LEN 1 +#define TFA98XX_PDM_CONFIG0_PDMLSEL_MAX 1 +#define TFA98XX_PDM_CONFIG0_PDMLSEL_MSK 0x8 + +/* + * pdm_right_sel + */ +#define TFA98XX_PDM_CONFIG0_PDMRSEL (0x1<<4) +#define TFA98XX_PDM_CONFIG0_PDMRSEL_POS 4 +#define TFA98XX_PDM_CONFIG0_PDMRSEL_LEN 1 +#define TFA98XX_PDM_CONFIG0_PDMRSEL_MAX 1 +#define TFA98XX_PDM_CONFIG0_PDMRSEL_MSK 0x10 + +/* + * enbl_micvdd + */ +#define TFA98XX_PDM_CONFIG0_MICVDDE (0x1<<5) +#define TFA98XX_PDM_CONFIG0_MICVDDE_POS 5 +#define TFA98XX_PDM_CONFIG0_MICVDDE_LEN 1 +#define TFA98XX_PDM_CONFIG0_MICVDDE_MAX 1 +#define TFA98XX_PDM_CONFIG0_MICVDDE_MSK 0x20 + + +/* + * (0x32)-pdm_config1 + */ + +/* + * pdm_nbck + */ +#define TFA98XX_PDM_CONFIG1_PDMCLRAT (0x3<<0) +#define TFA98XX_PDM_CONFIG1_PDMCLRAT_POS 0 +#define TFA98XX_PDM_CONFIG1_PDMCLRAT_LEN 2 +#define TFA98XX_PDM_CONFIG1_PDMCLRAT_MAX 3 +#define TFA98XX_PDM_CONFIG1_PDMCLRAT_MSK 0x3 + +/* + * pdm_gain + */ +#define TFA98XX_PDM_CONFIG1_PDMGAIN (0xf<<2) +#define TFA98XX_PDM_CONFIG1_PDMGAIN_POS 2 +#define TFA98XX_PDM_CONFIG1_PDMGAIN_LEN 4 +#define TFA98XX_PDM_CONFIG1_PDMGAIN_MAX 15 +#define TFA98XX_PDM_CONFIG1_PDMGAIN_MSK 0x3c + +/* + * sel_pdm_out_data + */ +#define TFA98XX_PDM_CONFIG1_PDMOSEL (0xf<<6) +#define TFA98XX_PDM_CONFIG1_PDMOSEL_POS 6 +#define TFA98XX_PDM_CONFIG1_PDMOSEL_LEN 4 +#define TFA98XX_PDM_CONFIG1_PDMOSEL_MAX 15 +#define TFA98XX_PDM_CONFIG1_PDMOSEL_MSK 0x3c0 + +/* + * sel_cf_haptic_data + */ +#define TFA98XX_PDM_CONFIG1_SELCFHAPD (0x1<<10) +#define TFA98XX_PDM_CONFIG1_SELCFHAPD_POS 10 +#define TFA98XX_PDM_CONFIG1_SELCFHAPD_LEN 1 +#define TFA98XX_PDM_CONFIG1_SELCFHAPD_MAX 1 +#define TFA98XX_PDM_CONFIG1_SELCFHAPD_MSK 0x400 + + +/* + * (0x33)-haptic_driver_config + */ + +/* + * haptic_duration + */ +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPTIME (0xff<<0) +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPTIME_POS 0 +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPTIME_LEN 8 +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPTIME_MAX 255 +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPTIME_MSK 0xff + +/* + * haptic_data + */ +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPLEVEL (0xff<<8) +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPLEVEL_POS 8 +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPLEVEL_LEN 8 +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPLEVEL_MAX 255 +#define TFA98XX_HAPTIC_DRIVER_CONFIG_HAPLEVEL_MSK 0xff00 + + +/* + * (0x34)-gpio_datain_reg + */ + +/* + * gpio_datain + */ +#define TFA98XX_GPIO_DATAIN_REG_GPIODIN (0xf<<0) +#define TFA98XX_GPIO_DATAIN_REG_GPIODIN_POS 0 +#define TFA98XX_GPIO_DATAIN_REG_GPIODIN_LEN 4 +#define TFA98XX_GPIO_DATAIN_REG_GPIODIN_MAX 15 +#define TFA98XX_GPIO_DATAIN_REG_GPIODIN_MSK 0xf + + +/* + * (0x35)-gpio_config + */ + +/* + * gpio_ctrl + */ +#define TFA98XX_GPIO_CONFIG_GPIOCTRL (0x1<<0) +#define TFA98XX_GPIO_CONFIG_GPIOCTRL_POS 0 +#define TFA98XX_GPIO_CONFIG_GPIOCTRL_LEN 1 +#define TFA98XX_GPIO_CONFIG_GPIOCTRL_MAX 1 +#define TFA98XX_GPIO_CONFIG_GPIOCTRL_MSK 0x1 + +/* + * gpio_dir + */ +#define TFA98XX_GPIO_CONFIG_GPIOCONF (0xf<<1) +#define TFA98XX_GPIO_CONFIG_GPIOCONF_POS 1 +#define TFA98XX_GPIO_CONFIG_GPIOCONF_LEN 4 +#define TFA98XX_GPIO_CONFIG_GPIOCONF_MAX 15 +#define TFA98XX_GPIO_CONFIG_GPIOCONF_MSK 0x1e + +/* + * gpio_dataout + */ +#define TFA98XX_GPIO_CONFIG_GPIODOUT (0xf<<5) +#define TFA98XX_GPIO_CONFIG_GPIODOUT_POS 5 +#define TFA98XX_GPIO_CONFIG_GPIODOUT_LEN 4 +#define TFA98XX_GPIO_CONFIG_GPIODOUT_MAX 15 +#define TFA98XX_GPIO_CONFIG_GPIODOUT_MSK 0x1e0 + + +/* + * (0x40)-interrupt_out_reg1 + */ + +/* + * int_out_flag_por + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTVDDS (0x1<<0) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTVDDS_POS 0 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTVDDS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTVDDS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTVDDS_MSK 0x1 + +/* + * int_out_flag_pll_lock + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTPLLS (0x1<<1) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTPLLS_POS 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTPLLS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTPLLS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTPLLS_MSK 0x2 + +/* + * int_out_flag_otpok + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOTDS (0x1<<2) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOTDS_POS 2 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOTDS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOTDS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOTDS_MSK 0x4 + +/* + * int_out_flag_ovpok + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOVDS (0x1<<3) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOVDS_POS 3 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOVDS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOVDS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTOVDS_MSK 0x8 + +/* + * int_out_flag_uvpok + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTUVDS (0x1<<4) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTUVDS_POS 4 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTUVDS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTUVDS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTUVDS_MSK 0x10 + +/* + * int_out_flag_clocks_stable + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTCLKS (0x1<<5) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTCLKS_POS 5 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTCLKS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTCLKS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTCLKS_MSK 0x20 + +/* + * int_out_flag_mtp_busy + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTMTPB (0x1<<6) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTMTPB_POS 6 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTMTPB_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTMTPB_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTMTPB_MSK 0x40 + +/* + * int_out_flag_lost_clk + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTNOCLK (0x1<<7) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTNOCLK_POS 7 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTNOCLK_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTNOCLK_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTNOCLK_MSK 0x80 + +/* + * int_out_flag_cf_speakererror + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSPKS (0x1<<8) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSPKS_POS 8 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSPKS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSPKS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSPKS_MSK 0x100 + +/* + * int_out_flag_cold_started + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTACS (0x1<<9) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTACS_POS 9 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTACS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTACS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTACS_MSK 0x200 + +/* + * int_out_flag_engage + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSWS (0x1<<10) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSWS_POS 10 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSWS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSWS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTSWS_MSK 0x400 + +/* + * int_out_flag_watchdog_reset + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTWDS (0x1<<11) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTWDS_POS 11 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTWDS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTWDS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTWDS_MSK 0x800 + +/* + * int_out_flag_enbl_amp + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAMPS (0x1<<12) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAMPS_POS 12 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAMPS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAMPS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAMPS_MSK 0x1000 + +/* + * int_out_flag_enbl_ref + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAREFS (0x1<<13) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAREFS_POS 13 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAREFS_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAREFS_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTAREFS_MSK 0x2000 + +/* + * int_out_flag_adc10_ready + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTADCCR (0x1<<14) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTADCCR_POS 14 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTADCCR_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTADCCR_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTADCCR_MSK 0x4000 + +/* + * int_out_flag_bod_vddd_nok + */ +#define TFA98XX_INTERRUPT_OUT_REG1_ISTBODNOK (0x1<<15) +#define TFA98XX_INTERRUPT_OUT_REG1_ISTBODNOK_POS 15 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTBODNOK_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTBODNOK_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG1_ISTBODNOK_MSK 0x8000 + + +/* + * (0x41)-interrupt_out_reg2 + */ + +/* + * int_out_flag_bst_bstcur + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTCU (0x1<<0) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTCU_POS 0 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTCU_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTCU_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTCU_MSK 0x1 + +/* + * int_out_flag_bst_hiz + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTHI (0x1<<1) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTHI_POS 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTHI_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTHI_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTHI_MSK 0x2 + +/* + * int_out_flag_bst_ocpok + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTOC (0x1<<2) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTOC_POS 2 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTOC_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTOC_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTOC_MSK 0x4 + +/* + * int_out_flag_bst_peakcur + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTPKCUR (0x1<<3) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTPKCUR_POS 3 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTPKCUR_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTPKCUR_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTPKCUR_MSK 0x8 + +/* + * int_out_flag_bst_voutcomp + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTVC (0x1<<4) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTVC_POS 4 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTVC_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTVC_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBSTVC_MSK 0x10 + +/* + * int_out_flag_bst_voutcomp86 + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST86 (0x1<<5) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST86_POS 5 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST86_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST86_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST86_MSK 0x20 + +/* + * int_out_flag_bst_voutcomp93 + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST93 (0x1<<6) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST93_POS 6 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST93_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST93_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTBST93_MSK 0x40 + +/* + * int_out_flag_rcvldop_ready + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTRCVLD (0x1<<7) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTRCVLD_POS 7 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTRCVLD_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTRCVLD_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTRCVLD_MSK 0x80 + +/* + * int_out_flag_ocp_alarm_left + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPL (0x1<<8) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPL_POS 8 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPL_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPL_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPL_MSK 0x100 + +/* + * int_out_flag_ocp_alarm_right + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPR (0x1<<9) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPR_POS 9 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPR_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPR_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTOCPR_MSK 0x200 + +/* + * int_out_flag_man_wait_src_settings + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSRC (0x1<<10) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSRC_POS 10 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSRC_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSRC_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSRC_MSK 0x400 + +/* + * int_out_flag_man_wait_cf_config + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWCFC (0x1<<11) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWCFC_POS 11 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWCFC_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWCFC_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWCFC_MSK 0x800 + +/* + * int_out_flag_man_start_mute_audio + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSMU (0x1<<12) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSMU_POS 12 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSMU_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSMU_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTMWSMU_MSK 0x1000 + +/* + * int_out_flag_cfma_err + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMER (0x1<<13) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMER_POS 13 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMER_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMER_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMER_MSK 0x2000 + +/* + * int_out_flag_cfma_ack + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMAC (0x1<<14) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMAC_POS 14 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMAC_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMAC_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCFMAC_MSK 0x4000 + +/* + * int_out_flag_clk_out_of_range + */ +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCLKOOR (0x1<<15) +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCLKOOR_POS 15 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCLKOOR_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCLKOOR_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG2_ISTCLKOOR_MSK 0x8000 + + +/* + * (0x42)-interrupt_out_reg3 + */ + +/* + * int_out_flag_tdm_error + */ +#define TFA98XX_INTERRUPT_OUT_REG3_ISTTDMER (0x1<<0) +#define TFA98XX_INTERRUPT_OUT_REG3_ISTTDMER_POS 0 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTTDMER_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTTDMER_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTTDMER_MSK 0x1 + +/* + * int_out_flag_clip_left + */ +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPL (0x1<<1) +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPL_POS 1 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPL_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPL_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPL_MSK 0x2 + +/* + * int_out_flag_clip_right + */ +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPR (0x1<<2) +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPR_POS 2 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPR_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPR_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTCLPR_MSK 0x4 + +/* + * int_out_flag_mic_ocpok + */ +#define TFA98XX_INTERRUPT_OUT_REG3_ISTOCPM (0x1<<3) +#define TFA98XX_INTERRUPT_OUT_REG3_ISTOCPM_POS 3 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTOCPM_LEN 1 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTOCPM_MAX 1 +#define TFA98XX_INTERRUPT_OUT_REG3_ISTOCPM_MSK 0x8 + + +/* + * (0x44)-interrupt_in_reg1 + */ + +/* + * int_in_flag_por + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLVDDS (0x1<<0) +#define TFA98XX_INTERRUPT_IN_REG1_ICLVDDS_POS 0 +#define TFA98XX_INTERRUPT_IN_REG1_ICLVDDS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLVDDS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLVDDS_MSK 0x1 + +/* + * int_in_flag_pll_lock + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLPLLS (0x1<<1) +#define TFA98XX_INTERRUPT_IN_REG1_ICLPLLS_POS 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLPLLS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLPLLS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLPLLS_MSK 0x2 + +/* + * int_in_flag_otpok + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLOTDS (0x1<<2) +#define TFA98XX_INTERRUPT_IN_REG1_ICLOTDS_POS 2 +#define TFA98XX_INTERRUPT_IN_REG1_ICLOTDS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLOTDS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLOTDS_MSK 0x4 + +/* + * int_in_flag_ovpok + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLOVDS (0x1<<3) +#define TFA98XX_INTERRUPT_IN_REG1_ICLOVDS_POS 3 +#define TFA98XX_INTERRUPT_IN_REG1_ICLOVDS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLOVDS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLOVDS_MSK 0x8 + +/* + * int_in_flag_uvpok + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLUVDS (0x1<<4) +#define TFA98XX_INTERRUPT_IN_REG1_ICLUVDS_POS 4 +#define TFA98XX_INTERRUPT_IN_REG1_ICLUVDS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLUVDS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLUVDS_MSK 0x10 + +/* + * int_in_flag_clocks_stable + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLCLKS (0x1<<5) +#define TFA98XX_INTERRUPT_IN_REG1_ICLCLKS_POS 5 +#define TFA98XX_INTERRUPT_IN_REG1_ICLCLKS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLCLKS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLCLKS_MSK 0x20 + +/* + * int_in_flag_mtp_busy + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLMTPB (0x1<<6) +#define TFA98XX_INTERRUPT_IN_REG1_ICLMTPB_POS 6 +#define TFA98XX_INTERRUPT_IN_REG1_ICLMTPB_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLMTPB_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLMTPB_MSK 0x40 + +/* + * int_in_flag_lost_clk + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLNOCLK (0x1<<7) +#define TFA98XX_INTERRUPT_IN_REG1_ICLNOCLK_POS 7 +#define TFA98XX_INTERRUPT_IN_REG1_ICLNOCLK_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLNOCLK_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLNOCLK_MSK 0x80 + +/* + * int_in_flag_cf_speakererror + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLSPKS (0x1<<8) +#define TFA98XX_INTERRUPT_IN_REG1_ICLSPKS_POS 8 +#define TFA98XX_INTERRUPT_IN_REG1_ICLSPKS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLSPKS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLSPKS_MSK 0x100 + +/* + * int_in_flag_cold_started + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLACS (0x1<<9) +#define TFA98XX_INTERRUPT_IN_REG1_ICLACS_POS 9 +#define TFA98XX_INTERRUPT_IN_REG1_ICLACS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLACS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLACS_MSK 0x200 + +/* + * int_in_flag_engage + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLSWS (0x1<<10) +#define TFA98XX_INTERRUPT_IN_REG1_ICLSWS_POS 10 +#define TFA98XX_INTERRUPT_IN_REG1_ICLSWS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLSWS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLSWS_MSK 0x400 + +/* + * int_in_flag_watchdog_reset + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLWDS (0x1<<11) +#define TFA98XX_INTERRUPT_IN_REG1_ICLWDS_POS 11 +#define TFA98XX_INTERRUPT_IN_REG1_ICLWDS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLWDS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLWDS_MSK 0x800 + +/* + * int_in_flag_enbl_amp + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLAMPS (0x1<<12) +#define TFA98XX_INTERRUPT_IN_REG1_ICLAMPS_POS 12 +#define TFA98XX_INTERRUPT_IN_REG1_ICLAMPS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLAMPS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLAMPS_MSK 0x1000 + +/* + * int_in_flag_enbl_ref + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLAREFS (0x1<<13) +#define TFA98XX_INTERRUPT_IN_REG1_ICLAREFS_POS 13 +#define TFA98XX_INTERRUPT_IN_REG1_ICLAREFS_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLAREFS_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLAREFS_MSK 0x2000 + +/* + * int_in_flag_adc10_ready + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLADCCR (0x1<<14) +#define TFA98XX_INTERRUPT_IN_REG1_ICLADCCR_POS 14 +#define TFA98XX_INTERRUPT_IN_REG1_ICLADCCR_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLADCCR_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLADCCR_MSK 0x4000 + +/* + * int_in_flag_bod_vddd_nok + */ +#define TFA98XX_INTERRUPT_IN_REG1_ICLBODNOK (0x1<<15) +#define TFA98XX_INTERRUPT_IN_REG1_ICLBODNOK_POS 15 +#define TFA98XX_INTERRUPT_IN_REG1_ICLBODNOK_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLBODNOK_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG1_ICLBODNOK_MSK 0x8000 + + +/* + * (0x45)-interrupt_in_reg2 + */ + +/* + * int_in_flag_bst_bstcur + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTCU (0x1<<0) +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTCU_POS 0 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTCU_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTCU_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTCU_MSK 0x1 + +/* + * int_in_flag_bst_hiz + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTHI (0x1<<1) +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTHI_POS 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTHI_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTHI_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTHI_MSK 0x2 + +/* + * int_in_flag_bst_ocpok + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTOC (0x1<<2) +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTOC_POS 2 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTOC_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTOC_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTOC_MSK 0x4 + +/* + * int_in_flag_bst_peakcur + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTPC (0x1<<3) +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTPC_POS 3 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTPC_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTPC_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTPC_MSK 0x8 + +/* + * int_in_flag_bst_voutcomp + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTVC (0x1<<4) +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTVC_POS 4 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTVC_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTVC_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBSTVC_MSK 0x10 + +/* + * int_in_flag_bst_voutcomp86 + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST86 (0x1<<5) +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST86_POS 5 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST86_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST86_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST86_MSK 0x20 + +/* + * int_in_flag_bst_voutcomp93 + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST93 (0x1<<6) +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST93_POS 6 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST93_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST93_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLBST93_MSK 0x40 + +/* + * int_in_flag_rcvldop_ready + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLRCVLD (0x1<<7) +#define TFA98XX_INTERRUPT_IN_REG2_ICLRCVLD_POS 7 +#define TFA98XX_INTERRUPT_IN_REG2_ICLRCVLD_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLRCVLD_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLRCVLD_MSK 0x80 + +/* + * int_in_flag_ocp_alarm_left + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPL (0x1<<8) +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPL_POS 8 +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPL_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPL_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPL_MSK 0x100 + +/* + * int_in_flag_ocp_alarm_right + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPR (0x1<<9) +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPR_POS 9 +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPR_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPR_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLOCPR_MSK 0x200 + +/* + * int_in_flag_man_wait_src_settings + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSRC (0x1<<10) +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSRC_POS 10 +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSRC_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSRC_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSRC_MSK 0x400 + +/* + * int_in_flag_man_wait_cf_config + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWCFC (0x1<<11) +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWCFC_POS 11 +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWCFC_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWCFC_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWCFC_MSK 0x800 + +/* + * int_in_flag_man_start_mute_audio + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSMU (0x1<<12) +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSMU_POS 12 +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSMU_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSMU_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLMWSMU_MSK 0x1000 + +/* + * int_in_flag_cfma_err + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMER (0x1<<13) +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMER_POS 13 +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMER_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMER_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMER_MSK 0x2000 + +/* + * int_in_flag_cfma_ack + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMAC (0x1<<14) +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMAC_POS 14 +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMAC_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMAC_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLCFMAC_MSK 0x4000 + +/* + * int_in_flag_clk_out_of_range + */ +#define TFA98XX_INTERRUPT_IN_REG2_ICLCLKOOR (0x1<<15) +#define TFA98XX_INTERRUPT_IN_REG2_ICLCLKOOR_POS 15 +#define TFA98XX_INTERRUPT_IN_REG2_ICLCLKOOR_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLCLKOOR_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG2_ICLCLKOOR_MSK 0x8000 + + +/* + * (0x46)-interrupt_in_reg3 + */ + +/* + * int_in_flag_tdm_error + */ +#define TFA98XX_INTERRUPT_IN_REG3_ICLTDMER (0x1<<0) +#define TFA98XX_INTERRUPT_IN_REG3_ICLTDMER_POS 0 +#define TFA98XX_INTERRUPT_IN_REG3_ICLTDMER_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG3_ICLTDMER_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG3_ICLTDMER_MSK 0x1 + +/* + * int_in_flag_clip_left + */ +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPL (0x1<<1) +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPL_POS 1 +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPL_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPL_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPL_MSK 0x2 + +/* + * int_in_flag_clip_right + */ +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPR (0x1<<2) +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPR_POS 2 +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPR_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPR_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG3_ICLCLPR_MSK 0x4 + +/* + * int_in_flag_mic_ocpok + */ +#define TFA98XX_INTERRUPT_IN_REG3_ICLOCPM (0x1<<3) +#define TFA98XX_INTERRUPT_IN_REG3_ICLOCPM_POS 3 +#define TFA98XX_INTERRUPT_IN_REG3_ICLOCPM_LEN 1 +#define TFA98XX_INTERRUPT_IN_REG3_ICLOCPM_MAX 1 +#define TFA98XX_INTERRUPT_IN_REG3_ICLOCPM_MSK 0x8 + + +/* + * (0x48)-interrupt_enable_reg1 + */ + +/* + * int_enable_flag_por + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEVDDS (0x1<<0) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEVDDS_POS 0 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEVDDS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEVDDS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEVDDS_MSK 0x1 + +/* + * int_enable_flag_pll_lock + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEPLLS (0x1<<1) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEPLLS_POS 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEPLLS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEPLLS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEPLLS_MSK 0x2 + +/* + * int_enable_flag_otpok + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOTDS (0x1<<2) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOTDS_POS 2 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOTDS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOTDS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOTDS_MSK 0x4 + +/* + * int_enable_flag_ovpok + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOVDS (0x1<<3) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOVDS_POS 3 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOVDS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOVDS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEOVDS_MSK 0x8 + +/* + * int_enable_flag_uvpok + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEUVDS (0x1<<4) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEUVDS_POS 4 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEUVDS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEUVDS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEUVDS_MSK 0x10 + +/* + * int_enable_flag_clocks_stable + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IECLKS (0x1<<5) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IECLKS_POS 5 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IECLKS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IECLKS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IECLKS_MSK 0x20 + +/* + * int_enable_flag_mtp_busy + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEMTPB (0x1<<6) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEMTPB_POS 6 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEMTPB_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEMTPB_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEMTPB_MSK 0x40 + +/* + * int_enable_flag_lost_clk + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IENOCLK (0x1<<7) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IENOCLK_POS 7 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IENOCLK_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IENOCLK_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IENOCLK_MSK 0x80 + +/* + * int_enable_flag_cf_speakererror + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESPKS (0x1<<8) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESPKS_POS 8 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESPKS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESPKS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESPKS_MSK 0x100 + +/* + * int_enable_flag_cold_started + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEACS (0x1<<9) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEACS_POS 9 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEACS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEACS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEACS_MSK 0x200 + +/* + * int_enable_flag_engage + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESWS (0x1<<10) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESWS_POS 10 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESWS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESWS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IESWS_MSK 0x400 + +/* + * int_enable_flag_watchdog_reset + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEWDS (0x1<<11) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEWDS_POS 11 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEWDS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEWDS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEWDS_MSK 0x800 + +/* + * int_enable_flag_enbl_amp + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAMPS (0x1<<12) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAMPS_POS 12 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAMPS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAMPS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAMPS_MSK 0x1000 + +/* + * int_enable_flag_enbl_ref + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAREFS (0x1<<13) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAREFS_POS 13 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAREFS_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAREFS_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEAREFS_MSK 0x2000 + +/* + * int_enable_flag_adc10_ready + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEADCCR (0x1<<14) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEADCCR_POS 14 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEADCCR_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEADCCR_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEADCCR_MSK 0x4000 + +/* + * int_enable_flag_bod_vddd_nok + */ +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEBODNOK (0x1<<15) +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEBODNOK_POS 15 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEBODNOK_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEBODNOK_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG1_IEBODNOK_MSK 0x8000 + + +/* + * (0x49)-interrupt_enable_reg2 + */ + +/* + * int_enable_flag_bst_bstcur + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTCU (0x1<<0) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTCU_POS 0 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTCU_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTCU_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTCU_MSK 0x1 + +/* + * int_enable_flag_bst_hiz + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTHI (0x1<<1) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTHI_POS 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTHI_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTHI_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTHI_MSK 0x2 + +/* + * int_enable_flag_bst_ocpok + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTOC (0x1<<2) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTOC_POS 2 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTOC_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTOC_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTOC_MSK 0x4 + +/* + * int_enable_flag_bst_peakcur + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTPC (0x1<<3) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTPC_POS 3 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTPC_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTPC_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTPC_MSK 0x8 + +/* + * int_enable_flag_bst_voutcomp + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTVC (0x1<<4) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTVC_POS 4 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTVC_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTVC_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBSTVC_MSK 0x10 + +/* + * int_enable_flag_bst_voutcomp86 + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST86 (0x1<<5) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST86_POS 5 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST86_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST86_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST86_MSK 0x20 + +/* + * int_enable_flag_bst_voutcomp93 + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST93 (0x1<<6) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST93_POS 6 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST93_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST93_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEBST93_MSK 0x40 + +/* + * int_enable_flag_rcvldop_ready + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IERCVLD (0x1<<7) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IERCVLD_POS 7 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IERCVLD_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IERCVLD_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IERCVLD_MSK 0x80 + +/* + * int_enable_flag_ocp_alarm_left + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPL (0x1<<8) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPL_POS 8 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPL_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPL_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPL_MSK 0x100 + +/* + * int_enable_flag_ocp_alarm_right + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPR (0x1<<9) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPR_POS 9 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPR_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPR_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEOCPR_MSK 0x200 + +/* + * int_enable_flag_man_wait_src_settings + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSRC (0x1<<10) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSRC_POS 10 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSRC_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSRC_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSRC_MSK 0x400 + +/* + * int_enable_flag_man_wait_cf_config + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWCFC (0x1<<11) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWCFC_POS 11 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWCFC_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWCFC_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWCFC_MSK 0x800 + +/* + * int_enable_flag_man_start_mute_audio + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSMU (0x1<<12) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSMU_POS 12 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSMU_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSMU_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IEMWSMU_MSK 0x1000 + +/* + * int_enable_flag_cfma_err + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMER (0x1<<13) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMER_POS 13 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMER_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMER_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMER_MSK 0x2000 + +/* + * int_enable_flag_cfma_ack + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMAC (0x1<<14) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMAC_POS 14 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMAC_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMAC_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECFMAC_MSK 0x4000 + +/* + * int_enable_flag_clk_out_of_range + */ +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECLKOOR (0x1<<15) +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECLKOOR_POS 15 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECLKOOR_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECLKOOR_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG2_IECLKOOR_MSK 0x8000 + + +/* + * (0x4a)-interrupt_enable_reg3 + */ + +/* + * int_enable_flag_tdm_error + */ +#define TFA98XX_INTERRUPT_ENABLE_REG3_IETDMER (0x1<<0) +#define TFA98XX_INTERRUPT_ENABLE_REG3_IETDMER_POS 0 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IETDMER_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IETDMER_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IETDMER_MSK 0x1 + +/* + * int_enable_flag_clip_left + */ +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPL (0x1<<1) +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPL_POS 1 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPL_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPL_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPL_MSK 0x2 + +/* + * int_enable_flag_clip_right + */ +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPR (0x1<<2) +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPR_POS 2 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPR_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPR_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IECLPR_MSK 0x4 + +/* + * int_enable_flag_mic_ocpok + */ +#define TFA98XX_INTERRUPT_ENABLE_REG3_IEOCPM1 (0x1<<3) +#define TFA98XX_INTERRUPT_ENABLE_REG3_IEOCPM1_POS 3 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IEOCPM1_LEN 1 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IEOCPM1_MAX 1 +#define TFA98XX_INTERRUPT_ENABLE_REG3_IEOCPM1_MSK 0x8 + + +/* + * (0x4c)-status_polarity_reg1 + */ + +/* + * int_polarity_flag_por + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOVDDS (0x1<<0) +#define TFA98XX_STATUS_POLARITY_REG1_IPOVDDS_POS 0 +#define TFA98XX_STATUS_POLARITY_REG1_IPOVDDS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOVDDS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOVDDS_MSK 0x1 + +/* + * int_polarity_flag_pll_lock + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOPLLS (0x1<<1) +#define TFA98XX_STATUS_POLARITY_REG1_IPOPLLS_POS 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOPLLS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOPLLS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOPLLS_MSK 0x2 + +/* + * int_polarity_flag_otpok + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOOTDS (0x1<<2) +#define TFA98XX_STATUS_POLARITY_REG1_IPOOTDS_POS 2 +#define TFA98XX_STATUS_POLARITY_REG1_IPOOTDS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOOTDS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOOTDS_MSK 0x4 + +/* + * int_polarity_flag_ovpok + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOOVDS (0x1<<3) +#define TFA98XX_STATUS_POLARITY_REG1_IPOOVDS_POS 3 +#define TFA98XX_STATUS_POLARITY_REG1_IPOOVDS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOOVDS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOOVDS_MSK 0x8 + +/* + * int_polarity_flag_uvpok + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOUVDS (0x1<<4) +#define TFA98XX_STATUS_POLARITY_REG1_IPOUVDS_POS 4 +#define TFA98XX_STATUS_POLARITY_REG1_IPOUVDS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOUVDS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOUVDS_MSK 0x10 + +/* + * int_polarity_flag_clocks_stable + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOCLKS (0x1<<5) +#define TFA98XX_STATUS_POLARITY_REG1_IPOCLKS_POS 5 +#define TFA98XX_STATUS_POLARITY_REG1_IPOCLKS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOCLKS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOCLKS_MSK 0x20 + +/* + * int_polarity_flag_mtp_busy + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOMTPB (0x1<<6) +#define TFA98XX_STATUS_POLARITY_REG1_IPOMTPB_POS 6 +#define TFA98XX_STATUS_POLARITY_REG1_IPOMTPB_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOMTPB_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOMTPB_MSK 0x40 + +/* + * int_polarity_flag_lost_clk + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPONOCLK (0x1<<7) +#define TFA98XX_STATUS_POLARITY_REG1_IPONOCLK_POS 7 +#define TFA98XX_STATUS_POLARITY_REG1_IPONOCLK_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPONOCLK_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPONOCLK_MSK 0x80 + +/* + * int_polarity_flag_cf_speakererror + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOSPKS (0x1<<8) +#define TFA98XX_STATUS_POLARITY_REG1_IPOSPKS_POS 8 +#define TFA98XX_STATUS_POLARITY_REG1_IPOSPKS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOSPKS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOSPKS_MSK 0x100 + +/* + * int_polarity_flag_cold_started + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOACS (0x1<<9) +#define TFA98XX_STATUS_POLARITY_REG1_IPOACS_POS 9 +#define TFA98XX_STATUS_POLARITY_REG1_IPOACS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOACS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOACS_MSK 0x200 + +/* + * int_polarity_flag_engage + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOSWS (0x1<<10) +#define TFA98XX_STATUS_POLARITY_REG1_IPOSWS_POS 10 +#define TFA98XX_STATUS_POLARITY_REG1_IPOSWS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOSWS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOSWS_MSK 0x400 + +/* + * int_polarity_flag_watchdog_reset + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOWDS (0x1<<11) +#define TFA98XX_STATUS_POLARITY_REG1_IPOWDS_POS 11 +#define TFA98XX_STATUS_POLARITY_REG1_IPOWDS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOWDS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOWDS_MSK 0x800 + +/* + * int_polarity_flag_enbl_amp + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOAMPS (0x1<<12) +#define TFA98XX_STATUS_POLARITY_REG1_IPOAMPS_POS 12 +#define TFA98XX_STATUS_POLARITY_REG1_IPOAMPS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOAMPS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOAMPS_MSK 0x1000 + +/* + * int_polarity_flag_enbl_ref + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOAREFS (0x1<<13) +#define TFA98XX_STATUS_POLARITY_REG1_IPOAREFS_POS 13 +#define TFA98XX_STATUS_POLARITY_REG1_IPOAREFS_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOAREFS_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOAREFS_MSK 0x2000 + +/* + * int_polarity_flag_adc10_ready + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOADCCR (0x1<<14) +#define TFA98XX_STATUS_POLARITY_REG1_IPOADCCR_POS 14 +#define TFA98XX_STATUS_POLARITY_REG1_IPOADCCR_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOADCCR_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOADCCR_MSK 0x4000 + +/* + * int_polarity_flag_bod_vddd_nok + */ +#define TFA98XX_STATUS_POLARITY_REG1_IPOBODNOK (0x1<<15) +#define TFA98XX_STATUS_POLARITY_REG1_IPOBODNOK_POS 15 +#define TFA98XX_STATUS_POLARITY_REG1_IPOBODNOK_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOBODNOK_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG1_IPOBODNOK_MSK 0x8000 + + +/* + * (0x4d)-status_polarity_reg2 + */ + +/* + * int_polarity_flag_bst_bstcur + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTCU (0x1<<0) +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTCU_POS 0 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTCU_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTCU_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTCU_MSK 0x1 + +/* + * int_polarity_flag_bst_hiz + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTHI (0x1<<1) +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTHI_POS 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTHI_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTHI_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTHI_MSK 0x2 + +/* + * int_polarity_flag_bst_ocpok + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTOC (0x1<<2) +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTOC_POS 2 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTOC_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTOC_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTOC_MSK 0x4 + +/* + * int_polarity_flag_bst_peakcur + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTPC (0x1<<3) +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTPC_POS 3 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTPC_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTPC_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTPC_MSK 0x8 + +/* + * int_polarity_flag_bst_voutcomp + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTVC (0x1<<4) +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTVC_POS 4 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTVC_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTVC_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBSTVC_MSK 0x10 + +/* + * int_polarity_flag_bst_voutcomp86 + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST86 (0x1<<5) +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST86_POS 5 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST86_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST86_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST86_MSK 0x20 + +/* + * int_polarity_flag_bst_voutcomp93 + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST93 (0x1<<6) +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST93_POS 6 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST93_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST93_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOBST93_MSK 0x40 + +/* + * int_polarity_flag_rcvldop_ready + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPORCVLD (0x1<<7) +#define TFA98XX_STATUS_POLARITY_REG2_IPORCVLD_POS 7 +#define TFA98XX_STATUS_POLARITY_REG2_IPORCVLD_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPORCVLD_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPORCVLD_MSK 0x80 + +/* + * int_polarity_flag_ocp_alarm_left + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPL (0x1<<8) +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPL_POS 8 +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPL_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPL_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPL_MSK 0x100 + +/* + * int_polarity_flag_ocp_alarm_right + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPR (0x1<<9) +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPR_POS 9 +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPR_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPR_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOOCPR_MSK 0x200 + +/* + * int_polarity_flag_man_wait_src_settings + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSRC (0x1<<10) +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSRC_POS 10 +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSRC_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSRC_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSRC_MSK 0x400 + +/* + * int_polarity_flag_man_wait_cf_config + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWCFC (0x1<<11) +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWCFC_POS 11 +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWCFC_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWCFC_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWCFC_MSK 0x800 + +/* + * int_polarity_flag_man_start_mute_audio + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSMU (0x1<<12) +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSMU_POS 12 +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSMU_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSMU_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOMWSMU_MSK 0x1000 + +/* + * int_polarity_flag_cfma_err + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMER (0x1<<13) +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMER_POS 13 +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMER_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMER_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMER_MSK 0x2000 + +/* + * int_polarity_flag_cfma_ack + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMAC (0x1<<14) +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMAC_POS 14 +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMAC_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMAC_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPOCFMAC_MSK 0x4000 + +/* + * int_polarity_flag_clk_out_of_range + */ +#define TFA98XX_STATUS_POLARITY_REG2_IPCLKOOR (0x1<<15) +#define TFA98XX_STATUS_POLARITY_REG2_IPCLKOOR_POS 15 +#define TFA98XX_STATUS_POLARITY_REG2_IPCLKOOR_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPCLKOOR_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG2_IPCLKOOR_MSK 0x8000 + + +/* + * (0x4e)-status_polarity_reg3 + */ + +/* + * int_polarity_flag_tdm_error + */ +#define TFA98XX_STATUS_POLARITY_REG3_IPOTDMER (0x1<<0) +#define TFA98XX_STATUS_POLARITY_REG3_IPOTDMER_POS 0 +#define TFA98XX_STATUS_POLARITY_REG3_IPOTDMER_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG3_IPOTDMER_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG3_IPOTDMER_MSK 0x1 + +/* + * int_polarity_flag_clip_left + */ +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPL (0x1<<1) +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPL_POS 1 +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPL_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPL_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPL_MSK 0x2 + +/* + * int_polarity_flag_clip_right + */ +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPR (0x1<<2) +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPR_POS 2 +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPR_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPR_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG3_IPOCLPR_MSK 0x4 + +/* + * int_polarity_flag_mic_ocpok + */ +#define TFA98XX_STATUS_POLARITY_REG3_IPOOCPM (0x1<<3) +#define TFA98XX_STATUS_POLARITY_REG3_IPOOCPM_POS 3 +#define TFA98XX_STATUS_POLARITY_REG3_IPOOCPM_LEN 1 +#define TFA98XX_STATUS_POLARITY_REG3_IPOOCPM_MAX 1 +#define TFA98XX_STATUS_POLARITY_REG3_IPOOCPM_MSK 0x8 + + +/* + * (0x50)-bat_prot_config + */ + +/* + * vbat_prot_attack_time + */ +#define TFA98XX_BAT_PROT_CONFIG_BSSCR (0x3<<0) +#define TFA98XX_BAT_PROT_CONFIG_BSSCR_POS 0 +#define TFA98XX_BAT_PROT_CONFIG_BSSCR_LEN 2 +#define TFA98XX_BAT_PROT_CONFIG_BSSCR_MAX 3 +#define TFA98XX_BAT_PROT_CONFIG_BSSCR_MSK 0x3 + +/* + * vbat_prot_thlevel + */ +#define TFA98XX_BAT_PROT_CONFIG_BSST (0xf<<2) +#define TFA98XX_BAT_PROT_CONFIG_BSST_POS 2 +#define TFA98XX_BAT_PROT_CONFIG_BSST_LEN 4 +#define TFA98XX_BAT_PROT_CONFIG_BSST_MAX 15 +#define TFA98XX_BAT_PROT_CONFIG_BSST_MSK 0x3c + +/* + * vbat_prot_max_reduct + */ +#define TFA98XX_BAT_PROT_CONFIG_BSSRL (0x3<<6) +#define TFA98XX_BAT_PROT_CONFIG_BSSRL_POS 6 +#define TFA98XX_BAT_PROT_CONFIG_BSSRL_LEN 2 +#define TFA98XX_BAT_PROT_CONFIG_BSSRL_MAX 3 +#define TFA98XX_BAT_PROT_CONFIG_BSSRL_MSK 0xc0 + +/* + * vbat_prot_release_time + */ +#define TFA98XX_BAT_PROT_CONFIG_BSSRR (0x7<<8) +#define TFA98XX_BAT_PROT_CONFIG_BSSRR_POS 8 +#define TFA98XX_BAT_PROT_CONFIG_BSSRR_LEN 3 +#define TFA98XX_BAT_PROT_CONFIG_BSSRR_MAX 7 +#define TFA98XX_BAT_PROT_CONFIG_BSSRR_MSK 0x700 + +/* + * vbat_prot_hysterese + */ +#define TFA98XX_BAT_PROT_CONFIG_BSSHY (0x3<<11) +#define TFA98XX_BAT_PROT_CONFIG_BSSHY_POS 11 +#define TFA98XX_BAT_PROT_CONFIG_BSSHY_LEN 2 +#define TFA98XX_BAT_PROT_CONFIG_BSSHY_MAX 3 +#define TFA98XX_BAT_PROT_CONFIG_BSSHY_MSK 0x1800 + +/* + * sel_vbat + */ +#define TFA98XX_BAT_PROT_CONFIG_BSSR (0x1<<14) +#define TFA98XX_BAT_PROT_CONFIG_BSSR_POS 14 +#define TFA98XX_BAT_PROT_CONFIG_BSSR_LEN 1 +#define TFA98XX_BAT_PROT_CONFIG_BSSR_MAX 1 +#define TFA98XX_BAT_PROT_CONFIG_BSSR_MSK 0x4000 + +/* + * bypass_clipper + */ +#define TFA98XX_BAT_PROT_CONFIG_BSSBY (0x1<<15) +#define TFA98XX_BAT_PROT_CONFIG_BSSBY_POS 15 +#define TFA98XX_BAT_PROT_CONFIG_BSSBY_LEN 1 +#define TFA98XX_BAT_PROT_CONFIG_BSSBY_MAX 1 +#define TFA98XX_BAT_PROT_CONFIG_BSSBY_MSK 0x8000 + + +/* + * (0x51)-audio_control + */ + +/* + * batsense_steepness + */ +#define TFA98XX_AUDIO_CONTROL_BSSS (0x1<<0) +#define TFA98XX_AUDIO_CONTROL_BSSS_POS 0 +#define TFA98XX_AUDIO_CONTROL_BSSS_LEN 1 +#define TFA98XX_AUDIO_CONTROL_BSSS_MAX 1 +#define TFA98XX_AUDIO_CONTROL_BSSS_MSK 0x1 + +/* + * soft_mute + */ +#define TFA98XX_AUDIO_CONTROL_INTSMUTE (0x1<<1) +#define TFA98XX_AUDIO_CONTROL_INTSMUTE_POS 1 +#define TFA98XX_AUDIO_CONTROL_INTSMUTE_LEN 1 +#define TFA98XX_AUDIO_CONTROL_INTSMUTE_MAX 1 +#define TFA98XX_AUDIO_CONTROL_INTSMUTE_MSK 0x2 + +/* + * cf_mute_left + */ +#define TFA98XX_AUDIO_CONTROL_CFSML (0x1<<2) +#define TFA98XX_AUDIO_CONTROL_CFSML_POS 2 +#define TFA98XX_AUDIO_CONTROL_CFSML_LEN 1 +#define TFA98XX_AUDIO_CONTROL_CFSML_MAX 1 +#define TFA98XX_AUDIO_CONTROL_CFSML_MSK 0x4 + +/* + * cf_mute_right + */ +#define TFA98XX_AUDIO_CONTROL_CFSMR (0x1<<3) +#define TFA98XX_AUDIO_CONTROL_CFSMR_POS 3 +#define TFA98XX_AUDIO_CONTROL_CFSMR_LEN 1 +#define TFA98XX_AUDIO_CONTROL_CFSMR_MAX 1 +#define TFA98XX_AUDIO_CONTROL_CFSMR_MSK 0x8 + +/* + * bypass_hp_left + */ +#define TFA98XX_AUDIO_CONTROL_HPFBYPL (0x1<<4) +#define TFA98XX_AUDIO_CONTROL_HPFBYPL_POS 4 +#define TFA98XX_AUDIO_CONTROL_HPFBYPL_LEN 1 +#define TFA98XX_AUDIO_CONTROL_HPFBYPL_MAX 1 +#define TFA98XX_AUDIO_CONTROL_HPFBYPL_MSK 0x10 + +/* + * bypass_hp_right + */ +#define TFA98XX_AUDIO_CONTROL_HPFBYPR (0x1<<5) +#define TFA98XX_AUDIO_CONTROL_HPFBYPR_POS 5 +#define TFA98XX_AUDIO_CONTROL_HPFBYPR_LEN 1 +#define TFA98XX_AUDIO_CONTROL_HPFBYPR_MAX 1 +#define TFA98XX_AUDIO_CONTROL_HPFBYPR_MSK 0x20 + +/* + * enbl_dpsa_left + */ +#define TFA98XX_AUDIO_CONTROL_DPSAL (0x1<<6) +#define TFA98XX_AUDIO_CONTROL_DPSAL_POS 6 +#define TFA98XX_AUDIO_CONTROL_DPSAL_LEN 1 +#define TFA98XX_AUDIO_CONTROL_DPSAL_MAX 1 +#define TFA98XX_AUDIO_CONTROL_DPSAL_MSK 0x40 + +/* + * enbl_dpsa_right + */ +#define TFA98XX_AUDIO_CONTROL_DPSAR (0x1<<7) +#define TFA98XX_AUDIO_CONTROL_DPSAR_POS 7 +#define TFA98XX_AUDIO_CONTROL_DPSAR_LEN 1 +#define TFA98XX_AUDIO_CONTROL_DPSAR_MAX 1 +#define TFA98XX_AUDIO_CONTROL_DPSAR_MSK 0x80 + +/* + * cf_volume + */ +#define TFA98XX_AUDIO_CONTROL_VOL (0xff<<8) +#define TFA98XX_AUDIO_CONTROL_VOL_POS 8 +#define TFA98XX_AUDIO_CONTROL_VOL_LEN 8 +#define TFA98XX_AUDIO_CONTROL_VOL_MAX 255 +#define TFA98XX_AUDIO_CONTROL_VOL_MSK 0xff00 + + +/* + * (0x52)-amplifier_config + */ + +/* + * ctrl_rcv + */ +#define TFA98XX_AMPLIFIER_CONFIG_HNDSFRCV (0x1<<0) +#define TFA98XX_AMPLIFIER_CONFIG_HNDSFRCV_POS 0 +#define TFA98XX_AMPLIFIER_CONFIG_HNDSFRCV_LEN 1 +#define TFA98XX_AMPLIFIER_CONFIG_HNDSFRCV_MAX 1 +#define TFA98XX_AMPLIFIER_CONFIG_HNDSFRCV_MSK 0x1 + +/* + * ctrl_cc + */ +#define TFA98XX_AMPLIFIER_CONFIG_CLIPCTRL (0x7<<2) +#define TFA98XX_AMPLIFIER_CONFIG_CLIPCTRL_POS 2 +#define TFA98XX_AMPLIFIER_CONFIG_CLIPCTRL_LEN 3 +#define TFA98XX_AMPLIFIER_CONFIG_CLIPCTRL_MAX 7 +#define TFA98XX_AMPLIFIER_CONFIG_CLIPCTRL_MSK 0x1c + +/* + * gain + */ +#define TFA98XX_AMPLIFIER_CONFIG_AMPGAIN (0xff<<5) +#define TFA98XX_AMPLIFIER_CONFIG_AMPGAIN_POS 5 +#define TFA98XX_AMPLIFIER_CONFIG_AMPGAIN_LEN 8 +#define TFA98XX_AMPLIFIER_CONFIG_AMPGAIN_MAX 255 +#define TFA98XX_AMPLIFIER_CONFIG_AMPGAIN_MSK 0x1fe0 + +/* + * ctrl_slopectrl + */ +#define TFA98XX_AMPLIFIER_CONFIG_SLOPEE (0x1<<13) +#define TFA98XX_AMPLIFIER_CONFIG_SLOPEE_POS 13 +#define TFA98XX_AMPLIFIER_CONFIG_SLOPEE_LEN 1 +#define TFA98XX_AMPLIFIER_CONFIG_SLOPEE_MAX 1 +#define TFA98XX_AMPLIFIER_CONFIG_SLOPEE_MSK 0x2000 + +/* + * ctrl_slope + */ +#define TFA98XX_AMPLIFIER_CONFIG_SLOPESET (0x3<<14) +#define TFA98XX_AMPLIFIER_CONFIG_SLOPESET_POS 14 +#define TFA98XX_AMPLIFIER_CONFIG_SLOPESET_LEN 2 +#define TFA98XX_AMPLIFIER_CONFIG_SLOPESET_MAX 3 +#define TFA98XX_AMPLIFIER_CONFIG_SLOPESET_MSK 0xc000 + + +/* + * (0x5a)-audio_control2 + */ + +/* + * cf_volume_sec + */ +#define TFA98XX_AUDIO_CONTROL2_VOLSEC (0xff<<0) +#define TFA98XX_AUDIO_CONTROL2_VOLSEC_POS 0 +#define TFA98XX_AUDIO_CONTROL2_VOLSEC_LEN 8 +#define TFA98XX_AUDIO_CONTROL2_VOLSEC_MAX 255 +#define TFA98XX_AUDIO_CONTROL2_VOLSEC_MSK 0xff + +/* + * sw_profile + */ +#define TFA98XX_AUDIO_CONTROL2_SWPROFIL (0xff<<8) +#define TFA98XX_AUDIO_CONTROL2_SWPROFIL_POS 8 +#define TFA98XX_AUDIO_CONTROL2_SWPROFIL_LEN 8 +#define TFA98XX_AUDIO_CONTROL2_SWPROFIL_MAX 255 +#define TFA98XX_AUDIO_CONTROL2_SWPROFIL_MSK 0xff00 + + +/* + * (0x70)-dcdc_control0 + */ + +/* + * boost_volt + */ +#define TFA98XX_DCDC_CONTROL0_DCVO (0x7<<0) +#define TFA98XX_DCDC_CONTROL0_DCVO_POS 0 +#define TFA98XX_DCDC_CONTROL0_DCVO_LEN 3 +#define TFA98XX_DCDC_CONTROL0_DCVO_MAX 7 +#define TFA98XX_DCDC_CONTROL0_DCVO_MSK 0x7 + +/* + * boost_cur + */ +#define TFA98XX_DCDC_CONTROL0_DCMCC (0xf<<3) +#define TFA98XX_DCDC_CONTROL0_DCMCC_POS 3 +#define TFA98XX_DCDC_CONTROL0_DCMCC_LEN 4 +#define TFA98XX_DCDC_CONTROL0_DCMCC_MAX 15 +#define TFA98XX_DCDC_CONTROL0_DCMCC_MSK 0x78 + +/* + * bst_coil_value + */ +#define TFA98XX_DCDC_CONTROL0_DCCV (0x3<<7) +#define TFA98XX_DCDC_CONTROL0_DCCV_POS 7 +#define TFA98XX_DCDC_CONTROL0_DCCV_LEN 2 +#define TFA98XX_DCDC_CONTROL0_DCCV_MAX 3 +#define TFA98XX_DCDC_CONTROL0_DCCV_MSK 0x180 + +/* + * boost_intel + */ +#define TFA98XX_DCDC_CONTROL0_DCIE (0x1<<9) +#define TFA98XX_DCDC_CONTROL0_DCIE_POS 9 +#define TFA98XX_DCDC_CONTROL0_DCIE_LEN 1 +#define TFA98XX_DCDC_CONTROL0_DCIE_MAX 1 +#define TFA98XX_DCDC_CONTROL0_DCIE_MSK 0x200 + +/* + * boost_speed + */ +#define TFA98XX_DCDC_CONTROL0_DCSR (0x1<<10) +#define TFA98XX_DCDC_CONTROL0_DCSR_POS 10 +#define TFA98XX_DCDC_CONTROL0_DCSR_LEN 1 +#define TFA98XX_DCDC_CONTROL0_DCSR_MAX 1 +#define TFA98XX_DCDC_CONTROL0_DCSR_MSK 0x400 + +/* + * dcdc_synchronisation + */ +#define TFA98XX_DCDC_CONTROL0_DCSYNCP (0x7<<11) +#define TFA98XX_DCDC_CONTROL0_DCSYNCP_POS 11 +#define TFA98XX_DCDC_CONTROL0_DCSYNCP_LEN 3 +#define TFA98XX_DCDC_CONTROL0_DCSYNCP_MAX 7 +#define TFA98XX_DCDC_CONTROL0_DCSYNCP_MSK 0x3800 + +/* + * dcdcoff_mode + */ +#define TFA98XX_DCDC_CONTROL0_DCDIS (0x1<<14) +#define TFA98XX_DCDC_CONTROL0_DCDIS_POS 14 +#define TFA98XX_DCDC_CONTROL0_DCDIS_LEN 1 +#define TFA98XX_DCDC_CONTROL0_DCDIS_MAX 1 +#define TFA98XX_DCDC_CONTROL0_DCDIS_MSK 0x4000 + + +/* + * (0x90)-cf_controls + */ + +/* + * cf_rst_dsp + */ +#define TFA98XX_CF_CONTROLS_RST (0x1<<0) +#define TFA98XX_CF_CONTROLS_RST_POS 0 +#define TFA98XX_CF_CONTROLS_RST_LEN 1 +#define TFA98XX_CF_CONTROLS_RST_MAX 1 +#define TFA98XX_CF_CONTROLS_RST_MSK 0x1 + +/* + * cf_dmem + */ +#define TFA98XX_CF_CONTROLS_DMEM (0x3<<1) +#define TFA98XX_CF_CONTROLS_DMEM_POS 1 +#define TFA98XX_CF_CONTROLS_DMEM_LEN 2 +#define TFA98XX_CF_CONTROLS_DMEM_MAX 3 +#define TFA98XX_CF_CONTROLS_DMEM_MSK 0x6 + +/* + * cf_aif + */ +#define TFA98XX_CF_CONTROLS_AIF (0x1<<3) +#define TFA98XX_CF_CONTROLS_AIF_POS 3 +#define TFA98XX_CF_CONTROLS_AIF_LEN 1 +#define TFA98XX_CF_CONTROLS_AIF_MAX 1 +#define TFA98XX_CF_CONTROLS_AIF_MSK 0x8 + +/* + * cf_int + */ +#define TFA98XX_CF_CONTROLS_CFINT (0x1<<4) +#define TFA98XX_CF_CONTROLS_CFINT_POS 4 +#define TFA98XX_CF_CONTROLS_CFINT_LEN 1 +#define TFA98XX_CF_CONTROLS_CFINT_MAX 1 +#define TFA98XX_CF_CONTROLS_CFINT_MSK 0x10 + +/* + * cf_cgate_off + */ +#define TFA98XX_CF_CONTROLS_CFCGATE (0x1<<5) +#define TFA98XX_CF_CONTROLS_CFCGATE_POS 5 +#define TFA98XX_CF_CONTROLS_CFCGATE_LEN 1 +#define TFA98XX_CF_CONTROLS_CFCGATE_MAX 1 +#define TFA98XX_CF_CONTROLS_CFCGATE_MSK 0x20 + +/* + * cf_req_cmd + */ +#define TFA98XX_CF_CONTROLS_REQCMD (0x1<<8) +#define TFA98XX_CF_CONTROLS_REQCMD_POS 8 +#define TFA98XX_CF_CONTROLS_REQCMD_LEN 1 +#define TFA98XX_CF_CONTROLS_REQCMD_MAX 1 +#define TFA98XX_CF_CONTROLS_REQCMD_MSK 0x100 + +/* + * cf_req_reset + */ +#define TFA98XX_CF_CONTROLS_REQRST (0x1<<9) +#define TFA98XX_CF_CONTROLS_REQRST_POS 9 +#define TFA98XX_CF_CONTROLS_REQRST_LEN 1 +#define TFA98XX_CF_CONTROLS_REQRST_MAX 1 +#define TFA98XX_CF_CONTROLS_REQRST_MSK 0x200 + +/* + * cf_req_mips + */ +#define TFA98XX_CF_CONTROLS_REQMIPS (0x1<<10) +#define TFA98XX_CF_CONTROLS_REQMIPS_POS 10 +#define TFA98XX_CF_CONTROLS_REQMIPS_LEN 1 +#define TFA98XX_CF_CONTROLS_REQMIPS_MAX 1 +#define TFA98XX_CF_CONTROLS_REQMIPS_MSK 0x400 + +/* + * cf_req_mute_ready + */ +#define TFA98XX_CF_CONTROLS_REQMUTED (0x1<<11) +#define TFA98XX_CF_CONTROLS_REQMUTED_POS 11 +#define TFA98XX_CF_CONTROLS_REQMUTED_LEN 1 +#define TFA98XX_CF_CONTROLS_REQMUTED_MAX 1 +#define TFA98XX_CF_CONTROLS_REQMUTED_MSK 0x800 + +/* + * cf_req_volume_ready + */ +#define TFA98XX_CF_CONTROLS_REQVOL (0x1<<12) +#define TFA98XX_CF_CONTROLS_REQVOL_POS 12 +#define TFA98XX_CF_CONTROLS_REQVOL_LEN 1 +#define TFA98XX_CF_CONTROLS_REQVOL_MAX 1 +#define TFA98XX_CF_CONTROLS_REQVOL_MSK 0x1000 + +/* + * cf_req_damage + */ +#define TFA98XX_CF_CONTROLS_REQDMG (0x1<<13) +#define TFA98XX_CF_CONTROLS_REQDMG_POS 13 +#define TFA98XX_CF_CONTROLS_REQDMG_LEN 1 +#define TFA98XX_CF_CONTROLS_REQDMG_MAX 1 +#define TFA98XX_CF_CONTROLS_REQDMG_MSK 0x2000 + +/* + * cf_req_calibrate_ready + */ +#define TFA98XX_CF_CONTROLS_REQCAL (0x1<<14) +#define TFA98XX_CF_CONTROLS_REQCAL_POS 14 +#define TFA98XX_CF_CONTROLS_REQCAL_LEN 1 +#define TFA98XX_CF_CONTROLS_REQCAL_MAX 1 +#define TFA98XX_CF_CONTROLS_REQCAL_MSK 0x4000 + +/* + * cf_req_reserved + */ +#define TFA98XX_CF_CONTROLS_REQRSV (0x1<<15) +#define TFA98XX_CF_CONTROLS_REQRSV_POS 15 +#define TFA98XX_CF_CONTROLS_REQRSV_LEN 1 +#define TFA98XX_CF_CONTROLS_REQRSV_MAX 1 +#define TFA98XX_CF_CONTROLS_REQRSV_MSK 0x8000 + + +/* + * (0x91)-cf_mad + */ + +/* + * cf_madd + */ +#define TFA98XX_CF_MAD_MADD (0xffff<<0) +#define TFA98XX_CF_MAD_MADD_POS 0 +#define TFA98XX_CF_MAD_MADD_LEN 16 +#define TFA98XX_CF_MAD_MADD_MAX 65535 +#define TFA98XX_CF_MAD_MADD_MSK 0xffff + + +/* + * (0x92)-cf_mem + */ + +/* + * cf_mema + */ +#define TFA98XX_CF_MEM_MEMA (0xffff<<0) +#define TFA98XX_CF_MEM_MEMA_POS 0 +#define TFA98XX_CF_MEM_MEMA_LEN 16 +#define TFA98XX_CF_MEM_MEMA_MAX 65535 +#define TFA98XX_CF_MEM_MEMA_MSK 0xffff + + +/* + * (0x93)-cf_status + */ + +/* + * cf_err + */ +#define TFA98XX_CF_STATUS_ERR (0xff<<0) +#define TFA98XX_CF_STATUS_ERR_POS 0 +#define TFA98XX_CF_STATUS_ERR_LEN 8 +#define TFA98XX_CF_STATUS_ERR_MAX 255 +#define TFA98XX_CF_STATUS_ERR_MSK 0xff + +/* + * cf_ack_cmd + */ +#define TFA98XX_CF_STATUS_ACKCMD (0x1<<8) +#define TFA98XX_CF_STATUS_ACKCMD_POS 8 +#define TFA98XX_CF_STATUS_ACKCMD_LEN 1 +#define TFA98XX_CF_STATUS_ACKCMD_MAX 1 +#define TFA98XX_CF_STATUS_ACKCMD_MSK 0x100 + +/* + * cf_ack_reset + */ +#define TFA98XX_CF_STATUS_ACKRST (0x1<<9) +#define TFA98XX_CF_STATUS_ACKRST_POS 9 +#define TFA98XX_CF_STATUS_ACKRST_LEN 1 +#define TFA98XX_CF_STATUS_ACKRST_MAX 1 +#define TFA98XX_CF_STATUS_ACKRST_MSK 0x200 + +/* + * cf_ack_mips + */ +#define TFA98XX_CF_STATUS_ACKMIPS (0x1<<10) +#define TFA98XX_CF_STATUS_ACKMIPS_POS 10 +#define TFA98XX_CF_STATUS_ACKMIPS_LEN 1 +#define TFA98XX_CF_STATUS_ACKMIPS_MAX 1 +#define TFA98XX_CF_STATUS_ACKMIPS_MSK 0x400 + +/* + * cf_ack_mute_ready + */ +#define TFA98XX_CF_STATUS_ACKMUTED (0x1<<11) +#define TFA98XX_CF_STATUS_ACKMUTED_POS 11 +#define TFA98XX_CF_STATUS_ACKMUTED_LEN 1 +#define TFA98XX_CF_STATUS_ACKMUTED_MAX 1 +#define TFA98XX_CF_STATUS_ACKMUTED_MSK 0x800 + +/* + * cf_ack_volume_ready + */ +#define TFA98XX_CF_STATUS_ACKVOL (0x1<<12) +#define TFA98XX_CF_STATUS_ACKVOL_POS 12 +#define TFA98XX_CF_STATUS_ACKVOL_LEN 1 +#define TFA98XX_CF_STATUS_ACKVOL_MAX 1 +#define TFA98XX_CF_STATUS_ACKVOL_MSK 0x1000 + +/* + * cf_ack_damage + */ +#define TFA98XX_CF_STATUS_ACKDMG (0x1<<13) +#define TFA98XX_CF_STATUS_ACKDMG_POS 13 +#define TFA98XX_CF_STATUS_ACKDMG_LEN 1 +#define TFA98XX_CF_STATUS_ACKDMG_MAX 1 +#define TFA98XX_CF_STATUS_ACKDMG_MSK 0x2000 + +/* + * cf_ack_calibrate_ready + */ +#define TFA98XX_CF_STATUS_ACKCAL (0x1<<14) +#define TFA98XX_CF_STATUS_ACKCAL_POS 14 +#define TFA98XX_CF_STATUS_ACKCAL_LEN 1 +#define TFA98XX_CF_STATUS_ACKCAL_MAX 1 +#define TFA98XX_CF_STATUS_ACKCAL_MSK 0x4000 + +/* + * cf_ack_reserved + */ +#define TFA98XX_CF_STATUS_ACKRSV (0x1<<15) +#define TFA98XX_CF_STATUS_ACKRSV_POS 15 +#define TFA98XX_CF_STATUS_ACKRSV_LEN 1 +#define TFA98XX_CF_STATUS_ACKRSV_MAX 1 +#define TFA98XX_CF_STATUS_ACKRSV_MSK 0x8000 + + +/* + * (0xa1)-mtpkey2_reg + */ + +/* + * mtpkey2 + */ +#define TFA98XX_MTPKEY2_REG_MTPK (0xff<<0) +#define TFA98XX_MTPKEY2_REG_MTPK_POS 0 +#define TFA98XX_MTPKEY2_REG_MTPK_LEN 8 +#define TFA98XX_MTPKEY2_REG_MTPK_MAX 255 +#define TFA98XX_MTPKEY2_REG_MTPK_MSK 0xff + + +/* + * (0xa2)-mtp_status + */ + +/* + * key01_locked + */ +#define TFA98XX_MTP_STATUS_KEY1LOCKED (0x1<<0) +#define TFA98XX_MTP_STATUS_KEY1LOCKED_POS 0 +#define TFA98XX_MTP_STATUS_KEY1LOCKED_LEN 1 +#define TFA98XX_MTP_STATUS_KEY1LOCKED_MAX 1 +#define TFA98XX_MTP_STATUS_KEY1LOCKED_MSK 0x1 + +/* + * key02_locked + */ +#define TFA98XX_MTP_STATUS_KEY2LOCKED (0x1<<1) +#define TFA98XX_MTP_STATUS_KEY2LOCKED_POS 1 +#define TFA98XX_MTP_STATUS_KEY2LOCKED_LEN 1 +#define TFA98XX_MTP_STATUS_KEY2LOCKED_MAX 1 +#define TFA98XX_MTP_STATUS_KEY2LOCKED_MSK 0x2 + + +/* + * (0xa3)-KEY_protected_mtp_control + */ + +/* + * auto_copy_iic_to_mtp + */ +#define TFA98XX_KEY_PROTECTED_MTP_CONTROL_CIMTP (0x1<<6) +#define TFA98XX_KEY_PROTECTED_MTP_CONTROL_CIMTP_POS 6 +#define TFA98XX_KEY_PROTECTED_MTP_CONTROL_CIMTP_LEN 1 +#define TFA98XX_KEY_PROTECTED_MTP_CONTROL_CIMTP_MAX 1 +#define TFA98XX_KEY_PROTECTED_MTP_CONTROL_CIMTP_MSK 0x40 + + +/* + * (0xa5)-mtp_data_out_msb + */ + +/* + * mtp_man_data_out_msb + */ +#define TFA98XX_MTP_DATA_OUT_MSB_MTPRDMSB (0xffff<<0) +#define TFA98XX_MTP_DATA_OUT_MSB_MTPRDMSB_POS 0 +#define TFA98XX_MTP_DATA_OUT_MSB_MTPRDMSB_LEN 16 +#define TFA98XX_MTP_DATA_OUT_MSB_MTPRDMSB_MAX 65535 +#define TFA98XX_MTP_DATA_OUT_MSB_MTPRDMSB_MSK 0xffff + + +/* + * (0xa6)-mtp_data_out_lsb + */ + +/* + * mtp_man_data_out_lsb + */ +#define TFA98XX_MTP_DATA_OUT_LSB_MTPRDLSB (0xffff<<0) +#define TFA98XX_MTP_DATA_OUT_LSB_MTPRDLSB_POS 0 +#define TFA98XX_MTP_DATA_OUT_LSB_MTPRDLSB_LEN 16 +#define TFA98XX_MTP_DATA_OUT_LSB_MTPRDLSB_MAX 65535 +#define TFA98XX_MTP_DATA_OUT_LSB_MTPRDLSB_MSK 0xffff + + +/* + * (0xb1)-temp_sensor_config + */ + +/* + * ext_temp + */ +#define TFA98XX_TEMP_SENSOR_CONFIG_EXTTS (0x1ff<<0) +#define TFA98XX_TEMP_SENSOR_CONFIG_EXTTS_POS 0 +#define TFA98XX_TEMP_SENSOR_CONFIG_EXTTS_LEN 9 +#define TFA98XX_TEMP_SENSOR_CONFIG_EXTTS_MAX 511 +#define TFA98XX_TEMP_SENSOR_CONFIG_EXTTS_MSK 0x1ff + +/* + * ext_temp_sel + */ +#define TFA98XX_TEMP_SENSOR_CONFIG_TROS (0x1<<9) +#define TFA98XX_TEMP_SENSOR_CONFIG_TROS_POS 9 +#define TFA98XX_TEMP_SENSOR_CONFIG_TROS_LEN 1 +#define TFA98XX_TEMP_SENSOR_CONFIG_TROS_MAX 1 +#define TFA98XX_TEMP_SENSOR_CONFIG_TROS_MSK 0x200 + + +/* + * (0xf0)-KEY2_protected_MTP0 + */ + +/* + * calibration_onetime + */ +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC (0x1<<0) +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS 0 +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_LEN 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MAX 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK 0x1 + +/* + * calibr_ron_done + */ +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPEX (0x1<<1) +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_LEN 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MAX 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK 0x2 + +/* + * calibr_dcdc_api_calibrate + */ +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCAPI (0x1<<2) +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCAPI_POS 2 +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCAPI_LEN 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCAPI_MAX 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCAPI_MSK 0x4 + +/* + * calibr_dcdc_delta_sign + */ +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCSB (0x1<<3) +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCSB_POS 3 +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCSB_LEN 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCSB_MAX 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_DCMCCSB_MSK 0x8 + +/* + * calibr_dcdc_delta + */ +#define TFA98XX_KEY2_PROTECTED_MTP0_USERDEF (0x7<<4) +#define TFA98XX_KEY2_PROTECTED_MTP0_USERDEF_POS 4 +#define TFA98XX_KEY2_PROTECTED_MTP0_USERDEF_LEN 3 +#define TFA98XX_KEY2_PROTECTED_MTP0_USERDEF_MAX 7 +#define TFA98XX_KEY2_PROTECTED_MTP0_USERDEF_MSK 0x70 + + +/* + * (0xf4)-KEY1_protected_MTP4 + */ + + +/* + * (0xf5)-KEY1_protected_MTP5 + */ + +#endif /* TFA98XX_GENREGS_H */ diff --git a/sound/soc/codecs/inc/tfa98xx_parameters.h b/sound/soc/codecs/inc/tfa98xx_parameters.h new file mode 100644 index 000000000000..50f9a8c82914 --- /dev/null +++ b/sound/soc/codecs/inc/tfa98xx_parameters.h @@ -0,0 +1,741 @@ +/* + * Copyright 2013-2017 NXP Semiconductors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * tfa98xx_parameters.h + * + * Created on: Jul 22, 2013 + * Author: NLV02095 + */ + +#ifndef TFA98XXPARAMETERS_H_ +#define TFA98XXPARAMETERS_H_ + +//#include "config.h" +// workaround for Visual Studio: +// fatal error C1083: Cannot open include file: 'config.h': No such file or directory +#ifdef __KERNEL__ +#include +#else +#include +#endif + +#include "tfa_service.h" + +#if (defined(WIN32) || defined(_X64)) +/* These warnings are disabled because it is only given by Windows and there is no easy fix */ +#pragma warning(disable:4200) +#pragma warning(disable:4214) +#endif + +/* + * profiles & volumesteps + * + */ +#define TFA_MAX_PROFILES (64) +#define TFA_MAX_VSTEPS (64) +#define TFA_MAX_VSTEP_MSG_MARKER (100) /* This marker is used to indicate if all msgs need to be written to the device */ +#define TFA_MAX_MSGS (10) + +// the pack pragma is required to make that the size in memory +// matches the actual variable lenghts +// This is to assure that the binary files can be transported between +// different platforms. +#pragma pack (push, 1) + +/* + * typedef for 24 bit value using 3 bytes + */ +typedef struct uint24 { + uint8_t b[3]; +} uint24_t; +/* + * the generic header + * all char types are in ASCII + */ +typedef struct nxpTfaHeader { + uint16_t id; + char version[2]; // "V_" : V=version, vv=subversion + char subversion[2]; // "vv" : vv=subversion + uint16_t size; // data size in bytes following CRC + uint32_t CRC; // 32-bits CRC for following data + char customer[8]; // “name of customer” + char application[8]; // “application name” + char type[8]; // “application type name” +} nxpTfaHeader_t; + +typedef enum nxpTfaSamplerate { + fs_8k, // 8kHz + fs_11k025, // 11.025kHz + fs_12k, // 12kHz + fs_16k, // 16kHz + fs_22k05, // 22.05kHz + fs_24k, // 24kHz + fs_32k, // 32kHz + fs_44k1, // 44.1kHz + fs_48k, // 48kHz + fs_96k, // 96kHz + fs_count // Should always be last item. +} nxpTfaSamplerate_t; + +// Keep in sync with nxpTfaSamplerate_t ! +static const int nxpTfaSamplerateHz[fs_count] = { 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 96000 }; + + +/* + * coolflux direct memory access + */ +typedef struct nxpTfaDspMem { + uint8_t type; /* 0--3: p, x, y, iomem */ + uint16_t address; /* target address */ + uint8_t size; /* data size in words */ + int words[]; /* payload in signed 32bit integer (two's complement) */ +} nxpTfaDspMem_t; + +/* + * the biquad coefficients for the API together with index in filter + * the biquad_index is the actual index in the equalizer +1 + */ +#define BIQUAD_COEFF_SIZE 6 + +/* +* Output fixed point coeffs structure +*/ +typedef struct { + int a2; + int a1; + int b2; + int b1; + int b0; +}nxpTfaBiquad_t; + +typedef struct nxpTfaBiquadOld { + uint8_t bytes[BIQUAD_COEFF_SIZE*sizeof(uint24_t)]; +}nxpTfaBiquadOld_t; + +typedef struct nxpTfaBiquadFloat { + float headroom; + float b0; + float b1; + float b2; + float a1; + float a2; +} nxpTfaBiquadFloat_t; + +/* +* EQ filter definitions +* Note: This is not in line with smartstudio (JV: 12/12/2016) +*/ +typedef enum nxpTfaFilterType { + fCustom, //User defined biquad coefficients + fFlat, //Vary only gain + fLowpass, //2nd order Butterworth low pass + fHighpass, //2nd order Butterworth high pass + fLowshelf, + fHighshelf, + fNotch, + fPeak, + fBandpass, + f1stLP, + f1stHP, + fElliptic +} nxpTfaFilterType_t; + +/* + * filter parameters for biquad (re-)calculation + */ +typedef struct nxpTfaFilter { + nxpTfaBiquadOld_t biquad; + uint8_t enabled; + uint8_t type; // (== enum FilterTypes, assure 8bits length) + float frequency; + float Q; + float gain; +} nxpTfaFilter_t ; //8 * float + int32 + byte == 37 + +/* + * biquad params for calculation +*/ + +#define TFA_BQ_EQ_INDEX 0 +#define TFA_BQ_ANTI_ALIAS_INDEX 10 +#define TFA_BQ_INTEGRATOR_INDEX 13 + +/* +* Loudspeaker Compensation filter definitions +*/ +typedef struct nxpTfaLsCompensationFilter { + nxpTfaBiquad_t biquad; + uint8_t lsCompOn; // Loudspeaker compensation on/off; when 'off', the DSP code doesn't apply the bwExt => bwExtOn GUI flag should be gray to avoid confusion + uint8_t bwExtOn; // Bandwidth extension on/off + float fRes; // [Hz] speaker resonance frequency + float Qt; // Speaker resonance Q-factor + float fBwExt; // [Hz] Band width extension frequency + float samplingFreq;// [Hz] Sampling frequency +} nxpTfaLsCompensationFilter_t; + +/* +* Anti Aliasing Elliptic filter definitions +*/ +typedef struct nxpTfaAntiAliasFilter { + nxpTfaBiquad_t biquad; /**< Output results fixed point coeffs */ + uint8_t enabled; + float cutOffFreq; // cut off frequency + float samplingFreq; // sampling frequency + float rippleDb; // range: [0.1 3.0] + float rolloff; // range: [-1.0 1.0] +} nxpTfaAntiAliasFilter_t; + +/** +* Integrator filter input definitions +*/ +typedef struct nxpTfaIntegratorFilter { + nxpTfaBiquad_t biquad; /**< Output results fixed point coeffs */ + uint8_t type; /**< Butterworth filter type: high or low pass */ + float cutOffFreq; /**< cut off frequency in Hertz; range: [100.0 4000.0] */ + float samplingFreq; /**< sampling frequency in Hertz */ + float leakage; /**< leakage factor; range [0.0 1.0] */ +} nxpTfaIntegratorFilter_t; + + +typedef struct nxpTfaEqFilter { + nxpTfaBiquad_t biquad; + uint8_t enabled; + uint8_t type; // (== enum FilterTypes, assure 8bits length) + float cutOffFreq; // cut off frequency, // range: [100.0 4000.0] + float samplingFreq; // sampling frequency + float Q; // range: [0.5 5.0] + float gainDb; // range: [-10.0 10.0] +} nxpTfaEqFilter_t ; //8 * float + int32 + byte == 37 + +typedef struct nxpTfaContAntiAlias { + int8_t index; /**< index determines destination type; anti-alias, integrator,eq */ + uint8_t type; + float cutOffFreq; // cut off frequency + float samplingFreq; + float rippleDb; // integrator leakage + float rolloff; + uint8_t bytes[5*3]; // payload 5*24buts coeffs +}nxpTfaContAntiAlias_t; + +typedef struct nxpTfaContIntegrator { + int8_t index; /**< index determines destination type; anti-alias, integrator,eq */ + uint8_t type; + float cutOffFreq; // cut off frequency + float samplingFreq; + float leakage; // integrator leakage + float reserved; + uint8_t bytes[5*3]; // payload 5*24buts coeffs +}nxpTfaContIntegrator_t; + +typedef struct nxpTfaContEq { + int8_t index; + uint8_t type; // (== enum FilterTypes, assure 8bits length) + float cutOffFreq; // cut off frequency, // range: [100.0 4000.0] + float samplingFreq; // sampling frequency + float Q; // range: [0.5 5.0] + float gainDb; // range: [-10.0 10.0] + uint8_t bytes[5*3]; // payload 5*24buts coeffs +} nxpTfaContEq_t ; //8 * float + int32 + byte == 37 + +typedef union nxpTfaContBiquad { + nxpTfaContEq_t eq; + nxpTfaContAntiAlias_t aa; + nxpTfaContIntegrator_t in; +}nxpTfaContBiquad_t; + +#define TFA_BQ_EQ_INDEX 0 +#define TFA_BQ_ANTI_ALIAS_INDEX 10 +#define TFA_BQ_INTEGRATOR_INDEX 13 +#define TFA98XX_MAX_EQ 10 + +typedef struct nxpTfaEqualizer { + nxpTfaFilter_t filter[TFA98XX_MAX_EQ]; +} nxpTfaEqualizer_t; + +/* + * files + */ +#define HDR(c1,c2) (c2<<8|c1) // little endian +typedef enum nxpTfaHeaderType { + paramsHdr = HDR('P','M'), /* containter file */ + volstepHdr = HDR('V','P'), + patchHdr = HDR('P','A'), + speakerHdr = HDR('S','P'), + presetHdr = HDR('P','R'), + configHdr = HDR('C','O'), + equalizerHdr = HDR('E','Q'), + drcHdr = HDR('D','R'), + msgHdr = HDR('M','G'), /* generic message */ + infoHdr = HDR('I','N') +} nxpTfaHeaderType_t; + +/* + * equalizer file + */ +#define NXPTFA_EQ_VERSION '1' +#define NXPTFA_EQ_SUBVERSION "00" +typedef struct nxpTfaEqualizerFile { + nxpTfaHeader_t hdr; + uint8_t samplerate; // ==enum samplerates, assure 8 bits + nxpTfaFilter_t filter[TFA98XX_MAX_EQ];// note: API index counts from 1..10 +} nxpTfaEqualizerFile_t; + +/* + * patch file + */ +#define NXPTFA_PA_VERSION '1' +#define NXPTFA_PA_SUBVERSION "00" +typedef struct nxpTfaPatchFile { + nxpTfaHeader_t hdr; + uint8_t data[]; +} nxpTfaPatch_t; + +/* + * generic message file + * - the payload of this file includes the opcode and is send straight to the DSP + */ +#define NXPTFA_MG_VERSION '3' +#define NXPTFA_MG_SUBVERSION "00" +typedef struct nxpTfaMsgFile { + nxpTfaHeader_t hdr; + uint8_t data[]; +} nxpTfaMsgFile_t; + +/* + * NOTE the tfa98xx API defines the enum Tfa98xx_config_type that defines + * the subtypes as decribes below. + * tfa98xx_dsp_config_parameter_type() can be used to get the + * supported type for the active device.. + */ +/* + * config file V1 sub 1 + */ +#define NXPTFA_CO_VERSION '1' +#define NXPTFA_CO3_VERSION '3' +#define NXPTFA_CO_SUBVERSION1 "01" +typedef struct nxpTfaConfigS1File { + nxpTfaHeader_t hdr; + uint8_t data[55*3]; +} nxpTfaConfigS1_t; + +/* + * config file V1 sub 2 + */ +#define NXPTFA_CO_SUBVERSION2 "02" +typedef struct nxpTfaConfigS2File { + nxpTfaHeader_t hdr; + uint8_t data[67*3]; +} nxpTfaConfigS2_t; + +/* + * config file V1 sub 3 + */ +#define NXPTFA_CO_SUBVERSION3 "03" +typedef struct nxpTfaConfigS3File { + nxpTfaHeader_t hdr; + uint8_t data[67*3]; +} nxpTfaConfigS3_t; + +/* + * config file V1.0 + */ +#define NXPTFA_CO_SUBVERSION "00" +typedef struct nxpTfaConfigFile { + nxpTfaHeader_t hdr; + uint8_t data[]; +} nxpTfaConfig_t; + +/* + * preset file + */ +#define NXPTFA_PR_VERSION '1' +#define NXPTFA_PR_SUBVERSION "00" +typedef struct nxpTfaPresetFile { + nxpTfaHeader_t hdr; + uint8_t data[]; +} nxpTfaPreset_t; + +/* + * drc file + */ +#define NXPTFA_DR_VERSION '1' +#define NXPTFA_DR_SUBVERSION "00" +typedef struct nxpTfaDrcFile { + nxpTfaHeader_t hdr; + uint8_t data[]; +} nxpTfaDrc_t; + +/* + * drc file + * for tfa 2 there is also a xml-version + */ +#define NXPTFA_DR3_VERSION '3' +#define NXPTFA_DR3_SUBVERSION "00" +typedef struct nxpTfaDrcFile2 { + nxpTfaHeader_t hdr; + uint8_t version[3]; + uint8_t data[]; +} nxpTfaDrc2_t; + +/* + * volume step structures + */ +// VP01 +#define NXPTFA_VP1_VERSION '1' +#define NXPTFA_VP1_SUBVERSION "01" +typedef struct nxpTfaVolumeStep1 { + float attenuation; // IEEE single float + uint8_t preset[TFA98XX_PRESET_LENGTH]; +} nxpTfaVolumeStep1_t; + +// VP02 +#define NXPTFA_VP2_VERSION '2' +#define NXPTFA_VP2_SUBVERSION "01" +typedef struct nxpTfaVolumeStep2 { + float attenuation; // IEEE single float + uint8_t preset[TFA98XX_PRESET_LENGTH]; + nxpTfaFilter_t filter[TFA98XX_MAX_EQ];// note: API index counts from 1..10 +} nxpTfaVolumeStep2_t; + +/* + * volumestep file + */ +#define NXPTFA_VP_VERSION '1' +#define NXPTFA_VP_SUBVERSION "00" +typedef struct nxpTfaVolumeStepFile { + nxpTfaHeader_t hdr; + uint8_t vsteps; // can also be calulated from size+type + uint8_t samplerate; // ==enum samplerates, assure 8 bits + uint8_t payload; //start of variable length contents:N times volsteps +}nxpTfaVolumeStepFile_t; +/* + * volumestep2 file + */ +typedef struct nxpTfaVolumeStep2File { + nxpTfaHeader_t hdr; + uint8_t vsteps; // can also be calulated from size+type + uint8_t samplerate; // ==enum samplerates, assure 8 bits + nxpTfaVolumeStep2_t vstep[]; //start of variable length contents:N times volsteps +}nxpTfaVolumeStep2File_t; + +/* + * volumestepMax2 file + */ +typedef struct nxpTfaVolumeStepMax2File { + nxpTfaHeader_t hdr; + uint8_t version[3]; + uint8_t NrOfVsteps; + uint8_t vstepsBin[]; +}nxpTfaVolumeStepMax2File_t; + +/* + * volumestepMax2 file + * This volumestep should ONLY be used for the use of bin2hdr! + * This can only be used to find the messagetype of the vstep (without header) + */ +typedef struct nxpTfaVolumeStepMax2_1File { + uint8_t version[3]; + uint8_t NrOfVsteps; + uint8_t vstepsBin[]; +}nxpTfaVolumeStepMax2_1File_t; + +struct nxpTfaVolumeStepRegisterInfo { + uint8_t NrOfRegisters; + uint16_t registerInfo[]; +}; + +struct nxpTfaVolumeStepMessageInfo { + uint8_t NrOfMessages; + uint8_t MessageType; + uint24_t MessageLength; + uint8_t CmdId[3]; + uint8_t ParameterData[]; +}; +/**************************old v2 *************************************************/ + +/* + * subv 00 volumestep file + */ +typedef struct nxpTfaOldHeader { + uint16_t id; + char version[2]; // "V_" : V=version, vv=subversion + char subversion[2]; // "vv" : vv=subversion + uint16_t size; // data size in bytes following CRC + uint32_t CRC; // 32-bits CRC for following data +} nxpTfaOldHeader_t; + +typedef struct nxpOldTfaFilter { + double bq[5]; + int32_t type; + double frequency; + double Q; + double gain; + uint8_t enabled; +} nxpTfaOldFilter_t ; + +typedef struct nxpTfaOldVolumeStep2 { + float attenuation; // IEEE single float + uint8_t preset[TFA98XX_PRESET_LENGTH]; + nxpTfaOldFilter_t eq[10]; +} nxpTfaOldVolumeStep2_t; + +typedef struct nxpTfaOldVolumeStepFile { + nxpTfaOldHeader_t hdr; + nxpTfaOldVolumeStep2_t step[]; +}nxpTfaOldVolumeStep2File_t; +/**************************end old v2 *************************************************/ + +/* + * speaker file header + */ +struct nxpTfaSpkHeader { + struct nxpTfaHeader hdr; + char name[8]; // speaker nick name (e.g. “dumbo”) + char vendor[16]; + char type[8]; + // dimensions (mm) + uint8_t height; + uint8_t width; + uint8_t depth; + uint16_t ohm; +}; + +/* + * speaker file + */ +#define NXPTFA_SP_VERSION '1' +#define NXPTFA_SP_SUBVERSION "00" +typedef struct nxpTfaSpeakerFile { + nxpTfaHeader_t hdr; + char name[8]; // speaker nick name (e.g. “dumbo”) + char vendor[16]; + char type[8]; + // dimensions (mm) + uint8_t height; + uint8_t width; + uint8_t depth; + uint8_t ohm_primary; + uint8_t ohm_secondary; + uint8_t data[]; //payload TFA98XX_SPEAKERPARAMETER_LENGTH +} nxpTfaSpeakerFile_t; + +#define NXPTFA_VP3_VERSION '3' +#define NXPTFA_VP3_SUBVERSION "00" + +struct nxpTfaFWVer { + uint8_t Major; + uint8_t minor; + uint8_t minor_update:6; + uint8_t Update:2; +}; + +struct nxpTfaFWMsg { + struct nxpTfaFWVer fwVersion; + struct nxpTfaMsg payload; +}; + +typedef struct nxpTfaLiveData { + char name[25]; + char addrs[25]; + int tracker; + int scalefactor; +} nxpTfaLiveData_t; + +#define NXPTFA_SP3_VERSION '3' +#define NXPTFA_SP3_SUBVERSION "00" +struct nxpTfaSpeakerFileMax2 { + nxpTfaHeader_t hdr; + char name[8]; // speaker nick name (e.g. “dumbo”) + char vendor[16]; + char type[8]; + // dimensions (mm) + uint8_t height; + uint8_t width; + uint8_t depth; + uint8_t ohm_primary; + uint8_t ohm_secondary; + struct nxpTfaFWMsg FWmsg; //payload including FW ver and Cmd ID +}; + +/* + * parameter container file + */ +/* + * descriptors + * Note 1: append new DescriptorType at the end + * Note 2: add new descriptors to dsc_name[] in tfaContUtil.c + */ +typedef enum nxpTfaDescriptorType { + dscDevice, // device list + dscProfile, // profile list + dscRegister, // register patch + dscString, // ascii, zero terminated string + dscFile, // filename + file contents + dscPatch, // patch file + dscMarker, // marker to indicate end of a list + dscMode, + dscSetInputSelect, + dscSetOutputSelect, + dscSetProgramConfig, + dscSetLagW, + dscSetGains, + dscSetvBatFactors, + dscSetSensesCal, + dscSetSensesDelay, + dscBitfield, + dscDefault, // used to reset bitfields to there default values + dscLiveData, + dscLiveDataString, + dscGroup, + dscCmd, + dscSetMBDrc, + dscFilter, + dscNoInit, + dscFeatures, + dscCfMem, // coolflux memory x,y,io + dscSetFwkUseCase, + dscSetVddpConfig, + dsc_last // trailer +} nxpTfaDescriptorType_t; + +#define TFA_BITFIELDDSCMSK 0x7fffffff +typedef struct nxpTfaDescPtr { + uint32_t offset:24; + uint32_t type:8; // (== enum nxpTfaDescriptorType, assure 8bits length) +}nxpTfaDescPtr_t; + +/* + * generic file descriptor + */ +typedef struct nxpTfaFileDsc { + nxpTfaDescPtr_t name; + uint32_t size; // file data length in bytes + uint8_t data[]; //payload +} nxpTfaFileDsc_t; + + +/* + * device descriptor list + */ +typedef struct nxpTfaDeviceList { + uint8_t length; // nr of items in the list + uint8_t bus; // bus + uint8_t dev; // device + uint8_t func; // subfunction or subdevice + uint32_t devid; // device hw fw id + nxpTfaDescPtr_t name; // device name + nxpTfaDescPtr_t list[]; // items list +} nxpTfaDeviceList_t; + +/* + * profile descriptor list + */ +typedef struct nxpTfaProfileList { + uint32_t length:8; // nr of items in the list + name + uint32_t group:8; // profile group number + uint32_t ID:16; // profile ID + nxpTfaDescPtr_t name; // profile name + nxpTfaDescPtr_t list[]; // items list (lenght-1 items) +} nxpTfaProfileList_t; +#define TFA_PROFID 0x1234 + +/* + * livedata descriptor list + */ +typedef struct nxpTfaLiveDataList { + uint32_t length:8; // nr of items in the list + uint32_t ID:24; // profile ID + nxpTfaDescPtr_t name; // livedata name + nxpTfaDescPtr_t list[]; // items list +} nxpTfaLiveDataList_t; +#define TFA_LIVEDATAID 0x5678 + +/* + * Bitfield descriptor + */ +typedef struct nxpTfaBitfield { + uint16_t value; + uint16_t field; // ==datasheet defined, 16 bits +} nxpTfaBitfield_t; + +/* + * Bitfield enumuration bits descriptor + */ +typedef struct nxpTfaBfEnum { + unsigned int len:4; // this is the actual length-1 + unsigned int pos:4; + unsigned int address:8; +} nxpTfaBfEnum_t; + +/* + * Register patch descriptor + */ +typedef struct nxpTfaRegpatch { + uint8_t address; // register address + uint16_t value; // value to write + uint16_t mask; // mask of bits to write +} nxpTfaRegpatch_t; + +/* + * Mode descriptor + */ +typedef struct nxpTfaUseCase { + int value; // mode value, maps to enum Tfa98xx_Mode +} nxpTfaMode_t; + +/* + * NoInit descriptor + */ +typedef struct nxpTfaNoInit { + uint8_t value; // noInit value +} nxpTfaNoInit_t; + +/* + * Features descriptor + */ +typedef struct nxpTfaFeatures { + uint16_t value[3]; // features value +} nxpTfaFeatures_t; + + +/* + * the container file + * - the size field is 32bits long (generic=16) + * - all char types are in ASCII + */ +#define NXPTFA_PM_VERSION '1' +#define NXPTFA_PM3_VERSION '3' +#define NXPTFA_PM_SUBVERSION '1' +typedef struct nxpTfaContainer { + char id[2]; // "XX" : XX=type + char version[2]; // "V_" : V=version, vv=subversion + char subversion[2]; // "vv" : vv=subversion + uint32_t size; // data size in bytes following CRC + uint32_t CRC; // 32-bits CRC for following data + uint16_t rev; // "extra chars for rev nr" + char customer[8]; // “name of customer” + char application[8]; // “application name” + char type[8]; // “application type name” + uint16_t ndev; // "nr of device lists" + uint16_t nprof; // "nr of profile lists" + uint16_t nliveData; // "nr of livedata lists" + nxpTfaDescPtr_t index[]; // start of item index table +} nxpTfaContainer_t; + +#pragma pack (pop) + +#endif /* TFA98XXPARAMETERS_H_ */ diff --git a/sound/soc/codecs/inc/tfa98xx_tfafieldnames.h b/sound/soc/codecs/inc/tfa98xx_tfafieldnames.h new file mode 100644 index 000000000000..3f9a437df8b9 --- /dev/null +++ b/sound/soc/codecs/inc/tfa98xx_tfafieldnames.h @@ -0,0 +1,147 @@ +/* + * Copyright 2014-2017 NXP Semiconductors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +typedef struct TfaBfName { + unsigned short bfEnum; + char *bfName; +} tfaBfName_t; + +typedef struct TfaIrqName { + unsigned short irqEnum; + char *irqName; +} tfaIrqName_t; + +#include "tfa1_tfafieldnames.h" +#include "tfa2_tfafieldnames_N1C.h" +/* diffs for specific devices */ +#include "tfa9887_tfafieldnames.h" +#include "tfa9890_tfafieldnames.h" +#include "tfa9891_tfafieldnames.h" +#include "tfa9872_tfafieldnames.h" +#include "tfa9912_tfafieldnames.h" +#include "tfa9896_tfafieldnames.h" +#include "tfa9874_tfafieldnames.h" +#include "tfa9894_tfafieldnames.h" + +/* missing 'common' defs break the build but unused in TFA1 context */ +#define TFA1_BF_AMPINSEL -1 +#define TFA1_BF_MANSCONF -1 +#define TFA1_BF_MANCOLD -1 +#define TFA1_BF_INTSMUTE -1 +#define TFA1_BF_CFSMR -1 +#define TFA1_BF_CFSML -1 +#define TFA1_BF_DCMCCAPI -1 +#define TFA1_BF_DCMCCSB -1 +#define TFA1_BF_USERDEF -1 +#define TFA1_BF_MANSTATE -1 +#define TFA1_BF_MANOPER -1 +#define TFA1_BF_REFCKSEL -1 +#define TFA1_BF_VOLSEC -1 +#define TFA1_BF_FRACTDEL -1 +#define TFA1_BF_ACKDMG -1 +#define TFA1_BF_SSRIGHTE -1 +#define TFA1_BF_SSLEFTE -1 +#define TFA1_BF_R25CL -1 +#define TFA1_BF_R25CR -1 +#define TFA1_BF_SWPROFIL 0x8045 /*!< profile save */ +#define TFA1_BF_SWVSTEP 0x80a5 /*!< vstep save */ + +/* missing 'common' defs break the build */ +#define TFA2_BF_CFSM -1 + + +/* MTP access uses registers + * defs are derived from corresponding bitfield names as used in the BF macros + */ +#define MTPKEY2 MTPK /* unlock key2 MTPK */ +#define MTP0 MTPOTC /* MTP data */ +#define MTP_CONTROL CIMTP /* copy i2c to mtp */ + +/* interrupt enable register uses HW name in TFA2 */ +#define TFA2_BF_INTENVDDS TFA2_BF_IEVDDS + + +/* TFA9891 specific bit field names */ +#define TFA1_BF_SAAMGAIN 0x2202 +#define TFA2_BF_SAAMGAIN -1 + +/* TFA9872 specific bit field names */ +#define TFA2_BF_IELP0 TFA9872_BF_IELP0 +#define TFA2_BF_ISTLP0 TFA9872_BF_ISTLP0 +#define TFA2_BF_IPOLP0 TFA9872_BF_IPOLP0 +#define TFA2_BF_IELP1 TFA9872_BF_IELP1 +#define TFA2_BF_ISTLP1 TFA9872_BF_ISTLP1 +#define TFA2_BF_IPOLP1 TFA9872_BF_IPOLP1 +#define TFA2_BF_LP0 TFA9872_BF_LP0 +#define TFA2_BF_LP1 TFA9872_BF_LP1 +#define TFA2_BF_R25C TFA9872_BF_R25C +#define TFA2_BF_SAMMODE TFA9872_BF_SAMMODE + +/* interrupt bit field names of TFA2 and TFA1 do not match */ +#define TFA1_BF_IEACS TFA1_BF_INTENACS +#define TFA1_BF_IPOACS TFA1_BF_INTPOLACS +#define TFA1_BF_ISTACS TFA1_BF_INTOACS +#define TFA1_BF_ISTVDDS TFA1_BF_INTOVDDS +#define TFA1_BF_ICLVDDS TFA1_BF_INTIVDDS +#define TFA1_BF_IPOVDDS TFA1_BF_INTPOLVDDS +#define TFA1_BF_IENOCLK TFA1_BF_INTENNOCLK +#define TFA1_BF_ISTNOCLK TFA1_BF_INTONOCLK +#define TFA1_BF_IPONOCLK TFA1_BF_INTPOLNOCLK + +/* interrupt bit fields not available on TFA1 */ +#define TFA1_BF_IECLKOOR -1 +#define TFA1_BF_ISTCLKOOR -1 +#define TFA1_BF_IEMWSRC -1 +#define TFA1_BF_ISTMWSRC -1 +#define TFA1_BF_IPOMWSRC -1 +#define TFA1_BF_IEMWSMU -1 +#define TFA1_BF_ISTMWSMU -1 +#define TFA1_BF_IPOMWSMU -1 +#define TFA1_BF_IEMWCFC -1 +#define TFA1_BF_ISTMWCFC -1 +#define TFA1_BF_IPOMWCFC -1 +#define TFA1_BF_CLKOOR -1 +#define TFA1_BF_MANWAIT1 -1 +#define TFA1_BF_MANWAIT2 -1 +#define TFA1_BF_MANMUTE -1 +#define TFA1_BF_IPCLKOOR -1 +#define TFA1_BF_ICLCLKOOR -1 +#define TFA1_BF_IPOSWS -1 +#define TFA1_BF_IESWS -1 +#define TFA1_BF_ISTSWS -1 +#define TFA1_BF_IESPKS -1 +#define TFA1_BF_ISTSPKS -1 +#define TFA1_BF_IPOSPKS -1 +#define TFA1_BF_IECLKS -1 +#define TFA1_BF_ISTCLKS -1 +#define TFA1_BF_IPOCLKS -1 +#define TFA1_BF_IEAMPS -1 +#define TFA1_BF_ISTAMPS -1 +#define TFA1_BF_IPOAMPS -1 +#define TFA1_BF_IELP0 -1 +#define TFA1_BF_ISTLP0 -1 +#define TFA1_BF_IPOLP0 -1 +#define TFA1_BF_IELP1 -1 +#define TFA1_BF_ISTLP1 -1 +#define TFA1_BF_IPOLP1 -1 +#define TFA1_BF_LP0 -1 +#define TFA1_BF_LP1 -1 +#define TFA1_BF_R25C -1 +#define TFA1_BF_SAMMODE -1 + +/* TDM STATUS fields not available on TFA1 */ +#define TFA1_BF_TDMLUTER -1 +#define TFA1_BF_TDMERR -1 diff --git a/sound/soc/codecs/inc/tfa9912_tfafieldnames.h b/sound/soc/codecs/inc/tfa9912_tfafieldnames.h new file mode 100644 index 000000000000..bc4da8ab048b --- /dev/null +++ b/sound/soc/codecs/inc/tfa9912_tfafieldnames.h @@ -0,0 +1,1764 @@ +/** Filename: tfa9912_tfafieldnames.h + * This file was generated automatically on 06/14/17 at 18:04:05. + * Source file: TFA9912_N1A_I2C_regmap_V1.41.xlsx + */ + +#ifndef _TFA9912_TFAFIELDNAMES_H +#define _TFA9912_TFAFIELDNAMES_H + + +#define TFA9912_I2CVERSION 1.41 + +typedef enum nxpTfa9912BfEnumList { + TFA9912_BF_PWDN = 0x0000, /*!< Powerdown selection */ + TFA9912_BF_I2CR = 0x0010, /*!< I2C Reset - Auto clear */ + TFA9912_BF_CFE = 0x0020, /*!< Enable CoolFlux */ + TFA9912_BF_AMPE = 0x0030, /*!< Enables the Amplifier */ + TFA9912_BF_DCA = 0x0040, /*!< Activate DC-to-DC converter */ + TFA9912_BF_SBSL = 0x0050, /*!< Coolflux configured */ + TFA9912_BF_AMPC = 0x0060, /*!< CoolFlux controls amplifier */ + TFA9912_BF_INTP = 0x0071, /*!< Interrupt config */ + TFA9912_BF_FSSSEL= 0x0090, /*!< Audio sample reference */ + TFA9912_BF_BYPOCP= 0x00b0, /*!< Bypass OCP */ + TFA9912_BF_TSTOCP= 0x00c0, /*!< OCP testing control */ + TFA9912_BF_AMPINSEL= 0x0101, /*!< Amplifier input selection */ + TFA9912_BF_MANSCONF= 0x0120, /*!< I2C configured */ + TFA9912_BF_MANCOLD= 0x0130, /*!< Execute cold start */ + TFA9912_BF_MANAOOSC= 0x0140, /*!< Internal osc off at PWDN */ + TFA9912_BF_MANROBOD= 0x0150, /*!< Reaction on BOD */ + TFA9912_BF_BODE = 0x0160, /*!< BOD Enable */ + TFA9912_BF_BODHYS= 0x0170, /*!< BOD Hysteresis */ + TFA9912_BF_BODFILT= 0x0181, /*!< BOD filter */ + TFA9912_BF_BODTHLVL= 0x01a1, /*!< BOD threshold */ + TFA9912_BF_MUTETO= 0x01d0, /*!< Time out SB mute sequence */ + TFA9912_BF_RCVNS = 0x01e0, /*!< Noise shaper selection */ + TFA9912_BF_MANWDE= 0x01f0, /*!< Watchdog enable */ + TFA9912_BF_AUDFS = 0x0203, /*!< Sample rate (fs) */ + TFA9912_BF_INPLEV= 0x0240, /*!< TDM output attenuation */ + TFA9912_BF_FRACTDEL= 0x0255, /*!< V/I Fractional delay */ + TFA9912_BF_BYPHVBF= 0x02b0, /*!< Bypass HVBAT filter */ + TFA9912_BF_TDMC = 0x02c0, /*!< TDM Compatibility with TFA9872 */ + TFA9912_BF_ENBLADC10= 0x02e0, /*!< ADC10 Enable - I2C direct mode */ + TFA9912_BF_REV = 0x030f, /*!< Revision info */ + TFA9912_BF_REFCKEXT= 0x0401, /*!< PLL external ref clock */ + TFA9912_BF_REFCKSEL= 0x0420, /*!< PLL internal ref clock */ + TFA9912_BF_ENCFCKSEL= 0x0430, /*!< Coolflux DSP clock scaling, low power mode */ + TFA9912_BF_CFCKSEL= 0x0441, /*!< Coolflux DSP clock scaler selection for low power mode */ + TFA9912_BF_TDMINFSEL= 0x0460, /*!< TDM clock selection */ + TFA9912_BF_DISBLAUTOCLKSEL= 0x0470, /*!< Disable Automatic dsp clock source selection */ + TFA9912_BF_SELCLKSRC= 0x0480, /*!< I2C selection of DSP clock when auto select is disabled */ + TFA9912_BF_SELTIMSRC= 0x0490, /*!< I2C selection of Watchdog and Timer clock */ + TFA9912_BF_SSLEFTE= 0x0500, /*!< */ + TFA9912_BF_SPKSSEN= 0x0510, /*!< Enable speaker path */ + TFA9912_BF_VSLEFTE= 0x0520, /*!< */ + TFA9912_BF_VSRIGHTE= 0x0530, /*!< Voltage sense */ + TFA9912_BF_CSLEFTE= 0x0540, /*!< */ + TFA9912_BF_CSRIGHTE= 0x0550, /*!< Current sense */ + TFA9912_BF_SSPDME= 0x0560, /*!< Sub-system PDM */ + TFA9912_BF_PGALE = 0x0570, /*!< Enable PGA chop clock for left channel */ + TFA9912_BF_PGARE = 0x0580, /*!< Enable PGA chop clock */ + TFA9912_BF_SSTDME= 0x0590, /*!< Sub-system TDM */ + TFA9912_BF_SSPBSTE= 0x05a0, /*!< Sub-system boost */ + TFA9912_BF_SSADCE= 0x05b0, /*!< Sub-system ADC */ + TFA9912_BF_SSFAIME= 0x05c0, /*!< Sub-system FAIM */ + TFA9912_BF_SSCFTIME= 0x05d0, /*!< CF Sub-system timer */ + TFA9912_BF_SSCFWDTE= 0x05e0, /*!< CF Sub-system WDT */ + TFA9912_BF_FAIMVBGOVRRL= 0x05f0, /*!< Over rule of vbg for FaIM access */ + TFA9912_BF_SAMSPKSEL= 0x0600, /*!< Input selection for TAP/SAM */ + TFA9912_BF_PDM2IISEN= 0x0610, /*!< PDM2IIS Bridge enable */ + TFA9912_BF_TAPRSTBYPASS= 0x0620, /*!< Tap decimator reset bypass - Bypass the decimator reset from tapdec */ + TFA9912_BF_CARDECISEL0= 0x0631, /*!< Cardec input 0 sel */ + TFA9912_BF_CARDECISEL1= 0x0651, /*!< Cardec input sel */ + TFA9912_BF_TAPDECSEL= 0x0670, /*!< Select TAP/Cardec for TAP */ + TFA9912_BF_COMPCOUNT= 0x0680, /*!< Comparator o/p filter selection */ + TFA9912_BF_STARTUPMODE= 0x0691, /*!< Startup Mode Selection */ + TFA9912_BF_AUTOTAP= 0x06b0, /*!< Enable auto tap switching */ + TFA9912_BF_COMPINITIME= 0x06c1, /*!< Comparator initialization time to be used in Tap Machine */ + TFA9912_BF_ANAPINITIME= 0x06e1, /*!< Analog initialization time to be used in Tap Machine */ + TFA9912_BF_CCHKTH= 0x0707, /*!< Clock check Higher Threshold */ + TFA9912_BF_CCHKTL= 0x0787, /*!< Clock check Higher Threshold */ + TFA9912_BF_AMPOCRT= 0x0802, /*!< Amplifier on-off criteria for shutdown */ + TFA9912_BF_AMPTCRR= 0x0832, /*!< Amplifier on-off criteria for tap mode entry */ + TFA9912_BF_STGS = 0x0d00, /*!< PDM side tone gain selector */ + TFA9912_BF_STGAIN= 0x0d18, /*!< Side tone gain */ + TFA9912_BF_STSMUTE= 0x0da0, /*!< Side tone soft mute */ + TFA9912_BF_ST1C = 0x0db0, /*!< side tone one s complement */ + TFA9912_BF_CMFBEL= 0x0e80, /*!< CMFB enable left */ + TFA9912_BF_VDDS = 0x1000, /*!< POR */ + TFA9912_BF_PLLS = 0x1010, /*!< PLL lock */ + TFA9912_BF_OTDS = 0x1020, /*!< OTP alarm */ + TFA9912_BF_OVDS = 0x1030, /*!< OVP alarm */ + TFA9912_BF_UVDS = 0x1040, /*!< UVP alarm */ + TFA9912_BF_CLKS = 0x1050, /*!< Clocks stable */ + TFA9912_BF_MTPB = 0x1060, /*!< MTP busy */ + TFA9912_BF_NOCLK = 0x1070, /*!< Lost clock */ + TFA9912_BF_ACS = 0x1090, /*!< Cold Start */ + TFA9912_BF_SWS = 0x10a0, /*!< Amplifier engage */ + TFA9912_BF_WDS = 0x10b0, /*!< Watchdog */ + TFA9912_BF_AMPS = 0x10c0, /*!< Amplifier enable */ + TFA9912_BF_AREFS = 0x10d0, /*!< References enable */ + TFA9912_BF_ADCCR = 0x10e0, /*!< Control ADC */ + TFA9912_BF_BODNOK= 0x10f0, /*!< BOD */ + TFA9912_BF_DCIL = 0x1100, /*!< DCDC current limiting */ + TFA9912_BF_DCDCA = 0x1110, /*!< DCDC active */ + TFA9912_BF_DCOCPOK= 0x1120, /*!< DCDC OCP nmos */ + TFA9912_BF_DCPEAKCUR= 0x1130, /*!< Indicates current is max in DC-to-DC converter */ + TFA9912_BF_DCHVBAT= 0x1140, /*!< DCDC level 1x */ + TFA9912_BF_DCH114= 0x1150, /*!< DCDC level 1.14x */ + TFA9912_BF_DCH107= 0x1160, /*!< DCDC level 1.07x */ + TFA9912_BF_STMUTEB= 0x1170, /*!< side tone (un)mute busy */ + TFA9912_BF_STMUTE= 0x1180, /*!< side tone mute state */ + TFA9912_BF_TDMLUTER= 0x1190, /*!< TDM LUT error */ + TFA9912_BF_TDMSTAT= 0x11a2, /*!< TDM status bits */ + TFA9912_BF_TDMERR= 0x11d0, /*!< TDM error */ + TFA9912_BF_HAPTIC= 0x11e0, /*!< Status haptic driver */ + TFA9912_BF_OCPOAP= 0x1300, /*!< OCPOK pmos A */ + TFA9912_BF_OCPOAN= 0x1310, /*!< OCPOK nmos A */ + TFA9912_BF_OCPOBP= 0x1320, /*!< OCPOK pmos B */ + TFA9912_BF_OCPOBN= 0x1330, /*!< OCPOK nmos B */ + TFA9912_BF_CLIPAH= 0x1340, /*!< Clipping A to Vddp */ + TFA9912_BF_CLIPAL= 0x1350, /*!< Clipping A to gnd */ + TFA9912_BF_CLIPBH= 0x1360, /*!< Clipping B to Vddp */ + TFA9912_BF_CLIPBL= 0x1370, /*!< Clipping B to gnd */ + TFA9912_BF_OCDS = 0x1380, /*!< OCP amplifier */ + TFA9912_BF_CLIPS = 0x1390, /*!< Amplifier clipping */ + TFA9912_BF_TCMPTRG= 0x13a0, /*!< Status Tap comparator triggered */ + TFA9912_BF_TAPDET= 0x13b0, /*!< Status Tap detected */ + TFA9912_BF_MANWAIT1= 0x13c0, /*!< Wait HW I2C settings */ + TFA9912_BF_MANWAIT2= 0x13d0, /*!< Wait CF config */ + TFA9912_BF_MANMUTE= 0x13e0, /*!< Audio mute sequence */ + TFA9912_BF_MANOPER= 0x13f0, /*!< Operating state */ + TFA9912_BF_SPKSL = 0x1400, /*!< Left speaker status */ + TFA9912_BF_SPKS = 0x1410, /*!< Speaker status */ + TFA9912_BF_CLKOOR= 0x1420, /*!< External clock status */ + TFA9912_BF_MANSTATE= 0x1433, /*!< Device manager status */ + TFA9912_BF_DCMODE= 0x1471, /*!< DCDC mode status bits */ + TFA9912_BF_DSPCLKSRC= 0x1490, /*!< DSP clock source selected by manager */ + TFA9912_BF_STARTUPMODSTAT= 0x14a1, /*!< Startup Mode Selected by Manager(Read Only) */ + TFA9912_BF_TSPMSTATE= 0x14c3, /*!< Tap Machine State */ + TFA9912_BF_BATS = 0x1509, /*!< Battery voltage (V) */ + TFA9912_BF_TEMPS = 0x1608, /*!< IC Temperature (C) */ + TFA9912_BF_VDDPS = 0x1709, /*!< IC VDDP voltage ( 1023*VDDP/13 V) */ + TFA9912_BF_DCILCF= 0x17a0, /*!< DCDC current limiting for DSP */ + TFA9912_BF_TDMUC = 0x2000, /*!< Mode setting */ + TFA9912_BF_DIO4SEL= 0x2011, /*!< DIO4 Input selection */ + TFA9912_BF_TDME = 0x2040, /*!< Enable TDM interface */ + TFA9912_BF_TDMMODE= 0x2050, /*!< Slave/master */ + TFA9912_BF_TDMCLINV= 0x2060, /*!< Reception data to BCK clock */ + TFA9912_BF_TDMFSLN= 0x2073, /*!< FS length */ + TFA9912_BF_TDMFSPOL= 0x20b0, /*!< FS polarity */ + TFA9912_BF_TDMNBCK= 0x20c3, /*!< N-BCK's in FS */ + TFA9912_BF_TDMSLOTS= 0x2103, /*!< N-slots in Frame */ + TFA9912_BF_TDMSLLN= 0x2144, /*!< N-bits in slot */ + TFA9912_BF_TDMBRMG= 0x2194, /*!< N-bits remaining */ + TFA9912_BF_TDMDEL= 0x21e0, /*!< data delay to FS */ + TFA9912_BF_TDMADJ= 0x21f0, /*!< data adjustment */ + TFA9912_BF_TDMOOMP= 0x2201, /*!< Received audio compression */ + TFA9912_BF_TDMSSIZE= 0x2224, /*!< Sample size per slot */ + TFA9912_BF_TDMTXDFO= 0x2271, /*!< Format unused bits in a slot */ + TFA9912_BF_TDMTXUS0= 0x2291, /*!< Format unused slots GAINIO */ + TFA9912_BF_TDMTXUS1= 0x22b1, /*!< Format unused slots DIO1 */ + TFA9912_BF_TDMTXUS2= 0x22d1, /*!< Format unused slots DIO2 */ + TFA9912_BF_TDMGIE= 0x2300, /*!< Control gain (channel in 0) */ + TFA9912_BF_TDMDCE= 0x2310, /*!< Control audio left (channel in 1 ) */ + TFA9912_BF_TDMSPKE= 0x2320, /*!< Control audio right (channel in 2 ) */ + TFA9912_BF_TDMCSE= 0x2330, /*!< Current sense */ + TFA9912_BF_TDMVSE= 0x2340, /*!< Voltage sense */ + TFA9912_BF_TDMGOE= 0x2350, /*!< DSP Gainout */ + TFA9912_BF_TDMCF2E= 0x2360, /*!< DSP 2 */ + TFA9912_BF_TDMCF3E= 0x2370, /*!< DSP 3 */ + TFA9912_BF_TDMCFE= 0x2380, /*!< DSP */ + TFA9912_BF_TDMES6= 0x2390, /*!< Loopback of Audio left (channel 1) */ + TFA9912_BF_TDMES7= 0x23a0, /*!< Loopback of Audio right (channel 2) */ + TFA9912_BF_TDMCF4E= 0x23b0, /*!< AEC ref right control */ + TFA9912_BF_TDMPD1E= 0x23c0, /*!< PDM 1 control */ + TFA9912_BF_TDMPD2E= 0x23d0, /*!< PDM 2 control */ + TFA9912_BF_TDMGIN= 0x2401, /*!< IO gainin */ + TFA9912_BF_TDMLIO= 0x2421, /*!< IO audio left */ + TFA9912_BF_TDMRIO= 0x2441, /*!< IO audio right */ + TFA9912_BF_TDMCSIO= 0x2461, /*!< IO Current Sense */ + TFA9912_BF_TDMVSIO= 0x2481, /*!< IO voltage sense */ + TFA9912_BF_TDMGOIO= 0x24a1, /*!< IO gain out */ + TFA9912_BF_TDMCFIO2= 0x24c1, /*!< IO DSP 2 */ + TFA9912_BF_TDMCFIO3= 0x24e1, /*!< IO DSP 3 */ + TFA9912_BF_TDMCFIO= 0x2501, /*!< IO DSP */ + TFA9912_BF_TDMLPB6= 0x2521, /*!< IO Source 6 */ + TFA9912_BF_TDMLPB7= 0x2541, /*!< IO Source 7 */ + TFA9912_BF_TDMGS = 0x2603, /*!< Control gainin */ + TFA9912_BF_TDMDCS= 0x2643, /*!< tdm slot for audio left (channel 1) */ + TFA9912_BF_TDMSPKS= 0x2683, /*!< tdm slot for audio right (channel 2) */ + TFA9912_BF_TDMCSS= 0x26c3, /*!< Slot Position of Current Sense Out */ + TFA9912_BF_TDMVSS= 0x2703, /*!< Slot Position of Voltage sense */ + TFA9912_BF_TDMCGOS= 0x2743, /*!< Slot Position of GAIN out */ + TFA9912_BF_TDMCF2S= 0x2783, /*!< Slot Position DSPout2 */ + TFA9912_BF_TDMCF3S= 0x27c3, /*!< Slot Position DSPout3 */ + TFA9912_BF_TDMCFS= 0x2803, /*!< Slot Position of DSPout */ + TFA9912_BF_TDMEDAT6S= 0x2843, /*!< Slot Position of loopback channel left */ + TFA9912_BF_TDMEDAT7S= 0x2883, /*!< Slot Position of loopback channel right */ + TFA9912_BF_TDMTXUS3= 0x2901, /*!< Format unused slots D3 */ + TFA9912_BF_PDMSM = 0x3100, /*!< PDM control */ + TFA9912_BF_PDMSTSEL= 0x3110, /*!< PDM Decimator input selection */ + TFA9912_BF_PDMSTENBL= 0x3120, /*!< Side tone input enable */ + TFA9912_BF_PDMLSEL= 0x3130, /*!< PDM data selection for left channel during PDM direct mode */ + TFA9912_BF_PDMRSEL= 0x3140, /*!< PDM data selection for right channel during PDM direct mode */ + TFA9912_BF_MICVDDE= 0x3150, /*!< Enable MICVDD */ + TFA9912_BF_PDMCLRAT= 0x3201, /*!< PDM BCK/Fs ratio */ + TFA9912_BF_PDMGAIN= 0x3223, /*!< PDM gain */ + TFA9912_BF_PDMOSEL= 0x3263, /*!< PDM output selection - RE/FE data combination */ + TFA9912_BF_SELCFHAPD= 0x32a0, /*!< Select the source for haptic data output (not for customer) */ + TFA9912_BF_ISTVDDS= 0x4000, /*!< Status POR */ + TFA9912_BF_ISTPLLS= 0x4010, /*!< Status PLL lock */ + TFA9912_BF_ISTOTDS= 0x4020, /*!< Status OTP alarm */ + TFA9912_BF_ISTOVDS= 0x4030, /*!< Status OVP alarm */ + TFA9912_BF_ISTUVDS= 0x4040, /*!< Status UVP alarm */ + TFA9912_BF_ISTCLKS= 0x4050, /*!< Status clocks stable */ + TFA9912_BF_ISTMTPB= 0x4060, /*!< Status MTP busy */ + TFA9912_BF_ISTNOCLK= 0x4070, /*!< Status lost clock */ + TFA9912_BF_ISTSPKS= 0x4080, /*!< Status speaker error */ + TFA9912_BF_ISTACS= 0x4090, /*!< Status cold start */ + TFA9912_BF_ISTSWS= 0x40a0, /*!< Status amplifier engage */ + TFA9912_BF_ISTWDS= 0x40b0, /*!< Status watchdog */ + TFA9912_BF_ISTAMPS= 0x40c0, /*!< Status amplifier enable */ + TFA9912_BF_ISTAREFS= 0x40d0, /*!< Status Ref enable */ + TFA9912_BF_ISTADCCR= 0x40e0, /*!< Status Control ADC */ + TFA9912_BF_ISTBODNOK= 0x40f0, /*!< Status BOD */ + TFA9912_BF_ISTBSTCU= 0x4100, /*!< Status DCDC current limiting */ + TFA9912_BF_ISTBSTHI= 0x4110, /*!< Status DCDC active */ + TFA9912_BF_ISTBSTOC= 0x4120, /*!< Status DCDC OCP */ + TFA9912_BF_ISTBSTPKCUR= 0x4130, /*!< Status bst peakcur */ + TFA9912_BF_ISTBSTVC= 0x4140, /*!< Status DCDC level 1x */ + TFA9912_BF_ISTBST86= 0x4150, /*!< Status DCDC level 1.14x */ + TFA9912_BF_ISTBST93= 0x4160, /*!< Status DCDC level 1.07x */ + TFA9912_BF_ISTRCVLD= 0x4170, /*!< Status rcvldop ready */ + TFA9912_BF_ISTOCPL= 0x4180, /*!< Status ocp alarm left */ + TFA9912_BF_ISTOCPR= 0x4190, /*!< Status ocp alarm */ + TFA9912_BF_ISTMWSRC= 0x41a0, /*!< Status Waits HW I2C settings */ + TFA9912_BF_ISTMWCFC= 0x41b0, /*!< Status waits CF config */ + TFA9912_BF_ISTMWSMU= 0x41c0, /*!< Status Audio mute sequence */ + TFA9912_BF_ISTCFMER= 0x41d0, /*!< Status cfma error */ + TFA9912_BF_ISTCFMAC= 0x41e0, /*!< Status cfma ack */ + TFA9912_BF_ISTCLKOOR= 0x41f0, /*!< Status flag_clk_out_of_range */ + TFA9912_BF_ISTTDMER= 0x4200, /*!< Status tdm error */ + TFA9912_BF_ISTCLPL= 0x4210, /*!< Status clip left */ + TFA9912_BF_ISTCLPR= 0x4220, /*!< Status clip */ + TFA9912_BF_ISTOCPM= 0x4230, /*!< Status mic ocpok */ + TFA9912_BF_ISTLP1= 0x4250, /*!< Status low power mode1 */ + TFA9912_BF_ISTLA = 0x4260, /*!< Status low amplitude detection */ + TFA9912_BF_ISTVDDP= 0x4270, /*!< Status VDDP greater than VBAT */ + TFA9912_BF_ISTTAPDET= 0x4280, /*!< Status Tap detected */ + TFA9912_BF_ISTAUDMOD= 0x4290, /*!< Status Audio Mode activated */ + TFA9912_BF_ISTSAMMOD= 0x42a0, /*!< Status SAM Mode activated */ + TFA9912_BF_ISTTAPMOD= 0x42b0, /*!< Status Tap Mode Activated */ + TFA9912_BF_ISTTAPTRG= 0x42c0, /*!< Status Tap comparator triggered */ + TFA9912_BF_ICLVDDS= 0x4400, /*!< Clear POR */ + TFA9912_BF_ICLPLLS= 0x4410, /*!< Clear PLL lock */ + TFA9912_BF_ICLOTDS= 0x4420, /*!< Clear OTP alarm */ + TFA9912_BF_ICLOVDS= 0x4430, /*!< Clear OVP alarm */ + TFA9912_BF_ICLUVDS= 0x4440, /*!< Clear UVP alarm */ + TFA9912_BF_ICLCLKS= 0x4450, /*!< Clear clocks stable */ + TFA9912_BF_ICLMTPB= 0x4460, /*!< Clear mtp busy */ + TFA9912_BF_ICLNOCLK= 0x4470, /*!< Clear lost clk */ + TFA9912_BF_ICLSPKS= 0x4480, /*!< Clear speaker error */ + TFA9912_BF_ICLACS= 0x4490, /*!< Clear cold started */ + TFA9912_BF_ICLSWS= 0x44a0, /*!< Clear amplifier engage */ + TFA9912_BF_ICLWDS= 0x44b0, /*!< Clear watchdog */ + TFA9912_BF_ICLAMPS= 0x44c0, /*!< Clear enbl amp */ + TFA9912_BF_ICLAREFS= 0x44d0, /*!< Clear ref enable */ + TFA9912_BF_ICLADCCR= 0x44e0, /*!< Clear control ADC */ + TFA9912_BF_ICLBODNOK= 0x44f0, /*!< Clear BOD */ + TFA9912_BF_ICLBSTCU= 0x4500, /*!< Clear DCDC current limiting */ + TFA9912_BF_ICLBSTHI= 0x4510, /*!< Clear DCDC active */ + TFA9912_BF_ICLBSTOC= 0x4520, /*!< Clear DCDC OCP */ + TFA9912_BF_ICLBSTPC= 0x4530, /*!< Clear bst peakcur */ + TFA9912_BF_ICLBSTVC= 0x4540, /*!< Clear DCDC level 1x */ + TFA9912_BF_ICLBST86= 0x4550, /*!< Clear DCDC level 1.14x */ + TFA9912_BF_ICLBST93= 0x4560, /*!< Clear DCDC level 1.07x */ + TFA9912_BF_ICLRCVLD= 0x4570, /*!< Clear rcvldop ready */ + TFA9912_BF_ICLOCPL= 0x4580, /*!< Clear ocp alarm left */ + TFA9912_BF_ICLOCPR= 0x4590, /*!< Clear ocp alarm */ + TFA9912_BF_ICLMWSRC= 0x45a0, /*!< Clear wait HW I2C settings */ + TFA9912_BF_ICLMWCFC= 0x45b0, /*!< Clear wait cf config */ + TFA9912_BF_ICLMWSMU= 0x45c0, /*!< Clear audio mute sequence */ + TFA9912_BF_ICLCFMER= 0x45d0, /*!< Clear cfma err */ + TFA9912_BF_ICLCFMAC= 0x45e0, /*!< Clear cfma ack */ + TFA9912_BF_ICLCLKOOR= 0x45f0, /*!< Clear flag_clk_out_of_range */ + TFA9912_BF_ICLTDMER= 0x4600, /*!< Clear tdm error */ + TFA9912_BF_ICLCLPL= 0x4610, /*!< Clear clip left */ + TFA9912_BF_ICLCLP= 0x4620, /*!< Clear clip */ + TFA9912_BF_ICLOCPM= 0x4630, /*!< Clear mic ocpok */ + TFA9912_BF_ICLLP1= 0x4650, /*!< Clear low power mode1 */ + TFA9912_BF_ICLLA = 0x4660, /*!< Clear low amplitude detection */ + TFA9912_BF_ICLVDDP= 0x4670, /*!< Clear VDDP greater then VBAT */ + TFA9912_BF_ICLTAPDET= 0x4680, /*!< Clear Tap detected */ + TFA9912_BF_ICLAUDMOD= 0x4690, /*!< Clear Audio Mode activated */ + TFA9912_BF_ICLSAMMOD= 0x46a0, /*!< Clear SAM Mode activated */ + TFA9912_BF_ICLTAPMOD= 0x46b0, /*!< Clear Tap Mode Activated */ + TFA9912_BF_ICLTAPTRG= 0x46c0, /*!< Clear Comparator Interrupt */ + TFA9912_BF_IEVDDS= 0x4800, /*!< Enable por */ + TFA9912_BF_IEPLLS= 0x4810, /*!< Enable pll lock */ + TFA9912_BF_IEOTDS= 0x4820, /*!< Enable OTP alarm */ + TFA9912_BF_IEOVDS= 0x4830, /*!< Enable OVP alarm */ + TFA9912_BF_IEUVDS= 0x4840, /*!< Enable UVP alarm */ + TFA9912_BF_IECLKS= 0x4850, /*!< Enable clocks stable */ + TFA9912_BF_IEMTPB= 0x4860, /*!< Enable mtp busy */ + TFA9912_BF_IENOCLK= 0x4870, /*!< Enable lost clk */ + TFA9912_BF_IESPKS= 0x4880, /*!< Enable speaker error */ + TFA9912_BF_IEACS = 0x4890, /*!< Enable cold started */ + TFA9912_BF_IESWS = 0x48a0, /*!< Enable amplifier engage */ + TFA9912_BF_IEWDS = 0x48b0, /*!< Enable watchdog */ + TFA9912_BF_IEAMPS= 0x48c0, /*!< Enable enbl amp */ + TFA9912_BF_IEAREFS= 0x48d0, /*!< Enable ref enable */ + TFA9912_BF_IEADCCR= 0x48e0, /*!< Enable Control ADC */ + TFA9912_BF_IEBODNOK= 0x48f0, /*!< Enable BOD */ + TFA9912_BF_IEBSTCU= 0x4900, /*!< Enable DCDC current limiting */ + TFA9912_BF_IEBSTHI= 0x4910, /*!< Enable DCDC active */ + TFA9912_BF_IEBSTOC= 0x4920, /*!< Enable DCDC OCP */ + TFA9912_BF_IEBSTPC= 0x4930, /*!< Enable bst peakcur */ + TFA9912_BF_IEBSTVC= 0x4940, /*!< Enable DCDC level 1x */ + TFA9912_BF_IEBST86= 0x4950, /*!< Enable DCDC level 1.14x */ + TFA9912_BF_IEBST93= 0x4960, /*!< Enable DCDC level 1.07x */ + TFA9912_BF_IERCVLD= 0x4970, /*!< Enable rcvldop ready */ + TFA9912_BF_IEOCPL= 0x4980, /*!< Enable ocp alarm left */ + TFA9912_BF_IEOCPR= 0x4990, /*!< Enable ocp alarm */ + TFA9912_BF_IEMWSRC= 0x49a0, /*!< Enable waits HW I2C settings */ + TFA9912_BF_IEMWCFC= 0x49b0, /*!< Enable man wait cf config */ + TFA9912_BF_IEMWSMU= 0x49c0, /*!< Enable man Audio mute sequence */ + TFA9912_BF_IECFMER= 0x49d0, /*!< Enable cfma err */ + TFA9912_BF_IECFMAC= 0x49e0, /*!< Enable cfma ack */ + TFA9912_BF_IECLKOOR= 0x49f0, /*!< Enable flag_clk_out_of_range */ + TFA9912_BF_IETDMER= 0x4a00, /*!< Enable tdm error */ + TFA9912_BF_IECLPL= 0x4a10, /*!< Enable clip left */ + TFA9912_BF_IECLPR= 0x4a20, /*!< Enable clip */ + TFA9912_BF_IEOCPM1= 0x4a30, /*!< Enable mic ocpok */ + TFA9912_BF_IELP1 = 0x4a50, /*!< Enable low power mode1 */ + TFA9912_BF_IELA = 0x4a60, /*!< Enable low amplitude detection */ + TFA9912_BF_IEVDDP= 0x4a70, /*!< Enable VDDP greater than VBAT */ + TFA9912_BF_IETAPDET= 0x4a80, /*!< Enable Tap detected */ + TFA9912_BF_IEAUDMOD= 0x4a90, /*!< Enable Audio Mode activated */ + TFA9912_BF_IESAMMOD= 0x4aa0, /*!< Enable SAM Mode activated */ + TFA9912_BF_IETAPMOD= 0x4ab0, /*!< Enable Tap Mode Activated */ + TFA9912_BF_IETAPTRG= 0x4ac0, /*!< Enable comparator interrupt */ + TFA9912_BF_IPOVDDS= 0x4c00, /*!< Polarity por */ + TFA9912_BF_IPOPLLS= 0x4c10, /*!< Polarity pll lock */ + TFA9912_BF_IPOOTDS= 0x4c20, /*!< Polarity OTP alarm */ + TFA9912_BF_IPOOVDS= 0x4c30, /*!< Polarity OVP alarm */ + TFA9912_BF_IPOUVDS= 0x4c40, /*!< Polarity UVP alarm */ + TFA9912_BF_IPOCLKS= 0x4c50, /*!< Polarity clocks stable */ + TFA9912_BF_IPOMTPB= 0x4c60, /*!< Polarity mtp busy */ + TFA9912_BF_IPONOCLK= 0x4c70, /*!< Polarity lost clk */ + TFA9912_BF_IPOSPKS= 0x4c80, /*!< Polarity speaker error */ + TFA9912_BF_IPOACS= 0x4c90, /*!< Polarity cold started */ + TFA9912_BF_IPOSWS= 0x4ca0, /*!< Polarity amplifier engage */ + TFA9912_BF_IPOWDS= 0x4cb0, /*!< Polarity watchdog */ + TFA9912_BF_IPOAMPS= 0x4cc0, /*!< Polarity enbl amp */ + TFA9912_BF_IPOAREFS= 0x4cd0, /*!< Polarity ref enable */ + TFA9912_BF_IPOADCCR= 0x4ce0, /*!< Polarity Control ADC */ + TFA9912_BF_IPOBODNOK= 0x4cf0, /*!< Polarity BOD */ + TFA9912_BF_IPOBSTCU= 0x4d00, /*!< Polarity DCDC current limiting */ + TFA9912_BF_IPOBSTHI= 0x4d10, /*!< Polarity DCDC active */ + TFA9912_BF_IPOBSTOC= 0x4d20, /*!< Polarity DCDC OCP */ + TFA9912_BF_IPOBSTPC= 0x4d30, /*!< Polarity bst peakcur */ + TFA9912_BF_IPOBSTVC= 0x4d40, /*!< Polarity DCDC level 1x */ + TFA9912_BF_IPOBST86= 0x4d50, /*!< Polarity DCDC level 1.14x */ + TFA9912_BF_IPOBST93= 0x4d60, /*!< Polarity DCDC level 1.07x */ + TFA9912_BF_IPORCVLD= 0x4d70, /*!< Polarity rcvldop ready */ + TFA9912_BF_IPOOCPL= 0x4d80, /*!< Polarity ocp alarm left */ + TFA9912_BF_IPOOCPR= 0x4d90, /*!< Polarity ocp alarm */ + TFA9912_BF_IPOMWSRC= 0x4da0, /*!< Polarity waits HW I2C settings */ + TFA9912_BF_IPOMWCFC= 0x4db0, /*!< Polarity man wait cf config */ + TFA9912_BF_IPOMWSMU= 0x4dc0, /*!< Polarity man audio mute sequence */ + TFA9912_BF_IPOCFMER= 0x4dd0, /*!< Polarity cfma err */ + TFA9912_BF_IPOCFMAC= 0x4de0, /*!< Polarity cfma ack */ + TFA9912_BF_IPOCLKOOR= 0x4df0, /*!< Polarity flag_clk_out_of_range */ + TFA9912_BF_IPOTDMER= 0x4e00, /*!< Polarity tdm error */ + TFA9912_BF_IPOCLPL= 0x4e10, /*!< Polarity clip left */ + TFA9912_BF_IPOCLPR= 0x4e20, /*!< Polarity clip */ + TFA9912_BF_IPOOCPM= 0x4e30, /*!< Polarity mic ocpok */ + TFA9912_BF_IPOLP1= 0x4e50, /*!< Polarity low power mode1 */ + TFA9912_BF_IPOLA = 0x4e60, /*!< Polarity low amplitude detection */ + TFA9912_BF_IPOVDDP= 0x4e70, /*!< Polarity VDDP greater than VBAT */ + TFA9912_BF_IPOLTAPDET= 0x4e80, /*!< PolarityTap detected */ + TFA9912_BF_IPOLAUDMOD= 0x4e90, /*!< PolarityAudio Mode activated */ + TFA9912_BF_IPOLSAMMOD= 0x4ea0, /*!< PolaritySAM Mode activated */ + TFA9912_BF_IPOLTAPMOD= 0x4eb0, /*!< Polarity Tap Mode Activated */ + TFA9912_BF_IPOLTAPTRG= 0x4ec0, /*!< PolarityTap Comparator Trigger */ + TFA9912_BF_BSSCR = 0x5001, /*!< Battery Safeguard attack time */ + TFA9912_BF_BSST = 0x5023, /*!< Battery Safeguard threshold voltage level */ + TFA9912_BF_BSSRL = 0x5061, /*!< Battery Safeguard maximum reduction */ + TFA9912_BF_BSSRR = 0x5082, /*!< Battery Safeguard release time */ + TFA9912_BF_BSSHY = 0x50b1, /*!< Battery Safeguard hysteresis */ + TFA9912_BF_BSSAC = 0x50d0, /*!< Reset clipper - Auto clear */ + TFA9912_BF_BSSR = 0x50e0, /*!< Battery voltage read out */ + TFA9912_BF_BSSBY = 0x50f0, /*!< Bypass HW clipper */ + TFA9912_BF_BSSS = 0x5100, /*!< Vbat prot steepness */ + TFA9912_BF_INTSMUTE= 0x5110, /*!< Soft mute HW */ + TFA9912_BF_CFSML = 0x5120, /*!< Soft mute FW left */ + TFA9912_BF_CFSM = 0x5130, /*!< Soft mute FW */ + TFA9912_BF_HPFBYPL= 0x5140, /*!< Bypass HPF left */ + TFA9912_BF_HPFBYP= 0x5150, /*!< Bypass HPF */ + TFA9912_BF_DPSAL = 0x5160, /*!< Enable DPSA left */ + TFA9912_BF_DPSA = 0x5170, /*!< Enable DPSA */ + TFA9912_BF_VOL = 0x5187, /*!< FW volume control for primary audio channel */ + TFA9912_BF_HNDSFRCV= 0x5200, /*!< Selection receiver */ + TFA9912_BF_CLIPCTRL= 0x5222, /*!< Clip control setting */ + TFA9912_BF_AMPGAIN= 0x5257, /*!< Amplifier gain */ + TFA9912_BF_SLOPEE= 0x52d0, /*!< Enables slope control */ + TFA9912_BF_SLOPESET= 0x52e0, /*!< Slope speed setting (bin. coded) */ + TFA9912_BF_CFTAPPAT= 0x5c07, /*!< Coolflux tap pattern */ + TFA9912_BF_TAPDBGINFO= 0x5c83, /*!< Reserved */ + TFA9912_BF_TATPSTAT1= 0x5d0f, /*!< Tap Status 1 from CF FW */ + TFA9912_BF_TCOMPTHR= 0x5f03, /*!< Comparator threshold (in uV) */ + TFA9912_BF_PGAGAIN= 0x6081, /*!< PGA gain selection */ + TFA9912_BF_TDMSPKG= 0x6123, /*!< System gain (INPLEV 0) */ + TFA9912_BF_LPM1LVL= 0x6505, /*!< low power mode1 detector ctrl threshold for low_audio_lvl */ + TFA9912_BF_LPM1HLD= 0x6565, /*!< Low power mode1 detector, ctrl hold time before low audio is reckoned to be low audio */ + TFA9912_BF_LPM1DIS= 0x65c0, /*!< low power mode1 detector control */ + TFA9912_BF_DCDIS = 0x6630, /*!< DCDC */ + TFA9912_BF_TDMSRCMAP= 0x6801, /*!< tdm source mapping */ + TFA9912_BF_TDMSRCAS= 0x6821, /*!< frame a selection */ + TFA9912_BF_TDMSRCBS= 0x6841, /*!< frame b selection */ + TFA9912_BF_ANC1C = 0x68a0, /*!< ANC one s complement */ + TFA9912_BF_SAMMODE= 0x6901, /*!< Sam mode */ + TFA9912_BF_DCMCC = 0x7033, /*!< Max coil current */ + TFA9912_BF_DCCV = 0x7071, /*!< Slope compensation current, represents LxF (inductance x frequency) value */ + TFA9912_BF_DCIE = 0x7090, /*!< Adaptive boost mode */ + TFA9912_BF_DCSR = 0x70a0, /*!< Soft ramp up/down */ + TFA9912_BF_DCINSEL= 0x70c1, /*!< DCDC IIR input Selection */ + TFA9912_BF_DCPWM = 0x70f0, /*!< DCDC PWM only mode */ + TFA9912_BF_DCTRIP= 0x7504, /*!< Adaptive boost trip levels 1, effective only when boost_intelligent is set to 1 */ + TFA9912_BF_DCTRIP2= 0x7554, /*!< Adaptive boost trip level 2, effective only when boost_intelligent is set to 1 */ + TFA9912_BF_DCTRIPT= 0x75a4, /*!< Adaptive boost trip levels, effective only when boost_intelligent is set to 1 */ + TFA9912_BF_DCVOF = 0x7635, /*!< First boost voltage level */ + TFA9912_BF_DCVOS = 0x7695, /*!< Second boost voltage level */ + TFA9912_BF_RST = 0x9000, /*!< Reset */ + TFA9912_BF_DMEM = 0x9011, /*!< Target memory */ + TFA9912_BF_AIF = 0x9030, /*!< Auto increment */ + TFA9912_BF_CFINT = 0x9040, /*!< Interrupt - auto clear */ + TFA9912_BF_CFCGATE= 0x9050, /*!< Coolflux clock gating disabling control */ + TFA9912_BF_REQCMD= 0x9080, /*!< Firmware event request rpc command */ + TFA9912_BF_REQRST= 0x9090, /*!< Firmware event request reset restart */ + TFA9912_BF_REQMIPS= 0x90a0, /*!< Firmware event request short on mips */ + TFA9912_BF_REQMUTED= 0x90b0, /*!< Firmware event request mute sequence ready */ + TFA9912_BF_REQVOL= 0x90c0, /*!< Firmware event request volume ready */ + TFA9912_BF_REQDMG= 0x90d0, /*!< Firmware event request speaker damage detected */ + TFA9912_BF_REQCAL= 0x90e0, /*!< Firmware event request calibration completed */ + TFA9912_BF_REQRSV= 0x90f0, /*!< Firmware event request reserved */ + TFA9912_BF_MADD = 0x910f, /*!< Memory address */ + TFA9912_BF_MEMA = 0x920f, /*!< Activate memory access */ + TFA9912_BF_ERR = 0x9307, /*!< Error flags */ + TFA9912_BF_ACKCMD= 0x9380, /*!< Firmware event acknowledge rpc command */ + TFA9912_BF_ACKRST= 0x9390, /*!< Firmware event acknowledge reset restart */ + TFA9912_BF_ACKMIPS= 0x93a0, /*!< Firmware event acknowledge short on mips */ + TFA9912_BF_ACKMUTED= 0x93b0, /*!< Firmware event acknowledge mute sequence ready */ + TFA9912_BF_ACKVOL= 0x93c0, /*!< Firmware event acknowledge volume ready */ + TFA9912_BF_ACKDMG= 0x93d0, /*!< Firmware event acknowledge speaker damage detected */ + TFA9912_BF_ACKCAL= 0x93e0, /*!< Firmware event acknowledge calibration completed */ + TFA9912_BF_ACKRSV= 0x93f0, /*!< Firmware event acknowledge reserved */ + TFA9912_BF_MTPK = 0xa107, /*!< MTP KEY2 register */ + TFA9912_BF_KEY1LOCKED= 0xa200, /*!< Indicates KEY1 is locked */ + TFA9912_BF_KEY2LOCKED= 0xa210, /*!< Indicates KEY2 is locked */ + TFA9912_BF_CIMTP = 0xa360, /*!< Start copying data from I2C mtp registers to mtp */ + TFA9912_BF_MTPRDMSB= 0xa50f, /*!< MSB word of MTP manual read data */ + TFA9912_BF_MTPRDLSB= 0xa60f, /*!< LSB word of MTP manual read data */ + TFA9912_BF_EXTTS = 0xb108, /*!< External temperature (C) */ + TFA9912_BF_TROS = 0xb190, /*!< Select temp Speaker calibration */ + TFA9912_BF_SWPROFIL= 0xee0f, /*!< Software profile data */ + TFA9912_BF_SWVSTEP= 0xef0f, /*!< Software vstep information */ + TFA9912_BF_MTPOTC= 0xf000, /*!< Calibration schedule */ + TFA9912_BF_MTPEX = 0xf010, /*!< Calibration Ron executed */ + TFA9912_BF_DCMCCAPI= 0xf020, /*!< Calibration current limit DCDC */ + TFA9912_BF_DCMCCSB= 0xf030, /*!< Sign bit for delta calibration current limit DCDC */ + TFA9912_BF_DCMCCCL= 0xf042, /*!< Calibration delta current limit DCDC */ + TFA9912_BF_USERDEF= 0xf078, /*!< Reserved space for allowing customer to store speaker information */ + TFA9912_BF_R25C = 0xf40f, /*!< Ron resistance of speaker coil */ +} nxpTfa9912BfEnumList_t; +#define TFA9912_NAMETABLE static tfaBfName_t Tfa9912DatasheetNames[]= {\ + { 0x0, "PWDN"}, /* Powerdown selection , */\ + { 0x10, "I2CR"}, /* I2C Reset - Auto clear , */\ + { 0x20, "CFE"}, /* Enable CoolFlux , */\ + { 0x30, "AMPE"}, /* Enables the Amplifier , */\ + { 0x40, "DCA"}, /* Activate DC-to-DC converter , */\ + { 0x50, "SBSL"}, /* Coolflux configured , */\ + { 0x60, "AMPC"}, /* CoolFlux controls amplifier , */\ + { 0x71, "INTP"}, /* Interrupt config , */\ + { 0x90, "FSSSEL"}, /* Audio sample reference , */\ + { 0xb0, "BYPOCP"}, /* Bypass OCP , */\ + { 0xc0, "TSTOCP"}, /* OCP testing control , */\ + { 0x101, "AMPINSEL"}, /* Amplifier input selection , */\ + { 0x120, "MANSCONF"}, /* I2C configured , */\ + { 0x130, "MANCOLD"}, /* Execute cold start , */\ + { 0x140, "MANAOOSC"}, /* Internal osc off at PWDN , */\ + { 0x150, "MANROBOD"}, /* Reaction on BOD , */\ + { 0x160, "BODE"}, /* BOD Enable , */\ + { 0x170, "BODHYS"}, /* BOD Hysteresis , */\ + { 0x181, "BODFILT"}, /* BOD filter , */\ + { 0x1a1, "BODTHLVL"}, /* BOD threshold , */\ + { 0x1d0, "MUTETO"}, /* Time out SB mute sequence , */\ + { 0x1e0, "RCVNS"}, /* Noise shaper selection , */\ + { 0x1f0, "MANWDE"}, /* Watchdog enable , */\ + { 0x203, "AUDFS"}, /* Sample rate (fs) , */\ + { 0x240, "INPLEV"}, /* TDM output attenuation , */\ + { 0x255, "FRACTDEL"}, /* V/I Fractional delay , */\ + { 0x2b0, "BYPHVBF"}, /* Bypass HVBAT filter , */\ + { 0x2c0, "TDMC"}, /* TDM Compatibility with TFA9872 , */\ + { 0x2e0, "ENBLADC10"}, /* ADC10 Enable - I2C direct mode , */\ + { 0x30f, "REV"}, /* Revision info , */\ + { 0x401, "REFCKEXT"}, /* PLL external ref clock , */\ + { 0x420, "REFCKSEL"}, /* PLL internal ref clock , */\ + { 0x430, "ENCFCKSEL"}, /* Coolflux DSP clock scaling, low power mode , */\ + { 0x441, "CFCKSEL"}, /* Coolflux DSP clock scaler selection for low power mode, */\ + { 0x460, "TDMINFSEL"}, /* TDM clock selection , */\ + { 0x470, "DISBLAUTOCLKSEL"}, /* Disable Automatic dsp clock source selection , */\ + { 0x480, "SELCLKSRC"}, /* I2C selection of DSP clock when auto select is disabled, */\ + { 0x490, "SELTIMSRC"}, /* I2C selection of Watchdog and Timer clock , */\ + { 0x500, "SSLEFTE"}, /* , */\ + { 0x510, "SPKSSEN"}, /* Enable speaker path , */\ + { 0x520, "VSLEFTE"}, /* , */\ + { 0x530, "VSRIGHTE"}, /* Voltage sense , */\ + { 0x540, "CSLEFTE"}, /* , */\ + { 0x550, "CSRIGHTE"}, /* Current sense , */\ + { 0x560, "SSPDME"}, /* Sub-system PDM , */\ + { 0x570, "PGALE"}, /* Enable PGA chop clock for left channel , */\ + { 0x580, "PGARE"}, /* Enable PGA chop clock , */\ + { 0x590, "SSTDME"}, /* Sub-system TDM , */\ + { 0x5a0, "SSPBSTE"}, /* Sub-system boost , */\ + { 0x5b0, "SSADCE"}, /* Sub-system ADC , */\ + { 0x5c0, "SSFAIME"}, /* Sub-system FAIM , */\ + { 0x5d0, "SSCFTIME"}, /* CF Sub-system timer , */\ + { 0x5e0, "SSCFWDTE"}, /* CF Sub-system WDT , */\ + { 0x5f0, "FAIMVBGOVRRL"}, /* Over rule of vbg for FaIM access , */\ + { 0x600, "SAMSPKSEL"}, /* Input selection for TAP/SAM , */\ + { 0x610, "PDM2IISEN"}, /* PDM2IIS Bridge enable , */\ + { 0x620, "TAPRSTBYPASS"}, /* Tap decimator reset bypass - Bypass the decimator reset from tapdec, */\ + { 0x631, "CARDECISEL0"}, /* Cardec input 0 sel , */\ + { 0x651, "CARDECISEL1"}, /* Cardec input sel , */\ + { 0x670, "TAPDECSEL"}, /* Select TAP/Cardec for TAP , */\ + { 0x680, "COMPCOUNT"}, /* Comparator o/p filter selection , */\ + { 0x691, "STARTUPMODE"}, /* Startup Mode Selection , */\ + { 0x6b0, "AUTOTAP"}, /* Enable auto tap switching , */\ + { 0x6c1, "COMPINITIME"}, /* Comparator initialization time to be used in Tap Machine, */\ + { 0x6e1, "ANAPINITIME"}, /* Analog initialization time to be used in Tap Machine, */\ + { 0x707, "CCHKTH"}, /* Clock check Higher Threshold , */\ + { 0x787, "CCHKTL"}, /* Clock check Higher Threshold , */\ + { 0x802, "AMPOCRT"}, /* Amplifier on-off criteria for shutdown , */\ + { 0x832, "AMPTCRR"}, /* Amplifier on-off criteria for tap mode entry , */\ + { 0xd00, "STGS"}, /* PDM side tone gain selector , */\ + { 0xd18, "STGAIN"}, /* Side tone gain , */\ + { 0xda0, "STSMUTE"}, /* Side tone soft mute , */\ + { 0xdb0, "ST1C"}, /* side tone one s complement , */\ + { 0xe80, "CMFBEL"}, /* CMFB enable left , */\ + { 0x1000, "VDDS"}, /* POR , */\ + { 0x1010, "PLLS"}, /* PLL lock , */\ + { 0x1020, "OTDS"}, /* OTP alarm , */\ + { 0x1030, "OVDS"}, /* OVP alarm , */\ + { 0x1040, "UVDS"}, /* UVP alarm , */\ + { 0x1050, "CLKS"}, /* Clocks stable , */\ + { 0x1060, "MTPB"}, /* MTP busy , */\ + { 0x1070, "NOCLK"}, /* Lost clock , */\ + { 0x1090, "ACS"}, /* Cold Start , */\ + { 0x10a0, "SWS"}, /* Amplifier engage , */\ + { 0x10b0, "WDS"}, /* Watchdog , */\ + { 0x10c0, "AMPS"}, /* Amplifier enable , */\ + { 0x10d0, "AREFS"}, /* References enable , */\ + { 0x10e0, "ADCCR"}, /* Control ADC , */\ + { 0x10f0, "BODNOK"}, /* BOD , */\ + { 0x1100, "DCIL"}, /* DCDC current limiting , */\ + { 0x1110, "DCDCA"}, /* DCDC active , */\ + { 0x1120, "DCOCPOK"}, /* DCDC OCP nmos , */\ + { 0x1130, "DCPEAKCUR"}, /* Indicates current is max in DC-to-DC converter , */\ + { 0x1140, "DCHVBAT"}, /* DCDC level 1x , */\ + { 0x1150, "DCH114"}, /* DCDC level 1.14x , */\ + { 0x1160, "DCH107"}, /* DCDC level 1.07x , */\ + { 0x1170, "STMUTEB"}, /* side tone (un)mute busy , */\ + { 0x1180, "STMUTE"}, /* side tone mute state , */\ + { 0x1190, "TDMLUTER"}, /* TDM LUT error , */\ + { 0x11a2, "TDMSTAT"}, /* TDM status bits , */\ + { 0x11d0, "TDMERR"}, /* TDM error , */\ + { 0x11e0, "HAPTIC"}, /* Status haptic driver , */\ + { 0x1300, "OCPOAP"}, /* OCPOK pmos A , */\ + { 0x1310, "OCPOAN"}, /* OCPOK nmos A , */\ + { 0x1320, "OCPOBP"}, /* OCPOK pmos B , */\ + { 0x1330, "OCPOBN"}, /* OCPOK nmos B , */\ + { 0x1340, "CLIPAH"}, /* Clipping A to Vddp , */\ + { 0x1350, "CLIPAL"}, /* Clipping A to gnd , */\ + { 0x1360, "CLIPBH"}, /* Clipping B to Vddp , */\ + { 0x1370, "CLIPBL"}, /* Clipping B to gnd , */\ + { 0x1380, "OCDS"}, /* OCP amplifier , */\ + { 0x1390, "CLIPS"}, /* Amplifier clipping , */\ + { 0x13a0, "TCMPTRG"}, /* Status Tap comparator triggered , */\ + { 0x13b0, "TAPDET"}, /* Status Tap detected , */\ + { 0x13c0, "MANWAIT1"}, /* Wait HW I2C settings , */\ + { 0x13d0, "MANWAIT2"}, /* Wait CF config , */\ + { 0x13e0, "MANMUTE"}, /* Audio mute sequence , */\ + { 0x13f0, "MANOPER"}, /* Operating state , */\ + { 0x1400, "SPKSL"}, /* Left speaker status , */\ + { 0x1410, "SPKS"}, /* Speaker status , */\ + { 0x1420, "CLKOOR"}, /* External clock status , */\ + { 0x1433, "MANSTATE"}, /* Device manager status , */\ + { 0x1471, "DCMODE"}, /* DCDC mode status bits , */\ + { 0x1490, "DSPCLKSRC"}, /* DSP clock source selected by manager , */\ + { 0x14a1, "STARTUPMODSTAT"}, /* Startup Mode Selected by Manager(Read Only) , */\ + { 0x14c3, "TSPMSTATE"}, /* Tap Machine State , */\ + { 0x1509, "BATS"}, /* Battery voltage (V) , */\ + { 0x1608, "TEMPS"}, /* IC Temperature (C) , */\ + { 0x1709, "VDDPS"}, /* IC VDDP voltage ( 1023*VDDP/13 V) , */\ + { 0x17a0, "DCILCF"}, /* DCDC current limiting for DSP , */\ + { 0x2000, "TDMUC"}, /* Mode setting , */\ + { 0x2011, "DIO4SEL"}, /* DIO4 Input selection , */\ + { 0x2040, "TDME"}, /* Enable TDM interface , */\ + { 0x2050, "TDMMODE"}, /* Slave/master , */\ + { 0x2060, "TDMCLINV"}, /* Reception data to BCK clock , */\ + { 0x2073, "TDMFSLN"}, /* FS length , */\ + { 0x20b0, "TDMFSPOL"}, /* FS polarity , */\ + { 0x20c3, "TDMNBCK"}, /* N-BCK's in FS , */\ + { 0x2103, "TDMSLOTS"}, /* N-slots in Frame , */\ + { 0x2144, "TDMSLLN"}, /* N-bits in slot , */\ + { 0x2194, "TDMBRMG"}, /* N-bits remaining , */\ + { 0x21e0, "TDMDEL"}, /* data delay to FS , */\ + { 0x21f0, "TDMADJ"}, /* data adjustment , */\ + { 0x2201, "TDMOOMP"}, /* Received audio compression , */\ + { 0x2224, "TDMSSIZE"}, /* Sample size per slot , */\ + { 0x2271, "TDMTXDFO"}, /* Format unused bits in a slot , */\ + { 0x2291, "TDMTXUS0"}, /* Format unused slots GAINIO , */\ + { 0x22b1, "TDMTXUS1"}, /* Format unused slots DIO1 , */\ + { 0x22d1, "TDMTXUS2"}, /* Format unused slots DIO2 , */\ + { 0x2300, "TDMGIE"}, /* Control gain (channel in 0) , */\ + { 0x2310, "TDMDCE"}, /* Control audio left (channel in 1 ) , */\ + { 0x2320, "TDMSPKE"}, /* Control audio right (channel in 2 ) , */\ + { 0x2330, "TDMCSE"}, /* Current sense , */\ + { 0x2340, "TDMVSE"}, /* Voltage sense , */\ + { 0x2350, "TDMGOE"}, /* DSP Gainout , */\ + { 0x2360, "TDMCF2E"}, /* DSP 2 , */\ + { 0x2370, "TDMCF3E"}, /* DSP 3 , */\ + { 0x2380, "TDMCFE"}, /* DSP , */\ + { 0x2390, "TDMES6"}, /* Loopback of Audio left (channel 1) , */\ + { 0x23a0, "TDMES7"}, /* Loopback of Audio right (channel 2) , */\ + { 0x23b0, "TDMCF4E"}, /* AEC ref right control , */\ + { 0x23c0, "TDMPD1E"}, /* PDM 1 control , */\ + { 0x23d0, "TDMPD2E"}, /* PDM 2 control , */\ + { 0x2401, "TDMGIN"}, /* IO gainin , */\ + { 0x2421, "TDMLIO"}, /* IO audio left , */\ + { 0x2441, "TDMRIO"}, /* IO audio right , */\ + { 0x2461, "TDMCSIO"}, /* IO Current Sense , */\ + { 0x2481, "TDMVSIO"}, /* IO voltage sense , */\ + { 0x24a1, "TDMGOIO"}, /* IO gain out , */\ + { 0x24c1, "TDMCFIO2"}, /* IO DSP 2 , */\ + { 0x24e1, "TDMCFIO3"}, /* IO DSP 3 , */\ + { 0x2501, "TDMCFIO"}, /* IO DSP , */\ + { 0x2521, "TDMLPB6"}, /* IO Source 6 , */\ + { 0x2541, "TDMLPB7"}, /* IO Source 7 , */\ + { 0x2603, "TDMGS"}, /* Control gainin , */\ + { 0x2643, "TDMDCS"}, /* tdm slot for audio left (channel 1) , */\ + { 0x2683, "TDMSPKS"}, /* tdm slot for audio right (channel 2) , */\ + { 0x26c3, "TDMCSS"}, /* Slot Position of Current Sense Out , */\ + { 0x2703, "TDMVSS"}, /* Slot Position of Voltage sense , */\ + { 0x2743, "TDMCGOS"}, /* Slot Position of GAIN out , */\ + { 0x2783, "TDMCF2S"}, /* Slot Position DSPout2 , */\ + { 0x27c3, "TDMCF3S"}, /* Slot Position DSPout3 , */\ + { 0x2803, "TDMCFS"}, /* Slot Position of DSPout , */\ + { 0x2843, "TDMEDAT6S"}, /* Slot Position of loopback channel left , */\ + { 0x2883, "TDMEDAT7S"}, /* Slot Position of loopback channel right , */\ + { 0x2901, "TDMTXUS3"}, /* Format unused slots D3 , */\ + { 0x3100, "PDMSM"}, /* PDM control , */\ + { 0x3110, "PDMSTSEL"}, /* PDM Decimator input selection , */\ + { 0x3120, "PDMSTENBL"}, /* Side tone input enable , */\ + { 0x3130, "PDMLSEL"}, /* PDM data selection for left channel during PDM direct mode, */\ + { 0x3140, "PDMRSEL"}, /* PDM data selection for right channel during PDM direct mode, */\ + { 0x3150, "MICVDDE"}, /* Enable MICVDD , */\ + { 0x3201, "PDMCLRAT"}, /* PDM BCK/Fs ratio , */\ + { 0x3223, "PDMGAIN"}, /* PDM gain , */\ + { 0x3263, "PDMOSEL"}, /* PDM output selection - RE/FE data combination , */\ + { 0x32a0, "SELCFHAPD"}, /* Select the source for haptic data output (not for customer), */\ + { 0x4000, "ISTVDDS"}, /* Status POR , */\ + { 0x4010, "ISTPLLS"}, /* Status PLL lock , */\ + { 0x4020, "ISTOTDS"}, /* Status OTP alarm , */\ + { 0x4030, "ISTOVDS"}, /* Status OVP alarm , */\ + { 0x4040, "ISTUVDS"}, /* Status UVP alarm , */\ + { 0x4050, "ISTCLKS"}, /* Status clocks stable , */\ + { 0x4060, "ISTMTPB"}, /* Status MTP busy , */\ + { 0x4070, "ISTNOCLK"}, /* Status lost clock , */\ + { 0x4080, "ISTSPKS"}, /* Status speaker error , */\ + { 0x4090, "ISTACS"}, /* Status cold start , */\ + { 0x40a0, "ISTSWS"}, /* Status amplifier engage , */\ + { 0x40b0, "ISTWDS"}, /* Status watchdog , */\ + { 0x40c0, "ISTAMPS"}, /* Status amplifier enable , */\ + { 0x40d0, "ISTAREFS"}, /* Status Ref enable , */\ + { 0x40e0, "ISTADCCR"}, /* Status Control ADC , */\ + { 0x40f0, "ISTBODNOK"}, /* Status BOD , */\ + { 0x4100, "ISTBSTCU"}, /* Status DCDC current limiting , */\ + { 0x4110, "ISTBSTHI"}, /* Status DCDC active , */\ + { 0x4120, "ISTBSTOC"}, /* Status DCDC OCP , */\ + { 0x4130, "ISTBSTPKCUR"}, /* Status bst peakcur , */\ + { 0x4140, "ISTBSTVC"}, /* Status DCDC level 1x , */\ + { 0x4150, "ISTBST86"}, /* Status DCDC level 1.14x , */\ + { 0x4160, "ISTBST93"}, /* Status DCDC level 1.07x , */\ + { 0x4170, "ISTRCVLD"}, /* Status rcvldop ready , */\ + { 0x4180, "ISTOCPL"}, /* Status ocp alarm left , */\ + { 0x4190, "ISTOCPR"}, /* Status ocp alarm , */\ + { 0x41a0, "ISTMWSRC"}, /* Status Waits HW I2C settings , */\ + { 0x41b0, "ISTMWCFC"}, /* Status waits CF config , */\ + { 0x41c0, "ISTMWSMU"}, /* Status Audio mute sequence , */\ + { 0x41d0, "ISTCFMER"}, /* Status cfma error , */\ + { 0x41e0, "ISTCFMAC"}, /* Status cfma ack , */\ + { 0x41f0, "ISTCLKOOR"}, /* Status flag_clk_out_of_range , */\ + { 0x4200, "ISTTDMER"}, /* Status tdm error , */\ + { 0x4210, "ISTCLPL"}, /* Status clip left , */\ + { 0x4220, "ISTCLPR"}, /* Status clip , */\ + { 0x4230, "ISTOCPM"}, /* Status mic ocpok , */\ + { 0x4250, "ISTLP1"}, /* Status low power mode1 , */\ + { 0x4260, "ISTLA"}, /* Status low amplitude detection , */\ + { 0x4270, "ISTVDDP"}, /* Status VDDP greater than VBAT , */\ + { 0x4280, "ISTTAPDET"}, /* Status Tap detected , */\ + { 0x4290, "ISTAUDMOD"}, /* Status Audio Mode activated , */\ + { 0x42a0, "ISTSAMMOD"}, /* Status SAM Mode activated , */\ + { 0x42b0, "ISTTAPMOD"}, /* Status Tap Mode Activated , */\ + { 0x42c0, "ISTTAPTRG"}, /* Status Tap comparator triggered , */\ + { 0x4400, "ICLVDDS"}, /* Clear POR , */\ + { 0x4410, "ICLPLLS"}, /* Clear PLL lock , */\ + { 0x4420, "ICLOTDS"}, /* Clear OTP alarm , */\ + { 0x4430, "ICLOVDS"}, /* Clear OVP alarm , */\ + { 0x4440, "ICLUVDS"}, /* Clear UVP alarm , */\ + { 0x4450, "ICLCLKS"}, /* Clear clocks stable , */\ + { 0x4460, "ICLMTPB"}, /* Clear mtp busy , */\ + { 0x4470, "ICLNOCLK"}, /* Clear lost clk , */\ + { 0x4480, "ICLSPKS"}, /* Clear speaker error , */\ + { 0x4490, "ICLACS"}, /* Clear cold started , */\ + { 0x44a0, "ICLSWS"}, /* Clear amplifier engage , */\ + { 0x44b0, "ICLWDS"}, /* Clear watchdog , */\ + { 0x44c0, "ICLAMPS"}, /* Clear enbl amp , */\ + { 0x44d0, "ICLAREFS"}, /* Clear ref enable , */\ + { 0x44e0, "ICLADCCR"}, /* Clear control ADC , */\ + { 0x44f0, "ICLBODNOK"}, /* Clear BOD , */\ + { 0x4500, "ICLBSTCU"}, /* Clear DCDC current limiting , */\ + { 0x4510, "ICLBSTHI"}, /* Clear DCDC active , */\ + { 0x4520, "ICLBSTOC"}, /* Clear DCDC OCP , */\ + { 0x4530, "ICLBSTPC"}, /* Clear bst peakcur , */\ + { 0x4540, "ICLBSTVC"}, /* Clear DCDC level 1x , */\ + { 0x4550, "ICLBST86"}, /* Clear DCDC level 1.14x , */\ + { 0x4560, "ICLBST93"}, /* Clear DCDC level 1.07x , */\ + { 0x4570, "ICLRCVLD"}, /* Clear rcvldop ready , */\ + { 0x4580, "ICLOCPL"}, /* Clear ocp alarm left , */\ + { 0x4590, "ICLOCPR"}, /* Clear ocp alarm , */\ + { 0x45a0, "ICLMWSRC"}, /* Clear wait HW I2C settings , */\ + { 0x45b0, "ICLMWCFC"}, /* Clear wait cf config , */\ + { 0x45c0, "ICLMWSMU"}, /* Clear audio mute sequence , */\ + { 0x45d0, "ICLCFMER"}, /* Clear cfma err , */\ + { 0x45e0, "ICLCFMAC"}, /* Clear cfma ack , */\ + { 0x45f0, "ICLCLKOOR"}, /* Clear flag_clk_out_of_range , */\ + { 0x4600, "ICLTDMER"}, /* Clear tdm error , */\ + { 0x4610, "ICLCLPL"}, /* Clear clip left , */\ + { 0x4620, "ICLCLP"}, /* Clear clip , */\ + { 0x4630, "ICLOCPM"}, /* Clear mic ocpok , */\ + { 0x4650, "ICLLP1"}, /* Clear low power mode1 , */\ + { 0x4660, "ICLLA"}, /* Clear low amplitude detection , */\ + { 0x4670, "ICLVDDP"}, /* Clear VDDP greater then VBAT , */\ + { 0x4680, "ICLTAPDET"}, /* Clear Tap detected , */\ + { 0x4690, "ICLAUDMOD"}, /* Clear Audio Mode activated , */\ + { 0x46a0, "ICLSAMMOD"}, /* Clear SAM Mode activated , */\ + { 0x46b0, "ICLTAPMOD"}, /* Clear Tap Mode Activated , */\ + { 0x46c0, "ICLTAPTRG"}, /* Clear Comparator Interrupt , */\ + { 0x4800, "IEVDDS"}, /* Enable por , */\ + { 0x4810, "IEPLLS"}, /* Enable pll lock , */\ + { 0x4820, "IEOTDS"}, /* Enable OTP alarm , */\ + { 0x4830, "IEOVDS"}, /* Enable OVP alarm , */\ + { 0x4840, "IEUVDS"}, /* Enable UVP alarm , */\ + { 0x4850, "IECLKS"}, /* Enable clocks stable , */\ + { 0x4860, "IEMTPB"}, /* Enable mtp busy , */\ + { 0x4870, "IENOCLK"}, /* Enable lost clk , */\ + { 0x4880, "IESPKS"}, /* Enable speaker error , */\ + { 0x4890, "IEACS"}, /* Enable cold started , */\ + { 0x48a0, "IESWS"}, /* Enable amplifier engage , */\ + { 0x48b0, "IEWDS"}, /* Enable watchdog , */\ + { 0x48c0, "IEAMPS"}, /* Enable enbl amp , */\ + { 0x48d0, "IEAREFS"}, /* Enable ref enable , */\ + { 0x48e0, "IEADCCR"}, /* Enable Control ADC , */\ + { 0x48f0, "IEBODNOK"}, /* Enable BOD , */\ + { 0x4900, "IEBSTCU"}, /* Enable DCDC current limiting , */\ + { 0x4910, "IEBSTHI"}, /* Enable DCDC active , */\ + { 0x4920, "IEBSTOC"}, /* Enable DCDC OCP , */\ + { 0x4930, "IEBSTPC"}, /* Enable bst peakcur , */\ + { 0x4940, "IEBSTVC"}, /* Enable DCDC level 1x , */\ + { 0x4950, "IEBST86"}, /* Enable DCDC level 1.14x , */\ + { 0x4960, "IEBST93"}, /* Enable DCDC level 1.07x , */\ + { 0x4970, "IERCVLD"}, /* Enable rcvldop ready , */\ + { 0x4980, "IEOCPL"}, /* Enable ocp alarm left , */\ + { 0x4990, "IEOCPR"}, /* Enable ocp alarm , */\ + { 0x49a0, "IEMWSRC"}, /* Enable waits HW I2C settings , */\ + { 0x49b0, "IEMWCFC"}, /* Enable man wait cf config , */\ + { 0x49c0, "IEMWSMU"}, /* Enable man Audio mute sequence , */\ + { 0x49d0, "IECFMER"}, /* Enable cfma err , */\ + { 0x49e0, "IECFMAC"}, /* Enable cfma ack , */\ + { 0x49f0, "IECLKOOR"}, /* Enable flag_clk_out_of_range , */\ + { 0x4a00, "IETDMER"}, /* Enable tdm error , */\ + { 0x4a10, "IECLPL"}, /* Enable clip left , */\ + { 0x4a20, "IECLPR"}, /* Enable clip , */\ + { 0x4a30, "IEOCPM1"}, /* Enable mic ocpok , */\ + { 0x4a50, "IELP1"}, /* Enable low power mode1 , */\ + { 0x4a60, "IELA"}, /* Enable low amplitude detection , */\ + { 0x4a70, "IEVDDP"}, /* Enable VDDP greater than VBAT , */\ + { 0x4a80, "IETAPDET"}, /* Enable Tap detected , */\ + { 0x4a90, "IEAUDMOD"}, /* Enable Audio Mode activated , */\ + { 0x4aa0, "IESAMMOD"}, /* Enable SAM Mode activated , */\ + { 0x4ab0, "IETAPMOD"}, /* Enable Tap Mode Activated , */\ + { 0x4ac0, "IETAPTRG"}, /* Enable comparator interrupt , */\ + { 0x4c00, "IPOVDDS"}, /* Polarity por , */\ + { 0x4c10, "IPOPLLS"}, /* Polarity pll lock , */\ + { 0x4c20, "IPOOTDS"}, /* Polarity OTP alarm , */\ + { 0x4c30, "IPOOVDS"}, /* Polarity OVP alarm , */\ + { 0x4c40, "IPOUVDS"}, /* Polarity UVP alarm , */\ + { 0x4c50, "IPOCLKS"}, /* Polarity clocks stable , */\ + { 0x4c60, "IPOMTPB"}, /* Polarity mtp busy , */\ + { 0x4c70, "IPONOCLK"}, /* Polarity lost clk , */\ + { 0x4c80, "IPOSPKS"}, /* Polarity speaker error , */\ + { 0x4c90, "IPOACS"}, /* Polarity cold started , */\ + { 0x4ca0, "IPOSWS"}, /* Polarity amplifier engage , */\ + { 0x4cb0, "IPOWDS"}, /* Polarity watchdog , */\ + { 0x4cc0, "IPOAMPS"}, /* Polarity enbl amp , */\ + { 0x4cd0, "IPOAREFS"}, /* Polarity ref enable , */\ + { 0x4ce0, "IPOADCCR"}, /* Polarity Control ADC , */\ + { 0x4cf0, "IPOBODNOK"}, /* Polarity BOD , */\ + { 0x4d00, "IPOBSTCU"}, /* Polarity DCDC current limiting , */\ + { 0x4d10, "IPOBSTHI"}, /* Polarity DCDC active , */\ + { 0x4d20, "IPOBSTOC"}, /* Polarity DCDC OCP , */\ + { 0x4d30, "IPOBSTPC"}, /* Polarity bst peakcur , */\ + { 0x4d40, "IPOBSTVC"}, /* Polarity DCDC level 1x , */\ + { 0x4d50, "IPOBST86"}, /* Polarity DCDC level 1.14x , */\ + { 0x4d60, "IPOBST93"}, /* Polarity DCDC level 1.07x , */\ + { 0x4d70, "IPORCVLD"}, /* Polarity rcvldop ready , */\ + { 0x4d80, "IPOOCPL"}, /* Polarity ocp alarm left , */\ + { 0x4d90, "IPOOCPR"}, /* Polarity ocp alarm , */\ + { 0x4da0, "IPOMWSRC"}, /* Polarity waits HW I2C settings , */\ + { 0x4db0, "IPOMWCFC"}, /* Polarity man wait cf config , */\ + { 0x4dc0, "IPOMWSMU"}, /* Polarity man audio mute sequence , */\ + { 0x4dd0, "IPOCFMER"}, /* Polarity cfma err , */\ + { 0x4de0, "IPOCFMAC"}, /* Polarity cfma ack , */\ + { 0x4df0, "IPOCLKOOR"}, /* Polarity flag_clk_out_of_range , */\ + { 0x4e00, "IPOTDMER"}, /* Polarity tdm error , */\ + { 0x4e10, "IPOCLPL"}, /* Polarity clip left , */\ + { 0x4e20, "IPOCLPR"}, /* Polarity clip , */\ + { 0x4e30, "IPOOCPM"}, /* Polarity mic ocpok , */\ + { 0x4e50, "IPOLP1"}, /* Polarity low power mode1 , */\ + { 0x4e60, "IPOLA"}, /* Polarity low amplitude detection , */\ + { 0x4e70, "IPOVDDP"}, /* Polarity VDDP greater than VBAT , */\ + { 0x4e80, "IPOLTAPDET"}, /* PolarityTap detected , */\ + { 0x4e90, "IPOLAUDMOD"}, /* PolarityAudio Mode activated , */\ + { 0x4ea0, "IPOLSAMMOD"}, /* PolaritySAM Mode activated , */\ + { 0x4eb0, "IPOLTAPMOD"}, /* Polarity Tap Mode Activated , */\ + { 0x4ec0, "IPOLTAPTRG"}, /* PolarityTap Comparator Trigger , */\ + { 0x5001, "BSSCR"}, /* Battery Safeguard attack time , */\ + { 0x5023, "BSST"}, /* Battery Safeguard threshold voltage level , */\ + { 0x5061, "BSSRL"}, /* Battery Safeguard maximum reduction , */\ + { 0x5082, "BSSRR"}, /* Battery Safeguard release time , */\ + { 0x50b1, "BSSHY"}, /* Battery Safeguard hysteresis , */\ + { 0x50d0, "BSSAC"}, /* Reset clipper - Auto clear , */\ + { 0x50e0, "BSSR"}, /* Battery voltage read out , */\ + { 0x50f0, "BSSBY"}, /* Bypass HW clipper , */\ + { 0x5100, "BSSS"}, /* Vbat prot steepness , */\ + { 0x5110, "INTSMUTE"}, /* Soft mute HW , */\ + { 0x5120, "CFSML"}, /* Soft mute FW left , */\ + { 0x5130, "CFSM"}, /* Soft mute FW , */\ + { 0x5140, "HPFBYPL"}, /* Bypass HPF left , */\ + { 0x5150, "HPFBYP"}, /* Bypass HPF , */\ + { 0x5160, "DPSAL"}, /* Enable DPSA left , */\ + { 0x5170, "DPSA"}, /* Enable DPSA , */\ + { 0x5187, "VOL"}, /* FW volume control for primary audio channel , */\ + { 0x5200, "HNDSFRCV"}, /* Selection receiver , */\ + { 0x5222, "CLIPCTRL"}, /* Clip control setting , */\ + { 0x5257, "AMPGAIN"}, /* Amplifier gain , */\ + { 0x52d0, "SLOPEE"}, /* Enables slope control , */\ + { 0x52e0, "SLOPESET"}, /* Slope speed setting (bin. coded) , */\ + { 0x5c07, "CFTAPPAT"}, /* Coolflux tap pattern , */\ + { 0x5c83, "TAPDBGINFO"}, /* Reserved , */\ + { 0x5d0f, "TATPSTAT1"}, /* Tap Status 1 from CF FW , */\ + { 0x5f03, "TCOMPTHR"}, /* Comparator threshold (in uV) , */\ + { 0x6081, "PGAGAIN"}, /* PGA gain selection , */\ + { 0x6123, "TDMSPKG"}, /* System gain (INPLEV 0) , */\ + { 0x6505, "LPM1LVL"}, /* low power mode1 detector ctrl threshold for low_audio_lvl , */\ + { 0x6565, "LPM1HLD"}, /* Low power mode1 detector, ctrl hold time before low audio is reckoned to be low audio, */\ + { 0x65c0, "LPM1DIS"}, /* low power mode1 detector control , */\ + { 0x6630, "DCDIS"}, /* DCDC , */\ + { 0x6801, "TDMSRCMAP"}, /* tdm source mapping , */\ + { 0x6821, "TDMSRCAS"}, /* frame a selection , */\ + { 0x6841, "TDMSRCBS"}, /* frame b selection , */\ + { 0x68a0, "ANC1C"}, /* ANC one s complement , */\ + { 0x6901, "SAMMODE"}, /* Sam mode , */\ + { 0x7033, "DCMCC"}, /* Max coil current , */\ + { 0x7071, "DCCV"}, /* Slope compensation current, represents LxF (inductance x frequency) value , */\ + { 0x7090, "DCIE"}, /* Adaptive boost mode , */\ + { 0x70a0, "DCSR"}, /* Soft ramp up/down , */\ + { 0x70c1, "DCINSEL"}, /* DCDC IIR input Selection , */\ + { 0x70f0, "DCPWM"}, /* DCDC PWM only mode , */\ + { 0x7504, "DCTRIP"}, /* Adaptive boost trip levels 1, effective only when boost_intelligent is set to 1, */\ + { 0x7554, "DCTRIP2"}, /* Adaptive boost trip level 2, effective only when boost_intelligent is set to 1, */\ + { 0x75a4, "DCTRIPT"}, /* Adaptive boost trip levels, effective only when boost_intelligent is set to 1, */\ + { 0x7635, "DCVOF"}, /* First boost voltage level , */\ + { 0x7695, "DCVOS"}, /* Second boost voltage level , */\ + { 0x9000, "RST"}, /* Reset , */\ + { 0x9011, "DMEM"}, /* Target memory , */\ + { 0x9030, "AIF"}, /* Auto increment , */\ + { 0x9040, "CFINT"}, /* Interrupt - auto clear , */\ + { 0x9050, "CFCGATE"}, /* Coolflux clock gating disabling control , */\ + { 0x9080, "REQCMD"}, /* Firmware event request rpc command , */\ + { 0x9090, "REQRST"}, /* Firmware event request reset restart , */\ + { 0x90a0, "REQMIPS"}, /* Firmware event request short on mips , */\ + { 0x90b0, "REQMUTED"}, /* Firmware event request mute sequence ready , */\ + { 0x90c0, "REQVOL"}, /* Firmware event request volume ready , */\ + { 0x90d0, "REQDMG"}, /* Firmware event request speaker damage detected , */\ + { 0x90e0, "REQCAL"}, /* Firmware event request calibration completed , */\ + { 0x90f0, "REQRSV"}, /* Firmware event request reserved , */\ + { 0x910f, "MADD"}, /* Memory address , */\ + { 0x920f, "MEMA"}, /* Activate memory access , */\ + { 0x9307, "ERR"}, /* Error flags , */\ + { 0x9380, "ACKCMD"}, /* Firmware event acknowledge rpc command , */\ + { 0x9390, "ACKRST"}, /* Firmware event acknowledge reset restart , */\ + { 0x93a0, "ACKMIPS"}, /* Firmware event acknowledge short on mips , */\ + { 0x93b0, "ACKMUTED"}, /* Firmware event acknowledge mute sequence ready , */\ + { 0x93c0, "ACKVOL"}, /* Firmware event acknowledge volume ready , */\ + { 0x93d0, "ACKDMG"}, /* Firmware event acknowledge speaker damage detected, */\ + { 0x93e0, "ACKCAL"}, /* Firmware event acknowledge calibration completed , */\ + { 0x93f0, "ACKRSV"}, /* Firmware event acknowledge reserved , */\ + { 0xa107, "MTPK"}, /* MTP KEY2 register , */\ + { 0xa200, "KEY1LOCKED"}, /* Indicates KEY1 is locked , */\ + { 0xa210, "KEY2LOCKED"}, /* Indicates KEY2 is locked , */\ + { 0xa360, "CIMTP"}, /* Start copying data from I2C mtp registers to mtp , */\ + { 0xa50f, "MTPRDMSB"}, /* MSB word of MTP manual read data , */\ + { 0xa60f, "MTPRDLSB"}, /* LSB word of MTP manual read data , */\ + { 0xb108, "EXTTS"}, /* External temperature (C) , */\ + { 0xb190, "TROS"}, /* Select temp Speaker calibration , */\ + { 0xee0f, "SWPROFIL"}, /* Software profile data , */\ + { 0xef0f, "SWVSTEP"}, /* Software vstep information , */\ + { 0xf000, "MTPOTC"}, /* Calibration schedule , */\ + { 0xf010, "MTPEX"}, /* Calibration Ron executed , */\ + { 0xf020, "DCMCCAPI"}, /* Calibration current limit DCDC , */\ + { 0xf030, "DCMCCSB"}, /* Sign bit for delta calibration current limit DCDC , */\ + { 0xf042, "DCMCCCL"}, /* Calibration delta current limit DCDC , */\ + { 0xf078, "USERDEF"}, /* Reserved space for allowing customer to store speaker information, */\ + { 0xf40f, "R25C"}, /* Ron resistance of speaker coil , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +#define TFA9912_BITNAMETABLE static tfaBfName_t Tfa9912BitNames[]= {\ + { 0x0, "powerdown"}, /* Powerdown selection , */\ + { 0x10, "reset"}, /* I2C Reset - Auto clear , */\ + { 0x20, "enbl_coolflux"}, /* Enable CoolFlux , */\ + { 0x30, "enbl_amplifier"}, /* Enables the Amplifier , */\ + { 0x40, "enbl_boost"}, /* Activate DC-to-DC converter , */\ + { 0x50, "coolflux_configured"}, /* Coolflux configured , */\ + { 0x60, "sel_enbl_amplifier"}, /* CoolFlux controls amplifier , */\ + { 0x71, "int_pad_io"}, /* Interrupt config , */\ + { 0x90, "fs_pulse_sel"}, /* Audio sample reference , */\ + { 0xb0, "bypass_ocp"}, /* Bypass OCP , */\ + { 0xc0, "test_ocp"}, /* OCP testing control , */\ + { 0x101, "vamp_sel"}, /* Amplifier input selection , */\ + { 0x120, "src_set_configured"}, /* I2C configured , */\ + { 0x130, "execute_cold_start"}, /* Execute cold start , */\ + { 0x140, "enbl_fro8m_auto_off"}, /* Internal osc off at PWDN , */\ + { 0x150, "man_enbl_brown_out"}, /* Reaction on BOD , */\ + { 0x160, "enbl_bod"}, /* BOD Enable , */\ + { 0x170, "enbl_bod_hyst"}, /* BOD Hysteresis , */\ + { 0x181, "bod_delay"}, /* BOD filter , */\ + { 0x1a1, "bod_lvlsel"}, /* BOD threshold , */\ + { 0x1d0, "disable_mute_time_out"}, /* Time out SB mute sequence , */\ + { 0x1e0, "pwm_sel_rcv_ns"}, /* Noise shaper selection , */\ + { 0x1f0, "man_enbl_watchdog"}, /* Watchdog enable , */\ + { 0x203, "audio_fs"}, /* Sample rate (fs) , */\ + { 0x240, "input_level"}, /* TDM output attenuation , */\ + { 0x255, "cs_frac_delay"}, /* V/I Fractional delay , */\ + { 0x2b0, "bypass_hvbat_filter"}, /* Bypass HVBAT filter , */\ + { 0x2c0, "tdm_tfa9872_compatible"}, /* TDM Compatibility with TFA9872 , */\ + { 0x2d0, "sel_hysteresis"}, /* Select hysteresis for clock range detector , */\ + { 0x2e0, "enbl_adc10"}, /* ADC10 Enable - I2C direct mode , */\ + { 0x30f, "device_rev"}, /* Revision info , */\ + { 0x401, "pll_clkin_sel"}, /* PLL external ref clock , */\ + { 0x420, "pll_clkin_sel_osc"}, /* PLL internal ref clock , */\ + { 0x430, "cf_clock_scaling"}, /* Coolflux DSP clock scaling, low power mode , */\ + { 0x441, "sel_cf_clock"}, /* Coolflux DSP clock scaler selection for low power mode, */\ + { 0x460, "tdm_intf_sel"}, /* TDM clock selection , */\ + { 0x470, "disable_auto_sel_refclk"}, /* Disable Automatic dsp clock source selection , */\ + { 0x480, "sel_clk_src"}, /* I2C selection of DSP clock when auto select is disabled, */\ + { 0x490, "wdt_tim_clk_src"}, /* I2C selection of Watchdog and Timer clock , */\ + { 0x510, "enbl_spkr_ss"}, /* Enable speaker path , */\ + { 0x530, "enbl_volsense"}, /* Voltage sense , */\ + { 0x550, "enbl_cursense"}, /* Current sense , */\ + { 0x560, "enbl_pdm_ss"}, /* Sub-system PDM , */\ + { 0x580, "enbl_pga_chop"}, /* Enable PGA chop clock , */\ + { 0x590, "enbl_tdm_ss"}, /* Sub-system TDM , */\ + { 0x5a0, "enbl_bst_ss"}, /* Sub-system boost , */\ + { 0x5b0, "enbl_adc10_ss"}, /* Sub-system ADC , */\ + { 0x5c0, "enbl_faim_ss"}, /* Sub-system FAIM , */\ + { 0x5d0, "enbl_tim_clk"}, /* CF Sub-system timer , */\ + { 0x5e0, "enbl_wdt_clk"}, /* CF Sub-system WDT , */\ + { 0x5f0, "faim_enable_vbg"}, /* Over rule of vbg for FaIM access , */\ + { 0x600, "aux_spkr_sel"}, /* Input selection for TAP/SAM , */\ + { 0x620, "bypass_tapdec_reset"}, /* Tap decimator reset bypass - Bypass the decimator reset from tapdec, */\ + { 0x631, "car_dec_in_sel0"}, /* Cardec input 0 sel , */\ + { 0x651, "car_dec_in_sel1"}, /* Cardec input sel , */\ + { 0x670, "tapdec_sel"}, /* Select TAP/Cardec for TAP , */\ + { 0x680, "comp_count"}, /* Comparator o/p filter selection , */\ + { 0x691, "startup_mode"}, /* Startup Mode Selection , */\ + { 0x6b0, "enable_auto_tap_switching"}, /* Enable auto tap switching , */\ + { 0x6c1, "comp_init_time"}, /* Comparator initialization time to be used in Tap Machine, */\ + { 0x6e1, "ana_init_time"}, /* Analog initialization time to be used in Tap Machine, */\ + { 0x707, "clkchk_th_hi"}, /* Clock check Higher Threshold , */\ + { 0x787, "clkchk_th_lo"}, /* Clock check Higher Threshold , */\ + { 0x802, "ctrl_on2off_criterion"}, /* Amplifier on-off criteria for shutdown , */\ + { 0x832, "ctrl_on2tap_criterion"}, /* Amplifier on-off criteria for tap mode entry , */\ + { 0xd00, "side_tone_gain_sel"}, /* PDM side tone gain selector , */\ + { 0xd18, "side_tone_gain"}, /* Side tone gain , */\ + { 0xda0, "mute_side_tone"}, /* Side tone soft mute , */\ + { 0xdb0, "side_tone_1scomplement"}, /* side tone one s complement , */\ + { 0xe07, "ctrl_digtoana"}, /* Spare control from digital to analog , */\ + { 0xf0f, "hidden_code"}, /* 5A6Bh, 23147d to access registers (default for engineering), */\ + { 0x1000, "flag_por"}, /* POR , */\ + { 0x1010, "flag_pll_lock"}, /* PLL lock , */\ + { 0x1020, "flag_otpok"}, /* OTP alarm , */\ + { 0x1030, "flag_ovpok"}, /* OVP alarm , */\ + { 0x1040, "flag_uvpok"}, /* UVP alarm , */\ + { 0x1050, "flag_clocks_stable"}, /* Clocks stable , */\ + { 0x1060, "flag_mtp_busy"}, /* MTP busy , */\ + { 0x1070, "flag_lost_clk"}, /* Lost clock , */\ + { 0x1090, "flag_cold_started"}, /* Cold Start , */\ + { 0x10a0, "flag_engage"}, /* Amplifier engage , */\ + { 0x10b0, "flag_watchdog_reset"}, /* Watchdog , */\ + { 0x10c0, "flag_enbl_amp"}, /* Amplifier enable , */\ + { 0x10d0, "flag_enbl_ref"}, /* References enable , */\ + { 0x10e0, "flag_adc10_ready"}, /* Control ADC , */\ + { 0x10f0, "flag_bod_vddd_nok"}, /* BOD , */\ + { 0x1100, "flag_bst_bstcur"}, /* DCDC current limiting , */\ + { 0x1110, "flag_bst_hiz"}, /* DCDC active , */\ + { 0x1120, "flag_bst_ocpok"}, /* DCDC OCP nmos , */\ + { 0x1130, "flag_bst_peakcur"}, /* Indicates current is max in DC-to-DC converter , */\ + { 0x1140, "flag_bst_voutcomp"}, /* DCDC level 1x , */\ + { 0x1150, "flag_bst_voutcomp86"}, /* DCDC level 1.14x , */\ + { 0x1160, "flag_bst_voutcomp93"}, /* DCDC level 1.07x , */\ + { 0x1170, "flag_soft_mute_busy"}, /* side tone (un)mute busy , */\ + { 0x1180, "flag_soft_mute_state"}, /* side tone mute state , */\ + { 0x1190, "flag_tdm_lut_error"}, /* TDM LUT error , */\ + { 0x11a2, "flag_tdm_status"}, /* TDM status bits , */\ + { 0x11d0, "flag_tdm_error"}, /* TDM error , */\ + { 0x1300, "flag_ocpokap"}, /* OCPOK pmos A , */\ + { 0x1310, "flag_ocpokan"}, /* OCPOK nmos A , */\ + { 0x1320, "flag_ocpokbp"}, /* OCPOK pmos B , */\ + { 0x1330, "flag_ocpokbn"}, /* OCPOK nmos B , */\ + { 0x1340, "flag_clipa_high"}, /* Clipping A to Vddp , */\ + { 0x1350, "flag_clipa_low"}, /* Clipping A to gnd , */\ + { 0x1360, "flag_clipb_high"}, /* Clipping B to Vddp , */\ + { 0x1370, "flag_clipb_low"}, /* Clipping B to gnd , */\ + { 0x1380, "flag_ocp_alarm"}, /* OCP amplifier , */\ + { 0x1390, "flag_clip"}, /* Amplifier clipping , */\ + { 0x13a0, "flag_tap_comp_trig"}, /* Status Tap comparator triggered , */\ + { 0x13b0, "flag_cf_tapdetected"}, /* Status Tap detected , */\ + { 0x13c0, "flag_man_wait_src_settings"}, /* Wait HW I2C settings , */\ + { 0x13d0, "flag_man_wait_cf_config"}, /* Wait CF config , */\ + { 0x13e0, "flag_man_start_mute_audio"}, /* Audio mute sequence , */\ + { 0x1410, "flag_cf_speakererror"}, /* Speaker status , */\ + { 0x1420, "flag_clk_out_of_range"}, /* External clock status , */\ + { 0x1433, "man_state"}, /* Device manager status , */\ + { 0x1471, "status_bst_mode"}, /* DCDC mode status bits , */\ + { 0x1490, "man_dsp_clk_src"}, /* DSP clock source selected by manager , */\ + { 0x14a1, "man_startup_mode"}, /* Startup Mode Selected by Manager(Read Only) , */\ + { 0x14c3, "tap_machine_state"}, /* Tap Machine State , */\ + { 0x1509, "bat_adc"}, /* Battery voltage (V) , */\ + { 0x1608, "temp_adc"}, /* IC Temperature (C) , */\ + { 0x1709, "vddp_adc"}, /* IC VDDP voltage ( 1023*VDDP/13 V) , */\ + { 0x17a0, "flag_bst_bstcur_cf"}, /* DCDC current limiting for DSP , */\ + { 0x2000, "tdm_usecase"}, /* Mode setting , */\ + { 0x2011, "dio4_input_sel"}, /* DIO4 Input selection , */\ + { 0x2040, "tdm_enable"}, /* Enable TDM interface , */\ + { 0x2060, "tdm_clk_inversion"}, /* Reception data to BCK clock , */\ + { 0x2073, "tdm_fs_ws_length"}, /* FS length , */\ + { 0x20b0, "tdm_fs_ws_polarity"}, /* FS polarity , */\ + { 0x20c3, "tdm_nbck"}, /* N-BCK's in FS , */\ + { 0x2103, "tdm_nb_of_slots"}, /* N-slots in Frame , */\ + { 0x2144, "tdm_slot_length"}, /* N-bits in slot , */\ + { 0x2194, "tdm_bits_remaining"}, /* N-bits remaining , */\ + { 0x21e0, "tdm_data_delay"}, /* data delay to FS , */\ + { 0x21f0, "tdm_data_adjustment"}, /* data adjustment , */\ + { 0x2201, "tdm_audio_sample_compression"}, /* Received audio compression , */\ + { 0x2224, "tdm_sample_size"}, /* Sample size per slot , */\ + { 0x2271, "tdm_txdata_format"}, /* Format unused bits in a slot , */\ + { 0x2291, "tdm_txdata_format_unused_slot_sd0"}, /* Format unused slots GAINIO , */\ + { 0x22b1, "tdm_txdata_format_unused_slot_sd1"}, /* Format unused slots DIO1 , */\ + { 0x22d1, "tdm_txdata_format_unused_slot_sd2"}, /* Format unused slots DIO2 , */\ + { 0x2300, "tdm_sink0_enable"}, /* Control gain (channel in 0) , */\ + { 0x2310, "tdm_sink1_enable"}, /* Control audio left (channel in 1 ) , */\ + { 0x2320, "tdm_sink2_enable"}, /* Control audio right (channel in 2 ) , */\ + { 0x2330, "tdm_source0_enable"}, /* Current sense , */\ + { 0x2340, "tdm_source1_enable"}, /* Voltage sense , */\ + { 0x2350, "tdm_source2_enable"}, /* DSP Gainout , */\ + { 0x2360, "tdm_source3_enable"}, /* DSP 2 , */\ + { 0x2370, "tdm_source4_enable"}, /* DSP 3 , */\ + { 0x2380, "tdm_source5_enable"}, /* DSP , */\ + { 0x2390, "tdm_source6_enable"}, /* Loopback of Audio left (channel 1) , */\ + { 0x23a0, "tdm_source7_enable"}, /* Loopback of Audio right (channel 2) , */\ + { 0x2401, "tdm_sink0_io"}, /* IO gainin , */\ + { 0x2421, "tdm_sink1_io"}, /* IO audio left , */\ + { 0x2441, "tdm_sink2_io"}, /* IO audio right , */\ + { 0x2461, "tdm_source0_io"}, /* IO Current Sense , */\ + { 0x2481, "tdm_source1_io"}, /* IO voltage sense , */\ + { 0x24a1, "tdm_source2_io"}, /* IO gain out , */\ + { 0x24c1, "tdm_source3_io"}, /* IO DSP 2 , */\ + { 0x24e1, "tdm_source4_io"}, /* IO DSP 3 , */\ + { 0x2501, "tdm_source5_io"}, /* IO DSP , */\ + { 0x2521, "tdm_source6_io"}, /* IO Source 6 , */\ + { 0x2541, "tdm_source7_io"}, /* IO Source 7 , */\ + { 0x2603, "tdm_sink0_slot"}, /* Control gainin , */\ + { 0x2643, "tdm_sink1_slot"}, /* tdm slot for audio left (channel 1) , */\ + { 0x2683, "tdm_sink2_slot"}, /* tdm slot for audio right (channel 2) , */\ + { 0x26c3, "tdm_source0_slot"}, /* Slot Position of Current Sense Out , */\ + { 0x2703, "tdm_source1_slot"}, /* Slot Position of Voltage sense , */\ + { 0x2743, "tdm_source2_slot"}, /* Slot Position of GAIN out , */\ + { 0x2783, "tdm_source3_slot"}, /* Slot Position DSPout2 , */\ + { 0x27c3, "tdm_source4_slot"}, /* Slot Position DSPout3 , */\ + { 0x2803, "tdm_source5_slot"}, /* Slot Position of DSPout , */\ + { 0x2843, "tdm_source6_slot"}, /* Slot Position of loopback channel left , */\ + { 0x2883, "tdm_source7_slot"}, /* Slot Position of loopback channel right , */\ + { 0x2901, "tdm_txdata_format_unused_slot_sd3"}, /* Format unused slots D3 , */\ + { 0x3100, "pdm_mode"}, /* PDM control , */\ + { 0x3110, "pdm_input_sel"}, /* PDM Decimator input selection , */\ + { 0x3120, "enbl_pdm_side_tone"}, /* Side tone input enable , */\ + { 0x3201, "pdm_nbck"}, /* PDM BCK/Fs ratio , */\ + { 0x4000, "int_out_flag_por"}, /* Status POR , */\ + { 0x4010, "int_out_flag_pll_lock"}, /* Status PLL lock , */\ + { 0x4020, "int_out_flag_otpok"}, /* Status OTP alarm , */\ + { 0x4030, "int_out_flag_ovpok"}, /* Status OVP alarm , */\ + { 0x4040, "int_out_flag_uvpok"}, /* Status UVP alarm , */\ + { 0x4050, "int_out_flag_clocks_stable"}, /* Status clocks stable , */\ + { 0x4060, "int_out_flag_mtp_busy"}, /* Status MTP busy , */\ + { 0x4070, "int_out_flag_lost_clk"}, /* Status lost clock , */\ + { 0x4080, "int_out_flag_cf_speakererror"}, /* Status speaker error , */\ + { 0x4090, "int_out_flag_cold_started"}, /* Status cold start , */\ + { 0x40a0, "int_out_flag_engage"}, /* Status amplifier engage , */\ + { 0x40b0, "int_out_flag_watchdog_reset"}, /* Status watchdog , */\ + { 0x40c0, "int_out_flag_enbl_amp"}, /* Status amplifier enable , */\ + { 0x40d0, "int_out_flag_enbl_ref"}, /* Status Ref enable , */\ + { 0x40e0, "int_out_flag_adc10_ready"}, /* Status Control ADC , */\ + { 0x40f0, "int_out_flag_bod_vddd_nok"}, /* Status BOD , */\ + { 0x4100, "int_out_flag_bst_bstcur"}, /* Status DCDC current limiting , */\ + { 0x4110, "int_out_flag_bst_hiz"}, /* Status DCDC active , */\ + { 0x4120, "int_out_flag_bst_ocpok"}, /* Status DCDC OCP , */\ + { 0x4130, "int_out_flag_bst_peakcur"}, /* Status bst peakcur , */\ + { 0x4140, "int_out_flag_bst_voutcomp"}, /* Status DCDC level 1x , */\ + { 0x4150, "int_out_flag_bst_voutcomp86"}, /* Status DCDC level 1.14x , */\ + { 0x4160, "int_out_flag_bst_voutcomp93"}, /* Status DCDC level 1.07x , */\ + { 0x4190, "int_out_flag_ocp_alarm"}, /* Status ocp alarm , */\ + { 0x41a0, "int_out_flag_man_wait_src_settings"}, /* Status Waits HW I2C settings , */\ + { 0x41b0, "int_out_flag_man_wait_cf_config"}, /* Status waits CF config , */\ + { 0x41c0, "int_out_flag_man_start_mute_audio"}, /* Status Audio mute sequence , */\ + { 0x41d0, "int_out_flag_cfma_err"}, /* Status cfma error , */\ + { 0x41e0, "int_out_flag_cfma_ack"}, /* Status cfma ack , */\ + { 0x41f0, "int_out_flag_clk_out_of_range"}, /* Status flag_clk_out_of_range , */\ + { 0x4200, "int_out_flag_tdm_error"}, /* Status tdm error , */\ + { 0x4220, "int_out_flag_clip"}, /* Status clip , */\ + { 0x4250, "int_out_flag_lp_detect_mode1"}, /* Status low power mode1 , */\ + { 0x4260, "int_out_flag_low_amplitude"}, /* Status low amplitude detection , */\ + { 0x4270, "int_out_flag_vddp_gt_vbat"}, /* Status VDDP greater than VBAT , */\ + { 0x4280, "int_out_newtap"}, /* Status Tap detected , */\ + { 0x4290, "int_out_audiomodeactive"}, /* Status Audio Mode activated , */\ + { 0x42a0, "int_out_sammodeactive"}, /* Status SAM Mode activated , */\ + { 0x42b0, "int_out_tapmodeactive"}, /* Status Tap Mode Activated , */\ + { 0x42c0, "int_out_flag_tap_comp_trig"}, /* Status Tap comparator triggered , */\ + { 0x4400, "int_in_flag_por"}, /* Clear POR , */\ + { 0x4410, "int_in_flag_pll_lock"}, /* Clear PLL lock , */\ + { 0x4420, "int_in_flag_otpok"}, /* Clear OTP alarm , */\ + { 0x4430, "int_in_flag_ovpok"}, /* Clear OVP alarm , */\ + { 0x4440, "int_in_flag_uvpok"}, /* Clear UVP alarm , */\ + { 0x4450, "int_in_flag_clocks_stable"}, /* Clear clocks stable , */\ + { 0x4460, "int_in_flag_mtp_busy"}, /* Clear mtp busy , */\ + { 0x4470, "int_in_flag_lost_clk"}, /* Clear lost clk , */\ + { 0x4480, "int_in_flag_cf_speakererror"}, /* Clear speaker error , */\ + { 0x4490, "int_in_flag_cold_started"}, /* Clear cold started , */\ + { 0x44a0, "int_in_flag_engage"}, /* Clear amplifier engage , */\ + { 0x44b0, "int_in_flag_watchdog_reset"}, /* Clear watchdog , */\ + { 0x44c0, "int_in_flag_enbl_amp"}, /* Clear enbl amp , */\ + { 0x44d0, "int_in_flag_enbl_ref"}, /* Clear ref enable , */\ + { 0x44e0, "int_in_flag_adc10_ready"}, /* Clear control ADC , */\ + { 0x44f0, "int_in_flag_bod_vddd_nok"}, /* Clear BOD , */\ + { 0x4500, "int_in_flag_bst_bstcur"}, /* Clear DCDC current limiting , */\ + { 0x4510, "int_in_flag_bst_hiz"}, /* Clear DCDC active , */\ + { 0x4520, "int_in_flag_bst_ocpok"}, /* Clear DCDC OCP , */\ + { 0x4530, "int_in_flag_bst_peakcur"}, /* Clear bst peakcur , */\ + { 0x4540, "int_in_flag_bst_voutcomp"}, /* Clear DCDC level 1x , */\ + { 0x4550, "int_in_flag_bst_voutcomp86"}, /* Clear DCDC level 1.14x , */\ + { 0x4560, "int_in_flag_bst_voutcomp93"}, /* Clear DCDC level 1.07x , */\ + { 0x4590, "int_in_flag_ocp_alarm"}, /* Clear ocp alarm , */\ + { 0x45a0, "int_in_flag_man_wait_src_settings"}, /* Clear wait HW I2C settings , */\ + { 0x45b0, "int_in_flag_man_wait_cf_config"}, /* Clear wait cf config , */\ + { 0x45c0, "int_in_flag_man_start_mute_audio"}, /* Clear audio mute sequence , */\ + { 0x45d0, "int_in_flag_cfma_err"}, /* Clear cfma err , */\ + { 0x45e0, "int_in_flag_cfma_ack"}, /* Clear cfma ack , */\ + { 0x45f0, "int_in_flag_clk_out_of_range"}, /* Clear flag_clk_out_of_range , */\ + { 0x4600, "int_in_flag_tdm_error"}, /* Clear tdm error , */\ + { 0x4620, "int_in_flag_clip"}, /* Clear clip , */\ + { 0x4650, "int_in_flag_lp_detect_mode1"}, /* Clear low power mode1 , */\ + { 0x4660, "int_in_flag_low_amplitude"}, /* Clear low amplitude detection , */\ + { 0x4670, "int_in_flag_vddp_gt_vbat"}, /* Clear VDDP greater then VBAT , */\ + { 0x4680, "int_in_newtap"}, /* Clear Tap detected , */\ + { 0x4690, "int_in_audiomodeactive"}, /* Clear Audio Mode activated , */\ + { 0x46a0, "int_in_sammodeactive"}, /* Clear SAM Mode activated , */\ + { 0x46b0, "int_in_tapmodeactive"}, /* Clear Tap Mode Activated , */\ + { 0x46c0, "int_in_flag_tap_comp_trig"}, /* Clear Comparator Interrupt , */\ + { 0x4800, "int_enable_flag_por"}, /* Enable por , */\ + { 0x4810, "int_enable_flag_pll_lock"}, /* Enable pll lock , */\ + { 0x4820, "int_enable_flag_otpok"}, /* Enable OTP alarm , */\ + { 0x4830, "int_enable_flag_ovpok"}, /* Enable OVP alarm , */\ + { 0x4840, "int_enable_flag_uvpok"}, /* Enable UVP alarm , */\ + { 0x4850, "int_enable_flag_clocks_stable"}, /* Enable clocks stable , */\ + { 0x4860, "int_enable_flag_mtp_busy"}, /* Enable mtp busy , */\ + { 0x4870, "int_enable_flag_lost_clk"}, /* Enable lost clk , */\ + { 0x4880, "int_enable_flag_cf_speakererror"}, /* Enable speaker error , */\ + { 0x4890, "int_enable_flag_cold_started"}, /* Enable cold started , */\ + { 0x48a0, "int_enable_flag_engage"}, /* Enable amplifier engage , */\ + { 0x48b0, "int_enable_flag_watchdog_reset"}, /* Enable watchdog , */\ + { 0x48c0, "int_enable_flag_enbl_amp"}, /* Enable enbl amp , */\ + { 0x48d0, "int_enable_flag_enbl_ref"}, /* Enable ref enable , */\ + { 0x48e0, "int_enable_flag_adc10_ready"}, /* Enable Control ADC , */\ + { 0x48f0, "int_enable_flag_bod_vddd_nok"}, /* Enable BOD , */\ + { 0x4900, "int_enable_flag_bst_bstcur"}, /* Enable DCDC current limiting , */\ + { 0x4910, "int_enable_flag_bst_hiz"}, /* Enable DCDC active , */\ + { 0x4920, "int_enable_flag_bst_ocpok"}, /* Enable DCDC OCP , */\ + { 0x4930, "int_enable_flag_bst_peakcur"}, /* Enable bst peakcur , */\ + { 0x4940, "int_enable_flag_bst_voutcomp"}, /* Enable DCDC level 1x , */\ + { 0x4950, "int_enable_flag_bst_voutcomp86"}, /* Enable DCDC level 1.14x , */\ + { 0x4960, "int_enable_flag_bst_voutcomp93"}, /* Enable DCDC level 1.07x , */\ + { 0x4990, "int_enable_flag_ocp_alarm"}, /* Enable ocp alarm , */\ + { 0x49a0, "int_enable_flag_man_wait_src_settings"}, /* Enable waits HW I2C settings , */\ + { 0x49b0, "int_enable_flag_man_wait_cf_config"}, /* Enable man wait cf config , */\ + { 0x49c0, "int_enable_flag_man_start_mute_audio"}, /* Enable man Audio mute sequence , */\ + { 0x49d0, "int_enable_flag_cfma_err"}, /* Enable cfma err , */\ + { 0x49e0, "int_enable_flag_cfma_ack"}, /* Enable cfma ack , */\ + { 0x49f0, "int_enable_flag_clk_out_of_range"}, /* Enable flag_clk_out_of_range , */\ + { 0x4a00, "int_enable_flag_tdm_error"}, /* Enable tdm error , */\ + { 0x4a20, "int_enable_flag_clip"}, /* Enable clip , */\ + { 0x4a50, "int_enable_flag_lp_detect_mode1"}, /* Enable low power mode1 , */\ + { 0x4a60, "int_enable_flag_low_amplitude"}, /* Enable low amplitude detection , */\ + { 0x4a70, "int_enable_flag_vddp_gt_vbat"}, /* Enable VDDP greater than VBAT , */\ + { 0x4a80, "int_enable_newtap"}, /* Enable Tap detected , */\ + { 0x4a90, "int_enable_audiomodeactive"}, /* Enable Audio Mode activated , */\ + { 0x4aa0, "int_enable_sammodeactive"}, /* Enable SAM Mode activated , */\ + { 0x4ab0, "int_enable_tapmodeactive"}, /* Enable Tap Mode Activated , */\ + { 0x4ac0, "int_enable_flag_tap_comp_trig"}, /* Enable comparator interrupt , */\ + { 0x4c00, "int_polarity_flag_por"}, /* Polarity por , */\ + { 0x4c10, "int_polarity_flag_pll_lock"}, /* Polarity pll lock , */\ + { 0x4c20, "int_polarity_flag_otpok"}, /* Polarity OTP alarm , */\ + { 0x4c30, "int_polarity_flag_ovpok"}, /* Polarity OVP alarm , */\ + { 0x4c40, "int_polarity_flag_uvpok"}, /* Polarity UVP alarm , */\ + { 0x4c50, "int_polarity_flag_clocks_stable"}, /* Polarity clocks stable , */\ + { 0x4c60, "int_polarity_flag_mtp_busy"}, /* Polarity mtp busy , */\ + { 0x4c70, "int_polarity_flag_lost_clk"}, /* Polarity lost clk , */\ + { 0x4c80, "int_polarity_flag_cf_speakererror"}, /* Polarity speaker error , */\ + { 0x4c90, "int_polarity_flag_cold_started"}, /* Polarity cold started , */\ + { 0x4ca0, "int_polarity_flag_engage"}, /* Polarity amplifier engage , */\ + { 0x4cb0, "int_polarity_flag_watchdog_reset"}, /* Polarity watchdog , */\ + { 0x4cc0, "int_polarity_flag_enbl_amp"}, /* Polarity enbl amp , */\ + { 0x4cd0, "int_polarity_flag_enbl_ref"}, /* Polarity ref enable , */\ + { 0x4ce0, "int_polarity_flag_adc10_ready"}, /* Polarity Control ADC , */\ + { 0x4cf0, "int_polarity_flag_bod_vddd_nok"}, /* Polarity BOD , */\ + { 0x4d00, "int_polarity_flag_bst_bstcur"}, /* Polarity DCDC current limiting , */\ + { 0x4d10, "int_polarity_flag_bst_hiz"}, /* Polarity DCDC active , */\ + { 0x4d20, "int_polarity_flag_bst_ocpok"}, /* Polarity DCDC OCP , */\ + { 0x4d30, "int_polarity_flag_bst_peakcur"}, /* Polarity bst peakcur , */\ + { 0x4d40, "int_polarity_flag_bst_voutcomp"}, /* Polarity DCDC level 1x , */\ + { 0x4d50, "int_polarity_flag_bst_voutcomp86"}, /* Polarity DCDC level 1.14x , */\ + { 0x4d60, "int_polarity_flag_bst_voutcomp93"}, /* Polarity DCDC level 1.07x , */\ + { 0x4d90, "int_polarity_flag_ocp_alarm"}, /* Polarity ocp alarm , */\ + { 0x4da0, "int_polarity_flag_man_wait_src_settings"}, /* Polarity waits HW I2C settings , */\ + { 0x4db0, "int_polarity_flag_man_wait_cf_config"}, /* Polarity man wait cf config , */\ + { 0x4dc0, "int_polarity_flag_man_start_mute_audio"}, /* Polarity man audio mute sequence , */\ + { 0x4dd0, "int_polarity_flag_cfma_err"}, /* Polarity cfma err , */\ + { 0x4de0, "int_polarity_flag_cfma_ack"}, /* Polarity cfma ack , */\ + { 0x4df0, "int_polarity_flag_clk_out_of_range"}, /* Polarity flag_clk_out_of_range , */\ + { 0x4e00, "int_polarity_flag_tdm_error"}, /* Polarity tdm error , */\ + { 0x4e20, "int_polarity_flag_clip"}, /* Polarity clip , */\ + { 0x4e50, "int_polarity_flag_lp_detect_mode1"}, /* Polarity low power mode1 , */\ + { 0x4e60, "int_polarity_flag_low_amplitude"}, /* Polarity low amplitude detection , */\ + { 0x4e70, "int_polarity_flag_vddp_gt_vbat"}, /* Polarity VDDP greater than VBAT , */\ + { 0x4e80, "int_polarity_newtap"}, /* PolarityTap detected , */\ + { 0x4e90, "int_polarity_audiomodeactive"}, /* PolarityAudio Mode activated , */\ + { 0x4ea0, "int_polarity_sammodeactive"}, /* PolaritySAM Mode activated , */\ + { 0x4eb0, "int_polarity_tapmodeactive"}, /* Polarity Tap Mode Activated , */\ + { 0x4ec0, "int_polarity_flag_tap_comp_trig"}, /* PolarityTap Comparator Trigger , */\ + { 0x5001, "vbat_prot_attack_time"}, /* Battery Safeguard attack time , */\ + { 0x5023, "vbat_prot_thlevel"}, /* Battery Safeguard threshold voltage level , */\ + { 0x5061, "vbat_prot_max_reduct"}, /* Battery Safeguard maximum reduction , */\ + { 0x5082, "vbat_prot_release_time"}, /* Battery Safeguard release time , */\ + { 0x50b1, "vbat_prot_hysterese"}, /* Battery Safeguard hysteresis , */\ + { 0x50d0, "rst_min_vbat"}, /* Reset clipper - Auto clear , */\ + { 0x50e0, "sel_vbat"}, /* Battery voltage read out , */\ + { 0x50f0, "bypass_clipper"}, /* Bypass HW clipper , */\ + { 0x5100, "batsense_steepness"}, /* Vbat prot steepness , */\ + { 0x5110, "soft_mute"}, /* Soft mute HW , */\ + { 0x5130, "cf_mute"}, /* Soft mute FW , */\ + { 0x5150, "bypass_hp"}, /* Bypass HPF , */\ + { 0x5170, "enbl_dpsa"}, /* Enable DPSA , */\ + { 0x5187, "cf_volume"}, /* FW volume control for primary audio channel , */\ + { 0x5222, "ctrl_cc"}, /* Clip control setting , */\ + { 0x5257, "gain"}, /* Amplifier gain , */\ + { 0x52d0, "ctrl_slopectrl"}, /* Enables slope control , */\ + { 0x52e0, "ctrl_slope"}, /* Slope speed setting (bin. coded) , */\ + { 0x5301, "dpsa_level"}, /* DPSA threshold levels , */\ + { 0x5321, "dpsa_release"}, /* DPSA Release time , */\ + { 0x5340, "clipfast"}, /* Clock selection for HW clipper for Battery Safeguard, */\ + { 0x5350, "bypass_lp"}, /* Bypass the low power filter inside temperature sensor, */\ + { 0x5360, "enbl_low_latency"}, /* CF low latency outputs for add module , */\ + { 0x5400, "first_order_mode"}, /* Overrule to 1st order mode of control stage when clipping, */\ + { 0x5410, "bypass_ctrlloop"}, /* Switch amplifier into open loop configuration , */\ + { 0x5430, "icomp_engage"}, /* Engage of icomp , */\ + { 0x5440, "ctrl_kickback"}, /* Prevent double pulses of output stage , */\ + { 0x5450, "icomp_engage_overrule"}, /* To overrule the functional icomp_engage signal during validation, */\ + { 0x5503, "ctrl_dem"}, /* Enable DEM icomp and DEM one bit dac , */\ + { 0x5543, "ctrl_dem_mismatch"}, /* Enable DEM icomp mismatch for testing , */\ + { 0x5582, "dpsa_drive"}, /* Drive setting (bin. coded) - I2C direct mode , */\ + { 0x570a, "enbl_amp"}, /* Switch on the class-D power sections, each part of the analog sections can be switched on/off individually , */\ + { 0x57b0, "enbl_engage"}, /* Enables/engage power stage and control loop - I2C direct mode, */\ + { 0x57c0, "enbl_engage_pst"}, /* Enables/engage power stage and control loop , */\ + { 0x5810, "hard_mute"}, /* Hard mute - PWM , */\ + { 0x5820, "pwm_shape"}, /* PWM shape , */\ + { 0x5844, "pwm_delay"}, /* PWM delay bits to set the delay, clock is 1/(k*2048*fs), */\ + { 0x5890, "reclock_pwm"}, /* Reclock the pwm signal inside analog , */\ + { 0x58a0, "reclock_voltsense"}, /* Reclock the voltage sense pwm signal , */\ + { 0x58c0, "enbl_pwm_phase_shift"}, /* Control for pwm phase shift , */\ + { 0x5c07, "flag_cf_tap_pattern"}, /* Coolflux tap pattern , */\ + { 0x5c83, "tap_debug_info"}, /* Reserved , */\ + { 0x5d0f, "tap_status_1"}, /* Tap Status 1 from CF FW , */\ + { 0x5f03, "tap_comp_threshold"}, /* Comparator threshold (in uV) , */\ + { 0x6081, "pga_gain_set"}, /* PGA gain selection , */\ + { 0x60b0, "pga_lowpass_enable"}, /* Lowpass enable , */\ + { 0x60c0, "pga_pwr_enable"}, /* PGA power enable , */\ + { 0x60d0, "pga_switch_enable"}, /* PGA switch enable , */\ + { 0x60e0, "pga_switch_aux_enable"}, /* Switch enable aux , */\ + { 0x6123, "ctrl_att"}, /* System gain (INPLEV 0) , */\ + { 0x6265, "zero_lvl"}, /* ctrl threshold for zero X-ing , */\ + { 0x62c1, "ctrl_fb_resistor"}, /* Select amplifier feedback resistor connection , */\ + { 0x62e1, "lownoisegain_mode"}, /* ctrl select mode , */\ + { 0x6305, "threshold_lvl"}, /* ctrl threshold for low_audio_lvl , */\ + { 0x6365, "hold_time"}, /* ctrl hold time before low audio is reckoned to be low audio, */\ + { 0x6405, "lpm1_cal_offset"}, /* low power mode1 detector ctrl cal_offset from gain module , */\ + { 0x6465, "lpm1_zero_lvl"}, /* low power mode1 detector ctrl threshold for zero X-ing , */\ + { 0x64e1, "lpm1_mode"}, /* low power mode1 detector ctrl select mode , */\ + { 0x6505, "lpm1_threshold_lvl"}, /* low power mode1 detector ctrl threshold for low_audio_lvl , */\ + { 0x6565, "lpm1_hold_time"}, /* Low power mode1 detector, ctrl hold time before low audio is reckoned to be low audio, */\ + { 0x65c0, "disable_low_power_mode"}, /* low power mode1 detector control , */\ + { 0x6600, "dcdc_pfm20khz_limit"}, /* DCDC in PFM mode pwm mode is activated each 50us to force a pwm pulse, */\ + { 0x6611, "dcdc_ctrl_maxzercnt"}, /* DCDC. Number of zero current flags to count before going to pfm mode, */\ + { 0x6630, "dcdcoff_mode"}, /* DCDC , */\ + { 0x6656, "dcdc_vbat_delta_detect"}, /* Threshold before booster is reacting on a delta Vbat (in PFM mode) by temporarily switching to PWM mode, */\ + { 0x66c0, "dcdc_ignore_vbat"}, /* Ignore an increase on Vbat , */\ + { 0x6700, "enbl_minion"}, /* Enables minion (small) power stage - direct ctrl , */\ + { 0x6713, "vth_vddpvbat"}, /* select vddp-vbat thres signal , */\ + { 0x6750, "lpen_vddpvbat"}, /* select vddp-vbat filtered vs unfiltered compare , */\ + { 0x6761, "ctrl_rfb"}, /* Feedback resistor selection - I2C direct mode , */\ + { 0x6801, "tdm_source_mapping"}, /* tdm source mapping , */\ + { 0x6821, "tdm_sourcea_frame_sel"}, /* frame a selection , */\ + { 0x6841, "tdm_sourceb_frame_sel"}, /* frame b selection , */\ + { 0x6901, "sam_mode"}, /* Sam mode , */\ + { 0x6931, "pdmdat_h_sel"}, /* pdm out value when pdm_clk is higth , */\ + { 0x6951, "pdmdat_l_sel"}, /* pdm out value when pdm_clk is low , */\ + { 0x6970, "cs_sam_set"}, /* Enable SAM input for current sense - I2C Direct Mode, */\ + { 0x6980, "cs_adc_nortz"}, /* Return to zero for current sense ADC , */\ + { 0x6990, "sam_spkr_sel"}, /* SAM o/p sel during SAM and audio , */\ + { 0x6b00, "disable_engage"}, /* Disable auto engage , */\ + { 0x6c02, "ns_hp2ln_criterion"}, /* 0..7 zeroes at ns as threshold to swap from high_power to low_noise, */\ + { 0x6c32, "ns_ln2hp_criterion"}, /* 0..7 zeroes at ns as threshold to swap from low_noise to high_power, */\ + { 0x6c60, "sel_clip_pwms"}, /* To select clip-flags , */\ + { 0x6c72, "pwms_clip_lvl"}, /* To set the amount of pwm pulse that may be skipped before clip-flag is triggered. , */\ + { 0x6ca5, "spare_out"}, /* spare_out , */\ + { 0x6d0f, "spare_in"}, /* spare_in , */\ + { 0x6e10, "flag_lp_detect_mode1"}, /* low power mode 1 detection , */\ + { 0x6e20, "flag_low_amplitude"}, /* low amplitude detection , */\ + { 0x6e30, "flag_vddp_gt_vbat"}, /* vddp greater than vbat , */\ + { 0x7033, "boost_cur"}, /* Max coil current , */\ + { 0x7071, "bst_slpcmplvl"}, /* Slope compensation current, represents LxF (inductance x frequency) value , */\ + { 0x7090, "boost_intel"}, /* Adaptive boost mode , */\ + { 0x70a0, "boost_speed"}, /* Soft ramp up/down , */\ + { 0x70c1, "dcdc_sel"}, /* DCDC IIR input Selection , */\ + { 0x70f0, "dcdc_pwmonly"}, /* DCDC PWM only mode , */\ + { 0x7104, "bst_drive"}, /* Binary coded drive setting for boost converter power stage, */\ + { 0x7151, "bst_scalecur"}, /* For testing direct control scale current , */\ + { 0x7174, "bst_slopecur"}, /* For testing direct control slope current - I2C direct mode, */\ + { 0x71c1, "bst_slope"}, /* Boost slope speed , */\ + { 0x71e0, "bst_bypass_bstcur"}, /* Bypass control for boost current settings , */\ + { 0x71f0, "bst_bypass_bstfoldback"}, /* Bypass control for boost foldback , */\ + { 0x7200, "enbl_bst_engage"}, /* Enable power stage dcdc controller - I2C direct mode, */\ + { 0x7210, "enbl_bst_hizcom"}, /* Enable hiz comparator - I2C direct mode , */\ + { 0x7220, "enbl_bst_peak2avg"}, /* Enable boost peak2avg functionality , */\ + { 0x7230, "enbl_bst_peakcur"}, /* Enable peak current - I2C direct mode , */\ + { 0x7240, "enbl_bst_power"}, /* Enable line of the powerstage - I2C direct mode , */\ + { 0x7250, "enbl_bst_slopecur"}, /* Enable bit of max-current dac - I2C direct mode , */\ + { 0x7260, "enbl_bst_voutcomp"}, /* Enable vout comparators - I2C direct mode , */\ + { 0x7270, "enbl_bst_voutcomp86"}, /* Enable vout-86 comparators - I2C direct mode , */\ + { 0x7280, "enbl_bst_voutcomp93"}, /* Enable vout-93 comparators - I2C direct mode , */\ + { 0x7290, "enbl_bst_windac"}, /* Enable window dac - I2C direct mode , */\ + { 0x72a5, "bst_windac"}, /* for testing direct control windac - I2C direct mode, */\ + { 0x7300, "boost_alg"}, /* Control for boost adaptive loop gain , */\ + { 0x7311, "boost_loopgain"}, /* DCDC boost loopgain setting , */\ + { 0x7331, "bst_freq"}, /* DCDC boost frequency control , */\ + { 0x7430, "boost_track"}, /* Boost algorithm selection, effective only when boost_intelligent is set to 1, */\ + { 0x7494, "boost_hold_time"}, /* Hold time for DCDC booster, effective only when boost_intelligent is set to 1, */\ + { 0x74e0, "sel_dcdc_envelope_8fs"}, /* Selection of data for adaptive boost algorithm, effective only when boost_intelligent is set to 1, */\ + { 0x74f0, "ignore_flag_voutcomp86"}, /* Ignore flag_voutcomp86 , */\ + { 0x7504, "boost_trip_lvl_1st"}, /* Adaptive boost trip levels 1, effective only when boost_intelligent is set to 1, */\ + { 0x7554, "boost_trip_lvl_2nd"}, /* Adaptive boost trip level 2, effective only when boost_intelligent is set to 1, */\ + { 0x75a4, "boost_trip_lvl_track"}, /* Adaptive boost trip levels, effective only when boost_intelligent is set to 1, */\ + { 0x7602, "track_decay"}, /* DCDC Boost decay speed after a peak value, effective only when boost_track is set to 1, */\ + { 0x7635, "frst_boost_voltage"}, /* First boost voltage level , */\ + { 0x7695, "scnd_boost_voltage"}, /* Second boost voltage level , */\ + { 0x7720, "pga_test_ldo_bypass"}, /* bypass internal PGA LDO , */\ + { 0x8001, "sel_clk_cs"}, /* Current sense clock duty cycle control , */\ + { 0x8021, "micadc_speed"}, /* Current sense clock for MiCADC selection - 32/44.1/48 KHz Fs band only, */\ + { 0x8050, "cs_gain_control"}, /* Current sense gain control , */\ + { 0x8060, "cs_bypass_gc"}, /* Bypasses the CS gain correction , */\ + { 0x8087, "cs_gain"}, /* Current sense gain , */\ + { 0x8200, "enbl_cmfb"}, /* Current sense common mode feedback control , */\ + { 0x8210, "invertpwm"}, /* Current sense common mode feedback pwm invert control, */\ + { 0x8222, "cmfb_gain"}, /* Current sense common mode feedback control gain , */\ + { 0x8256, "cmfb_offset"}, /* Current sense common mode feedback control offset , */\ + { 0x8305, "cs_ktemp"}, /* First order temperature compensation coefficient , */\ + { 0x8364, "cs_ktemp2"}, /* Second order temperature compensation coefficient , */\ + { 0x8400, "cs_adc_bsoinv"}, /* Bitstream inversion for current sense ADC , */\ + { 0x8421, "cs_adc_hifreq"}, /* Frequency mode current sense ADC , */\ + { 0x8453, "cs_adc_offset"}, /* Micadc ADC offset setting , */\ + { 0x8490, "cs_adc_slowdel"}, /* Select delay for current sense ADC (internal decision circuitry), */\ + { 0x84a4, "cs_adc_gain"}, /* Gain setting for current sense ADC (two's complement), */\ + { 0x8500, "cs_resonator_enable"}, /* Enable for resonator to improve SRN , */\ + { 0x8510, "cs_classd_tran_skip"}, /* Skip current sense connection during a classD amplifier transition, */\ + { 0x8530, "cs_inn_short"}, /* Short current sense negative to common mode , */\ + { 0x8540, "cs_inp_short"}, /* Short current sense positive to common mode , */\ + { 0x8550, "cs_ldo_bypass"}, /* Bypass current sense LDO , */\ + { 0x8560, "cs_ldo_pulldown"}, /* Pull down current sense LDO, only valid if left_enbl_cs_ldo is high, */\ + { 0x8574, "cs_ldo_voset"}, /* Current sense LDO voltage level setting (two's complement), */\ + { 0x8700, "enbl_cs_adc"}, /* Enable current sense ADC - I2C direct mode , */\ + { 0x8710, "enbl_cs_inn1"}, /* Enable connection of current sense negative1 - I2C direct mode, */\ + { 0x8720, "enbl_cs_inn2"}, /* Enable connection of current sense negative2 - I2C direct mode, */\ + { 0x8730, "enbl_cs_inp1"}, /* Enable connection of current sense positive1 , */\ + { 0x8740, "enbl_cs_inp2"}, /* Enable connection of current sense positive2 - I2C direct mode, */\ + { 0x8750, "enbl_cs_ldo"}, /* Enable current sense LDO - I2C direct mode , */\ + { 0x8760, "enbl_cs_nofloating_n"}, /* Connect current sense negative to gnda at transitions of booster or classd amplifiers. Otherwise floating (0), */\ + { 0x8770, "enbl_cs_nofloating_p"}, /* Connect current sense positive to gnda at transitions of booster or classd amplifiers. Otherwise floating (0), */\ + { 0x8780, "enbl_cs_vbatldo"}, /* Enable of current sense LDO -- I2C direct mode , */\ + { 0x8800, "volsense_pwm_sel"}, /* Voltage sense PWM source selection control , */\ + { 0x8810, "vol_cur_sense_dc_offset"}, /* voltage and current sense decimator offset control, */\ + { 0x8902, "cursense_comp_delay"}, /* To align compensation signal with current sense signal, */\ + { 0x8930, "cursense_comp_sign"}, /* To change polarity of compensation for current sense compensation, */\ + { 0x8940, "enbl_cursense_comp"}, /* To enable current sense compensation , */\ + { 0x9000, "cf_rst_dsp"}, /* Reset , */\ + { 0x9011, "cf_dmem"}, /* Target memory , */\ + { 0x9030, "cf_aif"}, /* Auto increment , */\ + { 0x9040, "cf_int"}, /* Interrupt - auto clear , */\ + { 0x9050, "cf_cgate_off"}, /* Coolflux clock gating disabling control , */\ + { 0x9080, "cf_req_cmd"}, /* Firmware event request rpc command , */\ + { 0x9090, "cf_req_reset"}, /* Firmware event request reset restart , */\ + { 0x90a0, "cf_req_mips"}, /* Firmware event request short on mips , */\ + { 0x90b0, "cf_req_mute_ready"}, /* Firmware event request mute sequence ready , */\ + { 0x90c0, "cf_req_volume_ready"}, /* Firmware event request volume ready , */\ + { 0x90d0, "cf_req_damage"}, /* Firmware event request speaker damage detected , */\ + { 0x90e0, "cf_req_calibrate_ready"}, /* Firmware event request calibration completed , */\ + { 0x90f0, "cf_req_reserved"}, /* Firmware event request reserved , */\ + { 0x910f, "cf_madd"}, /* Memory address , */\ + { 0x920f, "cf_mema"}, /* Activate memory access , */\ + { 0x9307, "cf_err"}, /* Error flags , */\ + { 0x9380, "cf_ack_cmd"}, /* Firmware event acknowledge rpc command , */\ + { 0x9390, "cf_ack_reset"}, /* Firmware event acknowledge reset restart , */\ + { 0x93a0, "cf_ack_mips"}, /* Firmware event acknowledge short on mips , */\ + { 0x93b0, "cf_ack_mute_ready"}, /* Firmware event acknowledge mute sequence ready , */\ + { 0x93c0, "cf_ack_volume_ready"}, /* Firmware event acknowledge volume ready , */\ + { 0x93d0, "cf_ack_damage"}, /* Firmware event acknowledge speaker damage detected, */\ + { 0x93e0, "cf_ack_calibrate_ready"}, /* Firmware event acknowledge calibration completed , */\ + { 0x93f0, "cf_ack_reserved"}, /* Firmware event acknowledge reserved , */\ + { 0xa007, "mtpkey1"}, /* 5Ah, 90d To access KEY1_Protected registers (Default for engineering), */\ + { 0xa107, "mtpkey2"}, /* MTP KEY2 register , */\ + { 0xa200, "key01_locked"}, /* Indicates KEY1 is locked , */\ + { 0xa210, "key02_locked"}, /* Indicates KEY2 is locked , */\ + { 0xa302, "mtp_man_address_in"}, /* MTP address from I2C register for read/writing mtp in manual single word mode, */\ + { 0xa330, "man_copy_mtp_to_iic"}, /* Start copying single word from mtp to I2C mtp register, */\ + { 0xa340, "man_copy_iic_to_mtp"}, /* Start copying single word from I2C mtp register to mtp, */\ + { 0xa350, "auto_copy_mtp_to_iic"}, /* Start copying all the data from mtp to I2C mtp registers, */\ + { 0xa360, "auto_copy_iic_to_mtp"}, /* Start copying data from I2C mtp registers to mtp , */\ + { 0xa400, "faim_set_clkws"}, /* Sets the FaIM controller clock wait state register, */\ + { 0xa410, "faim_sel_evenrows"}, /* All even rows of the FaIM are selected, active high, */\ + { 0xa420, "faim_sel_oddrows"}, /* All odd rows of the FaIM are selected, all rows in combination with sel_evenrows, */\ + { 0xa430, "faim_program_only"}, /* Skip the erase access at wr_faim command (write-program-marginread), */\ + { 0xa440, "faim_erase_only"}, /* Skip the program access at wr_faim command (write-erase-marginread), */\ + { 0xa50f, "mtp_man_data_out_msb"}, /* MSB word of MTP manual read data , */\ + { 0xa60f, "mtp_man_data_out_lsb"}, /* LSB word of MTP manual read data , */\ + { 0xa70f, "mtp_man_data_in_msb"}, /* MSB word of write data for MTP manual write , */\ + { 0xa80f, "mtp_man_data_in_lsb"}, /* LSB word of write data for MTP manual write , */\ + { 0xb010, "bypass_ocpcounter"}, /* Bypass OCP Counter , */\ + { 0xb020, "bypass_glitchfilter"}, /* Bypass glitch filter , */\ + { 0xb030, "bypass_ovp"}, /* Bypass OVP , */\ + { 0xb040, "bypass_uvp"}, /* Bypass UVP , */\ + { 0xb050, "bypass_otp"}, /* Bypass OTP , */\ + { 0xb060, "bypass_lost_clk"}, /* Bypass lost clock detector , */\ + { 0xb070, "ctrl_vpalarm"}, /* vpalarm (uvp ovp handling) , */\ + { 0xb087, "ocp_threshold"}, /* OCP threshold level , */\ + { 0xb108, "ext_temp"}, /* External temperature (C) , */\ + { 0xb190, "ext_temp_sel"}, /* Select temp Speaker calibration , */\ + { 0xc000, "use_direct_ctrls"}, /* Direct control to overrule several functions for testing, */\ + { 0xc010, "rst_datapath"}, /* Direct control for datapath reset , */\ + { 0xc020, "rst_cgu"}, /* Direct control for cgu reset , */\ + { 0xc038, "enbl_ref"}, /* Switch on the analog references, each part of the references can be switched on/off individually - - I2C direct mode, */\ + { 0xc0d0, "enbl_ringo"}, /* Enable the ring oscillator for test purpose , */\ + { 0xc0e0, "use_direct_clk_ctrl"}, /* Direct clock control to overrule several functions for testing, */\ + { 0xc0f0, "use_direct_pll_ctrl"}, /* Direct PLL control to overrule several functions for testing, */\ + { 0xc100, "enbl_tsense"}, /* Temperature sensor enable control - I2C direct mode, */\ + { 0xc110, "tsense_hibias"}, /* Bit to set the biasing in temp sensor to high - I2C direct mode, */\ + { 0xc120, "enbl_flag_vbg"}, /* Enable flagging of bandgap out of control , */\ + { 0xc130, "tap_comp_enable"}, /* Tap Comparator enable control - I2C direct mode , */\ + { 0xc140, "tap_comp_switch_enable"}, /* Tap Comparator Switch enable control - I2C direct mode, */\ + { 0xc150, "tap_comp_switch_aux_enable"}, /* Tap Comparator Switch enable control - I2C direct mode, */\ + { 0xc161, "tap_comp_test_enable"}, /* Comparator threshold - fine value , */\ + { 0xc180, "curdist_enable"}, /* Enable control - I2C direct mode , */\ + { 0xc190, "vbg2i_enbl"}, /* Enable control - I2C direct mode , */\ + { 0xc1a0, "bg_filt_bypass_enbl"}, /* Enable control , */\ + { 0xc20f, "abist_offset"}, /* Offset control for ABIST testing (two's complement), */\ + { 0xc300, "bypasslatch"}, /* Bypass latch , */\ + { 0xc311, "sourcea"}, /* Set OUTA to , */\ + { 0xc331, "sourceb"}, /* Set OUTB to , */\ + { 0xc350, "inverta"}, /* Invert pwma test signal , */\ + { 0xc360, "invertb"}, /* Invert pwmb test signal , */\ + { 0xc374, "pulselength"}, /* Pulse length setting test input for amplifier (clock d - k*2048*fs), */\ + { 0xc3c0, "tdm_enable_loopback"}, /* TDM loopback test , */\ + { 0xc3d0, "test_abistfft_enbl"}, /* FFT Coolflux , */\ + { 0xc3e0, "test_pwr_switch"}, /* Test mode for digital power switches core sw/mem sw/micvdd sw, */\ + { 0xc400, "bst_bypasslatch"}, /* Bypass latch in boost converter , */\ + { 0xc411, "bst_source"}, /* Sets the source of the pwmbst output to boost converter input for testing, */\ + { 0xc430, "bst_invertb"}, /* Invert pwmbst test signal , */\ + { 0xc444, "bst_pulselength"}, /* Pulse length setting test input for boost converter , */\ + { 0xc490, "test_bst_ctrlsthv"}, /* Test mode for boost control stage , */\ + { 0xc4a0, "test_bst_iddq"}, /* IDDQ testing in power stage of boost converter , */\ + { 0xc4b0, "test_bst_rdson"}, /* RDSON testing - boost power stage , */\ + { 0xc4c0, "test_bst_cvi"}, /* CVI testing - boost power stage , */\ + { 0xc4d0, "test_bst_ocp"}, /* Boost OCP. For old ocp (ctrl_reversebst is 0), For new ocp (ctrl_reversebst is 1), */\ + { 0xc4e0, "test_bst_sense"}, /* Test option for the sense NMOS in booster for current mode control., */\ + { 0xc500, "test_cvi"}, /* Analog BIST, switch choose which transistor will be used as current source (also cross coupled sources possible), */\ + { 0xc510, "test_discrete"}, /* Test function noise measurement , */\ + { 0xc520, "test_iddq"}, /* Set the power stages in iddq mode for gate stress., */\ + { 0xc540, "test_rdson"}, /* Analog BIST, switch to enable Rdson measurement , */\ + { 0xc550, "test_sdelta"}, /* Analog BIST, noise test , */\ + { 0xc560, "bypass_fro8"}, /* Bypass fro8 with pdm_clk , */\ + { 0xc570, "test_enbl_cs"}, /* Enable for digimux mode of current sense , */\ + { 0xc5b0, "pga_test_enable"}, /* Enable PGA test mode , */\ + { 0xc5c0, "pga_test_offset_enable"}, /* Enable PGA test offset , */\ + { 0xc5d0, "pga_test_shortinput_enable"}, /* Enable PGA test short input , */\ + { 0xc600, "enbl_pwm_dcc"}, /* Enables direct control of pwm duty cycle for DCDC power stage, */\ + { 0xc613, "pwm_dcc_cnt"}, /* Control pwm duty cycle when enbl_pwm_dcc is 1 , */\ + { 0xc650, "enbl_ldo_stress"}, /* Enable stress of internal supply voltages powerstages, */\ + { 0xc660, "enbl_powerswitch"}, /* Vddd core power switch control - overrules the manager control, */\ + { 0xc707, "digimuxa_sel"}, /* DigimuxA input selection control routed to DIO4 (see Digimux list for details), */\ + { 0xc787, "digimuxb_sel"}, /* DigimuxB input selection control routed to DIO3 (see Digimux list for details), */\ + { 0xc807, "digimuxc_sel"}, /* DigimuxC input selection control routed to TDO (see Digimux list for details), */\ + { 0xc901, "dio1_ehs"}, /* Speed/load setting for DIO1 IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc921, "dio2_ehs"}, /* Speed/load setting for DIO2 IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc941, "dio3_ehs"}, /* Speed/load setting for DIO3 cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc961, "dio4_ehs"}, /* Speed/load setting for DIO4 IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc981, "spdmo_ehs"}, /* Speed/load setting for PDMO IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc9a1, "tdo_ehs"}, /* Speed/load setting for TDM IO cell, clk or data mode range (see SLIMMF IO cell datasheet), */\ + { 0xc9c0, "int_ehs"}, /* Slew Rate INT IO cell, clk or data mode range (see IIC3V3 IO cell datasheet), */\ + { 0xc9d0, "pdmclk_ehs"}, /* Slew RateBCK2/PDMCLK IO cell, clk or data mode range (see IIC3V3 IO cell datasheet), */\ + { 0xc9e0, "fs2_ehs"}, /* Slew Rate DS2 IO cell, clk or data mode range (see IIC3V3 IO cell datasheet), */\ + { 0xc9f0, "hs_mode"}, /* I2C high speed mode control , */\ + { 0xca00, "enbl_anamux1"}, /* Enable anamux1 , */\ + { 0xca10, "enbl_anamux2"}, /* Enable anamux2 , */\ + { 0xca20, "enbl_anamux3"}, /* Enable anamux3 , */\ + { 0xca30, "enbl_anamux4"}, /* Enable anamux4 , */\ + { 0xca74, "anamux1"}, /* Anamux selection control - anamux on TEST1 , */\ + { 0xcb04, "anamux2"}, /* Anamux selection control - anamux on TEST2 , */\ + { 0xcb54, "anamux3"}, /* Anamux selection control - anamux on TEST3 , */\ + { 0xcba4, "anamux4"}, /* Anamux selection control - anamux on TEST4 , */\ + { 0xcc05, "pll_seli_lbw"}, /* PLL SELI - Low B/W PLL control mode or I2C direct PLL control mode only, */\ + { 0xcc64, "pll_selp_lbw"}, /* PLL SELP - Low B/W PLL control mode or I2C direct PLL control mode only, */\ + { 0xccb3, "pll_selr_lbw"}, /* PLL SELR - Low B/W PLL control mode or I2C direct PLL control mode only, */\ + { 0xccf0, "sel_user_pll_bw"}, /* PLL Low Bandwidth Mode control , */\ + { 0xcdf0, "pll_frm"}, /* PLL free running mode control; 1 in TCB direct control mode, else this control bit, */\ + { 0xce09, "pll_ndec"}, /* PLL NDEC - I2C direct PLL control mode only , */\ + { 0xcea0, "pll_mdec_msb"}, /* MSB of pll_mdec - I2C direct PLL control mode only, */\ + { 0xceb0, "enbl_pll"}, /* Enables PLL in I2C direct PLL control mode only , */\ + { 0xcec0, "enbl_fro8"}, /* Enables FRO8M in I2C direct control mode only , */\ + { 0xced0, "pll_bypass"}, /* PLL bypass control in I2C direct PLL control mode only, */\ + { 0xcee0, "pll_directi"}, /* PLL directi control in I2C direct PLL control mode only, */\ + { 0xcef0, "pll_directo"}, /* PLL directo control in I2C direct PLL control mode only, */\ + { 0xcf0f, "pll_mdec_lsb"}, /* Bits 15..0 of PLL MDEC are I2C direct PLL control mode only, */\ + { 0xd006, "pll_pdec"}, /* PLL PDEC - I2C direct PLL control mode only , */\ + { 0xd10f, "tsig_freq_lsb"}, /* Internal sinus test generator frequency control , */\ + { 0xd202, "tsig_freq_msb"}, /* Select internal sinus test generator, frequency control msb bits, */\ + { 0xd230, "inject_tsig"}, /* Control bit to switch to internal sinus test generator, */\ + { 0xd283, "tsig_gain"}, /* Test signal gain , */\ + { 0xd300, "adc10_reset"}, /* Reset for ADC10 - I2C direct control mode , */\ + { 0xd311, "adc10_test"}, /* Test mode selection signal for ADC10 - I2C direct control mode, */\ + { 0xd332, "adc10_sel"}, /* Select the input to convert for ADC10 - I2C direct control mode, */\ + { 0xd364, "adc10_prog_sample"}, /* ADC10 program sample setting - I2C direct control mode, */\ + { 0xd3b0, "adc10_enbl"}, /* Enable ADC10 - I2C direct control mode , */\ + { 0xd3c0, "bypass_lp_vbat"}, /* Bypass control for Low pass filter in batt sensor , */\ + { 0xd409, "data_adc10_tempbat"}, /* ADC 10 data output data for testing , */\ + { 0xd507, "ctrl_digtoana_hidden"}, /* Spare digital to analog control bits - Hidden , */\ + { 0xd580, "enbl_clk_range_chk"}, /* Clock out of range , */\ + { 0xd601, "clkdiv_dsp_sel"}, /* DSP clock divider selection in direct clock control mode, */\ + { 0xd621, "clkdiv_audio_sel"}, /* Audio clock divider selection in direct clock control mode, */\ + { 0xd641, "clkdiv_muxa_sel"}, /* DCDC MUXA clock divider selection in direct clock control mode, */\ + { 0xd661, "clkdiv_muxb_sel"}, /* DCDC MUXB clock divider selection in direct clock control mode, */\ + { 0xd681, "dsp_tap_clk"}, /* Dsp clock frequency selection in TAP mode; , */\ + { 0xd6a1, "sel_wdt_clk"}, /* Watch dog clock post divider value , */\ + { 0xd6c1, "sel_tim_clk"}, /* Timer clock post divider value , */\ + { 0xd700, "ads1_ehs"}, /* Slew Rate ADS1 IO cell, clk or data mode range (see IIC3V3 IO cell datasheet), */\ + { 0xd710, "ads2_ehs"}, /* Slew Rate ADS2 IO cell, clk or data mode range (see IIC3V3 IO cell datasheet), */\ + { 0xd822, "test_parametric_io"}, /* test io parametric , */\ + { 0xd850, "ctrl_bst_clk_lp1"}, /* boost clock control in low power mode1 , */\ + { 0xd861, "test_spare_out1"}, /* test spare out 1 , */\ + { 0xd880, "bst_dcmbst"}, /* dcm boost - I2C direct mode , */\ + { 0xd8a1, "force_pga_clock"}, /* force pga clock , */\ + { 0xd8c3, "test_spare_out2"}, /* test spare out 1 , */\ + { 0xd900, "overrules_usercase"}, /* Overrule Mode control use , */\ + { 0xd910, "ovr_switch_ref"}, /* Overrule Value , */\ + { 0xd920, "ovr_enbl_pll"}, /* Overrule Value , */\ + { 0xd930, "ovr_switch_amp"}, /* Overrule Value , */\ + { 0xd940, "ovr_enbl_clk_cs"}, /* Overrule Value , */\ + { 0xd951, "ovr_sel_clk_cs"}, /* CS clock selection overrule , */\ + { 0xd970, "ovr_switch_cs"}, /* Overrule Value , */\ + { 0xd980, "ovr_enbl_csvs_ss"}, /* Overrule Value , */\ + { 0xd990, "ovr_enbl_comp"}, /* Overrule Value , */\ + { 0xed00, "enbl_fro8cal"}, /* Enable FRO calibration , */\ + { 0xed10, "start_fro8_calibration"}, /* Start FRO8 Calibration , */\ + { 0xed20, "fro8_calibration_done"}, /* FRO8 Calibration done - Read Only , */\ + { 0xed45, "fro8_auto_trim_val"}, /* Calibration value from Auto Calibration block, to be written into MTP - Read Only, */\ + { 0xee0f, "sw_profile"}, /* Software profile data , */\ + { 0xef0f, "sw_vstep"}, /* Software vstep information , */\ + { 0xf000, "calibration_onetime"}, /* Calibration schedule , */\ + { 0xf010, "calibr_ron_done"}, /* Calibration Ron executed , */\ + { 0xf020, "calibr_dcdc_api_calibrate"}, /* Calibration current limit DCDC , */\ + { 0xf030, "calibr_dcdc_delta_sign"}, /* Sign bit for delta calibration current limit DCDC , */\ + { 0xf042, "calibr_dcdc_delta"}, /* Calibration delta current limit DCDC , */\ + { 0xf078, "calibr_speaker_info"}, /* Reserved space for allowing customer to store speaker information, */\ + { 0xf105, "calibr_vout_offset"}, /* DCDC offset calibration 2's complement (key1 protected), */\ + { 0xf163, "spare_mtp1_9_6"}, /* HW gain module - left channel (2's complement) , */\ + { 0xf1a5, "spare_mtp1_15_10"}, /* Offset for amplifier, HW gain module - left channel (2's complement), */\ + { 0xf203, "calibr_gain"}, /* HW gain module (2's complement) , */\ + { 0xf245, "calibr_offset"}, /* Offset for amplifier, HW gain module (2's complement), */\ + { 0xf2a3, "spare_mtp2_13_10"}, /* Trimming of LDO (2.7V) , */\ + { 0xf307, "spare_mtp3_7_0"}, /* SPARE , */\ + { 0xf387, "calibr_gain_cs"}, /* Current sense gain (signed two's complement format), */\ + { 0xf40f, "calibr_R25C"}, /* Ron resistance of speaker coil , */\ + { 0xf50f, "spare_mtp5_15_0"}, /* SPARE , */\ + { 0xf600, "mtp_lock_enbl_coolflux"}, /* Disable function dcdcoff_mode , */\ + { 0xf610, "mtp_pwm_delay_enbl_clk_auto_gating"}, /* Auto clock gating on pwm_delay , */\ + { 0xf620, "mtp_ocp_enbl_clk_auto_gating"}, /* Auto clock gating on module ocp , */\ + { 0xf630, "mtp_disable_clk_a_gating"}, /* Disable clock_a gating , */\ + { 0xf642, "spare_mtp6_6_3"}, /* SPARE , */\ + { 0xf686, "spare_mtp6_14_8"}, /* Offset of left amplifier level shifter B , */\ + { 0xf706, "ctrl_offset_a"}, /* Offset of level shifter A , */\ + { 0xf786, "ctrl_offset_b"}, /* Offset of amplifier level shifter B , */\ + { 0xf806, "htol_iic_addr"}, /* 7-bit I2C address to be used during HTOL testing , */\ + { 0xf870, "htol_iic_addr_en"}, /* HTOL I2C address enable control , */\ + { 0xf884, "calibr_temp_offset"}, /* Temperature offset 2's compliment (key1 protected), */\ + { 0xf8d2, "calibr_temp_gain"}, /* Temperature gain 2's compliment (key1 protected) , */\ + { 0xf910, "disable_sam_mode"}, /* Disable sam mode , */\ + { 0xf920, "mtp_lock_bypass_clipper"}, /* Disable function bypass_clipper , */\ + { 0xf930, "mtp_lock_max_dcdc_voltage"}, /* Disable programming of max dcdc boost voltage , */\ + { 0xf943, "calibr_vbg_trim"}, /* Bandgap trimming control , */\ + { 0xf987, "type_bits_fw"}, /* MTP-control FW - See Firmware I2C API document for details, */\ + { 0xfa0f, "mtpdataA"}, /* MTPdataA (key1 protected) , */\ + { 0xfb0f, "mtpdataB"}, /* MTPdataB (key1 protected) , */\ + { 0xfc0f, "mtpdataC"}, /* MTPdataC (key1 protected) , */\ + { 0xfd0f, "mtpdataD"}, /* MTPdataD (key1 protected) , */\ + { 0xfe0f, "mtpdataE"}, /* MTPdataE (key1 protected) , */\ + { 0xff05, "fro8_trim"}, /* 8 MHz oscillator trim code , */\ + { 0xff61, "fro8_short_nwell_r"}, /* Short 4 or 6 n-well resistors , */\ + { 0xff81, "fro8_boost_i"}, /* Self bias current selection , */\ + { 0xffff,"Unknown bitfield enum" } /* not found */\ +}; + +enum tfa9912_irq { + tfa9912_irq_stvdds = 0, + tfa9912_irq_stplls = 1, + tfa9912_irq_stotds = 2, + tfa9912_irq_stovds = 3, + tfa9912_irq_stuvds = 4, + tfa9912_irq_stclks = 5, + tfa9912_irq_stmtpb = 6, + tfa9912_irq_stnoclk = 7, + tfa9912_irq_stspks = 8, + tfa9912_irq_stacs = 9, + tfa9912_irq_stsws = 10, + tfa9912_irq_stwds = 11, + tfa9912_irq_stamps = 12, + tfa9912_irq_starefs = 13, + tfa9912_irq_stadccr = 14, + tfa9912_irq_stbodnok = 15, + tfa9912_irq_stbstcu = 16, + tfa9912_irq_stbsthi = 17, + tfa9912_irq_stbstoc = 18, + tfa9912_irq_stbstpkcur = 19, + tfa9912_irq_stbstvc = 20, + tfa9912_irq_stbst86 = 21, + tfa9912_irq_stbst93 = 22, + tfa9912_irq_strcvld = 23, + tfa9912_irq_stocpl = 24, + tfa9912_irq_stocpr = 25, + tfa9912_irq_stmwsrc = 26, + tfa9912_irq_stmwcfc = 27, + tfa9912_irq_stmwsmu = 28, + tfa9912_irq_stcfmer = 29, + tfa9912_irq_stcfmac = 30, + tfa9912_irq_stclkoor = 31, + tfa9912_irq_sttdmer = 32, + tfa9912_irq_stclpl = 33, + tfa9912_irq_stclpr = 34, + tfa9912_irq_stocpm = 35, + tfa9912_irq_stlp1 = 37, + tfa9912_irq_stla = 38, + tfa9912_irq_stvddp = 39, + tfa9912_irq_sttapdet = 40, + tfa9912_irq_staudmod = 41, + tfa9912_irq_stsammod = 42, + tfa9912_irq_sttapmod = 43, + tfa9912_irq_sttaptrg = 44, + tfa9912_irq_max = 45, + tfa9912_irq_all = -1 /* all irqs */}; + +#define TFA9912_IRQ_NAMETABLE static tfaIrqName_t Tfa9912IrqNames[]= {\ + { 0, "STVDDS"},\ + { 1, "STPLLS"},\ + { 2, "STOTDS"},\ + { 3, "STOVDS"},\ + { 4, "STUVDS"},\ + { 5, "STCLKS"},\ + { 6, "STMTPB"},\ + { 7, "STNOCLK"},\ + { 8, "STSPKS"},\ + { 9, "STACS"},\ + { 10, "STSWS"},\ + { 11, "STWDS"},\ + { 12, "STAMPS"},\ + { 13, "STAREFS"},\ + { 14, "STADCCR"},\ + { 15, "STBODNOK"},\ + { 16, "STBSTCU"},\ + { 17, "STBSTHI"},\ + { 18, "STBSTOC"},\ + { 19, "STBSTPKCUR"},\ + { 20, "STBSTVC"},\ + { 21, "STBST86"},\ + { 22, "STBST93"},\ + { 23, "STRCVLD"},\ + { 24, "STOCPL"},\ + { 25, "STOCPR"},\ + { 26, "STMWSRC"},\ + { 27, "STMWCFC"},\ + { 28, "STMWSMU"},\ + { 29, "STCFMER"},\ + { 30, "STCFMAC"},\ + { 31, "STCLKOOR"},\ + { 32, "STTDMER"},\ + { 33, "STCLPL"},\ + { 34, "STCLPR"},\ + { 35, "STOCPM"},\ + { 36, "36"},\ + { 37, "STLP1"},\ + { 38, "STLA"},\ + { 39, "STVDDP"},\ + { 40, "STTAPDET"},\ + { 41, "STAUDMOD"},\ + { 42, "STSAMMOD"},\ + { 43, "STTAPMOD"},\ + { 44, "STTAPTRG"},\ +}; +#endif /* _TFA9912_TFAFIELDNAMES_H */ diff --git a/sound/soc/codecs/inc/tfa_container.h b/sound/soc/codecs/inc/tfa_container.h new file mode 100644 index 000000000000..c35cba467ec7 --- /dev/null +++ b/sound/soc/codecs/inc/tfa_container.h @@ -0,0 +1,364 @@ +/* + * Copyright 2013-2017 NXP Semiconductors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * tfaContainer.h + * + * Created on: Sep 11, 2013 + * Author: wim + */ + +#ifndef TFACONTAINER_H_ +#define TFACONTAINER_H_ + +/* static limits */ +#define TFACONT_MAXDEVS (4) /* maximum nr of devices */ +#define TFACONT_MAXPROFS (16) /* maximum nr of profiles */ + +#include "tfa98xx_parameters.h" + +/** +* Pass the container buffer, initialize and allocate internal memory. +* +* @param cnt pointer to the start of the buffer holding the container file +* @param length of the data in bytes +* @return +* - tfa_error_ok if normal +* - tfa_error_container invalid container data +* - tfa_error_bad_param invalid parameter +* +*/ +enum tfa_error tfa_load_cnt(void *cnt, int length); + +/** + * Return the descriptor string + * @param cnt pointer to the container struct + * @param dsc pointer to nxpTfa descriptor + * @return descriptor string + */ +char *tfaContGetString(nxpTfaContainer_t *cnt, nxpTfaDescPtr_t *dsc); + +/** + * Gets the string for the given command type number + * @param type number representing a command + * @return string of a command + */ +char *tfaContGetCommandString(uint32_t type); + +/** + * get the device type from the patch in this devicelist + * - find the patch file for this devidx + * - return the devid from the patch or 0 if not found + * @param cnt pointer to container file + * @param dev_idx device index + * @return descriptor string + */ +int tfa_cnt_get_devid(nxpTfaContainer_t *cnt, int dev_idx); + +/** + * Get the slave for the device if it exists. + * @param tfa the device struct pointer + * @param slave_addr the index of the device + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContGetSlave(struct tfa_device *tfa, uint8_t *slave_addr); + +void tfaContSetSlave(uint8_t slave_addr); + +/** + * Get the index for a skave address. + * @param tfa the device struct pointer + * @return the device index + */ +int tfa_cont_get_idx(struct tfa_device *tfa); + +/** + * Write reg and bitfield items in the devicelist to the target. + * @param tfa the device struct pointer + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWriteRegsDev(struct tfa_device *tfa); + +/** + * Write reg and bitfield items in the profilelist to the target. + * @param tfa the device struct pointer + * @param prof_idx the profile index + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWriteRegsProf(struct tfa_device *tfa, int prof_idx); + +/** + * Write a patchfile in the devicelist to the target. + * @param tfa the device struct pointer + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWritePatch(struct tfa_device *tfa); + +/** + * Write all param files in the devicelist to the target. + * @param tfa the device struct pointer + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWriteFiles(struct tfa_device *tfa); + +/** + * Get sample rate from passed profile index + * @param tfa the device struct pointer + * @param prof_idx the index of the profile + * @return sample rate value + */ +unsigned int tfa98xx_get_profile_sr(struct tfa_device *tfa, unsigned int prof_idx); + +/** + * Get the device name string + * @param cnt the pointer to the container struct + * @param dev_idx the index of the device + * @return device name string or error string if not found + */ +char *tfaContDeviceName(nxpTfaContainer_t *cnt, int dev_idx); + +/** + * Get the application name from the container file application field + * @param tfa the device struct pointer + * @param name the input stringbuffer with size: sizeof(application field)+1 + * @return actual string length + */ +int tfa_cnt_get_app_name(struct tfa_device *tfa, char *name); + +/** + * Get profile index of the calibration profile + * @param tfa the device struct pointer + * @return profile index, -2 if no calibration profile is found or -1 on error + */ +int tfaContGetCalProfile(struct tfa_device *tfa); + +/** + * Is the profile a tap profile ? + * @param tfa the device struct pointer + * @param prof_idx the index of the profile + * @return 1 if the profile is a tap profile or 0 if not + */ +int tfaContIsTapProfile(struct tfa_device *tfa, int prof_idx); + +/** + * Get the name of the profile at certain index for a device in the container file + * @param cnt the pointer to the container struct + * @param dev_idx the index of the device + * @param prof_idx the index of the profile + * @return profile name string or error string if not found + */ +char *tfaContProfileName(nxpTfaContainer_t *cnt, int dev_idx, int prof_idx); + +/** + * Process all items in the profilelist + * NOTE an error return during processing will leave the device muted + * @param tfa the device struct pointer + * @param prof_idx index of the profile + * @param vstep_idx index of the vstep + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWriteProfile(struct tfa_device *tfa, int prof_idx, int vstep_idx); + +/** + * Specify the speaker configurations (cmd id) (Left, right, both, none) + * @param dev_idx index of the device + * @param configuration name string of the configuration + */ +void tfa98xx_set_spkr_select(int dev_idx, char *configuration); + +enum Tfa98xx_Error tfa_cont_write_filterbank(struct tfa_device *tfa, nxpTfaFilter_t *filter); + +/** + * Write all param files in the profilelist to the target + * this is used during startup when maybe ACS is set + * @param tfa the device struct pointer + * @param prof_idx the index of the profile + * @param vstep_idx the index of the vstep + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWriteFilesProf(struct tfa_device *tfa, int prof_idx, int vstep_idx); +enum Tfa98xx_Error tfaContWriteFilesVstep(struct tfa_device *tfa, int prof_idx, int vstep_idx); +enum Tfa98xx_Error tfaContWriteDrcFile(struct tfa_device *tfa, int size, uint8_t data[]); + +/** + * Get the device list dsc from the tfaContainer + * @param cont pointer to the tfaContainer + * @param dev_idx the index of the device + * @return device list pointer + */ +nxpTfaDeviceList_t *tfaContGetDevList(nxpTfaContainer_t *cont, int dev_idx); + +/** + * Get the Nth profile for the Nth device + * @param cont pointer to the tfaContainer + * @param dev_idx the index of the device + * @param prof_idx the index of the profile + * @return profile list pointer + */ +nxpTfaProfileList_t *tfaContGetDevProfList(nxpTfaContainer_t *cont, int dev_idx, int prof_idx); + +/** + * Get the number of profiles for device from contaienr + * @param cont pointer to the tfaContainer + * @param dev_idx the index of the device + * @return device list pointer + */ +int tfa_cnt_get_dev_nprof(struct tfa_device *tfa); + + +/** + * Get the Nth livedata for the Nth device + * @param cont pointer to the tfaContainer + * @param dev_idx the index of the device + * @param livedata_idx the index of the livedata + * @return livedata list pointer + */ +nxpTfaLiveDataList_t *tfaContGetDevLiveDataList(nxpTfaContainer_t *cont, int dev_idx, int livedata_idx); + +/** + * Check CRC for container + * @param cont pointer to the tfaContainer + * @return error value 0 on error + */ +int tfaContCrcCheckContainer(nxpTfaContainer_t *cont); + +/** + * Get the device list pointer + * @param cnt pointer to the container struct + * @param dev_idx the index of the device + * @return pointer to device list + */ +nxpTfaDeviceList_t *tfaContDevice(nxpTfaContainer_t *cnt, int dev_idx); + +/** + * Return the pointer to the first profile in a list from the tfaContainer + * @param cont pointer to the tfaContainer + * @return pointer to first profile in profile list + */ +nxpTfaProfileList_t *tfaContGet1stProfList(nxpTfaContainer_t *cont); + +/** + * Return the pointer to the next profile in a list + * @param prof is the pointer to the profile list + * @return profile list pointer + */ +nxpTfaProfileList_t* tfaContNextProfile(nxpTfaProfileList_t *prof); + +/** + * Return the pointer to the first livedata in a list from the tfaContainer + * @param cont pointer to the tfaContainer + * @return pointer to first livedata in profile list + */ +nxpTfaLiveDataList_t *tfaContGet1stLiveDataList(nxpTfaContainer_t *cont); + +/** + * Return the pointer to the next livedata in a list + * @param livedata_idx is the pointer to the livedata list + * @return livedata list pointer + */ +nxpTfaLiveDataList_t* tfaContNextLiveData(nxpTfaLiveDataList_t *livedata_idx); + +/** + * Write a bit field + * @param tfa the device struct pointer + * @param bf bitfield to write + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaRunWriteBitfield(struct tfa_device *tfa, nxpTfaBitfield_t bf); + +/** + * Write a parameter file to the device + * @param tfa the device struct pointer + * @param file filedescriptor pointer + * @param vstep_idx index to vstep + * @param vstep_msg_idx index to vstep message + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaContWriteFile(struct tfa_device *tfa, nxpTfaFileDsc_t *file, int vstep_idx, int vstep_msg_idx); + +/** + * Get the max volume step associated with Nth profile for the Nth device + * @param tfa the device struct pointer + * @param prof_idx profile index + * @return the number of vsteps + */ +int tfacont_get_max_vstep(struct tfa_device *tfa, int prof_idx); + +/** + * Get the file contents associated with the device or profile + * Search within the device tree, if not found, search within the profile + * tree. There can only be one type of file within profile or device. + * @param tfa the device struct pointer + * @param prof_idx I2C profile index in the device + * @param type file type + * @return 0 NULL if file type is not found + * @return 1 file contents + */ +nxpTfaFileDsc_t *tfacont_getfiledata(struct tfa_device *tfa, int prof_idx, enum nxpTfaHeaderType type); + +/** + * Dump the contents of the file header + * @param hdr pointer to file header data + */ +void tfaContShowHeader(nxpTfaHeader_t *hdr); + +/** + * Read a bit field + * @param tfa the device struct pointer + * @param bf bitfield to read out + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfaRunReadBitfield(struct tfa_device *tfa, nxpTfaBitfield_t *bf); + +/** + * Get hw feature bits from container file + * @param tfa the device struct pointer + * @param hw_feature_register pointer to where hw features are stored + */ +void get_hw_features_from_cnt(struct tfa_device *tfa, int *hw_feature_register); + +/** + * Get sw feature bits from container file + * @param tfa the device struct pointer + * @param sw_feature_register pointer to where sw features are stored + */ +void get_sw_features_from_cnt(struct tfa_device *tfa, int sw_feature_register[2]); + +/** + * Factory trimming for the Boost converter + * check if there is a correction needed + * @param tfa the device struct pointer + */ +enum Tfa98xx_Error tfa98xx_factory_trimmer(struct tfa_device *tfa); + +/** + * Search for filters settings and if found then write them to the device + * @param tfa the device struct pointer + * @param prof_idx profile to look in + * @return Tfa98xx_Error + */ +enum Tfa98xx_Error tfa_set_filters(struct tfa_device *tfa, int prof_idx); + +/** + * Get the firmware version from the patch in the container file + * @param tfa the device struct pointer + * @return firmware version + */ +int tfa_cnt_get_patch_version(struct tfa_device *tfa); + +int tfa_tib_dsp_msgmulti(struct tfa_device *tfa, int length, const char *buffer); + +#endif /* TFACONTAINER_H_ */ diff --git a/sound/soc/codecs/inc/tfa_device.h b/sound/soc/codecs/inc/tfa_device.h new file mode 100644 index 000000000000..732f4cc1a533 --- /dev/null +++ b/sound/soc/codecs/inc/tfa_device.h @@ -0,0 +1,296 @@ +/* + * Copyright 2014-2017 NXP Semiconductors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/**\file + * + * The tfa_device interface controls a single I2C device instance by + * referencing to the device specific context provided by means of the + * tfa_device structure pointer. + * Multiple instances of tfa_device structures will be created and maintained + * by the caller. + * + * The API is functionally grouped as: + * - tfa_dev basic codec interface to probe, start/stop and control the device state + * - access to internal MTP storage + * - abstraction for interrupt bits and handling + * - container reading support + */ +#ifndef __TFA_DEVICE_H__ +#define __TFA_DEVICE_H__ + +#include "config.h" + +struct tfa_device; + +/* + * hw/sw feature bit settings in MTP + */ +enum featureSupport { + supportNotSet, /**< default means not set yet */ + supportNo, /**< no support */ + supportYes /**< supported */ +}; +/* + * supported Digital Audio Interfaces bitmap + */ +enum Tfa98xx_DAI { + Tfa98xx_DAI_I2S = 0x01, /**< I2S only */ + Tfa98xx_DAI_TDM = 0x02, /**< TDM, I2S */ + Tfa98xx_DAI_PDM = 0x04, /**< PDM */ + }; + +/* + * device ops function structure + */ +struct tfa_device_ops { + enum Tfa98xx_Error(*dsp_msg)(struct tfa_device *tfa, int length, const char *buf); + enum Tfa98xx_Error(*dsp_msg_read)(struct tfa_device *tfa, int length, unsigned char *bytes); + enum Tfa98xx_Error(*reg_read)(struct tfa_device *tfa, unsigned char subaddress, unsigned short *value); + enum Tfa98xx_Error(*reg_write)(struct tfa_device *tfa, unsigned char subaddress, unsigned short value); + enum Tfa98xx_Error(*mem_read)(struct tfa_device *tfa, unsigned int start_offset, int num_words, int *pValues); + enum Tfa98xx_Error(*mem_write)(struct tfa_device *tfa, unsigned short address, int value, int memtype); + + enum Tfa98xx_Error (*tfa_init)(struct tfa_device *tfa); /**< init typically for loading optimal settings */ + enum Tfa98xx_Error (*dsp_reset)(struct tfa_device *tfa, int state); /**< reset the coolflux dsp */ + enum Tfa98xx_Error (*dsp_system_stable)(struct tfa_device *tfa, int *ready); /**< ready when clocks are stable to allow DSP subsystem access */ + enum Tfa98xx_Error (*dsp_write_tables)(struct tfa_device *tfa, int sample_rate); /**< write the device/type specific delaytables */ + enum Tfa98xx_Error (*auto_copy_mtp_to_iic)(struct tfa_device *tfa); /**< Set auto_copy_mtp_to_iic */ + enum Tfa98xx_Error (*factory_trimmer)(struct tfa_device *tfa); /**< Factory trimming for the Boost converter */ + int (*set_swprof)(struct tfa_device *tfa, unsigned short new_value); /**< Set the sw profile in the struct and the hw register */ + int (*get_swprof)(struct tfa_device *tfa); /**< Get the sw profile from the hw register */ + int(*set_swvstep)(struct tfa_device *tfa, unsigned short new_value); /**< Set the sw vstep in the struct and the hw register */ + int(*get_swvstep)(struct tfa_device *tfa); /**< Get the sw vstep from the hw register */ + int(*get_mtpb)(struct tfa_device *tfa); /**< get status of MTB busy bit*/ + enum Tfa98xx_Error (*set_mute)(struct tfa_device *tfa, int mute); /**< set mute */ + enum Tfa98xx_Error (*faim_protect)(struct tfa_device *tfa, int state); /**< Protect FAIM from being corrupted */ + enum Tfa98xx_Error(*set_osc_powerdown)(struct tfa_device *tfa, int state); /**< Allow to change internal osc. gating settings */ +}; + +/** + * Device states and modifier flags to allow a device/type independent fine + * grained control of the internal state.\n + * Values below 0x10 are referred to as base states which can be or-ed with + * state modifiers, from 0x10 and higher. + * + */ +enum tfa_state { + TFA_STATE_UNKNOWN, /**< unknown or invalid */ + TFA_STATE_POWERDOWN, /**< PLL in powerdown, Algo is up/warm */ + TFA_STATE_INIT_HW, /**< load I2C/PLL hardware setting (~wait2srcsettings) */ + TFA_STATE_INIT_CF, /**< coolflux HW access possible (~initcf) */ + TFA_STATE_INIT_FW, /**< DSP framework active (~patch loaded) */ + TFA_STATE_OPERATING, /**< Amp and Algo running */ + TFA_STATE_FAULT, /**< An alarm or error occurred */ + TFA_STATE_RESET, /**< I2C reset and ACS set */ + /* --sticky state modifiers-- */ + TFA_STATE_MUTE=0x10, /**< Algo & Amp mute */ + TFA_STATE_UNMUTE=0x20, /**< Algo & Amp unmute */ + TFA_STATE_CLOCK_ALWAYS=0x40, /**< PLL connect to internal oscillator */ + TFA_STATE_CLOCK_AUDIO=0x80, /**< PLL connect to audio clock (BCK/FS) */ + TFA_STATE_LOW_POWER=0x100, /**< lowest possible power state */ +}; + +/** + * This is the main tfa device context structure, it will carry all information + * that is needed to handle a single I2C device instance. + * All functions dealing with the device will need access to the fields herein. + */ +struct tfa_device { + int dev_idx; /**< device container index */ + int in_use; + int buffer_size; /**< lowest level max buffer size */ + int has_msg; /**< support direct dsp messaging */ + unsigned char slave_address; /**< I2C slave address (not shifted) */ + unsigned short rev; /**< full revid of this device */ + unsigned char tfa_family; /**< tfa1/tfa2 */ + enum featureSupport supportDrc; + enum featureSupport supportFramework; + enum featureSupport support_saam; + int sw_feature_bits[2]; /**< cached copy of sw feature bits */ + int hw_feature_bits; /**< cached copy of hw feature bits */ + int profile; /**< active profile */ + int vstep; /**< active vstep */ + unsigned char spkr_count; + unsigned char spkr_select; + unsigned char support_tcoef;/**< legacy tfa9887, will be removed */ + enum Tfa98xx_DAI daimap; /**< supported audio interface types */ + int mohm[3]; /**< speaker calibration values in milli ohms -1 is error */ + struct tfa_device_ops dev_ops; + uint16_t interrupt_enable[3]; + uint16_t interrupt_status[3]; + int ext_dsp; /**< respond to external DSP: -1:none, 0:no_dsp, 1:cold, 2:warm */ + int bus; /* TODO fix ext_dsp and bus handling */ + int tfadsp_event; /**< enum tfadsp_event_en is for external registry */ + int verbose; /**< verbosity level for debug print output */ + enum tfa_state state; /**< last known state or-ed with optional state_modifier */ + struct nxpTfaContainer *cnt;/**< the loaded container file */ + struct nxpTfaVolumeStepRegisterInfo *p_regInfo; /**< remember vstep for partial updates */ + int partial_enable; /**< enable partial updates */ + void *data; /**< typically pointing to Linux driver structure owning this device */ + int convert_dsp32; /**< convert 24 bit DSP messages to 32 bit */ + int sync_iv_delay; /**< synchronize I/V delay at cold start */ + int is_probus_device; /**< probus device: device without internal DSP */ + int needs_reset; /**< add the reset trigger for SetAlgoParams and SetMBDrc commands */ + struct kmem_cache *cachep; /**< Memory allocator handle */ +}; + +/** + * The tfa_dev_probe is called before accessing any device accessing functions. + * Access to the tfa device register 3 is attempted and will record the + * returned id for further use. If no device responds the function will abort. + * The recorded id will by used by the query functions to fill the remaining + * relevant data fields of the device structure. + * Data such as MTP features that requires device access will only be read when + * explicitly called and the result will be then cached in the struct. + * + * A structure pointer passed to this device needs to refer to existing memory + * space allocated by the caller. + * + * @param slave = I2C slave address of the target device (not shifted) + * @param tfa struct = points to memory that holds the context for this device + * instance + * + * @return + * - 0 if the I2C device responded to a read of register address 3\n + * when the device responds but with an unknown id a warning will be printed + * - -1 if no response from the I2C device + * + */ +int tfa_dev_probe(int slave, struct tfa_device *tfa); + +/** + * Start this instance at the profile and vstep as provided. + * The profile and vstep will be loaded first in case the current value differs + * from the requested values. + * Note that this call will not change the mute state of the tfa, which means + * that of this instance was called in muted state the caller will have to + * unmute in order to get audio. + * + * @param tfa struct = pointer to context of this device instance + * @param profile the selected profile to run + * @param vstep the selected vstep to use + * @return tfa_error enum + */ +enum tfa_error tfa_dev_start(struct tfa_device *tfa, int profile, int vstep); + + +/** + * Stop audio for this instance as gracefully as possible. + * Audio will be muted and the PLL will be shutdown together with any other + * device/type specific settings needed to prevent audio artifacts or + * workarounds. + * + * Note that this call will change state of the tfa to mute and powered down. + * + * @param tfa struct = pointer to context of this device instance + * @return tfa_error enum + */ +enum tfa_error tfa_dev_stop(struct tfa_device *tfa); + +/** + * This interface allows a device/type independent fine grained control of the + * internal state of the instance. + * Whenever a base state is requested an attempt is made to actively bring the device + * into this state. However this may depend on external conditions beyond control of + * this software layer. Therefore in case the state cannot be set an erro will + * be returned and the current state remains unchanged. + * The base states, lower values below 0x10, are all mutually exclusive, they higher ones + * can also function as a sticky modifier which means for example that operating + * state could be in either muted or unmuted state. Or in case of init_cf it can be + * internal clock (always) or external audio clock. + * This function is intended to be used for device mute/unmute synchronization + * when called from higher layers. Mostly internal calls will use this to control + * the startup and profile transitions in a device/type independent way. + * + * @param tfa struct = pointer to context of this device instance + * @param state struct = desired device state after function return + * @return tfa_error enum + */ +enum tfa_error tfa_dev_set_state(struct tfa_device *tfa, enum tfa_state state); + +/** + * Retrieve the current state of this instance in an active way. + * The state field in tfa structure will reflect the result unless an error is + * returned. + * Note that the hardware state may change on external events an as such this + * field should be treated as volatile. + * + * @param tfa struct = pointer to context of this device instance + * @return tfa_error enum + * + */ +enum tfa_state tfa_dev_get_state(struct tfa_device *tfa); + + +/*****************************************************************************/ +/*****************************************************************************/ +/** + * MTP support functions + */ +enum tfa_mtp { + TFA_MTP_OTC, /**< */ + TFA_MTP_EX, /**< */ + TFA_MTP_RE25, /**< */ + TFA_MTP_RE25_PRIM, /**< */ + TFA_MTP_RE25_SEC, /**< */ + TFA_MTP_LOCK, /**< */ +}; + +/** + * + */ +int tfa_dev_mtp_get(struct tfa_device *tfa, enum tfa_mtp item); + +/** + * + */ +enum tfa_error tfa_dev_mtp_set(struct tfa_device *tfa, enum tfa_mtp item, int value); + + +//irq +/* tfa2 interrupt support + * !!! enum tfa9912_irq !!!*/ +/* + * interrupt bit function to clear + */ +int tfa_irq_clear(struct tfa_device *tfa, int bit); +/* + * return state of irq or -1 if illegal bit + */ +int tfa_irq_get(struct tfa_device *tfa, int bit); +/* + * interrupt bit function that operates on the shadow regs in the handle + */ +int tfa_irq_ena(struct tfa_device *tfa, int bit, int state); +/* + * interrupt bit function that sets the polarity + */ +int tfa_irq_set_pol(struct tfa_device *tfa, int bit, int state); + +/* + * mask interrupts by disabling them + */ +int tfa_irq_mask(struct tfa_device *tfa); +/* + * unmask interrupts by enabling them again + */ +int tfa_irq_unmask(struct tfa_device *tfa); +//cnt read +//debug? + +#endif /* __TFA_DEVICE_H__ */ + diff --git a/sound/soc/codecs/inc/tfa_dsp_fw.h b/sound/soc/codecs/inc/tfa_dsp_fw.h new file mode 100644 index 000000000000..ce7340f70fee --- /dev/null +++ b/sound/soc/codecs/inc/tfa_dsp_fw.h @@ -0,0 +1,168 @@ +/* + * Copyright 2014-2017 NXP Semiconductors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TFA98XX_INTERNALS_H +#define TFA98XX_INTERNALS_H + +#include "config.h" + +#include "tfa_service.h" //TODO cleanup for enum Tfa98xx_Status_ID +/* + * tfadsp_fw_api.c + */ +/** + * Return a text version of the firmware status ID code + * @param status the given status ID code + * @return the firmware status ID string + */ +const char *tfadsp_fw_status_string(enum Tfa98xx_Status_ID status); +int tfadsp_fw_start(struct tfa_device *tfa, int prof_idx, int vstep_idx); +int tfadsp_fw_get_api_version(struct tfa_device *tfa, uint8_t *buffer); +#define FW_MAXTAG 150 +int tfadsp_fw_get_tag(struct tfa_device *tfa, uint8_t *buffer); +int tfadsp_fw_get_status_change(struct tfa_device *tfa, uint8_t *buffer); +int tfadsp_fw_set_re25(struct tfa_device *tfa, int prim, int sec ); +int tfadsp_fw_get_re25(struct tfa_device *tfa, uint8_t *buffer); + +/* + * the order matches the ACK bits order in TFA98XX_CF_STATUS + */ +enum tfa_fw_event { /* not all available on each device */ + tfa_fw_i2c_cmd_ack, + tfa_fw_reset_start, + tfa_fw_short_on_mips, + tfa_fw_soft_mute_ready, + tfa_fw_volume_ready, + tfa_fw_error_damage, + tfa_fw_calibrate_done, + tfa_fw_max +}; + +/* the following type mappings are compiler specific */ +#define subaddress_t unsigned char + +/* module Ids */ +#define MODULE_FRAMEWORK 0 +#define MODULE_SPEAKERBOOST 1 +#define MODULE_BIQUADFILTERBANK 2 +#define MODULE_TAPTRIGGER 5 +#define MODULE_SETRE 9 + +/* RPC commands */ +/* SET */ +#define FW_PAR_ID_SET_MEMORY 0x03 +#define FW_PAR_ID_SET_SENSES_DELAY 0x04 +#define FW_PAR_ID_SETSENSESCAL 0x05 +#define FW_PAR_ID_SET_INPUT_SELECTOR 0x06 +#define FW_PAR_ID_SET_OUTPUT_SELECTOR 0x08 +#define FW_PAR_ID_SET_PROGRAM_CONFIG 0x09 +#define FW_PAR_ID_SET_GAINS 0x0A +#define FW_PAR_ID_SET_MEMTRACK 0x0B +#define FW_PAR_ID_SET_FWKUSECASE 0x11 +#define TFA1_FW_PAR_ID_SET_CURRENT_DELAY 0x03 +#define TFA1_FW_PAR_ID_SET_CURFRAC_DELAY 0x06 +/* GET */ +#define FW_PAR_ID_GET_MEMORY 0x83 +#define FW_PAR_ID_GLOBAL_GET_INFO 0x84 +#define FW_PAR_ID_GET_FEATURE_INFO 0x85 +#define FW_PAR_ID_GET_MEMTRACK 0x8B +#define FW_PAR_ID_GET_TAG 0xFF +#define FW_PAR_ID_GET_API_VERSION 0xFE +#define FW_PAR_ID_GET_STATUS_CHANGE 0x8D + +/* Load a full model into SpeakerBoost. */ +/* SET */ +#define SB_PARAM_SET_ALGO_PARAMS 0x00 +#define SB_PARAM_SET_LAGW 0x01 +#define SB_PARAM_SET_ALGO_PARAMS_WITHOUT_RESET 0x02 +#define SB_PARAM_SET_RE25C 0x05 +#define SB_PARAM_SET_LSMODEL 0x06 +#define SB_PARAM_SET_MBDRC 0x07 +#define SB_PARAM_SET_MBDRC_WITHOUT_RESET 0x08 +#define SB_PARAM_SET_EXCURSION_FILTERS 0x0A +#define SB_PARAM_SET_DRC 0x0F +/* GET */ +#define SB_PARAM_GET_ALGO_PARAMS 0x80 +#define SB_PARAM_GET_LAGW 0x81 +#define SB_PARAM_GET_RE25C 0x85 +#define SB_PARAM_GET_LSMODEL 0x86 +#define SB_PARAM_GET_MBDRC 0x87 +#define SB_PARAM_GET_MBDRC_DYNAMICS 0x89 +#define SB_PARAM_GET_EXCURSION_FILTERS 0x8A +#define SB_PARAM_GET_TAG 0xFF + +#define SB_PARAM_SET_EQ 0x0A /* 2 Equaliser Filters. */ +#define SB_PARAM_SET_PRESET 0x0D /* Load a preset */ +#define SB_PARAM_SET_CONFIG 0x0E /* Load a config */ +#define SB_PARAM_SET_AGCINS 0x10 +#define SB_PARAM_SET_CURRENT_DELAY 0x03 +#define SB_PARAM_GET_STATE 0xC0 +#define SB_PARAM_GET_XMODEL 0xC1 /* Gets current Excursion Model. */ +#define SB_PARAM_GET_XMODEL_COEFFS 0x8C /* Get coefficients for XModel */ +#define SB_PARAM_GET_EXCURSION_FILTERS 0x8A /* Get excursion filters */ +#define SB_PARAM_SET_EXCURSION_FILTERS 0x0A /* Set excursion filters */ + +/* SET: TAPTRIGGER */ +#define TAP_PARAM_SET_ALGO_PARAMS 0x01 +#define TAP_PARAM_SET_DECIMATION_PARAMS 0x02 + +/* GET: TAPTRIGGER*/ +#define TAP_PARAM_GET_ALGO_PARAMS 0x81 +#define TAP_PARAM_GET_TAP_RESULTS 0x84 + +/* sets the speaker calibration impedance (@25 degrees celsius) */ +#define SB_PARAM_SET_RE0 0x89 + +#define BFB_PAR_ID_SET_COEFS 0x00 +#define BFB_PAR_ID_GET_COEFS 0x80 +#define BFB_PAR_ID_GET_CONFIG 0x81 + +/* for compatibility */ +#define FW_PARAM_GET_STATE FW_PAR_ID_GLOBAL_GET_INFO +#define FW_PARAM_GET_FEATURE_BITS FW_PAR_ID_GET_FEATURE_BITS + +/* RPC Status results */ +#define STATUS_OK 0 +#define STATUS_INVALID_MODULE_ID 2 +#define STATUS_INVALID_PARAM_ID 3 +#define STATUS_INVALID_INFO_ID 4 + +/* the maximum message length in the communication with the DSP */ +#define TFA2_MAX_PARAM_SIZE (507*3) /* TFA2 */ +#define TFA1_MAX_PARAM_SIZE (145*3) /* TFA1 */ + +#define ROUND_DOWN(a,n) (((a)/(n))*(n)) + +/* feature bits */ +#define FEATURE1_TCOEF 0x100 /* bit8 set means tCoefA expected */ +#define FEATURE1_DRC 0x200 /* bit9 NOT set means DRC expected */ + +/* DSP firmware xmem defines */ +#define TFA1_FW_XMEM_CALIBRATION_DONE 231 +#define TFA2_FW_XMEM_CALIBRATION_DONE 516 +#define TFA1_FW_XMEM_COUNT_BOOT 0xa1 +#define TFA2_FW_XMEM_COUNT_BOOT 512 +#define TFA2_FW_XMEM_CMD_COUNT 520 + +/* note that the following defs rely on the handle variable */ +#define TFA_FW_XMEM_CALIBRATION_DONE TFA_FAM_FW(tfa,XMEM_CALIBRATION_DONE) +#define TFA_FW_XMEM_COUNT_BOOT TFA_FAM_FW(tfa,XMEM_COUNT_BOOT) +#define TFA_FW_XMEM_CMD_COUNT TFA_FAM_FW(tfa,XMEM_CMD_COUNT) + +#define TFA2_FW_ReZ_SCALE 65536 +#define TFA1_FW_ReZ_SCALE 16384 + +#endif /* TFA98XX_INTERNALS_H */ diff --git a/sound/soc/codecs/inc/tfa_ext.h b/sound/soc/codecs/inc/tfa_ext.h new file mode 100644 index 000000000000..da68f779bbe2 --- /dev/null +++ b/sound/soc/codecs/inc/tfa_ext.h @@ -0,0 +1,57 @@ +/* + * Copyright 2016-2017 NXP Semiconductors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * tfa_ext.h + * + * Created on: Jun 8, 2016 + * Author: wim + */ + +#ifndef TFA_SRC_TFA_EXT_H_ +#define TFA_SRC_TFA_EXT_H_ + +#include "tfa_device.h" + +/* + * events + */ +/** Maximum value for enumerator */ +#define LVM_MAXENUM (0xffff) +/** +This enum type specifies the different events that may trigger a callback. +*/ +enum tfadsp_event_en +{ + TFADSP_CMD_ACK = 1, /**< Command handling is completed */ + TFADSP_SOFT_MUTE_READY = 8, /**< Muting completed */ + TFADSP_VOLUME_READY = 16, /**< Volume change completed */ + TFADSP_DAMAGED_SPEAKER = 32, /**< Damaged speaker was detected */ + TFADSP_CALIBRATE_DONE = 64, /**< Calibration is completed */ + TFADSP_SPARSESIG_DETECTED = 128, /**< Sparse signal detected */ + TFADSP_CMD_READY = 256, /**< Ready to receive commands */ + TFADSP_EXT_PWRUP = 0x8000,/**< DSP API has started, powered up */ + TFADSP_EXT_PWRDOWN = 0x8001,/**< DSP API stopped, power down */ + TFADSP_EVENT_DUMMY = LVM_MAXENUM +} ; + +typedef int (*tfa_event_handler_t)(struct tfa_device *tfa, enum tfadsp_event_en tfadsp_event); +typedef int (*dsp_send_message_t)(struct tfa_device *tfa, int length, const char *buf); +typedef int (*dsp_read_message_t)(struct tfa_device *tfa, int length, char *buf); +typedef int (*dsp_write_reg_t)(struct tfa_device *tfa, unsigned char subaddress, unsigned short value); + +int tfa_ext_register(dsp_write_reg_t tfa_write_reg, dsp_send_message_t tfa_send_message, dsp_read_message_t tfa_read_message, tfa_event_handler_t *tfa_event_handler); + +#endif /* TFA_SRC_TFA_EXT_H_ */ diff --git a/sound/soc/codecs/inc/tfa_service.h b/sound/soc/codecs/inc/tfa_service.h new file mode 100644 index 000000000000..5e4e90b6bc03 --- /dev/null +++ b/sound/soc/codecs/inc/tfa_service.h @@ -0,0 +1,1021 @@ +/* + * Copyright 2014-2017 NXP Semiconductors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TFA_SERVICE_H +#define TFA_SERVICE_H + +//#include "config.h" +// workaround for Visual Studio: +// fatal error C1083: Cannot open include file: 'config.h': No such file or directory +#ifdef __KERNEL__ +#include +#else +#include +#endif +#ifdef __cplusplus +extern "C" { +#include "NXP_I2C.h" +#endif + +/* Linux kernel module defines TFA98XX_GIT_VERSIONS in the linux_driver/Makefile */ +#if !defined(TFA98XX_GIT_VERSIONS) +//#include "versions.h" +#endif +#ifdef TFA98XX_GIT_VERSIONS + #define TFA98XX_API_REV_STR TFA98XX_GIT_VERSIONS +#else + #define TFA98XX_API_REV_STR "v6.5.0" +#endif + +#include "tfa_device.h" + +/* + * data previously defined in Tfa9888_dsp.h + */ +#define MEMTRACK_MAX_WORDS 150 +#define LSMODEL_MAX_WORDS 150 +#define TFA98XX_MAXTAG (150) +#define FW_VAR_API_VERSION (521) + +/* Indexes and scaling factors of GetLSmodel */ +#define tfa9888_fs_IDX 128 +#define tfa9888_leakageFactor_IDX 130 +#define tfa9888_ReCorrection_IDX 131 +#define tfa9888_Bl_IDX 132 +#define ReZ_IDX 147 + +#define tfa9872_leakageFactor_IDX 128 +#define tfa9872_ReCorrection_IDX 129 +#define tfa9872_Bl_IDX 130 + +#define fs_SCALE (double)1 +#define leakageFactor_SCALE (double)8388608 +#define ReCorrection_SCALE (double)8388608 +#define Bl_SCALE (double)2097152 +#define tCoef_SCALE (double)8388608 + +/* ---------------------------- Max1 ---------------------------- */ +/* Headroom applied to the main input signal */ +#define SPKRBST_HEADROOM 7 +/* Exponent used for AGC Gain related variables */ +#define SPKRBST_AGCGAIN_EXP SPKRBST_HEADROOM +#define SPKRBST_TEMPERATURE_EXP 9 +/* Exponent used for Gain Corection related variables */ +#define SPKRBST_LIMGAIN_EXP 4 +#define SPKRBST_TIMECTE_EXP 1 +#define DSP_MAX_GAIN_EXP 7 +/* -------------------------------------------------------------- */ + +/* speaker related parameters */ +#define TFA2_SPEAKERPARAMETER_LENGTH (3*151) /* MAX2=450 */ +#define TFA1_SPEAKERPARAMETER_LENGTH (3*141) /* MAX1=423 */ + +/* vstep related parameters */ +#define TFA2_ALGOPARAMETER_LENGTH (3*304) /* N1B = (304) 305 is including the cmd-id */ +#define TFA72_ALGOPARAMETER_LENGTH_MONO (3*183) +#define TFA72_ALGOPARAMETER_LENGTH_STEREO (3*356) +#define TFA2_MBDRCPARAMETER_LENGTH (3*152) /* 154 is including the cmd-id */ +#define TFA72_MBDRCPARAMETER_LENGTH (3*98) +#define TFA1_PRESET_LENGTH 87 +#define TFA1_DRC_LENGTH 381 /* 127 words */ +#define TFA2_FILTERCOEFSPARAMETER_LENGTH (3*168) /* 170 is including the cmd-id */ +#define TFA72_FILTERCOEFSPARAMETER_LENGTH (3*156) + +/* Maximum number of retries for DSP result + * Keep this value low! + * If certain calls require longer wait conditions, the + * application should poll, not the API + * The total wait time depends on device settings. Those + * are application specific. + */ +#define TFA98XX_WAITRESULT_NTRIES 40 +#define TFA98XX_WAITRESULT_NTRIES_LONG 2000 + +/* following lengths are in bytes */ +#define TFA98XX_PRESET_LENGTH 87 +#define TFA98XX_CONFIG_LENGTH 201 +#define TFA98XX_DRC_LENGTH 381 /* 127 words */ + +typedef unsigned char Tfa98xx_Config_t[TFA98XX_CONFIG_LENGTH]; +typedef unsigned char Tfa98xx_Preset_t[TFA98XX_PRESET_LENGTH]; +typedef unsigned char Tfa98xx_DrcParameters_t[TFA98XX_DRC_LENGTH]; + +/* Type containing all the possible errors that can occur + * + */ +enum Tfa98xx_Error { + Tfa98xx_Error_Ok = 0, + Tfa98xx_Error_Device, /* 1. Currently only used to keep in sync with tfa_error */ + Tfa98xx_Error_Bad_Parameter, /* 2. */ + Tfa98xx_Error_Fail, /* 3. generic failure, avoid mislead message */ + Tfa98xx_Error_NoClock, /* 4. no clock detected */ + Tfa98xx_Error_StateTimedOut, /* 5. */ + Tfa98xx_Error_DSP_not_running, /* 6. communication with the DSP failed */ + Tfa98xx_Error_AmpOn, /* 7. amp is still running */ + Tfa98xx_Error_NotOpen, /* 8. the given handle is not open */ + Tfa98xx_Error_InUse, /* 9. too many handles */ + Tfa98xx_Error_Buffer_too_small, /* 10. if a buffer is too small */ + /* the expected response did not occur within the expected time */ + Tfa98xx_Error_RpcBase = 100, + Tfa98xx_Error_RpcBusy = 101, + Tfa98xx_Error_RpcModId = 102, + Tfa98xx_Error_RpcParamId = 103, + Tfa98xx_Error_RpcInvalidCC = 104, + Tfa98xx_Error_RpcInvalidSeq = 105, + Tfa98xx_Error_RpcInvalidParam = 106, + Tfa98xx_Error_RpcBufferOverflow = 107, + Tfa98xx_Error_RpcCalibBusy = 108, + Tfa98xx_Error_RpcCalibFailed = 109, + Tfa98xx_Error_Not_Implemented, + Tfa98xx_Error_Not_Supported, + Tfa98xx_Error_I2C_Fatal, /* Fatal I2C error occurred */ + /* Nonfatal I2C error, and retry count reached */ + Tfa98xx_Error_I2C_NonFatal, + Tfa98xx_Error_Other = 1000 +}; + +/* + * Type containing all the possible msg returns DSP can give + * //TODO move to tfa_dsp_fw.h + */ +enum Tfa98xx_Status_ID { + Tfa98xx_DSP_Not_Running = -1, /* No response from DSP */ + Tfa98xx_I2C_Req_Done = 0, /* Request executed correctly and result, if any, is available for download */ + Tfa98xx_I2C_Req_Busy = 1, /* Request is being processed, just wait for result */ + Tfa98xx_I2C_Req_Invalid_M_ID = 2, /* Provided M-ID does not fit in valid rang [0..2] */ + Tfa98xx_I2C_Req_Invalid_P_ID = 3, /* Provided P-ID isn�t valid in the given M-ID context */ + Tfa98xx_I2C_Req_Invalid_CC = 4, /* Invalid channel configuration bits (SC|DS|DP|DC) combination */ + Tfa98xx_I2C_Req_Invalid_Seq = 5, /* Invalid sequence of commands, in case the DSP expects some commands in a specific order */ + Tfa98xx_I2C_Req_Invalid_Param = 6, /* Generic error */ + Tfa98xx_I2C_Req_Buffer_Overflow = 7, /* I2C buffer has overflowed: host has sent too many parameters, memory integrity is not guaranteed */ + Tfa98xx_I2C_Req_Calib_Busy = 8, /* Calibration not finished */ + Tfa98xx_I2C_Req_Calib_Failed = 9 /* Calibration failed */ +}; + +/* + * speaker as microphone + */ +enum Tfa98xx_saam { + Tfa98xx_saam_none, /*< SAAM feature not available */ + Tfa98xx_saam /*< SAAM feature available */ +}; + +/* + * config file subtypes + */ +enum Tfa98xx_config_type { + Tfa98xx_config_generic, + Tfa98xx_config_sub1, + Tfa98xx_config_sub2, + Tfa98xx_config_sub3, +}; + +enum Tfa98xx_AmpInputSel { + Tfa98xx_AmpInputSel_I2SLeft, + Tfa98xx_AmpInputSel_I2SRight, + Tfa98xx_AmpInputSel_DSP +}; + +enum Tfa98xx_OutputSel { + Tfa98xx_I2SOutputSel_CurrentSense, + Tfa98xx_I2SOutputSel_DSP_Gain, + Tfa98xx_I2SOutputSel_DSP_AEC, + Tfa98xx_I2SOutputSel_Amp, + Tfa98xx_I2SOutputSel_DataI3R, + Tfa98xx_I2SOutputSel_DataI3L, + Tfa98xx_I2SOutputSel_DcdcFFwdCur, +}; + +enum Tfa98xx_StereoGainSel { + Tfa98xx_StereoGainSel_Left, + Tfa98xx_StereoGainSel_Right +}; + +#define TFA98XX_MAXPATCH_LENGTH (3*1024) + +/* the number of biquads supported */ +#define TFA98XX_BIQUAD_NUM 10 + +enum Tfa98xx_Channel { + Tfa98xx_Channel_L, + Tfa98xx_Channel_R, + Tfa98xx_Channel_L_R, + Tfa98xx_Channel_Stereo +}; + +enum Tfa98xx_Mode { + Tfa98xx_Mode_Normal = 0, + Tfa98xx_Mode_RCV +}; + +enum Tfa98xx_Mute { + Tfa98xx_Mute_Off, + Tfa98xx_Mute_Digital, + Tfa98xx_Mute_Amplifier +}; + +enum Tfa98xx_SpeakerBoostStatusFlags { + Tfa98xx_SpeakerBoost_Activity = 0, /* Input signal activity. */ + Tfa98xx_SpeakerBoost_S_Ctrl, /* S Control triggers the limiter */ + Tfa98xx_SpeakerBoost_Muted, /* 1 when signal is muted */ + Tfa98xx_SpeakerBoost_X_Ctrl, /* X Control triggers the limiter */ + Tfa98xx_SpeakerBoost_T_Ctrl, /* T Control triggers the limiter */ + Tfa98xx_SpeakerBoost_NewModel, /* New model is available */ + Tfa98xx_SpeakerBoost_VolumeRdy, /* 0:stable vol, 1:still smoothing */ + Tfa98xx_SpeakerBoost_Damaged, /* Speaker Damage detected */ + Tfa98xx_SpeakerBoost_SignalClipping /* input clipping detected */ +}; + +struct Tfa98xx_DrcStateInfo { + float GRhighDrc1[2]; + float GRhighDrc2[2]; + float GRmidDrc1[2]; + float GRmidDrc2[2]; + float GRlowDrc1[2]; + float GRlowDrc2[2]; + float GRpostDrc1[2]; + float GRpostDrc2[2]; + float GRblDrc[2]; +}; +struct Tfa98xx_StateInfo { + /* SpeakerBoost State */ + float agcGain; /* Current AGC Gain value */ + float limGain; /* Current Limiter Gain value */ + float sMax; /* Current Clip/Lim threshold */ + int T; /* Current Speaker Temperature value */ + int statusFlag; /* Masked bit word */ + float X1; /* estimated excursion caused by Spkrboost gain ctrl */ + float X2; /* estimated excursion caused by manual gain setting */ + float Re; /* Loudspeaker blocked resistance */ + /* Framework state */ + /* increments each time a MIPS problem is detected on the DSP */ + int shortOnMips; + struct Tfa98xx_DrcStateInfo drcState; /* DRC state, when enabled */ +}; + +typedef struct nxpTfaMsg { + uint8_t msg_size; + unsigned char cmdId[3]; + int data[9]; +} nxpTfaMsg_t; + +typedef struct nxp_vstep_msg { + int fw_version; + uint8_t no_of_vsteps; + uint16_t reg_no; + uint8_t *msg_reg; + uint8_t msg_no; + uint32_t algo_param_length; + uint8_t *msg_algo_param; + uint32_t filter_coef_length; + uint8_t *msg_filter_coef; + uint32_t mbdrc_length; + uint8_t *msg_mbdrc; +} nxp_vstep_msg_t; + +typedef struct nxpTfaGroup { + uint8_t msg_size; + uint8_t profileId[64]; +} nxpTfaGroup_t; + + +struct nxpTfa98xx_Memtrack_data { + int length; + float mValues[MEMTRACK_MAX_WORDS]; + int mAdresses[MEMTRACK_MAX_WORDS]; + int trackers[MEMTRACK_MAX_WORDS]; + int scalingFactor[MEMTRACK_MAX_WORDS]; +}; + +/* possible memory values for DMEM in CF_CONTROLs */ +enum Tfa98xx_DMEM { + Tfa98xx_DMEM_ERR = -1, + Tfa98xx_DMEM_PMEM = 0, + Tfa98xx_DMEM_XMEM = 1, + Tfa98xx_DMEM_YMEM = 2, + Tfa98xx_DMEM_IOMEM = 3, +}; + +/** + * lookup the device type and return the family type + */ +int tfa98xx_dev2family(int dev_type); + +/** + * register definition structure + */ +struct regdef { + unsigned char offset; /**< subaddress offset */ + unsigned short pwronDefault; /**< register contents after poweron */ + unsigned short pwronTestmask; /**< mask of bits not test */ + char *name; /**< short register name */ +}; + +enum Tfa98xx_DMEM tfa98xx_filter_mem(struct tfa_device *tfa, int filter_index, unsigned short *address, int channel); + +/** + * Load the default HW settings in the device + * @param tfa the device struct pointer + */ +enum Tfa98xx_Error tfa98xx_init(struct tfa_device *tfa); + +/** + * If needed, this function can be used to get a text version of the status ID code + * @param status the given status ID code + * @return the I2C status ID string + */ +const char *tfa98xx_get_i2c_status_id_string(int status); + +/* control the powerdown bit + * @param tfa the device struct pointer + * @param powerdown must be 1 or 0 + */ +enum Tfa98xx_Error tfa98xx_powerdown(struct tfa_device *tfa, int powerdown); + +/* indicates on which channel of DATAI2 the gain from the IC is set + * @param tfa the device struct pointer + * @param gain_sel, see Tfa98xx_StereoGainSel_t + */ +enum Tfa98xx_Error tfa98xx_select_stereo_gain_channel(struct tfa_device *tfa, + enum Tfa98xx_StereoGainSel gain_sel); + +/** + * set the mtp with user controllable values + * @param tfa the device struct pointer + * @param value to be written + * @param mask to be applied toi the bits affected + */ +enum Tfa98xx_Error tfa98xx_set_mtp(struct tfa_device *tfa, uint16_t value, uint16_t mask); +enum Tfa98xx_Error tfa98xx_get_mtp(struct tfa_device *tfa, uint16_t *value); + +/** + * lock or unlock KEY2 + * lock = 1 will lock + * lock = 0 will unlock + * note that on return all the hidden key will be off + */ +void tfa98xx_key2(struct tfa_device *tfa, int lock); + +int tfa_calibrate(struct tfa_device *tfa) ; +void tfa98xx_set_exttemp(struct tfa_device *tfa, short ext_temp); +short tfa98xx_get_exttemp(struct tfa_device *tfa); + +/* control the volume of the DSP + * @param vol volume in bit field. It must be between 0 and 255 + */ +enum Tfa98xx_Error tfa98xx_set_volume_level(struct tfa_device *tfa, + unsigned short vol); + +/* set the input channel to use + * @param channel see Tfa98xx_Channel_t enumeration + */ +enum Tfa98xx_Error tfa98xx_select_channel(struct tfa_device *tfa, + enum Tfa98xx_Channel channel); + +/* set the mode for normal or receiver mode + * @param mode see Tfa98xx_Mode enumeration + */ +enum Tfa98xx_Error tfa98xx_select_mode(struct tfa_device *tfa, enum Tfa98xx_Mode mode ); + +/* mute/unmute the audio + * @param mute see Tfa98xx_Mute_t enumeration + */ +enum Tfa98xx_Error tfa98xx_set_mute(struct tfa_device *tfa, + enum Tfa98xx_Mute mute); + +/* + * tfa_supported_speakers - required for SmartStudio initialization + * returns the number of the supported speaker count + */ +enum Tfa98xx_Error tfa_supported_speakers(struct tfa_device *tfa, int* spkr_count); + +/** +* Return the tfa revision +*/ +void tfa98xx_rev(int *major, int *minor, int *revision); + +/* + * Return the feature bits from MTP and cnt file for comparison + */ +enum Tfa98xx_Error +tfa98xx_compare_features(struct tfa_device *tfa, int features_from_MTP[3], int features_from_cnt[3]); + +/* + * return feature bits + */ +enum Tfa98xx_Error +tfa98xx_dsp_get_sw_feature_bits(struct tfa_device *tfa, int features[2]); +enum Tfa98xx_Error +tfa98xx_dsp_get_hw_feature_bits(struct tfa_device *tfa, int *features); + +/* + * tfa98xx_supported_saam + * returns the speaker as microphone feature + * @param saam enum pointer + * @return error code + */ +enum Tfa98xx_Error tfa98xx_supported_saam(struct tfa_device *tfa, enum Tfa98xx_saam *saam); + +/* load the tables to the DSP + * called after patch load is done + * @return error code + */ +enum Tfa98xx_Error tfa98xx_dsp_write_tables(struct tfa_device *tfa, int sample_rate); + + +/* set or clear DSP reset signal + * @param new state + * @return error code + */ +enum Tfa98xx_Error tfa98xx_dsp_reset(struct tfa_device *tfa, int state); + +/* check the state of the DSP subsystem + * return ready = 1 when clocks are stable to allow safe DSP subsystem access + * @param tfa the device struct pointer + * @param ready pointer to state flag, non-zero if clocks are not stable + * @return error code + */ +enum Tfa98xx_Error tfa98xx_dsp_system_stable(struct tfa_device *tfa, int *ready); + +enum Tfa98xx_Error tfa98xx_auto_copy_mtp_to_iic(struct tfa_device *tfa); + +/** + * check the state of the DSP coolflux + * @param tfa the device struct pointer + * @return the value of CFE + */ +int tfa_cf_enabled(struct tfa_device *tfa); + +/* The following functions can only be called when the DSP is running + * - I2S clock must be active, + * - IC must be in operating mode + */ + +/** + * patch the ROM code of the DSP + * @param tfa the device struct pointer + * @param patchLength the number of bytes of patchBytes + * @param patchBytes pointer to the bytes to patch + */ +enum Tfa98xx_Error tfa_dsp_patch(struct tfa_device *tfa, + int patchLength, + const unsigned char *patchBytes); + +/** + * load explicitly the speaker parameters in case of free speaker, + * or when using a saved speaker model + */ +enum Tfa98xx_Error tfa98xx_dsp_write_speaker_parameters( + struct tfa_device *tfa, + int length, + const unsigned char *pSpeakerBytes); + +/** + * read the speaker parameters as used by the SpeakerBoost processing + */ +enum Tfa98xx_Error tfa98xx_dsp_read_speaker_parameters( + struct tfa_device *tfa, + int length, + unsigned char *pSpeakerBytes); + +/** + * read the current status of the DSP, typically used for development, + * not essential to be used in a product + */ +enum Tfa98xx_Error tfa98xx_dsp_get_state_info( + struct tfa_device *tfa, + unsigned char bytes[], + unsigned int *statesize); + +/** + * Check whether the DSP supports DRC + * pbSupportDrc=1 when DSP supports DRC, + * pbSupportDrc=0 when DSP doesn't support it + */ +enum Tfa98xx_Error tfa98xx_dsp_support_drc(struct tfa_device *tfa, + int *pbSupportDrc); + +enum Tfa98xx_Error +tfa98xx_dsp_support_framework(struct tfa_device *tfa, int *pbSupportFramework); + +/** + * read the speaker excursion model as used by SpeakerBoost processing + */ +enum Tfa98xx_Error tfa98xx_dsp_read_excursion_model( + struct tfa_device *tfa, + int length, + unsigned char *pSpeakerBytes); + +/** + * load all the parameters for a preset from a file + */ +enum Tfa98xx_Error tfa98xx_dsp_write_preset(struct tfa_device *tfa, + int length, const unsigned char + *pPresetBytes); + +/** + * wrapper for dsp_msg that adds opcode and only writes + */ +enum Tfa98xx_Error tfa_dsp_cmd_id_write(struct tfa_device *tfa, + unsigned char module_id, + unsigned char param_id, int num_bytes, + const unsigned char data[]); + +/** + * wrapper for dsp_msg that writes opcode and reads back the data + */ +enum Tfa98xx_Error tfa_dsp_cmd_id_write_read(struct tfa_device *tfa, + unsigned char module_id, + unsigned char param_id, int num_bytes, + unsigned char data[]); + +/** + * wrapper for dsp_msg that adds opcode and 3 bytes required for coefs + */ +enum Tfa98xx_Error tfa_dsp_cmd_id_coefs(struct tfa_device *tfa, + unsigned char module_id, + unsigned char param_id, int num_bytes, + unsigned char data[]); + +/** + * wrapper for dsp_msg that adds opcode and 3 bytes required for MBDrcDynamics + */ +enum Tfa98xx_Error tfa_dsp_cmd_id_MBDrc_dynamics(struct tfa_device *tfa, + unsigned char module_id, + unsigned char param_id, int index_subband, + int num_bytes, unsigned char data[]); + +/** + * Disable a certain biquad. + * @param tfa the device struct pointer + * @param biquad_index: 1-10 of the biquad that needs to be adressed +*/ +enum Tfa98xx_Error Tfa98xx_DspBiquad_Disable(struct tfa_device *tfa, + int biquad_index); + +/** + * fill the calibration value as milli ohms in the struct + * assume that the device has been calibrated + */ +enum Tfa98xx_Error +tfa_dsp_get_calibration_impedance(struct tfa_device *tfa); + +/* + * return the mohm value + */ +int tfa_get_calibration_info(struct tfa_device *tfa, int channel); + +/* + * return sign extended tap pattern + */ +int tfa_get_tap_pattern(struct tfa_device *tfa); + +/** + * Reads a number of words from dsp memory + * @param tfa the device struct pointer + * @param subaddress write address to set in address register + * @param pValue pointer to read data +*/ +enum Tfa98xx_Error tfa98xx_read_register16(struct tfa_device *tfa, + unsigned char subaddress, + unsigned short *pValue); + +/** + * Reads a number of words from dsp memory + * @param tfa the device struct pointer + * @param subaddress write address to set in address register + * @param value value to write int the memory +*/ +enum Tfa98xx_Error tfa98xx_write_register16(struct tfa_device *tfa, + unsigned char subaddress, + unsigned short value); + +/** + * Intialise the dsp + * @param tfa the device struct pointer + * @return tfa error enum +*/ +enum Tfa98xx_Error +tfa98xx_init_dsp(struct tfa_device *tfa); + +/** + * Get the status of the external DSP + * @param tfa the device struct pointer + * @return status +*/ +int tfa98xx_get_dsp_status(struct tfa_device *tfa); + +/** + * Write a command message (RPC) to the dsp + * @param tfa the device struct pointer + * @param num_bytes command buffer size in bytes + * @param command_buffer + * @return tfa error enum +*/ +enum Tfa98xx_Error +tfa98xx_write_dsp(struct tfa_device *tfa, int num_bytes, const char *command_buffer); + +/** + * Read the result from the last message from the dsp + * @param tfa the device struct pointer + * @param num_bytes result buffer size in bytes + * @param result_buffer + * @return tfa error enum +*/ +enum Tfa98xx_Error +tfa98xx_read_dsp(struct tfa_device *tfa, int num_bytes, unsigned char *result_buffer); + +/** + * Write a command message (RPC) to the dsp and return the result + * @param tfa the device struct pointer + * @param command_length command buffer size in bytes + * @param command_buffer command buffer + * @param result_length result buffer size in bytes + * @param result_buffer result buffer + * @return tfa error enum +*/ +enum Tfa98xx_Error +tfa98xx_writeread_dsp(struct tfa_device *tfa, int command_length, void *command_buffer, + int result_length, void *result_buffer); + +/** + * Reads a number of words from dsp memory + * @param tfa the device struct pointer + * @param start_offset offset from where to start reading + * @param num_words number of words to read + * @param pValues pointer to read data +*/ +enum Tfa98xx_Error tfa98xx_dsp_read_mem(struct tfa_device *tfa, + unsigned int start_offset, + int num_words, int *pValues); +/** + * Write a value to dsp memory + * @param tfa the device struct pointer + * @param address write address to set in address register + * @param value value to write int the memory + * @param memtype type of memory to write to +*/ +enum Tfa98xx_Error tfa98xx_dsp_write_mem_word(struct tfa_device *tfa, + unsigned short address, int value, int memtype); + +/** + * Read data from dsp memory + * @param tfa the device struct pointer + * @param subaddress write address to set in address register + * @param num_bytes number of bytes to read from dsp + * @param data the unsigned char buffer to read data into +*/ +enum Tfa98xx_Error tfa98xx_read_data(struct tfa_device *tfa, + unsigned char subaddress, + int num_bytes, unsigned char data[]); + +/** + * Write all the bytes specified by num_bytes and data to dsp memory + * @param tfa the device struct pointer + * @param subaddress the subaddress to write to + * @param num_bytes number of bytes to write + * @param data actual data to write +*/ +enum Tfa98xx_Error tfa98xx_write_data(struct tfa_device *tfa, + unsigned char subaddress, + int num_bytes, + const unsigned char data[]); + +enum Tfa98xx_Error tfa98xx_write_raw(struct tfa_device *tfa, + int num_bytes, + const unsigned char data[]); + +/* support for converting error codes into text */ +const char *tfa98xx_get_error_string(enum Tfa98xx_Error error); + +/** + * convert signed 24 bit integers to 32bit aligned bytes + * input: data contains "num_bytes/3" int24 elements + * output: bytes contains "num_bytes" byte elements + * @param num_data length of the input data array + * @param data input data as integer array + * @param bytes output data as unsigned char array +*/ +void tfa98xx_convert_data2bytes(int num_data, const int data[], + unsigned char bytes[]); + +/** + * convert memory bytes to signed 24 bit integers + * input: bytes contains "num_bytes" byte elements + * output: data contains "num_bytes/3" int24 elements + * @param num_bytes length of the input data array + * @param bytes input data as unsigned char array + * @param data output data as integer array +*/ +void tfa98xx_convert_bytes2data(int num_bytes, const unsigned char bytes[], + int data[]); + +/** + * Read a part of the dsp memory + * @param tfa the device struct pointer + * @param memoryType indicator to the memory type + * @param offset from where to start reading + * @param length the number of bytes to read + * @param bytes output data as unsigned char array +*/ +enum Tfa98xx_Error tfa98xx_dsp_get_memory(struct tfa_device *tfa, int memoryType, + int offset, int length, unsigned char bytes[]); + +/** + * Write a value to the dsp memory + * @param tfa the device struct pointer + * @param memoryType indicator to the memory type + * @param offset from where to start writing + * @param length the number of bytes to write + * @param value the value to write to the dsp +*/ +enum Tfa98xx_Error tfa98xx_dsp_set_memory(struct tfa_device *tfa, int memoryType, + int offset, int length, int value); + +enum Tfa98xx_Error tfa98xx_dsp_write_config(struct tfa_device *tfa, int length, const unsigned char *p_config_bytes); +enum Tfa98xx_Error tfa98xx_dsp_write_drc(struct tfa_device *tfa, int length, const unsigned char *p_drc_bytes); + +/** + * write/read raw msg functions : + * the buffer is provided in little endian format, each word occupying 3 bytes, length is in bytes. + * The functions will return immediately and do not not wait for DSP reponse. + * @param tfa the device struct pointer + * @param length length of the character buffer to write + * @param buf character buffer to write +*/ +enum Tfa98xx_Error tfa_dsp_msg(struct tfa_device *tfa, int length, const char *buf); + + +/** + * The wrapper functions to call the dsp msg, register and memory function for tfa or probus + */ +enum Tfa98xx_Error dsp_msg(struct tfa_device *tfa, int length, const char *buf); +enum Tfa98xx_Error dsp_msg_read(struct tfa_device *tfa, int length, unsigned char *bytes); +enum Tfa98xx_Error reg_write(struct tfa_device *tfa, unsigned char subaddress, unsigned short value); +enum Tfa98xx_Error reg_read(struct tfa_device *tfa, unsigned char subaddress, unsigned short *value); +enum Tfa98xx_Error mem_write(struct tfa_device *tfa, unsigned short address, int value, int memtype); +enum Tfa98xx_Error mem_read(struct tfa_device *tfa, unsigned int start_offset, int num_words, int *pValues); + +enum Tfa98xx_Error dsp_partial_coefficients(struct tfa_device *tfa, uint8_t *prev, uint8_t *next); + +/** + * write/read raw msg functions: + * the buffer is provided in little endian format, each word occupying 3 bytes, length is in bytes. + * The functions will return immediately and do not not wait for DSP reponse. + * An ID is added to modify the command-ID + * @param tfa the device struct pointer + * @param length length of the character buffer to write + * @param buf character buffer to write + * @param cmdid command identifier +*/ +enum Tfa98xx_Error tfa_dsp_msg_id(struct tfa_device *tfa, int length, const char *buf, uint8_t cmdid[3]); + +/** + * write raw dsp msg functions + * @param tfa the device struct pointer + * @param length length of the character buffer to write + * @param buffer character buffer to write +*/ +enum Tfa98xx_Error tfa_dsp_msg_write(struct tfa_device *tfa, int length, const char *buffer); + +/** + * write raw dsp msg functions + * @param tfa the device struct pointer + * @param length length of the character buffer to write + * @param buffer character buffer to write + * @param cmdid command identifier +*/ +enum Tfa98xx_Error tfa_dsp_msg_write_id(struct tfa_device *tfa, int length, const char *buffer, uint8_t cmdid[3]); + +/** + * status function used by tfa_dsp_msg() to retrieve command/msg status: + * return a <0 status of the DSP did not ACK. + * @param tfa the device struct pointer + * @param pRpcStatus status for remote processor communication +*/ +enum Tfa98xx_Error tfa_dsp_msg_status(struct tfa_device *tfa, int *pRpcStatus); + +/** + * Read a message from dsp + * @param tfa the device struct pointer + * @param length number of bytes of the message + * @param bytes pointer to unsigned char buffer +*/ +enum Tfa98xx_Error tfa_dsp_msg_read(struct tfa_device *tfa,int length, unsigned char *bytes); + +int tfa_set_bf(struct tfa_device *tfa, const uint16_t bf, const uint16_t value); +int tfa_set_bf_volatile(struct tfa_device *tfa, const uint16_t bf, const uint16_t value); + +/** + * Get the value of a given bitfield + * @param tfa the device struct pointer + * @param bf the value indicating which bitfield + */ +int tfa_get_bf(struct tfa_device *tfa, const uint16_t bf); + +/** + * Set the value of a given bitfield + * @param bf the value indicating which bitfield + * @param bf_value the value of the bitfield + * @param p_reg_value a pointer to the register where to write the bitfield value + */ +int tfa_set_bf_value(const uint16_t bf, const uint16_t bf_value, uint16_t *p_reg_value); + +uint16_t tfa_get_bf_value(const uint16_t bf, const uint16_t reg_value); +int tfa_write_reg(struct tfa_device *tfa, const uint16_t bf, const uint16_t reg_value); +int tfa_read_reg(struct tfa_device *tfa, const uint16_t bf); + +/* bitfield */ +/** + * get the datasheet or bitfield name corresponding to the bitfield number + * @param num is the number for which to get the bitfield name + * @param rev is the device type + */ +char *tfaContBfName(uint16_t num, unsigned short rev); + +/** + * get the datasheet name corresponding to the bitfield number + * @param num is the number for which to get the bitfield name + * @param rev is the device type + */ +char *tfaContDsName(uint16_t num, unsigned short rev); + +/** + * get the bitfield name corresponding to the bitfield number + * @param num is the number for which to get the bitfield name + * @param rev is the device type + */ +char *tfaContBitName(uint16_t num, unsigned short rev); + +/** + * get the bitfield number corresponding to the bitfield name + * @param name is the bitfield name for which to get the bitfield number + * @param rev is the device type + */ +uint16_t tfaContBfEnum(const char *name, unsigned short rev); + +/** +* get the bitfield number corresponding to the bitfield name, checks for all devices +* @param name is the bitfield name for which to get the bitfield number + */ +uint16_t tfaContBfEnumAny(const char *name); + +#define TFA_FAM(tfa, fieldname) ((tfa->tfa_family == 1) ? TFA1_BF_##fieldname : TFA2_BF_##fieldname) +#define TFA_FAM_FW(tfa, fwname) ((tfa->tfa_family == 1) ? TFA1_FW_##fwname : TFA2_FW_##fwname) + +/* set/get bit fields to HW register*/ +#define TFA_SET_BF(tfa, fieldname, value) tfa_set_bf(tfa, TFA_FAM(tfa, fieldname), value) +#define TFA_SET_BF_VOLATILE(tfa, fieldname, value) tfa_set_bf_volatile(tfa, TFA_FAM(tfa, fieldname), value) +#define TFA_GET_BF(tfa, fieldname) tfa_get_bf(tfa, TFA_FAM(tfa, fieldname)) + +/* set/get bit field in variable */ +#define TFA_SET_BF_VALUE(tfa, fieldname, bf_value, p_reg_value) tfa_set_bf_value(TFA_FAM(tfa, fieldname), bf_value, p_reg_value) +#define TFA_GET_BF_VALUE(tfa, fieldname, reg_value) tfa_get_bf_value(TFA_FAM(tfa, fieldname), reg_value) + +/* write/read registers using a bit field name to determine the register address */ +#define TFA_WRITE_REG(tfa, fieldname, value) tfa_write_reg(tfa, TFA_FAM(tfa, fieldname), value) +#define TFA_READ_REG(tfa, fieldname) tfa_read_reg(tfa, TFA_FAM(tfa, fieldname)) + +/* FOR CALIBRATION RETRIES */ +#define TFA98XX_API_WAITRESULT_NTRIES 3000 // defined in API + +/** + * run the startup/init sequence and set ACS bit + * @param tfa the device struct pointer + * @param state the cold start state that is requested + */ +enum Tfa98xx_Error tfaRunColdboot(struct tfa_device *tfa, int state); +enum Tfa98xx_Error tfaRunMute(struct tfa_device *tfa); +enum Tfa98xx_Error tfaRunUnmute(struct tfa_device *tfa); + +/** + * wait for calibrateDone + * @param tfa the device struct pointer + * @param calibrateDone pointer to status of calibration + */ +enum Tfa98xx_Error tfaRunWaitCalibration(struct tfa_device *tfa, int *calibrateDone); + +/** + * run the startup/init sequence and set ACS bit + * @param tfa the device struct pointer + * @param profile the profile that should be loaded + */ +enum Tfa98xx_Error tfaRunColdStartup(struct tfa_device *tfa, int profile); + +/** + * this will load the patch witch will implicitly start the DSP + * if no patch is available the DPS is started immediately + * @param tfa the device struct pointer + */ +enum Tfa98xx_Error tfaRunStartDSP(struct tfa_device *tfa); + +/** + * start the clocks and wait until the AMP is switching + * on return the DSP sub system will be ready for loading + * @param tfa the device struct pointer + * @param profile the profile that should be loaded on startup + */ +enum Tfa98xx_Error tfaRunStartup(struct tfa_device *tfa, int profile); + +/** + * start the maximus speakerboost algorithm + * this implies a full system startup when the system was not already started + * @param tfa the device struct pointer + * @param force indicates wether a full system startup should be allowed + * @param profile the profile that should be loaded + */ +enum Tfa98xx_Error tfaRunSpeakerBoost(struct tfa_device *tfa, int force, int profile); + +/** + * Startup the device and write all files from device and profile section + * @param tfa the device struct pointer + * @param force indicates wether a full system startup should be allowed + * @param profile the profile that should be loaded on speaker startup + */ +enum Tfa98xx_Error tfaRunSpeakerStartup(struct tfa_device *tfa, int force, int profile); + +/** + * Run calibration + * @param tfa the device struct pointer + */ +enum Tfa98xx_Error tfaRunSpeakerCalibration(struct tfa_device *tfa); + +/** + * startup all devices. all step until patch loading is handled + * @param tfa the device struct pointer + */ +int tfaRunStartupAll(struct tfa_device *tfa); + +/** + * powerup the coolflux subsystem and wait for it + * @param tfa the device struct pointer + */ +enum Tfa98xx_Error tfa_cf_powerup(struct tfa_device *tfa); + +/* + * print the current device manager state + * @param tfa the device struct pointer + */ +enum Tfa98xx_Error show_current_state(struct tfa_device *tfa); + +/** + * Init registers and coldboot dsp + * @param tfa the device struct pointer + */ +int tfa_reset(struct tfa_device *tfa); + +/** + * Get profile from a register + * @param tfa the device struct pointer + */ +int tfa_dev_get_swprof(struct tfa_device *tfa); + +/** + * Save profile in a register + */ +int tfa_dev_set_swprof(struct tfa_device *tfa, unsigned short new_value); + +int tfa_dev_get_swvstep(struct tfa_device *tfa); + +int tfa_dev_set_swvstep(struct tfa_device *tfa, unsigned short new_value); + +int tfa_needs_reset(struct tfa_device *tfa); + +int tfa_is_cold(struct tfa_device *tfa); + +void tfa_set_query_info(struct tfa_device *tfa); + +int tfa_get_pga_gain(struct tfa_device *tfa); +int tfa_set_pga_gain(struct tfa_device *tfa, uint16_t value); +int tfa_get_noclk(struct tfa_device *tfa); + +/** + * Status of used for monitoring + * @param tfa the device struct pointer + * @return tfa error enum + */ + +enum Tfa98xx_Error tfa_status(struct tfa_device *tfa); + +/* + * function overload for flag_mtp_busy + */ +int tfa_dev_get_mtpb(struct tfa_device *tfa); + +#ifdef __cplusplus +} +#endif +#endif /* TFA_SERVICE_H */ diff --git a/sound/soc/codecs/tas2560-algo.c b/sound/soc/codecs/tas2560-algo.c new file mode 100644 index 000000000000..cbc28a854d20 --- /dev/null +++ b/sound/soc/codecs/tas2560-algo.c @@ -0,0 +1,845 @@ +/* Copyright (c) 2012-2017, The Linux Foundation. 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 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. + */ + +#ifdef CONFIG_SND_SOC_TAS2560 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct mutex routing_lock; +/*Port IDs to be read from dts*/ +static uint32_t tx_port_id = TAS2560_ALGO_TX_PORT_ID; +static uint32_t rx_port_id = TAS2560_ALGO_RX_PORT_ID; +/*SPK IDs to be read from dts*/ +static uint32_t spk_id = 0; +/*Not much significance and can be removed in future*/ +static uint8_t calib_status; +static int32_t force_algo_disable; + +/*Pointer to hold the data received from DSP +* TODO:Decide whether it needs memory or pointer is sufficient +*/ +struct afe_tas2560_algo_params_t *tas2560_algo_payload; + +/*Call back to receive data from DSP*/ +static int32_t tas2560_algo_afe_callback(struct apr_client_data *data) +{ + if (!data) { + pr_err("TAS2560_ALGO:%s Invalid param data\n", __func__); + return -EINVAL; + } + + if (data->opcode == AFE_PORT_CMDRSP_GET_PARAM_V2) { + uint32_t *payload32 = data->payload; + + if ((payload32[1] == AFE_TAS2560_ALGO_MODULE_RX) || + (payload32[1] == AFE_TAS2560_ALGO_MODULE_TX)) { + tas2560_algo_payload = + (struct afe_tas2560_algo_params_t *)&payload32[4]; + } + } + return 0; +} + +/*AFE set param to DSP*/ +static int tas2560_algo_afe_set_param(uint32_t param_id, uint32_t module_id, + struct afe_tas2560_algo_set_config_t *arg1) +{ + int result = 0; + int index = 0; + int size = 0; + uint32_t port_id = rx_port_id; + struct afe_tas2560_algo_set_config_t *config = NULL; + struct afe_tas2560_algo_params_t *data_payload = NULL; + + if (module_id == AFE_TAS2560_ALGO_MODULE_TX) + port_id = tx_port_id; + + if ((q6audio_validate_port(port_id) < 0)) { + pr_err("TAS2560_ALGO:%s invalid port %d", __func__, port_id); + return -EINVAL; + } + + index = afe_get_port_index(port_id); + + /* Allocate memory for the message */ + size = sizeof(struct afe_tas2560_algo_set_config_t) + + sizeof(struct afe_tas2560_algo_params_t); + config = kzalloc(size, GFP_KERNEL); + if (config == NULL) { + pr_err("TAS2560_ALGO:%s Memory allocation failed!\n", + __func__); + return -ENOMEM; + } + data_payload = (struct afe_tas2560_algo_params_t *) + ((u8 *)config + sizeof(struct afe_tas2560_algo_set_config_t)); + + /* Configure actual parameter settings */ + if (arg1) + memcpy(data_payload->payload, arg1, + sizeof(struct afe_tas2560_algo_params_t)); + else + pr_err("TAS2560_ALGO:%s arg1 Null pointer received\n", + __func__); + + /* Set header section */ + config->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + config->hdr.pkt_size = size; + config->hdr.src_svc = APR_SVC_AFE; + config->hdr.src_domain = APR_DOMAIN_APPS; + config->hdr.src_port = 0; + config->hdr.dest_svc = APR_SVC_AFE; + config->hdr.dest_domain = APR_DOMAIN_ADSP; + config->hdr.dest_port = 0; + config->hdr.token = index; + config->hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2; + + /* Set param section */ + config->param.port_id = port_id; + config->param.payload_size = + sizeof(struct afe_port_param_data_v2) + + sizeof(struct afe_tas2560_algo_params_t); + config->param.payload_address_lsw = 0; + config->param.payload_address_msw = 0; + config->param.mem_map_handle = 0; + + /* Set data section */ + config->data.module_id = module_id; + config->data.param_id = param_id; + config->data.param_size = sizeof(struct afe_tas2560_algo_params_t); + config->data.reserved = 0; + + result = tas2560_algo_afe_apr_send_pkt(config, index); + if (result) { + pr_debug("TAS2560_ALGO:%s port %d failed with code %d\n", + __func__, port_id, result); + } else { + pr_debug("TAS2560_ALGO:%s packet 0x%08X to module 0x%08X\n", + __func__, param_id, module_id); + } + kfree(config); + return result; +} + +/*AFE get param to DSP*/ +static int tas2560_algo_afe_get_param(uint32_t param_id, uint32_t module_id) +{ + int result = 0; + int index = 0; + int size = 0; + uint32_t port_id = rx_port_id; + struct afe_tas2560_algo_get_config_t *config = NULL; + + if (module_id == AFE_TAS2560_ALGO_MODULE_TX) + port_id = tx_port_id; + + if ((q6audio_validate_port(port_id) < 0)) { + pr_err("TAS2560_ALGO:%s invalid port %d", __func__, port_id); + return -EINVAL; + } + + index = afe_get_port_index(port_id); + + /* Allocate memory for the message */ + size = sizeof(struct afe_tas2560_algo_get_config_t); + config = kzalloc(size, GFP_KERNEL); + if (config == NULL) { + pr_err("TAS2560_ALGO:%s Memory allocation failed!\n", + __func__); + return -ENOMEM; + } + + /* Set header section */ + config->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + config->hdr.pkt_size = size; + config->hdr.src_svc = APR_SVC_AFE; + config->hdr.src_domain = APR_DOMAIN_APPS; + config->hdr.src_port = 0; + config->hdr.dest_svc = APR_SVC_AFE; + config->hdr.dest_domain = APR_DOMAIN_ADSP; + config->hdr.dest_port = 0; + config->hdr.token = index; + config->hdr.opcode = AFE_PORT_CMD_GET_PARAM_V2; + + /* Set param section */ + config->param.port_id = port_id; + config->param.payload_size = + sizeof(struct afe_port_param_data_v2); + config->param.payload_address_lsw = 0; + config->param.payload_address_msw = 0; + config->param.mem_map_handle = 0; + config->param.module_id = module_id; + config->param.param_id = param_id; + + /* Set data section */ + config->data.module_id = module_id; + config->data.param_id = param_id; + config->data.reserved = 0; + + /* payload and param size section */ + config->param.payload_size += + sizeof(struct afe_tas2560_algo_params_t); + config->data.param_size = + sizeof(struct afe_tas2560_algo_params_t); + + result = tas2560_algo_afe_apr_send_pkt(config, index); + if (result) { + pr_debug("TAS2560_ALGO:%s for port %d failed with code %d\n", + __func__, port_id, result); + } else { + pr_debug("TAS2560_ALGO:%s packet 0x%08X to module 0x%08X\n", + __func__, param_id, module_id); + } + kfree(config); + return result; +} + +/*Wrapper to handle get set params*/ +static int tas2560_algo_get_set(u8 *user_data, uint32_t param_id, + uint32_t module_id, uint8_t get_set, int32_t length) +{ + int32_t ret = 0; + + switch (get_set) { + case TAS2560_ALGO_SET_PARAM: + pr_debug("TAS2560_ALGO:%s set param\n", __func__); + ret = tas2560_algo_afe_set_param(param_id, module_id, + (struct afe_tas2560_algo_set_config_t *)user_data); + break; + case TAS2560_ALGO_GET_PARAM: + pr_debug("TAS2560_ALGO:%s get param\n", __func__); + tas2560_algo_payload = NULL; + tas2560_algo_afe_set_callback(tas2560_algo_afe_callback); + ret = tas2560_algo_afe_get_param(param_id, module_id); + if ((user_data) && (tas2560_algo_payload)) + memcpy(user_data, tas2560_algo_payload, + TAS2560_ALGO_MIN(length, + sizeof(tas2560_algo_payload))); + else + pr_err("TAS2560_ALGO:%s GetParam Failed\n", + __func__); + break; + default: + pr_err("TAS2560_ALGO:%s Invalid case\n", __func__); + ret = -EINVAL; + break; + } + return ret; +} +/*Wrapper to control(with mutex) the get set params*/ +int afe_tas2560_algo_ctrl(u8 *data, u32 param_id, u32 module_id, u8 dir, + u8 size) +{ + int32_t ret = 0; + + mutex_lock(&routing_lock); + ret = tas2560_algo_get_set(data, param_id, module_id, dir, size); + mutex_unlock(&routing_lock); + + return ret; +} +EXPORT_SYMBOL(afe_tas2560_algo_ctrl); +/* FB Info */ + +/*Control:1 Start/Stop commands for Rdc-Calibration/F0-Q-test */ +static const char * const tas2560_algo_calib_text[] = { + "NONE", + "CALIB_START_RDC", + "CALIB_STOP_RDC", + "CALIB_START_F0_Q", + "CALIB_STOP_F0_Q" +}; + +static const struct soc_enum tas2560_algo_calib_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas2560_algo_calib_text), + tas2560_algo_calib_text) +}; +/*Can be ignored*/ +static int tas2560_algo_calib_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = calib_status; + pr_err("TAS2560_ALGO:%s data %d\n", __func__, calib_status); + return 0; +} + +static int tas2560_algo_calib_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + ((int32_t *)buff)[0] = 1; + calib_status = ucontrol->value.integer.value[0]; + switch (calib_status) { + case 0: + pr_err("TAS2560_ALGO:%s NONE\n", __func__); + break; + case 1: + paramid = TAS2560_ALGO_CALC_PIDX(paramid, + TAS2560_ALGO_CALIB_START_RDC, 1, SLAVE1); + pr_err("TAS2560_ALGO:%s CALIB_START_RDC paramid 0x%x\n", + __func__, paramid); + break; + case 2: + paramid = TAS2560_ALGO_CALC_PIDX(paramid, + TAS2560_ALGO_CALIB_STOP_RDC, 1, SLAVE1); + pr_err("TAS2560_ALGO:%s CALIB_STOP_RDC paramid 0x%x\n", + __func__, paramid); + break; + case 3: + pr_err("TAS2560_ALGO:%s CALIB_START_F0_Q Not Implemented\n", + __func__); + break; + case 4: + pr_err("TAS2560_ALGO:%s CALIB_STOP_F0_Q Not Implemented\n", + __func__); + break; + default: + pr_err("TAS2560_ALGO:%s Invalid Case 0x%x\n", __func__, + paramid); + ret = -EINVAL; + break; + } + + if (paramid) + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + return ret; +} + +/*Control:2 Command to get Rdc result after Rdc-Calibration*/ +static int tas2560_algo_get_re(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + int32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + paramid = TAS2560_ALGO_CALC_PIDX(paramid, TAS2560_ALGO_GET_RDC, 1, + SLAVE1); + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_GET_PARAM, sizeof(u32)); + ucontrol->value.integer.value[0] = ((int32_t *)buff)[0]; + + pr_err("TAS2560_ALGO:%s Rdc 0x%x\n", __func__, + (unsigned int)ucontrol->value.integer.value[0]); + return ret; +} + +/*Control:3 Command to get F0 result after F0-Q-test*/ +static int tas2560_algo_get_f0(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + paramid = TAS2560_ALGO_CALC_PIDX(paramid, TAS2560_ALGO_GET_F0, 1, + SLAVE1); + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_GET_PARAM, sizeof(u32)); + ucontrol->value.integer.value[0] = ((int32_t *)buff)[0]; + + pr_err("TAS2560_ALGO:%s F0 0x%x\n", __func__, + (unsigned int)ucontrol->value.integer.value[0]); + return ret; +} + +/*Control:4 Command to get Q result after F0-Q-test*/ +static int tas2560_algo_get_q(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + paramid = TAS2560_ALGO_CALC_PIDX(paramid, TAS2560_ALGO_GET_Q, 1, + SLAVE1); + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_GET_PARAM, sizeof(u32)); + ucontrol->value.integer.value[0] = ((int32_t *)buff)[0]; + + pr_err("TAS2560_ALGO:%s Q 0x%x\n", __func__, + (unsigned int)ucontrol->value.integer.value[0]); + return ret; +} + +/*Control:5 Command to bypass algorithm dynaically*/ +static const char * const tas2560_algo_text[] = { + "DISABLE", + "ENABLE" +}; + +static const struct soc_enum tas2560_algo_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas2560_algo_text), tas2560_algo_text) +}; + + +static int tas2560_algo_get_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + paramid = TAS2560_ALGO_CALC_PIDX(paramid, TAS2560_ALGO_CFG_ENABLE, 1, + SLAVE1); + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_GET_PARAM, sizeof(u32)); + ucontrol->value.integer.value[0] = ((int32_t *)buff)[0]; + pr_err("TAS2560_ALGO:%s status data %d", __func__, + ((int32_t *)buff)[0]); + return ret; +} + +static int tas2560_algo_set_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + ((int32_t *)buff)[0] = ucontrol->value.integer.value[0]; + pr_err("TAS2560_ALGO:%s status data %d", __func__, + ((int32_t *)buff)[0]); + paramid = TAS2560_ALGO_CALC_PIDX(paramid, TAS2560_ALGO_CFG_ENABLE, 1, + SLAVE1); + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + return ret; +} + +/*Control:6 Command to set calibrated Rdc*/ +static int tas2560_algo_set_cal(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_TX; + u8 buff[56] = {0}; + u8 *ptr = buff; + u8 re_count = 0, i = 0; + + /*If algo is forced to be disable, we do nothing here*/ + if (force_algo_disable) { + pr_err("TAS2560_ALGO,%s,the algo is force disabled,%d\n", + __func__, force_algo_disable); + return ret; + } + + /*Count is ignored as it is mono*/ + pr_err("TAS2560_ALGO:%s [0] = %ld [1] = %ld\n", __func__, + ucontrol->value.integer.value[0], + ucontrol->value.integer.value[1]); + re_count = ucontrol->value.integer.value[0]; + + /*Sending TX parameters + *TODO:Values of left-I,right-V or left-V,right-I + *can be made generic by reading from dts file + */ + pr_err("TAS2560_ALGO:%s Sending Tx-FB info", __func__); + ret = afe_spk_prot_feed_back_cfg(tx_port_id, rx_port_id, + 1, 0, 1); + + if (!ret) { + pr_err("TAS2560_ALGO:%s Sending Tx-Enable", __func__); + paramid = AFE_PARAM_ID_TAS2560_ALGO_TX_ENABLE; + ((int32_t *)buff)[0] = 1; + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + } else + pr_err("TAS2560_ALGO:%s Sending Tx-Enable already enabled\n", + __func__); + + /*Sending RX parameters*/ + moduleid = AFE_TAS2560_ALGO_MODULE_RX; + pr_err("TAS2560_ALGO:%s Sending Rx-Enable data", __func__); + paramid = AFE_PARAM_ID_TAS2560_ALGO_RX_ENABLE; + ((int32_t *)buff)[0] = 1; + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + if (!ret) { + pr_err("TAS2560_ALGO:%s Sending Speaker ID : %d",__func__,spk_id); + paramid = TAS2560_ALGO_CALC_PIDX(0, + AFE_PARAM_ID_TAS2560_SPEAKER_ID, 1,SLAVE1); + ((int32_t *)buff)[0] = spk_id; + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + + pr_err("TAS2560_ALGO:%s Sending Rx-Cfg", __func__); + paramid = AFE_PARAM_ID_TAS2560_ALGO_RX_CFG; + ((int32_t *)buff)[0] = 1; + afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + + pr_err("TAS2560_ALGO:%s Sending Rdc", __func__); + /*TODO:Note multi channel send Rdc is not verified*/ + for (i = 0; i < re_count; i++) { + paramid = TAS2560_ALGO_CALC_PIDX(0, + TAS2560_ALGO_CFG_SET_RE, 1, GET_SLAVE(i+1)); + ((int32_t *)buff)[0] = + ucontrol->value.integer.value[i+1]; + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + } + } else + pr_err("TAS2560_ALGO:%s Sending Rx-Enable failed", __func__); + return ret; +} + +/*Control:7 Command to Enable FF module*/ +static const char * const tas2560_algo_ff_module_text[] = { + "DISABLE", + "ENABLE" +}; + +static const struct soc_enum tas2560_algo_ff_module_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas2560_algo_ff_module_text), + tas2560_algo_ff_module_text) +}; + + +static int tas2560_algo_get_ff_module(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + int32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + paramid = AFE_PARAM_ID_TAS2560_ALGO_RX_ENABLE; + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_GET_PARAM, sizeof(u32)); + ucontrol->value.integer.value[0] = ((int32_t *)buff)[0]; + pr_err("TAS2560_ALGO:%s Recieving Rx-Enable data %d", __func__, + ((int32_t *)buff)[0]); + return ret; +} + +static int tas2560_algo_set_ff_module(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + ((int32_t *)buff)[0] = ucontrol->value.integer.value[0]; + pr_err("TAS2560_ALGO:%s Sending Rx-Enable data %d", __func__, + ((int32_t *)buff)[0]); + + if ((force_algo_disable) && (((int32_t *)buff)[0])) { + pr_err("TAS2560_ALGO:%s,try to enable ff while force disabled", + __func__); + return ret; + } + + paramid = AFE_PARAM_ID_TAS2560_ALGO_RX_ENABLE; + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + + return ret; +} +/*Control:8 Command to Enable FB module*/ +static const char * const tas2560_algo_fb_module_text[] = { + "DISABLE", + "ENABLE" +}; + +static const struct soc_enum tas2560_algo_fb_module_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas2560_algo_fb_module_text), + tas2560_algo_fb_module_text) +}; + + +static int tas2560_algo_get_fb_module(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + int32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_TX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + paramid = AFE_PARAM_ID_TAS2560_ALGO_TX_ENABLE; + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_GET_PARAM, sizeof(u32)); + ucontrol->value.integer.value[0] = ((int32_t *)buff)[0]; + pr_err("TAS2560_ALGO:%s Recieving Tx-Enable data %d", __func__, + ((int32_t *)buff)[0]); + return ret; +} + +static int tas2560_algo_set_fb_module(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_TX; + u8 buff[56] = {0}; + u8 *ptr = buff; + ((int32_t *)buff)[0] = ucontrol->value.integer.value[0]; + + pr_err("TAS2560_ALGO:%s Sending Tx-Enable data %d", __func__, + ((int32_t *)buff)[0]); + + if ((force_algo_disable) && (((int32_t *)buff)[0])) { + pr_err("TAS2560_ALGO:%s,try to enable fb while force disabled", + __func__); + return ret; + } + + paramid = AFE_PARAM_ID_TAS2560_ALGO_TX_ENABLE; + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + + return ret; +} +/*Control:9 Command to Set Profile*/ +/*TODO:Profile count can be extended, no implementation restrictions*/ +static const char * const tas2560_algo_profile_text[] = { + "NORMAL", + "VOICE", + "RING", + "SONIFICATION", + "CALIBRATION" +}; + +static const struct soc_enum tas2560_algo_profile_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas2560_algo_profile_text), + tas2560_algo_profile_text) +}; + + +static int tas2560_algo_get_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + int32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + paramid = TAS2560_ALGO_CALC_PIDX(paramid, TAS2560_ALGO_PROFILE, 1, + SLAVE1); + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_GET_PARAM, sizeof(u32)); + ucontrol->value.integer.value[0] = ((int32_t *)buff)[0]; + pr_debug("TAS2560_ALGO:%s %ld\n", __func__, + ucontrol->value.integer.value[0]); + + return ret; +} + +static int tas2560_algo_set_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}; + u8 *ptr = buff; + + ((int32_t *)buff)[0] = ucontrol->value.integer.value[0]; + pr_debug("TAS2560_ALGO: %s %ld\n", + __func__, ucontrol->value.integer.value[0]); + paramid = TAS2560_ALGO_CALC_PIDX(paramid, + TAS2560_ALGO_PROFILE, 1, SLAVE1); + ret = afe_tas2560_algo_ctrl(ptr, paramid, moduleid, + TAS2560_ALGO_SET_PARAM, sizeof(u32)); + return ret; +} + +/*Control:10 Command to disable TAS2560 ALGO to be enabled by audio hal, +* used only for factory mode +*/ +static const char * const tas2560_algo_disable_text[] = { + "FALSE", + "TRUE" +}; + +static const struct soc_enum tas2560_algo_disable_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas2560_algo_disable_text), + tas2560_algo_disable_text) +}; + + +static int tas2560_algo_get_disable(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = force_algo_disable; + pr_err("TAS2560_ALGO:%s force_algo_disable=%d", + __func__, force_algo_disable); + return 0; +} + +static int tas2560_algo_set_disable(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + force_algo_disable = ucontrol->value.integer.value[0]; + pr_err("TAS2560_ALGO:%s force_algo_disable=%d", + __func__, force_algo_disable); + return 0; +} + +static int tas2560_algo_get_tv(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + uint32_t paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u8 buff[56] = {0}, *ptr = buff; + + paramid = TAS2560_ALGO_CALC_PIDX(paramid, + TAS2560_ALGO_GET_TV, 1, SLAVE1); + ret = afe_tas2560_algo_ctrl(ptr, + paramid, moduleid, TAS2560_ALGO_GET_PARAM, sizeof(u32)); + ucontrol->value.integer.value[0] = ((int32_t *)buff)[0]; + + pr_err("TAS2560_ALGO:%s TV 0x%x\n", __func__, + (unsigned int)ucontrol->value.integer.value[0]); + return ret; +} + +const struct snd_kcontrol_new tas2560_algo_filter_mixer_controls[] = { + SOC_ENUM_EXT("TAS2560_ALGO_CALIB", tas2560_algo_calib_enum[0], + tas2560_algo_calib_get, tas2560_algo_calib_set), + SOC_SINGLE_EXT("TAS2560_ALGO_GET_RE", SND_SOC_NOPM, 0, 0x7FFFFFFF, 0, + tas2560_algo_get_re, NULL), + SOC_SINGLE_EXT("TAS2560_ALGO_GET_F0", SND_SOC_NOPM, 0, 0x7FFFFFFF, 0, + tas2560_algo_get_f0, NULL), + SOC_SINGLE_EXT("TAS2560_ALGO_GET_Q", SND_SOC_NOPM, 0, 0x7FFFFFFF, 0, + tas2560_algo_get_q, NULL), + SOC_SINGLE_EXT("TAS2560_ALGO_GET_TV", SND_SOC_NOPM, 0, 0x7FFFFFFF, 0, + tas2560_algo_get_tv, NULL), + SOC_SINGLE_MULTI_EXT("TAS2560_ALGO_CMD_SEND_CAL", SND_SOC_NOPM, 0, + 0x7FFFFFFF, 0, 5, NULL, tas2560_algo_set_cal), + SOC_ENUM_EXT("TAS2560_ALGO_STATUS", tas2560_algo_enum[0], + tas2560_algo_get_status, tas2560_algo_set_status), + SOC_ENUM_EXT("TAS2560_ALGO_FF_MODULE", tas2560_algo_ff_module_enum[0], + tas2560_algo_get_ff_module, tas2560_algo_set_ff_module), + SOC_ENUM_EXT("TAS2560_ALGO_FB_MODULE", tas2560_algo_fb_module_enum[0], + tas2560_algo_get_fb_module, tas2560_algo_set_fb_module), + SOC_ENUM_EXT("TAS2560_ALGO_PROFILE", tas2560_algo_profile_enum[0], + tas2560_algo_get_profile, tas2560_algo_set_profile), + SOC_ENUM_EXT("TAS2560_ALGO_DISABLE", tas2560_algo_disable_enum[0], + tas2560_algo_get_disable, tas2560_algo_set_disable) +}; + +/* +* tas2560_algo_routing_init +* - register mixer controls +*/ + +int tas2560_algo_routing_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + + /* add the codec controls here */ + mutex_init(&routing_lock); + + snd_soc_add_codec_controls(codec, tas2560_algo_filter_mixer_controls, + ARRAY_SIZE(tas2560_algo_filter_mixer_controls)); + + return 0; +} +EXPORT_SYMBOL(tas2560_algo_routing_init); + +static int tas2560_algo_probe(struct platform_device *pdev) +{ + int ret = 0; + uint32_t num_entries = 0; + uint32_t *val_array = NULL; + + pr_debug("TAS2560_ALGO:%s Enter.\n", __func__); + + mutex_init(&routing_lock); + + ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); + + ret = of_property_read_u32(pdev->dev.of_node, "ti,spk_type_id", &spk_id); + if (ret < 0) { + pr_err("ti,spk_type_id read error,use default spk id.\n"); + } + pr_err("TAS2560_ALGO:%s spk_id = 0x%x",__func__, spk_id); + + num_entries = of_property_count_u32_elems(pdev->dev.of_node, + "ti,tas2560-port-id"); + + if (num_entries > 0) { + val_array = kcalloc(num_entries, sizeof(uint32_t), GFP_KERNEL); + if (!val_array) { + pr_err("TAS2560_ALGO:%s malloc fail\n", __func__); + ret = -ENOMEM; + goto err; + } + ret = of_property_read_u32_array(pdev->dev.of_node, + "ti,tas2560-port-id", val_array, num_entries); + rx_port_id = (uint32_t)val_array[0]; + tx_port_id = (uint32_t)val_array[1]; + pr_err("TAS2560_ALGO:%s rx_port_id = 0x%x tx_port_id = 0x%x\n", + __func__, rx_port_id, tx_port_id); + } + kfree(val_array); +err: + return ret; +} + +static int tas2560_algo_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id tas2560_algo_of_match[] = { + { .compatible = "ti,tas2560-algo" }, + { }, +}; +MODULE_DEVICE_TABLE(of, tas2560_algo_of_match); + +static struct platform_driver tas2560_algo_driver = { + .probe = tas2560_algo_probe, + .remove = tas2560_algo_remove, + .driver = { + .name = "tas2560-algo", + .of_match_table = tas2560_algo_of_match, + }, +}; + +static int __init tas2560_algo_init(void) +{ + return platform_driver_register(&tas2560_algo_driver); +} +module_init(tas2560_algo_init); + +static void __exit tas2560_algo_exit(void) +{ + platform_driver_unregister(&tas2560_algo_driver); +} +module_exit(tas2560_algo_exit); + + +MODULE_DESCRIPTION("TAS2560 ALSA SOC Smart Amplifier algo driver"); +MODULE_LICENSE("GPL v2"); +#endif /* CONFIG_SND_SOC_TAS2560 */ + diff --git a/sound/soc/codecs/tas2560-calib.c b/sound/soc/codecs/tas2560-calib.c new file mode 100644 index 000000000000..2fe71cfbad05 --- /dev/null +++ b/sound/soc/codecs/tas2560-calib.c @@ -0,0 +1,203 @@ +/* +** ============================================================================= +** Copyright (c) 2016 Texas Instruments 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; version 2. +** +** 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: +** tas2560-calib.c +** +** Description: +** Dsp control driver for Texas Instruments TAS2560 High Performance Smart +** Amplifier Algorithm running as CAPIv2 Module in AFE. +** +** ============================================================================= +*/ + +#ifdef CONFIG_SND_SOC_TAS2560 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Holds the Packet data required for processing */ +struct tas_dsp_pkt { + u8 slave_id; + u8 book; + u8 page; + u8 offset; + u8 data[TAS2560_ALGO_PAYLOAD_SIZE * sizeof(u32)]; +}; + +static int tas_params_ctrl(uint8_t *input, u8 dir, u8 count) +{ + u32 length = count / sizeof(u32); + u32 paramid = 0, moduleid = AFE_TAS2560_ALGO_MODULE_RX; + u32 index; + int ret = 0; + int special_index = 0; + struct tas_dsp_pkt *ppacket; + + ppacket = kmalloc(sizeof(struct tas_dsp_pkt), + GFP_KERNEL); + if (!ppacket) + return -ENOMEM; + + memset(ppacket, 0, sizeof(struct tas_dsp_pkt)); + ret = copy_from_user(ppacket, input, sizeof(struct tas_dsp_pkt)); + if (ret) { + pr_err("TAS2560_ALGO:%s Error copying from user\n", __func__); + kfree(ppacket); + return -EFAULT; + } + index = TAS2560_ALGO_GET_IDX(ppacket->page, ppacket->offset); + special_index = TAS2560_ALGO_IS_SPL_IDX(index); + if (special_index == 0) { + if ((index < 0 || index > MAX_TAS2560_ALGO_PARAM_INDEX)) { + pr_err("TAS2560_ALGO:%s invalid index = %d\n", + __func__, index); + kfree(ppacket); + return -EINVAL; + } + } + pr_info("TAS2560_ALGO:%s valid index = %d special = %s\n", + __func__, index, special_index ? "Yes" : "No"); + + /* speakers are differentiated by slave-ids and instance + * combination(channel) + */ + if ((ppacket->slave_id == SLAVE1) || (ppacket->slave_id == SLAVE2)) + paramid = TAS2560_ALGO_CALC_PIDX(paramid, + index, length, ppacket->slave_id); + else + pr_err("TAS2560_ALGO:%s Wrong slaveid = %x\n", + __func__, ppacket->slave_id); + + /* + * Note: In any case calculated paramid should not match with + * default parameters from ACDB + */ + if (paramid == AFE_PARAM_ID_TAS2560_ALGO_RX_ENABLE || + paramid == AFE_PARAM_ID_TAS2560_ALGO_TX_ENABLE || + paramid == AFE_PARAM_ID_TAS2560_ALGO_RX_CFG || + paramid == AFE_PARAM_ID_TAS2560_ALGO_TX_CFG) { + pr_err("TAS2560_ALGO:%s %s Slave 0x%x params failed\n", + __func__, + dir == TAS2560_ALGO_GET_PARAM ? "get" : "set", + ppacket->slave_id); + kfree(ppacket); + return -EINVAL; + } + ret = afe_tas2560_algo_ctrl(ppacket->data, paramid, moduleid, + dir, length * sizeof(u32)); + if (ret) + pr_err("TAS2560_ALGO:%s %s Slave 0x%x param control failed\n", + __func__, + dir == TAS2560_ALGO_GET_PARAM ? "get" : "set", + ppacket->slave_id); + else + pr_info("TAS2560_ALGO:%s Algo control returned %d\n", + __func__, ret); + + if (dir == TAS2560_ALGO_GET_PARAM) { + ret = copy_to_user(input, ppacket, sizeof(struct tas_dsp_pkt)); + if (ret) { + pr_err("TAS2560_ALGO:%s Error copying to user\n", + __func__); + ret = -EFAULT; + } + } + pr_info("TAS2560_ALGO:%s ppacket.data 0x%x\n", __func__, + ((u32 *)(ppacket->data))[0]); + kfree(ppacket); + return ret; +} + +static int tas_calib_open(struct inode *inode, struct file *fd) +{ + return 0; +} + +static ssize_t tas_calib_write(struct file *file, + const char __user *buffer, size_t count, loff_t *offp) +{ + int rc = 0; + + rc = tas_params_ctrl((uint8_t *)buffer, TAS2560_ALGO_SET_PARAM, count); + return rc; +} + +static ssize_t tas_calib_read(struct file *file, char __user *buffer, + size_t count, loff_t *ptr) +{ + int rc; + + rc = tas_params_ctrl((uint8_t *)buffer, TAS2560_ALGO_GET_PARAM, count); + /*No need of retry*/ + if (rc < 0) + count = rc; + return count; +} + +static long tas_calib_ioctl(struct file *filp, uint cmd, ulong arg) +{ + return 0; +} + +static int tas_calib_release(struct inode *inode, struct file *fd) +{ + return 0; +} + +const struct file_operations tas_calib_fops = { + .owner = THIS_MODULE, + .open = tas_calib_open, + .write = tas_calib_write, + .read = tas_calib_read, + .release = tas_calib_release, + .unlocked_ioctl = tas_calib_ioctl, +}; + +static struct miscdevice tas_calib_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "tas_calib", + .fops = &tas_calib_fops, +}; + +static int __init tas_calib_init(void) +{ + int rc; + + pr_err("TAS2560_ALGO:%s", __func__); + rc = misc_register(&tas_calib_misc); + if (rc) + pr_err("TAS2560_ALGO:%s register calib failed\n", __func__); + return rc; +} + +static void __exit tas_calib_exit(void) +{ + misc_deregister(&tas_calib_misc); +} + +module_init(tas_calib_init); +module_exit(tas_calib_exit); + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("DSP Driver To Control SmartAmp Algorithm"); +#endif /* CONFIG_SND_SOC_TAS2560 */ diff --git a/sound/soc/codecs/tas2560-codec.c b/sound/soc/codecs/tas2560-codec.c new file mode 100644 index 000000000000..e8c29c6118ac --- /dev/null +++ b/sound/soc/codecs/tas2560-codec.c @@ -0,0 +1,510 @@ +/* +** ============================================================================= +** Copyright (c) 2016 Texas Instruments 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; version 2. +** +** 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: +** tas2560-codec.c +** +** Description: +** ALSA SoC driver for Texas Instruments TAS2560 High Performance 4W Smart Amplifier +** +** ============================================================================= +*/ + +#ifdef CONFIG_TAS2560_CODEC + +/* #define DEBUG */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tas2560.h" +#include "tas2560-core.h" + +#define TAS2560_MDELAY 0xFFFFFFFE +#define KCONTROL_CODEC + +static unsigned int tas2560_codec_read(struct snd_soc_codec *codec, unsigned int reg) +{ + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(codec); + unsigned int value = 0; + int ret; + + ret = pTAS2560->read(pTAS2560, reg, &value); + if (ret >= 0) + return value; + else + return ret; +} + +static int tas2560_codec_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(codec); + + return pTAS2560->write(pTAS2560, reg, value); +} + +static int tas2560_codec_suspend(struct snd_soc_codec *codec) +{ + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + mutex_lock(&pTAS2560->codec_lock); + + dev_dbg(pTAS2560->dev, "%s\n", __func__); + pTAS2560->runtime_suspend(pTAS2560); + + mutex_unlock(&pTAS2560->codec_lock); + return ret; +} + +static int tas2560_codec_resume(struct snd_soc_codec *codec) +{ + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + mutex_lock(&pTAS2560->codec_lock); + + dev_dbg(pTAS2560->dev, "%s\n", __func__); + pTAS2560->runtime_resume(pTAS2560); + + mutex_unlock(&pTAS2560->codec_lock); + return ret; +} + +static int tas2560_AIF_post_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + /* struct snd_soc_codec *codec = w->codec; */ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dev_dbg(pTAS2560->dev, "SND_SOC_DAPM_POST_PMU"); + break; + case SND_SOC_DAPM_POST_PMD: + dev_dbg(pTAS2560->dev, "SND_SOC_DAPM_POST_PMD"); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget tas2560_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN_E("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0, + tas2560_AIF_post_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUT_DRV("ClassD", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUTPUT("OUT") +}; + +static const struct snd_soc_dapm_route tas2560_audio_map[] = { + {"DAC", NULL, "ASI1"}, + {"ClassD", NULL, "DAC"}, + {"OUT", NULL, "ClassD"}, + {"DAC", NULL, "PLL"}, +}; + +static int tas2560_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(pTAS2560->dev, "%s\n", __func__); + + return 0; +} + +static void tas2560_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(pTAS2560->dev, "%s\n", __func__); +} + +static int tas2560_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(codec); + + mutex_lock(&pTAS2560->codec_lock); + dev_info(pTAS2560->dev, "%s, %d\n", __func__, mute); + tas2560_enable(pTAS2560, !mute); + mutex_unlock(&pTAS2560->codec_lock); + return 0; +} + +static int tas2560_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(pTAS2560->dev, "%s\n", __func__); + + return ret; +} + +static int tas2560_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(pTAS2560->dev, "%s\n", __func__); + + return 0; +} + +static int tas2560_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(pTAS2560->dev, "%s\n", __func__); + + return 0; +} + +static int tas2560_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(pTAS2560->dev, "%s, format=0x%x\n", __func__, fmt); + + return ret; +} + +static struct snd_soc_dai_ops tas2560_dai_ops = { + .startup = tas2560_startup, + .shutdown = tas2560_shutdown, + .digital_mute = tas2560_mute, + .hw_params = tas2560_hw_params, + .prepare = tas2560_prepare, + .set_sysclk = tas2560_set_dai_sysclk, + .set_fmt = tas2560_set_dai_fmt, +}; + +#define TAS2560_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) +static struct snd_soc_dai_driver tas2560_dai_driver[] = { + { + .name = "tas2560 ASI1", + .id = 0, + .playback = { + .stream_name = "ASI1 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = TAS2560_FORMATS, + }, + .ops = &tas2560_dai_ops, + .symmetric_rates = 1, + }, +}; + +static int tas2560_codec_probe(struct snd_soc_codec *codec) +{ + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(pTAS2560->dev, "%s\n", __func__); + + return 0; +} + +static int tas2560_codec_remove(struct snd_soc_codec *codec) +{ + return 0; +} + +static int tas2560_get_load(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_codec *pCodec = snd_soc_kcontrol_codec(pKcontrol); +#else + struct snd_soc_codec *pCodec = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(pCodec); + + pUcontrol->value.integer.value[0] = pTAS2560->mnLoad; + + return 0; +} + +static int tas2560_set_load(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_codec *pCodec = snd_soc_kcontrol_codec(pKcontrol); +#else + struct snd_soc_codec *pCodec = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(pCodec); + + mutex_lock(&pTAS2560->codec_lock); + pTAS2560->mnLoad = pUcontrol->value.integer.value[0]; + dev_dbg(pCodec->dev, "%s:load = 0x%x\n", __func__, + pTAS2560->mnLoad); + tas2560_setLoad(pTAS2560, pTAS2560->mnLoad); + mutex_unlock(&pTAS2560->codec_lock); + return 0; +} + +static int tas2560_get_Sampling_Rate(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_codec *pCodec = snd_soc_kcontrol_codec(pKcontrol); +#else + struct snd_soc_codec *pCodec = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(pCodec); + + pUcontrol->value.integer.value[0] = pTAS2560->mnSamplingRate; + dev_dbg(pCodec->dev, "%s: %d\n", __func__, + pTAS2560->mnSamplingRate); + return 0; +} + +static int tas2560_set_Sampling_Rate(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_codec *pCodec = snd_soc_kcontrol_codec(pKcontrol); +#else + struct snd_soc_codec *pCodec = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(pCodec); + int sampleRate = pUcontrol->value.integer.value[0]; + + mutex_lock(&pTAS2560->codec_lock); + dev_dbg(pCodec->dev, "%s: %d\n", __func__, sampleRate); + tas2560_set_SampleRate(pTAS2560, sampleRate); + mutex_unlock(&pTAS2560->codec_lock); + + return 0; +} + +static int tas2560_power_ctrl_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); +#else + struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(codec); + + pValue->value.integer.value[0] = pTAS2560->mbPowerUp; + dev_dbg(codec->dev, "tas2560_power_ctrl_get = 0x%x\n", + pTAS2560->mbPowerUp); + + return 0; +} + +static int tas2560_power_ctrl_put(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); +#else + struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(codec); + int bPowerUp = pValue->value.integer.value[0]; + + mutex_lock(&pTAS2560->codec_lock); + tas2560_enable(pTAS2560, bPowerUp); + mutex_unlock(&pTAS2560->codec_lock); + + return 0; +} + +static int tas2560_ear_switch_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *ucontrol) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_codec *pCodec = snd_soc_kcontrol_codec(pKcontrol); +#else + struct snd_soc_codec *pCodec = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(pCodec); + + ucontrol->value.integer.value[0] = pTAS2560->enablePmicEarPath; + dev_dbg(pCodec->dev, "%s: ear_switch_get = %d\n", __func__, (int)(pTAS2560->enablePmicEarPath)); + + return 0; +} + +static int tas2560_ear_switch_set(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *ucontrol) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); +#else + struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n", __func__, ucontrol->value.integer.value[0]); + + pTAS2560->enablePmicEarPath = (ucontrol->value.integer.value[0] ? true : false); + + if (!gpio_is_valid(pTAS2560->mnSwitchGPIO)) { + dev_dbg(codec->dev, "%s: Invalid gpio: %d\n", __func__, pTAS2560->mnSwitchGPIO); + return false; + } + + gpio_set_value_cansleep(pTAS2560->mnSwitchGPIO, pTAS2560->enablePmicEarPath); + + return 0; +} + + +static int tas2560_ppg_ctrl_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); +#else + struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(codec); + + pValue->value.integer.value[0] = tas2560_get_PPG(pTAS2560); + dev_dbg(codec->dev, "tas2560_ppg_ctrl_get = 0x%x\n", + pTAS2560->mnPPG); + + return 0; +} + +static int tas2560_ppg_ctrl_put(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ +#ifdef KCONTROL_CODEC + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); +#else + struct snd_soc_codec *codec = snd_kcontrol_chip(pKcontrol); +#endif + struct tas2560_priv *pTAS2560 = snd_soc_codec_get_drvdata(codec); + int mnPPG = pValue->value.integer.value[0]; + + mutex_lock(&pTAS2560->codec_lock); + tas2560_set_PPG(pTAS2560, mnPPG); + mutex_unlock(&pTAS2560->codec_lock); + + return 0; +} + +static const char *load_text[] = {"8_Ohm", "6_Ohm", "4_Ohm"}; + +static const struct soc_enum load_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(load_text), load_text), +}; + +static const char *Sampling_Rate_text[] = {"48_khz", "44.1_khz", "16_khz", "8_khz"}; + +static const struct soc_enum Sampling_Rate_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(Sampling_Rate_text), Sampling_Rate_text), +}; + +static const char * const tas2560_ear_switch_ctrl_text[] = {"DISABLE", "ENABLE"}; +static const struct soc_enum tas2560_ear_switch_ctl_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas2560_ear_switch_ctrl_text), tas2560_ear_switch_ctrl_text), +}; + +/* + * DAC digital volumes. From 0 to 15 dB in 1 dB steps + */ +static DECLARE_TLV_DB_SCALE(dac_tlv, 0, 100, 0); + +static const struct snd_kcontrol_new tas2560_snd_controls[] = { + SOC_SINGLE_TLV("DAC Playback Volume", TAS2560_SPK_CTRL_REG, 0, 0x0f, 0, + dac_tlv), + SOC_ENUM_EXT("TAS2560 Boost load", load_enum[0], + tas2560_get_load, tas2560_set_load), + SOC_ENUM_EXT("TAS2560 Sampling Rate", Sampling_Rate_enum[0], + tas2560_get_Sampling_Rate, tas2560_set_Sampling_Rate), + SOC_SINGLE_EXT("TAS2560 PowerCtrl", SND_SOC_NOPM, 0, 0x0001, 0, + tas2560_power_ctrl_get, tas2560_power_ctrl_put), + SOC_ENUM_EXT("TAS2560 EAR Switch", tas2560_ear_switch_ctl_enum[0], tas2560_ear_switch_get, tas2560_ear_switch_set), + SOC_SINGLE_EXT("TAS2560 PPG", SND_SOC_NOPM, 0, 0x006E, 0, + tas2560_ppg_ctrl_get, tas2560_ppg_ctrl_put), +}; + +static struct snd_soc_codec_driver soc_codec_driver_tas2560 = { + .probe = tas2560_codec_probe, + .remove = tas2560_codec_remove, + .read = tas2560_codec_read, + .write = tas2560_codec_write, + .suspend = tas2560_codec_suspend, + .resume = tas2560_codec_resume, + .component_driver = { + .controls = tas2560_snd_controls, + .num_controls = ARRAY_SIZE(tas2560_snd_controls), + .dapm_widgets = tas2560_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas2560_dapm_widgets), + .dapm_routes = tas2560_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas2560_audio_map), + }, +}; + +int tas2560_register_codec(struct tas2560_priv *pTAS2560) +{ + int nResult = 0; + + dev_info(pTAS2560->dev, "%s, enter\n", __func__); + nResult = snd_soc_register_codec(pTAS2560->dev, + &soc_codec_driver_tas2560, + tas2560_dai_driver, ARRAY_SIZE(tas2560_dai_driver)); + + return nResult; +} + +int tas2560_deregister_codec(struct tas2560_priv *pTAS2560) +{ + snd_soc_unregister_codec(pTAS2560->dev); + + return 0; +} + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("TAS2560 ALSA SOC Smart Amplifier driver"); +MODULE_LICENSE("GPL v2"); +#endif /* CONFIG_TAS2560_CODEC */ diff --git a/sound/soc/codecs/tas2560-codec.h b/sound/soc/codecs/tas2560-codec.h new file mode 100644 index 000000000000..069a7927bd90 --- /dev/null +++ b/sound/soc/codecs/tas2560-codec.h @@ -0,0 +1,30 @@ +/* +** ============================================================================= +** Copyright (c) 2016 Texas Instruments 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; version 2. +** +** 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: +** tas2560-codec.h +** +** Description: +** header file for tas2560-codec.c +** +** ============================================================================= +*/ + +#ifndef _TAS2560_CODEC_H +#define _TAS2560_CODEC_H + +#include "tas2560.h" + +int tas2560_register_codec(struct tas2560_priv *pTAS2560); +int tas2560_deregister_codec(struct tas2560_priv *pTAS2560); + +#endif /* _TAS2560_CODEC_H */ diff --git a/sound/soc/codecs/tas2560-core.c b/sound/soc/codecs/tas2560-core.c new file mode 100644 index 000000000000..f60c3a2d6cf2 --- /dev/null +++ b/sound/soc/codecs/tas2560-core.c @@ -0,0 +1,1053 @@ +/* +** ============================================================================= +** Copyright (c) 2016 Texas Instruments 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; version 2. +** +** 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: +** tas2560-core.c +** +** Description: +** TAS2560 common functions for Android Linux +** +** ============================================================================= +*/ +/* #define DEBUG */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tas2560.h" + +#define TAS2560_MDELAY 0xFFFFFFFE +#define TAS2560_MSLEEP 0xFFFFFFFD + +#define MAX_CLIENTS 8 + +static unsigned int p_tas2560_default_data[] = { + /* reg address size values */ + TAS2560_DR_BOOST_REG_1, 0x01, 0x0c, + TAS2560_DR_BOOST_REG_2, 0x01, 0x33, + /*rampDown 15dB/us, clock1 hysteresis, 0.34ms; clock2 hysteresis, 10.6us */ + TAS2560_CLK_ERR_CTRL2, 0x01, 0x21, + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2560_irq_config[] = { + TAS2560_IRQ_PIN_REG, 0x01, 0x41, + TAS2560_INT_MODE_REG, 0x01, 0x80,/* active high until INT_STICKY_1 and INT_STICKY_2 are read to be cleared. */ + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2560_48kSR_BCLK_1P536_data[] = { + TAS2560_CLK_SEL, 0x01, 0x01, /* BCLK in, P = 1 */ + TAS2560_SET_FREQ, 0x01, 0x20, /* J = 32 */ + TAS2560_PLL_D_MSB, 0x01, 0x00, /* D = 0 */ + TAS2560_PLL_D_LSB, 0x01, 0x00, + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2560_48kSR_BCLK_3P072_data[] = { + TAS2560_CLK_SEL, 0x01, 0x01, /* BCLK in, P = 1 */ + TAS2560_SET_FREQ, 0x01, 0x10, /* J = 16 */ + TAS2560_PLL_D_MSB, 0x01, 0x00, /* D = 0 */ + TAS2560_PLL_D_LSB, 0x01, 0x00, + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2560_startup_data[] = { + /* reg address size values */ + TAS2560_DEV_MODE_REG, 0x01, 0x02, + TAS2560_MUTE_REG, 0x01, 0x41, + TAS2560_MDELAY, 0x01, 0x02, + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2560_unmute_data[] = { + /* reg address size values */ + TAS2560_CLK_ERR_CTRL, 0x01, 0x0b, + TAS2560_INT_GEN_REG, 0x01, 0xff, + TAS2560_MUTE_REG, 0x01, 0x40, + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2560_shutdown_data[] = { + /* reg address size values */ + TAS2560_INT_GEN_REG, 0x01, 0x00, + TAS2560_CLK_ERR_CTRL, 0x01, 0x00, + TAS2560_MUTE_REG, 0x01, 0x41, + TAS2560_MSLEEP, 0x01, 10, /* delay 10ms */ + TAS2560_MUTE_REG, 0x01, 0x01, + TAS2560_MSLEEP, 0x01, 20, /* delay 20ms */ + TAS2560_DEV_MODE_REG, 0x01, 0x01, + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2560_boost_headroom_data[] = { + TAS2560_BOOST_HEAD, 0x04, 0x06, 0x66, 0x66, 0x00, + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2560_thermal_foldback[] = { + TAS2560_THERMAL_FOLDBACK, 0x04, 0x39, 0x80, 0x00, 0x00, + 0xFFFFFFFF, 0xFFFFFFFF +}; + +/* +* PPG = dec2hex(10^(/20)*2^31)) +*/ + +/*static unsigned int p_tas2560_PPG_data[] = { + TAS2560_PPG, 0x04, 0x65, 0xac, 0x8c, 0x2e, + 0xFFFFFFFF, 0xFFFFFFFF +};*/ + +static unsigned int p_tas2560_idle_chnl_detect[] = { + TAS2560_IDLE_CHNL_DETECT, 0x04, 0, 0, 0, 0, + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2560_HPF_data[] = { + /* reg address size values */ + /*Isense path HPF cut off -> 2Hz*/ + TAS2560_ISENSE_PATH_CTL1, 0x04, 0x7F, 0xFB, 0xB5, 0x00, + TAS2560_ISENSE_PATH_CTL2, 0x04, 0x80, 0x04, 0x4C, 0x00, + TAS2560_ISENSE_PATH_CTL3, 0x04, 0x7F, 0xF7, 0x6A, 0x00, + /*all pass*/ + TAS2560_HPF_CUTOFF_CTL1, 0x04, 0x7F, 0xFF, 0xFF, 0xFF, + TAS2560_HPF_CUTOFF_CTL2, 0x04, 0x00, 0x00, 0x00, 0x00, + TAS2560_HPF_CUTOFF_CTL3, 0x04, 0x00, 0x00, 0x00, 0x00, + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2560_Vsense_biquad_data[] = { + /* vsense delay in biquad = 3/8 sample @48KHz */ + TAS2560_VSENSE_DEL_CTL1, 0x04, 0x3a, 0x46, 0x74, 0x00, + TAS2560_VSENSE_DEL_CTL2, 0x04, 0x22, 0xf3, 0x07, 0x00, + TAS2560_VSENSE_DEL_CTL3, 0x04, 0x80, 0x77, 0x61, 0x00, + TAS2560_VSENSE_DEL_CTL4, 0x04, 0x22, 0xa7, 0xcc, 0x00, + TAS2560_VSENSE_DEL_CTL5, 0x04, 0x3a, 0x0c, 0x93, 0x00, + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2560_48khz_data[] = { + /* reg address size values */ + TAS2560_SR_CTRL1, 0x01, 0x01, + TAS2560_SR_CTRL2, 0x01, 0x08, + TAS2560_SR_CTRL3, 0x01, 0x10, + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2560_16khz_data[] = { + /* reg address size values */ + TAS2560_SR_CTRL1, 0x01, 0x01, + TAS2560_SR_CTRL2, 0x01, 0x18, + TAS2560_SR_CTRL3, 0x01, 0x20, + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2560_8khz_data[] = { + /* reg address size values */ + TAS2560_SR_CTRL1, 0x01, 0x01, + TAS2560_SR_CTRL2, 0x01, 0x30, + TAS2560_SR_CTRL3, 0x01, 0x20, + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2560_4Ohm_data[] = { + /* reg address size values */ + TAS2560_BOOST_ON, 0x04, 0x6f, 0x5c, 0x28, 0xf5, + TAS2560_BOOST_OFF, 0x04, 0x67, 0xae, 0x14, 0x7a, + TAS2560_BOOST_TABLE_CTRL1, 0x04, 0x1c, 0x00, 0x00, 0x00, + TAS2560_BOOST_TABLE_CTRL2, 0x04, 0x1f, 0x0a, 0x3d, 0x70, + TAS2560_BOOST_TABLE_CTRL3, 0x04, 0x22, 0x14, 0x7a, 0xe1, + TAS2560_BOOST_TABLE_CTRL4, 0x04, 0x25, 0x1e, 0xb8, 0x51, + TAS2560_BOOST_TABLE_CTRL5, 0x04, 0x28, 0x28, 0xf5, 0xc2, + TAS2560_BOOST_TABLE_CTRL6, 0x04, 0x2b, 0x33, 0x33, 0x33, + TAS2560_BOOST_TABLE_CTRL7, 0x04, 0x2e, 0x3d, 0x70, 0xa3, + TAS2560_BOOST_TABLE_CTRL8, 0x04, 0x31, 0x47, 0xae, 0x14, + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2560_6Ohm_data[] = { + /* reg address size values */ + TAS2560_BOOST_ON, 0x04, 0x73, 0x33, 0x33, 0x33, + TAS2560_BOOST_OFF, 0x04, 0x6b, 0x85, 0x1e, 0xb8, + TAS2560_BOOST_TABLE_CTRL1, 0x04, 0x1d, 0x99, 0x99, 0x99, + TAS2560_BOOST_TABLE_CTRL2, 0x04, 0x20, 0xcc, 0xcc, 0xcc, + TAS2560_BOOST_TABLE_CTRL3, 0x04, 0x24, 0x00, 0x00, 0x00, + TAS2560_BOOST_TABLE_CTRL4, 0x04, 0x27, 0x33, 0x33, 0x33, + TAS2560_BOOST_TABLE_CTRL5, 0x04, 0x2a, 0x66, 0x66, 0x66, + TAS2560_BOOST_TABLE_CTRL6, 0x04, 0x2d, 0x99, 0x99, 0x99, + TAS2560_BOOST_TABLE_CTRL7, 0x04, 0x30, 0xcc, 0xcc, 0xcc, + TAS2560_BOOST_TABLE_CTRL8, 0x04, 0x34, 0x00, 0x00, 0x00, + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned int p_tas2560_8Ohm_data[] = { + /* reg address size values */ + TAS2560_BOOST_ON, 0x04, 0x75, 0xc2, 0x8e, 0x00, + TAS2560_BOOST_OFF, 0x04, 0x6e, 0x14, 0x79, 0x00, + TAS2560_BOOST_TABLE_CTRL1, 0x04, 0x1e, 0x00, 0x00, 0x00, + TAS2560_BOOST_TABLE_CTRL2, 0x04, 0x21, 0x3d, 0x71, 0x00, + TAS2560_BOOST_TABLE_CTRL3, 0x04, 0x24, 0x7a, 0xe1, 0x00, + TAS2560_BOOST_TABLE_CTRL4, 0x04, 0x27, 0xb8, 0x52, 0x00, + TAS2560_BOOST_TABLE_CTRL5, 0x04, 0x2a, 0xf5, 0xc3, 0x00, + TAS2560_BOOST_TABLE_CTRL6, 0x04, 0x2e, 0x33, 0x33, 0x00, + TAS2560_BOOST_TABLE_CTRL7, 0x04, 0x31, 0x70, 0xa4, 0x00, + TAS2560_BOOST_TABLE_CTRL8, 0x04, 0x34, 0xae, 0x14, 0x00, + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned char ppg_gain[] = { + 0x00, 0x00, 0x1A, 0x87, + 0x00, 0x00, 0x1D, 0xC4, + 0x00, 0x00, 0x21, 0x65, + 0x00, 0x00, 0x25, 0x78, + 0x00, 0x00, 0x2A, 0x0B, + 0x00, 0x00, 0x2F, 0x2C, + 0x00, 0x00, 0x34, 0xEE, + 0x00, 0x00, 0x3B, 0x63, + 0x00, 0x00, 0x42, 0xA2, + 0x00, 0x00, 0x4A, 0xC3, + 0x00, 0x00, 0x53, 0xE3, + 0x00, 0x00, 0x5E, 0x1F, + 0x00, 0x00, 0x69, 0x9B, + 0x00, 0x00, 0x76, 0x7E, + 0x00, 0x00, 0x84, 0xF3, + 0x00, 0x00, 0x95, 0x2C, + 0x00, 0x00, 0xA7, 0x60, + 0x00, 0x00, 0xBB, 0xCC, + 0x00, 0x00, 0xD2, 0xB6, + 0x00, 0x00, 0xEC, 0x6C, + 0x00, 0x01, 0x09, 0x45, + 0x00, 0x01, 0x29, 0xA4, + 0x00, 0x01, 0x4D, 0xF5, + 0x00, 0x01, 0x76, 0xB5, + 0x00, 0x01, 0xA4, 0x6D, + 0x00, 0x01, 0xD7, 0xBA, + 0x00, 0x02, 0x11, 0x49, + 0x00, 0x02, 0x51, 0xDE, + 0x00, 0x02, 0x9A, 0x55, + 0x00, 0x02, 0xEB, 0xA3, + 0x00, 0x03, 0x46, 0xDC, + 0x00, 0x03, 0xAD, 0x38, + 0x00, 0x04, 0x20, 0x10, + 0x00, 0x04, 0xA0, 0xEC, + 0x00, 0x05, 0x31, 0x81, + 0x00, 0x05, 0xD3, 0xBB, + 0x00, 0x06, 0x89, 0xBF, + 0x00, 0x07, 0x55, 0xFA, + 0x00, 0x08, 0x3B, 0x20, + 0x00, 0x09, 0x3C, 0x3B, + 0x00, 0x0A, 0x5C, 0xB6, + 0x00, 0x0B, 0xA0, 0x64, + 0x00, 0x0D, 0x0B, 0x91, + 0x00, 0x0E, 0xA3, 0x0E, + 0x00, 0x10, 0x6C, 0x43, + 0x00, 0x12, 0x6D, 0x43, + 0x00, 0x14, 0xAC, 0xDB, + 0x00, 0x17, 0x32, 0xAE, + 0x00, 0x1A, 0x07, 0x4F, + 0x00, 0x1D, 0x34, 0x5B, + 0x00, 0x20, 0xC4, 0x9C, + 0x00, 0x24, 0xC4, 0x2C, + 0x00, 0x29, 0x40, 0xA2, + 0x00, 0x2E, 0x49, 0x39, + 0x00, 0x33, 0xEF, 0x0C, + 0x00, 0x3A, 0x45, 0x4A, + 0x00, 0x41, 0x61, 0x79, + 0x00, 0x49, 0x5B, 0xC1, + 0x00, 0x52, 0x4F, 0x3B, + 0x00, 0x5C, 0x5A, 0x4F, + 0x00, 0x67, 0x9F, 0x1C, + 0x00, 0x74, 0x43, 0xE8, + 0x00, 0x82, 0x73, 0xA6, + 0x00, 0x92, 0x5E, 0x89, + 0x00, 0xA4, 0x3A, 0xA2, + 0x00, 0xB8, 0x44, 0x9C, + 0x00, 0xCE, 0xC0, 0x8A, + 0x00, 0xE7, 0xFA, 0xCC, + 0x01, 0x04, 0x49, 0x15, + 0x01, 0x24, 0x0B, 0x8C, + 0x01, 0x47, 0xAE, 0x14, + 0x01, 0x6F, 0xA9, 0xBB, + 0x01, 0x9C, 0x86, 0x51, + 0x01, 0xCE, 0xDC, 0x3D, + 0x02, 0x07, 0x56, 0x7A, + 0x02, 0x46, 0xB4, 0xE4, + 0x02, 0x8D, 0xCE, 0xBC, + 0x02, 0xDD, 0x95, 0x8A, + 0x03, 0x37, 0x18, 0x4E, + 0x03, 0x9B, 0x87, 0x19, + 0x04, 0x0C, 0x37, 0x14, + 0x04, 0x8A, 0xA7, 0x0B, + 0x05, 0x18, 0x84, 0x80, + 0x05, 0xB7, 0xB1, 0x5B, + 0x06, 0x6A, 0x4A, 0x53, + 0x07, 0x32, 0xAE, 0x18, + 0x08, 0x13, 0x85, 0x62, + 0x09, 0x0F, 0xCB, 0xF8, + 0x0A, 0x2A, 0xDA, 0xD2, + 0x0B, 0x68, 0x73, 0x7A, + 0x0C, 0xCC, 0xCC, 0xCD, + 0x0E, 0x5C, 0xA1, 0x4C, + 0x10, 0x1D, 0x3F, 0x2E, + 0x12, 0x14, 0x9A, 0x60, + 0x14, 0x49, 0x60, 0xC5, + 0x16, 0xC3, 0x10, 0xE3, + 0x19, 0x8A, 0x13, 0x57, + 0x1C, 0xA7, 0xD7, 0x68, + 0x20, 0x26, 0xF3, 0x10, + 0x24, 0x13, 0x46, 0xF6, + 0x28, 0x7A, 0x26, 0xC5, + 0x2D, 0x6A, 0x86, 0x6F, + 0x32, 0xF5, 0x2C, 0xFF, + 0x39, 0x2C, 0xED, 0x8E, + 0x40, 0x26, 0xE7, 0x3D, + 0x47, 0xFA, 0xCC, 0xF0, + 0x50, 0xC3, 0x35, 0xD4, + 0x5A, 0x9D, 0xF7, 0xAC, + 0x65, 0xAC, 0x8C, 0x2F, + 0x72, 0x14, 0x82, 0xC0, + 0x7F, 0xFF, 0xFF, 0xFF +}; +static int tas2560_i2c_load_data(struct tas2560_priv *pTAS2560, unsigned int *pData) +{ + unsigned int nRegister; + unsigned int *nData; + unsigned char Buf[128]; + unsigned int nLength = 0; + unsigned int i = 0; + unsigned int nSize = 0; + int nResult = 0; + + do { + nRegister = pData[nLength]; + nSize = pData[nLength + 1]; + nData = &pData[nLength + 2]; + if (nRegister == TAS2560_MSLEEP) { + msleep(nData[0]); + dev_dbg(pTAS2560->dev, "%s, msleep = %d\n", + __func__, nData[0]); + } else if (nRegister == TAS2560_MDELAY) { + mdelay(nData[0]); + dev_dbg(pTAS2560->dev, "%s, mdelay = %d\n", + __func__, nData[0]); + } else { + if (nRegister != 0xFFFFFFFF) { + if (nSize > 128) { + dev_err(pTAS2560->dev, + "%s, Line=%d, invalid size, maximum is 128 bytes!\n", + __func__, __LINE__); + break; + } + + if (nSize > 1) { + for (i = 0; i < nSize; i++) + Buf[i] = (unsigned char)nData[i]; + nResult = pTAS2560->bulk_write(pTAS2560, nRegister, Buf, nSize); + if (nResult < 0) + break; + } else if (nSize == 1) { + nResult = pTAS2560->write(pTAS2560, nRegister, nData[0]); + if (nResult < 0) + break; + } else { + dev_err(pTAS2560->dev, + "%s, Line=%d,invalid size, minimum is 1 bytes!\n", + __func__, __LINE__); + } + } + } + nLength = nLength + 2 + pData[nLength + 1]; + } while (nRegister != 0xFFFFFFFF); + + return nResult; +} + +void tas2560_sw_shutdown(struct tas2560_priv *pTAS2560, int sw_shutdown) +{ + dev_dbg(pTAS2560->dev, "%s, %d\n", __func__, sw_shutdown); + + if (sw_shutdown) + pTAS2560->update_bits(pTAS2560, TAS2560_PWR_REG, + TAS2560_PWR_BIT_MASK, 0); + else + pTAS2560->update_bits(pTAS2560, TAS2560_PWR_REG, + TAS2560_PWR_BIT_MASK, TAS2560_PWR_BIT_MASK); +} + +int tas2560_set_SampleRate(struct tas2560_priv *pTAS2560, unsigned int nSamplingRate) +{ + int ret = 0; + + dev_dbg(pTAS2560->dev, "%s, %d\n", __func__, nSamplingRate); + + switch (nSamplingRate) { + case 48000: + tas2560_i2c_load_data(pTAS2560, p_tas2560_48khz_data); + break; + case 44100: + pTAS2560->write(pTAS2560, TAS2560_SR_CTRL1, 0x11); + break; + case 16000: + tas2560_i2c_load_data(pTAS2560, p_tas2560_16khz_data); + break; + case 8000: + tas2560_i2c_load_data(pTAS2560, p_tas2560_8khz_data); + break; + default: + dev_err(pTAS2560->dev, "Invalid Sampling rate, %d\n", nSamplingRate); + ret = -1; + break; + } + + if (ret >= 0) + pTAS2560->mnSamplingRate = nSamplingRate; + + return ret; +} + +int tas2560_set_bit_rate(struct tas2560_priv *pTAS2560, unsigned int nBitRate) +{ + int ret = 0, n = -1; + + dev_dbg(pTAS2560->dev, "%s, %d\n", __func__, nBitRate); + switch (nBitRate) { + case 16: + n = 0; + break; + case 20: + n = 1; + break; + case 24: + n = 2; + break; + case 32: + n = 3; + break; + } + + if (n >= 0) + ret = pTAS2560->update_bits(pTAS2560, + TAS2560_DAI_FMT, 0x03, n); + + return ret; +} + +int tas2560_get_bit_rate(struct tas2560_priv *pTAS2560) +{ + int nBitRate = -1, value = -1, ret = 0; + + ret = pTAS2560->read(pTAS2560, TAS2560_DAI_FMT, &value); + value &= 0x03; + + switch (value) { + case 0: + nBitRate = 16; + break; + case 1: + nBitRate = 20; + break; + case 2: + nBitRate = 24; + break; + case 3: + nBitRate = 32; + break; + default: + break; + } + + return nBitRate; +} + +int tas2560_set_ASI_fmt(struct tas2560_priv *pTAS2560, unsigned int fmt) +{ + u8 serial_format = 0, asi_cfg_1 = 0; + int ret = 0; + + dev_dbg(pTAS2560->dev, "%s, %d\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + asi_cfg_1 = 0x00; + break; + case SND_SOC_DAIFMT_CBS_CFM: + asi_cfg_1 = TAS2560_WCLKDIR; + break; + case SND_SOC_DAIFMT_CBM_CFS: + asi_cfg_1 = TAS2560_BCLKDIR; + break; + case SND_SOC_DAIFMT_CBM_CFM: + asi_cfg_1 = (TAS2560_BCLKDIR | TAS2560_WCLKDIR); + break; + default: + dev_err(pTAS2560->dev, "ASI format master is not found\n"); + ret = -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + asi_cfg_1 |= 0x00; + break; + case SND_SOC_DAIFMT_NB_IF: + asi_cfg_1 |= TAS2560_WCLKINV; + break; + case SND_SOC_DAIFMT_IB_NF: + asi_cfg_1 |= TAS2560_BCLKINV; + break; + case SND_SOC_DAIFMT_IB_IF: + asi_cfg_1 = (TAS2560_WCLKINV | TAS2560_BCLKINV); + break; + default: + dev_err(pTAS2560->dev, "ASI format Inverse is not found\n"); + ret = -EINVAL; + } + + pTAS2560->update_bits(pTAS2560, TAS2560_ASI_CFG_1, TAS2560_DIRINV_MASK | 0x02, + asi_cfg_1 | 0x02); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case (SND_SOC_DAIFMT_I2S): + serial_format |= (TAS2560_DATAFORMAT_I2S << TAS2560_DATAFORMAT_SHIFT); + break; + case (SND_SOC_DAIFMT_DSP_A): + case (SND_SOC_DAIFMT_DSP_B): + serial_format |= (TAS2560_DATAFORMAT_DSP << TAS2560_DATAFORMAT_SHIFT); + break; + case (SND_SOC_DAIFMT_RIGHT_J): + serial_format |= (TAS2560_DATAFORMAT_RIGHT_J << TAS2560_DATAFORMAT_SHIFT); + break; + case (SND_SOC_DAIFMT_LEFT_J): + serial_format |= (TAS2560_DATAFORMAT_LEFT_J << TAS2560_DATAFORMAT_SHIFT); + break; + default: + dev_err(pTAS2560->dev, "DAI Format is not found, fmt=0x%x\n", fmt); + ret = -EINVAL; + break; + } + + pTAS2560->update_bits(pTAS2560, TAS2560_DAI_FMT, TAS2560_DAI_FMT_MASK, + serial_format); + + return ret; +} + +int tas2560_set_pll_clkin(struct tas2560_priv *pTAS2560, int clk_id, + unsigned int freq) +{ + int ret = 0; + unsigned char pll_in = 0; + + dev_dbg(pTAS2560->dev, "%s, clkid=%d, freq=%d\n", __func__, clk_id, freq); + + switch (clk_id) { + case TAS2560_PLL_CLKIN_BCLK: + pll_in = 0; + break; + case TAS2560_PLL_CLKIN_MCLK: + pll_in = 1; + break; + case TAS2560_PLL_CLKIN_PDMCLK: + pll_in = 2; + break; + default: + dev_err(pTAS2560->dev, "Invalid clk id: %d\n", clk_id); + ret = -EINVAL; + break; + } + + if (ret >= 0) { + pTAS2560->update_bits(pTAS2560, TAS2560_CLK_SEL, + TAS2560_PLL_SRC_MASK, pll_in << 6); + pTAS2560->mnClkid = clk_id; + pTAS2560->mnClkin = freq; + } + + return ret; +} + +int tas2560_setupPLL(struct tas2560_priv *pTAS2560, int pll_clkin) +{ + unsigned int pll_clk = pTAS2560->mnSamplingRate * 1024; + unsigned int power = 0, temp; + unsigned int d, pll_clkin_divide; + u8 j, p; + int ret = 0; + + dev_dbg(pTAS2560->dev, "%s, %d\n", __func__, pll_clkin); + + if (!pll_clkin || (pll_clkin < 0)) { + if (pTAS2560->mnClkid != TAS2560_PLL_CLKIN_BCLK) { + dev_err(pTAS2560->dev, + "pll_in %d, pll_clkin frequency err:%d\n", + pTAS2560->mnClkid, pll_clkin); + return -EINVAL; + } + + pll_clkin = pTAS2560->mnSamplingRate * pTAS2560->mnFrameSize; + } + + pTAS2560->read(pTAS2560, TAS2560_PWR_REG, &power); + if (power & TAS2560_PWR_BIT_MASK) { + dev_dbg(pTAS2560->dev, "power down to update PLL\n"); + pTAS2560->write(pTAS2560, TAS2560_PWR_REG, TAS2560_PWR_BIT_MASK|TAS2560_MUTE_MASK); + msleep(1); + } + + /* Fill in the PLL control registers for J & D + * pll_clk = (pll_clkin * J.D) / P + * Need to fill in J and D here based on incoming freq + */ + if (pll_clkin <= 40000000) + p = 1; + else if (pll_clkin <= 80000000) + p = 2; + else if (pll_clkin <= 160000000) + p = 3; + else { + dev_err(pTAS2560->dev, "PLL Clk In %d not covered here\n", pll_clkin); + ret = -EINVAL; + } + + if (ret >= 0) { + j = (pll_clk * p) / pll_clkin; + d = (pll_clk * p) % pll_clkin; + d /= (pll_clkin / 10000); + + pll_clkin_divide = pll_clkin / (1 << p); + + if ((d == 0) + && ((pll_clkin_divide < 512000) || (pll_clkin_divide > 20000000))) { + dev_err(pTAS2560->dev, "PLL cal ERROR!!!, pll_in=%d\n", pll_clkin); + ret = -EINVAL; + } + + if ((d != 0) + && ((pll_clkin_divide < 10000000) || (pll_clkin_divide > 20000000))) { + dev_err(pTAS2560->dev, "PLL cal ERROR!!!, pll_in=%d\n", pll_clkin); + ret = -EINVAL; + } + + if (j == 0) { + dev_err(pTAS2560->dev, "PLL cal ERROR!!!, j ZERO\n"); + ret = -EINVAL; + } + } + + if (ret >= 0) { + dev_info(pTAS2560->dev, + "PLL clk_in = %d, P=%d, J.D=%d.%d\n", pll_clkin, p, j, d); + /* update P */ + if (p == 64) + temp = 0; + else + temp = p; + pTAS2560->update_bits(pTAS2560, TAS2560_CLK_SEL, TAS2560_PLL_P_MASK, temp); + + /* Update J */ + temp = j; + if (pll_clkin < 1000000) + temp |= 0x80; + pTAS2560->write(pTAS2560, TAS2560_SET_FREQ, temp); + + /* Update D */ + temp = (d & 0x00ff); + pTAS2560->write(pTAS2560, TAS2560_PLL_D_LSB, temp); + temp = ((d & 0x3f00) >> 8); + pTAS2560->write(pTAS2560, TAS2560_PLL_D_MSB, temp); + } + + /* Restore PLL status */ + if (power & TAS2560_PWR_BIT_MASK) { + pTAS2560->write(pTAS2560, TAS2560_PWR_REG, power); + msleep(1); + } + + return ret; +} + +/* 0, 8Ohm; 1, 6Ohm; 2, 4Ohm */ +int tas2560_setLoad(struct tas2560_priv *pTAS2560, int load) +{ + int ret = 0; + int value = -1; + + dev_dbg(pTAS2560->dev, "%s: %d\n", __func__, load); + + switch (load) { + case LOAD_8OHM: + value = 0; + tas2560_i2c_load_data(pTAS2560, p_tas2560_8Ohm_data); + break; + case LOAD_6OHM: + value = 1; + tas2560_i2c_load_data(pTAS2560, p_tas2560_6Ohm_data); + break; + case LOAD_4OHM: + value = 2; + tas2560_i2c_load_data(pTAS2560, p_tas2560_4Ohm_data); + break; + default: + break; + } + + if (value >= 0) { + pTAS2560->update_bits(pTAS2560, TAS2560_LOAD, LOAD_MASK, value << 3); + } + return ret; +} + +int tas2560_getLoad(struct tas2560_priv *pTAS2560) +{ + int ret = -1; + int value = -1; + + dev_dbg(pTAS2560->dev, "%s\n", __func__); + pTAS2560->read(pTAS2560, TAS2560_LOAD, &value); + + value = (value&0x18) >> 3; + + switch (value) { + case 0: + ret = LOAD_8OHM; + break; + case 1: + ret = LOAD_6OHM; + break; + case 2: + ret = LOAD_4OHM; + break; + default: + break; + } + + return ret; +} + +int tas2560_get_volume(struct tas2560_priv *pTAS2560) +{ + int ret = -1; + int value = -1; + + dev_dbg(pTAS2560->dev, "%s\n", __func__); + ret = pTAS2560->read(pTAS2560, TAS2560_SPK_CTRL_REG, &value); + if (ret >= 0) + return (value & 0x0f); + + return ret; +} + +int tas2560_set_volume(struct tas2560_priv *pTAS2560, int volume) +{ + int ret = -1; + + dev_dbg(pTAS2560->dev, "%s\n", __func__); + ret = pTAS2560->update_bits(pTAS2560, TAS2560_SPK_CTRL_REG, 0x0f, volume&0x0f); + + return ret; +} + +int tas2560_get_PPG(struct tas2560_priv *pTAS2560) +{ + + dev_dbg(pTAS2560->dev, "%s\n", __func__); + return pTAS2560->mnPPG; +} + +int tas2560_set_PPG(struct tas2560_priv *pTAS2560, int ppgvolume) +{ + int ret = -1; + unsigned char *ppg = ppg_gain; + + dev_dbg(pTAS2560->dev, "%s", __func__); + if ((ppgvolume > 110) || (ppgvolume < 0)) { + dev_err(pTAS2560->dev, "invalid ppg volume, %d", ppgvolume); + return ret; + } + pTAS2560->mnPPG = ppgvolume; + ppg += ppgvolume * 4; + dev_dbg(pTAS2560->dev, "PPG reg: 0x%x%x%x%x", + ppg[0], ppg[1], ppg[2], ppg[3]); + ret = pTAS2560->bulk_write(pTAS2560, TAS2560_PPG, ppg, 4); + return ret; +} +int tas2560_parse_dt(struct device *dev, struct tas2560_priv *pTAS2560) +{ + struct device_node *np = dev->of_node; + int rc = 0, ret = 0; + + rc = of_property_read_u32(np, "ti,load", &pTAS2560->mnLoad); + if (rc) { + dev_err(pTAS2560->dev, "Looking up %s property in node %s failed %d\n", + "ti,load", np->full_name, rc); + } else { + dev_dbg(pTAS2560->dev, "ti,load=%d", pTAS2560->mnLoad); + } + + rc = of_property_read_u32(np, "ti,asi-format", &pTAS2560->mnASIFormat); + if (rc) { + dev_err(pTAS2560->dev, "Looking up %s property in node %s failed %d\n", + "ti,asi-format", np->full_name, rc); + } else { + dev_dbg(pTAS2560->dev, "ti,asi-format=%d", pTAS2560->mnASIFormat); + } + + pTAS2560->mnResetGPIO = of_get_named_gpio(np, "ti,reset-gpio", 0); + if (!gpio_is_valid(pTAS2560->mnResetGPIO)) { + dev_err(pTAS2560->dev, "Looking up %s property in node %s failed %d\n", + "ti,reset-gpio", np->full_name, pTAS2560->mnResetGPIO); + } else { + dev_dbg(pTAS2560->dev, "ti,reset-gpio=%d", pTAS2560->mnResetGPIO); + } + + pTAS2560->mnIRQGPIO = of_get_named_gpio(np, "ti,irq-gpio", 0); + if (!gpio_is_valid(pTAS2560->mnIRQGPIO)) { + dev_err(pTAS2560->dev, "Looking up %s property in node %s failed %d\n", + "ti,irq-gpio", np->full_name, pTAS2560->mnIRQGPIO); + } else { + dev_dbg(pTAS2560->dev, "ti,irq-gpio=%d", pTAS2560->mnIRQGPIO); + } + + pTAS2560->mnSwitchGPIO = of_get_named_gpio(np, "ti,ear-switch-gpio", 0); + if (!gpio_is_valid(pTAS2560->mnSwitchGPIO)) { + dev_err(pTAS2560->dev, "Looking up %s property in node %s failed %d\n", + "ti,ear-switch-gpio", np->full_name, pTAS2560->mnSwitchGPIO); + } else { + dev_err(pTAS2560->dev, "ti,ear-switch-gpio=%d", pTAS2560->mnSwitchGPIO); + } + + rc = of_property_read_u32(np, "ti,ppg", &pTAS2560->mnPPG); + if (rc) { + dev_err(pTAS2560->dev, "Looking up %s property in node %s failed %d\n", + "ti,ppg", np->full_name, rc); + } else { + dev_dbg(pTAS2560->dev, "ti,ppg=%d", pTAS2560->mnPPG); + } + + rc = of_property_read_u32(np, "ti,pll", &pTAS2560->mnPLL); + if (rc) { + dev_err(pTAS2560->dev, "Looking up %s property in node %s failed %d\n", + "ti,pll", np->full_name, rc); + } else { + dev_dbg(pTAS2560->dev, "ti,pll=%d", pTAS2560->mnPLL); + } + + return ret; +} + +int tas2560_load_default(struct tas2560_priv *pTAS2560) +{ + return tas2560_i2c_load_data(pTAS2560, p_tas2560_default_data); +} + +int tas2560_load_platdata(struct tas2560_priv *pTAS2560) +{ + int nResult = 0; + + if (gpio_is_valid(pTAS2560->mnIRQGPIO)) { + nResult = tas2560_i2c_load_data(pTAS2560, p_tas2560_irq_config); + if (nResult < 0) + goto end; + } + + if (pTAS2560->mnPLL == PLL_BCLK_1P536_48KSR) + nResult = tas2560_i2c_load_data(pTAS2560, p_tas2560_48kSR_BCLK_1P536_data); + else if (pTAS2560->mnPLL == PLL_BCLK_3P072_48KSR) + nResult = tas2560_i2c_load_data(pTAS2560, p_tas2560_48kSR_BCLK_3P072_data); + if (nResult < 0) + goto end; + + if ((pTAS2560->mnPLL == PLL_BCLK_1P536_48KSR) + || (pTAS2560->mnPLL == PLL_BCLK_3P072_48KSR)) + nResult = tas2560_i2c_load_data(pTAS2560, p_tas2560_48khz_data); + if (nResult < 0) + goto end; + + if (pTAS2560->mnLoad == LOAD_8OHM) { + nResult = pTAS2560->write(pTAS2560, TAS2560_LOAD, 0x83); + } else if (pTAS2560->mnLoad == LOAD_6OHM) { + nResult = pTAS2560->write(pTAS2560, TAS2560_LOAD, 0x8b); + } else { + /* 4 Ohm speaker */ + nResult = pTAS2560->write(pTAS2560, TAS2560_LOAD, 0x9b); + } + if (nResult < 0) + goto end; + + if (pTAS2560->mnASIFormat == TAS2560_DATAFORMAT_I2S) { + nResult = tas2560_set_ASI_fmt(pTAS2560, + SND_SOC_DAIFMT_CBS_CFS|SND_SOC_DAIFMT_NB_NF|SND_SOC_DAIFMT_I2S); + if (nResult < 0) + goto end; + nResult = pTAS2560->write(pTAS2560, TAS2560_ASI_OFFSET_1, 0x00); + } else if (pTAS2560->mnASIFormat == TAS2560_DATAFORMAT_DSP) { + nResult = tas2560_set_ASI_fmt(pTAS2560, + SND_SOC_DAIFMT_CBS_CFS|SND_SOC_DAIFMT_IB_IF|SND_SOC_DAIFMT_DSP_A); + if (nResult < 0) + goto end; + nResult = pTAS2560->write(pTAS2560, TAS2560_ASI_OFFSET_1, 0x01); + } else { + dev_err(pTAS2560->dev, "need to implement!!!\n"); + } + + if (nResult < 0) + goto end; + + nResult = tas2560_set_bit_rate(pTAS2560, 16); + +end: + + return nResult; +} + +static int tas2560_load_postpwrup(struct tas2560_priv *pTAS2560) +{ + int nResult = 0; + unsigned char *ppg = ppg_gain; + + nResult = tas2560_i2c_load_data(pTAS2560, p_tas2560_idle_chnl_detect); + if (nResult < 0) + goto end; + + if ((pTAS2560->mnPPG >= 0) && (pTAS2560->mnPPG <= 110)) { + ppg += pTAS2560->mnPPG * 4; + pTAS2560->bulk_write(pTAS2560, TAS2560_PPG, ppg, 4); + } + + nResult = tas2560_i2c_load_data(pTAS2560, p_tas2560_HPF_data); + if (nResult < 0) + goto end; + + if (pTAS2560->mnLoad == LOAD_8OHM) { + nResult = tas2560_i2c_load_data(pTAS2560, p_tas2560_8Ohm_data); + } else if (pTAS2560->mnLoad == LOAD_6OHM) { + nResult = tas2560_i2c_load_data(pTAS2560, p_tas2560_6Ohm_data); + } + if (nResult < 0) + goto end; + + nResult = tas2560_i2c_load_data(pTAS2560, p_tas2560_boost_headroom_data); + if (nResult < 0) + goto end; + + nResult = tas2560_i2c_load_data(pTAS2560, p_tas2560_thermal_foldback); + if (nResult < 0) + goto end; + + nResult = tas2560_i2c_load_data(pTAS2560, p_tas2560_Vsense_biquad_data); + +end: + return nResult; +} + +int tas2560_LoadConfig(struct tas2560_priv *pTAS2560, bool bPowerOn) +{ + int nResult = 0; + + if (bPowerOn) { + dev_dbg(pTAS2560->dev, "%s power down to load config\n", __func__); + if (hrtimer_active(&pTAS2560->mtimer)) + hrtimer_cancel(&pTAS2560->mtimer); + pTAS2560->enableIRQ(pTAS2560, false); + nResult = tas2560_i2c_load_data(pTAS2560, p_tas2560_shutdown_data); + if (nResult < 0) + goto end; + } + + pTAS2560->hw_reset(pTAS2560); + + nResult = pTAS2560->write(pTAS2560, TAS2560_SW_RESET_REG, 0x01); + if (nResult < 0) + goto end; + msleep(1); + + nResult = tas2560_load_default(pTAS2560); + if (nResult < 0) { + goto end; + } + + nResult = tas2560_load_platdata(pTAS2560); + if (nResult < 0) { + goto end; + } + + if (bPowerOn) { + dev_dbg(pTAS2560->dev, "%s power up\n", __func__); + nResult = tas2560_i2c_load_data(pTAS2560, p_tas2560_startup_data); + if (nResult < 0) + goto end; + nResult = tas2560_load_postpwrup(pTAS2560); + if (nResult < 0) + goto end; + nResult = tas2560_i2c_load_data(pTAS2560, p_tas2560_unmute_data); + if (nResult < 0) + goto end; + pTAS2560->enableIRQ(pTAS2560, true); + } +end: + return nResult; +} + +int tas2560_enable(struct tas2560_priv *pTAS2560, bool bEnable) +{ + int nResult = 0; + + if (bEnable) { + if (!pTAS2560->mbPowerUp) { + dev_info(pTAS2560->dev, "%s power up\n", __func__); + pTAS2560->clearIRQ(pTAS2560); + nResult = tas2560_i2c_load_data(pTAS2560, p_tas2560_startup_data); + if (nResult < 0) + goto end; + nResult = tas2560_load_postpwrup(pTAS2560); + if (nResult < 0) + goto end; + nResult = tas2560_i2c_load_data(pTAS2560, p_tas2560_unmute_data); + pTAS2560->mbPowerUp = true; + if (nResult < 0) + goto end; + pTAS2560->enableIRQ(pTAS2560, true); + } + } else { + if (pTAS2560->mbPowerUp) { + dev_info(pTAS2560->dev, "%s power down\n", __func__); + if (hrtimer_active(&pTAS2560->mtimer)) + hrtimer_cancel(&pTAS2560->mtimer); + pTAS2560->enableIRQ(pTAS2560, false); + nResult = tas2560_i2c_load_data(pTAS2560, p_tas2560_shutdown_data); + pTAS2560->mbPowerUp = false; + } + } + +end: + return nResult; +} + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("TAS2560 common functions for Android Linux"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tas2560-core.h b/sound/soc/codecs/tas2560-core.h new file mode 100644 index 000000000000..c97d6645274a --- /dev/null +++ b/sound/soc/codecs/tas2560-core.h @@ -0,0 +1,45 @@ +/* +** ============================================================================= +** Copyright (c) 2016 Texas Instruments 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; version 2. +** +** 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: +** tas2560-core.h +** +** Description: +** header file for tas2560-core.c +** +** ============================================================================= +*/ + +#ifndef _TAS2560_CORE_H +#define _TAS2560_CORE_H + +#include "tas2560.h" + +int tas2560_set_bit_rate(struct tas2560_priv *pTAS2560, unsigned int nBitRate); +int tas2560_get_bit_rate(struct tas2560_priv *pTAS2560); +int tas2560_set_ASI_fmt(struct tas2560_priv *pTAS2560, unsigned int fmt); +int tas2560_setLoad(struct tas2560_priv *pTAS2560, int load); +int tas2560_getLoad(struct tas2560_priv *pTAS2560); +int tas2560_get_volume(struct tas2560_priv *pTAS2560); +int tas2560_set_volume(struct tas2560_priv *pTAS2560, int volume); +int tas2560_set_SampleRate(struct tas2560_priv *pTAS2560, unsigned int nSamplingRate); +int tas2560_setupPLL(struct tas2560_priv *pTAS2560, int pll_clkin); +int tas2560_set_pll_clkin(struct tas2560_priv *pTAS2560, int clk_id, unsigned int freq); +int tas2560_parse_dt(struct device *dev, struct tas2560_priv *pTAS2560); +void tas2560_sw_shutdown(struct tas2560_priv *pTAS2560, int sw_shutdown); +void tas2560_enable(struct tas2560_priv *pTAS2560, bool bEnable); +int tas2560_load_default(struct tas2560_priv *pTAS2560); +int tas2560_load_platdata(struct tas2560_priv *pTAS2560); +int tas2560_LoadConfig(struct tas2560_priv *pTAS2560, bool bPowerOn); +int tas2560_get_PPG(struct tas2560_priv *pTAS2560); +int tas2560_set_PPG(struct tas2560_priv *pTAS2560, int ppgvolume); +#endif /* _TAS2560_CORE_H */ diff --git a/sound/soc/codecs/tas2560-misc.c b/sound/soc/codecs/tas2560-misc.c new file mode 100644 index 000000000000..a929ab1a49bc --- /dev/null +++ b/sound/soc/codecs/tas2560-misc.c @@ -0,0 +1,438 @@ +/* +** ============================================================================= +** Copyright (c) 2016 Texas Instruments 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; version 2. +** +** 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: +** tas2560-misc.c +** +** Description: +** misc driver for Texas Instruments TAS2560 High Performance 4W Smart Amplifier +** +** ============================================================================= +*/ + +#ifdef CONFIG_TAS2560_MISC + +/* #define DEBUG */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tas2560.h" +#include "tas2560-core.h" +#include "tas2560-misc.h" +#include + + +static int g_logEnable = 1; +static struct tas2560_priv *g_tas2560; + +static int tas2560_file_open(struct inode *inode, struct file *file) +{ + struct tas2560_priv *pTAS2560 = g_tas2560; + + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + file->private_data = (void *)pTAS2560; + + if (g_logEnable) + dev_info(pTAS2560->dev, "%s\n", __func__); + return 0; +} + +static int tas2560_file_release(struct inode *inode, struct file *file) +{ + struct tas2560_priv *pTAS2560 = (struct tas2560_priv *)file->private_data; + + if (g_logEnable) + dev_info(pTAS2560->dev, "%s\n", __func__); + + file->private_data = (void *)NULL; + module_put(THIS_MODULE); + + return 0; +} + +static ssize_t tas2560_file_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + struct tas2560_priv *pTAS2560 = (struct tas2560_priv *)file->private_data; + int ret = 0; + unsigned int nValue = 0; + unsigned char value = 0; + unsigned char *p_kBuf = NULL; + + mutex_lock(&pTAS2560->file_lock); + + switch (pTAS2560->mnDBGCmd) { + case TIAUDIO_CMD_REG_READ: + { + if (g_logEnable) + dev_info(pTAS2560->dev, + "TIAUDIO_CMD_REG_READ: current_reg = 0x%x, count=%d\n", + pTAS2560->mnCurrentReg, (int)count); + if (count == 1) { + ret = pTAS2560->read(pTAS2560, pTAS2560->mnCurrentReg, &nValue); + if (ret < 0) { + dev_err(pTAS2560->dev, "dev read fail %d\n", ret); + break; + } + + value = (u8)nValue; + if (g_logEnable) + dev_info(pTAS2560->dev, "TIAUDIO_CMD_REG_READ: nValue=0x%x, value=0x%x\n", + nValue, value); + ret = copy_to_user(buf, &value, 1); + if (ret != 0) { + /* Failed to copy all the data, exit */ + dev_err(pTAS2560->dev, "copy to user fail %d\n", ret); + } + } else if (count > 1) { + p_kBuf = kzalloc(count, GFP_KERNEL); + if (p_kBuf != NULL) { + ret = pTAS2560->bulk_read(pTAS2560, pTAS2560->mnCurrentReg, p_kBuf, count); + if (ret < 0) { + dev_err(pTAS2560->dev, "dev bulk read fail %d\n", ret); + } else { + ret = copy_to_user(buf, p_kBuf, count); + if (ret != 0) { + /* Failed to copy all the data, exit */ + dev_err(pTAS2560->dev, "copy to user fail %d\n", ret); + } + } + + kfree(p_kBuf); + } else { + dev_err(pTAS2560->dev, "read no mem\n"); + } + } + } + break; + + case TIAUDIO_CMD_SAMPLERATE: + { + if (g_logEnable) + dev_info(pTAS2560->dev, + "TIAUDIO_CMD_SAMPLERATE: count = %d\n", + (int)count); + if (count == 4) { + p_kBuf = kzalloc(count, GFP_KERNEL); + if (p_kBuf != NULL) { + p_kBuf[0] = (pTAS2560->mnSamplingRate&0x000000ff); + p_kBuf[1] = ((pTAS2560->mnSamplingRate&0x0000ff00)>>8); + p_kBuf[2] = ((pTAS2560->mnSamplingRate&0x00ff0000)>>16); + p_kBuf[3] = ((pTAS2560->mnSamplingRate&0xff000000)>>24); + + ret = copy_to_user(buf, p_kBuf, count); + if (ret != 0) { + /* Failed to copy all the data, exit */ + dev_err(pTAS2560->dev, "copy to user fail %d\n", ret); + } + + kfree(p_kBuf); + } else { + dev_err(pTAS2560->dev, "read no mem\n"); + } + } + } + break; + + case TIAUDIO_CMD_BITRATE: + { + if (g_logEnable) + dev_info(pTAS2560->dev, + "TIAUDIO_CMD_BITRATE: count = %d\n", + (int)count); + + if (count == 1) { + int bitRate = tas2560_get_bit_rate(pTAS2560); + + if (bitRate >= 0) { + ret = copy_to_user(buf, &bitRate, 1); + if (ret != 0) { + /* Failed to copy all the data, exit */ + dev_err(pTAS2560->dev, "copy to user fail %d\n", ret); + } + } + } + } + break; + + case TIAUDIO_CMD_DACVOLUME: + { + if (g_logEnable) + dev_info(pTAS2560->dev, + "TIAUDIO_CMD_DACVOLUME: count = %d\n", + (int)count); + + if (count == 1) { + int volume = tas2560_get_volume(pTAS2560); + + if (volume >= 0) { + ret = copy_to_user(buf, &volume, 1); + if (ret != 0) { + /* Failed to copy all the data, exit */ + dev_err(pTAS2560->dev, "copy to user fail %d\n", ret); + } + } + } + } + break; + } + pTAS2560->mnDBGCmd = 0; + + mutex_unlock(&pTAS2560->file_lock); + return count; +} + +static ssize_t tas2560_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + struct tas2560_priv *pTAS2560 = (struct tas2560_priv *)file->private_data; + int ret = 0; + unsigned char *p_kBuf = NULL; + unsigned int reg = 0; + unsigned int len = 0; + + mutex_lock(&pTAS2560->file_lock); + + p_kBuf = kzalloc(count, GFP_KERNEL); + if (p_kBuf == NULL) { + dev_err(pTAS2560->dev, "write no mem\n"); + goto err; + } + + ret = copy_from_user(p_kBuf, buf, count); + if (ret != 0) { + dev_err(pTAS2560->dev, "copy_from_user failed.\n"); + goto err; + } + + pTAS2560->mnDBGCmd = p_kBuf[0]; + switch (pTAS2560->mnDBGCmd) { + case TIAUDIO_CMD_REG_WITE: + if (count > 5) { + reg = ((unsigned int)p_kBuf[1] << 24) + + ((unsigned int)p_kBuf[2] << 16) + + ((unsigned int)p_kBuf[3] << 8) + + (unsigned int)p_kBuf[4]; + len = count - 5; + if (len == 1) { + ret = pTAS2560->write(pTAS2560, reg, p_kBuf[5]); + if (g_logEnable) + dev_info(pTAS2560->dev, + "TIAUDIO_CMD_REG_WITE, Reg=0x%x, Val=0x%x\n", + reg, p_kBuf[5]); + } else { + ret = pTAS2560->bulk_write(pTAS2560, reg, &p_kBuf[5], len); + } + } else { + dev_err(pTAS2560->dev, "%s, write len fail, count=%d.\n", + __func__, (int)count); + } + pTAS2560->mnDBGCmd = 0; + break; + + case TIAUDIO_CMD_REG_READ: + if (count == 5) { + pTAS2560->mnCurrentReg = ((unsigned int)p_kBuf[1] << 24) + + ((unsigned int)p_kBuf[2] << 16) + + ((unsigned int)p_kBuf[3] << 8) + + (unsigned int)p_kBuf[4]; + if (g_logEnable) { + dev_info(pTAS2560->dev, + "TIAUDIO_CMD_REG_READ, whole=0x%x\n", + pTAS2560->mnCurrentReg); + } + } else { + dev_err(pTAS2560->dev, "read len fail.\n"); + } + break; + + case TIAUDIO_CMD_DEBUG_ON: + { + if (count == 2) { + g_logEnable = p_kBuf[1]; + } + pTAS2560->mnDBGCmd = 0; + } + break; + + case TIAUDIO_CMD_SAMPLERATE: + { + if (count == 5) { + unsigned int nSampleRate = ((unsigned int)p_kBuf[1] << 24) + + ((unsigned int)p_kBuf[2] << 16) + + ((unsigned int)p_kBuf[3] << 8) + + (unsigned int)p_kBuf[4]; + + if (g_logEnable) + dev_info(pTAS2560->dev, + "TIAUDIO_CMD_SAMPLERATE, set to %d\n", + nSampleRate); + + tas2560_set_SampleRate(pTAS2560, nSampleRate); + } + } + break; + + case TIAUDIO_CMD_BITRATE: + { + if (count == 2) { + if (g_logEnable) + dev_info(pTAS2560->dev, + "TIAUDIO_CMD_BITRATE, set to %d\n", + p_kBuf[1]); + + tas2560_set_bit_rate(pTAS2560, p_kBuf[1]); + } + } + break; + + case TIAUDIO_CMD_DACVOLUME: + { + if (count == 2) { + unsigned char volume = (p_kBuf[1] & 0x0f); + + if (g_logEnable) + dev_info(pTAS2560->dev, + "TIAUDIO_CMD_DACVOLUME, set to %d\n", + volume); + + tas2560_set_volume(pTAS2560, volume); + } + } + break; + + case TIAUDIO_CMD_SPEAKER: + { + if (count == 2) { + if (g_logEnable) + dev_info(pTAS2560->dev, + "TIAUDIO_CMD_SPEAKER, set to %d\n", + p_kBuf[1]); + tas2560_enable(pTAS2560, (p_kBuf[1] > 0)); + } + } + break; + + default: + pTAS2560->mnDBGCmd = 0; + break; + } + +err: + if (p_kBuf != NULL) + kfree(p_kBuf); + + mutex_unlock(&pTAS2560->file_lock); + + return count; +} + +static long tas2560_file_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct tas2560_priv *pTAS2560 = file->private_data; + int ret = 0; + + mutex_lock(&pTAS2560->file_lock); + + switch (cmd) { + case SMARTPA_SPK_DAC_VOLUME: + { + u8 volume = (arg & 0x0f); + + tas2560_set_volume(pTAS2560, volume); + } + break; + + case SMARTPA_SPK_POWER_ON: + { + tas2560_enable(pTAS2560, true); + } + break; + + case SMARTPA_SPK_POWER_OFF: + { + tas2560_enable(pTAS2560, false); + } + break; + + case SMARTPA_SPK_SET_SAMPLERATE: + { + tas2560_set_SampleRate(pTAS2560, arg); + } + break; + + case SMARTPA_SPK_SET_BITRATE: + { + tas2560_set_bit_rate(pTAS2560, arg); + } + break; + } + + mutex_unlock(&pTAS2560->file_lock); + return ret; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .read = tas2560_file_read, + .write = tas2560_file_write, + .unlocked_ioctl = tas2560_file_unlocked_ioctl, + .open = tas2560_file_open, + .release = tas2560_file_release, +}; + +#define MODULE_NAME "tas2560" +static struct miscdevice tas2560_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = MODULE_NAME, + .fops = &fops, +}; + +int tas2560_register_misc(struct tas2560_priv *pTAS2560) +{ + int ret = 0; + + g_tas2560 = pTAS2560; + ret = misc_register(&tas2560_misc); + if (ret) { + dev_err(pTAS2560->dev, "TAS2560 misc fail: %d\n", ret); + } + + dev_info(pTAS2560->dev, "%s, leave\n", __func__); + + return ret; +} + +int tas2560_deregister_misc(struct tas2560_priv *pTAS2560) +{ + misc_deregister(&tas2560_misc); + return 0; +} + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("TAS2560 Misc Smart Amplifier driver"); +MODULE_LICENSE("GPL v2"); +#endif /* CONFIG_TAS2560_MISC */ diff --git a/sound/soc/codecs/tas2560-misc.h b/sound/soc/codecs/tas2560-misc.h new file mode 100644 index 000000000000..b40f11297ea4 --- /dev/null +++ b/sound/soc/codecs/tas2560-misc.h @@ -0,0 +1,45 @@ +/* +** ============================================================================= +** Copyright (c) 2016 Texas Instruments 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; version 2. +** +** 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: +** tas2560-misc.h +** +** Description: +** header file for tas2560-misc.c +** +** ============================================================================= +*/ + +#ifndef _TAS2560_MISC_H +#define _TAS2560_MISC_H + +#define TIAUDIO_CMD_REG_WITE 1 +#define TIAUDIO_CMD_REG_READ 2 +#define TIAUDIO_CMD_DEBUG_ON 3 +#define TIAUDIO_CMD_CALIBRATION 7 +#define TIAUDIO_CMD_SAMPLERATE 8 +#define TIAUDIO_CMD_BITRATE 9 +#define TIAUDIO_CMD_DACVOLUME 10 +#define TIAUDIO_CMD_SPEAKER 11 + +#define TAS2560_MAGIC_NUMBER 'M' /* '2560' */ + +#define SMARTPA_SPK_DAC_VOLUME _IOWR(TAS2560_MAGIC_NUMBER, 1, unsigned long) +#define SMARTPA_SPK_POWER_ON _IOWR(TAS2560_MAGIC_NUMBER, 2, unsigned long) +#define SMARTPA_SPK_POWER_OFF _IOWR(TAS2560_MAGIC_NUMBER, 3, unsigned long) +#define SMARTPA_SPK_SET_SAMPLERATE _IOWR(TAS2560_MAGIC_NUMBER, 7, unsigned long) +#define SMARTPA_SPK_SET_BITRATE _IOWR(TAS2560_MAGIC_NUMBER, 8, unsigned long) + +int tas2560_register_misc(struct tas2560_priv *pTAS2560); +int tas2560_deregister_misc(struct tas2560_priv *pTAS2560); + +#endif /* _TAS2560_MISC_H */ diff --git a/sound/soc/codecs/tas2560-regmap.c b/sound/soc/codecs/tas2560-regmap.c new file mode 100644 index 000000000000..6d2345385673 --- /dev/null +++ b/sound/soc/codecs/tas2560-regmap.c @@ -0,0 +1,662 @@ +/* + * ALSA SoC Texas Instruments TAS2560 High Performance 4W Smart Amplifier + * + * Copyright (C) 2016 Texas Instruments, Inc. + * + * Author: saiprasad + * + * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ +#ifdef CONFIG_TAS2560_REGMAP + +/* #define DEBUG */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tas2560.h" +#include "tas2560-core.h" +#include "tas2560-misc.h" +#include "tas2560-codec.h" + +static int tas2560_change_book_page(struct tas2560_priv *pTAS2560, + int book, int page) +{ + int nResult = 0; + + if ((pTAS2560->mnCurrentBook == book) + && (pTAS2560->mnCurrentPage == page)) + goto end; + + if (pTAS2560->mnCurrentBook != book) { + nResult = regmap_write(pTAS2560->regmap, TAS2560_BOOKCTL_PAGE, 0); + if (nResult < 0) { + dev_err(pTAS2560->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, nResult); + goto end; + } + pTAS2560->mnCurrentPage = 0; + nResult = regmap_write(pTAS2560->regmap, TAS2560_BOOKCTL_REG, book); + if (nResult < 0) { + dev_err(pTAS2560->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, nResult); + goto end; + } + pTAS2560->mnCurrentBook = book; + } + + if (pTAS2560->mnCurrentPage != page) { + nResult = regmap_write(pTAS2560->regmap, TAS2560_BOOKCTL_PAGE, page); + if (nResult < 0) { + dev_err(pTAS2560->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, nResult); + goto end; + } + pTAS2560->mnCurrentPage = page; + } + +end: + return nResult; +} + +static int tas2560_dev_read(struct tas2560_priv *pTAS2560, + unsigned int reg, unsigned int *pValue) +{ + int nResult = 0; + + mutex_lock(&pTAS2560->dev_lock); + + nResult = tas2560_change_book_page(pTAS2560, + TAS2560_BOOK_ID(reg), TAS2560_PAGE_ID(reg)); + if (nResult < 0) + goto end; + + nResult = regmap_read(pTAS2560->regmap, TAS2560_PAGE_REG(reg), pValue); + if (nResult < 0) + dev_err(pTAS2560->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, nResult); + else + dev_dbg(pTAS2560->dev, "%s: BOOK:PAGE:REG %u:%u:%u\n", __func__, + TAS2560_BOOK_ID(reg), TAS2560_PAGE_ID(reg), + TAS2560_PAGE_REG(reg)); + +end: + mutex_unlock(&pTAS2560->dev_lock); + return nResult; +} + +static int tas2560_dev_write(struct tas2560_priv *pTAS2560, + unsigned int reg, unsigned int value) +{ + int nResult = 0; + + mutex_lock(&pTAS2560->dev_lock); + + nResult = tas2560_change_book_page(pTAS2560, + TAS2560_BOOK_ID(reg), TAS2560_PAGE_ID(reg)); + if (nResult < 0) + goto end; + + nResult = regmap_write(pTAS2560->regmap, TAS2560_PAGE_REG(reg), + value); + if (nResult < 0) + dev_err(pTAS2560->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, nResult); + else + dev_dbg(pTAS2560->dev, "%s: BOOK:PAGE:REG %u:%u:%u, VAL: 0x%02x\n", + __func__, TAS2560_BOOK_ID(reg), TAS2560_PAGE_ID(reg), + TAS2560_PAGE_REG(reg), value); + +end: + mutex_unlock(&pTAS2560->dev_lock); + return nResult; +} + +static int tas2560_dev_bulk_write(struct tas2560_priv *pTAS2560, + unsigned int reg, unsigned char *pData, unsigned int nLength) +{ + int nResult = 0; + + mutex_lock(&pTAS2560->dev_lock); + + nResult = tas2560_change_book_page(pTAS2560, + TAS2560_BOOK_ID(reg), TAS2560_PAGE_ID(reg)); + if (nResult < 0) + goto end; + + nResult = regmap_bulk_write(pTAS2560->regmap, + TAS2560_PAGE_REG(reg), pData, nLength); + if (nResult < 0) + dev_err(pTAS2560->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, nResult); + else + dev_dbg(pTAS2560->dev, "%s: BOOK:PAGE:REG %u:%u:%u, len: 0x%02x\n", + __func__, TAS2560_BOOK_ID(reg), TAS2560_PAGE_ID(reg), + TAS2560_PAGE_REG(reg), nLength); + +end: + mutex_unlock(&pTAS2560->dev_lock); + return nResult; +} + +static int tas2560_dev_bulk_read(struct tas2560_priv *pTAS2560, + unsigned int reg, unsigned char *pData, unsigned int nLength) +{ + int nResult = 0; + + mutex_lock(&pTAS2560->dev_lock); + + nResult = tas2560_change_book_page(pTAS2560, + TAS2560_BOOK_ID(reg), TAS2560_PAGE_ID(reg)); + if (nResult < 0) + goto end; + + nResult = regmap_bulk_read(pTAS2560->regmap, TAS2560_PAGE_REG(reg), pData, nLength); + if (nResult < 0) + dev_err(pTAS2560->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, nResult); + else + dev_dbg(pTAS2560->dev, "%s: BOOK:PAGE:REG %u:%u:%u, len: 0x%02x\n", + __func__, TAS2560_BOOK_ID(reg), TAS2560_PAGE_ID(reg), + TAS2560_PAGE_REG(reg), nLength); +end: + mutex_unlock(&pTAS2560->dev_lock); + return nResult; +} + +static int tas2560_dev_update_bits(struct tas2560_priv *pTAS2560, unsigned int reg, + unsigned int mask, unsigned int value) +{ + int nResult = 0; + + mutex_lock(&pTAS2560->dev_lock); + nResult = tas2560_change_book_page(pTAS2560, + TAS2560_BOOK_ID(reg), TAS2560_PAGE_ID(reg)); + if (nResult < 0) + goto end; + + nResult = regmap_update_bits(pTAS2560->regmap, TAS2560_PAGE_REG(reg), mask, value); + if (nResult < 0) + dev_err(pTAS2560->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, nResult); + else + dev_dbg(pTAS2560->dev, "%s: BOOK:PAGE:REG %u:%u:%u, mask: 0x%x, val=0x%x\n", + __func__, TAS2560_BOOK_ID(reg), TAS2560_PAGE_ID(reg), + TAS2560_PAGE_REG(reg), mask, value); +end: + mutex_unlock(&pTAS2560->dev_lock); + return nResult; +} + +static bool tas2560_volatile(struct device *dev, unsigned int reg) +{ + return false; +} + +static bool tas2560_writeable(struct device *dev, unsigned int reg) +{ + return true; +} + +static const struct regmap_config tas2560_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = tas2560_writeable, + .volatile_reg = tas2560_volatile, + .cache_type = REGCACHE_NONE, + .max_register = 128, +}; + +static void tas2560_hw_reset(struct tas2560_priv *pTAS2560) +{ + if (gpio_is_valid(pTAS2560->mnResetGPIO)) { + gpio_direction_output(pTAS2560->mnResetGPIO, 0); + msleep(5); + gpio_direction_output(pTAS2560->mnResetGPIO, 1); + msleep(2); + } + + pTAS2560->mnCurrentBook = -1; + pTAS2560->mnCurrentPage = -1; +} + +void tas2560_clearIRQ(struct tas2560_priv *pTAS2560) +{ + unsigned int nValue; + int nResult = 0; + + nResult = pTAS2560->read(pTAS2560, TAS2560_FLAGS_1, &nValue); + if (nResult >= 0) + pTAS2560->read(pTAS2560, TAS2560_FLAGS_2, &nValue); +} + +void tas2560_enableIRQ(struct tas2560_priv *pTAS2560, bool enable) +{ + if (enable) { + if (pTAS2560->mbIRQEnable) + return; + + if (gpio_is_valid(pTAS2560->mnIRQGPIO)) + enable_irq(pTAS2560->mnIRQ); + + schedule_delayed_work(&pTAS2560->irq_work, msecs_to_jiffies(10)); + pTAS2560->mbIRQEnable = true; + } else { + if (!pTAS2560->mbIRQEnable) + return; + + if (gpio_is_valid(pTAS2560->mnIRQGPIO)) + disable_irq_nosync(pTAS2560->mnIRQ); + pTAS2560->mbIRQEnable = false; + } +} + +static void irq_work_routine(struct work_struct *work) +{ + struct tas2560_priv *pTAS2560 = + container_of(work, struct tas2560_priv, irq_work.work); + unsigned int nDevInt1Status = 0, nDevInt2Status = 0; + int nCounter = 2; + int nResult = 0; + +#ifdef CONFIG_TAS2560_CODEC + mutex_lock(&pTAS2560->codec_lock); +#endif + +#ifdef CONFIG_TAS2560_MISC + mutex_lock(&pTAS2560->file_lock); +#endif + if (pTAS2560->mbRuntimeSuspend) { + dev_info(pTAS2560->dev, "%s, Runtime Suspended\n", __func__); + goto end; + } + + if (!pTAS2560->mbPowerUp) { + dev_info(pTAS2560->dev, "%s, device not powered\n", __func__); + goto end; + } + + nResult = tas2560_dev_write(pTAS2560, TAS2560_INT_GEN_REG, 0x00); + if (nResult < 0) + goto reload; + + nResult = tas2560_dev_read(pTAS2560, TAS2560_FLAGS_1, &nDevInt1Status); + if (nResult >= 0) + nResult = tas2560_dev_read(pTAS2560, TAS2560_FLAGS_2, &nDevInt2Status); + else + goto reload; + + if (((nDevInt1Status & 0xfc) != 0) || ((nDevInt2Status & 0xc0) != 0)) { + /* in case of INT_OC, INT_UV, INT_OT, INT_BO, INT_CL, INT_CLK1, INT_CLK2 */ + dev_dbg(pTAS2560->dev, "IRQ critical Error : 0x%x, 0x%x\n", + nDevInt1Status, nDevInt2Status); + + if (nDevInt1Status & 0x80) { + pTAS2560->mnErrCode |= ERROR_OVER_CURRENT; + dev_err(pTAS2560->dev, "SPK over current!\n"); + } else + pTAS2560->mnErrCode &= ~ERROR_OVER_CURRENT; + + if (nDevInt1Status & 0x40) { + pTAS2560->mnErrCode |= ERROR_UNDER_VOLTAGE; + dev_err(pTAS2560->dev, "SPK under voltage!\n"); + } else + pTAS2560->mnErrCode &= ~ERROR_UNDER_VOLTAGE; + + if (nDevInt1Status & 0x20) { + pTAS2560->mnErrCode |= ERROR_CLK_HALT; + dev_err(pTAS2560->dev, "clk halted!\n"); + } else + pTAS2560->mnErrCode &= ~ERROR_CLK_HALT; + + if (nDevInt1Status & 0x10) { + pTAS2560->mnErrCode |= ERROR_DIE_OVERTEMP; + dev_err(pTAS2560->dev, "die over temperature!\n"); + } else + pTAS2560->mnErrCode &= ~ERROR_DIE_OVERTEMP; + + if (nDevInt1Status & 0x08) { + pTAS2560->mnErrCode |= ERROR_BROWNOUT; + dev_err(pTAS2560->dev, "brownout!\n"); + } else + pTAS2560->mnErrCode &= ~ERROR_BROWNOUT; + + if (nDevInt1Status & 0x04) { + pTAS2560->mnErrCode |= ERROR_CLK_LOST; + } else + pTAS2560->mnErrCode &= ~ERROR_CLK_LOST; + + if (nDevInt2Status & 0x80) { + pTAS2560->mnErrCode |= ERROR_CLK_DET1; + dev_err(pTAS2560->dev, "clk detection 1!\n"); + } else + pTAS2560->mnErrCode &= ~ERROR_CLK_DET1; + + if (nDevInt2Status & 0x40) { + pTAS2560->mnErrCode |= ERROR_CLK_DET2; + dev_err(pTAS2560->dev, "clk detection 2!\n"); + } else + pTAS2560->mnErrCode &= ~ERROR_CLK_DET2; + + goto reload; + } else { + dev_dbg(pTAS2560->dev, "IRQ status : 0x%x, 0x%x\n", + nDevInt1Status, nDevInt2Status); + nCounter = 2; + + while (nCounter > 0) { + nResult = tas2560_dev_read(pTAS2560, TAS2560_POWER_UP_FLAG_REG, &nDevInt1Status); + if (nResult < 0) + goto reload; + + if ((nDevInt1Status & 0xc0) == 0xc0) + break; + + nCounter--; + if (nCounter > 0) { + /* in case check pow status just after power on TAS2560 */ + dev_dbg(pTAS2560->dev, "PowSts B: 0x%x, check again after 10ms\n", + nDevInt1Status); + msleep(10); + } + } + + if ((nDevInt1Status & 0xc0) != 0xc0) { + dev_err(pTAS2560->dev, "%s, Critical ERROR B[%d]_P[%d]_R[%d]= 0x%x\n", + __func__, + TAS2560_BOOK_ID(TAS2560_POWER_UP_FLAG_REG), + TAS2560_PAGE_ID(TAS2560_POWER_UP_FLAG_REG), + TAS2560_PAGE_REG(TAS2560_POWER_UP_FLAG_REG), + nDevInt1Status); + pTAS2560->mnErrCode |= ERROR_CLASSD_PWR; + goto reload; + } + pTAS2560->mnErrCode &= ~ERROR_CLASSD_PWR; + } + + nResult = tas2560_dev_write(pTAS2560, TAS2560_INT_GEN_REG, 0xff); + if (nResult < 0) + goto reload; + + goto end; + +reload: + /* hardware reset and reload */ + tas2560_LoadConfig(pTAS2560, true); + +end: + if (!hrtimer_active(&pTAS2560->mtimer)) { + dev_dbg(pTAS2560->dev, "%s, start timer\n", __func__); + hrtimer_start(&pTAS2560->mtimer, + ns_to_ktime((u64)CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL); + } +#ifdef CONFIG_TAS2560_MISC + mutex_unlock(&pTAS2560->file_lock); +#endif + +#ifdef CONFIG_TAS2560_CODEC + mutex_unlock(&pTAS2560->codec_lock); +#endif +} + +static enum hrtimer_restart timer_func(struct hrtimer *timer) +{ + struct tas2560_priv *pTAS2560 = container_of(timer, struct tas2560_priv, mtimer); + + if (pTAS2560->mbPowerUp) { + if (!delayed_work_pending(&pTAS2560->irq_work)) + schedule_delayed_work(&pTAS2560->irq_work, msecs_to_jiffies(20)); + } + + return HRTIMER_NORESTART; +} + +static irqreturn_t tas2560_irq_handler(int irq, void *dev_id) +{ + struct tas2560_priv *pTAS2560 = (struct tas2560_priv *)dev_id; + + tas2560_enableIRQ(pTAS2560, false); + + /* get IRQ status after 100 ms */ + if (!delayed_work_pending(&pTAS2560->irq_work)) + schedule_delayed_work(&pTAS2560->irq_work, msecs_to_jiffies(100)); + + return IRQ_HANDLED; +} + +static int tas2560_runtime_suspend(struct tas2560_priv *pTAS2560) +{ + dev_dbg(pTAS2560->dev, "%s\n", __func__); + + pTAS2560->mbRuntimeSuspend = true; + + if (hrtimer_active(&pTAS2560->mtimer)) { + dev_dbg(pTAS2560->dev, "cancel die temp timer\n"); + hrtimer_cancel(&pTAS2560->mtimer); + } + + if (delayed_work_pending(&pTAS2560->irq_work)) { + dev_dbg(pTAS2560->dev, "cancel IRQ work\n"); + cancel_delayed_work_sync(&pTAS2560->irq_work); + } + + return 0; +} + +static int tas2560_runtime_resume(struct tas2560_priv *pTAS2560) +{ + dev_dbg(pTAS2560->dev, "%s\n", __func__); + + if (pTAS2560->mbPowerUp) { + if (!hrtimer_active(&pTAS2560->mtimer)) { + dev_dbg(pTAS2560->dev, "%s, start check timer\n", __func__); + hrtimer_start(&pTAS2560->mtimer, + ns_to_ktime((u64)CHECK_PERIOD * NSEC_PER_MSEC), HRTIMER_MODE_REL); + } + } + + pTAS2560->mbRuntimeSuspend = false; + + return 0; +} + +static int tas2560_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tas2560_priv *pTAS2560; + int nResult; + + dev_info(&client->dev, "%s enter\n", __func__); + + pTAS2560 = devm_kzalloc(&client->dev, sizeof(struct tas2560_priv), GFP_KERNEL); + if (pTAS2560 == NULL) { + dev_err(&client->dev, "%s, -ENOMEM \n", __func__); + nResult = -ENOMEM; + goto end; + } + + pTAS2560->dev = &client->dev; + i2c_set_clientdata(client, pTAS2560); + dev_set_drvdata(&client->dev, pTAS2560); + + pTAS2560->regmap = devm_regmap_init_i2c(client, &tas2560_i2c_regmap); + if (IS_ERR(pTAS2560->regmap)) { + nResult = PTR_ERR(pTAS2560->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + nResult); + goto end; + } + + if (client->dev.of_node) + tas2560_parse_dt(&client->dev, pTAS2560); + + if (gpio_is_valid(pTAS2560->mnResetGPIO)) { + nResult = gpio_request(pTAS2560->mnResetGPIO, "TAS2560_RESET"); + if (nResult) { + dev_err(pTAS2560->dev, "%s: Failed to request gpio %d\n", __func__, + pTAS2560->mnResetGPIO); + nResult = -EINVAL; + goto free_gpio; + } + } + + if (gpio_is_valid(pTAS2560->mnSwitchGPIO)) { + nResult = gpio_request(pTAS2560->mnSwitchGPIO, "TAS2560_EAR_SWITH"); + if (nResult) { + dev_err(pTAS2560->dev, "%s: Failed to request gpio %d\n", __func__, + pTAS2560->mnSwitchGPIO); + nResult = -EINVAL; + goto free_gpio; + } + gpio_direction_output(pTAS2560->mnSwitchGPIO, 0); + } + + if (gpio_is_valid(pTAS2560->mnIRQGPIO)) { + nResult = gpio_request(pTAS2560->mnIRQGPIO, "TAS2560-IRQ"); + if (nResult < 0) { + dev_err(pTAS2560->dev, "%s: GPIO %d request error\n", + __func__, pTAS2560->mnIRQGPIO); + goto free_gpio; + } + gpio_direction_input(pTAS2560->mnIRQGPIO); + pTAS2560->mnIRQ = gpio_to_irq(pTAS2560->mnIRQGPIO); + dev_dbg(pTAS2560->dev, "irq = %d\n", pTAS2560->mnIRQ); + nResult = request_threaded_irq(pTAS2560->mnIRQ, tas2560_irq_handler, + NULL, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + client->name, pTAS2560); + if (nResult < 0) { + dev_err(pTAS2560->dev, + "request_irq failed, %d\n", nResult); + goto free_gpio; + } + disable_irq_nosync(pTAS2560->mnIRQ); + INIT_DELAYED_WORK(&pTAS2560->irq_work, irq_work_routine); + } + + pTAS2560->read = tas2560_dev_read; + pTAS2560->write = tas2560_dev_write; + pTAS2560->bulk_read = tas2560_dev_bulk_read; + pTAS2560->bulk_write = tas2560_dev_bulk_write; + pTAS2560->update_bits = tas2560_dev_update_bits; + pTAS2560->hw_reset = tas2560_hw_reset; + pTAS2560->enableIRQ = tas2560_enableIRQ; + pTAS2560->clearIRQ = tas2560_clearIRQ; + pTAS2560->runtime_suspend = tas2560_runtime_suspend; + pTAS2560->runtime_resume = tas2560_runtime_resume; + mutex_init(&pTAS2560->dev_lock); + + nResult = tas2560_LoadConfig(pTAS2560, false); + if (nResult < 0) + goto destroy_mutex; + +#ifdef CONFIG_TAS2560_CODEC + mutex_init(&pTAS2560->codec_lock); + tas2560_register_codec(pTAS2560); +#endif + +#ifdef CONFIG_TAS2560_MISC + mutex_init(&pTAS2560->file_lock); + tas2560_register_misc(pTAS2560); +#endif + + hrtimer_init(&pTAS2560->mtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + pTAS2560->mtimer.function = timer_func; + +destroy_mutex: + if (nResult < 0) + mutex_destroy(&pTAS2560->dev_lock); + +free_gpio: + if (nResult < 0) { + if (gpio_is_valid(pTAS2560->mnResetGPIO)) + gpio_free(pTAS2560->mnResetGPIO); + if (gpio_is_valid(pTAS2560->mnIRQGPIO)) + gpio_free(pTAS2560->mnIRQGPIO); + if (gpio_is_valid(pTAS2560->mnSwitchGPIO)) + gpio_free(pTAS2560->mnSwitchGPIO); + } + +end: + return nResult; +} + +static int tas2560_i2c_remove(struct i2c_client *client) +{ + struct tas2560_priv *pTAS2560 = i2c_get_clientdata(client); + + dev_info(pTAS2560->dev, "%s\n", __func__); + +#ifdef CONFIG_TAS2560_CODEC + tas2560_deregister_codec(pTAS2560); + mutex_destroy(&pTAS2560->codec_lock); +#endif + +#ifdef CONFIG_TAS2560_MISC + tas2560_deregister_misc(pTAS2560); + mutex_destroy(&pTAS2560->file_lock); +#endif + + if (gpio_is_valid(pTAS2560->mnResetGPIO)) + gpio_free(pTAS2560->mnResetGPIO); + if (gpio_is_valid(pTAS2560->mnIRQGPIO)) + gpio_free(pTAS2560->mnIRQGPIO); + if (gpio_is_valid(pTAS2560->mnSwitchGPIO)) + gpio_free(pTAS2560->mnSwitchGPIO); + + return 0; +} + + +static const struct i2c_device_id tas2560_i2c_id[] = { + { "tas2560", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas2560_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id tas2560_of_match[] = { + { .compatible = "ti,tas2560" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tas2560_of_match); +#endif + + +static struct i2c_driver tas2560_i2c_driver = { + .driver = { + .name = "tas2560", + .owner = THIS_MODULE, +#if defined(CONFIG_OF) + .of_match_table = of_match_ptr(tas2560_of_match), +#endif + }, + .probe = tas2560_i2c_probe, + .remove = tas2560_i2c_remove, + .id_table = tas2560_i2c_id, +}; + +module_i2c_driver(tas2560_i2c_driver); + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("TAS2560 I2C Smart Amplifier driver"); +MODULE_LICENSE("GPL v2"); +#endif diff --git a/sound/soc/codecs/tas2560.h b/sound/soc/codecs/tas2560.h new file mode 100644 index 000000000000..2098985af7f7 --- /dev/null +++ b/sound/soc/codecs/tas2560.h @@ -0,0 +1,223 @@ +/* +** ============================================================================= +** Copyright (c) 2016 Texas Instruments 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; version 2. +** +** 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: +** tas2560.h +** +** Description: +** definitions and data structures for TAS2560 Android Linux driver +** +** ============================================================================= +*/ + +#ifndef _TAS2560_H +#define _TAS2560_H + +/* Page Control Register */ +#define TAS2560_PAGECTL_REG 0 + +/* Book Control Register (available in page0 of each book) */ +#define TAS2560_BOOKCTL_PAGE 0 +#define TAS2560_BOOKCTL_REG 127 + +#define TAS2560_REG(book, page, reg) (((book * 256 * 128) + \ + (page * 128)) + reg) + +#define TAS2560_BOOK_ID(reg) (reg / (256 * 128)) +#define TAS2560_PAGE_ID(reg) ((reg % (256 * 128)) / 128) +#define TAS2560_BOOK_REG(reg) (reg % (256 * 128)) +#define TAS2560_PAGE_REG(reg) ((reg % (256 * 128)) % 128) + +/* Book0, Page0 registers */ +#define TAS2560_SW_RESET_REG TAS2560_REG(0, 0, 1) +#define TAS2560_DEV_MODE_REG TAS2560_REG(0, 0, 2) +#define TAS2560_SPK_CTRL_REG TAS2560_REG(0, 0, 4) +#define TAS2560_MUTE_REG TAS2560_REG(0, 0, 7) +#define TAS2560_PWR_REG TAS2560_REG(0, 0, 7) +#define TAS2560_PWR_BIT_MASK (0x3 << 6) +#define TAS2560_MUTE_MASK (0x7) + +#define TAS2560_SR_CTRL1 TAS2560_REG(0, 0, 8) +#define TAS2560_LOAD TAS2560_REG(0, 0, 9) +#define TAS2560_SR_CTRL2 TAS2560_REG(0, 0, 13) /* B0_P0_R0x0d*/ +#define TAS2560_SR_CTRL3 TAS2560_REG(0, 0, 14) /* B0_P0_R0x0e*/ + +#define TAS2560_CLK_SEL TAS2560_REG(0, 0, 15) +#define TAS2560_PLL_SRC_MASK (0xc0) +#define TAS2560_PLL_CLKIN_BCLK (0) +#define TAS2560_PLL_CLKIN_MCLK (1) +#define TAS2560_PLL_CLKIN_PDMCLK (2) +#define TAS2560_PLL_P_MASK (0x3f) + +#define TAS2560_SET_FREQ TAS2560_REG(0, 0, 16) +#define TAS2560_PLL_J_MASK (0x7f) + +#define TAS2560_PLL_D_MSB TAS2560_REG(0, 0, 17) /*B0_P0_R0x11*/ +#define TAS2560_PLL_D_LSB TAS2560_REG(0, 0, 18) /*B0_P0_R0x12*/ + +#define TAS2560_DAI_FMT TAS2560_REG(0, 0, 20) /*B0_P0_R0x14*/ +#define TAS2560_ASI_OFFSET_1 TAS2560_REG(0, 0, 22) +#define TAS2560_ASI_CFG_1 TAS2560_REG(0, 0, 24) /*B0_P0_R0x18*/ +#define TAS2560_DIRINV_MASK 0x3c +#define TAS2560_BCLKINV (1 << 2) +#define TAS2560_WCLKINV (1 << 3) +#define TAS2560_BCLKDIR (1 << 4) +#define TAS2560_WCLKDIR (1 << 5) + +#define TAS2560_CLK_ERR_CTRL TAS2560_REG(0, 0, 33) /* B0_P0_R0x21 */ +#define TAS2560_IRQ_PIN_REG TAS2560_REG(0, 0, 35) /* B0_P0_R0x23 */ +#define TAS2560_INT_MODE_REG TAS2560_REG(0, 0, 36) /* B0_P0_R0x24 */ +#define TAS2560_INT_GEN_REG TAS2560_REG(0, 0, 37) /* B0_P0_R0x25 */ +#define TAS2560_FLAGS_1 TAS2560_REG(0, 0, 38) /* B0_P0_R0x26 */ +#define TAS2560_FLAGS_2 TAS2560_REG(0, 0, 39) /* B0_P0_R0x27 */ +#define TAS2560_POWER_UP_FLAG_REG TAS2560_REG(0, 0, 42) /* B0_P0_R0x2a */ + +#define TAS2560_DR_BOOST_REG_2 TAS2560_REG(0, 0, 60) /* B0_P0_R0x3c */ +#define TAS2560_DR_BOOST_REG_1 TAS2560_REG(0, 0, 73) /* B0_P0_R0x49 */ +#define TAS2560_CLK_ERR_CTRL2 TAS2560_REG(0, 0, 80) /* B0_P0_R0x50 */ +#define TAS2560_ID_REG TAS2560_REG(0, 0, 125) + +/* Book0, Page50 registers */ +#define TAS2560_PPG TAS2560_REG(0, 50, 12) /*B0_P0x32_R0x0c*/ + +#define TAS2560_HPF_CUTOFF_CTL1 TAS2560_REG(0, 50, 28) /* B0_P0x32_R0x1c */ +#define TAS2560_HPF_CUTOFF_CTL2 TAS2560_REG(0, 50, 32) /* B0_P0x32_R0x20 */ +#define TAS2560_HPF_CUTOFF_CTL3 TAS2560_REG(0, 50, 36) /* B0_P0x32_R0x24 */ + +#define TAS2560_ISENSE_PATH_CTL1 TAS2560_REG(0, 50, 40) /* B0_P0x32_R0x28 */ +#define TAS2560_ISENSE_PATH_CTL2 TAS2560_REG(0, 50, 44) /* B0_P0x32_R0x2c */ +#define TAS2560_ISENSE_PATH_CTL3 TAS2560_REG(0, 50, 48) /* B0_P0x32_R0x30 */ + +#define TAS2560_VLIMIT_THRESHOLD TAS2560_REG(0, 50, 60) +#define TAS2560_IDLE_CHNL_DETECT TAS2560_REG(0, 50, 108) /* B0_P0x32_R0x6c */ + +/* Book0, Page51 registers */ +#define TAS2560_BOOST_HEAD TAS2560_REG(0, 51, 24) /* B0_P0x33_R0x18 */ +#define TAS2560_BOOST_ON TAS2560_REG(0, 51, 16) /* B0_P0x33_R0x10 */ +#define TAS2560_BOOST_OFF TAS2560_REG(0, 51, 20) /* B0_P0x33_R0x14 */ +#define TAS2560_BOOST_TABLE_CTRL1 TAS2560_REG(0, 51, 32) /* B0_P0x33_R0x20 */ +#define TAS2560_BOOST_TABLE_CTRL2 TAS2560_REG(0, 51, 36) /* B0_P0x33_R0x24 */ +#define TAS2560_BOOST_TABLE_CTRL3 TAS2560_REG(0, 51, 40) /* B0_P0x33_R0x28 */ +#define TAS2560_BOOST_TABLE_CTRL4 TAS2560_REG(0, 51, 44) /* B0_P0x33_R0x2c */ +#define TAS2560_BOOST_TABLE_CTRL5 TAS2560_REG(0, 51, 48) /* B0_P0x33_R0x30 */ +#define TAS2560_BOOST_TABLE_CTRL6 TAS2560_REG(0, 51, 52) /* B0_P0x33_R0x34 */ +#define TAS2560_BOOST_TABLE_CTRL7 TAS2560_REG(0, 51, 56) /* B0_P0x33_R0x38 */ +#define TAS2560_BOOST_TABLE_CTRL8 TAS2560_REG(0, 51, 60) /* B0_P0x33_R0x3c */ +#define TAS2560_THERMAL_FOLDBACK TAS2560_REG(0, 51, 100) /* B0_P0x33_R0x64 */ + +/* Book0, Page52 registers */ +#define TAS2560_VSENSE_DEL_CTL1 TAS2560_REG(0, 52, 52) /* B0_P0x34_R0x34 */ +#define TAS2560_VSENSE_DEL_CTL2 TAS2560_REG(0, 52, 56) /* B0_P0x34_R0x38 */ +#define TAS2560_VSENSE_DEL_CTL3 TAS2560_REG(0, 52, 60) /* B0_P0x34_R0x3c */ +#define TAS2560_VSENSE_DEL_CTL4 TAS2560_REG(0, 52, 64) /* B0_P0x34_R0x40 */ +#define TAS2560_VSENSE_DEL_CTL5 TAS2560_REG(0, 52, 68) /* B0_P0x34_R0x44 */ + +#define TAS2560_DATAFORMAT_SHIFT 2 +#define TAS2560_DATAFORMAT_I2S 0x0 +#define TAS2560_DATAFORMAT_DSP 0x1 +#define TAS2560_DATAFORMAT_RIGHT_J 0x2 +#define TAS2560_DATAFORMAT_LEFT_J 0x3 + +#define TAS2560_DAI_FMT_MASK (0x7 << TAS2560_DATAFORMAT_SHIFT) + +#define LOAD_MASK 0x18 +#define LOAD_8OHM (0) +#define LOAD_6OHM (1) +#define LOAD_4OHM (2) + +#define PLL_BCLK_1P536_48KSR (0) +#define PLL_BCLK_3P072_48KSR (1) + +#define CHECK_PERIOD 5000 /* 5 second */ + +#define ERROR_NONE 0x00000000 +#define ERROR_PLL_ABSENT 0x00000001 +#define ERROR_DEVA_I2C_COMM 0x00000002 +#define ERROR_DEVB_I2C_COMM 0x00000004 +#define ERROR_PRAM_CRCCHK 0x00000008 +#define ERROR_YRAM_CRCCHK 0x00000010 +#define ERROR_CLK_DET2 0x00000020 +#define ERROR_CLK_DET1 0x00000040 +#define ERROR_CLK_LOST 0x00000080 +#define ERROR_BROWNOUT 0x00000100 +#define ERROR_DIE_OVERTEMP 0x00000200 +#define ERROR_CLK_HALT 0x00000400 +#define ERROR_UNDER_VOLTAGE 0x00000800 +#define ERROR_OVER_CURRENT 0x00001000 +#define ERROR_CLASSD_PWR 0x00002000 +#define ERROR_FAILSAFE 0x40000000 + +struct tas2560_register { + int book; + int page; + int reg; +}; + +struct tas2560_dai_cfg { + unsigned int dai_fmt; + unsigned int tdm_delay; +}; + +struct tas2560_priv { + struct device *dev; + struct regmap *regmap; + struct mutex dev_lock; + struct delayed_work irq_work; + struct hrtimer mtimer; + int mnClkin; + int mnClkid; + bool mbPowerUp; + int mnCurrentBook; + int mnCurrentPage; + int mnLoad; + int mnASIFormat; + int mnResetGPIO; + int mnIRQGPIO; + int mnSwitchGPIO; + bool enablePmicEarPath; + int mnIRQ; + bool mbIRQEnable; + int mnSamplingRate; + int mnFrameSize; + int mnPLL; + int mnPPG; + + int (*read)(struct tas2560_priv *pTAS2560, unsigned int reg, + unsigned int *pValue); + int (*write)(struct tas2560_priv *pTAS2560, unsigned int reg, + unsigned int Value); + int (*bulk_read)(struct tas2560_priv *pTAS2560, unsigned int reg, + unsigned char *pData, unsigned int len); + int (*bulk_write)(struct tas2560_priv *pTAS2560, unsigned int reg, + unsigned char *pData, unsigned int len); + int (*update_bits)(struct tas2560_priv *pTAS2560, unsigned int reg, + unsigned int mask, unsigned int value); + void (*hw_reset)(struct tas2560_priv *pTAS2560); + void (*clearIRQ)(struct tas2560_priv *pTAS2560); + void (*enableIRQ)(struct tas2560_priv *pTAS2560, bool enable); + /* device is working, but system is suspended */ + int (*runtime_suspend)(struct tas2560_priv *pTAS2560); + int (*runtime_resume)(struct tas2560_priv *pTAS2560); + bool mbRuntimeSuspend; + + unsigned int mnErrCode; +#ifdef CONFIG_TAS2560_CODEC + struct mutex codec_lock; +#endif + +#ifdef CONFIG_TAS2560_MISC + int mnDBGCmd; + int mnCurrentReg; + struct mutex file_lock; +#endif +}; +#endif diff --git a/sound/soc/codecs/tas2562-codec.c b/sound/soc/codecs/tas2562-codec.c new file mode 100644 index 000000000000..e68924ec2035 --- /dev/null +++ b/sound/soc/codecs/tas2562-codec.c @@ -0,0 +1,1511 @@ +/* + * ============================================================================= + * Copyright (c) 2016 Texas Instruments 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; version 2. + * + * 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: + * tas2562-codec.c + * + * Description: + * ALSA SoC driver for Texas Instruments TAS2562 High Performance 4W Smart + * Amplifier + * + * ============================================================================= + */ + +#ifdef CONFIG_TAS2562_CODEC +//#define DEBUG 5 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tas2562.h" + +#ifdef CONFIG_TAS25XX_ALGO +#include +#include "tas25xx-calib.h" +#endif /*CONFIG_TAS25XX_ALGO*/ + +#define TAS2562_MDELAY 0xFFFFFFFE +#define TAS2562_MSLEEP 0xFFFFFFFD +#define TAS2562_IVSENSER_ENABLE 1 +#define TAS2562_IVSENSER_DISABLE 0 +#define MAX_STRING 200 +/* #define TAS2558_CODEC */ + +static char const *iv_enable_text[] = {"Off", "On"}; +static char const *brownout_enable_text[] = {"Off", "On"}; +static int tas2562iv_enable; +static int mb_mute; + +static const struct soc_enum tas2562_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(iv_enable_text), iv_enable_text), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(brownout_enable_text), brownout_enable_text), +}; +static int tas2562_set_fmt(struct tas2562_priv *p_tas2562, unsigned int fmt); + +static int tas2562_i2c_load_data(struct tas2562_priv *p_tas2562, + enum channel chn, unsigned int *p_data); +static int tas2562_system_mute_ctrl_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue); +static int tas2562_system_mute_ctrl_put(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue); +static int tas2562_mute_ctrl_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue); +static int tas2562_mute_ctrl_put(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue); + +static int tas2562_brownout_params_init(struct tas2562_priv *p_tas2562); + +static unsigned int p_tas2562_classh_d_data[] = { + /* reg address size values */ + TAS2562_CLASSHHEADROOM, 0x4, 0x09, 0x99, 0x99, 0x9a, + TAS2562_CLASSHHYSTERESIS, 0x4, 0x0, 0x0, 0x0, 0x0, + TAS2562_CLASSHMTCT, 0x4, 0xb, 0x0, 0x0, 0x0, + TAS2562_VBATFILTER, 0x1, 0x38, + TAS2562_CLASSHRELEASETIMER, 0x1, 0x3c, + TAS2562_BOOSTSLOPE, 0x1, 0x78, + TAS2562_TESTPAGECONFIGURATION, 0x1, 0xd, + TAS2562_CLASSDCONFIGURATION3, 0x1, 0x8e, + TAS2562_CLASSDCONFIGURATION2, 0x1, 0x49, + TAS2562_CLASSDCONFIGURATION4, 0x1, 0x21, + TAS2562_CLASSDCONFIGURATION1, 0x1, 0x80, + TAS2562_EFFICIENCYCONFIGURATION, 0x1, 0xc1, + 0xFFFFFFFF, 0xFFFFFFFF +}; + +static unsigned char digital_vol_ctl[] = { + 0x00, 0x00, 0x0D, 0x43, //-110dB + 0x00, 0x00, 0x0E, 0xE1, + 0x00, 0x00, 0x10, 0xB2, + 0x00, 0x00, 0x12, 0xBC, + 0x00, 0x00, 0x15, 0x05, + 0x00, 0x00, 0x17, 0x96, + 0x00, 0x00, 0x1A, 0x76, + 0x00, 0x00, 0x1D, 0xB1, + 0x00, 0x00, 0x21, 0x51, + 0x00, 0x00, 0x25, 0x61, + 0x00, 0x00, 0x29, 0xF1, + 0x00, 0x00, 0x2F, 0x0F, + 0x00, 0x00, 0x34, 0xCD, + 0x00, 0x00, 0x3B, 0x3F, + 0x00, 0x00, 0x42, 0x79, + 0x00, 0x00, 0x4A, 0x96, + 0x00, 0x00, 0x53, 0xAF, + 0x00, 0x00, 0x5D, 0xE6, + 0x00, 0x00, 0x69, 0x5B, + 0x00, 0x00, 0x76, 0x36, + 0x00, 0x00, 0x84, 0xA2, + 0x00, 0x00, 0x94, 0xD1, + 0x00, 0x00, 0xA6, 0xFA, + 0x00, 0x00, 0xBB, 0x5A, + 0x00, 0x00, 0xD2, 0x36, + 0x00, 0x00, 0xEB, 0xDC, + 0x00, 0x01, 0x08, 0xA4, + 0x00, 0x01, 0x28, 0xEF, + 0x00, 0x01, 0x4D, 0x2A, + 0x00, 0x01, 0x75, 0xD1, + 0x00, 0x01, 0xA3, 0x6E, + 0x00, 0x01, 0xD6, 0x9B, + 0x00, 0x02, 0x10, 0x08, + 0x00, 0x02, 0x50, 0x76, + 0x00, 0x02, 0x98, 0xC0, + 0x00, 0x02, 0xE9, 0xDD, + 0x00, 0x03, 0x44, 0xDF, + 0x00, 0x03, 0xAA, 0xFC, + 0x00, 0x04, 0x1D, 0x8F, + 0x00, 0x04, 0x9E, 0x1D, + 0x00, 0x05, 0x2E, 0x5A, + 0x00, 0x05, 0xD0, 0x31, + 0x00, 0x06, 0x85, 0xC8, + 0x00, 0x07, 0x51, 0x86, + 0x00, 0x08, 0x36, 0x21, + 0x00, 0x09, 0x36, 0xA1, + 0x00, 0x0A, 0x56, 0x6D, + 0x00, 0x0B, 0x99, 0x56, + 0x00, 0x0D, 0x03, 0xA7, + 0x00, 0x0E, 0x9A, 0x2D, + 0x00, 0x10, 0x62, 0x4D, + 0x00, 0x12, 0x62, 0x16, + 0x00, 0x14, 0xA0, 0x50, + 0x00, 0x17, 0x24, 0x9C, + 0x00, 0x19, 0xF7, 0x86, + 0x00, 0x1D, 0x22, 0xA4, + 0x00, 0x20, 0xB0, 0xBC, + 0x00, 0x24, 0xAD, 0xE0, + 0x00, 0x29, 0x27, 0x9D, + 0x00, 0x2E, 0x2D, 0x27, + 0x00, 0x33, 0xCF, 0x8D, + 0x00, 0x3A, 0x21, 0xF3, + 0x00, 0x41, 0x39, 0xD3, + 0x00, 0x49, 0x2F, 0x44, + 0x00, 0x52, 0x1D, 0x50, + 0x00, 0x5C, 0x22, 0x4E, + 0x00, 0x67, 0x60, 0x44, + 0x00, 0x73, 0xFD, 0x65, + 0x00, 0x82, 0x24, 0x8A, + 0x00, 0x92, 0x05, 0xC6, + 0x00, 0xA3, 0xD7, 0x0A, + 0x00, 0xB7, 0xD4, 0xDD, + 0x00, 0xCE, 0x43, 0x28, + 0x00, 0xE7, 0x6E, 0x1E, + 0x01, 0x03, 0xAB, 0x3D, + 0x01, 0x23, 0x5A, 0x71, + 0x01, 0x46, 0xE7, 0x5D, + 0x01, 0x6E, 0xCA, 0xC5, + 0x01, 0x9B, 0x8C, 0x27, + 0x01, 0xCD, 0xC3, 0x8C, + 0x02, 0x06, 0x1B, 0x89, + 0x02, 0x45, 0x53, 0x85, + 0x02, 0x8C, 0x42, 0x3F, + 0x02, 0xDB, 0xD8, 0xAD, + 0x03, 0x35, 0x25, 0x29, + 0x03, 0x99, 0x57, 0x0C, + 0x04, 0x09, 0xC2, 0xB0, + 0x04, 0x87, 0xE5, 0xFB, + 0x05, 0x15, 0x6D, 0x68, + 0x05, 0xB4, 0x39, 0xBC, + 0x06, 0x66, 0x66, 0x66, + 0x07, 0x2E, 0x50, 0xA6, + 0x08, 0x0E, 0x9F, 0x96, + 0x09, 0x0A, 0x4D, 0x2F, + 0x0A, 0x24, 0xB0, 0x62, + 0x0B, 0x61, 0x88, 0x71, + 0x0C, 0xC5, 0x09, 0xAB, + 0x0E, 0x53, 0xEB, 0xB3, + 0x10, 0x13, 0x79, 0x87, + 0x12, 0x09, 0xA3, 0x7A, + 0x14, 0x3D, 0x13, 0x62, //-10dB + 0x16, 0xB5, 0x43, 0x37, + 0x19, 0x7A, 0x96, 0x7F, + 0x1C, 0x96, 0x76, 0xC6, + 0x20, 0x13, 0x73, 0x9E, + 0x23, 0xFD, 0x66, 0x78, //-5dB + 0x28, 0x61, 0x9A, 0xE9, + 0x2D, 0x4E, 0xFB, 0xD5, + 0x32, 0xD6, 0x46, 0x17, + 0x39, 0x0A, 0x41, 0x5F, + 0x40, 0x00, 0x00, 0x00 //0 +}; + +static unsigned int tas2562_codec_read(struct snd_soc_codec *codec, + unsigned int reg) +{ + unsigned int value = 0; + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + p_tas2562->read (p_tas2562, channel_left, reg, &value); + dev_dbg(p_tas2562->dev, "%s, reg=%d, value=%d", __func__, reg, value); + + return value; +} + +static int tas2562_iv_enable(struct tas2562_priv *p_tas2562, int enable) +{ + int n_result; + + if (enable) { + pr_debug("%s:tas2562iv_enable\n", __func__); + n_result = p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_POWERCONTROL, + TAS2562_POWERCONTROL_ISNSPOWER_MASK | + TAS2562_POWERCONTROL_VSNSPOWER_MASK, + TAS2562_POWERCONTROL_VSNSPOWER_ACTIVE | + TAS2562_POWERCONTROL_ISNSPOWER_ACTIVE); + } else { + pr_debug("%s:tas2562iv_disable\n", __func__); + n_result = p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_POWERCONTROL, + TAS2562_POWERCONTROL_ISNSPOWER_MASK | + TAS2562_POWERCONTROL_VSNSPOWER_MASK, + TAS2562_POWERCONTROL_VSNSPOWER_POWEREDDOWN | + TAS2562_POWERCONTROL_ISNSPOWER_POWEREDDOWN); + } + tas2562iv_enable = enable; + + return n_result; +} + +static int tas2562iv_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tas2562_priv *p_tas2562 = NULL; + int iv_enable = 0, n_result = 0; + + if (codec == NULL) { + pr_err("%s:codec is NULL\n", __func__); + return 0; + } + + p_tas2562 = snd_soc_codec_get_drvdata(codec); + if (p_tas2562 == NULL) { + pr_err("%s:p_tas2562 is NULL\n", __func__); + return 0; + } + + iv_enable = ucontrol->value.integer.value[0]; + + n_result = tas2562_iv_enable(p_tas2562, iv_enable); + + pr_debug("%s: tas2562iv_enable = %d\n", __func__, tas2562iv_enable); + + return n_result; +} + +static int tas2562iv_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tas2562_priv *p_tas2562 = NULL; + int ret, value; + + if (codec == NULL) { + pr_err("%s:codec is NULL\n", __func__); + return 0; + } + + p_tas2562 = snd_soc_codec_get_drvdata(codec); + if (p_tas2562 == NULL) { + pr_err("%s:p_tas2562 is NULL\n", __func__); + return 0; + } + + ret = p_tas2562->read(p_tas2562, channel_left, + TAS2562_POWERCONTROL, &value); + if (ret < 0) + dev_err(p_tas2562->dev, "can't get ivsensor state %s, L=%d\n", + __func__, __LINE__); + else if (((value & TAS2562_POWERCONTROL_ISNSPOWER_MASK) + == TAS2562_POWERCONTROL_ISNSPOWER_ACTIVE) + && ((value & TAS2562_POWERCONTROL_VSNSPOWER_MASK) + == TAS2562_POWERCONTROL_VSNSPOWER_ACTIVE)) { + ucontrol->value.integer.value[0] = TAS2562_IVSENSER_ENABLE; + } else { + ucontrol->value.integer.value[0] = TAS2562_IVSENSER_DISABLE; + } + + tas2562iv_enable = ucontrol->value.integer.value[0]; + dev_err(p_tas2562->dev, "value: 0x%x, tas2562iv_enable %d\n", + value, tas2562iv_enable); + + return 0; +} + +int tas2562_brownout_enable(struct tas2562_priv *p_tas2562, bool enable) +{ + int ret = 0; + dev_info(p_tas2562->dev, "%s, enable = %d\n", __func__, enable); + /* Brownout Protection Init */ + if (enable) { + ret = p_tas2562->write(p_tas2562, channel_both, + TAS2562_LIMITERCONFIGURATIONREG0, + 0x51); + if (ret < 0) + return ret; + tas2562_brownout_params_init(p_tas2562); + p_tas2562->tas2562brownout_enable = true; + } else { + ret = p_tas2562->write(p_tas2562, channel_both, + TAS2562_LIMITERCONFIGURATIONREG0, + 0x12); + if (ret < 0) + return ret; + p_tas2562->tas2562brownout_enable = false; + } + + return ret; +} + +static int tas2562_brownout_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tas2562_priv *p_tas2562 = NULL; + int brownout_enable = 0, n_result = 0; + + if (codec == NULL) { + pr_err("%s:codec is NULL\n", __func__); + return 0; + } + + p_tas2562 = snd_soc_codec_get_drvdata(codec); + if (p_tas2562 == NULL) { + pr_err("%s:p_tas2562 is NULL\n", __func__); + return 0; + } + + brownout_enable = ucontrol->value.integer.value[0]; + + n_result = tas2562_brownout_enable(p_tas2562, brownout_enable); + + pr_debug("%s: brownout_enable = %d\n", __func__, brownout_enable); + + return n_result; +} + +static int tas2562_brownout_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tas2562_priv *p_tas2562 = NULL; + + if (codec == NULL) { + pr_err("%s:codec is NULL\n", __func__); + return 0; + } + + p_tas2562 = snd_soc_codec_get_drvdata(codec); + if (p_tas2562 == NULL) { + pr_err("%s:p_tas2562 is NULL\n", __func__); + return 0; + } + + ucontrol->value.integer.value[0] = p_tas2562->tas2562brownout_enable; + + dev_err(p_tas2562->dev, "brownout enable %d\n", p_tas2562->tas2562brownout_enable); + + return 0; +} + +static int tas2562_icn_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tas2562_priv *p_tas2562 = NULL; + + if (codec == NULL) { + pr_err("%s:codec is NULL\n", __func__); + return 0; + } + + p_tas2562 = snd_soc_codec_get_drvdata(codec); + if (p_tas2562 == NULL) { + pr_err("%s:p_tas2562 is NULL\n", __func__); + return 0; + } + + if (strnstr(ucontrol->id.name, "RIGHT", MAX_STRING)) { + ucontrol->value.integer.value[0] = p_tas2562->icn_enable_right; + } else { /*Assumed Left*/ + ucontrol->value.integer.value[0] = p_tas2562->icn_enable_left; + } + + dev_err(p_tas2562->dev, "icn enable %d\n", (int)ucontrol->value.integer.value[0]); + + return 0; +} + +static int tas2562_icn_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tas2562_priv *p_tas2562 = NULL; + + if (codec == NULL) { + pr_err("%s:codec is NULL\n", __func__); + return 0; + } + + p_tas2562 = snd_soc_codec_get_drvdata(codec); + if (p_tas2562 == NULL) { + pr_err("%s:p_tas2562 is NULL\n", __func__); + return 0; + } + + if (strnstr(ucontrol->id.name, "RIGHT", MAX_STRING)) { + p_tas2562->icn_enable_right = ucontrol->value.integer.value[0]; + } else { /*Assumed Left*/ + p_tas2562->icn_enable_left = ucontrol->value.integer.value[0]; + } + + pr_debug("%s: icn_enable = %d\n", __func__, (int)ucontrol->value.integer.value[0]); + + return 0; +} + +static const struct snd_kcontrol_new tas2562_controls[] = { +SOC_ENUM_EXT("TAS2562 IVSENSE ENABLE", tas2562_enum[0], + tas2562iv_get, tas2562iv_put), +SOC_ENUM_EXT("TAS2562 BROWNOUT PROTECT ENABLE", tas2562_enum[1], + tas2562_brownout_get, tas2562_brownout_put), +SOC_ENUM_EXT("TAS2562 ICN ENABLE LEFT", tas2562_enum[1], + tas2562_icn_get, tas2562_icn_put), +SOC_ENUM_EXT("TAS2562 ICN ENABLE RIGHT", tas2562_enum[1], + tas2562_icn_get, tas2562_icn_put), +}; + +static int tas2562_codec_write(struct snd_soc_codec *codec, unsigned int reg, + unsigned int value) +{ + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(p_tas2562->dev, "%s: %d, %d", __func__, reg, value); + p_tas2562->write (p_tas2562, channel_both, reg, value); + + return 0; +} + +static int tas2562_i2c_load_data(struct tas2562_priv *p_tas2562, + enum channel chn, + unsigned int *p_data) +{ + unsigned int n_register; + unsigned int *n_data; + unsigned char buf[128]; + unsigned int n_length = 0; + unsigned int i = 0; + unsigned int n_size = 0; + int n_result = 0; + + do { + n_register = p_data[n_length]; + n_size = p_data[n_length + 1]; + n_data = &p_data[n_length + 2]; + if (n_register == TAS2562_MSLEEP) { + msleep(n_data[0]); + dev_dbg(p_tas2562->dev, "%s, msleep = %d\n", + __func__, n_data[0]); + } else if (n_register == TAS2562_MDELAY) { + msleep(n_data[0]); + dev_dbg(p_tas2562->dev, "%s, mdelay = %d\n", + __func__, n_data[0]); + } else { + if (n_register != 0xFFFFFFFF) { + if (n_size > 128) { + dev_err(p_tas2562->dev, + "%s, Line=%d, invalid size, maximum is 128 bytes!\n", + __func__, __LINE__); + break; + } + if (n_size > 1) { + for (i = 0; i < n_size; i++) + buf[i] = (unsigned char)n_data[i]; + n_result = p_tas2562->bulk_write(p_tas2562, chn, + n_register, buf, n_size); + if (n_result < 0) + break; + } else if (n_size == 1) { + n_result = p_tas2562->write(p_tas2562, + chn, + n_register, n_data[0]); + if (n_result < 0) + break; + } else { + dev_err(p_tas2562->dev, + "%s, Line=%d,invalid size, minimum is 1 bytes!\n", + __func__, __LINE__); + } + } + } + n_length = n_length + 2 + p_data[n_length + 1]; + } while (n_register != 0xFFFFFFFF); + return n_result; +} + +#ifdef CODEC_PM +static int tas2562_codec_suspend(struct snd_soc_codec *codec) +{ + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + mutex_lock(&p_tas2562->codec_lock); + + dev_dbg(p_tas2562->dev, "%s\n", __func__); + p_tas2562->runtime_suspend(p_tas2562); + + mutex_unlock(&p_tas2562->codec_lock); + return ret; +} + +static int tas2562_codec_resume(struct snd_soc_codec *codec) +{ + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + mutex_lock(&p_tas2562->codec_lock); + + dev_dbg(p_tas2562->dev, "%s\n", __func__); + p_tas2562->runtime_resume(p_tas2562); + + mutex_unlock(&p_tas2562->codec_lock); + return ret; +} +#endif + +static int tas2562_set_power_state(struct tas2562_priv *p_tas2562, + enum channel chn, int state) +{ + int n_result = 0; + int irqreg; + /*unsigned int n_value;*/ + + if ((p_tas2562->mb_mute) && (state == TAS2562_POWER_ACTIVE)) + state = TAS2562_POWER_MUTE; + dev_err(p_tas2562->dev, "set power state: %d\n", state); + + switch (state) { + case TAS2562_POWER_ACTIVE: + /* if set format was not called by asoc, then set it default */ + if (p_tas2562->mn_asi_format == 0) + p_tas2562->mn_asi_format = SND_SOC_DAIFMT_CBS_CFS + | SND_SOC_DAIFMT_IB_NF + | SND_SOC_DAIFMT_I2S; + n_result = tas2562_set_fmt(p_tas2562, p_tas2562->mn_asi_format); + if (n_result < 0) + return n_result; + +/* Clear latched IRQ before power on */ + + p_tas2562->update_bits(p_tas2562, chn, + TAS2562_INTERRUPTCONFIGURATION, + TAS2562_INTERRUPTCONFIGURATION_LTCHINTCLEAR_MASK, + TAS2562_INTERRUPTCONFIGURATION_LTCHINTCLEAR); + + p_tas2562->read(p_tas2562, channel_left, + TAS2562_LATCHEDINTERRUPTREG0, &irqreg); + dev_info(p_tas2562->dev, "IRQ reg is: %s %d, %d\n", + __func__, irqreg, __LINE__); + p_tas2562->read(p_tas2562, channel_right, + TAS2562_LATCHEDINTERRUPTREG0, &irqreg); + dev_info(p_tas2562->dev, "IRQ reg is: %s %d, %d\n", + __func__, irqreg, __LINE__); + + + p_tas2562->mb_power_up = true; + p_tas2562->mn_power_state = TAS2562_POWER_ACTIVE; + schedule_delayed_work(&p_tas2562->irq_work, + msecs_to_jiffies(10)); + break; + + case TAS2562_POWER_MUTE: + n_result = p_tas2562->update_bits(p_tas2562, chn, + TAS2562_POWERCONTROL, + TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK | + TAS2562_POWERCONTROL_ISNSPOWER_MASK | + TAS2562_POWERCONTROL_VSNSPOWER_MASK, + TAS2562_POWERCONTROL_OPERATIONALMODE10_MUTE | + TAS2562_POWERCONTROL_VSNSPOWER_ACTIVE | + TAS2562_POWERCONTROL_ISNSPOWER_ACTIVE); + p_tas2562->mb_power_up = true; + p_tas2562->mn_power_state = TAS2562_POWER_MUTE; + break; + + case TAS2562_POWER_SHUTDOWN: + // Disable Comparator Hystersis befor Power Down. + n_result = p_tas2562->write(p_tas2562, chn, + TAS2562_REG(0x0, 0x01, 0x21), 0x0); + + n_result = p_tas2562->update_bits(p_tas2562, chn, + TAS2562_POWERCONTROL, + TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK, + TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN); + p_tas2562->mb_power_up = false; + p_tas2562->mn_power_state = TAS2562_POWER_SHUTDOWN; + msleep(20); + p_tas2562->enable_irq(p_tas2562, false); + + break; + + default: + dev_err(p_tas2562->dev, "wrong power state setting %d\n", + state); + } + + n_result = tas2562_brownout_enable(p_tas2562, p_tas2562->tas2562brownout_enable); + + return n_result; +} + + +static int tas2562_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + dev_info(p_tas2562->dev, "SND_SOC_DAPM_POST_PMU\n"); + break; + case SND_SOC_DAPM_PRE_PMD: + dev_info(p_tas2562->dev, "SND_SOC_DAPM_PRE_PMD\n"); + break; + + } + return 0; + +} + +static const struct snd_soc_dapm_widget tas2562_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2562_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("OUT") +}; + +static const struct snd_soc_dapm_route tas2562_audio_map[] = { + {"DAC", NULL, "ASI1"}, + {"OUT", NULL, "DAC"} +}; + +static int tas2562_get_left_speaker_switch(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *p_u_control) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + dev_info(p_tas2562->dev, "%s, p_u_control = %ld\n", + __func__, p_u_control->value.integer.value[0]); + p_u_control->value.integer.value[0] = p_tas2562->spk_l_control; + return 0; +} + +static int tas2562_set_left_speaker_switch(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *p_u_control) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + dev_info(p_tas2562->dev, "%s, spk_l_control = %d\n", + __func__, p_tas2562->spk_l_control); + p_tas2562->spk_l_control = p_u_control->value.integer.value[0]; + return 0; +} + +static int tas2562_get_right_speaker_switch(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *p_u_control) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + dev_info(p_tas2562->dev, "%s, p_u_control = %ld\n", + __func__, p_u_control->value.integer.value[0]); + p_u_control->value.integer.value[0] = p_tas2562->spk_r_control; + return 0; +} + +static int tas2562_set_right_speaker_switch(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *p_u_control) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + dev_info(p_tas2562->dev, "%s, spk_r_control = %d\n", + __func__, p_tas2562->spk_r_control); + p_tas2562->spk_r_control = p_u_control->value.integer.value[0]; + return 0; +} + +static int tas2562_get_digital_volume(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *p_u_control) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + dev_info(p_tas2562->dev, "%s, spk_digital_vol = %ld\n", + __func__, p_u_control->value.integer.value[0]); + p_u_control->value.integer.value[0] = p_tas2562->spk_digital_vol; + + return 0; +} + +static int tas2562_set_digital_volume(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *p_u_control) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + unsigned char *vol = digital_vol_ctl; + int ret = -1; + + dev_info(p_tas2562->dev, "%s, spk_digital_vol = %d\n", + __func__, p_tas2562->spk_digital_vol); + p_tas2562->spk_digital_vol = p_u_control->value.integer.value[0]; + + vol += p_tas2562->spk_digital_vol * 4; + ret = p_tas2562->bulk_write(p_tas2562, channel_left, TAS2562_DVC_REG, vol, 4); + + return ret; +} + +static int tas2562_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + enum channel chn; + + dev_dbg(p_tas2562->dev, "%s,%d\n", __func__, mute); + mutex_lock(&p_tas2562->codec_lock); + + if ((p_tas2562->spk_l_control == 1) + && (p_tas2562->spk_r_control == 1) + && (p_tas2562->mn_channels == 2)) + chn = channel_both; + else if (p_tas2562->spk_l_control == 1) + chn = channel_left; + else if ((p_tas2562->spk_r_control == 1) + && (p_tas2562->mn_channels == 2)) + chn = channel_right; + else + chn = channel_left; + + if (mute) { + tas2562_set_power_state(p_tas2562, + channel_both, TAS2562_POWER_SHUTDOWN); + } else { + tas2562_set_power_state(p_tas2562, chn, TAS2562_POWER_ACTIVE); + } + mutex_unlock(&p_tas2562->codec_lock); + return 0; +} + +static int tas2562_iv_slot_config(struct tas2562_priv *p_tas2562) +{ + int ret = 0; + + dev_info(p_tas2562->dev, "%s, %d\n", __func__, + p_tas2562->mn_slot_width); + + if (p_tas2562->mn_channels == 2) { + p_tas2562->update_bits(p_tas2562, channel_left, + TAS2562_TDMCONFIGURATIONREG5, 0xff, 0x42); + + p_tas2562->update_bits(p_tas2562, channel_left, + TAS2562_TDMCONFIGURATIONREG6, 0xff, 0x40); + + p_tas2562->update_bits(p_tas2562, channel_right, + TAS2562_TDMCONFIGURATIONREG5, 0xff, 0x46); + + p_tas2562->update_bits(p_tas2562, channel_right, + TAS2562_TDMCONFIGURATIONREG6, 0xff, 0x44); + } else if ((p_tas2562->mn_channels == 1) + && (p_tas2562->mn_slot_width == 32)) { + p_tas2562->update_bits(p_tas2562, channel_left, + TAS2562_TDMCONFIGURATIONREG5, 0xff, 0x44); + + p_tas2562->update_bits(p_tas2562, channel_left, + TAS2562_TDMCONFIGURATIONREG6, 0xff, 0x40); + } else if ((p_tas2562->mn_channels == 1) + && (p_tas2562->mn_slot_width == 16)) { + p_tas2562->update_bits(p_tas2562, channel_left, + TAS2562_TDMCONFIGURATIONREG5, 0xff, 0x42); + + p_tas2562->update_bits(p_tas2562, channel_left, + TAS2562_TDMCONFIGURATIONREG6, 0xff, 0x40); + } else { + dev_err(p_tas2562->dev, "%s, wrong params, %d\n", + __func__, p_tas2562->mn_slot_width); + } + + return ret; +} + +static int tas2562_set_slot(struct tas2562_priv *p_tas2562, int slot_width) +{ + int ret = 0; + + dev_info(p_tas2562->dev, "%s: slot_width = %d\n", __func__, slot_width); + switch (slot_width) { + case 16: + ret = p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG2, + TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_MASK, + TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_16BITS); + break; + + case 24: + ret = p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG2, + TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_MASK, + TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_24BITS); + break; + + case 32: + ret = p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG2, + TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_MASK, + TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_32BITS); + break; + + case 0: + /* Do not change slot width */ + break; + + default: + dev_err(p_tas2562->dev, "slot width not supported"); + ret = -EINVAL; + } + + if (ret >= 0) + p_tas2562->mn_slot_width = slot_width; + + return ret; +} + +static int tas2562_set_bitwidth(struct tas2562_priv *p_tas2562, int bitwidth) +{ + int slot_width_tmp = 16; + + dev_info(p_tas2562->dev, "%s %d\n", __func__, bitwidth); + switch (bitwidth) { + case SNDRV_PCM_FORMAT_S16_LE: + p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG2, + TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_MASK, + TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_16BITS); + p_tas2562->mn_ch_size = 16; + slot_width_tmp = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG2, + TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_MASK, + TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_24BITS); + p_tas2562->mn_ch_size = 24; + slot_width_tmp = 32; + break; + case SNDRV_PCM_FORMAT_S32_LE: + p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG2, + TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_MASK, + TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_32BITS); + p_tas2562->mn_ch_size = 32; + slot_width_tmp = 32; + break; + + default: + dev_info(p_tas2562->dev, "Not supported params format\n"); + } + + tas2562_set_slot(p_tas2562, slot_width_tmp); + + if (p_tas2562->mn_channels == 1) { + p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG2, + TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_MASK, + TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_STEREO_DOWNMIX); + } else { + p_tas2562->update_bits(p_tas2562, channel_left, + TAS2562_TDMCONFIGURATIONREG2, + TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_MASK, + TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_MONO_LEFT); + p_tas2562->update_bits(p_tas2562, channel_right, + TAS2562_TDMCONFIGURATIONREG2, + TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_MASK, + TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_MONO_RIGHT); + } + + tas2562_iv_slot_config(p_tas2562); + + dev_info(p_tas2562->dev, "mn_ch_size: %d\n", p_tas2562->mn_ch_size); + p_tas2562->mn_pcm_format = bitwidth; + + return 0; +} + +static int tas2562_set_samplerate(struct tas2562_priv *p_tas2562, + int samplerate) +{ + switch (samplerate) { + case 48000: + p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG0, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_MASK, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_48KHZ); + p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG0, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_MASK, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_44_1_48KHZ); + break; + case 44100: + p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG0, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_MASK, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_44_1KHZ); + p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG0, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_MASK, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_44_1_48KHZ); + break; + case 96000: + p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG0, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_MASK, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_48KHZ); + p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG0, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_MASK, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_88_2_96KHZ); + break; + case 88200: + p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG0, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_MASK, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_44_1KHZ); + p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG0, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_MASK, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_88_2_96KHZ); + break; + case 19200: + p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG0, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_MASK, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_48KHZ); + p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG0, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_MASK, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_176_4_192KHZ); + break; + case 17640: + p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG0, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_MASK, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_44_1KHZ); + p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG0, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_MASK, + TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_176_4_192KHZ); + break; + default: + dev_info(p_tas2562->dev, "%s, unsupported sample rate, %d\n", + __func__, samplerate); + } + + p_tas2562->mn_sampling_rate = samplerate; + return 0; +} + +static int tas2562_system_mute_ctrl_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + pValue->value.integer.value[0] = p_tas2562->mb_mute; + dev_dbg(p_tas2562->dev, "%s = %d\n", + __func__, p_tas2562->mb_mute); + + return 0; +} + +static int tas2562_system_mute_ctrl_put(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + mb_mute = pValue->value.integer.value[0]; + + dev_dbg(p_tas2562->dev, "%s = %d\n", __func__, mb_mute); + + p_tas2562->mb_mute = !!mb_mute; + + return 0; +} + +static int tas2562_mute_ctrl_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + pValue->value.integer.value[0] = p_tas2562->mb_mute; + + if((p_tas2562->mb_power_up == true)&& + (p_tas2562->mn_power_state == TAS2562_POWER_ACTIVE)) + pValue->value.integer.value[0] = 0; + else + pValue->value.integer.value[0] = 1; + + dev_dbg(p_tas2562->dev, "%s = %ld\n", + __func__, pValue->value.integer.value[0]); + + return 0; +} + +static int tas2562_mute_ctrl_put(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pValue) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(pKcontrol); + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + enum channel chn; + int mute = pValue->value.integer.value[0]; + + dev_dbg(p_tas2562->dev, "%s, %d\n", __func__, mute); + mutex_lock(&p_tas2562->codec_lock); + + if ((p_tas2562->spk_l_control == 1) + && (p_tas2562->spk_r_control == 1) + && (p_tas2562->mn_channels == 2)) + chn = channel_both; + else if (p_tas2562->spk_l_control == 1) + chn = channel_left; + else if ((p_tas2562->spk_r_control == 1) + && (p_tas2562->mn_channels == 2)) + chn = channel_right; + else + chn = channel_left; + + if (mute) { + tas2562_set_power_state(p_tas2562, + chn, TAS2562_POWER_MUTE); + } else { + tas2562_set_power_state(p_tas2562, chn, TAS2562_POWER_ACTIVE); + } + mutex_unlock(&p_tas2562->codec_lock); + return 0; +} + +static int tas2562_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + /* int blr_clk_ratio; */ + int n_result = 0; + + dev_dbg(p_tas2562->dev, "%s, format: %d\n", __func__, + params_format(params)); + + mutex_lock(&p_tas2562->codec_lock); + + n_result = tas2562_set_bitwidth(p_tas2562, + params_format(params)); + if (n_result < 0) { + dev_info(p_tas2562->dev, "set bitwidth failed, %d\n", n_result); + goto ret; + } + + dev_info(p_tas2562->dev, "%s, sample rate: %d\n", __func__, + params_rate(params)); + + n_result = tas2562_set_samplerate(p_tas2562, + params_rate(params)); + +ret: + mutex_unlock(&p_tas2562->codec_lock); + return n_result; +} + +static int tas2562_set_fmt(struct tas2562_priv *p_tas2562, unsigned int fmt) +{ + u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0; + int ret = 0; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + asi_cfg_1 = 0x00; + break; + default: + dev_err(p_tas2562->dev, "ASI format master is not found\n"); + ret = -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + dev_info(p_tas2562->dev, "INV format: NBNF\n"); + asi_cfg_1 |= TAS2562_TDMCONFIGURATIONREG1_RXEDGE_RISING; + break; + case SND_SOC_DAIFMT_IB_NF: + dev_info(p_tas2562->dev, "INV format: IBNF\n"); + asi_cfg_1 |= TAS2562_TDMCONFIGURATIONREG1_RXEDGE_FALLING; + break; + default: + dev_err(p_tas2562->dev, "ASI format Inverse is not found\n"); + ret = -EINVAL; + } + + p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_TDMCONFIGURATIONREG1, + TAS2562_TDMCONFIGURATIONREG1_RXEDGE_MASK, + asi_cfg_1); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case (SND_SOC_DAIFMT_I2S): + tdm_rx_start_slot = 1; + break; + case (SND_SOC_DAIFMT_DSP_A): + case (SND_SOC_DAIFMT_DSP_B): + tdm_rx_start_slot = 1; + break; + case (SND_SOC_DAIFMT_LEFT_J): + tdm_rx_start_slot = 0; + break; + default: + dev_err(p_tas2562->dev, "DAI Format is not found, fmt=0x%x\n", fmt); + ret = -EINVAL; + break; + } + + p_tas2562->update_bits(p_tas2562, channel_left, + TAS2562_TDMCONFIGURATIONREG1, + TAS2562_TDMCONFIGURATIONREG1_RXOFFSET51_MASK, + (tdm_rx_start_slot << + TAS2562_TDMCONFIGURATIONREG1_RXOFFSET51_SHIFT)); + + if (p_tas2562->mn_channels == 2) { + p_tas2562->update_bits(p_tas2562, channel_right, + TAS2562_TDMCONFIGURATIONREG1, + TAS2562_TDMCONFIGURATIONREG1_RXOFFSET51_MASK, + ((tdm_rx_start_slot + p_tas2562->mn_slot_width) << + TAS2562_TDMCONFIGURATIONREG1_RXOFFSET51_SHIFT)); + } + + p_tas2562->mn_asi_format = fmt; + + return 0; +} + +static int tas2562_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_codec *codec = dai->codec; + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + int ret = 0; + + dev_dbg(p_tas2562->dev, "%s, format=0x%x\n", __func__, fmt); + + ret = tas2562_set_fmt(p_tas2562, fmt); + return ret; +} + +static int tas2562_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + int ret = 0; + struct snd_soc_codec *codec = dai->codec; + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + dev_dbg(p_tas2562->dev, "%s, tx_mask:%d, rx_mask:%d, slots:%d, slot_width:%d", + __func__, tx_mask, rx_mask, slots, slot_width); + + ret = tas2562_set_slot(p_tas2562, slot_width); + + return ret; +} + +static struct snd_soc_dai_ops tas2562_dai_ops = { + .digital_mute = tas2562_mute, + .hw_params = tas2562_hw_params, + .set_fmt = tas2562_set_dai_fmt, + .set_tdm_slot = tas2562_set_dai_tdm_slot, +}; + +#define TAS2562_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define TAS2562_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 \ + SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_96000 |\ + SNDRV_PCM_RATE_176400 |\ + SNDRV_PCM_RATE_192000\ + ) + +static struct snd_soc_dai_driver tas2562_dai_driver[] = { + { + .name = "tas2562 ASI1", + .id = 0, + .playback = { + .stream_name = "ASI1 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = TAS2562_FORMATS, + }, + .capture = { + .stream_name = "ASI1 Capture", + .channels_min = 0, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = TAS2562_FORMATS, + }, + .ops = &tas2562_dai_ops, + .symmetric_rates = 1, + }, +}; + +static int tas2562_brownout_params_init(struct tas2562_priv *p_tas2562) +{ + int ret = 0; + + dev_info(p_tas2562->dev, "%s, lim_th_min = %d, lim_th_max = %d!\n", + __func__, p_tas2562->lim_th_min, p_tas2562->lim_th_max); + + if (p_tas2562->lim_th_min <= 0 || + p_tas2562->lim_th_min > p_tas2562->lim_th_max) { + dev_info(p_tas2562->dev, "%s: use default config!\n", __func__); + p_tas2562->lim_th_min = 4; + } + if (p_tas2562->lim_th_max <= 0 || + p_tas2562->lim_th_min > p_tas2562->lim_th_max) { + dev_info(p_tas2562->dev, "%s: use default config!\n", __func__); + p_tas2562->lim_th_max = 9; + } + + /* LIMB_TH_MAX config*/ + ret = p_tas2562->write(p_tas2562, channel_both, + TAS2562_BROWNOUT_LIMB_TH_MIN0, + TAS2562_BROWNOUT_VBAT_CONVERT(p_tas2562->lim_th_min, 0xFF000000, 24)); + if (ret < 0) + return ret; + + ret = p_tas2562->write(p_tas2562, channel_both, + TAS2562_BROWNOUT_LIMB_TH_MIN1, + TAS2562_BROWNOUT_VBAT_CONVERT(p_tas2562->lim_th_min, 0x00FF0000, 16)); + if (ret < 0) + return ret; + + ret = p_tas2562->write(p_tas2562, channel_both, + TAS2562_BROWNOUT_LIMB_TH_MIN2, + TAS2562_BROWNOUT_VBAT_CONVERT(p_tas2562->lim_th_min, 0x0000FF00, 8)); + if (ret < 0) + return ret; + + ret = p_tas2562->write(p_tas2562, channel_both, + TAS2562_BROWNOUT_LIMB_TH_MIN3, + TAS2562_BROWNOUT_VBAT_CONVERT(p_tas2562->lim_th_min, 0x000000FF, 0)); + if (ret < 0) + return ret; + + /* LIMB TH MAX CONFIG */ + ret = p_tas2562->write(p_tas2562, channel_both, + TAS2562_BROWNOUT_LIMB_TH_MAX0, + TAS2562_BROWNOUT_VBAT_CONVERT(p_tas2562->lim_th_max, 0xFF000000, 24)); + if (ret < 0) + return ret; + + ret = p_tas2562->write(p_tas2562, channel_both, + TAS2562_BROWNOUT_LIMB_TH_MIN1, + TAS2562_BROWNOUT_VBAT_CONVERT(p_tas2562->lim_th_max, 0x00FF0000, 16)); + if (ret < 0) + return ret; + + ret = p_tas2562->write(p_tas2562, channel_both, + TAS2562_BROWNOUT_LIMB_TH_MIN2, + TAS2562_BROWNOUT_VBAT_CONVERT(p_tas2562->lim_th_max, 0x0000FF00, 8)); + if (ret < 0) + return ret; + + ret = p_tas2562->write(p_tas2562, channel_both, + TAS2562_BROWNOUT_LIMB_TH_MIN3, + TAS2562_BROWNOUT_VBAT_CONVERT(p_tas2562->lim_th_max, 0x000000FF, 0)); + if (ret < 0) + return ret; + + return 0; +} + +static int tas2562_load_init(struct tas2562_priv *p_tas2562) +{ + int ret; + +#ifdef TAS2558_CODEC +/* Max voltage to 9V */ + ret = p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_BOOSTCONFIGURATION2, + TAS2562_BOOSTCONFIGURATION2_BOOSTMAXVOLTAGE_MASK, + 0x7); + ret = p_tas2562->update_bits(p_tas2562, channel_both, + TAS2562_PLAYBACKCONFIGURATIONREG0, + TAS2562_PLAYBACKCONFIGURATIONREG0_AMPLIFIERLEVEL51_MASK, + 0xd << 1); + if (ret < 0) + return ret; +#endif + + ret = p_tas2562->write(p_tas2562, channel_both, + TAS2562_MISCCONFIGURATIONREG0, 0xc7); + if (ret < 0) + return ret; + if(p_tas2562->mn_channels == 2) { + ret = p_tas2562->write(p_tas2562, channel_both, + TAS2562_TDMConfigurationReg4, 0x11); + if (ret < 0) + return ret; + } else { + ret = p_tas2562->write(p_tas2562, channel_both, + TAS2562_TDMConfigurationReg4, 0x01); + if (ret < 0) + return ret; + } + ret = p_tas2562->write(p_tas2562, channel_both, + TAS2562_CLOCKCONFIGURATION, 0x0c); + if (ret < 0) + return ret; + ret = tas2562_i2c_load_data(p_tas2562, channel_both, + p_tas2562_classh_d_data); + + return ret; +} + +static int tas2562_codec_probe(struct snd_soc_codec *codec) +{ + int ret; + struct tas2562_priv *p_tas2562 = snd_soc_codec_get_drvdata(codec); + + ret = snd_soc_add_codec_controls(codec, tas2562_controls, + ARRAY_SIZE(tas2562_controls)); + if (ret < 0) { + pr_err("%s: add_codec_controls failed, err %d\n", + __func__, ret); + return ret; + } + + tas2562_load_init(p_tas2562); + tas2562_iv_enable(p_tas2562, 1); +#ifdef CONFIG_TAS25XX_ALGO + tas_smartamp_add_algo_controls(codec); +#endif //CONFIG_TAS25XX_ALGO + + dev_err(p_tas2562->dev, "%s\n", __func__); + + return 0; +} + +static int tas2562_codec_remove(struct snd_soc_codec *codec) +{ +#ifdef CONFIG_TAS25XX_ALGO + tas_smartamp_remove_algo_controls(codec); +#endif //CONFIG_TAS25XX_ALGO + + return 0; +} + +static DECLARE_TLV_DB_SCALE(tas2562_digital_tlv, 1100, 50, 0); + +static const char * const speaker_switch_text[] = {"Off", "On"}; +static const struct soc_enum spk_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(speaker_switch_text), + speaker_switch_text), +}; + +static const struct snd_kcontrol_new tas2562_snd_controls[] = { + SOC_SINGLE_TLV("Amp Output Level", TAS2562_PLAYBACKCONFIGURATIONREG0, + 1, 0x16, 0, + tas2562_digital_tlv), + SOC_SINGLE_EXT("SmartPA System Mute", SND_SOC_NOPM, 0, 0x0001, 0, + tas2562_system_mute_ctrl_get, tas2562_system_mute_ctrl_put), + SOC_SINGLE_EXT("SmartPA Mute", SND_SOC_NOPM, 0, 0x0001, 0, + tas2562_mute_ctrl_get, tas2562_mute_ctrl_put), + SOC_ENUM_EXT("TAS2562 Left Speaker Switch", spk_enum[0], + tas2562_get_left_speaker_switch, + tas2562_set_left_speaker_switch), + SOC_ENUM_EXT("TAS2562 Right Speaker Switch", spk_enum[0], + tas2562_get_right_speaker_switch, + tas2562_set_right_speaker_switch), + SOC_SINGLE_EXT("TAS2562 Digital Volume", SND_SOC_NOPM, 0, 0x006E, 0, + tas2562_get_digital_volume, tas2562_set_digital_volume), +}; + +static struct snd_soc_codec_driver soc_codec_driver_tas2562 = { + .probe = tas2562_codec_probe, + .remove = tas2562_codec_remove, + .read = tas2562_codec_read, + .write = tas2562_codec_write, +#ifdef CODEC_PM + .suspend = tas2562_codec_suspend, + .resume = tas2562_codec_resume, +#endif + .component_driver = { + .controls = tas2562_snd_controls, + .num_controls = ARRAY_SIZE(tas2562_snd_controls), + .dapm_widgets = tas2562_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas2562_dapm_widgets), + .dapm_routes = tas2562_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas2562_audio_map), + }, +}; + +int tas2562_register_codec(struct tas2562_priv *p_tas2562) +{ + int n_result = 0; + + dev_info(p_tas2562->dev, "%s, enter\n", __func__); + n_result = snd_soc_register_codec(p_tas2562->dev, + &soc_codec_driver_tas2562, + tas2562_dai_driver, ARRAY_SIZE(tas2562_dai_driver)); + return n_result; +} + +int tas2562_deregister_codec(struct tas2562_priv *p_tas2562) +{ + snd_soc_unregister_codec(p_tas2562->dev); + + return 0; +} + +void tas2562_load_config(struct tas2562_priv *p_tas2562) +{ + int ret = 0; + + p_tas2562->hw_reset(p_tas2562); + msleep(20); + p_tas2562->write(p_tas2562, channel_both, TAS2562_SOFTWARERESET, + TAS2562_SOFTWARERESET_SOFTWARERESET_RESET); + msleep(20); + + ret = tas2562_iv_slot_config(p_tas2562); + if(ret < 0) { + goto end; + } + + tas2562_load_init(p_tas2562); + tas2562_iv_enable(p_tas2562, tas2562iv_enable); + + ret = tas2562_set_slot(p_tas2562, p_tas2562->mn_slot_width); + if (ret < 0) + goto end; + + ret = tas2562_set_fmt(p_tas2562, p_tas2562->mn_asi_format); + if (ret < 0) + goto end; + + ret = tas2562_set_bitwidth(p_tas2562, p_tas2562->mn_pcm_format); + if (ret < 0) + goto end; + + ret = tas2562_set_samplerate(p_tas2562, p_tas2562->mn_sampling_rate); + if (ret < 0) + goto end; + + ret = tas2562_set_power_state(p_tas2562, channel_both, + p_tas2562->mn_power_state); + if (ret < 0) + goto end; + +end: +/* power up failed, restart later */ + if (ret < 0) + schedule_delayed_work(&p_tas2562->irq_work, + msecs_to_jiffies(1000)); +} + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("TAS2562 ALSA SOC Smart Amplifier driver"); +MODULE_LICENSE("GPL v2"); +#endif /* CONFIG_TAS2562_CODEC */ diff --git a/sound/soc/codecs/tas2562-codec.h b/sound/soc/codecs/tas2562-codec.h new file mode 100644 index 000000000000..186933a36216 --- /dev/null +++ b/sound/soc/codecs/tas2562-codec.h @@ -0,0 +1,32 @@ +/* + * ============================================================================= + * Copyright (c) 2016 Texas Instruments 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; version 2. + * + * 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: + * tas2562-codec.h + * + * Description: + * header file for tas2562-codec.c + * + * ============================================================================= + */ + +#ifndef _TAS2562_CODEC_H +#define _TAS2562_CODEC_H + +#include "tas2562.h" + +int tas2562_register_codec(struct tas2562_priv *p_tas2562); +int tas2562_deregister_codec(struct tas2562_priv *p_tas2562); +int tas2562_load_config(struct tas2562_priv *p_tas2562); +int tas2562_brownout_enable(struct tas2562_priv *p_tas2562, bool enable); + +#endif /* _TAS2562_CODEC_H */ diff --git a/sound/soc/codecs/tas2562-misc.c b/sound/soc/codecs/tas2562-misc.c new file mode 100644 index 000000000000..876cd49ed1e2 --- /dev/null +++ b/sound/soc/codecs/tas2562-misc.c @@ -0,0 +1,259 @@ +/* + * ============================================================================= + * Copyright (c) 2016 Texas Instruments 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; version 2. + * + * 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: + * tas2562-misc.c + * + * Description: + * misc driver for Texas Instruments + * TAS2562 High Performance 4W Smart Amplifier + * + * ============================================================================= + */ + +//#define DEBUG +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tas2562.h" +#include "tas2562-misc.h" +#include + +static int g_log_enable = 1; +static struct tas2562_priv *g_tas2562; + +static int tas2562_file_open(struct inode *inode, struct file *file) +{ + struct tas2562_priv *p_tas2562 = g_tas2562; + + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + file->private_data = (void *)p_tas2562; + + if (g_log_enable) + dev_info(p_tas2562->dev, "%s\n", __func__); + return 0; +} + +static int tas2562_file_release(struct inode *inode, struct file *file) +{ + struct tas2562_priv *p_tas2562 = + (struct tas2562_priv *)file->private_data; + + if (g_log_enable) + dev_info(p_tas2562->dev, "%s\n", __func__); + + file->private_data = (void *)NULL; + module_put(THIS_MODULE); + + return 0; +} + +static ssize_t tas2562_file_read(struct file *file, + char *buf, size_t count, loff_t *ppos) +{ + struct tas2562_priv *p_tas2562 = + (struct tas2562_priv *)file->private_data; + int ret = 0; + unsigned int n_value = 0; + unsigned char value = 0; + unsigned char *p_kbuf = NULL; + + mutex_lock(&p_tas2562->file_lock); + + switch (p_tas2562->mn_dbg_cmd) { + case TIAUDIO_CMD_REG_READ: + { + if (g_log_enable) + dev_info(p_tas2562->dev, + "TIAUDIO_CMD_REG_READ: current_reg = 0x%x, count=%d\n", + p_tas2562->mn_current_reg, (int)count); + if (count == 1) { + ret = p_tas2562->read(p_tas2562, + p_tas2562->mn_current_reg, &n_value); + if (ret < 0) { + dev_err(p_tas2562->dev, + "dev read fail %d\n", ret); + break; + } + + value = (u8)n_value; + if (g_log_enable) + dev_info(p_tas2562->dev, "TIAUDIO_CMD_REG_READ: n_value=0x%x, value=0x%x\n", + n_value, value); + ret = copy_to_user(buf, &value, 1); + if (ret != 0) { + /* Failed to copy all the data, exit */ + dev_err(p_tas2562->dev, + "copy to user fail %d\n", ret); + } + } else if (count > 1) { + p_kbuf = kzalloc(count, GFP_KERNEL); + if (p_kbuf != NULL) { + ret = p_tas2562->bulk_read(p_tas2562, + p_tas2562->mn_current_reg, p_kbuf, count); + if (ret < 0) { + dev_err(p_tas2562->dev, + "dev bulk read fail %d\n", ret); + } else { + ret = copy_to_user(buf, p_kbuf, count); + if (ret != 0) { + /* Failed to copy all the data, exit */ + dev_err(p_tas2562->dev, + "copy to user fail %d\n", ret); + } + } + + kfree(p_kbuf); + } else { + dev_err(p_tas2562->dev, "read no mem\n"); + } + } + } + break; + } + p_tas2562->mn_dbg_cmd = 0; + + mutex_unlock(&p_tas2562->file_lock); + return count; +} + +static ssize_t tas2562_file_write(struct file *file, + const char *buf, size_t count, loff_t *ppos) +{ + struct tas2562_priv *p_tas2562 = + (struct tas2562_priv *)file->private_data; + int ret = 0; + unsigned char *p_kbuf = NULL; + unsigned int reg = 0; + unsigned int len = 0; + + mutex_lock(&p_tas2562->file_lock); + + p_kbuf = kzalloc(count, GFP_KERNEL); + if (p_kbuf == NULL) { + /* dev_err(p_tas2562->dev, "write no mem\n"); */ + goto err; + } + + ret = copy_from_user(p_kbuf, buf, count); + if (ret != 0) { + dev_err(p_tas2562->dev, "copy_from_user failed.\n"); + goto err; + } + + p_tas2562->mn_dbg_cmd = p_kbuf[0]; + switch (p_tas2562->mn_dbg_cmd) { + case TIAUDIO_CMD_REG_WITE: + if (count > 5) { + reg = ((unsigned int)p_kbuf[1] << 24) + + ((unsigned int)p_kbuf[2] << 16) + + ((unsigned int)p_kbuf[3] << 8) + + (unsigned int)p_kbuf[4]; + len = count - 5; + if (len == 1) { + ret = p_tas2562->write(p_tas2562, reg, p_kbuf[5]); + if (g_log_enable) + dev_info(p_tas2562->dev, + "TIAUDIO_CMD_REG_WITE, Reg=0x%x, Val=0x%x\n", + reg, p_kbuf[5]); + } else { + ret = p_tas2562->bulk_write(p_tas2562, + reg, &p_kbuf[5], len); + } + } else { + dev_err(p_tas2562->dev, "%s, write len fail, count=%d.\n", + __func__, (int)count); + } + p_tas2562->mn_dbg_cmd = 0; + break; + + case TIAUDIO_CMD_REG_READ: + if (count == 5) { + p_tas2562->mn_current_reg = ((unsigned int)p_kbuf[1] << 24) + + ((unsigned int)p_kbuf[2] << 16) + + ((unsigned int)p_kbuf[3] << 8) + + (unsigned int)p_kbuf[4]; + if (g_log_enable) { + dev_info(p_tas2562->dev, + "TIAUDIO_CMD_REG_READ, whole=0x%x\n", + p_tas2562->mn_current_reg); + } + } else { + dev_err(p_tas2562->dev, "read len fail.\n"); + } + break; + } +err: + if (p_kbuf != NULL) + kfree(p_kbuf); + + mutex_unlock(&p_tas2562->file_lock); + + return count; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .read = tas2562_file_read, + .write = tas2562_file_write, + .unlocked_ioctl = NULL, + .open = tas2562_file_open, + .release = tas2562_file_release, +}; + +#define MODULE_NAME "tas2562" +static struct miscdevice tas2562_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = MODULE_NAME, + .fops = &fops, +}; + +int tas2562_register_misc(struct tas2562_priv *p_tas2562) +{ + int ret = 0; + + g_tas2562 = p_tas2562; + ret = misc_register(&tas2562_misc); + if (ret) + dev_err(p_tas2562->dev, "TAS2562 misc fail: %d\n", ret); + + dev_info(p_tas2562->dev, "%s, leave\n", __func__); + + return ret; +} + +int tas2562_deregister_misc(struct tas2562_priv *p_tas2562) +{ + misc_deregister(&tas2562_misc); + return 0; +} + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("TAS2562 Misc Smart Amplifier driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tas2562-misc.h b/sound/soc/codecs/tas2562-misc.h new file mode 100644 index 000000000000..18fa8c391204 --- /dev/null +++ b/sound/soc/codecs/tas2562-misc.h @@ -0,0 +1,38 @@ +/* + * ============================================================================= + * Copyright (c) 2016 Texas Instruments 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; version 2. + * + * 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: + * tas2562-misc.h + * + * Description: + * header file for tas2562-misc.c + * + * ============================================================================= + */ + +#ifndef _TAS2562_MISC_H +#define _TAS2562_MISC_H + +#define TIAUDIO_CMD_REG_WITE 1 +#define TIAUDIO_CMD_REG_READ 2 +#define TIAUDIO_CMD_DEBUG_ON 3 +#define TIAUDIO_CMD_CALIBRATION 7 +#define TIAUDIO_CMD_SAMPLERATE 8 +#define TIAUDIO_CMD_BITRATE 9 +#define TIAUDIO_CMD_DACVOLUME 10 +#define TIAUDIO_CMD_SPEAKER 11 + +int tas2562_register_misc(struct tas2562_priv *p_tas2562); +int tas2562_deregister_misc(struct tas2562_priv *p_tas2562); + +#endif /* _TAS2562_MISC_H */ diff --git a/sound/soc/codecs/tas2562-regmap.c b/sound/soc/codecs/tas2562-regmap.c new file mode 100644 index 000000000000..cc51a4b580c8 --- /dev/null +++ b/sound/soc/codecs/tas2562-regmap.c @@ -0,0 +1,1214 @@ +/* + * ALSA SoC Texas Instruments TAS2562 High Performance 4W Smart Amplifier + * + * Copyright (C) 2016 Texas Instruments, Inc. + * + * Author: saiprasad + * + * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + */ +#ifdef CONFIG_TAS2562_REGMAP + +//#define DEBUG 5 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tas2562.h" +#include "tas2562-codec.h" +#include "tas2562-misc.h" +static char p_icn[] = {0x00, 0x03, 0x46, 0xdc}; + +static int tas2562_regmap_write(struct tas2562_priv *p_tas2562, + unsigned int reg, unsigned int value) +{ + int nResult = 0; + int retry_count = TAS2562_I2C_RETRY_COUNT; + + if(p_tas2562->i2c_suspend) + return ERROR_I2C_SUSPEND; + + while(retry_count--) + { + nResult = regmap_write(p_tas2562->regmap, reg, + value); + if (nResult >= 0) + break; + msleep(20); + } + if(retry_count == -1) + return ERROR_I2C_FAILED; + else + return 0; +} + +static int tas2562_regmap_bulk_write(struct tas2562_priv *p_tas2562, + unsigned int reg, unsigned char *pData, unsigned int nLength) +{ + int nResult = 0; + int retry_count = TAS2562_I2C_RETRY_COUNT; + + if(p_tas2562->i2c_suspend) + return ERROR_I2C_SUSPEND; + + while(retry_count --) + { + nResult = regmap_bulk_write(p_tas2562->regmap, reg, + pData, nLength); + if (nResult >= 0) + break; + msleep(20); + } + if(retry_count == -1) + return ERROR_I2C_FAILED; + else + return 0; +} + +static int tas2562_regmap_read(struct tas2562_priv *p_tas2562, + unsigned int reg, unsigned int *value) +{ + int nResult = 0; + int retry_count = TAS2562_I2C_RETRY_COUNT; + + if(p_tas2562->i2c_suspend) + return ERROR_I2C_SUSPEND; + + while(retry_count --) + { + nResult = regmap_read(p_tas2562->regmap, reg, + value); + if (nResult >= 0) + break; + msleep(20); + } + if(retry_count == -1) + return ERROR_I2C_FAILED; + else + return 0; +} + +static int tas2562_regmap_bulk_read(struct tas2562_priv *p_tas2562, + unsigned int reg, unsigned char *pData, unsigned int nLength) +{ + int nResult = 0; + int retry_count = TAS2562_I2C_RETRY_COUNT; + + if(p_tas2562->i2c_suspend) + return ERROR_I2C_SUSPEND; + + while(retry_count --) + { + nResult = regmap_bulk_read(p_tas2562->regmap, reg, + pData, nLength); + if (nResult >= 0) + break; + msleep(20); + } + if(retry_count == -1) + return ERROR_I2C_FAILED; + else + return 0; +} + +static int tas2562_regmap_update_bits(struct tas2562_priv *p_tas2562, + unsigned int reg, unsigned int mask, unsigned int value) +{ + int nResult = 0; + int retry_count = TAS2562_I2C_RETRY_COUNT; + + if(p_tas2562->i2c_suspend) + return ERROR_I2C_SUSPEND; + + while(retry_count--) + { + nResult = regmap_update_bits(p_tas2562->regmap, reg, + mask, value); + if (nResult >= 0) + break; + msleep(20); + } + if(retry_count == -1) + return ERROR_I2C_FAILED; + else + return 0; +} + +static int tas2562_change_book_page(struct tas2562_priv *p_tas2562, + enum channel chn, + int book, int page) +{ + int n_result = 0; + + + if ((chn&channel_left) || (p_tas2562->mn_channels == 1)) { + p_tas2562->client->addr = p_tas2562->mn_l_addr; + if (p_tas2562->mn_l_current_book != book) { + n_result = tas2562_regmap_write(p_tas2562, + TAS2562_BOOKCTL_PAGE, 0); + if (n_result < 0) { + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + goto end; + } + p_tas2562->mn_l_current_page = 0; + n_result = tas2562_regmap_write(p_tas2562, + TAS2562_BOOKCTL_REG, book); + if (n_result < 0) { + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + goto end; + } + p_tas2562->mn_l_current_book = book; + } + + if (p_tas2562->mn_l_current_page != page) { + n_result = tas2562_regmap_write(p_tas2562, + TAS2562_BOOKCTL_PAGE, page); + if (n_result < 0) { + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + goto end; + } + p_tas2562->mn_l_current_page = page; + } + } + + if ((chn&channel_right) && (p_tas2562->mn_channels == 2)) { + p_tas2562->client->addr = p_tas2562->mn_r_addr; + if (p_tas2562->mn_r_current_book != book) { + n_result = tas2562_regmap_write(p_tas2562, + TAS2562_BOOKCTL_PAGE, 0); + if (n_result < 0) { + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + goto end; + } + p_tas2562->mn_r_current_page = 0; + n_result = tas2562_regmap_write(p_tas2562, + TAS2562_BOOKCTL_REG, book); + if (n_result < 0) { + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + goto end; + } + p_tas2562->mn_r_current_book = book; + } + + if (p_tas2562->mn_r_current_page != page) { + n_result = tas2562_regmap_write(p_tas2562, + TAS2562_BOOKCTL_PAGE, page); + if (n_result < 0) { + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + goto end; + } + p_tas2562->mn_r_current_page = page; + } + } + +end: + return n_result; +} + +static int tas2562_dev_read(struct tas2562_priv *p_tas2562, + enum channel chn, + unsigned int reg, unsigned int *pValue) +{ + int n_result = 0; + + mutex_lock(&p_tas2562->dev_lock); + + n_result = tas2562_change_book_page(p_tas2562, chn, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); + if (n_result < 0) + goto end; + + if ((chn == channel_left) || (p_tas2562->mn_channels == 1)) + p_tas2562->client->addr = p_tas2562->mn_l_addr; + else if (chn == channel_right) + p_tas2562->client->addr = p_tas2562->mn_r_addr; + else + dev_err(p_tas2562->dev, "%s, wrong channel number\n", __func__); + + n_result = tas2562_regmap_read(p_tas2562, + TAS2562_PAGE_REG(reg), pValue); + if (n_result < 0) + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + else + dev_dbg(p_tas2562->dev, + "%s: chn:%x:BOOK:PAGE:REG %u:%u:%u,0x%x\n", __func__, + p_tas2562->client->addr, TAS2562_BOOK_ID(reg), + TAS2562_PAGE_ID(reg), + TAS2562_PAGE_REG(reg), *pValue); + +end: + mutex_unlock(&p_tas2562->dev_lock); + return n_result; +} + +static int tas2562_dev_write(struct tas2562_priv *p_tas2562, enum channel chn, + unsigned int reg, unsigned int value) +{ + int n_result = 0; + + mutex_lock(&p_tas2562->dev_lock); + + n_result = tas2562_change_book_page(p_tas2562, chn, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); + if (n_result < 0) + goto end; + + if ((chn&channel_left) || (p_tas2562->mn_channels == 1)) { + p_tas2562->client->addr = p_tas2562->mn_l_addr; + + n_result = tas2562_regmap_write(p_tas2562, + TAS2562_PAGE_REG(reg), value); + if (n_result < 0) + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + else + dev_dbg(p_tas2562->dev, + "%s: chn%x:BOOK:PAGE:REG %u:%u:%u, VAL: 0x%02x\n", + __func__, p_tas2562->client->addr, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), + TAS2562_PAGE_REG(reg), value); + } + + if ((chn&channel_right) && (p_tas2562->mn_channels == 2)) { + p_tas2562->client->addr = p_tas2562->mn_r_addr; + + n_result = tas2562_regmap_write(p_tas2562, + TAS2562_PAGE_REG(reg), + value); + if (n_result < 0) + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + else + dev_dbg(p_tas2562->dev, "%s: chn%x:BOOK:PAGE:REG %u:%u:%u, VAL: 0x%02x\n", + __func__, p_tas2562->client->addr, + TAS2562_BOOK_ID(reg), + TAS2562_PAGE_ID(reg), + TAS2562_PAGE_REG(reg), value); + } + +end: + mutex_unlock(&p_tas2562->dev_lock); + return n_result; +} + +static int tas2562_dev_bulk_write(struct tas2562_priv *p_tas2562, + enum channel chn, + unsigned int reg, unsigned char *p_data, unsigned int n_length) +{ + int n_result = 0; + + mutex_lock(&p_tas2562->dev_lock); + + n_result = tas2562_change_book_page(p_tas2562, chn, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); + if (n_result < 0) + goto end; + + if ((chn&channel_left) || (p_tas2562->mn_channels == 1)) { + p_tas2562->client->addr = p_tas2562->mn_l_addr; + n_result = tas2562_regmap_bulk_write(p_tas2562, + TAS2562_PAGE_REG(reg), p_data, n_length); + if (n_result < 0) + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + else + dev_dbg(p_tas2562->dev, "%s: chn%x:BOOK:PAGE:REG %u:%u:%u, len: 0x%02x\n", + __func__, p_tas2562->client->addr, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), + TAS2562_PAGE_REG(reg), n_length); + } + + if ((chn&channel_right) && (p_tas2562->mn_channels == 2)) { + p_tas2562->client->addr = p_tas2562->mn_r_addr; + n_result = tas2562_regmap_bulk_write(p_tas2562, + TAS2562_PAGE_REG(reg), p_data, n_length); + if (n_result < 0) + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + else + dev_dbg(p_tas2562->dev, "%s: %x:BOOK:PAGE:REG %u:%u:%u, len: 0x%02x\n", + __func__, p_tas2562->client->addr, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), + TAS2562_PAGE_REG(reg), n_length); + } + +end: + mutex_unlock(&p_tas2562->dev_lock); + return n_result; +} + +static int tas2562_dev_bulk_read(struct tas2562_priv *p_tas2562, + enum channel chn, + unsigned int reg, unsigned char *p_data, unsigned int n_length) +{ + int n_result = 0; + + mutex_lock(&p_tas2562->dev_lock); + + if ((chn == channel_left) || (p_tas2562->mn_channels == 1)) + p_tas2562->client->addr = p_tas2562->mn_l_addr; + else if (chn == channel_right) + p_tas2562->client->addr = p_tas2562->mn_r_addr; + else + dev_err(p_tas2562->dev, "%s, wrong channel number\n", __func__); + + n_result = tas2562_change_book_page(p_tas2562, chn, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); + if (n_result < 0) + goto end; + + n_result = tas2562_regmap_bulk_read(p_tas2562, + TAS2562_PAGE_REG(reg), p_data, n_length); + if (n_result < 0) + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + else + dev_dbg(p_tas2562->dev, "%s: chn%x:BOOK:PAGE:REG %u:%u:%u, len: 0x%02x\n", + __func__, p_tas2562->client->addr, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), + TAS2562_PAGE_REG(reg), n_length); +end: + mutex_unlock(&p_tas2562->dev_lock); + return n_result; +} + +static int tas2562_dev_update_bits(struct tas2562_priv *p_tas2562, + enum channel chn, + unsigned int reg, unsigned int mask, unsigned int value) +{ + int n_result = 0; + + mutex_lock(&p_tas2562->dev_lock); + n_result = tas2562_change_book_page(p_tas2562, chn, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg)); + if (n_result < 0) + goto end; + + if ((chn&channel_left) || (p_tas2562->mn_channels == 1)) { + p_tas2562->client->addr = p_tas2562->mn_l_addr; + n_result = tas2562_regmap_update_bits(p_tas2562, + TAS2562_PAGE_REG(reg), mask, value); + if (n_result < 0) + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + else + dev_dbg(p_tas2562->dev, + "%s: chn%x:BOOK:PAGE:REG %u:%u:%u, mask: 0x%x, val: 0x%x\n", + __func__, p_tas2562->client->addr, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), + TAS2562_PAGE_REG(reg), mask, value); + } + + if ((chn&channel_right) && (p_tas2562->mn_channels == 2)) { + p_tas2562->client->addr = p_tas2562->mn_r_addr; + n_result = tas2562_regmap_update_bits(p_tas2562, + TAS2562_PAGE_REG(reg), mask, value); + if (n_result < 0) + dev_err(p_tas2562->dev, "%s, ERROR, L=%d, E=%d\n", + __func__, __LINE__, n_result); + else + dev_dbg(p_tas2562->dev, + "%s:chn%x:BOOK:PAGE:REG %u:%u:%u,mask: 0x%x, val: 0x%x\n", + __func__, p_tas2562->client->addr, + TAS2562_BOOK_ID(reg), TAS2562_PAGE_ID(reg), + TAS2562_PAGE_REG(reg), mask, value); + } + +end: + mutex_unlock(&p_tas2562->dev_lock); + return n_result; +} + +static bool tas2562_volatile(struct device *dev, unsigned int reg) +{ + return true; +} + +static bool tas2562_writeable(struct device *dev, unsigned int reg) +{ + return true; +} +static const struct regmap_config tas2562_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = tas2562_writeable, + .volatile_reg = tas2562_volatile, + .cache_type = REGCACHE_NONE, + .max_register = 1 * 128, +}; + + +static void tas2562_hw_reset(struct tas2562_priv *p_tas2562) +{ + if (gpio_is_valid(p_tas2562->mn_reset_gpio)) { + gpio_direction_output(p_tas2562->mn_reset_gpio, 0); + if (gpio_is_valid(p_tas2562->mn_reset_gpio2)) { + gpio_direction_output(p_tas2562->mn_reset_gpio2, 0); + } + msleep(20); + gpio_direction_output(p_tas2562->mn_reset_gpio, 1); + if (gpio_is_valid(p_tas2562->mn_reset_gpio2)) { + gpio_direction_output(p_tas2562->mn_reset_gpio2, 1); + } + msleep(20); + } + dev_err(p_tas2562->dev, "gpio up !!\n"); + + p_tas2562->mn_l_current_book = -1; + p_tas2562->mn_l_current_page = -1; + p_tas2562->mn_r_current_book = -1; + p_tas2562->mn_r_current_page = -1; +} + +void tas2562_enable_irq(struct tas2562_priv *p_tas2562, bool enable) +{ + static int irq1_enabled = 0; + static int irq2_enabled = 0; + + dev_info(p_tas2562->dev, "%s: irq enable %d\n", __func__, enable); + if (enable) { + if (p_tas2562->mb_irq_eable) + return; + + if (gpio_is_valid(p_tas2562->mn_irq_gpio) && irq1_enabled == 0) { + enable_irq(p_tas2562->mn_irq); + irq1_enabled = 1; + } + if (gpio_is_valid(p_tas2562->mn_irq_gpio2) && irq2_enabled == 0) { + enable_irq(p_tas2562->mn_irq2); + irq2_enabled = 1; + } + + p_tas2562->mb_irq_eable = true; + } else { + if (gpio_is_valid(p_tas2562->mn_irq_gpio) && irq1_enabled == 1) { + disable_irq_nosync(p_tas2562->mn_irq); + irq1_enabled = 0; + } + if (gpio_is_valid(p_tas2562->mn_irq_gpio2) && irq2_enabled == 1) { + disable_irq_nosync(p_tas2562->mn_irq2); + irq2_enabled = 0; + } + p_tas2562->mb_irq_eable = false; + } +} + +static void irq_work_routine(struct work_struct *work) +{ + struct tas2562_priv *p_tas2562 = + container_of(work, struct tas2562_priv, irq_work.work); + unsigned int nDevInt1Status = 0, nDevInt2Status = 0, + nDevInt3Status = 0, nDevInt4Status = 0; + int n_counter = 2; + int n_result = 0; + int irqreg; + enum channel chn; + + dev_info(p_tas2562->dev, "%s\n", __func__); +#ifdef CONFIG_TAS2562_CODEC + mutex_lock(&p_tas2562->codec_lock); +#endif + tas2562_enable_irq(p_tas2562, false); + + if (p_tas2562->mb_runtime_suspend) { + dev_info(p_tas2562->dev, "%s, Runtime Suspended\n", __func__); + goto end; + } + + if (p_tas2562->mn_power_state == TAS2562_POWER_SHUTDOWN) { + dev_info(p_tas2562->dev, "%s, device not powered\n", __func__); + goto end; + } + + n_result = p_tas2562->write(p_tas2562, channel_both, + TAS2562_INTERRUPTMASKREG0, + TAS2562_INTERRUPTMASKREG0_DISABLE); + n_result = p_tas2562->write(p_tas2562, channel_both, + TAS2562_INTERRUPTMASKREG1, + TAS2562_INTERRUPTMASKREG1_DISABLE); + + if (n_result < 0) + goto reload; + + if ((p_tas2562->spk_l_control == 1) + && (p_tas2562->spk_r_control == 1) + && (p_tas2562->mn_channels == 2)) + chn = channel_both; + else if (p_tas2562->spk_l_control == 1) + chn = channel_left; + else if ((p_tas2562->spk_r_control == 1) + && (p_tas2562->mn_channels == 2)) + chn = channel_right; + else + chn = channel_left; + + if (chn & channel_left) + n_result = p_tas2562->read(p_tas2562, channel_left, + TAS2562_LATCHEDINTERRUPTREG0, &nDevInt1Status); + if (n_result >= 0) + n_result = p_tas2562->read(p_tas2562, channel_left, + TAS2562_LATCHEDINTERRUPTREG1, &nDevInt2Status); + else + goto reload; + + if (chn & channel_right) + n_result = p_tas2562->read(p_tas2562, channel_right, + TAS2562_LATCHEDINTERRUPTREG0, &nDevInt3Status); + if (n_result >= 0) + n_result = p_tas2562->read(p_tas2562, channel_right, + TAS2562_LATCHEDINTERRUPTREG1, &nDevInt4Status); + else + goto reload; + + dev_dbg(p_tas2562->dev, "IRQ status : 0x%x, 0x%x, 0x%x, 0x%x\n", + nDevInt1Status, nDevInt2Status, + nDevInt3Status, nDevInt4Status); + + if (((nDevInt1Status & 0x7) != 0) + || ((nDevInt2Status & 0x0f) != 0) || + ((nDevInt3Status & 0x7) != 0) + || ((nDevInt4Status & 0x0f) != 0)) { +/* in case of INT_CLK, INT_OC, INT_OT, + * INT_OVLT, INT_UVLT, INT_BO + */ + + if ((nDevInt1Status & + TAS2562_LATCHEDINTERRUPTREG0_TDMCLOCKERRORSTICKY_INTERRUPT) || + (nDevInt3Status & + TAS2562_LATCHEDINTERRUPTREG0_TDMCLOCKERRORSTICKY_INTERRUPT)) { + p_tas2562->mn_err_code |= ERROR_CLOCK; + dev_err(p_tas2562->dev, "TDM clock error!\n"); + } else + p_tas2562->mn_err_code &= ~ERROR_OVER_CURRENT; + + if ((nDevInt1Status & + TAS2562_LATCHEDINTERRUPTREG0_OCEFLAGSTICKY_INTERRUPT) || + (nDevInt3Status & + TAS2562_LATCHEDINTERRUPTREG0_OCEFLAGSTICKY_INTERRUPT)) { + p_tas2562->mn_err_code |= ERROR_OVER_CURRENT; + dev_err(p_tas2562->dev, "SPK over current!\n"); + } else + p_tas2562->mn_err_code &= ~ERROR_OVER_CURRENT; + + if ((nDevInt1Status & + TAS2562_LATCHEDINTERRUPTREG0_OTEFLAGSTICKY_INTERRUPT) || + (nDevInt3Status & + TAS2562_LATCHEDINTERRUPTREG0_OTEFLAGSTICKY_INTERRUPT)) { + p_tas2562->mn_err_code |= ERROR_DIE_OVERTEMP; + dev_err(p_tas2562->dev, "die over temperature!\n"); + } else + p_tas2562->mn_err_code &= ~ERROR_DIE_OVERTEMP; + + if ((nDevInt2Status & + TAS2562_LATCHEDINTERRUPTREG1_VBATOVLOSTICKY_INTERRUPT) || + (nDevInt4Status & + TAS2562_LATCHEDINTERRUPTREG1_VBATOVLOSTICKY_INTERRUPT)) { + p_tas2562->mn_err_code |= ERROR_OVER_VOLTAGE; + dev_err(p_tas2562->dev, "SPK over voltage!\n"); + } else + p_tas2562->mn_err_code &= ~ERROR_UNDER_VOLTAGE; + + if ((nDevInt2Status & + TAS2562_LATCHEDINTERRUPTREG1_VBATUVLOSTICKY_INTERRUPT) || + (nDevInt4Status & + TAS2562_LATCHEDINTERRUPTREG1_VBATUVLOSTICKY_INTERRUPT)) { + p_tas2562->mn_err_code |= ERROR_UNDER_VOLTAGE; + dev_err(p_tas2562->dev, "SPK under voltage!\n"); + } else + p_tas2562->mn_err_code &= ~ERROR_UNDER_VOLTAGE; + + if ((nDevInt2Status & + TAS2562_LATCHEDINTERRUPTREG1_BROWNOUTFLAGSTICKY_INTERRUPT) || + (nDevInt4Status & + TAS2562_LATCHEDINTERRUPTREG1_BROWNOUTFLAGSTICKY_INTERRUPT)) { + p_tas2562->mn_err_code |= ERROR_BROWNOUT; + dev_err(p_tas2562->dev, "brownout!\n"); + } else + p_tas2562->mn_err_code &= ~ERROR_BROWNOUT; + + goto reload; + } else { + n_counter = 2; + + while (n_counter > 0) { + if (chn & channel_left) + n_result = p_tas2562->read(p_tas2562, channel_left, + TAS2562_POWERCONTROL, &nDevInt1Status); + if (n_result < 0) + goto reload; + if (chn & channel_right) + n_result = p_tas2562->read(p_tas2562, channel_right, + TAS2562_POWERCONTROL, &nDevInt3Status); + if (n_result < 0) + goto reload; + + if ((nDevInt1Status + & TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK) + != TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN) { + /* If only left should be power on */ + if (chn == channel_left) + break; + /* If both should be power on */ + if ((nDevInt3Status + & TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK) + != + TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN) + break; + } + /*If only right should be power on */ + else if (chn == channel_right) { + if ((nDevInt3Status + & TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK) + != + TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN) + break; + } + + p_tas2562->read(p_tas2562, channel_left, + TAS2562_LATCHEDINTERRUPTREG0, &irqreg); + dev_info(p_tas2562->dev, "IRQ reg is: %s %d, %d\n", + __func__, irqreg, __LINE__); + p_tas2562->read(p_tas2562, channel_right, + TAS2562_LATCHEDINTERRUPTREG0, &irqreg); + dev_info(p_tas2562->dev, "IRQ reg is: %s %d, %d\n", + __func__, irqreg, __LINE__); + + n_result = p_tas2562->update_bits(p_tas2562, + chn, TAS2562_POWERCONTROL, + TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK, + TAS2562_POWERCONTROL_OPERATIONALMODE10_ACTIVE); + if (n_result < 0) + goto reload; + // Let the Power Up complete + msleep(2); + // Enable Comparator Hystersis + if (p_tas2562->icn_enable_left) { + n_result = p_tas2562->write(p_tas2562, channel_left, + TAS2562_REG(0x0, 0x01, 0x21), 0x08); + dev_info(p_tas2562->dev, "ICN enabled for Left\n"); + } + if (p_tas2562->icn_enable_right) { + n_result = p_tas2562->write(p_tas2562, channel_right, + TAS2562_REG(0x0, 0x01, 0x21), 0x08); + dev_info(p_tas2562->dev, "ICN enabled for Right\n"); + } + + dev_info(p_tas2562->dev, "set ICN to -80dB\n"); + n_result = p_tas2562->bulk_write(p_tas2562, chn, + TAS2562_ICN_REG, p_icn, 4); + + p_tas2562->read(p_tas2562, channel_left, + TAS2562_LATCHEDINTERRUPTREG0, &irqreg); + dev_info(p_tas2562->dev, "IRQ reg is: %s, %d, %d\n", + __func__, irqreg, __LINE__); + p_tas2562->read(p_tas2562, channel_right, + TAS2562_LATCHEDINTERRUPTREG0, &irqreg); + dev_info(p_tas2562->dev, "IRQ reg is: %s %d, %d\n", + __func__, irqreg, __LINE__); + + n_counter--; + if (n_counter > 0) { + /* in case check pow status just after power on TAS2562 */ + dev_dbg(p_tas2562->dev, "PowSts B: 0x%x, check again after 10ms\n", + nDevInt1Status); + msleep(20); + } + } + + if ((((nDevInt1Status + & TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK) + == TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN) + && (chn & channel_left)) + || (((nDevInt3Status + & TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK) + == TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN) + && (chn & channel_right))) { + dev_err(p_tas2562->dev, "%s, Critical ERROR REG[0x%x] = 0x%x\n", + __func__, + TAS2562_POWERCONTROL, + nDevInt1Status); + p_tas2562->mn_err_code |= ERROR_CLASSD_PWR; + goto reload; + } + p_tas2562->mn_err_code &= ~ERROR_CLASSD_PWR; + } + + n_result = p_tas2562->write(p_tas2562, channel_both, + TAS2562_INTERRUPTMASKREG0, 0xf8); + if (n_result < 0) + goto reload; + + n_result = p_tas2562->write(p_tas2562, channel_both, + TAS2562_INTERRUPTMASKREG1, 0xb1); + if (n_result < 0) + goto reload; + + n_result = tas2562_brownout_enable(p_tas2562, p_tas2562->tas2562brownout_enable); + if (n_result < 0) + dev_err(p_tas2562->dev, "brownout enable fail!\n"); + + goto end; + +reload: + /* hardware reset and reload */ + tas2562_load_config(p_tas2562); + +end: + tas2562_enable_irq(p_tas2562, true); +#ifdef CONFIG_TAS2562_CODEC + mutex_unlock(&p_tas2562->codec_lock); +#endif +} + +static void init_work_routine(struct work_struct *work) +{ + struct tas2562_priv *p_tas2562 = + container_of(work, struct tas2562_priv, init_work.work); + int nResult = 0; + //int irqreg; + //dev_info(p_tas2562->dev, "%s\n", __func__); +#ifdef CONFIG_TAS2562_CODEC + mutex_lock(&p_tas2562->codec_lock); +#endif + + p_tas2562->update_bits(p_tas2562, channel_both, TAS2562_POWERCONTROL, + TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK, + TAS2562_POWERCONTROL_OPERATIONALMODE10_ACTIVE); + + //dev_info(p_tas2562->dev, "set ICN to -80dB\n"); + nResult = p_tas2562->bulk_write(p_tas2562, channel_both, TAS2562_ICN_REG, p_icn, 4); + + nResult = gpio_get_value(p_tas2562->mn_irq_gpio); + //dev_info(p_tas2562->dev, "%s, irq GPIO state: %d\n", __func__, nResult); + +#ifdef CONFIG_TAS2562_CODEC + mutex_unlock(&p_tas2562->codec_lock); +#endif +} + +static irqreturn_t tas2562_irq_handler(int irq, void *dev_id) +{ + struct tas2562_priv *p_tas2562 = (struct tas2562_priv *)dev_id; + + /* get IRQ status after 100 ms */ + schedule_delayed_work(&p_tas2562->irq_work, msecs_to_jiffies(100)); + return IRQ_HANDLED; +} + +static int tas2562_runtime_suspend(struct tas2562_priv *p_tas2562) +{ + dev_dbg(p_tas2562->dev, "%s\n", __func__); + + p_tas2562->mb_runtime_suspend = true; + + if (delayed_work_pending(&p_tas2562->irq_work)) { + dev_dbg(p_tas2562->dev, "cancel IRQ work\n"); + cancel_delayed_work_sync(&p_tas2562->irq_work); + } + + return 0; +} + +static int tas2562_runtime_resume(struct tas2562_priv *p_tas2562) +{ + dev_dbg(p_tas2562->dev, "%s\n", __func__); + + p_tas2562->mb_runtime_suspend = false; + + return 0; +} + +static int tas2562_pm_suspend(struct device *dev) +{ + struct tas2562_priv *p_tas2562 = dev_get_drvdata(dev); + + if(!p_tas2562){ + dev_err(p_tas2562->dev, "drvdata is NULL\n"); + return -EINVAL; + } + + mutex_lock(&p_tas2562->codec_lock); + tas2562_runtime_suspend(p_tas2562); + mutex_unlock(&p_tas2562->codec_lock); + return 0; +} +static int tas2562_pm_resume(struct device *dev) +{ + struct tas2562_priv *p_tas2562 = dev_get_drvdata(dev); + + if(!p_tas2562){ + dev_err(p_tas2562->dev, "drvdata is NULL\n"); + return -EINVAL; + } + mutex_lock(&p_tas2562->codec_lock); + tas2562_runtime_resume(p_tas2562); + mutex_unlock(&p_tas2562->codec_lock); + return 0; +} + +static int tas2562_parse_dt(struct device *dev, struct tas2562_priv *p_tas2562) +{ + struct device_node *np = dev->of_node; + int rc = 0, ret = 0; + + rc = of_property_read_u32(np, "ti,asi-format", + &p_tas2562->mn_asi_format); + if (rc) { + dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", + "ti,asi-format", np->full_name, rc); + } else { + dev_dbg(p_tas2562->dev, "ti,asi-format=%d", + p_tas2562->mn_asi_format); + } + + rc = of_property_read_u32(np, "ti,channels", &p_tas2562->mn_channels); + if (rc) { + dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", + "ti,channels", np->full_name, rc); + } else { + dev_dbg(p_tas2562->dev, "ti,channels=%d", + p_tas2562->mn_channels); + } + + rc = of_property_read_u32(np, "ti,left-channel", &p_tas2562->mn_l_addr); + if (rc) { + dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", + "ti,left-channel", np->full_name, rc); + } else { + dev_dbg(p_tas2562->dev, "ti,left-channel=0x%x", + p_tas2562->mn_l_addr); + } + + p_tas2562->mn_reset_gpio = of_get_named_gpio(np, "ti,reset-gpio", 0); + if (!gpio_is_valid(p_tas2562->mn_reset_gpio)) { + dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", + "ti,reset-gpio", np->full_name, + p_tas2562->mn_reset_gpio); + } else { + dev_dbg(p_tas2562->dev, "ti,reset-gpio=%d", + p_tas2562->mn_reset_gpio); + } + + p_tas2562->mn_irq_gpio = of_get_named_gpio(np, "ti,irq-gpio", 0); + if (!gpio_is_valid(p_tas2562->mn_irq_gpio)) { + dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", + "ti,irq-gpio", np->full_name, p_tas2562->mn_irq_gpio); + } else { + dev_dbg(p_tas2562->dev, "ti,irq-gpio=%d", + p_tas2562->mn_irq_gpio); + } + + if(p_tas2562->mn_channels != 1) { + rc = of_property_read_u32(np, "ti,right-channel", + &p_tas2562->mn_r_addr); + if (rc) { + dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", + "ti,right-channel", np->full_name, rc); + } else { + dev_dbg(p_tas2562->dev, "ti,right-channel=0x%x", + p_tas2562->mn_r_addr); + } + + p_tas2562->mn_reset_gpio2 = of_get_named_gpio(np, "ti,reset-gpio2", 0); + if (!gpio_is_valid(p_tas2562->mn_reset_gpio2)) { + dev_dbg(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", + "ti,reset-gpio2", np->full_name, + p_tas2562->mn_reset_gpio2); + } else { + dev_dbg(p_tas2562->dev, "ti,reset-gpio2=%d", + p_tas2562->mn_reset_gpio2); + } + + p_tas2562->mn_irq_gpio2 = of_get_named_gpio(np, "ti,irq-gpio2", 0); + if (!gpio_is_valid(p_tas2562->mn_irq_gpio2)) { + dev_dbg(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", + "ti,irq-gpio2", np->full_name, p_tas2562->mn_irq_gpio2); + } else { + dev_dbg(p_tas2562->dev, "ti,irq-gpio2=%d", + p_tas2562->mn_irq_gpio2); + } + } + + + + rc = of_property_read_u32(np, "ti,lim_th_min", &p_tas2562->lim_th_min); + if (rc) { + dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", + "ti,lim_th_min", np->full_name, rc); + } else { + dev_dbg(p_tas2562->dev, "ti,lim_th_min=%d", + p_tas2562->lim_th_min); + } + + rc = of_property_read_u32(np, "ti,lim_th_max", &p_tas2562->lim_th_max); + if (rc) { + dev_err(p_tas2562->dev, "Looking up %s property in node %s failed %d\n", + "ti,lim_th_max", np->full_name, rc); + } else { + dev_dbg(p_tas2562->dev, "ti,lim_th_max=%d", + p_tas2562->lim_th_max); + } + + return ret; +} + +static int tas2562_i2c_probe(struct i2c_client *p_client, + const struct i2c_device_id *id) +{ + struct tas2562_priv *p_tas2562; + int n_result; + + dev_err(&p_client->dev, "Driver ID: %s\n", TAS2562_DRIVER_ID); + dev_info(&p_client->dev, "%s enter\n", __func__); + + p_tas2562 = devm_kzalloc(&p_client->dev, + sizeof(struct tas2562_priv), GFP_KERNEL); + if (p_tas2562 == NULL) { + /* dev_err(&p_client->dev, "failed to get i2c device\n"); */ + n_result = -ENOMEM; + goto err; + } + + p_tas2562->client = p_client; + p_tas2562->dev = &p_client->dev; + i2c_set_clientdata(p_client, p_tas2562); + dev_set_drvdata(&p_client->dev, p_tas2562); + + p_tas2562->regmap = devm_regmap_init_i2c(p_client, &tas2562_i2c_regmap); + if (IS_ERR(p_tas2562->regmap)) { + n_result = PTR_ERR(p_tas2562->regmap); + dev_err(&p_client->dev, "Failed to allocate register map: %d\n", + n_result); + goto err; + } + + if (p_client->dev.of_node) + tas2562_parse_dt(&p_client->dev, p_tas2562); + + if (gpio_is_valid(p_tas2562->mn_reset_gpio)) { + n_result = gpio_request(p_tas2562->mn_reset_gpio, + "TAS2562_RESET"); + if (n_result) { + dev_err(p_tas2562->dev, "%s: Failed to request gpio %d\n", + __func__, p_tas2562->mn_reset_gpio); + n_result = -EINVAL; + goto err; + } + tas2562_hw_reset(p_tas2562); + } + + if (gpio_is_valid(p_tas2562->mn_reset_gpio2)) { + n_result = gpio_request(p_tas2562->mn_reset_gpio2, + "TAS2562_RESET2"); + if (n_result) { + dev_err(p_tas2562->dev, "%s: Failed to request gpio %d\n", + __func__, p_tas2562->mn_reset_gpio2); + n_result = -EINVAL; + goto err; + } + tas2562_hw_reset(p_tas2562); + } + + p_tas2562->read = tas2562_dev_read; + p_tas2562->write = tas2562_dev_write; + p_tas2562->bulk_read = tas2562_dev_bulk_read; + p_tas2562->bulk_write = tas2562_dev_bulk_write; + p_tas2562->update_bits = tas2562_dev_update_bits; + p_tas2562->hw_reset = tas2562_hw_reset; + p_tas2562->enable_irq = tas2562_enable_irq; +#ifdef CODEC_PM + p_tas2562->runtime_suspend = tas2562_runtime_suspend; + p_tas2562->runtime_resume = tas2562_runtime_resume; + p_tas2562->mn_power_state = TAS2562_POWER_SHUTDOWN; +#endif + p_tas2562->mn_power_state = TAS2562_POWER_SHUTDOWN; + p_tas2562->spk_l_control = 1; + + mutex_init(&p_tas2562->dev_lock); + + dev_info(&p_client->dev, "Before SW reset\n"); + /* Reset the chip */ + n_result = tas2562_dev_write(p_tas2562, channel_both, + TAS2562_SOFTWARERESET, 0x01); + if (n_result < 0) { + dev_err(&p_client->dev, "I2c fail, %d\n", n_result); + goto err; + } + dev_info(&p_client->dev, "After SW reset\n"); + + if (gpio_is_valid(p_tas2562->mn_irq_gpio)) { + n_result = gpio_request(p_tas2562->mn_irq_gpio, "TAS2562-IRQ"); + if (n_result < 0) { + dev_err(p_tas2562->dev, "%s: GPIO %d request error\n", + __func__, p_tas2562->mn_irq_gpio); + goto err; + } + gpio_direction_input(p_tas2562->mn_irq_gpio); + tas2562_dev_write(p_tas2562, channel_both, + TAS2562_MISCCONFIGURATIONREG0, 0xc6); + + p_tas2562->mn_irq = gpio_to_irq(p_tas2562->mn_irq_gpio); + dev_info(p_tas2562->dev, "irq = %d\n", p_tas2562->mn_irq); + INIT_DELAYED_WORK(&p_tas2562->irq_work, irq_work_routine); + n_result = request_threaded_irq(p_tas2562->mn_irq, + tas2562_irq_handler, + NULL, IRQF_TRIGGER_FALLING|IRQF_ONESHOT, + p_client->name, p_tas2562); + if (n_result < 0) { + dev_err(p_tas2562->dev, + "request_irq failed, %d\n", n_result); + goto err; + } + disable_irq_nosync(p_tas2562->mn_irq); + } + if (gpio_is_valid(p_tas2562->mn_irq_gpio2) && + (p_tas2562->mn_channels == 2)) { + n_result = gpio_request(p_tas2562->mn_irq_gpio2, + "TAS2562-IRQ2"); + if (n_result < 0) { + dev_err(p_tas2562->dev, "%s: GPIO %d request error\n", + __func__, p_tas2562->mn_irq_gpio2); + goto err; + } + gpio_direction_input(p_tas2562->mn_irq_gpio2); + tas2562_dev_write(p_tas2562, channel_both, + TAS2562_MISCCONFIGURATIONREG0, 0xc6); + + p_tas2562->mn_irq2 = gpio_to_irq(p_tas2562->mn_irq_gpio2); + dev_info(p_tas2562->dev, "irq = %d\n", p_tas2562->mn_irq2); + INIT_DELAYED_WORK(&p_tas2562->irq_work, irq_work_routine); + n_result = request_threaded_irq(p_tas2562->mn_irq2, + tas2562_irq_handler, + NULL, IRQF_TRIGGER_FALLING|IRQF_ONESHOT, + p_client->name, p_tas2562); + if (n_result < 0) { + dev_err(p_tas2562->dev, + "request_irq failed, %d\n", n_result); + goto err; + } + disable_irq_nosync(p_tas2562->mn_irq2); + } + tas2562_enable_irq(p_tas2562, true); + INIT_DELAYED_WORK(&p_tas2562->init_work, init_work_routine); + +#ifdef CONFIG_TAS2562_CODEC + mutex_init(&p_tas2562->codec_lock); + n_result = tas2562_register_codec(p_tas2562); + if (n_result < 0) { + dev_err(p_tas2562->dev, + "register codec failed, %d\n", n_result); + goto err; + } +#endif + +#ifdef CONFIG_TAS2562_MISC + mutex_init(&p_tas2562->file_lock); + n_result = tas2562_register_misc(p_tas2562); + if (n_result < 0) { + dev_err(p_tas2562->dev, + "register codec failed, %d\n", n_result); + goto err; + } +#endif + +err: + return n_result; +} + +static int tas2562_i2c_remove(struct i2c_client *p_client) +{ + struct tas2562_priv *p_tas2562 = i2c_get_clientdata(p_client); + + dev_info(p_tas2562->dev, "%s\n", __func__); + +#ifdef CONFIG_TAS2562_CODEC + tas2562_deregister_codec(p_tas2562); + mutex_destroy(&p_tas2562->codec_lock); +#endif + +#ifdef CONFIG_TAS2562_MISC + tas2562_deregister_misc(p_tas2562); + mutex_destroy(&p_tas2562->file_lock); +#endif + + if (gpio_is_valid(p_tas2562->mn_reset_gpio)) + gpio_free(p_tas2562->mn_reset_gpio); + if (gpio_is_valid(p_tas2562->mn_irq_gpio)) + gpio_free(p_tas2562->mn_irq_gpio); + if (gpio_is_valid(p_tas2562->mn_reset_gpio2)) + gpio_free(p_tas2562->mn_reset_gpio2); + if (gpio_is_valid(p_tas2562->mn_irq_gpio2)) + gpio_free(p_tas2562->mn_irq_gpio2); + + return 0; +} + + +static const struct i2c_device_id tas2562_i2c_id[] = { + { "tas2562", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas2562_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id tas2562_of_match[] = { + { .compatible = "ti,tas2562" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tas2562_of_match); +#endif + +static const struct dev_pm_ops tas2562_pm_ops = { + .suspend = tas2562_pm_suspend, + .resume = tas2562_pm_resume +}; + +static struct i2c_driver tas2562_i2c_driver = { + .driver = { + .name = "tas2562", + .owner = THIS_MODULE, +#if defined(CONFIG_OF) + .of_match_table = of_match_ptr(tas2562_of_match), +#endif + .pm = &tas2562_pm_ops, + }, + .probe = tas2562_i2c_probe, + .remove = tas2562_i2c_remove, + .id_table = tas2562_i2c_id, +}; + +module_i2c_driver(tas2562_i2c_driver); + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("TAS2562 I2C Smart Amplifier driver"); +MODULE_LICENSE("GPL v2"); +#endif diff --git a/sound/soc/codecs/tas2562.h b/sound/soc/codecs/tas2562.h new file mode 100644 index 000000000000..78ba6de0c6f2 --- /dev/null +++ b/sound/soc/codecs/tas2562.h @@ -0,0 +1,706 @@ + +#ifndef __TAS2562_ +#define __TAS2562_ + +#define TAS2562_DRIVER_ID "2.1.0" + +/* Page Control Register */ +#define TAS2562_PAGECTL_REG 0 + +/* Book Control Register (available in page0 of each book) */ +#define TAS2562_BOOKCTL_PAGE 0 +#define TAS2562_BOOKCTL_REG 127 + +#define TAS2562_REG(book, page, reg) (((book * 256 * 128) + \ + (page * 128)) + reg) + +#define TAS2562_BOOK_ID(reg) (reg / (256 * 128)) + +#define TAS2562_PAGE_ID(reg) ((reg % (256 * 128)) / 128) + +#define TAS2562_BOOK_REG(reg) (reg % (256 * 128)) + +#define TAS2562_PAGE_REG(reg) ((reg % (256 * 128)) % 128) + + + /* Software Reset */ +#define TAS2562_SOFTWARERESET TAS2562_REG(0x0, 0x0, 0x01) +#define TAS2562_SOFTWARERESET_SOFTWARERESET_MASK (0x1 << 0), +#define TAS2562_SOFTWARERESET_SOFTWARERESET_DONTRESET (0x0 << 0) +#define TAS2562_SOFTWARERESET_SOFTWARERESET_RESET (0x1 << 0) + + /* Power Control */ +#define TAS2562_POWERCONTROL TAS2562_REG(0x0, 0x0, 0x02) +#define TAS2562_POWERCONTROL_ISNSPOWER_MASK (0x1 << 3) +#define TAS2562_POWERCONTROL_ISNSPOWER_ACTIVE (0x0 << 3) +#define TAS2562_POWERCONTROL_ISNSPOWER_POWEREDDOWN (0x1 << 3) +#define TAS2562_POWERCONTROL_VSNSPOWER_MASK (0x1 << 2) +#define TAS2562_POWERCONTROL_VSNSPOWER_ACTIVE (0x0 << 2) +#define TAS2562_POWERCONTROL_VSNSPOWER_POWEREDDOWN (0x1 << 2) +#define TAS2562_POWERCONTROL_OPERATIONALMODE10_MASK (0x3 << 0) +#define TAS2562_POWERCONTROL_OPERATIONALMODE10_ACTIVE (0x0 << 0) +#define TAS2562_POWERCONTROL_OPERATIONALMODE10_MUTE (0x1 << 0) +#define TAS2562_POWERCONTROL_OPERATIONALMODE10_SHUTDOWN (0x2 << 0) + + /* data format */ +#define TAS2562_DATAFORMAT_SHIFT 2 +#define TAS2562_DATAFORMAT_I2S 0x0 +#define TAS2562_DATAFORMAT_DSP 0x1 +#define TAS2562_DATAFORMAT_RIGHT_J 0x2 +#define TAS2562_DATAFORMAT_LEFT_J 0x3 + +#define TAS2562_DAI_FMT_MASK (0x7 << TAS2562_DATAFORMAT_SHIFT) + + /* Playback Configuration Reg0 */ +#define TAS2562_PLAYBACKCONFIGURATIONREG0 TAS2562_REG(0x0, 0x0, 0x03) +#define TAS2562_PLAYBACKCONFIGURATIONREG0_DCBLOCKER_MASK (0x1 << 6) +#define TAS2562_PLAYBACKCONFIGURATIONREG0_DCBLOCKER_ENABLED (0x0 << 6) +#define TAS2562_PLAYBACKCONFIGURATIONREG0_DCBLOCKER_DISABLED (0x1 << 6) +#define TAS2562_PLAYBACKCONFIGURATIONREG0_AMPLIFIERLEVEL51_MASK (0x1f << 1) + + /* Misc Configuration Reg0 */ +#define TAS2562_MISCCONFIGURATIONREG0 TAS2562_REG(0x0, 0x0, 0x04) +#define TAS2562_MISCCONFIGURATIONREG0_CPPGRETRY_MASK (0x1 << 7) +#define TAS2562_MISCCONFIGURATIONREG0_CPPGRETRY_DONOTRETRY (0x0 << 7) +#define TAS2562_MISCCONFIGURATIONREG0_CPPGRETRY_RETRY (0x1 << 7) +#define TAS2562_MISCCONFIGURATIONREG0_VBATPRORETRY_MASK (0x1 << 6) +#define TAS2562_MISCCONFIGURATIONREG0_VBATPRORETRY_DONOTRETRY (0x0 << 6) +#define TAS2562_MISCCONFIGURATIONREG0_VBATPRORETRY_RETRY (0x1 << 6) +#define TAS2562_MISCCONFIGURATIONREG0_OCERETRY_MASK (0x1 << 5) +#define TAS2562_MISCCONFIGURATIONREG0_OCERETRY_DONOTRETRY (0x0 << 5) +#define TAS2562_MISCCONFIGURATIONREG0_OCERETRY_RETRY (0x1 << 5) +#define TAS2562_MISCCONFIGURATIONREG0_OTERETRY_MASK (0x1 << 4) +#define TAS2562_MISCCONFIGURATIONREG0_OTERETRY_DONOTRETRY (0x0 << 4) +#define TAS2562_MISCCONFIGURATIONREG0_OTERETRY_RETRY (0x1 << 4) +#define TAS2562_MISCCONFIGURATIONREG0_IRQZPULL_MASK (0x1 << 3) +#define TAS2562_MISCCONFIGURATIONREG0_IRQZPULL_DISABLED (0x0 << 3) +#define TAS2562_MISCCONFIGURATIONREG0_IRQZPULL_ENABLED (0x1 << 3) +#define TAS2562_MISCCONFIGURATIONREG0_AMPSS_MASK (0x1 << 2) +#define TAS2562_MISCCONFIGURATIONREG0_AMPSS_DISABLED (0x0 << 2) +#define TAS2562_MISCCONFIGURATIONREG0_AMPSS_ENABLED (0x1 << 2) + + /* TDM Configuration Reg0 */ +#define TAS2562_TDMCONFIGURATIONREG0 TAS2562_REG(0x0, 0x0, 0x06) +#define TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_MASK (0x1 << 5) +#define TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_48KHZ (0x0 << 5) +#define TAS2562_TDMCONFIGURATIONREG0_SAMPRATERAMP_44_1KHZ (0x1 << 5) +#define TAS2562_TDMCONFIGURATIONREG0_DETECTSAMPRATE_MASK (0x1 << 4) +#define TAS2562_TDMCONFIGURATIONREG0_DETECTSAMPRATE_DISABLED (0x1 << 4) +#define TAS2562_TDMCONFIGURATIONREG0_DETECTSAMPRATE_ENABLED (0x0 << 4) +#define TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_MASK (0x7 << 1) +#define TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_14_7_16KHZ (0x1 << 1) +#define TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_44_1_48KHZ (0x4 << 1) +#define TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_88_2_96KHZ (0x5 << 1) +#define TAS2562_TDMCONFIGURATIONREG0_SAMPRATE31_176_4_192KHZ (0x6 << 1) +#define TAS2562_TDMCONFIGURATIONREG0_FRAMESTART_MASK (0x1 << 0) +#define TAS2562_TDMCONFIGURATIONREG0_FRAMESTART_LOWTOHIGH (0x0 << 0) +#define TAS2562_TDMCONFIGURATIONREG0_FRAMESTART_HIGHTOLOW (0x1 << 0) + + /* TDM Configuration Reg1 */ +#define TAS2562_TDMCONFIGURATIONREG1 TAS2562_REG(0x0, 0x0, 0x07) +#define TAS2562_TDMCONFIGURATIONREG1_RXJUSTIFY_MASK (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG1_RXJUSTIFY_LEFT (0x0 << 6) +#define TAS2562_TDMCONFIGURATIONREG1_RXJUSTIFY_RIGHT (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG1_RXOFFSET51_MASK (0x1f << 1) +#define TAS2562_TDMCONFIGURATIONREG1_RXOFFSET51_SHIFT (1) +#define TAS2562_TDMCONFIGURATIONREG1_RXEDGE_MASK (0x1 << 0) +#define TAS2562_TDMCONFIGURATIONREG1_RXEDGE_RISING (0x0 << 0) +#define TAS2562_TDMCONFIGURATIONREG1_RXEDGE_FALLING (0x1 << 0) + + /* TDM Configuration Reg2 */ +#define TAS2562_TDMCONFIGURATIONREG2 TAS2562_REG(0x0, 0x0, 0x08) +#define TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_MASK (0x3 << 4) +#define TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_MONO_I2C (0x0 << 4) +#define TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_MONO_LEFT (0x1 << 4) +#define TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_MONO_RIGHT (0x2 << 4) +#define TAS2562_TDMCONFIGURATIONREG2_RXSCFG54_STEREO_DOWNMIX (0x3 << 4) +#define TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_MASK (0x3 << 2) +#define TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_16BITS (0x0 << 2) +#define TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_20BITS (0x1 << 2) +#define TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_24BITS (0x2 << 2) +#define TAS2562_TDMCONFIGURATIONREG2_RXWLEN32_32BITS (0x3 << 2) +#define TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_MASK (0x3 << 0) +#define TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_16BITS (0x0 << 0) +#define TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_24BITS (0x1 << 0) +#define TAS2562_TDMCONFIGURATIONREG2_RXSLEN10_32BITS (0x2 << 0) + + /* TDM Configuration Reg3 */ +#define TAS2562_TDMCONFIGURATIONREG3 TAS2562_REG(0x0, 0x0, 0x09) +#define TAS2562_TDMCONFIGURATIONREG3_RXSLOTRIGHT74_MASK (0xf << 4) +#define TAS2562_TDMCONFIGURATIONREG3_RXSLOTLeft30_Mask (0xf << 0) + + /* TDM Configuration Reg4 */ +#define TAS2562_TDMConfigurationReg4 TAS2562_REG(0x0, 0x0, 0x0A) +#define TAS2562_TDMCONFIGURATIONREG4_TXKEEPER_MASK (0x1 << 5) +#define TAS2562_TDMCONFIGURATIONREG4_TXKEEPER_DISABLE (0x0 << 5) +#define TAS2562_TDMCONFIGURATIONREG4_TXKEEPER_ENABLE (0x1 << 5) +#define TAS2562_TDMCONFIGURATIONREG4_TXFILL_MASK (0x1 << 4) +#define TAS2562_TDMCONFIGURATIONREG4_TXFILL_TRANSMIT0 (0x0 << 4) +#define TAS2562_TDMCONFIGURATIONREG4_TXFILL_TRANSMITHIZ (0x1 << 4) +#define TAS2562_TDMCONFIGURATIONREG4_TXOFFSET31_MASK (0x7 << 1) +#define TAS2562_TDMCONFIGURATIONREG4_TXEDGE_MASK (0x1 << 0) +#define TAS2562_TDMCONFIGURATIONREG4_TXEDGE_RISING (0x0 << 0) +#define TAS2562_TDMCONFIGURATIONREG4_TXEDGE_FALLING (0x1 << 0) + + /* TDM Configuration Reg5 */ +#define TAS2562_TDMCONFIGURATIONREG5 TAS2562_REG(0x0, 0x0, 0x0B) +#define TAS2562_TDMCONFIGURATIONREG5_VSNSTX_MASK (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG5_VSNSTX_DISABLE (0x0 << 6), +#define TAS2562_TDMCONFIGURATIONREG5_VSNSTX_ENABLE (0x1 << 6), +#define TAS2562_TDMCONFIGURATIONREG5_VSNSSLOT50_MASK (0x3f << 0) + + /* TDM Configuration Reg6 */ +#define TAS2562_TDMCONFIGURATIONREG6 TAS2562_REG(0x0, 0x0, 0x0C) +#define TAS2562_TDMCONFIGURATIONREG6_ISNSTX_MASK (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG6_ISNSTX_DISABLE (0x0 << 6), +#define TAS2562_TDMCONFIGURATIONREG6_ISNSTX_ENABLE (0x1 << 6), +#define TAS2562_TDMCONFIGURATIONREG6_ISNSSLOT50_MASK (0x3f << 0) + + /* TDM Configuration Reg7 */ +#define TAS2562_TDMCONFIGURATIONREG7 TAS2562_REG(0x0, 0x0, 0x0D) +#define TAS2562_TDMCONFIGURATIONREG7_VBATSLEN_MASK (0x1 << 7) +#define TAS2562_TDMCONFIGURATIONREG7_VBATSLEN_8BITS (0x0 << 7) +#define TAS2562_TDMCONFIGURATIONREG7_VBATSLEN_16BITS (0x1 << 7) +#define TAS2562_TDMCONFIGURATIONREG7_VBATTX_MASK (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG7_VBATTX_DISABLE (0x0 << 6) +#define TAS2562_TDMCONFIGURATIONREG7_VBATTX_ENABLE (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG7_VBATSLOT50_MASK (0x3f << 0) + + /* TDM Configuration Reg8 */ +#define TAS2562_TDMCONFIGURATIONREG8 TAS2562_REG(0x0, 0x0, 0x0E) +#define TAS2562_TDMCONFIGURATIONREG8_TEMPTX_MASK (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG8_TEMPTX_DISABLE (0x0 << 6) +#define TAS2562_TDMCONFIGURATIONREG8_TEMPTX_ENABLE (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG8_TEMPSLOT50_MASK (0x3f << 0) + + /* TDM Configuration Reg9 */ +#define TAS2562_TDMCONFIGURATIONREG9 TAS2562_REG(0x0, 0x0, 0x0F) +#define TAS2562_TDMCONFIGURATIONREG9_GAINTX_MASK (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG9_GAINTX_DISABLE (0x0 << 6) +#define TAS2562_TDMCONFIGURATIONREG9_GAINTX_ENABLE (0x1 << 6) +#define TAS2562_TDMCONFIGURATIONREG9_GAINSLOT50_MASK (0x3f << 0) + + /* Limiter Configuration Reg0 */ +#define TAS2562_LIMITERCONFIGURATIONREG0 TAS2562_REG(0x0, 0x0, 0x12) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKST54_MASK (0x3 << 4) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKST54_1 (0x2 << 4) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKST54_2 (0x3 << 4) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKST54_0_25 (0x0 << 4) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKST54_0_5 (0x1 << 4) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKRT31_MASK (0x7 << 1) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKRT31_5 (0x0 << 1) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKRT31_10 (0x1 << 1) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKRT31_20 (0x2 << 1) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKRT31_40 (0x3 << 1) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKRT31_80 (0x4 << 1) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKRT31_160 (0x5 << 1) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKRT31_320 (0x6 << 1) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMATKRT31_640 (0x7 << 1) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMEN_MASK (0x1 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMEN_DISABLED (0x0 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG0_LIMEN_ENABLED (0x1 << 0) + + /* Limiter Configuration Reg1 */ +#define TAS2562_LIMITERCONFIGURATIONREG1 TAS2562_REG(0x0, 0x0, 0x13) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSST76_MASK (0x3 << 6) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSST76_1 (0x2 << 6) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSST76_2 (0x3 << 6) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSST76_0_25 (0x0 << 6) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSST76_0_5 (0x1 << 6) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSRT53_MASK (0x7 << 3) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSRT53_10 (0x0 << 3) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSRT53_50 (0x1 << 3) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSRT53_100 (0x2 << 3) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSRT53_250 (0x3 << 3) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSRT53_500 (0x4 << 3) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSRT53_750 (0x5 << 3) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSRT53_1000 (0x6 << 3) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMRLSRT53_1500 (0x7 << 3) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMHLDTM20_MASK (0x7 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMHLDTM20_0 (0x0 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMHLDTM20_10 (0x1 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMHLDTM20_25 (0x2 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMHLDTM20_50 (0x3 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMHLDTM20_100 (0x4 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMHLDTM20_250 (0x5 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMHLDTM20_500 (0x6 << 0) +#define TAS2562_LIMITERCONFIGURATIONREG1_LIMHLDTM20_1000 (0x7 << 0) + + /* Brown Out Prevention Reg0 */ +#define TAS2562_BROWNOUTPREVENTIONREG0 TAS2562_REG(0x0, 0x0, 0x14) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPSDEN_MASK (0x1 << 4) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPSDEN_DISABLED (0x0 << 4) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPSDEN_ENABLED (0x1 << 4) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPHLDCLR_MASK (0x1 << 3) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPHLDCLR_DONTCLEAR (0x0 << 3) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPHLDCLR_CLEAR (0x1 << 3) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPINFHLD_MASK (0x1 << 2) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPINFHLD_USEHOLDTIME (0x0 << 2) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPINFHLD_HOLDUNTILCLEARED (0x1 << 2) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPMUTE_MASK (0x1 << 1) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPMUTE_DONOTMUTE (0x0 << 1) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPMUTE_MUTE (0x1 << 1) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPEN_MASK (0x1 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPEN_DISABLED (0x0 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG0_BOPEN_ENABLED (0x1 << 0) + + /* Brown Out Prevention Reg1 */ +#define TAS2562_BROWNOUTPREVENTIONREG1 TAS2562_REG(0x0, 0x0, 0x15) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKRT75_MASK (0x7 << 5) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKRT75_1 (0x0 << 5) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKRT75_2 (0x1 << 5) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKRT75_4 (0x2 << 5) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKRT75_8 (0x3 << 5) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKRT75_16 (0x4 << 5) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKRT75_32 (0x5 << 5) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKRT75_64 (0x6 << 5) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKRT75_128 (0x7 << 5) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKST43_MASK (0x3 << 3) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKST43_1 (0x1 << 3) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKST43_2 (0x3 << 3) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKST43_0_5 (0x0 << 3) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPATKST43_1_5 (0x2 << 3) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPHLDTM20_MASK (0x7 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPHLDTM20_0 (0x0 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPHLDTM20_10 (0x1 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPHLDTM20_25 (0x2 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPHLDTM20_50 (0x3 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPHLDTM20_100 (0x4 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPHLDTM20_250 (0x5 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPHLDTM20_500 (0x6 << 0) +#define TAS2562_BROWNOUTPREVENTIONREG1_BOPHLDTM20_1000 (0x7 << 0) + +/* Interrupt Mask Reg0 */ +#define TAS2562_INTERRUPTMASKREG0 TAS2562_REG(0x0, 0x0, 0x1A) +#define TAS2562_INTERRUPTMASKREG0_LIMMUTEINTMASK_MASK (0x1 << 7) +#define TAS2562_INTERRUPTMASKREG0_LIMMUTEINTMASK_UNMASK (0x0 << 7) +#define TAS2562_INTERRUPTMASKREG0_LIMMUTEINTMASK_DISABLE (0x1 << 7) +#define TAS2562_INTERRUPTMASKREG0_LIMINFHLDINTMASK_MASK (0x1 << 6) +#define TAS2562_INTERRUPTMASKREG0_LIMINFHLDINTMASK_UNMASK (0x0 << 6) +#define TAS2562_INTERRUPTMASKREG0_LIMINFHLDINTMASK_DISABLE (0x1 << 6) +#define TAS2562_INTERRUPTMASKREG0_LIMMAXATNINTMASK_MASK (0x1 << 5) +#define TAS2562_INTERRUPTMASKREG0_LIMMAXATNINTMASK_UNMASK (0x0 << 5) +#define TAS2562_INTERRUPTMASKREG0_LIMMAXATNINTMASK_DISABLE (0x1 << 5) +#define TAS2562_INTERRUPTMASKREG0_VBATLESSTHANINFINTMASK_MASK (0x1 << 4) +#define TAS2562_INTERRUPTMASKREG0_VBATLESSTHANINFINTMASK_UNMASK (0x0 << 4) +#define TAS2562_INTERRUPTMASKREG0_VBATLESSTHANINFINTMASK_DISABLE (0x1 << 4) +#define TAS2562_INTERRUPTMASKREG0_LIMACTIVEFLAGINTMASK_MASK (0x1 << 3) +#define TAS2562_INTERRUPTMASKREG0_LIMACTIVEFLAGINTMASK_UNMASK (0x0 << 3) +#define TAS2562_INTERRUPTMASKREG0_LIMACTIVEFLAGINTMASK_DISABLE (0x1 << 3) +#define TAS2562_INTERRUPTMASKREG0_TDMCLOCKERRORINTMASK_MASK (0x1 << 2) +#define TAS2562_INTERRUPTMASKREG0_TDMCLOCKERRORINTMASK_UNMASK (0x0 << 2) +#define TAS2562_INTERRUPTMASKREG0_TDMCLOCKERRORINTMASK_DISABLE (0x1 << 2) +#define TAS2562_INTERRUPTMASKREG0_OCEINTMASK_MASK (0x1 << 1) +#define TAS2562_INTERRUPTMASKREG0_OCEINTMASK_UNMASK (0x0 << 1) +#define TAS2562_INTERRUPTMASKREG0_OCEINTMASK_DISABLE (0x1 << 1) +#define TAS2562_INTERRUPTMASKREG0_OTEINTMASK_MASK (0x1 << 0) +#define TAS2562_INTERRUPTMASKREG0_OTEINTMASK_UNMASK (0x0 << 0) +#define TAS2562_INTERRUPTMASKREG0_OTEINTMASK_DISABLE (0x1 << 0) +#define TAS2562_INTERRUPTMASKREG0_DISABLE 0xff + + /* Interrupt Mask Reg1 */ +#define TAS2562_INTERRUPTMASKREG1 TAS2562_REG(0x0, 0x0, 0x1B) +#define TAS2562_INTERRUPTMASKREG1_DSPOUTPUTINTMASK_MASK (0x1 << 7) +#define TAS2562_INTERRUPTMASKREG1_DSPOUTPUTINTMASK_UNMASK (0x0 << 7) +#define TAS2562_INTERRUPTMASKREG1_DSPOUTPUTINTMASK_DISABLE (0x1 << 7) +#define TAS2562_INTERRUPTMASKREG1_CRCINTMASK_MASK (0x1 << 6) +#define TAS2562_INTERRUPTMASKREG1_CRCINTMASK_UNMASK (0x0 << 6) +#define TAS2562_INTERRUPTMASKREG1_CRCINTMASK_DISABLE (0x1 << 6) +#define TAS2562_INTERRUPTMASKREG1_VBATOVLOINTMASK_MASK (0x1 << 2) +#define TAS2562_INTERRUPTMASKREG1_VBATOVLOINTMASK_UNMASK (0x0 << 2) +#define TAS2562_INTERRUPTMASKREG1_VBATOVLOINTMASK_DISABLE (0x1 << 2) +#define TAS2562_INTERRUPTMASKREG1_VBATUVLOINTMASK_MASK (0x1 << 1) +#define TAS2562_INTERRUPTMASKREG1_VBATUVLOINTMASK_UNMASK (0x0 << 1) +#define TAS2562_INTERRUPTMASKREG1_VBATUVLOINTMASK_DISABLE (0x1 << 1) +#define TAS2562_INTERRUPTMASKREG1_BROWNOUTFLAGINTMASK_MASK (0x1 << 0) +#define TAS2562_INTERRUPTMASKREG1_BROWNOUTFLAGINTMASK_UNMASK (0x0 << 0) +#define TAS2562_INTERRUPTMASKREG1_BROWNOUTFLAGINTMASK_DISABLE (0x1 << 0) +#define TAS2562_INTERRUPTMASKREG1_DISABLE 0xff + + /* Interrupt Mask Reg2 */ +#define TAS2562_INTERRUPTMASKREG2 TAS2562_REG(0x0, 0x0, 0x1C) +#define TAS2562_INTERRUPTMASKREG2_DACLKINTMASK_MASK (0x1 << 7) +#define TAS2562_INTERRUPTMASKREG2_DACLKINTMASK_UNMASK (0x0 << 7) +#define TAS2562_INTERRUPTMASKREG2_DACLKINTMASK_DISABLE (0x1 << 7) +#define TAS2562_INTERRUPTMASKREG2_BSTCLKINTMASK_MASK (0x1 << 6) +#define TAS2562_INTERRUPTMASKREG2_BSTCLKINTMASK_UNMASK (0x0 << 6) +#define TAS2562_INTERRUPTMASKREG2_BSTCLKINTMASK_DISABLE (0x1 << 6) +#define TAS2562_INTERRUPTMASKREG2_VBATPORCLKINTMASK_MASK (0x1 << 5) +#define TAS2562_INTERRUPTMASKREG2_VBATPORCLKINTMASK_UNMASK (0x0 << 5) +#define TAS2562_INTERRUPTMASKREG2_VBATPORCLKINTMASK_DISABLE (0x1 << 5) +#define TAS2562_INTERRUPTMASKREG2_PLLOCKINTMASK_MASK (0x1 << 4) +#define TAS2562_INTERRUPTMASKREG2_PLLOCKINTMASK_UNMASK (0x0 << 4) +#define TAS2562_INTERRUPTMASKREG2_PLLOCKINTMASK_DISABLE (0x1 << 4) +#define TAS2562_INTERRUPTMASKREG2_DCDETECTINTMASK_MASK (0x1 << 3) +#define TAS2562_INTERRUPTMASKREG2_DCDETECTINTMASK_UNMASK (0x0 << 3) +#define TAS2562_INTERRUPTMASKREG2_DCDETECTINTMASK_DISABLE (0x1 << 3) + + /* Live-Interrupt Reg0 */ +#define TAS2562_LIVEINTERRUPTREG0 TAS2562_REG(0x0, 0x0, 0x1F) +#define TAS2562_LIVEINTERRUPTREG0_LIMMUTE_MASK (0x1 << 7) +#define TAS2562_LIVEINTERRUPTREG0_LIMMUTE_NOINTERRUPT (0x0 << 7) +#define TAS2562_LIVEINTERRUPTREG0_LIMMUTE_INTERRUPT (0x1 << 7) +#define TAS2562_LIVEINTERRUPTREG0_LIMINFHLD_MASK (0x1 << 6) +#define TAS2562_LIVEINTERRUPTREG0_LIMINFHLD_NOINTERRUPT (0x0 << 6) +#define TAS2562_LIVEINTERRUPTREG0_LIMINFHLD_INTERRUPT (0x1 << 6) +#define TAS2562_LIVEINTERRUPTREG0_LIMMAXATN_MASK (0x1 << 5) +#define TAS2562_LIVEINTERRUPTREG0_LIMMAXATN_NOINTERRUPT (0x0 << 5) +#define TAS2562_LIVEINTERRUPTREG0_LIMMAXATN_INTERRUPT (0x1 << 5) +#define TAS2562_LIVEINTERRUPTREG0_VBATLESSTHANINF_MASK (0x1 << 4) +#define TAS2562_LIVEINTERRUPTREG0_VBATLESSTHANINF_NOINTERRUPT (0x0 << 4) +#define TAS2562_LIVEINTERRUPTREG0_VBATLESSTHANINF_INTERRUPT (0x1 << 4) +#define TAS2562_LIVEINTERRUPTREG0_LIMACTIVEFLAG_MASK (0x1 << 3) +#define TAS2562_LIVEINTERRUPTREG0_LIMACTIVEFLAG_NOINTERRUPT (0x0 << 3) +#define TAS2562_LIVEINTERRUPTREG0_LIMACTIVEFLAG_INTERRUPT (0x1 << 3) +#define TAS2562_LIVEINTERRUPTREG0_TDMCLOCKERROR_MASK (0x1 << 2) +#define TAS2562_LIVEINTERRUPTREG0_TDMCLOCKERROR_NOINTERRUPT (0x0 << 2) +#define TAS2562_LIVEINTERRUPTREG0_TDMCLOCKERROR_INTERRUPT (0x1 << 2) +#define TAS2562_LIVEINTERRUPTREG0_OCEFLAG_MASK (0x1 << 1) +#define TAS2562_LIVEINTERRUPTREG0_OCEFLAG_NOINTERRUPT (0x0 << 1) +#define TAS2562_LIVEINTERRUPTREG0_OCEFLAG_INTERRUPT (0x1 << 1) +#define TAS2562_LIVEINTERRUPTREG0_OTEFLAG_MASK (0x1 << 0) +#define TAS2562_LIVEINTERRUPTREG0_OTEFLAG_NOINTERRUPT (0x0 << 0) +#define TAS2562_LIVEINTERRUPTREG0_OTEFLAG_INTERRUPT (0x1 << 0) + + /* Live-Interrupt Reg1 */ +#define TAS2562_LIVEINTERRUPTREG1 TAS2562_REG(0x0, 0x0, 0x20) +#define TAS2562_LIVEINTERRUPTREG1_DSPINTOUTPUT_MASK (0x1 << 7) +#define TAS2562_LIVEINTERRUPTREG1_DSPINTOUTPUT_NOINTERRUPT (0x0 << 7) +#define TAS2562_LIVEINTERRUPTREG1_DSPINTOUTPUT_INTERRUPT (0x1 << 7) +#define TAS2562_LIVEINTERRUPTREG1_OTPCRC_MASK (0x1 << 6) +#define TAS2562_LIVEINTERRUPTREG1_OTPCRC_NOINTERRUPT (0x0 << 6) +#define TAS2562_LIVEINTERRUPTREG1_OTPCRC_INTERRUPT (0x1 << 6) +#define TAS2562_LIVEINTERRUPTREG1_BROWNOUTFLAG_MASK (0x1 << 1) +#define TAS2562_LIVEINTERRUPTREG1_BROWNOUTFLAG_NOINTERRUPT (0x0 << 1) +#define TAS2562_LIVEINTERRUPTREG1_BROWNOUTFLAG_INTERRUPT (0x1 << 1) +#define TAS2562_LIVEINTERRUPTREG1_BROWNOUTDETECTED_MASK (0x1 << 1) +#define TAS2562_LIVEINTERRUPTREG1_BROWNOUTDETECTED_NOINTERRUPT (0x0 << 1) +#define TAS2562_LIVEINTERRUPTREG1_BROWNOUTDETECTED_INTERRUPT (0x1 << 1) + + /* Latched-Interrupt Reg0 */ +#define TAS2562_LATCHEDINTERRUPTREG0 TAS2562_REG(0x0, 0x0, 0x24) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMMUTESTICKY_MASK (0x1 << 7) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMMUTESTICKY_NOINTERRUPT (0x0 << 7) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMMUTESTICKY_INTERRUPT (0x1 << 7) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMINFHLDSTICKY_MASK (0x1 << 6) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMINFHLDSTICKY_NOINTERRUPT (0x0 << 6) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMINFHLDSTICKY_INTERRUPT (0x1 << 6) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMMAXATNSTICKY_MASK (0x1 << 5) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMMAXATNSTICKY_NOINTERRUPT (0x0 << 5) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMMAXATNSTICKY_INTERRUPT (0x1 << 5) +#define TAS2562_LATCHEDINTERRUPTREG0_VBATLESSTHANINFSTICKY_MASK (0x1 << 4) +#define TAS2562_LATCHEDINTERRUPTREG0_VBATLESSTHANINFSTICKY_NOINTERRUPT \ + (0x0 << 4) +#define TAS2562_LATCHEDINTERRUPTREG0_VBATLESSTHANINFSTICKY_INTERRUPT (0x1 << 4) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMACTIVEFLAGSTICKY_MASK (0x1 << 3) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMACTIVEFLAGSTICKY_NOINTERRUPT (0x0 << 3) +#define TAS2562_LATCHEDINTERRUPTREG0_LIMACTIVEFLAGSTICKY_INTERRUPT (0x1 << 3) +#define TAS2562_LATCHEDINTERRUPTREG0_TDMCLOCKERRORSTICKY_MASK (0x1 << 2) +#define TAS2562_LATCHEDINTERRUPTREG0_TDMCLOCKERRORSTICKY_NOINTERRUPT (0x0 << 2) +#define TAS2562_LATCHEDINTERRUPTREG0_TDMCLOCKERRORSTICKY_INTERRUPT (0x1 << 2) +#define TAS2562_LATCHEDINTERRUPTREG0_OCEFLAGSTICKY_MASK (0x1 << 1) +#define TAS2562_LATCHEDINTERRUPTREG0_OCEFLAGSTICKY_NOINTERRUPT (0x0 << 1) +#define TAS2562_LATCHEDINTERRUPTREG0_OCEFLAGSTICKY_INTERRUPT (0x1 << 1) +#define TAS2562_LATCHEDINTERRUPTREG0_OTEFLAGSTICKY_MASK (0x1 << 0) +#define TAS2562_LATCHEDINTERRUPTREG0_OTEFLAGSTICKY_NOINTERRUPT (0x0 << 0) +#define TAS2562_LATCHEDINTERRUPTREG0_OTEFLAGSTICKY_INTERRUPT (0x1 << 0) + + /* Latched-Interrupt Reg1 */ +#define TAS2562_LATCHEDINTERRUPTREG1 TAS2562_REG(0x0, 0x0, 0x25) +#define TAS2562_LATCHEDINTERRUPTREG1_PDMAUDDATAINVALIDSTICKY_MASK (0x1 << 7) +#define TAS2562_LATCHEDINTERRUPTREG1_PDMAUDDATAINVALIDSTICKY_NOINTERRUPT \ + (0x0 << 7) +#define TAS2562_LATCHEDINTERRUPTREG1_PDMAUDDATAINVALIDSTICKY_INTERRUPT \ + (0x1 << 7) +#define TAS2562_LATCHEDINTERRUPTREG1_VBATOVLOSTICKY_MASK (0x1 << 3) +#define TAS2562_LATCHEDINTERRUPTREG1_VBATOVLOSTICKY_NOINTERRUPT (0x0 << 3) +#define TAS2562_LATCHEDINTERRUPTREG1_VBATOVLOSTICKY_INTERRUPT (0x1 << 3) +#define TAS2562_LATCHEDINTERRUPTREG1_VBATUVLOSTICKY_MASK (0x1 << 2) +#define TAS2562_LATCHEDINTERRUPTREG1_VBATUVLOSTICKY_NOINTERRUPT (0x0 << 2) +#define TAS2562_LATCHEDINTERRUPTREG1_VBATUVLOSTICKY_INTERRUPT (0x1 << 2) +#define TAS2562_LATCHEDINTERRUPTREG1_BROWNOUTFLAGSTICKY_MASK (0x1 << 1) +#define TAS2562_LATCHEDINTERRUPTREG1_BROWNOUTFLAGSTICKY_NOINTERRUPT (0x0 << 1) +#define TAS2562_LATCHEDINTERRUPTREG1_BROWNOUTFLAGSTICKY_INTERRUPT (0x1 << 1) +#define TAS2562_LATCHEDINTERRUPTREG1_PDMCLOCKERRORSTICKY_MASK (0x1 << 0) +#define TAS2562_LATCHEDINTERRUPTREG1_PDMCLOCKERRORSTICKY_NOINTERRUPT (0x0 << 0) +#define TAS2562_LATCHEDINTERRUPTREG1_PDMCLOCKERRORSTICKY_INTERRUPT (0x1 << 0) + + /* VBAT MSB */ +#define TAS2562_VBATMSB TAS2562_REG(0x0, 0x0, 0x2A) +#define TAS2562_VBATMSB_VBATMSB70_MASK (0xff << 0) + + /* VBAT LSB */ +#define TAS2562_VBATLSB TAS2562_REG(0x0, 0x0, 0x2B) +#define TAS2562_VBATLSB_VBATLSB74_MASK (0xf << 4) + + /* TEMP */ +#define TAS2562_TEMP TAS2562_REG(0x0, 0x0, 0x2C) +#define TAS2562_TEMP_TEMPMSB70_MASK (0xff << 0) + + + /* Interrupt Configuration */ +#define TAS2562_INTERRUPTCONFIGURATION TAS2562_REG(0x0, 0x0, 0x30) +#define TAS2562_INTERRUPTCONFIGURATION_LTCHINTCLEAR_MASK (0x1 << 2) +#define TAS2562_INTERRUPTCONFIGURATION_LTCHINTCLEAR (0x1 << 2) +#define TAS2562_INTERRUPTCONFIGURATION_PININTCONFIG10_MASK (0x3 << 0) +#define TAS2562_INTERRUPTCONFIGURATION_PININTCONFIG10_ASSERTONLIVEINTERRUPTS \ + (0x0 << 0) +#define \ +TAS2562_INTERRUPTCONFIGURATION_PININTCONFIG10_ASSERTONLATCHEDINTERRUPTS \ + (0x1 << 0) +#define \ +TAS2562_INTERRUPTCONFIGURATION_PININTCONFIG10_ASSERT2MSONLIVEINTERRUPTS \ + (0x2 << 0) +#define \ +TAS2562_INTERRUPTCONFIGURATION_PININTCONFIG10_ASSERT2MSONLATCHEDINTERRUPTS \ + (0x3 << 0) + + /* Digital Input Pin Pull Down */ +#define TAS2562_DIGITALINPUTPINPULLDOWN TAS2562_REG(0x0, 0x0, 0x31) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNSDOUT_MASK (0x1 << 7) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNSDOUT_DISABLED (0x0 << 7) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNSDOUT_ENABLED (0x1 << 7) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNSDIN_MASK (0x1 << 6) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNSDIN_DISABLED (0x0 << 6) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNSDIN_ENABLED (0x1 << 6) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNFSYNC_MASK (0x1 << 5) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNFSYNC_DISABLED (0x0 << 5) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNFSYNC_ENABLED (0x1 << 5) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNSBCLK_MASK (0x1 << 4) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNSBCLK_DISABLED (0x0 << 4) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNSBCLK_ENABLED (0x1 << 4) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMD0_MASK (0x1 << 3) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMD0_DISABLED (0x0 << 3) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMD0_ENABLED (0x1 << 3) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMD1_MASK (0x1 << 2) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMD1_DISABLED (0x0 << 2) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMD1_ENABLED (0x1 << 2) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMCK0_MASK (0x1 << 1) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMCK0_DISABLED (0x0 << 1) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMCK0_ENABLED (0x1 << 1) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMCK1_MASK (0x1 << 0) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMCK1_DISABLED (0x0 << 0) +#define TAS2562_DIGITALINPUTPINPULLDOWN_WKPULLDOWNPDMCK1_ENABLED (0x1 << 0) + + /* Misc IRQ */ +#define TAS2562_MISCIRQ TAS2562_REG(0x0, 0x0, 0x32) +#define TAS2562_MISCIRQ_IRQZREQD_MASK (0x1 << 7) +#define TAS2562_MISCIRQ_IRQZREQD_ACTIVEHIGH (0x0 << 7) +#define TAS2562_MISCIRQ_IRQZREQD_ACTIVELOW (0x1 << 7) +#define TAS2562_MISCIRQ_IRQZBITBANG_MASK (0x1 << 0) +#define TAS2562_MISCIRQ_IRQZBITBANG_IRQZINPUTBUF0 (0x0 << 0) +#define TAS2562_MISCIRQ_IRQZBITBANG_IRQZINPUTBUF1 (0x1 << 0) + +#define TAS2562_BOOSTCONFIGURATION2 TAS2562_REG(0x0, 0x0, 0x34) +#define TAS2562_BOOSTCONFIGURATION2_BOOSTMAXVOLTAGE_MASK (0x0f << 0) + +#define TAS2562_BOOSTSLOPE TAS2562_REG(0x0, 0x0, 0x35) +#define TAS2562_BOOSTSLOPE_MASK (0x3 << 2) +#define TAS2562_BOOSTSLOPE_3AV (0x1 << 2) +#define TAS2562_BOOSTSLOPE_2AV (0x2 << 2) + + /* Clock Configuration */ +#define TAS2562_CLOCKCONFIGURATION TAS2562_REG(0x0, 0x0, 0x38) +#define TAS2562_CLOCKCONFIGURATION_SBCLKTOFS52_MASK (0xf << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_16 (0x0 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_24 (0x1 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_32 (0x2 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_48 (0x3 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_64 (0x4 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_96 (0x5 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_128 (0x6 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_192 (0x7 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_256 (0x8 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_384 (0x9 << 2) +#define TAS2562_CLOCKCONFIGURATION_SBCLKtoFS52_512 (0xa << 2) +#define TAS2562_CLOCKCONFIGURATION_DISCLKRATEDETECT10_MASK (0x3 << 0) +#define TAS2562_CLOCKCONFIGURATION_DISCLKRATEDETECT10_DISABLED (0x1 << 0) +#define TAS2562_CLOCKCONFIGURATION_DISCLKRATEDETECT10_ENABLED (0x0 << 0) + +#define TAS2562_VBATFILTER TAS2562_REG(0x0, 0x0, 0x3b) +#define TAS2562_CLASSHRELEASETIMER TAS2562_REG(0x0, 0x0, 0x3c) + +#define TAS2562_ICN_REG TAS2562_REG(0x0, 0x2, 0x64) + +#define TAS2562_DVC_REG TAS2562_REG(0x0, 0x2, 0x0c) +#define TAS2562_DVC_CFG1_REG TAS2562_REG(0x0, 0x2, 0x0c) +#define TAS2562_DVC_CFG2_REG TAS2562_REG(0x0, 0x2, 0x0d) +#define TAS2562_DVC_CFG3_REG TAS2562_REG(0x0, 0x2, 0x0e) +#define TAS2562_DVC_CFG4_REG TAS2562_REG(0x0, 0x2, 0x0f) + +#define TAS2562_TESTPAGECONFIGURATION TAS2562_REG(0x0, 0xfd, 0xd) +#define TAS2562_CLASSDCONFIGURATION1 TAS2562_REG(0x0, 0xfd, 0x19) +#define TAS2562_CLASSDCONFIGURATION2 TAS2562_REG(0x0, 0xfd, 0x32) +#define TAS2562_CLASSDCONFIGURATION3 TAS2562_REG(0x0, 0xfd, 0x33) +#define TAS2562_CLASSDCONFIGURATION4 TAS2562_REG(0x0, 0xfd, 0x3f) +#define TAS2562_EFFICIENCYCONFIGURATION TAS2562_REG(0x0, 0xfd, 0x5f) + +#define TAS2562_CLASSHHEADROOM TAS2562_REG(0x64, 0x7, 0x48) +#define TAS2562_CLASSHHYSTERESIS TAS2562_REG(0x64, 0x7, 0x4c) +#define TAS2562_CLASSHMTCT TAS2562_REG(0x64, 0x5, 0x4c) + + /* Revision and PG ID */ +#define TAS2562_REVISIONANDPGID TAS2562_REG(0x0, 0x0, 0x7D) +#define TAS2562_REVISIONANDPGID_REVISIONID74_MASK (0xf << 4) +#define TAS2562_REVISIONANDPGID_PGID30_MASK (0xf << 0) + + /* I2C Checksum */ +#define TAS2562_I2CCHECKSUM TAS2562_REG(0x0, 0x0, 0x7E) +#define TAS2562_I2CCHECKSUM_I2CCHECKSUM70_MASK (0xff << 0) + + /* Book */ +#define TAS2562_BOOK TAS2562_REG(0x0, 0x0, 0x7F) +#define TAS2562_BOOK_BOOK70_MASK (0xff << 0) + + /* Brown out Prevention */ +#define TAS2562_BROWNOUT_LIM_CFG1 TAS2562_REG(0x0, 0x0, 0x13) +#define TAS2562_BROWNOUT_LIM_MAX_ATN0 TAS2562_REG(0x0, 0x02, 0x14) +#define TAS2562_BROWNOUT_LIM_MAX_ATN1 TAS2562_REG(0x0, 0x02, 0x15) +#define TAS2562_BROWNOUT_LIM_MAX_ATN2 TAS2562_REG(0x0, 0x02, 0x16) +#define TAS2562_BROWNOUT_LIM_MAX_ATN3 TAS2562_REG(0x0, 0x02, 0x17) + +#define TAS2562_BROWNOUT_LIMB_BOP_TH0 TAS2562_REG(0x0, 0x02, 0x2B) +#define TAS2562_BROWNOUT_LIMB_BOP_TH1 TAS2562_REG(0x0, 0x02, 0x2A) +#define TAS2562_BROWNOUT_LIMB_BOP_TH2 TAS2562_REG(0x0, 0x02, 0x29) +#define TAS2562_BROWNOUT_LIMB_BOP_TH3 TAS2562_REG(0x0, 0x02, 0x28) + +#define TAS2562_BROWNOUT_LIMB_SLOPE0 TAS2562_REG(0x0, 0x02, 0x27) +#define TAS2562_BROWNOUT_LIMB_SLOPE1 TAS2562_REG(0x0, 0x02, 0x26) +#define TAS2562_BROWNOUT_LIMB_SLOPE2 TAS2562_REG(0x0, 0x02, 0x25) +#define TAS2562_BROWNOUT_LIMB_SLOPE3 TAS2562_REG(0x0, 0x02, 0x24) + +#define TAS2562_BROWNOUT_LIMB_INF_PT0 TAS2562_REG(0x0, 0x02, 0x23) +#define TAS2562_BROWNOUT_LIMB_INF_PT1 TAS2562_REG(0x0, 0x02, 0x22) +#define TAS2562_BROWNOUT_LIMB_INF_PT2 TAS2562_REG(0x0, 0x02, 0x21) +#define TAS2562_BROWNOUT_LIMB_INF_PT3 TAS2562_REG(0x0, 0x02, 0x20) + +#define TAS2562_BROWNOUT_LIMB_TH_MIN0 TAS2562_REG(0x0, 0x02, 0x1F) +#define TAS2562_BROWNOUT_LIMB_TH_MIN1 TAS2562_REG(0x0, 0x02, 0x1E) +#define TAS2562_BROWNOUT_LIMB_TH_MIN2 TAS2562_REG(0x0, 0x02, 0x1D) +#define TAS2562_BROWNOUT_LIMB_TH_MIN3 TAS2562_REG(0x0, 0x02, 0x1C) + +#define TAS2562_BROWNOUT_LIMB_TH_MAX0 TAS2562_REG(0x0, 0x02, 0x1B) +#define TAS2562_BROWNOUT_LIMB_TH_MAX1 TAS2562_REG(0x0, 0x02, 0x1A) +#define TAS2562_BROWNOUT_LIMB_TH_MAX2 TAS2562_REG(0x0, 0x02, 0x09) +#define TAS2562_BROWNOUT_LIMB_TH_MAX3 TAS2562_REG(0x0, 0x02, 0x08) + +#define TAS2562_BROWNOUT_VBAT_CONVERT(arg, mask, shift) (((arg << 27) & mask) >> shift) +#define TAS2562_BROWNOUT_LIMB_INF_PT_CONVERT(arg, mask, shift) (((arg << 28) & mask) >> shift) +#define TAS2562_BROWNOUT_LIMB_SLOPE_CONVERT(arg, mask, shift) (((arg << 28) & mask) >> shift) +#define TAS2562_BROWNOUT_LIMB_BPO_TH_CONVERT(arg, mask, shift) (((arg << 28) & mask) >> shift) + + +#define TAS2562_POWER_ACTIVE 0 +#define TAS2562_POWER_MUTE 1 +#define TAS2562_POWER_SHUTDOWN 2 + +#define ERROR_NONE 0x0000000 +#define ERROR_PLL_ABSENT 0x0000000 +#define ERROR_DEVA_I2C_COMM 0x0000000 +#define ERROR_DEVB_I2C_COMM 0x0000000 +#define ERROR_CLOCK 0x0000000 +#define ERROR_YRAM_CRCCHK 0x0000001 +#define ERROR_OVER_CURRENT 0x0000002 +#define ERROR_DIE_OVERTEMP 0x0000004 +#define ERROR_OVER_VOLTAGE 0x0000008 +#define ERROR_UNDER_VOLTAGE 0x0000010 +#define ERROR_BROWNOUT 0x0000020 +#define ERROR_CLASSD_PWR 0x0000040 +#define ERROR_FAILSAFE 0x4000000 + +#define CHECK_PERIOD 5000 /* 5 second */ + +#define TAS2562_I2C_RETRY_COUNT 3 +#define ERROR_I2C_SUSPEND -1 +#define ERROR_I2C_FAILED -2 + +struct tas2562_register { +int book; +int page; +int reg; +}; + +struct tas2562_dai_cfg { +unsigned int dai_fmt; +unsigned int tdm_delay; +}; + +enum channel { + channel_left = 0x01, + channel_right = 0x02, + channel_both = channel_left|channel_right +}; + +struct tas2562_priv { +struct device *dev; +struct i2c_client *client; +struct regmap *regmap; +struct mutex dev_lock; +struct delayed_work irq_work; +struct delayed_work init_work; +struct hrtimer mtimer; +bool mb_power_up; +int mn_power_state; +int mn_l_current_book; +int mn_l_current_page; +int mn_r_current_book; +int mn_r_current_page; +int mn_l_addr; +int mn_r_addr; +int mn_asi_format; +int mn_reset_gpio; +int mn_irq_gpio; +int mn_irq; +int mn_reset_gpio2; +int mn_irq_gpio2; +int mn_irq2; +bool mb_irq_eable; +int mn_sampling_rate; +int mn_frame_size; +int mn_ppg; +int mn_ch_size; +int mn_slot_width; +int mn_pcm_format; +bool mb_mute; +bool i2c_suspend; +int mn_channels; +int spk_l_control; +int spk_r_control; +int spk_digital_vol; +int (*read)(struct tas2562_priv *p_tas2562, enum channel chn, + unsigned int reg, unsigned int *pValue); +int (*write)(struct tas2562_priv *p_tas2562, enum channel chn, + unsigned int reg, unsigned int Value); +int (*bulk_read)(struct tas2562_priv *p_tas2562, enum channel chn, + unsigned int reg, unsigned char *p_data, unsigned int len); +int (*bulk_write)(struct tas2562_priv *p_tas2562, enum channel chn, + unsigned int reg, unsigned char *p_data, unsigned int len); +int (*update_bits)(struct tas2562_priv *p_tas2562, enum channel chn, + unsigned int reg, unsigned int mask, unsigned int value); +void (*hw_reset)(struct tas2562_priv *p_tas2562); +void (*clear_irq)(struct tas2562_priv *p_tas2562); +void (*enable_irq)(struct tas2562_priv *p_tas2562, bool enable); + /* device is working, but system is suspended */ +int (*runtime_suspend)(struct tas2562_priv *p_tas2562); +int (*runtime_resume)(struct tas2562_priv *p_tas2562); +bool mb_runtime_suspend; + +unsigned int mn_err_code; +#ifdef CONFIG_TAS2562_CODEC +struct mutex codec_lock; +#endif + +#ifdef CONFIG_TAS2562_MISC +int mn_dbg_cmd; +int mn_current_reg; +struct mutex file_lock; +#endif +int max_ant; +int lim_th_max; +int lim_th_min; +bool tas2562brownout_enable; +bool icn_enable_left; +bool icn_enable_right; +}; + +#endif /* __TAS2562_ */ diff --git a/sound/soc/codecs/tas2562.txt b/sound/soc/codecs/tas2562.txt new file mode 100644 index 000000000000..ddb490b24191 --- /dev/null +++ b/sound/soc/codecs/tas2562.txt @@ -0,0 +1,45 @@ +Texas Instruments TAS2562 Smart PA + +Required properties: + + - compatible: Should contain "ti,tas2562". + - reg: The i2c address. Should contain <0x4c>, <0x4d>, + <0x4e>, or <0x4f>. + +Optional properties: + + - ti,left-channel: The i2c address of left device. + + - ti,right-channel: The i2c address of right device. + It can be NULL for mono design. + + + - ti,channels: This property is channels number from 1 to 2. + If given 1, it is mono design. + If given 2, it is stereo design. + + - ti,reset-gpio: Reset GPIO number of left device. + + - ti,reset-gpio2: Reset GPIO number of right device. + It can be NULL for mono design. + + - irq-gpio: IRQ GPIO number of left device. + + - irq-gpio2: IRQ GPIO number of right device. + It can be NULL for mono design. + +Examples: + + tas2562:tas2562@4c { + #sound-dai-cells = <0>; + compatible = "ti,tas2562"; + reg = <0x4c>; + ti,left-channel = <0x4c>; + ti,right-channel = <0x4d>; + ti,channels = <2>; /* channel number */ + ti,reset-gpio = <&gpio1 17 0>; + ti,reset-gpio2 = <&gpio1 16 0>; + ti,irq-gpio = <&gpio1 15 0>; + ti,irq-gpio2 = <&gpio1 14 0>; + status = "ok"; + }; \ No newline at end of file diff --git a/sound/soc/codecs/tas25xx-algo.c b/sound/soc/codecs/tas25xx-algo.c new file mode 100644 index 000000000000..181240fccdbf --- /dev/null +++ b/sound/soc/codecs/tas25xx-algo.c @@ -0,0 +1,824 @@ +#ifdef CONFIG_TAS25XX_ALGO +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "tas25xx-calib.h" + +#define USE_VFS 1 +#define CODEC_CONTROL 1 +#define CONFIG_SET_RE_IN_KERNEL +#define POISON_VAL 0xDEADDEAD +#define MAX_STRING (300) +#define SMARTAMP_SPEAKER_CALIBDATA_FILE "/mnt/vendor/persist/factory/audio/tas25xx_calib.bin" + +/*Master Control to Bypass the Smartamp TI CAPIv2 module*/ +static int s_tas_smartamp_bypass = 0; +static int s_tas_smartamp_enable = 0; +static struct mutex routing_lock; + +#ifdef CONFIG_SET_RE_IN_KERNEL +static int get_calibrated_re_tcalib (uint32_t *rdc_fix, uint32_t *tv_fix, int channel_count) +{ + struct file *file = NULL; + char calib_data[MAX_STRING] = {0}; + char filepath[128] = SMARTAMP_SPEAKER_CALIBDATA_FILE; + mm_segment_t fs; + int ret = 0; + + static int s_rdc_fix[2] = {POISON_VAL, POISON_VAL}; + static int s_tv_fix = POISON_VAL; + +#if USE_VFS + loff_t pos = 0; +#else + int size; + int fd; +#endif + + if ((s_rdc_fix[0] == POISON_VAL) && + (s_rdc_fix[1] == POISON_VAL) + ) + { + fs = get_fs(); + set_fs(get_ds()); + #if USE_VFS + file = filp_open(filepath, O_RDONLY, 0); + if(!IS_ERR(file)) + { + vfs_read(file, calib_data, MAX_STRING-1, &pos); + + if (channel_count == 1) { + if(sscanf(calib_data, "%d;%d;", s_rdc_fix, &s_tv_fix) != 2) { + pr_err("[Smartamp:%s] file %s read error\n", __func__, filepath); + ret = -EIO; + } + } else if (channel_count == 2) { + if(sscanf(calib_data, "%d;%d;%d;", &s_rdc_fix[0], &s_rdc_fix[1], &s_tv_fix) != 3) { + pr_err("[Smartamp:%s] file %s read error\n", __func__, filepath); + ret = -EIO; + } + } + filp_close(file, NULL); + + } else { + pr_err("[Smartamp:%s] file %s open failed %p \n ", __func__, filepath, file); + ret = -EIO; + } + #else + fd = sys_open (filepath, O_RDONLY, 0); + if (fd > 0) { + memset (calib_data, 0, sizeof(calib_data)); + size = sys_read (fd, calib_data, MAX_STRING-1); + sys_close (fd); + + pr_info ("TI-SmartPA: *** sys_read size = %d", size); + + if (channel_count == 1) { + if(sscanf(calib_data, "%d;%d;", &rdc_fix[0], tv_fix) != 2) { + pr_err("[TI-SmartPA:%s] file %s read error\n", __func__, filepath); + ret = -EIO; + } + } else if (channel_count == 2) { + if(sscanf(calib_data, "%d;%d;%d;", &rdc_fix[0], &rdc_fix[1], tv_fix) != 3) { + pr_err("[TI-SmartPA:%s] file %s read error\n", __func__, filepath); + ret = -EIO; + } + } + } else { + pr_err("[TI-SmartPA:%s] file %s open failed %p \n ", __func__, filepath, file); + ret = -EIO; + } + + #endif + + set_fs(fs); + } + + if (ret == 0) { + if (rdc_fix) { + rdc_fix[0] = (uint32_t)s_rdc_fix[0]; + + if (channel_count == 2) { + rdc_fix[1] = (uint32_t)s_rdc_fix[1]; + } + } + + if (tv_fix) + *tv_fix = (uint32_t)s_tv_fix; + } + return ret; +} +#endif //CONFIG_SET_RE_IN_KERNEL + +static int tas25xx_smartamp_get_set(u8 *user_data, uint32_t param_id, + uint8_t get_set, uint32_t length, uint32_t module_id) +{ + int ret = 0; + + switch (get_set) { + case TAS_SET_PARAM: + if (s_tas_smartamp_bypass) { + pr_err("TI-SmartPA: %s: SmartAmp is bypassed no control set", __func__); + goto fail_cmd; + } + ret = afe_tas_smartamp_set_calib_data(module_id, param_id, length, user_data); + break; + + case TAS_GET_PARAM: + memset(user_data, 0, length); + ret = afe_tas_smartamp_get_calib_data(module_id, param_id, length, user_data); + break; + + default: + goto fail_cmd; + } + +fail_cmd: + return ret; +} + +/*Wrapper arround set/get parameter, all set/get commands pass through this wrapper*/ +int tas25xx_smartamp_algo_ctrl(u8 *user_data, uint32_t param_id, + uint8_t get_set, uint32_t length, uint32_t module_id) +{ + int ret = 0; + mutex_lock(&routing_lock); + ret = tas25xx_smartamp_get_set(user_data, param_id, get_set, length, module_id); + mutex_unlock(&routing_lock); + return ret; +} +EXPORT_SYMBOL(tas25xx_smartamp_algo_ctrl); + +/*Control-1: Set Profile*/ +static const char *profile_index_text[] = { "NONE", TAS_ALGO_PROFILE_LIST, "CALIB"}; +static const struct soc_enum profile_index_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(profile_index_text), profile_index_text), +}; + +static int tas25xx_set_profile(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + int ret; + int profile_id = pUcontrol->value.integer.value[0]; + int param_id = 0; + int max_number_of_profiles = sizeof(profile_index_text)/sizeof(profile_index_text[0]); + if ((profile_id >= max_number_of_profiles) || (profile_id < 0)) + return -EINVAL; + + pr_info("TI-SmartPA: %s: Setting profile %s", __func__, profile_index_text[profile_id]); + if(profile_id) + profile_id -= 1; + else + return 0; + + param_id = TAS_CALC_PARAM_IDX(TAS_SA_SET_PROFILE, 1, CHANNEL0); + pr_info("TI-SmartPA: %s: Sending set profile", __func__); + ret = tas25xx_smartamp_algo_ctrl((u8 *)&profile_id, param_id, + TAS_SET_PARAM, sizeof(uint32_t), AFE_SMARTAMP_MODULE_RX); + if(ret < 0) + pr_err("TI-SmartPA: %s: Failed to set config", __func__); + return ret; +} + +static int tas25xx_get_profile(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + int ret; + int profile_id = 0; + int param_id = 0; + int max_number_of_profiles = sizeof(profile_index_text)/sizeof(profile_index_text[0]); + + + if(s_tas_smartamp_enable) { + param_id = TAS_CALC_PARAM_IDX(TAS_SA_SET_PROFILE, 1, CHANNEL0); + ret = tas25xx_smartamp_algo_ctrl((u8 *)&profile_id, param_id, + TAS_GET_PARAM, sizeof(uint32_t), AFE_SMARTAMP_MODULE_RX); + if(ret < 0) { + pr_err("TI-SmartPA: %s: Failed to get profile", __func__); + profile_id = 0; + } else { + profile_id += 1; + } + } + + pUcontrol->value.integer.value[0] = profile_id; + + if (profile_id < max_number_of_profiles) { + pr_info("TI-SmartPA: %s: getting profile %s", __func__, profile_index_text[profile_id]); + } + + return 0; +} + +/*Control-2: Set Calibrated Rdc*/ +static int tas25xx_set_Re_common (int re_value_in, int channel) +{ + int ret; + int param_id = 0; + int re_value = re_value_in; + + param_id = TAS_CALC_PARAM_IDX(TAS_SA_SET_RE, 1, channel); + ret = tas25xx_smartamp_algo_ctrl((u8 *)&re_value, param_id, + TAS_SET_PARAM, sizeof(uint32_t), AFE_SMARTAMP_MODULE_RX); + + return ret; +} + +static int tas25xx_set_Re_left(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + int re_value = pUcontrol->value.integer.value[0]; + + pr_info("TI-SmartPA: %s: Setting Re %d", __func__, re_value); + return tas25xx_set_Re_common(re_value, CHANNEL0); +} + +static int tas25xx_dummy_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + int ret = 0; + pUcontrol->value.integer.value[0] = 0; + return ret; +} + +static int tas25xx_dummy_set(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + return 0; +} + +/*Control-3: Calibration and Test(F0,Q,Tv) Controls*/ +static const char *tas25xx_calib_test_text[] = { + "NONE", + "CALIB_START", + "CALIB_STOP", + "TEST_START", + "TEST_STOP" +}; + +static const struct soc_enum tas25xx_calib_test_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas25xx_calib_test_text), tas25xx_calib_test_text), +}; + +static int tas25xx_calib_test_set_common (int calib_command, int channel) +{ + int ret = 0; + int param_id = 0; + int data = 1; + + switch(calib_command) { + case CALIB_START: + pr_info ("TI-SmartPA: %s: CALIB_START", __func__); + param_id = TAS_CALC_PARAM_IDX (TAS_SA_CALIB_INIT, 1, channel); + break; + + case CALIB_STOP: + pr_info ("TI-SmartPA: %s: CALIB_STOP", __func__); + param_id = TAS_CALC_PARAM_IDX (TAS_SA_CALIB_DEINIT, 1, channel); + break; + + case TEST_START: + case TEST_STOP: + default: + pr_info ("TI-SmartPA: %s: no impl", __func__); + ret = -EINVAL; + break; + } + + if (param_id) { + ret = tas25xx_smartamp_algo_ctrl ((u8 *)&data, param_id, + TAS_SET_PARAM, sizeof (uint32_t), AFE_SMARTAMP_MODULE_RX); + if(ret < 0) { + pr_err("TI-SmartPA: %s: Failed to set calib/test, ret=%d", __func__, ret); + } + } + + return ret; +} + +static int tas25xx_calib_test_set(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + int ret = 0; + + int user_data = pUcontrol->value.integer.value[0]; + if ((0 == (ret = tas25xx_calib_test_set_common (user_data, CHANNEL0)))) { +#ifdef CONFIG_TAS25XX_ALGO_STEREO + ret = tas25xx_calib_test_set_common (user_data, CHANNEL1); +#endif + } + + return ret; +} + +/*Control-4: Get Re*/ +//returns -ve error or +ve re value, 0 if not called +static int tas25xx_get_re_common (int channel) +{ + int ret = 0; + int re_value = 0; + int param_id = 0; + + pr_info("TI-SmartPA: %s, channel=%d", __func__, channel); + + if(s_tas_smartamp_enable) { + param_id = TAS_CALC_PARAM_IDX (TAS_SA_GET_RE, 1, channel); + ret = tas25xx_smartamp_algo_ctrl ((u8 *)&re_value, param_id, + TAS_GET_PARAM, sizeof(uint32_t), AFE_SMARTAMP_MODULE_RX); + if(ret < 0) { + pr_err("TI-SmartPA: %s: Failed to get Re", __func__); + } else { + ret = re_value; + } + } + + return ret; +} + +static int tas25xx_get_re_left(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + int ret = tas25xx_get_re_common (CHANNEL0); + if (ret >= 0) { + pUcontrol->value.integer.value[0] = ret; + ret = 0; + pr_info("TI-SmartPA: %s: Getting Re %d", __func__, ret); + } + + return ret; +} + +/*Control-5: Get F0*/ +static int tas25xx_get_f0_common(int channel) +{ + int f0_value = 0; + int param_id = 0; + int ret = 0; + + pr_info("TI-SmartPA: %s, channel=%d", __func__, channel); + + if(s_tas_smartamp_enable) { + param_id = TAS_CALC_PARAM_IDX(TAS_SA_GET_F0, 1, channel); + ret = tas25xx_smartamp_algo_ctrl ((u8 *)&f0_value, param_id, + TAS_GET_PARAM, sizeof(uint32_t), AFE_SMARTAMP_MODULE_RX); + if (ret < 0) { + pr_err ("TI-SmartPA: %s: Failed to get F0", __func__); + } else { + ret = f0_value; + } + } + + return ret; +} + +static int tas25xx_get_f0_left(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + int ret = tas25xx_get_f0_common (CHANNEL0); + + if (ret >= 0) { + pUcontrol->value.integer.value[0] = ret; + pr_info("TI-SmartPA: %s: Getting F0 val=%d", __func__, ret); + ret = 0; + } + + return ret; +} + +/*Control-6: Get Q*/ +static int tas25xx_get_q_common (int channel) +{ + int ret = 0; + int q_value = 0; + int param_id = 0; + + pr_info("TI-SmartPA: %s, channel=%d", __func__, channel); + + if (s_tas_smartamp_enable) { + param_id = TAS_CALC_PARAM_IDX (TAS_SA_GET_Q, 1, channel); + ret = tas25xx_smartamp_algo_ctrl ((u8 *)&q_value, param_id, + TAS_GET_PARAM, sizeof(uint32_t), AFE_SMARTAMP_MODULE_RX); + if (ret < 0) { + pr_err ("TI-SmartPA: %s: Failed to get F0", __func__); + } else { + ret = q_value; + } + } + + return ret; +} + +static int tas25xx_get_q_left(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + int ret = tas25xx_get_q_common (CHANNEL0); + if (ret >= 0) { + pUcontrol->value.integer.value[0] = ret; + pr_info("TI-SmartPA: %s: Getting Q %d", __func__, ret); + ret = 0; + } + + return ret; +} + +/*Control-7: Get Tv*/ +static int tas25xx_get_tv_common(int channel) +{ + int ret = 0; + int tv_value = 0; + int param_id = 0; + + pr_info("TI-SmartPA: %s, channel=%d", __func__, channel); + + if (s_tas_smartamp_enable) { + param_id = TAS_CALC_PARAM_IDX(TAS_SA_GET_TV, 1, channel); + ret = tas25xx_smartamp_algo_ctrl((u8 *)&tv_value, param_id, + TAS_GET_PARAM, sizeof(uint32_t), AFE_SMARTAMP_MODULE_RX); + if (ret < 0) { + pr_err ("TI-SmartPA: %s: Failed to get Tv", __func__); + } else { + ret = tv_value; + } + } + + return ret; +} + +static int tas25xx_get_tv_left(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + + int ret = tas25xx_get_tv_common (CHANNEL0); + if (ret >= 0) { + pUcontrol->value.integer.value[0] = ret; + pr_info("TI-SmartPA: %s: Getting Tv %d", __func__, ret); + ret = 0; + } + + return ret; +} + +/*Control-8: Smartamp Enable*/ +static const char *tas25xx_smartamp_enable_text[] = { + "DISABLE", + "ENABLE" +}; + +static const struct soc_enum tas25xx_smartamp_enable_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas25xx_smartamp_enable_text), tas25xx_smartamp_enable_text), +}; + +static int tas25xx_smartamp_enable_set(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + int ret = 0; + int param_id = 0; + int user_data = pUcontrol->value.integer.value[0]; +#ifdef CONFIG_SET_RE_IN_KERNEL + uint32_t calibration_data[3]; +#endif + +#ifdef CONFIG_TAS25XX_ALGO_STEREO + int number_of_ch = 2; +#else + int number_of_ch = 1; +#endif + pr_info("TI-SmartPA: %s: case %d, number_of_ch=%d", __func__, user_data, number_of_ch); + + s_tas_smartamp_enable = user_data; + if (s_tas_smartamp_enable == 0) { + pr_info("TI-SmartPA: %s: Disable called", __func__); + return 0; + } + + pr_info ("TI-SmartPA: %s: Setting the feedback module info for TAS", __func__); + ret = afe_spk_prot_feed_back_cfg (TAS_TX_PORT,TAS_RX_PORT, 1, 0, 1); + if (ret) { + pr_err("TI-SmartPA: %s: FB Path Info failed ignoring ret = 0x%x", __func__, ret); + } + + + pr_info ("TI-SmartPA: %s: Sending TX Enable", __func__); + param_id = CAPI_V2_TAS_TX_ENABLE; + ret = tas25xx_smartamp_algo_ctrl ((u8 *)&user_data, param_id, + TAS_SET_PARAM, sizeof(uint32_t), AFE_SMARTAMP_MODULE_TX); + if (ret) { + pr_err("TI-SmartPA: %s: TX Enable Failed ret = 0x%x", __func__, ret); + goto fail_cmd; + } + + //Special configuration indicating bin file + user_data = 0xB1B1B1B1; + pr_info("TI-SmartPA: %s: Sending TX Config", __func__); + param_id = CAPI_V2_TAS_TX_CFG; + ret = tas25xx_smartamp_algo_ctrl((u8 *)&user_data, param_id, + TAS_SET_PARAM, sizeof(uint32_t), AFE_SMARTAMP_MODULE_TX); + if(ret < 0) { + pr_err("TI-SmartPA: %s: Failed to set config", __func__); + } + + user_data = 1; + pr_info ("TI-SmartPA: %s: Sending RX Enable", __func__); + param_id = CAPI_V2_TAS_RX_ENABLE; + ret = tas25xx_smartamp_algo_ctrl ((u8 *)&user_data, param_id, + TAS_SET_PARAM, sizeof(uint32_t), AFE_SMARTAMP_MODULE_RX); + if(ret) { + pr_err("TI-SmartPA: %s: RX Enable Failed ret = 0x%x", __func__, ret); + goto fail_cmd; + } + + //Special configuration indicating bin file + user_data = 0xB1B1B1B1; + pr_info("TI-SmartPA: %s: Sending RX Config", __func__); + param_id = CAPI_V2_TAS_RX_CFG; + ret = tas25xx_smartamp_algo_ctrl((u8 *)&user_data, param_id, + TAS_SET_PARAM, sizeof(uint32_t), AFE_SMARTAMP_MODULE_RX); + if(ret < 0) { + pr_err("TI-SmartPA: %s: Failed to set config", __func__); + } + + s_tas_smartamp_enable = true; + +#ifdef CONFIG_SET_RE_IN_KERNEL + if (number_of_ch == 2) { + ret = get_calibrated_re_tcalib (calibration_data, &calibration_data[2], number_of_ch); + } else { + ret = get_calibrated_re_tcalib (calibration_data, &calibration_data[1], number_of_ch); + } + if(ret) { + pr_err("[Smartamp:%s] unable to get the calibration data = 0x%x", __func__, ret); + } else { + int32_t t_cal; + if (number_of_ch == 2) { + t_cal = calibration_data[2]; + pr_info("[Smartamp:%s] setting re %d,%d and tcal %d", __func__ + , calibration_data[0], calibration_data[1], calibration_data[2]); + } else { + t_cal = calibration_data[1]; + pr_info("[Smartamp:%s] setting re %d and tcal %d", __func__ + , calibration_data[0], calibration_data[1]); + } + + param_id = TAS_CALC_PARAM_IDX(TAS_SA_SET_TCAL, 1, CHANNEL0); + ret = tas25xx_smartamp_algo_ctrl ((u8 *)&t_cal, + param_id, TAS_SET_PARAM, sizeof(uint32_t), AFE_SMARTAMP_MODULE_RX); + if(ret < 0) { + pr_err("[Smartamp:%s] Failed to set Tcal", __func__); + goto fail_cmd; + } + + param_id = TAS_CALC_PARAM_IDX(TAS_SA_SET_RE, 1, CHANNEL0); + ret = tas25xx_smartamp_algo_ctrl ((u8 *)(&(calibration_data[0])), + param_id, TAS_SET_PARAM, sizeof(uint32_t), AFE_SMARTAMP_MODULE_RX); + if (ret < 0) { + pr_err("TI-SmartPA: %s: Failed to set Re", __func__); + goto fail_cmd; + } + + if (number_of_ch == 2) { + param_id = TAS_CALC_PARAM_IDX(TAS_SA_SET_RE, 1, CHANNEL1); + ret = tas25xx_smartamp_algo_ctrl ((u8 *)&(calibration_data[1]), + param_id, TAS_SET_PARAM, sizeof(uint32_t), AFE_SMARTAMP_MODULE_RX); + if(ret < 0) { + pr_err("TI-SmartPA: %s: Failed to set Re", __func__); + goto fail_cmd; + } + } + } +#endif //CONFIG_SET_RE_IN_KERNEL + +fail_cmd: + return ret; +} + +static int tas25xx_smartamp_enable_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + int ret = 0; + int user_data = s_tas_smartamp_enable; + pUcontrol->value.integer.value[0] = user_data; + pr_info("TI-SmartPA: %s: case %d(0=DISABLE, 1=ENABLE)", __func__, user_data); + return ret; +} + +/*Control-9: Smartamp Bypass */ +static const char *tas25xx_smartamp_bypass_text[] = { + "FALSE", + "TRUE" +}; + +static const struct soc_enum tas25xx_smartamp_bypass_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tas25xx_smartamp_bypass_text), tas25xx_smartamp_bypass_text), +}; + +static int tas25xx_smartamp_bypass_set(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + int ret = 0; + int user_data = pUcontrol->value.integer.value[0]; + if (s_tas_smartamp_enable) { + pr_debug("TI-SmartPA: %s: cannot update while smartamp enabled", __func__); + return -EINVAL; + } + + s_tas_smartamp_bypass = user_data; + pr_info("TI-SmartPA: %s: case %d(FALSE=0,TRUE=1)", __func__, user_data); + return ret; +} + +static int tas25xx_smartamp_bypass_get(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + int ret = 0; + pUcontrol->value.integer.value[0] = s_tas_smartamp_bypass; + pr_info("TI-SmartPA: %s: case %d", __func__, s_tas_smartamp_bypass); + return ret; +} + +static int tas25xx_set_spk_id(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + int ret = 0; + int speaker_id = pUcontrol->value.integer.value[0]; + int param_id = 0; + + pr_info("TI-SmartPA: %s: spk-id set %d", __func__, speaker_id); + param_id = TAS_CALC_PARAM_IDX(TAS_SA_SET_SPKID, 1, CHANNEL0); + ret = tas25xx_smartamp_algo_ctrl((u8 *)&speaker_id, param_id, + TAS_SET_PARAM, sizeof(uint32_t), AFE_SMARTAMP_MODULE_RX); + if (ret < 0) { + pr_err ("TI-SmartPA: %s: Failed to set spk id", __func__); + } + + return ret; +} + +static int tas25xx_set_t_calib(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + int ret = 0; + int calib_temp = pUcontrol->value.integer.value[0]; + int param_id = 0; + + pr_info("TI-SmartPA: %s: tcalib set %d", __func__, calib_temp); + if (s_tas_smartamp_enable) { + param_id = TAS_CALC_PARAM_IDX(TAS_SA_SET_TCAL, 1, CHANNEL0); + ret = tas25xx_smartamp_algo_ctrl((u8 *)&calib_temp, param_id, + TAS_SET_PARAM, sizeof(uint32_t), AFE_SMARTAMP_MODULE_RX); + if (ret < 0) { + pr_err ("TI-SmartPA: %s: Failed to set spk id", __func__); + } + } + + return ret; +} + +#ifdef CONFIG_TAS25XX_ALGO_STEREO +static int tas25xx_set_Re_right(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + int re_value = pUcontrol->value.integer.value[0]; + + pr_info("TI-SmartPA: %s: Setting Re %d", __func__, re_value); + return tas25xx_set_Re_common(re_value, CHANNEL1); +} + +static int tas25xx_get_re_right(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + int ret; + + ret = tas25xx_get_re_common (CHANNEL1); + if (ret >= 0) { + pUcontrol->value.integer.value[0] = ret; + ret = 0; + pr_info("TI-SmartPA: %s: Getting Re value=%d", __func__, ret); + } + + return ret; +} + +static int tas25xx_get_f0_right(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + int ret = tas25xx_get_f0_common (CHANNEL1); + + if (ret >= 0) { + pUcontrol->value.integer.value[0] = ret; + pr_info("TI-SmartPA: %s: Getting F0 valu=%d", __func__, ret); + ret = 0; + } + + return ret; +} + +static int tas25xx_get_q_right(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + int ret = tas25xx_get_q_common (CHANNEL1); + if (ret >= 0) { + pUcontrol->value.integer.value[0] = ret; + pr_info("TI-SmartPA: %s: Getting Q val=%d", __func__, ret); + ret = 0; + } + + return ret; +} + +static int tas25xx_get_tv_right(struct snd_kcontrol *pKcontrol, + struct snd_ctl_elem_value *pUcontrol) +{ + + int ret = tas25xx_get_tv_common (CHANNEL1); + if (ret >= 0) { + pUcontrol->value.integer.value[0] = ret; + pr_info("TI-SmartPA: %s: Getting Tv %d", __func__, ret); + ret = 0; + } + + return ret; +} +#endif + +static const struct snd_kcontrol_new smartamp_tas25xx_mixer_controls[] = { + SOC_ENUM_EXT("TAS25XX_ALGO_PROFILE", profile_index_enum[0], + tas25xx_get_profile, tas25xx_set_profile), + SOC_ENUM_EXT("TAS25XX_ALGO_CALIB_TEST", tas25xx_calib_test_enum[0], + tas25xx_dummy_get, tas25xx_calib_test_set), + SOC_SINGLE_EXT("TAS25XX_SET_SPK_ID", SND_SOC_NOPM, 0, 0x7fffffff, 0, + tas25xx_dummy_get, tas25xx_set_spk_id), + SOC_SINGLE_EXT("TAS25XX_SET_T_CALIB", SND_SOC_NOPM, 0, 100, 0, + tas25xx_dummy_get, tas25xx_set_t_calib), + + //left + SOC_SINGLE_EXT("TAS25XX_SET_RE_LEFT", SND_SOC_NOPM, 0, 0x7fffffff, 0, + tas25xx_dummy_get, tas25xx_set_Re_left), + SOC_SINGLE_EXT("TAS25XX_GET_RE_LEFT", SND_SOC_NOPM, 0, 0x7fffffff, 0, + tas25xx_get_re_left, tas25xx_dummy_set), + SOC_SINGLE_EXT("TAS25XX_GET_F0_LEFT", SND_SOC_NOPM, 0, 0x7fffffff, 0, + tas25xx_get_f0_left, tas25xx_dummy_set), + SOC_SINGLE_EXT("TAS25XX_GET_Q_LEFT", SND_SOC_NOPM, 0, 0x7fffffff, 0, + tas25xx_get_q_left, tas25xx_dummy_set), + SOC_SINGLE_EXT("TAS25XX_GET_TV_LEFT", SND_SOC_NOPM, 0, 0x7fffffff, 0, + tas25xx_get_tv_left, tas25xx_dummy_set), + + //Right +#ifdef CONFIG_TAS25XX_ALGO_STEREO + SOC_SINGLE_EXT("TAS25XX_SET_RE_RIGHT", SND_SOC_NOPM, 0, 0x7fffffff, 0, + tas25xx_dummy_get, tas25xx_set_Re_right), + SOC_SINGLE_EXT("TAS25XX_GET_RE_RIGHT", SND_SOC_NOPM, 0, 0x7fffffff, 0, + tas25xx_get_re_right, tas25xx_dummy_set), + SOC_SINGLE_EXT("TAS25XX_GET_F0_RIGHT", SND_SOC_NOPM, 0, 0x7fffffff, 0, + tas25xx_get_f0_right, tas25xx_dummy_set), + SOC_SINGLE_EXT("TAS25XX_GET_Q_RIGHT", SND_SOC_NOPM, 0, 0x7fffffff, 0, + tas25xx_get_q_right, tas25xx_dummy_set), + SOC_SINGLE_EXT("TAS25XX_GET_TV_RIGHT", SND_SOC_NOPM, 0, 0x7fffffff, 0, + tas25xx_get_tv_right, tas25xx_dummy_set), +#endif + + SOC_ENUM_EXT("TAS25XX_SMARTPA_ENABLE", tas25xx_smartamp_enable_enum[0], + tas25xx_smartamp_enable_get, tas25xx_smartamp_enable_set), + + SOC_ENUM_EXT("TAS25XX_ALGO_BYPASS", tas25xx_smartamp_bypass_enum[0], + tas25xx_smartamp_bypass_get, tas25xx_smartamp_bypass_set), +}; + +#if CODEC_CONTROL +void tas_smartamp_add_algo_controls(struct snd_soc_codec *codec) +{ + pr_err("TI-SmartPA: %s: Adding smartamp controls", __func__); + mutex_init(&routing_lock); + snd_soc_add_codec_controls(codec, smartamp_tas25xx_mixer_controls, + ARRAY_SIZE(smartamp_tas25xx_mixer_controls)); + tas_calib_init(); +} +EXPORT_SYMBOL(tas_smartamp_add_algo_controls); +#else +void tas_smartamp_add_algo_controls_for_platform (struct snd_soc_platform *platform) +{ + pr_err("TI-SmartPA: %s: Adding smartamp controls", __func__); + mutex_init(&routing_lock); + snd_soc_add_platform_controls (platform, smartamp_tas25xx_mixer_controls, ARRAY_SIZE(smartamp_tas25xx_mixer_controls)); + tas_calib_init(); +} +EXPORT_SYMBOL(tas_smartamp_add_algo_controls_for_platform); +#endif + +void tas_smartamp_remove_algo_controls(struct snd_soc_codec *codec) +{ + (void)codec; + tas_calib_exit(); +} +EXPORT_SYMBOL(tas_smartamp_remove_algo_controls); + +MODULE_AUTHOR("Texas Instrumnets Inc."); +MODULE_DESCRIPTION("TAS25xx ALSA SOC Smart Amplifier algo driver"); +MODULE_LICENSE("GPL v2"); +#endif /* CONFIG_TAS25XX_ALGO */ diff --git a/sound/soc/codecs/tas25xx-calib.c b/sound/soc/codecs/tas25xx-calib.c new file mode 100644 index 000000000000..47ea0a44a71e --- /dev/null +++ b/sound/soc/codecs/tas25xx-calib.c @@ -0,0 +1,193 @@ +/* +** ============================================================================= +** Copyright (c) 2017 Texas Instruments 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; version 2. +** +** 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: +** tas25xx-calib.c +** +** Description: +** misc driver for interfacing for Texas Instruments TAS25xx algorithm +** +** ============================================================================= +*/ + +#ifdef CONFIG_TAS25XX_CALIB +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Holds the Packet data required for processing */ +struct tas_dsp_pkt { + u8 slave_id; + u8 book; + u8 page; + u8 offset; + u8 data[TAS_PAYLOAD_SIZE * 4]; +}; + +static int smartamp_params_ctrl(uint8_t *input, u8 dir, u8 count) +{ + u32 length = count / 4; + u32 paramid = 0; + u32 index; + int ret = 0; + int special_index = 0; + struct tas_dsp_pkt *ppacket; + + ppacket = (struct tas_dsp_pkt*)kmalloc (sizeof(struct tas_dsp_pkt), GFP_KERNEL); + if (!ppacket) { + pr_err ("TI-SmartPA: %s: kmalloc failed!", __func__); + return -ENOMEM; + } + + memset(ppacket, 0, sizeof(struct tas_dsp_pkt)); + ret = copy_from_user (ppacket, input, sizeof(struct tas_dsp_pkt)); + if (ret){ + pr_err("TI-SmartPA: %s: Error copying from user\n", __func__); + kfree (ppacket); + return -EFAULT; + } + + index = (ppacket->page - 1) * 30 + (ppacket->offset - 8) / 4; + special_index = TAS_SA_IS_SPL_IDX(index); + pr_info("TI-SmartPA: %s: index = %d", __func__, index); + if (special_index == 0) { + if ((index < 0 || index > MAX_DSP_PARAM_INDEX)) { + pr_err("TI-SmartPA: %s: invalid index !\n", __func__); + kfree(ppacket); + return -EINVAL; + } + } + pr_info("TI-SmartPA: %s: Valid Index. special = %s\n", __func__, special_index ? "Yes" : "No"); + + /* speakers are differentiated by slave ids */ + if (ppacket->slave_id == SLAVE1 ) + { + paramid = (paramid | (index ) | (length << 16) | (1 << 24)); + pr_err("TI-SmartPA: %s: Rcvd Slave id for slave 1, %x\n", __func__, ppacket->slave_id); + }else if (ppacket->slave_id == SLAVE2 ) + { + paramid = (paramid | (index ) | (length << 16) | (2 << 24)); + pr_err("TI-SmartPA: %s: Rcvd Slave id for slave 2, %x\n", __func__, ppacket->slave_id); + }else{ + pr_err("TI-SmartPA: %s: Wrong slaveid = %x\n", __func__, ppacket->slave_id); + } + + /* + * Note: In any case calculated paramid should not match with + */ + if ((paramid == CAPI_V2_TAS_RX_ENABLE) || + (paramid == CAPI_V2_TAS_TX_ENABLE) || + (paramid == CAPI_V2_TAS_RX_CFG) || + (paramid == CAPI_V2_TAS_TX_CFG)) + { + pr_err("TI-SmartPA: %s: %s Slave 0x%x params failed, paramid mismatch\n", __func__, + dir == TAS_GET_PARAM ? "get" : "set", ppacket->slave_id); + kfree(ppacket); + return -EINVAL; + } + + ret = tas25xx_smartamp_algo_ctrl(ppacket->data, paramid, + dir, length * 4, AFE_SMARTAMP_MODULE_RX); + if (ret) + pr_err("TI-SmartPA: %s: %s Slave 0x%x params failed from afe, ret=%x\n", __func__, + dir == TAS_GET_PARAM ? "get" : "set", ppacket->slave_id, ret); + else + pr_info("TI-SmartPA: %s: Algo control returned %d\n", __func__, ret); + + if (dir == TAS_GET_PARAM) { + ret = copy_to_user(input, ppacket, sizeof(struct tas_dsp_pkt)); + if (ret) { + pr_err("TI-SmartPA: %s: Error copying to user after DSP", __func__); + ret = -EFAULT; + } + } + + kfree(ppacket); + return ret; +} + +static int tas_calib_open(struct inode *inode, struct file *fd) +{ + return 0; +} + +static ssize_t tas_calib_write(struct file *file, + const char __user *buffer, size_t count, loff_t *offp) +{ + int rc = 0; + rc = smartamp_params_ctrl((uint8_t *)buffer, TAS_SET_PARAM, count); + return rc; +} + +static ssize_t tas_calib_read(struct file *file, char __user *buffer, + size_t count, loff_t *ptr) +{ + int rc; + rc = smartamp_params_ctrl((uint8_t *)buffer, TAS_GET_PARAM, count); + if (rc < 0) + count = rc; + return count; +} + +static long tas_calib_ioctl(struct file *filp, uint cmd, ulong arg) +{ + return 0; +} + +static int tas_calib_release(struct inode *inode, struct file *fd) +{ + return 0; +} + +const struct file_operations tas_calib_fops = { + .owner = THIS_MODULE, + .open = tas_calib_open, + .write = tas_calib_write, + .read = tas_calib_read, + .release = tas_calib_release, + .unlocked_ioctl = tas_calib_ioctl, +}; + +static struct miscdevice tas_calib_misc = { + .minor = MISC_DYNAMIC_MINOR, + .name = "tas_calib", + .fops = &tas_calib_fops, +}; + +int tas_calib_init(void) +{ + int rc; + pr_err("TI-SmartPA: %s",__func__); + rc = misc_register(&tas_calib_misc); + if (rc) + pr_err("TI-SmartPA: %s: register calib misc failed\n", __func__); + return rc; +} + +void tas_calib_exit(void) +{ + misc_deregister(&tas_calib_misc); +} + +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("tas2560 Misc driver"); +MODULE_LICENSE("GPL v2"); +#endif /* CONFIG_TAS25XX_CALIB */ diff --git a/sound/soc/codecs/tas25xx-calib.h b/sound/soc/codecs/tas25xx-calib.h new file mode 100644 index 000000000000..ad6484d6c973 --- /dev/null +++ b/sound/soc/codecs/tas25xx-calib.h @@ -0,0 +1,28 @@ +/* +** ============================================================================= +** Copyright (c) 2016 Texas Instruments 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; version 2. +** +** 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: +** tas25xx-calib.h +** +** Description: +** header file for tas25xx-calib.c +** +** ============================================================================= +*/ + +#ifndef _TAS25XX_CALIB_H_ +#define _TAS25XX_CALIB_H_ + +int tas_calib_init(void); +void tas_calib_exit(void); + +#endif /* _TAS25XX_CALIB_H */ diff --git a/sound/soc/codecs/tfa98xx.c b/sound/soc/codecs/tfa98xx.c new file mode 100644 index 000000000000..254ecf4c512e --- /dev/null +++ b/sound/soc/codecs/tfa98xx.c @@ -0,0 +1,3584 @@ +/* + * tfa98xx.c tfa98xx codec module + * + * Copyright 2014-2017 NXP Semiconductors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define pr_fmt(fmt) "%s(): " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "config.h" +#include "tfa98xx.h" +#include "tfa.h" + +/* required for enum tfa9912_irq */ +#include "tfa98xx_tfafieldnames.h" + +#define TFA98XX_VERSION TFA98XX_API_REV_STR + +#define I2C_RETRIES 50 +#define I2C_RETRY_DELAY 5 /* ms */ + +/* Change volume selection behavior: + * Uncomment following line to generate a profile change when updating + * a volume control (also changes to the profile of the modified volume + * control) + */ +/*#define TFA98XX_ALSA_CTRL_PROF_CHG_ON_VOL 1 +*/ + +/* Supported rates and data formats */ +#define TFA98XX_RATES SNDRV_PCM_RATE_8000_48000 +#define TFA98XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define TF98XX_MAX_DSP_START_TRY_COUNT 10 + +/* data accessible by all instances */ +static struct kmem_cache *tfa98xx_cache = NULL; /* Memory pool used for DSP messages */ +/* Mutex protected data */ +static DEFINE_MUTEX(tfa98xx_mutex); +static LIST_HEAD(tfa98xx_device_list); +static int tfa98xx_device_count = 0; +static int tfa98xx_sync_count = 0; +static LIST_HEAD(profile_list); /* list of user selectable profiles */ +static int tfa98xx_mixer_profiles = 0; /* number of user selectable profiles */ +static int tfa98xx_mixer_profile = 0; /* current mixer profile */ +static struct snd_kcontrol_new *tfa98xx_controls; +static nxpTfaContainer_t *tfa98xx_container = NULL; + +static int tfa98xx_kmsg_regs = 0; +static int tfa98xx_ftrace_regs = 0; + +static uint32_t tfa98xx_rpc_send_delay = 0; + +static char *fw_name = "tfa98xx.cnt"; +module_param(fw_name, charp, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(fw_name, "TFA98xx DSP firmware (container file) name."); + +static int trace_level = 0; +module_param(trace_level, int, S_IRUGO); +MODULE_PARM_DESC(trace_level, "TFA98xx debug trace level (0=off, bits:1=verbose,2=regdmesg,3=regftrace,4=timing)."); + +static char *dflt_prof_name = ""; +module_param(dflt_prof_name, charp, S_IRUGO); + +static int no_start = 0; +module_param(no_start, int, S_IRUGO); +MODULE_PARM_DESC(no_start, "do not start the work queue; for debugging via user\n"); + +static int no_reset = 0; +module_param(no_reset, int, S_IRUGO); +MODULE_PARM_DESC(no_reset, "do not use the reset line; for debugging via user\n"); + +static int pcm_sample_format = 0; +module_param(pcm_sample_format, int, S_IRUGO); +MODULE_PARM_DESC(pcm_sample_format, "PCM sample format: 0=S16_LE, 1=S24_LE, 2=S32_LE\n"); + +static int pcm_no_constraint = 1; +module_param(pcm_no_constraint, int, S_IRUGO); +MODULE_PARM_DESC(pcm_no_constraint, "do not use constraints for PCM parameters\n"); + +static void tfa98xx_tapdet_check_update(struct tfa98xx *tfa98xx); +static int tfa98xx_get_fssel(unsigned int rate); +static void tfa98xx_interrupt_enable(struct tfa98xx *tfa98xx, bool enable); + +static int get_profile_from_list(char *buf, int id); +static int get_profile_id_for_sr(int id, unsigned int rate); + +struct tfa98xx_rate { + unsigned int rate; + unsigned int fssel; +}; + +static const struct tfa98xx_rate rate_to_fssel[] = { + { 8000, 0 }, + { 11025, 1 }, + { 12000, 2 }, + { 16000, 3 }, + { 22050, 4 }, + { 24000, 5 }, + { 32000, 6 }, + { 44100, 7 }, + { 48000, 8 }, +}; + +#ifdef TFA9874_NONDSP_STEREO +static atomic_t g_bypass; +static atomic_t g_Tx_enable; +static atomic_t g_Gain; + +extern int send_tfa_cal_set_bypass(void *buf, int cmd_size); +extern int send_tfa_cal_set_tx_enable(void *buf, int cmd_size); +extern int send_tfa_cal_in_band(void *buf, int cmd_size); + +/*************bypass control***************/ +static int tfa987x_algo_get_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + ucontrol->value.integer.value[0] = atomic_read(&g_bypass); + return ret; +} + +static int tfa987x_algo_set_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + u8 buff[56] = {0}, *ptr = buff; + ((int32_t *)buff)[0] = ucontrol->value.integer.value[0]; + pr_err("%s:status data %d\n", __func__, ((int32_t *)buff)[0]); + atomic_set(&g_bypass, ((int32_t *)buff)[0]); + ret = send_tfa_cal_set_bypass(ptr, 4); + return ret; +} + +static int tfa987x_algo_set_tx_enable(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + u8 buff[56] = {0}, *ptr = buff; + ((int32_t *)buff)[0] = ucontrol->value.integer.value[0]; + pr_err("%s:set_tx_enable %d\n", __func__, ((int32_t *)buff)[0]); + atomic_set(&g_Tx_enable, ((int32_t *)buff)[0]); + ret = send_tfa_cal_set_tx_enable(ptr, 4); + return ret; +} + +static int tfa987x_algo_get_tx_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + ucontrol->value.integer.value[0] = atomic_read(&g_Tx_enable); + return ret; +} + +static const char *tfa987x_algo_text[] = { + "DISABLE", "ENABLE" +}; + +static const char *tfa987x_tx_text[] = { + "DISABLE", "ENABLE" +}; + +static const struct soc_enum tfa987x_algo_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tfa987x_algo_text),tfa987x_algo_text) +}; + +static const struct soc_enum tfa987x_tx_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tfa987x_tx_text),tfa987x_tx_text) +}; + +const struct snd_kcontrol_new tfa987x_algo_filter_mixer_controls[] = { + SOC_ENUM_EXT("TFA987X_ALGO_STATUS", tfa987x_algo_enum[0], tfa987x_algo_get_status, tfa987x_algo_set_status), + SOC_ENUM_EXT("TFA987X_TX_ENABLE", tfa987x_tx_enum[0], tfa987x_algo_get_tx_status, tfa987x_algo_set_tx_enable) +}; +#endif +static inline char *tfa_cont_profile_name(struct tfa98xx *tfa98xx, int prof_idx) +{ + if (tfa98xx->tfa->cnt == NULL) + return NULL; + return tfaContProfileName(tfa98xx->tfa->cnt, tfa98xx->tfa->dev_idx, prof_idx); +} + +static enum tfa_error tfa98xx_write_re25(struct tfa_device *tfa, int value) +{ + enum tfa_error err; + + /* clear MTPEX */ + err = tfa_dev_mtp_set(tfa, TFA_MTP_EX, 0); + if (err == tfa_error_ok) { + /* set RE25 in shadow regiser */ + err = tfa_dev_mtp_set(tfa, TFA_MTP_RE25_PRIM, value); + } + if (err == tfa_error_ok) { + /* set MTPEX to copy RE25 into MTP */ + err = tfa_dev_mtp_set(tfa, TFA_MTP_EX, 2); + } + + return err; +} + +/* Wrapper for tfa start */ +static enum tfa_error tfa98xx_tfa_start(struct tfa98xx *tfa98xx, int next_profile, int vstep) +{ + enum tfa_error err; + + err = tfa_dev_start(tfa98xx->tfa, next_profile, vstep); + + if ((err == tfa_error_ok) && (tfa98xx->set_mtp_cal)) { + enum tfa_error err_cal; + err_cal = tfa98xx_write_re25(tfa98xx->tfa, tfa98xx->cal_data); + if (err_cal != tfa_error_ok) { + pr_err("Error, setting calibration value in mtp, err=%d\n", err_cal); + } else { + tfa98xx->set_mtp_cal = false; + pr_info("Calibration value (%d) set in mtp\n", + tfa98xx->cal_data); + } + } + + /* Check and update tap-detection state (in case of profile change) */ + tfa98xx_tapdet_check_update(tfa98xx); + + /* Remove sticky bit by reading it once */ + tfa_get_noclk(tfa98xx->tfa); + + /* A cold start erases the configuration, including interrupts setting. + * Restore it if required + */ + tfa98xx_interrupt_enable(tfa98xx, true); + + return err; +} + +static int tfa98xx_input_open(struct input_dev *dev) +{ + struct tfa98xx *tfa98xx = input_get_drvdata(dev); + dev_dbg(tfa98xx->codec->dev, "opening device file\n"); + + /* note: open function is called only once by the framework. + * No need to count number of open file instances. + */ + if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK) { + dev_dbg(&tfa98xx->i2c->dev, + "DSP not loaded, cannot start tap-detection\n"); + return -EIO; + } + + /* enable tap-detection service */ + tfa98xx->tapdet_open = true; + tfa98xx_tapdet_check_update(tfa98xx); + + return 0; +} + +static void tfa98xx_input_close(struct input_dev *dev) +{ + struct tfa98xx *tfa98xx = input_get_drvdata(dev); + + dev_dbg(tfa98xx->codec->dev, "closing device file\n"); + + /* Note: close function is called if the device is unregistered */ + + /* disable tap-detection service */ + tfa98xx->tapdet_open = false; + tfa98xx_tapdet_check_update(tfa98xx); +} + +static int tfa98xx_register_inputdev(struct tfa98xx *tfa98xx) +{ + int err; + struct input_dev *input; + input = input_allocate_device(); + + if (!input) { + dev_err(tfa98xx->codec->dev, "Unable to allocate input device\n"); + return -ENOMEM; + } + + input->evbit[0] = BIT_MASK(EV_KEY); + input->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0); + input->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1); + input->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2); + input->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3); + input->keybit[BIT_WORD(BTN_4)] |= BIT_MASK(BTN_4); + input->keybit[BIT_WORD(BTN_5)] |= BIT_MASK(BTN_5); + input->keybit[BIT_WORD(BTN_6)] |= BIT_MASK(BTN_6); + input->keybit[BIT_WORD(BTN_7)] |= BIT_MASK(BTN_7); + input->keybit[BIT_WORD(BTN_8)] |= BIT_MASK(BTN_8); + input->keybit[BIT_WORD(BTN_9)] |= BIT_MASK(BTN_9); + + input->open = tfa98xx_input_open; + input->close = tfa98xx_input_close; + + input->name = "tfa98xx-tapdetect"; + + input->id.bustype = BUS_I2C; + input_set_drvdata(input, tfa98xx); + + err = input_register_device(input); + if (err) { + dev_err(tfa98xx->codec->dev, "Unable to register input device\n"); + goto err_free_dev; + } + + dev_dbg(tfa98xx->codec->dev, "Input device for tap-detection registered: %s\n", + input->name); + tfa98xx->input = input; + return 0; + +err_free_dev: + input_free_device(input); + return err; +} + +/* + * Check if an input device for tap-detection can and shall be registered. + * Register it if appropriate. + * If already registered, check if still relevant and remove it if necessary. + * unregister: true to request inputdev unregistration. + */ +static void __tfa98xx_inputdev_check_register(struct tfa98xx *tfa98xx, bool unregister) +{ + bool tap_profile = false; + unsigned int i; + for (i = 0; i < tfa_cnt_get_dev_nprof(tfa98xx->tfa); i++) { + if (strstr(tfa_cont_profile_name(tfa98xx, i), ".tap")) { + tap_profile = true; + tfa98xx->tapdet_profiles |= 1 << i; + dev_info(tfa98xx->codec->dev, + "found a tap-detection profile (%d - %s)\n", + i, tfa_cont_profile_name(tfa98xx, i)); + } + } + + /* Check for device support: + * - at device level + * - at container (profile) level + */ + if (!(tfa98xx->flags & TFA98XX_FLAG_TAPDET_AVAILABLE) || + !tap_profile || + unregister) { + /* No input device supported or required */ + if (tfa98xx->input) { + input_unregister_device(tfa98xx->input); + tfa98xx->input = NULL; + } + return; + } + + /* input device required */ + if (tfa98xx->input) + dev_info(tfa98xx->codec->dev, "Input device already registered, skipping\n"); + else + tfa98xx_register_inputdev(tfa98xx); +} + +static void tfa98xx_inputdev_check_register(struct tfa98xx *tfa98xx) +{ + __tfa98xx_inputdev_check_register(tfa98xx, false); +} + +static void tfa98xx_inputdev_unregister(struct tfa98xx *tfa98xx) +{ + __tfa98xx_inputdev_check_register(tfa98xx, true); +} + +#ifdef CONFIG_DEBUG_FS +/* OTC reporting + * Returns the MTP0 OTC bit value + */ +static int tfa98xx_dbgfs_otc_get(void *data, u64 *val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + int value; + + mutex_lock(&tfa98xx->dsp_lock); + value = tfa_dev_mtp_get(tfa98xx->tfa, TFA_MTP_OTC); + mutex_unlock(&tfa98xx->dsp_lock); + + if (value < 0) { + pr_err("[0x%x] Unable to check DSP access: %d\n", tfa98xx->i2c->addr, value); + return -EIO; + } + + *val = value; + pr_debug("[0x%x] OTC : %d\n", tfa98xx->i2c->addr, value); + + return 0; +} + +static int tfa98xx_dbgfs_otc_set(void *data, u64 val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + enum tfa_error err; + + if (val != 0 && val != 1) { + pr_err("[0x%x] Unexpected value %llu\n", tfa98xx->i2c->addr, val); + return -EINVAL; + } + + mutex_lock(&tfa98xx->dsp_lock); + err = tfa_dev_mtp_set(tfa98xx->tfa, TFA_MTP_OTC, val); + mutex_unlock(&tfa98xx->dsp_lock); + + if (err != tfa_error_ok) { + pr_err("[0x%x] Unable to check DSP access: %d\n", tfa98xx->i2c->addr, err); + return -EIO; + } + + pr_debug("[0x%x] OTC < %llu\n", tfa98xx->i2c->addr, val); + + return 0; +} + +static int tfa98xx_dbgfs_mtpex_get(void *data, u64 *val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + int value; + + mutex_lock(&tfa98xx->dsp_lock); + value = tfa_dev_mtp_get(tfa98xx->tfa, TFA_MTP_EX); + mutex_unlock(&tfa98xx->dsp_lock); + + if (value < 0) { + pr_err("[0x%x] Unable to check DSP access: %d\n", tfa98xx->i2c->addr, value); + return -EIO; + } + + + *val = value; + pr_debug("[0x%x] MTPEX : %d\n", tfa98xx->i2c->addr, value); + + return 0; +} + +static int tfa98xx_dbgfs_mtpex_set(void *data, u64 val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + enum tfa_error err; + + if (val != 0) { + pr_err("[0x%x] Can only clear MTPEX (0 value expected)\n", tfa98xx->i2c->addr); + return -EINVAL; + } + + mutex_lock(&tfa98xx->dsp_lock); + err = tfa_dev_mtp_set(tfa98xx->tfa, TFA_MTP_EX, val); + mutex_unlock(&tfa98xx->dsp_lock); + + if (err != tfa_error_ok) { + pr_err("[0x%x] Unable to check DSP access: %d\n", tfa98xx->i2c->addr, err); + return -EIO; + } + + pr_debug("[0x%x] MTPEX < 0\n", tfa98xx->i2c->addr); + + return 0; +} + +static int tfa98xx_dbgfs_temp_get(void *data, u64 *val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + + mutex_lock(&tfa98xx->dsp_lock); + *val = tfa98xx_get_exttemp(tfa98xx->tfa); + mutex_unlock(&tfa98xx->dsp_lock); + + pr_debug("[0x%x] TEMP : %llu\n", tfa98xx->i2c->addr, *val); + + return 0; +} + +static int tfa98xx_dbgfs_temp_set(void *data, u64 val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + + mutex_lock(&tfa98xx->dsp_lock); + tfa98xx_set_exttemp(tfa98xx->tfa, (short)val); + mutex_unlock(&tfa98xx->dsp_lock); + + pr_debug("[0x%x] TEMP < %llu\n", tfa98xx->i2c->addr, val); + + return 0; +} + +static ssize_t tfa98xx_dbgfs_start_set(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + enum tfa_error ret; + char buf[32]; + const char ref[] = "please calibrate now"; + int buf_size; + + /* check string length, and account for eol */ + if (count > sizeof(ref) + 1 || count < (sizeof(ref) - 1)) + return -EINVAL; + + buf_size = min(count, (size_t)(sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + /* Compare string, excluding the trailing \0 and the potentials eol */ + if (strncmp(buf, ref, sizeof(ref) - 1)) + return -EINVAL; + + mutex_lock(&tfa98xx->dsp_lock); + ret = tfa_calibrate(tfa98xx->tfa); + if (ret == tfa_error_ok) + ret = tfa98xx_tfa_start(tfa98xx, tfa98xx->profile, tfa98xx->vstep); + if (ret == tfa_error_ok) + tfa_dev_set_state(tfa98xx->tfa, TFA_STATE_UNMUTE); + mutex_unlock(&tfa98xx->dsp_lock); + + if (ret) { + pr_info("[0x%x] Calibration start failed (%d)\n", tfa98xx->i2c->addr, ret); + return -EIO; + } else { + pr_info("[0x%x] Calibration started\n", tfa98xx->i2c->addr); + } + + return count; +} + +static ssize_t tfa98xx_dbgfs_r_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + char *str; + uint16_t status; + int ret; + + mutex_lock(&tfa98xx->dsp_lock); + + /* Need to ensure DSP is access-able, use mtp read access for this + * purpose + */ + ret = tfa98xx_get_mtp(tfa98xx->tfa, &status); + if (ret) { + ret = -EIO; + pr_err("[0x%x] MTP read failed\n", tfa98xx->i2c->addr); + goto r_c_err; + } + + ret = tfaRunSpeakerCalibration(tfa98xx->tfa); + if (ret) { + ret = -EIO; + pr_err("[0x%x] calibration failed\n", tfa98xx->i2c->addr); + goto r_c_err; + } + + str = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!str) { + ret = -ENOMEM; + pr_err("[0x%x] memory allocation failed\n", tfa98xx->i2c->addr); + goto r_c_err; + } + + if (tfa98xx->tfa->spkr_count > 1) { + ret = snprintf(str, PAGE_SIZE, + "Prim:%d mOhms, Sec:%d mOhms\n", + tfa98xx->tfa->mohm[0], + tfa98xx->tfa->mohm[1]); + } else { + ret = snprintf(str, PAGE_SIZE, + "Prim:%d mOhms\n", + tfa98xx->tfa->mohm[0]); + } + + pr_debug("[0x%x] calib_done: %s", tfa98xx->i2c->addr, str); + + if (ret < 0) + goto r_err; + + ret = simple_read_from_buffer(user_buf, count, ppos, str, ret); + +r_err: + kfree(str); +r_c_err: + mutex_unlock(&tfa98xx->dsp_lock); + return ret; +} + +static ssize_t tfa98xx_dbgfs_version_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + char str[] = TFA98XX_VERSION "\n"; + int ret; + + ret = simple_read_from_buffer(user_buf, count, ppos, str, sizeof(str)); + + return ret; +} + +static ssize_t tfa98xx_dbgfs_dsp_state_get(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + int ret = 0; + char *str; + + switch (tfa98xx->dsp_init) { + case TFA98XX_DSP_INIT_STOPPED: + str = "Stopped\n"; + break; + case TFA98XX_DSP_INIT_RECOVER: + str = "Recover requested\n"; + break; + case TFA98XX_DSP_INIT_FAIL: + str = "Failed init\n"; + break; + case TFA98XX_DSP_INIT_PENDING: + str = "Pending init\n"; + break; + case TFA98XX_DSP_INIT_DONE: + str = "Init complete\n"; + break; + default: + str = "Invalid\n"; + } + + pr_debug("[0x%x] dsp_state : %s\n", tfa98xx->i2c->addr, str); + + ret = simple_read_from_buffer(user_buf, count, ppos, str, strlen(str)); + return ret; +} + +static ssize_t tfa98xx_dbgfs_dsp_state_set(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + enum tfa_error ret; + char buf[32]; + const char start_cmd[] = "start"; + const char stop_cmd[] = "stop"; + const char mon_start_cmd[] = "monitor start"; + const char mon_stop_cmd[] = "monitor stop"; + int buf_size; + + buf_size = min(count, (size_t)(sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + /* Compare strings, excluding the trailing \0 */ + if (!strncmp(buf, start_cmd, sizeof(start_cmd) - 1)) { + pr_info("[0x%x] Manual triggering of dsp start...\n", tfa98xx->i2c->addr); + mutex_lock(&tfa98xx->dsp_lock); + ret = tfa98xx_tfa_start(tfa98xx, tfa98xx->profile, tfa98xx->vstep); + mutex_unlock(&tfa98xx->dsp_lock); + pr_debug("[0x%x] tfa_dev_start complete: %d\n", tfa98xx->i2c->addr, ret); + } else if (!strncmp(buf, stop_cmd, sizeof(stop_cmd) - 1)) { + pr_info("[0x%x] Manual triggering of dsp stop...\n", tfa98xx->i2c->addr); + mutex_lock(&tfa98xx->dsp_lock); + ret = tfa_dev_stop(tfa98xx->tfa); + mutex_unlock(&tfa98xx->dsp_lock); + pr_debug("[0x%x] tfa_dev_stop complete: %d\n", tfa98xx->i2c->addr, ret); + } else if (!strncmp(buf, mon_start_cmd, sizeof(mon_start_cmd) - 1)) { + pr_info("[0x%x] Manual start of monitor thread...\n", tfa98xx->i2c->addr); + queue_delayed_work(tfa98xx->tfa98xx_wq, + &tfa98xx->monitor_work, HZ); + } else if (!strncmp(buf, mon_stop_cmd, sizeof(mon_stop_cmd) - 1)) { + pr_info("[0x%x] Manual stop of monitor thread...\n", tfa98xx->i2c->addr); + cancel_delayed_work_sync(&tfa98xx->monitor_work); + } else { + return -EINVAL; + } + + return count; +} + +static ssize_t tfa98xx_dbgfs_fw_state_get(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + char *str; + + switch (tfa98xx->dsp_fw_state) { + case TFA98XX_DSP_FW_NONE: + str = "None\n"; + break; + case TFA98XX_DSP_FW_PENDING: + str = "Pending\n"; + break; + case TFA98XX_DSP_FW_FAIL: + str = "Fail\n"; + break; + case TFA98XX_DSP_FW_OK: + str = "Ok\n"; + break; + default: + str = "Invalid\n"; + } + + pr_debug("[0x%x] fw_state : %s", tfa98xx->i2c->addr, str); + + return simple_read_from_buffer(user_buf, count, ppos, str, strlen(str)); +} + +#ifdef TFA9874_NONDSP_STEREO +extern int send_tfa_cal_apr(void *buf, int cmd_size, bool bRead); +#endif + +#ifdef TFA9874_NONDSP_STEREO +static ssize_t tfa98xx_dbgfs_rpc_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + int ret = 0; + uint8_t *buffer; + + buffer = kmalloc(count, GFP_KERNEL); + if (buffer == NULL) { + pr_err("[0x%x] can not allocate memory\n", i2c->addr); + return -ENOMEM; + } + + mutex_lock(&tfa98xx->dsp_lock); + + ret = send_tfa_cal_apr(buffer, count, true); + + mutex_unlock(&tfa98xx->dsp_lock); + if (ret) { + pr_err("[0x%x] dsp_msg_read error: %d\n", i2c->addr, ret); + kfree(buffer); + return -EFAULT; + } + + ret = copy_to_user(user_buf, buffer, count); + kfree(buffer); + if (ret) + return -EFAULT; + + *ppos += count; + return count; +} +#else +static ssize_t tfa98xx_dbgfs_rpc_read(struct file *file, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + int ret = 0; + uint8_t *buffer; + enum Tfa98xx_Error error; + + if (tfa98xx->tfa == NULL) { + pr_debug("[0x%x] dsp is not available\n", tfa98xx->i2c->addr); + return -ENODEV; + } + + if (count == 0) + return 0; + + buffer = kmalloc(count, GFP_KERNEL); + if (buffer == NULL) { + pr_debug("[0x%x] can not allocate memory\n", tfa98xx->i2c->addr); + return -ENOMEM; + } + + mutex_lock(&tfa98xx->dsp_lock); + error = dsp_msg_read(tfa98xx->tfa, count, buffer); + mutex_unlock(&tfa98xx->dsp_lock); + if (error != Tfa98xx_Error_Ok) { + pr_debug("[0x%x] dsp_msg_read error: %d\n", tfa98xx->i2c->addr, error); + kfree(buffer); + return -EFAULT; + } + + ret = copy_to_user(user_buf, buffer, count); + kfree(buffer); + if (ret) + return -EFAULT; + + *ppos += count; + return count; +} +#endif + +#ifdef TFA9874_NONDSP_STEREO +static ssize_t tfa98xx_dbgfs_rpc_send(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + uint8_t *buffer; + int err = 0; + + if (count == 0) + return 0; + + /* msg_file.name is not used */ + buffer = kmalloc(count, GFP_KERNEL); + if ( buffer == NULL ) { + pr_err("[0x%x] can not allocate memory\n", i2c->addr); + return -ENOMEM; + } + if (copy_from_user(buffer, user_buf, count)) + return -EFAULT; + + mutex_lock(&tfa98xx->dsp_lock); + + err = send_tfa_cal_apr(buffer, count, false); + if (err) { + pr_err("[0x%x] dsp_msg error: %d\n", i2c->addr, err); + } + + mdelay(tfa98xx_rpc_send_delay); + + mutex_unlock(&tfa98xx->dsp_lock); + + kfree(buffer); + + if (err) + return err; + return count; +} +#else +static ssize_t tfa98xx_dbgfs_rpc_send(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct i2c_client *i2c = file->private_data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + nxpTfaFileDsc_t *msg_file; + enum Tfa98xx_Error error; + int err = 0; + + if (tfa98xx->tfa == NULL) { + pr_debug("[0x%x] dsp is not available\n", tfa98xx->i2c->addr); + return -ENODEV; + } + + if (count == 0) + return 0; + + /* msg_file.name is not used */ + msg_file = kmalloc(count + sizeof(nxpTfaFileDsc_t), GFP_KERNEL); + if ( msg_file == NULL ) { + pr_debug("[0x%x] can not allocate memory\n", tfa98xx->i2c->addr); + return -ENOMEM; + } + msg_file->size = count; + + if (copy_from_user(msg_file->data, user_buf, count)) + return -EFAULT; + + mutex_lock(&tfa98xx->dsp_lock); + if ((msg_file->data[0] == 'M') && (msg_file->data[1] == 'G')) { + error = tfaContWriteFile(tfa98xx->tfa, msg_file, 0, 0); /* int vstep_idx, int vstep_msg_idx both 0 */ + if (error != Tfa98xx_Error_Ok) { + pr_debug("[0x%x] tfaContWriteFile error: %d\n", tfa98xx->i2c->addr, error); + err = -EIO; + } + } else { + error = dsp_msg(tfa98xx->tfa, msg_file->size, msg_file->data); + if (error != Tfa98xx_Error_Ok) { + pr_debug("[0x%x] dsp_msg error: %d\n", tfa98xx->i2c->addr, error); + err = -EIO; + } + } + mutex_unlock(&tfa98xx->dsp_lock); + + kfree(msg_file); + + if (err) + return err; + return count; +} +#endif +/* -- RPC */ + +static int tfa98xx_dbgfs_pga_gain_get(void *data, u64 *val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + unsigned int value; + + value = tfa_get_pga_gain(tfa98xx->tfa); + if (value < 0) + return -EINVAL; + + *val = value; + return 0; +} + +static int tfa98xx_dbgfs_pga_gain_set(void *data, u64 val) +{ + struct i2c_client *i2c = (struct i2c_client *)data; + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + uint16_t value; + int err; + + value = val & 0xffff; + if (value > 7) + return -EINVAL; + + err = tfa_set_pga_gain(tfa98xx->tfa, value); + if (err < 0) + return -EINVAL; + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_calib_otc_fops, tfa98xx_dbgfs_otc_get, + tfa98xx_dbgfs_otc_set, "%llu\n"); +DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_calib_mtpex_fops, tfa98xx_dbgfs_mtpex_get, + tfa98xx_dbgfs_mtpex_set, "%llu\n"); +DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_calib_temp_fops, tfa98xx_dbgfs_temp_get, + tfa98xx_dbgfs_temp_set, "%llu\n"); + +DEFINE_SIMPLE_ATTRIBUTE(tfa98xx_dbgfs_pga_gain_fops, tfa98xx_dbgfs_pga_gain_get, + tfa98xx_dbgfs_pga_gain_set, "%llu\n"); + +static const struct file_operations tfa98xx_dbgfs_calib_start_fops = { + .open = simple_open, + .write = tfa98xx_dbgfs_start_set, + .llseek = default_llseek, +}; + +static const struct file_operations tfa98xx_dbgfs_r_fops = { + .open = simple_open, + .read = tfa98xx_dbgfs_r_read, + .llseek = default_llseek, +}; + +static const struct file_operations tfa98xx_dbgfs_version_fops = { + .open = simple_open, + .read = tfa98xx_dbgfs_version_read, + .llseek = default_llseek, +}; + +static const struct file_operations tfa98xx_dbgfs_dsp_state_fops = { + .open = simple_open, + .read = tfa98xx_dbgfs_dsp_state_get, + .write = tfa98xx_dbgfs_dsp_state_set, + .llseek = default_llseek, +}; + +static const struct file_operations tfa98xx_dbgfs_fw_state_fops = { + .open = simple_open, + .read = tfa98xx_dbgfs_fw_state_get, + .llseek = default_llseek, +}; + +static const struct file_operations tfa98xx_dbgfs_rpc_fops = { + .open = simple_open, + .read = tfa98xx_dbgfs_rpc_read, + .write = tfa98xx_dbgfs_rpc_send, + .llseek = default_llseek, +}; + +static void tfa98xx_debug_init(struct tfa98xx *tfa98xx, struct i2c_client *i2c) +{ + char name[50]; + + scnprintf(name, MAX_CONTROL_NAME, "%s-%x", i2c->name, i2c->addr); + tfa98xx->dbg_dir = debugfs_create_dir(name, NULL); + debugfs_create_file("OTC", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_calib_otc_fops); + debugfs_create_file("MTPEX", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_calib_mtpex_fops); + debugfs_create_file("TEMP", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_calib_temp_fops); + debugfs_create_file("calibrate", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_calib_start_fops); + debugfs_create_file("R", S_IRUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_r_fops); + debugfs_create_file("version", S_IRUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_version_fops); + debugfs_create_file("dsp-state", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_dsp_state_fops); + debugfs_create_file("fw-state", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_fw_state_fops); + debugfs_create_file("rpc", S_IRUGO|S_IWUGO, tfa98xx->dbg_dir, + i2c, &tfa98xx_dbgfs_rpc_fops); + + if (tfa98xx->flags & TFA98XX_FLAG_SAAM_AVAILABLE) { + dev_dbg(tfa98xx->dev, "Adding pga_gain debug interface\n"); + debugfs_create_file("pga_gain", S_IRUGO, tfa98xx->dbg_dir, + tfa98xx->i2c, + &tfa98xx_dbgfs_pga_gain_fops); + } +} + +static void tfa98xx_debug_remove(struct tfa98xx *tfa98xx) +{ + if (tfa98xx->dbg_dir) + debugfs_remove_recursive(tfa98xx->dbg_dir); +} +#endif + + +/* copies the profile basename (i.e. part until .) into buf */ +static void get_profile_basename(char* buf, char* profile) +{ + int cp_len = 0, idx = 0; + char *pch; + + pch = strchr(profile, '.'); + idx = pch - profile; + cp_len = (pch != NULL) ? idx : (int) strlen(profile); + memcpy(buf, profile, cp_len); + buf[cp_len] = 0; +} + +/* return the profile name accociated with id from the profile list */ +static int get_profile_from_list(char *buf, int id) +{ + struct tfa98xx_baseprofile *bprof; + + list_for_each_entry(bprof, &profile_list, list) { + if (bprof->item_id == id) { + strcpy(buf, bprof->basename); + return 0; + } + } + + return -1; +} + +/* search for the profile in the profile list */ +static int is_profile_in_list(char *profile, int len) +{ + struct tfa98xx_baseprofile *bprof; + + list_for_each_entry(bprof, &profile_list, list) { + + if ((len == bprof->len) && (0 == strncmp(bprof->basename, profile, len))) + return 1; + } + + return 0; +} + +/* + * for the profile with id, look if the requested samplerate is + * supported, if found return the (container)profile for this + * samplerate, on error or if not found return -1 + */ +static int get_profile_id_for_sr(int id, unsigned int rate) +{ + int idx = 0; + struct tfa98xx_baseprofile *bprof; + + list_for_each_entry(bprof, &profile_list, list) { + if (id == bprof->item_id) { + idx = tfa98xx_get_fssel(rate); + if (idx < 0) { + /* samplerate not supported */ + return -1; + } + + return bprof->sr_rate_sup[idx]; + } + } + + /* profile not found */ + return -1; +} + +/* check if this profile is a calibration profile */ +static int is_calibration_profile(char *profile) +{ + if (strstr(profile, ".cal") != NULL) + return 1; + return 0; +} + +/* + * adds the (container)profile index of the samplerate found in + * the (container)profile to a fixed samplerate table in the (mixer)profile + */ +static int add_sr_to_profile(struct tfa98xx *tfa98xx, char *basename, int len, int profile) +{ + struct tfa98xx_baseprofile *bprof; + int idx = 0; + unsigned int sr = 0; + + list_for_each_entry(bprof, &profile_list, list) { + if ((len == bprof->len) && (0 == strncmp(bprof->basename, basename, len))) { + /* add supported samplerate for this profile */ + sr = tfa98xx_get_profile_sr(tfa98xx->tfa, profile); + if (!sr) { + pr_err("unable to identify supported sample rate for %s\n", bprof->basename); + return -1; + } + + /* get the index for this samplerate */ + idx = tfa98xx_get_fssel(sr); + if (idx < 0 || idx >= TFA98XX_NUM_RATES) { + pr_err("invalid index for samplerate %d\n", idx); + return -1; + } + + /* enter the (container)profile for this samplerate at the corresponding index */ + bprof->sr_rate_sup[idx] = profile; + + pr_debug("added profile:samplerate = [%d:%d] for mixer profile: %s\n", profile, sr, bprof->basename); + } + } + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) +static struct snd_soc_codec *snd_soc_kcontrol_codec(struct snd_kcontrol *kcontrol) +{ + return snd_kcontrol_chip(kcontrol); +} +#endif + +static int tfa98xx_get_vstep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + int mixer_profile = kcontrol->private_value; + int ret = 0; + int profile; + + profile = get_profile_id_for_sr(mixer_profile, tfa98xx->rate); + if (profile < 0) { + pr_err("tfa98xx: tfa98xx_get_vstep: invalid profile %d (mixer_profile=%d, rate=%d)\n", profile, mixer_profile, tfa98xx->rate); + return -EINVAL; + } + + mutex_lock(&tfa98xx_mutex); + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + int vstep = tfa98xx->prof_vsteps[profile]; + + ucontrol->value.integer.value[tfa98xx->tfa->dev_idx] = + tfacont_get_max_vstep(tfa98xx->tfa, profile) + - vstep - 1; + } + mutex_unlock(&tfa98xx_mutex); + + return ret; +} + +static int tfa98xx_set_vstep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + int mixer_profile = kcontrol->private_value; + int profile; + int err = 0; + int change = 0; + + if (no_start != 0) + return 0; + + profile = get_profile_id_for_sr(mixer_profile, tfa98xx->rate); + if (profile < 0) { + pr_err("tfa98xx: tfa98xx_set_vstep: invalid profile %d (mixer_profile=%d, rate=%d)\n", profile, mixer_profile, tfa98xx->rate); + return -EINVAL; + } + + mutex_lock(&tfa98xx_mutex); + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + int vstep, vsteps; + int ready = 0; + int new_vstep; + int value = ucontrol->value.integer.value[tfa98xx->tfa->dev_idx]; + + vstep = tfa98xx->prof_vsteps[profile]; + vsteps = tfacont_get_max_vstep(tfa98xx->tfa, profile); + + if (vstep == vsteps - value - 1) + continue; + + new_vstep = vsteps - value - 1; + + if (new_vstep < 0) + new_vstep = 0; + + tfa98xx->prof_vsteps[profile] = new_vstep; + +#ifndef TFA98XX_ALSA_CTRL_PROF_CHG_ON_VOL + if (profile == tfa98xx->profile) { +#endif + /* this is the active profile, program the new vstep */ + tfa98xx->vstep = new_vstep; + mutex_lock(&tfa98xx->dsp_lock); + tfa98xx_dsp_system_stable(tfa98xx->tfa, &ready); + + if (ready) { + err = tfa98xx_tfa_start(tfa98xx, tfa98xx->profile, tfa98xx->vstep); + if (err) { + pr_err("Write vstep error: %d\n", err); + } else { + pr_debug("Succesfully changed vstep index!\n"); + change = 1; + } + } + + mutex_unlock(&tfa98xx->dsp_lock); +#ifndef TFA98XX_ALSA_CTRL_PROF_CHG_ON_VOL + } +#endif + pr_debug("%d: vstep:%d, (control value: %d) - profile %d\n", + tfa98xx->tfa->dev_idx, new_vstep, value, profile); + } + + if (change) { + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + mutex_lock(&tfa98xx->dsp_lock); + tfa_dev_set_state(tfa98xx->tfa, TFA_STATE_UNMUTE); + mutex_unlock(&tfa98xx->dsp_lock); + } + } + + mutex_unlock(&tfa98xx_mutex); + + return change; +} + +static int tfa98xx_info_vstep(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + + int mixer_profile = tfa98xx_mixer_profile; + int profile = get_profile_id_for_sr(mixer_profile, tfa98xx->rate); + if (profile < 0) { + pr_err("tfa98xx: tfa98xx_info_vstep: invalid profile %d (mixer_profile=%d, rate=%d)\n", profile, mixer_profile, tfa98xx->rate); + return -EINVAL; + } + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + mutex_lock(&tfa98xx_mutex); + uinfo->count = tfa98xx_device_count; + mutex_unlock(&tfa98xx_mutex); + uinfo->value.integer.min = 0; + uinfo->value.integer.max = max(0, tfacont_get_max_vstep(tfa98xx->tfa, profile) - 1); + pr_debug("vsteps count: %d [prof=%d]\n", tfacont_get_max_vstep(tfa98xx->tfa, profile), + profile); + return 0; +} + +static int tfa98xx_get_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + mutex_lock(&tfa98xx_mutex); + ucontrol->value.integer.value[0] = tfa98xx_mixer_profile; + mutex_unlock(&tfa98xx_mutex); + + return 0; +} + +static int tfa98xx_set_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + int change = 0; + int new_profile; + int prof_idx; + int profile_count = tfa98xx_mixer_profiles; + int profile = tfa98xx_mixer_profile; + + if (no_start != 0) + return 0; + + new_profile = ucontrol->value.integer.value[0]; + if (new_profile == profile) + return 0; + + if ((new_profile < 0) || (new_profile >= profile_count)) { + pr_err("not existing profile (%d)\n", new_profile); + return -EINVAL; + } + + /* get the container profile for the requested sample rate */ + prof_idx = get_profile_id_for_sr(new_profile, tfa98xx->rate); + if (prof_idx < 0) { + pr_err("tfa98xx: sample rate [%d] not supported for this mixer profile [%d].\n", tfa98xx->rate, new_profile); + return 0; + } + pr_debug("selected container profile [%d]\n", prof_idx); + + /* update mixer profile */ + tfa98xx_mixer_profile = new_profile; + + mutex_lock(&tfa98xx_mutex); + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + int err; + int ready = 0; + + /* update 'real' profile (container profile) */ + tfa98xx->profile = prof_idx; + tfa98xx->vstep = tfa98xx->prof_vsteps[prof_idx]; + + /* Don't call tfa_dev_start() if there is no clock. */ + mutex_lock(&tfa98xx->dsp_lock); + tfa98xx_dsp_system_stable(tfa98xx->tfa, &ready); + if (ready) { + /* Also re-enables the interrupts */ + err = tfa98xx_tfa_start(tfa98xx, prof_idx, tfa98xx->vstep); + if (err) { + pr_info("Write profile error: %d\n", err); + } else { + pr_debug("Changed to profile %d (vstep = %d)\n", + prof_idx, tfa98xx->vstep); + change = 1; + } + } + mutex_unlock(&tfa98xx->dsp_lock); + + /* Flag DSP as invalidated as the profile change may invalidate the + * current DSP configuration. That way, further stream start can + * trigger a tfa_dev_start. + */ + tfa98xx->dsp_init = TFA98XX_DSP_INIT_INVALIDATED; + } + + if (change) { + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + mutex_lock(&tfa98xx->dsp_lock); + tfa_dev_set_state(tfa98xx->tfa, TFA_STATE_UNMUTE); + mutex_unlock(&tfa98xx->dsp_lock); + } + } + + mutex_unlock(&tfa98xx_mutex); + + return change; +} + +static int tfa98xx_info_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + char profile_name[MAX_CONTROL_NAME] = {0}; + int count = tfa98xx_mixer_profiles, err = -1; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = count; + + if (uinfo->value.enumerated.item >= count) + uinfo->value.enumerated.item = count - 1; + + err = get_profile_from_list(profile_name, uinfo->value.enumerated.item); + if (err != 0) + return -EINVAL; + + strcpy(uinfo->value.enumerated.name, profile_name); + + return 0; +} + +static int tfa98xx_info_stop_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + mutex_lock(&tfa98xx_mutex); + uinfo->count = tfa98xx_device_count; + mutex_unlock(&tfa98xx_mutex); + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + + return 0; +} + +static int tfa98xx_get_stop_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tfa98xx *tfa98xx; + + mutex_lock(&tfa98xx_mutex); + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + ucontrol->value.integer.value[tfa98xx->tfa->dev_idx] = 0; + } + mutex_unlock(&tfa98xx_mutex); + + return 0; +} + +static int tfa98xx_set_stop_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tfa98xx *tfa98xx; + + mutex_lock(&tfa98xx_mutex); + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + int ready = 0; + int i = tfa98xx->tfa->dev_idx; + + pr_debug("%d: %ld\n", i, ucontrol->value.integer.value[i]); + + tfa98xx_dsp_system_stable(tfa98xx->tfa, &ready); + + if ((ucontrol->value.integer.value[i] != 0) && ready) { + cancel_delayed_work_sync(&tfa98xx->monitor_work); + + cancel_delayed_work_sync(&tfa98xx->init_work); + if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK) + continue; + + mutex_lock(&tfa98xx->dsp_lock); + tfa_dev_stop(tfa98xx->tfa); + tfa98xx->dsp_init = TFA98XX_DSP_INIT_STOPPED; + mutex_unlock(&tfa98xx->dsp_lock); + } + + ucontrol->value.integer.value[i] = 0; + } + mutex_unlock(&tfa98xx_mutex); + + return 1; +} + +static int tfa98xx_info_cal_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + mutex_lock(&tfa98xx_mutex); + uinfo->count = tfa98xx_device_count; + mutex_unlock(&tfa98xx_mutex); + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xffff; /* 16 bit value */ + + return 0; +} + +static int tfa98xx_set_cal_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tfa98xx *tfa98xx; + + mutex_lock(&tfa98xx_mutex); + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + enum tfa_error err; + int i = tfa98xx->tfa->dev_idx; + + tfa98xx->cal_data = (uint16_t)ucontrol->value.integer.value[i]; + + mutex_lock(&tfa98xx->dsp_lock); + err = tfa98xx_write_re25(tfa98xx->tfa, tfa98xx->cal_data); + tfa98xx->set_mtp_cal = (err != tfa_error_ok); + if (tfa98xx->set_mtp_cal == false) { + pr_info("Calibration value (%d) set in mtp\n", + tfa98xx->cal_data); + } + mutex_unlock(&tfa98xx->dsp_lock); + } + mutex_unlock(&tfa98xx_mutex); + + return 1; +} + +static int tfa98xx_get_cal_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tfa98xx *tfa98xx; + + mutex_lock(&tfa98xx_mutex); + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + mutex_lock(&tfa98xx->dsp_lock); + ucontrol->value.integer.value[tfa98xx->tfa->dev_idx] = tfa_dev_mtp_get(tfa98xx->tfa, TFA_MTP_RE25_PRIM); + mutex_unlock(&tfa98xx->dsp_lock); + } + mutex_unlock(&tfa98xx_mutex); + + return 0; +} + +#ifdef TFA9874_NONDSP_STEREO +#define CHIP_SELECTOR_STEREO (0) +#define CHIP_SELECTOR_LEFT (1) +#define CHIP_SELECTOR_RIGHT (2) +#define CHIP_SELECTOR_RCV (3) +#define CHIP_LEFT_ADDR (0x34) +#define CHIP_RIGHT_ADDR (0x35) +#define CHIP_RCV_ADDR (0x34) + +static int tfa98xx_info_stereo_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 3; + return 0; +} + +static int tfa98xx_set_stereo_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tfa98xx *tfa98xx; + int selector; + + selector = ucontrol->value.integer.value[0]; + + mutex_lock(&tfa98xx_mutex); + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + + if (selector == CHIP_SELECTOR_LEFT) { + if (tfa98xx->i2c->addr == CHIP_LEFT_ADDR) + tfa98xx->flags |= TFA98XX_FLAG_CHIP_SELECTED; + else + tfa98xx->flags &= ~TFA98XX_FLAG_CHIP_SELECTED; + } + else if (selector == CHIP_SELECTOR_RIGHT) { + if (tfa98xx->i2c->addr == CHIP_RIGHT_ADDR) + tfa98xx->flags |= TFA98XX_FLAG_CHIP_SELECTED; + else + tfa98xx->flags &= ~TFA98XX_FLAG_CHIP_SELECTED; + } + else if (selector == CHIP_SELECTOR_RCV) { + if (tfa98xx->i2c->addr == CHIP_RCV_ADDR) { + tfa98xx->flags |= TFA98XX_FLAG_CHIP_SELECTED; + tfa98xx->profile = 1; + tfa98xx_mixer_profile = 1; + } + else + tfa98xx->flags &= ~TFA98XX_FLAG_CHIP_SELECTED; + } + else { + tfa98xx->flags |= TFA98XX_FLAG_CHIP_SELECTED; + tfa98xx->profile = 0; + tfa98xx_mixer_profile = 0; + } + } + mutex_unlock(&tfa98xx_mutex); + + return 1; +} + +static int tfa98xx_get_stereo_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct tfa98xx *tfa98xx; + + mutex_lock(&tfa98xx_mutex); + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + ucontrol->value.integer.value[0] = tfa98xx->flags; + } + mutex_unlock(&tfa98xx_mutex); + + return 0; +} + +static int tfa98xx_algo_info_gain_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFF; + return 0; +} + +static int tfa98xx_algo_set_gain_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + u8 buff[8] = {0}, nr = 1; + + ((int32_t *)buff)[0] = ucontrol->value.integer.value[0]&0xff; + atomic_set(&g_Gain, ((int32_t *)buff)[0]); + + buff[nr++] = 0x04; /* 0x04: channel: mono */ + buff[nr++] = 0x81; + buff[nr++] = 0x04; + + buff[nr++] = 0x00; + buff[nr++] = 0x00; + buff[nr++] = buff[0]; /* 00:0dB, ff:-127.5dB, 0.5dB/scale */ + + if(0 == tfa98xx_mixer_profile) { + ret = send_tfa_cal_in_band(&buff[1], nr - 1); + } + return ret; +} + +static int tfa98xx_algo_get_gain_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int32_t ret = 0; + ucontrol->value.integer.value[0] = atomic_read(&g_Gain); + return ret; +} +#endif + +static int tfa98xx_info_mode_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 3; + return 0; +} + +static int tfa98xx_set_mode_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 1; +} + +static int tfa98xx_get_mode_ctl(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 0; +} + +static int tfa98xx_create_controls(struct tfa98xx *tfa98xx) +{ + int prof, nprof, mix_index = 0; + int nr_controls = 0, id = 0; + char *name; + int ret = 0; + struct tfa98xx_baseprofile *bprofile; + + /* Create the following controls: + * - enum control to select the active profile + * - one volume control for each profile hosting a vstep + * - Stop control on TFA1 devices + */ + + nr_controls = 2; /* Profile and stop control */ + + if (tfa98xx->flags & TFA98XX_FLAG_CALIBRATION_CTL) + nr_controls += 1; /* calibration */ + +#ifdef TFA9874_NONDSP_STEREO + nr_controls += 1; /* for TFA_CHIP_SELECTOR */ + nr_controls += 1; /* for NXP Volume */ +#endif + nr_controls += 1; /* for NXP Mode */ + + /* allocate the tfa98xx_controls base on the nr of profiles */ + nprof = tfa_cnt_get_dev_nprof(tfa98xx->tfa); + for (prof = 0; prof < nprof; prof++) { + if (tfacont_get_max_vstep(tfa98xx->tfa, prof)) + nr_controls++; /* Playback Volume control */ + } + + tfa98xx_controls = devm_kzalloc(tfa98xx->codec->dev, + nr_controls * sizeof(tfa98xx_controls[0]), GFP_KERNEL); + if (!tfa98xx_controls) + return -ENOMEM; + + /* Create a mixer item for selecting the active profile */ + name = devm_kzalloc(tfa98xx->codec->dev, MAX_CONTROL_NAME, GFP_KERNEL); + if (!name) + return -ENOMEM; + scnprintf(name, MAX_CONTROL_NAME, "%s Profile", tfa98xx->fw.name); + tfa98xx_controls[mix_index].name = name; + tfa98xx_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + tfa98xx_controls[mix_index].info = tfa98xx_info_profile; + tfa98xx_controls[mix_index].get = tfa98xx_get_profile; + tfa98xx_controls[mix_index].put = tfa98xx_set_profile; + // tfa98xx_controls[mix_index].private_value = profs; /* save number of profiles */ + mix_index++; + + /* create mixer items for each profile that has volume */ + for (prof = 0; prof < nprof; prof++) { + /* create an new empty profile */ + bprofile = devm_kzalloc(tfa98xx->codec->dev, sizeof(*bprofile), GFP_KERNEL); + if (!bprofile) + return -ENOMEM; + + bprofile->len = 0; + bprofile->item_id = -1; + INIT_LIST_HEAD(&bprofile->list); + + /* copy profile name into basename until the . */ + get_profile_basename(bprofile->basename, tfa_cont_profile_name(tfa98xx, prof)); + bprofile->len = strlen(bprofile->basename); + + /* + * search the profile list for a profile with basename, if it is not found then + * add it to the list and add a new mixer control (if it has vsteps) + * also, if it is a calibration profile, do not add it to the list + */ + if ((is_profile_in_list(bprofile->basename, bprofile->len) == 0) && + is_calibration_profile(tfa_cont_profile_name(tfa98xx, prof)) == 0) { + /* the profile is not present, add it to the list */ + list_add(&bprofile->list, &profile_list); + bprofile->item_id = id++; + + pr_debug("profile added [%d]: %s\n", bprofile->item_id, bprofile->basename); + + if (tfacont_get_max_vstep(tfa98xx->tfa, prof)) { + name = devm_kzalloc(tfa98xx->codec->dev, MAX_CONTROL_NAME, GFP_KERNEL); + if (!name) + return -ENOMEM; + + scnprintf(name, MAX_CONTROL_NAME, "%s %s Playback Volume", + tfa98xx->fw.name, bprofile->basename); + + tfa98xx_controls[mix_index].name = name; + tfa98xx_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + tfa98xx_controls[mix_index].info = tfa98xx_info_vstep; + tfa98xx_controls[mix_index].get = tfa98xx_get_vstep; + tfa98xx_controls[mix_index].put = tfa98xx_set_vstep; + tfa98xx_controls[mix_index].private_value = bprofile->item_id; /* save profile index */ + mix_index++; + } + } + + /* look for the basename profile in the list of mixer profiles and add the + container profile index to the supported samplerates of this mixer profile */ + add_sr_to_profile(tfa98xx, bprofile->basename, bprofile->len, prof); + } + + /* set the number of user selectable profiles in the mixer */ + tfa98xx_mixer_profiles = id; + + /* Create a mixer item for stop control on TFA1 */ + name = devm_kzalloc(tfa98xx->codec->dev, MAX_CONTROL_NAME, GFP_KERNEL); + if (!name) + return -ENOMEM; + + scnprintf(name, MAX_CONTROL_NAME, "%s Stop", tfa98xx->fw.name); + tfa98xx_controls[mix_index].name = name; + tfa98xx_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + tfa98xx_controls[mix_index].info = tfa98xx_info_stop_ctl; + tfa98xx_controls[mix_index].get = tfa98xx_get_stop_ctl; + tfa98xx_controls[mix_index].put = tfa98xx_set_stop_ctl; + mix_index++; + + if (tfa98xx->flags & TFA98XX_FLAG_CALIBRATION_CTL) { + name = devm_kzalloc(tfa98xx->codec->dev, MAX_CONTROL_NAME, GFP_KERNEL); + if (!name) + return -ENOMEM; + + scnprintf(name, MAX_CONTROL_NAME, "%s Calibration", tfa98xx->fw.name); + tfa98xx_controls[mix_index].name = name; + tfa98xx_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + tfa98xx_controls[mix_index].info = tfa98xx_info_cal_ctl; + tfa98xx_controls[mix_index].get = tfa98xx_get_cal_ctl; + tfa98xx_controls[mix_index].put = tfa98xx_set_cal_ctl; + mix_index++; + } + +#ifdef TFA9874_NONDSP_STEREO + tfa98xx_controls[mix_index].name = "TFA_CHIP_SELECTOR"; + tfa98xx_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + tfa98xx_controls[mix_index].info = tfa98xx_info_stereo_ctl; + tfa98xx_controls[mix_index].get = tfa98xx_get_stereo_ctl; + tfa98xx_controls[mix_index].put = tfa98xx_set_stereo_ctl; + mix_index++; + + tfa98xx_controls[mix_index].name = "NXP Volume"; + tfa98xx_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + tfa98xx_controls[mix_index].info = tfa98xx_algo_info_gain_ctl; + tfa98xx_controls[mix_index].get = tfa98xx_algo_get_gain_ctl; + tfa98xx_controls[mix_index].put = tfa98xx_algo_set_gain_ctl; + mix_index++; +#endif + + tfa98xx_controls[mix_index].name = "NXP Mode"; + tfa98xx_controls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + tfa98xx_controls[mix_index].info = tfa98xx_info_mode_ctl; + tfa98xx_controls[mix_index].get = tfa98xx_get_mode_ctl; + tfa98xx_controls[mix_index].put = tfa98xx_set_mode_ctl; + mix_index++; + + ret = snd_soc_add_codec_controls(tfa98xx->codec, + tfa98xx_controls, mix_index); + +#ifdef TFA9874_NONDSP_STEREO + ret = snd_soc_add_codec_controls(tfa98xx->codec, + tfa987x_algo_filter_mixer_controls, ARRAY_SIZE(tfa987x_algo_filter_mixer_controls)); +#endif + return ret; +} + +static void *tfa98xx_devm_kstrdup(struct device *dev, char *buf) +{ + char *str = devm_kzalloc(dev, strlen(buf) + 1, GFP_KERNEL); + if (!str) + return str; + memcpy(str, buf, strlen(buf)); + return str; +} + +static int tfa98xx_append_i2c_address(struct device *dev, + struct i2c_client *i2c, + struct snd_soc_dapm_widget *widgets, + int num_widgets, + struct snd_soc_dai_driver *dai_drv, + int num_dai) +{ + char buf[50]; + int i; + int i2cbus = i2c->adapter->nr; + int addr = i2c->addr; + if (dai_drv && num_dai > 0) + for(i = 0; i < num_dai; i++) { + snprintf(buf, 50, "%s-%x-%x",dai_drv[i].name, i2cbus, + addr); + dai_drv[i].name = tfa98xx_devm_kstrdup(dev, buf); + + snprintf(buf, 50, "%s-%x-%x", + dai_drv[i].playback.stream_name, + i2cbus, addr); + dai_drv[i].playback.stream_name = tfa98xx_devm_kstrdup(dev, buf); + + snprintf(buf, 50, "%s-%x-%x", + dai_drv[i].capture.stream_name, + i2cbus, addr); + dai_drv[i].capture.stream_name = tfa98xx_devm_kstrdup(dev, buf); + } + + /* the idea behind this is convert: + * SND_SOC_DAPM_AIF_IN("AIF IN", "AIF Playback", 0, SND_SOC_NOPM, 0, 0), + * into: + * SND_SOC_DAPM_AIF_IN("AIF IN", "AIF Playback-2-36", 0, SND_SOC_NOPM, 0, 0), + */ + if (widgets && num_widgets > 0) + for(i = 0; i < num_widgets; i++) { + if(!widgets[i].sname) + continue; + if((widgets[i].id == snd_soc_dapm_aif_in) + || (widgets[i].id == snd_soc_dapm_aif_out)) { + snprintf(buf, 50, "%s-%x-%x", widgets[i].sname, + i2cbus, addr); + widgets[i].sname = tfa98xx_devm_kstrdup(dev, buf); + } + } + + return 0; +} + +static struct snd_soc_dapm_widget tfa98xx_dapm_widgets_common[] = { + /* Stream widgets */ + SND_SOC_DAPM_AIF_IN("AIF IN", "AIF Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF OUT", "AIF Capture", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_OUTPUT("OUTL"), + SND_SOC_DAPM_INPUT("AEC Loopback"), +}; + +static struct snd_soc_dapm_widget tfa98xx_dapm_widgets_stereo[] = { + SND_SOC_DAPM_OUTPUT("OUTR"), +}; + +static struct snd_soc_dapm_widget tfa98xx_dapm_widgets_saam[] = { + SND_SOC_DAPM_INPUT("SAAM MIC"), +}; + +static struct snd_soc_dapm_widget tfa9888_dapm_inputs[] = { + SND_SOC_DAPM_INPUT("DMIC1"), + SND_SOC_DAPM_INPUT("DMIC2"), + SND_SOC_DAPM_INPUT("DMIC3"), + SND_SOC_DAPM_INPUT("DMIC4"), +}; + +static const struct snd_soc_dapm_route tfa98xx_dapm_routes_common[] = { + { "OUTL", NULL, "AIF IN" }, + { "AIF OUT", NULL, "AEC Loopback" }, +}; + +static const struct snd_soc_dapm_route tfa98xx_dapm_routes_saam[] = { + { "AIF OUT", NULL, "SAAM MIC" }, +}; + +static const struct snd_soc_dapm_route tfa98xx_dapm_routes_stereo[] = { + { "OUTR", NULL, "AIF IN" }, +}; + +static const struct snd_soc_dapm_route tfa9888_input_dapm_routes[] = { + { "AIF OUT", NULL, "DMIC1" }, + { "AIF OUT", NULL, "DMIC2" }, + { "AIF OUT", NULL, "DMIC3" }, + { "AIF OUT", NULL, "DMIC4" }, +}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4,2,0) +static struct snd_soc_dapm_context *snd_soc_codec_get_dapm(struct snd_soc_codec *codec) +{ + return &codec->dapm; +} +#endif + +static void tfa98xx_add_widgets(struct tfa98xx *tfa98xx) +{ + struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(tfa98xx->codec); + struct snd_soc_dapm_widget *widgets; + unsigned int num_dapm_widgets = ARRAY_SIZE(tfa98xx_dapm_widgets_common); + + widgets = devm_kzalloc(&tfa98xx->i2c->dev, + sizeof(struct snd_soc_dapm_widget) * + ARRAY_SIZE(tfa98xx_dapm_widgets_common), + GFP_KERNEL); + if (!widgets) + return; + memcpy(widgets, tfa98xx_dapm_widgets_common, + sizeof(struct snd_soc_dapm_widget) * + ARRAY_SIZE(tfa98xx_dapm_widgets_common)); + + tfa98xx_append_i2c_address(&tfa98xx->i2c->dev, + tfa98xx->i2c, + widgets, + num_dapm_widgets, + NULL, + 0); + + snd_soc_dapm_new_controls(dapm, widgets, + ARRAY_SIZE(tfa98xx_dapm_widgets_common)); + snd_soc_dapm_add_routes(dapm, tfa98xx_dapm_routes_common, + ARRAY_SIZE(tfa98xx_dapm_routes_common)); + + if (tfa98xx->flags & TFA98XX_FLAG_STEREO_DEVICE) { + snd_soc_dapm_new_controls(dapm, tfa98xx_dapm_widgets_stereo, + ARRAY_SIZE(tfa98xx_dapm_widgets_stereo)); + snd_soc_dapm_add_routes(dapm, tfa98xx_dapm_routes_stereo, + ARRAY_SIZE(tfa98xx_dapm_routes_stereo)); + } + + if (tfa98xx->flags & TFA98XX_FLAG_MULTI_MIC_INPUTS) { + snd_soc_dapm_new_controls(dapm, tfa9888_dapm_inputs, + ARRAY_SIZE(tfa9888_dapm_inputs)); + snd_soc_dapm_add_routes(dapm, tfa9888_input_dapm_routes, + ARRAY_SIZE(tfa9888_input_dapm_routes)); + } + + if (tfa98xx->flags & TFA98XX_FLAG_SAAM_AVAILABLE) { + snd_soc_dapm_new_controls(dapm, tfa98xx_dapm_widgets_saam, + ARRAY_SIZE(tfa98xx_dapm_widgets_saam)); + snd_soc_dapm_add_routes(dapm, tfa98xx_dapm_routes_saam, + ARRAY_SIZE(tfa98xx_dapm_routes_saam)); + } +} + +/* I2C wrapper functions */ +enum Tfa98xx_Error tfa98xx_write_register16(struct tfa_device *tfa, + unsigned char subaddress, + unsigned short value) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + struct tfa98xx *tfa98xx; + int ret; + int retries = I2C_RETRIES; + + if (tfa == NULL) { + pr_err("No device available\n"); + return Tfa98xx_Error_Fail; + } + + tfa98xx = (struct tfa98xx *)tfa->data; + if (!tfa98xx || !tfa98xx->regmap) { + pr_err("No tfa98xx regmap available\n"); + return Tfa98xx_Error_Bad_Parameter; + } +retry: + ret = regmap_write(tfa98xx->regmap, subaddress, value); + if (ret < 0) { + pr_warn("i2c error, retries left: %d\n", retries); + if (retries) { + retries--; + msleep(I2C_RETRY_DELAY); + goto retry; + } + return Tfa98xx_Error_Fail; + } + if (tfa98xx_ftrace_regs) + dev_err(&tfa98xx->i2c->dev, " WR reg=0x%02x, val=0x%04x %s\n", + subaddress, value, + ret<0? "Error!!" : ""); + return error; +} + +enum Tfa98xx_Error tfa98xx_read_register16(struct tfa_device *tfa, + unsigned char subaddress, + unsigned short *val) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + struct tfa98xx *tfa98xx; + unsigned int value; + int retries = I2C_RETRIES; + int ret; + + if (tfa == NULL) { + pr_err("No device available\n"); + return Tfa98xx_Error_Fail; + } + + tfa98xx = (struct tfa98xx *)tfa->data; + if (!tfa98xx || !tfa98xx->regmap) { + pr_err("No tfa98xx regmap available\n"); + return Tfa98xx_Error_Bad_Parameter; + } +retry: + ret = regmap_read(tfa98xx->regmap, subaddress, &value); + if (ret < 0) { + pr_warn("i2c error at subaddress 0x%x, retries left: %d\n", subaddress, retries); + if (retries) { + retries--; + msleep(I2C_RETRY_DELAY); + goto retry; + } + return Tfa98xx_Error_Fail; + } + *val = value & 0xffff; + + if (tfa98xx_ftrace_regs) + dev_err(&tfa98xx->i2c->dev, "RD reg=0x%02x, val=0x%04x %s\n", + subaddress, *val, + ret<0? "Error!!" : ""); + + return error; +} + + +/* + * init external dsp + */ +enum Tfa98xx_Error +tfa98xx_init_dsp(struct tfa_device *tfa) +{ + return Tfa98xx_Error_Not_Supported; +} + +int tfa98xx_get_dsp_status(struct tfa_device *tfa) +{ + return 0; +} + +/* + * write external dsp message + */ +enum Tfa98xx_Error +tfa98xx_write_dsp(struct tfa_device *tfa, int num_bytes, const char *command_buffer) +{ + return Tfa98xx_Error_Not_Supported; +} + +/* + * read external dsp message + */ +enum Tfa98xx_Error +tfa98xx_read_dsp(struct tfa_device *tfa, int num_bytes, unsigned char *result_buffer) +{ + return Tfa98xx_Error_Not_Supported; +} +/* + * write/read external dsp message + */ +enum Tfa98xx_Error +tfa98xx_writeread_dsp(struct tfa_device *tfa, int command_length, void *command_buffer, + int result_length, void *result_buffer) +{ + return Tfa98xx_Error_Not_Supported; +} + +enum Tfa98xx_Error tfa98xx_read_data(struct tfa_device *tfa, + unsigned char reg, + int len, unsigned char value[]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + struct tfa98xx *tfa98xx; + struct i2c_client *tfa98xx_client; + int err; + int tries = 0; + struct i2c_msg msgs[] = { + { + .flags = 0, + .len = 1, + .buf = ®, + }, { + .flags = I2C_M_RD, + .len = len, + .buf = value, + }, + }; + + if (tfa == NULL) { + pr_err("No device available\n"); + return Tfa98xx_Error_Fail; + } + + tfa98xx = (struct tfa98xx *)tfa->data; + if (tfa98xx->i2c) { + tfa98xx_client = tfa98xx->i2c; + msgs[0].addr = tfa98xx_client->addr; + msgs[1].addr = tfa98xx_client->addr; + + do { + err = i2c_transfer(tfa98xx_client->adapter, msgs, + ARRAY_SIZE(msgs)); + if (err != ARRAY_SIZE(msgs)) + msleep_interruptible(I2C_RETRY_DELAY); + } while ((err != ARRAY_SIZE(msgs)) && (++tries < I2C_RETRIES)); + + if (err != ARRAY_SIZE(msgs)) { + dev_err(&tfa98xx_client->dev, "read transfer error %d\n", + err); + error = Tfa98xx_Error_Fail; + } + + if (tfa98xx_kmsg_regs) + dev_dbg(&tfa98xx_client->dev, "RD-DAT reg=0x%02x, len=%d\n", + reg, len); + if (tfa98xx_ftrace_regs) + tfa98xx_trace_printk("\t\tRD-DAT reg=0x%02x, len=%d\n", + reg, len); + } else { + pr_err("No device available\n"); + error = Tfa98xx_Error_Fail; + } + return error; +} + +enum Tfa98xx_Error tfa98xx_write_raw(struct tfa_device *tfa, + int len, + const unsigned char data[]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + struct tfa98xx *tfa98xx; + int ret; + int retries = I2C_RETRIES; + + + if (tfa == NULL) { + pr_err("No device available\n"); + return Tfa98xx_Error_Fail; + } + + tfa98xx = (struct tfa98xx *)tfa->data; + +retry: + ret = i2c_master_send(tfa98xx->i2c, data, len); + if (ret < 0) { + pr_warn("i2c error, retries left: %d\n", retries); + if (retries) { + retries--; + msleep(I2C_RETRY_DELAY); + goto retry; + } + } + + if (ret == len) { + if (tfa98xx_kmsg_regs) + dev_dbg(&tfa98xx->i2c->dev, " WR-RAW len=%d\n", len); + if (tfa98xx_ftrace_regs) + tfa98xx_trace_printk("\t\tWR-RAW len=%d\n", len); + return Tfa98xx_Error_Ok; + } + pr_err(" WR-RAW (len=%d) Error I2C send size mismatch %d\n", len, ret); + error = Tfa98xx_Error_Fail; + + return error; +} + +/* Interrupts management */ + +static void tfa98xx_interrupt_enable_tfa2(struct tfa98xx *tfa98xx, bool enable) +{ + /* Only for 0x72 we need to enable NOCLK interrupts */ + if (tfa98xx->flags & TFA98XX_FLAG_REMOVE_PLOP_NOISE) + tfa_irq_ena(tfa98xx->tfa, tfa9912_irq_stnoclk, enable); + + if (tfa98xx->flags & TFA98XX_FLAG_LP_MODES) { + tfa_irq_ena(tfa98xx->tfa, 36, enable); /* FIXME: IELP0 does not excist for 9912 */ + tfa_irq_ena(tfa98xx->tfa, tfa9912_irq_stclpr, enable); + } +} + +/* Check if tap-detection can and shall be enabled. + * Configure SPK interrupt accordingly or setup polling mode + * Tap-detection shall be active if: + * - the service is enabled (tapdet_open), AND + * - the current profile is a tap-detection profile + * On TFA1 familiy of devices, activating tap-detection means enabling the SPK + * interrupt if available. + * We also update the tapdet_enabled and tapdet_poll variables. + */ +static void tfa98xx_tapdet_check_update(struct tfa98xx *tfa98xx) +{ + unsigned int enable = false; + + /* Support tap-detection on TFA1 family of devices */ + if ((tfa98xx->flags & TFA98XX_FLAG_TAPDET_AVAILABLE) == 0) + return; + + if (tfa98xx->tapdet_open && + (tfa98xx->tapdet_profiles & (1 << tfa98xx->profile))) + enable = true; + + if (!gpio_is_valid(tfa98xx->irq_gpio)) { + /* interrupt not available, setup polling mode */ + tfa98xx->tapdet_poll = true; + if (enable) + queue_delayed_work(tfa98xx->tfa98xx_wq, + &tfa98xx->tapdet_work, HZ/10); + else + cancel_delayed_work_sync(&tfa98xx->tapdet_work); + dev_dbg(tfa98xx->codec->dev, + "Polling for tap-detection: %s (%d; 0x%x, %d)\n", + enable? "enabled":"disabled", + tfa98xx->tapdet_open, tfa98xx->tapdet_profiles, + tfa98xx->profile); + + } else { + dev_dbg(tfa98xx->codec->dev, + "Interrupt for tap-detection: %s (%d; 0x%x, %d)\n", + enable? "enabled":"disabled", + tfa98xx->tapdet_open, tfa98xx->tapdet_profiles, + tfa98xx->profile); + /* enabled interrupt */ + tfa_irq_ena(tfa98xx->tfa, tfa9912_irq_sttapdet , enable); + } + + /* check disabled => enabled transition to clear pending events */ + if (!tfa98xx->tapdet_enabled && enable) { + /* clear pending event if any */ + tfa_irq_clear(tfa98xx->tfa, tfa9912_irq_sttapdet); + } + + if (!tfa98xx->tapdet_poll) + tfa_irq_ena(tfa98xx->tfa, tfa9912_irq_sttapdet, 1); /* enable again */ +} + +/* global enable / disable interrupts */ +static void tfa98xx_interrupt_enable(struct tfa98xx *tfa98xx, bool enable) +{ + if (tfa98xx->flags & TFA98XX_FLAG_SKIP_INTERRUPTS) + return; + + if (tfa98xx->tfa->tfa_family == 2) + tfa98xx_interrupt_enable_tfa2(tfa98xx, enable); +} + +/* Firmware management */ +static void tfa98xx_container_loaded(const struct firmware *cont, void *context) +{ + nxpTfaContainer_t *container; + struct tfa98xx *tfa98xx = context; + enum tfa_error tfa_err; + int container_size; + int ret; + + tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_FAIL; + + if (!cont) { + pr_err("Failed to read %s\n", fw_name); + return; + } + + pr_debug("loaded %s - size: %zu\n", fw_name, cont->size); + + mutex_lock(&tfa98xx_mutex); + if (tfa98xx_container == NULL) { + container = kzalloc(cont->size, GFP_KERNEL); + if (container == NULL) { + mutex_unlock(&tfa98xx_mutex); + release_firmware(cont); + pr_err("Error allocating memory\n"); + return; + } + + container_size = cont->size; + memcpy(container, cont->data, container_size); + release_firmware(cont); + + pr_debug("%.2s%.2s\n", container->version, container->subversion); + pr_debug("%.8s\n", container->customer); + pr_debug("%.8s\n", container->application); + pr_debug("%.8s\n", container->type); + pr_debug("%d ndev\n", container->ndev); + pr_debug("%d nprof\n", container->nprof); + + tfa_err = tfa_load_cnt(container, container_size); + if (tfa_err != tfa_error_ok) { + mutex_unlock(&tfa98xx_mutex); + kfree(container); + dev_err(tfa98xx->dev, "Cannot load container file, aborting\n"); + return; + } + + tfa98xx_container = container; + } else { + pr_debug("container file already loaded...\n"); + container = tfa98xx_container; + release_firmware(cont); + } + mutex_unlock(&tfa98xx_mutex); + + tfa98xx->tfa->cnt = container; + + /* + i2c transaction limited to 64k + (Documentation/i2c/writing-clients) + */ + tfa98xx->tfa->buffer_size = 65536; + + /* DSP messages via i2c */ + tfa98xx->tfa->has_msg = 0; + + if (tfa_dev_probe(tfa98xx->i2c->addr, tfa98xx->tfa) != 0) { + dev_err(tfa98xx->dev, "Failed to probe TFA98xx @ 0x%.2x\n", tfa98xx->i2c->addr); + return; + } + + tfa98xx->tfa->dev_idx = tfa_cont_get_idx(tfa98xx->tfa); + if (tfa98xx->tfa->dev_idx < 0) { + dev_err(tfa98xx->dev, "Failed to find TFA98xx @ 0x%.2x in container file\n", tfa98xx->i2c->addr); + return; + } + + /* Enable debug traces */ + tfa98xx->tfa->verbose = trace_level & 1; + + /* prefix is the application name from the cnt */ + tfa_cnt_get_app_name(tfa98xx->tfa, tfa98xx->fw.name); + + /* set default profile/vstep */ + tfa98xx->profile = 0; + tfa98xx->vstep = 0; + + /* Override default profile if requested */ + if (strcmp(dflt_prof_name, "")) { + unsigned int i; + int nprof = tfa_cnt_get_dev_nprof(tfa98xx->tfa); + for (i = 0; i < nprof; i++) { + if (strcmp(tfa_cont_profile_name(tfa98xx, i), + dflt_prof_name) == 0) { + tfa98xx->profile = i; + dev_info(tfa98xx->dev, + "changing default profile to %s (%d)\n", + dflt_prof_name, tfa98xx->profile); + break; + } + } + if (i >= nprof) + dev_info(tfa98xx->dev, + "Default profile override failed (%s profile not found)\n", + dflt_prof_name); + } + + tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_OK; + pr_debug("Firmware init complete\n"); + + if (no_start != 0) + return; + + /* Only controls for master device */ + if (tfa98xx->tfa->dev_idx == 0) + tfa98xx_create_controls(tfa98xx); + + tfa98xx_inputdev_check_register(tfa98xx); + + if (tfa_is_cold(tfa98xx->tfa) == 0) { + pr_debug("Warning: device 0x%.2x is still warm\n", tfa98xx->i2c->addr); + tfa_reset(tfa98xx->tfa); + } + + /* Preload settings using internal clock on TFA2 */ + if (tfa98xx->tfa->tfa_family == 2 && + tfa98xx->tfa->is_probus_device == 0) { + mutex_lock(&tfa98xx->dsp_lock); + ret = tfa98xx_tfa_start(tfa98xx, tfa98xx->profile, tfa98xx->vstep); + if (ret == Tfa98xx_Error_Not_Supported) + tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_FAIL; + mutex_unlock(&tfa98xx->dsp_lock); + } + + tfa98xx_interrupt_enable(tfa98xx, true); +} + +static int tfa98xx_load_container(struct tfa98xx *tfa98xx) +{ + tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_PENDING; + + return request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + fw_name, tfa98xx->dev, GFP_KERNEL, + tfa98xx, tfa98xx_container_loaded); +} + + +static void tfa98xx_tapdet(struct tfa98xx *tfa98xx) +{ + unsigned int tap_pattern; + int btn; + + /* check tap pattern (BTN_0 is "error" wrong tap indication */ + tap_pattern = tfa_get_tap_pattern(tfa98xx->tfa); + switch (tap_pattern) { + case 0xffffffff: + pr_info("More than 4 taps detected! (flagTapPattern = -1)\n"); + btn = BTN_0; + break; + case 0xfffffffe: + case 0xfe: + pr_info("Illegal tap detected!\n"); + btn = BTN_0; + break; + case 0: + pr_info("Unrecognized pattern! (flagTapPattern = 0)\n"); + btn = BTN_0; + break; + default: + pr_info("Detected pattern: %d\n", tap_pattern); + btn = BTN_0 + tap_pattern; + break; + } + + input_report_key(tfa98xx->input, btn, 1); + input_report_key(tfa98xx->input, btn, 0); + input_sync(tfa98xx->input); + + /* acknowledge event done by clearing interrupt */ + +} + +static void tfa98xx_tapdet_work(struct work_struct *work) +{ + struct tfa98xx *tfa98xx; + + //TODO check is this is still needed for tap polling + tfa98xx = container_of(work, struct tfa98xx, tapdet_work.work); + + if ( tfa_irq_get(tfa98xx->tfa, tfa9912_irq_sttapdet)) + tfa98xx_tapdet(tfa98xx); + + queue_delayed_work(tfa98xx->tfa98xx_wq, &tfa98xx->tapdet_work, HZ/10); +} + +static void tfa98xx_monitor(struct work_struct *work) +{ + struct tfa98xx *tfa98xx; + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + tfa98xx = container_of(work, struct tfa98xx, monitor_work.work); + + /* Check for tap-detection - bypass monitor if it is active */ + if (!tfa98xx->input) { + mutex_lock(&tfa98xx->dsp_lock); + error = tfa_status(tfa98xx->tfa); + mutex_unlock(&tfa98xx->dsp_lock); + if (error == Tfa98xx_Error_DSP_not_running) { + if (tfa98xx->dsp_init == TFA98XX_DSP_INIT_DONE) { + tfa98xx->dsp_init = TFA98XX_DSP_INIT_RECOVER; + queue_delayed_work(tfa98xx->tfa98xx_wq, &tfa98xx->init_work, 0); + } + } + } + + /* reschedule */ + queue_delayed_work(tfa98xx->tfa98xx_wq, &tfa98xx->monitor_work, 5*HZ); +} + +static void tfa98xx_dsp_init(struct tfa98xx *tfa98xx) +{ + int ret; + bool failed = false; + bool reschedule = false; + bool sync= false; + + if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK) { + pr_debug("Skipping tfa_dev_start (no FW: %d)\n", tfa98xx->dsp_fw_state); + return; + } + + if(tfa98xx->dsp_init == TFA98XX_DSP_INIT_DONE) { + pr_debug("Stream already started, skipping DSP power-on\n"); + return; + } + + mutex_lock(&tfa98xx_mutex); + mutex_lock(&tfa98xx->dsp_lock); + + tfa98xx->dsp_init = TFA98XX_DSP_INIT_PENDING; + + if (tfa98xx->init_count < TF98XX_MAX_DSP_START_TRY_COUNT) { + /* directly try to start DSP */ + ret = tfa98xx_tfa_start(tfa98xx, tfa98xx->profile, tfa98xx->vstep); + if (ret == Tfa98xx_Error_Not_Supported) { + tfa98xx->dsp_fw_state = TFA98XX_DSP_FW_FAIL; + dev_err(&tfa98xx->i2c->dev, "Failed starting device\n"); + failed = true; + } else if (ret != Tfa98xx_Error_Ok) { + /* It may fail as we may not have a valid clock at that + * time, so re-schedule and re-try later. + */ + dev_err(&tfa98xx->i2c->dev, + "tfa_dev_start failed! (err %d) - %d\n", + ret, tfa98xx->init_count); + reschedule = true; + } else { + if (tfa98xx_sync_count < tfa98xx_device_count) + tfa98xx_sync_count++; + sync = true; + + /* Subsystem ready, tfa init complete */ + tfa98xx->dsp_init = TFA98XX_DSP_INIT_DONE; + dev_dbg(&tfa98xx->i2c->dev, + "tfa_dev_start success (%d)\n", + tfa98xx->init_count); + /* cancel other pending init works */ + cancel_delayed_work(&tfa98xx->init_work); + tfa98xx->init_count = 0; + } + } else { + /* exceeded max number ot start tentatives, cancel start */ + dev_err(&tfa98xx->i2c->dev, + "Failed starting device (%d)\n", + tfa98xx->init_count); + failed = true; + } + if (reschedule) { + /* reschedule this init work for later */ + queue_delayed_work(tfa98xx->tfa98xx_wq, + &tfa98xx->init_work, + msecs_to_jiffies(5)); + tfa98xx->init_count++; + } + if (failed) { + tfa98xx->dsp_init = TFA98XX_DSP_INIT_FAIL; + /* cancel other pending init works */ + cancel_delayed_work(&tfa98xx->init_work); + tfa98xx->init_count = 0; + } + mutex_unlock(&tfa98xx->dsp_lock); + mutex_unlock(&tfa98xx_mutex); + + if (sync) { + /* check if all devices have started */ + bool do_sync; + mutex_lock(&tfa98xx_mutex); + do_sync = (tfa98xx_sync_count >= tfa98xx_device_count); + mutex_unlock(&tfa98xx_mutex); + + /* when all devices have started then unmute */ + if (do_sync) { + tfa98xx_sync_count = 0; + list_for_each_entry(tfa98xx, &tfa98xx_device_list, list) { + mutex_lock(&tfa98xx->dsp_lock); + tfa_dev_set_state(tfa98xx->tfa, TFA_STATE_UNMUTE); + + /* + * start monitor thread to check IC status bit + * periodically, and re-init IC to recover if + * needed. + */ + if (tfa98xx->tfa->tfa_family == 1) + queue_delayed_work(tfa98xx->tfa98xx_wq, + &tfa98xx->monitor_work, + 1*HZ); + mutex_unlock(&tfa98xx->dsp_lock); + } + + } + } + + + return; +} + + +static void tfa98xx_dsp_init_work(struct work_struct *work) +{ + struct tfa98xx *tfa98xx = container_of(work, struct tfa98xx, init_work.work); + + tfa98xx_dsp_init(tfa98xx); +} + +static void tfa98xx_interrupt(struct work_struct *work) +{ + struct tfa98xx *tfa98xx = container_of(work, struct tfa98xx, interrupt_work.work); + + pr_info("\n"); + + if (tfa98xx->flags & TFA98XX_FLAG_TAPDET_AVAILABLE) { + /* check for tap interrupt */ + if (tfa_irq_get(tfa98xx->tfa, tfa9912_irq_sttapdet)) { + tfa98xx_tapdet(tfa98xx); + + /* clear interrupt */ + tfa_irq_clear(tfa98xx->tfa, tfa9912_irq_sttapdet); + } + } /* TFA98XX_FLAG_TAPDET_AVAILABLE */ + + if (tfa98xx->flags & TFA98XX_FLAG_REMOVE_PLOP_NOISE) { + int start_triggered; + + mutex_lock(&tfa98xx->dsp_lock); + start_triggered = tfa_plop_noise_interrupt(tfa98xx->tfa, tfa98xx->profile, tfa98xx->vstep); + /* Only enable when the return value is 1, otherwise the interrupt is triggered twice */ + if(start_triggered) + tfa98xx_interrupt_enable(tfa98xx, true); + mutex_unlock(&tfa98xx->dsp_lock); + } /* TFA98XX_FLAG_REMOVE_PLOP_NOISE */ + + if (tfa98xx->flags & TFA98XX_FLAG_LP_MODES) { + tfa_lp_mode_interrupt(tfa98xx->tfa); + } /* TFA98XX_FLAG_LP_MODES */ + + /* unmask interrupts masked in IRQ handler */ + tfa_irq_unmask(tfa98xx->tfa); +} + +static int tfa98xx_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + unsigned int sr; + int len, prof, nprof, idx = 0; + char *basename; + u64 formats; + int err; + + /* + * Support CODEC to CODEC links, + * these are called with a NULL runtime pointer. + */ + if (!substream->runtime) + return 0; + + if (pcm_no_constraint != 0) + return 0; + + switch (pcm_sample_format) { + case 1: + formats = SNDRV_PCM_FMTBIT_S24_LE; + break; + case 2: + formats = SNDRV_PCM_FMTBIT_S32_LE; + break; + default: + formats = SNDRV_PCM_FMTBIT_S16_LE; + break; + } + + err = snd_pcm_hw_constraint_mask64(substream->runtime, + SNDRV_PCM_HW_PARAM_FORMAT, formats); + if (err < 0) + return err; + + if (no_start != 0) + return 0; + + if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK) { + dev_info(codec->dev, "Container file not loaded\n"); + return -EINVAL; + } + + basename = kzalloc(MAX_CONTROL_NAME, GFP_KERNEL); + if (!basename) + return -ENOMEM; + + /* copy profile name into basename until the . */ + get_profile_basename(basename, tfa_cont_profile_name(tfa98xx, tfa98xx->profile)); + len = strlen(basename); + + /* loop over all profiles and get the supported samples rate(s) from + * the profiles with the same basename + */ + nprof = tfa_cnt_get_dev_nprof(tfa98xx->tfa); + tfa98xx->rate_constraint.list = &tfa98xx->rate_constraint_list[0]; + tfa98xx->rate_constraint.count = 0; + for (prof = 0; prof < nprof; prof++) { + if (0 == strncmp(basename, tfa_cont_profile_name(tfa98xx, prof), len)) { + /* Check which sample rate is supported with current profile, + * and enforce this. + */ + sr = tfa98xx_get_profile_sr(tfa98xx->tfa, prof); + if (!sr) + dev_info(codec->dev, "Unable to identify supported sample rate\n"); + + if (tfa98xx->rate_constraint.count >= TFA98XX_NUM_RATES) { + dev_err(codec->dev, "too many sample rates\n"); + } else { + tfa98xx->rate_constraint_list[idx++] = sr; + tfa98xx->rate_constraint.count += 1; + } + } + } + + kfree(basename); + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &tfa98xx->rate_constraint); +} + +static int tfa98xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec_dai->codec); + + tfa98xx->sysclk = freq; + return 0; +} + +static int tfa98xx_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + pr_debug("\n"); + return 0; +} + +static int tfa98xx_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(dai->codec); + struct snd_soc_codec *codec = dai->codec; + + pr_debug("fmt=0x%x\n", fmt); + + /* Supported mode: regular I2S, slave, or PDM */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) { + dev_err(codec->dev, "Invalid Codec master mode\n"); + return -EINVAL; + } + break; + case SND_SOC_DAIFMT_PDM: + break; + default: + dev_err(codec->dev, "Unsupported DAI format %d\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } + + tfa98xx->audio_mode = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + + return 0; +} + +static int tfa98xx_get_fssel(unsigned int rate) +{ + int i; + for (i = 0; i < ARRAY_SIZE(rate_to_fssel); i++) { + if (rate_to_fssel[i].rate == rate) { + return rate_to_fssel[i].fssel; + } + } + return -EINVAL; +} + +static int tfa98xx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_codec *codec = dai->codec; + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + unsigned int rate; + int prof_idx; + + /* Supported */ + rate = params_rate(params); + pr_debug("Requested rate: %d, sample size: %d, physical size: %d\n", + rate, snd_pcm_format_width(params_format(params)), + snd_pcm_format_physical_width(params_format(params))); + + if (no_start != 0) + return 0; + + /* check if samplerate is supported for this mixer profile */ + prof_idx = get_profile_id_for_sr(tfa98xx_mixer_profile, rate); + if (prof_idx < 0) { + pr_err("tfa98xx: invalid sample rate %d.\n", rate); + return -EINVAL; + } + pr_debug("mixer profile:container profile = [%d:%d]\n", tfa98xx_mixer_profile, prof_idx); + + + /* update 'real' profile (container profile) */ + tfa98xx->profile = prof_idx; + + /* update to new rate */ + tfa98xx->rate = rate; + + return 0; +} + +#ifdef TFA9874_NONDSP_STEREO +static uint8_t bytes[3*3+1] = {0}; +#define MONO_I2C_ADDR (0x34) +#define IMPEDANCE_VALUE (4000) /*or 20000*/ + +enum Tfa98xx_Error tfa98xx_adsp_send_calib_values(struct tfa98xx *tfa98xx) +{ + int ret = 0; + struct tfa_device *tfa = tfa98xx->tfa; + int value = 0, nr, dsp_cal_value = 0; + + if (TFA_GET_BF(tfa, MTPEX) == 1 && tfa98xx->i2c->addr == MONO_I2C_ADDR) + { + value = tfa_dev_mtp_get(tfa, TFA_MTP_RE25); + dsp_cal_value = (value * 65536) / 1000; + + nr = 4; + /* We have to copy it for both channels. Even when mono! */ + bytes[nr++] = (uint8_t)((dsp_cal_value >> 16) & 0xff); + bytes[nr++] = (uint8_t)((dsp_cal_value >> 8) & 0xff); + bytes[nr++] = (uint8_t)(dsp_cal_value & 0xff); + + dev_err(&tfa98xx->i2c->dev, "%s: cal value 0x%x\n", __func__, dsp_cal_value); + + bytes[nr++] = bytes[4]; + bytes[nr++] = bytes[5]; + bytes[nr++] = bytes[6]; + /* RDC */ + if (value > IMPEDANCE_VALUE) + bytes[0] |= 0x11; + } + + if (bytes[0] == 0x11) + { + nr = 1; + bytes[nr++] = 0x00; + bytes[nr++] = 0x81; + bytes[nr++] = 0x05; + + dev_err(&tfa98xx->i2c->dev, "%s: send_tfa_cal_in_band \n", __func__); + + ret = send_tfa_cal_in_band(&bytes[1], sizeof(bytes) - 1); + + bytes[0] = 0; + } + + return ret; +} + +#endif + +static int tfa98xx_mute(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_codec *codec = dai->codec; + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + + dev_dbg(&tfa98xx->i2c->dev, "%s: state: %d\n", __func__, mute); +#ifdef TFA9874_NONDSP_STEREO + atomic_set(&g_bypass, 0); + atomic_set(&g_Tx_enable, 0); +#endif + if (no_start) { + pr_debug("no_start parameter set no tfa_dev_start or tfa_dev_stop, returning\n"); + return 0; + } + + if (mute) { + /* stop DSP only when both playback and capture streams + * are deactivated + */ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + tfa98xx->pstream = 0; + else + tfa98xx->cstream = 0; + if (tfa98xx->pstream != 0 || tfa98xx->cstream != 0) + return 0; + + mutex_lock(&tfa98xx_mutex); + tfa98xx_sync_count = 0; + mutex_unlock(&tfa98xx_mutex); + + cancel_delayed_work_sync(&tfa98xx->monitor_work); + + cancel_delayed_work_sync(&tfa98xx->init_work); + if (tfa98xx->dsp_fw_state != TFA98XX_DSP_FW_OK) + return 0; + mutex_lock(&tfa98xx->dsp_lock); + tfa_dev_stop(tfa98xx->tfa); + tfa98xx->dsp_init = TFA98XX_DSP_INIT_STOPPED; + mutex_unlock(&tfa98xx->dsp_lock); + } else { + if (stream == SNDRV_PCM_STREAM_PLAYBACK) +#ifdef TFA9874_NONDSP_STEREO + { + tfa98xx->pstream = 1; + + if(0 == tfa98xx->profile) { + tfa98xx_adsp_send_calib_values(tfa98xx); + } else { + pr_debug("current profile:%d\n",tfa98xx->profile); + } + /* Start DSP */ + if ((tfa98xx->flags & TFA98XX_FLAG_CHIP_SELECTED) && + (tfa98xx->dsp_init != TFA98XX_DSP_INIT_PENDING)) + queue_delayed_work(tfa98xx->tfa98xx_wq, &tfa98xx->init_work, 0); + } else + tfa98xx->cstream = 1; +#else + tfa98xx->pstream = 1; + else + tfa98xx->cstream = 1; + + switch(tfa98xx_controls); + + /* Start DSP */ + if (tfa98xx->dsp_init != TFA98XX_DSP_INIT_PENDING) + queue_delayed_work(tfa98xx->tfa98xx_wq, + &tfa98xx->init_work, 0); +#endif + } + + return 0; +} + +static const struct snd_soc_dai_ops tfa98xx_dai_ops = { + .startup = tfa98xx_startup, + .set_fmt = tfa98xx_set_fmt, + .set_sysclk = tfa98xx_set_dai_sysclk, + .set_tdm_slot = tfa98xx_set_tdm_slot, + .hw_params = tfa98xx_hw_params, + .mute_stream = tfa98xx_mute, +}; + +static struct snd_soc_dai_driver tfa98xx_dai[] = { + { + .name = "tfa98xx-aif", + .id = 1, + .playback = { + .stream_name = "AIF Playback", + .channels_min = 1, + .channels_max = 4, + .rates = TFA98XX_RATES, + .formats = TFA98XX_FORMATS, + }, + .capture = { + .stream_name = "AIF Capture", + .channels_min = 1, + .channels_max = 4, + .rates = TFA98XX_RATES, + .formats = TFA98XX_FORMATS, + }, + .ops = &tfa98xx_dai_ops, + .symmetric_rates = 1, +#if 0 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,14,0) + .symmetric_channels = 1, + .symmetric_samplebits = 1, +#endif +#endif + }, +}; + +static int tfa98xx_probe(struct snd_soc_codec *codec) +{ + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + int ret; + + pr_debug("\n"); + + /* setup work queue, will be used to initial DSP on first boot up */ + tfa98xx->tfa98xx_wq = create_singlethread_workqueue("tfa98xx"); + if (!tfa98xx->tfa98xx_wq) + return -ENOMEM; + + INIT_DELAYED_WORK(&tfa98xx->init_work, tfa98xx_dsp_init_work); + INIT_DELAYED_WORK(&tfa98xx->monitor_work, tfa98xx_monitor); + INIT_DELAYED_WORK(&tfa98xx->interrupt_work, tfa98xx_interrupt); + INIT_DELAYED_WORK(&tfa98xx->tapdet_work, tfa98xx_tapdet_work); + + tfa98xx->codec = codec; + + ret = tfa98xx_load_container(tfa98xx); + pr_debug("Container loading requested: %d\n", ret); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,16,0) + codec->control_data = tfa98xx->regmap; + ret = snd_soc_codec_set_cache_io(codec, 8, 16, SND_SOC_REGMAP); + if (ret != 0) { + dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); + return ret; + } +#endif + tfa98xx_add_widgets(tfa98xx); + + dev_info(codec->dev, "tfa98xx codec registered (%s)", + tfa98xx->fw.name); + + return ret; +} + +static int tfa98xx_remove(struct snd_soc_codec *codec) +{ + struct tfa98xx *tfa98xx = snd_soc_codec_get_drvdata(codec); + pr_debug("\n"); + + tfa98xx_interrupt_enable(tfa98xx, false); + + tfa98xx_inputdev_unregister(tfa98xx); + + cancel_delayed_work_sync(&tfa98xx->interrupt_work); + cancel_delayed_work_sync(&tfa98xx->monitor_work); + cancel_delayed_work_sync(&tfa98xx->init_work); + cancel_delayed_work_sync(&tfa98xx->tapdet_work); + + if (tfa98xx->tfa98xx_wq) + destroy_workqueue(tfa98xx->tfa98xx_wq); + + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0) +static struct regmap *tfa98xx_get_regmap(struct device *dev) +{ + struct tfa98xx *tfa98xx = dev_get_drvdata(dev); + + return tfa98xx->regmap; +} +#endif +static struct snd_soc_codec_driver soc_codec_dev_tfa98xx = { + .probe = tfa98xx_probe, + .remove = tfa98xx_remove, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,16,0) + .get_regmap = tfa98xx_get_regmap, +#endif +}; + + +static bool tfa98xx_writeable_register(struct device *dev, unsigned int reg) +{ + /* enable read access for all registers */ + return 1; +} + +static bool tfa98xx_readable_register(struct device *dev, unsigned int reg) +{ + /* enable read access for all registers */ + return 1; +} + +static bool tfa98xx_volatile_register(struct device *dev, unsigned int reg) +{ + /* enable read access for all registers */ + return 1; +} + +static const struct regmap_config tfa98xx_regmap = { + .reg_bits = 8, + .val_bits = 16, + + .max_register = TFA98XX_MAX_REGISTER, + .writeable_reg = tfa98xx_writeable_register, + .readable_reg = tfa98xx_readable_register, + .volatile_reg = tfa98xx_volatile_register, + .cache_type = REGCACHE_NONE, +}; + +static void tfa98xx_irq_tfa2(struct tfa98xx *tfa98xx) +{ + pr_info("\n"); + + /* + * mask interrupts + * will be unmasked after handling interrupts in workqueue + */ + tfa_irq_mask(tfa98xx->tfa); + queue_delayed_work(tfa98xx->tfa98xx_wq, &tfa98xx->interrupt_work, 0); +} + + +static irqreturn_t tfa98xx_irq(int irq, void *data) +{ + struct tfa98xx *tfa98xx = data; + + if (tfa98xx->tfa->tfa_family == 2) + tfa98xx_irq_tfa2(tfa98xx); + + return IRQ_HANDLED; +} + +static int tfa98xx_ext_reset(struct tfa98xx *tfa98xx) +{ + if (tfa98xx && gpio_is_valid(tfa98xx->reset_gpio)) { + gpio_set_value_cansleep(tfa98xx->reset_gpio, 1); + mdelay(1); + gpio_set_value_cansleep(tfa98xx->reset_gpio, 0); + mdelay(1); + } + return 0; +} + +static int tfa98xx_parse_dt(struct device *dev, struct tfa98xx *tfa98xx, + struct device_node *np) { + int ret = 0; + + tfa98xx->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (tfa98xx->reset_gpio < 0) + dev_dbg(dev, "No reset GPIO provided, will not HW reset device\n"); + + tfa98xx->irq_gpio = of_get_named_gpio(np, "irq-gpio", 0); + if (tfa98xx->irq_gpio < 0) + dev_dbg(dev, "No IRQ GPIO provided.\n"); + + ret = of_property_read_u32(np, "rpc-send-delay", &tfa98xx_rpc_send_delay); + if (ret < 0) { + dev_dbg(dev, "No rpc-send-delay provided. Set to default\n"); + tfa98xx_rpc_send_delay = 2; + } + + return 0; +} + +static ssize_t tfa98xx_reg_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct tfa98xx *tfa98xx = dev_get_drvdata(dev); + + if (count != 1) { + pr_debug("invalid register address"); + return -EINVAL; + } + + tfa98xx->reg = buf[0]; + + return 1; +} + +static ssize_t tfa98xx_rw_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct tfa98xx *tfa98xx = dev_get_drvdata(dev); + u8 *data; + int ret; + int retries = I2C_RETRIES; + + data = kmalloc(count+1, GFP_KERNEL); + if (data == NULL) { + pr_debug("can not allocate memory\n"); + return -ENOMEM; + } + + data[0] = tfa98xx->reg; + memcpy(&data[1], buf, count); + +retry: + ret = i2c_master_send(tfa98xx->i2c, data, count+1); + if (ret < 0) { + pr_warn("i2c error, retries left: %d\n", retries); + if (retries) { + retries--; + msleep(I2C_RETRY_DELAY); + goto retry; + } + } + + kfree(data); + + /* the number of data bytes written without the register address */ + return ((ret > 1) ? count : -EIO); +} + +static ssize_t tfa98xx_rw_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct tfa98xx *tfa98xx = dev_get_drvdata(dev); + struct i2c_msg msgs[] = { + { + .addr = tfa98xx->i2c->addr, + .flags = 0, + .len = 1, + .buf = &tfa98xx->reg, + }, + { + .addr = tfa98xx->i2c->addr, + .flags = I2C_M_RD, + .len = count, + .buf = buf, + }, + }; + int ret; + int retries = I2C_RETRIES; +retry: + ret = i2c_transfer(tfa98xx->i2c->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) { + pr_warn("i2c error, retries left: %d\n", retries); + if (retries) { + retries--; + msleep(I2C_RETRY_DELAY); + goto retry; + } + return ret; + } + /* ret contains the number of i2c transaction */ + /* return the number of bytes read */ + return ((ret > 1) ? count : -EIO); +} + +static struct bin_attribute dev_attr_rw = { + .attr = { + .name = "rw", + .mode = S_IRUSR | S_IWUSR, + }, + .size = 0, + .read = tfa98xx_rw_read, + .write = tfa98xx_rw_write, +}; + +static struct bin_attribute dev_attr_reg = { + .attr = { + .name = "reg", + .mode = S_IWUSR, + }, + .size = 0, + .read = NULL, + .write = tfa98xx_reg_write, +}; + +static int tfa98xx_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct snd_soc_dai_driver *dai; + struct tfa98xx *tfa98xx; + struct device_node *np = i2c->dev.of_node; + int irq_flags; + unsigned int reg; + int ret; + + pr_info("addr=0x%x\n", i2c->addr); + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + dev_err(&i2c->dev, "check_functionality failed\n"); + return -EIO; + } + + tfa98xx = devm_kzalloc(&i2c->dev, sizeof(struct tfa98xx), GFP_KERNEL); + if (tfa98xx == NULL) + return -ENOMEM; + + tfa98xx->dev = &i2c->dev; + tfa98xx->i2c = i2c; + tfa98xx->dsp_init = TFA98XX_DSP_INIT_STOPPED; + tfa98xx->rate = 48000; /* init to the default sample rate (48kHz) */ + tfa98xx->tfa = NULL; + + tfa98xx->regmap = devm_regmap_init_i2c(i2c, &tfa98xx_regmap); + if (IS_ERR(tfa98xx->regmap)) { + ret = PTR_ERR(tfa98xx->regmap); + dev_err(&i2c->dev, "Failed to allocate register map: %d\n", + ret); + return ret; + } + + i2c_set_clientdata(i2c, tfa98xx); + mutex_init(&tfa98xx->dsp_lock); + init_waitqueue_head(&tfa98xx->wq); + + if (np) { + ret = tfa98xx_parse_dt(&i2c->dev, tfa98xx, np); + if (ret) { + dev_err(&i2c->dev, "Failed to parse DT node\n"); + return ret; + } + if (no_start) + tfa98xx->irq_gpio = -1; + if (no_reset) + tfa98xx->reset_gpio = -1; + } else { + tfa98xx->reset_gpio = -1; + tfa98xx->irq_gpio = -1; + } + + if (gpio_is_valid(tfa98xx->reset_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, tfa98xx->reset_gpio, + GPIOF_OUT_INIT_LOW, "TFA98XX_RST"); + if (ret) + return ret; + } + + if (gpio_is_valid(tfa98xx->irq_gpio)) { + ret = devm_gpio_request_one(&i2c->dev, tfa98xx->irq_gpio, + GPIOF_DIR_IN, "TFA98XX_INT"); + if (ret) + return ret; + } + + /* Power up! */ + tfa98xx_ext_reset(tfa98xx); + + if ((no_start == 0) && (no_reset == 0)) { + ret = regmap_read(tfa98xx->regmap, 0x03, ®); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to read Revision register: %d\n", + ret); + return -EIO; + } + switch (reg & 0xff) { + case 0x72: /* tfa9872 */ + pr_info("TFA9872 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_MULTI_MIC_INPUTS; + tfa98xx->flags |= TFA98XX_FLAG_CALIBRATION_CTL; + tfa98xx->flags |= TFA98XX_FLAG_REMOVE_PLOP_NOISE; + /* tfa98xx->flags |= TFA98XX_FLAG_LP_MODES; */ + tfa98xx->flags |= TFA98XX_FLAG_TDM_DEVICE; + break; + case 0x74: /* tfa9874 */ + pr_info("TFA9874 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_MULTI_MIC_INPUTS; + tfa98xx->flags |= TFA98XX_FLAG_CALIBRATION_CTL; + tfa98xx->flags |= TFA98XX_FLAG_TDM_DEVICE; + break; + case 0x88: /* tfa9888 */ + pr_info("TFA9888 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_STEREO_DEVICE; + tfa98xx->flags |= TFA98XX_FLAG_MULTI_MIC_INPUTS; + tfa98xx->flags |= TFA98XX_FLAG_TDM_DEVICE; + break; + case 0x13: /* tfa9912 */ + pr_info("TFA9912 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_MULTI_MIC_INPUTS; + tfa98xx->flags |= TFA98XX_FLAG_TDM_DEVICE; + /* tfa98xx->flags |= TFA98XX_FLAG_TAPDET_AVAILABLE; */ + break; + case 0x94: /* tfa9894 */ + pr_info("TFA9894 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_MULTI_MIC_INPUTS; + tfa98xx->flags |= TFA98XX_FLAG_TDM_DEVICE; + break; + case 0x80: /* tfa9890 */ + case 0x81: /* tfa9890 */ + pr_info("TFA9890 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS; + break; + case 0x92: /* tfa9891 */ + pr_info("TFA9891 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_SAAM_AVAILABLE; + tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS; + break; + case 0x12: /* tfa9895 */ + pr_info("TFA9895 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS; + break; + case 0x97: + pr_info("TFA9897 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS; + tfa98xx->flags |= TFA98XX_FLAG_TDM_DEVICE; + break; + case 0x96: + pr_info("TFA9896 detected\n"); + tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS; + tfa98xx->flags |= TFA98XX_FLAG_TDM_DEVICE; + break; + default: + pr_info("Unsupported device revision (0x%x)\n", reg & 0xff); + return -EINVAL; + } + } + +#ifdef TFA9874_NONDSP_STEREO + tfa98xx->flags |= TFA98XX_FLAG_CHIP_SELECTED; +#endif + + tfa98xx->tfa = devm_kzalloc(&i2c->dev, sizeof(struct tfa_device), GFP_KERNEL); + if (tfa98xx->tfa == NULL) + return -ENOMEM; + + tfa98xx->tfa->data = (void *)tfa98xx; + tfa98xx->tfa->cachep = tfa98xx_cache; + + /* Modify the stream names, by appending the i2c device address. + * This is used with multicodec, in order to discriminate the devices. + * Stream names appear in the dai definition and in the stream . + * We create copies of original structures because each device will + * have its own instance of this structure, with its own address. + */ + dai = devm_kzalloc(&i2c->dev, sizeof(tfa98xx_dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + memcpy(dai, tfa98xx_dai, sizeof(tfa98xx_dai)); + + tfa98xx_append_i2c_address(&i2c->dev, + i2c, + NULL, + 0, + dai, + ARRAY_SIZE(tfa98xx_dai)); + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_tfa98xx, dai, + ARRAY_SIZE(tfa98xx_dai)); + + if (ret < 0) { + dev_err(&i2c->dev, "Failed to register TFA98xx: %d\n", ret); + return ret; + } + + if (gpio_is_valid(tfa98xx->irq_gpio) && + !(tfa98xx->flags & TFA98XX_FLAG_SKIP_INTERRUPTS)) { + /* register irq handler */ + irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; + ret = devm_request_threaded_irq(&i2c->dev, + gpio_to_irq(tfa98xx->irq_gpio), + NULL, tfa98xx_irq, irq_flags, + "tfa98xx", tfa98xx); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request IRQ %d: %d\n", + gpio_to_irq(tfa98xx->irq_gpio), ret); + return ret; + } + } else { + dev_info(&i2c->dev, "Skipping IRQ registration\n"); + /* disable feature support if gpio was invalid */ + tfa98xx->flags |= TFA98XX_FLAG_SKIP_INTERRUPTS; + } + +#ifdef CONFIG_DEBUG_FS + if (no_start == 0) + tfa98xx_debug_init(tfa98xx, i2c); +#endif + /* Register the sysfs files for climax backdoor access */ + ret = device_create_bin_file(&i2c->dev, &dev_attr_rw); + if (ret) + dev_info(&i2c->dev, "error creating sysfs files\n"); + ret = device_create_bin_file(&i2c->dev, &dev_attr_reg); + if (ret) + dev_info(&i2c->dev, "error creating sysfs files\n"); + + pr_info("%s Probe completed successfully!\n", __func__); + + INIT_LIST_HEAD(&tfa98xx->list); + + mutex_lock(&tfa98xx_mutex); + tfa98xx_device_count++; + list_add(&tfa98xx->list, &tfa98xx_device_list); + mutex_unlock(&tfa98xx_mutex); + + return 0; +} + +static int tfa98xx_i2c_remove(struct i2c_client *i2c) +{ + struct tfa98xx *tfa98xx = i2c_get_clientdata(i2c); + + pr_debug("addr=0x%x\n", i2c->addr); + + tfa98xx_interrupt_enable(tfa98xx, false); + + cancel_delayed_work_sync(&tfa98xx->interrupt_work); + cancel_delayed_work_sync(&tfa98xx->monitor_work); + cancel_delayed_work_sync(&tfa98xx->init_work); + cancel_delayed_work_sync(&tfa98xx->tapdet_work); + + device_remove_bin_file(&i2c->dev, &dev_attr_reg); + device_remove_bin_file(&i2c->dev, &dev_attr_rw); +#ifdef CONFIG_DEBUG_FS + tfa98xx_debug_remove(tfa98xx); +#endif + + snd_soc_unregister_codec(&i2c->dev); + + if (gpio_is_valid(tfa98xx->irq_gpio)) + devm_gpio_free(&i2c->dev, tfa98xx->irq_gpio); + if (gpio_is_valid(tfa98xx->reset_gpio)) + devm_gpio_free(&i2c->dev, tfa98xx->reset_gpio); + + mutex_lock(&tfa98xx_mutex); + list_del(&tfa98xx->list); + tfa98xx_device_count--; + if (tfa98xx_device_count == 0) { + kfree(tfa98xx_container); + tfa98xx_container = NULL; + } + mutex_unlock(&tfa98xx_mutex); + + return 0; +} + +static const struct i2c_device_id tfa98xx_i2c_id[] = { + { "tfa98xx", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tfa98xx_i2c_id); + +#ifdef CONFIG_OF +static struct of_device_id tfa98xx_dt_match[] = { + { .compatible = "nxp,tfa98xx" }, + { .compatible = "nxp,tfa9872" }, + { .compatible = "nxp,tfa9874" }, + { .compatible = "nxp,tfa9888" }, + { .compatible = "nxp,tfa9890" }, + { .compatible = "nxp,tfa9891" }, + { .compatible = "nxp,tfa9894" }, + { .compatible = "nxp,tfa9895" }, + { .compatible = "nxp,tfa9896" }, + { .compatible = "nxp,tfa9897" }, + { .compatible = "nxp,tfa9912" }, + { }, +}; +#endif + +static struct i2c_driver tfa98xx_i2c_driver = { + .driver = { + .name = "tfa98xx", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tfa98xx_dt_match), + }, + .probe = tfa98xx_i2c_probe, + .remove = tfa98xx_i2c_remove, + .id_table = tfa98xx_i2c_id, +}; + +static int __init tfa98xx_i2c_init(void) +{ + int ret = 0; + + pr_info("TFA98XX driver version %s\n", TFA98XX_VERSION); + + /* Enable debug traces */ + tfa98xx_kmsg_regs = trace_level & 2; + tfa98xx_ftrace_regs = trace_level & 4; + + /* Initialize kmem_cache */ + tfa98xx_cache = kmem_cache_create("tfa98xx_cache", /* Cache name /proc/slabinfo */ + PAGE_SIZE, /* Structure size, we should fit in single page */ + 0, /* Structure alignment */ + (SLAB_HWCACHE_ALIGN | SLAB_RECLAIM_ACCOUNT | + SLAB_MEM_SPREAD), /* Cache property */ + NULL); /* Object constructor */ + if (!tfa98xx_cache) { + pr_err("tfa98xx can't create memory pool\n"); + ret = -ENOMEM; + } + + ret = i2c_add_driver(&tfa98xx_i2c_driver); + + return ret; +} +module_init(tfa98xx_i2c_init); + +static void __exit tfa98xx_i2c_exit(void) +{ + i2c_del_driver(&tfa98xx_i2c_driver); + kmem_cache_destroy(tfa98xx_cache); +} +module_exit(tfa98xx_i2c_exit); + +MODULE_DESCRIPTION("ASoC TFA98XX driver"); +MODULE_LICENSE("GPL"); + diff --git a/sound/soc/codecs/tfa_container.c b/sound/soc/codecs/tfa_container.c new file mode 100644 index 000000000000..58e79b246163 --- /dev/null +++ b/sound/soc/codecs/tfa_container.c @@ -0,0 +1,2318 @@ +/* + * Copyright 2014-2017 NXP Semiconductors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dbgprint.h" +#include "tfa_container.h" +#include "tfa.h" +#include "tfa98xx_tfafieldnames.h" +#include "tfa_internal.h" + +/* defines */ +#define MODULE_BIQUADFILTERBANK 2 +#define BIQUAD_COEFF_SIZE 6 + +/* module globals */ +static uint8_t gslave_address = 0; /* This is used to SET the slave with the --slave option */ + +static int float_to_int(uint32_t x) +{ + unsigned e = (0x7F + 31) - ((*(unsigned *) &x & 0x7F800000) >> 23); + unsigned m = 0x80000000 | (*(unsigned *) &x << 8); + return -(int)((m >> e) & -(e < 32)); +} + +/* + * check the container file +*/ +enum tfa_error tfa_load_cnt(void *cnt, int length) +{ + nxpTfaContainer_t *cntbuf = (nxpTfaContainer_t *)cnt; + + if (length > TFA_MAX_CNT_LENGTH) { + pr_err("incorrect length\n"); + return tfa_error_container; + } + + if (HDR(cntbuf->id[0],cntbuf->id[1]) == 0) { + pr_err("header is 0\n"); + return tfa_error_container; + } + + if ( (HDR(cntbuf->id[0],cntbuf->id[1])) != paramsHdr ) { + pr_err("wrong header type: 0x%02x 0x%02x\n", cntbuf->id[0],cntbuf->id[1]); + return tfa_error_container; + } + + if (cntbuf->size == 0) { + pr_err("data size is 0\n"); + return tfa_error_container; + } + + /* check CRC */ + if ( tfaContCrcCheckContainer(cntbuf)) { + pr_err("CRC error\n"); + return tfa_error_container; + } + + /* check sub version level */ + if ((cntbuf->subversion[1] != NXPTFA_PM_SUBVERSION) && + (cntbuf->subversion[0] != '0')) { + pr_err("container sub-version not supported: %c%c\n", + cntbuf->subversion[0], cntbuf->subversion[1]); + return tfa_error_container; + } + + return tfa_error_ok; +} + +/* + * Dump the contents of the file header + */ +void tfaContShowHeader(nxpTfaHeader_t *hdr) { + char _id[2]; + + pr_debug("File header\n"); + + _id[1] = hdr->id >> 8; + _id[0] = hdr->id & 0xff; + pr_debug("\tid:%.2s version:%.2s subversion:%.2s\n", _id, + hdr->version, hdr->subversion); + pr_debug("\tsize:%d CRC:0x%08x \n", hdr->size, hdr->CRC); + pr_debug( "\tcustomer:%.8s application:%.8s type:%.8s\n", hdr->customer, + hdr->application, hdr->type); +} + +/* + * return device list dsc from index + */ +nxpTfaDeviceList_t *tfaContGetDevList(nxpTfaContainer_t *cont, int dev_idx) +{ + uint8_t *base = (uint8_t *) cont; + + if (cont == NULL) + return NULL; + + if ( (dev_idx < 0) || (dev_idx >= cont->ndev)) + return NULL; + + if (cont->index[dev_idx].type != dscDevice) + return NULL; + + base += cont->index[dev_idx].offset; + return (nxpTfaDeviceList_t *) base; +} + +/* + * get the Nth profile for the Nth device + */ +nxpTfaProfileList_t *tfaContGetDevProfList(nxpTfaContainer_t * cont, int devIdx, int profIdx) +{ + nxpTfaDeviceList_t *dev; + int idx, hit; + uint8_t *base = (uint8_t *) cont; + + dev = tfaContGetDevList(cont, devIdx); + if (dev) { + for (idx = 0, hit = 0; idx < dev->length; idx++) { + if (dev->list[idx].type == dscProfile) { + if (profIdx == hit++) + return (nxpTfaProfileList_t *) (dev->list[idx].offset+base); + } + } + } + + return NULL; +} + +/* + * get the number of profiles for the Nth device + */ +int tfa_cnt_get_dev_nprof(struct tfa_device *tfa) +{ + nxpTfaDeviceList_t *dev; + int idx, nprof = 0; + + if (tfa->cnt == NULL) + return 0; + + if ((tfa->dev_idx < 0) || (tfa->dev_idx >= tfa->cnt->ndev)) + return 0; + + dev = tfaContGetDevList(tfa->cnt, tfa->dev_idx); + if (dev) { + for (idx = 0; idx < dev->length; idx++) { + if (dev->list[idx].type == dscProfile) { + nprof++; + } + } + } + + return nprof; +} + +/* + * get the Nth lifedata for the Nth device + */ +nxpTfaLiveDataList_t *tfaContGetDevLiveDataList(nxpTfaContainer_t * cont, int devIdx, + int lifeDataIdx) +{ + nxpTfaDeviceList_t *dev; + int idx, hit; + uint8_t *base = (uint8_t *) cont; + + dev = tfaContGetDevList(cont, devIdx); + if (dev) { + for (idx = 0, hit = 0; idx < dev->length; idx++) { + if (dev->list[idx].type == dscLiveData) { + if (lifeDataIdx == hit++) + return (nxpTfaLiveDataList_t *) + (dev->list[idx].offset + base); + } + } + } + + return NULL; +} + +/* + * Get the max volume step associated with Nth profile for the Nth device + */ +int tfacont_get_max_vstep(struct tfa_device *tfa, int prof_idx) { + nxpTfaVolumeStep2File_t *vp; + struct nxpTfaVolumeStepMax2File *vp3; + int vstep_count = 0; + vp = (nxpTfaVolumeStep2File_t *) tfacont_getfiledata(tfa, prof_idx, volstepHdr); + if (vp == NULL) + return 0; + /* check the header type to load different NrOfVStep appropriately */ + if (tfa->tfa_family == 2) { + /* this is actually tfa2, so re-read the buffer*/ + vp3 = (struct nxpTfaVolumeStepMax2File *) + tfacont_getfiledata(tfa, prof_idx, volstepHdr); + if ( vp3 ) { + vstep_count = vp3->NrOfVsteps; + } + } else { + /* this is max1*/ + if ( vp ) { + vstep_count = vp->vsteps; + } + } + return vstep_count; +} + +/** + * Get the file contents associated with the device or profile + * Search within the device tree, if not found, search within the profile + * tree. There can only be one type of file within profile or device. + */ +nxpTfaFileDsc_t *tfacont_getfiledata(struct tfa_device *tfa, int prof_idx, enum nxpTfaHeaderType type) +{ + nxpTfaDeviceList_t *dev; + nxpTfaProfileList_t *prof; + nxpTfaFileDsc_t *file; + nxpTfaHeader_t *hdr; + unsigned int i; + + if (tfa->cnt == NULL) { + pr_err("invalid pointer to container file\n"); + return NULL; + } + + dev = tfaContGetDevList(tfa->cnt, tfa->dev_idx); + if (dev == NULL) { + pr_err("invalid pointer to container file device list\n"); + return NULL; + } + + /* process the device list until a file type is encountered */ + for (i=0;ilength;i++) { + if ( dev->list[i].type == dscFile ) { + file = (nxpTfaFileDsc_t *)(dev->list[i].offset+(uint8_t *)tfa->cnt); + if (file != NULL) { + hdr = (nxpTfaHeader_t *)file->data; + /* check for file type */ + if ( hdr->id == type) { + return (nxpTfaFileDsc_t *)&file->data; + } + } + } + } + + /* File not found in device tree. + * So, look in the profile list until the file type is encountered + */ + prof = tfaContGetDevProfList(tfa->cnt, tfa->dev_idx, prof_idx); + if (prof == NULL) { + pr_err("invalid pointer to container file profile list\n"); + return NULL; + } + + for (i=0;ilength;i++) { + if (prof->list[i].type == dscFile) { + file = (nxpTfaFileDsc_t *)(prof->list[i].offset+(uint8_t *)tfa->cnt); + if (file != NULL) { + hdr= (nxpTfaHeader_t *)file->data; + if (hdr != NULL) { + /* check for file type */ + if ( hdr->id == type) { + return (nxpTfaFileDsc_t *)&file->data; + } + } + } + } + } + + if (tfa->verbose) + pr_debug("%s: no file found of type %d\n", __FUNCTION__, type); + + return NULL; +} + +/* + * write a parameter file to the device + */ +static enum Tfa98xx_Error tfaContWriteVstep(struct tfa_device *tfa, nxpTfaVolumeStep2File_t *vp, int vstep) +{ + enum Tfa98xx_Error err; + unsigned short vol; + + if (vstep < vp->vsteps) { + /* vol = (unsigned short)(voldB / (-0.5f)); */ + vol = (unsigned short)(-2 * float_to_int(*((uint32_t *)&vp->vstep[vstep].attenuation))); + if (vol > 255) /* restricted to 8 bits */ + vol = 255; + + err = tfa98xx_set_volume_level(tfa, vol); + if (err != Tfa98xx_Error_Ok) + return err; + + err = tfa98xx_dsp_write_preset(tfa, sizeof(vp->vstep[0].preset), vp->vstep[vstep].preset); + if (err != Tfa98xx_Error_Ok) + return err; + err = tfa_cont_write_filterbank(tfa, vp->vstep[vstep].filter); + + } else { + pr_err("Incorrect volume given. The value vstep[%d] >= %d\n", vstep , vp->vsteps); + err = Tfa98xx_Error_Bad_Parameter; + } + + if (tfa->verbose) pr_debug("vstep[%d][%d]\n", tfa->dev_idx, vstep); + + return err; +} + +static struct nxpTfaVolumeStepMessageInfo * +tfaContGetmsgInfoFromReg(struct nxpTfaVolumeStepRegisterInfo *regInfo) +{ + char *p = (char*) regInfo; + p += sizeof(regInfo->NrOfRegisters) + (regInfo->NrOfRegisters * sizeof(uint32_t)); + return (struct nxpTfaVolumeStepMessageInfo*) p; +} + +static int +tfaContGetmsgLen(struct nxpTfaVolumeStepMessageInfo *msgInfo) +{ + return (msgInfo->MessageLength.b[0] << 16) + (msgInfo->MessageLength.b[1] << 8) + msgInfo->MessageLength.b[2]; +} + +static struct nxpTfaVolumeStepMessageInfo * +tfaContGetNextmsgInfo(struct nxpTfaVolumeStepMessageInfo *msgInfo) +{ + char *p = (char*) msgInfo; + int msgLen = tfaContGetmsgLen(msgInfo); + int type = msgInfo->MessageType; + + p += sizeof(msgInfo->MessageType) + sizeof(msgInfo->MessageLength); + if (type == 3) + p += msgLen; + else + p += msgLen * 3; + + return (struct nxpTfaVolumeStepMessageInfo*) p; +} + +static struct nxpTfaVolumeStepRegisterInfo* +tfaContGetNextRegFromEndInfo(struct nxpTfaVolumeStepMessageInfo *msgInfo) +{ + char *p = (char*) msgInfo; + p += sizeof(msgInfo->NrOfMessages); + return (struct nxpTfaVolumeStepRegisterInfo*) p; + +} + +static struct nxpTfaVolumeStepRegisterInfo* +tfaContGetRegForVstep(nxpTfaVolumeStepMax2File_t *vp, int idx) +{ + int i, j, nrMessage; + + struct nxpTfaVolumeStepRegisterInfo *regInfo + = (struct nxpTfaVolumeStepRegisterInfo*) vp->vstepsBin; + struct nxpTfaVolumeStepMessageInfo *msgInfo = NULL; + + for (i = 0; i < idx; i++) { + msgInfo = tfaContGetmsgInfoFromReg(regInfo); + nrMessage = msgInfo->NrOfMessages; + + for (j = 0; j < nrMessage; j++) { + msgInfo = tfaContGetNextmsgInfo(msgInfo); + } + regInfo = tfaContGetNextRegFromEndInfo(msgInfo); + } + + return regInfo; +} + +#pragma pack (push, 1) +struct tfa_partial_msg_block { + uint8_t offset; + uint16_t change; + uint8_t update[16][3]; +}; +#pragma pack (pop) + +static enum Tfa98xx_Error tfaContWriteVstepMax2_One(struct tfa_device *tfa, struct nxpTfaVolumeStepMessageInfo *new_msg, + struct nxpTfaVolumeStepMessageInfo *old_msg, int enable_partial_update) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int len = (tfaContGetmsgLen(new_msg) - 1) * 3; + char *buf = (char*)new_msg->ParameterData; + uint8_t *partial = NULL; + uint8_t cmdid[3]; + int use_partial_coeff = 0; + + if (enable_partial_update) { + if (new_msg->MessageType != old_msg->MessageType) { + pr_debug("Message type differ - Disable Partial Update\n"); + enable_partial_update = 0; + } else if (tfaContGetmsgLen(new_msg) != tfaContGetmsgLen(old_msg)) { + pr_debug("Message Length differ - Disable Partial Update\n"); + enable_partial_update = 0; + } + } + + if ((enable_partial_update) && (new_msg->MessageType == 1)) { + /* No patial updates for message type 1 (Coefficients) */ + enable_partial_update = 0; + if ((tfa->rev & 0xff) == 0x88) { + use_partial_coeff = 1; + } else if ((tfa->rev & 0xff) == 0x13) { + use_partial_coeff = 1; + } + } + + /* Change Message Len to the actual buffer len */ + memcpy(cmdid, new_msg->CmdId, sizeof(cmdid)); + + /* The algoparams and mbdrc msg id will be changed to the reset type when SBSL=0 + * if SBSL=1 the msg will remain unchanged. It's up to the tuning engineer to choose the 'without_reset' + * types inside the vstep. In other words: the reset msg is applied during SBSL==0 else it remains unchanged. + */ + if (tfa_needs_reset(tfa) == 1) { + if (new_msg->MessageType == 0) { + cmdid[2] = SB_PARAM_SET_ALGO_PARAMS; + if (tfa->verbose) + pr_debug("P-ID for SetAlgoParams modified!\n"); + } else if (new_msg->MessageType == 2) { + cmdid[2] = SB_PARAM_SET_MBDRC; + if (tfa->verbose) + pr_debug("P-ID for SetMBDrc modified!\n"); + } + } + + /* + * +sizeof(struct tfa_partial_msg_block) will allow to fit one + * additonnal partial block If the partial update goes over the len of + * a regular message ,we can safely write our block and check afterward + * that we are over the size of a usual update + */ + if (enable_partial_update) { + partial = kmem_cache_alloc(tfa->cachep, GFP_KERNEL); + if (!partial) + pr_debug("Partial update memory error - Disabling\n"); + } + + if (partial) { + uint8_t offset = 0, i = 0; + uint16_t *change; + uint8_t *n = new_msg->ParameterData; + uint8_t *o = old_msg->ParameterData; + uint8_t *p = partial; + uint8_t* trim = partial; + + /* set dspFiltersReset */ + *p++ = 0x02; + *p++ = 0x00; + *p++ = 0x00; + + while ((o < (old_msg->ParameterData + len)) && + (p < (partial + len - 3))) { + if ((offset == 0xff) || + (memcmp(n, o, 3 * sizeof(uint8_t)))) { + *p++ = offset; + change = (uint16_t*) p; + *change = 0; + p += 2; + + for (i = 0; + (i < 16) && (o < (old_msg->ParameterData + len)); + i++, n += 3, o += 3) { + if (memcmp(n, o, 3 * sizeof(uint8_t))) { + *change |= BIT(i); + memcpy(p, n, 3); + p += 3; + trim = p; + } + } + + offset = 0; + *change = cpu_to_be16(*change); + } else { + n += 3; + o += 3; + offset++; + } + } + + if (trim == partial) { + pr_debug("No Change in message - discarding %d bytes\n", len); + len = 0; + + } else if (trim < (partial + len - 3)) { + pr_debug("Using partial update: %d -> %d bytes\n", len , (int)(trim-partial+3)); + + /* Add the termination marker */ + memset(trim, 0x00, 3); + trim += 3; + + /* Signal This will be a partial update */ + cmdid[2] |= BIT(6); + buf = (char*) partial; + len = (int)(trim - partial); + } else { + pr_debug("Partial too big - use regular update\n"); + } + } + + if (use_partial_coeff) { + err = dsp_partial_coefficients(tfa, old_msg->ParameterData, new_msg->ParameterData); + } else if (len) { + uint8_t *buffer; + + if (tfa->verbose) + pr_debug("Command-ID used: 0x%02x%02x%02x \n", cmdid[0], cmdid[1], cmdid[2]); + + buffer = kmem_cache_alloc(tfa->cachep, GFP_KERNEL); + if (buffer == NULL) { + err = Tfa98xx_Error_Fail; + } else { + memcpy(&buffer[0], cmdid, 3); + memcpy(&buffer[3], buf, len); + err = dsp_msg(tfa, 3 + len, (char *)buffer); + kmem_cache_free(tfa->cachep, buffer); + } + } + + if (partial) + kmem_cache_free(tfa->cachep, partial); + + return err; +} + +static enum Tfa98xx_Error tfaContWriteVstepMax2(struct tfa_device *tfa, nxpTfaVolumeStepMax2File_t *vp, int vstep_idx, int vstep_msg_idx) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + struct nxpTfaVolumeStepRegisterInfo *regInfo = NULL; + struct nxpTfaVolumeStepMessageInfo *msgInfo = NULL, *p_msgInfo = NULL; + nxpTfaBitfield_t bitF; + int i, nrMessages, enp = tfa->partial_enable; + + if (vstep_idx >= vp->NrOfVsteps) { + pr_debug("Volumestep %d is not available \n", vstep_idx); + return Tfa98xx_Error_Bad_Parameter; + } + + if (tfa->p_regInfo == NULL) { + if (tfa->verbose) + pr_debug("Inital vstep write\n"); + enp = 0; + } + + regInfo = tfaContGetRegForVstep(vp, vstep_idx); + + msgInfo = tfaContGetmsgInfoFromReg(regInfo); + nrMessages = msgInfo->NrOfMessages; + + if (enp) { + p_msgInfo = tfaContGetmsgInfoFromReg(tfa->p_regInfo); + if (nrMessages != p_msgInfo->NrOfMessages) { + pr_debug("Message different - Disable partial update\n"); + enp = 0; + } + } + + for (i = 0; i < nrMessages; i++) { + /* Messagetype(3) is Smartstudio Info! Dont send this! */ + if(msgInfo->MessageType == 3) { + /* MessageLength is in bytes */ + msgInfo = tfaContGetNextmsgInfo(msgInfo); + if(enp) + p_msgInfo = tfaContGetNextmsgInfo(p_msgInfo); + continue; + } + + /* If no vstepMsgIndex is passed on, all message needs to be send */ + if ((vstep_msg_idx >= TFA_MAX_VSTEP_MSG_MARKER) || (vstep_msg_idx == i)) { + err = tfaContWriteVstepMax2_One(tfa, msgInfo, p_msgInfo, enp); + if (err != Tfa98xx_Error_Ok) { + /* + * Force a full update for the next write + * As the current status of the DSP is unknown + */ + tfa->p_regInfo = NULL; + return err; + } + } + + msgInfo = tfaContGetNextmsgInfo(msgInfo); + if(enp) + p_msgInfo = tfaContGetNextmsgInfo(p_msgInfo); + } + + tfa->p_regInfo = regInfo; + + for(i=0; iNrOfRegisters*2; i++) { + /* Byte swap the datasheetname */ + bitF.field = (uint16_t)(regInfo->registerInfo[i]>>8) | (regInfo->registerInfo[i]<<8); + i++; + bitF.value = (uint16_t)regInfo->registerInfo[i]>>8; + err = tfaRunWriteBitfield(tfa , bitF); + if (err != Tfa98xx_Error_Ok) + return err; + } + + /* Save the current vstep */ + tfa_dev_set_swvstep(tfa, (unsigned short)vstep_idx); + + return err; +} + +/* + * Write DRC message to the dsp + * If needed modify the cmd-id + */ + +enum Tfa98xx_Error tfaContWriteDrcFile(struct tfa_device *tfa, int size, uint8_t data[]) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + uint8_t *msg = NULL; + + msg = kmem_cache_alloc(tfa->cachep, GFP_KERNEL); + if (msg == NULL) + return Tfa98xx_Error_Fail; + memcpy(msg, data, size); + + if (TFA_GET_BF(tfa, SBSL) == 0) { + /* Only do this when not set already */ + if (msg[2] != SB_PARAM_SET_MBDRC) { + msg[2] = SB_PARAM_SET_MBDRC; + + if (tfa->verbose) { + pr_debug("P-ID for SetMBDrc modified!: "); + pr_debug("Command-ID used: 0x%02x%02x%02x \n", + msg[0], msg[1], msg[2]); + } + } + } + + /* Send cmdId + payload to dsp */ + err = dsp_msg(tfa, size, (const char *)msg); + + kmem_cache_free(tfa->cachep, msg); + + return err; +} + + +/* + * write a parameter file to the device + * The VstepIndex and VstepMsgIndex are only used to write a specific msg from the vstep file. + */ +enum Tfa98xx_Error tfaContWriteFile(struct tfa_device *tfa, nxpTfaFileDsc_t *file, int vstep_idx, int vstep_msg_idx) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaHeader_t *hdr = (nxpTfaHeader_t *)file->data; + nxpTfaHeaderType_t type; + int size; + + if (tfa->verbose) { + tfaContShowHeader(hdr); + } + + type = (nxpTfaHeaderType_t) hdr->id; + + switch (type) { + case msgHdr: /* generic DSP message */ + size = hdr->size - sizeof(nxpTfaMsgFile_t); + err = dsp_msg(tfa, size, (const char *)((nxpTfaMsgFile_t *)hdr)->data); + break; + case volstepHdr: + if (tfa->tfa_family == 2) { + err = tfaContWriteVstepMax2(tfa, (nxpTfaVolumeStepMax2File_t *)hdr, vstep_idx, vstep_msg_idx); + } else { + err = tfaContWriteVstep(tfa, (nxpTfaVolumeStep2File_t *)hdr, vstep_idx); + } + + /* If writing the vstep was succesfull, set new current vstep */ + if(err == Tfa98xx_Error_Ok) { + tfa->vstep = vstep_idx; + } + + break; + case speakerHdr: + if (tfa->tfa_family == 2) { + /* Remove header and xml_id */ + size = hdr->size - sizeof(struct nxpTfaSpkHeader) - sizeof(struct nxpTfaFWVer); + + err = dsp_msg(tfa, size, + (const char *)(((nxpTfaSpeakerFile_t *)hdr)->data + (sizeof(struct nxpTfaFWVer)))); + } else { + size = hdr->size - sizeof(nxpTfaSpeakerFile_t); + err = tfa98xx_dsp_write_speaker_parameters(tfa, size, + (const unsigned char *)((nxpTfaSpeakerFile_t *)hdr)->data); + } + break; + case presetHdr: + size = hdr->size - sizeof(nxpTfaPreset_t); + err = tfa98xx_dsp_write_preset(tfa, size, (const unsigned char *)((nxpTfaPreset_t *)hdr)->data); + break; + case equalizerHdr: + err = tfa_cont_write_filterbank(tfa, ((nxpTfaEqualizerFile_t *)hdr)->filter); + break; + case patchHdr: + size = hdr->size - sizeof(nxpTfaPatch_t ); // size is total length + err = tfa_dsp_patch(tfa, size, (const unsigned char *) ((nxpTfaPatch_t *)hdr)->data); + break; + case configHdr: + size = hdr->size - sizeof(nxpTfaConfig_t); + err = tfa98xx_dsp_write_config(tfa, size, (const unsigned char *)((nxpTfaConfig_t *)hdr)->data); + break; + case drcHdr: + if(hdr->version[0] == NXPTFA_DR3_VERSION) { + /* Size is total size - hdrsize(36) - xmlversion(3) */ + size = hdr->size - sizeof(nxpTfaDrc2_t); + err = tfaContWriteDrcFile(tfa, size, ((nxpTfaDrc2_t *)hdr)->data); + } else { + /* + * The DRC file is split as: + * 36 bytes for generic header (customer, application, and type) + * 127x3 (381) bytes first block contains the device and sample rate + * independent settings + * 127x3 (381) bytes block the device and sample rate specific values. + * The second block can always be recalculated from the first block, + * if vlsCal and the sample rate are known. + */ + //size = hdr->size - sizeof(nxpTfaDrc_t); + size = 381; /* fixed size for first block */ + + //+381 is done to only send the second part of the drc block + err = tfa98xx_dsp_write_drc(tfa, size, ((const unsigned char *)((nxpTfaDrc_t *)hdr)->data+381)); + } + break; + case infoHdr: + /* Ignore */ + break; + default: + pr_err("Header is of unknown type: 0x%x\n", type); + return Tfa98xx_Error_Bad_Parameter; + } + + return err; +} + +/** + * get the 1st of this dsc type this devicelist + */ +static nxpTfaDescPtr_t *tfa_cnt_get_dsc(nxpTfaContainer_t *cnt, nxpTfaDescriptorType_t type, int dev_idx) +{ + nxpTfaDeviceList_t *dev = tfaContDevice(cnt, dev_idx); + nxpTfaDescPtr_t *_this; + int i; + + if ( !dev ) { + return NULL; + } + /* process the list until a the type is encountered */ + for(i=0;ilength;i++) { + if ( dev->list[i].type == (uint32_t)type ) { + _this = (nxpTfaDescPtr_t *)(dev->list[i].offset+(uint8_t *)cnt); + return _this; + } + + } + + return NULL; +} + +/** + * get the device type from the patch in this devicelist + * - find the patch file for this devidx + * - return the devid from the patch or 0 if not found + */ +int tfa_cnt_get_devid(nxpTfaContainer_t *cnt, int dev_idx) +{ + nxpTfaPatch_t *patchfile; + nxpTfaDescPtr_t *patchdsc; + uint8_t *patchheader; + unsigned short devid, checkaddress; + int checkvalue; + + patchdsc = tfa_cnt_get_dsc(cnt, dscPatch, dev_idx); + if ( !patchdsc ) /* no patch for this device, assume non-i2c */ + return 0; + patchdsc += 2; /* first the filename dsc and filesize, so skip them */ + patchfile = (nxpTfaPatch_t *)patchdsc; + + patchheader = patchfile->data; + + checkaddress = (patchheader[1] << 8) + patchheader[2]; + checkvalue = + (patchheader[3] << 16) + (patchheader[4] << 8) + patchheader[5]; + + devid = patchheader[0]; + + if(checkaddress == 0xFFFF && checkvalue != 0xFFFFFF && checkvalue != 0) { + devid = patchheader[5]<<8 | patchheader[0]; /* full revid */ + } + + return devid; +} + +/** + * get the firmware version from the patch in this devicelist + */ +int tfa_cnt_get_patch_version(struct tfa_device *tfa) +{ + nxpTfaPatch_t *patchfile; + nxpTfaDescPtr_t *patchdsc; + uint8_t *data; + int size, version; + + if (tfa->cnt == NULL) + return -1; + + patchdsc = tfa_cnt_get_dsc(tfa->cnt, dscPatch, tfa->dev_idx); + patchdsc += 2; /* first the filename dsc and filesize, so skip them */ + patchfile = (nxpTfaPatch_t *)patchdsc; + + size = patchfile->hdr.size - sizeof(nxpTfaPatch_t); + data = patchfile->data; + + version = (data[size-3] << 16) + (data[size-2] << 8) + data[size-1]; + + return version; +} + + +/* + * get the slave for the device if it exists + */ +enum Tfa98xx_Error tfaContGetSlave(struct tfa_device *tfa, uint8_t *slave_addr) +{ + nxpTfaDeviceList_t *dev = NULL; + + /* Make sure the cnt file is loaded */ + if (tfa->cnt != NULL) { + dev = tfaContDevice(tfa->cnt, tfa->dev_idx); + } + + if (dev == NULL) { + /* Check if slave argument is used! */ + if(gslave_address == 0) { + return Tfa98xx_Error_Bad_Parameter; + } else { + *slave_addr = gslave_address; + return Tfa98xx_Error_Ok; + } + } + + *slave_addr = dev->dev; + return Tfa98xx_Error_Ok; +} + +/* If no container file is given, we can always have used the slave argument */ +void tfaContSetSlave(uint8_t slave_addr) +{ + gslave_address = slave_addr; +} + +/* + * lookup slave and return device index + */ +int tfa_cont_get_idx(struct tfa_device *tfa) +{ + nxpTfaDeviceList_t *dev = NULL; + int i; + + for (i=0; icnt->ndev; i++) { + dev = tfaContDevice(tfa->cnt, i); + if (dev->dev == tfa->slave_address) + break; + + } + if (i == tfa->cnt->ndev) + return -1; + + return i; +} + +/* + * write a bit field + */ +enum Tfa98xx_Error tfaRunWriteBitfield(struct tfa_device *tfa, nxpTfaBitfield_t bf) +{ + enum Tfa98xx_Error error; + uint16_t value; + union { + uint16_t field; + nxpTfaBfEnum_t Enum; + } bfUni; + + value=bf.value; + bfUni.field = bf.field; +#ifdef TFA_DEBUG + if (tfa->verbose) + pr_debug("bitfield: %s=0x%x (0x%x[%d..%d]=0x%x)\n", tfaContBfName(bfUni.field, tfa->rev), value, + bfUni.Enum.address, bfUni.Enum.pos, bfUni.Enum.pos+bfUni.Enum.len, value); +#endif + error = tfa_set_bf(tfa, bfUni.field, value); + + return error; +} + +/* + * read a bit field + */ +enum Tfa98xx_Error tfaRunReadBitfield(struct tfa_device *tfa, nxpTfaBitfield_t *bf) +{ + enum Tfa98xx_Error error; + union { + uint16_t field; + nxpTfaBfEnum_t Enum; + } bfUni; + uint16_t regvalue, msk; + + bfUni.field = bf->field; + + error = reg_read(tfa, (unsigned char)(bfUni.Enum.address), ®value); + if (error) return error; + + msk = ((1<<(bfUni.Enum.len+1))-1)<value = regvalue>>bfUni.Enum.pos; + + return error; +} + +/* + dsp mem direct write + */ +static enum Tfa98xx_Error tfaRunWriteDspMem(struct tfa_device *tfa, nxpTfaDspMem_t *cfmem) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int i; + + for(i=0;isize;i++) { + if (tfa->verbose) + pr_debug("dsp mem (%d): 0x%02x=0x%04x\n", cfmem->type, cfmem->address, cfmem->words[i]); + + error = mem_write(tfa, cfmem->address++, cfmem->words[i], cfmem->type); + if (error) return error; + } + + return error; +} + +/* + * write filter payload to DSP + * note that the data is in an aligned union for all filter variants + * the aa data is used but it's the same for all of them + */ +static enum Tfa98xx_Error tfaRunWriteFilter(struct tfa_device *tfa, nxpTfaContBiquad_t *bq) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + enum Tfa98xx_DMEM dmem; + uint16_t address; + uint8_t data[3*3+sizeof(bq->aa.bytes)]; + int i, channel=0, runs=1; + int8_t saved_index=bq->aa.index; /* This is used to set back the index */ + + /* Channel=1 is primary, Channel=2 is secondary*/ + if (bq->aa.index > 100) { + bq->aa.index -= 100; + channel = 2; + } else if (bq->aa.index > 50) { + bq->aa.index -= 50; + channel = 1; + } else if((tfa->rev & 0xff) == 0x88) { + runs=2; + } + + if (tfa->verbose) { + if(channel == 2) + pr_debug("filter[%d,S]", bq->aa.index); + else if(channel == 1) + pr_debug("filter[%d,P]", bq->aa.index); + else + pr_debug("filter[%d]", bq->aa.index); + } + + for(i=0; iaa.index, &address, channel); + if(dmem == Tfa98xx_DMEM_ERR) { + if (tfa->verbose) { + pr_debug("Warning: XFilter settings are applied via msg file (ini filter[x] format is skipped).\n"); + } + /* Dont exit with an error here, We could continue without problems */ + return Tfa98xx_Error_Ok; + } + + /* send a DSP memory message that targets the devices specific memory for the filter + * msg params: which_mem, start_offset, num_words + */ + memset(data, 0, 3*3); + data[2] = dmem; /* output[0] = which_mem */ + data[4] = address >> 8; /* output[1] = start_offset */ + data[5] = address & 0xff; + data[8] = sizeof(bq->aa.bytes)/3; /*output[2] = num_words */ + memcpy( &data[9], bq->aa.bytes, sizeof(bq->aa.bytes)); /* payload */ + + if(tfa->tfa_family == 2) { + error = tfa_dsp_cmd_id_write(tfa, MODULE_FRAMEWORK, FW_PAR_ID_SET_MEMORY, sizeof(data), data); + } else { + error = tfa_dsp_cmd_id_write(tfa, MODULE_FRAMEWORK, 4 /* param */ , sizeof(data), data); + } + } + +#ifdef TFA_DEBUG + if (tfa->verbose) { + if (bq->aa.index==13) { + pr_debug("=%d,%.0f,%.2f \n", + bq->in.type, bq->in.cutOffFreq, bq->in.leakage); + } else if(bq->aa.index >= 10 && bq->aa.index <= 12) { + pr_debug("=%d,%.0f,%.1f,%.1f \n", bq->aa.type, + bq->aa.cutOffFreq, bq->aa.rippleDb, bq->aa.rolloff); + } else { + pr_debug("= unsupported filter index \n"); + } + } +#endif + + /* Because we can load the same filters multiple times + * For example: When we switch profile we re-write in operating mode. + * We then need to remember the index (primary, secondary or both) + */ + bq->aa.index = saved_index; + + return error; +} + +/* + * write the register based on the input address, value and mask + * only the part that is masked will be updated + */ +static enum Tfa98xx_Error tfaRunWriteRegister(struct tfa_device *tfa, nxpTfaRegpatch_t *reg) +{ + enum Tfa98xx_Error error; + uint16_t value,newvalue; + + if (tfa->verbose) + pr_debug("register: 0x%02x=0x%04x (msk=0x%04x)\n", reg->address, reg->value, reg->mask); + + error = reg_read(tfa, reg->address, &value); + if (error) return error; + + value &= ~reg->mask; + newvalue = reg->value & reg->mask; + + value |= newvalue; + error = reg_write(tfa, reg->address, value); + + return error; + +} + +// write reg and bitfield items in the devicelist to the target +enum Tfa98xx_Error tfaContWriteRegsDev(struct tfa_device *tfa) +{ + nxpTfaDeviceList_t *dev = tfaContDevice(tfa->cnt, tfa->dev_idx); + nxpTfaBitfield_t *bitF; + int i; + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + if ( !dev ) { + return Tfa98xx_Error_Bad_Parameter; + } + + /* process the list until a patch, file of profile is encountered */ + for(i=0;ilength;i++) { + if ( dev->list[i].type == dscPatch || + dev->list[i].type ==dscFile || + dev->list[i].type ==dscProfile ) break; + + if ( dev->list[i].type == dscBitfield) { + bitF = (nxpTfaBitfield_t *)( dev->list[i].offset+(uint8_t *)tfa->cnt); + err = tfaRunWriteBitfield(tfa , *bitF); + } + if ( dev->list[i].type == dscRegister ) { + err = tfaRunWriteRegister(tfa, (nxpTfaRegpatch_t *)( dev->list[i].offset+(char*)tfa->cnt)); + } + + if ( err ) break; + } + + return err; +} + +// write reg and bitfield items in the profilelist the target +enum Tfa98xx_Error tfaContWriteRegsProf(struct tfa_device *tfa, int prof_idx) +{ + nxpTfaProfileList_t *prof = tfaContGetDevProfList(tfa->cnt, tfa->dev_idx, prof_idx); + nxpTfaBitfield_t *bitf; + unsigned int i; + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + if ( !prof ) { + return Tfa98xx_Error_Bad_Parameter; + } + + if (tfa->verbose) + pr_debug("----- profile: %s (%d) -----\n", tfaContGetString(tfa->cnt, &prof->name), prof_idx); + + /* process the list until the end of the profile or the default section */ + for(i=0;ilength;i++) { + /* We only want to write the values before the default section when we switch profile */ + if(prof->list[i].type == dscDefault) + break; + + if ( prof->list[i].type == dscBitfield) { + bitf = (nxpTfaBitfield_t *)( prof->list[i].offset+(uint8_t *)tfa->cnt); + err = tfaRunWriteBitfield(tfa , *bitf); + } + if ( prof->list[i].type == dscRegister ) { + err = tfaRunWriteRegister(tfa, (nxpTfaRegpatch_t *)( prof->list[i].offset+(char*)tfa->cnt)); + } + if ( err ) break; + } + return err; +} + +// write patchfile in the devicelist to the target +enum Tfa98xx_Error tfaContWritePatch(struct tfa_device *tfa) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaDeviceList_t *dev = tfaContDevice(tfa->cnt, tfa->dev_idx); + nxpTfaFileDsc_t *file; + nxpTfaPatch_t *patchfile; + int size, i; + + if ( !dev ) { + return Tfa98xx_Error_Bad_Parameter; + } + /* process the list until a patch is encountered */ + for(i=0;ilength;i++) { + if ( dev->list[i].type == dscPatch ) { + file = (nxpTfaFileDsc_t *)(dev->list[i].offset+(uint8_t *)tfa->cnt); + patchfile =(nxpTfaPatch_t *)&file->data; + if (tfa->verbose) tfaContShowHeader(&patchfile->hdr); + size = patchfile->hdr.size - sizeof(nxpTfaPatch_t ); // size is total length + err = tfa_dsp_patch(tfa, size, (const unsigned char *) patchfile->data); + if ( err ) return err; + } + } + + return Tfa98xx_Error_Ok; +} + +/** + * Create a buffer which can be used to send to the dsp. + */ +static void create_dsp_buffer_msg(struct tfa_device *tfa, nxpTfaMsg_t *msg, char *buffer, int *size) +{ + int i, nr = 0; + + (void)tfa; + + /* Copy cmdId. Remember that the cmdId is reversed */ + buffer[nr++] = msg->cmdId[2]; + buffer[nr++] = msg->cmdId[1]; + buffer[nr++] = msg->cmdId[0]; + + /* Copy the data to the buffer */ + for(i=0; imsg_size; i++) { + buffer[nr++] = (uint8_t) ((msg->data[i] >> 16) & 0xffff); + buffer[nr++] = (uint8_t) ((msg->data[i] >> 8) & 0xff); + buffer[nr++] = (uint8_t) (msg->data[i] & 0xff); + } + + *size = nr; +} + +// write all param files in the devicelist to the target +enum Tfa98xx_Error tfaContWriteFiles(struct tfa_device *tfa) +{ + nxpTfaDeviceList_t *dev = tfaContDevice(tfa->cnt, tfa->dev_idx); + nxpTfaFileDsc_t *file; + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + char buffer[(MEMTRACK_MAX_WORDS * 3) + 3] = {0}; //every word requires 3 and 3 is the msg + int i, size = 0; + + if ( !dev ) { + return Tfa98xx_Error_Bad_Parameter; + } + /* process the list and write all files */ + for(i=0;ilength;i++) { + if ( dev->list[i].type == dscFile ) { + file = (nxpTfaFileDsc_t *)(dev->list[i].offset+(uint8_t *)tfa->cnt); + if ( tfaContWriteFile(tfa, file, 0 , TFA_MAX_VSTEP_MSG_MARKER) ){ + return Tfa98xx_Error_Bad_Parameter; + } + } + + if ( dev->list[i].type == dscSetInputSelect || + dev->list[i].type == dscSetOutputSelect || + dev->list[i].type == dscSetProgramConfig || + dev->list[i].type == dscSetLagW || + dev->list[i].type == dscSetGains || + dev->list[i].type == dscSetvBatFactors || + dev->list[i].type == dscSetSensesCal || + dev->list[i].type == dscSetSensesDelay || + dev->list[i].type == dscSetMBDrc || + dev->list[i].type == dscSetFwkUseCase || + dev->list[i].type == dscSetVddpConfig ) { + create_dsp_buffer_msg(tfa, (nxpTfaMsg_t *) + ( dev->list[i].offset+(char*)tfa->cnt), buffer, &size); + if (tfa->verbose) { + pr_debug("command: %s=0x%02x%02x%02x \n", + tfaContGetCommandString(dev->list[i].type), + (unsigned char)buffer[0], (unsigned char)buffer[1], (unsigned char)buffer[2]); + } + + err = dsp_msg(tfa, size, buffer); + } + + if ( dev->list[i].type == dscCmd ) { + size = *(uint16_t *)(dev->list[i].offset+(char*)tfa->cnt); + + err = dsp_msg(tfa, size, dev->list[i].offset+2+(char*)tfa->cnt); + if (tfa->verbose) { + const char *cmd_id = dev->list[i].offset+2+(char*)tfa->cnt; + pr_debug("Writing cmd=0x%02x%02x%02x \n", (uint8_t)cmd_id[0], (uint8_t)cmd_id[1], (uint8_t)cmd_id[2]); + } + } + if (err != Tfa98xx_Error_Ok) + break; + + if ( dev->list[i].type == dscCfMem ) { + err = tfaRunWriteDspMem(tfa, (nxpTfaDspMem_t *)(dev->list[i].offset+(uint8_t *)tfa->cnt)); + } + + if (err != Tfa98xx_Error_Ok) + break; + } + + return err; +} + +/* + * write all param files in the profilelist to the target + * this is used during startup when maybe ACS is set + */ +enum Tfa98xx_Error tfaContWriteFilesProf(struct tfa_device *tfa, int prof_idx, int vstep_idx) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaProfileList_t *prof = tfaContGetDevProfList(tfa->cnt, tfa->dev_idx, prof_idx); + char buffer[(MEMTRACK_MAX_WORDS * 3) + 3] = {0}; //every word requires 3 and 3 is the msg + unsigned int i; + nxpTfaFileDsc_t *file; + nxpTfaPatch_t *patchfile; + int size; + + if ( !prof ) { + return Tfa98xx_Error_Bad_Parameter; + } + + /* process the list and write all files */ + for(i=0;ilength;i++) { + switch (prof->list[i].type) { + case dscFile: + file = (nxpTfaFileDsc_t *)(prof->list[i].offset+(uint8_t *)tfa->cnt); + err = tfaContWriteFile(tfa, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER); + break; + case dscPatch: + file = (nxpTfaFileDsc_t *)(prof->list[i].offset+(uint8_t *)tfa->cnt); + patchfile =(nxpTfaPatch_t *)&file->data; + if (tfa->verbose) tfaContShowHeader(&patchfile->hdr); + size = patchfile->hdr.size - sizeof(nxpTfaPatch_t ); // size is total length + err = tfa_dsp_patch(tfa, size, (const unsigned char *) patchfile->data); + break; + case dscCfMem: + err = tfaRunWriteDspMem(tfa, (nxpTfaDspMem_t *)(prof->list[i].offset+(uint8_t *)tfa->cnt)); + break; + case dscSetInputSelect: + case dscSetOutputSelect: + case dscSetProgramConfig: + case dscSetLagW: + case dscSetGains: + case dscSetvBatFactors: + case dscSetSensesCal: + case dscSetSensesDelay: + case dscSetMBDrc: + case dscSetFwkUseCase: + case dscSetVddpConfig: + create_dsp_buffer_msg(tfa, (nxpTfaMsg_t *) + (prof->list[i].offset+(uint8_t *)tfa->cnt), buffer, &size); + if (tfa->verbose) { + pr_debug("command: %s=0x%02x%02x%02x \n", + tfaContGetCommandString(prof->list[i].type), + (unsigned char)buffer[0], (unsigned char)buffer[1], (unsigned char)buffer[2]); + } + + err = dsp_msg(tfa, size, buffer); + break; + default: + /* ignore any other type */ + break; + } + } + + return err; +} + +static enum Tfa98xx_Error tfaContWriteItem(struct tfa_device *tfa, nxpTfaDescPtr_t * dsc) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaRegpatch_t *reg; + nxpTfaMode_t *cas; + nxpTfaBitfield_t *bitf; + + // When no DSP should only write to HW registers. + if(tfa->ext_dsp == 0 && !(dsc->type == dscBitfield || dsc->type == dscRegister)) { + return Tfa98xx_Error_Ok; + } + + switch (dsc->type) { + case dscDefault: + case dscDevice: // ignore + case dscProfile: // profile list + break; + case dscRegister: // register patch + reg = (nxpTfaRegpatch_t *)(dsc->offset+(uint8_t *)tfa->cnt); + return tfaRunWriteRegister(tfa, reg); + //pr_debug("$0x%2x=0x%02x,0x%02x\n", reg->address, reg->mask, reg->value); + break; + case dscString: // ascii: zero terminated string + pr_debug(";string: %s\n", tfaContGetString(tfa->cnt, dsc)); + break; + case dscFile: // filename + file contents + case dscPatch: + break; + case dscMode: + cas = (nxpTfaMode_t *)(dsc->offset+(uint8_t *)tfa->cnt); + if(cas->value == Tfa98xx_Mode_RCV) + tfa98xx_select_mode(tfa, Tfa98xx_Mode_RCV); + else + tfa98xx_select_mode(tfa, Tfa98xx_Mode_Normal); + break; + case dscCfMem: + err = tfaRunWriteDspMem(tfa, (nxpTfaDspMem_t *)(dsc->offset+(uint8_t *)tfa->cnt)); + break; + case dscBitfield: + bitf = (nxpTfaBitfield_t *)(dsc->offset+(uint8_t *)tfa->cnt); + return tfaRunWriteBitfield(tfa , *bitf); + break; + case dscFilter: + return tfaRunWriteFilter(tfa, (nxpTfaContBiquad_t *)(dsc->offset+(uint8_t *)tfa->cnt)); + break; + } + + return err; +} + +static unsigned int tfa98xx_sr_from_field(unsigned int field) +{ + switch (field) { + case 0: + return 8000; + case 1: + return 11025; + case 2: + return 12000; + case 3: + return 16000; + case 4: + return 22050; + case 5: + return 24000; + case 6: + return 32000; + case 7: + return 44100; + case 8: + return 48000; + default: + return 0; + } +} + +enum Tfa98xx_Error tfa_write_filters(struct tfa_device *tfa, int prof_idx) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaProfileList_t *prof = tfaContGetDevProfList(tfa->cnt, tfa->dev_idx, prof_idx); + unsigned int i; + int status; + + if ( !prof ) { + return Tfa98xx_Error_Bad_Parameter; + } + + if (tfa->verbose) { + pr_debug("----- profile: %s (%d) -----\n", tfaContGetString(tfa->cnt, &prof->name), prof_idx); + pr_debug("Waiting for CLKS... \n"); + } + + for(i=10; i>0; i--) { + err = tfa98xx_dsp_system_stable(tfa, &status); + if(status) + break; + else + msleep_interruptible(10); + } + + if(i==0) { + if (tfa->verbose) + pr_err("Unable to write filters, CLKS=0 \n"); + + return Tfa98xx_Error_StateTimedOut; + } + + /* process the list until the end of the profile or the default section */ + for(i=0;ilength;i++) { + if ( prof->list[i].type == dscFilter ) { + if (tfaContWriteItem(tfa, &prof->list[i]) != Tfa98xx_Error_Ok) + return Tfa98xx_Error_Bad_Parameter; + } + } + + return err; +} + +unsigned int tfa98xx_get_profile_sr(struct tfa_device *tfa, unsigned int prof_idx) +{ + nxpTfaBitfield_t *bitf; + unsigned int i; + nxpTfaDeviceList_t *dev; + nxpTfaProfileList_t *prof; + int fs_profile = -1; + + dev = tfaContDevice(tfa->cnt, tfa->dev_idx); + if (!dev) + return 0; + + prof = tfaContGetDevProfList(tfa->cnt, tfa->dev_idx, prof_idx); + if (!prof) + return 0; + + /* Check profile fields first */ + for(i = 0; i < prof->length; i++) { + if(prof->list[i].type == dscDefault) + break; + + /* check for profile settingd (AUDFS) */ + if (prof->list[i].type == dscBitfield) { + bitf = (nxpTfaBitfield_t *)(prof->list[i].offset+(uint8_t *)tfa->cnt); + if (bitf->field == TFA_FAM(tfa, AUDFS)) { + fs_profile = bitf->value; + break; + } + } + } + + if (tfa->verbose) + pr_debug("%s - profile fs: 0x%x = %dHz (%d - %d)\n", + __FUNCTION__, fs_profile, + tfa98xx_sr_from_field(fs_profile), + tfa->dev_idx, prof_idx); + + if (fs_profile != -1) + return tfa98xx_sr_from_field(fs_profile); + + /* Check for container default setting */ + /* process the list until a patch, file of profile is encountered */ + for(i = 0; i < dev->length; i++) { + if (dev->list[i].type == dscPatch || + dev->list[i].type ==dscFile || + dev->list[i].type ==dscProfile) + break; + + if (dev->list[i].type == dscBitfield) { + bitf = (nxpTfaBitfield_t *)( dev->list[i].offset+(uint8_t *)tfa->cnt); + if (bitf->field == TFA_FAM(tfa, AUDFS)) { + fs_profile = bitf->value; + break; + } + } + /* Ignore register case */ + } + + if (tfa->verbose) + pr_debug("%s - default fs: 0x%x = %dHz (%d - %d)\n", + __FUNCTION__, fs_profile, + tfa98xx_sr_from_field(fs_profile), + tfa->dev_idx, prof_idx); + + if (fs_profile != -1) + return tfa98xx_sr_from_field(fs_profile); + + return 48000; /* default of HW */ +} + +static enum Tfa98xx_Error get_sample_rate_info(struct tfa_device *tfa, nxpTfaProfileList_t *prof, nxpTfaProfileList_t *previous_prof, int fs_previous_profile) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaBitfield_t *bitf; + unsigned int i; + int fs_default_profile=8; /* default is 48kHz */ + int fs_next_profile=8; /* default is 48kHz */ + + + /* ---------- default settings previous profile ---------- */ + for(i=0;ilength;i++) { + /* Search for the default section */ + if(i == 0) { + while(previous_prof->list[i].type != dscDefault && i < previous_prof->length) { + i++; + } + i++; + } + + /* Only if we found the default section search for AUDFS */ + if(i < previous_prof->length) { + if ( previous_prof->list[i].type == dscBitfield ) { + bitf = (nxpTfaBitfield_t *)(previous_prof->list[i].offset+(uint8_t *)tfa->cnt); + if(bitf->field == TFA_FAM(tfa, AUDFS)) { + fs_default_profile = bitf->value; + break; + } + } + } + } + + /* ---------- settings next profile ---------- */ + for(i=0;ilength;i++) { + /* We only want to write the values before the default section */ + if(prof->list[i].type == dscDefault) + break; + /* search for AUDFS */ + if ( prof->list[i].type == dscBitfield ) { + bitf = (nxpTfaBitfield_t *)(prof->list[i].offset+(uint8_t *)tfa->cnt); + if(bitf->field == TFA_FAM(tfa, AUDFS)) { + fs_next_profile = bitf->value; + break; + } + } + } + + /* Enable if needed for debugging! + if (tfa->verbose) { + pr_debug("sample rate from the previous profile: %d \n", fs_previous_profile); + pr_debug("sample rate in the default section: %d \n", fs_default_profile); + pr_debug("sample rate for the next profile: %d \n", fs_next_profile); + } + */ + + if(fs_next_profile != fs_default_profile) { + if (tfa->verbose) + pr_debug("Writing delay tables for AUDFS=%d \n", fs_next_profile); + + /* If the AUDFS from the next profile is not the same as + * the AUDFS from the default we need to write new delay tables + */ + err = tfa98xx_dsp_write_tables(tfa, fs_next_profile); + } else if(fs_default_profile != fs_previous_profile) { + if (tfa->verbose) + pr_debug("Writing delay tables for AUDFS=%d \n", fs_default_profile); + + /* But if we do not have a new AUDFS in the next profile and + * the AUDFS from the default profile is not the same as the AUDFS + * from the previous profile we also need to write new delay tables + */ + err = tfa98xx_dsp_write_tables(tfa, fs_default_profile); + } + + return err; +} + +/* + * process all items in the profilelist + * NOTE an error return during processing will leave the device muted + * + */ +enum Tfa98xx_Error tfaContWriteProfile(struct tfa_device *tfa, int prof_idx, int vstep_idx) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaProfileList_t *prof = tfaContGetDevProfList(tfa->cnt, tfa->dev_idx, prof_idx); + nxpTfaProfileList_t *previous_prof = tfaContGetDevProfList(tfa->cnt, tfa->dev_idx, tfa_dev_get_swprof(tfa)); + char buffer[(MEMTRACK_MAX_WORDS * 4) + 4] = {0}; //every word requires 3 or 4 bytes, and 3 or 4 is the msg + unsigned int i, k=0, j=0, tries=0; + nxpTfaFileDsc_t *file; + int size = 0, ready, fs_previous_profile = 8; /* default fs is 48kHz*/ + + if ( !prof || !previous_prof ) { + pr_err("Error trying to get the (previous) swprofile \n"); + return Tfa98xx_Error_Bad_Parameter; + } + + if (tfa->verbose) { + tfa98xx_trace_printk("device:%s profile:%s vstep:%d\n", tfaContDeviceName(tfa->cnt, tfa->dev_idx), + tfaContProfileName(tfa->cnt, tfa->dev_idx,prof_idx),vstep_idx); + } + + /* We only make a power cycle when the profiles are not in the same group */ + if (prof->group == previous_prof->group && prof->group != 0) { + if (tfa->verbose) { + pr_debug("The new profile (%s) is in the same group as the current profile (%s) \n", + tfaContGetString(tfa->cnt, &prof->name), tfaContGetString(tfa->cnt, &previous_prof->name)); + } + } else { + /* mute */ + err = tfaRunMute(tfa); + if (err) return err; + + /* Get current sample rate before we start switching */ + fs_previous_profile = TFA_GET_BF(tfa, AUDFS); + + /* clear SBSL to make sure we stay in initCF state */ + if (tfa->tfa_family == 2) { + TFA_SET_BF_VOLATILE(tfa, SBSL, 0); + } + + /* When we switch profile we first power down the subsystem + * This should only be done when we are in operating mode + */ + if (((tfa->tfa_family == 2) && (TFA_GET_BF(tfa, MANSTATE) >= 6)) || (tfa->tfa_family != 2)) { + err = tfa98xx_powerdown(tfa, 1); + if (err) return err; + + /* Wait until we are in PLL powerdown */ + do { + err = tfa98xx_dsp_system_stable(tfa, &ready); + if (!ready) + break; + else + msleep_interruptible(10); /* wait 10ms to avoid busload */ + tries++; + } while (tries <= 100); + + if (tries > 100) { + pr_debug("Wait for PLL powerdown timed out!\n"); + return Tfa98xx_Error_StateTimedOut; + } + } else { + pr_debug("No need to go to powerdown now \n"); + } + } + + /* set all bitfield settings */ + /* First set all default settings */ + if (tfa->verbose) { + pr_debug("---------- default settings profile: %s (%d) ---------- \n", + tfaContGetString(tfa->cnt, &previous_prof->name), tfa_dev_get_swprof(tfa)); + } + + err = show_current_state(tfa); + + /* Loop profile length */ + for(i=0;ilength;i++) { + /* Search for the default section */ + if(i == 0) { + while(previous_prof->list[i].type != dscDefault && i < previous_prof->length) { + i++; + } + i++; + } + + /* Only if we found the default section try writing the items */ + if(i < previous_prof->length) { + if ( tfaContWriteItem(tfa, &previous_prof->list[i]) != Tfa98xx_Error_Ok ) + return Tfa98xx_Error_Bad_Parameter; + } + } + + if (tfa->verbose) + pr_debug("---------- new settings profile: %s (%d) ---------- \n", + tfaContGetString(tfa->cnt, &prof->name), prof_idx); + + /* set new settings */ + for(i=0;ilength;i++) { + /* Remember where we currently are with writing items*/ + j = i; + + /* We only want to write the values before the default section when we switch profile */ + /* process and write all non-file items */ + switch (prof->list[i].type) { + case dscFile: + case dscPatch: + case dscSetInputSelect: + case dscSetOutputSelect: + case dscSetProgramConfig: + case dscSetLagW: + case dscSetGains: + case dscSetvBatFactors: + case dscSetSensesCal: + case dscSetSensesDelay: + case dscSetMBDrc: + case dscSetFwkUseCase: + case dscSetVddpConfig: + case dscCmd: + case dscFilter: + case dscDefault: + /* When one of these files are found, we exit */ + i = prof->length; + break; + default: + err = tfaContWriteItem(tfa, &prof->list[i]); + if ( err != Tfa98xx_Error_Ok ) + return Tfa98xx_Error_Bad_Parameter; + break; + } + } + + if (prof->group != previous_prof->group || prof->group == 0) { + if (tfa->tfa_family == 2) + TFA_SET_BF_VOLATILE(tfa, MANSCONF, 1); + + /* Leave powerdown state */ + err = tfa_cf_powerup(tfa); + if (err) return err; + + err = show_current_state(tfa); + + if (tfa->tfa_family == 2) { + /* Reset SBSL to 0 (workaround of enbl_powerswitch=0) */ + TFA_SET_BF_VOLATILE(tfa, SBSL, 0); + /* Sending commands to DSP we need to make sure RST is 0 (otherwise we get no response)*/ + TFA_SET_BF(tfa, RST,0); + } + } + + /* Check if there are sample rate changes */ + err = get_sample_rate_info(tfa, prof, previous_prof, fs_previous_profile); + if (err) return err; + + + /* Write files from previous profile (default section) + * Should only be used for the patch&trap patch (file) + */ + if(tfa->ext_dsp != 0) { + if (tfa->tfa_family == 2) { + for(i=0;ilength;i++) { + /* Search for the default section */ + if(i == 0) { + while(previous_prof->list[i].type != dscDefault && i < previous_prof->length) { + i++; + } + i++; + } + + /* Only if we found the default section try writing the file */ + if(i < previous_prof->length) { + if(previous_prof->list[i].type == dscFile || previous_prof->list[i].type == dscPatch) { + /* Only write this once */ + if ( tfa->verbose && k==0) { + pr_debug("---------- files default profile: %s (%d) ---------- \n", + tfaContGetString(tfa->cnt, &previous_prof->name), prof_idx); + k++; + } + file = (nxpTfaFileDsc_t *)(previous_prof->list[i].offset+(uint8_t *)tfa->cnt); + err = tfaContWriteFile(tfa, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER); + } + } + } + } + + if ( tfa->verbose) { + pr_debug("---------- files new profile: %s (%d) ---------- \n", + tfaContGetString(tfa->cnt, &prof->name), prof_idx); + } + } + + /* write everything until end or the default section starts + * Start where we currenly left */ + for(i=j;ilength;i++) { + /* We only want to write the values before the default section when we switch profile */ + + if(prof->list[i].type == dscDefault) { + break; + } + + switch (prof->list[i].type) { + case dscFile: + case dscPatch: + /* For tiberius stereo 1 device does not have a dsp! */ + if(tfa->ext_dsp != 0){ + file = (nxpTfaFileDsc_t *)(prof->list[i].offset+(uint8_t *)tfa->cnt); + err = tfaContWriteFile(tfa, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER); + } + break; + case dscSetInputSelect: + case dscSetOutputSelect: + case dscSetProgramConfig: + case dscSetLagW: + case dscSetGains: + case dscSetvBatFactors: + case dscSetSensesCal: + case dscSetSensesDelay: + case dscSetMBDrc: + case dscSetFwkUseCase: + case dscSetVddpConfig: + /* For tiberius stereo 1 device does not have a dsp! */ + if(tfa->ext_dsp != 0) { + create_dsp_buffer_msg(tfa, (nxpTfaMsg_t *) + ( prof->list[i].offset+(char*)tfa->cnt), buffer, &size); + err = dsp_msg(tfa, size, buffer); + + if (tfa->verbose) { + pr_debug("command: %s=0x%02x%02x%02x \n", + tfaContGetCommandString(prof->list[i].type), + (unsigned char)buffer[0], (unsigned char)buffer[1], (unsigned char)buffer[2]); + } + } + break; + case dscCmd: + /* For tiberius stereo 1 device does not have a dsp! */ + if(tfa->ext_dsp != 0) { + size = *(uint16_t *)(prof->list[i].offset+(char*)tfa->cnt); + err = dsp_msg(tfa, size, prof->list[i].offset+2+(char*)tfa->cnt); + if (tfa->verbose) { + const char *cmd_id = prof->list[i].offset+2+(char*)tfa->cnt; + pr_debug("Writing cmd=0x%02x%02x%02x \n", (uint8_t)cmd_id[0], (uint8_t)cmd_id[1], (uint8_t)cmd_id[2]); + } + } + break; + default: + /* This allows us to write bitfield, registers or xmem after files */ + if (tfaContWriteItem(tfa, &prof->list[i]) != Tfa98xx_Error_Ok) { + return Tfa98xx_Error_Bad_Parameter; + } + break; + } + + if (err != Tfa98xx_Error_Ok) { + return err; + } + } + + if ((prof->group != previous_prof->group || prof->group == 0) && (tfa->tfa_family == 2)) { + if (tfa->is_probus_device) { + pr_debug("%s: probus device set AMPE=1", __func__); + TFA_SET_BF_VOLATILE(tfa, AMPE, 1); + } else if(TFA_GET_BF(tfa, REFCKSEL) == 0) { + /* set SBSL to go to operation mode */ + TFA_SET_BF_VOLATILE(tfa, SBSL, 1); + } + } + + return err; +} + +/* + * process only vstep in the profilelist + * + */ +enum Tfa98xx_Error tfaContWriteFilesVstep(struct tfa_device *tfa, int prof_idx, int vstep_idx) +{ + nxpTfaProfileList_t *prof = tfaContGetDevProfList(tfa->cnt, tfa->dev_idx, prof_idx); + unsigned int i; + nxpTfaFileDsc_t *file; + nxpTfaHeader_t *hdr; + nxpTfaHeaderType_t type; + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + if ( !prof ) + return Tfa98xx_Error_Bad_Parameter; + + if (tfa->verbose) + tfa98xx_trace_printk("device:%s profile:%s vstep:%d\n", tfaContDeviceName(tfa->cnt, tfa->dev_idx), + tfaContProfileName(tfa->cnt, tfa->dev_idx,prof_idx),vstep_idx); + + /* write vstep file only! */ + for(i=0;ilength;i++) { + if ( prof->list[i].type == dscFile ) { + file = (nxpTfaFileDsc_t *)(prof->list[i].offset+(uint8_t *)tfa->cnt); + hdr = (nxpTfaHeader_t *)file->data; + type = (nxpTfaHeaderType_t) hdr->id; + + switch (type) { + case volstepHdr: + if ( tfaContWriteFile(tfa, file, vstep_idx, TFA_MAX_VSTEP_MSG_MARKER) ) + return Tfa98xx_Error_Bad_Parameter; + break; + default: + break; + } + } + } + + return err; +} + +char *tfaContGetString(nxpTfaContainer_t *cnt, nxpTfaDescPtr_t *dsc) +{ + if ( dsc->type != dscString) + return "Undefined string"; + + return dsc->offset+(char*)cnt; +} + +char *tfaContGetCommandString(uint32_t type) +{ + if(type == dscSetInputSelect) + return "SetInputSelector"; + else if(type == dscSetOutputSelect) + return "SetOutputSelector"; + else if(type == dscSetProgramConfig) + return "SetProgramConfig"; + else if(type == dscSetLagW) + return "SetLagW"; + else if(type == dscSetGains) + return "SetGains"; + else if(type == dscSetvBatFactors) + return "SetvBatFactors"; + else if(type == dscSetSensesCal) + return "SetSensesCal"; + else if(type == dscSetSensesDelay) + return "SetSensesDelay"; + else if(type == dscSetMBDrc) + return "SetMBDrc"; + else if(type == dscSetFwkUseCase) + return "SetFwkUseCase"; + else if(type == dscSetVddpConfig) + return "SetVddpConfig"; + else if(type == dscFilter) + return "filter"; + else + return "Undefined string"; +} + +/* + * Get the name of the device at a certain index in the container file + * return device name + */ +char *tfaContDeviceName(nxpTfaContainer_t *cnt, int dev_idx) +{ + nxpTfaDeviceList_t *dev; + + dev = tfaContDevice(cnt, dev_idx); + if (dev == NULL) + return "!ERROR!"; + + return tfaContGetString(cnt, &dev->name); +} + +/* + * Get the application name from the container file application field + * note that the input stringbuffer should be sizeof(application field)+1 + * + */ +int tfa_cnt_get_app_name(struct tfa_device *tfa, char *name) +{ + unsigned int i; + int len = 0; + + for(i=0; icnt->application); i++) { + if (isalnum(tfa->cnt->application[i])) /* copy char if valid */ + name[len++] = tfa->cnt->application[i]; + if (tfa->cnt->application[i]=='\0') + break; + } + name[len++] = '\0'; + + return len; +} + +/* + * Get profile index of the calibration profile. + * Returns: (profile index) if found, (-2) if no + * calibration profile is found or (-1) on error + */ +int tfaContGetCalProfile(struct tfa_device *tfa) +{ + int prof, cal_idx = -2; + + if ( (tfa->dev_idx < 0) || (tfa->dev_idx >= tfa->cnt->ndev) ) + return -1; + + /* search for the calibration profile in the list of profiles */ + for (prof = 0; prof < tfa->cnt->nprof; prof++) { + if(strstr(tfaContProfileName(tfa->cnt, tfa->dev_idx, prof), ".cal") != NULL) { + cal_idx = prof; + pr_debug("Using calibration profile: '%s'\n", tfaContProfileName(tfa->cnt, tfa->dev_idx, prof)); + break; + } + } + + return cal_idx; +} + +/** + * Is the profile a tap profile + */ +int tfaContIsTapProfile(struct tfa_device *tfa, int prof_idx) +{ + if ((tfa->dev_idx < 0) || (tfa->dev_idx >= tfa->cnt->ndev)) + return -1; + + /* Check if next profile is tap profile */ + if (strstr(tfaContProfileName(tfa->cnt, tfa->dev_idx, prof_idx), ".tap") != NULL) { + pr_debug("Using Tap profile: '%s'\n", tfaContProfileName(tfa->cnt, tfa->dev_idx, prof_idx)); + return 1; + } + + return 0; +} + +/* + * Get the name of the profile at certain index for a device in the container file + * return profile name + */ +char *tfaContProfileName(nxpTfaContainer_t *cnt, int dev_idx, int prof_idx) +{ + nxpTfaProfileList_t *prof = NULL; + + /* the Nth profiles for this device */ + prof = tfaContGetDevProfList(cnt, dev_idx, prof_idx); + + /* If the index is out of bound */ + if (prof == NULL) + return "NONE"; + + return tfaContGetString(cnt, &prof->name); +} + +/* + * return 1st profile list + */ +nxpTfaProfileList_t *tfaContGet1stProfList(nxpTfaContainer_t * cont) +{ + nxpTfaProfileList_t *prof; + uint8_t *b = (uint8_t *) cont; + + int maxdev = 0; + nxpTfaDeviceList_t *dev; + + // get nr of devlists + maxdev = cont->ndev; + // get last devlist + dev = tfaContGetDevList(cont, maxdev - 1); + if(dev == NULL) + return NULL; + // the 1st profile starts after the last device list + b = (uint8_t *) dev + sizeof(nxpTfaDeviceList_t) + dev->length * (sizeof(nxpTfaDescPtr_t)); + prof = (nxpTfaProfileList_t *) b; + return prof; +} + +/* + * return 1st livedata list + */ +nxpTfaLiveDataList_t *tfaContGet1stLiveDataList(nxpTfaContainer_t * cont) +{ + nxpTfaLiveDataList_t *ldata; + nxpTfaProfileList_t *prof; + nxpTfaDeviceList_t *dev; + uint8_t *b = (uint8_t *) cont; + int maxdev, maxprof; + + // get nr of devlists+1 + maxdev = cont->ndev; + // get nr of proflists + maxprof = cont->nprof; + + // get last devlist + dev = tfaContGetDevList(cont, maxdev - 1); + // the 1st livedata starts after the last device list + b = (uint8_t *) dev + sizeof(nxpTfaDeviceList_t) + + dev->length * (sizeof(nxpTfaDescPtr_t)); + + while(maxprof != 0) { + // get last proflist + prof = (nxpTfaProfileList_t *) b; + b += sizeof(nxpTfaProfileList_t) + + ((prof->length-1) * (sizeof(nxpTfaDescPtr_t))); + maxprof--; + } + + /* Else the marker falls off */ + b += 4; //bytes + + ldata = (nxpTfaLiveDataList_t *) b; + return ldata; +} + +/* + * return the device list pointer + */ +nxpTfaDeviceList_t *tfaContDevice(nxpTfaContainer_t *cnt, int dev_idx) +{ + return tfaContGetDevList(cnt, dev_idx); +} + +/* + * return the next profile: + * - assume that all profiles are adjacent + * - calculate the total length of the input + * - the input profile + its length is the next profile + */ +nxpTfaProfileList_t* tfaContNextProfile(nxpTfaProfileList_t* prof) { + uint8_t *this, *next; /* byte pointers for byte pointer arithmetic */ + nxpTfaProfileList_t* nextprof; + int listlength; /* total length of list in bytes */ + + if(prof == NULL) + return NULL; + + if (prof->ID != TFA_PROFID) + return NULL; /* invalid input */ + + this = (uint8_t *)prof; + /* nr of items in the list, length includes name dsc so - 1*/ + listlength = (prof->length - 1)*sizeof(nxpTfaDescPtr_t); + /* the sizeof(nxpTfaProfileList_t) includes the list[0] length */ + next = this + listlength + sizeof(nxpTfaProfileList_t);// - sizeof(nxpTfaDescPtr_t); + nextprof = (nxpTfaProfileList_t *)next; + + if (nextprof->ID != TFA_PROFID) + return NULL; + + return nextprof; +} + +/* + * return the next livedata + */ +nxpTfaLiveDataList_t* tfaContNextLiveData(nxpTfaLiveDataList_t* livedata) { + nxpTfaLiveDataList_t* nextlivedata = (nxpTfaLiveDataList_t *)( (char*)livedata + (livedata->length*4) + + sizeof(nxpTfaLiveDataList_t) -4); + + if (nextlivedata->ID == TFA_LIVEDATAID) + return nextlivedata; + + return NULL; +} + +/* + * check CRC for container + * CRC is calculated over the bytes following the CRC field + * + * return non zero value on error + */ +int tfaContCrcCheckContainer(nxpTfaContainer_t *cont) +{ + uint8_t *base; + size_t size; + uint32_t crc; + + base = (uint8_t *)&cont->CRC + 4; // ptr to bytes following the CRC field + size = (size_t)(cont->size - (base - (uint8_t *)cont)); // nr of bytes following the CRC field + crc = ~crc32_le(~0u, base, size); + + return crc != cont->CRC; +} + +static void get_all_features_from_cnt(struct tfa_device *tfa, int *hw_feature_register, int sw_feature_register[2]) +{ + nxpTfaFeatures_t *features; + int i; + + nxpTfaDeviceList_t *dev = tfaContDevice(tfa->cnt, tfa->dev_idx); + + /* Init values in case no keyword is defined in cnt file: */ + *hw_feature_register = -1; + sw_feature_register[0] = -1; + sw_feature_register[1] = -1; + + if(dev == NULL) + return; + + // process the device list + for(i=0;ilength;i++) { + if (dev->list[i].type == dscFeatures) { + features = (nxpTfaFeatures_t *)(dev->list[i].offset+(uint8_t *)tfa->cnt); + *hw_feature_register = features->value[0]; + sw_feature_register[0] = features->value[1]; + sw_feature_register[1] = features->value[2]; + break; + } + } +} + +/* wrapper function */ +void get_hw_features_from_cnt(struct tfa_device *tfa, int *hw_feature_register) +{ + int sw_feature_register[2]; + get_all_features_from_cnt(tfa, hw_feature_register, sw_feature_register); +} + +/* wrapper function */ +void get_sw_features_from_cnt(struct tfa_device *tfa, int sw_feature_register[2]) +{ + int hw_feature_register; + get_all_features_from_cnt(tfa, &hw_feature_register, sw_feature_register); +} + +enum Tfa98xx_Error tfa98xx_factory_trimmer(struct tfa_device *tfa) +{ + return (tfa->dev_ops.factory_trimmer)(tfa); +} + +enum Tfa98xx_Error tfa_set_filters(struct tfa_device *tfa, int prof_idx) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaProfileList_t *prof = tfaContGetDevProfList(tfa->cnt, tfa->dev_idx, prof_idx); + unsigned int i; + + if ( !prof ) + return Tfa98xx_Error_Bad_Parameter; + + /* If we are in powerdown there is no need to set filters */ + if (TFA_GET_BF(tfa, PWDN) == 1) + return Tfa98xx_Error_Ok; + + /* loop the profile to find filter settings */ + for(i=0;ilength;i++) { + /* We only want to write the values before the default section */ + if(prof->list[i].type == dscDefault) + break; + + /* write all filter settings */ + if ( prof->list[i].type == dscFilter) { + if (tfaContWriteItem(tfa, &prof->list[i]) != Tfa98xx_Error_Ok) + return err; + } + } + + return err; +} + +int tfa_tib_dsp_msgmulti(struct tfa_device *tfa, int length, const char *buffer) +{ + uint8_t *buf = (uint8_t *)buffer; + static uint8_t *blob = NULL, *blobptr; /* TODO: not multi-thread safe */ + static int total = 0; /* TODO: not multi-thread safe */ + int post_len = 0; + + /* checks for 24b_BE or 32_LE */ + int len_word_in_bytes = (tfa->convert_dsp32) ? 4 : 3; + /* TODO: get rid of these magic constants max size should depend on the tfa device type */ + int tfadsp_max_msg_size = (tfa->convert_dsp32) ? 5336 : 4000; + + /* No data found*/ + if(length == -1 && blob == NULL) { + return -1; + } + + if (length == -1) { + int i; + /* set last length field to zero */ + for (i = total; i < (total + len_word_in_bytes); i++) { + blob[i] = 0; + } + total += len_word_in_bytes; + memcpy(buf, blob, total); + + kfree(blob); + blob = NULL; /* Set to NULL pointer, otherwise no new malloc is done! */ + return total; + } + + if (blob == NULL) { + if (tfa->verbose) + pr_debug("%s, Creating the multi-message \n\n", __FUNCTION__); + + blob = kmalloc(tfadsp_max_msg_size, GFP_KERNEL); + /* add command ID for multi-msg = 0x008015 */ + if (tfa->convert_dsp32) { + blob[0] = 0x15; + blob[1] = 0x80; + blob[2] = 0x0; + blob[3] = 0x0; + } + else { + blob[0] = 0x0; + blob[1] = 0x80; + blob[2] = 0x15; + } + blobptr = blob; + blobptr += len_word_in_bytes; + total = len_word_in_bytes; + } + + if (tfa->verbose) { + pr_debug("%s, id:0x%02x%02x%02x, length:%d \n", __FUNCTION__, buf[0], buf[1], buf[2], length); + } + + /* check total message size after concatination */ + post_len = total+length+(2*len_word_in_bytes); + if (post_len > tfadsp_max_msg_size) { + //pr_debug("New multi-message too large! (%d >= %d (max.)), current length: %d\n", post_len, tfadsp_max_msg_size, total); + return Tfa98xx_Error_Buffer_too_small; + } + + /* add length field (length in words) to the multi message */ + if (tfa->convert_dsp32) { + *blobptr++ = (uint8_t)((length/len_word_in_bytes) & 0xff); /* lsb */ + *blobptr++ = (uint8_t)(((length/len_word_in_bytes) & 0xff00) >> 8); /* msb */ + *blobptr++ = 0x0; + *blobptr++ = 0x0; + } + else { + *blobptr++ = 0x0; + *blobptr++ = (uint8_t)(((length/len_word_in_bytes) & 0xff00) >> 8); /* msb */ + *blobptr++ = (uint8_t)((length/len_word_in_bytes) & 0xff); /* lsb */ + } + memcpy(blobptr, buf, length); + blobptr += length; + total += (length + len_word_in_bytes); + + /* SetRe25 message is always the last message of the multi-msg */ + if (tfa->convert_dsp32) { + if (buf[1] == 0x81 && buf[0] == SB_PARAM_SET_RE25C) { + return 1; /* 1 means last message is done! */ + } + } else { + if (buf[1] == 0x81 && buf[2] == SB_PARAM_SET_RE25C) { + return 1; /* 1 means last message is done! */ + } + } + + return 0; +} diff --git a/sound/soc/codecs/tfa_debug.c b/sound/soc/codecs/tfa_debug.c new file mode 100644 index 000000000000..d6227262e77f --- /dev/null +++ b/sound/soc/codecs/tfa_debug.c @@ -0,0 +1,416 @@ +/* + * Copyright 2014-2017 NXP Semiconductors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dbgprint.h" +#include "tfa_service.h" +#include "tfa98xx_tfafieldnames.h" + +/* support for error code translation into text */ +static char latest_errorstr[64]; + +const char* tfa98xx_get_error_string(enum Tfa98xx_Error error) +{ + const char* pErrStr; + + switch (error) + { + case Tfa98xx_Error_Ok: + pErrStr = "Ok"; + break; + case Tfa98xx_Error_DSP_not_running: + pErrStr = "DSP_not_running"; + break; + case Tfa98xx_Error_Bad_Parameter: + pErrStr = "Bad_Parameter"; + break; + case Tfa98xx_Error_NotOpen: + pErrStr = "NotOpen"; + break; + case Tfa98xx_Error_InUse: + pErrStr = "InUse"; + break; + case Tfa98xx_Error_RpcBusy: + pErrStr = "RpcBusy"; + break; + case Tfa98xx_Error_RpcModId: + pErrStr = "RpcModId"; + break; + case Tfa98xx_Error_RpcParamId: + pErrStr = "RpcParamId"; + break; + case Tfa98xx_Error_RpcInvalidCC: + pErrStr = "RpcInvalidCC"; + break; + case Tfa98xx_Error_RpcInvalidSeq: + pErrStr = "RpcInvalidSeq"; + break; + case Tfa98xx_Error_RpcInvalidParam: + pErrStr = "RpcInvalidParam"; + break; + case Tfa98xx_Error_RpcBufferOverflow: + pErrStr = "RpcBufferOverflow"; + break; + case Tfa98xx_Error_RpcCalibBusy: + pErrStr = "RpcCalibBusy"; + break; + case Tfa98xx_Error_RpcCalibFailed: + pErrStr = "RpcCalibFailed"; + break; + case Tfa98xx_Error_Not_Supported: + pErrStr = "Not_Supported"; + break; + case Tfa98xx_Error_I2C_Fatal: + pErrStr = "I2C_Fatal"; + break; + case Tfa98xx_Error_I2C_NonFatal: + pErrStr = "I2C_NonFatal"; + break; + case Tfa98xx_Error_StateTimedOut: + pErrStr = "WaitForState_TimedOut"; + break; + default: + sprintf(latest_errorstr, "Unspecified error (%d)", (int)error); + pErrStr = latest_errorstr; + } + return pErrStr; +} +/*****************************************************************************/ +/* bitfield lookups */ +/* + * generic table lookup functions + */ +/** + * lookup bf in table + * return 'unkown' if not found + */ +static char *tfa_bf2name(tfaBfName_t *table, uint16_t bf) { + int n=0; + + do { + if ((table[n].bfEnum & 0xfff0 ) == (bf & 0xfff0 )) { + return table[n].bfName; + } + } + while( table[n++].bfEnum != 0xffff); + + return table[n-1].bfName; /* last name says unkown */ +} +/** + * lookup name in table + * return 0xffff if not found + */ +static uint16_t tfa_name2bf(tfaBfName_t *table,const char *name) { + int n = 0; + + do { + if (strcasecmp(name, table[n].bfName)==0) + return table[n].bfEnum; + } while (table[n++].bfEnum != 0xffff); + + return 0xffff; +} + +/* + * tfa2 bitfield name table + */ +TFA2_NAMETABLE +TFA2_BITNAMETABLE + +/* + * tfa1 bitfield name tables + */ +TFA1_NAMETABLE +TFA9896_NAMETABLE +TFA9872_NAMETABLE +TFA9874_NAMETABLE +TFA9890_NAMETABLE +TFA9891_NAMETABLE +TFA9887_NAMETABLE +TFA1_BITNAMETABLE +TFA9912_NAMETABLE +TFA9894_NAMETABLE +TFA9896_BITNAMETABLE +TFA9872_BITNAMETABLE +TFA9874_BITNAMETABLE +TFA9912_BITNAMETABLE +TFA9890_BITNAMETABLE +TFA9891_BITNAMETABLE +TFA9887_BITNAMETABLE +TFA9894_BITNAMETABLE + +char *tfaContBitName(uint16_t num, unsigned short rev) +{ + char *name; + /* end of list for the unknown string */ + int tableLength = sizeof(Tfa1DatasheetNames)/sizeof(tfaBfName_t); + const char *unknown=Tfa1DatasheetNames[tableLength-1].bfName; + + switch (rev & 0xff) { + case 0x88: + name = tfa_bf2name(Tfa2BitNames, num); + break; + case 0x97: + name = tfa_bf2name(Tfa1BitNames, num); + break; + case 0x96: + name = tfa_bf2name(Tfa9896BitNames, num); + break; + case 0x72: + name = tfa_bf2name(Tfa9872BitNames, num); + break; + case 0x74: + name = tfa_bf2name(Tfa9874BitNames, num); + break; + case 0x92: + name = tfa_bf2name(Tfa9891BitNames, num); + break; + case 0x91: + case 0x80: + case 0x81: + name = tfa_bf2name(Tfa9890BitNames, num); /* my tabel 1st */ + if (strcmp(unknown, name)==0) + name = tfa_bf2name(Tfa1BitNames, num); /* try generic table */ + break; + case 0x12: + name = tfa_bf2name(Tfa9887BitNames, num); /* my tabel 1st */ + if (strcmp(unknown, name)==0) + name = tfa_bf2name(Tfa1BitNames, num); /* try generic table */ + break; + case 0x13: + name = tfa_bf2name(Tfa9912BitNames, num); + break; + case 0x94: + name = tfa_bf2name(Tfa9894BitNames, num); + break; + default: + PRINT_ERROR("unknown REVID:0x%0x\n", rev); + tableLength = sizeof(Tfa1BitNames)/sizeof(tfaBfName_t); /* end of list */ + name = (char *)unknown; + break; + } + return name; +} + +char *tfaContDsName(uint16_t num, unsigned short rev) +{ + char *name; + /* end of list for the unknown string */ + int tableLength = sizeof(Tfa1DatasheetNames)/sizeof(tfaBfName_t); + const char *unknown=Tfa1DatasheetNames[tableLength-1].bfName; + + switch (rev & 0xff) { + case 0x88: + name = tfa_bf2name(Tfa2DatasheetNames, num); + break; + case 0x97: + name = tfa_bf2name(Tfa1DatasheetNames, num); + break; + case 0x96: + name = tfa_bf2name(Tfa9896DatasheetNames, num); + break; + case 0x72: + name = tfa_bf2name(Tfa9872DatasheetNames, num); + break; + case 0x74: + name = tfa_bf2name(Tfa9874DatasheetNames, num); + break; + case 0x92: + name = tfa_bf2name(Tfa9891DatasheetNames, num); + break; + case 0x91: + case 0x80: + case 0x81: + name = tfa_bf2name(Tfa9890DatasheetNames, num); /* my tabel 1st */ + if (strcmp(unknown, name)==0) + name = tfa_bf2name(Tfa1DatasheetNames, num); /* try generic table */ + break; + case 0x12: + name = tfa_bf2name(Tfa9887DatasheetNames, num); /* my tabel 1st */ + if (strcmp(unknown, name)==0) + name = tfa_bf2name(Tfa1DatasheetNames, num); /* try generic table */ + break; + case 0x13: + name = tfa_bf2name(Tfa9912DatasheetNames, num); + break; + case 0x94: + name = tfa_bf2name(Tfa9894DatasheetNames, num); + break; + default: + PRINT_ERROR("unknown REVID:0x%0x\n", rev); + tableLength = sizeof(Tfa1DatasheetNames)/sizeof(tfaBfName_t); /* end of list */ + name = (char *)unknown; + break; + } + return name; +} + +char *tfaContBfName(uint16_t num, unsigned short rev) +{ + char *name; + /* end of list for the unknown string */ + int tableLength = sizeof(Tfa1DatasheetNames)/sizeof(tfaBfName_t); + const char *unknown=Tfa1DatasheetNames[tableLength-1].bfName; + + /* if datasheet name does not exist look for bitfieldname */ + name = tfaContDsName(num, rev); + if (strcmp(unknown, name)==0) + name = tfaContBitName(num, rev); + + return name; +} + +uint16_t tfaContBfEnum(const char *name, unsigned short rev) +{ + uint16_t bfnum; + + switch (rev & 0xff) { + case 0x88: + bfnum = tfa_name2bf(Tfa2DatasheetNames, name); + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa2BitNames, name);/* try long bitname table */ + break; + case 0x97: + bfnum = tfa_name2bf(Tfa1DatasheetNames, name); + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa1BitNames, name);/* try generic table */ + break; + case 0x96: + bfnum = tfa_name2bf(Tfa9896DatasheetNames, name); + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa9896BitNames, name);/* try generic table */ + break; + case 0x72: + bfnum = tfa_name2bf(Tfa9872DatasheetNames, name); + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa9872BitNames, name);/* try long bitname table */ + break; + case 0x74: + bfnum = tfa_name2bf(Tfa9874DatasheetNames, name); + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa9874BitNames, name);/* try long bitname table */ + break; + case 0x92: + bfnum = tfa_name2bf(Tfa9891DatasheetNames, name); + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa9891BitNames, name);/* try long bitname table */ + break; + case 0x91: + case 0x80: + case 0x81: + bfnum = tfa_name2bf(Tfa9890DatasheetNames, name); /* my tabel 1st */ + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa1DatasheetNames, name);/* try generic table */ + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa1BitNames, name); /* try 2nd generic table */ + break; + case 0x12: + bfnum = tfa_name2bf(Tfa9887DatasheetNames, name); /* my tabel 1st */ + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa1DatasheetNames, name);/* try generic table */ + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa1BitNames, name);/* try 2nd generic table */ + break; + case 0x13: + bfnum = tfa_name2bf(Tfa9912DatasheetNames, name); + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa9912BitNames, name);/* try long bitname table */ + break; + case 0x94: + bfnum = tfa_name2bf(Tfa9894DatasheetNames, name); + if (bfnum==0xffff) + bfnum = tfa_name2bf(Tfa9894BitNames, name);/* try long bitname table */ + break; + + default: + PRINT_ERROR("unknown REVID:0x%0x\n", rev); + bfnum=0xffff; + break; + } + + return bfnum; +} + +/* + * check all lists for a hit + * this is for the parser to know if it's an existing bitname + */ +uint16_t tfaContBfEnumAny(const char *name) +{ + uint16_t bfnum; + + /* datasheet names first */ + bfnum = tfa_name2bf(Tfa2DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa1DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9891DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9890DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9887DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9872DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9874DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9896DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9912DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9894DatasheetNames, name); + if (bfnum!=0xffff) + return bfnum; + /* and then bitfield names */ + bfnum = tfa_name2bf(Tfa2BitNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa1BitNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9891BitNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9890BitNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9887BitNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9872BitNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9874BitNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9896BitNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9912BitNames, name); + if (bfnum!=0xffff) + return bfnum; + bfnum = tfa_name2bf(Tfa9894BitNames, name); + + return bfnum; +} diff --git a/sound/soc/codecs/tfa_dsp.c b/sound/soc/codecs/tfa_dsp.c new file mode 100644 index 000000000000..fe8b582b393a --- /dev/null +++ b/sound/soc/codecs/tfa_dsp.c @@ -0,0 +1,3852 @@ +/* + * Copyright 2014-2017 NXP Semiconductors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dbgprint.h" +#include "tfa_container.h" +#include "tfa.h" +#include "tfa98xx_tfafieldnames.h" +#include "tfa_internal.h" + +//#define TFA9874_NONDSP_STEREO +/* handle macro for bitfield */ +#define TFA_MK_BF(reg, pos, len) ((reg<<8)|(pos<<4)|(len-1)) + +/* abstract family for register */ +#define FAM_TFA98XX_CF_CONTROLS (TFA_FAM(tfa,RST) >> 8) +#define FAM_TFA98XX_CF_MEM (TFA_FAM(tfa,MEMA)>> 8) +#define FAM_TFA98XX_MTP0 (TFA_FAM(tfa,MTPOTC) >> 8) +#define FAM_TFA98xx_INT_EN (TFA_FAM(tfa,INTENVDDS) >> 8) + +#define CF_STATUS_I2C_CMD_ACK 0x01 + +/* Defines below are used for irq function (this removed the genregs include) */ +#define TFA98XX_INTERRUPT_ENABLE_REG1 0x48 +#define TFA98XX_INTERRUPT_IN_REG1 0x44 +#define TFA98XX_INTERRUPT_OUT_REG1 0x40 +#define TFA98XX_STATUS_POLARITY_REG1 0x4c +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK 0x2 +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK 0x1 +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_POS 1 +#define TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_POS 0 + +void tfanone_ops(struct tfa_device_ops *ops); +void tfa9872_ops(struct tfa_device_ops *ops); +void tfa9874_ops(struct tfa_device_ops *ops); +void tfa9912_ops(struct tfa_device_ops *ops); +void tfa9888_ops(struct tfa_device_ops *ops); +void tfa9891_ops(struct tfa_device_ops *ops); +void tfa9897_ops(struct tfa_device_ops *ops); +void tfa9896_ops(struct tfa_device_ops *ops); +void tfa9890_ops(struct tfa_device_ops *ops); +void tfa9895_ops(struct tfa_device_ops *ops); +void tfa9894_ops(struct tfa_device_ops *ops); + +#ifndef MIN +#define MIN(A,B) (Amohm[channel]; +} + +/* return sign extended tap pattern */ +int tfa_get_tap_pattern(struct tfa_device *tfa) +{ + int value = tfa_get_bf(tfa, TFA9912_BF_CFTAPPAT ); + int bitshift; + uint8_t field_len = 1 + (TFA9912_BF_CFTAPPAT & 0x0f); /* length of bitfield */ + + bitshift = 8*sizeof(int)-field_len; + /* signextend */ + value = (value << bitshift) >> bitshift; + + return value; +} +/* + * interrupt bit function to clear + */ +int tfa_irq_clear(struct tfa_device *tfa, enum tfa9912_irq bit) +{ + unsigned char reg; + + /* make bitfield enum */ + if ( bit == tfa9912_irq_all) { + /* operate on all bits */ + for(reg=TFA98XX_INTERRUPT_IN_REG1; reg>4)); + reg_write(tfa, reg, 1<<(bit & 0x0f)); /* only this bit */ + } else + return -1; + + return 0; +} +/* + * return state of irq or -1 if illegal bit + */ +int tfa_irq_get(struct tfa_device *tfa, enum tfa9912_irq bit) +{ + uint16_t value; + int reg, mask; + + if (bit < tfa9912_irq_max) { + /* only this bit */ + reg = TFA98XX_INTERRUPT_OUT_REG1 + (bit>>4); + mask = 1<<(bit & 0x0f); + reg_read(tfa, (unsigned char)reg, &value); + } else + return -1; + + return (value & mask) !=0 ; +} +/* + * interrupt bit function that operates on the shadow regs in the handle + */ + +int tfa_irq_ena(struct tfa_device *tfa, enum tfa9912_irq bit, int state) +{ + uint16_t value, new_value; + int reg=0, mask; + /* */ + if ( bit == tfa9912_irq_all) { + /* operate on all bits */ + for(reg=TFA98XX_INTERRUPT_ENABLE_REG1; reg<=TFA98XX_INTERRUPT_ENABLE_REG1+tfa9912_irq_max/16; reg++) { + reg_write(tfa, (unsigned char)reg, state ? 0xffff : 0); /* all bits */ + tfa->interrupt_enable[reg-TFA98XX_INTERRUPT_ENABLE_REG1] = state ? 0xffff : 0; /* all bits */ + } + } else if (bit < tfa9912_irq_max) { + /* only this bit */ + reg = TFA98XX_INTERRUPT_ENABLE_REG1 + (bit>>4); + mask = 1<<(bit & 0x0f); + reg_read(tfa, (unsigned char)reg, &value); + if (state) //set + new_value = (uint16_t)(value | mask); + else // clear + new_value = value & ~mask; + if ( new_value != value) { + reg_write(tfa, (unsigned char)reg, new_value); /* only this bit */ + tfa->interrupt_enable[reg-TFA98XX_INTERRUPT_ENABLE_REG1] = new_value; + } + } else + return -1; + + return 0; +} + +/* + * mask interrupts by disabling them + */ +int tfa_irq_mask(struct tfa_device *tfa) +{ + int reg; + + /* operate on all bits */ + for (reg=TFA98XX_INTERRUPT_ENABLE_REG1; reg<=TFA98XX_INTERRUPT_ENABLE_REG1+tfa9912_irq_max/16; reg++) + reg_write(tfa, (unsigned char)reg, 0); + + return 0; +} + +/* + * unmask interrupts by enabling them again + */ +int tfa_irq_unmask(struct tfa_device *tfa) +{ + int reg; + + /* operate on all bits */ + for (reg=TFA98XX_INTERRUPT_ENABLE_REG1; reg<=TFA98XX_INTERRUPT_ENABLE_REG1+tfa9912_irq_max/16; reg++) + reg_write(tfa, (unsigned char)reg, tfa->interrupt_enable[reg-TFA98XX_INTERRUPT_ENABLE_REG1]); + + return 0; +} + +/* + * interrupt bit function that sets the polarity + */ + +int tfa_irq_set_pol(struct tfa_device *tfa, enum tfa9912_irq bit, int state) +{ + uint16_t value, new_value; + int reg=0, mask; + + if (bit == tfa9912_irq_all) { + /* operate on all bits */ + for(reg=TFA98XX_STATUS_POLARITY_REG1; reg<=TFA98XX_STATUS_POLARITY_REG1+tfa9912_irq_max/16; reg++) { + reg_write(tfa, (unsigned char)reg, state ? 0xffff : 0); /* all bits */ + } + } else if (bit < tfa9912_irq_max) { + /* only this bit */ + reg = TFA98XX_STATUS_POLARITY_REG1 + (bit>>4); + mask = 1<<(bit & 0x0f); + reg_read(tfa, (unsigned char)reg, &value); + if (state) /* Active High */ + new_value = (uint16_t)(value | mask); + else /* Active Low */ + new_value = value & ~mask; + if ( new_value != value) { + reg_write(tfa, (unsigned char)reg, new_value); /* only this bit */ + } + } else + return -1; + + return 0; +} + +/* + * set device info and register device ops + */ +void tfa_set_query_info(struct tfa_device *tfa) +{ + /* invalidate device struct cached values */ + tfa->hw_feature_bits = -1; + tfa->sw_feature_bits[0] = -1; + tfa->sw_feature_bits[1] = -1; + tfa->profile = -1; + tfa->vstep = -1; + /* defaults */ + tfa->is_probus_device = 0; + tfa->tfa_family = 1; + tfa->daimap = Tfa98xx_DAI_I2S; /* all others */ + tfa->spkr_count = 1; + tfa->spkr_select = 0; + tfa->support_tcoef = supportYes; + tfa->supportDrc = supportNotSet; + tfa->support_saam = supportNotSet; + tfa->ext_dsp = -1; /* respond to external DSP: -1:none, 0:no_dsp, 1:cold, 2:warm */ + tfa->bus=0; + tfa->partial_enable = 0; + tfa->convert_dsp32 = 0; + tfa->sync_iv_delay = 0; + + /* TODO use the getfeatures() for retrieving the features [artf103523] + tfa->supportDrc = supportNotSet;*/ + + switch (tfa->rev & 0xff) { + case 0: /* tfanone : non-i2c external DSP device */ + /* e.g. qc adsp */ + tfa->supportDrc = supportYes; + tfa->tfa_family = 0; + tfa->spkr_count = 0; + tfa->daimap = 0; + tfanone_ops(&tfa->dev_ops); /* register device operations via tfa hal*/ + tfa->bus=1; + break; + case 0x72: + /* tfa9872 */ + tfa->supportDrc = supportYes; + tfa->tfa_family = 2; + tfa->spkr_count = 1; + tfa->is_probus_device = 1; + tfa->daimap = Tfa98xx_DAI_TDM; + tfa9872_ops(&tfa->dev_ops); /* register device operations */ + break; + case 0x74: + /* tfa9874 */ + tfa->supportDrc = supportYes; + tfa->tfa_family = 2; + tfa->spkr_count = 1; + tfa->is_probus_device = 1; + tfa->daimap = Tfa98xx_DAI_TDM; + tfa9874_ops(&tfa->dev_ops); /* register device operations */ + break; + case 0x88: + /* tfa9888 */ + tfa->tfa_family = 2; + tfa->spkr_count = 2; + tfa->daimap = Tfa98xx_DAI_TDM; + tfa9888_ops(&tfa->dev_ops); /* register device operations */ + break; + case 0x97: + /* tfa9897 */ + tfa->supportDrc = supportNo; + tfa->spkr_count = 1; + tfa->daimap = Tfa98xx_DAI_TDM; + tfa9897_ops(&tfa->dev_ops); /* register device operations */ + break; + case 0x96: + /* tfa9896 */ + tfa->supportDrc = supportNo; + tfa->spkr_count = 1; + tfa->daimap = Tfa98xx_DAI_TDM; + tfa9896_ops(&tfa->dev_ops); /* register device operations */ + break; + case 0x92: + /* tfa9891 */ + tfa->spkr_count = 1; + tfa->daimap = ( Tfa98xx_DAI_PDM | Tfa98xx_DAI_I2S ); + tfa9891_ops(&tfa->dev_ops); /* register device operations */ + break; + case 0x91: + /* tfa9890B */ + tfa->spkr_count = 1; + tfa->daimap = ( Tfa98xx_DAI_PDM | Tfa98xx_DAI_I2S ); + break; + case 0x80: + case 0x81: + /* tfa9890 */ + tfa->spkr_count = 1; + tfa->daimap = Tfa98xx_DAI_I2S; + tfa->supportDrc = supportNo; + tfa->supportFramework = supportNo; + tfa9890_ops(&tfa->dev_ops); /* register device operations */ + break; + case 0x12: + /* tfa9895 */ + tfa->spkr_count = 1; + tfa->daimap = Tfa98xx_DAI_I2S; + tfa9895_ops(&tfa->dev_ops); /* register device operations */ + break; + case 0x13: + /* tfa9912 */ + tfa->tfa_family = 2; + tfa->spkr_count = 1; + tfa->daimap = Tfa98xx_DAI_TDM; + tfa9912_ops(&tfa->dev_ops); /* register device operations */ + break; + case 0x94: + /* tfa9894 */ + tfa->tfa_family = 2; + tfa->spkr_count = 1; + tfa->daimap = Tfa98xx_DAI_TDM; + tfa9894_ops(&tfa->dev_ops); /* register device operations */ + break; + + default: + pr_err("unknown device type : 0x%02x\n", tfa->rev); + _ASSERT(0); + break; + } +} + +/* + * lookup the device type and return the family type + */ +int tfa98xx_dev2family(int dev_type) +{ + /* only look at the die ID part (lsb byte) */ + switch(dev_type & 0xff) { + case 0x12: + case 0x80: + case 0x81: + case 0x91: + case 0x92: + case 0x97: + case 0x96: + return 1; + case 0x88: + case 0x72: + case 0x13: + case 0x74: + case 0x94: + return 2; + case 0x50: + return 3; + default: + return 0; + } +} + +/* + * return the target address for the filter on this device + + filter_index: + [0..9] reserved for EQ (not deployed, calc. is available) + [10..12] anti-alias filter + [13] integrator filter + + */ +enum Tfa98xx_DMEM tfa98xx_filter_mem(struct tfa_device *tfa, int filter_index, unsigned short *address, int channel) +{ + enum Tfa98xx_DMEM dmem=-1; + int idx; + unsigned short bq_table[7][4] ={ + /* index: 10, 11, 12, 13 */ + {346,351,356,288}, //87 BRA_MAX_MRA4-2_7.00 + {346,351,356,288}, //90 BRA_MAX_MRA6_9.02 + {467,472,477,409}, //95 BRA_MAX_MRA7_10.02 + {406,411,416,348}, //97 BRA_MAX_MRA9_12.01 + {467,472,477,409}, //91 BRA_MAX_MRAA_13.02 + {8832, 8837, 8842, 8847}, //88 part1 + {8853, 8858, 8863, 8868} //88 part2 + /* Since the 88 is stereo we have 2 parts. + * Every index has 5 values except index 13 this one has 6 values + */ + }; + + if ( (10 <= filter_index) && (filter_index <= 13) ) { + dmem = Tfa98xx_DMEM_YMEM; /* for all devices */ + idx = filter_index-10; + + switch (tfa->rev & 0xff ) { // only compare lower byte + case 0x12: + *address = bq_table[2][idx]; + break; + case 0x97: + *address = bq_table[3][idx]; + break; + case 0x96: + *address = bq_table[3][idx]; + break; + case 0x80: + case 0x81: // for the RAM version + case 0x91: + *address = bq_table[1][idx]; + break; + case 0x92: + *address = bq_table[4][idx]; + break; + case 0x88: + /* Channel 1 = primary, 2 = secondary */ + if(channel == 1) + *address = bq_table[5][idx]; + else + *address = bq_table[6][idx]; + break; + case 0x72: + case 0x74: + case 0x13: + default: + /* unsupported case, possibly intermediate version */ + return -1; + _ASSERT(0); + } + } + return dmem; +} + +/************************ query functions ********************************************************/ +/** +* return revision +* Used by the LTT +*/ +void tfa98xx_rev(int *major, int *minor, int *revision) +{ + char version_str[] = TFA98XX_API_REV_STR; + sscanf(version_str, "v%d.%d.%d", major, minor, revision); +} + +/** + * tfa_supported_speakers + * returns the number of the supported speaker count + */ +enum Tfa98xx_Error tfa_supported_speakers(struct tfa_device *tfa, int* spkr_count) +{ + if (tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + else + *spkr_count = tfa->spkr_count; + + return Tfa98xx_Error_Ok; +} + +/* + * tfa98xx_supported_saam + * returns the supportedspeaker as microphone feature + */ +enum Tfa98xx_Error tfa98xx_supported_saam(struct tfa_device *tfa, enum Tfa98xx_saam *saam) +{ + int features; + enum Tfa98xx_Error error; + + if (tfa->support_saam == supportNotSet) { + error = tfa98xx_dsp_get_hw_feature_bits(tfa, &features); + if (error!=Tfa98xx_Error_Ok) + return error; + tfa->support_saam = + (features & 0x8000)? supportYes : supportNo; /* SAAM is bit15 */ + } + *saam = tfa->support_saam == supportYes ? Tfa98xx_saam : Tfa98xx_saam_none ; + + return Tfa98xx_Error_Ok; +} + +/* + * tfa98xx_compare_features + * Obtains features_from_MTP and features_from_cnt + */ +enum Tfa98xx_Error tfa98xx_compare_features(struct tfa_device *tfa, int features_from_MTP[3], int features_from_cnt[3]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + uint32_t value; + uint16_t mtpbf; + unsigned char bytes[3 * 2]; + int status; + + tfa98xx_dsp_system_stable(tfa, &status); + if (!status) + return Tfa98xx_Error_NoClock; // Only test when we have a clock. + + /* Set proper MTP location per device: */ + if (tfa->tfa_family == 1) { + mtpbf=0x850f; /* MTP5 for tfa1,16 bits */ + } else { + mtpbf=0xf907; /* MTP9 for tfa2, 8 bits */ + } + + /* Read HW features from MTP: */ + value = tfa_read_reg(tfa, mtpbf) & 0xffff; + features_from_MTP[0] = tfa->hw_feature_bits = value; + + /* Read SW features: */ + error = tfa_dsp_cmd_id_write_read(tfa, MODULE_FRAMEWORK, FW_PAR_ID_GET_FEATURE_INFO, sizeof(bytes), bytes); + if (error != Tfa98xx_Error_Ok) + return error; /* old ROM code may respond with Tfa98xx_Error_RpcParamId */ + + tfa98xx_convert_bytes2data(sizeof(bytes), bytes, &features_from_MTP[1]); + + /* check if feature bits from MTP match feature bits from cnt file: */ + get_hw_features_from_cnt(tfa, &features_from_cnt[0]); + get_sw_features_from_cnt(tfa, &features_from_cnt[1]); + + return error; +} + +/********************************* device specific ops ************************************************/ +/* the wrapper for DspReset, in case of full */ +enum Tfa98xx_Error tfa98xx_dsp_reset(struct tfa_device *tfa, int state) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + error = (tfa->dev_ops.dsp_reset)(tfa, state); + + return error; +} + +/* the ops wrapper for tfa98xx_dsp_SystemStable */ +enum Tfa98xx_Error tfa98xx_dsp_system_stable(struct tfa_device *tfa, int *ready) +{ + return (tfa->dev_ops.dsp_system_stable)(tfa, ready); +} + +/* the ops wrapper for tfa98xx_dsp_system_stable */ +enum Tfa98xx_Error tfa98xx_auto_copy_mtp_to_iic(struct tfa_device *tfa) +{ + return (tfa->dev_ops.auto_copy_mtp_to_iic)(tfa); +} + +/* the ops wrapper for tfa98xx_faim_protect */ +enum Tfa98xx_Error tfa98xx_faim_protect(struct tfa_device *tfa, int state) +{ + return (tfa->dev_ops.faim_protect)(tfa, state); +} + +/* + * bring the device into a state similar to reset + */ +enum Tfa98xx_Error tfa98xx_init(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + uint16_t value=0; + + /* reset all i2C registers to default + * Write the register directly to avoid the read in the bitfield function. + * The I2CR bit may overwrite the full register because it is reset anyway. + * This will save a reg read transaction. + */ + TFA_SET_BF_VALUE(tfa, I2CR, 1, &value ); + TFA_WRITE_REG(tfa, I2CR, value); + + /* Put DSP in reset */ + tfa98xx_dsp_reset(tfa, 1); /* in pair of tfaRunStartDSP() */ + + /* some other registers must be set for optimal amplifier behaviour + * This is implemented in a file specific for the type number + */ + if (tfa->dev_ops.tfa_init) + error = (tfa->dev_ops.tfa_init)(tfa); + + return error; +} + +enum Tfa98xx_Error tfa98xx_dsp_write_tables(struct tfa_device *tfa, int sample_rate) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + error = (tfa->dev_ops.dsp_write_tables)(tfa, sample_rate); + + return error; +} + +/** Set internal oscillator into power down mode. +* +* @param[in] tfa device description structure +* @param[in] state new state 0 - oscillator is on, 1 oscillator is off. +* +* @return Tfa98xx_Error_Ok when successfull, error otherwise. +*/ +enum Tfa98xx_Error tfa98xx_set_osc_powerdown(struct tfa_device *tfa, int state) +{ + if (tfa->dev_ops.set_osc_powerdown) { + return tfa->dev_ops.set_osc_powerdown(tfa, state); + } + + return Tfa98xx_Error_Not_Implemented; +} + +/** Check presence of powerswitch=1 in configuration and optimal setting. +* +* @param[in] tfa device description structure +* +* @return -1 when error, 0 or 1 depends on switch settings. +*/ +int tfa98xx_powerswitch_is_enabled(struct tfa_device *tfa) +{ + uint16_t value; + enum Tfa98xx_Error ret; + + if (((tfa->rev & 0xff) == 0x13) || ((tfa->rev & 0xff) == 0x88)) { + ret = reg_read(tfa, 0xc6, &value); + if (ret != Tfa98xx_Error_Ok) { + return -1; + } + /* PLMA5539: Check actual value of powerswitch. TODO: regmap v1.40 should make this bit public. */ + + return (int)(value & (1u << 6)); + } + + return 1; +} + +/********************* new tfa2 *********************************************************************/ +/* newly added messaging for tfa2 tfa1? */ +enum Tfa98xx_Error tfa98xx_dsp_get_memory(struct tfa_device *tfa, int memoryType, + int offset, int length, unsigned char bytes[]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + char msg[4*3]; + int nr = 0; + + msg[nr++] = 8; + msg[nr++] = MODULE_FRAMEWORK + 128; + msg[nr++] = FW_PAR_ID_GET_MEMORY; + + msg[nr++] = 0; + msg[nr++] = 0; + msg[nr++] = (char)memoryType; + + msg[nr++] = 0; + msg[nr++] = (offset>>8) & 0xff; + msg[nr++] = offset & 0xff; + + msg[nr++] = 0; + msg[nr++] = (length>>8) & 0xff; + msg[nr++] = length & 0xff; + + /* send msg */ + error = dsp_msg(tfa, nr, (char *)msg); + + if (error != Tfa98xx_Error_Ok) + return error; + + /* read the data from the device (length * 3) */ + error = dsp_msg_read(tfa, length * 3, bytes); + + return error; +} + +enum Tfa98xx_Error tfa98xx_dsp_set_memory(struct tfa_device *tfa, int memoryType, + int offset, int length, int value) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int nr = 0; + char msg[5*3]; + + msg[nr++] = 8; + msg[nr++] = MODULE_FRAMEWORK + 128; + msg[nr++] = FW_PAR_ID_SET_MEMORY; + + msg[nr++] = 0; + msg[nr++] = 0; + msg[nr++] = (char)memoryType; + + msg[nr++] = 0; + msg[nr++] = (offset>>8) & 0xff; + msg[nr++] = offset & 0xff; + + msg[nr++] = 0; + msg[nr++] = (length>>8) & 0xff; + msg[nr++] = length & 0xff; + + msg[nr++] = (value>>16) & 0xff; + msg[nr++] = (value>>8) & 0xff; + msg[nr++] = value & 0xff; + + /* send msg */ + error = dsp_msg(tfa, nr, (char *)msg); + + return error; +} +/****************************** calibration support **************************/ +/* + * get/set the mtp with user controllable values + * + * check if the relevant clocks are available + */ +enum Tfa98xx_Error tfa98xx_get_mtp(struct tfa_device *tfa, uint16_t *value) +{ + int status; + int result; + + /* not possible if PLL in powerdown */ + if ( TFA_GET_BF(tfa, PWDN) ) { + pr_debug("PLL in powerdown\n"); + return Tfa98xx_Error_NoClock; + } + + tfa98xx_dsp_system_stable(tfa, &status); + if (status==0) { + pr_debug("PLL not running\n"); + return Tfa98xx_Error_NoClock; + } + + result = TFA_READ_REG(tfa, MTP0); + if (result < 0) { + return -result; + } + *value = (uint16_t)result; + + return Tfa98xx_Error_Ok; +} + +/* + * lock or unlock KEY2 + * lock = 1 will lock + * lock = 0 will unlock + * + * note that on return all the hidden key will be off + */ +void tfa98xx_key2(struct tfa_device *tfa, int lock) +{ + /* unhide lock registers */ + reg_write(tfa, (tfa->tfa_family == 1) ? 0x40 :0x0F, 0x5A6B); + /* lock/unlock key2 MTPK */ + TFA_WRITE_REG(tfa, MTPKEY2, lock? 0 :0x5A ); + /* unhide lock registers */ + reg_write(tfa, (tfa->tfa_family == 1) ? 0x40 :0x0F, 0); +} + +enum Tfa98xx_Error tfa98xx_set_mtp(struct tfa_device *tfa, uint16_t value, uint16_t mask) +{ + unsigned short mtp_old, mtp_new; + int loop, status; + enum Tfa98xx_Error error; + + error = tfa98xx_get_mtp(tfa, &mtp_old); + + if (error != Tfa98xx_Error_Ok) + return error; + + mtp_new = (value & mask) | (mtp_old & ~mask); + + if ( mtp_old == mtp_new) /* no change */ { + if (tfa->verbose) + pr_info("No change in MTP. Value not written! \n"); + return Tfa98xx_Error_Ok; + } + + /* Assure FAIM is enabled (enable it when neccesery) */ + error = tfa98xx_faim_protect(tfa, 1); + if (error) { + return error; + } + if (tfa->verbose) { + pr_debug("MTP clock enabled.\n"); + } + + /* assure that the clock is up, else we can't write MTP */ + error = tfa98xx_dsp_system_stable(tfa, &status); + if (error){ + return error; + } + if (status==0){ + return Tfa98xx_Error_NoClock; + } + + tfa98xx_key2(tfa, 0); /* unlock */ + TFA_WRITE_REG(tfa, MTP0, mtp_new); /* write to i2c shadow reg */ + /* CIMTP=1 start copying all the data from i2c regs_mtp to mtp*/ + TFA_SET_BF(tfa, CIMTP, 1); + /* no check for MTPBUSY here, i2c delay assumed to be enough */ + tfa98xx_key2(tfa, 1); /* lock */ + + /* wait until MTP write is done */ + error = Tfa98xx_Error_StateTimedOut; + for(loop=0; loop<100 /*x10ms*/ ;loop++) { + msleep_interruptible(10); /* wait 10ms to avoid busload */ + if (tfa_dev_get_mtpb(tfa) == 0) { + error = Tfa98xx_Error_Ok; + break; + } + } + /* MTP setting failed due to timeout ?*/ + if (error) { + return error; + } + + /* Disable the FAIM, if this is neccessary */ + error = tfa98xx_faim_protect(tfa, 0); + if (error) { + return error; + } + if (tfa->verbose) { + pr_debug("MTP clock disabled.\n"); + } + + return error; +} +/* + * clear mtpex + * set ACS + * start tfa + */ +int tfa_calibrate(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error; + + /* clear mtpex */ + error = tfa98xx_set_mtp(tfa, 0, TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK); + if (error) + return error ; + + /* set RST=1 to put the DSP in Reset */ + TFA_SET_BF(tfa, RST, 1); + + /* set ACS/coldboot state */ + error = tfaRunColdboot(tfa, 1); + + /* start tfa by playing */ + return error; +} + +static short twos(short x) +{ + return (x<0)? x+512 : x; +} + +void tfa98xx_set_exttemp(struct tfa_device *tfa, short ext_temp) +{ + if ((-256 <= ext_temp) && (ext_temp <= 255)) { + /* make twos complement */ + pr_debug("Using ext temp %d C\n", twos(ext_temp)); + TFA_SET_BF(tfa, TROS, 1); + TFA_SET_BF(tfa, EXTTS, twos(ext_temp)); + } else { + pr_debug("Clearing ext temp settings\n"); + TFA_SET_BF(tfa, TROS, 0); + } +} +short tfa98xx_get_exttemp(struct tfa_device *tfa) +{ + short ext_temp = (short)TFA_GET_BF(tfa, EXTTS); + return (twos(ext_temp)); +} + +/************************** tfa simple bitfield interfacing ***************************************/ +/* convenience functions */ +enum Tfa98xx_Error tfa98xx_set_volume_level(struct tfa_device *tfa, unsigned short vol) +{ + if(tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + if (vol > 255) /* restricted to 8 bits */ + vol = 255; + + /* 0x00 -> 0.0 dB + * 0x01 -> -0.5 dB + * ... + * 0xFE -> -127dB + * 0xFF -> muted + */ + + /* volume value is in the top 8 bits of the register */ + return -TFA_SET_BF(tfa, VOL, (uint16_t)vol); +} + +static enum Tfa98xx_Error +tfa98xx_set_mute_tfa2(struct tfa_device *tfa, enum Tfa98xx_Mute mute) +{ + enum Tfa98xx_Error error; + + if (tfa->dev_ops.set_mute == NULL) + return Tfa98xx_Error_Not_Supported; + + switch (mute) { + case Tfa98xx_Mute_Off: + error = tfa->dev_ops.set_mute(tfa, 0); + break; + case Tfa98xx_Mute_Amplifier: + case Tfa98xx_Mute_Digital: + error = tfa->dev_ops.set_mute(tfa, 1); + break; + default: + return Tfa98xx_Error_Bad_Parameter; + } + + return error; +} + +static enum Tfa98xx_Error +tfa98xx_set_mute_tfa1(struct tfa_device *tfa, enum Tfa98xx_Mute mute) +{ + enum Tfa98xx_Error error; + unsigned short audioctrl_value; + unsigned short sysctrl_value; + int value; + + value = TFA_READ_REG(tfa, CFSM); /* audio control register */ + if (value < 0) + return -value; + audioctrl_value = (unsigned short)value; + value = TFA_READ_REG(tfa, AMPE); /* system control register */ + if (value < 0) + return -value; + sysctrl_value = (unsigned short)value; + + switch (mute) { + case Tfa98xx_Mute_Off: + /* previous state can be digital or amplifier mute, + * clear the cf_mute and set the enbl_amplifier bits + * + * To reduce PLOP at power on it is needed to switch the + * amplifier on with the DCDC in follower mode + * (enbl_boost = 0 ?). + * This workaround is also needed when toggling the + * powerdown bit! + */ + TFA_SET_BF_VALUE(tfa, CFSM, 0, &audioctrl_value); + TFA_SET_BF_VALUE(tfa, AMPE, 1, &sysctrl_value); + TFA_SET_BF_VALUE(tfa, DCA, 1, &sysctrl_value); + break; + case Tfa98xx_Mute_Digital: + /* expect the amplifier to run */ + /* set the cf_mute bit */ + TFA_SET_BF_VALUE(tfa, CFSM, 1, &audioctrl_value); + /* set the enbl_amplifier bit */ + TFA_SET_BF_VALUE(tfa, AMPE, 1, &sysctrl_value); + /* clear active mode */ + TFA_SET_BF_VALUE(tfa, DCA, 0, &sysctrl_value); + break; + case Tfa98xx_Mute_Amplifier: + /* clear the cf_mute bit */ + TFA_SET_BF_VALUE(tfa, CFSM, 0, &audioctrl_value); + /* clear the enbl_amplifier bit and active mode */ + TFA_SET_BF_VALUE(tfa, AMPE, 0, &sysctrl_value); + TFA_SET_BF_VALUE(tfa, DCA, 0, &sysctrl_value); + break; + default: + return Tfa98xx_Error_Bad_Parameter; + } + + error = -TFA_WRITE_REG(tfa, CFSM, audioctrl_value); + if (error) + return error; + error = -TFA_WRITE_REG(tfa, AMPE, sysctrl_value); + return error; +} + +enum Tfa98xx_Error +tfa98xx_set_mute(struct tfa_device *tfa, enum Tfa98xx_Mute mute) +{ + if (tfa->in_use == 0) { + pr_err("device is not opened \n"); + return Tfa98xx_Error_NotOpen; + } + + if (tfa->tfa_family == 1) + return tfa98xx_set_mute_tfa1(tfa, mute); + else + return tfa98xx_set_mute_tfa2(tfa, mute); +} + +/****************** patching **********************************************************/ +static enum Tfa98xx_Error +tfa98xx_process_patch_file(struct tfa_device *tfa, int length, + const unsigned char *bytes) +{ + unsigned short size; + int index = 0; + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + while (index < length) { + size = bytes[index] + bytes[index + 1] * 256; + index += 2; + if ((index + size) > length) { + /* outside the buffer, error in the input data */ + return Tfa98xx_Error_Bad_Parameter; + } + + if (size > tfa->buffer_size) { + /* too big, must fit buffer */ + return Tfa98xx_Error_Bad_Parameter; + } + + error = tfa98xx_write_raw(tfa, size, &bytes[index]); + if (error != Tfa98xx_Error_Ok) + break; + index += size; + } + return error; +} + + + +/* the patch contains a header with the following + * IC revision register: 1 byte, 0xFF means don't care + * XMEM address to check: 2 bytes, big endian, 0xFFFF means don't care + * XMEM value to expect: 3 bytes, big endian + */ +static enum Tfa98xx_Error +tfa98xx_check_ic_rom_version(struct tfa_device *tfa, const unsigned char patchheader[]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short checkrev, revid; + unsigned char lsb_revid; + unsigned short checkaddress; + int checkvalue; + int value = 0; + int status; + checkrev = patchheader[0]; + lsb_revid = tfa->rev & 0xff; /* only compare lower byte */ + + if ((checkrev != 0xFF) && (checkrev != lsb_revid)) + return Tfa98xx_Error_Not_Supported; + + checkaddress = (patchheader[1] << 8) + patchheader[2]; + checkvalue = + (patchheader[3] << 16) + (patchheader[4] << 8) + patchheader[5]; + if (checkaddress != 0xFFFF) { + /* before reading XMEM, check if we can access the DSP */ + error = tfa98xx_dsp_system_stable(tfa, &status); + if (error == Tfa98xx_Error_Ok) { + if (!status) { + /* DSP subsys not running */ + error = Tfa98xx_Error_DSP_not_running; + } + } + /* read register to check the correct ROM version */ + if (error == Tfa98xx_Error_Ok) { + error = mem_read(tfa, checkaddress, 1, &value); + } + if (error == Tfa98xx_Error_Ok) { + if (value != checkvalue) { + pr_err("patch file romid type check failed [0x%04x]: expected 0x%02x, actual 0x%02x\n", + checkaddress, value, checkvalue); + error = Tfa98xx_Error_Not_Supported; + } + } + } else { /* == 0xffff */ + /* check if the revid subtype is in there */ + if ( checkvalue != 0xFFFFFF && checkvalue != 0) { + revid = patchheader[5]<<8 | patchheader[0]; /* full revid */ + if ( revid != tfa->rev) { + pr_err("patch file device type check failed: expected 0x%02x, actual 0x%02x\n", + tfa->rev, revid); + return Tfa98xx_Error_Not_Supported; + } + } + } + + return error; +} + + +#define PATCH_HEADER_LENGTH 6 +enum Tfa98xx_Error +tfa_dsp_patch(struct tfa_device *tfa, int patchLength, + const unsigned char *patchBytes) +{ + enum Tfa98xx_Error error; + if(tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + if (patchLength < PATCH_HEADER_LENGTH) + return Tfa98xx_Error_Bad_Parameter; + + error = tfa98xx_check_ic_rom_version(tfa, patchBytes); + if (Tfa98xx_Error_Ok != error) { + return error; + } + error = + tfa98xx_process_patch_file(tfa, patchLength - PATCH_HEADER_LENGTH, + patchBytes + PATCH_HEADER_LENGTH); + + return error; +} + +/****************** end patching **********************************************************/ + +TFA_INTERNAL enum Tfa98xx_Error +tfa98xx_wait_result(struct tfa_device *tfa, int wait_retry_count) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int cf_status; /* the contents of the CF_STATUS register */ + int tries = 0; + do { + cf_status = TFA_GET_BF(tfa, ACK); + if (cf_status < 0) + error = -cf_status; + tries++; + } + // i2c_cmd_ack + /* don't wait forever, DSP is pretty quick to respond (< 1ms) */ + while ((error == Tfa98xx_Error_Ok) && ((cf_status & CF_STATUS_I2C_CMD_ACK) == 0) + && (tries < wait_retry_count)); + if (tries >= wait_retry_count) { + /* something wrong with communication with DSP */ + error = Tfa98xx_Error_DSP_not_running; + } + return error; +} + +/* + * * support functions for data conversion + */ +/** + convert memory bytes to signed 24 bit integers + input: bytes contains "num_bytes" byte elements + output: data contains "num_bytes/3" int24 elements +*/ +void tfa98xx_convert_bytes2data(int num_bytes, const unsigned char bytes[], + int data[]) +{ + int i; /* index for data */ + int k; /* index for bytes */ + int d; + int num_data = num_bytes / 3; + _ASSERT((num_bytes % 3) == 0); + for (i = 0, k = 0; i < num_data; ++i, k += 3) { + d = (bytes[k] << 16) | (bytes[k + 1] << 8) | (bytes[k + 2]); + _ASSERT(d >= 0); + _ASSERT(d < (1 << 24)); /* max 24 bits in use */ + if (bytes[k] & 0x80) /* sign bit was set */ + d = -((1 << 24) - d); + + data[i] = d; + } +} + + +/** + convert signed 32 bit integers to 24 bit aligned bytes + input: data contains "num_data" int elements + output: bytes contains "3 * num_data" byte elements +*/ +void tfa98xx_convert_data2bytes(int num_data, const int data[], + unsigned char bytes[]) +{ + int i; /* index for data */ + int k; /* index for bytes */ + int d; + /* note: cannot just take the lowest 3 bytes from the 32 bit + * integer, because also need to take care of clipping any + * value > 2&23 */ + for (i = 0, k = 0; i < num_data; ++i, k += 3) { + if (data[i] >= 0) + d = MIN(data[i], (1 << 23) - 1); + else { + /* 2's complement */ + d = (1 << 24) - MIN(-data[i], 1 << 23); + } + _ASSERT(d >= 0); + _ASSERT(d < (1 << 24)); /* max 24 bits in use */ + bytes[k] = (d >> 16) & 0xFF; /* MSB */ + bytes[k + 1] = (d >> 8) & 0xFF; + bytes[k + 2] = (d) & 0xFF; /* LSB */ + } +} + +/* + * DSP RPC message support functions + * depending on framework to be up and running + * need base i2c of memaccess (tfa1=0x70/tfa2=0x90) + */ + + +/* write dsp messages in function tfa_dsp_msg() */ +/* note the 'old' write_parameter() was more efficient because all i2c was in one burst transaction */ + +//TODO properly handle bitfields: state should be restored! (now it will change eg dmesg field to xmem) +enum Tfa98xx_Error tfa_dsp_msg_write(struct tfa_device *tfa, int length, const char *buffer) +{ + int offset = 0; + int chunk_size = ROUND_DOWN(tfa->buffer_size, 3); /* XMEM word size */ + int remaining_bytes = length; + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + uint16_t cfctl; + int value; + + value = TFA_READ_REG(tfa, DMEM); + if (value < 0) { + error = -value; + return error; + } + cfctl = (uint16_t)value; + /* assume no I2C errors from here */ + + TFA_SET_BF_VALUE(tfa, DMEM, (uint16_t)Tfa98xx_DMEM_XMEM, &cfctl); /* set cf ctl to DMEM */ + TFA_SET_BF_VALUE(tfa, AIF, 0, &cfctl ); /* set to autoincrement */ + TFA_WRITE_REG(tfa, DMEM, cfctl); + + /* xmem[1] is start of message + * direct write to register to save cycles avoiding read-modify-write + */ + TFA_WRITE_REG(tfa, MADD, 1); + + /* due to autoincrement in cf_ctrl, next write will happen at + * the next address */ + while ((error == Tfa98xx_Error_Ok) && (remaining_bytes > 0)) { + if (remaining_bytes < chunk_size) + chunk_size = remaining_bytes; + /* else chunk_size remains at initialize value above */ + error = tfa98xx_write_data(tfa, FAM_TFA98XX_CF_MEM, + chunk_size, (const unsigned char *)buffer + offset); + remaining_bytes -= chunk_size; + offset += chunk_size; + } + + /* notify the DSP */ + if (error == Tfa98xx_Error_Ok) { + /* cf_int=0, cf_aif=0, cf_dmem=XMEM=01, cf_rst_dsp=0 */ + /* set the cf_req1 and cf_int bit */ + TFA_SET_BF_VALUE(tfa, REQCMD, 0x01, &cfctl ); /* bit 0 */ + TFA_SET_BF_VALUE(tfa, CFINT, 1, &cfctl ); + error = -TFA_WRITE_REG(tfa, CFINT, cfctl); + } + + return error; +} + +enum Tfa98xx_Error tfa_dsp_msg_write_id(struct tfa_device *tfa, int length, const char *buffer, uint8_t cmdid[3]) +{ + int offset = 0; + int chunk_size = ROUND_DOWN(tfa->buffer_size, 3); /* XMEM word size */ + int remaining_bytes = length; + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + uint16_t cfctl; + int value; + + value = TFA_READ_REG(tfa, DMEM); + if (value < 0) { + error = -value; + return error; + } + cfctl = (uint16_t)value; + /* assume no I2C errors from here */ + + TFA_SET_BF_VALUE(tfa, DMEM, (uint16_t)Tfa98xx_DMEM_XMEM, &cfctl); /* set cf ctl to DMEM */ + TFA_SET_BF_VALUE(tfa, AIF, 0, &cfctl ); /* set to autoincrement */ + TFA_WRITE_REG(tfa, DMEM, cfctl); + + /* xmem[1] is start of message + * direct write to register to save cycles avoiding read-modify-write + */ + TFA_WRITE_REG(tfa, MADD, 1); + + /* write cmd-id */ + error = tfa98xx_write_data(tfa, FAM_TFA98XX_CF_MEM, 3, (const unsigned char *)cmdid); + + /* due to autoincrement in cf_ctrl, next write will happen at + * the next address */ + while ((error == Tfa98xx_Error_Ok) && (remaining_bytes > 0)) { + if (remaining_bytes < chunk_size) + chunk_size = remaining_bytes; + /* else chunk_size remains at initialize value above */ + error = tfa98xx_write_data(tfa, FAM_TFA98XX_CF_MEM, + chunk_size, (const unsigned char *)buffer + offset); + remaining_bytes -= chunk_size; + offset += chunk_size; + } + + /* notify the DSP */ + if (error == Tfa98xx_Error_Ok) { + /* cf_int=0, cf_aif=0, cf_dmem=XMEM=01, cf_rst_dsp=0 */ + /* set the cf_req1 and cf_int bit */ + TFA_SET_BF_VALUE(tfa, REQCMD, 0x01, &cfctl ); /* bit 0 */ + TFA_SET_BF_VALUE(tfa, CFINT, 1, &cfctl ); + error = -TFA_WRITE_REG(tfa, CFINT, cfctl); + } + + return error; +} + +/* +* status function used by tfa_dsp_msg() to retrieve command/msg status: +* return a <0 status of the DSP did not ACK. +*/ +enum Tfa98xx_Error tfa_dsp_msg_status(struct tfa_device *tfa, int *pRpcStatus) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + error = tfa98xx_wait_result(tfa, 2); /* 2 is only one try */ + if (error == Tfa98xx_Error_DSP_not_running) { + *pRpcStatus = -1; + return Tfa98xx_Error_Ok; + } + else if (error != Tfa98xx_Error_Ok) + return error; + + error = tfa98xx_check_rpc_status(tfa, pRpcStatus); + + return error; +} + +const char* tfa98xx_get_i2c_status_id_string(int status) +{ + const char* p_id_str; + + switch (status) + { + case Tfa98xx_DSP_Not_Running: + p_id_str = "No response from DSP"; + break; + case Tfa98xx_I2C_Req_Done: + p_id_str = "Ok"; + break; + case Tfa98xx_I2C_Req_Busy: + p_id_str = "Request is being processed"; + break; + case Tfa98xx_I2C_Req_Invalid_M_ID: + p_id_str = "Provided M-ID does not fit in valid rang [0..2]"; + break; + case Tfa98xx_I2C_Req_Invalid_P_ID: + p_id_str = "Provided P-ID is not valid in the given M-ID context"; + break; + case Tfa98xx_I2C_Req_Invalid_CC: + p_id_str = "Invalid channel configuration bits (SC|DS|DP|DC) combination"; + break; + case Tfa98xx_I2C_Req_Invalid_Seq: + p_id_str = "Invalid sequence of commands, in case the DSP expects some commands in a specific order"; + break; + case Tfa98xx_I2C_Req_Invalid_Param: + p_id_str = "Generic error, invalid parameter"; + break; + case Tfa98xx_I2C_Req_Buffer_Overflow: + p_id_str = "I2C buffer has overflowed: host has sent too many parameters, memory integrity is not guaranteed"; + break; + case Tfa98xx_I2C_Req_Calib_Busy: + p_id_str = "Calibration not completed"; + break; + case Tfa98xx_I2C_Req_Calib_Failed: + p_id_str = "Calibration failed"; + break; + + default: + p_id_str = "Unspecified error"; + } + + return p_id_str; +} + +enum Tfa98xx_Error tfa_dsp_msg_read(struct tfa_device *tfa, int length, unsigned char *bytes) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int burst_size; /* number of words per burst size */ + int bytes_per_word = 3; + int num_bytes; + int offset = 0; + unsigned short start_offset=2; /* msg starts @xmem[2] ,[1]=cmd */ + + if ( length > TFA2_MAX_PARAM_SIZE) + return Tfa98xx_Error_Bad_Parameter; + + TFA_SET_BF(tfa, DMEM, (uint16_t)Tfa98xx_DMEM_XMEM); + error = -TFA_WRITE_REG(tfa, MADD, start_offset); + if (error != Tfa98xx_Error_Ok) + return error; + + num_bytes = length; /* input param */ + while (num_bytes > 0) { + burst_size = ROUND_DOWN(tfa->buffer_size, bytes_per_word); + if (num_bytes < burst_size) + burst_size = num_bytes; + error = tfa98xx_read_data(tfa, FAM_TFA98XX_CF_MEM, burst_size, bytes + offset); + if (error != Tfa98xx_Error_Ok) + return error; + + num_bytes -= burst_size; + offset += burst_size; + } + + return error; +} + +enum Tfa98xx_Error dsp_msg(struct tfa_device *tfa, int length24, const char *buf24) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int lastmessage=0; + uint8_t *blob; + int i; + int *intbuf = NULL; + char* buf = (char *)buf24; + int length = length24; + + if (tfa->convert_dsp32) { + int idx = 0; + + length = 4 * length24 / 3; + intbuf = kmem_cache_alloc(tfa->cachep, GFP_KERNEL); + buf = (char *)intbuf; + + /* convert 24 bit DSP messages to a 32 bit integer */ + for (i=0; i> 8; + } + } + + /* Only create multi-msg when the dsp is cold */ + if(tfa->ext_dsp == 1) { + /* Creating the multi-msg */ + error = tfa_tib_dsp_msgmulti(tfa, length, buf); + if(error == Tfa98xx_Error_Fail) + return Tfa98xx_Error_Fail; + + /* if the buffer is full we need to send the existing message and add the current message */ + if(error == Tfa98xx_Error_Buffer_too_small) { + int len; + + /* (a) send the existing (full) message */ + blob = kmalloc(64*1024, GFP_KERNEL); // max length is 64k + len = tfa_tib_dsp_msgmulti(tfa, -1, (const char*)blob); + if (tfa->verbose) { + pr_debug("Multi-message buffer full. Sending multi-message, length=%d \n", len); + } + if (tfa->has_msg==0 ) /* via i2c */ { + /* Send tot the target selected */ + error = (tfa->dev_ops.dsp_msg)(tfa, len, (const char*)blob); + } else { /* via msg hal */ + error = tfa98xx_write_dsp(tfa, len, (const char*)blob); + } + kfree(blob); + + /* (b) add the current DSP message to a new multi-message */ + error = tfa_tib_dsp_msgmulti(tfa, length, buf); + if(error == Tfa98xx_Error_Fail) { + return Tfa98xx_Error_Fail; + } + } + + lastmessage = error; + + /* When the lastmessage is done we can send the multi-msg to the target */ + if(lastmessage == 1) { + + /* Get the full multi-msg data */ + blob = kmalloc(64*1024, GFP_KERNEL); //max length is 64k + length = tfa_tib_dsp_msgmulti(tfa, -1, (const char*)blob); + + if (tfa->verbose) + pr_debug("Last message for the multi-message received. Multi-message length=%d \n", length); + + if (tfa->has_msg==0 ) /* via i2c */ { + /* Send tot the target selected */ + error = (tfa->dev_ops.dsp_msg)(tfa, length, (const char*)blob); + } else { /* via msg hal */ + error = tfa98xx_write_dsp(tfa, length, (const char*)blob); + } + + kfree(blob); /* Free the kmalloc blob */ + lastmessage = 0; /* reset to be able to re-start */ + } + } else { + if (tfa->has_msg==0 ) /* via i2c */ { + error = (tfa->dev_ops.dsp_msg)(tfa, length, buf); + } else { /* via msg hal */ + error = tfa98xx_write_dsp(tfa, length, (const char*)buf); + } + } + + if(error != Tfa98xx_Error_Ok) + error = (enum Tfa98xx_Error) (error + Tfa98xx_Error_RpcBase); /* Get actual error code from softDSP */ + + /* DSP verbose has argument 0x04 */ + if((tfa->verbose & 0x04)!=0) { + pr_debug("DSP w [%d]: ", length); + for(i=0; iconvert_dsp32) { + kmem_cache_free(tfa->cachep, intbuf); + } + + return error; +} + +enum Tfa98xx_Error dsp_msg_read(struct tfa_device *tfa, int length24, unsigned char *bytes24) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int i; + int length = length24; + unsigned char *bytes = bytes24; + + if (tfa->convert_dsp32) { + length = 4 * length24 / 3; + bytes = kmem_cache_alloc(tfa->cachep, GFP_KERNEL); + } + + if (tfa->has_msg==0) /* via i2c */ { + error = (tfa->dev_ops.dsp_msg_read)(tfa, length, bytes); + } else { /* via msg hal */ + error = tfa98xx_read_dsp(tfa, length, bytes); + } + + if(error != Tfa98xx_Error_Ok) + error = (enum Tfa98xx_Error) (error + Tfa98xx_Error_RpcBase); /* Get actual error code from softDSP */ + + /* DSP verbose has argument 0x04 */ + if((tfa->verbose & 0x04)!=0) { + pr_debug("DSP R [%d]: ", length); + for(i=0; iconvert_dsp32) { + int idx = 0; + + /* convert 32 bit LE to 24 bit BE */ + for (i=0; icachep, bytes); + } + + return error; +} + +enum Tfa98xx_Error reg_read(struct tfa_device *tfa, unsigned char subaddress, unsigned short *value) +{ + enum Tfa98xx_Error error; + + error = (tfa->dev_ops.reg_read)(tfa, subaddress, value); + if(error != Tfa98xx_Error_Ok) + error = (enum Tfa98xx_Error) (error + Tfa98xx_Error_RpcBase); /* Get actual error code from softDSP */ + + return error; +} + +enum Tfa98xx_Error reg_write(struct tfa_device *tfa, unsigned char subaddress, unsigned short value) +{ + enum Tfa98xx_Error error; + + error = (tfa->dev_ops.reg_write)(tfa, subaddress, value); + if(error != Tfa98xx_Error_Ok) + error = (enum Tfa98xx_Error) (error + Tfa98xx_Error_RpcBase); /* Get actual error code from softDSP */ + + return error; +} + +enum Tfa98xx_Error mem_read(struct tfa_device *tfa, unsigned int start_offset, int num_words, int *pValues) +{ + enum Tfa98xx_Error error; + + error = (tfa->dev_ops.mem_read)(tfa, start_offset, num_words, pValues); + if(error != Tfa98xx_Error_Ok) + error = (enum Tfa98xx_Error) (error + Tfa98xx_Error_RpcBase); /* Get actual error code from softDSP */ + + return error; +} + +enum Tfa98xx_Error mem_write(struct tfa_device *tfa, unsigned short address, int value, int memtype) +{ + enum Tfa98xx_Error error; + + error = (tfa->dev_ops.mem_write)(tfa, address, value, memtype); + if(error != Tfa98xx_Error_Ok) + error = (enum Tfa98xx_Error) (error + Tfa98xx_Error_RpcBase); /* Get actual error code from softDSP */ + + return error; +} + + +/* + * write/read raw msg functions : + * the buffer is provided in little endian format, each word occupying 3 bytes, length is in bytes. + * The functions will return immediately and do not not wait for DSP reponse. + */ +#define MAX_WORDS (300) +enum Tfa98xx_Error tfa_dsp_msg(struct tfa_device *tfa, int length, const char *buf) +{ + enum Tfa98xx_Error error; + int tries, rpc_status = Tfa98xx_I2C_Req_Done; + + /* write the message and notify the DSP */ + error = tfa_dsp_msg_write(tfa, length, buf); + if( error != Tfa98xx_Error_Ok) + return error; + + /* get the result from the DSP (polling) */ + for(tries=TFA98XX_WAITRESULT_NTRIES; tries>0;tries--) { + error = tfa_dsp_msg_status(tfa, &rpc_status); + if (error == Tfa98xx_Error_Ok && rpc_status == Tfa98xx_I2C_Req_Done) + break; + /* If the rpc status is a specific error we want to know it. + * If it is busy or not running it should retry + */ + if(rpc_status != Tfa98xx_I2C_Req_Busy && rpc_status != Tfa98xx_DSP_Not_Running) + break; + } + + if (rpc_status != Tfa98xx_I2C_Req_Done) { + /* DSP RPC call returned an error */ + error = (enum Tfa98xx_Error) (rpc_status + Tfa98xx_Error_RpcBase); + pr_debug("DSP msg status: %d (%s)\n", rpc_status, tfa98xx_get_i2c_status_id_string(rpc_status)); + } + return error; +} + +/** + * write/read raw msg functions: + * the buffer is provided in little endian format, each word occupying 3 bytes, length is in bytes. + * The functions will return immediately and do not not wait for DSP reponse. + * An ID is added to modify the command-ID + */ +enum Tfa98xx_Error tfa_dsp_msg_id(struct tfa_device *tfa, int length, const char *buf, uint8_t cmdid[3]) +{ + enum Tfa98xx_Error error; + int tries, rpc_status = Tfa98xx_I2C_Req_Done; + + /* write the message and notify the DSP */ + error = tfa_dsp_msg_write_id(tfa, length, buf, cmdid); + if( error != Tfa98xx_Error_Ok) + return error; + + /* get the result from the DSP (polling) */ + for(tries=TFA98XX_WAITRESULT_NTRIES; tries>0;tries--) { + error = tfa_dsp_msg_status(tfa, &rpc_status); + if (error == Tfa98xx_Error_Ok && rpc_status == Tfa98xx_I2C_Req_Done) + break; + } + + if (rpc_status != Tfa98xx_I2C_Req_Done) { + /* DSP RPC call returned an error */ + error = (enum Tfa98xx_Error) (rpc_status + Tfa98xx_Error_RpcBase); + pr_debug("DSP msg status: %d (%s)\n", rpc_status, tfa98xx_get_i2c_status_id_string(rpc_status)); + } + return error; +} + +/* read the return code for the RPC call */ +TFA_INTERNAL enum Tfa98xx_Error +tfa98xx_check_rpc_status(struct tfa_device *tfa, int *pRpcStatus) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + /* the value to sent to the * CF_CONTROLS register: cf_req=00000000, + * cf_int=0, cf_aif=0, cf_dmem=XMEM=01, cf_rst_dsp=0 */ + unsigned short cf_ctrl = 0x0002; + /* memory address to be accessed (0: Status, 1: ID, 2: parameters) */ + unsigned short cf_mad = 0x0000; + + if(tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + if (pRpcStatus == NULL) + return Tfa98xx_Error_Bad_Parameter; + + /* 1) write DMEM=XMEM to the DSP XMEM */ + { + /* minimize the number of I2C transactions by making use of the autoincrement in I2C */ + unsigned char buffer[4]; + /* first the data for CF_CONTROLS */ + buffer[0] = (unsigned char)((cf_ctrl >> 8) & 0xFF); + buffer[1] = (unsigned char)(cf_ctrl & 0xFF); + /* write the contents of CF_MAD which is the subaddress following CF_CONTROLS */ + buffer[2] = (unsigned char)((cf_mad >> 8) & 0xFF); + buffer[3] = (unsigned char)(cf_mad & 0xFF); + error = tfa98xx_write_data(tfa, FAM_TFA98XX_CF_CONTROLS, sizeof(buffer), buffer); + } + if (error == Tfa98xx_Error_Ok) { + /* read 1 word (24 bit) from XMEM */ + error = tfa98xx_dsp_read_mem(tfa, 0, 1, pRpcStatus); + } + + return error; +} + +/***************************** xmem only **********************************/ +enum Tfa98xx_Error +tfa98xx_dsp_read_mem(struct tfa_device *tfa, + unsigned int start_offset, int num_words, int *pValues) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned char *bytes; + int burst_size; /* number of words per burst size */ + const int bytes_per_word = 3; + int dmem; + int num_bytes; + int *p; + + bytes = (unsigned char *)kmem_cache_alloc(tfa->cachep, GFP_KERNEL); + if (bytes == NULL) + return Tfa98xx_Error_Fail; + + /* If no offset is given, assume XMEM! */ + if(((start_offset>>16) & 0xf) > 0 ) + dmem = (start_offset>>16) & 0xf; + else + dmem = Tfa98xx_DMEM_XMEM; + + /* Remove offset from adress */ + start_offset = start_offset & 0xffff; + num_bytes = num_words * bytes_per_word; + p = pValues; + + TFA_SET_BF(tfa, DMEM, (uint16_t)dmem); + error = -TFA_WRITE_REG(tfa, MADD, (unsigned short)start_offset); + if (error != Tfa98xx_Error_Ok) + goto tfa98xx_dsp_read_mem_exit; + + for (; num_bytes > 0;) { + burst_size = ROUND_DOWN(tfa->buffer_size, bytes_per_word); + if (num_bytes < burst_size) + burst_size = num_bytes; + + _ASSERT(burst_size <= sizeof(bytes)); + error = tfa98xx_read_data(tfa, FAM_TFA98XX_CF_MEM, burst_size, bytes); + if (error != Tfa98xx_Error_Ok) + goto tfa98xx_dsp_read_mem_exit; + + tfa98xx_convert_bytes2data(burst_size, bytes, p); + + num_bytes -= burst_size; + p += burst_size / bytes_per_word; + } + +tfa98xx_dsp_read_mem_exit: + kmem_cache_free(tfa->cachep, bytes); + + return error; +} + + +enum Tfa98xx_Error +tfa98xx_dsp_write_mem_word(struct tfa_device *tfa, unsigned short address, int value, int memtype) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned char bytes[3]; + + TFA_SET_BF(tfa, DMEM, (uint16_t)memtype); + + error = -TFA_WRITE_REG(tfa, MADD, address); + if (error != Tfa98xx_Error_Ok) + return error; + + tfa98xx_convert_data2bytes(1, &value, bytes); + error = tfa98xx_write_data(tfa, FAM_TFA98XX_CF_MEM, 3, bytes); + + return error; +} + +enum Tfa98xx_Error tfa_cont_write_filterbank(struct tfa_device *tfa, nxpTfaFilter_t *filter) +{ + unsigned char biquad_index; + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + for(biquad_index=0;biquad_index<10;biquad_index++) { + if (filter[biquad_index].enabled ) { + error = tfa_dsp_cmd_id_write(tfa, MODULE_BIQUADFILTERBANK, + biquad_index+1, //start @1 + sizeof(filter[biquad_index].biquad.bytes), + filter[biquad_index].biquad.bytes); + } else { + error = Tfa98xx_DspBiquad_Disable(tfa, biquad_index+1); + } + if (error) return error; + + } + + return error; +} + +enum Tfa98xx_Error +Tfa98xx_DspBiquad_Disable(struct tfa_device *tfa, int biquad_index) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int coeff_buffer[BIQUAD_COEFF_SIZE]; + unsigned char bytes[3 + BIQUAD_COEFF_SIZE * 3]; + int nr = 0; + + if (biquad_index > TFA98XX_BIQUAD_NUM) + return Tfa98xx_Error_Bad_Parameter; + if (biquad_index < 1) + return Tfa98xx_Error_Bad_Parameter; + + /* make opcode */ + bytes[nr++] = 0; + bytes[nr++] = MODULE_BIQUADFILTERBANK+128; + bytes[nr++] = (unsigned char)biquad_index; + + + /* set in correct order and format for the DSP */ + coeff_buffer[0] = (int) - 8388608; /* -1.0f */ + coeff_buffer[1] = 0; + coeff_buffer[2] = 0; + coeff_buffer[3] = 0; + coeff_buffer[4] = 0; + coeff_buffer[5] = 0; + + /* convert to packed 24 */ + tfa98xx_convert_data2bytes(BIQUAD_COEFF_SIZE, coeff_buffer, &bytes[nr]); + nr += BIQUAD_COEFF_SIZE * 3; + + error = dsp_msg(tfa, nr, (char *)bytes); + + return error; +} + +/* wrapper for dsp_msg that adds opcode */ +enum Tfa98xx_Error tfa_dsp_cmd_id_write(struct tfa_device *tfa, + unsigned char module_id, + unsigned char param_id, int num_bytes, + const unsigned char data[]) +{ + enum Tfa98xx_Error error; + unsigned char *buffer; + int nr = 0; + + buffer = kmem_cache_alloc(tfa->cachep, GFP_KERNEL); + if (buffer == NULL) + return Tfa98xx_Error_Fail; + + buffer[nr++] = tfa->spkr_select; + buffer[nr++] = module_id + 128; + buffer[nr++] = param_id; + + memcpy(&buffer[nr], data, num_bytes); + nr += num_bytes; + + error = dsp_msg(tfa, nr, (char *)buffer); + + kmem_cache_free(tfa->cachep, buffer); + + return error; +} + +/* wrapper for dsp_msg that adds opcode */ +/* this is as the former tfa98xx_dsp_get_param() */ +enum Tfa98xx_Error tfa_dsp_cmd_id_write_read(struct tfa_device *tfa, + unsigned char module_id, + unsigned char param_id, int num_bytes, + unsigned char data[]) +{ + enum Tfa98xx_Error error; + unsigned char buffer[3]; + int nr = 0; + + if (num_bytes <= 0) { + pr_debug("Error: The number of READ bytes is smaller or equal to 0! \n"); + return Tfa98xx_Error_Fail; + } + + if ((tfa->is_probus_device) && (tfa->cnt->ndev == 1) && + (param_id == SB_PARAM_GET_RE25C || + param_id == SB_PARAM_GET_LSMODEL || + param_id == SB_PARAM_GET_ALGO_PARAMS)) { + /* Modifying the ID for GetRe25C */ + buffer[nr++] = 4; + } else { + buffer[nr++] = tfa->spkr_select; + } + buffer[nr++] = module_id + 128; + buffer[nr++] = param_id; + + error = dsp_msg(tfa, nr, (char *)buffer); + if (error != Tfa98xx_Error_Ok) + return error; + + /* read the data from the dsp */ + error = dsp_msg_read(tfa, num_bytes, data); + return error; +} + +/* wrapper for dsp_msg that adds opcode and 3 bytes required for coefs */ +enum Tfa98xx_Error tfa_dsp_cmd_id_coefs(struct tfa_device *tfa, + unsigned char module_id, + unsigned char param_id, int num_bytes, + unsigned char data[]) +{ + enum Tfa98xx_Error error; + unsigned char buffer[2*3]; + int nr = 0; + + buffer[nr++] = tfa->spkr_select; + buffer[nr++] = module_id + 128; + buffer[nr++] = param_id; + + buffer[nr++] = 0; + buffer[nr++] = 0; + buffer[nr++] = 0; + + error = dsp_msg(tfa, nr, (char *)buffer); + if (error != Tfa98xx_Error_Ok) + return error; + + /* read the data from the dsp */ + error = dsp_msg_read(tfa, num_bytes, data); + + return error; +} + +/* wrapper for dsp_msg that adds opcode and 3 bytes required for MBDrcDynamics */ +enum Tfa98xx_Error tfa_dsp_cmd_id_MBDrc_dynamics(struct tfa_device *tfa, + unsigned char module_id, + unsigned char param_id, int index_subband, + int num_bytes, unsigned char data[]) +{ + enum Tfa98xx_Error error; + unsigned char buffer[2 * 3]; + int nr = 0; + + buffer[nr++] = tfa->spkr_select; + buffer[nr++] = module_id + 128; + buffer[nr++] = param_id; + + buffer[nr++] = 0; + buffer[nr++] = 0; + buffer[nr++] = (unsigned char)index_subband; + + error = dsp_msg(tfa, nr, (char *)buffer); + if(error != Tfa98xx_Error_Ok) + return error; + + /* read the data from the dsp */ + error = dsp_msg_read(tfa, num_bytes, data); + + return error; +} + +enum Tfa98xx_Error +tfa98xx_dsp_write_preset(struct tfa_device *tfa, int length, + const unsigned char *p_preset_bytes) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + if (p_preset_bytes != NULL) { + /* by design: keep the data opaque and no + * interpreting/calculation */ + error = tfa_dsp_cmd_id_write(tfa, MODULE_SPEAKERBOOST, + SB_PARAM_SET_PRESET, length, + p_preset_bytes); + } else { + error = Tfa98xx_Error_Bad_Parameter; + } + return error; +} + +/* + * get features from MTP + */ +enum Tfa98xx_Error +tfa98xx_dsp_get_hw_feature_bits(struct tfa_device *tfa, int *features) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + uint32_t value; + uint16_t mtpbf; + + /* return the cache data if it's valid */ + if (tfa->hw_feature_bits != -1) { + *features = tfa->hw_feature_bits; + } else { + /* for tfa1 check if we have clock */ + if (tfa->tfa_family == 1) { + int status; + tfa98xx_dsp_system_stable(tfa, &status); + if (!status) { + get_hw_features_from_cnt(tfa, features); + /* skip reading MTP: */ + return (*features == -1) ? Tfa98xx_Error_Fail : Tfa98xx_Error_Ok; + } + mtpbf=0x850f; /* MTP5 for tfa1,16 bits */ + } else + mtpbf=0xf907; /* MTP9 for tfa2, 8 bits */ + value = tfa_read_reg(tfa, mtpbf) & 0xffff; + *features = tfa->hw_feature_bits = value; + } + + return error; +} + +enum Tfa98xx_Error +tfa98xx_dsp_get_sw_feature_bits(struct tfa_device *tfa, int features[2]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + const int byte_size = 2 * 3; + unsigned char bytes[2 * 3]; + + /* return the cache data if it's valid */ + if (tfa->sw_feature_bits[0] != -1) { + features[0] = tfa->sw_feature_bits[0]; + features[1] = tfa->sw_feature_bits[1]; + } else { + /* for tfa1 check if we have clock */ + if (tfa->tfa_family == 1) { + int status; + tfa98xx_dsp_system_stable(tfa, &status); + if (!status) { + get_sw_features_from_cnt(tfa, features); + /* skip reading MTP: */ + return (features[0] == -1) ? Tfa98xx_Error_Fail : Tfa98xx_Error_Ok; + } + } + error = tfa_dsp_cmd_id_write_read(tfa, MODULE_FRAMEWORK, + FW_PAR_ID_GET_FEATURE_INFO, byte_size, bytes); + + if (error != Tfa98xx_Error_Ok) { + /* old ROM code may respond with Tfa98xx_Error_RpcParamId */ + return error; + } + + tfa98xx_convert_bytes2data(byte_size, bytes, features); + } + return error; +} + +enum Tfa98xx_Error tfa98xx_dsp_get_state_info(struct tfa_device *tfa, unsigned char bytes[], unsigned int *statesize) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int bSupportFramework = 0; + unsigned int stateSize = 9; + + err = tfa98xx_dsp_support_framework(tfa, &bSupportFramework); + if (err == Tfa98xx_Error_Ok) { + if (bSupportFramework) { + err = tfa_dsp_cmd_id_write_read(tfa, MODULE_FRAMEWORK, + FW_PARAM_GET_STATE, 3 * stateSize, bytes); + } else { + /* old ROM code, ask SpeakerBoost and only do first portion */ + stateSize = 8; + err = tfa_dsp_cmd_id_write_read(tfa, MODULE_SPEAKERBOOST, + SB_PARAM_GET_STATE, 3 * stateSize, bytes); + } + } + + *statesize = stateSize; + + return err; +} + +enum Tfa98xx_Error tfa98xx_dsp_support_drc(struct tfa_device *tfa, int *pbSupportDrc) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + *pbSupportDrc = 0; + + if(tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + if (tfa->supportDrc != supportNotSet) { + *pbSupportDrc = (tfa->supportDrc == supportYes); + } else { + int featureBits[2]; + + error = tfa98xx_dsp_get_sw_feature_bits(tfa, featureBits); + if (error == Tfa98xx_Error_Ok) { + /* easy case: new API available */ + /* bit=0 means DRC enabled */ + *pbSupportDrc = (featureBits[0] & FEATURE1_DRC) == 0; + } else if (error == Tfa98xx_Error_RpcParamId) { + /* older ROM code, doesn't support it */ + *pbSupportDrc = 0; + error = Tfa98xx_Error_Ok; + } + /* else some other error, return transparently */ + /* pbSupportDrc only changed when error == Tfa98xx_Error_Ok */ + + if (error == Tfa98xx_Error_Ok) { + tfa->supportDrc = *pbSupportDrc ? supportYes : supportNo; + } + } + return error; +} + +enum Tfa98xx_Error +tfa98xx_dsp_support_framework(struct tfa_device *tfa, int *pbSupportFramework) +{ + int featureBits[2] = { 0, 0 }; + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + _ASSERT(pbSupportFramework != 0); + + if(tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + if (tfa->supportFramework != supportNotSet) { + if(tfa->supportFramework == supportNo) + *pbSupportFramework = 0; + else + *pbSupportFramework = 1; + } else { + error = tfa98xx_dsp_get_sw_feature_bits(tfa, featureBits); + if (error == Tfa98xx_Error_Ok) { + *pbSupportFramework = 1; + tfa->supportFramework = supportYes; + } else { + *pbSupportFramework = 0; + tfa->supportFramework = supportNo; + error = Tfa98xx_Error_Ok; + } + } + + /* *pbSupportFramework only changed when error == Tfa98xx_Error_Ok */ + return error; +} + +enum Tfa98xx_Error +tfa98xx_dsp_write_speaker_parameters(struct tfa_device *tfa, + int length, const unsigned char *p_speaker_bytes) +{ + enum Tfa98xx_Error error; + int bSupportDrc; + + if (p_speaker_bytes != NULL) { + /* by design: keep the data opaque and no + * interpreting/calculation */ + /* Use long WaitResult retry count */ + error = tfa_dsp_cmd_id_write( + tfa, + MODULE_SPEAKERBOOST, + SB_PARAM_SET_LSMODEL, length, + p_speaker_bytes); + } else { + error = Tfa98xx_Error_Bad_Parameter; + } + + if (error != Tfa98xx_Error_Ok) + return error; + + error = tfa98xx_dsp_support_drc(tfa, &bSupportDrc); + if (error != Tfa98xx_Error_Ok) + return error; + + if (bSupportDrc) { + /* Need to set AgcGainInsert back to PRE, + * as the SetConfig forces it to POST */ + uint8_t bytes[3] = {0, 0, 0}; + + error = tfa_dsp_cmd_id_write(tfa, + MODULE_SPEAKERBOOST, + SB_PARAM_SET_AGCINS, + 3, + bytes); + } + + return error; +} + +enum Tfa98xx_Error +tfa98xx_dsp_write_config(struct tfa_device *tfa, int length, + const unsigned char *p_config_bytes) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int bSupportDrc; + + error = tfa_dsp_cmd_id_write(tfa, + MODULE_SPEAKERBOOST, + SB_PARAM_SET_CONFIG, length, + p_config_bytes); + if (error != Tfa98xx_Error_Ok) + return error; + + error = tfa98xx_dsp_support_drc(tfa, &bSupportDrc); + if (error != Tfa98xx_Error_Ok) + return error; + + if (bSupportDrc) { + /* Need to set AgcGainInsert back to PRE, + * as the SetConfig forces it to POST */ + uint8_t bytes[3] = {0, 0, 0}; + + error = tfa_dsp_cmd_id_write(tfa, + MODULE_SPEAKERBOOST, + SB_PARAM_SET_AGCINS, + 3, + bytes); + } + + return error; +} + +/* load all the parameters for the DRC settings from a file */ +enum Tfa98xx_Error tfa98xx_dsp_write_drc(struct tfa_device *tfa, + int length, const unsigned char *p_drc_bytes) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + if (p_drc_bytes != NULL) { + error = tfa_dsp_cmd_id_write(tfa, + MODULE_SPEAKERBOOST, + SB_PARAM_SET_DRC, length, + p_drc_bytes); + + } else { + error = Tfa98xx_Error_Bad_Parameter; + } + return error; +} + +enum Tfa98xx_Error tfa98xx_powerdown(struct tfa_device *tfa, int powerdown) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + if (tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + error = TFA_SET_BF(tfa, PWDN, (uint16_t)powerdown); + + if (powerdown) { + /* Workaround for ticket PLMA5337 */ + if (tfa->tfa_family == 2) { + TFA_SET_BF_VOLATILE(tfa, AMPE, 0); + } + } + + return error; +} + +enum Tfa98xx_Error +tfa98xx_select_mode(struct tfa_device *tfa, enum Tfa98xx_Mode mode) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + if(tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + if (error == Tfa98xx_Error_Ok) { + switch (mode) { + + default: + error = Tfa98xx_Error_Bad_Parameter; + } + } + + return error; +} + +int tfa_set_bf(struct tfa_device *tfa, const uint16_t bf, const uint16_t value) +{ + enum Tfa98xx_Error err; + uint16_t regvalue, msk, oldvalue; + + /* + * bitfield enum: + * - 0..3 : len + * - 4..7 : pos + * - 8..15 : address + */ + uint8_t len = bf & 0x0f; + uint8_t pos = (bf >> 4) & 0x0f; + uint8_t address = (bf >> 8) & 0xff; + + err = reg_read(tfa, address, ®value); + if (err) { + pr_err("Error getting bf :%d \n", -err); + return -err; + } + + oldvalue = regvalue; + msk = ((1<<(len+1))-1)<> 4) & 0x0f; + uint8_t address = (bf >> 8) & 0xff; + + err = reg_read(tfa, address, ®value); + if (err) { + pr_err("Error getting bf :%d \n", -err); + return -err; + } + + msk = ((1<<(len+1))-1)<> 4) & 0x0f; + uint8_t address = (bf >> 8) & 0xff; + + err = reg_read(tfa, address, ®value); + if (err) { + pr_err("Error getting bf :%d \n", -err); + return -err; + } + + msk = ((1<<(len+1))-1)<>pos; + + return value; +} + +int tfa_set_bf_value(const uint16_t bf, const uint16_t bf_value, uint16_t *p_reg_value) +{ + uint16_t regvalue, msk; + + /* + * bitfield enum: + * - 0..3 : len + * - 4..7 : pos + * - 8..15 : address + */ + uint8_t len = bf & 0x0f; + uint8_t pos = (bf >> 4) & 0x0f; + + regvalue = *p_reg_value; + + msk = ((1<<(len+1))-1)<> 4) & 0x0f; + + msk = ((1<<(len+1))-1)<> pos; + + return value; +} + + +int tfa_write_reg(struct tfa_device *tfa, const uint16_t bf, const uint16_t reg_value) +{ + enum Tfa98xx_Error err; + + /* bitfield enum - 8..15 : address */ + uint8_t address = (bf >> 8) & 0xff; + + err = reg_write(tfa, address, reg_value); + if (err) + return -err; + + return 0; +} + +int tfa_read_reg(struct tfa_device *tfa, const uint16_t bf) +{ + enum Tfa98xx_Error err; + uint16_t regvalue; + + /* bitfield enum - 8..15 : address */ + uint8_t address = (bf >> 8) & 0xff; + + err = reg_read(tfa, address, ®value); + if (err) + return -err; + + return regvalue; +} + +/* + * powerup the coolflux subsystem and wait for it + */ +enum Tfa98xx_Error tfa_cf_powerup(struct tfa_device *tfa) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int tries, status; + + /* power on the sub system */ + TFA_SET_BF_VOLATILE(tfa, PWDN, 0); + + // wait until everything is stable, in case clock has been off + if (tfa->verbose) + pr_info("Waiting for DSP system stable...\n"); + for ( tries=CFSTABLE_TRIES; tries > 0; tries-- ) { + err = tfa98xx_dsp_system_stable(tfa, &status); + _ASSERT(err == Tfa98xx_Error_Ok); + if ( status ) + break; + else + msleep_interruptible(10); /* wait 10ms to avoid busload */ + } + if (tries==0) {// timedout + pr_err("DSP subsystem start timed out\n"); + return Tfa98xx_Error_StateTimedOut; + } + + return err; +} + +/* + * Enable/Disable the I2S output for TFA1 devices + * without TDM interface + */ +static enum Tfa98xx_Error tfa98xx_aec_output(struct tfa_device *tfa, int enable) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + if ((tfa->daimap & Tfa98xx_DAI_TDM) == Tfa98xx_DAI_TDM) + return err; + + if (tfa->tfa_family == 1) + err = -tfa_set_bf(tfa, TFA1_BF_I2SDOE, (enable!=0)); + else { + pr_err("I2SDOE on unsupported family\n"); + err = Tfa98xx_Error_Not_Supported; + } + + return err; +} + +/* + * Print the current state of the hardware manager + * Device manager status information, man_state from TFA9888_N1B_I2C_regmap_V12 + */ +enum Tfa98xx_Error show_current_state(struct tfa_device *tfa) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int manstate = -1; + + if (tfa->tfa_family == 2 && tfa->verbose) { + manstate = TFA_GET_BF(tfa, MANSTATE); + if (manstate < 0) + return -manstate; + + pr_debug("Current HW manager state: "); + + switch(manstate) { + case 0: pr_debug("power_down_state \n"); + break; + case 1: pr_debug("wait_for_source_settings_state \n"); + break; + case 2: pr_debug("connnect_pll_input_state \n"); + break; + case 3: pr_debug("disconnect_pll_input_state \n"); + break; + case 4: pr_debug("enable_pll_state \n"); + break; + case 5: pr_debug("enable_cgu_state \n"); + break; + case 6: pr_debug("init_cf_state \n"); + break; + case 7: pr_debug("enable_amplifier_state \n"); + break; + case 8: pr_debug("alarm_state \n"); + break; + case 9: pr_debug("operating_state \n"); + break; + case 10: pr_debug("mute_audio_state \n"); + break; + case 11: pr_debug("disable_cgu_pll_state \n"); + break; + default: + pr_debug("Unable to find current state \n"); + break; + } + } + + return err; +} + +/* + * start the speakerboost algorithm + * this implies a full system startup when the system was not already started + * + */ +enum Tfa98xx_Error tfaRunSpeakerBoost(struct tfa_device *tfa, int force, int profile) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int value; + + if (force) { + err= tfaRunColdStartup(tfa, profile); + if ( err ) return err; + } + + /* Returns 1 when device is "cold" and 0 when device is warm */ + value = tfa_is_cold(tfa); + + pr_debug("Startup of device [%s] is a %sstart\n", tfaContDeviceName(tfa->cnt, tfa->dev_idx), value ? "cold" : "warm"); + + /* cold start and not tap profile */ + if (value) { + /* Run startup and write all files */ + err = tfaRunSpeakerStartup(tfa, force, profile); + if ( err ) return err; + + /* Save the current profile and set the vstep to 0 */ + /* This needs to be overwriten even in CF bypass */ + tfa_dev_set_swprof(tfa, (unsigned short)profile); + tfa_dev_set_swvstep(tfa, 0); + + /* Synchonize I/V delay on 96/97 at cold start */ + if ((tfa->tfa_family == 1) && (tfa->daimap == Tfa98xx_DAI_TDM)) + tfa->sync_iv_delay = 1; + } + + return err; +} + +enum Tfa98xx_Error tfaRunSpeakerStartup(struct tfa_device *tfa, int force, int profile) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + if ( !force ) { // in case of force CF already runnning + err = tfaRunStartup(tfa, profile); + PRINT_ASSERT(err); + if ( err ) + return err; + + /* Startup with CF in bypass then return here */ + if (tfa_cf_enabled(tfa) == 0) + return err; + + /* respond to external DSP: -1:none, 0:no_dsp, 1:cold, 2:warm */ + if(tfa->ext_dsp == -1) { + err = tfaRunStartDSP(tfa); + if ( err ) + return err; + } + } + + /* Set auto_copy_mtp_to_iic (bit 5 of A3) to 1 */ + tfa98xx_auto_copy_mtp_to_iic(tfa); + + /* write all the files from the device list */ + err = tfaContWriteFiles(tfa); + if (err) { + pr_debug("[%s] tfaContWriteFiles error = %d \n", __FUNCTION__, err); + return err; + } + + /* write all the files from the profile list (use volumstep 0) */ + err = tfaContWriteFilesProf(tfa, profile, 0); + if (err) { + pr_debug("[%s] tfaContWriteFilesProf error = %d \n", __FUNCTION__, err); + return err; + } + + return err; +} + +/* + * Run calibration + */ +enum Tfa98xx_Error tfaRunSpeakerCalibration(struct tfa_device *tfa) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int calibrateDone; + + /* return if there is no audio running */ + if ((tfa->tfa_family == 2) && TFA_GET_BF(tfa, NOCLK)) + return Tfa98xx_Error_NoClock; + + /* When MTPOTC is set (cal=once) unlock key2 */ + if (TFA_GET_BF(tfa, MTPOTC) == 1) { + tfa98xx_key2(tfa, 0); + } + + /* await calibration, this should return ok */ + err = tfaRunWaitCalibration(tfa, &calibrateDone); + if (err == Tfa98xx_Error_Ok) { + err = tfa_dsp_get_calibration_impedance(tfa); + PRINT_ASSERT(err); + } + + /* When MTPOTC is set (cal=once) re-lock key2 */ + if (TFA_GET_BF(tfa, MTPOTC) == 1) { + tfa98xx_key2(tfa, 1); + } + + return err; +} + +enum Tfa98xx_Error tfaRunColdboot(struct tfa_device *tfa, int state) +{ +#define CF_CONTROL 0x8100 + enum Tfa98xx_Error err=Tfa98xx_Error_Ok; + int tries = 10; + + /* repeat set ACS bit until set as requested */ + while ( state != TFA_GET_BF(tfa, ACS)) { + /* set colstarted in CF_CONTROL to force ACS */ + err = mem_write(tfa, CF_CONTROL, state, Tfa98xx_DMEM_IOMEM); + PRINT_ASSERT(err); + + if (tries-- == 0) { + pr_debug("coldboot (ACS) did not %s\n", state ? "set":"clear"); + return Tfa98xx_Error_Other; + } + } + + return err; +} + + + +/* + * load the patch if any + * else tell no loaded + */ +static enum Tfa98xx_Error tfa_run_load_patch(struct tfa_device *tfa) +{ + return tfaContWritePatch(tfa); +} + +/* + * this will load the patch witch will implicitly start the DSP + * if no patch is available the DPS is started immediately + */ +enum Tfa98xx_Error tfaRunStartDSP(struct tfa_device *tfa) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + err = tfa_run_load_patch(tfa); + if (err) { /* patch load is fatal so return immediately*/ + return err; + } + + /* Clear count_boot, should be reset to 0 before the DSP reset is released */ + err = mem_write(tfa, 512, 0, Tfa98xx_DMEM_XMEM); + PRINT_ASSERT(err); + + /* Reset DSP once for sure after initializing */ + if ( err == Tfa98xx_Error_Ok) { + err = tfa98xx_dsp_reset(tfa, 0); + PRINT_ASSERT(err); + } + + /* Sample rate is needed to set the correct tables */ + err = tfa98xx_dsp_write_tables(tfa, TFA_GET_BF(tfa, AUDFS)); + PRINT_ASSERT(err); + + return err; +} + +/* + * start the clocks and wait until the AMP is switching + * on return the DSP sub system will be ready for loading + */ +enum Tfa98xx_Error tfaRunStartup(struct tfa_device *tfa, int profile) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + nxpTfaDeviceList_t *dev = tfaContDevice(tfa->cnt, tfa->dev_idx); + int i, noinit=0; + + if(dev == NULL) + return Tfa98xx_Error_Fail; + + if(dev->bus) /* no i2c device, do nothing */ + return Tfa98xx_Error_Ok; + + /* process the device list to see if the user implemented the noinit */ + for(i=0;ilength;i++) { + if (dev->list[i].type == dscNoInit) { + noinit=1; + break; + } + } + + if(!noinit) { + /* load the optimal TFA98XX in HW settings */ + err = tfa98xx_init(tfa); + PRINT_ASSERT(err); + } else { + pr_debug("\nWarning: No init keyword found in the cnt file. Init is skipped! \n"); + } + + /* I2S settings to define the audio input properties + * these must be set before the subsys is up */ + // this will run the list until a non-register item is encountered + err = tfaContWriteRegsDev(tfa); // write device register settings + PRINT_ASSERT(err); + // also write register the settings from the default profile + // NOTE we may still have ACS=1 so we can switch sample rate here + err = tfaContWriteRegsProf(tfa, profile); + PRINT_ASSERT(err); + + /* Factory trimming for the Boost converter */ + tfa98xx_factory_trimmer(tfa); + + /* Go to the initCF state */ + tfa_dev_set_state(tfa, TFA_STATE_INIT_CF); + + err = show_current_state(tfa); + + return err; +} + +/* + * run the startup/init sequence and set ACS bit + */ +enum Tfa98xx_Error tfaRunColdStartup(struct tfa_device *tfa, int profile) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + err = tfaRunStartup(tfa, profile); + PRINT_ASSERT(err); + if (err) + return err; + + if(!tfa->is_probus_device) { + /* force cold boot */ + err = tfaRunColdboot(tfa, 1); // set ACS + PRINT_ASSERT(err); + if (err) + return err; + } + + /* start */ + err = tfaRunStartDSP(tfa); + PRINT_ASSERT(err); + + return err; +} + +/* + * + */ +enum Tfa98xx_Error tfaRunMute(struct tfa_device *tfa) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int status; + int tries = 0; + + /* signal the TFA98XX to mute */ + if (tfa->tfa_family == 1) { + err = tfa98xx_set_mute(tfa, Tfa98xx_Mute_Amplifier); + + if(err == Tfa98xx_Error_Ok) { + /* now wait for the amplifier to turn off */ + do { + status = TFA_GET_BF(tfa, SWS); + if (status != 0) + msleep_interruptible(10); /* wait 10ms to avoid busload */ + else + break; + tries++; + } while (tries < AMPOFFWAIT_TRIES); + + + if (tfa->verbose) + pr_debug("-------------------- muted --------------------\n"); + + /*The amplifier is always switching*/ + if (tries == AMPOFFWAIT_TRIES) + return Tfa98xx_Error_Other; + } + } + + return err; +} +/* + * + */ +enum Tfa98xx_Error tfaRunUnmute(struct tfa_device *tfa) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + /* signal the TFA98XX to mute */ + err = tfa98xx_set_mute(tfa, Tfa98xx_Mute_Off); + + if (tfa->verbose) + pr_debug("-------------------unmuted ------------------\n"); + + return err; +} + +static void individual_calibration_results(struct tfa_device *tfa) +{ + int value_P, value_S; + + /* Read the calibration result in xmem (529=primary channel) (530=secondary channel) */ + mem_read(tfa, 529, 1, &value_P); + mem_read(tfa, 530, 1, &value_S); + + if(value_P != 1 && value_S != 1) + pr_debug("Calibration failed on both channels! \n"); + else if(value_P != 1) { + pr_debug("Calibration failed on Primary (Left) channel! \n"); + TFA_SET_BF_VOLATILE(tfa, SSLEFTE, 0); /* Disable the sound for the left speaker */ + } + else if(value_S != 1) { + pr_debug("Calibration failed on Secondary (Right) channel! \n"); + TFA_SET_BF_VOLATILE(tfa, SSRIGHTE, 0); /* Disable the sound for the right speaker */ + } + + TFA_SET_BF_VOLATILE(tfa, AMPINSEL, 0); /* Set amplifier input to TDM */ + TFA_SET_BF_VOLATILE(tfa, SBSL, 1); +} + +/* + * wait for calibrateDone + */ +enum Tfa98xx_Error tfaRunWaitCalibration(struct tfa_device *tfa, int *calibrateDone) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int tries = 0, mtp_busy = 1, tries_mtp_busy = 0; + + *calibrateDone = 0; + + /* in case of calibrate once wait for MTPEX */ + if (TFA_GET_BF(tfa, MTPOTC)) { + // Check if MTP_busy is clear! + while (tries_mtp_busy < MTPBWAIT_TRIES) + { + mtp_busy = tfa_dev_get_mtpb(tfa); + if (mtp_busy == 1) + msleep_interruptible(10); /* wait 10ms to avoid busload */ + else + break; + tries_mtp_busy++; + } + + if (tries_mtp_busy < MTPBWAIT_TRIES) { + /* Because of the msleep TFA98XX_API_WAITRESULT_NTRIES is way to long! + * Setting this to 25 will take it atleast 25*50ms = 1.25 sec + */ + while ((*calibrateDone == 0) && (tries < MTPEX_WAIT_NTRIES)) { + *calibrateDone = TFA_GET_BF(tfa, MTPEX); + if (*calibrateDone == 1) + break; + msleep_interruptible(50); /* wait 50ms to avoid busload */ + tries++; + } + + if (tries >= MTPEX_WAIT_NTRIES) { + tries = TFA98XX_API_WAITRESULT_NTRIES; + } + } else { + pr_err("MTP bussy after %d tries\n", MTPBWAIT_TRIES); + } + } + + /* poll xmem for calibrate always + * calibrateDone = 0 means "calibrating", + * calibrateDone = -1 (or 0xFFFFFF) means "fails" + * calibrateDone = 1 means calibration done + */ + while ((*calibrateDone != 1) && (triestfa_family == 2) && (TFA_GET_BF(tfa, REFCKSEL) == 1)) { + pr_err("Unable to calibrate the device with the internal clock! \n"); + } + } + + /* Check which speaker calibration failed. Only for 88C */ + if ((err != Tfa98xx_Error_Ok) && ((tfa->rev & 0x0FFF) == 0xc88)) { + individual_calibration_results(tfa); + } + + return err; +} + +/* + * tfa_dev_start will only do the basics: Going from powerdown to operating or a profile switch. + * for calibrating or akoustic shock handling use the tfa98xxCalibration function. + */ +enum tfa_error tfa_dev_start(struct tfa_device *tfa, int next_profile, int vstep) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + int active_profile = -1; + + /* Get currentprofile */ + active_profile = tfa_dev_get_swprof(tfa); + if (active_profile == 0xff) + active_profile = -1; + + /* TfaRun_SpeakerBoost implies un-mute */ + pr_debug("Active_profile:%s, next_profile:%s\n", + tfaContProfileName(tfa->cnt, tfa->dev_idx, active_profile), + tfaContProfileName(tfa->cnt, tfa->dev_idx, next_profile)); + + err = show_current_state(tfa); + + if ( tfa->tfa_family == 1 ) { /* TODO move this to ini file */ + /* Enable I2S output on TFA1 devices without TDM */ + err = tfa98xx_aec_output(tfa, 1); + if ( err != Tfa98xx_Error_Ok) + goto error_exit; + } + + if ( tfa->bus != 0 ) { /* non i2c */ +#ifndef __KERNEL__ + tfadsp_fw_start(tfa, next_profile, vstep); +#endif /* __KERNEL__ */ + } else { + /* Check if we need coldstart or ACS is set */ + err = tfaRunSpeakerBoost(tfa, 0, next_profile); + if ( err != Tfa98xx_Error_Ok) + goto error_exit; + + /* Make sure internal oscillator is running for DSP devices (non-dsp and max1 this is no-op) */ + tfa98xx_set_osc_powerdown(tfa, 0); + + /* Go to the Operating state */ + tfa_dev_set_state(tfa, TFA_STATE_OPERATING | TFA_STATE_MUTE); + } + active_profile = tfa_dev_get_swprof(tfa); + + /* Profile switching */ + if ((next_profile != active_profile && active_profile >= 0)) { + err = tfaContWriteProfile(tfa, next_profile, vstep); + if (err!=Tfa98xx_Error_Ok) + goto error_exit; + } + + /* If the profile contains the .standby suffix go to powerdown + * else we should be in operating state + */ + if(strstr(tfaContProfileName(tfa->cnt, tfa->dev_idx, next_profile), ".standby") != NULL) { + tfa_dev_set_swprof(tfa, (unsigned short)next_profile); + tfa_dev_set_swvstep(tfa, (unsigned short)tfa->vstep); + goto error_exit; + } + + err = show_current_state(tfa); + + if (vstep != tfa->vstep && vstep != -1) { + err = tfaContWriteFilesVstep(tfa, next_profile, vstep); + if ( err != Tfa98xx_Error_Ok) + goto error_exit; + } + + /* Always search and apply filters after a startup */ + err = tfa_set_filters(tfa, next_profile); + if (err!=Tfa98xx_Error_Ok) + goto error_exit; + + tfa_dev_set_swprof(tfa, (unsigned short)next_profile); + tfa_dev_set_swvstep(tfa, (unsigned short)tfa->vstep); + + /* PLMA5539: Gives information about current setting of powerswitch */ + if (tfa->verbose) { + if (!tfa98xx_powerswitch_is_enabled(tfa)) + pr_info("Device start without powerswitch enabled!\n"); + } + +error_exit: + show_current_state(tfa); + + return err; +} + +enum tfa_error tfa_dev_stop(struct tfa_device *tfa) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + /* mute */ + tfaRunMute(tfa); + + /* Make sure internal oscillator is not running for DSP devices (non-dsp and max1 this is no-op) */ + tfa98xx_set_osc_powerdown(tfa, 1); + + /* powerdown CF */ + err = tfa98xx_powerdown(tfa, 1 ); + if ( err != Tfa98xx_Error_Ok) + return err; + + /* disable I2S output on TFA1 devices without TDM */ + err = tfa98xx_aec_output(tfa, 0); + + return err; +} + +/* + * int registers and coldboot dsp + */ +int tfa_reset(struct tfa_device *tfa) +{ + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + + err = -TFA_SET_BF_VOLATILE(tfa, I2CR, 1); + if(err) return err; + + if (tfa->tfa_family == 2) { + /* restore MANSCONF to POR state */ + TFA_SET_BF_VOLATILE(tfa, MANSCONF, 0); + } + + if( !tfa->is_probus_device ) { + if (tfa->tfa_family == 2) { + /* restore MANCOLD to POR state */ + TFA_SET_BF_VOLATILE(tfa, MANCOLD, 1); + } + + /* powerup CF to access CF io */ + tfa98xx_powerdown(tfa, 0 ); + /* for clock */ + err = tfa_cf_powerup(tfa); + PRINT_ASSERT(err); + + /* force cold boot */ + err = tfaRunColdboot(tfa, 1); // set ACS + PRINT_ASSERT(err); + + /* reset all i2C registers to default */ + err = -TFA_SET_BF(tfa, I2CR, 1); + PRINT_ASSERT(err); + } else { + if (tfa->ext_dsp > 0) + tfa98xx_init_dsp(tfa); + } + + return err; +} + +/* + * Write all the bytes specified by num_bytes and data + */ +enum Tfa98xx_Error +tfa98xx_write_data(struct tfa_device *tfa, + unsigned char subaddress, int num_bytes, + const unsigned char data[]) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + /* subaddress followed by data */ + const int bytes2write = num_bytes + 1; + unsigned char *write_data; + + if (num_bytes > TFA2_MAX_PARAM_SIZE) + return Tfa98xx_Error_Bad_Parameter; + + write_data = (unsigned char *)kmem_cache_alloc(tfa->cachep, GFP_KERNEL); + if (write_data == NULL) + return Tfa98xx_Error_Fail; + + write_data[0] = subaddress; + memcpy(&write_data[1], data, num_bytes); + + error = tfa98xx_write_raw(tfa, bytes2write, write_data); + + kmem_cache_free(tfa->cachep, write_data); + return error; +} + +/* + * fill the calibration value as milli ohms in the struct + * + * assume that the device has been calibrated + */ +enum Tfa98xx_Error tfa_dsp_get_calibration_impedance(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned char bytes[3*2] = {0}; + int nr_bytes, i, data[2], calibrateDone, spkr_count=0, cal_idx=0; + unsigned int scaled_data; + int tries=0; + + error = tfa_supported_speakers(tfa, &spkr_count); + + if (tfa_dev_mtp_get(tfa, TFA_MTP_OTC)) { + pr_debug("Getting calibration values from MTP\n"); + + if((tfa->rev & 0xFF) == 0x88) { + for(i=0; imohm[i] = tfa_dev_mtp_get(tfa, TFA_MTP_RE25_PRIM); + else + tfa->mohm[i] = tfa_dev_mtp_get(tfa, TFA_MTP_RE25_SEC); + } + } else { + tfa->mohm[0] = tfa_dev_mtp_get(tfa, TFA_MTP_RE25); + } + } else { + pr_debug("Getting calibration values from Speakerboost\n"); + + /* Make sure the calibrateDone bit is set before getting the values from speakerboost! + * This does not work for 72 (because the dsp cannot set this bit) + */ + if (!tfa->is_probus_device) { + /* poll xmem for calibrate always + * calibrateDone = 0 means "calibrating", + * calibrateDone = -1 (or 0xFFFFFF) means "fails" + * calibrateDone = 1 means calibration done + */ + calibrateDone = 0; + while ((calibrateDone != 1) && (tries < TFA98XX_API_WAITRESULT_NTRIES)) { + error = mem_read(tfa, TFA_FW_XMEM_CALIBRATION_DONE, 1, &calibrateDone); + if (calibrateDone == 1) + break; + tries++; + } + + if (calibrateDone != 1) { + pr_err("Calibration failed! \n"); + error = Tfa98xx_Error_Bad_Parameter; + } + else if (tries == TFA98XX_API_WAITRESULT_NTRIES) { + pr_debug("Calibration has timedout! \n"); + error = Tfa98xx_Error_StateTimedOut; + } + } + /* SoftDSP interface differs from hw-dsp interfaces */ + if(tfa->is_probus_device && tfa->cnt->ndev > 1) { + spkr_count = tfa->cnt->ndev; + } + + nr_bytes = spkr_count * 3; + error = tfa_dsp_cmd_id_write_read(tfa, MODULE_SPEAKERBOOST, SB_PARAM_GET_RE25C, nr_bytes, bytes); + if (error == Tfa98xx_Error_Ok) { + tfa98xx_convert_bytes2data(nr_bytes, bytes, data); + + for(i=0; iis_probus_device && tfa->dev_idx >= 1) ) { + cal_idx=0; + } + + /* signed data has a limit of 30 Ohm */ + scaled_data = data[i]; + + if(tfa->tfa_family == 2) + tfa->mohm[cal_idx] = (scaled_data*1000)/TFA2_FW_ReZ_SCALE; + else + tfa->mohm[cal_idx] = (scaled_data*1000)/TFA1_FW_ReZ_SCALE; + } + } + } + + return error; +} + +/* start count from 1, 0 is invalid */ +int tfa_dev_get_swprof(struct tfa_device *tfa) +{ + return (tfa->dev_ops.get_swprof)(tfa); +} + +int tfa_dev_set_swprof(struct tfa_device *tfa, unsigned short new_value) +{ + return (tfa->dev_ops.set_swprof)(tfa, new_value+1); +} + +/* same value for all channels + * start count from 1, 0 is invalid */ +int tfa_dev_get_swvstep(struct tfa_device *tfa) +{ + return (tfa->dev_ops.get_swvstep)(tfa); +} + +int tfa_dev_set_swvstep(struct tfa_device *tfa, unsigned short new_value) +{ + return (tfa->dev_ops.set_swvstep)(tfa, new_value + 1); +} + +/* + function overload for MTPB + */ +int tfa_dev_get_mtpb(struct tfa_device *tfa) +{ + return (tfa->dev_ops.get_mtpb)(tfa); +} + +int tfa_is_cold(struct tfa_device *tfa) +{ + int value; + + /* + * check for cold boot status + */ + if (tfa->is_probus_device) { + if (tfa->ext_dsp > 0) { + if (tfa->ext_dsp == 2) + value = 0; // warm + else /* no dsp or cold */ + value = 1; // cold + } else { + value = (TFA_GET_BF(tfa, MANSCONF) == 0); + } + } else { + value = TFA_GET_BF(tfa, ACS); + } + + return value; +} + +int tfa_needs_reset(struct tfa_device *tfa) +{ + int value; + + /* checks if the DSP commands SetAlgoParams and SetMBDrc + * need a DSP reset (now: at coldstart or during calibration) + */ + if (tfa_is_cold(tfa) == 1 || tfa->needs_reset == 1) + value = 1; + else + value = 0; + + return value; +} + +int tfa_cf_enabled(struct tfa_device *tfa) +{ + int value; + + /* For 72 there is no CF */ + if(tfa->is_probus_device) { + value = (tfa->ext_dsp != 0); + } else { + value = TFA_GET_BF(tfa, CFE); + } + + return value; +} + +#define NR_COEFFS 6 +#define NR_BIQUADS 28 +#define BQ_SIZE (3 * NR_COEFFS) +#define DSP_MSG_OVERHEAD 27 + +#pragma pack (push, 1) +struct dsp_msg_all_coeff { + uint8_t select_eq[3]; + uint8_t biquad[NR_BIQUADS][NR_COEFFS][3]; +}; +#pragma pack (pop) + +/* number of biquads for each equalizer */ +static const int eq_biquads[] = { + 10, 10, 2, 2, 2, 2 +}; + +#define NR_EQ (int)(sizeof(eq_biquads) / sizeof(int)) + +enum Tfa98xx_Error dsp_partial_coefficients(struct tfa_device *tfa, uint8_t *prev, uint8_t *next) +{ + uint8_t bq, eq; + int eq_offset; + int new_cost, old_cost; + uint32_t eq_biquad_mask[NR_EQ]; + enum Tfa98xx_Error err = Tfa98xx_Error_Ok; + struct dsp_msg_all_coeff *data1 = (struct dsp_msg_all_coeff *)prev; + struct dsp_msg_all_coeff *data2 = (struct dsp_msg_all_coeff *)next; + + old_cost = DSP_MSG_OVERHEAD + 3 + sizeof(struct dsp_msg_all_coeff); + new_cost = 0; + + eq_offset = 0; + for (eq=0; eqbiquad[eq_offset][0][0]; + uint8_t *eq2 = &data2->biquad[eq_offset][0][0]; + + eq_biquad_mask[eq] = 0; + + if (memcmp(eq1, eq2, BQ_SIZE*eq_biquads[eq]) != 0) { + int nr_bq = 0; + int bq_sz, eq_sz; + + for (bq=0; bq < eq_biquads[eq]; bq++) { + uint8_t *bq1 = &eq1[bq*BQ_SIZE]; + uint8_t *bq2 = &eq2[bq*BQ_SIZE]; + + if (memcmp(bq1, bq2, BQ_SIZE) != 0) { + eq_biquad_mask[eq] |= (1<= eq_sz) { + eq_biquad_mask[eq] = 0xffffffff; + + new_cost += eq_sz; + + } else { + new_cost += bq_sz; + } + } + pr_debug("eq_biquad_mask[%d] = 0x%.8x\n", eq, eq_biquad_mask[eq]); + + eq_offset += eq_biquads[eq]; + } + + pr_debug("cost for writing all coefficients = %d\n", old_cost); + pr_debug("cost for writing changed coefficients = %d\n", new_cost); + + if (new_cost >= old_cost) { + const int buffer_sz = 3 + sizeof(struct dsp_msg_all_coeff); + uint8_t *buffer; + + buffer = kmalloc(buffer_sz, GFP_KERNEL); + if (buffer == NULL) + return Tfa98xx_Error_Fail; + + /* cmd id */ + buffer[0] = 0x00; + buffer[1] = 0x82; + buffer[2] = 0x00; + + /* parameters */ + memcpy(&buffer[3], data2, sizeof(struct dsp_msg_all_coeff)); + + err = dsp_msg(tfa, buffer_sz, (const char *)buffer); + + kfree(buffer); + if (err) + return err; + + } else { + eq_offset = 0; + for (eq=0; eqbiquad[eq_offset][0][0]; + + if (eq_biquad_mask[eq] == 0xffffffff) { + const int msg_sz = 6 + BQ_SIZE * eq_biquads[eq]; + uint8_t *msg; + + msg = kmalloc(msg_sz, GFP_KERNEL); + if (msg == NULL) + return Tfa98xx_Error_Fail; + + /* cmd id */ + msg[0] = 0x00; + msg[1] = 0x82; + msg[2] = 0x00; + + /* select eq and bq */ + msg[3] = 0x00; + msg[4] = eq+1; + msg[5] = 0x00; /* all biquads */ + + /* biquad parameters */ + memcpy(&msg[6], eq2, BQ_SIZE * eq_biquads[eq]); + + err = dsp_msg(tfa, msg_sz, (const char *)msg); + + kfree(msg); + if (err) + return err; + + } else if (eq_biquad_mask[eq] != 0) { + for(bq=0; bq < eq_biquads[eq]; bq++) { + + if (eq_biquad_mask[eq] & (1<cachep, GFP_KERNEL); + if (msg == NULL) + return Tfa98xx_Error_Fail; + + /* cmd id */ + msg[0] = 0x00; + msg[1] = 0x82; + msg[2] = 0x00; + + /* select eq and bq*/ + msg[3] = 0x00; + msg[4] = eq+1; + msg[5] = bq+1; + + /* biquad parameters */ + memcpy(&msg[6], bq2, BQ_SIZE); + + err = dsp_msg(tfa, msg_sz, (const char *)msg); + + kmem_cache_free(tfa->cachep, msg); + if (err) + return err; + } + } + } + eq_offset += eq_biquads[eq]; + } + } + + return err; +} + +/* fill context info */ +int tfa_dev_probe(int slave, struct tfa_device *tfa) +{ + uint16_t rev; + + tfa->slave_address = (unsigned char)slave; + + /* read revid via low level hal, register 3 */ + if (tfa98xx_read_register16(tfa, 3, &rev) != Tfa98xx_Error_Ok) { + PRINT("\nError: Unable to read revid from slave:0x%02x \n", slave); + return -1; + } + + tfa->rev = rev; + tfa->dev_idx = -1; + tfa->state = TFA_STATE_UNKNOWN; + tfa->p_regInfo = NULL; + + tfa_set_query_info(tfa); + + tfa->in_use = 1; + + return 0; +} + +enum tfa_error tfa_dev_set_state(struct tfa_device *tfa, enum tfa_state state) +{ + enum tfa_error err = tfa_error_ok; + int loop = 50, ready = 0; + int count; + + /* Base states */ + /* Do not change the order of setting bits as this is important! */ + switch (state & 0x0f) { + case TFA_STATE_POWERDOWN: /* PLL in powerdown, Algo up */ + break; + case TFA_STATE_INIT_HW: /* load I2C/PLL hardware setting (~wait2srcsettings) */ + break; + case TFA_STATE_INIT_CF: /* coolflux HW access possible (~initcf) */ + /* Start with SBSL=0 to stay in initCF state */ + TFA_SET_BF(tfa, SBSL, 0); + + /* We want to leave Wait4SrcSettings state for max2 */ + if (tfa->tfa_family == 2) + TFA_SET_BF(tfa, MANSCONF, 1); + + /* And finally set PWDN to 0 to leave powerdown state */ + TFA_SET_BF(tfa, PWDN, 0); + + /* Make sure the DSP is running! */ + do { + err = tfa98xx_dsp_system_stable(tfa, &ready); + if (err != tfa_error_ok) + return err; + if (ready) + break; + } while (loop--); + /* Enable FAIM when clock is stable, to avoid MTP corruption */ + err = tfa98xx_faim_protect(tfa, 1); + if (tfa->verbose) { + pr_debug("FAIM enabled (err:%d).\n", err); + } + break; + case TFA_STATE_INIT_FW: /* DSP framework active (~patch loaded) */ + break; + case TFA_STATE_OPERATING: /* Amp and Algo running */ + /* Depending on our previous state we need to set 3 bits */ + TFA_SET_BF(tfa, PWDN, 0); /* Coming from state 0 */ + TFA_SET_BF(tfa, MANSCONF, 1); /* Coming from state 1 */ +#ifdef TFA9874_NONDSP_STEREO + TFA_SET_BF(tfa, AMPE, 1); /* Coming from state 6 */ +#else + TFA_SET_BF(tfa, SBSL, 1); /* Coming from state 6 */ +#endif + /* + * Disable MTP clock to protect memory. + * However in case of calibration wait for DSP! (This should be case only during calibration). + */ + if (TFA_GET_BF(tfa, MTPOTC) == 1) { + count = MTPEX_WAIT_NTRIES * 4; /* Calibration takes a lot of time */ + while ((TFA_GET_BF(tfa, MTPEX) != 1) && count) { + msleep_interruptible(10); + count--; + } + } + err = tfa98xx_faim_protect(tfa, 0); + if (tfa->verbose) { + pr_debug("FAIM disabled (err:%d).\n", err); + } + + /* Synchonize I/V delay on 96/97 at cold start */ + if (tfa->sync_iv_delay) { + if (tfa->verbose) + pr_debug("syncing I/V delay for %x\n", + (tfa->rev & 0xff)); + + /* wait for ACS to be cleared */ + count = 10; + while ((TFA_GET_BF(tfa, ACS) == 1) && + (count-- > 0)) { + msleep_interruptible(1); + } + + tfa98xx_dsp_reset(tfa, 1); + tfa98xx_dsp_reset(tfa, 0); + tfa->sync_iv_delay = 0; + } + break; + case TFA_STATE_FAULT: /* An alarm or error occurred */ + break; + case TFA_STATE_RESET: /* I2C reset and ACS set */ + tfa98xx_init(tfa); + break; + default: + if (state & 0x0f) + return tfa_error_bad_param; + } + + /* state modifiers */ + + if (state & TFA_STATE_MUTE) + tfa98xx_set_mute(tfa, Tfa98xx_Mute_Amplifier); + + if (state & TFA_STATE_UNMUTE) + tfa98xx_set_mute(tfa, Tfa98xx_Mute_Off); + + tfa->state = state; + + return tfa_error_ok; +} + +enum tfa_state tfa_dev_get_state(struct tfa_device *tfa) +{ + int cold = TFA_GET_BF(tfa, ACS); + int manstate; + + /* different per family type */ + if ( tfa->tfa_family == 1 ) { + if ( cold && TFA_GET_BF(tfa, PWDN) ) + tfa->state = TFA_STATE_RESET; + else if ( !cold && TFA_GET_BF(tfa, SWS)) + tfa->state = TFA_STATE_OPERATING; + } else /* family 2 */ { + manstate = TFA_GET_BF(tfa, MANSTATE); + switch(manstate) { + case 0: + tfa->state = cold ? TFA_STATE_RESET : TFA_STATE_POWERDOWN; + break; + case 8: /* if dsp reset if off assume framework is running */ + tfa->state = TFA_GET_BF(tfa, RST) ? TFA_STATE_INIT_CF : TFA_STATE_INIT_FW; + break; + case 9: + tfa->state = TFA_STATE_OPERATING; + break; + default: + break; + } + } + + return tfa->state; +} + +int tfa_dev_mtp_get(struct tfa_device *tfa, enum tfa_mtp item) +{ + int value = 0; + + switch (item) { + case TFA_MTP_OTC: + value = TFA_GET_BF(tfa, MTPOTC); + break; + case TFA_MTP_EX: + value = TFA_GET_BF(tfa, MTPEX); + break; + case TFA_MTP_RE25: + case TFA_MTP_RE25_PRIM: + if(tfa->tfa_family == 2) { + if((tfa->rev & 0xFF) == 0x88) + value = TFA_GET_BF(tfa, R25CL); + else if((tfa->rev & 0xFF) == 0x13) + value = tfa_get_bf(tfa, TFA9912_BF_R25C); + else + value = TFA_GET_BF(tfa, R25C); + } else { + reg_read(tfa, 0x83, (unsigned short*)&value); + } + break; + case TFA_MTP_RE25_SEC: + if((tfa->rev & 0xFF) == 0x88) { + value = TFA_GET_BF(tfa, R25CR); + } else { + pr_debug("Error: Current device has no secondary Re25 channel \n"); + } + break; + case TFA_MTP_LOCK: + break; + } + + return value; +} + +enum tfa_error tfa_dev_mtp_set(struct tfa_device *tfa, enum tfa_mtp item, int value) +{ + enum tfa_error err = tfa_error_ok; + + switch (item) { + case TFA_MTP_OTC: + err = tfa98xx_set_mtp(tfa, (uint16_t)value, TFA98XX_KEY2_PROTECTED_MTP0_MTPOTC_MSK); + break; + case TFA_MTP_EX: + err = tfa98xx_set_mtp(tfa, (uint16_t)value, TFA98XX_KEY2_PROTECTED_MTP0_MTPEX_MSK); + break; + case TFA_MTP_RE25: + case TFA_MTP_RE25_PRIM: + if(tfa->tfa_family == 2) { + if((tfa->rev & 0xFF) == 0x88) + TFA_SET_BF(tfa, R25CL, (uint16_t)value); + else + TFA_SET_BF(tfa, R25C, (uint16_t)value); + } + break; + case TFA_MTP_RE25_SEC: + if((tfa->rev & 0xFF) == 0x88) { + TFA_SET_BF(tfa, R25CR, (uint16_t)value); + } else { + pr_debug("Error: Current device has no secondary Re25 channel \n"); + err = tfa_error_bad_param; + } + break; + case TFA_MTP_LOCK: + break; + } + + return err; +} + +int tfa_get_pga_gain(struct tfa_device *tfa) +{ + return TFA_GET_BF(tfa, SAAMGAIN); +} + +int tfa_set_pga_gain(struct tfa_device *tfa, uint16_t value) +{ + + return TFA_SET_BF(tfa, SAAMGAIN, value); +} + +int tfa_get_noclk(struct tfa_device *tfa) +{ + return TFA_GET_BF(tfa, NOCLK); +} + + +enum Tfa98xx_Error tfa_status(struct tfa_device *tfa) +{ + int value; + uint16_t val; + + /* + * check IC status bits: cold start + * and DSP watch dog bit to re init + */ + value = TFA_READ_REG(tfa, VDDS); /* STATUSREG */ + if (value < 0) + return -value; + val = (uint16_t)value; + + /* pr_debug("SYS_STATUS0: 0x%04x\n", val); */ + if (TFA_GET_BF_VALUE(tfa, ACS, val) || + TFA_GET_BF_VALUE(tfa, WDS, val)) { + + if (TFA_GET_BF_VALUE(tfa, ACS, val)) + pr_err("ERROR: ACS\n"); + if (TFA_GET_BF_VALUE(tfa, WDS, val)) + pr_err("ERROR: WDS\n"); + + return Tfa98xx_Error_DSP_not_running; + } + + if (TFA_GET_BF_VALUE(tfa, SPKS, val)) + pr_err("ERROR: SPKS\n"); + if (!TFA_GET_BF_VALUE(tfa, SWS, val)) + pr_err("ERROR: SWS\n"); + + /* Check secondary errors */ + if (!TFA_GET_BF_VALUE(tfa, CLKS, val) || + !TFA_GET_BF_VALUE(tfa, UVDS, val) || + !TFA_GET_BF_VALUE(tfa, OVDS, val) || + !TFA_GET_BF_VALUE(tfa, OTDS, val) || + !TFA_GET_BF_VALUE(tfa, PLLS, val) || + (!(tfa->daimap & Tfa98xx_DAI_TDM) && + !TFA_GET_BF_VALUE(tfa, VDDS, val)) ) + pr_err("Misc errors detected: STATUS_FLAG0 = 0x%x\n", val); + + if ((tfa->daimap & Tfa98xx_DAI_TDM) && (tfa->tfa_family == 2)) { + value = TFA_READ_REG(tfa, TDMERR); /* STATUS_FLAGS1 */ + if (value < 0) + return -value; + val = (uint16_t)value; + if (TFA_GET_BF_VALUE(tfa, TDMERR, val) || + TFA_GET_BF_VALUE(tfa, TDMLUTER, val)) + pr_err("TDM related errors: STATUS_FLAG1 = 0x%x\n", val); + } + + return Tfa98xx_Error_Ok; +} + +int tfa_plop_noise_interrupt(struct tfa_device *tfa, int profile, int vstep) +{ + enum Tfa98xx_Error err; + int no_clk=0; + + /* Remove sticky bit by reading it once */ + TFA_GET_BF(tfa, NOCLK); + + /* No clock detected */ + if (tfa_irq_get(tfa, tfa9912_irq_stnoclk)) { + no_clk = TFA_GET_BF(tfa, NOCLK); + + /* Detect for clock is lost! (clock is not stable) */ + if (no_clk == 1) { + /* Clock is lost. Set I2CR to remove POP noise */ + pr_info("No clock detected. Resetting the I2CR to avoid pop on 72! \n"); + err = tfa_dev_start(tfa, profile, vstep); + if (err != Tfa98xx_Error_Ok) { + pr_err("Error loading i2c registers (tfa_dev_start), err=%d\n", err); + } else { + pr_info("Setting i2c registers after I2CR succesfull\n"); + tfa_dev_set_state(tfa, TFA_STATE_UNMUTE); + } + + /* Remove sticky bit by reading it once */ + tfa_get_noclk(tfa); + + /* This is only for SAAM on the 72. + Since the NOCLK interrupt is only enabled for 72 this is the place + However: Not tested yet! But also does not harm normal flow! + */ + if (strstr(tfaContProfileName(tfa->cnt, tfa->dev_idx, profile), ".saam")) { + pr_info("Powering down from a SAAM profile, workaround PLMA4766 used! \n"); + TFA_SET_BF(tfa, PWDN, 1); + TFA_SET_BF(tfa, AMPE, 0); + TFA_SET_BF(tfa, SAMMODE, 0); + } + } + + /* If clk is stable set polarity to check for LOW (no clock)*/ + tfa_irq_set_pol(tfa, tfa9912_irq_stnoclk, (no_clk == 0)); + + /* clear interrupt */ + tfa_irq_clear(tfa, tfa9912_irq_stnoclk); + } + + /* return no_clk to know we called tfa_dev_start */ + return no_clk; +} + +void tfa_lp_mode_interrupt(struct tfa_device *tfa) +{ + const int irq_stclp0 = 36; /* FIXME: this 72 interrupt does not excist for 9912 */ + int lp0, lp1; + + if (tfa_irq_get(tfa, irq_stclp0)) { + lp0 = TFA_GET_BF(tfa, LP0); + if (lp0 > 0) { + pr_info("lowpower mode 0 detected\n"); + } else { + pr_info("lowpower mode 0 not detected\n"); + } + + tfa_irq_set_pol(tfa, irq_stclp0, (lp0 == 0)); + + /* clear interrupt */ + tfa_irq_clear(tfa, irq_stclp0); + } + + if (tfa_irq_get(tfa, tfa9912_irq_stclpr)) { + lp1 = TFA_GET_BF(tfa, LP1); + if (lp1 > 0) { + pr_info("lowpower mode 1 detected\n"); + } else { + pr_info("lowpower mode 1 not detected\n"); + } + + tfa_irq_set_pol(tfa, tfa9912_irq_stclpr, (lp1 == 0)); + + /* clear interrupt */ + tfa_irq_clear(tfa, tfa9912_irq_stclpr); + } +} diff --git a/sound/soc/codecs/tfa_init.c b/sound/soc/codecs/tfa_init.c new file mode 100644 index 000000000000..b4a692ad8283 --- /dev/null +++ b/sound/soc/codecs/tfa_init.c @@ -0,0 +1,1685 @@ +/* + * Copyright 2014-2017 NXP Semiconductors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "dbgprint.h" +#include "tfa_service.h" +#include "tfa_internal.h" +#include "tfa_container.h" +#include "tfa98xx_tfafieldnames.h" + +/* The CurrentSense4 registers are not in the datasheet */ +#define TFA98XX_CURRENTSENSE4_CTRL_CLKGATECFOFF (1<<2) +#define TFA98XX_CURRENTSENSE4 0x49 + +/***********************************************************************************/ +/* GLOBAL (Defaults) */ +/***********************************************************************************/ +static enum Tfa98xx_Error no_overload_function_available(struct tfa_device *tfa, int not_used) +{ + (void)tfa; + (void)not_used; + + return Tfa98xx_Error_Ok; +} + +static enum Tfa98xx_Error no_overload_function_available2(struct tfa_device *tfa) +{ + (void)tfa; + + return Tfa98xx_Error_Ok; +} + +/* tfa98xx_dsp_system_stable +* return: *ready = 1 when clocks are stable to allow DSP subsystem access +*/ +static enum Tfa98xx_Error tfa_dsp_system_stable(struct tfa_device *tfa, int *ready) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short status; + int value; + + /* check the contents of the STATUS register */ + value = TFA_READ_REG(tfa, AREFS); + if (value < 0) { + error = -value; + *ready = 0; + _ASSERT(error); /* an error here can be fatal */ + return error; + } + status = (unsigned short)value; + + /* check AREFS and CLKS: not ready if either is clear */ + *ready = !((TFA_GET_BF_VALUE(tfa, AREFS, status) == 0) + || (TFA_GET_BF_VALUE(tfa, CLKS, status) == 0)); + + return error; +} + +/* tfa98xx_toggle_mtp_clock + * Allows to stop clock for MTP/FAim needed for PLMA5505 */ +static enum Tfa98xx_Error tfa_faim_protect(struct tfa_device *tfa, int state) +{ + (void)tfa; + (void)state; + + return Tfa98xx_Error_Ok; +} + +/** Set internal oscillator into power down mode. + * + * This function is a worker for tfa98xx_set_osc_powerdown(). + * + * @param[in] tfa device description structure + * @param[in] state new state 0 - oscillator is on, 1 oscillator is off. + * + * @return Tfa98xx_Error_Ok when successfull, error otherwise. + */ +static enum Tfa98xx_Error tfa_set_osc_powerdown(struct tfa_device *tfa, int state) +{ + /* This function has no effect in general case, only for tfa9912 */ + (void)tfa; + (void)state; + + return Tfa98xx_Error_Ok; +} + +static enum Tfa98xx_Error tfa_dsp_reset(struct tfa_device *tfa, int state) +{ + /* generic function */ + TFA_SET_BF_VOLATILE(tfa, RST, (uint16_t)state); + + return Tfa98xx_Error_Ok; +} + +int tfa_set_swprofile(struct tfa_device *tfa, unsigned short new_value) +{ + int mtpk, active_value = tfa->profile; + + /* Also set the new value in the struct */ + tfa->profile = new_value; + + /* for TFA1 devices */ + /* it's in MTP shadow, so unlock if not done already */ + mtpk = TFA_GET_BF(tfa, MTPK); /* get current key */ + TFA_SET_BF_VOLATILE(tfa, MTPK, 0x5a); + TFA_SET_BF_VOLATILE(tfa, SWPROFIL, new_value); /* set current profile */ + TFA_SET_BF_VOLATILE(tfa, MTPK, (uint16_t)mtpk); /* restore key */ + + return active_value; +} + +static int tfa_get_swprofile(struct tfa_device *tfa) +{ + return TFA_GET_BF(tfa, SWPROFIL) - 1; +} + +static int tfa_set_swvstep(struct tfa_device *tfa, unsigned short new_value) +{ + int mtpk, active_value = tfa->vstep; + + /* Also set the new value in the struct */ + tfa->vstep = new_value; + + /* for TFA1 devices */ + /* it's in MTP shadow, so unlock if not done already */ + mtpk = TFA_GET_BF(tfa, MTPK); /* get current key */ + TFA_SET_BF_VOLATILE(tfa, MTPK, 0x5a); + TFA_SET_BF_VOLATILE(tfa, SWVSTEP, new_value); /* set current vstep */ + TFA_SET_BF_VOLATILE(tfa, MTPK, (uint16_t)mtpk); /* restore key */ + + return active_value; +} + +static int tfa_get_swvstep(struct tfa_device *tfa) +{ + int value = 0; + + if (tfa->vstep>0) + return tfa->vstep - 1; + + /* Set the new value in the hw register */ + value = TFA_GET_BF(tfa, SWVSTEP); + + /* Also set the new value in the struct */ + tfa->vstep = value; + + return value - 1; /* invalid if 0 */ +} + +static int tfa_get_mtpb(struct tfa_device *tfa) { + + int value=0; + + /* Set the new value in the hw register */ + value = TFA_GET_BF(tfa, MTPB); + + return value; +} + +static enum Tfa98xx_Error +tfa_set_mute_nodsp(struct tfa_device *tfa, int mute) +{ + (void)tfa; + (void)mute; + + return Tfa98xx_Error_Ok; +} + +void set_ops_defaults(struct tfa_device_ops *ops) +{ + /* defaults */ + ops->reg_read = tfa98xx_read_register16; + ops->reg_write = tfa98xx_write_register16; + ops->mem_read = tfa98xx_dsp_read_mem; + ops->mem_write = tfa98xx_dsp_write_mem_word; + ops->dsp_msg = tfa_dsp_msg; + ops->dsp_msg_read = tfa_dsp_msg_read; + ops->dsp_write_tables = no_overload_function_available; + ops->dsp_reset = tfa_dsp_reset; + ops->dsp_system_stable = tfa_dsp_system_stable; + ops->auto_copy_mtp_to_iic = no_overload_function_available2; + ops->factory_trimmer = no_overload_function_available2; + ops->set_swprof = tfa_set_swprofile; + ops->get_swprof = tfa_get_swprofile; + ops->set_swvstep = tfa_set_swvstep; + ops->get_swvstep = tfa_get_swvstep; + ops->get_mtpb = tfa_get_mtpb; + ops->set_mute = tfa_set_mute_nodsp; + ops->faim_protect = tfa_faim_protect; + ops->set_osc_powerdown = tfa_set_osc_powerdown; +} + +/***********************************************************************************/ +/* no TFA + * external DSP SB instance */ +/***********************************************************************************/ +static short tfanone_swvstep, swprof; //TODO emulate in hal plugin +static enum Tfa98xx_Error tfanone_dsp_system_stable(struct tfa_device *tfa, int *ready) +{ + (void)tfa; /* suppress warning */ + *ready = 1; /* assume always ready */ + + return Tfa98xx_Error_Ok; +} + +static int tfanone_set_swprofile(struct tfa_device *tfa, unsigned short new_value) +{ + int active_value = tfa_dev_get_swprof(tfa); + + /* Set the new value in the struct */ + tfa->profile = new_value; + + /* Set the new value in the hw register */ + swprof = new_value; + + return active_value; +} + +static int tfanone_get_swprofile(struct tfa_device *tfa) +{ + (void)tfa; /* suppress warning */ + return swprof; +} + +static int tfanone_set_swvstep(struct tfa_device *tfa, unsigned short new_value) +{ + int active_value = tfa_dev_get_swvstep(tfa); + + /* Set the new value in the struct */ + tfa->vstep = new_value; + + /* Set the new value in the hw register */ + tfanone_swvstep = new_value; + + return active_value; +} + +static int tfanone_get_swvstep(struct tfa_device *tfa) +{ + (void)tfa; /* suppress warning */ + return tfanone_swvstep; +} + +void tfanone_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->dsp_system_stable = tfanone_dsp_system_stable; + ops->set_swprof = tfanone_set_swprofile; + ops->get_swprof = tfanone_get_swprofile; + ops->set_swvstep = tfanone_set_swvstep; + ops->get_swvstep = tfanone_get_swvstep; + +} + +/***********************************************************************************/ +/* TFA9912 */ +/***********************************************************************************/ +static enum Tfa98xx_Error tfa9912_faim_protect(struct tfa_device *tfa, int status) +{ + enum Tfa98xx_Error ret = Tfa98xx_Error_Fail; + + if (tfa) { + if (status == 0 || status == 1) { + ret = -(tfa_set_bf(tfa, TFA9912_BF_SSFAIME, (uint16_t)status)); + } + } + + return ret; +} + +static enum Tfa98xx_Error tfa9912_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short value, xor; + + if (tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + /* Unlock keys to write settings */ + error = reg_write(tfa, 0x0F, 0x5A6B); + error = reg_read(tfa, 0xFB, &value); + xor = value ^ 0x005A; + error = reg_write(tfa, 0xA0, xor); + + /* The optimal settings */ + if (tfa->rev == 0x1a13) { + + /* ----- generated code start ----- */ + /* ----- version 1.41 ----- */ + reg_write(tfa, 0x00, 0x0255); //POR=0x0245 + reg_write(tfa, 0x01, 0x838a); //POR=0x83ca + reg_write(tfa, 0x02, 0x2dc8); //POR=0x2828 + reg_write(tfa, 0x05, 0x762a); //POR=0x766a + reg_write(tfa, 0x22, 0x543c); //POR=0x545c + reg_write(tfa, 0x26, 0x0100); //POR=0x0010 + reg_write(tfa, 0x51, 0x0000); //POR=0x0080 + reg_write(tfa, 0x52, 0x551c); //POR=0x1afc + reg_write(tfa, 0x53, 0x003e); //POR=0x001e + reg_write(tfa, 0x61, 0x000c); //POR=0x0018 + reg_write(tfa, 0x63, 0x0a96); //POR=0x0a9a + reg_write(tfa, 0x65, 0x0a82); //POR=0x0a8b + reg_write(tfa, 0x66, 0x0701); //POR=0x0700 + reg_write(tfa, 0x6c, 0x00d5); //POR=0x02d5 + reg_write(tfa, 0x70, 0x26f8); //POR=0x06e0 + reg_write(tfa, 0x71, 0x3074); //POR=0x2074 + reg_write(tfa, 0x75, 0x4484); //POR=0x4585 + reg_write(tfa, 0x76, 0x72ea); //POR=0x54a2 + reg_write(tfa, 0x83, 0x0716); //POR=0x0617 + reg_write(tfa, 0x89, 0x0013); //POR=0x0014 + reg_write(tfa, 0xb0, 0x4c08); //POR=0x4c00 + reg_write(tfa, 0xc6, 0x004e); //POR=0x000e /* PLMA5539: Please make sure bit 6 is always on! */ + /* ----- generated code end ----- */ + + /* PLMA5505: MTP key open makes vulanable for MTP corruption */ + tfa9912_faim_protect(tfa, 0); + } else { + pr_info("Warning: Optimal settings not found for device with revid = 0x%x \n", tfa->rev); + } + + return error; +} + +static enum Tfa98xx_Error tfa9912_tfa_dsp_write_tables(struct tfa_device *tfa, int sample_rate) +{ + unsigned char buffer[15] = { 0 }; + int size = 15 * sizeof(char); + + /* Write the fractional delay in the hardware register 'cs_frac_delay' */ + switch (sample_rate) { + case 0: /* 8kHz */ + TFA_SET_BF(tfa, FRACTDEL, 40); + break; + case 1: /* 11.025KHz */ + TFA_SET_BF(tfa, FRACTDEL, 38); + break; + case 2: /* 12kHz */ + TFA_SET_BF(tfa, FRACTDEL, 37); + break; + case 3: /* 16kHz */ + TFA_SET_BF(tfa, FRACTDEL, 59); + break; + case 4: /* 22.05KHz */ + TFA_SET_BF(tfa, FRACTDEL, 56); + break; + case 5: /* 24kHz */ + TFA_SET_BF(tfa, FRACTDEL, 56); + break; + case 6: /* 32kHz */ + TFA_SET_BF(tfa, FRACTDEL, 52); + break; + case 7: /* 44.1kHz */ + TFA_SET_BF(tfa, FRACTDEL, 48); + break; + case 8: + default:/* 48kHz */ + TFA_SET_BF(tfa, FRACTDEL, 46); + break; + } + + /* First copy the msg_id to the buffer */ + buffer[0] = (uint8_t)0; + buffer[1] = (uint8_t)MODULE_FRAMEWORK + 128; + buffer[2] = (uint8_t)FW_PAR_ID_SET_SENSES_DELAY; + + /* Required for all FS exept 8kHz (8kHz is all zero) */ + if (sample_rate != 0) { + buffer[5] = 1; /* Vdelay_P */ + buffer[8] = 0; /* Idelay_P */ + buffer[11] = 1; /* Vdelay_S */ + buffer[14] = 0; /* Idelay_S */ + } + + /* send SetSensesDelay msg */ + return dsp_msg(tfa, size, (char *)buffer); +} + +static enum Tfa98xx_Error tfa9912_factory_trimmer(struct tfa_device *tfa) +{ + unsigned short currentValue, delta; + int result; + + /* Factory trimming for the Boost converter */ + /* check if there is a correction needed */ + result = TFA_GET_BF(tfa, DCMCCAPI); + if (result) { + /* Get currentvalue of DCMCC and the Delta value */ + currentValue = (unsigned short)TFA_GET_BF(tfa, DCMCC); + delta = (unsigned short)TFA_GET_BF(tfa, USERDEF); + + /* check the sign bit (+/-) */ + result = TFA_GET_BF(tfa, DCMCCSB); + if (result == 0) { + /* Do not exceed the maximum value of 15 */ + if (currentValue + delta < 15) { + TFA_SET_BF_VOLATILE(tfa, DCMCC, currentValue + delta); + if (tfa->verbose) + pr_debug("Max coil current is set to: %d \n", currentValue + delta); + } + else { + TFA_SET_BF_VOLATILE(tfa, DCMCC, 15); + if (tfa->verbose) + pr_debug("Max coil current is set to: 15 \n"); + } + } + else if (result == 1) { + /* Do not exceed the minimum value of 0 */ + if (currentValue - delta > 0) { + TFA_SET_BF_VOLATILE(tfa, DCMCC, currentValue - delta); + if (tfa->verbose) + pr_debug("Max coil current is set to: %d \n", currentValue - delta); + } + else { + TFA_SET_BF_VOLATILE(tfa, DCMCC, 0); + if (tfa->verbose) + pr_debug("Max coil current is set to: 0 \n"); + } + } + } + + return Tfa98xx_Error_Ok; +} + +static enum Tfa98xx_Error tfa9912_auto_copy_mtp_to_iic(struct tfa_device *tfa) +{ + /* Set auto_copy_mtp_to_iic (bit 5 of A3) to 1. Workaround for 72, 88 and 9912/9892(see PLMA5290) */ + return reg_write(tfa, 0xA3, 0x20); +} + +static int tfa9912_set_swprofile(struct tfa_device *tfa, unsigned short new_value) +{ + int active_value = tfa_dev_get_swprof(tfa); + + /* Set the new value in the struct */ + tfa->profile = new_value; + + /* Set the new value in the hw register */ + tfa_set_bf_volatile(tfa, TFA9912_BF_SWPROFIL, new_value); + + return active_value; +} + +static int tfa9912_get_swprofile(struct tfa_device *tfa) +{ + return tfa_get_bf(tfa, TFA9912_BF_SWPROFIL) - 1; +} + +static int tfa9912_set_swvstep(struct tfa_device *tfa, unsigned short new_value) +{ + int active_value = tfa_dev_get_swvstep(tfa); + + /* Set the new value in the struct */ + tfa->vstep = new_value; + + /* Set the new value in the hw register */ + tfa_set_bf_volatile(tfa, TFA9912_BF_SWVSTEP, new_value); + + return active_value; +} + +static int tfa9912_get_swvstep(struct tfa_device *tfa) +{ + return tfa_get_bf(tfa, TFA9912_BF_SWVSTEP) - 1; +} + +static enum Tfa98xx_Error +tfa9912_set_mute(struct tfa_device *tfa, int mute) +{ + tfa_set_bf(tfa, TFA9912_BF_CFSM, (const uint16_t)mute); + + return Tfa98xx_Error_Ok; +} + +/* Maksimum value for combination of boost_voltage and vout calibration offset (see PLMA5322, PLMA5528). */ +#define TFA9912_VBOOST_MAX 57 +#define TFA9912_CALIBR_BOOST_MAX 63 +#define TFA9912_DCDCCNT6_REG (TFA9912_BF_DCVOF >> 8) +#define TFA9912_CALIBR_REG 0xf1 + +static uint16_t tfa9912_vboost_fixup(struct tfa_device *tfa, uint16_t dcdc_cnt6) +{ + unsigned short cal_offset; + unsigned short boost_v_1st, boost_v_2nd; + uint16_t new_dcdc_cnt6; + + /* Get current calibr_vout_offset, this register is not supported by bitfields */ + reg_read(tfa, TFA9912_CALIBR_REG, &cal_offset); + cal_offset = (cal_offset & 0x001f); + new_dcdc_cnt6 = dcdc_cnt6; + + /* Get current boost_volatage values */ + boost_v_1st = tfa_get_bf_value(TFA9912_BF_DCVOF, new_dcdc_cnt6); + boost_v_2nd = tfa_get_bf_value(TFA9912_BF_DCVOS, new_dcdc_cnt6); + + /* Check boost voltages */ + if (boost_v_1st > TFA9912_VBOOST_MAX) + boost_v_1st = TFA9912_VBOOST_MAX; + + if (boost_v_2nd > TFA9912_VBOOST_MAX) + boost_v_2nd = TFA9912_VBOOST_MAX; + + /* Recalculate values, max for the sum is TFA9912_CALIBR_BOOST_MAX */ + if (boost_v_1st + cal_offset > TFA9912_CALIBR_BOOST_MAX) + boost_v_1st = TFA9912_CALIBR_BOOST_MAX - cal_offset; + + if (boost_v_2nd + cal_offset > TFA9912_CALIBR_BOOST_MAX) + boost_v_2nd = TFA9912_CALIBR_BOOST_MAX - cal_offset; + + tfa_set_bf_value(TFA9912_BF_DCVOF, boost_v_1st, &new_dcdc_cnt6); + tfa_set_bf_value(TFA9912_BF_DCVOS, boost_v_2nd, &new_dcdc_cnt6); + + /* Change register value only when it's neccesary */ + if (new_dcdc_cnt6 != dcdc_cnt6) { + if (tfa->verbose) + pr_debug("tfa9912: V boost fixup applied. Old 0x%04x, new 0x%04x\n", + dcdc_cnt6, new_dcdc_cnt6); + dcdc_cnt6 = new_dcdc_cnt6; + } + + return dcdc_cnt6; +} + +/* PLMA5322, PLMA5528 - Limit values of DCVOS and DCVOF to range specified in datasheet. */ +enum Tfa98xx_Error tfa9912_reg_write(struct tfa_device *tfa, unsigned char subaddress, unsigned short value) +{ + if (subaddress == TFA9912_DCDCCNT6_REG) { + /* Correct V boost (first and secondary) to ensure 12V is not exceeded. */ + value = tfa9912_vboost_fixup(tfa, value); + } + + return tfa98xx_write_register16(tfa, subaddress, value); +} + +/** Set internal oscillator into power down mode for TFA9912. +* +* This function is a worker for tfa98xx_set_osc_powerdown(). +* +* @param[in] tfa device description structure +* @param[in] state new state 0 - oscillator is on, 1 oscillator is off. +* +* @return Tfa98xx_Error_Ok when successfull, error otherwise. +*/ +static enum Tfa98xx_Error tfa9912_set_osc_powerdown(struct tfa_device *tfa, int state) +{ + if (state == 1 || state == 0) { + return -tfa_set_bf(tfa, TFA9912_BF_MANAOOSC, (uint16_t) state); + } + + return Tfa98xx_Error_Bad_Parameter; +} + +void tfa9912_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9912_specific; + /* PLMA5322, PLMA5528 - Limits values of DCVOS and DCVOF. */ + ops->reg_write = tfa9912_reg_write; + ops->dsp_write_tables = tfa9912_tfa_dsp_write_tables; + ops->factory_trimmer = tfa9912_factory_trimmer; + ops->auto_copy_mtp_to_iic = tfa9912_auto_copy_mtp_to_iic; + ops->set_swprof = tfa9912_set_swprofile; + ops->get_swprof = tfa9912_get_swprofile; + ops->set_swvstep = tfa9912_set_swvstep; + ops->get_swvstep = tfa9912_get_swvstep; + ops->set_mute = tfa9912_set_mute; + ops->faim_protect = tfa9912_faim_protect; + ops->set_osc_powerdown = tfa9912_set_osc_powerdown; +} + +/***********************************************************************************/ +/* TFA9872 */ +/***********************************************************************************/ +static enum Tfa98xx_Error tfa9872_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + uint16_t MANAOOSC = 0x0140; /* version 17 */ + unsigned short value, xor; + + if (tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + /* Unlock key 1 and 2 */ + error = reg_write(tfa, 0x0F, 0x5A6B); + error = reg_read(tfa, 0xFB, &value); + xor = value ^ 0x005A; + error = reg_write(tfa, 0xA0, xor); + tfa98xx_key2(tfa, 0); + + switch (tfa->rev) { + case 0x1a72: + case 0x2a72: + /* ----- generated code start ----- */ + /* ----- version 26 ----- */ + reg_write(tfa, 0x00, 0x1801); //POR=0x0001 + reg_write(tfa, 0x02, 0x2dc8); //POR=0x2028 + reg_write(tfa, 0x20, 0x0890); //POR=0x2890 + reg_write(tfa, 0x22, 0x043c); //POR=0x045c + reg_write(tfa, 0x51, 0x0000); //POR=0x0080 + reg_write(tfa, 0x52, 0x1a1c); //POR=0x7ae8 + reg_write(tfa, 0x58, 0x161c); //POR=0x101c + reg_write(tfa, 0x61, 0x0198); //POR=0x0000 + reg_write(tfa, 0x65, 0x0a8b); //POR=0x0a9a + reg_write(tfa, 0x70, 0x07f5); //POR=0x06e6 + reg_write(tfa, 0x74, 0xcc84); //POR=0xd823 + reg_write(tfa, 0x82, 0x01ed); //POR=0x000d + reg_write(tfa, 0x83, 0x0014); //POR=0x0013 + reg_write(tfa, 0x84, 0x0021); //POR=0x0020 + reg_write(tfa, 0x85, 0x0001); //POR=0x0003 + /* ----- generated code end ----- */ + break; + case 0x1b72: + case 0x2b72: + case 0x3b72: + /* ----- generated code start ----- */ + /* ----- TFA9872 Probus Registers map N1B2 - Version 21 (10/19/2016) ----- */ + reg_write(tfa, 0x02, 0x2dc8); //POR=0x2828 + reg_write(tfa, 0x20, 0x0890); //POR=0x2890 + reg_write(tfa, 0x22, 0x043c); //POR=0x045c + reg_write(tfa, 0x23, 0x0001); //POR=0x0003 + reg_write(tfa, 0x51, 0x0000); //POR=0x0080 + reg_write(tfa, 0x52, 0x5a1c); //POR=0x7a08 + reg_write(tfa, 0x61, 0x0198); //POR=0x0000 + reg_write(tfa, 0x63, 0x0a9a); //POR=0x0a93 + reg_write(tfa, 0x65, 0x0a82); //POR=0x0a8d + reg_write(tfa, 0x6f, 0x01e3); //POR=0x02e4 + reg_write(tfa, 0x70, 0x06fd); //POR=0x06e6 + reg_write(tfa, 0x71, 0x307e); //POR=0x207e + reg_write(tfa, 0x74, 0xcc84); //POR=0xd913 + reg_write(tfa, 0x75, 0x1132); //POR=0x118a + reg_write(tfa, 0x82, 0x01ed); //POR=0x000d + reg_write(tfa, 0x83, 0x001a); //POR=0x0013 + /* ----- generated code end ----- */ + break; + default: + pr_info("\nWarning: Optimal settings not found for device with revid = 0x%x \n", tfa->rev); + break; + } + + /* Turn off the osc1m to save power: PLMA4928 */ + error = tfa_set_bf(tfa, MANAOOSC, 1); + + /* Bypass OVP by setting bit 3 from register 0xB0 (bypass_ovp=1): PLMA5258 */ + error = reg_read(tfa, 0xB0, &value); + value |= 1 << 3; + error = reg_write(tfa, 0xB0, value); + + return error; +} + +static enum Tfa98xx_Error tfa9872_auto_copy_mtp_to_iic(struct tfa_device *tfa) +{ + /* Set auto_copy_mtp_to_iic (bit 5 of A3) to 1. Workaround for 72 and 88 (see PLMA5290) */ + return reg_write(tfa, 0xA3, 0x20); +} + +static int tfa9872_set_swprofile(struct tfa_device *tfa, unsigned short new_value) +{ + int active_value = tfa_dev_get_swprof(tfa); + + /* Set the new value in the struct */ + tfa->profile = new_value; + + /* Set the new value in the hw register */ + tfa_set_bf_volatile(tfa, TFA9872_BF_SWPROFIL, new_value); + + return active_value; +} + +static int tfa9872_get_swprofile(struct tfa_device *tfa) +{ + return tfa_get_bf(tfa, TFA9872_BF_SWPROFIL) - 1; +} + +static int tfa9872_set_swvstep(struct tfa_device *tfa, unsigned short new_value) +{ + int active_value = tfa_dev_get_swvstep(tfa); + + /* Set the new value in the struct */ + tfa->vstep = new_value; + + /* Set the new value in the hw register */ + tfa_set_bf_volatile(tfa, TFA9872_BF_SWVSTEP, new_value); + + return active_value; +} + +static int tfa9872_get_swvstep(struct tfa_device *tfa) +{ + return tfa_get_bf(tfa, TFA9872_BF_SWVSTEP) - 1; +} + +void tfa9872_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9872_specific; + ops->auto_copy_mtp_to_iic = tfa9872_auto_copy_mtp_to_iic; + ops->set_swprof = tfa9872_set_swprofile; + ops->get_swprof = tfa9872_get_swprofile; + ops->set_swvstep = tfa9872_set_swvstep; + ops->get_swvstep = tfa9872_get_swvstep; + ops->set_mute = tfa_set_mute_nodsp; +} + +/***********************************************************************************/ +/* TFA9874 */ +/***********************************************************************************/ + +static enum Tfa98xx_Error tfa9874_faim_protect(struct tfa_device *tfa, int status) +{ + enum Tfa98xx_Error ret = Tfa98xx_Error_Ok; + /* 0b = FAIM protection enabled 1b = FAIM protection disabled*/ + ret = tfa_set_bf_volatile(tfa, TFA9874_BF_OPENMTP, (uint16_t)(status)); + return ret; +} + + +static enum Tfa98xx_Error tfa9874_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short value, xor; + + if (tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + /* Unlock key 1 and 2 */ + error = reg_write(tfa, 0x0F, 0x5A6B); + error = reg_read(tfa, 0xFB, &value); + xor = value ^ 0x005A; + error = reg_write(tfa, 0xA0, xor); + tfa98xx_key2(tfa, 0); + + switch (tfa->rev) { + case 0x0a74: /* Initial revision ID */ + /* ----- generated code start ----- */ + /* V25 */ + reg_write(tfa, 0x02, 0x22a8); //POR=0x25c8 + reg_write(tfa, 0x51, 0x0020); //POR=0x0000 + reg_write(tfa, 0x52, 0x57dc); //POR=0x56dc + reg_write(tfa, 0x58, 0x16a4); //POR=0x1614 + reg_write(tfa, 0x61, 0x0110); //POR=0x0198 + reg_write(tfa, 0x66, 0x0701); //POR=0x0700 + reg_write(tfa, 0x6f, 0x00a3); //POR=0x01a3 + reg_write(tfa, 0x70, 0x07f8); //POR=0x06f8 + reg_write(tfa, 0x73, 0x0007); //POR=0x0005 + reg_write(tfa, 0x74, 0x5068); //POR=0xcc80 + reg_write(tfa, 0x75, 0x0d28); //POR=0x1138 + reg_write(tfa, 0x83, 0x0594); //POR=0x061a + reg_write(tfa, 0x84, 0x0001); //POR=0x0021 + reg_write(tfa, 0x85, 0x0001); //POR=0x0003 + reg_write(tfa, 0x88, 0x0000); //POR=0x0002 + reg_write(tfa, 0xc4, 0x2001); //POR=0x0001 + /* ----- generated code end ----- */ + break; + case 0x0b74: + /* ----- generated code start ----- */ + /* V1.6 */ + reg_write(tfa, 0x02, 0x22a8); //POR=0x25c8 + reg_write(tfa, 0x51, 0x0020); //POR=0x0000 + reg_write(tfa, 0x52, 0x57dc); //POR=0x56dc + reg_write(tfa, 0x58, 0x16a4); //POR=0x1614 + reg_write(tfa, 0x61, 0x0110); //POR=0x0198 + reg_write(tfa, 0x66, 0x0701); //POR=0x0700 + reg_write(tfa, 0x6f, 0x00a3); //POR=0x01a3 + reg_write(tfa, 0x70, 0x07f8); //POR=0x06f8 + reg_write(tfa, 0x73, 0x0047); //POR=0x0045 + reg_write(tfa, 0x74, 0x5068); //POR=0xcc80 + reg_write(tfa, 0x75, 0x0d28); //POR=0x1138 + reg_write(tfa, 0x83, 0x0595); //POR=0x061a + reg_write(tfa, 0x84, 0x0001); //POR=0x0021 + reg_write(tfa, 0x85, 0x0001); //POR=0x0003 + reg_write(tfa, 0x88, 0x0000); //POR=0x0002 + reg_write(tfa, 0xc4, 0x2001); //POR=0x0001 + /* ----- generated code end ----- */ + break; + case 0x0c74: + /* ----- generated code start ----- */ + reg_write(tfa, 0x02, 0x22a8); //POR=0x25c8 + reg_write(tfa, 0x51, 0x0020); //POR=0x0000 + reg_write(tfa, 0x52, 0x57dc); //POR=0x56dc + reg_write(tfa, 0x58, 0x16a4); //POR=0x1614 + reg_write(tfa, 0x61, 0x0110); //POR=0x0198 + reg_write(tfa, 0x6f, 0x00a3); //POR=0x01a3 + reg_write(tfa, 0x70, 0x07f8); //POR=0x06f8 + reg_write(tfa, 0x73, 0x0047); //POR=0x0045 + reg_write(tfa, 0x74, 0x5068); //POR=0xcc80 + reg_write(tfa, 0x75, 0x0d28); //POR=0x1138 + reg_write(tfa, 0x83, 0x0595); //POR=0x061a + reg_write(tfa, 0x84, 0x0001); //POR=0x0021 + reg_write(tfa, 0x85, 0x0001); //POR=0x0003 + reg_write(tfa, 0x88, 0x0000); //POR=0x0002 + reg_write(tfa, 0xc4, 0x2001); //POR=0x0001 + /* ----- generated code end ----- */ + break; + default: + pr_info("\nWarning: Optimal settings not found for device with revid = 0x%x \n", tfa->rev); + break; + } + + return error; +} + +static int tfa9874_set_swprofile(struct tfa_device *tfa, unsigned short new_value) +{ + int active_value = tfa_dev_get_swprof(tfa); + + /* Set the new value in the struct */ + tfa->profile = new_value; + + /* Set the new value in the hw register */ + tfa_set_bf_volatile(tfa, TFA9874_BF_SWPROFIL, new_value); + + return active_value; +} + +static int tfa9874_get_swprofile(struct tfa_device *tfa) +{ + return tfa_get_bf(tfa, TFA9874_BF_SWPROFIL) - 1; +} + +static int tfa9874_set_swvstep(struct tfa_device *tfa, unsigned short new_value) +{ + int active_value = tfa_dev_get_swvstep(tfa); + + /* Set the new value in the struct */ + tfa->vstep = new_value; + + /* Set the new value in the hw register */ + tfa_set_bf_volatile(tfa, TFA9874_BF_SWVSTEP, new_value); + + return active_value; +} + +static int tfa9874_get_swvstep(struct tfa_device *tfa) +{ + return tfa_get_bf(tfa, TFA9874_BF_SWVSTEP) - 1; +} + +/* tfa98xx_dsp_system_stable +* return: *ready = 1 when clocks are stable to allow DSP subsystem access +*/ +static enum Tfa98xx_Error tfa9874_dsp_system_stable(struct tfa_device *tfa, int *ready) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + /* check CLKS: ready if set */ + *ready = tfa_get_bf(tfa, TFA9874_BF_CLKS)==1; + + return error; +} + +static int tfa9874_get_mtpb(struct tfa_device *tfa) { + + int value; + value = tfa_get_bf(tfa, TFA9874_BF_MTPB); + return value; +} + +void tfa9874_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9874_specific; + ops->set_swprof = tfa9874_set_swprofile; + ops->get_swprof = tfa9874_get_swprofile; + ops->set_swvstep = tfa9874_set_swvstep; + ops->get_swvstep = tfa9874_get_swvstep; + ops->dsp_system_stable = tfa9874_dsp_system_stable; + ops->faim_protect = tfa9874_faim_protect; + ops->get_mtpb = tfa9874_get_mtpb; + ops->set_mute = tfa_set_mute_nodsp; +} + +/***********************************************************************************/ +/* TFA9888 */ +/***********************************************************************************/ +static enum Tfa98xx_Error tfa9888_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short value, xor; + int patch_version; + + if (tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + /* Unlock keys to write settings */ + error = reg_write(tfa, 0x0F, 0x5A6B); + error = reg_read(tfa, 0xFB, &value); + xor = value ^ 0x005A; + error = reg_write(tfa, 0xA0, xor); + + /* Only N1C2 is supported */ + /* ----- generated code start ----- */ + /* --------- Version v1 ---------- */ + if (tfa->rev == 0x2c88) { + reg_write(tfa, 0x00, 0x164d); //POR=0x064d + reg_write(tfa, 0x01, 0x828b); //POR=0x92cb + reg_write(tfa, 0x02, 0x1dc8); //POR=0x1828 + reg_write(tfa, 0x0e, 0x0080); //POR=0x0000 + reg_write(tfa, 0x20, 0x089e); //POR=0x0890 + reg_write(tfa, 0x22, 0x543c); //POR=0x545c + reg_write(tfa, 0x23, 0x0006); //POR=0x0000 + reg_write(tfa, 0x24, 0x0014); //POR=0x0000 + reg_write(tfa, 0x25, 0x000a); //POR=0x0000 + reg_write(tfa, 0x26, 0x0100); //POR=0x0000 + reg_write(tfa, 0x28, 0x1000); //POR=0x0000 + reg_write(tfa, 0x51, 0x0000); //POR=0x00c0 + reg_write(tfa, 0x52, 0xfafe); //POR=0xbaf6 + reg_write(tfa, 0x70, 0x3ee4); //POR=0x3ee6 + reg_write(tfa, 0x71, 0x1074); //POR=0x3074 + reg_write(tfa, 0x83, 0x0014); //POR=0x0013 + /* ----- generated code end ----- */ + } else { + pr_info("Warning: Optimal settings not found for device with revid = 0x%x \n", tfa->rev); + } + + patch_version = tfa_cnt_get_patch_version(tfa); + if (patch_version >= 0x060401) + tfa->partial_enable = 1; + + return error; +} + +static enum Tfa98xx_Error tfa9888_tfa_dsp_write_tables(struct tfa_device *tfa, int sample_rate) +{ + unsigned char buffer[15] = { 0 }; + int size = 15 * sizeof(char); + + /* Write the fractional delay in the hardware register 'cs_frac_delay' */ + switch (sample_rate) { + case 0: /* 8kHz */ + TFA_SET_BF(tfa, FRACTDEL, 40); + break; + case 1: /* 11.025KHz */ + TFA_SET_BF(tfa, FRACTDEL, 38); + break; + case 2: /* 12kHz */ + TFA_SET_BF(tfa, FRACTDEL, 37); + break; + case 3: /* 16kHz */ + TFA_SET_BF(tfa, FRACTDEL, 59); + break; + case 4: /* 22.05KHz */ + TFA_SET_BF(tfa, FRACTDEL, 56); + break; + case 5: /* 24kHz */ + TFA_SET_BF(tfa, FRACTDEL, 56); + break; + case 6: /* 32kHz */ + TFA_SET_BF(tfa, FRACTDEL, 52); + break; + case 7: /* 44.1kHz */ + TFA_SET_BF(tfa, FRACTDEL, 48); + break; + case 8: + default:/* 48kHz */ + TFA_SET_BF(tfa, FRACTDEL, 46); + break; + } + + /* First copy the msg_id to the buffer */ + buffer[0] = (uint8_t)0; + buffer[1] = (uint8_t)MODULE_FRAMEWORK + 128; + buffer[2] = (uint8_t)FW_PAR_ID_SET_SENSES_DELAY; + + /* Required for all FS exept 8kHz (8kHz is all zero) */ + if (sample_rate != 0) { + buffer[5] = 1; /* Vdelay_P */ + buffer[8] = 0; /* Idelay_P */ + buffer[11] = 1; /* Vdelay_S */ + buffer[14] = 0; /* Idelay_S */ + } + + /* send SetSensesDelay msg */ + return dsp_msg(tfa, size, (char *)buffer); +} + +static enum Tfa98xx_Error tfa9888_auto_copy_mtp_to_iic(struct tfa_device *tfa) +{ + /* Set auto_copy_mtp_to_iic (bit 5 of A3) to 1. Workaround for 72 and 88 (see PLMA5290) */ + return reg_write(tfa, 0xA3, 0x20); +} + +static enum Tfa98xx_Error tfa9888_factory_trimmer(struct tfa_device *tfa) +{ + unsigned short currentValue, delta; + int result; + + /* Factory trimming for the Boost converter */ + /* check if there is a correction needed */ + result = TFA_GET_BF(tfa, DCMCCAPI); + if (result) { + /* Get currentvalue of DCMCC and the Delta value */ + currentValue = (unsigned short)TFA_GET_BF(tfa, DCMCC); + delta = (unsigned short)TFA_GET_BF(tfa, USERDEF); + + /* check the sign bit (+/-) */ + result = TFA_GET_BF(tfa, DCMCCSB); + if (result == 0) { + /* Do not exceed the maximum value of 15 */ + if (currentValue + delta < 15) { + TFA_SET_BF_VOLATILE(tfa, DCMCC, currentValue + delta); + if (tfa->verbose) + pr_debug("Max coil current is set to: %d \n", currentValue + delta); + } + else { + TFA_SET_BF_VOLATILE(tfa, DCMCC, 15); + if (tfa->verbose) + pr_debug("Max coil current is set to: 15 \n"); + } + } + else if (result == 1) { + /* Do not exceed the minimum value of 0 */ + if (currentValue - delta > 0) { + TFA_SET_BF_VOLATILE(tfa, DCMCC, currentValue - delta); + if (tfa->verbose) + pr_debug("Max coil current is set to: %d \n", currentValue - delta); + } + else { + TFA_SET_BF_VOLATILE(tfa, DCMCC, 0); + if (tfa->verbose) + pr_debug("Max coil current is set to: 0 \n"); + } + } + } + + return Tfa98xx_Error_Ok; +} + +static enum Tfa98xx_Error +tfa9888_set_mute(struct tfa_device *tfa, int mute) +{ + TFA_SET_BF(tfa, CFSMR, (const uint16_t)mute); + TFA_SET_BF(tfa, CFSML, (const uint16_t)mute); + + return Tfa98xx_Error_Ok; +} + +void tfa9888_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9888_specific; + ops->dsp_write_tables = tfa9888_tfa_dsp_write_tables; + ops->auto_copy_mtp_to_iic = tfa9888_auto_copy_mtp_to_iic; + ops->factory_trimmer = tfa9888_factory_trimmer; + ops->set_mute = tfa9888_set_mute; +} + +/***********************************************************************************/ +/* TFA9896 */ +/***********************************************************************************/ +static enum Tfa98xx_Error tfa9896_faim_protect(struct tfa_device *tfa, int status) +{ + enum Tfa98xx_Error ret = Tfa98xx_Error_Ok; + + if ( (tfa->rev == 0x2b96) || (tfa->rev == 0x3b96) ) { + ret = tfa_set_bf_volatile(tfa, TFA9896_BF_OPENMTP, (uint16_t)status); + } + + return ret; +} + +/***********************************************************************************/ +/* TFA9896 */ +/***********************************************************************************/ +static enum Tfa98xx_Error tfa9896_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short check_value; + + if (tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + /* all i2C registers must already set to default POR value */ + + /* $48:[3] - 1 ==> 0; iddqtestbst - default value changed. + * When Iddqtestbst is set to "0", the slewrate is reduced. + * This will lower the overshoot on IN-B to avoid NMOS damage of booster. + */ + if (tfa->rev == 0x1b96) { + /* ----- generated code start v17 ----- */ + reg_write(tfa, 0x06, 0x000b); //POR=0x0001 + reg_write(tfa, 0x07, 0x3e7f); //POR=0x1e7f + reg_write(tfa, 0x0a, 0x0d8a); //POR=0x0592 + reg_write(tfa, 0x48, 0x0300); //POR=0x0308 + reg_write(tfa, 0x88, 0x0100); //POR=0x0000 + /* ----- generated code end ----- */ + } + else if (tfa->rev == 0x2b96) { + /* ----- generated code start ----- v1*/ + reg_write(tfa, 0x06, 0x000b); //POR=0x0001 + reg_write(tfa, 0x07, 0x3e7f); //POR=0x1e7f + reg_write(tfa, 0x0a, 0x0d8a); //POR=0x0592 + reg_write(tfa, 0x48, 0x0300); //POR=0x0308 + reg_write(tfa, 0x88, 0x0100); //POR=0x0000 + /* ----- generated code end ----- */ + } + else if (tfa->rev == 0x3b96) { + /* ----- generated code start ----- v1*/ + reg_write(tfa, 0x06, 0x000b); //POR=0x0001 + reg_write(tfa, 0x07, 0x3e7f); //POR=0x1e7f + reg_write(tfa, 0x0a, 0x0d8a); //POR=0x0592 + reg_write(tfa, 0x48, 0x0300); //POR=0x0308 + reg_write(tfa, 0x88, 0x0100); //POR=0x0000 + /* ----- generated code end ----- */ + } + /* $49:[0] - 1 ==> 0; CLIP - default value changed. 0 means CLIPPER on */ + error = reg_read(tfa, 0x49, &check_value); + check_value &= ~0x1; + error = reg_write(tfa, 0x49, check_value); + return error; +} + +/* +* the int24 values for the vsfw delay table +*/ +static unsigned char tfa9896_vsfwdelay_table[] = { + 0,0,2, /* Index 0 - Current/Volt Fractional Delay for 8KHz */ + 0,0,0, /* Index 1 - Current/Volt Fractional Delay for 11KHz */ + 0,0,0, /* Index 2 - Current/Volt Fractional Delay for 12KHz */ + 0,0,2, /* Index 3 - Current/Volt Fractional Delay for 16KHz */ + 0,0,2, /* Index 4 - Current/Volt Fractional Delay for 22KHz */ + 0,0,2, /* Index 5 - Current/Volt Fractional Delay for 24KHz */ + 0,0,2, /* Index 6 - Current/Volt Fractional Delay for 32KHz */ + 0,0,2, /* Index 7 - Current/Volt Fractional Delay for 44KHz */ + 0,0,3 /* Index 8 - Current/Volt Fractional Delay for 48KHz */ +}; + +/* +* TODO make this tfa98xx +* Note that the former products write this table via the patch +* so moving this to the tfa98xx API requires also updating all patches +*/ +static enum Tfa98xx_Error tfa9896_dsp_write_vsfwdelay_table(struct tfa_device *tfa) +{ + return tfa_dsp_cmd_id_write(tfa, MODULE_FRAMEWORK, TFA1_FW_PAR_ID_SET_CURRENT_DELAY, sizeof(tfa9896_vsfwdelay_table), tfa9896_vsfwdelay_table); +} + +/* +* The int24 values for the fracdelay table +* For now applicable only for 8 and 48 kHz +*/ +static unsigned char tfa9896_cvfracdelay_table[] = { + 0,0,51, /* Index 0 - Current/Volt Fractional Delay for 8KHz */ + 0,0, 0, /* Index 1 - Current/Volt Fractional Delay for 11KHz */ + 0,0, 0, /* Index 2 - Current/Volt Fractional Delay for 12KHz */ + 0,0,38, /* Index 3 - Current/Volt Fractional Delay for 16KHz */ + 0,0,34, /* Index 4 - Current/Volt Fractional Delay for 22KHz */ + 0,0,33, /* Index 5 - Current/Volt Fractional Delay for 24KHz */ + 0,0,11, /* Index 6 - Current/Volt Fractional Delay for 32KHz */ + 0,0,2, /* Index 7 - Current/Volt Fractional Delay for 44KHz */ + 0,0,62 /* Index 8 - Current/Volt Fractional Delay for 48KHz */ +}; + +static enum Tfa98xx_Error tfa9896_dsp_write_cvfracdelay_table(struct tfa_device *tfa) +{ + return tfa_dsp_cmd_id_write(tfa, MODULE_FRAMEWORK, TFA1_FW_PAR_ID_SET_CURFRAC_DELAY, sizeof(tfa9896_cvfracdelay_table), tfa9896_cvfracdelay_table);; +} + +static enum Tfa98xx_Error tfa9896_tfa_dsp_write_tables(struct tfa_device *tfa, int sample_rate) +{ + enum Tfa98xx_Error error; + + /* Not used for max1! */ + (void)sample_rate; + + error = tfa9896_dsp_write_vsfwdelay_table(tfa); + if (error == Tfa98xx_Error_Ok) { + error = tfa9896_dsp_write_cvfracdelay_table(tfa); + } + + return error; +} + +void tfa9896_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9896_specific; + ops->dsp_write_tables = tfa9896_tfa_dsp_write_tables; + ops->faim_protect = tfa9896_faim_protect; +} + +/***********************************************************************************/ +/* TFA9897 */ +/***********************************************************************************/ +static enum Tfa98xx_Error tfa9897_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short check_value; + + if (tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + /* all i2C registers must already set to default POR value */ + + /* $48:[3] - 1 ==> 0; iddqtestbst - default value changed. + * When Iddqtestbst is set to "0", the slewrate is reduced. + * This will lower the overshoot on IN-B to avoid NMOS damage of booster */ + error = reg_write(tfa, 0x48, 0x0300); /* POR value = 0x308 */ + + /* $49:[0] - 1 ==> 0; CLIP - default value changed. 0 means CLIPPER on */ + error = reg_read(tfa, 0x49, &check_value); + check_value &= ~0x1; + error = reg_write(tfa, 0x49, check_value); + + return error; +} + +/* +* the int24 values for the vsfw delay table +*/ +static unsigned char tfa9897_vsfwdelay_table[] = { + 0,0,2, /* Index 0 - Current/Volt Fractional Delay for 8KHz */ + 0,0,0, /* Index 1 - Current/Volt Fractional Delay for 11KHz */ + 0,0,0, /* Index 2 - Current/Volt Fractional Delay for 12KHz */ + 0,0,2, /* Index 3 - Current/Volt Fractional Delay for 16KHz */ + 0,0,2, /* Index 4 - Current/Volt Fractional Delay for 22KHz */ + 0,0,2, /* Index 5 - Current/Volt Fractional Delay for 24KHz */ + 0,0,2, /* Index 6 - Current/Volt Fractional Delay for 32KHz */ + 0,0,2, /* Index 7 - Current/Volt Fractional Delay for 44KHz */ + 0,0,3 /* Index 8 - Current/Volt Fractional Delay for 48KHz */ +}; + +/* +* TODO make this tfa98xx +* Note that the former products write this table via the patch +* so moving this to the tfa98xx API requires also updating all patches +*/ +static enum Tfa98xx_Error tfa9897_dsp_write_vsfwdelay_table(struct tfa_device *tfa) +{ + return tfa_dsp_cmd_id_write(tfa, MODULE_FRAMEWORK, TFA1_FW_PAR_ID_SET_CURRENT_DELAY, sizeof(tfa9897_vsfwdelay_table), tfa9897_vsfwdelay_table);; +} + +/* +* The int24 values for the fracdelay table +* For now applicable only for 8 and 48 kHz +*/ +static unsigned char tfa9897_cvfracdelay_table[] = { + 0,0,51, /* Index 0 - Current/Volt Fractional Delay for 8KHz */ + 0,0, 0, /* Index 1 - Current/Volt Fractional Delay for 11KHz */ + 0,0, 0, /* Index 2 - Current/Volt Fractional Delay for 12KHz */ + 0,0,38, /* Index 3 - Current/Volt Fractional Delay for 16KHz */ + 0,0,34, /* Index 4 - Current/Volt Fractional Delay for 22KHz */ + 0,0,33, /* Index 5 - Current/Volt Fractional Delay for 24KHz */ + 0,0,11, /* Index 6 - Current/Volt Fractional Delay for 32KHz */ + 0,0,2, /* Index 7 - Current/Volt Fractional Delay for 44KHz */ + 0,0,62 /* Index 8 - Current/Volt Fractional Delay for 48KHz */ +}; + +static enum Tfa98xx_Error tfa9897_dsp_write_cvfracdelay_table(struct tfa_device *tfa) +{ + return tfa_dsp_cmd_id_write(tfa, MODULE_FRAMEWORK, TFA1_FW_PAR_ID_SET_CURFRAC_DELAY, sizeof(tfa9897_cvfracdelay_table), tfa9897_cvfracdelay_table);; +} + +static enum Tfa98xx_Error tfa9897_tfa_dsp_write_tables(struct tfa_device *tfa, int sample_rate) +{ + enum Tfa98xx_Error error; + + /* Not used for max1! */ + (void)sample_rate; + + error = tfa9897_dsp_write_vsfwdelay_table(tfa); + if (error == Tfa98xx_Error_Ok) { + error = tfa9897_dsp_write_cvfracdelay_table(tfa); + } + + return error; +} + +void tfa9897_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9897_specific; + ops->dsp_write_tables = tfa9897_tfa_dsp_write_tables; +} + +/***********************************************************************************/ +/* TFA9895 */ +/***********************************************************************************/ +static enum Tfa98xx_Error tfa9895_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + int result; + + if(tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + /* all i2C registers are already set to default */ + + result = TFA_SET_BF(tfa, AMPE, 1); + if (result < 0) + return -result; + + /* some other registers must be set for optimal amplifier behaviour */ + reg_write(tfa, 0x05, 0x13AB); + reg_write(tfa, 0x06, 0x001F); + /* peak voltage protection is always on, but may be written */ + reg_write(tfa, 0x08, 0x3C4E); + /*TFA98XX_SYSCTRL_DCA=0*/ + reg_write(tfa, 0x09, 0x024D); + reg_write(tfa, 0x41, 0x0308); + error = reg_write(tfa, 0x49, 0x0E82); + + return error; +} + +void tfa9895_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9895_specific; +} + +/***********************************************************************************/ +/* TFA9891 */ +/***********************************************************************************/ +static enum Tfa98xx_Error tfa9891_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + if (tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + /* ----- generated code start ----- */ + /* ----- version 18.0 ----- */ + reg_write(tfa, 0x09, 0x025d); //POR=0x024d + reg_write(tfa, 0x10, 0x0018); //POR=0x0024 + reg_write(tfa, 0x22, 0x0003); //POR=0x0023 + reg_write(tfa, 0x25, 0x0001); //POR=0x0000 + reg_write(tfa, 0x46, 0x0000); //POR=0x4000 + reg_write(tfa, 0x55, 0x3ffb); //POR=0x7fff + /* ----- generated code end ----- */ + + return error; +} + +void tfa9891_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9891_specific; +} + +/***********************************************************************************/ +/* TFA9890 */ +/***********************************************************************************/ +static enum Tfa98xx_Error tfa9890_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short regRead = 0; + + if(tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + /* all i2C registers are already set to default for N1C2 */ + + /* some PLL registers must be set optimal for amplifier behaviour */ + error = reg_write(tfa, 0x40, 0x5a6b); + if (error) + return error; + reg_read(tfa, 0x59, ®Read); + regRead |= 0x3; + reg_write(tfa, 0x59, regRead); + error = reg_write(tfa, 0x40, 0x0000); + error = reg_write(tfa, 0x47, 0x7BE1); + + return error; +} + +/* +* Disable clock gating +*/ +static enum Tfa98xx_Error tfa9890_clockgating(struct tfa_device *tfa, int on) +{ + enum Tfa98xx_Error error; + unsigned short value; + + /* for TFA9890 temporarily disable clock gating when dsp reset is used */ + error = reg_read(tfa, TFA98XX_CURRENTSENSE4, &value); + if (error) return error; + + if (Tfa98xx_Error_Ok == error) { + if (on) /* clock gating on - clear the bit */ + value &= ~TFA98XX_CURRENTSENSE4_CTRL_CLKGATECFOFF; + else /* clock gating off - set the bit */ + value |= TFA98XX_CURRENTSENSE4_CTRL_CLKGATECFOFF; + + error = reg_write(tfa, TFA98XX_CURRENTSENSE4, value); + } + + return error; +} + +/* +* Tfa9890_DspReset will deal with clock gating control in order +* to reset the DSP for warm state restart +*/ +static enum Tfa98xx_Error tfa9890_dsp_reset(struct tfa_device *tfa, int state) +{ + enum Tfa98xx_Error error; + + /* for TFA9890 temporarily disable clock gating + when dsp reset is used */ + tfa9890_clockgating(tfa, 0); + + TFA_SET_BF(tfa, RST, (uint16_t)state); + + /* clock gating restore */ + error = tfa9890_clockgating(tfa, 1); + + return error; +} + +/* + * Tfa9890_DspSystemStable will compensate for the wrong behavior of CLKS + * to determine if the DSP subsystem is ready for patch and config loading. + * + * A MTP calibration register is checked for non-zero. + * + * Note: This only works after i2c reset as this will clear the MTP contents. + * When we are configured then the DSP communication will synchronize access. + * + */ +static enum Tfa98xx_Error tfa9890_dsp_system_stable(struct tfa_device *tfa, int *ready) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short status, mtp0; + int result, tries; + + /* check the contents of the STATUS register */ + result = TFA_READ_REG(tfa, AREFS); + if (result < 0) { + error = -result; + goto errorExit; + } + status = (unsigned short)result; + + /* if AMPS is set then we were already configured and running + * no need to check further + */ + *ready = (TFA_GET_BF_VALUE(tfa, AMPS, status) == 1); + if (*ready) /* if ready go back */ + return error; /* will be Tfa98xx_Error_Ok */ + + /* check AREFS and CLKS: not ready if either is clear */ + *ready = !((TFA_GET_BF_VALUE(tfa, AREFS, status) == 0) + || (TFA_GET_BF_VALUE(tfa, CLKS, status) == 0)); + if (!*ready) /* if not ready go back */ + return error; /* will be Tfa98xx_Error_Ok */ + + /* check MTPB + * mtpbusy will be active when the subsys copies MTP to I2C + * 2 times retry avoids catching this short mtpbusy active period + */ + for (tries = 2; tries > 0; tries--) { + result = TFA_GET_BF(tfa, MTPB);/*TODO_MTPB*/ + if (result < 0) { + error = -result; + goto errorExit; + } + status = (unsigned short)result; + + /* check the contents of the STATUS register */ + *ready = (result == 0); + if (*ready) /* if ready go on */ + break; + } + if (tries == 0) /* ready will be 0 if retries exausted */ + return Tfa98xx_Error_Ok; + + /* check the contents of MTP register for non-zero, + * this indicates that the subsys is ready */ + + error = reg_read(tfa, 0x84, &mtp0); + if (error) + goto errorExit; + + *ready = (mtp0 != 0); /* The MTP register written? */ + + return error; + +errorExit: + *ready = 0; + return error; +} + +void tfa9890_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9890_specific; + ops->dsp_reset = tfa9890_dsp_reset; + ops->dsp_system_stable = tfa9890_dsp_system_stable; +} + +/***********************************************************************************/ +/* TFA9894 */ +/***********************************************************************************/ +static int tfa9894_set_swprofile(struct tfa_device *tfa, unsigned short new_value) +{ + int active_value = tfa_dev_get_swprof(tfa); + + /* Set the new value in the struct */ + tfa->profile = new_value; + + /* Set the new value in the hw register */ + tfa_set_bf_volatile(tfa, TFA9894_BF_SWPROFIL, new_value); + + return active_value; +} + +static int tfa9894_get_swprofile(struct tfa_device *tfa) +{ + return tfa_get_bf(tfa, TFA9894_BF_SWPROFIL) - 1; +} + +static int tfa9894_set_swvstep(struct tfa_device *tfa, unsigned short new_value) +{ + int active_value = tfa_dev_get_swvstep(tfa); + + /* Set the new value in the struct */ + tfa->vstep = new_value; + + /* Set the new value in the hw register */ + tfa_set_bf_volatile(tfa, TFA9894_BF_SWVSTEP, new_value); + + return active_value; +} + +static int tfa9894_get_swvstep(struct tfa_device *tfa) +{ + return tfa_get_bf(tfa, TFA9894_BF_SWVSTEP) - 1; +} + +static int tfa9894_get_mtpb(struct tfa_device *tfa) { + + int value = 0; + + /* Set the new value in the hw register */ + value = tfa_get_bf(tfa, TFA9894_BF_MTPB); + + return value; +} + +/** Set internal oscillator into power down mode for TFA9894. +* +* This function is a worker for tfa98xx_set_osc_powerdown(). +* +* @param[in] tfa device description structure +* @param[in] state new state 0 - oscillator is on, 1 oscillator is off. +* +* @return Tfa98xx_Error_Ok when successfull, error otherwise. +*/ +static enum Tfa98xx_Error tfa9894_set_osc_powerdown(struct tfa_device *tfa, int state) +{ + if (state == 1 || state == 0) { + return -tfa_set_bf(tfa, TFA9894_BF_MANAOOSC, (uint16_t)state); + } + + return Tfa98xx_Error_Bad_Parameter; +} + +static enum Tfa98xx_Error tfa9894_faim_protect(struct tfa_device *tfa, int status) +{ + enum Tfa98xx_Error ret = Tfa98xx_Error_Ok; + /* 0b = FAIM protection enabled 1b = FAIM protection disabled*/ + ret = tfa_set_bf_volatile(tfa, TFA9894_BF_OPENMTP, (uint16_t)(status)); + return ret; +} + +static enum Tfa98xx_Error tfa9894_specific(struct tfa_device *tfa) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + unsigned short value, xor; + + if (tfa->in_use == 0) + return Tfa98xx_Error_NotOpen; + + /* Unlock keys to write settings */ + error = reg_write(tfa, 0x0F, 0x5A6B); + error = reg_read(tfa, 0xFB, &value); + xor = value ^ 0x005A; + error = reg_write(tfa, 0xA0, xor); + + /* The optimal settings */ + if (tfa->rev == 0x0a94) { + /* V36 */ + /* ----- generated code start ----- */ + reg_write(tfa, 0x00, 0xa245); //POR=0x8245 + reg_write(tfa, 0x02, 0x51e8); //POR=0x55c8 + reg_write(tfa, 0x52, 0xbe17); //POR=0xb617 + reg_write(tfa, 0x57, 0x0344); //POR=0x0366 + reg_write(tfa, 0x61, 0x0033); //POR=0x0073 + reg_write(tfa, 0x71, 0x00cf); //POR=0x018d + reg_write(tfa, 0x72, 0x34a9); //POR=0x44e8 + reg_write(tfa, 0x73, 0x3808); //POR=0x3806 + reg_write(tfa, 0x76, 0x0067); //POR=0x0065 + reg_write(tfa, 0x80, 0x0000); //POR=0x0003 + reg_write(tfa, 0x81, 0x5715); //POR=0x561a + reg_write(tfa, 0x82, 0x0104); //POR=0x0044 + /* ----- generated code end ----- */ + } else if (tfa->rev == 0x1a94) { + /* V7 */ + /* ----- generated code start ----- */ + reg_write(tfa, 0x00, 0xa245); //POR=0x8245 + reg_write(tfa, 0x02, 0x5288); //POR=0x55c8 + reg_write(tfa, 0x52, 0xbe17); //POR=0xb617 + reg_write(tfa, 0x56, 0x05c3); //POR=0x07c3 + reg_write(tfa, 0x57, 0x0344); //POR=0x0366 + reg_write(tfa, 0x61, 0x0032); //POR=0x0073 + reg_write(tfa, 0x71, 0x00cf); //POR=0x018d + reg_write(tfa, 0x72, 0x34a9); //POR=0x44e8 + reg_write(tfa, 0x73, 0x3808); //POR=0x3806 + reg_write(tfa, 0x76, 0x0067); //POR=0x0065 + reg_write(tfa, 0x80, 0x0000); //POR=0x0003 + reg_write(tfa, 0x81, 0x5715); //POR=0x561a + reg_write(tfa, 0x82, 0x0104); //POR=0x0044 + /* ----- generated code end ----- */ + + } + + return error; +} + +static enum Tfa98xx_Error +tfa9894_set_mute(struct tfa_device *tfa, int mute) +{ + tfa_set_bf(tfa, TFA9894_BF_CFSM, (const uint16_t)mute); + + return Tfa98xx_Error_Ok; +} + +static enum Tfa98xx_Error tfa9894_dsp_system_stable(struct tfa_device *tfa, int *ready) +{ + enum Tfa98xx_Error error = Tfa98xx_Error_Ok; + + /* check CLKS: ready if set */ + *ready = tfa_get_bf(tfa, TFA9894_BF_CLKS)==1; + + return error; +} + +void tfa9894_ops(struct tfa_device_ops *ops) +{ + /* Set defaults for ops */ + set_ops_defaults(ops); + + ops->tfa_init = tfa9894_specific; + ops->dsp_system_stable = tfa9894_dsp_system_stable; + ops->set_mute = tfa9894_set_mute; + ops->faim_protect = tfa9894_faim_protect; + ops->get_mtpb = tfa9894_get_mtpb; + ops->set_swprof = tfa9894_set_swprofile; + ops->get_swprof = tfa9894_get_swprofile; + ops->set_swvstep = tfa9894_set_swvstep; + ops->get_swvstep = tfa9894_get_swvstep; + //ops->auto_copy_mtp_to_iic = tfa9894_auto_copy_mtp_to_iic; + ops->set_osc_powerdown = tfa9894_set_osc_powerdown; +} diff --git a/sound/soc/codecs/tfa_internal.h b/sound/soc/codecs/tfa_internal.h new file mode 100644 index 000000000000..9e8b35d3be76 --- /dev/null +++ b/sound/soc/codecs/tfa_internal.h @@ -0,0 +1,39 @@ +/* + * Copyright 2014-2017 NXP Semiconductors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + internal functions for TFA layer (not shared with SRV and HAL layer!) +*/ + +#ifndef __TFA_INTERNAL_H__ +#define __TFA_INTERNAL_H__ + +#include "tfa_dsp_fw.h" +#include "tfa_ext.h" + +#if __GNUC__ >= 4 + #define TFA_INTERNAL __attribute__ ((visibility ("hidden"))) +#else + #define TFA_INTERNAL +#endif + +#define TFA98XX_GENERIC_SLAVE_ADDRESS 0x1C + +TFA_INTERNAL enum Tfa98xx_Error tfa98xx_check_rpc_status(struct tfa_device *tfa, int *pRpcStatus); +TFA_INTERNAL enum Tfa98xx_Error tfa98xx_wait_result(struct tfa_device *tfa, int waitRetryCount); + +#endif /* __TFA_INTERNAL_H__ */ +